From a65b6092018c30583b13fca6c57e3f33fe9ee3a4 Mon Sep 17 00:00:00 2001 From: rnamiki Date: Wed, 15 Aug 2012 18:14:25 +0900 Subject: [PATCH 0001/1986] fixed bug different type command returns --- library.c | 20 + tests/TestRedis.php | 888 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 908 insertions(+) diff --git a/library.c b/library.c index d3ec9ef868..33042be3ff 100644 --- a/library.c +++ b/library.c @@ -692,6 +692,11 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA } if(inbuf[0] != '*') { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } return -1; } numElems = atoi(inbuf+1); @@ -1056,6 +1061,11 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo } if(inbuf[0] != '*') { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } return -1; } numElems = atoi(inbuf+1); @@ -1098,6 +1108,11 @@ PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, Red } if(inbuf[0] != '*') { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } return -1; } numElems = atoi(inbuf+1); @@ -1172,6 +1187,11 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R } if(inbuf[0] != '*') { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } return -1; } numElems = atoi(inbuf+1); diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 443fe06cfd..ca0c607c6a 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -2078,10 +2078,12 @@ public function testObject() { 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'); @@ -2118,10 +2120,12 @@ public function testMultiExec() { public function testPipeline() { $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, ""); } @@ -2686,6 +2690,890 @@ protected function sequence($mode) { $this->assertTrue($result === array(1.0, FALSE, FALSE, 2.0)); } + protected function differentType($mode) { + + // string + $key = 'string'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->set($key, 'value') + + // lists I/F + ->rPush($key, 'lvalue') + ->lPush($key, 'lvalue') + ->lLen($key) + ->lPop($key) + ->lGetRange($key, 0, -1) + ->lTrim($key, 0, 1) + ->lGet($key, 0) + ->lSet($key, 0, "newValue") + ->lRemove($key, 'lvalue', 1) + ->lPop($key) + ->rPop($key) + ->rPoplPush($key, __FUNCTION__ . 'lkey1') + + // sets I/F + ->sAdd($key, 'sValue1') + ->sRemove($key, 'sValue1') + ->sPop($key) + ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sSize($key) + ->sContains($key, 'sValue1') + ->sInter($key, __FUNCTION__ . 'skey2') + ->sUnion($key, __FUNCTION__ . 'skey4') + ->sDiff($key, __FUNCTION__ . 'skey7') + ->sMembers($key) + ->sRandMember($key) + + // sorted sets I/F + ->zAdd($key, 1, 'zValue1') + ->zDelete($key, 'zValue1') + ->zIncrBy($key, 1, 'zValue1') + ->zRank($key, 'zValue1') + ->zRevRank($key, 'zValue1') + ->zRange($key, 0, -1) + ->zReverseRange($key, 0, -1) + ->zRangeByScore($key, 1, 2) + ->zCount($key, 0, -1) + ->zCard($key) + ->zScore($key, 'zValue1') + ->zDeleteRangeByRank($key, 1, 2) + ->zDeleteRangeByScore($key, 1, 2) + + // hash I/F + ->hSet($key, 'key1', 'value1') + ->hGet($key, 'key1') + ->hMGet($key, array('key1')) + ->hMSet($key, array('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); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // ltrim + $this->assertTrue($ret[$i++] === FALSE); // lget + $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); // ssize + $this->assertTrue($ret[$i++] === FALSE); // scontains + $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); // zdelete + $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); // zdeleterangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + + $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($i, count($ret)); + + // list + $key = 'list'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->lpush($key, 'lvalue') + + // string I/F + ->get($key) + ->getset($key, 'value2') + ->append($key, 'append') + ->substr($key, 0, 8) + ->mget(array($key)) + ->incr($key) + ->incrBy($key, 1) + ->decr($key) + ->decrBy($key, 1) + + // sets I/F + ->sAdd($key, 'sValue1') + ->sRemove($key, 'sValue1') + ->sPop($key) + ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sSize($key) + ->sContains($key, 'sValue1') + ->sInter($key, __FUNCTION__ . 'skey2') + ->sUnion($key, __FUNCTION__ . 'skey4') + ->sDiff($key, __FUNCTION__ . 'skey7') + ->sMembers($key) + ->sRandMember($key) + + // sorted sets I/F + ->zAdd($key, 1, 'zValue1') + ->zDelete($key, 'zValue1') + ->zIncrBy($key, 1, 'zValue1') + ->zRank($key, 'zValue1') + ->zRevRank($key, 'zValue1') + ->zRange($key, 0, -1) + ->zReverseRange($key, 0, -1) + ->zRangeByScore($key, 1, 2) + ->zCount($key, 0, -1) + ->zCard($key) + ->zScore($key, 'zValue1') + ->zDeleteRangeByRank($key, 1, 2) + ->zDeleteRangeByScore($key, 1, 2) + + // hash I/F + ->hSet($key, 'key1', 'value1') + ->hGet($key, 'key1') + ->hMGet($key, array('key1')) + ->hMSet($key, array('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); // substr + $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // ssize + $this->assertTrue($ret[$i++] === FALSE); // scontains + $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); // zdelete + $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); // zdeleterangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + + $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($i, count($ret)); + + // set + $key = 'set'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->sAdd($key, 'sValue') + + // string I/F + ->get($key) + ->getset($key, 'value2') + ->append($key, 'append') + ->substr($key, 0, 8) + ->mget(array($key)) + ->incr($key) + ->incrBy($key, 1) + ->decr($key) + ->decrBy($key, 1) + + // lists I/F + ->rPush($key, 'lvalue') + ->lPush($key, 'lvalue') + ->lLen($key) + ->lPop($key) + ->lGetRange($key, 0, -1) + ->lTrim($key, 0, 1) + ->lGet($key, 0) + ->lSet($key, 0, "newValue") + ->lRemove($key, 'lvalue', 1) + ->lPop($key) + ->rPop($key) + ->rPoplPush($key, __FUNCTION__ . 'lkey1') + + // sorted sets I/F + ->zAdd($key, 1, 'zValue1') + ->zDelete($key, 'zValue1') + ->zIncrBy($key, 1, 'zValue1') + ->zRank($key, 'zValue1') + ->zRevRank($key, 'zValue1') + ->zRange($key, 0, -1) + ->zReverseRange($key, 0, -1) + ->zRangeByScore($key, 1, 2) + ->zCount($key, 0, -1) + ->zCard($key) + ->zScore($key, 'zValue1') + ->zDeleteRangeByRank($key, 1, 2) + ->zDeleteRangeByScore($key, 1, 2) + + // hash I/F + ->hSet($key, 'key1', 'value1') + ->hGet($key, 'key1') + ->hMGet($key, array('key1')) + ->hMSet($key, array('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); // substr + $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // ltrim + $this->assertTrue($ret[$i++] === FALSE); // lget + $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); // zdelete + $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); // zdeleterangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + + $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($i, count($ret)); + + // sorted set + $key = 'sortedset'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->zAdd($key, 0, 'zValue') + + // string I/F + ->get($key) + ->getset($key, 'value2') + ->append($key, 'append') + ->substr($key, 0, 8) + ->mget(array($key)) + ->incr($key) + ->incrBy($key, 1) + ->decr($key) + ->decrBy($key, 1) + + // lists I/F + ->rPush($key, 'lvalue') + ->lPush($key, 'lvalue') + ->lLen($key) + ->lPop($key) + ->lGetRange($key, 0, -1) + ->lTrim($key, 0, 1) + ->lGet($key, 0) + ->lSet($key, 0, "newValue") + ->lRemove($key, 'lvalue', 1) + ->lPop($key) + ->rPop($key) + ->rPoplPush($key, __FUNCTION__ . 'lkey1') + + // sets I/F + ->sAdd($key, 'sValue1') + ->sRemove($key, 'sValue1') + ->sPop($key) + ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sSize($key) + ->sContains($key, 'sValue1') + ->sInter($key, __FUNCTION__ . 'skey2') + ->sUnion($key, __FUNCTION__ . 'skey4') + ->sDiff($key, __FUNCTION__ . 'skey7') + ->sMembers($key) + ->sRandMember($key) + + // hash I/F + ->hSet($key, 'key1', 'value1') + ->hGet($key, 'key1') + ->hMGet($key, array('key1')) + ->hMSet($key, array('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); // substr + $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // ltrim + $this->assertTrue($ret[$i++] === FALSE); // lget + $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); // ssize + $this->assertTrue($ret[$i++] === FALSE); // scontains + $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($i, count($ret)); + + // hash + $key = 'hash'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->hset($key, 'key1', 'hValue') + + // string I/F + ->get($key) + ->getset($key, 'value2') + ->append($key, 'append') + ->substr($key, 0, 8) + ->mget(array($key)) + ->incr($key) + ->incrBy($key, 1) + ->decr($key) + ->decrBy($key, 1) + + // lists I/F + ->rPush($key, 'lvalue') + ->lPush($key, 'lvalue') + ->lLen($key) + ->lPop($key) + ->lGetRange($key, 0, -1) + ->lTrim($key, 0, 1) + ->lGet($key, 0) + ->lSet($key, 0, "newValue") + ->lRemove($key, 'lvalue', 1) + ->lPop($key) + ->rPop($key) + ->rPoplPush($key, __FUNCTION__ . 'lkey1') + + // sets I/F + ->sAdd($key, 'sValue1') + ->sRemove($key, 'sValue1') + ->sPop($key) + ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sSize($key) + ->sContains($key, 'sValue1') + ->sInter($key, __FUNCTION__ . 'skey2') + ->sUnion($key, __FUNCTION__ . 'skey4') + ->sDiff($key, __FUNCTION__ . 'skey7') + ->sMembers($key) + ->sRandMember($key) + + // sorted sets I/F + ->zAdd($key, 1, 'zValue1') + ->zDelete($key, 'zValue1') + ->zIncrBy($key, 1, 'zValue1') + ->zRank($key, 'zValue1') + ->zRevRank($key, 'zValue1') + ->zRange($key, 0, -1) + ->zReverseRange($key, 0, -1) + ->zRangeByScore($key, 1, 2) + ->zCount($key, 0, -1) + ->zCard($key) + ->zScore($key, 'zValue1') + ->zDeleteRangeByRank($key, 1, 2) + ->zDeleteRangeByScore($key, 1, 2) + + ->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); // substr + $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // ltrim + $this->assertTrue($ret[$i++] === FALSE); // lget + $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); // ssize + $this->assertTrue($ret[$i++] === FALSE); // scontains + $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); // zdelete + $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); // zdeleterangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + + $this->assertEquals($i, count($ret)); + } + + public function testDifferentTypeString() { + $key = 'string'; + $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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); + $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); + $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $this->assertEquals(FALSE, $this->redis->lPop($key)); + $this->assertEquals(FALSE, $this->redis->rPop($key)); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); + + // sets I/F + $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sPop($key)); + $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sSize($key)); + $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); + $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); + $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zCard($key)); + $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + + // 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, array('key1'))); + $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'list'; + $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->substr($key, 0, 8)); + $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals(FALSE, $this->redis->incr($key)); + $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); + $this->assertEquals(FALSE, $this->redis->decr($key)); + $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + + // sets I/F + $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sPop($key)); + $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sSize($key)); + $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); + $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); + $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zCard($key)); + $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + + // 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, array('key1'))); + $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'set'; + $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->substr($key, 0, 8)); + $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals(FALSE, $this->redis->incr($key)); + $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); + $this->assertEquals(FALSE, $this->redis->decr($key)); + $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + + // 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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); + $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); + $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $this->assertEquals(FALSE, $this->redis->lPop($key)); + $this->assertEquals(FALSE, $this->redis->rPop($key)); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); + + // sorted sets I/F + $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zCard($key)); + $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + + // 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, array('key1'))); + $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'sortedset'; + $this->redis->del($key); + $this->assertEquals(1, $this->redis->zAdd($key, 0, '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->substr($key, 0, 8)); + $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals(FALSE, $this->redis->incr($key)); + $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); + $this->assertEquals(FALSE, $this->redis->decr($key)); + $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + + // 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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); + $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); + $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $this->assertEquals(FALSE, $this->redis->lPop($key)); + $this->assertEquals(FALSE, $this->redis->rPop($key)); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); + + // sets I/F + $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sPop($key)); + $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sSize($key)); + $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); + $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); + $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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, array('key1'))); + $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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'; + $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->substr($key, 0, 8)); + $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals(FALSE, $this->redis->incr($key)); + $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); + $this->assertEquals(FALSE, $this->redis->decr($key)); + $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + + // 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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); + $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); + $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $this->assertEquals(FALSE, $this->redis->lPop($key)); + $this->assertEquals(FALSE, $this->redis->rPop($key)); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); + + // sets I/F + $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sPop($key)); + $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sSize($key)); + $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); + $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); + $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zCard($key)); + $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + } + public function testSerializerPHP() { $this->checkSerializer(Redis::SERIALIZER_PHP); From 3764a6cd800c20db3aa18e4ddb094e5cb44a72db Mon Sep 17 00:00:00 2001 From: kotas Date: Thu, 4 Oct 2012 22:18:18 +0900 Subject: [PATCH 0002/1986] add Redis::OPT_READ_TIMEOUT option for issue #70 --- common.h | 2 ++ library.c | 10 +++++++--- redis.c | 15 +++++++++++++++ tests/TestRedis.php | 9 +++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/common.h b/common.h index b48ac75b06..bf644ed718 100644 --- a/common.h +++ b/common.h @@ -34,6 +34,7 @@ typedef enum _REDIS_REPLY_TYPE { /* options */ #define REDIS_OPT_SERIALIZER 1 #define REDIS_OPT_PREFIX 2 +#define REDIS_OPT_READ_TIMEOUT 3 /* serializers */ #define REDIS_SERIALIZER_NONE 0 @@ -156,6 +157,7 @@ typedef struct { char *host; short port; double timeout; + double read_timeout; int failed; int status; int persistent; diff --git a/library.c b/library.c index 6a93f49096..b5845f4c25 100644 --- a/library.c +++ b/library.c @@ -841,6 +841,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por redis_sock->port = port; redis_sock->timeout = timeout; + redis_sock->read_timeout = timeout; redis_sock->serializer = REDIS_SERIALIZER_NONE; redis_sock->mode = ATOMIC; @@ -860,7 +861,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por */ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) { - struct timeval tv, *tv_ptr = NULL; + struct timeval tv, read_tv, *tv_ptr = NULL; char *host = NULL, *persistent_id = NULL, *errstr = NULL; int host_len, err = 0; php_netstream_data_t *sock; @@ -876,6 +877,9 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) tv_ptr = &tv; } + read_tv.tv_sec = (time_t)redis_sock->read_timeout; + read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); + if(redis_sock->host[0] == '/' && redis_sock->port < 1) { host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); } else { @@ -915,9 +919,9 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) php_stream_auto_cleanup(redis_sock->stream); - if(tv.tv_sec != 0) { + if(read_tv.tv_sec != 0) { php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT, - 0, &tv); + 0, &read_tv); } php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_WRITE_BUFFER, diff --git a/redis.c b/redis.c index f5aa435737..f068c3ef68 100644 --- a/redis.c +++ b/redis.c @@ -434,6 +434,7 @@ PHP_MINIT_FUNCTION(redis) /* options */ add_constant_long(redis_ce, "OPT_SERIALIZER", REDIS_OPT_SERIALIZER); add_constant_long(redis_ce, "OPT_PREFIX", REDIS_OPT_PREFIX); + add_constant_long(redis_ce, "OPT_READ_TIMEOUT", REDIS_OPT_READ_TIMEOUT); /* serializer */ add_constant_long(redis_ce, "SERIALIZER_NONE", REDIS_SERIALIZER_NONE); @@ -5650,6 +5651,9 @@ PHP_METHOD(Redis, getOption) { } RETURN_NULL(); + case REDIS_OPT_READ_TIMEOUT: + RETURN_DOUBLE(redis_sock->read_timeout); + default: RETURN_FALSE; @@ -5665,6 +5669,7 @@ PHP_METHOD(Redis, setOption) { long option, val_long; char *val_str; int val_len; + struct timeval read_tv; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", &object, redis_ce, &option, &val_str, &val_len) == FAILURE) { @@ -5704,6 +5709,16 @@ PHP_METHOD(Redis, setOption) { } RETURN_TRUE; + case REDIS_OPT_READ_TIMEOUT: + redis_sock->read_timeout = atof(val_str); + if(redis_sock->stream) { + read_tv.tv_sec = (time_t)redis_sock->read_timeout; + read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); + php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT, + 0, &read_tv); + } + RETURN_TRUE; + default: RETURN_FALSE; } diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 9f59d53525..21e8ffdb67 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -3321,6 +3321,15 @@ public function testTime() { 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)); + } + } exit(TestSuite::run("Redis_Test")); From 51b96938bc42d9de632ccae3d8ac2c7ce734d971 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 25 Oct 2012 14:19:04 -0700 Subject: [PATCH 0003/1986] SRANDMEMBER optional COUNT argument Adds support for the new COUNT argument to SRANDMEMBER. If called without a count, we will still return a string response (with one randome member from the set, or false on a failure/type error). If the count argument is passed, we return instead an array up to the number asked for, given how Redis will process this value. http://redis.io/commands/srandmember --- README.markdown | 16 ++++++++++-- redis.c | 52 ++++++++++++++++++++++++++++++++++--- tests/TestRedis.php | 62 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 124 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index d96e09c602..1489e94344 100644 --- a/README.markdown +++ b/README.markdown @@ -906,19 +906,31 @@ $redis->sPop('key1'); /* 'member3', 'key1' => {'member2'} */ ## sRandMember ##### *Description* -Returns a random element from the set value at Key, without removing it. +Returns a random element or random elements from the set value at Key, without removing it/them. ##### *Parameters* *key* -##### *Return value* +*count*: int, How many elements to return - optional. If the count is negative it will always return the # requested even if this requires duplicates to be returned +##### *Return value (No count passed)* *String* value from the set *Bool* `FALSE` if set identified by key is empty or doesn't exist. +##### *Return value (with count passed)* +*Array* Random value(s) from the set, or an empty array if the set is empty or doesn't exist ##### *Example*
 $redis->sAdd('key1' , 'member1');
 $redis->sAdd('key1' , 'member2');
 $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/
+
+// No count
 $redis->sRandMember('key1'); /* 'member1', 'key1' => {'member3', 'member1', 'member2'} */
 $redis->sRandMember('key1'); /* 'member3', 'key1' => {'member3', 'member1', 'member2'} */
+
+// With a count
+$redis->sRandMember('key1', 3); // Will return an array with all members from the set
+$redis->sRandMember('key1', 2); // Will an array with 2 members of the set
+$redis->sRandMember('key1', -100); // Will return an array of 100 elements, picked from our set (with dups)
+$redis->sRandMember('empty_set', 100); // Will return an empty array
+$redis->sRandMember('not_a_set', 100); // Will return FALSE
 
## sInter diff --git a/redis.c b/redis.c index fd103d30f9..bf80f6b0a9 100644 --- a/redis.c +++ b/redis.c @@ -2195,11 +2195,57 @@ PHP_METHOD(Redis, sPop) /* }}} */ /* }}} */ -/* {{{ proto string Redis::sRandMember(string key) +/* {{{ proto string Redis::sRandMember(string key [int count]) */ PHP_METHOD(Redis, sRandMember) { - generic_pop_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SRANDMEMBER", 11); + zval *object; + RedisSock *redis_sock; + char *key = NULL, *cmd; + int key_len, cmd_len, key_free = 0, have_count = 0; + long count; + + // Parse our params + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", + &object, redis_ce, &key, &key_len, &count) == FAILURE) { + RETURN_FALSE; + } + + // Get our redis socket + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } + + // Prefix our key if necissary + key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + + // Do we have a count + have_count = ZEND_NUM_ARGS() == 2; + + // If we have two arguments, we're running with an optional COUNT, which will return + // a multibulk reply. Without the argument we'll return a string response + if(have_count) { + // Construct our command with count + cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "sl", key, key_len, count); + } else { + // Construct our command + cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "s", key, key_len); + } + + // Free our key if we prefixed it + if(key_free) efree(key); + + // Process our command + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + + // Process our reply + IF_ATOMIC() { + // This will be bulk or multi-bulk depending if we passed the optional [COUNT] argument + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { + RETURN_FALSE; + } + } + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } /* }}} */ @@ -2207,7 +2253,7 @@ PHP_METHOD(Redis, sRandMember) */ PHP_METHOD(Redis, sContains) { - zval *object; + zval *object; RedisSock *redis_sock; char *key = NULL, *val = NULL, *cmd; int key_len, val_len, cmd_len; diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 9f59d53525..247503e18c 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -1083,6 +1083,61 @@ public function testsRandMember() { } } + public function testSRandMemberWithCount() { + // Make sure the set is nuked + $this->redis->delete('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 + // + + $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 testsContains() { $this->redis->delete('set'); @@ -3241,7 +3296,12 @@ public function testUnserialize() { 1,1.5,'one',Array('this','is','an','array') ); - foreach(Array(Redis::SERIALIZER_PHP, Redis::SERIALIZER_IGBINARY) as $mode) { + $serializers = Array(Redis::SERIALIZER_PHP); + if(defined('Redis::SERIALIZER_IGBINARY')) { + $serializers[] = Redis::SERIALIZER_IGBINARY; + } + + foreach($serializers as $mode) { $vals_enc = Array(); // Pass them through redis so they're serialized From 5cbe1cf6f938467464e2ed79887a5cac42446cff Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 25 Oct 2012 14:49:29 -0700 Subject: [PATCH 0004/1986] Fix formatting issue, remove unneccisary variable --- redis.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/redis.c b/redis.c index bf80f6b0a9..42925c67f7 100644 --- a/redis.c +++ b/redis.c @@ -2202,29 +2202,26 @@ PHP_METHOD(Redis, sRandMember) zval *object; RedisSock *redis_sock; char *key = NULL, *cmd; - int key_len, cmd_len, key_free = 0, have_count = 0; + int key_len, cmd_len, key_free = 0; long count; // Parse our params if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", &object, redis_ce, &key, &key_len, &count) == FAILURE) { RETURN_FALSE; - } + } // Get our redis socket if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; - } + } // Prefix our key if necissary key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - // Do we have a count - have_count = ZEND_NUM_ARGS() == 2; - // If we have two arguments, we're running with an optional COUNT, which will return // a multibulk reply. Without the argument we'll return a string response - if(have_count) { + if(ZEND_NUM_ARGS() == 2) { // Construct our command with count cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "sl", key, key_len, count); } else { From e1a5145ad2bf083c497edf63ed5d4ea470cbccf6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 18 Jan 2013 18:05:02 -0800 Subject: [PATCH 0005/1986] Changed the way we build the HMSET command such that we don't continue to destroy and reallocate the command buffer Added a simply library routine to append to a command buffer using a smart_str Made the unit tests work even if you're not compiled with igbinary Addresses issue #287 --- library.c | 14 ++++++++ library.h | 1 + redis.c | 82 ++++++++++++++++++++------------------------- tests/TestRedis.php | 5 ++- 4 files changed, 56 insertions(+), 46 deletions(-) diff --git a/library.c b/library.c index a954787f5b..d8c7f27573 100644 --- a/library.c +++ b/library.c @@ -447,6 +447,20 @@ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) return buf.len; } +/* + * Append a command sequence to a smart_str + */ +int redis_cmd_append_sstr(smart_str *str, char *append, int append_len) { + smart_str_appendc(str, '$'); + smart_str_append_long(str, append_len); + smart_str_appendl(str, _NL, sizeof(_NL) - 1); + smart_str_appendl(str, append, append_len); + smart_str_appendl(str, _NL, sizeof(_NL) - 1); + + // Return our new length + return str->len; +} + /* * Append an integer command to a Redis command */ diff --git a/library.h b/library.h index ea135bfbc5..d730b96b2e 100644 --- a/library.h +++ b/library.h @@ -4,6 +4,7 @@ int redis_cmd_format(char **ret, char *format, ...); int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); int redis_cmd_format_header(char **ret, char *keyword, int arg_count); int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len); +int redis_cmd_append_sstr(smart_str *str, char *append, int append_len); int redis_cmd_append_int(char **cmd, int cmd_len, int append); diff --git a/redis.c b/redis.c index fd103d30f9..593f1c8708 100644 --- a/redis.c +++ b/redis.c @@ -4856,18 +4856,15 @@ PHP_METHOD(Redis, hMget) { REDIS_PROCESS_RESPONSE_CLOSURE(redis_sock_read_multibulk_reply_assoc, z_keys); } - PHP_METHOD(Redis, hMset) { zval *object; RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; + char *key = NULL, *cmd, *old_cmd = NULL; + int key_len, cmd_len, key_free, i, element_count = 2; zval *z_hash; HashTable *ht_hash; - int i; - int element_count = 2; - char *old_cmd = NULL; + smart_str set_cmds = {0}; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, redis_ce, @@ -4885,68 +4882,63 @@ PHP_METHOD(Redis, hMset) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - cmd_len = redis_cmd_format(&cmd, + key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + cmd_len = redis_cmd_format(&cmd, "$5" _NL "HMSET" _NL "$%d" _NL "%s" _NL , key_len, key, key_len); - if(key_free) efree(key); + if(key_free) efree(key); /* looping on each item of the array */ - for(i =0, zend_hash_internal_pointer_reset(ht_hash); - zend_hash_has_more_elements(ht_hash) == SUCCESS; - i++, zend_hash_move_forward(ht_hash)) { - - char *hkey; - unsigned int hkey_len; - unsigned long idx; - int type; - int hkey_free = 0; - zval **z_value_p; + for(i =0, zend_hash_internal_pointer_reset(ht_hash); + zend_hash_has_more_elements(ht_hash) == SUCCESS; + i++, zend_hash_move_forward(ht_hash)) { + + char *hkey, hkey_str[40]; + unsigned int hkey_len; + unsigned long idx; + int type; + zval **z_value_p; - char *hval; + char *hval; int hval_len, hval_free; - type = zend_hash_get_current_key_ex(ht_hash, &hkey, &hkey_len, &idx, 0, NULL); + type = zend_hash_get_current_key_ex(ht_hash, &hkey, &hkey_len, &idx, 0, NULL); - if(zend_hash_get_current_data(ht_hash, (void**)&z_value_p) == FAILURE) { - continue; /* this should never happen */ - } + if(zend_hash_get_current_data(ht_hash, (void**)&z_value_p) == FAILURE) { + continue; /* this should never happen */ + } - if(type != HASH_KEY_IS_STRING) { /* convert to string */ - hkey_free = 1; - hkey = emalloc(40); - hkey_len = 1 + sprintf(hkey, "%ld", idx); + if(type != HASH_KEY_IS_STRING) { /* convert to string */ + hkey_len = 1 + sprintf(hkey_str, "%ld", idx); + hkey = (char*)hkey_str; } element_count += 2; /* key is set. */ hval_free = redis_serialize(redis_sock, *z_value_p, &hval, &hval_len TSRMLS_CC); - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "%s" - "$%d" _NL "%s" _NL - "$%d" _NL "%s" _NL - , cmd, cmd_len - , hkey_len-1, hkey, hkey_len-1 - , hval_len, hval, hval_len); - efree(old_cmd); + // Append our member and value in place + redis_cmd_append_sstr(&set_cmds, hkey, hkey_len - 1); + redis_cmd_append_sstr(&set_cmds, hval, hval_len); + if(hval_free) efree(hval); - if(hkey_free) efree(hkey); } + // Now construct the entire command old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "*%d" _NL "%s" - , element_count, cmd, cmd_len); + cmd_len = redis_cmd_format(&cmd, "*%d" _NL "%s%s", element_count, cmd, cmd_len, set_cmds.c, set_cmds.len); efree(old_cmd); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); -} + // Free the HMSET bits + efree(set_cmds.c); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_boolean_response); +} PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 1752b08151..be29aaec88 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -3263,7 +3263,10 @@ public function testUnserialize() { 1,1.5,'one',Array('this','is','an','array') ); - foreach(Array(Redis::SERIALIZER_PHP, Redis::SERIALIZER_IGBINARY) as $mode) { + $serializers = Array(Redis::SERIALIZER_PHP); + if(defined('Redis::SERIALIZER_IGBINARY')) $serializers[] = Redis::SERIALIZER_IGBINARY; + + foreach($serializers as $mode) { $vals_enc = Array(); // Pass them through redis so they're serialized From 6529458c0e9ad5f84036bb5bf5d1e3578dab272d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 19 Jan 2013 13:34:26 -0800 Subject: [PATCH 0006/1986] Add include for smart_str --- common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common.h b/common.h index b48ac75b06..2d42a9945d 100644 --- a/common.h +++ b/common.h @@ -1,5 +1,6 @@ #include "php.h" #include "php_ini.h" +#include #ifndef REDIS_COMMON_H #define REDIS_COMMON_H From 3fb643211e524112bd9a19792f833b12b68de600 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Mon, 21 Jan 2013 18:10:16 +0200 Subject: [PATCH 0007/1986] Retry delay - selectDB on array Added the possibility to delay each reconnection attempt, including a random factor to prevent several or many concurrent connections from trying to reconnect at the same time. Added the select command to RedisArray to select a DB on every connections in one instruction. --- common.h | 1 + library.c | 1496 ++++++++++++++++++++++---------------------- library.h | 2 +- redis.c | 15 +- redis_array.c | 57 +- redis_array.h | 1 + redis_array_impl.c | 33 +- redis_array_impl.h | 4 +- redis_session.c | 9 +- 9 files changed, 853 insertions(+), 765 deletions(-) diff --git a/common.h b/common.h index b48ac75b06..fda59c6aeb 100644 --- a/common.h +++ b/common.h @@ -156,6 +156,7 @@ typedef struct { char *host; short port; double timeout; + long retry_interval; int failed; int status; int persistent; diff --git a/library.c b/library.c index a954787f5b..15e71e5a1f 100644 --- a/library.c +++ b/library.c @@ -26,76 +26,83 @@ extern zend_class_entry *redis_exception_ce; extern zend_class_entry *spl_ce_RuntimeException; PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { - if (!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } else { - php_stream_pclose(redis_sock->stream); - } + if (!redis_sock->persistent) { + php_stream_close(redis_sock->stream); + } else { + php_stream_pclose(redis_sock->stream); + } } PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) { - int eof; - int count = 0; - - if (!redis_sock->stream) - return -1; - - eof = php_stream_eof(redis_sock->stream); - for (; eof; count++) { - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ - if(redis_sock->stream) { /* close stream if still here */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->watching = 0; - } - zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); - return -1; - } - if(redis_sock->stream) { /* close existing stream before reconnecting */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; - } - redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ - if(redis_sock->stream) { /* check for EOF again. */ - eof = php_stream_eof(redis_sock->stream); - } + int eof; + int count = 0; + + if (!redis_sock->stream) { + return -1; + } + + eof = php_stream_eof(redis_sock->stream); + for (; eof; count++) { + if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ + if(redis_sock->stream) { /* close stream if still here */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->watching = 0; + } + zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); + return -1; + } + if(redis_sock->stream) { /* close existing stream before reconnecting */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; + } + // Wait for a while before trying to reconnect + if (redis_sock->retry_interval) { + // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time + long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval)); + usleep(retry_interval); + } + redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ + if(redis_sock->stream) { /* check for EOF again. */ + eof = php_stream_eof(redis_sock->stream); } + } // Reselect the DB. - if (count && redis_sock->dbNumber) { - char *cmd, *response; - int cmd_len, response_len; + if (count && redis_sock->dbNumber) { + char *cmd, *response; + int cmd_len, response_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return -1; - } - efree(cmd); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return -1; + } + efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - return -1; - } + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return -1; + } - if (strncmp(response, "+OK", 3)) { - efree(response); - return -1; - } - efree(response); + if (strncmp(response, "+OK", 3)) { + efree(response); + return -1; } + efree(response); + } - return 0; + return 0; } PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char inbuf[1024]; - int numElems; + int numElems; zval *z_tab; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { @@ -103,7 +110,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -122,7 +129,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, numElems, 1, UNSERIALIZE_ALL); - return z_tab; + return z_tab; } /** @@ -145,13 +152,13 @@ PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_ char c; int i; - reply = emalloc(bytes+1); + reply = emalloc(bytes+1); while(offset < bytes) { got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); if (got <= 0) { /* Error or EOF */ - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); break; } offset += got; @@ -179,7 +186,7 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -190,12 +197,12 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) switch(inbuf[0]) { case '-': - err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); - /* stale data */ - if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } + err_len = strlen(inbuf+1) - 2; + redis_sock_set_err(redis_sock, inbuf+1, err_len); + /* stale data */ + if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } return NULL; case '$': @@ -212,7 +219,7 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) case '+': case ':': - // Single Line Reply + // Single Line Reply /* :123\r\n */ *buf_len = strlen(inbuf) - 2; if(*buf_len >= 2) { @@ -223,12 +230,12 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) } default: - zend_throw_exception_ex( - redis_exception_ce, - 0 TSRMLS_CC, - "protocol error, got '%c' as reply type byte\n", - inbuf[0] - ); + zend_throw_exception_ex( + redis_exception_ce, + 0 TSRMLS_CC, + "protocol error, got '%c' as reply type byte\n", + inbuf[0] + ); } return NULL; @@ -245,42 +252,42 @@ void add_constant_long(zend_class_entry *ce, char *name, int value) { int integer_length(int i) { - int sz = 0; - int ci = abs(i); - while (ci > 0) { - ci /= 10; - sz++; - } - if (i == 0) { /* log 0 doesn't make sense. */ - sz = 1; - } else if (i < 0) { /* allow for neg sign as well. */ - sz++; - } - return sz; + int sz = 0; + int ci = abs(i); + while (ci > 0) { + ci /= 10; + sz++; + } + if (i == 0) { /* log 0 doesn't make sense. */ + sz = 1; + } else if (i < 0) { /* allow for neg sign as well. */ + sz++; + } + return sz; } int redis_cmd_format_header(char **ret, char *keyword, int arg_count) { - // Our return buffer - smart_str buf = {0}; - - // Keyword length - int l = strlen(keyword); - - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, arg_count + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - // Set our return pointer - *ret = buf.c; - - // Return the length - return buf.len; + // Our return buffer + smart_str buf = {0}; + + // Keyword length + int l = strlen(keyword); + + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, arg_count + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + // Set our return pointer + *ret = buf.c; + + // Return the length + return buf.len; } int @@ -290,74 +297,74 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { va_list ap; smart_str buf = {0}; int l = strlen(keyword); - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - /* add header */ - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, strlen(format) + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - while (*p) { - smart_str_appendc(&buf, '$'); - - switch(*p) { - case 's': { - char *val = va_arg(ap, char*); - int val_len = va_arg(ap, int); - smart_str_append_long(&buf, val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, val, val_len); - } - break; - - case 'f': - case 'F': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'i': - case 'd': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - case 'l': - case 'L': { - long l = va_arg(ap, long); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - p++; - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - } - smart_str_0(&buf); - - *ret = buf.c; - - return buf.len; + char *dbl_str; + int dbl_len; + + va_start(ap, format); + + /* add header */ + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, strlen(format) + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + while (*p) { + smart_str_appendc(&buf, '$'); + + switch(*p) { + case 's': { + char *val = va_arg(ap, char*); + int val_len = va_arg(ap, int); + smart_str_append_long(&buf, val_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, val, val_len); + } + break; + + case 'f': + case 'F': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'i': + case 'd': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + case 'l': + case 'L': { + long l = va_arg(ap, long); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + p++; + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + } + smart_str_0(&buf); + + *ret = buf.c; + + return buf.len; } /** @@ -368,119 +375,119 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { int redis_cmd_format(char **ret, char *format, ...) { - smart_str buf = {0}; - va_list ap; - char *p = format; - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - while (*p) { - if (*p == '%') { - switch (*(++p)) { - case 's': { - char *tmp = va_arg(ap, char*); - int tmp_len = va_arg(ap, int); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - - case 'F': - case 'f': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'd': - case 'i': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - } else { - smart_str_appendc(&buf, *p); - } - - p++; - } - - smart_str_0(&buf); - - *ret = buf.c; - - return buf.len; + smart_str buf = {0}; + va_list ap; + char *p = format; + char *dbl_str; + int dbl_len; + + va_start(ap, format); + + while (*p) { + if (*p == '%') { + switch (*(++p)) { + case 's': { + char *tmp = va_arg(ap, char*); + int tmp_len = va_arg(ap, int); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + + case 'F': + case 'f': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'd': + case 'i': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + } else { + smart_str_appendc(&buf, *p); + } + + p++; + } + + smart_str_0(&buf); + + *ret = buf.c; + + return buf.len; } /* * Append a command sequence to a Redis command */ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) { - // Smart string buffer - smart_str buf = {0}; + // Smart string buffer + smart_str buf = {0}; - // Append the current command to our smart_str - smart_str_appendl(&buf, *cmd, cmd_len); + // Append the current command to our smart_str + smart_str_appendl(&buf, *cmd, cmd_len); - // Append our new command sequence - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, append, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + // Append our new command sequence + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, append, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - // Free our old command - efree(*cmd); + // Free our old command + efree(*cmd); - // Set our return pointer - *cmd = buf.c; + // Set our return pointer + *cmd = buf.c; - // Return new command length - return buf.len; + // Return new command length + return buf.len; } /* * Append an integer command to a Redis command */ int redis_cmd_append_int(char **cmd, int cmd_len, int append) { - char int_buf[32]; + char int_buf[32]; - // Conver to an int, capture length - int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); + // Conver to an int, capture length + int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); - // Return the new length - return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); + // Return the new length + return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); } PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - double ret; + double ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } - return; + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } + return; } ret = atof(response); efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_double(z_tab, ret); + add_next_index_double(z_tab, ret); } else { - RETURN_DOUBLE(ret); + RETURN_DOUBLE(ret); } } @@ -490,41 +497,41 @@ PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s long l; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } } if (strncmp(response, "+string", 7) == 0) { - l = REDIS_STRING; + l = REDIS_STRING; } else if (strncmp(response, "+set", 4) == 0){ - l = REDIS_SET; + l = REDIS_SET; } else if (strncmp(response, "+list", 5) == 0){ - l = REDIS_LIST; + l = REDIS_LIST; } else if (strncmp(response, "+zset", 5) == 0){ - l = REDIS_ZSET; + l = REDIS_ZSET; } else if (strncmp(response, "+hash", 5) == 0){ - l = REDIS_HASH; + l = REDIS_HASH; } else { - l = REDIS_NOT_FOUND; + l = REDIS_NOT_FOUND; } efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_long(z_tab, l); + add_next_index_long(z_tab, l); } else { - RETURN_LONG(l); + RETURN_LONG(l); } } PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - char *pos, *cur; - char *key, *value, *p; - int is_numeric; + char *pos, *cur; + char *key, *value, *p; + int is_numeric; zval *z_multi_result; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { @@ -540,13 +547,13 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s cur = response; while(1) { - /* skip comments and empty lines */ - if(*cur == '#' || *cur == '\r') { - if(!(cur = strchr(cur, '\n'))) - break; - cur++; - continue; - } + /* skip comments and empty lines */ + if(*cur == '#' || *cur == '\r') { + if(!(cur = strchr(cur, '\n'))) + break; + cur++; + continue; + } /* key */ pos = strchr(cur, ':'); @@ -601,10 +608,10 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock char ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; - } + return; + } RETURN_FALSE; } ret = response[0]; @@ -620,15 +627,15 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_bool(z_tab, 0); } } else { - if (ret == '+') { + if (ret == '+') { if (success_callback != NULL) { success_callback(redis_sock); } - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -641,38 +648,38 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s int response_len; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } } if(response[0] == ':') { long long ret = atoll(response + 1); IF_MULTI_OR_PIPELINE() { - if(ret > LONG_MAX) { /* overflow */ - add_next_index_stringl(z_tab, response+1, response_len-1, 1); - } else { - efree(response); - add_next_index_long(z_tab, (long)ret); - } + if(ret > LONG_MAX) { /* overflow */ + add_next_index_stringl(z_tab, response+1, response_len-1, 1); + } else { + efree(response); + add_next_index_long(z_tab, (long)ret); + } } else { - if(ret > LONG_MAX) { /* overflow */ - RETURN_STRINGL(response+1, response_len-1, 1); - } else { - efree(response); - RETURN_LONG((long)ret); - } - } + if(ret > LONG_MAX) { /* overflow */ + RETURN_STRINGL(response+1, response_len-1, 1); + } else { + efree(response); + RETURN_LONG((long)ret); + } + } } else { efree(response); IF_MULTI_OR_PIPELINE() { add_next_index_null(z_tab); } else { - RETURN_FALSE; - } + RETURN_FALSE; + } } } @@ -680,20 +687,20 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) { - /* - int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); - array_zip_values_and_scores(return_value, 0); - */ + /* + int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); + array_zip_values_and_scores(return_value, 0); + */ char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -717,10 +724,10 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); } return 0; @@ -728,43 +735,43 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); + return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); } PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); + return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); } PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - char *response; - int response_len; - char ret; - - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } - } - ret = response[1]; - efree(response); - - IF_MULTI_OR_PIPELINE() { - if(ret == '1') { - add_next_index_bool(z_tab, 1); - } else { - add_next_index_bool(z_tab, 0); - } - } else { - if (ret == '1') { - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + char *response; + int response_len; + char ret; + + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } + } + ret = response[1]; + efree(response); + + IF_MULTI_OR_PIPELINE() { + if(ret == '1') { + add_next_index_bool(z_tab, 1); + } else { + add_next_index_bool(z_tab, 0); + } + } else { + if (ret == '1') { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -775,25 +782,25 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { - if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { - RETURN_STRINGL(response, response_len, 0); - } else { - efree(response); - } - } + if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { + RETURN_STRINGL(response, response_len, 0); + } else { + efree(response); + } + } } /* like string response, but never unserialized. */ @@ -805,15 +812,15 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - add_next_index_stringl(z_tab, response, response_len, 0); + add_next_index_stringl(z_tab, response, response_len, 0); } else { - RETURN_STRINGL(response, response_len, 0); - } + RETURN_STRINGL(response, response_len, 0); + } } @@ -821,7 +828,8 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s * redis_sock_create */ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, - double timeout, int persistent, char *persistent_id) + double timeout, int persistent, char *persistent_id, + long retry_interval) { RedisSock *redis_sock; @@ -831,11 +839,11 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; redis_sock->dbNumber = 0; - + redis_sock->retry_interval = retry_interval * 1000; redis_sock->persistent = persistent; if(persistent_id) { - size_t persistent_id_len = strlen(persistent_id); + size_t persistent_id_len = strlen(persistent_id); redis_sock->persistent_id = ecalloc(persistent_id_len + 1, 1); memcpy(redis_sock->persistent_id, persistent_id, persistent_id_len); } else { @@ -869,8 +877,8 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) struct timeval tv, *tv_ptr = NULL; char *host = NULL, *persistent_id = NULL, *errstr = NULL; int host_len, err = 0; - php_netstream_data_t *sock; - int tcp_flag = 1; + php_netstream_data_t *sock; + int tcp_flag = 1; if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock TSRMLS_CC); @@ -879,15 +887,15 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) tv.tv_sec = (time_t)redis_sock->timeout; tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); if(tv.tv_sec != 0 || tv.tv_usec != 0) { - tv_ptr = &tv; + tv_ptr = &tv; } if(redis_sock->host[0] == '/' && redis_sock->port < 1) { - host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); + host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); } else { - if(redis_sock->port == 0) - redis_sock->port = 6379; - host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); + if(redis_sock->port == 0) + redis_sock->port = 6379; + host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); } if (redis_sock->persistent) { @@ -899,10 +907,10 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } redis_sock->stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE, - STREAM_XPORT_CLIENT - | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &errstr, &err - ); + STREAM_XPORT_CLIENT + | STREAM_XPORT_CONNECT, + persistent_id, tv_ptr, NULL, &errstr, &err + ); if (persistent_id) { efree(persistent_id); @@ -916,7 +924,7 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } /* set TCP_NODELAY */ - sock = (php_netstream_data_t*)redis_sock->stream->abstract; + sock = (php_netstream_data_t*)redis_sock->stream->abstract; setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); php_stream_auto_cleanup(redis_sock->stream); @@ -967,23 +975,23 @@ PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRML PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) { if (redis_sock == NULL) { - return 1; + return 1; } redis_sock->dbNumber = 0; if (redis_sock->stream != NULL) { - if (!redis_sock->persistent) { - redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); - } + if (!redis_sock->persistent) { + redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); + } - redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; + redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; - if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ - php_stream_close(redis_sock->stream); - } - redis_sock->stream = NULL; + if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ + php_stream_close(redis_sock->stream); + } + redis_sock->stream = NULL; - return 1; + return 1; } return 0; @@ -992,8 +1000,8 @@ PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *cmd; - int response_len, cmd_len; - char * response; + int response_len, cmd_len; + char * response; cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); @@ -1007,41 +1015,41 @@ PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_so RETURN_FALSE; } - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { - RETURN_TRUE; - } - RETURN_FALSE; + if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + RETURN_TRUE; + } + RETURN_FALSE; } /** * redis_sock_set_err */ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { - // Allocate/Reallocate our last error member - if(msg != NULL && msg_len > 0) { - if(redis_sock->err == NULL) { - redis_sock->err = emalloc(msg_len + 1); - } else if(msg_len > redis_sock->err_len) { - redis_sock->err = erealloc(redis_sock->err, msg_len +1); - } - - // Copy in our new error message, set new length, and null terminate - memcpy(redis_sock->err, msg, msg_len); - redis_sock->err[msg_len] = '\0'; - redis_sock->err_len = msg_len; - } else { - // Free our last error - if(redis_sock->err != NULL) { - efree(redis_sock->err); - } - - // Set to null, with zero length - redis_sock->err = NULL; - redis_sock->err_len = 0; - } - - // Success - return 0; + // Allocate/Reallocate our last error member + if(msg != NULL && msg_len > 0) { + if(redis_sock->err == NULL) { + redis_sock->err = emalloc(msg_len + 1); + } else if(msg_len > redis_sock->err_len) { + redis_sock->err = erealloc(redis_sock->err, msg_len +1); + } + + // Copy in our new error message, set new length, and null terminate + memcpy(redis_sock->err, msg, msg_len); + redis_sock->err[msg_len] = '\0'; + redis_sock->err_len = msg_len; + } else { + // Free our last error + if(redis_sock->err != NULL) { + efree(redis_sock->err); + } + + // Set to null, with zero length + redis_sock->err = NULL; + redis_sock->err_len = 0; + } + + // Success + return 0; } /** @@ -1050,14 +1058,14 @@ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_le PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1092,14 +1100,14 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1138,17 +1146,17 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re while(numElems > 0) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - int can_unserialize = unwrap_key; - if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) - can_unserialize = 0; - - if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + zval *z = NULL; + int can_unserialize = unwrap_key; + if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) + can_unserialize = 0; + + if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { add_next_index_bool(z_tab, 0); } @@ -1164,8 +1172,8 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R { char inbuf[1024], *response; int response_len; - int i, numElems; - zval *z_multi_result; + int i, numElems; + zval *z_multi_result; zval **z_keys = ctx; @@ -1173,7 +1181,7 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1192,30 +1200,30 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); - } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); - } - } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); - } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + } + } else { + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + } + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - INIT_PZVAL(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); - } + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + INIT_PZVAL(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); + } return 0; } @@ -1224,10 +1232,10 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R */ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { - if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { - zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); - return -1; - } + if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { + zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); + return -1; + } if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } @@ -1240,10 +1248,10 @@ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_D PHPAPI void redis_free_socket(RedisSock *redis_sock) { if(redis_sock->prefix) { - efree(redis_sock->prefix); - } + efree(redis_sock->prefix); + } if(redis_sock->err) { - efree(redis_sock->err); + efree(redis_sock->err); } efree(redis_sock->host); efree(redis_sock); @@ -1252,146 +1260,146 @@ PHPAPI void redis_free_socket(RedisSock *redis_sock) PHPAPI int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC) { #if ZEND_MODULE_API_NO >= 20100000 - php_serialize_data_t ht; + php_serialize_data_t ht; #else - HashTable ht; + HashTable ht; #endif - smart_str sstr = {0}; - zval *z_copy; - size_t sz; - uint8_t *val8; - - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - switch(Z_TYPE_P(z)) { - - case IS_STRING: - *val = Z_STRVAL_P(z); - *val_len = Z_STRLEN_P(z); - return 0; - - case IS_OBJECT: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Object", 6, 1); - break; - - case IS_ARRAY: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Array", 5, 1); - break; - - default: /* copy */ - MAKE_STD_ZVAL(z_copy); - *z_copy = *z; - zval_copy_ctor(z_copy); - break; - } - - /* return string */ - convert_to_string(z_copy); - *val = Z_STRVAL_P(z_copy); - *val_len = Z_STRLEN_P(z_copy); - efree(z_copy); - return 1; - - case REDIS_SERIALIZER_PHP: + smart_str sstr = {0}; + zval *z_copy; + size_t sz; + uint8_t *val8; + + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + switch(Z_TYPE_P(z)) { + + case IS_STRING: + *val = Z_STRVAL_P(z); + *val_len = Z_STRLEN_P(z); + return 0; + + case IS_OBJECT: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Object", 6, 1); + break; + + case IS_ARRAY: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Array", 5, 1); + break; + + default: /* copy */ + MAKE_STD_ZVAL(z_copy); + *z_copy = *z; + zval_copy_ctor(z_copy); + break; + } + + /* return string */ + convert_to_string(z_copy); + *val = Z_STRVAL_P(z_copy); + *val_len = Z_STRLEN_P(z_copy); + efree(z_copy); + return 1; + + case REDIS_SERIALIZER_PHP: #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_INIT(ht); + PHP_VAR_SERIALIZE_INIT(ht); #else - zend_hash_init(&ht, 10, NULL, NULL, 0); + zend_hash_init(&ht, 10, NULL, NULL, 0); #endif - php_var_serialize(&sstr, &z, &ht TSRMLS_CC); - *val = sstr.c; - *val_len = (int)sstr.len; + php_var_serialize(&sstr, &z, &ht TSRMLS_CC); + *val = sstr.c; + *val_len = (int)sstr.len; #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_DESTROY(ht); + PHP_VAR_SERIALIZE_DESTROY(ht); #else - zend_hash_destroy(&ht); + zend_hash_destroy(&ht); #endif - return 1; + return 1; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ - *val = (char*)val8; - *val_len = (int)sz; - return 1; - } + if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ + *val = (char*)val8; + *val_len = (int)sz; + return 1; + } #endif - return 0; - } - return 0; + return 0; + } + return 0; } PHPAPI int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) { - php_unserialize_data_t var_hash; - int ret; + php_unserialize_data_t var_hash; + int ret; - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - return 0; + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + return 0; - case REDIS_SERIALIZER_PHP: - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - } + case REDIS_SERIALIZER_PHP: + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_INIT(var_hash); + PHP_VAR_UNSERIALIZE_INIT(var_hash); #else - memset(&var_hash, 0, sizeof(var_hash)); + memset(&var_hash, 0, sizeof(var_hash)); #endif - if(!php_var_unserialize(return_value, (const unsigned char**)&val, - (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { - efree(*return_value); - ret = 0; - } else { - ret = 1; - } + if(!php_var_unserialize(return_value, (const unsigned char**)&val, + (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { + efree(*return_value); + ret = 0; + } else { + ret = 1; + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); #else - var_destroy(&var_hash); + var_destroy(&var_hash); #endif - return ret; + return ret; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - } - if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { - return 1; - } - efree(*return_value); + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + } + if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { + return 1; + } + efree(*return_value); #endif - return 0; - break; - } - return 0; + return 0; + break; + } + return 0; } PHPAPI int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { - int ret_len; - char *ret; - - if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { - return 0; - } - - ret_len = redis_sock->prefix_len + *key_len; - ret = ecalloc(1 + ret_len, 1); - memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); - memcpy(ret + redis_sock->prefix_len, *key, *key_len); - - *key = ret; - *key_len = ret_len; - return 1; + int ret_len; + char *ret; + + if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { + return 0; + } + + ret_len = redis_sock->prefix_len + *key_len; + ret = ecalloc(1 + ret_len, 1); + memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); + memcpy(ret + redis_sock->prefix_len, *key, *key_len); + + *key = ret; + *key_len = ret_len; + return 1; } /* @@ -1401,60 +1409,60 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { PHPAPI int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) { // Handle EOF - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } - if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { - // Close, put our socket state into error - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { + // Close, put our socket state into error + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; - // Throw a read error exception - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); - } + // Throw a read error exception + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + } - // We don't need \r\n - *line_size-=2; - buf[*line_size]='\0'; + // We don't need \r\n + *line_size-=2; + buf[*line_size]='\0'; - // Success! - return 0; + // Success! + return 0; } PHPAPI int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) { - // Make sure we haven't lost the connection, even trying to reconnect - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { - // Failure - return -1; - } - - // Attempt to read the reply-type byte - if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); - } - - // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here - if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { - // Buffer to hold size information - char inbuf[255]; - - // Read up to our newline - if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { - return -1; - } - - // Set our size response - *reply_info = atoi(inbuf); - } - - // Success! - return 0; + // Make sure we haven't lost the connection, even trying to reconnect + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + // Failure + return -1; + } + + // Attempt to read the reply-type byte + if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + } + + // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { + // Buffer to hold size information + char inbuf[255]; + + // Read up to our newline + if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { + return -1; + } + + // Set our size response + *reply_info = atoi(inbuf); + } + + // Success! + return 0; } /* @@ -1462,152 +1470,152 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int * */ PHPAPI int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC) { - // Buffer to read our single line reply - char inbuf[1024]; - size_t line_size; - - // Attempt to read our single line reply - if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { - return -1; - } - - // If this is an error response, check if it is a SYNC error, and throw in that case - if(reply_type == TYPE_ERR) { - if(memcmp(inbuf, "ERR SYNC", 9) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } - - // Set our last error - redis_sock_set_err(redis_sock, inbuf, line_size); - - // Set our response to FALSE - ZVAL_FALSE(*z_ret); - } else { - // Set our response to TRUE - ZVAL_TRUE(*z_ret); - } - - return 0; + // Buffer to read our single line reply + char inbuf[1024]; + size_t line_size; + + // Attempt to read our single line reply + if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { + return -1; + } + + // If this is an error response, check if it is a SYNC error, and throw in that case + if(reply_type == TYPE_ERR) { + if(memcmp(inbuf, "ERR SYNC", 9) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } + + // Set our last error + redis_sock_set_err(redis_sock, inbuf, line_size); + + // Set our response to FALSE + ZVAL_FALSE(*z_ret); + } else { + // Set our response to TRUE + ZVAL_TRUE(*z_ret); + } + + return 0; } PHPAPI int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) { - // Attempt to read the bulk reply - char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); - - // Set our reply to FALSE on failure, and the string on success - if(bulk_resp == NULL) { - ZVAL_FALSE(*z_ret); - return -1; - } else { - ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); - return 0; - } + // Attempt to read the bulk reply + char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); + + // Set our reply to FALSE on failure, and the string on success + if(bulk_resp == NULL) { + ZVAL_FALSE(*z_ret); + return -1; + } else { + ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); + return 0; + } } PHPAPI int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { - int reply_info; - REDIS_REPLY_TYPE reply_type; - zval *z_subelem; - - // Iterate while we have elements - while(elements > 0) { - // Attempt to read our reply type - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); - return -1; - } - - // Switch on our reply-type byte - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_INT: - // Add our long value - add_next_index_long(*z_ret, reply_info); - break; - case TYPE_BULK: - // Init a zval for our bulk response, read and add it - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_MULTIBULK: - // Construct an array for our sub element, and add it, and recurse - ALLOC_INIT_ZVAL(z_subelem); - array_init(z_subelem); - add_next_index_zval(*z_ret, z_subelem); - redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); - break; - } - - // Decrement our element counter - elements--; - } - - return 0; + int reply_info; + REDIS_REPLY_TYPE reply_type; + zval *z_subelem; + + // Iterate while we have elements + while(elements > 0) { + // Attempt to read our reply type + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); + return -1; + } + + // Switch on our reply-type byte + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_INT: + // Add our long value + add_next_index_long(*z_ret, reply_info); + break; + case TYPE_BULK: + // Init a zval for our bulk response, read and add it + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_MULTIBULK: + // Construct an array for our sub element, and add it, and recurse + ALLOC_INIT_ZVAL(z_subelem); + array_init(z_subelem); + add_next_index_zval(*z_ret, z_subelem); + redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); + break; + } + + // Decrement our element counter + elements--; + } + + return 0; } PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { - // Reply type, and reply size vars - REDIS_REPLY_TYPE reply_type; - int reply_info; - //char *bulk_resp; - zval *z_ret; - - // Attempt to read our header - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - return -1; - } - - // Our return ZVAL - MAKE_STD_ZVAL(z_ret); - - // Switch based on our top level reply type - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); - break; - case TYPE_INT: - ZVAL_LONG(z_ret, reply_info); - break; - case TYPE_BULK: - redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); - break; - case TYPE_MULTIBULK: - // Initialize an array for our multi-bulk response - array_init(z_ret); - - // If we've got more than zero elements, parse our multi bulk respoinse recursively - if(reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); - } - break; - default: - // Protocol error - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); - break; - } - - IF_MULTI_OR_PIPELINE() { - add_next_index_zval(z_tab, z_ret); - } else { - // Set our return value - *return_value = *z_ret; - zval_copy_ctor(return_value); - zval_dtor(z_ret); - efree(z_ret); - } - - // Success - return 0; + // Reply type, and reply size vars + REDIS_REPLY_TYPE reply_type; + int reply_info; + //char *bulk_resp; + zval *z_ret; + + // Attempt to read our header + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { + return -1; + } + + // Our return ZVAL + MAKE_STD_ZVAL(z_ret); + + // Switch based on our top level reply type + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); + break; + case TYPE_INT: + ZVAL_LONG(z_ret, reply_info); + break; + case TYPE_BULK: + redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); + break; + case TYPE_MULTIBULK: + // Initialize an array for our multi-bulk response + array_init(z_ret); + + // If we've got more than zero elements, parse our multi bulk respoinse recursively + if(reply_info > -1) { + redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); + } + break; + default: + // Protocol error + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); + break; + } + + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_ret); + } else { + // Set our return value + *return_value = *z_ret; + zval_copy_ctor(return_value); + zval_dtor(z_ret); + efree(z_ret); + } + + // Success + return 0; } /* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ diff --git a/library.h b/library.h index ea135bfbc5..20eaa08a40 100644 --- a/library.h +++ b/library.h @@ -19,7 +19,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id); +PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval); PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index fd103d30f9..0562a79bbc 100644 --- a/redis.c +++ b/redis.c @@ -520,7 +520,7 @@ PHP_METHOD(Redis,__destruct) { } } -/* {{{ proto boolean Redis::connect(string host, int port [, double timeout]) +/* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]]) */ PHP_METHOD(Redis, connect) { @@ -556,6 +556,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { int host_len, id; char *host = NULL; long port = -1; + long retry_interval = 0; char *persistent_id = NULL; int persistent_id_len = -1; @@ -568,9 +569,10 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { persistent = 0; #endif - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|lds", + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl", &object, redis_ce, &host, &host_len, &port, - &timeout, &persistent_id, &persistent_id_len) == FAILURE) { + &timeout, &persistent_id, &persistent_id_len, + &retry_interval) == FAILURE) { return FAILURE; } @@ -579,6 +581,11 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { return FAILURE; } + if (retry_interval < 0L || retry_interval > INT_MAX) { + zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC); + return FAILURE; + } + if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */ port = 6379; } @@ -595,7 +602,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zend_clear_exception(TSRMLS_C); /* clear exception triggered by non-existent socket during connect(). */ } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval); if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { redis_free_socket(redis_sock); diff --git a/redis_array.c b/redis_array.c index d0460f1021..be95c50d5a 100644 --- a/redis_array.c +++ b/redis_array.c @@ -51,6 +51,7 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, _rehash, NULL, ZEND_ACC_PUBLIC) /* special implementation for a few functions */ + PHP_ME(RedisArray, select, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, info, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mget, NULL, ZEND_ACC_PUBLIC) @@ -192,6 +193,7 @@ PHP_METHOD(RedisArray, __construct) RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; HashTable *hPrev = NULL, *hOpts = NULL; + long l_retry_interval = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { RETURN_FALSE; @@ -232,6 +234,19 @@ PHP_METHOD(RedisArray, __construct) if(FAILURE != zend_hash_find(hOpts, "autorehash", sizeof("autorehash"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { b_autorehash = Z_BVAL_PP(zpData); } + + /* extract retry_interval option. */ + zval **z_retry_interval_pp; + if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) { + if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) { + if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) { + l_retry_interval = Z_LVAL_PP(z_retry_interval_pp); + } + else { + l_retry_interval = atol(Z_STRVAL_PP(z_retry_interval_pp)); + } + } + } } /* extract either name of list of hosts from z0 */ @@ -241,7 +256,7 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); break; default: @@ -688,6 +703,46 @@ PHP_METHOD(RedisArray, setOption) efree(z_args[0]); efree(z_args[1]); } + +PHP_METHOD(RedisArray, select) +{ + zval *object, z_fun, *z_tmp, *z_args[2]; + int i; + RedisArray *ra; + long opt; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, redis_array_ce, &opt) == FAILURE) { + RETURN_FALSE; + } + + if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + RETURN_FALSE; + } + + /* prepare call */ + ZVAL_STRING(&z_fun, "select", 0); + + /* copy args */ + MAKE_STD_ZVAL(z_args[0]); + ZVAL_LONG(z_args[0], opt); + + array_init(return_value); + for(i = 0; i < ra->count; ++i) { + + MAKE_STD_ZVAL(z_tmp); + + /* Call each node in turn */ + call_user_function(&redis_ce->function_table, &ra->redis[i], + &z_fun, z_tmp, 1, z_args TSRMLS_CC); + + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } + + /* cleanup */ + efree(z_args[0]); +} + #define HANDLE_MULTI_EXEC(cmd) do {\ if (redis_array_get(getThis(), &ra TSRMLS_CC) >= 0 && ra->z_multi_exec) {\ int i, num_varargs;\ diff --git a/redis_array.h b/redis_array.h index bc7fdd8842..b2c7d86a50 100644 --- a/redis_array.h +++ b/redis_array.h @@ -15,6 +15,7 @@ PHP_METHOD(RedisArray, _function); PHP_METHOD(RedisArray, _distributor); PHP_METHOD(RedisArray, _rehash); +PHP_METHOD(RedisArray, select); PHP_METHOD(RedisArray, info); PHP_METHOD(RedisArray, ping); PHP_METHOD(RedisArray, mget); diff --git a/redis_array_impl.c b/redis_array_impl.c index d5370c826e..c38e2fe661 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -29,7 +29,7 @@ extern int le_redis_sock; extern zend_class_entry *redis_ce; RedisArray* -ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC) +ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC) { int i, host_len, id; int count = zend_hash_num_elements(hosts); @@ -67,7 +67,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC) call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC); /* create socket */ - redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL); /* TODO: persistence? */ + redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL, retry_interval); /* TODO: persistence? */ /* connect */ redis_sock_server_open(redis_sock, 1 TSRMLS_CC); @@ -158,9 +158,11 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval *z_params_funs, **z_data_pp, *z_fun = NULL, *z_dist = NULL; zval *z_params_index; zval *z_params_autorehash; + zval *z_params_retry_interval; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; + long l_retry_interval = 0; HashTable *hHosts = NULL, *hPrev = NULL; /* find entry */ @@ -223,8 +225,23 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } + /* find retry interval option */ + MAKE_STD_ZVAL(z_params_retry_interval); + array_init(z_params_retry_interval); + sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.retryinterval")), z_params_retry_interval TSRMLS_CC); + if (zend_hash_find(Z_ARRVAL_P(z_params_retry_interval), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if (Z_TYPE_PP(z_data_pp) == IS_LONG || Z_TYPE_PP(z_data_pp) == IS_STRING) { + if (Z_TYPE_PP(z_data_pp) == IS_LONG) { + l_retry_interval = Z_LVAL_PP(z_data_pp); + } + else { + l_retry_interval = atol(Z_STRVAL_PP(z_data_pp)); + } + } + } + /* create RedisArray object */ - ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index TSRMLS_CC); + ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); ra->auto_rehash = b_autorehash; /* cleanup */ @@ -238,12 +255,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { efree(z_params_index); zval_dtor(z_params_autorehash); efree(z_params_autorehash); + zval_dtor(z_params_retry_interval); + efree(z_params_retry_interval); return ra; } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC) { int count = zend_hash_num_elements(hosts); @@ -261,10 +280,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* init array data structures */ ra_init_function_table(ra); - if(NULL == ra_load_hosts(ra, hosts TSRMLS_CC)) { + if(NULL == ra_load_hosts(ra, hosts, retry_interval TSRMLS_CC)) { return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval TSRMLS_CC) : NULL; /* copy function if provided */ if(z_fun) { @@ -1112,7 +1131,7 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z zval *z_host, *z_count; z_cb->retval_ptr_ptr = &z_ret; - z_cb->params = &z_args; + z_cb->params = (struct _zval_struct ***)&z_args; z_cb->param_count = 2; z_cb->no_separation = 0; diff --git a/redis_array_impl.h b/redis_array_impl.h index 0e00258c3b..8dd5201bc8 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -5,9 +5,9 @@ #include "common.h" #include "redis_array.h" -RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC); +RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); diff --git a/redis_session.c b/redis_session.c index 95d9053302..009bac51b2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -206,6 +206,7 @@ PS_OPEN_FUNC(redis) int persistent = 0; int database = -1; char *prefix = NULL, *auth = NULL, *persistent_id = NULL; + long retry_interval = 0; /* translate unix: into file: */ if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) { @@ -240,7 +241,6 @@ PS_OPEN_FUNC(redis) convert_to_long_ex(param); weight = Z_LVAL_PP(param); } - if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { timeout = atof(Z_STRVAL_PP(param)); } @@ -260,13 +260,10 @@ PS_OPEN_FUNC(redis) convert_to_long_ex(param); database = Z_LVAL_PP(param); } - - /* // not supported yet if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { convert_to_long_ex(param); retry_interval = Z_LVAL_PP(param); } - */ zval_ptr_dtor(¶ms); } @@ -280,9 +277,9 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if(url->host) { - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval); } else { /* unix */ - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval); } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); From 6091727ba363e5afe7d6d373349fc7dd55affff7 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Wed, 23 Jan 2013 01:57:51 +0200 Subject: [PATCH 0008/1986] New select DB command to RedisArray - Added retry delay on reconnect Added the possibility to delay each reconnection attempt, including a random factor to prevent several or many concurrent connections from trying to reconnect at the same time. Added the select command to RedisArray to select a DB on every connections in one instruction. Also, fixed a compiler warning: redis_array_impl.c:1115:15: warning: incompatible pointer types assigning to 'zval **' (aka 'struct _zval_struct **') from 'zval **(*)[2]' [-Wincompatible-pointer-types] --- library.c | 1486 ++++++++++++++++++++++++++--------------------------- 1 file changed, 743 insertions(+), 743 deletions(-) diff --git a/library.c b/library.c index 15e71e5a1f..be3907f481 100644 --- a/library.c +++ b/library.c @@ -26,83 +26,83 @@ extern zend_class_entry *redis_exception_ce; extern zend_class_entry *spl_ce_RuntimeException; PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { - if (!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } else { - php_stream_pclose(redis_sock->stream); - } + if (!redis_sock->persistent) { + php_stream_close(redis_sock->stream); + } else { + php_stream_pclose(redis_sock->stream); + } } PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) { - int eof; - int count = 0; - - if (!redis_sock->stream) { - return -1; - } - - eof = php_stream_eof(redis_sock->stream); - for (; eof; count++) { - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ - if(redis_sock->stream) { /* close stream if still here */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->watching = 0; - } - zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); - return -1; - } - if(redis_sock->stream) { /* close existing stream before reconnecting */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; - } + int eof; + int count = 0; + + if (!redis_sock->stream) { + return -1; + } + + eof = php_stream_eof(redis_sock->stream); + for (; eof; count++) { + if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ + if(redis_sock->stream) { /* close stream if still here */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->watching = 0; + } + zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); + return -1; + } + if(redis_sock->stream) { /* close existing stream before reconnecting */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; + } // Wait for a while before trying to reconnect if (redis_sock->retry_interval) { // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval)); usleep(retry_interval); } - redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ - if(redis_sock->stream) { /* check for EOF again. */ - eof = php_stream_eof(redis_sock->stream); + redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ + if(redis_sock->stream) { /* check for EOF again. */ + eof = php_stream_eof(redis_sock->stream); + } } - } // Reselect the DB. - if (count && redis_sock->dbNumber) { - char *cmd, *response; - int cmd_len, response_len; + if (count && redis_sock->dbNumber) { + char *cmd, *response; + int cmd_len, response_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return -1; - } - efree(cmd); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return -1; + } + efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - return -1; - } + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return -1; + } - if (strncmp(response, "+OK", 3)) { - efree(response); - return -1; + if (strncmp(response, "+OK", 3)) { + efree(response); + return -1; + } + efree(response); } - efree(response); - } - return 0; + return 0; } PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char inbuf[1024]; - int numElems; + int numElems; zval *z_tab; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { @@ -110,7 +110,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -129,7 +129,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, numElems, 1, UNSERIALIZE_ALL); - return z_tab; + return z_tab; } /** @@ -152,13 +152,13 @@ PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_ char c; int i; - reply = emalloc(bytes+1); + reply = emalloc(bytes+1); while(offset < bytes) { got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); if (got <= 0) { /* Error or EOF */ - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); break; } offset += got; @@ -186,7 +186,7 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -197,12 +197,12 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) switch(inbuf[0]) { case '-': - err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); - /* stale data */ - if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } + err_len = strlen(inbuf+1) - 2; + redis_sock_set_err(redis_sock, inbuf+1, err_len); + /* stale data */ + if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } return NULL; case '$': @@ -219,7 +219,7 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) case '+': case ':': - // Single Line Reply + // Single Line Reply /* :123\r\n */ *buf_len = strlen(inbuf) - 2; if(*buf_len >= 2) { @@ -230,12 +230,12 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) } default: - zend_throw_exception_ex( - redis_exception_ce, - 0 TSRMLS_CC, - "protocol error, got '%c' as reply type byte\n", - inbuf[0] - ); + zend_throw_exception_ex( + redis_exception_ce, + 0 TSRMLS_CC, + "protocol error, got '%c' as reply type byte\n", + inbuf[0] + ); } return NULL; @@ -252,42 +252,42 @@ void add_constant_long(zend_class_entry *ce, char *name, int value) { int integer_length(int i) { - int sz = 0; - int ci = abs(i); - while (ci > 0) { - ci /= 10; - sz++; - } - if (i == 0) { /* log 0 doesn't make sense. */ - sz = 1; - } else if (i < 0) { /* allow for neg sign as well. */ - sz++; - } - return sz; + int sz = 0; + int ci = abs(i); + while (ci > 0) { + ci /= 10; + sz++; + } + if (i == 0) { /* log 0 doesn't make sense. */ + sz = 1; + } else if (i < 0) { /* allow for neg sign as well. */ + sz++; + } + return sz; } int redis_cmd_format_header(char **ret, char *keyword, int arg_count) { - // Our return buffer - smart_str buf = {0}; - - // Keyword length - int l = strlen(keyword); - - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, arg_count + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - // Set our return pointer - *ret = buf.c; - - // Return the length - return buf.len; + // Our return buffer + smart_str buf = {0}; + + // Keyword length + int l = strlen(keyword); + + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, arg_count + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + // Set our return pointer + *ret = buf.c; + + // Return the length + return buf.len; } int @@ -297,74 +297,74 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { va_list ap; smart_str buf = {0}; int l = strlen(keyword); - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - /* add header */ - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, strlen(format) + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - while (*p) { - smart_str_appendc(&buf, '$'); - - switch(*p) { - case 's': { - char *val = va_arg(ap, char*); - int val_len = va_arg(ap, int); - smart_str_append_long(&buf, val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, val, val_len); - } - break; - - case 'f': - case 'F': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'i': - case 'd': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - case 'l': - case 'L': { - long l = va_arg(ap, long); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - p++; - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - } - smart_str_0(&buf); - - *ret = buf.c; - - return buf.len; + char *dbl_str; + int dbl_len; + + va_start(ap, format); + + /* add header */ + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, strlen(format) + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + while (*p) { + smart_str_appendc(&buf, '$'); + + switch(*p) { + case 's': { + char *val = va_arg(ap, char*); + int val_len = va_arg(ap, int); + smart_str_append_long(&buf, val_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, val, val_len); + } + break; + + case 'f': + case 'F': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'i': + case 'd': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + case 'l': + case 'L': { + long l = va_arg(ap, long); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + p++; + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + } + smart_str_0(&buf); + + *ret = buf.c; + + return buf.len; } /** @@ -375,119 +375,119 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { int redis_cmd_format(char **ret, char *format, ...) { - smart_str buf = {0}; - va_list ap; - char *p = format; - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - while (*p) { - if (*p == '%') { - switch (*(++p)) { - case 's': { - char *tmp = va_arg(ap, char*); - int tmp_len = va_arg(ap, int); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - - case 'F': - case 'f': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'd': - case 'i': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - } else { - smart_str_appendc(&buf, *p); - } - - p++; - } - - smart_str_0(&buf); - - *ret = buf.c; - - return buf.len; + smart_str buf = {0}; + va_list ap; + char *p = format; + char *dbl_str; + int dbl_len; + + va_start(ap, format); + + while (*p) { + if (*p == '%') { + switch (*(++p)) { + case 's': { + char *tmp = va_arg(ap, char*); + int tmp_len = va_arg(ap, int); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + + case 'F': + case 'f': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'd': + case 'i': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + } else { + smart_str_appendc(&buf, *p); + } + + p++; + } + + smart_str_0(&buf); + + *ret = buf.c; + + return buf.len; } /* * Append a command sequence to a Redis command */ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) { - // Smart string buffer - smart_str buf = {0}; + // Smart string buffer + smart_str buf = {0}; - // Append the current command to our smart_str - smart_str_appendl(&buf, *cmd, cmd_len); + // Append the current command to our smart_str + smart_str_appendl(&buf, *cmd, cmd_len); - // Append our new command sequence - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, append, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + // Append our new command sequence + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, append, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - // Free our old command - efree(*cmd); + // Free our old command + efree(*cmd); - // Set our return pointer - *cmd = buf.c; + // Set our return pointer + *cmd = buf.c; - // Return new command length - return buf.len; + // Return new command length + return buf.len; } /* * Append an integer command to a Redis command */ int redis_cmd_append_int(char **cmd, int cmd_len, int append) { - char int_buf[32]; + char int_buf[32]; - // Conver to an int, capture length - int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); + // Conver to an int, capture length + int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); - // Return the new length - return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); + // Return the new length + return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); } PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - double ret; + double ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } - return; + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } + return; } ret = atof(response); efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_double(z_tab, ret); + add_next_index_double(z_tab, ret); } else { - RETURN_DOUBLE(ret); + RETURN_DOUBLE(ret); } } @@ -497,41 +497,41 @@ PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s long l; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } } if (strncmp(response, "+string", 7) == 0) { - l = REDIS_STRING; + l = REDIS_STRING; } else if (strncmp(response, "+set", 4) == 0){ - l = REDIS_SET; + l = REDIS_SET; } else if (strncmp(response, "+list", 5) == 0){ - l = REDIS_LIST; + l = REDIS_LIST; } else if (strncmp(response, "+zset", 5) == 0){ - l = REDIS_ZSET; + l = REDIS_ZSET; } else if (strncmp(response, "+hash", 5) == 0){ - l = REDIS_HASH; + l = REDIS_HASH; } else { - l = REDIS_NOT_FOUND; + l = REDIS_NOT_FOUND; } efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_long(z_tab, l); + add_next_index_long(z_tab, l); } else { - RETURN_LONG(l); + RETURN_LONG(l); } } PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - char *pos, *cur; - char *key, *value, *p; - int is_numeric; + char *pos, *cur; + char *key, *value, *p; + int is_numeric; zval *z_multi_result; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { @@ -547,13 +547,13 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s cur = response; while(1) { - /* skip comments and empty lines */ - if(*cur == '#' || *cur == '\r') { - if(!(cur = strchr(cur, '\n'))) - break; - cur++; - continue; - } + /* skip comments and empty lines */ + if(*cur == '#' || *cur == '\r') { + if(!(cur = strchr(cur, '\n'))) + break; + cur++; + continue; + } /* key */ pos = strchr(cur, ':'); @@ -608,10 +608,10 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock char ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; - } + return; + } RETURN_FALSE; } ret = response[0]; @@ -627,15 +627,15 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_bool(z_tab, 0); } } else { - if (ret == '+') { + if (ret == '+') { if (success_callback != NULL) { success_callback(redis_sock); } - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -648,38 +648,38 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s int response_len; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } } if(response[0] == ':') { long long ret = atoll(response + 1); IF_MULTI_OR_PIPELINE() { - if(ret > LONG_MAX) { /* overflow */ - add_next_index_stringl(z_tab, response+1, response_len-1, 1); - } else { - efree(response); - add_next_index_long(z_tab, (long)ret); - } + if(ret > LONG_MAX) { /* overflow */ + add_next_index_stringl(z_tab, response+1, response_len-1, 1); + } else { + efree(response); + add_next_index_long(z_tab, (long)ret); + } } else { - if(ret > LONG_MAX) { /* overflow */ - RETURN_STRINGL(response+1, response_len-1, 1); - } else { - efree(response); - RETURN_LONG((long)ret); - } - } + if(ret > LONG_MAX) { /* overflow */ + RETURN_STRINGL(response+1, response_len-1, 1); + } else { + efree(response); + RETURN_LONG((long)ret); + } + } } else { efree(response); IF_MULTI_OR_PIPELINE() { add_next_index_null(z_tab); } else { - RETURN_FALSE; - } + RETURN_FALSE; + } } } @@ -687,20 +687,20 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) { - /* - int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); - array_zip_values_and_scores(return_value, 0); - */ + /* + int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); + array_zip_values_and_scores(return_value, 0); + */ char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -724,10 +724,10 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); } return 0; @@ -735,43 +735,43 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); + return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); } PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); + return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); } PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - char *response; - int response_len; - char ret; - - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } - } - ret = response[1]; - efree(response); - - IF_MULTI_OR_PIPELINE() { - if(ret == '1') { - add_next_index_bool(z_tab, 1); - } else { - add_next_index_bool(z_tab, 0); - } - } else { - if (ret == '1') { - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + char *response; + int response_len; + char ret; + + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } + } + ret = response[1]; + efree(response); + + IF_MULTI_OR_PIPELINE() { + if(ret == '1') { + add_next_index_bool(z_tab, 1); + } else { + add_next_index_bool(z_tab, 0); + } + } else { + if (ret == '1') { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -782,25 +782,25 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { - if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { - RETURN_STRINGL(response, response_len, 0); - } else { - efree(response); - } - } + if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { + RETURN_STRINGL(response, response_len, 0); + } else { + efree(response); + } + } } /* like string response, but never unserialized. */ @@ -812,15 +812,15 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - add_next_index_stringl(z_tab, response, response_len, 0); + add_next_index_stringl(z_tab, response, response_len, 0); } else { - RETURN_STRINGL(response, response_len, 0); - } + RETURN_STRINGL(response, response_len, 0); + } } @@ -843,7 +843,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por redis_sock->persistent = persistent; if(persistent_id) { - size_t persistent_id_len = strlen(persistent_id); + size_t persistent_id_len = strlen(persistent_id); redis_sock->persistent_id = ecalloc(persistent_id_len + 1, 1); memcpy(redis_sock->persistent_id, persistent_id, persistent_id_len); } else { @@ -877,8 +877,8 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) struct timeval tv, *tv_ptr = NULL; char *host = NULL, *persistent_id = NULL, *errstr = NULL; int host_len, err = 0; - php_netstream_data_t *sock; - int tcp_flag = 1; + php_netstream_data_t *sock; + int tcp_flag = 1; if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock TSRMLS_CC); @@ -887,15 +887,15 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) tv.tv_sec = (time_t)redis_sock->timeout; tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); if(tv.tv_sec != 0 || tv.tv_usec != 0) { - tv_ptr = &tv; + tv_ptr = &tv; } if(redis_sock->host[0] == '/' && redis_sock->port < 1) { - host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); + host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); } else { - if(redis_sock->port == 0) - redis_sock->port = 6379; - host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); + if(redis_sock->port == 0) + redis_sock->port = 6379; + host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); } if (redis_sock->persistent) { @@ -907,10 +907,10 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } redis_sock->stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE, - STREAM_XPORT_CLIENT - | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &errstr, &err - ); + STREAM_XPORT_CLIENT + | STREAM_XPORT_CONNECT, + persistent_id, tv_ptr, NULL, &errstr, &err + ); if (persistent_id) { efree(persistent_id); @@ -924,7 +924,7 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } /* set TCP_NODELAY */ - sock = (php_netstream_data_t*)redis_sock->stream->abstract; + sock = (php_netstream_data_t*)redis_sock->stream->abstract; setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); php_stream_auto_cleanup(redis_sock->stream); @@ -975,23 +975,23 @@ PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRML PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) { if (redis_sock == NULL) { - return 1; + return 1; } redis_sock->dbNumber = 0; if (redis_sock->stream != NULL) { - if (!redis_sock->persistent) { - redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); - } + if (!redis_sock->persistent) { + redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); + } - redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; + redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; - if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ - php_stream_close(redis_sock->stream); - } - redis_sock->stream = NULL; + if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ + php_stream_close(redis_sock->stream); + } + redis_sock->stream = NULL; - return 1; + return 1; } return 0; @@ -1000,8 +1000,8 @@ PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *cmd; - int response_len, cmd_len; - char * response; + int response_len, cmd_len; + char * response; cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); @@ -1015,41 +1015,41 @@ PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_so RETURN_FALSE; } - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { - RETURN_TRUE; - } - RETURN_FALSE; + if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + RETURN_TRUE; + } + RETURN_FALSE; } /** * redis_sock_set_err */ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { - // Allocate/Reallocate our last error member - if(msg != NULL && msg_len > 0) { - if(redis_sock->err == NULL) { - redis_sock->err = emalloc(msg_len + 1); - } else if(msg_len > redis_sock->err_len) { - redis_sock->err = erealloc(redis_sock->err, msg_len +1); - } - - // Copy in our new error message, set new length, and null terminate - memcpy(redis_sock->err, msg, msg_len); - redis_sock->err[msg_len] = '\0'; - redis_sock->err_len = msg_len; - } else { - // Free our last error - if(redis_sock->err != NULL) { - efree(redis_sock->err); - } - - // Set to null, with zero length - redis_sock->err = NULL; - redis_sock->err_len = 0; - } - - // Success - return 0; + // Allocate/Reallocate our last error member + if(msg != NULL && msg_len > 0) { + if(redis_sock->err == NULL) { + redis_sock->err = emalloc(msg_len + 1); + } else if(msg_len > redis_sock->err_len) { + redis_sock->err = erealloc(redis_sock->err, msg_len +1); + } + + // Copy in our new error message, set new length, and null terminate + memcpy(redis_sock->err, msg, msg_len); + redis_sock->err[msg_len] = '\0'; + redis_sock->err_len = msg_len; + } else { + // Free our last error + if(redis_sock->err != NULL) { + efree(redis_sock->err); + } + + // Set to null, with zero length + redis_sock->err = NULL; + redis_sock->err_len = 0; + } + + // Success + return 0; } /** @@ -1058,14 +1058,14 @@ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_le PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1100,14 +1100,14 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1146,17 +1146,17 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re while(numElems > 0) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - int can_unserialize = unwrap_key; - if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) - can_unserialize = 0; - - if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + zval *z = NULL; + int can_unserialize = unwrap_key; + if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) + can_unserialize = 0; + + if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { add_next_index_bool(z_tab, 0); } @@ -1172,8 +1172,8 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R { char inbuf[1024], *response; int response_len; - int i, numElems; - zval *z_multi_result; + int i, numElems; + zval *z_multi_result; zval **z_keys = ctx; @@ -1181,7 +1181,7 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1200,30 +1200,30 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); - } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); - } - } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); - } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + } + } else { + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + } + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - INIT_PZVAL(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); - } + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + INIT_PZVAL(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); + } return 0; } @@ -1232,10 +1232,10 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R */ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { - if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { - zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); - return -1; - } + if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { + zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); + return -1; + } if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } @@ -1248,10 +1248,10 @@ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_D PHPAPI void redis_free_socket(RedisSock *redis_sock) { if(redis_sock->prefix) { - efree(redis_sock->prefix); - } + efree(redis_sock->prefix); + } if(redis_sock->err) { - efree(redis_sock->err); + efree(redis_sock->err); } efree(redis_sock->host); efree(redis_sock); @@ -1260,146 +1260,146 @@ PHPAPI void redis_free_socket(RedisSock *redis_sock) PHPAPI int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC) { #if ZEND_MODULE_API_NO >= 20100000 - php_serialize_data_t ht; + php_serialize_data_t ht; #else - HashTable ht; + HashTable ht; #endif - smart_str sstr = {0}; - zval *z_copy; - size_t sz; - uint8_t *val8; - - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - switch(Z_TYPE_P(z)) { - - case IS_STRING: - *val = Z_STRVAL_P(z); - *val_len = Z_STRLEN_P(z); - return 0; - - case IS_OBJECT: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Object", 6, 1); - break; - - case IS_ARRAY: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Array", 5, 1); - break; - - default: /* copy */ - MAKE_STD_ZVAL(z_copy); - *z_copy = *z; - zval_copy_ctor(z_copy); - break; - } - - /* return string */ - convert_to_string(z_copy); - *val = Z_STRVAL_P(z_copy); - *val_len = Z_STRLEN_P(z_copy); - efree(z_copy); - return 1; - - case REDIS_SERIALIZER_PHP: + smart_str sstr = {0}; + zval *z_copy; + size_t sz; + uint8_t *val8; + + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + switch(Z_TYPE_P(z)) { + + case IS_STRING: + *val = Z_STRVAL_P(z); + *val_len = Z_STRLEN_P(z); + return 0; + + case IS_OBJECT: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Object", 6, 1); + break; + + case IS_ARRAY: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Array", 5, 1); + break; + + default: /* copy */ + MAKE_STD_ZVAL(z_copy); + *z_copy = *z; + zval_copy_ctor(z_copy); + break; + } + + /* return string */ + convert_to_string(z_copy); + *val = Z_STRVAL_P(z_copy); + *val_len = Z_STRLEN_P(z_copy); + efree(z_copy); + return 1; + + case REDIS_SERIALIZER_PHP: #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_INIT(ht); + PHP_VAR_SERIALIZE_INIT(ht); #else - zend_hash_init(&ht, 10, NULL, NULL, 0); + zend_hash_init(&ht, 10, NULL, NULL, 0); #endif - php_var_serialize(&sstr, &z, &ht TSRMLS_CC); - *val = sstr.c; - *val_len = (int)sstr.len; + php_var_serialize(&sstr, &z, &ht TSRMLS_CC); + *val = sstr.c; + *val_len = (int)sstr.len; #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_DESTROY(ht); + PHP_VAR_SERIALIZE_DESTROY(ht); #else - zend_hash_destroy(&ht); + zend_hash_destroy(&ht); #endif - return 1; + return 1; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ - *val = (char*)val8; - *val_len = (int)sz; - return 1; - } + if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ + *val = (char*)val8; + *val_len = (int)sz; + return 1; + } #endif - return 0; - } - return 0; + return 0; + } + return 0; } PHPAPI int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) { - php_unserialize_data_t var_hash; - int ret; + php_unserialize_data_t var_hash; + int ret; - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - return 0; + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + return 0; - case REDIS_SERIALIZER_PHP: - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - } + case REDIS_SERIALIZER_PHP: + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_INIT(var_hash); + PHP_VAR_UNSERIALIZE_INIT(var_hash); #else - memset(&var_hash, 0, sizeof(var_hash)); + memset(&var_hash, 0, sizeof(var_hash)); #endif - if(!php_var_unserialize(return_value, (const unsigned char**)&val, - (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { - efree(*return_value); - ret = 0; - } else { - ret = 1; - } + if(!php_var_unserialize(return_value, (const unsigned char**)&val, + (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { + efree(*return_value); + ret = 0; + } else { + ret = 1; + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); #else - var_destroy(&var_hash); + var_destroy(&var_hash); #endif - return ret; + return ret; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - } - if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { - return 1; - } - efree(*return_value); + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + } + if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { + return 1; + } + efree(*return_value); #endif - return 0; - break; - } - return 0; + return 0; + break; + } + return 0; } PHPAPI int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { - int ret_len; - char *ret; - - if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { - return 0; - } - - ret_len = redis_sock->prefix_len + *key_len; - ret = ecalloc(1 + ret_len, 1); - memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); - memcpy(ret + redis_sock->prefix_len, *key, *key_len); - - *key = ret; - *key_len = ret_len; - return 1; + int ret_len; + char *ret; + + if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { + return 0; + } + + ret_len = redis_sock->prefix_len + *key_len; + ret = ecalloc(1 + ret_len, 1); + memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); + memcpy(ret + redis_sock->prefix_len, *key, *key_len); + + *key = ret; + *key_len = ret_len; + return 1; } /* @@ -1409,60 +1409,60 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { PHPAPI int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) { // Handle EOF - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } - if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { - // Close, put our socket state into error - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { + // Close, put our socket state into error + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; - // Throw a read error exception - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); - } + // Throw a read error exception + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + } - // We don't need \r\n - *line_size-=2; - buf[*line_size]='\0'; + // We don't need \r\n + *line_size-=2; + buf[*line_size]='\0'; - // Success! - return 0; + // Success! + return 0; } PHPAPI int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) { - // Make sure we haven't lost the connection, even trying to reconnect - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { - // Failure - return -1; - } - - // Attempt to read the reply-type byte - if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); - } - - // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here - if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { - // Buffer to hold size information - char inbuf[255]; - - // Read up to our newline - if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { - return -1; - } - - // Set our size response - *reply_info = atoi(inbuf); - } - - // Success! - return 0; + // Make sure we haven't lost the connection, even trying to reconnect + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + // Failure + return -1; + } + + // Attempt to read the reply-type byte + if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + } + + // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { + // Buffer to hold size information + char inbuf[255]; + + // Read up to our newline + if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { + return -1; + } + + // Set our size response + *reply_info = atoi(inbuf); + } + + // Success! + return 0; } /* @@ -1470,152 +1470,152 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int * */ PHPAPI int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC) { - // Buffer to read our single line reply - char inbuf[1024]; - size_t line_size; - - // Attempt to read our single line reply - if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { - return -1; - } - - // If this is an error response, check if it is a SYNC error, and throw in that case - if(reply_type == TYPE_ERR) { - if(memcmp(inbuf, "ERR SYNC", 9) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } - - // Set our last error - redis_sock_set_err(redis_sock, inbuf, line_size); - - // Set our response to FALSE - ZVAL_FALSE(*z_ret); - } else { - // Set our response to TRUE - ZVAL_TRUE(*z_ret); - } - - return 0; + // Buffer to read our single line reply + char inbuf[1024]; + size_t line_size; + + // Attempt to read our single line reply + if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { + return -1; + } + + // If this is an error response, check if it is a SYNC error, and throw in that case + if(reply_type == TYPE_ERR) { + if(memcmp(inbuf, "ERR SYNC", 9) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } + + // Set our last error + redis_sock_set_err(redis_sock, inbuf, line_size); + + // Set our response to FALSE + ZVAL_FALSE(*z_ret); + } else { + // Set our response to TRUE + ZVAL_TRUE(*z_ret); + } + + return 0; } PHPAPI int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) { - // Attempt to read the bulk reply - char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); - - // Set our reply to FALSE on failure, and the string on success - if(bulk_resp == NULL) { - ZVAL_FALSE(*z_ret); - return -1; - } else { - ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); - return 0; - } + // Attempt to read the bulk reply + char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); + + // Set our reply to FALSE on failure, and the string on success + if(bulk_resp == NULL) { + ZVAL_FALSE(*z_ret); + return -1; + } else { + ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); + return 0; + } } PHPAPI int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { - int reply_info; - REDIS_REPLY_TYPE reply_type; - zval *z_subelem; - - // Iterate while we have elements - while(elements > 0) { - // Attempt to read our reply type - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); - return -1; - } - - // Switch on our reply-type byte - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_INT: - // Add our long value - add_next_index_long(*z_ret, reply_info); - break; - case TYPE_BULK: - // Init a zval for our bulk response, read and add it - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_MULTIBULK: - // Construct an array for our sub element, and add it, and recurse - ALLOC_INIT_ZVAL(z_subelem); - array_init(z_subelem); - add_next_index_zval(*z_ret, z_subelem); - redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); - break; - } - - // Decrement our element counter - elements--; - } - - return 0; + int reply_info; + REDIS_REPLY_TYPE reply_type; + zval *z_subelem; + + // Iterate while we have elements + while(elements > 0) { + // Attempt to read our reply type + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); + return -1; + } + + // Switch on our reply-type byte + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_INT: + // Add our long value + add_next_index_long(*z_ret, reply_info); + break; + case TYPE_BULK: + // Init a zval for our bulk response, read and add it + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_MULTIBULK: + // Construct an array for our sub element, and add it, and recurse + ALLOC_INIT_ZVAL(z_subelem); + array_init(z_subelem); + add_next_index_zval(*z_ret, z_subelem); + redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); + break; + } + + // Decrement our element counter + elements--; + } + + return 0; } PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { - // Reply type, and reply size vars - REDIS_REPLY_TYPE reply_type; - int reply_info; - //char *bulk_resp; - zval *z_ret; - - // Attempt to read our header - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - return -1; - } - - // Our return ZVAL - MAKE_STD_ZVAL(z_ret); - - // Switch based on our top level reply type - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); - break; - case TYPE_INT: - ZVAL_LONG(z_ret, reply_info); - break; - case TYPE_BULK: - redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); - break; - case TYPE_MULTIBULK: - // Initialize an array for our multi-bulk response - array_init(z_ret); - - // If we've got more than zero elements, parse our multi bulk respoinse recursively - if(reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); - } - break; - default: - // Protocol error - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); - break; - } - - IF_MULTI_OR_PIPELINE() { - add_next_index_zval(z_tab, z_ret); - } else { - // Set our return value - *return_value = *z_ret; - zval_copy_ctor(return_value); - zval_dtor(z_ret); - efree(z_ret); - } - - // Success - return 0; + // Reply type, and reply size vars + REDIS_REPLY_TYPE reply_type; + int reply_info; + //char *bulk_resp; + zval *z_ret; + + // Attempt to read our header + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { + return -1; + } + + // Our return ZVAL + MAKE_STD_ZVAL(z_ret); + + // Switch based on our top level reply type + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); + break; + case TYPE_INT: + ZVAL_LONG(z_ret, reply_info); + break; + case TYPE_BULK: + redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); + break; + case TYPE_MULTIBULK: + // Initialize an array for our multi-bulk response + array_init(z_ret); + + // If we've got more than zero elements, parse our multi bulk respoinse recursively + if(reply_info > -1) { + redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); + } + break; + default: + // Protocol error + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); + break; + } + + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_ret); + } else { + // Set our return value + *return_value = *z_ret; + zval_copy_ctor(return_value); + zval_dtor(z_ret); + efree(z_ret); + } + + // Success + return 0; } /* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ From e0ee405fe9965692384d5ef9f3c23ff7e57ac046 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Tue, 29 Jan 2013 11:39:13 +0200 Subject: [PATCH 0009/1986] Revert "New select DB command to RedisArray - Added retry delay on reconnect" This reverts commit 6091727ba363e5afe7d6d373349fc7dd55affff7. --- library.c | 1486 ++++++++++++++++++++++++++--------------------------- 1 file changed, 743 insertions(+), 743 deletions(-) diff --git a/library.c b/library.c index be3907f481..15e71e5a1f 100644 --- a/library.c +++ b/library.c @@ -26,83 +26,83 @@ extern zend_class_entry *redis_exception_ce; extern zend_class_entry *spl_ce_RuntimeException; PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { - if (!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } else { - php_stream_pclose(redis_sock->stream); - } + if (!redis_sock->persistent) { + php_stream_close(redis_sock->stream); + } else { + php_stream_pclose(redis_sock->stream); + } } PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) { - int eof; - int count = 0; - - if (!redis_sock->stream) { - return -1; - } - - eof = php_stream_eof(redis_sock->stream); - for (; eof; count++) { - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ - if(redis_sock->stream) { /* close stream if still here */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->watching = 0; - } - zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); - return -1; - } - if(redis_sock->stream) { /* close existing stream before reconnecting */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; - } + int eof; + int count = 0; + + if (!redis_sock->stream) { + return -1; + } + + eof = php_stream_eof(redis_sock->stream); + for (; eof; count++) { + if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ + if(redis_sock->stream) { /* close stream if still here */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->watching = 0; + } + zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); + return -1; + } + if(redis_sock->stream) { /* close existing stream before reconnecting */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; + } // Wait for a while before trying to reconnect if (redis_sock->retry_interval) { // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval)); usleep(retry_interval); } - redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ - if(redis_sock->stream) { /* check for EOF again. */ - eof = php_stream_eof(redis_sock->stream); - } + redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ + if(redis_sock->stream) { /* check for EOF again. */ + eof = php_stream_eof(redis_sock->stream); } + } // Reselect the DB. - if (count && redis_sock->dbNumber) { - char *cmd, *response; - int cmd_len, response_len; + if (count && redis_sock->dbNumber) { + char *cmd, *response; + int cmd_len, response_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return -1; - } - efree(cmd); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return -1; + } + efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - return -1; - } + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return -1; + } - if (strncmp(response, "+OK", 3)) { - efree(response); - return -1; - } - efree(response); + if (strncmp(response, "+OK", 3)) { + efree(response); + return -1; } + efree(response); + } - return 0; + return 0; } PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char inbuf[1024]; - int numElems; + int numElems; zval *z_tab; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { @@ -110,7 +110,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -129,7 +129,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, numElems, 1, UNSERIALIZE_ALL); - return z_tab; + return z_tab; } /** @@ -152,13 +152,13 @@ PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_ char c; int i; - reply = emalloc(bytes+1); + reply = emalloc(bytes+1); while(offset < bytes) { got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); if (got <= 0) { /* Error or EOF */ - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); break; } offset += got; @@ -186,7 +186,7 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -197,12 +197,12 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) switch(inbuf[0]) { case '-': - err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); - /* stale data */ - if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } + err_len = strlen(inbuf+1) - 2; + redis_sock_set_err(redis_sock, inbuf+1, err_len); + /* stale data */ + if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } return NULL; case '$': @@ -219,7 +219,7 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) case '+': case ':': - // Single Line Reply + // Single Line Reply /* :123\r\n */ *buf_len = strlen(inbuf) - 2; if(*buf_len >= 2) { @@ -230,12 +230,12 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) } default: - zend_throw_exception_ex( - redis_exception_ce, - 0 TSRMLS_CC, - "protocol error, got '%c' as reply type byte\n", - inbuf[0] - ); + zend_throw_exception_ex( + redis_exception_ce, + 0 TSRMLS_CC, + "protocol error, got '%c' as reply type byte\n", + inbuf[0] + ); } return NULL; @@ -252,42 +252,42 @@ void add_constant_long(zend_class_entry *ce, char *name, int value) { int integer_length(int i) { - int sz = 0; - int ci = abs(i); - while (ci > 0) { - ci /= 10; - sz++; - } - if (i == 0) { /* log 0 doesn't make sense. */ - sz = 1; - } else if (i < 0) { /* allow for neg sign as well. */ - sz++; - } - return sz; + int sz = 0; + int ci = abs(i); + while (ci > 0) { + ci /= 10; + sz++; + } + if (i == 0) { /* log 0 doesn't make sense. */ + sz = 1; + } else if (i < 0) { /* allow for neg sign as well. */ + sz++; + } + return sz; } int redis_cmd_format_header(char **ret, char *keyword, int arg_count) { - // Our return buffer - smart_str buf = {0}; - - // Keyword length - int l = strlen(keyword); - - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, arg_count + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - // Set our return pointer - *ret = buf.c; - - // Return the length - return buf.len; + // Our return buffer + smart_str buf = {0}; + + // Keyword length + int l = strlen(keyword); + + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, arg_count + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + // Set our return pointer + *ret = buf.c; + + // Return the length + return buf.len; } int @@ -297,74 +297,74 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { va_list ap; smart_str buf = {0}; int l = strlen(keyword); - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - /* add header */ - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, strlen(format) + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - while (*p) { - smart_str_appendc(&buf, '$'); - - switch(*p) { - case 's': { - char *val = va_arg(ap, char*); - int val_len = va_arg(ap, int); - smart_str_append_long(&buf, val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, val, val_len); - } - break; - - case 'f': - case 'F': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'i': - case 'd': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - case 'l': - case 'L': { - long l = va_arg(ap, long); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - p++; - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - } - smart_str_0(&buf); - - *ret = buf.c; - - return buf.len; + char *dbl_str; + int dbl_len; + + va_start(ap, format); + + /* add header */ + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, strlen(format) + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + while (*p) { + smart_str_appendc(&buf, '$'); + + switch(*p) { + case 's': { + char *val = va_arg(ap, char*); + int val_len = va_arg(ap, int); + smart_str_append_long(&buf, val_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, val, val_len); + } + break; + + case 'f': + case 'F': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'i': + case 'd': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + case 'l': + case 'L': { + long l = va_arg(ap, long); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + p++; + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + } + smart_str_0(&buf); + + *ret = buf.c; + + return buf.len; } /** @@ -375,119 +375,119 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { int redis_cmd_format(char **ret, char *format, ...) { - smart_str buf = {0}; - va_list ap; - char *p = format; - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - while (*p) { - if (*p == '%') { - switch (*(++p)) { - case 's': { - char *tmp = va_arg(ap, char*); - int tmp_len = va_arg(ap, int); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - - case 'F': - case 'f': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'd': - case 'i': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - } else { - smart_str_appendc(&buf, *p); - } - - p++; - } - - smart_str_0(&buf); - - *ret = buf.c; - - return buf.len; + smart_str buf = {0}; + va_list ap; + char *p = format; + char *dbl_str; + int dbl_len; + + va_start(ap, format); + + while (*p) { + if (*p == '%') { + switch (*(++p)) { + case 's': { + char *tmp = va_arg(ap, char*); + int tmp_len = va_arg(ap, int); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + + case 'F': + case 'f': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'd': + case 'i': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + } else { + smart_str_appendc(&buf, *p); + } + + p++; + } + + smart_str_0(&buf); + + *ret = buf.c; + + return buf.len; } /* * Append a command sequence to a Redis command */ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) { - // Smart string buffer - smart_str buf = {0}; + // Smart string buffer + smart_str buf = {0}; - // Append the current command to our smart_str - smart_str_appendl(&buf, *cmd, cmd_len); + // Append the current command to our smart_str + smart_str_appendl(&buf, *cmd, cmd_len); - // Append our new command sequence - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, append, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + // Append our new command sequence + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, append, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - // Free our old command - efree(*cmd); + // Free our old command + efree(*cmd); - // Set our return pointer - *cmd = buf.c; + // Set our return pointer + *cmd = buf.c; - // Return new command length - return buf.len; + // Return new command length + return buf.len; } /* * Append an integer command to a Redis command */ int redis_cmd_append_int(char **cmd, int cmd_len, int append) { - char int_buf[32]; + char int_buf[32]; - // Conver to an int, capture length - int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); + // Conver to an int, capture length + int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); - // Return the new length - return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); + // Return the new length + return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); } PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - double ret; + double ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } - return; + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } + return; } ret = atof(response); efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_double(z_tab, ret); + add_next_index_double(z_tab, ret); } else { - RETURN_DOUBLE(ret); + RETURN_DOUBLE(ret); } } @@ -497,41 +497,41 @@ PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s long l; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } } if (strncmp(response, "+string", 7) == 0) { - l = REDIS_STRING; + l = REDIS_STRING; } else if (strncmp(response, "+set", 4) == 0){ - l = REDIS_SET; + l = REDIS_SET; } else if (strncmp(response, "+list", 5) == 0){ - l = REDIS_LIST; + l = REDIS_LIST; } else if (strncmp(response, "+zset", 5) == 0){ - l = REDIS_ZSET; + l = REDIS_ZSET; } else if (strncmp(response, "+hash", 5) == 0){ - l = REDIS_HASH; + l = REDIS_HASH; } else { - l = REDIS_NOT_FOUND; + l = REDIS_NOT_FOUND; } efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_long(z_tab, l); + add_next_index_long(z_tab, l); } else { - RETURN_LONG(l); + RETURN_LONG(l); } } PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - char *pos, *cur; - char *key, *value, *p; - int is_numeric; + char *pos, *cur; + char *key, *value, *p; + int is_numeric; zval *z_multi_result; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { @@ -547,13 +547,13 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s cur = response; while(1) { - /* skip comments and empty lines */ - if(*cur == '#' || *cur == '\r') { - if(!(cur = strchr(cur, '\n'))) - break; - cur++; - continue; - } + /* skip comments and empty lines */ + if(*cur == '#' || *cur == '\r') { + if(!(cur = strchr(cur, '\n'))) + break; + cur++; + continue; + } /* key */ pos = strchr(cur, ':'); @@ -608,10 +608,10 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock char ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; - } + return; + } RETURN_FALSE; } ret = response[0]; @@ -627,15 +627,15 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_bool(z_tab, 0); } } else { - if (ret == '+') { + if (ret == '+') { if (success_callback != NULL) { success_callback(redis_sock); } - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -648,38 +648,38 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s int response_len; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } } if(response[0] == ':') { long long ret = atoll(response + 1); IF_MULTI_OR_PIPELINE() { - if(ret > LONG_MAX) { /* overflow */ - add_next_index_stringl(z_tab, response+1, response_len-1, 1); - } else { - efree(response); - add_next_index_long(z_tab, (long)ret); - } + if(ret > LONG_MAX) { /* overflow */ + add_next_index_stringl(z_tab, response+1, response_len-1, 1); + } else { + efree(response); + add_next_index_long(z_tab, (long)ret); + } } else { - if(ret > LONG_MAX) { /* overflow */ - RETURN_STRINGL(response+1, response_len-1, 1); - } else { - efree(response); - RETURN_LONG((long)ret); - } - } + if(ret > LONG_MAX) { /* overflow */ + RETURN_STRINGL(response+1, response_len-1, 1); + } else { + efree(response); + RETURN_LONG((long)ret); + } + } } else { efree(response); IF_MULTI_OR_PIPELINE() { add_next_index_null(z_tab); } else { - RETURN_FALSE; - } + RETURN_FALSE; + } } } @@ -687,20 +687,20 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) { - /* - int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); - array_zip_values_and_scores(return_value, 0); - */ + /* + int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); + array_zip_values_and_scores(return_value, 0); + */ char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -724,10 +724,10 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); } return 0; @@ -735,43 +735,43 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); + return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); } PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); + return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); } PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - char *response; - int response_len; - char ret; - - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } - } - ret = response[1]; - efree(response); - - IF_MULTI_OR_PIPELINE() { - if(ret == '1') { - add_next_index_bool(z_tab, 1); - } else { - add_next_index_bool(z_tab, 0); - } - } else { - if (ret == '1') { - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + char *response; + int response_len; + char ret; + + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } + } + ret = response[1]; + efree(response); + + IF_MULTI_OR_PIPELINE() { + if(ret == '1') { + add_next_index_bool(z_tab, 1); + } else { + add_next_index_bool(z_tab, 0); + } + } else { + if (ret == '1') { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -782,25 +782,25 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { - if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { - RETURN_STRINGL(response, response_len, 0); - } else { - efree(response); - } - } + if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { + RETURN_STRINGL(response, response_len, 0); + } else { + efree(response); + } + } } /* like string response, but never unserialized. */ @@ -812,15 +812,15 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - add_next_index_stringl(z_tab, response, response_len, 0); + add_next_index_stringl(z_tab, response, response_len, 0); } else { - RETURN_STRINGL(response, response_len, 0); - } + RETURN_STRINGL(response, response_len, 0); + } } @@ -843,7 +843,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por redis_sock->persistent = persistent; if(persistent_id) { - size_t persistent_id_len = strlen(persistent_id); + size_t persistent_id_len = strlen(persistent_id); redis_sock->persistent_id = ecalloc(persistent_id_len + 1, 1); memcpy(redis_sock->persistent_id, persistent_id, persistent_id_len); } else { @@ -877,8 +877,8 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) struct timeval tv, *tv_ptr = NULL; char *host = NULL, *persistent_id = NULL, *errstr = NULL; int host_len, err = 0; - php_netstream_data_t *sock; - int tcp_flag = 1; + php_netstream_data_t *sock; + int tcp_flag = 1; if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock TSRMLS_CC); @@ -887,15 +887,15 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) tv.tv_sec = (time_t)redis_sock->timeout; tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); if(tv.tv_sec != 0 || tv.tv_usec != 0) { - tv_ptr = &tv; + tv_ptr = &tv; } if(redis_sock->host[0] == '/' && redis_sock->port < 1) { - host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); + host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); } else { - if(redis_sock->port == 0) - redis_sock->port = 6379; - host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); + if(redis_sock->port == 0) + redis_sock->port = 6379; + host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); } if (redis_sock->persistent) { @@ -907,10 +907,10 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } redis_sock->stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE, - STREAM_XPORT_CLIENT - | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &errstr, &err - ); + STREAM_XPORT_CLIENT + | STREAM_XPORT_CONNECT, + persistent_id, tv_ptr, NULL, &errstr, &err + ); if (persistent_id) { efree(persistent_id); @@ -924,7 +924,7 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } /* set TCP_NODELAY */ - sock = (php_netstream_data_t*)redis_sock->stream->abstract; + sock = (php_netstream_data_t*)redis_sock->stream->abstract; setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); php_stream_auto_cleanup(redis_sock->stream); @@ -975,23 +975,23 @@ PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRML PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) { if (redis_sock == NULL) { - return 1; + return 1; } redis_sock->dbNumber = 0; if (redis_sock->stream != NULL) { - if (!redis_sock->persistent) { - redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); - } + if (!redis_sock->persistent) { + redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); + } - redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; + redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; - if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ - php_stream_close(redis_sock->stream); - } - redis_sock->stream = NULL; + if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ + php_stream_close(redis_sock->stream); + } + redis_sock->stream = NULL; - return 1; + return 1; } return 0; @@ -1000,8 +1000,8 @@ PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *cmd; - int response_len, cmd_len; - char * response; + int response_len, cmd_len; + char * response; cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); @@ -1015,41 +1015,41 @@ PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_so RETURN_FALSE; } - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { - RETURN_TRUE; - } - RETURN_FALSE; + if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + RETURN_TRUE; + } + RETURN_FALSE; } /** * redis_sock_set_err */ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { - // Allocate/Reallocate our last error member - if(msg != NULL && msg_len > 0) { - if(redis_sock->err == NULL) { - redis_sock->err = emalloc(msg_len + 1); - } else if(msg_len > redis_sock->err_len) { - redis_sock->err = erealloc(redis_sock->err, msg_len +1); - } - - // Copy in our new error message, set new length, and null terminate - memcpy(redis_sock->err, msg, msg_len); - redis_sock->err[msg_len] = '\0'; - redis_sock->err_len = msg_len; - } else { - // Free our last error - if(redis_sock->err != NULL) { - efree(redis_sock->err); - } - - // Set to null, with zero length - redis_sock->err = NULL; - redis_sock->err_len = 0; - } - - // Success - return 0; + // Allocate/Reallocate our last error member + if(msg != NULL && msg_len > 0) { + if(redis_sock->err == NULL) { + redis_sock->err = emalloc(msg_len + 1); + } else if(msg_len > redis_sock->err_len) { + redis_sock->err = erealloc(redis_sock->err, msg_len +1); + } + + // Copy in our new error message, set new length, and null terminate + memcpy(redis_sock->err, msg, msg_len); + redis_sock->err[msg_len] = '\0'; + redis_sock->err_len = msg_len; + } else { + // Free our last error + if(redis_sock->err != NULL) { + efree(redis_sock->err); + } + + // Set to null, with zero length + redis_sock->err = NULL; + redis_sock->err_len = 0; + } + + // Success + return 0; } /** @@ -1058,14 +1058,14 @@ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_le PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1100,14 +1100,14 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1146,17 +1146,17 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re while(numElems > 0) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - int can_unserialize = unwrap_key; - if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) - can_unserialize = 0; - - if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + zval *z = NULL; + int can_unserialize = unwrap_key; + if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) + can_unserialize = 0; + + if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { add_next_index_bool(z_tab, 0); } @@ -1172,8 +1172,8 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R { char inbuf[1024], *response; int response_len; - int i, numElems; - zval *z_multi_result; + int i, numElems; + zval *z_multi_result; zval **z_keys = ctx; @@ -1181,7 +1181,7 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1200,30 +1200,30 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); - } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); - } - } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); - } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + } + } else { + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + } + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - INIT_PZVAL(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); - } + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + INIT_PZVAL(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); + } return 0; } @@ -1232,10 +1232,10 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R */ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { - if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { - zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); - return -1; - } + if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { + zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); + return -1; + } if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } @@ -1248,10 +1248,10 @@ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_D PHPAPI void redis_free_socket(RedisSock *redis_sock) { if(redis_sock->prefix) { - efree(redis_sock->prefix); - } + efree(redis_sock->prefix); + } if(redis_sock->err) { - efree(redis_sock->err); + efree(redis_sock->err); } efree(redis_sock->host); efree(redis_sock); @@ -1260,146 +1260,146 @@ PHPAPI void redis_free_socket(RedisSock *redis_sock) PHPAPI int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC) { #if ZEND_MODULE_API_NO >= 20100000 - php_serialize_data_t ht; + php_serialize_data_t ht; #else - HashTable ht; + HashTable ht; #endif - smart_str sstr = {0}; - zval *z_copy; - size_t sz; - uint8_t *val8; - - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - switch(Z_TYPE_P(z)) { - - case IS_STRING: - *val = Z_STRVAL_P(z); - *val_len = Z_STRLEN_P(z); - return 0; - - case IS_OBJECT: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Object", 6, 1); - break; - - case IS_ARRAY: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Array", 5, 1); - break; - - default: /* copy */ - MAKE_STD_ZVAL(z_copy); - *z_copy = *z; - zval_copy_ctor(z_copy); - break; - } - - /* return string */ - convert_to_string(z_copy); - *val = Z_STRVAL_P(z_copy); - *val_len = Z_STRLEN_P(z_copy); - efree(z_copy); - return 1; - - case REDIS_SERIALIZER_PHP: + smart_str sstr = {0}; + zval *z_copy; + size_t sz; + uint8_t *val8; + + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + switch(Z_TYPE_P(z)) { + + case IS_STRING: + *val = Z_STRVAL_P(z); + *val_len = Z_STRLEN_P(z); + return 0; + + case IS_OBJECT: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Object", 6, 1); + break; + + case IS_ARRAY: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Array", 5, 1); + break; + + default: /* copy */ + MAKE_STD_ZVAL(z_copy); + *z_copy = *z; + zval_copy_ctor(z_copy); + break; + } + + /* return string */ + convert_to_string(z_copy); + *val = Z_STRVAL_P(z_copy); + *val_len = Z_STRLEN_P(z_copy); + efree(z_copy); + return 1; + + case REDIS_SERIALIZER_PHP: #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_INIT(ht); + PHP_VAR_SERIALIZE_INIT(ht); #else - zend_hash_init(&ht, 10, NULL, NULL, 0); + zend_hash_init(&ht, 10, NULL, NULL, 0); #endif - php_var_serialize(&sstr, &z, &ht TSRMLS_CC); - *val = sstr.c; - *val_len = (int)sstr.len; + php_var_serialize(&sstr, &z, &ht TSRMLS_CC); + *val = sstr.c; + *val_len = (int)sstr.len; #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_DESTROY(ht); + PHP_VAR_SERIALIZE_DESTROY(ht); #else - zend_hash_destroy(&ht); + zend_hash_destroy(&ht); #endif - return 1; + return 1; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ - *val = (char*)val8; - *val_len = (int)sz; - return 1; - } + if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ + *val = (char*)val8; + *val_len = (int)sz; + return 1; + } #endif - return 0; - } - return 0; + return 0; + } + return 0; } PHPAPI int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) { - php_unserialize_data_t var_hash; - int ret; + php_unserialize_data_t var_hash; + int ret; - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - return 0; + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + return 0; - case REDIS_SERIALIZER_PHP: - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - } + case REDIS_SERIALIZER_PHP: + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_INIT(var_hash); + PHP_VAR_UNSERIALIZE_INIT(var_hash); #else - memset(&var_hash, 0, sizeof(var_hash)); + memset(&var_hash, 0, sizeof(var_hash)); #endif - if(!php_var_unserialize(return_value, (const unsigned char**)&val, - (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { - efree(*return_value); - ret = 0; - } else { - ret = 1; - } + if(!php_var_unserialize(return_value, (const unsigned char**)&val, + (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { + efree(*return_value); + ret = 0; + } else { + ret = 1; + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); #else - var_destroy(&var_hash); + var_destroy(&var_hash); #endif - return ret; + return ret; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - } - if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { - return 1; - } - efree(*return_value); + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + } + if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { + return 1; + } + efree(*return_value); #endif - return 0; - break; - } - return 0; + return 0; + break; + } + return 0; } PHPAPI int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { - int ret_len; - char *ret; - - if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { - return 0; - } - - ret_len = redis_sock->prefix_len + *key_len; - ret = ecalloc(1 + ret_len, 1); - memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); - memcpy(ret + redis_sock->prefix_len, *key, *key_len); - - *key = ret; - *key_len = ret_len; - return 1; + int ret_len; + char *ret; + + if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { + return 0; + } + + ret_len = redis_sock->prefix_len + *key_len; + ret = ecalloc(1 + ret_len, 1); + memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); + memcpy(ret + redis_sock->prefix_len, *key, *key_len); + + *key = ret; + *key_len = ret_len; + return 1; } /* @@ -1409,60 +1409,60 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { PHPAPI int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) { // Handle EOF - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } - if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { - // Close, put our socket state into error - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { + // Close, put our socket state into error + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; - // Throw a read error exception - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); - } + // Throw a read error exception + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + } - // We don't need \r\n - *line_size-=2; - buf[*line_size]='\0'; + // We don't need \r\n + *line_size-=2; + buf[*line_size]='\0'; - // Success! - return 0; + // Success! + return 0; } PHPAPI int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) { - // Make sure we haven't lost the connection, even trying to reconnect - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { - // Failure - return -1; - } - - // Attempt to read the reply-type byte - if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); - } - - // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here - if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { - // Buffer to hold size information - char inbuf[255]; - - // Read up to our newline - if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { - return -1; - } - - // Set our size response - *reply_info = atoi(inbuf); - } - - // Success! - return 0; + // Make sure we haven't lost the connection, even trying to reconnect + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + // Failure + return -1; + } + + // Attempt to read the reply-type byte + if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + } + + // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { + // Buffer to hold size information + char inbuf[255]; + + // Read up to our newline + if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { + return -1; + } + + // Set our size response + *reply_info = atoi(inbuf); + } + + // Success! + return 0; } /* @@ -1470,152 +1470,152 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int * */ PHPAPI int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC) { - // Buffer to read our single line reply - char inbuf[1024]; - size_t line_size; - - // Attempt to read our single line reply - if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { - return -1; - } - - // If this is an error response, check if it is a SYNC error, and throw in that case - if(reply_type == TYPE_ERR) { - if(memcmp(inbuf, "ERR SYNC", 9) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } - - // Set our last error - redis_sock_set_err(redis_sock, inbuf, line_size); - - // Set our response to FALSE - ZVAL_FALSE(*z_ret); - } else { - // Set our response to TRUE - ZVAL_TRUE(*z_ret); - } - - return 0; + // Buffer to read our single line reply + char inbuf[1024]; + size_t line_size; + + // Attempt to read our single line reply + if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { + return -1; + } + + // If this is an error response, check if it is a SYNC error, and throw in that case + if(reply_type == TYPE_ERR) { + if(memcmp(inbuf, "ERR SYNC", 9) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } + + // Set our last error + redis_sock_set_err(redis_sock, inbuf, line_size); + + // Set our response to FALSE + ZVAL_FALSE(*z_ret); + } else { + // Set our response to TRUE + ZVAL_TRUE(*z_ret); + } + + return 0; } PHPAPI int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) { - // Attempt to read the bulk reply - char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); - - // Set our reply to FALSE on failure, and the string on success - if(bulk_resp == NULL) { - ZVAL_FALSE(*z_ret); - return -1; - } else { - ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); - return 0; - } + // Attempt to read the bulk reply + char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); + + // Set our reply to FALSE on failure, and the string on success + if(bulk_resp == NULL) { + ZVAL_FALSE(*z_ret); + return -1; + } else { + ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); + return 0; + } } PHPAPI int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { - int reply_info; - REDIS_REPLY_TYPE reply_type; - zval *z_subelem; - - // Iterate while we have elements - while(elements > 0) { - // Attempt to read our reply type - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); - return -1; - } - - // Switch on our reply-type byte - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_INT: - // Add our long value - add_next_index_long(*z_ret, reply_info); - break; - case TYPE_BULK: - // Init a zval for our bulk response, read and add it - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_MULTIBULK: - // Construct an array for our sub element, and add it, and recurse - ALLOC_INIT_ZVAL(z_subelem); - array_init(z_subelem); - add_next_index_zval(*z_ret, z_subelem); - redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); - break; - } - - // Decrement our element counter - elements--; - } - - return 0; + int reply_info; + REDIS_REPLY_TYPE reply_type; + zval *z_subelem; + + // Iterate while we have elements + while(elements > 0) { + // Attempt to read our reply type + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); + return -1; + } + + // Switch on our reply-type byte + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_INT: + // Add our long value + add_next_index_long(*z_ret, reply_info); + break; + case TYPE_BULK: + // Init a zval for our bulk response, read and add it + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_MULTIBULK: + // Construct an array for our sub element, and add it, and recurse + ALLOC_INIT_ZVAL(z_subelem); + array_init(z_subelem); + add_next_index_zval(*z_ret, z_subelem); + redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); + break; + } + + // Decrement our element counter + elements--; + } + + return 0; } PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { - // Reply type, and reply size vars - REDIS_REPLY_TYPE reply_type; - int reply_info; - //char *bulk_resp; - zval *z_ret; - - // Attempt to read our header - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - return -1; - } - - // Our return ZVAL - MAKE_STD_ZVAL(z_ret); - - // Switch based on our top level reply type - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); - break; - case TYPE_INT: - ZVAL_LONG(z_ret, reply_info); - break; - case TYPE_BULK: - redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); - break; - case TYPE_MULTIBULK: - // Initialize an array for our multi-bulk response - array_init(z_ret); - - // If we've got more than zero elements, parse our multi bulk respoinse recursively - if(reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); - } - break; - default: - // Protocol error - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); - break; - } - - IF_MULTI_OR_PIPELINE() { - add_next_index_zval(z_tab, z_ret); - } else { - // Set our return value - *return_value = *z_ret; - zval_copy_ctor(return_value); - zval_dtor(z_ret); - efree(z_ret); - } - - // Success - return 0; + // Reply type, and reply size vars + REDIS_REPLY_TYPE reply_type; + int reply_info; + //char *bulk_resp; + zval *z_ret; + + // Attempt to read our header + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { + return -1; + } + + // Our return ZVAL + MAKE_STD_ZVAL(z_ret); + + // Switch based on our top level reply type + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); + break; + case TYPE_INT: + ZVAL_LONG(z_ret, reply_info); + break; + case TYPE_BULK: + redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); + break; + case TYPE_MULTIBULK: + // Initialize an array for our multi-bulk response + array_init(z_ret); + + // If we've got more than zero elements, parse our multi bulk respoinse recursively + if(reply_info > -1) { + redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); + } + break; + default: + // Protocol error + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); + break; + } + + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_ret); + } else { + // Set our return value + *return_value = *z_ret; + zval_copy_ctor(return_value); + zval_dtor(z_ret); + efree(z_ret); + } + + // Success + return 0; } /* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ From 9ba000c54cc47d470366c59af87ddf9d4dd454bc Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Tue, 29 Jan 2013 11:40:06 +0200 Subject: [PATCH 0010/1986] Revert "Retry delay - selectDB on array" This reverts commit 3fb643211e524112bd9a19792f833b12b68de600. --- common.h | 1 - library.c | 1496 ++++++++++++++++++++++---------------------- library.h | 2 +- redis.c | 15 +- redis_array.c | 57 +- redis_array.h | 1 - redis_array_impl.c | 33 +- redis_array_impl.h | 4 +- redis_session.c | 9 +- 9 files changed, 765 insertions(+), 853 deletions(-) diff --git a/common.h b/common.h index fda59c6aeb..b48ac75b06 100644 --- a/common.h +++ b/common.h @@ -156,7 +156,6 @@ typedef struct { char *host; short port; double timeout; - long retry_interval; int failed; int status; int persistent; diff --git a/library.c b/library.c index 15e71e5a1f..a954787f5b 100644 --- a/library.c +++ b/library.c @@ -26,83 +26,76 @@ extern zend_class_entry *redis_exception_ce; extern zend_class_entry *spl_ce_RuntimeException; PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { - if (!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } else { - php_stream_pclose(redis_sock->stream); - } + if (!redis_sock->persistent) { + php_stream_close(redis_sock->stream); + } else { + php_stream_pclose(redis_sock->stream); + } } PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) { - int eof; - int count = 0; - - if (!redis_sock->stream) { - return -1; - } - - eof = php_stream_eof(redis_sock->stream); - for (; eof; count++) { - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ - if(redis_sock->stream) { /* close stream if still here */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->watching = 0; - } - zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); - return -1; - } - if(redis_sock->stream) { /* close existing stream before reconnecting */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; - } - // Wait for a while before trying to reconnect - if (redis_sock->retry_interval) { - // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time - long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval)); - usleep(retry_interval); - } - redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ - if(redis_sock->stream) { /* check for EOF again. */ - eof = php_stream_eof(redis_sock->stream); + int eof; + int count = 0; + + if (!redis_sock->stream) + return -1; + + eof = php_stream_eof(redis_sock->stream); + for (; eof; count++) { + if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ + if(redis_sock->stream) { /* close stream if still here */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->watching = 0; + } + zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); + return -1; + } + if(redis_sock->stream) { /* close existing stream before reconnecting */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; + } + redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ + if(redis_sock->stream) { /* check for EOF again. */ + eof = php_stream_eof(redis_sock->stream); + } } - } // Reselect the DB. - if (count && redis_sock->dbNumber) { - char *cmd, *response; - int cmd_len, response_len; + if (count && redis_sock->dbNumber) { + char *cmd, *response; + int cmd_len, response_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return -1; - } - efree(cmd); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return -1; + } + efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - return -1; - } + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return -1; + } - if (strncmp(response, "+OK", 3)) { - efree(response); - return -1; + if (strncmp(response, "+OK", 3)) { + efree(response); + return -1; + } + efree(response); } - efree(response); - } - return 0; + return 0; } PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char inbuf[1024]; - int numElems; + int numElems; zval *z_tab; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { @@ -110,7 +103,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -129,7 +122,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, numElems, 1, UNSERIALIZE_ALL); - return z_tab; + return z_tab; } /** @@ -152,13 +145,13 @@ PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_ char c; int i; - reply = emalloc(bytes+1); + reply = emalloc(bytes+1); while(offset < bytes) { got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); if (got <= 0) { /* Error or EOF */ - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); break; } offset += got; @@ -186,7 +179,7 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -197,12 +190,12 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) switch(inbuf[0]) { case '-': - err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); - /* stale data */ - if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } + err_len = strlen(inbuf+1) - 2; + redis_sock_set_err(redis_sock, inbuf+1, err_len); + /* stale data */ + if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } return NULL; case '$': @@ -219,7 +212,7 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) case '+': case ':': - // Single Line Reply + // Single Line Reply /* :123\r\n */ *buf_len = strlen(inbuf) - 2; if(*buf_len >= 2) { @@ -230,12 +223,12 @@ PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) } default: - zend_throw_exception_ex( - redis_exception_ce, - 0 TSRMLS_CC, - "protocol error, got '%c' as reply type byte\n", - inbuf[0] - ); + zend_throw_exception_ex( + redis_exception_ce, + 0 TSRMLS_CC, + "protocol error, got '%c' as reply type byte\n", + inbuf[0] + ); } return NULL; @@ -252,42 +245,42 @@ void add_constant_long(zend_class_entry *ce, char *name, int value) { int integer_length(int i) { - int sz = 0; - int ci = abs(i); - while (ci > 0) { - ci /= 10; - sz++; - } - if (i == 0) { /* log 0 doesn't make sense. */ - sz = 1; - } else if (i < 0) { /* allow for neg sign as well. */ - sz++; - } - return sz; + int sz = 0; + int ci = abs(i); + while (ci > 0) { + ci /= 10; + sz++; + } + if (i == 0) { /* log 0 doesn't make sense. */ + sz = 1; + } else if (i < 0) { /* allow for neg sign as well. */ + sz++; + } + return sz; } int redis_cmd_format_header(char **ret, char *keyword, int arg_count) { - // Our return buffer - smart_str buf = {0}; - - // Keyword length - int l = strlen(keyword); - - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, arg_count + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - // Set our return pointer - *ret = buf.c; - - // Return the length - return buf.len; + // Our return buffer + smart_str buf = {0}; + + // Keyword length + int l = strlen(keyword); + + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, arg_count + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + // Set our return pointer + *ret = buf.c; + + // Return the length + return buf.len; } int @@ -297,74 +290,74 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { va_list ap; smart_str buf = {0}; int l = strlen(keyword); - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - /* add header */ - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, strlen(format) + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - while (*p) { - smart_str_appendc(&buf, '$'); - - switch(*p) { - case 's': { - char *val = va_arg(ap, char*); - int val_len = va_arg(ap, int); - smart_str_append_long(&buf, val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, val, val_len); - } - break; - - case 'f': - case 'F': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'i': - case 'd': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - case 'l': - case 'L': { - long l = va_arg(ap, long); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - p++; - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - } - smart_str_0(&buf); - - *ret = buf.c; - - return buf.len; + char *dbl_str; + int dbl_len; + + va_start(ap, format); + + /* add header */ + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, strlen(format) + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + while (*p) { + smart_str_appendc(&buf, '$'); + + switch(*p) { + case 's': { + char *val = va_arg(ap, char*); + int val_len = va_arg(ap, int); + smart_str_append_long(&buf, val_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, val, val_len); + } + break; + + case 'f': + case 'F': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'i': + case 'd': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + case 'l': + case 'L': { + long l = va_arg(ap, long); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + p++; + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + } + smart_str_0(&buf); + + *ret = buf.c; + + return buf.len; } /** @@ -375,119 +368,119 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { int redis_cmd_format(char **ret, char *format, ...) { - smart_str buf = {0}; - va_list ap; - char *p = format; - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - while (*p) { - if (*p == '%') { - switch (*(++p)) { - case 's': { - char *tmp = va_arg(ap, char*); - int tmp_len = va_arg(ap, int); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - - case 'F': - case 'f': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'd': - case 'i': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - } else { - smart_str_appendc(&buf, *p); - } - - p++; - } - - smart_str_0(&buf); - - *ret = buf.c; - - return buf.len; + smart_str buf = {0}; + va_list ap; + char *p = format; + char *dbl_str; + int dbl_len; + + va_start(ap, format); + + while (*p) { + if (*p == '%') { + switch (*(++p)) { + case 's': { + char *tmp = va_arg(ap, char*); + int tmp_len = va_arg(ap, int); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + + case 'F': + case 'f': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'd': + case 'i': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + } else { + smart_str_appendc(&buf, *p); + } + + p++; + } + + smart_str_0(&buf); + + *ret = buf.c; + + return buf.len; } /* * Append a command sequence to a Redis command */ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) { - // Smart string buffer - smart_str buf = {0}; + // Smart string buffer + smart_str buf = {0}; - // Append the current command to our smart_str - smart_str_appendl(&buf, *cmd, cmd_len); + // Append the current command to our smart_str + smart_str_appendl(&buf, *cmd, cmd_len); - // Append our new command sequence - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, append, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + // Append our new command sequence + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, append, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - // Free our old command - efree(*cmd); + // Free our old command + efree(*cmd); - // Set our return pointer - *cmd = buf.c; + // Set our return pointer + *cmd = buf.c; - // Return new command length - return buf.len; + // Return new command length + return buf.len; } /* * Append an integer command to a Redis command */ int redis_cmd_append_int(char **cmd, int cmd_len, int append) { - char int_buf[32]; + char int_buf[32]; - // Conver to an int, capture length - int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); + // Conver to an int, capture length + int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); - // Return the new length - return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); + // Return the new length + return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); } PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - double ret; + double ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } - return; + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } + return; } ret = atof(response); efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_double(z_tab, ret); + add_next_index_double(z_tab, ret); } else { - RETURN_DOUBLE(ret); + RETURN_DOUBLE(ret); } } @@ -497,41 +490,41 @@ PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s long l; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } } if (strncmp(response, "+string", 7) == 0) { - l = REDIS_STRING; + l = REDIS_STRING; } else if (strncmp(response, "+set", 4) == 0){ - l = REDIS_SET; + l = REDIS_SET; } else if (strncmp(response, "+list", 5) == 0){ - l = REDIS_LIST; + l = REDIS_LIST; } else if (strncmp(response, "+zset", 5) == 0){ - l = REDIS_ZSET; + l = REDIS_ZSET; } else if (strncmp(response, "+hash", 5) == 0){ - l = REDIS_HASH; + l = REDIS_HASH; } else { - l = REDIS_NOT_FOUND; + l = REDIS_NOT_FOUND; } efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_long(z_tab, l); + add_next_index_long(z_tab, l); } else { - RETURN_LONG(l); + RETURN_LONG(l); } } PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - char *pos, *cur; - char *key, *value, *p; - int is_numeric; + char *pos, *cur; + char *key, *value, *p; + int is_numeric; zval *z_multi_result; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { @@ -547,13 +540,13 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s cur = response; while(1) { - /* skip comments and empty lines */ - if(*cur == '#' || *cur == '\r') { - if(!(cur = strchr(cur, '\n'))) - break; - cur++; - continue; - } + /* skip comments and empty lines */ + if(*cur == '#' || *cur == '\r') { + if(!(cur = strchr(cur, '\n'))) + break; + cur++; + continue; + } /* key */ pos = strchr(cur, ':'); @@ -608,10 +601,10 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock char ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; - } + return; + } RETURN_FALSE; } ret = response[0]; @@ -627,15 +620,15 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_bool(z_tab, 0); } } else { - if (ret == '+') { + if (ret == '+') { if (success_callback != NULL) { success_callback(redis_sock); } - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -648,38 +641,38 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s int response_len; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } } if(response[0] == ':') { long long ret = atoll(response + 1); IF_MULTI_OR_PIPELINE() { - if(ret > LONG_MAX) { /* overflow */ - add_next_index_stringl(z_tab, response+1, response_len-1, 1); - } else { - efree(response); - add_next_index_long(z_tab, (long)ret); - } + if(ret > LONG_MAX) { /* overflow */ + add_next_index_stringl(z_tab, response+1, response_len-1, 1); + } else { + efree(response); + add_next_index_long(z_tab, (long)ret); + } } else { - if(ret > LONG_MAX) { /* overflow */ - RETURN_STRINGL(response+1, response_len-1, 1); - } else { - efree(response); - RETURN_LONG((long)ret); - } - } + if(ret > LONG_MAX) { /* overflow */ + RETURN_STRINGL(response+1, response_len-1, 1); + } else { + efree(response); + RETURN_LONG((long)ret); + } + } } else { efree(response); IF_MULTI_OR_PIPELINE() { add_next_index_null(z_tab); } else { - RETURN_FALSE; - } + RETURN_FALSE; + } } } @@ -687,20 +680,20 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) { - /* - int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); - array_zip_values_and_scores(return_value, 0); - */ + /* + int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); + array_zip_values_and_scores(return_value, 0); + */ char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -724,10 +717,10 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); } return 0; @@ -735,43 +728,43 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); + return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); } PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); + return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); } PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - char *response; - int response_len; - char ret; - - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } - } - ret = response[1]; - efree(response); - - IF_MULTI_OR_PIPELINE() { - if(ret == '1') { - add_next_index_bool(z_tab, 1); - } else { - add_next_index_bool(z_tab, 0); - } - } else { - if (ret == '1') { - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + char *response; + int response_len; + char ret; + + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } + } + ret = response[1]; + efree(response); + + IF_MULTI_OR_PIPELINE() { + if(ret == '1') { + add_next_index_bool(z_tab, 1); + } else { + add_next_index_bool(z_tab, 0); + } + } else { + if (ret == '1') { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -782,25 +775,25 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { - if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { - RETURN_STRINGL(response, response_len, 0); - } else { - efree(response); - } - } + if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { + RETURN_STRINGL(response, response_len, 0); + } else { + efree(response); + } + } } /* like string response, but never unserialized. */ @@ -812,15 +805,15 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - add_next_index_stringl(z_tab, response, response_len, 0); + add_next_index_stringl(z_tab, response, response_len, 0); } else { - RETURN_STRINGL(response, response_len, 0); - } + RETURN_STRINGL(response, response_len, 0); + } } @@ -828,8 +821,7 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s * redis_sock_create */ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, - double timeout, int persistent, char *persistent_id, - long retry_interval) + double timeout, int persistent, char *persistent_id) { RedisSock *redis_sock; @@ -839,11 +831,11 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; redis_sock->dbNumber = 0; - redis_sock->retry_interval = retry_interval * 1000; + redis_sock->persistent = persistent; if(persistent_id) { - size_t persistent_id_len = strlen(persistent_id); + size_t persistent_id_len = strlen(persistent_id); redis_sock->persistent_id = ecalloc(persistent_id_len + 1, 1); memcpy(redis_sock->persistent_id, persistent_id, persistent_id_len); } else { @@ -877,8 +869,8 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) struct timeval tv, *tv_ptr = NULL; char *host = NULL, *persistent_id = NULL, *errstr = NULL; int host_len, err = 0; - php_netstream_data_t *sock; - int tcp_flag = 1; + php_netstream_data_t *sock; + int tcp_flag = 1; if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock TSRMLS_CC); @@ -887,15 +879,15 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) tv.tv_sec = (time_t)redis_sock->timeout; tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); if(tv.tv_sec != 0 || tv.tv_usec != 0) { - tv_ptr = &tv; + tv_ptr = &tv; } if(redis_sock->host[0] == '/' && redis_sock->port < 1) { - host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); + host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); } else { - if(redis_sock->port == 0) - redis_sock->port = 6379; - host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); + if(redis_sock->port == 0) + redis_sock->port = 6379; + host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); } if (redis_sock->persistent) { @@ -907,10 +899,10 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } redis_sock->stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE, - STREAM_XPORT_CLIENT - | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &errstr, &err - ); + STREAM_XPORT_CLIENT + | STREAM_XPORT_CONNECT, + persistent_id, tv_ptr, NULL, &errstr, &err + ); if (persistent_id) { efree(persistent_id); @@ -924,7 +916,7 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } /* set TCP_NODELAY */ - sock = (php_netstream_data_t*)redis_sock->stream->abstract; + sock = (php_netstream_data_t*)redis_sock->stream->abstract; setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); php_stream_auto_cleanup(redis_sock->stream); @@ -975,23 +967,23 @@ PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRML PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) { if (redis_sock == NULL) { - return 1; + return 1; } redis_sock->dbNumber = 0; if (redis_sock->stream != NULL) { - if (!redis_sock->persistent) { - redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); - } + if (!redis_sock->persistent) { + redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC); + } - redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; + redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; - if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ - php_stream_close(redis_sock->stream); - } - redis_sock->stream = NULL; + if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ + php_stream_close(redis_sock->stream); + } + redis_sock->stream = NULL; - return 1; + return 1; } return 0; @@ -1000,8 +992,8 @@ PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *cmd; - int response_len, cmd_len; - char * response; + int response_len, cmd_len; + char * response; cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); @@ -1015,41 +1007,41 @@ PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_so RETURN_FALSE; } - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { - RETURN_TRUE; - } - RETURN_FALSE; + if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + RETURN_TRUE; + } + RETURN_FALSE; } /** * redis_sock_set_err */ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { - // Allocate/Reallocate our last error member - if(msg != NULL && msg_len > 0) { - if(redis_sock->err == NULL) { - redis_sock->err = emalloc(msg_len + 1); - } else if(msg_len > redis_sock->err_len) { - redis_sock->err = erealloc(redis_sock->err, msg_len +1); - } - - // Copy in our new error message, set new length, and null terminate - memcpy(redis_sock->err, msg, msg_len); - redis_sock->err[msg_len] = '\0'; - redis_sock->err_len = msg_len; - } else { - // Free our last error - if(redis_sock->err != NULL) { - efree(redis_sock->err); - } - - // Set to null, with zero length - redis_sock->err = NULL; - redis_sock->err_len = 0; - } - - // Success - return 0; + // Allocate/Reallocate our last error member + if(msg != NULL && msg_len > 0) { + if(redis_sock->err == NULL) { + redis_sock->err = emalloc(msg_len + 1); + } else if(msg_len > redis_sock->err_len) { + redis_sock->err = erealloc(redis_sock->err, msg_len +1); + } + + // Copy in our new error message, set new length, and null terminate + memcpy(redis_sock->err, msg, msg_len); + redis_sock->err[msg_len] = '\0'; + redis_sock->err_len = msg_len; + } else { + // Free our last error + if(redis_sock->err != NULL) { + efree(redis_sock->err); + } + + // Set to null, with zero length + redis_sock->err = NULL; + redis_sock->err_len = 0; + } + + // Success + return 0; } /** @@ -1058,14 +1050,14 @@ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_le PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1100,14 +1092,14 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1146,17 +1138,17 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re while(numElems > 0) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - int can_unserialize = unwrap_key; - if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) - can_unserialize = 0; - - if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + zval *z = NULL; + int can_unserialize = unwrap_key; + if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) + can_unserialize = 0; + + if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { add_next_index_bool(z_tab, 0); } @@ -1172,8 +1164,8 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R { char inbuf[1024], *response; int response_len; - int i, numElems; - zval *z_multi_result; + int i, numElems; + zval *z_multi_result; zval **z_keys = ctx; @@ -1181,7 +1173,7 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1200,30 +1192,30 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); - } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); - } - } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); - } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + } + } else { + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + } + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - INIT_PZVAL(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); - } + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + INIT_PZVAL(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); + } return 0; } @@ -1232,10 +1224,10 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R */ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { - if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { - zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); - return -1; - } + if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { + zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); + return -1; + } if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } @@ -1248,10 +1240,10 @@ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_D PHPAPI void redis_free_socket(RedisSock *redis_sock) { if(redis_sock->prefix) { - efree(redis_sock->prefix); - } + efree(redis_sock->prefix); + } if(redis_sock->err) { - efree(redis_sock->err); + efree(redis_sock->err); } efree(redis_sock->host); efree(redis_sock); @@ -1260,146 +1252,146 @@ PHPAPI void redis_free_socket(RedisSock *redis_sock) PHPAPI int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC) { #if ZEND_MODULE_API_NO >= 20100000 - php_serialize_data_t ht; + php_serialize_data_t ht; #else - HashTable ht; + HashTable ht; #endif - smart_str sstr = {0}; - zval *z_copy; - size_t sz; - uint8_t *val8; - - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - switch(Z_TYPE_P(z)) { - - case IS_STRING: - *val = Z_STRVAL_P(z); - *val_len = Z_STRLEN_P(z); - return 0; - - case IS_OBJECT: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Object", 6, 1); - break; - - case IS_ARRAY: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Array", 5, 1); - break; - - default: /* copy */ - MAKE_STD_ZVAL(z_copy); - *z_copy = *z; - zval_copy_ctor(z_copy); - break; - } - - /* return string */ - convert_to_string(z_copy); - *val = Z_STRVAL_P(z_copy); - *val_len = Z_STRLEN_P(z_copy); - efree(z_copy); - return 1; - - case REDIS_SERIALIZER_PHP: + smart_str sstr = {0}; + zval *z_copy; + size_t sz; + uint8_t *val8; + + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + switch(Z_TYPE_P(z)) { + + case IS_STRING: + *val = Z_STRVAL_P(z); + *val_len = Z_STRLEN_P(z); + return 0; + + case IS_OBJECT: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Object", 6, 1); + break; + + case IS_ARRAY: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Array", 5, 1); + break; + + default: /* copy */ + MAKE_STD_ZVAL(z_copy); + *z_copy = *z; + zval_copy_ctor(z_copy); + break; + } + + /* return string */ + convert_to_string(z_copy); + *val = Z_STRVAL_P(z_copy); + *val_len = Z_STRLEN_P(z_copy); + efree(z_copy); + return 1; + + case REDIS_SERIALIZER_PHP: #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_INIT(ht); + PHP_VAR_SERIALIZE_INIT(ht); #else - zend_hash_init(&ht, 10, NULL, NULL, 0); + zend_hash_init(&ht, 10, NULL, NULL, 0); #endif - php_var_serialize(&sstr, &z, &ht TSRMLS_CC); - *val = sstr.c; - *val_len = (int)sstr.len; + php_var_serialize(&sstr, &z, &ht TSRMLS_CC); + *val = sstr.c; + *val_len = (int)sstr.len; #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_DESTROY(ht); + PHP_VAR_SERIALIZE_DESTROY(ht); #else - zend_hash_destroy(&ht); + zend_hash_destroy(&ht); #endif - return 1; + return 1; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ - *val = (char*)val8; - *val_len = (int)sz; - return 1; - } + if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ + *val = (char*)val8; + *val_len = (int)sz; + return 1; + } #endif - return 0; - } - return 0; + return 0; + } + return 0; } PHPAPI int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) { - php_unserialize_data_t var_hash; - int ret; + php_unserialize_data_t var_hash; + int ret; - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - return 0; + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + return 0; - case REDIS_SERIALIZER_PHP: - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - } + case REDIS_SERIALIZER_PHP: + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_INIT(var_hash); + PHP_VAR_UNSERIALIZE_INIT(var_hash); #else - memset(&var_hash, 0, sizeof(var_hash)); + memset(&var_hash, 0, sizeof(var_hash)); #endif - if(!php_var_unserialize(return_value, (const unsigned char**)&val, - (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { - efree(*return_value); - ret = 0; - } else { - ret = 1; - } + if(!php_var_unserialize(return_value, (const unsigned char**)&val, + (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { + efree(*return_value); + ret = 0; + } else { + ret = 1; + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); #else - var_destroy(&var_hash); + var_destroy(&var_hash); #endif - return ret; + return ret; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - } - if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { - return 1; - } - efree(*return_value); + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + } + if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { + return 1; + } + efree(*return_value); #endif - return 0; - break; - } - return 0; + return 0; + break; + } + return 0; } PHPAPI int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { - int ret_len; - char *ret; - - if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { - return 0; - } - - ret_len = redis_sock->prefix_len + *key_len; - ret = ecalloc(1 + ret_len, 1); - memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); - memcpy(ret + redis_sock->prefix_len, *key, *key_len); - - *key = ret; - *key_len = ret_len; - return 1; + int ret_len; + char *ret; + + if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { + return 0; + } + + ret_len = redis_sock->prefix_len + *key_len; + ret = ecalloc(1 + ret_len, 1); + memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); + memcpy(ret + redis_sock->prefix_len, *key, *key_len); + + *key = ret; + *key_len = ret_len; + return 1; } /* @@ -1409,60 +1401,60 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { PHPAPI int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) { // Handle EOF - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } - if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { - // Close, put our socket state into error - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { + // Close, put our socket state into error + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; - // Throw a read error exception - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); - } + // Throw a read error exception + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + } - // We don't need \r\n - *line_size-=2; - buf[*line_size]='\0'; + // We don't need \r\n + *line_size-=2; + buf[*line_size]='\0'; - // Success! - return 0; + // Success! + return 0; } PHPAPI int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) { - // Make sure we haven't lost the connection, even trying to reconnect - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { - // Failure - return -1; - } - - // Attempt to read the reply-type byte - if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); - } - - // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here - if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { - // Buffer to hold size information - char inbuf[255]; - - // Read up to our newline - if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { - return -1; - } - - // Set our size response - *reply_info = atoi(inbuf); - } - - // Success! - return 0; + // Make sure we haven't lost the connection, even trying to reconnect + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + // Failure + return -1; + } + + // Attempt to read the reply-type byte + if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + } + + // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { + // Buffer to hold size information + char inbuf[255]; + + // Read up to our newline + if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { + return -1; + } + + // Set our size response + *reply_info = atoi(inbuf); + } + + // Success! + return 0; } /* @@ -1470,152 +1462,152 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int * */ PHPAPI int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC) { - // Buffer to read our single line reply - char inbuf[1024]; - size_t line_size; - - // Attempt to read our single line reply - if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { - return -1; - } - - // If this is an error response, check if it is a SYNC error, and throw in that case - if(reply_type == TYPE_ERR) { - if(memcmp(inbuf, "ERR SYNC", 9) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } - - // Set our last error - redis_sock_set_err(redis_sock, inbuf, line_size); - - // Set our response to FALSE - ZVAL_FALSE(*z_ret); - } else { - // Set our response to TRUE - ZVAL_TRUE(*z_ret); - } - - return 0; + // Buffer to read our single line reply + char inbuf[1024]; + size_t line_size; + + // Attempt to read our single line reply + if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { + return -1; + } + + // If this is an error response, check if it is a SYNC error, and throw in that case + if(reply_type == TYPE_ERR) { + if(memcmp(inbuf, "ERR SYNC", 9) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } + + // Set our last error + redis_sock_set_err(redis_sock, inbuf, line_size); + + // Set our response to FALSE + ZVAL_FALSE(*z_ret); + } else { + // Set our response to TRUE + ZVAL_TRUE(*z_ret); + } + + return 0; } PHPAPI int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) { - // Attempt to read the bulk reply - char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); - - // Set our reply to FALSE on failure, and the string on success - if(bulk_resp == NULL) { - ZVAL_FALSE(*z_ret); - return -1; - } else { - ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); - return 0; - } + // Attempt to read the bulk reply + char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); + + // Set our reply to FALSE on failure, and the string on success + if(bulk_resp == NULL) { + ZVAL_FALSE(*z_ret); + return -1; + } else { + ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); + return 0; + } } PHPAPI int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { - int reply_info; - REDIS_REPLY_TYPE reply_type; - zval *z_subelem; - - // Iterate while we have elements - while(elements > 0) { - // Attempt to read our reply type - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); - return -1; - } - - // Switch on our reply-type byte - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_INT: - // Add our long value - add_next_index_long(*z_ret, reply_info); - break; - case TYPE_BULK: - // Init a zval for our bulk response, read and add it - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_MULTIBULK: - // Construct an array for our sub element, and add it, and recurse - ALLOC_INIT_ZVAL(z_subelem); - array_init(z_subelem); - add_next_index_zval(*z_ret, z_subelem); - redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); - break; - } - - // Decrement our element counter - elements--; - } - - return 0; + int reply_info; + REDIS_REPLY_TYPE reply_type; + zval *z_subelem; + + // Iterate while we have elements + while(elements > 0) { + // Attempt to read our reply type + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); + return -1; + } + + // Switch on our reply-type byte + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_INT: + // Add our long value + add_next_index_long(*z_ret, reply_info); + break; + case TYPE_BULK: + // Init a zval for our bulk response, read and add it + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_MULTIBULK: + // Construct an array for our sub element, and add it, and recurse + ALLOC_INIT_ZVAL(z_subelem); + array_init(z_subelem); + add_next_index_zval(*z_ret, z_subelem); + redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); + break; + } + + // Decrement our element counter + elements--; + } + + return 0; } PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { - // Reply type, and reply size vars - REDIS_REPLY_TYPE reply_type; - int reply_info; - //char *bulk_resp; - zval *z_ret; - - // Attempt to read our header - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - return -1; - } - - // Our return ZVAL - MAKE_STD_ZVAL(z_ret); - - // Switch based on our top level reply type - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); - break; - case TYPE_INT: - ZVAL_LONG(z_ret, reply_info); - break; - case TYPE_BULK: - redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); - break; - case TYPE_MULTIBULK: - // Initialize an array for our multi-bulk response - array_init(z_ret); - - // If we've got more than zero elements, parse our multi bulk respoinse recursively - if(reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); - } - break; - default: - // Protocol error - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); - break; - } - - IF_MULTI_OR_PIPELINE() { - add_next_index_zval(z_tab, z_ret); - } else { - // Set our return value - *return_value = *z_ret; - zval_copy_ctor(return_value); - zval_dtor(z_ret); - efree(z_ret); - } - - // Success - return 0; + // Reply type, and reply size vars + REDIS_REPLY_TYPE reply_type; + int reply_info; + //char *bulk_resp; + zval *z_ret; + + // Attempt to read our header + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { + return -1; + } + + // Our return ZVAL + MAKE_STD_ZVAL(z_ret); + + // Switch based on our top level reply type + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); + break; + case TYPE_INT: + ZVAL_LONG(z_ret, reply_info); + break; + case TYPE_BULK: + redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); + break; + case TYPE_MULTIBULK: + // Initialize an array for our multi-bulk response + array_init(z_ret); + + // If we've got more than zero elements, parse our multi bulk respoinse recursively + if(reply_info > -1) { + redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); + } + break; + default: + // Protocol error + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); + break; + } + + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_ret); + } else { + // Set our return value + *return_value = *z_ret; + zval_copy_ctor(return_value); + zval_dtor(z_ret); + efree(z_ret); + } + + // Success + return 0; } /* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ diff --git a/library.h b/library.h index 20eaa08a40..ea135bfbc5 100644 --- a/library.h +++ b/library.h @@ -19,7 +19,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval); +PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id); PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index 0562a79bbc..fd103d30f9 100644 --- a/redis.c +++ b/redis.c @@ -520,7 +520,7 @@ PHP_METHOD(Redis,__destruct) { } } -/* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]]) +/* {{{ proto boolean Redis::connect(string host, int port [, double timeout]) */ PHP_METHOD(Redis, connect) { @@ -556,7 +556,6 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { int host_len, id; char *host = NULL; long port = -1; - long retry_interval = 0; char *persistent_id = NULL; int persistent_id_len = -1; @@ -569,10 +568,9 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { persistent = 0; #endif - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl", + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|lds", &object, redis_ce, &host, &host_len, &port, - &timeout, &persistent_id, &persistent_id_len, - &retry_interval) == FAILURE) { + &timeout, &persistent_id, &persistent_id_len) == FAILURE) { return FAILURE; } @@ -581,11 +579,6 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { return FAILURE; } - if (retry_interval < 0L || retry_interval > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC); - return FAILURE; - } - if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */ port = 6379; } @@ -602,7 +595,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zend_clear_exception(TSRMLS_C); /* clear exception triggered by non-existent socket during connect(). */ } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval); + redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id); if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { redis_free_socket(redis_sock); diff --git a/redis_array.c b/redis_array.c index be95c50d5a..d0460f1021 100644 --- a/redis_array.c +++ b/redis_array.c @@ -51,7 +51,6 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, _rehash, NULL, ZEND_ACC_PUBLIC) /* special implementation for a few functions */ - PHP_ME(RedisArray, select, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, info, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mget, NULL, ZEND_ACC_PUBLIC) @@ -193,7 +192,6 @@ PHP_METHOD(RedisArray, __construct) RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; HashTable *hPrev = NULL, *hOpts = NULL; - long l_retry_interval = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { RETURN_FALSE; @@ -234,19 +232,6 @@ PHP_METHOD(RedisArray, __construct) if(FAILURE != zend_hash_find(hOpts, "autorehash", sizeof("autorehash"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { b_autorehash = Z_BVAL_PP(zpData); } - - /* extract retry_interval option. */ - zval **z_retry_interval_pp; - if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) { - if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) { - if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) { - l_retry_interval = Z_LVAL_PP(z_retry_interval_pp); - } - else { - l_retry_interval = atol(Z_STRVAL_PP(z_retry_interval_pp)); - } - } - } } /* extract either name of list of hosts from z0 */ @@ -256,7 +241,7 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index TSRMLS_CC); break; default: @@ -703,46 +688,6 @@ PHP_METHOD(RedisArray, setOption) efree(z_args[0]); efree(z_args[1]); } - -PHP_METHOD(RedisArray, select) -{ - zval *object, z_fun, *z_tmp, *z_args[2]; - int i; - RedisArray *ra; - long opt; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", - &object, redis_array_ce, &opt) == FAILURE) { - RETURN_FALSE; - } - - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { - RETURN_FALSE; - } - - /* prepare call */ - ZVAL_STRING(&z_fun, "select", 0); - - /* copy args */ - MAKE_STD_ZVAL(z_args[0]); - ZVAL_LONG(z_args[0], opt); - - array_init(return_value); - for(i = 0; i < ra->count; ++i) { - - MAKE_STD_ZVAL(z_tmp); - - /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], - &z_fun, z_tmp, 1, z_args TSRMLS_CC); - - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } - - /* cleanup */ - efree(z_args[0]); -} - #define HANDLE_MULTI_EXEC(cmd) do {\ if (redis_array_get(getThis(), &ra TSRMLS_CC) >= 0 && ra->z_multi_exec) {\ int i, num_varargs;\ diff --git a/redis_array.h b/redis_array.h index b2c7d86a50..bc7fdd8842 100644 --- a/redis_array.h +++ b/redis_array.h @@ -15,7 +15,6 @@ PHP_METHOD(RedisArray, _function); PHP_METHOD(RedisArray, _distributor); PHP_METHOD(RedisArray, _rehash); -PHP_METHOD(RedisArray, select); PHP_METHOD(RedisArray, info); PHP_METHOD(RedisArray, ping); PHP_METHOD(RedisArray, mget); diff --git a/redis_array_impl.c b/redis_array_impl.c index c38e2fe661..d5370c826e 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -29,7 +29,7 @@ extern int le_redis_sock; extern zend_class_entry *redis_ce; RedisArray* -ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC) +ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC) { int i, host_len, id; int count = zend_hash_num_elements(hosts); @@ -67,7 +67,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC) call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC); /* create socket */ - redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL, retry_interval); /* TODO: persistence? */ + redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL); /* TODO: persistence? */ /* connect */ redis_sock_server_open(redis_sock, 1 TSRMLS_CC); @@ -158,11 +158,9 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval *z_params_funs, **z_data_pp, *z_fun = NULL, *z_dist = NULL; zval *z_params_index; zval *z_params_autorehash; - zval *z_params_retry_interval; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; - long l_retry_interval = 0; HashTable *hHosts = NULL, *hPrev = NULL; /* find entry */ @@ -225,23 +223,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } - /* find retry interval option */ - MAKE_STD_ZVAL(z_params_retry_interval); - array_init(z_params_retry_interval); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.retryinterval")), z_params_retry_interval TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_retry_interval), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { - if (Z_TYPE_PP(z_data_pp) == IS_LONG || Z_TYPE_PP(z_data_pp) == IS_STRING) { - if (Z_TYPE_PP(z_data_pp) == IS_LONG) { - l_retry_interval = Z_LVAL_PP(z_data_pp); - } - else { - l_retry_interval = atol(Z_STRVAL_PP(z_data_pp)); - } - } - } - /* create RedisArray object */ - ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); + ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index TSRMLS_CC); ra->auto_rehash = b_autorehash; /* cleanup */ @@ -255,14 +238,12 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { efree(z_params_index); zval_dtor(z_params_autorehash); efree(z_params_autorehash); - zval_dtor(z_params_retry_interval); - efree(z_params_retry_interval); return ra; } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC) { int count = zend_hash_num_elements(hosts); @@ -280,10 +261,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* init array data structures */ ra_init_function_table(ra); - if(NULL == ra_load_hosts(ra, hosts, retry_interval TSRMLS_CC)) { + if(NULL == ra_load_hosts(ra, hosts TSRMLS_CC)) { return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index TSRMLS_CC) : NULL; /* copy function if provided */ if(z_fun) { @@ -1131,7 +1112,7 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z zval *z_host, *z_count; z_cb->retval_ptr_ptr = &z_ret; - z_cb->params = (struct _zval_struct ***)&z_args; + z_cb->params = &z_args; z_cb->param_count = 2; z_cb->no_separation = 0; diff --git a/redis_array_impl.h b/redis_array_impl.h index 8dd5201bc8..0e00258c3b 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -5,9 +5,9 @@ #include "common.h" #include "redis_array.h" -RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC); +RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); diff --git a/redis_session.c b/redis_session.c index 009bac51b2..95d9053302 100644 --- a/redis_session.c +++ b/redis_session.c @@ -206,7 +206,6 @@ PS_OPEN_FUNC(redis) int persistent = 0; int database = -1; char *prefix = NULL, *auth = NULL, *persistent_id = NULL; - long retry_interval = 0; /* translate unix: into file: */ if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) { @@ -241,6 +240,7 @@ PS_OPEN_FUNC(redis) convert_to_long_ex(param); weight = Z_LVAL_PP(param); } + if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { timeout = atof(Z_STRVAL_PP(param)); } @@ -260,10 +260,13 @@ PS_OPEN_FUNC(redis) convert_to_long_ex(param); database = Z_LVAL_PP(param); } + + /* // not supported yet if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { convert_to_long_ex(param); retry_interval = Z_LVAL_PP(param); } + */ zval_ptr_dtor(¶ms); } @@ -277,9 +280,9 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if(url->host) { - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id); } else { /* unix */ - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id); } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); From d4c7f6413136a44d4afdfa416b7fe5553dce97d0 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Tue, 29 Jan 2013 11:47:36 +0200 Subject: [PATCH 0011/1986] New select DB command to RedisArray - Added retry delay on reconnect Added the possibility to delay each reconnection attempt, including a random factor to prevent several or many concurrent connections from trying to reconnect at the same time. Added the select command to RedisArray to select a DB on every connections in one instruction. Also, fixed a compiler warning: redis_array_impl.c:1115:15: warning: incompatible pointer types assigning to 'zval **' (aka 'struct _zval_struct **') from 'zval **(*)[2]' [-Wincompatible-pointer-types] --- common.h | 1 + library.c | 14 +++++++++--- library.h | 2 +- redis.c | 15 ++++++++---- redis_array.c | 57 +++++++++++++++++++++++++++++++++++++++++++++- redis_array.h | 1 + redis_array_impl.c | 33 +++++++++++++++++++++------ redis_array_impl.h | 4 ++-- redis_session.c | 9 +++----- 9 files changed, 112 insertions(+), 24 deletions(-) diff --git a/common.h b/common.h index b48ac75b06..fda59c6aeb 100644 --- a/common.h +++ b/common.h @@ -156,6 +156,7 @@ typedef struct { char *host; short port; double timeout; + long retry_interval; int failed; int status; int persistent; diff --git a/library.c b/library.c index a954787f5b..be3907f481 100644 --- a/library.c +++ b/library.c @@ -38,8 +38,9 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) int eof; int count = 0; - if (!redis_sock->stream) + if (!redis_sock->stream) { return -1; + } eof = php_stream_eof(redis_sock->stream); for (; eof; count++) { @@ -60,6 +61,12 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) redis_sock->mode = ATOMIC; redis_sock->watching = 0; } + // Wait for a while before trying to reconnect + if (redis_sock->retry_interval) { + // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time + long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval)); + usleep(retry_interval); + } redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ if(redis_sock->stream) { /* check for EOF again. */ eof = php_stream_eof(redis_sock->stream); @@ -821,7 +828,8 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s * redis_sock_create */ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, - double timeout, int persistent, char *persistent_id) + double timeout, int persistent, char *persistent_id, + long retry_interval) { RedisSock *redis_sock; @@ -831,7 +839,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; redis_sock->dbNumber = 0; - + redis_sock->retry_interval = retry_interval * 1000; redis_sock->persistent = persistent; if(persistent_id) { diff --git a/library.h b/library.h index ea135bfbc5..20eaa08a40 100644 --- a/library.h +++ b/library.h @@ -19,7 +19,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id); +PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval); PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index fd103d30f9..0562a79bbc 100644 --- a/redis.c +++ b/redis.c @@ -520,7 +520,7 @@ PHP_METHOD(Redis,__destruct) { } } -/* {{{ proto boolean Redis::connect(string host, int port [, double timeout]) +/* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]]) */ PHP_METHOD(Redis, connect) { @@ -556,6 +556,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { int host_len, id; char *host = NULL; long port = -1; + long retry_interval = 0; char *persistent_id = NULL; int persistent_id_len = -1; @@ -568,9 +569,10 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { persistent = 0; #endif - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|lds", + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl", &object, redis_ce, &host, &host_len, &port, - &timeout, &persistent_id, &persistent_id_len) == FAILURE) { + &timeout, &persistent_id, &persistent_id_len, + &retry_interval) == FAILURE) { return FAILURE; } @@ -579,6 +581,11 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { return FAILURE; } + if (retry_interval < 0L || retry_interval > INT_MAX) { + zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC); + return FAILURE; + } + if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */ port = 6379; } @@ -595,7 +602,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zend_clear_exception(TSRMLS_C); /* clear exception triggered by non-existent socket during connect(). */ } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval); if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { redis_free_socket(redis_sock); diff --git a/redis_array.c b/redis_array.c index d0460f1021..be95c50d5a 100644 --- a/redis_array.c +++ b/redis_array.c @@ -51,6 +51,7 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, _rehash, NULL, ZEND_ACC_PUBLIC) /* special implementation for a few functions */ + PHP_ME(RedisArray, select, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, info, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mget, NULL, ZEND_ACC_PUBLIC) @@ -192,6 +193,7 @@ PHP_METHOD(RedisArray, __construct) RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; HashTable *hPrev = NULL, *hOpts = NULL; + long l_retry_interval = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { RETURN_FALSE; @@ -232,6 +234,19 @@ PHP_METHOD(RedisArray, __construct) if(FAILURE != zend_hash_find(hOpts, "autorehash", sizeof("autorehash"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { b_autorehash = Z_BVAL_PP(zpData); } + + /* extract retry_interval option. */ + zval **z_retry_interval_pp; + if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) { + if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) { + if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) { + l_retry_interval = Z_LVAL_PP(z_retry_interval_pp); + } + else { + l_retry_interval = atol(Z_STRVAL_PP(z_retry_interval_pp)); + } + } + } } /* extract either name of list of hosts from z0 */ @@ -241,7 +256,7 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); break; default: @@ -688,6 +703,46 @@ PHP_METHOD(RedisArray, setOption) efree(z_args[0]); efree(z_args[1]); } + +PHP_METHOD(RedisArray, select) +{ + zval *object, z_fun, *z_tmp, *z_args[2]; + int i; + RedisArray *ra; + long opt; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, redis_array_ce, &opt) == FAILURE) { + RETURN_FALSE; + } + + if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + RETURN_FALSE; + } + + /* prepare call */ + ZVAL_STRING(&z_fun, "select", 0); + + /* copy args */ + MAKE_STD_ZVAL(z_args[0]); + ZVAL_LONG(z_args[0], opt); + + array_init(return_value); + for(i = 0; i < ra->count; ++i) { + + MAKE_STD_ZVAL(z_tmp); + + /* Call each node in turn */ + call_user_function(&redis_ce->function_table, &ra->redis[i], + &z_fun, z_tmp, 1, z_args TSRMLS_CC); + + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } + + /* cleanup */ + efree(z_args[0]); +} + #define HANDLE_MULTI_EXEC(cmd) do {\ if (redis_array_get(getThis(), &ra TSRMLS_CC) >= 0 && ra->z_multi_exec) {\ int i, num_varargs;\ diff --git a/redis_array.h b/redis_array.h index bc7fdd8842..b2c7d86a50 100644 --- a/redis_array.h +++ b/redis_array.h @@ -15,6 +15,7 @@ PHP_METHOD(RedisArray, _function); PHP_METHOD(RedisArray, _distributor); PHP_METHOD(RedisArray, _rehash); +PHP_METHOD(RedisArray, select); PHP_METHOD(RedisArray, info); PHP_METHOD(RedisArray, ping); PHP_METHOD(RedisArray, mget); diff --git a/redis_array_impl.c b/redis_array_impl.c index d5370c826e..c38e2fe661 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -29,7 +29,7 @@ extern int le_redis_sock; extern zend_class_entry *redis_ce; RedisArray* -ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC) +ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC) { int i, host_len, id; int count = zend_hash_num_elements(hosts); @@ -67,7 +67,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC) call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC); /* create socket */ - redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL); /* TODO: persistence? */ + redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL, retry_interval); /* TODO: persistence? */ /* connect */ redis_sock_server_open(redis_sock, 1 TSRMLS_CC); @@ -158,9 +158,11 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval *z_params_funs, **z_data_pp, *z_fun = NULL, *z_dist = NULL; zval *z_params_index; zval *z_params_autorehash; + zval *z_params_retry_interval; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; + long l_retry_interval = 0; HashTable *hHosts = NULL, *hPrev = NULL; /* find entry */ @@ -223,8 +225,23 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } + /* find retry interval option */ + MAKE_STD_ZVAL(z_params_retry_interval); + array_init(z_params_retry_interval); + sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.retryinterval")), z_params_retry_interval TSRMLS_CC); + if (zend_hash_find(Z_ARRVAL_P(z_params_retry_interval), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if (Z_TYPE_PP(z_data_pp) == IS_LONG || Z_TYPE_PP(z_data_pp) == IS_STRING) { + if (Z_TYPE_PP(z_data_pp) == IS_LONG) { + l_retry_interval = Z_LVAL_PP(z_data_pp); + } + else { + l_retry_interval = atol(Z_STRVAL_PP(z_data_pp)); + } + } + } + /* create RedisArray object */ - ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index TSRMLS_CC); + ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); ra->auto_rehash = b_autorehash; /* cleanup */ @@ -238,12 +255,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { efree(z_params_index); zval_dtor(z_params_autorehash); efree(z_params_autorehash); + zval_dtor(z_params_retry_interval); + efree(z_params_retry_interval); return ra; } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC) { int count = zend_hash_num_elements(hosts); @@ -261,10 +280,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* init array data structures */ ra_init_function_table(ra); - if(NULL == ra_load_hosts(ra, hosts TSRMLS_CC)) { + if(NULL == ra_load_hosts(ra, hosts, retry_interval TSRMLS_CC)) { return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval TSRMLS_CC) : NULL; /* copy function if provided */ if(z_fun) { @@ -1112,7 +1131,7 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z zval *z_host, *z_count; z_cb->retval_ptr_ptr = &z_ret; - z_cb->params = &z_args; + z_cb->params = (struct _zval_struct ***)&z_args; z_cb->param_count = 2; z_cb->no_separation = 0; diff --git a/redis_array_impl.h b/redis_array_impl.h index 0e00258c3b..8dd5201bc8 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -5,9 +5,9 @@ #include "common.h" #include "redis_array.h" -RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC); +RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); diff --git a/redis_session.c b/redis_session.c index 95d9053302..009bac51b2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -206,6 +206,7 @@ PS_OPEN_FUNC(redis) int persistent = 0; int database = -1; char *prefix = NULL, *auth = NULL, *persistent_id = NULL; + long retry_interval = 0; /* translate unix: into file: */ if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) { @@ -240,7 +241,6 @@ PS_OPEN_FUNC(redis) convert_to_long_ex(param); weight = Z_LVAL_PP(param); } - if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { timeout = atof(Z_STRVAL_PP(param)); } @@ -260,13 +260,10 @@ PS_OPEN_FUNC(redis) convert_to_long_ex(param); database = Z_LVAL_PP(param); } - - /* // not supported yet if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { convert_to_long_ex(param); retry_interval = Z_LVAL_PP(param); } - */ zval_ptr_dtor(¶ms); } @@ -280,9 +277,9 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if(url->host) { - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval); } else { /* unix */ - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval); } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); From 87dddda2abb7c70e4710d1759db005547456c7f2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 4 Feb 2013 13:10:48 -0800 Subject: [PATCH 0012/1986] Update documentation to new format for our count argument --- README.markdown | 3861 ++++++++++++++++++++++++++--------------------- 1 file changed, 2102 insertions(+), 1759 deletions(-) diff --git a/README.markdown b/README.markdown index 1489e94344..6fe6ce5702 100644 --- a/README.markdown +++ b/README.markdown @@ -1,54 +1,80 @@ -PhpRedis -============= +# PhpRedis + The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). This code has been developed and maintained by Owlient from November 2009 to March 2011. You can send comments, patches, questions [here on github](https://github.com/nicolasff/phpredis/issues) or to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)). -Installing/Configuring -====================== - -
+# Table of contents
+-----
+1. [Installing/Configuring](#installingconfiguring)
+   * [Installation](#installation)
+   * [Installation on OSX](#installation-on-osx)
+   * [PHP Session handler](#php-session-handler)
+   * [Distributed Redis Array](#distributed-redis-array)
+1. [Classes and methods](#classes-and-methods)
+   * [Usage](#usage)
+   * [Connection](#connection)
+   * [Server](#server)
+   * [Keys and strings](#keys-and-strings)
+   * [Hashes](#hashes)
+   * [Lists](#lists)
+   * [Sets](#sets)
+   * [Sorted sets](#sorted-sets)
+   * [Pub/sub](#pubsub)
+   * [Transactions](#transactions)
+   * [Scripting](#scripting)
+
+-----
+
+# Installing/Configuring
+-----
+
+Everything you should need to install PhpRedis on your system.
+
+## Installation
+
+~~~
 phpize
 ./configure [--enable-redis-igbinary]
 make && make install
-
+~~~ If you would like phpredis to serialize your data using the igbinary library, run configure with `--enable-redis-igbinary`. `make install` copies `redis.so` to an appropriate location, but you still need to enable the module in the PHP config file. To do so, either edit your php.ini or add a redis.ini file in `/etc/php5/conf.d` with the following contents: `extension=redis.so`. You can generate a debian package for PHP5, accessible from Apache 2 by running `./mkdeb-apache2.sh` or with `dpkg-buildpackage` or `svn-buildpackage`. -This extension exports a single class, `Redis` (and `RedisException` used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion. +This extension exports a single class, [Redis](#class-redis) (and [RedisException](#class-redisexception) used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion. -Install on OSX -============== + +## Installation on OSX If the install fails on OSX, type the following commands in your shell before trying again: -
+~~~
 MACOSX_DEPLOYMENT_TARGET=10.6
 CFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp"
 CCFLAGS="-arch i386 -arch x86_64 -g -Os -pipe"
 CXXFLAGS="-arch i386 -arch x86_64 -g -Os -pipe"
 LDFLAGS="-arch i386 -arch x86_64 -bind_at_load"
 export CFLAGS CXXFLAGS LDFLAGS CCFLAGS MACOSX_DEPLOYMENT_TARGET
-
+~~~ + +If that still fails and you are running Zend Server CE, try this right before "make": `./configure CFLAGS="-arch i386"`. -If that still fails and you are running Zend Server CE, try this right before "make": -
./configure CFLAGS="-arch i386"
Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tagged/phpredis). See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports). -Session handler (new) -============== + +## PHP Session handler phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions: -
+~~~
 session.save_handler = redis
 session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2"
-
+~~~ `session.save_path` can have a simple `host:port` format too, but you need to provide the `tcp://` scheme if you want to use the parameters. The following parameters are available: @@ -56,42 +82,75 @@ session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeou * timeout (float): the connection timeout to a redis host, expressed in seconds. If the host is unreachable in that amount of time, the session storage will be unavailable for the client. The default timeout is very high (86400 seconds). * persistent (integer, should be 1 or 0): defines if a persistent connection should be used. **(experimental setting)** * prefix (string, defaults to "PHPREDIS_SESSION:"): used as a prefix to the Redis key in which the session is stored. The key is composed of the prefix followed by the session ID. -* auth (string, empty by default): used to authenticate with the Redis server prior to sending commands. +* auth (string, empty by default): used to authenticate with the server prior to sending commands. * database (integer): selects a different database. Sessions have a lifetime expressed in seconds and stored in the INI variable "session.gc_maxlifetime". You can change it with [`ini_set()`](http://php.net/ini_set). The session handler requires a version of Redis with the `SETEX` command (at least 2.0). -Distributed Redis Array -======================= +## Distributed Redis Array See [dedicated page](https://github.com/nicolasff/phpredis/blob/master/arrays.markdown#readme). -Error handling -============== -phpredis throws a `RedisException` object if it can't reach the Redis server. That can happen in case of connectivity issues, if the Redis service is down, or if the redis host is overloaded. In any other problematic case that does not involve an unreachable server (such as a key not existing, an invalid command, etc), phpredis will return `FALSE`. +# Classes and methods +----- -Methods -========= +## Usage -## Redis::__construct -##### *Description* +1. [Class Redis](#class-redis) +1. [Class RedisException](#class-redisexception) +1. [Predefined constants](#predefined-constants) -Creates a Redis client +### Class Redis +----- +_**Description**_: Creates a Redis client ##### *Example* -
+~~~
 $redis = new Redis();
-
+~~~ + +### Class RedisException +----- +phpredis throws a [RedisException](#class-redisexception) object if it can't reach the Redis server. That can happen in case of connectivity issues, +if the Redis service is down, or if the redis host is overloaded. In any other problematic case that does not involve an +unreachable server (such as a key not existing, an invalid command, etc), phpredis will return `FALSE`. + +### Predefined constants +----- +_**Description**_: Available Redis Constants + +Redis data types, as returned by [type](#type) +~~~ +Redis::REDIS_STRING - String +Redis::REDIS_SET - Set +Redis::REDIS_LIST - List +Redis::REDIS_ZSET - Sorted set +Redis::REDIS_HASH - Hash +Redis::REDIS_NOT_FOUND - Not found / other +~~~ -## connect, open -##### *Description* +@TODO: OPT_SERIALIZER, AFTER, BEFORE,... -Connects to a Redis instance. +## Connection + +1. [connect, open](#connect-open) - Connect to a server +1. [pconnect, popen](#pconnect-popen) - Connect to a server (persistent) +1. [auth](#auth) - Authenticate to the server +1. [select](#select) - Change the selected database for the current connection +1. [close](#close) - Close the connection +1. [setOption](#setoption) - Set client option +1. [getOption](#getoption) - Get client option +1. [ping](#ping) - Ping the server +1. [echo](#echo) - Echo the given string + +### connect, open +----- +_**Description**_: Connects to a Redis instance. ##### *Parameters* @@ -99,23 +158,22 @@ Connects to a Redis instance. *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) -##### *Return Value* +##### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. ##### *Example* -
+~~~
 $redis->connect('127.0.0.1', 6379);
 $redis->connect('127.0.0.1'); // port 6379 by default
 $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
 $redis->connect('/tmp/redis.sock'); // unix domain socket.
-
- -## pconnect, popen -##### *Description* +~~~ -Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. +### pconnect, popen +----- +_**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. The connection will not be closed on `close` or end of request until the php process ends. So be patient on to many open FD's (specially on redis server side) when using persistent @@ -134,27 +192,55 @@ persistent equivalents. *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) *persistent_id*: string. identity for the requested persistent connection -##### *Return Value* +##### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. ##### *Example* -
+~~~
 $redis->pconnect('127.0.0.1', 6379);
 $redis->pconnect('127.0.0.1'); // port 6379 by default - same connection like before.
 $redis->pconnect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout and would be another connection than the two before.
 $redis->pconnect('127.0.0.1', 6379, 2.5, 'x'); // x is sent as persistent_id and would be another connection the the three before.
 $redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another connection than the four before.
-
+~~~ + +### auth +----- +_**Description**_: Authenticate the connection using a password. +*Warning*: The password is sent in plain-text over the network. + +##### *Parameters* +*STRING*: password + +##### *Return value* +*BOOL*: `TRUE` if the connection is authenticated, `FALSE` otherwise. + +##### *Example* +~~~ +$redis->auth('foobared'); +~~~ + +### select +----- +_**Description**_: Change the selected database for the current connection. + +##### *Parameters* +*INTEGER*: dbindex, the database number to switch to. + +##### *Return value* +`TRUE` in case of success, `FALSE` in case of failure. +##### *Example* +See method for example: [move](#move) -## close -##### *Description* -Disconnects from the Redis instance, except when `pconnect` is used. +### close +----- +_**Description**_: Disconnects from the Redis instance, except when `pconnect` is used. -## setOption -##### *Description* -Set client option. +### setOption +----- +_**Description**_: Set client option. ##### *Parameters* *parameter name* @@ -164,18 +250,18 @@ Set client option. *BOOL*: `TRUE` on success, `FALSE` on error. ##### *Example* -
+~~~
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);	// don't serialize data
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);	// use built-in serialize/unserialize
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);	// use igBinary serialize/unserialize
 
 $redis->setOption(Redis::OPT_PREFIX, 'myAppName:');	// use custom prefix on all keys
-
+~~~ -## getOption -##### *Description* -Get client option. +### getOption +----- +_**Description**_: Get client option. ##### *Parameters* *parameter name* @@ -184,121 +270,395 @@ Get client option. Parameter value. ##### *Example* -
+~~~
 $redis->getOption(Redis::OPT_SERIALIZER);	// return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, or Redis::SERIALIZER_IGBINARY.
-
+~~~ -## ping -##### *Description* - -Check the current connection status +### ping +----- +_**Description**_: Check the current connection status ##### *Parameters* (none) -##### *Return Value* - -*STRING*: `+PONG` on success. Throws a RedisException object on connectivity error, as described above. +##### *Return value* +*STRING*: `+PONG` on success. Throws a [RedisException](#class-redisexception) object on connectivity error, as described above. -## echo -##### *Description* -Sends a string to Redis, which replies with the same string +### echo +----- +_**Description**_: Sends a string to Redis, which replies with the same string ##### *Parameters* *STRING*: The message to send. -##### *Return Value* +##### *Return value* *STRING*: the same message. -## get -##### *Description* +## Server -Get the value related to the specified key +1. [bgrewriteaof](#bgrewriteaof) - Asynchronously rewrite the append-only file +1. [bgsave](#bgsave) - Asynchronously save the dataset to disk (in background) +1. [config](#config) - Get or Set the Redis server configuration parameters +1. [dbSize](#dbsize) - Return the number of keys in selected database +1. [flushAll](#flushall) - Remove all keys from all databases +1. [flushDB](#flushdb) - Remove all keys from the current database +1. [info](#info) - Get information and statistics about the server +1. [lastSave](#lastsave) - Get the timestamp of the last disk save +1. [resetStat](#resetstat) - Reset the stats returned by [info](#info) method. +1. [save](#save) - Synchronously save the dataset to disk (wait to complete) +1. [slaveof](#slaveof) - Make the server a slave of another instance, or promote it to master +1. [time](#time) - Return the current server time + +### bgrewriteaof +----- +_**Description**_: Start the background rewrite of AOF (Append-Only File) ##### *Parameters* +None. -*key* +##### *Return value* +*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. + +##### *Example* +~~~ +$redis->bgrewriteaof(); +~~~ + +### bgsave +----- +_**Description**_: Asynchronously save the dataset to disk (in background) + +##### *Parameters* +None. + +##### *Return value* +*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. + +##### *Example* +~~~ +$redis->bgSave(); +~~~ + +### config +----- +_**Description**_: Get or Set the Redis server configuration parameters. + +##### *Parameters* +*operation* (string) either `GET` or `SET` +*key* string for `SET`, glob-pattern for `GET`. See http://redis.io/commands/config-get for examples. +*value* optional string (only for `SET`) + +##### *Return value* +*Associative array* for `GET`, key -> value +*bool* for `SET` + +##### *Examples* +~~~ +$redis->config("GET", "*max-*-entries*"); +$redis->config("SET", "dir", "/var/run/redis/dumps/"); +~~~ + +### dbSize +----- +_**Description**_: Return the number of keys in selected database. + +##### *Parameters* +None. + +##### *Return value* +*INTEGER*: DB size, in number of keys. + +##### *Example* +~~~ +$count = $redis->dbSize(); +echo "Redis has $count keys\n"; +~~~ + +### flushAll +----- +_**Description**_: Remove all keys from all databases. + +##### *Parameters* +None. + +##### *Return value* +*BOOL*: Always `TRUE`. + +##### *Example* +~~~ +$redis->flushAll(); +~~~ + +### flushDB +----- +_**Description**_: Remove all keys from the current database. + +##### *Parameters* +None. + +##### *Return value* +*BOOL*: Always `TRUE`. + +##### *Example* +~~~ +$redis->flushDB(); +~~~ + +### info +----- +_**Description**_: Get information and statistics about the server + +Returns an associative array that provides information about the server. Passing no arguments to +INFO will call the standard REDIS INFO command, which returns information such as the following: + +* redis_version +* arch_bits +* uptime_in_seconds +* uptime_in_days +* connected_clients +* connected_slaves +* used_memory +* changes_since_last_save +* bgsave_in_progress +* last_save_time +* total_connections_received +* total_commands_processed +* role + +You can pass a variety of options to INFO ([per the Redis documentation](http://redis.io/commands/info)), +which will modify what is returned. + +##### *Parameters* +*option*: The option to provide redis (e.g. "COMMANDSTATS", "CPU") + +##### *Example* +~~~ +$redis->info(); /* standard redis INFO command */ +$redis->info("COMMANDSTATS"); /* Information on the commands that have been run (>=2.6 only) +$redis->info("CPU"); /* just CPU information from Redis INFO */ +~~~ + +### lastSave +----- +_**Description**_: Returns the timestamp of the last disk save. + +##### *Parameters* +None. -##### *Return Value* +##### *Return value* +*INT*: timestamp. + +##### *Example* +~~~ +$redis->lastSave(); +~~~ + +### resetStat +----- +_**Description**_: Reset the stats returned by [info](#info) method. + +These are the counters that are reset: + +* Keyspace hits +* Keyspace misses +* Number of commands processed +* Number of connections received +* Number of expired keys + + +##### *Parameters* +None. + +##### *Return value* +*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. + +##### *Example* +~~~ +$redis->resetStat(); +~~~ + +### save +----- +_**Description**_: Synchronously save the dataset to disk (wait to complete) + +##### *Parameters* +None. + +##### *Return value* +*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. + +##### *Example* +~~~ +$redis->save(); +~~~ + +### slaveof +----- +_**Description**_: Changes the slave status + +##### *Parameters* +Either host (string) and port (int), or no parameter to stop being a slave. + +##### *Return value* +*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. + +##### *Example* +~~~ +$redis->slaveof('10.0.1.7', 6379); +/* ... */ +$redis->slaveof(); +~~~ + +### time +----- +_**Description**_: Return the current server time. + +##### *Parameters* +(none) + +##### *Return value* +If successfull, the time will come back as an associative array with element zero being +the unix timestamp, and element one being microseconds. + +##### *Examples* +~~~ +$redis->time(); +~~~ + +## Keys and Strings + +### Strings +----- + +* [append](#append) - Append a value to a key +* [bitcount](#bitcount) - Count set bits in a string +* [bitop](#bitop) - Perform bitwise operations between strings +* [decr, decrBy](#decr-decrby) - Decrement the value of a key +* [get](#get) - Get the value of a key +* [getBit](#getbit) - Returns the bit value at offset in the string value stored at key +* [getRange](#getrange) - Get a substring of the string stored at a key +* [getSet](#getset) - Set the string value of a key and return its old value +* [incr, incrBy](#incr-incrby) - Increment the value of a key +* [incrByFloat](#incrbyfloat) - Increment the float value of a key by the given amount +* [mGet, getMultiple](#mget-getmultiple) - Get the values of all the given keys +* [mSet, mSetNX](#mset-msetnx) - Set multiple keys to multiple values +* [set](#set) - Set the string value of a key +* [setBit](#setbit) - Sets or clears the bit at offset in the string value stored at key +* [setex, psetex](#setex-psetex) - Set the value and expiration of a key +* [setnx](#setnx) - Set the value of a key, only if the key does not exist +* [setRange](#setrange) - Overwrite part of a string at key starting at the specified offset +* [strlen](#strlen) - Get the length of the value stored in a key + +### Keys +----- + +* [del, delete](#del-delete) - Delete a key +* [dump](#dump) - Return a serialized version of the value stored at the specified key. +* [exists](#exists) - Determine if a key exists +* [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds +* [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp +* [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern +* [migrate](#migrate) - Atomically transfer a key from a Redis instance to another one +* [move](#move) - Move a key to another database +* [object](#object) - Inspect the internals of Redis objects +* [persist](#persist) - Remove the expiration from a key +* [randomKey](#randomkey) - Return a random key from the keyspace +* [rename, renameKey](#rename-renamekey) - Rename a key +* [renameNx](#renamenx) - Rename a key, only if the new key does not exist +* [type](#type) - Determine the type stored at key +* [sort](#sort) - Sort the elements in a list, set or sorted set +* [ttl, pttl](#ttl-pttl) - Get the time to live for a key +* [restore](#restore) - Create a key using the provided serialized value, previously obtained with [dump](#dump). + +----- + +### get +----- +_**Description**_: Get the value related to the specified key + +##### *Parameters* +*key* +##### *Return value* *String* or *Bool*: If key didn't exist, `FALSE` is returned. Otherwise, the value related to this key is returned. ##### *Examples* -
+~~~
 $redis->get('key');
-
- -## set -##### Description +~~~ -Set the string value in argument as value of the key. +### set +----- +_**Description**_: Set the string value in argument as value of the key. -##### Parameters +##### *Parameters* *Key* *Value* *Timeout* (optional). Calling `SETEX` is preferred if you want a timeout. -##### Return value +##### *Return value* *Bool* `TRUE` if the command is successful. -##### Examples - -
+##### *Examples*
+~~~
 $redis->set('key', 'value');
-
+~~~ -## setex, psetex -##### Description +### setex, psetex +----- +_**Description**_: Set the string value in argument as value of the key, with a time to live. PSETEX uses a TTL in milliseconds. -Set the string value in argument as value of the key, with a time to live. PSETEX uses a TTL in milliseconds. - -##### Parameters +##### *Parameters* *Key* *TTL* *Value* -##### Return value +##### *Return value* *Bool* `TRUE` if the command is successful. -##### Examples +##### *Examples* -
+~~~
 $redis->setex('key', 3600, 'value'); // sets key → value, with 1h TTL.
 $redis->psetex('key', 100, 'value'); // sets key → value, with 0.1 sec TTL.
-
-## setnx -##### Description -Set the string value in argument as value of the key if the key doesn't already exist in the database. +~~~ -##### Parameters +### setnx +----- +_**Description**_: Set the string value in argument as value of the key if the key doesn't already exist in the database. + +##### *Parameters* *key* *value* -##### Return value +##### *Return value* *Bool* `TRUE` in case of success, `FALSE` in case of failure. -##### Examples -
+##### *Examples*
+~~~
 $redis->setnx('key', 'value'); /* return TRUE */
 $redis->setnx('key', 'value'); /* return FALSE */
-
+~~~ -## del, delete -##### Description -Remove specified keys. -##### Parameters +### del, delete +----- +_**Description**_: Remove specified keys. + +##### *Parameters* An array of keys, or an undefined number of parameters, each a key: *key1* *key2* *key3* ... *keyN* -##### Return value + +##### *Return value* *Long* Number of keys deleted. -##### Examples -
+
+##### *Examples*
+~~~
 $redis->set('key1', 'val1');
 $redis->set('key2', 'val2');
 $redis->set('key3', 'val3');
@@ -306,128 +666,39 @@ $redis->set('key4', 'val4');
 
 $redis->delete('key1', 'key2'); /* return 2 */
 $redis->delete(array('key3', 'key4')); /* return 2 */
-
+~~~ -## multi, exec, discard. -##### Description -Enter and exit transactional mode. -##### Parameters -(optional) `Redis::MULTI` or `Redis::PIPELINE`. Defaults to `Redis::MULTI`. A `Redis::MULTI` block of commands runs as a single transaction; a `Redis::PIPELINE` block is simply transmitted faster to the server, but without any guarantee of atomicity. `discard` cancels a transaction. -##### Return value -`multi()` returns the Redis instance and enters multi-mode. Once in multi-mode, all subsequent method calls return the same object until `exec()` is called. -##### Example -
-$ret = $redis->multi()
-    ->set('key1', 'val1')
-    ->get('key1')
-    ->set('key2', 'val2')
-    ->get('key2')
-    ->exec();
 
-/*
-$ret == array(
-    0 => TRUE,
-    1 => 'val1',
-    2 => TRUE,
-    3 => 'val2');
-*/
-
+### exists +----- +_**Description**_: Verify if the specified key exists. -## watch, unwatch -##### Description -Watches a key for modifications by another client. If the key is modified between `WATCH` and `EXEC`, the MULTI/EXEC transaction will fail (return `FALSE`). `unwatch` cancels all the watching of all keys by this client. -##### Parameters -*keys*: a list of keys -##### Example -
-$redis->watch('x');
-/* long code here during the execution of which other clients could well modify `x` */
-$ret = $redis->multi()
-    ->incr('x')
-    ->exec();
-/*
-$ret = FALSE if x has been modified between the call to WATCH and the call to EXEC.
-*/
-
+##### *Parameters* +*key* -## subscribe -##### Description -Subscribe to channels. Warning: this function will probably change in the future. -##### Parameters -*channels*: an array of channels to subscribe to -*callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. -##### Example -
-function f($redis, $chan, $msg) {
-	switch($chan) {
-		case 'chan-1':
-			...
-			break;
+##### *Return value*
+*BOOL*: If the key exists, return `TRUE`, otherwise return `FALSE`.
 
-		case 'chan-2':
-			...
-			break;
+##### *Examples*
+~~~
+$redis->set('key', 'value');
+$redis->exists('key'); /*  TRUE */
+$redis->exists('NonExistingKey'); /* FALSE */
+~~~
 
-		case 'chan-2':
-			...
-			break;
-	}
-}
+### incr, incrBy
+-----
+_**Description**_: Increment the number stored at key by one. If the second argument is filled, it will be used as the integer value of the increment.
 
-$redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
-
- -## psubscribe -##### Description -Subscribe to channels by pattern -##### Parameters -*patterns*: An array of patterns to match -*callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) -##### Example -
-function psubscribe($redis, $pattern, $chan, $msg) {
-	echo "Pattern: $pattern\n";
-	echo "Channel: $chan\n";
-	echo "Payload: $msg\n";
-}
-
- -## publish -##### Description -Publish messages to channels. Warning: this function will probably change in the future. -##### Parameters -*channel*: a channel to publish to -*messsage*: string -##### Example -
-$redis->publish('chan-1', 'hello, world!'); // send message.
-
- - -## exists -##### Description -Verify if the specified key exists. -##### Parameters -*key* -##### Return value -*BOOL*: If the key exists, return `TRUE`, otherwise return `FALSE`. -##### Examples -
-$redis->set('key', 'value');
-$redis->exists('key'); /*  TRUE */
-$redis->exists('NonExistingKey'); /* FALSE */
-
- -## incr, incrBy -##### Description -Increment the number stored at key by one. If the second argument is filled, it will be used as the integer value of the increment. -##### Parameters +##### *Parameters* *key* *value*: value that will be added to key (only for incrBy) -##### Return value + +##### *Return value* *INT* the new value -##### Examples -
+
+##### *Examples*
+~~~
 $redis->incr('key1'); /* key1 didn't exists, set to 0 before the increment */
 					  /* and now has the value 1  */
 
@@ -435,1435 +706,1648 @@ $redis->incr('key1'); /* 2 */
 $redis->incr('key1'); /* 3 */
 $redis->incr('key1'); /* 4 */
 $redis->incrBy('key1', 10); /* 14 */
-
+~~~ + +### incrByFloat +----- +_**Description**_: Increment the key with floating point precision. -## incrByFloat -##### Description -Increment the key with floating point precision. -##### Parameters +##### *Parameters* *key* *value*: (float) value that will be added to the key -##### Return value + +##### *Return value* *FLOAT* the new value -##### Examples -
+
+##### *Examples*
+~~~
 $redis->incrByFloat('key1', 1.5); /* key1 didn't exist, so it will now be 1.5 */
 
 
 $redis->incrByFloat('key1', 1.5); /* 3 */
 $redis->incrByFloat('key1', -1.5); /* 1.5 */
 $redis->incrByFloat('key1', 2.5); /* 3.5 */
-
+~~~ + +### decr, decrBy +----- +_**Description**_: Decrement the number stored at key by one. If the second argument is filled, it will be used as the integer value of the decrement. -## decr, decrBy -##### Description -Decrement the number stored at key by one. If the second argument is filled, it will be used as the integer value of the decrement. -##### Parameters +##### *Parameters* *key* *value*: value that will be substracted to key (only for decrBy) -##### Return value + +##### *Return value* *INT* the new value -##### Examples -
+
+##### *Examples*
+~~~
 $redis->decr('key1'); /* key1 didn't exists, set to 0 before the increment */
 					  /* and now has the value -1  */
 
 $redis->decr('key1'); /* -2 */
 $redis->decr('key1'); /* -3 */
 $redis->decrBy('key1', 10); /* -13 */
-
+~~~ + +### mGet, getMultiple +----- +_**Description**_: Get the values of all the specified keys. If one or more keys dont exist, the array will contain `FALSE` at the position of the key. -## mGet, getMultiple -##### Description -Get the values of all the specified keys. If one or more keys dont exist, the array will contain `FALSE` at the position of the key. -##### Parameters +##### *Parameters* *Array*: Array containing the list of the keys -##### Return value + +##### *Return value* *Array*: Array containing the values related to keys in argument -##### Examples -
+
+##### *Examples*
+~~~
 $redis->set('key1', 'value1');
 $redis->set('key2', 'value2');
 $redis->set('key3', 'value3');
 $redis->mGet(array('key1', 'key2', 'key3')); /* array('value1', 'value2', 'value3');
 $redis->mGet(array('key0', 'key1', 'key5')); /* array(`FALSE`, 'value2', `FALSE`);
-
- -## lPush -##### Description -Adds the string value to the head (left) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. -##### Parameters -*key* -*value* String, value to push in key -##### Return value -*LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### Examples -
-$redis->delete('key1');
-$redis->lPush('key1', 'C'); // returns 1
-$redis->lPush('key1', 'B'); // returns 2
-$redis->lPush('key1', 'A'); // returns 3
-/* key1 now points to the following list: [ 'A', 'B', 'C' ] */
-
- -## rPush -##### Description -Adds the string value to the tail (right) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. -##### Parameters -*key* -*value* String, value to push in key -##### Return value -*LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### Examples -
-$redis->delete('key1');
-$redis->rPush('key1', 'A'); // returns 1
-$redis->rPush('key1', 'B'); // returns 2
-$redis->rPush('key1', 'C'); // returns 3
-/* key1 now points to the following list: [ 'A', 'B', 'C' ] */
-
- -## lPushx -##### Description -Adds the string value to the head (left) of the list if the list exists. -##### Parameters -*key* -*value* String, value to push in key -##### Return value -*LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### Examples -
-$redis->delete('key1');
-$redis->lPushx('key1', 'A'); // returns 0
-$redis->lPush('key1', 'A'); // returns 1
-$redis->lPushx('key1', 'B'); // returns 2
-$redis->lPushx('key1', 'C'); // returns 3
-/* key1 now points to the following list: [ 'A', 'B', 'C' ] */
-
- -## rPushx -##### Description -Adds the string value to the tail (right) of the list if the ist exists. `FALSE` in case of Failure. -##### Parameters -*key* -*value* String, value to push in key -##### Return value -*LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### Examples -
-$redis->delete('key1');
-$redis->rPushx('key1', 'A'); // returns 0
-$redis->rPush('key1', 'A'); // returns 1
-$redis->rPushx('key1', 'B'); // returns 2
-$redis->rPushx('key1', 'C'); // returns 3
-/* key1 now points to the following list: [ 'A', 'B', 'C' ] */
-
+~~~ -## lPop -##### *Description* -Return and remove the first element of the list. +### getSet +----- +_**Description**_: Sets a value and returns the previous entry at that key. ##### *Parameters* -*key* -##### *Return value* -*STRING* if command executed successfully -*BOOL* `FALSE` in case of failure (empty list) -##### *Example* -
-$redis->rPush('key1', 'A');
-$redis->rPush('key1', 'B');
-$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */
-$redis->lPop('key1'); /* key1 => [ 'B', 'C' ] */
-
+*Key*: key + +*STRING*: value -## rPop -##### *Description* -Returns and removes the last element of the list. -##### *Parameters* -*key* ##### *Return value* -*STRING* if command executed successfully -*BOOL* `FALSE` in case of failure (empty list) +A string, the previous value located at this key. ##### *Example* -
-$redis->rPush('key1', 'A');
-$redis->rPush('key1', 'B');
-$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */
-$redis->rPop('key1'); /* key1 => [ 'A', 'B' ] */
-
+~~~ +$redis->set('x', '42'); +$exValue = $redis->getSet('x', 'lol'); // return '42', replaces x by 'lol' +$newValue = $redis->get('x')' // return 'lol' +~~~ -## blPop, brPop -##### *Description* -Is a blocking lPop(rPop) primitive. If at least one of the lists contains at least one element, the element will be popped from the head of the list and returned to the caller. -Il all the list identified by the keys passed in arguments are empty, blPop will block during the specified timeout until an element is pushed to one of those lists. This element will be popped. +### randomKey +----- +_**Description**_: Returns a random key. ##### *Parameters* -*ARRAY* Array containing the keys of the lists -*INTEGER* Timeout -Or -*STRING* Key1 -*STRING* Key2 -*STRING* Key3 -... -*STRING* Keyn -*INTEGER* Timeout +None. ##### *Return value* -*ARRAY* array('listName', 'element') +*STRING*: an existing key in redis. ##### *Example* -
-/* Non blocking feature */
-$redis->lPush('key1', 'A');
-$redis->delete('key2');
+~~~
+$key = $redis->randomKey();
+$surprise = $redis->get($key);	// who knows what's in there.
+~~~
 
-$redis->blPop('key1', 'key2', 10); /* array('key1', 'A') */
-/* OR */
-$redis->blPop(array('key1', 'key2'), 10); /* array('key1', 'A') */
+### move
+-----
+_**Description**_: Moves a key to a different database.
 
-$redis->brPop('key1', 'key2', 10); /* array('key1', 'A') */
-/* OR */
-$redis->brPop(array('key1', 'key2'), 10); /* array('key1', 'A') */
+##### *Parameters*
+*Key*: key, the key to move.
 
-/* Blocking feature */
+*INTEGER*: dbindex, the database number to move the key to.
 
-/* process 1 */
-$redis->delete('key1');
-$redis->blPop('key1', 10);
-/* blocking for 10 seconds */
+##### *Return value*
+*BOOL*: `TRUE` in case of success, `FALSE` in case of failure.
+##### *Example*
 
-/* process 2 */
-$redis->lPush('key1', 'A');
+~~~
+$redis->select(0);	// switch to DB 0
+$redis->set('x', '42');	// write 42 to x
+$redis->move('x', 1);	// move to DB 1
+$redis->select(1);	// switch to DB 1
+$redis->get('x');	// will return 42
+~~~
 
-/* process 1 */
-/* array('key1', 'A') is returned*/
+### rename, renameKey
+-----
+_**Description**_: Renames a key.
+##### *Parameters*
+*STRING*: srckey, the key to rename.
 
-
+*STRING*: dstkey, the new name for the key. -## lSize -##### *Description* -Returns the size of a list identified by Key. If the list didn't exist or is empty, the command returns 0. If the data type identified by Key is not a list, the command return `FALSE`. -##### *Parameters* -*Key* ##### *Return value* -*LONG* The size of the list identified by Key exists. -*BOOL* `FALSE` if the data type identified by Key is not list - +*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -
-$redis->rPush('key1', 'A');
-$redis->rPush('key1', 'B');
-$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */
-$redis->lSize('key1');/* 3 */
-$redis->rPop('key1'); 
-$redis->lSize('key1');/* 2 */
-
+~~~ +$redis->set('x', '42'); +$redis->rename('x', 'y'); +$redis->get('y'); // → 42 +$redis->get('x'); // → `FALSE` +~~~ -## lIndex, lGet -##### *Description* -Return the specified element of the list stored at the specified key. -0 the first element, 1 the second ... --1 the last element, -2 the penultimate ... -Return `FALSE` in case of a bad index or a key that doesn't point to a list. -##### *Parameters* -*key* -*index* +### renameNx +----- +_**Description**_: Same as rename, but will not replace a key if the destination already exists. This is the same behaviour as setNx. -##### *Return value* -*String* the element at this index -*Bool* `FALSE` if the key identifies a non-string data type, or no value corresponds to this index in the list `Key`. -##### *Example* -
-$redis->rPush('key1', 'A');
-$redis->rPush('key1', 'B');
-$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */
-$redis->lGet('key1', 0); /* 'A' */
-$redis->lGet('key1', -1); /* 'C' */
-$redis->lGet('key1', 10); /* `FALSE` */
-
+### expire, setTimeout, pexpire +----- +_**Description**_: Sets an expiration date (a timeout) on an item. pexpire requires a TTL in milliseconds. -## lSet -##### *Description* -Set the list at index with the new value. ##### *Parameters* -*key* -*index* -*value* +*Key*: key. The key that will disappear. + +*Integer*: ttl. The key's remaining Time To Live, in seconds. + ##### *Return value* -*BOOL* `TRUE` if the new value is setted. `FALSE` if the index is out of range, or data type identified by key is not a list. +*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -
-$redis->rPush('key1', 'A');
-$redis->rPush('key1', 'B');
-$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */
-$redis->lGet('key1', 0); /* 'A' */
-$redis->lSet('key1', 0, 'X');
-$redis->lGet('key1', 0); /* 'X' */ 
-
+~~~ +$redis->set('x', '42'); +$redis->setTimeout('x', 3); // x will disappear in 3 seconds. +sleep(5); // wait 5 seconds +$redis->get('x'); // will return `FALSE`, as 'x' has expired. +~~~ + +### expireAt, pexpireAt +----- +_**Description**_: Sets an expiration date (a timestamp) on an item. pexpireAt requires a timestamp in milliseconds. -## lRange, lGetRange -##### *Description* -Returns the specified elements of the list stored at the specified key in the range [start, end]. start and stop are interpretated as indices: -0 the first element, 1 the second ... --1 the last element, -2 the penultimate ... ##### *Parameters* -*key* -*start* -*end* +*Key*: key. The key that will disappear. + +*Integer*: Unix timestamp. The key's date of death, in seconds from Epoch time. ##### *Return value* -*Array* containing the values in specified range. +*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -
-$redis->rPush('key1', 'A');
-$redis->rPush('key1', 'B');
-$redis->rPush('key1', 'C');
-$redis->lRange('key1', 0, -1); /* array('A', 'B', 'C') */
-
+~~~ +$redis->set('x', '42'); +$now = time(NULL); // current timestamp +$redis->expireAt('x', $now + 3); // x will disappear in 3 seconds. +sleep(5); // wait 5 seconds +$redis->get('x'); // will return `FALSE`, as 'x' has expired. +~~~ + +### keys, getKeys +----- +_**Description**_: Returns the keys that match a certain pattern. -## lTrim, listTrim -##### *Description* -Trims an existing list so that it will contain only a specified range of elements. ##### *Parameters* -*key* -*start* -*stop* +*STRING*: pattern, using '*' as a wildcard. + ##### *Return value* -*Array* -*Bool* return `FALSE` if the key identify a non-list value. +*Array of STRING*: The keys that match a certain pattern. + ##### *Example* -
-$redis->rPush('key1', 'A');
-$redis->rPush('key1', 'B');
-$redis->rPush('key1', 'C');
-$redis->lRange('key1', 0, -1); /* array('A', 'B', 'C') */
-$redis->lTrim('key1', 0, 1);
-$redis->lRange('key1', 0, -1); /* array('A', 'B') */
-
+~~~ +$allKeys = $redis->keys('*'); // all keys will match this. +$keyWithUserPrefix = $redis->keys('user*'); +~~~ -## lRem, lRemove -##### *Description* -Removes the first `count` occurences of the value element from the list. If count is zero, all the matching elements are removed. If count is negative, elements are removed from tail to head. -**Note**: The argument order is not the same as in the Redis documentation. This difference is kept for compatibility reasons. + +### object +----- +_**Description**_: Describes the object pointed to by a key. ##### *Parameters* -*key* -*value* -*count* +The information to retrieve (string) and the key (string). Info can be one of the following: + +* "encoding" +* "refcount" +* "idletime" ##### *Return value* -*LONG* the number of elements to remove -*BOOL* `FALSE` if the value identified by key is not a list. +*STRING* for "encoding", *LONG* for "refcount" and "idletime", `FALSE` if the key doesn't exist. + ##### *Example* -
-$redis->lPush('key1', 'A');
-$redis->lPush('key1', 'B');
-$redis->lPush('key1', 'C'); 
-$redis->lPush('key1', 'A'); 
-$redis->lPush('key1', 'A'); 
+~~~
+$redis->object("encoding", "l"); // → ziplist
+$redis->object("refcount", "l"); // → 1
+$redis->object("idletime", "l"); // → 400 (in seconds, with a precision of 10 seconds).
+~~~
 
-$redis->lRange('key1', 0, -1); /* array('A', 'A', 'C', 'B', 'A') */
-$redis->lRem('key1', 'A', 2); /* 2 */
-$redis->lRange('key1', 0, -1); /* array('C', 'B', 'A') */
-
+### type +----- +_**Description**_: Returns the type of data pointed by a given key. -## lInsert -##### *Description* -Insert value in the list before or after the pivot value. the parameter options specify the position of the insert (before or after). -If the list didn't exists, or the pivot didn't exists, the value is not inserted. ##### *Parameters* -*key* -*position* Redis::BEFORE | Redis::AFTER -*pivot* -*value* +*Key*: key ##### *Return value* -The number of the elements in the list, -1 if the pivot didn't exists. + +Depending on the type of the data pointed by the key, this method will return the following value: +string: Redis::REDIS_STRING +set: Redis::REDIS_SET +list: Redis::REDIS_LIST +zset: Redis::REDIS_ZSET +hash: Redis::REDIS_HASH +other: Redis::REDIS_NOT_FOUND ##### *Example* -
-$redis->delete('key1');
-$redis->lInsert('key1', Redis::AFTER, 'A', 'X'); /* 0 */
+~~~
+$redis->type('key');
+~~~
 
-$redis->lPush('key1', 'A');
-$redis->lPush('key1', 'B');
-$redis->lPush('key1', 'C');
+### append
+-----
+_**Description**_: Append specified string to the string stored in specified key.
 
-$redis->lInsert('key1', Redis::BEFORE, 'C', 'X'); /* 4 */
-$redis->lRange('key1', 0, -1); /* array('A', 'B', 'X', 'C') */
+##### *Parameters*
+*Key*
+*Value*
 
-$redis->lInsert('key1', Redis::AFTER, 'C', 'Y'); /* 5 */
-$redis->lRange('key1', 0, -1); /* array('A', 'B', 'X', 'C', 'Y') */
+##### *Return value*
+*INTEGER*: Size of the value after the append
 
-$redis->lInsert('key1', Redis::AFTER, 'W', 'value'); /* -1 */
+##### *Example*
+~~~
+$redis->set('key', 'value1');
+$redis->append('key', 'value2'); /* 12 */
+$redis->get('key'); /* 'value1value2' */
+~~~
+
+### getRange
+-----
+_**Description**_: Return a substring of a larger string 
 
-
+*Note*: substr also supported but deprecated in redis. -## sAdd -##### *Description* -Adds a value to the set value stored at key. If this value is already in the set, `FALSE` is returned. ##### *Parameters* *key* -*value* +*start* +*end* ##### *Return value* -*LONG* the number of elements added to the set. -##### *Example* -
-$redis->sAdd('key1' , 'member1'); /* 1, 'key1' => {'member1'} */
-$redis->sAdd('key1' , 'member2', 'member3'); /* 2, 'key1' => {'member1', 'member2', 'member3'}*/
-$redis->sAdd('key1' , 'member2'); /* 0, 'key1' => {'member1', 'member2', 'member3'}*/
-
+*STRING*: the substring -## sRem, sRemove -##### *Description* -Removes the specified member from the set value stored at key. -##### *Parameters* -*key* -*member* -##### *Return value* -*LONG* The number of elements removed from the set. ##### *Example* -
-$redis->sAdd('key1' , 'member1');
-$redis->sAdd('key1' , 'member2');
-$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/
-$redis->sRem('key1', 'member2', 'member3'); /*return 2. 'key1' => {'member1'} */
-
+~~~ +$redis->set('key', 'string value'); +$redis->getRange('key', 0, 5); /* 'string' */ +$redis->getRange('key', -5, -1); /* 'value' */ +~~~ + +### setRange +----- +_**Description**_: Changes a substring of a larger string. -## sMove -##### *Description* -Moves the specified member from the set at srcKey to the set at dstKey. ##### *Parameters* -*srcKey* -*dstKey* -*member* +*key* +*offset* +*value* + ##### *Return value* -*BOOL* If the operation is successful, return `TRUE`. If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, `FALSE` is returned. +*STRING*: the length of the string after it was modified. + ##### *Example* -
-$redis->sAdd('key1' , 'member11');
-$redis->sAdd('key1' , 'member12');
-$redis->sAdd('key1' , 'member13'); /* 'key1' => {'member11', 'member12', 'member13'}*/
-$redis->sAdd('key2' , 'member21');
-$redis->sAdd('key2' , 'member22'); /* 'key2' => {'member21', 'member22'}*/
-$redis->sMove('key1', 'key2', 'member13'); /* 'key1' =>  {'member11', 'member12'} */
-					/* 'key2' =>  {'member21', 'member22', 'member13'} */
+~~~
+$redis->set('key', 'Hello world');
+$redis->setRange('key', 6, "redis"); /* returns 11 */
+$redis->get('key'); /* "Hello redis" */
+~~~
 
-
+### strlen +----- +_**Description**_: Get the length of a string value. -## sIsMember, sContains -##### *Description* -Checks if `value` is a member of the set stored at the key `key`. ##### *Parameters* *key* -*value* ##### *Return value* -*BOOL* `TRUE` if `value` is a member of the set at key `key`, `FALSE` otherwise. -##### *Example* -
-$redis->sAdd('key1' , 'member1');
-$redis->sAdd('key1' , 'member2');
-$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/
+*INTEGER*
 
-$redis->sIsMember('key1', 'member1'); /* TRUE */
-$redis->sIsMember('key1', 'memberX'); /* FALSE */
+##### *Example*
+~~~
+$redis->set('key', 'value');
+$redis->strlen('key'); /* 5 */
+~~~
 
-
+### getBit +----- +_**Description**_: Return a single bit out of a larger string -## sCard, sSize -##### *Description* -Returns the cardinality of the set identified by key. ##### *Parameters* -*key* +*key* +*offset* + ##### *Return value* -*LONG* the cardinality of the set identified by key, 0 if the set doesn't exist. +*LONG*: the bit value (0 or 1) + ##### *Example* -
-$redis->sAdd('key1' , 'member1');
-$redis->sAdd('key1' , 'member2');
-$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/
-$redis->sCard('key1'); /* 3 */
-$redis->sCard('keyX'); /* 0 */
-
+~~~ +$redis->set('key', "\x7f"); // this is 0111 1111 +$redis->getBit('key', 0); /* 0 */ +$redis->getBit('key', 1); /* 1 */ +~~~ + +### setBit +----- +_**Description**_: Changes a single bit of a string. -## sPop -##### *Description* -Removes and returns a random element from the set value at Key. ##### *Parameters* -*key* +*key* +*offset* +*value*: bool or int (1 or 0) + ##### *Return value* -*String* "popped" value -*Bool* `FALSE` if set identified by key is empty or doesn't exist. -##### *Example* -
-$redis->sAdd('key1' , 'member1');
-$redis->sAdd('key1' , 'member2');
-$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/
-$redis->sPop('key1'); /* 'member1', 'key1' => {'member3', 'member2'} */
-$redis->sPop('key1'); /* 'member3', 'key1' => {'member2'} */
-
+*LONG*: 0 or 1, the value of the bit before it was set. -## sRandMember -##### *Description* -Returns a random element or random elements from the set value at Key, without removing it/them. -##### *Parameters* -*key* -*count*: int, How many elements to return - optional. If the count is negative it will always return the # requested even if this requires duplicates to be returned -##### *Return value (No count passed)* -*String* value from the set -*Bool* `FALSE` if set identified by key is empty or doesn't exist. -##### *Return value (with count passed)* -*Array* Random value(s) from the set, or an empty array if the set is empty or doesn't exist ##### *Example* -
-$redis->sAdd('key1' , 'member1');
-$redis->sAdd('key1' , 'member2');
-$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/
-
-// No count
-$redis->sRandMember('key1'); /* 'member1', 'key1' => {'member3', 'member1', 'member2'} */
-$redis->sRandMember('key1'); /* 'member3', 'key1' => {'member3', 'member1', 'member2'} */
+~~~
+$redis->set('key', "*");	// ord("*") = 42 = 0x2f = "0010 1010"
+$redis->setBit('key', 5, 1); /* returns 0 */
+$redis->setBit('key', 7, 1); /* returns 0 */
+$redis->get('key'); /* chr(0x2f) = "/" = b("0010 1111") */
+~~~
 
-// With a count
-$redis->sRandMember('key1', 3); // Will return an array with all members from the set
-$redis->sRandMember('key1', 2); // Will an array with 2 members of the set
-$redis->sRandMember('key1', -100); // Will return an array of 100 elements, picked from our set (with dups)
-$redis->sRandMember('empty_set', 100); // Will return an empty array
-$redis->sRandMember('not_a_set', 100); // Will return FALSE
-
+### bitop +----- +_**Description**_: Bitwise operation on multiple keys. -## sInter +##### *Parameters* +*operation*: either "AND", "OR", "NOT", "XOR" +*ret_key*: return key +*key1* +*key2...* -##### *Description* +##### *Return value* +*LONG*: The size of the string stored in the destination key. -Returns the members of a set resulting from the intersection of all the sets held at the specified keys. -If just a single key is specified, then this command produces the members of this set. If one of the keys -is missing, `FALSE` is returned. +### bitcount +----- +_**Description**_: Count bits in a string. ##### *Parameters* +*key* -key1, key2, keyN: keys identifying the different sets on which we will apply the intersection. - ##### *Return value* +*LONG*: The number of bits set to 1 in the value behind the input key. -Array, contain the result of the intersection between those keys. If the intersection beteen the different sets is empty, the return value will be empty array. +### sort +----- +_**Description**_: Sort the elements in a list, set or sorted set. -##### *Examples* +##### *Parameters* +*Key*: key +*Options*: array(key => value, ...) - optional, with the following keys and values: +~~~ + 'by' => 'some_pattern_*', + 'limit' => array(0, 1), + 'get' => 'some_other_pattern_*' or an array of patterns, + 'sort' => 'asc' or 'desc', + 'alpha' => TRUE, + 'store' => 'external-key' +~~~ +##### *Return value* +An array of values, or a number corresponding to the number of elements stored if that was used. -
-$redis->sAdd('key1', 'val1');
-$redis->sAdd('key1', 'val2');
-$redis->sAdd('key1', 'val3');
-$redis->sAdd('key1', 'val4');
+##### *Example*
+~~~
+$redis->delete('s');
+$redis->sadd('s', 5);
+$redis->sadd('s', 4);
+$redis->sadd('s', 2);
+$redis->sadd('s', 1);
+$redis->sadd('s', 3);
 
-$redis->sAdd('key2', 'val3');
-$redis->sAdd('key2', 'val4');
+var_dump($redis->sort('s')); // 1,2,3,4,5
+var_dump($redis->sort('s', array('sort' => 'desc'))); // 5,4,3,2,1
+var_dump($redis->sort('s', array('sort' => 'desc', 'store' => 'out'))); // (int)5
+~~~
 
-$redis->sAdd('key3', 'val3');
-$redis->sAdd('key3', 'val4');
 
-var_dump($redis->sInter('key1', 'key2', 'key3'));
-
-Output: -
-array(2) {
-  [0]=>
-  string(4) "val4"
-  [1]=>
-  string(4) "val3"
-}
-
+### ttl, pttl +----- +_**Description**_: Returns the time to live left for a given key, in seconds. If the key doesn't exist, `FALSE` is returned. pttl returns a time in milliseconds. -## sInterStore -##### *Description* -Performs a sInter command and stores the result in a new set. ##### *Parameters* -*Key*: dstkey, the key to store the diff into. - -*Keys*: key1, key2... keyN. key1..keyN are intersected as in sInter. +*Key*: key ##### *Return value* -*INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. +Long, the time left to live in seconds. ##### *Example* -
-$redis->sAdd('key1', 'val1');
-$redis->sAdd('key1', 'val2');
-$redis->sAdd('key1', 'val3');
-$redis->sAdd('key1', 'val4');
-
-$redis->sAdd('key2', 'val3');
-$redis->sAdd('key2', 'val4');
-
-$redis->sAdd('key3', 'val3');
-$redis->sAdd('key3', 'val4');
+~~~
+$redis->ttl('key');
+~~~
 
-var_dump($redis->sInterStore('output', 'key1', 'key2', 'key3'));
-var_dump($redis->sMembers('output'));
-
+### persist +----- +_**Description**_: Remove the expiration timer from a key. -Output: +##### *Parameters* +*Key*: key -
-int(2)
+##### *Return value*
+*BOOL*: `TRUE` if a timeout was removed, `FALSE` if the key didn’t exist or didn’t have an expiration timer.
 
-array(2) {
-  [0]=>
-  string(4) "val4"
-  [1]=>
-  string(4) "val3"
-}
-
+##### *Example* +~~~ +$redis->persist('key'); +~~~ -## sUnion -##### *Description* -Performs the union between N sets and returns it. +### mset, msetnx +----- +_**Description**_: Sets multiple key-value pairs in one atomic command. MSETNX only returns TRUE if all the keys were set (see SETNX). ##### *Parameters* -*Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. +*Pairs*: array(key => value, ...) ##### *Return value* -*Array of strings*: The union of all these sets. +*Bool* `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -
-$redis->delete('s0', 's1', 's2');
+~~~
 
-$redis->sAdd('s0', '1');
-$redis->sAdd('s0', '2');
-$redis->sAdd('s1', '3');
-$redis->sAdd('s1', '1');
-$redis->sAdd('s2', '3');
-$redis->sAdd('s2', '4');
+$redis->mset(array('key0' => 'value0', 'key1' => 'value1'));
+var_dump($redis->get('key0'));
+var_dump($redis->get('key1'));
+
+~~~
+Output:
+~~~
+string(6) "value0"
+string(6) "value1"
+~~~
 
-var_dump($redis->sUnion('s0', 's1', 's2'));
-
-Return value: all elements that are either in s0 or in s1 or in s2. -
-array(4) {
-  [0]=>
-  string(1) "3"
-  [1]=>
-  string(1) "4"
-  [2]=>
-  string(1) "1"
-  [3]=>
-  string(1) "2"
-}
-
-## sUnionStore -##### *Description* -Performs the same action as sUnion, but stores the result in the first key +### dump +----- +_**Description**_: Dump a key out of a redis database, the value of which can later be passed into redis using the RESTORE command. The data +that comes out of DUMP is a binary representation of the key as Redis stores it. ##### *Parameters* -*Key*: dstkey, the key to store the diff into. +*key* string +##### *Return value* +The Redis encoded value of the key, or FALSE if the key doesn't exist +##### *Examples* +~~~ +$redis->set('foo', 'bar'); +$val = $redis->dump('foo'); // $val will be the Redis encoded key value +~~~ -*Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. +### restore +----- +_**Description**_: Restore a key from the result of a DUMP operation. +##### *Parameters* +*key* string. The key name +*ttl* integer. How long the key should live (if zero, no expire will be set on the key) +*value* string (binary). The Redis encoded key value (from DUMP) +##### *Examples* +~~~ +$redis->set('foo', 'bar'); +$val = $redis->dump('foo'); +$redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key 'foo' +~~~ -##### *Return value* -*INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. +### migrate +----- +_**Description**_: Migrates a key to a different Redis instance. +##### *Parameters* +*host* string. The destination host +*port* integer. The TCP port to connect to. +*key* string. The key to migrate. +*destination-db* integer. The target DB. +*timeout* integer. The maximum amount of time given to this transfer. +##### *Examples* +~~~ +$redis->migrate('backup', 6379, 'foo', 0, 3600); +~~~ -##### *Example* -
-$redis->delete('s0', 's1', 's2');
 
-$redis->sAdd('s0', '1');
-$redis->sAdd('s0', '2');
-$redis->sAdd('s1', '3');
-$redis->sAdd('s1', '1');
-$redis->sAdd('s2', '3');
-$redis->sAdd('s2', '4');
 
-var_dump($redis->sUnionStore('dst', 's0', 's1', 's2'));
-var_dump($redis->sMembers('dst'));
-
-Return value: the number of elements that are either in s0 or in s1 or in s2. -
-int(4)
-array(4) {
-  [0]=>
-  string(1) "3"
-  [1]=>
-  string(1) "4"
-  [2]=>
-  string(1) "1"
-  [3]=>
-  string(1) "2"
-}
-
+## Hashes -## sDiff -##### *Description* -Performs the difference between N sets and returns it. +* [hDel](#hdel) - Delete one or more hash fields +* [hExists](#hexists) - Determine if a hash field exists +* [hGet](#hget) - Get the value of a hash field +* [hGetAll](#hgetall) - Get all the fields and values in a hash +* [hIncrBy](#hincrby) - Increment the integer value of a hash field by the given number +* [hIncrByFloat](#hincrbyfloat) - Increment the float value of a hash field by the given amount +* [hKeys](#hkeys) - Get all the fields in a hash +* [hLen](#hlen) - Get the number of fields in a hash +* [hMGet](#hmget) - Get the values of all the given hash fields +* [hMSet](#hmset) - Set multiple hash fields to multiple values +* [hSet](#hset) - Set the string value of a hash field +* [hSetNx](#hsetnx) - Set the value of a hash field, only if the field does not exist +* [hVals](#hvals) - Get all the values in a hash +### hSet +----- +_**Description**_: Adds a value to the hash stored at key. If this value is already in the hash, `FALSE` is returned. ##### *Parameters* -*Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. +*key* +*hashKey* +*value* ##### *Return value* -*Array of strings*: The difference of the first set will all the others. - +*LONG* `1` if value didn't exist and was added successfully, `0` if the value was already present and was replaced, `FALSE` if there was an error. ##### *Example* -
-$redis->delete('s0', 's1', 's2');
+~~~
+$redis->delete('h')
+$redis->hSet('h', 'key1', 'hello'); /* 1, 'key1' => 'hello' in the hash at "h" */
+$redis->hGet('h', 'key1'); /* returns "hello" */
 
-$redis->sAdd('s0', '1');
-$redis->sAdd('s0', '2');
-$redis->sAdd('s0', '3');
-$redis->sAdd('s0', '4');
+$redis->hSet('h', 'key1', 'plop'); /* 0, value was replaced. */
+$redis->hGet('h', 'key1'); /* returns "plop" */
+~~~
 
-$redis->sAdd('s1', '1');
-$redis->sAdd('s2', '3');
+### hSetNx
+-----
+_**Description**_: Adds a value to the hash stored at key only if this field isn't already in the hash.
 
-var_dump($redis->sDiff('s0', 's1', 's2'));
-
-Return value: all elements of s0 that are neither in s1 nor in s2. -
-array(2) {
-  [0]=>
-  string(1) "4"
-  [1]=>
-  string(1) "2"
-}
-
+##### *Return value* +*BOOL* `TRUE` if the field was set, `FALSE` if it was already present. + +##### *Example* +~~~ +$redis->delete('h') +$redis->hSetNx('h', 'key1', 'hello'); /* TRUE, 'key1' => 'hello' in the hash at "h" */ +$redis->hSetNx('h', 'key1', 'world'); /* FALSE, 'key1' => 'hello' in the hash at "h". No change since the field wasn't replaced. */ +~~~ -## sDiffStore -##### *Description* -Performs the same action as sDiff, but stores the result in the first key + +### hGet +----- +_**Description**_: Gets a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* -*Key*: dstkey, the key to store the diff into. +*key* +*hashKey* -*Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis ##### *Return value* -*INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. +*STRING* The value, if the command executed successfully +*BOOL* `FALSE` in case of failure + + +### hLen +----- +_**Description**_: Returns the length of a hash, in number of items +##### *Parameters* +*key* +##### *Return value* +*LONG* the number of items in a hash, `FALSE` if the key doesn't exist or isn't a hash. ##### *Example* -
-$redis->delete('s0', 's1', 's2');
+~~~
+$redis->delete('h')
+$redis->hSet('h', 'key1', 'hello');
+$redis->hSet('h', 'key2', 'plop');
+$redis->hLen('h'); /* returns 2 */
+~~~
 
-$redis->sAdd('s0', '1');
-$redis->sAdd('s0', '2');
-$redis->sAdd('s0', '3');
-$redis->sAdd('s0', '4');
+### hDel
+-----
+_**Description**_: Removes a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned.  
+##### *Parameters*
+*key*  
+*hashKey*  
 
-$redis->sAdd('s1', '1');
-$redis->sAdd('s2', '3');
+##### *Return value*
+*BOOL* `TRUE` in case of success, `FALSE` in case of failure
 
-var_dump($redis->sDiffStore('dst', 's0', 's1', 's2'));
-var_dump($redis->sMembers('dst'));
-
-Return value: the number of elements of s0 that are neither in s1 nor in s2. -
-int(2)
-array(2) {
+
+### hKeys
+-----
+_**Description**_: Returns the keys in a hash, as an array of strings.
+
+##### *Parameters*
+*Key*: key
+
+##### *Return value*
+An array of elements, the keys of the hash. This works like PHP's array_keys().
+
+##### *Example*
+~~~
+$redis->delete('h');
+$redis->hSet('h', 'a', 'x');
+$redis->hSet('h', 'b', 'y');
+$redis->hSet('h', 'c', 'z');
+$redis->hSet('h', 'd', 't');
+var_dump($redis->hKeys('h'));
+~~~
+
+Output:
+~~~
+array(4) {
   [0]=>
-  string(1) "4"
+  string(1) "a"
   [1]=>
-  string(1) "2"
+  string(1) "b"
+  [2]=>
+  string(1) "c"
+  [3]=>
+  string(1) "d"
 }
-
+~~~ +The order is random and corresponds to redis' own internal representation of the set structure. -## sMembers, sGetMembers -##### *Description* -Returns the contents of a set. +### hVals +----- +_**Description**_: Returns the values in a hash, as an array of strings. ##### *Parameters* *Key*: key ##### *Return value* -An array of elements, the contents of the set. +An array of elements, the values of the hash. This works like PHP's array_values(). ##### *Example* -
-$redis->delete('s');
-$redis->sAdd('s', 'a');
-$redis->sAdd('s', 'b');
-$redis->sAdd('s', 'a');
-$redis->sAdd('s', 'c');
-var_dump($redis->sMembers('s'));
-
+~~~ +$redis->delete('h'); +$redis->hSet('h', 'a', 'x'); +$redis->hSet('h', 'b', 'y'); +$redis->hSet('h', 'c', 'z'); +$redis->hSet('h', 'd', 't'); +var_dump($redis->hVals('h')); +~~~ Output: -
-array(3) {
+~~~
+array(4) {
   [0]=>
-  string(1) "c"
+  string(1) "x"
   [1]=>
-  string(1) "a"
+  string(1) "y"
   [2]=>
-  string(1) "b"
+  string(1) "z"
+  [3]=>
+  string(1) "t"
 }
-
+~~~ The order is random and corresponds to redis' own internal representation of the set structure. -## getSet -##### *Description* -Sets a value and returns the previous entry at that key. +### hGetAll +----- +_**Description**_: Returns the whole hash, as an array of strings indexed by strings. + ##### *Parameters* *Key*: key -*STRING*: value - ##### *Return value* -A string, the previous value located at this key. +An array of elements, the contents of the hash. + ##### *Example* -
-$redis->set('x', '42');
-$exValue = $redis->getSet('x', 'lol');	// return '42', replaces x by 'lol'
-$newValue = $redis->get('x')'		// return 'lol'
-
+~~~ +$redis->delete('h'); +$redis->hSet('h', 'a', 'x'); +$redis->hSet('h', 'b', 'y'); +$redis->hSet('h', 'c', 'z'); +$redis->hSet('h', 'd', 't'); +var_dump($redis->hGetAll('h')); +~~~ -## randomKey -##### *Description* -Returns a random key. +Output: +~~~ +array(4) { + ["a"]=> + string(1) "x" + ["b"]=> + string(1) "y" + ["c"]=> + string(1) "z" + ["d"]=> + string(1) "t" +} +~~~ +The order is random and corresponds to redis' own internal representation of the set structure. +### hExists +----- +_**Description**_: Verify if the specified member exists in a key. ##### *Parameters* -None. +*key* +*memberKey* ##### *Return value* -*STRING*: an existing key in redis. +*BOOL*: If the member exists in the hash table, return `TRUE`, otherwise return `FALSE`. +##### *Examples* +~~~ +$redis->hSet('h', 'a', 'x'); +$redis->hExists('h', 'a'); /* TRUE */ +$redis->hExists('h', 'NonExistingKey'); /* FALSE */ +~~~ -##### *Example* -
-$key = $redis->randomKey();
-$surprise = $redis->get($key);	// who knows what's in there.
-
+### hIncrBy +----- +_**Description**_: Increments the value of a member from a hash by a given amount. +##### *Parameters* +*key* +*member* +*value*: (integer) value that will be added to the member's value +##### *Return value* +*LONG* the new value +##### *Examples* +~~~ +$redis->delete('h'); +$redis->hIncrBy('h', 'x', 2); /* returns 2: h[x] = 2 now. */ +$redis->hIncrBy('h', 'x', 1); /* h[x] ← 2 + 1. Returns 3 */ +~~~ -## select -##### *Description* -Switches to a given database. +### hIncrByFloat +----- +_**Description**_: Increments the value of a hash member by the provided float value +##### *Parameters* +*key* +*member* +*value*: (float) value that will be added to the member's value +##### *Return value* +*FLOAT* the new value +##### *Examples* +~~~ +$redis->delete('h'); +$redis->hIncrByFloat('h','x', 1.5); /* returns 1.5: h[x] = 1.5 now */ +$redis->hIncrByFLoat('h', 'x', 1.5); /* returns 3.0: h[x] = 3.0 now */ +$redis->hIncrByFloat('h', 'x', -3.0); /* returns 0.0: h[x] = 0.0 now */ +~~~ +### hMSet +----- +_**Description**_: Fills in a whole hash. Non-string values are converted to string, using the standard `(string)` cast. NULL values are stored as empty strings. ##### *Parameters* -*INTEGER*: dbindex, the database number to switch to. +*key* +*members*: key → value array +##### *Return value* +*BOOL* +##### *Examples* +~~~ +$redis->delete('user:1'); +$redis->hMset('user:1', array('name' => 'Joe', 'salary' => 2000)); +$redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. +~~~ +### hMGet +----- +_**Description**_: Retrieve the values associated to the specified fields in the hash. +##### *Parameters* +*key* +*memberKeys* Array ##### *Return value* -`TRUE` in case of success, `FALSE` in case of failure. +*Array* An array of elements, the values of the specified fields in the hash, with the hash keys as array keys. +##### *Examples* +~~~ +$redis->delete('h'); +$redis->hSet('h', 'field1', 'value1'); +$redis->hSet('h', 'field2', 'value2'); +$redis->hmGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */ +~~~ + + + +## Lists + +* [blPop, brPop](#blpop-brpop) - Remove and get the first/last element in a list +* [brpoplpush](#brpoplpush) - Pop a value from a list, push it to another list and return it +* [lIndex, lGet](#lindex-lget) - Get an element from a list by its index +* [lInsert](#linsert) - Insert an element before or after another element in a list +* [lLen, lSize](#llen-lsize) - Get the length/size of a list +* [lPop](#lpop) - Remove and get the first element in a list +* [lPush](#lpush) - Prepend one or multiple values to a list +* [lPushx](#lpushx) - Prepend a value to a list, only if the list exists +* [lRange, lGetRange](#lrange-lgetrange) - Get a range of elements from a list +* [lRem, lRemove](#lrem-lremove) - Remove elements from a list +* [lSet](#lset) - Set the value of an element in a list by its index +* [lTrim, listTrim](#ltrim-listtrim) - Trim a list to the specified range +* [rPop](#rpop) - Remove and get the last element in a list +* [rpoplpush](#rpoplpush) - Remove the last element in a list, append it to another list and return it (redis >= 1.1) +* [rPush](#rpush) - Append one or multiple values to a list +* [rPushx](#rpushx) - Append a value to a list, only if the list exists + +### blPop, brPop +----- +_**Description**_: Is a blocking lPop(rPop) primitive. If at least one of the lists contains at least one element, the element will be popped from the head of the list and returned to the caller. +Il all the list identified by the keys passed in arguments are empty, blPop will block during the specified timeout until an element is pushed to one of those lists. This element will be popped. + +##### *Parameters* +*ARRAY* Array containing the keys of the lists +*INTEGER* Timeout +Or +*STRING* Key1 +*STRING* Key2 +*STRING* Key3 +... +*STRING* Keyn +*INTEGER* Timeout + +##### *Return value* +*ARRAY* array('listName', 'element') + ##### *Example* -(See following function) +~~~ +/* Non blocking feature */ +$redis->lPush('key1', 'A'); +$redis->delete('key2'); + +$redis->blPop('key1', 'key2', 10); /* array('key1', 'A') */ +/* OR */ +$redis->blPop(array('key1', 'key2'), 10); /* array('key1', 'A') */ + +$redis->brPop('key1', 'key2', 10); /* array('key1', 'A') */ +/* OR */ +$redis->brPop(array('key1', 'key2'), 10); /* array('key1', 'A') */ -## move -##### *Description* -Moves a key to a different database. +/* Blocking feature */ + +/* process 1 */ +$redis->delete('key1'); +$redis->blPop('key1', 10); +/* blocking for 10 seconds */ + +/* process 2 */ +$redis->lPush('key1', 'A'); + +/* process 1 */ +/* array('key1', 'A') is returned*/ +~~~ + +### brpoplpush +----- +_**Description**_: A blocking version of `rpoplpush`, with an integral timeout in the third parameter. ##### *Parameters* -*Key*: key, the key to move. +*Key*: srckey +*Key*: dstkey +*Long*: timeout -*INTEGER*: dbindex, the database number to move the key to. +##### *Return value* +*STRING* The element that was moved in case of success, `FALSE` in case of timeout. + +### lIndex, lGet +----- +_**Description**_: Return the specified element of the list stored at the specified key. + +0 the first element, 1 the second ... +-1 the last element, -2 the penultimate ... + +Return `FALSE` in case of a bad index or a key that doesn't point to a list. + +##### *Parameters* +*key* +*index* ##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. +*String* the element at this index +*Bool* `FALSE` if the key identifies a non-string data type, or no value corresponds to this index in the list `Key`. + ##### *Example* +~~~ +$redis->rPush('key1', 'A'); +$redis->rPush('key1', 'B'); +$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ +$redis->lGet('key1', 0); /* 'A' */ +$redis->lGet('key1', -1); /* 'C' */ +$redis->lGet('key1', 10); /* `FALSE` */ +~~~ -
-$redis->select(0);	// switch to DB 0
-$redis->set('x', '42');	// write 42 to x
-$redis->move('x', 1);	// move to DB 1
-$redis->select(1);	// switch to DB 1
-$redis->get('x');	// will return 42
-
+### lInsert +----- +_**Description**_: Insert value in the list before or after the pivot value. -## rename, renameKey -##### *Description* -Renames a key. -##### *Parameters* -*STRING*: srckey, the key to rename. +The parameter options specify the position of the insert (before or after). +If the list didn't exists, or the pivot didn't exists, the value is not inserted. -*STRING*: dstkey, the new name for the key. +##### *Parameters* +*key* +*position* Redis::BEFORE | Redis::AFTER +*pivot* +*value* ##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. +The number of the elements in the list, -1 if the pivot didn't exists. + ##### *Example* -
-$redis->set('x', '42');
-$redis->rename('x', 'y');
-$redis->get('y'); 	// → 42
-$redis->get('x'); 	// → `FALSE`
-
+~~~ +$redis->delete('key1'); +$redis->lInsert('key1', Redis::AFTER, 'A', 'X'); /* 0 */ -## renameNx -##### *Description* -Same as rename, but will not replace a key if the destination already exists. This is the same behaviour as setNx. +$redis->lPush('key1', 'A'); +$redis->lPush('key1', 'B'); +$redis->lPush('key1', 'C'); -## setTimeout, expire, pexpire -##### *Description* -Sets an expiration date (a timeout) on an item. pexpire requires a TTL in milliseconds. +$redis->lInsert('key1', Redis::BEFORE, 'C', 'X'); /* 4 */ +$redis->lRange('key1', 0, -1); /* array('A', 'B', 'X', 'C') */ -##### *Parameters* -*Key*: key. The key that will disappear. +$redis->lInsert('key1', Redis::AFTER, 'C', 'Y'); /* 5 */ +$redis->lRange('key1', 0, -1); /* array('A', 'B', 'X', 'C', 'Y') */ -*Integer*: ttl. The key's remaining Time To Live, in seconds. +$redis->lInsert('key1', Redis::AFTER, 'W', 'value'); /* -1 */ +~~~ + +### lPop +----- +_**Description**_: Return and remove the first element of the list. + +##### *Parameters* +*key* ##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. +*STRING* if command executed successfully +*BOOL* `FALSE` in case of failure (empty list) + ##### *Example* -
-$redis->set('x', '42');
-$redis->setTimeout('x', 3);	// x will disappear in 3 seconds.
-sleep(5);				// wait 5 seconds
-$redis->get('x'); 		// will return `FALSE`, as 'x' has expired.
-
+~~~ +$redis->rPush('key1', 'A'); +$redis->rPush('key1', 'B'); +$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ +$redis->lPop('key1'); /* key1 => [ 'B', 'C' ] */ +~~~ -## expireAt, pexpireAt -##### *Description* -Sets an expiration date (a timestamp) on an item. pexpireAt requires a timestamp in milliseconds. +### lPush +----- +_**Description**_: Adds the string value to the head (left) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. ##### *Parameters* -*Key*: key. The key that will disappear. - -*Integer*: Unix timestamp. The key's date of death, in seconds from Epoch time. +*key* +*value* String, value to push in key ##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. -##### *Example* -
-$redis->set('x', '42');
-$now = time(NULL); // current timestamp
-$redis->expireAt('x', $now + 3);	// x will disappear in 3 seconds.
-sleep(5);				// wait 5 seconds
-$redis->get('x'); 		// will return `FALSE`, as 'x' has expired.
-
+*LONG* The new length of the list in case of success, `FALSE` in case of Failure. -## keys, getKeys -##### *Description* -Returns the keys that match a certain pattern. -##### *Description* +##### *Examples* +~~~ +$redis->delete('key1'); +$redis->lPush('key1', 'C'); // returns 1 +$redis->lPush('key1', 'B'); // returns 2 +$redis->lPush('key1', 'A'); // returns 3 +/* key1 now points to the following list: [ 'A', 'B', 'C' ] */ +~~~ + +### lPushx +----- +_**Description**_: Adds the string value to the head (left) of the list if the list exists. ##### *Parameters* -*STRING*: pattern, using '*' as a wildcard. +*key* +*value* String, value to push in key ##### *Return value* -*Array of STRING*: The keys that match a certain pattern. +*LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### *Example* -
-$allKeys = $redis->keys('*');	// all keys will match this.
-$keyWithUserPrefix = $redis->keys('user*');
-
+##### *Examples* +~~~ +$redis->delete('key1'); +$redis->lPushx('key1', 'A'); // returns 0 +$redis->lPush('key1', 'A'); // returns 1 +$redis->lPushx('key1', 'B'); // returns 2 +$redis->lPushx('key1', 'C'); // returns 3 +/* key1 now points to the following list: [ 'A', 'B', 'C' ] */ +~~~ -## dbSize -##### *Description* -Returns the current database's size. +### lRange, lGetRange +----- +_**Description**_: Returns the specified elements of the list stored at the specified key in the range [start, end]. start and stop are interpretated as indices: +0 the first element, 1 the second ... +-1 the last element, -2 the penultimate ... ##### *Parameters* -None. +*key* +*start* +*end* ##### *Return value* -*INTEGER*: DB size, in number of keys. +*Array* containing the values in specified range. ##### *Example* -
-$count = $redis->dbSize();
-echo "Redis has $count keys\n";
-
+~~~ +$redis->rPush('key1', 'A'); +$redis->rPush('key1', 'B'); +$redis->rPush('key1', 'C'); +$redis->lRange('key1', 0, -1); /* array('A', 'B', 'C') */ +~~~ -## auth -##### *Description* -Authenticate the connection using a password. -*Warning*: The password is sent in plain-text over the network. +### lRem, lRemove +----- +_**Description**_: Removes the first `count` occurences of the value element from the list. If count is zero, all the matching elements are removed. If count is negative, elements are removed from tail to head. + +**Note**: The argument order is not the same as in the Redis documentation. This difference is kept for compatibility reasons. ##### *Parameters* -*STRING*: password +*key* +*value* +*count* ##### *Return value* -*BOOL*: `TRUE` if the connection is authenticated, `FALSE` otherwise. +*LONG* the number of elements to remove +*BOOL* `FALSE` if the value identified by key is not a list. ##### *Example* -
-$redis->auth('foobared');
-
+~~~ +$redis->lPush('key1', 'A'); +$redis->lPush('key1', 'B'); +$redis->lPush('key1', 'C'); +$redis->lPush('key1', 'A'); +$redis->lPush('key1', 'A'); -## bgrewriteaof -##### *Description* -Starts the background rewrite of AOF (Append-Only File) +$redis->lRange('key1', 0, -1); /* array('A', 'A', 'C', 'B', 'A') */ +$redis->lRem('key1', 'A', 2); /* 2 */ +$redis->lRange('key1', 0, -1); /* array('C', 'B', 'A') */ +~~~ + +### lSet +----- +_**Description**_: Set the list at index with the new value. ##### *Parameters* -None. +*key* +*index* +*value* ##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. +*BOOL* `TRUE` if the new value is setted. `FALSE` if the index is out of range, or data type identified by key is not a list. ##### *Example* -
-$redis->bgrewriteaof();
-
+~~~ +$redis->rPush('key1', 'A'); +$redis->rPush('key1', 'B'); +$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ +$redis->lGet('key1', 0); /* 'A' */ +$redis->lSet('key1', 0, 'X'); +$redis->lGet('key1', 0); /* 'X' */ +~~~ -## slaveof -##### *Description* -Changes the slave status +### lTrim, listTrim +----- +_**Description**_: Trims an existing list so that it will contain only a specified range of elements. ##### *Parameters* -Either host (string) and port (int), or no parameter to stop being a slave. +*key* +*start* +*stop* ##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. +*Array* +*Bool* return `FALSE` if the key identify a non-list value. ##### *Example* -
-$redis->slaveof('10.0.1.7', 6379);
-/* ... */
-$redis->slaveof();
-
+~~~ +$redis->rPush('key1', 'A'); +$redis->rPush('key1', 'B'); +$redis->rPush('key1', 'C'); +$redis->lRange('key1', 0, -1); /* array('A', 'B', 'C') */ +$redis->lTrim('key1', 0, 1); +$redis->lRange('key1', 0, -1); /* array('A', 'B') */ +~~~ -## object -##### *Description* -Describes the object pointed to by a key. +### rPop +----- +_**Description**_: Returns and removes the last element of the list. ##### *Parameters* -The information to retrieve (string) and the key (string). Info can be one of the following: - -* "encoding" -* "refcount" -* "idletime" +*key* ##### *Return value* -*STRING* for "encoding", *LONG* for "refcount" and "idletime", `FALSE` if the key doesn't exist. +*STRING* if command executed successfully +*BOOL* `FALSE` in case of failure (empty list) ##### *Example* -
-$redis->object("encoding", "l"); // → ziplist
-$redis->object("refcount", "l"); // → 1
-$redis->object("idletime", "l"); // → 400 (in seconds, with a precision of 10 seconds).
-
+~~~ +$redis->rPush('key1', 'A'); +$redis->rPush('key1', 'B'); +$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ +$redis->rPop('key1'); /* key1 => [ 'A', 'B' ] */ +~~~ -## save -##### *Description* -Performs a synchronous save. +### rpoplpush +----- +_**Description**_: Pops a value from the tail of a list, and pushes it to the front of another list. Also return this value. (redis >= 1.1) ##### *Parameters* -None. +*Key*: srckey +*Key*: dstkey ##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. +*STRING* The element that was moved in case of success, `FALSE` in case of failure. ##### *Example* -
-$redis->save();
-
- -## bgsave - -##### *Description* -Performs a background save. - -##### *Parameters* -None. +~~~ +$redis->delete('x', 'y'); -##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. +$redis->lPush('x', 'abc'); +$redis->lPush('x', 'def'); +$redis->lPush('y', '123'); +$redis->lPush('y', '456'); -##### *Example* -
-$redis->bgSave();
-
+// move the last of x to the front of y. +var_dump($redis->rpoplpush('x', 'y')); +var_dump($redis->lRange('x', 0, -1)); +var_dump($redis->lRange('y', 0, -1)); -## lastSave +~~~ +Output: +~~~ +string(3) "abc" +array(1) { + [0]=> + string(3) "def" +} +array(3) { + [0]=> + string(3) "abc" + [1]=> + string(3) "456" + [2]=> + string(3) "123" +} +~~~ -##### *Description* -Returns the timestamp of the last disk save. +### rPush +----- +_**Description**_: Adds the string value to the tail (right) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. ##### *Parameters* -None. +*key* +*value* String, value to push in key ##### *Return value* -*INT*: timestamp. - -##### *Example* -
-$redis->lastSave();
-
+*LONG* The new length of the list in case of success, `FALSE` in case of Failure. -## type +##### *Examples* +~~~ +$redis->delete('key1'); +$redis->rPush('key1', 'A'); // returns 1 +$redis->rPush('key1', 'B'); // returns 2 +$redis->rPush('key1', 'C'); // returns 3 +/* key1 now points to the following list: [ 'A', 'B', 'C' ] */ +~~~ -##### *Description* -Returns the type of data pointed by a given key. +### rPushx +----- +_**Description**_: Adds the string value to the tail (right) of the list if the ist exists. `FALSE` in case of Failure. ##### *Parameters* -*Key*: key +*key* +*value* String, value to push in key ##### *Return value* +*LONG* The new length of the list in case of success, `FALSE` in case of Failure. -Depending on the type of the data pointed by the key, this method will return the following value: -string: Redis::REDIS_STRING -set: Redis::REDIS_SET -list: Redis::REDIS_LIST -zset: Redis::REDIS_ZSET -hash: Redis::REDIS_HASH -other: Redis::REDIS_NOT_FOUND +##### *Examples* +~~~ +$redis->delete('key1'); +$redis->rPushx('key1', 'A'); // returns 0 +$redis->rPush('key1', 'A'); // returns 1 +$redis->rPushx('key1', 'B'); // returns 2 +$redis->rPushx('key1', 'C'); // returns 3 +/* key1 now points to the following list: [ 'A', 'B', 'C' ] */ +~~~ -##### *Example* -
-$redis->type('key');
-
+### lLen, lSize +----- +_**Description**_: Returns the size of a list identified by Key. -## append -##### *Description* -Append specified string to the string stored in specified key. +If the list didn't exist or is empty, the command returns 0. If the data type identified by Key is not a list, the command return `FALSE`. ##### *Parameters* *Key* -*Value* ##### *Return value* -*INTEGER*: Size of the value after the append +*LONG* The size of the list identified by Key exists. +*BOOL* `FALSE` if the data type identified by Key is not list ##### *Example* -
-$redis->set('key', 'value1');
-$redis->append('key', 'value2'); /* 12 */
-$redis->get('key'); /* 'value1value2' */
-
- -## getRange (substr also supported but deprecated in redis) -##### *Description* -Return a substring of a larger string - +~~~ +$redis->rPush('key1', 'A'); +$redis->rPush('key1', 'B'); +$redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ +$redis->lSize('key1');/* 3 */ +$redis->rPop('key1'); +$redis->lSize('key1');/* 2 */ +~~~ + + +## Sets + +* [sAdd](#sadd) - Add one or more members to a set +* [sCard, sSize](#scard-ssize) - Get the number of members in a set +* [sDiff](#sdiff) - Subtract multiple sets +* [sDiffStore](#sdiffstore) - Subtract multiple sets and store the resulting set in a key +* [sInter](#sinter) - Intersect multiple sets +* [sInterStore](#sinterstore) - Intersect multiple sets and store the resulting set in a key +* [sIsMember, sContains](#sismember-scontains) - Determine if a given value is a member of a set +* [sMembers, sGetMembers](#smembers-sgetmembers) - Get all the members in a set +* [sMove](#smove) - Move a member from one set to another +* [sPop](#spop) - Remove and return a random member from a set +* [sRandMember](#srandmember) - Get one or multiple random members from a set +* [sRem, sRemove](#srem-sremove) - Remove one or more members from a set +* [sUnion](#sunion) - Add multiple sets +* [sUnionStore](#sunionstore) - Add multiple sets and store the resulting set in a key + +### sAdd +----- +_**Description**_: Adds a value to the set value stored at key. If this value is already in the set, `FALSE` is returned. ##### *Parameters* *key* -*start* -*end* +*value* ##### *Return value* -*STRING*: the substring - +*LONG* the number of elements added to the set. ##### *Example* -
-$redis->set('key', 'string value');
-$redis->getRange('key', 0, 5); /* 'string' */
-$redis->getRange('key', -5, -1); /* 'value' */
-
- -## setRange -##### *Description* -Changes a substring of a larger string. +~~~ +$redis->sAdd('key1' , 'member1'); /* 1, 'key1' => {'member1'} */ +$redis->sAdd('key1' , 'member2', 'member3'); /* 2, 'key1' => {'member1', 'member2', 'member3'}*/ +$redis->sAdd('key1' , 'member2'); /* 0, 'key1' => {'member1', 'member2', 'member3'}*/ +~~~ +### sCard, sSize +----- +_**Description**_: Returns the cardinality of the set identified by key. ##### *Parameters* -*key* -*offset* -*value* - +*key* ##### *Return value* -*STRING*: the length of the string after it was modified. - +*LONG* the cardinality of the set identified by key, 0 if the set doesn't exist. ##### *Example* -
-$redis->set('key', 'Hello world');
-$redis->setRange('key', 6, "redis"); /* returns 11 */
-$redis->get('key'); /* "Hello redis" */
-
+~~~ +$redis->sAdd('key1' , 'member1'); +$redis->sAdd('key1' , 'member2'); +$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ +$redis->sCard('key1'); /* 3 */ +$redis->sCard('keyX'); /* 0 */ +~~~ -## strlen -##### *Description* -Get the length of a string value. +### sDiff +----- +_**Description**_: Performs the difference between N sets and returns it. ##### *Parameters* -*key* +*Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. ##### *Return value* -*INTEGER* +*Array of strings*: The difference of the first set will all the others. ##### *Example* -
-$redis->set('key', 'value');
-$redis->strlen('key'); /* 5 */
-
- -## getBit -##### *Description* -Return a single bit out of a larger string - -##### *Parameters* -*key* -*offset* +~~~ +$redis->delete('s0', 's1', 's2'); -##### *Return value* -*LONG*: the bit value (0 or 1) +$redis->sAdd('s0', '1'); +$redis->sAdd('s0', '2'); +$redis->sAdd('s0', '3'); +$redis->sAdd('s0', '4'); -##### *Example* -
-$redis->set('key', "\x7f"); // this is 0111 1111
-$redis->getBit('key', 0); /* 0 */
-$redis->getBit('key', 1); /* 1 */
-
+$redis->sAdd('s1', '1'); +$redis->sAdd('s2', '3'); -## setBit -##### *Description* -Changes a single bit of a string. +var_dump($redis->sDiff('s0', 's1', 's2')); +~~~ +Return value: all elements of s0 that are neither in s1 nor in s2. +~~~ +array(2) { + [0]=> + string(1) "4" + [1]=> + string(1) "2" +} +~~~ +### sDiffStore +----- +_**Description**_: Performs the same action as sDiff, but stores the result in the first key ##### *Parameters* -*key* -*offset* -*value*: bool or int (1 or 0) +*Key*: dstkey, the key to store the diff into. +*Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis ##### *Return value* -*LONG*: 0 or 1, the value of the bit before it was set. +*INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* -
-$redis->set('key', "*");	// ord("*") = 42 = 0x2f = "0010 1010"
-$redis->setBit('key', 5, 1); /* returns 0 */
-$redis->setBit('key', 7, 1); /* returns 0 */
-$redis->get('key'); /* chr(0x2f) = "/" = b("0010 1111") */
-
+~~~ +$redis->delete('s0', 's1', 's2'); -## bitop -##### *Description* -Bitwise operation on multiple keys. +$redis->sAdd('s0', '1'); +$redis->sAdd('s0', '2'); +$redis->sAdd('s0', '3'); +$redis->sAdd('s0', '4'); -##### *Parameters* -*operation*: either "AND", "OR", "NOT", "XOR" -*ret_key*: return key -*key1* -*key2...* +$redis->sAdd('s1', '1'); +$redis->sAdd('s2', '3'); + +var_dump($redis->sDiffStore('dst', 's0', 's1', 's2')); +var_dump($redis->sMembers('dst')); +~~~ +Return value: the number of elements of s0 that are neither in s1 nor in s2. +~~~ +int(2) +array(2) { + [0]=> + string(1) "4" + [1]=> + string(1) "2" +} +~~~ -##### *Return value* -*LONG*: The size of the string stored in the destination key. +### sInter +----- +_**Description**_: Returns the members of a set resulting from the intersection of all the sets held at the specified keys. -## bitcount -##### *Description* -Count bits in a string. +If just a single key is specified, then this command produces the members of this set. If one of the keys +is missing, `FALSE` is returned. ##### *Parameters* -*key* +key1, key2, keyN: keys identifying the different sets on which we will apply the intersection. + ##### *Return value* -*LONG*: The number of bits set to 1 in the value behind the input key. -## flushDB +Array, contain the result of the intersection between those keys. If the intersection beteen the different sets is empty, the return value will be empty array. -##### *Description* -Removes all entries from the current database. +##### *Examples* +~~~ +$redis->sAdd('key1', 'val1'); +$redis->sAdd('key1', 'val2'); +$redis->sAdd('key1', 'val3'); +$redis->sAdd('key1', 'val4'); -##### *Parameters* -None. +$redis->sAdd('key2', 'val3'); +$redis->sAdd('key2', 'val4'); -##### *Return value* -*BOOL*: Always `TRUE`. +$redis->sAdd('key3', 'val3'); +$redis->sAdd('key3', 'val4'); -##### *Example* -
-$redis->flushDB();
-
+var_dump($redis->sInter('key1', 'key2', 'key3')); +~~~ +Output: -## flushAll -##### *Description* -Removes all entries from all databases. +~~~ +array(2) { + [0]=> + string(4) "val4" + [1]=> + string(4) "val3" +} +~~~ +### sInterStore +----- +_**Description**_: Performs a sInter command and stores the result in a new set. ##### *Parameters* -None. - -##### *Return value* -*BOOL*: Always `TRUE`. +*Key*: dstkey, the key to store the diff into. -##### *Example* -
-$redis->flushAll();
-
+*Keys*: key1, key2... keyN. key1..keyN are intersected as in sInter. -## sort -##### *Description* -##### *Parameters* -*Key*: key -*Options*: array(key => value, ...) - optional, with the following keys and values: -
-    'by' => 'some_pattern_*',
-    'limit' => array(0, 1),
-    'get' => 'some_other_pattern_*' or an array of patterns,
-    'sort' => 'asc' or 'desc',
-    'alpha' => TRUE,
-    'store' => 'external-key'
-
##### *Return value* -An array of values, or a number corresponding to the number of elements stored if that was used. +*INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* -
-$redis->delete('s');
-$redis->sadd('s', 5);
-$redis->sadd('s', 4);
-$redis->sadd('s', 2);
-$redis->sadd('s', 1);
-$redis->sadd('s', 3);
+~~~
+$redis->sAdd('key1', 'val1');
+$redis->sAdd('key1', 'val2');
+$redis->sAdd('key1', 'val3');
+$redis->sAdd('key1', 'val4');
 
-var_dump($redis->sort('s')); // 1,2,3,4,5
-var_dump($redis->sort('s', array('sort' => 'desc'))); // 5,4,3,2,1
-var_dump($redis->sort('s', array('sort' => 'desc', 'store' => 'out'))); // (int)5
-
+$redis->sAdd('key2', 'val3'); +$redis->sAdd('key2', 'val4'); +$redis->sAdd('key3', 'val3'); +$redis->sAdd('key3', 'val4'); -## info -##### *Description* -Returns an associative array from REDIS that provides information about the server. Passing -no arguments to INFO will call the standard REDIS INFO command, which returns information such -as the following: +var_dump($redis->sInterStore('output', 'key1', 'key2', 'key3')); +var_dump($redis->sMembers('output')); +~~~ -* redis_version -* arch_bits -* uptime_in_seconds -* uptime_in_days -* connected_clients -* connected_slaves -* used_memory -* changes_since_last_save -* bgsave_in_progress -* last_save_time -* total_connections_received -* total_commands_processed -* role +Output: + +~~~ +int(2) -You can pass a variety of options to INFO (per the Redis documentation), which will modify what is -returned. +array(2) { + [0]=> + string(4) "val4" + [1]=> + string(4) "val3" +} +~~~ +### sIsMember, sContains +----- +_**Description**_: Checks if `value` is a member of the set stored at the key `key`. ##### *Parameters* -*option*: The option to provide redis (e.g. "COMMANDSTATS", "CPU") +*key* +*value* +##### *Return value* +*BOOL* `TRUE` if `value` is a member of the set at key `key`, `FALSE` otherwise. ##### *Example* -
-$redis->info(); /* standard redis INFO command */
-$redis->info("COMMANDSTATS"); /* Information on the commands that have been run (>=2.6 only)
-$redis->info("CPU"); /* just CPU information from Redis INFO */
-
- -## resetStat -##### *Description* -Resets the statistics reported by Redis using the INFO command (`info()` function). - -These are the counters that are reset: +~~~ +$redis->sAdd('key1' , 'member1'); +$redis->sAdd('key1' , 'member2'); +$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ -* Keyspace hits -* Keyspace misses -* Number of commands processed -* Number of connections received -* Number of expired keys +$redis->sIsMember('key1', 'member1'); /* TRUE */ +$redis->sIsMember('key1', 'memberX'); /* FALSE */ +~~~ +### sMembers, sGetMembers +----- +_**Description**_: Returns the contents of a set. ##### *Parameters* -None. +*Key*: key ##### *Return value* -*BOOL*: `TRUE` in case of success, `FALSE` in case of failure. +An array of elements, the contents of the set. ##### *Example* -
-$redis->resetStat();
-
+~~~ +$redis->delete('s'); +$redis->sAdd('s', 'a'); +$redis->sAdd('s', 'b'); +$redis->sAdd('s', 'a'); +$redis->sAdd('s', 'c'); +var_dump($redis->sMembers('s')); +~~~ -## ttl, pttl -##### *Description* -Returns the time to live left for a given key, in seconds. If the key doesn't exist, `FALSE` is returned. pttl returns a time in milliseconds. +Output: +~~~ +array(3) { + [0]=> + string(1) "c" + [1]=> + string(1) "a" + [2]=> + string(1) "b" +} +~~~ +The order is random and corresponds to redis' own internal representation of the set structure. +### sMove +----- +_**Description**_: Moves the specified member from the set at srcKey to the set at dstKey. ##### *Parameters* -*Key*: key - +*srcKey* +*dstKey* +*member* ##### *Return value* -Long, the time left to live in seconds. - +*BOOL* If the operation is successful, return `TRUE`. If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, `FALSE` is returned. ##### *Example* -
-$redis->ttl('key');
-
+~~~ +$redis->sAdd('key1' , 'member11'); +$redis->sAdd('key1' , 'member12'); +$redis->sAdd('key1' , 'member13'); /* 'key1' => {'member11', 'member12', 'member13'}*/ +$redis->sAdd('key2' , 'member21'); +$redis->sAdd('key2' , 'member22'); /* 'key2' => {'member21', 'member22'}*/ +$redis->sMove('key1', 'key2', 'member13'); /* 'key1' => {'member11', 'member12'} */ + /* 'key2' => {'member21', 'member22', 'member13'} */ -## persist -##### *Description* -Remove the expiration timer from a key. +~~~ +### sPop +----- +_**Description**_: Removes and returns a random element from the set value at Key. ##### *Parameters* -*Key*: key +*key* +##### *Return value* +*String* "popped" value +*Bool* `FALSE` if set identified by key is empty or doesn't exist. +##### *Example* +~~~ +$redis->sAdd('key1' , 'member1'); +$redis->sAdd('key1' , 'member2'); +$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/ +$redis->sPop('key1'); /* 'member1', 'key1' => {'member3', 'member2'} */ +$redis->sPop('key1'); /* 'member3', 'key1' => {'member2'} */ +~~~ +### sRandMember +----- +_**Description**_: Returns one or more random elements from the set value at Key, without removing it. +##### *Parameters* +*key* +*count* Integer, optional ##### *Return value* -*BOOL*: `TRUE` if a timeout was removed, `FALSE` if the key didn’t exist or didn’t have an expiration timer. +If no count is specified, sRandMember will return a *String* value from the set. If a count is +provided, then an array of random values will be returned, following the semantics of how +Redis will treat the count itself. [SRANDMEMBER](http://www.redis.io/commands/srandmember). +*Bool* `FALSE` if set identified by key is empty or doesn't exist. +##### *Example* +~~~ +$redis->sAdd('key1' , 'member1'); +$redis->sAdd('key1' , 'member2'); +$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/ +// No count +$redis->sRandMember('key1'); /* 'member1', 'key1' => {'member3', 'member1', 'member2'} */ +$redis->sRandMember('key1'); /* 'member3', 'key1' => {'member3', 'member1', 'member2'} */ + +// With a count +$redis->sRandMember('key1', 3); // Will return an array with all members from the set +$redis->sRandMember('key1', 2); // Will an array with 2 members of the set +$redis->sRandMember('key1', -100); // Will return an array of 100 elements, picked from our set (with dups) +$redis->sRandMember('empty-set', 100); // Will return an empty array +$redis->sRandMember('not-a-set', 100); // Will return FALSE +~~~ + +### sRem, sRemove +----- +_**Description**_: Removes the specified member from the set value stored at key. +##### *Parameters* +*key* +*member* +##### *Return value* +*LONG* The number of elements removed from the set. ##### *Example* -
-$redis->persist('key');
-
+~~~ +$redis->sAdd('key1' , 'member1'); +$redis->sAdd('key1' , 'member2'); +$redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ +$redis->sRem('key1', 'member2', 'member3'); /*return 2. 'key1' => {'member1'} */ +~~~ -## mset, msetnx -##### *Description* -Sets multiple key-value pairs in one atomic command. MSETNX only returns TRUE if all the keys were set (see SETNX). +### sUnion +----- +_**Description**_: Performs the union between N sets and returns it. ##### *Parameters* -*Pairs*: array(key => value, ...) +*Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. ##### *Return value* -*Bool* `TRUE` in case of success, `FALSE` in case of failure. +*Array of strings*: The union of all these sets. ##### *Example* -
-
-$redis->mset(array('key0' => 'value0', 'key1' => 'value1'));
-var_dump($redis->get('key0'));
-var_dump($redis->get('key1'));
+~~~
+$redis->delete('s0', 's1', 's2');
 
-
-Output: -
-string(6) "value0"
-string(6) "value1"
-
+$redis->sAdd('s0', '1'); +$redis->sAdd('s0', '2'); +$redis->sAdd('s1', '3'); +$redis->sAdd('s1', '1'); +$redis->sAdd('s2', '3'); +$redis->sAdd('s2', '4'); +var_dump($redis->sUnion('s0', 's1', 's2')); +~~~ +Return value: all elements that are either in s0 or in s1 or in s2. +~~~ +array(4) { + [0]=> + string(1) "3" + [1]=> + string(1) "4" + [2]=> + string(1) "1" + [3]=> + string(1) "2" +} +~~~ -## rpoplpush (redis >= 1.1) -##### *Description* -Pops a value from the tail of a list, and pushes it to the front of another list. Also return this value. +### sUnionStore +----- +_**Description**_: Performs the same action as sUnion, but stores the result in the first key ##### *Parameters* -*Key*: srckey -*Key*: dstkey +*Key*: dstkey, the key to store the diff into. + +*Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. ##### *Return value* -*STRING* The element that was moved in case of success, `FALSE` in case of failure. +*INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* -
-$redis->delete('x', 'y');
-
-$redis->lPush('x', 'abc');
-$redis->lPush('x', 'def');
-$redis->lPush('y', '123');
-$redis->lPush('y', '456');
+~~~
+$redis->delete('s0', 's1', 's2');
 
-// move the last of x to the front of y.
-var_dump($redis->rpoplpush('x', 'y'));
-var_dump($redis->lRange('x', 0, -1));
-var_dump($redis->lRange('y', 0, -1));
+$redis->sAdd('s0', '1');
+$redis->sAdd('s0', '2');
+$redis->sAdd('s1', '3');
+$redis->sAdd('s1', '1');
+$redis->sAdd('s2', '3');
+$redis->sAdd('s2', '4');
 
-
-Output: -
-string(3) "abc"
-array(1) {
-  [0]=>
-  string(3) "def"
-}
-array(3) {
+var_dump($redis->sUnionStore('dst', 's0', 's1', 's2'));
+var_dump($redis->sMembers('dst'));
+~~~
+Return value: the number of elements that are either in s0 or in s1 or in s2.
+~~~
+int(4)
+array(4) {
   [0]=>
-  string(3) "abc"
+  string(1) "3"
   [1]=>
-  string(3) "456"
+  string(1) "4"
   [2]=>
-  string(3) "123"
+  string(1) "1"
+  [3]=>
+  string(1) "2"
 }
-
+~~~ -## brpoplpush -##### *Description* -A blocking version of `rpoplpush`, with an integral timeout in the third parameter. -##### *Parameters* -*Key*: srckey -*Key*: dstkey -*Long*: timeout +## Sorted sets -##### *Return value* -*STRING* The element that was moved in case of success, `FALSE` in case of timeout. +* [zAdd](#zadd) - Add one or more members to a sorted set or update its score if it already exists +* [zCard, zSize](#zcard-zsize) - Get the number of members in a sorted set +* [zCount](#zcount) - Count the members in a sorted set with scores within the given values +* [zIncrBy](#zincrby) - Increment the score of a member in a sorted set +* [zInter](#zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key +* [zRange](#zrange) - Return a range of members in a sorted set, by index +* [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score +* [zRank, zRevRank](#zrank-zrevrank) - Determine the index of a member in a sorted set +* [zRem, zDelete](#zrem-zdelete) - Remove one or more members from a sorted set +* [zRemRangeByRank, zDeleteRangeByRank](#zremrangebyrank-zdeleterangebyrank) - Remove all members in a sorted set within the given indexes +* [zRemRangeByScore, zDeleteRangeByScore](#zremrangebyscore-zdeleterangebyscore) - Remove all members in a sorted set within the given scores +* [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low +* [zScore](#zscore) - Get the score associated with the given member in a sorted set +* [zUnion](#zunion) - Add multiple sorted sets and store the resulting sorted set in a new key +### zAdd +----- +_**Description**_: Add one or more members to a sorted set or update its score if it already exists -## zAdd -##### *Description* -Adds the specified member with a given score to the sorted set stored at key. ##### *Parameters* *key* *score* : double @@ -1871,59 +2355,120 @@ Adds the specified member with a given score to the sorted set stored at key. ##### *Return value* *Long* 1 if the element is added. 0 otherwise. + ##### *Example* -
+~~~
 $redis->zAdd('key', 1, 'val1');
 $redis->zAdd('key', 0, 'val0');
 $redis->zAdd('key', 5, 'val5');
 $redis->zRange('key', 0, -1); // array(val0, val1, val5)
-
+~~~ + +### zCard, zSize +----- +_**Description**_: Returns the cardinality of an ordered set. -## zRange -##### *Description* -Returns a range of elements from the ordered set stored at the specified key, with values in the range [start, end]. start and stop are interpreted as zero-based indices: -0 the first element, 1 the second ... --1 the last element, -2 the penultimate ... ##### *Parameters* -*key* -*start*: long -*end*: long -*withscores*: bool = false +*key* ##### *Return value* -*Array* containing the values in specified range. +*Long*, the set's cardinality + ##### *Example* -
-$redis->zAdd('key1', 0, 'val0');
-$redis->zAdd('key1', 2, 'val2');
-$redis->zAdd('key1', 10, 'val10');
-$redis->zRange('key1', 0, -1); /* array('val0', 'val2', 'val10') */
+~~~
+$redis->zAdd('key', 0, 'val0');
+$redis->zAdd('key', 2, 'val2');
+$redis->zAdd('key', 10, 'val10');
+$redis->zSize('key'); /* 3 */
+~~~
 
-// with scores
-$redis->zRange('key1', 0, -1, true); /* array('val0' => 0, 'val2' => 2, 'val10' => 10) */
-
+### zCount +----- +_**Description**_: Returns the *number* of elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. -## zDelete, zRem -##### *Description* -Deletes a specified member from the ordered set. ##### *Parameters* *key* -*member* +*start*: string +*end*: string ##### *Return value* -*LONG* 1 on success, 0 on failure. +*LONG* the size of a corresponding zRangeByScore. + ##### *Example* -
+~~~
 $redis->zAdd('key', 0, 'val0');
 $redis->zAdd('key', 2, 'val2');
 $redis->zAdd('key', 10, 'val10');
-$redis->zDelete('key', 'val2');
-$redis->zRange('key', 0, -1); /* array('val0', 'val10') */
-
+$redis->zCount('key', 0, 3); /* 2, corresponding to array('val0', 'val2') */ +~~~ + +### zIncrBy +----- +_**Description**_: Increments the score of a member from a sorted set by a given amount. + +##### *Parameters* +*key* +*value*: (double) value that will be added to the member's score +*member* + +##### *Return value* +*DOUBLE* the new value + +##### *Examples* +~~~ +$redis->delete('key'); +$redis->zIncrBy('key', 2.5, 'member1'); /* key or member1 didn't exist, so member1's score is to 0 before the increment */ + /* and now has the value 2.5 */ +$redis->zIncrBy('key', 1, 'member1'); /* 3.5 */ +~~~ + +### zInter +----- +_**Description**_: Creates an intersection of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. + +The third optionnel argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. +The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. + +##### *Parameters* +*keyOutput* +*arrayZSetKeys* +*arrayWeights* +*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zInter. + +##### *Return value* +*LONG* The number of values in the new sorted set. + +##### *Example* +~~~ +$redis->delete('k1'); +$redis->delete('k2'); +$redis->delete('k3'); + +$redis->delete('ko1'); +$redis->delete('ko2'); +$redis->delete('ko3'); +$redis->delete('ko4'); + +$redis->zAdd('k1', 0, 'val0'); +$redis->zAdd('k1', 1, 'val1'); +$redis->zAdd('k1', 3, 'val3'); + +$redis->zAdd('k2', 2, 'val1'); +$redis->zAdd('k2', 3, 'val3'); + +$redis->zInter('ko1', array('k1', 'k2')); /* 2, 'ko1' => array('val1', 'val3') */ +$redis->zInter('ko2', array('k1', 'k2'), array(1, 1)); /* 2, 'ko2' => array('val1', 'val3') */ + +/* Weighted zInter */ +$redis->zInter('ko3', array('k1', 'k2'), array(1, 5), 'min'); /* 2, 'ko3' => array('val1', 'val3') */ +$redis->zInter('ko4', array('k1', 'k2'), array(1, 5), 'max'); /* 2, 'ko4' => array('val3', 'val1') */ +~~~ -## zRevRange -##### *Description* -Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpretated as zero-based indices: +### zRange +----- +_**Description**_: Returns a range of elements from the ordered set stored at the specified key, with values in the range [start, end]. + +Start and stop are interpreted as zero-based indices: 0 the first element, 1 the second ... -1 the last element, -2 the penultimate ... @@ -1935,20 +2480,22 @@ Returns the elements of the sorted set stored at the specified key in the range ##### *Return value* *Array* containing the values in specified range. + ##### *Example* -
-$redis->zAdd('key', 0, 'val0');
-$redis->zAdd('key', 2, 'val2');
-$redis->zAdd('key', 10, 'val10');
-$redis->zRevRange('key', 0, -1); /* array('val10', 'val2', 'val0') */
+~~~
+$redis->zAdd('key1', 0, 'val0');
+$redis->zAdd('key1', 2, 'val2');
+$redis->zAdd('key1', 10, 'val10');
+$redis->zRange('key1', 0, -1); /* array('val0', 'val2', 'val10') */
 
 // with scores
-$redis->zRevRange('key', 0, -1, true); /* array('val10' => 10, 'val2' => 2, 'val0' => 0) */
-
+$redis->zRange('key1', 0, -1, true); /* array('val0' => 0, 'val2' => 2, 'val10' => 10) */ +~~~ + +### zRangeByScore, zRevRangeByScore +----- +_**Description**_: Returns the elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. zRevRangeByScore returns the same items in reverse order, when the `start` and `end` parameters are swapped. -## zRangeByScore, zRevRangeByScore -##### *Description* -Returns the elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. zRevRangeByScore returns the same items in reverse order, when the `start` and `end` parameters are swapped. ##### *Parameters* *key* *start*: string @@ -1956,10 +2503,12 @@ Returns the elements of the sorted set stored at the specified key which have sc *options*: array Two options are available: `withscores => TRUE`, and `limit => array($offset, $count)` + ##### *Return value* *Array* containing the values in specified range. + ##### *Example* -
+~~~
 $redis->zAdd('key', 0, 'val0');
 $redis->zAdd('key', 2, 'val2');
 $redis->zAdd('key', 10, 'val10');
@@ -1968,134 +2517,141 @@ $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE); /* array('val0'
 $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2' => 2) */
 $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2') */
 $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1)); /* array('val2' => 2) */
-
+~~~ + +### zRank, zRevRank +----- +_**Description**_: Returns the rank of a given member in the specified sorted set, starting at 0 for the item with the smallest score. zRevRank starts at 0 for the item with the *largest* score. -## zCount -##### *Description* -Returns the *number* of elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. ##### *Parameters* *key* -*start*: string -*end*: string +*member* ##### *Return value* -*LONG* the size of a corresponding zRangeByScore. +*Long*, the item's score. + ##### *Example* -
-$redis->zAdd('key', 0, 'val0');
-$redis->zAdd('key', 2, 'val2');
-$redis->zAdd('key', 10, 'val10');
-$redis->zCount('key', 0, 3); /* 2, corresponding to array('val0', 'val2') */
-
+~~~ +$redis->delete('z'); +$redis->zAdd('key', 1, 'one'); +$redis->zAdd('key', 2, 'two'); +$redis->zRank('key', 'one'); /* 0 */ +$redis->zRank('key', 'two'); /* 1 */ +$redis->zRevRank('key', 'one'); /* 1 */ +$redis->zRevRank('key', 'two'); /* 0 */ +~~~ + +### zRem, zDelete +----- +_**Description**_: Deletes a specified member from the ordered set. -## zRemRangeByScore, zDeleteRangeByScore -##### *Description* -Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end]. ##### *Parameters* *key* -*start*: double or "+inf" or "-inf" string -*end*: double or "+inf" or "-inf" string +*member* ##### *Return value* -*LONG* The number of values deleted from the sorted set +*LONG* 1 on success, 0 on failure. + ##### *Example* -
+~~~
 $redis->zAdd('key', 0, 'val0');
 $redis->zAdd('key', 2, 'val2');
 $redis->zAdd('key', 10, 'val10');
-$redis->zRemRangeByScore('key', 0, 3); /* 2 */
-
+$redis->zDelete('key', 'val2'); +$redis->zRange('key', 0, -1); /* array('val0', 'val10') */ +~~~ + +### zRemRangeByRank, zDeleteRangeByRank +----- +_**Description**_: Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end]. -## zRemRangeByRank, zDeleteRangeByRank -##### *Description* -Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end]. ##### *Parameters* *key* *start*: LONG *end*: LONG + ##### *Return value* *LONG* The number of values deleted from the sorted set + ##### *Example* -
+~~~
 $redis->zAdd('key', 1, 'one');
 $redis->zAdd('key', 2, 'two');
 $redis->zAdd('key', 3, 'three');
 $redis->zRemRangeByRank('key', 0, 1); /* 2 */
 $redis->zRange('key', 0, -1, array('withscores' => TRUE)); /* array('three' => 3) */
-
+~~~ + +### zRemRangeByScore, zDeleteRangeByScore +----- +_**Description**_: Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end]. -## zSize, zCard -##### *Description* -Returns the cardinality of an ordered set. ##### *Parameters* -*key* +*key* +*start*: double or "+inf" or "-inf" string +*end*: double or "+inf" or "-inf" string ##### *Return value* -*Long*, the set's cardinality +*LONG* The number of values deleted from the sorted set + ##### *Example* -
+~~~
 $redis->zAdd('key', 0, 'val0');
 $redis->zAdd('key', 2, 'val2');
 $redis->zAdd('key', 10, 'val10');
-$redis->zSize('key'); /* 3 */
-
+$redis->zRemRangeByScore('key', 0, 3); /* 2 */ +~~~ + +### zRevRange +----- +_**Description**_: Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpretated as zero-based indices: +0 the first element, 1 the second ... +-1 the last element, -2 the penultimate ... -## zScore -##### *Description* -Returns the score of a given member in the specified sorted set. ##### *Parameters* *key* -*member* +*start*: long +*end*: long +*withscores*: bool = false ##### *Return value* -*Double* +*Array* containing the values in specified range. + ##### *Example* -
-$redis->zAdd('key', 2.5, 'val2');
-$redis->zScore('key', 'val2'); /* 2.5 */
-
+~~~ +$redis->zAdd('key', 0, 'val0'); +$redis->zAdd('key', 2, 'val2'); +$redis->zAdd('key', 10, 'val10'); +$redis->zRevRange('key', 0, -1); /* array('val10', 'val2', 'val0') */ + +// with scores +$redis->zRevRange('key', 0, -1, true); /* array('val10' => 10, 'val2' => 2, 'val0' => 0) */ +~~~ + +### zScore +----- +_**Description**_: Returns the score of a given member in the specified sorted set. -## zRank, zRevRank -##### *Description* -Returns the rank of a given member in the specified sorted set, starting at 0 for the item with the smallest score. zRevRank starts at 0 for the item with the *largest* score. ##### *Parameters* *key* *member* + ##### *Return value* -*Long*, the item's score. +*Double* + ##### *Example* -
-$redis->delete('z');
-$redis->zAdd('key', 1, 'one');
-$redis->zAdd('key', 2, 'two');
-$redis->zRank('key', 'one'); /* 0 */
-$redis->zRank('key', 'two'); /* 1 */
-$redis->zRevRank('key', 'one'); /* 1 */
-$redis->zRevRank('key', 'two'); /* 0 */
-
+~~~ +$redis->zAdd('key', 2.5, 'val2'); +$redis->zScore('key', 'val2'); /* 2.5 */ +~~~ -## zIncrBy -##### Description -Increments the score of a member from a sorted set by a given amount. -##### Parameters -*key* -*value*: (double) value that will be added to the member's score -*member* -##### Return value -*DOUBLE* the new value -##### Examples -
-$redis->delete('key');
-$redis->zIncrBy('key', 2.5, 'member1'); /* key or member1 didn't exist, so member1's score is to 0 before the increment */
-					  /* and now has the value 2.5  */
-$redis->zIncrBy('key', 1, 'member1'); /* 3.5 */
-
+### zUnion +----- +_**Description**_: Creates an union of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. -## zUnion -##### *Description* -Creates an union of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. The third optionnel argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. + ##### *Parameters* *keyOutput* *arrayZSetKeys* @@ -2104,355 +2660,177 @@ The forth argument defines the `AGGREGATE` option which specify how the results ##### *Return value* *LONG* The number of values in the new sorted set. -##### *Example* -
-$redis->delete('k1');
-$redis->delete('k2');
-$redis->delete('k3');
-$redis->delete('ko1');
-$redis->delete('ko2');
-$redis->delete('ko3');
-
-$redis->zAdd('k1', 0, 'val0');
-$redis->zAdd('k1', 1, 'val1');
-
-$redis->zAdd('k2', 2, 'val2');
-$redis->zAdd('k2', 3, 'val3');
-
-$redis->zUnion('ko1', array('k1', 'k2')); /* 4, 'ko1' => array('val0', 'val1', 'val2', 'val3') */
-
-/* Weighted zUnion */
-$redis->zUnion('ko2', array('k1', 'k2'), array(1, 1)); /* 4, 'ko2' => array('val0', 'val1', 'val2', 'val3') */
-$redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val0', 'val2', 'val3', 'val1') */
-
- -## zInter -##### *Description* -Creates an intersection of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. -The third optionnel argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. -The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. -##### *Parameters* -*keyOutput* -*arrayZSetKeys* -*arrayWeights* -*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zInter. -##### *Return value* -*LONG* The number of values in the new sorted set. ##### *Example* -
+~~~
 $redis->delete('k1');
 $redis->delete('k2');
 $redis->delete('k3');
-
 $redis->delete('ko1');
 $redis->delete('ko2');
 $redis->delete('ko3');
-$redis->delete('ko4');
 
 $redis->zAdd('k1', 0, 'val0');
-$redis->zAdd('k1', 1, 'val1');
-$redis->zAdd('k1', 3, 'val3');
-
-$redis->zAdd('k2', 2, 'val1');
-$redis->zAdd('k2', 3, 'val3');
-
-$redis->zInter('ko1', array('k1', 'k2')); 				/* 2, 'ko1' => array('val1', 'val3') */
-$redis->zInter('ko2', array('k1', 'k2'), array(1, 1)); 	/* 2, 'ko2' => array('val1', 'val3') */
-
-/* Weighted zInter */
-$redis->zInter('ko3', array('k1', 'k2'), array(1, 5), 'min'); /* 2, 'ko3' => array('val1', 'val3') */
-$redis->zInter('ko4', array('k1', 'k2'), array(1, 5), 'max'); /* 2, 'ko4' => array('val3', 'val1') */
-
-
- -## hSet -##### *Description* -Adds a value to the hash stored at key. If this value is already in the hash, `FALSE` is returned. -##### *Parameters* -*key* -*hashKey* -*value* - -##### *Return value* -*LONG* `1` if value didn't exist and was added successfully, `0` if the value was already present and was replaced, `FALSE` if there was an error. -##### *Example* -
-$redis->delete('h')
-$redis->hSet('h', 'key1', 'hello'); /* 1, 'key1' => 'hello' in the hash at "h" */
-$redis->hGet('h', 'key1'); /* returns "hello" */
+$redis->zAdd('k1', 1, 'val1');
 
-$redis->hSet('h', 'key1', 'plop'); /* 0, value was replaced. */
-$redis->hGet('h', 'key1'); /* returns "plop" */
-
+$redis->zAdd('k2', 2, 'val2'); +$redis->zAdd('k2', 3, 'val3'); -## hSetNx -##### *Description* -Adds a value to the hash stored at key only if this field isn't already in the hash. +$redis->zUnion('ko1', array('k1', 'k2')); /* 4, 'ko1' => array('val0', 'val1', 'val2', 'val3') */ -##### *Return value* -*BOOL* `TRUE` if the field was set, `FALSE` if it was already present. +/* Weighted zUnion */ +$redis->zUnion('ko2', array('k1', 'k2'), array(1, 1)); /* 4, 'ko2' => array('val0', 'val1', 'val2', 'val3') */ +$redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val0', 'val2', 'val3', 'val1') */ +~~~ -##### *Example* -
-$redis->delete('h')
-$redis->hSetNx('h', 'key1', 'hello'); /* TRUE, 'key1' => 'hello' in the hash at "h" */
-$redis->hSetNx('h', 'key1', 'world'); /* FALSE, 'key1' => 'hello' in the hash at "h". No change since the field wasn't replaced. */
-
+## Pub/sub + +* [psubscribe](#psubscribe) - Subscribe to channels by pattern +* [publish](#publish) - Post a message to a channel +* [subscribe](#subscribe) - Subscribe to channels +### psubscribe +----- +_**Description**_: Subscribe to channels by pattern -## hGet -##### *Description* -Gets a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* -*key* -*hashKey* +*patterns*: An array of patterns to match +*callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) -##### *Return value* -*STRING* The value, if the command executed successfully -*BOOL* `FALSE` in case of failure +##### *Example* +~~~ +function psubscribe($redis, $pattern, $chan, $msg) { + echo "Pattern: $pattern\n"; + echo "Channel: $chan\n"; + echo "Payload: $msg\n"; +} +~~~ +### publish +----- +_**Description**_: Publish messages to channels. Warning: this function will probably change in the future. -## hLen -##### *Description* -Returns the length of a hash, in number of items ##### *Parameters* -*key* +*channel*: a channel to publish to +*messsage*: string -##### *Return value* -*LONG* the number of items in a hash, `FALSE` if the key doesn't exist or isn't a hash. ##### *Example* -
-$redis->delete('h')
-$redis->hSet('h', 'key1', 'hello');
-$redis->hSet('h', 'key2', 'plop');
-$redis->hLen('h'); /* returns 2 */
-
+~~~ +$redis->publish('chan-1', 'hello, world!'); // send message. +~~~ + +### subscribe +----- +_**Description**_: Subscribe to channels. Warning: this function will probably change in the future. -## hDel -##### *Description* -Removes a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* -*key* -*hashKey* +*channels*: an array of channels to subscribe to +*callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. -##### *Return value* -*BOOL* `TRUE` in case of success, `FALSE` in case of failure +##### *Example* +~~~ +function f($redis, $chan, $msg) { + switch($chan) { + case 'chan-1': + ... + break; + case 'chan-2': + ... + break; -## hKeys -##### *Description* -Returns the keys in a hash, as an array of strings. + case 'chan-2': + ... + break; + } +} -##### *Parameters* -*Key*: key +$redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans +~~~ -##### *Return value* -An array of elements, the keys of the hash. This works like PHP's array_keys(). -##### *Example* -
-$redis->delete('h');
-$redis->hSet('h', 'a', 'x');
-$redis->hSet('h', 'b', 'y');
-$redis->hSet('h', 'c', 'z');
-$redis->hSet('h', 'd', 't');
-var_dump($redis->hKeys('h'));
-
+## Transactions -Output: -
-array(4) {
-  [0]=>
-  string(1) "a"
-  [1]=>
-  string(1) "b"
-  [2]=>
-  string(1) "c"
-  [3]=>
-  string(1) "d"
-}
-
-The order is random and corresponds to redis' own internal representation of the set structure. +1. [multi, exec, discard](#multi-exec-discard) - Enter and exit transactional mode +2. [watch, unwatch](#watch-unwatch) - Watches a key for modifications by another client. -## hVals -##### *Description* -Returns the values in a hash, as an array of strings. +### multi, exec, discard. +----- +_**Description**_: Enter and exit transactional mode. ##### *Parameters* -*Key*: key +(optional) `Redis::MULTI` or `Redis::PIPELINE`. Defaults to `Redis::MULTI`. A `Redis::MULTI` block of commands runs as a single transaction; a `Redis::PIPELINE` block is simply transmitted faster to the server, but without any guarantee of atomicity. `discard` cancels a transaction. ##### *Return value* -An array of elements, the values of the hash. This works like PHP's array_values(). +`multi()` returns the Redis instance and enters multi-mode. Once in multi-mode, all subsequent method calls return the same object until `exec()` is called. ##### *Example* -
-$redis->delete('h');
-$redis->hSet('h', 'a', 'x');
-$redis->hSet('h', 'b', 'y');
-$redis->hSet('h', 'c', 'z');
-$redis->hSet('h', 'd', 't');
-var_dump($redis->hVals('h'));
-
+~~~ +$ret = $redis->multi() + ->set('key1', 'val1') + ->get('key1') + ->set('key2', 'val2') + ->get('key2') + ->exec(); -Output: -
-array(4) {
-  [0]=>
-  string(1) "x"
-  [1]=>
-  string(1) "y"
-  [2]=>
-  string(1) "z"
-  [3]=>
-  string(1) "t"
-}
-
-The order is random and corresponds to redis' own internal representation of the set structure. +/* +$ret == array( + 0 => TRUE, + 1 => 'val1', + 2 => TRUE, + 3 => 'val2'); +*/ +~~~ -## hGetAll -##### *Description* -Returns the whole hash, as an array of strings indexed by strings. +### watch, unwatch +----- +_**Description**_: Watches a key for modifications by another client. -##### *Parameters* -*Key*: key +If the key is modified between `WATCH` and `EXEC`, the MULTI/EXEC transaction will fail (return `FALSE`). `unwatch` cancels all the watching of all keys by this client. -##### *Return value* -An array of elements, the contents of the hash. +##### *Parameters* +*keys*: a list of keys ##### *Example* -
-$redis->delete('h');
-$redis->hSet('h', 'a', 'x');
-$redis->hSet('h', 'b', 'y');
-$redis->hSet('h', 'c', 'z');
-$redis->hSet('h', 'd', 't');
-var_dump($redis->hGetAll('h'));
-
- -Output: -
-array(4) {
-  ["a"]=>
-  string(1) "x"
-  ["b"]=>
-  string(1) "y"
-  ["c"]=>
-  string(1) "z"
-  ["d"]=>
-  string(1) "t"
-}
-
-The order is random and corresponds to redis' own internal representation of the set structure. - -## hExists -##### Description -Verify if the specified member exists in a key. -##### Parameters -*key* -*memberKey* -##### Return value -*BOOL*: If the member exists in the hash table, return `TRUE`, otherwise return `FALSE`. -##### Examples -
-$redis->hSet('h', 'a', 'x');
-$redis->hExists('h', 'a'); /*  TRUE */
-$redis->hExists('h', 'NonExistingKey'); /* FALSE */
-
+~~~ +$redis->watch('x'); +/* long code here during the execution of which other clients could well modify `x` */ +$ret = $redis->multi() + ->incr('x') + ->exec(); +/* +$ret = FALSE if x has been modified between the call to WATCH and the call to EXEC. +*/ +~~~ -## hIncrBy -##### Description -Increments the value of a member from a hash by a given amount. -##### Parameters -*key* -*member* -*value*: (integer) value that will be added to the member's value -##### Return value -*LONG* the new value -##### Examples -
-$redis->delete('h');
-$redis->hIncrBy('h', 'x', 2); /* returns 2: h[x] = 2 now. */
-$redis->hIncrBy('h', 'x', 1); /* h[x] ← 2 + 1. Returns 3 */
-
-## hIncrByFloat -##### Description -Increments the value of a hash member by the provided float value -##### Parameters -*key* -*member* -*value*: (float) value that will be added to the member's value -##### Return value -*FLOAT* the new value -##### Examples -
-$redis->delete('h');
-$redis->hIncrByFloat('h','x', 1.5); /* returns 1.5: h[x] = 1.5 now */
-$redis->hIncrByFLoat('h', 'x', 1.5); /* returns 3.0: h[x] = 3.0 now */
-$redis->hIncrByFloat('h', 'x', -3.0); /* returns 0.0: h[x] = 0.0 now */
-
-## hMset -##### Description -Fills in a whole hash. Non-string values are converted to string, using the standard `(string)` cast. NULL values are stored as empty strings. -##### Parameters -*key* -*members*: key → value array -##### Return value -*BOOL* -##### Examples -
-$redis->delete('user:1');
-$redis->hMset('user:1', array('name' => 'Joe', 'salary' => 2000));
-$redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now.
-
+## Scripting -## hMGet -##### Description -Retrieve the values associated to the specified fields in the hash. -##### Parameters -*key* -*memberKeys* Array -##### Return value -*Array* An array of elements, the values of the specified fields in the hash, with the hash keys as array keys. -##### Examples -
-$redis->delete('h');
-$redis->hSet('h', 'field1', 'value1');
-$redis->hSet('h', 'field2', 'value2');
-$redis->hmGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */
-
+* [eval](#) - Evaluate a LUA script serverside +* [evalSha](#) - Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself +* [script](#) - Execute the Redis SCRIPT command to perform various operations on the scripting subsystem +* [getLastError](#) - The last error message (if any) +* [clearLastError](#) - Clear the last error message +* [_prefix](#) - A utility method to prefix the value with the prefix setting for phpredis +* [_unserialize](#) - A utility method to unserialize data with whatever serializer is set up -## config -##### Description -Get or Set the redis config keys. -##### Parameters -*operation* (string) either `GET` or `SET` -*key* string for `SET`, glob-pattern for `GET`. See http://redis.io/commands/config-get for examples. -*value* optional string (only for `SET`) -##### Return value -*Associative array* for `GET`, key -> value -*bool* for `SET` -##### Examples -
-$redis->config("GET", "*max-*-entries*");
-$redis->config("SET", "dir", "/var/run/redis/dumps/");
-
+### eval +----- +_**Description**_: Evaluate a LUA script serverside -## eval -##### Description -Evaluate a LUA script serverside -##### Parameters +##### *Parameters* *script* string. *args* array, optional. *num_keys* int, optional. -##### Return value + +##### *Return value* Mixed. What is returned depends on what the LUA script itself returns, which could be a scalar value (int/string), or an array. Arrays that are returned can also contain other arrays, if that's how it was set up in your LUA script. If there is an error executing the LUA script, the getLastError() function can tell you the message that came back from Redis (e.g. compile error). -##### Examples -
+
+##### *Examples*
+~~~
 $redis->eval("return 1"); // Returns an integer: 1
 $redis->eval("return {1,2,3}"); // Returns Array(1,2,3)
 $redis->del('mylist');
@@ -2461,64 +2839,77 @@ $redis->rpush('mylist','b');
 $redis->rpush('mylist','c');
 // Nested response:  Array(1,2,3,Array('a','b','c'));
 $redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}}");
-
+~~~ + +### evalSha +----- +_**Description**_: Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself. -## evalSha -##### Description -Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself. In order to run this command Redis -will have to have already loaded the script, either by running it or via the SCRIPT LOAD command. -##### Parameters +In order to run this command Redis will have to have already loaded the script, +either by running it or via the SCRIPT LOAD command. + +##### *Parameters* *script_sha* string. The sha1 encoded hash of the script you want to run. *args* array, optional. Arguments to pass to the LUA script. *num_keys* int, optional. The number of arguments that should go into the KEYS array, vs. the ARGV array when Redis spins the script -##### Return value + +##### *Return value* Mixed. See EVAL -##### Examples -
+
+##### *Examples*
+~~~
 $script = 'return 1';
 $sha = $redis->script('load', $script);
 $redis->evalSha($sha); // Returns 1
-
+~~~ -## script -##### Description -Execute the Redis SCRIPT command to perform various operations on the scripting subsystem. -##### Usage -
+### script
+-----
+_**Description**_: Execute the Redis SCRIPT command to perform various operations on the scripting subsystem.
+
+##### *Usage*
+~~~
 $redis->script('load', $script);
 $redis->script('flush');
 $redis->script('kill');
 $redis->script('exists', $script1, [$script2, $script3, ...]);
-
-##### Return value +~~~ + +##### *Return value* * SCRIPT LOAD will return the SHA1 hash of the passed script on success, and FALSE on failure. * SCRIPT FLUSH should always return TRUE * SCRIPT KILL will return true if a script was able to be killed and false if not * SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script -## getLastError -##### Description -The last error message (if any) -##### Parameters +### getLastError +----- +_**Description**_: The last error message (if any) + +##### *Parameters* *none* -##### Return Value + +##### *Return value* A string with the last returned script based error message, or NULL if there is no error -##### Examples -
+
+##### *Examples*
+~~~
 $redis->eval('this-is-not-lua');
 $err = $redis->getLastError(); 
 // "ERR Error compiling script (new function): user_script:1: '=' expected near '-'"
-
+~~~ + +### clearLastError +----- +_**Description**_: Clear the last error message -## clearLastError -##### Description -Clear the last error message -##### Parameters +##### *Parameters* *none* -##### Return Value + +##### *Return value* *BOOL* TRUE -##### Examples -
+
+##### *Examples*
+~~~
 $redis->set('x', 'a');
 $redis->incr('x');
 $err = $redis->getLastError();
@@ -2526,85 +2917,37 @@ $err = $redis->getLastError();
 $redis->clearLastError();
 $err = $redis->getLastError();
 // NULL
-
+~~~ + +### _prefix +----- +_**Description**_: A utility method to prefix the value with the prefix setting for phpredis. -## _prefix -##### Description -A utility method to prefix the value with the prefix setting for phpredis. -##### Parameters +##### *Parameters* *value* string. The value you wish to prefix -##### Return value + +##### *Return value* If a prefix is set up, the value now prefixed. If there is no prefix, the value will be returned unchanged. -##### Examples -
+
+##### *Examples*
+~~~
 $redis->setOption(Redis::OPT_PREFIX, 'my-prefix:');
 $redis->_prefix('my-value'); // Will return 'my-prefix:my-value'
-
- -## _unserialize -##### Description -A utility method to unserialize data with whatever serializer is set up. If there is no serializer set, the value will be -returned unchanged. If there is a serializer set up, and the data passed in is malformed, an exception will be thrown. -This can be useful if phpredis is serializing values, and you return something from redis in a LUA script that is serialized. -##### Parameters -*value* string. The value to be unserialized -##### Examples -
-$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
-$redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2,3)
-
+~~~ -## dump -##### Description -Dump a key out of a redis database, the value of which can later be passed into redis using the RESTORE command. The data -that comes out of DUMP is a binary representation of the key as Redis stores it. -##### Parameters -*key* string -##### Return value -The Redis encoded value of the key, or FALSE if the key doesn't exist -##### Examples -
-$redis->set('foo', 'bar');
-$val = $redis->dump('foo'); // $val will be the Redis encoded key value
-
+### _unserialize +----- +_**Description**_: A utility method to unserialize data with whatever serializer is set up. -## restore -##### Description -Restore a key from the result of a DUMP operation. -##### Parameters -*key* string. The key name -*ttl* integer. How long the key should live (if zero, no expire will be set on the key) -*value* string (binary). The Redis encoded key value (from DUMP) -##### Examples -
-$redis->set('foo', 'bar');
-$val = $redis->dump('foo');
-$redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key 'foo'
-
+If there is no serializer set, the value will be returned unchanged. If there is a serializer set up, +and the data passed in is malformed, an exception will be thrown. This can be useful if phpredis is +serializing values, and you return something from redis in a LUA script that is serialized. -## migrate -##### Description -Migrates a key to a different Redis instance. -##### Parameters -*host* string. The destination host -*port* integer. The TCP port to connect to. -*key* string. The key to migrate. -*destination-db* integer. The target DB. -*timeout* integer. The maximum amount of time given to this transfer. -##### Examples -
-$redis->migrate('backup', 6379, 'foo', 0, 3600);
-
+##### *Parameters* +*value* string. The value to be unserialized -## time -##### Description -Return the current Redis server time. -##### Parameters -(none) -##### Return value -If successfull, the time will come back as an associative array with element zero being -the unix timestamp, and element one being microseconds. -##### Examples -
-$redis->time();
-
+##### *Examples* +~~~ +$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); +$redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2,3) +~~~ From c1e7da4256428db4195cb555cf5a95b2cb6d65a1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 4 Feb 2013 13:13:00 -0800 Subject: [PATCH 0013/1986] Fix markdown formatting --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 6fe6ce5702..f6f044b6ac 100644 --- a/README.markdown +++ b/README.markdown @@ -2207,8 +2207,8 @@ $redis->sPop('key1'); /* 'member3', 'key1' => {'member2'} */ ----- _**Description**_: Returns one or more random elements from the set value at Key, without removing it. ##### *Parameters* -*key* -*count* Integer, optional +*key* +*count* Integer, optional ##### *Return value* If no count is specified, sRandMember will return a *String* value from the set. If a count is provided, then an array of random values will be returned, following the semantics of how From 71a002d7fe88dfd219c83b08b78726f31e83561f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 4 Feb 2013 13:18:22 -0800 Subject: [PATCH 0014/1986] Pulling README.markdown from master --- README.markdown | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/README.markdown b/README.markdown index f6f044b6ac..9ccfbbd55b 100644 --- a/README.markdown +++ b/README.markdown @@ -2205,31 +2205,19 @@ $redis->sPop('key1'); /* 'member3', 'key1' => {'member2'} */ ### sRandMember ----- -_**Description**_: Returns one or more random elements from the set value at Key, without removing it. +_**Description**_: Returns a random element from the set value at Key, without removing it. ##### *Parameters* -*key* -*count* Integer, optional +*key* ##### *Return value* -If no count is specified, sRandMember will return a *String* value from the set. If a count is -provided, then an array of random values will be returned, following the semantics of how -Redis will treat the count itself. [SRANDMEMBER](http://www.redis.io/commands/srandmember). +*String* value from the set *Bool* `FALSE` if set identified by key is empty or doesn't exist. ##### *Example* ~~~ $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/ - -// No count $redis->sRandMember('key1'); /* 'member1', 'key1' => {'member3', 'member1', 'member2'} */ $redis->sRandMember('key1'); /* 'member3', 'key1' => {'member3', 'member1', 'member2'} */ - -// With a count -$redis->sRandMember('key1', 3); // Will return an array with all members from the set -$redis->sRandMember('key1', 2); // Will an array with 2 members of the set -$redis->sRandMember('key1', -100); // Will return an array of 100 elements, picked from our set (with dups) -$redis->sRandMember('empty-set', 100); // Will return an empty array -$redis->sRandMember('not-a-set', 100); // Will return FALSE ~~~ ### sRem, sRemove From 85ee61d19b6c387c19ccfeda507c3f0fd1e67cf4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Oct 2012 14:40:24 -0700 Subject: [PATCH 0015/1986] Add prefix support to SUBSCRIBE/PSUBSCRIBE Now the Redis::OPT_PREFIX will apply to subscribe/psubscribe channels and will be applied before searching for messages. --- redis.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index caffb16997..20603b7f27 100644 --- a/redis.c +++ b/redis.c @@ -5322,8 +5322,8 @@ PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd) HashTable *arr_hash; HashPosition pointer; RedisSock *redis_sock; - char *cmd = "", *old_cmd = NULL; - int cmd_len, array_count; + char *cmd = "", *old_cmd = NULL, *key; + int cmd_len, array_count, key_len, key_free; zval *z_tab, **tmp; char *type_response; @@ -5358,10 +5358,24 @@ PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd) if(*cmd) { old_cmd = cmd; } - cmd_len = spprintf(&cmd, 0, "%s %s", cmd, Z_STRVAL_PP(data)); + + // Grab our key and len + key = Z_STRVAL_PP(data); + key_len = Z_STRLEN_PP(data); + + // Prefix our key if neccisary + key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + + cmd_len = spprintf(&cmd, 0, "%s %s", cmd, key); + if(old_cmd) { efree(old_cmd); } + + // Free our key if it was prefixed + if(key_free) { + efree(key); + } } } From 31174e8e7784b86e2f206afa880606bcc72d4011 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 15 Feb 2013 18:01:11 -0800 Subject: [PATCH 0016/1986] Just removed a debug line --- redis.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/redis.c b/redis.c index 6af6f64fb4..a655e2cb4d 100644 --- a/redis.c +++ b/redis.c @@ -2511,11 +2511,6 @@ PHPAPI int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword efree(keys_to_free); if(z_args) efree(z_args); - - /* - cmd[cmd_len] = 0; - php_printf("cmd=[%s]\n", cmd); - */ /* call REDIS_PROCESS_REQUEST and skip void returns */ IF_MULTI_OR_ATOMIC() { From 9a5196ed2e1bf385e51a9eaf668ff9e1a09f5fed Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 16 Feb 2013 17:57:32 -0800 Subject: [PATCH 0017/1986] CLIENT Commands This commit adds support for the CLIENT commands (list, getname, setname, kill). You can call them like so: $redis->client('list'); $redis->client('getname'); $redis->client('setname', $name); $redis->client('kill', $ip_port); Solves issue #300 --- README.markdown | 26 ++++++++++ library.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ library.h | 1 + php_redis.h | 2 + redis.c | 54 +++++++++++++++++++- tests/TestRedis.php | 26 ++++++++++ 6 files changed, 230 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index a6540533c9..0ce7330966 100644 --- a/README.markdown +++ b/README.markdown @@ -2881,6 +2881,32 @@ $redis->script('exists', $script1, [$script2, $script3, ...]); * SCRIPT KILL will return true if a script was able to be killed and false if not * SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script +### client +----- +_**Description**_: Issue the CLIENT command with various arguments. + +The Redis CLIENT command can be used in four ways. +1. CLIENT LIST +1. CLIENT GETNAME +1. CLIENT SETNAME [name] +1. CLIENT KILL [ip:port] +##### *Usage* +~~~ +$redis->client('list'); // Get a list of clients +$redis->client('getname'); // Get the name of the current connection +$redis->client('setname', 'somename'); // Set the name of the current connection +$redis->client('kill', ); // Kill the process at ip:port +~~~ +##### *Return value* +This will vary depending on which client command was executed. + +CLIENT LIST will return an array of arrys with client information. +CLIENT GETNAME will return the client name or false if none has been set +CLIENT SETNAME will return true if it can be set and false if not +CLIENT KILL will return true if the client can be killed, and false if not + +Note: phpredis will attempt to reconnect so you can actually kill your own connection +but may not notice losing it! ### getLastError ----- _**Description**_: The last error message (if any) diff --git a/library.c b/library.c index 7809ce420b..f35eb4c300 100644 --- a/library.c +++ b/library.c @@ -608,6 +608,128 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s } } +/* + * Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code + * to handle. + */ +PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { + char *resp; + int resp_len; + zval *z_result, *z_sub_result; + + // Make sure we can read a response from Redis + if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } + + // Allocate memory for our response + MAKE_STD_ZVAL(z_result); + array_init(z_result); + + // Allocate memory for one user (there should be at least one, namely us!) + ALLOC_INIT_ZVAL(z_sub_result); + array_init(z_sub_result); + + // Pointers for parsing + char *p = resp, *lpos = resp, *kpos = NULL, *vpos = NULL, *p2, *key, *value; + + // Key length, done flag + int klen, done = 0, is_numeric; + + // While we've got more to parse + while(!done) { + // What character are we on + switch(*p) { + /* We're done */ + case '\0': + done = 1; + break; + /* \n, ' ' mean we can pull a k/v pair */ + case '\n': + case ' ': + // Grab our value + vpos = lpos; + + // There is some communication error or Redis bug if we don't + // have a key and value, but check anyway. + if(kpos && vpos) { + // Allocate, copy in our key + key = emalloc(klen + 1); + strncpy(key, kpos, klen); + key[klen] = 0; + + // Allocate, copy in our value + value = emalloc(p-lpos+1); + strncpy(value,lpos,p-lpos+1); + value[p-lpos]=0; + + // Treat numbers as numbers, strings as strings + is_numeric = 1; + for(p2 = value; *p; ++p) { + if(*p < '0' || *p > '9') { + is_numeric = 0; + break; + } + } + + // Add as a long or string, depending + if(is_numeric == 1) { + add_assoc_long(z_sub_result, key, atol(value)); + efree(value); + } else { + add_assoc_string(z_sub_result, key, value, 0); + } + + // If we hit a '\n', then we can add this user to our list + if(*p == '\n') { + // Add our user + add_next_index_zval(z_result, z_sub_result); + + // If we have another user, make another one + if(*(p+1) != '\0') { + ALLOC_INIT_ZVAL(z_sub_result); + array_init(z_sub_result); + } + } + + // Free our key + efree(key); + } else { + // Something is wrong + efree(resp); + return -1; + } + + // Move forward + lpos = p + 1; + + break; + /* We can pull the key and null terminate at our sep */ + case '=': + // Key, key length + kpos = lpos; + klen = p - lpos; + + // Move forward + lpos = p + 1; + + break; + } + + // Increment + p++; + } + + // Free our respoonse + efree(resp); + + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_result); + } else { + RETVAL_ZVAL(z_result, 0, 1); + } +} + PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) { char *response; diff --git a/library.h b/library.h index d730b96b2e..93fa8e8990 100644 --- a/library.h +++ b/library.h @@ -59,6 +59,7 @@ PHPAPI int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret PHPAPI int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC); PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); +PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); #if ZEND_MODULE_API_NO >= 20100000 #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \ diff --git a/php_redis.h b/php_redis.h index 24f002150e..e6231d3be5 100644 --- a/php_redis.h +++ b/php_redis.h @@ -181,6 +181,8 @@ PHP_METHOD(Redis, setOption); PHP_METHOD(Redis, config); +PHP_METHOD(Redis, client); + #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) #else diff --git a/redis.c b/redis.c index 6af6f64fb4..6462477edd 100644 --- a/redis.c +++ b/redis.c @@ -227,6 +227,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) /* options */ PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC) @@ -6306,5 +6307,56 @@ PHP_METHOD(Redis, time) { REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw); } -/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ +/* + * $redis->client('list'); + * $redis->client('kill', ); + * $redis->client('setname', ); + * $redis->client('getname'); + */ +PHP_METHOD(Redis, client) { + zval *object; + RedisSock *redis_sock; + char *cmd, *opt=NULL, *arg=NULL; + int cmd_len, opt_len, arg_len; + + // Parse our method parameters + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s", + &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE) + { + RETURN_FALSE; + } + + // Grab our socket + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } + + // Build our CLIENT command + if(ZEND_NUM_ARGS() == 2) { + cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len, + arg, arg_len); + } else { + cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len); + } + + // Handle CLIENT LIST specifically + int is_list = !strncasecmp(opt, "list", 4); + + // Execute our queue command + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + + // We handle CLIENT LIST with a custom response function + if(!strncasecmp(opt, "list", 4)) { + IF_ATOMIC() { + redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL); + } + REDIS_PROCESS_RESPONSE(redis_client_list_reply); + } else { + IF_ATOMIC() { + redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL); + } + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); + } +} +/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 65db71880d..8f28f93b3d 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -1650,6 +1650,32 @@ public function testPersist() { $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 testinfo() { $info = $this->redis->info(); From b6721c1292c0203741bee6fef0ab07e6225283c0 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Sun, 17 Feb 2013 13:28:00 +0000 Subject: [PATCH 0018/1986] Document and test custom key distribution function Solves issue #299 --- arrays.markdown | 5 +++++ tests/array-tests.php | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/arrays.markdown b/arrays.markdown index 4c1f899d34..16c9601c6c 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -71,6 +71,11 @@ By default and in order to be compatible with other libraries, phpredis will try For instance, the keys “{user:1}:name” and “{user:1}:email” will be stored on the same server as only “user:1” will be hashed. You can provide a custom function name in your redis array with the "function" option; this function will be called every time a key needs to be hashed. It should take a string and return a string. +## Custom key distribution function +In order to control the distribution of keys by hand, you can provide a custom function or closure that returns the server number, which is the index in the array of servers that you created the RedisArray object with. + +For instance, instanciate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. + ## Migrating keys When a node is added or removed from a ring, RedisArray instances must be instanciated with a “previous” list of nodes. A single call to `$ra->_rehash()` causes all the keys to be redistributed according to the new list of nodes. Passing a callback function to `_rehash()` makes it possible to track the progress of that operation: the function is called with a node name and a number of keys that will be examined, e.g. `_rehash(function ($host, $count){ ... });`. diff --git a/tests/array-tests.php b/tests/array-tests.php index 2f72155abe..ff8b1a3384 100644 --- a/tests/array-tests.php +++ b/tests/array-tests.php @@ -497,6 +497,47 @@ public function testDiscard() { } +// Test custom distribution function +class Redis_Distributor_Test extends TestSuite { + + public $ra = NULL; + + public function setUp() { + + global $newRing, $oldRing, $useIndex; + // create array + $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'distributor' => array($this, 'distribute'))); + } + + public function testInit() { + $this->ra->set('{uk}test', 'joe'); + $this->ra->set('{us}test', 'bob'); + } + + public function distribute($key) { + $matches = array(); + if (preg_match('/{([^}]+)}.*/', $key, $matches) == 1) { + $countries = array('uk' => 0, 'us' => 1); + if (array_key_exists($matches[1], $countries)) { + return $countries[$matches[1]]; + } + } + return 2; // default server + } + + public function testDistribution() { + $ukServer = $this->ra->_target('{uk}test'); + $usServer = $this->ra->_target('{us}test'); + $deServer = $this->ra->_target('{de}test'); + $defaultServer = $this->ra->_target('unknown'); + + $nodes = $this->ra->_hosts(); + $this->assertTrue($ukServer === $nodes[0]); + $this->assertTrue($usServer === $nodes[1]); + $this->assertTrue($deServer === $nodes[2]); + $this->assertTrue($defaultServer === $nodes[2]); + } +} function run_tests($className) { // reset rings @@ -520,6 +561,7 @@ function run_tests($className) { run_tests('Redis_Rehashing_Test'); run_tests('Redis_Auto_Rehashing_Test'); run_tests('Redis_Multi_Exec_Test'); + run_tests('Redis_Distributor_Test'); } ?> From 507ba7c51399f713b59fdf00f2f0c20c9e470596 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Sun, 17 Feb 2013 13:37:22 +0000 Subject: [PATCH 0019/1986] Document unix sockets with the session handler --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index a6540533c9..d292d02bfb 100644 --- a/README.markdown +++ b/README.markdown @@ -87,6 +87,7 @@ session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeou Sessions have a lifetime expressed in seconds and stored in the INI variable "session.gc_maxlifetime". You can change it with [`ini_set()`](http://php.net/ini_set). The session handler requires a version of Redis with the `SETEX` command (at least 2.0). +phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0`. ## Distributed Redis Array From df060ca2cecec8e8d7e35a46e1ba0ce5f5a5fb22 Mon Sep 17 00:00:00 2001 From: Nicolas Favre-Felix Date: Sun, 17 Feb 2013 15:30:20 +0000 Subject: [PATCH 0020/1986] Link to instructions on how to build on Windows --- README.markdown | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.markdown b/README.markdown index d292d02bfb..bd5a067b75 100644 --- a/README.markdown +++ b/README.markdown @@ -11,6 +11,7 @@ You can send comments, patches, questions [here on github](https://github.com/ni 1. [Installing/Configuring](#installingconfiguring) * [Installation](#installation) * [Installation on OSX](#installation-on-osx) + * [Building on Windows](#building-on-windows) * [PHP Session handler](#php-session-handler) * [Distributed Redis Array](#distributed-redis-array) 1. [Classes and methods](#classes-and-methods) @@ -90,6 +91,11 @@ The session handler requires a version of Redis with the `SETEX` command (at lea phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0`. +## Building on Windows + +See [instructions from @char101](https://github.com/nicolasff/phpredis/issues/213#issuecomment-11361242) on how to build phpredis on Windows. + + ## Distributed Redis Array See [dedicated page](https://github.com/nicolasff/phpredis/blob/master/arrays.markdown#readme). From 90bdf9e8fdc1b8e9e22d3bf0e56ef22ad6605f1a Mon Sep 17 00:00:00 2001 From: Nicolas Favre-Felix Date: Sun, 17 Feb 2013 16:17:25 +0000 Subject: [PATCH 0021/1986] Fix test for TTL return value Resolves issue #302 --- tests/TestRedis.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 65db71880d..2130db13d7 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -2368,8 +2368,9 @@ protected function sequence($mode) { ->expireAt('key', '0000') ->exec(); $this->assertTrue(is_array($ret)); - $i = 0; - $this->assertTrue($ret[$i++] == -1); + $i = 0; + $ttl = $ret[$i++]; + $this->assertTrue($ttl === -1 || $ttl === -2); $this->assertTrue($ret[$i++] === array('val1', 'valX', FALSE)); // mget $this->assertTrue($ret[$i++] === TRUE); // mset $this->assertTrue($ret[$i++] === TRUE); // set From 8b4ee3dda2d02d1256a245689c7c402023903358 Mon Sep 17 00:00:00 2001 From: Nicolas Favre-Felix Date: Sun, 17 Feb 2013 16:20:44 +0000 Subject: [PATCH 0022/1986] Fix broken test with unexpected result for delete --- tests/TestRedis.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 2130db13d7..f122f86e9c 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -2727,7 +2727,7 @@ protected function sequence($mode) { $i = 0; $this->assertTrue(is_array($ret)); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 3); $i++; // deleting at most 3 keys + $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleting at most 5 keys $this->assertTrue($ret[$i++] === 1); $this->assertTrue($ret[$i++] === 1); $this->assertTrue($ret[$i++] === 1); From 6d3c9990a5314c3cc7203df3da8d855e8bc152d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Tue, 29 Jan 2013 11:47:36 +0200 Subject: [PATCH 0023/1986] Merged changes from @mobli New select DB command to RedisArray - Added retry delay on reconnect Added the possibility to delay each reconnection attempt, including a random factor to prevent several or many concurrent connections from trying to reconnect at the same time. Added the select command to RedisArray to select a DB on every connections in one instruction. Also, fixed a compiler warning: redis_array_impl.c:1115:15: warning: incompatible pointer types assigning to 'zval **' (aka 'struct _zval_struct **') from 'zval **(*)[2]' [-Wincompatible-pointer-types] Conflicts: common.h --- common.h | 1 + library.c | 14 +++++++++--- library.h | 2 +- redis.c | 15 ++++++++---- redis_array.c | 57 +++++++++++++++++++++++++++++++++++++++++++++- redis_array.h | 1 + redis_array_impl.c | 33 +++++++++++++++++++++------ redis_array_impl.h | 4 ++-- redis_session.c | 9 +++----- 9 files changed, 112 insertions(+), 24 deletions(-) diff --git a/common.h b/common.h index 1b9e97f591..6f000fa1ef 100644 --- a/common.h +++ b/common.h @@ -159,6 +159,7 @@ typedef struct { short port; double timeout; double read_timeout; + long retry_interval; int failed; int status; int persistent; diff --git a/library.c b/library.c index 1b6da39889..1d3c56f15b 100644 --- a/library.c +++ b/library.c @@ -38,8 +38,9 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) int eof; int count = 0; - if (!redis_sock->stream) + if (!redis_sock->stream) { return -1; + } eof = php_stream_eof(redis_sock->stream); for (; eof; count++) { @@ -60,6 +61,12 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) redis_sock->mode = ATOMIC; redis_sock->watching = 0; } + // Wait for a while before trying to reconnect + if (redis_sock->retry_interval) { + // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time + long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval)); + usleep(retry_interval); + } redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ if(redis_sock->stream) { /* check for EOF again. */ eof = php_stream_eof(redis_sock->stream); @@ -962,7 +969,8 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s * redis_sock_create */ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, - double timeout, int persistent, char *persistent_id) + double timeout, int persistent, char *persistent_id, + long retry_interval) { RedisSock *redis_sock; @@ -972,7 +980,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; redis_sock->dbNumber = 0; - + redis_sock->retry_interval = retry_interval * 1000; redis_sock->persistent = persistent; if(persistent_id) { diff --git a/library.h b/library.h index 93fa8e8990..0ee1f6febb 100644 --- a/library.h +++ b/library.h @@ -20,7 +20,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id); +PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval); PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index bf61593070..488e1e0a80 100644 --- a/redis.c +++ b/redis.c @@ -522,7 +522,7 @@ PHP_METHOD(Redis,__destruct) { } } -/* {{{ proto boolean Redis::connect(string host, int port [, double timeout]) +/* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]]) */ PHP_METHOD(Redis, connect) { @@ -558,6 +558,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { int host_len, id; char *host = NULL; long port = -1; + long retry_interval = 0; char *persistent_id = NULL; int persistent_id_len = -1; @@ -570,9 +571,10 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { persistent = 0; #endif - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|lds", + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl", &object, redis_ce, &host, &host_len, &port, - &timeout, &persistent_id, &persistent_id_len) == FAILURE) { + &timeout, &persistent_id, &persistent_id_len, + &retry_interval) == FAILURE) { return FAILURE; } @@ -581,6 +583,11 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { return FAILURE; } + if (retry_interval < 0L || retry_interval > INT_MAX) { + zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC); + return FAILURE; + } + if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */ port = 6379; } @@ -597,7 +604,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zend_clear_exception(TSRMLS_C); /* clear exception triggered by non-existent socket during connect(). */ } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval); if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { redis_free_socket(redis_sock); diff --git a/redis_array.c b/redis_array.c index d0460f1021..be95c50d5a 100644 --- a/redis_array.c +++ b/redis_array.c @@ -51,6 +51,7 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, _rehash, NULL, ZEND_ACC_PUBLIC) /* special implementation for a few functions */ + PHP_ME(RedisArray, select, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, info, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mget, NULL, ZEND_ACC_PUBLIC) @@ -192,6 +193,7 @@ PHP_METHOD(RedisArray, __construct) RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; HashTable *hPrev = NULL, *hOpts = NULL; + long l_retry_interval = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { RETURN_FALSE; @@ -232,6 +234,19 @@ PHP_METHOD(RedisArray, __construct) if(FAILURE != zend_hash_find(hOpts, "autorehash", sizeof("autorehash"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { b_autorehash = Z_BVAL_PP(zpData); } + + /* extract retry_interval option. */ + zval **z_retry_interval_pp; + if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) { + if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) { + if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) { + l_retry_interval = Z_LVAL_PP(z_retry_interval_pp); + } + else { + l_retry_interval = atol(Z_STRVAL_PP(z_retry_interval_pp)); + } + } + } } /* extract either name of list of hosts from z0 */ @@ -241,7 +256,7 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); break; default: @@ -688,6 +703,46 @@ PHP_METHOD(RedisArray, setOption) efree(z_args[0]); efree(z_args[1]); } + +PHP_METHOD(RedisArray, select) +{ + zval *object, z_fun, *z_tmp, *z_args[2]; + int i; + RedisArray *ra; + long opt; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, redis_array_ce, &opt) == FAILURE) { + RETURN_FALSE; + } + + if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + RETURN_FALSE; + } + + /* prepare call */ + ZVAL_STRING(&z_fun, "select", 0); + + /* copy args */ + MAKE_STD_ZVAL(z_args[0]); + ZVAL_LONG(z_args[0], opt); + + array_init(return_value); + for(i = 0; i < ra->count; ++i) { + + MAKE_STD_ZVAL(z_tmp); + + /* Call each node in turn */ + call_user_function(&redis_ce->function_table, &ra->redis[i], + &z_fun, z_tmp, 1, z_args TSRMLS_CC); + + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } + + /* cleanup */ + efree(z_args[0]); +} + #define HANDLE_MULTI_EXEC(cmd) do {\ if (redis_array_get(getThis(), &ra TSRMLS_CC) >= 0 && ra->z_multi_exec) {\ int i, num_varargs;\ diff --git a/redis_array.h b/redis_array.h index bc7fdd8842..b2c7d86a50 100644 --- a/redis_array.h +++ b/redis_array.h @@ -15,6 +15,7 @@ PHP_METHOD(RedisArray, _function); PHP_METHOD(RedisArray, _distributor); PHP_METHOD(RedisArray, _rehash); +PHP_METHOD(RedisArray, select); PHP_METHOD(RedisArray, info); PHP_METHOD(RedisArray, ping); PHP_METHOD(RedisArray, mget); diff --git a/redis_array_impl.c b/redis_array_impl.c index d5370c826e..c38e2fe661 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -29,7 +29,7 @@ extern int le_redis_sock; extern zend_class_entry *redis_ce; RedisArray* -ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC) +ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC) { int i, host_len, id; int count = zend_hash_num_elements(hosts); @@ -67,7 +67,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC) call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC); /* create socket */ - redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL); /* TODO: persistence? */ + redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL, retry_interval); /* TODO: persistence? */ /* connect */ redis_sock_server_open(redis_sock, 1 TSRMLS_CC); @@ -158,9 +158,11 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval *z_params_funs, **z_data_pp, *z_fun = NULL, *z_dist = NULL; zval *z_params_index; zval *z_params_autorehash; + zval *z_params_retry_interval; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; + long l_retry_interval = 0; HashTable *hHosts = NULL, *hPrev = NULL; /* find entry */ @@ -223,8 +225,23 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } + /* find retry interval option */ + MAKE_STD_ZVAL(z_params_retry_interval); + array_init(z_params_retry_interval); + sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.retryinterval")), z_params_retry_interval TSRMLS_CC); + if (zend_hash_find(Z_ARRVAL_P(z_params_retry_interval), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if (Z_TYPE_PP(z_data_pp) == IS_LONG || Z_TYPE_PP(z_data_pp) == IS_STRING) { + if (Z_TYPE_PP(z_data_pp) == IS_LONG) { + l_retry_interval = Z_LVAL_PP(z_data_pp); + } + else { + l_retry_interval = atol(Z_STRVAL_PP(z_data_pp)); + } + } + } + /* create RedisArray object */ - ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index TSRMLS_CC); + ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); ra->auto_rehash = b_autorehash; /* cleanup */ @@ -238,12 +255,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { efree(z_params_index); zval_dtor(z_params_autorehash); efree(z_params_autorehash); + zval_dtor(z_params_retry_interval); + efree(z_params_retry_interval); return ra; } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC) { int count = zend_hash_num_elements(hosts); @@ -261,10 +280,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* init array data structures */ ra_init_function_table(ra); - if(NULL == ra_load_hosts(ra, hosts TSRMLS_CC)) { + if(NULL == ra_load_hosts(ra, hosts, retry_interval TSRMLS_CC)) { return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval TSRMLS_CC) : NULL; /* copy function if provided */ if(z_fun) { @@ -1112,7 +1131,7 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z zval *z_host, *z_count; z_cb->retval_ptr_ptr = &z_ret; - z_cb->params = &z_args; + z_cb->params = (struct _zval_struct ***)&z_args; z_cb->param_count = 2; z_cb->no_separation = 0; diff --git a/redis_array_impl.h b/redis_array_impl.h index 0e00258c3b..8dd5201bc8 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -5,9 +5,9 @@ #include "common.h" #include "redis_array.h" -RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC); +RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); diff --git a/redis_session.c b/redis_session.c index 95d9053302..009bac51b2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -206,6 +206,7 @@ PS_OPEN_FUNC(redis) int persistent = 0; int database = -1; char *prefix = NULL, *auth = NULL, *persistent_id = NULL; + long retry_interval = 0; /* translate unix: into file: */ if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) { @@ -240,7 +241,6 @@ PS_OPEN_FUNC(redis) convert_to_long_ex(param); weight = Z_LVAL_PP(param); } - if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { timeout = atof(Z_STRVAL_PP(param)); } @@ -260,13 +260,10 @@ PS_OPEN_FUNC(redis) convert_to_long_ex(param); database = Z_LVAL_PP(param); } - - /* // not supported yet if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { convert_to_long_ex(param); retry_interval = Z_LVAL_PP(param); } - */ zval_ptr_dtor(¶ms); } @@ -280,9 +277,9 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if(url->host) { - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval); } else { /* unix */ - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval); } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); From acd324545685e3368bd8d9c626e7e019d4cdeb79 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Mon, 25 Feb 2013 00:38:47 +0200 Subject: [PATCH 0024/1986] Added lazy_connect option to RedisArray Added an option to let each RedisArray connection connect lazily to their respective server. This is useful then working with a redis cluster composed of many shards which are not necessarily in use all at once. --- README.markdown | 4 ++++ arrays.markdown | 18 +++++++++++++++--- common.h | 1 + library.c | 4 +++- library.h | 2 +- redis.c | 9 ++++++++- redis_array.c | 10 ++++++++-- redis_array_impl.c | 33 +++++++++++++++++++++++++-------- redis_array_impl.h | 4 ++-- redis_session.c | 4 ++-- 10 files changed, 69 insertions(+), 20 deletions(-) diff --git a/README.markdown b/README.markdown index bd5a067b75..b0ee36f717 100644 --- a/README.markdown +++ b/README.markdown @@ -164,6 +164,8 @@ _**Description**_: Connects to a Redis instance. *host*: string. can be a host, or the path to a unix domain socket *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) +*reserved*: should be NULL if retry_interval is specified +*retry_interval*: int, value in milliseconds (optional) ##### *Return value* @@ -176,6 +178,7 @@ $redis->connect('127.0.0.1', 6379); $redis->connect('127.0.0.1'); // port 6379 by default $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout. $redis->connect('/tmp/redis.sock'); // unix domain socket. +$redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts. ~~~ ### pconnect, popen @@ -198,6 +201,7 @@ persistent equivalents. *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) *persistent_id*: string. identity for the requested persistent connection +*retry_interval*: int, value in milliseconds (optional) ##### *Return value* diff --git a/arrays.markdown b/arrays.markdown index 16c9601c6c..de326e9ce8 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -17,7 +17,7 @@ There are several ways of creating Redis arrays; they can be pre-defined in red #### Declaring a new array with a list of nodes
-$ra = new RedisArray(array("host1", "host2:63792, "host2:6380"));
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"));
 
@@ -26,7 +26,7 @@ $ra = new RedisArray(array("host1", "host2:63792, "host2:6380")); function extract_key_part($k) { return substr($k, 0, 3); // hash only on first 3 characters. } -$ra = new RedisArray(array("host1", "host2:63792, "host2:6380"), array("function" => "extract_key_part")); +$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("function" => "extract_key_part")); #### Defining a "previous" array when nodes are added or removed. @@ -34,7 +34,19 @@ When a new node is added to an array, phpredis needs to know about it. The old l
 // adding host3 to a ring containing host1 and host2. Read commands will look in the previous ring if the data is not found in the main ring.
-$ra = new RedisArray(array('host1', 'host2', 'host3'), array('previous' => array('host1', 'host2')));
+$ra = new RedisArray(array("host1", "host2", "host3"), array("previous" => array("host1", "host2")));
+
+ +#### Specifying the "retry_interval" parameter +The retry_interval is used to specify a delay in milliseconds between reconnection attempts in case the client loses connection with a server +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_timeout" => 100)));
+
+ +#### Specifying the "lazy_connect" parameter +This option is useful when a cluster has many shards but not of them are necessarily used at one time. +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("lazy_connect" => true)));
 
#### Defining arrays in Redis.ini diff --git a/common.h b/common.h index b6e2fb3020..1f8d5674ce 100644 --- a/common.h +++ b/common.h @@ -181,6 +181,7 @@ typedef struct { char *err; int err_len; + zend_bool lazy_connect; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index 13bc2b5b9e..60dbd54487 100644 --- a/library.c +++ b/library.c @@ -848,7 +848,8 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s */ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, - long retry_interval) + long retry_interval, + zend_bool lazy_connect) { RedisSock *redis_sock; @@ -860,6 +861,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por redis_sock->dbNumber = 0; redis_sock->retry_interval = retry_interval * 1000; redis_sock->persistent = persistent; + redis_sock->lazy_connect = lazy_connect; if(persistent_id) { size_t persistent_id_len = strlen(persistent_id); diff --git a/library.h b/library.h index 711f0b5c90..79dc16646f 100644 --- a/library.h +++ b/library.h @@ -20,7 +20,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval); +PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index 4e514e878a..1a1bb508fc 100644 --- a/redis.c +++ b/redis.c @@ -377,6 +377,13 @@ PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_thr } return -1; } + if ((*redis_sock)->lazy_connect) + { + (*redis_sock)->lazy_connect = 0; + if (redis_sock_server_open(*redis_sock, 1 TSRMLS_CC) < 0) { + return -1; + } + } return Z_LVAL_PP(socket); } @@ -603,7 +610,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zend_clear_exception(TSRMLS_C); /* clear exception triggered by non-existent socket during connect(). */ } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval); + redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0); if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { redis_free_socket(redis_sock); diff --git a/redis_array.c b/redis_array.c index be95c50d5a..fb507f5560 100644 --- a/redis_array.c +++ b/redis_array.c @@ -194,6 +194,7 @@ PHP_METHOD(RedisArray, __construct) zend_bool b_index = 0, b_autorehash = 0; HashTable *hPrev = NULL, *hOpts = NULL; long l_retry_interval = 0; + zend_bool b_lazy_connect = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { RETURN_FALSE; @@ -236,7 +237,7 @@ PHP_METHOD(RedisArray, __construct) } /* extract retry_interval option. */ - zval **z_retry_interval_pp; + zval **z_retry_interval_pp; if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) { if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) { if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) { @@ -247,6 +248,11 @@ PHP_METHOD(RedisArray, __construct) } } } + + /* extract lazy connect option. */ + if(FAILURE != zend_hash_find(hOpts, "lazy_connect", sizeof("lazy_connect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { + b_lazy_connect = Z_BVAL_PP(zpData); + } } /* extract either name of list of hosts from z0 */ @@ -256,7 +262,7 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval, b_lazy_connect TSRMLS_CC); break; default: diff --git a/redis_array_impl.c b/redis_array_impl.c index c38e2fe661..f804f4759a 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -29,7 +29,7 @@ extern int le_redis_sock; extern zend_class_entry *redis_ce; RedisArray* -ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC) +ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool lazy_connect TSRMLS_DC) { int i, host_len, id; int count = zend_hash_num_elements(hosts); @@ -67,10 +67,13 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC) call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC); /* create socket */ - redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL, retry_interval); /* TODO: persistence? */ + redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL, retry_interval, lazy_connect); /* TODO: persistence? */ - /* connect */ - redis_sock_server_open(redis_sock, 1 TSRMLS_CC); + if (!lazy_connect) + { + /* connect */ + redis_sock_server_open(redis_sock, 1 TSRMLS_CC); + } /* attach */ #if PHP_VERSION_ID >= 50400 @@ -159,10 +162,12 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval *z_params_index; zval *z_params_autorehash; zval *z_params_retry_interval; + zval *z_params_lazy_connect; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0; long l_retry_interval = 0; + zend_bool b_lazy_connect = 0; HashTable *hHosts = NULL, *hPrev = NULL; /* find entry */ @@ -240,8 +245,18 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } + /* find retry interval option */ + MAKE_STD_ZVAL(z_params_lazy_connect); + array_init(z_params_lazy_connect); + sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.lazyconnect")), z_params_lazy_connect TSRMLS_CC); + if (zend_hash_find(Z_ARRVAL_P(z_params_lazy_connect), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { + b_lazy_connect = 1; + } + } + /* create RedisArray object */ - ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC); + ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval, b_lazy_connect TSRMLS_CC); ra->auto_rehash = b_autorehash; /* cleanup */ @@ -257,12 +272,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { efree(z_params_autorehash); zval_dtor(z_params_retry_interval); efree(z_params_retry_interval); + zval_dtor(z_params_lazy_connect); + efree(z_params_lazy_connect); return ra; } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval, zend_bool lazy_connect TSRMLS_DC) { int count = zend_hash_num_elements(hosts); @@ -280,10 +297,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* init array data structures */ ra_init_function_table(ra); - if(NULL == ra_load_hosts(ra, hosts, retry_interval TSRMLS_CC)) { + if(NULL == ra_load_hosts(ra, hosts, retry_interval, lazy_connect TSRMLS_CC)) { return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval, lazy_connect TSRMLS_CC) : NULL; /* copy function if provided */ if(z_fun) { diff --git a/redis_array_impl.h b/redis_array_impl.h index 8dd5201bc8..625314059c 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -5,9 +5,9 @@ #include "common.h" #include "redis_array.h" -RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC); +RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval, zend_bool lazy_connect TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); diff --git a/redis_session.c b/redis_session.c index 009bac51b2..5261e68dce 100644 --- a/redis_session.c +++ b/redis_session.c @@ -277,9 +277,9 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if(url->host) { - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval, 0); } else { /* unix */ - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval, 0); } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); From 1f3358b62cd0bc9f7d30c8c2b33479677ddec325 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Mon, 25 Feb 2013 01:00:40 +0200 Subject: [PATCH 0025/1986] Added lazy_connect option to RedisArray Added an option to let each RedisArray connection connect lazily to their respective server. This is useful then working with a redis cluster composed of many shards which are not necessarily in use all at once. --- common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/common.h b/common.h index 8d0e072071..496e02f472 100644 --- a/common.h +++ b/common.h @@ -158,7 +158,6 @@ typedef struct { char *host; short port; double timeout; - long retry_interval; double read_timeout; long retry_interval; int failed; From e457d9813d7d76e3547142616b1f5b60e636166d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 24 Feb 2013 16:32:40 -0800 Subject: [PATCH 0026/1986] Don't return a value from a void function --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 1d3c56f15b..8eaf12d5d0 100644 --- a/library.c +++ b/library.c @@ -704,7 +704,7 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red } else { // Something is wrong efree(resp); - return -1; + RETURN_FALSE; } // Move forward From 9954974778b60123ef27e2af9270c2478f9c3743 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 24 Feb 2013 16:45:11 -0800 Subject: [PATCH 0027/1986] Return a proper value redis_sock_read_multibulk_reply_zipped_with_flag should always return a value. We merged in a change that handled a certain error condition that was using RETURN_FALSE (and therefore not returning a value) Simply changed this to RETVAL_FALSE and return -1; --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 944dcf5701..f8add0af52 100644 --- a/library.c +++ b/library.c @@ -720,7 +720,7 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); } else { - RETURN_FALSE; + RETVAL_FALSE; } return -1; } From 703476ce01490157ef6cf0ed0ccde06714d9e81f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 24 Feb 2013 17:51:56 -0800 Subject: [PATCH 0028/1986] Fix each place we're erroring out but return a value as the methods are int returns Resolves issues pertaining to #303 --- library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index f8add0af52..5018a36546 100644 --- a/library.c +++ b/library.c @@ -1093,7 +1093,7 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); } else { - RETURN_FALSE; + RETVAL_FALSE; } return -1; } @@ -1140,7 +1140,7 @@ PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, Red IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); } else { - RETURN_FALSE; + RETVAL_FALSE; } return -1; } @@ -1219,7 +1219,7 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); } else { - RETURN_FALSE; + RETVAL_FALSE; } return -1; } From 5582166f770a30155c28b190303b869bf5b926b7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 25 Feb 2013 14:08:07 -0800 Subject: [PATCH 0029/1986] Fix documentation for CLIENT command Relates to issue #300 --- README.markdown | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index 102bbff45f..77e4c622c8 100644 --- a/README.markdown +++ b/README.markdown @@ -2893,10 +2893,10 @@ $redis->script('exists', $script1, [$script2, $script3, ...]); _**Description**_: Issue the CLIENT command with various arguments. The Redis CLIENT command can be used in four ways. -1. CLIENT LIST -1. CLIENT GETNAME -1. CLIENT SETNAME [name] -1. CLIENT KILL [ip:port] +* CLIENT LIST +* CLIENT GETNAME +* CLIENT SETNAME [name] +* CLIENT KILL [ip:port] ##### *Usage* ~~~ $redis->client('list'); // Get a list of clients @@ -2907,10 +2907,10 @@ $redis->client('kill', ); // Kill the process at ip:port ##### *Return value* This will vary depending on which client command was executed. -CLIENT LIST will return an array of arrys with client information. -CLIENT GETNAME will return the client name or false if none has been set -CLIENT SETNAME will return true if it can be set and false if not -CLIENT KILL will return true if the client can be killed, and false if not +* CLIENT LIST will return an array of arrays with client information. +* CLIENT GETNAME will return the client name or false if none has been set +* CLIENT SETNAME will return true if it can be set and false if not +* CLIENT KILL will return true if the client can be killed, and false if not Note: phpredis will attempt to reconnect so you can actually kill your own connection but may not notice losing it! From b096f5771bebcc3110eb2a5e19b1d0093c751283 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 25 Feb 2013 14:10:53 -0800 Subject: [PATCH 0030/1986] Formatting fix --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index 77e4c622c8..1f68c2db6d 100644 --- a/README.markdown +++ b/README.markdown @@ -2904,6 +2904,7 @@ $redis->client('getname'); // Get the name of the current connection $redis->client('setname', 'somename'); // Set the name of the current connection $redis->client('kill', ); // Kill the process at ip:port ~~~ + ##### *Return value* This will vary depending on which client command was executed. From 7deb21fe2d33b5f8d8e7985e5bcf6b9fc337a4a3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 25 Feb 2013 14:12:20 -0800 Subject: [PATCH 0031/1986] More formatting --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index 1f68c2db6d..3c2304cf6b 100644 --- a/README.markdown +++ b/README.markdown @@ -2897,6 +2897,7 @@ The Redis CLIENT command can be used in four ways. * CLIENT GETNAME * CLIENT SETNAME [name] * CLIENT KILL [ip:port] + ##### *Usage* ~~~ $redis->client('list'); // Get a list of clients From a4627bb94bdf84fec158c6a8b628f2dbe70399d8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 15 Mar 2013 16:34:05 -0700 Subject: [PATCH 0032/1986] Fixes a memory leak, and possible undefined behavior if we have a serialization failure and just return the raw contents of a key. Addresses issue #315 --- library.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index 5018a36546..856a805583 100644 --- a/library.c +++ b/library.c @@ -808,7 +808,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis } } else { if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { - RETURN_STRINGL(response, response_len, 0); + RETURN_STRINGL(response, response_len, 0); } else { efree(response); } @@ -1367,7 +1367,7 @@ PHPAPI int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) { php_unserialize_data_t var_hash; - int ret; + int ret, rv_free = 0; switch(redis_sock->serializer) { case REDIS_SERIALIZER_NONE: @@ -1376,6 +1376,7 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **re case REDIS_SERIALIZER_PHP: if(!*return_value) { MAKE_STD_ZVAL(*return_value); + rv_free = 1; } #if ZEND_MODULE_API_NO >= 20100000 PHP_VAR_UNSERIALIZE_INIT(var_hash); @@ -1384,7 +1385,7 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **re #endif if(!php_var_unserialize(return_value, (const unsigned char**)&val, (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { - efree(*return_value); + if(rv_free==1) efree(*return_value); ret = 0; } else { ret = 1; From c39ecd52f30a573d476e44baeccebd6ba429cafb Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 16 Mar 2013 11:48:51 -0700 Subject: [PATCH 0033/1986] Alias eval and evalsha --- redis.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis.c b/redis.c index a655e2cb4d..bf92513edb 100644 --- a/redis.c +++ b/redis.c @@ -263,6 +263,9 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, srem, sRemove, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zrevrange, zReverseRange, NULL, ZEND_ACC_PUBLIC) + + PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; From 8596eac45e1f6defc4036fac9375487e8abc63e7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 20 Mar 2013 09:00:57 -0700 Subject: [PATCH 0034/1986] Use another NULL for zend_is_callable_ex Given that PHP 5.2.X and PHP 5.3.X use a different signature for zend_is_callable_ex, and we're not using the error return (PHP 5.3.X) anyway, just pass in NULL. Addresses issue #318 --- redis_array_impl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index d5370c826e..0c361d74ac 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -288,12 +288,12 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev char * ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS_DC) { - char *error = NULL, *out; + char *out; zval z_ret; zval *z_argv0; /* check that we can call the extractor function */ - if(!zend_is_callable_ex(ra->z_fun, NULL, 0, NULL, NULL, NULL, &error TSRMLS_CC)) { + if(!zend_is_callable_ex(ra->z_fun, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function"); return NULL; } @@ -349,12 +349,11 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS zend_bool ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRMLS_DC) { - char *error = NULL; zval z_ret; zval *z_argv0; /* check that we can call the extractor function */ - if(!zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, &error TSRMLS_CC)) { + if(!zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); return 0; } From ef792320e7a5acbb7361ab4dfcb9624c115b7e39 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 27 Mar 2013 10:21:18 -0700 Subject: [PATCH 0035/1986] Introspection methods This commit adds methods to get information about the state of our phpredis object, such as what host/port we are connected to, our timeout, etc... The following methods have been added: getHost() getPort() getDBNum() getTimeout() getReadTimeout() isConnected() getPersistentID() getAuth() In addition, there is a small memory leak fix when a persistent id was specifically passed to connect() (it wasn't beeing freed). Addresses issue #320 --- README.markdown | 86 +++++++++++++++++++++++++ common.h | 1 + library.c | 6 ++ php_redis.h | 9 +++ redis.c | 152 ++++++++++++++++++++++++++++++++++++++++++++ tests/TestRedis.php | 7 ++ 6 files changed, 261 insertions(+) diff --git a/README.markdown b/README.markdown index bd5a067b75..bb403d2f5e 100644 --- a/README.markdown +++ b/README.markdown @@ -26,6 +26,7 @@ You can send comments, patches, questions [here on github](https://github.com/ni * [Pub/sub](#pubsub) * [Transactions](#transactions) * [Scripting](#scripting) + * [Introspection](#introspection) ----- @@ -2958,3 +2959,88 @@ serializing values, and you return something from redis in a LUA script that is $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); $redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2,3) ~~~ + + + +## Introspection + +### IsConnected +----- +_**Description**_: A method to determine if a phpredis object thinks it's connected to a server + +##### *Parameters* +None + +##### *Return value* +*Boolean* Returns TRUE if phpredis thinks it's connected and FALSE if not + +### GetHost +----- +_**Description**_: Retreive our host or unix socket that we're connected to + +##### *Parameters* +None + +##### *Return value* +*Mixed* The host or unix socket we're connected to or FALSE if we're not connected + + +### GetPort +----- +_**Description**_: Get the port we're connected to + +##### *Parameters* +None + +##### *Return value* +*Mixed* Returns the port we're connected to or FALSE if we're not connected + +### getDBNum +----- +_**Description**_: Get the database number phpredis is pointed to + +##### *Parameters* +None + +##### *Return value* +*Mixed* Returns the database number (LONG) phpredis thinks it's pointing to or FALSE if we're not connected + +### GetTimeout +----- +_**Description**_: Get the (write) timeout in use for phpreids + +##### *Parameters* +None + +##### *Return value* +*Mixed* The timeout (DOUBLE) specified in our connect call or FALSE if we're not connected + +### GetReadTimeout +_**Description**_: Get the read timeout specified to phpredis or FALSE if we're not connected + +##### *Parameters* +None + +##### *Return value* +*Mixed* Returns the read timeout (which can be set using setOption and Redis::OPT_READ_TIMOUT) or FALSE if we're not connected + +### GetPersistentID +----- +_**Description**_: Gets the persistent ID that phpredis is using + +##### *Parameters* +None + +##### *Return value* +*Mixed* Returns the persistent id phpredis is using (which will only be set if connected with pconnect), NULL if we're not +using a persistent ID, and FALSE if we're not connected + +### GetAuth +----- +_**Description**_: Get the password used to authenticate the phpredis connection + +### *Parameters* +None + +### *Return value* +*Mixed* Returns the password used to authenticate a phpredis session or NULL if none was used, and FALSE if we're not connected diff --git a/common.h b/common.h index 1b9e97f591..33d60ce6f6 100644 --- a/common.h +++ b/common.h @@ -157,6 +157,7 @@ typedef struct { php_stream *stream; char *host; short port; + char *auth; double timeout; double read_timeout; int failed; diff --git a/library.c b/library.c index 856a805583..32f77cd6d5 100644 --- a/library.c +++ b/library.c @@ -1283,6 +1283,12 @@ PHPAPI void redis_free_socket(RedisSock *redis_sock) if(redis_sock->err) { efree(redis_sock->err); } + if(redis_sock->auth) { + efree(redis_sock->auth); + } + if(redis_sock->persistent_id) { + efree(redis_sock->persistent_id); + } efree(redis_sock->host); efree(redis_sock); } diff --git a/php_redis.h b/php_redis.h index 24f002150e..6ed4babef9 100644 --- a/php_redis.h +++ b/php_redis.h @@ -181,6 +181,15 @@ PHP_METHOD(Redis, setOption); PHP_METHOD(Redis, config); +PHP_METHOD(Redis, getHost); +PHP_METHOD(Redis, getPort); +PHP_METHOD(Redis, getDBNum); +PHP_METHOD(Redis, getTimeout); +PHP_METHOD(Redis, getReadTimeout); +PHP_METHOD(Redis, isConnected); +PHP_METHOD(Redis, getPersistentID); +PHP_METHOD(Redis, getAuth); + #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) #else diff --git a/redis.c b/redis.c index bf92513edb..d274e67854 100644 --- a/redis.c +++ b/redis.c @@ -235,6 +235,16 @@ static zend_function_entry redis_functions[] = { /* config */ PHP_ME(Redis, config, NULL, ZEND_ACC_PUBLIC) + /* introspection */ + PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getPort, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getDBNum, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getTimeout, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getReadTimeout, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getPersistentID, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getAuth, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC) + /* aliases */ PHP_MALIAS(Redis, open, connect, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, popen, pconnect, NULL, ZEND_ACC_PUBLIC) @@ -384,6 +394,26 @@ PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_thr return Z_LVAL_PP(socket); } +/** + * redis_sock_get_direct + * Returns our attached RedisSock pointer if we're connected + */ +PHPAPI RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS TSRMLS_DC) { + zval *object; + RedisSock *redis_sock; + + // If we can't grab our object, or get a socket, or we're not connected, return NULL + if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) || + (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) + { + return NULL; + } + + // Return our socket + return redis_sock; +} + + /** * PHP_MINIT_FUNCTION */ @@ -3289,6 +3319,10 @@ PHP_METHOD(Redis, auth) { cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", password, password_len); + // Free previously stored auth if we have one, and store this password + if(redis_sock->auth) efree(redis_sock->auth); + redis_sock->auth = estrndup(password, password_len); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); @@ -6304,5 +6338,123 @@ PHP_METHOD(Redis, time) { REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw); } +/* + * Introspection stuff + */ + +/* + * {{{ proto Redis::IsConnected + */ +PHP_METHOD(Redis, isConnected) { + RedisSock *redis_sock; + + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} + +/* + * {{{ proto Redis::getHost() + */ +PHP_METHOD(Redis, getHost) { + RedisSock *redis_sock; + + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + RETURN_STRING(redis_sock->host, 1); + } else { + RETURN_FALSE; + } +} + +/* + * {{{ proto Redis::getPort() + */ +PHP_METHOD(Redis, getPort) { + RedisSock *redis_sock; + + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + // Return our port + RETURN_LONG(redis_sock->port); + } else { + RETURN_FALSE; + } +} + +/* + * {{{ proto Redis::getDBNum + */ +PHP_METHOD(Redis, getDBNum) { + RedisSock *redis_sock; + + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + // Return our db number + RETURN_LONG(redis_sock->dbNumber); + } else { + RETURN_FALSE; + } +} + +/* + * {{{ proto Redis::getTimeout + */ +PHP_METHOD(Redis, getTimeout) { + RedisSock *redis_sock; + + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + RETURN_DOUBLE(redis_sock->timeout); + } else { + RETURN_FALSE; + } +} + +/* + * {{{ proto Redis::getReadTimeout + */ +PHP_METHOD(Redis, getReadTimeout) { + RedisSock *redis_sock; + + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + RETURN_DOUBLE(redis_sock->read_timeout); + } else { + RETURN_FALSE; + } +} + +/* + * {{{ proto Redis::getPersistentID + */ +PHP_METHOD(Redis, getPersistentID) { + RedisSock *redis_sock; + + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if(redis_sock->persistent_id != NULL) { + RETURN_STRING(redis_sock->persistent_id, 1); + } else { + RETURN_NULL(); + } + } else { + RETURN_FALSE; + } +} + +/* + * {{{ proto Redis::getAuth + */ +PHP_METHOD(Redis, getAuth) { + RedisSock *redis_sock; + + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if(redis_sock->auth != NULL) { + RETURN_STRING(redis_sock->auth, 1); + } else { + RETURN_NULL(); + } + } else { + RETURN_FALSE; + } +} + /* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ diff --git a/tests/TestRedis.php b/tests/TestRedis.php index d4b512ef62..db9099ba76 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -4301,6 +4301,13 @@ public function testReadTimeoutOption() { $this->assertEquals(12.3, $this->redis->getOption(Redis::OPT_READ_TIMEOUT)); } + public function testIntrospection() { + // Simple introspection tests + $this->assertTrue($this->redis->getHost() === self::HOST); + $this->assertTrue($this->redis->getPort() === self::PORT); + $this->assertTrue($this->redis->getAuth() === self::AUTH); + } + } exit(TestSuite::run("Redis_Test")); From 411100da9e0c65526e19f616432ed551f56d1a4b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 28 Mar 2013 07:56:54 -0700 Subject: [PATCH 0036/1986] Don't duplicate TSRMLS_CC --- redis.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/redis.c b/redis.c index d274e67854..aef1613db6 100644 --- a/redis.c +++ b/redis.c @@ -398,7 +398,7 @@ PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_thr * redis_sock_get_direct * Returns our attached RedisSock pointer if we're connected */ -PHPAPI RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS TSRMLS_DC) { +PHPAPI RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) { zval *object; RedisSock *redis_sock; @@ -6348,7 +6348,7 @@ PHP_METHOD(Redis, time) { PHP_METHOD(Redis, isConnected) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { RETURN_TRUE; } else { RETURN_FALSE; @@ -6361,7 +6361,7 @@ PHP_METHOD(Redis, isConnected) { PHP_METHOD(Redis, getHost) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { RETURN_STRING(redis_sock->host, 1); } else { RETURN_FALSE; @@ -6374,7 +6374,7 @@ PHP_METHOD(Redis, getHost) { PHP_METHOD(Redis, getPort) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { // Return our port RETURN_LONG(redis_sock->port); } else { @@ -6388,7 +6388,7 @@ PHP_METHOD(Redis, getPort) { PHP_METHOD(Redis, getDBNum) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { // Return our db number RETURN_LONG(redis_sock->dbNumber); } else { @@ -6402,7 +6402,7 @@ PHP_METHOD(Redis, getDBNum) { PHP_METHOD(Redis, getTimeout) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { RETURN_DOUBLE(redis_sock->timeout); } else { RETURN_FALSE; @@ -6415,7 +6415,7 @@ PHP_METHOD(Redis, getTimeout) { PHP_METHOD(Redis, getReadTimeout) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { RETURN_DOUBLE(redis_sock->read_timeout); } else { RETURN_FALSE; @@ -6428,7 +6428,7 @@ PHP_METHOD(Redis, getReadTimeout) { PHP_METHOD(Redis, getPersistentID) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { if(redis_sock->persistent_id != NULL) { RETURN_STRING(redis_sock->persistent_id, 1); } else { @@ -6445,7 +6445,7 @@ PHP_METHOD(Redis, getPersistentID) { PHP_METHOD(Redis, getAuth) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU TSRMLS_CC))) { + if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { if(redis_sock->auth != NULL) { RETURN_STRING(redis_sock->auth, 1); } else { From f858f615a7d41570a0b3cc4c9d426a24f05f4b56 Mon Sep 17 00:00:00 2001 From: Yuki Matsukura Date: Sun, 28 Apr 2013 05:16:01 +0900 Subject: [PATCH 0037/1986] Update README.markdown - correct minor example mistake. --- README.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/README.markdown b/README.markdown index bb403d2f5e..8a88b683f8 100644 --- a/README.markdown +++ b/README.markdown @@ -2522,7 +2522,6 @@ $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); $redis->zRangeByScore('key', 0, 3); /* array('val0', 'val2') */ $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE); /* array('val0' => 0, 'val2' => 2) */ -$redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2' => 2) */ $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2') */ $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1)); /* array('val2' => 2) */ ~~~ From 5d1b3a4ddbff6d7966a8f5bee1a98535eff6d6a6 Mon Sep 17 00:00:00 2001 From: Yuki Matsukura Date: Sun, 28 Apr 2013 05:16:01 +0900 Subject: [PATCH 0038/1986] Update README.markdown - correct minor example mistake. --- README.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/README.markdown b/README.markdown index 9e51a77df6..0f4d9cb4dc 100644 --- a/README.markdown +++ b/README.markdown @@ -2522,7 +2522,6 @@ $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); $redis->zRangeByScore('key', 0, 3); /* array('val0', 'val2') */ $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE); /* array('val0' => 0, 'val2' => 2) */ -$redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2' => 2) */ $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2') */ $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1)); /* array('val2' => 2) */ ~~~ From 3e6d5b60d66e0a5b31080a7109c412b0ce6358e6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 28 Apr 2013 11:09:58 -0700 Subject: [PATCH 0039/1986] Cutting a release for 2.2.3. Added myself as a maintainer in the header files --- php_redis.h | 2 +- redis.c | 1 + redis_array.c | 1 + redis_array_impl.c | 1 + redis_session.c | 1 + rpm/php-redis.spec | 2 +- 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/php_redis.h b/php_redis.h index 41ef9142bc..a8f99be00e 100644 --- a/php_redis.h +++ b/php_redis.h @@ -256,7 +256,7 @@ extern zend_module_entry redis_module_entry; #define phpext_redis_ptr redis_module_ptr -#define PHP_REDIS_VERSION "2.2.2" +#define PHP_REDIS_VERSION "2.2.3" #endif diff --git a/redis.c b/redis.c index 43f2e0e4be..181a65fc3a 100644 --- a/redis.c +++ b/redis.c @@ -16,6 +16,7 @@ | Original author: Alfonso Jimenez | | Maintainer: Nicolas Favre-Felix | | Maintainer: Nasreddine Bouafif | + | Maintainer: Michael Grunder | + | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 9eb7b1dcb8..bafcbd49f2 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Nicolas Favre-Felix | + | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ #include "redis_array_impl.h" diff --git a/redis_session.c b/redis_session.c index 009bac51b2..4e633fc917 100644 --- a/redis_session.c +++ b/redis_session.c @@ -16,6 +16,7 @@ | Original author: Alfonso Jimenez | | Maintainer: Nicolas Favre-Felix | | Maintainer: Nasreddine Bouafif | + | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ diff --git a/rpm/php-redis.spec b/rpm/php-redis.spec index 633e3ed299..714854bcb9 100644 --- a/rpm/php-redis.spec +++ b/rpm/php-redis.spec @@ -3,7 +3,7 @@ %global php_version %(php-config --version 2>/dev/null || echo 0) Name: php-redis -Version: 2.2.2 +Version: 2.2.3 Release: 1%{?dist} Summary: The phpredis extension provides an API for communicating with the Redis key-value store. From 4080dd9d0b12ce7fe498dcf07636089d7ea8589d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 28 Apr 2013 11:39:34 -0700 Subject: [PATCH 0040/1986] Fix comment format --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 181a65fc3a..543ceecf0c 100644 --- a/redis.c +++ b/redis.c @@ -16,7 +16,7 @@ | Original author: Alfonso Jimenez | | Maintainer: Nicolas Favre-Felix | | Maintainer: Nasreddine Bouafif | - | Maintainer: Michael Grunder | +----------------------------------------------------------------------+ */ From 942fa840beab7300de897f7ba4ed0a20046abf14 Mon Sep 17 00:00:00 2001 From: Nicolas Favre-Felix Date: Mon, 29 Apr 2013 21:42:42 +0100 Subject: [PATCH 0041/1986] Add PECL package.xml --- package.xml | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 package.xml diff --git a/package.xml b/package.xml new file mode 100644 index 0000000000..1fd2ab3bbd --- /dev/null +++ b/package.xml @@ -0,0 +1,82 @@ + + + redis + pecl.php.net + PHP extension for interfacing with Redis + + This extension provides an API for communicating with Redis servers. + + + Nicolas Favre-Felix + nff + n.favrefelix@gmail.com + yes + + + Michael Grunder + mgrunder + michael.grunder@gmail.com + yes + + 2013-04-29 + + 2.2.3 + 2.2.3 + + + stable + stable + + PHP + + First public release + + + + + + + + + + + + + + + + + + + + + + + + + + 5.2.0 + 6.0.0 + 6.0.0 + + + 1.4.0b1 + + + + redis + + + + stablestable + 2.2.32.2.3 + 2013-04-29 + + First release to PECL + + + + From 2071696d03b4b6dacaf9c7f59b9d016694f1d189 Mon Sep 17 00:00:00 2001 From: Nicolas Favre-Felix Date: Mon, 29 Apr 2013 21:42:42 +0100 Subject: [PATCH 0042/1986] Add PECL package.xml --- package.xml | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 package.xml diff --git a/package.xml b/package.xml new file mode 100644 index 0000000000..1fd2ab3bbd --- /dev/null +++ b/package.xml @@ -0,0 +1,82 @@ + + + redis + pecl.php.net + PHP extension for interfacing with Redis + + This extension provides an API for communicating with Redis servers. + + + Nicolas Favre-Felix + nff + n.favrefelix@gmail.com + yes + + + Michael Grunder + mgrunder + michael.grunder@gmail.com + yes + + 2013-04-29 + + 2.2.3 + 2.2.3 + + + stable + stable + + PHP + + First public release + + + + + + + + + + + + + + + + + + + + + + + + + + 5.2.0 + 6.0.0 + 6.0.0 + + + 1.4.0b1 + + + + redis + + + + stablestable + 2.2.32.2.3 + 2013-04-29 + + First release to PECL + + + + From 5660e10c623350aec348a8916d2fa2ce25dd8f46 Mon Sep 17 00:00:00 2001 From: Nicolas Favre-Felix Date: Thu, 2 May 2013 19:57:49 +0100 Subject: [PATCH 0043/1986] Add FLUSHDB to RedisArray GitHub issue #334 --- redis_array.c | 6 ++++++ redis_array.h | 1 + 2 files changed, 7 insertions(+) diff --git a/redis_array.c b/redis_array.c index 9c414292b1..416e993e26 100644 --- a/redis_array.c +++ b/redis_array.c @@ -55,6 +55,7 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, select, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, info, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, ping, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, flushdb, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, del, NULL, ZEND_ACC_PUBLIC) @@ -577,6 +578,11 @@ PHP_METHOD(RedisArray, ping) multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PING"); } +PHP_METHOD(RedisArray, flushdb) +{ + multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB"); +} + PHP_METHOD(RedisArray, keys) { zval *object, *z_args[1], *z_tmp, z_fun; diff --git a/redis_array.h b/redis_array.h index b2c7d86a50..e26e42e1d6 100644 --- a/redis_array.h +++ b/redis_array.h @@ -18,6 +18,7 @@ PHP_METHOD(RedisArray, _rehash); PHP_METHOD(RedisArray, select); PHP_METHOD(RedisArray, info); PHP_METHOD(RedisArray, ping); +PHP_METHOD(RedisArray, flushdb); PHP_METHOD(RedisArray, mget); PHP_METHOD(RedisArray, mset); PHP_METHOD(RedisArray, del); From f9ef6efc7f834d131a249bc4796d0c19d2e0f0aa Mon Sep 17 00:00:00 2001 From: Nicolas Favre-Felix Date: Thu, 2 May 2013 19:59:27 +0100 Subject: [PATCH 0044/1986] Add FLUSHALL to RedisArray GitHub issue #334 --- redis_array.c | 6 ++++++ redis_array.h | 1 + 2 files changed, 7 insertions(+) diff --git a/redis_array.c b/redis_array.c index 416e993e26..4d5f0a9752 100644 --- a/redis_array.c +++ b/redis_array.c @@ -56,6 +56,7 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, info, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, flushdb, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, flushall, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, del, NULL, ZEND_ACC_PUBLIC) @@ -583,6 +584,11 @@ PHP_METHOD(RedisArray, flushdb) multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB"); } +PHP_METHOD(RedisArray, flushall) +{ + multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL"); +} + PHP_METHOD(RedisArray, keys) { zval *object, *z_args[1], *z_tmp, z_fun; diff --git a/redis_array.h b/redis_array.h index e26e42e1d6..e61b597730 100644 --- a/redis_array.h +++ b/redis_array.h @@ -19,6 +19,7 @@ PHP_METHOD(RedisArray, select); PHP_METHOD(RedisArray, info); PHP_METHOD(RedisArray, ping); PHP_METHOD(RedisArray, flushdb); +PHP_METHOD(RedisArray, flushall); PHP_METHOD(RedisArray, mget); PHP_METHOD(RedisArray, mset); PHP_METHOD(RedisArray, del); From e9d5e21980f13b9374b2de3983eb96a64f65677f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 9 May 2013 11:03:43 -0700 Subject: [PATCH 0045/1986] Support for '-inf', 'inf', and '+inf' for WEIGHTS Redis allows the use of these specialized numbers in the WEIGHTS argument for things like ZUNIONSTORE AND ZINTERSTORE --- redis.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/redis.c b/redis.c index 543ceecf0c..d85620de7f 100644 --- a/redis.c +++ b/redis.c @@ -4394,9 +4394,15 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c zend_hash_get_current_data_ex(arr_weights_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_weights_hash, &pointer)) { - if (Z_TYPE_PP(data) != IS_LONG && Z_TYPE_PP(data) != IS_DOUBLE) { - continue; // ignore non-numeric arguments. - } + // Ignore non numeric arguments, unless they're the special Redis numbers + // "inf" ,"-inf", and "+inf" which can be passed as weights + if (Z_TYPE_PP(data) != IS_LONG && Z_TYPE_PP(data) != IS_DOUBLE && + strncasecmp(Z_STRVAL_PP(data), "inf", sizeof("inf")) != 0 && + strncasecmp(Z_STRVAL_PP(data), "-inf", sizeof("-inf")) != 0 && + strncasecmp(Z_STRVAL_PP(data), "+inf", sizeof("+inf")) != 0) + { + continue; + } old_cmd = NULL; if(*cmd) { @@ -4412,12 +4418,18 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c , integer_length(Z_LVAL_PP(data)), Z_LVAL_PP(data)); } else if(Z_TYPE_PP(data) == IS_DOUBLE) { - cmd_len = redis_cmd_format(&cmd, "%s" /* cmd */ "$%f" _NL /* data, including size */ , cmd, cmd_len , Z_DVAL_PP(data)); + } else if(Z_TYPE_PP(data) == IS_STRING) { + cmd_len = redis_cmd_format(&cmd, + "%s" /* cmd */ + "$%d" _NL /* data len */ + "%s" _NL /* data */ + , cmd, cmd_len, Z_STRLEN_PP(data), + Z_STRVAL_PP(data), Z_STRLEN_PP(data)); } // keep track of elements added From 75ddd072a4f3180329c87669ca643d7b1bb142c7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 9 May 2013 11:55:43 -0700 Subject: [PATCH 0046/1986] Add unit tests for -inf/inf/+inf WEIGHTS Addresses issue #336 --- tests/TestRedis.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 20fdb35858..70d3835529 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -1974,11 +1974,33 @@ public function testZX() { $this->assertTrue($this->redis->zunion('key3', array('key1', 'key2'), array(2, 3.0)) === 3); - $this->redis->delete('key1'); $this->redis->delete('key2'); $this->redis->delete('key3'); + // Test 'inf', '-inf', and '+inf' weights (GitHub issue #336) + $this->redis->zadd('key1', 1, 'one', 2, 'two', 3, 'three'); + $this->redis->zadd('key2', 3, 'three', 4, 'four', 5, 'five'); + + // Make sure phpredis handles these weights + $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, 'inf')) === 5); + $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, '-inf')) === 5); + $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, '+inf')) === 5); + + // Now, confirm that they're being sent, and that it works + $arr_weights = Array('inf','-inf','+inf'); + + foreach($arr_weights as $str_weight) { + $r = $this->redis->zunionstore('key3', array('key1','key2'), array(1,$str_weight)); + $this->assertTrue($r===5); + $r = $this->redis->zrangebyscore('key3', '(-inf', '(inf',array('withscores'=>true)); + $this->assertTrue(count($r)===2); + $this->assertTrue(isset($r['one'])); + $this->assertTrue(isset($r['two'])); + } + + $this->redis->del('key1','key2','key3'); + $this->redis->zadd('key1', 2000.1, 'one'); $this->redis->zadd('key1', 3000.1, 'two'); $this->redis->zadd('key1', 4000.1, 'three'); From f4acf122ea14711e8a22dd942d66188dc0d53212 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Wed, 22 May 2013 06:12:44 +0300 Subject: [PATCH 0047/1986] Easy resharding distributor An integrated distributor for easy resharding. Works on the principle of redistributing keys from 1 shard to 2 shards evenly. --- redis_array_impl.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index f804f4759a..24206d6e9b 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -417,7 +417,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D uint32_t hash; char *out; - int pos, out_len; + int pos = 0, out_len; /* extract relevant part of the key */ out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC); @@ -425,7 +425,41 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D return NULL; if(ra->z_dist) { - if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) { + char *error = NULL; + if(Z_TYPE_P(ra->z_dist) == IS_ARRAY && !zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, &error TSRMLS_CC)) { + zval **z_original_pp; + zval **z_reshards_pp; + HashTable *shards = Z_ARRVAL_P(ra->z_dist); + if (zend_hash_num_elements(shards) != 2) { + return NULL; + } + if (zend_hash_index_find(shards, 0, (void **)&z_original_pp) != SUCCESS || Z_TYPE_PP(z_original_pp) != IS_LONG || + zend_hash_index_find(shards, 1, (void **)&z_reshards_pp) != SUCCESS || Z_TYPE_PP(z_reshards_pp) != IS_LONG) { + return NULL; + } + int total, num_original = Z_LVAL_PP(z_original_pp), num_reshards = Z_LVAL_PP(z_reshards_pp); + if (num_reshards < 1 || ra->count != (num_original * (1 << num_reshards))) { + return NULL; + } + /* Calculate original hash */ + hash = rcrc32(out, out_len); + efree(out); + uint64_t h64 = hash; + h64 *= num_original; + h64 /= 0xffffffff; + pos = (int)h64; + /* Infer the new position */ + for(int i = 0; i < num_reshards; i++) { + total = num_original * 2; + h64 = hash; + h64 *= total; + h64 /= 0xffffffff; + h64 %= 2; + pos = pos + h64 * num_original; + num_original = total; + } + } + else if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) { return NULL; } } @@ -441,7 +475,6 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D pos = (int)h64; } if(out_pos) *out_pos = pos; - return ra->redis[pos]; } From 6b30539cd6f60b704c48b728dc67edb49ce44e97 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Wed, 22 May 2013 06:31:26 +0300 Subject: [PATCH 0048/1986] Easy resharding distributor Updated documentation --- arrays.markdown | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arrays.markdown b/arrays.markdown index de326e9ce8..d4588f0c0c 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -88,6 +88,38 @@ In order to control the distribution of keys by hand, you can provide a custom f For instance, instanciate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. +You may also provide an array of 2 values that will be used as follows: +- The first value is the initial amount of shards in use before the resharding (the x first shards specified in the constructor) +- The second value is the resharding level, or number of resharding iterations. +For instance, suppose you started with 4 servers as follows: +0 => 0 1 2 3 + +After 1 iteration of resharding, keys will be assigned to the following servers: +1 => 0 4 1 5 2 6 3 7 + +After 2 iterations, keys will be assigned to the following servers: +2 => 0 8 4 12 1 9 5 13 2 10 6 14 3 11 7 15 + +After 3 iterations, keys will be assigned to the following servers: +3 => 0 16 8 24 4 20 12 28 1 17 9 25 5 21 13 29 2 18 10 26 6 22 14 30 3 19 11 27 7 23 15 31 + +And so on... + +The idea here is to be able to reshard the keys easily, without moving keys from 1 server to another. +The procedure to adopt is simple: +For each initial shard, setup a slave. For instance, for shard 1 we setup slave 5. +Keys will now be assigned to either shard 1 or shard 5. Once the application sees the new settings, just setup shard 5 as a master. Then, in order to reclaim memory, just cleanup keys from shard 1 that belong to shard 5 and vice-versa. +On the next iteration, setup a new slave 9 for shard 1 and a new slave 13 for shard 5. +Update the application settings, disconnect the new slaves and clean up the shards from keys that don't belong there anymore. +Apply the same procedure for each resharding iteration. + +### Example +
+$ra = new RedisArray(array("host1", "host2", "host3", "host4", "host5", "host6", "host7", "host8"), array("distributor" => array(2, 2)));
+
+ +This declares that we started with 2 shards and moved to 4 then 8 shards. The number of initial shards is 2 and the resharding level (or number of iterations) is 2. + ## Migrating keys When a node is added or removed from a ring, RedisArray instances must be instanciated with a “previous” list of nodes. A single call to `$ra->_rehash()` causes all the keys to be redistributed according to the new list of nodes. Passing a callback function to `_rehash()` makes it possible to track the progress of that operation: the function is called with a node name and a number of keys that will be examined, e.g. `_rehash(function ($host, $count){ ... });`. From d71ed8155de9f6c47eb2dd309da1ed008629baf5 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Wed, 22 May 2013 06:34:21 +0300 Subject: [PATCH 0049/1986] Easy resharding distributor Updated documentation --- arrays.markdown | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arrays.markdown b/arrays.markdown index d4588f0c0c..bc7ce3139d 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -91,26 +91,40 @@ For instance, instanciate a RedisArray object with `new RedisArray(array("us-hos You may also provide an array of 2 values that will be used as follows: - The first value is the initial amount of shards in use before the resharding (the x first shards specified in the constructor) - The second value is the resharding level, or number of resharding iterations. -For instance, suppose you started with 4 servers as follows: +For instance, suppose you started with 4 shards as follows: +
 0 => 0                    1                    2                     3
+
After 1 iteration of resharding, keys will be assigned to the following servers: +
 1 => 0         4          1         5          2          6          3          7
+
After 2 iterations, keys will be assigned to the following servers: +
 2 => 0    8    4    12    1    9    5    13    2    10    6    14    3    11    7    15
+
After 3 iterations, keys will be assigned to the following servers: +
 3 => 0 16 8 24 4 20 12 28 1 17 9 25 5 21 13 29 2 18 10 26 6 22 14 30 3 19 11 27 7 23 15 31
+
And so on... The idea here is to be able to reshard the keys easily, without moving keys from 1 server to another. + The procedure to adopt is simple: + For each initial shard, setup a slave. For instance, for shard 1 we setup slave 5. + Keys will now be assigned to either shard 1 or shard 5. Once the application sees the new settings, just setup shard 5 as a master. Then, in order to reclaim memory, just cleanup keys from shard 1 that belong to shard 5 and vice-versa. + On the next iteration, setup a new slave 9 for shard 1 and a new slave 13 for shard 5. + Update the application settings, disconnect the new slaves and clean up the shards from keys that don't belong there anymore. + Apply the same procedure for each resharding iteration. ### Example From 01f09301130364b659444037be8ee0bdd0253174 Mon Sep 17 00:00:00 2001 From: Emmanuel Merali Date: Wed, 22 May 2013 06:35:09 +0300 Subject: [PATCH 0050/1986] Easy resharding distributor Updated documentation --- arrays.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/arrays.markdown b/arrays.markdown index bc7ce3139d..012a8e657c 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -91,6 +91,7 @@ For instance, instanciate a RedisArray object with `new RedisArray(array("us-hos You may also provide an array of 2 values that will be used as follows: - The first value is the initial amount of shards in use before the resharding (the x first shards specified in the constructor) - The second value is the resharding level, or number of resharding iterations. + For instance, suppose you started with 4 shards as follows:
 0 => 0                    1                    2                     3

From 2f795ed0b4c4405582dba56fdf267f735f247dc3 Mon Sep 17 00:00:00 2001
From: Emmanuel Merali 
Date: Sun, 26 May 2013 01:25:27 +0300
Subject: [PATCH 0051/1986] FIx C99 compliance

FIx C99 compliance
---
 redis_array_impl.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index 24206d6e9b..2fba7a724e 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -448,8 +448,9 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D
 			h64 *= num_original;
 			h64 /= 0xffffffff;
 			pos = (int)h64;
+			int i;
 			/* Infer the new position */
-			for(int i = 0; i < num_reshards; i++) {
+			for(i = 0; i < num_reshards; i++) {
 				total = num_original * 2;
 				h64 = hash;
 				h64 *= total;

From e74e98e0d7b5fb9ecc7b5906e2e43ac8595ff2ca Mon Sep 17 00:00:00 2001
From: Axel Etcheverry 
Date: Mon, 24 Jun 2013 15:59:29 +0200
Subject: [PATCH 0052/1986] Add ZEND_ACC_CTOR and ZEND_ACC_DTOR for Reflection

---
 redis.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/redis.c b/redis.c
index d85620de7f..72ece2d26b 100644
--- a/redis.c
+++ b/redis.c
@@ -72,8 +72,8 @@ PHP_INI_END()
 ZEND_DECLARE_MODULE_GLOBALS(redis)
 
 static zend_function_entry redis_functions[] = {
-     PHP_ME(Redis, __construct, NULL, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, __destruct, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, __construct, NULL, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, __destruct, NULL, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC)
      PHP_ME(Redis, connect, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, pconnect, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, close, NULL, ZEND_ACC_PUBLIC)

From e47e18c4b29b9887b0fe472e76ed461c9407c273 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 24 Jun 2013 11:56:38 -0700
Subject: [PATCH 0053/1986] Create an alias for echo, called sendEcho

Addresses #345
---
 redis.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/redis.c b/redis.c
index d85620de7f..6b2831701c 100644
--- a/redis.c
+++ b/redis.c
@@ -276,6 +276,8 @@ static zend_function_entry redis_functions[] = {
      PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC)
      PHP_MALIAS(Redis, zrevrange, zReverseRange, NULL, ZEND_ACC_PUBLIC)
      
+     PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC) 
+
      PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC)
      PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC)
      {NULL, NULL, NULL}

From 958e062800c7f2727a3c85bd71696be88fcc830a Mon Sep 17 00:00:00 2001
From: Emmanuel Merali 
Date: Mon, 22 Jul 2013 04:50:11 +0300
Subject: [PATCH 0054/1986] Changed distributor to accepts strings

Changed distributor to accept strings as well as longs so that
definitions parsed from parse_ini_files may be used as is
---
 redis_array_impl.c | 26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index 2fba7a724e..090270b60e 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -427,17 +427,35 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D
 	if(ra->z_dist) {
 		char *error = NULL;
 		if(Z_TYPE_P(ra->z_dist) == IS_ARRAY && !zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, &error TSRMLS_CC)) {
+			int num_original, num_reshards;
 			zval **z_original_pp;
 			zval **z_reshards_pp;
 			HashTable *shards = Z_ARRVAL_P(ra->z_dist);
 			if (zend_hash_num_elements(shards) != 2) {
 				return NULL;
 			}
-			if (zend_hash_index_find(shards, 0, (void **)&z_original_pp) != SUCCESS || Z_TYPE_PP(z_original_pp) != IS_LONG || 
-				zend_hash_index_find(shards, 1, (void **)&z_reshards_pp) != SUCCESS || Z_TYPE_PP(z_reshards_pp) != IS_LONG) {
+			if (zend_hash_index_find(shards, 0, (void **)&z_original_pp) != SUCCESS ||
+				zend_hash_index_find(shards, 1, (void **)&z_reshards_pp) != SUCCESS) {
+				return NULL;
+			}
+			if (Z_TYPE_PP(z_original_pp) == IS_LONG) {
+				num_original = Z_LVAL_PP(z_original_pp);
+			}
+			else if (Z_TYPE_PP(z_original_pp) == IS_STRING) {
+				num_original = atol(Z_STRVAL_PP(z_original_pp));
+			}
+			else {
+				return NULL;
+			}
+			if (Z_TYPE_PP(z_reshards_pp) == IS_LONG) {
+				num_reshards = Z_LVAL_PP(z_reshards_pp);
+			}
+			else if (Z_TYPE_PP(z_reshards_pp) == IS_STRING) {
+				num_reshards = atol(Z_STRVAL_PP(z_reshards_pp));
+			}
+			else {
 				return NULL;
 			}
-			int total, num_original = Z_LVAL_PP(z_original_pp), num_reshards = Z_LVAL_PP(z_reshards_pp);
 			if (num_reshards < 1 || ra->count != (num_original * (1 << num_reshards))) {
 				return NULL;
 			}
@@ -451,7 +469,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D
 			int i;
 			/* Infer the new position */
 			for(i = 0; i < num_reshards; i++) {
-				total = num_original * 2;
+				int total = num_original * 2;
 				h64 = hash;
 				h64 *= total;
 				h64 /= 0xffffffff;

From 0ff393bdca1a470abb944c876cb4b33c1e4b0f5e Mon Sep 17 00:00:00 2001
From: Emmanuel Merali 
Date: Mon, 22 Jul 2013 05:29:35 +0300
Subject: [PATCH 0055/1986] Fix poor performance on initial use

Fix poor performance on initial use of easy reshard distributor
mechanism.
---
 redis_array_impl.c | 76 ++++++++++++++++++++++++++++------------------
 1 file changed, 46 insertions(+), 30 deletions(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index 090270b60e..602289d762 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -412,6 +412,50 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML
 	return 1;
 }
 
+zend_bool
+ra_check_distributor(RedisArray *ra, int *num_original, int *num_reshards TSRMLS_DC) {
+	if(Z_TYPE_P(ra->z_dist) == IS_ARRAY) {
+		zval **z_original_pp;
+		zval **z_reshards_pp;
+		HashTable *shards = Z_ARRVAL_P(ra->z_dist);
+		if (zend_hash_num_elements(shards) != 2) {
+			return 0;
+		}
+		if (zend_hash_index_find(shards, 0, (void **)&z_original_pp) != SUCCESS ||
+			zend_hash_index_find(shards, 1, (void **)&z_reshards_pp) != SUCCESS) {
+			return 0;
+		}
+		if (Z_TYPE_PP(z_original_pp) == IS_LONG) {
+			if ((*num_original = Z_LVAL_PP(z_original_pp)) == 0) {
+				return 0;
+			}
+		}
+		else if (Z_TYPE_PP(z_original_pp) == IS_STRING) {
+			if ((*num_original = atol(Z_STRVAL_PP(z_original_pp))) == 0) {
+				return 0;
+			}
+		}
+		else {
+			return 0;
+		}
+		if (Z_TYPE_PP(z_reshards_pp) == IS_LONG) {
+			if ((*num_reshards = Z_LVAL_PP(z_reshards_pp)) == 0) {
+				return 0;
+			}
+		}
+		else if (Z_TYPE_PP(z_reshards_pp) == IS_STRING) {
+			if ((*num_reshards = atol(Z_STRVAL_PP(z_reshards_pp))) == 0) {
+				return 0;
+			}
+		}
+		else {
+			return 0;
+		}
+		return 1;
+	}
+	return 0;
+}
+
 zval *
 ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) {
 
@@ -426,36 +470,8 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D
 
 	if(ra->z_dist) {
 		char *error = NULL;
-		if(Z_TYPE_P(ra->z_dist) == IS_ARRAY && !zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, &error TSRMLS_CC)) {
-			int num_original, num_reshards;
-			zval **z_original_pp;
-			zval **z_reshards_pp;
-			HashTable *shards = Z_ARRVAL_P(ra->z_dist);
-			if (zend_hash_num_elements(shards) != 2) {
-				return NULL;
-			}
-			if (zend_hash_index_find(shards, 0, (void **)&z_original_pp) != SUCCESS ||
-				zend_hash_index_find(shards, 1, (void **)&z_reshards_pp) != SUCCESS) {
-				return NULL;
-			}
-			if (Z_TYPE_PP(z_original_pp) == IS_LONG) {
-				num_original = Z_LVAL_PP(z_original_pp);
-			}
-			else if (Z_TYPE_PP(z_original_pp) == IS_STRING) {
-				num_original = atol(Z_STRVAL_PP(z_original_pp));
-			}
-			else {
-				return NULL;
-			}
-			if (Z_TYPE_PP(z_reshards_pp) == IS_LONG) {
-				num_reshards = Z_LVAL_PP(z_reshards_pp);
-			}
-			else if (Z_TYPE_PP(z_reshards_pp) == IS_STRING) {
-				num_reshards = atol(Z_STRVAL_PP(z_reshards_pp));
-			}
-			else {
-				return NULL;
-			}
+		int num_original, num_reshards;
+		if (ra_check_distributor(ra, &num_original, &num_reshards TSRMLS_CC)) {
 			if (num_reshards < 1 || ra->count != (num_original * (1 << num_reshards))) {
 				return NULL;
 			}

From 7e4cdb47f41e5cb7e0423f3bd76fbfaf6b797768 Mon Sep 17 00:00:00 2001
From: Mathieu Kooiman 
Date: Mon, 29 Jul 2013 14:00:36 +0200
Subject: [PATCH 0056/1986] Don't throw exceptions for (p)connect calls.

When Redis::(p)connect() is called on a newly created Redis instance that will always cause an exception to be thrown. The connect code will actually try and cancel the
exception but this is problematic when you are using tools like APM (pecl.php.net/apm) which hook into the exception handler: it will already have handled it. This change simply stops the connect from throwing exceptions for that situation.
---
 redis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index 6b2831701c..6827c7ed6c 100644
--- a/redis.c
+++ b/redis.c
@@ -629,7 +629,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
 	}
 
 	/* if there is a redis sock already we have to remove it from the list */
-	if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) > 0) {
+	if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) > 0) {
 		if (zend_hash_find(Z_OBJPROP_P(object), "socket",
 					sizeof("socket"), (void **) &socket) == FAILURE) {
 			/* maybe there is a socket but the id isn't known.. what to do? */

From 1b624141c052d89d4b0bd17815768dcb50520edb Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 31 Jul 2013 14:57:26 -0700
Subject: [PATCH 0057/1986] Update documentation and unit tests for TTL/PTTL

Addresses #358
---
 README.markdown     |  4 ++--
 tests/TestRedis.php | 26 +++++++++++++++++---------
 2 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/README.markdown b/README.markdown
index 0f4d9cb4dc..ce978c05ee 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1124,13 +1124,13 @@ var_dump($redis->sort('s', array('sort' => 'desc', 'store' => 'out'))); // (int)
 
 ### ttl, pttl
 -----
-_**Description**_: Returns the time to live left for a given key, in seconds. If the key doesn't exist, `FALSE` is returned. pttl returns a time in milliseconds.
+_**Description**_: Returns the time to live left for a given key in seconds (ttl), or milliseconds (pttl).
 
 ##### *Parameters*
 *Key*: key
 
 ##### *Return value*
-Long, the time left to live in seconds.
+*LONG*:  The time to live in seconds.  If the key has no ttl, `-1` will be returned, and `-2` if the key doesn't exist.
 
 ##### *Example*
 ~~~
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 70d3835529..6091a94d54 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -1626,18 +1626,26 @@ public function testlGetRange() {
 //    }
 
     public function testdbSize() {
-	$this->assertTrue($this->redis->flushDB());
-	$this->redis->set('x', 'y');
-	$this->assertTrue($this->redis->dbSize() === 1);
+        $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->setTimeout('x', 5);
-	for($i = 5; $i > 0; $i--) {
-		$this->assertEquals($i, $this->redis->ttl('x'));
-		sleep(1);
-	}
+        $this->redis->set('x', 'y');
+        $this->redis->setTimeout('x', 5);
+        for($i = 5; $i > 0; $i--) {
+            $this->assertEquals($i, $this->redis->ttl('x'));
+            sleep(1);
+        }
+
+        // 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
+        $this->redis->del('x');
+        $this->assertEquals($this->redis->ttl('x'), -2);
     }
 
     public function testPersist() {

From 0ce690c8fe7c9ac6b64d1ed0b5ddf7072ce8c01c Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 31 Jul 2013 21:20:22 -0700
Subject: [PATCH 0058/1986] Allow for LONG and STRING keys in MGET and MSET. 
 We can't allow for other types, as phpredis proper takes a key->value array
 and PHP doesn't support other array key types.

Addresses #360
---
 redis_array.c | 87 +++++++++++++++++++++++++++++++++++----------------
 1 file changed, 60 insertions(+), 27 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 4d5f0a9752..bbc4927ae8 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -826,18 +826,34 @@ PHP_METHOD(RedisArray, mget)
 	for (i = 0, zend_hash_internal_pointer_reset_ex(h_keys, &pointer);
 			zend_hash_get_current_data_ex(h_keys, (void**) &data,
 				&pointer) == SUCCESS;
-			zend_hash_move_forward_ex(h_keys, &pointer), ++i) {
-
-		if (Z_TYPE_PP(data) != IS_STRING) {
-			php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be string.");
-			efree(argv);
-			efree(pos);
-			efree(redis_instances);
-			efree(argc_each);
-			RETURN_FALSE;
-		}
+			zend_hash_move_forward_ex(h_keys, &pointer), ++i)
+	{
+	    /* If we need to represent a long key as a string */
+	    unsigned int key_len;
+	    char kbuf[40], *key_lookup;
+
+	    /* phpredis proper can only use string or long keys, so restrict to that here */
+	    if(Z_TYPE_PP(data) != IS_STRING && Z_TYPE_PP(data) != IS_LONG) {
+            php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs");
+            efree(argv);
+            efree(pos);
+            efree(redis_instances);
+            efree(argc_each);
+            RETURN_FALSE;
+	    }
+
+	    /* Convert to a string for hash lookup if it isn't one */
+	    if(Z_TYPE_PP(data) == IS_STRING) {
+	        key_len = Z_STRLEN_PP(data);
+            key_lookup = Z_STRVAL_PP(data);
+	    } else {
+	        key_len = snprintf(kbuf, sizeof(kbuf), "%ld", Z_LVAL_PP(data));
+	        key_lookup = (char*)kbuf;
+	    }
+
+		/* Find our node */
+        redis_instances[i] = ra_find_node(ra, key_lookup, key_len, &pos[i] TSRMLS_CC);
 
-		redis_instances[i] = ra_find_node(ra, Z_STRVAL_PP(data), Z_STRLEN_PP(data), &pos[i] TSRMLS_CC);
 		argc_each[pos[i]]++;	/* count number of keys per node */
 		argv[i] = *data;
 	}
@@ -886,7 +902,6 @@ PHP_METHOD(RedisArray, mget)
 		}
 		zval_dtor(z_ret);
 		efree(z_ret);
-
 	}
 
 	/* copy temp array in the right order to return_value */
@@ -918,8 +933,8 @@ PHP_METHOD(RedisArray, mset)
 	int *pos, argc, *argc_each;
 	HashTable *h_keys;
 	zval **redis_instances, *redis_inst, **argv;
-	char *key, **keys;
-	unsigned int key_len;
+	char *key, **keys, **key_free, kbuf[40];
+	unsigned int key_len, free_idx = 0;
 	int type, *key_lens;
 	unsigned long idx;
 
@@ -941,31 +956,43 @@ PHP_METHOD(RedisArray, mset)
 	argv = emalloc(argc * sizeof(zval*));
 	pos = emalloc(argc * sizeof(int));
 	keys = emalloc(argc * sizeof(char*));
-	key_lens = emalloc(argc * sizeof(int));
+    key_lens = emalloc(argc * sizeof(int));
 	redis_instances = emalloc(argc * sizeof(zval*));
 	memset(redis_instances, 0, argc * sizeof(zval*));
 
+	/* Allocate an array holding the indexes of any keys that need freeing */
+	key_free = emalloc(argc * sizeof(char*));
+
 	argc_each = emalloc(ra->count * sizeof(int));
 	memset(argc_each, 0, ra->count * sizeof(int));
 
 	/* associate each key to a redis node */
 	for(i = 0, zend_hash_internal_pointer_reset(h_keys);
 			zend_hash_has_more_elements(h_keys) == SUCCESS;
-			zend_hash_move_forward(h_keys), i++) {
-
-		type = zend_hash_get_current_key_ex(h_keys, &key, &key_len, &idx, 0, NULL);
-		if(type != HASH_KEY_IS_STRING) { /* ignore non-string keys */
-			continue;
-		}
-		if(zend_hash_get_current_data(h_keys, (void**)&data) == FAILURE) {
-			continue;
-		}
-
-		redis_instances[i] = ra_find_node(ra, key, (int)key_len - 1, &pos[i] TSRMLS_CC); /* -1 because of PHP assoc keys which count \0... */
+			zend_hash_move_forward(h_keys), i++)
+	{
+	    /* We have to skip the element if we can't get the array value */
+        if(zend_hash_get_current_data(h_keys, (void**)&data) == FAILURE) {
+            continue;
+        }
+
+		/* Grab our key */
+	    type = zend_hash_get_current_key_ex(h_keys, &key, &key_len, &idx, 0, NULL);
+
+	    /* If the key isn't a string, make a string representation of it */
+	    if(type != HASH_KEY_IS_STRING) {
+	        key_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx);
+	        key = estrndup(kbuf, key_len);
+	        key_free[free_idx++]=key;
+	    } else {
+	        key_len--; /* We don't want the null terminator */
+	    }
+
+		redis_instances[i] = ra_find_node(ra, key, (int)key_len, &pos[i] TSRMLS_CC);
 		argc_each[pos[i]]++;	/* count number of keys per node */
 		argv[i] = *data;
 		keys[i] = key;
-		key_lens[i] = (int)key_len - 1;
+		key_lens[i] = (int)key_len;
 	}
 
 
@@ -1018,8 +1045,14 @@ PHP_METHOD(RedisArray, mset)
 		zval_ptr_dtor(&z_argarray);
 	}
 
+	/* Free any keys that we needed to allocate memory for, because they weren't strings */
+	for(i=0; i
Date: Wed, 31 Jul 2013 21:50:00 -0700
Subject: [PATCH 0059/1986] Formatting

---
 redis_array.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index bbc4927ae8..b201894be6 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -834,12 +834,12 @@ PHP_METHOD(RedisArray, mget)
 
 	    /* phpredis proper can only use string or long keys, so restrict to that here */
 	    if(Z_TYPE_PP(data) != IS_STRING && Z_TYPE_PP(data) != IS_LONG) {
-            php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs");
-            efree(argv);
-            efree(pos);
-            efree(redis_instances);
-            efree(argc_each);
-            RETURN_FALSE;
+	        php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs");
+	        efree(argv);
+	        efree(pos);
+	        efree(redis_instances);
+	        efree(argc_each);
+	        RETURN_FALSE;
 	    }
 
 	    /* Convert to a string for hash lookup if it isn't one */

From 711f053b9f23116a917e761360c3f59f2fbdabea Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 2 Aug 2013 23:31:27 -0700
Subject: [PATCH 0060/1986] Redis >= 2.6.12 extended set options

Implement the new SET options as per the redis documentation:
http://redis.io/commands/set

You can now pass the new options (ex=>sec, px=>milisec, xx, nx) as
an array of options to the SET command and phpredis will handle them.

If you pass key, value,  phpredis will still redirect to SETEX
as it did before (to avoid breaking implementations).

Addresses #364
---
 README.markdown     |  15 +++-
 common.h            |  14 ++++
 redis.c             | 101 +++++++++++++++++++++------
 tests/TestRedis.php | 164 +++++++++++++++++++++++++++++---------------
 4 files changed, 217 insertions(+), 77 deletions(-)

diff --git a/README.markdown b/README.markdown
index ce978c05ee..7f9410ad73 100644
--- a/README.markdown
+++ b/README.markdown
@@ -604,19 +604,30 @@ $redis->get('key');
 
 ### set
 -----
-_**Description**_: Set the string value in argument as value of the key.
+_**Description**_: Set the string value in argument as value of the key.  If you're using Redis >= 2.6.12, you can pass extended options as explained below
 
 ##### *Parameters*
 *Key*  
 *Value*  
-*Timeout* (optional). Calling `SETEX` is preferred if you want a timeout.  
+*Timeout or Options Array* (optional). If you pass an integer, phpredis will redirect to SETEX, and will try to use Redis >= 2.6.12 extended options if you pass an array with valid values
 
 ##### *Return value*
 *Bool* `TRUE` if the command is successful.
 
 ##### *Examples*
 ~~~
+// Simple key -> value set
 $redis->set('key', 'value');
+
+// Will redirect, and actually make an SETEX call
+$redis->set('key','value', 10);
+
+// Will set the key, if it doesn't exist, with a ttl of 10 seconds
+$redis->set('key', 'value', Array('nx', 'ex'=>10);
+
+// Will set a key, if it does exist, with a ttl of 1000 miliseconds
+$redis->set('key', 'value', Array('xx', 'px'=>1000);
+
 ~~~
 
 ### setex, psetex
diff --git a/common.h b/common.h
index 2abb420930..3ed781c853 100644
--- a/common.h
+++ b/common.h
@@ -5,6 +5,11 @@
 #ifndef REDIS_COMMON_H
 #define REDIS_COMMON_H
 
+/* NULL check so Eclipse doesn't go crazy */
+#ifndef NULL
+#define NULL   ((void *) 0)
+#endif
+
 #define redis_sock_name "Redis Socket Buffer"
 #define REDIS_SOCK_STATUS_FAILED 0
 #define REDIS_SOCK_STATUS_DISCONNECTED 1
@@ -138,6 +143,15 @@ else if(redis_sock->mode == MULTI) { \
 
 #define REDIS_PROCESS_RESPONSE(function) REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL)
 
+/* Extended SET argument detection */
+#define IS_EX_ARG(a) ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
+#define IS_PX_ARG(a) ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
+#define IS_NX_ARG(a) ((a[0]=='n' || a[0]=='N') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
+#define IS_XX_ARG(a) ((a[0]=='x' || a[0]=='X') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
+
+#define IS_EX_PX_ARG(a) (IS_EX_ARG(a) || IS_PX_ARG(a))
+#define IS_NX_XX_ARG(a) (IS_NX_ARG(a) || IS_XX_ARG(a))
+
 typedef enum {ATOMIC, MULTI, PIPELINE} redis_mode;
 
 typedef struct fold_item {
diff --git a/redis.c b/redis.c
index 6b2831701c..c8d8fdfd13 100644
--- a/redis.c
+++ b/redis.c
@@ -802,43 +802,103 @@ PHP_METHOD(Redis, close)
 }
 /* }}} */
 
-/* {{{ proto boolean Redis::set(string key, mixed value)
- */
-PHP_METHOD(Redis, set)
-{
+/* {{{ proto boolean Redis::set(string key, mixed value, long timeout | array options) */
+PHP_METHOD(Redis, set) {
     zval *object;
     RedisSock *redis_sock;
-    char *key = NULL, *val = NULL, *cmd;
+    char *key = NULL, *val = NULL, *cmd, *exp_type = NULL, *set_type = NULL;
     int key_len, val_len, cmd_len;
     long expire = -1;
     int val_free = 0, key_free = 0;
-    zval *z_value;
+    zval *z_value, *z_opts = NULL;
 
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|l",
-                                     &object, redis_ce, &key, &key_len,
-                                     &z_value, &expire) == FAILURE) {
+    // Make sure the arguments are correct
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|z",
+                                    &object, redis_ce, &key, &key_len, &z_value,
+                                    &z_opts) == FAILURE)
+    {
         RETURN_FALSE;
     }
 
-    if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+    // Ensure we can grab our redis socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
+    /* Our optional argument can either be a long (to support legacy SETEX */
+    /* redirection), or an array with Redis >= 2.6.12 set options */
+    if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY) {
+        RETURN_FALSE;
+    }
+
+    /* Serialization, key prefixing */
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
-	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
-    if(expire > 0) {
-            cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", key, key_len, expire, val, val_len);
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+    if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
+        HashTable *kt = Z_ARRVAL_P(z_opts);
+        int type;
+        unsigned int ht_key_len;
+        unsigned long idx;
+        char *k;
+        zval **v;
+
+        /* Iterate our option array */
+        for(zend_hash_internal_pointer_reset(kt);
+            zend_hash_has_more_elements(kt) == SUCCESS;
+            zend_hash_move_forward(kt))
+        {
+            // Grab key and value
+            type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL);
+            zend_hash_get_current_data(kt, (void**)&v);
+
+            if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) &&
+               (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k))
+            {
+                exp_type = k;
+                expire = Z_LVAL_PP(v);
+            } else if(Z_TYPE_PP(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_PP(v))) {
+                set_type = Z_STRVAL_PP(v);
+            }
+        }
+    } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) {
+        expire = Z_LVAL_P(z_opts);
+    }
+
+    /* Now let's construct the command we want */
+    if(exp_type && set_type) {
+        /* SET   NX|XX PX|EX  */
+        cmd_len = redis_cmd_format_static(&cmd, "SET", "ssssl", key, key_len,
+                                          val, val_len, set_type, 2, exp_type,
+                                          2, expire);
+    } else if(exp_type) {
+        /* SET   PX|EX  */
+        cmd_len = redis_cmd_format_static(&cmd, "SET", "sssl", key, key_len,
+                                          val, val_len, exp_type, 2, expire);
+    } else if(set_type) {
+        /* SET   NX|XX */
+        cmd_len = redis_cmd_format_static(&cmd, "SET", "sss", key, key_len,
+                                          val, val_len, set_type, 2);
+    } else if(expire > 0) {
+        /* Backward compatible SETEX redirection */
+        cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", key, key_len,
+                                          expire, val, val_len);
     } else {
-            cmd_len = redis_cmd_format_static(&cmd, "SET", "ss", key, key_len, val, val_len);
+        /* SET   */
+        cmd_len = redis_cmd_format_static(&cmd, "SET", "ss", key, key_len,
+                                          val, val_len);
     }
-    if(val_free) efree(val);
+
+    /* Free our key or value if we prefixed/serialized */
     if(key_free) efree(key);
+    if(val_free) efree(val);
 
-	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
-	IF_ATOMIC() {
-		redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
-	}
-	REDIS_PROCESS_RESPONSE(redis_boolean_response);
+    /* Kick off the command */
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_boolean_response);
 }
 
 PHPAPI void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
@@ -2360,7 +2420,6 @@ PHP_METHOD(Redis, sMembers)
 }
 /* }}} */
 
-
 PHPAPI int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len,
 									 int min_argc, RedisSock **out_sock, int has_timeout, int all_keys, int can_serialize)
 {
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 6091a94d54..c0a7d882f9 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -138,84 +138,140 @@ public function testErr() {
 
     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', 'nil'));
+        $this->assertEquals('nil', $this->redis->get('key'));
 
-      	$this->assertEquals(TRUE, $this->redis->set('key', 'val'));
+        $this->assertEquals(TRUE, $this->redis->set('key', 'val'));
 
-	$this->assertEquals('val', $this->redis->get('key'));
-	$this->assertEquals('val', $this->redis->get('key'));
-	$this->redis->delete('keyNotExist');
-	$this->assertEquals(FALSE, $this->redis->get('keyNotExist'));
+        $this->assertEquals('val', $this->redis->get('key'));
+        $this->assertEquals('val', $this->redis->get('key'));
+        $this->redis->delete('keyNotExist');
+        $this->assertEquals(FALSE, $this->redis->get('keyNotExist'));
 
-	$this->redis->set('key2', 'val');
-	$this->assertEquals('val', $this->redis->get('key2'));
+        $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'));
+        $value = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
+        
+        $this->redis->set('key2', $value);
+        $this->assertEquals($value, $this->redis->get('key2'));
+        $this->assertEquals($value, $this->redis->get('key2'));
 
-	$this->redis->delete('key');
-	$this->redis->delete('key2');
+        $this->redis->delete('key');
+        $this->redis->delete('key2');
 
 
-	$i = 66000;
-	$value2 = 'X';
-	while($i--) {
-		$value2 .= 'A';
-	}
-	$value2 .= 'X';
+        $i = 66000;
+        $value2 = 'X';
+        while($i--) {
+		    $value2 .= 'A';
+        }
+	    $value2 .= 'X';
 
-	$this->redis->set('key', $value2);
+        $this->redis->set('key', $value2);
         $this->assertEquals($value2, $this->redis->get('key'));
-	$this->redis->delete('key');
-	$this->assertEquals(False, $this->redis->get('key'));
+        $this->redis->delete('key');
+        $this->assertEquals(False, $this->redis->get('key'));
 
-	$data = gzcompress('42');
+        $data = gzcompress('42');
         $this->assertEquals(True, $this->redis->set('key', $data));
-	$this->assertEquals('42', gzuncompress($this->redis->get('key')));
+        $this->assertEquals('42', gzuncompress($this->redis->get('key')));
 
-	$this->redis->delete('key');
-	$data = gzcompress('value1');
+        $this->redis->delete('key');
+        $data = gzcompress('value1');
         $this->assertEquals(True, $this->redis->set('key', $data));
-	$this->assertEquals('value1', gzuncompress($this->redis->get('key')));
-
-	$this->redis->delete('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('value1', gzuncompress($this->redis->get('key')));
+
+        $this->redis->delete('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', 'lt')) {
+            $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);
+
+        /* 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',Array('nx')));
+        $this->assertEquals($this->redis->get('foo'), 'bar');
+        $this->assertFalse($this->redis->set('foo','bar',Array('nx')));
+
+        /* Set if exists */
+        $this->assertTrue($this->redis->set('foo','bar',Array('xx')));
+        $this->assertEquals($this->redis->get('foo'), 'bar');
+        $this->redis->del('foo');
+        $this->assertFalse($this->redis->set('foo','bar',Array('xx')));
+
+        /* Set with a TTL */
+        $this->assertTrue($this->redis->set('foo','bar',Array('ex'=>100)));
+        $this->assertEquals($this->redis->ttl('foo'), 100);
+
+        /* Set with a PTTL */
+        $this->assertTrue($this->redis->set('foo','bar',Array('px'=>100000)));
+        $this->assertTrue(100000 - $this->redis->pttl('foo') < 1000);
+
+        /* Set if exists, with a TTL */
+        $this->assertTrue($this->redis->set('foo','bar',Array('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', Array('nx', 'ex'=>110)));
+        $this->assertEquals($this->redis->ttl('foo'), 110);
+        $this->assertEquals($this->redis->get('foo'), 'bar');
+        $this->assertFalse($this->redis->set('foo','bar', Array('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', Array('not-valid','nx','invalid','ex'=>200)));
+        $this->assertEquals($this->redis->ttl('foo'), 200);
+        $this->assertEquals($this->redis->get('foo'), 'barbaz');
     }
-    public function testGetSet() {
 
-	$this->redis->delete('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 testGetSet() {
+    	$this->redis->delete('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->assertTrue($this->redis->exists($k));
-	}
+            $this->assertTrue($this->redis->exists($k));
+        }
     }
 
     public function testRename() {

From 641841012f15ed63943718d8a155780a7f2a9f6c Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 6 Aug 2013 14:17:54 -0700
Subject: [PATCH 0061/1986] Make sure RedisArray::mget returns an array

Add a check in mget to make sure that the forwarded call is
returning with an array, and not NULL or something else.

Addresses issue #350
---
 redis_array.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 4d5f0a9752..31f7727801 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -849,7 +849,6 @@ PHP_METHOD(RedisArray, mget)
 
 	/* calls */
 	for(n = 0; n < ra->count; ++n) { /* for each node */
-
 		/* copy args for MGET call on node. */
 		MAKE_STD_ZVAL(z_argarray);
 		array_init(z_argarray);
@@ -873,7 +872,21 @@ PHP_METHOD(RedisArray, mget)
 		zval_ptr_dtor(&z_argarray);
 
 		for(i = 0, j = 0; i < argc; ++i) {
-			if(pos[i] != n) continue;
+		    /* Error out if we didn't get a proper response */
+		    if(Z_TYPE_P(z_ret) != IS_ARRAY) {
+		        /* cleanup */
+		        zval_dtor(z_ret);
+		        efree(z_ret);
+		        zval_ptr_dtor(&z_tmp_array);
+		        efree(pos);
+		        efree(redis_instances);
+		        efree(argc_each);
+
+		        /* failure */
+		        RETURN_FALSE;
+		    }
+
+		    if(pos[i] != n) continue;
 
 			zend_hash_quick_find(Z_ARRVAL_P(z_ret), NULL, 0, j, (void**)&z_cur);
 			j++;
@@ -886,7 +899,6 @@ PHP_METHOD(RedisArray, mget)
 		}
 		zval_dtor(z_ret);
 		efree(z_ret);
-
 	}
 
 	/* copy temp array in the right order to return_value */

From dcbeae7599ccc1fe0310d67ba29629c3774084d5 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 11 Aug 2013 08:54:59 -0700
Subject: [PATCH 0062/1986] Persistent connections with RedisArray

Re-integrating the now old ra-performance branch into a git-flow
branch.  I could have merged it but this was easier with so few
changes
---
 config.h           | 69 ++++++++++++++++++++++++++++++++++++++++++++++
 redis_array.c      | 13 ++++++---
 redis_array.h      |  1 +
 redis_array_impl.c | 21 +++++++++++---
 redis_array_impl.h |  2 +-
 5 files changed, 97 insertions(+), 9 deletions(-)
 create mode 100644 config.h

diff --git a/config.h b/config.h
new file mode 100644
index 0000000000..00c355bdcd
--- /dev/null
+++ b/config.h
@@ -0,0 +1,69 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* Whether to build redis as dynamic module */
+#define COMPILE_DL_REDIS 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_MEMORY_H 1
+
+/* Whether redis igbinary serializer is enabled */
+/* #undef HAVE_REDIS_IGBINARY */
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the  header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* redis sessions */
+#define PHP_SESSION 1
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
diff --git a/redis_array.c b/redis_array.c
index 31f7727801..81f8f6421e 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -194,7 +194,7 @@ PHP_METHOD(RedisArray, __construct)
 	zval *z0, *z_fun = NULL, *z_dist = NULL, **zpData, *z_opts = NULL;
 	int id;
 	RedisArray *ra = NULL;
-	zend_bool b_index = 0, b_autorehash = 0;
+	zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0;
 	HashTable *hPrev = NULL, *hOpts = NULL;
   long l_retry_interval = 0;
 
@@ -238,9 +238,14 @@ PHP_METHOD(RedisArray, __construct)
 			b_autorehash = Z_BVAL_PP(zpData);
 		}
 
+		/* pconnect */
+		if(FAILURE != zend_hash_find(hOpts, "pconnect", sizeof("pconnect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) {
+		    b_pconnect = Z_BVAL_PP(zpData);
+		}
+
 		/* extract retry_interval option. */
-    zval **z_retry_interval_pp;
-		if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) {
+		zval **z_retry_interval_pp;
+        if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) {
 			if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) {
 				if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) {
 					l_retry_interval = Z_LVAL_PP(z_retry_interval_pp);
@@ -259,7 +264,7 @@ PHP_METHOD(RedisArray, __construct)
 			break;
 
 		case IS_ARRAY:
-			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC);
+			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval, b_pconnect TSRMLS_CC);
 			break;
 
 		default:
diff --git a/redis_array.h b/redis_array.h
index e61b597730..652b5aefdc 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -41,6 +41,7 @@ typedef struct RedisArray_ {
 	zval *z_multi_exec;		/* Redis instance to be used in multi-exec */
 	zend_bool index;		/* use per-node index */
 	zend_bool auto_rehash; 	/* migrate keys on read operations */
+	zend_bool pconnect;     /* should we use pconnect */
 	zval *z_fun;			/* key extractor, callable */
 	zval *z_dist;			/* key distributor, callable */
 	zval *z_pure_cmds;		/* hash table */
diff --git a/redis_array_impl.c b/redis_array_impl.c
index bafcbd49f2..9b6f00bfb7 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -160,9 +160,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	zval *z_params_index;
 	zval *z_params_autorehash;
 	zval *z_params_retry_interval;
+	zval *z_params_pconnect;
 	RedisArray *ra = NULL;
 
-	zend_bool b_index = 0, b_autorehash = 0;
+	zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0;
 	long l_retry_interval = 0;
 	HashTable *hHosts = NULL, *hPrev = NULL;
 
@@ -241,8 +242,18 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 		}
 	}
 
+	/* find pconnect option */
+    MAKE_STD_ZVAL(z_params_pconnect);
+    array_init(z_params_pconnect);
+    sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.pconnect")), z_params_pconnect TSRMLS_CC);
+    if (zend_hash_find(Z_ARRVAL_P(z_params_pconnect), name, strlen(name) + 1, (void**) &z_data_pp) != FAILURE) {
+        if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) {
+            b_pconnect = 1;
+        }
+    }
+
 	/* create RedisArray object */
-	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval TSRMLS_CC);
+	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval, b_pconnect TSRMLS_CC);
 	ra->auto_rehash = b_autorehash;
 
 	/* cleanup */
@@ -258,12 +269,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	efree(z_params_autorehash);
 	zval_dtor(z_params_retry_interval);
 	efree(z_params_retry_interval);
+	zval_dtor(z_params_pconnect);
+	efree(z_params_pconnect);
 
 	return ra;
 }
 
 RedisArray *
-ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC) {
+ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval, zend_bool b_pconnect TSRMLS_DC) {
 
 	int count = zend_hash_num_elements(hosts);
 
@@ -284,7 +297,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
 	if(NULL == ra_load_hosts(ra, hosts, retry_interval TSRMLS_CC)) {
 		return NULL;
 	}
-	ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval TSRMLS_CC) : NULL;
+	ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval, b_pconnect TSRMLS_CC) : NULL;
 
 	/* copy function if provided */
 	if(z_fun) {
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 8dd5201bc8..cd893dc1b5 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -7,7 +7,7 @@
 
 RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC);
 RedisArray *ra_load_array(const char *name TSRMLS_DC);
-RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval TSRMLS_DC);
+RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval, zend_bool b_pconnect TSRMLS_DC);
 zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC);
 zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC);
 void ra_init_function_table(RedisArray *ra);

From 16185f2e37d48a8e619e2c6c9c07da9630155e82 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 11 Aug 2013 09:01:49 -0700
Subject: [PATCH 0063/1986] Re-add UNIX socket support to RedisArray

Merging the logic from ra-performance branch into feature/ra-performance
---
 redis_array_impl.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index 9b6f00bfb7..83a2d72329 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -36,7 +36,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC)
 	int count = zend_hash_num_elements(hosts);
 	char *host, *p;
 	short port;
-	zval **zpData, z_cons, z_ret;
+	zval **zpData, *z_args, z_cons, z_ret;
 	RedisSock *redis_sock  = NULL;
 
 	/* function calls on the Redis object */
@@ -59,7 +59,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC)
 		if((p = strchr(host, ':'))) { /* found port */
 			host_len = p - host;
 			port = (short)atoi(p+1);
-		}
+		} else if(strchr(host,'/') != NULL) { /* unix socket */
+            port = -1;
+        }
 
 		/* create Redis object */
 		MAKE_STD_ZVAL(ra->redis[i]);
@@ -68,7 +70,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval TSRMLS_DC)
 		call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC);
 
 		/* create socket */
-		redis_sock = redis_sock_create(host, host_len, port, 0, 0, NULL, retry_interval); /* TODO: persistence? */
+		redis_sock = redis_sock_create(host, host_len, port, 0, ra->pconnect, NULL, retry_interval);
 
 		/* connect */
 		redis_sock_server_open(redis_sock, 1 TSRMLS_CC);

From f97c8433d5204b0e2782759906b88d616911b678 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 11 Aug 2013 11:31:23 -0700
Subject: [PATCH 0064/1986] Integrating mobli's lazy connect and retry interval

---
 redis_array.c      |  2 +-
 redis_array_impl.c | 16 ++++++++--------
 redis_array_impl.h |  4 ++--
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index cd3f831f85..b9871015d5 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -270,7 +270,7 @@ PHP_METHOD(RedisArray, __construct)
 			break;
 
 		case IS_ARRAY:
-			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval, b_pconnect TSRMLS_CC);
+			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval, b_pconnect, b_lazy_connect TSRMLS_CC);
 			break;
 
 		default:
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 664790619e..067b59542d 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -30,13 +30,13 @@ extern int le_redis_sock;
 extern zend_class_entry *redis_ce;
 
 RedisArray*
-ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool lazy_connect TSRMLS_DC)
+ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC)
 {
 	int i, host_len, id;
 	int count = zend_hash_num_elements(hosts);
 	char *host, *p;
 	short port;
-	zval **zpData, *z_args, z_cons, z_ret;
+	zval **zpData, z_cons, z_ret;
 	RedisSock *redis_sock  = NULL;
 
 	/* function calls on the Redis object */
@@ -70,9 +70,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool l
 		call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC);
 
 		/* create socket */
-		redis_sock = redis_sock_create(host, host_len, port, 0, ra->pconnect, NULL, retry_interval, lazy_connect);
+		redis_sock = redis_sock_create(host, host_len, port, 0, ra->pconnect, NULL, retry_interval, b_lazy_connect);
 
-	    if (!lazy_connect)
+	    if (!b_lazy_connect)
     	{
 			/* connect */
 			redis_sock_server_open(redis_sock, 1 TSRMLS_CC);
@@ -269,7 +269,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	}
 
 	/* create RedisArray object */
-	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, l_retry_interval, b_pconnect, b_lazy_connect TSRMLS_CC);
+	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
 	ra->auto_rehash = b_autorehash;
 
 	/* cleanup */
@@ -294,7 +294,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 }
 
 RedisArray *
-ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval, zend_bool b_pconnect, zend_bool b_lazy_connect TSRMLS_DC) {
+ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) {
 
 	int count = zend_hash_num_elements(hosts);
 
@@ -312,10 +312,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
 	/* init array data structures */
 	ra_init_function_table(ra);
 
-	if(NULL == ra_load_hosts(ra, hosts, retry_interval, lazy_connect TSRMLS_CC)) {
+	if(NULL == ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC)) {
 		return NULL;
 	}
-	ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, retry_interval, b_pconnect, b_lazy_connect TSRMLS_CC) : NULL;
+	ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect TSRMLS_CC) : NULL;
 
 	/* copy function if provided */
 	if(z_fun) {
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 9c86ed50a0..10f8512ad1 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -5,9 +5,9 @@
 #include "common.h"
 #include "redis_array.h"
 
-RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool lazy_connect TSRMLS_DC);
+RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
 RedisArray *ra_load_array(const char *name TSRMLS_DC);
-RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, long retry_interval, zend_bool b_pconnect, zend_bool b_lazy_connect TSRMLS_DC);
+RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
 zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC);
 zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC);
 void ra_init_function_table(RedisArray *ra);

From 7aae259ecb86f4b945e3b52eb190ee0fb5be7770 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 11 Aug 2013 11:50:38 -0700
Subject: [PATCH 0065/1986] Fix argument order sent to ra_make_array

---
 redis_array.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis_array.c b/redis_array.c
index b9871015d5..ecc158eab6 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -270,7 +270,7 @@ PHP_METHOD(RedisArray, __construct)
 			break;
 
 		case IS_ARRAY:
-			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, l_retry_interval, b_pconnect, b_lazy_connect TSRMLS_CC);
+			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
 			break;
 
 		default:

From d01abbbd7d2532f037cdca80175f9dd04ff61f8e Mon Sep 17 00:00:00 2001
From: Marlies Heijkoop 
Date: Mon, 12 Aug 2013 21:11:56 +0200
Subject: [PATCH 0066/1986] Added tests for zUnion and zInter with aggregate
 functions but without weights

---
 tests/TestRedis.php | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 6091a94d54..2c0f7e708a 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -1971,6 +1971,20 @@ public function testZX() {
 	$this->redis->delete('key2');
 	$this->redis->delete('key3');
 
+	//test zUnion with weights and aggegration function
+	$this->redis->zadd('key1', 1, 'duplicate');
+	$this->redis->zadd('key2', 2, 'duplicate');
+	$this->redis->zUnion('keyU', array('key1','key2'), array(1,1), 'MIN');
+	$this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0);
+	$this->redis->delete('keyU');
+
+	//now test zUnion *without* weights but with aggregrate function
+	$this->redis->zUnion('keyU', array('key1','key2'), null, 'MIN');
+	$this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0);
+	$this->redis->delete('keyU', 'key1', 'key2');
+
+
+
 	// test integer and float weights (GitHub issue #109).
 	$this->redis->del('key1', 'key2', 'key3');
 
@@ -2087,6 +2101,10 @@ public function testZX() {
 	$this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'max'));
 	$this->assertTrue(array('val3', 'val1') === $this->redis->zRange('keyI', 0, -1));
 
+	$this->redis->delete('keyI');
+	$this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), null, 'max'));
+	$this->assertTrue($this->redis->zScore('keyI', 'val1') === floatval(7));
+
 	// zrank, zrevrank
 	$this->redis->delete('z');
 	$this->redis->zadd('z', 1, 'one');

From 68605de48c208eeced03b3cad85ae4b8fb0699f6 Mon Sep 17 00:00:00 2001
From: Marlies Heijkoop 
Date: Mon, 12 Aug 2013 21:13:36 +0200
Subject: [PATCH 0067/1986] Allow weights array to be null in generic_z_command
 (zUnion/zInter)

This way we can pass an aggregate function without having to also pass a dummy array(1,1,1..) with weights.
---
 redis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index 6b2831701c..34187c7d53 100644
--- a/redis.c
+++ b/redis.c
@@ -4304,7 +4304,7 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
 	int cmd_len, cmd_elements;
 	int free_key_output;
 
-	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|as",
+	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",
 					&object, redis_ce,
 					&key_output, &key_output_len, &keys_array, &weights_array, &operation, &operation_len) == FAILURE) {
 		RETURN_FALSE;

From daef4b73a022f527e20091493bc7d1598012cb09 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 15 Aug 2013 10:47:03 -0700
Subject: [PATCH 0068/1986] Increase double->string precision

When calling php library methods to convert doubles to strings
we were only keeping 8 digits after the decimal point.  For
double precision numbers, 15 are stable.

Redis uses the C library function strtod() to do the conversion,
which appears to use the first 16 digits for aproximation:
http://www.exploringbinary.com/how-strtod-works-and-sometimes-doesnt/

This hotfix increases the signifigant digits to 16, which I think
is the correct number to pick here.

Addresses issue #371
---
 library.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library.h b/library.h
index 0ee1f6febb..f07cdc807d 100644
--- a/library.h
+++ b/library.h
@@ -65,10 +65,10 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
 #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
 	char dbl_decsep; \
 	dbl_decsep = '.'; \
-	dbl_str = _php_math_number_format_ex(dbl, 8, &dbl_decsep, 1, NULL, 0); \
+    dbl_str = _php_math_number_format_ex(dbl, 15, &dbl_decsep, 1, NULL, 0); \
 	dbl_len = strlen(dbl_str);
 #else
 #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
-	dbl_str = _php_math_number_format(dbl, 8, '.', '\x00'); \
+	dbl_str = _php_math_number_format(dbl, 15, '.', '\x00'); \
 	dbl_len = strlen(dbl_str);
 #endif

From 535ff5d13f02ac4639bc4bbfb240bef486bf8b8f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 15 Aug 2013 10:54:41 -0700
Subject: [PATCH 0069/1986] Actually set the precision to 16 places!

Addresses #371
---
 library.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library.h b/library.h
index f07cdc807d..2f83b3eae6 100644
--- a/library.h
+++ b/library.h
@@ -65,10 +65,10 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
 #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
 	char dbl_decsep; \
 	dbl_decsep = '.'; \
-    dbl_str = _php_math_number_format_ex(dbl, 15, &dbl_decsep, 1, NULL, 0); \
+    dbl_str = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \
 	dbl_len = strlen(dbl_str);
 #else
 #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
-	dbl_str = _php_math_number_format(dbl, 15, '.', '\x00'); \
+	dbl_str = _php_math_number_format(dbl, 16, '.', '\x00'); \
 	dbl_len = strlen(dbl_str);
 #endif

From 7207aae8aafd2795e6a11219e710b806a421650d Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 27 Aug 2013 21:26:51 -0700
Subject: [PATCH 0070/1986] Add SLOWLOG command

Add support for the various slowlog commands you can execute
in Redis, including:

SLOWLOG GET [len]
SLOWLOG RESET
SLOWLOG LIST
---
 README.markdown     | 31 +++++++++++++++++++++++++
 php_redis.h         |  1 +
 redis.c             | 56 ++++++++++++++++++++++++++++++++++++++++++---
 tests/TestRedis.php | 10 ++++++++
 4 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/README.markdown b/README.markdown
index ce978c05ee..9354443404 100644
--- a/README.markdown
+++ b/README.markdown
@@ -322,6 +322,7 @@ _**Description**_: Sends a string to Redis, which replies with the same string
 1. [save](#save) - Synchronously save the dataset to disk (wait to complete)
 1. [slaveof](#slaveof) - Make the server a slave of another instance, or promote it to master
 1. [time](#time) - Return the current server time
+1. [slowlog](#slowlog) - Access the Redis slowlog entries
 
 ### bgrewriteaof
 -----
@@ -539,6 +540,36 @@ the unix timestamp, and element one being microseconds.
 $redis->time();
 ~~~
 
+### slowlog
+-----
+_**Description**_: Access the Redis slowlog
+
+##### *Parameters*
+*Operation* (string): This can be either `GET`, `LEN`, or `RESET` 
+*Length* (integer), optional: If executing a `SLOWLOG GET` command, you can pass an optional length.
+#####
+
+##### *Return value*
+The return value of SLOWLOG will depend on which operation was performed.
+SLOWLOG GET: Array of slowlog entries, as provided by Redis
+SLOGLOG LEN: Integer, the length of the slowlog
+SLOWLOG RESET: Boolean, depending on success
+#####
+
+##### *Examples*
+~~~
+// Get ten slowlog entries
+$redis->slowlog('get', 10); 
+// Get the default number of slowlog entries
+
+$redis->slowlog('get');
+// Reset our slowlog
+$redis->slowlog('reset');
+
+// Retrieve slowlog length
+$redis->slowlog('len');
+~~~
+
 ## Keys and Strings
 
 ### Strings
diff --git a/php_redis.h b/php_redis.h
index a8f99be00e..c0c11f68e8 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -180,6 +180,7 @@ PHP_METHOD(Redis, getOption);
 PHP_METHOD(Redis, setOption);
 
 PHP_METHOD(Redis, config);
+PHP_METHOD(Redis, slowlog);
 
 PHP_METHOD(Redis, client);
 
diff --git a/redis.c b/redis.c
index e311c95963..328e7d551b 100644
--- a/redis.c
+++ b/redis.c
@@ -237,6 +237,9 @@ static zend_function_entry redis_functions[] = {
      /* config */
      PHP_ME(Redis, config, NULL, ZEND_ACC_PUBLIC)
 
+     /* slowlog */
+     PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC)
+
      /* introspection */
      PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, getPort, NULL, ZEND_ACC_PUBLIC)
@@ -5885,6 +5888,56 @@ PHP_METHOD(Redis, config)
 /* }}} */
 
 
+/* {{{ proto boolean Redis::slowlog(string arg, [int option])
+ */
+PHP_METHOD(Redis, slowlog) {
+    zval *object;
+    RedisSock *redis_sock;
+    char *arg, *cmd;
+    int arg_len, cmd_len;
+    long option;
+    enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode;
+
+    // Make sure we can get parameters
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
+                                    &object, redis_ce, &arg, &arg_len, &option) == FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    // Figure out what kind of slowlog command we're executing
+    if(!strncasecmp(arg, "GET", 3)) {
+        mode = SLOWLOG_GET;
+    } else if(!strncasecmp(arg, "LEN", 3)) {
+        mode = SLOWLOG_LEN;
+    } else if(!strncasecmp(arg, "RESET", 5)) {
+        mode = SLOWLOG_RESET;
+    } else {
+        // This command is not valid
+        RETURN_FALSE;
+    }
+
+    // Make sure we can grab our redis socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // Create our command.  For everything except SLOWLOG GET (with an arg) it's just two parts
+    if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) {
+        cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl", arg, arg_len, option);
+    } else {
+        cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "s", arg, arg_len);
+    }
+
+    // Kick off our command
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
+            RETURN_FALSE;
+        }
+    }
+    REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+}
 
 // Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
 PHPAPI int
@@ -6511,9 +6564,6 @@ PHP_METHOD(Redis, client) {
         cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
     }
 
-    // Handle CLIENT LIST specifically
-    int is_list = !strncasecmp(opt, "list", 4);
-
     // Execute our queue command
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 2c0f7e708a..ddf828fe40 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -1684,6 +1684,16 @@ public function testClient() {
         $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 testinfo() {
 	$info = $this->redis->info();
 

From bab2a192b74be24719bc55ae213cc77882e56cb3 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 30 Aug 2013 21:11:02 -0700
Subject: [PATCH 0071/1986] Fix an erroneous unit test failure for TTL

When using the TTL command, Redis >= 2.8 will return -1 for a key
with no TTL and -2 for a key that doesn't exist.  This fixes the
unit tests so they don't expect this in versions < 2.8.
---
 tests/TestRedis.php | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index ddf828fe40..3094c40941 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -1643,9 +1643,11 @@ public function testttl() {
         $this->redis->del('x'); $this->redis->set('x', 'bar');
         $this->assertEquals($this->redis->ttl('x'), -1);
 
-        // A key that doesn't exist
-        $this->redis->del('x');
-        $this->assertEquals($this->redis->ttl('x'), -2);
+        // A key that doesn't exist (> 2.8 will return -2)
+        if(version_compare($this->version, "2.8.0", "gte")) {
+            $this->redis->del('x');
+            $this->assertEquals($this->redis->ttl('x'), -2);
+        }
     }
 
     public function testPersist() {

From 77b17e52ea0000d543dcce5ea3014cfb7adb9fc4 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 30 Aug 2013 21:30:00 -0700
Subject: [PATCH 0072/1986] Merge in MatMol's changes so we don't throw and
 then clear an exception in the context of connect or pconnect

Addresses #361
---
 redis.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/redis.c b/redis.c
index f08e22d0db..2ed6729a7a 100644
--- a/redis.c
+++ b/redis.c
@@ -634,13 +634,12 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
 	/* if there is a redis sock already we have to remove it from the list */
 	if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) > 0) {
 		if (zend_hash_find(Z_OBJPROP_P(object), "socket",
-					sizeof("socket"), (void **) &socket) == FAILURE) {
+					sizeof("socket"), (void **) &socket) == FAILURE)
+		{
 			/* maybe there is a socket but the id isn't known.. what to do? */
 		} else {
 			zend_list_delete(Z_LVAL_PP(socket)); /* the refcount should be decreased and the detructor called */
 		}
-	} else {
-		zend_clear_exception(TSRMLS_C); /* clear exception triggered by non-existent socket during connect(). */
 	}
 
 	redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval);

From 8e81c49b55fa978b5d83775008e0dbc114d276c4 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 31 Aug 2013 11:27:54 -0700
Subject: [PATCH 0073/1986] Performance increase for MGET

This commit changes the way we construct the MGET command
such that we avoid allocating and reallocating the command
string pointer for every new key that is added.

This means that MGET performance scales in a linear way rather
than exponentially.
---
 library.c | 15 +++++++++
 library.h |  1 +
 redis.c   | 91 +++++++++++++++++++++++++++----------------------------
 3 files changed, 60 insertions(+), 47 deletions(-)

diff --git a/library.c b/library.c
index 8a35ffdaea..93dc8b9509 100644
--- a/library.c
+++ b/library.c
@@ -454,6 +454,21 @@ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len)
 	return buf.len;
 }
 
+/*
+ * Given a smart string, number of arguments, a keyword, and the length of the keyword
+ * initialize our smart string with the proper Redis header for the command to follow
+ */
+int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len) {
+    smart_str_appendc(str, '*');
+    smart_str_append_long(str, num_args + 1);
+    smart_str_appendl(str, _NL, sizeof(_NL) -1);
+    smart_str_appendc(str, '$');
+    smart_str_append_long(str, keyword_len);
+    smart_str_appendl(str, _NL, sizeof(_NL) - 1);
+    smart_str_appendl(str, keyword, keyword_len);
+    smart_str_appendl(str, _NL, sizeof(_NL) - 1);
+}
+
 /*
  * Append a command sequence to a smart_str
  */
diff --git a/library.h b/library.h
index 0ee1f6febb..e2b3bffa48 100644
--- a/library.h
+++ b/library.h
@@ -4,6 +4,7 @@ int redis_cmd_format(char **ret, char *format, ...);
 int redis_cmd_format_static(char **ret, char *keyword, char *format, ...);
 int redis_cmd_format_header(char **ret, char *keyword, int arg_count);
 int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len);
+int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len);
 int redis_cmd_append_sstr(smart_str *str, char *append, int append_len);
 int redis_cmd_append_int(char **cmd, int cmd_len, int append);
 
diff --git a/redis.c b/redis.c
index 2ed6729a7a..0d962dab4f 100644
--- a/redis.c
+++ b/redis.c
@@ -1325,88 +1325,85 @@ PHP_METHOD(Redis, decrBy){
  */
 PHP_METHOD(Redis, getMultiple)
 {
-    zval *object, *array, **data;
-    HashTable *arr_hash;
-    HashPosition pointer;
+    zval *object, *z_args, **z_ele;
+    HashTable *hash;
+    HashPosition ptr;
     RedisSock *redis_sock;
-    char *cmd = "", *old_cmd = NULL;
-    int cmd_len = 0, array_count, elements = 1;
+    smart_str cmd = {0};
+    int arg_count;
 
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
-                                     &object, redis_ce, &array) == FAILURE) {
+    // Make sure we have proper arguments
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
+                                    &object, redis_ce, &z_args) == FAILURE) {
         RETURN_FALSE;
     }
 
-    if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+    // We'll need the socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    arr_hash    = Z_ARRVAL_P(array);
-    array_count = zend_hash_num_elements(arr_hash);
+    // Grab our array
+    hash = Z_ARRVAL_P(z_args);
 
-    if (array_count == 0) {
+    // We don't need to do anything if there aren't any keys
+    if((arg_count = zend_hash_num_elements(hash)) == 0) {
         RETURN_FALSE;
     }
 
-    for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer);
-         zend_hash_get_current_data_ex(arr_hash, (void**) &data,
-                                       &pointer) == SUCCESS;
-         zend_hash_move_forward_ex(arr_hash, &pointer)) {
+    // Build our command header
+    redis_cmd_init_sstr(&cmd, arg_count, "MGET", 4);
 
+    // Iterate through and grab our keys
+    for(zend_hash_internal_pointer_reset_ex(hash, &ptr);
+        zend_hash_get_current_data_ex(hash, (void**)&z_ele, &ptr) == SUCCESS;
+        zend_hash_move_forward_ex(hash, &ptr))
+    {
         char *key;
-        int key_len;
+        int key_len, key_free;
         zval *z_tmp = NULL;
-		char *old_cmd;
-		int key_free;
 
-        if (Z_TYPE_PP(data) == IS_STRING) {
-            key = Z_STRVAL_PP(data);
-            key_len = Z_STRLEN_PP(data);
-        } else { /* not a string, copy and convert. */
+        // If the key isn't a string, turn it into one
+        if(Z_TYPE_PP(z_ele) == IS_STRING) {
+            key = Z_STRVAL_PP(z_ele);
+            key_len = Z_STRLEN_PP(z_ele);
+        } else {
             MAKE_STD_ZVAL(z_tmp);
-            *z_tmp = **data;
+            *z_tmp = **z_ele;
             zval_copy_ctor(z_tmp);
             convert_to_string(z_tmp);
 
             key = Z_STRVAL_P(z_tmp);
             key_len = Z_STRLEN_P(z_tmp);
         }
-        old_cmd = NULL;
-        if(*cmd) {
-            old_cmd = cmd;
-        }
-		key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
-        cmd_len = redis_cmd_format(&cmd, "%s$%d" _NL "%s" _NL
-                        , cmd, cmd_len
-                        , key_len, key, key_len);
 
-		if(key_free) efree(key);
+        // Apply key prefix if necissary
+        key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
-        if(old_cmd) {
-            efree(old_cmd);
-        }
-        elements++;
+        // Append this key to our command
+        redis_cmd_append_sstr(&cmd, key, key_len);
+
+        // Free our key if it was prefixed
+        if(key_free) efree(key);
+
+        // Free oour temporary ZVAL if we converted from a non-string
         if(z_tmp) {
             zval_dtor(z_tmp);
             efree(z_tmp);
+            z_tmp = NULL;
         }
     }
 
-    old_cmd = cmd;
-    cmd_len = redis_cmd_format(&cmd, "*%d" _NL "$4" _NL "MGET" _NL "%s", elements, cmd, cmd_len);
-    efree(old_cmd);
-
-	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    // Kick off our command
+    REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
     IF_ATOMIC() {
-	    if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-											redis_sock, NULL, NULL) < 0) {
-    	    RETURN_FALSE;
-	    }
+        if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+                                           redis_sock, NULL, NULL) < 0) {
+            RETURN_FALSE;
+        }
     }
     REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
-
 }
-/* }}} */
 
 /* {{{ proto boolean Redis::exists(string key)
  */

From eb0bbbafa048d7948c14fbc3957247135c19bff6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 31 Aug 2013 19:20:34 -0700
Subject: [PATCH 0074/1986] Rollback the automatic resharding distributor

I accidentally pulled this when getting some of the pull requests
integrated (git flow style) for this release.  I like the idea
for sure, but I think it needs more detailed documentation and
further testing.

At the very least, I need to understand it :)
---
 arrays.markdown    |  40 ---------------
 redis_array_impl.c | 120 ++++++++++-----------------------------------
 2 files changed, 26 insertions(+), 134 deletions(-)

diff --git a/arrays.markdown b/arrays.markdown
index 012a8e657c..b122089162 100644
--- a/arrays.markdown
+++ b/arrays.markdown
@@ -88,46 +88,6 @@ In order to control the distribution of keys by hand, you can provide a custom f
 
 For instance, instanciate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server.
 
-You may also provide an array of 2 values that will be used as follows:
-- The first value is the initial amount of shards in use before the resharding (the x first shards specified in the constructor)
-- The second value is the resharding level, or number of resharding iterations.
-
-For instance, suppose you started with 4 shards as follows:
-
-0 => 0                    1                    2                     3
-
- -After 1 iteration of resharding, keys will be assigned to the following servers: -
-1 => 0         4          1         5          2          6          3          7
-
- -After 2 iterations, keys will be assigned to the following servers: -
-2 => 0    8    4    12    1    9    5    13    2    10    6    14    3    11    7    15
-
- -After 3 iterations, keys will be assigned to the following servers: -
-3 => 0 16 8 24 4 20 12 28 1 17 9 25 5 21 13 29 2 18 10 26 6 22 14 30 3 19 11 27 7 23 15 31
-
- -And so on... - -The idea here is to be able to reshard the keys easily, without moving keys from 1 server to another. - -The procedure to adopt is simple: - -For each initial shard, setup a slave. For instance, for shard 1 we setup slave 5. - -Keys will now be assigned to either shard 1 or shard 5. Once the application sees the new settings, just setup shard 5 as a master. Then, in order to reclaim memory, just cleanup keys from shard 1 that belong to shard 5 and vice-versa. - -On the next iteration, setup a new slave 9 for shard 1 and a new slave 13 for shard 5. - -Update the application settings, disconnect the new slaves and clean up the shards from keys that don't belong there anymore. - -Apply the same procedure for each resharding iteration. - ### Example
 $ra = new RedisArray(array("host1", "host2", "host3", "host4", "host5", "host6", "host7", "host8"), array("distributor" => array(2, 2)));
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 067b59542d..c3a1f36522 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -426,105 +426,37 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML
 	return 1;
 }
 
-zend_bool
-ra_check_distributor(RedisArray *ra, int *num_original, int *num_reshards TSRMLS_DC) {
-	if(Z_TYPE_P(ra->z_dist) == IS_ARRAY) {
-		zval **z_original_pp;
-		zval **z_reshards_pp;
-		HashTable *shards = Z_ARRVAL_P(ra->z_dist);
-		if (zend_hash_num_elements(shards) != 2) {
-			return 0;
-		}
-		if (zend_hash_index_find(shards, 0, (void **)&z_original_pp) != SUCCESS ||
-			zend_hash_index_find(shards, 1, (void **)&z_reshards_pp) != SUCCESS) {
-			return 0;
-		}
-		if (Z_TYPE_PP(z_original_pp) == IS_LONG) {
-			if ((*num_original = Z_LVAL_PP(z_original_pp)) == 0) {
-				return 0;
-			}
-		}
-		else if (Z_TYPE_PP(z_original_pp) == IS_STRING) {
-			if ((*num_original = atol(Z_STRVAL_PP(z_original_pp))) == 0) {
-				return 0;
-			}
-		}
-		else {
-			return 0;
-		}
-		if (Z_TYPE_PP(z_reshards_pp) == IS_LONG) {
-			if ((*num_reshards = Z_LVAL_PP(z_reshards_pp)) == 0) {
-				return 0;
-			}
-		}
-		else if (Z_TYPE_PP(z_reshards_pp) == IS_STRING) {
-			if ((*num_reshards = atol(Z_STRVAL_PP(z_reshards_pp))) == 0) {
-				return 0;
-			}
-		}
-		else {
-			return 0;
-		}
-		return 1;
-	}
-	return 0;
-}
-
 zval *
 ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) {
 
-	uint32_t hash;
-	char *out;
-	int pos = 0, out_len;
+        uint32_t hash;
+        char *out;
+        int pos, out_len;
 
-	/* extract relevant part of the key */
-	out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC);
-	if(!out)
-		return NULL;
+        /* extract relevant part of the key */
+        out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC);
+        if(!out)
+                return NULL;
 
-	if(ra->z_dist) {
-		char *error = NULL;
-		int num_original, num_reshards;
-		if (ra_check_distributor(ra, &num_original, &num_reshards TSRMLS_CC)) {
-			if (num_reshards < 1 || ra->count != (num_original * (1 << num_reshards))) {
-				return NULL;
-			}
-			/* Calculate original hash */
-			hash = rcrc32(out, out_len);
-			efree(out);
-			uint64_t h64 = hash;
-			h64 *= num_original;
-			h64 /= 0xffffffff;
-			pos = (int)h64;
-			int i;
-			/* Infer the new position */
-			for(i = 0; i < num_reshards; i++) {
-				int total = num_original * 2;
-				h64 = hash;
-				h64 *= total;
-				h64 /= 0xffffffff;
-				h64 %= 2;
-				pos = pos + h64 * num_original;
-				num_original = total;
-			}
-		}
-		else if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) {
-			return NULL;
-		}
-	}
-	else {
-		/* hash */
-		hash = rcrc32(out, out_len);
-		efree(out);
-	
-		/* get position on ring */
-		uint64_t h64 = hash;
-		h64 *= ra->count;
-		h64 /= 0xffffffff;
-		pos = (int)h64;
-	}
-	if(out_pos) *out_pos = pos;
-	return ra->redis[pos];
+        if(ra->z_dist) {
+                if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) {
+                        return NULL;
+                }
+        }
+        else {
+                /* hash */
+                hash = rcrc32(out, out_len);
+                efree(out);
+        
+                /* get position on ring */
+                uint64_t h64 = hash;
+                h64 *= ra->count;
+                h64 /= 0xffffffff;
+                pos = (int)h64;
+        }
+        if(out_pos) *out_pos = pos;
+
+        return ra->redis[pos];
 }
 
 zval *

From 6e0f0d786c84bcc095d3133007246125f6eb8a30 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 1 Sep 2013 07:48:08 -0700
Subject: [PATCH 0075/1986] Rework generic_z_command to build the command in
 linear time

This commit changes how we build the Redis protocol string for
ZUNIONSTORE and ZINTERSTORE such that we avoid reallocating
memory for the command buffer for each new key or weight
---
 library.c |  38 +++++++
 library.h |   4 +-
 redis.c   | 290 +++++++++++++++++++++++++-----------------------------
 3 files changed, 176 insertions(+), 156 deletions(-)

diff --git a/library.c b/library.c
index 1eeae05ebc..35ffb60cfd 100644
--- a/library.c
+++ b/library.c
@@ -483,6 +483,44 @@ int redis_cmd_append_sstr(smart_str *str, char *append, int append_len) {
     return str->len;
 }
 
+/*
+ * Append an integer to a smart string command
+ */
+int redis_cmd_append_sstr_int(smart_str *str, int append) {
+    char int_buf[32];
+    int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append);
+    return redis_cmd_append_sstr(str, int_buf, int_len);
+}
+
+/*
+ * Append a long to a smart string command
+ */
+int redis_cmd_append_sstr_long(smart_str *str, long append) {
+    char long_buf[32];
+    int long_len = snprintf(long_buf, sizeof(long_buf), "%ld", append);
+    return redis_cmd_append_sstr(str, long_buf, long_len);
+}
+
+/*
+ * Append a double to a smart string command
+ */
+int redis_cmd_append_sstr_dbl(smart_str *str, double value) {
+    char *dbl_str;
+    int dbl_len;
+
+    /// Convert to double
+    REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, value);
+
+    // Append the string
+    int retval = redis_cmd_append_sstr(str, dbl_str, dbl_len);
+
+    // Free our double string
+    efree(dbl_str);
+
+    // Return new length
+    return retval;
+}
+
 /*
  * Append an integer command to a Redis command
  */
diff --git a/library.h b/library.h
index bf2b04571d..7b1d0383ad 100644
--- a/library.h
+++ b/library.h
@@ -6,8 +6,10 @@ int redis_cmd_format_header(char **ret, char *keyword, int arg_count);
 int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len);
 int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len);
 int redis_cmd_append_sstr(smart_str *str, char *append, int append_len);
+int redis_cmd_append_sstr_int(smart_str *str, int append);
+int redis_cmd_append_sstr_long(smart_str *str, long append);
 int redis_cmd_append_int(char **cmd, int cmd_len, int append);
-
+int redis_cmd_append_sstr_dbl(smart_str *str, double value);
 
 PHPAPI char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC);
 
diff --git a/redis.c b/redis.c
index df88def62f..9ea4c475bf 100644
--- a/redis.c
+++ b/redis.c
@@ -4355,187 +4355,167 @@ PHP_METHOD(Redis, zIncrBy)
     generic_incrby_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZINCRBY", sizeof("ZINCRBY")-1);
 }
 /* }}} */
-PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) {
-
-	zval *object, *keys_array, *weights_array = NULL, **data;
-	HashTable *arr_weights_hash = NULL, *arr_keys_hash;
-	int key_output_len, array_weights_count, array_keys_count, operation_len = 0;
-	char *key_output, *operation;
-	RedisSock *redis_sock;
 
-	HashPosition pointer;
-	char *cmd = "";
-	char *old_cmd;
-	int cmd_len, cmd_elements;
-	int free_key_output;
+PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) {
+    zval *object, *z_keys, *z_weights = NULL, **z_data;
+    HashTable *ht_keys, *ht_weights = NULL;
+    RedisSock *redis_sock;
+    smart_str cmd = {0};
+    HashPosition ptr;
+    char *store_key, *agg_op = NULL;
+    int cmd_arg_count = 2, store_key_len, agg_op_len, keys_count;
 
-	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",
-					&object, redis_ce,
-					&key_output, &key_output_len, &keys_array, &weights_array, &operation, &operation_len) == FAILURE) {
-		RETURN_FALSE;
-	}
+    // Grab our parameters
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",
+                                    &object, redis_ce, &store_key, &store_key_len,
+                                    &z_keys, &z_weights, &agg_op, &agg_op_len) == FAILURE)
+    {
+        RETURN_FALSE;
+    }
 
+    // We'll need our socket
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
-		RETURN_FALSE;
+        RETURN_FALSE;
     }
 
-    arr_keys_hash    = Z_ARRVAL_P(keys_array);
-	array_keys_count = zend_hash_num_elements(arr_keys_hash);
+    // Grab our keys argument as an array
+    ht_keys = Z_ARRVAL_P(z_keys);
 
-    if (array_keys_count == 0) {
+    // Nothing to do if there aren't any keys
+    if((keys_count = zend_hash_num_elements(ht_keys)) == 0) {
         RETURN_FALSE;
+    } else {
+        // Increment our overall argument count
+        cmd_arg_count += keys_count;
     }
 
-	if(weights_array != NULL) {
-		arr_weights_hash    = Z_ARRVAL_P(weights_array);
-		array_weights_count = zend_hash_num_elements(arr_weights_hash);
-		if (array_weights_count == 0) {
-        	RETURN_FALSE;
-    	}
-		if((array_weights_count != 0) && (array_weights_count != array_keys_count)) {
-			RETURN_FALSE;
-		}
+    // Grab and validate our weights array
+    if(z_weights != NULL) {
+        ht_weights = Z_ARRVAL_P(z_weights);
 
-	}
+        // This command is invalid if the weights array isn't the same size
+        // as our keys array.
+        if(zend_hash_num_elements(ht_weights) != keys_count) {
+            RETURN_FALSE;
+        }
 
-    free_key_output = redis_key_prefix(redis_sock, &key_output, &key_output_len TSRMLS_CC);
-	cmd_elements = 3;
-	cmd_len = redis_cmd_format(&cmd,
-                    "$%d" _NL /* command_len */
-                    "%s" _NL  /* command */
+        // Increment our overall argument count by the number of keys
+        // plus one, for the "WEIGHTS" argument itself
+        cmd_arg_count += keys_count + 1;
+    }
 
-                    "$%d" _NL /* key_output_len */
-                    "%s" _NL  /* key_output */
+    // AGGREGATE option
+    if(agg_op != NULL) {
+        // Verify our aggregation option
+        if(strncasecmp(agg_op, "SUM", sizeof("SUM")) &&
+           strncasecmp(agg_op, "MIN", sizeof("MIN")) &&
+           strncasecmp(agg_op, "MAX", sizeof("MAX")))
+        {
+            RETURN_FALSE;
+        }
 
-                    "$%d" _NL
-                    "%d" _NL  /* array_keys_count */
+        // Two more arguments: "AGGREGATE" and agg_op
+        cmd_arg_count += 2;
+    }
 
-                    , command_len, command, command_len
-                    , key_output_len, key_output, key_output_len
-                    , integer_length(array_keys_count), array_keys_count);
-	if(free_key_output) efree(key_output);
+    // Command header
+    redis_cmd_init_sstr(&cmd, cmd_arg_count, command, command_len);
 
-	/* keys */
-    for (zend_hash_internal_pointer_reset_ex(arr_keys_hash, &pointer);
-         zend_hash_get_current_data_ex(arr_keys_hash, (void**) &data,
-                                       &pointer) == SUCCESS;
-         zend_hash_move_forward_ex(arr_keys_hash, &pointer)) {
+    // Prefix our key if necessary and add the output key
+    int key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC);
+    redis_cmd_append_sstr(&cmd, store_key, store_key_len);
+    if(key_free) efree(store_key);
 
-        if (Z_TYPE_PP(data) == IS_STRING) {
-            char *old_cmd = NULL;
-			char *data_str;
-			int data_len;
-			int free_data;
-            
-			if(*cmd) {
-                old_cmd = cmd;
-            }
-			data_str = Z_STRVAL_PP(data);
-			data_len = Z_STRLEN_PP(data);
-
-			free_data = redis_key_prefix(redis_sock, &data_str, &data_len TSRMLS_CC);
-            cmd_len = redis_cmd_format(&cmd,
-                            "%s" /* cmd */
-                            "$%d" _NL
-                            "%s" _NL
-                            , cmd, cmd_len
-                            , data_len, data_str, data_len);
-            cmd_elements++;
-			if(free_data) efree(data_str);
-            if(old_cmd) {
-                efree(old_cmd);
-            }
+    // Number of input keys argument
+    redis_cmd_append_sstr_int(&cmd, keys_count);
+
+    // Process input keys
+    for(zend_hash_internal_pointer_reset_ex(ht_keys, &ptr);
+        zend_hash_get_current_data_ex(ht_keys, (void**)&z_data, &ptr)==SUCCESS;
+        zend_hash_move_forward_ex(ht_keys, &ptr))
+    {
+        char *key;
+        int key_free, key_len;
+        zval *z_tmp = NULL;
+
+        if(Z_TYPE_PP(z_data) == IS_STRING) {
+            key = Z_STRVAL_PP(z_data);
+            key_len = Z_STRLEN_PP(z_data);
+        } else {
+            MAKE_STD_ZVAL(z_tmp);
+            *z_tmp = **z_data;
+            convert_to_string(z_tmp);
+
+            key = Z_STRVAL_P(z_tmp);
+            key_len = Z_STRLEN_P(z_tmp);
         }
-    }
 
-	/* weight */
-	if(weights_array != NULL) {
-        cmd_len = redis_cmd_format(&cmd,
-                        "%s" /* cmd */
-                        "$7" _NL
-                        "WEIGHTS" _NL
-                        , cmd, cmd_len);
-        cmd_elements++;
-
-		for (zend_hash_internal_pointer_reset_ex(arr_weights_hash, &pointer);
-			zend_hash_get_current_data_ex(arr_weights_hash, (void**) &data, &pointer) == SUCCESS;
-			zend_hash_move_forward_ex(arr_weights_hash, &pointer)) {
-
-            // Ignore non numeric arguments, unless they're the special Redis numbers
-            // "inf" ,"-inf", and "+inf" which can be passed as weights
-            if (Z_TYPE_PP(data) != IS_LONG && Z_TYPE_PP(data) != IS_DOUBLE &&
-                strncasecmp(Z_STRVAL_PP(data), "inf", sizeof("inf")) != 0 &&
-                strncasecmp(Z_STRVAL_PP(data), "-inf", sizeof("-inf")) != 0 &&
-                strncasecmp(Z_STRVAL_PP(data), "+inf", sizeof("+inf")) != 0)
-            {
-                continue;
-            }
+        // Apply key prefix if necessary
+        key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
-			old_cmd = NULL;
-			if(*cmd) {
-				old_cmd = cmd;
-			}
+        // Append this input set
+        redis_cmd_append_sstr(&cmd, key, key_len);
 
-			if(Z_TYPE_PP(data) == IS_LONG) {
-				cmd_len = redis_cmd_format(&cmd,
-						"%s" /* cmd */
-						"$%d" _NL /* data_len */
-						"%d" _NL  /* data */
-						, cmd, cmd_len
-						, integer_length(Z_LVAL_PP(data)), Z_LVAL_PP(data));
-
-			} else if(Z_TYPE_PP(data) == IS_DOUBLE) {
-				cmd_len = redis_cmd_format(&cmd,
-                                "%s" /* cmd */
-                                "$%f" _NL /* data, including size */
-                                , cmd, cmd_len
-                                , Z_DVAL_PP(data));
-			} else if(Z_TYPE_PP(data) == IS_STRING) {
-                cmd_len = redis_cmd_format(&cmd,
-                                "%s" /* cmd */
-                                "$%d" _NL /* data len */
-                                "%s" _NL /* data */
-                                , cmd, cmd_len, Z_STRLEN_PP(data),
-                                Z_STRVAL_PP(data), Z_STRLEN_PP(data));
-			}
+        // Free our key if it was prefixed
+        if(key_free) efree(key);
 
-			// keep track of elements added
-			cmd_elements++;
-			if(old_cmd) {
-				efree(old_cmd);
-			}
-		}
-	}
+        // Free our temporary z_val if it was converted
+        if(z_tmp) {
+            zval_dtor(z_tmp);
+            efree(z_tmp);
+            z_tmp = NULL;
+        }
+    }
 
- 	if(operation_len != 0) {
-		char *old_cmd = NULL;
-		old_cmd = cmd;
-        cmd_len = redis_cmd_format(&cmd,
-                        "%s" /* cmd */
-                        "$9" _NL
-                        "AGGREGATE" _NL
-                        "$%d" _NL
-                        "%s" _NL
-                        , cmd, cmd_len
-                        , operation_len, operation, operation_len);
-        cmd_elements += 2;
-		efree(old_cmd);
-	}
+    // Weights
+    if(ht_weights != NULL) {
+        // Append "WEIGHTS" argument
+        redis_cmd_append_sstr(&cmd, "WEIGHTS", sizeof("WEIGHTS"));
 
-	old_cmd = cmd;
-	cmd_len = redis_cmd_format(&cmd,
-                    "*%d" _NL
-                    "%s"
-                    , cmd_elements
-                    , cmd, cmd_len);
-	efree(old_cmd);
+        // Process weights
+        for(zend_hash_internal_pointer_reset_ex(ht_weights, &ptr);
+            zend_hash_get_current_data_ex(ht_weights, (void**)&z_data, &ptr)==SUCCESS;
+            zend_hash_move_forward_ex(ht_weights, &ptr))
+        {
+            // Ignore non numeric arguments, unless they're special Redis numbers
+            if (Z_TYPE_PP(z_data) != IS_LONG && Z_TYPE_PP(z_data) != IS_DOUBLE &&
+                 strncasecmp(Z_STRVAL_PP(z_data), "inf", sizeof("inf")) != 0 &&
+                 strncasecmp(Z_STRVAL_PP(z_data), "-inf", sizeof("-inf")) != 0 &&
+                 strncasecmp(Z_STRVAL_PP(z_data), "+inf", sizeof("+inf")) != 0)
+            {
+                // We should abort if we have an invalid weight, rather than pass
+                // a different number of weights than the user is expecting
+                efree(cmd.c);
+                RETURN_FALSE;
+            }
 
-	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
-	IF_ATOMIC() {
-	  redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
-	}
-	REDIS_PROCESS_RESPONSE(redis_long_response);
+            // Append the weight based on it's input type
+            switch(Z_TYPE_PP(z_data)) {
+                case IS_LONG:
+                    redis_cmd_append_sstr_long(&cmd, Z_LVAL_PP(z_data));
+                    break;
+                case IS_DOUBLE:
+                    redis_cmd_append_sstr_dbl(&cmd, Z_DVAL_PP(z_data));
+                    break;
+                case IS_STRING:
+                    redis_cmd_append_sstr(&cmd, Z_STRVAL_PP(z_data), Z_STRLEN_PP(z_data));
+                    break;
+            }
+        }
+    }
+
+    // Aggregation options, if we have them
+    if(agg_op) {
+        redis_cmd_append_sstr(&cmd, "AGGREGATE", sizeof("AGGREGATE"));
+        redis_cmd_append_sstr(&cmd, agg_op, agg_op_len);
+    }
 
+    // Kick off our request
+    REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
+    IF_ATOMIC() {
+        redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_long_response);
 }
 
 /* zInter */

From 5c4fe08b692fbfe9971583dd67f0d411cf627288 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 1 Sep 2013 09:33:20 -0700
Subject: [PATCH 0076/1986] generic_z_command fixes

Use the lengths for our optional parameters, as NULL will
come through as a non null zval pointer with a zero length

Properly return the length from the redis_cmd_sstr_init function
---
 library.c |  1 +
 redis.c   | 10 +++++-----
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/library.c b/library.c
index 35ffb60cfd..19a6f43b7e 100644
--- a/library.c
+++ b/library.c
@@ -467,6 +467,7 @@ int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword
     smart_str_appendl(str, _NL, sizeof(_NL) - 1);
     smart_str_appendl(str, keyword, keyword_len);
     smart_str_appendl(str, _NL, sizeof(_NL) - 1);
+    return str->len;
 }
 
 /*
diff --git a/redis.c b/redis.c
index 9ea4c475bf..7d26082e66 100644
--- a/redis.c
+++ b/redis.c
@@ -4405,7 +4405,7 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
     }
 
     // AGGREGATE option
-    if(agg_op != NULL) {
+    if(agg_op_len != 0) {
         // Verify our aggregation option
         if(strncasecmp(agg_op, "SUM", sizeof("SUM")) &&
            strncasecmp(agg_op, "MIN", sizeof("MIN")) &&
@@ -4470,7 +4470,7 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
     // Weights
     if(ht_weights != NULL) {
         // Append "WEIGHTS" argument
-        redis_cmd_append_sstr(&cmd, "WEIGHTS", sizeof("WEIGHTS"));
+        redis_cmd_append_sstr(&cmd, "WEIGHTS", sizeof("WEIGHTS") - 1);
 
         // Process weights
         for(zend_hash_internal_pointer_reset_ex(ht_weights, &ptr);
@@ -4489,7 +4489,7 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
                 RETURN_FALSE;
             }
 
-            // Append the weight based on it's input type
+            // Append the weight based on the input type
             switch(Z_TYPE_PP(z_data)) {
                 case IS_LONG:
                     redis_cmd_append_sstr_long(&cmd, Z_LVAL_PP(z_data));
@@ -4505,8 +4505,8 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
     }
 
     // Aggregation options, if we have them
-    if(agg_op) {
-        redis_cmd_append_sstr(&cmd, "AGGREGATE", sizeof("AGGREGATE"));
+    if(agg_op_len != 0) {
+        redis_cmd_append_sstr(&cmd, "AGGREGATE", sizeof("AGGREGATE") - 1);
         redis_cmd_append_sstr(&cmd, agg_op, agg_op_len);
     }
 

From f4960ddec2aa5f5875541498b4fdb67d40eacae3 Mon Sep 17 00:00:00 2001
From: Nicolas Favre-Felix 
Date: Sun, 1 Sep 2013 20:24:09 +0100
Subject: [PATCH 0077/1986] Release version 2.2.4

---
 debian.control     |  2 +-
 package.xml        | 14 +++++++++++---
 php_redis.h        |  2 +-
 rpm/php-redis.spec |  2 +-
 4 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/debian.control b/debian.control
index 5bd408b9e3..0a5fe73847 100644
--- a/debian.control
+++ b/debian.control
@@ -1,5 +1,5 @@
 Package: phpredis
-Version: 2.2.2
+Version: 2.2.4
 Section: web 
 Priority: optional
 Architecture: all
diff --git a/package.xml b/package.xml
index 1fd2ab3bbd..dd97ad7779 100644
--- a/package.xml
+++ b/package.xml
@@ -21,10 +21,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
   michael.grunder@gmail.com
   yes
  
- 2013-04-29
+ 2013-09-01
  
-  2.2.3
-  2.2.3
+  2.2.4
+  2.2.4
  
  
   stable
@@ -70,6 +70,14 @@ http://pear.php.net/dtd/package-2.0.xsd">
  redis
  
  
+   
+   stablestable
+   2.2.42.2.4
+   2013-09-01
+   
+	   See GitHub for release notes
+   
+  
    
    stablestable
    2.2.32.2.3
diff --git a/php_redis.h b/php_redis.h
index c0c11f68e8..879503aa38 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -257,7 +257,7 @@ extern zend_module_entry redis_module_entry;
 
 #define phpext_redis_ptr redis_module_ptr
 
-#define PHP_REDIS_VERSION "2.2.3"
+#define PHP_REDIS_VERSION "2.2.4"
 
 #endif
 
diff --git a/rpm/php-redis.spec b/rpm/php-redis.spec
index 714854bcb9..4f04fb1855 100644
--- a/rpm/php-redis.spec
+++ b/rpm/php-redis.spec
@@ -3,7 +3,7 @@
 %global php_version %(php-config --version 2>/dev/null || echo 0)
 
 Name:           php-redis
-Version:        2.2.3
+Version:        2.2.4
 Release:        1%{?dist}
 Summary:        The phpredis extension provides an API for communicating with the Redis key-value store.
 

From 02bac74dc3b91eb9cf83fc29695443efec0bade4 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 1 Sep 2013 13:24:48 -0700
Subject: [PATCH 0078/1986] Initialize agg_op_len to zero

---
 redis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index 7d26082e66..38a0114225 100644
--- a/redis.c
+++ b/redis.c
@@ -4363,7 +4363,7 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
     smart_str cmd = {0};
     HashPosition ptr;
     char *store_key, *agg_op = NULL;
-    int cmd_arg_count = 2, store_key_len, agg_op_len, keys_count;
+    int cmd_arg_count = 2, store_key_len, agg_op_len = 0, keys_count;
 
     // Grab our parameters
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",

From 18339ec05b4161c472df6de8218177fa6caad71a Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 3 Sep 2013 21:13:57 -0700
Subject: [PATCH 0079/1986] If no valid keys are found when processing an HMGET
 command, free up our command and keys pointer and simply return FALSE.

Addresses #379
---
 redis.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/redis.c b/redis.c
index 38a0114225..40c665c7a5 100644
--- a/redis.c
+++ b/redis.c
@@ -4993,6 +4993,13 @@ PHP_METHOD(Redis, hMget) {
             }
     }
 
+    // This is a failure if none of the keys were valid
+    if(i == 0) {
+        efree(cmd);
+        efree(z_keys);
+        RETURN_FALSE;
+    }
+
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 	IF_ATOMIC() {
 		redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, z_keys);

From a0e9b65d4ce16eb00a06ea579bf422b8f0493109 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 3 Sep 2013 21:17:38 -0700
Subject: [PATCH 0080/1986] Add a unit test for the scenario described in #379

---
 tests/TestRedis.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index d19f9e0b86..27ff047d94 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -2282,6 +2282,8 @@ public function testHashes() {
 	$this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123)));
 	$this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123)));
 
+    // Test with an array populated with things we can't use as keys
+    $this->assertTrue($this->redis->hmget('h', Array(false,NULL,false)) === FALSE);
 
 	// hmget/hmset with numeric fields
 	$this->redis->del('h');

From 1a7952d0d0a141e901b1374e079b4a90a1274982 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 3 Sep 2013 21:13:57 -0700
Subject: [PATCH 0081/1986] If no valid keys are found when processing an HMGET
 command, free up our command and keys pointer and simply return FALSE.

Addresses #379
---
 redis.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/redis.c b/redis.c
index 38a0114225..40c665c7a5 100644
--- a/redis.c
+++ b/redis.c
@@ -4993,6 +4993,13 @@ PHP_METHOD(Redis, hMget) {
             }
     }
 
+    // This is a failure if none of the keys were valid
+    if(i == 0) {
+        efree(cmd);
+        efree(z_keys);
+        RETURN_FALSE;
+    }
+
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 	IF_ATOMIC() {
 		redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, z_keys);

From 03d5a985c6a8ebae77e30970073b450e90356d9b Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 3 Sep 2013 21:17:38 -0700
Subject: [PATCH 0082/1986] Add a unit test for the scenario described in #379

---
 tests/TestRedis.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index d19f9e0b86..27ff047d94 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -2282,6 +2282,8 @@ public function testHashes() {
 	$this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123)));
 	$this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123)));
 
+    // Test with an array populated with things we can't use as keys
+    $this->assertTrue($this->redis->hmget('h', Array(false,NULL,false)) === FALSE);
 
 	// hmget/hmset with numeric fields
 	$this->redis->del('h');

From b144743345ff6ba7f269be6db90ff8c44f12f3c4 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 5 Sep 2013 15:08:30 -0700
Subject: [PATCH 0083/1986] Rework the HMGET command to skip invalid keys

This relates to a previous hotfix for issue #379 where phpredis
would time out if you sent an array of empty values.  The reason
it was timing out is that the argument count being sent wasn't
reflecting any skipped items in the array (meaning redis was
waiting for the rest of the command).

I realized that the previous fix would still fail if you were to
send some valid values, with invalid (null, empty string, etc)
ones mixed in.

Presently, we're just skipping invalid items in the array but there
might be a case to issue a php_error_docref type warning when we
encounter them, so the user can know that it happened.

In addition, HMGET now uses a smart_str to build the command, which
means the time it takes to build the key will scale in a linear fashion
---
 redis.c             | 125 ++++++++++++++++++++++----------------------
 tests/TestRedis.php |   3 ++
 2 files changed, 65 insertions(+), 63 deletions(-)

diff --git a/redis.c b/redis.c
index 40c665c7a5..d57a17acae 100644
--- a/redis.c
+++ b/redis.c
@@ -4918,93 +4918,92 @@ PHP_METHOD(Redis, hIncrBy)
 
 }
 
-
+/* {{{ array Redis::hMget(string hash, array keys) */
 PHP_METHOD(Redis, hMget) {
     zval *object;
     RedisSock *redis_sock;
-    char *key = NULL, *cmd;
-    int key_len, cmd_len, key_free;
-    zval *z_array;
-    zval **z_keys;
-    int nb_fields, i;
-	char *old_cmd = NULL;
-
-	zval **data;
-    HashTable *arr_hash;
-    HashPosition pointer;
+    char *key = NULL;
+    zval *z_array, **z_keys, **data;
+    int field_count, i, valid, key_len, key_free;
+    HashTable *ht_array;
+    HashPosition ptr;
+    smart_str cmd = {0};
 
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
-                                     &object, redis_ce,
-                                     &key, &key_len, &z_array) == FAILURE) {
+    // Make sure we can grab our arguments properly
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
+                                    &object, redis_ce, &key, &key_len, &z_array)
+                                    == FAILURE)
+    {
         RETURN_FALSE;
     }
 
-    if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+    // We'll need our socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
-    nb_fields = zend_hash_num_elements(Z_ARRVAL_P(z_array));
 
-    if( nb_fields == 0) {
+    // Grab member count and abort if we don't have any
+    if((field_count = zend_hash_num_elements(Z_ARRVAL_P(z_array))) == 0) {
         RETURN_FALSE;
     }
 
-    z_keys = ecalloc(nb_fields, sizeof(zval *));
+    // Prefix our key if we need to
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
-	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+    // Allocate enough memory for the number of keys being requested
+    z_keys = ecalloc(field_count, sizeof(zval *));
 
-    cmd_len = redis_cmd_format(&cmd,
-                    "*%d" _NL
-                    "$5" _NL
-                    "HMGET" _NL
+    // Grab our HashTable
+    ht_array = Z_ARRVAL_P(z_array);
 
-                    "$%d" _NL   /* key */
-                    "%s" _NL
-                    , nb_fields + 2
-                    , key_len, key, key_len);
-	if(key_free) efree(key);
+    // Iterate through our keys, grabbing members that are valid
+    for(valid=0, zend_hash_internal_pointer_reset_ex(ht_array, &ptr);
+        zend_hash_get_current_data_ex(ht_array, (void**)&data, &ptr)==SUCCESS;
+        zend_hash_move_forward_ex(ht_array, &ptr))
+    {
+        // Make sure the data is a long or string, and if it's a string that
+        // it isn't empty.  There is no reason to send empty length members.
+        if((Z_TYPE_PP(data) == IS_STRING && Z_STRLEN_PP(data)>0) ||
+            Z_TYPE_PP(data) == IS_LONG) 
+        {
+            // This is a key we can ask for, copy it and set it in our array
+            MAKE_STD_ZVAL(z_keys[valid]);
+            *z_keys[valid] = **data;
+            zval_copy_ctor(z_keys[valid]);
+            convert_to_string(z_keys[valid]);
+
+            // Increment the number of valid keys we've encountered
+            valid++;
+        }
+    }
 
-    arr_hash = Z_ARRVAL_P(z_array);
+    // If we don't have any valid keys, we can abort here
+    if(valid == 0) {
+        if(key_free) efree(key);
+        efree(z_keys);
+        RETURN_FALSE;
+    }
 
-    for (i = 0, zend_hash_internal_pointer_reset_ex(arr_hash, &pointer);
-                    zend_hash_get_current_data_ex(arr_hash, (void**) &data,
-                            &pointer) == SUCCESS;
-                    zend_hash_move_forward_ex(arr_hash, &pointer)) {
+    // Build command header.  One extra argument for the hash key itself
+    redis_cmd_init_sstr(&cmd, valid+1, "HMGET", sizeof("HMGET")-1);
 
-			if (Z_TYPE_PP(data) == IS_LONG || Z_TYPE_PP(data) == IS_STRING) {
+    // Add the hash key
+    redis_cmd_append_sstr(&cmd, key, key_len);
 
-				old_cmd = cmd;
-				if (Z_TYPE_PP(data) == IS_LONG) {
-				    cmd_len = redis_cmd_format(&cmd, "%s" "$%d" _NL "%d" _NL
-                                    , cmd, cmd_len
-                                    , integer_length(Z_LVAL_PP(data)), (int)Z_LVAL_PP(data));
-				} else if (Z_TYPE_PP(data) == IS_STRING) {
-				    cmd_len = redis_cmd_format(&cmd, "%s" "$%d" _NL "%s" _NL
-                                    , cmd, cmd_len
-                                    , Z_STRLEN_PP(data), Z_STRVAL_PP(data), Z_STRLEN_PP(data));
-				}
-				efree(old_cmd);
-                /* save context */
-                MAKE_STD_ZVAL(z_keys[i]);
-                *z_keys[i] = **data;
-                zval_copy_ctor(z_keys[i]);
-                convert_to_string(z_keys[i]);
+    // Free key memory if it was prefixed
+    if(key_free) efree(key);
 
-                i++;
-            }
+    // Iterate our keys, appending them as arguments
+    for(i=0;iassertTrue($this->redis->hmget('h', Array(false,NULL,false)) === FALSE);
 
+    // Test with some invalid keys mixed in (which should just be ignored)
+    $this->assertTrue(array('x'=>'123','y'=>'456','z'=>'abc') === $this->redis->hMget('h',Array('x',null,'y','','z',false)));
+
 	// hmget/hmset with numeric fields
 	$this->redis->del('h');
 	$this->assertTrue(TRUE === $this->redis->hMset('h', array(123 => 'x', 'y' => 456)));

From d999059918cfb9f185c230a49692bd6fefd70875 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 5 Sep 2013 22:18:20 -0700
Subject: [PATCH 0084/1986] Fix a merge fail

---
 redis.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/redis.c b/redis.c
index fa310df6ea..d57a17acae 100644
--- a/redis.c
+++ b/redis.c
@@ -5000,11 +5000,6 @@ PHP_METHOD(Redis, hMget) {
 
     // Kick off our request
     REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
-        efree(cmd);
-        efree(z_keys);
-        RETURN_FALSE;
-    }
-
     IF_ATOMIC() {
         redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, z_keys);
     }

From 4a6c01bc1a5d0c0a19f6403941a9ea928011b645 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 7 Sep 2013 13:51:33 -0700
Subject: [PATCH 0085/1986] Send a newline with the QUIT command

Addresses #381
---
 library.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library.c b/library.c
index 19a6f43b7e..7e7dc07233 100644
--- a/library.c
+++ b/library.c
@@ -1182,7 +1182,7 @@ PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC)
     redis_sock->dbNumber = 0;
     if (redis_sock->stream != NULL) {
 			if (!redis_sock->persistent) {
-				redis_sock_write(redis_sock, "QUIT", sizeof("QUIT") - 1 TSRMLS_CC);
+				redis_sock_write(redis_sock, "QUIT" _NL, sizeof("QUIT" _NL) - 1 TSRMLS_CC);
 			}
 
 			redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED;

From f3c0dd2b9fc6508326439911b7b630ebaa1fced5 Mon Sep 17 00:00:00 2001
From: Deniz Adrian 
Date: Fri, 4 Oct 2013 15:07:42 +0200
Subject: [PATCH 0086/1986] let setex properly handle long expire values

adjust the format string passed to redis_cmd_format_static() to
properly handle long int, in order to prevent integer overflows
resulting in negative expire times passed to redis.
---
 redis.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/redis.c b/redis.c
index d57a17acae..ed4ec9d99b 100644
--- a/redis.c
+++ b/redis.c
@@ -890,7 +890,7 @@ PHP_METHOD(Redis, set) {
                                           val, val_len, set_type, 2);
     } else if(expire > 0) {
         /* Backward compatible SETEX redirection */
-        cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", key, key_len,
+        cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sls", key, key_len,
                                           expire, val, val_len);
     } else {
         /* SET   */
@@ -932,7 +932,7 @@ PHPAPI void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
 
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
-    cmd_len = redis_cmd_format_static(&cmd, keyword, "sds", key, key_len, expire, val, val_len);
+    cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len);
     if(val_free) efree(val);
     if(key_free) efree(key);
 

From 0f003bcb2d406b7540d20ab3f2011357fe637187 Mon Sep 17 00:00:00 2001
From: Soenke Ruempler 
Date: Fri, 4 Oct 2013 15:04:02 +0200
Subject: [PATCH 0087/1986] regression test for: setex properly handles long
 expire values

---
 tests/TestRedis.php | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index b93cef1077..2a6847542e 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -408,6 +408,13 @@ public function testSetNX() {
 	    $this->assertTrue($this->redis->get('key') === '42');
     }
 
+    public function testExpireAtWithLong() {
+        $longExpiryTimeExceedingInt = 3153600000;
+        $this->redis->delete('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);

From b7e8b3b64edbfc6583f551a22c9d2186625e19c9 Mon Sep 17 00:00:00 2001
From: vostok4 
Date: Thu, 10 Oct 2013 13:07:01 +0200
Subject: [PATCH 0088/1986] Fix compilation errors on Win32 with VC11

This brings the W32 compilation fixes done by @char101 up to date and
allows building of php_redis.dll with VC11 on Win32 (allows for a php5.5
version).
---
 config.w32         |  12 ++++--
 library.c          | 104 +++++++++++++++++++++++++--------------------
 library.h          |  75 ++++++++++++++++----------------
 php_redis.h        |  36 ++++++++--------
 redis.c            |  95 +++++++++++++++++++++--------------------
 redis_array.c      |   9 ++--
 redis_array_impl.c |   5 ++-
 redis_session.c    |  14 +++---
 8 files changed, 185 insertions(+), 165 deletions(-)

diff --git a/config.w32 b/config.w32
index 8b39af9057..6926a02731 100644
--- a/config.w32
+++ b/config.w32
@@ -2,14 +2,20 @@
 
 ARG_ENABLE("redis", "whether to enable redis support", "yes");
 ARG_ENABLE("redis-session", "whether to enable sessions", "yes");
+ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no");
 
 if (PHP_REDIS != "no") {
-	var sources = "redis.c library.c igbinary\\igbinary.c igbinary\\hash_si.c igbinary\\hash_function.c";
+	var sources = "redis.c library.c redis_array.c redis_array_impl.c";
 	if (PHP_REDIS_SESSION != "no") {
 		AC_DEFINE('PHP_SESSION', 1);
 		sources += " redis_session.c";
 	}
-	
-	AC_DEFINE("PHP_EXPORTS", 1);
+
+	if (PHP_REDIS_IGBINARY != "no") {
+		CHECK_HEADER_ADD_INCLUDE("igbinary.h", "CFLAGS_REDIS", "ext\\igbinary");
+		AC_DEFINE('HAVE_REDIS_IGBINARY', 1);
+		ADD_EXTENSION_DEP('redis', 'igbinary');
+	}
+
 	EXTENSION("redis", sources);
 }
diff --git a/library.c b/library.c
index 7e7dc07233..7db595b823 100644
--- a/library.c
+++ b/library.c
@@ -18,6 +18,12 @@
 #include "library.h"
 #include 
 
+#ifdef _MSC_VER
+#define atoll _atoi64
+#define random rand
+#define usleep Sleep
+#endif
+
 #define UNSERIALIZE_ONLY_VALUES 0
 #define UNSERIALIZE_ALL 1
 
@@ -25,7 +31,7 @@ extern zend_class_entry *redis_ce;
 extern zend_class_entry *redis_exception_ce;
 extern zend_class_entry *spl_ce_RuntimeException;
 
-PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) {
+PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) {
 	if (!redis_sock->persistent) {
 		php_stream_close(redis_sock->stream);
 	} else {
@@ -33,7 +39,7 @@ PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) {
 	}
 }
 
-PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
+PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
 {
     int eof;
     int count = 0;
@@ -100,7 +106,7 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
     return 0;
 }
 
-PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
+PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
     char inbuf[1024];
 	int numElems;
     zval *z_tab;
@@ -135,7 +141,7 @@ PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS,
 /**
  * redis_sock_read_bulk_reply
  */
-PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC)
+PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC)
 {
     int offset = 0;
     size_t got;
@@ -175,7 +181,7 @@ PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_
 /**
  * redis_sock_read
  */
-PHPAPI char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC)
+PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC)
 {
     char inbuf[1024];
     char *resp = NULL;
@@ -298,6 +304,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) {
     smart_str buf = {0};
     int l = strlen(keyword);
 	char *dbl_str;
+    char dbl_decsep;
 	int dbl_len;
 
 	va_start(ap, format);
@@ -379,6 +386,7 @@ redis_cmd_format(char **ret, char *format, ...) {
 	va_list ap;
 	char *p = format;
 	char *dbl_str;
+    char dbl_decsep;
 	int dbl_len;
 
 	va_start(ap, format);
@@ -508,12 +516,14 @@ int redis_cmd_append_sstr_long(smart_str *str, long append) {
 int redis_cmd_append_sstr_dbl(smart_str *str, double value) {
     char *dbl_str;
     int dbl_len;
+    char dbl_decsep;
+    int retval;
 
     /// Convert to double
     REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, value);
 
     // Append the string
-    int retval = redis_cmd_append_sstr(str, dbl_str, dbl_len);
+    retval = redis_cmd_append_sstr(str, dbl_str, dbl_len);
 
     // Free our double string
     efree(dbl_str);
@@ -535,7 +545,7 @@ int redis_cmd_append_int(char **cmd, int cmd_len, int append) {
 	return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len);
 }
 
-PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
 
     char *response;
     int response_len;
@@ -559,7 +569,7 @@ PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
     }
 }
 
-PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
     char *response;
     int response_len;
     long l;
@@ -594,7 +604,7 @@ PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
     }
 }
 
-PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
     char *response;
     int response_len;
 	char *pos, *cur;
@@ -673,11 +683,17 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
  * Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code
  * to handle.
  */
-PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
+PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
     char *resp;
     int resp_len;
     zval *z_result, *z_sub_result;
-    
+
+    // Pointers for parsing
+    char *p = resp, *lpos = resp, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
+
+    // Key length, done flag
+    int klen, done = 0, is_numeric;
+
     // Make sure we can read a response from Redis
     if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) {
         RETURN_FALSE;
@@ -691,12 +707,6 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
     ALLOC_INIT_ZVAL(z_sub_result);
     array_init(z_sub_result);
 
-    // Pointers for parsing
-    char *p = resp, *lpos = resp, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
-
-    // Key length, done flag
-    int klen, done = 0, is_numeric;
-
     // While we've got more to parse
     while(!done) {
         // What character are we on
@@ -791,7 +801,7 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
     }
 }
 
-PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) {
+PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) {
 
     char *response;
     int response_len;
@@ -828,11 +838,11 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock
 	}
 }
 
-PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
     redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, NULL);
 }
 
-PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * z_tab, void *ctx) {
+PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * z_tab, void *ctx) {
 
     char *response;
     int response_len;
@@ -875,7 +885,7 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
 
 
 
-PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) {
+PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) {
 
 	/*
 	int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC);
@@ -928,16 +938,16 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA
     return 0;
 }
 
-PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
 
 	return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1);
 }
 
-PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
 	return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0);
 }
 
-PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
 
 	char *response;
 	int response_len;
@@ -969,7 +979,7 @@ PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock
 	}
 }
 
-PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
 
     char *response;
     int response_len;
@@ -999,7 +1009,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis
 }
 
 /* like string response, but never unserialized. */
-PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
 
     char *response;
     int response_len;
@@ -1022,7 +1032,7 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
 /**
  * redis_sock_create
  */
-PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port,
+PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port,
                                     double timeout, int persistent, char *persistent_id,
                                     long retry_interval,
                                     zend_bool lazy_connect)
@@ -1070,7 +1080,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por
 /**
  * redis_sock_connect
  */
-PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC)
+PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC)
 {
     struct timeval tv, read_tv, *tv_ptr = NULL;
     char *host = NULL, *persistent_id = NULL, *errstr = NULL;
@@ -1146,7 +1156,7 @@ PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC)
 /**
  * redis_sock_server_open
  */
-PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC)
+PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC)
 {
     int res = -1;
 
@@ -1173,7 +1183,7 @@ PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRML
 /**
  * redis_sock_disconnect
  */
-PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC)
+PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC)
 {
     if (redis_sock == NULL) {
 	    return 1;
@@ -1198,7 +1208,7 @@ PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC)
     return 0;
 }
 
-PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock)
+PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock)
 {
     char *cmd;
 	int response_len, cmd_len;
@@ -1225,7 +1235,7 @@ PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_so
 /**
  * redis_sock_set_err
  */
-PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) {
+PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) {
 	// Allocate/Reallocate our last error member
 	if(msg != NULL && msg_len > 0) {
 		if(redis_sock->err == NULL) {
@@ -1256,7 +1266,7 @@ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_le
 /**
  * redis_sock_read_multibulk_reply
  */
-PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
     char inbuf[1024];
 	int numElems;
@@ -1303,7 +1313,7 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo
 /**
  * Like multibulk reply, but don't touch the values, they won't be compressed. (this is used by HKEYS).
  */
-PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
     char inbuf[1024];
 	int numElems;
@@ -1347,7 +1357,7 @@ PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, Red
     return 0;
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                                      zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only)
 {
@@ -1379,7 +1389,7 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re
 /**
  * redis_sock_read_multibulk_reply_assoc
  */
-PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
     char inbuf[1024], *response;
     int response_len;
@@ -1446,7 +1456,7 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, R
 /**
  * redis_sock_write
  */
-PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC)
+PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC)
 {
 	if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) {
 		zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC);
@@ -1461,7 +1471,7 @@ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_D
 /**
  * redis_free_socket
  */
-PHPAPI void redis_free_socket(RedisSock *redis_sock)
+PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
 {
     if(redis_sock->prefix) {
 		efree(redis_sock->prefix);
@@ -1479,7 +1489,7 @@ PHPAPI void redis_free_socket(RedisSock *redis_sock)
     efree(redis_sock);
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC) {
 #if ZEND_MODULE_API_NO >= 20100000
 	php_serialize_data_t ht;
@@ -1555,7 +1565,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_
 	return 0;
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) {
 
 	php_unserialize_data_t var_hash;
@@ -1606,7 +1616,7 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **re
 	return 0;
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) {
 	int ret_len;
 	char *ret;
@@ -1629,7 +1639,7 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) {
  * Processing for variant reply types (think EVAL)
  */
 
-PHPAPI int
+PHP_REDIS_API int
 redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) {
     // Handle EOF
 	if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) {
@@ -1657,7 +1667,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_siz
 	return 0;
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) {
 	// Make sure we haven't lost the connection, even trying to reconnect
 	if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) {
@@ -1691,7 +1701,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *
 /*
  * Read a single line response, having already consumed the reply-type byte
  */
-PHPAPI int
+PHP_REDIS_API int
 redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC) {
 	// Buffer to read our single line reply
 	char inbuf[1024];
@@ -1721,7 +1731,7 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval
 	return 0;
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) {
 	// Attempt to read the bulk reply
 	char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC);
@@ -1736,7 +1746,7 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC)
 	}
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) {
 	int reply_info;
 	REDIS_REPLY_TYPE reply_type;
@@ -1784,7 +1794,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret
 	return 0;
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
 	// Reply type, and reply size vars
 	REDIS_REPLY_TYPE reply_type;
diff --git a/library.h b/library.h
index 7b1d0383ad..ec34987c7a 100644
--- a/library.h
+++ b/library.h
@@ -11,44 +11,44 @@ int redis_cmd_append_sstr_long(smart_str *str, long append);
 int redis_cmd_append_int(char **cmd, int cmd_len, int append);
 int redis_cmd_append_sstr_dbl(smart_str *str, double value);
 
-PHPAPI char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC);
+PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC);
 
-PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx);
+PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx);
 typedef void (*SuccessCallback)(RedisSock *redis_sock);
-PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback);
-PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect);
-PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC);
-PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC);
-PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC);
-PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock);
-PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC);
-PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx);
-PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only);
-PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
-PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
-PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC);
+PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback);
+PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect);
+PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC);
+PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC);
+PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC);
+PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock);
+PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC);
+PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx);
+PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only);
+PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
+PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
+PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC);
 //PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC);
-PHPAPI void redis_free_socket(RedisSock *redis_sock);
-PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock);
-PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len);
+PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock);
+PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock);
+PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len);
 
-PHPAPI int
+PHP_REDIS_API int
 redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC);
-PHPAPI int
+PHP_REDIS_API int
 redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC);
 
-PHPAPI int
+PHP_REDIS_API int
 redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC);
 
 
@@ -56,17 +56,16 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **re
 * Variant Read methods, mostly to implement eval
 */
 
-PHPAPI int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC);
-PHPAPI int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC);
-PHPAPI int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC);
-PHPAPI int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC);
-PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
+PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC);
+PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC);
+PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC);
+PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC);
+PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
 
-PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
+PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
 
 #if ZEND_MODULE_API_NO >= 20100000
 #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
-	char dbl_decsep; \
 	dbl_decsep = '.'; \
     dbl_str = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \
 	dbl_len = strlen(dbl_str);
diff --git a/php_redis.h b/php_redis.h
index 879503aa38..905c7b3b88 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -209,32 +209,32 @@ PHP_RINIT_FUNCTION(redis);
 PHP_RSHUTDOWN_FUNCTION(redis);
 PHP_MINFO_FUNCTION(redis);
 
-PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent);
-PHPAPI void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int count);
-PHPAPI int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len,
+PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent);
+PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int count);
+PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len,
 									 int min_argc, RedisSock **redis_sock, int has_timeout, int all_keys, int can_serialize);
-PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha);
+PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha);
 typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI void generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ResultCallback result_callback);
-PHPAPI void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...);
-PHPAPI void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...);
+PHP_REDIS_API void generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ResultCallback result_callback);
+PHP_REDIS_API void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...);
+PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...);
 
-PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd);
-PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd);
+PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd);
+PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd);
 
-PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC);
-PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC);
+PHP_REDIS_API void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC);
+PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC);
 
-PHPAPI int get_flag(zval *object TSRMLS_DC);
-PHPAPI void set_flag(zval *object, int new_flag TSRMLS_DC);
+PHP_REDIS_API int get_flag(zval *object TSRMLS_DC);
+PHP_REDIS_API void set_flag(zval *object, int new_flag TSRMLS_DC);
 
-PHPAPI int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems);
+PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems);
 
 /* pipeline */
-PHPAPI request_item* get_pipeline_head(zval *object);
-PHPAPI void set_pipeline_head(zval *object, request_item *head);
-PHPAPI request_item* get_pipeline_current(zval *object);
-PHPAPI void set_pipeline_current(zval *object, request_item *current);
+PHP_REDIS_API request_item* get_pipeline_head(zval *object);
+PHP_REDIS_API void set_pipeline_head(zval *object, request_item *head);
+PHP_REDIS_API request_item* get_pipeline_current(zval *object);
+PHP_REDIS_API void set_pipeline_current(zval *object, request_item *current);
 
 #ifndef _MSC_VER
 ZEND_BEGIN_MODULE_GLOBALS(redis)
diff --git a/redis.c b/redis.c
index ed4ec9d99b..f869d0015f 100644
--- a/redis.c
+++ b/redis.c
@@ -69,7 +69,9 @@ PHP_INI_BEGIN()
 	PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL)
 PHP_INI_END()
 
+#ifdef ZTS
 ZEND_DECLARE_MODULE_GLOBALS(redis)
+#endif
 
 static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, __construct, NULL, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC)
@@ -307,7 +309,7 @@ zend_module_entry redis_module_entry = {
 ZEND_GET_MODULE(redis)
 #endif
 
-PHPAPI zend_class_entry *redis_get_exception_base(int root TSRMLS_DC)
+PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC)
 {
 #if HAVE_SPL
         if (!root) {
@@ -373,7 +375,7 @@ static void redis_destructor_redis_sock(zend_rsrc_list_entry * rsrc TSRMLS_DC)
 /**
  * redis_sock_get
  */
-PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw)
+PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw)
 {
 
     zval **socket;
@@ -412,7 +414,7 @@ PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_thr
  * redis_sock_get_direct
  * Returns our attached RedisSock pointer if we're connected
  */
-PHPAPI RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) {
+PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) {
     zval *object;
     RedisSock *redis_sock;
 
@@ -550,12 +552,13 @@ PHP_METHOD(Redis, __construct)
     Public Destructor
  */
 PHP_METHOD(Redis,__destruct) {
+    RedisSock *redis_sock;
+
 	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) {
 		RETURN_FALSE;
 	}
 
 	// Grab our socket
-	RedisSock *redis_sock;
 	if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 1) < 0) {
 		RETURN_FALSE;
 	}
@@ -598,7 +601,7 @@ PHP_METHOD(Redis, pconnect)
 }
 /* }}} */
 
-PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
+PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
 	zval *object;
 	zval **socket;
 	int host_len, id;
@@ -910,7 +913,7 @@ PHP_METHOD(Redis, set) {
     REDIS_PROCESS_RESPONSE(redis_boolean_response);
 }
 
-PHPAPI void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
+PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
 
     zval *object;
     RedisSock *redis_sock;
@@ -1231,7 +1234,7 @@ PHP_METHOD(Redis, ping)
 }
 /* }}} */
 
-PHPAPI void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int count) {
+PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int count) {
 
     zval *object;
     RedisSock *redis_sock;
@@ -1523,12 +1526,12 @@ PHP_METHOD(Redis, delete)
 }
 /* }}} */
 
-PHPAPI void redis_set_watch(RedisSock *redis_sock)
+PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock)
 {
     redis_sock->watching = 1;
 }
 
-PHPAPI void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+PHP_REDIS_API void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
     redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_set_watch);
 }
@@ -1551,12 +1554,12 @@ PHP_METHOD(Redis, watch)
 }
 /* }}} */
 
-PHPAPI void redis_clear_watch(RedisSock *redis_sock)
+PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock)
 {
     redis_sock->watching = 0;
 }
 
-PHPAPI void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+PHP_REDIS_API void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
     redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_clear_watch);
 }
@@ -1808,7 +1811,7 @@ PHP_METHOD(Redis, strlen)
 	REDIS_PROCESS_RESPONSE(redis_long_response);
 }
 
-PHPAPI void
+PHP_REDIS_API void
 generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
     zval *object;
     RedisSock *redis_sock;
@@ -1931,7 +1934,7 @@ PHP_METHOD(Redis, rPushx)
 	generic_push_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RPUSHX", sizeof("RPUSHX")-1);
 }
 
-PHPAPI void
+PHP_REDIS_API void
 generic_pop_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
 
     zval *object;
@@ -2426,7 +2429,7 @@ PHP_METHOD(Redis, sMembers)
 }
 /* }}} */
 
-PHPAPI int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len,
+PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len,
 									 int min_argc, RedisSock **out_sock, int has_timeout, int all_keys, int can_serialize)
 {
     zval **z_args, *z_array;
@@ -2959,7 +2962,7 @@ PHP_METHOD(Redis, sort) {
     }
 }
 
-PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha) {
+PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha) {
 
     zval *object;
     RedisSock *redis_sock;
@@ -3170,7 +3173,7 @@ PHP_METHOD(Redis, sortDescAlpha)
 }
 /* }}} */
 
-PHPAPI void generic_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
+PHP_REDIS_API void generic_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
     zval *object;
     RedisSock *redis_sock;
     char *key = NULL, *cmd, *t;
@@ -3266,7 +3269,7 @@ PHP_METHOD(Redis, lSet) {
 }
 /* }}} */
 
-PHPAPI void generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ResultCallback result_callback) {
+PHP_REDIS_API void generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ResultCallback result_callback) {
     zval *object;
     RedisSock *redis_sock;
 
@@ -3286,7 +3289,7 @@ PHPAPI void generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int
     REDIS_PROCESS_RESPONSE(result_callback);
 }
 
-PHPAPI void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...) {
+PHP_REDIS_API void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...) {
     generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len, redis_boolean_response);
 }
 
@@ -3312,7 +3315,7 @@ PHP_METHOD(Redis, bgSave)
 }
 /* }}} */
 
-PHPAPI void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...) {
+PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...) {
 
     zval *object;
     RedisSock *redis_sock;
@@ -3438,7 +3441,7 @@ PHP_METHOD(Redis, persist) {
 }
 /* }}} */
 
-PHPAPI void generic_ttl(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
+PHP_REDIS_API void generic_ttl(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
     zval *object;
     RedisSock *redis_sock;
 
@@ -3588,7 +3591,7 @@ PHP_METHOD(Redis, move) {
 }
 /* }}} */
 
-PHPAPI void
+PHP_REDIS_API void
 generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *, zval *, void *)) {
 
     zval *object;
@@ -3703,7 +3706,7 @@ PHP_METHOD(Redis, msetnx) {
 }
 /* }}} */
 
-PHPAPI void common_rpoplpush(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+PHP_REDIS_API void common_rpoplpush(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 		char *srckey, int srckey_len, char *dstkey, int dstkey_len, int timeout) {
 
 	char *cmd;
@@ -3786,7 +3789,9 @@ PHP_METHOD(Redis, zAdd) {
     char *key, *val;
     int val_free, key_free = 0;
 	char *dbl_str;
+    char dbl_decsep;
 	int dbl_len;
+    smart_str buf = {0};
 
 	zval **z_args;
 	int argc = ZEND_NUM_ARGS(), i;
@@ -3817,7 +3822,6 @@ PHP_METHOD(Redis, zAdd) {
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
 	/* start building the command */
-    smart_str buf = {0};
 	smart_str_appendc(&buf, '*');
 	smart_str_append_long(&buf, argc + 1); /* +1 for ZADD command */
 	smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
@@ -4053,7 +4057,7 @@ PHP_METHOD(Redis, zReverseRange)
 }
 /* }}} */
 
-PHPAPI void
+PHP_REDIS_API void
 redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
 
     zval *object, *z_options = NULL, **z_limit_val_pp = NULL, **z_withscores_val_pp = NULL;
@@ -4266,7 +4270,7 @@ PHP_METHOD(Redis, zScore)
 /* }}} */
 
 
-PHPAPI void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
+PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
     zval *object;
     RedisSock *redis_sock;
     char *key = NULL, *val = NULL, *cmd;
@@ -4314,7 +4318,7 @@ PHP_METHOD(Redis, zRevRank) {
 }
 /* }}} */
 
-PHPAPI void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
+PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
     zval *object;
     RedisSock *redis_sock;
 
@@ -4356,14 +4360,14 @@ PHP_METHOD(Redis, zIncrBy)
 }
 /* }}} */
 
-PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) {
+PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) {
     zval *object, *z_keys, *z_weights = NULL, **z_data;
     HashTable *ht_keys, *ht_weights = NULL;
     RedisSock *redis_sock;
     smart_str cmd = {0};
     HashPosition ptr;
     char *store_key, *agg_op = NULL;
-    int cmd_arg_count = 2, store_key_len, agg_op_len = 0, keys_count;
+    int cmd_arg_count = 2, store_key_len, agg_op_len = 0, keys_count, key_free;
 
     // Grab our parameters
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",
@@ -4422,7 +4426,7 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
     redis_cmd_init_sstr(&cmd, cmd_arg_count, command, command_len);
 
     // Prefix our key if necessary and add the output key
-    int key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC);
+    key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC);
     redis_cmd_append_sstr(&cmd, store_key, store_key_len);
     if(key_free) efree(store_key);
 
@@ -4530,7 +4534,7 @@ PHP_METHOD(Redis, zUnion) {
 
 /* hashes */
 
-PHPAPI void
+PHP_REDIS_API void
 generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *, zval *, void *)) {
     zval *object;
     RedisSock *redis_sock;
@@ -4636,7 +4640,7 @@ PHP_METHOD(Redis, hLen)
 }
 /* }}} */
 
-PHPAPI RedisSock*
+PHP_REDIS_API RedisSock*
 generic_hash_command_2(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len, char **out_cmd, int *out_len) {
 
     zval *object;
@@ -4697,7 +4701,7 @@ PHP_METHOD(Redis, hExists)
 
 }
 
-PHPAPI RedisSock*
+PHP_REDIS_API RedisSock*
 generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) {
 
     zval *object;
@@ -4785,7 +4789,7 @@ PHP_METHOD(Redis, hGetAll) {
 	REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings);
 }
 
-PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC) {
+PHP_REDIS_API void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC) {
 
     zval *z_ret;
 	HashTable *keytable;
@@ -5091,7 +5095,7 @@ PHP_METHOD(Redis, hMset)
 }
 
 
-PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) {
+PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) {
 
 	char *response;
 	int response_len, ret = 0;
@@ -5183,7 +5187,7 @@ PHP_METHOD(Redis, discard)
 	redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
 }
 
-PHPAPI int redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock)
+PHP_REDIS_API int redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock)
 {
     zval *z_tab;
     MAKE_STD_ZVAL(z_tab);
@@ -5202,7 +5206,7 @@ PHPAPI int redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS
 
 }
 /* redis_sock_read_multibulk_multi_reply */
-PHPAPI int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
+PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
                                       RedisSock *redis_sock)
 {
 
@@ -5348,11 +5352,11 @@ PHP_METHOD(Redis, exec)
 	}
 }
 
-PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) {
+PHP_REDIS_API void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) {
 	item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC);
 }
 
-PHPAPI int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
+PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
 							RedisSock *redis_sock, zval *z_tab, int numElems)
 {
 
@@ -5423,7 +5427,7 @@ PHP_METHOD(Redis, publish)
     REDIS_PROCESS_RESPONSE(redis_long_response);
 }
 
-PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd)
+PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd)
 {
 	zval *object, *array, **data;
     HashTable *arr_hash;
@@ -5433,7 +5437,8 @@ PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd)
     int cmd_len, array_count, key_len, key_free;
 	zval *z_tab, **tmp;
 	char *type_response;
-	
+    int is_pmsg, tab_idx;
+
 	// Function call information
 	zend_fcall_info z_callback;
 	zend_fcall_info_cache z_callback_cache;
@@ -5525,8 +5530,8 @@ PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd)
 		/* call the callback with this z_tab in argument */
 		zval **type, **channel, **pattern, **data;
 	    z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
-	    int is_pmsg, tab_idx = 1;
-		
+	    tab_idx = 1;
+
 		if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) {
 			//ERROR
 			break;
@@ -5615,7 +5620,7 @@ PHP_METHOD(Redis, subscribe) {
  * );
  **/
 
-PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd)
+PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd)
 {
     zval *object, *array, **data;
     HashTable *arr_hash;
@@ -5988,7 +5993,7 @@ PHP_METHOD(Redis, slowlog) {
 }
 
 // Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
-PHPAPI int
+PHP_REDIS_API int
 redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) {
 	zval **elem;
 	HashTable *args_hash;
@@ -6130,7 +6135,7 @@ PHP_METHOD(Redis, eval)
     REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
 }
 
-PHPAPI int
+PHP_REDIS_API int
 redis_build_script_exists_cmd(char **ret, zval **argv, int argc) {
 	// Our command length and iterator
 	int cmd_len = 0, i;
diff --git a/redis_array.c b/redis_array.c
index ecc158eab6..12fe98308a 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -116,7 +116,7 @@ void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
 /**
  * redis_array_get
  */
-PHPAPI int redis_array_get(zval *id, RedisArray **ra TSRMLS_DC)
+PHP_REDIS_API int redis_array_get(zval *id, RedisArray **ra TSRMLS_DC)
 {
 
     zval **socket;
@@ -198,6 +198,7 @@ PHP_METHOD(RedisArray, __construct)
 	HashTable *hPrev = NULL, *hOpts = NULL;
   long l_retry_interval = 0;
   	zend_bool b_lazy_connect = 0;
+  	zval **z_retry_interval_pp;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) {
 		RETURN_FALSE;
@@ -245,7 +246,6 @@ PHP_METHOD(RedisArray, __construct)
 		}
 
 		/* extract retry_interval option. */
-		zval **z_retry_interval_pp;
         if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) {
 			if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) {
 				if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) {
@@ -961,6 +961,8 @@ PHP_METHOD(RedisArray, mset)
 	unsigned int key_len, free_idx = 0;
 	int type, *key_lens;
 	unsigned long idx;
+	int found;
+	zval *z_tmp;
 
 	/* Multi/exec support */
 	HANDLE_MULTI_EXEC("MSET");
@@ -1028,13 +1030,12 @@ PHP_METHOD(RedisArray, mset)
 		redis_inst = ra->redis[n];
 
 		/* copy args */
-		int found = 0;
+		found = 0;
 		MAKE_STD_ZVAL(z_argarray);
 		array_init(z_argarray);
 		for(i = 0; i < argc; ++i) {
 			if(pos[i] != n) continue;
 
-			zval *z_tmp;
 			ALLOC_ZVAL(z_tmp);
 			*z_tmp = *argv[i];
 			zval_copy_ctor(z_tmp);
diff --git a/redis_array_impl.c b/redis_array_impl.c
index c3a1f36522..65ccd61fbe 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -430,6 +430,7 @@ zval *
 ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) {
 
         uint32_t hash;
+        uint64_t h64;
         char *out;
         int pos, out_len;
 
@@ -449,7 +450,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D
                 efree(out);
         
                 /* get position on ring */
-                uint64_t h64 = hash;
+                h64 = hash;
                 h64 *= ra->count;
                 h64 /= 0xffffffff;
                 pos = (int)h64;
@@ -544,13 +545,13 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) {
 
 	/* Initialize key array */
 	zval *z_keys, **z_entry_pp;
+	HashPosition pos;
 	MAKE_STD_ZVAL(z_keys);
 #if PHP_VERSION_ID > 50300
 	array_init_size(z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs)));
 #else
 	array_init(z_keys);
 #endif
-	HashPosition pos;
 
 	/* Go through input array and add values to the key array */
 	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z_pairs), &pos);
diff --git a/redis_session.c b/redis_session.c
index 591f07b84e..e1d107f2ee 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -70,12 +70,12 @@ typedef struct {
 
 } redis_pool;
 
-PHPAPI redis_pool*
+PHP_REDIS_API redis_pool*
 redis_pool_new(TSRMLS_D) {
 	return ecalloc(1, sizeof(redis_pool));
 }
 
-PHPAPI void
+PHP_REDIS_API void
 redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight,
                 int database, char *prefix, char *auth TSRMLS_DC) {
 
@@ -96,7 +96,7 @@ redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight,
 	pool->totalWeight += weight;
 }
 
-PHPAPI void
+PHP_REDIS_API void
 redis_pool_free(redis_pool *pool TSRMLS_DC) {
 
 	redis_pool_member *rpm, *next;
@@ -148,15 +148,13 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) {
     efree(cmd);
 }
 
-PHPAPI redis_pool_member *
+PHP_REDIS_API redis_pool_member *
 redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) {
-
+	redis_pool_member *rpm = pool->head;
 	unsigned int pos, i;
 	memcpy(&pos, key, sizeof(pos));
 	pos %= pool->totalWeight;
 
-	redis_pool_member *rpm = pool->head;
-
 	for(i = 0; i < pool->totalWeight;) {
 		if(pos >= i && pos < i + rpm->weight) {
 			int needs_auth = 0;
@@ -188,6 +186,7 @@ PS_OPEN_FUNC(redis)
 	php_url *url;
 	zval *params, **param;
 	int i, j, path_len;
+	RedisSock *redis_sock;
 
 	redis_pool *pool = redis_pool_new(TSRMLS_C);
 
@@ -276,7 +275,6 @@ PS_OPEN_FUNC(redis)
 				return FAILURE;
 			}
 
-			RedisSock *redis_sock;
             if(url->host) {
                     redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval, 0);
             } else { /* unix */

From 5655f0574e1df30c7c39d67dea40fb9fece334f4 Mon Sep 17 00:00:00 2001
From: vostok4 
Date: Wed, 16 Oct 2013 11:39:51 +0200
Subject: [PATCH 0089/1986] Last fix for win32 compiles

This fixes the rc command using <> characters which breaks it.
---
 CREDITS | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/CREDITS b/CREDITS
index 5959659911..c501060a77 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1,4 +1,4 @@
 Redis client extension for PHP
-Alfonso Jimenez 
-Nasreddine Bouafif 
-Nicolas Favre-Felix 
+Alfonso Jimenez (yo@alfonsojimenez.com)
+Nasreddine Bouafif (n.bouafif@owlient.eu)
+Nicolas Favre-Felix (n.favre-felix@owlient.eu)

From 978fbcf6fc355fcd344feb76ab4b7d6b77e9f7c1 Mon Sep 17 00:00:00 2001
From: vostok4 
Date: Fri, 18 Oct 2013 14:37:06 +0200
Subject: [PATCH 0090/1986] Further fixes for building on VC9 Win32, C89
 compliance

Include win32/php_stdint.h on Win32 (fixes compilation on VC9)
Change C++ comments to C89 style (to adhere to PHP project)
---
 library.c          | 188 ++++++++++++++++++++++-----------------------
 library.h          |   2 +-
 redis_array.c      |  24 +++---
 redis_array.h      |   7 +-
 redis_array_impl.c |  28 +++----
 redis_array_impl.h |   5 ++
 6 files changed, 132 insertions(+), 122 deletions(-)

diff --git a/library.c b/library.c
index 7db595b823..4fd523de35 100644
--- a/library.c
+++ b/library.c
@@ -67,9 +67,9 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
 			redis_sock->mode   = ATOMIC;
             redis_sock->watching = 0;
 	}
-    // Wait for a while before trying to reconnect
+    /* Wait for a while before trying to reconnect */
     if (redis_sock->retry_interval) {
-    	// Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time
+    	/* Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time */
    		long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval));
     	usleep(retry_interval);
     }
@@ -79,7 +79,7 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
         }
     }
 
-    // Reselect the DB.
+    /* Reselect the DB. */
     if (count && redis_sock->dbNumber) {
         char *cmd, *response;
         int cmd_len, response_len;
@@ -157,7 +157,7 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes
     } else {
         char c;
         int i;
-        
+
 		reply = emalloc(bytes+1);
 
         while(offset < bytes) {
@@ -225,7 +225,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D
 
         case '+':
         case ':':
-	    // Single Line Reply
+	    /* Single Line Reply */
             /* :123\r\n */
             *buf_len = strlen(inbuf) - 2;
             if(*buf_len >= 2) {
@@ -274,10 +274,10 @@ integer_length(int i) {
 
 int
 redis_cmd_format_header(char **ret, char *keyword, int arg_count) {
-	// Our return buffer
+	/* Our return buffer */
 	smart_str buf = {0};
 
-	// Keyword length
+	/* Keyword length */
 	int l = strlen(keyword);
 
 	smart_str_appendc(&buf, '*');
@@ -289,10 +289,10 @@ redis_cmd_format_header(char **ret, char *keyword, int arg_count) {
 	smart_str_appendl(&buf, keyword, l);
 	smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
 
-	// Set our return pointer
+	/* Set our return pointer */
 	*ret = buf.c;
 
-	// Return the length
+	/* Return the length */
 	return buf.len;
 }
 
@@ -439,26 +439,26 @@ redis_cmd_format(char **ret, char *format, ...) {
  * Append a command sequence to a Redis command
  */
 int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) {
-	// Smart string buffer
+	/* Smart string buffer */
 	smart_str buf = {0};
 
-	// Append the current command to our smart_str
+	/* Append the current command to our smart_str */
 	smart_str_appendl(&buf, *cmd, cmd_len);
 
-	// Append our new command sequence
+	/* Append our new command sequence */
 	smart_str_appendc(&buf, '$');
 	smart_str_append_long(&buf, append_len);
 	smart_str_appendl(&buf, _NL, sizeof(_NL) -1);
 	smart_str_appendl(&buf, append, append_len);
 	smart_str_appendl(&buf, _NL, sizeof(_NL) -1);
 
-	// Free our old command
+	/* Free our old command */
 	efree(*cmd);
 
-	// Set our return pointer
+	/* Set our return pointer */
 	*cmd = buf.c;
 
-	// Return new command length
+	/* Return new command length */
 	return buf.len;
 }
 
@@ -488,7 +488,7 @@ int redis_cmd_append_sstr(smart_str *str, char *append, int append_len) {
     smart_str_appendl(str, append, append_len);
     smart_str_appendl(str, _NL, sizeof(_NL) - 1);
 
-    // Return our new length
+    /* Return our new length */
     return str->len;
 }
 
@@ -519,16 +519,16 @@ int redis_cmd_append_sstr_dbl(smart_str *str, double value) {
     char dbl_decsep;
     int retval;
 
-    /// Convert to double
+    /* Convert to double */
     REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, value);
 
-    // Append the string
+    /* Append the string */
     retval = redis_cmd_append_sstr(str, dbl_str, dbl_len);
 
-    // Free our double string
+    /* Free our double string */
     efree(dbl_str);
 
-    // Return new length
+    /* Return new length */
     return retval;
 }
 
@@ -538,10 +538,10 @@ int redis_cmd_append_sstr_dbl(smart_str *str, double value) {
 int redis_cmd_append_int(char **cmd, int cmd_len, int append) {
 	char int_buf[32];
 
-	// Conver to an int, capture length
+	/* Conver to an int, capture length */
 	int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append);
 
-	// Return the new length
+	/* Return the new length */
 	return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len);
 }
 
@@ -688,28 +688,28 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo
     int resp_len;
     zval *z_result, *z_sub_result;
 
-    // Pointers for parsing
+    /* Pointers for parsing */
     char *p = resp, *lpos = resp, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
 
-    // Key length, done flag
+    /* Key length, done flag */
     int klen, done = 0, is_numeric;
 
-    // Make sure we can read a response from Redis
+    /* Make sure we can read a response from Redis */
     if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) {
         RETURN_FALSE;
     }
 
-    // Allocate memory for our response
+    /* Allocate memory for our response */
     MAKE_STD_ZVAL(z_result);
     array_init(z_result);
 
-    // Allocate memory for one user (there should be at least one, namely us!)
+    /* Allocate memory for one user (there should be at least one, namely us!) */
     ALLOC_INIT_ZVAL(z_sub_result);
     array_init(z_sub_result);
 
-    // While we've got more to parse
+    /* While we've got more to parse */
     while(!done) {
-        // What character are we on
+        /* What character are we on */
         switch(*p) {
             /* We're done */
             case '\0':
@@ -718,23 +718,23 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo
             /* \n, ' ' mean we can pull a k/v pair */
             case '\n':
             case ' ':
-                // Grab our value
+                /* Grab our value */
                 vpos = lpos;
 
-                // There is some communication error or Redis bug if we don't
-                // have a key and value, but check anyway.
+                /* There is some communication error or Redis bug if we don't
+                   have a key and value, but check anyway. */
                 if(kpos && vpos) {
-                    // Allocate, copy in our key
+                    /* Allocate, copy in our key */
                     key = emalloc(klen + 1);
                     strncpy(key, kpos, klen);
                     key[klen] = 0;
 
-                    // Allocate, copy in our value
+                    /* Allocate, copy in our value */
                     value = emalloc(p-lpos+1);
                     strncpy(value,lpos,p-lpos+1);
                     value[p-lpos]=0;
 
-                    // Treat numbers as numbers, strings as strings
+                    /* Treat numbers as numbers, strings as strings */
                     is_numeric = 1;
                     for(p2 = value; *p; ++p) {
                         if(*p < '0' || *p > '9') {
@@ -743,7 +743,7 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo
                         }
                     }
 
-                    // Add as a long or string, depending
+                    /* Add as a long or string, depending */
                     if(is_numeric == 1) {
                         add_assoc_long(z_sub_result, key, atol(value));
                         efree(value);
@@ -751,50 +751,50 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo
                         add_assoc_string(z_sub_result, key, value, 0);
                     }
 
-                    // If we hit a '\n', then we can add this user to our list
+                    /* If we hit a '\n', then we can add this user to our list */
                     if(*p == '\n') {
-                        // Add our user
+                        /* Add our user */
                         add_next_index_zval(z_result, z_sub_result);
 
-                        // If we have another user, make another one
+                        /* If we have another user, make another one */
                         if(*(p+1) != '\0') {
                             ALLOC_INIT_ZVAL(z_sub_result);
                             array_init(z_sub_result);
                         }
                     }
-                    
-                    // Free our key
+
+                    /* Free our key */
                     efree(key);
                 } else {
-                    // Something is wrong
+                    /* Something is wrong */
                     efree(resp);
                     RETURN_FALSE;
                 }
 
-                // Move forward
+                /* Move forward */
                 lpos = p + 1;
 
                 break;
             /* We can pull the key and null terminate at our sep */
             case '=':
-                // Key, key length
+                /* Key, key length */
                 kpos = lpos;
                 klen = p - lpos;
 
-                // Move forward
+                /* Move forward */
                 lpos = p + 1;
 
                 break;
         }
 
-        // Increment
+        /* Increment */
         p++;
     }
 
-    // Free our respoonse
+    /* Free our respoonse */
     efree(resp);
 
-    IF_MULTI_OR_PIPELINE() { 
+    IF_MULTI_OR_PIPELINE() {
         add_next_index_zval(z_tab, z_result);
     } else {
         RETVAL_ZVAL(z_result, 0, 1);
@@ -1236,7 +1236,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
  * redis_sock_set_err
  */
 PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) {
-	// Allocate/Reallocate our last error member
+	/* Allocate/Reallocate our last error member */
 	if(msg != NULL && msg_len > 0) {
 		if(redis_sock->err == NULL) {
 			redis_sock->err = emalloc(msg_len + 1);
@@ -1244,22 +1244,22 @@ PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int
 			redis_sock->err = erealloc(redis_sock->err, msg_len +1);
 		}
 
-		// Copy in our new error message, set new length, and null terminate
+		/* Copy in our new error message, set new length, and null terminate */
 		memcpy(redis_sock->err, msg, msg_len);
 		redis_sock->err[msg_len] = '\0';
 		redis_sock->err_len = msg_len;
 	} else {
-		// Free our last error
+		/* Free our last error */
 		if(redis_sock->err != NULL) {
 			efree(redis_sock->err);
 		}
 
-		// Set to null, with zero length
+		/* Set to null, with zero length */
 		redis_sock->err = NULL;
 		redis_sock->err_len = 0;
 	}
 
-	// Success
+	/* Success */
 	return 0;
 }
 
@@ -1306,7 +1306,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
         *return_value = *z_multi_result;
         efree(z_multi_result);
     }
-    //zval_copy_ctor(return_value);
+    /*zval_copy_ctor(return_value); */
     return 0;
 }
 
@@ -1353,7 +1353,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETE
         *return_value = *z_multi_result;
         efree(z_multi_result);
     }
-    //zval_copy_ctor(return_value);
+    /*zval_copy_ctor(return_value); */
     return 0;
 }
 
@@ -1620,7 +1620,7 @@ PHP_REDIS_API int
 redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) {
 	int ret_len;
 	char *ret;
-	
+
 	if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) {
 		return 0;
 	}
@@ -1641,60 +1641,60 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) {
 
 PHP_REDIS_API int
 redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) {
-    // Handle EOF
+    /* Handle EOF */
 	if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) {
         return -1;
     }
 
 	if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) {
-		// Close, put our socket state into error
+		/* Close, put our socket state into error */
 		redis_stream_close(redis_sock TSRMLS_CC);
 		redis_sock->stream = NULL;
 		redis_sock->status = REDIS_SOCK_STATUS_FAILED;
 		redis_sock->mode = ATOMIC;
 		redis_sock->watching = 0;
 
-		// Throw a read error exception
+		/* Throw a read error exception */
 		zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC);
 	}
 
-	// We don't need \r\n
+	/* We don't need \r\n */
 	*line_size-=2;
 	buf[*line_size]='\0';
 
 
-	// Success!
+	/* Success! */
 	return 0;
 }
 
 PHP_REDIS_API int
 redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) {
-	// Make sure we haven't lost the connection, even trying to reconnect
+	/* Make sure we haven't lost the connection, even trying to reconnect */
 	if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) {
-		// Failure
+		/* Failure */
 		return -1;
 	}
 
-	// Attempt to read the reply-type byte
+	/* Attempt to read the reply-type byte */
 	if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) {
 		zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC);
 	}
 
-	// If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here
+	/* If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here */
 	if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) {
-		// Buffer to hold size information
+		/* Buffer to hold size information */
 		char inbuf[255];
 
-		// Read up to our newline
+		/* Read up to our newline */
 		if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) {
 			return -1;
 		}
 
-		// Set our size response
+		/* Set our size response */
 		*reply_info = atoi(inbuf);
 	}
 
-	// Success!
+	/* Success! */
 	return 0;
 }
 
@@ -1703,28 +1703,28 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *
  */
 PHP_REDIS_API int
 redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC) {
-	// Buffer to read our single line reply
+	/* Buffer to read our single line reply */
 	char inbuf[1024];
 	size_t line_size;
 
-	// Attempt to read our single line reply
+	/* Attempt to read our single line reply */
 	if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) {
 		return -1;
 	}
 
-	// If this is an error response, check if it is a SYNC error, and throw in that case
+	/* If this is an error response, check if it is a SYNC error, and throw in that case */
 	if(reply_type == TYPE_ERR) {
 		if(memcmp(inbuf, "ERR SYNC", 9) == 0) {
 			zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC);
 		}
 
-		// Set our last error
+		/* Set our last error */
 		redis_sock_set_err(redis_sock, inbuf, line_size);
 
-		// Set our response to FALSE
+		/* Set our response to FALSE */
 		ZVAL_FALSE(*z_ret);
 	} else {
-		// Set our response to TRUE
+		/* Set our response to TRUE */
 		ZVAL_TRUE(*z_ret);
 	}
 
@@ -1733,10 +1733,10 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval
 
 PHP_REDIS_API int
 redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) {
-	// Attempt to read the bulk reply
+	/* Attempt to read the bulk reply */
 	char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC);
 
-	// Set our reply to FALSE on failure, and the string on success
+	/* Set our reply to FALSE on failure, and the string on success */
 	if(bulk_resp == NULL) {
 		ZVAL_FALSE(*z_ret);
 		return -1;
@@ -1752,15 +1752,15 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret
 	REDIS_REPLY_TYPE reply_type;
 	zval *z_subelem;
 
-	// Iterate while we have elements
+	/* Iterate while we have elements */
 	while(elements > 0) {
-		// Attempt to read our reply type
+		/* Attempt to read our reply type */
 		if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) {
 			zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type);
 			return -1;
 		}
 
-		// Switch on our reply-type byte
+		/* Switch on our reply-type byte */
 		switch(reply_type) {
 			case TYPE_ERR:
 			case TYPE_LINE:
@@ -1769,17 +1769,17 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret
 				add_next_index_zval(*z_ret, z_subelem);
 				break;
 			case TYPE_INT:
-				// Add our long value
+				/* Add our long value */
 				add_next_index_long(*z_ret, reply_info);
 				break;
 			case TYPE_BULK:
-				// Init a zval for our bulk response, read and add it
+				/* Init a zval for our bulk response, read and add it */
 				ALLOC_INIT_ZVAL(z_subelem);
 				redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC);
 				add_next_index_zval(*z_ret, z_subelem);
 				break;
 			case TYPE_MULTIBULK:
-				// Construct an array for our sub element, and add it, and recurse
+				/* Construct an array for our sub element, and add it, and recurse */
 				ALLOC_INIT_ZVAL(z_subelem);
 				array_init(z_subelem);
 				add_next_index_zval(*z_ret, z_subelem);
@@ -1787,7 +1787,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret
 				break;
 		}
 
-		// Decrement our element counter
+		/* Decrement our element counter */
 		elements--;
 	}
 
@@ -1796,21 +1796,21 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret
 
 PHP_REDIS_API int
 redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
-	// Reply type, and reply size vars
+	/* Reply type, and reply size vars */
 	REDIS_REPLY_TYPE reply_type;
 	int reply_info;
-	//char *bulk_resp;
+	/*char *bulk_resp; */
 	zval *z_ret;
 
-	// Attempt to read our header
+	/* Attempt to read our header */
 	if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) {
 		return -1;
 	}
 
-	// Our return ZVAL
+	/* Our return ZVAL */
 	MAKE_STD_ZVAL(z_ret);
 
-	// Switch based on our top level reply type
+	/* Switch based on our top level reply type */
 	switch(reply_type) {
 		case TYPE_ERR:
 		case TYPE_LINE:
@@ -1823,16 +1823,16 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
 			redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC);
 			break;
 		case TYPE_MULTIBULK:
-			// Initialize an array for our multi-bulk response
+			/* Initialize an array for our multi-bulk response */
 			array_init(z_ret);
 
-			// If we've got more than zero elements, parse our multi bulk respoinse recursively
+			/* If we've got more than zero elements, parse our multi bulk respoinse recursively */
 			if(reply_info > -1) {
 				redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC);
 			}
 			break;
 		default:
-			// Protocol error
+			/* Protocol error */
 			zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type);
 			break;
 	}
@@ -1840,14 +1840,14 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
 	IF_MULTI_OR_PIPELINE() {
 		add_next_index_zval(z_tab, z_ret);
 	} else {
-		// Set our return value
+		/* Set our return value */
 		*return_value = *z_ret;
 	    zval_copy_ctor(return_value);
 	    zval_dtor(z_ret);
 		efree(z_ret);
 	}
 
-	// Success
+	/* Success */
 	return 0;
 }
 
diff --git a/library.h b/library.h
index ec34987c7a..b749cf6304 100644
--- a/library.h
+++ b/library.h
@@ -38,7 +38,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAME
 PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
 PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
 PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC);
-//PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC);
+/* PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC); */
 PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock);
 PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock);
 PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len);
diff --git a/redis_array.c b/redis_array.c
index 12fe98308a..60d1022be9 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -293,7 +293,7 @@ static void
 ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, int cmd_len, zval *z_args, zval *z_new_target) {
 
 	zval **zp_tmp, z_tmp;
-	char *key = NULL; // set to avoid "unused-but-set-variable"
+	char *key = NULL; /* set to avoid "unused-but-set-variable" */
 	int key_len;
 	int i;
 	zval *redis_inst;
@@ -607,41 +607,41 @@ PHP_METHOD(RedisArray, keys)
 	char *pattern;
 	int pattern_len, i;
 
-	// Make sure the prototype is correct
+	/* Make sure the prototype is correct */
 	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
 								    &object, redis_array_ce, &pattern, &pattern_len) == FAILURE)
 	{
 		RETURN_FALSE;
 	}
 
-	// Make sure we can grab our RedisArray object
+	/* Make sure we can grab our RedisArray object */
 	if(redis_array_get(object, &ra TSRMLS_CC) < 0) {
 		RETURN_FALSE;
 	}
 
-	// Set up our function call (KEYS)
+	/* Set up our function call (KEYS) */
 	ZVAL_STRING(&z_fun, "KEYS", 0);
 
-	// We will be passing with one string argument (the pattern)
+	/* We will be passing with one string argument (the pattern) */
 	MAKE_STD_ZVAL(z_args[0]);
 	ZVAL_STRINGL(z_args[0], pattern, pattern_len, 0);
 
-	// Init our array return
+	/* Init our array return */
 	array_init(return_value);
 
-	// Iterate our RedisArray nodes
+	/* Iterate our RedisArray nodes */
 	for(i=0; icount; ++i) {
-		// Return for this node
+		/* Return for this node */
 		MAKE_STD_ZVAL(z_tmp);
 
-		// Call KEYS on each node
+		/* Call KEYS on each node */
 		call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args TSRMLS_CC);
 
-		// Add the result for this host
+		/* Add the result for this host */
 		add_assoc_zval(return_value, ra->hosts[i], z_tmp);
 	}
 
-	// Free arg array
+	/* Free arg array */
 	efree(z_args[0]);
 }
 
@@ -1186,7 +1186,7 @@ PHP_METHOD(RedisArray, del)
 			found++;
 		}
 
-		if(!found) {	// don't run empty DELs
+		if(!found) {	/* don't run empty DELs */
 			zval_dtor(z_argarray);
 			efree(z_argarray);
 			continue;
diff --git a/redis_array.h b/redis_array.h
index 652b5aefdc..3b1163bf88 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -1,7 +1,12 @@
 #ifndef REDIS_ARRAY_H
 #define REDIS_ARRAY_H
 
+#ifdef PHP_WIN32
+#include "win32/php_stdint.h"
+#else
 #include 
+#endif
+
 #include "common.h"
 
 void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC);
@@ -34,7 +39,7 @@ PHP_METHOD(RedisArray, unwatch);
 
 
 typedef struct RedisArray_ {
-	
+
 	int count;
 	char **hosts;			/* array of host:port strings */
 	zval **redis;			/* array of Redis instances */
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 65ccd61fbe..10c7dc81ec 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -348,7 +348,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR
 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function");
 		return NULL;
 	}
-	//convert_to_string(ra->z_fun);
+	/* convert_to_string(ra->z_fun); */
 
 	/* call extraction function */
 	MAKE_STD_ZVAL(z_argv0);
@@ -408,7 +408,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML
 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function");
 		return 0;
 	}
-	//convert_to_string(ra->z_fun);
+	/* convert_to_string(ra->z_fun); */
 
 	/* call extraction function */
 	MAKE_STD_ZVAL(z_argv0);
@@ -448,7 +448,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D
                 /* hash */
                 hash = rcrc32(out, out_len);
                 efree(out);
-        
+
                 /* get position on ring */
                 h64 = hash;
                 h64 *= ra->count;
@@ -502,7 +502,7 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) {
 	ZVAL_LONG(z_args[0], multi_value);
 	call_user_function(&redis_ce->function_table, &z_redis, &z_fun_multi, &z_ret, 1, z_args TSRMLS_CC);
 	efree(z_args[0]);
-	//zval_dtor(&z_ret);
+	/* zval_dtor(&z_ret); */
 }
 
 static void
@@ -630,8 +630,8 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) {
 		zval_dtor(&z_ret);
 	}
 
-	//zval *zptr = &z_ret;
-	//php_var_dump(&zptr, 0 TSRMLS_CC);
+	/* zval *zptr = &z_ret; */
+	/* php_var_dump(&zptr, 0 TSRMLS_CC); */
 }
 
 void
@@ -773,7 +773,7 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long
 			if(zend_hash_get_current_data(retHash, (void**)&z_data) == FAILURE) {
 				success = 0;
 				break;
-			}	
+			}
 			if(Z_TYPE_PP(z_data) != IS_LONG) {
 				success = 0;
 				break;
@@ -865,7 +865,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS
 	unsigned int val_len;
 	int i;
 	unsigned long idx;
-	
+
 	/* run ZRANGE key 0 -1 WITHSCORES on source */
 	ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6, 0);
 	for(i = 0; i < 4; ++i) {
@@ -917,7 +917,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS
 				ZVAL_LONG(z_zadd_args[i+1], (long)idx);
 				break;
 			default:
-				return -1; // Todo: log error
+				return -1; /* Todo: log error */
 				break;
 		}
 		i += 2;
@@ -1123,23 +1123,23 @@ ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) {
 			case REDIS_STRING:
 				success = ra_move_string(key, key_len, z_from, z_to, ttl TSRMLS_CC);
 				break;
-	
+
 			case REDIS_SET:
 				success = ra_move_set(key, key_len, z_from, z_to, ttl TSRMLS_CC);
 				break;
-	
+
 			case REDIS_LIST:
 				success = ra_move_list(key, key_len, z_from, z_to, ttl TSRMLS_CC);
 				break;
-	
+
 			case REDIS_ZSET:
 				success = ra_move_zset(key, key_len, z_from, z_to, ttl TSRMLS_CC);
 				break;
-	
+
 			case REDIS_HASH:
 				success = ra_move_hash(key, key_len, z_from, z_to, ttl TSRMLS_CC);
 				break;
-	
+
 			default:
 				/* TODO: report? */
 				break;
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 10f8512ad1..8f10654260 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -1,7 +1,12 @@
 #ifndef REDIS_ARRAY_IMPL_H
 #define REDIS_ARRAY_IMPL_H
 
+#ifdef PHP_WIN32
+#include "win32/php_stdint.h"
+#else
 #include 
+#endif
+
 #include "common.h"
 #include "redis_array.h"
 

From f3f361a4274a2e2933665b1afccbb2a2765d5ed0 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 13 Nov 2013 11:16:41 -0800
Subject: [PATCH 0091/1986] Fix serializer support for SRANDMEMBER for both one
 member and when the count argument is passed.

Addresses #391
---
 redis.c             | 22 ++++++++++++++++++--
 tests/TestRedis.php | 50 ++++++++++++++++++++++++++++++++-------------
 2 files changed, 56 insertions(+), 16 deletions(-)

diff --git a/redis.c b/redis.c
index ed4ec9d99b..3cd71e50e4 100644
--- a/redis.c
+++ b/redis.c
@@ -2346,14 +2346,32 @@ PHP_METHOD(Redis, sRandMember)
     // Process our command
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 
-    // Process our reply
-    IF_ATOMIC() {
+    // Either bulk or multi-bulk depending on argument count
+    if(ZEND_NUM_ARGS() == 2) {
+        IF_ATOMIC() {
+            if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+                                               redis_sock, NULL, NULL) < 0)
+            {
+                RETURN_FALSE;
+            }
+        }
+        REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
+    } else {
+        IF_ATOMIC() {
+            redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+                                  NULL, NULL);
+        }
+        REDIS_PROCESS_RESPONSE(redis_string_response);
+    }
+
+    /*IF_ATOMIC() {
         // This will be bulk or multi-bulk depending if we passed the optional [COUNT] argument
         if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
             RETURN_FALSE;
         }
     }
     REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+    */
 }
 /* }}} */
 
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 2a6847542e..85d185bc97 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -1127,23 +1127,45 @@ public function testsPop()
     }
 
     public function testsRandMember() {
-	$this->redis->delete('set0');
-	$this->assertTrue($this->redis->sRandMember('set0') === FALSE);
+        $this->redis->delete('set0');
+        $this->assertTrue($this->redis->sRandMember('set0') === FALSE);
 
-	$this->redis->sAdd('set0', 'val');
-	$this->redis->sAdd('set0', 'val2');
+        $this->redis->sAdd('set0', 'val');
+        $this->redis->sAdd('set0', 'val2');
 
-	$got = array();
-	while(true) {
-	    $v = $this->redis->sRandMember('set0');
-	    $this->assertTrue(2 === $this->redis->sSize('set0')); // no change.
-	    $this->assertTrue($v === 'val' || $v === 'val2');
+        $got = array();
+        while(true) {
+            $v = $this->redis->sRandMember('set0');
+            $this->assertTrue(2 === $this->redis->sSize('set0')); // no change.
+            $this->assertTrue($v === 'val' || $v === 'val2');
 
-	    $got[$v] = $v;
-	    if(count($got) == 2) {
-	        break;
-	    }
-	}
+            $got[$v] = $v;
+            if(count($got) == 2) {
+                break;
+            }
+        }
+
+        // 
+        // With and without count, while serializing
+        // 
+
+        $this->redis->delete('set0');
+        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::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_SERIALIZER, Redis::SERIALIZER_NONE);
     }
 
     public function testSRandMemberWithCount() {

From 0fd41c2aefaf8e7bb9713b4f9a55bc63b0279ca0 Mon Sep 17 00:00:00 2001
From: Nicolas Van Eenaeme 
Date: Wed, 23 Oct 2013 14:30:07 +0000
Subject: [PATCH 0092/1986] Fixed ra->pconnect parameter in ra_make_array This
 parameter was ignored and caused pconnect not to work

---
 redis_array_impl.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index c3a1f36522..9f1a2238f2 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -308,6 +308,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
 	ra->z_multi_exec = NULL;
 	ra->index = b_index;
 	ra->auto_rehash = 0;
+	ra->pconnect = b_pconnect;
 
 	/* init array data structures */
 	ra_init_function_table(ra);

From eff6a913404402c8b429c165f290d60d7ffdc96c Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 13 Nov 2013 15:00:13 -0800
Subject: [PATCH 0093/1986] Enforce offset range limitations for GETBIT and
 SETBIT

Addresses #401
---
 common.h            |  4 ++++
 redis.c             | 19 ++++++++++---------
 tests/TestRedis.php |  8 +++++++-
 3 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/common.h b/common.h
index 63b1d9ee30..dce83388a1 100644
--- a/common.h
+++ b/common.h
@@ -47,6 +47,10 @@ typedef enum _REDIS_REPLY_TYPE {
 #define REDIS_SERIALIZER_PHP 		1
 #define REDIS_SERIALIZER_IGBINARY 	2
 
+/* GETBIT/SETBIT offset range limits */
+#define BITOP_MIN_OFFSET 0
+#define BITOP_MAX_OFFSET 4294967295
+
 #define IF_MULTI() if(redis_sock->mode == MULTI)
 #define IF_MULTI_OR_ATOMIC() if(redis_sock->mode == MULTI || redis_sock->mode == ATOMIC)\
 
diff --git a/redis.c b/redis.c
index 3cd71e50e4..ec400b6aba 100644
--- a/redis.c
+++ b/redis.c
@@ -1739,6 +1739,11 @@ PHP_METHOD(Redis, getBit)
 		RETURN_FALSE;
 	}
 
+	// GETBIT and SETBIT only work for 0 - 2^32-1
+	if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) {
+	    RETURN_FALSE;
+	}
+
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 	cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd", key, key_len, (int)offset);
 	if(key_free) efree(key);
@@ -1768,6 +1773,11 @@ PHP_METHOD(Redis, setBit)
 		RETURN_FALSE;
 	}
 
+	// GETBIT and SETBIT only work for 0 - 2^32-1
+	if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) {
+	    RETURN_FALSE;
+	}
+
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 	cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd", key, key_len, (int)offset, (int)val);
 	if(key_free) efree(key);
@@ -2363,15 +2373,6 @@ PHP_METHOD(Redis, sRandMember)
         }
         REDIS_PROCESS_RESPONSE(redis_string_response);
     }
-
-    /*IF_ATOMIC() {
-        // This will be bulk or multi-bulk depending if we passed the optional [COUNT] argument
-        if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
-            RETURN_FALSE;
-        }
-    }
-    REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
-    */
 }
 /* }}} */
 
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 85d185bc97..65bc63caba 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -109,7 +109,13 @@ public function testBitsets() {
 
 	    // values above 1 are changed to 1 but don't overflow on bits to the right.
 	    $this->assertTrue(0 === $this->redis->setBit('key', 0, 0xff));
-	    $this->assertTrue("\x9f" === $this->redis->get('key'));
+        $this->assertTrue("\x9f" === $this->redis->get('key'));
+
+        // Verify valid offset ranges
+        $this->assertFalse($this->redis->getBit('key', -1));
+        $this->assertFalse($this->redis->getBit('key', 4294967296));
+        $this->assertFalse($this->redis->setBit('key', -1, 1));
+        $this->assertFalse($this->redis->setBit('key', 4294967296, 1));
     }
 
     public function test1000() {

From 32837e06e92bd31c0866ee680aff78aa38c7145d Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 18 Nov 2013 11:41:19 -0800
Subject: [PATCH 0094/1986] Allow for NULL to be passed in our optional
 arguments and just ignore it if it is (but still set the key).

Addresses #407
---
 redis.c             | 4 +++-
 tests/TestRedis.php | 6 ++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index ec400b6aba..c2a3d87c0c 100644
--- a/redis.c
+++ b/redis.c
@@ -836,7 +836,9 @@ PHP_METHOD(Redis, set) {
 
     /* Our optional argument can either be a long (to support legacy SETEX */
     /* redirection), or an array with Redis >= 2.6.12 set options */
-    if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY) {
+    if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY
+       && Z_TYPE_P(z_opts) != IS_NULL) 
+    {
         RETURN_FALSE;
     }
 
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 65bc63caba..60f015f531 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -264,6 +264,12 @@ public function testExtendedSet() {
         $this->assertTrue($this->redis->set('foo','barbaz', Array('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);
     }
 
     public function testGetSet() {

From 2108446ecbf33375878f8a77a04f41d339443d69 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 19 Nov 2013 07:40:02 -0800
Subject: [PATCH 0095/1986] Don't free the key before we use it Fixes #408

---
 redis.c             |  6 ++----
 tests/TestRedis.php | 44 +++++++++++++++++++++++++++-----------------
 2 files changed, 29 insertions(+), 21 deletions(-)

diff --git a/redis.c b/redis.c
index c2a3d87c0c..3ed22b632a 100644
--- a/redis.c
+++ b/redis.c
@@ -1330,13 +1330,11 @@ PHP_METHOD(Redis, incrByFloat) {
 		RETURN_FALSE;
 	}
 
-	// Prefix our key, free it if we have
+	// Prefix key, format command, free old key if necissary
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+    cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val);
 	if(key_free) efree(key);
 
-	// Format our INCRBYFLOAT command
-	cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val);
-
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 	IF_ATOMIC() {
 		redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 60f015f531..e4e41c4410 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -462,31 +462,41 @@ public function testIncr()
 
     public function testIncrByFloat()
     {
-	// incrbyfloat is new in 2.6.0
-	if (version_compare($this->version, "2.5.0", "lt")) {
-		$this->markTestSkipped();
-	}
+	    // incrbyfloat is new in 2.6.0
+	    if (version_compare($this->version, "2.5.0", "lt")) {
+            $this->markTestSkipped();
+        }
 
-	$this->redis->delete('key');
+        $this->redis->delete('key');
+        
+        $this->redis->set('key', 0);
 
-	$this->redis->set('key', 0);
+        $this->redis->incrbyfloat('key', 1.5);
+        $this->assertEquals('1.5', $this->redis->get('key'));
 
-	$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('3.75', $this->redis->get('key'));
+        $this->redis->incrbyfloat('key', -2.25);
+        $this->assertEquals('1.5', $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->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'));
+        $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.8', $this->redis->get('key'));
+        $this->redis->setOption(Redis::OPT_PREFIX, '');
+        $this->assertTrue($this->redis->exists('someprefix:key'));
+        $this->redis->del('someprefix:key');
 
-	$this->redis->incrbyfloat('key', -1.5);
-	$this->assertTrue("abc" === $this->redis->get('key'));
     }
 
     public function testDecr()

From 77afbe3dac5e3fb3fb91b558084515a7c6dc9886 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 7 Dec 2013 14:08:18 -0800
Subject: [PATCH 0096/1986] Implement WAIT command

This commit implements the new WAIT command which currently lives
in redis unstable, along with a unit test.

Also added myself to the README.markdown file, as well as a link to
"the twitter".
---
 README.markdown     |  2 +-
 php_redis.h         |  2 ++
 redis.c             | 40 ++++++++++++++++++++++++++++++++++++++++
 tests/TestRedis.php | 28 ++++++++++++++++++++++++++++
 4 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 7d5d2dc86f..536609f05f 100644
--- a/README.markdown
+++ b/README.markdown
@@ -3,7 +3,7 @@
 The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt).
 This code has been developed and maintained by Owlient from November 2009 to March 2011.
 
-You can send comments, patches, questions [here on github](https://github.com/nicolasff/phpredis/issues) or to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)).
+You can send comments, patches, questions [here on github](https://github.com/nicolasff/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)), or to michael.grunder@gmail.com ([@grumi78](http://twitter.com/grumi78)).
 
 
 # Table of contents
diff --git a/php_redis.h b/php_redis.h
index 879503aa38..c7719f98d3 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -14,6 +14,7 @@
   +----------------------------------------------------------------------+
   | Original author: Alfonso Jimenez              |
   | Maintainer: Nicolas Favre-Felix            |
+  | Maintainer: Michael Grunder               |
   | Maintainer: Nasreddine Bouafif                 |
   +----------------------------------------------------------------------+
 */
@@ -181,6 +182,7 @@ PHP_METHOD(Redis, setOption);
 
 PHP_METHOD(Redis, config);
 PHP_METHOD(Redis, slowlog);
+PHP_METHOD(Redis, wait);
 
 PHP_METHOD(Redis, client);
 
diff --git a/redis.c b/redis.c
index 3ed22b632a..c6db29d84c 100644
--- a/redis.c
+++ b/redis.c
@@ -250,6 +250,8 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, getAuth, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC)
 
+     PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC)
+
      /* aliases */
      PHP_MALIAS(Redis, open, connect, NULL, ZEND_ACC_PUBLIC)
      PHP_MALIAS(Redis, popen, pconnect, NULL, ZEND_ACC_PUBLIC)
@@ -6006,6 +6008,44 @@ PHP_METHOD(Redis, slowlog) {
     REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
 }
 
+/* {{{ proto Redis::wait(int num_slaves, int ms) }}}
+ */
+PHP_METHOD(Redis, wait) {
+    zval *object;
+    RedisSock *redis_sock;
+    long num_slaves, timeout;
+    char *cmd;
+    int cmd_len;
+
+    // Make sure arguments are valid
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", 
+                                    &object, redis_ce, &num_slaves, &timeout)
+                                    ==FAILURE) 
+    {
+        RETURN_FALSE;
+    }
+
+    // Don't even send this to Redis if our args are negative
+    if(num_slaves < 0 || timeout < 0) {
+        RETURN_FALSE;
+    }
+
+    // Grab our socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) {
+        RETURN_FALSE;
+    }
+
+    // Construct the command
+    cmd_len = redis_cmd_format_static(&cmd, "WAIT", "ll", num_slaves, timeout);
+
+    // Kick it off
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_long_response);
+}
+
 // Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
 PHPAPI int
 redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) {
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index e4e41c4410..f9f2b1b028 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -1803,6 +1803,34 @@ public function testSlowlog() {
         $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', 'lt')) {
+            $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_slaves, 100), $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+1, 100) < $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(-1, 20));
+    }
+
     public function testinfo() {
 	$info = $this->redis->info();
 

From ab553893f97c77b77f96557ca86ae133a2711de2 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 14 Dec 2013 10:51:03 -0800
Subject: [PATCH 0097/1986] SCAN and variants

This commit introduces support for the Redis SCAN, HSCAN, SSCAN,
and ZSCAN commands.

In the case of HSCAN, SSCAN, and ZSCAN, we take a key and iterator
as required arguments, and for SCAN just an iterator.  Matching
the Redis commands, each variant can optionally take a pattern
to match against and a count value which hints at Redis how many
keys to return at a time.

When scanning keys or members (especially with a large keyspace when
searching for a pattern), Redis will sometimes return an empty result
of keys/members.  PHPRedis can be set up to abstract this from the
caller by setting:

$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);

Which instructs PHPRedis to keep retrying the scan until members are
returned OR the iteration completes (Redis returns to us a zero iterator).

By default this option is set to Redis::SCAN_NORETRY, meaning that
empty results are possible, requiring an explicit check for FALSE in the
scanning loop, like so:

```php
$it = NULL;
while(($arr_keys = $redis->scan($it, "*pattern*"))!==FALSE) {
	print_r($arr_keys);
}
```
---
 README.markdown     | 116 +++++++++++++++++-
 common.h            |  17 +++
 library.c           |  50 ++++++++
 library.h           |   2 +
 php_redis.h         |   7 ++
 redis.c             | 281 ++++++++++++++++++++++++++++++++++++--------
 tests/TestRedis.php | 184 +++++++++++++++++++++++++++++
 7 files changed, 609 insertions(+), 48 deletions(-)

diff --git a/README.markdown b/README.markdown
index 536609f05f..b43cda7527 100644
--- a/README.markdown
+++ b/README.markdown
@@ -268,6 +268,15 @@ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);	// use built-in
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);	// use igBinary serialize/unserialize
 
 $redis->setOption(Redis::OPT_PREFIX, 'myAppName:');	// use custom prefix on all keys
+
+/* Options for the SCAN family of commands, indicating whether to abstract
+   empty results from the user.  If set to SCAN_NORETRY (the default), phpredis
+   will just issue one SCAN command at a time, sometimes returning an empty
+   array of results.  If set to SCAN_RETRY, phpredis will retry the scan command
+   until keys come back OR Redis returns an iterator of zero
+*/
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
 ~~~
 
 
@@ -607,6 +616,7 @@ $redis->slowlog('len');
 * [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds
 * [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp
 * [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern
+* [scan](#scan) - Scan for keys in the keyspace (Redis >= 2.8.0)
 * [migrate](#migrate) - Atomically transfer a key from a Redis instance to another one
 * [move](#move) - Move a key to another database
 * [object](#object) - Inspect the internals of Redis objects
@@ -953,7 +963,29 @@ $allKeys = $redis->keys('*');	// all keys will match this.
 $keyWithUserPrefix = $redis->keys('user*');
 ~~~
 
+### scan
+-----
+_**Description**_:  Scan the keyspace for keys
+
+##### *Parameters*
+*LONG (reference)*:  Iterator, initialized to NULL
+*STRING, Optional*:  Pattern to match
+*LONG, Optional)*: Count of keys per iteration (only a suggestion to Redis)
 
+##### *Return value*
+*Array, boolean*:  This function will return an array of keys or FALSE if there are no more keys
+
+##### *Example*
+~~~
+$it = NULL; /* Initialize our iterator to NULL */
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* retry when we get no keys back */
+while($arr_keys = $redis->scan($it)) {
+    foreach($arr_keys as $str_key) {
+        echo "Here is a key: $str_key\n";
+    }
+    echo "No more keys to scan!\n";
+}
+~~~
 
 ### object
 -----
@@ -1283,7 +1315,7 @@ $redis->migrate('backup', 6379, 'foo', 0, 3600);
 * [hSet](#hset) - Set the string value of a hash field
 * [hSetNx](#hsetnx) - Set the value of a hash field, only if the field does not exist
 * [hVals](#hvals) - Get all the values in a hash
-
+* [hScan](#hscan) - Scan a hash key for members
 ### hSet
 -----
 _**Description**_: Adds a value to the hash stored at key. If this value is already in the hash, `FALSE` is returned.  
@@ -1542,7 +1574,28 @@ $redis->hSet('h', 'field2', 'value2');
 $redis->hmGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */
 ~~~
 
+### hScan
+-----
+_**Description**_:  Scan a HASH value for members, with an optional pattern and count
+##### *Parameters*
+*key*: String
+*iterator*: Long (reference)
+*pattern*: Optional pattern to match against
+*count*: How many keys to return in a go (only a sugestion to Redis)
+##### *Return value* 
+*Array* An array of members that match our pattern
 
+##### *Examples*
+~~~
+$it = NULL;
+/* Don't ever return an empty array until we're done iterating */
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+while($arr_keys = $redis->hscan('hash', $it)) {
+    foreach($arr_keys as $str_field => $str_value) {
+        echo "$str_field => $str_value\n"; /* Print the hash member and value */
+    }
+}
+~~~
 
 ## Lists
 
@@ -1981,6 +2034,7 @@ $redis->lSize('key1');/* 2 */
 * [sRem, sRemove](#srem-sremove) - Remove one or more members from a set
 * [sUnion](#sunion) - Add multiple sets
 * [sUnionStore](#sunionstore) - Add multiple sets and store the resulting set in a key
+* [sScan](#sscan) - Scan a set for members
 
 ### sAdd
 -----
@@ -2380,6 +2434,41 @@ array(4) {
 }
 ~~~
 
+### sScan
+-----
+_**Description**_: Scan a set for members
+
+##### *Parameters*
+*Key*: The set to search
+*iterator*: LONG (reference) to the iterator as we go
+*pattern*: String, optional pattern to match against
+*count*: How many members to return at a time (Redis might return a different amount)
+
+##### *Retur value*
+*Array, boolean*: PHPRedis will return an array of keys or FALSE when we're done iterating
+
+##### *Example*
+~~~
+$it = NULL;
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* don't return empty results until we're done */
+while($arr_mems = $redis->sscan('set', $it, "*pattern*")) {
+    foreach($arr_mems as $str_mem) {
+        echo "Member: $str_mem\n";
+    }
+}
+
+$it = NULL;
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); /* return after each iteration, even if empty */
+while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) {
+    if(count($arr_mems) > 0) {
+        foreach($arr_mems as $str_mem) {
+            echo "Member found: $str_mem\n";
+        }
+    } else {
+        echo "No members in this iteration, iterator value: $it\n");
+    }
+}
+~~~
 
 ## Sorted sets
 
@@ -2397,6 +2486,7 @@ array(4) {
 * [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low
 * [zScore](#zscore) - Get the score associated with the given member in a sorted set
 * [zUnion](#zunion) - Add multiple sorted sets and store the resulting sorted set in a new key
+* [zScan](#zscan) - Scan a sorted set for members
 
 ### zAdd
 -----
@@ -2736,6 +2826,30 @@ $redis->zUnion('ko2', array('k1', 'k2'), array(1, 1)); /* 4, 'ko2' => array('val
 $redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val0', 'val2', 'val3', 'val1') */
 ~~~
 
+### zScan
+-----
+_**Description*_: Scan a sorted set for members, with optional pattern and count
+
+##### *Parameters*
+*key*: String, the set to scan
+*iterator*: Long (reference), initialized to NULL
+*pattern*: String (optional), the pattern to match
+*count*: How many keys to return per iteration (Redis might return a different number)
+
+##### *Return value*
+*Array, boolean* PHPReids will return matching keys from Redis, or FALSE when iteration is complete
+
+##### *Example*
+~~~
+$it = NULL;
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+while($arr_matches = $redis->zscan('zset', $it, '*pattern*')) {
+    foreach($arr_matches as $str_mem => $f_score) {
+        echo "Key: $str_mem, Score: $f_score\n";
+    }
+}
+~~~
+
 ## Pub/sub
 
 * [psubscribe](#psubscribe) - Subscribe to channels by pattern
diff --git a/common.h b/common.h
index dce83388a1..65eff46c3b 100644
--- a/common.h
+++ b/common.h
@@ -37,16 +37,30 @@ typedef enum _REDIS_REPLY_TYPE {
 	TYPE_MULTIBULK = '*'
 } REDIS_REPLY_TYPE;
 
+/* SCAN variants */
+typedef enum _REDIS_SCAN_TYPE {
+    TYPE_SCAN,
+    TYPE_SSCAN,
+    TYPE_HSCAN,
+    TYPE_ZSCAN
+} REDIS_SCAN_TYPE;
+
 /* options */
 #define REDIS_OPT_SERIALIZER		1
 #define REDIS_OPT_PREFIX		    2
 #define REDIS_OPT_READ_TIMEOUT		3
+#define REDIS_OPT_SCAN              4
 
 /* serializers */
 #define REDIS_SERIALIZER_NONE		0
 #define REDIS_SERIALIZER_PHP 		1
 #define REDIS_SERIALIZER_IGBINARY 	2
 
+/* SCAN options */
+
+#define REDIS_SCAN_NORETRY 0
+#define REDIS_SCAN_RETRY 1
+
 /* GETBIT/SETBIT offset range limits */
 #define BITOP_MIN_OFFSET 0
 #define BITOP_MAX_OFFSET 4294967295
@@ -57,6 +71,7 @@ typedef enum _REDIS_REPLY_TYPE {
 #define IF_MULTI_OR_PIPELINE() if(redis_sock->mode == MULTI || redis_sock->mode == PIPELINE)
 #define IF_PIPELINE() if(redis_sock->mode == PIPELINE)
 #define IF_NOT_MULTI() if(redis_sock->mode != MULTI)
+#define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC)
 #define IF_ATOMIC() if(redis_sock->mode == ATOMIC)
 #define ELSE_IF_MULTI() else if(redis_sock->mode == MULTI) { \
 	if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\
@@ -201,6 +216,8 @@ typedef struct {
     char           *err;
     int            err_len;
     zend_bool      lazy_connect;
+
+    int            scan;
 } RedisSock;
 /* }}} */
 
diff --git a/library.c b/library.c
index 7e7dc07233..9498395a93 100644
--- a/library.c
+++ b/library.c
@@ -100,6 +100,54 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
     return 0;
 }
 
+
+PHPAPI int 
+redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                           REDIS_SCAN_TYPE type, long *iter TSRMLS_DC)
+{
+    REDIS_REPLY_TYPE reply_type;
+    int reply_info;
+    char *p_iter;
+
+    // Our response should have two multibulk replies
+    if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
+       || reply_type != TYPE_MULTIBULK || reply_info != 2)
+    {
+        return -1;
+    }
+
+    // The BULK response iterator
+    if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
+       || reply_type != TYPE_BULK)
+    {
+        return -1;
+    }
+
+    // Attempt to read the iterator
+    if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info TSRMLS_CC))) {
+        return -1;
+    }
+
+    // Push the iterator out to the caller
+    *iter = atol(p_iter);
+    efree(p_iter);
+
+    // Read our actual keys/members/etc differently depending on what kind of
+    // scan command this is.  They all come back in slightly different ways
+    switch(type) {
+        case TYPE_SCAN:
+            return redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        case TYPE_SSCAN:
+            return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        case TYPE_ZSCAN:
+            return redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        case TYPE_HSCAN:
+            return redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        default:
+            return -1;
+    }
+}
+
 PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
     char inbuf[1024];
 	int numElems;
@@ -1064,6 +1112,8 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por
     redis_sock->err = NULL;
     redis_sock->err_len = 0;
 
+    redis_sock->scan = REDIS_SCAN_NORETRY;
+
     return redis_sock;
 }
 
diff --git a/library.h b/library.h
index 7b1d0383ad..57075f6bcb 100644
--- a/library.h
+++ b/library.h
@@ -35,6 +35,8 @@ PHPAPI int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, Re
 PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHPAPI int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter TSRMLS_DC);
+
 PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
 PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
 PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC);
diff --git a/php_redis.h b/php_redis.h
index c7719f98d3..a50c7c1826 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -186,6 +186,13 @@ PHP_METHOD(Redis, wait);
 
 PHP_METHOD(Redis, client);
 
+/* SCAN and friends  */
+PHP_METHOD(Redis, scan);
+PHP_METHOD(Redis, hscan);
+PHP_METHOD(Redis, sscan);
+PHP_METHOD(Redis, zscan);
+
+/* Reflection */
 PHP_METHOD(Redis, getHost);
 PHP_METHOD(Redis, getPort);
 PHP_METHOD(Redis, getDBNum);
diff --git a/redis.c b/redis.c
index c6db29d84c..b14471aa42 100644
--- a/redis.c
+++ b/redis.c
@@ -69,6 +69,25 @@ PHP_INI_BEGIN()
 	PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL)
 PHP_INI_END()
 
+/**
+ * Argument info for the SCAN proper
+ */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 1)
+    ZEND_ARG_INFO(1, i_iterator)
+    ZEND_ARG_INFO(0, str_pattern)
+    ZEND_ARG_INFO(0, i_count)
+ZEND_END_ARG_INFO();
+
+/**
+ * Argument info for key scanning
+ */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2)
+    ZEND_ARG_INFO(0, str_key)
+    ZEND_ARG_INFO(1, i_iterator)
+    ZEND_ARG_INFO(0, str_pattern)
+    ZEND_ARG_INFO(0, i_count)
+ZEND_END_ARG_INFO();
+
 ZEND_DECLARE_MODULE_GLOBALS(redis)
 
 static zend_function_entry redis_functions[] = {
@@ -230,6 +249,12 @@ static zend_function_entry redis_functions[] = {
 
      PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)
 
+     /* SCAN and friends */
+     PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC)
+
      /* options */
      PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, setOption, NULL, ZEND_ACC_PUBLIC)
@@ -488,6 +513,11 @@ PHP_MINIT_FUNCTION(redis)
     /* serializer */
     add_constant_long(redis_ce, "SERIALIZER_NONE", REDIS_SERIALIZER_NONE);
     add_constant_long(redis_ce, "SERIALIZER_PHP", REDIS_SERIALIZER_PHP);
+
+    /* scan options*/
+    add_constant_long(redis_ce, "OPT_SCAN", REDIS_OPT_SCAN);
+    add_constant_long(redis_ce, "SCAN_RETRY", REDIS_SCAN_RETRY);
+    add_constant_long(redis_ce, "SCAN_NORETRY", REDIS_SCAN_NORETRY);
 #ifdef HAVE_REDIS_IGBINARY
     add_constant_long(redis_ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY);
 #endif
@@ -5820,22 +5850,19 @@ PHP_METHOD(Redis, getOption)  {
     }
 
     switch(option) {
-
-            case REDIS_OPT_SERIALIZER:
-                    RETURN_LONG(redis_sock->serializer);
-
-            case REDIS_OPT_PREFIX:
-					if(redis_sock->prefix) {
-						RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len, 1);
-					}
-                    RETURN_NULL();
-
-            case REDIS_OPT_READ_TIMEOUT:
-                    RETURN_DOUBLE(redis_sock->read_timeout);
-
-            default:
-                    RETURN_FALSE;
-
+        case REDIS_OPT_SERIALIZER:
+            RETURN_LONG(redis_sock->serializer);
+        case REDIS_OPT_PREFIX:
+            if(redis_sock->prefix) {
+                RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len, 1);
+            }
+            RETURN_NULL();
+        case REDIS_OPT_READ_TIMEOUT:
+            RETURN_DOUBLE(redis_sock->read_timeout);
+        case REDIS_OPT_SCAN:
+            RETURN_LONG(redis_sock->scan);
+        default:
+            RETURN_FALSE;
     }
 }
 /* }}} */
@@ -5860,46 +5887,50 @@ PHP_METHOD(Redis, setOption) {
     }
 
     switch(option) {
-            case REDIS_OPT_SERIALIZER:
-					val_long = atol(val_str);
-                    if(val_long == REDIS_SERIALIZER_NONE
+        case REDIS_OPT_SERIALIZER:
+            val_long = atol(val_str);
+            if(val_long == REDIS_SERIALIZER_NONE
 #ifdef HAVE_REDIS_IGBINARY
-						 	|| val_long == REDIS_SERIALIZER_IGBINARY
+                    || val_long == REDIS_SERIALIZER_IGBINARY
 #endif
-							|| val_long == REDIS_SERIALIZER_PHP) {
-                            redis_sock->serializer = val_long;
-                            RETURN_TRUE;
+                    || val_long == REDIS_SERIALIZER_PHP) {
+                        redis_sock->serializer = val_long;
+                        RETURN_TRUE;
                     } else {
-                            RETURN_FALSE;
+                        RETURN_FALSE;
                     }
                     break;
-
 			case REDIS_OPT_PREFIX:
-					if(redis_sock->prefix) {
-						efree(redis_sock->prefix);
-					}
-					if(val_len == 0) {
-						redis_sock->prefix = NULL;
-						redis_sock->prefix_len = 0;
-					} else {
-						redis_sock->prefix_len = val_len;
-						redis_sock->prefix = ecalloc(1+val_len, 1);
-						memcpy(redis_sock->prefix, val_str, val_len);
-					}
-					RETURN_TRUE;
-
+			    if(redis_sock->prefix) {
+			        efree(redis_sock->prefix);
+			    }
+			    if(val_len == 0) {
+			        redis_sock->prefix = NULL;
+			        redis_sock->prefix_len = 0;
+			    } else {
+			        redis_sock->prefix_len = val_len;
+			        redis_sock->prefix = ecalloc(1+val_len, 1);
+			        memcpy(redis_sock->prefix, val_str, val_len);
+			    }
+			    RETURN_TRUE;
             case REDIS_OPT_READ_TIMEOUT:
-                    redis_sock->read_timeout = atof(val_str);
-                    if(redis_sock->stream) {
-                        read_tv.tv_sec  = (time_t)redis_sock->read_timeout;
-                        read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000);
-                        php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT,
-                                              0, &read_tv);
-                    }
+                redis_sock->read_timeout = atof(val_str);
+                if(redis_sock->stream) {
+                    read_tv.tv_sec  = (time_t)redis_sock->read_timeout;
+                    read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000);
+                    php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT,0, &read_tv);
+                }
+                RETURN_TRUE;
+            case REDIS_OPT_SCAN:
+                val_long = atol(val_str);
+                if(val_long == REDIS_SCAN_NORETRY || val_long == REDIS_SCAN_RETRY) {
+                    redis_sock->scan = val_long;
                     RETURN_TRUE;
-
+                }
+                RETURN_FALSE;
+                break;
             default:
-                    RETURN_FALSE;
+                RETURN_FALSE;
     }
 }
 /* }}} */
@@ -6688,4 +6719,160 @@ PHP_METHOD(Redis, client) {
     }
 }
 
+/**
+ * Helper to format any combination of SCAN arguments
+ */
+PHPAPI int
+redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
+                     int iter, char *pattern, int pattern_len, int count)
+{
+    char *keyword;
+    int arg_count, cmd_len;
+
+    // Count our arguments +1 for key if it's got one, and + 2 for pattern
+    // or count given that they each carry keywords with them.
+    arg_count = 1 + (key_len>0) + (pattern_len>0?2:0) + (count>0?2:0);
+
+    // Turn our type into a keyword
+    switch(type) {
+        case TYPE_SCAN:
+            keyword = "SCAN";
+            break;
+        case TYPE_SSCAN:
+            keyword = "SSCAN";
+            break;
+        case TYPE_HSCAN:
+            keyword = "HSCAN";
+            break;
+        case TYPE_ZSCAN:
+            keyword = "ZSCAN";
+            break;
+    }
+
+    // Start the command
+    cmd_len = redis_cmd_format_header(cmd, keyword, arg_count);
+
+    // Add the key in question if we have one
+    if(key_len) {
+        cmd_len = redis_cmd_append_str(cmd, cmd_len, key, key_len);
+    }
+
+    // Add our iterator
+    cmd_len = redis_cmd_append_int(cmd, cmd_len, iter);
+
+    // Append COUNT if we've got it
+    if(count) {
+        cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", sizeof("COUNT")-1);
+        cmd_len = redis_cmd_append_int(cmd, cmd_len, count);
+    }
+
+    // Append MATCH if we've got it
+    if(pattern_len) {
+        cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", sizeof("MATCH")-1);
+        cmd_len = redis_cmd_append_str(cmd, cmd_len, pattern, pattern_len);
+    }
+
+    // Return our command length
+    return cmd_len;
+}
+
+/**
+ * {{{ proto redis::scan(&$iterator, [pattern, [count]])
+ */
+PHPAPI void
+generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
+    zval *object, *z_iter;
+    RedisSock *redis_sock;
+    HashTable *hash;
+    char *pattern=NULL;
+    long count=0, iter;
+    char *cmd, *key=NULL;
+    int cmd_len, key_len=0, pattern_len=0, num_elements;
+
+    // Different prototype depending on if this is a key based scan
+    if(type != TYPE_SCAN) {
+        // Requires a key
+        if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz/|s!l",
+                                        &object, redis_ce, &key, &key_len, &z_iter,
+                                        &pattern, &pattern_len, &count)==FAILURE)
+        {
+            RETURN_FALSE;
+        }
+    } else {
+        // Doesn't require a key
+        if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz/|s!l",
+                                        &object, redis_ce, &z_iter, &pattern, &pattern_len,
+                                        &count) == FAILURE)
+        {
+            RETURN_FALSE;
+        }
+    }
+
+    // Grab our socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // Calling this in a pipeline makes no sense
+    IF_NOT_ATOMIC() {
+        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't call SCAN commands in multi or pipeline mode!");
+        RETURN_FALSE;
+    }
+
+    // The iterator should be passed in as NULL for the first iteration, but we can treat
+    // any NON LONG value as NULL for these purposes as we've seperated the variable anyway.
+    if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter)<0) {
+        // Convert to long
+        convert_to_long(z_iter);
+        iter = 0;
+    } else if(Z_LVAL_P(z_iter)!=0) {
+        // Update our iterator value for the next passthru
+        iter = Z_LVAL_P(z_iter);
+    } else {
+        // We're done, back to iterator zero
+        RETURN_FALSE;
+    }
+
+    /**
+     * Redis can return to us empty keys, especially in the case where there are a large
+     * number of keys to scan, and we're matching against a pattern.  PHPRedis can be set
+     * up to abstract this from the user, by setting OPT_SCAN to REDIS_SCAN_RETRY.  Otherwise
+     * we will return empty keys and the user will need to make subsequent calls with
+     * an updated iterator.
+     */
+    do {
+        // Format our SCAN command
+        cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter,
+                                   pattern, pattern_len, count);
+
+        // Execute our command getting our new iterator value
+        REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+        if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+                                      redis_sock,type,&iter)<0)
+        {
+            RETURN_FALSE;
+        }
+
+        // Get the number of elements
+        hash = Z_ARRVAL_P(return_value);
+        num_elements = zend_hash_num_elements(hash);
+    } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && num_elements == 0);
+
+    // Update our iterator reference
+    Z_LVAL_P(z_iter) = iter;
+}
+
+PHP_METHOD(Redis, scan) {
+    generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SCAN);
+}
+PHP_METHOD(Redis, hscan) {
+    generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN);
+}
+PHP_METHOD(Redis, sscan) {
+    generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN);
+}
+PHP_METHOD(Redis, zscan) {
+    generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
+}
+
 /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index f9f2b1b028..1c671ea1d5 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -4534,6 +4534,190 @@ public function testIntrospection() {
         $this->assertTrue($this->redis->getAuth() === self::AUTH);
     }
 
+    /**
+     * Scan and variants
+     */
+
+    protected function get_keyspace_count($str_db) {
+        $arr_info = $this->redis->info();
+        $arr_info = $arr_info[$str_db];
+        $arr_info = explode(',', $arr_info);
+        $arr_info = explode('=', $arr_info[0]);
+        return $arr_info[1];
+    }
+
+    public function testScan() {
+        if(version_compare($this->version, "2.8.0", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        // Key count
+        $i_key_count = $this->get_keyspace_count('db0');
+
+        // Have scan retry
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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);
+    }
+
+    public function testHScan() {
+        if(version_compare($this->version, "2.8.0", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+    
+        // Never get empty sets
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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(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(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_SCAN, Redis::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(0, $i_p_score);
+        $this->assertEquals(0, $i_p_count);
+    }
 }
 
 exit(TestSuite::run("Redis_Test"));

From 2e4ff5711c68d4222619e35d4c31221506f09dd4 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 14 Dec 2013 11:00:37 -0800
Subject: [PATCH 0098/1986] Documentation fix

---
 README.markdown | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.markdown b/README.markdown
index b43cda7527..c5d25cc8d9 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1316,6 +1316,7 @@ $redis->migrate('backup', 6379, 'foo', 0, 3600);
 * [hSetNx](#hsetnx) - Set the value of a hash field, only if the field does not exist
 * [hVals](#hvals) - Get all the values in a hash
 * [hScan](#hscan) - Scan a hash key for members
+
 ### hSet
 -----
 _**Description**_: Adds a value to the hash stored at key. If this value is already in the hash, `FALSE` is returned.  

From 84f3fb50b5b5cf3294f1cf777bc441b958b22d16 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 14 Dec 2013 11:01:51 -0800
Subject: [PATCH 0099/1986] Another documentation fix :)

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index c5d25cc8d9..c133e55d26 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2829,7 +2829,7 @@ $redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val
 
 ### zScan
 -----
-_**Description*_: Scan a sorted set for members, with optional pattern and count
+_**Description**_: Scan a sorted set for members, with optional pattern and count
 
 ##### *Parameters*
 *key*: String, the set to scan

From f39faf7dac6a570d8461db4fe8cb559ce45fd178 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 14 Dec 2013 12:23:23 -0800
Subject: [PATCH 0100/1986] Don't forget to prefix our keys.  :)

---
 redis.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/redis.c b/redis.c
index b14471aa42..b2e88627e5 100644
--- a/redis.c
+++ b/redis.c
@@ -6784,10 +6784,9 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
     zval *object, *z_iter;
     RedisSock *redis_sock;
     HashTable *hash;
-    char *pattern=NULL;
+    char *pattern=NULL, *cmd, *key=NULL;
+    int cmd_len, key_len=0, pattern_len=0, num_elements, key_free=0;
     long count=0, iter;
-    char *cmd, *key=NULL;
-    int cmd_len, key_len=0, pattern_len=0, num_elements;
 
     // Different prototype depending on if this is a key based scan
     if(type != TYPE_SCAN) {
@@ -6833,6 +6832,11 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         RETURN_FALSE;
     }
 
+    // Prefix our key if we've got one and we have a prefix set
+    if(key_len) {
+        key_free = redis_key_prefix(redis_sock, &key, &key_len);
+    }
+
     /**
      * Redis can return to us empty keys, especially in the case where there are a large
      * number of keys to scan, and we're matching against a pattern.  PHPRedis can be set
@@ -6850,6 +6854,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
                                       redis_sock,type,&iter)<0)
         {
+            if(key_free) efree(key);
             RETURN_FALSE;
         }
 
@@ -6858,6 +6863,9 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         num_elements = zend_hash_num_elements(hash);
     } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && num_elements == 0);
 
+    // Free our key if it was prefixed
+    if(key_free) efree(key);
+
     // Update our iterator reference
     Z_LVAL_P(z_iter) = iter;
 }

From a77439a7cfcc9279747ea0167efe2bbf38428de1 Mon Sep 17 00:00:00 2001
From: Nicolas Favre-Felix 
Date: Sat, 14 Dec 2013 21:00:06 +0000
Subject: [PATCH 0101/1986] Minor documentation fixes

---
 README.markdown | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/README.markdown b/README.markdown
index c133e55d26..540bc2e711 100644
--- a/README.markdown
+++ b/README.markdown
@@ -970,7 +970,7 @@ _**Description**_:  Scan the keyspace for keys
 ##### *Parameters*
 *LONG (reference)*:  Iterator, initialized to NULL
 *STRING, Optional*:  Pattern to match
-*LONG, Optional)*: Count of keys per iteration (only a suggestion to Redis)
+*LONG, Optional*: Count of keys per iteration (only a suggestion to Redis)
 
 ##### *Return value*
 *Array, boolean*:  This function will return an array of keys or FALSE if there are no more keys
@@ -2445,7 +2445,7 @@ _**Description**_: Scan a set for members
 *pattern*: String, optional pattern to match against
 *count*: How many members to return at a time (Redis might return a different amount)
 
-##### *Retur value*
+##### *Return value*
 *Array, boolean*: PHPRedis will return an array of keys or FALSE when we're done iterating
 
 ##### *Example*
@@ -2466,7 +2466,7 @@ while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) {
             echo "Member found: $str_mem\n";
         }
     } else {
-        echo "No members in this iteration, iterator value: $it\n");
+        echo "No members in this iteration, iterator value: $it\n";
     }
 }
 ~~~
@@ -2838,7 +2838,7 @@ _**Description**_: Scan a sorted set for members, with optional pattern and coun
 *count*: How many keys to return per iteration (Redis might return a different number)
 
 ##### *Return value*
-*Array, boolean* PHPReids will return matching keys from Redis, or FALSE when iteration is complete
+*Array, boolean* PHPRedis will return matching keys from Redis, or FALSE when iteration is complete
 
 ##### *Example*
 ~~~
@@ -3195,7 +3195,7 @@ None
 
 ### GetTimeout
 -----
-_**Description**_:  Get the (write) timeout in use for phpreids
+_**Description**_:  Get the (write) timeout in use for phpredis
 
 ##### *Parameters*
 None  

From 8732d89be9a7bd1f27c9d8f1158502e7db0f471a Mon Sep 17 00:00:00 2001
From: Mathias Verraes 
Date: Sun, 22 Dec 2013 20:30:22 +0100
Subject: [PATCH 0102/1986] added homebrew installation instructions

---
 README.markdown | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.markdown b/README.markdown
index 540bc2e711..c55af80cc1 100644
--- a/README.markdown
+++ b/README.markdown
@@ -69,6 +69,10 @@ Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tag
 
 See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports).
 
+You can install install it using Homebrew:
+
+- [Get homebrew-php](https://github.com/josegonzalez/homebrew-php)
+- `brew install php55-redis` (or php53-redis, php54-redis)
 
 ## PHP Session handler
 

From 3f0dcd8692eb06e681fb3b6ea877350bf78a5761 Mon Sep 17 00:00:00 2001
From: Mithun Satheesh 
Date: Tue, 31 Dec 2013 15:06:31 +0530
Subject: [PATCH 0103/1986] Update README.markdown

fixed syntax error in set examples.
---
 README.markdown | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.markdown b/README.markdown
index c55af80cc1..aeed26a0d3 100644
--- a/README.markdown
+++ b/README.markdown
@@ -672,10 +672,10 @@ $redis->set('key', 'value');
 $redis->set('key','value', 10);
 
 // Will set the key, if it doesn't exist, with a ttl of 10 seconds
-$redis->set('key', 'value', Array('nx', 'ex'=>10);
+$redis->set('key', 'value', Array('nx', 'ex'=>10));
 
 // Will set a key, if it does exist, with a ttl of 1000 miliseconds
-$redis->set('key', 'value', Array('xx', 'px'=>1000);
+$redis->set('key', 'value', Array('xx', 'px'=>1000));
 
 ~~~
 

From 727b824924c4f47d83aa49372cb42d512830330b Mon Sep 17 00:00:00 2001
From: Mithun Satheesh 
Date: Tue, 31 Dec 2013 15:06:31 +0530
Subject: [PATCH 0104/1986] Update README.markdown

fixed syntax error in set examples.
---
 README.markdown | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.markdown b/README.markdown
index 7d5d2dc86f..e3ab9294ac 100644
--- a/README.markdown
+++ b/README.markdown
@@ -658,10 +658,10 @@ $redis->set('key', 'value');
 $redis->set('key','value', 10);
 
 // Will set the key, if it doesn't exist, with a ttl of 10 seconds
-$redis->set('key', 'value', Array('nx', 'ex'=>10);
+$redis->set('key', 'value', Array('nx', 'ex'=>10));
 
 // Will set a key, if it does exist, with a ttl of 1000 miliseconds
-$redis->set('key', 'value', Array('xx', 'px'=>1000);
+$redis->set('key', 'value', Array('xx', 'px'=>1000));
 
 ~~~
 

From f92b74ba35a332f837e163ec8f9e7683266768dd Mon Sep 17 00:00:00 2001
From: liaolliso2012 
Date: Mon, 6 Jan 2014 15:56:42 +0800
Subject: [PATCH 0105/1986] Update README.markdown

incrByFloat function in example returns wrong value
---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index e3ab9294ac..fb4c122c09 100644
--- a/README.markdown
+++ b/README.markdown
@@ -780,7 +780,7 @@ $redis->incrByFloat('key1', 1.5); /* key1 didn't exist, so it will now be 1.5 */
 
 $redis->incrByFloat('key1', 1.5); /* 3 */
 $redis->incrByFloat('key1', -1.5); /* 1.5 */
-$redis->incrByFloat('key1', 2.5); /* 3.5 */
+$redis->incrByFloat('key1', 2.5); /* 4 */
 ~~~
 
 ### decr, decrBy

From 1a89ec2ff43c06c6b7d9e1174768c8c51e5efed6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 14 Jan 2014 13:43:06 -0800
Subject: [PATCH 0106/1986] Add Redis COPY and REPLACE flag support for Migrate
 command.

Addresses #426
---
 README.markdown |  6 +++++-
 library.c       |  6 +++---
 redis.c         | 31 ++++++++++++++++++++++++++-----
 3 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/README.markdown b/README.markdown
index f0d483311e..fa3f2cffe8 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1296,10 +1296,14 @@ _**Description**_: Migrates a key to a different Redis instance.
 *port* integer.  The TCP port to connect to.  
 *key* string. The key to migrate.  
 *destination-db* integer.  The target DB.  
-*timeout* integer.  The maximum amount of time given to this transfer.  
+*timeout* integer.  The maximum amount of time given to this transfer. 
+*copy* boolean, optional.  Should we send the COPY flag to redis
+*replace* boolean, optional.  Should we send the REPLACE flag to redis 
 ##### *Examples*
 ~~~
 $redis->migrate('backup', 6379, 'foo', 0, 3600);
+$redis->migrate('backup', 6379, 'foo', 0, 3600, true, true); /* copy and replace */
+$redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE flag */
 ~~~
 
 
diff --git a/library.c b/library.c
index 9498395a93..af311130a1 100644
--- a/library.c
+++ b/library.c
@@ -846,10 +846,10 @@ PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock
     char ret;
 
     if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) {
-	IF_MULTI_OR_PIPELINE() {
+        IF_MULTI_OR_PIPELINE() {
             add_next_index_bool(z_tab, 0);
-	    return;
-	}
+            return;
+        }
         RETURN_FALSE;
     }
     ret = response[0];
diff --git a/redis.c b/redis.c
index b2e88627e5..8e25ec10ed 100644
--- a/redis.c
+++ b/redis.c
@@ -6376,18 +6376,20 @@ PHP_METHOD(Redis, restore) {
 }
 
 /*
- * {{{ proto Redis::migrate(host port key dest-db timeout)
+ * {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, bool replace])
  */
 PHP_METHOD(Redis, migrate) {
 	zval *object;
 	RedisSock *redis_sock;
 	char *cmd, *host, *key;
 	int cmd_len, host_len, key_len, key_free;
+    zend_bool copy=0, replace=0;
 	long port, dest_db, timeout;
 
 	// Parse arguments
-	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll", &object, redis_ce,
-									&host, &host_len, &port, &key, &key_len, &dest_db, &timeout) == FAILURE) {
+	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll|bb", &object, redis_ce,
+									&host, &host_len, &port, &key, &key_len, &dest_db, &timeout, 
+                                    ©, &replace) == FAILURE) {
 		RETURN_FALSE;
 	}
 
@@ -6398,8 +6400,27 @@ PHP_METHOD(Redis, migrate) {
 
 	// Prefix our key if we need to, build our command
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
-	cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, key, key_len, dest_db, timeout);
-	if(key_free) efree(key);
+
+    // Construct our command
+    if(copy && replace) {
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port, 
+                                          key, key_len, dest_db, timeout, "COPY", 
+                                          sizeof("COPY")-1, "REPLACE", sizeof("REPLACE")-1);
+    } else if(copy) {
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
+                                          key, key_len, dest_db, timeout, "COPY", 
+                                          sizeof("COPY")-1);
+    } else if(replace) {
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
+                                          key, key_len, dest_db, timeout, "REPLACE",
+                                          sizeof("REPLACE")-1);
+    } else {
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, 
+                                          key, key_len, dest_db, timeout);
+    }
+
+    // Free our key if we prefixed it
+    if(key_free) efree(key);
 
 	// Kick off our MIGRATE request
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);

From 48ae8e52f6c37e5fea607ed64959f6e7a6712850 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 21 Jan 2014 14:50:34 -0800
Subject: [PATCH 0107/1986] Implement PUBSUB command

This commit implements the Redis PUBSUB command, a new command
available since Redis 2.8.0 and described here:
http://redis.io/commands/pubsub

Addresses #427
---
 README.markdown     |  21 ++++++
 common.h            |   7 ++
 php_redis.h         |   1 +
 redis.c             | 164 ++++++++++++++++++++++++++++++++++++++++++++
 tests/TestRedis.php |  43 +++++++++++-
 5 files changed, 235 insertions(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index fa3f2cffe8..59f980c5f9 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2864,6 +2864,7 @@ while($arr_matches = $redis->zscan('zset', $it, '*pattern*')) {
 * [psubscribe](#psubscribe) - Subscribe to channels by pattern
 * [publish](#publish) - Post a message to a channel
 * [subscribe](#subscribe) - Subscribe to channels
+* [pubsub](#pubsub) - Introspection into the pub/sub subsystem
 
 ### psubscribe
 -----
@@ -2924,6 +2925,26 @@ function f($redis, $chan, $msg) {
 $redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
 ~~~
 
+### pubsub
+-----
+_**Description**_: A command allowing you to get information on the Redis pub/sub system.
+
+##### *Parameters*
+*keyword*: String, which can be: "channels", "numsub", or "numpat"
+*argument*:  Optional, variant.  For the "channels" subcommand, you can pass a string pattern.  For "numsub" an array of channel names.
+
+##### *Return value*
+*CHANNELS*: Returns an array where the members are the matching channels.
+*NUMSUB*:  Returns a key/value array where the keys are channel names and values are their counts.
+*NUMPAT*:  Integer return containing the number active pattern subscriptions
+
+##### *Example*
+~~~
+$redis->pubsub("channels"); /*All channels */
+$redis->pubsub("channels", "*pattern*"); /* Just channels matching your pattern */
+$redis->pubsub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/
+$redsi->pubsub("numpat"); /* Get the number of pattern subscribers */
+```
 
 ## Transactions
 
diff --git a/common.h b/common.h
index 65eff46c3b..a0623fbdc7 100644
--- a/common.h
+++ b/common.h
@@ -45,6 +45,13 @@ typedef enum _REDIS_SCAN_TYPE {
     TYPE_ZSCAN
 } REDIS_SCAN_TYPE;
 
+/* PUBSUB subcommands */
+typedef enum _PUBSUB_TYPE {
+    PUBSUB_CHANNELS,
+    PUBSUB_NUMSUB,
+    PUBSUB_NUMPAT
+} PUBSUB_TYPE;
+
 /* options */
 #define REDIS_OPT_SERIALIZER		1
 #define REDIS_OPT_PREFIX		    2
diff --git a/php_redis.h b/php_redis.h
index a50c7c1826..e05461a1a5 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -183,6 +183,7 @@ PHP_METHOD(Redis, setOption);
 PHP_METHOD(Redis, config);
 PHP_METHOD(Redis, slowlog);
 PHP_METHOD(Redis, wait);
+PHP_METHOD(Redis, pubsub);
 
 PHP_METHOD(Redis, client);
 
diff --git a/redis.c b/redis.c
index 8e25ec10ed..0d4ad75853 100644
--- a/redis.c
+++ b/redis.c
@@ -276,6 +276,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC)
 
      PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, pubsub, NULL, ZEND_ACC_PUBLIC)
 
      /* aliases */
      PHP_MALIAS(Redis, open, connect, NULL, ZEND_ACC_PUBLIC)
@@ -6077,6 +6078,169 @@ PHP_METHOD(Redis, wait) {
     REDIS_PROCESS_RESPONSE(redis_long_response);
 }
 
+/*
+ * Construct a PUBSUB command
+ */
+PHPAPI int
+redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
+                       zval *arg TSRMLS_CC)
+{
+    HashTable *ht_chan;
+    HashPosition ptr;    
+    zval **z_ele;
+    char *key;
+    int cmd_len, key_len, key_free;
+    smart_str cmd = {0};
+
+    if(type == PUBSUB_CHANNELS) {
+        if(arg) {
+            // Get string argument and length.
+            key = Z_STRVAL_P(arg);
+            key_len = Z_STRLEN_P(arg);
+
+            // Prefix if necissary
+            key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+            // With a pattern
+            cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", "CHANNELS", sizeof("CHANNELS")-1,
+                                              key, key_len);
+
+            // Free the channel name if we prefixed it
+            if(key_free) efree(key);
+
+            // Return command length
+            return cmd_len;
+        } else {
+            // No pattern
+            return redis_cmd_format_static(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS")-1);
+        }
+    } else if(type == PUBSUB_NUMSUB) {
+        ht_chan = Z_ARRVAL_P(arg);
+        
+        // Add PUBSUB and NUMSUB bits
+        redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
+        redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
+
+        // Iterate our elements
+        for(zend_hash_internal_pointer_reset_ex(ht_chan, &ptr);
+            zend_hash_get_current_data_ex(ht_chan, (void**)&z_ele, &ptr)==SUCCESS;
+            zend_hash_move_forward_ex(ht_chan, &ptr))
+        {
+            char *key;
+            int key_len, key_free;
+            zval *z_tmp = NULL;
+
+            if(Z_TYPE_PP(z_ele) == IS_STRING) {
+                key = Z_STRVAL_PP(z_ele);
+                key_len = Z_STRLEN_PP(z_ele);
+            } else {
+                MAKE_STD_ZVAL(z_tmp);
+                *z_tmp = **z_ele;
+                zval_copy_ctor(z_tmp);
+                convert_to_string(z_tmp);
+
+                key = Z_STRVAL_P(z_tmp);
+                key_len = Z_STRLEN_P(z_tmp);
+            }
+
+            // Apply prefix if required
+            key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+            // Append this channel
+            redis_cmd_append_sstr(&cmd, key, key_len);
+
+            // Free key if prefixed
+            if(key_free) efree(key);
+
+            // Free our temp var if we converted from something other than a string
+            if(z_tmp) {
+                zval_dtor(z_tmp);
+                efree(z_tmp);
+                z_tmp = NULL;
+            }
+        }
+
+        // Set return
+        *ret = cmd.c;
+        return cmd.len;
+    } else if(type == PUBSUB_NUMPAT) {
+        return redis_cmd_format_static(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT")-1);
+    }
+
+    // Shouldn't ever happen
+    return -1;
+}
+
+/*
+ * {{{ proto Redis::pubsub("channels", pattern);
+ *     proto Redis::pubsub("numsub", Array channels);
+ *     proto Redis::pubsub("numpat"); }}}
+ */
+PHP_METHOD(Redis, pubsub) {
+    zval *object;
+    RedisSock *redis_sock;
+    char *keyword, *cmd;
+    int kw_len, cmd_len;
+    PUBSUB_TYPE type;
+    zval *arg=NULL;
+
+    // Parse arguments
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z", 
+                                    &object, redis_ce, &keyword, &kw_len, &arg)
+                                    ==FAILURE) 
+    {
+        RETURN_FALSE;
+    }
+
+    // Validate our sub command keyword, and that we've got proper arguments
+    if(!strncasecmp(keyword, "channels", sizeof("channels"))) {
+        // One (optional) string argument
+        if(arg && Z_TYPE_P(arg) != IS_STRING) {
+            RETURN_FALSE;
+        }
+        type = PUBSUB_CHANNELS;
+    } else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
+        // One array argument
+        if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
+           zend_hash_num_elements(Z_ARRVAL_P(arg))==0) 
+        {
+            RETURN_FALSE;
+        }
+        type = PUBSUB_NUMSUB;
+    } else if(!strncasecmp(keyword, "numpat", sizeof("numpat"))) {
+        type = PUBSUB_NUMPAT;
+    } else {
+        // Invalid keyword
+        RETURN_FALSE;
+    }
+
+    // Grab our socket context object
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) {
+        RETURN_FALSE;
+    }
+
+    // Construct our "PUBSUB" command
+    cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg TSRMLS_CC);
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+
+    if(type == PUBSUB_NUMSUB) {
+        IF_ATOMIC() {
+            if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) {
+                RETURN_FALSE;
+            }
+        }
+        REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
+    } else {
+        IF_ATOMIC() {
+            if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL)<0) {
+                RETURN_FALSE;
+            }
+        }
+        REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+    }
+}
+
 // Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
 PHPAPI int
 redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) {
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 1c671ea1d5..7b8cd3e07a 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -70,7 +70,48 @@ public function testPipelinePublish() {
 			->exec();
 
 		$this->assertTrue(is_array($ret) && count($ret) === 1 && $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", "lt")) {
+            $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", Array($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(Array($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() {
 

From f200730fe96f8666fc665cda340f4d26090ba9fb Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 4 Feb 2014 11:55:08 -0800
Subject: [PATCH 0108/1986] _serialize method

This commit adds a utility method as a counterpart to the _unserialize
utility method, allowing users to manually serialize data themselves
before sending it to Redis.  This could be useful for calls going through
EVAL, as phpredis can't automatically serialize/unserialize in that case.

Addresses #431
---
 README.markdown     | 23 +++++++++++++++++++++++
 php_redis.h         |  1 +
 redis.c             | 30 ++++++++++++++++++++++++++++++
 tests/TestRedis.php | 28 ++++++++++++++++++++++++++++
 4 files changed, 82 insertions(+)

diff --git a/README.markdown b/README.markdown
index fa3f2cffe8..f54516e9d9 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2990,6 +2990,7 @@ $ret = FALSE if x has been modified between the call to WATCH and the call to EX
 * [clearLastError](#) - Clear the last error message
 * [_prefix](#) - A utility method to prefix the value with the prefix setting for phpredis
 * [_unserialize](#) - A utility method to unserialize data with whatever serializer is set up
+* [_serialize](#) - A utility method to serialize data with whatever serializer is set up
 
 ### eval
 -----
@@ -3139,6 +3140,28 @@ $redis->setOption(Redis::OPT_PREFIX, 'my-prefix:');
 $redis->_prefix('my-value'); // Will return 'my-prefix:my-value'
 ~~~
 
+### _serialize
+-----
+_**Description**_: A utility method to serialize values manually.
+
+This method allows you to serialize a value with whatever serializer is configured, manually.
+This can be useful for serialization/unserialization of data going in and out of EVAL commands
+as phpredis can't automatically do this itself.  Note that if no serializer is set, phpredis
+will change Array values to 'Array', and Objects to 'Object'.
+
+##### *Parameters*
+*value*:  Mixed.  The value to be serialized
+
+##### *Examples*
+~~~
+$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
+$redis->_serialize("foo"); // returns "foo"
+$redis->_serialize(Array()); // Returns "Array"
+$redis->_serialize(new stdClass()); // Returns "Object"
+
+$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
+$redis->_serialize("foo"); // Returns 's:3:"foo";'
+
 ### _unserialize
 -----
 _**Description**_: A utility method to unserialize data with whatever serializer is set up.
diff --git a/php_redis.h b/php_redis.h
index a50c7c1826..176ba2b60e 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -142,6 +142,7 @@ PHP_METHOD(Redis, time);
 PHP_METHOD(Redis, getLastError);
 PHP_METHOD(Redis, clearLastError);
 PHP_METHOD(Redis, _prefix);
+PHP_METHOD(Redis, _serialize);
 PHP_METHOD(Redis, _unserialize);
 
 PHP_METHOD(Redis, mset);
diff --git a/redis.c b/redis.c
index 8e25ec10ed..c1b2a5e043 100644
--- a/redis.c
+++ b/redis.c
@@ -245,6 +245,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, clearLastError, NULL, ZEND_ACC_PUBLIC)
 
      PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, _serialize, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC)
 
      PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)
@@ -6458,6 +6459,35 @@ PHP_METHOD(Redis, _prefix) {
 	}
 }
 
+/*
+ * {{{ proto Redis::_serialize(value)
+ */
+PHP_METHOD(Redis, _serialize) {
+    zval *object;
+    RedisSock *redis_sock;
+    zval *z_val;
+    char *val;
+    int val_free, val_len;
+
+    // Parse arguments
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
+                                    &object, redis_ce, &z_val) == FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    // Grab socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // Serialize, which will return a value even if no serializer is set
+    val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
+
+    // Return serialized value.  Tell PHP to make a copy if redis_serialize didn't.
+    RETURN_STRINGL(val, val_len, !val_free);
+}
+
 /*
  * {{{ proto Redis::_unserialize(value)
  */
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 1c671ea1d5..94ebfc9d41 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -4428,6 +4428,34 @@ public function testEvalSHA() {
     	$this->assertTrue(1 === $this->redis->evalsha($sha));
     }
 
+    public function testSerialize() {
+        $vals = Array(1, 1.5, 'one', Array('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()) === 'Array');
+        $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object');
+
+        $arr_serializers = Array(Redis::SERIALIZER_PHP);
+        if(defined('Redis::SERIALIZER_IGBINARY')) {
+            $arr_serializers[] = Redis::SERIALIZER_IGBINARY;
+        }
+
+        foreach($arr_serializers as $mode) {
+            $arr_enc = Array(); 
+            $arr_dec = Array();
+
+            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 = Array(
     		1,1.5,'one',Array('this','is','an','array')

From d2832bfa85534affabdb9e10261ec50c4eb22957 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 20 Feb 2014 15:48:15 -0800
Subject: [PATCH 0109/1986] We need to return immediately from our variant
 reply parsing function if we get a protocol error, rather than just throwing
 and exception and breaking execution of the loop.

Addresses #437
---
 library.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library.c b/library.c
index 7e7dc07233..efd97bfe90 100644
--- a/library.c
+++ b/library.c
@@ -1824,7 +1824,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
 		default:
 			// Protocol error
 			zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type);
-			break;
+            return FAILURE;
 	}
 
 	IF_MULTI_OR_PIPELINE() {

From 9eaf14756b37d6b63567070385da2b304ed4bd70 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 21 Feb 2014 06:12:32 -0800
Subject: [PATCH 0110/1986] Don't attempt MGET on nodes where no keys resolve

The MGET call in RedisArray was failing under circumstances where
none of the passed keys hashed into any given node in the ring.
What was happening is that RedisArray was passing through to the
phpredis MGET command an empty array, which was returning false.
This in turn caused RedisArray to abort the process and return
false as well.

This change updates RedisArray MGET such that if a given node
doesn't have any keys, we skip the call to it all together.

Addresses #435
Addresses #436
---
 redis_array.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/redis_array.c b/redis_array.c
index ecc158eab6..2c43142466 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -876,7 +876,10 @@ PHP_METHOD(RedisArray, mget)
 
 	/* calls */
 	for(n = 0; n < ra->count; ++n) { /* for each node */
-		/* copy args for MGET call on node. */
+	    /* We don't even need to make a call to this node if no keys go there */
+	    if(!argc_each[n]) continue;
+
+	    /* copy args for MGET call on node. */
 		MAKE_STD_ZVAL(z_argarray);
 		array_init(z_argarray);
 

From 6f5e47cec331a2997e35d0226c07be0035aa647e Mon Sep 17 00:00:00 2001
From: Mike 
Date: Thu, 27 Jun 2013 00:58:56 -0400
Subject: [PATCH 0111/1986] add support for 'connect_timeout' option for redis
 arrays Conflicts: 	redis_array.c 	redis_array_impl.c 
 redis_array_impl.h

---
 redis_array.c      | 19 +++++++++++++++++--
 redis_array.h      |  1 +
 redis_array_impl.c | 28 ++++++++++++++++++++++++----
 redis_array_impl.h |  2 +-
 4 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 2c43142466..8576b9da01 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -196,8 +196,9 @@ PHP_METHOD(RedisArray, __construct)
 	RedisArray *ra = NULL;
 	zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0;
 	HashTable *hPrev = NULL, *hOpts = NULL;
-  long l_retry_interval = 0;
+	long l_retry_interval = 0;
   	zend_bool b_lazy_connect = 0;
+	double d_connect_timeout = 0;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) {
 		RETURN_FALSE;
@@ -261,6 +262,19 @@ PHP_METHOD(RedisArray, __construct)
 		if(FAILURE != zend_hash_find(hOpts, "lazy_connect", sizeof("lazy_connect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) {
 			b_lazy_connect = Z_BVAL_PP(zpData);
 		}
+		
+		/* extract connect_timeout option */
+		zval **z_connect_timeout_pp;
+		if (FAILURE != zend_hash_find(hOpts, "connect_timeout", sizeof("connect_timeout"), (void**)&z_connect_timeout_pp)) {
+			if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE || Z_TYPE_PP(z_connect_timeout_pp) == IS_STRING) {
+				if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE) {
+					d_connect_timeout = Z_DVAL_PP(z_connect_timeout_pp);
+				}
+				else {
+					d_connect_timeout = atof(Z_STRVAL_PP(z_connect_timeout_pp));
+				}
+			}
+		}		
 	}
 
 	/* extract either name of list of hosts from z0 */
@@ -270,7 +284,7 @@ PHP_METHOD(RedisArray, __construct)
 			break;
 
 		case IS_ARRAY:
-			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
+			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC);
 			break;
 
 		default:
@@ -280,6 +294,7 @@ PHP_METHOD(RedisArray, __construct)
 
 	if(ra) {
 		ra->auto_rehash = b_autorehash;
+		ra->connect_timeout = d_connect_timeout;
 #if PHP_VERSION_ID >= 50400
 		id = zend_list_insert(ra, le_redis_array TSRMLS_CC);
 #else
diff --git a/redis_array.h b/redis_array.h
index 652b5aefdc..1be2813af7 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -45,6 +45,7 @@ typedef struct RedisArray_ {
 	zval *z_fun;			/* key extractor, callable */
 	zval *z_dist;			/* key distributor, callable */
 	zval *z_pure_cmds;		/* hash table */
+	double connect_timeout; /* socket connect timeout */
 
 	struct RedisArray_ *prev;
 } RedisArray;
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 9f1a2238f2..5467c91693 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -70,7 +70,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b
 		call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC);
 
 		/* create socket */
-		redis_sock = redis_sock_create(host, host_len, port, 0, ra->pconnect, NULL, retry_interval, b_lazy_connect);
+		redis_sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect);
 
 	    if (!b_lazy_connect)
     	{
@@ -166,12 +166,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	zval *z_params_autorehash;
 	zval *z_params_retry_interval;
 	zval *z_params_pconnect;
+	zval *z_params_connect_timeout;
 	zval *z_params_lazy_connect;
 	RedisArray *ra = NULL;
 
 	zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0;
 	long l_retry_interval = 0;
 	zend_bool b_lazy_connect = 0;
+	double d_connect_timeout = 0;
 	HashTable *hHosts = NULL, *hPrev = NULL;
 
 	/* find entry */
@@ -264,12 +266,27 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.lazyconnect")), z_params_lazy_connect TSRMLS_CC);
 	if (zend_hash_find(Z_ARRVAL_P(z_params_lazy_connect), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) {
 		if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) {
+	/* find connect timeout option */
+	MAKE_STD_ZVAL(z_params_connect_timeout);
+	array_init(z_params_connect_timeout);
+	sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.connecttimeout")), z_params_connect_timeout TSRMLS_CC);
+	if (zend_hash_find(Z_ARRVAL_P(z_params_connect_timeout), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) {
+		if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE || Z_TYPE_PP(z_data_pp) == IS_STRING) {
+			if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE) {
+				d_connect_timeout = Z_DVAL_PP(z_data_pp);
+			}
+			else {
+				d_connect_timeout = atol(Z_STRVAL_PP(z_data_pp));
+			}
+		}
+	}
+	
 			b_lazy_connect = 1;
 		}
 	}
 
 	/* create RedisArray object */
-	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
+	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC);
 	ra->auto_rehash = b_autorehash;
 
 	/* cleanup */
@@ -287,6 +304,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	efree(z_params_retry_interval);
 	zval_dtor(z_params_pconnect);
 	efree(z_params_pconnect);
+	zval_dtor(z_params_connect_timeout);
+	efree(z_params_connect_timeout);
 	zval_dtor(z_params_lazy_connect);
 	efree(z_params_lazy_connect);
 
@@ -294,7 +313,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 }
 
 RedisArray *
-ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) {
+ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC) {
 
 	int count = zend_hash_num_elements(hosts);
 
@@ -309,6 +328,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
 	ra->index = b_index;
 	ra->auto_rehash = 0;
 	ra->pconnect = b_pconnect;
+	ra->connect_timeout = connect_timeout;
 
 	/* init array data structures */
 	ra_init_function_table(ra);
@@ -316,7 +336,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
 	if(NULL == ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC)) {
 		return NULL;
 	}
-	ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect TSRMLS_CC) : NULL;
+	ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL;
 
 	/* copy function if provided */
 	if(z_fun) {
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 10f8512ad1..4c2a1b05bd 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -7,7 +7,7 @@
 
 RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
 RedisArray *ra_load_array(const char *name TSRMLS_DC);
-RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
+RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC);
 zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC);
 zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC);
 void ra_init_function_table(RedisArray *ra);

From e3403e9588619daf751674e6633a02c62dc9aeaf Mon Sep 17 00:00:00 2001
From: Mike 
Date: Thu, 27 Jun 2013 01:36:22 -0400
Subject: [PATCH 0112/1986] fix for parsing ini value from string (atol should
 have been atof)

---
 redis_array_impl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index 5467c91693..d4af846301 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -276,7 +276,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 				d_connect_timeout = Z_DVAL_PP(z_data_pp);
 			}
 			else {
-				d_connect_timeout = atol(Z_STRVAL_PP(z_data_pp));
+				d_connect_timeout = atof(Z_STRVAL_PP(z_data_pp));
 			}
 		}
 	}

From 893c15af6733c87455b65b5c99f09d4e25918955 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 23 Feb 2014 12:24:16 -0800
Subject: [PATCH 0113/1986] Cleanup merge

---
 redis_array_impl.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index d4af846301..a39abc92bb 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -260,13 +260,18 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
             b_pconnect = 1;
         }
     }
-	/* find retry interval option */
+	
+    /* find lazy connect option */
 	MAKE_STD_ZVAL(z_params_lazy_connect);
 	array_init(z_params_lazy_connect);
 	sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.lazyconnect")), z_params_lazy_connect TSRMLS_CC);
 	if (zend_hash_find(Z_ARRVAL_P(z_params_lazy_connect), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) {
 		if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) {
-	/* find connect timeout option */
+            b_lazy_connect = 1;
+        }
+    }
+        
+    /* find connect timeout option */
 	MAKE_STD_ZVAL(z_params_connect_timeout);
 	array_init(z_params_connect_timeout);
 	sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.connecttimeout")), z_params_connect_timeout TSRMLS_CC);
@@ -281,10 +286,6 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 		}
 	}
 	
-			b_lazy_connect = 1;
-		}
-	}
-
 	/* create RedisArray object */
 	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC);
 	ra->auto_rehash = b_autorehash;

From e4d907c76adf8b059d26177c0f287e93b1235949 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 25 Feb 2014 05:57:54 -0800
Subject: [PATCH 0114/1986] Pass TSRMLS_CC macro

---
 redis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index c1b2a5e043..900ed73108 100644
--- a/redis.c
+++ b/redis.c
@@ -6903,7 +6903,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         // Execute our command getting our new iterator value
         REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
         if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                                      redis_sock,type,&iter)<0)
+                                      redis_sock,type,&iter TSRMLS_CC)<0)
         {
             if(key_free) efree(key);
             RETURN_FALSE;

From 8f006cba2d20479a3b15741fc1a0c801aec4c246 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 26 Feb 2014 06:59:11 -0800
Subject: [PATCH 0115/1986] Properly fix TRMLS_CC references :)

---
 library.c | 2 +-
 library.h | 2 +-
 redis.c   | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/library.c b/library.c
index 390c95d53b..e4ad72b9ee 100644
--- a/library.c
+++ b/library.c
@@ -103,7 +103,7 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
 
 PHPAPI int 
 redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                           REDIS_SCAN_TYPE type, long *iter TSRMLS_DC)
+                           REDIS_SCAN_TYPE type, long *iter)
 {
     REDIS_REPLY_TYPE reply_type;
     int reply_info;
diff --git a/library.h b/library.h
index 57075f6bcb..666d6addca 100644
--- a/library.h
+++ b/library.h
@@ -35,7 +35,7 @@ PHPAPI int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, Re
 PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHPAPI int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter TSRMLS_DC);
+PHPAPI int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter);
 
 PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
 PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
diff --git a/redis.c b/redis.c
index 900ed73108..0b199f9dc3 100644
--- a/redis.c
+++ b/redis.c
@@ -6885,7 +6885,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
 
     // Prefix our key if we've got one and we have a prefix set
     if(key_len) {
-        key_free = redis_key_prefix(redis_sock, &key, &key_len);
+        key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     }
 
     /**
@@ -6903,7 +6903,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         // Execute our command getting our new iterator value
         REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
         if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                                      redis_sock,type,&iter TSRMLS_CC)<0)
+                                      redis_sock,type,&iter)<0)
         {
             if(key_free) efree(key);
             RETURN_FALSE;

From e74ffe036643265b18e864a9a4139cdb3d072d65 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 3 Mar 2014 06:23:38 -0800
Subject: [PATCH 0116/1986] Implemented BITPOS command

This commit introduces the new BITPOS redis command
http://redis.io/commands/bitpos
---
 php_redis.h         |  1 +
 redis.c             | 54 +++++++++++++++++++++++++++++++++++++++++++++
 tests/TestRedis.php | 19 ++++++++++++++++
 3 files changed, 74 insertions(+)

diff --git a/php_redis.h b/php_redis.h
index 176ba2b60e..12eb7462fb 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -129,6 +129,7 @@ PHP_METHOD(Redis, slaveof);
 PHP_METHOD(Redis, object);
 PHP_METHOD(Redis, bitop);
 PHP_METHOD(Redis, bitcount);
+PHP_METHOD(Redis, bitpos);
 
 PHP_METHOD(Redis, eval);
 PHP_METHOD(Redis, evalsha);
diff --git a/redis.c b/redis.c
index 0b199f9dc3..cb3ed2deb1 100644
--- a/redis.c
+++ b/redis.c
@@ -177,6 +177,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, object, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, bitop, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, bitcount, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, bitpos, NULL, ZEND_ACC_PUBLIC)
 
      /* 1.1 */
      PHP_ME(Redis, mset, NULL, ZEND_ACC_PUBLIC)
@@ -820,6 +821,59 @@ PHP_METHOD(Redis, bitcount)
 }
 /* }}} */
 
+/* {{{ proto integer Redis::bitpos(string key, int bit, [int start], [int end]) */
+PHP_METHOD(Redis, bitpos) 
+{
+    zval *object;
+    RedisSock *redis_sock;
+    char *key, *cmd;
+    int key_len, cmd_len, argc, key_free=0;
+    long bit, start, end;
+
+    argc = ZEND_NUM_ARGS();
+
+    if(zend_parse_method_parameters(argc TSRMLS_CC, getThis(), "Osl|ll",
+                                    &object, redis_ce, &key, &key_len, &bit,
+                                    &start, &end)==FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // We can prevalidate the first argument
+    if(bit != 0 && bit != 1) {
+        RETURN_FALSE;
+    }
+
+    // Prefix our key
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+    // Various command semantics
+    if(argc == 2) {
+        cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd", key, key_len,
+                                          bit);
+    } else if(argc == 3) {
+        cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sdd", key, key_len,
+                                          bit, start);
+    } else {
+        cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sddd", key, key_len,
+                                          bit, start, end);
+    }
+
+    // Free our key if it was prefixed
+    if(key_free) efree(key);
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_long_response);
+}
+/* }}} */
+
 /* {{{ proto boolean Redis::close()
  */
 PHP_METHOD(Redis, close)
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 94ebfc9d41..9fa220aee8 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -118,6 +118,25 @@ public function testBitsets() {
         $this->assertFalse($this->redis->setBit('key', 4294967296, 1));
     }
 
+    public function testBitPos() {
+        if(version_compare($this->version, "2.8.7", "lt")) {
+            $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', 1, 0), 8);
+        $this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 8);
+
+        $this->redis->set('bpkey', "\x00\x00\x00");
+        $this->assertEquals($this->redis->bitpos('bpkey', 1), -1);
+    }
+
     public function test1000() {
 
 	 $s = str_repeat('A', 1000);

From 0bf19719513b7aeac327a61f39bdbfdcd2b2df86 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 9 Mar 2014 11:05:45 -0700
Subject: [PATCH 0117/1986] Data type check on redis array host info

RedisArray will segfault if you pass something other than strings
into your array of hosts.  This is invalid input (needs strings here)
but it shouldn't crash php.
---
 redis_array_impl.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index 9f1a2238f2..6b125a4c44 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -44,7 +44,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b
 
 	/* init connections */
 	for(i = 0; i < count; ++i) {
-		if(FAILURE == zend_hash_quick_find(hosts, NULL, 0, i, (void**)&zpData)) {
+		if(FAILURE == zend_hash_quick_find(hosts, NULL, 0, i, (void**)&zpData) ||
+           Z_TYPE_PP(zpData) != IS_STRING) 
+        {
 			efree(ra);
 			return NULL;
 		}

From f72a3177a30286c392b347807f2fe10b8482c022 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 10 Mar 2014 13:18:19 -0700
Subject: [PATCH 0118/1986] Fix autorehashing in RedisArray

This commit fixes auto rehashing in RedisArray as well as fixes
a couple of memory leaks found along the way

Addresses #442 and #294
---
 redis_array.c      | 91 ++++++++++++++++++++++++----------------------
 redis_array_impl.c |  4 ++
 2 files changed, 52 insertions(+), 43 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 2c43142466..2a36c56362 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -32,6 +32,12 @@
 #include "redis_array.h"
 #include "redis_array_impl.h"
 
+/* Simple macro to detect failure in a RedisArray call */
+#define RA_CALL_FAILED(rv, cmd) \
+    ((Z_TYPE_P(rv) == IS_BOOL && Z_BVAL_P(rv) == 0) || \
+    (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \
+    (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE"))) \
+
 extern zend_class_entry *redis_ce;
 zend_class_entry *redis_array_ce;
 
@@ -76,41 +82,48 @@ zend_function_entry redis_array_functions[] = {
      {NULL, NULL, NULL}
 };
 
-int le_redis_array;
-void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
-{
-	int i;
-	RedisArray *ra = (RedisArray*)rsrc->ptr;
+static void redis_array_free(RedisArray *ra) {
+    int i;
 
-	/* delete Redis objects */
-	for(i = 0; i < ra->count; ++i) {
-		zval_dtor(ra->redis[i]);
-		efree(ra->redis[i]);
+    // Redis objects
+    for(i=0;icount;i++) {
+        zval_dtor(ra->redis[i]);
+        efree(ra->redis[i]);
+        efree(ra->hosts[i]);
+    }
+    efree(ra->redis);
+    efree(ra->hosts);
 
-		/* remove host too */
-		efree(ra->hosts[i]);
-	}
-	efree(ra->redis);
-	efree(ra->hosts);
+    /* delete hash function */
+    if(ra->z_fun) {
+        zval_dtor(ra->z_fun);
+        efree(ra->z_fun);
+    }
 
-	/* delete function */
-	if(ra->z_fun) {
-		zval_dtor(ra->z_fun);
-		efree(ra->z_fun);
-	}
+    /* Distributor */
+    if(ra->z_dist) {
+        zval_dtor(ra->z_dist);
+        efree(ra->z_dist);
+    }
 
-	/* delete distributor */
-	if(ra->z_dist) {
-		zval_dtor(ra->z_dist);
-		efree(ra->z_dist);
-	}
+    /* Delete pur commands */
+    zval_dtor(ra->z_pure_cmds);
+    efree(ra->z_pure_cmds);
+
+    // Free structure itself
+    efree(ra);
+}
 
-	/* delete list of pure commands */
-	zval_dtor(ra->z_pure_cmds);
-	efree(ra->z_pure_cmds);
+int le_redis_array;
+void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
+{
+    RedisArray *ra = (RedisArray*)rsrc->ptr;
 
-	/* free container */
-	efree(ra);
+    /* Free previous ring if it's set */
+    if(ra->prev) redis_array_free(ra->prev);
+
+    /* Free parent array */
+    redis_array_free(ra);
 }
 
 /**
@@ -280,6 +293,7 @@ PHP_METHOD(RedisArray, __construct)
 
 	if(ra) {
 		ra->auto_rehash = b_autorehash;
+		if(ra->prev) ra->prev->auto_rehash = b_autorehash;
 #if PHP_VERSION_ID >= 50400
 		id = zend_list_insert(ra, le_redis_array TSRMLS_CC);
 #else
@@ -302,7 +316,6 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i
 	HashTable *h_args;
 
 	int argc;
-	int failed;
 	zend_bool b_write_cmd = 0;
 
 	h_args = Z_ARRVAL_P(z_args);
@@ -366,23 +379,15 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i
 	} else { /* call directly through. */
 		call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC);
 
-		failed = 0;
-		if((Z_TYPE_P(return_value) == IS_BOOL && Z_BVAL_P(return_value) == 0) ||
-		   (Z_TYPE_P(return_value) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(return_value)) == 0) ||
-		   (Z_TYPE_P(return_value) == IS_LONG && Z_LVAL_P(return_value) == 0 && !strcasecmp(cmd, "TYPE")))
-
-		{
-			failed = 1;
-		}
-
 		/* check if we have an error. */
-		if(failed && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */
-			/* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */
+		if(RA_CALL_FAILED(return_value,cmd) && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */
+		    /* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */
 			ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra->prev, cmd, cmd_len, z_args, z_new_target?z_new_target:redis_inst);
 		}
 
-		if(!failed && !b_write_cmd && z_new_target && ra->auto_rehash) { /* move key from old ring to new ring */
-				ra_move_key(key, key_len, redis_inst, z_new_target TSRMLS_CC);
+		/* Autorehash if the key was found on the previous node if this is a read command and auto rehashing is on */
+		if(!RA_CALL_FAILED(return_value,cmd) && !b_write_cmd && z_new_target && ra->auto_rehash) { /* move key from old ring to new ring */
+		    ra_move_key(key, key_len, redis_inst, z_new_target TSRMLS_CC);
 		}
 	}
 
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 6b125a4c44..09f7c8b56d 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -273,6 +273,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	/* create RedisArray object */
 	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
 	ra->auto_rehash = b_autorehash;
+	if(ra->prev) ra->prev->auto_rehash = b_autorehash;
 
 	/* cleanup */
 	zval_dtor(z_params_hosts);
@@ -605,6 +606,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) {
 
 	/* don't dtor z_ret, since we're returning z_redis */
 	efree(z_args[0]);
+    zval_dtor(z_args[1]);
 	efree(z_args[1]);
 }
 
@@ -967,6 +969,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl
 		ZVAL_STRINGL(z_args[0], key, key_len, 0);
 		ZVAL_LONG(z_args[1], ttl);
 		ZVAL_STRINGL(z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */
+		zval_dtor(&z_ret); /* free memory from our previous call */
 		call_user_function(&redis_ce->function_table, &z_to, &z_fun_set, &z_ret, 3, z_args TSRMLS_CC);
 		/* cleanup */
 		efree(z_args[1]);
@@ -977,6 +980,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl
 		ZVAL_STRINGL(&z_fun_set, "SET", 3, 0);
 		ZVAL_STRINGL(z_args[0], key, key_len, 0);
 		ZVAL_STRINGL(z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */
+		zval_dtor(&z_ret); /* free memory from our previous return value */
 		call_user_function(&redis_ce->function_table, &z_to, &z_fun_set, &z_ret, 2, z_args TSRMLS_CC);
 		/* cleanup */
 		zval_dtor(z_args[1]);

From 1d47fc8813ee0c975935b79830397ad34ec30ce6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 15 Mar 2014 10:20:55 -0700
Subject: [PATCH 0119/1986] Bump!

---
 php_redis.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/php_redis.h b/php_redis.h
index 4bb79f74d9..80ba22fbd9 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -269,7 +269,7 @@ extern zend_module_entry redis_module_entry;
 
 #define phpext_redis_ptr redis_module_ptr
 
-#define PHP_REDIS_VERSION "2.2.4"
+#define PHP_REDIS_VERSION "2.2.5"
 
 #endif
 

From 94c369cfb6b67aa7a4d1374c7b840e94d57096b0 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 15 Mar 2014 10:41:14 -0700
Subject: [PATCH 0120/1986] Incorporate PECL and rpm changes to reflect 2.2.5

---
 package.xml        | 16 ++++++++++++----
 rpm/php-redis.spec |  2 +-
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/package.xml b/package.xml
index dd97ad7779..573fb9086a 100644
--- a/package.xml
+++ b/package.xml
@@ -21,10 +21,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
   michael.grunder@gmail.com
   yes
  
- 2013-09-01
+ 2014-03-15
  
-  2.2.4
-  2.2.4
+  2.2.5
+  2.2.5
  
  
   stable
@@ -32,7 +32,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
  PHP
  
-	 First public release
+	 Third public release
  
  
   
@@ -70,6 +70,14 @@ http://pear.php.net/dtd/package-2.0.xsd">
  redis
  
  
+   
+   stablestable
+   2.2.52.2.5
+   2014-03-15
+   
+       See GitHub for release notes
+   
+   
    
    stablestable
    2.2.42.2.4
diff --git a/rpm/php-redis.spec b/rpm/php-redis.spec
index 4f04fb1855..5363d1eead 100644
--- a/rpm/php-redis.spec
+++ b/rpm/php-redis.spec
@@ -3,7 +3,7 @@
 %global php_version %(php-config --version 2>/dev/null || echo 0)
 
 Name:           php-redis
-Version:        2.2.4
+Version:        2.2.5
 Release:        1%{?dist}
 Summary:        The phpredis extension provides an API for communicating with the Redis key-value store.
 

From d4b5631b81c6f493a57d62d3d3c9d872063fe06b Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 17 Mar 2014 07:40:42 -0700
Subject: [PATCH 0121/1986] TSRMLS_DC, not TSRMLS_CC in definition

Addresses #444
---
 redis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index efd385432f..478848091a 100644
--- a/redis.c
+++ b/redis.c
@@ -6138,7 +6138,7 @@ PHP_METHOD(Redis, wait) {
  */
 PHPAPI int
 redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
-                       zval *arg TSRMLS_CC)
+                       zval *arg TSRMLS_DC)
 {
     HashTable *ht_chan;
     HashPosition ptr;    

From 2fc8e78711bb44dd863d5cfdb9976c6ae47dd428 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 19 Mar 2014 08:19:11 -0700
Subject: [PATCH 0122/1986] Add my email to CREDITS file

---
 CREDITS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CREDITS b/CREDITS
index 5959659911..6c8626a8a6 100644
--- a/CREDITS
+++ b/CREDITS
@@ -2,3 +2,4 @@ Redis client extension for PHP
 Alfonso Jimenez 
 Nasreddine Bouafif 
 Nicolas Favre-Felix 
+Michael Grunder 

From f07110ba6aae886c94cfc10f9f35dde90b055188 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 19 Mar 2014 09:40:20 -0700
Subject: [PATCH 0123/1986] Update package.xml for the next PECL update

---
 package.xml | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 70 insertions(+), 3 deletions(-)

diff --git a/package.xml b/package.xml
index 573fb9086a..3bf3e60946 100644
--- a/package.xml
+++ b/package.xml
@@ -32,7 +32,25 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
  PHP
  
-	 Third public release
+    phpredis 2.2.5
+    
+    This is a minor release with several bug fixes as well as additions to support
+    new commands that have been introduced to Redis since our last release.
+    
+    A special thanks to everyone who helps the project by commenting on issues and
+    submitting pull requests!  :)
+    
+    [NEW] Support for the BITPOS command
+    [NEW] Connection timeout option for RedisArray (@MikeToString)
+    [NEW] A _serialize method, to complement our existing _unserialize method
+    [NEW] Support for the PUBSUB command
+    [NEW] Support for SCAN, SSCAN, HSCAN, and ZSCAN
+    [NEW] Support for the WAIT command
+    
+    [FIX] Handle the COPY and REPLACE arguments for the MIGRATE command
+    
+    [DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh)
+    [DOC] Homebrew documentation instructions (@mathias)
  
  
   
@@ -75,7 +93,26 @@ http://pear.php.net/dtd/package-2.0.xsd">
    2.2.52.2.5
    2014-03-15
    
-       See GitHub for release notes
+    phpredis 2.2.5
+
+    This is a minor release with several bug fixes as well as additions to support
+    new commands that have been introduced to Redis since our last release.
+
+    A special thanks to everyone who helps the project by commenting on issues and
+    submitting pull requests!  :)
+
+    [NEW] Support for the BITPOS command
+    [NEW] Connection timeout option for RedisArray (@MikeToString)
+    [NEW] A _serialize method, to complement our existing _unserialize method
+    [NEW] Support for the PUBSUB command
+    [NEW] Support for SCAN, SSCAN, HSCAN, and ZSCAN
+    [NEW] Support for the WAIT command
+
+    [FIX] Handle the COPY and REPLACE arguments for the MIGRATE command
+
+    [DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh)
+    [DOC] Homebrew documentation instructions (@mathias)
+
    
    
    
@@ -83,7 +120,37 @@ http://pear.php.net/dtd/package-2.0.xsd">
    2.2.42.2.4
    2013-09-01
    
-	   See GitHub for release notes
+   **
+   ** Features / Improvements
+   **
+
+   * Randomized reconnect delay for RedisArray @mobli
+     This feature adds an optional parameter when constructing a RedisArray object
+     such that a random delay will be introduced if reconnections are made,
+     mitigating any 'thundering herd' type problems.
+
+   * Lazy connections to RedisArray servers @mobli
+     By default, RedisArray will attempt to connect to each server you pass in
+     the ring on construction.  This feature lets you specify that you would
+     rather have RedisArray only attempt a connection when it needs to get data
+     from a particular node (throughput/performance improvement).
+
+   * Allow LONG and STRING keys in MGET/MSET
+   * Extended SET options for Redis >= 2.6.12
+   * Persistent connections and UNIX SOCKET support for RedisArray
+   * Allow aggregates for ZUNION/ZINTER without weights @mheijkoop
+   * Support for SLOWLOG command
+   * Reworked MGET algorithm to run in linear time regardless of key count.
+   * Reworked ZINTERSTORE/ZUNIONSTORE algorithm to run in linear time
+
+   **
+   ** Bug fixes
+   **
+
+   * C99 Compliance (or rather lack thereof) fix @mobli
+   * Added ZEND_ACC_CTOR and ZEND_ACC_DTOR @euskadi31
+   * Stop throwing and clearing an exception on connect failure @matmoi
+   * Fix a false positive unit test failure having to do with TTL returns
    
   
    

From a6aa390675a2fa51ad7dfdf38adf3146dc7a7876 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 20 Mar 2014 09:28:27 +0100
Subject: [PATCH 0124/1986] add tests to pecl package, fixed #332

---
 package.xml | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/package.xml b/package.xml
index 3bf3e60946..f008cfb128 100644
--- a/package.xml
+++ b/package.xml
@@ -71,6 +71,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
    
    
    
+   
+     
+     
+     
+     
+     
+    
    
  
  

From 5520be1042d1b97728a3f8e04ed3c5a6fd0da9f8 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 20 Mar 2014 13:48:04 +0100
Subject: [PATCH 0125/1986] Fix memory corruption observed with PHP 5.6.0-dev

---
 library.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/library.c b/library.c
index e4ad72b9ee..6525ade4b9 100644
--- a/library.c
+++ b/library.c
@@ -1560,6 +1560,11 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_
 					ZVAL_STRINGL(z_copy, "Array", 5, 1);
 					break;
 
+				case IS_NULL:
+					MAKE_STD_ZVAL(z_copy);
+					ZVAL_STRINGL(z_copy, "", 0, 1);
+					break;
+
 				default: /* copy */
 					MAKE_STD_ZVAL(z_copy);
 					*z_copy = *z;

From 8805b133b9b1d0bf18a59ef97d95ca8c3a1ab9a7 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 20 Mar 2014 13:49:26 +0100
Subject: [PATCH 0126/1986] redis.c:7022:13: warning: 'keyword' may be used
 uninitialized in this function (mostly to make gcc happy)

---
 redis.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/redis.c b/redis.c
index 478848091a..c17bab1037 100644
--- a/redis.c
+++ b/redis.c
@@ -7014,6 +7014,7 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
             keyword = "HSCAN";
             break;
         case TYPE_ZSCAN:
+		default:
             keyword = "ZSCAN";
             break;
     }

From 7634db4283409d1fc113596d00a12e531d05c13f Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 20 Mar 2014 13:54:53 +0100
Subject: [PATCH 0127/1986] Fix: library.c:743:78: warning: unused variable
 'p2' (PLEASE CHECK)

---
 library.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library.c b/library.c
index 6525ade4b9..680debd946 100644
--- a/library.c
+++ b/library.c
@@ -774,8 +774,8 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
 
                     // Treat numbers as numbers, strings as strings
                     is_numeric = 1;
-                    for(p2 = value; *p; ++p) {
-                        if(*p < '0' || *p > '9') {
+                    for(p2 = value; *p2; ++p2) {
+                        if(*p2 < '0' || *p2 > '9') {
                             is_numeric = 0;
                             break;
                         }

From 47ddcf75fd3582d50937bf90750b67531db5d5c8 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 20 Mar 2014 13:56:11 +0100
Subject: [PATCH 0128/1986] Fix ibrary.c:1541:9: warning: unused variable 'sz'
 + 'val8'

---
 library.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/library.c b/library.c
index 680debd946..f02f559ecb 100644
--- a/library.c
+++ b/library.c
@@ -1538,8 +1538,10 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_
 #endif
 	smart_str sstr = {0};
 	zval *z_copy;
+#ifdef HAVE_REDIS_IGBINARY
 	size_t sz;
 	uint8_t *val8;
+#endif
 
 	switch(redis_sock->serializer) {
 		case REDIS_SERIALIZER_NONE:

From d74b0d27f347d6fc77c8310259f8462890d38f9b Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 20 Mar 2014 13:56:49 +0100
Subject: [PATCH 0129/1986] Fix warning: 'klen' may be used uninitialized in
 this function

---
 library.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library.c b/library.c
index f02f559ecb..57d413e998 100644
--- a/library.c
+++ b/library.c
@@ -743,7 +743,7 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
     char *p = resp, *lpos = resp, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
 
     // Key length, done flag
-    int klen, done = 0, is_numeric;
+    int klen = 0, done = 0, is_numeric;
 
     // While we've got more to parse
     while(!done) {

From b9a16b5ad5d8c80eac53f530f37e89761ec2c720 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 20 Mar 2014 15:06:40 +0100
Subject: [PATCH 0130/1986] revert previous, and better fix for memory
 corruption (STR_FREE available since 5.0)

---
 library.c |  5 -----
 redis.c   | 47 ++++++++++++++++++++++++-----------------------
 2 files changed, 24 insertions(+), 28 deletions(-)

diff --git a/library.c b/library.c
index 57d413e998..b0f15db4a5 100644
--- a/library.c
+++ b/library.c
@@ -1562,11 +1562,6 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_
 					ZVAL_STRINGL(z_copy, "Array", 5, 1);
 					break;
 
-				case IS_NULL:
-					MAKE_STD_ZVAL(z_copy);
-					ZVAL_STRINGL(z_copy, "", 0, 1);
-					break;
-
 				default: /* copy */
 					MAKE_STD_ZVAL(z_copy);
 					*z_copy = *z;
diff --git a/redis.c b/redis.c
index c17bab1037..8c12657ae8 100644
--- a/redis.c
+++ b/redis.c
@@ -990,7 +990,7 @@ PHP_METHOD(Redis, set) {
 
     /* Free our key or value if we prefixed/serialized */
     if(key_free) efree(key);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
 
     /* Kick off the command */
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1023,7 +1023,7 @@ PHPAPI void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1072,7 +1072,7 @@ PHP_METHOD(Redis, setnx)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1110,7 +1110,7 @@ PHP_METHOD(Redis, getSet)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1928,7 +1928,7 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -2004,9 +2004,9 @@ PHP_METHOD(Redis, lInsert)
         val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
         pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC);
         cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len);
-        if(val_free) efree(val);
+        if(val_free) STR_FREE(val);
 		if(key_free) efree(key);
-        if(pivot_free) efree(pivot);
+        if(pivot_free) STR_FREE(pivot);
 
 		REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); 
 		IF_ATOMIC() { 
@@ -2178,7 +2178,7 @@ PHP_METHOD(Redis, lRemove)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -2382,7 +2382,7 @@ PHP_METHOD(Redis, sMove)
 	src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC);
 	dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(src_free) efree(src);
     if(dst_free) efree(dst);
 
@@ -2488,7 +2488,7 @@ PHP_METHOD(Redis, sContains)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -2716,7 +2716,7 @@ PHPAPI int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword
 	/* cleanup prefixed keys. */
 	for(i = 0; i < real_argc + (has_timeout?-1:0); ++i) {
 		if(keys_to_free[i])
-			efree(keys[i]);
+			STR_FREE(keys[i]);
 	}
 	if(single_array && has_timeout) { /* cleanup string created to contain timeout value */
 		efree(keys[real_argc-1]);
@@ -3362,7 +3362,7 @@ PHP_METHOD(Redis, lSet) {
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -3782,7 +3782,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI
 				memcpy(p, _NL, 2); p += 2;
 			}
 
-			if(val_free) efree(val);
+			if(val_free) STR_FREE(val);
 			if(key_free) efree(key);
 		}
 	}
@@ -3964,7 +3964,7 @@ PHP_METHOD(Redis, zAdd) {
 		smart_str_appendl(&buf, val, val_len);
 		smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
 
-		if(val_free) efree(val);
+		if(val_free) STR_FREE(val);
 	}
 
 	/* end string */
@@ -4361,7 +4361,7 @@ PHP_METHOD(Redis, zScore)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4394,7 +4394,7 @@ PHPAPI void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4444,7 +4444,7 @@ PHPAPI void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, i
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4659,7 +4659,7 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -5179,7 +5179,7 @@ PHP_METHOD(Redis, hMset)
         redis_cmd_append_sstr(&set_cmds, hkey, hkey_len - 1);
         redis_cmd_append_sstr(&set_cmds, hval, hval_len);
 
-        if(hval_free) efree(hval);
+        if(hval_free) STR_FREE(hval);
     }
 
     // Now construct the entire command
@@ -6685,7 +6685,7 @@ PHP_METHOD(Redis, _serialize) {
     RedisSock *redis_sock;
     zval *z_val;
     char *val;
-    int val_free, val_len;
+    int val_len;
 
     // Parse arguments
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
@@ -6700,10 +6700,11 @@ PHP_METHOD(Redis, _serialize) {
     }
 
     // Serialize, which will return a value even if no serializer is set
-    val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
+    redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
 
-    // Return serialized value.  Tell PHP to make a copy if redis_serialize didn't.
-    RETURN_STRINGL(val, val_len, !val_free);
+    // Return serialized value.  Tell PHP to make a copy as some can be interned.
+    RETVAL_STRINGL(val, val_len, 1);
+    STR_FREE(val);
 }
 
 /*

From 110a9933a3b74ea466078b9f3e0d90a454c70834 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Apr 2014 16:30:11 -0700
Subject: [PATCH 0131/1986] Initial commit of HyperLogLog commands

This is the initial commit of the HyperLogLog probabilistic counting
command introduced in Redis.

Support for the following commands is implemented

  * PFADD    ... 
  * PFCOUNT 
  * PFMERGE    ... 
---
 php_redis.h         |   5 ++
 redis.c             | 206 ++++++++++++++++++++++++++++++++++++++++++++
 tests/TestRedis.php |  82 +++++++++++++++++-
 3 files changed, 292 insertions(+), 1 deletion(-)

diff --git a/php_redis.h b/php_redis.h
index 80ba22fbd9..3c802bc64f 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -195,6 +195,11 @@ PHP_METHOD(Redis, hscan);
 PHP_METHOD(Redis, sscan);
 PHP_METHOD(Redis, zscan);
 
+/* HyperLogLog commands */
+PHP_METHOD(Redis, pfadd);
+PHP_METHOD(Redis, pfcount);
+PHP_METHOD(Redis, pfmerge);
+
 /* Reflection */
 PHP_METHOD(Redis, getHost);
 PHP_METHOD(Redis, getPort);
diff --git a/redis.c b/redis.c
index 8c12657ae8..f796d0b32c 100644
--- a/redis.c
+++ b/redis.c
@@ -257,6 +257,11 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC)
 
+     /* HyperLogLog commands */
+     PHP_ME(Redis, pfadd, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, pfcount, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, pfmerge, NULL, ZEND_ACC_PUBLIC)
+
      /* options */
      PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, setOption, NULL, ZEND_ACC_PUBLIC)
@@ -7154,4 +7159,205 @@ PHP_METHOD(Redis, zscan) {
     generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
 }
 
+/* 
+ * HyperLogLog based commands 
+ */
+
+/* {{{ proto Redis::PFADD(string key, array elements) }}} */
+PHP_METHOD(Redis, pfadd) {
+    zval *object;
+    RedisSock *redis_sock;
+    char *key;
+    int key_len, key_free, argc=1;
+    zval *z_mems, **z_mem;
+    HashTable *ht_mems;
+    HashPosition pos;
+    smart_str cmd = {0};
+
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
+                                    &object, redis_ce, &key, &key_len, &z_mems)
+                                    ==FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // Grab members as an array
+    ht_mems = Z_ARRVAL_P(z_mems);
+
+    // Total arguments we'll be sending
+    argc += zend_hash_num_elements(ht_mems);
+
+    // If the array was empty we can just exit
+    if(argc < 3) {
+        RETURN_FALSE;
+    }
+
+    // Start constructing our command
+    redis_cmd_init_sstr(&cmd, argc, "PFADD", sizeof("PFADD")-1);
+
+    // Prefix our key if we're prefixing
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+    redis_cmd_append_sstr(&cmd, key, key_len);
+    if(key_free) efree(key);
+
+    // Iterate over members we're adding
+    for(zend_hash_internal_pointer_reset_ex(ht_mems, &pos);
+        zend_hash_get_current_data_ex(ht_mems, (void**)&z_mem, &pos)==SUCCESS;
+        zend_hash_move_forward_ex(ht_mems, &pos))
+    {
+        char *mem;
+        int mem_len, val_free;
+        zval *z_tmp = NULL;
+
+        // Serialize if requested
+        val_free = redis_serialize(redis_sock, *z_mem, &mem, &mem_len TSRMLS_CC);
+
+        // Allow for non string members if we're not serializing
+        if(!val_free) {
+            if(Z_TYPE_PP(z_mem)==IS_STRING) {
+                mem = Z_STRVAL_PP(z_mem);
+                mem_len = Z_STRLEN_PP(z_mem);
+            } else {
+                MAKE_STD_ZVAL(z_tmp);
+                *z_tmp = **z_mem;
+                convert_to_string(z_tmp);
+
+                mem = Z_STRVAL_P(z_tmp);
+                mem_len = Z_STRLEN_P(z_tmp);
+            }
+        }
+    
+        // Append this member
+        redis_cmd_append_sstr(&cmd, mem, mem_len);
+
+        // Free memory if we serialized or converted types
+        if(z_tmp) {
+            zval_dtor(z_tmp);
+            STR_FREE(z_tmp);
+            z_tmp = NULL;
+        } else if(val_free) {
+            efree(mem);
+        }
+    }
+   
+    REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); 
+    IF_ATOMIC() {
+        redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_1_response);   
+}
+
+/* {{{ proto Redis::PHFCOUNT(string key) }}}*/
+PHP_METHOD(Redis, pfcount) {
+    zval *object;
+    RedisSock *redis_sock;
+    char *key, *cmd;
+    int key_len, cmd_len, key_free;
+
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+                                    &object, redis_ce, &key, &key_len)==FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // Prefix key if neccisary and construct our command
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+    cmd_len = redis_cmd_format_static(&cmd, "PFCOUNT", "s", key, key_len);
+    if(key_free) efree(key);
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_long_response);
+}
+
+/* {{{ proto Redis::pfMerge(array keys) }}}*/
+PHP_METHOD(Redis, pfmerge) {
+    zval *object;    
+    RedisSock *redis_sock;
+    zval *z_keys, **z_key;
+    HashTable *ht_keys;
+    HashPosition pos;
+    smart_str cmd = {0};
+    int key_len, key_free, argc=1;
+    char *key;
+
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
+                                    &object, redis_ce, &key, &key_len, &z_keys)==FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // Grab keys as an array
+    ht_keys = Z_ARRVAL_P(z_keys);
+
+    // Total arguments we'll be sending
+    argc += zend_hash_num_elements(ht_keys);
+
+    // If no keys were passed we can abort
+    if(argc<2) {
+        RETURN_FALSE;
+    }
+
+    // Initial construction of our command
+    redis_cmd_init_sstr(&cmd, argc, "PFMERGE", sizeof("PFMERGE")-1);
+
+    // Add our destination key (prefixed if necessary)
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+    redis_cmd_append_sstr(&cmd, key, key_len);
+    if(key_free) efree(key);
+
+    // Iterate our keys array
+    for(zend_hash_internal_pointer_reset_ex(ht_keys, &pos);
+        zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &pos)==SUCCESS;
+        zend_hash_move_forward_ex(ht_keys, &pos))
+    {
+        zval *z_tmp = NULL;
+
+        // Keys could look like a number
+        if(Z_TYPE_PP(z_key) == IS_STRING) {
+            key = Z_STRVAL_PP(z_key);
+            key_len = Z_STRLEN_PP(z_key);
+        } else {
+            MAKE_STD_ZVAL(z_tmp);
+            *z_tmp = **z_key;
+            convert_to_string(z_tmp);
+
+            key = Z_STRVAL_P(z_tmp);
+            key_len = Z_STRLEN_P(z_tmp);
+        }
+
+        // Prefix our key if necissary and append this key
+        key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+        redis_cmd_append_sstr(&cmd, key, key_len);
+        if(key_free) efree(key);
+
+        // Free temporary zval if we converted
+        if(z_tmp) {
+            zval_dtor(z_tmp);
+            STR_FREE(z_tmp);
+            z_tmp = NULL;
+        }
+    }
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
+    IF_ATOMIC() {
+        redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_boolean_response);
+}
+
 /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 83fdd5cca3..13b43a977e 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -4492,7 +4492,7 @@ public function testSerialize() {
         $vals = Array(1, 1.5, 'one', Array('here','is','an','array'));
         
         // Test with no serialization at all
-        $this->assertTrue($this->redis->_serialize('test') === 'test');
+        $this->assertTrue($this->redis->_serialize('test') === 'test');        
         $this->assertTrue($this->redis->_serialize(1) === '1');
         $this->assertTrue($this->redis->_serialize(Array()) === 'Array');
         $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object');
@@ -4806,6 +4806,86 @@ public function testZScan() {
         $this->assertEquals(0, $i_p_score);
         $this->assertEquals(0, $i_p_count);
     }
+
+    //
+    // HyperLogLog (PF) commands
+    //
+
+    protected function createPFKey($str_key, $i_count) {
+        $arr_mems = Array();
+        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", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $str_uniq = uniqid();
+        $arr_mems = Array();
+
+        for($i=0;$i<1000;$i++) {
+            if($i%2 == 0) {
+                $arr_mems[] = $str_uniq . '-' . $i;
+            } else {
+                $arr_mems[] = $i;
+            }
+        }
+
+        // How many keys to create
+        $i_keys = 10;
+
+        // Iterate prefixing/serialization options
+        foreach(Array(Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP) as $str_ser) {
+            foreach(Array('', 'hl-key-prefix:') as $str_prefix) {
+                $arr_keys = Array();
+
+                // 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');
+            }
+        }
+    }
 }
 
 exit(TestSuite::run("Redis_Test"));

From 7256026b4e208a7915c06bc3c4465ccb8f84e2fd Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Apr 2014 16:37:28 -0700
Subject: [PATCH 0132/1986] Fix an invalid free in _serialize

If serialization isn't on, and you call _serialize with a string,
the string is not copied at all, so we don't need to STR_FREE it
---
 redis.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/redis.c b/redis.c
index 8c12657ae8..d3976583ae 100644
--- a/redis.c
+++ b/redis.c
@@ -6700,11 +6700,11 @@ PHP_METHOD(Redis, _serialize) {
     }
 
     // Serialize, which will return a value even if no serializer is set
-    redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
+    int val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
 
     // Return serialized value.  Tell PHP to make a copy as some can be interned.
     RETVAL_STRINGL(val, val_len, 1);
-    STR_FREE(val);
+    if(val_free) STR_FREE(val);
 }
 
 /*

From 189e280b4c140ce3d20c767df115ebb281845574 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Apr 2014 16:47:32 -0700
Subject: [PATCH 0133/1986] Add an additional test function to test.php

---
 tests/test.php | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/tests/test.php b/tests/test.php
index 8ccba5cfb7..6c714f1b4c 100644
--- a/tests/test.php
+++ b/tests/test.php
@@ -19,6 +19,16 @@ protected function assertTrue($bool) {
 			$bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
 	}
 
+    protected function assertLess($a, $b) {
+        if($a < $b) 
+            return;
+
+        $bt = debug_backtrace(false);
+        self::$errors[] = sprintf("Assertion failed (%s >= %s): %s: %d (%s\n",
+            print_r($a, true), print_r($b, true),
+            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
+    }
+
 	protected function assertEquals($a, $b) {
 		if($a === $b)
 			return;
@@ -43,7 +53,6 @@ public static function run($className) {
 		$methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC);
 
 		foreach($methods as $m) {
-
 			$name = $m->name;
 			if(substr($name, 0, 4) !== 'test')
 				continue;

From 76f77dbaaa700d74a3de217afbba8def48f51002 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Apr 2014 16:59:06 -0700
Subject: [PATCH 0134/1986] Use efree on a zval, not STR_FREE() :)

---
 redis.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/redis.c b/redis.c
index f796d0b32c..a9a7d95b6d 100644
--- a/redis.c
+++ b/redis.c
@@ -7163,7 +7163,7 @@ PHP_METHOD(Redis, zscan) {
  * HyperLogLog based commands 
  */
 
-/* {{{ proto Redis::PFADD(string key, array elements) }}} */
+/* {{{ proto Redis::pfAdd(string key, array elements) }}} */
 PHP_METHOD(Redis, pfadd) {
     zval *object;
     RedisSock *redis_sock;
@@ -7237,7 +7237,7 @@ PHP_METHOD(Redis, pfadd) {
         // Free memory if we serialized or converted types
         if(z_tmp) {
             zval_dtor(z_tmp);
-            STR_FREE(z_tmp);
+            efree(z_tmp);
             z_tmp = NULL;
         } else if(val_free) {
             efree(mem);
@@ -7251,7 +7251,7 @@ PHP_METHOD(Redis, pfadd) {
     REDIS_PROCESS_RESPONSE(redis_1_response);   
 }
 
-/* {{{ proto Redis::PHFCOUNT(string key) }}}*/
+/* {{{ proto Redis::pfCount(string key) }}}*/
 PHP_METHOD(Redis, pfcount) {
     zval *object;
     RedisSock *redis_sock;
@@ -7340,7 +7340,7 @@ PHP_METHOD(Redis, pfmerge) {
             key_len = Z_STRLEN_P(z_tmp);
         }
 
-        // Prefix our key if necissary and append this key
+        // Prefix our key if necessary and append this key
         key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
         redis_cmd_append_sstr(&cmd, key, key_len);
         if(key_free) efree(key);
@@ -7348,7 +7348,7 @@ PHP_METHOD(Redis, pfmerge) {
         // Free temporary zval if we converted
         if(z_tmp) {
             zval_dtor(z_tmp);
-            STR_FREE(z_tmp);
+            efree(z_tmp);
             z_tmp = NULL;
         }
     }

From 85fa1361dee1d90aa98a2e3a56f7c95980051a40 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Apr 2014 18:48:02 -0700
Subject: [PATCH 0135/1986] Calculate the proper minimum argc.  :)

---
 redis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index a9a7d95b6d..b88f9a9080 100644
--- a/redis.c
+++ b/redis.c
@@ -7192,7 +7192,7 @@ PHP_METHOD(Redis, pfadd) {
     argc += zend_hash_num_elements(ht_mems);
 
     // If the array was empty we can just exit
-    if(argc < 3) {
+    if(argc < 2) {
         RETURN_FALSE;
     }
 

From 215dc3e935cda95c6892cb3a992a3963f2610dbe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mariano=20P=C3=A9rez=20Rodr=C3=ADguez?=
 
Date: Mon, 7 Apr 2014 23:13:36 -0300
Subject: [PATCH 0136/1986] Fixed missing "~~~" before "_userialize"

My OCD compelled me to fix this, it was just a missing "~~~" right after the "_serialize" examples, nothing fancy, it just really hurt to see all the markdown messed up :)
---
 README.markdown | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.markdown b/README.markdown
index 634193ac6f..fbf7d7425b 100644
--- a/README.markdown
+++ b/README.markdown
@@ -3182,6 +3182,7 @@ $redis->_serialize(new stdClass()); // Returns "Object"
 
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
 $redis->_serialize("foo"); // Returns 's:3:"foo";'
+~~~
 
 ### _unserialize
 -----

From 9c12c40a66cbd76f5efa053b1718fa936320400b Mon Sep 17 00:00:00 2001
From: vostok4 
Date: Wed, 9 Apr 2014 11:14:45 +0200
Subject: [PATCH 0137/1986] Merge nicolasff:b9a16b5ad5 in, fixing for Win32

Now we should be up to master with upstream for an easier merge.
---
 CREDITS             |   1 +
 README.markdown     | 177 ++++++++++++-
 common.h            |  28 ++
 library.c           |  54 +++-
 library.h           |   2 +
 package.xml         |  92 ++++++-
 php_redis.h         |  14 +-
 redis.c             | 609 +++++++++++++++++++++++++++++++++++++++++---
 redis_array.c       |  72 ++++--
 redis_array.h       |   1 +
 redis_array_impl.c  |  40 ++-
 redis_array_impl.h  |   2 +-
 rpm/php-redis.spec  |   2 +-
 tests/TestRedis.php | 344 +++++++++++++++++++++++++
 14 files changed, 1352 insertions(+), 86 deletions(-)

diff --git a/CREDITS b/CREDITS
index c501060a77..5301007913 100644
--- a/CREDITS
+++ b/CREDITS
@@ -2,3 +2,4 @@ Redis client extension for PHP
 Alfonso Jimenez (yo@alfonsojimenez.com)
 Nasreddine Bouafif (n.bouafif@owlient.eu)
 Nicolas Favre-Felix (n.favre-felix@owlient.eu)
+Michael Grunder (michael.grunder@gmail.com)
\ No newline at end of file
diff --git a/README.markdown b/README.markdown
index 7d5d2dc86f..91d09f195a 100644
--- a/README.markdown
+++ b/README.markdown
@@ -3,7 +3,7 @@
 The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt).
 This code has been developed and maintained by Owlient from November 2009 to March 2011.
 
-You can send comments, patches, questions [here on github](https://github.com/nicolasff/phpredis/issues) or to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)).
+You can send comments, patches, questions [here on github](https://github.com/nicolasff/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)), or to michael.grunder@gmail.com ([@grumi78](http://twitter.com/grumi78)).
 
 
 # Table of contents
@@ -69,6 +69,10 @@ Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tag
 
 See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports).
 
+You can install install it using Homebrew:
+
+- [Get homebrew-php](https://github.com/josegonzalez/homebrew-php)
+- `brew install php55-redis` (or php53-redis, php54-redis)
 
 ## PHP Session handler
 
@@ -268,6 +272,15 @@ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);	// use built-in
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);	// use igBinary serialize/unserialize
 
 $redis->setOption(Redis::OPT_PREFIX, 'myAppName:');	// use custom prefix on all keys
+
+/* Options for the SCAN family of commands, indicating whether to abstract
+   empty results from the user.  If set to SCAN_NORETRY (the default), phpredis
+   will just issue one SCAN command at a time, sometimes returning an empty
+   array of results.  If set to SCAN_RETRY, phpredis will retry the scan command
+   until keys come back OR Redis returns an iterator of zero
+*/
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
 ~~~
 
 
@@ -607,6 +620,7 @@ $redis->slowlog('len');
 * [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds
 * [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp
 * [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern
+* [scan](#scan) - Scan for keys in the keyspace (Redis >= 2.8.0)
 * [migrate](#migrate) - Atomically transfer a key from a Redis instance to another one
 * [move](#move) - Move a key to another database
 * [object](#object) - Inspect the internals of Redis objects
@@ -658,10 +672,10 @@ $redis->set('key', 'value');
 $redis->set('key','value', 10);
 
 // Will set the key, if it doesn't exist, with a ttl of 10 seconds
-$redis->set('key', 'value', Array('nx', 'ex'=>10);
+$redis->set('key', 'value', Array('nx', 'ex'=>10));
 
 // Will set a key, if it does exist, with a ttl of 1000 miliseconds
-$redis->set('key', 'value', Array('xx', 'px'=>1000);
+$redis->set('key', 'value', Array('xx', 'px'=>1000));
 
 ~~~
 
@@ -780,7 +794,7 @@ $redis->incrByFloat('key1', 1.5); /* key1 didn't exist, so it will now be 1.5 */
 
 $redis->incrByFloat('key1', 1.5); /* 3 */
 $redis->incrByFloat('key1', -1.5); /* 1.5 */
-$redis->incrByFloat('key1', 2.5); /* 3.5 */
+$redis->incrByFloat('key1', 2.5); /* 4 */
 ~~~
 
 ### decr, decrBy
@@ -953,7 +967,29 @@ $allKeys = $redis->keys('*');	// all keys will match this.
 $keyWithUserPrefix = $redis->keys('user*');
 ~~~
 
+### scan
+-----
+_**Description**_:  Scan the keyspace for keys
+
+##### *Parameters*
+*LONG (reference)*:  Iterator, initialized to NULL
+*STRING, Optional*:  Pattern to match
+*LONG, Optional*: Count of keys per iteration (only a suggestion to Redis)
 
+##### *Return value*
+*Array, boolean*:  This function will return an array of keys or FALSE if there are no more keys
+
+##### *Example*
+~~~
+$it = NULL; /* Initialize our iterator to NULL */
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* retry when we get no keys back */
+while($arr_keys = $redis->scan($it)) {
+    foreach($arr_keys as $str_key) {
+        echo "Here is a key: $str_key\n";
+    }
+    echo "No more keys to scan!\n";
+}
+~~~
 
 ### object
 -----
@@ -1261,9 +1297,13 @@ _**Description**_: Migrates a key to a different Redis instance.
 *key* string. The key to migrate.  
 *destination-db* integer.  The target DB.  
 *timeout* integer.  The maximum amount of time given to this transfer.  
+*copy* boolean, optional.  Should we send the COPY flag to redis
+*replace* boolean, optional.  Should we send the REPLACE flag to redis 
 ##### *Examples*
 ~~~
 $redis->migrate('backup', 6379, 'foo', 0, 3600);
+$redis->migrate('backup', 6379, 'foo', 0, 3600, true, true); /* copy and replace */
+$redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE flag */
 ~~~
 
 
@@ -1283,6 +1323,7 @@ $redis->migrate('backup', 6379, 'foo', 0, 3600);
 * [hSet](#hset) - Set the string value of a hash field
 * [hSetNx](#hsetnx) - Set the value of a hash field, only if the field does not exist
 * [hVals](#hvals) - Get all the values in a hash
+* [hScan](#hscan) - Scan a hash key for members
 
 ### hSet
 -----
@@ -1542,7 +1583,28 @@ $redis->hSet('h', 'field2', 'value2');
 $redis->hmGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */
 ~~~
 
+### hScan
+-----
+_**Description**_:  Scan a HASH value for members, with an optional pattern and count
+##### *Parameters*
+*key*: String
+*iterator*: Long (reference)
+*pattern*: Optional pattern to match against
+*count*: How many keys to return in a go (only a sugestion to Redis)
+##### *Return value* 
+*Array* An array of members that match our pattern
 
+##### *Examples*
+~~~
+$it = NULL;
+/* Don't ever return an empty array until we're done iterating */
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+while($arr_keys = $redis->hscan('hash', $it)) {
+    foreach($arr_keys as $str_field => $str_value) {
+        echo "$str_field => $str_value\n"; /* Print the hash member and value */
+    }
+}
+~~~
 
 ## Lists
 
@@ -1981,6 +2043,7 @@ $redis->lSize('key1');/* 2 */
 * [sRem, sRemove](#srem-sremove) - Remove one or more members from a set
 * [sUnion](#sunion) - Add multiple sets
 * [sUnionStore](#sunionstore) - Add multiple sets and store the resulting set in a key
+* [sScan](#sscan) - Scan a set for members
 
 ### sAdd
 -----
@@ -2380,6 +2443,41 @@ array(4) {
 }
 ~~~
 
+### sScan
+-----
+_**Description**_: Scan a set for members
+
+##### *Parameters*
+*Key*: The set to search
+*iterator*: LONG (reference) to the iterator as we go
+*pattern*: String, optional pattern to match against
+*count*: How many members to return at a time (Redis might return a different amount)
+
+##### *Return value*
+*Array, boolean*: PHPRedis will return an array of keys or FALSE when we're done iterating
+
+##### *Example*
+~~~
+$it = NULL;
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* don't return empty results until we're done */
+while($arr_mems = $redis->sscan('set', $it, "*pattern*")) {
+    foreach($arr_mems as $str_mem) {
+        echo "Member: $str_mem\n";
+    }
+}
+
+$it = NULL;
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); /* return after each iteration, even if empty */
+while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) {
+    if(count($arr_mems) > 0) {
+        foreach($arr_mems as $str_mem) {
+            echo "Member found: $str_mem\n";
+        }
+    } else {
+        echo "No members in this iteration, iterator value: $it\n";
+    }
+}
+~~~
 
 ## Sorted sets
 
@@ -2397,6 +2495,7 @@ array(4) {
 * [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low
 * [zScore](#zscore) - Get the score associated with the given member in a sorted set
 * [zUnion](#zunion) - Add multiple sorted sets and store the resulting sorted set in a new key
+* [zScan](#zscan) - Scan a sorted set for members
 
 ### zAdd
 -----
@@ -2736,11 +2835,36 @@ $redis->zUnion('ko2', array('k1', 'k2'), array(1, 1)); /* 4, 'ko2' => array('val
 $redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val0', 'val2', 'val3', 'val1') */
 ~~~
 
+### zScan
+-----
+_**Description**_: Scan a sorted set for members, with optional pattern and count
+
+##### *Parameters*
+*key*: String, the set to scan
+*iterator*: Long (reference), initialized to NULL
+*pattern*: String (optional), the pattern to match
+*count*: How many keys to return per iteration (Redis might return a different number)
+
+##### *Return value*
+*Array, boolean* PHPRedis will return matching keys from Redis, or FALSE when iteration is complete
+
+##### *Example*
+~~~
+$it = NULL;
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+while($arr_matches = $redis->zscan('zset', $it, '*pattern*')) {
+    foreach($arr_matches as $str_mem => $f_score) {
+        echo "Key: $str_mem, Score: $f_score\n";
+    }
+}
+~~~
+
 ## Pub/sub
 
 * [psubscribe](#psubscribe) - Subscribe to channels by pattern
 * [publish](#publish) - Post a message to a channel
 * [subscribe](#subscribe) - Subscribe to channels
+* [pubsub](#pubsub) - Introspection into the pub/sub subsystem
 
 ### psubscribe
 -----
@@ -2801,6 +2925,26 @@ function f($redis, $chan, $msg) {
 $redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
 ~~~
 
+### pubsub
+-----
+_**Description**_: A command allowing you to get information on the Redis pub/sub system.
+
+##### *Parameters*
+*keyword*: String, which can be: "channels", "numsub", or "numpat"
+*argument*:  Optional, variant.  For the "channels" subcommand, you can pass a string pattern.  For "numsub" an array of channel names.
+
+##### *Return value*
+*CHANNELS*: Returns an array where the members are the matching channels.
+*NUMSUB*:  Returns a key/value array where the keys are channel names and values are their counts.
+*NUMPAT*:  Integer return containing the number active pattern subscriptions
+
+##### *Example*
+~~~
+$redis->pubsub("channels"); /*All channels */
+$redis->pubsub("channels", "*pattern*"); /* Just channels matching your pattern */
+$redis->pubsub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/
+$redsi->pubsub("numpat"); /* Get the number of pattern subscribers */
+```
 
 ## Transactions
 
@@ -2867,6 +3011,7 @@ $ret = FALSE if x has been modified between the call to WATCH and the call to EX
 * [clearLastError](#) - Clear the last error message
 * [_prefix](#) - A utility method to prefix the value with the prefix setting for phpredis
 * [_unserialize](#) - A utility method to unserialize data with whatever serializer is set up
+* [_serialize](#) - A utility method to serialize data with whatever serializer is set up
 
 ### eval
 -----
@@ -3016,6 +3161,28 @@ $redis->setOption(Redis::OPT_PREFIX, 'my-prefix:');
 $redis->_prefix('my-value'); // Will return 'my-prefix:my-value'
 ~~~
 
+### _serialize
+-----
+_**Description**_: A utility method to serialize values manually.
+
+This method allows you to serialize a value with whatever serializer is configured, manually.
+This can be useful for serialization/unserialization of data going in and out of EVAL commands
+as phpredis can't automatically do this itself.  Note that if no serializer is set, phpredis
+will change Array values to 'Array', and Objects to 'Object'.
+
+##### *Parameters*
+*value*:  Mixed.  The value to be serialized
+
+##### *Examples*
+~~~
+$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
+$redis->_serialize("foo"); // returns "foo"
+$redis->_serialize(Array()); // Returns "Array"
+$redis->_serialize(new stdClass()); // Returns "Object"
+
+$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
+$redis->_serialize("foo"); // Returns 's:3:"foo";'
+
 ### _unserialize
 -----
 _**Description**_: A utility method to unserialize data with whatever serializer is set up.
@@ -3080,7 +3247,7 @@ None
 
 ### GetTimeout
 -----
-_**Description**_:  Get the (write) timeout in use for phpreids
+_**Description**_:  Get the (write) timeout in use for phpredis
 
 ##### *Parameters*
 None  
diff --git a/common.h b/common.h
index 63b1d9ee30..a0623fbdc7 100644
--- a/common.h
+++ b/common.h
@@ -37,22 +37,48 @@ typedef enum _REDIS_REPLY_TYPE {
 	TYPE_MULTIBULK = '*'
 } REDIS_REPLY_TYPE;
 
+/* SCAN variants */
+typedef enum _REDIS_SCAN_TYPE {
+    TYPE_SCAN,
+    TYPE_SSCAN,
+    TYPE_HSCAN,
+    TYPE_ZSCAN
+} REDIS_SCAN_TYPE;
+
+/* PUBSUB subcommands */
+typedef enum _PUBSUB_TYPE {
+    PUBSUB_CHANNELS,
+    PUBSUB_NUMSUB,
+    PUBSUB_NUMPAT
+} PUBSUB_TYPE;
+
 /* options */
 #define REDIS_OPT_SERIALIZER		1
 #define REDIS_OPT_PREFIX		    2
 #define REDIS_OPT_READ_TIMEOUT		3
+#define REDIS_OPT_SCAN              4
 
 /* serializers */
 #define REDIS_SERIALIZER_NONE		0
 #define REDIS_SERIALIZER_PHP 		1
 #define REDIS_SERIALIZER_IGBINARY 	2
 
+/* SCAN options */
+
+#define REDIS_SCAN_NORETRY 0
+#define REDIS_SCAN_RETRY 1
+
+/* GETBIT/SETBIT offset range limits */
+#define BITOP_MIN_OFFSET 0
+#define BITOP_MAX_OFFSET 4294967295
+
 #define IF_MULTI() if(redis_sock->mode == MULTI)
 #define IF_MULTI_OR_ATOMIC() if(redis_sock->mode == MULTI || redis_sock->mode == ATOMIC)\
 
 #define IF_MULTI_OR_PIPELINE() if(redis_sock->mode == MULTI || redis_sock->mode == PIPELINE)
 #define IF_PIPELINE() if(redis_sock->mode == PIPELINE)
 #define IF_NOT_MULTI() if(redis_sock->mode != MULTI)
+#define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC)
 #define IF_ATOMIC() if(redis_sock->mode == ATOMIC)
 #define ELSE_IF_MULTI() else if(redis_sock->mode == MULTI) { \
 	if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\
@@ -197,6 +223,8 @@ typedef struct {
     char           *err;
     int            err_len;
     zend_bool      lazy_connect;
+
+    int            scan;
 } RedisSock;
 /* }}} */
 
diff --git a/library.c b/library.c
index 4fd523de35..c368d3a3cf 100644
--- a/library.c
+++ b/library.c
@@ -106,6 +106,54 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
     return 0;
 }
 
+
+PHP_REDIS_API int 
+redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                           REDIS_SCAN_TYPE type, long *iter)
+{
+    REDIS_REPLY_TYPE reply_type;
+    int reply_info;
+    char *p_iter;
+
+    /* Our response should have two multibulk replies */
+    if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
+       || reply_type != TYPE_MULTIBULK || reply_info != 2)
+    {
+        return -1;
+    }
+
+    /* The BULK response iterator */
+    if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
+       || reply_type != TYPE_BULK)
+    {
+        return -1;
+    }
+
+    /* Attempt to read the iterator */
+    if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info TSRMLS_CC))) {
+        return -1;
+    }
+
+    /* Push the iterator out to the caller */
+    *iter = atol(p_iter);
+    efree(p_iter);
+
+    /* Read our actual keys/members/etc differently depending on what kind of
+       scan command this is.  They all come back in slightly different ways */
+    switch(type) {
+        case TYPE_SCAN:
+            return redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        case TYPE_SSCAN:
+            return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        case TYPE_ZSCAN:
+            return redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        case TYPE_HSCAN:
+            return redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        default:
+            return -1;
+    }
+}
+
 PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
     char inbuf[1024];
 	int numElems;
@@ -1074,6 +1122,8 @@ PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned sh
     redis_sock->err = NULL;
     redis_sock->err_len = 0;
 
+    redis_sock->scan = REDIS_SCAN_NORETRY;
+
     return redis_sock;
 }
 
@@ -1498,8 +1548,10 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_
 #endif
 	smart_str sstr = {0};
 	zval *z_copy;
+#ifdef HAVE_REDIS_IGBINARY
 	size_t sz;
 	uint8_t *val8;
+#endif
 
 	switch(redis_sock->serializer) {
 		case REDIS_SERIALIZER_NONE:
@@ -1834,7 +1886,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
 		default:
 			/* Protocol error */
 			zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type);
-			break;
+            return FAILURE;
 	}
 
 	IF_MULTI_OR_PIPELINE() {
diff --git a/library.h b/library.h
index b749cf6304..4813b1263b 100644
--- a/library.h
+++ b/library.h
@@ -35,6 +35,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMET
 PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter);
+
 PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
 PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
 PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC);
diff --git a/package.xml b/package.xml
index dd97ad7779..f3b82d4f0c 100644
--- a/package.xml
+++ b/package.xml
@@ -21,10 +21,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
   michael.grunder@gmail.com
   yes
  
- 2013-09-01
+ 2014-03-15
  
-  2.2.4
-  2.2.4
+  2.2.5
+  2.2.5
  
  
   stable
@@ -32,7 +32,25 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
  PHP
  
-	 First public release
+    phpredis 2.2.5
+    
+    This is a minor release with several bug fixes as well as additions to support
+    new commands that have been introduced to Redis since our last release.
+    
+    A special thanks to everyone who helps the project by commenting on issues and
+    submitting pull requests!  :)
+    
+    [NEW] Support for the BITPOS command
+    [NEW] Connection timeout option for RedisArray (@MikeToString)
+    [NEW] A _serialize method, to complement our existing _unserialize method
+    [NEW] Support for the PUBSUB command
+    [NEW] Support for SCAN, SSCAN, HSCAN, and ZSCAN
+    [NEW] Support for the WAIT command
+    
+    [FIX] Handle the COPY and REPLACE arguments for the MIGRATE command
+    
+    [DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh)
+    [DOC] Homebrew documentation instructions (@mathias)
  
  
   
@@ -53,6 +71,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
    
    
    
+  
+     
+     
+     
+     
+     
+    
    
  
  
@@ -70,12 +95,69 @@ http://pear.php.net/dtd/package-2.0.xsd">
  redis
  
  
+   
+   stablestable
+   2.2.52.2.5
+   2014-03-15
+   
+    phpredis 2.2.5
+
+    This is a minor release with several bug fixes as well as additions to support
+    new commands that have been introduced to Redis since our last release.
+
+    A special thanks to everyone who helps the project by commenting on issues and
+    submitting pull requests!  :)
+
+    [NEW] Support for the BITPOS command
+    [NEW] Connection timeout option for RedisArray (@MikeToString)
+    [NEW] A _serialize method, to complement our existing _unserialize method
+    [NEW] Support for the PUBSUB command
+    [NEW] Support for SCAN, SSCAN, HSCAN, and ZSCAN
+    [NEW] Support for the WAIT command
+
+    [FIX] Handle the COPY and REPLACE arguments for the MIGRATE command
+
+    [DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh)
+    [DOC] Homebrew documentation instructions (@mathias)
+
+   
+   
    
    stablestable
    2.2.42.2.4
    2013-09-01
    
-	   See GitHub for release notes
+   **
+   ** Features / Improvements
+   **
+
+   * Randomized reconnect delay for RedisArray @mobli
+     This feature adds an optional parameter when constructing a RedisArray object
+     such that a random delay will be introduced if reconnections are made,
+     mitigating any 'thundering herd' type problems.
+
+   * Lazy connections to RedisArray servers @mobli
+     By default, RedisArray will attempt to connect to each server you pass in
+     the ring on construction.  This feature lets you specify that you would
+     rather have RedisArray only attempt a connection when it needs to get data
+     from a particular node (throughput/performance improvement).
+
+   * Allow LONG and STRING keys in MGET/MSET
+   * Extended SET options for Redis >= 2.6.12
+   * Persistent connections and UNIX SOCKET support for RedisArray
+   * Allow aggregates for ZUNION/ZINTER without weights @mheijkoop
+   * Support for SLOWLOG command
+   * Reworked MGET algorithm to run in linear time regardless of key count.
+   * Reworked ZINTERSTORE/ZUNIONSTORE algorithm to run in linear time
+
+   **
+   ** Bug fixes
+   **
+
+   * C99 Compliance (or rather lack thereof) fix @mobli
+   * Added ZEND_ACC_CTOR and ZEND_ACC_DTOR @euskadi31
+   * Stop throwing and clearing an exception on connect failure @matmoi
+   * Fix a false positive unit test failure having to do with TTL returns
    
   
    
diff --git a/php_redis.h b/php_redis.h
index 905c7b3b88..ccc2e724ef 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -14,6 +14,7 @@
   +----------------------------------------------------------------------+
   | Original author: Alfonso Jimenez              |
   | Maintainer: Nicolas Favre-Felix            |
+  | Maintainer: Michael Grunder               |
   | Maintainer: Nasreddine Bouafif                 |
   +----------------------------------------------------------------------+
 */
@@ -128,6 +129,7 @@ PHP_METHOD(Redis, slaveof);
 PHP_METHOD(Redis, object);
 PHP_METHOD(Redis, bitop);
 PHP_METHOD(Redis, bitcount);
+PHP_METHOD(Redis, bitpos);
 
 PHP_METHOD(Redis, eval);
 PHP_METHOD(Redis, evalsha);
@@ -141,6 +143,7 @@ PHP_METHOD(Redis, time);
 PHP_METHOD(Redis, getLastError);
 PHP_METHOD(Redis, clearLastError);
 PHP_METHOD(Redis, _prefix);
+PHP_METHOD(Redis, _serialize);
 PHP_METHOD(Redis, _unserialize);
 
 PHP_METHOD(Redis, mset);
@@ -181,9 +184,18 @@ PHP_METHOD(Redis, setOption);
 
 PHP_METHOD(Redis, config);
 PHP_METHOD(Redis, slowlog);
+PHP_METHOD(Redis, wait);
+PHP_METHOD(Redis, pubsub);
 
 PHP_METHOD(Redis, client);
 
+/* SCAN and friends  */
+PHP_METHOD(Redis, scan);
+PHP_METHOD(Redis, hscan);
+PHP_METHOD(Redis, sscan);
+PHP_METHOD(Redis, zscan);
+
+/* Reflection */
 PHP_METHOD(Redis, getHost);
 PHP_METHOD(Redis, getPort);
 PHP_METHOD(Redis, getDBNum);
@@ -257,7 +269,7 @@ extern zend_module_entry redis_module_entry;
 
 #define phpext_redis_ptr redis_module_ptr
 
-#define PHP_REDIS_VERSION "2.2.4"
+#define PHP_REDIS_VERSION "2.2.5"
 
 #endif
 
diff --git a/redis.c b/redis.c
index f869d0015f..b565069d4c 100644
--- a/redis.c
+++ b/redis.c
@@ -69,6 +69,25 @@ PHP_INI_BEGIN()
 	PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL)
 PHP_INI_END()
 
+/**
+ * Argument info for the SCAN proper
+ */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 1)
+    ZEND_ARG_INFO(1, i_iterator)
+    ZEND_ARG_INFO(0, str_pattern)
+    ZEND_ARG_INFO(0, i_count)
+ZEND_END_ARG_INFO();
+
+/**
+ * Argument info for key scanning
+ */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2)
+    ZEND_ARG_INFO(0, str_key)
+    ZEND_ARG_INFO(1, i_iterator)
+    ZEND_ARG_INFO(0, str_pattern)
+    ZEND_ARG_INFO(0, i_count)
+ZEND_END_ARG_INFO();
+
 #ifdef ZTS
 ZEND_DECLARE_MODULE_GLOBALS(redis)
 #endif
@@ -160,6 +179,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, object, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, bitop, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, bitcount, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, bitpos, NULL, ZEND_ACC_PUBLIC)
 
      /* 1.1 */
      PHP_ME(Redis, mset, NULL, ZEND_ACC_PUBLIC)
@@ -228,10 +248,17 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, clearLastError, NULL, ZEND_ACC_PUBLIC)
 
      PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, _serialize, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC)
 
      PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)
 
+     /* SCAN and friends */
+     PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC)
+
      /* options */
      PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, setOption, NULL, ZEND_ACC_PUBLIC)
@@ -252,6 +279,9 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, getAuth, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC)
 
+     PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, pubsub, NULL, ZEND_ACC_PUBLIC)
+
      /* aliases */
      PHP_MALIAS(Redis, open, connect, NULL, ZEND_ACC_PUBLIC)
      PHP_MALIAS(Redis, popen, pconnect, NULL, ZEND_ACC_PUBLIC)
@@ -488,6 +518,11 @@ PHP_MINIT_FUNCTION(redis)
     /* serializer */
     add_constant_long(redis_ce, "SERIALIZER_NONE", REDIS_SERIALIZER_NONE);
     add_constant_long(redis_ce, "SERIALIZER_PHP", REDIS_SERIALIZER_PHP);
+
+    /* scan options*/
+    add_constant_long(redis_ce, "OPT_SCAN", REDIS_OPT_SCAN);
+    add_constant_long(redis_ce, "SCAN_RETRY", REDIS_SCAN_RETRY);
+    add_constant_long(redis_ce, "SCAN_NORETRY", REDIS_SCAN_NORETRY);
 #ifdef HAVE_REDIS_IGBINARY
     add_constant_long(redis_ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY);
 #endif
@@ -790,6 +825,59 @@ PHP_METHOD(Redis, bitcount)
 }
 /* }}} */
 
+/* {{{ proto integer Redis::bitpos(string key, int bit, [int start], [int end]) */
+PHP_METHOD(Redis, bitpos) 
+{
+    zval *object;
+    RedisSock *redis_sock;
+    char *key, *cmd;
+    int key_len, cmd_len, argc, key_free=0;
+    long bit, start, end;
+
+    argc = ZEND_NUM_ARGS();
+
+    if(zend_parse_method_parameters(argc TSRMLS_CC, getThis(), "Osl|ll",
+                                    &object, redis_ce, &key, &key_len, &bit,
+                                    &start, &end)==FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // We can prevalidate the first argument
+    if(bit != 0 && bit != 1) {
+        RETURN_FALSE;
+    }
+
+    // Prefix our key
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+    // Various command semantics
+    if(argc == 2) {
+        cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd", key, key_len,
+                                          bit);
+    } else if(argc == 3) {
+        cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sdd", key, key_len,
+                                          bit, start);
+    } else {
+        cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sddd", key, key_len,
+                                          bit, start, end);
+    }
+
+    // Free our key if it was prefixed
+    if(key_free) efree(key);
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_long_response);
+}
+/* }}} */
+
 /* {{{ proto boolean Redis::close()
  */
 PHP_METHOD(Redis, close)
@@ -839,7 +927,9 @@ PHP_METHOD(Redis, set) {
 
     /* Our optional argument can either be a long (to support legacy SETEX */
     /* redirection), or an array with Redis >= 2.6.12 set options */
-    if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY) {
+    if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY
+       && Z_TYPE_P(z_opts) != IS_NULL) 
+    {
         RETURN_FALSE;
     }
 
@@ -903,7 +993,7 @@ PHP_METHOD(Redis, set) {
 
     /* Free our key or value if we prefixed/serialized */
     if(key_free) efree(key);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
 
     /* Kick off the command */
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -936,7 +1026,7 @@ PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keywo
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -985,7 +1075,7 @@ PHP_METHOD(Redis, setnx)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1023,7 +1113,7 @@ PHP_METHOD(Redis, getSet)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1331,12 +1421,10 @@ PHP_METHOD(Redis, incrByFloat) {
 		RETURN_FALSE;
 	}
 
-	// Prefix our key, free it if we have
+	// Prefix key, format command, free old key if necissary
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
-	if(key_free) efree(key);
-
-	// Format our INCRBYFLOAT command
 	cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val);
+	if(key_free) efree(key);	
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 	IF_ATOMIC() {
@@ -1742,6 +1830,11 @@ PHP_METHOD(Redis, getBit)
 		RETURN_FALSE;
 	}
 
+	// GETBIT and SETBIT only work for 0 - 2^32-1
+	if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) {
+	    RETURN_FALSE;
+	}
+
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 	cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd", key, key_len, (int)offset);
 	if(key_free) efree(key);
@@ -1771,6 +1864,11 @@ PHP_METHOD(Redis, setBit)
 		RETURN_FALSE;
 	}
 
+	// GETBIT and SETBIT only work for 0 - 2^32-1
+	if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) {
+	    RETURN_FALSE;
+	}
+
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 	cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd", key, key_len, (int)offset, (int)val);
 	if(key_free) efree(key);
@@ -1833,7 +1931,7 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1909,9 +2007,9 @@ PHP_METHOD(Redis, lInsert)
         val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
         pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC);
         cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len);
-        if(val_free) efree(val);
+        if(val_free) STR_FREE(val);
 		if(key_free) efree(key);
-        if(pivot_free) efree(pivot);
+        if(pivot_free) STR_FREE(pivot);
 
 		REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); 
 		IF_ATOMIC() { 
@@ -2083,7 +2181,7 @@ PHP_METHOD(Redis, lRemove)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -2287,7 +2385,7 @@ PHP_METHOD(Redis, sMove)
 	src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC);
 	dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(src_free) efree(src);
     if(dst_free) efree(dst);
 
@@ -2349,14 +2447,23 @@ PHP_METHOD(Redis, sRandMember)
     // Process our command
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 
-    // Process our reply
+    // Either bulk or multi-bulk depending on argument count
+    if(ZEND_NUM_ARGS() == 2) {
     IF_ATOMIC() {
-        // This will be bulk or multi-bulk depending if we passed the optional [COUNT] argument
-        if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
+            if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+                                               redis_sock, NULL, NULL) < 0)
+            {
             RETURN_FALSE;
         }
     }
-    REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+        REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
+    } else {
+        IF_ATOMIC() {
+            redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+                                  NULL, NULL);
+        }
+        REDIS_PROCESS_RESPONSE(redis_string_response);
+    }
 }
 /* }}} */
 
@@ -2384,7 +2491,7 @@ PHP_METHOD(Redis, sContains)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -2612,7 +2719,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
 	/* cleanup prefixed keys. */
 	for(i = 0; i < real_argc + (has_timeout?-1:0); ++i) {
 		if(keys_to_free[i])
-			efree(keys[i]);
+			STR_FREE(keys[i]);
 	}
 	if(single_array && has_timeout) { /* cleanup string created to contain timeout value */
 		efree(keys[real_argc-1]);
@@ -3258,7 +3365,7 @@ PHP_METHOD(Redis, lSet) {
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -3678,7 +3785,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI
 				memcpy(p, _NL, 2); p += 2;
 			}
 
-			if(val_free) efree(val);
+			if(val_free) STR_FREE(val);
 			if(key_free) efree(key);
 		}
 	}
@@ -3861,7 +3968,7 @@ PHP_METHOD(Redis, zAdd) {
 		smart_str_appendl(&buf, val, val_len);
 		smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
 
-		if(val_free) efree(val);
+		if(val_free) STR_FREE(val);
 	}
 
 	/* end string */
@@ -4258,7 +4365,7 @@ PHP_METHOD(Redis, zScore)
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4291,7 +4398,7 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4341,7 +4448,7 @@ PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *key
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4556,7 +4663,7 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI
     val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len);
-    if(val_free) efree(val);
+    if(val_free) STR_FREE(val);
     if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -5076,7 +5183,7 @@ PHP_METHOD(Redis, hMset)
         redis_cmd_append_sstr(&set_cmds, hkey, hkey_len - 1);
         redis_cmd_append_sstr(&set_cmds, hval, hval_len);
 
-        if(hval_free) efree(hval);
+        if(hval_free) STR_FREE(hval);
     }
 
     // Now construct the entire command
@@ -5804,22 +5911,19 @@ PHP_METHOD(Redis, getOption)  {
     }
 
     switch(option) {
-
             case REDIS_OPT_SERIALIZER:
                     RETURN_LONG(redis_sock->serializer);
-
             case REDIS_OPT_PREFIX:
 					if(redis_sock->prefix) {
 						RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len, 1);
 					}
                     RETURN_NULL();
-
             case REDIS_OPT_READ_TIMEOUT:
                     RETURN_DOUBLE(redis_sock->read_timeout);
-
+        case REDIS_OPT_SCAN:
+            RETURN_LONG(redis_sock->scan);
             default:
                     RETURN_FALSE;
-
     }
 }
 /* }}} */
@@ -5857,7 +5961,6 @@ PHP_METHOD(Redis, setOption) {
                             RETURN_FALSE;
                     }
                     break;
-
 			case REDIS_OPT_PREFIX:
 					if(redis_sock->prefix) {
 						efree(redis_sock->prefix);
@@ -5871,17 +5974,22 @@ PHP_METHOD(Redis, setOption) {
 						memcpy(redis_sock->prefix, val_str, val_len);
 					}
 					RETURN_TRUE;
-
             case REDIS_OPT_READ_TIMEOUT:
                     redis_sock->read_timeout = atof(val_str);
                     if(redis_sock->stream) {
                         read_tv.tv_sec  = (time_t)redis_sock->read_timeout;
                         read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000);
-                        php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT,
-                                              0, &read_tv);
+                    php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT,0, &read_tv);
                     }
                     RETURN_TRUE;
-
+            case REDIS_OPT_SCAN:
+                val_long = atol(val_str);
+                if(val_long == REDIS_SCAN_NORETRY || val_long == REDIS_SCAN_RETRY) {
+                    redis_sock->scan = val_long;
+                    RETURN_TRUE;
+                }
+                RETURN_FALSE;
+                break;
             default:
                     RETURN_FALSE;
     }
@@ -5992,6 +6100,207 @@ PHP_METHOD(Redis, slowlog) {
     REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
 }
 
+/* {{{ proto Redis::wait(int num_slaves, int ms) }}}
+ */
+PHP_METHOD(Redis, wait) {
+    zval *object;
+    RedisSock *redis_sock;
+    long num_slaves, timeout;
+    char *cmd;
+    int cmd_len;
+
+    // Make sure arguments are valid
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", 
+                                    &object, redis_ce, &num_slaves, &timeout)
+                                    ==FAILURE) 
+    {
+        RETURN_FALSE;
+    }
+
+    // Don't even send this to Redis if our args are negative
+    if(num_slaves < 0 || timeout < 0) {
+        RETURN_FALSE;
+    }
+
+    // Grab our socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) {
+        RETURN_FALSE;
+    }
+
+    // Construct the command
+    cmd_len = redis_cmd_format_static(&cmd, "WAIT", "ll", num_slaves, timeout);
+
+    // Kick it off
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_long_response);
+}
+
+/*
+ * Construct a PUBSUB command
+ */
+PHP_REDIS_API int
+redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
+                       zval *arg TSRMLS_DC)
+{
+    HashTable *ht_chan;
+    HashPosition ptr;    
+    zval **z_ele;
+    char *key;
+    int cmd_len, key_len, key_free;
+    smart_str cmd = {0};
+
+    if(type == PUBSUB_CHANNELS) {
+        if(arg) {
+            // Get string argument and length.
+            key = Z_STRVAL_P(arg);
+            key_len = Z_STRLEN_P(arg);
+
+            // Prefix if necissary
+            key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+            // With a pattern
+            cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", "CHANNELS", sizeof("CHANNELS")-1,
+                                              key, key_len);
+
+            // Free the channel name if we prefixed it
+            if(key_free) efree(key);
+
+            // Return command length
+            return cmd_len;
+        } else {
+            // No pattern
+            return redis_cmd_format_static(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS")-1);
+        }
+    } else if(type == PUBSUB_NUMSUB) {
+        ht_chan = Z_ARRVAL_P(arg);
+        
+        // Add PUBSUB and NUMSUB bits
+        redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
+        redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
+
+        // Iterate our elements
+        for(zend_hash_internal_pointer_reset_ex(ht_chan, &ptr);
+            zend_hash_get_current_data_ex(ht_chan, (void**)&z_ele, &ptr)==SUCCESS;
+            zend_hash_move_forward_ex(ht_chan, &ptr))
+        {
+            char *key;
+            int key_len, key_free;
+            zval *z_tmp = NULL;
+
+            if(Z_TYPE_PP(z_ele) == IS_STRING) {
+                key = Z_STRVAL_PP(z_ele);
+                key_len = Z_STRLEN_PP(z_ele);
+            } else {
+                MAKE_STD_ZVAL(z_tmp);
+                *z_tmp = **z_ele;
+                zval_copy_ctor(z_tmp);
+                convert_to_string(z_tmp);
+
+                key = Z_STRVAL_P(z_tmp);
+                key_len = Z_STRLEN_P(z_tmp);
+            }
+
+            // Apply prefix if required
+            key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+            // Append this channel
+            redis_cmd_append_sstr(&cmd, key, key_len);
+
+            // Free key if prefixed
+            if(key_free) efree(key);
+
+            // Free our temp var if we converted from something other than a string
+            if(z_tmp) {
+                zval_dtor(z_tmp);
+                efree(z_tmp);
+                z_tmp = NULL;
+            }
+        }
+
+        // Set return
+        *ret = cmd.c;
+        return cmd.len;
+    } else if(type == PUBSUB_NUMPAT) {
+        return redis_cmd_format_static(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT")-1);
+    }
+
+    // Shouldn't ever happen
+    return -1;
+}
+
+/*
+ * {{{ proto Redis::pubsub("channels", pattern);
+ *     proto Redis::pubsub("numsub", Array channels);
+ *     proto Redis::pubsub("numpat"); }}}
+ */
+PHP_METHOD(Redis, pubsub) {
+    zval *object;
+    RedisSock *redis_sock;
+    char *keyword, *cmd;
+    int kw_len, cmd_len;
+    PUBSUB_TYPE type;
+    zval *arg=NULL;
+
+    // Parse arguments
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z", 
+                                    &object, redis_ce, &keyword, &kw_len, &arg)
+                                    ==FAILURE) 
+    {
+        RETURN_FALSE;
+    }
+
+    // Validate our sub command keyword, and that we've got proper arguments
+    if(!strncasecmp(keyword, "channels", sizeof("channels"))) {
+        // One (optional) string argument
+        if(arg && Z_TYPE_P(arg) != IS_STRING) {
+            RETURN_FALSE;
+        }
+        type = PUBSUB_CHANNELS;
+    } else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
+        // One array argument
+        if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
+           zend_hash_num_elements(Z_ARRVAL_P(arg))==0) 
+        {
+            RETURN_FALSE;
+        }
+        type = PUBSUB_NUMSUB;
+    } else if(!strncasecmp(keyword, "numpat", sizeof("numpat"))) {
+        type = PUBSUB_NUMPAT;
+    } else {
+        // Invalid keyword
+        RETURN_FALSE;
+    }
+
+    // Grab our socket context object
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) {
+        RETURN_FALSE;
+    }
+
+    // Construct our "PUBSUB" command
+    cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg TSRMLS_CC);
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+
+    if(type == PUBSUB_NUMSUB) {
+        IF_ATOMIC() {
+            if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) {
+                RETURN_FALSE;
+            }
+        }
+        REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
+    } else {
+        IF_ATOMIC() {
+            if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL)<0) {
+                RETURN_FALSE;
+            }
+        }
+        REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+    }
+}
+
 // Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
 PHP_REDIS_API int
 redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) {
@@ -6291,18 +6600,20 @@ PHP_METHOD(Redis, restore) {
 }
 
 /*
- * {{{ proto Redis::migrate(host port key dest-db timeout)
+ * {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, bool replace])
  */
 PHP_METHOD(Redis, migrate) {
 	zval *object;
 	RedisSock *redis_sock;
 	char *cmd, *host, *key;
 	int cmd_len, host_len, key_len, key_free;
+    zend_bool copy=0, replace=0;
 	long port, dest_db, timeout;
 
 	// Parse arguments
-	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll", &object, redis_ce,
-									&host, &host_len, &port, &key, &key_len, &dest_db, &timeout) == FAILURE) {
+	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll|bb", &object, redis_ce,
+									&host, &host_len, &port, &key, &key_len, &dest_db, &timeout, 
+                                    ©, &replace) == FAILURE) {
 		RETURN_FALSE;
 	}
 
@@ -6313,7 +6624,26 @@ PHP_METHOD(Redis, migrate) {
 
 	// Prefix our key if we need to, build our command
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
-	cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, key, key_len, dest_db, timeout);
+
+    // Construct our command
+    if(copy && replace) {
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port, 
+                                          key, key_len, dest_db, timeout, "COPY", 
+                                          sizeof("COPY")-1, "REPLACE", sizeof("REPLACE")-1);
+    } else if(copy) {
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
+                                          key, key_len, dest_db, timeout, "COPY", 
+                                          sizeof("COPY")-1);
+    } else if(replace) {
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
+                                          key, key_len, dest_db, timeout, "REPLACE",
+                                          sizeof("REPLACE")-1);
+    } else {
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, 
+                                          key, key_len, dest_db, timeout);
+    }
+
+    // Free our key if we prefixed it
 	if(key_free) efree(key);
 
 	// Kick off our MIGRATE request
@@ -6352,6 +6682,36 @@ PHP_METHOD(Redis, _prefix) {
 	}
 }
 
+/*
+ * {{{ proto Redis::_serialize(value)
+ */
+PHP_METHOD(Redis, _serialize) {
+    zval *object;
+    RedisSock *redis_sock;
+    zval *z_val;
+    char *val;
+    int val_len;
+
+    // Parse arguments
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
+                                    &object, redis_ce, &z_val) == FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    // Grab socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // Serialize, which will return a value even if no serializer is set
+    redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
+
+    // Return serialized value.  Tell PHP to make a copy as some can be interned.
+    RETVAL_STRINGL(val, val_len, 1);
+    STR_FREE(val);
+}
+
 /*
  * {{{ proto Redis::_unserialize(value)
  */
@@ -6634,4 +6994,169 @@ PHP_METHOD(Redis, client) {
     }
 }
 
+/**
+ * Helper to format any combination of SCAN arguments
+ */
+PHP_REDIS_API int
+redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
+                     int iter, char *pattern, int pattern_len, int count)
+{
+    char *keyword;
+    int arg_count, cmd_len;
+
+    // Count our arguments +1 for key if it's got one, and + 2 for pattern
+    // or count given that they each carry keywords with them.
+    arg_count = 1 + (key_len>0) + (pattern_len>0?2:0) + (count>0?2:0);
+
+    // Turn our type into a keyword
+    switch(type) {
+        case TYPE_SCAN:
+            keyword = "SCAN";
+            break;
+        case TYPE_SSCAN:
+            keyword = "SSCAN";
+            break;
+        case TYPE_HSCAN:
+            keyword = "HSCAN";
+            break;
+        case TYPE_ZSCAN:
+		default:
+            keyword = "ZSCAN";
+            break;
+    }
+
+    // Start the command
+    cmd_len = redis_cmd_format_header(cmd, keyword, arg_count);
+
+    // Add the key in question if we have one
+    if(key_len) {
+        cmd_len = redis_cmd_append_str(cmd, cmd_len, key, key_len);
+    }
+
+    // Add our iterator
+    cmd_len = redis_cmd_append_int(cmd, cmd_len, iter);
+
+    // Append COUNT if we've got it
+    if(count) {
+        cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", sizeof("COUNT")-1);
+        cmd_len = redis_cmd_append_int(cmd, cmd_len, count);
+    }
+
+    // Append MATCH if we've got it
+    if(pattern_len) {
+        cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", sizeof("MATCH")-1);
+        cmd_len = redis_cmd_append_str(cmd, cmd_len, pattern, pattern_len);
+    }
+
+    // Return our command length
+    return cmd_len;
+}
+
+/**
+ * {{{ proto redis::scan(&$iterator, [pattern, [count]])
+ */
+PHP_REDIS_API void
+generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
+    zval *object, *z_iter;
+    RedisSock *redis_sock;
+    HashTable *hash;
+    char *pattern=NULL, *cmd, *key=NULL;
+    int cmd_len, key_len=0, pattern_len=0, num_elements, key_free=0;
+    long count=0, iter;
+
+    // Different prototype depending on if this is a key based scan
+    if(type != TYPE_SCAN) {
+        // Requires a key
+        if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz/|s!l",
+                                        &object, redis_ce, &key, &key_len, &z_iter,
+                                        &pattern, &pattern_len, &count)==FAILURE)
+        {
+            RETURN_FALSE;
+        }
+    } else {
+        // Doesn't require a key
+        if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz/|s!l",
+                                        &object, redis_ce, &z_iter, &pattern, &pattern_len,
+                                        &count) == FAILURE)
+        {
+            RETURN_FALSE;
+        }
+    }
+
+    // Grab our socket
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    // Calling this in a pipeline makes no sense
+    IF_NOT_ATOMIC() {
+        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't call SCAN commands in multi or pipeline mode!");
+        RETURN_FALSE;
+    }
+
+    // The iterator should be passed in as NULL for the first iteration, but we can treat
+    // any NON LONG value as NULL for these purposes as we've seperated the variable anyway.
+    if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter)<0) {
+        // Convert to long
+        convert_to_long(z_iter);
+        iter = 0;
+    } else if(Z_LVAL_P(z_iter)!=0) {
+        // Update our iterator value for the next passthru
+        iter = Z_LVAL_P(z_iter);
+    } else {
+        // We're done, back to iterator zero
+        RETURN_FALSE;
+    }
+
+    // Prefix our key if we've got one and we have a prefix set
+    if(key_len) {
+        key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+    }
+
+    /**
+     * Redis can return to us empty keys, especially in the case where there are a large
+     * number of keys to scan, and we're matching against a pattern.  PHPRedis can be set
+     * up to abstract this from the user, by setting OPT_SCAN to REDIS_SCAN_RETRY.  Otherwise
+     * we will return empty keys and the user will need to make subsequent calls with
+     * an updated iterator.
+     */
+    do {
+        // Format our SCAN command
+        cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter,
+                                   pattern, pattern_len, count);
+
+        // Execute our command getting our new iterator value
+        REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+        if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+                                      redis_sock,type,&iter)<0)
+        {
+            if(key_free) efree(key);
+            RETURN_FALSE;
+        }
+
+        // Get the number of elements
+        hash = Z_ARRVAL_P(return_value);
+        num_elements = zend_hash_num_elements(hash);
+    } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && num_elements == 0);
+
+    // Free our key if it was prefixed
+    if(key_free) efree(key);
+
+    // Update our iterator reference
+    Z_LVAL_P(z_iter) = iter;
+}
+
+PHP_METHOD(Redis, scan) {
+    generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SCAN);
+}
+PHP_METHOD(Redis, hscan) {
+    generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN);
+}
+PHP_METHOD(Redis, sscan) {
+    generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN);
+}
+PHP_METHOD(Redis, zscan) {
+    generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
+}
+
 /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */
diff --git a/redis_array.c b/redis_array.c
index 60d1022be9..e9a56e7ac8 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -32,6 +32,12 @@
 #include "redis_array.h"
 #include "redis_array_impl.h"
 
+/* Simple macro to detect failure in a RedisArray call */
+#define RA_CALL_FAILED(rv, cmd) \
+    ((Z_TYPE_P(rv) == IS_BOOL && Z_BVAL_P(rv) == 0) || \
+    (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \
+    (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE"))) \
+
 extern zend_class_entry *redis_ce;
 zend_class_entry *redis_array_ce;
 
@@ -76,43 +82,50 @@ zend_function_entry redis_array_functions[] = {
      {NULL, NULL, NULL}
 };
 
-int le_redis_array;
-void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
-{
+static void redis_array_free(RedisArray *ra) {
 	int i;
-	RedisArray *ra = (RedisArray*)rsrc->ptr;
 
-	/* delete Redis objects */
-	for(i = 0; i < ra->count; ++i) {
+    // Redis objects
+    for(i=0;icount;i++) {
 		zval_dtor(ra->redis[i]);
 		efree(ra->redis[i]);
-
-		/* remove host too */
 		efree(ra->hosts[i]);
 	}
 	efree(ra->redis);
 	efree(ra->hosts);
 
-	/* delete function */
+    /* delete hash function */
 	if(ra->z_fun) {
 		zval_dtor(ra->z_fun);
 		efree(ra->z_fun);
 	}
 
-	/* delete distributor */
+    /* Distributor */
 	if(ra->z_dist) {
 		zval_dtor(ra->z_dist);
 		efree(ra->z_dist);
 	}
 
-	/* delete list of pure commands */
+    /* Delete pur commands */
 	zval_dtor(ra->z_pure_cmds);
 	efree(ra->z_pure_cmds);
 
-	/* free container */
+    // Free structure itself
 	efree(ra);
 }
 
+int le_redis_array;
+void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
+{
+    RedisArray *ra = (RedisArray*)rsrc->ptr;
+
+    /* Free previous ring if it's set */
+    if(ra->prev) redis_array_free(ra->prev);
+
+    /* Free parent array */
+    redis_array_free(ra);
+}
+
 /**
  * redis_array_get
  */
@@ -199,6 +212,8 @@ PHP_METHOD(RedisArray, __construct)
   long l_retry_interval = 0;
   	zend_bool b_lazy_connect = 0;
   	zval **z_retry_interval_pp;
+  	double d_connect_timeout = 0;
+  	zval **z_connect_timeout_pp;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) {
 		RETURN_FALSE;
@@ -261,6 +276,18 @@ PHP_METHOD(RedisArray, __construct)
 		if(FAILURE != zend_hash_find(hOpts, "lazy_connect", sizeof("lazy_connect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) {
 			b_lazy_connect = Z_BVAL_PP(zpData);
 		}
+		
+		/* extract connect_timeout option */		
+		if (FAILURE != zend_hash_find(hOpts, "connect_timeout", sizeof("connect_timeout"), (void**)&z_connect_timeout_pp)) {
+			if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE || Z_TYPE_PP(z_connect_timeout_pp) == IS_STRING) {
+				if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE) {
+					d_connect_timeout = Z_DVAL_PP(z_connect_timeout_pp);
+				}
+				else {
+					d_connect_timeout = atof(Z_STRVAL_PP(z_connect_timeout_pp));
+				}
+			}
+		}		
 	}
 
 	/* extract either name of list of hosts from z0 */
@@ -270,7 +297,7 @@ PHP_METHOD(RedisArray, __construct)
 			break;
 
 		case IS_ARRAY:
-			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
+			ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC);
 			break;
 
 		default:
@@ -280,6 +307,8 @@ PHP_METHOD(RedisArray, __construct)
 
 	if(ra) {
 		ra->auto_rehash = b_autorehash;
+		ra->connect_timeout = d_connect_timeout;
+		if(ra->prev) ra->prev->auto_rehash = b_autorehash;
 #if PHP_VERSION_ID >= 50400
 		id = zend_list_insert(ra, le_redis_array TSRMLS_CC);
 #else
@@ -366,22 +395,14 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i
 	} else { /* call directly through. */
 		call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC);
 
-		failed = 0;
-		if((Z_TYPE_P(return_value) == IS_BOOL && Z_BVAL_P(return_value) == 0) ||
-		   (Z_TYPE_P(return_value) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(return_value)) == 0) ||
-		   (Z_TYPE_P(return_value) == IS_LONG && Z_LVAL_P(return_value) == 0 && !strcasecmp(cmd, "TYPE")))
-
-		{
-			failed = 1;
-		}
-
 		/* check if we have an error. */
-		if(failed && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */
+		if(RA_CALL_FAILED(return_value,cmd) && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */
 			/* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */
 			ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra->prev, cmd, cmd_len, z_args, z_new_target?z_new_target:redis_inst);
 		}
 
-		if(!failed && !b_write_cmd && z_new_target && ra->auto_rehash) { /* move key from old ring to new ring */
+		/* Autorehash if the key was found on the previous node if this is a read command and auto rehashing is on */
+		if(!RA_CALL_FAILED(return_value,cmd) && !b_write_cmd && z_new_target && ra->auto_rehash) { /* move key from old ring to new ring */
 				ra_move_key(key, key_len, redis_inst, z_new_target TSRMLS_CC);
 		}
 	}
@@ -876,6 +897,9 @@ PHP_METHOD(RedisArray, mget)
 
 	/* calls */
 	for(n = 0; n < ra->count; ++n) { /* for each node */
+	    /* We don't even need to make a call to this node if no keys go there */
+	    if(!argc_each[n]) continue;
+
 		/* copy args for MGET call on node. */
 		MAKE_STD_ZVAL(z_argarray);
 		array_init(z_argarray);
diff --git a/redis_array.h b/redis_array.h
index 3b1163bf88..2a8baf44ee 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -50,6 +50,7 @@ typedef struct RedisArray_ {
 	zval *z_fun;			/* key extractor, callable */
 	zval *z_dist;			/* key distributor, callable */
 	zval *z_pure_cmds;		/* hash table */
+	double connect_timeout; /* socket connect timeout */
 
 	struct RedisArray_ *prev;
 } RedisArray;
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 10c7dc81ec..ef499e1a0a 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -44,7 +44,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b
 
 	/* init connections */
 	for(i = 0; i < count; ++i) {
-		if(FAILURE == zend_hash_quick_find(hosts, NULL, 0, i, (void**)&zpData)) {
+		if(FAILURE == zend_hash_quick_find(hosts, NULL, 0, i, (void**)&zpData) ||
+           Z_TYPE_PP(zpData) != IS_STRING) 
+        {
 			efree(ra);
 			return NULL;
 		}
@@ -70,7 +72,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b
 		call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC);
 
 		/* create socket */
-		redis_sock = redis_sock_create(host, host_len, port, 0, ra->pconnect, NULL, retry_interval, b_lazy_connect);
+		redis_sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect);
 
 	    if (!b_lazy_connect)
     	{
@@ -166,12 +168,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	zval *z_params_autorehash;
 	zval *z_params_retry_interval;
 	zval *z_params_pconnect;
+	zval *z_params_connect_timeout;
 	zval *z_params_lazy_connect;
 	RedisArray *ra = NULL;
 
 	zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0;
 	long l_retry_interval = 0;
 	zend_bool b_lazy_connect = 0;
+	double d_connect_timeout = 0;
 	HashTable *hHosts = NULL, *hPrev = NULL;
 
 	/* find entry */
@@ -258,7 +262,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
             b_pconnect = 1;
         }
     }
-	/* find retry interval option */
+	
+    /* find lazy connect option */
 	MAKE_STD_ZVAL(z_params_lazy_connect);
 	array_init(z_params_lazy_connect);
 	sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.lazyconnect")), z_params_lazy_connect TSRMLS_CC);
@@ -268,9 +273,25 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 		}
 	}
 
+    /* find connect timeout option */
+	MAKE_STD_ZVAL(z_params_connect_timeout);
+	array_init(z_params_connect_timeout);
+	sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.connecttimeout")), z_params_connect_timeout TSRMLS_CC);
+	if (zend_hash_find(Z_ARRVAL_P(z_params_connect_timeout), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) {
+		if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE || Z_TYPE_PP(z_data_pp) == IS_STRING) {
+			if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE) {
+				d_connect_timeout = Z_DVAL_PP(z_data_pp);
+			}
+			else {
+				d_connect_timeout = atof(Z_STRVAL_PP(z_data_pp));
+			}
+		}
+	}
+	
 	/* create RedisArray object */
-	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
+	ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC);
 	ra->auto_rehash = b_autorehash;
+	if(ra->prev) ra->prev->auto_rehash = b_autorehash;
 
 	/* cleanup */
 	zval_dtor(z_params_hosts);
@@ -287,6 +308,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 	efree(z_params_retry_interval);
 	zval_dtor(z_params_pconnect);
 	efree(z_params_pconnect);
+	zval_dtor(z_params_connect_timeout);
+	efree(z_params_connect_timeout);
 	zval_dtor(z_params_lazy_connect);
 	efree(z_params_lazy_connect);
 
@@ -294,7 +317,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
 }
 
 RedisArray *
-ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) {
+ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC) {
 
 	int count = zend_hash_num_elements(hosts);
 
@@ -308,6 +331,8 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
 	ra->z_multi_exec = NULL;
 	ra->index = b_index;
 	ra->auto_rehash = 0;
+	ra->pconnect = b_pconnect;
+	ra->connect_timeout = connect_timeout;
 
 	/* init array data structures */
 	ra_init_function_table(ra);
@@ -315,7 +340,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
 	if(NULL == ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC)) {
 		return NULL;
 	}
-	ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect TSRMLS_CC) : NULL;
+	ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL;
 
 	/* copy function if provided */
 	if(z_fun) {
@@ -603,6 +628,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) {
 
 	/* don't dtor z_ret, since we're returning z_redis */
 	efree(z_args[0]);
+    zval_dtor(z_args[1]);
 	efree(z_args[1]);
 }
 
@@ -965,6 +991,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl
 		ZVAL_STRINGL(z_args[0], key, key_len, 0);
 		ZVAL_LONG(z_args[1], ttl);
 		ZVAL_STRINGL(z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */
+		zval_dtor(&z_ret); /* free memory from our previous call */
 		call_user_function(&redis_ce->function_table, &z_to, &z_fun_set, &z_ret, 3, z_args TSRMLS_CC);
 		/* cleanup */
 		efree(z_args[1]);
@@ -975,6 +1002,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl
 		ZVAL_STRINGL(&z_fun_set, "SET", 3, 0);
 		ZVAL_STRINGL(z_args[0], key, key_len, 0);
 		ZVAL_STRINGL(z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */
+		zval_dtor(&z_ret); /* free memory from our previous return value */
 		call_user_function(&redis_ce->function_table, &z_to, &z_fun_set, &z_ret, 2, z_args TSRMLS_CC);
 		/* cleanup */
 		zval_dtor(z_args[1]);
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 8f10654260..06b5332a4a 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -12,7 +12,7 @@
 
 RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
 RedisArray *ra_load_array(const char *name TSRMLS_DC);
-RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
+RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC);
 zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC);
 zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC);
 void ra_init_function_table(RedisArray *ra);
diff --git a/rpm/php-redis.spec b/rpm/php-redis.spec
index 4f04fb1855..5363d1eead 100644
--- a/rpm/php-redis.spec
+++ b/rpm/php-redis.spec
@@ -3,7 +3,7 @@
 %global php_version %(php-config --version 2>/dev/null || echo 0)
 
 Name:           php-redis
-Version:        2.2.4
+Version:        2.2.5
 Release:        1%{?dist}
 Summary:        The phpredis extension provides an API for communicating with the Redis key-value store.
 
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 2a6847542e..ad5772022e 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -72,6 +72,47 @@ public function testPipelinePublish() {
 		$this->assertTrue(is_array($ret) && count($ret) === 1 && $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", "lt")) {
+            $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", Array($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(Array($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->delete('key');
@@ -110,6 +151,31 @@ public function testBitsets() {
 	    // values above 1 are changed to 1 but don't overflow on bits to the right.
 	    $this->assertTrue(0 === $this->redis->setBit('key', 0, 0xff));
 	    $this->assertTrue("\x9f" === $this->redis->get('key'));
+
+        // Verify valid offset ranges
+        $this->assertFalse($this->redis->getBit('key', -1));
+        $this->assertFalse($this->redis->getBit('key', 4294967296));
+        $this->assertFalse($this->redis->setBit('key', -1, 1));
+        $this->assertFalse($this->redis->setBit('key', 4294967296, 1));
+    }
+
+    public function testBitPos() {
+        if(version_compare($this->version, "2.8.7", "lt")) {
+            $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', 1, 0), 8);
+        $this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 8);
+
+        $this->redis->set('bpkey', "\x00\x00\x00");
+        $this->assertEquals($this->redis->bitpos('bpkey', 1), -1);
     }
 
     public function test1000() {
@@ -258,6 +324,12 @@ public function testExtendedSet() {
         $this->assertTrue($this->redis->set('foo','barbaz', Array('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);
     }
 
     public function testGetSet() {
@@ -475,6 +547,16 @@ public function testIncrByFloat()
 
 	$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.8', $this->redis->get('key'));
+        $this->redis->setOption(Redis::OPT_PREFIX, '');
+        $this->assertTrue($this->redis->exists('someprefix:key'));
+        $this->redis->del('someprefix:key');
+
     }
 
     public function testDecr()
@@ -1144,6 +1226,28 @@ public function testsRandMember() {
 	        break;
 	    }
 	}
+
+        // 
+        // With and without count, while serializing
+        // 
+
+        $this->redis->delete('set0');
+        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::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_SERIALIZER, Redis::SERIALIZER_NONE);
     }
 
     public function testSRandMemberWithCount() {
@@ -1759,6 +1863,34 @@ public function testSlowlog() {
         $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', 'lt')) {
+            $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_slaves, 100), $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+1, 100) < $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(-1, 20));
+    }
+
     public function testinfo() {
 	$info = $this->redis->info();
 
@@ -4356,6 +4488,34 @@ public function testEvalSHA() {
     	$this->assertTrue(1 === $this->redis->evalsha($sha));
     }
 
+    public function testSerialize() {
+        $vals = Array(1, 1.5, 'one', Array('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()) === 'Array');
+        $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object');
+
+        $arr_serializers = Array(Redis::SERIALIZER_PHP);
+        if(defined('Redis::SERIALIZER_IGBINARY')) {
+            $arr_serializers[] = Redis::SERIALIZER_IGBINARY;
+        }
+
+        foreach($arr_serializers as $mode) {
+            $arr_enc = Array(); 
+            $arr_dec = Array();
+
+            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 = Array(
     		1,1.5,'one',Array('this','is','an','array')
@@ -4462,6 +4622,190 @@ public function testIntrospection() {
         $this->assertTrue($this->redis->getAuth() === self::AUTH);
     }
 
+    /**
+     * Scan and variants
+     */
+
+    protected function get_keyspace_count($str_db) {
+        $arr_info = $this->redis->info();
+        $arr_info = $arr_info[$str_db];
+        $arr_info = explode(',', $arr_info);
+        $arr_info = explode('=', $arr_info[0]);
+        return $arr_info[1];
+    }
+
+    public function testScan() {
+        if(version_compare($this->version, "2.8.0", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        // Key count
+        $i_key_count = $this->get_keyspace_count('db0');
+
+        // Have scan retry
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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);
+    }
+
+    public function testHScan() {
+        if(version_compare($this->version, "2.8.0", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+    
+        // Never get empty sets
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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", "lt")) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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(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(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_SCAN, Redis::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(0, $i_p_score);
+        $this->assertEquals(0, $i_p_count);
+    }
 }
 
 exit(TestSuite::run("Redis_Test"));

From 566d673f83170e9f5a49d8f072c8e5e0a1c8d6b1 Mon Sep 17 00:00:00 2001
From: vostok4 
Date: Wed, 9 Apr 2014 11:21:32 +0200
Subject: [PATCH 0138/1986] Fix comments for C89 compatibility

---
 redis.c       | 538 +++++++++++++++++++++++++-------------------------
 redis_array.c |   4 +-
 2 files changed, 271 insertions(+), 271 deletions(-)

diff --git a/redis.c b/redis.c
index b565069d4c..1158e2a240 100644
--- a/redis.c
+++ b/redis.c
@@ -413,7 +413,7 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int
 
     if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "socket",
                                   sizeof("socket"), (void **) &socket) == FAILURE) {
-    	// Throw an exception unless we've been requested not to
+    	/* Throw an exception unless we've been requested not to */
         if(!no_throw) {
         	zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC);
         }
@@ -423,7 +423,7 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int
     *redis_sock = (RedisSock *) zend_list_find(Z_LVAL_PP(socket), &resource_type);
 
     if (!*redis_sock || resource_type != le_redis_sock) {
-		// Throw an exception unless we've been requested not to
+		/* Throw an exception unless we've been requested not to */
     	if(!no_throw) {
     		zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC);
     	}
@@ -448,14 +448,14 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS)
     zval *object;
     RedisSock *redis_sock;
 
-    // If we can't grab our object, or get a socket, or we're not connected, return NULL
+    /* If we can't grab our object, or get a socket, or we're not connected, return NULL */
     if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) ||
        (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || redis_sock->status != REDIS_SOCK_STATUS_CONNECTED)
     {
         return NULL;
     }
 
-    // Return our socket
+    /* Return our socket */
     return redis_sock;
 }
 
@@ -593,14 +593,14 @@ PHP_METHOD(Redis,__destruct) {
 		RETURN_FALSE;
 	}
 
-	// Grab our socket
+	/* Grab our socket */
 	if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 1) < 0) {
 		RETURN_FALSE;
 	}
 
-	// If we think we're in MULTI mode, send a discard
+	/* If we think we're in MULTI mode, send a discard */
 	if(redis_sock->mode == MULTI) {
-		// Discard any multi commands, and free any callbacks that have been queued
+		/* Discard any multi commands, and free any callbacks that have been queued */
 		send_discard_static(redis_sock TSRMLS_CC);
 		free_reply_callbacks(getThis(), redis_sock);
 	}
@@ -847,15 +847,15 @@ PHP_METHOD(Redis, bitpos)
         RETURN_FALSE;
     }
 
-    // We can prevalidate the first argument
+    /* We can prevalidate the first argument */
     if(bit != 0 && bit != 1) {
         RETURN_FALSE;
     }
 
-    // Prefix our key
+    /* Prefix our key */
     key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
-    // Various command semantics
+    /* Various command semantics */
     if(argc == 2) {
         cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd", key, key_len,
                                           bit);
@@ -867,7 +867,7 @@ PHP_METHOD(Redis, bitpos)
                                           bit, start, end);
     }
 
-    // Free our key if it was prefixed
+    /* Free our key if it was prefixed */
     if(key_free) efree(key);
 
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -912,7 +912,7 @@ PHP_METHOD(Redis, set) {
     int val_free = 0, key_free = 0;
     zval *z_value, *z_opts = NULL;
 
-    // Make sure the arguments are correct
+    /* Make sure the arguments are correct */
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|z",
                                     &object, redis_ce, &key, &key_len, &z_value,
                                     &z_opts) == FAILURE)
@@ -920,7 +920,7 @@ PHP_METHOD(Redis, set) {
         RETURN_FALSE;
     }
 
-    // Ensure we can grab our redis socket
+    /* Ensure we can grab our redis socket */
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
@@ -950,7 +950,7 @@ PHP_METHOD(Redis, set) {
             zend_hash_has_more_elements(kt) == SUCCESS;
             zend_hash_move_forward(kt))
         {
-            // Grab key and value
+            /* Grab key and value */
             type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL);
             zend_hash_get_current_data(kt, (void**)&v);
 
@@ -1421,7 +1421,7 @@ PHP_METHOD(Redis, incrByFloat) {
 		RETURN_FALSE;
 	}
 
-	// Prefix key, format command, free old key if necissary
+	/* Prefix key, format command, free old key if necissary */
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 	cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val);
 	if(key_free) efree(key);	
@@ -1490,29 +1490,29 @@ PHP_METHOD(Redis, getMultiple)
     smart_str cmd = {0};
     int arg_count;
 
-    // Make sure we have proper arguments
+    /* Make sure we have proper arguments */
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
                                     &object, redis_ce, &z_args) == FAILURE) {
         RETURN_FALSE;
     }
 
-    // We'll need the socket
+    /* We'll need the socket */
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    // Grab our array
+    /* Grab our array */
     hash = Z_ARRVAL_P(z_args);
 
-    // We don't need to do anything if there aren't any keys
+    /* We don't need to do anything if there aren't any keys */
     if((arg_count = zend_hash_num_elements(hash)) == 0) {
         RETURN_FALSE;
     }
 
-    // Build our command header
+    /* Build our command header */
     redis_cmd_init_sstr(&cmd, arg_count, "MGET", 4);
 
-    // Iterate through and grab our keys
+    /* Iterate through and grab our keys */
     for(zend_hash_internal_pointer_reset_ex(hash, &ptr);
         zend_hash_get_current_data_ex(hash, (void**)&z_ele, &ptr) == SUCCESS;
         zend_hash_move_forward_ex(hash, &ptr))
@@ -1521,7 +1521,7 @@ PHP_METHOD(Redis, getMultiple)
         int key_len, key_free;
         zval *z_tmp = NULL;
 
-        // If the key isn't a string, turn it into one
+        /* If the key isn't a string, turn it into one */
         if(Z_TYPE_PP(z_ele) == IS_STRING) {
             key = Z_STRVAL_PP(z_ele);
             key_len = Z_STRLEN_PP(z_ele);
@@ -1535,16 +1535,16 @@ PHP_METHOD(Redis, getMultiple)
             key_len = Z_STRLEN_P(z_tmp);
         }
 
-        // Apply key prefix if necissary
+        /* Apply key prefix if necissary */
         key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
-        // Append this key to our command
+        /* Append this key to our command */
         redis_cmd_append_sstr(&cmd, key, key_len);
 
-        // Free our key if it was prefixed
+        /* Free our key if it was prefixed */
         if(key_free) efree(key);
 
-        // Free oour temporary ZVAL if we converted from a non-string
+        /* Free oour temporary ZVAL if we converted from a non-string */
         if(z_tmp) {
             zval_dtor(z_tmp);
             efree(z_tmp);
@@ -1552,7 +1552,7 @@ PHP_METHOD(Redis, getMultiple)
         }
     }
 
-    // Kick off our command
+    /* Kick off our command */
     REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
     IF_ATOMIC() {
         if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
@@ -1830,7 +1830,7 @@ PHP_METHOD(Redis, getBit)
 		RETURN_FALSE;
 	}
 
-	// GETBIT and SETBIT only work for 0 - 2^32-1
+	/* GETBIT and SETBIT only work for 0 - 2^32-1 */
 	if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) {
 	    RETURN_FALSE;
 	}
@@ -1864,7 +1864,7 @@ PHP_METHOD(Redis, setBit)
 		RETURN_FALSE;
 	}
 
-	// GETBIT and SETBIT only work for 0 - 2^32-1
+	/* GETBIT and SETBIT only work for 0 - 2^32-1 */
 	if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) {
 	    RETURN_FALSE;
 	}
@@ -2417,37 +2417,37 @@ PHP_METHOD(Redis, sRandMember)
     int key_len, cmd_len, key_free = 0;
     long count;
 
-    // Parse our params
+    /* Parse our params */
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
                                     &object, redis_ce, &key, &key_len, &count) == FAILURE) {
         RETURN_FALSE;
     }
 
-    // Get our redis socket
+    /* Get our redis socket */
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    // Prefix our key if necissary
+    /* Prefix our key if necissary */
     key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
-    // If we have two arguments, we're running with an optional COUNT, which will return
-    // a multibulk reply.  Without the argument we'll return a string response
+    /* If we have two arguments, we're running with an optional COUNT, which will return */
+    /* a multibulk reply.  Without the argument we'll return a string response */
     if(ZEND_NUM_ARGS() == 2) {
-        // Construct our command with count
+        /* Construct our command with count */
         cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "sl", key, key_len, count);
     } else {
-        // Construct our command
+        /* Construct our command */
         cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "s", key, key_len);
     }
 
-    // Free our key if we prefixed it
+    /* Free our key if we prefixed it */
     if(key_free) efree(key);
 
-    // Process our command
+    /* Process our command */
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 
-    // Either bulk or multi-bulk depending on argument count
+    /* Either bulk or multi-bulk depending on argument count */
     if(ZEND_NUM_ARGS() == 2) {
     IF_ATOMIC() {
             if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
@@ -2655,7 +2655,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
         }
 		if(has_timeout) {
 			keys_len[j] = spprintf(&keys[j], 0, "%d", timeout);
-			cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; // $ + size + NL + string + NL 
+			cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; /* $ + size + NL + string + NL  */
 			j++;
 			real_argc++;
 		}
@@ -2685,7 +2685,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
        	        keys[j] = Z_STRVAL_P(z_args[i]);
            	    keys_len[j] = Z_STRLEN_P(z_args[i]);
 
-           	    // If we have a timeout it should be the last argument, which we do not want to prefix
+           	    /* If we have a timeout it should be the last argument, which we do not want to prefix */
 				if(!has_timeout || i < argc-1) {
 					keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j] TSRMLS_CC); /* add optional prefix  TSRMLS_CC*/
 				}
@@ -2697,7 +2697,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
 		}
     }
 
-    cmd_len += 1 + integer_length(real_argc+1) + 2; // *count NL 
+    cmd_len += 1 + integer_length(real_argc+1) + 2; /* *count NL  */
     cmd = emalloc(cmd_len+1);
 
     sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1+real_argc, keyword_len, keyword);
@@ -2708,7 +2708,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
 
     /* copy each key to its destination */
     for(i = 0; i < real_argc; ++i) {
-        sprintf(cmd + pos, "$%d" _NL, keys_len[i]);     // size
+        sprintf(cmd + pos, "$%d" _NL, keys_len[i]);     /* size */
         pos += 1 + integer_length(keys_len[i]) + 2;
         memcpy(cmd + pos, keys[i], keys_len[i]);
         pos += keys_len[i];
@@ -2953,7 +2953,7 @@ PHP_METHOD(Redis, sort) {
                                                  , Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur));
                 elements += 2;
                 efree(old_cmd);
-            } else if(Z_TYPE_PP(z_cur) == IS_ARRAY) { // loop over the strings in that array and add them as patterns
+            } else if(Z_TYPE_PP(z_cur) == IS_ARRAY) { /* loop over the strings in that array and add them as patterns */
 
                 HashTable *keytable = Z_ARRVAL_PP(z_cur);
                 for(zend_hash_internal_pointer_reset(keytable);
@@ -3005,7 +3005,7 @@ PHP_METHOD(Redis, sort) {
 
             if(zend_hash_num_elements(Z_ARRVAL_PP(z_cur)) == 2) {
                 zval **z_offset_pp, **z_count_pp;
-                // get the two values from the table, check that they are indeed of LONG type
+                /* get the two values from the table, check that they are indeed of LONG type */
                 if(SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_cur), 0, (void**)&z_offset_pp) &&
                   SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_cur), 1, (void**)&z_count_pp)) {
 
@@ -3105,7 +3105,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in
     cmd_lines[2] = estrdup("SORT");
     cmd_sizes[2] = 4;
 
-    // Prefix our key if we need to
+    /* Prefix our key if we need to */
     key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
     /* second line, key */
@@ -3115,7 +3115,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in
     cmd_lines[4][key_len] = 0;
     cmd_sizes[4] = key_len;
 
-    // If we prefixed our key, free it
+    /* If we prefixed our key, free it */
     if(key_free) efree(key);
 
     cmd_elements = 5;
@@ -3505,7 +3505,7 @@ PHP_METHOD(Redis, auth) {
 
     cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", password, password_len);
 
-    // Free previously stored auth if we have one, and store this password
+    /* Free previously stored auth if we have one, and store this password */
     if(redis_sock->auth) efree(redis_sock->auth);
     redis_sock->auth = estrndup(password, password_len);
 
@@ -3607,7 +3607,7 @@ PHP_METHOD(Redis, info) {
         RETURN_FALSE;
     }
 
-    // Build a standalone INFO command or one with an option
+    /* Build a standalone INFO command or one with an option */
     if(opt != NULL) {
     	cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len);
     } else {
@@ -3706,7 +3706,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI
 
     char *cmd = NULL, *p = NULL;
     int cmd_len = 0, argc = 0, kw_len = strlen(kw);
-	int step = 0;	// 0: compute size; 1: copy strings.
+	int step = 0;	/* 0: compute size; 1: copy strings. */
     zval *z_array;
 
 	HashTable *keytable;
@@ -3753,14 +3753,14 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI
 				continue; 	/* this should never happen, according to the PHP people. */
 			}
 
-			// If the key isn't a string, use the index value returned when grabbing the
-			// key.  We typecast to long, because they could actually be negative.
+			/* If the key isn't a string, use the index value returned when grabbing the */
+			/* key.  We typecast to long, because they could actually be negative. */
 			if(type != HASH_KEY_IS_STRING) {
-				// Create string representation of our index
+				/* Create string representation of our index */
 				key_len = snprintf(buf, sizeof(buf), "%ld", (long)idx);
 				key = (char*)buf;
 			} else if(key_len > 0) {
-				// When not an integer key, the length will include the \0
+				/* When not an integer key, the length will include the \0 */
 				key_len--;
 			}
 
@@ -3916,7 +3916,7 @@ PHP_METHOD(Redis, zAdd) {
 
 	/* need key, score, value, [score, value...] */
 	if(argc > 1) {
-		convert_to_string(z_args[0]); // required string
+		convert_to_string(z_args[0]); /* required string */
 	}
 	if(argc < 3 || Z_TYPE_P(z_args[0]) != IS_STRING || (argc-1) % 2 != 0) {
 		efree(z_args);
@@ -3948,8 +3948,8 @@ PHP_METHOD(Redis, zAdd) {
 	smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
 
 	for(i = 1; i < argc; i +=2) {
-		convert_to_double(z_args[i]); // convert score to double
-		val_free = redis_serialize(redis_sock, z_args[i+1], &val, &val_len TSRMLS_CC); // possibly serialize value.
+		convert_to_double(z_args[i]); /* convert score to double */
+		val_free = redis_serialize(redis_sock, z_args[i+1], &val, &val_len TSRMLS_CC); /* possibly serialize value. */
 
 		/* add score */
 		score = Z_DVAL_P(z_args[i]);
@@ -4203,7 +4203,7 @@ redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) {
         if(zend_hash_find(Z_ARRVAL_P(z_options), "limit", sizeof("limit"), (void**)&z_limit_val_pp)== SUCCESS) {;
             if(zend_hash_num_elements(Z_ARRVAL_PP(z_limit_val_pp)) == 2) {
                 zval **z_offset_pp, **z_count_pp;
-                // get the two values from the table, check that they are indeed of LONG type
+                /* get the two values from the table, check that they are indeed of LONG type */
                 if(SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_limit_val_pp), 0, (void**)&z_offset_pp) &&
                   SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_limit_val_pp), 1, (void**)&z_count_pp) &&
                   Z_TYPE_PP(z_offset_pp) == IS_LONG &&
@@ -4476,7 +4476,7 @@ PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command
     char *store_key, *agg_op = NULL;
     int cmd_arg_count = 2, store_key_len, agg_op_len = 0, keys_count, key_free;
 
-    // Grab our parameters
+    /* Grab our parameters */
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",
                                     &object, redis_ce, &store_key, &store_key_len,
                                     &z_keys, &z_weights, &agg_op, &agg_op_len) == FAILURE)
@@ -4484,40 +4484,40 @@ PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command
         RETURN_FALSE;
     }
 
-    // We'll need our socket
+    /* We'll need our socket */
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    // Grab our keys argument as an array
+    /* Grab our keys argument as an array */
     ht_keys = Z_ARRVAL_P(z_keys);
 
-    // Nothing to do if there aren't any keys
+    /* Nothing to do if there aren't any keys */
     if((keys_count = zend_hash_num_elements(ht_keys)) == 0) {
         RETURN_FALSE;
     } else {
-        // Increment our overall argument count
+        /* Increment our overall argument count */
         cmd_arg_count += keys_count;
     }
 
-    // Grab and validate our weights array
+    /* Grab and validate our weights array */
     if(z_weights != NULL) {
         ht_weights = Z_ARRVAL_P(z_weights);
 
-        // This command is invalid if the weights array isn't the same size
-        // as our keys array.
+        /* This command is invalid if the weights array isn't the same size */
+        /* as our keys array. */
         if(zend_hash_num_elements(ht_weights) != keys_count) {
             RETURN_FALSE;
         }
 
-        // Increment our overall argument count by the number of keys
-        // plus one, for the "WEIGHTS" argument itself
+        /* Increment our overall argument count by the number of keys */
+        /* plus one, for the "WEIGHTS" argument itself */
         cmd_arg_count += keys_count + 1;
     }
 
-    // AGGREGATE option
+    /* AGGREGATE option */
     if(agg_op_len != 0) {
-        // Verify our aggregation option
+        /* Verify our aggregation option */
         if(strncasecmp(agg_op, "SUM", sizeof("SUM")) &&
            strncasecmp(agg_op, "MIN", sizeof("MIN")) &&
            strncasecmp(agg_op, "MAX", sizeof("MAX")))
@@ -4525,22 +4525,22 @@ PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command
             RETURN_FALSE;
         }
 
-        // Two more arguments: "AGGREGATE" and agg_op
+        /* Two more arguments: "AGGREGATE" and agg_op */
         cmd_arg_count += 2;
     }
 
-    // Command header
+    /* Command header */
     redis_cmd_init_sstr(&cmd, cmd_arg_count, command, command_len);
 
-    // Prefix our key if necessary and add the output key
+    /* Prefix our key if necessary and add the output key */
     key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC);
     redis_cmd_append_sstr(&cmd, store_key, store_key_len);
     if(key_free) efree(store_key);
 
-    // Number of input keys argument
+    /* Number of input keys argument */
     redis_cmd_append_sstr_int(&cmd, keys_count);
 
-    // Process input keys
+    /* Process input keys */
     for(zend_hash_internal_pointer_reset_ex(ht_keys, &ptr);
         zend_hash_get_current_data_ex(ht_keys, (void**)&z_data, &ptr)==SUCCESS;
         zend_hash_move_forward_ex(ht_keys, &ptr))
@@ -4561,16 +4561,16 @@ PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command
             key_len = Z_STRLEN_P(z_tmp);
         }
 
-        // Apply key prefix if necessary
+        /* Apply key prefix if necessary */
         key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
-        // Append this input set
+        /* Append this input set */
         redis_cmd_append_sstr(&cmd, key, key_len);
 
-        // Free our key if it was prefixed
+        /* Free our key if it was prefixed */
         if(key_free) efree(key);
 
-        // Free our temporary z_val if it was converted
+        /* Free our temporary z_val if it was converted */
         if(z_tmp) {
             zval_dtor(z_tmp);
             efree(z_tmp);
@@ -4578,29 +4578,29 @@ PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command
         }
     }
 
-    // Weights
+    /* Weights */
     if(ht_weights != NULL) {
-        // Append "WEIGHTS" argument
+        /* Append "WEIGHTS" argument */
         redis_cmd_append_sstr(&cmd, "WEIGHTS", sizeof("WEIGHTS") - 1);
 
-        // Process weights
+        /* Process weights */
         for(zend_hash_internal_pointer_reset_ex(ht_weights, &ptr);
             zend_hash_get_current_data_ex(ht_weights, (void**)&z_data, &ptr)==SUCCESS;
             zend_hash_move_forward_ex(ht_weights, &ptr))
         {
-            // Ignore non numeric arguments, unless they're special Redis numbers
+            /* Ignore non numeric arguments, unless they're special Redis numbers */
             if (Z_TYPE_PP(z_data) != IS_LONG && Z_TYPE_PP(z_data) != IS_DOUBLE &&
                  strncasecmp(Z_STRVAL_PP(z_data), "inf", sizeof("inf")) != 0 &&
                  strncasecmp(Z_STRVAL_PP(z_data), "-inf", sizeof("-inf")) != 0 &&
                  strncasecmp(Z_STRVAL_PP(z_data), "+inf", sizeof("+inf")) != 0)
             {
-                // We should abort if we have an invalid weight, rather than pass
-                // a different number of weights than the user is expecting
+                /* We should abort if we have an invalid weight, rather than pass */
+                /* a different number of weights than the user is expecting */
                 efree(cmd.c);
                 RETURN_FALSE;
             }
 
-            // Append the weight based on the input type
+            /* Append the weight based on the input type */
             switch(Z_TYPE_PP(z_data)) {
                 case IS_LONG:
                     redis_cmd_append_sstr_long(&cmd, Z_LVAL_PP(z_data));
@@ -4615,13 +4615,13 @@ PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command
         }
     }
 
-    // Aggregation options, if we have them
+    /* Aggregation options, if we have them */
     if(agg_op_len != 0) {
         redis_cmd_append_sstr(&cmd, "AGGREGATE", sizeof("AGGREGATE") - 1);
         redis_cmd_append_sstr(&cmd, agg_op, agg_op_len);
     }
 
-    // Kick off our request
+    /* Kick off our request */
     REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
     IF_ATOMIC() {
         redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
@@ -4964,14 +4964,14 @@ PHP_METHOD(Redis, hIncrByFloat)
 	int key_len, member_len, cmd_len, key_free;
 	double val;
 
-	// Validate we have the right number of arguments
+	/* Validate we have the right number of arguments */
 	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossd",
 									&object, redis_ce,
 									&key, &key_len, &member, &member_len, &val) == FAILURE) {
 		RETURN_FALSE;
 	}
 
-	// Grab our socket
+	/* Grab our socket */
 	if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
 		RETURN_FALSE;
 	}
@@ -5040,7 +5040,7 @@ PHP_METHOD(Redis, hMget) {
     HashPosition ptr;
     smart_str cmd = {0};
 
-    // Make sure we can grab our arguments properly
+    /* Make sure we can grab our arguments properly */
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa",
                                     &object, redis_ce, &key, &key_len, &z_array)
                                     == FAILURE)
@@ -5048,68 +5048,68 @@ PHP_METHOD(Redis, hMget) {
         RETURN_FALSE;
     }
 
-    // We'll need our socket
+    /* We'll need our socket */
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    // Grab member count and abort if we don't have any
+    /* Grab member count and abort if we don't have any */
     if((field_count = zend_hash_num_elements(Z_ARRVAL_P(z_array))) == 0) {
         RETURN_FALSE;
     }
 
-    // Prefix our key if we need to
+    /* Prefix our key if we need to */
     key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
-    // Allocate enough memory for the number of keys being requested
+    /* Allocate enough memory for the number of keys being requested */
     z_keys = ecalloc(field_count, sizeof(zval *));
 
-    // Grab our HashTable
+    /* Grab our HashTable */
     ht_array = Z_ARRVAL_P(z_array);
 
-    // Iterate through our keys, grabbing members that are valid
+    /* Iterate through our keys, grabbing members that are valid */
     for(valid=0, zend_hash_internal_pointer_reset_ex(ht_array, &ptr);
         zend_hash_get_current_data_ex(ht_array, (void**)&data, &ptr)==SUCCESS;
         zend_hash_move_forward_ex(ht_array, &ptr))
     {
-        // Make sure the data is a long or string, and if it's a string that
-        // it isn't empty.  There is no reason to send empty length members.
+        /* Make sure the data is a long or string, and if it's a string that */
+        /* it isn't empty.  There is no reason to send empty length members. */
         if((Z_TYPE_PP(data) == IS_STRING && Z_STRLEN_PP(data)>0) ||
             Z_TYPE_PP(data) == IS_LONG) 
         {
-            // This is a key we can ask for, copy it and set it in our array
+            /* This is a key we can ask for, copy it and set it in our array */
             MAKE_STD_ZVAL(z_keys[valid]);
             *z_keys[valid] = **data;
             zval_copy_ctor(z_keys[valid]);
             convert_to_string(z_keys[valid]);
 
-            // Increment the number of valid keys we've encountered
+            /* Increment the number of valid keys we've encountered */
             valid++;
         }
     }
 
-    // If we don't have any valid keys, we can abort here
+    /* If we don't have any valid keys, we can abort here */
     if(valid == 0) {
         if(key_free) efree(key);
         efree(z_keys);
         RETURN_FALSE;
     }
 
-    // Build command header.  One extra argument for the hash key itself
+    /* Build command header.  One extra argument for the hash key itself */
     redis_cmd_init_sstr(&cmd, valid+1, "HMGET", sizeof("HMGET")-1);
 
-    // Add the hash key
+    /* Add the hash key */
     redis_cmd_append_sstr(&cmd, key, key_len);
 
-    // Free key memory if it was prefixed
+    /* Free key memory if it was prefixed */
     if(key_free) efree(key);
 
-    // Iterate our keys, appending them as arguments
+    /* Iterate our keys, appending them as arguments */
     for(i=0;i  0) {
-	    	// Header for our EVAL command
+	    	/* Header for our EVAL command */
 	    	cmd_len = redis_cmd_format_header(ret, keyword, eval_cmd_count + args_count);
 
-	    	// Now append the script itself, and the number of arguments to treat as keys
+	    	/* Now append the script itself, and the number of arguments to treat as keys */
 	    	cmd_len = redis_cmd_append_str(ret, cmd_len, value, val_len);
 	    	cmd_len = redis_cmd_append_int(ret, cmd_len, keys_count);
 
-			// Iterate the values in our "keys" array
+			/* Iterate the values in our "keys" array */
 			for(zend_hash_internal_pointer_reset_ex(args_hash, &hash_pos);
 				zend_hash_get_current_data_ex(args_hash, (void **)&elem, &hash_pos) == SUCCESS;
 				zend_hash_move_forward_ex(args_hash, &hash_pos))
@@ -6338,7 +6338,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val
 					key = Z_STRVAL_PP(elem);
 					key_len = Z_STRLEN_PP(elem);
 				} else {
-					// Convert it to a string
+					/* Convert it to a string */
 					MAKE_STD_ZVAL(z_tmp);
 					*z_tmp = **elem;
 					zval_copy_ctor(z_tmp);
@@ -6348,20 +6348,20 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val
 					key_len = Z_STRLEN_P(z_tmp);
 				}
 
-				// Keep track of the old command pointer
+				/* Keep track of the old command pointer */
 				old_cmd = *ret;
 
-				// If this is still a key argument, prefix it if we've been set up to prefix keys
+				/* If this is still a key argument, prefix it if we've been set up to prefix keys */
 				key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC) : 0;
 
-				// Append this key to our EVAL command, free our old command
+				/* Append this key to our EVAL command, free our old command */
 				cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, cmd_len, key_len, key, key_len);
 				efree(old_cmd);
 
-				// Free our key, old command if we need to
+				/* Free our key, old command if we need to */
 				if(key_free) efree(key);
 
-				// Free our temporary zval (converted from non string) if we've got one
+				/* Free our temporary zval (converted from non string) if we've got one */
 				if(z_tmp) {
 					zval_dtor(z_tmp);
 					efree(z_tmp);
@@ -6370,12 +6370,12 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val
 	    }
 	}
 
-	// If there weren't any arguments (none passed, or an empty array), construct a standard no args command
+	/* If there weren't any arguments (none passed, or an empty array), construct a standard no args command */
 	if(args_count < 1) {
 		cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, val_len, 0);
 	}
 
-	// Return our command length
+	/* Return our command length */
 	return cmd_len;
 }
 
@@ -6394,12 +6394,12 @@ PHP_METHOD(Redis, evalsha)
 		RETURN_FALSE;
 	}
 
-	// Attempt to grab socket
+	/* Attempt to grab socket */
 	if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
 		RETURN_FALSE;
 	}
 
-	// Construct our EVALSHA command
+	/* Construct our EVALSHA command */
 	cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVALSHA", sha, sha_len, args, keys_count TSRMLS_CC);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -6421,18 +6421,18 @@ PHP_METHOD(Redis, eval)
 	int script_len, cmd_len;
 	long keys_count = 0;
 
-	// Attempt to parse parameters
+	/* Attempt to parse parameters */
 	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|al",
 							 &object, redis_ce, &script, &script_len, &args, &keys_count) == FAILURE) {
 		RETURN_FALSE;
 	}
 
-	// Attempt to grab socket
+	/* Attempt to grab socket */
     if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    // Construct our EVAL command
+    /* Construct our EVAL command */
     cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVAL", script, script_len, args, keys_count TSRMLS_CC);
 
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -6446,23 +6446,23 @@ PHP_METHOD(Redis, eval)
 
 PHP_REDIS_API int
 redis_build_script_exists_cmd(char **ret, zval **argv, int argc) {
-	// Our command length and iterator
+	/* Our command length and iterator */
 	int cmd_len = 0, i;
 
-	// Start building our command
-	cmd_len = redis_cmd_format_header(ret, "SCRIPT", argc + 1); // +1 for "EXISTS"
+	/* Start building our command */
+	cmd_len = redis_cmd_format_header(ret, "SCRIPT", argc + 1); /* +1 for "EXISTS" */
 	cmd_len = redis_cmd_append_str(ret, cmd_len, "EXISTS", 6);
 
-	// Iterate our arguments
+	/* Iterate our arguments */
 	for(i=0;iprefix != NULL && redis_sock->prefix_len > 0) {
 		redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 		RETURN_STRINGL(key, key_len, 0);
@@ -6692,22 +6692,22 @@ PHP_METHOD(Redis, _serialize) {
     char *val;
     int val_len;
 
-    // Parse arguments
+    /* Parse arguments */
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
                                     &object, redis_ce, &z_val) == FAILURE)
     {
         RETURN_FALSE;
     }
 
-    // Grab socket
+    /* Grab socket */
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    // Serialize, which will return a value even if no serializer is set
+    /* Serialize, which will return a value even if no serializer is set */
     redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
 
-    // Return serialized value.  Tell PHP to make a copy as some can be interned.
+    /* Return serialized value.  Tell PHP to make a copy as some can be interned. */
     RETVAL_STRINGL(val, val_len, 1);
     STR_FREE(val);
 }
@@ -6721,27 +6721,27 @@ PHP_METHOD(Redis, _unserialize) {
 	char *value;
 	int value_len;
 
-	// Parse our arguments
+	/* Parse our arguments */
 	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce,
 									&value, &value_len) == FAILURE) {
 		RETURN_FALSE;
 	}
-	// Grab socket
+	/* Grab socket */
 	if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
 		RETURN_FALSE;
 	}
 
-	// We only need to attempt unserialization if we have a serializer running
+	/* We only need to attempt unserialization if we have a serializer running */
 	if(redis_sock->serializer != REDIS_SERIALIZER_NONE) {
 		zval *z_ret = NULL;
 		if(redis_unserialize(redis_sock, value, value_len, &z_ret TSRMLS_CC) == 0) {
-			// Badly formed input, throw an execption
+			/* Badly formed input, throw an execption */
 			zend_throw_exception(redis_exception_ce, "Invalid serialized data, or unserialization error", 0 TSRMLS_CC);
 			RETURN_FALSE;
 		}
 		RETURN_ZVAL(z_ret, 0, 1);
 	} else {
-		// Just return the value that was passed to us
+		/* Just return the value that was passed to us */
 		RETURN_STRINGL(value, value_len, 1);
 	}
 }
@@ -6753,16 +6753,16 @@ PHP_METHOD(Redis, getLastError) {
 	zval *object;
 	RedisSock *redis_sock;
 
-	// Grab our object
+	/* Grab our object */
 	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) {
 		RETURN_FALSE;
 	}
-	// Grab socket
+	/* Grab socket */
 	if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
 		RETURN_FALSE;
 	}
 
-	// Return our last error or NULL if we don't have one
+	/* Return our last error or NULL if we don't have one */
 	if(redis_sock->err != NULL && redis_sock->err_len > 0) {
 		RETURN_STRINGL(redis_sock->err, redis_sock->err_len, 1);
 	} else {
@@ -6777,16 +6777,16 @@ PHP_METHOD(Redis, clearLastError) {
 	zval *object;
 	RedisSock *redis_sock;
 
-	// Grab our object
+	/* Grab our object */
 	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) {
 		RETURN_FALSE;
 	}
-	// Grab socket
+	/* Grab socket */
 	if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
 		RETURN_FALSE;
 	}
 
-	// Clear error message
+	/* Clear error message */
 	if(redis_sock->err) {
 		efree(redis_sock->err);
 	}
@@ -6805,19 +6805,19 @@ PHP_METHOD(Redis, time) {
 	char *cmd;
 	int cmd_len;
 
-	// Grab our object
+	/* Grab our object */
 	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) {
 		RETURN_FALSE;
 	}
-	// Grab socket
+	/* Grab socket */
 	if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
 		RETURN_FALSE;
 	}
 
-	// Build TIME command
+	/* Build TIME command */
 	cmd_len = redis_cmd_format_static(&cmd, "TIME", "");
 
-	// Execute or queue command
+	/* Execute or queue command */
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 	IF_ATOMIC() {
 		if(redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
@@ -6864,7 +6864,7 @@ PHP_METHOD(Redis, getPort) {
     RedisSock *redis_sock;
 
     if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
-        // Return our port
+        /* Return our port */
         RETURN_LONG(redis_sock->port);
     } else {
         RETURN_FALSE;
@@ -6878,7 +6878,7 @@ PHP_METHOD(Redis, getDBNum) {
     RedisSock *redis_sock;
 
     if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
-        // Return our db number
+        /* Return our db number */
         RETURN_LONG(redis_sock->dbNumber);
     } else {
         RETURN_FALSE;
@@ -6957,19 +6957,19 @@ PHP_METHOD(Redis, client) {
     char *cmd, *opt=NULL, *arg=NULL;
     int cmd_len, opt_len, arg_len;
 
-    // Parse our method parameters
+    /* Parse our method parameters */
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s", 
         &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE) 
     {
         RETURN_FALSE;
     }
 
-    // Grab our socket
+    /* Grab our socket */
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    // Build our CLIENT command
+    /* Build our CLIENT command */
     if(ZEND_NUM_ARGS() == 2) {
         cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len,
                                           arg, arg_len); 
@@ -6977,10 +6977,10 @@ PHP_METHOD(Redis, client) {
         cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
     }
 
-    // Execute our queue command
+    /* Execute our queue command */
     REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 
-    // We handle CLIENT LIST with a custom response function
+    /* We handle CLIENT LIST with a custom response function */
     if(!strncasecmp(opt, "list", 4)) {
         IF_ATOMIC() {
             redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
@@ -7004,11 +7004,11 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
     char *keyword;
     int arg_count, cmd_len;
 
-    // Count our arguments +1 for key if it's got one, and + 2 for pattern
-    // or count given that they each carry keywords with them.
+    /* Count our arguments +1 for key if it's got one, and + 2 for pattern */
+    /* or count given that they each carry keywords with them. */
     arg_count = 1 + (key_len>0) + (pattern_len>0?2:0) + (count>0?2:0);
 
-    // Turn our type into a keyword
+    /* Turn our type into a keyword */
     switch(type) {
         case TYPE_SCAN:
             keyword = "SCAN";
@@ -7025,30 +7025,30 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
             break;
     }
 
-    // Start the command
+    /* Start the command */
     cmd_len = redis_cmd_format_header(cmd, keyword, arg_count);
 
-    // Add the key in question if we have one
+    /* Add the key in question if we have one */
     if(key_len) {
         cmd_len = redis_cmd_append_str(cmd, cmd_len, key, key_len);
     }
 
-    // Add our iterator
+    /* Add our iterator */
     cmd_len = redis_cmd_append_int(cmd, cmd_len, iter);
 
-    // Append COUNT if we've got it
+    /* Append COUNT if we've got it */
     if(count) {
         cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", sizeof("COUNT")-1);
         cmd_len = redis_cmd_append_int(cmd, cmd_len, count);
     }
 
-    // Append MATCH if we've got it
+    /* Append MATCH if we've got it */
     if(pattern_len) {
         cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", sizeof("MATCH")-1);
         cmd_len = redis_cmd_append_str(cmd, cmd_len, pattern, pattern_len);
     }
 
-    // Return our command length
+    /* Return our command length */
     return cmd_len;
 }
 
@@ -7064,9 +7064,9 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
     int cmd_len, key_len=0, pattern_len=0, num_elements, key_free=0;
     long count=0, iter;
 
-    // Different prototype depending on if this is a key based scan
+    /* Different prototype depending on if this is a key based scan */
     if(type != TYPE_SCAN) {
-        // Requires a key
+        /* Requires a key */
         if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz/|s!l",
                                         &object, redis_ce, &key, &key_len, &z_iter,
                                         &pattern, &pattern_len, &count)==FAILURE)
@@ -7074,7 +7074,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
             RETURN_FALSE;
         }
     } else {
-        // Doesn't require a key
+        /* Doesn't require a key */
         if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz/|s!l",
                                         &object, redis_ce, &z_iter, &pattern, &pattern_len,
                                         &count) == FAILURE)
@@ -7083,32 +7083,32 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         }
     }
 
-    // Grab our socket
+    /* Grab our socket */
     if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
     }
 
-    // Calling this in a pipeline makes no sense
+    /* Calling this in a pipeline makes no sense */
     IF_NOT_ATOMIC() {
         php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't call SCAN commands in multi or pipeline mode!");
         RETURN_FALSE;
     }
 
-    // The iterator should be passed in as NULL for the first iteration, but we can treat
-    // any NON LONG value as NULL for these purposes as we've seperated the variable anyway.
+    /* The iterator should be passed in as NULL for the first iteration, but we can treat */
+    /* any NON LONG value as NULL for these purposes as we've seperated the variable anyway. */
     if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter)<0) {
-        // Convert to long
+        /* Convert to long */
         convert_to_long(z_iter);
         iter = 0;
     } else if(Z_LVAL_P(z_iter)!=0) {
-        // Update our iterator value for the next passthru
+        /* Update our iterator value for the next passthru */
         iter = Z_LVAL_P(z_iter);
     } else {
-        // We're done, back to iterator zero
+        /* We're done, back to iterator zero */
         RETURN_FALSE;
     }
 
-    // Prefix our key if we've got one and we have a prefix set
+    /* Prefix our key if we've got one and we have a prefix set */
     if(key_len) {
         key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
     }
@@ -7121,11 +7121,11 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
      * an updated iterator.
      */
     do {
-        // Format our SCAN command
+        /* Format our SCAN command */
         cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter,
                                    pattern, pattern_len, count);
 
-        // Execute our command getting our new iterator value
+        /* Execute our command getting our new iterator value */
         REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
         if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
                                       redis_sock,type,&iter)<0)
@@ -7134,15 +7134,15 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
             RETURN_FALSE;
         }
 
-        // Get the number of elements
+        /* Get the number of elements */
         hash = Z_ARRVAL_P(return_value);
         num_elements = zend_hash_num_elements(hash);
     } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && num_elements == 0);
 
-    // Free our key if it was prefixed
+    /* Free our key if it was prefixed */
     if(key_free) efree(key);
 
-    // Update our iterator reference
+    /* Update our iterator reference */
     Z_LVAL_P(z_iter) = iter;
 }
 
diff --git a/redis_array.c b/redis_array.c
index e9a56e7ac8..05f0c2e4b0 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -85,7 +85,7 @@ zend_function_entry redis_array_functions[] = {
 static void redis_array_free(RedisArray *ra) {
 	int i;
 
-    // Redis objects
+    /* Redis objects */
     for(i=0;icount;i++) {
 		zval_dtor(ra->redis[i]);
 		efree(ra->redis[i]);
@@ -110,7 +110,7 @@ static void redis_array_free(RedisArray *ra) {
 	zval_dtor(ra->z_pure_cmds);
 	efree(ra->z_pure_cmds);
 
-    // Free structure itself
+    /* Free structure itself */
 	efree(ra);
 }
 

From 52b3c95dab9012b4725070e11f23f5acaf8fe68b Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 15 May 2014 12:11:16 -0700
Subject: [PATCH 0139/1986] SAVE and BGSAVE are distrubte commands

SAVE and BGSAVE need to be passed through multihost_distribute

Refs #472
---
 redis_array.c | 13 +++++++++++++
 redis_array.h |  2 ++
 2 files changed, 15 insertions(+)

diff --git a/redis_array.c b/redis_array.c
index 36254df04f..3c23ca07df 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -69,6 +69,8 @@ zend_function_entry redis_array_functions[] = {
      PHP_ME(RedisArray, getOption, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(RedisArray, setOption, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(RedisArray, keys, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisArray, save, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisArray, bgsave, NULL, ZEND_ACC_PUBLIC)
 
 	 /* Multi/Exec */
      PHP_ME(RedisArray, multi, NULL, ZEND_ACC_PUBLIC)
@@ -620,6 +622,17 @@ PHP_METHOD(RedisArray, flushall)
 	multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL");
 }
 
+PHP_METHOD(RedisArray, save) 
+{
+    multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE");
+}
+
+PHP_METHOD(RedisArray, bgsave)
+{
+    multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE");
+}
+
+
 PHP_METHOD(RedisArray, keys)
 {
 	zval *object, *z_args[1], *z_tmp, z_fun;
diff --git a/redis_array.h b/redis_array.h
index 1be2813af7..02f05c3fa1 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -26,6 +26,8 @@ PHP_METHOD(RedisArray, del);
 PHP_METHOD(RedisArray, keys);
 PHP_METHOD(RedisArray, getOption);
 PHP_METHOD(RedisArray, setOption);
+PHP_METHOD(RedisArray, save);
+PHP_METHOD(RedisArray, bgsave);
 
 PHP_METHOD(RedisArray, multi);
 PHP_METHOD(RedisArray, exec);

From 3aa3c2d2f1578b8d00547677abd774274536bb9a Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 11:28:57 +0200
Subject: [PATCH 0140/1986] split the igbinary stuff into a separate option

---
 config.w32 | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/config.w32 b/config.w32
index 8b39af9057..9600f68e3e 100644
--- a/config.w32
+++ b/config.w32
@@ -2,9 +2,15 @@
 
 ARG_ENABLE("redis", "whether to enable redis support", "yes");
 ARG_ENABLE("redis-session", "whether to enable sessions", "yes");
+ARG_ENABLE("redis-igbinary", "whether to enable igbinary support", "no");
 
 if (PHP_REDIS != "no") {
-	var sources = "redis.c library.c igbinary\\igbinary.c igbinary\\hash_si.c igbinary\\hash_function.c";
+	var sources = "redis.c library.c"
+	
+	if (PHP_REDIS_IGBINARY != "no") {
+		sources += " igbinary\\igbinary.c igbinary\\hash_si.c igbinary\\hash_function.c";
+	}
+
 	if (PHP_REDIS_SESSION != "no") {
 		AC_DEFINE('PHP_SESSION', 1);
 		sources += " redis_session.c";

From 603b20e763ac0cfb7c08cefeb33df51fb077bab1 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 11:29:30 +0200
Subject: [PATCH 0141/1986] fix random() vs php_rand() for portability

---
 library.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/library.c b/library.c
index b0f15db4a5..dfaa248c85 100644
--- a/library.c
+++ b/library.c
@@ -17,6 +17,7 @@
 #include "php_redis.h"
 #include "library.h"
 #include 
+#include 
 
 #define UNSERIALIZE_ONLY_VALUES 0
 #define UNSERIALIZE_ALL 1
@@ -64,7 +65,7 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
     // Wait for a while before trying to reconnect
     if (redis_sock->retry_interval) {
     	// Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time
-   		long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval));
+   		long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval));
     	usleep(retry_interval);
     }
         redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */

From 792a71da7325a3a1e8b671ad757a2ce513e64dd6 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 11:33:23 +0200
Subject: [PATCH 0142/1986] fixed prototype

---
 library.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/library.c b/library.c
index dfaa248c85..b11db1d6e7 100644
--- a/library.c
+++ b/library.c
@@ -19,6 +19,13 @@
 #include 
 #include 
 
+#ifdef PHP_WIN32
+# if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 4
+/* This proto is available from 5.5 on only */
+PHPAPI int usleep(unsigned int useconds);
+# endif
+#endif
+
 #define UNSERIALIZE_ONLY_VALUES 0
 #define UNSERIALIZE_ALL 1
 

From 00be281e1b7385db156a1bb51e1716ceecf78d70 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 11:41:55 +0200
Subject: [PATCH 0143/1986] C89 compat

---
 library.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/library.c b/library.c
index b11db1d6e7..312ec38a4f 100644
--- a/library.c
+++ b/library.c
@@ -564,12 +564,13 @@ int redis_cmd_append_sstr_long(smart_str *str, long append) {
 int redis_cmd_append_sstr_dbl(smart_str *str, double value) {
     char *dbl_str;
     int dbl_len;
+	int retval;
 
     /// Convert to double
     REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, value);
 
     // Append the string
-    int retval = redis_cmd_append_sstr(str, dbl_str, dbl_len);
+    retval = redis_cmd_append_sstr(str, dbl_str, dbl_len);
 
     // Free our double string
     efree(dbl_str);
@@ -583,9 +584,10 @@ int redis_cmd_append_sstr_dbl(smart_str *str, double value) {
  */
 int redis_cmd_append_int(char **cmd, int cmd_len, int append) {
 	char int_buf[32];
+	int int_len;
 
 	// Conver to an int, capture length
-	int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append);
+	int_len = snprintf(int_buf, sizeof(int_buf), "%d", append);
 
 	// Return the new length
 	return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len);
@@ -733,6 +735,10 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
     char *resp;
     int resp_len;
     zval *z_result, *z_sub_result;
+    // Pointers for parsing
+    char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
+    // Key length, done flag
+    int klen = 0, done = 0, is_numeric;
     
     // Make sure we can read a response from Redis
     if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) {
@@ -747,11 +753,8 @@ PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
     ALLOC_INIT_ZVAL(z_sub_result);
     array_init(z_sub_result);
 
-    // Pointers for parsing
-    char *p = resp, *lpos = resp, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
-
-    // Key length, done flag
-    int klen = 0, done = 0, is_numeric;
+    p = resp;
+	lpos = resp;
 
     // While we've got more to parse
     while(!done) {

From 1fdefc27558b55c20b7023f0631f8fcb7e56f9d9 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 11:45:41 +0200
Subject: [PATCH 0144/1986] atoll vs _atoi64

---
 library.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/library.c b/library.c
index 312ec38a4f..1aa5954a22 100644
--- a/library.c
+++ b/library.c
@@ -906,7 +906,11 @@ PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
     }
 
     if(response[0] == ':') {
+#ifdef PHP_WIN32
+        __int64 ret = _atoi64(response + 1);
+#else
         long long ret = atoll(response + 1);
+#endif
         IF_MULTI_OR_PIPELINE() {
 			if(ret > LONG_MAX) { /* overflow */
 				add_next_index_stringl(z_tab, response+1, response_len-1, 1);

From 7194e31fe9591f8aad81812a2ba6ead49c05eb15 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 11:50:36 +0200
Subject: [PATCH 0145/1986] fix stdint usage

---
 redis_array.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/redis_array.h b/redis_array.h
index 1be2813af7..c3670aa56b 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -1,7 +1,11 @@
 #ifndef REDIS_ARRAY_H
 #define REDIS_ARRAY_H
 
+#ifdef PHP_WIN32
+#include "win32/php_stdint.h"
+#else
 #include 
+#endif
 #include "common.h"
 
 void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC);

From 3f9d0f7192bc3dcbefd89d1ba5d47f0f0456961a Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 11:58:35 +0200
Subject: [PATCH 0146/1986] more C89 compat

---
 library.h |  5 +++--
 redis.c   | 11 +++++++----
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/library.h b/library.h
index 666d6addca..ab6796d2e1 100644
--- a/library.h
+++ b/library.h
@@ -67,11 +67,12 @@ PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red
 PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
 
 #if ZEND_MODULE_API_NO >= 20100000
-#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
+#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) do { \
 	char dbl_decsep; \
 	dbl_decsep = '.'; \
     dbl_str = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \
-	dbl_len = strlen(dbl_str);
+	dbl_len = strlen(dbl_str); \
+	} while (0);
 #else
 #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
 	dbl_str = _php_math_number_format(dbl, 16, '.', '\x00'); \
diff --git a/redis.c b/redis.c
index 8c12657ae8..6747b4ba70 100644
--- a/redis.c
+++ b/redis.c
@@ -585,12 +585,13 @@ PHP_METHOD(Redis, __construct)
     Public Destructor
  */
 PHP_METHOD(Redis,__destruct) {
+	RedisSock *redis_sock;
+
 	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) {
 		RETURN_FALSE;
 	}
 
 	// Grab our socket
-	RedisSock *redis_sock;
 	if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 1) < 0) {
 		RETURN_FALSE;
 	}
@@ -3898,6 +3899,8 @@ PHP_METHOD(Redis, zAdd) {
 	zval **z_args;
 	int argc = ZEND_NUM_ARGS(), i;
 
+    smart_str buf = {0};
+
 	/* get redis socket */
     if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
@@ -3924,7 +3927,6 @@ PHP_METHOD(Redis, zAdd) {
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 
 	/* start building the command */
-    smart_str buf = {0};
 	smart_str_appendc(&buf, '*');
 	smart_str_append_long(&buf, argc + 1); /* +1 for ZADD command */
 	smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
@@ -4471,6 +4473,7 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
     HashPosition ptr;
     char *store_key, *agg_op = NULL;
     int cmd_arg_count = 2, store_key_len, agg_op_len = 0, keys_count;
+	int key_free;
 
     // Grab our parameters
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s",
@@ -4529,7 +4532,7 @@ PHPAPI void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int c
     redis_cmd_init_sstr(&cmd, cmd_arg_count, command, command_len);
 
     // Prefix our key if necessary and add the output key
-    int key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC);
+    key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC);
     redis_cmd_append_sstr(&cmd, store_key, store_key_len);
     if(key_free) efree(store_key);
 
@@ -5630,9 +5633,9 @@ PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd)
 	/* Multibulk Response, format : {message type, originating channel, message payload} */
 	while(1) {		
 		/* call the callback with this z_tab in argument */
+	    int is_pmsg, tab_idx = 1;
 		zval **type, **channel, **pattern, **data;
 	    z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
-	    int is_pmsg, tab_idx = 1;
 		
 		if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) {
 			//ERROR

From 0303e1594b4207ed96a63291ea6b2075af0e6c16 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 12:01:27 +0200
Subject: [PATCH 0147/1986] C89 compat for session

---
 redis_session.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/redis_session.c b/redis_session.c
index 591f07b84e..e3a3c6e089 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -152,11 +152,11 @@ PHPAPI redis_pool_member *
 redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) {
 
 	unsigned int pos, i;
+	redis_pool_member *rpm = pool->head;
+
 	memcpy(&pos, key, sizeof(pos));
 	pos %= pool->totalWeight;
 
-	redis_pool_member *rpm = pool->head;
-
 	for(i = 0; i < pool->totalWeight;) {
 		if(pos >= i && pos < i + rpm->weight) {
 			int needs_auth = 0;
@@ -208,6 +208,7 @@ PS_OPEN_FUNC(redis)
             int database = -1;
             char *prefix = NULL, *auth = NULL, *persistent_id = NULL;
       long retry_interval = 0;
+			RedisSock *redis_sock;
 
             /* translate unix: into file: */
 			if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) {
@@ -276,7 +277,6 @@ PS_OPEN_FUNC(redis)
 				return FAILURE;
 			}
 
-			RedisSock *redis_sock;
             if(url->host) {
                     redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval, 0);
             } else { /* unix */

From 6c377eee18a5a36df71da2498eddbc0cd4f1ba76 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 12:19:01 +0200
Subject: [PATCH 0148/1986] added redis array stuff to win part done some fixes
 to it

---
 config.w32         | 2 +-
 redis_array.c      | 9 +++++----
 redis_array_impl.c | 7 +++++--
 redis_array_impl.h | 5 +++++
 4 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/config.w32 b/config.w32
index 9600f68e3e..b8f99d32c2 100644
--- a/config.w32
+++ b/config.w32
@@ -5,7 +5,7 @@ ARG_ENABLE("redis-session", "whether to enable sessions", "yes");
 ARG_ENABLE("redis-igbinary", "whether to enable igbinary support", "no");
 
 if (PHP_REDIS != "no") {
-	var sources = "redis.c library.c"
+	var sources = "redis.c library.c redis_array.c redis_array_impl.c"
 	
 	if (PHP_REDIS_IGBINARY != "no") {
 		sources += " igbinary\\igbinary.c igbinary\\hash_si.c igbinary\\hash_function.c";
diff --git a/redis_array.c b/redis_array.c
index 36254df04f..fc9ded5d54 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -219,6 +219,8 @@ PHP_METHOD(RedisArray, __construct)
 
 	/* extract options */
 	if(z_opts) {
+		zval **z_retry_interval_pp;
+		zval **z_connect_timeout_pp;
 
 		hOpts = Z_ARRVAL_P(z_opts);
 
@@ -259,7 +261,6 @@ PHP_METHOD(RedisArray, __construct)
 		}
 
 		/* extract retry_interval option. */
-		zval **z_retry_interval_pp;
         if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) {
 			if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) {
 				if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) {
@@ -277,7 +278,6 @@ PHP_METHOD(RedisArray, __construct)
 		}
 		
 		/* extract connect_timeout option */
-		zval **z_connect_timeout_pp;
 		if (FAILURE != zend_hash_find(hOpts, "connect_timeout", sizeof("connect_timeout"), (void**)&z_connect_timeout_pp)) {
 			if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE || Z_TYPE_PP(z_connect_timeout_pp) == IS_STRING) {
 				if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE) {
@@ -1045,19 +1045,20 @@ PHP_METHOD(RedisArray, mset)
 
 	/* calls */
 	for(n = 0; n < ra->count; ++n) { /* for each node */
+		int found = 0;
 
 		/* prepare call */
 		ZVAL_STRING(&z_fun, "MSET", 0);
 		redis_inst = ra->redis[n];
 
 		/* copy args */
-		int found = 0;
 		MAKE_STD_ZVAL(z_argarray);
 		array_init(z_argarray);
 		for(i = 0; i < argc; ++i) {
+			zval *z_tmp;
+
 			if(pos[i] != n) continue;
 
-			zval *z_tmp;
 			ALLOC_ZVAL(z_tmp);
 			*z_tmp = *argv[i];
 			zval_copy_ctor(z_tmp);
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 615f503bc0..eb09362fd7 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -469,12 +469,14 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D
                 }
         }
         else {
+                uint64_t h64;
+
                 /* hash */
                 hash = rcrc32(out, out_len);
                 efree(out);
         
                 /* get position on ring */
-                uint64_t h64 = hash;
+                h64 = hash;
                 h64 *= ra->count;
                 h64 /= 0xffffffff;
                 pos = (int)h64;
@@ -569,13 +571,14 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) {
 
 	/* Initialize key array */
 	zval *z_keys, **z_entry_pp;
+	HashPosition pos;
+
 	MAKE_STD_ZVAL(z_keys);
 #if PHP_VERSION_ID > 50300
 	array_init_size(z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs)));
 #else
 	array_init(z_keys);
 #endif
-	HashPosition pos;
 
 	/* Go through input array and add values to the key array */
 	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z_pairs), &pos);
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 4c2a1b05bd..d12844daa5 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -1,7 +1,12 @@
 #ifndef REDIS_ARRAY_IMPL_H
 #define REDIS_ARRAY_IMPL_H
 
+#ifdef PHP_WIN32
+#include "win32/php_stdint.h"
+#else
 #include 
+#endif
+#include "common.h"
 #include "common.h"
 #include "redis_array.h"
 

From 3880c9eb9e20327823f49ad5c7004f573d03a7e6 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 12:37:11 +0200
Subject: [PATCH 0149/1986] fix CREDITS for resource compilation to work

see also CREDITS in other exts for the correct format
---
 CREDITS | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/CREDITS b/CREDITS
index 6c8626a8a6..f6d6cf885f 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1,5 +1,2 @@
 Redis client extension for PHP
-Alfonso Jimenez 
-Nasreddine Bouafif 
-Nicolas Favre-Felix 
-Michael Grunder 
+Alfonso Jimenez, Nasreddine Bouafif, Nicolas Favre-Felix, Michael Grunder

From 48138096da71018cae6b7690810790a83b2b20b7 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 14:00:36 +0200
Subject: [PATCH 0150/1986] reworked config.w32

---
 config.w32 | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/config.w32 b/config.w32
index b8f99d32c2..5f125fda58 100644
--- a/config.w32
+++ b/config.w32
@@ -2,20 +2,30 @@
 
 ARG_ENABLE("redis", "whether to enable redis support", "yes");
 ARG_ENABLE("redis-session", "whether to enable sessions", "yes");
-ARG_ENABLE("redis-igbinary", "whether to enable igbinary support", "no");
+ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no");
 
 if (PHP_REDIS != "no") {
-	var sources = "redis.c library.c redis_array.c redis_array_impl.c"
-	
-	if (PHP_REDIS_IGBINARY != "no") {
-		sources += " igbinary\\igbinary.c igbinary\\hash_si.c igbinary\\hash_function.c";
-	}
+	EXTENSION("redis", "redis.c library.c redis_array_impl.c redis_array.c");
+	AC_DEFINE("HAVE_REDIS", 1);
+	ADD_FLAG("CFLAGS_REDIS", ' /D PHP_EXPORTS=1 ');
 
 	if (PHP_REDIS_SESSION != "no") {
-		AC_DEFINE('PHP_SESSION', 1);
-		sources += " redis_session.c";
+		ADD_SOURCES(configure_module_dirname, "redis_session.c", "redis");
+		ADD_EXTENSION_DEP("redis", "session");
+		ADD_FLAG("CFLAGS_REDIS", ' /D PHP_SESSION=1 ');
+		AC_DEFINE("HAVE_REDIS_SESSION", 1);
+	}
+
+	if (PHP_REDIS_IGBINARY != "no") {
+		if (CHECK_HEADER_ADD_INCLUDE("igbinary.h", "CFLAGS_REDIS", configure_module_dirname + "\\..\\igbinary")) {
+
+			ADD_EXTENSION_DEP("redis", "igbinary");
+			AC_DEFINE("HAVE_REDIS_IGBINARY", 1);
+		} else {
+			WARNING("redis igbinary support not enabled");
+		}
 	}
 	
-	AC_DEFINE("PHP_EXPORTS", 1);
-	EXTENSION("redis", sources);
+
 }
+

From 79efa8d791ac2db33a7eb29e51813faacdb7bb21 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 16:50:23 +0200
Subject: [PATCH 0151/1986] fixes after the merge

---
 redis.c         |  2 --
 redis_array.c   | 11 -----------
 redis_session.c |  1 -
 3 files changed, 14 deletions(-)

diff --git a/redis.c b/redis.c
index 956303d4d4..3fc8a62987 100644
--- a/redis.c
+++ b/redis.c
@@ -3903,8 +3903,6 @@ PHP_METHOD(Redis, zAdd) {
 	zval **z_args;
 	int argc = ZEND_NUM_ARGS(), i;
 
-    smart_str buf = {0};
-
 	/* get redis socket */
     if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
diff --git a/redis_array.c b/redis_array.c
index 02412ba6c9..1d4b6c92d3 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -126,17 +126,6 @@ void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
     redis_array_free(ra);
 }
 
-int le_redis_array;
-void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
-{
-    RedisArray *ra = (RedisArray*)rsrc->ptr;
-
-    /* Free previous ring if it's set */
-    if(ra->prev) redis_array_free(ra->prev);
-
-    /* Free parent array */
-    redis_array_free(ra);
-}
 
 /**
  * redis_array_get
diff --git a/redis_session.c b/redis_session.c
index dc4e94e2d7..1189d25a49 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -152,7 +152,6 @@ PHP_REDIS_API redis_pool_member *
 redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) {
 	redis_pool_member *rpm = pool->head;
 	unsigned int pos, i;
-	redis_pool_member *rpm = pool->head;
 
 	memcpy(&pos, key, sizeof(pos));
 	pos %= pool->totalWeight;

From cdd4557f014a82c7b7ce3a841654ab4cfa0f12f2 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Tue, 1 Jul 2014 16:59:41 +0200
Subject: [PATCH 0152/1986] fix unreferenced variable warnings

---
 library.c          | 2 --
 redis.c            | 2 --
 redis_array.c      | 4 +---
 redis_array_impl.c | 1 -
 redis_session.c    | 1 -
 5 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/library.c b/library.c
index 1b52e52c1a..a655b66b89 100644
--- a/library.c
+++ b/library.c
@@ -360,7 +360,6 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) {
     smart_str buf = {0};
     int l = strlen(keyword);
 	char *dbl_str;
-    char dbl_decsep;
 	int dbl_len;
 
 	va_start(ap, format);
@@ -442,7 +441,6 @@ redis_cmd_format(char **ret, char *format, ...) {
 	va_list ap;
 	char *p = format;
 	char *dbl_str;
-    char dbl_decsep;
 	int dbl_len;
 
 	va_start(ap, format);
diff --git a/redis.c b/redis.c
index 3fc8a62987..15ac9bd057 100644
--- a/redis.c
+++ b/redis.c
@@ -3896,7 +3896,6 @@ PHP_METHOD(Redis, zAdd) {
     char *key, *val;
     int val_free, key_free = 0;
 	char *dbl_str;
-    char dbl_decsep;
 	int dbl_len;
     smart_str buf = {0};
 
@@ -5545,7 +5544,6 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
     int cmd_len, array_count, key_len, key_free;
 	zval *z_tab, **tmp;
 	char *type_response;
-    int is_pmsg, tab_idx;
 
 	/* Function call information */
 	zend_fcall_info z_callback;
diff --git a/redis_array.c b/redis_array.c
index 1d4b6c92d3..899ba718ee 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -985,8 +985,6 @@ PHP_METHOD(RedisArray, mset)
 	unsigned int key_len, free_idx = 0;
 	int type, *key_lens;
 	unsigned long idx;
-	int found;
-	zval *z_tmp;
 
 	/* Multi/exec support */
 	HANDLE_MULTI_EXEC("MSET");
@@ -1006,7 +1004,7 @@ PHP_METHOD(RedisArray, mset)
 	argv = emalloc(argc * sizeof(zval*));
 	pos = emalloc(argc * sizeof(int));
 	keys = emalloc(argc * sizeof(char*));
-    key_lens = emalloc(argc * sizeof(int));
+	key_lens = emalloc(argc * sizeof(int));
 	redis_instances = emalloc(argc * sizeof(zval*));
 	memset(redis_instances, 0, argc * sizeof(zval*));
 
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 6a38733970..0776d57053 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -455,7 +455,6 @@ zval *
 ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) {
 
         uint32_t hash;
-        uint64_t h64;
         char *out;
         int pos, out_len;
 
diff --git a/redis_session.c b/redis_session.c
index 1189d25a49..a6b15b59c2 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -187,7 +187,6 @@ PS_OPEN_FUNC(redis)
 	php_url *url;
 	zval *params, **param;
 	int i, j, path_len;
-	RedisSock *redis_sock;
 
 	redis_pool *pool = redis_pool_new(TSRMLS_C);
 

From e223916eb459779094938f06c97d358ca1e96c58 Mon Sep 17 00:00:00 2001
From: Anatol Belski 
Date: Fri, 4 Jul 2014 10:05:54 +0200
Subject: [PATCH 0153/1986] cleanup config.w32

---
 config.w32 | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/config.w32 b/config.w32
index 573a1ebd03..c368d10cbb 100644
--- a/config.w32
+++ b/config.w32
@@ -23,12 +23,6 @@ if (PHP_REDIS != "no") {
 		}
 	}
 
-	if (PHP_REDIS_IGBINARY != "no") {
-		CHECK_HEADER_ADD_INCLUDE("igbinary.h", "CFLAGS_REDIS", "ext\\igbinary");
-		AC_DEFINE('HAVE_REDIS_IGBINARY', 1);
-		ADD_EXTENSION_DEP('redis', 'igbinary');
-	}
-
 	EXTENSION("redis", sources);
 }
 

From 7a80c10905fb7727599671da025248cd3542d77a Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 6 Jul 2014 10:58:22 -0700
Subject: [PATCH 0154/1986] Remove duplicate pubsub test

---
 tests/TestRedis.php | 41 -----------------------------------------
 1 file changed, 41 deletions(-)

diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 458b4bf584..9fe4e039f5 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -113,47 +113,6 @@ public function testPubSub() {
         $this->assertFalse($this->redis->pubsub("numsub", "not-an-array"));
     }
 
-    // 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", "lt")) {
-            $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", Array($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(Array($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->delete('key');

From fb10a14b7c92c710b83059d53413e305afff4798 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 6 Jul 2014 10:59:21 -0700
Subject: [PATCH 0155/1986] config.h shouldn't be in the repo

---
 config.h | 69 --------------------------------------------------------
 1 file changed, 69 deletions(-)
 delete mode 100644 config.h

diff --git a/config.h b/config.h
deleted file mode 100644
index 00c355bdcd..0000000000
--- a/config.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.in by autoheader.  */
-
-/* Whether to build redis as dynamic module */
-#define COMPILE_DL_REDIS 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_DLFCN_H 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_MEMORY_H 1
-
-/* Whether redis igbinary serializer is enabled */
-/* #undef HAVE_REDIS_IGBINARY */
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the  header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
-   */
-#define LT_OBJDIR ".libs/"
-
-/* Define to 1 if your C compiler doesn't accept -c and -o together. */
-/* #undef NO_MINUS_C_MINUS_O */
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT ""
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME ""
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING ""
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME ""
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION ""
-
-/* redis sessions */
-#define PHP_SESSION 1
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1

From ba4a21a5d52868b0ddfb31dd39874d9099b97749 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 6 Jul 2014 11:07:41 -0700
Subject: [PATCH 0156/1986] Fix compiler error

---
 redis.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/redis.c b/redis.c
index 23f2f279a0..fb5bf25f29 100644
--- a/redis.c
+++ b/redis.c
@@ -6694,7 +6694,7 @@ PHP_METHOD(Redis, _serialize) {
     RedisSock *redis_sock;
     zval *z_val;
     char *val;
-    int val_len;
+    int val_len, val_free;
 
     /* Parse arguments */
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
@@ -6709,7 +6709,7 @@ PHP_METHOD(Redis, _serialize) {
     }
 
     /* Serialize, which will return a value even if no serializer is set */
-    redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
+    val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
 
     /* Return serialized value.  Tell PHP to make a copy as some can be interned. */
     RETVAL_STRINGL(val, val_len, 1);

From 96374a91051ded6275885dd2459f0c7bd5d300fe Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 21 Jul 2014 12:50:29 -0700
Subject: [PATCH 0157/1986] Implement DEBUG OBJECT

Addresses #342
---
 library.c           | 61 +++++++++++++++++++++++++++++++++++++++++++++
 library.h           |  1 +
 php_redis.h         |  1 +
 redis.c             | 32 ++++++++++++++++++++++++
 tests/TestRedis.php |  6 +++++
 5 files changed, 101 insertions(+)

diff --git a/library.c b/library.c
index a655b66b89..40d43fa2bf 100644
--- a/library.c
+++ b/library.c
@@ -1089,6 +1089,67 @@ PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
 	}
 }
 
+/* Response for DEBUG object which is a formatted single line reply */
+PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, 
+                                        zval *z_tab, void *ctx)
+{
+    char *resp, *p, *p2, *p3, *p4;
+    int is_numeric,  resp_len;
+    zval *z_result;
+
+    /* Add or return false if we can't read from the socket */
+    if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC))==NULL) {
+        IF_MULTI_OR_PIPELINE() {
+            add_next_index_bool(z_tab, 0);
+            return;
+        }
+        RETURN_FALSE;
+    }
+
+    MAKE_STD_ZVAL(z_result);
+    array_init(z_result);
+
+    /* Skip the '+' */
+    p = resp + 1;
+
+    /* :  ... */
+    while((p2 = strchr(p, ':'))!=NULL) {
+        /* Null terminate at the ':' */
+        *p2++ = '\0';
+       
+        /* Null terminate at the space if we have one */
+        if((p3 = strchr(p2, ' '))!=NULL) {
+            *p3++ = '\0';
+        } else {
+            p3 = resp + resp_len;
+        }
+
+        is_numeric = 1;
+        for(p4=p2; *p4; ++p4) {
+            if(*p4 < '0' || *p4 > '9') {
+                is_numeric = 0;
+                break;
+            }
+        }
+
+        /* Add our value */
+        if(is_numeric) {
+            add_assoc_long(z_result, p, atol(p2));
+        } else {
+            add_assoc_string(z_result, p, p2, 1);
+        }
+   
+        p = p3;
+    }
+
+    efree(resp);
+
+    IF_MULTI_OR_PIPELINE() {
+        add_next_index_zval(z_tab, z_result);
+    } else {
+        RETVAL_ZVAL(z_result, 0, 1);
+    }
+}
 
 /**
  * redis_sock_create
diff --git a/library.h b/library.h
index 22e1d62d2f..652db6f407 100644
--- a/library.h
+++ b/library.h
@@ -21,6 +21,7 @@ PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSoc
 PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect);
diff --git a/php_redis.h b/php_redis.h
index ccc2e724ef..e6ec1ef2f8 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -134,6 +134,7 @@ PHP_METHOD(Redis, bitpos);
 PHP_METHOD(Redis, eval);
 PHP_METHOD(Redis, evalsha);
 PHP_METHOD(Redis, script);
+PHP_METHOD(Redis, debug);
 PHP_METHOD(Redis, dump);
 PHP_METHOD(Redis, restore);
 PHP_METHOD(Redis, migrate);
diff --git a/redis.c b/redis.c
index 15ac9bd057..3591aba1c6 100644
--- a/redis.c
+++ b/redis.c
@@ -240,6 +240,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, evalsha, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, script, NULL, ZEND_ACC_PUBLIC)
 
+     PHP_ME(Redis, debug, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, dump, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, restore, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, migrate, NULL, ZEND_ACC_PUBLIC)
@@ -6564,6 +6565,37 @@ PHP_METHOD(Redis, dump) {
 	REDIS_PROCESS_RESPONSE(redis_ping_response);
 }
 
+/* {{{ proto Redis::DEBUG(string key) */
+PHP_METHOD(Redis, debug) {
+    zval *object;
+    RedisSock *redis_sock;
+    char *cmd, *key;
+    int cmd_len, key_len, key_free;
+
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", 
+                                    &object, redis_ce, &key, &key_len)==FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    /* Grab our socket */
+    if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) {
+        RETURN_FALSE;
+    }
+
+    /* Prefix key, format command */
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+    cmd_len = redis_cmd_format_static(&cmd, "DEBUG", "ss", "OBJECT", sizeof("OBJECT")-1, key, key_len);
+    if(key_free) efree(key);
+
+    /* Kick it off */
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        redis_debug_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_debug_response);
+}
+
 /*
  * {{{ proto Redis::restore(ttl, key, value)
  */
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 9fe4e039f5..9bbef4daca 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -4237,6 +4237,12 @@ private function checkSerializer($mode) {
 	    $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE);		// get ok
     }
 
+    public function testDebug() {
+        $this->redis->set('foo', 0);
+        $arr_info = $this->redis->debug('foo');
+        $this->assertTrue(isset($arr_info['encoding']) && $arr_info['encoding']=='int');
+    }
+
     public function testDumpRestore() {
 
 		if (version_compare($this->version, "2.5.0", "lt")) {

From 78f4f50c2defb2e71f14ead3f92177d3ebf4b7b1 Mon Sep 17 00:00:00 2001
From: iSage 
Date: Mon, 4 Aug 2014 11:59:51 +0400
Subject: [PATCH 0158/1986] Allow to break out of subscribe loop

---
 redis.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/redis.c b/redis.c
index c9fe454707..9b36096c6a 100644
--- a/redis.c
+++ b/redis.c
@@ -5699,6 +5699,14 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
 		}
 
 		/* If we have a return value, free it.  Note, we could use the return value to break the subscribe loop */
+		if (z_ret) {
+			if(Z_TYPE_P(z_ret) == IS_BOOL && Z_BVAL_P(z_ret) == 0) {
+				zval_ptr_dtor(&z_ret);
+				zval_dtor(z_tab);
+				efree(z_tab);
+				break;
+			}
+		}
 		if(z_ret) zval_ptr_dtor(&z_ret);
 
         /* TODO: provide a way to break out of the loop. */

From 273dd8fcadebdae011aa494769da29de17515302 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 4 Aug 2014 08:43:19 -0700
Subject: [PATCH 0159/1986] Break out of subscribe loop

* Modified @isage pull request slightly for OCD reasons.
* Fixed an old memory leak in subscribe.

Related to #493
---
 redis.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/redis.c b/redis.c
index 9b36096c6a..1d0cd640c2 100644
--- a/redis.c
+++ b/redis.c
@@ -5622,15 +5622,18 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
 	if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&tmp) == SUCCESS) {
 		type_response = Z_STRVAL_PP(tmp);
 		if(strcmp(type_response, sub_cmd) != 0) {
-			efree(tmp);
-			efree(z_tab);	
+            efree(tmp);
+			zval_dtor(z_tab);
+            efree(z_tab);	
 			RETURN_FALSE;
 		} 
 	} else {
-		efree(z_tab);	
+		zval_dtor(z_tab);
+        efree(z_tab);	
 		RETURN_FALSE;
 	}
-	efree(z_tab);	
+	zval_dtor(z_tab);
+    efree(z_tab);	
 
 	/* Set a pointer to our return value and to our arguments. */
 	z_callback.retval_ptr_ptr = &z_ret;
@@ -5698,18 +5701,19 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
 			break;
 		}
 
-		/* If we have a return value, free it.  Note, we could use the return value to break the subscribe loop */
+        /* Free our return value if we have one.  If the return value is a bool
+         * that is FALSE, break our subscribe loop and return control to the
+         * userland code */
 		if (z_ret) {
 			if(Z_TYPE_P(z_ret) == IS_BOOL && Z_BVAL_P(z_ret) == 0) {
-				zval_ptr_dtor(&z_ret);
+                zval_ptr_dtor(&z_ret);
 				zval_dtor(z_tab);
 				efree(z_tab);
 				break;
 			}
+            zval_ptr_dtor(&z_ret);
 		}
-		if(z_ret) zval_ptr_dtor(&z_ret);
 
-        /* TODO: provide a way to break out of the loop. */
 		zval_dtor(z_tab);
 		efree(z_tab);
 	}

From b1ad5435ff4d8a7d53c747c04f81678a1aa05ce6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 30 Aug 2014 11:45:09 -0700
Subject: [PATCH 0160/1986] ZRANGEBYLEX command

This commit adds the command ZRANGEBYLEX to phpredis, which was
introduced in 2.8.9.  Like with most commands, phpredis will do
some simple validation on the client side, to avoid sending
calls which are not correct (e.g. min/max that aren't valid
for the call, etc).

Addresses #498 and #465
---
 README.markdown     |  25 ++++++
 common.h            |   9 ++-
 library.c           |   1 +
 php_redis.h         |   1 +
 redis.c             | 192 +++++++++++++++++++++++++++++---------------
 tests/TestRedis.php |  69 ++++++++++++----
 tests/test.php      |  15 +++-
 7 files changed, 224 insertions(+), 88 deletions(-)

diff --git a/README.markdown b/README.markdown
index fbf7d7425b..cb32804b2b 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2488,6 +2488,7 @@ while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) {
 * [zInter](#zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key
 * [zRange](#zrange) - Return a range of members in a sorted set, by index
 * [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score
+* [zRangeByLex](#zrangebylex) - Return a lexigraphical range from members that share the same score
 * [zRank, zRevRank](#zrank-zrevrank) - Determine the index of a member in a sorted set
 * [zRem, zDelete](#zrem-zdelete) - Remove one or more members from a sorted set
 * [zRemRangeByRank, zDeleteRangeByRank](#zremrangebyrank-zdeleterangebyrank) - Remove all members in a sorted set within the given indexes
@@ -2671,6 +2672,30 @@ $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2
 $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1)); /* array('val2' => 2) */
 ~~~
 
+### zRangeByLex
+-----
+_**Description**_:  Returns a lexigraphical range of members in a sorted set, assuming the members have the same score.  The min and max values are required to start with '(' (exclusive), '[' (inclusive), or be exactly the values '-' (negative inf) or '+' (positive inf).  The command must be called with either three *or* five arguments or will return FALSE.
+
+##### *Parameters*
+*key*: The ZSET you wish to run against
+*min*: The minimum alphanumeric value you wish to get
+*max*: The maximum alphanumeric value you wish to get
+*offset*:  Optional argument if you wish to start somewhere other than the first element.
+*limit*: Optional argument if you wish to limit the number of elements returned.
+
+##### *Return value*
+*Array* containing the values in the specified range.
+
+##### *Example*
+~~~
+foreach(Array('a','b','c','d','e','f','g') as $c)
+    $redis->zAdd('key',0,$c);
+
+$redis->zRangeByLex('key','-','[c') /* Array('a','b','c'); */
+$redis->zRangeByLex('key','-','(c') /* Array('a','b') */
+$redis->zRangeByLex('key','-','[c',1,2) /* Array('b','c') */
+~~~
+
 ### zRank, zRevRank
 -----
 _**Description**_: Returns the rank of a given member in the specified sorted set, starting at 0 for the item with the smallest score. zRevRank starts at 0 for the item with the *largest* score.
diff --git a/common.h b/common.h
index a0623fbdc7..a46d006a3f 100644
--- a/common.h
+++ b/common.h
@@ -30,7 +30,8 @@
 
 /* reply types */
 typedef enum _REDIS_REPLY_TYPE {
-	TYPE_LINE      = '+',
+    TYPE_EOF       = EOF,
+    TYPE_LINE      = '+',
 	TYPE_INT       = ':',
 	TYPE_ERR       = '-',
 	TYPE_BULK      = '$',
@@ -178,6 +179,12 @@ else if(redis_sock->mode == MULTI) { \
 #define IS_EX_PX_ARG(a) (IS_EX_ARG(a) || IS_PX_ARG(a))
 #define IS_NX_XX_ARG(a) (IS_NX_ARG(a) || IS_XX_ARG(a))
 
+/* Given a string and length, validate a zRangeByLex argument.  The semantics
+ * here are that the argument must start with '(' or '[' or be just the char
+ * '+' or '-' */
+#define IS_LEX_ARG(s,l) \
+    (l>0 && (*s=='(' || *s=='[' || (l==1 && (*s=='+' || *s=='-'))))
+
 typedef enum {ATOMIC, MULTI, PIPELINE} redis_mode;
 
 typedef struct fold_item {
diff --git a/library.c b/library.c
index 40d43fa2bf..3c67a2d036 100644
--- a/library.c
+++ b/library.c
@@ -1911,6 +1911,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret
 				add_next_index_zval(*z_ret, z_subelem);
 				redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC);
 				break;
+            default: break; /* We know it's not < 0 from previous check */
 		}
 
 		/* Decrement our element counter */
diff --git a/php_redis.h b/php_redis.h
index 437d0df8ae..23e263109a 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -111,6 +111,7 @@ PHP_METHOD(Redis, zDelete);
 PHP_METHOD(Redis, zRange);
 PHP_METHOD(Redis, zReverseRange);
 PHP_METHOD(Redis, zRangeByScore);
+PHP_METHOD(Redis, zRangeByLex);
 PHP_METHOD(Redis, zRevRangeByScore);
 PHP_METHOD(Redis, zCount);
 PHP_METHOD(Redis, zDeleteRangeByScore);
diff --git a/redis.c b/redis.c
index 1d0cd640c2..02c0eb2129 100644
--- a/redis.c
+++ b/redis.c
@@ -192,6 +192,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, zReverseRange, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zRangeByScore, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zRevRangeByScore, NULL, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, zRangeByLex, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zCount, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC)
@@ -316,8 +317,8 @@ static zend_function_entry redis_functions[] = {
      PHP_MALIAS(Redis, srem, sRemove, NULL, ZEND_ACC_PUBLIC)
      PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC)
      PHP_MALIAS(Redis, zrevrange, zReverseRange, NULL, ZEND_ACC_PUBLIC)
-     
-     PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC) 
+
+     PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC)
 
      PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC)
      PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC)
@@ -476,7 +477,7 @@ PHP_MINIT_FUNCTION(redis)
     zend_class_entry redis_exception_class_entry;
 
 	REGISTER_INI_ENTRIES();
-    
+
 	/* Redis class */
 	INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions);
     redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC);
@@ -652,7 +653,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
 
 	char *persistent_id = NULL;
 	int persistent_id_len = -1;
-	
+
 	double timeout = 0.0;
 	RedisSock *redis_sock  = NULL;
 
@@ -832,7 +833,7 @@ PHP_METHOD(Redis, bitcount)
 /* }}} */
 
 /* {{{ proto integer Redis::bitpos(string key, int bit, [int start], [int end]) */
-PHP_METHOD(Redis, bitpos) 
+PHP_METHOD(Redis, bitpos)
 {
     zval *object;
     RedisSock *redis_sock;
@@ -934,7 +935,7 @@ PHP_METHOD(Redis, set) {
     /* Our optional argument can either be a long (to support legacy SETEX */
     /* redirection), or an array with Redis >= 2.6.12 set options */
     if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY
-       && Z_TYPE_P(z_opts) != IS_NULL) 
+       && Z_TYPE_P(z_opts) != IS_NULL)
     {
         RETURN_FALSE;
     }
@@ -1430,7 +1431,7 @@ PHP_METHOD(Redis, incrByFloat) {
 	/* Prefix key, format command, free old key if necissary */
 	key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
 	cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val);
-	if(key_free) efree(key);	
+	if(key_free) efree(key);
 
 	REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
 	IF_ATOMIC() {
@@ -1992,11 +1993,11 @@ PHP_METHOD(Redis, lInsert)
 	int pivot_len, position_len, key_len, val_len, cmd_len;
     int val_free, pivot_free, key_free;
     zval *z_value, *z_pivot;
-	
+
 
 	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osszz",
 					&object, redis_ce,
-					&key, &key_len, 
+					&key, &key_len,
 					&position, &position_len,
 					&z_pivot,
 					&z_value) == FAILURE) {
@@ -2006,7 +2007,7 @@ PHP_METHOD(Redis, lInsert)
 	if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
 		RETURN_FALSE;
 	}
-	
+
 	if(strncasecmp(position, "after", 5) == 0 || strncasecmp(position, "before", 6) == 0) {
 
 		key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
@@ -2017,15 +2018,15 @@ PHP_METHOD(Redis, lInsert)
 		if(key_free) efree(key);
         if(pivot_free) STR_FREE(pivot);
 
-		REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); 
-		IF_ATOMIC() { 
-			redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); 
-		} 
-		REDIS_PROCESS_RESPONSE(redis_long_response); 
+		REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+		IF_ATOMIC() {
+			redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+		}
+		REDIS_PROCESS_RESPONSE(redis_long_response);
 	} else {
 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error on position");
 	}
-	
+
 }
 
 PHP_METHOD(Redis, lPushx)
@@ -2669,7 +2670,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
 		if(has_timeout && Z_TYPE_P(z_args[argc - 1]) != IS_LONG) {
 			php_error_docref(NULL TSRMLS_CC, E_ERROR, "Syntax error on timeout");
 		}
-			
+
         for(i = 0, j = 0; i < argc; ++i) { /* store each key */
 			if(!all_keys && j != 0) { /* not just operating on keys */
 
@@ -2683,7 +2684,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
 				}
 
 			} else {
-				
+
 				if(Z_TYPE_P(z_args[i]) != IS_STRING) {
 					convert_to_string(z_args[i]);
 				}
@@ -2707,7 +2708,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
     cmd = emalloc(cmd_len+1);
 
     sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1+real_argc, keyword_len, keyword);
-	
+
     pos = 1 +integer_length(real_argc + 1) + 2
           + 1 + integer_length(keyword_len) + 2
           + keyword_len + 2;
@@ -4314,6 +4315,65 @@ PHP_METHOD(Redis, zCount)
 }
 /* }}} */
 
+/* {{{ proto array Redis::zRangeByLex(string $key, string $min, string $max,
+ *                                    [long $offset, long $count]) */
+PHP_METHOD(Redis, zRangeByLex) {
+    zval *object;
+    RedisSock *redis_sock;
+    char *cmd, *key, *min, *max;
+    long offset, count;
+    int argc, cmd_len, key_len;
+    int key_free, min_len, max_len;
+
+    /* We need either three or five arguments for this to be a valid call */
+    argc = ZEND_NUM_ARGS();
+    if (argc != 3 && argc != 5) {
+        RETURN_FALSE;
+    }
+
+    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
+                                     "Osss|ll", &object, redis_ce, &key, &key_len,
+                                     &min, &min_len, &max, &max_len, &offset,
+                                     &count) == FAILURE)
+    {
+        RETURN_FALSE;
+    }
+
+    /* We can do some simple validation for the user, as we know how min/max are
+     * required to start */
+    if (!IS_LEX_ARG(min,min_len) || !IS_LEX_ARG(max,max_len)) {
+        RETURN_FALSE;
+    }
+
+    if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
+        RETURN_FALSE;
+    }
+
+    key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
+
+    /* Construct our command depending on argc */
+    if (argc == 3) {
+        cmd_len = redis_cmd_format_static(&cmd, "ZRANGEBYLEX", "sss", key,
+            key_len, min, min_len, max, max_len);
+    } else {
+        cmd_len = redis_cmd_format_static(&cmd, "ZRANGEBYLEX", "ssssll", key,
+            key_len, min, min_len, max, max_len, "LIMIT", sizeof("LIMIT")-1,
+            offset, count);
+    }
+
+    if(key_free) efree(key);
+
+    /* Kick it off */
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    IF_ATOMIC() {
+        if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+                                            redis_sock, NULL, NULL) < 0) {
+            RETURN_FALSE;
+        }
+    }
+    REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
+}
+
 /* {{{ proto long Redis::zCard(string key)
  */
 PHP_METHOD(Redis, zCard)
@@ -4906,7 +4966,7 @@ PHP_REDIS_API void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_ta
 
     zval *z_ret;
 	HashTable *keytable;
-    
+
 	MAKE_STD_ZVAL(z_ret);
     array_init(z_ret);
     keytable = Z_ARRVAL_P(z_tab);
@@ -5081,7 +5141,7 @@ PHP_METHOD(Redis, hMget) {
         /* Make sure the data is a long or string, and if it's a string that */
         /* it isn't empty.  There is no reason to send empty length members. */
         if((Z_TYPE_PP(data) == IS_STRING && Z_STRLEN_PP(data)>0) ||
-            Z_TYPE_PP(data) == IS_LONG) 
+            Z_TYPE_PP(data) == IS_LONG)
         {
             /* This is a key we can ask for, copy it and set it in our array */
             MAKE_STD_ZVAL(z_keys[valid]);
@@ -5166,7 +5226,7 @@ PHP_METHOD(Redis, hMset)
         unsigned long idx;
         int type;
         zval **z_value_p;
-        
+
         char *hval;
         int hval_len, hval_free;
 
@@ -5361,7 +5421,7 @@ free_reply_callbacks(zval *z_this, RedisSock *redis_sock) {
 	fold_item *fi;
     fold_item *head = redis_sock->head;
 	request_item *ri;
-    
+
 	for(fi = head; fi; ) {
         fold_item *fi_next = fi->next;
         free(fi);
@@ -5508,9 +5568,9 @@ PHP_METHOD(Redis, pipeline)
 	RETURN_ZVAL(getThis(), 1, 0);
 }
 
-/* 
-	publish channel message 
-	@return the number of subscribers 
+/*
+	publish channel message
+	@return the number of subscribers
 */
 PHP_METHOD(Redis, publish)
 {
@@ -5556,10 +5616,10 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
 	zend_fcall_info_cache z_callback_cache;
 
 	zval *z_ret, **z_args[4];
-	
+
 	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oaf",
 									 &object, redis_ce, &array, &z_callback, &z_callback_cache) == FAILURE) {
-		RETURN_FALSE;	
+		RETURN_FALSE;
 	}
 
     if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
@@ -5611,9 +5671,9 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
         RETURN_FALSE;
     }
     efree(cmd);
-	
+
 	/* read the status of the execution of the command `subscribe` */
-	
+
     z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
 	if(z_tab == NULL) {
 		RETURN_FALSE;
@@ -5624,16 +5684,16 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
 		if(strcmp(type_response, sub_cmd) != 0) {
             efree(tmp);
 			zval_dtor(z_tab);
-            efree(z_tab);	
+            efree(z_tab);
 			RETURN_FALSE;
-		} 
+		}
 	} else {
 		zval_dtor(z_tab);
-        efree(z_tab);	
+        efree(z_tab);
 		RETURN_FALSE;
 	}
 	zval_dtor(z_tab);
-    efree(z_tab);	
+    efree(z_tab);
 
 	/* Set a pointer to our return value and to our arguments. */
 	z_callback.retval_ptr_ptr = &z_ret;
@@ -5641,12 +5701,12 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub
 	z_callback.no_separation = 0;
 
 	/* Multibulk Response, format : {message type, originating channel, message payload} */
-	while(1) {		
+	while(1) {
 		/* call the callback with this z_tab in argument */
 	    int is_pmsg, tab_idx = 1;
 		zval **type, **channel, **pattern, **data;
 	    z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
-		
+
 		if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) {
 			/*ERROR */
 			break;
@@ -5732,7 +5792,7 @@ PHP_METHOD(Redis, subscribe) {
 	generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "subscribe");
 }
 
-/** 
+/**
  *	[p]unsubscribe channel_0 channel_1 ... channel_n
  *  [p]unsubscribe(array(channel_0, channel_1, ..., channel_n))
  * response format :
@@ -5752,13 +5812,13 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u
     RedisSock *redis_sock;
     char *cmd = "", *old_cmd = NULL;
     int cmd_len, array_count;
-	
+
 	int i;
 	zval *z_tab, **z_channel;
-	
-	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", 
+
+	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
 									 &object, redis_ce, &array) == FAILURE) {
-		RETURN_FALSE;	
+		RETURN_FALSE;
 	}
     if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
         RETURN_FALSE;
@@ -5804,10 +5864,10 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u
 	while( i <= array_count) {
 	    z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
 
-		if(Z_TYPE_P(z_tab) == IS_ARRAY) { 
+		if(Z_TYPE_P(z_tab) == IS_ARRAY) {
 			if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) == FAILURE) {
 				RETURN_FALSE;
-			}		
+			}
 			add_assoc_bool(return_value, Z_STRVAL_PP(z_channel), 1);
 		} else {
 			/*error */
@@ -6127,9 +6187,9 @@ PHP_METHOD(Redis, wait) {
     int cmd_len;
 
     /* Make sure arguments are valid */
-    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", 
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll",
                                     &object, redis_ce, &num_slaves, &timeout)
-                                    ==FAILURE) 
+                                    ==FAILURE)
     {
         RETURN_FALSE;
     }
@@ -6163,7 +6223,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
                        zval *arg TSRMLS_DC)
 {
     HashTable *ht_chan;
-    HashPosition ptr;    
+    HashPosition ptr;
     zval **z_ele;
     char *key;
     int cmd_len, key_len, key_free;
@@ -6193,7 +6253,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
         }
     } else if(type == PUBSUB_NUMSUB) {
         ht_chan = Z_ARRVAL_P(arg);
-        
+
         /* Add PUBSUB and NUMSUB bits */
         redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
         redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
@@ -6262,9 +6322,9 @@ PHP_METHOD(Redis, pubsub) {
     zval *arg=NULL;
 
     /* Parse arguments */
-    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z", 
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z",
                                     &object, redis_ce, &keyword, &kw_len, &arg)
-                                    ==FAILURE) 
+                                    ==FAILURE)
     {
         RETURN_FALSE;
     }
@@ -6279,7 +6339,7 @@ PHP_METHOD(Redis, pubsub) {
     } else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
         /* One array argument */
         if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
-           zend_hash_num_elements(Z_ARRVAL_P(arg))==0) 
+           zend_hash_num_elements(Z_ARRVAL_P(arg))==0)
         {
             RETURN_FALSE;
         }
@@ -6589,7 +6649,7 @@ PHP_METHOD(Redis, debug) {
     char *cmd, *key;
     int cmd_len, key_len, key_free;
 
-    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", 
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
                                     &object, redis_ce, &key, &key_len)==FAILURE)
     {
         RETURN_FALSE;
@@ -6660,7 +6720,7 @@ PHP_METHOD(Redis, migrate) {
 
 	/* Parse arguments */
 	if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll|bb", &object, redis_ce,
-									&host, &host_len, &port, &key, &key_len, &dest_db, &timeout, 
+									&host, &host_len, &port, &key, &key_len, &dest_db, &timeout,
                                     ©, &replace) == FAILURE) {
 		RETURN_FALSE;
 	}
@@ -6675,19 +6735,19 @@ PHP_METHOD(Redis, migrate) {
 
     /* Construct our command */
     if(copy && replace) {
-        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port, 
-                                          key, key_len, dest_db, timeout, "COPY", 
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port,
+                                          key, key_len, dest_db, timeout, "COPY",
                                           sizeof("COPY")-1, "REPLACE", sizeof("REPLACE")-1);
     } else if(copy) {
         cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
-                                          key, key_len, dest_db, timeout, "COPY", 
+                                          key, key_len, dest_db, timeout, "COPY",
                                           sizeof("COPY")-1);
     } else if(replace) {
         cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
                                           key, key_len, dest_db, timeout, "REPLACE",
                                           sizeof("REPLACE")-1);
     } else {
-        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, 
+        cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port,
                                           key, key_len, dest_db, timeout);
     }
 
@@ -7006,8 +7066,8 @@ PHP_METHOD(Redis, client) {
     int cmd_len, opt_len, arg_len;
 
     /* Parse our method parameters */
-    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s", 
-        &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE) 
+    if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s",
+        &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE)
     {
         RETURN_FALSE;
     }
@@ -7020,7 +7080,7 @@ PHP_METHOD(Redis, client) {
     /* Build our CLIENT command */
     if(ZEND_NUM_ARGS() == 2) {
         cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len,
-                                          arg, arg_len); 
+                                          arg, arg_len);
     } else {
         cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
     }
@@ -7207,8 +7267,8 @@ PHP_METHOD(Redis, zscan) {
     generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
 }
 
-/* 
- * HyperLogLog based commands 
+/*
+ * HyperLogLog based commands
  */
 
 /* {{{ proto Redis::pfAdd(string key, array elements) }}} */
@@ -7278,7 +7338,7 @@ PHP_METHOD(Redis, pfadd) {
                 mem_len = Z_STRLEN_P(z_tmp);
             }
         }
-    
+
         // Append this member
         redis_cmd_append_sstr(&cmd, mem, mem_len);
 
@@ -7291,12 +7351,12 @@ PHP_METHOD(Redis, pfadd) {
             efree(mem);
         }
     }
-   
-    REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); 
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
     IF_ATOMIC() {
         redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
     }
-    REDIS_PROCESS_RESPONSE(redis_1_response);   
+    REDIS_PROCESS_RESPONSE(redis_1_response);
 }
 
 /* {{{ proto Redis::pfCount(string key) }}}*/
@@ -7330,7 +7390,7 @@ PHP_METHOD(Redis, pfcount) {
 
 /* {{{ proto Redis::pfMerge(array keys) }}}*/
 PHP_METHOD(Redis, pfmerge) {
-    zval *object;    
+    zval *object;
     RedisSock *redis_sock;
     zval *z_keys, **z_key;
     HashTable *ht_keys;
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 0d7c53f5a4..343e7984b6 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -88,7 +88,7 @@ public function testPubSub() {
         $this->assertTrue(is_array($result));
 
         // PUBSUB NUMSUB
-        
+
         $c1 = uniqid() . '-' . rand(1,100);
         $c2 = uniqid() . '-' . rand(1,100);
 
@@ -218,7 +218,7 @@ public function testSet()
         $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'));
@@ -528,7 +528,7 @@ public function testIncrByFloat()
         }
 
         $this->redis->delete('key');
-        
+
         $this->redis->set('key', 0);
 
         $this->redis->incrbyfloat('key', 1.5);
@@ -1227,9 +1227,9 @@ public function testsRandMember() {
 	    }
 	}
 
-        // 
+        //
         // With and without count, while serializing
-        // 
+        //
 
         $this->redis->delete('set0');
         $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
@@ -1253,11 +1253,11 @@ public function testsRandMember() {
     public function testSRandMemberWithCount() {
         // Make sure the set is nuked
         $this->redis->delete('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));
@@ -1845,10 +1845,10 @@ public function testClient() {
 
         // 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));
     }
@@ -2328,6 +2328,40 @@ public function testZX() {
 
     }
 
+    public function testZRangeByLex() {
+        /* Only out since 2.8.9 */
+        if (version_compare($this->version,  '2.8.9', 'lt')) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $arr_vals = Array('a','b','c','d','e','f','g');
+
+        $this->redis->del('zlex');
+        foreach($arr_vals as $str_val) {
+            $this->redis->zadd('zlex', 0, $str_val);
+        }
+
+        /* These tests were taken off of redis.io out of sheer laziness :) */
+        $arr_ret = $this->redis->zRangeByLex('zlex', '-', '[c');
+        $this->assertTrue($arr_ret === Array('a','b','c'));
+
+        $arr_ret = $this->redis->zRangeByLex('zlex', '-', '(c');
+        $this->assertTrue($arr_ret === Array('a','b'));
+
+        $arr_ret = $this->redis->zRangeByLex('zlex', '[aaa', '(g');
+        $this->assertTrue($arr_ret === Array('b','c','d','e','f'));
+
+        /* Test with a limit and count */
+        $arr_ret = $this->redis->zRangeBylex('zlex', '-', '[c', 1, 2);
+        $this->assertTrue($arr_ret === Array('b','c'));
+
+        /* Test some invalid calls */
+        $this->assertFalse($this->redis->zRangeByLex('zlex','b','[s'));
+        $this->assertFalse($this->redis->zRangeByLex('zlex','(a', ''));
+        $this->assertFalse($this->redis->zRangeByLex('zlex','(a','[b',1));
+    }
+
     public function testHashes() {
 	$this->redis->delete('h', 'key');
 
@@ -4496,9 +4530,9 @@ public function testEvalSHA() {
 
     public function testSerialize() {
         $vals = Array(1, 1.5, 'one', Array('here','is','an','array'));
-        
+
         // Test with no serialization at all
-        $this->assertTrue($this->redis->_serialize('test') === 'test');        
+        $this->assertTrue($this->redis->_serialize('test') === 'test');
         $this->assertTrue($this->redis->_serialize(1) === '1');
         $this->assertTrue($this->redis->_serialize(Array()) === 'Array');
         $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object');
@@ -4509,7 +4543,7 @@ public function testSerialize() {
         }
 
         foreach($arr_serializers as $mode) {
-            $arr_enc = Array(); 
+            $arr_enc = Array();
             $arr_dec = Array();
 
             foreach($vals as $k => $v) {
@@ -4679,7 +4713,7 @@ public function testHScan() {
             $this->markTestSkipped();
             return;
         }
-    
+
         // Never get empty sets
         $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
 
@@ -4688,7 +4722,7 @@ public function testHScan() {
 
         for($i=0;$i<100;$i++) {
             if($i>3) {
-                $this->redis->hset('hash', "member:$i", "value:$i");    
+                $this->redis->hset('hash', "member:$i", "value:$i");
             } else {
                 $this->redis->hset('hash', "foomember:$i", "value:$i");
                 $i_foo_mems++;
@@ -4766,7 +4800,7 @@ public function testZScan() {
             } else {
                 $this->redis->zadd('zset', $i, "mem:$i");
             }
-            
+
             $i_tot_score += $i;
         }
 
@@ -4877,7 +4911,7 @@ public function testPFCommands() {
 
                 // Clean up merge key
                 $this->redis->del('pf-merge-key');
-                
+
                 // Merge the counters
                 $this->assertTrue($this->redis->pfmerge('pf-merge-key', $arr_keys));
 
@@ -4894,6 +4928,7 @@ public function testPFCommands() {
     }
 }
 
-exit(TestSuite::run("Redis_Test"));
+$str_test = isset($argv[1]) ? $argv[1] : NULL;
+exit(TestSuite::run("Redis_Test", $str_test));
 
 ?>
diff --git a/tests/test.php b/tests/test.php
index 6c714f1b4c..43d6a44517 100644
--- a/tests/test.php
+++ b/tests/test.php
@@ -2,7 +2,6 @@
 
 // phpunit is such a pain to install, we're going with pure-PHP here.
 class TestSuite {
-
 	public static $errors = array();
 	public static $warnings = array();
 
@@ -47,16 +46,24 @@ protected function markTestSkipped($msg='') {
 		throw new Exception($msg);
 	}
 
-	public static function run($className) {
-
-		$rc = new ReflectionClass($className);
+	public static function run($className, $str_limit) {
+        $rc = new ReflectionClass($className);
 		$methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC);
 
+        if ($str_limit) {
+            echo "Limiting to tests with the substring: '$str_limit'\n";
+        }
+
 		foreach($methods as $m) {
 			$name = $m->name;
 			if(substr($name, 0, 4) !== 'test')
 				continue;
 
+            /* If TestRedis.php was envoked with an argument, do a simple
+             * match against the routine.  Useful to limit to one test */
+            if ($str_limit && strpos(strtolower($name),strtolower($str_limit))===false)
+                continue;
+
 			$count = count($className::$errors);
 			$rt = new $className;
 			try {

From 4c1f1bcaa2d1d85d4815ebc4d1026a40071d52c6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 2 Sep 2014 12:27:28 -0700
Subject: [PATCH 0161/1986] Fixes a memory leak in SCAN, with OPT_SCAN_RETRY
 enabled.

When you instruct phpredis to retry the scan command in the event
of a non zero iterator but zero elements being returned, it was
leaking memory, as it did not free the previous result.

Addresses #501
---
 redis.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/redis.c b/redis.c
index 3591aba1c6..bde1f7d551 100644
--- a/redis.c
+++ b/redis.c
@@ -7152,6 +7152,13 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
      * an updated iterator.
      */
     do {
+        /* Free our previous reply if we're back in the loop.  We know we are
+         * if our return_value is an array. */
+        if(Z_TYPE_P(return_value) == IS_ARRAY) {
+            zval_dtor(return_value);
+            ZVAL_NULL(return_value);
+        }
+
         /* Format our SCAN command */
         cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter,
                                    pattern, pattern_len, count);

From ebb354193ad8e80885ba307cfb71476da633faa6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 2 Sep 2014 12:27:28 -0700
Subject: [PATCH 0162/1986] Fixes a memory leak in SCAN, with OPT_SCAN_RETRY
 enabled.

When you instruct phpredis to retry the scan command in the event
of a non zero iterator but zero elements being returned, it was
leaking memory, as it did not free the previous result.

Addresses #501
---
 redis.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/redis.c b/redis.c
index 02c0eb2129..9d13d0a7b0 100644
--- a/redis.c
+++ b/redis.c
@@ -7229,6 +7229,13 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
      * an updated iterator.
      */
     do {
+        /* Free our previous reply if we're back in the loop.  We know we are
+         * if our return_value is an array. */
+        if(Z_TYPE_P(return_value) == IS_ARRAY) {
+            zval_dtor(return_value);
+            ZVAL_NULL(return_value);
+        }
+
         /* Format our SCAN command */
         cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter,
                                    pattern, pattern_len, count);

From 837e44358e5b9ed6b6921fee356769ac9e3c66d6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 8 Sep 2014 12:43:13 -0700
Subject: [PATCH 0163/1986] Support for the "command" command.

This is a simple addition that allows a client to call any given
Redis command by sending the command name followed by a list of
arguments.

This is useful for the cases where there are new commands in Redis
that have not yet been specifically implemented in phpredis, or
if you want to use phpredis as a pass-through where the commands
and arguments are coming from somewhere else (e.g. monitor logs, etc).
---
 php_redis.h |  2 ++
 redis.c     | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/php_redis.h b/php_redis.h
index 23e263109a..3c1a41abd8 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -212,6 +212,8 @@ PHP_METHOD(Redis, isConnected);
 PHP_METHOD(Redis, getPersistentID);
 PHP_METHOD(Redis, getAuth);
 
+PHP_METHOD(Redis, command);
+
 #ifdef PHP_WIN32
 #define PHP_REDIS_API __declspec(dllexport)
 #else
diff --git a/redis.c b/redis.c
index 9d13d0a7b0..1ae819b381 100644
--- a/redis.c
+++ b/redis.c
@@ -276,6 +276,9 @@ static zend_function_entry redis_functions[] = {
      /* slowlog */
      PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC)
 
+     /* Send a raw command and read raw results */
+     PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC)
+
      /* introspection */
      PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, getPort, NULL, ZEND_ACC_PUBLIC)
@@ -6177,6 +6180,49 @@ PHP_METHOD(Redis, slowlog) {
     REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
 }
 
+/* {{{ proto Redis::command(string cmd, arg, arg, arg, ...) }}} */
+PHP_METHOD(Redis, command) {
+    zval **z_args;
+    RedisSock *redis_sock;
+    int argc = ZEND_NUM_ARGS(), i;
+    smart_str cmd = {0};
+
+    /* We need at least one argument */
+    z_args = emalloc(argc * sizeof(zval*));
+    if (argc < 1 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
+        efree(z_args);
+        RETURN_FALSE;
+    }
+
+    if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) {
+        efree(z_args);
+        RETURN_FALSE;
+    }
+
+    /* Initialize the command we'll send */
+    convert_to_string(z_args[0]);
+    redis_cmd_init_sstr(&cmd,argc-1,Z_STRVAL_P(z_args[0]),Z_STRLEN_P(z_args[0]));
+
+    /* Iterate over the remainder of our arguments, appending */
+    for (i = 1; i < argc; i++) {
+        convert_to_string(z_args[i]);
+        redis_cmd_append_sstr(&cmd, Z_STRVAL_P(z_args[i]), Z_STRLEN_P(z_args[i])); 
+    }
+
+    efree(z_args);
+
+    /* Kick off our request and read response or enqueue handler */
+    REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
+    IF_ATOMIC() {
+        if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, 
+                                     redis_sock, NULL) < 0)
+        {
+            RETURN_FALSE;
+        }
+    }
+    REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+}
+
 /* {{{ proto Redis::wait(int num_slaves, int ms) }}}
  */
 PHP_METHOD(Redis, wait) {

From 9ec0c93fb4d9232d1453df5ea23495067e56c256 Mon Sep 17 00:00:00 2001
From: amolrajoba 
Date: Mon, 15 Sep 2014 20:56:17 +0530
Subject: [PATCH 0164/1986] Update arrays.markdown

Documentation with example for great addition of connetion timeout 'connect_timeout'
---
 arrays.markdown | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/arrays.markdown b/arrays.markdown
index b122089162..efc3f05db4 100644
--- a/arrays.markdown
+++ b/arrays.markdown
@@ -40,13 +40,19 @@ $ra = new RedisArray(array("host1", "host2", "host3"), array("previous" => array
 #### Specifying the "retry_interval" parameter
 The retry_interval is used to specify a delay in milliseconds between reconnection attempts in case the client loses connection with a server
 
-$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_timeout" => 100)));
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_timeout" => 100));
 
#### Specifying the "lazy_connect" parameter This option is useful when a cluster has many shards but not of them are necessarily used at one time.
-$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("lazy_connect" => true)));
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("lazy_connect" => true));
+
+ +#### Specifying the "connect_timeout" parameter +The connect_timeout value is a double and is used to specify a timeout in number of seconds when creating redis socket connections used in the RedisArray. +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("connect_timeout" => 0.5));
 
#### Defining arrays in Redis.ini From aecfbfd650d6b504193d48f82a4a170ce3ce4cd6 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 3 Oct 2014 19:19:33 +0200 Subject: [PATCH 0165/1986] Fix segfault with igbinary, fix #341 --- library.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index 40d43fa2bf..3e91e933af 100644 --- a/library.c +++ b/library.c @@ -1730,11 +1730,12 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **re #ifdef HAVE_REDIS_IGBINARY if(!*return_value) { MAKE_STD_ZVAL(*return_value); + rv_free = 1; } if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { return 1; } - efree(*return_value); + if(rv_free==1) efree(*return_value); #endif return 0; break; From ed386cdd1e15ffbe6c63e3faa29615c789f454a9 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 3 Oct 2014 19:48:37 +0200 Subject: [PATCH 0166/1986] just to avoid warning: 'klen' may be used uninitialized in this function [-Wmaybe-uninitialized] --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 3e91e933af..ca31cf3caf 100644 --- a/library.c +++ b/library.c @@ -746,7 +746,7 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value; /* Key length, done flag */ - int klen, done = 0, is_numeric; + int klen = 0, done = 0, is_numeric; /* Make sure we can read a response from Redis */ if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { From 6c6299cb0d2a7e84f27771a2de50abda6aba762f Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 3 Oct 2014 19:19:33 +0200 Subject: [PATCH 0167/1986] Fix segfault with igbinary, fix #341 --- library.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index 3c67a2d036..65e5423f20 100644 --- a/library.c +++ b/library.c @@ -1730,11 +1730,12 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **re #ifdef HAVE_REDIS_IGBINARY if(!*return_value) { MAKE_STD_ZVAL(*return_value); + rv_free = 1; } if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { return 1; } - efree(*return_value); + if(rv_free==1) efree(*return_value); #endif return 0; break; From 0c4ddd90a6cfb0660cb59fd4521b40d289d6067c Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 3 Oct 2014 19:19:33 +0200 Subject: [PATCH 0168/1986] Fix segfault with igbinary, fix #341 --- library.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index 40d43fa2bf..3e91e933af 100644 --- a/library.c +++ b/library.c @@ -1730,11 +1730,12 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **re #ifdef HAVE_REDIS_IGBINARY if(!*return_value) { MAKE_STD_ZVAL(*return_value); + rv_free = 1; } if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { return 1; } - efree(*return_value); + if(rv_free==1) efree(*return_value); #endif return 0; break; From 66cdeb6b594178fae987745e2c34261f1f23e8c5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 20 Oct 2014 12:20:57 -0700 Subject: [PATCH 0169/1986] Allow integer connect_timeout in RedisArray Addresses #520 --- redis_array.c | 11 ++++++++--- redis_array_impl.c | 10 +++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/redis_array.c b/redis_array.c index 899ba718ee..fb44191139 100644 --- a/redis_array.c +++ b/redis_array.c @@ -280,11 +280,16 @@ PHP_METHOD(RedisArray, __construct) /* extract connect_timeout option */ if (FAILURE != zend_hash_find(hOpts, "connect_timeout", sizeof("connect_timeout"), (void**)&z_connect_timeout_pp)) { - if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE || Z_TYPE_PP(z_connect_timeout_pp) == IS_STRING) { + if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE || + Z_TYPE_PP(z_connect_timeout_pp) == IS_STRING || + Z_TYPE_PP(z_connect_timeout_pp) == IS_LONG) + { if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE) { d_connect_timeout = Z_DVAL_PP(z_connect_timeout_pp); - } - else { + } else if (Z_TYPE_PP(z_connect_timeout_pp) == IS_LONG) { + d_connect_timeout = Z_LVAL_PP(z_connect_timeout_pp); +php_printf("Connect timeout: %f\n", d_connect_timeout); + } else { d_connect_timeout = atof(Z_STRVAL_PP(z_connect_timeout_pp)); } } diff --git a/redis_array_impl.c b/redis_array_impl.c index 0776d57053..2fcf18e480 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -278,11 +278,15 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { array_init(z_params_connect_timeout); sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.connecttimeout")), z_params_connect_timeout TSRMLS_CC); if (zend_hash_find(Z_ARRVAL_P(z_params_connect_timeout), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { - if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE || Z_TYPE_PP(z_data_pp) == IS_STRING) { + if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE || + Z_TYPE_PP(z_data_pp) == IS_STRING || + Z_TYPE_PP(z_data_pp) == IS_LONG) + { if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE) { d_connect_timeout = Z_DVAL_PP(z_data_pp); - } - else { + } else if (Z_TYPE_PP(z_data_pp) == IS_LONG) { + d_connect_timeout = Z_LVAL_PP(z_data_pp); + } else { d_connect_timeout = atof(Z_STRVAL_PP(z_data_pp)); } } From c1f862c99db7c8935d31a88f7259d2755a7ba295 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 22 Oct 2014 13:52:31 -0700 Subject: [PATCH 0170/1986] Remove debug line --- redis_array.c | 1 - 1 file changed, 1 deletion(-) diff --git a/redis_array.c b/redis_array.c index fb44191139..c271fc1a26 100644 --- a/redis_array.c +++ b/redis_array.c @@ -288,7 +288,6 @@ PHP_METHOD(RedisArray, __construct) d_connect_timeout = Z_DVAL_PP(z_connect_timeout_pp); } else if (Z_TYPE_PP(z_connect_timeout_pp) == IS_LONG) { d_connect_timeout = Z_LVAL_PP(z_connect_timeout_pp); -php_printf("Connect timeout: %f\n", d_connect_timeout); } else { d_connect_timeout = atof(Z_STRVAL_PP(z_connect_timeout_pp)); } From 24f75854511d771ff23c95e65e45601a032c6918 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 27 Oct 2014 21:55:01 -0700 Subject: [PATCH 0171/1986] Modify error handling for specific errors, and reathorize when reconnecting. For certain error types (Redis LOADING the dataset, SYNC in progress/master down, and AUTH failures) it may be prudent to throw an exception rather than simply return false. In addition, this commit adds logic to reauthorize the connection in the event of a reconnect (for whatever reason). Addresses #515 --- common.h | 9 ++- library.c | 176 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 135 insertions(+), 50 deletions(-) diff --git a/common.h b/common.h index a0623fbdc7..c972619e29 100644 --- a/common.h +++ b/common.h @@ -64,7 +64,6 @@ typedef enum _PUBSUB_TYPE { #define REDIS_SERIALIZER_IGBINARY 2 /* SCAN options */ - #define REDIS_SCAN_NORETRY 0 #define REDIS_SCAN_RETRY 1 @@ -72,6 +71,14 @@ typedef enum _PUBSUB_TYPE { #define BITOP_MIN_OFFSET 0 #define BITOP_MAX_OFFSET 4294967295 +/* Specific error messages we want to throw against */ +#define REDIS_ERR_LOADING_MSG "LOADING Redis is loading the dataset in memory" +#define REDIS_ERR_LOADING_KW "LOADING" +#define REDIS_ERR_AUTH_MSG "NOAUTH Authentication required." +#define REDIS_ERR_AUTH_KW "NOAUTH" +#define REDIS_ERR_SYNC_MSG "MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'" +#define REDIS_ERR_SYNC_KW "MASTERDOWN" + #define IF_MULTI() if(redis_sock->mode == MULTI) #define IF_MULTI_OR_ATOMIC() if(redis_sock->mode == MULTI || redis_sock->mode == ATOMIC)\ diff --git a/library.c b/library.c index 3e91e933af..5b2c799a20 100644 --- a/library.c +++ b/library.c @@ -39,6 +39,89 @@ extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; extern zend_class_entry *spl_ce_RuntimeException; +/* Helper to reselect the proper DB number when we reconnect */ +static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { + char *cmd, *response; + int cmd_len, response_len; + + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); + + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return -1; + } + + efree(cmd); + + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return -1; + } + + if (strncmp(response, "+OK", 3)) { + efree(response); + return -1; + } + + efree(response); + return 0; +} + +/* Helper to resend AUTH in the case of a reconnect */ +static int resend_auth(RedisSock *redis_sock TSRMLS_DC) { + char *cmd, *response; + int cmd_len, response_len; + + cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", redis_sock->auth, + strlen(redis_sock->auth)); + + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_DC) < 0) { + efree(cmd); + return -1; + } + + efree(cmd); + + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + if (response == NULL) { + return -1; + } + + if (strncmp(response, "+OK", 3)) { + efree(response); + return -1; + } + + efree(response); + return 0; +} + +/* Helper function that will throw an exception for a small number of ERR codes + * returned by Redis. Typically we just return FALSE to the caller in the event + * of an ERROR reply, but for the following error types: + * 1) MASTERDOWN + * 2) AUTH + * 3) LOADING + */ +static void redis_error_throw(char *err, size_t err_len TSRMLS_DC) { + /* Handle stale data error (slave syncing with master) */ + if (err_len == sizeof(REDIS_ERR_SYNC_MSG) - 1 && + !memcmp(err,REDIS_ERR_SYNC_KW,sizeof(REDIS_ERR_SYNC_KW)-1)) + { + zend_throw_exception(redis_exception_ce, + "SYNC with master in progress or master down!", 0 TSRMLS_CC); + } else if (err_len == sizeof(REDIS_ERR_LOADING_MSG) - 1 && + !memcmp(err,REDIS_ERR_LOADING_KW,sizeof(REDIS_ERR_LOADING_KW)-1)) + { + zend_throw_exception(redis_exception_ce, + "Redis is LOADING the dataset", 0 TSRMLS_CC); + } else if (err_len == sizeof(REDIS_ERR_AUTH_MSG) -1 && + !memcmp(err,REDIS_ERR_AUTH_KW,sizeof(REDIS_ERR_AUTH_KW)-1)) + { + zend_throw_exception(redis_exception_ce, + "Failed to AUTH connection", 0 TSRMLS_CC); + } +} + PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { if (!redis_sock->persistent) { php_stream_close(redis_sock->stream); @@ -58,64 +141,60 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) eof = php_stream_eof(redis_sock->stream); for (; eof; count++) { - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ - if(redis_sock->stream) { /* close stream if still here */ + /* Only try up to a certain point */ + if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { + if(redis_sock->stream) { /* close stream if still here */ redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; + redis_sock->mode = ATOMIC; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->watching = 0; - } + } + zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); - return -1; - } - if(redis_sock->stream) { /* close existing stream before reconnecting */ + return -1; + } + + /* Close existing stream before reconnecting */ + if(redis_sock->stream) { redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; + redis_sock->mode = ATOMIC; redis_sock->watching = 0; - } - /* Wait for a while before trying to reconnect */ - if (redis_sock->retry_interval) { - // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time - long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); - usleep(retry_interval); - } + } + + /* Wait for a while before trying to reconnect */ + if (redis_sock->retry_interval) { + // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time + long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) redis_sock->retry_interval)); + usleep(retry_interval); + } + redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ if(redis_sock->stream) { /* check for EOF again. */ eof = php_stream_eof(redis_sock->stream); } } - /* Reselect the DB. */ - if (count && redis_sock->dbNumber) { - char *cmd, *response; - int cmd_len, response_len; - - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); - - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); + /* We've reconnected if we have a count */ + if (count) { + /* If we're using a password, attempt a reauthorization */ + if (redis_sock->auth && resend_auth(redis_sock) != 0) { return -1; } - efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + /* If we're using a non zero db, reselect it */ + if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) { return -1; } - - if (strncmp(response, "+OK", 3)) { - efree(response); - return -1; - } - efree(response); } + /* Success */ return 0; } -PHP_REDIS_API int +PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter) { @@ -259,14 +338,14 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D switch(inbuf[0]) { case '-': - err_len = strlen(inbuf+1) - 2; + /* Set the last error */ + err_len = strlen(inbuf+1) - 2; redis_sock_set_err(redis_sock, inbuf+1, err_len); - /* stale data */ - if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } - return NULL; + /* Filter our ERROR through the few that should actually throw */ + redis_error_throw(inbuf + 1, err_len); + + return NULL; case '$': *buf_len = atoi(inbuf + 1); resp = redis_sock_read_bulk_reply(redis_sock, *buf_len TSRMLS_CC); @@ -432,7 +511,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { /** * This command behave somehow like printf, except that strings need 2 arguments: * Their data and their size (strlen). - * Supported formats are: %d, %i, %s, %l + * Supported formats are:d, %i, %s, %l */ int redis_cmd_format(char **ret, char *format, ...) { @@ -1090,7 +1169,7 @@ PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } /* Response for DEBUG object which is a formatted single line reply */ -PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *resp, *p, *p2, *p3, *p4; @@ -1116,7 +1195,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock while((p2 = strchr(p, ':'))!=NULL) { /* Null terminate at the ':' */ *p2++ = '\0'; - + /* Null terminate at the space if we have one */ if((p3 = strchr(p2, ' '))!=NULL) { *p3++ = '\0'; @@ -1138,7 +1217,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } else { add_assoc_string(z_result, p, p2, 1); } - + p = p3; } @@ -1493,7 +1572,7 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re if(response != NULL) { zval *z = NULL; int can_unserialize = unwrap_key; - if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) + if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems 2 == 0) can_unserialize = 0; if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { @@ -1789,7 +1868,6 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_siz *line_size-=2; buf[*line_size]='\0'; - /* Success! */ return 0; } @@ -1839,11 +1917,11 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval return -1; } - /* If this is an error response, check if it is a SYNC error, and throw in that case */ + /* If this is an error response, filter specific errors that should throw + * an exception, and set our error field in our RedisSock object. */ if(reply_type == TYPE_ERR) { - if(memcmp(inbuf, "ERR SYNC", 9) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } + /* Handle throwable errors */ + redis_error_throw(inbuf, line_size TSRMLS_CC); /* Set our last error */ redis_sock_set_err(redis_sock, inbuf, line_size); From 346e4d43cd555e1e470737c6e653103e3b5bdb1f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 29 Oct 2014 10:00:04 -0700 Subject: [PATCH 0172/1986] Re-add % signs accidentally deleted --- library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 5b2c799a20..b0085cfd58 100644 --- a/library.c +++ b/library.c @@ -166,7 +166,7 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) /* Wait for a while before trying to reconnect */ if (redis_sock->retry_interval) { // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time - long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) redis_sock->retry_interval)); + long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); usleep(retry_interval); } @@ -1572,7 +1572,7 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re if(response != NULL) { zval *z = NULL; int can_unserialize = unwrap_key; - if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems 2 == 0) + if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) can_unserialize = 0; if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { From a302564c4c9a2800a3ff88def8f900400fbedec2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 31 Oct 2014 15:00:59 -0700 Subject: [PATCH 0173/1986] Fix parsing of 'zipped' replies for various uses As discovered in issue #523, phpredis was attempting to unserialize both the keys *and* scores for commands like zRangeByScore. This had to do with the logic around deserialization in the response. In addition, this same bug would have caused issues when running commands like $r->config('get'), because that too, would have tried to unserialize the values, which we don't want to do. This commit reworks parsing and zipping up replies by allowing the call to be configured to unseraialize any combination of keys or values (or none or both). --- library.c | 250 ++++++++++++++++++++++++++++++++++---------- library.h | 15 +-- php_redis.h | 1 - redis.c | 218 ++++++++++++++------------------------ tests/TestRedis.php | 48 ++++----- 5 files changed, 307 insertions(+), 225 deletions(-) diff --git a/library.c b/library.c index 3e91e933af..1c6fb75d1e 100644 --- a/library.c +++ b/library.c @@ -32,8 +32,14 @@ PHPAPI int usleep(unsigned int useconds); #define usleep Sleep #endif -#define UNSERIALIZE_ONLY_VALUES 0 -#define UNSERIALIZE_ALL 1 +#define UNSERIALIZE_NONE 0 +#define UNSERIALIZE_KEYS 1 +#define UNSERIALIZE_VALS 2 +#define UNSERIALIZE_ALL 3 + +#define SCORE_DECODE_NONE 0 +#define SCORE_DECODE_INT 1 +#define SCORE_DECODE_DOUBLE 2 extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; @@ -115,7 +121,7 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) } -PHP_REDIS_API int +PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter) { @@ -150,13 +156,13 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, scan command this is. They all come back in slightly different ways */ switch(type) { case TYPE_SCAN: - return redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_SSCAN: return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_ZSCAN: - return redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_HSCAN: - return redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); default: return -1; } @@ -189,9 +195,10 @@ PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM MAKE_STD_ZVAL(z_tab); array_init(z_tab); - redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, z_tab, numElems, 1, UNSERIALIZE_ALL); - return z_tab; + redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, + numElems, UNSERIALIZE_ALL); + + return z_tab; } /** @@ -944,15 +951,90 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } } +/* Helper method to convert [key, value, key, value] into [key => value, + * key => value] when returning data to the caller. Depending on our decode + * flag we'll convert the value data types */ +static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, + int decode TSRMLS_DC) +{ + + zval *z_ret; + HashTable *keytable; + + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + keytable = Z_ARRVAL_P(z_tab); + + for(zend_hash_internal_pointer_reset(keytable); + zend_hash_has_more_elements(keytable) == SUCCESS; + zend_hash_move_forward(keytable)) { + + char *tablekey, *hkey, *hval; + unsigned int tablekey_len; + int hkey_len; + unsigned long idx; + zval **z_key_pp, **z_value_pp; + + zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { + continue; /* this should never happen, according to the PHP people. */ + } + + /* get current value, a key */ + convert_to_string(*z_key_pp); + hkey = Z_STRVAL_PP(z_key_pp); + hkey_len = Z_STRLEN_PP(z_key_pp); + + /* move forward */ + zend_hash_move_forward(keytable); + + /* fetch again */ + zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { + continue; /* this should never happen, according to the PHP people. */ + } + + /* get current value, a hash value now. */ + hval = Z_STRVAL_PP(z_value_pp); + /* Decode the score depending on flag */ + if (decode == SCORE_DECODE_INT && Z_STRLEN_PP(z_value_pp) > 0) { + add_assoc_long_ex(z_ret, hkey, 1+hkey_len, atoi(hval+1)); + } else if (decode == SCORE_DECODE_DOUBLE) { + add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); + } else { + zval *z = NULL; + MAKE_STD_ZVAL(z); + *z = **z_value_pp; + zval_copy_ctor(z); + add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); + } + + /* + if(use_atof) { + add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); + } else { + zval *z = NULL; + MAKE_STD_ZVAL(z); + *z = **z_value_pp; + zval_copy_ctor(z); + add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); + }*/ -PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int flag) { + } + /* replace */ + zval_dtor(z_tab); + *z_tab = *z_ret; + zval_copy_ctor(z_tab); + zval_dtor(z_ret); - /* - int ret = redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab TSRMLS_CC); - array_zip_values_and_scores(return_value, 0); - */ + efree(z_ret); +} +static int +redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, int unserialize, int decode) +{ char inbuf[1024]; int numElems; zval *z_multi_result; @@ -982,10 +1064,12 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNC MAKE_STD_ZVAL(z_multi_result); array_init(z_multi_result); /* pre-allocate array for multi's results. */ - redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, z_multi_result, numElems, 1, flag ? UNSERIALIZE_ALL : UNSERIALIZE_ONLY_VALUES); + /* Grab our key, value, key, value array */ + redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_multi_result, numElems, unserialize); - array_zip_values_and_scores(redis_sock, z_multi_result, 0 TSRMLS_CC); + /* Zip keys and values */ + array_zip_values_and_scores(redis_sock, z_multi_result, decode TSRMLS_CC); IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); @@ -999,13 +1083,35 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNC return 0; } -PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +/* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */ +PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, UNSERIALIZE_NONE, SCORE_DECODE_NONE); +} - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 1); +/* Zipped key => value reply unserializing keys and decoding the score as an integer (PUBSUB) */ +PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_INT); } -PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, 0); +/* Zipped key => value reply unserializing keys and decoding the score as a double (ZSET commands) */ +PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_DOUBLE); +} + +/* Zipped key => value reply where only the values are unserialized (e.g. HMGET) */ +PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -1090,7 +1196,7 @@ PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } /* Response for DEBUG object which is a formatted single line reply */ -PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *resp, *p, *p2, *p3, *p4; @@ -1116,7 +1222,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock while((p2 = strchr(p, ':'))!=NULL) { /* Null terminate at the ':' */ *p2++ = '\0'; - + /* Null terminate at the space if we have one */ if((p3 = strchr(p2, ' '))!=NULL) { *p3++ = '\0'; @@ -1138,7 +1244,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } else { add_assoc_string(z_result, p, p2, 1); } - + p = p3; } @@ -1421,8 +1527,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, MAKE_STD_ZVAL(z_multi_result); array_init(z_multi_result); /* pre-allocate array for multi's results. */ - redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, z_multi_result, numElems, 1, UNSERIALIZE_ALL); + redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_multi_result, numElems, UNSERIALIZE_ALL); IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); @@ -1437,7 +1543,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, /** * Like multibulk reply, but don't touch the values, they won't be compressed. (this is used by HKEYS). */ -PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; int numElems; @@ -1468,8 +1574,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETE MAKE_STD_ZVAL(z_multi_result); array_init(z_multi_result); /* pre-allocate array for multi's results. */ - redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, z_multi_result, numElems, 0, UNSERIALIZE_ALL); + redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_multi_result, numElems, UNSERIALIZE_NONE); IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); @@ -1481,6 +1587,42 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETE return 0; } +PHP_REDIS_API void +redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, int count, int unserialize) +{ + char *line; + int len; + + while(count > 0) { + line = redis_sock_read(redis_sock, &len TSRMLS_CC); + if (line != NULL) { + zval *z = NULL; + int unwrap; + + /* We will attempt unserialization, if we're unserializing everything, + * or if we're unserializing keys and we're on a key, or we're + * unserializing values and we're on a value! */ + unwrap = unserialize == UNSERIALIZE_ALL || + (unserialize == UNSERIALIZE_KEYS && count % 2 == 0) || + (unserialize == UNSERIALIZE_VALS && count % 2 != 0); + + if (unwrap && redis_unserialize(redis_sock, line, len, &z TSRMLS_CC)) { + efree(line); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, line, len, 0); + } + } else { + add_next_index_bool(z_tab, 0); + } + + count--; + } +} + + +/* PHP_REDIS_API int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only) @@ -1509,16 +1651,16 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re } return 0; } +*/ -/** - * redis_sock_read_multibulk_reply_assoc - */ -PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +/* Specialized multibulk processing for HMGET where we need to pair requested + * keys with their returned values */ +PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024], *response; int response_len; - int i, numElems; - zval *z_multi_result; + int i, numElems; + zval *z_multi_result; zval **z_keys = ctx; @@ -1526,7 +1668,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAME return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1550,30 +1692,30 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAME for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); - } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); - } - } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); - } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + efree(response); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + } + } else { + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + } + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - INIT_PZVAL(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); - } + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + INIT_PZVAL(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); + } return 0; } diff --git a/library.h b/library.h index 652db6f407..2e2d770acd 100644 --- a/library.h +++ b/library.h @@ -31,13 +31,16 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only); -PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter); +PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); +PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); + +PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter); PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC); diff --git a/php_redis.h b/php_redis.h index e6ec1ef2f8..5054a321d5 100644 --- a/php_redis.h +++ b/php_redis.h @@ -235,7 +235,6 @@ PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cm PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd); PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd); -PHP_REDIS_API void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC); PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int get_flag(zval *object TSRMLS_DC); diff --git a/redis.c b/redis.c index bde1f7d551..09f8730117 100644 --- a/redis.c +++ b/redis.c @@ -311,8 +311,8 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, srem, sRemove, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zrevrange, zReverseRange, NULL, ZEND_ACC_PUBLIC) - - PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC) + + PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC) @@ -471,7 +471,7 @@ PHP_MINIT_FUNCTION(redis) zend_class_entry redis_exception_class_entry; REGISTER_INI_ENTRIES(); - + /* Redis class */ INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions); redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC); @@ -647,7 +647,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { char *persistent_id = NULL; int persistent_id_len = -1; - + double timeout = 0.0; RedisSock *redis_sock = NULL; @@ -827,7 +827,7 @@ PHP_METHOD(Redis, bitcount) /* }}} */ /* {{{ proto integer Redis::bitpos(string key, int bit, [int start], [int end]) */ -PHP_METHOD(Redis, bitpos) +PHP_METHOD(Redis, bitpos) { zval *object; RedisSock *redis_sock; @@ -929,7 +929,7 @@ PHP_METHOD(Redis, set) { /* Our optional argument can either be a long (to support legacy SETEX */ /* redirection), or an array with Redis >= 2.6.12 set options */ if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY - && Z_TYPE_P(z_opts) != IS_NULL) + && Z_TYPE_P(z_opts) != IS_NULL) { RETURN_FALSE; } @@ -1425,7 +1425,7 @@ PHP_METHOD(Redis, incrByFloat) { /* Prefix key, format command, free old key if necissary */ key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val); - if(key_free) efree(key); + if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1688,12 +1688,11 @@ PHP_METHOD(Redis, getKeys) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if (redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { + if (redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw); } /* }}} */ @@ -1987,11 +1986,11 @@ PHP_METHOD(Redis, lInsert) int pivot_len, position_len, key_len, val_len, cmd_len; int val_free, pivot_free, key_free; zval *z_value, *z_pivot; - + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osszz", &object, redis_ce, - &key, &key_len, + &key, &key_len, &position, &position_len, &z_pivot, &z_value) == FAILURE) { @@ -2001,7 +2000,7 @@ PHP_METHOD(Redis, lInsert) if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - + if(strncasecmp(position, "after", 5) == 0 || strncasecmp(position, "before", 6) == 0) { key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); @@ -2012,15 +2011,15 @@ PHP_METHOD(Redis, lInsert) if(key_free) efree(key); if(pivot_free) STR_FREE(pivot); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_long_response); } else { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error on position"); } - + } PHP_METHOD(Redis, lPushx) @@ -2664,7 +2663,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * if(has_timeout && Z_TYPE_P(z_args[argc - 1]) != IS_LONG) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Syntax error on timeout"); } - + for(i = 0, j = 0; i < argc; ++i) { /* store each key */ if(!all_keys && j != 0) { /* not just operating on keys */ @@ -2678,7 +2677,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * } } else { - + if(Z_TYPE_P(z_args[i]) != IS_STRING) { convert_to_string(z_args[i]); } @@ -2702,7 +2701,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * cmd = emalloc(cmd_len+1); sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1+real_argc, keyword_len, keyword); - + pos = 1 +integer_length(real_argc + 1) + 2 + 1 + integer_length(keyword_len) + 2 + keyword_len + 2; @@ -4019,9 +4018,9 @@ PHP_METHOD(Redis, zRange) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(withscores) { IF_ATOMIC() { - redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); } else { IF_ATOMIC() { if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, @@ -4149,9 +4148,9 @@ PHP_METHOD(Redis, zReverseRange) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(withscores) { IF_ATOMIC() { - redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); } else { IF_ATOMIC() { if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, @@ -4243,11 +4242,11 @@ redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { * we want [elt0 => val0, elt1 => val1], etc. */ IF_ATOMIC() { - if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { + if(redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); } else { IF_ATOMIC() { if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, @@ -4855,12 +4854,11 @@ PHP_METHOD(Redis, hKeys) RETURN_FALSE; IF_ATOMIC() { - if (redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { + if (redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw); } @@ -4889,72 +4887,12 @@ PHP_METHOD(Redis, hGetAll) { RETURN_FALSE; IF_ATOMIC() { - if (redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, + if (redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings); -} - -PHP_REDIS_API void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC) { - - zval *z_ret; - HashTable *keytable; - - MAKE_STD_ZVAL(z_ret); - array_init(z_ret); - keytable = Z_ARRVAL_P(z_tab); - - for(zend_hash_internal_pointer_reset(keytable); - zend_hash_has_more_elements(keytable) == SUCCESS; - zend_hash_move_forward(keytable)) { - - char *tablekey, *hkey, *hval; - unsigned int tablekey_len; - int hkey_len; - unsigned long idx; - zval **z_key_pp, **z_value_pp; - - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ - } - - /* get current value, a key */ - convert_to_string(*z_key_pp); - hkey = Z_STRVAL_PP(z_key_pp); - hkey_len = Z_STRLEN_PP(z_key_pp); - - /* move forward */ - zend_hash_move_forward(keytable); - - /* fetch again */ - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ - } - - /* get current value, a hash value now. */ - hval = Z_STRVAL_PP(z_value_pp); - - if(use_atof) { /* zipping a score */ - add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); - } else { /* add raw copy */ - zval *z = NULL; - MAKE_STD_ZVAL(z); - *z = **z_value_pp; - zval_copy_ctor(z); - add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); - } - } - /* replace */ - zval_dtor(z_tab); - *z_tab = *z_ret; - zval_copy_ctor(z_tab); - zval_dtor(z_ret); - - efree(z_ret); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_vals); } PHP_METHOD(Redis, hIncrByFloat) @@ -5076,7 +5014,7 @@ PHP_METHOD(Redis, hMget) { /* Make sure the data is a long or string, and if it's a string that */ /* it isn't empty. There is no reason to send empty length members. */ if((Z_TYPE_PP(data) == IS_STRING && Z_STRLEN_PP(data)>0) || - Z_TYPE_PP(data) == IS_LONG) + Z_TYPE_PP(data) == IS_LONG) { /* This is a key we can ask for, copy it and set it in our array */ MAKE_STD_ZVAL(z_keys[valid]); @@ -5113,9 +5051,9 @@ PHP_METHOD(Redis, hMget) { /* Kick off our request */ REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); IF_ATOMIC() { - redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, z_keys); + redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, z_keys); } - REDIS_PROCESS_RESPONSE_CLOSURE(redis_sock_read_multibulk_reply_assoc, z_keys); + REDIS_PROCESS_RESPONSE_CLOSURE(redis_mbulk_reply_assoc, z_keys); } PHP_METHOD(Redis, hMset) @@ -5161,7 +5099,7 @@ PHP_METHOD(Redis, hMset) unsigned long idx; int type; zval **z_value_p; - + char *hval; int hval_len, hval_free; @@ -5356,7 +5294,7 @@ free_reply_callbacks(zval *z_this, RedisSock *redis_sock) { fold_item *fi; fold_item *head = redis_sock->head; request_item *ri; - + for(fi = head; fi; ) { fold_item *fi_next = fi->next; free(fi); @@ -5503,9 +5441,9 @@ PHP_METHOD(Redis, pipeline) RETURN_ZVAL(getThis(), 1, 0); } -/* - publish channel message - @return the number of subscribers +/* + publish channel message + @return the number of subscribers */ PHP_METHOD(Redis, publish) { @@ -5551,10 +5489,10 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub zend_fcall_info_cache z_callback_cache; zval *z_ret, **z_args[4]; - + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oaf", &object, redis_ce, &array, &z_callback, &z_callback_cache) == FAILURE) { - RETURN_FALSE; + RETURN_FALSE; } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { @@ -5606,9 +5544,9 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub RETURN_FALSE; } efree(cmd); - + /* read the status of the execution of the command `subscribe` */ - + z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); if(z_tab == NULL) { RETURN_FALSE; @@ -5618,14 +5556,14 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub type_response = Z_STRVAL_PP(tmp); if(strcmp(type_response, sub_cmd) != 0) { efree(tmp); - efree(z_tab); + efree(z_tab); RETURN_FALSE; - } + } } else { - efree(z_tab); + efree(z_tab); RETURN_FALSE; } - efree(z_tab); + efree(z_tab); /* Set a pointer to our return value and to our arguments. */ z_callback.retval_ptr_ptr = &z_ret; @@ -5633,12 +5571,12 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub z_callback.no_separation = 0; /* Multibulk Response, format : {message type, originating channel, message payload} */ - while(1) { + while(1) { /* call the callback with this z_tab in argument */ int is_pmsg, tab_idx = 1; zval **type, **channel, **pattern, **data; z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - + if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) { /*ERROR */ break; @@ -5715,7 +5653,7 @@ PHP_METHOD(Redis, subscribe) { generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "subscribe"); } -/** +/** * [p]unsubscribe channel_0 channel_1 ... channel_n * [p]unsubscribe(array(channel_0, channel_1, ..., channel_n)) * response format : @@ -5735,13 +5673,13 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u RedisSock *redis_sock; char *cmd = "", *old_cmd = NULL; int cmd_len, array_count; - + int i; zval *z_tab, **z_channel; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_ce, &array) == FAILURE) { - RETURN_FALSE; + RETURN_FALSE; } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -5787,10 +5725,10 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u while( i <= array_count) { z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - if(Z_TYPE_P(z_tab) == IS_ARRAY) { + if(Z_TYPE_P(z_tab) == IS_ARRAY) { if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) == FAILURE) { RETURN_FALSE; - } + } add_assoc_bool(return_value, Z_STRVAL_PP(z_channel), 1); } else { /*error */ @@ -6030,9 +5968,9 @@ PHP_METHOD(Redis, config) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) IF_ATOMIC() { - redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw); } else if(mode == CFG_SET && val != NULL) { cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len); @@ -6110,9 +6048,9 @@ PHP_METHOD(Redis, wait) { int cmd_len; /* Make sure arguments are valid */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", &object, redis_ce, &num_slaves, &timeout) - ==FAILURE) + ==FAILURE) { RETURN_FALSE; } @@ -6146,7 +6084,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, zval *arg TSRMLS_DC) { HashTable *ht_chan; - HashPosition ptr; + HashPosition ptr; zval **z_ele; char *key; int cmd_len, key_len, key_free; @@ -6176,7 +6114,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, } } else if(type == PUBSUB_NUMSUB) { ht_chan = Z_ARRVAL_P(arg); - + /* Add PUBSUB and NUMSUB bits */ redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1); redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1); @@ -6245,9 +6183,9 @@ PHP_METHOD(Redis, pubsub) { zval *arg=NULL; /* Parse arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z", + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z", &object, redis_ce, &keyword, &kw_len, &arg) - ==FAILURE) + ==FAILURE) { RETURN_FALSE; } @@ -6262,7 +6200,7 @@ PHP_METHOD(Redis, pubsub) { } else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) { /* One array argument */ if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY || - zend_hash_num_elements(Z_ARRVAL_P(arg))==0) + zend_hash_num_elements(Z_ARRVAL_P(arg))==0) { RETURN_FALSE; } @@ -6286,11 +6224,11 @@ PHP_METHOD(Redis, pubsub) { if(type == PUBSUB_NUMSUB) { IF_ATOMIC() { - if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) { + if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) { RETURN_FALSE; } } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_int); } else { IF_ATOMIC() { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL)<0) { @@ -6572,7 +6510,7 @@ PHP_METHOD(Redis, debug) { char *cmd, *key; int cmd_len, key_len, key_free; - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce, &key, &key_len)==FAILURE) { RETURN_FALSE; @@ -6643,7 +6581,7 @@ PHP_METHOD(Redis, migrate) { /* Parse arguments */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll|bb", &object, redis_ce, - &host, &host_len, &port, &key, &key_len, &dest_db, &timeout, + &host, &host_len, &port, &key, &key_len, &dest_db, &timeout, ©, &replace) == FAILURE) { RETURN_FALSE; } @@ -6658,19 +6596,19 @@ PHP_METHOD(Redis, migrate) { /* Construct our command */ if(copy && replace) { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port, - key, key_len, dest_db, timeout, "COPY", + cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port, + key, key_len, dest_db, timeout, "COPY", sizeof("COPY")-1, "REPLACE", sizeof("REPLACE")-1); } else if(copy) { cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port, - key, key_len, dest_db, timeout, "COPY", + key, key_len, dest_db, timeout, "COPY", sizeof("COPY")-1); } else if(replace) { cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port, key, key_len, dest_db, timeout, "REPLACE", sizeof("REPLACE")-1); } else { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, + cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, key, key_len, dest_db, timeout); } @@ -6851,11 +6789,11 @@ PHP_METHOD(Redis, time) { /* Execute or queue command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { + if(redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw); } /* @@ -6989,8 +6927,8 @@ PHP_METHOD(Redis, client) { int cmd_len, opt_len, arg_len; /* Parse our method parameters */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s", - &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s", + &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE) { RETURN_FALSE; } @@ -7003,7 +6941,7 @@ PHP_METHOD(Redis, client) { /* Build our CLIENT command */ if(ZEND_NUM_ARGS() == 2) { cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len, - arg, arg_len); + arg, arg_len); } else { cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len); } diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 9bbef4daca..c6955b6953 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -88,7 +88,7 @@ public function testPubSub() { $this->assertTrue(is_array($result)); // PUBSUB NUMSUB - + $c1 = uniqid() . '-' . rand(1,100); $c2 = uniqid() . '-' . rand(1,100); @@ -101,7 +101,7 @@ public function testPubSub() { // Make sure the elements are correct, and have zero counts foreach(Array($c1,$c2) as $channel) { $this->assertTrue(isset($result[$channel])); - $this->assertEquals($result[$channel], "0"); + $this->assertEquals($result[$channel], 0); } // PUBSUB NUMPAT @@ -218,7 +218,7 @@ public function testSet() $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')); @@ -528,7 +528,7 @@ public function testIncrByFloat() } $this->redis->delete('key'); - + $this->redis->set('key', 0); $this->redis->incrbyfloat('key', 1.5); @@ -1227,9 +1227,9 @@ public function testsRandMember() { } } - // + // // With and without count, while serializing - // + // $this->redis->delete('set0'); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); @@ -1253,11 +1253,11 @@ public function testsRandMember() { public function testSRandMemberWithCount() { // Make sure the set is nuked $this->redis->delete('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)); @@ -1845,10 +1845,10 @@ public function testClient() { // 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)); } @@ -2037,14 +2037,13 @@ public function testBRpopLpush() { } public function testZAddFirstArg() { + $zsetName = 100; // Make sure int keys work + $this->redis->delete($zsetName); - $this->redis->delete('key'); - - $zsetName = 100; // not a string! - $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1')); + $this->assertEquals(1, $this->redis->zAdd($zsetName, 0, 'val0')); + $this->assertEquals(1, $this->redis->zAdd($zsetName, 1, 'val1')); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1)); + $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1)); } public function testZX() { @@ -4496,7 +4495,7 @@ public function testEvalSHA() { public function testSerialize() { $vals = Array(1, 1.5, 'one', Array('here','is','an','array')); - + // Test with no serialization at all $this->assertTrue($this->redis->_serialize('test') === 'test'); $this->assertTrue($this->redis->_serialize(1) === '1'); @@ -4509,7 +4508,7 @@ public function testSerialize() { } foreach($arr_serializers as $mode) { - $arr_enc = Array(); + $arr_enc = Array(); $arr_dec = Array(); foreach($vals as $k => $v) { @@ -4679,7 +4678,7 @@ public function testHScan() { $this->markTestSkipped(); return; } - + // Never get empty sets $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); @@ -4688,7 +4687,7 @@ public function testHScan() { for($i=0;$i<100;$i++) { if($i>3) { - $this->redis->hset('hash', "member:$i", "value:$i"); + $this->redis->hset('hash', "member:$i", "value:$i"); } else { $this->redis->hset('hash', "foomember:$i", "value:$i"); $i_foo_mems++; @@ -4766,7 +4765,7 @@ public function testZScan() { } else { $this->redis->zadd('zset', $i, "mem:$i"); } - + $i_tot_score += $i; } @@ -4778,8 +4777,9 @@ public function testZScan() { $i--; } } + $this->assertEquals(0, $i); - $this->assertEquals(0, $i_tot_score); + $this->assertEquals(0.0, $i_tot_score); // Just scan "pmem" members $it = NULL; @@ -4791,7 +4791,7 @@ public function testZScan() { $i_p_count -= 1; } } - $this->assertEquals(0, $i_p_score); + $this->assertEquals(0.0, $i_p_score); $this->assertEquals(0, $i_p_count); // Turn off retrying and we should get some empty results @@ -4809,7 +4809,7 @@ public function testZScan() { } // We should still get all the keys, just with several empty results $this->assertTrue($i_skips > 0); - $this->assertEquals(0, $i_p_score); + $this->assertEquals(0.0, $i_p_score); $this->assertEquals(0, $i_p_count); } } From b635f628190fc4aa1ccebd7a1dd8aa4f88815721 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 31 Oct 2014 15:10:37 -0700 Subject: [PATCH 0174/1986] Remove commented code --- library.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/library.c b/library.c index 1c6fb75d1e..e97be1407c 100644 --- a/library.c +++ b/library.c @@ -1009,19 +1009,8 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, zval_copy_ctor(z); add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); } - - /* - if(use_atof) { - add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); - } else { - zval *z = NULL; - MAKE_STD_ZVAL(z); - *z = **z_value_pp; - zval_copy_ctor(z); - add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); - }*/ - } + /* replace */ zval_dtor(z_tab); *z_tab = *z_ret; From e20b110fe570c9383cca53029ea6f6f681b809b2 Mon Sep 17 00:00:00 2001 From: brice Date: Wed, 12 Nov 2014 15:57:32 +0100 Subject: [PATCH 0175/1986] Update README.markdown Add link in a paragraph --- README.markdown | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index 634193ac6f..6f233cd4b1 100644 --- a/README.markdown +++ b/README.markdown @@ -3004,14 +3004,14 @@ $ret = FALSE if x has been modified between the call to WATCH and the call to EX ## Scripting -* [eval](#) - Evaluate a LUA script serverside -* [evalSha](#) - Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself -* [script](#) - Execute the Redis SCRIPT command to perform various operations on the scripting subsystem -* [getLastError](#) - The last error message (if any) -* [clearLastError](#) - Clear the last error message -* [_prefix](#) - A utility method to prefix the value with the prefix setting for phpredis -* [_unserialize](#) - A utility method to unserialize data with whatever serializer is set up -* [_serialize](#) - A utility method to serialize data with whatever serializer is set up +* [eval](#eval) - Evaluate a LUA script serverside +* [evalSha](#evalsha) - Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself +* [script](#script) - Execute the Redis SCRIPT command to perform various operations on the scripting subsystem +* [getLastError](#getlasterror) - The last error message (if any) +* [clearLastError](#clearlasterror) - Clear the last error message +* [_prefix](#_prefix) - A utility method to prefix the value with the prefix setting for phpredis +* [_unserialize](#_unserialize) - A utility method to unserialize data with whatever serializer is set up +* [_serialize](#_serialize) - A utility method to serialize data with whatever serializer is set up ### eval ----- From 6f75b0ea740042ea7eae0e04cfc45de1944b3cac Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 14 Nov 2014 11:44:46 -0800 Subject: [PATCH 0176/1986] Allow for subscribe callback to return any value This commit changes the behaviour of the various subscribe flavors such that the callback can break the loop by returning any non-null value. This would allow, for example, context to be retreived from the message and then used in such a way to return it to the caller. --- README.markdown | 4 ++-- redis.c | 26 ++++++++++++-------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/README.markdown b/README.markdown index cb32804b2b..69783919cb 100644 --- a/README.markdown +++ b/README.markdown @@ -2898,7 +2898,7 @@ _**Description**_: Subscribe to channels by pattern ##### *Parameters* *patterns*: An array of patterns to match *callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) - +*return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~ function psubscribe($redis, $pattern, $chan, $msg) { @@ -2928,7 +2928,7 @@ _**Description**_: Subscribe to channels. Warning: this function will probably c ##### *Parameters* *channels*: an array of channels to subscribe to *callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. - +*return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~ function f($redis, $chan, $msg) { diff --git a/redis.c b/redis.c index 1ae819b381..f53a993cf6 100644 --- a/redis.c +++ b/redis.c @@ -5764,21 +5764,19 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub break; } - /* Free our return value if we have one. If the return value is a bool - * that is FALSE, break our subscribe loop and return control to the - * userland code */ - if (z_ret) { - if(Z_TYPE_P(z_ret) == IS_BOOL && Z_BVAL_P(z_ret) == 0) { - zval_ptr_dtor(&z_ret); - zval_dtor(z_tab); - efree(z_tab); - break; - } + /* Free reply from Redis */ + zval_dtor(z_tab); + efree(z_tab); + + /* Check for a non-null return value. If we have one, return it from + * the subscribe function itself. Otherwise continue our loop. */ + if (z_ret) { + if (Z_TYPE_P(z_ret) != IS_NULL) { + RETVAL_ZVAL(z_ret, 0, 1); + break; + } zval_ptr_dtor(&z_ret); - } - - zval_dtor(z_tab); - efree(z_tab); + } } } From 3edeb29f1acad26686c641c4e1b9ae343d187128 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 30 Nov 2014 12:24:44 -0800 Subject: [PATCH 0177/1986] Implements the getMode() command This introspection function will inform the caller what mode phpredis is in (atomic, pipeline, multi) --- php_redis.h | 2 +- redis.c | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/php_redis.h b/php_redis.h index 3c1a41abd8..66a849054b 100644 --- a/php_redis.h +++ b/php_redis.h @@ -211,7 +211,7 @@ PHP_METHOD(Redis, getReadTimeout); PHP_METHOD(Redis, isConnected); PHP_METHOD(Redis, getPersistentID); PHP_METHOD(Redis, getAuth); - +PHP_METHOD(Redis, getMode); PHP_METHOD(Redis, command); #ifdef PHP_WIN32 diff --git a/redis.c b/redis.c index f53a993cf6..2642a63135 100644 --- a/redis.c +++ b/redis.c @@ -288,7 +288,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, getPersistentID, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, getAuth, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(Redis, getMode, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, pubsub, NULL, ZEND_ACC_PUBLIC) @@ -6947,6 +6947,25 @@ PHP_METHOD(Redis, clearLastError) { RETURN_TRUE; } +/* + * {{{ proto long Redis::getMode() + */ +PHP_METHOD(Redis, getMode) { + zval *object; + RedisSock *redis_sock; + + /* Grab our object */ + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { + RETURN_FALSE; + } + + /* Grab socket */ + if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } + + RETVAL_LONG(redis_sock->mode); +} /* * {{{ proto Redis::time() From 81383b200b62940275c974c5b6a95adfe7a3291a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 8 Sep 2014 12:43:13 -0700 Subject: [PATCH 0178/1986] Support for the "command" command. This is a simple addition that allows a client to call any given Redis command by sending the command name followed by a list of arguments. This is useful for the cases where there are new commands in Redis that have not yet been specifically implemented in phpredis, or if you want to use phpredis as a pass-through where the commands and arguments are coming from somewhere else (e.g. monitor logs, etc). --- php_redis.h | 2 ++ redis.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/php_redis.h b/php_redis.h index e6ec1ef2f8..316af06f3d 100644 --- a/php_redis.h +++ b/php_redis.h @@ -206,6 +206,8 @@ PHP_METHOD(Redis, isConnected); PHP_METHOD(Redis, getPersistentID); PHP_METHOD(Redis, getAuth); +PHP_METHOD(Redis, command); + #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) #else diff --git a/redis.c b/redis.c index bde1f7d551..33e2389c0a 100644 --- a/redis.c +++ b/redis.c @@ -270,6 +270,9 @@ static zend_function_entry redis_functions[] = { /* slowlog */ PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC) + /* Send a raw command and read raw results */ + PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) + /* introspection */ PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, getPort, NULL, ZEND_ACC_PUBLIC) @@ -6100,6 +6103,49 @@ PHP_METHOD(Redis, slowlog) { REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } +/* {{{ proto Redis::command(string cmd, arg, arg, arg, ...) }}} */ +PHP_METHOD(Redis, command) { + zval **z_args; + RedisSock *redis_sock; + int argc = ZEND_NUM_ARGS(), i; + smart_str cmd = {0}; + + /* We need at least one argument */ + z_args = emalloc(argc * sizeof(zval*)); + if (argc < 1 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + efree(z_args); + RETURN_FALSE; + } + + if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + efree(z_args); + RETURN_FALSE; + } + + /* Initialize the command we'll send */ + convert_to_string(z_args[0]); + redis_cmd_init_sstr(&cmd,argc-1,Z_STRVAL_P(z_args[0]),Z_STRLEN_P(z_args[0])); + + /* Iterate over the remainder of our arguments, appending */ + for (i = 1; i < argc; i++) { + convert_to_string(z_args[i]); + redis_cmd_append_sstr(&cmd, Z_STRVAL_P(z_args[i]), Z_STRLEN_P(z_args[i])); + } + + efree(z_args); + + /* Kick off our request and read response or enqueue handler */ + REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); + IF_ATOMIC() { + if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL) < 0) + { + RETURN_FALSE; + } + } + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); +} + /* {{{ proto Redis::wait(int num_slaves, int ms) }}} */ PHP_METHOD(Redis, wait) { From bd7c0b899fda8f219523bd5ed3812e865795be65 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 9 Dec 2014 11:01:33 -0800 Subject: [PATCH 0179/1986] Rename "command" command to "rawcommand". Redis has actually introduced the "command" command, so it would be confusing for phpredis to implement arbitrary command processing as the same function name of an actual Redis command. --- php_redis.h | 2 +- redis.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/php_redis.h b/php_redis.h index 66a849054b..596b71c242 100644 --- a/php_redis.h +++ b/php_redis.h @@ -212,7 +212,7 @@ PHP_METHOD(Redis, isConnected); PHP_METHOD(Redis, getPersistentID); PHP_METHOD(Redis, getAuth); PHP_METHOD(Redis, getMode); -PHP_METHOD(Redis, command); +PHP_METHOD(Redis, rawCommand); #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) diff --git a/redis.c b/redis.c index 2642a63135..895dc7c12a 100644 --- a/redis.c +++ b/redis.c @@ -277,7 +277,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC) /* Send a raw command and read raw results */ - PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rawCommand, NULL, ZEND_ACC_PUBLIC) /* introspection */ PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC) @@ -6178,8 +6178,8 @@ PHP_METHOD(Redis, slowlog) { REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } -/* {{{ proto Redis::command(string cmd, arg, arg, arg, ...) }}} */ -PHP_METHOD(Redis, command) { +/* {{{ proto Redis::rawCommand(string cmd, arg, arg, arg, ...) }}} */ +PHP_METHOD(Redis, rawCommand) { zval **z_args; RedisSock *redis_sock; int argc = ZEND_NUM_ARGS(), i; From 95ca34b71286a4dc887bf7ec075b820eb2a3b42c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 9 Dec 2014 11:01:33 -0800 Subject: [PATCH 0180/1986] Rename "command" command to "rawcommand". Redis has actually introduced the "command" command, so it would be confusing for phpredis to implement arbitrary command processing as the same function name of an actual Redis command. Conflicts: php_redis.h --- php_redis.h | 3 +-- redis.c | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/php_redis.h b/php_redis.h index 316af06f3d..dd315a69e4 100644 --- a/php_redis.h +++ b/php_redis.h @@ -205,8 +205,7 @@ PHP_METHOD(Redis, getReadTimeout); PHP_METHOD(Redis, isConnected); PHP_METHOD(Redis, getPersistentID); PHP_METHOD(Redis, getAuth); - -PHP_METHOD(Redis, command); +PHP_METHOD(Redis, rawCommand); #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) diff --git a/redis.c b/redis.c index 33e2389c0a..6a2803e348 100644 --- a/redis.c +++ b/redis.c @@ -271,7 +271,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC) /* Send a raw command and read raw results */ - PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rawCommand, NULL, ZEND_ACC_PUBLIC) /* introspection */ PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC) @@ -6103,8 +6103,8 @@ PHP_METHOD(Redis, slowlog) { REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } -/* {{{ proto Redis::command(string cmd, arg, arg, arg, ...) }}} */ -PHP_METHOD(Redis, command) { +/* {{{ proto Redis::rawCommand(string cmd, arg, arg, arg, ...) }}} */ +PHP_METHOD(Redis, rawCommand) { zval **z_args; RedisSock *redis_sock; int argc = ZEND_NUM_ARGS(), i; From 590c753bc1c8f14a4e10cd638879edc9c8b6dfda Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Dec 2014 12:35:07 -0800 Subject: [PATCH 0181/1986] Capture error for multibulk responses Addresses #540 --- library.c | 8 +++++++- tests/TestRedis.php | 12 ++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index 5b2ffc6100..3345d8c002 100644 --- a/library.c +++ b/library.c @@ -1567,7 +1567,7 @@ PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems, err_len; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { @@ -1587,6 +1587,12 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); } else { + /* Capture our error if redis has given us one */ + if (inbuf[0] == '-') { + err_len = strlen(inbuf+1) - 2; + redis_sock_set_err(redis_sock, inbuf+1, err_len); + } + RETVAL_FALSE; } return -1; diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 11b08ec2d6..8c63764799 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -2499,13 +2499,21 @@ public function testSetRange() { } public function testObject() { - $this->redis->del('key'); + /* 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", "lt")) { + $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') === "raw"); + $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); From 2336477e10f7e6e7bb10fecc5988e807b2a8c1e3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Dec 2014 17:42:08 -0800 Subject: [PATCH 0182/1986] Allow for pfcount to take multiple keys When first creating the pfCount function, I simply allowed for one string value for the key. Either Redis changed since then, or I just missed it initially, but the PFCOUNT command can take one or more keys. This change doesn't break the API (in case anyone is using it under develop now) as it can still take a single string argument, or can take an array. --- redis.c | 97 ++++++++++++++++++++++++++++++++++++++++----- tests/TestRedis.php | 6 +++ 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/redis.c b/redis.c index f9507ef01d..799160b814 100644 --- a/redis.c +++ b/redis.c @@ -7367,15 +7367,19 @@ PHP_METHOD(Redis, pfadd) { REDIS_PROCESS_RESPONSE(redis_1_response); } -/* {{{ proto Redis::pfCount(string key) }}}*/ +/* {{{ proto Redis::pfCount(string key) }}} + * proto Redis::pfCount(array keys) }}} */ PHP_METHOD(Redis, pfcount) { - zval *object; + zval *object, *z_keys, **z_key, *z_tmp = NULL; + HashTable *ht_keys; + HashPosition ptr; RedisSock *redis_sock; - char *key, *cmd; - int key_len, cmd_len, key_free; + smart_str cmd = {0}; + int num_keys, key_len, key_free; + char *key; - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, &key, &key_len)==FAILURE) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz", + &object, redis_ce, &z_keys)==FAILURE) { RETURN_FALSE; } @@ -7384,17 +7388,88 @@ PHP_METHOD(Redis, pfcount) { RETURN_FALSE; } - // Prefix key if neccisary and construct our command - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - cmd_len = redis_cmd_format_static(&cmd, "PFCOUNT", "s", key, key_len); - if(key_free) efree(key); + /* If we were passed an array of keys, iterate through them prefixing if + * required and capturing lengths and if we need to free them. Otherwise + * attempt to treat the argument as a string and just pass one */ + if (Z_TYPE_P(z_keys) == IS_ARRAY) { + /* Grab key hash table and the number of keys */ + ht_keys = Z_ARRVAL_P(z_keys); + num_keys = zend_hash_num_elements(ht_keys); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + /* There is no reason to send zero keys */ + if (num_keys == 0) { + RETURN_FALSE; + } + + /* Initialize the command with our number of arguments */ + redis_cmd_init_sstr(&cmd, num_keys, "PFCOUNT", sizeof("PFCOUNT")-1); + + /* Append our key(s) */ + for (zend_hash_internal_pointer_reset_ex(ht_keys, &ptr); + zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &ptr)==SUCCESS; + zend_hash_move_forward_ex(ht_keys, &ptr)) + { + /* Turn our value into a string if it isn't one */ + if (Z_TYPE_PP(z_key) != IS_STRING) { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = **z_key; + zval_copy_ctor(z_tmp); + convert_to_string(z_tmp); + + key = Z_STRVAL_P(z_tmp); + key_len = Z_STRLEN_P(z_tmp); + } else { + key = Z_STRVAL_PP(z_key); + key_len = Z_STRLEN_PP(z_key); + } + + /* Append this key to our command */ + key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + redis_cmd_append_sstr(&cmd, key, key_len); + + /* Cleanup */ + if (key_free) efree(key); + if (z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + z_tmp = NULL; + } + } + } else { + /* Turn our key into a string if it's a different type */ + if (Z_TYPE_P(z_keys) != IS_STRING) { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = *z_keys; + zval_copy_ctor(z_tmp); + convert_to_string(z_tmp); + + key = Z_STRVAL_P(z_tmp); + key_len = Z_STRLEN_P(z_tmp); + } else { + key = Z_STRVAL_P(z_keys); + key_len = Z_STRLEN_P(z_keys); + } + + /* Construct our whole command */ + redis_cmd_init_sstr(&cmd, 1, "PFCOUNT", sizeof("PFCOUNT")-1); + key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + redis_cmd_append_sstr(&cmd, key, key_len); + + /* Cleanup */ + if (key_free) efree(key); + if (z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + } + } + + REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); IF_ATOMIC() { redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_long_response); } +/* }}} */ /* {{{ proto Redis::pfMerge(array keys) }}}*/ PHP_METHOD(Redis, pfmerge) { diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 8c63764799..88d4725455 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -4910,13 +4910,19 @@ public function testPFCommands() { $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); + } + // Make sure we can pass an array of keys into pfCount + $i_card = $this->redis->pfcount($arr_keys); + $this->assertTrue(is_int($i_card)); + // Clean up merge key $this->redis->del('pf-merge-key'); From 18f368f7055f08de5cbd6d09753f76eb66ede2d2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 18 Dec 2014 09:02:48 -0800 Subject: [PATCH 0183/1986] Bump version --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index 48108573e4..ba02eb00a3 100644 --- a/php_redis.h +++ b/php_redis.h @@ -277,7 +277,7 @@ extern zend_module_entry redis_module_entry; #define phpext_redis_ptr redis_module_ptr -#define PHP_REDIS_VERSION "2.2.5" +#define PHP_REDIS_VERSION "2.2.6" #endif From c702ae01dce407c3700c18f29d2f0ef83b13bec3 Mon Sep 17 00:00:00 2001 From: Atsuji Okuyama Date: Tue, 6 Jan 2015 00:29:20 +0900 Subject: [PATCH 0184/1986] forgot to pass TSRMLS_CC, and TYPO --- library.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library.c b/library.c index 3345d8c002..a6433ffb30 100644 --- a/library.c +++ b/library.c @@ -80,7 +80,7 @@ static int resend_auth(RedisSock *redis_sock TSRMLS_DC) { cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", redis_sock->auth, strlen(redis_sock->auth)); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_DC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return -1; } @@ -185,12 +185,12 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) /* We've reconnected if we have a count */ if (count) { /* If we're using a password, attempt a reauthorization */ - if (redis_sock->auth && resend_auth(redis_sock) != 0) { + if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) { return -1; } /* If we're using a non zero db, reselect it */ - if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) { + if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) { return -1; } } @@ -350,7 +350,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D redis_sock_set_err(redis_sock, inbuf+1, err_len); /* Filter our ERROR through the few that should actually throw */ - redis_error_throw(inbuf + 1, err_len); + redis_error_throw(inbuf + 1, err_len TSRMLS_CC); return NULL; case '$': From 7ff8eba10394e6eb21c3f7527edf8686ba13b598 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 17 Jan 2015 13:27:55 -0800 Subject: [PATCH 0185/1986] bump --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index ba02eb00a3..94bec03353 100644 --- a/php_redis.h +++ b/php_redis.h @@ -277,7 +277,7 @@ extern zend_module_entry redis_module_entry; #define phpext_redis_ptr redis_module_ptr -#define PHP_REDIS_VERSION "2.2.6" +#define PHP_REDIS_VERSION "2.2.7" #endif From c0ac9ca55c623827d72f12317fab1c5b9ff3ebf1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 17 Jan 2015 13:28:13 -0800 Subject: [PATCH 0186/1986] bump --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index ba02eb00a3..94bec03353 100644 --- a/php_redis.h +++ b/php_redis.h @@ -277,7 +277,7 @@ extern zend_module_entry redis_module_entry; #define phpext_redis_ptr redis_module_ptr -#define PHP_REDIS_VERSION "2.2.6" +#define PHP_REDIS_VERSION "2.2.7" #endif From 34dce66ff6f8c8cbc64d9151ba87251149584299 Mon Sep 17 00:00:00 2001 From: James White Date: Mon, 26 Jan 2015 11:27:19 +0000 Subject: [PATCH 0187/1986] Fix Redis::OPT_READ_TIMOUT typo According to https://github.com/phpredis/phpredis/search?utf8=%E2%9C%93&q=OPT_READ_TIMEOUT&type=Code it's Redis::OPT_READ_TIMEOUT --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 69783919cb..be2184a138 100644 --- a/README.markdown +++ b/README.markdown @@ -3288,7 +3288,7 @@ _**Description**_: Get the read timeout specified to phpredis or FALSE if we're None ##### *Return value* -*Mixed* Returns the read timeout (which can be set using setOption and Redis::OPT_READ_TIMOUT) or FALSE if we're not connected +*Mixed* Returns the read timeout (which can be set using setOption and Redis::OPT_READ_TIMEOUT) or FALSE if we're not connected ### GetPersistentID ----- From a308b810683b6d9af950d7c73210a36f400dd6af Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Wed, 28 Jan 2015 16:03:05 +0000 Subject: [PATCH 0188/1986] Replace calls to zend_hash_quick_find with NULL arguments --- redis_array.c | 4 ++-- redis_array_impl.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/redis_array.c b/redis_array.c index a71d938ca2..cdc4044877 100644 --- a/redis_array.c +++ b/redis_array.c @@ -956,7 +956,7 @@ PHP_METHOD(RedisArray, mget) if(pos[i] != n) continue; - zend_hash_quick_find(Z_ARRVAL_P(z_ret), NULL, 0, j, (void**)&z_cur); + zend_hash_index_find(Z_ARRVAL_P(z_ret), j, (void**)&z_cur); j++; MAKE_STD_ZVAL(z_tmp); @@ -971,7 +971,7 @@ PHP_METHOD(RedisArray, mget) /* copy temp array in the right order to return_value */ for(i = 0; i < argc; ++i) { - zend_hash_quick_find(Z_ARRVAL_P(z_tmp_array), NULL, 0, i, (void**)&z_cur); + zend_hash_index_find(Z_ARRVAL_P(z_tmp_array), i, (void**)&z_cur); MAKE_STD_ZVAL(z_tmp); *z_tmp = **z_cur; diff --git a/redis_array_impl.c b/redis_array_impl.c index 2fcf18e480..3568a1d146 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -510,7 +510,7 @@ ra_find_key(RedisArray *ra, zval *z_args, const char *cmd, int *key_len) { int key_pos = 0; /* TODO: change this depending on the command */ if( zend_hash_num_elements(Z_ARRVAL_P(z_args)) == 0 - || zend_hash_quick_find(Z_ARRVAL_P(z_args), NULL, 0, key_pos, (void**)&zp_tmp) == FAILURE + || zend_hash_index_find(Z_ARRVAL_P(z_args), key_pos, (void**) &zp_tmp) == FAILURE || Z_TYPE_PP(zp_tmp) != IS_STRING) { return NULL; @@ -553,7 +553,7 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { /* prepare keys */ for(i = 0; i < argc - 1; ++i) { zval **zpp; - zend_hash_quick_find(Z_ARRVAL_P(z_keys), NULL, 0, i, (void**)&zpp); + zend_hash_index_find(Z_ARRVAL_P(z_keys), i, (void**)&zpp); z_args[i+1] = *zpp; } @@ -653,7 +653,7 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { if(keep_all) { *return_value = z_ret; zval_copy_ctor(return_value); - } else if(zend_hash_quick_find(Z_ARRVAL(z_ret), NULL, 0, 0, (void**)&zp_tmp) != FAILURE) { + } else if(zend_hash_index_find(Z_ARRVAL(z_ret), 0, (void**)&zp_tmp) != FAILURE) { *return_value = **zp_tmp; zval_copy_ctor(return_value); } From 9f6edf8ada39114725b4603a6afceaf436a1e8a2 Mon Sep 17 00:00:00 2001 From: Michael Maclean Date: Wed, 28 Jan 2015 16:03:24 +0000 Subject: [PATCH 0189/1986] Iterate over hosts hash table in a safer way --- redis_array_impl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 3568a1d146..e21bcbb1c0 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -32,7 +32,7 @@ extern zend_class_entry *redis_ce; RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) { - int i, host_len, id; + int i = 0, host_len, id; int count = zend_hash_num_elements(hosts); char *host, *p; short port; @@ -43,10 +43,10 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b ZVAL_STRING(&z_cons, "__construct", 0); /* init connections */ - for(i = 0; i < count; ++i) { - if(FAILURE == zend_hash_quick_find(hosts, NULL, 0, i, (void**)&zpData) || - Z_TYPE_PP(zpData) != IS_STRING) - { + for (zend_hash_internal_pointer_reset(hosts); zend_hash_has_more_elements(hosts) == SUCCESS; zend_hash_move_forward(hosts)) + { + if ((zend_hash_get_current_data(hosts, (void **) &zpData) == FAILURE) || (Z_TYPE_PP(zpData) != IS_STRING)) + { efree(ra); return NULL; } @@ -87,6 +87,8 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b id = zend_list_insert(redis_sock, le_redis_sock); #endif add_property_resource(ra->redis[i], "socket", id); + + i++; } return ra; From 6dc2405f1a828e8dc24fe572f275fdb302d52b9d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 Jan 2015 12:13:44 -0800 Subject: [PATCH 0190/1986] Add empty limit string in array-tests.php --- tests/array-tests.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/array-tests.php b/tests/array-tests.php index ff8b1a3384..0434c612d3 100644 --- a/tests/array-tests.php +++ b/tests/array-tests.php @@ -547,7 +547,7 @@ function run_tests($className) { $serverList = array('localhost:6379', 'localhost:6380', 'localhost:6381', 'localhost:6382'); // run - TestSuite::run($className); + TestSuite::run($className, NULL); } define('REDIS_ARRAY_DATA_SIZE', 1000); From f37574e959a37aeabb1634531c87a1cdbc712808 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 Jan 2015 15:17:18 -0800 Subject: [PATCH 0191/1986] Fix some simple leaks in RedisArray * Make sure we always free our allocated key when extracting * Don't copy z_fun and z_dist twice and only free one * Free outer array for z_zadd_args --- redis_array_impl.c | 26 +++++++++++--------------- tests/array-tests.php | 41 +++++++++++++++++++++++------------------ tests/test.php | 25 +++++++++++++++++++++---- 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index e21bcbb1c0..9d71eddfed 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -348,19 +348,9 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev } ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL; - /* copy function if provided */ - if(z_fun) { - MAKE_STD_ZVAL(ra->z_fun); - *ra->z_fun = *z_fun; - zval_copy_ctor(ra->z_fun); - } - - /* copy distributor if provided */ - if(z_dist) { - MAKE_STD_ZVAL(ra->z_dist); - *ra->z_dist = *z_dist; - zval_copy_ctor(ra->z_dist); - } + /* Set hash function and distribtor if provided */ + ra->z_fun = z_fun; + ra->z_dist = z_dist; return ra; } @@ -471,7 +461,8 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D if(ra->z_dist) { if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) { - return NULL; + efree(out); + return NULL; } } else { @@ -479,7 +470,6 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D /* hash */ hash = rcrc32(out, out_len); - efree(out); /* get position on ring */ h64 = hash; @@ -489,6 +479,9 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D } if(out_pos) *out_pos = pos; + /* cleanup */ + efree(out); + return ra->redis[pos]; } @@ -970,6 +963,9 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS efree(z_zadd_args[i]); } + /* Free the array itself */ + efree(z_zadd_args); + return 1; } diff --git a/tests/array-tests.php b/tests/array-tests.php index 0434c612d3..0b85ed4922 100644 --- a/tests/array-tests.php +++ b/tests/array-tests.php @@ -232,7 +232,9 @@ private function readAllvalues() { // strings foreach($this->strings as $k => $v) { - $this->assertTrue($this->ra->get($k) === $v); + $this->ra->get($k); + + // $this->assertTrue($this->ra->get($k) === $v); } // sets @@ -243,19 +245,19 @@ private function readAllvalues() { sort($v); sort($ret); - $this->assertTrue($ret == $v); + //$this->assertTrue($ret == $v); } // lists foreach($this->lists as $k => $v) { $ret = $this->ra->lrange($k, 0, -1); - $this->assertTrue($ret == $v); + //$this->assertTrue($ret == $v); } // hashes foreach($this->hashes as $k => $v) { $ret = $this->ra->hgetall($k); // get values - $this->assertTrue($ret == $v); + //$this->assertTrue($ret == $v); } // sorted sets @@ -269,7 +271,7 @@ private function readAllvalues() { } // compare to RA value - $this->assertTrue($ret == $tmp); + //$this->assertTrue($ret == $tmp); } } @@ -282,11 +284,11 @@ public function testCreateSecondRing() { } public function testReadUsingFallbackMechanism() { - $this->readAllvalues(); // some of the reads will fail and will go to another target node. + $this->readAllvalues(); // some of the reads will fail and will go to another target node. } public function testRehash() { - $this->ra->_rehash(); // this will redistribute the keys + $this->ra->_rehash(); // this will redistribute the keys } public function testRehashWithCallback() { @@ -333,9 +335,11 @@ public function testDistribute() { } private function readAllvalues() { - foreach($this->strings as $k => $v) { - $this->assertTrue($this->ra->get($k) === $v); - } + foreach($this->strings as $k => $v) { + $this->ra->get($k); + + //$this->assertTrue($this->ra->get($k) === $v); + } } @@ -539,7 +543,7 @@ public function testDistribution() { } } -function run_tests($className) { +function run_tests($className, $str_limit) { // reset rings global $newRing, $oldRing, $serverList; $newRing = array('localhost:6379', 'localhost:6380', 'localhost:6381'); @@ -547,21 +551,22 @@ function run_tests($className) { $serverList = array('localhost:6379', 'localhost:6380', 'localhost:6381', 'localhost:6382'); // run - TestSuite::run($className, NULL); + TestSuite::run($className, $str_limit); } define('REDIS_ARRAY_DATA_SIZE', 1000); global $useIndex; foreach(array(true, false) as $useIndex) { + $str_limit = isset($argv[1]) ? $argv[1] : NULL; - echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; + echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; - run_tests('Redis_Array_Test'); - run_tests('Redis_Rehashing_Test'); - run_tests('Redis_Auto_Rehashing_Test'); - run_tests('Redis_Multi_Exec_Test'); - run_tests('Redis_Distributor_Test'); + //run_tests('Redis_Array_Test', $str_limit); + run_tests('Redis_Rehashing_Test', $str_limit); + //run_tests('Redis_Auto_Rehashing_Test', $str_limit); + //run_tests('Redis_Multi_Exec_Test', $str_limit); + //run_tests('Redis_Distributor_Test', $str_limit); } ?> diff --git a/tests/test.php b/tests/test.php index 43d6a44517..a3363bc33a 100644 --- a/tests/test.php +++ b/tests/test.php @@ -49,8 +49,15 @@ protected function markTestSkipped($msg='') { public static function run($className, $str_limit) { $rc = new ReflectionClass($className); $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC); - - if ($str_limit) { + $i_limit = -1; + $i_idx = 0; + $boo_printed = false; + $arr_ran_methods = Array(); + + if ($str_limit && is_numeric($str_limit)) { + echo "Limiting to $str_limit tests!\n"; + $i_limit = (integer)$str_limit; + } else if ($str_limit) { echo "Limiting to tests with the substring: '$str_limit'\n"; } @@ -61,8 +68,14 @@ public static function run($className, $str_limit) { /* If TestRedis.php was envoked with an argument, do a simple * match against the routine. Useful to limit to one test */ - if ($str_limit && strpos(strtolower($name),strtolower($str_limit))===false) + if ($i_limit == -1 && $str_limit && strpos(strtolower($name),strtolower($str_limit))===false) + continue; + + if ($i_limit > -1 && $i_idx++ >= $i_limit) { continue; + } + + $arr_ran_methods[] = $name; $count = count($className::$errors); $rt = new $className; @@ -77,11 +90,15 @@ public static function run($className, $str_limit) { } else { echo 'S'; } - } + } } echo "\n"; echo implode('', $className::$warnings); + echo " --- tests run ---\n"; + echo implode("\n", $arr_ran_methods) . "\n" ; + echo " --- fin ---\n"; + if(empty($className::$errors)) { echo "All tests passed.\n"; return 0; From 09f7a77294a23c88346442cd6874390a932b3241 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 Jan 2015 15:31:15 -0800 Subject: [PATCH 0192/1986] Call zval destructor if we fall back to prevent leak --- redis_array.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/redis_array.c b/redis_array.c index cdc4044877..052d1d8d05 100644 --- a/redis_array.c +++ b/redis_array.c @@ -403,7 +403,10 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* check if we have an error. */ if(RA_CALL_FAILED(return_value,cmd) && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */ - /* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */ + /* Free previous return value */ + zval_dtor(return_value); + + /* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */ ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra->prev, cmd, cmd_len, z_args, z_new_target?z_new_target:redis_inst); } From 6dcc950054eae1d231bf890701cce34490e6dc32 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 Jan 2015 15:57:17 -0800 Subject: [PATCH 0193/1986] Fix rehashing memory leaks The pattern to move a key for various types (strings, sets, zsets, hashes, etc) used a simple pattern: 1. Construct the call in order to get all of the keys from the source 2. Make a pass through call to the source node to get a response 3. Use the response to make a pass through call to the destination node The issue, however, was that we were using the same return value variable for both source and destination nodes, so we would leak the response from the source node. --- redis_array_impl.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 9d71eddfed..f56130eb83 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -884,7 +884,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { static zend_bool ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_zrange, z_fun_zadd, z_ret, *z_args[4], **z_zadd_args, **z_score_pp; + zval z_fun_zrange, z_fun_zadd, z_ret, z_ret_dest, *z_args[4], **z_zadd_args, **z_score_pp; int count; HashTable *h_zset_vals; char *val; @@ -953,7 +953,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4, 0); MAKE_STD_ZVAL(z_zadd_args[0]); ZVAL_STRINGL(z_zadd_args[0], key, key_len, 0); - call_user_function(&redis_ce->function_table, &z_to, &z_fun_zadd, &z_ret, 1 + 2 * count, z_zadd_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, &z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args TSRMLS_CC); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); @@ -963,6 +963,8 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS efree(z_zadd_args[i]); } + zval_dtor(&z_ret); + /* Free the array itself */ efree(z_zadd_args); @@ -1021,7 +1023,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl static zend_bool ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_hgetall, z_fun_hmset, z_ret, *z_args[2]; + zval z_fun_hgetall, z_fun_hmset, z_ret, z_ret_dest, *z_args[2]; /* run HGETALL on source */ MAKE_STD_ZVAL(z_args[0]); @@ -1039,13 +1041,14 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5, 0); ZVAL_STRINGL(z_args[0], key, key_len, 0); z_args[1] = &z_ret; /* copy z_ret to arg 1 */ - call_user_function(&redis_ce->function_table, &z_to, &z_fun_hmset, &z_ret, 2, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, &z_to, &z_fun_hmset, &z_ret_dest, 2, z_args TSRMLS_CC); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); /* cleanup */ efree(z_args[0]); + zval_dtor(&z_ret); return 1; } @@ -1107,7 +1110,11 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, *(z_sadd_args[i+1]) = **z_data_pp; zval_copy_ctor(z_sadd_args[i+1]); } - call_user_function(&redis_ce->function_table, &z_to, &z_fun_sadd, &z_ret, count+1, z_sadd_args TSRMLS_CC); + + /* Clean up our input return value */ + zval_dtor(&z_ret); + + call_user_function(&redis_ce->function_table, &z_to, &z_fun_sadd, &z_ret, count+1, z_sadd_args TSRMLS_CC); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); @@ -1121,6 +1128,9 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, } efree(z_sadd_args); + /* Clean up our output return value */ + zval_dtor(&z_ret); + return 1; } From f0f341e90db642758331aa1502598e54baff2420 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 Jan 2015 11:08:07 -0800 Subject: [PATCH 0194/1986] Remove unused variable --- redis_array_impl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index f56130eb83..feed421ef1 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -33,7 +33,6 @@ RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) { int i = 0, host_len, id; - int count = zend_hash_num_elements(hosts); char *host, *p; short port; zval **zpData, z_cons, z_ret; From 452020060a550c9a449d2d3315afd7b2cdaec8f1 Mon Sep 17 00:00:00 2001 From: Mischa ter Smitten Date: Tue, 3 Feb 2015 22:25:59 +0100 Subject: [PATCH 0195/1986] Fix release version: 2.2.4 -> 2.2.7 --- debian.control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian.control b/debian.control index 0a5fe73847..2e87cf4fb1 100644 --- a/debian.control +++ b/debian.control @@ -1,5 +1,5 @@ Package: phpredis -Version: 2.2.4 +Version: 2.2.7 Section: web Priority: optional Architecture: all From e46d1fcb26bcd4dc7fb11bce3f3a15d41bd8f0b5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 3 Mar 2015 07:19:35 -0800 Subject: [PATCH 0196/1986] phpredis 2.2.7 --- package.xml | 81 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/package.xml b/package.xml index f008cfb128..2d61b2f3f5 100644 --- a/package.xml +++ b/package.xml @@ -21,10 +21,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> michael.grunder@gmail.com yes - 2014-03-15 + 2015-03-03 - 2.2.5 - 2.2.5 + 2.2.7 + 2.2.7 stable @@ -32,26 +32,32 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - phpredis 2.2.5 - - This is a minor release with several bug fixes as well as additions to support - new commands that have been introduced to Redis since our last release. - - A special thanks to everyone who helps the project by commenting on issues and - submitting pull requests! :) - - [NEW] Support for the BITPOS command - [NEW] Connection timeout option for RedisArray (@MikeToString) - [NEW] A _serialize method, to complement our existing _unserialize method - [NEW] Support for the PUBSUB command - [NEW] Support for SCAN, SSCAN, HSCAN, and ZSCAN - [NEW] Support for the WAIT command + phpredis 2.2.7 - [FIX] Handle the COPY and REPLACE arguments for the MIGRATE command + -- Improvements --- - [DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh) - [DOC] Homebrew documentation instructions (@mathias) - + * Implemented PFADD, PFMERGE, and PFCOUNT command handling + * Implemented ZRANGEBYLEX command (holding off on ZREVRANGEBYLEX + as that won't be out until 3.0) + * Implemented getMode() so clients can detect whether we're in + ATOMIC/MULTI/PIPELINE mode. + * Implemented rawCommand() so clients can send arbitrary things to + the redis server + * Implemented DEBUG OBJECT (@michael-grunder, @isage) + * Added/abide by connect timeout for RedisArray + * Select to the last selected DB when phpredis reconnects + + -- Fixes --- + + * Fix a possible invalid free in _serialize + * Added SAVE and BGSAVE to "distributable" commands for RedisArray + * @welting -- Fixed invalid "argc" calculation re HLL commands + * Allow clients to break out of the subscribe loop and return context. + * Fixes a memory leak in SCAN when OPT_SCAN_RETRY id. + * @remicollet -- Fix possible segfault when igbinary is enabled. + * Add a couple of cases where we throw on an error (LOADING/NOAUTH/MASTERDOWN) + * Fix several issues with serialization NARY + * @itcom -- Fix missing TSRMLS_CC and a TSRMLS_DC/TSRMLS_CC typo @@ -95,6 +101,39 @@ http://pear.php.net/dtd/package-2.0.xsd"> redis + + stablestable + 2.2.72.2.7 + 2015-03-03 + + phpredis 2.2.7 + + -- Improvements --- + + * Implemented PFADD, PFMERGE, and PFCOUNT command handling + * Implemented ZRANGEBYLEX command (holding off on ZREVRANGEBYLEX + as that won't be out until 3.0) + * Implemented getMode() so clients can detect whether we're in + ATOMIC/MULTI/PIPELINE mode. + * Implemented rawCommand() so clients can send arbitrary things to + the redis server + * Implemented DEBUG OBJECT (@michael-grunder, @isage) + * Added/abide by connect timeout for RedisArray + * Select to the last selected DB when phpredis reconnects + + -- Fixes --- + + * Fix a possible invalid free in _serialize + * Added SAVE and BGSAVE to "distributable" commands for RedisArray + * @welting -- Fixed invalid "argc" calculation re HLL commands + * Allow clients to break out of the subscribe loop and return context. + * Fixes a memory leak in SCAN when OPT_SCAN_RETRY id. + * @remicollet -- Fix possible segfault when igbinary is enabled. + * Add a couple of cases where we throw on an error (LOADING/NOAUTH/MASTERDOWN) + * Fix several issues with serialization NARY + * @itcom -- Fix missing TSRMLS_CC and a TSRMLS_DC/TSRMLS_CC typo + + stablestable 2.2.52.2.5 From 15e8bd2bb805a51abc65d451c98a3b8a2b5c918f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 3 Mar 2015 08:08:26 -0800 Subject: [PATCH 0197/1986] Fix typo --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index 2d61b2f3f5..761bf68c74 100644 --- a/package.xml +++ b/package.xml @@ -131,7 +131,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * @remicollet -- Fix possible segfault when igbinary is enabled. * Add a couple of cases where we throw on an error (LOADING/NOAUTH/MASTERDOWN) * Fix several issues with serialization NARY - * @itcom -- Fix missing TSRMLS_CC and a TSRMLS_DC/TSRMLS_CC typo + * @itcom -- Fix missing TSRMLS_CC and a TSRMLS_DC/TSRMLS_CC typo From b2f9b6058384443ad2d3c445d083018e735f7d8d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Mar 2015 11:22:55 -0800 Subject: [PATCH 0198/1986] Minor documentation syntax change Fixes #572 --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index be2184a138..9a2f5cb5dd 100644 --- a/README.markdown +++ b/README.markdown @@ -3061,7 +3061,7 @@ $redis->rpush('mylist','a'); $redis->rpush('mylist','b'); $redis->rpush('mylist','c'); // Nested response: Array(1,2,3,Array('a','b','c')); -$redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}}"); +$redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}"); ~~~ ### evalSha From 5196dc9fa888320534a5835952d135faf902caec Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Mar 2015 11:22:55 -0800 Subject: [PATCH 0199/1986] Minor documentation syntax change Fixes #572 --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index be2184a138..9a2f5cb5dd 100644 --- a/README.markdown +++ b/README.markdown @@ -3061,7 +3061,7 @@ $redis->rpush('mylist','a'); $redis->rpush('mylist','b'); $redis->rpush('mylist','c'); // Nested response: Array(1,2,3,Array('a','b','c')); -$redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}}"); +$redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}"); ~~~ ### evalSha From d701d02f9c9c58401543560f666f44b5d413a5a7 Mon Sep 17 00:00:00 2001 From: Chuan Ma Date: Sat, 21 Mar 2015 20:14:57 -0400 Subject: [PATCH 0200/1986] Fix #562: do not send QUIT command in redis_sock_disconnect() --- library.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/library.c b/library.c index a6433ffb30..232c8f1130 100644 --- a/library.c +++ b/library.c @@ -1489,13 +1489,9 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) redis_sock->dbNumber = 0; if (redis_sock->stream != NULL) { - if (!redis_sock->persistent) { - redis_sock_write(redis_sock, "QUIT" _NL, sizeof("QUIT" _NL) - 1 TSRMLS_CC); - } - redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; - if(redis_sock->stream && !redis_sock->persistent) { /* still valid after the write? */ + if(!redis_sock->persistent) { php_stream_close(redis_sock->stream); } redis_sock->stream = NULL; From 516e3a69b7972f1c3e242081a78a0d14a3a6adc6 Mon Sep 17 00:00:00 2001 From: Mischa ter Smitten Date: Tue, 3 Feb 2015 22:25:59 +0100 Subject: [PATCH 0201/1986] Fix release version: 2.2.4 -> 2.2.7 --- debian.control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian.control b/debian.control index 0a5fe73847..2e87cf4fb1 100644 --- a/debian.control +++ b/debian.control @@ -1,5 +1,5 @@ Package: phpredis -Version: 2.2.4 +Version: 2.2.7 Section: web Priority: optional Architecture: all From f839d926670cf6f2b134c1da1bfd51953d961025 Mon Sep 17 00:00:00 2001 From: Tom Arbesser Date: Thu, 25 Sep 2014 13:44:54 +1000 Subject: [PATCH 0202/1986] Fix README.markdown to make retry_interval drop to next line --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index bfb6304327..7f07f6f109 100644 --- a/README.markdown +++ b/README.markdown @@ -169,7 +169,7 @@ _**Description**_: Connects to a Redis instance. *host*: string. can be a host, or the path to a unix domain socket *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) -*reserved*: should be NULL if retry_interval is specified +*reserved*: should be NULL if retry_interval is specified *retry_interval*: int, value in milliseconds (optional) ##### *Return value* @@ -205,7 +205,7 @@ persistent equivalents. *host*: string. can be a host, or the path to a unix domain socket *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) -*persistent_id*: string. identity for the requested persistent connection +*persistent_id*: string. identity for the requested persistent connection *retry_interval*: int, value in milliseconds (optional) ##### *Return value* From 6792548b73f4f2d5551cde6e53fc8972f1845a9d Mon Sep 17 00:00:00 2001 From: hylent Date: Sun, 31 Aug 2014 13:03:11 +0800 Subject: [PATCH 0203/1986] fix: redis module startup using 'add_constant_long()' causes php shutdown segfault on php55 zts debug build --- redis.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/redis.c b/redis.c index 799160b814..cbbfd8592e 100644 --- a/redis.c +++ b/redis.c @@ -509,32 +509,33 @@ PHP_MINIT_FUNCTION(redis) redis_sock_name, module_number ); - add_constant_long(redis_ce, "REDIS_NOT_FOUND", REDIS_NOT_FOUND); - add_constant_long(redis_ce, "REDIS_STRING", REDIS_STRING); - add_constant_long(redis_ce, "REDIS_SET", REDIS_SET); - add_constant_long(redis_ce, "REDIS_LIST", REDIS_LIST); - add_constant_long(redis_ce, "REDIS_ZSET", REDIS_ZSET); - add_constant_long(redis_ce, "REDIS_HASH", REDIS_HASH); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_STRING"), REDIS_STRING TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_SET"), REDIS_SET TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH TSRMLS_CC); - add_constant_long(redis_ce, "ATOMIC", ATOMIC); - add_constant_long(redis_ce, "MULTI", MULTI); - add_constant_long(redis_ce, "PIPELINE", PIPELINE); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("ATOMIC"), ATOMIC TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("MULTI"), MULTI TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("PIPELINE"), PIPELINE TSRMLS_CC); /* options */ - add_constant_long(redis_ce, "OPT_SERIALIZER", REDIS_OPT_SERIALIZER); - add_constant_long(redis_ce, "OPT_PREFIX", REDIS_OPT_PREFIX); - add_constant_long(redis_ce, "OPT_READ_TIMEOUT", REDIS_OPT_READ_TIMEOUT); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT TSRMLS_CC); /* serializer */ - add_constant_long(redis_ce, "SERIALIZER_NONE", REDIS_SERIALIZER_NONE); - add_constant_long(redis_ce, "SERIALIZER_PHP", REDIS_SERIALIZER_PHP); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP TSRMLS_CC); /* scan options*/ - add_constant_long(redis_ce, "OPT_SCAN", REDIS_OPT_SCAN); - add_constant_long(redis_ce, "SCAN_RETRY", REDIS_SCAN_RETRY); - add_constant_long(redis_ce, "SCAN_NORETRY", REDIS_SCAN_NORETRY); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY TSRMLS_CC); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY TSRMLS_CC); + #ifdef HAVE_REDIS_IGBINARY - add_constant_long(redis_ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY); + zend_declare_class_constant_long(redis_ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); #endif zend_declare_class_constant_stringl(redis_ce, "AFTER", 5, "after", 5 TSRMLS_CC); From a28c1b0c0ade3c083f0d28cfa61d150196f55a31 Mon Sep 17 00:00:00 2001 From: Barbery <380032007@qq.com> Date: Sat, 12 Jul 2014 16:53:44 +0800 Subject: [PATCH 0204/1986] Update README.markdown --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 7f07f6f109..d5b741f2db 100644 --- a/README.markdown +++ b/README.markdown @@ -3011,11 +3011,11 @@ _**Description**_: Watches a key for modifications by another client. If the key is modified between `WATCH` and `EXEC`, the MULTI/EXEC transaction will fail (return `FALSE`). `unwatch` cancels all the watching of all keys by this client. ##### *Parameters* -*keys*: a list of keys +*keys*: string for one key or array for a list of keys ##### *Example* ~~~ -$redis->watch('x'); +$redis->watch('x'); // or for a list of keys: $redis->watch(array('x','another key')); /* long code here during the execution of which other clients could well modify `x` */ $ret = $redis->multi() ->incr('x') From 37af5ed021d9f7c9c196cb35782c4d382fe3756f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 29 Apr 2015 09:50:22 -0700 Subject: [PATCH 0205/1986] Use the correct return type for PFADD command Addresses #587 --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index cbbfd8592e..430d6a1d3a 100644 --- a/redis.c +++ b/redis.c @@ -7363,7 +7363,7 @@ PHP_METHOD(Redis, pfadd) { REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_1_response); } From 2f69b07ee482386bbe8b0a8f0a093ad712212c41 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 27 Apr 2014 16:26:11 -0700 Subject: [PATCH 0206/1986] Initial commit of RedisCluster class and library This is an initial commmit which adds a RedisCluster class as well as the framework around which we'll be building proper cluster support. The first commit just contains the code to set up and use our new RedisCluster class as well as parsing logic to handline CLUSTER NODES such that we can map the keyspace. Next up, command processing and then pipelining in a sane way. --- cluster_library.c | 506 ++++++++++++++++++++++++++++++++++++++++++++++ cluster_library.h | 97 +++++++++ common.h | 20 ++ config.m4 | 2 +- crc16.h | 87 ++++++++ library.c | 71 ++++++- redis.c | 23 ++- redis_cluster.c | 191 +++++++++++++++++ redis_cluster.h | 28 +++ 9 files changed, 1018 insertions(+), 7 deletions(-) create mode 100644 cluster_library.c create mode 100644 cluster_library.h create mode 100644 crc16.h create mode 100644 redis_cluster.c create mode 100644 redis_cluster.h diff --git a/cluster_library.c b/cluster_library.c new file mode 100644 index 0000000000..1282d62382 --- /dev/null +++ b/cluster_library.c @@ -0,0 +1,506 @@ +#include "php_redis.h" +#include "common.h" +#include "library.h" +#include "cluster_library.h" +#include "crc16.h" +#include + +extern zend_class_entry *redis_cluster_exception_ce; + +static void free_cluster_info(clusterNodeInfo *info) { + if(info->name) { + efree(info->name); + } + if(info->master_name) { + efree(info->master_name); + } + if(info->host) { + efree(info->host); + } + efree(info); +} + +/* Destructor callback for a hash table containing inner hash tables */ +static void free_inner_ht(void *data) { + if(*(HashTable**)data) { + zend_hash_destroy(*(HashTable**)data); + efree(*(HashTable**)data); + } +} + +/* Destructor for HashTable containing clusterNodeInfo */ +static void free_inner_info(void *data) { + clusterNodeInfo *info = *(clusterNodeInfo**)data; + if(info) free_cluster_info(info); +} + +/* Get the hash slot for a given key */ +unsigned short cluster_hash_key(const char *key, int len) { + int s, e; + + // Find first occurrence of {, if any + for(s=0;sname = estrndup(array[CLUSTER_NODES_HASH], CLUSTER_NAME_LEN); + + // Set the host/port bits + if(memcmp(array[CLUSTER_NODES_HOST_PORT], ":0", sizeof(":0"))==0) { + info->seed = 1; + info->host_len = strlen(sock->host); + info->host = estrndup(sock->host, info->host_len); + info->port = sock->port; + } else if((p = strchr(array[CLUSTER_NODES_HOST_PORT], ':'))!=NULL) { + /* Null terminate at the : character */ + *p = '\0'; + + info->seed = 0; + info->host_len = p - array[CLUSTER_NODES_HOST_PORT]; + info->host = estrndup(array[CLUSTER_NODES_HOST_PORT], info->host_len); + info->port = atoi(p+1); + } else { + efree(array); + return -1; + } + + // If we've got a master hash slot, set it + if(memcmp(array[CLUSTER_NODES_MASTER_HASH], "-", sizeof("-"))!=0) { + if(strlen(array[CLUSTER_NODES_MASTER_HASH])!=CLUSTER_NAME_LEN) { + efree(array); + return -1; + } + info->master_name = estrndup(array[CLUSTER_NODES_MASTER_HASH], CLUSTER_NAME_LEN); + } else { + info->master_name = NULL; + } + + // If this is a master, parse slots + if(count == CLUSTER_NODES_MASTER_ELE) { + // This must be in the form - + if((p = strchr(array[CLUSTER_SLOTS], '-'))==NULL) { + efree(array); + return -1; + } + + // Null terminate a the - + *p = '\0'; + + // Pull out start and end slot + info->start_slot = atoi(array[CLUSTER_SLOTS]); + info->end_slot = atoi(p+1); + } else { + info->start_slot = 0; + info->end_slot = 0; + } + + // Free our array + efree(array); + + // Success! + return 0; +} + +/* + * Execute a CLUSTER NODES command against the given seed and return an array + * of nodes that came back, along with setting a pointer as to how many there + * are. + */ +static clusterNodeInfo +**cluster_get_nodes(redisCluster *cluster, int *len, RedisSock *redis_sock + TSRMLS_DC) +{ + clusterNodeInfo **nodes; + REDIS_REPLY_TYPE type; + char *cmd, *reply, **lines; + int cmd_len, i, j, count; + + // Create our CLUSTER NODES command + cmd_len = redis_cmd_format_static(&cmd, "CLUSTER", "s", "NODES", + sizeof("NODES")-1); + + // Send the command directly to this socket + if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)<0) { + efree(cmd); + return NULL; + } + + // Make sure we've got a bulk reply and we can read it + if(redis_read_reply_type(redis_sock, &type, len TSRMLS_CC)<0 || type!=TYPE_BULK || + (reply = redis_sock_read_bulk_reply(redis_sock, *len TSRMLS_CC))==NULL) + { + efree(cmd); + return NULL; + } + + // Split by \n + lines = split_str_by_delim(reply, "\n", &count); + + // Allocate storage for nodes + nodes = ecalloc(count, sizeof(clusterNodeInfo*)); + + for(i=0;iname = estrndup(info->name, CLUSTER_NAME_LEN); + if(info->master_name) { + node->master_name = estrndup(info->master_name, CLUSTER_NAME_LEN); + } + node->start_slot = info->start_slot; + node->end_slot = info->end_slot; + + /* Attach our RedisSock */ + /* TODO: Lazy connect as an option? */ + node->sock = redis_sock_create(info->host, info->host_len, info->port, + cluster->timeout, 0, NULL, 0, 1); + + // Return our node + return node; +} + +/* Free a redisClusterNode structure */ +PHPAPI void cluster_free_node(redisClusterNode *node) { + efree(node->name); + if(node->master_name) { + efree(node->master_name); + } + if(node->slaves) { + zend_hash_destroy(node->slaves); + efree(node->slaves); + } + redis_free_socket(node->sock); + efree(node); +} + +/* Free an array of clusterNodeInfo structs */ +void cluster_free_info_array(clusterNodeInfo **array, int count) { + int i; + + // Free each info structure we've got + for(i=0;islaves) { + ALLOC_HASHTABLE(master->slaves); + zend_hash_init(master->slaves, 0, NULL, NULL, 0); + } + + // Create our slave node + slave_node = cluster_node_create(cluster, slave); + + // Attach it to our slave + if(zend_hash_next_index_insert(master->slaves, (void*)&slave_node, + sizeof(redisClusterNode*), NULL)==FAILURE) + { + return -1; + } + + // Index by host:port + key_len = snprintf(key, sizeof(key), "%s:%u", slave_node->sock->host, + slave_node->sock->port); + + // Add this to our overall table of nodes + zend_hash_update(cluster->nodes, key, key_len+1, + (void*)&slave_node, sizeof(redisClusterNode*),NULL); + + // Success + return 0; +} + +/* Given masters and slaves from node discovery, set up our nodes */ +static int +cluster_set_node_info(redisCluster *cluster, HashTable *masters, + HashTable *slaves TSRMLS_DC) +{ + clusterNodeInfo **info, **slave; + HashTable **master_slaves; + redisClusterNode *node; + char *name, key[1024]; + int i, key_len; + uint name_len; + ulong idx; + + // Iterate over our master nodes + for(zend_hash_internal_pointer_reset(masters); + zend_hash_has_more_elements(masters)==SUCCESS; + zend_hash_move_forward(masters)) + { + // Get our node name (stored in the key) as well as information. + zend_hash_get_current_key_ex(masters, &name, &name_len, &idx, 0, NULL); + zend_hash_get_current_data(masters, (void**)&info); + + // Create our master node + node = cluster_node_create(cluster, *info); + + // If we find slaves for this master, add them to the node + if(zend_hash_find(slaves, (*info)->name, CLUSTER_NAME_LEN+1, + (void**)&master_slaves)==SUCCESS) + { + // Iterate through the slaves for this node + for(zend_hash_internal_pointer_reset(*master_slaves); + zend_hash_has_more_elements(*master_slaves)==SUCCESS; + zend_hash_move_forward(*master_slaves)) + { + zend_hash_get_current_data(*master_slaves, (void**)&slave); + + if(cluster_node_add_slave(cluster, node, *slave TSRMLS_CC)!=0) { + zend_throw_exception(redis_cluster_exception_ce, + "Can't add slave node to master instance", 0 TSRMLS_CC); + return -1; + } + } + } + + // Point appropriate slots to this node + for(i=node->start_slot;i<=node->end_slot;i++) { + cluster->master[i] = node; + } + + // Create a host:port key for this node + key_len = snprintf(key, sizeof(key), "%s:%u", node->sock->host, + node->sock->port); + + zend_hash_update(cluster->nodes, key, key_len+1, + (void*)&node, sizeof(redisClusterNode*),NULL); + } + + // Success + return 0; +} + +/* Initialize seeds */ +PHPAPI int +cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { + RedisSock *redis_sock; + char *str, *psep, key[1024]; + int key_len; + zval **z_seed; + + // Iterate our seeds array + for(zend_hash_internal_pointer_reset(ht_seeds); + zend_hash_has_more_elements(ht_seeds)==SUCCESS; + zend_hash_move_forward(ht_seeds)) + { + // Grab seed string + zend_hash_get_current_data(ht_seeds, (void**)&z_seed); + + // Skip anything that isn't a string + if(Z_TYPE_PP(z_seed)!=IS_STRING) + continue; + + // Grab a copy of the string + str = Z_STRVAL_PP(z_seed); + + // Must be in host:port form + if(!(psep = strchr(str, ':'))) + continue; + + // Allocate a structure for this seed + redis_sock = redis_sock_create(str, psep-str, + (unsigned short)atoi(psep+1), cluster->timeout,0,NULL,0,0); + + // Index this seed by host/port + key_len = snprintf(key, sizeof(key), "%s:%u", redis_sock->host, + redis_sock->port); + + // Add to our seed HashTable + zend_hash_update(cluster->seeds, key, key_len+1, (void*)&redis_sock, + sizeof(RedisSock*),NULL); + } + + // Success if at least one seed seems valid + return zend_hash_num_elements(cluster->seeds) > 0 ? 0 : -1; +} + +/* Initial mapping of our cluster keyspace */ +PHPAPI int +cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { + RedisSock **seed; + clusterNodeInfo **node; + + HashTable *masters, *slaves, **sub_slaves; + int count, valid=0, i; + + // Iterate over our seeds attempting to map keyspace + for(zend_hash_internal_pointer_reset(cluster->seeds); + zend_hash_has_more_elements(cluster->seeds) == SUCCESS; + zend_hash_move_forward(cluster->seeds)) + { + // Grab the redis_sock for this seed + zend_hash_get_current_data(cluster->seeds, (void**)&seed); + + // Attempt to connect to this seed node + if(redis_sock_connect(*seed TSRMLS_CC)!=0) { + continue; + } + + // Parse out cluster nodes. Flag mapped if we are valid + if((node = cluster_get_nodes(cluster, &count, *seed TSRMLS_CC))) { + valid = 1; + break; + } + } + + // Throw an exception if we couldn't map + if(!valid) { + zend_throw_exception(redis_cluster_exception_ce, + "Couldn't map cluster keyspace using any provided seed", 0 + TSRMLS_CC); + return -1; + } + + // Hashes for masters and slaves + ALLOC_HASHTABLE(masters); + zend_hash_init(masters, 0, NULL, free_inner_info, 0); + ALLOC_HASHTABLE(slaves); + zend_hash_init(slaves, 0, NULL, free_inner_ht, 0); + + // Iterate nodes, splitting into master and slave groups + for(i=0;imaster_name == NULL) { + zend_hash_update(masters, node[i]->name, CLUSTER_NAME_LEN+1, + (void*)&node[i], sizeof(clusterNodeInfo*), NULL); + } else { + HashTable *ht_inner; + + // Determine if we've already got at least one slave for this master + if(zend_hash_find(slaves, node[i]->master_name, CLUSTER_NAME_LEN+1, + (void**)&sub_slaves)==FAILURE) + { + ALLOC_HASHTABLE(ht_inner); + zend_hash_init(ht_inner, 0, NULL, free_inner_info, 0); + + zend_hash_update(slaves, node[i]->master_name, + CLUSTER_NAME_LEN+1, (void*)&ht_inner, sizeof(HashTable*), + NULL); + } else { + ht_inner = *sub_slaves; + } + + // Add to this masters slaves. + zend_hash_next_index_insert(ht_inner, (void*)&node[i], + sizeof(clusterNodeInfo*), NULL); + } + } + + // Now that we have the key space mapped by master ID, we can set + // socket information on them for communication. + cluster_set_node_info(cluster, masters, slaves TSRMLS_CC); + + // Free our array of clusterNodeInfo* objects. The HashTables will clean + // up the clusterNodeInfo* pointers themselves. + efree(node); + + // Destroy our hash tables + zend_hash_destroy(masters); + efree(masters); + zend_hash_destroy(slaves); + efree(slaves); + + return 0; +} + +/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h new file mode 100644 index 0000000000..d21609200b --- /dev/null +++ b/cluster_library.h @@ -0,0 +1,97 @@ +#ifndef _PHPREDIS_CLUSTER_LIBRARY_H +#define _PHPREDIS_CLUSTER_LIBRARY_H + +#include "common.h" + +#ifdef ZTS +#include "TSRM.h" +#endif + +/* Redis cluster hash slots and N-1 which we'll use to find it */ +#define REDIS_CLUSTER_SLOTS 16384 +#define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) + +/* Nodes we expect for slave or master */ +#define CLUSTER_NODES_MASTER_ELE 9 +#define CLUSTER_NODES_SLAVE_ELE 8 + +/* Length of a cluster name */ +#define CLUSTER_NAME_LEN 40 + +/* The parts for our cluster nodes command */ +#define CLUSTER_NODES_HASH 0 +#define CLUSTER_NODES_HOST_PORT 1 +#define CLUSTER_NODES_TYPE 2 +#define CLUSTER_NODES_MASTER_HASH 3 +#define CLUSTER_NODES_PING 4 +#define CLUSTER_NODES_PONG 5 +#define CLUSTER_NODES_EPOCH 6 +#define CLUSTER_NODES_CONNECTED 7 +#define CLUSTER_SLOTS 8 + +/* Specific destructor to free a cluster object */ +// void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); + +/* Bits related to CLUSTER NODES output */ +typedef struct clusterNodeInfo { + char *name, *master_name; + + short seed; + + char *host; + int host_len; + + unsigned short port; + + unsigned short start_slot; + unsigned short end_slot; +} clusterNodeInfo; + +/* A Redis Cluster master node */ +typedef struct redisClusterNode { + /* Our cluster ID and master ID */ + char *name; + char *master_name; + + /* Our Redis socket in question */ + RedisSock *sock; + + /* Our start and end slots that we serve */ + unsigned short start_slot; + unsigned short end_slot; + + /* A HashTable containing any slaves */ + HashTable *slaves; +} redisClusterNode; + +/* RedisCluster implementation structure */ +typedef struct redisCluster { + /* Object reference for Zend */ + zend_object std; + + /* Timeout and read timeout */ + double timeout; + double read_timeout; + + /* Hash table of seed host/ports */ + HashTable *seeds; + + /* RedisCluster masters, by direct slot */ + redisClusterNode *master[REDIS_CLUSTER_SLOTS]; + + /* All RedisCluster objects we've created/are connected to */ + HashTable *nodes; +} redisCluster; + +/* Hash a key to it's slot, using the Redis Cluster hash algorithm */ +unsigned short cluster_hash_key(const char *key, int len); + +PHPAPI int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds); +PHPAPI int cluster_map_keyspace(redisCluster *cluster TSRMLS_DC); +PHPAPI void cluster_free_node(redisClusterNode *node); + +PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len TSRMLS_DC); + +PHPAPI int cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, clusterNodeInfo *slave TSRMLS_DC); + +#endif diff --git a/common.h b/common.h index 8ac05d87e9..579611710c 100644 --- a/common.h +++ b/common.h @@ -53,6 +53,13 @@ typedef enum _PUBSUB_TYPE { PUBSUB_NUMPAT } PUBSUB_TYPE; +/* Cluster redirection type */ +typedef enum _MOVED_TYPE { + MOVED_NONE, + MOVED_MOVED, + MOVED_ASK +} MOVED_TYPE; + /* options */ #define REDIS_OPT_SERIALIZER 1 #define REDIS_OPT_PREFIX 2 @@ -177,6 +184,13 @@ else if(redis_sock->mode == MULTI) { \ #define REDIS_PROCESS_RESPONSE(function) REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) +/* Clear redirection info */ +#define REDIS_MOVED_CLEAR(redis_sock) \ + redis_sock->redir_slot = 0; \ + redis_sock->redir_port = 0; \ + redis_sock->redir_type = MOVED_NONE; \ + + /* Extended SET argument detection */ #define IS_EX_ARG(a) ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') #define IS_PX_ARG(a) ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') @@ -239,6 +253,12 @@ typedef struct { zend_bool lazy_connect; int scan; + + /* Cluster node redirection */ + MOVED_TYPE redir_type; + char *redir_host; + unsigned short redir_port; + unsigned short redir_slot; } RedisSock; /* }}} */ diff --git a/config.m4 b/config.m4 index 205a858c20..af8c5f33da 100755 --- a/config.m4 +++ b/config.m4 @@ -99,5 +99,5 @@ dnl Check for igbinary dnl dnl PHP_SUBST(REDIS_SHARED_LIBADD) - PHP_NEW_EXTENSION(redis, redis.c library.c redis_session.c redis_array.c redis_array_impl.c, $ext_shared) + PHP_NEW_EXTENSION(redis, redis.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c, $ext_shared) fi diff --git a/crc16.h b/crc16.h new file mode 100644 index 0000000000..f7d2aee385 --- /dev/null +++ b/crc16.h @@ -0,0 +1,87 @@ +/* + * Copyright 2001-2010 Georges Menie (www.menie.org) + * Copyright 2010 Salvatore Sanfilippo (adapted to Redis coding style) + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* CRC16 implementation according to CCITT standards. + * + * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the + * following parameters: + * + * Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" + * Width : 16 bit + * Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) + * Initialization : 0000 + * Reflect Input byte : False + * Reflect Output CRC : False + * Xor constant to output CRC : 0000 + * Output for "123456789" : 31C3 + */ + +#include + +static const uint16_t crc16tab[256]= { + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, + 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, + 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, + 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, + 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, + 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, + 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, + 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, + 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, + 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, + 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, + 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, + 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, + 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, + 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, + 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, + 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, + 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, + 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, + 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, + 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, + 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, + 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, + 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, + 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, + 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, + 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, + 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, + 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, + 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, + 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 +}; + +static uint16_t crc16(const char *buf, int len) { + int counter; + uint16_t crc = 0; + for (counter = 0; counter < len; counter++) + crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF]; + return crc; +} diff --git a/library.c b/library.c index 2f45cfd10b..f701d1d827 100644 --- a/library.c +++ b/library.c @@ -320,6 +320,58 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes return reply; } +/** + * Parse MOVED or ASK redirection (Redis Cluster) + * We should get slot host:port + */ +PHPAPI +int redis_sock_redir(RedisSock *redis_sock, const char *msg, int len, + MOVED_TYPE type TSRMLS_DC) +{ + char buf[24], *p1, *p2; + + // Find the space and ':' seperating host/port and do a sanity check on the + // lengths we get back. + if(!(p1 = strchr(msg, ' ')) || !(p2 = strchr(p1,':')) || (len-(p2-msg)>6)) + { + zend_throw_exception(redis_exception_ce, + "Error parsing MOVED/ASK redirection", 0 TSRMLS_CC); + return -1; + } + + // Free previously stored redirection host + if(redis_sock->redir_host) efree(redis_sock->redir_host); + + // Copy and convert slot + strncpy(buf, msg, p1-msg); + buf[p1-msg]='\0'; + redis_sock->redir_slot = (unsigned short)atoi(buf); + + // Make a copy of our host + redis_sock->redir_host = estrndup(p1+1, p2-p1-1); + + // Copy and convert port + strncpy(buf, p2+1, len-(p2-msg)); + buf[len-(p2-msg)+1]='\0'; + redis_sock->redir_port = (unsigned short)atoi(buf); + + // Success + return 0; +} + +/** + * Clear redirection information + */ +PHPAPI void redis_sock_redir_clear(RedisSock *redis_sock) +{ + if(redis_sock->redir_host) { + efree(redis_sock->redir_host); + redis_sock->redir_host = NULL; + } + redis_sock->redir_slot = 0; + redis_sock->redir_port = 0; +} + /** * redis_sock_read */ @@ -343,15 +395,25 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D return NULL; } + // Clear any previous MOVED or ASK redirection + REDIS_MOVED_CLEAR(redis_sock); + switch(inbuf[0]) { case '-': /* Set the last error */ err_len = strlen(inbuf+1) - 2; redis_sock_set_err(redis_sock, inbuf+1, err_len); - /* Filter our ERROR through the few that should actually throw */ - redis_error_throw(inbuf + 1, err_len TSRMLS_CC); - + /* Handle stale data or MOVED/ASK redirection */ + if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } else if(memcmp(inbuf, "-MOVED ", sizeof("-MOVED ")-1)==0) { + redis_sock_redir(redis_sock,inbuf+sizeof("-MOVED "), + err_len-sizeof("-MOVED ")-1, MOVED_MOVED TSRMLS_CC); + } else if(memcmp(inbuf, "-ASK ", sizeof("-ASK ")-1)==0) { + redis_sock_redir(redis_sock,inbuf+sizeof("-ASK "), + err_len-sizeof("-ASK ")-1, MOVED_ASK TSRMLS_CC); + } return NULL; case '$': *buf_len = atoi(inbuf + 1); @@ -1821,6 +1883,9 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) if(redis_sock->persistent_id) { efree(redis_sock->persistent_id); } + if(redis_sock->redir_host) { + efree(redis_sock->redir_host); + } efree(redis_sock->host); efree(redis_sock); } diff --git a/redis.c b/redis.c index 430d6a1d3a..aaa306d027 100644 --- a/redis.c +++ b/redis.c @@ -29,6 +29,7 @@ #include "php_ini.h" #include "php_redis.h" #include "redis_array.h" +#include "redis_cluster.h" #include #ifdef PHP_SESSION @@ -53,11 +54,14 @@ extern ps_module ps_mod_redis; #endif extern zend_class_entry *redis_array_ce; +extern zend_class_entry *redis_cluster_ce; zend_class_entry *redis_ce; zend_class_entry *redis_exception_ce; +extern zend_class_entry *redis_cluster_exception_ce; zend_class_entry *spl_ce_RuntimeException = NULL; extern zend_function_entry redis_array_functions[]; +extern zend_function_entry redis_cluster_functions[]; PHP_INI_BEGIN() /* redis arrays */ @@ -469,7 +473,6 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) return redis_sock; } - /** * PHP_MINIT_FUNCTION */ @@ -477,7 +480,9 @@ PHP_MINIT_FUNCTION(redis) { zend_class_entry redis_class_entry; zend_class_entry redis_array_class_entry; + zend_class_entry redis_cluster_class_entry; zend_class_entry redis_exception_class_entry; + zend_class_entry redis_cluster_exception_class_entry; REGISTER_INI_ENTRIES(); @@ -489,6 +494,11 @@ PHP_MINIT_FUNCTION(redis) INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions); redis_array_ce = zend_register_internal_class(&redis_array_class_entry TSRMLS_CC); + /* RedisCluster class */ + INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_functions); + redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry TSRMLS_CC); + redis_cluster_ce->create_object = create_cluster_context; + le_redis_array = zend_register_list_destructors_ex( redis_destructor_redis_array, NULL, @@ -503,6 +513,13 @@ PHP_MINIT_FUNCTION(redis) NULL TSRMLS_CC ); + /* RedisClusterException class */ + INIT_CLASS_ENTRY(redis_cluster_exception_class_entry, "RedisClusterException", NULL); + redis_cluster_exception_ce = zend_register_internal_class_ex( + &redis_cluster_exception_class_entry, rediscluster_get_exception_base(0 TSRMLS_CC), + NULL TSRMLS_CC + ); + le_redis_sock = zend_register_list_destructors_ex( redis_destructor_redis_sock, NULL, @@ -662,7 +679,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { RedisSock *redis_sock = NULL; #ifdef ZTS - /* not sure how in threaded mode this works so disabled persistents at first */ + /* not sure how in threaded mode this works so disabled persistence at first */ persistent = 0; #endif @@ -694,7 +711,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { { /* maybe there is a socket but the id isn't known.. what to do? */ } else { - zend_list_delete(Z_LVAL_PP(socket)); /* the refcount should be decreased and the detructor called */ + zend_list_delete(Z_LVAL_PP(socket)); /* the refcount should be decreased and the destructor called */ } } diff --git a/redis_cluster.c b/redis_cluster.c new file mode 100644 index 0000000000..3d41b18dc8 --- /dev/null +++ b/redis_cluster.c @@ -0,0 +1,191 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2009 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Michael Grunder | + | Maintainer: Nicolas Favre-Felix | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "common.h" +#include "php_redis.h" +#include "ext/standard/info.h" +#include "crc16.h" +#include "redis_cluster.h" +#include +#include "library.h" + +zend_class_entry *redis_cluster_ce; + +/* Exception handler */ +zend_class_entry *redis_cluster_exception_ce; +zend_class_entry *spl_rte_ce = NULL; + +/* Function table */ +zend_function_entry redis_cluster_functions[] = { + PHP_ME(RedisCluster, __construct, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, get, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +/* Our context seeds will be a hash table with RedisSock* pointers */ +static void ht_free_seed(void *data) { + RedisSock *redis_sock = *(RedisSock**)data; + if(redis_sock) redis_free_socket(redis_sock); +} + +/* Free redisClusterNode objects we've stored */ +static void ht_free_node(void *data) { + redisClusterNode *node = *(redisClusterNode**)data; + cluster_free_node(node); +} + +/* Initialize/Register our RedisCluster exceptions */ +PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC) { +#if HAVE_SPL + if(!root) { + if(!spl_rte_ce) { + zend_class_entry **pce; + + if(zend_hash_find(CG(class_table), "runtimeexception", + sizeof("runtimeexception"), (void**)&pce) + ==SUCCESS) + { + spl_rte_ce = *pce; + return *pce; + } + } else { + return spl_rte_ce; + } + } +#endif +#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) + return zend_exception_get_default(); +#else + return zend_exception_get_default(TSRMLS_C); +#endif +} + +/* Create redisCluster context */ +zend_object_value +create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { + zend_object_value retval; + redisCluster *cluster; + + // Allocate our actual struct + cluster = emalloc(sizeof(redisCluster)); + memset(cluster, 0, sizeof(redisCluster)); + + // Allocate our hash table for seeds + ALLOC_HASHTABLE(cluster->seeds); + zend_hash_init(cluster->seeds, 0, NULL, ht_free_seed, 0); + + // Allocate our hash table for connected Redis objects + ALLOC_HASHTABLE(cluster->nodes); + zend_hash_init(cluster->nodes, 0, NULL, ht_free_node, 0); + + // Initialize it + zend_object_std_init(&cluster->std, class_type TSRMLS_CC); + +#if PHP_VERSION_ID < 50399 + zval *tmp; + + zend_hash_copy(cluster->std.properties, &class_type->default_properties, + (copy_ctor_func_t)zval_add_ref, (void*)&tmp, sizeof(zval*)); +#endif + + retval.handle = zend_objects_store_put(cluster, + (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_cluster_context, NULL TSRMLS_CC); + + retval.handlers = zend_get_std_object_handlers(); + + return retval; +} + +/* Free redisCluster context */ +void free_cluster_context(void *object TSRMLS_DC) { + redisCluster *cluster; + redisClusterNode **node; + + // Grab context + cluster = (redisCluster*)object; + + // Free seeds HashTable itself + zend_hash_destroy(cluster->seeds); + efree(cluster->seeds); + + // Destroy all Redis objects and free our nodes HashTable + zend_hash_destroy(cluster->nodes); + efree(cluster->nodes); + + // Finally, free the redisCluster structure itself + efree(cluster); +} + +// +// PHP Methods +// + +/* Create a RedisCluster Object */ +PHP_METHOD(RedisCluster, __construct) { + zval *object, *z_seeds=NULL; + char *name; + long name_len; + double timeout = 0.0, read_timeout = 0.0; + redisCluster *context = GET_CONTEXT(); + + // Parse arguments + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|add", + &object, redis_cluster_ce, &name, &name_len, + &z_seeds, &timeout, &read_timeout)==FAILURE) + { + zend_throw_exception(redis_cluster_exception_ce, + "To instance a RedisCluster object, you must at least pass in a name", + 0 TSRMLS_CC); + RETURN_FALSE; + } + + // Validate timeout + if(timeout < 0L || timeout > INT_MAX) { + zend_throw_exception(redis_cluster_exception_ce, "Invalid timeout", 0 TSRMLS_CC); + RETURN_FALSE; + } + + // Validate our read timeout + if(read_timeout < 0L || read_timeout > INT_MAX) { + zend_throw_exception(redis_cluster_exception_ce, "Invalid read timeout", 0 TSRMLS_CC); + RETURN_FALSE; + } + + // TODO: Implement seed retrieval from php.ini + if(!z_seeds || zend_hash_num_elements(Z_ARRVAL_P(z_seeds))==0) { + zend_throw_exception(redis_cluster_exception_ce, "Must pass seeds", 0 TSRMLS_CC); + RETURN_FALSE; + } + + // Initialize our RedisSock "seed" objects + cluster_init_seeds(context, Z_ARRVAL_P(z_seeds)); + + // Create and map our key space + cluster_map_keyspace(context TSRMLS_CC); +} + +/* GET */ +PHP_METHOD(RedisCluster, get) { + RETURN_FALSE; +} diff --git a/redis_cluster.h b/redis_cluster.h new file mode 100644 index 0000000000..57dda0314a --- /dev/null +++ b/redis_cluster.h @@ -0,0 +1,28 @@ +#ifndef REDIS_CLUSTER_H +#define REDIS_CLUSTER_H + +#include "cluster_library.h" +#include +#include + +/* Redis cluster hash slots and N-1 which we'll use to find it */ +#define REDIS_CLUSTER_SLOTS 16384 +#define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) + +/* Get attached object context */ +#define GET_CONTEXT() (redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC) + +/* For the creation of RedisCluster specific exceptions */ +PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); + +/* Initialization and cleanup of our attached object */ +zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC); +void free_cluster_context(void *object TSRMLS_DC); + +/* Inittialize our class with PHP */ +void init_rediscluster(TSRMLS_D); + +PHP_METHOD(RedisCluster, __construct); +PHP_METHOD(RedisCluster, get); + +#endif From b7f9b14416f53cee7140858b2f066d56ff949a8c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 6 May 2014 08:17:40 -0700 Subject: [PATCH 0207/1986] get/set placeholders --- redis_cluster.c | 5 +++++ redis_cluster.h | 1 + 2 files changed, 6 insertions(+) diff --git a/redis_cluster.c b/redis_cluster.c index 3d41b18dc8..0f3b09d809 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -189,3 +189,8 @@ PHP_METHOD(RedisCluster, __construct) { PHP_METHOD(RedisCluster, get) { RETURN_FALSE; } + +/* SET */ +PHP_METHOD(RedisCluster, set) { + RETURN_FALSE; +} diff --git a/redis_cluster.h b/redis_cluster.h index 57dda0314a..d7b2f439ed 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -24,5 +24,6 @@ void init_rediscluster(TSRMLS_D); PHP_METHOD(RedisCluster, __construct); PHP_METHOD(RedisCluster, get); +PHP_METHOD(RedisCluster, set); #endif From 7ac92792319246965d90545cd7a0dbb896232982 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 22 May 2014 10:30:07 -0700 Subject: [PATCH 0208/1986] Add a method to easily hash a ZVAL --- cluster_library.c | 33 +++++++++++++++++++++++++++++++++ cluster_library.h | 11 +++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 1282d62382..0b2964f33f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -58,6 +58,39 @@ unsigned short cluster_hash_key(const char *key, int len) { return crc16(key+s+1,e-s-1) & REDIS_CLUSTER_MOD; } +/* Hash a key from a ZVAL */ +unsigned short cluster_hash_key_zval(zval *z_key) { + const char *kptr; + char buf[255]; + int klen; + + switch(Z_TYPE_P(z_key)) { + case IS_STRING: + kptr = Z_STRVAL_P(z_key); + klen = Z_STRLEN_P(z_key); + break; + case IS_LONG: + klen = snprintf(buf,sizeof(buf),"%ld",Z_LVAL_P(z_key)); + kptr = (const char *)buf; + break; + case IS_DOUBLE: + klen = snprintf(buf,sizeof(buf),"%f",Z_DVAL_P(z_key)); + kptr = (const char *)buf; + break; + case IS_ARRAY: + kptr = "Array"; + klen = sizeof("Array")-1; + break; + case IS_OBJECT: + kptr = "Object"; + klen = sizeof("Object")-1; + break; + } + + // Hash the string representation + return cluster_hash_key(kptr, klen); +} + static char **split_str_by_delim(char *str, char *delim, int *len) { char **array, *tok, *tok_buf; int size=16; diff --git a/cluster_library.h b/cluster_library.h index d21609200b..e54343db14 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -50,8 +50,8 @@ typedef struct clusterNodeInfo { /* A Redis Cluster master node */ typedef struct redisClusterNode { /* Our cluster ID and master ID */ - char *name; - char *master_name; + char *name; + char *master_name; /* Our Redis socket in question */ RedisSock *sock; @@ -84,14 +84,17 @@ typedef struct redisCluster { } redisCluster; /* Hash a key to it's slot, using the Redis Cluster hash algorithm */ +unsigned short cluster_hash_key_zval(zval *key); unsigned short cluster_hash_key(const char *key, int len); +/* Send a command to where we think the key(s) should live and redirect when needed */ +PHPAPI int cluster_send_command(redisCluster *cluster, unsigned short slot, + const char *cmd, int cmd_len); + PHPAPI int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds); PHPAPI int cluster_map_keyspace(redisCluster *cluster TSRMLS_DC); PHPAPI void cluster_free_node(redisClusterNode *node); - PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len TSRMLS_DC); - PHPAPI int cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, clusterNodeInfo *slave TSRMLS_DC); #endif From a8dbe68a211439b37a499ad1828c006fa337c7b5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 27 May 2014 14:12:19 -0700 Subject: [PATCH 0209/1986] Initial commit of RedisCluster send/receive loop for commands. Every time we communicate with the cluster we have to determine which slot to try, and then if we get a MOVED or ASK redirection, retry at the new slot. In the case of MOVED, we'll update the node mapping we cache locally. For ASK, we'll leave it unchanged but try at the slot where directed --- cluster_library.c | 343 +++++++++++++++++++++++++++++++++++++++++++++- cluster_library.h | 54 +++++++- redis_cluster.c | 10 +- 3 files changed, 395 insertions(+), 12 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0b2964f33f..24ea4b9e79 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -7,6 +7,28 @@ extern zend_class_entry *redis_cluster_exception_ce; +/* Set our last error string encountered */ +static void cluster_set_err(redisCluster *c, char *err, int err_len) +{ + if(err && err_len>0) { + if(c->err == NULL) { + c->err = emalloc(err_len+1); + } else if(err_len > c->err_len) { + c->err = erealloc(c->err, err_len + 1); + } + memcpy(c->err,err,err_len); + c->err[err_len]='\0'; + c->err_len = err_len; + } else { + if(c->err) { + efree(c->err); + } + c->err = NULL; + c->err_len = 0; + } +} + +/* Free a cluster info structure */ static void free_cluster_info(clusterNodeInfo *info) { if(info->name) { efree(info->name); @@ -63,7 +85,8 @@ unsigned short cluster_hash_key_zval(zval *z_key) { const char *kptr; char buf[255]; int klen; - + + // Switch based on ZVAL type switch(Z_TYPE_P(z_key)) { case IS_STRING: kptr = Z_STRVAL_P(z_key); @@ -165,7 +188,8 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { efree(array); return -1; } - info->master_name = estrndup(array[CLUSTER_NODES_MASTER_HASH], CLUSTER_NAME_LEN); + info->master_name = estrndup(array[CLUSTER_NODES_MASTER_HASH], + CLUSTER_NAME_LEN); } else { info->master_name = NULL; } @@ -221,8 +245,9 @@ static clusterNodeInfo } // Make sure we've got a bulk reply and we can read it - if(redis_read_reply_type(redis_sock, &type, len TSRMLS_CC)<0 || type!=TYPE_BULK || - (reply = redis_sock_read_bulk_reply(redis_sock, *len TSRMLS_CC))==NULL) + if(redis_read_reply_type(redis_sock, &type, len TSRMLS_CC)<0 || + type!=TYPE_BULK || (reply = redis_sock_read_bulk_reply(redis_sock, + *len TSRMLS_CC))==NULL) { efree(cmd); return NULL; @@ -279,6 +304,27 @@ cluster_node_create(redisCluster *cluster, clusterNodeInfo *info) { return node; } +/* Helper to create a cluster node struct from each bit of info */ +static redisClusterNode* +cluster_node_create_ex(redisCluster *c, const char *name, + const char *master_name, const char *host, + int host_len, unsigned short port, + unsigned short start_slot, + unsigned short end_slot) +{ + clusterNodeInfo info = {0}; + + info.name = (char*)name; + info.master_name = (char*)master_name; + info.host = (char*)host; + info.host_len = host_len; + info.port = port; + info.start_slot = start_slot; + info.end_slot = end_slot; + + return cluster_node_create(c, &info); +} + /* Free a redisClusterNode structure */ PHPAPI void cluster_free_node(redisClusterNode *node) { efree(node->name); @@ -306,6 +352,47 @@ void cluster_free_info_array(clusterNodeInfo **array, int count) { efree(array); } +/* Get a RedisSock object from the host and port where we have been + * directed from an ASK response. We'll first see if we have + * connected to this node already, and return that. If not, we + * create it and add it to our nodes. + */ +static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { + redisClusterNode **ppNode; + clusterNodeInfo pInfo = {0}; + char key[1024]; + int key_len; + + // It'll be hashed as host:port in our nodes HashTable + key_len = snprintf(key, sizeof(key), "%s:%u", c->redir_host, + c->redir_port); + + // See if we've already attached to it + if(zend_hash_find(c->nodes, key, key_len+1, (void**)&ppNode)==SUCCESS) + { + return (*ppNode)->sock; + } + + // We have yet to encounter this host:port so create + pInfo.name = NULL; + pInfo.master_name = NULL; + pInfo.host = c->redir_host; + pInfo.host_len = strlen(c->redir_host); + pInfo.port = c->redir_port; + pInfo.start_slot = c->redir_slot; + pInfo.end_slot = c->redir_slot; + + // Create a redisClusterNode + *ppNode = cluster_node_create(c, &pInfo); + + // Now add it to the nodes we have + zend_hash_update(c->nodes, key, key_len+1, (void*)ppNode, + sizeof(redisClusterNode*), NULL); + + // Return the RedisSock + return (*ppNode)->sock; +} + /* Attach a slave to a cluster node */ int cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, @@ -536,4 +623,252 @@ cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { return 0; } +/* Helper to find if we've got a host:port mapped in our cluster nodes. */ +static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, + unsigned short port) +{ + redisClusterNode **ret = NULL; + int key_len; + char key[1024]; + + key_len = snprintf(key,sizeof(key),"%s:%d", host, port); + + if(zend_hash_find(c->nodes, key, key_len+1, (void**)&ret)==SUCCESS) { + return *ret; + } + + // Not found + return NULL; +} + +/* Once we write a command to a node in our cluster, this function will check + * the reply type and extract information from those that will specify a length + * bit. If we encounter an error condition, we'll check for MOVED or ASK + * redirection, parsing out slot host and port so the caller can take + * appropriate action. + * + * In the case of a non MOVED/ASK error, we wlll set our cluster error + * condition so GetLastError can be queried by the client. + * + * This function will return -1 on a critical error (e.g. parse/communication + * error, 0 if no redirection was encountered, and 1 if the data was moved. */ +static int cluster_check_response(redisCluster *c, unsigned short slot, + REDIS_REPLY_TYPE *reply_type, + int *reply_len TSRMLS_DC) +{ + // Check for general socket EOF and then EOF on our reply type request + if((-1 == redis_check_eof(SLOT(c,slot)->sock)) || + (*reply_type = php_stream_getc(SLOT_STREAM(c,slot)))) + { + // Actual communications error + return -1; + } + + // In the event of an ERROR, check if it's a MOVED/ASK error + if(*reply_type == TYPE_ERR) { + char inbuf[1024]; + int moved; + + // Attempt to read the error + if(php_stream_gets(SLOT_STREAM(c,slot), inbuf, sizeof(inbuf))<0) { + return -1; + } + + // Check for MOVED or ASK redirection + if((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { + char *pslot, *phost, *pport; + + // Move pased MOVED or ASK error message + if(moved) { + pslot = inbuf + MOVED_LEN; + } else { + pslot = inbuf + ASK_LEN; + } + + // We will need to see a slot separator + if(!(phost = strchr(pslot, ' '))) { + return -1; + } + + // Null terminate at the separator + *phost++ = '\0'; + + // We'll need to see host:port separator + if(!(pport = strchr(phost, ':'))) { + return -1; + } + + // Null terminate here + *pport++ = '\0'; + + // Set our cluster redirection information + c->redir_type = moved ? REDIR_MOVED : REDIR_ASK; + strncpy(c->redir_host, phost, sizeof(c->redir_host)); + c->redir_host_len = pport - phost - 1; + c->redir_slot = (unsigned short)atoi(pslot); + c->redir_port = (unsigned short)atoi(pport); + + // Data moved + return 1; + } else { + // Capture the error string Redis returned + cluster_set_err(c, inbuf+1, strlen(inbuf+1)-2); + return 0; + } + } + + // For BULK, MULTI BULK, or simply INTEGER response typese we can get + // the response length. + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || + *reply_type == TYPE_MULTIBULK) + { + char inbuf[255]; + + if(php_stream_gets(SLOT_STREAM(c,slot), inbuf, sizeof(inbuf))<0) { + return -1; + } + + // Size information + *reply_len = atoi(inbuf); + } + + // Clear out any previous error, and return that the data is here + cluster_set_err(c, NULL, 0); + return 0; +} + +/* Attempt to write to a cluster node. If the node is NULL (e.g. + * it's been umapped, we keep falling back until we run out of nodes + * to try */ +static int cluster_sock_write(redisCluster *c, unsigned short slot, + const char *cmd, size_t sz TSRMLS_DC) +{ + RedisSock *redis_sock; + int i; + + // If we're in an ASK redirection state, attempt a connection to that + // host and port. Otherwise, try on the requested slot. + if(c->redir_type != REDIR_ASK) { + redis_sock = SLOT_SOCK(c,slot); + } else { + redis_sock = cluster_get_asking_sock(c); + } + + // First attempt to write it to the slot that's been requested + if(redis_sock && !redis_check_eof(redis_sock TSRMLS_CC) && + !php_stream_write(redis_sock->stream, cmd, sz)) + { + // We were able to write it + return 0; + } + + // Fall back by attempting to write the request to other nodes + // TODO: Randomize the slots we request from + for(i=0;istream, cmd, sz)) + { + // Return the slot where we actually sent the request + return i; + } + } + + // We were unable to write to any node in our cluster + return -1; +} + +/* Provided a redisCluster object, the slot where we thought data was and + * the slot where data was moved, update our node mapping */ +static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { + redisClusterNode *node; + + // Invalidate original slot + c->master[orig_slot] = NULL; + + // Do we already have the new slot mapped + if(c->master[c->redir_slot]) { + // Has this slot changed host or port + if(CLUSTER_REDIR_CMP(c)) { + // Check to see if we have this new node mapped + node = cluster_find_node(c, c->redir_host, c->redir_port); + + if(node) { + // Just point to this slot + c->master[c->redir_slot] = node; + } else { + // Create our node + node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, + c->redir_host_len, c->redir_port, c->redir_slot, + c->redir_slot); + + // Now point our slot at the node + c->master[c->redir_slot] = node; + } + } + } else { + // Check to see if the ip and port are mapped + node = cluster_find_node(c, c->redir_host, c->redir_port); + if(!node) { + node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, + c->redir_host_len, c->redir_port, c->redir_slot, c->redir_slot); + } + + // Map the slot to this node + c->master[c->redir_slot] = node; + } +} + +/* Send a command to given slot in our cluster. If we get a MOVED + * or ASK error we attempt to send the command to the node as + * directed. */ +PHPAPI short cluster_send_command(redisCluster *c, short slot, + const char *cmd, int cmd_len TSRMLS_DC) +{ + REDIS_REPLY_TYPE reply_type; + int resp, reply_len, rslot = slot; + + // Issue commands until we find the right node or fail + do { + // Attempt to send the command to the slot requested + if((slot = cluster_sock_write(c, slot, cmd, cmd_len + TSRMLS_CC))==-1) + { + // We have no choice but to throw an exception. We + // can't communicate with any node at all. + zend_throw_exception(redis_cluster_exception_ce, + "Can't communicate with any node in the cluster", + 0 TSRMLS_CC); + return -1; + } + + // Check the response from the slot we ended up querying. + resp = cluster_check_response(c, slot, &reply_type, &reply_len + TSRMLS_CC); + + // If we're getting an error condition, impose a slight delay before + // we try again (e.g. server went down, election in process). If the + // data has been moved, update node configuration, and if ASK has been + // encountered, we'll just try again at that slot. + if(resp == -1) { + // TODO: More robust error handling, count errors and ultimately + // fail? + sleep(1); + } else if(resp == 1) { + // In case of a MOVED redirection, update our node mapping + if(c->redir_type == REDIR_MOVED) { + cluster_update_slot(c, rslot); + } + } + } while(resp != 0); + + // Clear out redirection flag + c->redir_type = REDIR_NONE; + + // Success, return the slot where data exists. + return slot; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index e54343db14..0649ec011e 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -29,6 +29,33 @@ #define CLUSTER_NODES_CONNECTED 7 #define CLUSTER_SLOTS 8 +/* Cluster redirection enum */ +typedef enum CLUSTER_REDIR_TYPE { + REDIR_NONE, + REDIR_MOVED, + REDIR_ASK +} CLUSTER_REDIR_TYPE; + +/* MOVED/ASK comparison macros */ +#define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ + p[5]=='D' && p[6]==' ') +#define IS_ASK(p) (p[0]=='A' && p[1]=='S' && p[3]=='K' && p[4]==' ') + +/* MOVED/ASK lengths */ +#define MOVED_LEN (sizeof("MOVED ")-1) +#define ASK_LEN (sizeof("ASK ")-1) + +/* Slot/RedisSock/RedisSock->stream macros */ +#define SLOT(c,s) (c->master[s]) +#define SLOT_SOCK(c,s) (SLOT(c,s)->sock) +#define SLOT_STREAM(c,s) (SLOT_SOCK(c,s)->stream) + +/* Compare redirection slot information with what we have */ +#define CLUSTER_REDIR_CMP(c) \ + (SLOT_SOCK(c,c->redir_slot)->port != c->redir_port || \ + strlen(SLOT_SOCK(c,c->redir_slot)->host) != c->redir_host_len || \ + memcmp(SLOT_SOCK(c,c->redir_slot)->host,c->redir_host,c->redir_host_len)) + /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); @@ -81,20 +108,37 @@ typedef struct redisCluster { /* All RedisCluster objects we've created/are connected to */ HashTable *nodes; + + /* The last ERROR we encountered */ + char *err; + int err_len; + + /* Last MOVED or ASK redirection response information */ + CLUSTER_REDIR_TYPE redir_type; + char redir_host[255]; + int redir_host_len; + unsigned short redir_slot; + unsigned short redir_port; } redisCluster; /* Hash a key to it's slot, using the Redis Cluster hash algorithm */ unsigned short cluster_hash_key_zval(zval *key); unsigned short cluster_hash_key(const char *key, int len); -/* Send a command to where we think the key(s) should live and redirect when needed */ -PHPAPI int cluster_send_command(redisCluster *cluster, unsigned short slot, - const char *cmd, int cmd_len); +/* Send a command to where we think the key(s) should live and redirect when + * needed */ +PHPAPI short cluster_send_command(redisCluster *cluster, short slot, + const char *cmd, int cmd_len TSRMLS_DC); PHPAPI int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds); PHPAPI int cluster_map_keyspace(redisCluster *cluster TSRMLS_DC); PHPAPI void cluster_free_node(redisClusterNode *node); -PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len TSRMLS_DC); -PHPAPI int cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, clusterNodeInfo *slave TSRMLS_DC); + +PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, + int *len TSRMLS_DC); + +PHPAPI int cluster_node_add_slave(redisCluster *cluster, + redisClusterNode *master, + clusterNodeInfo *slave TSRMLS_DC); #endif diff --git a/redis_cluster.c b/redis_cluster.c index 0f3b09d809..7af4bf4f04 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -162,19 +162,22 @@ PHP_METHOD(RedisCluster, __construct) { // Validate timeout if(timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, "Invalid timeout", 0 TSRMLS_CC); + zend_throw_exception(redis_cluster_exception_ce, + "Invalid timeout", 0 TSRMLS_CC); RETURN_FALSE; } // Validate our read timeout if(read_timeout < 0L || read_timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, "Invalid read timeout", 0 TSRMLS_CC); + zend_throw_exception(redis_cluster_exception_ce, + "Invalid read timeout", 0 TSRMLS_CC); RETURN_FALSE; } // TODO: Implement seed retrieval from php.ini if(!z_seeds || zend_hash_num_elements(Z_ARRVAL_P(z_seeds))==0) { - zend_throw_exception(redis_cluster_exception_ce, "Must pass seeds", 0 TSRMLS_CC); + zend_throw_exception(redis_cluster_exception_ce, + "Must pass seeds", 0 TSRMLS_CC); RETURN_FALSE; } @@ -187,6 +190,7 @@ PHP_METHOD(RedisCluster, __construct) { /* GET */ PHP_METHOD(RedisCluster, get) { + RETURN_FALSE; } From b1d0ffedb3495c366e4a0e95c362636e636e0d83 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 May 2014 02:29:33 -0700 Subject: [PATCH 0210/1986] Integrated command loop processing to send command to our Redis Cluster instance, as well as formalized a generic macro we will use for any return type. Further work to be done creating the response parsing generics and then later in combining them with standard phpredis response parsing. --- cluster_library.c | 16 ++++++++-------- cluster_library.h | 20 ++++++++++++++++++++ redis_cluster.c | 18 ++++++++++++++++++ redis_cluster.h | 11 ++++++++--- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 24ea4b9e79..2f73ae434b 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -653,8 +653,7 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, * This function will return -1 on a critical error (e.g. parse/communication * error, 0 if no redirection was encountered, and 1 if the data was moved. */ static int cluster_check_response(redisCluster *c, unsigned short slot, - REDIS_REPLY_TYPE *reply_type, - int *reply_len TSRMLS_DC) + REDIS_REPLY_TYPE *reply_type, TSRMLS_DC) { // Check for general socket EOF and then EOF on our reply type request if((-1 == redis_check_eof(SLOT(c,slot)->sock)) || @@ -729,7 +728,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, } // Size information - *reply_len = atoi(inbuf); + c->reply_len = atoi(inbuf); } // Clear out any previous error, and return that the data is here @@ -824,7 +823,7 @@ static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { /* Send a command to given slot in our cluster. If we get a MOVED * or ASK error we attempt to send the command to the node as * directed. */ -PHPAPI short cluster_send_command(redisCluster *c, short slot, +PHPAPI int cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { REDIS_REPLY_TYPE reply_type; @@ -845,8 +844,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, } // Check the response from the slot we ended up querying. - resp = cluster_check_response(c, slot, &reply_type, &reply_len - TSRMLS_CC); + resp = cluster_check_response(c, slot, &reply_type TSRMLS_CC); // If we're getting an error condition, impose a slight delay before // we try again (e.g. server went down, election in process). If the @@ -864,11 +862,13 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, } } while(resp != 0); - // Clear out redirection flag + // Inform the cluster where to read the rest of our response, + // and clear out redirection flag. + c->reply_slot = slot; c->redir_type = REDIR_NONE; // Success, return the slot where data exists. - return slot; + return 0; } /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index 0649ec011e..97bd1a197c 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -56,6 +56,16 @@ typedef enum CLUSTER_REDIR_TYPE { strlen(SLOT_SOCK(c,c->redir_slot)->host) != c->redir_host_len || \ memcmp(SLOT_SOCK(c,c->redir_slot)->host,c->redir_host,c->redir_host_len)) +/* Send a request to our cluster, and process it's response */ +#define CLUSTER_PROCESS_REQUEST(cluster, slot, cmd, cmd_len, resp_cb) \ + if(cluster_send_command(cluster,slot,cmd,cmd_len TSRMLS_CC)<0 || \ + resp_cb(cluster, INTERNAL_FUNCTION_PARAM_PASSTHRU)<0) \ + { \ + RETVAL_FALSE; \ + } \ + efree(cmd); \` + + /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); @@ -113,6 +123,13 @@ typedef struct redisCluster { char *err; int err_len; + /* The slot where we should read replies */ + short reply_slot; + + /* The last reply length we got, which we'll use to parse and + * format our replies to the client. */ + int reply_len; + /* Last MOVED or ASK redirection response information */ CLUSTER_REDIR_TYPE redir_type; char redir_host[255]; @@ -141,4 +158,7 @@ PHPAPI int cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, clusterNodeInfo *slave TSRMLS_DC); +/* Response handlers */ +PHPAPI int cluster_bulk_response( + #endif diff --git a/redis_cluster.c b/redis_cluster.c index 7af4bf4f04..805cd9a27c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -190,6 +190,24 @@ PHP_METHOD(RedisCluster, __construct) { /* GET */ PHP_METHOD(RedisCluster, get) { + zval *z_obj; + char *cmd, *key; + int cmd_len, key_len; + redisCluster *c = GET_CONTEXT(); + + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os", &z_obj, redis_cluster_ce, &key, + &key_len)==FAILURE) + { + RETURN_FALSE; + } + + // Format our command + cmd_len = redis_cmd_format_static(&cmd, "GET", "s", key, key_len); + + // Send it to our cluster + CLUSTER_PROCESS_REQUEST(c, cluster_hash_key(key,key_len), cmd, cmd_len); + CLUSTER_READ_RESPONSE(c, cluster_string_response); RETURN_FALSE; } diff --git a/redis_cluster.h b/redis_cluster.h index d7b2f439ed..5998689f66 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -10,18 +10,23 @@ #define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) /* Get attached object context */ -#define GET_CONTEXT() (redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC) +#define GET_CONTEXT() \ + (redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC) /* For the creation of RedisCluster specific exceptions */ PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); -/* Initialization and cleanup of our attached object */ -zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC); +/* Create cluster context */ +zend_object_value create_cluster_context(zend_class_entry *class_type + TSRMLS_DC); + +/* Free cluster context struct */ void free_cluster_context(void *object TSRMLS_DC); /* Inittialize our class with PHP */ void init_rediscluster(TSRMLS_D); +/* RedisCluster method implementation */ PHP_METHOD(RedisCluster, __construct); PHP_METHOD(RedisCluster, get); PHP_METHOD(RedisCluster, set); From 2ce8e4f6dd1ddd9c9b656a08dbedfc6d76fe4500 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 May 2014 12:50:08 -0700 Subject: [PATCH 0211/1986] Modify redis_serialize and redis_unserialize Modify these functions to accept a direct serializer value, rather than a RedisSock pointer, such that we can re-use them in the context of a cluster, without having to set each individual RedisSock object to the mode when it's set/changed. --- library.c | 45 +++++--- library.h | 10 +- redis.c | 332 +++++------------------------------------------------- 3 files changed, 61 insertions(+), 326 deletions(-) diff --git a/library.c b/library.c index f701d1d827..64348fa602 100644 --- a/library.c +++ b/library.c @@ -1290,14 +1290,18 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } IF_MULTI_OR_PIPELINE() { zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + if(redis_unserialize(redis_sock->serializer, response, response_len, + &z TSRMLS_CC) == 1) + { efree(response); add_next_index_zval(z_tab, z); } else { add_next_index_stringl(z_tab, response, response_len, 0); } } else { - if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { + if(redis_unserialize(redis_sock->serializer, response, response_len, + &return_value TSRMLS_CC) == 0) + { RETURN_STRINGL(response, response_len, 0); } else { efree(response); @@ -1770,7 +1774,10 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) can_unserialize = 0; - if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { + if(can_unserialize && redis_unserialize(redis_sock->serializer, + response, response_len, + &z TSRMLS_CC) == 1) + { efree(response); add_next_index_zval(z_tab, z); } else { @@ -1824,18 +1831,20 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); - } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); - } - } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); - } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval *z = NULL; + if(redis_unserialize(redis_sock->serializer, response, + response_len, &z TSRMLS_CC) == 1) + { + efree(response); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + } + } else { + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + } + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); @@ -1968,8 +1977,10 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_ return 0; } -PHP_REDIS_API int -redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) { +PHPAPI int +redis_unserialize(int mode, const char *val, int val_len, zval **return_value + TSRMLS_DC) +{ php_unserialize_data_t var_hash; int ret, rv_free = 0; diff --git a/library.h b/library.h index 2e2d770acd..56f9ffeace 100644 --- a/library.h +++ b/library.h @@ -57,13 +57,9 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); -PHP_REDIS_API int -redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC); -PHP_REDIS_API int -redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC); - -PHP_REDIS_API int -redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC); +PHP_REDIS_API int redis_serialize(int serializer, zval *z, char **val, int *val_len TSRMLS_DC); +PHP_REDIS_API int redis_unserialize(int serializer, const char *val, int val_len, zval **return_value TSRMLS_DC); +PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC); /* diff --git a/redis.c b/redis.c index aaa306d027..40dabb0ec8 100644 --- a/redis.c +++ b/redis.c @@ -962,7 +962,7 @@ PHP_METHOD(Redis, set) { } /* Serialization, key prefixing */ - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { @@ -1051,7 +1051,7 @@ PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keywo RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len); if(val_free) STR_FREE(val); @@ -1100,7 +1100,7 @@ PHP_METHOD(Redis, setnx) RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -1138,7 +1138,7 @@ PHP_METHOD(Redis, getSet) RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -1955,7 +1955,7 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -2031,8 +2031,8 @@ PHP_METHOD(Redis, lInsert) if(strncasecmp(position, "after", 5) == 0 || strncasecmp(position, "before", 6) == 0) { key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serialier, z_value, &val, &val_len TSRMLS_CC); + pivot_free = redis_serialize(redis_sock->serializer, z_pivot, &pivot, &pivot_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2205,7 +2205,7 @@ PHP_METHOD(Redis, lRemove) /* LREM key count value */ - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len); if(val_free) STR_FREE(val); @@ -2408,7 +2408,7 @@ PHP_METHOD(Redis, sMove) RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len); @@ -2515,7 +2515,7 @@ PHP_METHOD(Redis, sContains) RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -2654,7 +2654,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * if(!all_keys && j != 0) { /* not just operating on keys */ if(can_serialize) { - keys_to_free[j] = redis_serialize(redis_sock, *z_value_pp, &keys[j], &keys_len[j] TSRMLS_CC); + keys_to_free[j] = redis_serialize(redis_sock->serializer, *z_value_pp, &keys[j], &keys_len[j] TSRMLS_CC); } else { convert_to_string(*z_value_pp); keys[j] = Z_STRVAL_PP(z_value_pp); @@ -2695,7 +2695,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * if(!all_keys && j != 0) { /* not just operating on keys */ if(can_serialize) { - keys_to_free[j] = redis_serialize(redis_sock, z_args[i], &keys[j], &keys_len[j] TSRMLS_CC); + keys_to_free[j] = redis_serialize(redis_sock->serializer, z_args[i], &keys[j], &keys_len[j] TSRMLS_CC); } else { convert_to_string(z_args[i]); keys[j] = Z_STRVAL_P(z_args[i]); @@ -3389,7 +3389,7 @@ PHP_METHOD(Redis, lSet) { RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len); if(val_free) STR_FREE(val); @@ -3794,7 +3794,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI if(step == 0) argc++; /* found a valid arg */ - val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->seriaizer, *z_value_pp, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len TSRMLS_CC); if(step == 0) { /* counting */ @@ -3974,8 +3974,8 @@ PHP_METHOD(Redis, zAdd) { smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); for(i = 1; i < argc; i +=2) { - convert_to_double(z_args[i]); /* convert score to double */ - val_free = redis_serialize(redis_sock, z_args[i+1], &val, &val_len TSRMLS_CC); /* possibly serialize value. */ + convert_to_double(z_args[i]); // convert score to double + val_free = redis_serialize(redis_sock->serializer, z_args[i+1], &val, &val_len TSRMLS_CC); // possibly serialize value. /* add score */ score = Z_DVAL_P(z_args[i]); @@ -4447,7 +4447,7 @@ PHP_METHOD(Redis, zScore) RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -4480,7 +4480,7 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -4530,7 +4530,7 @@ PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *key RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len); if(val_free) STR_FREE(val); @@ -4746,7 +4746,7 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len); if(val_free) STR_FREE(val); @@ -5202,7 +5202,7 @@ PHP_METHOD(Redis, hMset) element_count += 2; /* key is set. */ - hval_free = redis_serialize(redis_sock, *z_value_p, &hval, &hval_len TSRMLS_CC); + hval_free = redis_serialize(redis_sock->serializer, *z_value_p, &hval, &hval_len TSRMLS_CC); /* Append our member and value in place */ redis_cmd_append_sstr(&set_cmds, hkey, hkey_len - 1); @@ -6812,8 +6812,8 @@ PHP_METHOD(Redis, _serialize) { RETURN_FALSE; } - /* Serialize, which will return a value even if no serializer is set */ - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + // Serialize, which will return a value even if no serializer is set + redis_serialize(redis_sock->serializer, z_val, &val, &val_len TSRMLS_CC); /* Return serialized value. Tell PHP to make a copy as some can be interned. */ RETVAL_STRINGL(val, val_len, 1); @@ -6842,9 +6842,13 @@ PHP_METHOD(Redis, _unserialize) { /* We only need to attempt unserialization if we have a serializer running */ if(redis_sock->serializer != REDIS_SERIALIZER_NONE) { zval *z_ret = NULL; - if(redis_unserialize(redis_sock, value, value_len, &z_ret TSRMLS_CC) == 0) { - /* Badly formed input, throw an execption */ - zend_throw_exception(redis_exception_ce, "Invalid serialized data, or unserialization error", 0 TSRMLS_CC); + if(redis_unserialize(redis_sock->serializer, value, value_len, &z_ret + TSRMLS_CC) == 0) + { + // Badly formed input, throw an execption + zend_throw_exception(redis_exception_ce, + "Invalid serialized data, or unserialization error", + 0 TSRMLS_CC); RETURN_FALSE; } RETURN_ZVAL(z_ret, 0, 1); @@ -7293,280 +7297,4 @@ PHP_METHOD(Redis, zscan) { generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN); } -/* - * HyperLogLog based commands - */ - -/* {{{ proto Redis::pfAdd(string key, array elements) }}} */ -PHP_METHOD(Redis, pfadd) { - zval *object; - RedisSock *redis_sock; - char *key; - int key_len, key_free, argc=1; - zval *z_mems, **z_mem; - HashTable *ht_mems; - HashPosition pos; - smart_str cmd = {0}; - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", - &object, redis_ce, &key, &key_len, &z_mems) - ==FAILURE) - { - RETURN_FALSE; - } - - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - // Grab members as an array - ht_mems = Z_ARRVAL_P(z_mems); - - // Total arguments we'll be sending - argc += zend_hash_num_elements(ht_mems); - - // If the array was empty we can just exit - if(argc < 2) { - RETURN_FALSE; - } - - // Start constructing our command - redis_cmd_init_sstr(&cmd, argc, "PFADD", sizeof("PFADD")-1); - - // Prefix our key if we're prefixing - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - redis_cmd_append_sstr(&cmd, key, key_len); - if(key_free) efree(key); - - // Iterate over members we're adding - for(zend_hash_internal_pointer_reset_ex(ht_mems, &pos); - zend_hash_get_current_data_ex(ht_mems, (void**)&z_mem, &pos)==SUCCESS; - zend_hash_move_forward_ex(ht_mems, &pos)) - { - char *mem; - int mem_len, val_free; - zval *z_tmp = NULL; - - // Serialize if requested - val_free = redis_serialize(redis_sock, *z_mem, &mem, &mem_len TSRMLS_CC); - - // Allow for non string members if we're not serializing - if(!val_free) { - if(Z_TYPE_PP(z_mem)==IS_STRING) { - mem = Z_STRVAL_PP(z_mem); - mem_len = Z_STRLEN_PP(z_mem); - } else { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_mem; - convert_to_string(z_tmp); - - mem = Z_STRVAL_P(z_tmp); - mem_len = Z_STRLEN_P(z_tmp); - } - } - - // Append this member - redis_cmd_append_sstr(&cmd, mem, mem_len); - - // Free memory if we serialized or converted types - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } else if(val_free) { - efree(mem); - } - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); -} - -/* {{{ proto Redis::pfCount(string key) }}} - * proto Redis::pfCount(array keys) }}} */ -PHP_METHOD(Redis, pfcount) { - zval *object, *z_keys, **z_key, *z_tmp = NULL; - HashTable *ht_keys; - HashPosition ptr; - RedisSock *redis_sock; - smart_str cmd = {0}; - int num_keys, key_len, key_free; - char *key; - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz", - &object, redis_ce, &z_keys)==FAILURE) - { - RETURN_FALSE; - } - - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* If we were passed an array of keys, iterate through them prefixing if - * required and capturing lengths and if we need to free them. Otherwise - * attempt to treat the argument as a string and just pass one */ - if (Z_TYPE_P(z_keys) == IS_ARRAY) { - /* Grab key hash table and the number of keys */ - ht_keys = Z_ARRVAL_P(z_keys); - num_keys = zend_hash_num_elements(ht_keys); - - /* There is no reason to send zero keys */ - if (num_keys == 0) { - RETURN_FALSE; - } - - /* Initialize the command with our number of arguments */ - redis_cmd_init_sstr(&cmd, num_keys, "PFCOUNT", sizeof("PFCOUNT")-1); - - /* Append our key(s) */ - for (zend_hash_internal_pointer_reset_ex(ht_keys, &ptr); - zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_keys, &ptr)) - { - /* Turn our value into a string if it isn't one */ - if (Z_TYPE_PP(z_key) != IS_STRING) { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_key; - zval_copy_ctor(z_tmp); - convert_to_string(z_tmp); - - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); - } else { - key = Z_STRVAL_PP(z_key); - key_len = Z_STRLEN_PP(z_key); - } - - /* Append this key to our command */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - redis_cmd_append_sstr(&cmd, key, key_len); - - /* Cleanup */ - if (key_free) efree(key); - if (z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } - } - } else { - /* Turn our key into a string if it's a different type */ - if (Z_TYPE_P(z_keys) != IS_STRING) { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_keys; - zval_copy_ctor(z_tmp); - convert_to_string(z_tmp); - - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); - } else { - key = Z_STRVAL_P(z_keys); - key_len = Z_STRLEN_P(z_keys); - } - - /* Construct our whole command */ - redis_cmd_init_sstr(&cmd, 1, "PFCOUNT", sizeof("PFCOUNT")-1); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - redis_cmd_append_sstr(&cmd, key, key_len); - - /* Cleanup */ - if (key_free) efree(key); - if (z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - } - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); -} -/* }}} */ - -/* {{{ proto Redis::pfMerge(array keys) }}}*/ -PHP_METHOD(Redis, pfmerge) { - zval *object; - RedisSock *redis_sock; - zval *z_keys, **z_key; - HashTable *ht_keys; - HashPosition pos; - smart_str cmd = {0}; - int key_len, key_free, argc=1; - char *key; - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", - &object, redis_ce, &key, &key_len, &z_keys)==FAILURE) - { - RETURN_FALSE; - } - - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - // Grab keys as an array - ht_keys = Z_ARRVAL_P(z_keys); - - // Total arguments we'll be sending - argc += zend_hash_num_elements(ht_keys); - - // If no keys were passed we can abort - if(argc<2) { - RETURN_FALSE; - } - - // Initial construction of our command - redis_cmd_init_sstr(&cmd, argc, "PFMERGE", sizeof("PFMERGE")-1); - - // Add our destination key (prefixed if necessary) - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - redis_cmd_append_sstr(&cmd, key, key_len); - if(key_free) efree(key); - - // Iterate our keys array - for(zend_hash_internal_pointer_reset_ex(ht_keys, &pos); - zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &pos)==SUCCESS; - zend_hash_move_forward_ex(ht_keys, &pos)) - { - zval *z_tmp = NULL; - - // Keys could look like a number - if(Z_TYPE_PP(z_key) == IS_STRING) { - key = Z_STRVAL_PP(z_key); - key_len = Z_STRLEN_PP(z_key); - } else { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_key; - convert_to_string(z_tmp); - - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); - } - - // Prefix our key if necessary and append this key - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - redis_cmd_append_sstr(&cmd, key, key_len); - if(key_free) efree(key); - - // Free temporary zval if we converted - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); -} - /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 3bd16b971b8484c1971316b67d713c66c0cd731b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 May 2014 15:00:50 -0700 Subject: [PATCH 0212/1986] Remove TSRMLS_CC from redis_key_prefix redis_key_prefix doesn't use any methods that require TSRMLS_CC, so remove it from all of the calls. This is to prepare for making command construction generic between phpredis and phpredis-cluster implementations, so they can share. --- library.c | 2 +- redis.c | 168 +++++++++++++++++++++++++++--------------------------- 2 files changed, 85 insertions(+), 85 deletions(-) diff --git a/library.c b/library.c index 64348fa602..045bf373d4 100644 --- a/library.c +++ b/library.c @@ -2032,7 +2032,7 @@ redis_unserialize(int mode, const char *val, int val_len, zval **return_value } PHP_REDIS_API int -redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { +redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len) { int ret_len; char *ret; diff --git a/redis.c b/redis.c index 40dabb0ec8..17afa3823b 100644 --- a/redis.c +++ b/redis.c @@ -773,7 +773,7 @@ PHP_METHOD(Redis, bitop) keys[i] = Z_STRVAL_P(z_args[i]); keys_len[i] = Z_STRLEN_P(z_args[i]); if(i != 0) - key_free = redis_key_prefix(redis_sock, &keys[i], &keys_len[i] TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &keys[i], &keys_len[i]); } /* start building the command */ @@ -840,7 +840,7 @@ PHP_METHOD(Redis, bitcount) } /* BITCOUNT key start end */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "BITCOUNT", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -881,7 +881,7 @@ PHP_METHOD(Redis, bitpos) } /* Prefix our key */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Various command semantics */ if(argc == 2) { @@ -963,7 +963,7 @@ PHP_METHOD(Redis, set) { /* Serialization, key prefixing */ val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { HashTable *kt = Z_ARRVAL_P(z_opts); @@ -1052,7 +1052,7 @@ PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keywo } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1101,7 +1101,7 @@ PHP_METHOD(Redis, setnx) } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1139,7 +1139,7 @@ PHP_METHOD(Redis, getSet) } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1203,7 +1203,7 @@ PHP_METHOD(Redis, echo) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s", key, key_len); if(key_free) efree(key); @@ -1238,8 +1238,8 @@ PHP_METHOD(Redis, renameKey) RETURN_FALSE; } - src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC); - dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC); + src_free = redis_key_prefix(redis_sock, &src, &src_len); + dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); cmd_len = redis_cmd_format_static(&cmd, "RENAME", "ss", src, src_len, dst, dst_len); if(src_free) efree(src); if(dst_free) efree(dst); @@ -1275,8 +1275,8 @@ PHP_METHOD(Redis, renameNx) RETURN_FALSE; } - src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC); - dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC); + src_free = redis_key_prefix(redis_sock, &src, &src_len); + dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); cmd_len = redis_cmd_format_static(&cmd, "RENAMENX", "ss", src, src_len, dst, dst_len); if(src_free) efree(src); if(dst_free) efree(dst); @@ -1310,7 +1310,7 @@ PHP_METHOD(Redis, get) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "GET", "s", key, key_len); if(key_free) efree(key); @@ -1370,7 +1370,7 @@ PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *ke if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); if (val == 1) { cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); } else { @@ -1450,7 +1450,7 @@ PHP_METHOD(Redis, incrByFloat) { } /* Prefix key, format command, free old key if necissary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val); if(key_free) efree(key); @@ -1564,7 +1564,7 @@ PHP_METHOD(Redis, getMultiple) } /* Apply key prefix if necissary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Append this key to our command */ redis_cmd_append_sstr(&cmd, key, key_len); @@ -1610,7 +1610,7 @@ PHP_METHOD(Redis, exists) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "EXISTS", "s", key, key_len); if(key_free) efree(key); @@ -1709,7 +1709,7 @@ PHP_METHOD(Redis, getKeys) RETURN_FALSE; } - pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len TSRMLS_CC); + pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len); cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pattern, pattern_len); if(pattern_free) efree(pattern); @@ -1742,7 +1742,7 @@ PHP_METHOD(Redis, type) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "TYPE", "s", key, key_len); if(key_free) efree(key); @@ -1772,7 +1772,7 @@ PHP_METHOD(Redis, append) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "APPEND", "ss", key, key_len, val, val_len); if(key_free) efree(key); @@ -1801,7 +1801,7 @@ PHP_METHOD(Redis, getRange) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "GETRANGE", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1829,7 +1829,7 @@ PHP_METHOD(Redis, setRange) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "SETRANGE", "sds", key, key_len, (int)offset, val, val_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1862,7 +1862,7 @@ PHP_METHOD(Redis, getBit) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd", key, key_len, (int)offset); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1896,7 +1896,7 @@ PHP_METHOD(Redis, setBit) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd", key, key_len, (int)offset, (int)val); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1925,7 +1925,7 @@ PHP_METHOD(Redis, strlen) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "STRLEN", "s", key, key_len); if(key_free) efree(key); @@ -1956,7 +1956,7 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2030,7 +2030,7 @@ PHP_METHOD(Redis, lInsert) if(strncasecmp(position, "after", 5) == 0 || strncasecmp(position, "before", 6) == 0) { - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); val_free = redis_serialize(redis_sock->serialier, z_value, &val, &val_len TSRMLS_CC); pivot_free = redis_serialize(redis_sock->serializer, z_pivot, &pivot, &pivot_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len); @@ -2077,7 +2077,7 @@ generic_pop_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_le RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); if(key_free) efree(key); @@ -2167,7 +2167,7 @@ PHP_METHOD(Redis, lSize) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "LLEN", "s", key, key_len); if(key_free) efree(key); @@ -2206,7 +2206,7 @@ PHP_METHOD(Redis, lRemove) /* LREM key count value */ val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2239,7 +2239,7 @@ PHP_METHOD(Redis, listTrim) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "LTRIM", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -2273,7 +2273,7 @@ PHP_METHOD(Redis, lGet) } /* LINDEX key pos */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "LINDEX", "sd", key, key_len, (int)index); if(key_free) efree(key); @@ -2306,7 +2306,7 @@ PHP_METHOD(Redis, lGetRange) } /* LRANGE key start end */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "LRANGE", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -2356,7 +2356,7 @@ PHP_METHOD(Redis, sSize) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "SCARD", "s", key, key_len); if(key_free) efree(key); @@ -2409,8 +2409,8 @@ PHP_METHOD(Redis, sMove) } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC); - dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC); + src_free = redis_key_prefix(redis_sock, &src, &src_len); + dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len); if(val_free) STR_FREE(val); if(src_free) efree(src); @@ -2456,7 +2456,7 @@ PHP_METHOD(Redis, sRandMember) } /* Prefix our key if necissary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* If we have two arguments, we're running with an optional COUNT, which will return */ /* a multibulk reply. Without the argument we'll return a string response */ @@ -2516,7 +2516,7 @@ PHP_METHOD(Redis, sContains) } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2548,7 +2548,7 @@ PHP_METHOD(Redis, sMembers) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "SMEMBERS", "s", key, key_len); if(key_free) efree(key); @@ -2673,7 +2673,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * keys[j] = Z_STRVAL_PP(z_value_pp); keys_len[j] = Z_STRLEN_PP(z_value_pp); - keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j] TSRMLS_CC); /* add optional prefix */ + keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j]); /* add optional prefix */ } cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; /* $ + size + NL + string + NL */ @@ -2714,7 +2714,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * /* If we have a timeout it should be the last argument, which we do not want to prefix */ if(!has_timeout || i < argc-1) { - keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j] TSRMLS_CC); /* add optional prefix TSRMLS_CC*/ + keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j]); /* add optional prefix TSRMLS_CC*/ } } @@ -2912,7 +2912,7 @@ PHP_METHOD(Redis, sort) { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format(&cmd, "$4" _NL "SORT" _NL "$%d" _NL "%s" _NL, key_len, key, key_len); if(key_free) efree(key); @@ -3133,7 +3133,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in cmd_sizes[2] = 4; /* Prefix our key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* second line, key */ cmd_sizes[3] = redis_cmd_format(&cmd_lines[3], "$%d", key_len); @@ -3329,7 +3329,7 @@ PHP_REDIS_API void generic_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keywor if(t[i] < '0' || t[i] > '9') RETURN_FALSE; - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, t, t_len); if(key_free) efree(key); @@ -3390,7 +3390,7 @@ PHP_METHOD(Redis, lSet) { } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -3563,7 +3563,7 @@ PHP_METHOD(Redis, persist) { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "PERSIST", "s", key, key_len); if(key_free) efree(key); @@ -3591,7 +3591,7 @@ PHP_REDIS_API void generic_ttl(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); if(key_free) efree(key); @@ -3712,7 +3712,7 @@ PHP_METHOD(Redis, move) { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "MOVE", "sd", key, key_len, dbNumber); if(key_free) efree(key); @@ -3795,7 +3795,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI argc++; /* found a valid arg */ val_free = redis_serialize(redis_sock->seriaizer, *z_value_pp, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len); if(step == 0) { /* counting */ cmd_len += 1 + integer_length(key_len) + 2 @@ -3846,8 +3846,8 @@ PHP_REDIS_API void common_rpoplpush(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red char *cmd; int cmd_len; - int srckey_free = redis_key_prefix(redis_sock, &srckey, &srckey_len TSRMLS_CC); - int dstkey_free = redis_key_prefix(redis_sock, &dstkey, &dstkey_len TSRMLS_CC); + int srckey_free = redis_key_prefix(redis_sock, &srckey, &srckey_len); + int dstkey_free = redis_key_prefix(redis_sock, &dstkey, &dstkey_len); if(timeout < 0) { cmd_len = redis_cmd_format_static(&cmd, "RPOPLPUSH", "ss", srckey, srckey_len, dstkey, dstkey_len); } else { @@ -3952,7 +3952,7 @@ PHP_METHOD(Redis, zAdd) { /* possibly serialize key */ key = Z_STRVAL_P(z_args[0]); key_len = Z_STRLEN_P(z_args[0]); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* start building the command */ smart_str_appendc(&buf, '*'); @@ -4034,7 +4034,7 @@ PHP_METHOD(Redis, zRange) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdds", key, key_len, start, end, "WITHSCORES", 10); } else { @@ -4097,7 +4097,7 @@ PHP_METHOD(Redis, zDeleteRangeByScore) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYSCORE", "sss", key, key_len, start, start_len, end, end_len); if(key_free) efree(key); @@ -4130,7 +4130,7 @@ PHP_METHOD(Redis, zDeleteRangeByRank) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYRANK", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -4164,7 +4164,7 @@ PHP_METHOD(Redis, zReverseRange) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdds", key, key_len, start, end, "WITHSCORES", 10); } else { @@ -4243,7 +4243,7 @@ redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { } } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { if(has_limit) { cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdds", @@ -4323,7 +4323,7 @@ PHP_METHOD(Redis, zCount) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "ZCOUNT", "sss", key, key_len, start, start_len, end, end_len); if(key_free) efree(key); @@ -4413,7 +4413,7 @@ PHP_METHOD(Redis, zCard) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "ZCARD", "s", key, key_len); if(key_free) efree(key); @@ -4448,7 +4448,7 @@ PHP_METHOD(Redis, zScore) } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4481,7 +4481,7 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4531,7 +4531,7 @@ PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *key } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4619,7 +4619,7 @@ PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command redis_cmd_init_sstr(&cmd, cmd_arg_count, command, command_len); /* Prefix our key if necessary and add the output key */ - key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len TSRMLS_CC); + int key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len); redis_cmd_append_sstr(&cmd, store_key, store_key_len); if(key_free) efree(store_key); @@ -4648,7 +4648,7 @@ PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command } /* Apply key prefix if necessary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Append this input set */ redis_cmd_append_sstr(&cmd, key, key_len); @@ -4747,7 +4747,7 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI } val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4789,7 +4789,7 @@ PHP_METHOD(Redis, hGet) if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "HGET", "ss", key, key_len, member, member_len); if(key_free) efree(key); @@ -4820,7 +4820,7 @@ PHP_METHOD(Redis, hLen) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "HLEN", "s", key, key_len); if(key_free) efree(key); @@ -4852,7 +4852,7 @@ generic_hash_command_2(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ ZVAL_BOOL(return_value, 0); return NULL; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, member, member_len); if(key_free) efree(key); @@ -4913,7 +4913,7 @@ generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ ZVAL_BOOL(return_value, 0); return NULL; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); if(key_free) efree(key); @@ -5001,7 +5001,7 @@ PHP_METHOD(Redis, hIncrByFloat) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "HINCRBYFLOAT", "ssf", key, key_len, member, member_len, val); if(key_free) efree(key); @@ -5042,7 +5042,7 @@ PHP_METHOD(Redis, hIncrBy) } /* HINCRBY key member amount */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "HINCRBY", "sss", key, key_len, member, member_len, val, val_len); if(key_free) efree(key); @@ -5084,7 +5084,7 @@ PHP_METHOD(Redis, hMget) { } /* Prefix our key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Allocate enough memory for the number of keys being requested */ z_keys = ecalloc(field_count, sizeof(zval *)); @@ -5168,7 +5168,7 @@ PHP_METHOD(Redis, hMset) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format(&cmd, "$5" _NL "HMSET" _NL "$%d" _NL "%s" _NL @@ -5548,7 +5548,7 @@ PHP_METHOD(Redis, publish) RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "PUBLISH", "ss", key, key_len, val, val_len); if(key_free) efree(key); @@ -5607,7 +5607,7 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub key_len = Z_STRLEN_PP(data); /* Prefix our key if neccisary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = spprintf(&cmd, 0, "%s %s", cmd, key); @@ -6236,7 +6236,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, key_len = Z_STRLEN_P(arg); /* Prefix if necissary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* With a pattern */ cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", "CHANNELS", sizeof("CHANNELS")-1, @@ -6281,7 +6281,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, } /* Apply prefix if required */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Append this channel */ redis_cmd_append_sstr(&cmd, key, key_len); @@ -6429,7 +6429,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val old_cmd = *ret; /* If this is still a key argument, prefix it if we've been set up to prefix keys */ - key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC) : 0; + key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len) : 0; /* Append this key to our EVAL command, free our old command */ cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, cmd_len, key_len, key, key_len); @@ -6630,7 +6630,7 @@ PHP_METHOD(Redis, dump) { } /* Prefix our key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "DUMP", "s", key, key_len); if(key_free) efree(key); @@ -6695,7 +6695,7 @@ PHP_METHOD(Redis, restore) { } /* Prefix the key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "RESTORE", "sls", key, key_len, ttl, value, value_len); if(key_free) efree(key); @@ -6730,8 +6730,8 @@ PHP_METHOD(Redis, migrate) { RETURN_FALSE; } - /* Prefix our key if we need to, build our command */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + // Prefix our key if we need to, build our command + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Construct our command */ if(copy && replace) { @@ -6783,7 +6783,7 @@ PHP_METHOD(Redis, _prefix) { /* Prefix our key if we need to */ if(redis_sock->prefix != NULL && redis_sock->prefix_len > 0) { - redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + redis_key_prefix(redis_sock, &key, &key_len); RETURN_STRINGL(key, key_len, 0); } else { RETURN_STRINGL(key, key_len, 1); @@ -7241,7 +7241,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Prefix our key if we've got one and we have a prefix set */ if(key_len) { - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); } /** From 32d49c6f9d05549ebde67caa35b24ea60fe06a54 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 May 2014 17:46:01 -0700 Subject: [PATCH 0213/1986] Revert to using a RedisSock pointer for serialization --- library.c | 17 ++++++++--------- redis.c | 42 +++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/library.c b/library.c index 045bf373d4..09c486ccdd 100644 --- a/library.c +++ b/library.c @@ -1290,7 +1290,7 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } IF_MULTI_OR_PIPELINE() { zval *z = NULL; - if(redis_unserialize(redis_sock->serializer, response, response_len, + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { efree(response); @@ -1299,7 +1299,7 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_stringl(z_tab, response, response_len, 0); } } else { - if(redis_unserialize(redis_sock->serializer, response, response_len, + if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { RETURN_STRINGL(response, response_len, 0); @@ -1774,9 +1774,8 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) can_unserialize = 0; - if(can_unserialize && redis_unserialize(redis_sock->serializer, - response, response_len, - &z TSRMLS_CC) == 1) + if(can_unserialize && redis_unserialize(redis_sock, response, + response_len, &z TSRMLS_CC)==1) { efree(response); add_next_index_zval(z_tab, z); @@ -1832,8 +1831,8 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { zval *z = NULL; - if(redis_unserialize(redis_sock->serializer, response, - response_len, &z TSRMLS_CC) == 1) + if(redis_unserialize(redis_sock, response, response_len, &z + TSRMLS_CC) == 1) { efree(response); add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); @@ -1978,8 +1977,8 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_ } PHPAPI int -redis_unserialize(int mode, const char *val, int val_len, zval **return_value - TSRMLS_DC) +redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, + zval **return_value TSRMLS_DC) { php_unserialize_data_t var_hash; diff --git a/redis.c b/redis.c index 17afa3823b..27f70c042a 100644 --- a/redis.c +++ b/redis.c @@ -962,7 +962,7 @@ PHP_METHOD(Redis, set) { } /* Serialization, key prefixing */ - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { @@ -1051,7 +1051,7 @@ PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keywo RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len); if(val_free) STR_FREE(val); @@ -1100,7 +1100,7 @@ PHP_METHOD(Redis, setnx) RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -1138,7 +1138,7 @@ PHP_METHOD(Redis, getSet) RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -1955,7 +1955,7 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -2031,8 +2031,8 @@ PHP_METHOD(Redis, lInsert) if(strncasecmp(position, "after", 5) == 0 || strncasecmp(position, "before", 6) == 0) { key_free = redis_key_prefix(redis_sock, &key, &key_len); - val_free = redis_serialize(redis_sock->serialier, z_value, &val, &val_len TSRMLS_CC); - pivot_free = redis_serialize(redis_sock->serializer, z_pivot, &pivot, &pivot_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC); cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2205,7 +2205,7 @@ PHP_METHOD(Redis, lRemove) /* LREM key count value */ - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len); if(val_free) STR_FREE(val); @@ -2408,7 +2408,7 @@ PHP_METHOD(Redis, sMove) RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len); @@ -2515,7 +2515,7 @@ PHP_METHOD(Redis, sContains) RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -2654,7 +2654,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * if(!all_keys && j != 0) { /* not just operating on keys */ if(can_serialize) { - keys_to_free[j] = redis_serialize(redis_sock->serializer, *z_value_pp, &keys[j], &keys_len[j] TSRMLS_CC); + keys_to_free[j] = redis_serialize(redis_sock, *z_value_pp, &keys[j], &keys_len[j] TSRMLS_CC); } else { convert_to_string(*z_value_pp); keys[j] = Z_STRVAL_PP(z_value_pp); @@ -2695,7 +2695,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * if(!all_keys && j != 0) { /* not just operating on keys */ if(can_serialize) { - keys_to_free[j] = redis_serialize(redis_sock->serializer, z_args[i], &keys[j], &keys_len[j] TSRMLS_CC); + keys_to_free[j] = redis_serialize(redis_sock, z_args[i], &keys[j], &keys_len[j] TSRMLS_CC); } else { convert_to_string(z_args[i]); keys[j] = Z_STRVAL_P(z_args[i]); @@ -3389,7 +3389,7 @@ PHP_METHOD(Redis, lSet) { RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len); if(val_free) STR_FREE(val); @@ -3794,7 +3794,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI if(step == 0) argc++; /* found a valid arg */ - val_free = redis_serialize(redis_sock->seriaizer, *z_value_pp, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len); if(step == 0) { /* counting */ @@ -3975,7 +3975,7 @@ PHP_METHOD(Redis, zAdd) { for(i = 1; i < argc; i +=2) { convert_to_double(z_args[i]); // convert score to double - val_free = redis_serialize(redis_sock->serializer, z_args[i+1], &val, &val_len TSRMLS_CC); // possibly serialize value. + val_free = redis_serialize(redis_sock, z_args[i+1], &val, &val_len TSRMLS_CC); // possibly serialize value. /* add score */ score = Z_DVAL_P(z_args[i]); @@ -4447,7 +4447,7 @@ PHP_METHOD(Redis, zScore) RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -4480,7 +4480,7 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); @@ -4530,7 +4530,7 @@ PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *key RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len); if(val_free) STR_FREE(val); @@ -4746,7 +4746,7 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI RETURN_FALSE; } - val_free = redis_serialize(redis_sock->serializer, z_value, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len); if(val_free) STR_FREE(val); @@ -5202,7 +5202,7 @@ PHP_METHOD(Redis, hMset) element_count += 2; /* key is set. */ - hval_free = redis_serialize(redis_sock->serializer, *z_value_p, &hval, &hval_len TSRMLS_CC); + hval_free = redis_serialize(redis_sock, *z_value_p, &hval, &hval_len TSRMLS_CC); /* Append our member and value in place */ redis_cmd_append_sstr(&set_cmds, hkey, hkey_len - 1); @@ -6813,7 +6813,7 @@ PHP_METHOD(Redis, _serialize) { } // Serialize, which will return a value even if no serializer is set - redis_serialize(redis_sock->serializer, z_val, &val, &val_len TSRMLS_CC); + redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); /* Return serialized value. Tell PHP to make a copy as some can be interned. */ RETVAL_STRINGL(val, val_len, 1); From 2640918426b0059786f7ddbfcef0c8bd070a86be Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 May 2014 18:01:53 -0700 Subject: [PATCH 0214/1986] Added a 'k' type to redis_cmd_format_static Added a specialized string type "key", or 'k' specifier to redis_cmd_format_static, which takes the length as well as a RedisSock* pointer, and will automatically prefix the key if we should. --- library.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/library.c b/library.c index 09c486ccdd..3ec3047477 100644 --- a/library.c +++ b/library.c @@ -502,7 +502,6 @@ redis_cmd_format_header(char **ret, char *keyword, int arg_count) { int redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { - char *p = format; va_list ap; smart_str buf = {0}; @@ -527,14 +526,24 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { switch(*p) { case 's': { - char *val = va_arg(ap, char*); - int val_len = va_arg(ap, int); - smart_str_append_long(&buf, val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, val, val_len); - } + char *val = va_arg(ap, char*); + int val_len = va_arg(ap, int); + smart_str_append_long(&buf, val_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, val, val_len); + } break; - + case 'k': { + char *key = va_arg(ap, char*); + int key_len = va_arg(ap, int); + RedisSock *redis_sock = va_arg(ap, RedisSock*); + int key_free = redis_key_prefix(&key, &key_len); + smart_str_append_long(&buf, key_len); + smart_str_appendl(&buf, _NL, sizeof(_NL)-1); + smart_str_appendl(&buf,key,key_len); + if(key_free) efree(key); + } + break; case 'f': case 'F': { double d = va_arg(ap, double); From e1c80864f19e1d8159bad2c17e83def9ac9845f8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 May 2014 19:04:03 -0700 Subject: [PATCH 0215/1986] Add a 'v' type to redis_cmd_format_static Added a new format specifier, 'v', to redis_cmd_format_static which stands for 'value', meaning that we can automatically serialize the value if our RedisSock* object specifies that we should. This should remove quite a bit of redundant code --- library.c | 14 +++++++++++++- library.h | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 3ec3047477..ca7e37f7b9 100644 --- a/library.c +++ b/library.c @@ -501,7 +501,9 @@ redis_cmd_format_header(char **ret, char *keyword, int arg_count) { } int -redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { +redis_cmd_format_static(char **ret, char *keyword, char *format TSRMLS_DC, + ...) +{ char *p = format; va_list ap; smart_str buf = {0}; @@ -543,6 +545,16 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { smart_str_appendl(&buf,key,key_len); if(key_free) efree(key); } + break; + case 'v': { + char *zval = va_arg(ap,zval*); + RedisSock *redis_sock = va_arg(ap,RedisSock*); + int val_free = redis_serialize(redis_sock,&val,&val_len TSRMLS_CC); + smart_str_append_long(&buf,val_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf,val,val_len); + if(val_free) STR_FREE(val); + } break; case 'f': case 'F': { diff --git a/library.h b/library.h index 56f9ffeace..33799883e4 100644 --- a/library.h +++ b/library.h @@ -1,7 +1,7 @@ void add_constant_long(zend_class_entry *ce, char *name, int value); int integer_length(int i); int redis_cmd_format(char **ret, char *format, ...); -int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); +int redis_cmd_format_static(char **ret, char *keyword, char *format TSRMLS_DC, ...); int redis_cmd_format_header(char **ret, char *keyword, int arg_count); int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len); int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len); From b3c83ce450a71f4fff977d001b0474849b921cfd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 May 2014 20:11:36 -0700 Subject: [PATCH 0216/1986] Incorporate TSRMLS_CC into redis_cmd_format_static Add our TSRMLS_CC argument to each instance of redis_cmd_format_static. --- library.c | 39 +++--- redis.c | 311 ++++++++++++++++++++++++++++++------------------ redis_session.c | 18 ++- 3 files changed, 227 insertions(+), 141 deletions(-) diff --git a/library.c b/library.c index ca7e37f7b9..978e063868 100644 --- a/library.c +++ b/library.c @@ -324,9 +324,9 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes * Parse MOVED or ASK redirection (Redis Cluster) * We should get slot host:port */ -PHPAPI -int redis_sock_redir(RedisSock *redis_sock, const char *msg, int len, - MOVED_TYPE type TSRMLS_DC) +PHPAPI +int redis_sock_redir(RedisSock *redis_sock, const char *msg, int len, + MOVED_TYPE type TSRMLS_DC) { char buf[24], *p1, *p2; @@ -334,7 +334,7 @@ int redis_sock_redir(RedisSock *redis_sock, const char *msg, int len, // lengths we get back. if(!(p1 = strchr(msg, ' ')) || !(p2 = strchr(p1,':')) || (len-(p2-msg)>6)) { - zend_throw_exception(redis_exception_ce, + zend_throw_exception(redis_exception_ce, "Error parsing MOVED/ASK redirection", 0 TSRMLS_CC); return -1; } @@ -408,10 +408,10 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); } else if(memcmp(inbuf, "-MOVED ", sizeof("-MOVED ")-1)==0) { - redis_sock_redir(redis_sock,inbuf+sizeof("-MOVED "), + redis_sock_redir(redis_sock,inbuf+sizeof("-MOVED "), err_len-sizeof("-MOVED ")-1, MOVED_MOVED TSRMLS_CC); } else if(memcmp(inbuf, "-ASK ", sizeof("-ASK ")-1)==0) { - redis_sock_redir(redis_sock,inbuf+sizeof("-ASK "), + redis_sock_redir(redis_sock,inbuf+sizeof("-ASK "), err_len-sizeof("-ASK ")-1, MOVED_ASK TSRMLS_CC); } return NULL; @@ -501,8 +501,8 @@ redis_cmd_format_header(char **ret, char *keyword, int arg_count) { } int -redis_cmd_format_static(char **ret, char *keyword, char *format TSRMLS_DC, - ...) +redis_cmd_format_static(char **ret, char *keyword, char *format TSRMLS_DC, + ...) { char *p = format; va_list ap; @@ -1311,8 +1311,8 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } IF_MULTI_OR_PIPELINE() { zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, - &z TSRMLS_CC) == 1) + if(redis_unserialize(redis_sock, response, response_len, + &z TSRMLS_CC) == 1) { efree(response); add_next_index_zval(z_tab, z); @@ -1320,8 +1320,8 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_stringl(z_tab, response, response_len, 0); } } else { - if(redis_unserialize(redis_sock, response, response_len, - &return_value TSRMLS_CC) == 0) + if(redis_unserialize(redis_sock, response, response_len, + &return_value TSRMLS_CC) == 0) { RETURN_STRINGL(response, response_len, 0); } else { @@ -1595,7 +1595,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r int response_len, cmd_len; char * response; - cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); + cmd_len = redis_cmd_format_static(&cmd, "DISCARD", "" TSRMLS_CC); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -1795,8 +1795,8 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) can_unserialize = 0; - if(can_unserialize && redis_unserialize(redis_sock, response, - response_len, &z TSRMLS_CC)==1) + if(can_unserialize && redis_unserialize(redis_sock, response, + response_len, &z TSRMLS_CC)==1) { efree(response); add_next_index_zval(z_tab, z); @@ -1852,8 +1852,8 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z - TSRMLS_CC) == 1) + if(redis_unserialize(redis_sock, response, response_len, &z + TSRMLS_CC) == 1) { efree(response); add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); @@ -1998,8 +1998,8 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_ } PHPAPI int -redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, - zval **return_value TSRMLS_DC) +redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, + zval **return_value TSRMLS_DC) { php_unserialize_data_t var_hash; @@ -2287,4 +2287,3 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv } /* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ - diff --git a/redis.c b/redis.c index 27f70c042a..73b7f85924 100644 --- a/redis.c +++ b/redis.c @@ -387,7 +387,7 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { int response_len, cmd_len; /* format our discard command */ - cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); + cmd_len = redis_cmd_format_static(&cmd, "DISCARD", "" TSRMLS_CC); /* send our DISCARD command */ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && @@ -841,7 +841,8 @@ PHP_METHOD(Redis, bitcount) /* BITCOUNT key start end */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "BITCOUNT", "sdd", key, key_len, (int)start, (int)end); + cmd_len = redis_cmd_format_static(&cmd, "BITCOUNT" TSRMLS_CC, "sdd", key, + key_len, (int)start, (int)end); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -885,13 +886,13 @@ PHP_METHOD(Redis, bitpos) /* Various command semantics */ if(argc == 2) { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd", key, key_len, + cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd" TSRMLS_CC, key, key_len, bit); } else if(argc == 3) { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sdd", key, key_len, + cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sdd" TSRMLS_CC, key, key_len, bit, start); } else { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sddd", key, key_len, + cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sddd" TSRMLS_CC, key, key_len, bit, start, end); } @@ -998,25 +999,26 @@ PHP_METHOD(Redis, set) { /* Now let's construct the command we want */ if(exp_type && set_type) { /* SET NX|XX PX|EX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "ssssl", key, key_len, - val, val_len, set_type, 2, exp_type, - 2, expire); + cmd_len = redis_cmd_format_static(&cmd, "SET", "ssssl" TSRMLS_CC, key, + key_len, val, val_len, set_type, 2, + exp_type, 2, expire); } else if(exp_type) { /* SET PX|EX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "sssl", key, key_len, - val, val_len, exp_type, 2, expire); + cmd_len = redis_cmd_format_static(&cmd, "SET", "sssl" TSRMLS_CC, key, + key_len, val, val_len, exp_type, 2, + expire); } else if(set_type) { /* SET NX|XX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "sss", key, key_len, - val, val_len, set_type, 2); + cmd_len = redis_cmd_format_static(&cmd, "SET", "sss" TSRMLS_CC, key, + key_len, val, val_len, set_type, 2); } else if(expire > 0) { /* Backward compatible SETEX redirection */ - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sls", key, key_len, - expire, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sls" TSRMLS_CC, key, + key_len, expire, val, val_len); } else { /* SET */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "ss", key, key_len, - val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "SET", "ss" TSRMLS_CC, key, + key_len, val, val_len); } /* Free our key or value if we prefixed/serialized */ @@ -1026,7 +1028,8 @@ PHP_METHOD(Redis, set) { /* Kick off the command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_boolean_response); } @@ -1053,7 +1056,8 @@ PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keywo val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "sls" TSRMLS_CC, key, + key_len, expire, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1102,7 +1106,8 @@ PHP_METHOD(Redis, setnx) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss" TSRMLS_CC, key, + key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1140,7 +1145,8 @@ PHP_METHOD(Redis, getSet) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss" TSRMLS_CC, key, + key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1204,7 +1210,7 @@ PHP_METHOD(Redis, echo) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1240,7 +1246,8 @@ PHP_METHOD(Redis, renameKey) src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "RENAME", "ss", src, src_len, dst, dst_len); + cmd_len = redis_cmd_format_static(&cmd, "RENAME", "ss" TSRMLS_CC, src, + src_len, dst, dst_len); if(src_free) efree(src); if(dst_free) efree(dst); @@ -1277,7 +1284,8 @@ PHP_METHOD(Redis, renameNx) src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "RENAMENX", "ss", src, src_len, dst, dst_len); + cmd_len = redis_cmd_format_static(&cmd, "RENAMENX", "ss" TSRMLS_CC, src, + src_len, dst, dst_len); if(src_free) efree(src); if(dst_free) efree(dst); @@ -1311,7 +1319,7 @@ PHP_METHOD(Redis, get) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GET", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "GET", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1342,7 +1350,7 @@ PHP_METHOD(Redis, ping) RETURN_FALSE; } - cmd_len = redis_cmd_format_static(&cmd, "PING", ""); + cmd_len = redis_cmd_format_static(&cmd, "PING", "" TSRMLS_CC); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1372,9 +1380,11 @@ PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *ke } key_free = redis_key_prefix(redis_sock, &key, &key_len); if (val == 1) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "s" TSRMLS_CC, key, + key_len); } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "sl", key, key_len, val); + cmd_len = redis_cmd_format_static(&cmd, keyword, "sl" TSRMLS_CC, key, + key_len, val); } if(key_free) efree(key); @@ -1451,7 +1461,8 @@ PHP_METHOD(Redis, incrByFloat) { /* Prefix key, format command, free old key if necissary */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val); + cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf" TSRMLS_CC, key, + key_len, val); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1611,7 +1622,8 @@ PHP_METHOD(Redis, exists) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "EXISTS", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "EXISTS", "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1710,7 +1722,8 @@ PHP_METHOD(Redis, getKeys) } pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len); - cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pattern, pattern_len); + cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s" TSRMLS_CC, pattern, + pattern_len); if(pattern_free) efree(pattern); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1743,7 +1756,7 @@ PHP_METHOD(Redis, type) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "TYPE", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "TYPE", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1773,7 +1786,8 @@ PHP_METHOD(Redis, append) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "APPEND", "ss", key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "APPEND", "ss" TSRMLS_CC, key, + key_len, val, val_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1802,7 +1816,8 @@ PHP_METHOD(Redis, getRange) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETRANGE", "sdd", key, key_len, (int)start, (int)end); + cmd_len = redis_cmd_format_static(&cmd, "GETRANGE", "sdd" TSRMLS_CC, key, + key_len, (int)start, (int)end); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1830,7 +1845,8 @@ PHP_METHOD(Redis, setRange) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETRANGE", "sds", key, key_len, (int)offset, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "SETRANGE", "sds" TSRMLS_CC, key, + key_len, (int)offset, val, val_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1863,7 +1879,8 @@ PHP_METHOD(Redis, getBit) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd", key, key_len, (int)offset); + cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd" TSRMLS_CC, key, + key_len, (int)offset); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1897,7 +1914,8 @@ PHP_METHOD(Redis, setBit) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd", key, key_len, (int)offset, (int)val); + cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd" TSRMLS_CC, key, + key_len, (int)offset, (int)val); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1926,7 +1944,7 @@ PHP_METHOD(Redis, strlen) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "STRLEN", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "STRLEN", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1957,7 +1975,8 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "ss" TSRMLS_CC, key, + key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2033,7 +2052,9 @@ PHP_METHOD(Redis, lInsert) key_free = redis_key_prefix(redis_sock, &key, &key_len); val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC); - cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss" TSRMLS_CC, + key, key_len, position, position_len, + pivot, pivot_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); if(pivot_free) STR_FREE(pivot); @@ -2078,7 +2099,8 @@ generic_pop_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_le } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2168,7 +2190,7 @@ PHP_METHOD(Redis, lSize) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LLEN", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "LLEN", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2207,7 +2229,8 @@ PHP_METHOD(Redis, lRemove) /* LREM key count value */ val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds" TSRMLS_CC, key, + key_len, count, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2240,7 +2263,8 @@ PHP_METHOD(Redis, listTrim) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LTRIM", "sdd", key, key_len, (int)start, (int)end); + cmd_len = redis_cmd_format_static(&cmd, "LTRIM", "sdd" TSRMLS_CC, key, + key_len, (int)start, (int)end); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2274,7 +2298,8 @@ PHP_METHOD(Redis, lGet) /* LINDEX key pos */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LINDEX", "sd", key, key_len, (int)index); + cmd_len = redis_cmd_format_static(&cmd, "LINDEX", "sd" TSRMLS_CC, key, + key_len, (int)index); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2307,7 +2332,8 @@ PHP_METHOD(Redis, lGetRange) /* LRANGE key start end */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LRANGE", "sdd", key, key_len, (int)start, (int)end); + cmd_len = redis_cmd_format_static(&cmd, "LRANGE", "sdd" TSRMLS_CC, key, + key_len, (int)start, (int)end); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2357,7 +2383,8 @@ PHP_METHOD(Redis, sSize) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SCARD", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "SCARD", "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2411,7 +2438,8 @@ PHP_METHOD(Redis, sMove) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss" TSRMLS_CC, src, + src_len, dst, dst_len, val, val_len); if(val_free) STR_FREE(val); if(src_free) efree(src); if(dst_free) efree(dst); @@ -2517,7 +2545,8 @@ PHP_METHOD(Redis, sContains) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss" TSRMLS_CC, key, + key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2549,7 +2578,8 @@ PHP_METHOD(Redis, sMembers) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SMEMBERS", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "SMEMBERS", "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2682,7 +2712,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * } if(has_timeout) { keys_len[j] = spprintf(&keys[j], 0, "%d", timeout); - cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; /* $ + size + NL + string + NL */ + cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; // $ + size + NL + string + NL j++; real_argc++; } @@ -3330,7 +3360,8 @@ PHP_REDIS_API void generic_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keywor RETURN_FALSE; key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, t, t_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "ss" TSRMLS_CC, key, + key_len, t, t_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3391,7 +3422,8 @@ PHP_METHOD(Redis, lSet) { val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds" TSRMLS_CC, key, + key_len, index, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -3432,7 +3464,7 @@ PHP_REDIS_API void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, in PHP_METHOD(Redis, save) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "SAVE", ""); + int cmd_len = redis_cmd_format_static(&cmd, "SAVE", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -3443,7 +3475,7 @@ PHP_METHOD(Redis, save) PHP_METHOD(Redis, bgSave) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "BGSAVE", ""); + int cmd_len = redis_cmd_format_static(&cmd, "BGSAVE", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -3475,7 +3507,7 @@ PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cm PHP_METHOD(Redis, lastSave) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "LASTSAVE", ""); + int cmd_len = redis_cmd_format_static(&cmd, "LASTSAVE", "" TSRMLS_CC); generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3486,7 +3518,7 @@ PHP_METHOD(Redis, lastSave) PHP_METHOD(Redis, flushDB) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "FLUSHDB", ""); + int cmd_len = redis_cmd_format_static(&cmd, "FLUSHDB", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3496,7 +3528,7 @@ PHP_METHOD(Redis, flushDB) PHP_METHOD(Redis, flushAll) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "FLUSHALL", ""); + int cmd_len = redis_cmd_format_static(&cmd, "FLUSHALL", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3506,7 +3538,7 @@ PHP_METHOD(Redis, flushAll) PHP_METHOD(Redis, dbSize) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "DBSIZE", ""); + int cmd_len = redis_cmd_format_static(&cmd, "DBSIZE", "" TSRMLS_CC); generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3530,7 +3562,8 @@ PHP_METHOD(Redis, auth) { RETURN_FALSE; } - cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", password, password_len); + cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s" TSRMLS_CC, password, + password_len); /* Free previously stored auth if we have one, and store this password */ if(redis_sock->auth) efree(redis_sock->auth); @@ -3564,7 +3597,8 @@ PHP_METHOD(Redis, persist) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "PERSIST", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "PERSIST", "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3592,7 +3626,8 @@ PHP_REDIS_API void generic_ttl(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3636,9 +3671,10 @@ PHP_METHOD(Redis, info) { /* Build a standalone INFO command or one with an option */ if(opt != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len); + cmd_len = redis_cmd_format_static(&cmd, "INFO", "s" TSRMLS_CC, opt, + opt_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); + cmd_len = redis_cmd_format_static(&cmd, "INFO", "" TSRMLS_CC); } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3655,7 +3691,8 @@ PHP_METHOD(Redis, info) { PHP_METHOD(Redis, resetStat) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "s", "RESETSTAT", 9); + int cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "s" TSRMLS_CC, + "RESETSTAT", 9); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3682,7 +3719,7 @@ PHP_METHOD(Redis, select) { redis_sock->dbNumber = dbNumber; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", dbNumber); + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d" TSRMLS_CC, dbNumber); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -3713,7 +3750,8 @@ PHP_METHOD(Redis, move) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "MOVE", "sd", key, key_len, dbNumber); + cmd_len = redis_cmd_format_static(&cmd, "MOVE", "sd" TSRMLS_CC, key, key_len, + dbNumber); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3849,9 +3887,12 @@ PHP_REDIS_API void common_rpoplpush(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red int srckey_free = redis_key_prefix(redis_sock, &srckey, &srckey_len); int dstkey_free = redis_key_prefix(redis_sock, &dstkey, &dstkey_len); if(timeout < 0) { - cmd_len = redis_cmd_format_static(&cmd, "RPOPLPUSH", "ss", srckey, srckey_len, dstkey, dstkey_len); + cmd_len = redis_cmd_format_static(&cmd, "RPOPLPUSH", "ss" TSRMLS_CC, + srckey, srckey_len, dstkey, dstkey_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "BRPOPLPUSH", "ssd", srckey, srckey_len, dstkey, dstkey_len, timeout); + cmd_len = redis_cmd_format_static(&cmd, "BRPOPLPUSH", "ssd" TSRMLS_CC, + srckey, srckey_len, dstkey, dstkey_len, + timeout); } if(srckey_free) efree(srckey); if(dstkey_free) efree(dstkey); @@ -4036,9 +4077,11 @@ PHP_METHOD(Redis, zRange) key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { - cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdds", key, key_len, start, end, "WITHSCORES", 10); + cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdds" TSRMLS_CC, key, + key_len, start, end, "WITHSCORES", 10); } else { - cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdd", key, key_len, start, end); + cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdd" TSRMLS_CC, key, + key_len, start, end); } if(key_free) efree(key); @@ -4098,7 +4141,9 @@ PHP_METHOD(Redis, zDeleteRangeByScore) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYSCORE", "sss", key, key_len, start, start_len, end, end_len); + cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYSCORE", "sss" TSRMLS_CC, + key, key_len, start, start_len, end, + end_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -4131,7 +4176,8 @@ PHP_METHOD(Redis, zDeleteRangeByRank) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYRANK", "sdd", key, key_len, (int)start, (int)end); + cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYRANK", "sdd" TSRMLS_CC, + key, key_len, (int)start, (int)end); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -4166,9 +4212,12 @@ PHP_METHOD(Redis, zReverseRange) key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { - cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdds", key, key_len, start, end, "WITHSCORES", 10); + cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdds" TSRMLS_CC, + key, key_len, start, end, "WITHSCORES", + 10); } else { - cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdd", key, key_len, start, end); + cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdd" TSRMLS_CC, + key, key_len, start, end); } if(key_free) efree(key); @@ -4246,18 +4295,23 @@ redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { if(has_limit) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdds", - key, key_len, start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high, "WITHSCORES", 10); + cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdds" TSRMLS_CC, + key, key_len, start, start_len, end, end_len, "LIMIT", + 5, limit_low, limit_high, "WITHSCORES", 10); } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssss", - key, key_len, start, start_len, end, end_len, "WITHSCORES", 10); + cmd_len = redis_cmd_format_static(&cmd, keyword, "ssss" TSRMLS_CC, + key, key_len, start, start_len, end, end_len, + "WITHSCORES", 10); } } else { if(has_limit) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdd", - key, key_len, start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high); + cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdd" TSRMLS_CC, + key, key_len, start, start_len, end, end_len, + "LIMIT", 5, limit_low, limit_high); } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "sss", key, key_len, start, start_len, end, end_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "sss" TSRMLS_CC, + key, key_len, start, start_len, + end, end_len); } } if(key_free) efree(key); @@ -4324,7 +4378,8 @@ PHP_METHOD(Redis, zCount) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZCOUNT", "sss", key, key_len, start, start_len, end, end_len); + cmd_len = redis_cmd_format_static(&cmd, "ZCOUNT", "sss" TSRMLS_CC, key, + key_len, start, start_len, end, end_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -4414,7 +4469,8 @@ PHP_METHOD(Redis, zCard) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZCARD", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "ZCARD", "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -4449,7 +4505,8 @@ PHP_METHOD(Redis, zScore) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss" TSRMLS_CC, key, + key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4482,7 +4539,8 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "ss" TSRMLS_CC, key, + key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4532,7 +4590,8 @@ PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *key val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs" TSRMLS_CC, key, + key_len, add, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4748,7 +4807,8 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, kw, "sss" TSRMLS_CC, key, key_len, + member, member_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4790,7 +4850,8 @@ PHP_METHOD(Redis, hGet) RETURN_FALSE; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HGET", "ss", key, key_len, member, member_len); + cmd_len = redis_cmd_format_static(&cmd, "HGET", "ss" TSRMLS_CC, key, + key_len, member, member_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -4821,7 +4882,7 @@ PHP_METHOD(Redis, hLen) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HLEN", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "HLEN", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -4853,7 +4914,8 @@ generic_hash_command_2(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ return NULL; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, member, member_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "ss" TSRMLS_CC, key, + key_len, member, member_len); if(key_free) efree(key); *out_cmd = cmd; @@ -4914,7 +4976,8 @@ generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ return NULL; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, keyword, "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); /* call REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) without breaking the return value */ @@ -5002,7 +5065,8 @@ PHP_METHOD(Redis, hIncrByFloat) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HINCRBYFLOAT", "ssf", key, key_len, member, member_len, val); + cmd_len = redis_cmd_format_static(&cmd, "HINCRBYFLOAT", "ssf" TSRMLS_CC, key, + key_len, member, member_len, val); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -5043,7 +5107,9 @@ PHP_METHOD(Redis, hIncrBy) /* HINCRBY key member amount */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HINCRBY", "sss", key, key_len, member, member_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "HINCRBY", "sss" TSRMLS_CC, key, + key_len, member, member_len, val, + val_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -5275,7 +5341,7 @@ PHP_METHOD(Redis, multi) redis_sock->current = NULL; IF_MULTI() { - cmd_len = redis_cmd_format_static(&cmd, "MULTI", ""); + cmd_len = redis_cmd_format_static(&cmd, "MULTI", "" TSRMLS_CC); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -5419,7 +5485,7 @@ PHP_METHOD(Redis, exec) IF_MULTI() { - cmd_len = redis_cmd_format_static(&cmd, "EXEC", ""); + cmd_len = redis_cmd_format_static(&cmd, "EXEC", "" TSRMLS_CC); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -5549,7 +5615,8 @@ PHP_METHOD(Redis, publish) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "PUBLISH", "ss", key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "PUBLISH", "ss" TSRMLS_CC, key, + key_len, val, val_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -5665,6 +5732,7 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub int is_pmsg, tab_idx = 1; zval **type, **channel, **pattern, **data; z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + int is_pmsg, tab_idx = 1; if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) { /*ERROR */ @@ -5851,7 +5919,7 @@ PHP_METHOD(Redis, punsubscribe) PHP_METHOD(Redis, bgrewriteaof) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "BGREWRITEAOF", ""); + int cmd_len = redis_cmd_format_static(&cmd, "BGREWRITEAOF", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -5876,9 +5944,11 @@ PHP_METHOD(Redis, slaveof) } if(host && host_len) { - cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "sd", host, host_len, (int)port); + cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "sd" TSRMLS_CC, host, + host_len, (int)port); } else { - cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "ss", "NO", 2, "ONE", 3); + cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "ss" TSRMLS_CC, "NO", + 2, "ONE", 3); } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -5906,7 +5976,8 @@ PHP_METHOD(Redis, object) RETURN_FALSE; } - cmd_len = redis_cmd_format_static(&cmd, "OBJECT", "ss", info, info_len, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "OBJECT", "ss" TSRMLS_CC, info, + info_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(info_len == 8 && (strncasecmp(info, "refcount", 8) == 0 || strncasecmp(info, "idletime", 8) == 0)) { @@ -6060,7 +6131,8 @@ PHP_METHOD(Redis, config) } if (mode == CFG_GET && val == NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss" TSRMLS_CC, op, + op_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) IF_ATOMIC() { @@ -6069,7 +6141,8 @@ PHP_METHOD(Redis, config) REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw); } else if(mode == CFG_SET && val != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len); + cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss" TSRMLS_CC, op, + op_len, key, key_len, val, val_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) IF_ATOMIC() { @@ -6119,9 +6192,11 @@ PHP_METHOD(Redis, slowlog) { /* Create our command. For everything except SLOWLOG GET (with an arg) it's just two parts */ if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) { - cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl", arg, arg_len, option); + cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl" TSRMLS_CC, arg, + arg_len, option); } else { - cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "s", arg, arg_len); + cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "s" TSRMLS_CC, arg, + arg_len); } /* Kick off our command */ @@ -6204,8 +6279,9 @@ PHP_METHOD(Redis, wait) { RETURN_FALSE; } - /* Construct the command */ - cmd_len = redis_cmd_format_static(&cmd, "WAIT", "ll", num_slaves, timeout); + // Construct the command + cmd_len = redis_cmd_format_static(&cmd, "WAIT", "ll" TSRMLS_CC, num_slaves, + timeout); /* Kick it off */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -6301,7 +6377,8 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, *ret = cmd.c; return cmd.len; } else if(type == PUBSUB_NUMPAT) { - return redis_cmd_format_static(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT")-1); + return redis_cmd_format_static(ret, "PUBSUB", "s" TSRMLS_CC, "NUMPAT", + sizeof("NUMPAT")-1); } /* Shouldn't ever happen */ @@ -6449,7 +6526,8 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val /* If there weren't any arguments (none passed, or an empty array), construct a standard no args command */ if(args_count < 1) { - cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, val_len, 0); + cmd_len = redis_cmd_format_static(ret, keyword, "sd" TSRMLS_CC, value, + val_len, 0); } /* Return our command length */ @@ -6631,7 +6709,8 @@ PHP_METHOD(Redis, dump) { /* Prefix our key if we need to */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "DUMP", "s", key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "DUMP", "s" TSRMLS_CC, key, + key_len); if(key_free) efree(key); /* Kick off our request */ @@ -6696,7 +6775,8 @@ PHP_METHOD(Redis, restore) { /* Prefix the key if we need to */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "RESTORE", "sls", key, key_len, ttl, value, value_len); + cmd_len = redis_cmd_format_static(&cmd, "RESTORE", "sls" TSRMLS_CC, key, + key_len, ttl, value, value_len); if(key_free) efree(key); /* Kick off our restore request */ @@ -6842,12 +6922,12 @@ PHP_METHOD(Redis, _unserialize) { /* We only need to attempt unserialization if we have a serializer running */ if(redis_sock->serializer != REDIS_SERIALIZER_NONE) { zval *z_ret = NULL; - if(redis_unserialize(redis_sock->serializer, value, value_len, &z_ret - TSRMLS_CC) == 0) + if(redis_unserialize(redis_sock->serializer, value, value_len, &z_ret + TSRMLS_CC) == 0) { // Badly formed input, throw an execption - zend_throw_exception(redis_exception_ce, - "Invalid serialized data, or unserialization error", + zend_throw_exception(redis_exception_ce, + "Invalid serialized data, or unserialization error", 0 TSRMLS_CC); RETURN_FALSE; } @@ -7105,7 +7185,8 @@ PHP_METHOD(Redis, client) { cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len); + cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s" TSRMLS_CC, + opt, opt_len); } /* Execute our queue command */ diff --git a/redis_session.c b/redis_session.c index a6b15b59c2..77f0ad9e4b 100644 --- a/redis_session.c +++ b/redis_session.c @@ -122,7 +122,8 @@ redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { if(!rpm->auth || !rpm->auth_len) { /* no password given. */ return; } - cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", rpm->auth, rpm->auth_len); + cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s" TSRMLS_CC, rpm->auth, + rpm->auth_len); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { @@ -138,7 +139,8 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { char *response, *cmd; int response_len, cmd_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", rpm->database); + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d" TSRMLS_CC, + rpm->database); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { @@ -349,7 +351,8 @@ PS_READ_FUNC(redis) /* send GET command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, session_len); + cmd_len = redis_cmd_format_static(&cmd, "GET", "s" TSRMLS_CC, session, + session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -382,7 +385,10 @@ PS_WRITE_FUNC(redis) /* send SET command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); + cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds" TSRMLS_CC, session, + session_len, + INI_INT("session.gc_maxlifetime"), + val, vallen); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -421,7 +427,8 @@ PS_DESTROY_FUNC(redis) /* send DEL command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", session, session_len); + cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", TSRMLS_CC, session, + session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -454,4 +461,3 @@ PS_GC_FUNC(redis) #endif /* vim: set tabstop=4 expandtab: */ - From 0cfb34e58220ea8f22dbde9e38f13d3d1563ef8d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 May 2014 20:39:05 -0700 Subject: [PATCH 0217/1986] Remove 'k' type from redis_cmd_format_static We'll need the post-prefixed key outside of the format function, in order to properly calculate the cluster slot, so obsuring it in this way isn't helpful. --- library.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/library.c b/library.c index 978e063868..68aba0382b 100644 --- a/library.c +++ b/library.c @@ -535,17 +535,6 @@ redis_cmd_format_static(char **ret, char *keyword, char *format TSRMLS_DC, smart_str_appendl(&buf, val, val_len); } break; - case 'k': { - char *key = va_arg(ap, char*); - int key_len = va_arg(ap, int); - RedisSock *redis_sock = va_arg(ap, RedisSock*); - int key_free = redis_key_prefix(&key, &key_len); - smart_str_append_long(&buf, key_len); - smart_str_appendl(&buf, _NL, sizeof(_NL)-1); - smart_str_appendl(&buf,key,key_len); - if(key_free) efree(key); - } - break; case 'v': { char *zval = va_arg(ap,zval*); RedisSock *redis_sock = va_arg(ap,RedisSock*); From af3cec22eb0bdb4402e4d6ac1ed42548d572c463 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 05:23:11 -0700 Subject: [PATCH 0218/1986] Update redis_cmd_format_static with context Further change to redis_cmd_format_static to take a RedisSock* object every time. This is because for virtually all commands there will be at least a key (for which the RedisSock*) can provide our prefix, and often a value (for which we can use RedisSock* to give us serialization information.) --- library.c | 17 ++++- library.h | 2 +- redis.c | 198 ++++++++++++++++++++++++------------------------ redis_session.c | 10 +-- 4 files changed, 118 insertions(+), 109 deletions(-) diff --git a/library.c b/library.c index 68aba0382b..9d8862e317 100644 --- a/library.c +++ b/library.c @@ -501,8 +501,8 @@ redis_cmd_format_header(char **ret, char *keyword, int arg_count) { } int -redis_cmd_format_static(char **ret, char *keyword, char *format TSRMLS_DC, - ...) +redis_cmd_format_static(RedisSock *redis_sock, char **ret, char *keyword, + char *format TSRMLS_DC, ...) { char *p = format; va_list ap; @@ -535,9 +535,18 @@ redis_cmd_format_static(char **ret, char *keyword, char *format TSRMLS_DC, smart_str_appendl(&buf, val, val_len); } break; + case 'k': { + char *key = va_arg(ap, char*); + int key_len = va_arg(ap, int); + int key_free = redis_key_prefix(redis_sock, &key, &key_len); + smart_str_append_long(&buf, key_len); + smart_str_appendl(&buf, _NL, sizeof(_NL)-1); + smart_str_appendl(&buf, key, key_len); + if(key_free) efree(key); + } + break; case 'v': { char *zval = va_arg(ap,zval*); - RedisSock *redis_sock = va_arg(ap,RedisSock*); int val_free = redis_serialize(redis_sock,&val,&val_len TSRMLS_CC); smart_str_append_long(&buf,val_len); smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); @@ -1584,7 +1593,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r int response_len, cmd_len; char * response; - cmd_len = redis_cmd_format_static(&cmd, "DISCARD", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "DISCARD", "" TSRMLS_CC); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); diff --git a/library.h b/library.h index 33799883e4..601ef09a69 100644 --- a/library.h +++ b/library.h @@ -1,7 +1,7 @@ void add_constant_long(zend_class_entry *ce, char *name, int value); int integer_length(int i); int redis_cmd_format(char **ret, char *format, ...); -int redis_cmd_format_static(char **ret, char *keyword, char *format TSRMLS_DC, ...); +int redis_cmd_format_static(RedisSock *redis_sock, char **ret, char *keyword, char *format TSRMLS_DC, ...); int redis_cmd_format_header(char **ret, char *keyword, int arg_count); int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len); int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len); diff --git a/redis.c b/redis.c index 73b7f85924..cd4388d685 100644 --- a/redis.c +++ b/redis.c @@ -387,7 +387,7 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { int response_len, cmd_len; /* format our discard command */ - cmd_len = redis_cmd_format_static(&cmd, "DISCARD", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(redis_sock, redis_sock, &cmd, "DISCARD", "" TSRMLS_CC); /* send our DISCARD command */ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && @@ -841,7 +841,7 @@ PHP_METHOD(Redis, bitcount) /* BITCOUNT key start end */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "BITCOUNT" TSRMLS_CC, "sdd", key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BITCOUNT" TSRMLS_CC, "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -886,13 +886,13 @@ PHP_METHOD(Redis, bitpos) /* Various command semantics */ if(argc == 2) { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BITPOS", "sd" TSRMLS_CC, key, key_len, bit); } else if(argc == 3) { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sdd" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BITPOS", "sdd" TSRMLS_CC, key, key_len, bit, start); } else { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sddd" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BITPOS", "sddd" TSRMLS_CC, key, key_len, bit, start, end); } @@ -999,25 +999,25 @@ PHP_METHOD(Redis, set) { /* Now let's construct the command we want */ if(exp_type && set_type) { /* SET NX|XX PX|EX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "ssssl" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SET", "ssssl" TSRMLS_CC, key, key_len, val, val_len, set_type, 2, exp_type, 2, expire); } else if(exp_type) { /* SET PX|EX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "sssl" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SET", "sssl" TSRMLS_CC, key, key_len, val, val_len, exp_type, 2, expire); } else if(set_type) { /* SET NX|XX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "sss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SET", "sss" TSRMLS_CC, key, key_len, val, val_len, set_type, 2); } else if(expire > 0) { /* Backward compatible SETEX redirection */ - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sls" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETEX", "sls" TSRMLS_CC, key, key_len, expire, val, val_len); } else { /* SET */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SET", "ss" TSRMLS_CC, key, key_len, val, val_len); } @@ -1056,7 +1056,7 @@ PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keywo val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "sls" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "sls" TSRMLS_CC, key, key_len, expire, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1106,7 +1106,7 @@ PHP_METHOD(Redis, setnx) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETNX", "ss" TSRMLS_CC, key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1145,7 +1145,7 @@ PHP_METHOD(Redis, getSet) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GETSET", "ss" TSRMLS_CC, key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1210,7 +1210,7 @@ PHP_METHOD(Redis, echo) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ECHO", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1246,7 +1246,7 @@ PHP_METHOD(Redis, renameKey) src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "RENAME", "ss" TSRMLS_CC, src, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "RENAME", "ss" TSRMLS_CC, src, src_len, dst, dst_len); if(src_free) efree(src); if(dst_free) efree(dst); @@ -1284,7 +1284,7 @@ PHP_METHOD(Redis, renameNx) src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "RENAMENX", "ss" TSRMLS_CC, src, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "RENAMENX", "ss" TSRMLS_CC, src, src_len, dst, dst_len); if(src_free) efree(src); if(dst_free) efree(dst); @@ -1319,7 +1319,7 @@ PHP_METHOD(Redis, get) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GET", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GET", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1350,7 +1350,7 @@ PHP_METHOD(Redis, ping) RETURN_FALSE; } - cmd_len = redis_cmd_format_static(&cmd, "PING", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "PING", "" TSRMLS_CC); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1380,10 +1380,10 @@ PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *ke } key_free = redis_key_prefix(redis_sock, &key, &key_len); if (val == 1) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "s" TSRMLS_CC, key, key_len); } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "sl" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "sl" TSRMLS_CC, key, key_len, val); } if(key_free) efree(key); @@ -1461,7 +1461,7 @@ PHP_METHOD(Redis, incrByFloat) { /* Prefix key, format command, free old key if necissary */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "INCRBYFLOAT", "sf" TSRMLS_CC, key, key_len, val); if(key_free) efree(key); @@ -1622,7 +1622,7 @@ PHP_METHOD(Redis, exists) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "EXISTS", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "EXISTS", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -1722,7 +1722,7 @@ PHP_METHOD(Redis, getKeys) } pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len); - cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s" TSRMLS_CC, pattern, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "KEYS", "s" TSRMLS_CC, pattern, pattern_len); if(pattern_free) efree(pattern); @@ -1756,7 +1756,7 @@ PHP_METHOD(Redis, type) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "TYPE", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "TYPE", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1786,7 +1786,7 @@ PHP_METHOD(Redis, append) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "APPEND", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "APPEND", "ss" TSRMLS_CC, key, key_len, val, val_len); if(key_free) efree(key); @@ -1816,7 +1816,7 @@ PHP_METHOD(Redis, getRange) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETRANGE", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GETRANGE", "sdd" TSRMLS_CC, key, key_len, (int)start, (int)end); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1845,7 +1845,7 @@ PHP_METHOD(Redis, setRange) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETRANGE", "sds" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETRANGE", "sds" TSRMLS_CC, key, key_len, (int)offset, val, val_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1879,7 +1879,7 @@ PHP_METHOD(Redis, getBit) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GETBIT", "sd" TSRMLS_CC, key, key_len, (int)offset); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1914,7 +1914,7 @@ PHP_METHOD(Redis, setBit) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETBIT", "sdd" TSRMLS_CC, key, key_len, (int)offset, (int)val); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1944,7 +1944,7 @@ PHP_METHOD(Redis, strlen) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "STRLEN", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "STRLEN", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1975,7 +1975,7 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ss" TSRMLS_CC, key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2052,7 +2052,7 @@ PHP_METHOD(Redis, lInsert) key_free = redis_key_prefix(redis_sock, &key, &key_len); val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC); - cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LINSERT", "ssss" TSRMLS_CC, key, key_len, position, position_len, pivot, pivot_len, val, val_len); if(val_free) STR_FREE(val); @@ -2099,7 +2099,7 @@ generic_pop_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_le } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -2190,7 +2190,7 @@ PHP_METHOD(Redis, lSize) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LLEN", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LLEN", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2229,7 +2229,7 @@ PHP_METHOD(Redis, lRemove) /* LREM key count value */ val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LREM", "sds" TSRMLS_CC, key, key_len, count, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2263,7 +2263,7 @@ PHP_METHOD(Redis, listTrim) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LTRIM", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LTRIM", "sdd" TSRMLS_CC, key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -2298,7 +2298,7 @@ PHP_METHOD(Redis, lGet) /* LINDEX key pos */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LINDEX", "sd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LINDEX", "sd" TSRMLS_CC, key, key_len, (int)index); if(key_free) efree(key); @@ -2332,7 +2332,7 @@ PHP_METHOD(Redis, lGetRange) /* LRANGE key start end */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LRANGE", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LRANGE", "sdd" TSRMLS_CC, key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -2383,7 +2383,7 @@ PHP_METHOD(Redis, sSize) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SCARD", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SCARD", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -2438,7 +2438,7 @@ PHP_METHOD(Redis, sMove) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss" TSRMLS_CC, src, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SMOVE", "sss" TSRMLS_CC, src, src_len, dst, dst_len, val, val_len); if(val_free) STR_FREE(val); if(src_free) efree(src); @@ -2545,7 +2545,7 @@ PHP_METHOD(Redis, sContains) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SISMEMBER", "ss" TSRMLS_CC, key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2578,7 +2578,7 @@ PHP_METHOD(Redis, sMembers) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SMEMBERS", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SMEMBERS", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -3360,7 +3360,7 @@ PHP_REDIS_API void generic_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keywor RETURN_FALSE; key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ss" TSRMLS_CC, key, key_len, t, t_len); if(key_free) efree(key); @@ -3422,7 +3422,7 @@ PHP_METHOD(Redis, lSet) { val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LSET", "sds" TSRMLS_CC, key, key_len, index, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -3464,7 +3464,7 @@ PHP_REDIS_API void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, in PHP_METHOD(Redis, save) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "SAVE", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SAVE", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -3475,7 +3475,7 @@ PHP_METHOD(Redis, save) PHP_METHOD(Redis, bgSave) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "BGSAVE", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BGSAVE", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -3507,7 +3507,7 @@ PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cm PHP_METHOD(Redis, lastSave) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "LASTSAVE", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LASTSAVE", "" TSRMLS_CC); generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3518,7 +3518,7 @@ PHP_METHOD(Redis, lastSave) PHP_METHOD(Redis, flushDB) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "FLUSHDB", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "FLUSHDB", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3528,7 +3528,7 @@ PHP_METHOD(Redis, flushDB) PHP_METHOD(Redis, flushAll) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "FLUSHALL", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "FLUSHALL", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3538,7 +3538,7 @@ PHP_METHOD(Redis, flushAll) PHP_METHOD(Redis, dbSize) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "DBSIZE", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "DBSIZE", "" TSRMLS_CC); generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3562,7 +3562,7 @@ PHP_METHOD(Redis, auth) { RETURN_FALSE; } - cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s" TSRMLS_CC, password, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "AUTH", "s" TSRMLS_CC, password, password_len); /* Free previously stored auth if we have one, and store this password */ @@ -3597,7 +3597,7 @@ PHP_METHOD(Redis, persist) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "PERSIST", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "PERSIST", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -3626,7 +3626,7 @@ PHP_REDIS_API void generic_ttl(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -3671,10 +3671,10 @@ PHP_METHOD(Redis, info) { /* Build a standalone INFO command or one with an option */ if(opt != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "INFO", "s" TSRMLS_CC, opt, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "INFO", "s" TSRMLS_CC, opt, opt_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "INFO", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "INFO", "" TSRMLS_CC); } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3691,7 +3691,7 @@ PHP_METHOD(Redis, info) { PHP_METHOD(Redis, resetStat) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "s" TSRMLS_CC, + int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "CONFIG", "s" TSRMLS_CC, "RESETSTAT", 9); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -3719,7 +3719,7 @@ PHP_METHOD(Redis, select) { redis_sock->dbNumber = dbNumber; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d" TSRMLS_CC, dbNumber); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SELECT", "d" TSRMLS_CC, dbNumber); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -3750,7 +3750,7 @@ PHP_METHOD(Redis, move) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "MOVE", "sd" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "MOVE", "sd" TSRMLS_CC, key, key_len, dbNumber); if(key_free) efree(key); @@ -3887,10 +3887,10 @@ PHP_REDIS_API void common_rpoplpush(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red int srckey_free = redis_key_prefix(redis_sock, &srckey, &srckey_len); int dstkey_free = redis_key_prefix(redis_sock, &dstkey, &dstkey_len); if(timeout < 0) { - cmd_len = redis_cmd_format_static(&cmd, "RPOPLPUSH", "ss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "RPOPLPUSH", "ss" TSRMLS_CC, srckey, srckey_len, dstkey, dstkey_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "BRPOPLPUSH", "ssd" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BRPOPLPUSH", "ssd" TSRMLS_CC, srckey, srckey_len, dstkey, dstkey_len, timeout); } @@ -4077,10 +4077,10 @@ PHP_METHOD(Redis, zRange) key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { - cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdds" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZRANGE", "sdds" TSRMLS_CC, key, key_len, start, end, "WITHSCORES", 10); } else { - cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZRANGE", "sdd" TSRMLS_CC, key, key_len, start, end); } if(key_free) efree(key); @@ -4141,7 +4141,7 @@ PHP_METHOD(Redis, zDeleteRangeByScore) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYSCORE", "sss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZREMRANGEBYSCORE", "sss" TSRMLS_CC, key, key_len, start, start_len, end, end_len); if(key_free) efree(key); @@ -4176,7 +4176,7 @@ PHP_METHOD(Redis, zDeleteRangeByRank) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYRANK", "sdd" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZREMRANGEBYRANK", "sdd" TSRMLS_CC, key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -4212,11 +4212,11 @@ PHP_METHOD(Redis, zReverseRange) key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { - cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdds" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZREVRANGE", "sdds" TSRMLS_CC, key, key_len, start, end, "WITHSCORES", 10); } else { - cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdd" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZREVRANGE", "sdd" TSRMLS_CC, key, key_len, start, end); } if(key_free) efree(key); @@ -4295,21 +4295,21 @@ redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { if(has_limit) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdds" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ssssdds" TSRMLS_CC, key, key_len, start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high, "WITHSCORES", 10); } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ssss" TSRMLS_CC, key, key_len, start, start_len, end, end_len, "WITHSCORES", 10); } } else { if(has_limit) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdd" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ssssdd" TSRMLS_CC, key, key_len, start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high); } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "sss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "sss" TSRMLS_CC, key, key_len, start, start_len, end, end_len); } @@ -4378,7 +4378,7 @@ PHP_METHOD(Redis, zCount) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZCOUNT", "sss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZCOUNT", "sss" TSRMLS_CC, key, key_len, start, start_len, end, end_len); if(key_free) efree(key); @@ -4469,7 +4469,7 @@ PHP_METHOD(Redis, zCard) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZCARD", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZCARD", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -4505,7 +4505,7 @@ PHP_METHOD(Redis, zScore) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZSCORE", "ss" TSRMLS_CC, key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4539,7 +4539,7 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ss" TSRMLS_CC, key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4590,7 +4590,7 @@ PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *key val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "sfs" TSRMLS_CC, key, key_len, add, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4807,7 +4807,7 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, kw, "sss" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, kw, "sss" TSRMLS_CC, key, key_len, member, member_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4850,7 +4850,7 @@ PHP_METHOD(Redis, hGet) RETURN_FALSE; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HGET", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "HGET", "ss" TSRMLS_CC, key, key_len, member, member_len); if(key_free) efree(key); @@ -4882,7 +4882,7 @@ PHP_METHOD(Redis, hLen) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HLEN", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "HLEN", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -4914,7 +4914,7 @@ generic_hash_command_2(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ return NULL; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ss" TSRMLS_CC, key, key_len, member, member_len); if(key_free) efree(key); @@ -4976,7 +4976,7 @@ generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ return NULL; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -5065,7 +5065,7 @@ PHP_METHOD(Redis, hIncrByFloat) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HINCRBYFLOAT", "ssf" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "HINCRBYFLOAT", "ssf" TSRMLS_CC, key, key_len, member, member_len, val); if(key_free) efree(key); @@ -5107,7 +5107,7 @@ PHP_METHOD(Redis, hIncrBy) /* HINCRBY key member amount */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HINCRBY", "sss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "HINCRBY", "sss" TSRMLS_CC, key, key_len, member, member_len, val, val_len); if(key_free) efree(key); @@ -5341,7 +5341,7 @@ PHP_METHOD(Redis, multi) redis_sock->current = NULL; IF_MULTI() { - cmd_len = redis_cmd_format_static(&cmd, "MULTI", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "MULTI", "" TSRMLS_CC); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -5485,7 +5485,7 @@ PHP_METHOD(Redis, exec) IF_MULTI() { - cmd_len = redis_cmd_format_static(&cmd, "EXEC", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "EXEC", "" TSRMLS_CC); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -5615,7 +5615,7 @@ PHP_METHOD(Redis, publish) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "PUBLISH", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "PUBLISH", "ss" TSRMLS_CC, key, key_len, val, val_len); if(key_free) efree(key); @@ -5919,7 +5919,7 @@ PHP_METHOD(Redis, punsubscribe) PHP_METHOD(Redis, bgrewriteaof) { char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "BGREWRITEAOF", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BGREWRITEAOF", "" TSRMLS_CC); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -5944,10 +5944,10 @@ PHP_METHOD(Redis, slaveof) } if(host && host_len) { - cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "sd" TSRMLS_CC, host, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SLAVEOF", "sd" TSRMLS_CC, host, host_len, (int)port); } else { - cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "ss" TSRMLS_CC, "NO", + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SLAVEOF", "ss" TSRMLS_CC, "NO", 2, "ONE", 3); } @@ -5976,7 +5976,7 @@ PHP_METHOD(Redis, object) RETURN_FALSE; } - cmd_len = redis_cmd_format_static(&cmd, "OBJECT", "ss" TSRMLS_CC, info, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "OBJECT", "ss" TSRMLS_CC, info, info_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -6131,7 +6131,7 @@ PHP_METHOD(Redis, config) } if (mode == CFG_GET && val == NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss" TSRMLS_CC, op, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "CONFIG", "ss" TSRMLS_CC, op, op_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) @@ -6141,7 +6141,7 @@ PHP_METHOD(Redis, config) REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw); } else if(mode == CFG_SET && val != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss" TSRMLS_CC, op, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "CONFIG", "sss" TSRMLS_CC, op, op_len, key, key_len, val, val_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) @@ -6192,10 +6192,10 @@ PHP_METHOD(Redis, slowlog) { /* Create our command. For everything except SLOWLOG GET (with an arg) it's just two parts */ if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) { - cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl" TSRMLS_CC, arg, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SLOWLOG", "sl" TSRMLS_CC, arg, arg_len, option); } else { - cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "s" TSRMLS_CC, arg, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SLOWLOG", "s" TSRMLS_CC, arg, arg_len); } @@ -6280,7 +6280,7 @@ PHP_METHOD(Redis, wait) { } // Construct the command - cmd_len = redis_cmd_format_static(&cmd, "WAIT", "ll" TSRMLS_CC, num_slaves, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "WAIT", "ll" TSRMLS_CC, num_slaves, timeout); /* Kick it off */ @@ -6377,7 +6377,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, *ret = cmd.c; return cmd.len; } else if(type == PUBSUB_NUMPAT) { - return redis_cmd_format_static(ret, "PUBSUB", "s" TSRMLS_CC, "NUMPAT", + return redis_cmd_format_static(redis_sock, ret, "PUBSUB", "s" TSRMLS_CC, "NUMPAT", sizeof("NUMPAT")-1); } @@ -6526,7 +6526,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val /* If there weren't any arguments (none passed, or an empty array), construct a standard no args command */ if(args_count < 1) { - cmd_len = redis_cmd_format_static(ret, keyword, "sd" TSRMLS_CC, value, + cmd_len = redis_cmd_format_static(redis_sock, ret, keyword, "sd" TSRMLS_CC, value, val_len, 0); } @@ -6709,7 +6709,7 @@ PHP_METHOD(Redis, dump) { /* Prefix our key if we need to */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "DUMP", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "DUMP", "s" TSRMLS_CC, key, key_len); if(key_free) efree(key); @@ -6775,7 +6775,7 @@ PHP_METHOD(Redis, restore) { /* Prefix the key if we need to */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "RESTORE", "sls" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "RESTORE", "sls" TSRMLS_CC, key, key_len, ttl, value, value_len); if(key_free) efree(key); @@ -7185,7 +7185,7 @@ PHP_METHOD(Redis, client) { cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "CLIENT", "s" TSRMLS_CC, opt, opt_len); } diff --git a/redis_session.c b/redis_session.c index 77f0ad9e4b..4ddf793f61 100644 --- a/redis_session.c +++ b/redis_session.c @@ -122,7 +122,7 @@ redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { if(!rpm->auth || !rpm->auth_len) { /* no password given. */ return; } - cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s" TSRMLS_CC, rpm->auth, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "AUTH", "s" TSRMLS_CC, rpm->auth, rpm->auth_len); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { @@ -139,7 +139,7 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { char *response, *cmd; int response_len, cmd_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d" TSRMLS_CC, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SELECT", "d" TSRMLS_CC, rpm->database); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { @@ -351,7 +351,7 @@ PS_READ_FUNC(redis) /* send GET command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "GET", "s" TSRMLS_CC, session, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GET", "s" TSRMLS_CC, session, session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { @@ -385,7 +385,7 @@ PS_WRITE_FUNC(redis) /* send SET command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds" TSRMLS_CC, session, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETEX", "sds" TSRMLS_CC, session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); @@ -427,7 +427,7 @@ PS_DESTROY_FUNC(redis) /* send DEL command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", TSRMLS_CC, session, + cmd_len = redis_cmd_format_static(redis_sock, &cmd, "DEL", "s", TSRMLS_CC, session, session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { From e9b0a0b77dcbeec1cdc9618c67657830916efdaa Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 05:53:24 -0700 Subject: [PATCH 0219/1986] Remove TSRMLS_CC, and RedisSock from redis_cmd_format_static Although it would remove code duplication, serialization and key prefixing inside of redis_cmd_format static removes much needed context that the cluster methods wlll require (e.g. hashing the whole key, prefix included). --- library.c | 24 +---- library.h | 2 +- redis.c | 257 +++++++++++++++++++++++++----------------------- redis_session.c | 10 +- 4 files changed, 143 insertions(+), 150 deletions(-) diff --git a/library.c b/library.c index 9d8862e317..0f36c98c75 100644 --- a/library.c +++ b/library.c @@ -501,8 +501,7 @@ redis_cmd_format_header(char **ret, char *keyword, int arg_count) { } int -redis_cmd_format_static(RedisSock *redis_sock, char **ret, char *keyword, - char *format TSRMLS_DC, ...) +redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { char *p = format; va_list ap; @@ -535,25 +534,6 @@ redis_cmd_format_static(RedisSock *redis_sock, char **ret, char *keyword, smart_str_appendl(&buf, val, val_len); } break; - case 'k': { - char *key = va_arg(ap, char*); - int key_len = va_arg(ap, int); - int key_free = redis_key_prefix(redis_sock, &key, &key_len); - smart_str_append_long(&buf, key_len); - smart_str_appendl(&buf, _NL, sizeof(_NL)-1); - smart_str_appendl(&buf, key, key_len); - if(key_free) efree(key); - } - break; - case 'v': { - char *zval = va_arg(ap,zval*); - int val_free = redis_serialize(redis_sock,&val,&val_len TSRMLS_CC); - smart_str_append_long(&buf,val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf,val,val_len); - if(val_free) STR_FREE(val); - } - break; case 'f': case 'F': { double d = va_arg(ap, double); @@ -1593,7 +1573,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r int response_len, cmd_len; char * response; - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "DISCARD", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); diff --git a/library.h b/library.h index 601ef09a69..56f9ffeace 100644 --- a/library.h +++ b/library.h @@ -1,7 +1,7 @@ void add_constant_long(zend_class_entry *ce, char *name, int value); int integer_length(int i); int redis_cmd_format(char **ret, char *format, ...); -int redis_cmd_format_static(RedisSock *redis_sock, char **ret, char *keyword, char *format TSRMLS_DC, ...); +int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); int redis_cmd_format_header(char **ret, char *keyword, int arg_count); int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len); int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len); diff --git a/redis.c b/redis.c index cd4388d685..723a3f833e 100644 --- a/redis.c +++ b/redis.c @@ -387,7 +387,7 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { int response_len, cmd_len; /* format our discard command */ - cmd_len = redis_cmd_format_static(redis_sock, redis_sock, &cmd, "DISCARD", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); /* send our DISCARD command */ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && @@ -841,7 +841,7 @@ PHP_METHOD(Redis, bitcount) /* BITCOUNT key start end */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BITCOUNT" TSRMLS_CC, "sdd", key, + cmd_len = redis_cmd_format_static(&cmd, "BITCOUNT", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -886,13 +886,13 @@ PHP_METHOD(Redis, bitpos) /* Various command semantics */ if(argc == 2) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BITPOS", "sd" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd", key, key_len, bit); } else if(argc == 3) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BITPOS", "sdd" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sdd", key, key_len, bit, start); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BITPOS", "sddd" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sddd", key, key_len, bit, start, end); } @@ -999,25 +999,25 @@ PHP_METHOD(Redis, set) { /* Now let's construct the command we want */ if(exp_type && set_type) { /* SET NX|XX PX|EX */ - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SET", "ssssl" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SET", "ssssl", key, key_len, val, val_len, set_type, 2, exp_type, 2, expire); } else if(exp_type) { /* SET PX|EX */ - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SET", "sssl" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SET", "sssl", key, key_len, val, val_len, exp_type, 2, expire); } else if(set_type) { /* SET NX|XX */ - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SET", "sss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SET", "sss", key, key_len, val, val_len, set_type, 2); } else if(expire > 0) { /* Backward compatible SETEX redirection */ - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETEX", "sls" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sls", key, key_len, expire, val, val_len); } else { /* SET */ - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SET", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SET", "ss", key, key_len, val, val_len); } @@ -1056,7 +1056,7 @@ PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keywo val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "sls" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1106,7 +1106,7 @@ PHP_METHOD(Redis, setnx) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETNX", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1145,7 +1145,7 @@ PHP_METHOD(Redis, getSet) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GETSET", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -1210,7 +1210,7 @@ PHP_METHOD(Redis, echo) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ECHO", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s", key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1246,7 +1246,7 @@ PHP_METHOD(Redis, renameKey) src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "RENAME", "ss" TSRMLS_CC, src, + cmd_len = redis_cmd_format_static(&cmd, "RENAME", "ss", src, src_len, dst, dst_len); if(src_free) efree(src); if(dst_free) efree(dst); @@ -1284,7 +1284,7 @@ PHP_METHOD(Redis, renameNx) src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "RENAMENX", "ss" TSRMLS_CC, src, + cmd_len = redis_cmd_format_static(&cmd, "RENAMENX", "ss", src, src_len, dst, dst_len); if(src_free) efree(src); if(dst_free) efree(dst); @@ -1319,7 +1319,7 @@ PHP_METHOD(Redis, get) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GET", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "GET", "s", key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1350,7 +1350,7 @@ PHP_METHOD(Redis, ping) RETURN_FALSE; } - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "PING", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(&cmd, "PING", ""); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1380,10 +1380,10 @@ PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *ke } key_free = redis_key_prefix(redis_sock, &key, &key_len); if (val == 1) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "sl" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "sl", key, key_len, val); } if(key_free) efree(key); @@ -1461,7 +1461,7 @@ PHP_METHOD(Redis, incrByFloat) { /* Prefix key, format command, free old key if necissary */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "INCRBYFLOAT", "sf" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val); if(key_free) efree(key); @@ -1622,7 +1622,7 @@ PHP_METHOD(Redis, exists) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "EXISTS", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "EXISTS", "s", key, key_len); if(key_free) efree(key); @@ -1722,7 +1722,7 @@ PHP_METHOD(Redis, getKeys) } pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "KEYS", "s" TSRMLS_CC, pattern, + cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pattern, pattern_len); if(pattern_free) efree(pattern); @@ -1756,7 +1756,7 @@ PHP_METHOD(Redis, type) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "TYPE", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "TYPE", "s", key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1786,7 +1786,7 @@ PHP_METHOD(Redis, append) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "APPEND", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "APPEND", "ss", key, key_len, val, val_len); if(key_free) efree(key); @@ -1816,7 +1816,7 @@ PHP_METHOD(Redis, getRange) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GETRANGE", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "GETRANGE", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1845,7 +1845,7 @@ PHP_METHOD(Redis, setRange) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETRANGE", "sds" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SETRANGE", "sds", key, key_len, (int)offset, val, val_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1879,7 +1879,7 @@ PHP_METHOD(Redis, getBit) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GETBIT", "sd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd", key, key_len, (int)offset); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1914,7 +1914,7 @@ PHP_METHOD(Redis, setBit) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETBIT", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd", key, key_len, (int)offset, (int)val); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1944,7 +1944,7 @@ PHP_METHOD(Redis, strlen) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "STRLEN", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "STRLEN", "s", key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1975,7 +1975,7 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2052,7 +2052,7 @@ PHP_METHOD(Redis, lInsert) key_free = redis_key_prefix(redis_sock, &key, &key_len); val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LINSERT", "ssss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len); if(val_free) STR_FREE(val); @@ -2099,7 +2099,7 @@ generic_pop_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_le } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); if(key_free) efree(key); @@ -2190,7 +2190,7 @@ PHP_METHOD(Redis, lSize) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LLEN", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "LLEN", "s", key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2229,7 +2229,7 @@ PHP_METHOD(Redis, lRemove) /* LREM key count value */ val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LREM", "sds" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2263,7 +2263,7 @@ PHP_METHOD(Redis, listTrim) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LTRIM", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "LTRIM", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -2298,7 +2298,7 @@ PHP_METHOD(Redis, lGet) /* LINDEX key pos */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LINDEX", "sd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "LINDEX", "sd", key, key_len, (int)index); if(key_free) efree(key); @@ -2332,7 +2332,7 @@ PHP_METHOD(Redis, lGetRange) /* LRANGE key start end */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LRANGE", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "LRANGE", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -2383,7 +2383,7 @@ PHP_METHOD(Redis, sSize) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SCARD", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SCARD", "s", key, key_len); if(key_free) efree(key); @@ -2438,7 +2438,7 @@ PHP_METHOD(Redis, sMove) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SMOVE", "sss" TSRMLS_CC, src, + cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len); if(val_free) STR_FREE(val); if(src_free) efree(src); @@ -2489,11 +2489,13 @@ PHP_METHOD(Redis, sRandMember) /* If we have two arguments, we're running with an optional COUNT, which will return */ /* a multibulk reply. Without the argument we'll return a string response */ if(ZEND_NUM_ARGS() == 2) { - /* Construct our command with count */ - cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "sl", key, key_len, count); + // Construct our command with count + cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "sl", + key, key_len, count); } else { - /* Construct our command */ - cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "s", key, key_len); + // Construct our command + cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "s", + key, key_len); } /* Free our key if we prefixed it */ @@ -2545,7 +2547,7 @@ PHP_METHOD(Redis, sContains) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SISMEMBER", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -2578,7 +2580,7 @@ PHP_METHOD(Redis, sMembers) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SMEMBERS", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "SMEMBERS", "s", key, key_len); if(key_free) efree(key); @@ -3360,7 +3362,7 @@ PHP_REDIS_API void generic_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keywor RETURN_FALSE; key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, t, t_len); if(key_free) efree(key); @@ -3422,7 +3424,7 @@ PHP_METHOD(Redis, lSet) { val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LSET", "sds" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -3464,7 +3466,7 @@ PHP_REDIS_API void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, in PHP_METHOD(Redis, save) { char *cmd; - int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SAVE", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(&cmd, "SAVE", ""); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -3475,7 +3477,7 @@ PHP_METHOD(Redis, save) PHP_METHOD(Redis, bgSave) { char *cmd; - int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BGSAVE", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(&cmd, "BGSAVE", ""); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -3507,7 +3509,7 @@ PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cm PHP_METHOD(Redis, lastSave) { char *cmd; - int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "LASTSAVE", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(&cmd, "LASTSAVE", ""); generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3518,7 +3520,7 @@ PHP_METHOD(Redis, lastSave) PHP_METHOD(Redis, flushDB) { char *cmd; - int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "FLUSHDB", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(&cmd, "FLUSHDB", ""); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3528,7 +3530,7 @@ PHP_METHOD(Redis, flushDB) PHP_METHOD(Redis, flushAll) { char *cmd; - int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "FLUSHALL", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(&cmd, "FLUSHALL", ""); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3538,7 +3540,7 @@ PHP_METHOD(Redis, flushAll) PHP_METHOD(Redis, dbSize) { char *cmd; - int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "DBSIZE", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(&cmd, "DBSIZE", ""); generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } /* }}} */ @@ -3562,7 +3564,7 @@ PHP_METHOD(Redis, auth) { RETURN_FALSE; } - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "AUTH", "s" TSRMLS_CC, password, + cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", password, password_len); /* Free previously stored auth if we have one, and store this password */ @@ -3597,7 +3599,7 @@ PHP_METHOD(Redis, persist) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "PERSIST", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "PERSIST", "s", key, key_len); if(key_free) efree(key); @@ -3626,7 +3628,7 @@ PHP_REDIS_API void generic_ttl(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); if(key_free) efree(key); @@ -3671,10 +3673,10 @@ PHP_METHOD(Redis, info) { /* Build a standalone INFO command or one with an option */ if(opt != NULL) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "INFO", "s" TSRMLS_CC, opt, + cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "INFO", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3691,7 +3693,7 @@ PHP_METHOD(Redis, info) { PHP_METHOD(Redis, resetStat) { char *cmd; - int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "CONFIG", "s" TSRMLS_CC, + int cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "s", "RESETSTAT", 9); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -3719,7 +3721,7 @@ PHP_METHOD(Redis, select) { redis_sock->dbNumber = dbNumber; - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SELECT", "d" TSRMLS_CC, dbNumber); + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", dbNumber); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -3750,7 +3752,7 @@ PHP_METHOD(Redis, move) { } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "MOVE", "sd" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(&cmd, "MOVE", "sd", key, key_len, dbNumber); if(key_free) efree(key); @@ -3887,10 +3889,10 @@ PHP_REDIS_API void common_rpoplpush(INTERNAL_FUNCTION_PARAMETERS, RedisSock *red int srckey_free = redis_key_prefix(redis_sock, &srckey, &srckey_len); int dstkey_free = redis_key_prefix(redis_sock, &dstkey, &dstkey_len); if(timeout < 0) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "RPOPLPUSH", "ss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "RPOPLPUSH", "ss", srckey, srckey_len, dstkey, dstkey_len); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BRPOPLPUSH", "ssd" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "BRPOPLPUSH", "ssd", srckey, srckey_len, dstkey, dstkey_len, timeout); } @@ -4077,10 +4079,10 @@ PHP_METHOD(Redis, zRange) key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZRANGE", "sdds" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdds", key, key_len, start, end, "WITHSCORES", 10); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZRANGE", "sdd" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdd", key, key_len, start, end); } if(key_free) efree(key); @@ -4141,7 +4143,7 @@ PHP_METHOD(Redis, zDeleteRangeByScore) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZREMRANGEBYSCORE", "sss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYSCORE", "sss", key, key_len, start, start_len, end, end_len); if(key_free) efree(key); @@ -4176,7 +4178,7 @@ PHP_METHOD(Redis, zDeleteRangeByRank) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZREMRANGEBYRANK", "sdd" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYRANK", "sdd", key, key_len, (int)start, (int)end); if(key_free) efree(key); @@ -4212,11 +4214,11 @@ PHP_METHOD(Redis, zReverseRange) key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZREVRANGE", "sdds" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdds", key, key_len, start, end, "WITHSCORES", 10); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZREVRANGE", "sdd" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdd", key, key_len, start, end); } if(key_free) efree(key); @@ -4295,21 +4297,21 @@ redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { key_free = redis_key_prefix(redis_sock, &key, &key_len); if(withscores) { if(has_limit) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ssssdds" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdds", key, key_len, start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high, "WITHSCORES", 10); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ssss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, keyword, "ssss", key, key_len, start, start_len, end, end_len, "WITHSCORES", 10); } } else { if(has_limit) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ssssdd" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdd", key, key_len, start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "sss" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, keyword, "sss", key, key_len, start, start_len, end, end_len); } @@ -4378,7 +4380,7 @@ PHP_METHOD(Redis, zCount) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZCOUNT", "sss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "ZCOUNT", "sss", key, key_len, start, start_len, end, end_len); if(key_free) efree(key); @@ -4469,7 +4471,7 @@ PHP_METHOD(Redis, zCard) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZCARD", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "ZCARD", "s", key, key_len); if(key_free) efree(key); @@ -4505,7 +4507,7 @@ PHP_METHOD(Redis, zScore) val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "ZSCORE", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4539,7 +4541,7 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4590,7 +4592,7 @@ PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *key val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "sfs" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4807,7 +4809,7 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, kw, "sss" TSRMLS_CC, key, key_len, + cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len); if(val_free) STR_FREE(val); if(key_free) efree(key); @@ -4850,7 +4852,7 @@ PHP_METHOD(Redis, hGet) RETURN_FALSE; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "HGET", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "HGET", "ss", key, key_len, member, member_len); if(key_free) efree(key); @@ -4882,7 +4884,7 @@ PHP_METHOD(Redis, hLen) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "HLEN", "s" TSRMLS_CC, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "HLEN", "s", key, key_len); if(key_free) efree(key); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -4914,7 +4916,7 @@ generic_hash_command_2(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ return NULL; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, member, member_len); if(key_free) efree(key); @@ -4976,7 +4978,7 @@ generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ return NULL; } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, keyword, "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, key_len); if(key_free) efree(key); @@ -5065,7 +5067,7 @@ PHP_METHOD(Redis, hIncrByFloat) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "HINCRBYFLOAT", "ssf" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "HINCRBYFLOAT", "ssf", key, key_len, member, member_len, val); if(key_free) efree(key); @@ -5107,7 +5109,7 @@ PHP_METHOD(Redis, hIncrBy) /* HINCRBY key member amount */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "HINCRBY", "sss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "HINCRBY", "sss", key, key_len, member, member_len, val, val_len); if(key_free) efree(key); @@ -5341,7 +5343,7 @@ PHP_METHOD(Redis, multi) redis_sock->current = NULL; IF_MULTI() { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "MULTI", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(&cmd, "MULTI", ""); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -5485,7 +5487,7 @@ PHP_METHOD(Redis, exec) IF_MULTI() { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "EXEC", "" TSRMLS_CC); + cmd_len = redis_cmd_format_static(&cmd, "EXEC", ""); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -5615,7 +5617,7 @@ PHP_METHOD(Redis, publish) } key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "PUBLISH", "ss" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "PUBLISH", "ss", key, key_len, val, val_len); if(key_free) efree(key); @@ -5919,7 +5921,7 @@ PHP_METHOD(Redis, punsubscribe) PHP_METHOD(Redis, bgrewriteaof) { char *cmd; - int cmd_len = redis_cmd_format_static(redis_sock, &cmd, "BGREWRITEAOF", "" TSRMLS_CC); + int cmd_len = redis_cmd_format_static(&cmd, "BGREWRITEAOF", ""); generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); } @@ -5944,10 +5946,10 @@ PHP_METHOD(Redis, slaveof) } if(host && host_len) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SLAVEOF", "sd" TSRMLS_CC, host, + cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "sd", host, host_len, (int)port); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SLAVEOF", "ss" TSRMLS_CC, "NO", + cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "ss", "NO", 2, "ONE", 3); } @@ -5976,7 +5978,7 @@ PHP_METHOD(Redis, object) RETURN_FALSE; } - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "OBJECT", "ss" TSRMLS_CC, info, + cmd_len = redis_cmd_format_static(&cmd, "OBJECT", "ss", info, info_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -6131,7 +6133,7 @@ PHP_METHOD(Redis, config) } if (mode == CFG_GET && val == NULL) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "CONFIG", "ss" TSRMLS_CC, op, + cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) @@ -6141,7 +6143,7 @@ PHP_METHOD(Redis, config) REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw); } else if(mode == CFG_SET && val != NULL) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "CONFIG", "sss" TSRMLS_CC, op, + cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) @@ -6192,10 +6194,10 @@ PHP_METHOD(Redis, slowlog) { /* Create our command. For everything except SLOWLOG GET (with an arg) it's just two parts */ if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SLOWLOG", "sl" TSRMLS_CC, arg, + cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl", arg, arg_len, option); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SLOWLOG", "s" TSRMLS_CC, arg, + cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "s", arg, arg_len); } @@ -6280,7 +6282,7 @@ PHP_METHOD(Redis, wait) { } // Construct the command - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "WAIT", "ll" TSRMLS_CC, num_slaves, + cmd_len = redis_cmd_format_static(&cmd, "WAIT", "ll", num_slaves, timeout); /* Kick it off */ @@ -6314,8 +6316,9 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Prefix if necissary */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - /* With a pattern */ - cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", "CHANNELS", sizeof("CHANNELS")-1, + // With a pattern + cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", + "CHANNELS", sizeof("CHANNELS")-1, key, key_len); /* Free the channel name if we prefixed it */ @@ -6324,8 +6327,9 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Return command length */ return cmd_len; } else { - /* No pattern */ - return redis_cmd_format_static(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS")-1); + // No pattern + return redis_cmd_format_static(ret, "PUBSUB", "s", + "CHANNELS", sizeof("CHANNELS")-1); } } else if(type == PUBSUB_NUMSUB) { ht_chan = Z_ARRVAL_P(arg); @@ -6377,7 +6381,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, *ret = cmd.c; return cmd.len; } else if(type == PUBSUB_NUMPAT) { - return redis_cmd_format_static(redis_sock, ret, "PUBSUB", "s" TSRMLS_CC, "NUMPAT", + return redis_cmd_format_static(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT")-1); } @@ -6526,7 +6530,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val /* If there weren't any arguments (none passed, or an empty array), construct a standard no args command */ if(args_count < 1) { - cmd_len = redis_cmd_format_static(redis_sock, ret, keyword, "sd" TSRMLS_CC, value, + cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, val_len, 0); } @@ -6653,8 +6657,10 @@ PHP_METHOD(Redis, script) { /* Branch based on the directive */ if(!strcasecmp(Z_STRVAL_P(z_args[0]), "flush") || !strcasecmp(Z_STRVAL_P(z_args[0]), "kill")) { - /* Simple SCRIPT FLUSH, or SCRIPT_KILL command */ - cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "s", Z_STRVAL_P(z_args[0]), Z_STRLEN_P(z_args[0])); + // Simple SCRIPT FLUSH, or SCRIPT_KILL command + cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "s", + Z_STRVAL_P(z_args[0]), + Z_STRLEN_P(z_args[0])); } else if(!strcasecmp(Z_STRVAL_P(z_args[0]), "load")) { /* Make sure we have a second argument, and it's not empty. If it is */ /* empty, we can just return an empty array (which is what Redis does) */ @@ -6664,8 +6670,10 @@ PHP_METHOD(Redis, script) { RETURN_FALSE; } - /* Format our SCRIPT LOAD command */ - cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "ss", "LOAD", 4, Z_STRVAL_P(z_args[1]), Z_STRLEN_P(z_args[1])); + // Format our SCRIPT LOAD command + cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "ss", + "LOAD", 4, Z_STRVAL_P(z_args[1]), + Z_STRLEN_P(z_args[1])); } else if(!strcasecmp(Z_STRVAL_P(z_args[0]), "exists")) { /* Construct our SCRIPT EXISTS command */ cmd_len = redis_build_script_exists_cmd(&cmd, &(z_args[1]), argc-1); @@ -6709,7 +6717,7 @@ PHP_METHOD(Redis, dump) { /* Prefix our key if we need to */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "DUMP", "s" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "DUMP", "s", key, key_len); if(key_free) efree(key); @@ -6775,7 +6783,7 @@ PHP_METHOD(Redis, restore) { /* Prefix the key if we need to */ key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "RESTORE", "sls" TSRMLS_CC, key, + cmd_len = redis_cmd_format_static(&cmd, "RESTORE", "sls", key, key_len, ttl, value, value_len); if(key_free) efree(key); @@ -6815,20 +6823,25 @@ PHP_METHOD(Redis, migrate) { /* Construct our command */ if(copy && replace) { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port, - key, key_len, dest_db, timeout, "COPY", - sizeof("COPY")-1, "REPLACE", sizeof("REPLACE")-1); + cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", + host, host_len, port, key, key_len, + dest_db, timeout, "COPY", + sizeof("COPY")-1, "REPLACE", + sizeof("REPLACE")-1); } else if(copy) { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port, - key, key_len, dest_db, timeout, "COPY", + cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", + host, host_len, port, key, key_len, + dest_db, timeout, "COPY", sizeof("COPY")-1); } else if(replace) { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port, - key, key_len, dest_db, timeout, "REPLACE", + cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", + host, host_len, port, key, key_len, + dest_db, timeout, "REPLACE", sizeof("REPLACE")-1); } else { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, - key, key_len, dest_db, timeout); + cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", + host, host_len, port, key, key_len, + dest_db, timeout); } /* Free our key if we prefixed it */ @@ -7182,10 +7195,10 @@ PHP_METHOD(Redis, client) { /* Build our CLIENT command */ if(ZEND_NUM_ARGS() == 2) { - cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len, - arg, arg_len); + cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", + opt, opt_len, arg, arg_len); } else { - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "CLIENT", "s" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len); } diff --git a/redis_session.c b/redis_session.c index 4ddf793f61..725c0ae337 100644 --- a/redis_session.c +++ b/redis_session.c @@ -122,7 +122,7 @@ redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { if(!rpm->auth || !rpm->auth_len) { /* no password given. */ return; } - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "AUTH", "s" TSRMLS_CC, rpm->auth, + cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", rpm->auth, rpm->auth_len); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { @@ -139,7 +139,7 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { char *response, *cmd; int response_len, cmd_len; - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SELECT", "d" TSRMLS_CC, + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", rpm->database); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { @@ -351,7 +351,7 @@ PS_READ_FUNC(redis) /* send GET command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "GET", "s" TSRMLS_CC, session, + cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { @@ -385,7 +385,7 @@ PS_WRITE_FUNC(redis) /* send SET command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "SETEX", "sds" TSRMLS_CC, session, + cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); @@ -427,7 +427,7 @@ PS_DESTROY_FUNC(redis) /* send DEL command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(redis_sock, &cmd, "DEL", "s", TSRMLS_CC, session, + cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", TSRMLS_CC, session, session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { From 67137d980047cc62bdc89ad825b8febecb745a03 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 08:12:52 -0700 Subject: [PATCH 0220/1986] Fix typos --- redis.c | 123 ++++++++------------------------------------------------ 1 file changed, 17 insertions(+), 106 deletions(-) diff --git a/redis.c b/redis.c index 723a3f833e..a4f5e5e1c9 100644 --- a/redis.c +++ b/redis.c @@ -28,6 +28,7 @@ #include "ext/standard/info.h" #include "php_ini.h" #include "php_redis.h" +#include "redis_commands.h" #include "redis_array.h" #include "redis_cluster.h" #include @@ -933,98 +934,17 @@ PHP_METHOD(Redis, close) /* {{{ proto boolean Redis::set(string key, mixed value, long timeout | array options) */ PHP_METHOD(Redis, set) { - zval *object; RedisSock *redis_sock; - char *key = NULL, *val = NULL, *cmd, *exp_type = NULL, *set_type = NULL; - int key_len, val_len, cmd_len; - long expire = -1; - int val_free = 0, key_free = 0; - zval *z_value, *z_opts = NULL; - - /* Make sure the arguments are correct */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|z", - &object, redis_ce, &key, &key_len, &z_value, - &z_opts) == FAILURE) - { - RETURN_FALSE; - } - - /* Ensure we can grab our redis socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } + char *cmd; + int cmd_len; - /* Our optional argument can either be a long (to support legacy SETEX */ - /* redirection), or an array with Redis >= 2.6.12 set options */ - if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY - && Z_TYPE_P(z_opts) != IS_NULL) + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || + redis_set_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &cmd, + &cmd_len, NULL)==FAILURE) { RETURN_FALSE; } - /* Serialization, key prefixing */ - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { - HashTable *kt = Z_ARRVAL_P(z_opts); - int type; - unsigned int ht_key_len; - unsigned long idx; - char *k; - zval **v; - - /* Iterate our option array */ - for(zend_hash_internal_pointer_reset(kt); - zend_hash_has_more_elements(kt) == SUCCESS; - zend_hash_move_forward(kt)) - { - /* Grab key and value */ - type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL); - zend_hash_get_current_data(kt, (void**)&v); - - if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) && - (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k)) - { - exp_type = k; - expire = Z_LVAL_PP(v); - } else if(Z_TYPE_PP(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_PP(v))) { - set_type = Z_STRVAL_PP(v); - } - } - } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { - expire = Z_LVAL_P(z_opts); - } - - /* Now let's construct the command we want */ - if(exp_type && set_type) { - /* SET NX|XX PX|EX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "ssssl", key, - key_len, val, val_len, set_type, 2, - exp_type, 2, expire); - } else if(exp_type) { - /* SET PX|EX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "sssl", key, - key_len, val, val_len, exp_type, 2, - expire); - } else if(set_type) { - /* SET NX|XX */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "sss", key, - key_len, val, val_len, set_type, 2); - } else if(expire > 0) { - /* Backward compatible SETEX redirection */ - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sls", key, - key_len, expire, val, val_len); - } else { - /* SET */ - cmd_len = redis_cmd_format_static(&cmd, "SET", "ss", key, - key_len, val, val_len); - } - - /* Free our key or value if we prefixed/serialized */ - if(key_free) efree(key); - if(val_free) STR_FREE(val); - /* Kick off the command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -1302,32 +1222,24 @@ PHP_METHOD(Redis, renameNx) */ PHP_METHOD(Redis, get) { - zval *object; RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len; - int key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } + char *cmd; + int cmd_len; - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + // Grab our socket and parse arguments/build command + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || + redis_get_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &cmd, + &cmd_len, NULL)==FAILURE) + { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GET", "s", key, key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_string_response); - } /* }}} */ @@ -1498,7 +1410,6 @@ PHP_METHOD(Redis, decr) /* {{{ proto boolean Redis::decrBy(string key ,int value) */ PHP_METHOD(Redis, decrBy){ - zval *object; char *key = NULL; int key_len; @@ -6935,7 +6846,7 @@ PHP_METHOD(Redis, _unserialize) { /* We only need to attempt unserialization if we have a serializer running */ if(redis_sock->serializer != REDIS_SERIALIZER_NONE) { zval *z_ret = NULL; - if(redis_unserialize(redis_sock->serializer, value, value_len, &z_ret + if(redis_unserialize(redis_sock, value, value_len, &z_ret TSRMLS_CC) == 0) { // Badly formed input, throw an execption From 8e12374b44de6565c59006cc0c4bff56552b5c80 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 10:21:33 -0700 Subject: [PATCH 0221/1986] Initial commit of request/redirect/request logic This is the initial draft of the logic we'll use in phpredis to direct requests at a cluster, and then upon getting a moved or ask response, try at the node delivered. --- cluster_library.c | 148 +++++++++++++++++++++++++++++----------------- cluster_library.h | 43 +++++++++----- 2 files changed, 121 insertions(+), 70 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 2f73ae434b..ef11b6158f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -86,7 +86,7 @@ unsigned short cluster_hash_key_zval(zval *z_key) { char buf[255]; int klen; - // Switch based on ZVAL type + // Switch based on ZVAL type switch(Z_TYPE_P(z_key)) { case IS_STRING: kptr = Z_STRVAL_P(z_key); @@ -153,7 +153,7 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { efree(array); return -1; } - + // Sanity check on our cluster ID value if(strlen(array[CLUSTER_NODES_HASH])!=CLUSTER_NAME_LEN) { efree(array); @@ -181,14 +181,14 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { efree(array); return -1; } - + // If we've got a master hash slot, set it if(memcmp(array[CLUSTER_NODES_MASTER_HASH], "-", sizeof("-"))!=0) { if(strlen(array[CLUSTER_NODES_MASTER_HASH])!=CLUSTER_NAME_LEN) { efree(array); return -1; } - info->master_name = estrndup(array[CLUSTER_NODES_MASTER_HASH], + info->master_name = estrndup(array[CLUSTER_NODES_MASTER_HASH], CLUSTER_NAME_LEN); } else { info->master_name = NULL; @@ -202,7 +202,7 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { return -1; } - // Null terminate a the - + // Null terminate a the - *p = '\0'; // Pull out start and end slot @@ -220,12 +220,12 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { return 0; } -/* +/* * Execute a CLUSTER NODES command against the given seed and return an array * of nodes that came back, along with setting a pointer as to how many there * are. */ -static clusterNodeInfo +static clusterNodeInfo **cluster_get_nodes(redisCluster *cluster, int *len, RedisSock *redis_sock TSRMLS_DC) { @@ -235,7 +235,7 @@ static clusterNodeInfo int cmd_len, i, j, count; // Create our CLUSTER NODES command - cmd_len = redis_cmd_format_static(&cmd, "CLUSTER", "s", "NODES", + cmd_len = redis_cmd_format_static(&cmd, "CLUSTER", "s", "NODES", sizeof("NODES")-1); // Send the command directly to this socket @@ -245,8 +245,8 @@ static clusterNodeInfo } // Make sure we've got a bulk reply and we can read it - if(redis_read_reply_type(redis_sock, &type, len TSRMLS_CC)<0 || - type!=TYPE_BULK || (reply = redis_sock_read_bulk_reply(redis_sock, + if(redis_read_reply_type(redis_sock, &type, len TSRMLS_CC)<0 || + type!=TYPE_BULK || (reply = redis_sock_read_bulk_reply(redis_sock, *len TSRMLS_CC))==NULL) { efree(cmd); @@ -281,12 +281,12 @@ static clusterNodeInfo } /* Create a cluster node struct */ -static redisClusterNode* +static redisClusterNode* cluster_node_create(redisCluster *cluster, clusterNodeInfo *info) { redisClusterNode *node; node = ecalloc(1, sizeof(redisClusterNode)); - + // Set top level cluster info node->name = estrndup(info->name, CLUSTER_NAME_LEN); if(info->master_name) { @@ -353,7 +353,7 @@ void cluster_free_info_array(clusterNodeInfo **array, int count) { } /* Get a RedisSock object from the host and port where we have been - * directed from an ASK response. We'll first see if we have + * directed from an ASK response. We'll first see if we have * connected to this node already, and return that. If not, we * create it and add it to our nodes. */ @@ -364,7 +364,7 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { int key_len; // It'll be hashed as host:port in our nodes HashTable - key_len = snprintf(key, sizeof(key), "%s:%u", c->redir_host, + key_len = snprintf(key, sizeof(key), "%s:%u", c->redir_host, c->redir_port); // See if we've already attached to it @@ -410,10 +410,10 @@ cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, // Create our slave node slave_node = cluster_node_create(cluster, slave); - + // Attach it to our slave if(zend_hash_next_index_insert(master->slaves, (void*)&slave_node, - sizeof(redisClusterNode*), NULL)==FAILURE) + sizeof(redisClusterNode*), NULL)==FAILURE) { return -1; } @@ -431,8 +431,8 @@ cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, } /* Given masters and slaves from node discovery, set up our nodes */ -static int -cluster_set_node_info(redisCluster *cluster, HashTable *masters, +static int +cluster_set_node_info(redisCluster *cluster, HashTable *masters, HashTable *slaves TSRMLS_DC) { clusterNodeInfo **info, **slave; @@ -442,7 +442,7 @@ cluster_set_node_info(redisCluster *cluster, HashTable *masters, int i, key_len; uint name_len; ulong idx; - + // Iterate over our master nodes for(zend_hash_internal_pointer_reset(masters); zend_hash_has_more_elements(masters)==SUCCESS; @@ -561,13 +561,13 @@ cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { if((node = cluster_get_nodes(cluster, &count, *seed TSRMLS_CC))) { valid = 1; break; - } + } } // Throw an exception if we couldn't map if(!valid) { zend_throw_exception(redis_cluster_exception_ce, - "Couldn't map cluster keyspace using any provided seed", 0 + "Couldn't map cluster keyspace using any provided seed", 0 TSRMLS_CC); return -1; } @@ -581,7 +581,7 @@ cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { // Iterate nodes, splitting into master and slave groups for(i=0;imaster_name == NULL) { - zend_hash_update(masters, node[i]->name, CLUSTER_NAME_LEN+1, + zend_hash_update(masters, node[i]->name, CLUSTER_NAME_LEN+1, (void*)&node[i], sizeof(clusterNodeInfo*), NULL); } else { HashTable *ht_inner; @@ -636,28 +636,31 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, if(zend_hash_find(c->nodes, key, key_len+1, (void**)&ret)==SUCCESS) { return *ret; } - + // Not found return NULL; } -/* Once we write a command to a node in our cluster, this function will check +/* Once we write a command to a node in our cluster, this function will check * the reply type and extract information from those that will specify a length - * bit. If we encounter an error condition, we'll check for MOVED or ASK - * redirection, parsing out slot host and port so the caller can take + * bit. If we encounter an error condition, we'll check for MOVED or ASK + * redirection, parsing out slot host and port so the caller can take * appropriate action. * - * In the case of a non MOVED/ASK error, we wlll set our cluster error - * condition so GetLastError can be queried by the client. + * In the case of a non MOVED/ASK error, we wlll set our cluster error + * condition so GetLastError can be queried by the client. * * This function will return -1 on a critical error (e.g. parse/communication * error, 0 if no redirection was encountered, and 1 if the data was moved. */ -static int cluster_check_response(redisCluster *c, unsigned short slot, - REDIS_REPLY_TYPE *reply_type, TSRMLS_DC) +static int cluster_check_response(redisCluster *c, unsigned short slot, + REDIS_REPLY_TYPE *reply_type TSRMLS_DC) { + // Clear out any prior error state + c->err_state = 0; + // Check for general socket EOF and then EOF on our reply type request if((-1 == redis_check_eof(SLOT(c,slot)->sock)) || - (*reply_type = php_stream_getc(SLOT_STREAM(c,slot)))) + (*reply_type = php_stream_getc(SLOT_STREAM(c,slot)))) { // Actual communications error return -1; @@ -676,7 +679,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, // Check for MOVED or ASK redirection if((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { char *pslot, *phost, *pport; - + // Move pased MOVED or ASK error message if(moved) { pslot = inbuf + MOVED_LEN; @@ -699,7 +702,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, // Null terminate here *pport++ = '\0'; - + // Set our cluster redirection information c->redir_type = moved ? REDIR_MOVED : REDIR_ASK; strncpy(c->redir_host, phost, sizeof(c->redir_host)); @@ -715,13 +718,13 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, return 0; } } - + // For BULK, MULTI BULK, or simply INTEGER response typese we can get // the response length. - if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { - char inbuf[255]; + char inbuf[1024]; if(php_stream_gets(SLOT_STREAM(c,slot), inbuf, sizeof(inbuf))<0) { return -1; @@ -736,8 +739,8 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, return 0; } -/* Attempt to write to a cluster node. If the node is NULL (e.g. - * it's been umapped, we keep falling back until we run out of nodes +/* Attempt to write to a cluster node. If the node is NULL (e.g. + * it's been umapped, we keep falling back until we run out of nodes * to try */ static int cluster_sock_write(redisCluster *c, unsigned short slot, const char *cmd, size_t sz TSRMLS_DC) @@ -765,14 +768,14 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, // TODO: Randomize the slots we request from for(i=0;istream, cmd, sz)) { // Return the slot where we actually sent the request return i; - } + } } // We were unable to write to any node in our cluster @@ -783,7 +786,7 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, * the slot where data was moved, update our node mapping */ static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { redisClusterNode *node; - + // Invalidate original slot c->master[orig_slot] = NULL; @@ -799,8 +802,8 @@ static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { c->master[c->redir_slot] = node; } else { // Create our node - node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, - c->redir_host_len, c->redir_port, c->redir_slot, + node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, + c->redir_host_len, c->redir_port, c->redir_slot, c->redir_slot); // Now point our slot at the node @@ -811,20 +814,20 @@ static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { // Check to see if the ip and port are mapped node = cluster_find_node(c, c->redir_host, c->redir_port); if(!node) { - node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, + node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, c->redir_slot); } - + // Map the slot to this node c->master[c->redir_slot] = node; } } -/* Send a command to given slot in our cluster. If we get a MOVED - * or ASK error we attempt to send the command to the node as +/* Send a command to given slot in our cluster. If we get a MOVED + * or ASK error we attempt to send the command to the node as * directed. */ -PHPAPI int cluster_send_command(redisCluster *c, short slot, - const char *cmd, int cmd_len TSRMLS_DC) +PHPAPI short cluster_send_command(redisCluster *c, short slot, + const char *cmd, int cmd_len TSRMLS_DC) { REDIS_REPLY_TYPE reply_type; int resp, reply_len, rslot = slot; @@ -832,13 +835,13 @@ PHPAPI int cluster_send_command(redisCluster *c, short slot, // Issue commands until we find the right node or fail do { // Attempt to send the command to the slot requested - if((slot = cluster_sock_write(c, slot, cmd, cmd_len - TSRMLS_CC))==-1) + if((slot = cluster_sock_write(c, slot, cmd, cmd_len + TSRMLS_CC))==-1) { - // We have no choice but to throw an exception. We + // We have no choice but to throw an exception. We // can't communicate with any node at all. zend_throw_exception(redis_cluster_exception_ce, - "Can't communicate with any node in the cluster", + "Can't communicate with any node in the cluster", 0 TSRMLS_CC); return -1; } @@ -852,7 +855,7 @@ PHPAPI int cluster_send_command(redisCluster *c, short slot, // encountered, we'll just try again at that slot. if(resp == -1) { // TODO: More robust error handling, count errors and ultimately - // fail? + // fail? sleep(1); } else if(resp == 1) { // In case of a MOVED redirection, update our node mapping @@ -862,7 +865,7 @@ PHPAPI int cluster_send_command(redisCluster *c, short slot, } } while(resp != 0); - // Inform the cluster where to read the rest of our response, + // Inform the cluster where to read the rest of our response, // and clear out redirection flag. c->reply_slot = slot; c->redir_type = REDIR_NONE; @@ -871,4 +874,39 @@ PHPAPI int cluster_send_command(redisCluster *c, short slot, return 0; } +/* RedisCluster response handlers. These methods all have the same prototype + * and set the proper return value for the calling cluster method. These + * methods will never be called in the case of a communication error when + * we try to send the request to the Cluster *or* if a non MOVED or ASK + * error is encountered, in which case our response processing macro will + * short circuit and RETURN_FALSE, as the error will have already been + * consumed. + */ + +/* Unmodified BULK response handler */ +PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +{ + char *resp; + + // Make sure we can read the response + if((resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), + c->reply_len TSRMLS_CC))==NULL) + { + RETURN_FALSE; + } + + // Return the string if we can unserialize it + if(redis_unserialize(c->flags, resp, c->reply_len, &return_value)==0) { + RETURN_STRINGL(resp,c->reply_len,0); + } else { + efree(resp); + } +} + +/* A boolean response, which if we get here, is a success */ +PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +{ + RETURN_TRUE; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index 97bd1a197c..e74874eec8 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -56,15 +56,13 @@ typedef enum CLUSTER_REDIR_TYPE { strlen(SLOT_SOCK(c,c->redir_slot)->host) != c->redir_host_len || \ memcmp(SLOT_SOCK(c,c->redir_slot)->host,c->redir_host,c->redir_host_len)) -/* Send a request to our cluster, and process it's response */ -#define CLUSTER_PROCESS_REQUEST(cluster, slot, cmd, cmd_len, resp_cb) \ - if(cluster_send_command(cluster,slot,cmd,cmd_len TSRMLS_CC)<0 || \ - resp_cb(cluster, INTERNAL_FUNCTION_PARAM_PASSTHRU)<0) \ - { \ - RETVAL_FALSE; \ - } \ - efree(cmd); \` +/* Send a request to our cluster, but take a key and key_len */ +#define CLUSTER_PROCESS_REQUEST_KEY(key, key_len, cmd, cmd_len, func) \ + CLUSTER_PROCESS_REQUEST(cluster_hash_key(key,key_len),cmd,cmd_len,func) +/* Send a request to the cluster with a key stored as a zval pointer */ +#define CLUSTER_PROCESS_REQUEST_ZVAL(key, cmd, cmd_len, func) \ + CLUSTER_PROCESS_REQUEST(cluster_hash_key_zval(key),cmd,cmd_len,func) /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); @@ -95,7 +93,7 @@ typedef struct redisClusterNode { /* Our start and end slots that we serve */ unsigned short start_slot; - unsigned short end_slot; + unsigned short end_slot; /* A HashTable containing any slaves */ HashTable *slaves; @@ -119,6 +117,9 @@ typedef struct redisCluster { /* All RedisCluster objects we've created/are connected to */ HashTable *nodes; + /* Are we currently in an ERROR state */ + int err_state; + /* The last ERROR we encountered */ char *err; int err_len; @@ -126,6 +127,9 @@ typedef struct redisCluster { /* The slot where we should read replies */ short reply_slot; + /* One RedisSock* object for serialization and prefix information */ + RedisSock *flags; + /* The last reply length we got, which we'll use to parse and * format our replies to the client. */ int reply_len; @@ -144,21 +148,30 @@ unsigned short cluster_hash_key(const char *key, int len); /* Send a command to where we think the key(s) should live and redirect when * needed */ -PHPAPI short cluster_send_command(redisCluster *cluster, short slot, +PHPAPI short cluster_send_command(redisCluster *cluster, short slot, const char *cmd, int cmd_len TSRMLS_DC); PHPAPI int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds); PHPAPI int cluster_map_keyspace(redisCluster *cluster TSRMLS_DC); PHPAPI void cluster_free_node(redisClusterNode *node); -PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, +PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len TSRMLS_DC); -PHPAPI int cluster_node_add_slave(redisCluster *cluster, - redisClusterNode *master, +PHPAPI int cluster_node_add_slave(redisCluster *cluster, + redisClusterNode *master, clusterNodeInfo *slave TSRMLS_DC); -/* Response handlers */ -PHPAPI int cluster_bulk_response( +/* + * Redis Cluster response handlers. All of our response handlers take the + * following form: + * PHPAPI void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) + * + * Reply handlers are responsible for setting the PHP return value (either to + * something valid, or FALSE in the case of some failures). + */ + +PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); #endif From 7da57ed45baf4c8bf5f6a9af7619f9011b16e3d1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 10:22:42 -0700 Subject: [PATCH 0222/1986] Initial commit of redis_commands.c Given that non cluster and cluster based redis objects should act exactly the same way (in terms of argument handling/processing) we're extracting the parsing of arguments and command construction from being directly inside of the PHP_METHOD itself. This way, we don't repeat ourselves having two identical places where we do this but in a slightly different way. Every command takes a RedisSock pointer (coming from redis_sock_get in a standard redis class, and cluster->flags in a cluster). If we're passed a pointer for slot, we'll hash one of the keys to return it's proper slot. --- config.m4 | 2 +- redis_commands.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 32 ++++++++++++ 3 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 redis_commands.c create mode 100644 redis_commands.h diff --git a/config.m4 b/config.m4 index af8c5f33da..3a80badf09 100755 --- a/config.m4 +++ b/config.m4 @@ -99,5 +99,5 @@ dnl Check for igbinary dnl dnl PHP_SUBST(REDIS_SHARED_LIBADD) - PHP_NEW_EXTENSION(redis, redis.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c, $ext_shared) + PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c, $ext_shared) fi diff --git a/redis_commands.c b/redis_commands.c new file mode 100644 index 0000000000..1b47a69a40 --- /dev/null +++ b/redis_commands.c @@ -0,0 +1,129 @@ +/* -*- Mode: C; tab-width: 4 -*- */ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2009 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Original Author: Michael Grunder | + +----------------------------------------------------------------------+ +*/ + +#include "redis_commands.h" + +/* GET */ +int redis_get_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + char *key=NULL; + int key_len, key_free; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, + &key_len)==FAILURE) + { + return FAILURE; + } + + key_free = redis_key_prefix(redis_sock, &key, &key_len); + *cmd_len = redis_cmd_format_static(cmd, "GET", "s", key, key_len); + + CMD_SET_SLOT(slot, key, key_len); + if(key_free) efree(key); + + return SUCCESS; +} + +/* SET */ +int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + zval *z_value, *z_opts=NULL; + char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; + int key_len, val_len, key_free, val_free; + long expire = -1; + + // Make sure the function is being called correctly + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, + &z_value, &z_opts)==FAILURE) + { + return FAILURE; + } + + // Serialize and key prefix if required + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Check for an options array + if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { + HashTable *kt = Z_ARRVAL_P(z_opts); + int type; + unsigned int ht_key_len; + unsigned long idx; + char *k; + zval **v; + + /* Iterate our option array */ + for(zend_hash_internal_pointer_reset(kt); + zend_hash_has_more_elements(kt) == SUCCESS; + zend_hash_move_forward(kt)) + { + // Grab key and value + type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL); + zend_hash_get_current_data(kt, (void**)&v); + + if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) && + (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k)) + { + exp_type = k; + expire = Z_LVAL_PP(v); + } else if(Z_TYPE_PP(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_PP(v))) { + set_type = Z_STRVAL_PP(v); + } + } + } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { + expire = Z_LVAL_P(z_opts); + } + + /* Now let's construct the command we want */ + if(exp_type && set_type) { + /* SET NX|XX PX|EX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "ssssl", key, key_len, + val, val_len, set_type, 2, exp_type, + 2, expire); + } else if(exp_type) { + /* SET PX|EX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "sssl", key, key_len, + val, val_len, exp_type, 2, expire); + } else if(set_type) { + /* SET NX|XX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "sss", key, key_len, val, + val_len, set_type, 2); + } else if(expire > 0) { + /* Backward compatible SETEX redirection */ + *cmd_len = redis_cmd_format_static(cmd, "SETEX", "sls", key, key_len, + expire, val, val_len); + } else { + /* SET */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "ss", key, key_len, val, + val_len); + } + + // If we've been passed a slot pointer, return the key's slot + CMD_SET_SLOT(slot,key,key_len); + + if(key_free) efree(key); + if(val_free) efree(val); + + return SUCCESS; +} + +/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h new file mode 100644 index 0000000000..9735cb46af --- /dev/null +++ b/redis_commands.h @@ -0,0 +1,32 @@ +#ifndef REDIS_COMMANDS_H +#define REDIS_COMMANDS_H + +#include "common.h" +#include "library.h" +#include "cluster_library.h" + +/* Macro for setting the slot if we've been asked to */ +#define CMD_SET_SLOT(slot,key,key_len) \ + if(slot) *slot = cluster_hash_key(key,key_len); + +/* Redis command construction routines, which always take the form: + * int function(INTERNAL_FUNCTION_PARAMETERS, RedisSock* redis_sock, + * char **cmd, int *cmd_len, short *slot); + * + * The functions will return SUCCESS on success, and FAILURE on failure. In + * the case of a failure, the cmd pointer will not have been updated, and + * no memory wlll have been allocated (that wasn't freed). + * + * If the slot pointer is passed as non-null, it will be set to the Redis + * Cluster hash slot where the key(s) belong. + */ + +int redis_get_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot); + +int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot); + +#endif + +/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From ca03b118222f34394cfa2592b69eddfd72b4aec4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 10:25:17 -0700 Subject: [PATCH 0223/1986] Protect library.h --- library.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library.h b/library.h index 56f9ffeace..4d3c2d0f4d 100644 --- a/library.h +++ b/library.h @@ -1,3 +1,6 @@ +#ifndef REDIS_LIBRARY_H +#define REDIS_LIBRARY_H + void add_constant_long(zend_class_entry *ce, char *name, int value); int integer_length(int i); int redis_cmd_format(char **ret, char *format, ...); @@ -86,3 +89,5 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo dbl_str = _php_math_number_format(dbl, 16, '.', '\x00'); \ dbl_len = strlen(dbl_str); #endif + +#endif From f41689248f122b46555bb86fec572461c90582d3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 10:25:57 -0700 Subject: [PATCH 0224/1986] Updated command processing logic. Given that we're formalizing command builing functions such that they are always in the form redis_cmdname_cmd, and that our response processing functions always take the same arguments, our PHP_METHOD bodies can be very generic. --- redis_cluster.c | 47 ++++++++++++++++++----------------------------- redis_cluster.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 805cd9a27c..a3461136c3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -26,6 +26,7 @@ #include "ext/standard/info.h" #include "crc16.h" #include "redis_cluster.h" +#include "redis_commands.h" #include #include "library.h" @@ -81,7 +82,7 @@ PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC) { } /* Create redisCluster context */ -zend_object_value +zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { zend_object_value retval; redisCluster *cluster; @@ -90,6 +91,9 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { cluster = emalloc(sizeof(redisCluster)); memset(cluster, 0, sizeof(redisCluster)); + // Allocate our RedisSock we'll use to store prefix/serialization flags + cluster->flags = emalloc(sizeof(RedisSock)); + // Allocate our hash table for seeds ALLOC_HASHTABLE(cluster->seeds); zend_hash_init(cluster->seeds, 0, NULL, ht_free_seed, 0); @@ -108,8 +112,8 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { (copy_ctor_func_t)zval_add_ref, (void*)&tmp, sizeof(zval*)); #endif - retval.handle = zend_objects_store_put(cluster, - (zend_objects_store_dtor_t)zend_objects_destroy_object, + retval.handle = zend_objects_store_put(cluster, + (zend_objects_store_dtor_t)zend_objects_destroy_object, free_cluster_context, NULL TSRMLS_CC); retval.handlers = zend_get_std_object_handlers(); @@ -121,10 +125,14 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { void free_cluster_context(void *object TSRMLS_DC) { redisCluster *cluster; redisClusterNode **node; - + // Grab context cluster = (redisCluster*)object; + // Free any allocated prefix, as well as the struct + if(cluster->flags->prefix) efree(cluster->flags->prefix); + efree(cluster->flags); + // Free seeds HashTable itself zend_hash_destroy(cluster->seeds); efree(cluster->seeds); @@ -148,7 +156,7 @@ PHP_METHOD(RedisCluster, __construct) { long name_len; double timeout = 0.0, read_timeout = 0.0; redisCluster *context = GET_CONTEXT(); - + // Parse arguments if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|add", &object, redis_cluster_ce, &name, &name_len, @@ -162,21 +170,21 @@ PHP_METHOD(RedisCluster, __construct) { // Validate timeout if(timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, + zend_throw_exception(redis_cluster_exception_ce, "Invalid timeout", 0 TSRMLS_CC); RETURN_FALSE; } // Validate our read timeout if(read_timeout < 0L || read_timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, + zend_throw_exception(redis_cluster_exception_ce, "Invalid read timeout", 0 TSRMLS_CC); RETURN_FALSE; } // TODO: Implement seed retrieval from php.ini if(!z_seeds || zend_hash_num_elements(Z_ARRVAL_P(z_seeds))==0) { - zend_throw_exception(redis_cluster_exception_ce, + zend_throw_exception(redis_cluster_exception_ce, "Must pass seeds", 0 TSRMLS_CC); RETURN_FALSE; } @@ -190,29 +198,10 @@ PHP_METHOD(RedisCluster, __construct) { /* GET */ PHP_METHOD(RedisCluster, get) { - zval *z_obj; - char *cmd, *key; - int cmd_len, key_len; - redisCluster *c = GET_CONTEXT(); - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os", &z_obj, redis_cluster_ce, &key, - &key_len)==FAILURE) - { - RETURN_FALSE; - } - - // Format our command - cmd_len = redis_cmd_format_static(&cmd, "GET", "s", key, key_len); - - // Send it to our cluster - CLUSTER_PROCESS_REQUEST(c, cluster_hash_key(key,key_len), cmd, cmd_len); - CLUSTER_READ_RESPONSE(c, cluster_string_response); - - RETURN_FALSE; + CLUSTER_PROCESS_REQUEST(get, cluster_bulk_resp); } /* SET */ PHP_METHOD(RedisCluster, set) { - RETURN_FALSE; + CLUSTER_PROCESS_REQUEST(set, cluster_bool_resp); } diff --git a/redis_cluster.h b/redis_cluster.h index 5998689f66..7135b736a6 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -13,6 +13,41 @@ #define GET_CONTEXT() \ (redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC) +/* Command building/processing is identical for every command */ +#define CLUSTER_BUILD_CMD(name, c, cmd, cmd_len, slot) \ + redis_##name##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, &cmd_len, &slot) + +#define CLUSTER_PROCESS_REQUEST(cmdname, resp_func) \ + redisCluster *c = GET_CONTEXT(); \ + char *cmd; \ + int cmd_len; \ + short slot; \ + if(redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,c->flags, &cmd, \ + &cmd_len, &slot)==FAILURE) \ + { \ + RETURN_FALSE; \ + } \ + if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || \ + c->err_state!=0) \ + { \ + efree(cmd); \ + RETURN_FALSE; \ + } \ + resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c); + + + +/* Send a request to our cluster, and process the response */ +/*#define CLUSTER_PROCESS_REQUEST(c, slot, cmd, cmd_len, resp_func) \ + if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || \ + c->err_state!=0) || \ + { \ + efree(cmd); \ + RETURN_FALSE; \ + } \ + efree(cmd); +*/ + /* For the creation of RedisCluster specific exceptions */ PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); From 1408edecb5bf43361d9174050990ab24d4428b7a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 10:27:50 -0700 Subject: [PATCH 0225/1986] Fix improper redis_cmd_format_static calls --- redis_session.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/redis_session.c b/redis_session.c index 725c0ae337..8faf29a778 100644 --- a/redis_session.c +++ b/redis_session.c @@ -139,8 +139,7 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { char *response, *cmd; int response_len, cmd_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", - rpm->database); + cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", rpm->database); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { @@ -351,9 +350,9 @@ PS_READ_FUNC(redis) /* send GET command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, - session_len); - efree(session); + cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, session_len); + + efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; @@ -427,8 +426,7 @@ PS_DESTROY_FUNC(redis) /* send DEL command */ session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", TSRMLS_CC, session, - session_len); + cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", session, session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); From 7a0502534b94c58bc76840e0ec556dd25ec8b264 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 30 May 2014 11:23:56 -0700 Subject: [PATCH 0226/1986] Add lazy connect logic, use seeds not nodes for fallback. We need to try to connect to our node if we've mapped it but not yet connected to the server itself. In addition, rather than iterating through nodes 0-CLUSTER_MAX_SLOT as a fallback, just use our seeds, as in almost all cases many slots will point to the same Redis instance. --- cluster_library.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index ef11b6158f..2c6bbbd109 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -746,6 +746,7 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, const char *cmd, size_t sz TSRMLS_DC) { RedisSock *redis_sock; + redisClusterNode **seed_node; int i; // If we're in an ASK redirection state, attempt a connection to that @@ -756,24 +757,33 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, redis_sock = cluster_get_asking_sock(c); } + // If the lazy_connect flag is still set, we've not actually + // connected to this node, so do that now. + if(redis_sock->lazy_connect) { + redis_sock->lazy_connect = 0; + redis_sock_server_open(redis_sock, 1 TSRMLS_CC); + } + // First attempt to write it to the slot that's been requested - if(redis_sock && !redis_check_eof(redis_sock TSRMLS_CC) && + if(redis_sock && redis_sock->stream && + !redis_check_eof(redis_sock TSRMLS_CC) && !php_stream_write(redis_sock->stream, cmd, sz)) { // We were able to write it return 0; } - // Fall back by attempting to write the request to other nodes - // TODO: Randomize the slots we request from - for(i=0;inodes); + zend_hash_has_more_elements(c->nodes)==SUCCESS; + zend_hash_move_forward(c->nodes)) + { + zend_hash_get_current_data(c->seeds, (void**)&seed_node); - // Attempt the write to this node - if(!redis_check_eof(redis_sock TSRMLS_CC) && - !php_stream_write(redis_sock->stream, cmd, sz)) + // Attempt to write our request to this seed node + if(!redis_check_eof((*seed_node)->sock TSRMLS_CC) && + !php_stream_write((*seed_node)->sock->stream, cmd, sz)) { - // Return the slot where we actually sent the request return i; } } From f08370173e7527363e2e17d25212b8d89f89f2fc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 31 May 2014 13:01:36 -0700 Subject: [PATCH 0227/1986] Several updates and fixes to the cluster library. Updated node parsing to understand that clusters can serve 1-N sequential slots, rather than just one contiguous series. Updated how we detected masters by using that column in CLUSTER NODES rather than counting them, as a master can be set up that serves no slots (e.g. newly added or data was migrated away from it). --- cluster_library.c | 321 +++++++++++++++++++++++++++++----------------- cluster_library.h | 54 +++++--- 2 files changed, 237 insertions(+), 138 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 2c6bbbd109..6461a32193 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -7,6 +7,27 @@ extern zend_class_entry *redis_cluster_exception_ce; +/* Some debug methods that will go away when we're through with them */ + +static void cluster_dump_nodes(redisCluster *c) { + redisClusterNode **pp, *p; + int i; + for(zend_hash_internal_pointer_reset(c->nodes); + zend_hash_has_more_elements(c->nodes)==SUCCESS; + zend_hash_move_forward(c->nodes)) + { + zend_hash_get_current_data(c->nodes, (void**)&pp); + p = *pp; + + const char *slave = (p->slave) ? "slave" : "master"; + php_printf("%d %s %d ", p->sock->port, slave,p->sock->prefix_len); + for(i=0;islots_size;i++) { + php_printf("[%d-%d] ",p->slots[i].start,p->slots[i].end); + } + php_printf("\n"); + } +} + /* Set our last error string encountered */ static void cluster_set_err(redisCluster *c, char *err, int err_len) { @@ -20,9 +41,7 @@ static void cluster_set_err(redisCluster *c, char *err, int err_len) c->err[err_len]='\0'; c->err_len = err_len; } else { - if(c->err) { - efree(c->err); - } + if(c->err) efree(c->err); c->err = NULL; c->err_len = 0; } @@ -39,6 +58,9 @@ static void free_cluster_info(clusterNodeInfo *info) { if(info->host) { efree(info->host); } + if(info->slots) { + efree(info->slots); + } efree(info); } @@ -143,27 +165,28 @@ static char **split_str_by_delim(char *str, char *delim, int *len) { /* Simple function to split a string into up to N parts */ static int cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { - char **array, *p; - int count; + char **array, *p, *p2; + int count, i; // First split by space array = split_str_by_delim(line, " ", &count); - if(count != CLUSTER_NODES_MASTER_ELE && count != CLUSTER_NODES_SLAVE_ELE) { + // Sanity check on the number of elements we see + if(count < CLUSTER_MIN_NODE_LINE) { efree(array); return -1; } - + // Sanity check on our cluster ID value if(strlen(array[CLUSTER_NODES_HASH])!=CLUSTER_NAME_LEN) { efree(array); return -1; } - + // Our cluster ID info->name = estrndup(array[CLUSTER_NODES_HASH], CLUSTER_NAME_LEN); - - // Set the host/port bits + + // Set the host/port if(memcmp(array[CLUSTER_NODES_HOST_PORT], ":0", sizeof(":0"))==0) { info->seed = 1; info->host_len = strlen(sock->host); @@ -181,6 +204,9 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { efree(array); return -1; } + + // Flag this a slave node if it's a slave + info->slave = strstr(array[CLUSTER_NODES_TYPE], "slave")!=NULL; // If we've got a master hash slot, set it if(memcmp(array[CLUSTER_NODES_MASTER_HASH], "-", sizeof("-"))!=0) { @@ -190,27 +216,37 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { } info->master_name = estrndup(array[CLUSTER_NODES_MASTER_HASH], CLUSTER_NAME_LEN); - } else { - info->master_name = NULL; + } else if(info->slave) { + // Slaves should always have a master hash + efree(array); + return -1; } + + // See if the node serves slots + if(count >= CLUSTER_MIN_SLOTS_COUNT) { + // Allocate for enough ranges + info->slots_size = count - CLUSTER_MIN_SLOTS_COUNT + 1; + info->slots = ecalloc(info->slots_size, sizeof(clusterSlotRange)); + + // Now iterate over each range + for(i=0;islots_size;i++) { + p = array[i+CLUSTER_MIN_SLOTS_COUNT-1]; + + // If we don't see -, this node only serves one slot + if((p2 = strchr(p,'-'))==NULL) { + info->slots[i].start = atoi(p); + info->slots[i].end = atoi(p); + } else { + *p2++ = '\0'; - // If this is a master, parse slots - if(count == CLUSTER_NODES_MASTER_ELE) { - // This must be in the form - - if((p = strchr(array[CLUSTER_SLOTS], '-'))==NULL) { - efree(array); - return -1; + // Set this range + info->slots[i].start = atoi(p); + info->slots[i].end = atoi(p2); + } } - - // Null terminate a the - - *p = '\0'; - - // Pull out start and end slot - info->start_slot = atoi(array[CLUSTER_SLOTS]); - info->end_slot = atoi(p+1); } else { - info->start_slot = 0; - info->end_slot = 0; + info->slots_size = 0; + info->slots = NULL; } // Free our array @@ -260,11 +296,11 @@ static clusterNodeInfo nodes = ecalloc(count, sizeof(clusterNodeInfo*)); for(i=0;imaster_name) { node->master_name = estrndup(info->master_name, CLUSTER_NAME_LEN); } - node->start_slot = info->start_slot; - node->end_slot = info->end_slot; + + // Pull in our contiguous slot ranges + node->slots = info->slots; + node->slots_size = info->slots_size; + + // Set slave flag + node->slave = info->slave; - /* Attach our RedisSock */ - /* TODO: Lazy connect as an option? */ + // Create and attach our socket node->sock = redis_sock_create(info->host, info->host_len, info->port, cluster->timeout, 0, NULL, 0, 1); @@ -319,8 +359,12 @@ cluster_node_create_ex(redisCluster *c, const char *name, info.host = (char*)host; info.host_len = host_len; info.port = port; - info.start_slot = start_slot; - info.end_slot = end_slot; + + info.slots = ecalloc(1, sizeof(clusterSlotRange)); + info.slots_size = 1; + + info.slots[0].start = start_slot; + info.slots[0].end = end_slot; return cluster_node_create(c, &info); } @@ -379,8 +423,12 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { pInfo.host = c->redir_host; pInfo.host_len = strlen(c->redir_host); pInfo.port = c->redir_port; - pInfo.start_slot = c->redir_slot; - pInfo.end_slot = c->redir_slot; + + // At this point, we only know one slot it might serve + pInfo.slots = ecalloc(1, sizeof(clusterSlotRange)); + pInfo.slots_size = 1; + pInfo.slots[0].start = c->redir_slot; + pInfo.slots[0].end = c->redir_slot; // Create a redisClusterNode *ppNode = cluster_node_create(c, &pInfo); @@ -408,6 +456,15 @@ cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, zend_hash_init(master->slaves, 0, NULL, NULL, 0); } + // Inherit the start - end slots from our master + slave->slots = ecalloc(master->slots_size, sizeof(clusterSlotRange)); + slave->slots_size = master->slots_size; + memcpy(slave->slots, master->slots, + master->slots_size * sizeof(clusterSlotRange)); + + // Flag this node as a slave + slave->slave = 1; + // Create our slave node slave_node = cluster_node_create(cluster, slave); @@ -439,7 +496,7 @@ cluster_set_node_info(redisCluster *cluster, HashTable *masters, HashTable **master_slaves; redisClusterNode *node; char *name, key[1024]; - int i, key_len; + int i, j, key_len; uint name_len; ulong idx; @@ -474,10 +531,12 @@ cluster_set_node_info(redisCluster *cluster, HashTable *masters, } } - // Point appropriate slots to this node - for(i=node->start_slot;i<=node->end_slot;i++) { - cluster->master[i] = node; - } + // Iterate each contiguous slot range, and point to the node + for(i=0;islots_size;i++) { + for(j=node->slots[i].start;j<=node->slots[i].end;j++) { + cluster->master[j] = node; + } + } // Create a host:port key for this node key_len = snprintf(key, sizeof(key), "%s:%u", node->sock->host, @@ -506,21 +565,21 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { { // Grab seed string zend_hash_get_current_data(ht_seeds, (void**)&z_seed); - + // Skip anything that isn't a string if(Z_TYPE_PP(z_seed)!=IS_STRING) continue; - + // Grab a copy of the string str = Z_STRVAL_PP(z_seed); - + // Must be in host:port form if(!(psep = strchr(str, ':'))) continue; // Allocate a structure for this seed redis_sock = redis_sock_create(str, psep-str, - (unsigned short)atoi(psep+1), cluster->timeout,0,NULL,0,0); + (unsigned short)atoi(psep+1),cluster->timeout,0,NULL,0,0); // Index this seed by host/port key_len = snprintf(key, sizeof(key), "%s:%u", redis_sock->host, @@ -580,13 +639,14 @@ cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { // Iterate nodes, splitting into master and slave groups for(i=0;imaster_name == NULL) { + if(!node[i]->slave) { zend_hash_update(masters, node[i]->name, CLUSTER_NAME_LEN+1, (void*)&node[i], sizeof(clusterNodeInfo*), NULL); } else { HashTable *ht_inner; - // Determine if we've already got at least one slave for this master + // Determine if we've already got at least one slave for this + // master node. if(zend_hash_find(slaves, node[i]->master_name, CLUSTER_NAME_LEN+1, (void**)&sub_slaves)==FAILURE) { @@ -609,7 +669,7 @@ cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { // Now that we have the key space mapped by master ID, we can set // socket information on them for communication. cluster_set_node_info(cluster, masters, slaves TSRMLS_CC); - + // Free our array of clusterNodeInfo* objects. The HashTables will clean // up the clusterNodeInfo* pointers themselves. efree(node); @@ -641,6 +701,38 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, return NULL; } +/* Parse the MOVED OR ASK redirection payload when we get such a response + * and apply this information to our cluster. If we encounter a parse error + * nothing in the cluster will be modified, and -1 is returned. */ +static int cluster_set_redirection(redisCluster* c, char *msg, int moved) +{ + char *host, *port; + + // Move past MOVED or ASK + if(moved) { + msg += MOVED_LEN; + } else { + msg += ASK_LEN; + } + + // We need a slot seperator + if(!(host = strchr(msg, ' '))) return -1; + *host++ = '\0'; + + // We need a : that seperates host from port + if(!(port = strchr(host,':'))) return -1; + *port++ = '\0'; + + // Success, apply it + c->redir_type = moved ? REDIR_MOVED : REDIR_ASK; + strncpy(c->redir_host, host, sizeof(c->redir_host)); + c->redir_host_len = port - host - 1; + c->redir_slot = (unsigned short)atoi(msg); + c->redir_port = (unsigned short)atoi(port); + + return 0; +} + /* Once we write a command to a node in our cluster, this function will check * the reply type and extract information from those that will specify a length * bit. If we encounter an error condition, we'll check for MOVED or ASK @@ -656,16 +748,14 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, REDIS_REPLY_TYPE *reply_type TSRMLS_DC) { // Clear out any prior error state - c->err_state = 0; + CLUSTER_CLEAR_ERROR(c); - // Check for general socket EOF and then EOF on our reply type request - if((-1 == redis_check_eof(SLOT(c,slot)->sock)) || - (*reply_type = php_stream_getc(SLOT_STREAM(c,slot)))) + if(-1 == redis_check_eof(SLOT_SOCK(c,slot)) || + EOF == (*reply_type = php_stream_getc(SLOT_STREAM(c,slot)))) { - // Actual communications error return -1; } - + // In the event of an ERROR, check if it's a MOVED/ASK error if(*reply_type == TYPE_ERR) { char inbuf[1024]; @@ -678,38 +768,11 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, // Check for MOVED or ASK redirection if((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { - char *pslot, *phost, *pport; - - // Move pased MOVED or ASK error message - if(moved) { - pslot = inbuf + MOVED_LEN; - } else { - pslot = inbuf + ASK_LEN; - } - - // We will need to see a slot separator - if(!(phost = strchr(pslot, ' '))) { + // Set our redirection information + if(cluster_set_redirection(c,inbuf,moved)<0) { return -1; } - // Null terminate at the separator - *phost++ = '\0'; - - // We'll need to see host:port separator - if(!(pport = strchr(phost, ':'))) { - return -1; - } - - // Null terminate here - *pport++ = '\0'; - - // Set our cluster redirection information - c->redir_type = moved ? REDIR_MOVED : REDIR_ASK; - strncpy(c->redir_host, phost, sizeof(c->redir_host)); - c->redir_host_len = pport - phost - 1; - c->redir_slot = (unsigned short)atoi(pslot); - c->redir_port = (unsigned short)atoi(pport); - // Data moved return 1; } else { @@ -735,7 +798,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, } // Clear out any previous error, and return that the data is here - cluster_set_err(c, NULL, 0); + CLUSTER_CLEAR_ERROR(c); return 0; } @@ -747,7 +810,6 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, { RedisSock *redis_sock; redisClusterNode **seed_node; - int i; // If we're in an ASK redirection state, attempt a connection to that // host and port. Otherwise, try on the requested slot. @@ -759,32 +821,36 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, // If the lazy_connect flag is still set, we've not actually // connected to this node, so do that now. - if(redis_sock->lazy_connect) { - redis_sock->lazy_connect = 0; - redis_sock_server_open(redis_sock, 1 TSRMLS_CC); - } + CLUSTER_LAZY_CONNECT(redis_sock); // First attempt to write it to the slot that's been requested if(redis_sock && redis_sock->stream && !redis_check_eof(redis_sock TSRMLS_CC) && - !php_stream_write(redis_sock->stream, cmd, sz)) + php_stream_write(redis_sock->stream, cmd, sz)==sz) { // We were able to write it - return 0; + return slot; } - // Fall back by attempting the request against our seeds + // Fall back by attempting the request against every connected node for(zend_hash_internal_pointer_reset(c->nodes); zend_hash_has_more_elements(c->nodes)==SUCCESS; zend_hash_move_forward(c->nodes)) { - zend_hash_get_current_data(c->seeds, (void**)&seed_node); + zend_hash_get_current_data(c->nodes, (void**)&seed_node); + + // TODO: Allow for failure/redirection queries to be sent + // to slave nodes, but for now, stick with masters. + if((*seed_node)->slave) continue; + + CLUSTER_LAZY_CONNECT((*seed_node)->sock); - // Attempt to write our request to this seed node + // Attempt to write our request to this node if(!redis_check_eof((*seed_node)->sock TSRMLS_CC) && - !php_stream_write((*seed_node)->sock->stream, cmd, sz)) + php_stream_write((*seed_node)->sock->stream, cmd, sz)==sz) { - return i; + // Just return the first slot we think this node handles + return (*seed_node)->slots[0].start; } } @@ -802,23 +868,25 @@ static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { // Do we already have the new slot mapped if(c->master[c->redir_slot]) { - // Has this slot changed host or port - if(CLUSTER_REDIR_CMP(c)) { - // Check to see if we have this new node mapped - node = cluster_find_node(c, c->redir_host, c->redir_port); - - if(node) { - // Just point to this slot - c->master[c->redir_slot] = node; - } else { - // Create our node - node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, - c->redir_host_len, c->redir_port, c->redir_slot, - c->redir_slot); + // No need to do anything if it's the same node + if(!CLUSTER_REDIR_CMP(c)) { + return; + } - // Now point our slot at the node - c->master[c->redir_slot] = node; - } + // Check to see if we have this new node mapped + node = cluster_find_node(c, c->redir_host, c->redir_port); + + if(node) { + // Just point to this slot + c->master[c->redir_slot] = node; + } else { + // Create our node + node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, + c->redir_host_len, c->redir_port, c->redir_slot, + c->redir_slot); + + // Now point our slot at the node + c->master[c->redir_slot] = node; } } else { // Check to see if the ip and port are mapped @@ -831,6 +899,10 @@ static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { // Map the slot to this node c->master[c->redir_slot] = node; } + + // Make sure we unflag this node as a slave, as Redis Cluster will only + // ever direct us to master nodes. + node->slave = 0; } /* Send a command to given slot in our cluster. If we get a MOVED @@ -840,13 +912,12 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { REDIS_REPLY_TYPE reply_type; - int resp, reply_len, rslot = slot; + int resp, rslot = slot; // Issue commands until we find the right node or fail do { // Attempt to send the command to the slot requested - if((slot = cluster_sock_write(c, slot, cmd, cmd_len - TSRMLS_CC))==-1) + if((slot = cluster_sock_write(c, slot, cmd, cmd_len TSRMLS_CC))==-1) { // We have no choice but to throw an exception. We // can't communicate with any node at all. @@ -864,14 +935,13 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, // data has been moved, update node configuration, and if ASK has been // encountered, we'll just try again at that slot. if(resp == -1) { - // TODO: More robust error handling, count errors and ultimately - // fail? sleep(1); } else if(resp == 1) { // In case of a MOVED redirection, update our node mapping if(c->redir_type == REDIR_MOVED) { cluster_update_slot(c, rslot); } + slot = c->redir_slot; } } while(resp != 0); @@ -913,9 +983,20 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } } -/* A boolean response, which if we get here, is a success */ +/* A boolean response. If we get here, we've consumed the '+' reply + * type and will now just verify we can read the OK */ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { + char buf[1024]; + size_t line_len; + + // As long as we can consume up to \r\n past '+', we're OK + if(c->err || redis_sock_gets(SLOT_SOCK(c,c->reply_slot), buf, sizeof(buf), + &line_len)==-1) + { + RETURN_FALSE; + } + RETURN_TRUE; } diff --git a/cluster_library.h b/cluster_library.h index e74874eec8..efaf68fad5 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -11,9 +11,10 @@ #define REDIS_CLUSTER_SLOTS 16384 #define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) -/* Nodes we expect for slave or master */ -#define CLUSTER_NODES_MASTER_ELE 9 -#define CLUSTER_NODES_SLAVE_ELE 8 +/* Minimum valid CLUSTER NODES line element count + * and the minimum we expect if there are slots */ +#define CLUSTER_MIN_NODE_LINE 8 +#define CLUSTER_MIN_SLOTS_COUNT 9 /* Length of a cluster name */ #define CLUSTER_NAME_LEN 40 @@ -38,7 +39,7 @@ typedef enum CLUSTER_REDIR_TYPE { /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ - p[5]=='D' && p[6]==' ') + p[4]=='D' && p[5]==' ') #define IS_ASK(p) (p[0]=='A' && p[1]=='S' && p[3]=='K' && p[4]==' ') /* MOVED/ASK lengths */ @@ -56,17 +57,29 @@ typedef enum CLUSTER_REDIR_TYPE { strlen(SLOT_SOCK(c,c->redir_slot)->host) != c->redir_host_len || \ memcmp(SLOT_SOCK(c,c->redir_slot)->host,c->redir_host,c->redir_host_len)) -/* Send a request to our cluster, but take a key and key_len */ -#define CLUSTER_PROCESS_REQUEST_KEY(key, key_len, cmd, cmd_len, func) \ - CLUSTER_PROCESS_REQUEST(cluster_hash_key(key,key_len),cmd,cmd_len,func) - -/* Send a request to the cluster with a key stored as a zval pointer */ -#define CLUSTER_PROCESS_REQUEST_ZVAL(key, cmd, cmd_len, func) \ - CLUSTER_PROCESS_REQUEST(cluster_hash_key_zval(key),cmd,cmd_len,func) +/* Lazy connect logic */ +#define CLUSTER_LAZY_CONNECT(s) \ + if(s->lazy_connect) { \ + s->lazy_connect = 0; \ + redis_sock_server_open(s, 1 TSRMLS_CC); \ + } + +/* Clear out our "last error" */ +#define CLUSTER_CLEAR_ERROR(c) \ + if(c->err) { \ + efree(c->err); \ + c->err = NULL; \ + c->err_len = 0; \ + } /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); +/* Slot range structure */ +typedef struct clusterSlotRange { + unsigned short start, end; +} clusterSlotRange; + /* Bits related to CLUSTER NODES output */ typedef struct clusterNodeInfo { char *name, *master_name; @@ -77,9 +90,11 @@ typedef struct clusterNodeInfo { int host_len; unsigned short port; + + unsigned short slave; - unsigned short start_slot; - unsigned short end_slot; + clusterSlotRange *slots; + size_t slots_size; } clusterNodeInfo; /* A Redis Cluster master node */ @@ -91,9 +106,12 @@ typedef struct redisClusterNode { /* Our Redis socket in question */ RedisSock *sock; - /* Our start and end slots that we serve */ - unsigned short start_slot; - unsigned short end_slot; + /* Contiguous slots we serve */ + clusterSlotRange *slots; + size_t slots_size; + + /* Is this a slave node */ + unsigned short slave; /* A HashTable containing any slaves */ HashTable *slaves; @@ -117,8 +135,8 @@ typedef struct redisCluster { /* All RedisCluster objects we've created/are connected to */ HashTable *nodes; - /* Are we currently in an ERROR state */ - int err_state; + /* How many failures have we had in a row */ + int failures; /* The last ERROR we encountered */ char *err; From c52ef602b97410ee06ef222d405a26b43ecab9f2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 31 May 2014 13:03:39 -0700 Subject: [PATCH 0228/1986] Added EOF as a REDIS_REPLY_TYPE error state This allows us to check that we don't get EOF when getting a single character on the stream, while still pulling into a REDIS_REPLY_TYPE variable. --- common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.h b/common.h index 579611710c..c62d973be0 100644 --- a/common.h +++ b/common.h @@ -30,7 +30,7 @@ /* reply types */ typedef enum _REDIS_REPLY_TYPE { - TYPE_EOF = EOF, + TYPE_EOF = -1, TYPE_LINE = '+', TYPE_INT = ':', TYPE_ERR = '-', From c4121dc728e9f60b5cdb33bb98431bc4d5cd1379 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 31 May 2014 13:05:55 -0700 Subject: [PATCH 0229/1986] Reply type updates Added a default switch element to reply type traversal such that the compiler doesn't warn us about missing a type. Exposed redis_sock_gets externally --- library.c | 10 ++++++---- library.h | 7 +++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/library.c b/library.c index 0f36c98c75..45752987df 100644 --- a/library.c +++ b/library.c @@ -2031,14 +2031,14 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len) { - int ret_len; + int ret_len; char *ret; if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { return 0; } - - ret_len = redis_sock->prefix_len + *key_len; + + ret_len = redis_sock->prefix_len + *key_len; ret = ecalloc(1 + ret_len, 1); memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); memcpy(ret + redis_sock->prefix_len, *key, *key_len); @@ -2197,7 +2197,9 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret add_next_index_zval(*z_ret, z_subelem); redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); break; - default: break; /* We know it's not < 0 from previous check */ + default: + // Stop the compiler from whinging + break; } /* Decrement our element counter */ diff --git a/library.h b/library.h index 4d3c2d0f4d..362e2cd8f9 100644 --- a/library.h +++ b/library.h @@ -14,11 +14,14 @@ int redis_cmd_append_sstr_long(smart_str *str, long append); int redis_cmd_append_int(char **cmd, int cmd_len, int append); int redis_cmd_append_sstr_dbl(smart_str *str, double value); -PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); - +PHP_REDIS_API char* redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx); +PHP_REDIS_API char* redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); +PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len TSRMLS_DC); + typedef void (*SuccessCallback)(RedisSock *redis_sock); + PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); From 435baa9bcebdd740e7bcb5fc55010232b164bcb4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 31 May 2014 13:07:07 -0700 Subject: [PATCH 0230/1986] Command processing macros and fixes Updated our command processing macros as well as changed the allocation method used for our "flags" RedisSock pointer to use ecalloc, rather than emalloc, so we didn't get random segfaults. :) --- redis_cluster.c | 11 ++++++----- redis_cluster.h | 13 ++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index a3461136c3..d8a0bebe89 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -40,6 +40,7 @@ zend_class_entry *spl_rte_ce = NULL; zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, __construct, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, get, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, set, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -92,7 +93,7 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { memset(cluster, 0, sizeof(redisCluster)); // Allocate our RedisSock we'll use to store prefix/serialization flags - cluster->flags = emalloc(sizeof(RedisSock)); + cluster->flags = ecalloc(1, sizeof(RedisSock)); // Allocate our hash table for seeds ALLOC_HASHTABLE(cluster->seeds); @@ -174,24 +175,24 @@ PHP_METHOD(RedisCluster, __construct) { "Invalid timeout", 0 TSRMLS_CC); RETURN_FALSE; } - + // Validate our read timeout if(read_timeout < 0L || read_timeout > INT_MAX) { zend_throw_exception(redis_cluster_exception_ce, "Invalid read timeout", 0 TSRMLS_CC); RETURN_FALSE; } - + // TODO: Implement seed retrieval from php.ini if(!z_seeds || zend_hash_num_elements(Z_ARRVAL_P(z_seeds))==0) { zend_throw_exception(redis_cluster_exception_ce, "Must pass seeds", 0 TSRMLS_CC); RETURN_FALSE; } - + // Initialize our RedisSock "seed" objects cluster_init_seeds(context, Z_ARRVAL_P(z_seeds)); - + // Create and map our key space cluster_map_keyspace(context TSRMLS_CC); } diff --git a/redis_cluster.h b/redis_cluster.h index 7135b736a6..8f8f2e4fe9 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -15,7 +15,8 @@ /* Command building/processing is identical for every command */ #define CLUSTER_BUILD_CMD(name, c, cmd, cmd_len, slot) \ - redis_##name##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, &cmd_len, &slot) + redis_##name##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, \ + &cmd_len, &slot) #define CLUSTER_PROCESS_REQUEST(cmdname, resp_func) \ redisCluster *c = GET_CONTEXT(); \ @@ -23,18 +24,16 @@ int cmd_len; \ short slot; \ if(redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,c->flags, &cmd, \ - &cmd_len, &slot)==FAILURE) \ - { \ + &cmd_len, &slot)==FAILURE) { \ RETURN_FALSE; \ } \ - if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || \ - c->err_state!=0) \ - { \ + if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) {\ efree(cmd); \ RETURN_FALSE; \ } \ + efree(cmd); \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c); - + /* Send a request to our cluster, and process the response */ From a274b5816b33c3a40fb3197f81bd6416648337ef Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 09:21:06 -0700 Subject: [PATCH 0231/1986] Remove MOVED/ASK handling from library.c The generic (non cluster) library functions don't need to understand MOVED or ASK redirection, so that logic has been removed. --- common.h | 13 ----------- library.c | 70 ++++--------------------------------------------------- 2 files changed, 4 insertions(+), 79 deletions(-) diff --git a/common.h b/common.h index c62d973be0..b23da56a45 100644 --- a/common.h +++ b/common.h @@ -53,13 +53,6 @@ typedef enum _PUBSUB_TYPE { PUBSUB_NUMPAT } PUBSUB_TYPE; -/* Cluster redirection type */ -typedef enum _MOVED_TYPE { - MOVED_NONE, - MOVED_MOVED, - MOVED_ASK -} MOVED_TYPE; - /* options */ #define REDIS_OPT_SERIALIZER 1 #define REDIS_OPT_PREFIX 2 @@ -253,12 +246,6 @@ typedef struct { zend_bool lazy_connect; int scan; - - /* Cluster node redirection */ - MOVED_TYPE redir_type; - char *redir_host; - unsigned short redir_port; - unsigned short redir_slot; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index 45752987df..0efa018c52 100644 --- a/library.c +++ b/library.c @@ -320,58 +320,6 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes return reply; } -/** - * Parse MOVED or ASK redirection (Redis Cluster) - * We should get slot host:port - */ -PHPAPI -int redis_sock_redir(RedisSock *redis_sock, const char *msg, int len, - MOVED_TYPE type TSRMLS_DC) -{ - char buf[24], *p1, *p2; - - // Find the space and ':' seperating host/port and do a sanity check on the - // lengths we get back. - if(!(p1 = strchr(msg, ' ')) || !(p2 = strchr(p1,':')) || (len-(p2-msg)>6)) - { - zend_throw_exception(redis_exception_ce, - "Error parsing MOVED/ASK redirection", 0 TSRMLS_CC); - return -1; - } - - // Free previously stored redirection host - if(redis_sock->redir_host) efree(redis_sock->redir_host); - - // Copy and convert slot - strncpy(buf, msg, p1-msg); - buf[p1-msg]='\0'; - redis_sock->redir_slot = (unsigned short)atoi(buf); - - // Make a copy of our host - redis_sock->redir_host = estrndup(p1+1, p2-p1-1); - - // Copy and convert port - strncpy(buf, p2+1, len-(p2-msg)); - buf[len-(p2-msg)+1]='\0'; - redis_sock->redir_port = (unsigned short)atoi(buf); - - // Success - return 0; -} - -/** - * Clear redirection information - */ -PHPAPI void redis_sock_redir_clear(RedisSock *redis_sock) -{ - if(redis_sock->redir_host) { - efree(redis_sock->redir_host); - redis_sock->redir_host = NULL; - } - redis_sock->redir_slot = 0; - redis_sock->redir_port = 0; -} - /** * redis_sock_read */ @@ -395,24 +343,17 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D return NULL; } - // Clear any previous MOVED or ASK redirection - REDIS_MOVED_CLEAR(redis_sock); - switch(inbuf[0]) { case '-': /* Set the last error */ err_len = strlen(inbuf+1) - 2; redis_sock_set_err(redis_sock, inbuf+1, err_len); - /* Handle stale data or MOVED/ASK redirection */ + /* Handle stale data error */ if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { - zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); - } else if(memcmp(inbuf, "-MOVED ", sizeof("-MOVED ")-1)==0) { - redis_sock_redir(redis_sock,inbuf+sizeof("-MOVED "), - err_len-sizeof("-MOVED ")-1, MOVED_MOVED TSRMLS_CC); - } else if(memcmp(inbuf, "-ASK ", sizeof("-ASK ")-1)==0) { - redis_sock_redir(redis_sock,inbuf+sizeof("-ASK "), - err_len-sizeof("-ASK ")-1, MOVED_ASK TSRMLS_CC); + zend_throw_exception(redis_exception_ce, + "SYNC with master in progress", + 0 TSRMLS_CC); } return NULL; case '$': @@ -1890,9 +1831,6 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) if(redis_sock->persistent_id) { efree(redis_sock->persistent_id); } - if(redis_sock->redir_host) { - efree(redis_sock->redir_host); - } efree(redis_sock->host); efree(redis_sock); } From 73ef2b6ccf29c0da752509bcc7ec1e6b15a3bde2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 09:54:10 -0700 Subject: [PATCH 0232/1986] SETEX/PSETEX commands Abstracted SETEX/PSETEX argument parsing and added it to RedisCluster. --- redis.c | 36 +++++++++++------------------------- redis_cluster.c | 7 +++++++ redis_cluster.h | 23 ++++++++++++++--------- redis_commands.c | 32 ++++++++++++++++++++++++++++++++ redis_commands.h | 11 +++++++++-- 5 files changed, 73 insertions(+), 36 deletions(-) diff --git a/redis.c b/redis.c index a4f5e5e1c9..89fbbbce79 100644 --- a/redis.c +++ b/redis.c @@ -955,37 +955,23 @@ PHP_METHOD(Redis, set) { } PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { - - zval *object; RedisSock *redis_sock; - char *key = NULL, *val = NULL, *cmd; - int key_len, val_len, cmd_len; - long expire; - int val_free = 0, key_free = 0; - zval *z_value; + char *cmd; + int cmd_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslz", - &object, redis_ce, &key, &key_len, - &expire, &z_value) == FAILURE) { + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || + redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + keyword, &cmd, &cmd_len, NULL)==FAILURE) + { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, - key_len, expire, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_RESPONSE(redis_string_response); } /* {{{ proto boolean Redis::setex(string key, long expire, string value) diff --git a/redis_cluster.c b/redis_cluster.c index d8a0bebe89..5fcd99d613 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -41,6 +41,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, __construct, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, get, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, set, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setex, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, psetex, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -206,3 +208,8 @@ PHP_METHOD(RedisCluster, get) { PHP_METHOD(RedisCluster, set) { CLUSTER_PROCESS_REQUEST(set, cluster_bool_resp); } + +/* SETEX */ +PHP_METHOD(RedisCluster, setex) { + CLUSTER_PROCESS_KW_REQUEST(redis_gen_setex_cmd, "SETEX", cluster_bool_resp); +} diff --git a/redis_cluster.h b/redis_cluster.h index 8f8f2e4fe9..9a7d6a8ec4 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -18,6 +18,7 @@ redis_##name##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, \ &cmd_len, &slot) +/* Simple 1-1 command -> response macro */ #define CLUSTER_PROCESS_REQUEST(cmdname, resp_func) \ redisCluster *c = GET_CONTEXT(); \ char *cmd; \ @@ -34,18 +35,20 @@ efree(cmd); \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c); - - -/* Send a request to our cluster, and process the response */ -/*#define CLUSTER_PROCESS_REQUEST(c, slot, cmd, cmd_len, resp_func) \ - if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || \ - c->err_state!=0) || \ - { \ +/* More generic processing, where only the keyword differs */ +#define CLUSTER_PROCESS_KW_REQUEST(cmdfunc, kw, resp_func) \ + redisCluster *c = GET_CONTEXT(); \ + char *cmd; int cmd_len; short slot; \ + if(cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,\ + &slot)==FAILURE) { \ + RETURN_FALSE; \ + } \ + if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { \ efree(cmd); \ RETURN_FALSE; \ } \ - efree(cmd); -*/ + efree(cmd); \ + resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c); /* For the creation of RedisCluster specific exceptions */ PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); @@ -64,5 +67,7 @@ void init_rediscluster(TSRMLS_D); PHP_METHOD(RedisCluster, __construct); PHP_METHOD(RedisCluster, get); PHP_METHOD(RedisCluster, set); +PHP_METHOD(RedisCluster, setex); +PHP_METHOD(RedisCluster, psetex); #endif diff --git a/redis_commands.c b/redis_commands.c index 1b47a69a40..be12fcab02 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -126,4 +126,36 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int +redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key = NULL, *val=NULL; + int key_len, val_len, val_free, key_free; + long expire; + zval *z_val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &key, &key_len, + &expire, &z_val)==FAILURE) + { + return FAILURE; + } + + // Serialize value, prefix key + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct our command + *cmd_len = redis_cmd_format_static(cmd, kw, "sls", key, key_len, expire, + val, val_len); + + // Set the slot if directed + CMD_SET_SLOT(slot,key,key_len); + + if(val_free) STR_FREE(val); + if(key_free) efree(key); + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 9735cb46af..92ea18cc00 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -9,9 +9,14 @@ #define CMD_SET_SLOT(slot,key,key_len) \ if(slot) *slot = cluster_hash_key(key,key_len); -/* Redis command construction routines, which always take the form: - * int function(INTERNAL_FUNCTION_PARAMETERS, RedisSock* redis_sock, +/* Redis command construction routines, which generally take the form: + * int function(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * char **cmd, int *cmd_len, short *slot); + * + * OR + * + * int function(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + * char *kw, char **cmd, int *cmd_len, short *slot); * * The functions will return SUCCESS on success, and FAILURE on failure. In * the case of a failure, the cmd pointer will not have been updated, and @@ -27,6 +32,8 @@ int redis_get_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot); +int redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 8d06b5e23e33380619f8db9dba7260ed140e3189 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 09:58:49 -0700 Subject: [PATCH 0233/1986] Finally created a .gitignore file for php odds and ends --- .gitignore | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..10c2636c51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*.deps +*.libs +Makefile* +ac*.m4 +config.* +*.o +install-sh +libtool +*.sh +configure* +*.lo +build* +missing +autom4te.cache +mkinstalldirs +run-tests.php From b6b5f09c1697632e0ae3c28b9c8c605fdf6afe74 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 12:43:03 -0700 Subject: [PATCH 0234/1986] Process and keep the first line in any response. Added a mechanism to our RedisCluster context structure to retain the last reply_type, single line reply, and/or integer response which could either be the length of a BULK/MULTIBULK payload, or an actual integer. This makes command processing functions simpler, as we just have to check that the reply type is correct, and for single line/int responses, the values themselves. --- cluster_library.c | 48 ++++++++++++++++++++++++++--------------------- cluster_library.h | 16 ++++++++++++---- library.c | 4 ++-- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 6461a32193..4a9faf361d 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -747,8 +747,9 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) static int cluster_check_response(redisCluster *c, unsigned short slot, REDIS_REPLY_TYPE *reply_type TSRMLS_DC) { - // Clear out any prior error state + // Clear out any prior error state and our last line response CLUSTER_CLEAR_ERROR(c); + CLUSTER_CLEAR_REPLY(c); if(-1 == redis_check_eof(SLOT_SOCK(c,slot)) || EOF == (*reply_type = php_stream_getc(SLOT_STREAM(c,slot)))) @@ -782,19 +783,16 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, } } - // For BULK, MULTI BULK, or simply INTEGER response typese we can get - // the response length. - if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || - *reply_type == TYPE_MULTIBULK) + // Fetch the first line of our response from Redis. + if(redis_sock_gets(SLOT_SOCK(c,slot),c->line_reply,sizeof(c->line_reply), + &c->reply_len)<0) { - char inbuf[1024]; - - if(php_stream_gets(SLOT_STREAM(c,slot), inbuf, sizeof(inbuf))<0) { - return -1; - } + return -1; + } - // Size information - c->reply_len = atoi(inbuf); + // For replies that will give us a numberic length, convert it + if(*reply_type != TYPE_LINE) { + c->reply_len = atoi(c->line_reply); } // Clear out any previous error, and return that the data is here @@ -911,7 +909,6 @@ static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { - REDIS_REPLY_TYPE reply_type; int resp, rslot = slot; // Issue commands until we find the right node or fail @@ -928,7 +925,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, } // Check the response from the slot we ended up querying. - resp = cluster_check_response(c, slot, &reply_type TSRMLS_CC); + resp = cluster_check_response(c, slot, &c->reply_type TSRMLS_CC); // If we're getting an error condition, impose a slight delay before // we try again (e.g. server went down, election in process). If the @@ -969,7 +966,8 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) char *resp; // Make sure we can read the response - if((resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), + if(c->reply_type != TYPE_BULK || + (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), c->reply_len TSRMLS_CC))==NULL) { RETURN_FALSE; @@ -987,12 +985,9 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) * type and will now just verify we can read the OK */ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { - char buf[1024]; - size_t line_len; - - // As long as we can consume up to \r\n past '+', we're OK - if(c->err || redis_sock_gets(SLOT_SOCK(c,c->reply_slot), buf, sizeof(buf), - &line_len)==-1) + // Check that we have +OK + if(c->reply_type != TYPE_LINE || c->reply_len != 2 || + c->line_reply[0] != 'O' || c->line_reply[1] != 'K') { RETURN_FALSE; } @@ -1000,4 +995,15 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) RETURN_TRUE; } +/* 1 or 0 response, for things like SETNX */ +PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +{ + // Validate our reply type, and check for a zero + if(c->reply_type != TYPE_INT || c->reply_len == 0) { + RETURN_FALSE; + } + + RETURN_TRUE; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index efaf68fad5..fb5332721f 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -72,6 +72,10 @@ typedef enum CLUSTER_REDIR_TYPE { c->err_len = 0; \ } +/* Reset our last single line reply buffer and length */ +#define CLUSTER_CLEAR_REPLY(c) \ + *c->line_reply = '\0'; c->reply_len = 0; + /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); @@ -148,9 +152,13 @@ typedef struct redisCluster { /* One RedisSock* object for serialization and prefix information */ RedisSock *flags; - /* The last reply length we got, which we'll use to parse and - * format our replies to the client. */ - int reply_len; + /* The first line of our last reply, not including our reply type byte + * or the trailing \r\n */ + char line_reply[1024]; + + /* The last reply type and length or integer response we got */ + REDIS_REPLY_TYPE reply_type; + size_t reply_len; /* Last MOVED or ASK redirection response information */ CLUSTER_REDIR_TYPE redir_type; @@ -191,5 +199,5 @@ PHPAPI int cluster_node_add_slave(redisCluster *cluster, PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); - +PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); #endif diff --git a/library.c b/library.c index 0efa018c52..1127876d3e 100644 --- a/library.c +++ b/library.c @@ -262,7 +262,8 @@ PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "read error on connection", + 0 TSRMLS_CC); return NULL; } @@ -379,7 +380,6 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D resp[*buf_len] = 0; return resp; } - default: zend_throw_exception_ex( redis_exception_ce, From 0a69e6d5aef9084e14d552f82d262e17038ffbdc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 12:45:35 -0700 Subject: [PATCH 0235/1986] SETNX/GETSET Implemented SETNX and GETSET in a generic way for both Redis and RedisCluster objects. --- redis.c | 62 ++++++++++++------------------------------ redis_cluster.c | 27 ++++++++++++++++--- redis_cluster.h | 6 +++-- redis_commands.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-- redis_commands.h | 4 +++ 5 files changed, 116 insertions(+), 53 deletions(-) diff --git a/redis.c b/redis.c index 89fbbbce79..4c49c30ca8 100644 --- a/redis.c +++ b/redis.c @@ -992,73 +992,45 @@ PHP_METHOD(Redis, psetex) */ PHP_METHOD(Redis, setnx) { - - zval *object; RedisSock *redis_sock; - char *key = NULL, *val = NULL, *cmd; - int key_len, val_len, cmd_len; - int val_free = 0, key_free = 0; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz", - &object, redis_ce, &key, &key_len, - &z_value) == FAILURE) { - RETURN_FALSE; - } + char *cmd; + int cmd_len; - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0 || + redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "SETNX", + &cmd, &cmd_len, NULL)==FAILURE) + { RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, - key_len, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); } - REDIS_PROCESS_RESPONSE(redis_1_response); - } + /* }}} */ /* {{{ proto string Redis::getSet(string key, string value) */ PHP_METHOD(Redis, getSet) { - - zval *object; RedisSock *redis_sock; - char *key = NULL, *val = NULL, *cmd; - int key_len, val_len, cmd_len; - int val_free = 0, key_free = 0; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz", - &object, redis_ce, &key, &key_len, - &z_value) == FAILURE) { - RETURN_FALSE; - } + char *cmd; + int cmd_len; - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || + redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "GETSET", + &cmd, &cmd_len, NULL)==FAILURE) + { RETURN_FALSE; } - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, - key_len, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_string_response); diff --git a/redis_cluster.c b/redis_cluster.c index 5fcd99d613..1f9933de52 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -43,6 +43,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, set, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, psetex, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setnx, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getset, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -127,7 +129,6 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { /* Free redisCluster context */ void free_cluster_context(void *object TSRMLS_DC) { redisCluster *cluster; - redisClusterNode **node; // Grab context cluster = (redisCluster*)object; @@ -201,15 +202,33 @@ PHP_METHOD(RedisCluster, __construct) { /* GET */ PHP_METHOD(RedisCluster, get) { - CLUSTER_PROCESS_REQUEST(get, cluster_bulk_resp); + CLUSTER_PROCESS_CMD(get, cluster_bulk_resp); } /* SET */ PHP_METHOD(RedisCluster, set) { - CLUSTER_PROCESS_REQUEST(set, cluster_bool_resp); + CLUSTER_PROCESS_CMD(set, cluster_bool_resp); } /* SETEX */ PHP_METHOD(RedisCluster, setex) { - CLUSTER_PROCESS_KW_REQUEST(redis_gen_setex_cmd, "SETEX", cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD(redis_gen_setex_cmd, "SETEX", cluster_bool_resp); } + +/* PSETEX */ +PHP_METHOD(RedisCluster, psetex) { + CLUSTER_PROCESS_KW_CMD(redis_gen_setex_cmd, "PSETEX", + cluster_bool_resp); +} + +/* {{{ proto bool RedisCluster::setnx(string key, string value) */ +PHP_METHOD(RedisCluster, setnx) { + CLUSTER_PROCESS_KW_CMD(redis_gen_kv_cmd, "SETNX", cluster_int_resp); +} + +/* {{{ proto string RedisCluster::getSet(string key, string value) */ +PHP_METHOD(RedisCluster, getset) { + CLUSTER_PROCESS_KW_CMD(redis_gen_kv_cmd, "GETSET", cluster_bulk_resp); +} + +/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 9a7d6a8ec4..f2d7be750f 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -19,7 +19,7 @@ &cmd_len, &slot) /* Simple 1-1 command -> response macro */ -#define CLUSTER_PROCESS_REQUEST(cmdname, resp_func) \ +#define CLUSTER_PROCESS_CMD(cmdname, resp_func) \ redisCluster *c = GET_CONTEXT(); \ char *cmd; \ int cmd_len; \ @@ -36,7 +36,7 @@ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c); /* More generic processing, where only the keyword differs */ -#define CLUSTER_PROCESS_KW_REQUEST(cmdfunc, kw, resp_func) \ +#define CLUSTER_PROCESS_KW_CMD(cmdfunc, kw, resp_func) \ redisCluster *c = GET_CONTEXT(); \ char *cmd; int cmd_len; short slot; \ if(cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,\ @@ -69,5 +69,7 @@ PHP_METHOD(RedisCluster, get); PHP_METHOD(RedisCluster, set); PHP_METHOD(RedisCluster, setex); PHP_METHOD(RedisCluster, psetex); +PHP_METHOD(RedisCluster, setnx); +PHP_METHOD(RedisCluster, getset); #endif diff --git a/redis_commands.c b/redis_commands.c index be12fcab02..a5898decd9 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -77,7 +77,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_hash_move_forward(kt)) { // Grab key and value - type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL); + type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, + NULL); zend_hash_get_current_data(kt, (void**)&v); if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) && @@ -85,7 +86,9 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { exp_type = k; expire = Z_LVAL_PP(v); - } else if(Z_TYPE_PP(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_PP(v))) { + } else if(Z_TYPE_PP(v) == IS_STRING && + IS_NX_XX_ARG(Z_STRVAL_PP(v))) + { set_type = Z_STRVAL_PP(v); } } @@ -126,6 +129,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* SETEX / PSETEX */ int redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) @@ -158,4 +162,66 @@ redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* SETNX */ +int redis_setnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + char *key = NULL, *val = NULL; + int key_len, val_len, key_free, val_free; + zval *z_val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, + &z_val)==FAILURE) + { + return FAILURE; + } + + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + *cmd_len = redis_cmd_format_static(cmd, "SETNX", "ss", key, key_len, val, + val_len); + + // Set slot if directed + CMD_SET_SLOT(slot,key,key_len); + + if(val_free) STR_FREE(val); + if(key_free) efree(key); + + return SUCCESS; +} + +/* Generic command construction when we just take a key and value */ +int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key, *val; + int key_len, val_len, key_free, val_free; + zval *z_val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, + &z_val)==FAILURE) + { + return FAILURE; + } + + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // If the key is empty, we can abort + if(key_len == 0) { + if(key_free) efree(key); + if(val_free) STR_FREE(val); + return FAILURE; + } + + // Construct our command + *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, + val_len); + + // Set our slot if directed + CMD_SET_SLOT(slot,key,key_len); + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 92ea18cc00..01207e041c 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -34,6 +34,10 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); + +int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 09c8fae668b3b254c057bde439692eb70c489279 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 12:48:15 -0700 Subject: [PATCH 0236/1986] Redis seems to allow an empty key name, so phpredis should too --- redis_commands.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index a5898decd9..2ff6146828 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -207,13 +207,6 @@ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - // If the key is empty, we can abort - if(key_len == 0) { - if(key_free) efree(key); - if(val_free) STR_FREE(val); - return FAILURE; - } - // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, val_len); From f85481fae0511a369cbefee43e14f1470a3b336d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 13:09:28 -0700 Subject: [PATCH 0237/1986] Remove redundant command, re deny empty keys SETNX didn't need it's own command construction function as it can be handled by our generic key and value command. On second thought, don't allow setting an empty ("") key. Redis does seem to allow this so we might change it, but I don't see why allowing an empty key makes sense. --- redis_commands.c | 38 ++++++++++---------------------------- redis_commands.h | 3 +++ 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 2ff6146828..e6c22c2297 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -162,34 +162,6 @@ redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -/* SETNX */ -int redis_setnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) -{ - char *key = NULL, *val = NULL; - int key_len, val_len, key_free, val_free; - zval *z_val; - - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, - &z_val)==FAILURE) - { - return FAILURE; - } - - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - *cmd_len = redis_cmd_format_static(cmd, "SETNX", "ss", key, key_len, val, - val_len); - - // Set slot if directed - CMD_SET_SLOT(slot,key,key_len); - - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - return SUCCESS; -} - /* Generic command construction when we just take a key and value */ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) @@ -207,6 +179,13 @@ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); + // Don't allow setting an empty key + if(key_len == 0) { + if(val_free) STR_FREE(val); + if(key_free) efree(key); + return FAILURE; + } + // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, val_len); @@ -214,6 +193,9 @@ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set our slot if directed CMD_SET_SLOT(slot,key,key_len); + if(val_free) STR_FREE(val); + if(key_free) efree(key); + return SUCCESS; } diff --git a/redis_commands.h b/redis_commands.h index 01207e041c..b8fd0f7877 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -38,6 +38,9 @@ int redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); +int redis_gen_ss_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 32b55e20af04a8a75433f9c24f63033922756e6c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 14:17:41 -0700 Subject: [PATCH 0238/1986] Created a generic "one key" command builder --- redis_commands.c | 55 +++++++++++++++++++++++++++++------------------- redis_commands.h | 6 +++--- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index e6c22c2297..20b00bdc7b 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -20,28 +20,6 @@ #include "redis_commands.h" -/* GET */ -int redis_get_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) -{ - char *key=NULL; - int key_len, key_free; - - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, - &key_len)==FAILURE) - { - return FAILURE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - *cmd_len = redis_cmd_format_static(cmd, "GET", "s", key, key_len); - - CMD_SET_SLOT(slot, key, key_len); - if(key_free) efree(key); - - return SUCCESS; -} - /* SET */ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot) @@ -199,4 +177,37 @@ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic command where we take a single key */ +int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key; + int key_len, key_free; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) + ==FAILURE) + { + return FAILURE; + } + + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Don't allow an empty key + if(key_len == 0) { + if(key_free) efree(key); + return FAILURE; + } + + // Construct our command + *cmd_len = redis_cmd_format_static(cmd, kw, "s", key, key_len); + + // Set slot if directed + CMD_SET_SLOT(slot,key,key_len); + + if(key_free) efree(key); + + return SUCCESS; +} + + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index b8fd0f7877..50a8f55e5b 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -26,9 +26,6 @@ * Cluster hash slot where the key(s) belong. */ -int redis_get_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot); - int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot); @@ -38,6 +35,9 @@ int redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); +int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); + int redis_gen_ss_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); From ebe50806f6c914f1e36e4dc5f8c4f32720321de7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 14:17:57 -0700 Subject: [PATCH 0239/1986] Created generic macro to process Redis requests Duplicated the CLUSTER_PROCESS_CMD type macros for the Redis object proper, as we don't need to duplicate so much boilerplate code for every method. --- common.h | 43 ++++++++++++++++--- redis.c | 126 ++++--------------------------------------------------- 2 files changed, 45 insertions(+), 124 deletions(-) diff --git a/common.h b/common.h index b23da56a45..40cf0d45fa 100644 --- a/common.h +++ b/common.h @@ -182,13 +182,46 @@ else if(redis_sock->mode == MULTI) { \ redis_sock->redir_slot = 0; \ redis_sock->redir_port = 0; \ redis_sock->redir_type = MOVED_NONE; \ - + +/* Process a command assuming our command where our command building + * function is redis__cmd */ +#define REDIS_PROCESS_CMD(cmdname, resp_func) \ + RedisSock *redis_sock; char *cmd; int cmd_len; \ + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || \ + redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, \ + &cmd, &cmd_len, NULL)==FAILURE) { \ + RETURN_FALSE; \ + } \ + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ + IF_ATOMIC() { \ + resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); \ + } \ + REDIS_PROCESS_RESPONSE(resp_func); + +/* Process a command but with a specific command building function + * and keyword which is passed to us*/ +#define REDIS_PROCESS_KW_CMD(cmdfunc, kw, resp_func) \ + RedisSock *redis_sock; char *cmd; int cmd_len; \ + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || \ + cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, \ + &cmd_len, NULL)==FAILURE) { \ + RETURN_FALSE; \ + } \ + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ + IF_ATOMIC() { \ + resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); \ + } \ + REDIS_PROCESS_RESPONSE(resp_func); /* Extended SET argument detection */ -#define IS_EX_ARG(a) ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_PX_ARG(a) ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_NX_ARG(a) ((a[0]=='n' || a[0]=='N') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_XX_ARG(a) ((a[0]=='x' || a[0]=='X') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') +#define IS_EX_ARG(a) \ + ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') +#define IS_PX_ARG(a) \ + ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') +#define IS_NX_ARG(a) \ + ((a[0]=='n' || a[0]=='N') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') +#define IS_XX_ARG(a) \ + ((a[0]=='x' || a[0]=='X') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') #define IS_EX_PX_ARG(a) (IS_EX_ARG(a) || IS_PX_ARG(a)) #define IS_NX_XX_ARG(a) (IS_NX_ARG(a) || IS_XX_ARG(a)) diff --git a/redis.c b/redis.c index 4c49c30ca8..1babce2a4e 100644 --- a/redis.c +++ b/redis.c @@ -934,81 +934,28 @@ PHP_METHOD(Redis, close) /* {{{ proto boolean Redis::set(string key, mixed value, long timeout | array options) */ PHP_METHOD(Redis, set) { - RedisSock *redis_sock; - char *cmd; - int cmd_len; - - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || - redis_set_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &cmd, - &cmd_len, NULL)==FAILURE) - { - RETURN_FALSE; - } - - /* Kick off the command */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); -} - -PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { - RedisSock *redis_sock; - char *cmd; - int cmd_len; - - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || - redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - keyword, &cmd, &cmd_len, NULL)==FAILURE) - { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); + REDIS_PROCESS_CMD(set, redis_boolean_response); } /* {{{ proto boolean Redis::setex(string key, long expire, string value) */ PHP_METHOD(Redis, setex) { - redis_generic_setex(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SETEX"); + REDIS_PROCESS_KW_CMD(redis_gen_setex_cmd, "SETEX", redis_string_response); } /* {{{ proto boolean Redis::psetex(string key, long expire, string value) */ PHP_METHOD(Redis, psetex) { - redis_generic_setex(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PSETEX"); + REDIS_PROCESS_KW_CMD(redis_gen_setex_cmd, "PSETEX", redis_string_response); } /* {{{ proto boolean Redis::setnx(string key, string value) */ PHP_METHOD(Redis, setnx) { - RedisSock *redis_sock; - char *cmd; - int cmd_len; - - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0 || - redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "SETNX", - &cmd, &cmd_len, NULL)==FAILURE) - { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); + REDIS_PROCESS_KW_CMD(redis_gen_kv_cmd, "SETNX", redis_1_response); } /* }}} */ @@ -1016,24 +963,7 @@ PHP_METHOD(Redis, setnx) */ PHP_METHOD(Redis, getSet) { - RedisSock *redis_sock; - char *cmd; - int cmd_len; - - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || - redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "GETSET", - &cmd, &cmd_len, NULL)==FAILURE) - { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); - + REDIS_PROCESS_KW_CMD(redis_gen_kv_cmd, "GETSET", redis_string_response); } /* }}} */ @@ -1180,24 +1110,7 @@ PHP_METHOD(Redis, renameNx) */ PHP_METHOD(Redis, get) { - RedisSock *redis_sock; - char *cmd; - int cmd_len; - - // Grab our socket and parse arguments/build command - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || - redis_get_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &cmd, - &cmd_len, NULL)==FAILURE) - { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); + REDIS_PROCESS_KW_CMD(redis_gen_key_cmd, "GET", redis_string_response); } /* }}} */ @@ -1475,32 +1388,7 @@ PHP_METHOD(Redis, getMultiple) */ PHP_METHOD(Redis, exists) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len; - int key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "EXISTS", "s", key, - key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); - + REDIS_PROCESS_KW_CMD(redis_gen_key_cmd, "EXISTS", redis_1_response); } /* }}} */ From b84366c90643a3898e454f088a07502c51415d75 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 14:19:06 -0700 Subject: [PATCH 0240/1986] Added integer response handler for cluster --- cluster_library.c | 11 ++++++++++- cluster_library.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 4a9faf361d..4c7a59c683 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -996,7 +996,7 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } /* 1 or 0 response, for things like SETNX */ -PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { // Validate our reply type, and check for a zero if(c->reply_type != TYPE_INT || c->reply_len == 0) { @@ -1006,4 +1006,13 @@ PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) RETURN_TRUE; } +/* Generic integer response */ +PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +{ + if(c->reply_type != TYPE_INT) { + RETURN_FALSE; + } + RETURN_TRUE; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index fb5332721f..f21d0d4c24 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -199,5 +199,6 @@ PHPAPI int cluster_node_add_slave(redisCluster *cluster, PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); #endif From d39e210f35034a6865279f5cc9d58ec4c061d786 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 14:19:30 -0700 Subject: [PATCH 0241/1986] Added the EXISTS command and some line length reformatting 80 chars is the future. :) --- redis_cluster.c | 47 ++++++++++++++++++++++++++++++++++------------- redis_cluster.h | 1 + 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 1f9933de52..ef7e3d2f77 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -45,6 +45,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, psetex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setnx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getset, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, exists, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -162,14 +163,19 @@ PHP_METHOD(RedisCluster, __construct) { redisCluster *context = GET_CONTEXT(); // Parse arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|add", - &object, redis_cluster_ce, &name, &name_len, - &z_seeds, &timeout, &read_timeout)==FAILURE) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|add", &object, redis_cluster_ce, &name, + &name_len, &z_seeds, &timeout, + &read_timeout)==FAILURE) { + RETURN_FALSE; + } + + // Require a name + if(name_len == 0) { zend_throw_exception(redis_cluster_exception_ce, - "To instance a RedisCluster object, you must at least pass in a name", + "You must give this cluster a name!", 0 TSRMLS_CC); - RETURN_FALSE; } // Validate timeout @@ -200,35 +206,50 @@ PHP_METHOD(RedisCluster, __construct) { cluster_map_keyspace(context TSRMLS_CC); } -/* GET */ +/* + * RedisCluster methods + */ + +/* {{{ proto string RedisCluster::get(string key) */ PHP_METHOD(RedisCluster, get) { - CLUSTER_PROCESS_CMD(get, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD(redis_gen_key_cmd, "GET", cluster_bulk_resp); } +/* }}} */ -/* SET */ +/* {{{ proto bool RedisCluster::set(string key, string value) */ PHP_METHOD(RedisCluster, set) { CLUSTER_PROCESS_CMD(set, cluster_bool_resp); } +/* }}} */ -/* SETEX */ +/* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { CLUSTER_PROCESS_KW_CMD(redis_gen_setex_cmd, "SETEX", cluster_bool_resp); } +/* }}} */ -/* PSETEX */ +/* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, psetex) { - CLUSTER_PROCESS_KW_CMD(redis_gen_setex_cmd, "PSETEX", - cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD(redis_gen_setex_cmd, "PSETEX", cluster_bool_resp); } +/* }}} */ /* {{{ proto bool RedisCluster::setnx(string key, string value) */ PHP_METHOD(RedisCluster, setnx) { - CLUSTER_PROCESS_KW_CMD(redis_gen_kv_cmd, "SETNX", cluster_int_resp); + CLUSTER_PROCESS_KW_CMD(redis_gen_kv_cmd, "SETNX", cluster_1_resp); } +/* }}} */ /* {{{ proto string RedisCluster::getSet(string key, string value) */ PHP_METHOD(RedisCluster, getset) { CLUSTER_PROCESS_KW_CMD(redis_gen_kv_cmd, "GETSET", cluster_bulk_resp); } +/* }}} */ + +/* {{{ proto int RedisCluster::exists(string key) */ +PHP_METHOD(RedisCluster, exists) { + CLUSTER_PROCESS_KW_CMD(redis_gen_key_cmd, "EXISTS", cluster_int_resp); +} +/* }}} */ /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index f2d7be750f..b51177459c 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -71,5 +71,6 @@ PHP_METHOD(RedisCluster, setex); PHP_METHOD(RedisCluster, psetex); PHP_METHOD(RedisCluster, setnx); PHP_METHOD(RedisCluster, getset); +PHP_METHOD(RedisCluster, exists); #endif From 18f84efb93b501a9ae45abf494dfc9260c288051 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 15:17:18 -0700 Subject: [PATCH 0242/1986] Switched macro ordering and added mbulk processing for cluster --- cluster_library.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ cluster_library.h | 6 ++++++ common.h | 2 +- redis.c | 41 ++++++++------------------------------- redis_cluster.c | 18 +++++++++++------ redis_cluster.h | 3 ++- 6 files changed, 78 insertions(+), 41 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 4c7a59c683..c7f4e5fe0b 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1015,4 +1015,53 @@ PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) RETURN_TRUE; } +/* Raw multibulk reply response */ +PHPAPI void cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c) +{ + zval *z_result; + + // Verify reply type + if(c->reply_type != TYPE_MULTIBULK) { + RETURN_FALSE; + } + + // Allocate our array + MAKE_STD_ZVAL(z_result); + array_init(z_result); + + if(cluster_mbulk_resp_loop_raw(SLOT_SOCK(c,c->reply_slot), z_result, + c->reply_len TSRMLS_CC)==FAILURE) + { + zval_dtor(z_result); + FREE_ZVAL(z_result); + RETURN_FALSE; + } + + // Return our array + *return_value = *z_result; + efree(z_result); +} + +/* Raw multibulk reply loop */ +PHPAPI int cluster_mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, + size_t count TSRMLS_DC) +{ + char *line; + int line_len; + + // Iterate over the number we have + while(count--) { + // Read the line, which should never come back null + line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + if(line == NULL) return FAILURE; + + // Add to our result array + add_next_index_stringl(z_result, line, line_len, 0); + } + + // Success! + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index f21d0d4c24..daec2fc5be 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -201,4 +201,10 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); + +/* Raw (untouched) multi bulk processing */ +PHPAPI void cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c); +PHPAPI int cluster_mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, + size_t count TSRMLS_DC); #endif diff --git a/common.h b/common.h index 40cf0d45fa..3e8385547d 100644 --- a/common.h +++ b/common.h @@ -200,7 +200,7 @@ else if(redis_sock->mode == MULTI) { \ /* Process a command but with a specific command building function * and keyword which is passed to us*/ -#define REDIS_PROCESS_KW_CMD(cmdfunc, kw, resp_func) \ +#define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ RedisSock *redis_sock; char *cmd; int cmd_len; \ if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || \ cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, \ diff --git a/redis.c b/redis.c index 1babce2a4e..e095396012 100644 --- a/redis.c +++ b/redis.c @@ -941,21 +941,21 @@ PHP_METHOD(Redis, set) { */ PHP_METHOD(Redis, setex) { - REDIS_PROCESS_KW_CMD(redis_gen_setex_cmd, "SETEX", redis_string_response); + REDIS_PROCESS_KW_CMD("SETEX", redis_gen_setex_cmd, redis_string_response); } /* {{{ proto boolean Redis::psetex(string key, long expire, string value) */ PHP_METHOD(Redis, psetex) { - REDIS_PROCESS_KW_CMD(redis_gen_setex_cmd, "PSETEX", redis_string_response); + REDIS_PROCESS_KW_CMD("PSETEX", redis_gen_setex_cmd, redis_string_response); } /* {{{ proto boolean Redis::setnx(string key, string value) */ PHP_METHOD(Redis, setnx) { - REDIS_PROCESS_KW_CMD(redis_gen_kv_cmd, "SETNX", redis_1_response); + REDIS_PROCESS_KW_CMD("SETNX", redis_gen_kv_cmd, redis_1_response); } /* }}} */ @@ -963,7 +963,7 @@ PHP_METHOD(Redis, setnx) */ PHP_METHOD(Redis, getSet) { - REDIS_PROCESS_KW_CMD(redis_gen_kv_cmd, "GETSET", redis_string_response); + REDIS_PROCESS_KW_CMD("GETSET", redis_gen_kv_cmd, redis_string_response); } /* }}} */ @@ -1110,7 +1110,7 @@ PHP_METHOD(Redis, renameNx) */ PHP_METHOD(Redis, get) { - REDIS_PROCESS_KW_CMD(redis_gen_key_cmd, "GET", redis_string_response); + REDIS_PROCESS_KW_CMD("GET", redis_gen_key_cmd, redis_string_response); } /* }}} */ @@ -1388,7 +1388,7 @@ PHP_METHOD(Redis, getMultiple) */ PHP_METHOD(Redis, exists) { - REDIS_PROCESS_KW_CMD(redis_gen_key_cmd, "EXISTS", redis_1_response); + REDIS_PROCESS_KW_CMD("EXISTS", redis_gen_key_cmd, redis_1_response); } /* }}} */ @@ -1463,33 +1463,8 @@ PHP_METHOD(Redis, unwatch) */ PHP_METHOD(Redis, getKeys) { - zval *object; - RedisSock *redis_sock; - char *pattern = NULL, *cmd; - int pattern_len, cmd_len, pattern_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &pattern, &pattern_len) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len); - cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pattern, - pattern_len); - if(pattern_free) efree(pattern); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if (redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw); + REDIS_PROCESS_KW_CMD("KEYS", redis_gen_key_cmd, + redis_sock_read_multibulk_reply_raw); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index ef7e3d2f77..8533ee4542 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -46,6 +46,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, setnx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exists, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, keys, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -212,7 +213,7 @@ PHP_METHOD(RedisCluster, __construct) { /* {{{ proto string RedisCluster::get(string key) */ PHP_METHOD(RedisCluster, get) { - CLUSTER_PROCESS_KW_CMD(redis_gen_key_cmd, "GET", cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("GET", redis_gen_key_cmd, cluster_bulk_resp); } /* }}} */ @@ -224,32 +225,37 @@ PHP_METHOD(RedisCluster, set) { /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { - CLUSTER_PROCESS_KW_CMD(redis_gen_setex_cmd, "SETEX", cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("SETEX", redis_gen_setex_cmd, cluster_bool_resp); } /* }}} */ /* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, psetex) { - CLUSTER_PROCESS_KW_CMD(redis_gen_setex_cmd, "PSETEX", cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("PSETEX", redis_gen_setex_cmd, cluster_bool_resp); } /* }}} */ /* {{{ proto bool RedisCluster::setnx(string key, string value) */ PHP_METHOD(RedisCluster, setnx) { - CLUSTER_PROCESS_KW_CMD(redis_gen_kv_cmd, "SETNX", cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("SETNX", redis_gen_kv_cmd, cluster_1_resp); } /* }}} */ /* {{{ proto string RedisCluster::getSet(string key, string value) */ PHP_METHOD(RedisCluster, getset) { - CLUSTER_PROCESS_KW_CMD(redis_gen_kv_cmd, "GETSET", cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("GETSET", redis_gen_kv_cmd, cluster_bulk_resp); } /* }}} */ /* {{{ proto int RedisCluster::exists(string key) */ PHP_METHOD(RedisCluster, exists) { - CLUSTER_PROCESS_KW_CMD(redis_gen_key_cmd, "EXISTS", cluster_int_resp); + CLUSTER_PROCESS_KW_CMD("EXISTS", redis_gen_key_cmd, cluster_int_resp); } /* }}} */ +/* {{{ proto array Redis::keys(string pattern) */ +PHP_METHOD(RedisCluster, keys) { + CLUSTER_PROCESS_KW_CMD("KEYS", redis_gen_key_cmd, cluster_multibulk_resp); +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index b51177459c..39e366589b 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -36,7 +36,7 @@ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c); /* More generic processing, where only the keyword differs */ -#define CLUSTER_PROCESS_KW_CMD(cmdfunc, kw, resp_func) \ +#define CLUSTER_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ redisCluster *c = GET_CONTEXT(); \ char *cmd; int cmd_len; short slot; \ if(cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,\ @@ -72,5 +72,6 @@ PHP_METHOD(RedisCluster, psetex); PHP_METHOD(RedisCluster, setnx); PHP_METHOD(RedisCluster, getset); PHP_METHOD(RedisCluster, exists); +PHP_METHOD(RedisCluster, keys); #endif From 1c1a7de1b4f6169a68895265c6ff95fdd74300dc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 15:35:44 -0700 Subject: [PATCH 0243/1986] Disable KEYS command, and add TYPE We need to figure out how the KEYS command should work in the context of cluster. We could either send it to all nodes, or send it to just one slot. Added TYPE command and cluster response processor --- cluster_library.c | 22 ++++++++++++++++++++++ cluster_library.h | 1 + redis.c | 25 +------------------------ redis_cluster.c | 12 +++++++++++- redis_cluster.h | 1 + 5 files changed, 36 insertions(+), 25 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index c7f4e5fe0b..89aa35f5c8 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1015,6 +1015,28 @@ PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) RETURN_TRUE; } +/* TYPE response handler */ +PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +{ + // Make sure we got the right kind of response + if(c->reply_type != TYPE_LINE) { + RETURN_FALSE; + } + + // Switch on the type + if(strncmp(c->line_reply, "+string", 7)==0) { + RETURN_LONG(REDIS_STRING); + } else if(strncmp(c->line_reply, "+set", 4)==0) { + RETURN_LONG(REDIS_SET); + } else if(strncmp(c->line_reply, "+list", 5)==0) { + RETURN_LONG(REDIS_LIST); + } else if(strncmp(c->line_reply, "+hash", 5)==0) { + RETURN_LONG(REDIS_HASH); + } else { + RETURN_LONG(REDIS_NOT_FOUND); + } +} + /* Raw multibulk reply response */ PHPAPI void cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) diff --git a/cluster_library.h b/cluster_library.h index daec2fc5be..9cf23d45cc 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -201,6 +201,7 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); /* Raw (untouched) multi bulk processing */ PHPAPI void cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, diff --git a/redis.c b/redis.c index e095396012..1da6e05db0 100644 --- a/redis.c +++ b/redis.c @@ -1472,30 +1472,7 @@ PHP_METHOD(Redis, getKeys) */ PHP_METHOD(Redis, type) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "TYPE", "s", key, key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_type_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_type_response); + REDIS_PROCESS_KW_CMD("TYPE", redis_gen_key_cmd, redis_type_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 8533ee4542..1d83992d7b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -47,6 +47,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, getset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, keys, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, type, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -255,7 +256,16 @@ PHP_METHOD(RedisCluster, exists) { /* {{{ proto array Redis::keys(string pattern) */ PHP_METHOD(RedisCluster, keys) { - CLUSTER_PROCESS_KW_CMD("KEYS", redis_gen_key_cmd, cluster_multibulk_resp); + // TODO: Figure out how to implement this, as we may want to send it across + // all nodes (although that seems dangerous), or ask for a specified slot. + //CLUSTER_PROCESS_KW_CMD("KEYS", redis_gen_key_cmd, cluster_multibulk_resp); + zend_throw_exception(redis_cluster_exception_ce, + "KEYS command not implemented", 0 TSRMLS_CC); +} + +/* {{{ proto int RedisCluster::type(string key) */ +PHP_METHOD(RedisCluster, type) { + CLUSTER_PROCESS_KW_CMD("TYPE", redis_gen_key_cmd, cluster_type_resp); } /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 39e366589b..812d2006e5 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -73,5 +73,6 @@ PHP_METHOD(RedisCluster, setnx); PHP_METHOD(RedisCluster, getset); PHP_METHOD(RedisCluster, exists); PHP_METHOD(RedisCluster, keys); +PHP_METHOD(RedisCluster, type); #endif From 50ad87b15d1db2a213f6a40d8b1a424b68c846f7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 20:42:39 -0700 Subject: [PATCH 0244/1986] LPOP, RPOP, SPOP Implemented LPOP, RPOP, and SPOP commands. Some formating fixes in library.c --- library.c | 12 ++++++------ redis.c | 7 +++---- redis_cluster.c | 24 ++++++++++++++++++++++++ redis_cluster.h | 4 ++++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/library.c b/library.c index 1127876d3e..0cc537eea6 100644 --- a/library.c +++ b/library.c @@ -725,17 +725,17 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } if (strncmp(response, "+string", 7) == 0) { - l = REDIS_STRING; + l = REDIS_STRING; } else if (strncmp(response, "+set", 4) == 0){ - l = REDIS_SET; + l = REDIS_SET; } else if (strncmp(response, "+list", 5) == 0){ - l = REDIS_LIST; + l = REDIS_LIST; } else if (strncmp(response, "+zset", 5) == 0){ - l = REDIS_ZSET; + l = REDIS_ZSET; } else if (strncmp(response, "+hash", 5) == 0){ - l = REDIS_HASH; + l = REDIS_HASH; } else { - l = REDIS_NOT_FOUND; + l = REDIS_NOT_FOUND; } efree(response); diff --git a/redis.c b/redis.c index 1da6e05db0..90982fced0 100644 --- a/redis.c +++ b/redis.c @@ -1823,7 +1823,7 @@ generic_pop_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_le */ PHP_METHOD(Redis, lPop) { - generic_pop_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LPOP", sizeof("LPOP")-1); + REDIS_PROCESS_KW_CMD("LPOP", redis_gen_key_cmd, redis_string_response); } /* }}} */ @@ -1831,7 +1831,7 @@ PHP_METHOD(Redis, lPop) */ PHP_METHOD(Redis, rPop) { - generic_pop_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RPOP", sizeof("RPOP")-1); + REDIS_PROCESS_KW_CMD("RPOP", redis_gen_key_cmd, redis_string_response); } /* }}} */ @@ -2166,11 +2166,10 @@ PHP_METHOD(Redis, sMove) */ PHP_METHOD(Redis, sPop) { - generic_pop_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SPOP", 4); + REDIS_PROCESS_KW_CMD("SPOP", redis_gen_key_cmd, redis_string_response); } /* }}} */ -/* }}} */ /* {{{ proto string Redis::sRandMember(string key [int count]) */ PHP_METHOD(Redis, sRandMember) diff --git a/redis_cluster.c b/redis_cluster.c index 1d83992d7b..a87e95fcfb 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -48,6 +48,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, exists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, keys, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, type, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lpop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -262,10 +265,31 @@ PHP_METHOD(RedisCluster, keys) { zend_throw_exception(redis_cluster_exception_ce, "KEYS command not implemented", 0 TSRMLS_CC); } +/* }}} */ /* {{{ proto int RedisCluster::type(string key) */ PHP_METHOD(RedisCluster, type) { CLUSTER_PROCESS_KW_CMD("TYPE", redis_gen_key_cmd, cluster_type_resp); } +/* }}} */ + +/* {{{ proto string RedisCluster::pop(string key) */ +PHP_METHOD(RedisCluster, lpop) { + CLUSTER_PROCESS_KW_CMD("LPOP", redis_gen_key_cmd, cluster_bulk_resp); +} +/* }}} */ + +/* {{{ proto string RedisCluster::rpop(string key) */ +PHP_METHOD(RedisCluster, rpop) { + CLUSTER_PROCESS_KW_CMD("RPOP", redis_gen_key_cmd, cluster_bulk_resp); +} +/* }}} */ + +/* {{{ proto string RedisCluster::spop(string key) */ +PHP_METHOD(RedisCluster, spop) { + CLUSTER_PROCESS_KW_CMD("SPOP", redis_gen_key_cmd, cluster_bulk_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 812d2006e5..3c0271fea8 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -75,4 +75,8 @@ PHP_METHOD(RedisCluster, exists); PHP_METHOD(RedisCluster, keys); PHP_METHOD(RedisCluster, type); +PHP_METHOD(RedisCluster, lpop); +PHP_METHOD(RedisCluster, rpop); +PHP_METHOD(RedisCluster, spop); + #endif From 2ba20e6f774a8cef744aa28efbcb4851c4bcb2d1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 20:48:03 -0700 Subject: [PATCH 0245/1986] Implemented STRLEN command, and removed depreciated function --- redis.c | 59 +++---------------------------------------------- redis_cluster.c | 5 +++++ redis_cluster.h | 1 + 3 files changed, 9 insertions(+), 56 deletions(-) diff --git a/redis.c b/redis.c index 90982fced0..a48a04fc52 100644 --- a/redis.c +++ b/redis.c @@ -1633,35 +1633,12 @@ PHP_METHOD(Redis, setBit) REDIS_PROCESS_RESPONSE(redis_long_response); } - +/* {{{ proto long Redis::strlen(string key) */ PHP_METHOD(Redis, strlen) { - zval *object; - RedisSock *redis_sock; - char *cmd; - int cmd_len, key_len, key_free; - char *key; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "STRLEN", "s", key, key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("STRLEN", redis_gen_key_cmd, redis_long_response); } +/* }}} */ PHP_REDIS_API void generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) { @@ -1789,36 +1766,6 @@ PHP_METHOD(Redis, rPushx) generic_push_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RPUSHX", sizeof("RPUSHX")-1); } -PHP_REDIS_API void -generic_pop_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) { - - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, - key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); -} - /* {{{ proto string Redis::lPOP(string key) */ PHP_METHOD(Redis, lPop) diff --git a/redis_cluster.c b/redis_cluster.c index a87e95fcfb..288f75839f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -51,6 +51,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, lpop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -291,5 +292,9 @@ PHP_METHOD(RedisCluster, spop) { } /* }}} */ +/* {{{ proto string RedisCluster::strlen(string key) */ +PHP_METHOD(RedisCluster, strlen) { + CLUSTER_PROCESS_KW_CMD("STRLEN", redis_gen_key_cmd, cluster_bulk_resp); +} /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 3c0271fea8..b4dc3516ea 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -79,4 +79,5 @@ PHP_METHOD(RedisCluster, lpop); PHP_METHOD(RedisCluster, rpop); PHP_METHOD(RedisCluster, spop); +PHP_METHOD(RedisCluster, strlen); #endif From 3cdc11d90bef19a7c8d22fc9c81f570261944097 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 20:54:52 -0700 Subject: [PATCH 0246/1986] LPUSHX/RPUSHX Implemented LPUSHX and RPUSHX in our new generic way for both Redis and RedisCluster classes. --- cluster_library.c | 2 +- cluster_library.h | 2 +- redis.c | 44 ++++++++------------------------------------ redis_cluster.c | 16 +++++++++++++++- redis_cluster.h | 3 ++- 5 files changed, 27 insertions(+), 40 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 89aa35f5c8..c7eb4334bc 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1007,7 +1007,7 @@ PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } /* Generic integer response */ -PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { if(c->reply_type != TYPE_INT) { RETURN_FALSE; diff --git a/cluster_library.h b/cluster_library.h index 9cf23d45cc..f29d8241e7 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -200,7 +200,7 @@ PHPAPI int cluster_node_add_slave(redisCluster *cluster, PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -PHPAPI void cluster_int_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); /* Raw (untouched) multi bulk processing */ diff --git a/redis.c b/redis.c index a48a04fc52..24f5997945 100644 --- a/redis.c +++ b/redis.c @@ -1640,39 +1640,6 @@ PHP_METHOD(Redis, strlen) } /* }}} */ -PHP_REDIS_API void -generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) { - zval *object; - RedisSock *redis_sock; - char *cmd, *key, *val; - int cmd_len, key_len, val_len; - zval *z_value; - int val_free, key_free = 0; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz", - &object, redis_ce, - &key, &key_len, &z_value) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, - key_len, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); -} - /* {{{ proto boolean Redis::lPush(string key , string value) */ PHP_METHOD(Redis, lPush) @@ -1756,16 +1723,21 @@ PHP_METHOD(Redis, lInsert) } +/* {{{ proto long Redis::lPushx(string key, mixed value) */ PHP_METHOD(Redis, lPushx) -{ - generic_push_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LPUSHX", sizeof("LPUSHX")-1); +{ + REDIS_PROCESS_KW_CMD("LPUSHX", redis_gen_kv_cmd, redis_string_response); } +/* }}} */ +/* {{{ proto long Redis::rPushx(string key, mixed value) */ PHP_METHOD(Redis, rPushx) { - generic_push_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RPUSHX", sizeof("RPUSHX")-1); + REDIS_PROCESS_KW_CMD("RPUSHX", redis_gen_kv_cmd, redis_string_response); } +/* }}} */ + /* {{{ proto string Redis::lPOP(string key) */ PHP_METHOD(Redis, lPop) diff --git a/redis_cluster.c b/redis_cluster.c index 288f75839f..0b0d1e8747 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -51,6 +51,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, lpop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -254,7 +256,7 @@ PHP_METHOD(RedisCluster, getset) { /* {{{ proto int RedisCluster::exists(string key) */ PHP_METHOD(RedisCluster, exists) { - CLUSTER_PROCESS_KW_CMD("EXISTS", redis_gen_key_cmd, cluster_int_resp); + CLUSTER_PROCESS_KW_CMD("EXISTS", redis_gen_key_cmd, cluster_long_resp); } /* }}} */ @@ -297,4 +299,16 @@ PHP_METHOD(RedisCluster, strlen) { CLUSTER_PROCESS_KW_CMD("STRLEN", redis_gen_key_cmd, cluster_bulk_resp); } +/* {{{ proto long RedisCluster::rpushx(string key, mixed value) */ +PHP_METHOD(RedisCluster, rpushx) { + CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_gen_kv_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::lpushx(string key, mixed value) */ +PHP_METHOD(RedisCluster, lpushx) { + CLUSTER_PROCESS_KW_CMD("LPUSHX", redis_gen_kv_cmd, cluster_long_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index b4dc3516ea..5d80601913 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -78,6 +78,7 @@ PHP_METHOD(RedisCluster, type); PHP_METHOD(RedisCluster, lpop); PHP_METHOD(RedisCluster, rpop); PHP_METHOD(RedisCluster, spop); - +PHP_METHOD(RedisCluster, rpushx); +PHP_METHOD(RedisCluster, lpushx); PHP_METHOD(RedisCluster, strlen); #endif From 98e38c837bfdfba61e3c7b167681b8fd2553abef Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Jun 2014 21:02:07 -0700 Subject: [PATCH 0247/1986] LLEN, SMEMBERS commands implemented o --- redis.c | 55 +++---------------------------------------------- redis_cluster.c | 14 +++++++++++++ redis_cluster.h | 4 ++++ 3 files changed, 21 insertions(+), 52 deletions(-) diff --git a/redis.c b/redis.c index 24f5997945..d4d414ed4f 100644 --- a/redis.c +++ b/redis.c @@ -1802,31 +1802,7 @@ PHP_METHOD(Redis, brPop) */ PHP_METHOD(Redis, lSize) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LLEN", "s", key, key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - + REDIS_PROCESS_KW_CMD("LLEN", redis_gen_key_cmd, redis_long_response); } /* }}} */ @@ -1991,35 +1967,10 @@ PHP_METHOD(Redis, sAdd) } /* }}} */ -/* {{{ proto int Redis::sSize(string key) - */ +/* {{{ proto int Redis::sSize(string key) */ PHP_METHOD(Redis, sSize) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SCARD", "s", key, - key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 0b0d1e8747..04e4c25d34 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -53,6 +53,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, llen, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, scard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -311,4 +313,16 @@ PHP_METHOD(RedisCluster, lpushx) { } /* }}} */ +/* {{{ proto long RedisCluster::llen(string key) }}} */ +PHP_METHOD(RedisCluster, llen) { + CLUSTER_PROCESS_KW_CMD("LLEN", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::smembers(string key) }}} */ +PHP_METHOD(RedisCluster, scard) { + CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 5d80601913..08d1939a73 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -80,5 +80,9 @@ PHP_METHOD(RedisCluster, rpop); PHP_METHOD(RedisCluster, spop); PHP_METHOD(RedisCluster, rpushx); PHP_METHOD(RedisCluster, lpushx); +PHP_METHOD(RedisCluster, llen); + +PHP_METHOD(RedisCluster, scard); + PHP_METHOD(RedisCluster, strlen); #endif From 6f2a309b7ece337399e16652a8568a9b020886b6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 08:25:53 -0700 Subject: [PATCH 0248/1986] Added PERSIST and SMEMBERS command, also spaces > tabs :) --- cluster_library.c | 68 +++- cluster_library.h | 15 +- library.c | 859 +++++++++++++++++++++++----------------------- redis.c | 121 +++---- redis_cluster.c | 21 +- redis_cluster.h | 3 +- 6 files changed, 564 insertions(+), 523 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index c7eb4334bc..30f8ef0bfa 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1037,37 +1037,56 @@ PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } } -/* Raw multibulk reply response */ -PHPAPI void cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c) +/* Generic MULTI BULK response processor */ +PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, mbulk_cb cb) { zval *z_result; - // Verify reply type + // Verify our reply type byte is correct if(c->reply_type != TYPE_MULTIBULK) { RETURN_FALSE; } - // Allocate our array + // Allocate array MAKE_STD_ZVAL(z_result); array_init(z_result); - if(cluster_mbulk_resp_loop_raw(SLOT_SOCK(c,c->reply_slot), z_result, - c->reply_len TSRMLS_CC)==FAILURE) + // Call our specified callback + if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len TSRMLS_CC) + ==FAILURE) { zval_dtor(z_result); FREE_ZVAL(z_result); RETURN_FALSE; } - - // Return our array + + // Success, make this array our return value *return_value = *z_result; efree(z_result); } -/* Raw multibulk reply loop */ -PHPAPI int cluster_mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - size_t count TSRMLS_DC) +/* Raw MULTI BULK reply */ +PHPAPI void +cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, + c, mbulk_resp_loop_raw); +} + +/* Unserialize all the things */ +PHPAPI void +cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, + c, mbulk_resp_loop); +} + +/* + * Various MULTI BULK reply callback functions + */ + +/* MULTI BULK response where we don't touch the values (e.g. KEYS) */ +int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, + size_t count TSRMLS_DC) { char *line; int line_len; @@ -1086,4 +1105,29 @@ PHPAPI int cluster_mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, return SUCCESS; } +/* MULTI BULK response where we unserialize everything */ +int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, size_t count + TSRMLS_CC) +{ + char *line; + int line_len; + zval *z; + + // Iterate over the lines we have to process + while(count--) { + // Read the line + line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + if(line == NULL) return FAILURE; + + if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { + add_next_index_zval(z_result, z); + efree(line); + } else { + add_next_index_stringl(z_result, line, line_len, 0); + } + } + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index f29d8241e7..6f2bea0ca7 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -76,6 +76,9 @@ typedef enum CLUSTER_REDIR_TYPE { #define CLUSTER_CLEAR_REPLY(c) \ *c->line_reply = '\0'; c->reply_len = 0; +/* MULTI BULK response callback typedef */ +typedef int (*mbulk_cb)(RedisSock*,zval*,size_t TSRMLS_DC); + /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); @@ -203,9 +206,17 @@ PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -/* Raw (untouched) multi bulk processing */ +/* MULTI BULK response functions */ +PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, mbulk_cb func); PHPAPI void cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -PHPAPI int cluster_mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, +PHPAPI void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c); + +/* MULTI BULK processing callbacks */ +int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, + size_t count TSRMLS_DC); +int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, size_t count TSRMLS_DC); #endif diff --git a/library.c b/library.c index 0cc537eea6..e4c1b02dd5 100644 --- a/library.c +++ b/library.c @@ -141,41 +141,38 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) int eof; int count = 0; - if (!redis_sock->stream) { - return -1; - } + if (!redis_sock->stream) { + return -1; + } - eof = php_stream_eof(redis_sock->stream); + eof = php_stream_eof(redis_sock->stream); for (; eof; count++) { - /* Only try up to a certain point */ - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { - if(redis_sock->stream) { /* close stream if still here */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->watching = 0; - } - - zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); - return -1; - } - - /* Close existing stream before reconnecting */ - if(redis_sock->stream) { + if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ + if(redis_sock->stream) { /* close stream if still here */ redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->mode = ATOMIC; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->watching = 0; - } - - /* Wait for a while before trying to reconnect */ - if (redis_sock->retry_interval) { - // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time - long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); - usleep(retry_interval); } - + zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); + return -1; + } + if(redis_sock->stream) { /* close existing stream before reconnecting */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; + } + // Wait for a while before trying to reconnect + if (redis_sock->retry_interval) { + // Random factor to avoid having several (or many) concurrent + // connections trying to reconnect at the same time + long retry_interval = (count ? redis_sock->retry_interval + : (random() % redis_sock->retry_interval)); + + usleep(retry_interval); + } redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ if(redis_sock->stream) { /* check for EOF again. */ eof = php_stream_eof(redis_sock->stream); @@ -237,7 +234,8 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, case TYPE_SCAN: return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_SSCAN: - return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_sock_read_multibulk_reply( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_ZSCAN: return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_HSCAN: @@ -249,7 +247,7 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char inbuf[1024]; - int numElems; + int numElems; zval *z_tab; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { @@ -257,13 +255,13 @@ PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", - 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, + "read error on connection", 0 TSRMLS_CC); return NULL; } @@ -301,13 +299,15 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes char c; int i; - reply = emalloc(bytes+1); + reply = emalloc(bytes+1); while(offset < bytes) { - got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); + got = php_stream_read(redis_sock->stream, reply + offset, + bytes-offset); if (got <= 0) { /* Error or EOF */ - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, + "socket error on read socket", 0 TSRMLS_CC); break; } offset += got; @@ -335,7 +335,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -346,13 +346,12 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D switch(inbuf[0]) { case '-': - /* Set the last error */ err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); + redis_sock_set_err(redis_sock, inbuf+1, err_len); /* Handle stale data error */ - if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { - zend_throw_exception(redis_exception_ce, + if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); } @@ -381,12 +380,12 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D return resp; } default: - zend_throw_exception_ex( - redis_exception_ce, - 0 TSRMLS_CC, - "protocol error, got '%c' as reply type byte\n", - inbuf[0] - ); + zend_throw_exception_ex( + redis_exception_ce, + 0 TSRMLS_CC, + "protocol error, got '%c' as reply type byte\n", + inbuf[0] + ); } return NULL; @@ -403,18 +402,18 @@ void add_constant_long(zend_class_entry *ce, char *name, int value) { int integer_length(int i) { - int sz = 0; - int ci = abs(i); - while (ci > 0) { - ci /= 10; - sz++; - } - if (i == 0) { /* log 0 doesn't make sense. */ - sz = 1; - } else if (i < 0) { /* allow for neg sign as well. */ - sz++; - } - return sz; + int sz = 0; + int ci = abs(i); + while (ci > 0) { + ci /= 10; + sz++; + } + if (i == 0) { /* log 0 doesn't make sense. */ + sz = 1; + } else if (i < 0) { /* allow for neg sign as well. */ + sz++; + } + return sz; } int @@ -425,14 +424,14 @@ redis_cmd_format_header(char **ret, char *keyword, int arg_count) { /* Keyword length */ int l = strlen(keyword); - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, arg_count + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, arg_count + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); /* Set our return pointer */ *ret = buf.c; @@ -448,133 +447,134 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) va_list ap; smart_str buf = {0}; int l = strlen(keyword); - char *dbl_str; - int dbl_len; + char *dbl_str; + int dbl_len; - va_start(ap, format); + va_start(ap, format); - /* add header */ - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, strlen(format) + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + /* add header */ + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, strlen(format) + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - while (*p) { - smart_str_appendc(&buf, '$'); + while (*p) { + smart_str_appendc(&buf, '$'); - switch(*p) { - case 's': { + switch(*p) { + case 's': { char *val = va_arg(ap, char*); int val_len = va_arg(ap, int); smart_str_append_long(&buf, val_len); smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); smart_str_appendl(&buf, val, val_len); } - break; - case 'f': - case 'F': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; + break; + case 'f': + case 'F': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; - case 'i': - case 'd': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - case 'l': - case 'L': { - long l = va_arg(ap, long); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - p++; - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - } - smart_str_0(&buf); + case 'i': + case 'd': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + case 'l': + case 'L': { + long l = va_arg(ap, long); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); + smart_str_append_long(&buf, tmp_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + p++; + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + } + smart_str_0(&buf); - *ret = buf.c; + *ret = buf.c; - return buf.len; + return buf.len; } /** - * This command behave somehow like printf, except that strings need 2 arguments: - * Their data and their size (strlen). - * Supported formats are:d, %i, %s, %l + * This command behave somehow like printf, except that strings need 2 + * arguments: + * Their data and their size (strlen). + * Supported formats are: %d, %i, %s, %l */ int redis_cmd_format(char **ret, char *format, ...) { - smart_str buf = {0}; - va_list ap; - char *p = format; - char *dbl_str; - int dbl_len; - - va_start(ap, format); - - while (*p) { - if (*p == '%') { - switch (*(++p)) { - case 's': { - char *tmp = va_arg(ap, char*); - int tmp_len = va_arg(ap, int); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - - case 'F': - case 'f': { - double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); - } - break; - - case 'd': - case 'i': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_appendl(&buf, tmp, tmp_len); - } - break; - } - } else { - smart_str_appendc(&buf, *p); - } + smart_str buf = {0}; + va_list ap; + char *p = format; + char *dbl_str; + int dbl_len; - p++; - } + va_start(ap, format); + + while (*p) { + if (*p == '%') { + switch (*(++p)) { + case 's': { + char *tmp = va_arg(ap, char*); + int tmp_len = va_arg(ap, int); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + + case 'F': + case 'f': { + double d = va_arg(ap, double); + REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) + smart_str_append_long(&buf, dbl_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_str_appendl(&buf, dbl_str, dbl_len); + efree(dbl_str); + } + break; + + case 'd': + case 'i': { + int i = va_arg(ap, int); + char tmp[32]; + int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); + smart_str_appendl(&buf, tmp, tmp_len); + } + break; + } + } else { + smart_str_appendc(&buf, *p); + } - smart_str_0(&buf); + p++; + } - *ret = buf.c; + smart_str_0(&buf); - return buf.len; + *ret = buf.c; + + return buf.len; } /* @@ -691,23 +691,23 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi char *response; int response_len; - double ret; + double ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } - return; + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } + return; } ret = atof(response); efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_double(z_tab, ret); + add_next_index_double(z_tab, ret); } else { - RETURN_DOUBLE(ret); + RETURN_DOUBLE(ret); } } @@ -717,11 +717,11 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * long l; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + RETURN_FALSE; + } } if (strncmp(response, "+string", 7) == 0) { @@ -740,18 +740,18 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * efree(response); IF_MULTI_OR_PIPELINE() { - add_next_index_long(z_tab, l); + add_next_index_long(z_tab, l); } else { - RETURN_LONG(l); + RETURN_LONG(l); } } PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - char *pos, *cur; - char *key, *value, *p; - int is_numeric; + char *pos, *cur; + char *key, *value, *p; + int is_numeric; zval *z_multi_result; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { @@ -767,13 +767,13 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * cur = response; while(1) { - /* skip comments and empty lines */ - if(*cur == '#' || *cur == '\r') { - if(!(cur = strchr(cur, '\n'))) - break; - cur++; - continue; - } + /* skip comments and empty lines */ + if(*cur == '#' || *cur == '\r') { + if(!(cur = strchr(cur, '\n'))) + break; + cur++; + continue; + } /* key */ pos = strchr(cur, ':'); @@ -972,15 +972,15 @@ PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, Red add_next_index_bool(z_tab, 0); } } else { - if (ret == '+') { + if (ret == '+') { if (success_callback != NULL) { success_callback(redis_sock); } - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -993,12 +993,12 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * int response_len; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } } if(response[0] == ':') { @@ -1008,27 +1008,27 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * long long ret = atoll(response + 1); #endif IF_MULTI_OR_PIPELINE() { - if(ret > LONG_MAX) { /* overflow */ - add_next_index_stringl(z_tab, response+1, response_len-1, 1); - } else { - efree(response); - add_next_index_long(z_tab, (long)ret); - } + if(ret > LONG_MAX) { /* overflow */ + add_next_index_stringl(z_tab, response+1, response_len-1, 1); + } else { + efree(response); + add_next_index_long(z_tab, (long)ret); + } } else { - if(ret > LONG_MAX) { /* overflow */ - RETURN_STRINGL(response+1, response_len-1, 1); - } else { - efree(response); - RETURN_LONG((long)ret); - } - } + if(ret > LONG_MAX) { /* overflow */ + RETURN_STRINGL(response+1, response_len-1, 1); + } else { + efree(response); + RETURN_LONG((long)ret); + } + } } else { efree(response); IF_MULTI_OR_PIPELINE() { add_next_index_null(z_tab); } else { - RETURN_FALSE; - } + RETURN_FALSE; + } } } @@ -1106,14 +1106,14 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int unserialize, int decode) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1144,10 +1144,10 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); + *return_value = *z_multi_result; + zval_copy_ctor(return_value); + zval_dtor(z_multi_result); + efree(z_multi_result); } return 0; @@ -1184,36 +1184,34 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, Re z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } -PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - - char *response; - int response_len; - char ret; + char *response; + int response_len; + char ret; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } - } - ret = response[1]; - efree(response); + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + return; + } else { + RETURN_FALSE; + } + } + ret = response[1]; + efree(response); - IF_MULTI_OR_PIPELINE() { - if(ret == '1') { - add_next_index_bool(z_tab, 1); - } else { - add_next_index_bool(z_tab, 0); - } - } else { - if (ret == '1') { - RETURN_TRUE; - } else { - RETURN_FALSE; - } - } + IF_MULTI_OR_PIPELINE() { + if(ret == '1') { + add_next_index_bool(z_tab, 1); + } else { + add_next_index_bool(z_tab, 0); + } + } else { + if (ret == '1') { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + } } PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -1224,29 +1222,29 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { - if(redis_unserialize(redis_sock, response, response_len, + if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { - RETURN_STRINGL(response, response_len, 0); - } else { - efree(response); - } - } + RETURN_STRINGL(response, response_len, 0); + } else { + efree(response); + } + } } /* like string response, but never unserialized. */ @@ -1258,15 +1256,15 @@ PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); - return; + return; } RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - add_next_index_stringl(z_tab, response, response_len, 0); + add_next_index_stringl(z_tab, response, response_len, 0); } else { - RETURN_STRINGL(response, response_len, 0); - } + RETURN_STRINGL(response, response_len, 0); + } } /* Response for DEBUG object which is a formatted single line reply */ @@ -1352,7 +1350,7 @@ PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned sh redis_sock->lazy_connect = lazy_connect; if(persistent_id) { - size_t persistent_id_len = strlen(persistent_id); + size_t persistent_id_len = strlen(persistent_id); redis_sock->persistent_id = ecalloc(persistent_id_len + 1, 1); memcpy(redis_sock->persistent_id, persistent_id, persistent_id_len); } else { @@ -1389,8 +1387,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) struct timeval tv, read_tv, *tv_ptr = NULL; char *host = NULL, *persistent_id = NULL, *errstr = NULL; int host_len, err = 0; - php_netstream_data_t *sock; - int tcp_flag = 1; + php_netstream_data_t *sock; + int tcp_flag = 1; if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock TSRMLS_CC); @@ -1399,18 +1397,18 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) tv.tv_sec = (time_t)redis_sock->timeout; tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); if(tv.tv_sec != 0 || tv.tv_usec != 0) { - tv_ptr = &tv; + tv_ptr = &tv; } read_tv.tv_sec = (time_t)redis_sock->read_timeout; read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); if(redis_sock->host[0] == '/' && redis_sock->port < 1) { - host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); + host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); } else { - if(redis_sock->port == 0) - redis_sock->port = 6379; - host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); + if(redis_sock->port == 0) + redis_sock->port = 6379; + host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); } if (redis_sock->persistent) { @@ -1422,10 +1420,10 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } redis_sock->stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE, - STREAM_XPORT_CLIENT - | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &errstr, &err - ); + STREAM_XPORT_CLIENT + | STREAM_XPORT_CONNECT, + persistent_id, tv_ptr, NULL, &errstr, &err + ); if (persistent_id) { efree(persistent_id); @@ -1439,7 +1437,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } /* set TCP_NODELAY */ - sock = (php_netstream_data_t*)redis_sock->stream->abstract; + sock = (php_netstream_data_t*)redis_sock->stream->abstract; setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); php_stream_auto_cleanup(redis_sock->stream); @@ -1490,7 +1488,7 @@ PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connec PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) { if (redis_sock == NULL) { - return 1; + return 1; } redis_sock->dbNumber = 0; @@ -1502,7 +1500,7 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) } redis_sock->stream = NULL; - return 1; + return 1; } return 0; @@ -1511,8 +1509,8 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *cmd; - int response_len, cmd_len; - char * response; + int response_len, cmd_len; + char * response; cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); @@ -1526,10 +1524,10 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r RETURN_FALSE; } - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { - RETURN_TRUE; - } - RETURN_FALSE; + if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + RETURN_TRUE; + } + RETURN_FALSE; } /** @@ -1569,14 +1567,14 @@ PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems, err_len; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1622,14 +1620,14 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); + redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; @@ -1709,19 +1707,22 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re while(numElems > 0) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - int can_unserialize = unwrap_key; - if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && numElems % 2 == 0) - can_unserialize = 0; + zval *z = NULL; + int can_unserialize = unwrap_key; + if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && + numElems % 2 == 0) + { + can_unserialize = 0; + } - if(can_unserialize && redis_unserialize(redis_sock, response, + if(can_unserialize && redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC)==1) { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } + efree(response); + add_next_index_zval(z_tab, z); + } else { + add_next_index_stringl(z_tab, response, response_len, 0); + } } else { add_next_index_bool(z_tab, 0); } @@ -1770,20 +1771,20 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z + zval *z = NULL; + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); - } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); - } - } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); - } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + efree(response); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + } + } else { + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + } + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); @@ -1804,10 +1805,10 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc */ PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { - if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { - zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); - return -1; - } + if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { + zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); + return -1; + } if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } @@ -1820,10 +1821,10 @@ PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz T PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) { if(redis_sock->prefix) { - efree(redis_sock->prefix); - } + efree(redis_sock->prefix); + } if(redis_sock->err) { - efree(redis_sock->err); + efree(redis_sock->err); } if(redis_sock->auth) { efree(redis_sock->auth); @@ -1838,79 +1839,79 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC) { #if ZEND_MODULE_API_NO >= 20100000 - php_serialize_data_t ht; + php_serialize_data_t ht; #else - HashTable ht; + HashTable ht; #endif - smart_str sstr = {0}; - zval *z_copy; + smart_str sstr = {0}; + zval *z_copy; #ifdef HAVE_REDIS_IGBINARY - size_t sz; - uint8_t *val8; + size_t sz; + uint8_t *val8; #endif - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - switch(Z_TYPE_P(z)) { - - case IS_STRING: - *val = Z_STRVAL_P(z); - *val_len = Z_STRLEN_P(z); - return 0; - - case IS_OBJECT: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Object", 6, 1); - break; - - case IS_ARRAY: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Array", 5, 1); - break; - - default: /* copy */ - MAKE_STD_ZVAL(z_copy); - *z_copy = *z; - zval_copy_ctor(z_copy); - break; - } + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + switch(Z_TYPE_P(z)) { + + case IS_STRING: + *val = Z_STRVAL_P(z); + *val_len = Z_STRLEN_P(z); + return 0; + + case IS_OBJECT: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Object", 6, 1); + break; + + case IS_ARRAY: + MAKE_STD_ZVAL(z_copy); + ZVAL_STRINGL(z_copy, "Array", 5, 1); + break; + + default: /* copy */ + MAKE_STD_ZVAL(z_copy); + *z_copy = *z; + zval_copy_ctor(z_copy); + break; + } - /* return string */ - convert_to_string(z_copy); - *val = Z_STRVAL_P(z_copy); - *val_len = Z_STRLEN_P(z_copy); - efree(z_copy); - return 1; + /* return string */ + convert_to_string(z_copy); + *val = Z_STRVAL_P(z_copy); + *val_len = Z_STRLEN_P(z_copy); + efree(z_copy); + return 1; - case REDIS_SERIALIZER_PHP: + case REDIS_SERIALIZER_PHP: #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_INIT(ht); + PHP_VAR_SERIALIZE_INIT(ht); #else - zend_hash_init(&ht, 10, NULL, NULL, 0); + zend_hash_init(&ht, 10, NULL, NULL, 0); #endif - php_var_serialize(&sstr, &z, &ht TSRMLS_CC); - *val = sstr.c; - *val_len = (int)sstr.len; + php_var_serialize(&sstr, &z, &ht TSRMLS_CC); + *val = sstr.c; + *val_len = (int)sstr.len; #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_SERIALIZE_DESTROY(ht); + PHP_VAR_SERIALIZE_DESTROY(ht); #else - zend_hash_destroy(&ht); + zend_hash_destroy(&ht); #endif - return 1; + return 1; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ - *val = (char*)val8; - *val_len = (int)sz; - return 1; - } + if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ + *val = (char*)val8; + *val_len = (int)sz; + return 1; + } #endif - return 0; - } - return 0; + return 0; + } + return 0; } PHPAPI int @@ -1918,39 +1919,39 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) { - php_unserialize_data_t var_hash; - int ret, rv_free = 0; + php_unserialize_data_t var_hash; + int ret, rv_free = 0; - switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - return 0; + switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + return 0; - case REDIS_SERIALIZER_PHP: - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - rv_free = 1; - } + case REDIS_SERIALIZER_PHP: + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + rv_free = 1; + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_INIT(var_hash); + PHP_VAR_UNSERIALIZE_INIT(var_hash); #else - memset(&var_hash, 0, sizeof(var_hash)); + memset(&var_hash, 0, sizeof(var_hash)); #endif - if(!php_var_unserialize(return_value, (const unsigned char**)&val, - (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { - if(rv_free==1) efree(*return_value); - ret = 0; - } else { - ret = 1; - } + if(!php_var_unserialize(return_value, (const unsigned char**)&val, + (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { + if(rv_free==1) efree(*return_value); + ret = 0; + } else { + ret = 1; + } #if ZEND_MODULE_API_NO >= 20100000 - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); #else - var_destroy(&var_hash); + var_destroy(&var_hash); #endif - return ret; + return ret; - case REDIS_SERIALIZER_IGBINARY: + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY if(!*return_value) { MAKE_STD_ZVAL(*return_value); @@ -1961,29 +1962,29 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, } if(rv_free==1) efree(*return_value); #endif - return 0; - break; - } - return 0; + return 0; + break; + } + return 0; } PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len) { int ret_len; - char *ret; + char *ret; - if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { - return 0; - } - + if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { + return 0; + } + ret_len = redis_sock->prefix_len + *key_len; - ret = ecalloc(1 + ret_len, 1); - memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); - memcpy(ret + redis_sock->prefix_len, *key, *key_len); + ret = ecalloc(1 + ret_len, 1); + memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); + memcpy(ret + redis_sock->prefix_len, *key, *key_len); - *key = ret; - *key_len = ret_len; - return 1; + *key = ret; + *key_len = ret_len; + return 1; } /* @@ -2078,7 +2079,7 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval ZVAL_TRUE(*z_ret); } - return 0; + return 0; } PHP_REDIS_API int @@ -2098,9 +2099,9 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { - int reply_info; - REDIS_REPLY_TYPE reply_type; - zval *z_subelem; + int reply_info; + REDIS_REPLY_TYPE reply_type; + zval *z_subelem; /* Iterate while we have elements */ while(elements > 0) { @@ -2138,13 +2139,13 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret default: // Stop the compiler from whinging break; - } + } /* Decrement our element counter */ elements--; } - return 0; + return 0; } PHP_REDIS_API int @@ -2188,7 +2189,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv /* Protocol error */ zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); return FAILURE; - } + } IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_ret); diff --git a/redis.c b/redis.c index d4d414ed4f..14a13d10f6 100644 --- a/redis.c +++ b/redis.c @@ -2142,39 +2142,16 @@ PHP_METHOD(Redis, sContains) */ PHP_METHOD(Redis, sMembers) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SMEMBERS", "s", key, - key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, + redis_sock_read_multibulk_reply); } /* }}} */ -PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len, - int min_argc, RedisSock **out_sock, int has_timeout, int all_keys, int can_serialize) +PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, + char *keyword, int keyword_len, + int min_argc, RedisSock **out_sock, + int has_timeout, int all_keys, + int can_serialize) { zval **z_args, *z_array; char **keys, *cmd; @@ -2219,7 +2196,9 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * argc = zend_hash_num_elements(Z_ARRVAL_P(z_array)); } } else if(has_timeout == 1) { - if(argc == 2 && Z_TYPE_P(z_args[0]) == IS_ARRAY && Z_TYPE_P(z_args[1]) == IS_LONG) { + if(argc == 2 && Z_TYPE_P(z_args[0]) == IS_ARRAY && + Z_TYPE_P(z_args[1]) == IS_LONG) + { single_array = 1; z_array = z_args[0]; timeout = Z_LVAL_P(z_args[1]); @@ -2230,7 +2209,8 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * } } - /* prepare an array for the keys, one for their lengths, one to mark the keys to free. */ + /* prepare an array for the keys, one for their lengths, one to mark the + * keys to free. */ array_size = argc; if(has_timeout) array_size++; @@ -2241,7 +2221,8 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * memset(keys_to_free, 0, array_size * sizeof(int)); - cmd_len = 1 + integer_length(keyword_len) + 2 +keyword_len + 2; /* start computing the command length */ + /* Start computing the command length */ + cmd_len = 1 + integer_length(keyword_len) + 2 +keyword_len + 2; if(single_array) { /* loop over the array */ HashTable *keytable = Z_ARRVAL_P(z_array); @@ -2255,16 +2236,21 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * unsigned long idx; zval **z_value_pp; - zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ + zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, + NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) + == FAILURE) + { + /* this should never happen, according to the PHP people */ + continue; } if(!all_keys && j != 0) { /* not just operating on keys */ if(can_serialize) { - keys_to_free[j] = redis_serialize(redis_sock, *z_value_pp, &keys[j], &keys_len[j] TSRMLS_CC); + keys_to_free[j] = redis_serialize(redis_sock, *z_value_pp, + &keys[j], &keys_len[j] TSRMLS_CC); } else { convert_to_string(*z_value_pp); keys[j] = Z_STRVAL_PP(z_value_pp); @@ -2283,29 +2269,33 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * keys[j] = Z_STRVAL_PP(z_value_pp); keys_len[j] = Z_STRLEN_PP(z_value_pp); - keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j]); /* add optional prefix */ + keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], + &keys_len[j]); } - - cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; /* $ + size + NL + string + NL */ + /* $ + size + NL + string + NL */ + cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; j++; real_argc++; } if(has_timeout) { keys_len[j] = spprintf(&keys[j], 0, "%d", timeout); - cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; // $ + size + NL + string + NL + + /* $ + size + NL + string + NL */ + cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; j++; real_argc++; } } else { if(has_timeout && Z_TYPE_P(z_args[argc - 1]) != IS_LONG) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Syntax error on timeout"); + php_error_docref(NULL TSRMLS_CC, E_ERROR, + "Syntax error on timeout"); } for(i = 0, j = 0; i < argc; ++i) { /* store each key */ if(!all_keys && j != 0) { /* not just operating on keys */ - if(can_serialize) { - keys_to_free[j] = redis_serialize(redis_sock, z_args[i], &keys[j], &keys_len[j] TSRMLS_CC); + keys_to_free[j] = redis_serialize(redis_sock, z_args[i], + &keys[j], &keys_len[j] TSRMLS_CC); } else { convert_to_string(z_args[i]); keys[j] = Z_STRVAL_P(z_args[i]); @@ -2322,13 +2312,16 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * keys[j] = Z_STRVAL_P(z_args[i]); keys_len[j] = Z_STRLEN_P(z_args[i]); - /* If we have a timeout it should be the last argument, which we do not want to prefix */ + // If we have a timeout it should be the last argument, which + // we do not want to prefix if(!has_timeout || i < argc-1) { - keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], &keys_len[j]); /* add optional prefix TSRMLS_CC*/ + keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], + &keys_len[j]); } } - cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; /* $ + size + NL + string + NL */ + /* $ + size + NL + string + NL */ + cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; j++; real_argc++; } @@ -2337,7 +2330,8 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * cmd_len += 1 + integer_length(real_argc+1) + 2; /* *count NL */ cmd = emalloc(cmd_len+1); - sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1+real_argc, keyword_len, keyword); + sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1+real_argc, keyword_len, + keyword); pos = 1 +integer_length(real_argc + 1) + 2 + 1 + integer_length(keyword_len) + 2 @@ -2358,7 +2352,9 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char * if(keys_to_free[i]) STR_FREE(keys[i]); } - if(single_array && has_timeout) { /* cleanup string created to contain timeout value */ + + /* Cleanup timeout string */ + if(single_array && has_timeout) { efree(keys[real_argc-1]); } @@ -3160,32 +3156,7 @@ PHP_METHOD(Redis, auth) { /* {{{ proto long Redis::persist(string key) */ PHP_METHOD(Redis, persist) { - - zval *object; - RedisSock *redis_sock; - - char *cmd, *key; - int cmd_len, key_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "PERSIST", "s", key, - key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); + REDIS_PROCESS_KW_CMD("PERSIST", redis_gen_key_cmd, redis_1_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 04e4c25d34..da72fe9c0b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -55,7 +55,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, llen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, scard, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, smembers, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, persist, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -266,7 +268,6 @@ PHP_METHOD(RedisCluster, exists) { PHP_METHOD(RedisCluster, keys) { // TODO: Figure out how to implement this, as we may want to send it across // all nodes (although that seems dangerous), or ask for a specified slot. - //CLUSTER_PROCESS_KW_CMD("KEYS", redis_gen_key_cmd, cluster_multibulk_resp); zend_throw_exception(redis_cluster_exception_ce, "KEYS command not implemented", 0 TSRMLS_CC); } @@ -313,15 +314,27 @@ PHP_METHOD(RedisCluster, lpushx) { } /* }}} */ -/* {{{ proto long RedisCluster::llen(string key) }}} */ +/* {{{ proto long RedisCluster::llen(string key) */ PHP_METHOD(RedisCluster, llen) { CLUSTER_PROCESS_KW_CMD("LLEN", redis_gen_key_cmd, cluster_long_resp); } /* }}} */ -/* {{{ proto long RedisCluster::smembers(string key) }}} */ +/* {{{ proto long RedisCluster::scard(string key) */ PHP_METHOD(RedisCluster, scard) { - CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("SCARD", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::smembers(string key) */ +PHP_METHOD(RedisCluster, smembers) { + CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, cluster_mbulk_resp); +} +/* }}} */ + +/* {{{ proto bool RedisCluster::persist(string key) */ +PHP_METHOD(RedisCluster, persist) { + CLUSTER_PROCESS_KW_CMD("PERSIST", redis_gen_key_cmd, cluster_1_resp); } /* }}} */ diff --git a/redis_cluster.h b/redis_cluster.h index 08d1939a73..acd1f94f63 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -74,7 +74,7 @@ PHP_METHOD(RedisCluster, getset); PHP_METHOD(RedisCluster, exists); PHP_METHOD(RedisCluster, keys); PHP_METHOD(RedisCluster, type); - +PHP_METHOD(RedisCluster, persist); PHP_METHOD(RedisCluster, lpop); PHP_METHOD(RedisCluster, rpop); PHP_METHOD(RedisCluster, spop); @@ -83,6 +83,7 @@ PHP_METHOD(RedisCluster, lpushx); PHP_METHOD(RedisCluster, llen); PHP_METHOD(RedisCluster, scard); +PHP_METHOD(RedisCluster, smembers); PHP_METHOD(RedisCluster, strlen); #endif From 0bbd49b9a61a5ed2a88c1453a9e80f89718acc19 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 08:31:05 -0700 Subject: [PATCH 0249/1986] TTL/PTTL Implemented TTL and PTTL for both cluster and redis proper --- redis.c | 32 ++------------------------------ redis_cluster.c | 12 ++++++++++++ redis_cluster.h | 3 +++ 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/redis.c b/redis.c index 14a13d10f6..3426c5360b 100644 --- a/redis.c +++ b/redis.c @@ -3160,45 +3160,17 @@ PHP_METHOD(Redis, persist) { } /* }}} */ -PHP_REDIS_API void generic_ttl(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { - zval *object; - RedisSock *redis_sock; - - char *cmd, *key; - int cmd_len, key_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, - key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); -} - /* {{{ proto long Redis::ttl(string key) */ PHP_METHOD(Redis, ttl) { - generic_ttl(INTERNAL_FUNCTION_PARAM_PASSTHRU, "TTL"); + REDIS_PROCESS_KW_CMD("TTL", redis_gen_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::pttl(string key) */ PHP_METHOD(Redis, pttl) { - generic_ttl(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PTTL"); + REDIS_PROCESS_KW_CMD("PTTL", redis_gen_key_cmd, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index da72fe9c0b..235cdfe503 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -338,4 +338,16 @@ PHP_METHOD(RedisCluster, persist) { } /* }}} */ +/* {{{ proto long RedisCluster::ttl(string key) */ +PHP_METHOD(RedisCluster, ttl) { + CLUSTER_PROCESS_KW_CMD("TTL", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::pttl(string key) */ +PHP_METHOD(RedisCluster, pttl) { + CLUSTER_PROCESS_KW_CMD("PTTL", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index acd1f94f63..1b925bb684 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -86,4 +86,7 @@ PHP_METHOD(RedisCluster, scard); PHP_METHOD(RedisCluster, smembers); PHP_METHOD(RedisCluster, strlen); + +PHP_METHOD(RedisCluster, ttl); +PHP_METHOD(RedisCluster, pttl); #endif From 4d771d3bd9898807a8f65aa29c5bd1c9ee16f24c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 08:34:14 -0700 Subject: [PATCH 0250/1986] ZCARD --- redis_cluster.c | 9 +++++++++ redis_cluster.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/redis_cluster.c b/redis_cluster.c index 235cdfe503..1e02f718e1 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -58,6 +58,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, smembers, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, persist, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ttl, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pttl, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zcard, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -350,4 +353,10 @@ PHP_METHOD(RedisCluster, pttl) { } /* }}} */ +/* {{{ proto long RedisCluster::zcard(string key) */ +PHP_METHOD(RedisCluster, zcard) { + CLUSTER_PROCESS_KW_CMD("ZCARD", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 1b925bb684..00ba4dea9a 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -89,4 +89,6 @@ PHP_METHOD(RedisCluster, strlen); PHP_METHOD(RedisCluster, ttl); PHP_METHOD(RedisCluster, pttl); + +PHP_METHOD(RedisCluster, zcard); #endif From 69096d4d375161092d522b27ecde13c74e1cf7a8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 08:52:03 -0700 Subject: [PATCH 0251/1986] ZCARD/ZSCORE as well as double response handler for cluster --- cluster_library.c | 21 +++++++++++++++++ cluster_library.h | 1 + redis.c | 59 ++++------------------------------------------- redis_cluster.c | 7 ++++++ redis_cluster.h | 1 + 5 files changed, 34 insertions(+), 55 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 30f8ef0bfa..0e654712e1 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -981,6 +981,27 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } } +/* Bulk response where we expect a double */ +PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +{ + char *resp; + double dbl; + + // Make sure we can read the response + if(c->reply_type != TYPE_BULK || + (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), + c->reply_len TSRMLS_CC))==NULL) + { + RETURN_FALSE; + } + + // Convert to double, free response + dbl = atof(resp); + efree(resp); + + RETURN_DOUBLE(dbl); +} + /* A boolean response. If we get here, we've consumed the '+' reply * type and will now just verify we can read the OK */ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) diff --git a/cluster_library.h b/cluster_library.h index 6f2bea0ca7..58b3776b7d 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -202,6 +202,7 @@ PHPAPI int cluster_node_add_slave(redisCluster *cluster, PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); diff --git a/redis.c b/redis.c index 3426c5360b..510f9130ad 100644 --- a/redis.c +++ b/redis.c @@ -3160,6 +3160,7 @@ PHP_METHOD(Redis, persist) { } /* }}} */ + /* {{{ proto long Redis::ttl(string key) */ PHP_METHOD(Redis, ttl) { @@ -3976,32 +3977,7 @@ PHP_METHOD(Redis, zRangeByLex) { */ PHP_METHOD(Redis, zCard) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZCARD", "s", key, - key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - + REDIS_PROCESS_KW_CMD("ZCARD", redis_gen_key_cmd, redis_long_response); } /* }}} */ @@ -4009,35 +3985,8 @@ PHP_METHOD(Redis, zCard) */ PHP_METHOD(Redis, zScore) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *val = NULL, *cmd; - int key_len, val_len, cmd_len; - int val_free, key_free = 0; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz", - &object, redis_ce, &key, &key_len, - &z_value) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, - key_len, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_bulk_double_response); + REDIS_PROCESS_KW_CMD("ZSCORE", redis_gen_kv_cmd, + redis_bulk_double_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 1e02f718e1..8781e3caa0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -61,6 +61,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, ttl, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pttl, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcard, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -359,4 +360,10 @@ PHP_METHOD(RedisCluster, zcard) { } /* }}} */ +/* {{{ proto double RedisCluster::zscore(string key) */ +PHP_METHOD(RedisCluster, zscore) { + CLUSTER_PROCESS_KW_CMD("ZSCORE", redis_gen_kv_cmd, cluster_dbl_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 00ba4dea9a..73f652c7ab 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -91,4 +91,5 @@ PHP_METHOD(RedisCluster, ttl); PHP_METHOD(RedisCluster, pttl); PHP_METHOD(RedisCluster, zcard); +PHP_METHOD(RedisCluster, zscore); #endif From 58c6080c8cd9005dbd77f2af228781dd43828e25 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 08:54:51 -0700 Subject: [PATCH 0252/1986] HLEN commands for Redis and RedisCluster --- redis.c | 26 +------------------------- redis_cluster.c | 7 +++++++ redis_cluster.h | 2 ++ 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/redis.c b/redis.c index 510f9130ad..d446a96ae4 100644 --- a/redis.c +++ b/redis.c @@ -4338,31 +4338,7 @@ PHP_METHOD(Redis, hGet) /* hLen */ PHP_METHOD(Redis, hLen) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HLEN", "s", key, key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - + REDIS_PROCESS_KW_CMD("HLEN", redis_gen_key_cmd, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 8781e3caa0..8d09a5d93a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -62,6 +62,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, pttl, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -366,4 +367,10 @@ PHP_METHOD(RedisCluster, zscore) { } /* }}} */ +/* {{{ proto lont RedisCluster::hlen(string key) */ +PHP_METHOD(RedisCluster, hlen) { + CLUSTER_PROCESS_KW_CMD("HLEN", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 73f652c7ab..889c34084d 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -92,4 +92,6 @@ PHP_METHOD(RedisCluster, pttl); PHP_METHOD(RedisCluster, zcard); PHP_METHOD(RedisCluster, zscore); + +PHP_METHOD(RedisCluster, hlen); #endif From 39796aa09513a32790b2845b2b555b51d345a93a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 09:02:32 -0700 Subject: [PATCH 0253/1986] DUMP command, raw bulk handler Added support for the DUMP command and created a Cluster based RAW bulk handler. Thinking we should do like MULTI BULK and create a generic handler, with various callbacks for encoding --- cluster_library.c | 20 +++++++++++++++++++- cluster_library.h | 1 + redis.c | 29 +---------------------------- redis_cluster.c | 8 +++++++- redis_cluster.h | 1 + 5 files changed, 29 insertions(+), 30 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0e654712e1..5c979020bc 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -960,7 +960,25 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, * consumed. */ -/* Unmodified BULK response handler */ +/* RAW bulk response handler */ +PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c) +{ + char *resp; + + // Make sure we can read the response + if(c->reply_type != TYPE_BULK || + (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), + c->reply_len TSRMLS_CC))==NULL) + { + RETURN_FALSE; + } + + // Return our response raw + RETURN_STRINGL(resp,c->reply_len, 0); +} + +/* BULK response handler */ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { char *resp; diff --git a/cluster_library.h b/cluster_library.h index 58b3776b7d..1598600991 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -202,6 +202,7 @@ PHPAPI int cluster_node_add_slave(redisCluster *cluster, PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); diff --git a/redis.c b/redis.c index d446a96ae4..8d1b76ddbb 100644 --- a/redis.c +++ b/redis.c @@ -6145,34 +6145,7 @@ PHP_METHOD(Redis, script) { /* {{{ proto DUMP key */ PHP_METHOD(Redis, dump) { - zval *object; - RedisSock *redis_sock; - char *cmd, *key; - int cmd_len, key_len, key_free; - - /* Parse our arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - /* Grab our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* Prefix our key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "DUMP", "s", key, - key_len); - if(key_free) efree(key); - - /* Kick off our request */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_ping_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_ping_response); + REDIS_PROCESS_KW_CMD("DUMP", redis_gen_key_cmd, redis_ping_response); } /* {{{ proto Redis::DEBUG(string key) */ diff --git a/redis_cluster.c b/redis_cluster.c index 8d09a5d93a..06baeb0a82 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -63,6 +63,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zcard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -367,10 +368,15 @@ PHP_METHOD(RedisCluster, zscore) { } /* }}} */ -/* {{{ proto lont RedisCluster::hlen(string key) */ +/* {{{ proto long RedisCluster::hlen(string key) */ PHP_METHOD(RedisCluster, hlen) { CLUSTER_PROCESS_KW_CMD("HLEN", redis_gen_key_cmd, cluster_long_resp); } /* }}} */ +/* {{{ proto string RedisCluster::dump(string key) */ +PHP_METHOD(RedisCluster, dump) { + CLUSTER_PROCESS_KW_CMD("DUMP", redis_gen_key_cmd, cluster_bulk_raw_resp); +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 889c34084d..40f203a856 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -67,6 +67,7 @@ void init_rediscluster(TSRMLS_D); PHP_METHOD(RedisCluster, __construct); PHP_METHOD(RedisCluster, get); PHP_METHOD(RedisCluster, set); +PHP_METHOD(RedisCluster, dump); PHP_METHOD(RedisCluster, setex); PHP_METHOD(RedisCluster, psetex); PHP_METHOD(RedisCluster, setnx); From f0892858500f18f3fa0663c1f780d955e46429f2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 09:10:12 -0700 Subject: [PATCH 0254/1986] HKEYS/HVALS --- redis.c | 34 ++++++++-------------------------- redis_cluster.h | 2 ++ 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/redis.c b/redis.c index 8d1b76ddbb..fb701083a1 100644 --- a/redis.c +++ b/redis.c @@ -4443,37 +4443,19 @@ generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ return redis_sock; } -/* hKeys */ +/* {{{ proto array Redis::hkeys(string key) */ PHP_METHOD(Redis, hKeys) { - RedisSock *redis_sock = generic_hash_command_1(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HKEYS", sizeof("HKEYS")-1); - if(!redis_sock) - RETURN_FALSE; - - IF_ATOMIC() { - if (redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw); - - + REDIS_PROCESS_KW_CMD("HKEYS", redis_gen_key_cmd, + redis_sock_read_multibulk_reply_raw); } -/* hVals */ +/* }}} */ + +/* {{{ proto array Redis::hvals(string key) */ PHP_METHOD(Redis, hVals) { - RedisSock *redis_sock = generic_hash_command_1(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HVALS", sizeof("HVALS")-1); - if(!redis_sock) - RETURN_FALSE; - - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); - + REDIS_PROCESS_KW_CMD("HVALS", redis_gen_key_cmd, + redis_sock_read_multibulk_reply); } diff --git a/redis_cluster.h b/redis_cluster.h index 40f203a856..162c0ea861 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -95,4 +95,6 @@ PHP_METHOD(RedisCluster, zcard); PHP_METHOD(RedisCluster, zscore); PHP_METHOD(RedisCluster, hlen); +PHP_METHOD(RedisCluster, hkeys); +PHP_METHOD(RedisCluster, hvals); #endif From 0ec2e12feb51fd008333553153e9ae9bd34a6eb1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 09:10:47 -0700 Subject: [PATCH 0255/1986] Actually commit the file for HKEYS/HVALS --- redis_cluster.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/redis_cluster.c b/redis_cluster.c index 06baeb0a82..8ba957a958 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -63,6 +63,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zcard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hvals, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -374,6 +376,18 @@ PHP_METHOD(RedisCluster, hlen) { } /* }}} */ +/* {{{ proto array RedisCluster::hkeys(string key) */ +PHP_METHOD(RedisCluster, hkeys) { + CLUSTER_PROCESS_KW_CMD("HKEYS", redis_gen_key_cmd, cluster_mbulk_resp_raw); +} +/* }}} */ + +/* {{{ proto array RedisCluster::hvals(string key) */ +PHP_METHOD(RedisCluster, hvals) { + CLUSTER_PROCESS_KW_CMD("HVALS", redis_gen_key_cmd, cluster_mbulk_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_gen_key_cmd, cluster_bulk_raw_resp); From 7e5047e18c04935c10e4be47ac1969bf3b18a44b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 09:14:30 -0700 Subject: [PATCH 0256/1986] SISMEMBER, fix SMEMBERS proto typo --- redis.c | 30 +----------------------------- redis_cluster.c | 9 ++++++++- redis_cluster.h | 2 +- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/redis.c b/redis.c index fb701083a1..054dec153d 100644 --- a/redis.c +++ b/redis.c @@ -2106,35 +2106,7 @@ PHP_METHOD(Redis, sRandMember) */ PHP_METHOD(Redis, sContains) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *val = NULL, *cmd; - int key_len, val_len, cmd_len; - int val_free, key_free = 0; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz", - &object, redis_ce, - &key, &key_len, &z_value) == FAILURE) { - return; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, - key_len, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); + REDIS_PROCESS_KW_CMD("SISMEMBER", redis_gen_kv_cmd, redis_1_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 8ba957a958..660cf892ac 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -56,6 +56,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, llen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, scard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, smembers, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sismember, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, persist, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ttl, NULL, ZEND_ACC_PUBLIC) @@ -334,12 +335,18 @@ PHP_METHOD(RedisCluster, scard) { } /* }}} */ -/* {{{ proto long RedisCluster::smembers(string key) */ +/* {{{ proto array RedisCluster::smembers(string key) */ PHP_METHOD(RedisCluster, smembers) { CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, cluster_mbulk_resp); } /* }}} */ +/* {{{ proto long RedisCluster::sismember(string key) */ +PHP_METHOD(RedisCluster, sismember) { + CLUSTER_PROCESS_KW_CMD("SISMEMBER", redis_gen_kv_cmd, cluster_1_resp); +} +/* }}} */ + /* {{{ proto bool RedisCluster::persist(string key) */ PHP_METHOD(RedisCluster, persist) { CLUSTER_PROCESS_KW_CMD("PERSIST", redis_gen_key_cmd, cluster_1_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 162c0ea861..3265d7bdab 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -85,7 +85,7 @@ PHP_METHOD(RedisCluster, llen); PHP_METHOD(RedisCluster, scard); PHP_METHOD(RedisCluster, smembers); - +PHP_METHOD(RedisCluster, sismember); PHP_METHOD(RedisCluster, strlen); PHP_METHOD(RedisCluster, ttl); From 0b7ed2531a060a1152e4f6db4e487cf5d44f7a01 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 13:03:00 -0700 Subject: [PATCH 0257/1986] ZRANK/ZREVRANK --- redis.c | 9 +++------ redis_cluster.c | 15 +++++++++++++++ redis_cluster.h | 2 ++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/redis.c b/redis.c index 054dec153d..0d9b90b1fc 100644 --- a/redis.c +++ b/redis.c @@ -3999,16 +3999,13 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo /* {{{ proto long Redis::zRank(string key, string member) */ PHP_METHOD(Redis, zRank) { - - generic_rank_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANK", 5); + REDIS_PROCESS_KW_CMD("ZRANK", redis_gen_kv_cmd, redis_long_response); } /* }}} */ -/* {{{ proto long Redis::zRevRank(string key, string member) - */ +/* {{{ proto long Redis::zRevRank(string key, string member) */ PHP_METHOD(Redis, zRevRank) { - - generic_rank_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANK", 8); + REDIS_PROCESS_KW_CMD("ZREVRANK", redis_gen_kv_cmd, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 660cf892ac..ac630294e5 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -67,6 +67,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hvals, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrank, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -377,6 +379,19 @@ PHP_METHOD(RedisCluster, zscore) { } /* }}} */ +/* {{{ proto long RedisCluster::zrank(string key, mixed member) */ +PHP_METHOD(RedisCluster, zrank) { + CLUSTER_PROCESS_KW_CMD("ZRANK", redis_gen_kv_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::zrevrank(string key, mixed member) */ +PHP_METHOD(RedisCluster, zrevrank) { + CLUSTER_PROCESS_KW_CMD("ZREVRANK", redis_gen_kv_cmd, cluster_long_resp); +} +/* }}} */ + + /* {{{ proto long RedisCluster::hlen(string key) */ PHP_METHOD(RedisCluster, hlen) { CLUSTER_PROCESS_KW_CMD("HLEN", redis_gen_key_cmd, cluster_long_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 3265d7bdab..7d6abbe564 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -93,6 +93,8 @@ PHP_METHOD(RedisCluster, pttl); PHP_METHOD(RedisCluster, zcard); PHP_METHOD(RedisCluster, zscore); +PHP_METHOD(RedisCluster, zrank); +PHP_METHOD(RedisCluster, zrevrank); PHP_METHOD(RedisCluster, hlen); PHP_METHOD(RedisCluster, hkeys); From 4a73150007f2df8f898c254ad7ff0f4f66b5b9a0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 13:03:23 -0700 Subject: [PATCH 0258/1986] Remove deprecated function --- redis.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/redis.c b/redis.c index 0d9b90b1fc..44ae33e718 100644 --- a/redis.c +++ b/redis.c @@ -3962,40 +3962,6 @@ PHP_METHOD(Redis, zScore) } /* }}} */ - -PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *val = NULL, *cmd; - int key_len, val_len, cmd_len; - int val_free, key_free = 0; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz", - &object, redis_ce, &key, &key_len, - &z_value) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, - key_len, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); -} - - /* {{{ proto long Redis::zRank(string key, string member) */ PHP_METHOD(Redis, zRank) { From 9b2db3d3b8eeaa49bd01be9fae8ed31ab6598c08 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 13:59:21 -0700 Subject: [PATCH 0259/1986] Use long long for reply length/count storage We can't use size_t here because Redis might return to us a negative value (e.g. -1 for a bulk or multi bulk length part). --- cluster_library.c | 10 +++++++--- cluster_library.h | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 5c979020bc..159abeedaa 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -747,6 +747,8 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) static int cluster_check_response(redisCluster *c, unsigned short slot, REDIS_REPLY_TYPE *reply_type TSRMLS_DC) { + size_t sz; + // Clear out any prior error state and our last line response CLUSTER_CLEAR_ERROR(c); CLUSTER_CLEAR_REPLY(c); @@ -785,7 +787,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, // Fetch the first line of our response from Redis. if(redis_sock_gets(SLOT_SOCK(c,slot),c->line_reply,sizeof(c->line_reply), - &c->reply_len)<0) + &sz)<0) { return -1; } @@ -793,6 +795,8 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, // For replies that will give us a numberic length, convert it if(*reply_type != TYPE_LINE) { c->reply_len = atoi(c->line_reply); + } else { + c->reply_len = (long long)sz; } // Clear out any previous error, and return that the data is here @@ -1125,7 +1129,7 @@ cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { /* MULTI BULK response where we don't touch the values (e.g. KEYS) */ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - size_t count TSRMLS_DC) + long long count TSRMLS_DC) { char *line; int line_len; @@ -1145,7 +1149,7 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, } /* MULTI BULK response where we unserialize everything */ -int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, size_t count +int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count TSRMLS_CC) { char *line; diff --git a/cluster_library.h b/cluster_library.h index 1598600991..1c44ca7909 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -77,7 +77,7 @@ typedef enum CLUSTER_REDIR_TYPE { *c->line_reply = '\0'; c->reply_len = 0; /* MULTI BULK response callback typedef */ -typedef int (*mbulk_cb)(RedisSock*,zval*,size_t TSRMLS_DC); +typedef int (*mbulk_cb)(RedisSock*,zval*,long long TSRMLS_DC); /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); @@ -161,7 +161,7 @@ typedef struct redisCluster { /* The last reply type and length or integer response we got */ REDIS_REPLY_TYPE reply_type; - size_t reply_len; + long long reply_len; /* Last MOVED or ASK redirection response information */ CLUSTER_REDIR_TYPE redir_type; @@ -218,7 +218,7 @@ PHPAPI void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, - size_t count TSRMLS_DC); + long long count TSRMLS_DC); int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - size_t count TSRMLS_DC); + long long count TSRMLS_DC); #endif From d6916a43e02580431c7d4ee8e78198761d7a7cab Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 14:28:17 -0700 Subject: [PATCH 0260/1986] Added zipped with strings multi bulk response handler --- cluster_library.c | 51 ++++++++++++++++++++++++++++++++++++++++++++--- cluster_library.h | 6 ++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 159abeedaa..933b199dc3 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1086,8 +1086,9 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, { zval *z_result; - // Verify our reply type byte is correct - if(c->reply_type != TYPE_MULTIBULK) { + // Verify our reply type byte is correct and that this isn't a NULL + // (e.g. -1 count) multi bulk response. + if(c->reply_type != TYPE_MULTIBULK || c->reply_len == -1) { RETURN_FALSE; } @@ -1123,6 +1124,14 @@ cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { c, mbulk_resp_loop); } +/* For handling responses where we get key, value, key, value that + * we will turn into key => value, key => value. */ +PHPAPI void +cluster_mbulk_resp_zipstr(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, + c, mbulk_resp_loop_zipstr); +} + /* * Various MULTI BULK reply callback functions */ @@ -1150,7 +1159,7 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, /* MULTI BULK response where we unserialize everything */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count - TSRMLS_CC) + TSRMLS_DC) { char *line; int line_len; @@ -1173,4 +1182,40 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count return SUCCESS; } +/* MULTI BULK response where we turn key1,value1 into key1=>value1 */ +int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, + long long count TSRMLS_DC) +{ + char *line, *key; + int line_len; + long long idx=0; + zval *z; + + // Our count wil need to be divisible by 2 + if(count % 2 != 0) { + return -1; + } + + // Iterate through our elements + while(count--) { + // Grab our line, bomb out on failure + line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + if(!line) return -1; + + if(idx % 2 == 0) { + // Save our key + key = line; + } else { + // Attempt unserialization, add value + if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { + add_assoc_zval(z_result, key, z); + } else { + add_assoc_stringl(z_result, key, line, line_len, 0); + } + } + } + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index 1c44ca7909..6b50b6b819 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -215,10 +215,16 @@ PHPAPI void cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c); /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count TSRMLS_DC); int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, long long count TSRMLS_DC); +int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, + long long count TSRMLS_DC); #endif + +/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From d7776a97276a9a8962e6d245c828abfe61993fbd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jun 2014 17:18:36 -0700 Subject: [PATCH 0261/1986] MULTI BULK processing for things like HGETALL and ZRANGE --- cluster_library.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 933b199dc3..3e36b8c17f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1187,7 +1187,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count TSRMLS_DC) { char *line, *key; - int line_len; + int line_len, key_len; long long idx=0; zval *z; @@ -1202,15 +1202,17 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); if(!line) return -1; - if(idx % 2 == 0) { - // Save our key + if(idx++ % 2 == 0) { + // Save our key and length key = line; + key_len = line_len; } else { // Attempt unserialization, add value if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval(z_result, key, z); } else { - add_assoc_stringl(z_result, key, line, line_len, 0); + add_assoc_stringl_ex(z_result, key, 1+key_len, line, + line_len, 0); } } } @@ -1218,4 +1220,34 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, return SUCCESS; } +/* MULTI BULK loop processor where we expect key,score key, score */ +int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, + long long count TSRMLS_DC) +{ + char *line, *key; + int line_len, key_len; + long long idx=0; + double score; + + // Our context will need to be divisible by 2 + if(count %2 != 0) { + return -1; + } + + // While we have elements + while(count--) { + line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + if(!line) return -1; + + if(idx++ % 2 == 0) { + key = line; + key_len = line_len; + } else { + add_assoc_double_ex(z_result, key, 1+key_len, atof(line)); + } + } + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 1b591b8fc9018a3674e345d3bbbce95c782d7950 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Jun 2014 07:36:12 -0700 Subject: [PATCH 0262/1986] HGETALL/INCR/INCRBY/DECR/DECRBY Added zipstr handler for k,v,k2,kv to k=>v,k2=>v2 as well as implemented int based incr and decr commands. --- cluster_library.c | 10 +++++-- cluster_library.h | 5 ++++ redis.c | 73 ++++------------------------------------------- redis_cluster.c | 36 +++++++++++++++++++++++ redis_cluster.h | 7 +++++ redis_commands.c | 35 ++++++++++++++++++++++- redis_commands.h | 3 ++ 7 files changed, 98 insertions(+), 71 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 3e36b8c17f..08f863fb20 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1127,11 +1127,18 @@ cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { /* For handling responses where we get key, value, key, value that * we will turn into key => value, key => value. */ PHPAPI void -cluster_mbulk_resp_zipstr(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { +cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_zipstr); } +/* Handling key,value to key=>value where the values are doubles */ +PHPAPI void +cluster_mbulk_resp_zipdbl(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, + c, mbulk_resp_loop_zipdbl); +} + /* * Various MULTI BULK reply callback functions */ @@ -1227,7 +1234,6 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, char *line, *key; int line_len, key_len; long long idx=0; - double score; // Our context will need to be divisible by 2 if(count %2 != 0) { diff --git a/cluster_library.h b/cluster_library.h index 6b50b6b819..f41231ce63 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -217,6 +217,8 @@ PHPAPI void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); PHPAPI void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c); /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, @@ -225,6 +227,9 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, long long count TSRMLS_DC); int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count TSRMLS_DC); +int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, + long long count TSRMLS_DC); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis.c b/redis.c index 44ae33e718..aa12402701 100644 --- a/redis.c +++ b/redis.c @@ -1181,46 +1181,14 @@ PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *ke /* {{{ proto boolean Redis::incr(string key [,int value]) */ PHP_METHOD(Redis, incr){ - - zval *object; - char *key = NULL; - int key_len; - long val = 1; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", - &object, redis_ce, - &key, &key_len, &val) == FAILURE) { - RETURN_FALSE; - } - - if(val == 1) { - redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INCR", 1); - } else { - redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INCRBY", val); - } + REDIS_PROCESS_KW_CMD("INCR", redis_gen_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::incrBy(string key ,int value) */ PHP_METHOD(Redis, incrBy){ - - zval *object; - char *key = NULL; - int key_len; - long val = 1; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl", - &object, redis_ce, - &key, &key_len, &val) == FAILURE) { - RETURN_FALSE; - } - - if(val == 1) { - redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INCR", 1); - } else { - redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INCRBY", val); - } + REDIS_PROCESS_KW_CMD("INCRBY", redis_gen_key_long_cmd, redis_long_response); } /* }}} */ @@ -1255,48 +1223,17 @@ PHP_METHOD(Redis, incrByFloat) { REDIS_PROCESS_RESPONSE(redis_bulk_double_response); } -/* {{{ proto boolean Redis::decr(string key [,int value]) - */ +/* {{{ proto boolean Redis::decr(string key) */ PHP_METHOD(Redis, decr) { - zval *object; - char *key = NULL; - int key_len; - long val = 1; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", - &object, redis_ce, - &key, &key_len, &val) == FAILURE) { - RETURN_FALSE; - } - - if(val == 1) { - redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DECR", 1); - } else { - redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DECRBY", val); - } + REDIS_PROCESS_KW_CMD("DECR", redis_gen_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::decrBy(string key ,int value) */ PHP_METHOD(Redis, decrBy){ - zval *object; - char *key = NULL; - int key_len; - long val = 1; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl", - &object, redis_ce, - &key, &key_len, &val) == FAILURE) { - RETURN_FALSE; - } - - if(val == 1) { - redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DECR", 1); - } else { - redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DECRBY", val); - } + REDIS_PROCESS_KW_CMD("DECRBY", redis_gen_key_long_cmd, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index ac630294e5..531c139291 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -66,9 +66,14 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hvals, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrank, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incr, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, decr, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incrby, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, decrby, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -410,9 +415,40 @@ PHP_METHOD(RedisCluster, hvals) { } /* }}} */ +/* {{{ proto array RedisCluster::hgetall(string key) */ +PHP_METHOD(RedisCluster, hgetall) { + CLUSTER_PROCESS_KW_CMD("HGETALL", redis_gen_key_cmd, + cluster_mbulk_zipstr_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_gen_key_cmd, cluster_bulk_raw_resp); } +/* {{{ proto long RedisCluster::incr(string key) */ +PHP_METHOD(RedisCluster, incr) { + CLUSTER_PROCESS_KW_CMD("INCR", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::incrby(string key, long byval) */ +PHP_METHOD(RedisCluster, incrby) { + CLUSTER_PROCESS_KW_CMD("INCRBY", redis_gen_key_long_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::decr(string key) */ +PHP_METHOD(RedisCluster, decr) { + CLUSTER_PROCESS_KW_CMD("DECR", redis_gen_key_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::decrby(string key, long byval) */ +PHP_METHOD(RedisCluster, decrby) { + CLUSTER_PROCESS_KW_CMD("DECRBY", redis_gen_key_long_cmd, cluster_long_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 7d6abbe564..fd6b41498d 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -99,4 +99,11 @@ PHP_METHOD(RedisCluster, zrevrank); PHP_METHOD(RedisCluster, hlen); PHP_METHOD(RedisCluster, hkeys); PHP_METHOD(RedisCluster, hvals); +PHP_METHOD(RedisCluster, hgetall); + +PHP_METHOD(RedisCluster, incr); +PHP_METHOD(RedisCluster, decr); +PHP_METHOD(RedisCluster, incrby); +PHP_METHOD(RedisCluster, decrby); + #endif diff --git a/redis_commands.c b/redis_commands.c index 20b00bdc7b..229a139aed 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -177,6 +177,39 @@ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic command construction where we take a key and a long */ +int redis_gen_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key; + int key_len, key_free; + long lval; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &key, &key_len, + &lval)==FAILURE) + { + return FAILURE; + } + + // Prefix key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Disallow zero length keys (for now) + if(key_len == 0) { + if(key_free) efree(key); + return FAILURE; + } + + // Construct our command + *cmd_len = redis_cmd_format_static(cmd, kw, "sl", key, key_len, lval); + + // Set slot if directed + CMD_SET_SLOT(slot, key, key_len); + + // Success! + return SUCCESS; +} + /* Generic command where we take a single key */ int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) @@ -190,6 +223,7 @@ int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } + // Prefix our key key_free = redis_key_prefix(redis_sock, &key, &key_len); // Don't allow an empty key @@ -209,5 +243,4 @@ int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } - /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 50a8f55e5b..9d29460fc7 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -38,6 +38,9 @@ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); +int redis_gen_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); + int redis_gen_ss_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); From 7c31b07e61ebd55965c4663214c42b031297a90f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Jun 2014 07:38:11 -0700 Subject: [PATCH 0263/1986] Re allow for empty keys. According to @antirez, an empty (zero length key) is valid and should be allowed, so allow it as he probably knows :) --- redis_commands.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 229a139aed..9c939f0b81 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -157,13 +157,6 @@ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - // Don't allow setting an empty key - if(key_len == 0) { - if(val_free) STR_FREE(val); - if(key_free) efree(key); - return FAILURE; - } - // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, val_len); @@ -226,12 +219,6 @@ int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix our key key_free = redis_key_prefix(redis_sock, &key, &key_len); - // Don't allow an empty key - if(key_len == 0) { - if(key_free) efree(key); - return FAILURE; - } - // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "s", key, key_len); From 9974c849010fb53428cc132262aff1274a4fe423 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Jun 2014 08:11:01 -0700 Subject: [PATCH 0264/1986] Fixed cluster_long_resp and added increment/decrement commands We were using RETURN_TRUE instead of RETURN_LONG in cluster_long_resp, which was wrong. Added/tested INCR/DECR/INCRBY/DECRBY/INCRBYFLOAT --- cluster_library.c | 2 +- redis_cluster.c | 15 +++++++++++++++ redis_cluster.h | 1 + redis_commands.c | 28 ++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 08f863fb20..1eaec3d0e8 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1055,7 +1055,7 @@ PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) if(c->reply_type != TYPE_INT) { RETURN_FALSE; } - RETURN_TRUE; + RETURN_LONG(c->reply_len); } /* TYPE response handler */ diff --git a/redis_cluster.c b/redis_cluster.c index 531c139291..c0d2339996 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -74,6 +74,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, decr, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, incrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, decrby, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incrbyfloat, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -451,4 +452,18 @@ PHP_METHOD(RedisCluster, decrby) { } /* }}} */ +/* {{{ proto double RedisCluster::incrbyfloat(string key, double val) */ +PHP_METHOD(RedisCluster, incrbyfloat) { + CLUSTER_PROCESS_KW_CMD("INCRBYFLOAT", redis_gen_key_dbl_cmd, + cluster_dbl_resp); +} +/* }}} */ + +/* {{{ proto double RedisCluster::decrbyfloat(string key, double val) */ +PHP_METHOD(RedisCluster, decrbyfloat) { + CLUSTER_PROCESS_KW_CMD("DECRBYFLOAT", redis_gen_key_dbl_cmd, + cluster_dbl_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index fd6b41498d..5affd4b176 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -105,5 +105,6 @@ PHP_METHOD(RedisCluster, incr); PHP_METHOD(RedisCluster, decr); PHP_METHOD(RedisCluster, incrby); PHP_METHOD(RedisCluster, decrby); +PHP_METHOD(RedisCluster, incrbyfloat); #endif diff --git a/redis_commands.c b/redis_commands.c index 9c939f0b81..e19e305a8e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -230,4 +230,32 @@ int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic command where we take a key and a double */ +int redis_gen_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key; + int key_len, key_free; + double val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &key, &key_len, + &val)==FAILURE) + { + return FAILURE; + } + + // Prefix our key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct our command + *cmd_len = redis_cmd_format_static(cmd, kw, "sf", key, key_len, val); + + // Set slot if directed + CMD_SET_SLOT(slot,key,key_len); + + if(key_free) efree(key); + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 9d29460fc7..e257bfe778 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -41,6 +41,9 @@ int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_gen_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); +int redis_gen_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); + int redis_gen_ss_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); From edd34b31ce312533465b863f80715732553a40be Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Jun 2014 08:57:30 -0700 Subject: [PATCH 0265/1986] Removed deprecated functions, implemented expires Removed redis_atomic_increment, and generic_expire_command Added EXPIRE/EXPIREAT/PEXPIRE/PEXPIREAT --- redis.c | 79 ++++--------------------------------------------- redis_cluster.c | 31 +++++++++++++++++++ redis_cluster.h | 5 ++++ 3 files changed, 41 insertions(+), 74 deletions(-) diff --git a/redis.c b/redis.c index aa12402701..c1d5e1d9b2 100644 --- a/redis.c +++ b/redis.c @@ -1143,41 +1143,6 @@ PHP_METHOD(Redis, ping) } /* }}} */ -PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int count) { - - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len; - long val = 1; - int key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", - &object, redis_ce, - &key, &key_len, &val) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - key_free = redis_key_prefix(redis_sock, &key, &key_len); - if (val == 1) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, - key_len); - } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "sl", key, - key_len, val); - } - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); -} - /* {{{ proto boolean Redis::incr(string key [,int value]) */ PHP_METHOD(Redis, incr){ @@ -2822,66 +2787,32 @@ PHP_METHOD(Redis, sortDescAlpha) } /* }}} */ -PHP_REDIS_API void generic_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd, *t; - int key_len, cmd_len, key_free, t_len; - int i; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, &key, &key_len, - &t, &t_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* check that we have a number */ - for(i = 0; i < t_len; ++i) - if(t[i] < '0' || t[i] > '9') - RETURN_FALSE; - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, - key_len, t, t_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); -} - /* {{{ proto array Redis::setTimeout(string key, int timeout) */ PHP_METHOD(Redis, setTimeout) { - generic_expire_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "EXPIRE", sizeof("EXPIRE")-1); + REDIS_PROCESS_KW_CMD("EXPIRE", redis_gen_key_long_cmd, redis_1_response); } +/* }}} */ PHP_METHOD(Redis, pexpire) { - generic_expire_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PEXPIRE", sizeof("PEXPIRE")-1); + REDIS_PROCESS_KW_CMD("PEXPIRE", redis_gen_key_long_cmd, redis_1_response); } /* }}} */ /* {{{ proto array Redis::expireAt(string key, int timestamp) */ PHP_METHOD(Redis, expireAt) { - generic_expire_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "EXPIREAT", sizeof("EXPIREAT")-1); + REDIS_PROCESS_KW_CMD("EXPIREAT", redis_gen_key_long_cmd, redis_1_response); } /* }}} */ /* {{{ proto array Redis::pexpireAt(string key, int timestamp) */ PHP_METHOD(Redis, pexpireAt) { - generic_expire_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PEXPIREAT", sizeof("PEXPIREAT")-1); + REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_gen_key_long_cmd, redis_1_response); } /* }}} */ - /* {{{ proto array Redis::lSet(string key, int index, string value) */ PHP_METHOD(Redis, lSet) { diff --git a/redis_cluster.c b/redis_cluster.c index c0d2339996..3a10f5158a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -75,6 +75,12 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, incrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, decrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, incrbyfloat, NULL, ZEND_ACC_PUBLIC) + + PHP_ME(RedisCluster, expire, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pexpire, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, expireat, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pexpireat, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} }; @@ -466,4 +472,29 @@ PHP_METHOD(RedisCluster, decrbyfloat) { } /* }}} */ +/* {{{ proto bool RedisCluster::expire(string key, long sec) */ +PHP_METHOD(RedisCluster, expire) { + CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_gen_key_long_cmd, cluster_1_resp); +} +/* }}} */ + +/* {{{ proto bool RedisCluster::expireat(string key, long ts) */ +PHP_METHOD(RedisCluster, expireat) { + CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_gen_key_long_cmd, cluster_1_resp); +} + +/* {{{ proto bool RedisCluster::pexpire(string key, long ms) */ +PHP_METHOD(RedisCluster, pexpire) { + CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_gen_key_long_cmd, cluster_1_resp); +} +/* }}} */ + +/* {{{ proto bool RedisCluster::pexpireat(string key, long ts) */ +PHP_METHOD(RedisCluster, pexpireat) { + CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_gen_key_long_cmd, cluster_1_resp); +} +/* }}} */ + + + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 5affd4b176..b05bc4f1a6 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -107,4 +107,9 @@ PHP_METHOD(RedisCluster, incrby); PHP_METHOD(RedisCluster, decrby); PHP_METHOD(RedisCluster, incrbyfloat); +PHP_METHOD(RedisCluster, expire); +PHP_METHOD(RedisCluster, expireat); +PHP_METHOD(RedisCluster, pexpire); +PHP_METHOD(RedisCluster, pexpireat); + #endif From b769156932c6ad8d5207265e10b524b064aa135d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Jun 2014 09:04:16 -0700 Subject: [PATCH 0266/1986] APPEND command --- redis.c | 29 +++-------------------------- redis_cluster.c | 10 ++++++++-- redis_cluster.h | 2 ++ 3 files changed, 13 insertions(+), 28 deletions(-) diff --git a/redis.c b/redis.c index c1d5e1d9b2..d95a7bc747 100644 --- a/redis.c +++ b/redis.c @@ -1378,35 +1378,12 @@ PHP_METHOD(Redis, type) } /* }}} */ +/* {{{ proto long Redis::append(string key, string val) */ PHP_METHOD(Redis, append) { - zval *object; - RedisSock *redis_sock; - char *cmd; - int cmd_len, key_len, val_len, key_free; - char *key, *val; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, - &key, &key_len, &val, &val_len) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "APPEND", "ss", key, - key_len, val, val_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("APPEND", redis_gen_kv_cmd, redis_long_response); } +/* }}} */ PHP_METHOD(Redis, getRange) { diff --git a/redis_cluster.c b/redis_cluster.c index 3a10f5158a..65e3effa90 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -80,7 +80,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, pexpire, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, expireat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pexpireat, NULL, ZEND_ACC_PUBLIC) - + + PHP_ME(RedisCluster, append, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} }; @@ -495,6 +497,10 @@ PHP_METHOD(RedisCluster, pexpireat) { } /* }}} */ - +/* {{{ proto long RedisCluster::append(string key, string val) */ +PHP_METHOD(RedisCluster, append) { + CLUSTER_PROCESS_KW_CMD("APPEND", redis_gen_kv_cmd, cluster_long_resp); +} +/* }}} */ /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index b05bc4f1a6..d874866266 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -112,4 +112,6 @@ PHP_METHOD(RedisCluster, expireat); PHP_METHOD(RedisCluster, pexpire); PHP_METHOD(RedisCluster, pexpireat); +PHP_METHOD(RedisCluster, append); + #endif From cdc74930c83ba45c1fe16828478289f0aceb0697 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Jun 2014 09:14:32 -0700 Subject: [PATCH 0267/1986] Updated MOVE in Redis proper Updated move command in redis proper to use the new calling method, but didn't implement in cluster as it's not allowed --- redis.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/redis.c b/redis.c index d95a7bc747..d1b730abce 100644 --- a/redis.c +++ b/redis.c @@ -3073,34 +3073,7 @@ PHP_METHOD(Redis, select) { /* {{{ proto bool Redis::move(string key, long dbindex) */ PHP_METHOD(Redis, move) { - - zval *object; - RedisSock *redis_sock; - - char *cmd, *key; - int cmd_len, key_len, key_free; - long dbNumber; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl", - &object, redis_ce, &key, &key_len, &dbNumber) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "MOVE", "sd", key, key_len, - dbNumber); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); - + REDIS_PROCESS_KW_CMD("MOVE", redis_gen_key_long_cmd, redis_1_response); } /* }}} */ From b15acc0ea7ac0bd199d3dc271ea93cbdf3722df6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Jun 2014 09:18:07 -0700 Subject: [PATCH 0268/1986] Updated INCRBYFLOAT in Redis proper to use new convention --- redis.c | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/redis.c b/redis.c index d1b730abce..2ae7d848b7 100644 --- a/redis.c +++ b/redis.c @@ -1160,33 +1160,10 @@ PHP_METHOD(Redis, incrBy){ /* {{{ proto float Redis::incrByFloat(string key, float value) */ PHP_METHOD(Redis, incrByFloat) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - double val; - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osd", - &object, redis_ce, &key, &key_len, &val) == FAILURE) { - RETURN_FALSE; - } - - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* Prefix key, format command, free old key if necissary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, - key_len, val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_bulk_double_response); + REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_gen_key_dbl_cmd, + redis_bulk_double_response); } +/* }}} */ /* {{{ proto boolean Redis::decr(string key) */ PHP_METHOD(Redis, decr) From 91b57f7d1fbf47fba5bd972723760138c8048c55 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 06:53:42 -0700 Subject: [PATCH 0269/1986] Rework naming convention for command construction --- redis.c | 141 +++++++++++++++-------------------------------- redis_cluster.c | 95 +++++++++++++++++-------------- redis_cluster.h | 3 + redis_commands.c | 10 ++-- redis_commands.h | 12 ++-- 5 files changed, 113 insertions(+), 148 deletions(-) diff --git a/redis.c b/redis.c index 2ae7d848b7..2a0d90c220 100644 --- a/redis.c +++ b/redis.c @@ -941,21 +941,21 @@ PHP_METHOD(Redis, set) { */ PHP_METHOD(Redis, setex) { - REDIS_PROCESS_KW_CMD("SETEX", redis_gen_setex_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("SETEX", redis_setex_cmd, redis_string_response); } /* {{{ proto boolean Redis::psetex(string key, long expire, string value) */ PHP_METHOD(Redis, psetex) { - REDIS_PROCESS_KW_CMD("PSETEX", redis_gen_setex_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("PSETEX", redis_setex_cmd, redis_string_response); } /* {{{ proto boolean Redis::setnx(string key, string value) */ PHP_METHOD(Redis, setnx) { - REDIS_PROCESS_KW_CMD("SETNX", redis_gen_kv_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("SETNX", redis_kv_cmd, redis_1_response); } /* }}} */ @@ -963,7 +963,7 @@ PHP_METHOD(Redis, setnx) */ PHP_METHOD(Redis, getSet) { - REDIS_PROCESS_KW_CMD("GETSET", redis_gen_kv_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("GETSET", redis_kv_cmd, redis_string_response); } /* }}} */ @@ -1110,7 +1110,7 @@ PHP_METHOD(Redis, renameNx) */ PHP_METHOD(Redis, get) { - REDIS_PROCESS_KW_CMD("GET", redis_gen_key_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response); } /* }}} */ @@ -1146,21 +1146,21 @@ PHP_METHOD(Redis, ping) /* {{{ proto boolean Redis::incr(string key [,int value]) */ PHP_METHOD(Redis, incr){ - REDIS_PROCESS_KW_CMD("INCR", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("INCR", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::incrBy(string key ,int value) */ PHP_METHOD(Redis, incrBy){ - REDIS_PROCESS_KW_CMD("INCRBY", redis_gen_key_long_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, redis_long_response); } /* }}} */ /* {{{ proto float Redis::incrByFloat(string key, float value) */ PHP_METHOD(Redis, incrByFloat) { - REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_gen_key_dbl_cmd, + REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, redis_bulk_double_response); } /* }}} */ @@ -1168,14 +1168,14 @@ PHP_METHOD(Redis, incrByFloat) { /* {{{ proto boolean Redis::decr(string key) */ PHP_METHOD(Redis, decr) { - REDIS_PROCESS_KW_CMD("DECR", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("DECR", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto boolean Redis::decrBy(string key ,int value) */ PHP_METHOD(Redis, decrBy){ - REDIS_PROCESS_KW_CMD("DECRBY", redis_gen_key_long_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, redis_long_response); } /* }}} */ @@ -1267,7 +1267,7 @@ PHP_METHOD(Redis, getMultiple) */ PHP_METHOD(Redis, exists) { - REDIS_PROCESS_KW_CMD("EXISTS", redis_gen_key_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("EXISTS", redis_key_cmd, redis_1_response); } /* }}} */ @@ -1342,7 +1342,7 @@ PHP_METHOD(Redis, unwatch) */ PHP_METHOD(Redis, getKeys) { - REDIS_PROCESS_KW_CMD("KEYS", redis_gen_key_cmd, + REDIS_PROCESS_KW_CMD("KEYS", redis_key_cmd, redis_sock_read_multibulk_reply_raw); } /* }}} */ @@ -1351,14 +1351,14 @@ PHP_METHOD(Redis, getKeys) */ PHP_METHOD(Redis, type) { - REDIS_PROCESS_KW_CMD("TYPE", redis_gen_key_cmd, redis_type_response); + REDIS_PROCESS_KW_CMD("TYPE", redis_key_cmd, redis_type_response); } /* }}} */ /* {{{ proto long Redis::append(string key, string val) */ PHP_METHOD(Redis, append) { - REDIS_PROCESS_KW_CMD("APPEND", redis_gen_kv_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("APPEND", redis_kv_cmd, redis_long_response); } /* }}} */ @@ -1420,39 +1420,12 @@ PHP_METHOD(Redis, setRange) REDIS_PROCESS_RESPONSE(redis_long_response); } +/* {{{ proto long Redis::getbit(string key, long idx) */ PHP_METHOD(Redis, getBit) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long offset; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl", - &object, redis_ce, &key, &key_len, - &offset) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* GETBIT and SETBIT only work for 0 - 2^32-1 */ - if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd", key, - key_len, (int)offset); - if(key_free) efree(key); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, redis_long_response); } +/* }}} */ PHP_METHOD(Redis, setBit) { @@ -1492,7 +1465,7 @@ PHP_METHOD(Redis, setBit) /* {{{ proto long Redis::strlen(string key) */ PHP_METHOD(Redis, strlen) { - REDIS_PROCESS_KW_CMD("STRLEN", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("STRLEN", redis_key_cmd, redis_long_response); } /* }}} */ @@ -1582,14 +1555,14 @@ PHP_METHOD(Redis, lInsert) /* {{{ proto long Redis::lPushx(string key, mixed value) */ PHP_METHOD(Redis, lPushx) { - REDIS_PROCESS_KW_CMD("LPUSHX", redis_gen_kv_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_string_response); } /* }}} */ /* {{{ proto long Redis::rPushx(string key, mixed value) */ PHP_METHOD(Redis, rPushx) { - REDIS_PROCESS_KW_CMD("RPUSHX", redis_gen_kv_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, redis_string_response); } /* }}} */ @@ -1598,7 +1571,7 @@ PHP_METHOD(Redis, rPushx) */ PHP_METHOD(Redis, lPop) { - REDIS_PROCESS_KW_CMD("LPOP", redis_gen_key_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("LPOP", redis_key_cmd, redis_string_response); } /* }}} */ @@ -1606,7 +1579,7 @@ PHP_METHOD(Redis, lPop) */ PHP_METHOD(Redis, rPop) { - REDIS_PROCESS_KW_CMD("RPOP", redis_gen_key_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("RPOP", redis_key_cmd, redis_string_response); } /* }}} */ @@ -1658,7 +1631,7 @@ PHP_METHOD(Redis, brPop) */ PHP_METHOD(Redis, lSize) { - REDIS_PROCESS_KW_CMD("LLEN", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("LLEN", redis_key_cmd, redis_long_response); } /* }}} */ @@ -1740,33 +1713,7 @@ PHP_METHOD(Redis, listTrim) */ PHP_METHOD(Redis, lGet) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long index; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl", - &object, redis_ce, - &key, &key_len, &index) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* LINDEX key pos */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LINDEX", "sd", key, - key_len, (int)index); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); + REDIS_PROCESS_KW_CMD("LGET", redis_key_long_cmd, redis_string_response); } /* }}} */ @@ -1826,7 +1773,7 @@ PHP_METHOD(Redis, sAdd) /* {{{ proto int Redis::sSize(string key) */ PHP_METHOD(Redis, sSize) { - REDIS_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, redis_long_response); } /* }}} */ @@ -1892,7 +1839,7 @@ PHP_METHOD(Redis, sMove) */ PHP_METHOD(Redis, sPop) { - REDIS_PROCESS_KW_CMD("SPOP", redis_gen_key_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("SPOP", redis_key_cmd, redis_string_response); } /* }}} */ @@ -1962,7 +1909,7 @@ PHP_METHOD(Redis, sRandMember) */ PHP_METHOD(Redis, sContains) { - REDIS_PROCESS_KW_CMD("SISMEMBER", redis_gen_kv_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, redis_1_response); } /* }}} */ @@ -1970,7 +1917,7 @@ PHP_METHOD(Redis, sContains) */ PHP_METHOD(Redis, sMembers) { - REDIS_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, + REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, redis_sock_read_multibulk_reply); } /* }}} */ @@ -2744,26 +2691,26 @@ PHP_METHOD(Redis, sortDescAlpha) /* {{{ proto array Redis::setTimeout(string key, int timeout) */ PHP_METHOD(Redis, setTimeout) { - REDIS_PROCESS_KW_CMD("EXPIRE", redis_gen_key_long_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, redis_1_response); } /* }}} */ PHP_METHOD(Redis, pexpire) { - REDIS_PROCESS_KW_CMD("PEXPIRE", redis_gen_key_long_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, redis_1_response); } /* }}} */ /* {{{ proto array Redis::expireAt(string key, int timestamp) */ PHP_METHOD(Redis, expireAt) { - REDIS_PROCESS_KW_CMD("EXPIREAT", redis_gen_key_long_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, redis_1_response); } /* }}} */ /* {{{ proto array Redis::pexpireAt(string key, int timestamp) */ PHP_METHOD(Redis, pexpireAt) { - REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_gen_key_long_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, redis_1_response); } /* }}} */ @@ -2950,7 +2897,7 @@ PHP_METHOD(Redis, auth) { /* {{{ proto long Redis::persist(string key) */ PHP_METHOD(Redis, persist) { - REDIS_PROCESS_KW_CMD("PERSIST", redis_gen_key_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("PERSIST", redis_key_cmd, redis_1_response); } /* }}} */ @@ -2958,14 +2905,14 @@ PHP_METHOD(Redis, persist) { /* {{{ proto long Redis::ttl(string key) */ PHP_METHOD(Redis, ttl) { - REDIS_PROCESS_KW_CMD("TTL", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("TTL", redis_key_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::pttl(string key) */ PHP_METHOD(Redis, pttl) { - REDIS_PROCESS_KW_CMD("PTTL", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("PTTL", redis_key_cmd, redis_long_response); } /* }}} */ @@ -3050,7 +2997,7 @@ PHP_METHOD(Redis, select) { /* {{{ proto bool Redis::move(string key, long dbindex) */ PHP_METHOD(Redis, move) { - REDIS_PROCESS_KW_CMD("MOVE", redis_gen_key_long_cmd, redis_1_response); + REDIS_PROCESS_KW_CMD("MOVE", redis_key_long_cmd, redis_1_response); } /* }}} */ @@ -3744,7 +3691,7 @@ PHP_METHOD(Redis, zRangeByLex) { */ PHP_METHOD(Redis, zCard) { - REDIS_PROCESS_KW_CMD("ZCARD", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("ZCARD", redis_key_cmd, redis_long_response); } /* }}} */ @@ -3752,7 +3699,7 @@ PHP_METHOD(Redis, zCard) */ PHP_METHOD(Redis, zScore) { - REDIS_PROCESS_KW_CMD("ZSCORE", redis_gen_kv_cmd, + REDIS_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, redis_bulk_double_response); } /* }}} */ @@ -3760,13 +3707,13 @@ PHP_METHOD(Redis, zScore) /* {{{ proto long Redis::zRank(string key, string member) */ PHP_METHOD(Redis, zRank) { - REDIS_PROCESS_KW_CMD("ZRANK", redis_gen_kv_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::zRevRank(string key, string member) */ PHP_METHOD(Redis, zRevRank) { - REDIS_PROCESS_KW_CMD("ZREVRANK", redis_gen_kv_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, redis_long_response); } /* }}} */ @@ -4068,7 +4015,7 @@ PHP_METHOD(Redis, hGet) /* hLen */ PHP_METHOD(Redis, hLen) { - REDIS_PROCESS_KW_CMD("HLEN", redis_gen_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("HLEN", redis_key_cmd, redis_long_response); } /* }}} */ @@ -4176,7 +4123,7 @@ generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_ /* {{{ proto array Redis::hkeys(string key) */ PHP_METHOD(Redis, hKeys) { - REDIS_PROCESS_KW_CMD("HKEYS", redis_gen_key_cmd, + REDIS_PROCESS_KW_CMD("HKEYS", redis_key_cmd, redis_sock_read_multibulk_reply_raw); } /* }}} */ @@ -4184,7 +4131,7 @@ PHP_METHOD(Redis, hKeys) /* {{{ proto array Redis::hvals(string key) */ PHP_METHOD(Redis, hVals) { - REDIS_PROCESS_KW_CMD("HVALS", redis_gen_key_cmd, + REDIS_PROCESS_KW_CMD("HVALS", redis_key_cmd, redis_sock_read_multibulk_reply); } @@ -5857,7 +5804,7 @@ PHP_METHOD(Redis, script) { /* {{{ proto DUMP key */ PHP_METHOD(Redis, dump) { - REDIS_PROCESS_KW_CMD("DUMP", redis_gen_key_cmd, redis_ping_response); + REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_ping_response); } /* {{{ proto Redis::DEBUG(string key) */ diff --git a/redis_cluster.c b/redis_cluster.c index 65e3effa90..691e86429f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -83,6 +83,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, append, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getbit, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lget, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} }; @@ -249,7 +252,7 @@ PHP_METHOD(RedisCluster, __construct) { /* {{{ proto string RedisCluster::get(string key) */ PHP_METHOD(RedisCluster, get) { - CLUSTER_PROCESS_KW_CMD("GET", redis_gen_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp); } /* }}} */ @@ -261,31 +264,31 @@ PHP_METHOD(RedisCluster, set) { /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { - CLUSTER_PROCESS_KW_CMD("SETEX", redis_gen_setex_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("SETEX", redis_setex_cmd, cluster_bool_resp); } /* }}} */ /* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, psetex) { - CLUSTER_PROCESS_KW_CMD("PSETEX", redis_gen_setex_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("PSETEX", redis_setex_cmd, cluster_bool_resp); } /* }}} */ /* {{{ proto bool RedisCluster::setnx(string key, string value) */ PHP_METHOD(RedisCluster, setnx) { - CLUSTER_PROCESS_KW_CMD("SETNX", redis_gen_kv_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("SETNX", redis_kv_cmd, cluster_1_resp); } /* }}} */ /* {{{ proto string RedisCluster::getSet(string key, string value) */ PHP_METHOD(RedisCluster, getset) { - CLUSTER_PROCESS_KW_CMD("GETSET", redis_gen_kv_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("GETSET", redis_kv_cmd, cluster_bulk_resp); } /* }}} */ /* {{{ proto int RedisCluster::exists(string key) */ PHP_METHOD(RedisCluster, exists) { - CLUSTER_PROCESS_KW_CMD("EXISTS", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("EXISTS", redis_key_cmd, cluster_long_resp); } /* }}} */ @@ -300,206 +303,218 @@ PHP_METHOD(RedisCluster, keys) { /* {{{ proto int RedisCluster::type(string key) */ PHP_METHOD(RedisCluster, type) { - CLUSTER_PROCESS_KW_CMD("TYPE", redis_gen_key_cmd, cluster_type_resp); + CLUSTER_PROCESS_KW_CMD("TYPE", redis_key_cmd, cluster_type_resp); } /* }}} */ /* {{{ proto string RedisCluster::pop(string key) */ PHP_METHOD(RedisCluster, lpop) { - CLUSTER_PROCESS_KW_CMD("LPOP", redis_gen_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("LPOP", redis_key_cmd, cluster_bulk_resp); } /* }}} */ /* {{{ proto string RedisCluster::rpop(string key) */ PHP_METHOD(RedisCluster, rpop) { - CLUSTER_PROCESS_KW_CMD("RPOP", redis_gen_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("RPOP", redis_key_cmd, cluster_bulk_resp); } /* }}} */ /* {{{ proto string RedisCluster::spop(string key) */ PHP_METHOD(RedisCluster, spop) { - CLUSTER_PROCESS_KW_CMD("SPOP", redis_gen_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_cmd, cluster_bulk_resp); } /* }}} */ /* {{{ proto string RedisCluster::strlen(string key) */ PHP_METHOD(RedisCluster, strlen) { - CLUSTER_PROCESS_KW_CMD("STRLEN", redis_gen_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_bulk_resp); } /* {{{ proto long RedisCluster::rpushx(string key, mixed value) */ PHP_METHOD(RedisCluster, rpushx) { - CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_gen_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::lpushx(string key, mixed value) */ PHP_METHOD(RedisCluster, lpushx) { - CLUSTER_PROCESS_KW_CMD("LPUSHX", redis_gen_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::llen(string key) */ PHP_METHOD(RedisCluster, llen) { - CLUSTER_PROCESS_KW_CMD("LLEN", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("LLEN", redis_key_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::scard(string key) */ PHP_METHOD(RedisCluster, scard) { - CLUSTER_PROCESS_KW_CMD("SCARD", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("SCARD", redis_key_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto array RedisCluster::smembers(string key) */ PHP_METHOD(RedisCluster, smembers) { - CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_gen_key_cmd, cluster_mbulk_resp); + CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, cluster_mbulk_resp); } /* }}} */ /* {{{ proto long RedisCluster::sismember(string key) */ PHP_METHOD(RedisCluster, sismember) { - CLUSTER_PROCESS_KW_CMD("SISMEMBER", redis_gen_kv_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, cluster_1_resp); } /* }}} */ /* {{{ proto bool RedisCluster::persist(string key) */ PHP_METHOD(RedisCluster, persist) { - CLUSTER_PROCESS_KW_CMD("PERSIST", redis_gen_key_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("PERSIST", redis_key_cmd, cluster_1_resp); } /* }}} */ /* {{{ proto long RedisCluster::ttl(string key) */ PHP_METHOD(RedisCluster, ttl) { - CLUSTER_PROCESS_KW_CMD("TTL", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("TTL", redis_key_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::pttl(string key) */ PHP_METHOD(RedisCluster, pttl) { - CLUSTER_PROCESS_KW_CMD("PTTL", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("PTTL", redis_key_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::zcard(string key) */ PHP_METHOD(RedisCluster, zcard) { - CLUSTER_PROCESS_KW_CMD("ZCARD", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZCARD", redis_key_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto double RedisCluster::zscore(string key) */ PHP_METHOD(RedisCluster, zscore) { - CLUSTER_PROCESS_KW_CMD("ZSCORE", redis_gen_kv_cmd, cluster_dbl_resp); + CLUSTER_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, cluster_dbl_resp); } /* }}} */ /* {{{ proto long RedisCluster::zrank(string key, mixed member) */ PHP_METHOD(RedisCluster, zrank) { - CLUSTER_PROCESS_KW_CMD("ZRANK", redis_gen_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::zrevrank(string key, mixed member) */ PHP_METHOD(RedisCluster, zrevrank) { - CLUSTER_PROCESS_KW_CMD("ZREVRANK", redis_gen_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::hlen(string key) */ PHP_METHOD(RedisCluster, hlen) { - CLUSTER_PROCESS_KW_CMD("HLEN", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("HLEN", redis_key_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto array RedisCluster::hkeys(string key) */ PHP_METHOD(RedisCluster, hkeys) { - CLUSTER_PROCESS_KW_CMD("HKEYS", redis_gen_key_cmd, cluster_mbulk_resp_raw); + CLUSTER_PROCESS_KW_CMD("HKEYS", redis_key_cmd, cluster_mbulk_resp_raw); } /* }}} */ /* {{{ proto array RedisCluster::hvals(string key) */ PHP_METHOD(RedisCluster, hvals) { - CLUSTER_PROCESS_KW_CMD("HVALS", redis_gen_key_cmd, cluster_mbulk_resp); + CLUSTER_PROCESS_KW_CMD("HVALS", redis_key_cmd, cluster_mbulk_resp); } /* }}} */ /* {{{ proto array RedisCluster::hgetall(string key) */ PHP_METHOD(RedisCluster, hgetall) { - CLUSTER_PROCESS_KW_CMD("HGETALL", redis_gen_key_cmd, + CLUSTER_PROCESS_KW_CMD("HGETALL", redis_key_cmd, cluster_mbulk_zipstr_resp); } /* }}} */ /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { - CLUSTER_PROCESS_KW_CMD("DUMP", redis_gen_key_cmd, cluster_bulk_raw_resp); + CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp); } /* {{{ proto long RedisCluster::incr(string key) */ PHP_METHOD(RedisCluster, incr) { - CLUSTER_PROCESS_KW_CMD("INCR", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("INCR", redis_key_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::incrby(string key, long byval) */ PHP_METHOD(RedisCluster, incrby) { - CLUSTER_PROCESS_KW_CMD("INCRBY", redis_gen_key_long_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::decr(string key) */ PHP_METHOD(RedisCluster, decr) { - CLUSTER_PROCESS_KW_CMD("DECR", redis_gen_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("DECR", redis_key_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto long RedisCluster::decrby(string key, long byval) */ PHP_METHOD(RedisCluster, decrby) { - CLUSTER_PROCESS_KW_CMD("DECRBY", redis_gen_key_long_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, cluster_long_resp); } /* }}} */ /* {{{ proto double RedisCluster::incrbyfloat(string key, double val) */ PHP_METHOD(RedisCluster, incrbyfloat) { - CLUSTER_PROCESS_KW_CMD("INCRBYFLOAT", redis_gen_key_dbl_cmd, + CLUSTER_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, cluster_dbl_resp); } /* }}} */ /* {{{ proto double RedisCluster::decrbyfloat(string key, double val) */ PHP_METHOD(RedisCluster, decrbyfloat) { - CLUSTER_PROCESS_KW_CMD("DECRBYFLOAT", redis_gen_key_dbl_cmd, + CLUSTER_PROCESS_KW_CMD("DECRBYFLOAT", redis_key_dbl_cmd, cluster_dbl_resp); } /* }}} */ /* {{{ proto bool RedisCluster::expire(string key, long sec) */ PHP_METHOD(RedisCluster, expire) { - CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_gen_key_long_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, cluster_1_resp); } /* }}} */ /* {{{ proto bool RedisCluster::expireat(string key, long ts) */ PHP_METHOD(RedisCluster, expireat) { - CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_gen_key_long_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, cluster_1_resp); } /* {{{ proto bool RedisCluster::pexpire(string key, long ms) */ PHP_METHOD(RedisCluster, pexpire) { - CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_gen_key_long_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, cluster_1_resp); } /* }}} */ /* {{{ proto bool RedisCluster::pexpireat(string key, long ts) */ PHP_METHOD(RedisCluster, pexpireat) { - CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_gen_key_long_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, cluster_1_resp); } /* }}} */ /* {{{ proto long RedisCluster::append(string key, string val) */ PHP_METHOD(RedisCluster, append) { - CLUSTER_PROCESS_KW_CMD("APPEND", redis_gen_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("APPEND", redis_kv_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::getbit(string key, long val) */ +PHP_METHOD(RedisCluster, getbit) { + CLUSTER_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto string Redis::lget(string key, long index) */ +PHP_METHOD(RedisCluster, lget) { + CLUSTER_PROCESS_KW_CMD("LGET", redis_key_long_cmd, cluster_bulk_resp); } /* }}} */ diff --git a/redis_cluster.h b/redis_cluster.h index d874866266..c903f3bdf4 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -114,4 +114,7 @@ PHP_METHOD(RedisCluster, pexpireat); PHP_METHOD(RedisCluster, append); +PHP_METHOD(RedisCluster, getbit); +PHP_METHOD(RedisCluster, lget); + #endif diff --git a/redis_commands.c b/redis_commands.c index e19e305a8e..1127ef4510 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -109,7 +109,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* SETEX / PSETEX */ int -redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +redis_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { char *key = NULL, *val=NULL; @@ -141,7 +141,7 @@ redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Generic command construction when we just take a key and value */ -int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { char *key, *val; @@ -171,7 +171,7 @@ int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Generic command construction where we take a key and a long */ -int redis_gen_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { char *key; @@ -204,7 +204,7 @@ int redis_gen_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Generic command where we take a single key */ -int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { char *key; @@ -231,7 +231,7 @@ int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Generic command where we take a key and a double */ -int redis_gen_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { char *key; diff --git a/redis_commands.h b/redis_commands.h index e257bfe778..e4d0b6045f 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -29,22 +29,22 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot); -int redis_gen_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); -int redis_gen_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); -int redis_gen_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); -int redis_gen_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); -int redis_gen_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); -int redis_gen_ss_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_ss_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); #endif From be1efe32c4186a3a7a3e2973cba822cc6e92e4db Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 07:25:40 -0700 Subject: [PATCH 0270/1986] GETRANGE/LRANGE/LTRIM/ZREMRANGEBYRANK --- redis.c | 116 ++++------------------------------------------- redis_cluster.c | 33 +++++++++++++- redis_cluster.h | 4 ++ redis_commands.c | 29 ++++++++++++ redis_commands.h | 3 ++ 5 files changed, 77 insertions(+), 108 deletions(-) diff --git a/redis.c b/redis.c index 2a0d90c220..e1cd7005ac 100644 --- a/redis.c +++ b/redis.c @@ -1362,34 +1362,13 @@ PHP_METHOD(Redis, append) } /* }}} */ +/* {{{ proto string Redis::GetRange(string key, long start, long end) */ PHP_METHOD(Redis, getRange) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long start, end; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll", - &object, redis_ce, &key, &key_len, - &start, &end) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "GETRANGE", "sdd", key, - key_len, (int)start, (int)end); - if(key_free) efree(key); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); + REDIS_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd, + redis_string_response); } +/* }}} */ PHP_METHOD(Redis, setRange) { @@ -1679,33 +1658,7 @@ PHP_METHOD(Redis, lRemove) */ PHP_METHOD(Redis, listTrim) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long start, end; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll", - &object, redis_ce, &key, &key_len, - &start, &end) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LTRIM", "sdd", key, - key_len, (int)start, (int)end); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); - + REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, redis_boolean_response); } /* }}} */ @@ -1721,34 +1674,8 @@ PHP_METHOD(Redis, lGet) */ PHP_METHOD(Redis, lGetRange) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long start, end; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll", - &object, redis_ce, - &key, &key_len, &start, &end) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* LRANGE key start end */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LRANGE", "sdd", key, - key_len, (int)start, (int)end); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); - + REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, + redis_sock_read_multibulk_reply); } /* }}} */ @@ -3397,33 +3324,8 @@ PHP_METHOD(Redis, zDeleteRangeByScore) */ PHP_METHOD(Redis, zDeleteRangeByRank) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long start, end; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll", - &object, redis_ce, - &key, &key_len, &start, &end) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYRANK", "sdd", - key, key_len, (int)start, (int)end); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - + REDIS_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd, + redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 691e86429f..197bce0b99 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -85,7 +85,11 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, getbit, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lget, NULL, ZEND_ACC_PUBLIC) - + + PHP_ME(RedisCluster, getrange, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ltrim, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lrange, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zremrangebyrank, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -518,4 +522,31 @@ PHP_METHOD(RedisCluster, lget) { } /* }}} */ +/* {{{ proto string RedisCluster::getrange(string key, long start, long end) */ +PHP_METHOD(RedisCluster, getrange) { + CLUSTER_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd, + cluster_bulk_resp); +} +/* }}} */ + +/* {{{ proto string RedisCluster::ltrim(string key, long start, long end) */ +PHP_METHOD(RedisCluster, ltrim) { + CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp); +} +/* }}} */ + +/* {{{ proto array RedisCluster::lrange(string key, long start, long end) */ +PHP_METHOD(RedisCluster, lrange) { + CLUSTER_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, + cluster_mbulk_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::zremrangebyrank(string k, long s, long e) */ +PHP_METHOD(RedisCluster, zremrangebyrank) { + CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd, + cluster_long_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index c903f3bdf4..5887178fca 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -117,4 +117,8 @@ PHP_METHOD(RedisCluster, append); PHP_METHOD(RedisCluster, getbit); PHP_METHOD(RedisCluster, lget); +PHP_METHOD(RedisCluster, getrange); +PHP_METHOD(RedisCluster, ltrim); +PHP_METHOD(RedisCluster, lrange); +PHP_METHOD(RedisCluster, zremrangebyrank); #endif diff --git a/redis_commands.c b/redis_commands.c index 1127ef4510..f348e00186 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -203,6 +203,35 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* key, long, long */ +int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key; + int key_len, key_free; + long val1, val2; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &key, &key_len, + &val1, &val2)==FAILURE) + { + return FAILURE; + } + + // Prefix our key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, kw, "sll", key, key_len, val1, + val2); + + // Set slot + CMD_SET_SLOT(slot,key,key_len); + + if(key_free) efree(key); + + return SUCCESS; +} + /* Generic command where we take a single key */ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) diff --git a/redis_commands.h b/redis_commands.h index e4d0b6045f..52d01e83c7 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -41,6 +41,9 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); +int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); + int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); From 82cad13a8a413b108f28f85feb0a0b8e9e4d3955 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 07:59:22 -0700 Subject: [PATCH 0271/1986] HEXISTS/HGETALL Also removed deprecated generic functions in Redis proper --- redis.c | 71 ++++-------------------------------------------- redis_cluster.c | 7 +++++ redis_cluster.h | 1 + redis_commands.c | 26 ++++++++++++++++++ redis_commands.h | 5 +++- 5 files changed, 44 insertions(+), 66 deletions(-) diff --git a/redis.c b/redis.c index e1cd7005ac..036c9c771a 100644 --- a/redis.c +++ b/redis.c @@ -3966,60 +3966,10 @@ PHP_METHOD(Redis, hDel) REDIS_PROCESS_RESPONSE(redis_long_response); } -/* hExists */ +/* {{{ proto bool Redis::hExists(string key, string mem) */ PHP_METHOD(Redis, hExists) { - char *cmd; - int cmd_len; - RedisSock *redis_sock = generic_hash_command_2(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HEXISTS", 7, &cmd, &cmd_len); - if(!redis_sock) - RETURN_FALSE; - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); - -} - -PHP_REDIS_API RedisSock* -generic_hash_command_1(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) { - - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - ZVAL_BOOL(return_value, 0); - return NULL; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - ZVAL_BOOL(return_value, 0); - return NULL; - } - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "s", key, - key_len); - if(key_free) efree(key); - - /* call REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) without breaking the return value */ - IF_MULTI_OR_ATOMIC() { - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return NULL; - } - efree(cmd); - } - IF_PIPELINE() { - PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); - efree(cmd); - } - return redis_sock; + REDIS_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, redis_1_response); } /* {{{ proto array Redis::hkeys(string key) */ @@ -4037,21 +3987,12 @@ PHP_METHOD(Redis, hVals) redis_sock_read_multibulk_reply); } - +/* {{{ proto array Redis::hgetall(string key) */ PHP_METHOD(Redis, hGetAll) { - - RedisSock *redis_sock = generic_hash_command_1(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HGETALL", sizeof("HGETALL")-1); - if(!redis_sock) - RETURN_FALSE; - - IF_ATOMIC() { - if (redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_vals); + REDIS_PROCESS_KW_CMD("HGETALL", redis_key_cmd, + redis_sock_read_multibulk_reply_zipped_strings); } +/* }}} */ PHP_METHOD(Redis, hIncrByFloat) { diff --git a/redis_cluster.c b/redis_cluster.c index 197bce0b99..e30e816b7b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -67,6 +67,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hvals, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrank, NULL, ZEND_ACC_PUBLIC) @@ -438,6 +439,12 @@ PHP_METHOD(RedisCluster, hgetall) { } /* }}} */ +/* {{{ proto bool RedisCluster::hexists(string key, string member) */ +PHP_METHOD(RedisCluster, hexists) { + CLUSTER_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, cluster_1_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 5887178fca..eb59f40359 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -100,6 +100,7 @@ PHP_METHOD(RedisCluster, hlen); PHP_METHOD(RedisCluster, hkeys); PHP_METHOD(RedisCluster, hvals); PHP_METHOD(RedisCluster, hgetall); +PHP_METHOD(RedisCluster, hexists); PHP_METHOD(RedisCluster, incr); PHP_METHOD(RedisCluster, decr); diff --git a/redis_commands.c b/redis_commands.c index f348e00186..647849ecdd 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -170,6 +170,32 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic command that takes a key and an unserialized value */ +int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key, *val; + int key_len, val_len, key_free; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, + &val, &val_len)==FAILURE) + { + return FAILURE; + } + + // Prefix key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, + val_len); + + // Set slot if directed + CMD_SET_SLOT(slot,key,key_len); + + return SUCCESS; +} + /* Generic command construction where we take a key and a long */ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) diff --git a/redis_commands.h b/redis_commands.h index 52d01e83c7..0095e00d44 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -33,7 +33,10 @@ int redis_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); + +int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); From 6e669bb2ee91bac05be8dd9ffeaca6cb1ea73989 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 08:38:27 -0700 Subject: [PATCH 0272/1986] PUBLISH command --- redis.c | 32 +++----------------------------- redis_cluster.c | 13 +++++++++---- redis_cluster.h | 10 +--------- 3 files changed, 13 insertions(+), 42 deletions(-) diff --git a/redis.c b/redis.c index 036c9c771a..9814ec7d5f 100644 --- a/redis.c +++ b/redis.c @@ -4543,38 +4543,12 @@ PHP_METHOD(Redis, pipeline) RETURN_ZVAL(getThis(), 1, 0); } -/* - publish channel message - @return the number of subscribers -*/ +/* {{{ proto long Redis::publish(string channel, string msg) */ PHP_METHOD(Redis, publish) { - zval *object; - RedisSock *redis_sock; - char *cmd, *key, *val; - int cmd_len, key_len, val_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, - &key, &key_len, &val, &val_len) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "PUBLISH", "ss", key, - key_len, val, val_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, redis_long_response); } +/* }}} */ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd) { diff --git a/redis_cluster.c b/redis_cluster.c index e30e816b7b..a8769eedcc 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -76,21 +76,18 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, incrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, decrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, incrbyfloat, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, expire, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pexpire, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, expireat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pexpireat, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, append, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getbit, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lget, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ltrim, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zremrangebyrank, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, publish, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -556,4 +553,12 @@ PHP_METHOD(RedisCluster, zremrangebyrank) { } /* }}} */ +/* {{{ proto long RedisCluster::publish(string key, string msg) */ +PHP_METHOD(RedisCluster, publish) { + CLUSTER_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, cluster_long_resp); +} +/* }}} */ + + + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index eb59f40359..f98a58eed1 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -82,44 +82,36 @@ PHP_METHOD(RedisCluster, spop); PHP_METHOD(RedisCluster, rpushx); PHP_METHOD(RedisCluster, lpushx); PHP_METHOD(RedisCluster, llen); - PHP_METHOD(RedisCluster, scard); PHP_METHOD(RedisCluster, smembers); PHP_METHOD(RedisCluster, sismember); PHP_METHOD(RedisCluster, strlen); - PHP_METHOD(RedisCluster, ttl); PHP_METHOD(RedisCluster, pttl); - PHP_METHOD(RedisCluster, zcard); PHP_METHOD(RedisCluster, zscore); PHP_METHOD(RedisCluster, zrank); PHP_METHOD(RedisCluster, zrevrank); - PHP_METHOD(RedisCluster, hlen); PHP_METHOD(RedisCluster, hkeys); PHP_METHOD(RedisCluster, hvals); PHP_METHOD(RedisCluster, hgetall); PHP_METHOD(RedisCluster, hexists); - PHP_METHOD(RedisCluster, incr); PHP_METHOD(RedisCluster, decr); PHP_METHOD(RedisCluster, incrby); PHP_METHOD(RedisCluster, decrby); PHP_METHOD(RedisCluster, incrbyfloat); - PHP_METHOD(RedisCluster, expire); PHP_METHOD(RedisCluster, expireat); PHP_METHOD(RedisCluster, pexpire); PHP_METHOD(RedisCluster, pexpireat); - PHP_METHOD(RedisCluster, append); - PHP_METHOD(RedisCluster, getbit); PHP_METHOD(RedisCluster, lget); - PHP_METHOD(RedisCluster, getrange); PHP_METHOD(RedisCluster, ltrim); PHP_METHOD(RedisCluster, lrange); PHP_METHOD(RedisCluster, zremrangebyrank); +PHP_METHOD(RedisCluster, publish); #endif From 1c6398f44bb490f2b30715577a1e627725187058 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 10:07:09 -0700 Subject: [PATCH 0273/1986] More commands, updated redis proper to use generics * Make key, long val generic * Update lset to use new style * Added PUBLISH commnad --- redis.c | 40 ++++++---------------------------------- redis_cluster.c | 14 +++++++++++--- redis_cluster.h | 1 + redis_commands.c | 4 ++-- redis_commands.h | 4 ++-- 5 files changed, 22 insertions(+), 41 deletions(-) diff --git a/redis.c b/redis.c index 9814ec7d5f..7c8a168129 100644 --- a/redis.c +++ b/redis.c @@ -941,14 +941,16 @@ PHP_METHOD(Redis, set) { */ PHP_METHOD(Redis, setex) { - REDIS_PROCESS_KW_CMD("SETEX", redis_setex_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, + redis_string_response); } /* {{{ proto boolean Redis::psetex(string key, long expire, string value) */ PHP_METHOD(Redis, psetex) { - REDIS_PROCESS_KW_CMD("PSETEX", redis_setex_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, + redis_string_response); } /* {{{ proto boolean Redis::setnx(string key, string value) @@ -2644,38 +2646,8 @@ PHP_METHOD(Redis, pexpireAt) { /* {{{ proto array Redis::lSet(string key, int index, string value) */ PHP_METHOD(Redis, lSet) { - - zval *object; - RedisSock *redis_sock; - - char *cmd; - int cmd_len, key_len, val_len; - long index; - char *key, *val; - int val_free, key_free = 0; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslz", - &object, redis_ce, &key, &key_len, &index, &z_value) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, - key_len, index, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, + redis_boolean_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index a8769eedcc..abea14368e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -50,6 +50,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, type, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) @@ -266,13 +267,15 @@ PHP_METHOD(RedisCluster, set) { /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { - CLUSTER_PROCESS_KW_CMD("SETEX", redis_setex_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, + cluster_bool_resp); } /* }}} */ /* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, psetex) { - CLUSTER_PROCESS_KW_CMD("PSETEX", redis_setex_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, + cluster_bool_resp); } /* }}} */ @@ -321,6 +324,12 @@ PHP_METHOD(RedisCluster, rpop) { } /* }}} */ +/* {{{ proto bool RedisCluster::lset(string key, long index, string val) */ +PHP_METHOD(RedisCluster, lset) { + CLUSTER_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, cluster_bool_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::spop(string key) */ PHP_METHOD(RedisCluster, spop) { CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_cmd, cluster_bulk_resp); @@ -560,5 +569,4 @@ PHP_METHOD(RedisCluster, publish) { /* }}} */ - /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index f98a58eed1..ec952d6ea9 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -114,4 +114,5 @@ PHP_METHOD(RedisCluster, ltrim); PHP_METHOD(RedisCluster, lrange); PHP_METHOD(RedisCluster, zremrangebyrank); PHP_METHOD(RedisCluster, publish); +PHP_METHOD(RedisCluster, lset); #endif diff --git a/redis_commands.c b/redis_commands.c index 647849ecdd..4184a14ed9 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -107,9 +107,9 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -/* SETEX / PSETEX */ +/* Key, long, zval (serialized) */ int -redis_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { char *key = NULL, *val=NULL; diff --git a/redis_commands.h b/redis_commands.h index 0095e00d44..50d10033fd 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -29,8 +29,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot); -int redis_setex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); +int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); From 1a7fcd07244bde483ef4ac3adcd46354290fda4b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 12:15:53 -0700 Subject: [PATCH 0274/1986] Actually only hash the bits between { and } --- cluster_library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 1eaec3d0e8..b7057a3475 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -86,9 +86,9 @@ unsigned short cluster_hash_key(const char *key, int len) { for(s=0;s Date: Thu, 5 Jun 2014 12:16:10 -0700 Subject: [PATCH 0275/1986] RENAME, RENAMENX For now we're pre-processing each key to determine if they all live in the same place. This might change for commands that could take very large numbers of keys --- redis.c | 66 +++--------------------------------------------- redis_cluster.c | 13 ++++++++++ redis_cluster.h | 3 +++ redis_commands.c | 44 ++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 5 files changed, 67 insertions(+), 62 deletions(-) diff --git a/redis.c b/redis.c index 7c8a168129..06faf2cee7 100644 --- a/redis.c +++ b/redis.c @@ -1036,37 +1036,7 @@ PHP_METHOD(Redis, echo) */ PHP_METHOD(Redis, renameKey) { - - zval *object; - RedisSock *redis_sock; - char *cmd, *src, *dst; - int cmd_len, src_len, dst_len; - int src_free, dst_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, - &src, &src_len, - &dst, &dst_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - src_free = redis_key_prefix(redis_sock, &src, &src_len); - dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "RENAME", "ss", src, - src_len, dst, dst_len); - if(src_free) efree(src); - if(dst_free) efree(dst); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); - + REDIS_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, redis_boolean_response); } /* }}} */ @@ -1074,40 +1044,12 @@ PHP_METHOD(Redis, renameKey) */ PHP_METHOD(Redis, renameNx) { - - zval *object; - RedisSock *redis_sock; - char *cmd, *src, *dst; - int cmd_len, src_len, dst_len; - int src_free, dst_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, - &src, &src_len, - &dst, &dst_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - src_free = redis_key_prefix(redis_sock, &src, &src_len); - dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "RENAMENX", "ss", src, - src_len, dst, dst_len); - if(src_free) efree(src); - if(dst_free) efree(dst); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); - + REDIS_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, redis_1_response); } /* }}} */ +/* }}} */ + /* {{{ proto string Redis::get(string key) */ PHP_METHOD(Redis, get) diff --git a/redis_cluster.c b/redis_cluster.c index abea14368e..84f295c05b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -89,6 +89,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, lrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zremrangebyrank, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, publish, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rename, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, renamenx, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -568,5 +570,16 @@ PHP_METHOD(RedisCluster, publish) { } /* }}} */ +/* {{{ proto bool RedisCluster::rename(string key1, string key2) */ +PHP_METHOD(RedisCluster, rename) { + CLUSTER_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, cluster_bool_resp); +} +/* }}} */ + +/* {{{ proto bool RedisCluster::renamenx(string key1, string key2) */ +PHP_METHOD(RedisCluster, renamenx) { + CLUSTER_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, cluster_1_resp); +} +/* }}} */ /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index ec952d6ea9..3c99cee64f 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -115,4 +115,7 @@ PHP_METHOD(RedisCluster, lrange); PHP_METHOD(RedisCluster, zremrangebyrank); PHP_METHOD(RedisCluster, publish); PHP_METHOD(RedisCluster, lset); +PHP_METHOD(RedisCluster, rename); +PHP_METHOD(RedisCluster, renamenx); + #endif diff --git a/redis_commands.c b/redis_commands.c index 4184a14ed9..b584d7f2fe 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -196,6 +196,50 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic command that takes two keys */ +int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key1, *key2; + int key1_len, key2_len; + int key1_free, key2_free; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key1, &key1_len, + &key2, &key2_len)==FAILURE) + { + return FAILURE; + } + + // Prefix both keys + key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); + key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); + + // If a slot is requested, we can test that they hash the same + if(slot) { + // Slots where these keys resolve + short slot1 = cluster_hash_key(key1, key1_len); + short slot2 = cluster_hash_key(key2, key2_len); +php_printf("%d, %d\n", slot1, slot2); + // Check if Redis would give us a CROSSLOT error + if(slot1 != slot2) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, + "Keys don't hash to the same slot"); + if(key1_free) efree(key1); + if(key2_free) efree(key2); + return FAILURE; + } + + // They're both the same + *slot = slot1; + } + + // Construct our command + *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key1, key1_len, key2, + key2_len); + + return SUCCESS; +} + /* Generic command construction where we take a key and a long */ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) diff --git a/redis_commands.h b/redis_commands.h index 50d10033fd..f2eb857e50 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -38,6 +38,9 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); +int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); + int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); From 3a0f6d5e2a0c768f2aac8fa3a11a622db671af4d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 13:15:10 -0700 Subject: [PATCH 0276/1986] Reformatting of command code, RPOPLPUSH/BRPOPLPUSH Reorganized redis_commands.h and redis_commands.c to put the generics first, and the specific command construction at the end. --- redis.c | 68 +------------ redis_commands.c | 249 +++++++++++++++++++++++++++++------------------ redis_commands.h | 50 +++++----- 3 files changed, 179 insertions(+), 188 deletions(-) diff --git a/redis.c b/redis.c index 06faf2cee7..7b8749107d 100644 --- a/redis.c +++ b/redis.c @@ -2957,77 +2957,17 @@ PHP_METHOD(Redis, msetnx) { } /* }}} */ -PHP_REDIS_API void common_rpoplpush(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *srckey, int srckey_len, char *dstkey, int dstkey_len, int timeout) { - - char *cmd; - int cmd_len; - - int srckey_free = redis_key_prefix(redis_sock, &srckey, &srckey_len); - int dstkey_free = redis_key_prefix(redis_sock, &dstkey, &dstkey_len); - if(timeout < 0) { - cmd_len = redis_cmd_format_static(&cmd, "RPOPLPUSH", "ss", - srckey, srckey_len, dstkey, dstkey_len); - } else { - cmd_len = redis_cmd_format_static(&cmd, "BRPOPLPUSH", "ssd", - srckey, srckey_len, dstkey, dstkey_len, - timeout); - } - if(srckey_free) efree(srckey); - if(dstkey_free) efree(dstkey); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); - -} - /* {{{ proto string Redis::rpoplpush(string srckey, string dstkey) */ PHP_METHOD(Redis, rpoplpush) { - zval *object; - RedisSock *redis_sock; - char *srckey = NULL, *dstkey = NULL; - int srckey_len, dstkey_len; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, &srckey, &srckey_len, - &dstkey, &dstkey_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - common_rpoplpush(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, srckey, srckey_len, dstkey, dstkey_len, -1); + REDIS_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, redis_string_response); } /* }}} */ -/* {{{ proto string Redis::brpoplpush(string srckey, string dstkey) - */ -PHP_METHOD(Redis, brpoplpush) -{ - zval *object; - RedisSock *redis_sock; - char *srckey = NULL, *dstkey = NULL; - int srckey_len, dstkey_len; - long timeout = 0; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossl", - &object, redis_ce, &srckey, &srckey_len, - &dstkey, &dstkey_len, &timeout) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - common_rpoplpush(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, srckey, srckey_len, dstkey, dstkey_len, timeout); +/* {{{ proto string Redis::brpoplpush(string src, string dst, int timeout) */ +PHP_METHOD(Redis, brpoplpush) { + REDIS_PROCESS_CMD(brpoplpush, redis_string_response); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index b584d7f2fe..ebe0bbdf89 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -20,95 +20,12 @@ #include "redis_commands.h" -/* SET */ -int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) -{ - zval *z_value, *z_opts=NULL; - char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; - int key_len, val_len, key_free, val_free; - long expire = -1; - - // Make sure the function is being called correctly - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, - &z_value, &z_opts)==FAILURE) - { - return FAILURE; - } - - // Serialize and key prefix if required - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Check for an options array - if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { - HashTable *kt = Z_ARRVAL_P(z_opts); - int type; - unsigned int ht_key_len; - unsigned long idx; - char *k; - zval **v; - - /* Iterate our option array */ - for(zend_hash_internal_pointer_reset(kt); - zend_hash_has_more_elements(kt) == SUCCESS; - zend_hash_move_forward(kt)) - { - // Grab key and value - type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, - NULL); - zend_hash_get_current_data(kt, (void**)&v); - - if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) && - (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k)) - { - exp_type = k; - expire = Z_LVAL_PP(v); - } else if(Z_TYPE_PP(v) == IS_STRING && - IS_NX_XX_ARG(Z_STRVAL_PP(v))) - { - set_type = Z_STRVAL_PP(v); - } - } - } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { - expire = Z_LVAL_P(z_opts); - } - - /* Now let's construct the command we want */ - if(exp_type && set_type) { - /* SET NX|XX PX|EX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "ssssl", key, key_len, - val, val_len, set_type, 2, exp_type, - 2, expire); - } else if(exp_type) { - /* SET PX|EX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "sssl", key, key_len, - val, val_len, exp_type, 2, expire); - } else if(set_type) { - /* SET NX|XX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "sss", key, key_len, val, - val_len, set_type, 2); - } else if(expire > 0) { - /* Backward compatible SETEX redirection */ - *cmd_len = redis_cmd_format_static(cmd, "SETEX", "sls", key, key_len, - expire, val, val_len); - } else { - /* SET */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "ss", key, key_len, val, - val_len); - } - - // If we've been passed a slot pointer, return the key's slot - CMD_SET_SLOT(slot,key,key_len); - - if(key_free) efree(key); - if(val_free) efree(val); - - return SUCCESS; -} +/* Generic commands based on method signature and what kind of things we're + * processing. Lots of Redis commands take something like key, value, or + * key, value long. Each unique signature like this is written only once */ /* Key, long, zval (serialized) */ -int +int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { @@ -126,7 +43,7 @@ redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Serialize value, prefix key val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); - + // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "sls", key, key_len, expire, val, val_len); @@ -148,7 +65,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_len, val_len, key_free, val_free; zval *z_val; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, &z_val)==FAILURE) { return FAILURE; @@ -158,7 +75,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct our command - *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, + *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, val_len); // Set our slot if directed @@ -187,7 +104,7 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command - *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, + *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, val_len); // Set slot if directed @@ -219,10 +136,10 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Slots where these keys resolve short slot1 = cluster_hash_key(key1, key1_len); short slot2 = cluster_hash_key(key2, key2_len); -php_printf("%d, %d\n", slot1, slot2); + // Check if Redis would give us a CROSSLOT error if(slot1 != slot2) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Keys don't hash to the same slot"); if(key1_free) efree(key1); if(key2_free) efree(key2); @@ -232,9 +149,9 @@ php_printf("%d, %d\n", slot1, slot2); // They're both the same *slot = slot1; } - + // Construct our command - *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key1, key1_len, key2, + *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key1, key1_len, key2, key2_len); return SUCCESS; @@ -291,7 +208,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command - *cmd_len = redis_cmd_format_static(cmd, kw, "sll", key, key_len, val1, + *cmd_len = redis_cmd_format_static(cmd, kw, "sll", key, key_len, val1, val2); // Set slot @@ -317,7 +234,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix our key key_free = redis_key_prefix(redis_sock, &key, &key_len); - + // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "s", key, key_len); @@ -357,4 +274,142 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Commands with specific signatures or that need unique functions because they + * have specific processing (argument validation, etc) that make them unique */ + + /* SET */ + int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) + { + zval *z_value, *z_opts=NULL; + char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; + int key_len, val_len, key_free, val_free; + long expire = -1; + + // Make sure the function is being called correctly + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, + &z_value, &z_opts)==FAILURE) + { + return FAILURE; + } + + // Serialize and key prefix if required + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Check for an options array + if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { + HashTable *kt = Z_ARRVAL_P(z_opts); + int type; + unsigned int ht_key_len; + unsigned long idx; + char *k; + zval **v; + + /* Iterate our option array */ + for(zend_hash_internal_pointer_reset(kt); + zend_hash_has_more_elements(kt) == SUCCESS; + zend_hash_move_forward(kt)) + { + // Grab key and value + type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, + NULL); + zend_hash_get_current_data(kt, (void**)&v); + + if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) && + (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k)) + { + exp_type = k; + expire = Z_LVAL_PP(v); + } else if(Z_TYPE_PP(v) == IS_STRING && + IS_NX_XX_ARG(Z_STRVAL_PP(v))) + { + set_type = Z_STRVAL_PP(v); + } + } + } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { + expire = Z_LVAL_P(z_opts); + } + + /* Now let's construct the command we want */ + if(exp_type && set_type) { + /* SET NX|XX PX|EX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "ssssl", key, key_len, + val, val_len, set_type, 2, exp_type, + 2, expire); + } else if(exp_type) { + /* SET PX|EX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "sssl", key, key_len, + val, val_len, exp_type, 2, expire); + } else if(set_type) { + /* SET NX|XX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "sss", key, key_len, val, + val_len, set_type, 2); + } else if(expire > 0) { + /* Backward compatible SETEX redirection */ + *cmd_len = redis_cmd_format_static(cmd, "SETEX", "sls", key, key_len, + expire, val, val_len); + } else { + /* SET */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "ss", key, key_len, val, + val_len); + } + + // If we've been passed a slot pointer, return the key's slot + CMD_SET_SLOT(slot,key,key_len); + + if(key_free) efree(key); + if(val_free) efree(val); + + return SUCCESS; + } + + /* BRPOPLPUSH */ + int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) + { + char *key1, *key2; + int key1_len, key2_len; + int key1_free, key2_free; + short slot1, slot2; + long timeout; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key1, &key1_len, + &key2, &key2_len, &timeout)==FAILURE) + { + return FAILURE; + } + + // Key prefixing + key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); + key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); + + // In cluster mode, verify the slots match + if(slot) { + slot1 = cluster_hash_key(key1, key1_len); + slot2 = cluster_hash_key(key2, key2_len); + if(slot1 != slot2) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Keys hash to different slots!"); + if(key1_free) efree(key1); + if(key2_free) efree(key2); + return FAILURE; + } + + // Both slots are the same + *slot = slot1; + } + + // Consistency with Redis, if timeout < 0 use RPOPLPUSH + if(timeout < 0) { + *cmd_len = redis_cmd_format_static(cmd, "RPOPLPUSH", "ss", key1, + key1_len, key2, key2_len); + } else { + *cmd_len = redis_cmd_format_static(cmd, "BRPOPLPUSH", "ssd", key1, + key1_len, key2, key2_len, timeout); + } + + return SUCCESS; + } + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index f2eb857e50..3e0427d2f9 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -9,52 +9,48 @@ #define CMD_SET_SLOT(slot,key,key_len) \ if(slot) *slot = cluster_hash_key(key,key_len); -/* Redis command construction routines, which generally take the form: - * int function(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - * char **cmd, int *cmd_len, short *slot); - * - * OR - * - * int function(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - * char *kw, char **cmd, int *cmd_len, short *slot); - * - * The functions will return SUCCESS on success, and FAILURE on failure. In - * the case of a failure, the cmd pointer will not have been updated, and - * no memory wlll have been allocated (that wasn't freed). - * - * If the slot pointer is passed as non-null, it will be set to the Redis - * Cluster hash slot where the key(s) belong. - */ -int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot); +/* Redis command generics. Many commands share common prototypes meaning that + * we can write one function to handle all of them. For example, there are + * many COMMAND key value commands, or COMMAND key commands. */ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); int redis_ss_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot); + char *kw, char **cmd, int *cmd_len, short *slot); + +/* Commands which need a unique construction mechanism. This is either because + * they don't share a signature with any other command, or because there is + * specific processing we do (e.g. verifying subarguments) that make them + * unique */ + +int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot); + +int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot); #endif From 3aff864ab7e139c27099ab211d00254a2613c356 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 13:19:04 -0700 Subject: [PATCH 0277/1986] Actually add RPOPLPUSH and BRPOPLPUSH into cluster --- redis_cluster.c | 14 ++++++++++++++ redis_cluster.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/redis_cluster.c b/redis_cluster.c index 84f295c05b..2df1ac6100 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -54,6 +54,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, brpoplpush, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpoplpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, llen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, scard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, smembers, NULL, ZEND_ACC_PUBLIC) @@ -355,6 +357,18 @@ PHP_METHOD(RedisCluster, lpushx) { } /* }}} */ +/* {{{ proto string RedisCluster::rpoplpush(string key, string key) */ +PHP_METHOD(RedisCluster, rpoplpush) { + CLUSTER_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, cluster_bulk_resp); +} +/* }}} */ + +/* {{{ proto string RedisCluster::brpoplpush(string key, string key, long tm) */ +PHP_METHOD(RedisCluster, brpoplpush) { + CLUSTER_PROCESS_CMD(brpoplpush, cluster_bulk_resp); +} +/* }}} */ + /* {{{ proto long RedisCluster::llen(string key) */ PHP_METHOD(RedisCluster, llen) { CLUSTER_PROCESS_KW_CMD("LLEN", redis_key_cmd, cluster_long_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 3c99cee64f..d4b969c325 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -81,6 +81,8 @@ PHP_METHOD(RedisCluster, rpop); PHP_METHOD(RedisCluster, spop); PHP_METHOD(RedisCluster, rpushx); PHP_METHOD(RedisCluster, lpushx); +PHP_METHOD(RedisCluster, brpoplpush); +PHP_METHOD(RedisCluster, rpoplpush); PHP_METHOD(RedisCluster, llen); PHP_METHOD(RedisCluster, scard); PHP_METHOD(RedisCluster, smembers); From a425ed587d67e4e416cf8494e64cfaada048854c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 13:30:35 -0700 Subject: [PATCH 0278/1986] Implemented HSET command --- redis.c | 28 ++-------------------------- redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/redis.c b/redis.c index 7b8749107d..930ad4c7d3 100644 --- a/redis.c +++ b/redis.c @@ -3737,34 +3737,10 @@ PHP_METHOD(Redis, hSetNx) /* }}} */ -/* hGet */ +/* proto string Redis::hget(string key, string mem) */ PHP_METHOD(Redis, hGet) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd, *member; - int key_len, member_len, cmd_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, - &key, &key_len, &member, &member_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HGET", "ss", key, - key_len, member, member_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); - + REDIS_PROCESS_KW_CMD("HGET", redis_key_str_cmd, redis_string_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 2df1ac6100..60ef772b65 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -69,6 +69,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hvals, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) @@ -454,6 +455,12 @@ PHP_METHOD(RedisCluster, hvals) { } /* }}} */ +/* {{{ proto string RedisCluster::hget(string key, string mem) */ +PHP_METHOD(RedisCluster, hget) { + CLUSTER_PROCESS_KW_CMD("HGET", redis_key_str_cmd, cluster_bulk_resp); +} +/* }}} */ + /* {{{ proto array RedisCluster::hgetall(string key) */ PHP_METHOD(RedisCluster, hgetall) { CLUSTER_PROCESS_KW_CMD("HGETALL", redis_key_cmd, diff --git a/redis_cluster.h b/redis_cluster.h index d4b969c325..e5a3de03ce 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -95,6 +95,7 @@ PHP_METHOD(RedisCluster, zscore); PHP_METHOD(RedisCluster, zrank); PHP_METHOD(RedisCluster, zrevrank); PHP_METHOD(RedisCluster, hlen); +PHP_METHOD(RedisCluster, hget); PHP_METHOD(RedisCluster, hkeys); PHP_METHOD(RedisCluster, hvals); PHP_METHOD(RedisCluster, hgetall); From b5d2a5fa5c8fa80bb3a8081ed20755d5afa0efc1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 13:31:33 -0700 Subject: [PATCH 0279/1986] Removed redundant generic function in Redis proper --- redis.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/redis.c b/redis.c index 930ad4c7d3..3d36c81723 100644 --- a/redis.c +++ b/redis.c @@ -3751,35 +3751,6 @@ PHP_METHOD(Redis, hLen) } /* }}} */ -PHP_REDIS_API RedisSock* -generic_hash_command_2(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len, char **out_cmd, int *out_len) { - - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd, *member; - int key_len, cmd_len, member_len, key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, - &key, &key_len, &member, &member_len) == FAILURE) { - ZVAL_BOOL(return_value, 0); - return NULL; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - ZVAL_BOOL(return_value, 0); - return NULL; - } - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, - key_len, member, member_len); - if(key_free) efree(key); - - *out_cmd = cmd; - *out_len = cmd_len; - return redis_sock; -} - /* hDel */ PHP_METHOD(Redis, hDel) { From 737ad2bd97822b65847e8541dd66943db9a17bf3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 13:54:48 -0700 Subject: [PATCH 0280/1986] HINCRBY --- redis.c | 43 +------ redis_cluster.c | 7 ++ redis_cluster.h | 1 + redis_commands.c | 294 ++++++++++++++++++++++++++--------------------- redis_commands.h | 3 + 5 files changed, 175 insertions(+), 173 deletions(-) diff --git a/redis.c b/redis.c index 3d36c81723..609dc7932f 100644 --- a/redis.c +++ b/redis.c @@ -3827,49 +3827,12 @@ PHP_METHOD(Redis, hIncrByFloat) REDIS_PROCESS_RESPONSE(redis_bulk_double_response); } +/* {{{ proto long Redis::hincrby(string key, string mem, long byval) */ PHP_METHOD(Redis, hIncrBy) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd, *member, *val; - int key_len, member_len, cmd_len, val_len, key_free; - int i; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss", - &object, redis_ce, - &key, &key_len, &member, &member_len, &val, &val_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* check for validity of numeric string */ - i = 0; - if(val_len && val[0] == '-') { /* negative case */ - i++; - } - for(; i < val_len; ++i) { - if(val[i] < '0' || val[i] > '9') { - RETURN_FALSE; - } - } - - /* HINCRBY key member amount */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HINCRBY", "sss", key, - key_len, member, member_len, val, - val_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - + REDIS_PROCESS_REQUEST(hincrby, redis_long_response); } +/* }}} */ /* {{{ array Redis::hMget(string hash, array keys) */ PHP_METHOD(Redis, hMget) { diff --git a/redis_cluster.c b/redis_cluster.c index 60ef772b65..3b007e5f1e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -72,6 +72,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hincrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrank, NULL, ZEND_ACC_PUBLIC) @@ -474,6 +475,12 @@ PHP_METHOD(RedisCluster, hexists) { } /* }}} */ +/* {{{ proto long RedisCluster::hincr(string key, string mem, long val) */ +PHP_METHOD(RedisCluster, hincrby) { + CLUSTER_PROCESS_CMD(hincrby, cluster_long_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp); diff --git a/redis_cluster.h b/redis_cluster.h index e5a3de03ce..14551816fa 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -100,6 +100,7 @@ PHP_METHOD(RedisCluster, hkeys); PHP_METHOD(RedisCluster, hvals); PHP_METHOD(RedisCluster, hgetall); PHP_METHOD(RedisCluster, hexists); +PHP_METHOD(RedisCluster, hincrby); PHP_METHOD(RedisCluster, incr); PHP_METHOD(RedisCluster, decr); PHP_METHOD(RedisCluster, incrby); diff --git a/redis_commands.c b/redis_commands.c index ebe0bbdf89..cd71761412 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -277,139 +277,167 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Commands with specific signatures or that need unique functions because they * have specific processing (argument validation, etc) that make them unique */ - /* SET */ - int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) - { - zval *z_value, *z_opts=NULL; - char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; - int key_len, val_len, key_free, val_free; - long expire = -1; - - // Make sure the function is being called correctly - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, - &z_value, &z_opts)==FAILURE) - { - return FAILURE; - } - - // Serialize and key prefix if required - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Check for an options array - if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { - HashTable *kt = Z_ARRVAL_P(z_opts); - int type; - unsigned int ht_key_len; - unsigned long idx; - char *k; - zval **v; - - /* Iterate our option array */ - for(zend_hash_internal_pointer_reset(kt); - zend_hash_has_more_elements(kt) == SUCCESS; - zend_hash_move_forward(kt)) - { - // Grab key and value - type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, - NULL); - zend_hash_get_current_data(kt, (void**)&v); - - if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) && - (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k)) - { - exp_type = k; - expire = Z_LVAL_PP(v); - } else if(Z_TYPE_PP(v) == IS_STRING && - IS_NX_XX_ARG(Z_STRVAL_PP(v))) - { - set_type = Z_STRVAL_PP(v); - } - } - } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { - expire = Z_LVAL_P(z_opts); - } - - /* Now let's construct the command we want */ - if(exp_type && set_type) { - /* SET NX|XX PX|EX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "ssssl", key, key_len, - val, val_len, set_type, 2, exp_type, - 2, expire); - } else if(exp_type) { - /* SET PX|EX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "sssl", key, key_len, - val, val_len, exp_type, 2, expire); - } else if(set_type) { - /* SET NX|XX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "sss", key, key_len, val, - val_len, set_type, 2); - } else if(expire > 0) { - /* Backward compatible SETEX redirection */ - *cmd_len = redis_cmd_format_static(cmd, "SETEX", "sls", key, key_len, - expire, val, val_len); - } else { - /* SET */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "ss", key, key_len, val, +/* SET */ +int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + zval *z_value, *z_opts=NULL; + char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; + int key_len, val_len, key_free, val_free; + long expire = -1; + + // Make sure the function is being called correctly + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, + &z_value, &z_opts)==FAILURE) + { + return FAILURE; + } + + // Serialize and key prefix if required + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Check for an options array + if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { + HashTable *kt = Z_ARRVAL_P(z_opts); + int type; + unsigned int ht_key_len; + unsigned long idx; + char *k; + zval **v; + + /* Iterate our option array */ + for(zend_hash_internal_pointer_reset(kt); + zend_hash_has_more_elements(kt) == SUCCESS; + zend_hash_move_forward(kt)) + { + // Grab key and value + type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, + NULL); + zend_hash_get_current_data(kt, (void**)&v); + + if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) && + (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k)) + { + exp_type = k; + expire = Z_LVAL_PP(v); + } else if(Z_TYPE_PP(v) == IS_STRING && + IS_NX_XX_ARG(Z_STRVAL_PP(v))) + { + set_type = Z_STRVAL_PP(v); + } + } + } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { + expire = Z_LVAL_P(z_opts); + } + + /* Now let's construct the command we want */ + if(exp_type && set_type) { + /* SET NX|XX PX|EX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "ssssl", key, key_len, + val, val_len, set_type, 2, exp_type, + 2, expire); + } else if(exp_type) { + /* SET PX|EX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "sssl", key, key_len, + val, val_len, exp_type, 2, expire); + } else if(set_type) { + /* SET NX|XX */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "sss", key, key_len, val, + val_len, set_type, 2); + } else if(expire > 0) { + /* Backward compatible SETEX redirection */ + *cmd_len = redis_cmd_format_static(cmd, "SETEX", "sls", key, key_len, + expire, val, val_len); + } else { + /* SET */ + *cmd_len = redis_cmd_format_static(cmd, "SET", "ss", key, key_len, val, val_len); - } - - // If we've been passed a slot pointer, return the key's slot - CMD_SET_SLOT(slot,key,key_len); - - if(key_free) efree(key); - if(val_free) efree(val); - - return SUCCESS; - } - - /* BRPOPLPUSH */ - int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) - { - char *key1, *key2; - int key1_len, key2_len; - int key1_free, key2_free; - short slot1, slot2; - long timeout; - - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key1, &key1_len, - &key2, &key2_len, &timeout)==FAILURE) - { - return FAILURE; - } - - // Key prefixing - key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); - key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); - - // In cluster mode, verify the slots match - if(slot) { - slot1 = cluster_hash_key(key1, key1_len); - slot2 = cluster_hash_key(key2, key2_len); - if(slot1 != slot2) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Keys hash to different slots!"); - if(key1_free) efree(key1); - if(key2_free) efree(key2); - return FAILURE; - } - - // Both slots are the same - *slot = slot1; - } - - // Consistency with Redis, if timeout < 0 use RPOPLPUSH - if(timeout < 0) { - *cmd_len = redis_cmd_format_static(cmd, "RPOPLPUSH", "ss", key1, - key1_len, key2, key2_len); - } else { - *cmd_len = redis_cmd_format_static(cmd, "BRPOPLPUSH", "ssd", key1, - key1_len, key2, key2_len, timeout); - } - - return SUCCESS; - } + } + + // If we've been passed a slot pointer, return the key's slot + CMD_SET_SLOT(slot,key,key_len); + + if(key_free) efree(key); + if(val_free) efree(val); + + return SUCCESS; +} + +/* BRPOPLPUSH */ +int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + char *key1, *key2; + int key1_len, key2_len; + int key1_free, key2_free; + short slot1, slot2; + long timeout; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key1, &key1_len, + &key2, &key2_len, &timeout)==FAILURE) + { + return FAILURE; + } + + // Key prefixing + key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); + key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); + + // In cluster mode, verify the slots match + if(slot) { + slot1 = cluster_hash_key(key1, key1_len); + slot2 = cluster_hash_key(key2, key2_len); + if(slot1 != slot2) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Keys hash to different slots!"); + if(key1_free) efree(key1); + if(key2_free) efree(key2); + return FAILURE; + } + + // Both slots are the same + *slot = slot1; + } + + // Consistency with Redis, if timeout < 0 use RPOPLPUSH + if(timeout < 0) { + *cmd_len = redis_cmd_format_static(cmd, "RPOPLPUSH", "ss", key1, + key1_len, key2, key2_len); + } else { + *cmd_len = redis_cmd_format_static(cmd, "BRPOPLPUSH", "ssd", key1, + key1_len, key2, key2_len, timeout); + } + + return SUCCESS; +} + +/* HINCRBY */ +int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + + char *key, *mem; + int key_len, mem_len, key_free; + long byval; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key, &key_len, + &mem, &mem_len, &byval)==FAILURE) + { + return FAILURE; + } + + // Prefix our key if necissary + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, "HINCRBY", "ssd", key, key_len, mem, + mem_len, byval); + // Set slot + CMD_SET_SLOT(slot,key,key_len); + + // Success + return SUCCESS; +} /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 3e0427d2f9..2070cd0f81 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -52,6 +52,9 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot); +int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 637591a5a473e7b3e5879d1c26a4e253ecb5e2f6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 14:12:03 -0700 Subject: [PATCH 0281/1986] HINCRBYFLOAT --- redis.c | 91 +++++++++++++++++++++++++++++++++--------------- redis_cluster.c | 7 ++++ redis_cluster.h | 1 + redis_commands.c | 28 +++++++++++++++ redis_commands.h | 3 ++ 5 files changed, 102 insertions(+), 28 deletions(-) diff --git a/redis.c b/redis.c index 609dc7932f..d0935f2906 100644 --- a/redis.c +++ b/redis.c @@ -3795,42 +3795,77 @@ PHP_METHOD(Redis, hGetAll) { } /* }}} */ -PHP_METHOD(Redis, hIncrByFloat) -{ - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd, *member; - int key_len, member_len, cmd_len, key_free; - double val; - - /* Validate we have the right number of arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossd", - &object, redis_ce, - &key, &key_len, &member, &member_len, &val) == FAILURE) { - RETURN_FALSE; - } +PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC) { - /* Grab our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } + zval *z_ret; + HashTable *keytable; - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "HINCRBYFLOAT", "ssf", key, - key_len, member, member_len, val); - if(key_free) efree(key); + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + keytable = Z_ARRVAL_P(z_tab); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_bulk_double_response); + for(zend_hash_internal_pointer_reset(keytable); + zend_hash_has_more_elements(keytable) == SUCCESS; + zend_hash_move_forward(keytable)) { + + char *tablekey, *hkey, *hval; + unsigned int tablekey_len; + int hkey_len; + unsigned long idx; + zval **z_key_pp, **z_value_pp; + + zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { + continue; /* this should never happen, according to the PHP people. */ + } + + /* get current value, a key */ + convert_to_string(*z_key_pp); + hkey = Z_STRVAL_PP(z_key_pp); + hkey_len = Z_STRLEN_PP(z_key_pp); + + /* move forward */ + zend_hash_move_forward(keytable); + + /* fetch again */ + zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { + continue; /* this should never happen, according to the PHP people. */ + } + + /* get current value, a hash value now. */ + hval = Z_STRVAL_PP(z_value_pp); + + if(use_atof) { /* zipping a score */ + add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); + } else { /* add raw copy */ + zval *z = NULL; + MAKE_STD_ZVAL(z); + *z = **z_value_pp; + zval_copy_ctor(z); + add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); + } + } + /* replace */ + zval_dtor(z_tab); + *z_tab = *z_ret; + zval_copy_ctor(z_tab); + zval_dtor(z_ret); + + efree(z_ret); +} + +/* {{{ proto double Redis::hIncrByFloat(string k, string me, double v) */ +PHP_METHOD(Redis, hIncrByFloat) +{ + REDIS_PROCESS_CMD(hincrbyfloat, redis_bulk_double_response); } +/* }}} */ /* {{{ proto long Redis::hincrby(string key, string mem, long byval) */ PHP_METHOD(Redis, hIncrBy) { - REDIS_PROCESS_REQUEST(hincrby, redis_long_response); + REDIS_PROCESS_CMD(hincrby, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 3b007e5f1e..296a6e365b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -73,6 +73,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrby, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hincrbyfloat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrank, NULL, ZEND_ACC_PUBLIC) @@ -481,6 +482,12 @@ PHP_METHOD(RedisCluster, hincrby) { } /* }}} */ +/* {{{ proto double RedisCluster::hincrbyfloat(string k, string m, double v) */ +PHP_METHOD(RedisCluster, hincrbyfloat) { + CLUSTER_PROCESS_CMD(hincrbyfloat, cluster_dbl_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 14551816fa..53e7701869 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -101,6 +101,7 @@ PHP_METHOD(RedisCluster, hvals); PHP_METHOD(RedisCluster, hgetall); PHP_METHOD(RedisCluster, hexists); PHP_METHOD(RedisCluster, hincrby); +PHP_METHOD(RedisCluster, hincrbyfloat); PHP_METHOD(RedisCluster, incr); PHP_METHOD(RedisCluster, decr); PHP_METHOD(RedisCluster, incrby); diff --git a/redis_commands.c b/redis_commands.c index cd71761412..6d36ffc596 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -440,4 +440,32 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* HINCRBYFLOAT */ +int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + char *key, *mem; + int key_len, mem_len, key_free; + double byval; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssd", &key, &key_len, + &mem, &mem_len, &byval)==FAILURE) + { + return FAILURE; + } + + // Prefix key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, "HINCRBYFLOAT", "ssf", key, key_len, + mem, mem_len, byval); + + // Set slot + CMD_SET_SLOT(slot,key,key_len); + + // Success + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 2070cd0f81..bae7198b6c 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -55,6 +55,9 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot); +int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 9af07ca679fad08691ab290e1a07477e54c5a979 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Jun 2014 14:28:37 -0700 Subject: [PATCH 0282/1986] ZCOUNT/ZREMRANGEBYSCORE --- redis.c | 69 +++++------------------------------------------- redis_cluster.c | 15 +++++++++++ redis_cluster.h | 2 ++ redis_commands.c | 30 +++++++++++++++++++++ redis_commands.h | 3 +++ 5 files changed, 56 insertions(+), 63 deletions(-) diff --git a/redis.c b/redis.c index d0935f2906..07e3643327 100644 --- a/redis.c +++ b/redis.c @@ -3138,39 +3138,12 @@ PHP_METHOD(Redis, zDelete) REDIS_PROCESS_RESPONSE(redis_long_response); } /* }}} */ -/* {{{ proto long Redis::zDeleteRangeByScore(string key, string start, string end) - */ + +/* {{{ proto long Redis::zDeleteRangeByScore(string k, string s, string e) */ PHP_METHOD(Redis, zDeleteRangeByScore) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - char *start, *end; - int start_len, end_len; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss", - &object, redis_ce, - &key, &key_len, &start, &start_len, &end, &end_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZREMRANGEBYSCORE", "sss", - key, key_len, start, start_len, end, - end_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - + REDIS_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd, + redis_long_response); } /* }}} */ @@ -3347,40 +3320,10 @@ PHP_METHOD(Redis, zRevRangeByScore) redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE"); } -/* {{{ proto array Redis::zCount(string key, string start , string end) - */ +/* {{{ proto array Redis::zCount(string key, string start , string end) */ PHP_METHOD(Redis, zCount) { - zval *object; - - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - char *start, *end; - int start_len, end_len; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss", - &object, redis_ce, - &key, &key_len, - &start, &start_len, - &end, &end_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ZCOUNT", "sss", key, - key_len, start, start_len, end, end_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 296a6e365b..b5f957bf7e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -65,6 +65,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, ttl, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pttl, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcard, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zcount, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zremrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) @@ -426,6 +428,19 @@ PHP_METHOD(RedisCluster, zscore) { } /* }}} */ +/* {{{ proto RedisCluster::zremrangebyscore(string k, string s, string e) */ +PHP_METHOD(RedisCluster, zremrangebyscore) { + CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd, + cluster_long_resp); +} +/* }}} */ + +/* {{{ proto RedisCluster::zcount(string key, string s, string e) */ +PHP_METHOD(RedisCluster, zcount) { + CLUSTER_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, cluster_long_resp); +} +/* }}} */ + /* {{{ proto long RedisCluster::zrank(string key, mixed member) */ PHP_METHOD(RedisCluster, zrank) { CLUSTER_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, cluster_long_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 53e7701869..c847043fe7 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -92,6 +92,8 @@ PHP_METHOD(RedisCluster, ttl); PHP_METHOD(RedisCluster, pttl); PHP_METHOD(RedisCluster, zcard); PHP_METHOD(RedisCluster, zscore); +PHP_METHOD(RedisCluster, zcount); +PHP_METHOD(RedisCluster, zremrangebyscore); PHP_METHOD(RedisCluster, zrank); PHP_METHOD(RedisCluster, zrevrank); PHP_METHOD(RedisCluster, hlen); diff --git a/redis_commands.c b/redis_commands.c index 6d36ffc596..08a9ec5dc9 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -113,6 +113,36 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Key, string, string without serialization (ZCOUNT, ZREMRANGEBYSCORE) */ +int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key, *val1, *val2; + int key_len, val1_len, val2_len, key_free; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, + &val1, &val1_len, &val2, &val2_len)==FAILURE) + { + return FAILURE; + } + + // Prefix key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, val1, + val1_len, val2, val2_len); + + // Set slot + CMD_SET_SLOT(slot,key,key_len); + + // Free key if prefixed + if(key_free) efree(key); + + // Success! + return SUCCESS; +} + /* Generic command that takes two keys */ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) diff --git a/redis_commands.h b/redis_commands.h index bae7198b6c..576ec4a64a 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -35,6 +35,9 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); +int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot); + int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot); From 48bf0b57af55ebb8ac82b52e74ef4510d23fb902 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 6 Jun 2014 13:51:04 -0700 Subject: [PATCH 0283/1986] HMSET, with command handler --- redis.c | 117 ++++++++--------------------------------------- redis_cluster.c | 7 +++ redis_cluster.h | 1 + redis_commands.c | 85 ++++++++++++++++++++++++++++++++++ redis_commands.h | 7 ++- 5 files changed, 118 insertions(+), 99 deletions(-) diff --git a/redis.c b/redis.c index 07e3643327..501ceb70ca 100644 --- a/redis.c +++ b/redis.c @@ -3900,106 +3900,12 @@ PHP_METHOD(Redis, hMget) { REDIS_PROCESS_RESPONSE_CLOSURE(redis_mbulk_reply_assoc, z_keys); } +/* {{{ proto bool Redis::hmset(string key, array keyvals) */ PHP_METHOD(Redis, hMset) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd, *old_cmd = NULL; - int key_len, cmd_len, key_free, i, element_count = 2; - zval *z_hash; - HashTable *ht_hash; - smart_str set_cmds = {0}; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", - &object, redis_ce, - &key, &key_len, &z_hash) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - ht_hash = Z_ARRVAL_P(z_hash); - - if (zend_hash_num_elements(ht_hash) == 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format(&cmd, - "$5" _NL "HMSET" _NL - "$%d" _NL "%s" _NL - , key_len, key, key_len); - if(key_free) efree(key); - - /* looping on each item of the array */ - for(i =0, zend_hash_internal_pointer_reset(ht_hash); - zend_hash_has_more_elements(ht_hash) == SUCCESS; - i++, zend_hash_move_forward(ht_hash)) { - - char *hkey, hkey_str[40]; - unsigned int hkey_len; - unsigned long idx; - int type; - zval **z_value_p; - - char *hval; - int hval_len, hval_free; - - type = zend_hash_get_current_key_ex(ht_hash, &hkey, &hkey_len, &idx, 0, NULL); - - if(zend_hash_get_current_data(ht_hash, (void**)&z_value_p) == FAILURE) { - continue; /* this should never happen */ - } - - if(type != HASH_KEY_IS_STRING) { /* convert to string */ - hkey_len = 1 + sprintf(hkey_str, "%ld", idx); - hkey = (char*)hkey_str; - } - element_count += 2; - - /* key is set. */ - hval_free = redis_serialize(redis_sock, *z_value_p, &hval, &hval_len TSRMLS_CC); - - /* Append our member and value in place */ - redis_cmd_append_sstr(&set_cmds, hkey, hkey_len - 1); - redis_cmd_append_sstr(&set_cmds, hval, hval_len); - - if(hval_free) STR_FREE(hval); - } - - /* Now construct the entire command */ - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "*%d" _NL "%s%s", element_count, cmd, cmd_len, set_cmds.c, set_cmds.len); - efree(old_cmd); - - /* Free the HMSET bits */ - efree(set_cmds.c); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); -} - - -PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { - - char *response; - int response_len, ret = 0; - - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - return 0; - } - - if(strncmp(response, "+QUEUED", 7) == 0) { - ret = 1; - } - efree(response); - return ret; + REDIS_PROCESS_CMD(hmset, redis_boolean_response); } +/* }}} */ /* flag : get, set {ATOMIC, MULTI, PIPELINE} */ @@ -4242,7 +4148,22 @@ PHP_METHOD(Redis, exec) } } -PHP_REDIS_API void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) { +PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { + char *resp; + int resp_len, ret = 0; + + if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { + return 0; + } + + if(strncmp(resp, "+QUEUED", 7) == 0) { + ret = 1; + } + efree(resp); + return ret; +} + +PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) { item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC); } diff --git a/redis_cluster.c b/redis_cluster.c index b5f957bf7e..d7a1ac5254 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -75,6 +75,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrby, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hmset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrbyfloat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) @@ -503,6 +504,12 @@ PHP_METHOD(RedisCluster, hincrbyfloat) { } /* }}} */ +/* {{{ proto bool RedisCluster::hmset(string key, array key_vals) */ +PHP_METHOD(RedisCluster, hmset) { + CLUSTER_PROCESS_CMD(hmset, cluster_bool_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp); diff --git a/redis_cluster.h b/redis_cluster.h index c847043fe7..636d1e502a 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -100,6 +100,7 @@ PHP_METHOD(RedisCluster, hlen); PHP_METHOD(RedisCluster, hget); PHP_METHOD(RedisCluster, hkeys); PHP_METHOD(RedisCluster, hvals); +PHP_METHOD(RedisCluster, hmset); PHP_METHOD(RedisCluster, hgetall); PHP_METHOD(RedisCluster, hexists); PHP_METHOD(RedisCluster, hincrby); diff --git a/redis_commands.c b/redis_commands.c index 08a9ec5dc9..b193bfa521 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -498,4 +498,89 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* HMGET */ +int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + +} + +/* HMSET */ +int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot) +{ + char *key; + int key_len, key_free, count, ktype; + unsigned long idx; + zval *z_arr; + HashTable *ht_vals; + HashPosition pos; + smart_str cmdstr = {0}; + + // Parse args + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + &z_arr)==FAILURE) + { + return FAILURE; + } + + // We can abort if we have no fields + if((count = zend_hash_num_elements(Z_ARRVAL_P(z_arr)))==0) { + return FAILURE; + } + + // Prefix our key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Grab our array as a HashTable + ht_vals = Z_ARRVAL_P(z_arr); + + // Initialize our HMSET command (key + 2x each array entry), add key + redis_cmd_init_sstr(&cmdstr, 1+(count*2), "HMSET", sizeof("HMSET")-1); + redis_cmd_append_sstr(&cmdstr, key, key_len); + + // Start traversing our key => value array + for(zend_hash_internal_pointer_reset_ex(ht_vals, &pos); + zend_hash_has_more_elements_ex(ht_vals, &pos)==SUCCESS; + zend_hash_move_forward_ex(ht_vals, &pos)) + { + char *val, kbuf[40]; + int val_len, val_free; + unsigned int key_len; + zval **z_val; + + // Grab our key, and value for this element in our input + ktype = zend_hash_get_current_key_ex(ht_vals, &key, + &key_len, &idx, 0, &pos); + zend_hash_get_current_data_ex(ht_vals, (void**)&z_val, &pos); + + // If the hash key is an integer, convert it to a string + if(ktype != HASH_KEY_IS_STRING) { + key_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); + key = (char*)kbuf; + } else { + // Length returned includes the \0 + key_len--; + } + + // Serialize value (if directed) + val_free = redis_serialize(redis_sock, *z_val, &val, &val_len + TSRMLS_CC); + + // Append the key and value to our command + redis_cmd_append_sstr(&cmdstr, key, key_len); + redis_cmd_append_sstr(&cmdstr, val, val_len); + } + + // Set slot if directed + CMD_SET_SLOT(slot,key,key_len); + + // Push return pointers + *cmd_len = cmdstr.len; + *cmd = cmdstr.c; + + // Success! + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 576ec4a64a..3732cd2c6d 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -9,7 +9,6 @@ #define CMD_SET_SLOT(slot,key,key_len) \ if(slot) *slot = cluster_hash_key(key,key_len); - /* Redis command generics. Many commands share common prototypes meaning that * we can write one function to handle all of them. For example, there are * many COMMAND key value commands, or COMMAND key commands. */ @@ -61,6 +60,12 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot); +int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot); + +int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From d5cd5d081f6d1aa183d2a06cdef17ab7511c7f78 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 6 Jun 2014 16:06:33 -0700 Subject: [PATCH 0284/1986] Added context, HMGET/HMSET Added context void pointer that we pass around, which is (thus far) really just for hmget (as we need to keep the keys around to bind them with the returned values). This requires every command construction routine and each response callback be passed this void pointer, which is always NULL except for HMGET Added HMGET and HMSET commands --- cluster_library.c | 99 +++++++++++++++++++++++++++++--------- cluster_library.h | 47 +++++++++++------- common.h | 19 ++++---- redis_cluster.c | 9 +++- redis_cluster.h | 15 +++--- redis_commands.c | 118 +++++++++++++++++++++++++++++++++++++++------- redis_commands.h | 32 ++++++------- 7 files changed, 249 insertions(+), 90 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index b7057a3475..08e81f52ed 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -966,7 +966,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, /* RAW bulk response handler */ PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c) + redisCluster *c, void *ctx) { char *resp; @@ -983,7 +983,8 @@ PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, } /* BULK response handler */ -PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { char *resp; @@ -1004,7 +1005,8 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } /* Bulk response where we expect a double */ -PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { char *resp; double dbl; @@ -1026,7 +1028,8 @@ PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) /* A boolean response. If we get here, we've consumed the '+' reply * type and will now just verify we can read the OK */ -PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { // Check that we have +OK if(c->reply_type != TYPE_LINE || c->reply_len != 2 || @@ -1039,7 +1042,8 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } /* 1 or 0 response, for things like SETNX */ -PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { // Validate our reply type, and check for a zero if(c->reply_type != TYPE_INT || c->reply_len == 0) { @@ -1050,7 +1054,8 @@ PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } /* Generic integer response */ -PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { if(c->reply_type != TYPE_INT) { RETURN_FALSE; @@ -1059,7 +1064,8 @@ PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) } /* TYPE response handler */ -PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) +PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { // Make sure we got the right kind of response if(c->reply_type != TYPE_LINE) { @@ -1082,7 +1088,7 @@ PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) /* Generic MULTI BULK response processor */ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, mbulk_cb cb) + redisCluster *c, mbulk_cb cb, void *ctx) { zval *z_result; @@ -1097,7 +1103,7 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, array_init(z_result); // Call our specified callback - if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len TSRMLS_CC) + if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len, ctx TSRMLS_CC) ==FAILURE) { zval_dtor(z_result); @@ -1112,31 +1118,44 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, /* Raw MULTI BULK reply */ PHPAPI void -cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { +cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, mbulk_resp_loop_raw); + c, mbulk_resp_loop_raw, NULL); } /* Unserialize all the things */ PHPAPI void -cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { +cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, mbulk_resp_loop); + c, mbulk_resp_loop, NULL); } /* For handling responses where we get key, value, key, value that * we will turn into key => value, key => value. */ PHPAPI void -cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { +cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, mbulk_resp_loop_zipstr); + c, mbulk_resp_loop_zipstr, NULL); } /* Handling key,value to key=>value where the values are doubles */ PHPAPI void -cluster_mbulk_resp_zipdbl(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { +cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, mbulk_resp_loop_zipdbl); + c, mbulk_resp_loop_zipdbl, NULL); +} + +/* Associate multi bulk response (for HMGET really) */ +PHPAPI void +cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + mbulk_resp_loop_assoc, ctx); } /* @@ -1145,7 +1164,7 @@ cluster_mbulk_resp_zipdbl(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) { /* MULTI BULK response where we don't touch the values (e.g. KEYS) */ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - long long count TSRMLS_DC) + long long count, void *ctx TSRMLS_DC) { char *line; int line_len; @@ -1165,8 +1184,8 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, } /* MULTI BULK response where we unserialize everything */ -int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count - TSRMLS_DC) +int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, + long long count, void *ctx TSRMLS_DC) { char *line; int line_len; @@ -1191,7 +1210,7 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count /* MULTI BULK response where we turn key1,value1 into key1=>value1 */ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, - long long count TSRMLS_DC) + long long count, void *ctx TSRMLS_DC) { char *line, *key; int line_len, key_len; @@ -1229,7 +1248,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, /* MULTI BULK loop processor where we expect key,score key, score */ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, - long long count TSRMLS_DC) + long long count, void *ctx TSRMLS_DC) { char *line, *key; int line_len, key_len; @@ -1256,4 +1275,40 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, return SUCCESS; } +/* MULTI BULK where we're passed the keys, and we attach vals */ +int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, + long long count, void *ctx TSRMLS_DC) +{ + char *line; + int line_len,i=0; + zval **z_keys = ctx, *z; + + // Loop while we've got replies + while(count--) { + line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + if(!line) return -1; + + if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { + efree(line); + add_assoc_zval_ex(z_result,Z_STRVAL_P(z_keys[i]), + 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_result, Z_STRVAL_P(z_keys[i]), + 1+Z_STRLEN_P(z_keys[i]), line, line_len, 0); + } + + // Clean up key context + zval_dtor(z_keys[i]); + efree(z_keys[i]); + + // Move to the next key + i++; + } + + // Clean up our keys overall + efree(z_keys); + + // Success! + return SUCCESS; +} /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index f41231ce63..0e34f79f6a 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -77,7 +77,7 @@ typedef enum CLUSTER_REDIR_TYPE { *c->line_reply = '\0'; c->reply_len = 0; /* MULTI BULK response callback typedef */ -typedef int (*mbulk_cb)(RedisSock*,zval*,long long TSRMLS_DC); +typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void* TSRMLS_DC); /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); @@ -200,35 +200,46 @@ PHPAPI int cluster_node_add_slave(redisCluster *cluster, * something valid, or FALSE in the case of some failures). */ -PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); -PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c); +PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); +PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); +PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); +PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); +PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); +PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); +PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); /* MULTI BULK response functions */ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, mbulk_cb func); -PHPAPI void cluster_mbulk_resp_raw(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c); + redisCluster *c, mbulk_cb func, void *ctx); +PHPAPI void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); PHPAPI void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c); + redisCluster *c, void *ctx); PHPAPI void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c); + redisCluster *c, void *ctx); PHPAPI void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c); + redisCluster *c, void *ctx); +PHPAPI void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, - long long count TSRMLS_DC); + long long count, void *ctx TSRMLS_DC); int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - long long count TSRMLS_DC); + long long count, void *ctx TSRMLS_DC); int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, - long long count TSRMLS_DC); + long long count, void *ctx TSRMLS_DC); int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, - long long count TSRMLS_DC); + long long count, void *ctx TSRMLS_DC); +int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, + long long count, void *ctx TSRMLS_DC); #endif diff --git a/common.h b/common.h index 3e8385547d..2f87cbbcca 100644 --- a/common.h +++ b/common.h @@ -175,7 +175,8 @@ else if(redis_sock->mode == MULTI) { \ REDIS_ELSE_IF_MULTI(function, closure_context) \ REDIS_ELSE_IF_PIPELINE(function, closure_context); -#define REDIS_PROCESS_RESPONSE(function) REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) +#define REDIS_PROCESS_RESPONSE(function) \ + REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) /* Clear redirection info */ #define REDIS_MOVED_CLEAR(redis_sock) \ @@ -186,32 +187,32 @@ else if(redis_sock->mode == MULTI) { \ /* Process a command assuming our command where our command building * function is redis__cmd */ #define REDIS_PROCESS_CMD(cmdname, resp_func) \ - RedisSock *redis_sock; char *cmd; int cmd_len; \ + RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || \ redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, \ - &cmd, &cmd_len, NULL)==FAILURE) { \ + &cmd, &cmd_len, NULL, &ctx)==FAILURE) { \ RETURN_FALSE; \ } \ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ IF_ATOMIC() { \ - resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); \ + resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \ } \ - REDIS_PROCESS_RESPONSE(resp_func); + REDIS_PROCESS_RESPONSE_CLOSURE(resp_func,ctx); /* Process a command but with a specific command building function * and keyword which is passed to us*/ #define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ - RedisSock *redis_sock; char *cmd; int cmd_len; \ + RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || \ cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, \ - &cmd_len, NULL)==FAILURE) { \ + &cmd_len, NULL, &ctx)==FAILURE) { \ RETURN_FALSE; \ } \ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ IF_ATOMIC() { \ - resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); \ + resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \ } \ - REDIS_PROCESS_RESPONSE(resp_func); + REDIS_PROCESS_RESPONSE_CLOSURE(resp_func,ctx); /* Extended SET argument detection */ #define IS_EX_ARG(a) \ diff --git a/redis_cluster.c b/redis_cluster.c index d7a1ac5254..d59cd1177e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -75,6 +75,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrby, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hmget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hmset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrbyfloat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) @@ -463,7 +464,7 @@ PHP_METHOD(RedisCluster, hlen) { /* {{{ proto array RedisCluster::hkeys(string key) */ PHP_METHOD(RedisCluster, hkeys) { - CLUSTER_PROCESS_KW_CMD("HKEYS", redis_key_cmd, cluster_mbulk_resp_raw); + CLUSTER_PROCESS_KW_CMD("HKEYS", redis_key_cmd, cluster_mbulk_raw_resp); } /* }}} */ @@ -510,6 +511,12 @@ PHP_METHOD(RedisCluster, hmset) { } /* }}} */ +/* {{{ proto array RedisCluster::hmget(string key, array members) */ +PHP_METHOD(RedisCluster, hmget) { + CLUSTER_PROCESS_CMD(hmget, cluster_mbulk_assoc_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 636d1e502a..4fd5d5f0b0 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -21,11 +21,9 @@ /* Simple 1-1 command -> response macro */ #define CLUSTER_PROCESS_CMD(cmdname, resp_func) \ redisCluster *c = GET_CONTEXT(); \ - char *cmd; \ - int cmd_len; \ - short slot; \ + char *cmd; int cmd_len; short slot; void *ctx=NULL; \ if(redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,c->flags, &cmd, \ - &cmd_len, &slot)==FAILURE) { \ + &cmd_len, &slot, &ctx)==FAILURE) { \ RETURN_FALSE; \ } \ if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) {\ @@ -33,14 +31,14 @@ RETURN_FALSE; \ } \ efree(cmd); \ - resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c); + resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); /* More generic processing, where only the keyword differs */ #define CLUSTER_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ redisCluster *c = GET_CONTEXT(); \ - char *cmd; int cmd_len; short slot; \ + char *cmd; int cmd_len; short slot; void *ctx=NULL; \ if(cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,\ - &slot)==FAILURE) { \ + &slot,&ctx)==FAILURE) { \ RETURN_FALSE; \ } \ if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { \ @@ -48,7 +46,7 @@ RETURN_FALSE; \ } \ efree(cmd); \ - resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c); + resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); /* For the creation of RedisCluster specific exceptions */ PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); @@ -100,6 +98,7 @@ PHP_METHOD(RedisCluster, hlen); PHP_METHOD(RedisCluster, hget); PHP_METHOD(RedisCluster, hkeys); PHP_METHOD(RedisCluster, hvals); +PHP_METHOD(RedisCluster, hmget); PHP_METHOD(RedisCluster, hmset); PHP_METHOD(RedisCluster, hgetall); PHP_METHOD(RedisCluster, hexists); diff --git a/redis_commands.c b/redis_commands.c index b193bfa521..c824e28268 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -27,7 +27,8 @@ /* Key, long, zval (serialized) */ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key = NULL, *val=NULL; int key_len, val_len, val_free, key_free; @@ -59,7 +60,8 @@ redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command construction when we just take a key and value */ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key, *val; int key_len, val_len, key_free, val_free; @@ -89,7 +91,8 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command that takes a key and an unserialized value */ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key, *val; int key_len, val_len, key_free; @@ -115,7 +118,8 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Key, string, string without serialization (ZCOUNT, ZREMRANGEBYSCORE) */ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key, *val1, *val2; int key_len, val1_len, val2_len, key_free; @@ -145,7 +149,8 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command that takes two keys */ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key1, *key2; int key1_len, key2_len; @@ -189,7 +194,8 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command construction where we take a key and a long */ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key; int key_len, key_free; @@ -222,7 +228,8 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* key, long, long */ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key; int key_len, key_free; @@ -251,7 +258,8 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command where we take a single key */ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key; int key_len, key_free; @@ -278,7 +286,8 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command where we take a key and a double */ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key; int key_len, key_free; @@ -309,7 +318,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* SET */ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) + char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_value, *z_opts=NULL; char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; @@ -396,7 +405,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* BRPOPLPUSH */ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) + char **cmd, int *cmd_len, short *slot, void **ctx) { char *key1, *key2; int key1_len, key2_len; @@ -444,9 +453,8 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* HINCRBY */ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) + char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key, *mem; int key_len, mem_len, key_free; long byval; @@ -472,7 +480,7 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* HINCRBYFLOAT */ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) + char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; int key_len, mem_len, key_free; @@ -500,14 +508,89 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* HMGET */ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot) + char **cmd, int *cmd_len, short *slot, void **ctx) { + char *key; + zval *z_arr, **z_mems, **z_mem; + int i, count, valid=0, key_len, key_free; + HashTable *ht_arr; + HashPosition ptr; + smart_str cmdstr = {0}; + + // Parse arguments + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + &z_arr)==FAILURE) + { + return FAILURE; + } + + // Our HashTable + ht_arr = Z_ARRVAL_P(z_arr); + // We can abort if we have no elements + if((count = zend_hash_num_elements(ht_arr))==0) { + return FAILURE; + } + + // Prefix our key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Allocate memory for the max members we'll grab + z_mems = ecalloc(count, sizeof(zval*)); + + // Iterate over our member array + for(zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); + zend_hash_get_current_data_ex(ht_arr, (void**)&z_mem, &ptr)==SUCCESS; + zend_hash_move_forward_ex(ht_arr, &ptr)) + { + // We can only handle string or long values here + if(Z_TYPE_PP(z_mem)==IS_STRING || Z_TYPE_PP(z_mem)==IS_LONG) { + // Copy into our member array + MAKE_STD_ZVAL(z_mems[valid]); + *z_mems[valid] = **z_mem; + zval_copy_ctor(z_mems[valid]); + convert_to_string(z_mems[valid]); + + // Increment the member count to actually send + valid++; + } + } + + // If nothing was valid, fail + if(valid == 0) { + if(key_free) efree(key); + efree(z_mems); + return FAILURE; + } + + // Start command construction + redis_cmd_init_sstr(&cmdstr, valid+1, "HMGET", sizeof("HMGET")-1); + redis_cmd_append_sstr(&cmdstr, key, key_len); + + // Iterate over members, appending as arguments + for(i=0;i Date: Fri, 6 Jun 2014 16:10:30 -0700 Subject: [PATCH 0285/1986] Updated HMGET in redis.c Updated HMGET to the new calling convention in redis.c --- redis.c | 85 +-------------------------------------------------------- 1 file changed, 1 insertion(+), 84 deletions(-) diff --git a/redis.c b/redis.c index 501ceb70ca..91c5aaa1af 100644 --- a/redis.c +++ b/redis.c @@ -3814,90 +3814,7 @@ PHP_METHOD(Redis, hIncrBy) /* {{{ array Redis::hMget(string hash, array keys) */ PHP_METHOD(Redis, hMget) { - zval *object; - RedisSock *redis_sock; - char *key = NULL; - zval *z_array, **z_keys, **data; - int field_count, i, valid, key_len, key_free; - HashTable *ht_array; - HashPosition ptr; - smart_str cmd = {0}; - - /* Make sure we can grab our arguments properly */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", - &object, redis_ce, &key, &key_len, &z_array) - == FAILURE) - { - RETURN_FALSE; - } - - /* We'll need our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* Grab member count and abort if we don't have any */ - if((field_count = zend_hash_num_elements(Z_ARRVAL_P(z_array))) == 0) { - RETURN_FALSE; - } - - /* Prefix our key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* Allocate enough memory for the number of keys being requested */ - z_keys = ecalloc(field_count, sizeof(zval *)); - - /* Grab our HashTable */ - ht_array = Z_ARRVAL_P(z_array); - - /* Iterate through our keys, grabbing members that are valid */ - for(valid=0, zend_hash_internal_pointer_reset_ex(ht_array, &ptr); - zend_hash_get_current_data_ex(ht_array, (void**)&data, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_array, &ptr)) - { - /* Make sure the data is a long or string, and if it's a string that */ - /* it isn't empty. There is no reason to send empty length members. */ - if((Z_TYPE_PP(data) == IS_STRING && Z_STRLEN_PP(data)>0) || - Z_TYPE_PP(data) == IS_LONG) - { - /* This is a key we can ask for, copy it and set it in our array */ - MAKE_STD_ZVAL(z_keys[valid]); - *z_keys[valid] = **data; - zval_copy_ctor(z_keys[valid]); - convert_to_string(z_keys[valid]); - - /* Increment the number of valid keys we've encountered */ - valid++; - } - } - - /* If we don't have any valid keys, we can abort here */ - if(valid == 0) { - if(key_free) efree(key); - efree(z_keys); - RETURN_FALSE; - } - - /* Build command header. One extra argument for the hash key itself */ - redis_cmd_init_sstr(&cmd, valid+1, "HMGET", sizeof("HMGET")-1); - - /* Add the hash key */ - redis_cmd_append_sstr(&cmd, key, key_len); - - /* Free key memory if it was prefixed */ - if(key_free) efree(key); - - /* Iterate our keys, appending them as arguments */ - for(i=0;i Date: Sat, 7 Jun 2014 09:41:31 -0700 Subject: [PATCH 0286/1986] Rework no arg commands, remove non-redis method Each of the commands that take no arguments can be reworked such that they use the new calling convention in Redis proper Impelemnted BITPOS Removed Redis::resetStat. This isn't a Redis method, but rather an option on CONFIG, and it probably should be called that way. --- php_redis.h | 1 - redis.c | 143 ++++------------------------------------------- redis_commands.c | 41 ++++++++++++++ 3 files changed, 52 insertions(+), 133 deletions(-) diff --git a/php_redis.h b/php_redis.h index 94bec03353..2c35c822a7 100644 --- a/php_redis.h +++ b/php_redis.h @@ -103,7 +103,6 @@ PHP_METHOD(Redis, ttl); PHP_METHOD(Redis, pttl); PHP_METHOD(Redis, persist); PHP_METHOD(Redis, info); -PHP_METHOD(Redis, resetStat); PHP_METHOD(Redis, select); PHP_METHOD(Redis, move); PHP_METHOD(Redis, zAdd); diff --git a/redis.c b/redis.c index 91c5aaa1af..e3070a7d62 100644 --- a/redis.c +++ b/redis.c @@ -176,7 +176,6 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, pttl, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, persist, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, info, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, resetStat, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, select, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, move, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, bgrewriteaof, NULL, ZEND_ACC_PUBLIC) @@ -858,53 +857,7 @@ PHP_METHOD(Redis, bitcount) /* {{{ proto integer Redis::bitpos(string key, int bit, [int start], [int end]) */ PHP_METHOD(Redis, bitpos) { - zval *object; - RedisSock *redis_sock; - char *key, *cmd; - int key_len, cmd_len, argc, key_free=0; - long bit, start, end; - - argc = ZEND_NUM_ARGS(); - - if(zend_parse_method_parameters(argc TSRMLS_CC, getThis(), "Osl|ll", - &object, redis_ce, &key, &key_len, &bit, - &start, &end)==FAILURE) - { - RETURN_FALSE; - } - - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* We can prevalidate the first argument */ - if(bit != 0 && bit != 1) { - RETURN_FALSE; - } - - /* Prefix our key */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* Various command semantics */ - if(argc == 2) { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd", key, key_len, - bit); - } else if(argc == 3) { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sdd", key, key_len, - bit, start); - } else { - cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sddd", key, key_len, - bit, start, end); - } - - /* Free our key if it was prefixed */ - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_CMD(bitpos, redis_long_response); } /* }}} */ @@ -961,6 +914,7 @@ PHP_METHOD(Redis, setnx) } /* }}} */ + /* {{{ proto string Redis::getSet(string key, string value) */ PHP_METHOD(Redis, getSet) @@ -1276,9 +1230,7 @@ PHP_REDIS_API void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSoc */ PHP_METHOD(Redis, unwatch) { - char cmd[] = "*1" _NL "$7" _NL "UNWATCH" _NL; - generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, estrdup(cmd), sizeof(cmd)-1, redis_unwatch_response); - + REDIS_PROCESS_KW_CMD("UNWATCH", redis_empty_cmd, redis_unwatch_response); } /* }}} */ @@ -2593,38 +2545,11 @@ PHP_METHOD(Redis, lSet) { } /* }}} */ -PHP_REDIS_API void generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ResultCallback result_callback) { - zval *object; - RedisSock *redis_sock; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - result_callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(result_callback); -} - -PHP_REDIS_API void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...) { - generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len, redis_boolean_response); -} - /* {{{ proto string Redis::save() */ PHP_METHOD(Redis, save) { - char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "SAVE", ""); - generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); - + REDIS_PROCESS_KW_CMD("SAVE", redis_empty_cmd, redis_boolean_response); } /* }}} */ @@ -2632,52 +2557,23 @@ PHP_METHOD(Redis, save) */ PHP_METHOD(Redis, bgSave) { - char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "BGSAVE", ""); - generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); - + REDIS_PROECSS_KW_CMD("BGSAVE", redis_empty_cmd, redis_boolean_response); } /* }}} */ -PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...) { - - zval *object; - RedisSock *redis_sock; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); -} - /* {{{ proto integer Redis::lastSave() */ PHP_METHOD(Redis, lastSave) { - char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "LASTSAVE", ""); - generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); + REDIS_PROCESS_KW_CMD("LASTSAVE", redis_empty_cmd, redis_long_response); } /* }}} */ - /* {{{ proto bool Redis::flushDB() */ PHP_METHOD(Redis, flushDB) { - char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "FLUSHDB", ""); - generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); + REDIS_PROCESS_KW_CMD("FLUSHDB", redis_empty_cmd, redis_boolean_response); } /* }}} */ @@ -2685,9 +2581,7 @@ PHP_METHOD(Redis, flushDB) */ PHP_METHOD(Redis, flushAll) { - char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "FLUSHALL", ""); - generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); + REDIS_PROCESS_KW_CMD("FLUSHALL", redis_empty_cmd, redis_boolean_response); } /* }}} */ @@ -2695,9 +2589,7 @@ PHP_METHOD(Redis, flushAll) */ PHP_METHOD(Redis, dbSize) { - char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "DBSIZE", ""); - generic_empty_long_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); + REDIS_PROCESS_KW_CMD("DBSIZE", redis_empty_cmd, redis_long_response); } /* }}} */ @@ -2792,17 +2684,6 @@ PHP_METHOD(Redis, info) { } /* }}} */ -/* {{{ proto string Redis::resetStat() - */ -PHP_METHOD(Redis, resetStat) -{ - char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "s", - "RESETSTAT", 9); - generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); -} -/* }}} */ - /* {{{ proto bool Redis::select(long dbNumber) */ PHP_METHOD(Redis, select) { @@ -4422,10 +4303,8 @@ PHP_METHOD(Redis, punsubscribe) */ PHP_METHOD(Redis, bgrewriteaof) { - char *cmd; - int cmd_len = redis_cmd_format_static(&cmd, "BGREWRITEAOF", ""); - generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, cmd, cmd_len); - + REDIS_PROCESS_KW_CMD("BGREWRITEAOF", redis_empty_cmd, + redis_boolean_response); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index c824e28268..55e8e91d7f 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -669,4 +669,45 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* BITPOS */ +int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key; + int argc, key_len, key_free; + long bit, start, end; + + argc = ZEND_NUM_ARGS(); + if(zend_parse_parameters(argc TSRMLS_CC, "sl|ll", &key, &key_len, &bit, + &start, &end)==FAILURE) + { + return FAILURE; + } + + // Prevalidate bit + if(bit != 0 && bit != 1) { + return FAILURE; + } + + // Prefix key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct command based on arg count + if(argc == 2) { + *cmd_len = redis_cmd_format_static(cmd, "BITPOS", "sd", key, key_len, + bit); + } else if(argc == 3) { + *cmd_len = redis_cmd_format_static(cmd, "BITPOS", "sdd", key, key_len, + bit, start); + } else { + *cmd_len = redis_cmd_format_static(cmd, "BITPOS", "sddd", key, key_len, + bit, start, end); + } + + // Set our slot + CMD_SET_SLOT(slot, key, key_len); + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From cc420c1810391b6a6f087e11c25c6e71c32361f6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 10:41:15 -0700 Subject: [PATCH 0287/1986] BITPOS/BITCOUNT/BITOP, formatting, etc Commit the rest of our "empty" commands semantics Implemented BITOP/BITPOS/BITCOUNT commands for both Redis and RedisCluster --- redis.c | 136 ++++++----------------------------------------- redis_cluster.c | 22 ++++++++ redis_cluster.h | 3 ++ redis_commands.c | 102 +++++++++++++++++++++++++++++++++++ redis_commands.h | 12 +++++ 5 files changed, 154 insertions(+), 121 deletions(-) diff --git a/redis.c b/redis.c index e3070a7d62..33fae8d71b 100644 --- a/redis.c +++ b/redis.c @@ -361,7 +361,9 @@ PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) zend_class_entry **pce; if (zend_hash_find(CG(class_table), "runtimeexception", - sizeof("RuntimeException"), (void **) &pce) == SUCCESS) { + sizeof("RuntimeException"), + (void**)&pce) == SUCCESS) + { spl_ce_RuntimeException = *pce; return *pce; } @@ -377,27 +379,26 @@ PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) #endif } -/** - * Send a static DISCARD in case we're in MULTI mode. - */ +/* Send a static DISCARD in case we're in MULTI mode. */ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { int result = FAILURE; - char *cmd, *response; - int response_len, cmd_len; + char *cmd, *resp; + int resp_len, cmd_len; /* format our discard command */ cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); /* send our DISCARD command */ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && - (response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) != NULL) { + (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) + { /* success if we get OK */ - result = (response_len == 3 && strncmp(response,"+OK", 3) == 0) ? SUCCESS : FAILURE; + result = (resp_len == 3 && strncmp(resp,"+OK", 3)==0) ? SUCCESS:FAILURE; /* free our response */ - efree(response); + efree(resp); } /* free our command */ @@ -732,125 +733,18 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { return SUCCESS; } -/* {{{ proto boolean Redis::bitop(string op, string key, ...) - */ +/* {{{ proto long Redis::bitop(string op, string key, ...) */ PHP_METHOD(Redis, bitop) { - char *cmd; - int cmd_len; - - zval **z_args; - char **keys; - int *keys_len; - int argc = ZEND_NUM_ARGS(), i; - RedisSock *redis_sock = NULL; - smart_str buf = {0}; - int key_free = 0; - - /* get redis socket */ - if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* fetch args */ - z_args = emalloc(argc * sizeof(zval*)); - if(zend_get_parameters_array(ht, argc, z_args) == FAILURE - || argc < 3 /* 3 args min. */ - || Z_TYPE_P(z_args[0]) != IS_STRING /* operation must be a string. */ - ) { - efree(z_args); - RETURN_FALSE; - } - - - keys = emalloc(argc * sizeof(char*)); - keys_len = emalloc(argc * sizeof(int)); - - /* prefix keys */ - for(i = 0; i < argc; ++i) { - convert_to_string(z_args[i]); - - keys[i] = Z_STRVAL_P(z_args[i]); - keys_len[i] = Z_STRLEN_P(z_args[i]); - if(i != 0) - key_free = redis_key_prefix(redis_sock, &keys[i], &keys_len[i]); - } - - /* start building the command */ - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, argc + 1); /* +1 for BITOP command */ - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - /* add command name */ - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, 5); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, "BITOP", 5); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - /* add keys */ - for(i = 0; i < argc; ++i) { - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, keys_len[i]); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, keys[i], keys_len[i]); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - } - /* end string */ - smart_str_0(&buf); - cmd = buf.c; - cmd_len = buf.len; - - /* cleanup */ - if(key_free) - for(i = 1; i < argc; ++i) { - efree(keys[i]); - } - efree(keys); - efree(keys_len); - efree(z_args); - - /* send */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_CMD(bitop, redis_long_response); } /* }}} */ -/* {{{ proto boolean Redis::bitcount(string key, [int start], [int end]) +/* {{{ proto long Redis::bitcount(string key, [int start], [int end]) */ PHP_METHOD(Redis, bitcount) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long start = 0, end = -1; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ll", - &object, redis_ce, - &key, &key_len, &start, &end) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* BITCOUNT key start end */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "BITCOUNT", "sdd", key, - key_len, (int)start, (int)end); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - + REDIS_PROCESS_CMD(bitcount, redis_long_response); } /* }}} */ @@ -2557,7 +2451,7 @@ PHP_METHOD(Redis, save) */ PHP_METHOD(Redis, bgSave) { - REDIS_PROECSS_KW_CMD("BGSAVE", redis_empty_cmd, redis_boolean_response); + REDIS_PROCESS_KW_CMD("BGSAVE", redis_empty_cmd, redis_boolean_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index d59cd1177e..a38f1462ae 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -92,6 +92,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, pexpireat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, append, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getbit, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitpos, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitcount, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ltrim, NULL, ZEND_ACC_PUBLIC) @@ -595,6 +598,25 @@ PHP_METHOD(RedisCluster, getbit) { } /* }}} */ +/* {{{ proto long RedisCluster::bitop(string op,string key,[string key2,...]) */ +PHP_METHOD(RedisCluster, bitop) +{ + CLUSTER_PROCESS_CMD(bitop, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::bitcount(string key, [int start, int end]) */ +PHP_METHOD(RedisCluster, bitcount) { + CLUSTER_PROCESS_CMD(bitcount, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::bitpos(string key, int bit, [int s, int end]) */ +PHP_METHOD(RedisCluster, bitpos) { + CLUSTER_PROCESS_CMD(bitpos, cluster_long_resp); +} +/* }}} */ + /* {{{ proto string Redis::lget(string key, long index) */ PHP_METHOD(RedisCluster, lget) { CLUSTER_PROCESS_KW_CMD("LGET", redis_key_long_cmd, cluster_bulk_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 4fd5d5f0b0..a0cde9c716 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -115,6 +115,9 @@ PHP_METHOD(RedisCluster, pexpire); PHP_METHOD(RedisCluster, pexpireat); PHP_METHOD(RedisCluster, append); PHP_METHOD(RedisCluster, getbit); +PHP_METHOD(RedisCluster, bitop); +PHP_METHOD(RedisCluster, bitpos); +PHP_METHOD(RedisCluster, bitcount); PHP_METHOD(RedisCluster, lget); PHP_METHOD(RedisCluster, getrange); PHP_METHOD(RedisCluster, ltrim); diff --git a/redis_commands.c b/redis_commands.c index 55e8e91d7f..1f7b1d052c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -24,6 +24,15 @@ * processing. Lots of Redis commands take something like key, value, or * key, value long. Each unique signature like this is written only once */ +/* A command that takes no arguments */ +int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + *cmd_len = redis_cmd_format_static(cmd, kw, ""); + return SUCCESS; +} + /* Key, long, zval (serialized) */ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, @@ -710,4 +719,97 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* BITOP */ +int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zval **z_args; + char *key; + int key_len, i, key_free, argc = ZEND_NUM_ARGS(); + smart_str cmdstr = {0}; + short kslot; + + // Allocate space for args, parse them as an array + z_args = emalloc(argc * sizeof(zval*)); + if(zend_get_parameters_array(ht, argc, z_args)==FAILURE || + argc < 3 || Z_TYPE_P(z_args[0]) != IS_STRING) + { + efree(z_args); + return FAILURE; + } + + // If we were passed a slot pointer, init to a sentinel value + if(slot) *slot = -1; + + // Initialize command construction, add our operation argument + redis_cmd_init_sstr(&cmdstr, argc, "BITOP", sizeof("BITOP")-1); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_args[0]), + Z_STRLEN_P(z_args[0])); + + // Now iterate over our keys argument + for(i=1;i Date: Sat, 7 Jun 2014 10:48:22 -0700 Subject: [PATCH 0288/1986] RANDOMKEY/PFCOUNT Changed RANDOMKEY to use our empty command semantics, as well as implemented PFCOUNT for both Redis and Cluster --- redis.c | 201 ++++++++++++++++++++++++++++++++++++++++++------ redis_cluster.c | 7 ++ redis_cluster.h | 2 +- 3 files changed, 186 insertions(+), 24 deletions(-) diff --git a/redis.c b/redis.c index 33fae8d71b..bb8a1bea33 100644 --- a/redis.c +++ b/redis.c @@ -821,29 +821,7 @@ PHP_METHOD(Redis, getSet) */ PHP_METHOD(Redis, randomKey) { - - zval *object; - RedisSock *redis_sock; - char *cmd; - int cmd_len; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - cmd_len = redis_cmd_format(&cmd, "*1" _NL "$9" _NL "RANDOMKEY" _NL); - /* TODO: remove prefix from key */ - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_ping_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_ping_response); + REDIS_PROCESS_KW_CMD("RANDOMKEY", redis_ping_response); } /* }}} */ @@ -5639,4 +5617,181 @@ PHP_METHOD(Redis, zscan) { generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN); } +/* + * HyperLogLog based commands + */ + +/* {{{ proto Redis::pfAdd(string key, array elements) }}} */ +PHP_METHOD(Redis, pfadd) { + zval *object; + RedisSock *redis_sock; + char *key; + int key_len, key_free, argc=1; + zval *z_mems, **z_mem; + HashTable *ht_mems; + HashPosition pos; + smart_str cmd = {0}; + + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", + &object, redis_ce, &key, &key_len, &z_mems) + ==FAILURE) + { + RETURN_FALSE; + } + + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } + + // Grab members as an array + ht_mems = Z_ARRVAL_P(z_mems); + + // Total arguments we'll be sending + argc += zend_hash_num_elements(ht_mems); + + // If the array was empty we can just exit + if(argc < 2) { + RETURN_FALSE; + } + + // Start constructing our command + redis_cmd_init_sstr(&cmd, argc, "PFADD", sizeof("PFADD")-1); + + // Prefix our key if we're prefixing + key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + redis_cmd_append_sstr(&cmd, key, key_len); + if(key_free) efree(key); + + // Iterate over members we're adding + for(zend_hash_internal_pointer_reset_ex(ht_mems, &pos); + zend_hash_get_current_data_ex(ht_mems, (void**)&z_mem, &pos)==SUCCESS; + zend_hash_move_forward_ex(ht_mems, &pos)) + { + char *mem; + int mem_len, val_free; + zval *z_tmp = NULL; + + // Serialize if requested + val_free = redis_serialize(redis_sock, *z_mem, &mem, &mem_len TSRMLS_CC); + + // Allow for non string members if we're not serializing + if(!val_free) { + if(Z_TYPE_PP(z_mem)==IS_STRING) { + mem = Z_STRVAL_PP(z_mem); + mem_len = Z_STRLEN_PP(z_mem); + } else { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = **z_mem; + convert_to_string(z_tmp); + + mem = Z_STRVAL_P(z_tmp); + mem_len = Z_STRLEN_P(z_tmp); + } + } + + // Append this member + redis_cmd_append_sstr(&cmd, mem, mem_len); + + // Free memory if we serialized or converted types + if(z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + z_tmp = NULL; + } else if(val_free) { + efree(mem); + } + } + + REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); + IF_ATOMIC() { + redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_1_response); +} + +/* {{{ proto Redis::pfCount(string key) }}}*/ +PHP_METHOD(Redis, pfcount) { + REDIS_PROCESS_KW_CMD("PFCOUNT", redis_key_cmd, redis_long_response); +} + +/* {{{ proto Redis::pfMerge(array keys) }}}*/ +PHP_METHOD(Redis, pfmerge) { + zval *object; + RedisSock *redis_sock; + zval *z_keys, **z_key; + HashTable *ht_keys; + HashPosition pos; + smart_str cmd = {0}; + int key_len, key_free, argc=1; + char *key; + + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", + &object, redis_ce, &key, &key_len, &z_keys)==FAILURE) + { + RETURN_FALSE; + } + + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } + + // Grab keys as an array + ht_keys = Z_ARRVAL_P(z_keys); + + // Total arguments we'll be sending + argc += zend_hash_num_elements(ht_keys); + + // If no keys were passed we can abort + if(argc<2) { + RETURN_FALSE; + } + + // Initial construction of our command + redis_cmd_init_sstr(&cmd, argc, "PFMERGE", sizeof("PFMERGE")-1); + + // Add our destination key (prefixed if necessary) + key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + redis_cmd_append_sstr(&cmd, key, key_len); + if(key_free) efree(key); + + // Iterate our keys array + for(zend_hash_internal_pointer_reset_ex(ht_keys, &pos); + zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &pos)==SUCCESS; + zend_hash_move_forward_ex(ht_keys, &pos)) + { + zval *z_tmp = NULL; + + // Keys could look like a number + if(Z_TYPE_PP(z_key) == IS_STRING) { + key = Z_STRVAL_PP(z_key); + key_len = Z_STRLEN_PP(z_key); + } else { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = **z_key; + convert_to_string(z_tmp); + + key = Z_STRVAL_P(z_tmp); + key_len = Z_STRLEN_P(z_tmp); + } + + // Prefix our key if necessary and append this key + key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); + redis_cmd_append_sstr(&cmd, key, key_len); + if(key_free) efree(key); + + // Free temporary zval if we converted + if(z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + z_tmp = NULL; + } + } + + REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); + IF_ATOMIC() { + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_boolean_response); +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.c b/redis_cluster.c index a38f1462ae..cced960a6c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -103,6 +103,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, publish, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rename, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, renamenx, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfcount, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -668,4 +669,10 @@ PHP_METHOD(RedisCluster, renamenx) { } /* }}} */ +/* {{{ proto long RedisCluster::pfcount(string key) */ +PHP_METHOD(RedisCluster, pfcount) { + CLUSTER_PROCESS_KW_CMD("PFCOUNT", redis_key_cmd, cluster_long_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index a0cde9c716..6d7f611673 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -127,5 +127,5 @@ PHP_METHOD(RedisCluster, publish); PHP_METHOD(RedisCluster, lset); PHP_METHOD(RedisCluster, rename); PHP_METHOD(RedisCluster, renamenx); - +PHP_METHOD(RedisCluster, pfcount); #endif From c0ce2551d5bf8ceabef74022784a6c61daeca7a4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 12:10:38 -0700 Subject: [PATCH 0289/1986] Properly handle RANDOMKEY, PFADD/PFMERGE Fixed a compile bug where we were invoking the empty command RANDOMKEY improperly. Added PFMERGE and PFCOUNT to redis and RedisCluster objects --- redis.c | 166 ++--------------------------------------------- redis_cluster.c | 15 +++++ redis_cluster.h | 2 + redis_commands.c | 121 ++++++++++++++++++++++++++++++++++ redis_commands.h | 6 ++ 5 files changed, 148 insertions(+), 162 deletions(-) diff --git a/redis.c b/redis.c index bb8a1bea33..53e5648375 100644 --- a/redis.c +++ b/redis.c @@ -821,7 +821,7 @@ PHP_METHOD(Redis, getSet) */ PHP_METHOD(Redis, randomKey) { - REDIS_PROCESS_KW_CMD("RANDOMKEY", redis_ping_response); + REDIS_PROCESS_KW_CMD("RANDOMKEY", redis_empty_cmd, redis_ping_response); } /* }}} */ @@ -5623,90 +5623,7 @@ PHP_METHOD(Redis, zscan) { /* {{{ proto Redis::pfAdd(string key, array elements) }}} */ PHP_METHOD(Redis, pfadd) { - zval *object; - RedisSock *redis_sock; - char *key; - int key_len, key_free, argc=1; - zval *z_mems, **z_mem; - HashTable *ht_mems; - HashPosition pos; - smart_str cmd = {0}; - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", - &object, redis_ce, &key, &key_len, &z_mems) - ==FAILURE) - { - RETURN_FALSE; - } - - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - // Grab members as an array - ht_mems = Z_ARRVAL_P(z_mems); - - // Total arguments we'll be sending - argc += zend_hash_num_elements(ht_mems); - - // If the array was empty we can just exit - if(argc < 2) { - RETURN_FALSE; - } - - // Start constructing our command - redis_cmd_init_sstr(&cmd, argc, "PFADD", sizeof("PFADD")-1); - - // Prefix our key if we're prefixing - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - redis_cmd_append_sstr(&cmd, key, key_len); - if(key_free) efree(key); - - // Iterate over members we're adding - for(zend_hash_internal_pointer_reset_ex(ht_mems, &pos); - zend_hash_get_current_data_ex(ht_mems, (void**)&z_mem, &pos)==SUCCESS; - zend_hash_move_forward_ex(ht_mems, &pos)) - { - char *mem; - int mem_len, val_free; - zval *z_tmp = NULL; - - // Serialize if requested - val_free = redis_serialize(redis_sock, *z_mem, &mem, &mem_len TSRMLS_CC); - - // Allow for non string members if we're not serializing - if(!val_free) { - if(Z_TYPE_PP(z_mem)==IS_STRING) { - mem = Z_STRVAL_PP(z_mem); - mem_len = Z_STRLEN_PP(z_mem); - } else { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_mem; - convert_to_string(z_tmp); - - mem = Z_STRVAL_P(z_tmp); - mem_len = Z_STRLEN_P(z_tmp); - } - } - - // Append this member - redis_cmd_append_sstr(&cmd, mem, mem_len); - - // Free memory if we serialized or converted types - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } else if(val_free) { - efree(mem); - } - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); + REDIS_PROCESS_CMD(pfadd, redis_1_response); } /* {{{ proto Redis::pfCount(string key) }}}*/ @@ -5714,84 +5631,9 @@ PHP_METHOD(Redis, pfcount) { REDIS_PROCESS_KW_CMD("PFCOUNT", redis_key_cmd, redis_long_response); } -/* {{{ proto Redis::pfMerge(array keys) }}}*/ +/* {{{ proto Redis::pfMerge(string dstkey, array keys) }}}*/ PHP_METHOD(Redis, pfmerge) { - zval *object; - RedisSock *redis_sock; - zval *z_keys, **z_key; - HashTable *ht_keys; - HashPosition pos; - smart_str cmd = {0}; - int key_len, key_free, argc=1; - char *key; - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", - &object, redis_ce, &key, &key_len, &z_keys)==FAILURE) - { - RETURN_FALSE; - } - - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - // Grab keys as an array - ht_keys = Z_ARRVAL_P(z_keys); - - // Total arguments we'll be sending - argc += zend_hash_num_elements(ht_keys); - - // If no keys were passed we can abort - if(argc<2) { - RETURN_FALSE; - } - - // Initial construction of our command - redis_cmd_init_sstr(&cmd, argc, "PFMERGE", sizeof("PFMERGE")-1); - - // Add our destination key (prefixed if necessary) - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - redis_cmd_append_sstr(&cmd, key, key_len); - if(key_free) efree(key); - - // Iterate our keys array - for(zend_hash_internal_pointer_reset_ex(ht_keys, &pos); - zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &pos)==SUCCESS; - zend_hash_move_forward_ex(ht_keys, &pos)) - { - zval *z_tmp = NULL; - - // Keys could look like a number - if(Z_TYPE_PP(z_key) == IS_STRING) { - key = Z_STRVAL_PP(z_key); - key_len = Z_STRLEN_PP(z_key); - } else { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_key; - convert_to_string(z_tmp); - - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); - } - - // Prefix our key if necessary and append this key - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - redis_cmd_append_sstr(&cmd, key, key_len); - if(key_free) efree(key); - - // Free temporary zval if we converted - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_CMD(pfmerge, redis_boolean_response); } /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.c b/redis_cluster.c index cced960a6c..d9e3044dfe 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -104,6 +104,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, rename, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, renamenx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pfcount, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfadd, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfmerge, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -675,4 +677,17 @@ PHP_METHOD(RedisCluster, pfcount) { } /* }}} */ +/* {{{ proto bool RedisCluster::pfadd(string key, array vals) */ +PHP_METHOD(RedisCluster, pfadd) { + CLUSTER_PROCESS_CMD(pfadd, cluster_1_resp); +} +/* }}} */ + +/* {{{ proto bool RedisCluster::pfmerge(string key, array keys) */ +PHP_METHOD(RedisCluster, pfmerge) { + CLUSTER_PROCESS_CMD(pfmerge, cluster_bool_resp); +} +/* }}} */ + + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 6d7f611673..a09600bf96 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -128,4 +128,6 @@ PHP_METHOD(RedisCluster, lset); PHP_METHOD(RedisCluster, rename); PHP_METHOD(RedisCluster, renamenx); PHP_METHOD(RedisCluster, pfcount); +PHP_METHOD(RedisCluster, pfadd); +PHP_METHOD(RedisCluster, pfmerge); #endif diff --git a/redis_commands.c b/redis_commands.c index 1f7b1d052c..85d04ea53f 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -812,4 +812,125 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* PFADD and PFMERGE are the same except that in one case we serialize, + * and in the other case we key prefix */ +static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, int kw_len, int is_keys, char **cmd, + int *cmd_len, short *slot) +{ + zval *z_arr, **z_ele; + HashTable *ht_arr; + HashPosition pos; + smart_str cmdstr = {0}; + char *mem, *key; + int key_len, key_free; + int mem_len, mem_free, argc=1; + + // Parse arguments + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + &z_arr)==FAILURE) + { + return FAILURE; + } + + // Grab HashTable, count total argc + ht_arr = Z_ARRVAL_P(z_arr); + argc += zend_hash_num_elements(ht_arr); + + // We need at least two arguments + if(argc < 2) { + return FAILURE; + } + + // Prefix key, set initial hash slot + key_free = redis_key_prefix(redis_sock, &key, &key_len); + if(slot) *slot = cluster_hash_key(key, key_len); + + // Start command construction + redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); + redis_cmd_append_sstr(&cmdstr, key, key_len); + + // Free key if we prefixed + if(key_free) efree(key); + + // Now iterate over the rest of our keys or values + for(zend_hash_internal_pointer_reset_ex(ht_arr, &pos); + zend_hash_get_current_data_ex(ht_arr, (void**)&z_ele, &pos)==SUCCESS; + zend_hash_move_forward_ex(ht_arr, &pos)) + { + zval *z_tmp = NULL; + + // Prefix keys, serialize values + if(is_keys) { + if(Z_TYPE_PP(z_ele)!=IS_STRING) { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = **z_ele; + convert_to_string(z_tmp); + z_ele = &z_tmp; + } + mem = Z_STRVAL_PP(z_ele); + mem_len = Z_STRLEN_PP(z_ele); + + // Key prefix + mem_free = redis_key_prefix(redis_sock, &mem, &mem_len); + + // Verify slot + if(slot && *slot != cluster_hash_key(mem, mem_len)) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "All keys must hash to the same slot!"); + if(key_free) efree(key); + if(z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + } + return FAILURE; + } + } else { + if(redis_serialize(redis_sock, *z_ele, &mem, &mem_len TSRMLS_CC)==0) + { + if(Z_TYPE_PP(z_ele)!=IS_STRING) { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = **z_ele; + convert_to_string(z_tmp); + z_ele = &z_tmp; + } + mem = Z_STRVAL_PP(z_ele); + mem_len = Z_STRLEN_PP(z_ele); + } + } + + // Append our key or member + redis_cmd_append_sstr(&cmdstr, mem, mem_len); + + // Clean up our temp val if it was used + if(z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + z_tmp = NULL; + } + } + + // Push output arguments + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +/* PFADD */ +int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + "PFADD", sizeof("PFADD")-1, 0, cmd, cmd_len, slot); +} + +/* PFMERGE */ +int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + "PFMERGE", sizeof("PFMERGE")-1, 1, cmd, cmd_len, slot); +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index ad7f07c6a8..a80a6cbf6d 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -78,6 +78,12 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From d8450d93f2e6f38965869fd5deb5e03500b80d2d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 12:50:47 -0700 Subject: [PATCH 0290/1986] Update/fix ECHO, implement auth Updated ECHO and fixed it (we shouldn't prefix like it's a key) Implemented AUTH in the new command structure. Interesting to see how AUTH works in Cluster. --- redis.c | 57 +++------------------------------------------- redis_commands.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++-- redis_commands.h | 6 +++++ 3 files changed, 66 insertions(+), 56 deletions(-) diff --git a/redis.c b/redis.c index 53e5648375..51eb96b945 100644 --- a/redis.c +++ b/redis.c @@ -825,35 +825,11 @@ PHP_METHOD(Redis, randomKey) } /* }}} */ -/* {{{ proto string Redis::echo(string key) +/* {{{ proto string Redis::echo(string msg) */ PHP_METHOD(Redis, echo) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len; - int key_free; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s", key, key_len); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); + REDIS_PROCESS_KW_CMD("ECHO", redis_str_cmd, redis_string_response); } /* }}} */ @@ -2468,34 +2444,7 @@ PHP_METHOD(Redis, dbSize) /* {{{ proto bool Redis::auth(string passwd) */ PHP_METHOD(Redis, auth) { - - zval *object; - RedisSock *redis_sock; - - char *cmd, *password; - int cmd_len, password_len; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, &password, &password_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", password, - password_len); - - /* Free previously stored auth if we have one, and store this password */ - if(redis_sock->auth) efree(redis_sock->auth); - redis_sock->auth = estrndup(password, password_len); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_CMD(auth, redis_boolean_response); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 85d04ea53f..f6e599e1d4 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -33,6 +33,26 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic command where we just take a string and do nothing to it*/ +int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *arg; + int arg_len; + + // Parse args + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) + ==FAILURE) + { + return FAILURE; + } + + // Build the command without molesting the string + *cmd_len = redis_cmd_format_static(cmd, kw, "s", arg, arg_len); + + return SUCCESS; +} + /* Key, long, zval (serialized) */ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, @@ -886,8 +906,10 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } } else { - if(redis_serialize(redis_sock, *z_ele, &mem, &mem_len TSRMLS_CC)==0) - { + mem_free = redis_serialize(redis_sock, *z_ele, &mem, &mem_len + TSRMLS_CC); + + if(!mem_free) { if(Z_TYPE_PP(z_ele)!=IS_STRING) { MAKE_STD_ZVAL(z_tmp); *z_tmp = **z_ele; @@ -908,6 +930,15 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, efree(z_tmp); z_tmp = NULL; } + + // Clean up prefixed or serialized data + if(mem_free) { + if(!is_keys) { + STR_FREE(mem); + } else { + efree(mem); + } + } } // Push output arguments @@ -933,4 +964,28 @@ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "PFMERGE", sizeof("PFMERGE")-1, 1, cmd, cmd_len, slot); } +/* AUTH -- we need to update the password stored in RedisSock */ +int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *pw; + int pw_len; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pw, &pw_len) + ==FAILURE) + { + return FAILURE; + } + + // Construct our AUTH command + *cmd_len = redis_cmd_format_static(cmd, "AUTH", "s", pw, pw_len); + + // Free previously allocated password, and update + if(redis_sock->auth) efree(redis_sock->auth); + redis_sock->auth = estrndup(pw, pw_len); + + // Success + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index a80a6cbf6d..396eaec86e 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -16,6 +16,9 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -84,6 +87,9 @@ int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 63390a667dd7e277cbc9b69f971de853a123b578 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 12:52:34 -0700 Subject: [PATCH 0291/1986] Implemented PING in new way Implemented PING using the new command structure. Still need to figure out how something like PING should work in the context of cluster (random node, user directed) --- redis.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/redis.c b/redis.c index 51eb96b945..99d7d71b97 100644 --- a/redis.c +++ b/redis.c @@ -865,27 +865,7 @@ PHP_METHOD(Redis, get) */ PHP_METHOD(Redis, ping) { - zval *object; - RedisSock *redis_sock; - char *cmd; - int cmd_len; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - cmd_len = redis_cmd_format_static(&cmd, "PING", ""); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_ping_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_ping_response); + REDIS_PROCESS_KW_CMD("PING", redis_empty_cmd, redis_ping_response); } /* }}} */ From 42c97b081b72547e751283f3333ab74afe9c65f9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 16:32:17 -0700 Subject: [PATCH 0292/1986] SETRANGE/RESTORE Implemented SETRANGE and RESTORE commands, which both take the prototype key, long, string --- redis.c | 58 ++++-------------------------------------------- redis_cluster.c | 15 +++++++++++++ redis_cluster.h | 2 ++ redis_commands.c | 38 +++++++++++++++++++++++++++---- redis_commands.h | 3 +++ 5 files changed, 58 insertions(+), 58 deletions(-) diff --git a/redis.c b/redis.c index 99d7d71b97..98ea0a7cc9 100644 --- a/redis.c +++ b/redis.c @@ -1096,31 +1096,8 @@ PHP_METHOD(Redis, getRange) PHP_METHOD(Redis, setRange) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *val, *cmd; - int key_len, val_len, cmd_len, key_free; - long offset; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osls", - &object, redis_ce, &key, &key_len, - &offset, &val, &val_len) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETRANGE", "sds", key, - key_len, (int)offset, val, val_len); - if(key_free) efree(key); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, + redis_long_response); } /* {{{ proto long Redis::getbit(string key, long idx) */ @@ -4919,35 +4896,8 @@ PHP_METHOD(Redis, debug) { * {{{ proto Redis::restore(ttl, key, value) */ PHP_METHOD(Redis, restore) { - zval *object; - RedisSock *redis_sock; - char *cmd, *key, *value; - int cmd_len, key_len, value_len, key_free; - long ttl; - - /* Parse our arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osls", &object, redis_ce, - &key, &key_len, &ttl, &value, &value_len) == FAILURE) { - RETURN_FALSE; - } - - /* Grab our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* Prefix the key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "RESTORE", "sls", key, - key_len, ttl, value, value_len); - if(key_free) efree(key); - - /* Kick off our restore request */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_val_cmd, + redis_boolean_response); } /* diff --git a/redis_cluster.c b/redis_cluster.c index d9e3044dfe..6e4ce54af7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -106,6 +106,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, pfcount, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pfadd, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pfmerge, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setrange, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, restore, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -689,5 +691,18 @@ PHP_METHOD(RedisCluster, pfmerge) { } /* }}} */ +/* {{{ proto boolean RedisCluster::restore(string key, long ttl, string val) */ +PHP_METHOD(RedisCluster, restore) { + CLUSTER_PROCESS_KW_CMD("RESTORE", redis_key_long_str_cmd, + cluster_bool_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::setrange(string key, long offset, string val) */ +PHP_METHOD(RedisCluster, setrange) { + CLUSTER_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, + cluster_long_resp); +} +/* }}} */ /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index a09600bf96..68fac549f8 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -130,4 +130,6 @@ PHP_METHOD(RedisCluster, renamenx); PHP_METHOD(RedisCluster, pfcount); PHP_METHOD(RedisCluster, pfadd); PHP_METHOD(RedisCluster, pfmerge); +PHP_METHOD(RedisCluster, restore); +PHP_METHOD(RedisCluster, setrange); #endif diff --git a/redis_commands.c b/redis_commands.c index f6e599e1d4..955a5d8f37 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -54,10 +54,9 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, } /* Key, long, zval (serialized) */ -int -redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key = NULL, *val=NULL; int key_len, val_len, val_free, key_free; @@ -87,6 +86,37 @@ redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic key, long, string (unserialized) */ +int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + char *key, *val; + int key_len, val_len, key_free; + long lval; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &key, &key_len, + &lval, &val, &val_len)==FAILURE) + { + return FAILURE; + } + + // Prefix our key if requested + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct command + *cmd_len = redis_cmd_format_static(&cmd, kw, "sds", key, key_len, (int)lval, + val, val_len); + + // Set slot + CMD_SET_SLOT(slot,key,key_len); + + // Free our key if we prefixed + if(key_free) efree(key); + + return SUCCESS; +} + /* Generic command construction when we just take a key and value */ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, diff --git a/redis_commands.h b/redis_commands.h index 396eaec86e..8a8360b435 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -22,6 +22,9 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); From 5bd71f921e45565b67d21088388698ec7f3e36a3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 16:44:38 -0700 Subject: [PATCH 0293/1986] SETBIT Implemented SETBIT command --- redis.c | 32 +------------------------------- redis_cluster.c | 6 ++++++ redis_cluster.h | 1 + redis_commands.c | 35 ++++++++++++++++++++++++++++++++++- redis_commands.h | 3 +++ 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/redis.c b/redis.c index 98ea0a7cc9..4ad7b0807e 100644 --- a/redis.c +++ b/redis.c @@ -1109,37 +1109,7 @@ PHP_METHOD(Redis, getBit) PHP_METHOD(Redis, setBit) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long offset; - zend_bool val; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslb", - &object, redis_ce, &key, &key_len, - &offset, &val) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* GETBIT and SETBIT only work for 0 - 2^32-1 */ - if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd", key, - key_len, (int)offset, (int)val); - if(key_free) efree(key); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_CMD(setbit, redis_long_response); } /* {{{ proto long Redis::strlen(string key) */ diff --git a/redis_cluster.c b/redis_cluster.c index 6e4ce54af7..c4b24daa12 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -92,6 +92,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, pexpireat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, append, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getbit, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setbit, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitpos, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitcount, NULL, ZEND_ACC_PUBLIC) @@ -603,6 +604,11 @@ PHP_METHOD(RedisCluster, getbit) { } /* }}} */ +/* {{{ proto long RedisCluster::setbit(string key, long offset, bool onoff) */ +PHP_METHOD(RedisCluster, setbit) { + CLUSTER_PROCESS_CMD(setbit, cluster_long_resp); +} + /* {{{ proto long RedisCluster::bitop(string op,string key,[string key2,...]) */ PHP_METHOD(RedisCluster, bitop) { diff --git a/redis_cluster.h b/redis_cluster.h index 68fac549f8..4ecdd7eee0 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -115,6 +115,7 @@ PHP_METHOD(RedisCluster, pexpire); PHP_METHOD(RedisCluster, pexpireat); PHP_METHOD(RedisCluster, append); PHP_METHOD(RedisCluster, getbit); +PHP_METHOD(RedisCluster, setbit); PHP_METHOD(RedisCluster, bitop); PHP_METHOD(RedisCluster, bitpos); PHP_METHOD(RedisCluster, bitcount); diff --git a/redis_commands.c b/redis_commands.c index 955a5d8f37..9f05466895 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -105,7 +105,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command - *cmd_len = redis_cmd_format_static(&cmd, kw, "sds", key, key_len, (int)lval, + *cmd_len = redis_cmd_format_static(cmd, kw, "sds", key, key_len, (int)lval, val, val_len); // Set slot @@ -1018,4 +1018,37 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* SETBIT */ +int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key; + int key_len, key_free; + long offset; + zend_bool val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slb", &key, &key_len, + &offset, &val)==FAILURE) + { + return FAILURE; + } + + // Validate our offset + if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Invalid OFFSET for bitop command (must be between 0-2^32-1)"); + return FAILURE; + } + + key_free = redis_key_prefix(redis_sock, &key, &key_len); + *cmd_len = redis_cmd_format_static(cmd, "SETBIT", "sdd", key, key_len, + (int)offset, (int)val); + + CMD_SET_SLOT(slot, key, key_len); + + if(key_free) efree(key); + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 8a8360b435..33c2766b23 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -93,6 +93,9 @@ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 6eeee86f9fa1b9d37a8bbba6241a02cac0ccb799 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 16:58:10 -0700 Subject: [PATCH 0294/1986] LINSERT Implemented LINSERT command --- redis.c | 44 +------------------------------------------- redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + redis_commands.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 5 files changed, 56 insertions(+), 43 deletions(-) diff --git a/redis.c b/redis.c index 4ad7b0807e..9a227cfe7f 100644 --- a/redis.c +++ b/redis.c @@ -1157,49 +1157,7 @@ PHP_METHOD(Redis, rPush) PHP_METHOD(Redis, lInsert) { - - zval *object; - RedisSock *redis_sock; - char *pivot, *position, *key, *val, *cmd; - int pivot_len, position_len, key_len, val_len, cmd_len; - int val_free, pivot_free, key_free; - zval *z_value, *z_pivot; - - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osszz", - &object, redis_ce, - &key, &key_len, - &position, &position_len, - &z_pivot, - &z_value) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - if(strncasecmp(position, "after", 5) == 0 || strncasecmp(position, "before", 6) == 0) { - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC); - cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", - key, key_len, position, position_len, - pivot, pivot_len, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - if(pivot_free) STR_FREE(pivot); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - } else { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error on position"); - } - + REDIS_PROCESS_CMD(linsert, redis_long_response); } /* {{{ proto long Redis::lPushx(string key, mixed value) */ diff --git a/redis_cluster.c b/redis_cluster.c index c4b24daa12..a15d861439 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -54,6 +54,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, linsert, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, brpoplpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpoplpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, llen, NULL, ZEND_ACC_PUBLIC) @@ -373,6 +374,12 @@ PHP_METHOD(RedisCluster, lpushx) { } /* }}} */ +/* {{{ proto long RedisCluster::linsert(string k,string pos,mix pvt,mix val) */ +PHP_METHOD(RedisCluster, linsert) { + CLUSTER_PROCESS_CMD(linsert, cluster_long_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::rpoplpush(string key, string key) */ PHP_METHOD(RedisCluster, rpoplpush) { CLUSTER_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, cluster_bulk_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 4ecdd7eee0..a60fafe82f 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -79,6 +79,7 @@ PHP_METHOD(RedisCluster, rpop); PHP_METHOD(RedisCluster, spop); PHP_METHOD(RedisCluster, rpushx); PHP_METHOD(RedisCluster, lpushx); +PHP_METHOD(RedisCluster, linsert); PHP_METHOD(RedisCluster, brpoplpush); PHP_METHOD(RedisCluster, rpoplpush); PHP_METHOD(RedisCluster, llen); diff --git a/redis_commands.c b/redis_commands.c index 9f05466895..128181f8d4 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1051,4 +1051,48 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* LINSERT */ +int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *pivot, *pos, *val; + int key_len, pivot_len, pos_len, val_len; + int key_free, pivot_free, val_free; + zval *z_val, *z_pivot; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sszz", &key, &key_len, + &pos, &pos_len, &z_pivot, &z_val)==FAILURE) + { + return FAILURE; + } + + // Validate position + if(strncasecmp(pos, "after", 5)!=0 && strncasecmp(pos, "before", 6)==0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Position must be either 'BEFORE' or 'AFTER'"); + return FAILURE; + } + + // Prefix key, serialize value and position + key_free = redis_key_prefix(redis_sock, &key, &key_len); + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len + TSRMLS_CC); + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, "LINSERT", "ssss", key, key_len, + pos, pos_len, pivot, pivot_len, val, val_len); + + // Set slot + CMD_SET_SLOT(slot, key, key_len); + + // Clean up + if(val_free) STR_FREE(val); + if(key_free) efree(key); + if(pivot_free) STR_FREE(pivot); + + // Success + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 33c2766b23..eddc07dd1e 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -96,6 +96,9 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 2f3257a8e3c1bec7578028851e60013a2745676b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 17:06:02 -0700 Subject: [PATCH 0295/1986] LREM Implemented LREM command --- redis.c | 34 +--------------------------------- redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + redis_commands.c | 34 ++++++++++++++++++++++++++++++++++ redis_commands.h | 2 ++ 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/redis.c b/redis.c index 9a227cfe7f..7323f494f7 100644 --- a/redis.c +++ b/redis.c @@ -1247,39 +1247,7 @@ PHP_METHOD(Redis, lSize) */ PHP_METHOD(Redis, lRemove) { - zval *object; - RedisSock *redis_sock; - char *cmd; - int cmd_len, key_len, val_len; - char *key, *val; - long count = 0; - zval *z_value; - int val_free, key_free = 0; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|l", - &object, redis_ce, - &key, &key_len, &z_value, &count) == FAILURE) { - RETURN_NULL(); - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - - /* LREM key count value */ - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, - key_len, count, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_CMD(lrem, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index a15d861439..4a3f5c4aa4 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -55,6 +55,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, linsert, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lrem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, brpoplpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpoplpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, llen, NULL, ZEND_ACC_PUBLIC) @@ -380,6 +381,12 @@ PHP_METHOD(RedisCluster, linsert) { } /* }}} */ +/* {{{ proto long RedisCluster::lrem(string key, long count, string val) */ +PHP_METHOD(RedisCluster, lrem) { + CLUSTER_PROCESS_CMD(lrem, cluster_long_resp); +} +/* }}} */ + /* {{{ proto string RedisCluster::rpoplpush(string key, string key) */ PHP_METHOD(RedisCluster, rpoplpush) { CLUSTER_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, cluster_bulk_resp); diff --git a/redis_cluster.h b/redis_cluster.h index a60fafe82f..c4d1cb2be1 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -80,6 +80,7 @@ PHP_METHOD(RedisCluster, spop); PHP_METHOD(RedisCluster, rpushx); PHP_METHOD(RedisCluster, lpushx); PHP_METHOD(RedisCluster, linsert); +PHP_METHOD(RedisCluster, lrem); PHP_METHOD(RedisCluster, brpoplpush); PHP_METHOD(RedisCluster, rpoplpush); PHP_METHOD(RedisCluster, llen); diff --git a/redis_commands.c b/redis_commands.c index 128181f8d4..7eaf5b4416 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1095,4 +1095,38 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* LREM */ +int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *val; + int key_len, val_len, key_free, val_free; + long count = 0; + zval *z_val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &key, &key_len, + &z_val, &count)==FAILURE) + { + return FAILURE; + } + + // Prefix key, serialize value + key_free = redis_key_prefix(redis_sock, &key, &key_len); + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, "LREM", "sds", key, key_len, count, + val, val_len); + + // Set slot + CMD_SET_SLOT(slot, key, key_len); + + // Cleanup + if(val_free) STR_FREE(val); + if(key_free) efree(key); + + // Success! + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index eddc07dd1e..e535b18d39 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -99,6 +99,8 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 27815795fa7fec0d25a02bfbf0fe0456b107ba38 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 17:15:37 -0700 Subject: [PATCH 0296/1986] SMOVE Implemented SMOVE command --- redis.c | 34 +--------------------------------- redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + redis_commands.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 4 ++++ 5 files changed, 59 insertions(+), 33 deletions(-) diff --git a/redis.c b/redis.c index 7323f494f7..769b991b20 100644 --- a/redis.c +++ b/redis.c @@ -1322,39 +1322,7 @@ PHP_METHOD(Redis, sRemove) */ PHP_METHOD(Redis, sMove) { - zval *object; - RedisSock *redis_sock; - char *src = NULL, *dst = NULL, *val = NULL, *cmd; - int src_len, dst_len, val_len, cmd_len; - int val_free, src_free, dst_free; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossz", - &object, redis_ce, - &src, &src_len, - &dst, &dst_len, - &z_value) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - src_free = redis_key_prefix(redis_sock, &src, &src_len); - dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); - cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, - src_len, dst, dst_len, val, val_len); - if(val_free) STR_FREE(val); - if(src_free) efree(src); - if(dst_free) efree(dst); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_1_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_1_response); + REDIS_PROCESS_CMD(smove, redis_1_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 4a3f5c4aa4..8321a8be76 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -111,6 +111,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, pfmerge, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, restore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, smove, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -423,6 +424,12 @@ PHP_METHOD(RedisCluster, sismember) { } /* }}} */ +/* {{{ proto bool RedisCluster::smove(sting src, string dst, string mem) */ +PHP_METHOD(RedisCluster, smove) { + CLUSTER_PROCESS_CMD(smove, cluster_1_resp); +} +/* }}} */ + /* {{{ proto bool RedisCluster::persist(string key) */ PHP_METHOD(RedisCluster, persist) { CLUSTER_PROCESS_KW_CMD("PERSIST", redis_key_cmd, cluster_1_resp); diff --git a/redis_cluster.h b/redis_cluster.h index c4d1cb2be1..c2016c2f1e 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -135,4 +135,5 @@ PHP_METHOD(RedisCluster, pfadd); PHP_METHOD(RedisCluster, pfmerge); PHP_METHOD(RedisCluster, restore); PHP_METHOD(RedisCluster, setrange); +PHP_METHOD(RedisCluster, smove); #endif diff --git a/redis_commands.c b/redis_commands.c index 7eaf5b4416..156e0256b4 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1129,4 +1129,50 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *src, *dst, *val; + int src_len, dst_len, val_len; + int val_free, src_free, dst_free; + zval *z_val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &src, &src_len, + &dst, &dst_len, &z_val)==FAILURE) + { + return FAILURE; + } + + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + src_free = redis_key_prefix(redis_sock, &src, &src_len); + dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); + + // Protect against a CROSSSLOT error + if(slot) { + short slot1 = cluster_hash_key(src, src_len); + short slot2 = cluster_hash_key(dst, dst_len); + if(slot1 != slot2) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Source and destination keys don't hash to the same slot!"); + if(val_free) STR_FREE(val); + if(src_free) efree(src); + if(dst_free) efree(dst); + return FAILURE; + } + *slot = slot1; + } + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, "SMOVE", "sss", src, src_len, dst, + dst_len, val, val_len); + + // Cleanup + if(val_free) STR_FREE(val); + if(src_free) efree(src); + if(dst_free) efree(dst); + + // Succcess! + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index e535b18d39..006d4a7913 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -101,6 +101,10 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 39a8673b86c8e073613d6b52f2c48f1a22f8d9ff Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 18:10:34 -0700 Subject: [PATCH 0297/1986] Added ZRANGE and ZREVRANGE Implemented ZRANGE and ZREVRANGE for both Redis and cluster. We can't use generic command processing here as the return type depends on the optional WITHSCORES bit. In addition, switched the code around such that zReverseRange is an alias of zRevRange because ZREVRANGE is the actual Redis command name. --- php_redis.h | 2 +- redis.c | 126 +++++++++++++++-------------------------------- redis_cluster.c | 44 +++++++++++++++++ redis_cluster.h | 2 + redis_commands.c | 34 +++++++++++++ redis_commands.h | 5 +- 6 files changed, 123 insertions(+), 90 deletions(-) diff --git a/php_redis.h b/php_redis.h index 2c35c822a7..428440e848 100644 --- a/php_redis.h +++ b/php_redis.h @@ -108,7 +108,7 @@ PHP_METHOD(Redis, move); PHP_METHOD(Redis, zAdd); PHP_METHOD(Redis, zDelete); PHP_METHOD(Redis, zRange); -PHP_METHOD(Redis, zReverseRange); +PHP_METHOD(Redis, zRevRange); PHP_METHOD(Redis, zRangeByScore); PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zRevRangeByScore); diff --git a/redis.c b/redis.c index 769b991b20..48dc900f2c 100644 --- a/redis.c +++ b/redis.c @@ -193,7 +193,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zAdd, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zDelete, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zReverseRange, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRange, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByLex, NULL, ZEND_ACC_PUBLIC) @@ -323,7 +323,7 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, scard, sSize, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, srem, sRemove, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zrevrange, zReverseRange, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zReverseRange, zRevRange, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC) @@ -2596,53 +2596,55 @@ PHP_METHOD(Redis, zAdd) { REDIS_PROCESS_RESPONSE(redis_long_response); } /* }}} */ -/* {{{ proto array Redis::zRange(string key, int start , int end, bool withscores = FALSE) - */ -PHP_METHOD(Redis, zRange) -{ - zval *object; + +/* Handle ZRANGE and ZREVRANGE as they're the same except for keyword */ +static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw) { + char *cmd; + int cmd_len; RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long start, end; - long withscores = 0; + zend_bool withscores=0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll|b", - &object, redis_ce, - &key, &key_len, &start, &end, &withscores) == FAILURE) { + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if(redis_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, + &cmd_len, &withscores, NULL, NULL)==FAILURE) + { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len); - if(withscores) { - cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdds", key, - key_len, start, end, "WITHSCORES", 10); - } else { - cmd_len = redis_cmd_format_static(&cmd, "ZRANGE", "sdd", key, - key_len, start, end); - } - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(withscores) { - IF_ATOMIC() { - redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); + IF_ATOMIC() { + redis_sock_read_multibulk_reply_zipped( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped); } else { - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } + IF_ATOMIC() { + if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL)<0) + { + RETURN_FALSE; } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + } + REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); } } + +/* {{{ proto array Redis::zRange(string key,int start,int end,bool scores=0) */ +PHP_METHOD(Redis, zRange) +{ + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE"); +} + +/* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores=0) */ +PHP_METHOD(Redis, zRevRange) { + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE"); +} +/* }}} */ + /* }}} */ /* {{{ proto long Redis::zDelete(string key, string member) */ @@ -2679,57 +2681,7 @@ PHP_METHOD(Redis, zDeleteRangeByRank) } /* }}} */ -/* {{{ proto array Redis::zReverseRange(string key, int start , int end, bool withscores = FALSE) - */ -PHP_METHOD(Redis, zReverseRange) -{ - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - long start, end; - long withscores = 0; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osll|b", - &object, redis_ce, - &key, &key_len, &start, &end, &withscores) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - if(withscores) { - cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdds", - key, key_len, start, end, "WITHSCORES", - 10); - } else { - cmd_len = redis_cmd_format_static(&cmd, "ZREVRANGE", "sdd", - key, key_len, start, end); - } - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - if(withscores) { - IF_ATOMIC() { - redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); - } else { - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); - } -} -/* }}} */ - -PHP_REDIS_API void +PHPAPI void redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { zval *object, *z_options = NULL, **z_limit_val_pp = NULL, **z_withscores_val_pp = NULL; diff --git a/redis_cluster.c b/redis_cluster.c index 8321a8be76..6640047d39 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -112,6 +112,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, setrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, restore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, smove, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrange, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrange, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -732,4 +734,46 @@ PHP_METHOD(RedisCluster, setrange) { } /* }}} */ +/* Generic implementation for both ZRANGE and ZREVRANGE */ +static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw) { + redisCluster *c = GET_CONTEXT(); + char *cmd; int cmd_len; short slot; + zend_bool withscores; + + if(redis_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, + &cmd_len, &withscores, &slot, NULL)==FAILURE) + { + efree(cmd); + RETURN_FALSE; + } + + if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { + efree(cmd); + RETURN_FALSE; + } + + efree(cmd); + + // Response type differs if we use WITHSCORES or not + if(!withscores) { + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } +} + +/* {{{ proto + * array RedisCluster::zrange(string k, long s, long e, bool score=0) */ +PHP_METHOD(RedisCluster, zrange) { + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE"); +} +/* }}} */ + +/* {{{ proto + * array RedisCluster::zrevrange(string k,long s,long e,bool scores=0) */ +PHP_METHOD(RedisCluster, zrevrange) { + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE"); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index c2016c2f1e..cecf8b0284 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -136,4 +136,6 @@ PHP_METHOD(RedisCluster, pfmerge); PHP_METHOD(RedisCluster, restore); PHP_METHOD(RedisCluster, setrange); PHP_METHOD(RedisCluster, smove); +PHP_METHOD(RedisCluster, zrange); +PHP_METHOD(RedisCluster, zrevrange); #endif diff --git a/redis_commands.c b/redis_commands.c index 156e0256b4..928c019a2a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -372,6 +372,40 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* ZRANGE/ZREVRANGE */ +int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, zend_bool *withscores, + short *slot, void **ctx) +{ + char *key; + int key_len, key_free; + long start, end; + zend_bool ws; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|b", &key, &key_len, + &start, &end, &ws)==FAILURE) + { + return FAILURE; + } + + key_free = redis_key_prefix(redis_sock, &key, &key_len); + if(ws) { + *cmd_len = redis_cmd_format_static(cmd, kw, "sdds", key, key_len, start, + end, "WITHSCORES", sizeof("WITHSCORES")-1); + } else { + *cmd_len = redis_cmd_format_static(cmd, kw, "sdd", key, key_len, start, + end); + } + + CMD_SET_SLOT(slot, key, key_len); + + // Free key, push out WITHSCORES option + if(key_free) efree(key); + *withscores = ws; + + return SUCCESS; +} + /* Commands with specific signatures or that need unique functions because they * have specific processing (argument validation, etc) that make them unique */ diff --git a/redis_commands.h b/redis_commands.h index 006d4a7913..03e245bb8d 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -49,8 +49,9 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_ss_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, zend_bool *withscores, short *slot, + void **ctx); /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is From 8591ead2e84c6c246e7efe12a2469c10a5ef067c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 18:12:28 -0700 Subject: [PATCH 0298/1986] Small formatting fix --- cluster_library.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 08e81f52ed..566ef3473e 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1127,7 +1127,8 @@ cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* Unserialize all the things */ PHPAPI void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) { + void *ctx) +{ cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop, NULL); } From 6534d933e1c12e4389de174dea09107838b768fd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 19:45:16 -0700 Subject: [PATCH 0299/1986] HSET/HSETNX Implemented HSET and HSETNX commands --- redis.c | 42 +++++----------------------------------- redis_cluster.c | 12 ++++++++++++ redis_cluster.h | 2 ++ redis_commands.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 7 +++++++ 5 files changed, 76 insertions(+), 37 deletions(-) diff --git a/redis.c b/redis.c index 48dc900f2c..76a3fefd93 100644 --- a/redis.c +++ b/redis.c @@ -3109,52 +3109,20 @@ PHP_METHOD(Redis, zUnion) { /* hashes */ -PHP_REDIS_API void -generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *, zval *, void *)) { - zval *object; - RedisSock *redis_sock; - char *key = NULL, *cmd, *member, *val; - int key_len, member_len, cmd_len, val_len; - int val_free, key_free = 0; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ossz", - &object, redis_ce, - &key, &key_len, &member, &member_len, &z_value) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, - member, member_len, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(fun); -} -/* hSet */ +/* proto long Redis::hset(string key, string mem, string val) */ PHP_METHOD(Redis, hSet) { - generic_hset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HSET", redis_long_response); + REDIS_PROCESS_CMD(hset, redis_long_response); } /* }}} */ -/* hSetNx */ + +/* proto bool Redis::hSetNx(string key, string mem, string val) */ PHP_METHOD(Redis, hSetNx) { - generic_hset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HSETNX", redis_1_response); + REDIS_PROCESS_CMD(hsetnx, redis_1_response); } /* }}} */ - /* proto string Redis::hget(string key, string mem) */ PHP_METHOD(Redis, hGet) { diff --git a/redis_cluster.c b/redis_cluster.c index 6640047d39..2cf77bcec0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -512,6 +512,18 @@ PHP_METHOD(RedisCluster, hget) { } /* }}} */ +/* {{{ proto bool RedisCluster::hset(string key, string mem, string val) */ +PHP_METHOD(RedisCluster, hset) { + CLUSTER_PROCESS_CMD(hset, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto bool RedisCluster::hsetnx(string key, string mem, string val) */ +PHP_METHOD(RedisCluster, hsetnx) { + CLUSTER_PROCESS_CMD(hsetnx, cluster_1_resp); +} +/* }}} */ + /* {{{ proto array RedisCluster::hgetall(string key) */ PHP_METHOD(RedisCluster, hgetall) { CLUSTER_PROCESS_KW_CMD("HGETALL", redis_key_cmd, diff --git a/redis_cluster.h b/redis_cluster.h index cecf8b0284..2add68d755 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -106,6 +106,8 @@ PHP_METHOD(RedisCluster, hgetall); PHP_METHOD(RedisCluster, hexists); PHP_METHOD(RedisCluster, hincrby); PHP_METHOD(RedisCluster, hincrbyfloat); +PHP_METHOD(RedisCluster, hset); +PHP_METHOD(RedisCluster, hsetnx); PHP_METHOD(RedisCluster, incr); PHP_METHOD(RedisCluster, decr); PHP_METHOD(RedisCluster, incrby); diff --git a/redis_commands.c b/redis_commands.c index 928c019a2a..da1b627e0f 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1209,4 +1209,54 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic command construction for HSET and HSETNX */ +static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot) +{ + char *key, *mem, *val; + int key_len, mem_len, val_len; + int val_free, key_free; + zval *z_val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &key, &key_len, + &mem, &mem_len, &z_val)==FAILURE) + { + return FAILURE; + } + + // Prefix/serialize + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Construct command + *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, mem, + mem_len, val, val_len); + + // Set slot + CMD_SET_SLOT(slot,key,key_len); + + // Cleanup + if(key_free) STR_FREE(val); + if(val_free) efree(key); + + // Success + return SUCCESS; +} + +/* HSET */ +int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return gen_hset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "HSET", + cmd, cmd_len, slot); +} + +/* HSETNX */ +int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return gen_hset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "HSETNX", + cmd, cmd_len, slot); +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 03e245bb8d..d9a166be96 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -106,6 +106,13 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 711950df9dc4a424bb0b9d2ce4dc09c8ea75573d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 20:37:23 -0700 Subject: [PATCH 0300/1986] SRANDMEMBER Implemented SRANDMEMBER command for Redis and RedisCluster --- redis.c | 47 +++++++++++------------------------------------ redis_cluster.c | 30 ++++++++++++++++++++++++++++++ redis_cluster.h | 1 + redis_commands.c | 39 +++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 5 files changed, 84 insertions(+), 36 deletions(-) diff --git a/redis.c b/redis.c index 76a3fefd93..f88934dfbb 100644 --- a/redis.c +++ b/redis.c @@ -1339,49 +1339,24 @@ PHP_METHOD(Redis, sPop) */ PHP_METHOD(Redis, sRandMember) { - zval *object; + char *cmd; + int cmd_len; + short have_count; RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free = 0; - long count; - /* Parse our params */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", - &object, redis_ce, &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - - /* Get our redis socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + // Grab our socket, validate call + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || + redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + &cmd, &cmd_len, NULL, NULL, &have_count)==FAILURE) + { RETURN_FALSE; } - /* Prefix our key if necissary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* If we have two arguments, we're running with an optional COUNT, which will return */ - /* a multibulk reply. Without the argument we'll return a string response */ - if(ZEND_NUM_ARGS() == 2) { - // Construct our command with count - cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "sl", - key, key_len, count); - } else { - // Construct our command - cmd_len = redis_cmd_format_static(&cmd, "SRANDMEMBER", "s", - key, key_len); - } - - /* Free our key if we prefixed it */ - if(key_free) efree(key); - - /* Process our command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - - /* Either bulk or multi-bulk depending on argument count */ - if(ZEND_NUM_ARGS() == 2) { + if(have_count) { IF_ATOMIC() { if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) + redis_sock, NULL, NULL)<0) { RETURN_FALSE; } @@ -1390,7 +1365,7 @@ PHP_METHOD(Redis, sRandMember) } else { IF_ATOMIC() { redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); + NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_string_response); } diff --git a/redis_cluster.c b/redis_cluster.c index 2cf77bcec0..e5675374bc 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -62,6 +62,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, scard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, smembers, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sismember, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, srandmember, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, persist, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ttl, NULL, ZEND_ACC_PUBLIC) @@ -361,6 +362,35 @@ PHP_METHOD(RedisCluster, spop) { } /* }}} */ +/* {{{ proto string|array RedisCluster::srandmember(string key, [long count]) */ +PHP_METHOD(RedisCluster, srandmember) { + redisCluster *c = GET_CONTEXT(); + char *cmd; int cmd_len; short slot; + short have_count; + + if(redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, + &cmd, &cmd_len, &slot, NULL, &have_count) + ==FAILURE) + { + RETURN_FALSE; + } + + if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { + efree(cmd); + RETURN_FALSE; + } + + // Clean up command + efree(cmd); + + // Response type differs if we use WITHSCORES or not + if(have_count) { + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } +} + /* {{{ proto string RedisCluster::strlen(string key) */ PHP_METHOD(RedisCluster, strlen) { CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_bulk_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 2add68d755..6f87735fec 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -138,6 +138,7 @@ PHP_METHOD(RedisCluster, pfmerge); PHP_METHOD(RedisCluster, restore); PHP_METHOD(RedisCluster, setrange); PHP_METHOD(RedisCluster, smove); +PHP_METHOD(RedisCluster, srandmember); PHP_METHOD(RedisCluster, zrange); PHP_METHOD(RedisCluster, zrevrange); #endif diff --git a/redis_commands.c b/redis_commands.c index da1b627e0f..e26680e43c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1259,4 +1259,43 @@ int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, cmd, cmd_len, slot); } +/* SRANDMEMBER */ +int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx, + short *have_count) +{ + char *key; + int key_len, key_free; + long count; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, + &count)==FAILURE) + { + return FAILURE; + } + + // Prefix key if requested + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Set our have count flag + *have_count = ZEND_NUM_ARGS() == 2; + + // Two args means we have the optional COUNT + if(*have_count) { + *cmd_len = redis_cmd_format_static(cmd, "SRANDMEMBER", "sl", key, + key_len, count); + } else { + *cmd_len = redis_cmd_format_static(cmd, "SRANDMEMBER", "s", key, + key_len); + } + + // Set slot + CMD_SET_SLOT(slot,key,key_len); + + // Cleanup + if(key_free) efree(key); + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index d9a166be96..67d3b63ac1 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -113,6 +113,9 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx, + short *have_count); #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From c89b3a651fa7674fc591342bc737e44d3745ba69 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Jun 2014 21:25:23 -0700 Subject: [PATCH 0301/1986] ZINCRBY Implemented ZINCRBY command --- redis.c | 37 +------------------------------------ redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + redis_commands.c | 32 ++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 5 files changed, 44 insertions(+), 36 deletions(-) diff --git a/redis.c b/redis.c index f88934dfbb..562306fc20 100644 --- a/redis.c +++ b/redis.c @@ -2866,46 +2866,11 @@ PHP_METHOD(Redis, zRevRank) { } /* }}} */ -PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len) { - zval *object; - RedisSock *redis_sock; - - char *key = NULL, *cmd, *val; - int key_len, val_len, cmd_len; - double add; - int val_free, key_free; - zval *z_value; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osdz", - &object, redis_ce, - &key, &key_len, &add, &z_value) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, - key_len, add, val, val_len); - if(val_free) STR_FREE(val); - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_bulk_double_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_bulk_double_response); - -} - /* {{{ proto double Redis::zIncrBy(string key, double value, mixed member) */ PHP_METHOD(Redis, zIncrBy) { - generic_incrby_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZINCRBY", sizeof("ZINCRBY")-1); + REDIS_PROCESS_CMD(zincrby, redis_bulk_double_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index e5675374bc..8bdfa27be3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -71,6 +71,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zcount, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zremrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zincrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hvals, NULL, ZEND_ACC_PUBLIC) @@ -492,6 +493,12 @@ PHP_METHOD(RedisCluster, zscore) { } /* }}} */ +/* {{{ proto double RedisCluster::zincrby(string key, double by, string mem) */ +PHP_METHOD(RedisCluster, zincrby) { + CLUSTER_PROCESS_CMD(zincrby, cluster_dbl_resp); +} +/* }}} */ + /* {{{ proto RedisCluster::zremrangebyscore(string k, string s, string e) */ PHP_METHOD(RedisCluster, zremrangebyscore) { CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd, diff --git a/redis_cluster.h b/redis_cluster.h index 6f87735fec..0019fdfa0b 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -96,6 +96,7 @@ PHP_METHOD(RedisCluster, zcount); PHP_METHOD(RedisCluster, zremrangebyscore); PHP_METHOD(RedisCluster, zrank); PHP_METHOD(RedisCluster, zrevrank); +PHP_METHOD(RedisCluster, zincrby); PHP_METHOD(RedisCluster, hlen); PHP_METHOD(RedisCluster, hget); PHP_METHOD(RedisCluster, hkeys); diff --git a/redis_commands.c b/redis_commands.c index e26680e43c..24146a98b9 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1298,4 +1298,36 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* ZINCRBY */ +int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *mem; + int key_len, mem_len; + int key_free, mem_free; + double incrby; + zval *z_val; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sdz", &key, &key_len, + &incrby, &z_val)==FAILURE) + { + return FAILURE; + } + + // Prefix key, serialize + key_free = redis_key_prefix(redis_sock, &key, &key_len); + mem_free = redis_serialize(redis_sock, z_val, &mem, &mem_len TSRMLS_CC); + + *cmd_len = redis_cmd_format_static(cmd, "ZINCRBY", "sfs", key, key_len, + incrby, mem, mem_len); + + CMD_SET_SLOT(slot,key,key_len); + + // Cleanup + if(key_free) efree(key); + if(mem_free) STR_FREE(mem); + + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 67d3b63ac1..ba58cdf54f 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -116,6 +116,9 @@ int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx, short *have_count); + +int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 05fad67e8888e800126dc94e69d4dda4fae1a78f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 9 Jun 2014 12:18:06 -0700 Subject: [PATCH 0302/1986] SORT command Implemented SORT command in a new way (using a zval array to construct each argument, then creating the command from that), as well as updated Redis proper and RedisCluster to use it. --- redis.c | 281 ++++++++++------------------------------------- redis_cluster.c | 29 +++++ redis_cluster.h | 1 + redis_commands.c | 244 ++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 13 ++- 5 files changed, 341 insertions(+), 227 deletions(-) diff --git a/redis.c b/redis.c index 562306fc20..08058bfd40 100644 --- a/redis.c +++ b/redis.c @@ -474,6 +474,44 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) return redis_sock; } +/* Redis and RedisCluster objects share serialization/prefixing settings so + * this is a generic function to add class constants to either */ +static void add_class_constants(zend_class_entry *ce, int only_atomic) { + add_constant_long(ce, "REDIS_NOT_FOUND", REDIS_NOT_FOUND); + add_constant_long(ce, "REDIS_STRING", REDIS_STRING); + add_constant_long(ce, "REDIS_SET", REDIS_SET); + add_constant_long(ce, "REDIS_LIST", REDIS_LIST); + add_constant_long(ce, "REDIS_ZSET", REDIS_ZSET); + add_constant_long(ce, "REDIS_HASH", REDIS_HASH); + + /* Cluster doesn't support pipelining at this time */ + if(!only_atomic) { + add_constant_long(ce, "ATOMIC", ATOMIC); + add_constant_long(ce, "MULTI", MULTI); + add_constant_long(ce, "PIPELINE", PIPELINE); + } + + /* options */ + add_constant_long(ce, "OPT_SERIALIZER", REDIS_OPT_SERIALIZER); + add_constant_long(ce, "OPT_PREFIX", REDIS_OPT_PREFIX); + add_constant_long(ce, "OPT_READ_TIMEOUT", REDIS_OPT_READ_TIMEOUT); + + /* serializer */ + add_constant_long(ce, "SERIALIZER_NONE", REDIS_SERIALIZER_NONE); + add_constant_long(ce, "SERIALIZER_PHP", REDIS_SERIALIZER_PHP); + + /* scan options*/ + add_constant_long(ce, "OPT_SCAN", REDIS_OPT_SCAN); + add_constant_long(ce, "SCAN_RETRY", REDIS_SCAN_RETRY); + add_constant_long(ce, "SCAN_NORETRY", REDIS_SCAN_NORETRY); +#ifdef HAVE_REDIS_IGBINARY + add_constant_long(ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY); +#endif + + zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5 TSRMLS_CC); + zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); +} + /** * PHP_MINIT_FUNCTION */ @@ -515,9 +553,11 @@ PHP_MINIT_FUNCTION(redis) ); /* RedisClusterException class */ - INIT_CLASS_ENTRY(redis_cluster_exception_class_entry, "RedisClusterException", NULL); + INIT_CLASS_ENTRY(redis_cluster_exception_class_entry, + "RedisClusterException", NULL); redis_cluster_exception_ce = zend_register_internal_class_ex( - &redis_cluster_exception_class_entry, rediscluster_get_exception_base(0 TSRMLS_CC), + &redis_cluster_exception_class_entry, + rediscluster_get_exception_base(0 TSRMLS_CC), NULL TSRMLS_CC ); @@ -527,42 +567,9 @@ PHP_MINIT_FUNCTION(redis) redis_sock_name, module_number ); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_STRING"), REDIS_STRING TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_SET"), REDIS_SET TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH TSRMLS_CC); - - zend_declare_class_constant_long(redis_ce, ZEND_STRL("ATOMIC"), ATOMIC TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("MULTI"), MULTI TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("PIPELINE"), PIPELINE TSRMLS_CC); - - /* options */ - zend_declare_class_constant_long(redis_ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT TSRMLS_CC); - - /* serializer */ - zend_declare_class_constant_long(redis_ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP TSRMLS_CC); - - /* scan options*/ - zend_declare_class_constant_long(redis_ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY TSRMLS_CC); - zend_declare_class_constant_long(redis_ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY TSRMLS_CC); - -#ifdef HAVE_REDIS_IGBINARY - zend_declare_class_constant_long(redis_ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); -#endif - - zend_declare_class_constant_stringl(redis_ce, "AFTER", 5, "after", 5 TSRMLS_CC); - zend_declare_class_constant_stringl(redis_ce, "BEFORE", 6, "before", 6 TSRMLS_CC); - -#ifdef PHP_SESSION - /* declare session handler */ - php_session_register_module(&ps_mod_redis); -#endif + /* Add class constants to Redis and RedisCluster objects */ + add_class_constants(redis_ce, 0); + add_class_constants(redis_cluster_ce, 1); return SUCCESS; } @@ -1743,204 +1750,34 @@ PHP_METHOD(Redis, sDiffStore) { /* }}} */ PHP_METHOD(Redis, sort) { - - zval *object = getThis(), *z_array = NULL, **z_cur; - char *cmd, *old_cmd = NULL, *key; - int cmd_len, elements = 2, key_len, key_free; - int using_store = 0; + char *cmd; + int cmd_len, have_store; RedisSock *redis_sock; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|a", - &object, redis_ce, - &key, &key_len, &z_array) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + // Grab socket, handle command construction + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || + redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &have_store, + &cmd, &cmd_len, NULL, NULL)==FAILURE) + { RETURN_FALSE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len); - cmd_len = redis_cmd_format(&cmd, "$4" _NL "SORT" _NL "$%d" _NL "%s" _NL, key_len, key, key_len); - if(key_free) efree(key); - - if(z_array) { - if ((zend_hash_find(Z_ARRVAL_P(z_array), "by", sizeof("by"), (void **) &z_cur) == SUCCESS - || zend_hash_find(Z_ARRVAL_P(z_array), "BY", sizeof("BY"), (void **) &z_cur) == SUCCESS) - && Z_TYPE_PP(z_cur) == IS_STRING) { - - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "%s" - "$2" _NL - "BY" _NL - "$%d" _NL - "%s" _NL - , cmd, cmd_len - , Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur)); - elements += 2; - efree(old_cmd); - - } - - if ((zend_hash_find(Z_ARRVAL_P(z_array), "sort", sizeof("sort"), (void **) &z_cur) == SUCCESS - || zend_hash_find(Z_ARRVAL_P(z_array), "SORT", sizeof("SORT"), (void **) &z_cur) == SUCCESS) - && Z_TYPE_PP(z_cur) == IS_STRING) { - - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "%s" - "$%d" _NL - "%s" _NL - , cmd, cmd_len - , Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur)); - elements += 1; - efree(old_cmd); - } - - if ((zend_hash_find(Z_ARRVAL_P(z_array), "store", sizeof("store"), (void **) &z_cur) == SUCCESS - || zend_hash_find(Z_ARRVAL_P(z_array), "STORE", sizeof("STORE"), (void **) &z_cur) == SUCCESS) - && Z_TYPE_PP(z_cur) == IS_STRING) { - - using_store = 1; - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "%s" - "$5" _NL - "STORE" _NL - "$%d" _NL - "%s" _NL - , cmd, cmd_len - , Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur)); - elements += 2; - efree(old_cmd); - } - - if ((zend_hash_find(Z_ARRVAL_P(z_array), "get", sizeof("get"), (void **) &z_cur) == SUCCESS - || zend_hash_find(Z_ARRVAL_P(z_array), "GET", sizeof("GET"), (void **) &z_cur) == SUCCESS) - && (Z_TYPE_PP(z_cur) == IS_STRING || Z_TYPE_PP(z_cur) == IS_ARRAY)) { - - if(Z_TYPE_PP(z_cur) == IS_STRING) { - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "%s" - "$3" _NL - "GET" _NL - "$%d" _NL - "%s" _NL - , cmd, cmd_len - , Z_STRLEN_PP(z_cur), Z_STRVAL_PP(z_cur), Z_STRLEN_PP(z_cur)); - elements += 2; - efree(old_cmd); - } else if(Z_TYPE_PP(z_cur) == IS_ARRAY) { /* loop over the strings in that array and add them as patterns */ - - HashTable *keytable = Z_ARRVAL_PP(z_cur); - for(zend_hash_internal_pointer_reset(keytable); - zend_hash_has_more_elements(keytable) == SUCCESS; - zend_hash_move_forward(keytable)) { - - char *key; - unsigned int key_len; - unsigned long idx; - zval **z_value_pp; - - zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ - } - - if(Z_TYPE_PP(z_value_pp) == IS_STRING) { - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "%s" - "$3" _NL - "GET" _NL - "$%d" _NL - "%s" _NL - , cmd, cmd_len - , Z_STRLEN_PP(z_value_pp), Z_STRVAL_PP(z_value_pp), Z_STRLEN_PP(z_value_pp)); - elements += 2; - efree(old_cmd); - } - } - } - } - - if ((zend_hash_find(Z_ARRVAL_P(z_array), "alpha", sizeof("alpha"), (void **) &z_cur) == SUCCESS - || zend_hash_find(Z_ARRVAL_P(z_array), "ALPHA", sizeof("ALPHA"), (void **) &z_cur) == SUCCESS) - && Z_TYPE_PP(z_cur) == IS_BOOL && Z_BVAL_PP(z_cur) == 1) { - - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "%s" - "$5" _NL - "ALPHA" _NL - , cmd, cmd_len); - elements += 1; - efree(old_cmd); - } - - if ((zend_hash_find(Z_ARRVAL_P(z_array), "limit", sizeof("limit"), (void **) &z_cur) == SUCCESS - || zend_hash_find(Z_ARRVAL_P(z_array), "LIMIT", sizeof("LIMIT"), (void **) &z_cur) == SUCCESS) - && Z_TYPE_PP(z_cur) == IS_ARRAY) { - - if(zend_hash_num_elements(Z_ARRVAL_PP(z_cur)) == 2) { - zval **z_offset_pp, **z_count_pp; - /* get the two values from the table, check that they are indeed of LONG type */ - if(SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_cur), 0, (void**)&z_offset_pp) && - SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_cur), 1, (void**)&z_count_pp)) { - - long limit_low, limit_high; - if((Z_TYPE_PP(z_offset_pp) == IS_LONG || Z_TYPE_PP(z_offset_pp) == IS_STRING) && - (Z_TYPE_PP(z_count_pp) == IS_LONG || Z_TYPE_PP(z_count_pp) == IS_STRING)) { - - - if(Z_TYPE_PP(z_offset_pp) == IS_LONG) { - limit_low = Z_LVAL_PP(z_offset_pp); - } else { - limit_low = atol(Z_STRVAL_PP(z_offset_pp)); - } - if(Z_TYPE_PP(z_count_pp) == IS_LONG) { - limit_high = Z_LVAL_PP(z_count_pp); - } else { - limit_high = atol(Z_STRVAL_PP(z_count_pp)); - } - - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "%s" - "$5" _NL - "LIMIT" _NL - "$%d" _NL - "%d" _NL - "$%d" _NL - "%d" _NL - , cmd, cmd_len - , integer_length(limit_low), limit_low - , integer_length(limit_high), limit_high); - elements += 3; - efree(old_cmd); - } - } - } - } - - } - - /* complete with prefix */ - - old_cmd = cmd; - cmd_len = redis_cmd_format(&cmd, "*%d" _NL "%s", elements, cmd, cmd_len); - efree(old_cmd); - - /* run command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - if(using_store) { + if(have_store) { IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_long_response); } else { IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { + if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL)<0) + { RETURN_FALSE; } + REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); } } diff --git a/redis_cluster.c b/redis_cluster.c index 8bdfa27be3..740371dec7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -116,6 +116,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, smove, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrange, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -825,4 +826,32 @@ PHP_METHOD(RedisCluster, zrevrange) { } /* }}} */ +/* {{{ proto RedisCluster::sort(string key, array options) */ +PHP_METHOD(RedisCluster, sort) { + redisCluster *c = GET_CONTEXT(); + char *cmd; int cmd_len, have_store; short slot; + + if(redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &have_store, + &cmd, &cmd_len, &slot, NULL)==FAILURE) + { + efree(cmd); + RETURN_FALSE; + } + + if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { + efree(cmd); + RETURN_FALSE; + } + + efree(cmd); + + // Response type differs based on presence of STORE argument + if(!have_store) { + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } + +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 0019fdfa0b..29e4f682ee 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -142,4 +142,5 @@ PHP_METHOD(RedisCluster, smove); PHP_METHOD(RedisCluster, srandmember); PHP_METHOD(RedisCluster, zrange); PHP_METHOD(RedisCluster, zrevrange); +PHP_METHOD(RedisCluster, sort); #endif diff --git a/redis_commands.c b/redis_commands.c index 24146a98b9..09edb5134e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1330,4 +1330,248 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* SORT */ +int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + int *using_store, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + zval *z_opts=NULL, **z_ele, *z_argv; + char *key; + HashTable *ht_opts; + smart_str cmdstr = {0}; + int key_len, key_free; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &key, &key_len, + &z_opts)==FAILURE) + { + return FAILURE; + } + + // Default that we're not using store + *using_store = 0; + + // Handle key prefixing + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // If we don't have an options array, the command is quite simple + if(!z_opts) { + // Construct command + *cmd_len = redis_cmd_format_static(cmd, "SORT", "s", key, key_len); + + // Push out slot, store flag, and clean up + *using_store = 0; + CMD_SET_SLOT(slot,key,key_len); + if(key_free) efree(key); + + return SUCCESS; + } + + // Create our hash table to hold our sort arguments + ALLOC_INIT_ZVAL(z_argv); + array_init(z_argv); + + // SORT + add_next_index_stringl(z_argv, key, key_len, 0); + + // Set slot + CMD_SET_SLOT(slot,key,key_len); + + // Grab the hash table + ht_opts = Z_ARRVAL_P(z_opts); + + // Handle BY pattern + if((zend_hash_find(ht_opts, "by", sizeof("by"), (void**)&z_ele)==SUCCESS || + zend_hash_find(ht_opts, "BY", sizeof("BY"), (void**)&z_ele)==SUCCESS) && + Z_TYPE_PP(z_ele)==IS_STRING) + { + // "BY" option is disabled in cluster + if(slot) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "SORT BY option is not allowed in Redis Cluster"); + if(key_free) efree(key); + zval_dtor(z_argv); + efree(z_argv); + return FAILURE; + } + + // ... BY + add_next_index_stringl(z_argv, "BY", sizeof("BY")-1, 1); + add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),0); + } + + // Handle ASC/DESC option + if((zend_hash_find(ht_opts,"sort",sizeof("sort"),(void**)&z_ele)==SUCCESS || + zend_hash_find(ht_opts,"SORT",sizeof("SORT"),(void**)&z_ele)==SUCCESS) && + Z_TYPE_PP(z_ele)==IS_STRING) + { + // 'asc'|'desc' + add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),0); + } + + // STORE option + if((zend_hash_find(ht_opts,"store",6,(void**)&z_ele)==SUCCESS || + zend_hash_find(ht_opts,"STORE",6,(void**)&z_ele)==SUCCESS) && + Z_TYPE_PP(z_ele)==IS_STRING) + { + // Slot verification + int cross_slot = slot && *slot != cluster_hash_key( + Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele)); + + if(cross_slot) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Error, SORT key and STORE key have different slots!"); + if(key_free) efree(key); + zval_dtor(z_argv); + efree(z_argv); + return FAILURE; + } + + // STORE + add_next_index_stringl(z_argv,"STORE",sizeof("STORE")-1, 1); + add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),0); + + // We are using STORE + *using_store = 1; + } + + // GET option + if((zend_hash_find(ht_opts,"get",4,(void**)&z_ele)==SUCCESS || + zend_hash_find(ht_opts,"GET",4,(void**)&z_ele)==SUCCESS) && + (Z_TYPE_PP(z_ele)==IS_STRING || Z_TYPE_PP(z_ele)==IS_ARRAY)) + { + // Disabled in cluster + if(slot) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "GET option for SORT disabled in Redis Cluster"); + if(key_free) efree(key); + zval_dtor(z_argv); + efree(z_argv); + return FAILURE; + } + + // If it's a string just add it + if(Z_TYPE_PP(z_ele)==IS_STRING) { + add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,0); + add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele), + Z_STRLEN_PP(z_ele), 0); + } else { + HashTable *ht_keys = Z_ARRVAL_PP(z_ele); + int added=0; + + // Add our "GET" option + add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,0); + + for(zend_hash_internal_pointer_reset(ht_keys); + zend_hash_has_more_elements(ht_keys)==SUCCESS; + zend_hash_move_forward(ht_keys)) + { + zval **z_key; + + // If we can't get the data, or it's not a string, skip + if(zend_hash_get_current_data(ht_keys,(void**)&z_key)==FAILURE) + continue; + if(Z_TYPE_PP(z_key)!=IS_STRING) + continue; + + // Add this key to our argv array + add_next_index_stringl(z_argv, Z_STRVAL_PP(z_key), + Z_STRLEN_PP(z_key), 0); + added++; + } + + // Make sure we were able to add at least one + if(added==0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Array of GET values requested, but none are valid"); + if(key_free) efree(key); + zval_dtor(z_argv); + efree(z_argv); + return FAILURE; + } + } + } + + // ALPHA + if((zend_hash_find(ht_opts,"alpha",6,(void**)&z_ele)==SUCCESS || + zend_hash_find(ht_opts,"ALPHA",6,(void**)&z_ele)==SUCCESS) && + Z_TYPE_PP(z_ele)==IS_BOOL && Z_BVAL_PP(z_ele)==1) + { + add_next_index_stringl(z_argv, "ALPHA", sizeof("ALPHA")-1,1); + } + + // LIMIT + if((zend_hash_find(ht_opts,"limit",6,(void**)&z_ele)==SUCCESS || + zend_hash_find(ht_opts,"LIMIT",6,(void**)&z_ele)==SUCCESS) && + Z_TYPE_PP(z_ele)==IS_ARRAY) + { + HashTable *ht_off = Z_ARRVAL_PP(z_ele); + zval **z_off, **z_cnt; + + if(zend_hash_index_find(ht_off, 0, (void**)&z_off)==SUCCESS && + zend_hash_index_find(ht_off, 1, (void**)&z_cnt)==SUCCESS) + { + if((Z_TYPE_PP(z_off)!=IS_STRING && Z_TYPE_PP(z_off)!=IS_LONG) || + (Z_TYPE_PP(z_cnt)!=IS_STRING && Z_TYPE_PP(z_cnt)!=IS_LONG)) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "LIMIT options on SORT command must be longs or strings"); + if(key_free) efree(key); + zval_dtor(z_argv); + efree(z_argv); + return FAILURE; + } + + // Add LIMIT argument + add_next_index_stringl(z_argv,"LIMIT",sizeof("LIMIT")-1,1); + + long low, high; + if(Z_TYPE_PP(z_off)==IS_STRING) { + low = atol(Z_STRVAL_PP(z_off)); + } else { + low = Z_LVAL_PP(z_off); + } + if(Z_TYPE_PP(z_cnt)==IS_STRING) { + high = atol(Z_STRVAL_PP(z_cnt)); + } else { + high = Z_LVAL_PP(z_cnt); + } + + // Add our two LIMIT arguments + add_next_index_long(z_argv, low); + add_next_index_long(z_argv, high); + } + } + + // Start constructing our command + HashTable *ht_argv = Z_ARRVAL_P(z_argv); + redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(ht_argv), "SORT", + sizeof("SORT")-1); + + // Iterate through our arguments + for(zend_hash_internal_pointer_reset(ht_argv); + zend_hash_get_current_data(ht_argv, (void**)&z_ele)==SUCCESS; + zend_hash_move_forward(ht_argv)) + { + // Args are strings or longs + if(Z_TYPE_PP(z_ele)==IS_STRING) { + redis_cmd_append_sstr(&cmdstr,Z_STRVAL_PP(z_ele), + Z_STRLEN_PP(z_ele)); + } else { + redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_PP(z_ele)); + } + } + + // Free key if we prefixed, destroy argv array + if(key_free) efree(key); + zval_dtor(z_argv); + efree(z_argv); + + // Push our length and command + *cmd_len = cmdstr.len; + *cmd = cmdstr.c; + + // Success! + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index ba58cdf54f..0feab75cc8 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -108,17 +108,20 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); + char **cmd, int *cmd_len, short *slot, void **ctx); int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); + char **cmd, int *cmd_len, short *slot, void **ctx); int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx, - short *have_count); + char **cmd, int *cmd_len, short *slot, void **ctx, short *have_count); int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + int *using_store, char **cmd, int *cmd_len, short *slot, void **ctx); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From b74a27e10158c5a1ae3f20fc9a83b522c74820cc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 9 Jun 2014 13:12:15 -0700 Subject: [PATCH 0303/1986] ZRANGEBYSCORE/ZREVRANGEBYSCORE Implemented ZRANGEBYSCORE and ZREVRANGEBYSCORE in the new way for both Redis and RedisCluster objects. In addition, ZRANGE and ZREVRANGE handling is nearly identical to "BYSCORE" variants, so made this generic. --- redis.c | 144 ++++++++--------------------------------------- redis_cluster.c | 37 +++++++++--- redis_cluster.h | 1 + redis_commands.c | 80 +++++++++++++++++++++++++- redis_commands.h | 11 +++- 5 files changed, 143 insertions(+), 130 deletions(-) diff --git a/redis.c b/redis.c index 08058bfd40..63e2b04443 100644 --- a/redis.c +++ b/redis.c @@ -2410,18 +2410,20 @@ PHP_METHOD(Redis, zAdd) { /* }}} */ /* Handle ZRANGE and ZREVRANGE as they're the same except for keyword */ -static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw) { +static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, + zrange_cb fun) +{ char *cmd; int cmd_len; RedisSock *redis_sock; - zend_bool withscores=0; + int withscores=0; if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { RETURN_FALSE; } - if(redis_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, - &cmd_len, &withscores, NULL, NULL)==FAILURE) + if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, + &cmd_len, &withscores, NULL, NULL)==FAILURE) { RETURN_FALSE; } @@ -2448,16 +2450,32 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw) { /* {{{ proto array Redis::zRange(string key,int start,int end,bool scores=0) */ PHP_METHOD(Redis, zRange) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE"); + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE", + redis_zrange_cmd); } /* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores=0) */ PHP_METHOD(Redis, zRevRange) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE"); + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE", + redis_zrange_cmd); } /* }}} */ +/* {{{ proto array Redis::zRangeByScore(string k,string s,string e,array opt) */ +PHP_METHOD(Redis, zRangeByScore) { + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE", + redis_zrangebyscore_cmd); +} /* }}} */ + +/* {{{ proto array Redis::zRevRangeByScore(string key, string start, string end, + * array options) */ +PHP_METHOD(Redis, zRevRangeByScore) { + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE", + redis_zrangebyscore_cmd); +} +/* }}} */ + /* {{{ proto long Redis::zDelete(string key, string member) */ PHP_METHOD(Redis, zDelete) @@ -2493,120 +2511,6 @@ PHP_METHOD(Redis, zDeleteRangeByRank) } /* }}} */ -PHPAPI void -redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAMETERS, char *keyword) { - - zval *object, *z_options = NULL, **z_limit_val_pp = NULL, **z_withscores_val_pp = NULL; - - RedisSock *redis_sock; - char *key = NULL, *cmd; - int key_len, cmd_len, key_free; - zend_bool withscores = 0; - char *start, *end; - int start_len, end_len; - int has_limit = 0; - long limit_low, limit_high; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss|a", - &object, redis_ce, - &key, &key_len, - &start, &start_len, - &end, &end_len, - &z_options) == FAILURE) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* options */ - if (z_options && Z_TYPE_P(z_options) == IS_ARRAY) { - /* add scores */ - zend_hash_find(Z_ARRVAL_P(z_options), "withscores", sizeof("withscores"), (void**)&z_withscores_val_pp); - withscores = (z_withscores_val_pp ? Z_BVAL_PP(z_withscores_val_pp) : 0); - - /* limit offset, count: - z_limit_val_pp points to an array($longFrom, $longCount) - */ - if(zend_hash_find(Z_ARRVAL_P(z_options), "limit", sizeof("limit"), (void**)&z_limit_val_pp)== SUCCESS) {; - if(zend_hash_num_elements(Z_ARRVAL_PP(z_limit_val_pp)) == 2) { - zval **z_offset_pp, **z_count_pp; - /* get the two values from the table, check that they are indeed of LONG type */ - if(SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_limit_val_pp), 0, (void**)&z_offset_pp) && - SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(z_limit_val_pp), 1, (void**)&z_count_pp) && - Z_TYPE_PP(z_offset_pp) == IS_LONG && - Z_TYPE_PP(z_count_pp) == IS_LONG) { - - has_limit = 1; - limit_low = Z_LVAL_PP(z_offset_pp); - limit_high = Z_LVAL_PP(z_count_pp); - } - } - } - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - if(withscores) { - if(has_limit) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdds", - key, key_len, start, start_len, end, end_len, "LIMIT", - 5, limit_low, limit_high, "WITHSCORES", 10); - } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssss", - key, key_len, start, start_len, end, end_len, - "WITHSCORES", 10); - } - } else { - if(has_limit) { - cmd_len = redis_cmd_format_static(&cmd, keyword, "ssssdd", - key, key_len, start, start_len, end, end_len, - "LIMIT", 5, limit_low, limit_high); - } else { - cmd_len = redis_cmd_format_static(&cmd, keyword, "sss", - key, key_len, start, start_len, - end, end_len); - } - } - if(key_free) efree(key); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - if(withscores) { - /* with scores! we have to transform the return array. - * return_value currently holds this: [elt0, val0, elt1, val1 ... ] - * we want [elt0 => val0, elt1 => val1], etc. - */ - IF_ATOMIC() { - if(redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); - } else { - IF_ATOMIC() { - if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); - } -} - -/* {{{ proto array Redis::zRangeByScore(string key, string start , string end [,array options = NULL]) - */ -PHP_METHOD(Redis, zRangeByScore) -{ - redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE"); -} -/* }}} */ -/* {{{ proto array Redis::zRevRangeByScore(string key, string start , string end [,array options = NULL]) - */ -PHP_METHOD(Redis, zRevRangeByScore) -{ - redis_generic_zrange_by_score(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE"); -} - /* {{{ proto array Redis::zCount(string key, string start , string end) */ PHP_METHOD(Redis, zCount) { diff --git a/redis_cluster.c b/redis_cluster.c index 740371dec7..f890183afc 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -116,6 +116,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, smove, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrange, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -784,14 +785,17 @@ PHP_METHOD(RedisCluster, setrange) { } /* }}} */ -/* Generic implementation for both ZRANGE and ZREVRANGE */ -static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw) { +/* Generic implementation for ZRANGE, ZREVRANGE, ZRANGEBYSCORE, + * ZREVRANGEBYSCORE */ +static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, + zrange_cb fun) +{ redisCluster *c = GET_CONTEXT(); char *cmd; int cmd_len; short slot; - zend_bool withscores; + int withscores; - if(redis_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, - &cmd_len, &withscores, &slot, NULL)==FAILURE) + if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, + &withscores, &slot, NULL)==FAILURE) { efree(cmd); RETURN_FALSE; @@ -815,17 +819,36 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw) { /* {{{ proto * array RedisCluster::zrange(string k, long s, long e, bool score=0) */ PHP_METHOD(RedisCluster, zrange) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE"); + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE", + redis_zrange_cmd); } /* }}} */ /* {{{ proto * array RedisCluster::zrevrange(string k,long s,long e,bool scores=0) */ PHP_METHOD(RedisCluster, zrevrange) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE"); + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE", + redis_zrange_cmd); +} +/* }}} */ + +/* {{{ proto array + * RedisCluster::zrangebyscore(string k, long s, long e, array opts) */ +PHP_METHOD(RedisCluster, zrangebyscore) { + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE", + redis_zrangebyscore_cmd); } /* }}} */ +/* {{{ proto array + * RedisCluster::zrevrangebyscore(string k, long s, long e, array opts) */ +PHP_METHOD(RedisCluster, zrevrangebyscore) { + generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE", + redis_zrangebyscore_cmd); +} +/* }}} */ + + /* {{{ proto RedisCluster::sort(string key, array options) */ PHP_METHOD(RedisCluster, sort) { redisCluster *c = GET_CONTEXT(); diff --git a/redis_cluster.h b/redis_cluster.h index 29e4f682ee..aa2f9a6acf 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -142,5 +142,6 @@ PHP_METHOD(RedisCluster, smove); PHP_METHOD(RedisCluster, srandmember); PHP_METHOD(RedisCluster, zrange); PHP_METHOD(RedisCluster, zrevrange); +PHP_METHOD(RedisCluster, zrangebyscore); PHP_METHOD(RedisCluster, sort); #endif diff --git a/redis_commands.c b/redis_commands.c index 09edb5134e..58458318ae 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -374,7 +374,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* ZRANGE/ZREVRANGE */ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, zend_bool *withscores, + char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, void **ctx) { char *key; @@ -406,6 +406,84 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* ZRANGEBYSCORE/ZREVRANGEBYSCORE */ +int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, int *withscores, + short *slot, void **ctx) +{ + char *key; + int key_len, key_free; + char *start, *end; + int start_len, end_len; + int has_limit=0; + long limit_low, limit_high; + zval *z_opt=NULL, **z_ele; + HashTable *ht_opt; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a", &key, &key_len, + &start, &start_len, &end, &end_len, &z_opt) + ==FAILURE) + { + return FAILURE; + } + + // Check for an options array + if(z_opt && Z_TYPE_P(z_opt)==IS_ARRAY) { + ht_opt = Z_ARRVAL_P(z_opt); + + // Check for WITHSCORES + *withscores = (zend_hash_find(ht_opt,"withscores",sizeof("withscores"), + (void**)&z_ele)==SUCCESS && Z_TYPE_PP(z_ele)==IS_BOOL && + Z_BVAL_PP(z_ele)==1); + + // LIMIT + if(zend_hash_find(ht_opt,"limit",sizeof("limit"),(void**)&z_ele) + ==SUCCESS) + { + HashTable *ht_limit = Z_ARRVAL_PP(z_ele); + zval **z_off, **z_cnt; + if(zend_hash_index_find(ht_limit,0,(void**)&z_off)==SUCCESS && + zend_hash_index_find(ht_limit,1,(void**)&z_cnt)==SUCCESS && + Z_TYPE_PP(z_off)==IS_LONG && Z_TYPE_PP(z_cnt)==IS_LONG) + { + has_limit = 1; + limit_low = Z_LVAL_PP(z_off); + limit_high = Z_LVAL_PP(z_cnt); + } + } + } + + // Prefix our key, set slot + key_free = redis_key_prefix(redis_sock, &key, &key_len); + CMD_SET_SLOT(slot,key,key_len); + + // Construct our command + if(*withscores) { + if(has_limit) { + *cmd_len = redis_cmd_format_static(cmd, kw, "ssssdds", key, key_len, + start, start_len, end, end_len, "LIMIT", 5, limit_low, + limit_high, "WITHSCORES", 10); + } else { + *cmd_len = redis_cmd_format_static(cmd, kw, "ssss", key, key_len, + start, start_len, end, end_len, "WITHSCORES", 10); + } + } else { + if(has_limit) { + *cmd_len = redis_cmd_format_static(cmd, kw, "ssssdd", key, key_len, + start, start_len, end, end_len, "LIMIT", 5, limit_low, + limit_high); + } else { + *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, + start, start_len, end, end_len); + } + } + + // Free our key if we prefixed + if(key_free) efree(key); + + return SUCCESS; +} + /* Commands with specific signatures or that need unique functions because they * have specific processing (argument validation, etc) that make them unique */ diff --git a/redis_commands.h b/redis_commands.h index 0feab75cc8..3205a7324e 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -49,8 +49,16 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +// ZRANGE, ZREVRANGE, ZRANGEBYSCORE, and ZREVRANGEBYSCORE callback type +typedef int (*zrange_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *,char**,int*,int*,short*,void**); + int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, zend_bool *withscores, short *slot, + char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, + void **ctx); + +int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, void **ctx); /* Commands which need a unique construction mechanism. This is either because @@ -106,7 +114,6 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From 98a16f681b1e87615089ec4645cb3780a3e78d98 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 9 Jun 2014 14:15:12 -0700 Subject: [PATCH 0304/1986] ZUNIONSTORE/ZINTERSTORE Implemented ZUNIONSTORE and ZINTERSTORE in a generic way that works for both cluster and Redis proper. --- redis.c | 167 +--------------------------------------------- redis_cluster.c | 16 +++++ redis_cluster.h | 2 + redis_commands.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 + 5 files changed, 193 insertions(+), 165 deletions(-) diff --git a/redis.c b/redis.c index 63e2b04443..7b94cc051c 100644 --- a/redis.c +++ b/redis.c @@ -2615,177 +2615,14 @@ PHP_METHOD(Redis, zIncrBy) } /* }}} */ -PHP_REDIS_API void generic_z_command(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) { - zval *object, *z_keys, *z_weights = NULL, **z_data; - HashTable *ht_keys, *ht_weights = NULL; - RedisSock *redis_sock; - smart_str cmd = {0}; - HashPosition ptr; - char *store_key, *agg_op = NULL; - int cmd_arg_count = 2, store_key_len, agg_op_len = 0, keys_count; - int key_free; - - /* Grab our parameters */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa|a!s", - &object, redis_ce, &store_key, &store_key_len, - &z_keys, &z_weights, &agg_op, &agg_op_len) == FAILURE) - { - RETURN_FALSE; - } - - /* We'll need our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* Grab our keys argument as an array */ - ht_keys = Z_ARRVAL_P(z_keys); - - /* Nothing to do if there aren't any keys */ - if((keys_count = zend_hash_num_elements(ht_keys)) == 0) { - RETURN_FALSE; - } else { - /* Increment our overall argument count */ - cmd_arg_count += keys_count; - } - - /* Grab and validate our weights array */ - if(z_weights != NULL) { - ht_weights = Z_ARRVAL_P(z_weights); - - /* This command is invalid if the weights array isn't the same size */ - /* as our keys array. */ - if(zend_hash_num_elements(ht_weights) != keys_count) { - RETURN_FALSE; - } - - /* Increment our overall argument count by the number of keys */ - /* plus one, for the "WEIGHTS" argument itself */ - cmd_arg_count += keys_count + 1; - } - - /* AGGREGATE option */ - if(agg_op_len != 0) { - /* Verify our aggregation option */ - if(strncasecmp(agg_op, "SUM", sizeof("SUM")) && - strncasecmp(agg_op, "MIN", sizeof("MIN")) && - strncasecmp(agg_op, "MAX", sizeof("MAX"))) - { - RETURN_FALSE; - } - - /* Two more arguments: "AGGREGATE" and agg_op */ - cmd_arg_count += 2; - } - - /* Command header */ - redis_cmd_init_sstr(&cmd, cmd_arg_count, command, command_len); - - /* Prefix our key if necessary and add the output key */ - int key_free = redis_key_prefix(redis_sock, &store_key, &store_key_len); - redis_cmd_append_sstr(&cmd, store_key, store_key_len); - if(key_free) efree(store_key); - - /* Number of input keys argument */ - redis_cmd_append_sstr_int(&cmd, keys_count); - - /* Process input keys */ - for(zend_hash_internal_pointer_reset_ex(ht_keys, &ptr); - zend_hash_get_current_data_ex(ht_keys, (void**)&z_data, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_keys, &ptr)) - { - char *key; - int key_free, key_len; - zval *z_tmp = NULL; - - if(Z_TYPE_PP(z_data) == IS_STRING) { - key = Z_STRVAL_PP(z_data); - key_len = Z_STRLEN_PP(z_data); - } else { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_data; - convert_to_string(z_tmp); - - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); - } - - /* Apply key prefix if necessary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* Append this input set */ - redis_cmd_append_sstr(&cmd, key, key_len); - - /* Free our key if it was prefixed */ - if(key_free) efree(key); - - /* Free our temporary z_val if it was converted */ - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } - } - - /* Weights */ - if(ht_weights != NULL) { - /* Append "WEIGHTS" argument */ - redis_cmd_append_sstr(&cmd, "WEIGHTS", sizeof("WEIGHTS") - 1); - - /* Process weights */ - for(zend_hash_internal_pointer_reset_ex(ht_weights, &ptr); - zend_hash_get_current_data_ex(ht_weights, (void**)&z_data, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_weights, &ptr)) - { - /* Ignore non numeric arguments, unless they're special Redis numbers */ - if (Z_TYPE_PP(z_data) != IS_LONG && Z_TYPE_PP(z_data) != IS_DOUBLE && - strncasecmp(Z_STRVAL_PP(z_data), "inf", sizeof("inf")) != 0 && - strncasecmp(Z_STRVAL_PP(z_data), "-inf", sizeof("-inf")) != 0 && - strncasecmp(Z_STRVAL_PP(z_data), "+inf", sizeof("+inf")) != 0) - { - /* We should abort if we have an invalid weight, rather than pass */ - /* a different number of weights than the user is expecting */ - efree(cmd.c); - RETURN_FALSE; - } - - /* Append the weight based on the input type */ - switch(Z_TYPE_PP(z_data)) { - case IS_LONG: - redis_cmd_append_sstr_long(&cmd, Z_LVAL_PP(z_data)); - break; - case IS_DOUBLE: - redis_cmd_append_sstr_dbl(&cmd, Z_DVAL_PP(z_data)); - break; - case IS_STRING: - redis_cmd_append_sstr(&cmd, Z_STRVAL_PP(z_data), Z_STRLEN_PP(z_data)); - break; - } - } - } - - /* Aggregation options, if we have them */ - if(agg_op_len != 0) { - redis_cmd_append_sstr(&cmd, "AGGREGATE", sizeof("AGGREGATE") - 1); - redis_cmd_append_sstr(&cmd, agg_op, agg_op_len); - } - - /* Kick off our request */ - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); -} - /* zInter */ PHP_METHOD(Redis, zInter) { - generic_z_command(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZINTERSTORE", 11); + REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response); } /* zUnion */ PHP_METHOD(Redis, zUnion) { - generic_z_command(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZUNIONSTORE", 11); + REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, redis_long_response); } /* hashes */ diff --git a/redis_cluster.c b/redis_cluster.c index f890183afc..ace70117ed 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -117,6 +117,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrangebyscore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zunionstore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zinterstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -840,6 +842,20 @@ PHP_METHOD(RedisCluster, zrangebyscore) { } /* }}} */ +/* {{{ proto RedisCluster::zunionstore(string dst, array keys, [array weights, + * string agg]) */ +PHP_METHOD(RedisCluster, zunionstore) { + CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto RedisCluster::zinterstore(string dst, array keys, [array weights, + * string agg]) */ +PHP_METHOD(RedisCluster, zinterstore) { + CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, cluster_long_resp); +} +/* }}} */ + /* {{{ proto array * RedisCluster::zrevrangebyscore(string k, long s, long e, array opts) */ PHP_METHOD(RedisCluster, zrevrangebyscore) { diff --git a/redis_cluster.h b/redis_cluster.h index aa2f9a6acf..4c84451fd9 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -143,5 +143,7 @@ PHP_METHOD(RedisCluster, srandmember); PHP_METHOD(RedisCluster, zrange); PHP_METHOD(RedisCluster, zrevrange); PHP_METHOD(RedisCluster, zrangebyscore); +PHP_METHOD(RedisCluster, zunionstore); +PHP_METHOD(RedisCluster, zinterstore); PHP_METHOD(RedisCluster, sort); #endif diff --git a/redis_commands.c b/redis_commands.c index 58458318ae..092849061b 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -484,6 +484,176 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* ZUNIONSTORE, ZINTERSTORE */ +int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + char *key, *agg_op=NULL; + int key_free, key_len; + zval *z_keys, *z_weights=NULL, **z_ele; + HashTable *ht_keys, *ht_weights=NULL; + HashPosition ptr; + smart_str cmdstr = {0}; + int argc = 2, agg_op_len=0, keys_count; + + // Parse args + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a!s", &key, + &key_len, &z_keys, &z_weights, &agg_op, + &agg_op_len)==FAILURE) + { + return FAILURE; + } + + // Grab our keys + ht_keys = Z_ARRVAL_P(z_keys); + + // Nothing to do if there aren't any + if((keys_count = zend_hash_num_elements(ht_keys))==0) { + return FAILURE; + } else { + argc += keys_count; + } + + // Handle WEIGHTS + if(z_weights != NULL) { + ht_weights = Z_ARRVAL_P(z_weights); + if(zend_hash_num_elements(ht_weights) != keys_count) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "WEIGHTS and keys array should be the same size!"); + return FAILURE; + } + + // "WEIGHTS" + key count + argc += keys_count + 1; + } + + // AGGREGATE option + if(agg_op_len != 0) { + if(strncasecmp(agg_op, "SUM", sizeof("SUM")) && + strncasecmp(agg_op, "MIN", sizeof("MIN")) && + strncasecmp(agg_op, "MAX", sizeof("MAX"))) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Invalid AGGREGATE option provided!"); + return FAILURE; + } + + // "AGGREGATE" + type + argc += 2; + } + + // Prefix key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Start building our command + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + redis_cmd_append_sstr(&cmdstr, key, key_len); + redis_cmd_append_sstr_int(&cmdstr, keys_count); + + // Set our slot, free the key if we prefixed it + CMD_SET_SLOT(slot,key,key_len); + if(key_free) efree(key); + + // Process input keys + for(zend_hash_internal_pointer_reset_ex(ht_keys, &ptr); + zend_hash_get_current_data_ex(ht_keys,(void**)&z_ele,&ptr)==SUCCESS; + zend_hash_move_forward_ex(ht_keys, &ptr)) + { + char *key; + int key_free, key_len; + zval *z_tmp = NULL; + + if(Z_TYPE_PP(z_ele) == IS_STRING) { + key = Z_STRVAL_PP(z_ele); + key_len = Z_STRLEN_PP(z_ele); + } else { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = **z_ele; + convert_to_string(z_tmp); + + key = Z_STRVAL_P(z_tmp); + key_len = Z_STRLEN_P(z_tmp); + } + + // Prefix key if necissary + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // If we're in Cluster mode, verify the slot is the same + if(slot && *slot != cluster_hash_key(key,key_len)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "All keys don't hash to the same slot!"); + efree(cmdstr.c); + if(key_free) efree(key); + if(z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + } + return FAILURE; + } + + // Append this input set + redis_cmd_append_sstr(&cmdstr, key, key_len); + + // Cleanup + if(key_free) efree(key); + if(z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + z_tmp = NULL; + } + } + + // Weights + if(ht_weights != NULL) { + redis_cmd_append_sstr(&cmdstr, "WEIGHTS", sizeof("WEIGHTS")-1); + + // Process our weights + for(zend_hash_internal_pointer_reset_ex(ht_weights,&ptr); + zend_hash_get_current_data_ex(ht_weights,(void**)&z_ele,&ptr) + ==SUCCESS; + zend_hash_move_forward_ex(ht_weights,&ptr)) + { + // Ignore non numeric args unless they're inf/-inf + if(Z_TYPE_PP(z_ele)!=IS_LONG && Z_TYPE_PP(z_ele)!=IS_DOUBLE && + strncasecmp(Z_STRVAL_PP(z_ele),"inf",sizeof("inf"))!=0 && + strncasecmp(Z_STRVAL_PP(z_ele),"-inf",sizeof("-inf"))!=0 && + strncasecmp(Z_STRVAL_PP(z_ele),"+inf",sizeof("+inf"))!=0) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Weights must be numeric or '-inf','inf','+inf'"); + efree(cmdstr.c); + return FAILURE; + } + + switch(Z_TYPE_PP(z_ele)) { + case IS_LONG: + redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_PP(z_ele)); + break; + case IS_DOUBLE: + redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_PP(z_ele)); + break; + case IS_STRING: + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_PP(z_ele), + Z_STRLEN_PP(z_ele)); + break; + } + } + } + + // AGGREGATE + if(agg_op_len != 0) { + redis_cmd_append_sstr(&cmdstr, "AGGREGATE", sizeof("AGGREGATE")-1); + redis_cmd_append_sstr(&cmdstr, agg_op, agg_op_len); + } + + // Push out values + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* Commands with specific signatures or that need unique functions because they * have specific processing (argument validation, etc) that make them unique */ diff --git a/redis_commands.h b/redis_commands.h index 3205a7324e..e23faf67f6 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -61,6 +61,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, void **ctx); +int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them From b16ace6ba6173168828bb937a781522f97e3f3b5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 9 Jun 2014 14:55:29 -0700 Subject: [PATCH 0305/1986] Added command generic, LPUSH/RPUSH Added a generic command routine where we take a key and then a variable number of arguments. This works for stuff like LPUSH, RPUSH, SADD, SREM, and the like... Implemented LPUSH/RPUSH --- redis.c | 25 ++----------------- redis_cluster.c | 14 +++++++++++ redis_cluster.h | 2 ++ redis_commands.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++-- redis_commands.h | 3 +++ 5 files changed, 84 insertions(+), 25 deletions(-) diff --git a/redis.c b/redis.c index 7b94cc051c..bc63c01209 100644 --- a/redis.c +++ b/redis.c @@ -1130,17 +1130,7 @@ PHP_METHOD(Redis, strlen) */ PHP_METHOD(Redis, lPush) { - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "LPUSH", sizeof("LPUSH") - 1, - 2, &redis_sock, 0, 0, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, redis_long_response); } /* }}} */ @@ -1148,17 +1138,7 @@ PHP_METHOD(Redis, lPush) */ PHP_METHOD(Redis, rPush) { - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "RPUSH", sizeof("RPUSH") - 1, - 2, &redis_sock, 0, 0, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, redis_long_response); } /* }}} */ @@ -1469,7 +1449,6 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, keys_to_free = emalloc(array_size * sizeof(int)); memset(keys_to_free, 0, array_size * sizeof(int)); - /* Start computing the command length */ cmd_len = 1 + integer_length(keyword_len) + 2 +keyword_len + 2; diff --git a/redis_cluster.c b/redis_cluster.c index ace70117ed..1a8a57e0b3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -52,6 +52,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, rpop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lpush, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, linsert, NULL, ZEND_ACC_PUBLIC) @@ -401,6 +403,18 @@ PHP_METHOD(RedisCluster, strlen) { CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_bulk_resp); } +/* {{{ proto long RedisCluster::lpush(string key, string val1, ... valN) */ +PHP_METHOD(RedisCluster, lpush) { + CLUSTER_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::rpush(string key, string val1, ... valN) */ +PHP_METHOD(RedisCluster, rpush) { + CLUSTER_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, cluster_long_resp); +} +/* }}} */ + /* {{{ proto long RedisCluster::rpushx(string key, mixed value) */ PHP_METHOD(RedisCluster, rpushx) { CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, cluster_long_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 4c84451fd9..12d40ecd6a 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -77,6 +77,8 @@ PHP_METHOD(RedisCluster, persist); PHP_METHOD(RedisCluster, lpop); PHP_METHOD(RedisCluster, rpop); PHP_METHOD(RedisCluster, spop); +PHP_METHOD(RedisCluster, rpush); +PHP_METHOD(RedisCluster, lpush); PHP_METHOD(RedisCluster, rpushx); PHP_METHOD(RedisCluster, lpushx); PHP_METHOD(RedisCluster, linsert); diff --git a/redis_commands.c b/redis_commands.c index 092849061b..626178588d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -654,8 +654,69 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -/* Commands with specific signatures or that need unique functions because they - * have specific processing (argument validation, etc) that make them unique */ +/* Commands that take a key followed by a variable list of serializable + * values (RPUSH, LPUSH, SADD, SREM, etc...) */ +int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + zval **z_args; + smart_str cmdstr = {0}; + char *arg; + int arg_free, arg_len, i; + int argc = ZEND_NUM_ARGS(); + + // We at least need a key and one value + if(argc < 2) { + return FAILURE; + } + + // Make sure we at least have a key, and we can get other args + z_args = emalloc(argc * sizeof(zval*)); + if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + efree(z_args); + return FAILURE; + } + + // Grab the first argument (our key) as a string + convert_to_string(z_args[0]); + arg = Z_STRVAL_P(z_args[0]); + arg_len = Z_STRLEN_P(z_args[0]); + + // Prefix if required + arg_free = redis_key_prefix(redis_sock, &arg, &arg_len); + + // Start command construction + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + redis_cmd_append_sstr(&cmdstr, arg, arg_len); + + // Set our slot, free key prefix if we prefixed it + CMD_SET_SLOT(slot,arg,arg_len); + if(arg_free) efree(arg); + + // Add our members + for(i=1;i Date: Mon, 9 Jun 2014 15:01:34 -0700 Subject: [PATCH 0306/1986] SADD/SREM Implemented SADD and SREM for both Redis proper and cluster --- redis.c | 25 +++---------------------- redis_cluster.c | 14 ++++++++++++++ redis_cluster.h | 2 ++ 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/redis.c b/redis.c index bc63c01209..b2e828fed0 100644 --- a/redis.c +++ b/redis.c @@ -1267,17 +1267,7 @@ PHP_METHOD(Redis, lGetRange) */ PHP_METHOD(Redis, sAdd) { - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "SADD", sizeof("SADD") - 1, - 2, &redis_sock, 0, 0, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, redis_long_response); } /* }}} */ @@ -1292,19 +1282,10 @@ PHP_METHOD(Redis, sSize) */ PHP_METHOD(Redis, sRemove) { - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "SREM", sizeof("SREM") - 1, - 2, &redis_sock, 0, 0, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, redis_long_response); } /* }}} */ + /* {{{ proto boolean Redis::sMove(string set_src, string set_dst, mixed value) */ PHP_METHOD(Redis, sMove) diff --git a/redis_cluster.c b/redis_cluster.c index 1a8a57e0b3..d749079d50 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -64,6 +64,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, scard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, smembers, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sismember, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sadd, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, srem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, srandmember, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, persist, NULL, ZEND_ACC_PUBLIC) @@ -475,6 +477,18 @@ PHP_METHOD(RedisCluster, sismember) { } /* }}} */ +/* {{{ proto long RedisCluster::sadd(string key, string val1 [, ...]) */ +PHP_METHOD(RedisCluster, sadd) { + CLUSTER_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::srem(string key, string val1 [, ...]) */ +PHP_METHOD(RedisCluster, srem) { + CLUSTER_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, cluster_long_resp); +} +/* }}} */ + /* {{{ proto bool RedisCluster::smove(sting src, string dst, string mem) */ PHP_METHOD(RedisCluster, smove) { CLUSTER_PROCESS_CMD(smove, cluster_1_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 12d40ecd6a..2c1303dfca 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -89,6 +89,8 @@ PHP_METHOD(RedisCluster, llen); PHP_METHOD(RedisCluster, scard); PHP_METHOD(RedisCluster, smembers); PHP_METHOD(RedisCluster, sismember); +PHP_METHOD(RedisCluster, sadd); +PHP_METHOD(RedisCluster, srem); PHP_METHOD(RedisCluster, strlen); PHP_METHOD(RedisCluster, ttl); PHP_METHOD(RedisCluster, pttl); From 0fa7f0b993b0073db4f5b62c711447055563b973 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 9 Jun 2014 15:07:17 -0700 Subject: [PATCH 0307/1986] ZREM Added ZREM for both cluster and redis proper --- redis.c | 12 +----------- redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/redis.c b/redis.c index b2e828fed0..bb7e93fea7 100644 --- a/redis.c +++ b/redis.c @@ -2440,17 +2440,7 @@ PHP_METHOD(Redis, zRevRangeByScore) { */ PHP_METHOD(Redis, zDelete) { - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "ZREM", sizeof("ZREM") - 1, - 2, &redis_sock, 0, 0, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index d749079d50..bfce3352cc 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -123,6 +123,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zunionstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zinterstore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -884,6 +885,12 @@ PHP_METHOD(RedisCluster, zinterstore) { } /* }}} */ +/* {{{ proto RedisCluster::zrem(string key, string val1, ... valN) */ +PHP_METHOD(RedisCluster, zrem) { + CLUSTER_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, cluster_long_resp); +} +/* }}} */ + /* {{{ proto array * RedisCluster::zrevrangebyscore(string k, long s, long e, array opts) */ PHP_METHOD(RedisCluster, zrevrangebyscore) { diff --git a/redis_cluster.h b/redis_cluster.h index 2c1303dfca..943f5955db 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -97,6 +97,7 @@ PHP_METHOD(RedisCluster, pttl); PHP_METHOD(RedisCluster, zcard); PHP_METHOD(RedisCluster, zscore); PHP_METHOD(RedisCluster, zcount); +PHP_METHOD(RedisCluster, zrem); PHP_METHOD(RedisCluster, zremrangebyscore); PHP_METHOD(RedisCluster, zrank); PHP_METHOD(RedisCluster, zrevrank); From 4865e6eb0a4f60038f3fddb6d1df7f43247c221d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 9 Jun 2014 15:27:15 -0700 Subject: [PATCH 0308/1986] HDEL command Implemented HDEL command for both Redis and RedisCluster objects --- redis.c | 12 +---------- redis_cluster.c | 7 ++++++ redis_cluster.h | 1 + redis_commands.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 5 files changed, 68 insertions(+), 11 deletions(-) diff --git a/redis.c b/redis.c index bb7e93fea7..29c46c356b 100644 --- a/redis.c +++ b/redis.c @@ -2608,17 +2608,7 @@ PHP_METHOD(Redis, hLen) /* hDel */ PHP_METHOD(Redis, hDel) { - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "HDEL", sizeof("HDEL") - 1, - 2, &redis_sock, 0, 0, 0)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_CMD(hdel, redis_long_response); } /* {{{ proto bool Redis::hExists(string key, string mem) */ diff --git a/redis_cluster.c b/redis_cluster.c index bfce3352cc..f5a78753ce 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -85,6 +85,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hincrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hmget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hmset, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hdel, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrbyfloat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) @@ -625,6 +626,12 @@ PHP_METHOD(RedisCluster, hmset) { } /* }}} */ +/* {{{ proto long RedisCluster::hdel(string key, string mem1, ... memN) */ +PHP_METHOD(RedisCluster, hdel) { + CLUSTER_PROCESS_CMD(hdel, cluster_long_resp); +} +/* }}} */ + /* {{{ proto array RedisCluster::hmget(string key, array members) */ PHP_METHOD(RedisCluster, hmget) { CLUSTER_PROCESS_CMD(hmget, cluster_mbulk_assoc_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 943f5955db..781ba949f5 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -108,6 +108,7 @@ PHP_METHOD(RedisCluster, hkeys); PHP_METHOD(RedisCluster, hvals); PHP_METHOD(RedisCluster, hmget); PHP_METHOD(RedisCluster, hmset); +PHP_METHOD(RedisCluster, hdel); PHP_METHOD(RedisCluster, hgetall); PHP_METHOD(RedisCluster, hexists); PHP_METHOD(RedisCluster, hincrby); diff --git a/redis_commands.c b/redis_commands.c index 626178588d..88fac7e857 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1883,4 +1883,60 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* HDEL */ +int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zval **z_args; + smart_str cmdstr = {0}; + char *arg; + int arg_free, arg_len, i; + int argc = ZEND_NUM_ARGS(); + + // We need at least KEY and one member + if(argc < 2) { + return FAILURE; + } + + // Grab arguments as an array + z_args = emalloc(argc * sizeof(zval*)); + if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + efree(z_args); + return FAILURE; + } + + // Get first argument (the key) as a string + convert_to_string(z_args[0]); + arg = Z_STRVAL_P(z_args[0]); + arg_len = Z_STRLEN_P(z_args[0]); + + // Prefix + arg_free = redis_key_prefix(redis_sock, &arg, &arg_len); + + // Start command construction + redis_cmd_init_sstr(&cmdstr, argc, "HDEL", sizeof("HDEL")-1); + redis_cmd_append_sstr(&cmdstr, arg, arg_len); + + // Set our slot, free key if we prefixed it + CMD_SET_SLOT(slot,arg,arg_len); + if(arg_free) efree(arg); + + // Iterate through the members we're removing + for(i=1;i Date: Mon, 9 Jun 2014 16:05:15 -0700 Subject: [PATCH 0309/1986] ZADD command Implemented ZADD command for both Redis and RedisCluster --- redis.c | 98 +----------------------------------------------- redis_cluster.c | 7 ++++ redis_cluster.h | 1 + redis_commands.c | 61 ++++++++++++++++++++++++++++++ redis_commands.h | 3 ++ 5 files changed, 73 insertions(+), 97 deletions(-) diff --git a/redis.c b/redis.c index 29c46c356b..3f5374236e 100644 --- a/redis.c +++ b/redis.c @@ -2269,103 +2269,7 @@ PHP_METHOD(Redis, brpoplpush) { /* {{{ proto long Redis::zAdd(string key, int score, string value) */ PHP_METHOD(Redis, zAdd) { - - RedisSock *redis_sock; - - char *cmd; - int cmd_len, key_len, val_len; - double score; - char *key, *val; - int val_free, key_free = 0; - char *dbl_str; - int dbl_len; - smart_str buf = {0}; - - zval **z_args; - int argc = ZEND_NUM_ARGS(), i; - - /* get redis socket */ - if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - z_args = emalloc(argc * sizeof(zval*)); - if(zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - /* need key, score, value, [score, value...] */ - if(argc > 1) { - convert_to_string(z_args[0]); /* required string */ - } - if(argc < 3 || Z_TYPE_P(z_args[0]) != IS_STRING || (argc-1) % 2 != 0) { - efree(z_args); - RETURN_FALSE; - } - - /* possibly serialize key */ - key = Z_STRVAL_P(z_args[0]); - key_len = Z_STRLEN_P(z_args[0]); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* start building the command */ - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, argc + 1); /* +1 for ZADD command */ - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - /* add command name */ - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, 4); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, "ZADD", 4); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - /* add key */ - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, key_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, key, key_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - for(i = 1; i < argc; i +=2) { - convert_to_double(z_args[i]); // convert score to double - val_free = redis_serialize(redis_sock, z_args[i+1], &val, &val_len TSRMLS_CC); // possibly serialize value. - - /* add score */ - score = Z_DVAL_P(z_args[i]); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, score) - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - efree(dbl_str); - - /* add value */ - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, val, val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - - if(val_free) STR_FREE(val); - } - - /* end string */ - smart_str_0(&buf); - cmd = buf.c; - cmd_len = buf.len; - if(key_free) efree(key); - - /* cleanup */ - efree(z_args); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_CMD(zadd, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index f5a78753ce..bde5b6ded3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -75,6 +75,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zcount, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zremrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zadd, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zincrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) @@ -527,6 +528,12 @@ PHP_METHOD(RedisCluster, zscore) { } /* }}} */ +/* {{{ proto long RedisCluster::zadd(string key,double score,string mem, ...) */ +PHP_METHOD(RedisCluster, zadd) { + CLUSTER_PROCESS_CMD(zadd, cluster_long_resp); +} +/* }}} */ + /* {{{ proto double RedisCluster::zincrby(string key, double by, string mem) */ PHP_METHOD(RedisCluster, zincrby) { CLUSTER_PROCESS_CMD(zincrby, cluster_dbl_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 781ba949f5..100255744f 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -101,6 +101,7 @@ PHP_METHOD(RedisCluster, zrem); PHP_METHOD(RedisCluster, zremrangebyscore); PHP_METHOD(RedisCluster, zrank); PHP_METHOD(RedisCluster, zrevrank); +PHP_METHOD(RedisCluster, zadd); PHP_METHOD(RedisCluster, zincrby); PHP_METHOD(RedisCluster, hlen); PHP_METHOD(RedisCluster, hget); diff --git a/redis_commands.c b/redis_commands.c index 88fac7e857..b928bbd83c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1939,4 +1939,65 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* ZADD */ +int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zval **z_args; + char *key, *val; + int key_len, key_free, val_len, val_free; + int argc = ZEND_NUM_ARGS(), i; + smart_str cmdstr = {0}; + + z_args = emalloc(argc * sizeof(zval*)); + if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + efree(z_args); + return FAILURE; + } + + // Need key, score, value, [score, value...] */ + if(argc>0) convert_to_string(z_args[0]); + if(argc<3 || Z_TYPE_P(z_args[0])!=IS_STRING || (argc-1)%2 != 0) { + efree(z_args); + return FAILURE; + } + + // Prefix our key + key = Z_STRVAL_P(z_args[0]); + key_len = Z_STRLEN_P(z_args[0]); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Start command construction + redis_cmd_init_sstr(&cmdstr, argc, "ZADD", sizeof("ZADD")-1); + redis_cmd_append_sstr(&cmdstr, key, key_len); + + // Set our slot, free key if we prefixed it + CMD_SET_SLOT(slot,key,key_len); + if(key_free) efree(key); + + // Now the rest of our arguments + for(i=1;i Date: Mon, 9 Jun 2014 16:50:47 -0700 Subject: [PATCH 0310/1986] OBJECT command Implemented the OBJECT command for both Redis and Redis Cluster --- redis.c | 50 +++++++++++++++++++++--------------------------- redis_cluster.c | 30 +++++++++++++++++++++++++++-- redis_cluster.h | 1 + redis_commands.c | 43 +++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 4 ++++ 5 files changed, 98 insertions(+), 30 deletions(-) diff --git a/redis.c b/redis.c index 3f5374236e..491b37dc83 100644 --- a/redis.c +++ b/redis.c @@ -3270,39 +3270,33 @@ PHP_METHOD(Redis, slaveof) */ PHP_METHOD(Redis, object) { - zval *object; RedisSock *redis_sock; - char *cmd = "", *info = NULL, *key = NULL; - int cmd_len, info_len, key_len; + char *cmd; int cmd_len; + REDIS_REPLY_TYPE rtype; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", - &object, redis_ce, &info, &info_len, &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { + RETURN_FALSE; } - cmd_len = redis_cmd_format_static(&cmd, "OBJECT", "ss", info, - info_len, key, key_len); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + if(redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &rtype, + &cmd, &cmd_len, NULL, NULL)==FAILURE) + { + RETURN_FALSE; + } - if(info_len == 8 && (strncasecmp(info, "refcount", 8) == 0 || strncasecmp(info, "idletime", 8) == 0)) { - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - } else if(info_len == 8 && strncasecmp(info, "encoding", 8) == 0) { - IF_ATOMIC() { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); - } else { /* fail */ - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); - } + if(rtype == TYPE_INT) { + IF_ATOMIC() { + redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_long_response); + } else { + IF_ATOMIC() { + redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_string_response); + } } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index bde5b6ded3..e352dd2858 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -127,6 +127,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zinterstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, object, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -922,7 +923,6 @@ PHP_METHOD(RedisCluster, sort) { if(redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &have_store, &cmd, &cmd_len, &slot, NULL)==FAILURE) { - efree(cmd); RETURN_FALSE; } @@ -939,7 +939,33 @@ PHP_METHOD(RedisCluster, sort) { } else { cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } - +} + +/* {{{ proto Redis::object(string subcmd, string key) */ +PHP_METHOD(RedisCluster, object) { + redisCluster *c = GET_CONTEXT(); + char *cmd; int cmd_len; short slot; + REDIS_REPLY_TYPE rtype; + + if(redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &rtype, + &cmd, &cmd_len, &slot, NULL)==FAILURE) + { + RETURN_FALSE; + } + + if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { + efree(cmd); + RETURN_FALSE; + } + + efree(cmd); + + // Use the correct response type + if(rtype == TYPE_INT) { + cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } } /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 100255744f..5f3dceb61d 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -153,4 +153,5 @@ PHP_METHOD(RedisCluster, zrangebyscore); PHP_METHOD(RedisCluster, zunionstore); PHP_METHOD(RedisCluster, zinterstore); PHP_METHOD(RedisCluster, sort); +PHP_METHOD(RedisCluster, object); #endif diff --git a/redis_commands.c b/redis_commands.c index b928bbd83c..72f0089c81 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2000,4 +2000,47 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* OBJECT */ +int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + REDIS_REPLY_TYPE *rtype, char **cmd, int *cmd_len, + short *slot, void **ctx) +{ + char *key, *subcmd; + int key_len, key_free, subcmd_len; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &subcmd, + &subcmd_len, &key, &key_len)==FAILURE) + { + return FAILURE; + } + + // Prefix our key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Format our command + *cmd_len = redis_cmd_format_static(cmd, "OBJECT", "ss", subcmd, subcmd_len, + key, key_len); + + // Set our slot, free key if we prefixed + CMD_SET_SLOT(slot,key,key_len); + if(key_free) efree(key); + + // Push the reply type to our caller + if(subcmd_len == 8 && (!strncasecmp(subcmd,"refcount",8) || + !strncasecmp(subcmd,"idletime",8))) + { + *rtype = TYPE_INT; + } else if(subcmd_len == 8 && !strncasecmp(subcmd, "encoding", 8)) { + *rtype = TYPE_BULK; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Invalid subcommand sent to OBJECT"); + efree(*cmd); + return FAILURE; + } + + // Success + return SUCCESS; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index f61225fc2e..33b5ac8413 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -141,6 +141,10 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + REDIS_REPLY_TYPE *rtype, char **cmd, int *cmd_len, short *slot, + void **ctx); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From b4b9f3720b8c38281bd015ac0b0f2fbf55b39d2b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 08:43:09 -0700 Subject: [PATCH 0311/1986] Reworked generic_multiple_args command, DEL Implemented a new version of generic_multiple_args_command which is slightly simpler, avoiding trying to do everything (including different commands with keys, or keys/values) and only operating on keys. The other methods have been implemented differently. Implemented DEL for Redis proper. Cluster DEL will need to be a special kind of command where the client splits the request to various nodes. --- redis.c | 4 +- redis_commands.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 30 ++++++++ 3 files changed, 230 insertions(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 491b37dc83..71e2614fc5 100644 --- a/redis.c +++ b/redis.c @@ -1008,6 +1008,8 @@ PHP_METHOD(Redis, exists) */ PHP_METHOD(Redis, delete) { + REDIS_PROCESS_CMD(del, redis_long_response); +/* RedisSock *redis_sock; if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, @@ -1019,7 +1021,7 @@ PHP_METHOD(Redis, delete) redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_long_response); - +*/ } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 72f0089c81..a60034bbc5 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -713,6 +713,123 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic function that takes a variable number of keys, with an optional + * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type + * commands. */ +static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, int kw_len, int min_argc, int has_timeout, + char **cmd, int *cmd_len, short *slot) +{ + zval **z_args, **z_ele; + HashTable *ht_arr; + char *key; + int key_free, key_len, i, tail; + int single_array = 0, argc = ZEND_NUM_ARGS(); + smart_str cmdstr = {0}; + long timeout; + short kslot = -1; + + if(argc < min_argc) { + zend_wrong_param_count(TSRMLS_C); + return FAILURE; + } + + // Allocate args + z_args = emalloc(argc * sizeof(zval *)); + if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + efree(z_args); + return FAILURE; + } + + // Handle our "single array" case + if(has_timeout == 0) { + single_array = argc==1 && Z_TYPE_P(z_args[0])==IS_ARRAY; + } else { + single_array = argc==2 && Z_TYPE_P(z_args[0])==IS_ARRAY && + Z_TYPE_P(z_args[1])==IS_LONG; + timeout = Z_LVAL_P(z_args[1]); + } + + // If we're running a single array, rework args + if(single_array) { + ht_arr = Z_ARRVAL_P(z_args[0]); + argc = zend_hash_num_elements(ht_arr); + efree(z_args); + z_args = NULL; + } + + // Begin construction of our command + redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); + + if(single_array) { + for(zend_hash_internal_pointer_reset(ht_arr); + zend_hash_get_current_data(ht_arr,(void**)&z_ele)==SUCCESS; + zend_hash_move_forward(ht_arr)) + { + convert_to_string(*z_ele); + key = Z_STRVAL_PP(z_ele); + key_len = Z_STRLEN_PP(z_ele); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Protect against CROSSLOT errors + if(slot) { + if(kslot == -1) { + kslot = cluster_hash_key(key, key_len); + } else if(cluster_hash_key(key,key_len)!=kslot) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Not all keys hash to the same slot!"); + return FAILURE; + } + } + + // Append this key, free it if we prefixed + redis_cmd_append_sstr(&cmdstr, key, key_len); + if(key_free) efree(key); + } + if(has_timeout) { + redis_cmd_append_sstr_long(&cmdstr, timeout); + } + } else { + if(has_timeout && Z_TYPE_P(z_args[argc-1])!=IS_LONG) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, + "Timeout value must be a LONG"); + return FAILURE; + } + + tail = has_timeout ? argc-1 : argc; + for(i=0;i Date: Tue, 10 Jun 2014 08:46:45 -0700 Subject: [PATCH 0312/1986] WATCH command, formatting --- redis.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/redis.c b/redis.c index 71e2614fc5..f336aa29ca 100644 --- a/redis.c +++ b/redis.c @@ -1009,19 +1009,6 @@ PHP_METHOD(Redis, exists) PHP_METHOD(Redis, delete) { REDIS_PROCESS_CMD(del, redis_long_response); -/* - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "DEL", sizeof("DEL") - 1, - 1, &redis_sock, 0, 1, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); -*/ } /* }}} */ @@ -1030,26 +1017,18 @@ PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock) redis_sock->watching = 1; } -PHP_REDIS_API void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHPAPI void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_set_watch); + redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, ctx, redis_set_watch); } /* {{{ proto boolean Redis::watch(string key1, string key2...) */ PHP_METHOD(Redis, watch) { - RedisSock *redis_sock; - - generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "WATCH", sizeof("WATCH") - 1, - 1, &redis_sock, 0, 1, 1); - redis_sock->watching = 1; - IF_ATOMIC() { - redis_watch_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_watch_response); - + REDIS_PROCESS_CMD(watch, redis_watch_response); } /* }}} */ @@ -1058,9 +1037,12 @@ PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock) redis_sock->watching = 0; } -PHP_REDIS_API void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHPAPI void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_clear_watch); + redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, ctx, redis_clear_watch); } /* {{{ proto boolean Redis::unwatch() From 955604cbd6a1aada0c7b2691b8dc961bf5ba16ed Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 09:34:06 -0700 Subject: [PATCH 0313/1986] Fix memory leak, inc arg count Properly free allocated z_args array, as well as increment our argc value in the case of a single array + timeout command. --- redis.c | 31 ++----------------------------- redis_cluster.c | 14 ++++++++++++++ redis_cluster.h | 2 ++ redis_commands.c | 8 +++++++- 4 files changed, 25 insertions(+), 30 deletions(-) diff --git a/redis.c b/redis.c index f336aa29ca..7efad61d54 100644 --- a/redis.c +++ b/redis.c @@ -1166,21 +1166,7 @@ PHP_METHOD(Redis, rPop) */ PHP_METHOD(Redis, blPop) { - - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "BLPOP", sizeof("BLPOP") - 1, - 2, &redis_sock, 1, 1, 1)) - return; - - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + REDIS_PROCESS_CMD(blpop, redis_sock_read_multibulk_reply); } /* }}} */ @@ -1188,20 +1174,7 @@ PHP_METHOD(Redis, blPop) */ PHP_METHOD(Redis, brPop) { - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "BRPOP", sizeof("BRPOP") - 1, - 2, &redis_sock, 1, 1, 1)) - return; - - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + REDIS_PROCESS_CMD(brpop, redis_sock_read_multibulk_reply); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index e352dd2858..5271775528 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -54,6 +54,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpush, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, blpop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, brpop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, linsert, NULL, ZEND_ACC_PUBLIC) @@ -421,6 +423,18 @@ PHP_METHOD(RedisCluster, rpush) { } /* }}} */ +/* {{{ proto array RedisCluster::blpop(string key1, ... keyN, long timeout) */ +PHP_METHOD(RedisCluster, blpop) { + CLUSTER_PROCESS_CMD(blpop, cluster_mbulk_resp); +} +/* }}} */ + +/* {{{ proto array RedisCluster::brpop(string key1, ... keyN, long timeout */ +PHP_METHOD(RedisCluster, brpop) { + CLUSTER_PROCESS_CMD(brpop, cluster_mbulk_resp); +} +/* }}} */ + /* {{{ proto long RedisCluster::rpushx(string key, mixed value) */ PHP_METHOD(RedisCluster, rpushx) { CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, cluster_long_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 5f3dceb61d..fe7f553f98 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -79,6 +79,8 @@ PHP_METHOD(RedisCluster, rpop); PHP_METHOD(RedisCluster, spop); PHP_METHOD(RedisCluster, rpush); PHP_METHOD(RedisCluster, lpush); +PHP_METHOD(RedisCluster, blpop); +PHP_METHOD(RedisCluster, brpop); PHP_METHOD(RedisCluster, rpushx); PHP_METHOD(RedisCluster, lpushx); PHP_METHOD(RedisCluster, linsert); diff --git a/redis_commands.c b/redis_commands.c index a60034bbc5..2bcc96e898 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -754,6 +754,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(single_array) { ht_arr = Z_ARRVAL_P(z_args[0]); argc = zend_hash_num_elements(ht_arr); + if(has_timeout) argc++; efree(z_args); z_args = NULL; } @@ -793,6 +794,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(has_timeout && Z_TYPE_P(z_args[argc-1])!=IS_LONG) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Timeout value must be a LONG"); + efree(z_args); return FAILURE; } @@ -810,6 +812,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else if(cluster_hash_key(key,key_len)!=kslot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not all keys hash to the same slot"); + efree(z_args); return FAILURE; } @@ -820,6 +823,9 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(has_timeout) { redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_args[tail])); } + + // Cleanup args + efree(z_args); } // Push out parameters @@ -2181,7 +2187,7 @@ int redis_blpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - "BLPOP", sizeof("BLPOP")-1, 1, 1, cmd, cmd_len, slot); + "BLPOP", sizeof("BLPOP")-1, 2, 1, cmd, cmd_len, slot); } /* BRPOP */ From 5c326bf4efa8ee6098620b80d5fd600888960b6e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 09:53:28 -0700 Subject: [PATCH 0314/1986] Implemented set aggregation methods for Redis and Cluster Implemented the various set aggregation functions and store variations for both Redis and RedisCluster in a generic way. * SUNION * SUNIONSTORE * SINTER * SINTERSTORE * SDIFF * SDIFFSTORE --- redis.c | 91 ++++--------------------------------------------- redis_cluster.c | 42 +++++++++++++++++++++++ redis_cluster.h | 6 ++++ 3 files changed, 54 insertions(+), 85 deletions(-) diff --git a/redis.c b/redis.c index 7efad61d54..f74ffac52f 100644 --- a/redis.c +++ b/redis.c @@ -1549,120 +1549,41 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, /* {{{ proto array Redis::sInter(string key0, ... string keyN) */ PHP_METHOD(Redis, sInter) { - - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "SINTER", sizeof("SINTER") - 1, - 0, &redis_sock, 0, 1, 1)) - return; - - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + REDIS_PROCESS_CMD(sinter, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto array Redis::sInterStore(string destination, string key0, ... string keyN) */ PHP_METHOD(Redis, sInterStore) { - - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "SINTERSTORE", sizeof("SINTERSTORE") - 1, - 1, &redis_sock, 0, 1, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - - + REDIS_PROCESS_CMD(sinterstore, redis_long_response); } /* }}} */ /* {{{ proto array Redis::sUnion(string key0, ... string keyN) */ PHP_METHOD(Redis, sUnion) { - - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "SUNION", sizeof("SUNION") - 1, - 0, &redis_sock, 0, 1, 1)) - return; - - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + REDIS_PROCESS_CMD(sunion, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto array Redis::sUnionStore(string destination, string key0, ... string keyN) */ PHP_METHOD(Redis, sUnionStore) { - - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "SUNIONSTORE", sizeof("SUNIONSTORE") - 1, - 1, &redis_sock, 0, 1, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_CMD(sunionstore, redis_long_response); } - /* }}} */ /* {{{ proto array Redis::sDiff(string key0, ... string keyN) */ PHP_METHOD(Redis, sDiff) { - - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "SDIFF", sizeof("SDIFF") - 1, - 0, &redis_sock, 0, 1, 1)) - return; - - IF_ATOMIC() { - /* read multibulk reply */ - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + REDIS_PROCESS_CMD(sdiff, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto array Redis::sDiffStore(string destination, string key0, ... string keyN) */ PHP_METHOD(Redis, sDiffStore) { - - RedisSock *redis_sock; - - if(FAILURE == generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - "SDIFFSTORE", sizeof("SDIFFSTORE") - 1, - 1, &redis_sock, 0, 1, 1)) - return; - - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_CMD(sdiffstore, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 5271775528..b283d87cd2 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -68,6 +68,12 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, sismember, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sadd, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, srem, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sunion, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sunionstore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sinter, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sinterstore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sdiff, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sdiffstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, srandmember, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, persist, NULL, ZEND_ACC_PUBLIC) @@ -507,6 +513,42 @@ PHP_METHOD(RedisCluster, srem) { } /* }}} */ +/* {{{ proto array RedisCluster::sunion(string key1, ... keyN) */ +PHP_METHOD(RedisCluster, sunion) { + CLUSTER_PROCESS_CMD(sunion, cluster_mbulk_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::sunionstore(string dst, string k1, ... kN) */ +PHP_METHOD(RedisCluster, sunionstore) { + CLUSTER_PROCESS_CMD(sunionstore, cluster_long_resp); +} +/* }}} */ + +/* {{{ ptoto array RedisCluster::sinter(string k1, ... kN) */ +PHP_METHOD(RedisCluster, sinter) { + CLUSTER_PROCESS_CMD(sinter, cluster_mbulk_resp); +} +/* }}} */ + +/* {{{ ptoto long RedisCluster::sinterstore(string dst, string k1, ... kN) */ +PHP_METHOD(RedisCluster, sinterstore) { + CLUSTER_PROCESS_CMD(sinterstore, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto array RedisCluster::sdiff(string k1, ... kN) */ +PHP_METHOD(RedisCluster, sdiff) { + CLUSTER_PROCESS_CMD(sdiff, cluster_mbulk_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::sdiffstore(string dst, string k1, ... kN) */ +PHP_METHOD(RedisCluster, sdiffstore) { + CLUSTER_PROCESS_CMD(sdiffstore, cluster_long_resp); +} +/* }}} */ + /* {{{ proto bool RedisCluster::smove(sting src, string dst, string mem) */ PHP_METHOD(RedisCluster, smove) { CLUSTER_PROCESS_CMD(smove, cluster_1_resp); diff --git a/redis_cluster.h b/redis_cluster.h index fe7f553f98..af3d41085f 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -93,6 +93,12 @@ PHP_METHOD(RedisCluster, smembers); PHP_METHOD(RedisCluster, sismember); PHP_METHOD(RedisCluster, sadd); PHP_METHOD(RedisCluster, srem); +PHP_METHOD(RedisCluster, sunion); +PHP_METHOD(RedisCluster, sunionstore); +PHP_METHOD(RedisCluster, sinter); +PHP_METHOD(RedisCluster, sinterstore); +PHP_METHOD(RedisCluster, sdiff); +PHP_METHOD(RedisCluster, sdiffstore); PHP_METHOD(RedisCluster, strlen); PHP_METHOD(RedisCluster, ttl); PHP_METHOD(RedisCluster, pttl); From 62556a4f409a7ef62d85316bf146f59406c2cd4b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 09:56:06 -0700 Subject: [PATCH 0315/1986] Remove the old generic_multiple_arg_command function. This function isn't needed anymore, as it's been split into a couple different functions to avoid doing everything in one place with lots of checks/variations. --- redis.c | 232 -------------------------------------------------------- 1 file changed, 232 deletions(-) diff --git a/redis.c b/redis.c index f74ffac52f..c7311e32d0 100644 --- a/redis.c +++ b/redis.c @@ -1314,238 +1314,6 @@ PHP_METHOD(Redis, sMembers) } /* }}} */ -PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, - char *keyword, int keyword_len, - int min_argc, RedisSock **out_sock, - int has_timeout, int all_keys, - int can_serialize) -{ - zval **z_args, *z_array; - char **keys, *cmd; - int cmd_len, *keys_len, *keys_to_free; - int i, j, argc = ZEND_NUM_ARGS(), real_argc = 0; - int single_array = 0; - int timeout = 0; - int pos; - int array_size; - - RedisSock *redis_sock; - - if(argc < min_argc) { - zend_wrong_param_count(TSRMLS_C); - ZVAL_BOOL(return_value, 0); - return FAILURE; - } - - /* get redis socket */ - if (redis_sock_get(getThis(), out_sock TSRMLS_CC, 0) < 0) { - ZVAL_BOOL(return_value, 0); - return FAILURE; - } - redis_sock = *out_sock; - - z_args = emalloc(argc * sizeof(zval*)); - if(zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - ZVAL_BOOL(return_value, 0); - return FAILURE; - } - - /* case of a single array */ - if(has_timeout == 0) { - if(argc == 1 && Z_TYPE_P(z_args[0]) == IS_ARRAY) { - single_array = 1; - z_array = z_args[0]; - efree(z_args); - z_args = NULL; - - /* new count */ - argc = zend_hash_num_elements(Z_ARRVAL_P(z_array)); - } - } else if(has_timeout == 1) { - if(argc == 2 && Z_TYPE_P(z_args[0]) == IS_ARRAY && - Z_TYPE_P(z_args[1]) == IS_LONG) - { - single_array = 1; - z_array = z_args[0]; - timeout = Z_LVAL_P(z_args[1]); - efree(z_args); - z_args = NULL; - /* new count */ - argc = zend_hash_num_elements(Z_ARRVAL_P(z_array)); - } - } - - /* prepare an array for the keys, one for their lengths, one to mark the - * keys to free. */ - array_size = argc; - if(has_timeout) - array_size++; - - keys = emalloc(array_size * sizeof(char*)); - keys_len = emalloc(array_size * sizeof(int)); - keys_to_free = emalloc(array_size * sizeof(int)); - memset(keys_to_free, 0, array_size * sizeof(int)); - - /* Start computing the command length */ - cmd_len = 1 + integer_length(keyword_len) + 2 +keyword_len + 2; - - if(single_array) { /* loop over the array */ - HashTable *keytable = Z_ARRVAL_P(z_array); - - for(j = 0, zend_hash_internal_pointer_reset(keytable); - zend_hash_has_more_elements(keytable) == SUCCESS; - zend_hash_move_forward(keytable)) { - - char *key; - unsigned int key_len; - unsigned long idx; - zval **z_value_pp; - - zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, - NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) - == FAILURE) - { - /* this should never happen, according to the PHP people */ - continue; - } - - - if(!all_keys && j != 0) { /* not just operating on keys */ - - if(can_serialize) { - keys_to_free[j] = redis_serialize(redis_sock, *z_value_pp, - &keys[j], &keys_len[j] TSRMLS_CC); - } else { - convert_to_string(*z_value_pp); - keys[j] = Z_STRVAL_PP(z_value_pp); - keys_len[j] = Z_STRLEN_PP(z_value_pp); - keys_to_free[j] = 0; - } - - } else { - - /* only accept strings */ - if(Z_TYPE_PP(z_value_pp) != IS_STRING) { - convert_to_string(*z_value_pp); - } - - /* get current value */ - keys[j] = Z_STRVAL_PP(z_value_pp); - keys_len[j] = Z_STRLEN_PP(z_value_pp); - - keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], - &keys_len[j]); - } - /* $ + size + NL + string + NL */ - cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; - j++; - real_argc++; - } - if(has_timeout) { - keys_len[j] = spprintf(&keys[j], 0, "%d", timeout); - - /* $ + size + NL + string + NL */ - cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; - j++; - real_argc++; - } - } else { - if(has_timeout && Z_TYPE_P(z_args[argc - 1]) != IS_LONG) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, - "Syntax error on timeout"); - } - - for(i = 0, j = 0; i < argc; ++i) { /* store each key */ - if(!all_keys && j != 0) { /* not just operating on keys */ - if(can_serialize) { - keys_to_free[j] = redis_serialize(redis_sock, z_args[i], - &keys[j], &keys_len[j] TSRMLS_CC); - } else { - convert_to_string(z_args[i]); - keys[j] = Z_STRVAL_P(z_args[i]); - keys_len[j] = Z_STRLEN_P(z_args[i]); - keys_to_free[j] = 0; - } - - } else { - - if(Z_TYPE_P(z_args[i]) != IS_STRING) { - convert_to_string(z_args[i]); - } - - keys[j] = Z_STRVAL_P(z_args[i]); - keys_len[j] = Z_STRLEN_P(z_args[i]); - - // If we have a timeout it should be the last argument, which - // we do not want to prefix - if(!has_timeout || i < argc-1) { - keys_to_free[j] = redis_key_prefix(redis_sock, &keys[j], - &keys_len[j]); - } - } - - /* $ + size + NL + string + NL */ - cmd_len += 1 + integer_length(keys_len[j]) + 2 + keys_len[j] + 2; - j++; - real_argc++; - } - } - - cmd_len += 1 + integer_length(real_argc+1) + 2; /* *count NL */ - cmd = emalloc(cmd_len+1); - - sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1+real_argc, keyword_len, - keyword); - - pos = 1 +integer_length(real_argc + 1) + 2 - + 1 + integer_length(keyword_len) + 2 - + keyword_len + 2; - - /* copy each key to its destination */ - for(i = 0; i < real_argc; ++i) { - sprintf(cmd + pos, "$%d" _NL, keys_len[i]); /* size */ - pos += 1 + integer_length(keys_len[i]) + 2; - memcpy(cmd + pos, keys[i], keys_len[i]); - pos += keys_len[i]; - memcpy(cmd + pos, _NL, 2); - pos += 2; - } - - /* cleanup prefixed keys. */ - for(i = 0; i < real_argc + (has_timeout?-1:0); ++i) { - if(keys_to_free[i]) - STR_FREE(keys[i]); - } - - /* Cleanup timeout string */ - if(single_array && has_timeout) { - efree(keys[real_argc-1]); - } - - efree(keys); - efree(keys_len); - efree(keys_to_free); - - if(z_args) efree(z_args); - - /* call REDIS_PROCESS_REQUEST and skip void returns */ - IF_MULTI_OR_ATOMIC() { - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return FAILURE; - } - efree(cmd); - } - IF_PIPELINE() { - PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); - efree(cmd); - } - - return SUCCESS; -} - /* {{{ proto array Redis::sInter(string key0, ... string keyN) */ PHP_METHOD(Redis, sInter) { From 7d29952bab9537b6d3bfd407129864756ac54cd2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 10:11:05 -0700 Subject: [PATCH 0316/1986] Spaces > tabs :) --- redis.c | 887 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 444 insertions(+), 443 deletions(-) diff --git a/redis.c b/redis.c index c7311e32d0..7fae8d2c62 100644 --- a/redis.c +++ b/redis.c @@ -65,13 +65,13 @@ extern zend_function_entry redis_array_functions[]; extern zend_function_entry redis_cluster_functions[]; PHP_INI_BEGIN() - /* redis arrays */ - PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.index", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL) + /* redis arrays */ + PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.index", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL) PHP_INI_END() /** @@ -233,13 +233,13 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, watch, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, unwatch, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, publish, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, subscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, psubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, unsubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, punsubscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, publish, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, subscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, psubscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, unsubscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, punsubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, time, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, time, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, eval, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, evalsha, NULL, ZEND_ACC_PUBLIC) @@ -382,30 +382,30 @@ PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) /* Send a static DISCARD in case we're in MULTI mode. */ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { - int result = FAILURE; - char *cmd, *resp; - int resp_len, cmd_len; + int result = FAILURE; + char *cmd, *resp; + int resp_len, cmd_len; - /* format our discard command */ - cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); + /* format our discard command */ + cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); - /* send our DISCARD command */ - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && - (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) + /* send our DISCARD command */ + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && + (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) { - /* success if we get OK */ - result = (resp_len == 3 && strncmp(resp,"+OK", 3)==0) ? SUCCESS:FAILURE; + /* success if we get OK */ + result = (resp_len == 3 && strncmp(resp,"+OK", 3)==0) ? SUCCESS:FAILURE; - /* free our response */ - efree(resp); - } + /* free our response */ + efree(resp); + } - /* free our command */ - efree(cmd); + /* free our command */ + efree(cmd); - /* return success/failure */ - return result; + /* return success/failure */ + return result; } /** @@ -430,7 +430,7 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int sizeof("socket"), (void **) &socket) == FAILURE) { /* Throw an exception unless we've been requested not to */ if(!no_throw) { - zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); } return -1; } @@ -523,14 +523,14 @@ PHP_MINIT_FUNCTION(redis) zend_class_entry redis_exception_class_entry; zend_class_entry redis_cluster_exception_class_entry; - REGISTER_INI_ENTRIES(); + REGISTER_INI_ENTRIES(); - /* Redis class */ - INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions); + /* Redis class */ + INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions); redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC); - /* RedisArray class */ - INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions); + /* RedisArray class */ + INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions); redis_array_ce = zend_register_internal_class(&redis_array_class_entry TSRMLS_CC); /* RedisCluster class */ @@ -544,7 +544,7 @@ PHP_MINIT_FUNCTION(redis) "Redis Array", module_number ); - /* RedisException class */ + /* RedisException class */ INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL); redis_exception_ce = zend_register_internal_class_ex( &redis_exception_class_entry, @@ -623,34 +623,33 @@ PHP_METHOD(Redis, __construct) Public Destructor */ PHP_METHOD(Redis,__destruct) { - RedisSock *redis_sock; - - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { - RETURN_FALSE; - } + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { + RETURN_FALSE; + } - /* Grab our socket */ - if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 1) < 0) { - RETURN_FALSE; - } + // Grab our socket + RedisSock *redis_sock; + if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 1) < 0) { + RETURN_FALSE; + } - /* If we think we're in MULTI mode, send a discard */ - if(redis_sock->mode == MULTI) { - /* Discard any multi commands, and free any callbacks that have been queued */ - send_discard_static(redis_sock TSRMLS_CC); - free_reply_callbacks(getThis(), redis_sock); - } + // If we think we're in MULTI mode, send a discard + if(redis_sock->mode == MULTI) { + // Discard any multi commands, and free any callbacks that have been queued + send_discard_static(redis_sock TSRMLS_CC); + free_reply_callbacks(getThis(), redis_sock); + } } /* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]]) */ PHP_METHOD(Redis, connect) { - if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0) == FAILURE) { - RETURN_FALSE; - } else { - RETURN_TRUE; - } + if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0) == FAILURE) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } } /* }}} */ @@ -658,86 +657,89 @@ PHP_METHOD(Redis, connect) */ PHP_METHOD(Redis, pconnect) { - if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1) == FAILURE) { - RETURN_FALSE; - } else { - /* reset multi/exec state if there is one. */ - RedisSock *redis_sock; - if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } + if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1) == FAILURE) { + RETURN_FALSE; + } else { + /* reset multi/exec state if there is one. */ + RedisSock *redis_sock; + if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } - RETURN_TRUE; - } + RETURN_TRUE; + } } /* }}} */ -PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { - zval *object; - zval **socket; - int host_len, id; - char *host = NULL; - long port = -1; - long retry_interval = 0; +PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { + zval *object; + zval **socket; + int host_len, id; + char *host = NULL; + long port = -1; + long retry_interval = 0; - char *persistent_id = NULL; - int persistent_id_len = -1; + char *persistent_id = NULL; + int persistent_id_len = -1; - double timeout = 0.0; - RedisSock *redis_sock = NULL; + double timeout = 0.0; + RedisSock *redis_sock = NULL; #ifdef ZTS - /* not sure how in threaded mode this works so disabled persistence at first */ + /* not sure how in threaded mode this works so disabled persistence at + * first */ persistent = 0; #endif - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl", - &object, redis_ce, &host, &host_len, &port, - &timeout, &persistent_id, &persistent_id_len, - &retry_interval) == FAILURE) { - return FAILURE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|ldsl", &object, redis_ce, &host, + &host_len, &port, &timeout, &persistent_id, + &persistent_id_len, &retry_interval) + == FAILURE) + { + return FAILURE; + } - if (timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid timeout", 0 TSRMLS_CC); - return FAILURE; - } + if (timeout < 0L || timeout > INT_MAX) { + zend_throw_exception(redis_exception_ce, "Invalid timeout", 0 TSRMLS_CC); + return FAILURE; + } - if (retry_interval < 0L || retry_interval > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC); - return FAILURE; - } + if (retry_interval < 0L || retry_interval > INT_MAX) { + zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC); + return FAILURE; + } - if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */ - port = 6379; - } + if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */ + port = 6379; + } - /* if there is a redis sock already we have to remove it from the list */ - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) > 0) { - if (zend_hash_find(Z_OBJPROP_P(object), "socket", - sizeof("socket"), (void **) &socket) == FAILURE) - { - /* maybe there is a socket but the id isn't known.. what to do? */ - } else { - zend_list_delete(Z_LVAL_PP(socket)); /* the refcount should be decreased and the destructor called */ - } - } + /* if there is a redis sock already we have to remove it from the list */ + if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) > 0) { + if (zend_hash_find(Z_OBJPROP_P(object), "socket", + sizeof("socket"), (void **) &socket) == FAILURE) + { + /* maybe there is a socket but the id isn't known.. what to do? */ + } else { + zend_list_delete(Z_LVAL_PP(socket)); /* the refcount should be decreased and the destructor called */ + } + } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0); - if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { - redis_free_socket(redis_sock); - return FAILURE; - } + if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { + redis_free_socket(redis_sock); + return FAILURE; + } #if PHP_VERSION_ID >= 50400 - id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); + id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); #else - id = zend_list_insert(redis_sock, le_redis_sock); + id = zend_list_insert(redis_sock, le_redis_sock); #endif - add_property_resource(object, "socket", id); + add_property_resource(object, "socket", id); - return SUCCESS; + return SUCCESS; } /* {{{ proto long Redis::bitop(string op, string key, ...) */ @@ -1133,7 +1135,7 @@ PHP_METHOD(Redis, lInsert) /* {{{ proto long Redis::lPushx(string key, mixed value) */ PHP_METHOD(Redis, lPushx) -{ +{ REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_string_response); } /* }}} */ @@ -1400,7 +1402,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in char *cmd_lines[30]; int cmd_sizes[30]; - int sort_len; + int sort_len; int i, pos; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|sslls", @@ -1555,14 +1557,14 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in efree(cmd_lines[i]); } - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL) < 0) { + RETURN_FALSE; + } + } + REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); } @@ -1729,17 +1731,17 @@ PHP_METHOD(Redis, info) { /* Build a standalone INFO command or one with an option */ if(opt != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, + cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); + cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); } - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_info_response); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_info_response); } /* }}} */ @@ -1768,11 +1770,11 @@ PHP_METHOD(Redis, select) { cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", dbNumber); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_boolean_response); } /* }}} */ @@ -1791,10 +1793,10 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI char *cmd = NULL, *p = NULL; int cmd_len = 0, argc = 0, kw_len = strlen(kw); - int step = 0; /* 0: compute size; 1: copy strings. */ + int step = 0; // 0: compute size; 1: copy strings. zval *z_array; - HashTable *keytable; + HashTable *keytable; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_ce, &z_array) == FAILURE) { @@ -1809,34 +1811,34 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI RETURN_FALSE; } - for(step = 0; step < 2; ++step) { - if(step == 1) { - cmd_len += 1 + integer_length(1 + 2 * argc) + 2; /* star + arg count + NL */ - cmd_len += 1 + integer_length(kw_len) + 2; /* dollar + strlen(kw) + NL */ - cmd_len += kw_len + 2; /* kw + NL */ + for(step = 0; step < 2; ++step) { + if(step == 1) { + cmd_len += 1 + integer_length(1 + 2 * argc) + 2; /* star + arg count + NL */ + cmd_len += 1 + integer_length(kw_len) + 2; /* dollar + strlen(kw) + NL */ + cmd_len += kw_len + 2; /* kw + NL */ - p = cmd = emalloc(cmd_len + 1); /* alloc */ - p += sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1 + 2 * argc, kw_len, kw); /* copy header */ - } + p = cmd = emalloc(cmd_len + 1); /* alloc */ + p += sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1 + 2 * argc, kw_len, kw); /* copy header */ + } - keytable = Z_ARRVAL_P(z_array); - for(zend_hash_internal_pointer_reset(keytable); - zend_hash_has_more_elements(keytable) == SUCCESS; - zend_hash_move_forward(keytable)) { - - char *key, *val; - unsigned int key_len; - int val_len; - unsigned long idx; - int type; - zval **z_value_pp; - int val_free, key_free; - char buf[32]; - - type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ - } + keytable = Z_ARRVAL_P(z_array); + for(zend_hash_internal_pointer_reset(keytable); + zend_hash_has_more_elements(keytable) == SUCCESS; + zend_hash_move_forward(keytable)) { + + char *key, *val; + unsigned int key_len; + int val_len; + unsigned long idx; + int type; + zval **z_value_pp; + int val_free, key_free; + char buf[32]; + + type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { + continue; /* this should never happen, according to the PHP people. */ + } /* If the key isn't a string, use the index value returned when grabbing the */ /* key. We typecast to long, because they could actually be negative. */ @@ -1849,44 +1851,44 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI key_len--; } - if(step == 0) - argc++; /* found a valid arg */ - - val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len); - - if(step == 0) { /* counting */ - cmd_len += 1 + integer_length(key_len) + 2 - + key_len + 2 - + 1 + integer_length(val_len) + 2 - + val_len + 2; - } else { - p += sprintf(p, "$%d" _NL, key_len); /* key len */ - memcpy(p, key, key_len); p += key_len; /* key */ - memcpy(p, _NL, 2); p += 2; - - p += sprintf(p, "$%d" _NL, val_len); /* val len */ - memcpy(p, val, val_len); p += val_len; /* val */ - memcpy(p, _NL, 2); p += 2; - } + if(step == 0) + argc++; /* found a valid arg */ - if(val_free) STR_FREE(val); - if(key_free) efree(key); - } - } + val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + if(step == 0) { /* counting */ + cmd_len += 1 + integer_length(key_len) + 2 + + key_len + 2 + + 1 + integer_length(val_len) + 2 + + val_len + 2; + } else { + p += sprintf(p, "$%d" _NL, key_len); /* key len */ + memcpy(p, key, key_len); p += key_len; /* key */ + memcpy(p, _NL, 2); p += 2; - IF_ATOMIC() { - fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(fun); + p += sprintf(p, "$%d" _NL, val_len); /* val len */ + memcpy(p, val, val_len); p += val_len; /* val */ + memcpy(p, _NL, 2); p += 2; + } + + if(val_free) STR_FREE(val); + if(key_free) efree(key); + } + } + + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + + IF_ATOMIC() { + fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(fun); } /* {{{ proto bool Redis::mset(array (key => value, ...)) */ PHP_METHOD(Redis, mset) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response); + generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response); } /* }}} */ @@ -1894,7 +1896,7 @@ PHP_METHOD(Redis, mset) { /* {{{ proto bool Redis::msetnx(array (key => value, ...)) */ PHP_METHOD(Redis, msetnx) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response); + generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response); } /* }}} */ @@ -2117,7 +2119,7 @@ PHP_METHOD(Redis, zIncrBy) /* zInter */ PHP_METHOD(Redis, zInter) { - REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response); } /* zUnion */ @@ -2158,7 +2160,7 @@ PHP_METHOD(Redis, hLen) /* hDel */ PHP_METHOD(Redis, hDel) { - REDIS_PROCESS_CMD(hdel, redis_long_response); + REDIS_PROCESS_CMD(hdel, redis_long_response); } /* {{{ proto bool Redis::hExists(string key, string mem) */ @@ -2192,9 +2194,9 @@ PHP_METHOD(Redis, hGetAll) { PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC) { zval *z_ret; - HashTable *keytable; + HashTable *keytable; - MAKE_STD_ZVAL(z_ret); + MAKE_STD_ZVAL(z_ret); array_init(z_ret); keytable = Z_ARRVAL_P(z_tab); @@ -2210,7 +2212,7 @@ PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ + continue; /* this should never happen, according to the PHP people. */ } /* get current value, a key */ @@ -2224,7 +2226,7 @@ PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int /* fetch again */ zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ + continue; /* this should never happen, according to the PHP people. */ } /* get current value, a hash value now. */ @@ -2282,12 +2284,12 @@ PHP_METHOD(Redis, multi) RedisSock *redis_sock; char *cmd; - int response_len, cmd_len; - char * response; - zval *object; - long multi_value = MULTI; + int response_len, cmd_len; + char * response; + zval *object; + long multi_value = MULTI; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &object, redis_ce, &multi_value) == FAILURE) { RETURN_FALSE; } @@ -2298,47 +2300,47 @@ PHP_METHOD(Redis, multi) RETURN_FALSE; } - if(multi_value == MULTI || multi_value == PIPELINE) { - redis_sock->mode = multi_value; - } else { + if(multi_value == MULTI || multi_value == PIPELINE) { + redis_sock->mode = multi_value; + } else { RETURN_FALSE; - } + } redis_sock->current = NULL; - IF_MULTI() { + IF_MULTI() { cmd_len = redis_cmd_format_static(&cmd, "MULTI", ""); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - RETURN_FALSE; - } - efree(cmd); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + RETURN_FALSE; + } + efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } if(strncmp(response, "+OK", 3) == 0) { efree(response); - RETURN_ZVAL(getThis(), 1, 0); - } + RETURN_ZVAL(getThis(), 1, 0); + } efree(response); - RETURN_FALSE; - } - IF_PIPELINE() { + RETURN_FALSE; + } + IF_PIPELINE() { free_reply_callbacks(getThis(), redis_sock); - RETURN_ZVAL(getThis(), 1, 0); - } + RETURN_ZVAL(getThis(), 1, 0); + } } /* discard */ PHP_METHOD(Redis, discard) { RedisSock *redis_sock; - zval *object; + zval *object; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } @@ -2347,8 +2349,8 @@ PHP_METHOD(Redis, discard) RETURN_FALSE; } - redis_sock->mode = ATOMIC; - redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + redis_sock->mode = ATOMIC; + redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } PHP_REDIS_API int redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) @@ -2375,8 +2377,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME { char inbuf[1024]; - int numElems; - zval *z_tab; + int numElems; + zval *z_tab; redis_check_eof(redis_sock TSRMLS_CC); @@ -2385,7 +2387,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME return -1; } - /* number of responses */ + /* number of responses */ numElems = atoi(inbuf+1); if(numElems < 0) { @@ -2409,11 +2411,11 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME void free_reply_callbacks(zval *z_this, RedisSock *redis_sock) { - fold_item *fi; + fold_item *fi; fold_item *head = redis_sock->head; - request_item *ri; + request_item *ri; - for(fi = head; fi; ) { + for(fi = head; fi; ) { fold_item *fi_next = fi->next; free(fi); fi = fi_next; @@ -2437,83 +2439,83 @@ PHP_METHOD(Redis, exec) RedisSock *redis_sock; char *cmd; - int cmd_len; - zval *object; + int cmd_len; + zval *object; struct request_item *ri; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; + if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; } - IF_MULTI() { + IF_MULTI() { cmd_len = redis_cmd_format_static(&cmd, "EXEC", ""); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - RETURN_FALSE; - } - efree(cmd); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + RETURN_FALSE; + } + efree(cmd); - if (redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) { + if (redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) { zval_dtor(return_value); free_reply_callbacks(object, redis_sock); redis_sock->mode = ATOMIC; redis_sock->watching = 0; - RETURN_FALSE; - } + RETURN_FALSE; + } free_reply_callbacks(object, redis_sock); - redis_sock->mode = ATOMIC; + redis_sock->mode = ATOMIC; redis_sock->watching = 0; - } + } - IF_PIPELINE() { + IF_PIPELINE() { - char *request = NULL; - int total = 0; - int offset = 0; + char *request = NULL; + int total = 0; + int offset = 0; /* compute the total request size */ - for(ri = redis_sock->pipeline_head; ri; ri = ri->next) { + for(ri = redis_sock->pipeline_head; ri; ri = ri->next) { total += ri->request_size; - } + } if(total) { - request = malloc(total); + request = malloc(total); } /* concatenate individual elements one by one in the target buffer */ - for(ri = redis_sock->pipeline_head; ri; ri = ri->next) { - memcpy(request + offset, ri->request_str, ri->request_size); - offset += ri->request_size; - } + for(ri = redis_sock->pipeline_head; ri; ri = ri->next) { + memcpy(request + offset, ri->request_str, ri->request_size); + offset += ri->request_size; + } - if(request != NULL) { - if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0) { - free(request); + if(request != NULL) { + if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0) { + free(request); free_reply_callbacks(object, redis_sock); redis_sock->mode = ATOMIC; - RETURN_FALSE; - } - free(request); - } else { + RETURN_FALSE; + } + free(request); + } else { redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); array_init(return_value); /* empty array when no command was run. */ return; } - if (redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) { - redis_sock->mode = ATOMIC; + if (redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) { + redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); - RETURN_FALSE; - } - redis_sock->mode = ATOMIC; + RETURN_FALSE; + } + redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); - } + } } PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { @@ -2532,17 +2534,17 @@ PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { } PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) { - item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC); + item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC); } -PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, int numElems) +PHPAPI int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, int numElems) { fold_item *head = redis_sock->head; fold_item *current = redis_sock->current; for(current = head; current; current = current->next) { - fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, z_tab); + fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, z_tab); } redis_sock->current = current; return 0; @@ -2551,9 +2553,9 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_P PHP_METHOD(Redis, pipeline) { RedisSock *redis_sock; - zval *object; + zval *object; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } @@ -2562,16 +2564,16 @@ PHP_METHOD(Redis, pipeline) if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - redis_sock->mode = PIPELINE; + redis_sock->mode = PIPELINE; - /* - NB : we keep the function fold, to detect the last function. - We need the response format of the n - 1 command. So, we can delete when n > 2, the { 1 .. n - 2} commands - */ + /* + NB : we keep the function fold, to detect the last function. + We need the response format of the n - 1 command. So, we can delete when n > 2, the { 1 .. n - 2} commands + */ free_reply_callbacks(getThis(), redis_sock); - RETURN_ZVAL(getThis(), 1, 0); + RETURN_ZVAL(getThis(), 1, 0); } /* {{{ proto long Redis::publish(string channel, string msg) */ @@ -2583,25 +2585,25 @@ PHP_METHOD(Redis, publish) PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd) { - zval *object, *array, **data; + zval *object, *array, **data; HashTable *arr_hash; HashPosition pointer; RedisSock *redis_sock; char *cmd = "", *old_cmd = NULL, *key; int cmd_len, array_count, key_len, key_free; - zval *z_tab, **tmp; - char *type_response; + zval *z_tab, **tmp; + char *type_response; - /* Function call information */ - zend_fcall_info z_callback; - zend_fcall_info_cache z_callback_cache; + // Function call information + zend_fcall_info z_callback; + zend_fcall_info_cache z_callback_cache; - zval *z_ret, **z_args[4]; + zval *z_ret, **z_args[4]; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oaf", - &object, redis_ce, &array, &z_callback, &z_callback_cache) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oaf", + &object, redis_ce, &array, &z_callback, &z_callback_cache) == FAILURE) { + RETURN_FALSE; + } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -2639,7 +2641,7 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub /* Free our key if it was prefixed */ if(key_free) { - efree(key); + efree(key); } } } @@ -2653,12 +2655,12 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub } efree(cmd); - /* read the status of the execution of the command `subscribe` */ + /* read the status of the execution of the command `subscribe` */ z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - if(z_tab == NULL) { - RETURN_FALSE; - } + if(z_tab == NULL) { + RETURN_FALSE; + } if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&tmp) == SUCCESS) { type_response = Z_STRVAL_PP(tmp); @@ -2694,9 +2696,9 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub break; } - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&type) == FAILURE || Z_TYPE_PP(type) != IS_STRING) { - break; - } + if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&type) == FAILURE || Z_TYPE_PP(type) != IS_STRING) { + break; + } /* Make sure we have a message or pmessage */ if(!strncmp(Z_STRVAL_PP(type), "message", 7) || !strncmp(Z_STRVAL_PP(type), "pmessage", 8)) { @@ -2743,9 +2745,8 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub break; } - /* Free reply from Redis */ - zval_dtor(z_tab); - efree(z_tab); + // If we have a return value, free it. Note, we could use the return value to break the subscribe loop + if(z_ret) zval_ptr_dtor(&z_ret); /* Check for a non-null return value. If we have one, return it from * the subscribe function itself. Otherwise continue our loop. */ @@ -2763,24 +2764,24 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub */ PHP_METHOD(Redis, psubscribe) { - generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "psubscribe"); + generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "psubscribe"); } /* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */ PHP_METHOD(Redis, subscribe) { - generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "subscribe"); + generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "subscribe"); } /** - * [p]unsubscribe channel_0 channel_1 ... channel_n + * [p]unsubscribe channel_0 channel_1 ... channel_n * [p]unsubscribe(array(channel_0, channel_1, ..., channel_n)) * response format : * array( - * channel_0 => TRUE|FALSE, - * channel_1 => TRUE|FALSE, - * ... - * channel_n => TRUE|FALSE + * channel_0 => TRUE|FALSE, + * channel_1 => TRUE|FALSE, + * ... + * channel_n => TRUE|FALSE * ); **/ @@ -2793,13 +2794,13 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u char *cmd = "", *old_cmd = NULL; int cmd_len, array_count; - int i; - zval *z_tab, **z_channel; + int i; + zval *z_tab, **z_channel; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", - &object, redis_ce, &array) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + &object, redis_ce, &array) == FAILURE) { + RETURN_FALSE; + } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } @@ -2838,11 +2839,11 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u } efree(cmd); - i = 1; - array_init(return_value); + i = 1; + array_init(return_value); - while( i <= array_count) { - z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + while( i <= array_count) { + z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); if(Z_TYPE_P(z_tab) == IS_ARRAY) { if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) == FAILURE) { @@ -2861,12 +2862,12 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u PHP_METHOD(Redis, unsubscribe) { - generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNSUBSCRIBE"); + generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNSUBSCRIBE"); } PHP_METHOD(Redis, punsubscribe) { - generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUNSUBSCRIBE"); + generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUNSUBSCRIBE"); } /* {{{ proto string Redis::bgrewriteaof() @@ -2888,10 +2889,10 @@ PHP_METHOD(Redis, slaveof) int cmd_len, host_len; long port = 6379; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sl", - &object, redis_ce, &host, &host_len, &port) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sl", + &object, redis_ce, &host, &host_len, &port) == FAILURE) { + RETURN_FALSE; + } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } @@ -2904,9 +2905,9 @@ PHP_METHOD(Redis, slaveof) 2, "ONE", 3); } - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_boolean_response); } @@ -2953,10 +2954,10 @@ PHP_METHOD(Redis, getOption) { zval *object; long option; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", - &object, redis_ce, &option) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, redis_ce, &option) == FAILURE) { + RETURN_FALSE; + } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -2986,14 +2987,14 @@ PHP_METHOD(Redis, setOption) { RedisSock *redis_sock; zval *object; long option, val_long; - char *val_str; - int val_len; + char *val_str; + int val_len; struct timeval read_tv; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", - &object, redis_ce, &option, &val_str, &val_len) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", + &object, redis_ce, &option, &val_str, &val_len) == FAILURE) { + RETURN_FALSE; + } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -3013,19 +3014,19 @@ PHP_METHOD(Redis, setOption) { RETURN_FALSE; } break; - case REDIS_OPT_PREFIX: - if(redis_sock->prefix) { - efree(redis_sock->prefix); - } - if(val_len == 0) { - redis_sock->prefix = NULL; - redis_sock->prefix_len = 0; - } else { - redis_sock->prefix_len = val_len; - redis_sock->prefix = ecalloc(1+val_len, 1); - memcpy(redis_sock->prefix, val_str, val_len); - } - RETURN_TRUE; + case REDIS_OPT_PREFIX: + if(redis_sock->prefix) { + efree(redis_sock->prefix); + } + if(val_len == 0) { + redis_sock->prefix = NULL; + redis_sock->prefix_len = 0; + } else { + redis_sock->prefix_len = val_len; + redis_sock->prefix = ecalloc(1+val_len, 1); + memcpy(redis_sock->prefix, val_str, val_len); + } + RETURN_TRUE; case REDIS_OPT_READ_TIMEOUT: redis_sock->read_timeout = atof(val_str); if(redis_sock->stream) { @@ -3056,7 +3057,7 @@ PHP_METHOD(Redis, config) RedisSock *redis_sock; char *key = NULL, *val = NULL, *cmd, *op = NULL; int key_len, val_len, cmd_len, op_len; - enum {CFG_GET, CFG_SET} mode; + enum {CFG_GET, CFG_SET} mode; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss|s", &object, redis_ce, &op, &op_len, &key, &key_len, @@ -3064,14 +3065,14 @@ PHP_METHOD(Redis, config) RETURN_FALSE; } - /* op must be GET or SET */ - if(strncasecmp(op, "GET", 3) == 0) { - mode = CFG_GET; - } else if(strncasecmp(op, "SET", 3) == 0) { - mode = CFG_SET; - } else { - RETURN_FALSE; - } + /* op must be GET or SET */ + if(strncasecmp(op, "GET", 3) == 0) { + mode = CFG_GET; + } else if(strncasecmp(op, "SET", 3) == 0) { + mode = CFG_SET; + } else { + RETURN_FALSE; + } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -3091,14 +3092,14 @@ PHP_METHOD(Redis, config) cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) + IF_ATOMIC() { + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_boolean_response); } else { - RETURN_FALSE; - } + RETURN_FALSE; + } } /* }}} */ @@ -3407,11 +3408,11 @@ PHP_METHOD(Redis, pubsub) { /* Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter */ PHP_REDIS_API int redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) { - zval **elem; - HashTable *args_hash; - HashPosition hash_pos; - int cmd_len, args_count = 0; - int eval_cmd_count = 2; + zval **elem; + HashTable *args_hash; + HashPosition hash_pos; + int cmd_len, args_count = 0; + int eval_cmd_count = 2; /* If we've been provided arguments, we'll want to include those in our eval command */ if(args != NULL) { @@ -3447,9 +3448,9 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val zval_copy_ctor(z_tmp); convert_to_string(z_tmp); - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); - } + key = Z_STRVAL_P(z_tmp); + key_len = Z_STRLEN_P(z_tmp); + } /* Keep track of the old command pointer */ old_cmd = *ret; @@ -3477,7 +3478,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val if(args_count < 1) { cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, val_len, 0); - } + } /* Return our command length */ return cmd_len; @@ -3487,16 +3488,16 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val */ PHP_METHOD(Redis, evalsha) { - zval *object, *args= NULL; - char *cmd, *sha; - int cmd_len, sha_len; - long keys_count = 0; - RedisSock *redis_sock; + zval *object, *args= NULL; + char *cmd, *sha; + int cmd_len, sha_len; + long keys_count = 0; + RedisSock *redis_sock; - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|al", - &object, redis_ce, &sha, &sha_len, &args, &keys_count) == FAILURE) { - RETURN_FALSE; - } + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|al", + &object, redis_ce, &sha, &sha_len, &args, &keys_count) == FAILURE) { + RETURN_FALSE; + } /* Attempt to grab socket */ if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { @@ -3506,24 +3507,24 @@ PHP_METHOD(Redis, evalsha) /* Construct our EVALSHA command */ cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVALSHA", sha, sha_len, args, keys_count TSRMLS_CC); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_read_variant_reply); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { + RETURN_FALSE; + } + } + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } /* {{{ proto variant Redis::eval(string script, [array keys, int num_key_args]) */ PHP_METHOD(Redis, eval) { - zval *object, *args = NULL; - RedisSock *redis_sock; - char *script, *cmd = ""; - int script_len, cmd_len; - long keys_count = 0; + zval *object, *args = NULL; + RedisSock *redis_sock; + char *script, *cmd = ""; + int script_len, cmd_len; + long keys_count = 0; /* Attempt to parse parameters */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|al", @@ -3541,9 +3542,9 @@ PHP_METHOD(Redis, eval) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { - RETURN_FALSE; - } + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { + RETURN_FALSE; + } } REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } @@ -3576,10 +3577,10 @@ redis_build_script_exists_cmd(char **ret, zval **argv, int argc) { * {{{ proto int Reids::script('exists', script_sha1 [, script_sha2, ...]) */ PHP_METHOD(Redis, script) { - zval **z_args; - RedisSock *redis_sock; - int cmd_len, argc; - char *cmd; + zval **z_args; + RedisSock *redis_sock; + int cmd_len, argc; + char *cmd; /* Attempt to grab our socket */ if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { @@ -3615,8 +3616,8 @@ PHP_METHOD(Redis, script) { RETURN_FALSE; } - // Format our SCRIPT LOAD command - cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "ss", + // Format our SCRIPT LOAD command + cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "ss", "LOAD", 4, Z_STRVAL_P(z_args[1]), Z_STRLEN_P(z_args[1])); } else if(!strcasecmp(Z_STRVAL_P(z_args[0]), "exists")) { @@ -3690,27 +3691,27 @@ PHP_METHOD(Redis, restore) { * {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, bool replace]) */ PHP_METHOD(Redis, migrate) { - zval *object; - RedisSock *redis_sock; - char *cmd, *host, *key; - int cmd_len, host_len, key_len, key_free; + zval *object; + RedisSock *redis_sock; + char *cmd, *host, *key; + int cmd_len, host_len, key_len, key_free; zend_bool copy=0, replace=0; - long port, dest_db, timeout; + long port, dest_db, timeout; /* Parse arguments */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll|bb", &object, redis_ce, &host, &host_len, &port, &key, &key_len, &dest_db, &timeout, ©, &replace) == FAILURE) { - RETURN_FALSE; - } + RETURN_FALSE; + } /* Grabg our socket */ if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - // Prefix our key if we need to, build our command - key_free = redis_key_prefix(redis_sock, &key, &key_len); + // Prefix our key if we need to, build our command + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Construct our command */ if(copy && replace) { @@ -3750,10 +3751,10 @@ PHP_METHOD(Redis, migrate) { * {{{ proto Redis::_prefix(key) */ PHP_METHOD(Redis, _prefix) { - zval *object; - RedisSock *redis_sock; - char *key; - int key_len; + zval *object; + RedisSock *redis_sock; + char *key; + int key_len; /* Parse our arguments */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce, @@ -3808,10 +3809,10 @@ PHP_METHOD(Redis, _serialize) { * {{{ proto Redis::_unserialize(value) */ PHP_METHOD(Redis, _unserialize) { - zval *object; - RedisSock *redis_sock; - char *value; - int value_len; + zval *object; + RedisSock *redis_sock; + char *value; + int value_len; /* Parse our arguments */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce, @@ -3829,8 +3830,8 @@ PHP_METHOD(Redis, _unserialize) { if(redis_unserialize(redis_sock, value, value_len, &z_ret TSRMLS_CC) == 0) { - // Badly formed input, throw an execption - zend_throw_exception(redis_exception_ce, + // Badly formed input, throw an execption + zend_throw_exception(redis_exception_ce, "Invalid serialized data, or unserialization error", 0 TSRMLS_CC); RETURN_FALSE; @@ -3846,8 +3847,8 @@ PHP_METHOD(Redis, _unserialize) { * {{{ proto Redis::getLastError() */ PHP_METHOD(Redis, getLastError) { - zval *object; - RedisSock *redis_sock; + zval *object; + RedisSock *redis_sock; /* Grab our object */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { @@ -3870,8 +3871,8 @@ PHP_METHOD(Redis, getLastError) { * {{{ proto Redis::clearLastError() */ PHP_METHOD(Redis, clearLastError) { - zval *object; - RedisSock *redis_sock; + zval *object; + RedisSock *redis_sock; /* Grab our object */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { @@ -3888,7 +3889,7 @@ PHP_METHOD(Redis, clearLastError) { } redis_sock->err = NULL; - RETURN_TRUE; + RETURN_TRUE; } /* @@ -3915,10 +3916,10 @@ PHP_METHOD(Redis, getMode) { * {{{ proto Redis::time() */ PHP_METHOD(Redis, time) { - zval *object; - RedisSock *redis_sock; - char *cmd; - int cmd_len; + zval *object; + RedisSock *redis_sock; + char *cmd; + int cmd_len; /* Grab our object */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { @@ -4136,7 +4137,7 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, keyword = "HSCAN"; break; case TYPE_ZSCAN: - default: + default: keyword = "ZSCAN"; break; } From 110ba117d7a94ad4a5a10362a215b3fda68f0a09 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 10:57:42 -0700 Subject: [PATCH 0317/1986] 80 chars is the future :) --- redis.c | 935 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 541 insertions(+), 394 deletions(-) diff --git a/redis.c b/redis.c index 7fae8d2c62..2c06cb4d1d 100644 --- a/redis.c +++ b/redis.c @@ -384,28 +384,27 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { int result = FAILURE; char *cmd, *resp; - int resp_len, cmd_len; + int resp_len, cmd_len; - /* format our discard command */ - cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); + /* format our discard command */ + cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); - /* send our DISCARD command */ - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && - (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) + /* send our DISCARD command */ + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && + (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) { + /* success if we get OK */ + result = (resp_len == 3 && strncmp(resp,"+OK", 3)==0) ? SUCCESS:FAILURE; - /* success if we get OK */ - result = (resp_len == 3 && strncmp(resp,"+OK", 3)==0) ? SUCCESS:FAILURE; - - /* free our response */ - efree(resp); - } + /* free our response */ + efree(resp); + } - /* free our command */ - efree(cmd); + /* free our command */ + efree(cmd); - /* return success/failure */ - return result; + /* return success/failure */ + return result; } /** @@ -417,6 +416,7 @@ static void redis_destructor_redis_sock(zend_rsrc_list_entry * rsrc TSRMLS_DC) redis_sock_disconnect(redis_sock TSRMLS_CC); redis_free_socket(redis_sock); } + /** * redis_sock_get */ @@ -427,22 +427,26 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int int resource_type; if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "socket", - sizeof("socket"), (void **) &socket) == FAILURE) { - /* Throw an exception unless we've been requested not to */ + sizeof("socket"), (void **) &socket) == FAILURE) + { + // Throw an exception unless we've been requested not to if(!no_throw) { - zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "Redis server went away", + 0 TSRMLS_CC); } return -1; } - *redis_sock = (RedisSock *) zend_list_find(Z_LVAL_PP(socket), &resource_type); + *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_PP(socket), + &resource_type); if (!*redis_sock || resource_type != le_redis_sock) { - /* Throw an exception unless we've been requested not to */ - if(!no_throw) { - zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); - } - return -1; + // Throw an exception unless we've been requested not to + if(!no_throw) { + zend_throw_exception(redis_exception_ce, "Redis server went away", + 0 TSRMLS_CC); + } + return -1; } if ((*redis_sock)->lazy_connect) { @@ -463,9 +467,11 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) zval *object; RedisSock *redis_sock; - /* If we can't grab our object, or get a socket, or we're not connected, return NULL */ - if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) || - (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) + // If we can't grab our object, or get a socket, or we're not connected, return NULL + if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_ce) == FAILURE) || + (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || + redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { return NULL; } @@ -530,12 +536,16 @@ PHP_MINIT_FUNCTION(redis) redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC); /* RedisArray class */ - INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions); - redis_array_ce = zend_register_internal_class(&redis_array_class_entry TSRMLS_CC); + INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", + redis_array_functions); + redis_array_ce = zend_register_internal_class(&redis_array_class_entry + TSRMLS_CC); /* RedisCluster class */ - INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_functions); - redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry TSRMLS_CC); + INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", + redis_cluster_functions); + redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry + TSRMLS_CC); redis_cluster_ce->create_object = create_cluster_context; le_redis_array = zend_register_list_destructors_ex( @@ -635,7 +645,8 @@ PHP_METHOD(Redis,__destruct) { // If we think we're in MULTI mode, send a discard if(redis_sock->mode == MULTI) { - // Discard any multi commands, and free any callbacks that have been queued + // Discard any multi commands, and free any callbacks that have been + // queued send_discard_static(redis_sock TSRMLS_CC); free_reply_callbacks(getThis(), redis_sock); } @@ -701,16 +712,19 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { } if (timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid timeout", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "Invalid timeout", + 0 TSRMLS_CC); return FAILURE; } if (retry_interval < 0L || retry_interval > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "Invalid retry interval", + 0 TSRMLS_CC); return FAILURE; } - if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */ + /* If it's not a unix socket, set to default */ + if(port == -1 && host_len && host[0] != '/') { port = 6379; } @@ -721,11 +735,13 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { { /* maybe there is a socket but the id isn't known.. what to do? */ } else { - zend_list_delete(Z_LVAL_PP(socket)); /* the refcount should be decreased and the destructor called */ + /* The refcount should be decreased and destructor invoked */ + zend_list_delete(Z_LVAL_PP(socket)); } } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, + persistent_id, retry_interval, 0); if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { redis_free_socket(redis_sock); @@ -757,7 +773,7 @@ PHP_METHOD(Redis, bitcount) } /* }}} */ -/* {{{ proto integer Redis::bitpos(string key, int bit, [int start], [int end]) */ +/* {{{ proto integer Redis::bitpos(string key, int bit, [int start, int end]) */ PHP_METHOD(Redis, bitpos) { REDIS_PROCESS_CMD(bitpos, redis_long_response); @@ -1201,7 +1217,8 @@ PHP_METHOD(Redis, lRemove) */ PHP_METHOD(Redis, listTrim) { - REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, redis_boolean_response); + REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, + redis_boolean_response); } /* }}} */ @@ -1323,7 +1340,7 @@ PHP_METHOD(Redis, sInter) { } /* }}} */ -/* {{{ proto array Redis::sInterStore(string destination, string key0, ... string keyN) +/* {{{ proto array Redis::sInterStore(string dst, string key0, ... string keyN) */ PHP_METHOD(Redis, sInterStore) { REDIS_PROCESS_CMD(sinterstore, redis_long_response); @@ -1336,7 +1353,7 @@ PHP_METHOD(Redis, sUnion) { REDIS_PROCESS_CMD(sunion, redis_sock_read_multibulk_reply); } /* }}} */ -/* {{{ proto array Redis::sUnionStore(string destination, string key0, ... string keyN) +/* {{{ proto array Redis::sUnionStore(string dst, string key0, ... string keyN) */ PHP_METHOD(Redis, sUnionStore) { REDIS_PROCESS_CMD(sunionstore, redis_long_response); @@ -1350,13 +1367,14 @@ PHP_METHOD(Redis, sDiff) { } /* }}} */ -/* {{{ proto array Redis::sDiffStore(string destination, string key0, ... string keyN) +/* {{{ proto array Redis::sDiffStore(string dst, string key0, ... string keyN) */ PHP_METHOD(Redis, sDiffStore) { REDIS_PROCESS_CMD(sdiffstore, redis_long_response); } /* }}} */ +/* {{{ proto array Redis::sort(string key, array options) */ PHP_METHOD(Redis, sort) { char *cmd; int cmd_len, have_store; @@ -1389,12 +1407,14 @@ PHP_METHOD(Redis, sort) { } } -PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha) { +PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, + int use_alpha) +{ zval *object; RedisSock *redis_sock; char *key = NULL, *pattern = NULL, *get = NULL, *store = NULL, *cmd; - int key_len, pattern_len = -1, get_len = -1, store_len = -1, cmd_len, key_free; + int key_len, pattern_len=-1, get_len=-1, store_len=-1, cmd_len, key_free; long sort_start = -1, sort_count = -1; int cmd_elements; @@ -1405,10 +1425,12 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in int sort_len; int i, pos; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|sslls", - &object, redis_ce, - &key, &key_len, &pattern, &pattern_len, - &get, &get_len, &sort_start, &sort_count, &store, &store_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|sslls", &object, redis_ce, &key, + &key_len, &pattern, &pattern_len, &get, + &get_len, &sort_start, &sort_count, &store, + &store_len) == FAILURE) + { RETURN_FALSE; } @@ -1449,7 +1471,8 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in cmd_elements++; /* pattern */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", pattern_len); + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + "$%d", pattern_len); cmd_elements++; cmd_lines[cmd_elements] = emalloc(pattern_len + 1); memcpy(cmd_lines[cmd_elements], pattern, pattern_len); @@ -1467,15 +1490,19 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in cmd_elements++; /* start */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", integer_length(sort_start)); + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + "$%d", integer_length(sort_start)); cmd_elements++; - cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, "%d", (int)sort_start); + cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, + "%d", (int)sort_start); cmd_elements++; /* count */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", integer_length(sort_count)); + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + "$%d", integer_length(sort_count)); cmd_elements++; - cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, "%d", (int)sort_count); + cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, + "%d", (int)sort_count); cmd_elements++; } if(get && get_len) { @@ -1488,7 +1515,8 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in cmd_elements++; /* pattern */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", get_len); + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + "$%d", get_len); cmd_elements++; cmd_lines[cmd_elements] = emalloc(get_len + 1); memcpy(cmd_lines[cmd_elements], get, get_len); @@ -1499,7 +1527,8 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in /* add ASC or DESC */ sort_len = strlen(sort); - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", sort_len); + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", + sort_len); cmd_elements++; cmd_lines[cmd_elements] = emalloc(sort_len + 1); memcpy(cmd_lines[cmd_elements], sort, sort_len); @@ -1526,7 +1555,8 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in cmd_elements++; /* store key */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", store_len); + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + "$%d", store_len); cmd_elements++; cmd_lines[cmd_elements] = emalloc(store_len + 1); memcpy(cmd_lines[cmd_elements], store, store_len); @@ -1541,7 +1571,8 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in /* compute the command size */ cmd_len = 0; for(i = 0; i < cmd_elements; ++i) { - cmd_len += cmd_sizes[i] + sizeof(_NL) - 1; /* each line followeb by _NL */ + /* Each line followed by a _NL (\r\n) */ + cmd_len += cmd_sizes[i] + sizeof(_NL) - 1; } /* copy all lines into the final command. */ @@ -1568,7 +1599,8 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, in } -/* {{{ proto array Redis::sortAsc(string key, string pattern, string get, int start, int end, bool getList]) +/* {{{ proto array Redis::sortAsc(string key, string pattern, string get, + * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortAsc) { @@ -1576,7 +1608,8 @@ PHP_METHOD(Redis, sortAsc) } /* }}} */ -/* {{{ proto array Redis::sortAscAlpha(string key, string pattern, string get, int start, int end, bool getList]) +/* {{{ proto array Redis::sortAscAlpha(string key, string pattern, string get, + * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortAscAlpha) { @@ -1584,7 +1617,8 @@ PHP_METHOD(Redis, sortAscAlpha) } /* }}} */ -/* {{{ proto array Redis::sortDesc(string key, string pattern, string get, int start, int end, bool getList]) +/* {{{ proto array Redis::sortDesc(string key, string pattern, string get, + * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortDesc) { @@ -1592,7 +1626,8 @@ PHP_METHOD(Redis, sortDesc) } /* }}} */ -/* {{{ proto array Redis::sortDescAlpha(string key, string pattern, string get, int start, int end, bool getList]) +/* {{{ proto array Redis::sortDescAlpha(string key, string pattern, string get, + * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortDescAlpha) { @@ -1720,8 +1755,10 @@ PHP_METHOD(Redis, info) { char *cmd, *opt = NULL; int cmd_len, opt_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|s", - &object, redis_ce, &opt, &opt_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "O|s", &object, redis_ce, &opt, &opt_len) + == FAILURE) + { RETURN_FALSE; } @@ -1739,7 +1776,8 @@ PHP_METHOD(Redis, info) { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, + NULL); } REDIS_PROCESS_RESPONSE(redis_info_response); @@ -1772,7 +1810,8 @@ PHP_METHOD(Redis, select) { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_boolean_response); } @@ -1785,9 +1824,8 @@ PHP_METHOD(Redis, move) { } /* }}} */ -PHP_REDIS_API void -generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *, zval *, void *)) { - +PHPAPI void +generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { zval *object; RedisSock *redis_sock; @@ -1799,7 +1837,8 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI HashTable *keytable; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", - &object, redis_ce, &z_array) == FAILURE) { + &object, redis_ce, &z_array) == FAILURE) + { RETURN_FALSE; } @@ -1813,12 +1852,16 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI for(step = 0; step < 2; ++step) { if(step == 1) { - cmd_len += 1 + integer_length(1 + 2 * argc) + 2; /* star + arg count + NL */ - cmd_len += 1 + integer_length(kw_len) + 2; /* dollar + strlen(kw) + NL */ - cmd_len += kw_len + 2; /* kw + NL */ - - p = cmd = emalloc(cmd_len + 1); /* alloc */ - p += sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1 + 2 * argc, kw_len, kw); /* copy header */ + /* '*' + arg count + NL */ + cmd_len += 1 + integer_length(1 + 2 * argc) + 2; + /* '$' + strlen(kw) + NL */ + cmd_len += 1 + integer_length(kw_len) + 2; + /* kw + NL */ + cmd_len += kw_len + 2; + + p = cmd = emalloc(cmd_len + 1); + p += sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1 + 2 * argc, + kw_len, kw); } keytable = Z_ARRVAL_P(z_array); @@ -1835,26 +1878,32 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI int val_free, key_free; char buf[32]; - type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ + type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, + 0, NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) + == FAILURE) + { + /* Should never happen, according to the PHP people */ + continue; } - /* If the key isn't a string, use the index value returned when grabbing the */ - /* key. We typecast to long, because they could actually be negative. */ - if(type != HASH_KEY_IS_STRING) { - /* Create string representation of our index */ - key_len = snprintf(buf, sizeof(buf), "%ld", (long)idx); - key = (char*)buf; - } else if(key_len > 0) { - /* When not an integer key, the length will include the \0 */ - key_len--; - } + // If the key isn't a string, use the index value returned when + // grabbing it. We typecast to long, because they could actually + // be negative. + if(type != HASH_KEY_IS_STRING) { + // Create string representation of our index + key_len = snprintf(buf, sizeof(buf), "%ld", (long)idx); + key = (char*)buf; + } else if(key_len > 0) { + // When not an integer key, the length will include the \0 + key_len--; + } if(step == 0) argc++; /* found a valid arg */ - val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len + TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len); if(step == 0) { /* counting */ @@ -1888,7 +1937,8 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI /* {{{ proto bool Redis::mset(array (key => value, ...)) */ PHP_METHOD(Redis, mset) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response); + generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", + redis_boolean_response); } /* }}} */ @@ -1896,7 +1946,8 @@ PHP_METHOD(Redis, mset) { /* {{{ proto bool Redis::msetnx(array (key => value, ...)) */ PHP_METHOD(Redis, msetnx) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response); + generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", + redis_1_response); } /* }}} */ @@ -2191,7 +2242,11 @@ PHP_METHOD(Redis, hGetAll) { } /* }}} */ -PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int use_atof TSRMLS_DC) { +/* Turn an array in the form key1, value1, key2, value2 into the form + * key1=>value1, key2=>value2, optionally treating values as doubles. */ +PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, + int use_atof TSRMLS_DC) +{ zval *z_ret; HashTable *keytable; @@ -2210,9 +2265,11 @@ PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int unsigned long idx; zval **z_key_pp, **z_value_pp; - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); + zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, + 0, NULL); if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ + /* this should never happen, according to the PHP people */ + continue; } /* get current value, a key */ @@ -2224,9 +2281,11 @@ PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int zend_hash_move_forward(keytable); /* fetch again */ - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); + zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, + 0, NULL); if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ + /* this should never happen, according to the PHP people */ + continue; } /* get current value, a hash value now. */ @@ -2289,13 +2348,15 @@ PHP_METHOD(Redis, multi) zval *object; long multi_value = MULTI; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", - &object, redis_ce, &multi_value) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "O|l", &object, redis_ce, &multi_value) + == FAILURE) + { RETURN_FALSE; } - /* if the flag is activated, send the command, the reply will be "QUEUED" or -ERR */ - + /* if the flag is activated, send the command, the reply will be "QUEUED" + * or -ERR */ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } @@ -2317,7 +2378,9 @@ PHP_METHOD(Redis, multi) } efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) + { RETURN_FALSE; } @@ -2353,14 +2416,16 @@ PHP_METHOD(Redis, discard) redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } -PHP_REDIS_API int redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) +PHPAPI int +redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock) { zval *z_tab; MAKE_STD_ZVAL(z_tab); array_init(z_tab); redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, z_tab, 0); + redis_sock, z_tab, 0); *return_value = *z_tab; efree(z_tab); @@ -2461,7 +2526,9 @@ PHP_METHOD(Redis, exec) } efree(cmd); - if (redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) { + if(redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock) < 0) + { zval_dtor(return_value); free_reply_callbacks(object, redis_sock); redis_sock->mode = ATOMIC; @@ -2504,11 +2571,15 @@ PHP_METHOD(Redis, exec) } else { redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); - array_init(return_value); /* empty array when no command was run. */ + + /* Empty array when no command was run. */ + array_init(return_value); return; } - if (redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) { + if (redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock) < 0) + { redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); RETURN_FALSE; @@ -2533,18 +2604,22 @@ PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { return ret; } -PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) { +PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, + RedisSock *redis_sock, zval *z_tab) +{ item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC); } PHPAPI int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, int numElems) + RedisSock *redis_sock, + zval *z_tab, int numElems) { fold_item *head = redis_sock->head; fold_item *current = redis_sock->current; for(current = head; current; current = current->next) { - fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, z_tab); + fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, + z_tab); } redis_sock->current = current; return 0; @@ -2560,17 +2635,16 @@ PHP_METHOD(Redis, pipeline) RETURN_FALSE; } - /* if the flag is activated, send the command, the reply will be "QUEUED" or -ERR */ + /* if the flag is activated, send the command, the reply will be "QUEUED" + * or -ERR */ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } redis_sock->mode = PIPELINE; - /* - NB : we keep the function fold, to detect the last function. - We need the response format of the n - 1 command. So, we can delete when n > 2, the { 1 .. n - 2} commands - */ - + /* NB : we keep the function fold, to detect the last function. + * We need the response format of the n - 1 command. So, we can delete + * when n > 2, the { 1 .. n - 2} commands */ free_reply_callbacks(getThis(), redis_sock); RETURN_ZVAL(getThis(), 1, 0); @@ -2601,7 +2675,9 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub zval *z_ret, **z_args[4]; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oaf", - &object, redis_ce, &array, &z_callback, &z_callback_cache) == FAILURE) { + &object, redis_ce, &array, &z_callback, + &z_callback_cache) == FAILURE) + { RETURN_FALSE; } @@ -2656,8 +2732,8 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub efree(cmd); /* read the status of the execution of the command `subscribe` */ - - z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock); if(z_tab == NULL) { RETURN_FALSE; } @@ -2683,46 +2759,57 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub z_callback.params = z_args; z_callback.no_separation = 0; - /* Multibulk Response, format : {message type, originating channel, message payload} */ - while(1) { - /* call the callback with this z_tab in argument */ - int is_pmsg, tab_idx = 1; - zval **type, **channel, **pattern, **data; - z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - int is_pmsg, tab_idx = 1; + /* Multibulk Response, format : {message type, originating channel, message + * payload} */ + while(1) { + /* call the callback with this z_tab in argument */ + zval **type, **channel, **pattern, **data; + z_tab = redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + int is_pmsg, tab_idx = 1; if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) { /*ERROR */ break; } - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&type) == FAILURE || Z_TYPE_PP(type) != IS_STRING) { + if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&type) == FAILURE + || Z_TYPE_PP(type) != IS_STRING) + { break; } - /* Make sure we have a message or pmessage */ - if(!strncmp(Z_STRVAL_PP(type), "message", 7) || !strncmp(Z_STRVAL_PP(type), "pmessage", 8)) { - /* Is this a pmessage */ - is_pmsg = *Z_STRVAL_PP(type) == 'p'; - } else { - continue; /* It's not a message or pmessage */ - } + // Make sure we have a message or pmessage + if(!strncmp(Z_STRVAL_PP(type), "message", 7) || + !strncmp(Z_STRVAL_PP(type), "pmessage", 8)) + { + // Is this a pmessage + is_pmsg = *Z_STRVAL_PP(type) == 'p'; + } else { + continue; // It's not a message or pmessage + } - /* If this is a pmessage, we'll want to extract the pattern first */ - if(is_pmsg) { - /* Extract pattern */ - if(zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&pattern) == FAILURE) { - break; - } - } + // If this is a pmessage, we'll want to extract the pattern first + if(is_pmsg) { + // Extract pattern + if(zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, + (void**)&pattern) == FAILURE) + { + break; + } + } - /* Extract channel and data */ - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&channel) == FAILURE) { - break; - } - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&data) == FAILURE) { - break; - } + // Extract channel and data + if (zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&channel) + == FAILURE) + { + break; + } + if (zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&data) + == FAILURE) + { + break; + } /* Always pass the Redis object through */ z_args[0] = &getThis(); @@ -2740,12 +2827,15 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub /* Set our argument information */ z_callback.param_count = tab_idx; - /* Break if we can't call the function */ - if(zend_call_function(&z_callback, &z_callback_cache TSRMLS_CC) != SUCCESS) { - break; - } + // Break if we can't call the function + if(zend_call_function(&z_callback, &z_callback_cache TSRMLS_CC) + != SUCCESS) + { + break; + } - // If we have a return value, free it. Note, we could use the return value to break the subscribe loop + // If we have a return value, free it. Note, we could use the return + // value to break the subscribe loop if(z_ret) zval_ptr_dtor(&z_ret); /* Check for a non-null return value. If we have one, return it from @@ -2774,7 +2864,7 @@ PHP_METHOD(Redis, subscribe) { } /** - * [p]unsubscribe channel_0 channel_1 ... channel_n + * [p]unsubscribe channel_0 channel_1 ... channel_n * [p]unsubscribe(array(channel_0, channel_1, ..., channel_n)) * response format : * array( @@ -2785,7 +2875,8 @@ PHP_METHOD(Redis, subscribe) { * ); **/ -PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd) +PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, + char *unsub_cmd) { zval *object, *array, **data; HashTable *arr_hash; @@ -2843,21 +2934,24 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *u array_init(return_value); while( i <= array_count) { - z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock); - if(Z_TYPE_P(z_tab) == IS_ARRAY) { - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) == FAILURE) { - RETURN_FALSE; - } - add_assoc_bool(return_value, Z_STRVAL_PP(z_channel), 1); - } else { - /*error */ - efree(z_tab); - RETURN_FALSE; - } - efree(z_tab); - i ++; - } + if(Z_TYPE_P(z_tab) == IS_ARRAY) { + if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) + == FAILURE) + { + RETURN_FALSE; + } + add_assoc_bool(return_value, Z_STRVAL_PP(z_channel), 1); + } else { + //error + efree(z_tab); + RETURN_FALSE; + } + efree(z_tab); + i ++; + } } PHP_METHOD(Redis, unsubscribe) @@ -2889,8 +2983,10 @@ PHP_METHOD(Redis, slaveof) int cmd_len, host_len; long port = 6379; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sl", - &object, redis_ce, &host, &host_len, &port) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "O|sl", &object, redis_ce, &host, + &host_len, &port) == FAILURE) + { RETURN_FALSE; } if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { @@ -2907,7 +3003,8 @@ PHP_METHOD(Redis, slaveof) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_boolean_response); } @@ -2991,8 +3088,10 @@ PHP_METHOD(Redis, setOption) { int val_len; struct timeval read_tv; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", - &object, redis_ce, &option, &val_str, &val_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Ols", &object, redis_ce, &option, + &val_str, &val_len) == FAILURE) + { RETURN_FALSE; } @@ -3031,13 +3130,16 @@ PHP_METHOD(Redis, setOption) { redis_sock->read_timeout = atof(val_str); if(redis_sock->stream) { read_tv.tv_sec = (time_t)redis_sock->read_timeout; - read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); - php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT,0, &read_tv); + read_tv.tv_usec = (int)((redis_sock->read_timeout - + read_tv.tv_sec) * 1000000); + php_stream_set_option(redis_sock->stream, + PHP_STREAM_OPTION_READ_TIMEOUT, 0, + &read_tv); } RETURN_TRUE; case REDIS_OPT_SCAN: val_long = atol(val_str); - if(val_long == REDIS_SCAN_NORETRY || val_long == REDIS_SCAN_RETRY) { + if(val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) { redis_sock->scan = val_long; RETURN_TRUE; } @@ -3059,9 +3161,10 @@ PHP_METHOD(Redis, config) int key_len, val_len, cmd_len, op_len; enum {CFG_GET, CFG_SET} mode; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss|s", - &object, redis_ce, &op, &op_len, &key, &key_len, - &val, &val_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Oss|s", &object, redis_ce, &op, &op_len, + &key, &key_len, &val, &val_len) == FAILURE) + { RETURN_FALSE; } @@ -3079,14 +3182,15 @@ PHP_METHOD(Redis, config) } if (mode == CFG_GET && val == NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, - op_len, key, key_len); + cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, key, + key_len); - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) - IF_ATOMIC() { - redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) + IF_ATOMIC() { + redis_sock_read_multibulk_reply_zipped_strings( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings); } else if(mode == CFG_SET && val != NULL) { cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, @@ -3094,7 +3198,8 @@ PHP_METHOD(Redis, config) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_boolean_response( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_boolean_response); } else { @@ -3114,9 +3219,10 @@ PHP_METHOD(Redis, slowlog) { long option; enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; - /* Make sure we can get parameters */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", - &object, redis_ce, &arg, &arg_len, &option) == FAILURE) + // Make sure we can get parameters + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|l", &object, redis_ce, &arg, &arg_len, + &option) == FAILURE) { RETURN_FALSE; } @@ -3138,7 +3244,8 @@ PHP_METHOD(Redis, slowlog) { RETURN_FALSE; } - /* Create our command. For everything except SLOWLOG GET (with an arg) it's just two parts */ + // Create our command. For everything except SLOWLOG GET (with an arg) it's + // just two parts if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) { cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl", arg, arg_len, option); @@ -3150,7 +3257,9 @@ PHP_METHOD(Redis, slowlog) { /* Kick off our command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL) < 0) + { RETURN_FALSE; } } @@ -3234,7 +3343,8 @@ PHP_METHOD(Redis, wait) { /* Kick it off */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, + NULL); } REDIS_PROCESS_RESPONSE(redis_long_response); } @@ -3264,8 +3374,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, // With a pattern cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", - "CHANNELS", sizeof("CHANNELS")-1, - key, key_len); + "CHANNELS", sizeof("CHANNELS")-1, key, key_len); /* Free the channel name if we prefixed it */ if(key_free) efree(key); @@ -3275,18 +3384,19 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, } else { // No pattern return redis_cmd_format_static(ret, "PUBSUB", "s", - "CHANNELS", sizeof("CHANNELS")-1); + "CHANNELS", sizeof("CHANNELS")-1); } } else if(type == PUBSUB_NUMSUB) { ht_chan = Z_ARRVAL_P(arg); - /* Add PUBSUB and NUMSUB bits */ - redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1); + // Add PUBSUB and NUMSUB bits + redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", + sizeof("PUBSUB")-1); redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1); /* Iterate our elements */ for(zend_hash_internal_pointer_reset_ex(ht_chan, &ptr); - zend_hash_get_current_data_ex(ht_chan, (void**)&z_ele, &ptr)==SUCCESS; + zend_hash_get_current_data_ex(ht_chan,(void**)&z_ele,&ptr)==SUCCESS; zend_hash_move_forward_ex(ht_chan, &ptr)) { char *key; @@ -3315,7 +3425,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Free key if prefixed */ if(key_free) efree(key); - /* Free our temp var if we converted from something other than a string */ + // Free our temp var if we used it if(z_tmp) { zval_dtor(z_tmp); efree(z_tmp); @@ -3328,7 +3438,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, return cmd.len; } else if(type == PUBSUB_NUMPAT) { return redis_cmd_format_static(ret, "PUBSUB", "s", "NUMPAT", - sizeof("NUMPAT")-1); + sizeof("NUMPAT")-1); } /* Shouldn't ever happen */ @@ -3348,10 +3458,10 @@ PHP_METHOD(Redis, pubsub) { PUBSUB_TYPE type; zval *arg=NULL; - /* Parse arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z", - &object, redis_ce, &keyword, &kw_len, &arg) - ==FAILURE) + // Parse arguments + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|z", &object, redis_ce, &keyword, + &kw_len, &arg)==FAILURE) { RETURN_FALSE; } @@ -3390,14 +3500,18 @@ PHP_METHOD(Redis, pubsub) { if(type == PUBSUB_NUMSUB) { IF_ATOMIC() { - if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) { + if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL)<0) + { RETURN_FALSE; } } REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_int); } else { IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL)<0) { + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL)<0) + { RETURN_FALSE; } } @@ -3405,38 +3519,44 @@ PHP_METHOD(Redis, pubsub) { } } -/* Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter */ -PHP_REDIS_API int -redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) { +// Construct an EVAL or EVALSHA command, with option argument array and number +// of arguments that are keys parameter +PHPAPI int +redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, + char *value, int val_len, zval *args, int keys_count + TSRMLS_DC) +{ zval **elem; HashTable *args_hash; HashPosition hash_pos; int cmd_len, args_count = 0; int eval_cmd_count = 2; - /* If we've been provided arguments, we'll want to include those in our eval command */ - if(args != NULL) { - /* Init our hash array value, and grab the count */ - args_hash = Z_ARRVAL_P(args); - args_count = zend_hash_num_elements(args_hash); + // If we've been provided arguments, we'll want to include those in our eval + // command + if(args != NULL) { + // Init our hash array value, and grab the count + args_hash = Z_ARRVAL_P(args); + args_count = zend_hash_num_elements(args_hash); /* We only need to process the arguments if the array is non empty */ if(args_count > 0) { /* Header for our EVAL command */ cmd_len = redis_cmd_format_header(ret, keyword, eval_cmd_count + args_count); - /* Now append the script itself, and the number of arguments to treat as keys */ - cmd_len = redis_cmd_append_str(ret, cmd_len, value, val_len); - cmd_len = redis_cmd_append_int(ret, cmd_len, keys_count); + // Now append the script itself, and the number of arguments to + // treat as keys + cmd_len = redis_cmd_append_str(ret, cmd_len, value, val_len); + cmd_len = redis_cmd_append_int(ret, cmd_len, keys_count); - /* Iterate the values in our "keys" array */ - for(zend_hash_internal_pointer_reset_ex(args_hash, &hash_pos); - zend_hash_get_current_data_ex(args_hash, (void **)&elem, &hash_pos) == SUCCESS; - zend_hash_move_forward_ex(args_hash, &hash_pos)) - { - zval *z_tmp = NULL; - char *key, *old_cmd; - int key_len, key_free; + // Iterate the values in our "keys" array + for(zend_hash_internal_pointer_reset_ex(args_hash, &hash_pos); + zend_hash_get_current_data_ex(args_hash,(void **)&elem,&hash_pos) == SUCCESS; + zend_hash_move_forward_ex(args_hash, &hash_pos)) + { + zval *z_tmp = NULL; + char *key, *old_cmd; + int key_len, key_free; if(Z_TYPE_PP(elem) == IS_STRING) { key = Z_STRVAL_PP(elem); @@ -3455,28 +3575,31 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val /* Keep track of the old command pointer */ old_cmd = *ret; - /* If this is still a key argument, prefix it if we've been set up to prefix keys */ - key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len) : 0; + // If this is still a key argument, prefix it if we've been set + // up to prefix keys + key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len) : 0; - /* Append this key to our EVAL command, free our old command */ - cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, cmd_len, key_len, key, key_len); - efree(old_cmd); + // Append this key to our EVAL command, free our old command + cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, + cmd_len, key_len, key, key_len); + efree(old_cmd); /* Free our key, old command if we need to */ if(key_free) efree(key); - /* Free our temporary zval (converted from non string) if we've got one */ - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - } - } - } - } + // Free our temporary arg if we created one + if(z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + } + } + } + } - /* If there weren't any arguments (none passed, or an empty array), construct a standard no args command */ - if(args_count < 1) { - cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, + // If there weren't any arguments (none passed, or an empty array), + // construct a standard no args command + if(args_count < 1) { + cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, val_len, 0); } @@ -3484,7 +3607,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *val return cmd_len; } -/* {{{ proto variant Redis::evalsha(string script_sha1, [array keys, int num_key_args]) +/* {{{ proto variant Redis::evalsha(string sha1, [array keys, int num_key_args]) */ PHP_METHOD(Redis, evalsha) { @@ -3494,8 +3617,10 @@ PHP_METHOD(Redis, evalsha) long keys_count = 0; RedisSock *redis_sock; - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|al", - &object, redis_ce, &sha, &sha_len, &args, &keys_count) == FAILURE) { + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|al", &object, redis_ce, &sha, &sha_len, + &args, &keys_count) == FAILURE) + { RETURN_FALSE; } @@ -3504,12 +3629,15 @@ PHP_METHOD(Redis, evalsha) RETURN_FALSE; } - /* Construct our EVALSHA command */ - cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVALSHA", sha, sha_len, args, keys_count TSRMLS_CC); + // Construct our EVALSHA command + cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVALSHA", sha, sha_len, + args, keys_count TSRMLS_CC); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL) < 0) + { RETURN_FALSE; } } @@ -3526,23 +3654,29 @@ PHP_METHOD(Redis, eval) int script_len, cmd_len; long keys_count = 0; - /* Attempt to parse parameters */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|al", - &object, redis_ce, &script, &script_len, &args, &keys_count) == FAILURE) { - RETURN_FALSE; - } + // Attempt to parse parameters + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|al", &object, redis_ce, &script, + &script_len, &args, &keys_count) + == FAILURE) + { + RETURN_FALSE; + } /* Attempt to grab socket */ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - /* Construct our EVAL command */ - cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVAL", script, script_len, args, keys_count TSRMLS_CC); + // Construct our EVAL command + cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVAL", script, script_len, + args, keys_count TSRMLS_CC); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL) < 0) + { RETURN_FALSE; } } @@ -3563,9 +3697,10 @@ redis_build_script_exists_cmd(char **ret, zval **argv, int argc) { /* Convert our argument to a string if we need to */ convert_to_string(argv[i]); - /* Append this script sha to our SCRIPT EXISTS command */ - cmd_len = redis_cmd_append_str(ret, cmd_len, Z_STRVAL_P(argv[i]), Z_STRLEN_P(argv[i])); - } + // Append this script sha to our SCRIPT EXISTS command + cmd_len = redis_cmd_append_str(ret, cmd_len, Z_STRVAL_P(argv[i]), + Z_STRLEN_P(argv[i])); + } /* Success */ return cmd_len; @@ -3601,20 +3736,24 @@ PHP_METHOD(Redis, script) { RETURN_FALSE; } - /* Branch based on the directive */ - if(!strcasecmp(Z_STRVAL_P(z_args[0]), "flush") || !strcasecmp(Z_STRVAL_P(z_args[0]), "kill")) { - // Simple SCRIPT FLUSH, or SCRIPT_KILL command - cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "s", + // Branch based on the directive + if(!strcasecmp(Z_STRVAL_P(z_args[0]), "flush") || + !strcasecmp(Z_STRVAL_P(z_args[0]), "kill")) + { + // Simple SCRIPT FLUSH, or SCRIPT_KILL command + cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "s", Z_STRVAL_P(z_args[0]), Z_STRLEN_P(z_args[0])); - } else if(!strcasecmp(Z_STRVAL_P(z_args[0]), "load")) { - /* Make sure we have a second argument, and it's not empty. If it is */ - /* empty, we can just return an empty array (which is what Redis does) */ - if(argc < 2 || Z_TYPE_P(z_args[1]) != IS_STRING || Z_STRLEN_P(z_args[1]) < 1) { - /* Free our args */ - efree(z_args); - RETURN_FALSE; - } + } else if(!strcasecmp(Z_STRVAL_P(z_args[0]), "load")) { + // Make sure we have a second argument, and it's not empty. If it is + // empty, we can just return an empty array (which is what Redis does) + if(argc < 2 || Z_TYPE_P(z_args[1]) != IS_STRING || + Z_STRLEN_P(z_args[1]) < 1) + { + // Free our args + efree(z_args); + RETURN_FALSE; + } // Format our SCRIPT LOAD command cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "ss", @@ -3632,14 +3771,16 @@ PHP_METHOD(Redis, script) { /* Free our alocated arguments */ efree(z_args); - /* Kick off our request */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_read_variant_reply); + // Kick off our request + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL) < 0) + { + RETURN_FALSE; + } + } + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } /* {{{ proto DUMP key @@ -3688,7 +3829,8 @@ PHP_METHOD(Redis, restore) { } /* - * {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, bool replace]) + * {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, + * bool replace]) */ PHP_METHOD(Redis, migrate) { zval *object; @@ -3698,10 +3840,12 @@ PHP_METHOD(Redis, migrate) { zend_bool copy=0, replace=0; long port, dest_db, timeout; - /* Parse arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll|bb", &object, redis_ce, - &host, &host_len, &port, &key, &key_len, &dest_db, &timeout, - ©, &replace) == FAILURE) { + // Parse arguments + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Oslsll|bb", &object, redis_ce, &host, + &host_len, &port, &key, &key_len, &dest_db, + &timeout, ©, &replace) == FAILURE) + { RETURN_FALSE; } @@ -3739,12 +3883,13 @@ PHP_METHOD(Redis, migrate) { /* Free our key if we prefixed it */ if(key_free) efree(key); - /* Kick off our MIGRATE request */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + // Kick off our MIGRATE request + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL, NULL); + } + REDIS_PROCESS_RESPONSE(redis_boolean_response); } /* @@ -3756,15 +3901,18 @@ PHP_METHOD(Redis, _prefix) { char *key; int key_len; - /* Parse our arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce, - &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - /* Grab socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } + // Parse our arguments + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + &object, redis_ce, &key, &key_len) + == FAILURE) + { + RETURN_FALSE; + } + + // Grab socket + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } /* Prefix our key if we need to */ if(redis_sock->prefix != NULL && redis_sock->prefix_len > 0) { @@ -3800,7 +3948,8 @@ PHP_METHOD(Redis, _serialize) { // Serialize, which will return a value even if no serializer is set redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - /* Return serialized value. Tell PHP to make a copy as some can be interned. */ + // Return serialized value. Tell PHP to make a copy as some can be + // interned. RETVAL_STRINGL(val, val_len, 1); if(val_free) STR_FREE(val); } @@ -3814,15 +3963,18 @@ PHP_METHOD(Redis, _unserialize) { char *value; int value_len; - /* Parse our arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_ce, - &value, &value_len) == FAILURE) { - RETURN_FALSE; - } - /* Grab socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } + // Parse our arguments + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + &object, redis_ce, &value, &value_len) + == FAILURE) + { + RETURN_FALSE; + } + + // Grab socket + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } /* We only need to attempt unserialization if we have a serializer running */ if(redis_sock->serializer != REDIS_SERIALIZER_NONE) { @@ -3832,15 +3984,15 @@ PHP_METHOD(Redis, _unserialize) { { // Badly formed input, throw an execption zend_throw_exception(redis_exception_ce, - "Invalid serialized data, or unserialization error", - 0 TSRMLS_CC); - RETURN_FALSE; - } - RETURN_ZVAL(z_ret, 0, 1); - } else { - /* Just return the value that was passed to us */ - RETURN_STRINGL(value, value_len, 1); - } + "Invalid serialized data, or unserialization error", + 0 TSRMLS_CC); + RETURN_FALSE; + } + RETURN_ZVAL(z_ret, 0, 1); + } else { + // Just return the value that was passed to us + RETURN_STRINGL(value, value_len, 1); + } } /* @@ -3850,14 +4002,17 @@ PHP_METHOD(Redis, getLastError) { zval *object; RedisSock *redis_sock; - /* Grab our object */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } - /* Grab socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } + // Grab our object + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_ce) == FAILURE) + { + RETURN_FALSE; + } + + // Grab socket + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } /* Return our last error or NULL if we don't have one */ if(redis_sock->err != NULL && redis_sock->err_len > 0) { @@ -3874,33 +4029,10 @@ PHP_METHOD(Redis, clearLastError) { zval *object; RedisSock *redis_sock; - /* Grab our object */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } - /* Grab socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* Clear error message */ - if(redis_sock->err) { - efree(redis_sock->err); - } - redis_sock->err = NULL; - - RETURN_TRUE; -} - -/* - * {{{ proto long Redis::getMode() - */ -PHP_METHOD(Redis, getMode) { - zval *object; - RedisSock *redis_sock; - - /* Grab our object */ - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { + // Grab our object + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_ce) == FAILURE) + { RETURN_FALSE; } @@ -3921,26 +4053,31 @@ PHP_METHOD(Redis, time) { char *cmd; int cmd_len; - /* Grab our object */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } - /* Grab socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } + // Grab our object + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_ce) == FAILURE) + { + RETURN_FALSE; + } + + // Grab socket + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } /* Build TIME command */ cmd_len = redis_cmd_format_static(&cmd, "TIME", ""); - /* Execute or queue command */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if(redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_raw); + // Execute or queue command + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + if(redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL) < 0) + { + RETURN_FALSE; + } + } + REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw); } /* @@ -4073,9 +4210,10 @@ PHP_METHOD(Redis, client) { char *cmd, *opt=NULL, *arg=NULL; int cmd_len, opt_len, arg_len; - /* Parse our method parameters */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s", - &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE) + // Parse our method parameters + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|s", &object, redis_ce, &opt, &opt_len, + &arg, &arg_len) == FAILURE) { RETURN_FALSE; } @@ -4100,12 +4238,14 @@ PHP_METHOD(Redis, client) { /* We handle CLIENT LIST with a custom response function */ if(!strncasecmp(opt, "list", 4)) { IF_ATOMIC() { - redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL); + redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, + NULL); } REDIS_PROCESS_RESPONSE(redis_client_list_reply); } else { IF_ATOMIC() { - redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL); + redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock,NULL); } REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } @@ -4155,13 +4295,15 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, /* Append COUNT if we've got it */ if(count) { - cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", sizeof("COUNT")-1); + cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", + sizeof("COUNT")-1); cmd_len = redis_cmd_append_int(cmd, cmd_len, count); } /* Append MATCH if we've got it */ if(pattern_len) { - cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", sizeof("MATCH")-1); + cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", + sizeof("MATCH")-1); cmd_len = redis_cmd_append_str(cmd, cmd_len, pattern, pattern_len); } @@ -4183,18 +4325,20 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Different prototype depending on if this is a key based scan */ if(type != TYPE_SCAN) { - /* Requires a key */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz/|s!l", - &object, redis_ce, &key, &key_len, &z_iter, - &pattern, &pattern_len, &count)==FAILURE) + // Requires a key + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Osz/|s!l", &object, redis_ce, &key, + &key_len, &z_iter, &pattern, + &pattern_len, &count)==FAILURE) { RETURN_FALSE; } } else { - /* Doesn't require a key */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz/|s!l", - &object, redis_ce, &z_iter, &pattern, &pattern_len, - &count) == FAILURE) + // Doesn't require a key + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Oz/|s!l", &object, redis_ce, &z_iter, + &pattern, &pattern_len, &count) + == FAILURE) { RETURN_FALSE; } @@ -4207,12 +4351,14 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Calling this in a pipeline makes no sense */ IF_NOT_ATOMIC() { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't call SCAN commands in multi or pipeline mode!"); + php_error_docref(NULL TSRMLS_CC, E_ERROR, + "Can't call SCAN commands in multi or pipeline mode!"); RETURN_FALSE; } - /* The iterator should be passed in as NULL for the first iteration, but we can treat */ - /* any NON LONG value as NULL for these purposes as we've seperated the variable anyway. */ + // The iterator should be passed in as NULL for the first iteration, but we + // can treat any NON LONG value as NULL for these purposes as we've + // seperated the variable anyway. if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter)<0) { /* Convert to long */ convert_to_long(z_iter); @@ -4231,11 +4377,11 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } /** - * Redis can return to us empty keys, especially in the case where there are a large - * number of keys to scan, and we're matching against a pattern. PHPRedis can be set - * up to abstract this from the user, by setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise - * we will return empty keys and the user will need to make subsequent calls with - * an updated iterator. + * Redis can return to us empty keys, especially in the case where there are + * a large number of keys to scan, and we're matching against a pattern. + * PHPRedis can be set up to abstract this from the user, by setting + * OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty keys and + * the user will need to make subsequent calls with an updated iterator. */ do { /* Free our previous reply if we're back in the loop. We know we are @@ -4261,7 +4407,8 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Get the number of elements */ hash = Z_ARRVAL_P(return_value); num_elements = zend_hash_num_elements(hash); - } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && num_elements == 0); + } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && + num_elements == 0); /* Free our key if it was prefixed */ if(key_free) efree(key); From 2b26f96f00ca3a91e3d2461b6cebe3b1944872fa Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 11:28:35 -0700 Subject: [PATCH 0318/1986] Some simple formatting changes Moved our ResultCallback define to the top (before function protos) as well as moved PHP_REDIS_VERSION define to the very top of php_redis.h --- php_redis.h | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/php_redis.h b/php_redis.h index 428440e848..cce5b29ac0 100644 --- a/php_redis.h +++ b/php_redis.h @@ -24,6 +24,9 @@ #ifndef PHP_REDIS_H #define PHP_REDIS_H +/* phpredis version */ +#define PHP_REDIS_VERSION "2.2.5" + PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); PHP_METHOD(Redis, connect); @@ -229,25 +232,32 @@ PHP_RINIT_FUNCTION(redis); PHP_RSHUTDOWN_FUNCTION(redis); PHP_MINFO_FUNCTION(redis); -PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); -PHP_REDIS_API void redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int count); -PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_len, - int min_argc, RedisSock **redis_sock, int has_timeout, int all_keys, int can_serialize); -PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha); -typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void generic_empty_cmd_impl(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ResultCallback result_callback); -PHP_REDIS_API void generic_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...); -PHP_REDIS_API void generic_empty_long_cmd(INTERNAL_FUNCTION_PARAMETERS, char *cmd, int cmd_len, ...); +/* Redis response handler function callback prototype */ +typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); + +PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); + +PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, + int use_alpha); + +PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd); + +PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, + char *unsub_cmd); -PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd); -PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd); +PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, + int use_atof TSRMLS_DC); -PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC); +PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int get_flag(zval *object TSRMLS_DC); -PHP_REDIS_API void set_flag(zval *object, int new_flag TSRMLS_DC); +PHPAPI int get_flag(zval *object TSRMLS_DC); -PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems); +PHPAPI void set_flag(zval *object, int new_flag TSRMLS_DC); + +PHPAPI int redis_sock_read_multibulk_multi_reply_loop( + INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, + int numElems); /* pipeline */ PHP_REDIS_API request_item* get_pipeline_head(zval *object); @@ -261,7 +271,6 @@ ZEND_END_MODULE_GLOBALS(redis) #endif struct redis_queued_item { - /* reading function */ zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ...); @@ -272,12 +281,10 @@ struct redis_queued_item { }; extern zend_module_entry redis_module_entry; -#define redis_module_ptr &redis_module_entry +#define redis_module_ptr &redis_module_entry #define phpext_redis_ptr redis_module_ptr -#define PHP_REDIS_VERSION "2.2.7" - #endif /* From 1f5aa6e8ed3c9852c451080e9017d1b8fb43e665 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 12:44:13 -0700 Subject: [PATCH 0319/1986] OCD formatting changes --- redis.c | 434 +++++++++++++------------------------------------------- 1 file changed, 100 insertions(+), 334 deletions(-) diff --git a/redis.c b/redis.c index 2c06cb4d1d..d081abe788 100644 --- a/redis.c +++ b/redis.c @@ -1161,35 +1161,30 @@ PHP_METHOD(Redis, rPushx) { REDIS_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, redis_string_response); } - /* }}} */ -/* {{{ proto string Redis::lPOP(string key) - */ +/* {{{ proto string Redis::lPOP(string key) */ PHP_METHOD(Redis, lPop) { REDIS_PROCESS_KW_CMD("LPOP", redis_key_cmd, redis_string_response); } /* }}} */ -/* {{{ proto string Redis::rPOP(string key) - */ +/* {{{ proto string Redis::rPOP(string key) */ PHP_METHOD(Redis, rPop) { REDIS_PROCESS_KW_CMD("RPOP", redis_key_cmd, redis_string_response); } /* }}} */ -/* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout) - */ +/* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout) */ PHP_METHOD(Redis, blPop) { REDIS_PROCESS_CMD(blpop, redis_sock_read_multibulk_reply); } /* }}} */ -/* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout) - */ +/* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout) */ PHP_METHOD(Redis, brPop) { REDIS_PROCESS_CMD(brpop, redis_sock_read_multibulk_reply); @@ -1197,24 +1192,21 @@ PHP_METHOD(Redis, brPop) /* }}} */ -/* {{{ proto int Redis::lSize(string key) - */ +/* {{{ proto int Redis::lSize(string key) */ PHP_METHOD(Redis, lSize) { REDIS_PROCESS_KW_CMD("LLEN", redis_key_cmd, redis_long_response); } /* }}} */ -/* {{{ proto boolean Redis::lRemove(string list, string value, int count = 0) - */ +/* {{{ proto boolean Redis::lRemove(string list, string value, int count = 0) */ PHP_METHOD(Redis, lRemove) { REDIS_PROCESS_CMD(lrem, redis_long_response); } /* }}} */ -/* {{{ proto boolean Redis::listTrim(string key , int start , int end) - */ +/* {{{ proto boolean Redis::listTrim(string key , int start , int end) */ PHP_METHOD(Redis, listTrim) { REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, @@ -1222,16 +1214,14 @@ PHP_METHOD(Redis, listTrim) } /* }}} */ -/* {{{ proto string Redis::lGet(string key , int index) - */ +/* {{{ proto string Redis::lGet(string key , int index) */ PHP_METHOD(Redis, lGet) { REDIS_PROCESS_KW_CMD("LGET", redis_key_long_cmd, redis_string_response); } /* }}} */ -/* {{{ proto array Redis::lGetRange(string key, int start , int end) - */ +/* {{{ proto array Redis::lGetRange(string key, int start , int end) */ PHP_METHOD(Redis, lGetRange) { REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, @@ -1239,8 +1229,7 @@ PHP_METHOD(Redis, lGetRange) } /* }}} */ -/* {{{ proto boolean Redis::sAdd(string key , mixed value) - */ +/* {{{ proto boolean Redis::sAdd(string key , mixed value) */ PHP_METHOD(Redis, sAdd) { REDIS_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, redis_long_response); @@ -1254,33 +1243,28 @@ PHP_METHOD(Redis, sSize) } /* }}} */ -/* {{{ proto boolean Redis::sRemove(string set, string value) - */ +/* {{{ proto boolean Redis::sRemove(string set, string value) */ PHP_METHOD(Redis, sRemove) { REDIS_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, redis_long_response); } /* }}} */ -/* {{{ proto boolean Redis::sMove(string set_src, string set_dst, mixed value) - */ +/* {{{ proto boolean Redis::sMove(string src, string dst, mixed value) */ PHP_METHOD(Redis, sMove) { REDIS_PROCESS_CMD(smove, redis_1_response); } /* }}} */ -/* }}} */ -/* {{{ proto string Redis::sPop(string key) - */ +/* {{{ proto string Redis::sPop(string key) */ PHP_METHOD(Redis, sPop) { REDIS_PROCESS_KW_CMD("SPOP", redis_key_cmd, redis_string_response); } /* }}} */ -/* {{{ proto string Redis::sRandMember(string key [int count]) - */ +/* {{{ proto string Redis::sRandMember(string key [int count]) */ PHP_METHOD(Redis, sRandMember) { char *cmd; @@ -1316,16 +1300,14 @@ PHP_METHOD(Redis, sRandMember) } /* }}} */ -/* {{{ proto boolean Redis::sContains(string set, string value) - */ +/* {{{ proto boolean Redis::sContains(string set, string value) */ PHP_METHOD(Redis, sContains) { REDIS_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, redis_1_response); } /* }}} */ -/* {{{ proto array Redis::sMembers(string set) - */ +/* {{{ proto array Redis::sMembers(string set) */ PHP_METHOD(Redis, sMembers) { REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, @@ -1333,42 +1315,37 @@ PHP_METHOD(Redis, sMembers) } /* }}} */ -/* {{{ proto array Redis::sInter(string key0, ... string keyN) - */ +/* {{{ proto array Redis::sInter(string key0, ... string keyN) */ PHP_METHOD(Redis, sInter) { REDIS_PROCESS_CMD(sinter, redis_sock_read_multibulk_reply); } /* }}} */ -/* {{{ proto array Redis::sInterStore(string dst, string key0, ... string keyN) - */ +/* {{{ proto array Redis::sInterStore(string dst, string key0,...string keyN) */ PHP_METHOD(Redis, sInterStore) { REDIS_PROCESS_CMD(sinterstore, redis_long_response); } /* }}} */ -/* {{{ proto array Redis::sUnion(string key0, ... string keyN) - */ +/* {{{ proto array Redis::sUnion(string key0, ... string keyN) */ PHP_METHOD(Redis, sUnion) { REDIS_PROCESS_CMD(sunion, redis_sock_read_multibulk_reply); } /* }}} */ -/* {{{ proto array Redis::sUnionStore(string dst, string key0, ... string keyN) - */ + +/* {{{ proto array Redis::sUnionStore(string dst, string key0, ... keyN) */ PHP_METHOD(Redis, sUnionStore) { REDIS_PROCESS_CMD(sunionstore, redis_long_response); } /* }}} */ -/* {{{ proto array Redis::sDiff(string key0, ... string keyN) - */ +/* {{{ proto array Redis::sDiff(string key0, ... string keyN) */ PHP_METHOD(Redis, sDiff) { REDIS_PROCESS_CMD(sdiff, redis_sock_read_multibulk_reply); } /* }}} */ -/* {{{ proto array Redis::sDiffStore(string dst, string key0, ... string keyN) - */ +/* {{{ proto array Redis::sDiffStore(string dst, string key0, ... keyN) */ PHP_METHOD(Redis, sDiffStore) { REDIS_PROCESS_CMD(sdiffstore, redis_long_response); } @@ -1600,8 +1577,7 @@ PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, } /* {{{ proto array Redis::sortAsc(string key, string pattern, string get, - * int start, int end, bool getList]) - */ + * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortAsc) { generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ASC", 0); @@ -1609,8 +1585,7 @@ PHP_METHOD(Redis, sortAsc) /* }}} */ /* {{{ proto array Redis::sortAscAlpha(string key, string pattern, string get, - * int start, int end, bool getList]) - */ + * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortAscAlpha) { generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ASC", 1); @@ -1618,8 +1593,7 @@ PHP_METHOD(Redis, sortAscAlpha) /* }}} */ /* {{{ proto array Redis::sortDesc(string key, string pattern, string get, - * int start, int end, bool getList]) - */ + * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortDesc) { generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DESC", 0); @@ -1627,127 +1601,112 @@ PHP_METHOD(Redis, sortDesc) /* }}} */ /* {{{ proto array Redis::sortDescAlpha(string key, string pattern, string get, - * int start, int end, bool getList]) - */ + * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortDescAlpha) { generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DESC", 1); } /* }}} */ -/* {{{ proto array Redis::setTimeout(string key, int timeout) - */ +/* {{{ proto array Redis::setTimeout(string key, int timeout) */ PHP_METHOD(Redis, setTimeout) { REDIS_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, redis_1_response); } /* }}} */ +/* {{{ proto bool Redis::pexpire(string key, long ms) */ PHP_METHOD(Redis, pexpire) { REDIS_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, redis_1_response); } /* }}} */ -/* {{{ proto array Redis::expireAt(string key, int timestamp) - */ +/* {{{ proto array Redis::expireAt(string key, int timestamp) */ PHP_METHOD(Redis, expireAt) { REDIS_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, redis_1_response); } /* }}} */ -/* {{{ proto array Redis::pexpireAt(string key, int timestamp) - */ +/* {{{ proto array Redis::pexpireAt(string key, int timestamp) */ PHP_METHOD(Redis, pexpireAt) { REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, redis_1_response); } /* }}} */ -/* {{{ proto array Redis::lSet(string key, int index, string value) - */ +/* {{{ proto array Redis::lSet(string key, int index, string value) */ PHP_METHOD(Redis, lSet) { REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, redis_boolean_response); } /* }}} */ -/* {{{ proto string Redis::save() - */ +/* {{{ proto string Redis::save() */ PHP_METHOD(Redis, save) { REDIS_PROCESS_KW_CMD("SAVE", redis_empty_cmd, redis_boolean_response); } /* }}} */ -/* {{{ proto string Redis::bgSave() - */ +/* {{{ proto string Redis::bgSave() */ PHP_METHOD(Redis, bgSave) { REDIS_PROCESS_KW_CMD("BGSAVE", redis_empty_cmd, redis_boolean_response); } /* }}} */ -/* {{{ proto integer Redis::lastSave() - */ +/* {{{ proto integer Redis::lastSave() */ PHP_METHOD(Redis, lastSave) { REDIS_PROCESS_KW_CMD("LASTSAVE", redis_empty_cmd, redis_long_response); } /* }}} */ -/* {{{ proto bool Redis::flushDB() - */ +/* {{{ proto bool Redis::flushDB() */ PHP_METHOD(Redis, flushDB) { REDIS_PROCESS_KW_CMD("FLUSHDB", redis_empty_cmd, redis_boolean_response); } /* }}} */ -/* {{{ proto bool Redis::flushAll() - */ +/* {{{ proto bool Redis::flushAll() */ PHP_METHOD(Redis, flushAll) { REDIS_PROCESS_KW_CMD("FLUSHALL", redis_empty_cmd, redis_boolean_response); } /* }}} */ -/* {{{ proto int Redis::dbSize() - */ +/* {{{ proto int Redis::dbSize() */ PHP_METHOD(Redis, dbSize) { REDIS_PROCESS_KW_CMD("DBSIZE", redis_empty_cmd, redis_long_response); } /* }}} */ -/* {{{ proto bool Redis::auth(string passwd) - */ +/* {{{ proto bool Redis::auth(string passwd) */ PHP_METHOD(Redis, auth) { REDIS_PROCESS_CMD(auth, redis_boolean_response); } /* }}} */ -/* {{{ proto long Redis::persist(string key) - */ +/* {{{ proto long Redis::persist(string key) */ PHP_METHOD(Redis, persist) { REDIS_PROCESS_KW_CMD("PERSIST", redis_key_cmd, redis_1_response); } /* }}} */ -/* {{{ proto long Redis::ttl(string key) - */ +/* {{{ proto long Redis::ttl(string key) */ PHP_METHOD(Redis, ttl) { REDIS_PROCESS_KW_CMD("TTL", redis_key_cmd, redis_long_response); } /* }}} */ -/* {{{ proto long Redis::pttl(string key) - */ +/* {{{ proto long Redis::pttl(string key) */ PHP_METHOD(Redis, pttl) { REDIS_PROCESS_KW_CMD("PTTL", redis_key_cmd, redis_long_response); } /* }}} */ -/* {{{ proto array Redis::info() - */ +/* {{{ proto array Redis::info() */ PHP_METHOD(Redis, info) { zval *object; @@ -1784,8 +1743,7 @@ PHP_METHOD(Redis, info) { } /* }}} */ -/* {{{ proto bool Redis::select(long dbNumber) - */ +/* {{{ proto bool Redis::select(long dbNumber) */ PHP_METHOD(Redis, select) { zval *object; @@ -1817,8 +1775,7 @@ PHP_METHOD(Redis, select) { } /* }}} */ -/* {{{ proto bool Redis::move(string key, long dbindex) - */ +/* {{{ proto bool Redis::move(string key, long dbindex) */ PHP_METHOD(Redis, move) { REDIS_PROCESS_KW_CMD("MOVE", redis_key_long_cmd, redis_1_response); } @@ -1934,8 +1891,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { REDIS_PROCESS_RESPONSE(fun); } -/* {{{ proto bool Redis::mset(array (key => value, ...)) - */ +/* {{{ proto bool Redis::mset(array (key => value, ...)) */ PHP_METHOD(Redis, mset) { generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response); @@ -1943,16 +1899,14 @@ PHP_METHOD(Redis, mset) { /* }}} */ -/* {{{ proto bool Redis::msetnx(array (key => value, ...)) - */ +/* {{{ proto bool Redis::msetnx(array (key => value, ...)) */ PHP_METHOD(Redis, msetnx) { generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response); } /* }}} */ -/* {{{ proto string Redis::rpoplpush(string srckey, string dstkey) - */ +/* {{{ proto string Redis::rpoplpush(string srckey, string dstkey) */ PHP_METHOD(Redis, rpoplpush) { REDIS_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, redis_string_response); @@ -1965,8 +1919,7 @@ PHP_METHOD(Redis, brpoplpush) { } /* }}} */ -/* {{{ proto long Redis::zAdd(string key, int score, string value) - */ +/* {{{ proto long Redis::zAdd(string key, int score, string value) */ PHP_METHOD(Redis, zAdd) { REDIS_PROCESS_CMD(zadd, redis_long_response); } @@ -2039,8 +1992,7 @@ PHP_METHOD(Redis, zRevRangeByScore) { } /* }}} */ -/* {{{ proto long Redis::zDelete(string key, string member) - */ +/* {{{ proto long Redis::zDelete(string key, string member) */ PHP_METHOD(Redis, zDelete) { REDIS_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, redis_long_response); @@ -2055,8 +2007,7 @@ PHP_METHOD(Redis, zDeleteRangeByScore) } /* }}} */ -/* {{{ proto long Redis::zDeleteRangeByRank(string key, long start, long end) - */ +/* {{{ proto long Redis::zDeleteRangeByRank(string key, long start, long end) */ PHP_METHOD(Redis, zDeleteRangeByRank) { REDIS_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd, @@ -2071,75 +2022,14 @@ PHP_METHOD(Redis, zCount) } /* }}} */ -/* {{{ proto array Redis::zRangeByLex(string $key, string $min, string $max, - * [long $offset, long $count]) */ -PHP_METHOD(Redis, zRangeByLex) { - zval *object; - RedisSock *redis_sock; - char *cmd, *key, *min, *max; - long offset, count; - int argc, cmd_len, key_len; - int key_free, min_len, max_len; - - /* We need either three or five arguments for this to be a valid call */ - argc = ZEND_NUM_ARGS(); - if (argc != 3 && argc != 5) { - RETURN_FALSE; - } - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Osss|ll", &object, redis_ce, &key, &key_len, - &min, &min_len, &max, &max_len, &offset, - &count) == FAILURE) - { - RETURN_FALSE; - } - - /* We can do some simple validation for the user, as we know how min/max are - * required to start */ - if (!IS_LEX_ARG(min,min_len) || !IS_LEX_ARG(max,max_len)) { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - - /* Construct our command depending on argc */ - if (argc == 3) { - cmd_len = redis_cmd_format_static(&cmd, "ZRANGEBYLEX", "sss", key, - key_len, min, min_len, max, max_len); - } else { - cmd_len = redis_cmd_format_static(&cmd, "ZRANGEBYLEX", "ssssll", key, - key_len, min, min_len, max, max_len, "LIMIT", sizeof("LIMIT")-1, - offset, count); - } - - if(key_free) efree(key); - - /* Kick it off */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); -} - -/* {{{ proto long Redis::zCard(string key) - */ +/* {{{ proto long Redis::zCard(string key) */ PHP_METHOD(Redis, zCard) { REDIS_PROCESS_KW_CMD("ZCARD", redis_key_cmd, redis_long_response); } /* }}} */ -/* {{{ proto double Redis::zScore(string key, mixed member) - */ +/* {{{ proto double Redis::zScore(string key, mixed member) */ PHP_METHOD(Redis, zScore) { REDIS_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, @@ -2147,8 +2037,7 @@ PHP_METHOD(Redis, zScore) } /* }}} */ -/* {{{ proto long Redis::zRank(string key, string member) - */ +/* {{{ proto long Redis::zRank(string key, string member) */ PHP_METHOD(Redis, zRank) { REDIS_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, redis_long_response); } @@ -2160,8 +2049,7 @@ PHP_METHOD(Redis, zRevRank) { } /* }}} */ -/* {{{ proto double Redis::zIncrBy(string key, double value, mixed member) - */ +/* {{{ proto double Redis::zIncrBy(string key, double value, mixed member) */ PHP_METHOD(Redis, zIncrBy) { REDIS_PROCESS_CMD(zincrby, redis_bulk_double_response); @@ -2180,35 +2068,35 @@ PHP_METHOD(Redis, zUnion) { /* hashes */ -/* proto long Redis::hset(string key, string mem, string val) */ +/* {{{ proto long Redis::hset(string key, string mem, string val) */ PHP_METHOD(Redis, hSet) { REDIS_PROCESS_CMD(hset, redis_long_response); } /* }}} */ -/* proto bool Redis::hSetNx(string key, string mem, string val) */ +/* {{{ proto bool Redis::hSetNx(string key, string mem, string val) */ PHP_METHOD(Redis, hSetNx) { REDIS_PROCESS_CMD(hsetnx, redis_1_response); } /* }}} */ -/* proto string Redis::hget(string key, string mem) */ +/* {{{ proto string Redis::hget(string key, string mem) */ PHP_METHOD(Redis, hGet) { REDIS_PROCESS_KW_CMD("HGET", redis_key_str_cmd, redis_string_response); } /* }}} */ -/* hLen */ +/* {{{ proto long Redis::hLen(string key) */ PHP_METHOD(Redis, hLen) { REDIS_PROCESS_KW_CMD("HLEN", redis_key_cmd, redis_long_response); } /* }}} */ -/* hDel */ +/* {{{ proto long Redis::hDel(string key, string mem1, ... memN) */ PHP_METHOD(Redis, hDel) { REDIS_PROCESS_CMD(hdel, redis_long_response); @@ -2436,6 +2324,7 @@ redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, return 0; } + /* redis_sock_read_multibulk_multi_reply */ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) @@ -2607,7 +2496,8 @@ PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) { - item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC); + item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx + TSRMLS_CC); } PHPAPI int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, @@ -2850,15 +2740,13 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub } } -/* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN)) - */ +/* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN)) */ PHP_METHOD(Redis, psubscribe) { generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "psubscribe"); } -/* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) - */ +/* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */ PHP_METHOD(Redis, subscribe) { generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "subscribe"); } @@ -2964,8 +2852,7 @@ PHP_METHOD(Redis, punsubscribe) generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUNSUBSCRIBE"); } -/* {{{ proto string Redis::bgrewriteaof() - */ +/* {{{ proto string Redis::bgrewriteaof() */ PHP_METHOD(Redis, bgrewriteaof) { REDIS_PROCESS_KW_CMD("BGREWRITEAOF", redis_empty_cmd, @@ -2973,8 +2860,7 @@ PHP_METHOD(Redis, bgrewriteaof) } /* }}} */ -/* {{{ proto string Redis::slaveof([host, port]) - */ +/* {{{ proto string Redis::slaveof([host, port]) */ PHP_METHOD(Redis, slaveof) { zval *object; @@ -3010,8 +2896,7 @@ PHP_METHOD(Redis, slaveof) } /* }}} */ -/* {{{ proto string Redis::object(key) - */ +/* {{{ proto string Redis::object(key) */ PHP_METHOD(Redis, object) { RedisSock *redis_sock; @@ -3044,8 +2929,7 @@ PHP_METHOD(Redis, object) } /* }}} */ -/* {{{ proto string Redis::getOption($option) - */ +/* {{{ proto string Redis::getOption($option) */ PHP_METHOD(Redis, getOption) { RedisSock *redis_sock; zval *object; @@ -3078,8 +2962,7 @@ PHP_METHOD(Redis, getOption) { } /* }}} */ -/* {{{ proto string Redis::setOption(string $option, mixed $value) - */ +/* {{{ proto string Redis::setOption(string $option, mixed $value) */ PHP_METHOD(Redis, setOption) { RedisSock *redis_sock; zval *object; @@ -3151,8 +3034,7 @@ PHP_METHOD(Redis, setOption) { } /* }}} */ -/* {{{ proto boolean Redis::config(string op, string key [, mixed value]) - */ +/* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */ PHP_METHOD(Redis, config) { zval *object; @@ -3209,8 +3091,7 @@ PHP_METHOD(Redis, config) /* }}} */ -/* {{{ proto boolean Redis::slowlog(string arg, [int option]) - */ +/* {{{ proto boolean Redis::slowlog(string arg, [int option]) */ PHP_METHOD(Redis, slowlog) { zval *object; RedisSock *redis_sock; @@ -3266,51 +3147,7 @@ PHP_METHOD(Redis, slowlog) { REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } -/* {{{ proto Redis::rawCommand(string cmd, arg, arg, arg, ...) }}} */ -PHP_METHOD(Redis, rawCommand) { - zval **z_args; - RedisSock *redis_sock; - int argc = ZEND_NUM_ARGS(), i; - smart_str cmd = {0}; - - /* We need at least one argument */ - z_args = emalloc(argc * sizeof(zval*)); - if (argc < 1 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { - efree(z_args); - RETURN_FALSE; - } - - /* Initialize the command we'll send */ - convert_to_string(z_args[0]); - redis_cmd_init_sstr(&cmd,argc-1,Z_STRVAL_P(z_args[0]),Z_STRLEN_P(z_args[0])); - - /* Iterate over the remainder of our arguments, appending */ - for (i = 1; i < argc; i++) { - convert_to_string(z_args[i]); - redis_cmd_append_sstr(&cmd, Z_STRVAL_P(z_args[i]), Z_STRLEN_P(z_args[i])); - } - - efree(z_args); - - /* Kick off our request and read response or enqueue handler */ - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { - if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL) < 0) - { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_read_variant_reply); -} - -/* {{{ proto Redis::wait(int num_slaves, int ms) }}} - */ +/* {{{ proto Redis::wait(int num_slaves, int ms) }}} */ PHP_METHOD(Redis, wait) { zval *object; RedisSock *redis_sock; @@ -3349,10 +3186,8 @@ PHP_METHOD(Redis, wait) { REDIS_PROCESS_RESPONSE(redis_long_response); } -/* - * Construct a PUBSUB command - */ -PHP_REDIS_API int +/* Construct a PUBSUB command */ +PHPAPI int redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, zval *arg TSRMLS_DC) { @@ -3607,8 +3442,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, return cmd_len; } -/* {{{ proto variant Redis::evalsha(string sha1, [array keys, int num_key_args]) - */ +/* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */ PHP_METHOD(Redis, evalsha) { zval *object, *args= NULL; @@ -3644,8 +3478,7 @@ PHP_METHOD(Redis, evalsha) REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } -/* {{{ proto variant Redis::eval(string script, [array keys, int num_key_args]) - */ +/* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */ PHP_METHOD(Redis, eval) { zval *object, *args = NULL; @@ -3783,55 +3616,19 @@ PHP_METHOD(Redis, script) { REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } -/* {{{ proto DUMP key - */ +/* {{{ proto DUMP key */ PHP_METHOD(Redis, dump) { REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_ping_response); } -/* {{{ proto Redis::DEBUG(string key) */ -PHP_METHOD(Redis, debug) { - zval *object; - RedisSock *redis_sock; - char *cmd, *key; - int cmd_len, key_len, key_free; - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, &key, &key_len)==FAILURE) - { - RETURN_FALSE; - } - - /* Grab our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) { - RETURN_FALSE; - } - - /* Prefix key, format command */ - key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC); - cmd_len = redis_cmd_format_static(&cmd, "DEBUG", "ss", "OBJECT", sizeof("OBJECT")-1, key, key_len); - if(key_free) efree(key); - - /* Kick it off */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_debug_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_debug_response); -} - -/* - * {{{ proto Redis::restore(ttl, key, value) - */ +/* {{{ proto Redis::restore(ttl, key, value) */ PHP_METHOD(Redis, restore) { REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_val_cmd, redis_boolean_response); } -/* - * {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, - * bool replace]) - */ +/* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, + * bool replace]) */ PHP_METHOD(Redis, migrate) { zval *object; RedisSock *redis_sock; @@ -3892,9 +3689,7 @@ PHP_METHOD(Redis, migrate) { REDIS_PROCESS_RESPONSE(redis_boolean_response); } -/* - * {{{ proto Redis::_prefix(key) - */ +/* {{{ proto Redis::_prefix(key) */ PHP_METHOD(Redis, _prefix) { zval *object; RedisSock *redis_sock; @@ -3923,9 +3718,7 @@ PHP_METHOD(Redis, _prefix) { } } -/* - * {{{ proto Redis::_serialize(value) - */ +/* {{{ proto Redis::_serialize(value) */ PHP_METHOD(Redis, _serialize) { zval *object; RedisSock *redis_sock; @@ -3954,9 +3747,7 @@ PHP_METHOD(Redis, _serialize) { if(val_free) STR_FREE(val); } -/* - * {{{ proto Redis::_unserialize(value) - */ +/* {{{ proto Redis::_unserialize(value) */ PHP_METHOD(Redis, _unserialize) { zval *object; RedisSock *redis_sock; @@ -3995,9 +3786,7 @@ PHP_METHOD(Redis, _unserialize) { } } -/* - * {{{ proto Redis::getLastError() - */ +/* {{{ proto Redis::getLastError() */ PHP_METHOD(Redis, getLastError) { zval *object; RedisSock *redis_sock; @@ -4022,9 +3811,7 @@ PHP_METHOD(Redis, getLastError) { } } -/* - * {{{ proto Redis::clearLastError() - */ +/* {{{ proto Redis::clearLastError() */ PHP_METHOD(Redis, clearLastError) { zval *object; RedisSock *redis_sock; @@ -4044,9 +3831,8 @@ PHP_METHOD(Redis, clearLastError) { RETVAL_LONG(redis_sock->mode); } -/* - * {{{ proto Redis::time() - */ + +/* {{{ proto Redis::time() */ PHP_METHOD(Redis, time) { zval *object; RedisSock *redis_sock; @@ -4084,9 +3870,7 @@ PHP_METHOD(Redis, time) { * Introspection stuff */ -/* - * {{{ proto Redis::IsConnected - */ +/* {{{ proto Redis::IsConnected */ PHP_METHOD(Redis, isConnected) { RedisSock *redis_sock; @@ -4097,9 +3881,7 @@ PHP_METHOD(Redis, isConnected) { } } -/* - * {{{ proto Redis::getHost() - */ +/* {{{ proto Redis::getHost() */ PHP_METHOD(Redis, getHost) { RedisSock *redis_sock; @@ -4110,9 +3892,7 @@ PHP_METHOD(Redis, getHost) { } } -/* - * {{{ proto Redis::getPort() - */ +/* {{{ proto Redis::getPort() */ PHP_METHOD(Redis, getPort) { RedisSock *redis_sock; @@ -4124,9 +3904,7 @@ PHP_METHOD(Redis, getPort) { } } -/* - * {{{ proto Redis::getDBNum - */ +/* {{{ proto Redis::getDBNum */ PHP_METHOD(Redis, getDBNum) { RedisSock *redis_sock; @@ -4138,9 +3916,7 @@ PHP_METHOD(Redis, getDBNum) { } } -/* - * {{{ proto Redis::getTimeout - */ +/* {{{ proto Redis::getTimeout */ PHP_METHOD(Redis, getTimeout) { RedisSock *redis_sock; @@ -4151,9 +3927,7 @@ PHP_METHOD(Redis, getTimeout) { } } -/* - * {{{ proto Redis::getReadTimeout - */ +/* {{{ proto Redis::getReadTimeout */ PHP_METHOD(Redis, getReadTimeout) { RedisSock *redis_sock; @@ -4164,9 +3938,7 @@ PHP_METHOD(Redis, getReadTimeout) { } } -/* - * {{{ proto Redis::getPersistentID - */ +/* {{{ proto Redis::getPersistentID */ PHP_METHOD(Redis, getPersistentID) { RedisSock *redis_sock; @@ -4181,9 +3953,7 @@ PHP_METHOD(Redis, getPersistentID) { } } -/* - * {{{ proto Redis::getAuth - */ +/* {{{ proto Redis::getAuth */ PHP_METHOD(Redis, getAuth) { RedisSock *redis_sock; @@ -4251,10 +4021,8 @@ PHP_METHOD(Redis, client) { } } -/** - * Helper to format any combination of SCAN arguments - */ -PHP_REDIS_API int +/* Helper to format any combination of SCAN arguments */ +PHPAPI int redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, int iter, char *pattern, int pattern_len, int count) { @@ -4311,10 +4079,8 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, return cmd_len; } -/** - * {{{ proto redis::scan(&$iterator, [pattern, [count]]) - */ -PHP_REDIS_API void +/* {{{ proto redis::scan(&$iterator, [pattern, [count]]) */ +PHPAPI void generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { zval *object, *z_iter; RedisSock *redis_sock; From fe67324a319aefe57adfa8f003ca95bad77cf202 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 15:19:29 -0700 Subject: [PATCH 0320/1986] Introspection commands Implemented various introspection/option commands for both Redis and RedisCluster. Commands implemented: * getOption * setOption * _prefix * _serialize * _unserialize With these implemented, we can properly test RedisCluster with things like serialization and prefixing turned on. --- library.h | 22 ++-- redis.c | 166 ++----------------------- redis_cluster.c | 102 ++++++++++----- redis_cluster.h | 9 +- redis_commands.c | 317 ++++++++++++++++++++++++++++++++++++----------- redis_commands.h | 16 +++ 6 files changed, 359 insertions(+), 273 deletions(-) diff --git a/library.h b/library.h index 362e2cd8f9..145cfaf790 100644 --- a/library.h +++ b/library.h @@ -22,21 +22,13 @@ PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size typedef void (*SuccessCallback)(RedisSock *redis_sock); -PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); -PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); -PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); -PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); -PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); -PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); +PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); +PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); +PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC); +PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int nothrow); +PHPAPI void redis_free_socket(RedisSock *redis_sock); +PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/redis.c b/redis.c index d081abe788..3f18672f85 100644 --- a/redis.c +++ b/redis.c @@ -2932,105 +2932,24 @@ PHP_METHOD(Redis, object) /* {{{ proto string Redis::getOption($option) */ PHP_METHOD(Redis, getOption) { RedisSock *redis_sock; - zval *object; - long option; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", - &object, redis_ce, &option) == FAILURE) { + if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - switch(option) { - case REDIS_OPT_SERIALIZER: - RETURN_LONG(redis_sock->serializer); - case REDIS_OPT_PREFIX: - if(redis_sock->prefix) { - RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len, 1); - } - RETURN_NULL(); - case REDIS_OPT_READ_TIMEOUT: - RETURN_DOUBLE(redis_sock->read_timeout); - case REDIS_OPT_SCAN: - RETURN_LONG(redis_sock->scan); - default: - RETURN_FALSE; - } + redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } /* }}} */ /* {{{ proto string Redis::setOption(string $option, mixed $value) */ PHP_METHOD(Redis, setOption) { RedisSock *redis_sock; - zval *object; - long option, val_long; - char *val_str; - int val_len; - struct timeval read_tv; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Ols", &object, redis_ce, &option, - &val_str, &val_len) == FAILURE) - { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { RETURN_FALSE; } - switch(option) { - case REDIS_OPT_SERIALIZER: - val_long = atol(val_str); - if(val_long == REDIS_SERIALIZER_NONE -#ifdef HAVE_REDIS_IGBINARY - || val_long == REDIS_SERIALIZER_IGBINARY -#endif - || val_long == REDIS_SERIALIZER_PHP) { - redis_sock->serializer = val_long; - RETURN_TRUE; - } else { - RETURN_FALSE; - } - break; - case REDIS_OPT_PREFIX: - if(redis_sock->prefix) { - efree(redis_sock->prefix); - } - if(val_len == 0) { - redis_sock->prefix = NULL; - redis_sock->prefix_len = 0; - } else { - redis_sock->prefix_len = val_len; - redis_sock->prefix = ecalloc(1+val_len, 1); - memcpy(redis_sock->prefix, val_str, val_len); - } - RETURN_TRUE; - case REDIS_OPT_READ_TIMEOUT: - redis_sock->read_timeout = atof(val_str); - if(redis_sock->stream) { - read_tv.tv_sec = (time_t)redis_sock->read_timeout; - read_tv.tv_usec = (int)((redis_sock->read_timeout - - read_tv.tv_sec) * 1000000); - php_stream_set_option(redis_sock->stream, - PHP_STREAM_OPTION_READ_TIMEOUT, 0, - &read_tv); - } - RETURN_TRUE; - case REDIS_OPT_SCAN: - val_long = atol(val_str); - if(val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) { - redis_sock->scan = val_long; - RETURN_TRUE; - } - RETURN_FALSE; - break; - default: - RETURN_FALSE; - } + redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } /* }}} */ @@ -3691,99 +3610,38 @@ PHP_METHOD(Redis, migrate) { /* {{{ proto Redis::_prefix(key) */ PHP_METHOD(Redis, _prefix) { - zval *object; RedisSock *redis_sock; - char *key; - int key_len; - // Parse our arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, &key, &key_len) - == FAILURE) - { - RETURN_FALSE; - } - - // Grab socket - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { RETURN_FALSE; } - /* Prefix our key if we need to */ - if(redis_sock->prefix != NULL && redis_sock->prefix_len > 0) { - redis_key_prefix(redis_sock, &key, &key_len); - RETURN_STRINGL(key, key_len, 0); - } else { - RETURN_STRINGL(key, key_len, 1); - } + redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } /* {{{ proto Redis::_serialize(value) */ PHP_METHOD(Redis, _serialize) { - zval *object; RedisSock *redis_sock; - zval *z_val; - char *val; - int val_len, val_free; - - /* Parse arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz", - &object, redis_ce, &z_val) == FAILURE) - { - RETURN_FALSE; - } - /* Grab socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + // Grab socket + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - // Serialize, which will return a value even if no serializer is set - redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - - // Return serialized value. Tell PHP to make a copy as some can be - // interned. - RETVAL_STRINGL(val, val_len, 1); - if(val_free) STR_FREE(val); + redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } /* {{{ proto Redis::_unserialize(value) */ PHP_METHOD(Redis, _unserialize) { - zval *object; RedisSock *redis_sock; - char *value; - int value_len; - - // Parse our arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_ce, &value, &value_len) - == FAILURE) - { - RETURN_FALSE; - } // Grab socket - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - /* We only need to attempt unserialization if we have a serializer running */ - if(redis_sock->serializer != REDIS_SERIALIZER_NONE) { - zval *z_ret = NULL; - if(redis_unserialize(redis_sock, value, value_len, &z_ret - TSRMLS_CC) == 0) - { - // Badly formed input, throw an execption - zend_throw_exception(redis_exception_ce, - "Invalid serialized data, or unserialization error", - 0 TSRMLS_CC); - RETURN_FALSE; - } - RETURN_ZVAL(z_ret, 0, 1); - } else { - // Just return the value that was passed to us - RETURN_STRINGL(value, value_len, 1); - } + redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_exception_ce); } /* {{{ proto Redis::getLastError() */ diff --git a/redis_cluster.c b/redis_cluster.c index b283d87cd2..c0b255862e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -136,6 +136,12 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, object, NULL, ZEND_ACC_PUBLIC) + + PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _serialize, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _unserialize, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -253,14 +259,14 @@ PHP_METHOD(RedisCluster, __construct) { redisCluster *context = GET_CONTEXT(); // Parse arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|add", &object, redis_cluster_ce, &name, - &name_len, &z_seeds, &timeout, + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|add", &object, redis_cluster_ce, &name, + &name_len, &z_seeds, &timeout, &read_timeout)==FAILURE) { RETURN_FALSE; } - + // Require a name if(name_len == 0) { zend_throw_exception(redis_cluster_exception_ce, @@ -274,24 +280,24 @@ PHP_METHOD(RedisCluster, __construct) { "Invalid timeout", 0 TSRMLS_CC); RETURN_FALSE; } - + // Validate our read timeout if(read_timeout < 0L || read_timeout > INT_MAX) { zend_throw_exception(redis_cluster_exception_ce, "Invalid read timeout", 0 TSRMLS_CC); RETURN_FALSE; } - + // TODO: Implement seed retrieval from php.ini if(!z_seeds || zend_hash_num_elements(Z_ARRVAL_P(z_seeds))==0) { zend_throw_exception(redis_cluster_exception_ce, "Must pass seeds", 0 TSRMLS_CC); RETURN_FALSE; } - + // Initialize our RedisSock "seed" objects cluster_init_seeds(context, Z_ARRVAL_P(z_seeds)); - + // Create and map our key space cluster_map_keyspace(context TSRMLS_CC); } @@ -312,16 +318,16 @@ PHP_METHOD(RedisCluster, set) { } /* }}} */ -/* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ +/* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { - CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, + CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, cluster_bool_resp); } /* }}} */ /* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, psetex) { - CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, + CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, cluster_bool_resp); } /* }}} */ @@ -348,7 +354,7 @@ PHP_METHOD(RedisCluster, exists) { PHP_METHOD(RedisCluster, keys) { // TODO: Figure out how to implement this, as we may want to send it across // all nodes (although that seems dangerous), or ask for a specified slot. - zend_throw_exception(redis_cluster_exception_ce, + zend_throw_exception(redis_cluster_exception_ce, "KEYS command not implemented", 0 TSRMLS_CC); } /* }}} */ @@ -453,7 +459,7 @@ PHP_METHOD(RedisCluster, lpushx) { } /* }}} */ -/* {{{ proto long RedisCluster::linsert(string k,string pos,mix pvt,mix val) */ +/* {{{ proto long RedisCluster::linsert(string k,string pos,mix pvt,mix val) */ PHP_METHOD(RedisCluster, linsert) { CLUSTER_PROCESS_CMD(linsert, cluster_long_resp); } @@ -576,7 +582,7 @@ PHP_METHOD(RedisCluster, pttl) { /* {{{ proto long RedisCluster::zcard(string key) */ PHP_METHOD(RedisCluster, zcard) { CLUSTER_PROCESS_KW_CMD("ZCARD", redis_key_cmd, cluster_long_resp); -} +} /* }}} */ /* {{{ proto double RedisCluster::zscore(string key) */ @@ -636,7 +642,7 @@ PHP_METHOD(RedisCluster, hkeys) { /* }}} */ /* {{{ proto array RedisCluster::hvals(string key) */ -PHP_METHOD(RedisCluster, hvals) { +PHP_METHOD(RedisCluster, hvals) { CLUSTER_PROCESS_KW_CMD("HVALS", redis_key_cmd, cluster_mbulk_resp); } /* }}} */ @@ -661,9 +667,9 @@ PHP_METHOD(RedisCluster, hsetnx) { /* {{{ proto array RedisCluster::hgetall(string key) */ PHP_METHOD(RedisCluster, hgetall) { - CLUSTER_PROCESS_KW_CMD("HGETALL", redis_key_cmd, + CLUSTER_PROCESS_KW_CMD("HGETALL", redis_key_cmd, cluster_mbulk_zipstr_resp); -} +} /* }}} */ /* {{{ proto bool RedisCluster::hexists(string key, string member) */ @@ -733,7 +739,7 @@ PHP_METHOD(RedisCluster, decrby) { /* {{{ proto double RedisCluster::incrbyfloat(string key, double val) */ PHP_METHOD(RedisCluster, incrbyfloat) { - CLUSTER_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, + CLUSTER_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, cluster_dbl_resp); } /* }}} */ @@ -825,7 +831,7 @@ PHP_METHOD(RedisCluster, ltrim) { /* {{{ proto array RedisCluster::lrange(string key, long start, long end) */ PHP_METHOD(RedisCluster, lrange) { - CLUSTER_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, + CLUSTER_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, cluster_mbulk_resp); } /* }}} */ @@ -875,28 +881,28 @@ PHP_METHOD(RedisCluster, pfmerge) { /* {{{ proto boolean RedisCluster::restore(string key, long ttl, string val) */ PHP_METHOD(RedisCluster, restore) { - CLUSTER_PROCESS_KW_CMD("RESTORE", redis_key_long_str_cmd, + CLUSTER_PROCESS_KW_CMD("RESTORE", redis_key_long_str_cmd, cluster_bool_resp); } /* }}} */ /* {{{ proto long RedisCluster::setrange(string key, long offset, string val) */ PHP_METHOD(RedisCluster, setrange) { - CLUSTER_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, + CLUSTER_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, cluster_long_resp); } /* }}} */ -/* Generic implementation for ZRANGE, ZREVRANGE, ZRANGEBYSCORE, +/* Generic implementation for ZRANGE, ZREVRANGE, ZRANGEBYSCORE, * ZREVRANGEBYSCORE */ -static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, - zrange_cb fun) +static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, + zrange_cb fun) { redisCluster *c = GET_CONTEXT(); - char *cmd; int cmd_len; short slot; + char *cmd; int cmd_len; short slot; int withscores; - if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, + if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, &withscores, &slot, NULL)==FAILURE) { efree(cmd); @@ -918,7 +924,7 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, } } -/* {{{ proto +/* {{{ proto * array RedisCluster::zrange(string k, long s, long e, bool score=0) */ PHP_METHOD(RedisCluster, zrange) { generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE", @@ -926,7 +932,7 @@ PHP_METHOD(RedisCluster, zrange) { } /* }}} */ -/* {{{ proto +/* {{{ proto * array RedisCluster::zrevrange(string k,long s,long e,bool scores=0) */ PHP_METHOD(RedisCluster, zrevrange) { generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE", @@ -934,7 +940,7 @@ PHP_METHOD(RedisCluster, zrevrange) { } /* }}} */ -/* {{{ proto array +/* {{{ proto array * RedisCluster::zrangebyscore(string k, long s, long e, array opts) */ PHP_METHOD(RedisCluster, zrangebyscore) { generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE", @@ -1024,4 +1030,42 @@ PHP_METHOD(RedisCluster, object) { } } +/* Commands that do not interact with Redis, but just report stuff about + * various options, etc */ + +/* {{{ proto long RedisCluster::getOption(long option */ +PHP_METHOD(RedisCluster, getoption) { + redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, + GET_CONTEXT()->flags); +} +/* }}} */ + +/* {{{ proto bool RedisCluster::setOption(long option, mixed value) */ +PHP_METHOD(RedisCluster, setoption) { + redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, + GET_CONTEXT()->flags); +} +/* }}} */ + +/* {{{ proto string RedisCluster::_prefix(string key) */ +PHP_METHOD(RedisCluster, _prefix) { + redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, + GET_CONTEXT()->flags); +} +/* }}} */ + +/* {{{ proto string RedisCluster::_serialize(mixed val) */ +PHP_METHOD(RedisCluster, _serialize) { + redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, + GET_CONTEXT()->flags); +} +/* }}} */ + +/* {{{ proto mixed RedisCluster::_unserialize(string val) */ +PHP_METHOD(RedisCluster, _unserialize) { + redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, + GET_CONTEXT()->flags, redis_cluster_exception_ce); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index af3d41085f..7e5ea194fe 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -11,7 +11,7 @@ /* Get attached object context */ #define GET_CONTEXT() \ - (redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC) + ((redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC)) /* Command building/processing is identical for every command */ #define CLUSTER_BUILD_CMD(name, c, cmd, cmd_len, slot) \ @@ -162,4 +162,11 @@ PHP_METHOD(RedisCluster, zunionstore); PHP_METHOD(RedisCluster, zinterstore); PHP_METHOD(RedisCluster, sort); PHP_METHOD(RedisCluster, object); + +/* Introspection */ +PHP_METHOD(RedisCluster, getoption); +PHP_METHOD(RedisCluster, setoption); +PHP_METHOD(RedisCluster, _prefix); +PHP_METHOD(RedisCluster, _serialize); +PHP_METHOD(RedisCluster, _unserialize); #endif diff --git a/redis_commands.c b/redis_commands.c index 2bcc96e898..d251d9649f 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -19,6 +19,7 @@ */ #include "redis_commands.h" +#include /* Generic commands based on method signature and what kind of things we're * processing. Lots of Redis commands take something like key, value, or @@ -103,7 +104,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix our key if requested key_free = redis_key_prefix(redis_sock, &key, &key_len); - + // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sds", key, key_len, (int)lval, val, val_len); @@ -119,7 +120,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command construction when we just take a key and value */ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *val; @@ -150,7 +151,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command that takes a key and an unserialized value */ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *val; @@ -177,7 +178,7 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Key, string, string without serialization (ZCOUNT, ZREMRANGEBYSCORE) */ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *val1, *val2; @@ -208,7 +209,7 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command that takes two keys */ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key1, *key2; @@ -230,7 +231,7 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Slots where these keys resolve short slot1 = cluster_hash_key(key1, key1_len); short slot2 = cluster_hash_key(key2, key2_len); - + // Check if Redis would give us a CROSSLOT error if(slot1 != slot2) { php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -253,7 +254,7 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command construction where we take a key and a long */ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; @@ -287,7 +288,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* key, long, long */ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; @@ -317,7 +318,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command where we take a single key */ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; @@ -345,7 +346,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic command where we take a key and a double */ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; @@ -435,7 +436,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *withscores = (zend_hash_find(ht_opt,"withscores",sizeof("withscores"), (void**)&z_ele)==SUCCESS && Z_TYPE_PP(z_ele)==IS_BOOL && Z_BVAL_PP(z_ele)==1); - + // LIMIT if(zend_hash_find(ht_opt,"limit",sizeof("limit"),(void**)&z_ele) ==SUCCESS) @@ -449,14 +450,14 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, has_limit = 1; limit_low = Z_LVAL_PP(z_off); limit_high = Z_LVAL_PP(z_cnt); - } + } } - } - + } + // Prefix our key, set slot key_free = redis_key_prefix(redis_sock, &key, &key_len); CMD_SET_SLOT(slot,key,key_len); - + // Construct our command if(*withscores) { if(has_limit) { @@ -470,7 +471,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else { if(has_limit) { *cmd_len = redis_cmd_format_static(cmd, kw, "ssssdd", key, key_len, - start, start_len, end, end_len, "LIMIT", 5, limit_low, + start, start_len, end, end_len, "LIMIT", 5, limit_low, limit_high); } else { *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, @@ -486,7 +487,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* ZUNIONSTORE, ZINTERSTORE */ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *agg_op=NULL; @@ -498,8 +499,8 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int argc = 2, agg_op_len=0, keys_count; // Parse args - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a!s", &key, - &key_len, &z_keys, &z_weights, &agg_op, + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a!s", &key, + &key_len, &z_keys, &z_weights, &agg_op, &agg_op_len)==FAILURE) { return FAILURE; @@ -530,7 +531,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // AGGREGATE option if(agg_op_len != 0) { - if(strncasecmp(agg_op, "SUM", sizeof("SUM")) && + if(strncasecmp(agg_op, "SUM", sizeof("SUM")) && strncasecmp(agg_op, "MIN", sizeof("MIN")) && strncasecmp(agg_op, "MAX", sizeof("MAX"))) { @@ -545,12 +546,12 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix key key_free = redis_key_prefix(redis_sock, &key, &key_len); - + // Start building our command redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); redis_cmd_append_sstr(&cmdstr, key, key_len); redis_cmd_append_sstr_int(&cmdstr, keys_count); - + // Set our slot, free the key if we prefixed it CMD_SET_SLOT(slot,key,key_len); if(key_free) efree(key); @@ -651,7 +652,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd = cmdstr.c; *cmd_len = cmdstr.len; - return SUCCESS; + return SUCCESS; } /* Commands that take a key followed by a variable list of serializable @@ -717,7 +718,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, int kw_len, int min_argc, int has_timeout, + char *kw, int kw_len, int min_argc, int has_timeout, char **cmd, int *cmd_len, short *slot) { zval **z_args, **z_ele; @@ -771,7 +772,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key = Z_STRVAL_PP(z_ele); key_len = Z_STRLEN_PP(z_ele); key_free = redis_key_prefix(redis_sock, &key, &key_len); - + // Protect against CROSSLOT errors if(slot) { if(kslot == -1) { @@ -838,7 +839,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* * Commands with specific signatures or that need unique functions because they - * have specific processing (argument validation, etc) that make them unique + * have specific processing (argument validation, etc) that make them unique */ /* SET */ @@ -849,7 +850,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; int key_len, val_len, key_free, val_free; long expire = -1; - + // Make sure the function is being called correctly if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, &z_value, &z_opts)==FAILURE) @@ -966,10 +967,10 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Consistency with Redis, if timeout < 0 use RPOPLPUSH if(timeout < 0) { - *cmd_len = redis_cmd_format_static(cmd, "RPOPLPUSH", "ss", key1, + *cmd_len = redis_cmd_format_static(cmd, "RPOPLPUSH", "ss", key1, key1_len, key2, key2_len); } else { - *cmd_len = redis_cmd_format_static(cmd, "BRPOPLPUSH", "ssd", key1, + *cmd_len = redis_cmd_format_static(cmd, "BRPOPLPUSH", "ssd", key1, key1_len, key2, key2_len, timeout); } @@ -990,15 +991,15 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix our key if necissary + // Prefix our key if necissary key_free = redis_key_prefix(redis_sock, &key, &key_len); - + // Construct command *cmd_len = redis_cmd_format_static(cmd, "HINCRBY", "ssd", key, key_len, mem, mem_len, byval); // Set slot CMD_SET_SLOT(slot,key,key_len); - + // Success return SUCCESS; } @@ -1094,7 +1095,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate over members, appending as arguments for(i=0;i add_next_index_stringl(z_argv,"STORE",sizeof("STORE")-1, 1); add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),0); - + // We are using STORE *using_store = 1; } @@ -1889,7 +1890,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else { HashTable *ht_keys = Z_ARRVAL_PP(z_ele); int added=0; - + // Add our "GET" option add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,0); @@ -1904,9 +1905,9 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, continue; if(Z_TYPE_PP(z_key)!=IS_STRING) continue; - + // Add this key to our argv array - add_next_index_stringl(z_argv, Z_STRVAL_PP(z_key), + add_next_index_stringl(z_argv, Z_STRVAL_PP(z_key), Z_STRLEN_PP(z_key), 0); added++; } @@ -2007,7 +2008,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* HDEL */ -int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval **z_args; @@ -2047,7 +2048,7 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate through the members we're removing for(i=1;iserializer); + case REDIS_OPT_PREFIX: + if(redis_sock->prefix) { + RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len, 1); + } + RETURN_NULL(); + case REDIS_OPT_READ_TIMEOUT: + RETURN_DOUBLE(redis_sock->read_timeout); + case REDIS_OPT_SCAN: + RETURN_LONG(redis_sock->scan); + default: + RETURN_FALSE; + } +} + +void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock) +{ + long option, val_long; + char *val_str; + int val_len; + struct timeval read_tv; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &option, + &val_str, &val_len) == FAILURE) + { + RETURN_FALSE; + } + + switch(option) { + case REDIS_OPT_SERIALIZER: + val_long = atol(val_str); + if(val_long == REDIS_SERIALIZER_NONE +#ifdef HAVE_REDIS_IGBINARY + || val_long == REDIS_SERIALIZER_IGBINARY +#endif + || val_long == REDIS_SERIALIZER_PHP) + { + redis_sock->serializer = val_long; + RETURN_TRUE; + } else { + RETURN_FALSE; + } + break; + case REDIS_OPT_PREFIX: + if(redis_sock->prefix) { + efree(redis_sock->prefix); + } + if(val_len == 0) { + redis_sock->prefix = NULL; + redis_sock->prefix_len = 0; + } else { + redis_sock->prefix_len = val_len; + redis_sock->prefix = ecalloc(1+val_len, 1); + memcpy(redis_sock->prefix, val_str, val_len); + } + RETURN_TRUE; + case REDIS_OPT_READ_TIMEOUT: + redis_sock->read_timeout = atof(val_str); + if(redis_sock->stream) { + read_tv.tv_sec = (time_t)redis_sock->read_timeout; + read_tv.tv_usec = (int)((redis_sock->read_timeout - + read_tv.tv_sec) * 1000000); + php_stream_set_option(redis_sock->stream, + PHP_STREAM_OPTION_READ_TIMEOUT, 0, + &read_tv); + } + RETURN_TRUE; + case REDIS_OPT_SCAN: + val_long = atol(val_str); + if(val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) { + redis_sock->scan = val_long; + RETURN_TRUE; + } + RETURN_FALSE; + break; + default: + RETURN_FALSE; + } +} + +void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + char *key; + int key_len; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) + ==FAILURE) + { + RETURN_FALSE; + } + + if(redis_sock->prefix != NULL && redis_sock->prefix_len>0) { + redis_key_prefix(redis_sock, &key, &key_len); + RETURN_STRINGL(key, key_len, 0); + } else { + RETURN_STRINGL(key, key_len, 1); + } +} + +void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock) +{ + zval *z_val; + char *val; + int val_len; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_val)==FAILURE) { + RETURN_FALSE; + } + + int val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + + RETVAL_STRINGL(val, val_len, 1); + if(val_free) STR_FREE(val); +} + +void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zend_class_entry *ex) +{ + char *value; + int value_len; + + // Parse our arguments + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) + == FAILURE) + { + RETURN_FALSE; + } + + // We only need to attempt unserialization if we have a serializer running + if(redis_sock->serializer != REDIS_SERIALIZER_NONE) { + zval *z_ret = NULL; + if(redis_unserialize(redis_sock, value, value_len, &z_ret + TSRMLS_CC) == 0) + { + // Badly formed input, throw an execption + zend_throw_exception(ex, + "Invalid serialized data, or unserialization error", + 0 TSRMLS_CC); + RETURN_FALSE; + } + RETURN_ZVAL(z_ret, 0, 1); + } else { + // Just return the value that was passed to us + RETURN_STRINGL(value, value_len, 1); + } +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index bda22eeb19..8e189088ba 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -175,6 +175,22 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +/* Commands that don't communicate with Redis at all (such as getOption, + * setOption, _prefix, _serialize, etc). These can be handled in one place + * with the method of grabbing our RedisSock* object in different ways + * depending if this is a Redis object or a RedisCluster object. */ + +void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock); +void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock); +void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock); +void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock); +void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zend_class_entry *ex); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From ab429f5d98e40a9e3138ed9b0db2876c382c3cb9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Jun 2014 15:52:42 -0700 Subject: [PATCH 0321/1986] More formatting --- redis.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/redis.c b/redis.c index 3f18672f85..153fdce203 100644 --- a/redis.c +++ b/redis.c @@ -420,7 +420,8 @@ static void redis_destructor_redis_sock(zend_rsrc_list_entry * rsrc TSRMLS_DC) /** * redis_sock_get */ -PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) +PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, + int no_throw) { zval **socket; @@ -467,7 +468,8 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) zval *object; RedisSock *redis_sock; - // If we can't grab our object, or get a socket, or we're not connected, return NULL + // If we can't grab our object, or get a socket, or we're not connected, + // return NULL if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) || (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || @@ -804,7 +806,8 @@ PHP_METHOD(Redis, close) } /* }}} */ -/* {{{ proto boolean Redis::set(string key, mixed value, long timeout | array options) */ +/* {{{ proto boolean Redis::set(string key, mixed val, long timeout, + * [array opt) */ PHP_METHOD(Redis, set) { REDIS_PROCESS_CMD(set, redis_boolean_response); } @@ -2171,7 +2174,8 @@ PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, /* fetch again */ zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { + if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) + { /* this should never happen, according to the PHP people */ continue; } From cd87c04a21514f7d95f9913840f65cfb4b1a623e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 11 Jun 2014 12:45:09 -0700 Subject: [PATCH 0322/1986] Created cluster specific option handling Added distribution option to RedisCluster in preperation for implementation of commands which can be distributed by the client. The two distribution modes will be: * DIST_OOE : Maintain order of execution * DIST_SPEED : Most efficient delivery Either way, the RedisCluster object will need to return values that were gathered in the same way they were sent in, but might execute them in a different order if the option is set to DIST_SPEED. --- bybits.php | 25 +++++++++++++++++++++++++ cluster_library.h | 14 ++++++++++++-- redis.c | 31 ++++++++++++++++++++++++------- redis_cluster.c | 4 ++-- redis_commands.c | 34 ++++++++++++++++++++++++++++++++-- redis_commands.h | 4 ++-- 6 files changed, 97 insertions(+), 15 deletions(-) create mode 100644 bybits.php diff --git a/bybits.php b/bybits.php new file mode 100644 index 0000000000..f182990034 --- /dev/null +++ b/bybits.php @@ -0,0 +1,25 @@ + $count) { + echo $proto . "\t" . $count . "\n"; +} +?> diff --git a/cluster_library.h b/cluster_library.h index 0e34f79f6a..8f1a94fc5f 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -12,13 +12,20 @@ #define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) /* Minimum valid CLUSTER NODES line element count - * and the minimum we expect if there are slots */ + and the minimum we expect if there are slots */ #define CLUSTER_MIN_NODE_LINE 8 #define CLUSTER_MIN_SLOTS_COUNT 9 /* Length of a cluster name */ #define CLUSTER_NAME_LEN 40 +/* RedisCluster class constants */ +#define CLUSTER_OPT_DISTRIBUTE 5 + +/* Maintain order of execution vs. efficiency of delivery */ +#define CLUSTER_DIST_OOE 0 +#define CLUSTER_DIST_SPEED 1 + /* The parts for our cluster nodes command */ #define CLUSTER_NODES_HASH 0 #define CLUSTER_NODES_HOST_PORT 1 @@ -152,9 +159,12 @@ typedef struct redisCluster { /* The slot where we should read replies */ short reply_slot; - /* One RedisSock* object for serialization and prefix information */ + /* One RedisSock* struct for serialization and prefix information */ RedisSock *flags; + /* Cluster distribution mode (speed, vs. maintaining order of execution) */ + short dist_mode; + /* The first line of our last reply, not including our reply type byte * or the trailing \r\n */ char line_reply[1024]; diff --git a/redis.c b/redis.c index 153fdce203..32135aa0d6 100644 --- a/redis.c +++ b/redis.c @@ -484,7 +484,7 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) /* Redis and RedisCluster objects share serialization/prefixing settings so * this is a generic function to add class constants to either */ -static void add_class_constants(zend_class_entry *ce, int only_atomic) { +static void add_class_constants(zend_class_entry *ce, int is_cluster) { add_constant_long(ce, "REDIS_NOT_FOUND", REDIS_NOT_FOUND); add_constant_long(ce, "REDIS_STRING", REDIS_STRING); add_constant_long(ce, "REDIS_SET", REDIS_SET); @@ -493,12 +493,14 @@ static void add_class_constants(zend_class_entry *ce, int only_atomic) { add_constant_long(ce, "REDIS_HASH", REDIS_HASH); /* Cluster doesn't support pipelining at this time */ - if(!only_atomic) { - add_constant_long(ce, "ATOMIC", ATOMIC); - add_constant_long(ce, "MULTI", MULTI); + if(!is_cluster) { add_constant_long(ce, "PIPELINE", PIPELINE); } + /* Add common mode constants */ + add_constant_long(ce, "ATOMIC", ATOMIC); + add_constant_long(ce, "MULTI", MULTI); + /* options */ add_constant_long(ce, "OPT_SERIALIZER", REDIS_OPT_SERIALIZER); add_constant_long(ce, "OPT_PREFIX", REDIS_OPT_PREFIX); @@ -520,6 +522,16 @@ static void add_class_constants(zend_class_entry *ce, int only_atomic) { zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } +/* Settings specific to RedisCluster */ +static void add_cluster_constants(zend_class_entry *ce) { + /* options */ + add_constant_long(ce, "OPT_DISTRIBUTE", CLUSTER_OPT_DISTRIBUTE); + + /* Distribution settings */ + add_constant_long(ce, "DIST_OOE", CLUSTER_DIST_OOE); + add_constant_long(ce, "DIST_SPEED", CLUSTER_DIST_SPEED); +} + /** * PHP_MINIT_FUNCTION */ @@ -579,10 +591,13 @@ PHP_MINIT_FUNCTION(redis) redis_sock_name, module_number ); - /* Add class constants to Redis and RedisCluster objects */ + /* Add shared class constants to Redis and RedisCluster objects */ add_class_constants(redis_ce, 0); add_class_constants(redis_cluster_ce, 1); + /* Add specific RedisCluster class constants */ + add_cluster_constants(redis_cluster_ce); + return SUCCESS; } @@ -2941,7 +2956,8 @@ PHP_METHOD(Redis, getOption) { RETURN_FALSE; } - redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL); } /* }}} */ @@ -2953,7 +2969,8 @@ PHP_METHOD(Redis, setOption) { RETURN_FALSE; } - redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + NULL); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index c0b255862e..aee6541a23 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1036,14 +1036,14 @@ PHP_METHOD(RedisCluster, object) { /* {{{ proto long RedisCluster::getOption(long option */ PHP_METHOD(RedisCluster, getoption) { redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, - GET_CONTEXT()->flags); + GET_CONTEXT()->flags, GET_CONTEXT()); } /* }}} */ /* {{{ proto bool RedisCluster::setOption(long option, mixed value) */ PHP_METHOD(RedisCluster, setoption) { redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, - GET_CONTEXT()->flags); + GET_CONTEXT()->flags, GET_CONTEXT()); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index d251d9649f..5e4e3b3def 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2254,7 +2254,7 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * return value handling, and thread safety. */ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock) + RedisSock *redis_sock, redisCluster *c) { long option; @@ -2264,6 +2264,16 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } + // Check if we're asking for a RedisCluster specific option + if(c != NULL && option > REDIS_OPT_SCAN) { + switch(option) { + case CLUSTER_OPT_DISTRIBUTE: + RETURN_LONG(c->dist_mode); + default: + RETURN_FALSE; + } + } + // Return the requested option switch(option) { case REDIS_OPT_SERIALIZER: @@ -2283,7 +2293,7 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, } void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock) + RedisSock *redis_sock, redisCluster *c) { long option, val_long; char *val_str; @@ -2296,6 +2306,26 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } + // If we've been passed a RedisCluster object, check if the option + // being set is specific to RedisCluster. + if(c != NULL && option > REDIS_OPT_SCAN) { + switch(option) { + case CLUSTER_OPT_DISTRIBUTE: + val_long = atol(val_str); + if(val_long == CLUSTER_DIST_OOE || + val_long == CLUSTER_DIST_SPEED) + { + c->dist_mode = val_long; + RETURN_TRUE; + } else { + RETURN_FALSE; + } + break; + default: + RETURN_FALSE; + } + } + switch(option) { case REDIS_OPT_SERIALIZER: val_long = atol(val_str); diff --git a/redis_commands.h b/redis_commands.h index 8e189088ba..f02d7941a8 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -181,9 +181,9 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * depending if this is a Redis object or a RedisCluster object. */ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock); + RedisSock *redis_sock, redisCluster *c); void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock); + RedisSock *redis_sock, redisCluster *c); void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, From 6caee92868bb9957ff66b86a4d5d364c539747c9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 11 Jun 2014 16:26:03 -0700 Subject: [PATCH 0323/1986] Reformatting, spaces > tabs --- common.h | 182 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 92 insertions(+), 90 deletions(-) diff --git a/common.h b/common.h index 2f87cbbcca..53576ebc6f 100644 --- a/common.h +++ b/common.h @@ -11,10 +11,10 @@ #endif #define redis_sock_name "Redis Socket Buffer" -#define REDIS_SOCK_STATUS_FAILED 0 +#define REDIS_SOCK_STATUS_FAILED 0 #define REDIS_SOCK_STATUS_DISCONNECTED 1 -#define REDIS_SOCK_STATUS_UNKNOWN 2 -#define REDIS_SOCK_STATUS_CONNECTED 3 +#define REDIS_SOCK_STATUS_UNKNOWN 2 +#define REDIS_SOCK_STATUS_CONNECTED 3 #define redis_multi_access_type_name "Redis Multi type access" @@ -22,20 +22,20 @@ /* properties */ #define REDIS_NOT_FOUND 0 -#define REDIS_STRING 1 -#define REDIS_SET 2 -#define REDIS_LIST 3 -#define REDIS_ZSET 4 -#define REDIS_HASH 5 +#define REDIS_STRING 1 +#define REDIS_SET 2 +#define REDIS_LIST 3 +#define REDIS_ZSET 4 +#define REDIS_HASH 5 /* reply types */ typedef enum _REDIS_REPLY_TYPE { TYPE_EOF = -1, TYPE_LINE = '+', - TYPE_INT = ':', - TYPE_ERR = '-', - TYPE_BULK = '$', - TYPE_MULTIBULK = '*' + TYPE_INT = ':', + TYPE_ERR = '-', + TYPE_BULK = '$', + TYPE_MULTIBULK = '*' } REDIS_REPLY_TYPE; /* SCAN variants */ @@ -54,15 +54,15 @@ typedef enum _PUBSUB_TYPE { } PUBSUB_TYPE; /* options */ -#define REDIS_OPT_SERIALIZER 1 -#define REDIS_OPT_PREFIX 2 -#define REDIS_OPT_READ_TIMEOUT 3 -#define REDIS_OPT_SCAN 4 +#define REDIS_OPT_SERIALIZER 1 +#define REDIS_OPT_PREFIX 2 +#define REDIS_OPT_READ_TIMEOUT 3 +#define REDIS_OPT_SCAN 4 /* serializers */ -#define REDIS_SERIALIZER_NONE 0 -#define REDIS_SERIALIZER_PHP 1 -#define REDIS_SERIALIZER_IGBINARY 2 +#define REDIS_SERIALIZER_NONE 0 +#define REDIS_SERIALIZER_PHP 1 +#define REDIS_SERIALIZER_IGBINARY 2 /* SCAN options */ #define REDIS_SCAN_NORETRY 0 @@ -89,91 +89,93 @@ typedef enum _PUBSUB_TYPE { #define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC) #define IF_ATOMIC() if(redis_sock->mode == ATOMIC) #define ELSE_IF_MULTI() else if(redis_sock->mode == MULTI) { \ - if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ - RETURN_ZVAL(getThis(), 1, 0);\ - } else {\ - RETURN_FALSE;\ - } \ + if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ + RETURN_ZVAL(getThis(), 1, 0);\ + } else {\ + RETURN_FALSE;\ + } \ } -#define ELSE_IF_PIPELINE() else IF_PIPELINE() { \ - RETURN_ZVAL(getThis(), 1, 0);\ +#define ELSE_IF_PIPELINE() else IF_PIPELINE() { \ + RETURN_ZVAL(getThis(), 1, 0);\ } - #define MULTI_RESPONSE(callback) IF_MULTI_OR_PIPELINE() { \ - fold_item *f1, *current; \ - f1 = malloc(sizeof(fold_item)); \ - f1->fun = (void *)callback; \ - f1->next = NULL; \ - current = redis_sock->current;\ - if(current) current->next = f1; \ - redis_sock->current = f1; \ + fold_item *f1, *current; \ + f1 = malloc(sizeof(fold_item)); \ + f1->fun = (void *)callback; \ + f1->next = NULL; \ + current = redis_sock->current;\ + if(current) current->next = f1; \ + redis_sock->current = f1; \ } #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) request_item *tmp; \ - struct request_item *current_request;\ - tmp = malloc(sizeof(request_item));\ - tmp->request_str = calloc(cmd_len, 1);\ - memcpy(tmp->request_str, cmd, cmd_len);\ - tmp->request_size = cmd_len;\ - tmp->next = NULL;\ - current_request = redis_sock->pipeline_current; \ - if(current_request) {\ - current_request->next = tmp;\ - } \ - redis_sock->pipeline_current = tmp; \ - if(NULL == redis_sock->pipeline_head) { \ - redis_sock->pipeline_head = redis_sock->pipeline_current;\ - } - -#define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { \ - efree(cmd); \ + struct request_item *current_request;\ + tmp = malloc(sizeof(request_item));\ + tmp->request_str = calloc(cmd_len, 1);\ + memcpy(tmp->request_str, cmd, cmd_len);\ + tmp->request_size = cmd_len;\ + tmp->next = NULL;\ + current_request = redis_sock->pipeline_current; \ + if(current_request) {\ + current_request->next = tmp;\ + } \ + redis_sock->pipeline_current = tmp; \ + if(NULL == redis_sock->pipeline_head) { \ + redis_sock->pipeline_head = redis_sock->pipeline_current;\ + } + +#define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) \ + if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { \ + efree(cmd); \ RETURN_FALSE; \ } -#define REDIS_SAVE_CALLBACK(callback, closure_context) IF_MULTI_OR_PIPELINE() { \ - fold_item *f1, *current; \ - f1 = malloc(sizeof(fold_item)); \ - f1->fun = (void *)callback; \ - f1->ctx = closure_context; \ - f1->next = NULL; \ - current = redis_sock->current;\ - if(current) current->next = f1; \ - redis_sock->current = f1; \ - if(NULL == redis_sock->head) { \ - redis_sock->head = redis_sock->current;\ - }\ +#define REDIS_SAVE_CALLBACK(callback, closure_context) \ + IF_MULTI_OR_PIPELINE() { \ + fold_item *f1, *current; \ + f1 = malloc(sizeof(fold_item)); \ + f1->fun = (void *)callback; \ + f1->ctx = closure_context; \ + f1->next = NULL; \ + current = redis_sock->current;\ + if(current) current->next = f1; \ + redis_sock->current = f1; \ + if(NULL == redis_sock->head) { \ + redis_sock->head = redis_sock->current;\ + }\ } #define REDIS_ELSE_IF_MULTI(function, closure_context) \ -else if(redis_sock->mode == MULTI) { \ - if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ - REDIS_SAVE_CALLBACK(function, closure_context); \ - RETURN_ZVAL(getThis(), 1, 0);\ - } else {\ - RETURN_FALSE;\ - }\ + else if(redis_sock->mode == MULTI) { \ + if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ + REDIS_SAVE_CALLBACK(function, closure_context); \ + RETURN_ZVAL(getThis(), 1, 0);\ + } else {\ + RETURN_FALSE;\ + }\ } -#define REDIS_ELSE_IF_PIPELINE(function, closure_context) else IF_PIPELINE() { \ - REDIS_SAVE_CALLBACK(function, closure_context); \ - RETURN_ZVAL(getThis(), 1, 0);\ +#define REDIS_ELSE_IF_PIPELINE(function, closure_context) \ + else IF_PIPELINE() { \ + REDIS_SAVE_CALLBACK(function, closure_context); \ + RETURN_ZVAL(getThis(), 1, 0); \ } -#define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \ - IF_MULTI_OR_ATOMIC() { \ - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \ - efree(cmd); \ - }\ - IF_PIPELINE() { \ - PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \ - efree(cmd); \ - } +#define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \ + IF_MULTI_OR_ATOMIC() { \ + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \ + efree(cmd); \ + }\ + IF_PIPELINE() { \ + PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \ + efree(cmd); \ + } #define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ - REDIS_ELSE_IF_MULTI(function, closure_context) \ - REDIS_ELSE_IF_PIPELINE(function, closure_context); + REDIS_ELSE_IF_MULTI(function, closure_context) \ + REDIS_ELSE_IF_PIPELINE(function, closure_context); #define REDIS_PROCESS_RESPONSE(function) \ REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) @@ -236,15 +238,15 @@ else if(redis_sock->mode == MULTI) { \ typedef enum {ATOMIC, MULTI, PIPELINE} redis_mode; typedef struct fold_item { - zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, void *, ...); - void *ctx; - struct fold_item *next; + zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, void *, ...); + void *ctx; + struct fold_item *next; } fold_item; typedef struct request_item { - char *request_str; - int request_size; /* size_t */ - struct request_item *next; + char *request_str; + int request_size; /* size_t */ + struct request_item *next; } request_item; /* {{{ struct RedisSock */ From 4a88dd9d89241c0d6cd06ab1736eb78725f6461d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 11 Jun 2014 16:38:01 -0700 Subject: [PATCH 0324/1986] Moved our typedef below all defines --- cluster_library.h | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index 8f1a94fc5f..873c38bb32 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -37,12 +37,8 @@ #define CLUSTER_NODES_CONNECTED 7 #define CLUSTER_SLOTS 8 -/* Cluster redirection enum */ -typedef enum CLUSTER_REDIR_TYPE { - REDIR_NONE, - REDIR_MOVED, - REDIR_ASK -} CLUSTER_REDIR_TYPE; +/* Complete representation of a MULTI command in RESP */ +#define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ @@ -83,12 +79,48 @@ typedef enum CLUSTER_REDIR_TYPE { #define CLUSTER_CLEAR_REPLY(c) \ *c->line_reply = '\0'; c->reply_len = 0; +/* Cluster redirection enum */ +typedef enum CLUSTER_REDIR_TYPE { + REDIR_NONE, + REDIR_MOVED, + REDIR_ASK +} CLUSTER_REDIR_TYPE; + /* MULTI BULK response callback typedef */ typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void* TSRMLS_DC); /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); +typedef struct clusterDistArg { + /* Command payload */ + char *cmd; + + /* Length and size of our string */ + size_t size, len; +} clusterDistArg; + +typedef struct clusterDistCmd { + /* An array of multi-bulk argv values */ + clusterDistArg *argv; + + /* argv capacity and length */ + size_t size, argc; + + /* Our fully constructed command */ + smart_str cmd; +} clusterDistCmd; + +/* A structure to hold commands we'll distribute to multiple nodes, for + * example MGET, MSET, DEL */ +typedef struct clusterDistList { + /* An array of commands to distribute, by node */ + clusterDistCmd *commands; + + /* The size and length of our list */ + size_t size, len; +} clusterDistList; + /* Slot range structure */ typedef struct clusterSlotRange { unsigned short start, end; From 2884279a703f0dceafa643716a42d59b446fe529 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 11 Jun 2014 22:14:14 -0700 Subject: [PATCH 0325/1986] Formatting updates --- library.c | 306 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 181 insertions(+), 125 deletions(-) diff --git a/library.c b/library.c index e4c1b02dd5..cedb06d0e8 100644 --- a/library.c +++ b/library.c @@ -1495,10 +1495,12 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->stream != NULL) { redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; - if(!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } - redis_sock->stream = NULL; + + /* Stil valid? */ + if(redis_sock->stream && !redis_sock->persistent) { + php_stream_close(redis_sock->stream); + } + redis_sock->stream = NULL; return 1; } @@ -1506,7 +1508,8 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) return 0; } -PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) +PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock) { char *cmd; int response_len, cmd_len; @@ -1520,7 +1523,9 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r } efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) + { RETURN_FALSE; } @@ -1533,14 +1538,16 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r /** * redis_sock_set_err */ -PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { - /* Allocate/Reallocate our last error member */ - if(msg != NULL && msg_len > 0) { - if(redis_sock->err == NULL) { - redis_sock->err = emalloc(msg_len + 1); - } else if(msg_len > redis_sock->err_len) { - redis_sock->err = erealloc(redis_sock->err, msg_len +1); - } +PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, + int msg_len) +{ + // Allocate/Reallocate our last error member + if(msg != NULL && msg_len > 0) { + if(redis_sock->err == NULL) { + redis_sock->err = emalloc(msg_len + 1); + } else if(msg_len > redis_sock->err_len) { + redis_sock->err = erealloc(redis_sock->err, msg_len +1); + } /* Copy in our new error message, set new length, and null terminate */ memcpy(redis_sock->err, msg, msg_len); @@ -1564,7 +1571,9 @@ PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int /** * redis_sock_read_multibulk_reply */ -PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + void *ctx) { char inbuf[1024]; int numElems; @@ -1579,7 +1588,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "read error on connection", 0 + TSRMLS_CC); return -1; } @@ -1615,9 +1625,12 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, } /** - * Like multibulk reply, but don't touch the values, they won't be compressed. (this is used by HKEYS). + * Like multibulk reply, but don't touch the values, they won't be compressed. + * (this is used by HKEYS). */ -PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, + zval *z_tab, void *ctx) { char inbuf[1024]; int numElems; @@ -1632,7 +1645,8 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "read error on connection", + 0 TSRMLS_CC); return -1; } @@ -1732,9 +1746,12 @@ redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re } */ -/* Specialized multibulk processing for HMGET where we need to pair requested - * keys with their returned values */ -PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +/** + * redis_sock_read_multibulk_reply_assoc + */ +PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, + zval *z_tab, void *ctx) { char inbuf[1024], *response; int response_len; @@ -1752,7 +1769,8 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "read error on connection", + 0 TSRMLS_CC); return -1; } @@ -1776,15 +1794,18 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc TSRMLS_CC) == 1) { efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), + 1+Z_STRLEN_P(z_keys[i]), z); } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), + 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); } } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), + 1+Z_STRLEN_P(z_keys[i]), 0); } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); @@ -1803,10 +1824,12 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc /** * redis_sock_write */ -PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) +PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz + TSRMLS_DC) { if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { - zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "Connection closed", + 0 TSRMLS_CC); return -1; } if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { @@ -1836,8 +1859,10 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) efree(redis_sock); } -PHP_REDIS_API int -redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC) { +PHPAPI int +redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len + TSRMLS_DC) +{ #if ZEND_MODULE_API_NO >= 20100000 php_serialize_data_t ht; #else @@ -1903,7 +1928,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_ case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { /* ok */ + if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { *val = (char*)val8; *val_len = (int)sz; return 1; @@ -1991,24 +2016,29 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len) { * Processing for variant reply types (think EVAL) */ -PHP_REDIS_API int -redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) { - /* Handle EOF */ - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { +PHPAPI int +redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, + size_t *line_size TSRMLS_DC) +{ + // Handle EOF + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { return -1; } - if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { - /* Close, put our socket state into error */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) + == NULL) + { + // Close, put our socket state into error + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; - /* Throw a read error exception */ - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); - } + // Throw a read error exception + zend_throw_exception(redis_exception_ce, "read error on connection", + 0 TSRMLS_CC); + } /* We don't need \r\n */ *line_size-=2; @@ -2018,23 +2048,29 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_siz return 0; } -PHP_REDIS_API int -redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) { - /* Make sure we haven't lost the connection, even trying to reconnect */ - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { - /* Failure */ - return -1; - } +PHPAPI int +redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, + int *reply_info TSRMLS_DC) +{ + // Make sure we haven't lost the connection, even trying to reconnect + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + // Failure + return -1; + } - /* Attempt to read the reply-type byte */ - if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { - zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); - } + // Attempt to read the reply-type byte + if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { + zend_throw_exception(redis_exception_ce, "socket error on read socket", + 0 TSRMLS_CC); + } - /* If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here */ - if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { - /* Buffer to hold size information */ - char inbuf[255]; + // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can + // extract the value or size info here + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || + *reply_type == TYPE_MULTIBULK) + { + // Buffer to hold size information + char inbuf[255]; /* Read up to our newline */ if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { @@ -2052,11 +2088,13 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int * /* * Read a single line response, having already consumed the reply-type byte */ -PHP_REDIS_API int -redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC) { - /* Buffer to read our single line reply */ - char inbuf[1024]; - size_t line_size; +PHPAPI int +redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, + zval **z_ret TSRMLS_DC) +{ + // Buffer to read our single line reply + char inbuf[1024]; + size_t line_size; /* Attempt to read our single line reply */ if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { @@ -2082,10 +2120,12 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval return 0; } -PHP_REDIS_API int -redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) { - /* Attempt to read the bulk reply */ - char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); +PHPAPI int +redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret + TSRMLS_DC) +{ + // Attempt to read the bulk reply + char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); /* Set our reply to FALSE on failure, and the string on success */ if(bulk_resp == NULL) { @@ -2097,45 +2137,55 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) } } -PHP_REDIS_API int -redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { +PHPAPI int +redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret + TSRMLS_DC) +{ int reply_info; REDIS_REPLY_TYPE reply_type; zval *z_subelem; - /* Iterate while we have elements */ - while(elements > 0) { - /* Attempt to read our reply type */ - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); - return -1; - } + // Iterate while we have elements + while(elements > 0) { + // Attempt to read our reply type + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info + TSRMLS_CC) < 0) + { + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, + "protocol error, couldn't parse MULTI-BULK response\n", + reply_type); + return -1; + } - /* Switch on our reply-type byte */ - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_line(redis_sock, reply_type, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_INT: - /* Add our long value */ - add_next_index_long(*z_ret, reply_info); - break; - case TYPE_BULK: - /* Init a zval for our bulk response, read and add it */ - ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); - break; - case TYPE_MULTIBULK: - /* Construct an array for our sub element, and add it, and recurse */ - ALLOC_INIT_ZVAL(z_subelem); - array_init(z_subelem); - add_next_index_zval(*z_ret, z_subelem); - redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem TSRMLS_CC); - break; + // Switch on our reply-type byte + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_line(redis_sock, reply_type, &z_subelem + TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_INT: + // Add our long value + add_next_index_long(*z_ret, reply_info); + break; + case TYPE_BULK: + // Init a zval for our bulk response, read and add it + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_bulk(redis_sock, reply_info, &z_subelem + TSRMLS_CC); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_MULTIBULK: + // Construct an array for our sub element, and add it, + // and recurse + ALLOC_INIT_ZVAL(z_subelem); + array_init(z_subelem); + add_next_index_zval(*z_ret, z_subelem); + redis_read_multibulk_recursive(redis_sock, reply_info, + &z_subelem TSRMLS_CC); + break; default: // Stop the compiler from whinging break; @@ -2148,18 +2198,21 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret return 0; } -PHP_REDIS_API int -redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { - /* Reply type, and reply size vars */ - REDIS_REPLY_TYPE reply_type; - int reply_info; - /*char *bulk_resp; */ - zval *z_ret; - - /* Attempt to read our header */ - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - return -1; - } +PHPAPI int +redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab) +{ + // Reply type, and reply size vars + REDIS_REPLY_TYPE reply_type; + int reply_info; + //char *bulk_resp; + zval *z_ret; + + // Attempt to read our header + if(redis_read_reply_type(redis_sock,&reply_type,&reply_info TSRMLS_CC) < 0) + { + return -1; + } /* Our return ZVAL */ MAKE_STD_ZVAL(z_ret); @@ -2180,14 +2233,17 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv /* Initialize an array for our multi-bulk response */ array_init(z_ret); - /* If we've got more than zero elements, parse our multi bulk respoinse recursively */ - if(reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret TSRMLS_CC); - } - break; - default: - /* Protocol error */ - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); + // If we've got more than zero elements, parse our multi bulk + // response recursively + if(reply_info > -1) { + redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret + TSRMLS_CC); + } + break; + default: + // Protocol error + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, + "protocol error, got '%c' as reply-type byte\n", reply_type); return FAILURE; } From 8c17793209c6fab2ed2d89063cc5c0e2825e09e2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 12 Jun 2014 14:28:36 -0700 Subject: [PATCH 0326/1986] BUGFIX: Hash the key, not the last member! --- redis_commands.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 5e4e3b3def..52b05c9ad5 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1153,23 +1153,23 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_hash_has_more_elements_ex(ht_vals, &pos)==SUCCESS; zend_hash_move_forward_ex(ht_vals, &pos)) { - char *val, kbuf[40]; + char *mem, *val, kbuf[40]; int val_len, val_free; - unsigned int key_len; + unsigned int mem_len; zval **z_val; // Grab our key, and value for this element in our input - ktype = zend_hash_get_current_key_ex(ht_vals, &key, - &key_len, &idx, 0, &pos); + ktype = zend_hash_get_current_key_ex(ht_vals, &mem, + &mem_len, &idx, 0, &pos); zend_hash_get_current_data_ex(ht_vals, (void**)&z_val, &pos); // If the hash key is an integer, convert it to a string if(ktype != HASH_KEY_IS_STRING) { - key_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); - key = (char*)kbuf; + mem_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); + mem = (char*)kbuf; } else { // Length returned includes the \0 - key_len--; + mem_len--; } // Serialize value (if directed) @@ -1177,7 +1177,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, TSRMLS_CC); // Append the key and value to our command - redis_cmd_append_sstr(&cmdstr, key, key_len); + redis_cmd_append_sstr(&cmdstr, mem, mem_len); redis_cmd_append_sstr(&cmdstr, val, val_len); } From 95ecefcec6e805c2884503e8bce02e08bbb328ad Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 12 Jun 2014 14:30:15 -0700 Subject: [PATCH 0327/1986] Transaction support This is the initial commit supporting transactions in RedisCluster via MULTI..EXEC. Given that the data can be distributed across many nodes, we accomplish transactions as follows. 1. When entering MULTI mode, the cluster context state is changed to MULTI 2. When we send a command to a node, we check that node's RedisSock state, and if it's still ATOMIC, deliver the MULTI command, updating the node's state to MULTI. 3. When reading replies, we check if the RedisSock* is in a MULTI state, which means we need to issue the EXEC command. We do this and then flip the state back to ATOMIC. 4. On completion, everything is reverted to ATOMIC state and our reply callbacks are freed and set to NULL. For transactions, we enforce writes such that they MUST be able to be processed by the node in question (e.g. they can't work during a reshard, because there is no way to know what has failed, etc). This is the same as RedisCluster which will also cancel transactions in the case where a request was made for data that is somewhere else. --- cluster_library.c | 237 ++++++++++++++++++++++++++++++++++++++-------- cluster_library.h | 142 ++++++++++++++++++--------- library.c | 65 +++++++------ redis.c | 1 - redis_cluster.c | 89 ++++++++++++++++- redis_cluster.h | 52 ++++++++++ 6 files changed, 473 insertions(+), 113 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 566ef3473e..9ba7dff1e7 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -256,14 +256,11 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { return 0; } -/* - * Execute a CLUSTER NODES command against the given seed and return an array +/* Execute a CLUSTER NODES command against the given seed and return an array * of nodes that came back, along with setting a pointer as to how many there - * are. - */ + * are. */ static clusterNodeInfo -**cluster_get_nodes(redisCluster *cluster, int *len, RedisSock *redis_sock - TSRMLS_DC) +**cluster_get_nodes(redisCluster *c, int *len, RedisSock *redis_sock TSRMLS_DC) { clusterNodeInfo **nodes; REDIS_REPLY_TYPE type; @@ -330,7 +327,8 @@ cluster_node_create(redisCluster *cluster, clusterNodeInfo *info) { } // Pull in our contiguous slot ranges - node->slots = info->slots; + node->slots = emalloc(sizeof(clusterSlotRange)*info->slots_size); + memcpy(node->slots, info->slots, sizeof(clusterSlotRange)*info->slots_size); node->slots_size = info->slots_size; // Set slave flag @@ -379,6 +377,7 @@ PHPAPI void cluster_free_node(redisClusterNode *node) { zend_hash_destroy(node->slaves); efree(node->slaves); } + efree(node->slots); redis_free_socket(node->sock); efree(node); } @@ -804,11 +803,24 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, return 0; } -/* Attempt to write to a cluster node. If the node is NULL (e.g. - * it's been umapped, we keep falling back until we run out of nodes - * to try */ +/* Disconnect from each node we're connected to */ +PHPAPI void cluster_disconnect(redisCluster *c TSRMLS_DC) { + redisClusterNode **node; + + for(zend_hash_internal_pointer_reset(c->nodes); + zend_hash_get_current_data(c->nodes, (void**)&node)==SUCCESS; + zend_hash_move_forward(c->nodes)) + { + redis_sock_disconnect((*node)->sock TSRMLS_CC); + (*node)->sock->lazy_connect = 1; + } +} + +/* Attempt to write to a cluster node. If the node is NULL (e.g. it's been + * umapped, we keep falling back until we run out of nodes to try */ static int cluster_sock_write(redisCluster *c, unsigned short slot, - const char *cmd, size_t sz TSRMLS_DC) + const char *cmd, size_t sz, int + direct TSRMLS_DC) { RedisSock *redis_sock; redisClusterNode **seed_node; @@ -834,6 +846,9 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, return slot; } + // Don't fall back if direct communication with this slot is required. + if(direct) return -1; + // Fall back by attempting the request against every connected node for(zend_hash_internal_pointer_reset(c->nodes); zend_hash_has_more_elements(c->nodes)==SUCCESS; @@ -907,9 +922,91 @@ static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { node->slave = 0; } -/* Send a command to given slot in our cluster. If we get a MOVED - * or ASK error we attempt to send the command to the node as - * directed. */ +/* Send EXEC to a specific slot */ +PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { + // We have to be able to write this to the slot requested + if(cluster_sock_write(c, slot, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD)-1, 1 + TSRMLS_CC)==-1) + { + return -1; + } + + // We have to get a proper response from the slot to continue + if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || + c->reply_type != TYPE_MULTIBULK) + { + return -1; + } + + // Return the number of multi-bulk replies + return c->reply_len; +} + +/* Send DISCARD to a specific slot */ +PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { + if(cluster_sock_write(c, slot, RESP_DISCARD_CMD, sizeof(RESP_DISCARD_CMD)-1, + 1 TSRMLS_CC)==-1) + { + return -1; + } + + if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || + c->reply_type != TYPE_LINE) + { + return -1; + } + + return 0; +} + + +/* When we encounter an error on EXEC (e.g. we can't properly send the EXEC + * command or read a proper response, we try as a last ditch effort to discard + * on each node we haven't yet sent EXEC. If we can't do this, our connections + * are in an unknown state, and we have to reset our connections. */ +PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { + clusterFoldItem *fi = c->multi_head; + + // Loop through our fold items + while(fi) { + if(SLOT_SOCK(c,fi->slot)->mode == MULTI) { + if(cluster_send_discard(c, fi->slot TSRMLS_CC)<0) { + cluster_disconnect(c TSRMLS_CC); + return -1; + } + SLOT_SOCK(c,fi->slot)->mode = ATOMIC; + } + + fi = fi->next; + } + + // Success + return 0; +} + +/* Send MULTI to a given slot and consume the response. If we can't send the + * command OR we get an error in our response, we have to fail. */ +static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { + // We have to be able to communicate with the node we want + if(cluster_sock_write(c, slot, RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD)-1, 1 + TSRMLS_CC)==-1) + { + return -1; + } + + // We have to get a proper response + if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || + c->reply_type != TYPE_LINE) + { + return -1; + } + + // Success + return 0; +} + +/* Send a command to given slot in our cluster. If we get a MOVED or ASK error + * we attempt to send the command to the node as directed. */ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { @@ -917,8 +1014,22 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, // Issue commands until we find the right node or fail do { + // Send MULTI to the node if we haven't yet. + if(c->flags->mode == MULTI && SLOT_SOCK(c,slot)->mode != MULTI) { + // We have to fail if we can't send MULTI to the node + if(cluster_send_multi(c, slot TSRMLS_CC)==-1) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to enter MULTI mode on required slot", + 0 TSRMLS_CC); + return -1; + } + + // This node is now inside a transaction + SLOT_SOCK(c,slot)->mode = MULTI; + } + // Attempt to send the command to the slot requested - if((slot = cluster_sock_write(c, slot, cmd, cmd_len TSRMLS_CC))==-1) + if((slot = cluster_sock_write(c, slot, cmd, cmd_len, 0 TSRMLS_CC))==-1) { // We have no choice but to throw an exception. We // can't communicate with any node at all. @@ -938,6 +1049,15 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, if(resp == -1) { sleep(1); } else if(resp == 1) { + // If we get a MOVED response inside of a transaction, we have to + // abort, because the transaction would be invalid. + if(c->flags->mode == MULTI) { + zend_throw_exception(redis_cluster_exception_ce, + "Can't process MULTI sequence when cluster is resharding", + 0 TSRMLS_CC); + return -1; + } + // In case of a MOVED redirection, update our node mapping if(c->redir_type == REDIR_MOVED) { cluster_update_slot(c, rslot); @@ -975,11 +1095,15 @@ PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), c->reply_len TSRMLS_CC))==NULL) { - RETURN_FALSE; + if(c->flags->mode != MULTI) { + RETURN_FALSE; + } else { + add_next_index_bool(c->multi_resp, 0); + } } // Return our response raw - RETURN_STRINGL(resp,c->reply_len, 0); + CLUSTER_RETURN_STRING(c, resp, c->reply_len); } /* BULK response handler */ @@ -987,19 +1111,25 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; + zval *z_ret; // Make sure we can read the response if(c->reply_type != TYPE_BULK || (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), c->reply_len TSRMLS_CC))==NULL) { - RETURN_FALSE; + CLUSTER_RETURN_FALSE(c); } // Return the string if we can unserialize it - if(redis_unserialize(c->flags, resp, c->reply_len, &return_value)==0) { - RETURN_STRINGL(resp,c->reply_len,0); + if(redis_unserialize(c->flags, resp, c->reply_len, &z_ret)==0) { + CLUSTER_RETURN_STRING(c, resp, c->reply_len); } else { + if(CLUSTER_IS_ATOMIC(c)) { + return_value = z_ret; + } else { + add_next_index_zval(c->multi_resp, z_ret); + } efree(resp); } } @@ -1016,14 +1146,14 @@ PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), c->reply_len TSRMLS_CC))==NULL) { - RETURN_FALSE; + CLUSTER_RETURN_FALSE(c); } // Convert to double, free response dbl = atof(resp); efree(resp); - RETURN_DOUBLE(dbl); + CLUSTER_RETURN_DOUBLE(c, dbl); } /* A boolean response. If we get here, we've consumed the '+' reply @@ -1035,10 +1165,10 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, if(c->reply_type != TYPE_LINE || c->reply_len != 2 || c->line_reply[0] != 'O' || c->line_reply[1] != 'K') { - RETURN_FALSE; + CLUSTER_RETURN_FALSE(c); } - RETURN_TRUE; + CLUSTER_RETURN_BOOL(c, 1); } /* 1 or 0 response, for things like SETNX */ @@ -1047,10 +1177,10 @@ PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, { // Validate our reply type, and check for a zero if(c->reply_type != TYPE_INT || c->reply_len == 0) { - RETURN_FALSE; + CLUSTER_RETURN_FALSE(c); } - RETURN_TRUE; + CLUSTER_RETURN_BOOL(c, 1); } /* Generic integer response */ @@ -1058,9 +1188,9 @@ PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if(c->reply_type != TYPE_INT) { - RETURN_FALSE; + CLUSTER_RETURN_FALSE(c); } - RETURN_LONG(c->reply_len); + CLUSTER_RETURN_LONG(c, c->reply_len); } /* TYPE response handler */ @@ -1069,20 +1199,20 @@ PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, { // Make sure we got the right kind of response if(c->reply_type != TYPE_LINE) { - RETURN_FALSE; + CLUSTER_RETURN_FALSE(c); } // Switch on the type if(strncmp(c->line_reply, "+string", 7)==0) { - RETURN_LONG(REDIS_STRING); + CLUSTER_RETURN_LONG(c, REDIS_STRING); } else if(strncmp(c->line_reply, "+set", 4)==0) { - RETURN_LONG(REDIS_SET); + CLUSTER_RETURN_LONG(c, REDIS_SET); } else if(strncmp(c->line_reply, "+list", 5)==0) { - RETURN_LONG(REDIS_LIST); + CLUSTER_RETURN_LONG(c, REDIS_LIST); } else if(strncmp(c->line_reply, "+hash", 5)==0) { - RETURN_LONG(REDIS_HASH); + CLUSTER_RETURN_LONG(c, REDIS_HASH); } else { - RETURN_LONG(REDIS_NOT_FOUND); + CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND); } } @@ -1095,7 +1225,7 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, // Verify our reply type byte is correct and that this isn't a NULL // (e.g. -1 count) multi bulk response. if(c->reply_type != TYPE_MULTIBULK || c->reply_len == -1) { - RETURN_FALSE; + CLUSTER_RETURN_FALSE(c); } // Allocate array @@ -1108,12 +1238,43 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, { zval_dtor(z_result); FREE_ZVAL(z_result); - RETURN_FALSE; + CLUSTER_RETURN_FALSE(c); } // Success, make this array our return value - *return_value = *z_result; - efree(z_result); + if(CLUSTER_IS_ATOMIC(c)) { + *return_value = *z_result; + efree(z_result); + } else { + add_next_index_zval(c->multi_resp, z_result); + } +} + +/* MULTI MULTI BULK reply (for EXEC) */ +PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx) +{ + MAKE_STD_ZVAL(c->multi_resp); + array_init(c->multi_resp); + + clusterFoldItem *fi = c->multi_head; + while(fi) { + if(cluster_check_response(c, fi->slot, &c->reply_type TSRMLS_CC)<0) { + zval_dtor(c->multi_resp); + efree(c->multi_resp); + RETURN_FALSE; + } + + // Override reply slot, process response, move on + c->reply_slot = fi->slot; + fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx); + fi = fi->next; + } + + // Set our return array + zval_dtor(return_value); + *return_value = *c->multi_resp; + efree(c->multi_resp); } /* Raw MULTI BULK reply */ diff --git a/cluster_library.h b/cluster_library.h index 873c38bb32..8190dbaf05 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -37,8 +37,10 @@ #define CLUSTER_NODES_CONNECTED 7 #define CLUSTER_SLOTS 8 -/* Complete representation of a MULTI command in RESP */ -#define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" +/* Complete representation for MULTI and EXEC in RESP */ +#define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" +#define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" +#define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ @@ -79,6 +81,54 @@ #define CLUSTER_CLEAR_REPLY(c) \ *c->line_reply = '\0'; c->reply_len = 0; +/* Helper to determine if we're in MULTI mode */ +#define CLUSTER_IS_ATOMIC(c) (c->flags->mode != MULTI) + +/* Helper that either returns false or adds false in multi mode */ +#define CLUSTER_RETURN_FALSE(c) \ + if(CLUSTER_IS_ATOMIC(c)) { \ + RETURN_FALSE; \ + } else { \ + add_next_index_bool(c->multi_resp, 0); \ + return; \ + } + +/* Helper to either return a bool value or add it to MULTI response */ +#define CLUSTER_RETURN_BOOL(c, b) \ + if(CLUSTER_IS_ATOMIC(c)) { \ + if(b==1) {\ + RETURN_TRUE; \ + } else {\ + RETURN_FALSE; \ + } \ + } else { \ + add_next_index_bool(c->multi_resp, b); \ + } + +/* Helper to respond with a double or add it to our MULTI response */ +#define CLUSTER_RETURN_DOUBLE(c, d) \ + if(CLUSTER_IS_ATOMIC(c)) { \ + RETURN_DOUBLE(d); \ + } else { \ + add_next_index_double(c->multi_resp, d); \ + } + +/* Helper to return a string value */ +#define CLUSTER_RETURN_STRING(c, str, len) \ + if(CLUSTER_IS_ATOMIC(c)) { \ + RETURN_STRINGL(str, len, 0); \ + } else { \ + add_next_index_stringl(c->multi_resp, str, len, 0); \ + } \ + +/* Return a LONG value */ +#define CLUSTER_RETURN_LONG(c, val) \ + if(CLUSTER_IS_ATOMIC(c)) { \ + RETURN_LONG(val); \ + } else { \ + add_next_index_long(c->multi_resp, val); \ + } + /* Cluster redirection enum */ typedef enum CLUSTER_REDIR_TYPE { REDIR_NONE, @@ -87,40 +137,11 @@ typedef enum CLUSTER_REDIR_TYPE { } CLUSTER_REDIR_TYPE; /* MULTI BULK response callback typedef */ -typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void* TSRMLS_DC); +typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void* TSRMLS_DC); /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); -typedef struct clusterDistArg { - /* Command payload */ - char *cmd; - - /* Length and size of our string */ - size_t size, len; -} clusterDistArg; - -typedef struct clusterDistCmd { - /* An array of multi-bulk argv values */ - clusterDistArg *argv; - - /* argv capacity and length */ - size_t size, argc; - - /* Our fully constructed command */ - smart_str cmd; -} clusterDistCmd; - -/* A structure to hold commands we'll distribute to multiple nodes, for - * example MGET, MSET, DEL */ -typedef struct clusterDistList { - /* An array of commands to distribute, by node */ - clusterDistCmd *commands; - - /* The size and length of our list */ - size_t size, len; -} clusterDistList; - /* Slot range structure */ typedef struct clusterSlotRange { unsigned short start, end; @@ -135,9 +156,7 @@ typedef struct clusterNodeInfo { char *host; int host_len; - unsigned short port; - - unsigned short slave; + unsigned short port, slave; clusterSlotRange *slots; size_t slots_size; @@ -163,6 +182,8 @@ typedef struct redisClusterNode { HashTable *slaves; } redisClusterNode; +typedef struct clusterFoldItem clusterFoldItem; + /* RedisCluster implementation structure */ typedef struct redisCluster { /* Object reference for Zend */ @@ -181,6 +202,13 @@ typedef struct redisCluster { /* All RedisCluster objects we've created/are connected to */ HashTable *nodes; + /* Transaction handling linked list, and where we are as we EXEC */ + clusterFoldItem *multi_head; + clusterFoldItem *multi_curr; + + /* Variable to store MULTI response */ + zval *multi_resp; + /* How many failures have we had in a row */ int failures; @@ -213,25 +241,47 @@ typedef struct redisCluster { unsigned short redir_port; } redisCluster; +/* RedisCluster response processing callback */ +typedef void (*cluster_cb)(INTERNAL_FUNCTION_PARAMETERS, redisCluster*, void*); + +/* Context for processing transactions */ +struct clusterFoldItem { + /* Response processing callback */ + cluster_cb callback; + + /* The slot where this response was sent */ + short slot; + + /* Any context we need to send to our callback */ + void *ctx; + + /* Next item in our list */ + struct clusterFoldItem *next; +}; + /* Hash a key to it's slot, using the Redis Cluster hash algorithm */ unsigned short cluster_hash_key_zval(zval *key); unsigned short cluster_hash_key(const char *key, int len); -/* Send a command to where we think the key(s) should live and redirect when - * needed */ -PHPAPI short cluster_send_command(redisCluster *cluster, short slot, - const char *cmd, int cmd_len TSRMLS_DC); +PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, + int cmd_len TSRMLS_DC); -PHPAPI int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds); -PHPAPI int cluster_map_keyspace(redisCluster *cluster TSRMLS_DC); +PHPAPI void cluster_disconnect(redisCluster *c TSRMLS_DC); + +PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC); +PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); +PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC); +PHPAPI int cluster_reset_multi(redisCluster *c); + +PHPAPI int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); +PHPAPI int cluster_map_keyspace(redisCluster *c TSRMLS_DC); PHPAPI void cluster_free_node(redisClusterNode *node); PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, - int *len TSRMLS_DC); + int *len TSRMLS_DC); -PHPAPI int cluster_node_add_slave(redisCluster *cluster, - redisClusterNode *master, - clusterNodeInfo *slave TSRMLS_DC); +PHPAPI int cluster_node_add_slave(redisCluster *c, redisClusterNode *master, + clusterNodeInfo *slave TSRMLS_DC); /* * Redis Cluster response handlers. All of our response handlers take the @@ -270,6 +320,8 @@ PHPAPI void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, diff --git a/library.c b/library.c index cedb06d0e8..dff875b6ea 100644 --- a/library.c +++ b/library.c @@ -1188,7 +1188,9 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, Re int response_len; char ret; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) + { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); return; @@ -1219,7 +1221,9 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) + { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); return; @@ -1248,12 +1252,17 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } /* like string response, but never unserialized. */ -PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +PHPAPI void +redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) + { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); return; @@ -1332,10 +1341,10 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock /** * redis_sock_create */ -PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, - double timeout, int persistent, char *persistent_id, - long retry_interval, - zend_bool lazy_connect) +PHPAPI RedisSock* +redis_sock_create(char *host, int host_len, unsigned short port, double timeout, + int persistent, char *persistent_id, long retry_interval, + zend_bool lazy_connect) { RedisSock *redis_sock; @@ -1401,32 +1410,33 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } read_tv.tv_sec = (time_t)redis_sock->read_timeout; - read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); + read_tv.tv_usec = (int)((redis_sock->read_timeout-read_tv.tv_sec)*1000000); if(redis_sock->host[0] == '/' && redis_sock->port < 1) { host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); } else { if(redis_sock->port == 0) redis_sock->port = 6379; - host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, redis_sock->port); + host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, + redis_sock->port); } if (redis_sock->persistent) { - if (redis_sock->persistent_id) { - spprintf(&persistent_id, 0, "phpredis:%s:%s", host, redis_sock->persistent_id); - } else { - spprintf(&persistent_id, 0, "phpredis:%s:%f", host, redis_sock->timeout); - } + if (redis_sock->persistent_id) { + spprintf(&persistent_id, 0, "phpredis:%s:%s", host, + redis_sock->persistent_id); + } else { + spprintf(&persistent_id, 0, "phpredis:%s:%f", host, + redis_sock->timeout); + } } - redis_sock->stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE, - STREAM_XPORT_CLIENT - | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &errstr, &err - ); + redis_sock->stream = php_stream_xport_create(host, host_len, + ENFORCE_SAFE_MODE, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, + persistent_id, tv_ptr, NULL, &errstr, &err); if (persistent_id) { - efree(persistent_id); + efree(persistent_id); } efree(host); @@ -1438,17 +1448,17 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) /* set TCP_NODELAY */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; - setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); + setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, + sizeof(int)); php_stream_auto_cleanup(redis_sock->stream); if(tv.tv_sec != 0 || tv.tv_usec != 0) { - php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT, - 0, &read_tv); + php_stream_set_option(redis_sock->stream,PHP_STREAM_OPTION_READ_TIMEOUT, + 0, &read_tv); } php_stream_set_option(redis_sock->stream, - PHP_STREAM_OPTION_WRITE_BUFFER, - PHP_STREAM_BUFFER_NONE, NULL); + PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; @@ -1458,7 +1468,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) /** * redis_sock_server_open */ -PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC) +PHPAPI int +redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC) { int res = -1; diff --git a/redis.c b/redis.c index 32135aa0d6..8839e82f3f 100644 --- a/redis.c +++ b/redis.c @@ -2409,7 +2409,6 @@ free_reply_callbacks(zval *z_this, RedisSock *redis_sock) { /* exec */ PHP_METHOD(Redis, exec) { - RedisSock *redis_sock; char *cmd; int cmd_len; diff --git a/redis_cluster.c b/redis_cluster.c index aee6541a23..e0a168abca 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -39,8 +39,11 @@ zend_class_entry *spl_rte_ce = NULL; /* Function table */ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, __construct, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, close, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, get, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, set, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, mget, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, mset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, psetex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setnx, NULL, ZEND_ACC_PUBLIC) @@ -142,6 +145,10 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _serialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _unserialize, NULL, ZEND_ACC_PUBLIC) + + PHP_ME(RedisCluster, multi, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, exec, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} }; @@ -302,10 +309,16 @@ PHP_METHOD(RedisCluster, __construct) { cluster_map_keyspace(context TSRMLS_CC); } -/* - * RedisCluster methods +/* + * RedisCluster method implementation */ +/* {{{ proto bool RedisCluster::close() */ +PHP_METHOD(RedisCluster, close) { + cluster_disconnect(GET_CONTEXT() TSRMLS_CC); + RETURN_TRUE; +} + /* {{{ proto string RedisCluster::get(string key) */ PHP_METHOD(RedisCluster, get) { CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp); @@ -318,6 +331,16 @@ PHP_METHOD(RedisCluster, set) { } /* }}} */ +/* {{{ proto array RedisCluster::mget(string key1, ... string keyN) */ +PHP_METHOD(RedisCluster, mget) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Coming soon!"); +} + +/* {{{ proto bool RedisCluster::mset(string key1, string val1, ... kN, vN) */ +PHP_METHOD(RedisCluster, mset) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Coming soon!"); +} + /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, @@ -1068,4 +1091,66 @@ PHP_METHOD(RedisCluster, _unserialize) { } /* }}} */ +/* + * Transaction handling + */ + +/* {{{ proto bool RedisCluster::multi() */ +PHP_METHOD(RedisCluster, multi) { + redisCluster *c = GET_CONTEXT(); + + if(c->flags->mode == MULTI) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "RedisCluster is already in MULTI mode, ignoring"); + RETURN_FALSE; + } + + // Go into MULTI mode + c->flags->mode = MULTI; + + // Success + RETURN_TRUE; +} + +/* {{{ proto array RedisCluster::exec() */ +PHP_METHOD(RedisCluster, exec) { + redisCluster *c = GET_CONTEXT(); + clusterFoldItem *fi; + + // Verify we are in fact in multi mode + if(CLUSTER_IS_ATOMIC(c)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "RedisCluster is not in MULTI mode"); + RETURN_FALSE; + } + + // First pass, send EXEC and abort on failure + fi = c->multi_head; + while(fi) { + if(SLOT_SOCK(c, fi->slot)->mode == MULTI) { + if(cluster_send_exec(c, fi->slot TSRMLS_CC)<0) { + cluster_abort_exec(c TSRMLS_CC); + + zend_throw_exception(redis_cluster_exception_ce, + "Error processing EXEC across the cluster", + 0 TSRMLS_CC); + + // Free our queue, reset MULTI state + CLUSTER_FREE_QUEUE(c); + CLUSTER_RESET_MULTI(c); + + RETURN_FALSE; + } + SLOT_SOCK(c, fi->slot)->mode = ATOMIC; + } + fi = fi->next; + } + + // MULTI multi-bulk response handler + cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + + // Free our queue + CLUSTER_FREE_QUEUE(c); +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 7e5ea194fe..9fb6f59c17 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -18,6 +18,44 @@ redis_##name##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, \ &cmd_len, &slot) +/* Append information required to handle MULTI commands to the tail of our MULTI + * linked list. */ +#define CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx) \ + clusterFoldItem *_item; \ + _item = emalloc(sizeof(clusterFoldItem)); \ + _item->callback = cb; \ + _item->slot = slot; \ + _item->ctx = ctx; \ + _item->next = NULL; \ + if(c->multi_head == NULL) { \ + c->multi_head = _item; \ + c->multi_curr = _item; \ + } else { \ + c->multi_curr->next = _item; \ + c->multi_curr = _item; \ + } \ + +/* Simple macro to free our enqueued callbacks after we EXEC */ +#define CLUSTER_FREE_QUEUE(c) \ + clusterFoldItem *_item = c->multi_head, *_tmp; \ + while(_item) { \ + _tmp = _item->next; \ + efree(_item); \ + _item = _tmp; \ + } \ + c->multi_head = c->multi_curr = NULL; \ + +/* Reset anything flagged as MULTI */ +#define CLUSTER_RESET_MULTI(c) \ + redisClusterNode **_node; \ + for(zend_hash_internal_pointer_reset(c->nodes); \ + zend_hash_get_current_data(c->nodes, (void**)&_node); \ + zend_hash_move_forward(c->nodes)) \ + { \ + (*_node)->sock->mode = ATOMIC; \ + } + + /* Simple 1-1 command -> response macro */ #define CLUSTER_PROCESS_CMD(cmdname, resp_func) \ redisCluster *c = GET_CONTEXT(); \ @@ -31,6 +69,10 @@ RETURN_FALSE; \ } \ efree(cmd); \ + if(c->flags->mode == MULTI) { \ + CLUSTER_ENQUEUE_RESPONSE(c, slot, resp_func, ctx); \ + RETURN_ZVAL(getThis(), 1, 0); \ + } \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); /* More generic processing, where only the keyword differs */ @@ -46,6 +88,10 @@ RETURN_FALSE; \ } \ efree(cmd); \ + if(c->flags->mode == MULTI) { \ + CLUSTER_ENQUEUE_RESPONSE(c, slot, resp_func, ctx); \ + RETURN_ZVAL(getThis(), 1, 0); \ + } \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); /* For the creation of RedisCluster specific exceptions */ @@ -63,8 +109,11 @@ void init_rediscluster(TSRMLS_D); /* RedisCluster method implementation */ PHP_METHOD(RedisCluster, __construct); +PHP_METHOD(RedisCluster, close); PHP_METHOD(RedisCluster, get); PHP_METHOD(RedisCluster, set); +PHP_METHOD(RedisCluster, mget); +PHP_METHOD(RedisCluster, mset); PHP_METHOD(RedisCluster, dump); PHP_METHOD(RedisCluster, setex); PHP_METHOD(RedisCluster, psetex); @@ -169,4 +218,7 @@ PHP_METHOD(RedisCluster, setoption); PHP_METHOD(RedisCluster, _prefix); PHP_METHOD(RedisCluster, _serialize); PHP_METHOD(RedisCluster, _unserialize); + +PHP_METHOD(RedisCluster, multi); +PHP_METHOD(RedisCluster, exec); #endif From 241c6cdcb7b66674936c0a2dae12d88a55b660b5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 12 Jun 2014 15:07:45 -0700 Subject: [PATCH 0328/1986] Fixed HMGET, more MULTI support Fixed HMGET so we don't leak memory when getting members that don't exist. Added a call to CLUSTER_RESET_MULTI() at the end of our EXEC function, so afterwards we're not in MULTI mode. --- cluster_library.c | 20 ++++++++++++-------- redis_cluster.c | 3 ++- redis_cluster.h | 4 ++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 9ba7dff1e7..815e631a6d 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1448,15 +1448,19 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, // Loop while we've got replies while(count--) { line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); - if(!line) return -1; - - if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { - efree(line); - add_assoc_zval_ex(z_result,Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), z); + + if(line != NULL) { + if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { + efree(line); + add_assoc_zval_ex(z_result,Z_STRVAL_P(z_keys[i]), + 1+Z_STRLEN_P(z_keys[i]), z); + } else { + add_assoc_stringl_ex(z_result, Z_STRVAL_P(z_keys[i]), + 1+Z_STRLEN_P(z_keys[i]), line, line_len, 0); + } } else { - add_assoc_stringl_ex(z_result, Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), line, line_len, 0); + add_assoc_bool_ex(z_result, Z_STRVAL_P(z_keys[i]), + 1+Z_STRLEN_P(z_keys[i]), 0); } // Clean up key context diff --git a/redis_cluster.c b/redis_cluster.c index e0a168abca..e4280ab7a8 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1149,8 +1149,9 @@ PHP_METHOD(RedisCluster, exec) { // MULTI multi-bulk response handler cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - // Free our queue + // Free our queue, and reset MULTI state CLUSTER_FREE_QUEUE(c); + CLUSTER_RESET_MULTI(c); } /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 9fb6f59c17..b625c2f06b 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -53,8 +53,8 @@ zend_hash_move_forward(c->nodes)) \ { \ (*_node)->sock->mode = ATOMIC; \ - } - + } \ + c->flags->mode = ATOMIC; /* Simple 1-1 command -> response macro */ #define CLUSTER_PROCESS_CMD(cmdname, resp_func) \ From 846f945f7df803d003d8c6c45e628e72ac736f7c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 12 Jun 2014 15:55:42 -0700 Subject: [PATCH 0329/1986] DISCARD command, better cleanup. Implemented our DISCARD command for RedisCluster Added a mechanism where we can free any context we have (which is currently always a zval** array for HMGET arguments), even if we manually DISCARD the transaction OR if the transactino were to actually fail. --- cluster_library.c | 13 +++++++------ redis_cluster.c | 20 ++++++++++++++++++++ redis_cluster.h | 10 ++++++++++ redis_commands.c | 8 ++++++-- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 815e631a6d..f5c77eca80 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -960,10 +960,9 @@ PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { } -/* When we encounter an error on EXEC (e.g. we can't properly send the EXEC - * command or read a proper response, we try as a last ditch effort to discard - * on each node we haven't yet sent EXEC. If we can't do this, our connections - * are in an unknown state, and we have to reset our connections. */ +/* Abort any transaction in process, by sending DISCARD to any nodes that + * have active transactions in progress. If we can't send DISCARD, we need + * to disconnect as it would leave us in an undefined state. */ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { clusterFoldItem *fi = c->multi_head; @@ -976,10 +975,12 @@ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { } SLOT_SOCK(c,fi->slot)->mode = ATOMIC; } - fi = fi->next; } - + + // Update our overall cluster state + c->flags->mode = ATOMIC; + // Success return 0; } diff --git a/redis_cluster.c b/redis_cluster.c index e4280ab7a8..970b9534e4 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -148,6 +148,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, multi, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exec, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, discard, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -1154,4 +1155,23 @@ PHP_METHOD(RedisCluster, exec) { CLUSTER_RESET_MULTI(c); } +/* {{{ proto bool RedisCluster::discard() */ +PHP_METHOD(RedisCluster, discard) { + redisCluster *c = GET_CONTEXT(); + + if(CLUSTER_IS_ATOMIC(c)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Cluster is not in MULTI mode"); + RETURN_FALSE; + } + + if(cluster_abort_exec(c TSRMLS_CC)<0) { + CLUSTER_RESET_MULTI(c); + } + + CLUSTER_FREE_QUEUE(c); + + RETURN_TRUE; +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index b625c2f06b..f44b12a227 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -40,6 +40,15 @@ clusterFoldItem *_item = c->multi_head, *_tmp; \ while(_item) { \ _tmp = _item->next; \ + if(_item->ctx) { \ + zval **z_arr = (zval**)_item->ctx; int i=0; \ + while(z_arr[i]!=NULL) { \ + zval_dtor(z_arr[i]); \ + efree(z_arr[i]); \ + i++; \ + } \ + efree(z_arr); \ + } \ efree(_item); \ _item = _tmp; \ } \ @@ -221,4 +230,5 @@ PHP_METHOD(RedisCluster, _unserialize); PHP_METHOD(RedisCluster, multi); PHP_METHOD(RedisCluster, exec); +PHP_METHOD(RedisCluster, discard); #endif diff --git a/redis_commands.c b/redis_commands.c index 52b05c9ad5..25f073513d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1061,8 +1061,8 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix our key key_free = redis_key_prefix(redis_sock, &key, &key_len); - // Allocate memory for the max members we'll grab - z_mems = ecalloc(count, sizeof(zval*)); + // Allocate memory for mems+1 so we can have a sentinel + z_mems = ecalloc(count+1, sizeof(zval*)); // Iterate over our member array for(zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); @@ -1089,6 +1089,10 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } + // Sentinel so we can free this even if it's used and then we discard + // the transaction manually or there is a transaction failure + z_mems[valid]=NULL; + // Start command construction redis_cmd_init_sstr(&cmdstr, valid+1, "HMGET", sizeof("HMGET")-1); redis_cmd_append_sstr(&cmdstr, key, key_len); From 2451c75ed8fbb11d4f444a2c68dc69e5941f8e95 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 13 Jun 2014 16:41:37 -0700 Subject: [PATCH 0330/1986] WATCH command Implemented the WATCH command for RedisCluster. This command can take any number of keys, so phpredis splits the request across the cluster in the best way it can. For every key in a multiple key command, Redis Cluster requires that they all hash to the same SLOT, else it will return a CROSSLOT error and fail. For WATCH in RedisCluster, a few things to note: * The command will fail if phpredis is out of sync with the keyspace. This is because we'll need to know where to deliver each command, or can't possibly deliver them correctlhy. * The command will fail if any command delivery failures occur on any node. This is the case either for a normal communication error or if RedisCluster returns to us MOVED/ASK redirection. --- cluster_library.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++ cluster_library.h | 29 ++++++++++ library.c | 113 +++++++++++------------------------- php_redis.h | 10 ++-- redis.c | 10 ++-- redis_cluster.c | 94 +++++++++++++++++++++++++++++- redis_cluster.h | 7 ++- redis_commands.h | 4 ++ 8 files changed, 316 insertions(+), 94 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index f5c77eca80..de407f436f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -28,6 +28,129 @@ static void cluster_dump_nodes(redisCluster *c) { } } +/* Cluster key distribution helpers. For a small handlful of commands, we + * want to distribute them across 1-N nodes. These methods provide + * simple containers for the purposes of splitting keys/values in this way */ + +/* Free cluster distribution list inside a HashTable */ +static void cluster_dist_free_ht(void *p) { + clusterDistList *dl = *(clusterDistList**)p; + int i; + + for(i=0; i < dl->len; i++) { + if(dl->entry[i].key_free) efree(dl->entry[i].key); + if(dl->entry[i].val_free) efree(dl->entry[i].val); + } + + efree(dl->entry); + efree(dl); +} + +/* Spin up a HashTable that will contain distribution lists */ +HashTable *cluster_dist_create() { + HashTable *ret; + + ALLOC_HASHTABLE(ret); + zend_hash_init(ret, 0, NULL, cluster_dist_free_ht, 0); + + return ret; +} + +/* Free distribution list */ +void cluster_dist_free(HashTable *ht) { + zend_hash_destroy(ht); + efree(ht); +} + +/* Create a clusterDistList object */ +static clusterDistList *cluster_dl_create() { + clusterDistList *dl; + + dl = emalloc(sizeof(clusterDistList)); + dl->entry = emalloc(CLUSTER_KEYDIST_ALLOC * sizeof(clusterKeyVal)); + dl->size = CLUSTER_KEYDIST_ALLOC; + dl->len = 0; + + return dl; +} + +/* Add a key to a dist list, returning the keval entry */ +static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, + int key_len, int key_free) +{ + // Reallocate if required + if(dl->len==dl->size) { + dl->entry = erealloc(dl->entry, sizeof(clusterKeyVal) * dl->size * 2); + dl->size *= 2; + } + + // Set key info + dl->entry[dl->len].key = key; + dl->entry[dl->len].key_len = key_len; + dl->entry[dl->len].key_free = key_free; + + // NULL out any values + dl->entry[dl->len].val = NULL; + dl->entry[dl->len].val_len = 0; + dl->entry[dl->len].val_free = 0; + + return &(dl->entry[dl->len++]); +} + +/* Add a key, returning a pointer to the entry where passed for easy adding + * of values to match this key */ +int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, + int key_len, clusterKeyVal **kv) +{ + int key_free; + short slot; + clusterDistList **ppdl, *dl; + clusterKeyVal *retptr; + + // Prefix our key and hash it + key_free = redis_key_prefix(c->flags, &key, &key_len); + slot = cluster_hash_key(key, key_len); + + // We can't do this if we don't fully understand the keyspace + if(c->master[slot] == NULL) { + if(key_free) efree(key); + return FAILURE; + } + + // Look for this slot + if(zend_hash_index_find(ht, (ulong)slot, (void**)&ppdl)==FAILURE) { + dl = cluster_dl_create(); + zend_hash_index_update(ht, (ulong)slot, (void**)&dl, + sizeof(clusterDistList*), NULL); + } else { + dl = *ppdl; + } + + // Now actually add this key + retptr = cluster_dl_add_key(dl, key, key_len, key_free); + + // Push our return pointer if requested + if(kv) *kv = retptr; + + return SUCCESS; +} + +/* Provided a clusterKeyVal, add a value */ +void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val + TSRMLS_CC) +{ + char *val; + int val_len, val_free; + + // Serialize our value + val_free = redis_serialize(c->flags, z_val, &val, &val_len TSRMLS_CC); + + // Attach it to the provied keyval entry + kv->val = val; + kv->val_len = val_len; + kv->val_free = val_free; +} + /* Set our last error string encountered */ static void cluster_set_err(redisCluster *c, char *err, int err_len) { @@ -1006,6 +1129,26 @@ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { return 0; } +/* Send a command to a specific node (without falling back), in addition we + * can check for a reply type if not sent as TYPE_EOF */ +PHPAPI int cluster_send_direct(redisCluster *c, short slot, char *cmd, + int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC) +{ + // Try only this node + if(cluster_sock_write(c, slot, cmd, cmd_len, 1 TSRMLS_CC)==-1) { + return -1; + } + + // Require a +OK response + if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || + (rtype != TYPE_EOF && rtype != c->reply_type)) + { + return -1; + } + + return 0; +} + /* Send a command to given slot in our cluster. If we get a MOVED or ASK error * we attempt to send the command to the node as directed. */ PHPAPI short cluster_send_command(redisCluster *c, short slot, diff --git a/cluster_library.h b/cluster_library.h index 8190dbaf05..f292737d83 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -51,6 +51,9 @@ #define MOVED_LEN (sizeof("MOVED ")-1) #define ASK_LEN (sizeof("ASK ")-1) +/* Initial allocation size for key distribution container */ +#define CLUSTER_KEYDIST_ALLOC 8 + /* Slot/RedisSock/RedisSock->stream macros */ #define SLOT(c,s) (c->master[s]) #define SLOT_SOCK(c,s) (SLOT(c,s)->sock) @@ -182,6 +185,7 @@ typedef struct redisClusterNode { HashTable *slaves; } redisClusterNode; +/* Forward decl */ typedef struct clusterFoldItem clusterFoldItem; /* RedisCluster implementation structure */ @@ -259,6 +263,28 @@ struct clusterFoldItem { struct clusterFoldItem *next; }; +/* Key and value container, with info if they need freeing */ +typedef struct clusterKeyVal { + char *key, *val; + int key_len, val_len; + int key_free, val_free; +} clusterKeyVal; + +/* Container to hold keys (and possibly values) for when we need to distribute + * commands across more than 1 node (e.g. WATCH, MGET, MSET, etc) */ +typedef struct clusterDistList { + clusterKeyVal *entry; + size_t len, size; +} clusterDistList; + +/* Cluster distribution helpers */ +HashTable *cluster_dist_create(); +void cluster_dist_free(HashTable *ht); +int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, + int key_len, clusterKeyVal **kv); +void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val + TSRMLS_CC); + /* Hash a key to it's slot, using the Redis Cluster hash algorithm */ unsigned short cluster_hash_key_zval(zval *key); unsigned short cluster_hash_key(const char *key, int len); @@ -273,6 +299,9 @@ PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC); PHPAPI int cluster_reset_multi(redisCluster *c); +PHPAPI int cluster_send_direct(redisCluster *c, short slot, char *cmd, + int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC); + PHPAPI int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); PHPAPI int cluster_map_keyspace(redisCluster *c TSRMLS_DC); PHPAPI void cluster_free_node(redisClusterNode *node); diff --git a/library.c b/library.c index dff875b6ea..0922a0a442 100644 --- a/library.c +++ b/library.c @@ -147,15 +147,16 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) eof = php_stream_eof(redis_sock->stream); for (; eof; count++) { - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ - if(redis_sock->stream) { /* close stream if still here */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->watching = 0; - } - zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); + if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ + if(redis_sock->stream) { /* close stream if still here */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->watching = 0; + } + zend_throw_exception(redis_exception_ce, "Connection lost", + 0 TSRMLS_CC); return -1; } if(redis_sock->stream) { /* close existing stream before reconnecting */ @@ -352,8 +353,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D /* Handle stale data error */ if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { zend_throw_exception(redis_exception_ce, - "SYNC with master in progress", - 0 TSRMLS_CC); + "SYNC with master in progress", 0 TSRMLS_CC); } return NULL; case '$': @@ -946,7 +946,10 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo } } -PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) { +PHPAPI void +redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, + SuccessCallback success_callback) +{ char *response; int response_len; @@ -983,16 +986,25 @@ PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, Red } } -PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, NULL); +PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + void *ctx) +{ + redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, ctx, NULL); } -PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * z_tab, void *ctx) { +PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval * z_tab, + void *ctx) +{ char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) + { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); return; @@ -1042,68 +1054,10 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, zval *z_ret; HashTable *keytable; - MAKE_STD_ZVAL(z_ret); - array_init(z_ret); - keytable = Z_ARRVAL_P(z_tab); - - for(zend_hash_internal_pointer_reset(keytable); - zend_hash_has_more_elements(keytable) == SUCCESS; - zend_hash_move_forward(keytable)) { - - char *tablekey, *hkey, *hval; - unsigned int tablekey_len; - int hkey_len; - unsigned long idx; - zval **z_key_pp, **z_value_pp; - - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ - } - - /* get current value, a key */ - convert_to_string(*z_key_pp); - hkey = Z_STRVAL_PP(z_key_pp); - hkey_len = Z_STRLEN_PP(z_key_pp); - - /* move forward */ - zend_hash_move_forward(keytable); - - /* fetch again */ - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { - continue; /* this should never happen, according to the PHP people. */ - } - - /* get current value, a hash value now. */ - hval = Z_STRVAL_PP(z_value_pp); - - /* Decode the score depending on flag */ - if (decode == SCORE_DECODE_INT && Z_STRLEN_PP(z_value_pp) > 0) { - add_assoc_long_ex(z_ret, hkey, 1+hkey_len, atoi(hval+1)); - } else if (decode == SCORE_DECODE_DOUBLE) { - add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); - } else { - zval *z = NULL; - MAKE_STD_ZVAL(z); - *z = **z_value_pp; - zval_copy_ctor(z); - add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); - } - } - - /* replace */ - zval_dtor(z_tab); - *z_tab = *z_ret; - zval_copy_ctor(z_tab); - zval_dtor(z_ret); - - efree(z_ret); -} - -static int -redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, int unserialize, int decode) +PHPAPI int +redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, + zval *z_tab, int flag) { char inbuf[1024]; int numElems; @@ -1118,7 +1072,8 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, + "read error on connection", 0 TSRMLS_CC); return -1; } diff --git a/php_redis.h b/php_redis.h index cce5b29ac0..0368b17a8a 100644 --- a/php_redis.h +++ b/php_redis.h @@ -271,13 +271,13 @@ ZEND_END_MODULE_GLOBALS(redis) #endif struct redis_queued_item { - /* reading function */ - zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ...); + /* reading function */ + zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ...); - char *cmd; - int cmd_len; + char *cmd; + int cmd_len; - struct redis_queued_item *next; + struct redis_queued_item *next; }; extern zend_module_entry redis_module_entry; diff --git a/redis.c b/redis.c index 8839e82f3f..10a49264fc 100644 --- a/redis.c +++ b/redis.c @@ -721,8 +721,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl", &object, redis_ce, &host, - &host_len, &port, &timeout, &persistent_id, - &persistent_id_len, &retry_interval) + &host_len, &port, &timeout, &persistent_id, &persistent_id_len, &retry_interval) == FAILURE) { return FAILURE; @@ -1466,8 +1465,7 @@ PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_elements++; /* pattern */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], - "$%d", pattern_len); + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", pattern_len); cmd_elements++; cmd_lines[cmd_elements] = emalloc(pattern_len + 1); memcpy(cmd_lines[cmd_elements], pattern, pattern_len); @@ -2119,6 +2117,7 @@ PHP_METHOD(Redis, hDel) { REDIS_PROCESS_CMD(hdel, redis_long_response); } +/* }}} */ /* {{{ proto bool Redis::hExists(string key, string mem) */ PHP_METHOD(Redis, hExists) @@ -2137,7 +2136,7 @@ PHP_METHOD(Redis, hKeys) /* {{{ proto array Redis::hvals(string key) */ PHP_METHOD(Redis, hVals) { - REDIS_PROCESS_KW_CMD("HVALS", redis_key_cmd, + REDIS_PROCESS_KW_CMD("HVALS", redis_key_cmd, redis_sock_read_multibulk_reply); } @@ -2235,6 +2234,7 @@ PHP_METHOD(Redis, hIncrBy) PHP_METHOD(Redis, hMget) { REDIS_PROCESS_CMD(hmget, redis_sock_read_multibulk_reply_assoc); } +/* }}} */ /* {{{ proto bool Redis::hmset(string key, array keyvals) */ PHP_METHOD(Redis, hMset) diff --git a/redis_cluster.c b/redis_cluster.c index 970b9534e4..271ec7123a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -139,7 +139,6 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, object, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) @@ -149,6 +148,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, multi, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exec, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, discard, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, watch, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -652,7 +652,6 @@ PHP_METHOD(RedisCluster, zrevrank) { } /* }}} */ - /* {{{ proto long RedisCluster::hlen(string key) */ PHP_METHOD(RedisCluster, hlen) { CLUSTER_PROCESS_KW_CMD("HLEN", redis_key_cmd, cluster_long_resp); @@ -1027,7 +1026,7 @@ PHP_METHOD(RedisCluster, sort) { } } -/* {{{ proto Redis::object(string subcmd, string key) */ +/* {{{ proto RedisCluster::object(string subcmd, string key) */ PHP_METHOD(RedisCluster, object) { redisCluster *c = GET_CONTEXT(); char *cmd; int cmd_len; short slot; @@ -1113,6 +1112,95 @@ PHP_METHOD(RedisCluster, multi) { RETURN_TRUE; } +/* {{{ proto bool RedisCluster::watch() */ +PHP_METHOD(RedisCluster, watch) { + redisCluster *c = GET_CONTEXT(); + HashTable *ht_dist; + clusterDistList **dl; + smart_str cmd = {0}; + zval **z_args; + int argc = ZEND_NUM_ARGS(), i; + ulong slot; + + // Disallow in MULTI mode + if(c->flags->mode == MULTI) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "WATCH command not allowed in MULTI mode"); + RETURN_FALSE; + } + + // Don't need to process zero arguments + if(!argc) RETURN_FALSE; + + // Create our distribution HashTable + ht_dist = cluster_dist_create(); + + // Allocate args, and grab them + z_args = emalloc(sizeof(zval*)*argc); + if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + efree(z_args); + cluster_dist_free(ht_dist); + RETURN_FALSE; + } + + // Loop through arguments, prefixing if needed + for(i=0;ilen, "WATCH", sizeof("WATCH")-1); + for(i=0;i<(*dl)->len;i++) { + redis_cmd_append_sstr(&cmd, (*dl)->entry[i].key, + (*dl)->entry[i].key_len); + } + + // Send this command directly to our server + if(cluster_send_direct(c, (short)slot, cmd.c, cmd.len, TYPE_LINE)==-1) { + zend_throw_exception(redis_cluster_exception_ce, + "WATCH command failed. Nodes are possibly resharding", + 0 TSRMLS_CC); + RETURN_FALSE; + } + + // Zero out our command buffer + cmd.len = 0; + } + + // Cleanup + cluster_dist_free(ht_dist); + efree(z_args); + efree(cmd.c); + + RETURN_TRUE; +} + /* {{{ proto array RedisCluster::exec() */ PHP_METHOD(RedisCluster, exec) { redisCluster *c = GET_CONTEXT(); diff --git a/redis_cluster.h b/redis_cluster.h index f44b12a227..3c4905a44b 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -61,9 +61,11 @@ zend_hash_get_current_data(c->nodes, (void**)&_node); \ zend_hash_move_forward(c->nodes)) \ { \ - (*_node)->sock->mode = ATOMIC; \ + (*_node)->sock->watching = 0; \ + (*_node)->sock->mode = ATOMIC; \ } \ - c->flags->mode = ATOMIC; + c->flags->watching = 0; \ + c->flags->mode = ATOMIC; \ /* Simple 1-1 command -> response macro */ #define CLUSTER_PROCESS_CMD(cmdname, resp_func) \ @@ -231,4 +233,5 @@ PHP_METHOD(RedisCluster, _unserialize); PHP_METHOD(RedisCluster, multi); PHP_METHOD(RedisCluster, exec); PHP_METHOD(RedisCluster, discard); +PHP_METHOD(RedisCluster, watch); #endif diff --git a/redis_commands.h b/redis_commands.h index f02d7941a8..679ffadae8 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -5,6 +5,10 @@ #include "library.h" #include "cluster_library.h" +/* Pick a random slot, any slot (for stuff like publish/subscribe) */ +#define CMD_RAND_SLOT(slot,key,key_len) \ + if(slot) *slot = rand() % REDIS_CLUSTER_MOD + /* Macro for setting the slot if we've been asked to */ #define CMD_SET_SLOT(slot,key,key_len) \ if(slot) *slot = cluster_hash_key(key,key_len); From 8890994857b19ec07d4b8b374ab6f0b7dd43c2ee Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 13 Jun 2014 17:12:56 -0700 Subject: [PATCH 0331/1986] UNWATCH command. Added the UNWATCH command which will deliver this command only to nodes that we think we're watching. --- cluster_library.c | 8 ++++---- cluster_library.h | 5 +++-- redis_cluster.c | 33 ++++++++++++++++++++++++++++++--- redis_cluster.h | 1 + 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index de407f436f..3cb5523828 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1097,6 +1097,7 @@ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { return -1; } SLOT_SOCK(c,fi->slot)->mode = ATOMIC; + SLOT_SOCK(c,fi->slot)->watching = 0; } fi = fi->next; } @@ -1129,10 +1130,9 @@ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { return 0; } -/* Send a command to a specific node (without falling back), in addition we - * can check for a reply type if not sent as TYPE_EOF */ -PHPAPI int cluster_send_direct(redisCluster *c, short slot, char *cmd, - int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC) +/* Send a command to a specific slot */ +PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, + int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC) { // Try only this node if(cluster_sock_write(c, slot, cmd, cmd_len, 1 TSRMLS_CC)==-1) { diff --git a/cluster_library.h b/cluster_library.h index f292737d83..79edb0f1b2 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -37,10 +37,11 @@ #define CLUSTER_NODES_CONNECTED 7 #define CLUSTER_SLOTS 8 -/* Complete representation for MULTI and EXEC in RESP */ +/* Complete representation for various MULTI/EXEC commands in RESP */ #define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" #define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" #define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" +#define RESP_UNWATCH_CMD "*1\r\n$7\r\nUNWATCH\r\n" /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ @@ -299,7 +300,7 @@ PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC); PHPAPI int cluster_reset_multi(redisCluster *c); -PHPAPI int cluster_send_direct(redisCluster *c, short slot, char *cmd, +PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC); PHPAPI int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); diff --git a/redis_cluster.c b/redis_cluster.c index 271ec7123a..9eff7f688d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -149,7 +149,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, exec, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, discard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, watch, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(RedisCluster, unwatch, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -1182,13 +1182,16 @@ PHP_METHOD(RedisCluster, watch) { } // Send this command directly to our server - if(cluster_send_direct(c, (short)slot, cmd.c, cmd.len, TYPE_LINE)==-1) { + if(cluster_send_slot(c, (short)slot, cmd.c, cmd.len, TYPE_LINE)==-1) { zend_throw_exception(redis_cluster_exception_ce, "WATCH command failed. Nodes are possibly resharding", 0 TSRMLS_CC); RETURN_FALSE; } + // This node is watching + SLOT_SOCK(c, (short)slot)->watching = 1; + // Zero out our command buffer cmd.len = 0; } @@ -1201,6 +1204,29 @@ PHP_METHOD(RedisCluster, watch) { RETURN_TRUE; } +/* {{{ proto bool RedisCluster::unwatch() */ +PHP_METHOD(RedisCluster, unwatch) { + redisCluster *c = GET_CONTEXT(); + short slot; + + // Send UNWATCH to nodes that need it + for(slot=0;slotmaster[slot] && SLOT_SOCK(c,slot)->watching) { + if(cluster_send_slot(c, slot, RESP_UNWATCH_CMD, + sizeof(RESP_UNWATCH_CMD)-1, + TYPE_LINE TSRMLS_CC)==-1) + { + CLUSTER_RETURN_BOOL(c, 0); + } + + // No longer watching + SLOT_SOCK(c,slot)->watching = 0; + } + } + + CLUSTER_RETURN_BOOL(c, 1); +} + /* {{{ proto array RedisCluster::exec() */ PHP_METHOD(RedisCluster, exec) { redisCluster *c = GET_CONTEXT(); @@ -1230,7 +1256,8 @@ PHP_METHOD(RedisCluster, exec) { RETURN_FALSE; } - SLOT_SOCK(c, fi->slot)->mode = ATOMIC; + SLOT_SOCK(c, fi->slot)->mode = ATOMIC; + SLOT_SOCK(c, fi->slot)->watching = 0; } fi = fi->next; } diff --git a/redis_cluster.h b/redis_cluster.h index 3c4905a44b..e9d6d6b5ab 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -234,4 +234,5 @@ PHP_METHOD(RedisCluster, multi); PHP_METHOD(RedisCluster, exec); PHP_METHOD(RedisCluster, discard); PHP_METHOD(RedisCluster, watch); +PHP_METHOD(RedisCluster, unwatch); #endif From 32d1f407d863958d137dfe94647f2c59e9b189c7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 13 Jun 2014 20:27:12 -0700 Subject: [PATCH 0332/1986] SUBSCRIBE/PSUBSCRIBE Implemented SUBSCRIBE and PSUBSCRIBE for Redis Cluster. Currently Cluster will send publish messages to every node, so we can subscribe wherever we like. --- cluster_library.c | 152 +++++++++++++++++++++++++++++++++++++++++++++- cluster_library.h | 7 ++- redis_cluster.c | 15 +++++ redis_cluster.h | 14 +++-- redis_commands.c | 63 +++++++++++++++++++ redis_commands.h | 13 +++- 6 files changed, 255 insertions(+), 9 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 3cb5523828..e4d7ae9a09 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1,6 +1,7 @@ #include "php_redis.h" #include "common.h" #include "library.h" +#include "redis_commands.h" #include "cluster_library.h" #include "crc16.h" #include @@ -1360,6 +1361,122 @@ PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } } +/* SUBSCRIBE/PSCUBSCRIBE handler */ +PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + subscribeContext *sctx = (subscribeContext*)ctx; + zval *z_tab, **z_tmp, *z_ret, **z_args[4]; + int pull=0; + + // Consume each MULTI BULK response (one per channel/pattern) + while(sctx->argc--) { + z_tab = cluster_sub_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + pull, mbulk_resp_loop_raw); + + if(!z_tab) { + efree(sctx); + RETURN_FALSE; + } + + if(zend_hash_index_find(Z_ARRVAL_P(z_tab),0,(void**)&z_tmp)==FAILURE || + strcasecmp(Z_STRVAL_PP(z_tmp), sctx->kw) != 0) + { + zval_dtor(z_tab); + FREE_ZVAL(z_tab); + efree(sctx); + RETURN_FALSE; + } + + zval_dtor(z_tab); + pull = 1; + } + + // Set up our callback pointers + sctx->cb.retval_ptr_ptr = &z_ret; + sctx->cb.params = z_args; + sctx->cb.no_separation = 0; + + /* Multibulk response, {[pattern], type, channel, payload} */ + while(1) { + /* Arguments */ + zval **z_type, **z_chan, **z_pat, **z_data; + int tab_idx=1, is_pmsg; + + // Get the next subscribe response + z_tab = cluster_sub_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + 1, mbulk_resp_loop); + + if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&z_type) + ==FAILURE) + { + break; + } + + // Make sure we have a message or pmessage + if(!strncmp(Z_STRVAL_PP(z_type), "message", 7) || + !strncmp(Z_STRVAL_PP(z_type), "pmessage", 8)) + { + is_pmsg = *Z_STRVAL_PP(z_type) == 'p'; + } else { + zval_dtor(z_tab); + efree(z_tab); + continue; + } + + if(is_pmsg && zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, + (void**)&z_pat)==FAILURE) + { + break; + } + + // Extract channel and data + if(zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, + (void**)&z_chan)==FAILURE || + zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, + (void**)&z_data)==FAILURE) + { + break; + } + + // Always pass our object through + z_args[0] = &getThis(); + + // Set up calbacks depending on type + if(is_pmsg) { + z_args[1] = z_pat; + z_args[2] = z_chan; + z_args[3] = z_data; + } else { + z_args[1] = z_chan; + z_args[2] = z_data; + } + + // Set arg count + sctx->cb.param_count = tab_idx; + + // Execute our callback + if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC)!= + SUCCESS) + { + break; + } + + zval_dtor(z_tab); + efree(z_tab); + } + + // Cleanup + efree(sctx); + if(z_tab) { + zval_dtor(z_tab); + efree(z_tab); + } + + // Failure + RETURN_FALSE; +} + /* Generic MULTI BULK response processor */ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb cb, void *ctx) @@ -1378,7 +1495,7 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, // Call our specified callback if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len, ctx TSRMLS_CC) - ==FAILURE) + ==FAILURE) { zval_dtor(z_result); FREE_ZVAL(z_result); @@ -1394,6 +1511,39 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } } +/* MULTI BULK response loop where we might pull the next one */ +PHPAPI zval *cluster_sub_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, int pull, mbulk_cb cb) +{ + zval *z_result; + + // Pull our next response if directed + if(pull) { + if(cluster_check_response(c, c->reply_slot, &c->reply_type + TSRMLS_CC)<0) + { + return NULL; + } + } + + // Validate reply type and length + if(c->reply_type != TYPE_MULTIBULK || c->reply_len == -1) { + return NULL; + } + + MAKE_STD_ZVAL(z_result); + array_init(z_result); + + // Call our callback + if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len, NULL)==FAILURE) { + zval_dtor(z_result); + FREE_ZVAL(z_result); + return NULL; + } + + return z_result; +} + /* MULTI MULTI BULK reply (for EXEC) */ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) diff --git a/cluster_library.h b/cluster_library.h index 79edb0f1b2..e311521a3f 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -326,8 +326,7 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); +PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, @@ -336,6 +335,8 @@ PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); /* MULTI BULK response functions */ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, @@ -352,6 +353,8 @@ PHPAPI void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHPAPI zval *cluster_sub_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, int pull, mbulk_cb cb); /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, diff --git a/redis_cluster.c b/redis_cluster.c index 9eff7f688d..bd0c3b6d44 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -139,6 +139,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, object, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, subscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, psubscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) @@ -1053,6 +1056,18 @@ PHP_METHOD(RedisCluster, object) { } } +/* {{{ proto null RedisCluster::subscribe(array chans, callable cb) */ +PHP_METHOD(RedisCluster, subscribe) { + CLUSTER_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp); +} +/* }}} */ + +/* {{{ proto null RedisCluster::psubscribe(array pats, callable cb) */ +PHP_METHOD(RedisCluster, psubscribe) { + CLUSTER_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp); +} +/* }}} */ + /* Commands that do not interact with Redis, but just report stuff about * various options, etc */ diff --git a/redis_cluster.h b/redis_cluster.h index e9d6d6b5ab..515b243a68 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -222,6 +222,15 @@ PHP_METHOD(RedisCluster, zunionstore); PHP_METHOD(RedisCluster, zinterstore); PHP_METHOD(RedisCluster, sort); PHP_METHOD(RedisCluster, object); +PHP_METHOD(RedisCluster, subscribe); +PHP_METHOD(RedisCluster, psubscribe); + +/* Transactions */ +PHP_METHOD(RedisCluster, multi); +PHP_METHOD(RedisCluster, exec); +PHP_METHOD(RedisCluster, discard); +PHP_METHOD(RedisCluster, watch); +PHP_METHOD(RedisCluster, unwatch); /* Introspection */ PHP_METHOD(RedisCluster, getoption); @@ -230,9 +239,4 @@ PHP_METHOD(RedisCluster, _prefix); PHP_METHOD(RedisCluster, _serialize); PHP_METHOD(RedisCluster, _unserialize); -PHP_METHOD(RedisCluster, multi); -PHP_METHOD(RedisCluster, exec); -PHP_METHOD(RedisCluster, discard); -PHP_METHOD(RedisCluster, watch); -PHP_METHOD(RedisCluster, unwatch); #endif diff --git a/redis_commands.c b/redis_commands.c index 25f073513d..b8966dd70c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -655,6 +655,69 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* SUBSCRIBE/PSUBSCRIBE */ +int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + zval *z_arr, **z_chan; + HashTable *ht_chan; + HashPosition ptr; + smart_str cmdstr = {0}; + subscribeContext *sctx = emalloc(sizeof(subscribeContext)); + int key_len, key_free; + char *key; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &z_arr, + &(sctx->cb), &(sctx->cb_cache))==FAILURE) + { + efree(sctx); + return FAILURE; + } + + ht_chan = Z_ARRVAL_P(z_arr); + sctx->kw = kw; + sctx->argc = zend_hash_num_elements(ht_chan); + + if(sctx->argc==0) { + efree(sctx); + return FAILURE; + } + + // Start command construction + redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); + + // Iterate over channels + for(zend_hash_internal_pointer_reset_ex(ht_chan, &ptr); + zend_hash_get_current_data_ex(ht_chan, (void**)&z_chan, &ptr)==SUCCESS; + zend_hash_move_forward_ex(ht_chan, &ptr)) + { + // We want to deal with strings here + convert_to_string(*z_chan); + + // Grab channel name, prefix if required + key = Z_STRVAL_PP(z_chan); + key_len = Z_STRLEN_PP(z_chan); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + // Add this channel + redis_cmd_append_sstr(&cmdstr, key, key_len); + + // Free our key if it was prefixed + if(key_free) efree(key); + } + + // Push values out + *cmd_len = cmdstr.len; + *cmd = cmdstr.c; + *ctx = (void*)sctx; + + // Pick a slot at random + CMD_RAND_SLOT(slot); + + return SUCCESS; +} + /* Commands that take a key followed by a variable list of serializable * values (RPUSH, LPUSH, SADD, SREM, etc...) */ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, diff --git a/redis_commands.h b/redis_commands.h index 679ffadae8..ef1a80b813 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -6,13 +6,21 @@ #include "cluster_library.h" /* Pick a random slot, any slot (for stuff like publish/subscribe) */ -#define CMD_RAND_SLOT(slot,key,key_len) \ +#define CMD_RAND_SLOT(slot) \ if(slot) *slot = rand() % REDIS_CLUSTER_MOD /* Macro for setting the slot if we've been asked to */ #define CMD_SET_SLOT(slot,key,key_len) \ if(slot) *slot = cluster_hash_key(key,key_len); +/* Simple container so we can push subscribe context out */ +typedef struct subscribeContext { + char *kw; + int argc; + zend_fcall_info cb; + zend_fcall_info_cache cb_cache; +} subscribeContext; + /* Redis command generics. Many commands share common prototypes meaning that * we can write one function to handle all of them. For example, there are * many COMMAND key value commands, or COMMAND key commands. */ @@ -71,6 +79,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them From 5daa5a2f045c43de88dcd5790be96b37d89b42eb Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 14 Jun 2014 09:30:18 -0700 Subject: [PATCH 0333/1986] UNSUBSCRIBE/PUNSUBSCRIBE Implemented the two unsubscribe commands in Redis Cluster. Presently, like with the standard Redis class once you subscribe you're there for good, but it would be nice to be able to use the callback return value to break out of the subscribe loop. --- cluster_library.c | 65 +++++++++++++++++++++++++++++++++++++++++++---- cluster_library.h | 7 ++++- redis_cluster.c | 59 +++++++++++++++++++++++++++++++++++++++++- redis_cluster.h | 2 ++ redis_commands.c | 46 +++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 6 files changed, 175 insertions(+), 7 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index e4d7ae9a09..86cec935b7 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1371,7 +1371,7 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Consume each MULTI BULK response (one per channel/pattern) while(sctx->argc--) { - z_tab = cluster_sub_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw); if(!z_tab) { @@ -1397,6 +1397,9 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, sctx->cb.params = z_args; sctx->cb.no_separation = 0; + /* We're in a subscribe loop */ + c->subscribed_slot = c->reply_slot; + /* Multibulk response, {[pattern], type, channel, payload} */ while(1) { /* Arguments */ @@ -1404,7 +1407,7 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int tab_idx=1, is_pmsg; // Get the next subscribe response - z_tab = cluster_sub_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, mbulk_resp_loop); if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&z_type) @@ -1465,7 +1468,10 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, zval_dtor(z_tab); efree(z_tab); } - + + // We're no longer subscribing, due to an error + c->subscribed_slot = -1; + // Cleanup efree(sctx); if(z_tab) { @@ -1477,6 +1483,55 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, RETURN_FALSE; } +/* UNSUBSCRIBE/PUNSUBSCRIBE */ +PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx) +{ + subscribeContext *sctx = (subscribeContext*)ctx; + zval *z_tab, **z_chan, **z_flag; + char *flag; + int pull = 0; + + array_init(return_value); + + // Consume each response + while(sctx->argc--) { + z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, + c, pull, mbulk_resp_loop_raw); + + // Fail if we didn't get an array or can't find index 1 + if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, + (void**)&z_chan)==FAILURE) + { + zval_dtor(return_value); + RETURN_FALSE; + } + + // Find the flag for this channel/pattern + if(zend_hash_index_find(Z_ARRVAL_P(z_tab), 2, (void**)&z_flag) + ==FAILURE) + { + zval_dtor(return_value); + RETURN_FALSE; + } + + // Sanity check + if(Z_STRLEN_PP(z_flag) != 2) { + zval_dtor(return_value); + RETURN_FALSE; + } + + // Redis will give us either :1 or :0 here + char *flag = Z_STRVAL_PP(z_flag); + + // Add result + add_assoc_bool(return_value, Z_STRVAL_PP(z_chan), flag[1]=='1'); + + efree(z_tab); + pull = 1; + } +} + /* Generic MULTI BULK response processor */ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb cb, void *ctx) @@ -1512,8 +1567,8 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* MULTI BULK response loop where we might pull the next one */ -PHPAPI zval *cluster_sub_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, int pull, mbulk_cb cb) +PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, int pull, mbulk_cb cb) { zval *z_result; diff --git a/cluster_library.h b/cluster_library.h index e311521a3f..0549179996 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -224,6 +224,9 @@ typedef struct redisCluster { /* The slot where we should read replies */ short reply_slot; + /* The slot where we're subscribed */ + short subscribed_slot; + /* One RedisSock* struct for serialization and prefix information */ RedisSock *flags; @@ -337,6 +340,8 @@ PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); /* MULTI BULK response functions */ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, @@ -353,7 +358,7 @@ PHPAPI void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI zval *cluster_sub_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb); /* MULTI BULK processing callbacks */ diff --git a/redis_cluster.c b/redis_cluster.c index bd0c3b6d44..6f05580430 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -141,7 +141,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, object, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, subscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, psubscribe, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(RedisCluster, unsubscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, punsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) @@ -204,6 +205,9 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { cluster = emalloc(sizeof(redisCluster)); memset(cluster, 0, sizeof(redisCluster)); + // We're not currently subscribed anywhere + cluster->subscribed_slot = -1; + // Allocate our RedisSock we'll use to store prefix/serialization flags cluster->flags = ecalloc(1, sizeof(RedisSock)); @@ -1068,6 +1072,59 @@ PHP_METHOD(RedisCluster, psubscribe) { } /* }}} */ +static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + char *kw) +{ + char *cmd; + int cmd_len; + void *ctx; + short slot; + + // There is not reason to unsubscribe outside of a subscribe loop + if(c->subscribed_slot == -1) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "You can't unsubscribe outside of a subscribe loop"); + RETURN_FALSE; + } + + // Call directly because we're going to set the slot manually + if(redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, + &cmd, &cmd_len, &slot, &ctx) + ==FAILURE) + { + RETURN_FALSE; + } + + // This has to operate on our subscribe slot + if(cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK) + ==FAILURE) + { + zend_throw_exception(redis_cluster_exception_ce, + "Failed to UNSUBSCRIBE within our subscribe loop!", 0 TSRMLS_CC); + RETURN_FALSE; + } + + // Now process response from the slot we're subscribed on + cluster_unsub_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); + + // Cleanup our command + efree(cmd); +} + +/* {{{ proto array RedisCluster::unsubscribe(array chans) */ +PHP_METHOD(RedisCluster, unsubscribe) { + generic_unsub_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), + "UNSUBSCRIBE"); +} +/* }}} */ + +/* {{{ proto array RedisCluster::punsubscribe(array pats) */ +PHP_METHOD(RedisCluster, punsubscribe) { + generic_unsub_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), + "PUNSUBSCRIBE"); +} +/* }}} */ + /* Commands that do not interact with Redis, but just report stuff about * various options, etc */ diff --git a/redis_cluster.h b/redis_cluster.h index 515b243a68..ead579829d 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -224,6 +224,8 @@ PHP_METHOD(RedisCluster, sort); PHP_METHOD(RedisCluster, object); PHP_METHOD(RedisCluster, subscribe); PHP_METHOD(RedisCluster, psubscribe); +PHP_METHOD(RedisCluster, unsubscribe); +PHP_METHOD(RedisCluster, punsubscribe); /* Transactions */ PHP_METHOD(RedisCluster, multi); diff --git a/redis_commands.c b/redis_commands.c index b8966dd70c..6e5e13115c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -718,6 +718,52 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* UNSUBSCRIBE/PUNSUBSCRIBE */ +int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + zval *z_arr, **z_chan; + HashTable *ht_arr; + HashPosition ptr; + smart_str cmdstr = {0}; + subscribeContext *sctx = emalloc(sizeof(subscribeContext)); + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { + efree(sctx); + return FAILURE; + } + + ht_arr = Z_ARRVAL_P(z_arr); + + sctx->argc = zend_hash_num_elements(ht_arr); + if(sctx->argc == 0) { + efree(sctx); + return FAILURE; + } + + redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); + + for(zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); + zend_hash_get_current_data_ex(ht_arr, (void**)&z_chan, &ptr)==SUCCESS; + zend_hash_move_forward_ex(ht_arr, &ptr)) + { + char *key = Z_STRVAL_PP(z_chan); + int key_len = Z_STRLEN_PP(z_chan), key_free; + + key_free = redis_key_prefix(redis_sock, &key, &key_len); + redis_cmd_append_sstr(&cmdstr, key, key_len); + if(key_free) efree(key); + } + + // Push out vals + *cmd_len = cmdstr.len; + *cmd = cmdstr.c; + *ctx = (void*)sctx; + + return SUCCESS; +} + /* Commands that take a key followed by a variable list of serializable * values (RPUSH, LPUSH, SADD, SREM, etc...) */ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, diff --git a/redis_commands.h b/redis_commands.h index ef1a80b813..bb04d76b97 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -82,6 +82,9 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them From 55e5d491269d4b4e0b64ea2dcd89c980769d2573 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 14 Jun 2014 10:25:10 -0700 Subject: [PATCH 0334/1986] SUBSCRIBE/PSUBSCRIBE updated in Redis proper Moved processing of the (p)subscribe response out of redis.c, and also used our redis_subscribe_cmd construction function to build it. --- cluster_library.c | 1 - library.c | 124 ++++++++++++++++++++++++++- library.h | 3 + redis.c | 210 +++------------------------------------------- 4 files changed, 135 insertions(+), 203 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 86cec935b7..6524d7626e 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1489,7 +1489,6 @@ PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, { subscribeContext *sctx = (subscribeContext*)ctx; zval *z_tab, **z_chan, **z_flag; - char *flag; int pull = 0; array_init(return_value); diff --git a/library.c b/library.c index 0922a0a442..018de888d5 100644 --- a/library.c +++ b/library.c @@ -16,6 +16,7 @@ #include #include "php_redis.h" #include "library.h" +#include "redis_commands.h" #include #include @@ -147,7 +148,8 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) eof = php_stream_eof(redis_sock->stream); for (; eof; count++) { - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ + if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { + /* too many failures */ if(redis_sock->stream) { /* close stream if still here */ redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; @@ -246,7 +248,125 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } -PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { +PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + void *ctx) +{ + subscribeContext *sctx = (subscribeContext*)ctx; + zval **z_tmp, *z_ret, **z_args[4]; + + // Consume response(s) from subscribe, which will vary on argc + while(sctx->argc--) { + z_tab = redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + if(!z_tab) return -1; + + // We'll need to find the command response + if(zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&z_tmp) + ==FAILURE) + { + zval_dtor(z_tab); + efree(z_tab); + return -1; + } + + // Make sure the command response matches the command we called + if(strcasecmp(Z_STRVAL_PP(z_tmp), sctx->kw) !=0) { + zval_dtor(z_tab); + efree(z_tab); + return -1; + } + + zval_dtor(z_tab); + efree(z_tab); + } + + sctx->cb.retval_ptr_ptr = &z_ret; + sctx->cb.params = z_args; + sctx->cb.no_separation = 0; + + /* Multibulk response, {[pattern], type, channel, payload } */ + while(1) { + zval **z_type, **z_chan, **z_pat, **z_data; + HashTable *ht_tab; + int tab_idx=1, is_pmsg; + + z_tab = redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + if(z_tab == NULL) break; + + ht_tab = Z_ARRVAL_P(z_tab); + + if(zend_hash_index_find(ht_tab, 0, (void**)&z_type)==FAILURE || + Z_TYPE_PP(z_type) != IS_STRING) + { + break; + } + + // Check for message or pmessage + if(!strncmp(Z_STRVAL_PP(z_type), "message", 7) || + !strncmp(Z_STRVAL_PP(z_type), "pmessage", 8)) + { + is_pmsg = *Z_STRVAL_PP(z_type)=='p'; + } else { + break; + } + + // Extract pattern if it's a pmessage + if(is_pmsg) { + if(zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_pat) + ==FAILURE) + { + break; + } + } + + // Extract channel and data + if(zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_chan)==FAILURE || + zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_data)==FAILURE) + { + break; + } + + // Different args for SUBSCRIBE and PSUBSCRIBE + z_args[0] = &getThis(); + if(is_pmsg) { + z_args[1] = z_pat; + z_args[2] = z_chan; + z_args[3] = z_data; + } else { + z_args[1] = z_chan; + z_args[2] = z_data; + } + + // Set arg count + sctx->cb.param_count = tab_idx; + + // Execute callback + if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC) + ==FAILURE) + { + break; + } + + // If we have a return value free it + if(z_ret) zval_ptr_dtor(&z_ret); + + zval_dtor(z_tab); + efree(z_tab); + } + + if(z_tab) { + zval_dtor(z_tab); + efree(z_tab); + } + + return -1; +} + +PHPAPI zval * +redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock) { char inbuf[1024]; int numElems; zval *z_tab; diff --git a/library.h b/library.h index 145cfaf790..9ac35ec512 100644 --- a/library.h +++ b/library.h @@ -22,6 +22,9 @@ PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size typedef void (*SuccessCallback)(RedisSock *redis_sock); +PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); + PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index 10a49264fc..dfdb553b60 100644 --- a/redis.c +++ b/redis.c @@ -2565,208 +2565,17 @@ PHP_METHOD(Redis, publish) } /* }}} */ -PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd) -{ - zval *object, *array, **data; - HashTable *arr_hash; - HashPosition pointer; - RedisSock *redis_sock; - char *cmd = "", *old_cmd = NULL, *key; - int cmd_len, array_count, key_len, key_free; - zval *z_tab, **tmp; - char *type_response; - - // Function call information - zend_fcall_info z_callback; - zend_fcall_info_cache z_callback_cache; - - zval *z_ret, **z_args[4]; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oaf", - &object, redis_ce, &array, &z_callback, - &z_callback_cache) == FAILURE) - { - RETURN_FALSE; - } - - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - arr_hash = Z_ARRVAL_P(array); - array_count = zend_hash_num_elements(arr_hash); - - if (array_count == 0) { - RETURN_FALSE; - } - for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); - zend_hash_get_current_data_ex(arr_hash, (void**) &data, - &pointer) == SUCCESS; - zend_hash_move_forward_ex(arr_hash, &pointer)) { - - if (Z_TYPE_PP(data) == IS_STRING) { - char *old_cmd = NULL; - if(*cmd) { - old_cmd = cmd; - } - - /* Grab our key and len */ - key = Z_STRVAL_PP(data); - key_len = Z_STRLEN_PP(data); - - /* Prefix our key if neccisary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - cmd_len = spprintf(&cmd, 0, "%s %s", cmd, key); - - if(old_cmd) { - efree(old_cmd); - } - - /* Free our key if it was prefixed */ - if(key_free) { - efree(key); - } - } - } - - old_cmd = cmd; - cmd_len = spprintf(&cmd, 0, "%s %s\r\n", sub_cmd, cmd); - efree(old_cmd); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - RETURN_FALSE; - } - efree(cmd); - - /* read the status of the execution of the command `subscribe` */ - z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock); - if(z_tab == NULL) { - RETURN_FALSE; - } - - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&tmp) == SUCCESS) { - type_response = Z_STRVAL_PP(tmp); - if(strcmp(type_response, sub_cmd) != 0) { - efree(tmp); - zval_dtor(z_tab); - efree(z_tab); - RETURN_FALSE; - } - } else { - zval_dtor(z_tab); - efree(z_tab); - RETURN_FALSE; - } - zval_dtor(z_tab); - efree(z_tab); - - /* Set a pointer to our return value and to our arguments. */ - z_callback.retval_ptr_ptr = &z_ret; - z_callback.params = z_args; - z_callback.no_separation = 0; - - /* Multibulk Response, format : {message type, originating channel, message - * payload} */ - while(1) { - /* call the callback with this z_tab in argument */ - zval **type, **channel, **pattern, **data; - z_tab = redis_sock_read_multibulk_reply_zval( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - int is_pmsg, tab_idx = 1; - - if(z_tab == NULL || Z_TYPE_P(z_tab) != IS_ARRAY) { - /*ERROR */ - break; - } - - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&type) == FAILURE - || Z_TYPE_PP(type) != IS_STRING) - { - break; - } - - // Make sure we have a message or pmessage - if(!strncmp(Z_STRVAL_PP(type), "message", 7) || - !strncmp(Z_STRVAL_PP(type), "pmessage", 8)) - { - // Is this a pmessage - is_pmsg = *Z_STRVAL_PP(type) == 'p'; - } else { - continue; // It's not a message or pmessage - } - - // If this is a pmessage, we'll want to extract the pattern first - if(is_pmsg) { - // Extract pattern - if(zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, - (void**)&pattern) == FAILURE) - { - break; - } - } - - // Extract channel and data - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&channel) - == FAILURE) - { - break; - } - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&data) - == FAILURE) - { - break; - } - - /* Always pass the Redis object through */ - z_args[0] = &getThis(); - - /* Set up our callback args depending on the message type */ - if(is_pmsg) { - z_args[1] = pattern; - z_args[2] = channel; - z_args[3] = data; - } else { - z_args[1] = channel; - z_args[2] = data; - } - - /* Set our argument information */ - z_callback.param_count = tab_idx; - - // Break if we can't call the function - if(zend_call_function(&z_callback, &z_callback_cache TSRMLS_CC) - != SUCCESS) - { - break; - } - - // If we have a return value, free it. Note, we could use the return - // value to break the subscribe loop - if(z_ret) zval_ptr_dtor(&z_ret); - - /* Check for a non-null return value. If we have one, return it from - * the subscribe function itself. Otherwise continue our loop. */ - if (z_ret) { - if (Z_TYPE_P(z_ret) != IS_NULL) { - RETVAL_ZVAL(z_ret, 0, 1); - break; - } - zval_ptr_dtor(&z_ret); - } - } -} - /* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN)) */ PHP_METHOD(Redis, psubscribe) { - generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "psubscribe"); + REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, + redis_subscribe_response); } /* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */ PHP_METHOD(Redis, subscribe) { - generic_subscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "subscribe"); + REDIS_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd, + redis_subscribe_response); } /** @@ -4021,11 +3830,12 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } /** - * Redis can return to us empty keys, especially in the case where there are - * a large number of keys to scan, and we're matching against a pattern. - * PHPRedis can be set up to abstract this from the user, by setting - * OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty keys and - * the user will need to make subsequent calls with an updated iterator. + * Redis can return to us empty keys, especially in the case where there + * are a large number of keys to scan, and we're matching against a + * pattern. phpredis can be set up to abstract this from the user, by + * setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty + * keys and the user will need to make subsequent calls with an updated + * iterator. */ do { /* Free our previous reply if we're back in the loop. We know we are From cb1e84aa9093d6e6e451b5d49616ae176425c052 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 14 Jun 2014 12:06:32 -0700 Subject: [PATCH 0335/1986] Fix memory leaks for (P)SUBSCRIBE/(P)UNSUBSCRIBE --- cluster_library.c | 24 +++++++++++++++--------- library.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- library.h | 4 +++- redis.c | 6 ++++-- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 6524d7626e..5da2e87dbe 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1389,6 +1389,7 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } zval_dtor(z_tab); + efree(z_tab); pull = 1; } @@ -1465,6 +1466,9 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, break; } + // If we have a return value, free it + if(z_ret) zval_ptr_dtor(&z_ret); + zval_dtor(z_tab); efree(z_tab); } @@ -1489,12 +1493,13 @@ PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, { subscribeContext *sctx = (subscribeContext*)ctx; zval *z_tab, **z_chan, **z_flag; - int pull = 0; + int pull = 0, argc = sctx->argc; + efree(sctx); array_init(return_value); // Consume each response - while(sctx->argc--) { + while(argc--) { z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw); @@ -1502,20 +1507,20 @@ PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_chan)==FAILURE) { + if(z_tab) { + zval_dtor(z_tab); + efree(z_tab); + } zval_dtor(return_value); RETURN_FALSE; } // Find the flag for this channel/pattern if(zend_hash_index_find(Z_ARRVAL_P(z_tab), 2, (void**)&z_flag) - ==FAILURE) + ==FAILURE || Z_STRLEN_PP(z_flag)!=2) { - zval_dtor(return_value); - RETURN_FALSE; - } - - // Sanity check - if(Z_STRLEN_PP(z_flag) != 2) { + zval_dtor(z_tab); + efree(z_tab); zval_dtor(return_value); RETURN_FALSE; } @@ -1526,6 +1531,7 @@ PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, // Add result add_assoc_bool(return_value, Z_STRVAL_PP(z_chan), flag[1]=='1'); + zval_dtor(z_tab); efree(z_tab); pull = 1; } diff --git a/library.c b/library.c index 018de888d5..6d5e1c052d 100644 --- a/library.c +++ b/library.c @@ -259,7 +259,10 @@ PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, while(sctx->argc--) { z_tab = redis_sock_read_multibulk_reply_zval( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - if(!z_tab) return -1; + if(!z_tab) { + efree(sctx); + return -1; + } // We'll need to find the command response if(zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&z_tmp) @@ -267,6 +270,7 @@ PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, { zval_dtor(z_tab); efree(z_tab); + efree(sctx); return -1; } @@ -274,6 +278,7 @@ PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, if(strcasecmp(Z_STRVAL_PP(z_tmp), sctx->kw) !=0) { zval_dtor(z_tab); efree(z_tab); + efree(sctx); return -1; } @@ -356,14 +361,54 @@ PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, efree(z_tab); } + // This is an error state, clean up if(z_tab) { zval_dtor(z_tab); efree(z_tab); } + efree(sctx); return -1; } +PHPAPI int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + void *ctx) +{ + subscribeContext *sctx = (subscribeContext*)ctx; + zval **z_chan, *z_ret; + int i=0; + + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + + while(iargc) { + z_tab = redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + + if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, + (void**)&z_chan)==FAILURE) + { + zval_dtor(z_ret); + efree(z_ret); + return -1; + } + + add_assoc_bool(z_ret, Z_STRVAL_PP(z_chan), 1); + + zval_dtor(z_tab); + efree(z_tab); + i++; + } + + efree(sctx); + + RETVAL_ZVAL(z_ret, 0, 1); + + // Success + return 0; +} + PHPAPI zval * redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { diff --git a/library.h b/library.h index 9ac35ec512..54d54cc034 100644 --- a/library.h +++ b/library.h @@ -23,7 +23,9 @@ PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size typedef void (*SuccessCallback)(RedisSock *redis_sock); PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index dfdb553b60..9ca56a57a0 100644 --- a/redis.c +++ b/redis.c @@ -2671,12 +2671,14 @@ PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, PHP_METHOD(Redis, unsubscribe) { - generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNSUBSCRIBE"); + REDIS_PROCESS_KW_CMD("UNSUBSCRIBE", redis_unsubscribe_cmd, + redis_unsubscribe_response); } PHP_METHOD(Redis, punsubscribe) { - generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUNSUBSCRIBE"); + REDIS_PROCESS_KW_CMD("PUNSUBSCRIBE", redis_unsubscribe_cmd, + redis_unsubscribe_response); } /* {{{ proto string Redis::bgrewriteaof() */ From b571a439d6b5a7ce2ca837e4187766360fe0187e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 14 Jun 2014 13:10:32 -0700 Subject: [PATCH 0336/1986] Free cluster err on shutdown --- redis.c | 2 +- redis_cluster.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 9ca56a57a0..e695f085ef 100644 --- a/redis.c +++ b/redis.c @@ -1679,7 +1679,7 @@ PHP_METHOD(Redis, lastSave) /* {{{ proto bool Redis::flushDB() */ PHP_METHOD(Redis, flushDB) { - REDIS_PROCESS_KW_CMD("FLUSHDB", redis_empty_cmd, redis_boolean_response); + REDIS_PROCESS_KW_CMD("FLUSHDB", redis_empty_cmd, redis_boolean_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 6f05580430..35bc482e22 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -143,8 +143,10 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, psubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, unsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, punsubscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _serialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _unserialize, NULL, ZEND_ACC_PUBLIC) @@ -257,6 +259,8 @@ void free_cluster_context(void *object TSRMLS_DC) { zend_hash_destroy(cluster->nodes); efree(cluster->nodes); + if(cluster->err) efree(cluster->err); + // Finally, free the redisCluster structure itself efree(cluster); } From 4bcad65a6eadbfc553b8ba34ef19602bf58b6857 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 14 Jun 2014 14:06:27 -0700 Subject: [PATCH 0337/1986] Minor formatting changes --- redis.c | 53 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/redis.c b/redis.c index e695f085ef..4b907fd3e6 100644 --- a/redis.c +++ b/redis.c @@ -2433,8 +2433,9 @@ PHP_METHOD(Redis, exec) } efree(cmd); - if(redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock) < 0) + if(redis_sock_read_multibulk_multi_reply( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock) < 0) { zval_dtor(return_value); free_reply_callbacks(object, redis_sock); @@ -2484,8 +2485,9 @@ PHP_METHOD(Redis, exec) return; } - if (redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock) < 0) + if (redis_sock_read_multibulk_pipeline_reply( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock) < 0) { redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); @@ -2518,9 +2520,10 @@ PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, TSRMLS_CC); } -PHPAPI int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, - zval *z_tab, int numElems) +PHPAPI int +redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + int numElems) { fold_item *head = redis_sock->head; @@ -2649,8 +2652,8 @@ PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, array_init(return_value); while( i <= array_count) { - z_tab = redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock); + z_tab = redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); if(Z_TYPE_P(z_tab) == IS_ARRAY) { if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) @@ -2665,7 +2668,7 @@ PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } efree(z_tab); - i ++; + i++; } } @@ -2814,8 +2817,8 @@ PHP_METHOD(Redis, config) } if (mode == CFG_GET && val == NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, key, - key_len); + cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, + key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) IF_ATOMIC() { @@ -3085,8 +3088,9 @@ PHP_METHOD(Redis, pubsub) { if(type == PUBSUB_NUMSUB) { IF_ATOMIC() { - if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) + if(redis_sock_read_multibulk_reply_zipped( + INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL)<0) { RETURN_FALSE; } @@ -3124,10 +3128,11 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, args_hash = Z_ARRVAL_P(args); args_count = zend_hash_num_elements(args_hash); - /* We only need to process the arguments if the array is non empty */ - if(args_count > 0) { - /* Header for our EVAL command */ - cmd_len = redis_cmd_format_header(ret, keyword, eval_cmd_count + args_count); + // We only need to process the arguments if the array is non empty + if(args_count > 0) { + // Header for our EVAL command + cmd_len = redis_cmd_format_header(ret, keyword, + eval_cmd_count + args_count); // Now append the script itself, and the number of arguments to // treat as keys @@ -3136,7 +3141,8 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, // Iterate the values in our "keys" array for(zend_hash_internal_pointer_reset_ex(args_hash, &hash_pos); - zend_hash_get_current_data_ex(args_hash,(void **)&elem,&hash_pos) == SUCCESS; + zend_hash_get_current_data_ex(args_hash,(void **)&elem, + &hash_pos) == SUCCESS; zend_hash_move_forward_ex(args_hash, &hash_pos)) { zval *z_tmp = NULL; @@ -3162,7 +3168,8 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, // If this is still a key argument, prefix it if we've been set // up to prefix keys - key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len) : 0; + key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, + &key, &key_len) : 0; // Append this key to our EVAL command, free our old command cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, @@ -3271,9 +3278,9 @@ redis_build_script_exists_cmd(char **ret, zval **argv, int argc) { /* Our command length and iterator */ int cmd_len = 0, i; - /* Start building our command */ - cmd_len = redis_cmd_format_header(ret, "SCRIPT", argc + 1); /* +1 for "EXISTS" */ - cmd_len = redis_cmd_append_str(ret, cmd_len, "EXISTS", 6); + // Start building our command + cmd_len = redis_cmd_format_header(ret, "SCRIPT", argc + 1); + cmd_len = redis_cmd_append_str(ret, cmd_len, "EXISTS", 6); /* Iterate our arguments */ for(i=0;i Date: Sun, 15 Jun 2014 10:42:58 -0700 Subject: [PATCH 0338/1986] Allow WATCH even when the cluster is resharding. We can actully execute multi commands like WATCH/MSET/MGET while resharding, given that we'll just get bounced around until all the keys are in the correct place. --- cluster_library.c | 2 +- redis_cluster.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 5da2e87dbe..014d2bb5f0 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1140,7 +1140,7 @@ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, return -1; } - // Require a +OK response + // Check our response and verify the type unless passed in as TYPE_EOF if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || (rtype != TYPE_EOF && rtype != c->reply_type)) { diff --git a/redis_cluster.c b/redis_cluster.c index 35bc482e22..d76f93582a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1257,11 +1257,10 @@ PHP_METHOD(RedisCluster, watch) { (*dl)->entry[i].key_len); } - // Send this command directly to our server - if(cluster_send_slot(c, (short)slot, cmd.c, cmd.len, TYPE_LINE)==-1) { - zend_throw_exception(redis_cluster_exception_ce, - "WATCH command failed. Nodes are possibly resharding", - 0 TSRMLS_CC); + // If we get a failure from this, we have to abort + if((slot = cluster_send_command(c, (short)slot, cmd.c, cmd.len + TSRMLS_CC))==-1) + { RETURN_FALSE; } From e4bab084b630100658070ddc830fda8852679ca8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 15 Jun 2014 17:13:54 -0700 Subject: [PATCH 0339/1986] MGET Initial commit of logic for the MGET command, which will distribute the commands across nodes in the cluster, based on how the slots hash. The response for MGET in cluster differs from Redis proper in a couple of ways. 1) Except for a critical failure (e.g. being unable to write the command *anywhere*, or internal PHP HashTable malfunctions, the client will always get an array back, even on within the processing. 2) This array will correspond 1-1 with the keys requested, so that the client can use the keys passed in array as a sentinel for the responses. 3) When NO key is found, the resulting value will be NULL, whereas in the event of an error it will be FALSE. --- cluster_library.c | 93 +++++++++++++++++++++++---- cluster_library.h | 56 +++++++++++++++- redis_cluster.c | 159 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 292 insertions(+), 16 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 014d2bb5f0..f8471cfe74 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -152,6 +152,25 @@ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val kv->val_free = val_free; } +/* Free allocated memory for a clusterMultiCmd */ +void cluster_multi_free(clusterMultiCmd *mc) { + efree(mc->cmd.c); + efree(mc->args.c); +} + +/* Add an argument to a clusterMultiCmd */ +void cluster_multi_add(clusterMultiCmd *mc, char *data, int data_len) { + mc->argc++; + redis_cmd_append_sstr(&(mc->args), data, data_len); +} + +/* Finalize a clusterMutliCmd by constructing the whole thing */ +void cluster_multi_fini(clusterMultiCmd *mc) { + mc->cmd.len = 0; + redis_cmd_init_sstr(&(mc->cmd), mc->argc, mc->kw, mc->kw_len); + smart_str_appendl(&(mc->cmd), mc->args.c, mc->args.len); +} + /* Set our last error string encountered */ static void cluster_set_err(redisCluster *c, char *err, int err_len) { @@ -1619,7 +1638,6 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } - // Override reply slot, process response, move on c->reply_slot = fi->slot; fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx); fi = fi->next; @@ -1631,12 +1649,56 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, efree(c->multi_resp); } +/* Generic handler for things like MGET/MSET/MSETNX */ +PHPAPI void cluster_mgetset_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, mbulk_cb func, + void *ctx) +{ + clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; + + // Protect against an invalid response type, -1 response length, and failure + // to consume the responses. + short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || + func(SLOT_SOCK(c,c->reply_slot), mctx->z_multi, c->reply_len, + NULL TSRMLS_CC)==FAILURE; + + // If we had a failure, pad results with FALSE to indicate failure. Non + // existant keys (e.g. for MGET will come back as NULL) + if(fail) { + while(mctx->count--) { + add_next_index_bool(mctx->z_multi, 0); + } + } + + // If this is the tail of our multi command, we can set our returns + if(mctx->last) { + if(CLUSTER_IS_ATOMIC(c)) { + *return_value = *(mctx->z_multi); + efree(mctx->z_multi); + } else { + add_next_index_zval(c->multi_resp, mctx->z_multi); + } + } + + // Clean up this context item + efree(mctx); +} + +/* Response for MGET */ +PHPAPI void +cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + cluster_mgetset_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + mbulk_resp_loop, ctx); +} + /* Raw MULTI BULK reply */ PHPAPI void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, mbulk_resp_loop_raw, NULL); + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + mbulk_resp_loop_raw, NULL); } /* Unserialize all the things */ @@ -1644,8 +1706,8 @@ PHPAPI void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, mbulk_resp_loop, NULL); + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + mbulk_resp_loop, NULL); } /* For handling responses where we get key, value, key, value that @@ -1653,16 +1715,16 @@ cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, PHPAPI void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, mbulk_resp_loop_zipstr, NULL); + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + mbulk_resp_loop_zipstr, NULL); } /* Handling key,value to key=>value where the values are doubles */ PHPAPI void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, mbulk_resp_loop_zipdbl, NULL); + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + mbulk_resp_loop_zipdbl, NULL); } /* Associate multi bulk response (for HMGET really) */ @@ -1713,11 +1775,16 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); if(line == NULL) return FAILURE; - if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { - add_next_index_zval(z_result, z); - efree(line); + if(line_len > 0) { + if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { + add_next_index_zval(z_result, z); + efree(line); + } else { + add_next_index_stringl(z_result, line, line_len, 0); + } } else { - add_next_index_stringl(z_result, line, line_len, 0); + efree(line); + add_next_index_null(z_result); } } diff --git a/cluster_library.h b/cluster_library.h index 0549179996..b05025d256 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -133,6 +133,17 @@ add_next_index_long(c->multi_resp, val); \ } +/* Macro to clear out a clusterMultiCmd structure */ +#define CLUSTER_MULTI_CLEAR(mc) \ + mc->cmd.len = 0; \ + mc->args.len = 0; \ + mc->argc = 0; \ + +/* Initialzie a clusterMultiCmd with a keyword and length */ +#define CLUSTER_MULTI_INIT(mc, keyword, keyword_len) \ + mc.kw = keyword; \ + mc.kw_len = keyword_len; \ + /* Cluster redirection enum */ typedef enum CLUSTER_REDIR_TYPE { REDIR_NONE, @@ -281,7 +292,36 @@ typedef struct clusterDistList { size_t len, size; } clusterDistList; -/* Cluster distribution helpers */ +/* Context for things like MGET/MSET/MSETNX. When executing in MULTI mode, + * we'll want to re-integrate into one running array, except for the last + * command execution, in which we'll want to return the value (or add it) */ +typedef struct clusterMultiCtx { + /* Our running array */ + zval *z_multi; + + /* How many keys did we request for this bit */ + int count; + + /* Is this the last entry */ + short last; +} clusterMultiCtx; + +/* Container for things like MGET, MSET, and MSETNX, which split the command + * into a header and payload while aggregating to a specific slot. */ +typedef struct clusterMultiCmd { + /* Keyword and keyword length */ + char *kw; + int kw_len; + + /* Arguments in our payload */ + int argc; + + /* The full command, built into cmd, and args as we aggregate */ + smart_str cmd; + smart_str args; +} clusterMultiCmd; + +/* Cluster distribution helpers for WATCH */ HashTable *cluster_dist_create(); void cluster_dist_free(HashTable *ht); int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, @@ -289,6 +329,12 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val TSRMLS_CC); +/* Aggregation for multi commands like MGET, MSET, and MSETNX */ +void cluster_multi_init(clusterMultiCmd *mc, char *kw, int kw_len); +void cluster_multi_free(clusterMultiCmd *mc); +void cluster_multi_add(clusterMultiCmd *mc, char *data, int data_len); +void cluster_multi_fini(clusterMultiCmd *mc); + /* Hash a key to it's slot, using the Redis Cluster hash algorithm */ unsigned short cluster_hash_key_zval(zval *key); unsigned short cluster_hash_key(const char *key, int len); @@ -361,6 +407,14 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb); +/* MULTI BULK handlers for things like MGET/MSET/MSETNX */ +PHPAPI void cluster_mgetset_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, mbulk_cb func, void *ctx); +PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); +PHPAPI void cluster_mbulk_mset_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); + /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); diff --git a/redis_cluster.c b/redis_cluster.c index d76f93582a..1fc3140529 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -343,9 +343,164 @@ PHP_METHOD(RedisCluster, set) { } /* }}} */ -/* {{{ proto array RedisCluster::mget(string key1, ... string keyN) */ +/* Multiple key response handler mechanism for things like MGET/MSET/MSETEX */ +static int +mkey_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, cluster_cb cb, + short slot, clusterMultiCmd *mc, zval *z_ret, int last) +{ + clusterMultiCtx *ctx; + + // Finalize multi command + cluster_multi_fini(mc); + + // Spin up multi context + ctx = emalloc(sizeof(clusterMultiCtx)); + ctx->z_multi = z_ret; + ctx->count = mc->argc; + ctx->last = last; + + // Attempt to send the command + if(cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len TSRMLS_CC)<0 || + c->err!=NULL) + { + cluster_multi_free(mc); + zval_dtor(z_ret); + efree(z_ret); + return -1; + } + + if(CLUSTER_IS_ATOMIC(c)) { + // Process response now + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx); + } else { + CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + } + + // Clear out our command but retain allocated memory + CLUSTER_MULTI_CLEAR(mc); + + return 0; +} + +/* {{{ proto array RedisCluster::mget(array keys) */ PHP_METHOD(RedisCluster, mget) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Coming soon!"); + redisCluster *c = GET_CONTEXT(); + clusterMultiCmd mc = {0}; + zval *z_arr, **z_key, *z_ret; + HashTable *ht_arr; + HashPosition ptr; + int i=1, argc, key_len, key_free; + short slot, kslot; + char *key; + + // Parse our arguments + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { + RETURN_FALSE; + } + + // No reason to send zero arguments + ht_arr = Z_ARRVAL_P(z_arr); + if((argc = zend_hash_num_elements(ht_arr))==0) { + RETURN_FALSE; + } + + // Spin up a running array for our response + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + + // Initialize our "multi" command handler with command/len + CLUSTER_MULTI_INIT(mc, "MGET", sizeof("MGET")-1); + + // Process the first key outside of our loop, so we don't have to check if + // it's the first iteration every time, needlessly + zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); + if(zend_hash_get_current_data_ex(ht_arr, (void**)&z_key, &ptr)==FAILURE) { + // Shouldn't happen, but check anyway + zend_throw_exception(redis_cluster_exception_ce, + "Internal Zend HashTable error", 0 TSRMLS_CC); + zval_dtor(z_ret); + efree(z_ret); + RETURN_FALSE; + } + + // Always string keys + convert_to_string(*z_key); + + // Grab info, prefix if required + key = Z_STRVAL_PP(z_key); + key_len = Z_STRLEN_PP(z_key); + key_free = redis_key_prefix(c->flags, &key, &key_len); + + // Hash it and add to our MGET commands + slot = cluster_hash_key(key, key_len); + cluster_multi_add(&mc, key, key_len); + + // Free key if we prefixed + if(key_free) efree(key); + + // Move to the next key + zend_hash_move_forward_ex(ht_arr, &ptr); + + // Iterate over keys 2...N + while(zend_hash_has_more_elements_ex(ht_arr, &ptr)==SUCCESS) { + if(zend_hash_get_current_data_ex(ht_arr, (void**)&z_key, &ptr) + ==FAILURE) + { + // Shouldn't happen, but check anyway + zend_throw_exception(redis_cluster_exception_ce, + "Internal Zend HashTable error", 0 TSRMLS_CC); + zval_dtor(z_ret); + efree(z_ret); + RETURN_FALSE; + } + + // Always want to work with strings + convert_to_string(*z_key); + + // Grab key bits, prefixing if required + key = Z_STRVAL_PP(z_key); + key_len = Z_STRLEN_PP(z_key); + key_free = redis_key_prefix(c->flags, &key, &key_len); + + // Hash our key + kslot = cluster_hash_key(key, key_len); + + // If the slots have changed, kick off the keys we've aggregated + if(slot != kslot) { + // Process this batch of MGET keys + if(mkey_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + cluster_mbulk_mget_resp, slot, &mc, + z_ret, i==argc)<0) + { + RETURN_FALSE; + } + } + + // Add this key to the command + cluster_multi_add(&mc, key, key_len); + + // Free key if we prefixed + if(key_free) efree(key); + + // Update the last slot we encountered, and the key we're on + slot = kslot; + i++; + + zend_hash_move_forward_ex(ht_arr, &ptr); + } + + // If we've got straggler(s) process them + if(mc.argc > 0) { + if(mkey_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + cluster_mbulk_mget_resp, slot, &mc, + z_ret, 1)<0) + { + RETURN_FALSE; + } + } + + // Free our command + cluster_multi_free(&mc); } /* {{{ proto bool RedisCluster::mset(string key1, string val1, ... kN, vN) */ From 6ba01b4a1f60b38b7e26f3da82cc91b1182922be Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 17 Jun 2014 15:35:04 -0700 Subject: [PATCH 0340/1986] Updated MGET response handler, added a helper function --- redis_cluster.c | 72 ++++++++++++++++++++++++++++--------------------- redis_cluster.h | 2 ++ 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 1fc3140529..c9cfc71a7d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -44,6 +44,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, set, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, mget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, mset, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, msetnx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, psetex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setnx, NULL, ZEND_ACC_PUBLIC) @@ -343,10 +344,10 @@ PHP_METHOD(RedisCluster, set) { } /* }}} */ -/* Multiple key response handler mechanism for things like MGET/MSET/MSETEX */ +/* Specific handler for MGET */ static int -mkey_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, cluster_cb cb, - short slot, clusterMultiCmd *mc, zval *z_ret, int last) +mget_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, + clusterMultiCmd *mc, zval *z_ret, int last) { clusterMultiCtx *ctx; @@ -371,9 +372,10 @@ mkey_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, cluster_cb cb, if(CLUSTER_IS_ATOMIC(c)) { // Process response now - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx); + cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + (void*)ctx); } else { - CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_mbulk_mget_resp, ctx); } // Clear out our command but retain allocated memory @@ -382,6 +384,27 @@ mkey_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, cluster_cb cb, return 0; } +/* Helper function to stringify a zval** key, prefix, and hash it */ +static int process_zval_key(redisCluster *c, zval **z_key_pp, char **key, + int *key_len, short *slot) +{ + int key_free; + + // Always want strings + convert_to_string(*z_key_pp); + + // Set up initial pointers, prefix + *key = Z_STRVAL_PP(z_key_pp); + *key_len = Z_STRLEN_PP(z_key_pp); + key_free = redis_key_prefix(c->flags, key, key_len); + + // Hash the slot + *slot = cluster_hash_key(*key, *key_len); + + // Return if we need to free the pointer + return key_free; +} + /* {{{ proto array RedisCluster::mget(array keys) */ PHP_METHOD(RedisCluster, mget) { redisCluster *c = GET_CONTEXT(); @@ -423,16 +446,8 @@ PHP_METHOD(RedisCluster, mget) { RETURN_FALSE; } - // Always string keys - convert_to_string(*z_key); - - // Grab info, prefix if required - key = Z_STRVAL_PP(z_key); - key_len = Z_STRLEN_PP(z_key); - key_free = redis_key_prefix(c->flags, &key, &key_len); - - // Hash it and add to our MGET commands - slot = cluster_hash_key(key, key_len); + // Process our key and add it to the command + key_free = process_zval_key(c, z_key, &key, &key_len, &slot); cluster_multi_add(&mc, key, key_len); // Free key if we prefixed @@ -453,23 +468,14 @@ PHP_METHOD(RedisCluster, mget) { efree(z_ret); RETURN_FALSE; } - - // Always want to work with strings - convert_to_string(*z_key); - - // Grab key bits, prefixing if required - key = Z_STRVAL_PP(z_key); - key_len = Z_STRLEN_PP(z_key); - key_free = redis_key_prefix(c->flags, &key, &key_len); - - // Hash our key - kslot = cluster_hash_key(key, key_len); + + // Proceess and hash this key + key_free = process_zval_key(c, z_key, &key, &key_len, &kslot); // If the slots have changed, kick off the keys we've aggregated if(slot != kslot) { // Process this batch of MGET keys - if(mkey_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - cluster_mbulk_mget_resp, slot, &mc, + if(mget_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, i==argc)<0) { RETURN_FALSE; @@ -491,8 +497,7 @@ PHP_METHOD(RedisCluster, mget) { // If we've got straggler(s) process them if(mc.argc > 0) { - if(mkey_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - cluster_mbulk_mget_resp, slot, &mc, + if(mget_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, 1)<0) { RETURN_FALSE; @@ -503,11 +508,16 @@ PHP_METHOD(RedisCluster, mget) { cluster_multi_free(&mc); } -/* {{{ proto bool RedisCluster::mset(string key1, string val1, ... kN, vN) */ +/* {{{ proto bool RedisCluster::mset(array keyvalues) */ PHP_METHOD(RedisCluster, mset) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Coming soon!"); } +/* {{{ proto array RedisCluster::msetnx(array keyvalues) */ +PHP_METHOD(RedisCluster, msetnx) { +} +/* }}} */ + /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, diff --git a/redis_cluster.h b/redis_cluster.h index ead579829d..704fd30b4b 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -125,6 +125,8 @@ PHP_METHOD(RedisCluster, get); PHP_METHOD(RedisCluster, set); PHP_METHOD(RedisCluster, mget); PHP_METHOD(RedisCluster, mset); +PHP_METHOD(RedisCluster, msetnx); +PHP_METHOD(RedisCluster, mset); PHP_METHOD(RedisCluster, dump); PHP_METHOD(RedisCluster, setex); PHP_METHOD(RedisCluster, psetex); From 36e78540a65bc00361f71b33c5af0fdd33131afd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 18 Jun 2014 19:23:28 -0700 Subject: [PATCH 0341/1986] Initial commit of MSET and MSETNX commands. More work needs to be done to work through what, exactly, to do in the case of a protocol error in commands like this. If Redis (or communication with Redis) bombs out halfway through a MSETNX reply handler, we will have alredy sent the commands but not yet consumed all of the responses. This would put RedisCluster in an inconsistent state, and we have a few options. 1. Abort immediately 2. Continue to process responses, but php_error_docref that it occured and use FALSE to indicate a failure occured. 3. Stop processing responses, and reset the connection to Cluster More testing will need to be done to figure out what the best stragegy is. --- cluster_library.c | 84 ++++++++++++--- cluster_library.h | 11 +- redis_cluster.c | 270 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 294 insertions(+), 71 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index f8471cfe74..282419e807 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -922,7 +922,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, return 1; } else { // Capture the error string Redis returned - cluster_set_err(c, inbuf+1, strlen(inbuf+1)-2); + cluster_set_err(c, inbuf, strlen(inbuf)-1); return 0; } } @@ -1649,22 +1649,23 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, efree(c->multi_resp); } -/* Generic handler for things like MGET/MSET/MSETNX */ -PHPAPI void cluster_mgetset_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, mbulk_cb func, - void *ctx) +/* Generic handler for MGET */ +PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; // Protect against an invalid response type, -1 response length, and failure // to consume the responses. short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || - func(SLOT_SOCK(c,c->reply_slot), mctx->z_multi, c->reply_len, - NULL TSRMLS_CC)==FAILURE; + mbulk_resp_loop(SLOT_SOCK(c,c->reply_slot), mctx->z_multi, + c->reply_len, NULL TSRMLS_CC)==FAILURE; // If we had a failure, pad results with FALSE to indicate failure. Non // existant keys (e.g. for MGET will come back as NULL) if(fail) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Invalid response from Redis for MGET command"); while(mctx->count--) { add_next_index_bool(mctx->z_multi, 0); } @@ -1684,13 +1685,70 @@ PHPAPI void cluster_mgetset_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, efree(mctx); } -/* Response for MGET */ -PHPAPI void -cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +/* Handler for MSETNX */ +PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; + int real_argc = mctx->count/2; + + // Protect against an invalid response type + if(c->reply_type != TYPE_INT) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Invalid response type for MSETNX"); + while(real_argc--) { + add_next_index_bool(mctx->z_multi, 0); + } + return; + } + + // Response will be 1/0 per key, so the client can match them up + while(real_argc--) { + add_next_index_long(mctx->z_multi, c->reply_len); + } + + // Set return value if it's our last response + if(mctx->last) { + if(CLUSTER_IS_ATOMIC(c)) { + *return_value = *(mctx->z_multi); + efree(mctx->z_multi); + } else { + add_next_index_zval(c->multi_resp, mctx->z_multi); + } + } + + // Free multi context + efree(mctx); +} + +/* Handler for MSET */ +PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) { - cluster_mgetset_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - mbulk_resp_loop, ctx); + clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; + + // If we get an invalid reply type something very wrong has happened, + // and we have to abort. + if(c->reply_type != TYPE_LINE) { + php_error_docref(0 TSRMLS_CC, E_ERROR, + "Invalid reply type returned for MSET command"); + ZVAL_FALSE(return_value); + efree(mctx->z_multi); + efree(mctx); + return; + } + + // Set our return if it's the last call + if(mctx->last) { + if(CLUSTER_IS_ATOMIC(c)) { + ZVAL_BOOL(return_value, Z_BVAL_P(mctx->z_multi)); + } else { + add_next_index_bool(c->multi_resp, Z_BVAL_P(mctx->z_multi)); + } + efree(mctx->z_multi); + } + + efree(mctx); } /* Raw MULTI BULK reply */ diff --git a/cluster_library.h b/cluster_library.h index b05025d256..9eb2f0c011 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -375,7 +375,8 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, @@ -407,12 +408,12 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb); -/* MULTI BULK handlers for things like MGET/MSET/MSETNX */ -PHPAPI void cluster_mgetset_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, mbulk_cb func, void *ctx); +/* Handlers for things like MGET/MSET/MSETNX */ PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_mbulk_mset_resp(INTERNAL_FUNCTION_PARAMETERS, +PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); +PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* MULTI BULK processing callbacks */ diff --git a/redis_cluster.c b/redis_cluster.c index c9cfc71a7d..bcbb1bed04 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -96,6 +96,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrby, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hset, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hsetnx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hmget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hmset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hdel, NULL, ZEND_ACC_PUBLIC) @@ -344,10 +346,10 @@ PHP_METHOD(RedisCluster, set) { } /* }}} */ -/* Specific handler for MGET */ +/* Generic handler for MGET/MSET/MSETNX */ static int -mget_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, - clusterMultiCmd *mc, zval *z_ret, int last) +distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, + clusterMultiCmd *mc, zval *z_ret, int last, cluster_cb cb) { clusterMultiCtx *ctx; @@ -367,15 +369,15 @@ mget_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, cluster_multi_free(mc); zval_dtor(z_ret); efree(z_ret); + efree(ctx); return -1; } if(CLUSTER_IS_ATOMIC(c)) { // Process response now - cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - (void*)ctx); + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx); } else { - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_mbulk_mget_resp, ctx); + CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); } // Clear out our command but retain allocated memory @@ -384,37 +386,98 @@ mget_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, return 0; } -/* Helper function to stringify a zval** key, prefix, and hash it */ -static int process_zval_key(redisCluster *c, zval **z_key_pp, char **key, - int *key_len, short *slot) +/* Container struct for a key/value pair pulled from an array */ +typedef struct clusterKeyValHT { + char kbuf[22]; + + char *key; + int key_len, key_free; + short slot; + + char *val; + int val_len, val_free; +} clusterKeyValHT; + +/* Helper to pull a key/value pair from a HashTable */ +static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, + clusterKeyValHT *kv TSRMLS_DC) +{ + zval **z_val; + unsigned int key_len; + ulong idx; + + // Grab the key, convert it to a string using provided kbuf buffer if it's + // a LONG style key + switch(zend_hash_get_current_key_ex(ht, &(kv->key), &key_len, &idx, 0, ptr)) + { + case HASH_KEY_IS_LONG: + kv->key_len = snprintf(kv->kbuf,sizeof(kv->kbuf),"%ld",(long)idx); + kv->key = kv->kbuf; + break; + case HASH_KEY_IS_STRING: + kv->key_len = (int)(key_len-1); + break; + default: + zend_throw_exception(redis_cluster_exception_ce, + "Internal Zend HashTable error", 0 TSRMLS_CC); + return -1; + } + + // Prefix our key if we need to, set the slot + kv->key_free = redis_key_prefix(c->flags, &(kv->key), &(kv->key_len)); + kv->slot = cluster_hash_key(kv->key, kv->key_len); + + // Now grab our value + if(zend_hash_get_current_data_ex(ht, (void**)&z_val, ptr)==FAILURE) { + zend_throw_exception(redis_cluster_exception_ce, + "Internal Zend HashTable error", 0 TSRMLS_CC); + return -1; + } + + // Serialize our value if required + kv->val_free = redis_serialize(c->flags,*z_val,&(kv->val),&(kv->val_len)); + + // Success + return 0; +} + +/* Helper to pull, prefix, and hash a key from a HashTable value */ +static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, + clusterKeyValHT *kv TSRMLS_DC) { - int key_free; + zval **z_key; + + if(zend_hash_get_current_data_ex(ht, (void**)&z_key, ptr)==FAILURE) { + // Shouldn't happen, but check anyway + zend_throw_exception(redis_cluster_exception_ce, + "Internal Zend HashTable error", 0 TSRMLS_CC); + return -1; + } - // Always want strings - convert_to_string(*z_key_pp); + // Always want to work with strings + convert_to_string(*z_key); - // Set up initial pointers, prefix - *key = Z_STRVAL_PP(z_key_pp); - *key_len = Z_STRLEN_PP(z_key_pp); - key_free = redis_key_prefix(c->flags, key, key_len); + kv->key = Z_STRVAL_PP(z_key); + kv->key_len = Z_STRLEN_PP(z_key); + kv->key_free = redis_key_prefix(c->flags, &(kv->key), &(kv->key_len)); - // Hash the slot - *slot = cluster_hash_key(*key, *key_len); + // Hash our key + kv->slot = cluster_hash_key(kv->key, kv->key_len); - // Return if we need to free the pointer - return key_free; + // Success + return 0; } /* {{{ proto array RedisCluster::mget(array keys) */ PHP_METHOD(RedisCluster, mget) { redisCluster *c = GET_CONTEXT(); clusterMultiCmd mc = {0}; - zval *z_arr, **z_key, *z_ret; + clusterKeyValHT kv; + zval *z_arr, *z_ret; HashTable *ht_arr; HashPosition ptr; - int i=1, argc, key_len, key_free; - short slot, kslot; - char *key; + int i=1, argc; + short slot; // Parse our arguments if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { @@ -437,59 +500,49 @@ PHP_METHOD(RedisCluster, mget) { // Process the first key outside of our loop, so we don't have to check if // it's the first iteration every time, needlessly zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); - if(zend_hash_get_current_data_ex(ht_arr, (void**)&z_key, &ptr)==FAILURE) { - // Shouldn't happen, but check anyway - zend_throw_exception(redis_cluster_exception_ce, - "Internal Zend HashTable error", 0 TSRMLS_CC); + if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { zval_dtor(z_ret); efree(z_ret); RETURN_FALSE; } // Process our key and add it to the command - key_free = process_zval_key(c, z_key, &key, &key_len, &slot); - cluster_multi_add(&mc, key, key_len); + cluster_multi_add(&mc, kv.key, kv.key_len); // Free key if we prefixed - if(key_free) efree(key); + if(kv.key_free) efree(kv.key); // Move to the next key zend_hash_move_forward_ex(ht_arr, &ptr); // Iterate over keys 2...N + slot = kv.slot; while(zend_hash_has_more_elements_ex(ht_arr, &ptr)==SUCCESS) { - if(zend_hash_get_current_data_ex(ht_arr, (void**)&z_key, &ptr) - ==FAILURE) - { - // Shouldn't happen, but check anyway - zend_throw_exception(redis_cluster_exception_ce, - "Internal Zend HashTable error", 0 TSRMLS_CC); + if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { zval_dtor(z_ret); efree(z_ret); RETURN_FALSE; } - // Proceess and hash this key - key_free = process_zval_key(c, z_key, &key, &key_len, &kslot); - // If the slots have changed, kick off the keys we've aggregated - if(slot != kslot) { + if(slot != kv.slot) { // Process this batch of MGET keys - if(mget_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, - z_ret, i==argc)<0) + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, + &mc, z_ret, i==argc, + cluster_mbulk_mget_resp)<0) { RETURN_FALSE; } } // Add this key to the command - cluster_multi_add(&mc, key, key_len); + cluster_multi_add(&mc, kv.key, kv.key_len); // Free key if we prefixed - if(key_free) efree(key); + if(kv.key_free) efree(kv.key); // Update the last slot we encountered, and the key we're on - slot = kslot; + slot = kv.slot; i++; zend_hash_move_forward_ex(ht_arr, &ptr); @@ -497,8 +550,8 @@ PHP_METHOD(RedisCluster, mget) { // If we've got straggler(s) process them if(mc.argc > 0) { - if(mget_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, - z_ret, 1)<0) + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, + z_ret, 1, cluster_mbulk_mget_resp)<0) { RETURN_FALSE; } @@ -508,27 +561,138 @@ PHP_METHOD(RedisCluster, mget) { cluster_multi_free(&mc); } +/* Handler for both MSET and MSETNX */ +static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, + zval *z_ret, cluster_cb cb) +{ + redisCluster *c = GET_CONTEXT(); + clusterKeyValHT kv; + clusterMultiCmd mc = {0}; + zval *z_arr; + HashTable *ht_arr; + HashPosition ptr; + int i=1, argc; + short slot; + + // Parse our arguments + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { + return -1; + } + + // No reason to send zero args + ht_arr = Z_ARRVAL_P(z_arr); + if((argc = zend_hash_num_elements(ht_arr))==0) { + return -1; + } + + // Set up our multi command handler + CLUSTER_MULTI_INIT(mc, kw, kw_len); + + // Process the first key/value pair outside of our loop + zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); + if(get_key_val_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)==-1) return -1; + zend_hash_move_forward_ex(ht_arr, &ptr); + + // Add this to our multi cmd, set slot, free key if we prefixed + cluster_multi_add(&mc, kv.key, kv.key_len); + cluster_multi_add(&mc, kv.val, kv.val_len); + if(kv.key_free) efree(kv.key); + if(kv.val_free) STR_FREE(kv.val); + + // While we've got more keys to set + slot = kv.slot; + while(zend_hash_has_more_elements_ex(ht_arr, &ptr)==SUCCESS) { + // Pull the next key/value pair + if(get_key_val_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)==-1) { + return -1; + } + + // If the slots have changed, process responses + if(slot != kv.slot) { + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + slot, &mc, z_ret, i==argc, cb)<0) + { + return -1; + } + } + + // Add this key and value to our command + cluster_multi_add(&mc, kv.key, kv.key_len); + cluster_multi_add(&mc, kv.val, kv.val_len); + + // Free our key and value if we need to + if(kv.key_free) efree(kv.key); + if(kv.val_free) STR_FREE(kv.val); + + // Update our slot, increment position + slot = kv.slot; + i++; + + // Move on + zend_hash_move_forward_ex(ht_arr, &ptr); + } + + // If we've got stragglers, process them too + if(mc.argc > 0) { + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, + z_ret, 1, cb)<0) + { + return -1; + } + } + + // Free our command + cluster_multi_free(&mc); + + // Success + return 0; +} + /* {{{ proto bool RedisCluster::mset(array keyvalues) */ PHP_METHOD(RedisCluster, mset) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Coming soon!"); + zval *z_ret; + + // Response, defaults to TRUE + MAKE_STD_ZVAL(z_ret); + ZVAL_TRUE(z_ret); + + // Parse args and process. If we get a failure, free zval and return FALSE. + if(cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", + sizeof("MSET")-1, z_ret, cluster_mset_resp)==-1) + { + efree(z_ret); + RETURN_FALSE; + } } /* {{{ proto array RedisCluster::msetnx(array keyvalues) */ PHP_METHOD(RedisCluster, msetnx) { + zval *z_ret; + + // Array response + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + + // Parse args and process. If we get a failure, free mem and return FALSE + if(cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", + sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp)==-1) + { + zval_dtor(z_ret); + efree(z_ret); + RETURN_FALSE; + } } /* }}} */ /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { - CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, - cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, cluster_bool_resp); } /* }}} */ /* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, psetex) { - CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, - cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, cluster_bool_resp); } /* }}} */ From 4b3e05714922f6a89c261ab9ea5fe1d96386c554 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 19 Jun 2014 05:34:10 -0700 Subject: [PATCH 0342/1986] DEL command implemented Modified our MSET handler and made it generic (it has the same general mechanism as DEL, just with a different return type) and implemented the DEL command. We've got the same consideration to make. What to do in the event of a catastrophic failure. --- cluster_library.c | 29 ++++++++++++++++++ cluster_library.h | 4 ++- redis_cluster.c | 77 ++++++++++++++++++++++++++++++++++------------- redis_cluster.h | 1 + 4 files changed, 89 insertions(+), 22 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 282419e807..4b0a7a995d 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1721,6 +1721,35 @@ PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, efree(mctx); } +/* Handler for DEL */ +PHPAPI void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; + + // If we get an invalid reply, inform the client + if(c->reply_type != TYPE_INT) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Invalid reply type returned for DEL command"); + efree(mctx); + return; + } + + // Increment by the number of keys deleted + Z_LVAL_P(mctx->z_multi) += c->reply_len; + + if(mctx->last) { + if(CLUSTER_IS_ATOMIC(c)) { + ZVAL_LONG(return_value, Z_LVAL_P(mctx->z_multi)); + } else { + add_next_index_long(c->multi_resp, Z_LVAL_P(mctx->z_multi)); + } + efree(mctx->z_multi); + } + + efree(ctx); +} + /* Handler for MSET */ PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) diff --git a/cluster_library.h b/cluster_library.h index 9eb2f0c011..61b73746db 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -408,7 +408,9 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb); -/* Handlers for things like MGET/MSET/MSETNX */ +/* Handlers for things like DEL/MGET/MSET/MSETNX */ +PHPAPI void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, diff --git a/redis_cluster.c b/redis_cluster.c index bcbb1bed04..d381abf609 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -45,6 +45,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, mget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, mset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, msetnx, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, del, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, psetex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setnx, NULL, ZEND_ACC_PUBLIC) @@ -468,12 +469,14 @@ static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, return 0; } -/* {{{ proto array RedisCluster::mget(array keys) */ -PHP_METHOD(RedisCluster, mget) { +/* Handler for both MGET and DEL */ +static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, + zval *z_ret, cluster_cb cb) +{ redisCluster *c = GET_CONTEXT(); clusterMultiCmd mc = {0}; clusterKeyValHT kv; - zval *z_arr, *z_ret; + zval *z_arr; HashTable *ht_arr; HashPosition ptr; int i=1, argc; @@ -481,29 +484,23 @@ PHP_METHOD(RedisCluster, mget) { // Parse our arguments if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { - RETURN_FALSE; + return -1; } // No reason to send zero arguments ht_arr = Z_ARRVAL_P(z_arr); if((argc = zend_hash_num_elements(ht_arr))==0) { - RETURN_FALSE; + return -1; } - // Spin up a running array for our response - MAKE_STD_ZVAL(z_ret); - array_init(z_ret); - // Initialize our "multi" command handler with command/len - CLUSTER_MULTI_INIT(mc, "MGET", sizeof("MGET")-1); + CLUSTER_MULTI_INIT(mc, kw, kw_len); // Process the first key outside of our loop, so we don't have to check if // it's the first iteration every time, needlessly zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { - zval_dtor(z_ret); - efree(z_ret); - RETURN_FALSE; + return -1; } // Process our key and add it to the command @@ -519,19 +516,18 @@ PHP_METHOD(RedisCluster, mget) { slot = kv.slot; while(zend_hash_has_more_elements_ex(ht_arr, &ptr)==SUCCESS) { if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { - zval_dtor(z_ret); - efree(z_ret); - RETURN_FALSE; + cluster_multi_free(&mc); + return -1; } // If the slots have changed, kick off the keys we've aggregated if(slot != kv.slot) { // Process this batch of MGET keys if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, - &mc, z_ret, i==argc, - cluster_mbulk_mget_resp)<0) + &mc, z_ret, i==argc, cb)<0) { - RETURN_FALSE; + cluster_multi_free(&mc); + return -1; } } @@ -551,14 +547,18 @@ PHP_METHOD(RedisCluster, mget) { // If we've got straggler(s) process them if(mc.argc > 0) { if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, - z_ret, 1, cluster_mbulk_mget_resp)<0) + z_ret, 1, cb)<0) { - RETURN_FALSE; + cluster_multi_free(&mc); + return -1; } } // Free our command cluster_multi_free(&mc); + + // Success + return 0; } /* Handler for both MSET and MSETNX */ @@ -648,6 +648,41 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, return 0; } +/* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */ +PHP_METHOD(RedisCluster, del) { + zval *z_ret; + + // Initialize a LONG value to zero for our return + MAKE_STD_ZVAL(z_ret); + ZVAL_LONG(z_ret, 0); + + // Parse args, process + if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", + sizeof("DEL")-1, z_ret, cluster_del_resp)<0) + { + efree(z_ret); + RETURN_FALSE; + } +} + +/* {{{ proto array RedisCluster::mget(array keys) */ +PHP_METHOD(RedisCluster, mget) { + zval *z_ret; + + // Array response + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + + // Parse args, process + if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MGET", + sizeof("MGET")-1, z_ret, cluster_mbulk_mget_resp)<0) + { + zval_dtor(z_ret); + efree(z_ret); + RETURN_FALSE; + } +} + /* {{{ proto bool RedisCluster::mset(array keyvalues) */ PHP_METHOD(RedisCluster, mset) { zval *z_ret; diff --git a/redis_cluster.h b/redis_cluster.h index 704fd30b4b..e4fa952319 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -127,6 +127,7 @@ PHP_METHOD(RedisCluster, mget); PHP_METHOD(RedisCluster, mset); PHP_METHOD(RedisCluster, msetnx); PHP_METHOD(RedisCluster, mset); +PHP_METHOD(RedisCluster, del); PHP_METHOD(RedisCluster, dump); PHP_METHOD(RedisCluster, setex); PHP_METHOD(RedisCluster, psetex); From 55e1d54cd011474f9281a8ee9a9a8a9b099928e7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 19 Jun 2014 09:31:18 -0700 Subject: [PATCH 0343/1986] Return getThis() pointer from MULTI --- cluster_library.c | 6 ++++-- redis_cluster.c | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 4b0a7a995d..7ebf780139 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -39,8 +39,10 @@ static void cluster_dist_free_ht(void *p) { int i; for(i=0; i < dl->len; i++) { - if(dl->entry[i].key_free) efree(dl->entry[i].key); - if(dl->entry[i].val_free) efree(dl->entry[i].val); + if(dl->entry[i].key_free) + efree(dl->entry[i].key); + if(dl->entry[i].val_free) + efree(dl->entry[i].val); } efree(dl->entry); diff --git a/redis_cluster.c b/redis_cluster.c index d381abf609..d540e5c261 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -379,6 +379,7 @@ distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx); } else { CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + RETVAL_ZVAL(getThis(), 1, 0); } // Clear out our command but retain allocated memory From 7c54277e4e3a9cea09eaeb6c26ca0a7c0af2e375 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 19 Jun 2014 21:48:16 -0700 Subject: [PATCH 0344/1986] Refactoring, updated handling of MULTI commands Minor formatting changes Updated how we delete context variables, such that our context pointers aren't always going to be a zval** array. This introduces the possibility of creating a memory leak if we have to abort a transaction due to a communications error, but this can be handled later by attaching a void dtor(void*) callback. --- cluster_library.c | 13 +++++++------ cluster_library.h | 2 +- redis_cluster.c | 13 ++++++++----- redis_cluster.h | 9 --------- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 7ebf780139..666234cb19 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1784,16 +1784,15 @@ PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* Raw MULTI BULK reply */ PHPAPI void -cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) { +cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +{ cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_raw, NULL); } /* Unserialize all the things */ PHPAPI void -cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop, NULL); @@ -1803,7 +1802,8 @@ cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, * we will turn into key => value, key => value. */ PHPAPI void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) { + void *ctx) +{ cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_zipstr, NULL); } @@ -1811,7 +1811,8 @@ cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* Handling key,value to key=>value where the values are doubles */ PHPAPI void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) { + void *ctx) +{ cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_zipdbl, NULL); } diff --git a/cluster_library.h b/cluster_library.h index 61b73746db..7d20f3e252 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -197,7 +197,7 @@ typedef struct redisClusterNode { HashTable *slaves; } redisClusterNode; -/* Forward decl */ +/* Forward declarations */ typedef struct clusterFoldItem clusterFoldItem; /* RedisCluster implementation structure */ diff --git a/redis_cluster.c b/redis_cluster.c index d540e5c261..c62e4dc63b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -379,7 +379,6 @@ distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx); } else { CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); - RETVAL_ZVAL(getThis(), 1, 0); } // Clear out our command but retain allocated memory @@ -472,7 +471,7 @@ static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, /* Handler for both MGET and DEL */ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, - zval *z_ret, cluster_cb cb) + zval *z_ret, cluster_cb cb) { redisCluster *c = GET_CONTEXT(); clusterMultiCmd mc = {0}; @@ -547,8 +546,8 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // If we've got straggler(s) process them if(mc.argc > 0) { - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, - z_ret, 1, cb)<0) + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, + &mc, z_ret, 1, cb)<0) { cluster_multi_free(&mc); return -1; @@ -558,6 +557,9 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Free our command cluster_multi_free(&mc); + if(!CLUSTER_IS_ATOMIC(c)) + RETVAL_ZVAL(getThis(), 1, 0); + // Success return 0; } @@ -1705,7 +1707,8 @@ PHP_METHOD(RedisCluster, exec) { // MULTI multi-bulk response handler cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - // Free our queue, and reset MULTI state + // Free our callback queue, any enqueued distributed command context items + // and reset our MULTI state. CLUSTER_FREE_QUEUE(c); CLUSTER_RESET_MULTI(c); } diff --git a/redis_cluster.h b/redis_cluster.h index e4fa952319..45bc288b46 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -40,15 +40,6 @@ clusterFoldItem *_item = c->multi_head, *_tmp; \ while(_item) { \ _tmp = _item->next; \ - if(_item->ctx) { \ - zval **z_arr = (zval**)_item->ctx; int i=0; \ - while(z_arr[i]!=NULL) { \ - zval_dtor(z_arr[i]); \ - efree(z_arr[i]); \ - i++; \ - } \ - efree(z_arr); \ - } \ efree(_item); \ _item = _tmp; \ } \ From 381a6477efa0e2f7a38ea8eb0ac9dd62ef22cfe7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 20 Jun 2014 07:14:08 -0700 Subject: [PATCH 0345/1986] Initial commit for ASKING logic --- cluster_library.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- cluster_library.h | 3 +++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 666234cb19..e745c15913 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -540,6 +540,39 @@ void cluster_free_info_array(clusterNodeInfo **array, int count) { efree(array); } +/* When we're in an ASK redirection state, Redis Cluster wants us to send + * the command only after starting our query with ASKING, or it'll just + * bounce us back and forth until the slots have migrated */ +static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) +{ + REDIS_REPLY_TYPE reply_type; + char buf[255]; + + // Make sure we can send the request + if(redis_check_eof(redis_sock TSRMLS_DC) || + php_stream_write(redis_sock->stream, RESP_ASKING_CMD, + sizeof(RESP_ASKING_CMD)-1) != sizeof(RESP_ASKING_CMD)-1) + { + return -1; + } + + // Read our reply type + if((redis_check_eof(redis_sock TSRMLS_CC) == - 1) || + (reply_type = php_stream_getc(redis_sock->stream TSRMLS_DC) + != TYPE_LINE)) + { + return -1; + } + + // Consume the rest of our response + if(php_stream_gets(redis_sock->stream, buf, sizeof(buf)<0)) { + return -1; + } + + // Success + return 0; +} + /* Get a RedisSock object from the host and port where we have been * directed from an ASK response. We'll first see if we have * connected to this node already, and return that. If not, we @@ -586,9 +619,8 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { } /* Attach a slave to a cluster node */ -int -cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, - clusterNodeInfo *slave TSRMLS_DC) +int cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, + clusterNodeInfo *slave TSRMLS_DC) { redisClusterNode *slave_node; char key[1024]; @@ -970,12 +1002,17 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, RedisSock *redis_sock; redisClusterNode **seed_node; - // If we're in an ASK redirection state, attempt a connection to that - // host and port. Otherwise, try on the requested slot. + // If we're not in ASK redirection, use the slot requested, otherwise + // send our ASKING command and use the asking slot. if(c->redir_type != REDIR_ASK) { redis_sock = SLOT_SOCK(c,slot); } else { redis_sock = cluster_get_asking_sock(c); + + // Redis Cluster wants this command preceded by the "ASKING" command + if(cluster_send_asking(redis_sock TSRMLS_CC)<0) { + return -1; + } } // If the lazy_connect flag is still set, we've not actually diff --git a/cluster_library.h b/cluster_library.h index 7d20f3e252..1ea3dc0161 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -43,6 +43,9 @@ #define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" #define RESP_UNWATCH_CMD "*1\r\n$7\r\nUNWATCH\r\n" +/* ASKING RESP */ +#define RESP_ASKING_CMD "*1\r\n$6\r\nASKING\r\n" + /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ p[4]=='D' && p[5]==' ') From 92606171950ff7bed4b8a5603880d68d5d6d59b6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 20 Jun 2014 10:36:41 -0700 Subject: [PATCH 0346/1986] Don't NULL out the original slot on MOVED error There is no reason to NULL out the originally requested slot when we get a MOVED error, because all we really know is that the data (at slot N) that we're requesting is somewhere else. Instead, just update the node mapping for the destination MOVED slot, if it has changed. The source mapping will change organically as we send more requests. --- cluster_library.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index e745c15913..8d0470ad0c 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1059,12 +1059,9 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, /* Provided a redisCluster object, the slot where we thought data was and * the slot where data was moved, update our node mapping */ -static void cluster_update_slot(redisCluster *c, short orig_slot TSRMLS_CC) { +static void cluster_update_slot(redisCluster *c TSRMLS_CC) { redisClusterNode *node; - // Invalidate original slot - c->master[orig_slot] = NULL; - // Do we already have the new slot mapped if(c->master[c->redir_slot]) { // No need to do anything if it's the same node @@ -1210,8 +1207,8 @@ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, /* Send a command to given slot in our cluster. If we get a MOVED or ASK error * we attempt to send the command to the node as directed. */ -PHPAPI short cluster_send_command(redisCluster *c, short slot, - const char *cmd, int cmd_len TSRMLS_DC) +PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, + int cmd_len TSRMLS_DC) { int resp, rslot = slot; @@ -1260,10 +1257,10 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, 0 TSRMLS_CC); return -1; } - + // In case of a MOVED redirection, update our node mapping if(c->redir_type == REDIR_MOVED) { - cluster_update_slot(c, rslot); + cluster_update_slot(c); } slot = c->redir_slot; } From 03d14ab743ce5e9e2542e4868824b667cd2cf3ea Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 25 Jun 2014 16:17:47 -0700 Subject: [PATCH 0347/1986] Remove options, unsed variable Remove distribution options for things like MGET/MSET as they are handled differently. Remove an unsed variable in our command delivery process --- cluster_library.c | 2 +- cluster_library.h | 7 ------- redis.c | 7 +------ redis_commands.c | 30 ------------------------------ 4 files changed, 2 insertions(+), 44 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 8d0470ad0c..b71d85ef8b 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1210,7 +1210,7 @@ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { - int resp, rslot = slot; + int resp; // Issue commands until we find the right node or fail do { diff --git a/cluster_library.h b/cluster_library.h index 1ea3dc0161..f020d487d7 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -19,13 +19,6 @@ /* Length of a cluster name */ #define CLUSTER_NAME_LEN 40 -/* RedisCluster class constants */ -#define CLUSTER_OPT_DISTRIBUTE 5 - -/* Maintain order of execution vs. efficiency of delivery */ -#define CLUSTER_DIST_OOE 0 -#define CLUSTER_DIST_SPEED 1 - /* The parts for our cluster nodes command */ #define CLUSTER_NODES_HASH 0 #define CLUSTER_NODES_HOST_PORT 1 diff --git a/redis.c b/redis.c index 4b907fd3e6..5e38601ff6 100644 --- a/redis.c +++ b/redis.c @@ -524,12 +524,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) { /* Settings specific to RedisCluster */ static void add_cluster_constants(zend_class_entry *ce) { - /* options */ - add_constant_long(ce, "OPT_DISTRIBUTE", CLUSTER_OPT_DISTRIBUTE); - - /* Distribution settings */ - add_constant_long(ce, "DIST_OOE", CLUSTER_DIST_OOE); - add_constant_long(ce, "DIST_SPEED", CLUSTER_DIST_SPEED); + // TODO: Placeholder if we add constants } /** diff --git a/redis_commands.c b/redis_commands.c index 6e5e13115c..14503f2694 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2377,16 +2377,6 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } - // Check if we're asking for a RedisCluster specific option - if(c != NULL && option > REDIS_OPT_SCAN) { - switch(option) { - case CLUSTER_OPT_DISTRIBUTE: - RETURN_LONG(c->dist_mode); - default: - RETURN_FALSE; - } - } - // Return the requested option switch(option) { case REDIS_OPT_SERIALIZER: @@ -2419,26 +2409,6 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } - // If we've been passed a RedisCluster object, check if the option - // being set is specific to RedisCluster. - if(c != NULL && option > REDIS_OPT_SCAN) { - switch(option) { - case CLUSTER_OPT_DISTRIBUTE: - val_long = atol(val_str); - if(val_long == CLUSTER_DIST_OOE || - val_long == CLUSTER_DIST_SPEED) - { - c->dist_mode = val_long; - RETURN_TRUE; - } else { - RETURN_FALSE; - } - break; - default: - RETURN_FALSE; - } - } - switch(option) { case REDIS_OPT_SERIALIZER: val_long = atol(val_str); From 8b83b8783f75a1a16d106a616678f4e9c6bd1ac8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 26 Jun 2014 12:52:01 -0700 Subject: [PATCH 0348/1986] Initial commit of save like commands For commands like SAVE, BGSAVE, etc we need to change the semantics from how you would call them directly, given that we're communicating with multiple redis cluster nodes. Each of these commands can be called in one of two ways. 1) Pass a single argument, which will be treated as a key, and the command will be executed against the node where we believe that key would live. 2) Pass a host and port, and as long as we know about this node the command will be forwarded there. --- cluster_library.c | 57 ++++++++++++++++-------- cluster_library.h | 3 ++ redis_cluster.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++ redis_cluster.h | 9 ++++ 4 files changed, 162 insertions(+), 18 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index b71d85ef8b..577739b8cc 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -859,24 +859,6 @@ cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { return 0; } -/* Helper to find if we've got a host:port mapped in our cluster nodes. */ -static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, - unsigned short port) -{ - redisClusterNode **ret = NULL; - int key_len; - char key[1024]; - - key_len = snprintf(key,sizeof(key),"%s:%d", host, port); - - if(zend_hash_find(c->nodes, key, key_len+1, (void**)&ret)==SUCCESS) { - return *ret; - } - - // Not found - return NULL; -} - /* Parse the MOVED OR ASK redirection payload when we get such a response * and apply this information to our cluster. If we encounter a parse error * nothing in the cluster will be modified, and -1 is returned. */ @@ -1057,6 +1039,24 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, return -1; } +/* Helper to find if we've got a host:port mapped in our cluster nodes. */ +static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, + unsigned short port) +{ + redisClusterNode **ret = NULL; + int key_len; + char key[1024]; + + key_len = snprintf(key,sizeof(key),"%s:%d", host, port); + + if(zend_hash_find(c->nodes, key, key_len+1, (void**)&ret)==SUCCESS) { + return *ret; + } + + // Not found + return NULL; +} + /* Provided a redisCluster object, the slot where we thought data was and * the slot where data was moved, update our node mapping */ static void cluster_update_slot(redisCluster *c TSRMLS_CC) { @@ -1186,6 +1186,27 @@ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { return 0; } +/* Iterate through our slots, looking for the host/port in question. This + * should perform well enough as in almost all situations, a few or a few + * dozen servers will map all the slots */ +PHPAPI short cluster_find_slot(redisCluster *c, const char *host, + unsigned short port) +{ + int i; + + for(i=0;imaster[i] && c->master[i]->sock && + c->master[i]->sock->port == port && + !strcasecmp(c->master[i]->sock->host, host)) + { + return i; + } + } + + // We didn't find it + return -1; +} + /* Send a command to a specific slot */ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC) diff --git a/cluster_library.h b/cluster_library.h index f020d487d7..c02890c54b 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -345,6 +345,9 @@ PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC); PHPAPI int cluster_reset_multi(redisCluster *c); + +PHPAPI short cluster_find_slot(redisCluster *c, const char *host, + unsigned short port); PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC); diff --git a/redis_cluster.c b/redis_cluster.c index c62e4dc63b..ac0af13b74 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -160,6 +160,14 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, discard, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, watch, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, unwatch, NULL, ZEND_ACC_PUBLIC) + + PHP_ME(RedisCluster, save, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bgsave, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushdb, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushall, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, dbsize, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bgrewriteaof, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lastsave, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -1732,4 +1740,107 @@ PHP_METHOD(RedisCluster, discard) { RETURN_TRUE; } +/* Generic handler for things we want directed at a given node, like SAVE, + * BGSAVE, FLUSHDB, FLUSHALL, etc */ +static void +cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, + REDIS_REPLY_TYPE reply_type, cluster_cb cb) +{ + redisCluster *c = GET_CONTEXT(); + char *cmd, *arg1; + int arg1_len, cmd_len, arg1_free; + short slot; + long arg2; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &arg1, &arg1_len, + &arg2)==FAILURE) + { + RETURN_FALSE; + } + + // One argument means find the node (treated like a key), and two means + // send the command to a specific host and port + if(ZEND_NUM_ARGS() == 1) { + // Treat our argument like a key, and look for the slot that way + arg1_free = redis_key_prefix(c->flags, &arg1, &arg1_len); + slot = cluster_hash_key(arg1, arg1_len); + if(arg1_free) efree(arg1); + } else { + // Find the slot by IP/port + slot = cluster_find_slot(c, (const char *)arg1, (unsigned short)arg2); + if(slot<0) { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", + arg1, arg2); + RETURN_FALSE; + } + } + + // Construct our command + cmd_len = redis_cmd_format_static(&cmd, kw, ""); + + // Kick off our command + if(cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC)<0) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to send command at a specific node", 0 TSRMLS_CC); + efree(cmd); + RETURN_FALSE; + } + + // Our response callback + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + + // Free our command + efree(cmd); +} + +/* {{{ proto RedisCluster::save(string key) + * proto RedisCluster::save(string host, long port) */ +PHP_METHOD(RedisCluster, save) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE", TYPE_LINE, + cluster_bool_resp); +} + +/* {{{ proto RedisCluster::bgsave(string key) + * proto RedisCluster::bgsave(string host, long port) */ +PHP_METHOD(RedisCluster, bgsave) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE", + TYPE_LINE, cluster_bool_resp); +} + +/* {{{ proto RedisCluster::flushdb(string key) + * proto RedisCluster::flushdb(string host, long port) */ +PHP_METHOD(RedisCluster, flushdb) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB", + TYPE_LINE, cluster_bool_resp); +} + +/* {{{ proto RedisCluster::flushall(string key) + * proto RedisCluster::flushall(string host, long port) */ +PHP_METHOD(RedisCluster, flushall) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL", + TYPE_LINE, cluster_bool_resp); +} + +/* {{{ proto RedisCluster::dbsize(string key) + * proto RedisCluster::dbsize(string host, long port) */ +PHP_METHOD(RedisCluster, dbsize) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DBSIZE", + TYPE_LINE, cluster_bool_resp); +} + +/* {{{ proto RedisCluster::bgrewriteaof(string key) + * proto RedisCluster::bgrewriteaof(string host, long port) */ +PHP_METHOD(RedisCluster, bgrewriteaof) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGREWRITEAOF", + TYPE_LINE, cluster_bool_resp); +} + +/* {{{ proto RedisCluster::lastsave(string key) + * proto RedisCluster::lastsave(string host, long port) */ +PHP_METHOD(RedisCluster, lastsave) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LASTSAVE", + TYPE_INT, cluster_long_resp); +} + + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 45bc288b46..321618a60c 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -228,6 +228,15 @@ PHP_METHOD(RedisCluster, discard); PHP_METHOD(RedisCluster, watch); PHP_METHOD(RedisCluster, unwatch); +/* DB saving, etc */ +PHP_METHOD(RedisCluster, save); +PHP_METHOD(RedisCluster, bgsave); +PHP_METHOD(RedisCluster, flushdb); +PHP_METHOD(RedisCluster, flushall); +PHP_METHOD(RedisCluster, dbsize); +PHP_METHOD(RedisCluster, bgrewriteaof); +PHP_METHOD(RedisCluster, lastsave); + /* Introspection */ PHP_METHOD(RedisCluster, getoption); PHP_METHOD(RedisCluster, setoption); From f3440ad9ae4c72101771ec7fedfa03b23d1346cc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 1 Jul 2014 16:32:10 -0700 Subject: [PATCH 0349/1986] Key based SCAN variants Implemented HSCAN, SSCAN, and ZSCAN for cluster --- cluster_library.c | 65 +++++++++++++++++++++++++- cluster_library.h | 8 +++- redis_cluster.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++ redis_cluster.h | 6 +++ redis_commands.c | 38 +++++++++++++++ redis_commands.h | 9 +++- 6 files changed, 240 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 577739b8cc..25ee36d9c1 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -709,9 +709,11 @@ cluster_set_node_info(redisCluster *cluster, HashTable *masters, // Iterate each contiguous slot range, and point to the node for(i=0;islots_size;i++) { - for(j=node->slots[i].start;j<=node->slots[i].end;j++) { + node->slot = node->slots[i].start; + for(j=node->slots[i].start;j<=node->slots[i].end;j++) { cluster->master[j] = node; } + } // Create a host:port key for this node @@ -1096,6 +1098,9 @@ static void cluster_update_slot(redisCluster *c TSRMLS_CC) { c->master[c->redir_slot] = node; } + // Update slot inside of node, so it can be found for command sending + node->slot = c->redir_slot; + // Make sure we unflag this node as a slave, as Redis Cluster will only // ever direct us to master nodes. node->slave = 0; @@ -1647,6 +1652,61 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } } +/* HSCAN, SSCAN, ZSCAN */ +PHPAPI int cluster_kscan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + REDIS_SCAN_TYPE type, long *it) +{ + char *pit; + + // We always want to see a MULTIBULK response with two elements + if(c->reply_type != TYPE_MULTIBULK || c->reply_len != 2) + { + return FAILURE; + } + + // Read the BULK size + if(cluster_check_response(c, c->reply_slot, &c->reply_type TSRMLS_CC),0 || + c->reply_type != TYPE_BULK) + { + return FAILURE; + } + + // Read the iterator + if((pit = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), + c->reply_len TSRMLS_CC))==NULL) + { + return FAILURE; + } + + // Push the new iterator value to our caller + *it = atol(pit); + efree(pit); + + // We'll need another MULTIBULK response for the payload + if(cluster_check_response(c, c->reply_slot, &c->reply_type TSRMLS_CC)<0) + { + return FAILURE; + } + + // Use the proper response callback depending on scan type + switch(type) { + case TYPE_SSCAN: + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); + break; + case TYPE_HSCAN: + cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); + break; + case TYPE_ZSCAN: + cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); + break; + default: + return FAILURE; + } + + // Success + return SUCCESS; +} + /* MULTI BULK response loop where we might pull the next one */ PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb) @@ -1968,6 +2028,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, add_assoc_stringl_ex(z_result, key, 1+key_len, line, line_len, 0); } + efree(key); } } @@ -1997,6 +2058,8 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, key_len = line_len; } else { add_assoc_double_ex(z_result, key, 1+key_len, atof(line)); + efree(key); + efree(line); } } diff --git a/cluster_library.h b/cluster_library.h index c02890c54b..7884f1bf82 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -182,6 +182,9 @@ typedef struct redisClusterNode { /* Our Redis socket in question */ RedisSock *sock; + /* A slot where one of these lives */ + short slot; + /* Contiguous slots we serve */ clusterSlotRange *slots; size_t slots_size; @@ -345,7 +348,6 @@ PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC); PHPAPI int cluster_reset_multi(redisCluster *c); - PHPAPI short cluster_find_slot(redisCluster *c, const char *host, unsigned short port); PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, @@ -417,6 +419,10 @@ PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +/* Response handler for ZSCAN, SSCAN, and HSCAN */ +PHPAPI int cluster_kscan_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, REDIS_SCAN_TYPE type, long *it); + /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); diff --git a/redis_cluster.c b/redis_cluster.c index ac0af13b74..f415ec652d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -36,6 +36,14 @@ zend_class_entry *redis_cluster_ce; zend_class_entry *redis_cluster_exception_ce; zend_class_entry *spl_rte_ce = NULL; +/* Argument info for HSCAN, SSCAN, HSCAN */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(1, i_iterator) + ZEND_ARG_INFO(0, str_pattern) + ZEND_ARG_INFO(0, i_count) +ZEND_END_ARG_INFO(); + /* Function table */ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, __construct, NULL, ZEND_ACC_PUBLIC) @@ -147,6 +155,11 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, psubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, unsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, punsubscribe, NULL, ZEND_ACC_PUBLIC) + + PHP_ME(RedisCluster, scan, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) @@ -1793,6 +1806,110 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, efree(cmd); } +/* Generic method for HSCAN, SSCAN, and ZSCAN */ +static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, + REDIS_SCAN_TYPE type) +{ + redisCluster *c = GET_CONTEXT(); + char *cmd, *pat=NULL, *key=NULL; + int cmd_len, key_len=0, pat_len=0, key_free=0; + short slot; + zval *z_it; + HashTable *hash; + long it, num_ele, count=0; + + // Can't be in MULTI mode + if(!CLUSTER_IS_ATOMIC(c)) { + zend_throw_exception(redis_cluster_exception_ce, + "SCAN type commands can't be called in MULTI mode!", 0 TSRMLS_CC); + RETURN_FALSE; + } + + // Requires a key + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|s!l", &key, &key_len, + &z_it, &pat, &pat_len, &count)==FAILURE) + { + RETURN_FALSE; + } + + // Convert iterator to long if it isn't, update our long iterator if it's + // set and >0, and finish if it's back to zero + if(Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it)<0) { + convert_to_long(z_it); + it = 0; + } else if(Z_LVAL_P(z_it)!=0) { + it = Z_LVAL_P(z_it); + } else { + RETURN_FALSE; + } + + // Apply any key prefix we have, get the slot + key_free = redis_key_prefix(c->flags, &key, &key_len); + slot = cluster_hash_key(key, key_len); + + // If SCAN_RETRY is set, loop until we get a zero iterator or until + // we get non-zero elements. Otherwise we just send the command once. + do { + // Create command + cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, it, pat, pat_len, + count); + + // Send it off + if(cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC)==FAILURE) + { + zend_throw_exception(redis_cluster_exception_ce, + "Couldn't send SCAN command", 0 TSRMLS_CC); + if(key_free) efree(key); + efree(cmd); + RETURN_FALSE; + } + + // Read response + if(cluster_kscan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, + &it)==FAILURE) + { + zend_throw_exception(redis_cluster_exception_ce, + "Couldn't read SCAN response", 0 TSRMLS_CC); + if(key_free) efree(key); + efree(cmd); + RETURN_FALSE; + } + + // Count the elements we got back + hash = Z_ARRVAL_P(return_value); + num_ele = zend_hash_num_elements(hash); + + // Free our command + efree(cmd); + } while(c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0); + + // Free our key + if(key_free) efree(key); + + // Update iterator reference + Z_LVAL_P(z_it) = it; +} + +/* {{{ proto RedisCluster::scan(long it [, string pat, long count]) */ +PHP_METHOD(RedisCluster, scan) { +} + +/* {{{ proto RedisCluster::sscan(string key, long it [string pat, long cnt]) */ +PHP_METHOD(RedisCluster, sscan) { + cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN); +} + +/* {{{ proto RedisCluster::zscan(string key, long it [string pat, long cnt]) */ +PHP_METHOD(RedisCluster, zscan) { + cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN); +} + +/* {{{ proto RedisCluster::hscan(string key, long it [string pat, long cnt]) */ +PHP_METHOD(RedisCluster, hscan) { + cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN); +} + + /* {{{ proto RedisCluster::save(string key) * proto RedisCluster::save(string host, long port) */ PHP_METHOD(RedisCluster, save) { diff --git a/redis_cluster.h b/redis_cluster.h index 321618a60c..e7d2255c06 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -221,6 +221,12 @@ PHP_METHOD(RedisCluster, psubscribe); PHP_METHOD(RedisCluster, unsubscribe); PHP_METHOD(RedisCluster, punsubscribe); +/* SCAN and friends */ +PHP_METHOD(RedisCluster, scan); +PHP_METHOD(RedisCluster, zscan); +PHP_METHOD(RedisCluster, hscan); +PHP_METHOD(RedisCluster, sscan); + /* Transactions */ PHP_METHOD(RedisCluster, multi); PHP_METHOD(RedisCluster, exec); diff --git a/redis_commands.c b/redis_commands.c index 14503f2694..2b07a228be 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -373,6 +373,44 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic to construct SCAN and variant commands */ +int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, + long it, char *pat, int pat_len, long count) +{ + static char *kw[] = {"SCAN","SSCAN","HSCAN","ZSCAN"}; + int argc; + smart_str cmdstr = {0}; + + // Figure out our argument count + argc = 1 + (type!=TYPE_SCAN) + (pat_len>0?2:0) + (count>0?2:0); + + redis_cmd_init_sstr(&cmdstr, argc, kw[type], strlen(kw[type])); + + // Append our key if it's not a regular SCAN command + if(type != TYPE_SCAN) { + redis_cmd_append_sstr(&cmdstr, key, key_len); + } + + // Append cursor + redis_cmd_append_sstr_long(&cmdstr, it); + + // Append count if we've got one + if(count) { + redis_cmd_append_sstr(&cmdstr,"COUNT",sizeof("COUNT")-1); + redis_cmd_append_sstr_long(&cmdstr, count); + } + + // Append pattern if we've got one + if(pat_len) { + redis_cmd_append_sstr(&cmdstr,"MATCH",sizeof("MATCH")-1); + redis_cmd_append_sstr(&cmdstr,pat,pat_len); + } + + // Push command to the caller, return length + *cmd = cmdstr.c; + return cmdstr.len; +} + /* ZRANGE/ZREVRANGE */ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, diff --git a/redis_commands.h b/redis_commands.h index bb04d76b97..64a8a0c015 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -64,7 +64,11 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); -// ZRANGE, ZREVRANGE, ZRANGEBYSCORE, and ZREVRANGEBYSCORE callback type +/* Construct SCAN and similar commands, as well as check iterator */ +int redis_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + REDIS_SCAN_TYPE type, char **cmd, int *cmd_len); + +/* ZRANGE, ZREVRANGE, ZRANGEBYSCORE, and ZREVRANGEBYSCORE callback type */ typedef int (*zrange_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *,char**,int*,int*,short*,void**); @@ -193,6 +197,9 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, + long it, char *pat, int pat_len, long count); + /* Commands that don't communicate with Redis at all (such as getOption, * setOption, _prefix, _serialize, etc). These can be handled in one place * with the method of grabbing our RedisSock* object in different ways From c434f0ad8f8dbbedd1ddaf794206b0d04e31cfab Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 3 Jul 2014 13:45:48 -0700 Subject: [PATCH 0350/1986] Added hiredis like response handling Added a couple of simple mechanisms that parse Redis responses in a hiredis-like way, where replies are returned in clusterReply structures, and can handle N level deep nesting, etc --- cluster_library.c | 188 +++++++++++++++++++++++++++++++++++++++++++++- cluster_library.h | 16 ++++ 2 files changed, 201 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 25ee36d9c1..c06eec610f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -29,9 +29,191 @@ static void cluster_dump_nodes(redisCluster *c) { } } -/* Cluster key distribution helpers. For a small handlful of commands, we - * want to distribute them across 1-N nodes. These methods provide - * simple containers for the purposes of splitting keys/values in this way */ +/* Direct handling of variant replies, in a hiredis like way. These methods + * are used for non userland facing commands, as well as passed through from + * them when the reply is just variant (e.g. eval) */ + +/* Debug function to dump a clusterReply structure recursively */ +static void dump_reply(clusterReply *reply, int indent) { + smart_str buf = {0}; + int i; + char ibuf[255]; + + switch(reply->type) { + case TYPE_ERR: + smart_str_appendl(&buf, "(error) ", sizeof("(error) ")-1); + smart_str_appendl(&buf, reply->str, reply->len); + break; + case TYPE_LINE: + smart_str_appendl(&buf, reply->str, reply->len); + break; + case TYPE_INT: + smart_str_appendl(&buf, "(integer) ", sizeof("(integer) ")-1); + smart_str_append_long(&buf, reply->integer); + break; + case TYPE_BULK: + smart_str_appendl(&buf,"\"", 1); + smart_str_appendl(&buf, reply->str, reply->len); + smart_str_appendl(&buf, "\"", 1); + break; + case TYPE_MULTIBULK: + if(reply->elements == (size_t)-1) { + smart_str_appendl(&buf, "(nil)", sizeof("(nil)")-1); + } else { + for(i=0;ielements;i++) { + dump_reply(reply->element[i], indent+2); + } + } + break; + default: + break; + } + + if(buf.len > 0) { + for(i=0;itype) { + case TYPE_ERR: + case TYPE_LINE: + case TYPE_BULK: + if(free_data) + efree(reply->str); + break; + case TYPE_MULTIBULK: + for(i=0;ielements && reply->element[i]; i++) { + cluster_free_reply(reply->element[i], free_data); + } + efree(reply->element); + break; + default: + break; + } + efree(reply); +} + +static void +cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, + clusterReply **element, int *err TSRMLS_DC) +{ + size_t idx = 0; + clusterReply *r; + int len; + char buf[1024]; + + while(elements-- > 0) { + element[idx] = ecalloc(1, sizeof(clusterReply)); + r = element[idx]; + + // Bomb out, flag error condition on a communication failure + if(redis_read_reply_type(sock, &r->type, &len TSRMLS_CC)<0) { + *err = 1; + return; + } + + r->len = len; + + switch(r->type) { + case TYPE_ERR: + case TYPE_LINE: + if(redis_sock_gets(sock,buf,sizeof(buf),&r->len TSRMLS_CC)<0) { + *err = 1; + return; + } + r->str = estrndup(buf,r->len); + break; + case TYPE_INT: + r->integer = len; + break; + case TYPE_BULK: + r->str = redis_sock_read_bulk_reply(sock,r->len TSRMLS_CC); + if(!r->str) { + *err = 1; + return; + } + break; + case TYPE_MULTIBULK: + r->element = ecalloc(r->len,r->len*sizeof(clusterReply*)); + r->elements = r->len; + cluster_multibulk_resp_recursive(sock, r->elements, r->element, + err TSRMLS_CC); + if(*err) return; + break; + default: + *err = 1; + return; + } + + idx++; + } +} + +/* Read a variant response from the cluster into a redisReply struct */ +clusterReply *cluster_read_resp(redisCluster *c TSRMLS_DC) { + // Allocate our reply, set it's overall type + clusterReply *reply = ecalloc(1, sizeof(clusterReply)); + reply->type = c->reply_type; + + // Local copy of the socket in question + RedisSock *redis_sock = SLOT_SOCK(c,c->reply_slot); + + // Error flag in case we go recursive + int err = 0; + + switch(reply->type) { + case TYPE_INT: + reply->integer = c->reply_len; + break; + case TYPE_BULK: + reply->len = c->reply_len; + reply->str = redis_sock_read_bulk_reply(redis_sock, c->reply_len + TSRMLS_CC); + if(reply->len != -1 && !reply->str) { + cluster_free_reply(reply, 1); + return NULL; + } + break; + case TYPE_MULTIBULK: + reply->elements = c->reply_len; + if(c->reply_len != (size_t)-1) { + reply->element = ecalloc(reply->elements, + sizeof(clusterReply*)*reply->elements); + cluster_multibulk_resp_recursive(redis_sock, reply->elements, + reply->element, &err TSRMLS_CC); + } + break; + default: + cluster_free_reply(reply,1); + return NULL; + } + + // Free/return null on communication error + if(err) { + cluster_free_reply(reply,1); + return NULL; + } + + // Success, return our reply + return reply; +} + +/* Cluster key distribution helpers. For a small handlful of commands, we want + * to distribute them across 1-N nodes. These methods provide simple containers + * for the purposes of splitting keys/values in this way */ /* Free cluster distribution list inside a HashTable */ static void cluster_dist_free_ht(void *p) { diff --git a/cluster_library.h b/cluster_library.h index 7884f1bf82..402b04d746 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -320,6 +320,22 @@ typedef struct clusterMultiCmd { smart_str args; } clusterMultiCmd; +/* Hiredis like structure for processing any sort of reply Redis Cluster might + * give us, including N level deep nested multi-bulk replies. Unlike hiredis + * we don't encode errors, here as that's handled in the cluster structure. */ +typedef struct clusterReply { + REDIS_REPLY_TYPE type; /* Our reply type */ + long long integer; /* Integer reply */ + size_t len; /* Length of our string */ + char *str; /* String reply */ + size_t elements; /* Count of array elements */ + struct clusterReply **element; /* Array elements */ +} clusterReply; + +/* Direct variant response handler */ +clusterReply *cluster_read_resp(redisCluster *c TSRMLS_DC); +void cluster_free_reply(clusterReply *reply, int free_data); + /* Cluster distribution helpers for WATCH */ HashTable *cluster_dist_create(); void cluster_dist_free(HashTable *ht); From 936d2c912c17fa560bf2b8c878a090948f58d491 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 4 Jul 2014 14:43:20 -0700 Subject: [PATCH 0351/1986] Use CLUSTER SLOTS to get node information Use the new command by @mattsta to map the keyspace on construction rather than CLUSTER NODES. Output from CLUSTER SLOTS is much easier to process, and has everything we need. --- cluster_library.c | 489 ++++++++++++++++------------------------------ cluster_library.h | 26 +-- 2 files changed, 173 insertions(+), 342 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index c06eec610f..23e832a0f5 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -12,7 +12,7 @@ extern zend_class_entry *redis_cluster_exception_ce; static void cluster_dump_nodes(redisCluster *c) { redisClusterNode **pp, *p; - int i; + for(zend_hash_internal_pointer_reset(c->nodes); zend_hash_has_more_elements(c->nodes)==SUCCESS; zend_hash_move_forward(c->nodes)) @@ -21,10 +21,9 @@ static void cluster_dump_nodes(redisCluster *c) { p = *pp; const char *slave = (p->slave) ? "slave" : "master"; - php_printf("%d %s %d ", p->sock->port, slave,p->sock->prefix_len); - for(i=0;islots_size;i++) { - php_printf("[%d-%d] ",p->slots[i].start,p->slots[i].end); - } + php_printf("%d %s %d %d", p->sock->port, slave,p->sock->prefix_len, + p->slot); + php_printf("\n"); } } @@ -37,7 +36,6 @@ static void cluster_dump_nodes(redisCluster *c) { static void dump_reply(clusterReply *reply, int indent) { smart_str buf = {0}; int i; - char ibuf[255]; switch(reply->type) { case TYPE_ERR: @@ -162,53 +160,57 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, } } -/* Read a variant response from the cluster into a redisReply struct */ +/* Read the response from a cluster */ clusterReply *cluster_read_resp(redisCluster *c TSRMLS_DC) { - // Allocate our reply, set it's overall type - clusterReply *reply = ecalloc(1, sizeof(clusterReply)); - reply->type = c->reply_type; + return cluster_read_sock_resp(SLOT_SOCK(c,c->reply_slot), c->reply_type, + c->reply_len TSRMLS_CC); +} - // Local copy of the socket in question - RedisSock *redis_sock = SLOT_SOCK(c,c->reply_slot); +/* Read any sort of response from the socket, having already issued the + * command and consumed the reply type and meta info (length) */ +clusterReply* +cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, + size_t len TSRMLS_DC) +{ + clusterReply *r = ecalloc(1, sizeof(clusterReply)); + r->type = type; // Error flag in case we go recursive int err = 0; - switch(reply->type) { + switch(r->type) { case TYPE_INT: - reply->integer = c->reply_len; + r->integer = len; break; case TYPE_BULK: - reply->len = c->reply_len; - reply->str = redis_sock_read_bulk_reply(redis_sock, c->reply_len - TSRMLS_CC); - if(reply->len != -1 && !reply->str) { - cluster_free_reply(reply, 1); + r->len = len; + r->str = redis_sock_read_bulk_reply(redis_sock, len TSRMLS_CC); + if(r->len != -1 && !r->str) { + cluster_free_reply(r, 1); return NULL; } break; case TYPE_MULTIBULK: - reply->elements = c->reply_len; - if(c->reply_len != (size_t)-1) { - reply->element = ecalloc(reply->elements, - sizeof(clusterReply*)*reply->elements); - cluster_multibulk_resp_recursive(redis_sock, reply->elements, - reply->element, &err TSRMLS_CC); + r->elements = len; + if(len != (size_t)-1) { + r->element = ecalloc(len, sizeof(clusterReply*)*len); + cluster_multibulk_resp_recursive(redis_sock, len, r->element, + &err TSRMLS_CC); } break; default: - cluster_free_reply(reply,1); + cluster_free_reply(r,1); return NULL; } // Free/return null on communication error if(err) { - cluster_free_reply(reply,1); + cluster_free_reply(r,1); return NULL; } - // Success, return our reply - return reply; + // Success, return the reply + return r; } /* Cluster key distribution helpers. For a small handlful of commands, we want @@ -391,20 +393,13 @@ static void free_cluster_info(clusterNodeInfo *info) { efree(info); } -/* Destructor callback for a hash table containing inner hash tables */ -static void free_inner_ht(void *data) { - if(*(HashTable**)data) { - zend_hash_destroy(*(HashTable**)data); - efree(*(HashTable**)data); +/* Destructor for slaves */ +static void ht_free_slave(void *data) { + if(*(redisClusterNode**)data) { + cluster_free_node(*(redisClusterNode**)data); } } -/* Destructor for HashTable containing clusterNodeInfo */ -static void free_inner_info(void *data) { - clusterNodeInfo *info = *(clusterNodeInfo**)data; - if(info) free_cluster_info(info); -} - /* Get the hash slot for a given key */ unsigned short cluster_hash_key(const char *key, int len) { int s, e; @@ -583,128 +578,143 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { return 0; } -/* Execute a CLUSTER NODES command against the given seed and return an array - * of nodes that came back, along with setting a pointer as to how many there - * are. */ -static clusterNodeInfo -**cluster_get_nodes(redisCluster *c, int *len, RedisSock *redis_sock TSRMLS_DC) +/* Execute a CLUSTER SLOTS command against the seed socket, and return the + * reply or NULL on failure. */ +clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) { - clusterNodeInfo **nodes; + clusterReply *r; REDIS_REPLY_TYPE type; - char *cmd, *reply, **lines; - int cmd_len, i, j, count; - - // Create our CLUSTER NODES command - cmd_len = redis_cmd_format_static(&cmd, "CLUSTER", "s", "NODES", - sizeof("NODES")-1); + int len; - // Send the command directly to this socket - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)<0) { - efree(cmd); + // Send the command to the socket and consume reply type + if(redis_sock_write(redis_sock, RESP_CLUSTER_SLOTS_CMD, + sizeof(RESP_CLUSTER_SLOTS_CMD)-1 TSRMLS_CC)<0 || + redis_read_reply_type(redis_sock, &type, &len)<0) + { return NULL; } - // Make sure we've got a bulk reply and we can read it - if(redis_read_reply_type(redis_sock, &type, len TSRMLS_CC)<0 || - type!=TYPE_BULK || (reply = redis_sock_read_bulk_reply(redis_sock, - *len TSRMLS_CC))==NULL) + // Consume the rest of our response + if((r = cluster_read_sock_resp(redis_sock, type, len))==NULL || + r->type != TYPE_MULTIBULK || r->elements < 3) { - efree(cmd); + if(r) cluster_free_reply(r, 1); return NULL; } - // Split by \n - lines = split_str_by_delim(reply, "\n", &count); - - // Allocate storage for nodes - nodes = ecalloc(count, sizeof(clusterNodeInfo*)); - - for(i=0;iname = estrndup(info->name, CLUSTER_NAME_LEN); - if(info->master_name) { - node->master_name = estrndup(info->master_name, CLUSTER_NAME_LEN); - } - - // Pull in our contiguous slot ranges - node->slots = emalloc(sizeof(clusterSlotRange)*info->slots_size); - memcpy(node->slots, info->slots, sizeof(clusterSlotRange)*info->slots_size); - node->slots_size = info->slots_size; - - // Set slave flag - node->slave = info->slave; + // It lives in at least this slot, flag slave status + node->slot = slot; + node->slave = slave; + node->slaves = NULL; - // Create and attach our socket - node->sock = redis_sock_create(info->host, info->host_len, info->port, - cluster->timeout, 0, NULL, 0, 1); + // Attach socket + node->sock = redis_sock_create(host, host_len, port, c->timeout, + 0, NULL, 0, 1); - // Return our node return node; } -/* Helper to create a cluster node struct from each bit of info */ -static redisClusterNode* -cluster_node_create_ex(redisCluster *c, const char *name, - const char *master_name, const char *host, - int host_len, unsigned short port, - unsigned short start_slot, - unsigned short end_slot) +/* Attach a slave to a master */ +PHPAPI int +cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) { - clusterNodeInfo info = {0}; + // Allocate our slaves hash table if we haven't yet + if(!master->slaves) { + ALLOC_HASHTABLE(master->slaves); + zend_hash_init(master->slaves, 0, NULL, ht_free_slave, 0); + } + + return zend_hash_next_index_insert(master->slaves, (void*)&slave, + sizeof(redisClusterNode*), NULL)!=SUCCESS; +} - info.name = (char*)name; - info.master_name = (char*)master_name; - info.host = (char*)host; - info.host_len = host_len; - info.port = port; +/* Sanity check/validation for CLUSTER SLOTS command */ +#define VALIDATE_SLOTS_OUTER(r) \ + (r->elements>=3 && r2->element[0]->type == TYPE_INT && \ + r->element[1]->type==TYPE_INT) +#define VALIDATE_SLOTS_INNER(r) \ + (r->type == TYPE_MULTIBULK && r->elements>=2 && \ + r->element[0]->type == TYPE_BULK && r->element[1]->type==TYPE_INT) - info.slots = ecalloc(1, sizeof(clusterSlotRange)); - info.slots_size = 1; +/* Use the output of CLUSTER SLOTS to map our nodes */ +static int cluster_map_slots(redisCluster *c, clusterReply *r) { + int i,j, hlen, klen; + short low, high; + clusterReply *r2, *r3; + redisClusterNode **ppnode, *master, *slave; + unsigned short port; + char *host, key[1024]; - info.slots[0].start = start_slot; - info.slots[0].end = end_slot; + for(i=0;ielements;i++) { + // Inner response + r2 = r->element[i]; + + // Validate outer and master slot + if(!VALIDATE_SLOTS_OUTER(r2) || !VALIDATE_SLOTS_INNER(r2->element[2])) { + return -1; + } + + // Master + r3 = r2->element[2]; + + // Grab our slot range, as well as master host/port + low = (unsigned short)r2->element[0]->integer; + high = (unsigned short)r2->element[1]->integer; + host = r3->element[0]->str; + hlen = r3->element[0]->len; + port = (unsigned short)r3->element[1]->integer; + + // If the node is new, create and add to nodes. Otherwise use it. + klen = snprintf(key,sizeof(key),"%s:%ld",host,port); + if(zend_hash_find(c->nodes,key,klen+1,(void**)&ppnode)==FAILURE) { + master = cluster_node_create(c, host, hlen, port, low, 0); + zend_hash_update(c->nodes, key, klen+1, (void*)&master, + sizeof(redisClusterNode*), NULL); + } else { + master = *ppnode; + } + + // Attach slaves + for(j=3;jelements;j++) { + r3 = r2->element[j]; + if(!VALIDATE_SLOTS_INNER(r3)) { + return -1; + } + + // Attach this node to our slave + slave = cluster_node_create(c, r3->element[0]->str, + (int)r3->element[0]->len, + (unsigned short)r3->element[1]->integer, low, 1); + cluster_node_add_slave(master, slave); + } + + // Attach this node to each slot in the range + for(j=low;j<=high;j++) { + c->master[j]=master; + } + } - return cluster_node_create(c, &info); + // Success + return 0; } /* Free a redisClusterNode structure */ PHPAPI void cluster_free_node(redisClusterNode *node) { - efree(node->name); - if(node->master_name) { - efree(node->master_name); - } if(node->slaves) { zend_hash_destroy(node->slaves); efree(node->slaves); } - efree(node->slots); redis_free_socket(node->sock); efree(node); } @@ -762,7 +772,6 @@ static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) */ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { redisClusterNode **ppNode; - clusterNodeInfo pInfo = {0}; char key[1024]; int key_len; @@ -776,21 +785,9 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { return (*ppNode)->sock; } - // We have yet to encounter this host:port so create - pInfo.name = NULL; - pInfo.master_name = NULL; - pInfo.host = c->redir_host; - pInfo.host_len = strlen(c->redir_host); - pInfo.port = c->redir_port; - - // At this point, we only know one slot it might serve - pInfo.slots = ecalloc(1, sizeof(clusterSlotRange)); - pInfo.slots_size = 1; - pInfo.slots[0].start = c->redir_slot; - pInfo.slots[0].end = c->redir_slot; - // Create a redisClusterNode - *ppNode = cluster_node_create(c, &pInfo); + *ppNode = cluster_node_create(c, c->redir_host, c->redir_host_len, + c->redir_port, c->redir_slot, 0); // Now add it to the nodes we have zend_hash_update(c->nodes, key, key_len+1, (void*)ppNode, @@ -800,116 +797,6 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { return (*ppNode)->sock; } -/* Attach a slave to a cluster node */ -int cluster_node_add_slave(redisCluster *cluster, redisClusterNode *master, - clusterNodeInfo *slave TSRMLS_DC) -{ - redisClusterNode *slave_node; - char key[1024]; - int key_len; - - // Allocate our hash table if needed - if(!master->slaves) { - ALLOC_HASHTABLE(master->slaves); - zend_hash_init(master->slaves, 0, NULL, NULL, 0); - } - - // Inherit the start - end slots from our master - slave->slots = ecalloc(master->slots_size, sizeof(clusterSlotRange)); - slave->slots_size = master->slots_size; - memcpy(slave->slots, master->slots, - master->slots_size * sizeof(clusterSlotRange)); - - // Flag this node as a slave - slave->slave = 1; - - // Create our slave node - slave_node = cluster_node_create(cluster, slave); - - // Attach it to our slave - if(zend_hash_next_index_insert(master->slaves, (void*)&slave_node, - sizeof(redisClusterNode*), NULL)==FAILURE) - { - return -1; - } - - // Index by host:port - key_len = snprintf(key, sizeof(key), "%s:%u", slave_node->sock->host, - slave_node->sock->port); - - // Add this to our overall table of nodes - zend_hash_update(cluster->nodes, key, key_len+1, - (void*)&slave_node, sizeof(redisClusterNode*),NULL); - - // Success - return 0; -} - -/* Given masters and slaves from node discovery, set up our nodes */ -static int -cluster_set_node_info(redisCluster *cluster, HashTable *masters, - HashTable *slaves TSRMLS_DC) -{ - clusterNodeInfo **info, **slave; - HashTable **master_slaves; - redisClusterNode *node; - char *name, key[1024]; - int i, j, key_len; - uint name_len; - ulong idx; - - // Iterate over our master nodes - for(zend_hash_internal_pointer_reset(masters); - zend_hash_has_more_elements(masters)==SUCCESS; - zend_hash_move_forward(masters)) - { - // Get our node name (stored in the key) as well as information. - zend_hash_get_current_key_ex(masters, &name, &name_len, &idx, 0, NULL); - zend_hash_get_current_data(masters, (void**)&info); - - // Create our master node - node = cluster_node_create(cluster, *info); - - // If we find slaves for this master, add them to the node - if(zend_hash_find(slaves, (*info)->name, CLUSTER_NAME_LEN+1, - (void**)&master_slaves)==SUCCESS) - { - // Iterate through the slaves for this node - for(zend_hash_internal_pointer_reset(*master_slaves); - zend_hash_has_more_elements(*master_slaves)==SUCCESS; - zend_hash_move_forward(*master_slaves)) - { - zend_hash_get_current_data(*master_slaves, (void**)&slave); - - if(cluster_node_add_slave(cluster, node, *slave TSRMLS_CC)!=0) { - zend_throw_exception(redis_cluster_exception_ce, - "Can't add slave node to master instance", 0 TSRMLS_CC); - return -1; - } - } - } - - // Iterate each contiguous slot range, and point to the node - for(i=0;islots_size;i++) { - node->slot = node->slots[i].start; - for(j=node->slots[i].start;j<=node->slots[i].end;j++) { - cluster->master[j] = node; - } - - } - - // Create a host:port key for this node - key_len = snprintf(key, sizeof(key), "%s:%u", node->sock->host, - node->sock->port); - - zend_hash_update(cluster->nodes, key, key_len+1, - (void*)&node, sizeof(redisClusterNode*),NULL); - } - - // Success - return 0; -} - /* Initialize seeds */ PHPAPI int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { @@ -956,20 +843,18 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { /* Initial mapping of our cluster keyspace */ PHPAPI int -cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { +cluster_map_keyspace(redisCluster *c TSRMLS_DC) { RedisSock **seed; - clusterNodeInfo **node; + clusterReply *slots; + int mapped=0; - HashTable *masters, *slaves, **sub_slaves; - int count, valid=0, i; - - // Iterate over our seeds attempting to map keyspace - for(zend_hash_internal_pointer_reset(cluster->seeds); - zend_hash_has_more_elements(cluster->seeds) == SUCCESS; - zend_hash_move_forward(cluster->seeds)) + // Iterate over seeds until we can get slots + for(zend_hash_internal_pointer_reset(c->seeds); + !mapped && zend_hash_has_more_elements(c->seeds) == SUCCESS; + zend_hash_move_forward(c->seeds)) { // Grab the redis_sock for this seed - zend_hash_get_current_data(cluster->seeds, (void**)&seed); + zend_hash_get_current_data(c->seeds, (void**)&seed); // Attempt to connect to this seed node if(redis_sock_connect(*seed TSRMLS_CC)!=0) { @@ -977,69 +862,26 @@ cluster_map_keyspace(redisCluster *cluster TSRMLS_DC) { } // Parse out cluster nodes. Flag mapped if we are valid - if((node = cluster_get_nodes(cluster, &count, *seed TSRMLS_CC))) { - valid = 1; - break; + slots = cluster_get_slots(*seed TSRMLS_CC); + if(slots) mapped = !cluster_map_slots(c, slots); + + // Bin anything mapped, if we failed somewhere + if(!mapped && slots) { + memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); } } + // Clean up slots reply if we got one + if(slots) cluster_free_reply(slots, 1); + // Throw an exception if we couldn't map - if(!valid) { + if(!mapped) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't map cluster keyspace using any provided seed", 0 TSRMLS_CC); return -1; } - // Hashes for masters and slaves - ALLOC_HASHTABLE(masters); - zend_hash_init(masters, 0, NULL, free_inner_info, 0); - ALLOC_HASHTABLE(slaves); - zend_hash_init(slaves, 0, NULL, free_inner_ht, 0); - - // Iterate nodes, splitting into master and slave groups - for(i=0;islave) { - zend_hash_update(masters, node[i]->name, CLUSTER_NAME_LEN+1, - (void*)&node[i], sizeof(clusterNodeInfo*), NULL); - } else { - HashTable *ht_inner; - - // Determine if we've already got at least one slave for this - // master node. - if(zend_hash_find(slaves, node[i]->master_name, CLUSTER_NAME_LEN+1, - (void**)&sub_slaves)==FAILURE) - { - ALLOC_HASHTABLE(ht_inner); - zend_hash_init(ht_inner, 0, NULL, free_inner_info, 0); - - zend_hash_update(slaves, node[i]->master_name, - CLUSTER_NAME_LEN+1, (void*)&ht_inner, sizeof(HashTable*), - NULL); - } else { - ht_inner = *sub_slaves; - } - - // Add to this masters slaves. - zend_hash_next_index_insert(ht_inner, (void*)&node[i], - sizeof(clusterNodeInfo*), NULL); - } - } - - // Now that we have the key space mapped by master ID, we can set - // socket information on them for communication. - cluster_set_node_info(cluster, masters, slaves TSRMLS_CC); - - // Free our array of clusterNodeInfo* objects. The HashTables will clean - // up the clusterNodeInfo* pointers themselves. - efree(node); - - // Destroy our hash tables - zend_hash_destroy(masters); - efree(masters); - zend_hash_destroy(slaves); - efree(slaves); - return 0; } @@ -1215,7 +1057,7 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, php_stream_write((*seed_node)->sock->stream, cmd, sz)==sz) { // Just return the first slot we think this node handles - return (*seed_node)->slots[0].start; + return (*seed_node)->slot; } } @@ -1261,9 +1103,8 @@ static void cluster_update_slot(redisCluster *c TSRMLS_CC) { c->master[c->redir_slot] = node; } else { // Create our node - node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, - c->redir_host_len, c->redir_port, c->redir_slot, - c->redir_slot); + node = cluster_node_create(c, c->redir_host, c->redir_host_len, + c->redir_port, c->redir_slot, 0); // Now point our slot at the node c->master[c->redir_slot] = node; @@ -1272,8 +1113,8 @@ static void cluster_update_slot(redisCluster *c TSRMLS_CC) { // Check to see if the ip and port are mapped node = cluster_find_node(c, c->redir_host, c->redir_port); if(!node) { - node = cluster_node_create_ex(c, NULL, NULL, c->redir_host, - c->redir_host_len, c->redir_port, c->redir_slot, c->redir_slot); + node = cluster_node_create(c, c->redir_host, c->redir_host_len, + c->redir_port, c->redir_slot, 0); } // Map the slot to this node diff --git a/cluster_library.h b/cluster_library.h index 402b04d746..b8c58eba9f 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -31,13 +31,12 @@ #define CLUSTER_SLOTS 8 /* Complete representation for various MULTI/EXEC commands in RESP */ -#define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" -#define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" -#define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" -#define RESP_UNWATCH_CMD "*1\r\n$7\r\nUNWATCH\r\n" - -/* ASKING RESP */ -#define RESP_ASKING_CMD "*1\r\n$6\r\nASKING\r\n" +#define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" +#define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" +#define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" +#define RESP_UNWATCH_CMD "*1\r\n$7\r\nUNWATCH\r\n" +#define RESP_CLUSTER_SLOTS_CMD "*2\r\n$7\r\nCLUSTER\r\n$5\r\nSLOTS\r\n" +#define RESP_ASKING_CMD "*1\r\n$6\r\nASKING\r\n" /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ @@ -175,20 +174,12 @@ typedef struct clusterNodeInfo { /* A Redis Cluster master node */ typedef struct redisClusterNode { - /* Our cluster ID and master ID */ - char *name; - char *master_name; - /* Our Redis socket in question */ RedisSock *sock; /* A slot where one of these lives */ short slot; - /* Contiguous slots we serve */ - clusterSlotRange *slots; - size_t slots_size; - /* Is this a slave node */ unsigned short slave; @@ -334,6 +325,8 @@ typedef struct clusterReply { /* Direct variant response handler */ clusterReply *cluster_read_resp(redisCluster *c TSRMLS_DC); +clusterReply *cluster_read_sock_resp(RedisSock *redis_sock, + REDIS_REPLY_TYPE type, size_t reply_len TSRMLS_DC); void cluster_free_reply(clusterReply *reply, int free_data); /* Cluster distribution helpers for WATCH */ @@ -376,9 +369,6 @@ PHPAPI void cluster_free_node(redisClusterNode *node); PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len TSRMLS_DC); -PHPAPI int cluster_node_add_slave(redisCluster *c, redisClusterNode *master, - clusterNodeInfo *slave TSRMLS_DC); - /* * Redis Cluster response handlers. All of our response handlers take the * following form: From fca97491abb750bac57d28c37478a76864f61c98 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 4 Jul 2014 14:50:19 -0700 Subject: [PATCH 0352/1986] Remove legacy clusterNodeInfo structure --- cluster_library.c | 36 +++--------------------------------- cluster_library.h | 15 --------------- 2 files changed, 3 insertions(+), 48 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 23e832a0f5..49682b9b94 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -376,23 +376,6 @@ static void cluster_set_err(redisCluster *c, char *err, int err_len) } } -/* Free a cluster info structure */ -static void free_cluster_info(clusterNodeInfo *info) { - if(info->name) { - efree(info->name); - } - if(info->master_name) { - efree(info->master_name); - } - if(info->host) { - efree(info->host); - } - if(info->slots) { - efree(info->slots); - } - efree(info); -} - /* Destructor for slaves */ static void ht_free_slave(void *data) { if(*(redisClusterNode**)data) { @@ -484,8 +467,7 @@ static char **split_str_by_delim(char *str, char *delim, int *len) { return array; } -/* Simple function to split a string into up to N parts */ -static int +/*static int cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { char **array, *p, *p2; int count, i; @@ -515,7 +497,7 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { info->host = estrndup(sock->host, info->host_len); info->port = sock->port; } else if((p = strchr(array[CLUSTER_NODES_HOST_PORT], ':'))!=NULL) { - /* Null terminate at the : character */ + // Null terminate at the : character *p = '\0'; info->seed = 0; @@ -577,6 +559,7 @@ cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { // Success! return 0; } +*/ /* Execute a CLUSTER SLOTS command against the seed socket, and return the * reply or NULL on failure. */ @@ -719,19 +702,6 @@ PHPAPI void cluster_free_node(redisClusterNode *node) { efree(node); } -/* Free an array of clusterNodeInfo structs */ -void cluster_free_info_array(clusterNodeInfo **array, int count) { - int i; - - // Free each info structure we've got - for(i=0;i Date: Sun, 6 Jul 2014 10:52:58 -0700 Subject: [PATCH 0353/1986] Comment update --- cluster_library.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.h b/cluster_library.h index facbce05cc..fffe83d7b8 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -30,7 +30,7 @@ #define CLUSTER_NODES_CONNECTED 7 #define CLUSTER_SLOTS 8 -/* Complete representation for various MULTI/EXEC commands in RESP */ +/* Complete representation for various commands in RESP */ #define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" #define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" #define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" From b0152f2c0d40eb0cb6f6e5ed52c529d7e8302772 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 8 Jul 2014 10:32:15 -0700 Subject: [PATCH 0354/1986] Remove another deprecated struct, update comment --- cluster_library.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index fffe83d7b8..028f772439 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -152,11 +152,6 @@ typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void* TSRMLS_DC); /* Specific destructor to free a cluster object */ // void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); -/* Slot range structure */ -typedef struct clusterSlotRange { - unsigned short start, end; -} clusterSlotRange; - /* A Redis Cluster master node */ typedef struct redisClusterNode { /* Our Redis socket in question */ @@ -355,9 +350,10 @@ PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len TSRMLS_DC); /* - * Redis Cluster response handlers. All of our response handlers take the + * Redis Cluster response handlers. Our response handlers generally take the * following form: - * PHPAPI void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c) + * PHPAPI void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + * void *ctx) * * Reply handlers are responsible for setting the PHP return value (either to * something valid, or FALSE in the case of some failures). From ba9485d7ef881a99539882bba60106759cbc12e7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 8 Jul 2014 15:37:01 -0700 Subject: [PATCH 0355/1986] EVAL/EVALSHA Implemented EVAL and EVALSHA commands for cluster. Thes commands present a bit of an issue from the context of a client. Given that the code being executed can be any arbitrary LUA code, phpredis can't always protect against trying to access keys that live on a different cluster node, especially if the "keys count" isn't passed correctly, or in cases where the keys are generated in the script. Work in progress, but there is unlikely a perfect solution to this at all, given the nature of Redis Cluster and LUA execution. --- cluster_library.c | 91 ++++++++++++++++++++++++++++++++++++ cluster_library.h | 4 ++ redis_cluster.c | 117 +++++++++++++++++++++++++++++++++++++++++++++- redis_cluster.h | 2 + 4 files changed, 213 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 49682b9b94..a44f4ea2b6 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1611,6 +1611,97 @@ PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, } } +/* Recursive MULTI BULK -> PHP style response handling */ +static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) +{ + zval *z_sub_ele; + + switch(r->type) { + case TYPE_INT: + add_next_index_long(z_ret, r->integer); + break; + case TYPE_LINE: + add_next_index_bool(z_ret, 1); + break; + case TYPE_BULK: + add_next_index_stringl(z_ret, r->str, r->len, 0); + break; + case TYPE_MULTIBULK: + MAKE_STD_ZVAL(z_sub_ele); + array_init(z_sub_ele); + cluster_mbulk_variant_resp(r, z_sub_ele); + add_next_index_zval(z_ret, z_sub_ele); + break; + default: + add_next_index_bool(z_ret, 0); + break; + } +} + +/* Variant response handling, for things like EVAL and various other responses + * where we just map the replies from Redis type values to PHP ones directly. */ +PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + clusterReply *r; + zval *z_arr; + + // Make sure we can read it + if((r = cluster_read_resp(c TSRMLS_CC))==NULL) { + CLUSTER_RETURN_FALSE(c); + } + + // Handle ATOMIC vs. MULTI mode in a seperate switch + if(CLUSTER_IS_ATOMIC(c)) { + switch(r->type) { + case TYPE_INT: + RETVAL_LONG(r->integer); + break; + case TYPE_LINE: + RETVAL_TRUE; + break; + case TYPE_BULK: + RETVAL_STRINGL(r->str, r->len, 0); + break; + case TYPE_MULTIBULK: + MAKE_STD_ZVAL(z_arr); + array_init(z_arr); + cluster_mbulk_variant_resp(r, z_arr); + + *return_value = *z_arr; + efree(z_arr); + break; + default: + RETVAL_FALSE; + break; + } + } else { + switch(r->type) { + case TYPE_INT: + add_next_index_long(c->multi_resp, r->integer); + break; + case TYPE_LINE: + add_next_index_bool(c->multi_resp, 1); + break; + case TYPE_BULK: + add_next_index_stringl(c->multi_resp, r->str, r->len, 0); + break; + case TYPE_MULTIBULK: + MAKE_STD_ZVAL(z_arr); + array_init(z_arr); + cluster_mbulk_variant_resp(r, z_arr); + add_next_index_zval(c->multi_resp, z_arr); + break; + default: + add_next_index_bool(c->multi_resp, 0); + break; + } + } + + // Free our response structs, but not allocated data itself + cluster_free_reply(r, 0); +} + /* Generic MULTI BULK response processor */ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb cb, void *ctx) diff --git a/cluster_library.h b/cluster_library.h index 028f772439..26df6b2e40 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -378,6 +378,10 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +/* Generic/Variant handler for stuff like EVAL */ +PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); + /* MULTI BULK response functions */ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb func, void *ctx); diff --git a/redis_cluster.c b/redis_cluster.c index f415ec652d..18baa1558f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -44,6 +44,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2) ZEND_ARG_INFO(0, i_count) ZEND_END_ARG_INFO(); +/* Argument infor for SCAN */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 2) + ZEND_ARG_INFO(1, i_node_iterator) + ZEND_ARG_INFO(1, i_iterator) + ZEND_ARG_INFO(0, str_pattern) + ZEND_ARG_INFO(0, i_count) +ZEND_END_ARG_INFO(); + /* Function table */ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, __construct, NULL, ZEND_ACC_PUBLIC) @@ -155,7 +163,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, psubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, unsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, punsubscribe, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(RedisCluster, eval, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, evalsha, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, scan, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) @@ -1517,6 +1527,111 @@ PHP_METHOD(RedisCluster, punsubscribe) { } /* }}} */ +/* Parse arguments for EVAL or EVALSHA in the context of cluster. If we aren't + * provided any "keys" as arguments, the only choice is to send the command to + * a random node in the cluster. If we are passed key arguments the best we + * can do is make sure they all map to the same "node", as we don't know what + * the user is actually doing in the LUA source itself. */ +/* EVAL/EVALSHA */ +static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + char *kw, int kw_len) +{ + redisClusterNode *node=NULL; + char *lua, *key; + int key_free, args_count=0, lua_len, key_len; + zval *z_arr=NULL, **z_ele; + HashTable *ht_arr; + HashPosition ptr; + long num_keys = 0; + short slot; + smart_str cmdstr = {0}; + + /* Parse args */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, + &z_arr, &num_keys)==FAILURE) + { + RETURN_FALSE; + } + + /* Grab arg count */ + if(z_arr != NULL) { + ht_arr = Z_ARRVAL_P(z_arr); + args_count = zend_hash_num_elements(ht_arr); + } + + /* Format header, add script or SHA, and the number of args which are keys */ + redis_cmd_init_sstr(&cmdstr, 2 + args_count, kw, kw_len); + redis_cmd_append_sstr(&cmdstr, lua, lua_len); + redis_cmd_append_sstr_long(&cmdstr, num_keys); + + // Iterate over our args if we have any + if(args_count > 0) { + for(zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); + zend_hash_get_current_data_ex(ht_arr, (void**)&z_ele, &ptr)==SUCCESS; + zend_hash_move_forward_ex(ht_arr, &ptr)) + { + convert_to_string(*z_ele); + key = Z_STRVAL_PP(z_ele); + key_len = Z_STRLEN_PP(z_ele); + + /* If we're still on a key, prefix it check node */ + if(num_keys-- > 0) { + key_free = redis_key_prefix(c->flags, &key, &key_len); + slot = cluster_hash_key(key, key_len); + + /* validate that this key maps to the same node */ + if(node && c->master[slot] != node) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Keys appear to map to different nodes"); + RETURN_FALSE; + } + + node = c->master[slot]; + } else { + key_free = 0; + } + + /* Append this key/argument */ + redis_cmd_append_sstr(&cmdstr, key, key_len); + + /* Free key if we prefixed */ + if(key_free) efree(key); + } + } else { + /* Pick a slot at random, we're being told there are no keys */ + slot = rand() % REDIS_CLUSTER_MOD; + } + + if(cluster_send_command(c, slot, cmdstr.c, cmdstr.len TSRMLS_CC)<0) { + efree(cmdstr.c); + RETURN_FALSE; + } + + if(CLUSTER_IS_ATOMIC(c)) { + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + void *ctx = NULL; + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); + RETURN_ZVAL(getThis(), 1, 0); + } + + efree(cmdstr.c); +} + +/* {{{ proto mixed RedisCluster::eval(string script, [array args, int numkeys) */ +PHP_METHOD(RedisCluster, eval) { + cluster_eval_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), + "EVAL", 4); +} +/* }}} */ + +/* {{{ proto mixed RedisCluster::evalsha(string sha, [array args, int numkeys]) */ +PHP_METHOD(RedisCluster, evalsha) { + cluster_eval_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), + "EVALSHA", 7); +} +/* }}} */ + /* Commands that do not interact with Redis, but just report stuff about * various options, etc */ diff --git a/redis_cluster.h b/redis_cluster.h index e7d2255c06..66c4c35cf0 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -220,6 +220,8 @@ PHP_METHOD(RedisCluster, subscribe); PHP_METHOD(RedisCluster, psubscribe); PHP_METHOD(RedisCluster, unsubscribe); PHP_METHOD(RedisCluster, punsubscribe); +PHP_METHOD(RedisCluster, eval); +PHP_METHOD(RedisCluster, evalsha); /* SCAN and friends */ PHP_METHOD(RedisCluster, scan); From 4462913cb7b9e46034c719df220cf85f6c400a25 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 8 Jul 2014 16:54:40 -0700 Subject: [PATCH 0356/1986] Added _masters method Added a helper method RedisCluster::_masters to get every master that phpredis knows about, for the purposes of things like scanning across the whole keyspace --- cluster_library.h | 4 ++-- redis_cluster.c | 34 ++++++++++++++++++++++++++++++---- redis_cluster.h | 1 + 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index 26df6b2e40..26b7406faa 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -13,8 +13,8 @@ /* Minimum valid CLUSTER NODES line element count and the minimum we expect if there are slots */ -#define CLUSTER_MIN_NODE_LINE 8 -#define CLUSTER_MIN_SLOTS_COUNT 9 +#define CLUSTER_MIN_NODE_LINE 8 +#define CLUSTER_MIN_SLOTS_COUNT 9 /* Length of a cluster name */ #define CLUSTER_NAME_LEN 40 diff --git a/redis_cluster.c b/redis_cluster.c index 18baa1558f..5bc83e2a8a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -177,7 +177,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _serialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _unserialize, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(RedisCluster, _masters, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, multi, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exec, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, discard, NULL, ZEND_ACC_PUBLIC) @@ -1670,6 +1671,30 @@ PHP_METHOD(RedisCluster, _unserialize) { } /* }}} */ +/* {{{ proto array RedisCluster::_masters() */ +PHP_METHOD(RedisCluster, _masters) { + redisCluster *c = GET_CONTEXT(); + zval *z_ret; + redisClusterNode **node; + char buf[1024]; + size_t len; + + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + + for(zend_hash_internal_pointer_reset(c->nodes); + zend_hash_get_current_data(c->nodes, (void**)&node)==SUCCESS; + zend_hash_move_forward(c->nodes)) + { + len = snprintf(buf, sizeof(buf), "%s:%d", (*node)->sock->host, + (*node)->sock->port); + add_next_index_stringl(z_ret, buf, (int)len, 1); + } + + *return_value = *z_ret; + efree(z_ret); +} + /* * Transaction handling */ @@ -1940,9 +1965,9 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } - // Requires a key - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|s!l", &key, &key_len, - &z_it, &pat, &pat_len, &count)==FAILURE) + /* Parse arguments */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|s!l", &key, + &key_len, &z_it, &pat, &pat_len, &count)==FAILURE) { RETURN_FALSE; } @@ -2007,6 +2032,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, /* {{{ proto RedisCluster::scan(long it [, string pat, long count]) */ PHP_METHOD(RedisCluster, scan) { + } /* {{{ proto RedisCluster::sscan(string key, long it [string pat, long cnt]) */ diff --git a/redis_cluster.h b/redis_cluster.h index 66c4c35cf0..9c9fba459c 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -251,5 +251,6 @@ PHP_METHOD(RedisCluster, setoption); PHP_METHOD(RedisCluster, _prefix); PHP_METHOD(RedisCluster, _serialize); PHP_METHOD(RedisCluster, _unserialize); +PHP_METHOD(RedisCluster, _masters); #endif From 488cd3c3163be2178fe3354b2304b16fd874ffda Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 8 Jul 2014 20:14:18 -0700 Subject: [PATCH 0357/1986] SCAN command Initial commit of the SCAN command for cluster. As a first iteration, the command takes a host:port string (which can be retreived from the introspection method RedisCluster::_masters()), to scan either everything in the cluster (e.g. in a loop), or one in particular. A simple way to scan everything in the whole cluster Keyspace $obj_c->setOption(RedisCluster::OPT_SCAN, RedisCluster::SCAN_RETRY); foreach($obj_c->_masters() as $str_hash) { $it = NULL; while($arr = $obj_c->scan($it, $str_hash)) { foreach($arr as $key) { echo "$str_hash -> " . $key . "\n"; } } } --- cluster_library.c | 5 ++- cluster_library.h | 2 +- redis_cluster.c | 80 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index a44f4ea2b6..67e8fc75b7 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1737,7 +1737,7 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* HSCAN, SSCAN, ZSCAN */ -PHPAPI int cluster_kscan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, REDIS_SCAN_TYPE type, long *it) { char *pit; @@ -1774,6 +1774,9 @@ PHPAPI int cluster_kscan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Use the proper response callback depending on scan type switch(type) { + case TYPE_SCAN: + cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); + break; case TYPE_SSCAN: cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); break; diff --git a/cluster_library.h b/cluster_library.h index 26b7406faa..1c916fac86 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -411,7 +411,7 @@ PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* Response handler for ZSCAN, SSCAN, and HSCAN */ -PHPAPI int cluster_kscan_resp(INTERNAL_FUNCTION_PARAMETERS, +PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, REDIS_SCAN_TYPE type, long *it); /* MULTI BULK processing callbacks */ diff --git a/redis_cluster.c b/redis_cluster.c index 5bc83e2a8a..39edb5a27d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -46,8 +46,8 @@ ZEND_END_ARG_INFO(); /* Argument infor for SCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 2) - ZEND_ARG_INFO(1, i_node_iterator) ZEND_ARG_INFO(1, i_iterator) + ZEND_ARG_INFO(0, str_node) ZEND_ARG_INFO(0, str_pattern) ZEND_ARG_INFO(0, i_count) ZEND_END_ARG_INFO(); @@ -166,7 +166,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, eval, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, evalsha, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, scan, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, scan, arginfo_scan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) @@ -2005,7 +2005,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, } // Read response - if(cluster_kscan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, + if(cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, &it)==FAILURE) { zend_throw_exception(redis_cluster_exception_ce, @@ -2030,9 +2030,81 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, Z_LVAL_P(z_it) = it; } -/* {{{ proto RedisCluster::scan(long it [, string pat, long count]) */ +/* {{{ proto RedisCluster::scan(string master, long it [, string pat, long cnt]) */ PHP_METHOD(RedisCluster, scan) { + redisCluster *c = GET_CONTEXT(); + redisClusterNode **n; + char *cmd, *node, *pat=NULL, *key=NULL; + int pat_len=0, node_len, cmd_len; + short slot; + zval *z_it; + HashTable *hash; + long it, num_ele, count=0; + + /* Can't be in MULTI mode */ + if(!CLUSTER_IS_ATOMIC(c)) { + zend_throw_exception(redis_cluster_exception_ce, + "SCAN type commands can't be called in MULTI mode", 0 TSRMLS_CC); + RETURN_FALSE; + } + + /* Parse arguments */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/s|s!l", &z_it, &node, + &node_len, &pat, &pat_len, &count)==FAILURE) + { + RETURN_FALSE; + } + + /* Convert or update iterator */ + if(Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it)<0) { + convert_to_long(z_it); + it = 0; + } else if(Z_LVAL_P(z_it)!=0) { + it = Z_LVAL_P(z_it); + } else { + RETURN_FALSE; + } + + /* With SCAN_RETRY on, loop until we get some keys, otherwise just return + * what Redis does, as it does */ + do { + /* Construct our command */ + cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, + count); + + /* Find this slot by node */ + if(zend_hash_find(c->nodes, node, node_len+1, (void**)&n)==FAILURE) { + zend_throw_exception(redis_cluster_exception_ce, + "Unknown host:port passed to SCAN command", 0 TSRMLS_CC); + efree(cmd); + RETURN_FALSE; + } + + // Send it to the node in question + slot = (*n)->slot; + if(cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC)<0) + { + zend_throw_exception(redis_cluster_exception_ce, + "Couldn't send SCAN to node", 0 TSRMLS_CC); + efree(cmd); + RETURN_FALSE; + } + if(cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, TYPE_SCAN, + &it)==FAILURE || Z_TYPE_P(return_value)!=IS_ARRAY) + { + zend_throw_exception(redis_cluster_exception_ce, + "Couldn't process SCAN response from node", 0 TSRMLS_CC); + efree(cmd); + RETURN_FALSE; + } + + efree(cmd); + + num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value)); + } while(c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0); + + Z_LVAL_P(z_it) = it; } /* {{{ proto RedisCluster::sscan(string key, long it [string pat, long cnt]) */ From 7d3a84af326e232faca1b35d02887231863f83f9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 10 Jul 2014 17:00:49 -0700 Subject: [PATCH 0358/1986] Updated _masters to return in two ways Fixed a couple of compiler warnings --- redis_cluster.c | 36 ++++++++++++++++++++++++++++-------- redis_cluster.h | 1 + 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 39edb5a27d..296c37853f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -165,7 +165,6 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, punsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, eval, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, evalsha, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, scan, arginfo_scan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) @@ -1674,11 +1673,20 @@ PHP_METHOD(RedisCluster, _unserialize) { /* {{{ proto array RedisCluster::_masters() */ PHP_METHOD(RedisCluster, _masters) { redisCluster *c = GET_CONTEXT(); - zval *z_ret; + zend_bool as_arr=0; + zval *z_ret, *z_sub; redisClusterNode **node; - char buf[1024]; + char buf[1024], *host; + short port; size_t len; + // See if the user wants this as an array of host:port strings, or an array + // of [host, port] arrays. + if(!zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &as_arr)==FAILURE) + { + RETURN_FALSE; + } + MAKE_STD_ZVAL(z_ret); array_init(z_ret); @@ -1686,9 +1694,22 @@ PHP_METHOD(RedisCluster, _masters) { zend_hash_get_current_data(c->nodes, (void**)&node)==SUCCESS; zend_hash_move_forward(c->nodes)) { - len = snprintf(buf, sizeof(buf), "%s:%d", (*node)->sock->host, - (*node)->sock->port); - add_next_index_stringl(z_ret, buf, (int)len, 1); + host = (*node)->sock->host; + port = (*node)->sock->port; + + // What response type were we asked for + if(as_arr) { + MAKE_STD_ZVAL(z_sub); + array_init(z_sub); + + add_next_index_stringl(z_sub, host, strlen(host), 1); + add_next_index_long(z_sub, port); + + add_next_index_zval(z_ret, z_sub); + } else { + len = snprintf(buf, sizeof(buf), "%s:%d", host, port); + add_next_index_stringl(z_ret, buf, (int)len, 1); + } } *return_value = *z_ret; @@ -2034,11 +2055,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, PHP_METHOD(RedisCluster, scan) { redisCluster *c = GET_CONTEXT(); redisClusterNode **n; - char *cmd, *node, *pat=NULL, *key=NULL; + char *cmd, *node, *pat=NULL; int pat_len=0, node_len, cmd_len; short slot; zval *z_it; - HashTable *hash; long it, num_ele, count=0; /* Can't be in MULTI mode */ diff --git a/redis_cluster.h b/redis_cluster.h index 9c9fba459c..5d8c9c9ebb 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -222,6 +222,7 @@ PHP_METHOD(RedisCluster, unsubscribe); PHP_METHOD(RedisCluster, punsubscribe); PHP_METHOD(RedisCluster, eval); PHP_METHOD(RedisCluster, evalsha); +PHP_METHOD(RedisCluter, info); /* SCAN and friends */ PHP_METHOD(RedisCluster, scan); From c4b664439759518e810a956cd369f16183ca48ca Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 10 Jul 2014 17:27:42 -0700 Subject: [PATCH 0359/1986] Extracted INFO parsing from the response handler into it's own methos, so both Redis and RedisCluster can use the same one. --- library.c | 42 +++++++++++++++++++++++++----------------- library.h | 21 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/library.c b/library.c index 6d5e1c052d..958b1b8320 100644 --- a/library.c +++ b/library.c @@ -914,24 +914,37 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - char *pos, *cur; - char *key, *value, *p; int is_numeric; - zval *z_multi_result; + zval *z_ret; + /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { RETURN_FALSE; } - MAKE_STD_ZVAL(z_multi_result); - array_init(z_multi_result); /* pre-allocate array for multi's results. */ - /* response :: [response_line] - * response_line :: key ':' value CRLF - */ + /* Parse it into a zval array */ + z_ret = redis_parse_info_response(response); + + /* Free source response */ + efree(response); + + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_ret); + } else { + RETVAL_ZVAL(z_ret, 0, 1); + } +} + +PHPAPI zval *redis_parse_info_response(char *response) { + zval *z_ret; + char *key, *value, *p, *cur, *pos; + int is_numeric; + + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); cur = response; while(1) { - /* skip comments and empty lines */ if(*cur == '#' || *cur == '\r') { if(!(cur = strchr(cur, '\n'))) @@ -970,20 +983,15 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } if(is_numeric == 1) { - add_assoc_long(z_multi_result, key, atol(value)); + add_assoc_long(z_ret, key, atol(value)); efree(value); } else { - add_assoc_string(z_multi_result, key, value, 0); + add_assoc_string(z_ret, key, value, 0); } efree(key); } - efree(response); - IF_MULTI_OR_PIPELINE() { - add_next_index_zval(z_tab, z_multi_result); - } else { - RETVAL_ZVAL(z_multi_result, 0, 1); - } + return z_ret; } /* diff --git a/library.h b/library.h index 54d54cc034..97eb543e22 100644 --- a/library.h +++ b/library.h @@ -21,6 +21,27 @@ PHP_REDIS_API char* redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len TSRMLS_DC); typedef void (*SuccessCallback)(RedisSock *redis_sock); +PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); +PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI zval *redis_parse_info_response(char *resp); +PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); +PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); +PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); +PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); +PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); +PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); +PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only); +PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter); PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); From 59038f4232fe3de5a1380516be54300078a499c5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 10 Jul 2014 17:49:14 -0700 Subject: [PATCH 0360/1986] INFO command as well as a direct slot tweak This commit implements the INFO command for RedisCluster, which behaves like other direct to node commands and can take either a key or a host and port. Updated cluster_send_slot function such that on success it sets the proper reply slot. --- cluster_library.c | 30 ++++++++++++++++++++++++++++++ cluster_library.h | 4 ++++ redis_cluster.c | 22 ++++++++++++++++++++-- redis_cluster.h | 2 +- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 67e8fc75b7..e4354fc30b 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1221,6 +1221,9 @@ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, return -1; } + // Update our reply slot + c->reply_slot = slot; + return 0; } @@ -1794,6 +1797,33 @@ PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, return SUCCESS; } +/* INFO response */ +PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + zval *z_result; + char *info, *p; + + // Read our bulk response + if((info = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), + c->reply_len TSRMLS_CC))==NULL) + { + CLUSTER_RETURN_FALSE(c); + } + + /* Parse response, free memory */ + z_result = redis_parse_info_response(info); + efree(info); + + // Return our array + if(CLUSTER_IS_ATOMIC(c)) { + *return_value = *z_result; + efree(z_result); + } else { + add_next_index_zval(c->multi_resp, z_result); + } +} + /* MULTI BULK response loop where we might pull the next one */ PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb) diff --git a/cluster_library.h b/cluster_library.h index 1c916fac86..9d5d3e729f 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -414,6 +414,10 @@ PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, REDIS_SCAN_TYPE type, long *it); +/* INFO response handler */ +PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); + /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); diff --git a/redis_cluster.c b/redis_cluster.c index 296c37853f..00977abf2d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -169,7 +169,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) - + PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) @@ -191,6 +191,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, dbsize, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bgrewriteaof, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lastsave, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, info, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -2126,22 +2127,25 @@ PHP_METHOD(RedisCluster, scan) { Z_LVAL_P(z_it) = it; } +/* }}} */ /* {{{ proto RedisCluster::sscan(string key, long it [string pat, long cnt]) */ PHP_METHOD(RedisCluster, sscan) { cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN); } +/* }}} */ /* {{{ proto RedisCluster::zscan(string key, long it [string pat, long cnt]) */ PHP_METHOD(RedisCluster, zscan) { cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN); } +/* }}} */ /* {{{ proto RedisCluster::hscan(string key, long it [string pat, long cnt]) */ PHP_METHOD(RedisCluster, hscan) { cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN); } - +/* }}} */ /* {{{ proto RedisCluster::save(string key) * proto RedisCluster::save(string host, long port) */ @@ -2149,6 +2153,7 @@ PHP_METHOD(RedisCluster, save) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE", TYPE_LINE, cluster_bool_resp); } +/* }}} */ /* {{{ proto RedisCluster::bgsave(string key) * proto RedisCluster::bgsave(string host, long port) */ @@ -2156,6 +2161,7 @@ PHP_METHOD(RedisCluster, bgsave) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE", TYPE_LINE, cluster_bool_resp); } +/* }}} */ /* {{{ proto RedisCluster::flushdb(string key) * proto RedisCluster::flushdb(string host, long port) */ @@ -2163,6 +2169,7 @@ PHP_METHOD(RedisCluster, flushdb) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB", TYPE_LINE, cluster_bool_resp); } +/* }}} */ /* {{{ proto RedisCluster::flushall(string key) * proto RedisCluster::flushall(string host, long port) */ @@ -2170,6 +2177,7 @@ PHP_METHOD(RedisCluster, flushall) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL", TYPE_LINE, cluster_bool_resp); } +/* }}} */ /* {{{ proto RedisCluster::dbsize(string key) * proto RedisCluster::dbsize(string host, long port) */ @@ -2177,6 +2185,7 @@ PHP_METHOD(RedisCluster, dbsize) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DBSIZE", TYPE_LINE, cluster_bool_resp); } +/* }}} */ /* {{{ proto RedisCluster::bgrewriteaof(string key) * proto RedisCluster::bgrewriteaof(string host, long port) */ @@ -2184,6 +2193,7 @@ PHP_METHOD(RedisCluster, bgrewriteaof) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGREWRITEAOF", TYPE_LINE, cluster_bool_resp); } +/* }}} */ /* {{{ proto RedisCluster::lastsave(string key) * proto RedisCluster::lastsave(string host, long port) */ @@ -2191,6 +2201,14 @@ PHP_METHOD(RedisCluster, lastsave) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LASTSAVE", TYPE_INT, cluster_long_resp); } +/* }}} */ +/* {{{ proto RedisCluster::info(string key) + * proto RedisCluster::info(string host, long port) */ +PHP_METHOD(RedisCluster, info) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INFO", + TYPE_BULK, cluster_info_resp); +} +/* }}} */ /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 5d8c9c9ebb..e063510168 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -222,7 +222,7 @@ PHP_METHOD(RedisCluster, unsubscribe); PHP_METHOD(RedisCluster, punsubscribe); PHP_METHOD(RedisCluster, eval); PHP_METHOD(RedisCluster, evalsha); -PHP_METHOD(RedisCluter, info); +PHP_METHOD(RedisCluster, info); /* SCAN and friends */ PHP_METHOD(RedisCluster, scan); From 0c7a1ba6e873fad2fb413b1d8c19d00fed77744c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 10 Jul 2014 19:04:24 -0700 Subject: [PATCH 0361/1986] ZRANGEBYLEX Implemented ZRANGEBYLEX for both Redis and RedisCluster --- php_redis.h | 1 + redis.c | 7 +++++++ redis_cluster.c | 8 +++++++ redis_cluster.h | 2 ++ redis_commands.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 6 files changed, 75 insertions(+) diff --git a/php_redis.h b/php_redis.h index 0368b17a8a..96a0f39011 100644 --- a/php_redis.h +++ b/php_redis.h @@ -115,6 +115,7 @@ PHP_METHOD(Redis, zRevRange); PHP_METHOD(Redis, zRangeByScore); PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zRevRangeByScore); +PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zCount); PHP_METHOD(Redis, zDeleteRangeByScore); PHP_METHOD(Redis, zDeleteRangeByRank); diff --git a/redis.c b/redis.c index 5e38601ff6..a715741a37 100644 --- a/redis.c +++ b/redis.c @@ -2003,6 +2003,13 @@ PHP_METHOD(Redis, zRevRangeByScore) { } /* }}} */ +/* {{{ proto array Redis::zRangeByLex(string key, string min, string max, [ + * offset, limit]) */ +PHP_METHOD(Redis, zRangeByLex) { + REDIS_PROCESS_CMD(zrangebylex, redis_sock_read_multibulk_reply); +} +/* }}} */ + /* {{{ proto long Redis::zDelete(string key, string member) */ PHP_METHOD(Redis, zDelete) { diff --git a/redis_cluster.c b/redis_cluster.c index 00977abf2d..13d88471d0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -154,6 +154,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrangebyscore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrangebyscore, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrangebylex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zunionstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zinterstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) @@ -1409,6 +1411,12 @@ PHP_METHOD(RedisCluster, zrevrangebyscore) { } /* }}} */ +/* {{{ proto RedisCluster::zrangebylex(string key, string min, string max, [ + * LIMIT offset count) */ +PHP_METHOD(RedisCluster, zrangebylex) { + CLUSTER_PROCESS_CMD(zrangebylex, cluster_mbulk_resp); +} +/* }}} */ /* {{{ proto RedisCluster::sort(string key, array options) */ PHP_METHOD(RedisCluster, sort) { diff --git a/redis_cluster.h b/redis_cluster.h index e063510168..36228df0ef 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -212,6 +212,8 @@ PHP_METHOD(RedisCluster, srandmember); PHP_METHOD(RedisCluster, zrange); PHP_METHOD(RedisCluster, zrevrange); PHP_METHOD(RedisCluster, zrangebyscore); +PHP_METHOD(RedisCluster, zrevrangebyscore); +PHP_METHOD(RedisCluster, zrangebylex); PHP_METHOD(RedisCluster, zunionstore); PHP_METHOD(RedisCluster, zinterstore); PHP_METHOD(RedisCluster, sort); diff --git a/redis_commands.c b/redis_commands.c index 2b07a228be..48692f892d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2398,6 +2398,60 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 2, 0, cmd, cmd_len, slot); } +/* ZRANGEBYLEX */ +int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *min, *max; + int key_len, min_len, max_len, key_free; + long offset, count; + int argc = ZEND_NUM_ARGS(); + + /* We need either 3 or 5 arguments for this to be valid */ + if(argc != 3 && argc != 5) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Must pass either 3 or 5 arguments"); + return FAILURE; + } + + if(zend_parse_parameters(argc TSRMLS_CC, "sss|ll", &key, + &key_len, &min, &min_len, &max, &max_len, + &offset, &count)==FAILURE) + { + return FAILURE; + } + + /* min and max must start with '(' or '[' */ + if(min_len < 1 || max_len < 1 || (min[0] != '(' && min[0] != '[') || + (max[0] != '(' && max[0] != '[')) + { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "min and max arguments must start with '[' or '('"); + return FAILURE; + } + + /* Prefix key */ + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + /* Construct command */ + if(argc == 3) { + *cmd_len = redis_cmd_format_static(cmd, "ZRANGEBYLEX", "sss", key, + key_len, min, min_len, max, max_len); + } else { + *cmd_len = redis_cmd_format_static(cmd, "ZRANGEBYLEX", "ssssll", key, + key_len, min, min_len, max, max_len, "LIMIT", sizeof("LIMIT")-1, + offset, count); + } + + /* Pick our slot */ + CMD_SET_SLOT(slot,key,key_len); + + /* Free key if we prefixed */ + if(key_free) efree(key); + + return SUCCESS; +} + /* * Redis commands that don't deal with the server at all. The RedisSock* * pointer is the only thing retreived differently, so we just take that diff --git a/redis_commands.h b/redis_commands.h index 64a8a0c015..08763c233c 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -197,6 +197,9 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); From a4b160c4a14cd52e2611207c3a0c7515bc892261 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 11 Jul 2014 06:23:29 -0700 Subject: [PATCH 0362/1986] ZLEXCOUNT Implemented ZLEXCOUNT for both Redis and RedisCluster. Removed unused variable in INFO response processor --- cluster_library.c | 2 +- php_redis.h | 1 + redis.c | 7 +++++++ redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + redis_commands.c | 39 +++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 7 files changed, 59 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index e4354fc30b..81d807b062 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1802,7 +1802,7 @@ PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval *z_result; - char *info, *p; + char *info; // Read our bulk response if((info = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), diff --git a/php_redis.h b/php_redis.h index 96a0f39011..8b31af5d1b 100644 --- a/php_redis.h +++ b/php_redis.h @@ -116,6 +116,7 @@ PHP_METHOD(Redis, zRangeByScore); PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zRevRangeByScore); PHP_METHOD(Redis, zRangeByLex); +PHP_METHOD(Redis, zLexCount); PHP_METHOD(Redis, zCount); PHP_METHOD(Redis, zDeleteRangeByScore); PHP_METHOD(Redis, zDeleteRangeByRank); diff --git a/redis.c b/redis.c index a715741a37..55c663837c 100644 --- a/redis.c +++ b/redis.c @@ -197,6 +197,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByLex, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zLexCount, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zCount, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC) @@ -2010,6 +2011,12 @@ PHP_METHOD(Redis, zRangeByLex) { } /* }}} */ +/* {{{ proto long Redis::zLexCount(string key, string min, string max) */ +PHP_METHOD(Redis, zLexCount) { + REDIS_PROCESS_CMD(zlexcount, redis_long_response); +} +/* }}} */ + /* {{{ proto long Redis::zDelete(string key, string member) */ PHP_METHOD(Redis, zDelete) { diff --git a/redis_cluster.c b/redis_cluster.c index 13d88471d0..611559fe8f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -156,6 +156,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrangebylex, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zlexcount, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zunionstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zinterstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) @@ -1418,6 +1419,12 @@ PHP_METHOD(RedisCluster, zrangebylex) { } /* }}} */ +/* {{{ proto RedisCluster::zlexcount(string key, string min, string max) */ +PHP_METHOD(RedisCluster, zlexcount) { + CLUSTER_PROCESS_CMD(zlexcount, cluster_long_resp); +} +/* }}} */ + /* {{{ proto RedisCluster::sort(string key, array options) */ PHP_METHOD(RedisCluster, sort) { redisCluster *c = GET_CONTEXT(); diff --git a/redis_cluster.h b/redis_cluster.h index 36228df0ef..593e6877c4 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -214,6 +214,7 @@ PHP_METHOD(RedisCluster, zrevrange); PHP_METHOD(RedisCluster, zrangebyscore); PHP_METHOD(RedisCluster, zrevrangebyscore); PHP_METHOD(RedisCluster, zrangebylex); +PHP_METHOD(RedisCluster, zlexcount); PHP_METHOD(RedisCluster, zunionstore); PHP_METHOD(RedisCluster, zinterstore); PHP_METHOD(RedisCluster, sort); diff --git a/redis_commands.c b/redis_commands.c index 48692f892d..3c4687a7ee 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2452,6 +2452,45 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* ZLEXCOUNT */ +int redis_zlexcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *min, *max; + int key_len, min_len, max_len, key_free; + + /* Parse args */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, + &min, &min_len, &max, &max_len)==FAILURE) + { + return FAILURE; + } + + /* Quick sanity check on min/max */ + if(min_len<1 || max_len<1 || (min[0]!='(' && min[0]!='[') || + (max[0]!='(' && max[0]!='[')) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Min and Max arguments must begin with '(' or '['"); + return FAILURE; + } + + /* Prefix key if we need to */ + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + /* Construct command */ + *cmd_len = redis_cmd_format_static(cmd, "ZLEXCOUNT", "sss", key, key_len, + min, min_len, max, max_len); + + /* set slot */ + CMD_SET_SLOT(slot,key,key_len); + + /* Free key if prefixed */ + if(key_free) efree(key); + + return SUCCESS; +} + /* * Redis commands that don't deal with the server at all. The RedisSock* * pointer is the only thing retreived differently, so we just take that diff --git a/redis_commands.h b/redis_commands.h index 08763c233c..3451a63c4f 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -200,6 +200,9 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_zlexcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); From 23572bb2e2d944d70980a549335fda2655a6f0ab Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 11 Jul 2014 07:19:03 -0700 Subject: [PATCH 0363/1986] LINDEX Implemented LINDEX command in RedisCluster --- redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + 2 files changed, 8 insertions(+) diff --git a/redis_cluster.c b/redis_cluster.c index 611559fe8f..104d4dd3f3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -80,6 +80,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, linsert, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lindex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lrem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, brpoplpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpoplpush, NULL, ZEND_ACC_PUBLIC) @@ -902,6 +903,12 @@ PHP_METHOD(RedisCluster, linsert) { } /* }}} */ +/* {{{ proto string RedisCluster::lindex(string key, long index) */ +PHP_METHOD(RedisCluster, lindex) { + CLUSTER_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, cluster_bulk_resp); +} +/* }}} */ + /* {{{ proto long RedisCluster::lrem(string key, long count, string val) */ PHP_METHOD(RedisCluster, lrem) { CLUSTER_PROCESS_CMD(lrem, cluster_long_resp); diff --git a/redis_cluster.h b/redis_cluster.h index 593e6877c4..86ac7f2471 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -138,6 +138,7 @@ PHP_METHOD(RedisCluster, brpop); PHP_METHOD(RedisCluster, rpushx); PHP_METHOD(RedisCluster, lpushx); PHP_METHOD(RedisCluster, linsert); +PHP_METHOD(RedisCluster, lindex); PHP_METHOD(RedisCluster, lrem); PHP_METHOD(RedisCluster, brpoplpush); PHP_METHOD(RedisCluster, rpoplpush); From a85e04b2fec1697111876256c1461dcbccc1ee4b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 11 Jul 2014 08:37:23 -0700 Subject: [PATCH 0364/1986] TIME/ROLE Implemented ROLE and TIME commands in RedisCluster, as well as updated the TIME command for Redis proper such that we use the new calling convention. Updated redis_read_variant_reply to take a context void* so it conforms with the correct prototype. --- cluster_library.c | 13 ++++++++++--- library.c | 2 +- php_redis.h | 1 + redis.c | 49 +++++++++++++---------------------------------- redis_cluster.c | 22 +++++++++++++++++++-- redis_cluster.h | 4 +++- 6 files changed, 48 insertions(+), 43 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 81d807b062..b7213f76dc 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1618,6 +1618,7 @@ PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) { zval *z_sub_ele; + int i; switch(r->type) { case TYPE_INT: @@ -1632,7 +1633,9 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) case TYPE_MULTIBULK: MAKE_STD_ZVAL(z_sub_ele); array_init(z_sub_ele); - cluster_mbulk_variant_resp(r, z_sub_ele); + for(i=0;ielements;i++) { + cluster_mbulk_variant_resp(r->element[i], z_sub_ele); + } add_next_index_zval(z_ret, z_sub_ele); break; default: @@ -1648,6 +1651,7 @@ PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, { clusterReply *r; zval *z_arr; + int i; // Make sure we can read it if((r = cluster_read_resp(c TSRMLS_CC))==NULL) { @@ -1669,8 +1673,11 @@ PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, case TYPE_MULTIBULK: MAKE_STD_ZVAL(z_arr); array_init(z_arr); - cluster_mbulk_variant_resp(r, z_arr); - + + for(i=0;ielements;i++) { + cluster_mbulk_variant_resp(r->element[i], z_arr); + } + *return_value = *z_arr; efree(z_arr); break; diff --git a/library.c b/library.c index 958b1b8320..9706fbe3ea 100644 --- a/library.c +++ b/library.c @@ -2339,7 +2339,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab) + zval *z_tab, void *ctx) { // Reply type, and reply size vars REDIS_REPLY_TYPE reply_type; diff --git a/php_redis.h b/php_redis.h index 8b31af5d1b..d5ae3de961 100644 --- a/php_redis.h +++ b/php_redis.h @@ -145,6 +145,7 @@ PHP_METHOD(Redis, restore); PHP_METHOD(Redis, migrate); PHP_METHOD(Redis, time); +PHP_METHOD(Redis, role); PHP_METHOD(Redis, getLastError); PHP_METHOD(Redis, clearLastError); diff --git a/redis.c b/redis.c index 55c663837c..3680089625 100644 --- a/redis.c +++ b/redis.c @@ -241,7 +241,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, punsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, time, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(Redis, role, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, eval, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, evalsha, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, script, NULL, ZEND_ACC_PUBLIC) @@ -2901,7 +2901,7 @@ PHP_METHOD(Redis, slowlog) { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL) < 0) + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -3108,7 +3108,7 @@ PHP_METHOD(Redis, pubsub) { } else { IF_ATOMIC() { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL)<0) + redis_sock, NULL, NULL)<0) { RETURN_FALSE; } @@ -3236,7 +3236,7 @@ PHP_METHOD(Redis, evalsha) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL) < 0) + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -3274,7 +3274,7 @@ PHP_METHOD(Redis, eval) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL) < 0) + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -3374,7 +3374,7 @@ PHP_METHOD(Redis, script) { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL) < 0) + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -3539,36 +3539,13 @@ PHP_METHOD(Redis, clearLastError) { /* {{{ proto Redis::time() */ PHP_METHOD(Redis, time) { - zval *object; - RedisSock *redis_sock; - char *cmd; - int cmd_len; - - // Grab our object - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) - { - RETURN_FALSE; - } - - // Grab socket - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - /* Build TIME command */ - cmd_len = redis_cmd_format_static(&cmd, "TIME", ""); + REDIS_PROCESS_KW_CMD("TIME", redis_empty_cmd, + redis_sock_read_multibulk_reply_raw); +} - // Execute or queue command - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if(redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) - { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw); +/* {{{ proto array Redis::role() */ +PHP_METHOD(Redis, role) { + REDIS_PROCESS_KW_CMD("ROLE", redis_empty_cmd, redis_read_variant_reply); } /* @@ -3720,7 +3697,7 @@ PHP_METHOD(Redis, client) { } else { IF_ATOMIC() { redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock,NULL); + redis_sock,NULL,NULL); } REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } diff --git a/redis_cluster.c b/redis_cluster.c index 104d4dd3f3..adaf9c55db 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -196,6 +196,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, bgrewriteaof, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lastsave, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, info, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, role, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, time, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} }; @@ -2225,12 +2228,27 @@ PHP_METHOD(RedisCluster, lastsave) { } /* }}} */ -/* {{{ proto RedisCluster::info(string key) - * proto RedisCluster::info(string host, long port) */ +/* {{{ proto array RedisCluster::info(string key) + * proto array RedisCluster::info(string host, long port) */ PHP_METHOD(RedisCluster, info) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INFO", TYPE_BULK, cluster_info_resp); } /* }}} */ +/* {{{ proto array RedisCluster::role(string key) + * proto array RedisCluster::role(string key, long port) */ +PHP_METHOD(RedisCluster, role) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ROLE", + TYPE_MULTIBULK, cluster_variant_resp); +} + +/* {{{ proto array RedisCluster::time(string key) + * proto array RedisCluster::time(string key, long port */ +PHP_METHOD(RedisCluster, time) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "TIME", + TYPE_MULTIBULK, cluster_variant_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 86ac7f2471..6468e6e742 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -241,7 +241,7 @@ PHP_METHOD(RedisCluster, discard); PHP_METHOD(RedisCluster, watch); PHP_METHOD(RedisCluster, unwatch); -/* DB saving, etc */ +/* DB saving, server info, etc */ PHP_METHOD(RedisCluster, save); PHP_METHOD(RedisCluster, bgsave); PHP_METHOD(RedisCluster, flushdb); @@ -249,6 +249,8 @@ PHP_METHOD(RedisCluster, flushall); PHP_METHOD(RedisCluster, dbsize); PHP_METHOD(RedisCluster, bgrewriteaof); PHP_METHOD(RedisCluster, lastsave); +PHP_METHOD(RedisCluster, role); +PHP_METHOD(RedisCluster, time); /* Introspection */ PHP_METHOD(RedisCluster, getoption); From 56419cdaa6482a66248f5573168b56b77b544034 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 11 Jul 2014 08:43:08 -0700 Subject: [PATCH 0365/1986] RANDOMKEY Implemented RANDOMKEY command for Redis Cluster --- redis_cluster.c | 13 +++++++++++-- redis_cluster.h | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index adaf9c55db..816f0c3c26 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -198,6 +198,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, info, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, role, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, time, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -2237,18 +2238,26 @@ PHP_METHOD(RedisCluster, info) { /* }}} */ /* {{{ proto array RedisCluster::role(string key) - * proto array RedisCluster::role(string key, long port) */ + * proto array RedisCluster::role(string host, long port) */ PHP_METHOD(RedisCluster, role) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ROLE", TYPE_MULTIBULK, cluster_variant_resp); } /* {{{ proto array RedisCluster::time(string key) - * proto array RedisCluster::time(string key, long port */ + * proto array RedisCluster::time(string host, long port */ PHP_METHOD(RedisCluster, time) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "TIME", TYPE_MULTIBULK, cluster_variant_resp); } /* }}} */ +/* {{{ proto string RedisCluster::randomkey(string key) + * proto string RedisCluster::randomkey(string host, long port) */ +PHP_METHOD(RedisCluster, randomkey) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RANDOMKEY", + TYPE_BULK, cluster_bulk_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 6468e6e742..2097f0b7a9 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -241,7 +241,7 @@ PHP_METHOD(RedisCluster, discard); PHP_METHOD(RedisCluster, watch); PHP_METHOD(RedisCluster, unwatch); -/* DB saving, server info, etc */ +/* Commands we direct to a node, with no args */ PHP_METHOD(RedisCluster, save); PHP_METHOD(RedisCluster, bgsave); PHP_METHOD(RedisCluster, flushdb); @@ -251,6 +251,7 @@ PHP_METHOD(RedisCluster, bgrewriteaof); PHP_METHOD(RedisCluster, lastsave); PHP_METHOD(RedisCluster, role); PHP_METHOD(RedisCluster, time); +PHP_METHOD(RedisCluster, randomkey); /* Introspection */ PHP_METHOD(RedisCluster, getoption); From 3714ebb23db2fc7c978cac5154d820b3523f2525 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 11 Jul 2014 08:52:44 -0700 Subject: [PATCH 0366/1986] PING response Implemented PING for RedisCluster --- cluster_library.c | 13 +++++++++++++ cluster_library.h | 2 ++ redis_cluster.c | 9 +++++++++ redis_cluster.h | 1 + 4 files changed, 25 insertions(+) diff --git a/cluster_library.c b/cluster_library.c index b7213f76dc..833e33ec96 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1393,6 +1393,19 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, CLUSTER_RETURN_BOOL(c, 1); } +/* Boolean response, specialized for PING */ +PHPAPI void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + if(c->reply_type != TYPE_LINE || c->reply_len != 4 || + memcmp(c->line_reply,"PONG",sizeof("PONG")-1)) + { + CLUSTER_RETURN_FALSE(c); + } + + CLUSTER_RETURN_BOOL(c, 1); +} + /* 1 or 0 response, for things like SETNX */ PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) diff --git a/cluster_library.h b/cluster_library.h index 9d5d3e729f..a4b44522c8 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -361,6 +361,8 @@ PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHPAPI void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, diff --git a/redis_cluster.c b/redis_cluster.c index 816f0c3c26..9f6941cb8a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -199,6 +199,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, role, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, time, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -2260,4 +2261,12 @@ PHP_METHOD(RedisCluster, randomkey) { } /* }}} */ +/* {{{ proto bool RedisCluster::ping(string key) + * proto bool RedisCluster::ping(string host, long port) */ +PHP_METHOD(RedisCluster, ping) { + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PING", + TYPE_LINE, cluster_ping_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 2097f0b7a9..3ed7b305d9 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -252,6 +252,7 @@ PHP_METHOD(RedisCluster, lastsave); PHP_METHOD(RedisCluster, role); PHP_METHOD(RedisCluster, time); PHP_METHOD(RedisCluster, randomkey); +PHP_METHOD(RedisCluster, ping); /* Introspection */ PHP_METHOD(RedisCluster, getoption); From 77bcc2b2c436318d3e43ba6c86e51cbd4a32cffe Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 11 Jul 2014 09:25:37 -0700 Subject: [PATCH 0367/1986] ZREVRANGEBYLEX Implemented ZREVRANGEBYLEX for Redis and RedisCluster, and made command construction a generic that can handle either. --- php_redis.h | 1 + redis.c | 10 +++- redis_cluster.c | 16 ++++-- redis_cluster.h | 1 + redis_commands.c | 130 +++++++++++++++++++++++------------------------ redis_commands.h | 6 +-- 6 files changed, 92 insertions(+), 72 deletions(-) diff --git a/php_redis.h b/php_redis.h index d5ae3de961..804740d6bf 100644 --- a/php_redis.h +++ b/php_redis.h @@ -116,6 +116,7 @@ PHP_METHOD(Redis, zRangeByScore); PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zRevRangeByScore); PHP_METHOD(Redis, zRangeByLex); +PHP_METHOD(Redis, zRevRangeByLex); PHP_METHOD(Redis, zLexCount); PHP_METHOD(Redis, zCount); PHP_METHOD(Redis, zDeleteRangeByScore); diff --git a/redis.c b/redis.c index 3680089625..1562076ad5 100644 --- a/redis.c +++ b/redis.c @@ -197,6 +197,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByLex, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRangeByLex, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zLexCount, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zCount, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC) @@ -2007,7 +2008,14 @@ PHP_METHOD(Redis, zRevRangeByScore) { /* {{{ proto array Redis::zRangeByLex(string key, string min, string max, [ * offset, limit]) */ PHP_METHOD(Redis, zRangeByLex) { - REDIS_PROCESS_CMD(zrangebylex, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, + redis_sock_read_multibulk_reply); +} +/* }}} */ + +PHP_METHOD(Redis, zRevRangeByLex) { + REDIS_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd, + redis_sock_read_multibulk_reply); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 9f6941cb8a..a0b37e9548 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -157,6 +157,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrangebylex, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrangebylex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zlexcount, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zunionstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zinterstore, NULL, ZEND_ACC_PUBLIC) @@ -1424,10 +1425,19 @@ PHP_METHOD(RedisCluster, zrevrangebyscore) { } /* }}} */ -/* {{{ proto RedisCluster::zrangebylex(string key, string min, string max, [ - * LIMIT offset count) */ +/* {{{ proto array RedisCluster::zrangebylex(string key, string min, string max, + * [offset, count]) */ PHP_METHOD(RedisCluster, zrangebylex) { - CLUSTER_PROCESS_CMD(zrangebylex, cluster_mbulk_resp); + CLUSTER_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, + cluster_mbulk_resp); +} +/* }}} */ + +/* {{{ proto array RedisCluster::zrevrangebylex(string key, string min, + * string min, [long off, long limit) */ +PHP_METHOD(RedisCluster, zrevrangebylex) { + CLUSTER_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd, + cluster_mbulk_resp); } /* }}} */ diff --git a/redis_cluster.h b/redis_cluster.h index 3ed7b305d9..3d7363c56e 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -215,6 +215,7 @@ PHP_METHOD(RedisCluster, zrevrange); PHP_METHOD(RedisCluster, zrangebyscore); PHP_METHOD(RedisCluster, zrevrangebyscore); PHP_METHOD(RedisCluster, zrangebylex); +PHP_METHOD(RedisCluster, zrevrangebylex); PHP_METHOD(RedisCluster, zlexcount); PHP_METHOD(RedisCluster, zunionstore); PHP_METHOD(RedisCluster, zinterstore); diff --git a/redis_commands.c b/redis_commands.c index 3c4687a7ee..c34409af23 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -374,7 +374,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Generic to construct SCAN and variant commands */ -int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, +int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count) { static char *kw[] = {"SCAN","SSCAN","HSCAN","ZSCAN"}; @@ -385,7 +385,7 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, argc = 1 + (type!=TYPE_SCAN) + (pat_len>0?2:0) + (count>0?2:0); redis_cmd_init_sstr(&cmdstr, argc, kw[type], strlen(kw[type])); - + // Append our key if it's not a regular SCAN command if(type != TYPE_SCAN) { redis_cmd_append_sstr(&cmdstr, key, key_len); @@ -706,7 +706,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_len, key_free; char *key; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &z_arr, + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &z_arr, &(sctx->cb), &(sctx->cb_cache))==FAILURE) { efree(sctx); @@ -715,7 +715,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ht_chan = Z_ARRVAL_P(z_arr); sctx->kw = kw; - sctx->argc = zend_hash_num_elements(ht_chan); + sctx->argc = zend_hash_num_elements(ht_chan); if(sctx->argc==0) { efree(sctx); @@ -742,7 +742,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, key, key_len); // Free our key if it was prefixed - if(key_free) efree(key); + if(key_free) efree(key); } // Push values out @@ -773,7 +773,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ht_arr = Z_ARRVAL_P(z_arr); - + sctx->argc = zend_hash_num_elements(ht_arr); if(sctx->argc == 0) { efree(sctx); @@ -781,7 +781,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); - + for(zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); zend_hash_get_current_data_ex(ht_arr, (void**)&z_chan, &ptr)==SUCCESS; zend_hash_move_forward_ex(ht_arr, &ptr)) @@ -799,7 +799,61 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd = cmdstr.c; *ctx = (void*)sctx; - return SUCCESS; + return SUCCESS; +} + +/* ZRANGEBYLEX/ZREVRANGEBYLEX */ +int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + char *key, *min, *max; + int key_len, min_len, max_len, key_free; + long offset, count; + int argc = ZEND_NUM_ARGS(); + + /* We need either 3 or 5 arguments for this to be valid */ + if(argc != 3 && argc != 5) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Must pass either 3 or 5 arguments"); + return FAILURE; + } + + if(zend_parse_parameters(argc TSRMLS_CC, "sss|ll", &key, + &key_len, &min, &min_len, &max, &max_len, + &offset, &count)==FAILURE) + { + return FAILURE; + } + + /* min and max must start with '(' or '[' */ + if(min_len < 1 || max_len < 1 || (min[0] != '(' && min[0] != '[') || + (max[0] != '(' && max[0] != '[')) + { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "min and max arguments must start with '[' or '('"); + return FAILURE; + } + + /* Prefix key */ + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + /* Construct command */ + if(argc == 3) { + *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, + min_len, max, max_len); + } else { + *cmd_len = redis_cmd_format_static(cmd, kw, "ssssll", key, key_len, min, + min_len, max, max_len, "LIMIT", sizeof("LIMIT")-1, offset, count); + } + + /* Pick our slot */ + CMD_SET_SLOT(slot,key,key_len); + + /* Free key if we prefixed */ + if(key_free) efree(key); + + return SUCCESS; } /* Commands that take a key followed by a variable list of serializable @@ -2398,60 +2452,6 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 2, 0, cmd, cmd_len, slot); } -/* ZRANGEBYLEX */ -int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key, *min, *max; - int key_len, min_len, max_len, key_free; - long offset, count; - int argc = ZEND_NUM_ARGS(); - - /* We need either 3 or 5 arguments for this to be valid */ - if(argc != 3 && argc != 5) { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Must pass either 3 or 5 arguments"); - return FAILURE; - } - - if(zend_parse_parameters(argc TSRMLS_CC, "sss|ll", &key, - &key_len, &min, &min_len, &max, &max_len, - &offset, &count)==FAILURE) - { - return FAILURE; - } - - /* min and max must start with '(' or '[' */ - if(min_len < 1 || max_len < 1 || (min[0] != '(' && min[0] != '[') || - (max[0] != '(' && max[0] != '[')) - { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "min and max arguments must start with '[' or '('"); - return FAILURE; - } - - /* Prefix key */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* Construct command */ - if(argc == 3) { - *cmd_len = redis_cmd_format_static(cmd, "ZRANGEBYLEX", "sss", key, - key_len, min, min_len, max, max_len); - } else { - *cmd_len = redis_cmd_format_static(cmd, "ZRANGEBYLEX", "ssssll", key, - key_len, min, min_len, max, max_len, "LIMIT", sizeof("LIMIT")-1, - offset, count); - } - - /* Pick our slot */ - CMD_SET_SLOT(slot,key,key_len); - - /* Free key if we prefixed */ - if(key_free) efree(key); - - return SUCCESS; -} - /* ZLEXCOUNT */ int redis_zlexcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -2610,7 +2610,7 @@ void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { } } -void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, +void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zval *z_val; @@ -2627,7 +2627,7 @@ void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, if(val_free) STR_FREE(val); } -void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, +void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_class_entry *ex) { char *value; @@ -2647,7 +2647,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, TSRMLS_CC) == 0) { // Badly formed input, throw an execption - zend_throw_exception(ex, + zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0 TSRMLS_CC); RETURN_FALSE; diff --git a/redis_commands.h b/redis_commands.h index 3451a63c4f..af39eac317 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -89,6 +89,9 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them @@ -197,9 +200,6 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_zlexcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From 265837230d5fa52a07d1bfcbfa40affc521460ef Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 11 Jul 2014 09:47:30 -0700 Subject: [PATCH 0368/1986] ZREMRANGEBYLEX Implemented ZREMRANGEBYLEX in Redis and RedisCluster, and made the zlexcount command generic, as it has the same semantics. --- php_redis.h | 1 + redis.c | 10 +++++- redis_cluster.c | 12 ++++++-- redis_cluster.h | 1 + redis_commands.c | 79 ++++++++++++++++++++++++------------------------ redis_commands.h | 6 ++-- 6 files changed, 64 insertions(+), 45 deletions(-) diff --git a/php_redis.h b/php_redis.h index 804740d6bf..6077c8b1ac 100644 --- a/php_redis.h +++ b/php_redis.h @@ -117,6 +117,7 @@ PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zRevRangeByScore); PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zRevRangeByLex); +PHP_METHOD(Redis, zRemRangeByLex); PHP_METHOD(Redis, zLexCount); PHP_METHOD(Redis, zCount); PHP_METHOD(Redis, zDeleteRangeByScore); diff --git a/redis.c b/redis.c index 1562076ad5..40b8dca706 100644 --- a/redis.c +++ b/redis.c @@ -199,6 +199,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zRangeByLex, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRangeByLex, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zLexCount, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRemRangeByLex, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zCount, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC) @@ -2021,7 +2022,14 @@ PHP_METHOD(Redis, zRevRangeByLex) { /* {{{ proto long Redis::zLexCount(string key, string min, string max) */ PHP_METHOD(Redis, zLexCount) { - REDIS_PROCESS_CMD(zlexcount, redis_long_response); + REDIS_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, redis_long_response); +} +/* }}} */ + +/* {{{ proto long Redis::zRemRangeByLex(string key, string min, string max) */ +PHP_METHOD(Redis, zRemRangeByLex) { + REDIS_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, + redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index a0b37e9548..cdf5f339b8 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -159,6 +159,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zrangebylex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrangebylex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zlexcount, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zremrangebylex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zunionstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zinterstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) @@ -1441,9 +1442,16 @@ PHP_METHOD(RedisCluster, zrevrangebylex) { } /* }}} */ -/* {{{ proto RedisCluster::zlexcount(string key, string min, string max) */ +/* {{{ proto long RedisCluster::zlexcount(string key, string min, string max) */ PHP_METHOD(RedisCluster, zlexcount) { - CLUSTER_PROCESS_CMD(zlexcount, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, cluster_long_resp); +} +/* }}} */ + +/* {{{ proto long RedisCluster::zremrangebylex(string key, string min, string max) */ +PHP_METHOD(RedisCluster, zremrangebylex) { + CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, + cluster_long_resp); } /* }}} */ diff --git a/redis_cluster.h b/redis_cluster.h index 3d7363c56e..66d88d6307 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -217,6 +217,7 @@ PHP_METHOD(RedisCluster, zrevrangebyscore); PHP_METHOD(RedisCluster, zrangebylex); PHP_METHOD(RedisCluster, zrevrangebylex); PHP_METHOD(RedisCluster, zlexcount); +PHP_METHOD(RedisCluster, zremrangebylex); PHP_METHOD(RedisCluster, zunionstore); PHP_METHOD(RedisCluster, zinterstore); PHP_METHOD(RedisCluster, sort); diff --git a/redis_commands.c b/redis_commands.c index c34409af23..39ed31b056 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -856,6 +856,46 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* ZLEXCOUNT/ZREMRANGEBYLEX */ +int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + char *key, *min, *max; + int key_len, min_len, max_len, key_free; + + /* Parse args */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, + &min, &min_len, &max, &max_len)==FAILURE) + { + return FAILURE; + } + + /* Quick sanity check on min/max */ + if(min_len<1 || max_len<1 || (min[0]!='(' && min[0]!='[') || + (max[0]!='(' && max[0]!='[')) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Min and Max arguments must begin with '(' or '['"); + return FAILURE; + } + + /* Prefix key if we need to */ + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + /* Construct command */ + *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, + min_len, max, max_len); + + /* set slot */ + CMD_SET_SLOT(slot,key,key_len); + + /* Free key if prefixed */ + if(key_free) efree(key); + + return SUCCESS; +} + /* Commands that take a key followed by a variable list of serializable * values (RPUSH, LPUSH, SADD, SREM, etc...) */ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, @@ -2452,45 +2492,6 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 2, 0, cmd, cmd_len, slot); } -/* ZLEXCOUNT */ -int redis_zlexcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key, *min, *max; - int key_len, min_len, max_len, key_free; - - /* Parse args */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, - &min, &min_len, &max, &max_len)==FAILURE) - { - return FAILURE; - } - - /* Quick sanity check on min/max */ - if(min_len<1 || max_len<1 || (min[0]!='(' && min[0]!='[') || - (max[0]!='(' && max[0]!='[')) - { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Min and Max arguments must begin with '(' or '['"); - return FAILURE; - } - - /* Prefix key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* Construct command */ - *cmd_len = redis_cmd_format_static(cmd, "ZLEXCOUNT", "sss", key, key_len, - min, min_len, max, max_len); - - /* set slot */ - CMD_SET_SLOT(slot,key,key_len); - - /* Free key if prefixed */ - if(key_free) efree(key); - - return SUCCESS; -} - /* * Redis commands that don't deal with the server at all. The RedisSock* * pointer is the only thing retreived differently, so we just take that diff --git a/redis_commands.h b/redis_commands.h index af39eac317..f8f1e879ee 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -92,6 +92,9 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them @@ -200,9 +203,6 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_zlexcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); From 65947466164f08460055d71484f9916542bbe9da Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 11 Jul 2014 11:09:54 -0700 Subject: [PATCH 0369/1986] ECHO command Implemented the ECHO command for cluster, which like other commands where you send to a specific node, can be done either by sending "at" a key, or by sending to a host/port --- redis.c | 1 - redis_cluster.c | 81 ++++++++++++++++++++++++++++++++++++++++--------- redis_cluster.h | 3 +- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/redis.c b/redis.c index 40b8dca706..07fa750fe7 100644 --- a/redis.c +++ b/redis.c @@ -870,7 +870,6 @@ PHP_METHOD(Redis, randomKey) PHP_METHOD(Redis, echo) { REDIS_PROCESS_KW_CMD("ECHO", redis_str_cmd, redis_string_response); - } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index cdf5f339b8..22458893ac 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -202,6 +202,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, time, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -1960,6 +1961,29 @@ PHP_METHOD(RedisCluster, discard) { RETURN_TRUE; } +/* Get a slot either by key or host/port */ +static short +cluster_cmd_get_slot(redisCluster *c, int by_key, char *arg1, int arg1_len, + long port) +{ + short slot; + int key_free; + + if(by_key) { + key_free = redis_key_prefix(c->flags, &arg1, &arg1_len); + slot = cluster_hash_key(arg1, arg1_len); + if(key_free) efree(arg1); + } else { + slot = cluster_find_slot(c,(const char *)arg1, (unsigned short)port); + if(slot<0) { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", + arg1, port); + } + } + + return slot; +} + /* Generic handler for things we want directed at a given node, like SAVE, * BGSAVE, FLUSHDB, FLUSHALL, etc */ static void @@ -1968,7 +1992,7 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, { redisCluster *c = GET_CONTEXT(); char *cmd, *arg1; - int arg1_len, cmd_len, arg1_free; + int arg1_len, cmd_len; short slot; long arg2; @@ -1980,19 +2004,9 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, // One argument means find the node (treated like a key), and two means // send the command to a specific host and port - if(ZEND_NUM_ARGS() == 1) { - // Treat our argument like a key, and look for the slot that way - arg1_free = redis_key_prefix(c->flags, &arg1, &arg1_len); - slot = cluster_hash_key(arg1, arg1_len); - if(arg1_free) efree(arg1); - } else { - // Find the slot by IP/port - slot = cluster_find_slot(c, (const char *)arg1, (unsigned short)arg2); - if(slot<0) { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", - arg1, arg2); - RETURN_FALSE; - } + slot = cluster_cmd_get_slot(c, ZEND_NUM_ARGS()==1, arg1, arg1_len, arg2); + if(slot<0) { + RETURN_FALSE; } // Construct our command @@ -2287,4 +2301,43 @@ PHP_METHOD(RedisCluster, ping) { } /* }}} */ +/* {{{ proto string RedisCluster::echo(string msg, string key) + * proto string RedisCluster::echo(string msg, string host, long port) */ +PHP_METHOD(RedisCluster, echo) { + redisCluster *c = GET_CONTEXT(); + char *cmd, *arg2, *msg; + int cmd_len, arg2_len, msg_len; + short slot; + long arg3; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &msg, &msg_len, + &arg2, &arg2_len, &arg3)==FAILURE) + { + RETURN_FALSE; + } + + /* Grab slot either by key or host/port */ + slot = cluster_cmd_get_slot(c, ZEND_NUM_ARGS()==2, arg2, arg2_len, arg3); + if(slot<0) { + RETURN_FALSE; + } + + /* Construct our command */ + cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s", msg, msg_len); + + /* Send it off */ + if(cluster_send_slot(c,slot,cmd,cmd_len,TYPE_BULK TSRMLS_CC)<0) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to send commnad at the specificed node", 0 TSRMLS_CC); + efree(cmd); + RETURN_FALSE; + } + + /* Process bulk response */ + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + + efree(cmd); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index 66d88d6307..c79928d601 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -243,7 +243,7 @@ PHP_METHOD(RedisCluster, discard); PHP_METHOD(RedisCluster, watch); PHP_METHOD(RedisCluster, unwatch); -/* Commands we direct to a node, with no args */ +/* Commands we direct to a node */ PHP_METHOD(RedisCluster, save); PHP_METHOD(RedisCluster, bgsave); PHP_METHOD(RedisCluster, flushdb); @@ -255,6 +255,7 @@ PHP_METHOD(RedisCluster, role); PHP_METHOD(RedisCluster, time); PHP_METHOD(RedisCluster, randomkey); PHP_METHOD(RedisCluster, ping); +PHP_METHOD(RedisCluster, echo); /* Introspection */ PHP_METHOD(RedisCluster, getoption); From 03082de7b4b3c6bcd5a0bfecf958b912ef19999c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 12 Jul 2014 09:37:09 -0700 Subject: [PATCH 0370/1986] COMMAND command Implement the new COMMAND command in Redis for both cluster and non cluster classes. This command is really more of a debug tool but should actually be useful for updating the unit tests as we can now simply detect which commands do and don't exist, etc. --- cluster_library.c | 1 - php_redis.h | 1 + redis.c | 9 +++++++ redis_cluster.c | 9 +++++++ redis_cluster.h | 1 + redis_commands.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 7 files changed, 88 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 833e33ec96..1619af2b02 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -132,7 +132,6 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, *err = 1; return; } - r->str = estrndup(buf,r->len); break; case TYPE_INT: r->integer = len; diff --git a/php_redis.h b/php_redis.h index 6077c8b1ac..ffa88a2b36 100644 --- a/php_redis.h +++ b/php_redis.h @@ -197,6 +197,7 @@ PHP_METHOD(Redis, wait); PHP_METHOD(Redis, pubsub); PHP_METHOD(Redis, client); +PHP_METHOD(Redis, command); /* SCAN and friends */ PHP_METHOD(Redis, scan); diff --git a/redis.c b/redis.c index 07fa750fe7..d4e36280b6 100644 --- a/redis.c +++ b/redis.c @@ -261,6 +261,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) /* SCAN and friends */ PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) @@ -3718,6 +3719,14 @@ PHP_METHOD(Redis, client) { } } +/* proto array Redis::command() + * proto array Redis::command('info', string cmd) + * proto array Redis::command('getkeys', array cmd_args) */ +PHP_METHOD(Redis, command) { + REDIS_PROCESS_CMD(command, redis_read_variant_reply); +} +/* }}} */ + /* Helper to format any combination of SCAN arguments */ PHPAPI int redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, diff --git a/redis_cluster.c b/redis_cluster.c index 22458893ac..0f213ee8a0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -203,6 +203,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -2340,4 +2341,12 @@ PHP_METHOD(RedisCluster, echo) { } /* }}} */ +/* {{{ proto array RedisCluster::command() + * proto array RedisCluster::command('INFO', string cmd) + * proto array RedisCluster::command('GETKEYS', array cmd_args) */ +PHP_METHOD(RedisCluster, command) { + CLUSTER_PROCESS_CMD(command, cluster_variant_resp); +} +/* }}} */ + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index c79928d601..a86b447a25 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -256,6 +256,7 @@ PHP_METHOD(RedisCluster, time); PHP_METHOD(RedisCluster, randomkey); PHP_METHOD(RedisCluster, ping); PHP_METHOD(RedisCluster, echo); +PHP_METHOD(RedisCluster, command); /* Introspection */ PHP_METHOD(RedisCluster, getoption); diff --git a/redis_commands.c b/redis_commands.c index 39ed31b056..cf0d7592db 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2492,6 +2492,71 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 2, 0, cmd, cmd_len, slot); } +/* COMMAND */ +int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *kw=NULL; + zval *z_arg; + int kw_len; + + /* Parse our args */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sz", &kw, &kw_len, + &z_arg)==FAILURE) + { + return FAILURE; + } + + /* Construct our command */ + if(!kw) { + *cmd_len = redis_cmd_format_static(cmd, "COMMAND", ""); + } else if(kw && !z_arg) { + /* Sanity check */ + if(strncasecmp(kw, "info", sizeof("info")-1) || + Z_TYPE_P(z_arg)!=IS_STRING) + { + return FAILURE; + } + + /* COMMAND INFO */ + *cmd_len = redis_cmd_format_static(cmd, "COMMAND", "ss", "INFO", + sizeof("INFO")-1, Z_STRVAL_P(z_arg), Z_STRLEN_P(z_arg)); + } else { + int arr_len; + + /* Sanity check on args */ + if(strncasecmp(kw, "getkeys", sizeof("getkeys")-1) || + Z_TYPE_P(z_arg)!=IS_ARRAY || + (arr_len=zend_hash_num_elements(Z_ARRVAL_P(z_arg)))<1) + { + return FAILURE; + } + + zval **z_ele; + HashTable *ht_arr = Z_ARRVAL_P(z_arg); + smart_str cmdstr = {0}; + + redis_cmd_init_sstr(&cmdstr, 1 + arr_len, "COMMAND", sizeof("COMMAND")-1); + redis_cmd_append_sstr(&cmdstr, "GETKEYS", sizeof("GETKEYS")-1); + + for(zend_hash_internal_pointer_reset(ht_arr); + zend_hash_get_current_data(ht_arr, (void**)&z_ele)==SUCCESS; + zend_hash_move_forward(ht_arr)) + { + convert_to_string(*z_ele); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_PP(z_ele), Z_STRLEN_PP(z_ele)); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + } + + /* Any slot will do */ + CMD_RAND_SLOT(slot); + + return SUCCESS; +} + /* * Redis commands that don't deal with the server at all. The RedisSock* * pointer is the only thing retreived differently, so we just take that diff --git a/redis_commands.h b/redis_commands.h index f8f1e879ee..6ac9abeeaf 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -203,6 +203,9 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); From 13dd6067d1b5b9d2a92be8846ec6dcac43af7272 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 12 Jul 2014 11:47:43 -0700 Subject: [PATCH 0371/1986] Update how we call node directed commands Initially when sending commands to a specific node (e.g. INFO, BGSAVE, etc), the semantics were either to send a key or a host and port in two arguments. I think this would have been confusing and caused issues with more complicated commands like PUBSUB or SCRIPT. Now, to send a command, one either passes a string key or an array with host and port (which is what _masters now returns always). --- library.c | 1 - redis_cluster.c | 99 +++++++++++++++++++++++++------------------------ 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/library.c b/library.c index 9706fbe3ea..14be38028e 100644 --- a/library.c +++ b/library.c @@ -914,7 +914,6 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - int is_numeric; zval *z_ret; /* Read bulk response */ diff --git a/redis_cluster.c b/redis_cluster.c index 0f213ee8a0..508c30b023 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1721,19 +1721,10 @@ PHP_METHOD(RedisCluster, _unserialize) { /* {{{ proto array RedisCluster::_masters() */ PHP_METHOD(RedisCluster, _masters) { redisCluster *c = GET_CONTEXT(); - zend_bool as_arr=0; zval *z_ret, *z_sub; redisClusterNode **node; - char buf[1024], *host; + char *host; short port; - size_t len; - - // See if the user wants this as an array of host:port strings, or an array - // of [host, port] arrays. - if(!zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &as_arr)==FAILURE) - { - RETURN_FALSE; - } MAKE_STD_ZVAL(z_ret); array_init(z_ret); @@ -1745,19 +1736,12 @@ PHP_METHOD(RedisCluster, _masters) { host = (*node)->sock->host; port = (*node)->sock->port; - // What response type were we asked for - if(as_arr) { - MAKE_STD_ZVAL(z_sub); - array_init(z_sub); + MAKE_STD_ZVAL(z_sub); + array_init(z_sub); - add_next_index_stringl(z_sub, host, strlen(host), 1); - add_next_index_long(z_sub, port); - - add_next_index_zval(z_ret, z_sub); - } else { - len = snprintf(buf, sizeof(buf), "%s:%d", host, port); - add_next_index_stringl(z_ret, buf, (int)len, 1); - } + add_next_index_stringl(z_sub, host, strlen(host), 1); + add_next_index_long(z_sub, port); + add_next_index_zval(z_ret, z_sub); } *return_value = *z_ret; @@ -1962,23 +1946,44 @@ PHP_METHOD(RedisCluster, discard) { RETURN_TRUE; } -/* Get a slot either by key or host/port */ +/* Get a slot either by key (string) or host/port array */ static short -cluster_cmd_get_slot(redisCluster *c, int by_key, char *arg1, int arg1_len, - long port) +cluster_cmd_get_slot(redisCluster *c, zval *z_arg) { short slot; - int key_free; - if(by_key) { - key_free = redis_key_prefix(c->flags, &arg1, &arg1_len); - slot = cluster_hash_key(arg1, arg1_len); - if(key_free) efree(arg1); + /* If it's a string, treat it as a key. Otherwise, look for a two + * element array */ + if(Z_TYPE_P(z_arg)==IS_STRING) { + char *key = Z_STRVAL_P(z_arg); + int key_len = Z_STRLEN_P(z_arg), key_free; + + /* Hash it */ + key_free = redis_key_prefix(c->flags, &key, &key_len); + slot = cluster_hash_key(key, key_len); + if(key_free) efree(key); } else { - slot = cluster_find_slot(c,(const char *)arg1, (unsigned short)port); - if(slot<0) { + zval **z_host, **z_port; + + /* We'll need two elements, one string, one long */ + if(Z_TYPE_P(z_arg) != IS_ARRAY || + zend_hash_index_find(Z_ARRVAL_P(z_arg),0,(void**)&z_host)==FAILURE || + zend_hash_index_find(Z_ARRVAL_P(z_arg),1,(void**)&z_port)==FAILURE || + Z_TYPE_PP(z_host)!=IS_STRING || Z_TYPE_PP(z_port)!=IS_LONG) + { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Directed commands must be passed string key or [host,port]"); + return -1; + } + + /* Attempt to find this specific node by host:port */ + slot = cluster_find_slot(c,(const char *)Z_STRVAL_PP(z_host), + (unsigned short)Z_LVAL_PP(z_port)); + + /* Inform the caller if they've passed bad data */ + if(slot < 0) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", - arg1, port); + Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port)); } } @@ -1992,20 +1997,18 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply_type, cluster_cb cb) { redisCluster *c = GET_CONTEXT(); - char *cmd, *arg1; - int arg1_len, cmd_len; + char *cmd; + int cmd_len; + zval *z_arg; short slot; - long arg2; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &arg1, &arg1_len, - &arg2)==FAILURE) - { + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_arg)==FAILURE) { RETURN_FALSE; } // One argument means find the node (treated like a key), and two means // send the command to a specific host and port - slot = cluster_cmd_get_slot(c, ZEND_NUM_ARGS()==1, arg1, arg1_len, arg2); + slot = cluster_cmd_get_slot(c, z_arg); if(slot<0) { RETURN_FALSE; } @@ -2302,23 +2305,23 @@ PHP_METHOD(RedisCluster, ping) { } /* }}} */ -/* {{{ proto string RedisCluster::echo(string msg, string key) - * proto string RedisCluster::echo(string msg, string host, long port) */ +/* {{{ proto string RedisCluster::echo(mixed node, string msg) + * proto string RedisCluster::echo(mixed node, string msg) */ PHP_METHOD(RedisCluster, echo) { redisCluster *c = GET_CONTEXT(); - char *cmd, *arg2, *msg; - int cmd_len, arg2_len, msg_len; + zval *z_arg; + char *cmd, *msg; + int cmd_len, msg_len; short slot; - long arg3; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &msg, &msg_len, - &arg2, &arg2_len, &arg3)==FAILURE) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &msg, + &msg_len)==FAILURE) { RETURN_FALSE; } /* Grab slot either by key or host/port */ - slot = cluster_cmd_get_slot(c, ZEND_NUM_ARGS()==2, arg2, arg2_len, arg3); + slot = cluster_cmd_get_slot(c, z_arg); if(slot<0) { RETURN_FALSE; } From 01414b1f4fbeae20f809a797db85ca4c3f7cc41c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 12 Jul 2014 13:00:40 -0700 Subject: [PATCH 0372/1986] Implement remaining commands For certain commands that need to be directed at a node (CONFIG, CLIENT, SCRIPT, etc), the syntax can be complicated and highly variant. For this reason, these remaining commands have been implemented in a generic way, where users will rely on the error message from Redis to figure out what went wrong. All of the commands take our standardized "node" argument which can either take the form of a string key, or Array(host, port). --- redis_cluster.c | 137 ++++++++++++++++++++++++++++++++++++++++++++---- redis_cluster.h | 6 +++ 2 files changed, 134 insertions(+), 9 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 508c30b023..d25521153f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -204,7 +204,12 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(RedisCluster, cluster, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, client, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, config, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pubsub, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, script, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, slowlog, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -2031,6 +2036,72 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, efree(cmd); } +/* Generic routine for handling various commands which need to be directed at + * a node, but have complex syntax. We simply parse out the arguments and send + * the command as constructed by the caller */ +static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) +{ + redisCluster *c = GET_CONTEXT(); + smart_str cmd = {0}; + zval **z_args; + short slot; + int cmd_len; + int i, argc = ZEND_NUM_ARGS(); + + /* Commands using this pass-thru don't need to be enabled in MULTI mode */ + if(!CLUSTER_IS_ATOMIC(c)) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Command can't be issued in MULTI mode"); + RETURN_FALSE; + } + + /* We at least need the key or [host,port] argument */ + if(argc<1) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Command requires at least an argument to direct to a node"); + RETURN_FALSE; + } + + /* Allocate an array to process arguments */ + z_args = emalloc(argc * sizeof(zval*)); + + /* Grab args */ + if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + efree(z_args); + RETURN_FALSE; + } + + /* First argument needs to be the "where" */ + if((slot = cluster_cmd_get_slot(c, z_args[0]))<0) { + RETURN_FALSE; + } + + /* Initialize our command */ + redis_cmd_init_sstr(&cmd, argc-1, kw, kw_len); + + /* Iterate, appending args */ + for(i=1;i Date: Mon, 14 Jul 2014 09:57:08 -0700 Subject: [PATCH 0373/1986] Update SCAN to use new node semantics We've changed how you direct to a node, and this updates the SCAN command to reflect that. --- redis_cluster.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index d25521153f..ac0c5e6431 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2189,11 +2189,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, /* {{{ proto RedisCluster::scan(string master, long it [, string pat, long cnt]) */ PHP_METHOD(RedisCluster, scan) { redisCluster *c = GET_CONTEXT(); - redisClusterNode **n; - char *cmd, *node, *pat=NULL; + char *cmd, *pat=NULL; int pat_len=0, node_len, cmd_len; short slot; - zval *z_it; + zval *z_it, *z_node; long it, num_ele, count=0; /* Can't be in MULTI mode */ @@ -2204,8 +2203,8 @@ PHP_METHOD(RedisCluster, scan) { } /* Parse arguments */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/s|s!l", &z_it, &node, - &node_len, &pat, &pat_len, &count)==FAILURE) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z|s!l", &z_it, + &z_node, &pat, &pat_len, &count)==FAILURE) { RETURN_FALSE; } @@ -2226,17 +2225,12 @@ PHP_METHOD(RedisCluster, scan) { /* Construct our command */ cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, count); - - /* Find this slot by node */ - if(zend_hash_find(c->nodes, node, node_len+1, (void**)&n)==FAILURE) { - zend_throw_exception(redis_cluster_exception_ce, - "Unknown host:port passed to SCAN command", 0 TSRMLS_CC); - efree(cmd); - RETURN_FALSE; + + if((slot = cluster_cmd_get_slot(c, z_node))<0) { + RETURN_FALSE; } // Send it to the node in question - slot = (*n)->slot; if(cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, From 05597f1e6d3f8d47b40690993f8da51fdcd11cf2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 15 Jul 2014 10:51:13 -0700 Subject: [PATCH 0374/1986] Add a no_throw option to redis_check_eof We don't want redis_check_eof to throw an exception in Cluster, as we'll be getting bounced around while the slave election is in process. Added a very simple debugging function for testing stability, etc. --- cluster_library.c | 22 +++++++++++++++++----- library.c | 26 ++++++++++++++------------ library.h | 2 +- redis.c | 2 +- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 1619af2b02..9173cdcd21 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -28,6 +28,18 @@ static void cluster_dump_nodes(redisCluster *c) { } } +static void cluster_log(char *fmt, ...) +{ + va_list args; + char buffer[1024]; + + va_start(args, fmt); + vsnprintf(buffer,sizeof(buffer),fmt,args); + va_end(args); + + fprintf(stderr, "%s\n", buffer); +} + /* Direct handling of variant replies, in a hiredis like way. These methods * are used for non userland facing commands, as well as passed through from * them when the reply is just variant (e.g. eval) */ @@ -710,7 +722,7 @@ static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) char buf[255]; // Make sure we can send the request - if(redis_check_eof(redis_sock TSRMLS_DC) || + if(redis_check_eof(redis_sock, 1 TSRMLS_DC) || php_stream_write(redis_sock->stream, RESP_ASKING_CMD, sizeof(RESP_ASKING_CMD)-1) != sizeof(RESP_ASKING_CMD)-1) { @@ -718,7 +730,7 @@ static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) } // Read our reply type - if((redis_check_eof(redis_sock TSRMLS_CC) == - 1) || + if((redis_check_eof(redis_sock, 1 TSRMLS_CC) == - 1) || (reply_type = php_stream_getc(redis_sock->stream TSRMLS_DC) != TYPE_LINE)) { @@ -906,7 +918,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, CLUSTER_CLEAR_ERROR(c); CLUSTER_CLEAR_REPLY(c); - if(-1 == redis_check_eof(SLOT_SOCK(c,slot)) || + if(-1 == redis_check_eof(SLOT_SOCK(c,slot), 1 TSRMLS_CC) || EOF == (*reply_type = php_stream_getc(SLOT_STREAM(c,slot)))) { return -1; @@ -998,7 +1010,7 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, // First attempt to write it to the slot that's been requested if(redis_sock && redis_sock->stream && - !redis_check_eof(redis_sock TSRMLS_CC) && + !redis_check_eof(redis_sock, 1 TSRMLS_CC) && php_stream_write(redis_sock->stream, cmd, sz)==sz) { // We were able to write it @@ -1022,7 +1034,7 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, CLUSTER_LAZY_CONNECT((*seed_node)->sock); // Attempt to write our request to this node - if(!redis_check_eof((*seed_node)->sock TSRMLS_CC) && + if(!redis_check_eof((*seed_node)->sock, 1 TSRMLS_CC) && php_stream_write((*seed_node)->sock->stream, cmd, sz)==sz) { // Just return the first slot we think this node handles diff --git a/library.c b/library.c index 14be38028e..82fcaae6d4 100644 --- a/library.c +++ b/library.c @@ -157,8 +157,10 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->watching = 0; } - zend_throw_exception(redis_exception_ce, "Connection lost", - 0 TSRMLS_CC); + if(!no_throw) { + zend_throw_exception(redis_exception_ce, "Connection lost", + 0 TSRMLS_CC); + } return -1; } if(redis_sock->stream) { /* close existing stream before reconnecting */ @@ -416,7 +418,7 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, int numElems; zval *z_tab; - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return NULL; } @@ -455,7 +457,7 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes char * reply; - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return NULL; } @@ -496,7 +498,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D char *resp = NULL; size_t err_len; - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return NULL; } @@ -1235,7 +1237,7 @@ redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, int numElems; zval *z_multi_result; - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { @@ -1717,7 +1719,7 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, int numElems; zval *z_multi_result; - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { @@ -1774,7 +1776,7 @@ PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, int numElems; zval *z_multi_result; - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { @@ -1898,7 +1900,7 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, zval **z_keys = ctx; - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { @@ -1970,7 +1972,7 @@ PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz 0 TSRMLS_CC); return -1; } - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; } return php_stream_write(redis_sock->stream, cmd, sz); @@ -2159,7 +2161,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) { // Handle EOF - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; } @@ -2191,7 +2193,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) { // Make sure we haven't lost the connection, even trying to reconnect - if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { // Failure return -1; } diff --git a/library.h b/library.h index 97eb543e22..bcd3bd5bbd 100644 --- a/library.h +++ b/library.h @@ -50,7 +50,7 @@ PHPAPI int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); -PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC); +PHPAPI int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC); PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int nothrow); PHPAPI void redis_free_socket(RedisSock *redis_sock); PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); diff --git a/redis.c b/redis.c index d4e36280b6..dd168a7b6a 100644 --- a/redis.c +++ b/redis.c @@ -2378,7 +2378,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME int numElems; zval *z_tab; - redis_check_eof(redis_sock TSRMLS_CC); + redis_check_eof(redis_sock, 0 TSRMLS_CC); php_stream_gets(redis_sock->stream, inbuf, 1024); if(inbuf[0] != '*') { From 394708cbd48c715f9acb86e5ef1ec01309f70b5e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 15 Jul 2014 12:15:06 -0700 Subject: [PATCH 0375/1986] Initialize variable to avoid a segfault if all nodes are downo --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 9173cdcd21..c05064be8c 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -826,7 +826,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { PHPAPI int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { RedisSock **seed; - clusterReply *slots; + clusterReply *slots=NULL; int mapped=0; // Iterate over seeds until we can get slots From 6b3108837588dbb445d7a9910d6582f9cce707e6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 16 Jul 2014 16:07:11 -0700 Subject: [PATCH 0376/1986] Check for empty slave hosts CLUSTER SLOTS will return "" as the slave host for failed slaves, so we should skip them in node discovery. --- cluster_library.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index c05064be8c..c681a17e4e 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -685,7 +685,10 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { if(!VALIDATE_SLOTS_INNER(r3)) { return -1; } - + + // Skip slaves where the host is "" + if(r3->element[0]->len == 0) continue; + // Attach this node to our slave slave = cluster_node_create(c, r3->element[0]->str, (int)r3->element[0]->len, From 5ed61ea3055c21cd635f1d8d1f103f81a38888a8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 16 Jul 2014 16:35:10 -0700 Subject: [PATCH 0377/1986] Added getlasterror()/clearlasterror() routines, and a "last redirection" introspection method. --- cluster_library.c | 2 ++ redis_cluster.c | 51 ++++++++++++++++++++++++++++++++++++++++++----- redis_cluster.h | 3 +++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index c681a17e4e..16f02d0f27 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -380,6 +380,8 @@ static void cluster_set_err(redisCluster *c, char *err, int err_len) memcpy(c->err,err,err_len); c->err[err_len]='\0'; c->err_len = err_len; +php_printf("Set error to: %s\n", c->err); + } else { if(c->err) efree(c->err); c->err = NULL; diff --git a/redis_cluster.c b/redis_cluster.c index ac0c5e6431..d9d115c017 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -176,14 +176,17 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getlasterror, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, clearlasterror, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _serialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _unserialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _masters, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(RedisCluster, _redir, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, multi, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exec, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, discard, NULL, ZEND_ACC_PUBLIC) @@ -260,7 +263,7 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { // Allocate our actual struct cluster = emalloc(sizeof(redisCluster)); memset(cluster, 0, sizeof(redisCluster)); - + // We're not currently subscribed anywhere cluster->subscribed_slot = -1; @@ -1688,6 +1691,32 @@ PHP_METHOD(RedisCluster, evalsha) { /* Commands that do not interact with Redis, but just report stuff about * various options, etc */ +/* {{{ proto string RedisCluster::getlasterror() */ +PHP_METHOD(RedisCluster, getlasterror) { + redisCluster *c = GET_CONTEXT(); + + if(c->flags->err != NULL && c->flags->err_len > 0) { + RETURN_STRINGL(c->flags->err, c->flags->err_len, 1); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto bool RedisCluster::clearlasterror() */ +PHP_METHOD(RedisCluster, clearlasterror) { + redisCluster *c = GET_CONTEXT(); + + if(c->flags->err) { + efree(c->flags->err); + } + c->flags->err = NULL; + c->flags->err_len = 0; + + RETURN_TRUE; +} +/* }}} */ + /* {{{ proto long RedisCluster::getOption(long option */ PHP_METHOD(RedisCluster, getoption) { redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, @@ -1753,6 +1782,19 @@ PHP_METHOD(RedisCluster, _masters) { efree(z_ret); } +PHP_METHOD(RedisCluster, _redir) { + redisCluster *c = GET_CONTEXT(); + char buf[255]; + size_t len; + + len = snprintf(buf, sizeof(buf), "%s:%d", c->redir_host, c->redir_port); + if(c->redir_host && c->redir_host_len) { + RETURN_STRINGL(buf, len, 1); + } else { + RETURN_NULL(); + } +} + /* * Transaction handling */ @@ -2045,7 +2087,6 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) smart_str cmd = {0}; zval **z_args; short slot; - int cmd_len; int i, argc = ZEND_NUM_ARGS(); /* Commands using this pass-thru don't need to be enabled in MULTI mode */ @@ -2190,7 +2231,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, PHP_METHOD(RedisCluster, scan) { redisCluster *c = GET_CONTEXT(); char *cmd, *pat=NULL; - int pat_len=0, node_len, cmd_len; + int pat_len=0, cmd_len; short slot; zval *z_it, *z_node; long it, num_ele, count=0; diff --git a/redis_cluster.h b/redis_cluster.h index 8008eb94ca..9bf0144e27 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -265,11 +265,14 @@ PHP_METHOD(RedisCluster, echo); PHP_METHOD(RedisCluster, command); /* Introspection */ +PHP_METHOD(RedisCluster, getlasterror); +PHP_METHOD(RedisCluster, clearlasterror); PHP_METHOD(RedisCluster, getoption); PHP_METHOD(RedisCluster, setoption); PHP_METHOD(RedisCluster, _prefix); PHP_METHOD(RedisCluster, _serialize); PHP_METHOD(RedisCluster, _unserialize); PHP_METHOD(RedisCluster, _masters); +PHP_METHOD(RedisCluster, _redir); #endif From a65301bc2e9b678896675584e3a8e3219aa221f7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 16 Jul 2014 16:57:25 -0700 Subject: [PATCH 0378/1986] Update redirection logic to throw on CLUSTERDOWN When Redis Cluster responds with a CLUSTERDOWN error, we should throw an exception. It seems that in the current state of cluster (during slave election), it will enter a CLUSTERDOWN state for a short period of time. This allows clients to trap the exception and then either wait and try again, or do something else. --- cluster_library.c | 27 +++++++++++++++++---------- cluster_library.h | 7 ++++--- redis_cluster.c | 3 +++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 16f02d0f27..155f026bd3 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -372,6 +372,13 @@ void cluster_multi_fini(clusterMultiCmd *mc) { static void cluster_set_err(redisCluster *c, char *err, int err_len) { if(err && err_len>0) { + if(err_len >= sizeof("CLUSTERDOWN")-1 && + !memcmp(err, "CLUSTERDOWN", sizeof("CLUSTERDOWN")-1)) + { + c->clusterdown = 1; + return; + } + if(c->err == NULL) { c->err = emalloc(err_len+1); } else if(err_len > c->err_len) { @@ -380,8 +387,6 @@ static void cluster_set_err(redisCluster *c, char *err, int err_len) memcpy(c->err,err,err_len); c->err[err_len]='\0'; c->err_len = err_len; -php_printf("Set error to: %s\n", c->err); - } else { if(c->err) efree(c->err); c->err = NULL; @@ -1280,13 +1285,8 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, // Check the response from the slot we ended up querying. resp = cluster_check_response(c, slot, &c->reply_type TSRMLS_CC); - // If we're getting an error condition, impose a slight delay before - // we try again (e.g. server went down, election in process). If the - // data has been moved, update node configuration, and if ASK has been - // encountered, we'll just try again at that slot. - if(resp == -1) { - sleep(1); - } else if(resp == 1) { + /* Handle MOVED or ASKING redirection */ + if(resp == 1) { // If we get a MOVED response inside of a transaction, we have to // abort, because the transaction would be invalid. if(c->flags->mode == MULTI) { @@ -1302,7 +1302,14 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, } slot = c->redir_slot; } - } while(resp != 0); + } while(resp != 0 && !c->clusterdown); + + // If we've detected the cluster is down, throw an exception + if(c->clusterdown) { + zend_throw_exception(redis_cluster_exception_ce, + "The Redis Cluster is down (CLUSTERDOWN)", 0 TSRMLS_CC); + return -1; + } // Inform the cluster where to read the rest of our response, // and clear out redirection flag. diff --git a/cluster_library.h b/cluster_library.h index a4b44522c8..fb6b153ecd 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -74,7 +74,8 @@ efree(c->err); \ c->err = NULL; \ c->err_len = 0; \ - } + } \ + c->clusterdown = 0; /* Reset our last single line reply buffer and length */ #define CLUSTER_CLEAR_REPLY(c) \ @@ -195,8 +196,8 @@ typedef struct redisCluster { /* Variable to store MULTI response */ zval *multi_resp; - /* How many failures have we had in a row */ - int failures; + /* Flag for when we get a CLUSTERDOWN error */ + short clusterdown; /* The last ERROR we encountered */ char *err; diff --git a/redis_cluster.c b/redis_cluster.c index d9d115c017..ad175131a3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -267,6 +267,9 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { // We're not currently subscribed anywhere cluster->subscribed_slot = -1; + // Assume we're up initially + cluster->clusterdown = 0; + // Allocate our RedisSock we'll use to store prefix/serialization flags cluster->flags = ecalloc(1, sizeof(RedisSock)); From c88cb399cee90b2291b288e8fa51bd9f7af190b3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 17 Jul 2014 10:41:21 -0700 Subject: [PATCH 0379/1986] Skip the failed node when falling back --- cluster_library.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cluster_library.c b/cluster_library.c index 155f026bd3..0d202dc8b0 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1035,8 +1035,12 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, zend_hash_has_more_elements(c->nodes)==SUCCESS; zend_hash_move_forward(c->nodes)) { + /* Grab node */ zend_hash_get_current_data(c->nodes, (void**)&seed_node); + /* Skip this node if it's the one that failed */ + if((*seed_node)->sock == redis_sock) continue; + // TODO: Allow for failure/redirection queries to be sent // to slave nodes, but for now, stick with masters. if((*seed_node)->slave) continue; From f7328835303e5e554058ef9b4a32d5755f34096c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 23 Jul 2014 13:20:11 -0700 Subject: [PATCH 0380/1986] php_stream_gets is a char* return, not int --- cluster_library.c | 4 ++-- redis_cluster.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0d202dc8b0..33073172f3 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -748,7 +748,7 @@ static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) } // Consume the rest of our response - if(php_stream_gets(redis_sock->stream, buf, sizeof(buf)<0)) { + if(!php_stream_gets(redis_sock->stream, buf, sizeof(buf))) { return -1; } @@ -940,7 +940,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, int moved; // Attempt to read the error - if(php_stream_gets(SLOT_STREAM(c,slot), inbuf, sizeof(inbuf))<0) { + if(!php_stream_gets(SLOT_STREAM(c,slot), inbuf, sizeof(inbuf))) { return -1; } diff --git a/redis_cluster.c b/redis_cluster.c index ad175131a3..55fecd69c6 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -325,9 +325,9 @@ void free_cluster_context(void *object TSRMLS_DC) { efree(cluster); } -// -// PHP Methods -// +/* + * PHP Methods + */ /* Create a RedisCluster Object */ PHP_METHOD(RedisCluster, __construct) { From 6b9d4d29416bb767451762b1bb5b0da217081eec Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 23 Jul 2014 13:20:40 -0700 Subject: [PATCH 0381/1986] Added READONLY and READWRITE RESP for future slave failover logic --- cluster_library.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cluster_library.h b/cluster_library.h index fb6b153ecd..98ee305cdc 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -37,6 +37,8 @@ #define RESP_UNWATCH_CMD "*1\r\n$7\r\nUNWATCH\r\n" #define RESP_CLUSTER_SLOTS_CMD "*2\r\n$7\r\nCLUSTER\r\n$5\r\nSLOTS\r\n" #define RESP_ASKING_CMD "*1\r\n$6\r\nASKING\r\n" +#define RESP_READONLY_CMD "*1\r\n$8\r\nREADONLY\r\n" +#define RESP_READWRITE_CMD "*1\r\n$9\r\nREADWRITE\r\n" /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ From 383528d6964451637922bcee3991090e217b6eda Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 23 Jul 2014 14:15:37 -0700 Subject: [PATCH 0382/1986] KEYS command Keys should never be used in production, but this implementation allows the use of KEYS in such a way that you can call it normally and it will look through all of the known masters and return the whole keyspace. We might want to implement this only as an option (even at compile time) as it's very dangerous to run on large keyspaces. --- redis_cluster.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 55fecd69c6..b4b05e4410 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -810,10 +810,68 @@ PHP_METHOD(RedisCluster, exists) { /* {{{ proto array Redis::keys(string pattern) */ PHP_METHOD(RedisCluster, keys) { - // TODO: Figure out how to implement this, as we may want to send it across - // all nodes (although that seems dangerous), or ask for a specified slot. - zend_throw_exception(redis_cluster_exception_ce, - "KEYS command not implemented", 0 TSRMLS_CC); + redisCluster *c = GET_CONTEXT(); + redisClusterNode **node; + int pat_len, pat_free, cmd_len; + char *pat, *cmd; + clusterReply *resp; + zval *z_ret; + int i; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pat, &pat_len) + ==FAILURE) + { + RETURN_FALSE; + } + + /* Prefix and then build our command */ + pat_free = redis_key_prefix(c->flags, &pat, &pat_len); + cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pat, pat_len); + if(pat_free) efree(pat); + + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + + /* Iterate over our known nodes */ + for(zend_hash_internal_pointer_reset(c->nodes); + zend_hash_get_current_data(c->nodes, (void**)&node)==SUCCESS; + zend_hash_move_forward(c->nodes)) + { + if(cluster_send_slot(c, (*node)->slot, cmd, cmd_len, TYPE_MULTIBULK + TSRMLS_CC)<0) + { + php_error_docref(0 TSRMLS_CC, E_ERROR, "Can't send KEYS to %s:%d", + (*node)->sock->host, (*node)->sock->port); + efree(cmd); + RETURN_FALSE; + } + + /* Ensure we can get a response */ + resp = cluster_read_resp(c TSRMLS_CC); + if(!resp) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Can't read response from %s:%d", (*node)->sock->host, + (*node)->sock->port); + continue; + } + + /* Iterate keys, adding to our big array */ + for(i=0;ielements;i++) { + /* Skip non bulk responses, they should all be bulk */ + if(resp->element[i]->type != TYPE_BULK) { + continue; + } + + add_next_index_stringl(z_ret, resp->element[i]->str, + resp->element[i]->len, 0); + } + + /* Free response, don't free data */ + cluster_free_reply(resp, 0); + } + + /* Return our keys */ + RETURN_ZVAL(z_ret, 0, 1); } /* }}} */ From 03f2394838a964e7a1a00f7d490cc0f66cb48e9b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 23 Jul 2014 14:18:07 -0700 Subject: [PATCH 0383/1986] Don't leak memory in KEYS :) --- redis_cluster.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/redis_cluster.c b/redis_cluster.c index b4b05e4410..4adf7ea997 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -870,6 +870,8 @@ PHP_METHOD(RedisCluster, keys) { cluster_free_reply(resp, 0); } + efree(cmd); + /* Return our keys */ RETURN_ZVAL(z_ret, 0, 1); } From 15be7babcc22ceb3f76c461be223c24756b494ab Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 23 Jul 2014 15:52:17 -0700 Subject: [PATCH 0384/1986] INFO can take an argument Modify the INFO command in cluster to allow for an argument (e.g. CPU, COMMANDSTATS). --- redis_cluster.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 4adf7ea997..349ecabd87 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2435,11 +2435,42 @@ PHP_METHOD(RedisCluster, lastsave) { } /* }}} */ -/* {{{ proto array RedisCluster::info(string key) - * proto array RedisCluster::info(array host_port) */ +/* {{{ proto array RedisCluster::info(string key, [string $arg]) + * proto array RedisCluster::info(array host_port, [string $arg]) */ PHP_METHOD(RedisCluster, info) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INFO", - TYPE_BULK, cluster_info_resp); + redisCluster *c = GET_CONTEXT(); + char *cmd, *opt=NULL; + int cmd_len, opt_len; + zval *z_arg; + short slot; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &opt, + &opt_len)==FAILURE) + { + RETURN_FALSE; + } + + slot = cluster_cmd_get_slot(c, z_arg); + if(slot<0) { + RETURN_FALSE; + } + + if(opt != NULL) { + cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len); + } else { + cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); + } + + if(cluster_send_slot(c, slot, cmd, cmd_len, TYPE_BULK TSRMLS_CC)<0) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to send INFO command to spacific node", 0 TSRMLS_CC); + efree(cmd); + RETURN_FALSE; + } + + cluster_info_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + + efree(cmd); } /* }}} */ From fd4bda8f6a792092949db1e82bc90e5382c720a4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 11 Sep 2014 08:21:46 -0700 Subject: [PATCH 0385/1986] Don't call SMEMBERS for an SCARD operation :) --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index dd168a7b6a..4a683b210d 100644 --- a/redis.c +++ b/redis.c @@ -1254,7 +1254,7 @@ PHP_METHOD(Redis, sAdd) /* {{{ proto int Redis::sSize(string key) */ PHP_METHOD(Redis, sSize) { - REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("SCARD", redis_key_cmd, redis_long_response); } /* }}} */ From 46634a2928ab19c9440a6f3710cff247827d8682 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 1 Dec 2014 12:53:01 -0800 Subject: [PATCH 0386/1986] Formatting --- redis.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/redis.c b/redis.c index 4a683b210d..7c0c4766d4 100644 --- a/redis.c +++ b/redis.c @@ -720,7 +720,8 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl", &object, redis_ce, &host, - &host_len, &port, &timeout, &persistent_id, &persistent_id_len, &retry_interval) + &host_len, &port, &timeout, &persistent_id, + &persistent_id_len, &retry_interval) == FAILURE) { return FAILURE; @@ -2449,7 +2450,6 @@ PHP_METHOD(Redis, exec) } IF_MULTI() { - cmd_len = redis_cmd_format_static(&cmd, "EXEC", ""); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { @@ -2459,8 +2459,8 @@ PHP_METHOD(Redis, exec) efree(cmd); if(redis_sock_read_multibulk_multi_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock) < 0) + INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock) < 0) { zval_dtor(return_value); free_reply_callbacks(object, redis_sock); @@ -2474,7 +2474,6 @@ PHP_METHOD(Redis, exec) } IF_PIPELINE() { - char *request = NULL; int total = 0; int offset = 0; @@ -2511,8 +2510,8 @@ PHP_METHOD(Redis, exec) } if (redis_sock_read_multibulk_pipeline_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock) < 0) + INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock) < 0) { redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); From 1a231a979b5120c14f688097c82588997fca9860 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 1 Dec 2014 12:57:29 -0800 Subject: [PATCH 0387/1986] More formatting --- cluster_library.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 33073172f3..0600a8f52f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -756,11 +756,9 @@ static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) return 0; } -/* Get a RedisSock object from the host and port where we have been - * directed from an ASK response. We'll first see if we have - * connected to this node already, and return that. If not, we - * create it and add it to our nodes. - */ +/* Get a RedisSock object from the host and port where we have been directed + * from an ASK response. We'll first see if we have connected to this node + * already, and return that. If not, we create it and add it to our nodes. */ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { redisClusterNode **ppNode; char key[1024]; From d804342a6f8258aa25147eeacbd4e3b22fa4faa6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 1 Dec 2014 15:22:29 -0800 Subject: [PATCH 0388/1986] Fix variant reply handling --- cluster_library.c | 13 ++++++++++++- redis_cluster.c | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0600a8f52f..09b34fc3bc 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -183,7 +183,9 @@ clusterReply* cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, size_t len TSRMLS_DC) { - clusterReply *r = ecalloc(1, sizeof(clusterReply)); + clusterReply *r; + + r = ecalloc(1, sizeof(clusterReply)); r->type = type; // Error flag in case we go recursive @@ -193,6 +195,9 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, case TYPE_INT: r->integer = len; break; + case TYPE_LINE: + case TYPE_ERR: + return r; case TYPE_BULK: r->len = len; r->str = redis_sock_read_bulk_reply(redis_sock, len TSRMLS_CC); @@ -1702,6 +1707,9 @@ PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, case TYPE_INT: RETVAL_LONG(r->integer); break; + case TYPE_ERR: + RETVAL_FALSE; + break; case TYPE_LINE: RETVAL_TRUE; break; @@ -1728,6 +1736,9 @@ PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, case TYPE_INT: add_next_index_long(c->multi_resp, r->integer); break; + case TYPE_ERR: + add_next_index_bool(c->multi_resp, 0); + break; case TYPE_LINE: add_next_index_bool(c->multi_resp, 1); break; diff --git a/redis_cluster.c b/redis_cluster.c index 349ecabd87..4722937070 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2181,10 +2181,10 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* Initialize our command */ - redis_cmd_init_sstr(&cmd, argc-1, kw, kw_len); + redis_cmd_init_sstr(&cmd, argc, kw, kw_len); /* Iterate, appending args */ - for(i=1;i Date: Mon, 1 Dec 2014 21:06:31 -0800 Subject: [PATCH 0389/1986] Initial commit of formalized "redirection" timeout logic When phpredis is communicating with a cluster, there are two different kinds of timeout events. The first, is your standard read or write timeout where the socket is blocked either because of network issues, or because Redis is taking longer than the timeout to complete the request. The second is unique to cluster. Because Redis Cluster attempts to automatically failover (in the case of replicas), phpredis cluster will attempt to get data from a node where it thinks the key would live, and upon a failure to connect, try a different node (at random). This is because Redis could be resharding the connection and may point the client to a new (now good node). However, if it's not yet detected a failure, it will just bounce us back to the prior node (which could be actually down or have just sputtered due to various issues). So in this case, phpredis uses a second timeout mechanism where we keep track (in milleseconds) when we entered the query/response loop. Once we've been unsuccessful up to this timeout, phpredis will abort with a different (catchable) exception. TODO: It may be a good idea to implement some small delay, so we don't hit the cluster with lots of requests over and over until the cluster comes back. --- cluster_library.c | 30 +++++++++++++++++++++++++++--- cluster_library.h | 11 +++++++---- redis_cluster.c | 12 +++++++++++- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 09b34fc3bc..ee2a61aa8f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -430,6 +430,18 @@ unsigned short cluster_hash_key(const char *key, int len) { return crc16((char*)key+s+1,e-s-1) & REDIS_CLUSTER_MOD; } +/* Grab the current time in milliseconds */ +long long mstime(void) { + struct timeval tv; + long long mst; + + gettimeofday(&tv, NULL); + mst = ((long long)tv.tv_sec)*1000; + mst += tv.tv_usec/1000; + + return mst; +} + /* Hash a key from a ZVAL */ unsigned short cluster_hash_key_zval(zval *z_key) { const char *kptr; @@ -1260,9 +1272,15 @@ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { - int resp; + int resp, timedout=0; + long msstart; - // Issue commands until we find the right node or fail + /* Grab the current time in milliseconds */ + msstart = mstime(); + + /* Our main cluster request/reply loop. This loop runs until we're able + * to get a valid reply from a node, hit our "request" timeout, or encounter + * a CLUSTERDOWN state from Redis cluster. */ do { // Send MULTI to the node if we haven't yet. if(c->flags->mode == MULTI && SLOT_SOCK(c,slot)->mode != MULTI) { @@ -1309,13 +1327,19 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, } slot = c->redir_slot; } - } while(resp != 0 && !c->clusterdown); + + /* If we didn't get a valid response and we do have a timeout check it */ + timedout = resp && c->waitms ? mstime() - msstart >= c->waitms : 0; + } while(resp != 0 && !c->clusterdown && !timedout); // If we've detected the cluster is down, throw an exception if(c->clusterdown) { zend_throw_exception(redis_cluster_exception_ce, "The Redis Cluster is down (CLUSTERDOWN)", 0 TSRMLS_CC); return -1; + } else if (timedout) { + zend_throw_exception(redis_cluster_exception_ce, + "Timed out attempting to find data in the correct node!", 0 TSRMLS_CC); } // Inform the cluster where to read the rest of our response, diff --git a/cluster_library.h b/cluster_library.h index 98ee305cdc..162b0d30d4 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -178,10 +178,13 @@ typedef struct redisCluster { /* Object reference for Zend */ zend_object std; - /* Timeout and read timeout */ + /* Timeout and read timeout (for normal operations) */ double timeout; double read_timeout; + /* How long in milliseconds should we wait when being bounced around */ + long waitms; + /* Hash table of seed host/ports */ HashTable *seeds; @@ -214,9 +217,6 @@ typedef struct redisCluster { /* One RedisSock* struct for serialization and prefix information */ RedisSock *flags; - /* Cluster distribution mode (speed, vs. maintaining order of execution) */ - short dist_mode; - /* The first line of our last reply, not including our reply type byte * or the trailing \r\n */ char line_reply[1024]; @@ -330,6 +330,9 @@ void cluster_multi_fini(clusterMultiCmd *mc); unsigned short cluster_hash_key_zval(zval *key); unsigned short cluster_hash_key(const char *key, int len); +/* Get the current time in miliseconds */ +long long mstime(void); + PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC); diff --git a/redis_cluster.c b/redis_cluster.c index 4722937070..7d077d96f6 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -333,7 +333,7 @@ void free_cluster_context(void *object TSRMLS_DC) { PHP_METHOD(RedisCluster, __construct) { zval *object, *z_seeds=NULL; char *name; - long name_len; + long name_len, tmsec; double timeout = 0.0, read_timeout = 0.0; redisCluster *context = GET_CONTEXT(); @@ -374,6 +374,16 @@ PHP_METHOD(RedisCluster, __construct) { RETURN_FALSE; } + /* Set our timeout and read_timeout which we'll pass through to the + * socket type operations */ + context->timeout = timeout; + context->read_timeout = read_timeout; + + /* Calculate the number of miliseconds we will wait when bouncing around, + * (e.g. a node goes down), which is not the same as a standard timeout. */ + tmsec = (long)timeout * 1000; + context->waitms = tmsec + ((timeout-(long)timeout) * 1000); + // Initialize our RedisSock "seed" objects cluster_init_seeds(context, Z_ARRVAL_P(z_seeds)); From a271c4852cb1dd712fd14ea01ff90a38bf626558 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Dec 2014 14:29:56 -0800 Subject: [PATCH 0390/1986] Initial commit incorporating a "readonly" flag. We may want to configure phpredis such that it will attempt to fall back to a given master's slave, if the master were to go down (and the command is read only). --- cluster_library.c | 97 +------------------- cluster_library.h | 4 + redis_cluster.c | 226 +++++++++++++++++++++++++--------------------- redis_cluster.h | 6 +- 4 files changed, 134 insertions(+), 199 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index ee2a61aa8f..f4730f4dfe 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -502,100 +502,6 @@ static char **split_str_by_delim(char *str, char *delim, int *len) { return array; } -/*static int -cluster_parse_node_line(RedisSock *sock, char *line, clusterNodeInfo *info) { - char **array, *p, *p2; - int count, i; - - // First split by space - array = split_str_by_delim(line, " ", &count); - - // Sanity check on the number of elements we see - if(count < CLUSTER_MIN_NODE_LINE) { - efree(array); - return -1; - } - - // Sanity check on our cluster ID value - if(strlen(array[CLUSTER_NODES_HASH])!=CLUSTER_NAME_LEN) { - efree(array); - return -1; - } - - // Our cluster ID - info->name = estrndup(array[CLUSTER_NODES_HASH], CLUSTER_NAME_LEN); - - // Set the host/port - if(memcmp(array[CLUSTER_NODES_HOST_PORT], ":0", sizeof(":0"))==0) { - info->seed = 1; - info->host_len = strlen(sock->host); - info->host = estrndup(sock->host, info->host_len); - info->port = sock->port; - } else if((p = strchr(array[CLUSTER_NODES_HOST_PORT], ':'))!=NULL) { - // Null terminate at the : character - *p = '\0'; - - info->seed = 0; - info->host_len = p - array[CLUSTER_NODES_HOST_PORT]; - info->host = estrndup(array[CLUSTER_NODES_HOST_PORT], info->host_len); - info->port = atoi(p+1); - } else { - efree(array); - return -1; - } - - // Flag this a slave node if it's a slave - info->slave = strstr(array[CLUSTER_NODES_TYPE], "slave")!=NULL; - - // If we've got a master hash slot, set it - if(memcmp(array[CLUSTER_NODES_MASTER_HASH], "-", sizeof("-"))!=0) { - if(strlen(array[CLUSTER_NODES_MASTER_HASH])!=CLUSTER_NAME_LEN) { - efree(array); - return -1; - } - info->master_name = estrndup(array[CLUSTER_NODES_MASTER_HASH], - CLUSTER_NAME_LEN); - } else if(info->slave) { - // Slaves should always have a master hash - efree(array); - return -1; - } - - // See if the node serves slots - if(count >= CLUSTER_MIN_SLOTS_COUNT) { - // Allocate for enough ranges - info->slots_size = count - CLUSTER_MIN_SLOTS_COUNT + 1; - info->slots = ecalloc(info->slots_size, sizeof(clusterSlotRange)); - - // Now iterate over each range - for(i=0;islots_size;i++) { - p = array[i+CLUSTER_MIN_SLOTS_COUNT-1]; - - // If we don't see -, this node only serves one slot - if((p2 = strchr(p,'-'))==NULL) { - info->slots[i].start = atoi(p); - info->slots[i].end = atoi(p); - } else { - *p2++ = '\0'; - - // Set this range - info->slots[i].start = atoi(p); - info->slots[i].end = atoi(p2); - } - } - } else { - info->slots_size = 0; - info->slots = NULL; - } - - // Free our array - efree(array); - - // Success! - return 0; -} -*/ - /* Execute a CLUSTER SLOTS command against the seed socket, and return the * reply or NULL on failure. */ clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) @@ -1357,8 +1263,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, * we try to send the request to the Cluster *or* if a non MOVED or ASK * error is encountered, in which case our response processing macro will * short circuit and RETURN_FALSE, as the error will have already been - * consumed. - */ + * consumed. */ /* RAW bulk response handler */ PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, diff --git a/cluster_library.h b/cluster_library.h index 162b0d30d4..69de5020fb 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -185,6 +185,10 @@ typedef struct redisCluster { /* How long in milliseconds should we wait when being bounced around */ long waitms; + /* Are we flagged as being in readonly mode, meaning we could fall back to + * a given master's slave */ + short readonly; + /* Hash table of seed host/ports */ HashTable *seeds; diff --git a/redis_cluster.c b/redis_cluster.c index 7d077d96f6..a175a8e8c9 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -403,13 +403,13 @@ PHP_METHOD(RedisCluster, close) { /* {{{ proto string RedisCluster::get(string key) */ PHP_METHOD(RedisCluster, get) { - CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp, 1); } /* }}} */ /* {{{ proto bool RedisCluster::set(string key, string value) */ PHP_METHOD(RedisCluster, set) { - CLUSTER_PROCESS_CMD(set, cluster_bool_resp); + CLUSTER_PROCESS_CMD(set, cluster_bool_resp, 0); } /* }}} */ @@ -559,6 +559,9 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, return -1; } + /* MGET is readonly, DEL is not */ + c->readonly = kw_len == 3 && CLUSTER_IS_ATOMIC(c); + // Initialize our "multi" command handler with command/len CLUSTER_MULTI_INIT(mc, kw, kw_len); @@ -654,6 +657,9 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, return -1; } + /* This is a write command */ + c->readonly = 0; + // Set up our multi command handler CLUSTER_MULTI_INIT(mc, kw, kw_len); @@ -790,31 +796,31 @@ PHP_METHOD(RedisCluster, msetnx) { /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { - CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, psetex) { - CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::setnx(string key, string value) */ PHP_METHOD(RedisCluster, setnx) { - CLUSTER_PROCESS_KW_CMD("SETNX", redis_kv_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("SETNX", redis_kv_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::getSet(string key, string value) */ PHP_METHOD(RedisCluster, getset) { - CLUSTER_PROCESS_KW_CMD("GETSET", redis_kv_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("GETSET", redis_kv_cmd, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto int RedisCluster::exists(string key) */ PHP_METHOD(RedisCluster, exists) { - CLUSTER_PROCESS_KW_CMD("EXISTS", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("EXISTS", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ @@ -842,6 +848,9 @@ PHP_METHOD(RedisCluster, keys) { MAKE_STD_ZVAL(z_ret); array_init(z_ret); + /* Treat as readonly */ + c->readonly = CLUSTER_IS_ATOMIC(c); + /* Iterate over our known nodes */ for(zend_hash_internal_pointer_reset(c->nodes); zend_hash_get_current_data(c->nodes, (void**)&node)==SUCCESS; @@ -889,31 +898,31 @@ PHP_METHOD(RedisCluster, keys) { /* {{{ proto int RedisCluster::type(string key) */ PHP_METHOD(RedisCluster, type) { - CLUSTER_PROCESS_KW_CMD("TYPE", redis_key_cmd, cluster_type_resp); + CLUSTER_PROCESS_KW_CMD("TYPE", redis_key_cmd, cluster_type_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::pop(string key) */ PHP_METHOD(RedisCluster, lpop) { - CLUSTER_PROCESS_KW_CMD("LPOP", redis_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("LPOP", redis_key_cmd, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::rpop(string key) */ PHP_METHOD(RedisCluster, rpop) { - CLUSTER_PROCESS_KW_CMD("RPOP", redis_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("RPOP", redis_key_cmd, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::lset(string key, long index, string val) */ PHP_METHOD(RedisCluster, lset) { - CLUSTER_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::spop(string key) */ PHP_METHOD(RedisCluster, spop) { - CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_cmd, cluster_bulk_resp, 0); } /* }}} */ @@ -923,6 +932,9 @@ PHP_METHOD(RedisCluster, srandmember) { char *cmd; int cmd_len; short slot; short have_count; + /* Treat as readonly */ + c->readonly = CLUSTER_IS_ATOMIC(c); + if(redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, &cmd_len, &slot, NULL, &have_count) ==FAILURE) @@ -948,481 +960,481 @@ PHP_METHOD(RedisCluster, srandmember) { /* {{{ proto string RedisCluster::strlen(string key) */ PHP_METHOD(RedisCluster, strlen) { - CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_bulk_resp, 1); } /* {{{ proto long RedisCluster::lpush(string key, string val1, ... valN) */ PHP_METHOD(RedisCluster, lpush) { - CLUSTER_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::rpush(string key, string val1, ... valN) */ PHP_METHOD(RedisCluster, rpush) { - CLUSTER_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::blpop(string key1, ... keyN, long timeout) */ PHP_METHOD(RedisCluster, blpop) { - CLUSTER_PROCESS_CMD(blpop, cluster_mbulk_resp); + CLUSTER_PROCESS_CMD(blpop, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::brpop(string key1, ... keyN, long timeout */ PHP_METHOD(RedisCluster, brpop) { - CLUSTER_PROCESS_CMD(brpop, cluster_mbulk_resp); + CLUSTER_PROCESS_CMD(brpop, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::rpushx(string key, mixed value) */ PHP_METHOD(RedisCluster, rpushx) { - CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::lpushx(string key, mixed value) */ PHP_METHOD(RedisCluster, lpushx) { - CLUSTER_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::linsert(string k,string pos,mix pvt,mix val) */ PHP_METHOD(RedisCluster, linsert) { - CLUSTER_PROCESS_CMD(linsert, cluster_long_resp); + CLUSTER_PROCESS_CMD(linsert, cluster_long_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::lindex(string key, long index) */ PHP_METHOD(RedisCluster, lindex) { - CLUSTER_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::lrem(string key, long count, string val) */ PHP_METHOD(RedisCluster, lrem) { - CLUSTER_PROCESS_CMD(lrem, cluster_long_resp); + CLUSTER_PROCESS_CMD(lrem, cluster_long_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::rpoplpush(string key, string key) */ PHP_METHOD(RedisCluster, rpoplpush) { - CLUSTER_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto string RedisCluster::brpoplpush(string key, string key, long tm) */ PHP_METHOD(RedisCluster, brpoplpush) { - CLUSTER_PROCESS_CMD(brpoplpush, cluster_bulk_resp); + CLUSTER_PROCESS_CMD(brpoplpush, cluster_bulk_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::llen(string key) */ PHP_METHOD(RedisCluster, llen) { - CLUSTER_PROCESS_KW_CMD("LLEN", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("LLEN", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::scard(string key) */ PHP_METHOD(RedisCluster, scard) { - CLUSTER_PROCESS_KW_CMD("SCARD", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("SCARD", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::smembers(string key) */ PHP_METHOD(RedisCluster, smembers) { - CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, cluster_mbulk_resp); + CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::sismember(string key) */ PHP_METHOD(RedisCluster, sismember) { - CLUSTER_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, cluster_1_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::sadd(string key, string val1 [, ...]) */ PHP_METHOD(RedisCluster, sadd) { - CLUSTER_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::srem(string key, string val1 [, ...]) */ PHP_METHOD(RedisCluster, srem) { - CLUSTER_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::sunion(string key1, ... keyN) */ PHP_METHOD(RedisCluster, sunion) { - CLUSTER_PROCESS_CMD(sunion, cluster_mbulk_resp); + CLUSTER_PROCESS_CMD(sunion, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::sunionstore(string dst, string k1, ... kN) */ PHP_METHOD(RedisCluster, sunionstore) { - CLUSTER_PROCESS_CMD(sunionstore, cluster_long_resp); + CLUSTER_PROCESS_CMD(sunionstore, cluster_long_resp, 0); } /* }}} */ /* {{{ ptoto array RedisCluster::sinter(string k1, ... kN) */ PHP_METHOD(RedisCluster, sinter) { - CLUSTER_PROCESS_CMD(sinter, cluster_mbulk_resp); + CLUSTER_PROCESS_CMD(sinter, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ ptoto long RedisCluster::sinterstore(string dst, string k1, ... kN) */ PHP_METHOD(RedisCluster, sinterstore) { - CLUSTER_PROCESS_CMD(sinterstore, cluster_long_resp); + CLUSTER_PROCESS_CMD(sinterstore, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::sdiff(string k1, ... kN) */ PHP_METHOD(RedisCluster, sdiff) { - CLUSTER_PROCESS_CMD(sdiff, cluster_mbulk_resp); + CLUSTER_PROCESS_CMD(sdiff, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::sdiffstore(string dst, string k1, ... kN) */ PHP_METHOD(RedisCluster, sdiffstore) { - CLUSTER_PROCESS_CMD(sdiffstore, cluster_long_resp); + CLUSTER_PROCESS_CMD(sdiffstore, cluster_long_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::smove(sting src, string dst, string mem) */ PHP_METHOD(RedisCluster, smove) { - CLUSTER_PROCESS_CMD(smove, cluster_1_resp); + CLUSTER_PROCESS_CMD(smove, cluster_1_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::persist(string key) */ PHP_METHOD(RedisCluster, persist) { - CLUSTER_PROCESS_KW_CMD("PERSIST", redis_key_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("PERSIST", redis_key_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::ttl(string key) */ PHP_METHOD(RedisCluster, ttl) { - CLUSTER_PROCESS_KW_CMD("TTL", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("TTL", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::pttl(string key) */ PHP_METHOD(RedisCluster, pttl) { - CLUSTER_PROCESS_KW_CMD("PTTL", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("PTTL", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zcard(string key) */ PHP_METHOD(RedisCluster, zcard) { - CLUSTER_PROCESS_KW_CMD("ZCARD", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZCARD", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto double RedisCluster::zscore(string key) */ PHP_METHOD(RedisCluster, zscore) { - CLUSTER_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, cluster_dbl_resp); + CLUSTER_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, cluster_dbl_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zadd(string key,double score,string mem, ...) */ PHP_METHOD(RedisCluster, zadd) { - CLUSTER_PROCESS_CMD(zadd, cluster_long_resp); + CLUSTER_PROCESS_CMD(zadd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto double RedisCluster::zincrby(string key, double by, string mem) */ PHP_METHOD(RedisCluster, zincrby) { - CLUSTER_PROCESS_CMD(zincrby, cluster_dbl_resp); + CLUSTER_PROCESS_CMD(zincrby, cluster_dbl_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zremrangebyscore(string k, string s, string e) */ PHP_METHOD(RedisCluster, zremrangebyscore) { CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd, - cluster_long_resp); + cluster_long_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zcount(string key, string s, string e) */ PHP_METHOD(RedisCluster, zcount) { - CLUSTER_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zrank(string key, mixed member) */ PHP_METHOD(RedisCluster, zrank) { - CLUSTER_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zrevrank(string key, mixed member) */ PHP_METHOD(RedisCluster, zrevrank) { - CLUSTER_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::hlen(string key) */ PHP_METHOD(RedisCluster, hlen) { - CLUSTER_PROCESS_KW_CMD("HLEN", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("HLEN", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::hkeys(string key) */ PHP_METHOD(RedisCluster, hkeys) { - CLUSTER_PROCESS_KW_CMD("HKEYS", redis_key_cmd, cluster_mbulk_raw_resp); + CLUSTER_PROCESS_KW_CMD("HKEYS", redis_key_cmd, cluster_mbulk_raw_resp, 1); } /* }}} */ /* {{{ proto array RedisCluster::hvals(string key) */ PHP_METHOD(RedisCluster, hvals) { - CLUSTER_PROCESS_KW_CMD("HVALS", redis_key_cmd, cluster_mbulk_resp); + CLUSTER_PROCESS_KW_CMD("HVALS", redis_key_cmd, cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::hget(string key, string mem) */ PHP_METHOD(RedisCluster, hget) { - CLUSTER_PROCESS_KW_CMD("HGET", redis_key_str_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("HGET", redis_key_str_cmd, cluster_bulk_resp, 1); } /* }}} */ /* {{{ proto bool RedisCluster::hset(string key, string mem, string val) */ PHP_METHOD(RedisCluster, hset) { - CLUSTER_PROCESS_CMD(hset, cluster_long_resp); + CLUSTER_PROCESS_CMD(hset, cluster_long_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::hsetnx(string key, string mem, string val) */ PHP_METHOD(RedisCluster, hsetnx) { - CLUSTER_PROCESS_CMD(hsetnx, cluster_1_resp); + CLUSTER_PROCESS_CMD(hsetnx, cluster_1_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::hgetall(string key) */ PHP_METHOD(RedisCluster, hgetall) { CLUSTER_PROCESS_KW_CMD("HGETALL", redis_key_cmd, - cluster_mbulk_zipstr_resp); + cluster_mbulk_zipstr_resp, 1); } /* }}} */ /* {{{ proto bool RedisCluster::hexists(string key, string member) */ PHP_METHOD(RedisCluster, hexists) { - CLUSTER_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, cluster_1_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::hincr(string key, string mem, long val) */ PHP_METHOD(RedisCluster, hincrby) { - CLUSTER_PROCESS_CMD(hincrby, cluster_long_resp); + CLUSTER_PROCESS_CMD(hincrby, cluster_long_resp, 0); } /* }}} */ /* {{{ proto double RedisCluster::hincrbyfloat(string k, string m, double v) */ PHP_METHOD(RedisCluster, hincrbyfloat) { - CLUSTER_PROCESS_CMD(hincrbyfloat, cluster_dbl_resp); + CLUSTER_PROCESS_CMD(hincrbyfloat, cluster_dbl_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::hmset(string key, array key_vals) */ PHP_METHOD(RedisCluster, hmset) { - CLUSTER_PROCESS_CMD(hmset, cluster_bool_resp); + CLUSTER_PROCESS_CMD(hmset, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::hdel(string key, string mem1, ... memN) */ PHP_METHOD(RedisCluster, hdel) { - CLUSTER_PROCESS_CMD(hdel, cluster_long_resp); + CLUSTER_PROCESS_CMD(hdel, cluster_long_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::hmget(string key, array members) */ PHP_METHOD(RedisCluster, hmget) { - CLUSTER_PROCESS_CMD(hmget, cluster_mbulk_assoc_resp); + CLUSTER_PROCESS_CMD(hmget, cluster_mbulk_assoc_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { - CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp); + CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp, 1); } /* {{{ proto long RedisCluster::incr(string key) */ PHP_METHOD(RedisCluster, incr) { - CLUSTER_PROCESS_KW_CMD("INCR", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("INCR", redis_key_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::incrby(string key, long byval) */ PHP_METHOD(RedisCluster, incrby) { - CLUSTER_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::decr(string key) */ PHP_METHOD(RedisCluster, decr) { - CLUSTER_PROCESS_KW_CMD("DECR", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("DECR", redis_key_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::decrby(string key, long byval) */ PHP_METHOD(RedisCluster, decrby) { - CLUSTER_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto double RedisCluster::incrbyfloat(string key, double val) */ PHP_METHOD(RedisCluster, incrbyfloat) { CLUSTER_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, - cluster_dbl_resp); + cluster_dbl_resp, 0); } /* }}} */ /* {{{ proto double RedisCluster::decrbyfloat(string key, double val) */ PHP_METHOD(RedisCluster, decrbyfloat) { CLUSTER_PROCESS_KW_CMD("DECRBYFLOAT", redis_key_dbl_cmd, - cluster_dbl_resp); + cluster_dbl_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::expire(string key, long sec) */ PHP_METHOD(RedisCluster, expire) { - CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::expireat(string key, long ts) */ PHP_METHOD(RedisCluster, expireat) { - CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, cluster_1_resp, 0); } /* {{{ proto bool RedisCluster::pexpire(string key, long ms) */ PHP_METHOD(RedisCluster, pexpire) { - CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::pexpireat(string key, long ts) */ PHP_METHOD(RedisCluster, pexpireat) { - CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::append(string key, string val) */ PHP_METHOD(RedisCluster, append) { - CLUSTER_PROCESS_KW_CMD("APPEND", redis_kv_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("APPEND", redis_kv_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::getbit(string key, long val) */ PHP_METHOD(RedisCluster, getbit) { - CLUSTER_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::setbit(string key, long offset, bool onoff) */ PHP_METHOD(RedisCluster, setbit) { - CLUSTER_PROCESS_CMD(setbit, cluster_long_resp); + CLUSTER_PROCESS_CMD(setbit, cluster_long_resp, 0); } /* {{{ proto long RedisCluster::bitop(string op,string key,[string key2,...]) */ PHP_METHOD(RedisCluster, bitop) { - CLUSTER_PROCESS_CMD(bitop, cluster_long_resp); + CLUSTER_PROCESS_CMD(bitop, cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::bitcount(string key, [int start, int end]) */ PHP_METHOD(RedisCluster, bitcount) { - CLUSTER_PROCESS_CMD(bitcount, cluster_long_resp); + CLUSTER_PROCESS_CMD(bitcount, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::bitpos(string key, int bit, [int s, int end]) */ PHP_METHOD(RedisCluster, bitpos) { - CLUSTER_PROCESS_CMD(bitpos, cluster_long_resp); + CLUSTER_PROCESS_CMD(bitpos, cluster_long_resp, 1); } /* }}} */ /* {{{ proto string Redis::lget(string key, long index) */ PHP_METHOD(RedisCluster, lget) { - CLUSTER_PROCESS_KW_CMD("LGET", redis_key_long_cmd, cluster_bulk_resp); + CLUSTER_PROCESS_KW_CMD("LGET", redis_key_long_cmd, cluster_bulk_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::getrange(string key, long start, long end) */ PHP_METHOD(RedisCluster, getrange) { CLUSTER_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd, - cluster_bulk_resp); + cluster_bulk_resp, 1); } /* }}} */ /* {{{ proto string RedisCluster::ltrim(string key, long start, long end) */ PHP_METHOD(RedisCluster, ltrim) { - CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::lrange(string key, long start, long end) */ PHP_METHOD(RedisCluster, lrange) { CLUSTER_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, - cluster_mbulk_resp); + cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zremrangebyrank(string k, long s, long e) */ PHP_METHOD(RedisCluster, zremrangebyrank) { CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd, - cluster_long_resp); + cluster_long_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::publish(string key, string msg) */ PHP_METHOD(RedisCluster, publish) { - CLUSTER_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::rename(string key1, string key2) */ PHP_METHOD(RedisCluster, rename) { - CLUSTER_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, cluster_bool_resp); + CLUSTER_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::renamenx(string key1, string key2) */ PHP_METHOD(RedisCluster, renamenx) { - CLUSTER_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, cluster_1_resp); + CLUSTER_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::pfcount(string key) */ PHP_METHOD(RedisCluster, pfcount) { - CLUSTER_PROCESS_KW_CMD("PFCOUNT", redis_key_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("PFCOUNT", redis_key_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto bool RedisCluster::pfadd(string key, array vals) */ PHP_METHOD(RedisCluster, pfadd) { - CLUSTER_PROCESS_CMD(pfadd, cluster_1_resp); + CLUSTER_PROCESS_CMD(pfadd, cluster_1_resp, 0); } /* }}} */ /* {{{ proto bool RedisCluster::pfmerge(string key, array keys) */ PHP_METHOD(RedisCluster, pfmerge) { - CLUSTER_PROCESS_CMD(pfmerge, cluster_bool_resp); + CLUSTER_PROCESS_CMD(pfmerge, cluster_bool_resp, 0); } /* }}} */ /* {{{ proto boolean RedisCluster::restore(string key, long ttl, string val) */ PHP_METHOD(RedisCluster, restore) { CLUSTER_PROCESS_KW_CMD("RESTORE", redis_key_long_str_cmd, - cluster_bool_resp); + cluster_bool_resp, 0); } /* }}} */ /* {{{ proto long RedisCluster::setrange(string key, long offset, string val) */ PHP_METHOD(RedisCluster, setrange) { CLUSTER_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, - cluster_long_resp); + cluster_long_resp, 0); } /* }}} */ @@ -1484,20 +1496,20 @@ PHP_METHOD(RedisCluster, zrangebyscore) { /* {{{ proto RedisCluster::zunionstore(string dst, array keys, [array weights, * string agg]) */ PHP_METHOD(RedisCluster, zunionstore) { - CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zinterstore(string dst, array keys, [array weights, * string agg]) */ PHP_METHOD(RedisCluster, zinterstore) { - CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zrem(string key, string val1, ... valN) */ PHP_METHOD(RedisCluster, zrem) { - CLUSTER_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, cluster_long_resp, 0); } /* }}} */ @@ -1513,7 +1525,7 @@ PHP_METHOD(RedisCluster, zrevrangebyscore) { * [offset, count]) */ PHP_METHOD(RedisCluster, zrangebylex) { CLUSTER_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, - cluster_mbulk_resp); + cluster_mbulk_resp, 1); } /* }}} */ @@ -1521,20 +1533,20 @@ PHP_METHOD(RedisCluster, zrangebylex) { * string min, [long off, long limit) */ PHP_METHOD(RedisCluster, zrevrangebylex) { CLUSTER_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd, - cluster_mbulk_resp); + cluster_mbulk_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zlexcount(string key, string min, string max) */ PHP_METHOD(RedisCluster, zlexcount) { - CLUSTER_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, cluster_long_resp); + CLUSTER_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, cluster_long_resp, 1); } /* }}} */ /* {{{ proto long RedisCluster::zremrangebylex(string key, string min, string max) */ PHP_METHOD(RedisCluster, zremrangebylex) { CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, - cluster_long_resp); + cluster_long_resp, 0); } /* }}} */ @@ -1593,13 +1605,13 @@ PHP_METHOD(RedisCluster, object) { /* {{{ proto null RedisCluster::subscribe(array chans, callable cb) */ PHP_METHOD(RedisCluster, subscribe) { - CLUSTER_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp); + CLUSTER_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp, 0); } /* }}} */ /* {{{ proto null RedisCluster::psubscribe(array pats, callable cb) */ PHP_METHOD(RedisCluster, psubscribe) { - CLUSTER_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp); + CLUSTER_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp, 0); } /* }}} */ @@ -2242,6 +2254,9 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } + /* Treat as readonly */ + c->readonly = 1; + // Convert iterator to long if it isn't, update our long iterator if it's // set and >0, and finish if it's back to zero if(Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it)<0) { @@ -2309,6 +2324,9 @@ PHP_METHOD(RedisCluster, scan) { zval *z_it, *z_node; long it, num_ele, count=0; + /* Treat as read-only */ + c->readonly = CLUSTER_IS_ATOMIC(c); + /* Can't be in MULTI mode */ if(!CLUSTER_IS_ATOMIC(c)) { zend_throw_exception(redis_cluster_exception_ce, @@ -2460,6 +2478,9 @@ PHP_METHOD(RedisCluster, info) { RETURN_FALSE; } + /* Treat INFO as non read-only, as we probably want the master */ + c->readonly = 0; + slot = cluster_cmd_get_slot(c, z_arg); if(slot<0) { RETURN_FALSE; @@ -2578,6 +2599,9 @@ PHP_METHOD(RedisCluster, echo) { RETURN_FALSE; } + /* Treat this as a readonly command */ + c->readonly = CLUSTER_IS_ATOMIC(c); + /* Grab slot either by key or host/port */ slot = cluster_cmd_get_slot(c, z_arg); if(slot<0) { @@ -2606,7 +2630,7 @@ PHP_METHOD(RedisCluster, echo) { * proto array RedisCluster::command('INFO', string cmd) * proto array RedisCluster::command('GETKEYS', array cmd_args) */ PHP_METHOD(RedisCluster, command) { - CLUSTER_PROCESS_CMD(command, cluster_variant_resp); + CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); } /* }}} */ diff --git a/redis_cluster.h b/redis_cluster.h index 9bf0144e27..a801fc5193 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -59,8 +59,9 @@ c->flags->mode = ATOMIC; \ /* Simple 1-1 command -> response macro */ -#define CLUSTER_PROCESS_CMD(cmdname, resp_func) \ +#define CLUSTER_PROCESS_CMD(cmdname, resp_func, readcmd) \ redisCluster *c = GET_CONTEXT(); \ + c->readonly = CLUSTER_IS_ATOMIC(c) && readcmd; \ char *cmd; int cmd_len; short slot; void *ctx=NULL; \ if(redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,c->flags, &cmd, \ &cmd_len, &slot, &ctx)==FAILURE) { \ @@ -78,8 +79,9 @@ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); /* More generic processing, where only the keyword differs */ -#define CLUSTER_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ +#define CLUSTER_PROCESS_KW_CMD(kw, cmdfunc, resp_func, readcmd) \ redisCluster *c = GET_CONTEXT(); \ + c->readonly = CLUSTER_IS_ATOMIC(c) && readcmd; \ char *cmd; int cmd_len; short slot; void *ctx=NULL; \ if(cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,\ &slot,&ctx)==FAILURE) { \ From 24823ef2ff21897207d0fa47d828c76a829ccc9d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 9 Dec 2014 10:26:48 -0800 Subject: [PATCH 0391/1986] Minor formatting, cleanup --- cluster_library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index f4730f4dfe..36ce262871 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -951,7 +951,7 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, // Don't fall back if direct communication with this slot is required. if(direct) return -1; - // Fall back by attempting the request against every connected node + // Fall back by attempting the request against every known node for(zend_hash_internal_pointer_reset(c->nodes); zend_hash_has_more_elements(c->nodes)==SUCCESS; zend_hash_move_forward(c->nodes)) @@ -1234,7 +1234,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, slot = c->redir_slot; } - /* If we didn't get a valid response and we do have a timeout check it */ + /* If we didn't get a valid response see if we've now timed out */ timedout = resp && c->waitms ? mstime() - msstart >= c->waitms : 0; } while(resp != 0 && !c->clusterdown && !timedout); From 55f1835e2eb2e99ed0736dd57599860edaa1fd76 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 27 Jan 2015 14:15:56 -0800 Subject: [PATCH 0392/1986] Remove deprecated defines --- cluster_library.h | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index 69de5020fb..bbb08362f4 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -11,25 +11,6 @@ #define REDIS_CLUSTER_SLOTS 16384 #define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) -/* Minimum valid CLUSTER NODES line element count - and the minimum we expect if there are slots */ -#define CLUSTER_MIN_NODE_LINE 8 -#define CLUSTER_MIN_SLOTS_COUNT 9 - -/* Length of a cluster name */ -#define CLUSTER_NAME_LEN 40 - -/* The parts for our cluster nodes command */ -#define CLUSTER_NODES_HASH 0 -#define CLUSTER_NODES_HOST_PORT 1 -#define CLUSTER_NODES_TYPE 2 -#define CLUSTER_NODES_MASTER_HASH 3 -#define CLUSTER_NODES_PING 4 -#define CLUSTER_NODES_PONG 5 -#define CLUSTER_NODES_EPOCH 6 -#define CLUSTER_NODES_CONNECTED 7 -#define CLUSTER_SLOTS 8 - /* Complete representation for various commands in RESP */ #define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" #define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" From cc8dde6fa276c24642215140ec1e775edb0e9b97 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Feb 2015 21:13:56 -0800 Subject: [PATCH 0393/1986] Fix ZTS typos --- cluster_library.h | 2 +- redis.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index bbb08362f4..ebd3eb8493 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -303,7 +303,7 @@ void cluster_dist_free(HashTable *ht); int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, int key_len, clusterKeyVal **kv); void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val - TSRMLS_CC); + TSRMLS_DC); /* Aggregation for multi commands like MGET, MSET, and MSETNX */ void cluster_multi_init(clusterMultiCmd *mc, char *kw, int kw_len); diff --git a/redis.c b/redis.c index 7c0c4766d4..019d851b2f 100644 --- a/redis.c +++ b/redis.c @@ -488,7 +488,7 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) /* Redis and RedisCluster objects share serialization/prefixing settings so * this is a generic function to add class constants to either */ -static void add_class_constants(zend_class_entry *ce, int is_cluster) { +static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) { add_constant_long(ce, "REDIS_NOT_FOUND", REDIS_NOT_FOUND); add_constant_long(ce, "REDIS_STRING", REDIS_STRING); add_constant_long(ce, "REDIS_SET", REDIS_SET); @@ -591,8 +591,8 @@ PHP_MINIT_FUNCTION(redis) ); /* Add shared class constants to Redis and RedisCluster objects */ - add_class_constants(redis_ce, 0); - add_class_constants(redis_cluster_ce, 1); + add_class_constants(redis_ce, 0 TSRMLS_CC); + add_class_constants(redis_cluster_ce, 1 TSRMLS_CC); /* Add specific RedisCluster class constants */ add_cluster_constants(redis_cluster_ce); From 8b2fc3f08717ae533d2dd3cd59b4e91fef2aa1e9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 21 Feb 2015 21:21:22 -0800 Subject: [PATCH 0394/1986] Fix pesky ZTS compile errors --- cluster_library.c | 27 +++++++++++++-------------- redis_cluster.c | 19 ++++++++++--------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 36ce262871..373d806c31 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -340,7 +340,7 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, /* Provided a clusterKeyVal, add a value */ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val - TSRMLS_CC) + TSRMLS_DC) { char *val; int val_len, val_free; @@ -513,13 +513,13 @@ clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) // Send the command to the socket and consume reply type if(redis_sock_write(redis_sock, RESP_CLUSTER_SLOTS_CMD, sizeof(RESP_CLUSTER_SLOTS_CMD)-1 TSRMLS_CC)<0 || - redis_read_reply_type(redis_sock, &type, &len)<0) + redis_read_reply_type(redis_sock, &type, &len TSRMLS_CC)<0) { return NULL; } // Consume the rest of our response - if((r = cluster_read_sock_resp(redis_sock, type, len))==NULL || + if((r = cluster_read_sock_resp(redis_sock, type, len TSRMLS_CC))==NULL || r->type != TYPE_MULTIBULK || r->elements < 3) { if(r) cluster_free_reply(r, 1); @@ -651,11 +651,10 @@ PHPAPI void cluster_free_node(redisClusterNode *node) { * bounce us back and forth until the slots have migrated */ static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) { - REDIS_REPLY_TYPE reply_type; char buf[255]; // Make sure we can send the request - if(redis_check_eof(redis_sock, 1 TSRMLS_DC) || + if(redis_check_eof(redis_sock, 1 TSRMLS_CC) || php_stream_write(redis_sock->stream, RESP_ASKING_CMD, sizeof(RESP_ASKING_CMD)-1) != sizeof(RESP_ASKING_CMD)-1) { @@ -664,9 +663,7 @@ static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) // Read our reply type if((redis_check_eof(redis_sock, 1 TSRMLS_CC) == - 1) || - (reply_type = php_stream_getc(redis_sock->stream TSRMLS_DC) - != TYPE_LINE)) - { + (php_stream_getc(redis_sock->stream) != TYPE_LINE)) { return -1; } @@ -883,7 +880,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, // Fetch the first line of our response from Redis. if(redis_sock_gets(SLOT_SOCK(c,slot),c->line_reply,sizeof(c->line_reply), - &sz)<0) + &sz TSRMLS_CC)<0) { return -1; } @@ -927,7 +924,7 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, if(c->redir_type != REDIR_ASK) { redis_sock = SLOT_SOCK(c,slot); } else { - redis_sock = cluster_get_asking_sock(c); + redis_sock = cluster_get_asking_sock(c TSRMLS_CC); // Redis Cluster wants this command preceded by the "ASKING" command if(cluster_send_asking(redis_sock TSRMLS_CC)<0) { @@ -1001,7 +998,7 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, /* Provided a redisCluster object, the slot where we thought data was and * the slot where data was moved, update our node mapping */ -static void cluster_update_slot(redisCluster *c TSRMLS_CC) { +static void cluster_update_slot(redisCluster *c TSRMLS_DC) { redisClusterNode *node; // Do we already have the new slot mapped @@ -1229,7 +1226,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, // In case of a MOVED redirection, update our node mapping if(c->redir_type == REDIR_MOVED) { - cluster_update_slot(c); + cluster_update_slot(c TSRMLS_CC); } slot = c->redir_slot; } @@ -1303,7 +1300,7 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } // Return the string if we can unserialize it - if(redis_unserialize(c->flags, resp, c->reply_len, &z_ret)==0) { + if(redis_unserialize(c->flags, resp, c->reply_len, &z_ret TSRMLS_CC)==0) { CLUSTER_RETURN_STRING(c, resp, c->reply_len); } else { if(CLUSTER_IS_ATOMIC(c)) { @@ -1833,7 +1830,9 @@ PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, array_init(z_result); // Call our callback - if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len, NULL)==FAILURE) { + if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len, NULL TSRMLS_CC) + ==FAILURE) + { zval_dtor(z_result); FREE_ZVAL(z_result); return NULL; diff --git a/redis_cluster.c b/redis_cluster.c index a175a8e8c9..12c5682eb7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -502,7 +502,8 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, } // Serialize our value if required - kv->val_free = redis_serialize(c->flags,*z_val,&(kv->val),&(kv->val_len)); + kv->val_free = redis_serialize(c->flags,*z_val,&(kv->val),&(kv->val_len) + TSRMLS_CC); // Success return 0; @@ -1639,8 +1640,8 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } // This has to operate on our subscribe slot - if(cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK) - ==FAILURE) + if(cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK + TSRMLS_CC) ==FAILURE) { zend_throw_exception(redis_cluster_exception_ce, "Failed to UNSUBSCRIBE within our subscribe loop!", 0 TSRMLS_CC); @@ -2080,7 +2081,7 @@ PHP_METHOD(RedisCluster, discard) { /* Get a slot either by key (string) or host/port array */ static short -cluster_cmd_get_slot(redisCluster *c, zval *z_arg) +cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { short slot; @@ -2140,7 +2141,7 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, // One argument means find the node (treated like a key), and two means // send the command to a specific host and port - slot = cluster_cmd_get_slot(c, z_arg); + slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); if(slot<0) { RETURN_FALSE; } @@ -2198,7 +2199,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* First argument needs to be the "where" */ - if((slot = cluster_cmd_get_slot(c, z_args[0]))<0) { + if((slot = cluster_cmd_get_slot(c, z_args[0] TSRMLS_CC))<0) { RETURN_FALSE; } @@ -2358,7 +2359,7 @@ PHP_METHOD(RedisCluster, scan) { cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, count); - if((slot = cluster_cmd_get_slot(c, z_node))<0) { + if((slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC))<0) { RETURN_FALSE; } @@ -2481,7 +2482,7 @@ PHP_METHOD(RedisCluster, info) { /* Treat INFO as non read-only, as we probably want the master */ c->readonly = 0; - slot = cluster_cmd_get_slot(c, z_arg); + slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); if(slot<0) { RETURN_FALSE; } @@ -2603,7 +2604,7 @@ PHP_METHOD(RedisCluster, echo) { c->readonly = CLUSTER_IS_ATOMIC(c); /* Grab slot either by key or host/port */ - slot = cluster_cmd_get_slot(c, z_arg); + slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); if(slot<0) { RETURN_FALSE; } From 060837f9edf9bffe9b88a074f9c772dc7e98a626 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 23 Feb 2015 12:08:46 -0800 Subject: [PATCH 0395/1986] Add functionality to specify cluster configuration in redis.ini --- cluster_library.h | 3 + common.h | 6 ++ redis.c | 10 ++++ redis_cluster.c | 144 ++++++++++++++++++++++++++++++++++------------ redis_commands.c | 13 +++++ 5 files changed, 139 insertions(+), 37 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index ebd3eb8493..c4fa9883b8 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -170,6 +170,9 @@ typedef struct redisCluster { * a given master's slave */ short readonly; + /* RedisCluster failover options (never, on error, to load balance) */ + short failover; + /* Hash table of seed host/ports */ HashTable *seeds; diff --git a/common.h b/common.h index 53576ebc6f..673cd931f3 100644 --- a/common.h +++ b/common.h @@ -59,6 +59,12 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_READ_TIMEOUT 3 #define REDIS_OPT_SCAN 4 +/* cluster options */ +#define CLUSTER_OPT_FAILOVER 5 +#define CLUSTER_FAILOVER_NEVER 0 +#define CLUSTER_FAILOVER_ERROR 1 +#define CLUSTER_FAILOVER_LB 2 + /* serializers */ #define REDIS_SERIALIZER_NONE 0 #define REDIS_SERIALIZER_PHP 1 diff --git a/redis.c b/redis.c index 019d851b2f..abd9879aa8 100644 --- a/redis.c +++ b/redis.c @@ -72,6 +72,11 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.index", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL) + + /* redis cluster */ + PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.clusters.timeout", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL) PHP_INI_END() /** @@ -518,6 +523,11 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) add_constant_long(ce, "OPT_SCAN", REDIS_OPT_SCAN); add_constant_long(ce, "SCAN_RETRY", REDIS_SCAN_RETRY); add_constant_long(ce, "SCAN_NORETRY", REDIS_SCAN_NORETRY); + + /* Cluster option to allow for slave failover */ + if (is_cluster) { + add_constant_long(ce, "OPT_SLAVE_FAILOVER", CLUSTER_OPT_FAILOVER); + } #ifdef HAVE_REDIS_IGBINARY add_constant_long(ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY); #endif diff --git a/redis_cluster.c b/redis_cluster.c index 12c5682eb7..e45f10c451 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -29,6 +29,8 @@ #include "redis_commands.h" #include #include "library.h" +#include +#include zend_class_entry *redis_cluster_ce; @@ -325,6 +327,102 @@ void free_cluster_context(void *object TSRMLS_DC) { efree(cluster); } +/* Attempt to connect to a Redis cluster provided seeds and timeout options */ +void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, + double read_timeout TSRMLS_DC) +{ + // Validate timeout + if(timeout < 0L || timeout > INT_MAX) { + zend_throw_exception(redis_cluster_exception_ce, + "Invalid timeout", 0 TSRMLS_CC); + } + + // Validate our read timeout + if(read_timeout < 0L || read_timeout > INT_MAX) { + zend_throw_exception(redis_cluster_exception_ce, + "Invalid read timeout", 0 TSRMLS_CC); + } + + /* Make sure there are some seeds */ + if(zend_hash_num_elements(ht_seeds)==0) { + zend_throw_exception(redis_cluster_exception_ce, + "Must pass seeds", 0 TSRMLS_CC); + } + + /* Set our timeout and read_timeout which we'll pass through to the + * socket type operations */ + c->timeout = timeout; + c->read_timeout = read_timeout; + + /* Calculate the number of miliseconds we will wait when bouncing around, + * (e.g. a node goes down), which is not the same as a standard timeout. */ + c->waitms = (long)(timeout * 1000); + + // Initialize our RedisSock "seed" objects + cluster_init_seeds(c, ht_seeds); + + // Create and map our key space + cluster_map_keyspace(c TSRMLS_CC); +} + +/* Attempt to load a named cluster configured in php.ini */ +void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { + zval *z_seeds, *z_timeout, *z_read_timeout, **z_value; + char *iptr; + double timeout=0, read_timeout=0; + HashTable *ht_seeds = NULL; + + /* Seeds */ + MAKE_STD_ZVAL(z_seeds); + array_init(z_seeds); + iptr = estrdup(INI_STR("redis.clusters.seeds")); + sapi_module.treat_data(PARSE_STRING, iptr, z_seeds TSRMLS_CC); + if (zend_hash_find(Z_ARRVAL_P(z_seeds), name, name_len+1, (void**)&z_value) != FAILURE) { + ht_seeds = Z_ARRVAL_PP(z_value); + } else { + zval_dtor(z_seeds); + efree(z_seeds); + zend_throw_exception(redis_cluster_exception_ce, "Couldn't find seeds for cluster", 0 TSRMLS_CC); + } + + /* Connection timeout */ + MAKE_STD_ZVAL(z_timeout); + array_init(z_timeout); + iptr = estrdup(INI_STR("redis.clusters.timeout")); + sapi_module.treat_data(PARSE_STRING, iptr, z_timeout TSRMLS_CC); + if (zend_hash_find(Z_ARRVAL_P(z_timeout), name, name_len+1, (void**)&z_value) != FAILURE) { + if (Z_TYPE_PP(z_value) == IS_STRING) { + timeout = atof(Z_STRVAL_PP(z_value)); + } else if (Z_TYPE_PP(z_value) == IS_DOUBLE) { + timeout = Z_DVAL_PP(z_value); + } + } + + /* Read timeout */ + MAKE_STD_ZVAL(z_read_timeout); + array_init(z_read_timeout); + iptr = estrdup(INI_STR("redis.clusters.read_timeout")); + sapi_module.treat_data(PARSE_STRING, iptr, z_read_timeout TSRMLS_CC); + if (zend_hash_find(Z_ARRVAL_P(z_read_timeout), name, name_len+1, (void**)&z_value) != FAILURE) { + if (Z_TYPE_PP(z_value) == IS_STRING) { + read_timeout = atof(Z_STRVAL_PP(z_value)); + } else if (Z_TYPE_PP(z_value) == IS_DOUBLE) { + read_timeout = Z_DVAL_PP(z_value); + } + } + + /* Attempt to create/connect to the cluster */ + redis_cluster_init(c, ht_seeds, timeout, read_timeout TSRMLS_CC); + + /* Clean up our arrays */ + zval_dtor(z_seeds); + efree(z_seeds); + zval_dtor(z_timeout); + efree(z_timeout); + zval_dtor(z_read_timeout); + efree(z_read_timeout); +} + /* * PHP Methods */ @@ -347,48 +445,20 @@ PHP_METHOD(RedisCluster, __construct) { } // Require a name - if(name_len == 0) { + if(name_len == 0 && ZEND_NUM_ARGS() < 2) { zend_throw_exception(redis_cluster_exception_ce, - "You must give this cluster a name!", + "You must specify a name or pass seeds!", 0 TSRMLS_CC); } - // Validate timeout - if(timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, - "Invalid timeout", 0 TSRMLS_CC); - RETURN_FALSE; - } - - // Validate our read timeout - if(read_timeout < 0L || read_timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, - "Invalid read timeout", 0 TSRMLS_CC); - RETURN_FALSE; - } - - // TODO: Implement seed retrieval from php.ini - if(!z_seeds || zend_hash_num_elements(Z_ARRVAL_P(z_seeds))==0) { - zend_throw_exception(redis_cluster_exception_ce, - "Must pass seeds", 0 TSRMLS_CC); - RETURN_FALSE; + /* If we've been passed only one argument, the user is attempting to connect + * to a named cluster, stored in php.ini, otherwise we'll need manual seeds */ + if (ZEND_NUM_ARGS() > 2) { + redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout + TSRMLS_CC); + } else { + redis_cluster_load(context, name, name_len TSRMLS_CC); } - - /* Set our timeout and read_timeout which we'll pass through to the - * socket type operations */ - context->timeout = timeout; - context->read_timeout = read_timeout; - - /* Calculate the number of miliseconds we will wait when bouncing around, - * (e.g. a node goes down), which is not the same as a standard timeout. */ - tmsec = (long)timeout * 1000; - context->waitms = tmsec + ((timeout-(long)timeout) * 1000); - - // Initialize our RedisSock "seed" objects - cluster_init_seeds(context, Z_ARRVAL_P(z_seeds)); - - // Create and map our key space - cluster_map_keyspace(context TSRMLS_CC); } /* diff --git a/redis_commands.c b/redis_commands.c index cf0d7592db..d521d5874c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2587,6 +2587,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_DOUBLE(redis_sock->read_timeout); case REDIS_OPT_SCAN: RETURN_LONG(redis_sock->scan); + case CLUSTER_OPT_FAILOVER: + RETURN_LONG(c->failover); default: RETURN_FALSE; } @@ -2653,6 +2655,17 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, } RETURN_FALSE; break; + case CLUSTER_OPT_FAILOVER: + val_long = atol(val_str); + if (val_long == CLUSTER_FAILOVER_NEVER || + val_long == CLUSTER_FAILOVER_ERROR || + val_long == CLUSTER_FAILOVER_LB) + { + c->failover = val_long; + RETURN_TRUE; + } else { + RETURN_FALSE; + } default: RETURN_FALSE; } From 0bb500c1e0f845cceaddca877504ec8c478837b7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 23 Feb 2015 15:32:35 -0800 Subject: [PATCH 0396/1986] Added options for slave failover --- cluster_library.c | 86 ++++++++++++++++++++++------------------------- common.h | 8 ++--- redis.c | 5 ++- redis_commands.c | 10 +++--- 4 files changed, 54 insertions(+), 55 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 373d806c31..4fecbd9471 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -913,30 +913,27 @@ PHPAPI void cluster_disconnect(redisCluster *c TSRMLS_DC) { /* Attempt to write to a cluster node. If the node is NULL (e.g. it's been * umapped, we keep falling back until we run out of nodes to try */ static int cluster_sock_write(redisCluster *c, unsigned short slot, - const char *cmd, size_t sz, int - direct TSRMLS_DC) + const char *cmd, size_t sz, int direct TSRMLS_DC) { RedisSock *redis_sock; redisClusterNode **seed_node; - // If we're not in ASK redirection, use the slot requested, otherwise - // send our ASKING command and use the asking slot. + /* If we're not in ASK redirection, use the slot requested, otherwise send + * our ASKING command and use the asking slot. */ if(c->redir_type != REDIR_ASK) { redis_sock = SLOT_SOCK(c,slot); } else { redis_sock = cluster_get_asking_sock(c TSRMLS_CC); - - // Redis Cluster wants this command preceded by the "ASKING" command if(cluster_send_asking(redis_sock TSRMLS_CC)<0) { return -1; } } - // If the lazy_connect flag is still set, we've not actually - // connected to this node, so do that now. + /* If the lazy_connect flag is still set, we've not actually connected to + * this node, so do that now. */ CLUSTER_LAZY_CONNECT(redis_sock); - // First attempt to write it to the slot that's been requested + /* First attempt to write it to the slot that's been requested */ if(redis_sock && redis_sock->stream && !redis_check_eof(redis_sock, 1 TSRMLS_CC) && php_stream_write(redis_sock->stream, cmd, sz)==sz) @@ -945,10 +942,10 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, return slot; } - // Don't fall back if direct communication with this slot is required. + /* Don't fall back if direct communication with this slot is required. */ if(direct) return -1; - // Fall back by attempting the request against every known node + /* Fall back by attempting the request against every known node */ for(zend_hash_internal_pointer_reset(c->nodes); zend_hash_has_more_elements(c->nodes)==SUCCESS; zend_hash_move_forward(c->nodes)) @@ -959,22 +956,21 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, /* Skip this node if it's the one that failed */ if((*seed_node)->sock == redis_sock) continue; - // TODO: Allow for failure/redirection queries to be sent - // to slave nodes, but for now, stick with masters. + /* Skip slave nodes */ if((*seed_node)->slave) continue; CLUSTER_LAZY_CONNECT((*seed_node)->sock); - // Attempt to write our request to this node + /* Attempt to write our request to this node */ if(!redis_check_eof((*seed_node)->sock, 1 TSRMLS_CC) && php_stream_write((*seed_node)->sock->stream, cmd, sz)==sz) { - // Just return the first slot we think this node handles + /* Just return the first slot we think this node handles */ return (*seed_node)->slot; } } - // We were unable to write to any node in our cluster + /* We were unable to write to any node in our cluster */ return -1; } @@ -992,7 +988,7 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, return *ret; } - // Not found + /* Not found */ return NULL; } @@ -1001,57 +997,57 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, static void cluster_update_slot(redisCluster *c TSRMLS_DC) { redisClusterNode *node; - // Do we already have the new slot mapped + /* Do we already have the new slot mapped */ if(c->master[c->redir_slot]) { - // No need to do anything if it's the same node + /* No need to do anything if it's the same node */ if(!CLUSTER_REDIR_CMP(c)) { return; } - // Check to see if we have this new node mapped + /* Check to see if we have this new node mapped */ node = cluster_find_node(c, c->redir_host, c->redir_port); if(node) { - // Just point to this slot + /* Just point to this slot */ c->master[c->redir_slot] = node; } else { - // Create our node + /* Create our node */ node = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); - // Now point our slot at the node + /* Now point our slot at the node */ c->master[c->redir_slot] = node; } } else { - // Check to see if the ip and port are mapped + /* Check to see if the ip and port are mapped */ node = cluster_find_node(c, c->redir_host, c->redir_port); if(!node) { node = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); } - // Map the slot to this node + /* Map the slot to this node */ c->master[c->redir_slot] = node; } - // Update slot inside of node, so it can be found for command sending + /* Update slot inside of node, so it can be found for command sending */ node->slot = c->redir_slot; - // Make sure we unflag this node as a slave, as Redis Cluster will only - // ever direct us to master nodes. + /* Make sure we unflag this node as a slave, as Redis Cluster will only ever + * direct us to master nodes. */ node->slave = 0; } /* Send EXEC to a specific slot */ PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { - // We have to be able to write this to the slot requested + /* We have to be able to write this to the slot requested */ if(cluster_sock_write(c, slot, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD)-1, 1 TSRMLS_CC)==-1) { return -1; } - // We have to get a proper response from the slot to continue + /* We have to get a proper response from the slot to continue */ if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || c->reply_type != TYPE_MULTIBULK) { @@ -1086,7 +1082,7 @@ PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { clusterFoldItem *fi = c->multi_head; - // Loop through our fold items + /* Loop through our fold items */ while(fi) { if(SLOT_SOCK(c,fi->slot)->mode == MULTI) { if(cluster_send_discard(c, fi->slot TSRMLS_CC)<0) { @@ -1099,31 +1095,31 @@ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { fi = fi->next; } - // Update our overall cluster state + /* Update our overall cluster state */ c->flags->mode = ATOMIC; - // Success + /* Success */ return 0; } /* Send MULTI to a given slot and consume the response. If we can't send the * command OR we get an error in our response, we have to fail. */ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { - // We have to be able to communicate with the node we want + /* We have to be able to communicate with the node we want */ if(cluster_sock_write(c, slot, RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD)-1, 1 TSRMLS_CC)==-1) { return -1; } - // We have to get a proper response + /* We have to get a proper response */ if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || c->reply_type != TYPE_LINE) { return -1; } - // Success + /* Success */ return 0; } @@ -1185,9 +1181,9 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, * to get a valid reply from a node, hit our "request" timeout, or encounter * a CLUSTERDOWN state from Redis cluster. */ do { - // Send MULTI to the node if we haven't yet. + /* Send MULTI to the node if we haven't yet. */ if(c->flags->mode == MULTI && SLOT_SOCK(c,slot)->mode != MULTI) { - // We have to fail if we can't send MULTI to the node + /* We have to fail if we can't send MULTI to the node */ if(cluster_send_multi(c, slot TSRMLS_CC)==-1) { zend_throw_exception(redis_cluster_exception_ce, "Unable to enter MULTI mode on required slot", @@ -1195,28 +1191,28 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, return -1; } - // This node is now inside a transaction + /* This node is now inside a transaction */ SLOT_SOCK(c,slot)->mode = MULTI; } - // Attempt to send the command to the slot requested + /* Attempt to send the command to the slot requested */ if((slot = cluster_sock_write(c, slot, cmd, cmd_len, 0 TSRMLS_CC))==-1) { - // We have no choice but to throw an exception. We - // can't communicate with any node at all. + /* We have no choice but to throw an exception. We can't communicate + * with any node at all. */ zend_throw_exception(redis_cluster_exception_ce, "Can't communicate with any node in the cluster", 0 TSRMLS_CC); return -1; } - // Check the response from the slot we ended up querying. + /* Check the response from the slot we ended up querying. */ resp = cluster_check_response(c, slot, &c->reply_type TSRMLS_CC); /* Handle MOVED or ASKING redirection */ if(resp == 1) { - // If we get a MOVED response inside of a transaction, we have to - // abort, because the transaction would be invalid. + /* If we get a MOVED response inside of a transaction, we have to + abort, because the transaction would be invalid. */ if(c->flags->mode == MULTI) { zend_throw_exception(redis_cluster_exception_ce, "Can't process MULTI sequence when cluster is resharding", diff --git a/common.h b/common.h index 673cd931f3..4f7f8c633d 100644 --- a/common.h +++ b/common.h @@ -60,10 +60,10 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_SCAN 4 /* cluster options */ -#define CLUSTER_OPT_FAILOVER 5 -#define CLUSTER_FAILOVER_NEVER 0 -#define CLUSTER_FAILOVER_ERROR 1 -#define CLUSTER_FAILOVER_LB 2 +#define REDIS_OPT_FAILOVER 5 +#define REDIS_FAILOVER_NONE 0 +#define REDIS_FAILOVER_ERROR 1 +#define REDIS_FAILOVER_DISTRIBUTE 2 /* serializers */ #define REDIS_SERIALIZER_NONE 0 diff --git a/redis.c b/redis.c index abd9879aa8..615d726f54 100644 --- a/redis.c +++ b/redis.c @@ -526,7 +526,10 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) /* Cluster option to allow for slave failover */ if (is_cluster) { - add_constant_long(ce, "OPT_SLAVE_FAILOVER", CLUSTER_OPT_FAILOVER); + add_constant_long(ce, "OPT_SLAVE_FAILOVER", REDIS_OPT_FAILOVER); + add_constant_long(ce, "FAILOVER_NONE", REDIS_FAILOVER_NONE); + add_constant_long(ce, "FAILOVER_ERROR", REDIS_FAILOVER_ERROR); + add_constant_long(ce, "FAILOVER_DISTRIBUTE", REDIS_FAILOVER_DISTRIBUTE); } #ifdef HAVE_REDIS_IGBINARY add_constant_long(ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY); diff --git a/redis_commands.c b/redis_commands.c index d521d5874c..5bb1ed39d1 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2587,7 +2587,7 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_DOUBLE(redis_sock->read_timeout); case REDIS_OPT_SCAN: RETURN_LONG(redis_sock->scan); - case CLUSTER_OPT_FAILOVER: + case REDIS_OPT_FAILOVER: RETURN_LONG(c->failover); default: RETURN_FALSE; @@ -2655,11 +2655,11 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, } RETURN_FALSE; break; - case CLUSTER_OPT_FAILOVER: + case REDIS_OPT_FAILOVER: val_long = atol(val_str); - if (val_long == CLUSTER_FAILOVER_NEVER || - val_long == CLUSTER_FAILOVER_ERROR || - val_long == CLUSTER_FAILOVER_LB) + if (val_long == REDIS_FAILOVER_NONE || + val_long == REDIS_FAILOVER_ERROR || + val_long == REDIS_FAILOVER_DISTRIBUTE) { c->failover = val_long; RETURN_TRUE; From 043b360651651a5a7c53a93f2de4cd5c4989b6dc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 26 Feb 2015 07:43:20 -0800 Subject: [PATCH 0397/1986] More updates for auto-failover logic --- cluster_library.c | 544 ++++++++++++++++++++++++++-------------------- cluster_library.h | 30 ++- common.h | 12 +- library.c | 2 + redis.c | 8 - redis_cluster.c | 5 + 6 files changed, 347 insertions(+), 254 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 4fecbd9471..cc40f37fdd 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -40,10 +40,6 @@ static void cluster_log(char *fmt, ...) fprintf(stderr, "%s\n", buffer); } -/* Direct handling of variant replies, in a hiredis like way. These methods - * are used for non userland facing commands, as well as passed through from - * them when the reply is just variant (e.g. eval) */ - /* Debug function to dump a clusterReply structure recursively */ static void dump_reply(clusterReply *reply, int indent) { smart_str buf = {0}; @@ -171,10 +167,28 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, } } +/* Return the socket for a slot and slave index */ +static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, + ulong slaveidx) +{ + redisClusterNode **node; + + /* Return the master if we're not looking for a slave */ + if (slaveidx == 0) { + return SLOT_SOCK(c, slot); + } + + /* Abort if we can't find this slave */ + if (!SLOT_SLAVES(c, slot) || zend_hash_index_find(SLOT_SLAVES(c,slot), + slaveidx, (void**)&node)==FAILURE) return NULL; + + /* Success, return the slave */ + return (*node)->sock; +} + /* Read the response from a cluster */ clusterReply *cluster_read_resp(redisCluster *c TSRMLS_DC) { - return cluster_read_sock_resp(SLOT_SOCK(c,c->reply_slot), c->reply_type, - c->reply_len TSRMLS_CC); + return cluster_read_sock_resp(c->cmd_sock,c->reply_type,c->reply_len TSRMLS_CC); } /* Read any sort of response from the socket, having already issued the @@ -229,9 +243,78 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, return r; } -/* Cluster key distribution helpers. For a small handlful of commands, we want +/* + * Helpers to send various 'control type commands to a specific node, e.g. + * MULTI, ASKING, READONLY, READWRITE, etc + */ + +/* Send a command to the specific socket and validate reply type */ +static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len, + REDIS_REPLY_TYPE type TSRMLS_DC) +{ + /* Send the command and validate the reply type */ + if (!CLUSTER_SEND_PAYLOAD(redis_sock,cmd,cmd_len) || + !CLUSTER_VALIDATE_REPLY_TYPE(redis_sock, type)) return -1; + + /* Success! */ + return 0; +} + +static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) { + return cluster_send_direct(redis_sock, RESP_ASKING_CMD, + sizeof(RESP_ASKING_CMD)-1, TYPE_LINE TSRMLS_CC); +} + +static int cluster_send_readonly(RedisSock *redis_sock TSRMLS_DC) { + int ret; + + /* We don't have to do anything if we're already in readonly mode */ + if (redis_sock->readonly) return 0; + + /* Return success if we can send it */ + ret = cluster_send_direct(redis_sock, RESP_READONLY_CMD, + sizeof(RESP_READONLY_CMD)-1, TYPE_LINE TSRMLS_CC); + + /* Flag this socket as READONLY if our command worked */ + redis_sock->readonly = !ret; + + /* Return the result of our send */ + return ret; +} + +static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { + if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_MULTI_CMD, + sizeof(RESP_MULTI_CMD)-1, TYPE_LINE TSRMLS_CC)==0) + { + c->cmd_sock->mode = MULTI; + return 0; + } + return -1; +} + +PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { + if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_EXEC_CMD, + sizeof(RESP_EXEC_CMD)-1, TYPE_MULTIBULK TSRMLS_CC)) + { + return c->reply_len; + } + return -1; +} + +PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { + if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_DISCARD_CMD, + sizeof(RESP_DISCARD_CMD)-1, TYPE_LINE TSRMLS_CC)) + { + return 0; + } + return -1; +} + +/* + * Cluster key distribution helpers. For a small handlful of commands, we want * to distribute them across 1-N nodes. These methods provide simple containers - * for the purposes of splitting keys/values in this way */ + * for the purposes of splitting keys/values in this way + * */ /* Free cluster distribution list inside a HashTable */ static void cluster_dist_free_ht(void *p) { @@ -312,7 +395,7 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, // Prefix our key and hash it key_free = redis_key_prefix(c->flags, &key, &key_len); - slot = cluster_hash_key(key, key_len); + slot = cluster_hash_key(key, key_len); // We can't do this if we don't fully understand the keyspace if(c->master[slot] == NULL) { @@ -553,14 +636,19 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, PHPAPI int cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) { + ulong index; + // Allocate our slaves hash table if we haven't yet if(!master->slaves) { ALLOC_HASHTABLE(master->slaves); zend_hash_init(master->slaves, 0, NULL, ht_free_slave, 0); + index = 1; + } else { + index = master->slaves->nNextFreeElement; } - return zend_hash_next_index_insert(master->slaves, (void*)&slave, - sizeof(redisClusterNode*), NULL)!=SUCCESS; + return zend_hash_index_update(master->slaves, index, (void*)&slave, + sizeof(redisClusterNode*), NULL) != SUCCESS; } /* Sanity check/validation for CLUSTER SLOTS command */ @@ -646,64 +734,32 @@ PHPAPI void cluster_free_node(redisClusterNode *node) { efree(node); } -/* When we're in an ASK redirection state, Redis Cluster wants us to send - * the command only after starting our query with ASKING, or it'll just - * bounce us back and forth until the slots have migrated */ -static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) -{ - char buf[255]; - - // Make sure we can send the request - if(redis_check_eof(redis_sock, 1 TSRMLS_CC) || - php_stream_write(redis_sock->stream, RESP_ASKING_CMD, - sizeof(RESP_ASKING_CMD)-1) != sizeof(RESP_ASKING_CMD)-1) - { - return -1; - } - - // Read our reply type - if((redis_check_eof(redis_sock, 1 TSRMLS_CC) == - 1) || - (php_stream_getc(redis_sock->stream) != TYPE_LINE)) { - return -1; - } - - // Consume the rest of our response - if(!php_stream_gets(redis_sock->stream, buf, sizeof(buf))) { - return -1; - } - - // Success - return 0; -} - -/* Get a RedisSock object from the host and port where we have been directed - * from an ASK response. We'll first see if we have connected to this node - * already, and return that. If not, we create it and add it to our nodes. */ -static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { +/* Get or create a redisClusterNode that corresponds to the asking redirection */ +static redisClusterNode *cluster_get_asking_node(redisCluster *c TSRMLS_DC) { redisClusterNode **ppNode; char key[1024]; int key_len; - // It'll be hashed as host:port in our nodes HashTable - key_len = snprintf(key, sizeof(key), "%s:%u", c->redir_host, - c->redir_port); + /* Hashed by host:port */ + key_len = snprintf(key, sizeof(key), "%s:%u", c->redir_host, c->redir_port); - // See if we've already attached to it - if(zend_hash_find(c->nodes, key, key_len+1, (void**)&ppNode)==SUCCESS) - { - return (*ppNode)->sock; + /* See if we've already attached to it */ + if (zend_hash_find(c->nodes, key, key_len+1, (void**)&ppNode) == SUCCESS) { + return *ppNode; } - // Create a redisClusterNode + /* This host:port is unknown to us, so add it */ *ppNode = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); - // Now add it to the nodes we have - zend_hash_update(c->nodes, key, key_len+1, (void*)ppNode, - sizeof(redisClusterNode*), NULL); + /* Return the node */ + return *ppNode; +} - // Return the RedisSock - return (*ppNode)->sock; +/* Get or create a node at the host:port we were asked to check, and return the + * redis_sock for it. */ +static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { + return cluster_get_asking_node(c TSRMLS_CC)->sock; } /* Initialize seeds */ @@ -800,13 +856,9 @@ cluster_map_keyspace(redisCluster *c TSRMLS_DC) { static int cluster_set_redirection(redisCluster* c, char *msg, int moved) { char *host, *port; - - // Move past MOVED or ASK - if(moved) { - msg += MOVED_LEN; - } else { - msg += ASK_LEN; - } + + /* Move past "MOVED" or "ASK */ + msg += moved ? MOVED_LEN : ASK_LEN; // We need a slot seperator if(!(host = strchr(msg, ' '))) return -1; @@ -837,8 +889,8 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) * * This function will return -1 on a critical error (e.g. parse/communication * error, 0 if no redirection was encountered, and 1 if the data was moved. */ -static int cluster_check_response(redisCluster *c, unsigned short slot, - REDIS_REPLY_TYPE *reply_type TSRMLS_DC) +static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type + TSRMLS_DC) { size_t sz; @@ -846,8 +898,8 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, CLUSTER_CLEAR_ERROR(c); CLUSTER_CLEAR_REPLY(c); - if(-1 == redis_check_eof(SLOT_SOCK(c,slot), 1 TSRMLS_CC) || - EOF == (*reply_type = php_stream_getc(SLOT_STREAM(c,slot)))) + if(-1 == redis_check_eof(c->cmd_sock, 1 TSRMLS_CC) || + EOF == (*reply_type = php_stream_getc(c->cmd_sock->stream))) { return -1; } @@ -858,7 +910,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, int moved; // Attempt to read the error - if(!php_stream_gets(SLOT_STREAM(c,slot), inbuf, sizeof(inbuf))) { + if(!php_stream_gets(c->cmd_sock->stream, inbuf, sizeof(inbuf))) { return -1; } @@ -879,7 +931,7 @@ static int cluster_check_response(redisCluster *c, unsigned short slot, } // Fetch the first line of our response from Redis. - if(redis_sock_gets(SLOT_SOCK(c,slot),c->line_reply,sizeof(c->line_reply), + if(redis_sock_gets(c->cmd_sock,c->line_reply,sizeof(c->line_reply), &sz TSRMLS_CC)<0) { return -1; @@ -910,36 +962,129 @@ PHPAPI void cluster_disconnect(redisCluster *c TSRMLS_DC) { } } -/* Attempt to write to a cluster node. If the node is NULL (e.g. it's been - * umapped, we keep falling back until we run out of nodes to try */ -static int cluster_sock_write(redisCluster *c, unsigned short slot, - const char *cmd, size_t sz, int direct TSRMLS_DC) +/* Fisher-Yates shuffle for integer array */ +static void fyshuffle(int *array, size_t len) { + int temp, n = len; + size_t r; + + /* Randomize */ + while (n > 1) { + r = ((int)((double)n-- * (rand() / (RAND_MAX+1.0)))); + temp = array[n]; + array[n] = array[r]; + array[r] = temp; + }; +} + +/* This method attempts to write our command at random to the master and any + * attached slaves, until we either successufly do so, or fail. */ +static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, + int nomaster TSRMLS_DC) { + int i, count=1, *nodes; RedisSock *redis_sock; + + /* Allocate enough memory for the master and all of our slaves */ + if (c->master[c->cmd_slot]->slaves) { + count += zend_hash_num_elements(c->master[c->cmd_slot]->slaves); + } + nodes = emalloc(sizeof(int)*count); + + /* Populate our array with the master and each of it's slaves, then + * randomize them, so we will pick from the master or some slave. */ + for (i = 0; i < count; i++) nodes[i] = i; + fyshuffle(nodes, count); + + /* Iterate through our nodes until we find one we can write to or fail */ + for (i = nomaster; i < count; i++) { + /* Get the slave for this index */ + redis_sock = cluster_slot_sock(c, c->cmd_slot, nodes[i]); + if (!redis_sock) continue; + + /* Connect to this node if we haven't already */ + CLUSTER_LAZY_CONNECT(redis_sock); + + /* If we're not on the master, attempt to send the READONLY commadn to + * this slave, and skip it if that fails */ + if (nodes[i] == 0 || redis_sock->readonly || + cluster_send_readonly(redis_sock TSRMLS_CC) == 0) + { + /* Attempt to send the command */ + if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz)) { + c->cmd_sock = redis_sock; + efree(nodes); + return 0; + } + } + } + + /* Clean up our shuffled array */ + efree(nodes); + + /* Couldn't send to the master or any slave */ + return -1; +} + +/* Attempt to write our command to the current c->cmd_sock socket. For write + * commands, we attempt to query the master for this slot, and in the event of + * a failure, try to query every remaining node for a redirection. + * + * If we're issuing a readonly command, we use one of three strategies, depending + * on our redisCluster->failover setting. + * + * REDIS_FAILOVER_NONE: + * The command is treated just like a write command, and will only be executed + * against the known master for this slot. + * REDIS_FAILOVER_ERROR: + * If we're unable to communicate with this slot's master, we attempt the query + * against any slaves (at random) that this master has. + * REDIS_FAILOVER_DISTRIBUTE: + * We pick at random from the master and any slaves it has. This option is + * used to load balance read queries against N slaves. + * + * Once we are able to find a node we can write to, we check for MOVED or + * ASKING redirection, such that the keyspace can be updated. +*/ +static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, + int direct TSRMLS_DC) +{ redisClusterNode **seed_node; + RedisSock *redis_sock; + int failover; - /* If we're not in ASK redirection, use the slot requested, otherwise send - * our ASKING command and use the asking slot. */ - if(c->redir_type != REDIR_ASK) { - redis_sock = SLOT_SOCK(c,slot); - } else { + /* First try the socket requested */ + redis_sock = c->cmd_sock; + + /* Readonly is irrelevant if we're not configured to failover */ + failover = c->readonly && c->failover != REDIS_FAILOVER_NONE ? + c->failover : REDIS_FAILOVER_NONE; + + /* If in ASK redirection, get/create the node for that host:port, otherwise + * just use the command socket. */ + if(c->redir_type == REDIR_ASK) { redis_sock = cluster_get_asking_sock(c TSRMLS_CC); if(cluster_send_asking(redis_sock TSRMLS_CC)<0) { return -1; } } - /* If the lazy_connect flag is still set, we've not actually connected to - * this node, so do that now. */ - CLUSTER_LAZY_CONNECT(redis_sock); - - /* First attempt to write it to the slot that's been requested */ - if(redis_sock && redis_sock->stream && - !redis_check_eof(redis_sock, 1 TSRMLS_CC) && - php_stream_write(redis_sock->stream, cmd, sz)==sz) - { - // We were able to write it - return slot; + /* Attempt to send our command payload to the cluster. If we're not set up + * to failover, just try the master. If we're configured to failover on + * error, try the master and then fall back to any slaves. When we're set + * up to distribute the commands, try to write to any node on this slot + * at random. */ + if (failover == REDIS_FAILOVER_NONE) { + /* Success if we can send our payload to the master */ + CLUSTER_LAZY_CONNECT(redis_sock); + if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz)) return 0; + } else if (failover == REDIS_FAILOVER_ERROR) { + /* Try the master, then fall back to any slaves we may have */ + CLUSTER_LAZY_CONNECT(redis_sock); + if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz) || + !cluster_dist_write(c, cmd, sz, 1 TSRMLS_CC)) return 0; + } else if (!cluster_dist_write(c, cmd, sz, 0 TSRMLS_CC)) { + /* We were able to write to a master or slave at random */ + return 0; } /* Don't fall back if direct communication with this slot is required. */ @@ -953,20 +1098,17 @@ static int cluster_sock_write(redisCluster *c, unsigned short slot, /* Grab node */ zend_hash_get_current_data(c->nodes, (void**)&seed_node); - /* Skip this node if it's the one that failed */ - if((*seed_node)->sock == redis_sock) continue; - - /* Skip slave nodes */ - if((*seed_node)->slave) continue; + /* Skip this node if it's the one that failed, or if it's a slave */ + if((*seed_node)->sock == redis_sock || (*seed_node)->slave) continue; + /* Connect to this node if we haven't already */ CLUSTER_LAZY_CONNECT((*seed_node)->sock); /* Attempt to write our request to this node */ - if(!redis_check_eof((*seed_node)->sock, 1 TSRMLS_CC) && - php_stream_write((*seed_node)->sock->stream, cmd, sz)==sz) - { - /* Just return the first slot we think this node handles */ - return (*seed_node)->slot; + if (CLUSTER_SEND_PAYLOAD((*seed_node)->sock, cmd, sz)) { + c->cmd_slot = (*seed_node)->slot; + c->cmd_sock = (*seed_node)->sock; + return 0; } } @@ -1038,44 +1180,6 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { node->slave = 0; } -/* Send EXEC to a specific slot */ -PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { - /* We have to be able to write this to the slot requested */ - if(cluster_sock_write(c, slot, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD)-1, 1 - TSRMLS_CC)==-1) - { - return -1; - } - - /* We have to get a proper response from the slot to continue */ - if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || - c->reply_type != TYPE_MULTIBULK) - { - return -1; - } - - // Return the number of multi-bulk replies - return c->reply_len; -} - -/* Send DISCARD to a specific slot */ -PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { - if(cluster_sock_write(c, slot, RESP_DISCARD_CMD, sizeof(RESP_DISCARD_CMD)-1, - 1 TSRMLS_CC)==-1) - { - return -1; - } - - if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || - c->reply_type != TYPE_LINE) - { - return -1; - } - - return 0; -} - - /* Abort any transaction in process, by sending DISCARD to any nodes that * have active transactions in progress. If we can't send DISCARD, we need * to disconnect as it would leave us in an undefined state. */ @@ -1102,27 +1206,6 @@ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { return 0; } -/* Send MULTI to a given slot and consume the response. If we can't send the - * command OR we get an error in our response, we have to fail. */ -static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { - /* We have to be able to communicate with the node we want */ - if(cluster_sock_write(c, slot, RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD)-1, 1 - TSRMLS_CC)==-1) - { - return -1; - } - - /* We have to get a proper response */ - if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || - c->reply_type != TYPE_LINE) - { - return -1; - } - - /* Success */ - return 0; -} - /* Iterate through our slots, looking for the host/port in question. This * should perform well enough as in almost all situations, a few or a few * dozen servers will map all the slots */ @@ -1148,21 +1231,20 @@ PHPAPI short cluster_find_slot(redisCluster *c, const char *host, PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC) { - // Try only this node - if(cluster_sock_write(c, slot, cmd, cmd_len, 1 TSRMLS_CC)==-1) { - return -1; - } - - // Check our response and verify the type unless passed in as TYPE_EOF - if(cluster_check_response(c, slot, &c->reply_type TSRMLS_CC)!=0 || - (rtype != TYPE_EOF && rtype != c->reply_type)) - { + /* Point our cluster to this slot and it's socket */ + c->cmd_slot = slot; + c->cmd_sock = SLOT_SOCK(c, slot); + + /* Try the slot */ + if(cluster_sock_write(c, cmd, cmd_len, 1 TSRMLS_CC)==-1) { return -1; } - // Update our reply slot - c->reply_slot = slot; + /* Check our response */ + if(cluster_check_response(c, &c->reply_type TSRMLS_CC)!=0 || + (rtype != TYPE_EOF && rtype != c->reply_type)) return -1; + /* Success */ return 0; } @@ -1174,60 +1256,59 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, int resp, timedout=0; long msstart; - /* Grab the current time in milliseconds */ + /* Set the slot we're operating against as well as it's socket. These can + * change during our request loop if we have a master failure and are + * configured to fall back to slave nodes, or if we have to fall back to + * a different slot due to no nodes serving this slot being reachable. */ + c->cmd_slot = slot; + c->cmd_sock = SLOT_SOCK(c, slot); + + /* Get the current time in milliseconds to handle any timeout */ msstart = mstime(); - /* Our main cluster request/reply loop. This loop runs until we're able - * to get a valid reply from a node, hit our "request" timeout, or encounter - * a CLUSTERDOWN state from Redis cluster. */ + /* Our main cluster request/reply loop. This loop runs until we're able to + * get a valid reply from a node, hit our "request" timeout, or enounter a + * CLUSTERDOWN state from Redis Cluster. */ do { - /* Send MULTI to the node if we haven't yet. */ - if(c->flags->mode == MULTI && SLOT_SOCK(c,slot)->mode != MULTI) { + /* Send MULTI to the socket if we're in MULTI mode but haven't yet */ + if (c->flags->mode == MULTI && CMD_SOCK(c)->mode != MULTI) { /* We have to fail if we can't send MULTI to the node */ - if(cluster_send_multi(c, slot TSRMLS_CC)==-1) { + if (cluster_send_multi(c, slot TSRMLS_CC) == -1) { zend_throw_exception(redis_cluster_exception_ce, - "Unable to enter MULTI mode on required slot", + "Unable to enter MULTI mode on requested slot", 0 TSRMLS_CC); return -1; } - - /* This node is now inside a transaction */ - SLOT_SOCK(c,slot)->mode = MULTI; } - /* Attempt to send the command to the slot requested */ - if((slot = cluster_sock_write(c, slot, cmd, cmd_len, 0 TSRMLS_CC))==-1) - { - /* We have no choice but to throw an exception. We can't communicate - * with any node at all. */ + /* Attempt to deliver our command to the node, and that failing, to any + * node until we find one that is available. */ + if (cluster_sock_write(c, cmd, cmd_len, 0 TSRMLS_CC) == -1) { + /* We have to abort, as no nodes are reachable */ zend_throw_exception(redis_cluster_exception_ce, "Can't communicate with any node in the cluster", 0 TSRMLS_CC); return -1; } - /* Check the response from the slot we ended up querying. */ - resp = cluster_check_response(c, slot, &c->reply_type TSRMLS_CC); + /* Now check the response from the node we queried. */ + resp = cluster_check_response(c, &c->reply_type TSRMLS_CC); /* Handle MOVED or ASKING redirection */ - if(resp == 1) { - /* If we get a MOVED response inside of a transaction, we have to - abort, because the transaction would be invalid. */ - if(c->flags->mode == MULTI) { - zend_throw_exception(redis_cluster_exception_ce, - "Can't process MULTI sequence when cluster is resharding", - 0 TSRMLS_CC); - return -1; - } - - // In case of a MOVED redirection, update our node mapping - if(c->redir_type == REDIR_MOVED) { - cluster_update_slot(c TSRMLS_CC); - } - slot = c->redir_slot; + if (resp == 1) { + /* Abort if we're in a transaction as it will be invalid */ + if (c->flags->mode == MULTI) { + zend_throw_exception(redis_cluster_exception_ce, + "Can't process MULTI sequence when cluster is resharding", + 0 TSRMLS_CC); + return -1; + } + + /* Update mapping if the data has MOVED */ + if (c->redir_type == REDIR_MOVED) cluster_update_slot(c TSRMLS_CC); } - /* If we didn't get a valid response see if we've now timed out */ + /* Figure out if we've timed out trying to read or write the data */ timedout = resp && c->waitms ? mstime() - msstart >= c->waitms : 0; } while(resp != 0 && !c->clusterdown && !timedout); @@ -1241,9 +1322,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, "Timed out attempting to find data in the correct node!", 0 TSRMLS_CC); } - // Inform the cluster where to read the rest of our response, - // and clear out redirection flag. - c->reply_slot = slot; + /* Clear redirection flag */ c->redir_type = REDIR_NONE; // Success, return the slot where data exists. @@ -1266,8 +1345,7 @@ PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, // Make sure we can read the response if(c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), - c->reply_len TSRMLS_CC))==NULL) + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC))==NULL) { if(c->flags->mode != MULTI) { RETURN_FALSE; @@ -1289,8 +1367,7 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Make sure we can read the response if(c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), - c->reply_len TSRMLS_CC))==NULL) + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC))==NULL) { CLUSTER_RETURN_FALSE(c); } @@ -1317,8 +1394,7 @@ PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Make sure we can read the response if(c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), - c->reply_len TSRMLS_CC))==NULL) + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC))==NULL) { CLUSTER_RETURN_FALSE(c); } @@ -1441,7 +1517,7 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, sctx->cb.no_separation = 0; /* We're in a subscribe loop */ - c->subscribed_slot = c->reply_slot; + c->subscribed_slot = c->cmd_slot; /* Multibulk response, {[pattern], type, channel, payload} */ while(1) { @@ -1700,9 +1776,7 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, array_init(z_result); // Call our specified callback - if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len, ctx TSRMLS_CC) - ==FAILURE) - { + if(cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC)==FAILURE) { zval_dtor(z_result); FREE_ZVAL(z_result); CLUSTER_RETURN_FALSE(c); @@ -1730,15 +1804,14 @@ PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } // Read the BULK size - if(cluster_check_response(c, c->reply_slot, &c->reply_type TSRMLS_CC),0 || + if(cluster_check_response(c, &c->reply_type TSRMLS_CC),0 || c->reply_type != TYPE_BULK) { return FAILURE; } // Read the iterator - if((pit = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), - c->reply_len TSRMLS_CC))==NULL) + if((pit = redis_sock_read_bulk_reply(c->cmd_sock,c->reply_len TSRMLS_CC))==NULL) { return FAILURE; } @@ -1748,7 +1821,7 @@ PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, efree(pit); // We'll need another MULTIBULK response for the payload - if(cluster_check_response(c, c->reply_slot, &c->reply_type TSRMLS_CC)<0) + if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) { return FAILURE; } @@ -1783,8 +1856,7 @@ PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, char *info; // Read our bulk response - if((info = redis_sock_read_bulk_reply(SLOT_SOCK(c,c->reply_slot), - c->reply_len TSRMLS_CC))==NULL) + if((info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC))==NULL) { CLUSTER_RETURN_FALSE(c); } @@ -1810,8 +1882,7 @@ PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, // Pull our next response if directed if(pull) { - if(cluster_check_response(c, c->reply_slot, &c->reply_type - TSRMLS_CC)<0) + if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) { return NULL; } @@ -1826,9 +1897,7 @@ PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, array_init(z_result); // Call our callback - if(cb(SLOT_SOCK(c,c->reply_slot), z_result, c->reply_len, NULL TSRMLS_CC) - ==FAILURE) - { + if(cb(c->cmd_sock, z_result, c->reply_len, NULL TSRMLS_CC)==FAILURE) { zval_dtor(z_result); FREE_ZVAL(z_result); return NULL; @@ -1846,13 +1915,18 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, clusterFoldItem *fi = c->multi_head; while(fi) { - if(cluster_check_response(c, fi->slot, &c->reply_type TSRMLS_CC)<0) { + /* Set the slot where we should look for responses. We don't allow + * failover inside a transaction, so it will be the master we have + * mapped. */ + c->cmd_slot = fi->slot; + c->cmd_sock = SLOT_SOCK(c, fi->slot); + + if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) { zval_dtor(c->multi_resp); efree(c->multi_resp); RETURN_FALSE; } - c->reply_slot = fi->slot; fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx); fi = fi->next; } @@ -1869,11 +1943,10 @@ PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; - // Protect against an invalid response type, -1 response length, and failure - // to consume the responses. + /* Protect against an invalid response type, -1 response length, and failure + * to consume the responses. */ short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || - mbulk_resp_loop(SLOT_SOCK(c,c->reply_slot), mctx->z_multi, - c->reply_len, NULL TSRMLS_CC)==FAILURE; + mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL TSRMLS_CC)==FAILURE; // If we had a failure, pad results with FALSE to indicate failure. Non // existant keys (e.g. for MGET will come back as NULL) @@ -2203,4 +2276,5 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, // Success! return SUCCESS; } + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index c4fa9883b8..457a96942c 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -21,6 +21,8 @@ #define RESP_READONLY_CMD "*1\r\n$8\r\nREADONLY\r\n" #define RESP_READWRITE_CMD "*1\r\n$9\r\nREADWRITE\r\n" +#define RESP_READONLY_CMD_LEN (sizeof(RESP_READONLY_CMD)-1) + /* MOVED/ASK comparison macros */ #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \ p[4]=='D' && p[5]==' ') @@ -33,10 +35,15 @@ /* Initial allocation size for key distribution container */ #define CLUSTER_KEYDIST_ALLOC 8 -/* Slot/RedisSock/RedisSock->stream macros */ +/* Macros to access nodes, sockets, and streams for a given slot */ #define SLOT(c,s) (c->master[s]) #define SLOT_SOCK(c,s) (SLOT(c,s)->sock) #define SLOT_STREAM(c,s) (SLOT_SOCK(c,s)->stream) +#define SLOT_SLAVES(c,s) (c->master[s]->slaves) + +/* Macros to access socket and stream for the node we're communicating with */ +#define CMD_SOCK(c) (c->cmd_sock) +#define CMD_STREAM(c) (c->cmd_sock->stream) /* Compare redirection slot information with what we have */ #define CLUSTER_REDIR_CMP(c) \ @@ -60,6 +67,16 @@ } \ c->clusterdown = 0; +/* Protected sending of data down the wire to a RedisSock->stream */ +#define CLUSTER_SEND_PAYLOAD(sock, buf, len) \ + (sock && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \ + php_stream_write(sock->stream, buf, len)==len) + +/* Macro to read our reply type character */ +#define CLUSTER_VALIDATE_REPLY_TYPE(sock, type) \ + (redis_check_eof(sock, 1 TSRMLS_CC) == 0 && \ + (php_stream_getc(sock->stream) == type)) + /* Reset our last single line reply buffer and length */ #define CLUSTER_CLEAR_REPLY(c) \ *c->line_reply = '\0'; c->reply_len = 0; @@ -196,13 +213,14 @@ typedef struct redisCluster { char *err; int err_len; - /* The slot where we should read replies */ - short reply_slot; + /* The slot our command is operating on, as well as it's socket */ + unsigned short cmd_slot; + RedisSock *cmd_sock; /* The slot where we're subscribed */ short subscribed_slot; - /* One RedisSock* struct for serialization and prefix information */ + /* One RedisSock struct for serialization and prefix information */ RedisSock *flags; /* The first line of our last reply, not including our reply type byte @@ -229,8 +247,8 @@ struct clusterFoldItem { /* Response processing callback */ cluster_cb callback; - /* The slot where this response was sent */ - short slot; + /* The actual socket where we send this request */ + unsigned short slot; /* Any context we need to send to our callback */ void *ctx; diff --git a/common.h b/common.h index 4f7f8c633d..124bf02d4c 100644 --- a/common.h +++ b/common.h @@ -95,14 +95,14 @@ typedef enum _PUBSUB_TYPE { #define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC) #define IF_ATOMIC() if(redis_sock->mode == ATOMIC) #define ELSE_IF_MULTI() else if(redis_sock->mode == MULTI) { \ - if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ + if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) { \ RETURN_ZVAL(getThis(), 1, 0);\ - } else {\ - RETURN_FALSE;\ - } \ + } else { \ + RETURN_FALSE; \ + } \ } -#define ELSE_IF_PIPELINE() else IF_PIPELINE() { \ +#define ELSE_IF_PIPELINE() else IF_PIPELINE() { \ RETURN_ZVAL(getThis(), 1, 0);\ } @@ -288,6 +288,8 @@ typedef struct { zend_bool lazy_connect; int scan; + + int readonly; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index 82fcaae6d4..7351c8b656 100644 --- a/library.c +++ b/library.c @@ -1513,6 +1513,8 @@ redis_sock_create(char *host, int host_len, unsigned short port, double timeout, redis_sock->err_len = 0; redis_sock->scan = REDIS_SCAN_NORETRY; + + redis_sock->readonly = 0; return redis_sock; } diff --git a/redis.c b/redis.c index 615d726f54..015cc817c1 100644 --- a/redis.c +++ b/redis.c @@ -539,11 +539,6 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } -/* Settings specific to RedisCluster */ -static void add_cluster_constants(zend_class_entry *ce) { - // TODO: Placeholder if we add constants -} - /** * PHP_MINIT_FUNCTION */ @@ -607,9 +602,6 @@ PHP_MINIT_FUNCTION(redis) add_class_constants(redis_ce, 0 TSRMLS_CC); add_class_constants(redis_cluster_ce, 1 TSRMLS_CC); - /* Add specific RedisCluster class constants */ - add_cluster_constants(redis_cluster_ce); - return SUCCESS; } diff --git a/redis_cluster.c b/redis_cluster.c index e45f10c451..0b95faa298 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -261,7 +261,12 @@ zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { zend_object_value retval; redisCluster *cluster; + struct timeval t1; + /* Seed random generator for failover */ + gettimeofday(&t1, NULL); + srand(t1.tv_usec * t1.tv_sec); + // Allocate our actual struct cluster = emalloc(sizeof(redisCluster)); memset(cluster, 0, sizeof(redisCluster)); From f7f1e9d600f0974baa3ce09e4e2f97b18198b897 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 26 Feb 2015 08:48:51 -0800 Subject: [PATCH 0398/1986] We need to process the whole line when sending direct mode commands --- cluster_library.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index cc40f37fdd..700e2c7479 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -252,9 +252,15 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len, REDIS_REPLY_TYPE type TSRMLS_DC) { - /* Send the command and validate the reply type */ + char buf[1024]; + + /* Connect to the socket if we aren't yet */ + CLUSTER_LAZY_CONNECT(redis_sock); + + /* Send our command, validate the reply type, and consume the first line */ if (!CLUSTER_SEND_PAYLOAD(redis_sock,cmd,cmd_len) || - !CLUSTER_VALIDATE_REPLY_TYPE(redis_sock, type)) return -1; + !CLUSTER_VALIDATE_REPLY_TYPE(redis_sock, type) || + !php_stream_gets(redis_sock->stream, buf, sizeof(buf))) return -1; /* Success! */ return 0; From 224155ef6b39e8952a62150b3a399b6b64c7f293 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 26 Feb 2015 10:48:11 -0800 Subject: [PATCH 0399/1986] Use cluster_send_slot for EXEC as we'll want to capture the MULTI-BULK len, which isn't handled properly by cluster_send_direct. We can go through the normal logic chain, as neither MULTI or EXEC are marked as "readonly" commands, and therefore will only be delivered to master nodes. --- cluster_library.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 700e2c7479..372d17c2cd 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -271,6 +271,9 @@ static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) { sizeof(RESP_ASKING_CMD)-1, TYPE_LINE TSRMLS_CC); } +/* Send READONLY to a specific RedisSock unless it's already flagged as being + * in READONLY mode. If we can send the command, we flag the socket as being + * in that mode. */ static int cluster_send_readonly(RedisSock *redis_sock TSRMLS_DC) { int ret; @@ -288,6 +291,7 @@ static int cluster_send_readonly(RedisSock *redis_sock TSRMLS_DC) { return ret; } +/* Send MULTI to a specific ReidsSock */ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD)-1, TYPE_LINE TSRMLS_CC)==0) @@ -298,13 +302,13 @@ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { return -1; } +/* Send EXEC to a given slot. We can use the normal command processing mechanism + * here because we know we'll only have sent MULTI to the master nodes. We can't + * failover inside a transaction, as we don't know if the transaction will only + * be readonly commands, or contain write commands as well */ PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { - if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_EXEC_CMD, - sizeof(RESP_EXEC_CMD)-1, TYPE_MULTIBULK TSRMLS_CC)) - { - return c->reply_len; - } - return -1; + return cluster_send_slot(c, slot, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD)-1, + TYPE_MULTIBULK TSRMLS_CC); } PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { From d2d6f0d36618d994a5dcb0c7154320987c4008ae Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 28 Feb 2015 09:06:45 -0800 Subject: [PATCH 0400/1986] Fix detection of "before" and "after" --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 5bb1ed39d1..2be1f6c57b 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1779,7 +1779,7 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Validate position - if(strncasecmp(pos, "after", 5)!=0 && strncasecmp(pos, "before", 6)==0) { + if(strncasecmp(pos, "after", 5) && strncasecmp(pos, "before", 6)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Position must be either 'BEFORE' or 'AFTER'"); return FAILURE; From 479487e66cf170d3ab5bcddd13fe9712ef611ef4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 28 Feb 2015 09:57:12 -0800 Subject: [PATCH 0401/1986] Initial commit of a simple script to create a redis cluster --- .gitignore | 2 +- tests/make-cluster.sh | 136 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100755 tests/make-cluster.sh diff --git a/.gitignore b/.gitignore index 10c2636c51..046241a943 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ config.* *.o install-sh libtool -*.sh +./*.sh configure* *.lo build* diff --git a/tests/make-cluster.sh b/tests/make-cluster.sh new file mode 100755 index 0000000000..64061486b7 --- /dev/null +++ b/tests/make-cluster.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +# make-cluster.sh +# This is a simple script used to automatically spin up a Redis cluster instance +# simplifying the process of running unit tests. +# +# Usage: +# ./make-cluster.sh start +# ./make-cluster.sh stop +# + +BASEDIR=`dirname $@` +NODEDIR=$BASEDIR/nodes +MAPFILE=$NODEDIR/nodemap + +# Nodes, replicas, ports, etc. Change if you want different values +NODES=12 +REPLICAS=3 +START_PORT=7000 +END_PORT=`expr $START_PORT + $NODES` + +# Helper to determine if we have an executable +checkExe() { + if ! hash $1 > /dev/null 2>&1; then + echo "Error: Must have $1 on the path!" + exit 1 + fi +} + +# Run a command and output what we're running +verboseRun() { + echo "Running: $@" + $@ +} + +# Spawn a specific redis instance, cluster enabled +spawnNode() { + # Attempt to spawn the node + verboseRun redis-server --cluster-enabled yes --dir $NODEDIR --port $PORT \ + --cluster-config-file node-$PORT.conf --daemonize yes --save \'\' \ + --bind '127.0.0.1' --dbfilename node-$PORT.rdb + + # Abort if we can't spin this instance + if [ $? -ne 0 ]; then + echo "Error: Can't spawn node at port $PORT." + exit 1 + fi +} + +# Spawn nodes from start to end port +spawnNodes() { + for PORT in `seq $START_PORT $END_PORT`; do + # Attempt to spawn the node + spawnNode $PORT + + # Add this host:port to our nodemap so the tests can get seeds + echo "127.0.0.1:$PORT" >> $MAPFILE + done +} + +# Check to see if any nodes are running +checkNodes() { + echo -n "Checking port availability " + + for PORT in `seq $START_PORT $END_PORT`; do + redis-cli -p $PORT ping > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "FAIL" + echo "Error: There appears to be an instance running at port $PORT" + exit 1 + fi + done + + echo "OK" +} + +# Create our 'node' directory if it doesn't exist and clean out any previous +# configuration files from a previous run. +cleanConfigInfo() { + verboseRun mkdir -p $NODEDIR + verboseRun rm -f $NODEDIR/* +} + +# Initialize our cluster with redis-trib.rb +initCluster() { + TRIBARGS="" + for PORT in `seq $START_PORT $END_PORT`; do + TRIBARGS="$TRIBARGS 127.0.0.1:$PORT" + done + + verboseRun redis-trib.rb create --replicas $REPLICAS $TRIBARGS + + if [ $? -ne 0 ]; then + echo "Error: Couldn't create cluster!" + exit 1 + fi +} + +# Attempt to spin up our cluster +startCluster() { + # Make sure none of these nodes are already running + checkNodes + + # Clean out node configuration, etc + cleanConfigInfo + + # Attempt to spawn the nodes + spawnNodes + + # Attempt to initialize the cluster + initCluster +} + +# Shut down nodes in our cluster +stopCluster() { + for PORT in `seq $START_PORT $END_PORT`; do + verboseRun redis-cli -p $PORT SHUTDOWN NOSAVE > /dev/null 2>&1 + done +} + +# Make sure we have redis-server and redis-trib.rb on the path +checkExe redis-server +checkExe redis-trib.rb + +# Main entry point to start or stop/kill a cluster +case "$1" in + start) + startCluster + ;; + stop) + stopCluster + ;; + *) + echo "Usage $0 [start|stop]" + ;; +esac From b1ee9be873111a894dbff13bcd780fe792d1fca0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 28 Feb 2015 09:58:25 -0800 Subject: [PATCH 0402/1986] We don't need to protect against CROSSLOT if we're not hashing --- redis_commands.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 2be1f6c57b..5fa7c64104 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1048,14 +1048,16 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); - // Protect against CROSSLOT errors - if(kslot == -1) { - kslot = cluster_hash_key(key, key_len); - } else if(cluster_hash_key(key,key_len)!=kslot) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Not all keys hash to the same slot"); - efree(z_args); - return FAILURE; + /* Protect against CROSSSLOT errors if we've got a slot */ + if (slot) { + if( kslot == -1) { + kslot = cluster_hash_key(key, key_len); + } else if(cluster_hash_key(key,key_len)!=kslot) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Not all keys hash to the same slot"); + efree(z_args); + return FAILURE; + } } // Append this key From 79cb6a8905ad8e2d2900e1b51db5fdaf37129632 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 28 Feb 2015 12:38:09 -0800 Subject: [PATCH 0403/1986] Initial commit reworking unit tests This commit reworks the unit tests such that all three classes (Redis, RedisArray, and RedisCluster) can be tested using the TestRedis.php script. Now, when running TestRedis.php an option can be passed for which part of phpredis we would like to test (defaults to Redis). --- tests/{array-tests.php => RedisArrayTest.php} | 4 +- tests/RedisClusterTest.php | 27 + tests/RedisTest.php | 4882 ++++++++++++++++ tests/TestRedis.php | 4969 +---------------- tests/{test.php => TestSuite.php} | 0 tests/memory.php | 39 - 6 files changed, 4940 insertions(+), 4981 deletions(-) rename tests/{array-tests.php => RedisArrayTest.php} (99%) create mode 100644 tests/RedisClusterTest.php create mode 100644 tests/RedisTest.php rename tests/{test.php => TestSuite.php} (100%) delete mode 100644 tests/memory.php diff --git a/tests/array-tests.php b/tests/RedisArrayTest.php similarity index 99% rename from tests/array-tests.php rename to tests/RedisArrayTest.php index 0b85ed4922..2670e2ddf0 100644 --- a/tests/array-tests.php +++ b/tests/RedisArrayTest.php @@ -1,7 +1,7 @@ _arr_node_map = array_filter(file_get_contents('nodes/nodemap')); + } + + /* Override newInstance as we want a RedisCluster object */ + protected function newInstance() { + return new RedisCluster(NULL, $this->_arr_node_map); + } +} diff --git a/tests/RedisTest.php b/tests/RedisTest.php new file mode 100644 index 0000000000..e92fb71769 --- /dev/null +++ b/tests/RedisTest.php @@ -0,0 +1,4882 @@ +redis = $this->newInstance(); + $info = $this->redis->info(); + $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); + } + + protected function newInstance() { + $r = new Redis(); + $r->connect(self::HOST, self::PORT); + + if(self::AUTH) { + $this->assertTrue($r->auth(self::AUTH)); + } + return $r; + } + + public function tearDown() + { + if($this->redis) { + $this->redis->close(); + } + // unset($this->redis); + } + + public function reset() + { + $this->setUp(); + $this->tearDown(); + } + + public function testMinimumVersion() + { + // Minimum server version required for tests + $this->assertTrue(version_compare($this->version, "2.4.0", "ge")); + } + + public function testPing() + { + + $this->assertEquals('+PONG', $this->redis->ping()); + + $count = 1000; + while($count --) { + $this->assertEquals('+PONG', $this->redis->ping()); + } + } + + public function testPipelinePublish() { + + $ret = $this->redis->pipeline() + ->publish('chan', 'msg') + ->exec(); + + $this->assertTrue(is_array($ret) && count($ret) === 1 && $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", "lt")) { + $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", Array($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(Array($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->delete('key'); + $this->assertTrue(0 === $this->redis->getBit('key', 0)); + $this->assertTrue(FALSE === $this->redis->getBit('key', -1)); + $this->assertTrue(0 === $this->redis->getBit('key', 100000)); + + $this->redis->set('key', "\xff"); + for($i = 0; $i < 8; $i++) { + $this->assertTrue(1 === $this->redis->getBit('key', $i)); + } + $this->assertTrue(0 === $this->redis->getBit('key', 8)); + + // change bit 0 + $this->assertTrue(1 === $this->redis->setBit('key', 0, 0)); + $this->assertTrue(0 === $this->redis->setBit('key', 0, 0)); + $this->assertTrue(0 === $this->redis->getBit('key', 0)); + $this->assertTrue("\x7f" === $this->redis->get('key')); + + // change bit 1 + $this->assertTrue(1 === $this->redis->setBit('key', 1, 0)); + $this->assertTrue(0 === $this->redis->setBit('key', 1, 0)); + $this->assertTrue(0 === $this->redis->getBit('key', 1)); + $this->assertTrue("\x3f" === $this->redis->get('key')); + + // change bit > 1 + $this->assertTrue(1 === $this->redis->setBit('key', 2, 0)); + $this->assertTrue(0 === $this->redis->setBit('key', 2, 0)); + $this->assertTrue(0 === $this->redis->getBit('key', 2)); + $this->assertTrue("\x1f" === $this->redis->get('key')); + + // values above 1 are changed to 1 but don't overflow on bits to the right. + $this->assertTrue(0 === $this->redis->setBit('key', 0, 0xff)); + $this->assertTrue("\x9f" === $this->redis->get('key')); + + // Verify valid offset ranges + $this->assertFalse($this->redis->getBit('key', -1)); + $this->assertTrue($this->redis->getBit('key', 4294967295)); + } + + public function testBitPos() { + if(version_compare($this->version, "2.8.7", "lt")) { + $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', 1, 0), 8); + $this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 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->delete('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->delete('key'); + $this->redis->delete('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->delete('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->delete('key'); + $data = gzcompress('value1'); + $this->assertEquals(True, $this->redis->set('key', $data)); + $this->assertEquals('value1', gzuncompress($this->redis->get('key'))); + + $this->redis->delete('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', 'lt')) { + $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); + + /* 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',Array('nx'))); + $this->assertEquals($this->redis->get('foo'), 'bar'); + $this->assertFalse($this->redis->set('foo','bar',Array('nx'))); + + /* Set if exists */ + $this->assertTrue($this->redis->set('foo','bar',Array('xx'))); + $this->assertEquals($this->redis->get('foo'), 'bar'); + $this->redis->del('foo'); + $this->assertFalse($this->redis->set('foo','bar',Array('xx'))); + + /* Set with a TTL */ + $this->assertTrue($this->redis->set('foo','bar',Array('ex'=>100))); + $this->assertEquals($this->redis->ttl('foo'), 100); + + /* Set with a PTTL */ + $this->assertTrue($this->redis->set('foo','bar',Array('px'=>100000))); + $this->assertTrue(100000 - $this->redis->pttl('foo') < 1000); + + /* Set if exists, with a TTL */ + $this->assertTrue($this->redis->set('foo','bar',Array('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', Array('nx', 'ex'=>110))); + $this->assertEquals($this->redis->ttl('foo'), 110); + $this->assertEquals($this->redis->get('foo'), 'bar'); + $this->assertFalse($this->redis->set('foo','bar', Array('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', Array('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); + } + + public function testGetSet() { + $this->redis->delete('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->assertTrue($this->redis->exists($k)); + } + } + + public function testRename() { + + // strings + $this->redis->delete('key0'); + $this->redis->set('key0', 'val0'); + $this->redis->renameKey('key0', 'key1'); + $this->assertTrue($this->redis->get('key0') === FALSE); + $this->assertTrue($this->redis->get('key1') === 'val0'); + + + // lists + $this->redis->delete('key0'); + $this->redis->lPush('key0', 'val0'); + $this->redis->lPush('key0', 'val1'); + $this->redis->renameKey('key0', 'key1'); + $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); + $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1', 'val0')); + + // variadic + $this->redis->delete('key0'); + $this->assertTrue(3 === $this->redis->lPush('key0', 'val0', 'val1', 'val2')); + $this->assertTrue(array('val2', 'val1', 'val0') === $this->redis->lrange('key0', 0, -1)); + + $this->redis->delete('key0'); + $this->assertTrue(3 === $this->redis->rPush('key0', 'val0', 'val1', 'val2')); + $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->lrange('key0', 0, -1)); + } + + public function testRenameNx() { + + // strings + $this->redis->delete('key0', 'key1'); + $this->redis->set('key0', 'val0'); + $this->redis->set('key1', 'val1'); + $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); + $this->assertTrue($this->redis->get('key0') === 'val0'); + $this->assertTrue($this->redis->get('key1') === 'val1'); + + // lists + $this->redis->delete('key0'); + $this->redis->delete('key1'); + $this->redis->lPush('key0', 'val0'); + $this->redis->lPush('key0', 'val1'); + $this->redis->lPush('key1', 'val1-0'); + $this->redis->lPush('key1', 'val1-1'); + $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); + $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array('val1', 'val0')); + $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1-1', 'val1-0')); + + $this->redis->delete('key2'); + $this->assertTrue($this->redis->renameNx('key0', 'key2') === TRUE); + $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); + $this->assertTrue($this->redis->lGetRange('key2', 0, -1) === array('val1', 'val0')); + + } + + public function testMultiple() { + + $this->redis->delete('k1'); + $this->redis->delete('k2'); + $this->redis->delete('k3'); + + $this->redis->set('k1', 'v1'); + $this->redis->set('k2', 'v2'); + $this->redis->set('k3', 'v3'); + $this->redis->set(1, 'test'); + + $this->assertEquals(array('v1'), $this->redis->getMultiple(array('k1'))); + $this->assertEquals(array('v1', 'v3', false), $this->redis->getMultiple(array('k1', 'k3', 'NoKey'))); + $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + + $this->redis->set('k5', '$1111111111'); + $this->assertEquals(array(0 => '$1111111111'), $this->redis->getMultiple(array('k5'))); + + $this->assertEquals(array(0 => 'test'), $this->redis->getMultiple(array(1))); // non-string + } + + public function testMultipleBin() { + + $this->redis->delete('k1'); + $this->redis->delete('k2'); + $this->redis->delete('k3'); + + $this->redis->set('k1', gzcompress('v1')); + $this->redis->set('k2', gzcompress('v2')); + $this->redis->set('k3', gzcompress('v3')); + + $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + + } + + public function testSetTimeout() { + + $this->redis->delete('key'); + $this->redis->set('key', 'value'); + $this->assertEquals('value', $this->redis->get('key')); + $this->redis->setTimeout('key', 1); + $this->assertEquals('value', $this->redis->get('key')); + sleep(2); + $this->assertEquals(False, $this->redis->get('key')); + } + + public function testExpireAt() { + + $this->redis->delete('key'); + $this->redis->set('key', 'value'); + $now = time(NULL); + $this->redis->expireAt('key', $now + 1); + $this->assertEquals('value', $this->redis->get('key')); + sleep(2); + $this->assertEquals(FALSE, $this->redis->get('key')); + } + + public function testSetEx() { + + $this->redis->delete('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 testSetNX() { + + $this->redis->set('key', 42); + $this->assertTrue($this->redis->setnx('key', 'err') === FALSE); + $this->assertTrue($this->redis->get('key') === '42'); + + $this->redis->delete('key'); + $this->assertTrue($this->redis->setnx('key', '42') === TRUE); + $this->assertTrue($this->redis->get('key') === '42'); + } + + public function testExpireAtWithLong() { + $longExpiryTimeExceedingInt = 3153600000; + $this->redis->delete('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->incr('key', 3); + $this->assertEquals(5, (int)$this->redis->get('key')); + + $this->redis->incrBy('key', 3); + $this->assertEquals(8, (int)$this->redis->get('key')); + + $this->redis->incrBy('key', 1); + $this->assertEquals(9, (int)$this->redis->get('key')); + + $this->redis->incrBy('key', -1); + $this->assertEquals(8, (int)$this->redis->get('key')); + + $this->redis->delete('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')); + } + + public function testIncrByFloat() + { + // incrbyfloat is new in 2.6.0 + if (version_compare($this->version, "2.5.0", "lt")) { + $this->markTestSkipped(); + } + + $this->redis->delete('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.8', $this->redis->get('key')); + $this->redis->setOption(Redis::OPT_PREFIX, ''); + $this->assertTrue($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->decr('key', 2); + $this->assertEquals(1, (int)$this->redis->get('key')); + + $this->redis->decr('key', 2); + $this->assertEquals(-1, (int)$this->redis->get('key')); + + $this->redis->decrBy('key', 2); + $this->assertEquals(-3, (int)$this->redis->get('key')); + + $this->redis->decrBy('key', 1); + $this->assertEquals(-4, (int)$this->redis->get('key')); + + $this->redis->decr('key', -10); + $this->assertEquals(6, (int)$this->redis->get('key')); + } + + public function testExists() + { + + $this->redis->delete('key'); + $this->assertFalse($this->redis->exists('key')); + $this->redis->set('key', 'val'); + $this->assertEquals(True, $this->redis->exists('key')); + } + + public function testGetKeys() + { + + $pattern = 'getKeys-test-'; + for($i = 1; $i < 10; $i++) { + $this->redis->set($pattern.$i, $i); + } + $this->redis->delete($pattern.'3'); + $keys = $this->redis->getKeys($pattern.'*'); + + $this->redis->set($pattern.'3', 'something'); + + $keys2 = $this->redis->getKeys($pattern.'*'); + + $this->assertEquals((count($keys) + 1), count($keys2)); + + // empty array when no key matches + $this->assertEquals(array(), $this->redis->getKeys(rand().rand().rand().'*')); + } + + public function testDelete() + { + $key = 'key' . rand(); + $this->redis->set($key, 'val'); + $this->assertEquals('val', $this->redis->get($key)); + $this->assertEquals(1, $this->redis->delete($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->delete('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->delete('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->delete('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->delete(array('x', 'y'))); + + } + + public function testType() + { + // 0 => none, (key didn't exist) + // 1=> string, + // 2 => set, + // 3 => list, + // 4 => zset, + // 5 => hash + + // 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->delete('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->delete(123); + $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); + $this->assertTrue(array('val0') === $this->redis->sMembers(123)); + + // zset + $this->redis->delete('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->delete('keyHash'); + $this->redis->hSet('keyHash', 'key0', 'val0'); + $this->redis->hSet('keyHash', 'key1', 'val1'); + $this->assertEquals(Redis::REDIS_HASH, $this->redis->type('keyHash')); + + //None + $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->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', 0, 3) === 'This'); + $this->assertTrue($this->redis->getRange('key', -6, -1) === 'string'); + $this->assertTrue($this->redis->getRange('key', -6, 100000) === '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->delete('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->delete('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->delete('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->delete('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->delete('list'); + $this->redis->lPush('list', 'val1'); + $this->redis->lPush('list', 'val2'); + $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val2')); + $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val1')); + + $this->redis->delete('list'); + $this->redis->lPush('list', 'val1'); + $this->redis->lPush('list', 'val2'); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val1')); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val2')); + + // blocking blpop, brpop + $this->redis->delete('list'); + $this->assertTrue($this->redis->blPop(array('list'), 1) === array()); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array()); + + // TODO: fix this broken test. +// $this->redis->delete('list'); +// $params = array( +// 0 => array("pipe", "r"), +// 1 => array("pipe", "w"), +// 2 => array("file", "/dev/null", "w") +// ); +// if(function_exists('proc_open')) { +// $env = array('PHPREDIS_key' =>'list', 'PHPREDIS_value' => 'value'); +// $process = proc_open('php', $params, $pipes, '/tmp', $env); +// +// if (is_resource($process)) { +// fwrite($pipes[0], 'connect("'.self::HOST.'", '.self::PORT.'); +// if("'.addslashes(self::AUTH).'") { +// $r->auth("'.addslashes(self::AUTH).'"); +// } +// $r->lPush($_ENV["PHPREDIS_key"], $_ENV["PHPREDIS_value"]); +// ?' . '>'); +// +// fclose($pipes[0]); +// fclose($pipes[1]); +// $re = proc_close($process); +// +// $this->assertTrue($this->redis->blPop(array('list'), 5) === array("list", "value")); +// } +// } + + } + + public function testlSize() + { + + $this->redis->delete('list'); + + $this->redis->lPush('list', 'val'); + $this->assertEquals(1, $this->redis->lSize('list')); + + $this->redis->lPush('list', 'val2'); + $this->assertEquals(2, $this->redis->lSize('list')); + + $this->assertEquals('val2', $this->redis->lPop('list')); + $this->assertEquals(1, $this->redis->lSize('list')); + + $this->assertEquals('val', $this->redis->lPop('list')); + $this->assertEquals(0, $this->redis->lSize('list')); + + $this->assertEquals(FALSE, $this->redis->lPop('list')); + $this->assertEquals(0, $this->redis->lSize('list')); // empty returns 0 + + $this->redis->delete('list'); + $this->assertEquals(0, $this->redis->lSize('list')); // non-existent returns 0 + + $this->redis->set('list', 'actually not a list'); + $this->assertEquals(FALSE, $this->redis->lSize('list'));// not a list returns FALSE + } + + //lInsert, lPopx, rPopx + public function testlPopx() { + //test lPushx/rPushx + $this->redis->delete('keyNotExists'); + $this->assertTrue($this->redis->lPushx('keyNotExists', 'value') === 0); + $this->assertTrue($this->redis->rPushx('keyNotExists', 'value') === 0); + + $this->redis->delete('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->lGetRange('key', 0, -1) === array('val1', 'val0', 'val2')); + + //test linsert + $this->redis->delete('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->lGetRange('key', 0, -1) === array('val2', 'val0', 'val1')); + } + + // ltrim, lsize, lpop + public function testlistTrim() + { + + $this->redis->delete('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->listTrim('list', 0, 2)); + $this->assertEquals(3, $this->redis->lSize('list')); + + $this->redis->listTrim('list', 0, 0); + $this->assertEquals(1, $this->redis->lSize('list')); + $this->assertEquals('val4', $this->redis->lPop('list')); + + $this->assertEquals(TRUE, $this->redis->listTrim('list', 10, 10000)); + $this->assertEquals(TRUE, $this->redis->listTrim('list', 10000, 10)); + + // test invalid type + $this->redis->set('list', 'not a list...'); + $this->assertEquals(FALSE, $this->redis->listTrim('list', 0, 2)); + + } + + 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->delete('person:id'); + foreach(array(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(array('1','2','3'), $this->redis->sortAsc('some-item')); + $this->assertEquals(array('3','2','1'), $this->redis->sortDesc('some-item')); + $this->assertEquals(array('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(); + + $this->assertTrue(FALSE === $this->redis->sortAsc(NULL)); + + // sort by age and get IDs + $byAgeAsc = array('3','1','2','4'); + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*')); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'sort' => 'asc'))); + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL)); // check that NULL works. + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sort('person:id', array('sort' => 'asc'))); + + // sort by age and get names + $byAgeAsc = array('Carol','Alice','Bob','Dave'); + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*')); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'))); + + $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 2)); + $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 2), 'sort' => 'asc'))); + + $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); + $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(1, 2), 'sort' => 'asc'))); + $this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 4)))); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, "4")))); // with strings + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array("0", 4)))); + $this->assertEquals(array(), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. + + // sort by salary and get ages + $agesBySalaryAsc = array('34', '27', '25', '41'); + $this->assertEquals($agesBySalaryAsc, $this->redis->sortAsc('person:id', 'person:salary_*', 'person:age_*')); + $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'))); + + $agesAndSalaries = $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => array('person:age_*', 'person:salary_*'), 'sort' => 'asc')); + $this->assertEquals(array('34', '2000', '27', '2500', '25', '2800', '41', '3100'), $agesAndSalaries); + + + // sort non-alpha doesn't change all-string lists + // list → [ghi, def, abc] + $list = array('abc', 'def', 'ghi'); + $this->redis->delete('list'); + foreach($list as $i) { + $this->redis->lPush('list', $i); + } + + // SORT list → [ghi, def, abc] + if (version_compare($this->version, "2.5.0", "lt")) { + $this->assertEquals(array_reverse($list), $this->redis->sortAsc('list')); + $this->assertEquals(array_reverse($list), $this->redis->sort('list', array('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->sortAscAlpha('list')); + $this->assertEquals($list, $this->redis->sort('list', array('sort' => 'asc', 'alpha' => TRUE))); + } + + public function testSortDesc() { + + $this->setupSort(); + + // sort by age and get IDs + $byAgeDesc = array('4','2','1','3'); + $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*')); + + // sort by age and get names + $byAgeDesc = array('Dave', 'Bob', 'Alice', 'Carol'); + $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*')); + + $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 0, 2)); + $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 1, 2)); + + // sort by salary and get ages + $agesBySalaryDesc = array('41', '25', '27', '34'); + $this->assertEquals($agesBySalaryDesc, $this->redis->sortDesc('person:id', 'person:salary_*', 'person:age_*')); + + // sort non-alpha doesn't change all-string lists + $list = array('def', 'abc', 'ghi'); + $this->redis->delete('list'); + foreach($list as $i) { + $this->redis->lPush('list', $i); + } + + // SORT list → [ghi, abc, def] + if (version_compare($this->version, "2.5.0", "lt")) { + $this->assertEquals(array_reverse($list), $this->redis->sortDesc('list')); + } 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(array('ghi', 'def', 'abc'), $this->redis->sortDescAlpha('list')); + } + + // LINDEX + public function testlGet() { + + $this->redis->delete('list'); + + $this->redis->lPush('list', 'val'); + $this->redis->lPush('list', 'val2'); + $this->redis->lPush('list', 'val3'); + + $this->assertEquals('val3', $this->redis->lGet('list', 0)); + $this->assertEquals('val2', $this->redis->lGet('list', 1)); + $this->assertEquals('val', $this->redis->lGet('list', 2)); + $this->assertEquals('val', $this->redis->lGet('list', -1)); + $this->assertEquals('val2', $this->redis->lGet('list', -2)); + $this->assertEquals('val3', $this->redis->lGet('list', -3)); + $this->assertEquals(FALSE, $this->redis->lGet('list', -4)); + + $this->redis->rPush('list', 'val4'); + $this->assertEquals('val4', $this->redis->lGet('list', 3)); + $this->assertEquals('val4', $this->redis->lGet('list', -1)); + } + + // lRem testing + public function testlRemove() { + $this->redis->delete('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->lRemove('list', 'b', 2); + // ['c', 'c', 'c', 'a'] + $this->assertEquals(2, $return); + $this->assertEquals('c', $this->redis->lGET('list', 0)); + $this->assertEquals('c', $this->redis->lGET('list', 1)); + $this->assertEquals('c', $this->redis->lGET('list', 2)); + $this->assertEquals('a', $this->redis->lGET('list', 3)); + + $this->redis->delete('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->lRemove('list', 'c', -2); + // ['c', 'b', 'b', 'a'] + $this->assertEquals(2, $return); + $this->assertEquals('c', $this->redis->lGET('list', 0)); + $this->assertEquals('b', $this->redis->lGET('list', 1)); + $this->assertEquals('b', $this->redis->lGET('list', 2)); + $this->assertEquals('a', $this->redis->lGET('list', 3)); + + // remove each element + $this->assertEquals(1, $this->redis->lRemove('list', 'a', 0)); + $this->assertEquals(0, $this->redis->lRemove('list', 'x', 0)); + $this->assertEquals(2, $this->redis->lRemove('list', 'b', 0)); + $this->assertEquals(1, $this->redis->lRemove('list', 'c', 0)); + $this->assertEquals(FALSE, $this->redis->get('list')); + + $this->redis->set('list', 'actually not a list'); + $this->assertEquals(FALSE, $this->redis->lRemove('list', 'x')); + + } + + public function testsAdd() + { + $this->redis->delete('set'); + + $this->assertEquals(1, $this->redis->sAdd('set', 'val')); + $this->assertEquals(0, $this->redis->sAdd('set', 'val')); + + $this->assertTrue($this->redis->sContains('set', 'val')); + $this->assertFalse($this->redis->sContains('set', 'val2')); + + $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); + + $this->assertTrue($this->redis->sContains('set', 'val2')); + } + public function testsSize() + { + $this->redis->delete('set'); + + $this->assertEquals(1, $this->redis->sAdd('set', 'val')); + + $this->assertEquals(1, $this->redis->sSize('set')); + + $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); + + $this->assertEquals(2, $this->redis->sSize('set')); + } + + public function testsRemove() + { + $this->redis->delete('set'); + + $this->redis->sAdd('set', 'val'); + $this->redis->sAdd('set', 'val2'); + + $this->redis->sRemove('set', 'val'); + + $this->assertEquals(1, $this->redis->sSize('set')); + + $this->redis->sRemove('set', 'val2'); + + $this->assertEquals(0, $this->redis->sSize('set')); + } + + public function testsMove() + { + $this->redis->delete('set0'); + $this->redis->delete('set1'); + + $this->redis->sAdd('set0', 'val'); + $this->redis->sAdd('set0', 'val2'); + + $this->assertTrue($this->redis->sMove('set0', 'set1', 'val')); + $this->assertFalse($this->redis->sMove('set0', 'set1', 'val')); + $this->assertFalse($this->redis->sMove('set0', 'set1', 'val-what')); + + $this->assertEquals(1, $this->redis->sSize('set0')); + $this->assertEquals(1, $this->redis->sSize('set1')); + + $this->assertEquals(array('val2'), $this->redis->sGetMembers('set0')); + $this->assertEquals(array('val'), $this->redis->sGetMembers('set1')); + } + + public function testsPop() + { + $this->redis->delete('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(1 === $this->redis->sSize('set0')); + $this->assertTrue($v0 === 'val' || $v0 === 'val2'); + $v1 = $this->redis->sPop('set0'); + $this->assertTrue(0 === $this->redis->sSize('set0')); + $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); + + $this->assertTrue($this->redis->sPop('set0') === FALSE); + } + + public function testsRandMember() { + $this->redis->delete('set0'); + $this->assertTrue($this->redis->sRandMember('set0') === FALSE); + + $this->redis->sAdd('set0', 'val'); + $this->redis->sAdd('set0', 'val2'); + + $got = array(); + while(true) { + $v = $this->redis->sRandMember('set0'); + $this->assertTrue(2 === $this->redis->sSize('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->delete('set0'); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::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_SERIALIZER, Redis::SERIALIZER_NONE); + } + + public function testSRandMemberWithCount() { + // Make sure the set is nuked + $this->redis->delete('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 + // + + $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 testsContains() + { + $this->redis->delete('set'); + + $this->redis->sAdd('set', 'val'); + + $this->assertTrue($this->redis->sContains('set', 'val')); + $this->assertFalse($this->redis->sContains('set', 'val2')); + } + + public function testsGetMembers() + { + $this->redis->delete('set'); + + $this->redis->sAdd('set', 'val'); + $this->redis->sAdd('set', 'val2'); + $this->redis->sAdd('set', 'val3'); + + $array = array('val', 'val2', 'val3'); + + $sGetMembers = $this->redis->sGetMembers('set'); + sort($sGetMembers); + $this->assertEquals($array, $sGetMembers); + + $sMembers = $this->redis->sMembers('set'); + sort($sMembers); + $this->assertEquals($array, $sMembers); // test alias + } + + public function testlSet() { + + $this->redis->delete('list'); + $this->redis->lPush('list', 'val'); + $this->redis->lPush('list', 'val2'); + $this->redis->lPush('list', 'val3'); + + $this->assertEquals($this->redis->lGet('list', 0), 'val3'); + $this->assertEquals($this->redis->lGet('list', 1), 'val2'); + $this->assertEquals($this->redis->lGet('list', 2), 'val'); + + $this->assertEquals(TRUE, $this->redis->lSet('list', 1, 'valx')); + + $this->assertEquals($this->redis->lGet('list', 0), 'val3'); + $this->assertEquals($this->redis->lGet('list', 1), 'valx'); + $this->assertEquals($this->redis->lGet('list', 2), 'val'); + + } + + public function testsInter() { + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + + $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + foreach($x as $i) { + $this->redis->sAdd('x', $i); + } + + $y = array(1,2,3,5,7,11,13,17,19,23); + foreach($y as $i) { + $this->redis->sAdd('y', $i); + } + + $z = array(1,4,9,16,25); + foreach($z as $i) { + $this->redis->sAdd('z', $i); + } + + $t = array(2,5,10,17,26); + foreach($t as $i) { + $this->redis->sAdd('t', $i); + } + + $xy = $this->redis->sInter('x', 'y'); // odd prime numbers + foreach($xy as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_intersect($x, $y))); + } + $xy = $this->redis->sInter(array('x', 'y')); // odd prime numbers, as array. + foreach($xy as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_intersect($x, $y))); + } + + $yz = $this->redis->sInter('y', 'z'); // set of odd squares + foreach($yz as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_intersect($y, $z))); + } + $yz = $this->redis->sInter(array('y', 'z')); // set of odd squares, as array + foreach($yz as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_intersect($y, $z))); + } + + $zt = $this->redis->sInter('z', 't'); // prime squares + $this->assertTrue($zt === array()); + $zt = $this->redis->sInter(array('z', 't')); // prime squares, as array + $this->assertTrue($zt === array()); + + $xyz = $this->redis->sInter('x', 'y', 'z');// odd prime squares + $this->assertTrue($xyz === array('1')); + + $xyz = $this->redis->sInter(array('x', 'y', 'z'));// odd prime squares, with an array as a parameter + $this->assertTrue($xyz === array('1')); + + $nil = $this->redis->sInter(); + $this->assertTrue($nil === FALSE); + $nil = $this->redis->sInter(array()); + $this->assertTrue($nil === FALSE); + } + + public function testsInterStore() { + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + + $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + foreach($x as $i) { + $this->redis->sAdd('x', $i); + } + + $y = array(1,2,3,5,7,11,13,17,19,23); + foreach($y as $i) { + $this->redis->sAdd('y', $i); + } + + $z = array(1,4,9,16,25); + foreach($z as $i) { + $this->redis->sAdd('z', $i); + } + + $t = array(2,5,10,17,26); + foreach($t as $i) { + $this->redis->sAdd('t', $i); + } + + $count = $this->redis->sInterStore('k', 'x', 'y'); // odd prime numbers + $this->assertEquals($count, $this->redis->sSize('k')); + foreach(array_intersect($x, $y) as $i) { + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $count = $this->redis->sInterStore('k', 'y', 'z'); // set of odd squares + $this->assertEquals($count, $this->redis->sSize('k')); + foreach(array_intersect($y, $z) as $i) { + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $count = $this->redis->sInterStore('k', 'z', 't'); // squares of the form n^2 + 1 + $this->assertEquals($count, 0); + $this->assertEquals($count, $this->redis->sSize('k')); + + $this->redis->delete('z'); + $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // only z missing, expect 0. + $this->assertTrue($xyz === 0); + + $this->redis->delete('y'); + $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // y and z missing, expect 0. + $this->assertTrue($xyz === 0); + + $this->redis->delete('x'); + $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // x y and z ALL missing, expect 0. + $this->assertTrue($xyz === 0); + + $o = $this->redis->sInterStore('k'); + $this->assertTrue($o === FALSE); // error, wrong parameter count + } + + public function testsUnion() { + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + + $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + foreach($x as $i) { + $this->redis->sAdd('x', $i); + } + + $y = array(1,2,3,5,7,11,13,17,19,23); + foreach($y as $i) { + $this->redis->sAdd('y', $i); + } + + $z = array(1,4,9,16,25); + foreach($z as $i) { + $this->redis->sAdd('z', $i); + } + + $t = array(2,5,10,17,26); + foreach($t as $i) { + $this->redis->sAdd('t', $i); + } + + $xy = $this->redis->sUnion('x', 'y'); // x U y + foreach($xy as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_merge($x, $y))); + } + + $yz = $this->redis->sUnion('y', 'z'); // y U Z + foreach($yz as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_merge($y, $z))); + } + + $zt = $this->redis->sUnion('z', 't'); // z U t + foreach($zt as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_merge($z, $t))); + } + + $xyz = $this->redis->sUnion('x', 'y', 'z'); // x U y U z + foreach($xyz as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_merge($x, $y, $z))); + } + + $nil = $this->redis->sUnion(); + $this->assertTrue($nil === FALSE); + } + + public function testsUnionStore() { + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + + $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + foreach($x as $i) { + $this->redis->sAdd('x', $i); + } + + $y = array(1,2,3,5,7,11,13,17,19,23); + foreach($y as $i) { + $this->redis->sAdd('y', $i); + } + + $z = array(1,4,9,16,25); + foreach($z as $i) { + $this->redis->sAdd('z', $i); + } + + $t = array(2,5,10,17,26); + foreach($t as $i) { + $this->redis->sAdd('t', $i); + } + + $count = $this->redis->sUnionStore('k', 'x', 'y'); // x U y + $xy = array_unique(array_merge($x, $y)); + $this->assertEquals($count, count($xy)); + foreach($xy as $i) { + $i = (int)$i; + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $count = $this->redis->sUnionStore('k', 'y', 'z'); // y U z + $yz = array_unique(array_merge($y, $z)); + $this->assertEquals($count, count($yz)); + foreach($yz as $i) { + $i = (int)$i; + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $count = $this->redis->sUnionStore('k', 'z', 't'); // z U t + $zt = array_unique(array_merge($z, $t)); + $this->assertEquals($count, count($zt)); + foreach($zt as $i) { + $i = (int)$i; + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z + $xyz = array_unique(array_merge($x, $y, $z)); + $this->assertEquals($count, count($xyz)); + foreach($xyz as $i) { + $i = (int)$i; + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $this->redis->delete('x'); // x missing now + $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z + $this->assertTrue($count === count(array_unique(array_merge($y, $z)))); + + $this->redis->delete('y'); // x and y missing + $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z + $this->assertTrue($count === count(array_unique($z))); + + $this->redis->delete('z'); // x, y, and z ALL missing + $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z + $this->assertTrue($count === 0); + + $count = $this->redis->sUnionStore('k'); // Union on nothing... + $this->assertTrue($count === FALSE); + } + + public function testsDiff() { + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + + $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + foreach($x as $i) { + $this->redis->sAdd('x', $i); + } + + $y = array(1,2,3,5,7,11,13,17,19,23); + foreach($y as $i) { + $this->redis->sAdd('y', $i); + } + + $z = array(1,4,9,16,25); + foreach($z as $i) { + $this->redis->sAdd('z', $i); + } + + $t = array(2,5,10,17,26); + foreach($t as $i) { + $this->redis->sAdd('t', $i); + } + + $xy = $this->redis->sDiff('x', 'y'); // x U y + foreach($xy as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_diff($x, $y))); + } + + $yz = $this->redis->sDiff('y', 'z'); // y U Z + foreach($yz as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_diff($y, $z))); + } + + $zt = $this->redis->sDiff('z', 't'); // z U t + foreach($zt as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_diff($z, $t))); + } + + $xyz = $this->redis->sDiff('x', 'y', 'z'); // x U y U z + foreach($xyz as $i) { + $i = (int)$i; + $this->assertTrue(in_array($i, array_diff($x, $y, $z))); + } + + $nil = $this->redis->sDiff(); + $this->assertTrue($nil === FALSE); + } + + public function testsDiffStore() { + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + + $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + foreach($x as $i) { + $this->redis->sAdd('x', $i); + } + + $y = array(1,2,3,5,7,11,13,17,19,23); + foreach($y as $i) { + $this->redis->sAdd('y', $i); + } + + $z = array(1,4,9,16,25); + foreach($z as $i) { + $this->redis->sAdd('z', $i); + } + + $t = array(2,5,10,17,26); + foreach($t as $i) { + $this->redis->sAdd('t', $i); + } + + $count = $this->redis->sDiffStore('k', 'x', 'y'); // x - y + $xy = array_unique(array_diff($x, $y)); + $this->assertEquals($count, count($xy)); + foreach($xy as $i) { + $i = (int)$i; + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $count = $this->redis->sDiffStore('k', 'y', 'z'); // y - z + $yz = array_unique(array_diff($y, $z)); + $this->assertEquals($count, count($yz)); + foreach($yz as $i) { + $i = (int)$i; + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $count = $this->redis->sDiffStore('k', 'z', 't'); // z - t + $zt = array_unique(array_diff($z, $t)); + $this->assertEquals($count, count($zt)); + foreach($zt as $i) { + $i = (int)$i; + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z + $xyz = array_unique(array_diff($x, $y, $z)); + $this->assertEquals($count, count($xyz)); + foreach($xyz as $i) { + $i = (int)$i; + $this->assertTrue($this->redis->sContains('k', $i)); + } + + $this->redis->delete('x'); // x missing now + $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z + $this->assertTrue($count === 0); + + $this->redis->delete('y'); // x and y missing + $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z + $this->assertTrue($count === 0); + + $this->redis->delete('z'); // x, y, and z ALL missing + $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z + $this->assertTrue($count === 0); + + $count = $this->redis->sDiffStore('k'); // diff on nothing... + $this->assertTrue($count === FALSE); + } + + public function testlGetRange() { + + $this->redis->delete('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->lGetRange('list', 0, 0), array('val3')); + $this->assertEquals($this->redis->lGetRange('list', 0, 1), array('val3', 'val2')); + $this->assertEquals($this->redis->lGetRange('list', 0, 2), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lGetRange('list', 0, 3), array('val3', 'val2', 'val')); + + $this->assertEquals($this->redis->lGetRange('list', 0, -1), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lGetRange('list', 0, -2), array('val3', 'val2')); + $this->assertEquals($this->redis->lGetRange('list', -2, -1), array('val2', 'val')); + + $this->redis->delete('list'); + $this->assertEquals($this->redis->lGetRange('list', 0, -1), array()); + } + + +// public function testsave() { +// $this->assertTrue($this->redis->save() === TRUE); // don't really know how else to test this... +// } +// public function testbgSave() { +// // let's try to fill the DB and then bgSave twice. We expect the second one to fail. +// for($i = 0; $i < 10e+4; $i++) { +// $s = md5($i); +// $this->redis->set($s, $s); +// } +// $this->assertTrue($this->redis->bgSave() === TRUE); // the first one should work. +// $this->assertTrue($this->redis->bgSave() === FALSE); // the second one should fail (still working on the first one) +// } +// +// public function testlastSave() { +// while(!$this->redis->save()) { +// sleep(1); +// } +// $t_php = microtime(TRUE); +// $t_redis = $this->redis->lastSave(); +// +// $this->assertTrue($t_php - $t_redis < 10000); // check that it's approximately what we've measured in PHP. +// } +// +// public function testflushDb() { +// $this->redis->set('x', 'y'); +// $this->assertTrue($this->redis->flushDb()); +// $this->assertTrue($this->redis->getKeys('*') === array()); +// } +// +// public function testflushAll() { +// $this->redis->set('x', 'y'); +// $this->assertTrue($this->redis->flushAll()); +// $this->assertTrue($this->redis->getKeys('*') === array()); +// } + + 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->setTimeout('x', 5); + for($i = 5; $i > 0; $i--) { + $this->assertEquals($i, $this->redis->ttl('x')); + sleep(1); + } + + // 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", "gte")) { + $this->redis->del('x'); + $this->assertEquals($this->redis->ttl('x'), -2); + } + } + + public function testPersist() { + $this->redis->set('x', 'y'); + $this->redis->setTimeout('x', 100); + $this->assertTrue(TRUE === $this->redis->persist('x')); // true if there is a timeout + $this->assertTrue(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. + $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout + $this->redis->delete('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', 'lt')) { + $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_slaves, 100), $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+1, 100) < $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(-1, 20)); + } + + public function testinfo() { + $info = $this->redis->info(); + + $keys = array( + "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", "lt")) { + 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($k, array_keys($info))); + } + } + + public function testInfoCommandStats() { + + // INFO COMMANDSTATS is new in 2.6.0 + if (version_compare($this->version, "2.5.0", "lt")) { + $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 testMset() { + $this->redis->delete('x', 'y', 'z'); // remove x y z + $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z + + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z + + $this->redis->delete('x'); // delete just x + $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z + + $this->assertFalse($this->redis->mset(array())); // set ø → FALSE + + + /* + * Integer keys + */ + + // No prefix + $set_array = Array(-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three'); + $this->redis->delete(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->delete(array_keys($set_array)); + + // With a prefix + $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:'); + $this->redis->delete(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->delete(array_keys($set_array)); + $this->redis->setOption(Redis::OPT_PREFIX, ''); + } + + public function testMsetNX() { + $this->redis->delete('x', 'y', 'z'); // remove x y z + $this->assertTrue(TRUE === $this->redis->msetnx(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z + + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z + + $this->redis->delete('x'); // delete just x + $this->assertTrue(FALSE === $this->redis->msetnx(array('x' => 'A', 'y' => 'B', 'z' => 'C'))); // set x y z + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array(FALSE, 'b', 'c')); // check x y z + + $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE + } + + public function testRpopLpush() { + + // standard case. + $this->redis->delete('x', 'y'); + $this->redis->lpush('x', 'abc'); + $this->redis->lpush('x', 'def'); // x = [def, abc] + + $this->redis->lpush('y', '123'); + $this->redis->lpush('y', '456'); // y = [456, 123] + + $this->assertEquals($this->redis->rpoplpush('x', 'y'), 'abc'); // we RPOP x, yielding abc. + $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. + $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. + + // with an empty source, expecting no change. + $this->redis->delete('x', 'y'); + $this->assertTrue(FALSE === $this->redis->rpoplpush('x', 'y')); + $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); + $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); + + } + + public function testBRpopLpush() { + + // standard case. + $this->redis->delete('x', 'y'); + $this->redis->lpush('x', 'abc'); + $this->redis->lpush('x', 'def'); // x = [def, abc] + + $this->redis->lpush('y', '123'); + $this->redis->lpush('y', '456'); // y = [456, 123] + + $this->assertEquals($this->redis->brpoplpush('x', 'y', 1), 'abc'); // we RPOP x, yielding abc. + $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. + $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. + + // with an empty source, expecting no change. + $this->redis->delete('x', 'y'); + $this->assertTrue(FALSE === $this->redis->brpoplpush('x', 'y', 1)); + $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); + $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); + + } + + public function testZAddFirstArg() { + + $this->redis->delete('key'); + + $zsetName = 100; // not a string! + $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); + $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1')); + + $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1)); + } + + public function testZX() { + + $this->redis->delete('key'); + + $this->assertTrue(array() === $this->redis->zRange('key', 0, -1)); + $this->assertTrue(array() === $this->redis->zRange('key', 0, -1, true)); + + $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); + $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); + $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters + + $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); + + // withscores + $ret = $this->redis->zRange('key', 0, -1, true); + $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(0 === $this->redis->zDelete('key', 'valX')); + $this->assertTrue(1 === $this->redis->zDelete('key', 'val3')); + $this->assertTrue(1 === $this->redis->zDelete('key', 'val4')); + $this->assertTrue(1 === $this->redis->zDelete('key', 'val5')); + + $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->zRange('key', 0, -1)); + + // zGetReverseRange + + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); + + $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); + $this->assertTrue(array('val0', 'val1', 'val2', 'aal3', 'val3') === $zero_to_three || array('val0', 'val1', 'val2', 'val3', 'aal3') === $zero_to_three); + + $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); + $this->assertTrue(array_reverse(array('val0', 'val1', 'val2', 'aal3', 'val3')) === $three_to_zero || array_reverse(array('val0', 'val1', 'val2', 'val3', 'aal3')) === $three_to_zero); + + $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); + + // withscores + $this->redis->zRemove('key', 'aal3'); + $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); + $this->assertTrue(array('val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3) == $zero_to_three); + $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); + + // limit + $this->assertTrue(array('val0') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 1)))); + $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 2)))); + $this->assertTrue(array('val1', 'val2') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 2)))); + $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => array(0, 100)))); + + $this->assertTrue(array('val3') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 1)))); + $this->assertTrue(array('val3', 'val2') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 2)))); + $this->assertTrue(array('val2', 'val1') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(1, 2)))); + $this->assertTrue(array('val1', 'val0') === $this->redis->zRevRangeByScore('key', 1, 0, array('limit' => array(0, 100)))); + + $this->assertTrue(4 === $this->redis->zSize('key')); + $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); + $this->assertFalse($this->redis->zScore('key', 'val')); + $this->assertFalse($this->redis->zScore(3, 2)); + + // with () and +inf, -inf + $this->redis->delete('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(array('foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4) == $this->redis->zRangeByScore('zset', '-inf', '+inf', array('withscores' => TRUE))); + $this->assertTrue(array('foo' => 1, 'bar' => 2) == $this->redis->zRangeByScore('zset', 1, 2, array('withscores' => TRUE))); + $this->assertTrue(array('bar' => 2) == $this->redis->zRangeByScore('zset', '(1', 2, array('withscores' => TRUE))); + $this->assertTrue(array() == $this->redis->zRangeByScore('zset', '(1', '(2', array('withscores' => TRUE))); + + $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); + $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); + $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); + $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2')); + + + // zincrby + $this->redis->delete('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')); + + //zUnion + $this->redis->delete('key1'); + $this->redis->delete('key2'); + $this->redis->delete('key3'); + $this->redis->delete('keyU'); + + $this->redis->zAdd('key1', 0, 'val0'); + $this->redis->zAdd('key1', 1, 'val1'); + + $this->redis->zAdd('key2', 2, 'val2'); + $this->redis->zAdd('key2', 3, 'val3'); + + $this->redis->zAdd('key3', 4, 'val4'); + $this->redis->zAdd('key3', 5, 'val5'); + + $this->assertTrue(4 === $this->redis->zUnion('keyU', array('key1', 'key3'))); + $this->assertTrue(array('val0', 'val1', 'val4', 'val5') === $this->redis->zRange('keyU', 0, -1)); + + // Union on non existing keys + $this->redis->delete('keyU'); + $this->assertTrue(0 === $this->redis->zUnion('keyU', array('X', 'Y'))); + $this->assertTrue(array() === $this->redis->zRange('keyU', 0, -1)); + + // !Exist U Exist → copy of existing zset. + $this->redis->delete('keyU', 'X'); + $this->assertTrue(2 === $this->redis->zUnion('keyU', array('key1', 'X'))); + + // test weighted zUnion + $this->redis->delete('keyZ'); + $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(1, 1))); + $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('keyZ', 0, -1)); + + $this->redis->zDeleteRangeByScore('keyZ', 0, 10); + $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(5, 1))); + $this->assertTrue(array('val0', 'val2', 'val3', 'val1') === $this->redis->zRange('keyZ', 0, -1)); + + $this->redis->delete('key1'); + $this->redis->delete('key2'); + $this->redis->delete('key3'); + + //test zUnion with weights and aggegration function + $this->redis->zadd('key1', 1, 'duplicate'); + $this->redis->zadd('key2', 2, 'duplicate'); + $this->redis->zUnion('keyU', array('key1','key2'), array(1,1), 'MIN'); + $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); + $this->redis->delete('keyU'); + + //now test zUnion *without* weights but with aggregrate function + $this->redis->zUnion('keyU', array('key1','key2'), null, 'MIN'); + $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); + $this->redis->delete('keyU', 'key1', 'key2'); + + + + // test integer and float weights (GitHub issue #109). + $this->redis->del('key1', 'key2', 'key3'); + + $this->redis->zadd('key1', 1, 'one'); + $this->redis->zadd('key1', 2, 'two'); + $this->redis->zadd('key2', 1, 'one'); + $this->redis->zadd('key2', 2, 'two'); + $this->redis->zadd('key2', 3, 'three'); + + $this->assertTrue($this->redis->zunion('key3', array('key1', 'key2'), array(2, 3.0)) === 3); + + $this->redis->delete('key1'); + $this->redis->delete('key2'); + $this->redis->delete('key3'); + + // Test 'inf', '-inf', and '+inf' weights (GitHub issue #336) + $this->redis->zadd('key1', 1, 'one', 2, 'two', 3, 'three'); + $this->redis->zadd('key2', 3, 'three', 4, 'four', 5, 'five'); + + // Make sure phpredis handles these weights + $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, 'inf')) === 5); + $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, '-inf')) === 5); + $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, '+inf')) === 5); + + // Now, confirm that they're being sent, and that it works + $arr_weights = Array('inf','-inf','+inf'); + + foreach($arr_weights as $str_weight) { + $r = $this->redis->zunionstore('key3', array('key1','key2'), array(1,$str_weight)); + $this->assertTrue($r===5); + $r = $this->redis->zrangebyscore('key3', '(-inf', '(inf',array('withscores'=>true)); + $this->assertTrue(count($r)===2); + $this->assertTrue(isset($r['one'])); + $this->assertTrue(isset($r['two'])); + } + + $this->redis->del('key1','key2','key3'); + + $this->redis->zadd('key1', 2000.1, 'one'); + $this->redis->zadd('key1', 3000.1, 'two'); + $this->redis->zadd('key1', 4000.1, 'three'); + + $ret = $this->redis->zRange('key1', 0, -1, TRUE); + $this->assertTrue(count($ret) === 3); + $retValues = array_keys($ret); + + $this->assertTrue(array('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->delete('key1'); + + // ZREMRANGEBYRANK + $this->redis->zAdd('key1', 1, 'one'); + $this->redis->zAdd('key1', 2, 'two'); + $this->redis->zAdd('key1', 3, 'three'); + $this->assertTrue(2 === $this->redis->zremrangebyrank('key1', 0, 1)); + $this->assertTrue(array('three' => 3) == $this->redis->zRange('key1', 0, -1, TRUE)); + + $this->redis->delete('key1'); + + // zInter + + $this->redis->zAdd('key1', 0, 'val0'); + $this->redis->zAdd('key1', 1, 'val1'); + $this->redis->zAdd('key1', 3, 'val3'); + + $this->redis->zAdd('key2', 2, 'val1'); + $this->redis->zAdd('key2', 3, 'val3'); + + $this->redis->zAdd('key3', 4, 'val3'); + $this->redis->zAdd('key3', 5, 'val5'); + + $this->redis->delete('keyI'); + $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'))); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + + // Union on non existing keys + $this->assertTrue(0 === $this->redis->zInter('keyX', array('X', 'Y'))); + $this->assertTrue(array() === $this->redis->zRange('keyX', 0, -1)); + + // !Exist U Exist + $this->assertTrue(0 === $this->redis->zInter('keyY', array('key1', 'X'))); + $this->assertTrue(array() === $this->redis->zRange('keyY', 0, -1)); + + + // test weighted zInter + $this->redis->delete('key1'); + $this->redis->delete('key2'); + $this->redis->delete('key3'); + + $this->redis->zAdd('key1', 0, 'val0'); + $this->redis->zAdd('key1', 1, 'val1'); + $this->redis->zAdd('key1', 3, 'val3'); + + + $this->redis->zAdd('key2', 2, 'val1'); + $this->redis->zAdd('key2', 1, 'val3'); + + $this->redis->zAdd('key3', 7, 'val1'); + $this->redis->zAdd('key3', 3, 'val3'); + + $this->redis->delete('keyI'); + $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'), array(1, 1))); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + + $this->redis->delete('keyI'); + $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'min')); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + $this->redis->delete('keyI'); + $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'max')); + $this->assertTrue(array('val3', 'val1') === $this->redis->zRange('keyI', 0, -1)); + + $this->redis->delete('keyI'); + $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), null, 'max')); + $this->assertTrue($this->redis->zScore('keyI', 'val1') === floatval(7)); + + // zrank, zrevrank + $this->redis->delete('z'); + $this->redis->zadd('z', 1, 'one'); + $this->redis->zadd('z', 2, 'two'); + $this->redis->zadd('z', 5, 'five'); + + $this->assertTrue(0 === $this->redis->zRank('z', 'one')); + $this->assertTrue(1 === $this->redis->zRank('z', 'two')); + $this->assertTrue(2 === $this->redis->zRank('z', 'five')); + + $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); + $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); + $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); + + } + + public function testHashes() { + $this->redis->delete('h', 'key'); + + $this->assertTrue(0 === $this->redis->hLen('h')); + $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); + $this->assertTrue(1 === $this->redis->hLen('h')); + $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); + $this->assertTrue(2 === $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(0 === $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(1 === $this->redis->hDel('h', 'a')); // 1 on success + $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure + + $this->redis->delete('h'); + $this->redis->hSet('h', 'x', 'a'); + $this->redis->hSet('h', 'y', 'b'); + $this->assertTrue(2 === $this->redis->hDel('h', 'x', 'y')); // variadic + + // hsetnx + $this->redis->delete('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 === array('x', 'y') || $keys === array('y', 'x')); + + // values + $values = $this->redis->hVals('h'); + $this->assertTrue($values === array('a', 'b') || $values === array('b', 'a')); + + // keys + values + $all = $this->redis->hGetAll('h'); + $this->assertTrue($all === array('x' => 'a', 'y' => 'b') || $all === array('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->delete('h'); + $this->assertTrue(FALSE === $this->redis->hExists('h', 'x')); + + // hIncrBy + $this->redis->delete('h'); + $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); + $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); + $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); + $this->assertTrue(FALSE === $this->redis->hIncrBy('h', 'x', "not-a-number")); + $this->assertTrue("2" === $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", "ge")) { + // hIncrByFloat + $this->redis->delete('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->redis->hset('h','y','not-a-number'); + $this->assertTrue(FALSE === $this->redis->hIncrByFloat('h', 'y', 1.5)); + } + + // hmset + $this->redis->delete('h'); + $this->assertTrue(TRUE === $this->redis->hMset('h', array('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(array('x' => '123', 'y' => '456') === $this->redis->hMget('h', array('x', 'y'))); + $this->assertTrue(array('z' => 'abc') === $this->redis->hMget('h', array('z'))); + $this->assertTrue(array('x' => '123', 't' => FALSE, 'y' => '456') === $this->redis->hMget('h', array('x', 't', 'y'))); + $this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123))); + $this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123))); + + // Test with an array populated with things we can't use as keys + $this->assertTrue($this->redis->hmget('h', Array(false,NULL,false)) === FALSE); + + // Test with some invalid keys mixed in (which should just be ignored) + $this->assertTrue(array('x'=>'123','y'=>'456','z'=>'abc') === $this->redis->hMget('h',Array('x',null,'y','','z',false))); + + // hmget/hmset with numeric fields + $this->redis->del('h'); + $this->assertTrue(TRUE === $this->redis->hMset('h', array(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(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); + + // check non-string types. + $this->redis->delete('h1'); + $this->assertTrue(TRUE === $this->redis->hMSet('h1', array('x' => 0, 'y' => array(), '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']); + + } + + public function testSetRange() { + + $this->redis->delete('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->delete('key'); + $this->redis->setRange('key', 6, 'foo'); + $this->assertTrue("\x00\x00\x00\x00\x00\x00foo" === $this->redis->get('key')); + } + + public function testObject() { + $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') === "raw"); + $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'); + $this->assertTrue($this->redis->object('encoding', 'key') === "ziplist"); + $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. + $this->assertTrue($this->redis->object('encoding', 'key') === "linkedlist"); + $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 === array('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 === array('44')); // succeeded since we've cancel the WATCH command. + } + + public function testPipeline() { + $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, ""); + } + + protected function sequence($mode) { + + $ret = $this->redis->multi($mode) + ->set('x', 42) + ->info() + ->type('x') + ->get('x') + ->exec(); + + $this->assertTrue(is_array($ret)); + $i = 0; + $this->assertTrue($ret[$i++] == TRUE); + $this->assertTrue(is_array($ret[$i++])); + $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_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer + $ret = $this->redis->multi($mode) + ->delete('key1') + ->set('key1', 'value1') + ->get('key1') + ->getSet('key1', 'value2') + ->get('key1') + ->set('key2', 4) + ->incr('key2') + ->get('key2') + ->decr('key2') + ->get('key2') + ->renameKey('key2', 'key3') + ->get('key3') + ->renameNx('key3', 'key1') + ->renameKey('key3', 'key2') + ->incr('key2', 5) + ->get('key2') + ->decr('key2', 5) + ->get('key2') + ->exec(); + + $this->assertTrue(is_array($ret)); + $i = 0; + $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) + ->delete('key1') + ->delete('key2') + ->set('key1', 'val1') + ->setnx('key1', 'valX') + ->setnx('key2', 'valX') + ->exists('key1') + ->exists('key3') + ->ping() + ->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); + $this->assertTrue($ret[7] == '+PONG'); + + $ret = $this->redis->multi($mode) + ->randomKey() + ->exec(); + $ret = $this->redis->multi($mode) + ->exec(); + $this->assertTrue($ret == array()); + + // ttl, mget, mset, msetnx, expire, expireAt + $this->redis->delete('key'); + $ret = $this->redis->multi($mode) + ->ttl('key') + ->mget(array('key1', 'key2', 'key3')) + ->mset(array('key3' => 'value3', 'key4' => '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 === -1 || $ttl === -2); + $this->assertTrue($ret[$i++] === array('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('lkey', 'x') + ->set('lDest', 'y') + ->delete('lkey', 'lDest') + ->rpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->rpoplpush('lkey', 'lDest') + ->lGetRange('lDest', 0, -1) + ->lpop('lkey') + ->llen('lkey') + ->lRemove('lkey', 'lvalue', 3) + ->llen('lkey') + ->lget('lkey', 0) + ->lGetRange('lkey', 0, -1) + ->lSet('lkey', 1, "newValue") // check errors on key not exists + ->lGetRange('lkey', 0, -1) + ->llen('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++] === array('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++] === array("lvalue")); // this is the current list. + $this->assertTrue($ret[$i++] === FALSE); // updating a non-existent element fails. + $this->assertTrue($ret[$i++] === array("lvalue")); // this is the current list. + $this->assertTrue($ret[$i++] === 1); // 1 element left + $this->assertTrue(count($ret) == $i); + + + $ret = $this->redis->multi(Redis::PIPELINE) + ->delete('lkey', 'lDest') + ->rpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->rpoplpush('lkey', 'lDest') + ->lGetRange('lDest', 0, -1) + ->lpop('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++] === array('lvalue')); // rpoplpush returns the element: "lvalue" + $this->assertTrue($ret[$i++] === 'lvalue'); // pop returns the front element: "lvalue" + $this->assertTrue(count($ret) == $i); + + + // general command + $ret = $this->redis->multi($mode) + ->select(3) + ->set('keyAAA', 'value') + ->set('keyAAB', 'value') + ->dbSize() + ->lastsave() + ->exec(); + + $this->redis->select(0); // back to normal + + $i = 0; + $this->assertTrue(is_array($ret)); + $this->assertTrue($ret[$i++] === TRUE); // select + $this->assertTrue($ret[$i++] === TRUE); // set + $this->assertTrue($ret[$i++] === TRUE); // set + $this->assertTrue(is_long($ret[$i++])); // dbsize + $this->assertTrue(is_long($ret[$i++])); // lastsave + + $this->assertTrue(count($ret) === $i); + + $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer + $ret = $this->redis->multi($mode) + ->delete('key1') + ->set('key1', 'value1') + ->get('key1') + ->getSet('key1', 'value2') + ->get('key1') + ->set('key2', 4) + ->incr('key2') + ->get('key2') + ->decr('key2') + ->get('key2') + ->renameKey('key2', 'key3') + ->get('key3') + ->renameNx('key3', 'key1') + ->renameKey('key3', 'key2') + ->incr('key2', 5) + ->get('key2') + ->decr('key2', 5) + ->get('key2') + ->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->redis->setOption(Redis::OPT_SERIALIZER, $serializer); + + $ret = $this->redis->multi($mode) + ->delete('key1') + ->delete('key2') + ->set('key1', 'val1') + ->setnx('key1', 'valX') + ->setnx('key2', 'valX') + ->exists('key1') + ->exists('key3') + ->ping() + ->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); + $this->assertTrue($ret[7] == '+PONG'); + + $ret = $this->redis->multi($mode) + ->randomKey() + ->exec(); + + $this->assertTrue(is_array($ret) && count($ret) === 1); + $this->assertTrue(is_string($ret[0])); + + // ttl, mget, mset, msetnx, expire, expireAt + $ret = $this->redis->multi($mode) + ->ttl('key') + ->mget(array('key1', 'key2', 'key3')) + ->mset(array('key3' => 'value3', 'key4' => '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) + ->delete('lkey', 'lDest') + ->rpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->rpoplpush('lkey', 'lDest') + ->lGetRange('lDest', 0, -1) + ->lpop('lkey') + ->llen('lkey') + ->lRemove('lkey', 'lvalue', 3) + ->llen('lkey') + ->lget('lkey', 0) + ->lGetRange('lkey', 0, -1) + ->lSet('lkey', 1, "newValue") // check errors on missing key + ->lGetRange('lkey', 0, -1) + ->llen('lkey') + ->exec(); + + $this->assertTrue(is_array($ret)); + $i = 0; + $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // delete + $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++] === array('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++] === array('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++] === array('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) + ->delete('skey1', 'skey2', 'skeydest', 'skeyUnion', 'sDiffDest') + ->sadd('skey1', 'sValue1') + ->sadd('skey1', 'sValue2') + ->sadd('skey1', 'sValue3') + ->sadd('skey1', 'sValue4') + + ->sadd('skey2', 'sValue1') + ->sadd('skey2', 'sValue2') + + ->sSize('skey1') + ->sRemove('skey1', 'sValue2') + ->sSize('skey1') + ->sMove('skey1', 'skey2', 'sValue4') + ->sSize('skey2') + ->sContains('skey2', 'sValue4') + ->sMembers('skey1') + ->sMembers('skey2') + ->sInter('skey1', 'skey2') + ->sInterStore('skeydest', 'skey1', 'skey2') + ->sMembers('skeydest') + ->sUnion('skey2', 'skeydest') + ->sUnionStore('skeyUnion', 'skey2', 'skeydest') + ->sMembers('skeyUnion') + ->sDiff('skey1', 'skey2') + ->sDiffStore('sDiffDest', 'skey1', 'skey2') + ->sMembers('sDiffDest') + ->sPop('skey2') + ->sSize('skey2') + ->exec(); + + $i = 0; + $this->assertTrue(is_array($ret)); + $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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(array('sValue1', 'sValue3') as $k) { // sKey1 contains sValue1 and sValue3. + $this->assertTrue(in_array($k, $ret[$i])); + } + $this->assertTrue(count($ret[$i++]) === 2); + foreach(array('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++] === array('sValue1')); // intersection + $this->assertTrue($ret[$i++] === 1); // intersection + store → 1 value in the destination set. + $this->assertTrue($ret[$i++] === array('sValue1')); // sinterstore destination contents + + foreach(array('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(array('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++] === array('sValue3')); // diff skey1, skey2 : only sValue3 is not shared. + $this->assertTrue($ret[$i++] === 1); // sdiffstore size == 1 + $this->assertTrue($ret[$i++] === array('sValue3')); // contents of sDiffDest + + $this->assertTrue(in_array($ret[$i++], array('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) + ->delete('zkey1', 'zkey2', 'zkey5', 'zInter', 'zUnion') + ->zadd('zkey1', 1, 'zValue1') + ->zadd('zkey1', 5, 'zValue5') + ->zadd('zkey1', 2, 'zValue2') + ->zRange('zkey1', 0, -1) + ->zDelete('zkey1', 'zValue2') + ->zRange('zkey1', 0, -1) + ->zadd('zkey1', 11, 'zValue11') + ->zadd('zkey1', 12, 'zValue12') + ->zadd('zkey1', 13, 'zValue13') + ->zadd('zkey1', 14, 'zValue14') + ->zadd('zkey1', 15, 'zValue15') + ->zDeleteRangeByScore('zkey1', 11, 13) + ->zrange('zkey1', 0, -1) + ->zReverseRange('zkey1', 0, -1) + ->zRangeByScore('zkey1', 1, 6) + ->zCard('zkey1') + ->zScore('zkey1', 'zValue15') + ->zadd('zkey2', 5, 'zValue5') + ->zadd('zkey2', 2, 'zValue2') + ->zInter('zInter', array('zkey1', 'zkey2')) + ->zRange('zkey1', 0, -1) + ->zRange('zkey2', 0, -1) + ->zRange('zInter', 0, -1) + ->zUnion('zUnion', array('zkey1', 'zkey2')) + ->zRange('zUnion', 0, -1) + ->zadd('zkey5', 5, 'zValue5') + ->zIncrBy('zkey5', 3, 'zValue5') // fix this + ->zScore('zkey5', 'zValue5') + ->zScore('zkey5', 'unknown') + ->exec(); + + $i = 0; + $this->assertTrue(is_array($ret)); + $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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++] === array('zValue1', 'zValue2', 'zValue5')); + $this->assertTrue($ret[$i++] === 1); + $this->assertTrue($ret[$i++] === array('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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); + $this->assertTrue($ret[$i++] === array('zValue15', 'zValue14', 'zValue5', 'zValue1')); + $this->assertTrue($ret[$i++] === array('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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); // zkey1 contents + $this->assertTrue($ret[$i++] === array('zValue2', 'zValue5')); // zkey2 contents + $this->assertTrue($ret[$i++] === array('zValue5')); // zinter contents + $this->assertTrue($ret[$i++] === 5); // zUnion has 5 values (1,2,5,14,15) + $this->assertTrue($ret[$i++] === array('zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15')); // zunion contents + $this->assertTrue($ret[$i++] === 1); // added value to zkey5, 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) + ->delete('hkey1') + ->hset('hkey1', 'key1', 'value1') + ->hset('hkey1', 'key2', 'value2') + ->hset('hkey1', 'key3', 'value3') + ->hmget('hkey1', array('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++] === array('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] === array('key1', 'key3') || $ret[$i] === array('key3', 'key1')); $i++; // hkeys + $this->assertTrue($ret[$i] === array('value1', 'value3') || $ret[$i] === array('value3', 'value1')); $i++; // hvals + $this->assertTrue($ret[$i] === array('key1' => 'value1', 'key3' => 'value3') || $ret[$i] === array('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. + ->delete('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 === array(1.0, FALSE, FALSE, 2.0)); + } + + protected function differentType($mode) { + + // string + $key = 'string'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->set($key, 'value') + + // lists I/F + ->rPush($key, 'lvalue') + ->lPush($key, 'lvalue') + ->lLen($key) + ->lPop($key) + ->lGetRange($key, 0, -1) + ->lTrim($key, 0, 1) + ->lGet($key, 0) + ->lSet($key, 0, "newValue") + ->lRemove($key, 'lvalue', 1) + ->lPop($key) + ->rPop($key) + ->rPoplPush($key, __FUNCTION__ . 'lkey1') + + // sets I/F + ->sAdd($key, 'sValue1') + ->sRemove($key, 'sValue1') + ->sPop($key) + ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sSize($key) + ->sContains($key, 'sValue1') + ->sInter($key, __FUNCTION__ . 'skey2') + ->sUnion($key, __FUNCTION__ . 'skey4') + ->sDiff($key, __FUNCTION__ . 'skey7') + ->sMembers($key) + ->sRandMember($key) + + // sorted sets I/F + ->zAdd($key, 1, 'zValue1') + ->zDelete($key, 'zValue1') + ->zIncrBy($key, 1, 'zValue1') + ->zRank($key, 'zValue1') + ->zRevRank($key, 'zValue1') + ->zRange($key, 0, -1) + ->zReverseRange($key, 0, -1) + ->zRangeByScore($key, 1, 2) + ->zCount($key, 0, -1) + ->zCard($key) + ->zScore($key, 'zValue1') + ->zDeleteRangeByRank($key, 1, 2) + ->zDeleteRangeByScore($key, 1, 2) + + // hash I/F + ->hSet($key, 'key1', 'value1') + ->hGet($key, 'key1') + ->hMGet($key, array('key1')) + ->hMSet($key, array('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); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // ltrim + $this->assertTrue($ret[$i++] === FALSE); // lget + $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); // ssize + $this->assertTrue($ret[$i++] === FALSE); // scontains + $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); // zdelete + $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); // zdeleterangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + + $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($i, count($ret)); + + // list + $key = 'list'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->lpush($key, 'lvalue') + + // string I/F + ->get($key) + ->getset($key, 'value2') + ->append($key, 'append') + ->substr($key, 0, 8) + ->mget(array($key)) + ->incr($key) + ->incrBy($key, 1) + ->decr($key) + ->decrBy($key, 1) + + // sets I/F + ->sAdd($key, 'sValue1') + ->sRemove($key, 'sValue1') + ->sPop($key) + ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sSize($key) + ->sContains($key, 'sValue1') + ->sInter($key, __FUNCTION__ . 'skey2') + ->sUnion($key, __FUNCTION__ . 'skey4') + ->sDiff($key, __FUNCTION__ . 'skey7') + ->sMembers($key) + ->sRandMember($key) + + // sorted sets I/F + ->zAdd($key, 1, 'zValue1') + ->zDelete($key, 'zValue1') + ->zIncrBy($key, 1, 'zValue1') + ->zRank($key, 'zValue1') + ->zRevRank($key, 'zValue1') + ->zRange($key, 0, -1) + ->zReverseRange($key, 0, -1) + ->zRangeByScore($key, 1, 2) + ->zCount($key, 0, -1) + ->zCard($key) + ->zScore($key, 'zValue1') + ->zDeleteRangeByRank($key, 1, 2) + ->zDeleteRangeByScore($key, 1, 2) + + // hash I/F + ->hSet($key, 'key1', 'value1') + ->hGet($key, 'key1') + ->hMGet($key, array('key1')) + ->hMSet($key, array('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); // substr + $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // ssize + $this->assertTrue($ret[$i++] === FALSE); // scontains + $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); // zdelete + $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); // zdeleterangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + + $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($i, count($ret)); + + // set + $key = 'set'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->sAdd($key, 'sValue') + + // string I/F + ->get($key) + ->getset($key, 'value2') + ->append($key, 'append') + ->substr($key, 0, 8) + ->mget(array($key)) + ->incr($key) + ->incrBy($key, 1) + ->decr($key) + ->decrBy($key, 1) + + // lists I/F + ->rPush($key, 'lvalue') + ->lPush($key, 'lvalue') + ->lLen($key) + ->lPop($key) + ->lGetRange($key, 0, -1) + ->lTrim($key, 0, 1) + ->lGet($key, 0) + ->lSet($key, 0, "newValue") + ->lRemove($key, 'lvalue', 1) + ->lPop($key) + ->rPop($key) + ->rPoplPush($key, __FUNCTION__ . 'lkey1') + + // sorted sets I/F + ->zAdd($key, 1, 'zValue1') + ->zDelete($key, 'zValue1') + ->zIncrBy($key, 1, 'zValue1') + ->zRank($key, 'zValue1') + ->zRevRank($key, 'zValue1') + ->zRange($key, 0, -1) + ->zReverseRange($key, 0, -1) + ->zRangeByScore($key, 1, 2) + ->zCount($key, 0, -1) + ->zCard($key) + ->zScore($key, 'zValue1') + ->zDeleteRangeByRank($key, 1, 2) + ->zDeleteRangeByScore($key, 1, 2) + + // hash I/F + ->hSet($key, 'key1', 'value1') + ->hGet($key, 'key1') + ->hMGet($key, array('key1')) + ->hMSet($key, array('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); // substr + $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // ltrim + $this->assertTrue($ret[$i++] === FALSE); // lget + $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); // zdelete + $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); // zdeleterangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + + $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($i, count($ret)); + + // sorted set + $key = 'sortedset'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->zAdd($key, 0, 'zValue') + + // string I/F + ->get($key) + ->getset($key, 'value2') + ->append($key, 'append') + ->substr($key, 0, 8) + ->mget(array($key)) + ->incr($key) + ->incrBy($key, 1) + ->decr($key) + ->decrBy($key, 1) + + // lists I/F + ->rPush($key, 'lvalue') + ->lPush($key, 'lvalue') + ->lLen($key) + ->lPop($key) + ->lGetRange($key, 0, -1) + ->lTrim($key, 0, 1) + ->lGet($key, 0) + ->lSet($key, 0, "newValue") + ->lRemove($key, 'lvalue', 1) + ->lPop($key) + ->rPop($key) + ->rPoplPush($key, __FUNCTION__ . 'lkey1') + + // sets I/F + ->sAdd($key, 'sValue1') + ->sRemove($key, 'sValue1') + ->sPop($key) + ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sSize($key) + ->sContains($key, 'sValue1') + ->sInter($key, __FUNCTION__ . 'skey2') + ->sUnion($key, __FUNCTION__ . 'skey4') + ->sDiff($key, __FUNCTION__ . 'skey7') + ->sMembers($key) + ->sRandMember($key) + + // hash I/F + ->hSet($key, 'key1', 'value1') + ->hGet($key, 'key1') + ->hMGet($key, array('key1')) + ->hMSet($key, array('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); // substr + $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // ltrim + $this->assertTrue($ret[$i++] === FALSE); // lget + $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); // ssize + $this->assertTrue($ret[$i++] === FALSE); // scontains + $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($i, count($ret)); + + // hash + $key = 'hash'; + $ret = $this->redis->multi($mode) + ->delete($key) + ->hset($key, 'key1', 'hValue') + + // string I/F + ->get($key) + ->getset($key, 'value2') + ->append($key, 'append') + ->substr($key, 0, 8) + ->mget(array($key)) + ->incr($key) + ->incrBy($key, 1) + ->decr($key) + ->decrBy($key, 1) + + // lists I/F + ->rPush($key, 'lvalue') + ->lPush($key, 'lvalue') + ->lLen($key) + ->lPop($key) + ->lGetRange($key, 0, -1) + ->lTrim($key, 0, 1) + ->lGet($key, 0) + ->lSet($key, 0, "newValue") + ->lRemove($key, 'lvalue', 1) + ->lPop($key) + ->rPop($key) + ->rPoplPush($key, __FUNCTION__ . 'lkey1') + + // sets I/F + ->sAdd($key, 'sValue1') + ->sRemove($key, 'sValue1') + ->sPop($key) + ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sSize($key) + ->sContains($key, 'sValue1') + ->sInter($key, __FUNCTION__ . 'skey2') + ->sUnion($key, __FUNCTION__ . 'skey4') + ->sDiff($key, __FUNCTION__ . 'skey7') + ->sMembers($key) + ->sRandMember($key) + + // sorted sets I/F + ->zAdd($key, 1, 'zValue1') + ->zDelete($key, 'zValue1') + ->zIncrBy($key, 1, 'zValue1') + ->zRank($key, 'zValue1') + ->zRevRank($key, 'zValue1') + ->zRange($key, 0, -1) + ->zReverseRange($key, 0, -1) + ->zRangeByScore($key, 1, 2) + ->zCount($key, 0, -1) + ->zCard($key) + ->zScore($key, 'zValue1') + ->zDeleteRangeByRank($key, 1, 2) + ->zDeleteRangeByScore($key, 1, 2) + + ->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); // substr + $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // ltrim + $this->assertTrue($ret[$i++] === FALSE); // lget + $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); // ssize + $this->assertTrue($ret[$i++] === FALSE); // scontains + $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); // zdelete + $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); // zdeleterangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + + $this->assertEquals($i, count($ret)); + } + + public function testDifferentTypeString() { + $key = 'string'; + $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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); + $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); + $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $this->assertEquals(FALSE, $this->redis->lPop($key)); + $this->assertEquals(FALSE, $this->redis->rPop($key)); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); + + // sets I/F + $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sPop($key)); + $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sSize($key)); + $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); + $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); + $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zCard($key)); + $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + + // 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, array('key1'))); + $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'list'; + $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->substr($key, 0, 8)); + $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals(FALSE, $this->redis->incr($key)); + $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); + $this->assertEquals(FALSE, $this->redis->decr($key)); + $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + + // sets I/F + $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sPop($key)); + $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sSize($key)); + $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); + $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); + $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zCard($key)); + $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + + // 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, array('key1'))); + $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'set'; + $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->substr($key, 0, 8)); + $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals(FALSE, $this->redis->incr($key)); + $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); + $this->assertEquals(FALSE, $this->redis->decr($key)); + $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + + // 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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); + $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); + $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $this->assertEquals(FALSE, $this->redis->lPop($key)); + $this->assertEquals(FALSE, $this->redis->rPop($key)); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); + + // sorted sets I/F + $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zCard($key)); + $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + + // 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, array('key1'))); + $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'sortedset'; + $this->redis->del($key); + $this->assertEquals(1, $this->redis->zAdd($key, 0, '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->substr($key, 0, 8)); + $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals(FALSE, $this->redis->incr($key)); + $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); + $this->assertEquals(FALSE, $this->redis->decr($key)); + $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + + // 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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); + $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); + $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $this->assertEquals(FALSE, $this->redis->lPop($key)); + $this->assertEquals(FALSE, $this->redis->rPop($key)); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); + + // sets I/F + $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sPop($key)); + $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sSize($key)); + $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); + $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); + $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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, array('key1'))); + $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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'; + $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->substr($key, 0, 8)); + $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals(FALSE, $this->redis->incr($key)); + $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); + $this->assertEquals(FALSE, $this->redis->decr($key)); + $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + + // 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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); + $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); + $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $this->assertEquals(FALSE, $this->redis->lPop($key)); + $this->assertEquals(FALSE, $this->redis->rPop($key)); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); + + // sets I/F + $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sPop($key)); + $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sSize($key)); + $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); + $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); + $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zCard($key)); + $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + } + + 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, ""); + } + } + + private function checkSerializer($mode) { + + $this->redis->delete('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 = array('hello world', 42, TRUE, array('' => 1729)); + $this->redis->delete('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]); + + // lGetRange + $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); + + // lGet + $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); + $this->assertTrue($a[1] === $this->redis->lGet('key', 1)); + $this->assertTrue($a[2] === $this->redis->lGet('key', 2)); + $this->assertTrue($a[3] === $this->redis->lGet('key', 3)); + + // lRemove + $this->assertTrue($this->redis->lRemove('key', $a[3]) === 1); + $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lGetRange('key', 0, -1)); + + // lSet + $a[0] = array('k' => 'v'); // update + $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); + $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); + + // lInsert + $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], array(1,2,3)) === 4); + $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], array(4,5,6)) === 5); + + $a = array(array(1,2,3), $a[0], array(4,5,6), $a[1], $a[2]); + $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); + + // sAdd + $this->redis->delete('key'); + $s = array(1,'a', array(1,2,3), array('k' => 'v')); + + $this->assertTrue(1 === $this->redis->sAdd('key', $s[0])); + $this->assertTrue(1 === $this->redis->sAdd('key', $s[1])); + $this->assertTrue(1 === $this->redis->sAdd('key', $s[2])); + $this->assertTrue(1 === $this->redis->sAdd('key', $s[3])); + + // variadic sAdd + $this->redis->delete('k'); + $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); + $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); + + // sRemove + $this->assertTrue(1 === $this->redis->sRemove('key', $s[3])); + $this->assertTrue(0 === $this->redis->sRemove('key', $s[3])); + // variadic + $this->redis->delete('k'); + $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); + $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); + $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); + $this->assertTrue(FALSE === $this->redis->exists('k')); + + // sContains + $this->assertTrue(TRUE === $this->redis->sContains('key', $s[0])); + $this->assertTrue(TRUE === $this->redis->sContains('key', $s[1])); + $this->assertTrue(TRUE === $this->redis->sContains('key', $s[2])); + $this->assertTrue(FALSE === $this->redis->sContains('key', $s[3])); + unset($s[3]); + + // sMove + $this->redis->delete('tmp'); + $this->redis->sMove('key', 'tmp', $s[0]); + $this->assertTrue(FALSE === $this->redis->sContains('key', $s[0])); + $this->assertTrue(TRUE === $this->redis->sContains('tmp', $s[0])); + unset($s[0]); + + + // sorted sets + $z = array('z0', array('k' => 'v'), FALSE, NULL); + $this->redis->delete('key'); + + // zAdd + $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0])); + $this->assertTrue(1 === $this->redis->zAdd('key', 1, $z[1])); + $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2])); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3])); + + // zDelete + $this->assertTrue(1 === $this->redis->zDelete('key', $z[3])); + $this->assertTrue(0 === $this->redis->zDelete('key', $z[3])); + unset($z[3]); + + // check that zDelete doesn't crash with a missing parameter (GitHub issue #102): + $this->assertTrue(FALSE === @$this->redis->zDelete('key')); + + // variadic + $this->redis->delete('k'); + $this->redis->zAdd('k', 0, 'a'); + $this->redis->zAdd('k', 1, 'b'); + $this->redis->zAdd('k', 2, 'c'); + $this->assertTrue(2 === $this->redis->zDelete('k', 'a', 'c')); + $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); + $this->assertTrue($this->redis->zRange('k', 0, -1, true) == array('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(0 === $this->redis->zRank('key', $z[0])); + $this->assertTrue(1 === $this->redis->zRank('key', $z[1])); + $this->assertTrue(2 === $this->redis->zRank('key', $z[2])); + + // zRevRank + $this->assertTrue(2 === $this->redis->zRevRank('key', $z[0])); + $this->assertTrue(1 === $this->redis->zRevRank('key', $z[1])); + $this->assertTrue(0 === $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 = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); + $this->assertTrue(TRUE === $this->redis->mset($a)); + foreach($a as $k => $v) { + $this->assertTrue($this->redis->get($k) === $v); + } + + $a = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); + + // hSet + $this->redis->delete('key'); + foreach($a as $k => $v) { + $this->assertTrue(1 === $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->delete('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', array('x' => 'y')); + $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->getMultiple(array('a', 'b', 'c', 'd'))); + + // pipeline + $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', array(new stdClass, new stdClass)); + $x = $this->redis->get('x'); + $this->assertTrue(is_array($x)); + $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_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE); // set ok + $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok + } + + public function testDumpRestore() { + + if (version_compare($this->version, "2.5.0", "lt")) { + $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); + + // Throw some invalid lua at redis + $this->redis->eval("not-a-lua-script"); + + // Now we should have an error + $evalError = $this->redis->getLastError(); + $this->assertTrue(strlen($evalError) > 0); + + // test getLastError with a regular command + $this->redis->set('x', 'a'); + $this->assertFalse($this->redis->incr('x')); + $incrError = $this->redis->getLastError(); + $this->assertTrue($incrError !== $evalError); // error has changed + $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 = array(); + + 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", "lt")) { + $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", "lt")) { + $this->markTestSkipped(); + } + + // Basic single line response tests + $this->assertTrue(1 == $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('mylist'); + $this->redis->rpush('mylist', 'a'); + $this->redis->rpush('mylist', 'b'); + $this->redis->rpush('mylist', 'c'); + + // Make a set + $this->redis->del('myset'); + $this->redis->sadd('myset', 'd'); + $this->redis->sadd('myset', 'e'); + $this->redis->sadd('myset', 'f'); + + // Basic keys + $this->redis->set('key1', 'hello, world'); + $this->redis->set('key2', 'hello again!'); + + // Use a script to return our list, and verify its response + $list = $this->redis->eval("return redis.call('lrange', 'mylist', 0, -1)"); + $this->assertTrue($list === Array('a','b','c')); + + // Use a script to return our set + $set = $this->redis->eval("return redis.call('smembers', 'myset')"); + $this->assertTrue($set == Array('d','e','f')); + + // Test an empty MULTI BULK response + $this->redis->del('not-any-kind-of-list'); + $empty_resp = $this->redis->eval("return redis.call('lrange', 'not-any-kind-of-list', 0, -1)"); + $this->assertTrue(is_array($empty_resp) && empty($empty_resp)); + + // Now test a nested reply + $nested_script = " + return { + 1,2,3, { + redis.call('get', 'key1'), + redis.call('get', 'key2'), + redis.call('lrange', 'not-any-kind-of-list', 0, -1), + { + redis.call('smembers','myset'), + redis.call('lrange', 'mylist', 0, -1) + } + } + } + "; + + $expected = Array( + 1, 2, 3, Array( + 'hello, world', + 'hello again!', + Array(), + Array( + Array('d','e','f'), + Array('a','b','c') + ) + ) + ); + + // Now run our script, and check our values against each other + $eval_result = $this->redis->eval($nested_script); + $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; + + foreach(Array(Redis::PIPELINE, Redis::MULTI) as $mode) { + $this->redis->multi($mode); + for($i=0;$i<$num_scripts;$i++) { + $this->redis->eval($nested_script); + } + $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 = Array('k1','k2','k3','v1','v2','v3'); + $args_result = $this->redis->eval($args_script, $args_args, 3); + $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_args, 3); + + // Make sure our first three are prefixed + for($i=0;$iassertTrue($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", "lt")) { + $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(1 === $this->redis->eval($scr)); + $this->assertTrue(1 === $this->redis->evalsha($sha)); + } + + public function testSerialize() { + $vals = Array(1, 1.5, 'one', Array('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()) === 'Array'); + $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object'); + + $arr_serializers = Array(Redis::SERIALIZER_PHP); + if(defined('Redis::SERIALIZER_IGBINARY')) { + $arr_serializers[] = Redis::SERIALIZER_IGBINARY; + } + + foreach($arr_serializers as $mode) { + $arr_enc = Array(); + $arr_dec = Array(); + + 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 = Array( + 1,1.5,'one',Array('this','is','an','array') + ); + + $serializers = Array(Redis::SERIALIZER_PHP); + if(defined('Redis::SERIALIZER_IGBINARY')) { + $serializers[] = Redis::SERIALIZER_IGBINARY; + } + + foreach($serializers as $mode) { + $vals_enc = Array(); + + // 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_SERIALIZER, Redis::SERIALIZER_NONE); + $vals_enc[] = $this->redis->get($key); + } + + // Run through our array comparing values + for($i=0;$iredis->setOption(Redis::OPT_SERIALIZER, $mode); + $this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i])); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); + } + } + } + + 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 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->delete($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", "lt")) { + $this->markTestSkipped(); + } + + $time_arr = $this->redis->time(); + $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && + 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() === self::HOST); + $this->assertTrue($this->redis->getPort() === self::PORT); + $this->assertTrue($this->redis->getAuth() === self::AUTH); + } + + /** + * Scan and variants + */ + + protected function get_keyspace_count($str_db) { + $arr_info = $this->redis->info(); + $arr_info = $arr_info[$str_db]; + $arr_info = explode(',', $arr_info); + $arr_info = explode('=', $arr_info[0]); + return $arr_info[1]; + } + + public function testScan() { + if(version_compare($this->version, "2.8.0", "lt")) { + $this->markTestSkipped(); + return; + } + + // Key count + $i_key_count = $this->get_keyspace_count('db0'); + + // Have scan retry + $this->redis->setOption(Redis::OPT_SCAN, Redis::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); + } + + public function testHScan() { + if(version_compare($this->version, "2.8.0", "lt")) { + $this->markTestSkipped(); + return; + } + + // Never get empty sets + $this->redis->setOption(Redis::OPT_SCAN, Redis::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", "lt")) { + $this->markTestSkipped(); + return; + } + + $this->redis->setOption(Redis::OPT_SCAN, Redis::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", "lt")) { + $this->markTestSkipped(); + return; + } + + $this->redis->setOption(Redis::OPT_SCAN, Redis::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(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(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_SCAN, Redis::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(0, $i_p_score); + $this->assertEquals(0, $i_p_count); + } + + // + // HyperLogLog (PF) commands + // + + protected function createPFKey($str_key, $i_count) { + $arr_mems = Array(); + 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", "lt")) { + $this->markTestSkipped(); + return; + } + + $str_uniq = uniqid(); + $arr_mems = Array(); + + for($i=0;$i<1000;$i++) { + if($i%2 == 0) { + $arr_mems[] = $str_uniq . '-' . $i; + } else { + $arr_mems[] = $i; + } + } + + // How many keys to create + $i_keys = 10; + + // Iterate prefixing/serialization options + foreach(Array(Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP) as $str_ser) { + foreach(Array('', 'hl-key-prefix:') as $str_prefix) { + $arr_keys = Array(); + + // 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'); + } + } + } +} +?> diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 88d4725455..953fb4484d 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -1,4948 +1,37 @@ redis = $this->newInstance(); - $info = $this->redis->info(); - $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); - } - - private function newInstance() { - $r = new Redis(); - $r->connect(self::HOST, self::PORT); - - if(self::AUTH) { - $this->assertTrue($r->auth(self::AUTH)); - } - return $r; - } - - public function tearDown() - { - if($this->redis) { - $this->redis->close(); - } - // unset($this->redis); - } - - public function reset() - { - $this->setUp(); - $this->tearDown(); - } - - public function testMinimumVersion() - { - // Minimum server version required for tests - $this->assertTrue(version_compare($this->version, "2.4.0", "ge")); - } - - public function testPing() - { - - $this->assertEquals('+PONG', $this->redis->ping()); - - $count = 1000; - while($count --) { - $this->assertEquals('+PONG', $this->redis->ping()); - } - } - - public function testPipelinePublish() { - - $ret = $this->redis->pipeline() - ->publish('chan', 'msg') - ->exec(); - - $this->assertTrue(is_array($ret) && count($ret) === 1 && $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", "lt")) { - $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", Array($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(Array($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->delete('key'); - $this->assertTrue(0 === $this->redis->getBit('key', 0)); - $this->assertTrue(FALSE === $this->redis->getBit('key', -1)); - $this->assertTrue(0 === $this->redis->getBit('key', 100000)); - - $this->redis->set('key', "\xff"); - for($i = 0; $i < 8; $i++) { - $this->assertTrue(1 === $this->redis->getBit('key', $i)); - } - $this->assertTrue(0 === $this->redis->getBit('key', 8)); - - // negative offset doesn't work - $this->assertTrue(FALSE === $this->redis->setBit('key', -1, 0)); - $this->assertTrue(1 === $this->redis->getBit('key', 0)); - - // change bit 0 - $this->assertTrue(1 === $this->redis->setBit('key', 0, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 0, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 0)); - $this->assertTrue("\x7f" === $this->redis->get('key')); - - // change bit 1 - $this->assertTrue(1 === $this->redis->setBit('key', 1, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 1, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 1)); - $this->assertTrue("\x3f" === $this->redis->get('key')); - - // change bit > 1 - $this->assertTrue(1 === $this->redis->setBit('key', 2, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 2, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 2)); - $this->assertTrue("\x1f" === $this->redis->get('key')); - - // values above 1 are changed to 1 but don't overflow on bits to the right. - $this->assertTrue(0 === $this->redis->setBit('key', 0, 0xff)); - $this->assertTrue("\x9f" === $this->redis->get('key')); - - // Verify valid offset ranges - $this->assertFalse($this->redis->getBit('key', -1)); - $this->assertFalse($this->redis->getBit('key', 4294967296)); - $this->assertFalse($this->redis->setBit('key', -1, 1)); - $this->assertFalse($this->redis->setBit('key', 4294967296, 1)); - } - - public function testBitPos() { - if(version_compare($this->version, "2.8.7", "lt")) { - $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', 1, 0), 8); - $this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 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->delete('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->delete('key'); - $this->redis->delete('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->delete('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->delete('key'); - $data = gzcompress('value1'); - $this->assertEquals(True, $this->redis->set('key', $data)); - $this->assertEquals('value1', gzuncompress($this->redis->get('key'))); - - $this->redis->delete('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', 'lt')) { - $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); - - /* 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',Array('nx'))); - $this->assertEquals($this->redis->get('foo'), 'bar'); - $this->assertFalse($this->redis->set('foo','bar',Array('nx'))); - - /* Set if exists */ - $this->assertTrue($this->redis->set('foo','bar',Array('xx'))); - $this->assertEquals($this->redis->get('foo'), 'bar'); - $this->redis->del('foo'); - $this->assertFalse($this->redis->set('foo','bar',Array('xx'))); - - /* Set with a TTL */ - $this->assertTrue($this->redis->set('foo','bar',Array('ex'=>100))); - $this->assertEquals($this->redis->ttl('foo'), 100); - - /* Set with a PTTL */ - $this->assertTrue($this->redis->set('foo','bar',Array('px'=>100000))); - $this->assertTrue(100000 - $this->redis->pttl('foo') < 1000); - - /* Set if exists, with a TTL */ - $this->assertTrue($this->redis->set('foo','bar',Array('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', Array('nx', 'ex'=>110))); - $this->assertEquals($this->redis->ttl('foo'), 110); - $this->assertEquals($this->redis->get('foo'), 'bar'); - $this->assertFalse($this->redis->set('foo','bar', Array('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', Array('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); - } - - public function testGetSet() { - $this->redis->delete('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->assertTrue($this->redis->exists($k)); - } - } - - public function testRename() { - - // strings - $this->redis->delete('key0'); - $this->redis->set('key0', 'val0'); - $this->redis->renameKey('key0', 'key1'); - $this->assertTrue($this->redis->get('key0') === FALSE); - $this->assertTrue($this->redis->get('key1') === 'val0'); - - - // lists - $this->redis->delete('key0'); - $this->redis->lPush('key0', 'val0'); - $this->redis->lPush('key0', 'val1'); - $this->redis->renameKey('key0', 'key1'); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); - $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1', 'val0')); - - // variadic - $this->redis->delete('key0'); - $this->assertTrue(3 === $this->redis->lPush('key0', 'val0', 'val1', 'val2')); - $this->assertTrue(array('val2', 'val1', 'val0') === $this->redis->lrange('key0', 0, -1)); - - $this->redis->delete('key0'); - $this->assertTrue(3 === $this->redis->rPush('key0', 'val0', 'val1', 'val2')); - $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->lrange('key0', 0, -1)); - } - - public function testRenameNx() { - - // strings - $this->redis->delete('key0', 'key1'); - $this->redis->set('key0', 'val0'); - $this->redis->set('key1', 'val1'); - $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); - $this->assertTrue($this->redis->get('key0') === 'val0'); - $this->assertTrue($this->redis->get('key1') === 'val1'); - - // lists - $this->redis->delete('key0'); - $this->redis->delete('key1'); - $this->redis->lPush('key0', 'val0'); - $this->redis->lPush('key0', 'val1'); - $this->redis->lPush('key1', 'val1-0'); - $this->redis->lPush('key1', 'val1-1'); - $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array('val1', 'val0')); - $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1-1', 'val1-0')); - - $this->redis->delete('key2'); - $this->assertTrue($this->redis->renameNx('key0', 'key2') === TRUE); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); - $this->assertTrue($this->redis->lGetRange('key2', 0, -1) === array('val1', 'val0')); - - } - - public function testMultiple() { - - $this->redis->delete('k1'); - $this->redis->delete('k2'); - $this->redis->delete('k3'); - - $this->redis->set('k1', 'v1'); - $this->redis->set('k2', 'v2'); - $this->redis->set('k3', 'v3'); - $this->redis->set(1, 'test'); - - $this->assertEquals(array('v1'), $this->redis->getMultiple(array('k1'))); - $this->assertEquals(array('v1', 'v3', false), $this->redis->getMultiple(array('k1', 'k3', 'NoKey'))); - $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - - $this->redis->set('k5', '$1111111111'); - $this->assertEquals(array(0 => '$1111111111'), $this->redis->getMultiple(array('k5'))); - - $this->assertEquals(array(0 => 'test'), $this->redis->getMultiple(array(1))); // non-string - } - - public function testMultipleBin() { - - $this->redis->delete('k1'); - $this->redis->delete('k2'); - $this->redis->delete('k3'); - - $this->redis->set('k1', gzcompress('v1')); - $this->redis->set('k2', gzcompress('v2')); - $this->redis->set('k3', gzcompress('v3')); - - $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - - } - - public function testSetTimeout() { - - $this->redis->delete('key'); - $this->redis->set('key', 'value'); - $this->assertEquals('value', $this->redis->get('key')); - $this->redis->setTimeout('key', 1); - $this->assertEquals('value', $this->redis->get('key')); - sleep(2); - $this->assertEquals(False, $this->redis->get('key')); - } - - public function testExpireAt() { - - $this->redis->delete('key'); - $this->redis->set('key', 'value'); - $now = time(NULL); - $this->redis->expireAt('key', $now + 1); - $this->assertEquals('value', $this->redis->get('key')); - sleep(2); - $this->assertEquals(FALSE, $this->redis->get('key')); - } - - public function testSetEx() { - - $this->redis->delete('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 testSetNX() { - - $this->redis->set('key', 42); - $this->assertTrue($this->redis->setnx('key', 'err') === FALSE); - $this->assertTrue($this->redis->get('key') === '42'); - - $this->redis->delete('key'); - $this->assertTrue($this->redis->setnx('key', '42') === TRUE); - $this->assertTrue($this->redis->get('key') === '42'); - } - - public function testExpireAtWithLong() { - $longExpiryTimeExceedingInt = 3153600000; - $this->redis->delete('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->incr('key', 3); - $this->assertEquals(5, (int)$this->redis->get('key')); - - $this->redis->incrBy('key', 3); - $this->assertEquals(8, (int)$this->redis->get('key')); - - $this->redis->incrBy('key', 1); - $this->assertEquals(9, (int)$this->redis->get('key')); - - $this->redis->incrBy('key', -1); - $this->assertEquals(8, (int)$this->redis->get('key')); - - $this->redis->delete('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')); - } - - public function testIncrByFloat() - { - // incrbyfloat is new in 2.6.0 - if (version_compare($this->version, "2.5.0", "lt")) { - $this->markTestSkipped(); - } - - $this->redis->delete('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.8', $this->redis->get('key')); - $this->redis->setOption(Redis::OPT_PREFIX, ''); - $this->assertTrue($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->decr('key', 2); - $this->assertEquals(1, (int)$this->redis->get('key')); - - $this->redis->decr('key', 2); - $this->assertEquals(-1, (int)$this->redis->get('key')); - - $this->redis->decrBy('key', 2); - $this->assertEquals(-3, (int)$this->redis->get('key')); - - $this->redis->decrBy('key', 1); - $this->assertEquals(-4, (int)$this->redis->get('key')); - - $this->redis->decr('key', -10); - $this->assertEquals(6, (int)$this->redis->get('key')); - } - - public function testExists() - { - - $this->redis->delete('key'); - $this->assertFalse($this->redis->exists('key')); - $this->redis->set('key', 'val'); - $this->assertEquals(True, $this->redis->exists('key')); - } - - public function testGetKeys() - { - - $pattern = 'getKeys-test-'; - for($i = 1; $i < 10; $i++) { - $this->redis->set($pattern.$i, $i); - } - $this->redis->delete($pattern.'3'); - $keys = $this->redis->getKeys($pattern.'*'); - - $this->redis->set($pattern.'3', 'something'); - - $keys2 = $this->redis->getKeys($pattern.'*'); - - $this->assertEquals((count($keys) + 1), count($keys2)); - - // empty array when no key matches - $this->assertEquals(array(), $this->redis->getKeys(rand().rand().rand().'*')); - } - - public function testDelete() - { - $key = 'key' . rand(); - $this->redis->set($key, 'val'); - $this->assertEquals('val', $this->redis->get($key)); - $this->assertEquals(1, $this->redis->delete($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->delete('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->delete('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->delete('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->delete(array('x', 'y'))); - - } - - public function testType() - { - // 0 => none, (key didn't exist) - // 1=> string, - // 2 => set, - // 3 => list, - // 4 => zset, - // 5 => hash - - // 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->delete('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->delete(123); - $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); - $this->assertTrue(array('val0') === $this->redis->sMembers(123)); - - // zset - $this->redis->delete('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->delete('keyHash'); - $this->redis->hSet('keyHash', 'key0', 'val0'); - $this->redis->hSet('keyHash', 'key1', 'val1'); - $this->assertEquals(Redis::REDIS_HASH, $this->redis->type('keyHash')); - - //None - $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->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', 0, 3) === 'This'); - $this->assertTrue($this->redis->getRange('key', -6, -1) === 'string'); - $this->assertTrue($this->redis->getRange('key', -6, 100000) === '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->delete('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->delete('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->delete('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->delete('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->delete('list'); - $this->redis->lPush('list', 'val1'); - $this->redis->lPush('list', 'val2'); - $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val2')); - $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val1')); - - $this->redis->delete('list'); - $this->redis->lPush('list', 'val1'); - $this->redis->lPush('list', 'val2'); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val1')); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val2')); - - // blocking blpop, brpop - $this->redis->delete('list'); - $this->assertTrue($this->redis->blPop(array('list'), 1) === array()); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array()); - - // TODO: fix this broken test. -// $this->redis->delete('list'); -// $params = array( -// 0 => array("pipe", "r"), -// 1 => array("pipe", "w"), -// 2 => array("file", "/dev/null", "w") -// ); -// if(function_exists('proc_open')) { -// $env = array('PHPREDIS_key' =>'list', 'PHPREDIS_value' => 'value'); -// $process = proc_open('php', $params, $pipes, '/tmp', $env); -// -// if (is_resource($process)) { -// fwrite($pipes[0], 'connect("'.self::HOST.'", '.self::PORT.'); -// if("'.addslashes(self::AUTH).'") { -// $r->auth("'.addslashes(self::AUTH).'"); -// } -// $r->lPush($_ENV["PHPREDIS_key"], $_ENV["PHPREDIS_value"]); -// ?' . '>'); -// -// fclose($pipes[0]); -// fclose($pipes[1]); -// $re = proc_close($process); -// -// $this->assertTrue($this->redis->blPop(array('list'), 5) === array("list", "value")); -// } -// } - - } - - public function testlSize() - { - - $this->redis->delete('list'); - - $this->redis->lPush('list', 'val'); - $this->assertEquals(1, $this->redis->lSize('list')); - - $this->redis->lPush('list', 'val2'); - $this->assertEquals(2, $this->redis->lSize('list')); - - $this->assertEquals('val2', $this->redis->lPop('list')); - $this->assertEquals(1, $this->redis->lSize('list')); - - $this->assertEquals('val', $this->redis->lPop('list')); - $this->assertEquals(0, $this->redis->lSize('list')); - - $this->assertEquals(FALSE, $this->redis->lPop('list')); - $this->assertEquals(0, $this->redis->lSize('list')); // empty returns 0 - - $this->redis->delete('list'); - $this->assertEquals(0, $this->redis->lSize('list')); // non-existent returns 0 - - $this->redis->set('list', 'actually not a list'); - $this->assertEquals(FALSE, $this->redis->lSize('list'));// not a list returns FALSE - } - - //lInsert, lPopx, rPopx - public function testlPopx() { - //test lPushx/rPushx - $this->redis->delete('keyNotExists'); - $this->assertTrue($this->redis->lPushx('keyNotExists', 'value') === 0); - $this->assertTrue($this->redis->rPushx('keyNotExists', 'value') === 0); - - $this->redis->delete('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->lGetRange('key', 0, -1) === array('val1', 'val0', 'val2')); - - //test linsert - $this->redis->delete('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->lGetRange('key', 0, -1) === array('val2', 'val0', 'val1')); - } - - // ltrim, lsize, lpop - public function testlistTrim() - { - - $this->redis->delete('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->listTrim('list', 0, 2)); - $this->assertEquals(3, $this->redis->lSize('list')); - - $this->redis->listTrim('list', 0, 0); - $this->assertEquals(1, $this->redis->lSize('list')); - $this->assertEquals('val4', $this->redis->lPop('list')); - - $this->assertEquals(TRUE, $this->redis->listTrim('list', 10, 10000)); - $this->assertEquals(TRUE, $this->redis->listTrim('list', 10000, 10)); - - // test invalid type - $this->redis->set('list', 'not a list...'); - $this->assertEquals(FALSE, $this->redis->listTrim('list', 0, 2)); - - } - - 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->delete('person:id'); - foreach(array(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(array('1','2','3'), $this->redis->sortAsc('some-item')); - $this->assertEquals(array('3','2','1'), $this->redis->sortDesc('some-item')); - $this->assertEquals(array('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(); - - $this->assertTrue(FALSE === $this->redis->sortAsc(NULL)); - - // sort by age and get IDs - $byAgeAsc = array('3','1','2','4'); - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*')); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'sort' => 'asc'))); - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL)); // check that NULL works. - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sort('person:id', array('sort' => 'asc'))); - - // sort by age and get names - $byAgeAsc = array('Carol','Alice','Bob','Dave'); - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*')); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'))); - - $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 2)); - $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 2), 'sort' => 'asc'))); - - $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); - $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(1, 2), 'sort' => 'asc'))); - $this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 4)))); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, "4")))); // with strings - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array("0", 4)))); - $this->assertEquals(array(), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. - - // sort by salary and get ages - $agesBySalaryAsc = array('34', '27', '25', '41'); - $this->assertEquals($agesBySalaryAsc, $this->redis->sortAsc('person:id', 'person:salary_*', 'person:age_*')); - $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'))); - - $agesAndSalaries = $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => array('person:age_*', 'person:salary_*'), 'sort' => 'asc')); - $this->assertEquals(array('34', '2000', '27', '2500', '25', '2800', '41', '3100'), $agesAndSalaries); - - - // sort non-alpha doesn't change all-string lists - // list → [ghi, def, abc] - $list = array('abc', 'def', 'ghi'); - $this->redis->delete('list'); - foreach($list as $i) { - $this->redis->lPush('list', $i); - } - - // SORT list → [ghi, def, abc] - if (version_compare($this->version, "2.5.0", "lt")) { - $this->assertEquals(array_reverse($list), $this->redis->sortAsc('list')); - $this->assertEquals(array_reverse($list), $this->redis->sort('list', array('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->sortAscAlpha('list')); - $this->assertEquals($list, $this->redis->sort('list', array('sort' => 'asc', 'alpha' => TRUE))); - } - - public function testSortDesc() { - - $this->setupSort(); - - // sort by age and get IDs - $byAgeDesc = array('4','2','1','3'); - $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*')); - - // sort by age and get names - $byAgeDesc = array('Dave', 'Bob', 'Alice', 'Carol'); - $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*')); - - $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 0, 2)); - $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 1, 2)); - - // sort by salary and get ages - $agesBySalaryDesc = array('41', '25', '27', '34'); - $this->assertEquals($agesBySalaryDesc, $this->redis->sortDesc('person:id', 'person:salary_*', 'person:age_*')); - - // sort non-alpha doesn't change all-string lists - $list = array('def', 'abc', 'ghi'); - $this->redis->delete('list'); - foreach($list as $i) { - $this->redis->lPush('list', $i); - } - - // SORT list → [ghi, abc, def] - if (version_compare($this->version, "2.5.0", "lt")) { - $this->assertEquals(array_reverse($list), $this->redis->sortDesc('list')); - } 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(array('ghi', 'def', 'abc'), $this->redis->sortDescAlpha('list')); - } - - // LINDEX - public function testlGet() { - - $this->redis->delete('list'); - - $this->redis->lPush('list', 'val'); - $this->redis->lPush('list', 'val2'); - $this->redis->lPush('list', 'val3'); - - $this->assertEquals('val3', $this->redis->lGet('list', 0)); - $this->assertEquals('val2', $this->redis->lGet('list', 1)); - $this->assertEquals('val', $this->redis->lGet('list', 2)); - $this->assertEquals('val', $this->redis->lGet('list', -1)); - $this->assertEquals('val2', $this->redis->lGet('list', -2)); - $this->assertEquals('val3', $this->redis->lGet('list', -3)); - $this->assertEquals(FALSE, $this->redis->lGet('list', -4)); - - $this->redis->rPush('list', 'val4'); - $this->assertEquals('val4', $this->redis->lGet('list', 3)); - $this->assertEquals('val4', $this->redis->lGet('list', -1)); - } - - // lRem testing - public function testlRemove() { - $this->redis->delete('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->lRemove('list', 'b', 2); - // ['c', 'c', 'c', 'a'] - $this->assertEquals(2, $return); - $this->assertEquals('c', $this->redis->lGET('list', 0)); - $this->assertEquals('c', $this->redis->lGET('list', 1)); - $this->assertEquals('c', $this->redis->lGET('list', 2)); - $this->assertEquals('a', $this->redis->lGET('list', 3)); - - $this->redis->delete('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->lRemove('list', 'c', -2); - // ['c', 'b', 'b', 'a'] - $this->assertEquals(2, $return); - $this->assertEquals('c', $this->redis->lGET('list', 0)); - $this->assertEquals('b', $this->redis->lGET('list', 1)); - $this->assertEquals('b', $this->redis->lGET('list', 2)); - $this->assertEquals('a', $this->redis->lGET('list', 3)); - - // remove each element - $this->assertEquals(1, $this->redis->lRemove('list', 'a', 0)); - $this->assertEquals(0, $this->redis->lRemove('list', 'x', 0)); - $this->assertEquals(2, $this->redis->lRemove('list', 'b', 0)); - $this->assertEquals(1, $this->redis->lRemove('list', 'c', 0)); - $this->assertEquals(FALSE, $this->redis->get('list')); - - $this->redis->set('list', 'actually not a list'); - $this->assertEquals(FALSE, $this->redis->lRemove('list', 'x')); - - } - - public function testsAdd() - { - $this->redis->delete('set'); - - $this->assertEquals(1, $this->redis->sAdd('set', 'val')); - $this->assertEquals(0, $this->redis->sAdd('set', 'val')); - - $this->assertTrue($this->redis->sContains('set', 'val')); - $this->assertFalse($this->redis->sContains('set', 'val2')); - - $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); - - $this->assertTrue($this->redis->sContains('set', 'val2')); - } - public function testsSize() - { - $this->redis->delete('set'); - - $this->assertEquals(1, $this->redis->sAdd('set', 'val')); - - $this->assertEquals(1, $this->redis->sSize('set')); - - $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); - - $this->assertEquals(2, $this->redis->sSize('set')); - } - - public function testsRemove() - { - $this->redis->delete('set'); - - $this->redis->sAdd('set', 'val'); - $this->redis->sAdd('set', 'val2'); - - $this->redis->sRemove('set', 'val'); - - $this->assertEquals(1, $this->redis->sSize('set')); - - $this->redis->sRemove('set', 'val2'); - - $this->assertEquals(0, $this->redis->sSize('set')); - } - - public function testsMove() - { - $this->redis->delete('set0'); - $this->redis->delete('set1'); - - $this->redis->sAdd('set0', 'val'); - $this->redis->sAdd('set0', 'val2'); - - $this->assertTrue($this->redis->sMove('set0', 'set1', 'val')); - $this->assertFalse($this->redis->sMove('set0', 'set1', 'val')); - $this->assertFalse($this->redis->sMove('set0', 'set1', 'val-what')); - - $this->assertEquals(1, $this->redis->sSize('set0')); - $this->assertEquals(1, $this->redis->sSize('set1')); - - $this->assertEquals(array('val2'), $this->redis->sGetMembers('set0')); - $this->assertEquals(array('val'), $this->redis->sGetMembers('set1')); - } - - public function testsPop() - { - $this->redis->delete('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(1 === $this->redis->sSize('set0')); - $this->assertTrue($v0 === 'val' || $v0 === 'val2'); - $v1 = $this->redis->sPop('set0'); - $this->assertTrue(0 === $this->redis->sSize('set0')); - $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); - - $this->assertTrue($this->redis->sPop('set0') === FALSE); - } - - public function testsRandMember() { - $this->redis->delete('set0'); - $this->assertTrue($this->redis->sRandMember('set0') === FALSE); - - $this->redis->sAdd('set0', 'val'); - $this->redis->sAdd('set0', 'val2'); - - $got = array(); - while(true) { - $v = $this->redis->sRandMember('set0'); - $this->assertTrue(2 === $this->redis->sSize('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->delete('set0'); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::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_SERIALIZER, Redis::SERIALIZER_NONE); - } - - public function testSRandMemberWithCount() { - // Make sure the set is nuked - $this->redis->delete('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 - // - - $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 testsContains() - { - $this->redis->delete('set'); - - $this->redis->sAdd('set', 'val'); - - $this->assertTrue($this->redis->sContains('set', 'val')); - $this->assertFalse($this->redis->sContains('set', 'val2')); - } - - public function testsGetMembers() - { - $this->redis->delete('set'); - - $this->redis->sAdd('set', 'val'); - $this->redis->sAdd('set', 'val2'); - $this->redis->sAdd('set', 'val3'); - - $array = array('val', 'val2', 'val3'); - - $sGetMembers = $this->redis->sGetMembers('set'); - sort($sGetMembers); - $this->assertEquals($array, $sGetMembers); - - $sMembers = $this->redis->sMembers('set'); - sort($sMembers); - $this->assertEquals($array, $sMembers); // test alias - } - - public function testlSet() { - - $this->redis->delete('list'); - $this->redis->lPush('list', 'val'); - $this->redis->lPush('list', 'val2'); - $this->redis->lPush('list', 'val3'); - - $this->assertEquals($this->redis->lGet('list', 0), 'val3'); - $this->assertEquals($this->redis->lGet('list', 1), 'val2'); - $this->assertEquals($this->redis->lGet('list', 2), 'val'); - - $this->assertEquals(TRUE, $this->redis->lSet('list', 1, 'valx')); - - $this->assertEquals($this->redis->lGet('list', 0), 'val3'); - $this->assertEquals($this->redis->lGet('list', 1), 'valx'); - $this->assertEquals($this->redis->lGet('list', 2), 'val'); - - } - - public function testsInter() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 - - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); - foreach($x as $i) { - $this->redis->sAdd('x', $i); - } - - $y = array(1,2,3,5,7,11,13,17,19,23); - foreach($y as $i) { - $this->redis->sAdd('y', $i); - } - - $z = array(1,4,9,16,25); - foreach($z as $i) { - $this->redis->sAdd('z', $i); - } - - $t = array(2,5,10,17,26); - foreach($t as $i) { - $this->redis->sAdd('t', $i); - } - - $xy = $this->redis->sInter('x', 'y'); // odd prime numbers - foreach($xy as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($x, $y))); - } - $xy = $this->redis->sInter(array('x', 'y')); // odd prime numbers, as array. - foreach($xy as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($x, $y))); - } - - $yz = $this->redis->sInter('y', 'z'); // set of odd squares - foreach($yz as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($y, $z))); - } - $yz = $this->redis->sInter(array('y', 'z')); // set of odd squares, as array - foreach($yz as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($y, $z))); - } - - $zt = $this->redis->sInter('z', 't'); // prime squares - $this->assertTrue($zt === array()); - $zt = $this->redis->sInter(array('z', 't')); // prime squares, as array - $this->assertTrue($zt === array()); - - $xyz = $this->redis->sInter('x', 'y', 'z');// odd prime squares - $this->assertTrue($xyz === array('1')); - - $xyz = $this->redis->sInter(array('x', 'y', 'z'));// odd prime squares, with an array as a parameter - $this->assertTrue($xyz === array('1')); - - $nil = $this->redis->sInter(); - $this->assertTrue($nil === FALSE); - $nil = $this->redis->sInter(array()); - $this->assertTrue($nil === FALSE); - } - - public function testsInterStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 - - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); - foreach($x as $i) { - $this->redis->sAdd('x', $i); - } - - $y = array(1,2,3,5,7,11,13,17,19,23); - foreach($y as $i) { - $this->redis->sAdd('y', $i); - } - - $z = array(1,4,9,16,25); - foreach($z as $i) { - $this->redis->sAdd('z', $i); - } - - $t = array(2,5,10,17,26); - foreach($t as $i) { - $this->redis->sAdd('t', $i); - } - - $count = $this->redis->sInterStore('k', 'x', 'y'); // odd prime numbers - $this->assertEquals($count, $this->redis->sSize('k')); - foreach(array_intersect($x, $y) as $i) { - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $count = $this->redis->sInterStore('k', 'y', 'z'); // set of odd squares - $this->assertEquals($count, $this->redis->sSize('k')); - foreach(array_intersect($y, $z) as $i) { - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $count = $this->redis->sInterStore('k', 'z', 't'); // squares of the form n^2 + 1 - $this->assertEquals($count, 0); - $this->assertEquals($count, $this->redis->sSize('k')); - - $this->redis->delete('z'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // only z missing, expect 0. - $this->assertTrue($xyz === 0); - - $this->redis->delete('y'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // y and z missing, expect 0. - $this->assertTrue($xyz === 0); - - $this->redis->delete('x'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // x y and z ALL missing, expect 0. - $this->assertTrue($xyz === 0); - - $o = $this->redis->sInterStore('k'); - $this->assertTrue($o === FALSE); // error, wrong parameter count - } - - public function testsUnion() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 - - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); - foreach($x as $i) { - $this->redis->sAdd('x', $i); - } - - $y = array(1,2,3,5,7,11,13,17,19,23); - foreach($y as $i) { - $this->redis->sAdd('y', $i); - } - - $z = array(1,4,9,16,25); - foreach($z as $i) { - $this->redis->sAdd('z', $i); - } - - $t = array(2,5,10,17,26); - foreach($t as $i) { - $this->redis->sAdd('t', $i); - } - - $xy = $this->redis->sUnion('x', 'y'); // x U y - foreach($xy as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($x, $y))); - } - - $yz = $this->redis->sUnion('y', 'z'); // y U Z - foreach($yz as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($y, $z))); - } - - $zt = $this->redis->sUnion('z', 't'); // z U t - foreach($zt as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($z, $t))); - } - - $xyz = $this->redis->sUnion('x', 'y', 'z'); // x U y U z - foreach($xyz as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($x, $y, $z))); - } - - $nil = $this->redis->sUnion(); - $this->assertTrue($nil === FALSE); - } - - public function testsUnionStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 - - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); - foreach($x as $i) { - $this->redis->sAdd('x', $i); - } - - $y = array(1,2,3,5,7,11,13,17,19,23); - foreach($y as $i) { - $this->redis->sAdd('y', $i); - } - - $z = array(1,4,9,16,25); - foreach($z as $i) { - $this->redis->sAdd('z', $i); - } - - $t = array(2,5,10,17,26); - foreach($t as $i) { - $this->redis->sAdd('t', $i); - } - - $count = $this->redis->sUnionStore('k', 'x', 'y'); // x U y - $xy = array_unique(array_merge($x, $y)); - $this->assertEquals($count, count($xy)); - foreach($xy as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $count = $this->redis->sUnionStore('k', 'y', 'z'); // y U z - $yz = array_unique(array_merge($y, $z)); - $this->assertEquals($count, count($yz)); - foreach($yz as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $count = $this->redis->sUnionStore('k', 'z', 't'); // z U t - $zt = array_unique(array_merge($z, $t)); - $this->assertEquals($count, count($zt)); - foreach($zt as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $xyz = array_unique(array_merge($x, $y, $z)); - $this->assertEquals($count, count($xyz)); - foreach($xyz as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $this->redis->delete('x'); // x missing now - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === count(array_unique(array_merge($y, $z)))); - - $this->redis->delete('y'); // x and y missing - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === count(array_unique($z))); - - $this->redis->delete('z'); // x, y, and z ALL missing - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === 0); - - $count = $this->redis->sUnionStore('k'); // Union on nothing... - $this->assertTrue($count === FALSE); - } - - public function testsDiff() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 - - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); - foreach($x as $i) { - $this->redis->sAdd('x', $i); - } - - $y = array(1,2,3,5,7,11,13,17,19,23); - foreach($y as $i) { - $this->redis->sAdd('y', $i); - } - - $z = array(1,4,9,16,25); - foreach($z as $i) { - $this->redis->sAdd('z', $i); - } - - $t = array(2,5,10,17,26); - foreach($t as $i) { - $this->redis->sAdd('t', $i); - } - - $xy = $this->redis->sDiff('x', 'y'); // x U y - foreach($xy as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($x, $y))); - } - - $yz = $this->redis->sDiff('y', 'z'); // y U Z - foreach($yz as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($y, $z))); - } - - $zt = $this->redis->sDiff('z', 't'); // z U t - foreach($zt as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($z, $t))); - } - - $xyz = $this->redis->sDiff('x', 'y', 'z'); // x U y U z - foreach($xyz as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($x, $y, $z))); - } - - $nil = $this->redis->sDiff(); - $this->assertTrue($nil === FALSE); - } - - public function testsDiffStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 - - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); - foreach($x as $i) { - $this->redis->sAdd('x', $i); - } - - $y = array(1,2,3,5,7,11,13,17,19,23); - foreach($y as $i) { - $this->redis->sAdd('y', $i); - } - - $z = array(1,4,9,16,25); - foreach($z as $i) { - $this->redis->sAdd('z', $i); - } - - $t = array(2,5,10,17,26); - foreach($t as $i) { - $this->redis->sAdd('t', $i); - } - - $count = $this->redis->sDiffStore('k', 'x', 'y'); // x - y - $xy = array_unique(array_diff($x, $y)); - $this->assertEquals($count, count($xy)); - foreach($xy as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $count = $this->redis->sDiffStore('k', 'y', 'z'); // y - z - $yz = array_unique(array_diff($y, $z)); - $this->assertEquals($count, count($yz)); - foreach($yz as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $count = $this->redis->sDiffStore('k', 'z', 't'); // z - t - $zt = array_unique(array_diff($z, $t)); - $this->assertEquals($count, count($zt)); - foreach($zt as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $xyz = array_unique(array_diff($x, $y, $z)); - $this->assertEquals($count, count($xyz)); - foreach($xyz as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); - } - - $this->redis->delete('x'); // x missing now - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); - - $this->redis->delete('y'); // x and y missing - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); - - $this->redis->delete('z'); // x, y, and z ALL missing - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); - - $count = $this->redis->sDiffStore('k'); // diff on nothing... - $this->assertTrue($count === FALSE); - } - - public function testlGetRange() { - - $this->redis->delete('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->lGetRange('list', 0, 0), array('val3')); - $this->assertEquals($this->redis->lGetRange('list', 0, 1), array('val3', 'val2')); - $this->assertEquals($this->redis->lGetRange('list', 0, 2), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lGetRange('list', 0, 3), array('val3', 'val2', 'val')); - - $this->assertEquals($this->redis->lGetRange('list', 0, -1), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lGetRange('list', 0, -2), array('val3', 'val2')); - $this->assertEquals($this->redis->lGetRange('list', -2, -1), array('val2', 'val')); - - $this->redis->delete('list'); - $this->assertEquals($this->redis->lGetRange('list', 0, -1), array()); - } - - -// public function testsave() { -// $this->assertTrue($this->redis->save() === TRUE); // don't really know how else to test this... -// } -// public function testbgSave() { -// // let's try to fill the DB and then bgSave twice. We expect the second one to fail. -// for($i = 0; $i < 10e+4; $i++) { -// $s = md5($i); -// $this->redis->set($s, $s); -// } -// $this->assertTrue($this->redis->bgSave() === TRUE); // the first one should work. -// $this->assertTrue($this->redis->bgSave() === FALSE); // the second one should fail (still working on the first one) -// } -// -// public function testlastSave() { -// while(!$this->redis->save()) { -// sleep(1); -// } -// $t_php = microtime(TRUE); -// $t_redis = $this->redis->lastSave(); -// -// $this->assertTrue($t_php - $t_redis < 10000); // check that it's approximately what we've measured in PHP. -// } -// -// public function testflushDb() { -// $this->redis->set('x', 'y'); -// $this->assertTrue($this->redis->flushDb()); -// $this->assertTrue($this->redis->getKeys('*') === array()); -// } -// -// public function testflushAll() { -// $this->redis->set('x', 'y'); -// $this->assertTrue($this->redis->flushAll()); -// $this->assertTrue($this->redis->getKeys('*') === array()); -// } - - 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->setTimeout('x', 5); - for($i = 5; $i > 0; $i--) { - $this->assertEquals($i, $this->redis->ttl('x')); - sleep(1); - } - - // 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", "gte")) { - $this->redis->del('x'); - $this->assertEquals($this->redis->ttl('x'), -2); - } - } - - public function testPersist() { - $this->redis->set('x', 'y'); - $this->redis->setTimeout('x', 100); - $this->assertTrue(TRUE === $this->redis->persist('x')); // true if there is a timeout - $this->assertTrue(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. - $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout - $this->redis->delete('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', 'lt')) { - $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_slaves, 100), $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+1, 100) < $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(-1, 20)); - } - - public function testinfo() { - $info = $this->redis->info(); - - $keys = array( - "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", "lt")) { - 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($k, array_keys($info))); - } - } - - public function testInfoCommandStats() { - - // INFO COMMANDSTATS is new in 2.6.0 - if (version_compare($this->version, "2.5.0", "lt")) { - $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 testMset() { - $this->redis->delete('x', 'y', 'z'); // remove x y z - $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z - - $this->redis->delete('x'); // delete just x - $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z - - $this->assertFalse($this->redis->mset(array())); // set ø → FALSE - - - /* - * Integer keys - */ - - // No prefix - $set_array = Array(-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three'); - $this->redis->delete(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->delete(array_keys($set_array)); - - // With a prefix - $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:'); - $this->redis->delete(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->delete(array_keys($set_array)); - $this->redis->setOption(Redis::OPT_PREFIX, ''); - } - - public function testMsetNX() { - $this->redis->delete('x', 'y', 'z'); // remove x y z - $this->assertTrue(TRUE === $this->redis->msetnx(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z - - $this->redis->delete('x'); // delete just x - $this->assertTrue(FALSE === $this->redis->msetnx(array('x' => 'A', 'y' => 'B', 'z' => 'C'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array(FALSE, 'b', 'c')); // check x y z - - $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE - } - - public function testRpopLpush() { - - // standard case. - $this->redis->delete('x', 'y'); - $this->redis->lpush('x', 'abc'); - $this->redis->lpush('x', 'def'); // x = [def, abc] - - $this->redis->lpush('y', '123'); - $this->redis->lpush('y', '456'); // y = [456, 123] - - $this->assertEquals($this->redis->rpoplpush('x', 'y'), 'abc'); // we RPOP x, yielding abc. - $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. - $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. - - // with an empty source, expecting no change. - $this->redis->delete('x', 'y'); - $this->assertTrue(FALSE === $this->redis->rpoplpush('x', 'y')); - $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); - $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); - - } - - public function testBRpopLpush() { - - // standard case. - $this->redis->delete('x', 'y'); - $this->redis->lpush('x', 'abc'); - $this->redis->lpush('x', 'def'); // x = [def, abc] - - $this->redis->lpush('y', '123'); - $this->redis->lpush('y', '456'); // y = [456, 123] - - $this->assertEquals($this->redis->brpoplpush('x', 'y', 1), 'abc'); // we RPOP x, yielding abc. - $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. - $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. - - // with an empty source, expecting no change. - $this->redis->delete('x', 'y'); - $this->assertTrue(FALSE === $this->redis->brpoplpush('x', 'y', 1)); - $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); - $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); - - } - - public function testZAddFirstArg() { - $zsetName = 100; // Make sure int keys work - $this->redis->delete($zsetName); - - $this->assertEquals(1, $this->redis->zAdd($zsetName, 0, 'val0')); - $this->assertEquals(1, $this->redis->zAdd($zsetName, 1, 'val1')); - - $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1)); - } - - public function testZX() { - - $this->redis->delete('key'); - - $this->assertTrue(array() === $this->redis->zRange('key', 0, -1)); - $this->assertTrue(array() === $this->redis->zRange('key', 0, -1, true)); - - $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); - $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); - $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters - - $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); - - // withscores - $ret = $this->redis->zRange('key', 0, -1, true); - $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(0 === $this->redis->zDelete('key', 'valX')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val3')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val4')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val5')); - - $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->zRange('key', 0, -1)); - - // zGetReverseRange - - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); - - $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); - $this->assertTrue(array('val0', 'val1', 'val2', 'aal3', 'val3') === $zero_to_three || array('val0', 'val1', 'val2', 'val3', 'aal3') === $zero_to_three); - - $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); - $this->assertTrue(array_reverse(array('val0', 'val1', 'val2', 'aal3', 'val3')) === $three_to_zero || array_reverse(array('val0', 'val1', 'val2', 'val3', 'aal3')) === $three_to_zero); - - $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); - - // withscores - $this->redis->zRemove('key', 'aal3'); - $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); - $this->assertTrue(array('val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3) == $zero_to_three); - $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); - - // limit - $this->assertTrue(array('val0') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 1)))); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 2)))); - $this->assertTrue(array('val1', 'val2') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 2)))); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => array(0, 100)))); - - $this->assertTrue(array('val3') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 1)))); - $this->assertTrue(array('val3', 'val2') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 2)))); - $this->assertTrue(array('val2', 'val1') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(1, 2)))); - $this->assertTrue(array('val1', 'val0') === $this->redis->zRevRangeByScore('key', 1, 0, array('limit' => array(0, 100)))); - - $this->assertTrue(4 === $this->redis->zSize('key')); - $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); - $this->assertFalse($this->redis->zScore('key', 'val')); - $this->assertFalse($this->redis->zScore(3, 2)); - - // with () and +inf, -inf - $this->redis->delete('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(array('foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4) == $this->redis->zRangeByScore('zset', '-inf', '+inf', array('withscores' => TRUE))); - $this->assertTrue(array('foo' => 1, 'bar' => 2) == $this->redis->zRangeByScore('zset', 1, 2, array('withscores' => TRUE))); - $this->assertTrue(array('bar' => 2) == $this->redis->zRangeByScore('zset', '(1', 2, array('withscores' => TRUE))); - $this->assertTrue(array() == $this->redis->zRangeByScore('zset', '(1', '(2', array('withscores' => TRUE))); - - $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); - $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); - $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); - $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2')); - - - // zincrby - $this->redis->delete('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')); - - //zUnion - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); - $this->redis->delete('keyU'); - - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); - - $this->redis->zAdd('key2', 2, 'val2'); - $this->redis->zAdd('key2', 3, 'val3'); - - $this->redis->zAdd('key3', 4, 'val4'); - $this->redis->zAdd('key3', 5, 'val5'); - - $this->assertTrue(4 === $this->redis->zUnion('keyU', array('key1', 'key3'))); - $this->assertTrue(array('val0', 'val1', 'val4', 'val5') === $this->redis->zRange('keyU', 0, -1)); - - // Union on non existing keys - $this->redis->delete('keyU'); - $this->assertTrue(0 === $this->redis->zUnion('keyU', array('X', 'Y'))); - $this->assertTrue(array() === $this->redis->zRange('keyU', 0, -1)); - - // !Exist U Exist → copy of existing zset. - $this->redis->delete('keyU', 'X'); - $this->assertTrue(2 === $this->redis->zUnion('keyU', array('key1', 'X'))); - - // test weighted zUnion - $this->redis->delete('keyZ'); - $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(1, 1))); - $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('keyZ', 0, -1)); - - $this->redis->zDeleteRangeByScore('keyZ', 0, 10); - $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(5, 1))); - $this->assertTrue(array('val0', 'val2', 'val3', 'val1') === $this->redis->zRange('keyZ', 0, -1)); - - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); - - //test zUnion with weights and aggegration function - $this->redis->zadd('key1', 1, 'duplicate'); - $this->redis->zadd('key2', 2, 'duplicate'); - $this->redis->zUnion('keyU', array('key1','key2'), array(1,1), 'MIN'); - $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); - $this->redis->delete('keyU'); - - //now test zUnion *without* weights but with aggregrate function - $this->redis->zUnion('keyU', array('key1','key2'), null, 'MIN'); - $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); - $this->redis->delete('keyU', 'key1', 'key2'); - - - - // test integer and float weights (GitHub issue #109). - $this->redis->del('key1', 'key2', 'key3'); - - $this->redis->zadd('key1', 1, 'one'); - $this->redis->zadd('key1', 2, 'two'); - $this->redis->zadd('key2', 1, 'one'); - $this->redis->zadd('key2', 2, 'two'); - $this->redis->zadd('key2', 3, 'three'); - - $this->assertTrue($this->redis->zunion('key3', array('key1', 'key2'), array(2, 3.0)) === 3); - - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); - - // Test 'inf', '-inf', and '+inf' weights (GitHub issue #336) - $this->redis->zadd('key1', 1, 'one', 2, 'two', 3, 'three'); - $this->redis->zadd('key2', 3, 'three', 4, 'four', 5, 'five'); - - // Make sure phpredis handles these weights - $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, 'inf')) === 5); - $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, '-inf')) === 5); - $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, '+inf')) === 5); - - // Now, confirm that they're being sent, and that it works - $arr_weights = Array('inf','-inf','+inf'); - - foreach($arr_weights as $str_weight) { - $r = $this->redis->zunionstore('key3', array('key1','key2'), array(1,$str_weight)); - $this->assertTrue($r===5); - $r = $this->redis->zrangebyscore('key3', '(-inf', '(inf',array('withscores'=>true)); - $this->assertTrue(count($r)===2); - $this->assertTrue(isset($r['one'])); - $this->assertTrue(isset($r['two'])); - } - - $this->redis->del('key1','key2','key3'); - - $this->redis->zadd('key1', 2000.1, 'one'); - $this->redis->zadd('key1', 3000.1, 'two'); - $this->redis->zadd('key1', 4000.1, 'three'); - - $ret = $this->redis->zRange('key1', 0, -1, TRUE); - $this->assertTrue(count($ret) === 3); - $retValues = array_keys($ret); - - $this->assertTrue(array('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->delete('key1'); - - // ZREMRANGEBYRANK - $this->redis->zAdd('key1', 1, 'one'); - $this->redis->zAdd('key1', 2, 'two'); - $this->redis->zAdd('key1', 3, 'three'); - $this->assertTrue(2 === $this->redis->zremrangebyrank('key1', 0, 1)); - $this->assertTrue(array('three' => 3) == $this->redis->zRange('key1', 0, -1, TRUE)); - - $this->redis->delete('key1'); - - // zInter - - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); - $this->redis->zAdd('key1', 3, 'val3'); - - $this->redis->zAdd('key2', 2, 'val1'); - $this->redis->zAdd('key2', 3, 'val3'); - - $this->redis->zAdd('key3', 4, 'val3'); - $this->redis->zAdd('key3', 5, 'val5'); - - $this->redis->delete('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - - // Union on non existing keys - $this->assertTrue(0 === $this->redis->zInter('keyX', array('X', 'Y'))); - $this->assertTrue(array() === $this->redis->zRange('keyX', 0, -1)); - - // !Exist U Exist - $this->assertTrue(0 === $this->redis->zInter('keyY', array('key1', 'X'))); - $this->assertTrue(array() === $this->redis->zRange('keyY', 0, -1)); - - - // test weighted zInter - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); - - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); - $this->redis->zAdd('key1', 3, 'val3'); - - - $this->redis->zAdd('key2', 2, 'val1'); - $this->redis->zAdd('key2', 1, 'val3'); - - $this->redis->zAdd('key3', 7, 'val1'); - $this->redis->zAdd('key3', 3, 'val3'); - - $this->redis->delete('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'), array(1, 1))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - - $this->redis->delete('keyI'); - $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'min')); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - $this->redis->delete('keyI'); - $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'max')); - $this->assertTrue(array('val3', 'val1') === $this->redis->zRange('keyI', 0, -1)); - - $this->redis->delete('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), null, 'max')); - $this->assertTrue($this->redis->zScore('keyI', 'val1') === floatval(7)); - - // zrank, zrevrank - $this->redis->delete('z'); - $this->redis->zadd('z', 1, 'one'); - $this->redis->zadd('z', 2, 'two'); - $this->redis->zadd('z', 5, 'five'); - - $this->assertTrue(0 === $this->redis->zRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRank('z', 'two')); - $this->assertTrue(2 === $this->redis->zRank('z', 'five')); - - $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); - $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); - - } - - public function testZRangeByLex() { - /* Only out since 2.8.9 */ - if (version_compare($this->version, '2.8.9', 'lt')) { - $this->markTestSkipped(); - return; - } - - $arr_vals = Array('a','b','c','d','e','f','g'); - - $this->redis->del('zlex'); - foreach($arr_vals as $str_val) { - $this->redis->zadd('zlex', 0, $str_val); - } - - /* These tests were taken off of redis.io out of sheer laziness :) */ - $arr_ret = $this->redis->zRangeByLex('zlex', '-', '[c'); - $this->assertTrue($arr_ret === Array('a','b','c')); - - $arr_ret = $this->redis->zRangeByLex('zlex', '-', '(c'); - $this->assertTrue($arr_ret === Array('a','b')); - - $arr_ret = $this->redis->zRangeByLex('zlex', '[aaa', '(g'); - $this->assertTrue($arr_ret === Array('b','c','d','e','f')); - - /* Test with a limit and count */ - $arr_ret = $this->redis->zRangeBylex('zlex', '-', '[c', 1, 2); - $this->assertTrue($arr_ret === Array('b','c')); - - /* Test some invalid calls */ - $this->assertFalse($this->redis->zRangeByLex('zlex','b','[s')); - $this->assertFalse($this->redis->zRangeByLex('zlex','(a', '')); - $this->assertFalse($this->redis->zRangeByLex('zlex','(a','[b',1)); - } - - public function testHashes() { - $this->redis->delete('h', 'key'); - - $this->assertTrue(0 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); - $this->assertTrue(1 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); - $this->assertTrue(2 === $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(0 === $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(1 === $this->redis->hDel('h', 'a')); // 1 on success - $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure - - $this->redis->delete('h'); - $this->redis->hSet('h', 'x', 'a'); - $this->redis->hSet('h', 'y', 'b'); - $this->assertTrue(2 === $this->redis->hDel('h', 'x', 'y')); // variadic - - // hsetnx - $this->redis->delete('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 === array('x', 'y') || $keys === array('y', 'x')); - - // values - $values = $this->redis->hVals('h'); - $this->assertTrue($values === array('a', 'b') || $values === array('b', 'a')); - - // keys + values - $all = $this->redis->hGetAll('h'); - $this->assertTrue($all === array('x' => 'a', 'y' => 'b') || $all === array('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->delete('h'); - $this->assertTrue(FALSE === $this->redis->hExists('h', 'x')); - - // hIncrBy - $this->redis->delete('h'); - $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); - $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); - $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); - $this->assertTrue(FALSE === $this->redis->hIncrBy('h', 'x', "not-a-number")); - $this->assertTrue("2" === $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", "ge")) { - // hIncrByFloat - $this->redis->delete('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->redis->hset('h','y','not-a-number'); - $this->assertTrue(FALSE === $this->redis->hIncrByFloat('h', 'y', 1.5)); - } - - // hmset - $this->redis->delete('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', array('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(array('x' => '123', 'y' => '456') === $this->redis->hMget('h', array('x', 'y'))); - $this->assertTrue(array('z' => 'abc') === $this->redis->hMget('h', array('z'))); - $this->assertTrue(array('x' => '123', 't' => FALSE, 'y' => '456') === $this->redis->hMget('h', array('x', 't', 'y'))); - $this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123))); - $this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123))); - - // Test with an array populated with things we can't use as keys - $this->assertTrue($this->redis->hmget('h', Array(false,NULL,false)) === FALSE); - - // Test with some invalid keys mixed in (which should just be ignored) - $this->assertTrue(array('x'=>'123','y'=>'456','z'=>'abc') === $this->redis->hMget('h',Array('x',null,'y','','z',false))); - - // hmget/hmset with numeric fields - $this->redis->del('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', array(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(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); - - // check non-string types. - $this->redis->delete('h1'); - $this->assertTrue(TRUE === $this->redis->hMSet('h1', array('x' => 0, 'y' => array(), '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']); - - } - - public function testSetRange() { - - $this->redis->delete('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->delete('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", "lt")) { - $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'); - $this->assertTrue($this->redis->object('encoding', 'key') === "ziplist"); - $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. - $this->assertTrue($this->redis->object('encoding', 'key') === "linkedlist"); - $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 === array('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 === array('44')); // succeeded since we've cancel the WATCH command. - } - - public function testPipeline() { - $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, ""); - } - - protected function sequence($mode) { - - $ret = $this->redis->multi($mode) - ->set('x', 42) - ->info() - ->type('x') - ->get('x') - ->exec(); - - $this->assertTrue(is_array($ret)); - $i = 0; - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue(is_array($ret[$i++])); - $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_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer - $ret = $this->redis->multi($mode) - ->delete('key1') - ->set('key1', 'value1') - ->get('key1') - ->getSet('key1', 'value2') - ->get('key1') - ->set('key2', 4) - ->incr('key2') - ->get('key2') - ->decr('key2') - ->get('key2') - ->renameKey('key2', 'key3') - ->get('key3') - ->renameNx('key3', 'key1') - ->renameKey('key3', 'key2') - ->incr('key2', 5) - ->get('key2') - ->decr('key2', 5) - ->get('key2') - ->exec(); - - $this->assertTrue(is_array($ret)); - $i = 0; - $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) - ->delete('key1') - ->delete('key2') - ->set('key1', 'val1') - ->setnx('key1', 'valX') - ->setnx('key2', 'valX') - ->exists('key1') - ->exists('key3') - ->ping() - ->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); - $this->assertTrue($ret[7] == '+PONG'); - - $ret = $this->redis->multi($mode) - ->randomKey() - ->exec(); - $ret = $this->redis->multi($mode) - ->exec(); - $this->assertTrue($ret == array()); - - // ttl, mget, mset, msetnx, expire, expireAt - $this->redis->delete('key'); - $ret = $this->redis->multi($mode) - ->ttl('key') - ->mget(array('key1', 'key2', 'key3')) - ->mset(array('key3' => 'value3', 'key4' => '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 === -1 || $ttl === -2); - $this->assertTrue($ret[$i++] === array('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('lkey', 'x') - ->set('lDest', 'y') - ->delete('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) - ->lpop('lkey') - ->llen('lkey') - ->lRemove('lkey', 'lvalue', 3) - ->llen('lkey') - ->lget('lkey', 0) - ->lGetRange('lkey', 0, -1) - ->lSet('lkey', 1, "newValue") // check errors on key not exists - ->lGetRange('lkey', 0, -1) - ->llen('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++] === array('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++] === array("lvalue")); // this is the current list. - $this->assertTrue($ret[$i++] === FALSE); // updating a non-existent element fails. - $this->assertTrue($ret[$i++] === array("lvalue")); // this is the current list. - $this->assertTrue($ret[$i++] === 1); // 1 element left - $this->assertTrue(count($ret) == $i); - - - $ret = $this->redis->multi(Redis::PIPELINE) - ->delete('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) - ->lpop('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++] === array('lvalue')); // rpoplpush returns the element: "lvalue" - $this->assertTrue($ret[$i++] === 'lvalue'); // pop returns the front element: "lvalue" - $this->assertTrue(count($ret) == $i); - - - // general command - $ret = $this->redis->multi($mode) - ->select(3) - ->set('keyAAA', 'value') - ->set('keyAAB', 'value') - ->dbSize() - ->lastsave() - ->exec(); - - $this->redis->select(0); // back to normal - - $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue($ret[$i++] === TRUE); // select - $this->assertTrue($ret[$i++] === TRUE); // set - $this->assertTrue($ret[$i++] === TRUE); // set - $this->assertTrue(is_long($ret[$i++])); // dbsize - $this->assertTrue(is_long($ret[$i++])); // lastsave - - $this->assertTrue(count($ret) === $i); - - $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer - $ret = $this->redis->multi($mode) - ->delete('key1') - ->set('key1', 'value1') - ->get('key1') - ->getSet('key1', 'value2') - ->get('key1') - ->set('key2', 4) - ->incr('key2') - ->get('key2') - ->decr('key2') - ->get('key2') - ->renameKey('key2', 'key3') - ->get('key3') - ->renameNx('key3', 'key1') - ->renameKey('key3', 'key2') - ->incr('key2', 5) - ->get('key2') - ->decr('key2', 5) - ->get('key2') - ->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->redis->setOption(Redis::OPT_SERIALIZER, $serializer); - - $ret = $this->redis->multi($mode) - ->delete('key1') - ->delete('key2') - ->set('key1', 'val1') - ->setnx('key1', 'valX') - ->setnx('key2', 'valX') - ->exists('key1') - ->exists('key3') - ->ping() - ->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); - $this->assertTrue($ret[7] == '+PONG'); - - $ret = $this->redis->multi($mode) - ->randomKey() - ->exec(); - - $this->assertTrue(is_array($ret) && count($ret) === 1); - $this->assertTrue(is_string($ret[0])); - - // ttl, mget, mset, msetnx, expire, expireAt - $ret = $this->redis->multi($mode) - ->ttl('key') - ->mget(array('key1', 'key2', 'key3')) - ->mset(array('key3' => 'value3', 'key4' => '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) - ->delete('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) - ->lpop('lkey') - ->llen('lkey') - ->lRemove('lkey', 'lvalue', 3) - ->llen('lkey') - ->lget('lkey', 0) - ->lGetRange('lkey', 0, -1) - ->lSet('lkey', 1, "newValue") // check errors on missing key - ->lGetRange('lkey', 0, -1) - ->llen('lkey') - ->exec(); - - $this->assertTrue(is_array($ret)); - $i = 0; - $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // delete - $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++] === array('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++] === array('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++] === array('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) - ->delete('skey1', 'skey2', 'skeydest', 'skeyUnion', 'sDiffDest') - ->sadd('skey1', 'sValue1') - ->sadd('skey1', 'sValue2') - ->sadd('skey1', 'sValue3') - ->sadd('skey1', 'sValue4') - - ->sadd('skey2', 'sValue1') - ->sadd('skey2', 'sValue2') - - ->sSize('skey1') - ->sRemove('skey1', 'sValue2') - ->sSize('skey1') - ->sMove('skey1', 'skey2', 'sValue4') - ->sSize('skey2') - ->sContains('skey2', 'sValue4') - ->sMembers('skey1') - ->sMembers('skey2') - ->sInter('skey1', 'skey2') - ->sInterStore('skeydest', 'skey1', 'skey2') - ->sMembers('skeydest') - ->sUnion('skey2', 'skeydest') - ->sUnionStore('skeyUnion', 'skey2', 'skeydest') - ->sMembers('skeyUnion') - ->sDiff('skey1', 'skey2') - ->sDiffStore('sDiffDest', 'skey1', 'skey2') - ->sMembers('sDiffDest') - ->sPop('skey2') - ->sSize('skey2') - ->exec(); - - $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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(array('sValue1', 'sValue3') as $k) { // sKey1 contains sValue1 and sValue3. - $this->assertTrue(in_array($k, $ret[$i])); - } - $this->assertTrue(count($ret[$i++]) === 2); - foreach(array('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++] === array('sValue1')); // intersection - $this->assertTrue($ret[$i++] === 1); // intersection + store → 1 value in the destination set. - $this->assertTrue($ret[$i++] === array('sValue1')); // sinterstore destination contents - - foreach(array('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(array('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++] === array('sValue3')); // diff skey1, skey2 : only sValue3 is not shared. - $this->assertTrue($ret[$i++] === 1); // sdiffstore size == 1 - $this->assertTrue($ret[$i++] === array('sValue3')); // contents of sDiffDest - - $this->assertTrue(in_array($ret[$i++], array('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) - ->delete('zkey1', 'zkey2', 'zkey5', 'zInter', 'zUnion') - ->zadd('zkey1', 1, 'zValue1') - ->zadd('zkey1', 5, 'zValue5') - ->zadd('zkey1', 2, 'zValue2') - ->zRange('zkey1', 0, -1) - ->zDelete('zkey1', 'zValue2') - ->zRange('zkey1', 0, -1) - ->zadd('zkey1', 11, 'zValue11') - ->zadd('zkey1', 12, 'zValue12') - ->zadd('zkey1', 13, 'zValue13') - ->zadd('zkey1', 14, 'zValue14') - ->zadd('zkey1', 15, 'zValue15') - ->zDeleteRangeByScore('zkey1', 11, 13) - ->zrange('zkey1', 0, -1) - ->zReverseRange('zkey1', 0, -1) - ->zRangeByScore('zkey1', 1, 6) - ->zCard('zkey1') - ->zScore('zkey1', 'zValue15') - ->zadd('zkey2', 5, 'zValue5') - ->zadd('zkey2', 2, 'zValue2') - ->zInter('zInter', array('zkey1', 'zkey2')) - ->zRange('zkey1', 0, -1) - ->zRange('zkey2', 0, -1) - ->zRange('zInter', 0, -1) - ->zUnion('zUnion', array('zkey1', 'zkey2')) - ->zRange('zUnion', 0, -1) - ->zadd('zkey5', 5, 'zValue5') - ->zIncrBy('zkey5', 3, 'zValue5') // fix this - ->zScore('zkey5', 'zValue5') - ->zScore('zkey5', 'unknown') - ->exec(); - - $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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++] === array('zValue1', 'zValue2', 'zValue5')); - $this->assertTrue($ret[$i++] === 1); - $this->assertTrue($ret[$i++] === array('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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); - $this->assertTrue($ret[$i++] === array('zValue15', 'zValue14', 'zValue5', 'zValue1')); - $this->assertTrue($ret[$i++] === array('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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); // zkey1 contents - $this->assertTrue($ret[$i++] === array('zValue2', 'zValue5')); // zkey2 contents - $this->assertTrue($ret[$i++] === array('zValue5')); // zinter contents - $this->assertTrue($ret[$i++] === 5); // zUnion has 5 values (1,2,5,14,15) - $this->assertTrue($ret[$i++] === array('zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15')); // zunion contents - $this->assertTrue($ret[$i++] === 1); // added value to zkey5, 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) - ->delete('hkey1') - ->hset('hkey1', 'key1', 'value1') - ->hset('hkey1', 'key2', 'value2') - ->hset('hkey1', 'key3', 'value3') - ->hmget('hkey1', array('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++] === array('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] === array('key1', 'key3') || $ret[$i] === array('key3', 'key1')); $i++; // hkeys - $this->assertTrue($ret[$i] === array('value1', 'value3') || $ret[$i] === array('value3', 'value1')); $i++; // hvals - $this->assertTrue($ret[$i] === array('key1' => 'value1', 'key3' => 'value3') || $ret[$i] === array('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. - ->delete('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 === array(1.0, FALSE, FALSE, 2.0)); - } - - protected function differentType($mode) { - - // string - $key = 'string'; - $ret = $this->redis->multi($mode) - ->delete($key) - ->set($key, 'value') - - // lists I/F - ->rPush($key, 'lvalue') - ->lPush($key, 'lvalue') - ->lLen($key) - ->lPop($key) - ->lGetRange($key, 0, -1) - ->lTrim($key, 0, 1) - ->lGet($key, 0) - ->lSet($key, 0, "newValue") - ->lRemove($key, 'lvalue', 1) - ->lPop($key) - ->rPop($key) - ->rPoplPush($key, __FUNCTION__ . 'lkey1') - - // sets I/F - ->sAdd($key, 'sValue1') - ->sRemove($key, 'sValue1') - ->sPop($key) - ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') - ->sSize($key) - ->sContains($key, 'sValue1') - ->sInter($key, __FUNCTION__ . 'skey2') - ->sUnion($key, __FUNCTION__ . 'skey4') - ->sDiff($key, __FUNCTION__ . 'skey7') - ->sMembers($key) - ->sRandMember($key) - - // sorted sets I/F - ->zAdd($key, 1, 'zValue1') - ->zDelete($key, 'zValue1') - ->zIncrBy($key, 1, 'zValue1') - ->zRank($key, 'zValue1') - ->zRevRank($key, 'zValue1') - ->zRange($key, 0, -1) - ->zReverseRange($key, 0, -1) - ->zRangeByScore($key, 1, 2) - ->zCount($key, 0, -1) - ->zCard($key) - ->zScore($key, 'zValue1') - ->zDeleteRangeByRank($key, 1, 2) - ->zDeleteRangeByScore($key, 1, 2) - - // hash I/F - ->hSet($key, 'key1', 'value1') - ->hGet($key, 'key1') - ->hMGet($key, array('key1')) - ->hMSet($key, array('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); // lgetrange - $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lget - $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); // ssize - $this->assertTrue($ret[$i++] === FALSE); // scontains - $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); // zdelete - $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); // zdeleterangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore - - $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($i, count($ret)); - - // list - $key = 'list'; - $ret = $this->redis->multi($mode) - ->delete($key) - ->lpush($key, 'lvalue') - - // string I/F - ->get($key) - ->getset($key, 'value2') - ->append($key, 'append') - ->substr($key, 0, 8) - ->mget(array($key)) - ->incr($key) - ->incrBy($key, 1) - ->decr($key) - ->decrBy($key, 1) - - // sets I/F - ->sAdd($key, 'sValue1') - ->sRemove($key, 'sValue1') - ->sPop($key) - ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') - ->sSize($key) - ->sContains($key, 'sValue1') - ->sInter($key, __FUNCTION__ . 'skey2') - ->sUnion($key, __FUNCTION__ . 'skey4') - ->sDiff($key, __FUNCTION__ . 'skey7') - ->sMembers($key) - ->sRandMember($key) - - // sorted sets I/F - ->zAdd($key, 1, 'zValue1') - ->zDelete($key, 'zValue1') - ->zIncrBy($key, 1, 'zValue1') - ->zRank($key, 'zValue1') - ->zRevRank($key, 'zValue1') - ->zRange($key, 0, -1) - ->zReverseRange($key, 0, -1) - ->zRangeByScore($key, 1, 2) - ->zCount($key, 0, -1) - ->zCard($key) - ->zScore($key, 'zValue1') - ->zDeleteRangeByRank($key, 1, 2) - ->zDeleteRangeByScore($key, 1, 2) - - // hash I/F - ->hSet($key, 'key1', 'value1') - ->hGet($key, 'key1') - ->hMGet($key, array('key1')) - ->hMSet($key, array('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); // substr - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // ssize - $this->assertTrue($ret[$i++] === FALSE); // scontains - $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); // zdelete - $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); // zdeleterangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore - - $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($i, count($ret)); - - // set - $key = 'set'; - $ret = $this->redis->multi($mode) - ->delete($key) - ->sAdd($key, 'sValue') - - // string I/F - ->get($key) - ->getset($key, 'value2') - ->append($key, 'append') - ->substr($key, 0, 8) - ->mget(array($key)) - ->incr($key) - ->incrBy($key, 1) - ->decr($key) - ->decrBy($key, 1) - - // lists I/F - ->rPush($key, 'lvalue') - ->lPush($key, 'lvalue') - ->lLen($key) - ->lPop($key) - ->lGetRange($key, 0, -1) - ->lTrim($key, 0, 1) - ->lGet($key, 0) - ->lSet($key, 0, "newValue") - ->lRemove($key, 'lvalue', 1) - ->lPop($key) - ->rPop($key) - ->rPoplPush($key, __FUNCTION__ . 'lkey1') - - // sorted sets I/F - ->zAdd($key, 1, 'zValue1') - ->zDelete($key, 'zValue1') - ->zIncrBy($key, 1, 'zValue1') - ->zRank($key, 'zValue1') - ->zRevRank($key, 'zValue1') - ->zRange($key, 0, -1) - ->zReverseRange($key, 0, -1) - ->zRangeByScore($key, 1, 2) - ->zCount($key, 0, -1) - ->zCard($key) - ->zScore($key, 'zValue1') - ->zDeleteRangeByRank($key, 1, 2) - ->zDeleteRangeByScore($key, 1, 2) - - // hash I/F - ->hSet($key, 'key1', 'value1') - ->hGet($key, 'key1') - ->hMGet($key, array('key1')) - ->hMSet($key, array('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); // substr - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange - $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lget - $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); // zdelete - $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); // zdeleterangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore - - $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($i, count($ret)); - - // sorted set - $key = 'sortedset'; - $ret = $this->redis->multi($mode) - ->delete($key) - ->zAdd($key, 0, 'zValue') - - // string I/F - ->get($key) - ->getset($key, 'value2') - ->append($key, 'append') - ->substr($key, 0, 8) - ->mget(array($key)) - ->incr($key) - ->incrBy($key, 1) - ->decr($key) - ->decrBy($key, 1) - - // lists I/F - ->rPush($key, 'lvalue') - ->lPush($key, 'lvalue') - ->lLen($key) - ->lPop($key) - ->lGetRange($key, 0, -1) - ->lTrim($key, 0, 1) - ->lGet($key, 0) - ->lSet($key, 0, "newValue") - ->lRemove($key, 'lvalue', 1) - ->lPop($key) - ->rPop($key) - ->rPoplPush($key, __FUNCTION__ . 'lkey1') - - // sets I/F - ->sAdd($key, 'sValue1') - ->sRemove($key, 'sValue1') - ->sPop($key) - ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') - ->sSize($key) - ->sContains($key, 'sValue1') - ->sInter($key, __FUNCTION__ . 'skey2') - ->sUnion($key, __FUNCTION__ . 'skey4') - ->sDiff($key, __FUNCTION__ . 'skey7') - ->sMembers($key) - ->sRandMember($key) - - // hash I/F - ->hSet($key, 'key1', 'value1') - ->hGet($key, 'key1') - ->hMGet($key, array('key1')) - ->hMSet($key, array('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); // substr - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange - $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lget - $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); // ssize - $this->assertTrue($ret[$i++] === FALSE); // scontains - $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($i, count($ret)); - - // hash - $key = 'hash'; - $ret = $this->redis->multi($mode) - ->delete($key) - ->hset($key, 'key1', 'hValue') - - // string I/F - ->get($key) - ->getset($key, 'value2') - ->append($key, 'append') - ->substr($key, 0, 8) - ->mget(array($key)) - ->incr($key) - ->incrBy($key, 1) - ->decr($key) - ->decrBy($key, 1) - - // lists I/F - ->rPush($key, 'lvalue') - ->lPush($key, 'lvalue') - ->lLen($key) - ->lPop($key) - ->lGetRange($key, 0, -1) - ->lTrim($key, 0, 1) - ->lGet($key, 0) - ->lSet($key, 0, "newValue") - ->lRemove($key, 'lvalue', 1) - ->lPop($key) - ->rPop($key) - ->rPoplPush($key, __FUNCTION__ . 'lkey1') - - // sets I/F - ->sAdd($key, 'sValue1') - ->sRemove($key, 'sValue1') - ->sPop($key) - ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') - ->sSize($key) - ->sContains($key, 'sValue1') - ->sInter($key, __FUNCTION__ . 'skey2') - ->sUnion($key, __FUNCTION__ . 'skey4') - ->sDiff($key, __FUNCTION__ . 'skey7') - ->sMembers($key) - ->sRandMember($key) - - // sorted sets I/F - ->zAdd($key, 1, 'zValue1') - ->zDelete($key, 'zValue1') - ->zIncrBy($key, 1, 'zValue1') - ->zRank($key, 'zValue1') - ->zRevRank($key, 'zValue1') - ->zRange($key, 0, -1) - ->zReverseRange($key, 0, -1) - ->zRangeByScore($key, 1, 2) - ->zCount($key, 0, -1) - ->zCard($key) - ->zScore($key, 'zValue1') - ->zDeleteRangeByRank($key, 1, 2) - ->zDeleteRangeByScore($key, 1, 2) - - ->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); // substr - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $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); // lgetrange - $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lget - $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); // ssize - $this->assertTrue($ret[$i++] === FALSE); // scontains - $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); // zdelete - $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); // zdeleterangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore - - $this->assertEquals($i, count($ret)); - } - - public function testDifferentTypeString() { - $key = 'string'; - $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->lGetRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->rPop($key)); - $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); - - // sets I/F - $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sPop($key)); - $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sSize($key)); - $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); - - // 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, array('key1'))); - $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'list'; - $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->substr($key, 0, 8)); - $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); - - // sets I/F - $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sPop($key)); - $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sSize($key)); - $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); - - // 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, array('key1'))); - $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'set'; - $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->substr($key, 0, 8)); - $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); - - // 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->lGetRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->rPop($key)); - $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); - - // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); - - // 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, array('key1'))); - $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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 = 'sortedset'; - $this->redis->del($key); - $this->assertEquals(1, $this->redis->zAdd($key, 0, '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->substr($key, 0, 8)); - $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); - - // 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->lGetRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->rPop($key)); - $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); - - // sets I/F - $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sPop($key)); - $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sSize($key)); - $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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, array('key1'))); - $this->assertEquals(FALSE, $this->redis->hMSet($key, array('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'; - $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->substr($key, 0, 8)); - $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); - - // 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->lGetRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->rPop($key)); - $this->assertEquals(FALSE, $this->redis->rPoplPush($key, __FUNCTION__ . 'lkey1')); - - // sets I/F - $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sPop($key)); - $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sSize($key)); - $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . '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($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); - } - - 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, ""); - } - } - - private function checkSerializer($mode) { - - $this->redis->delete('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 = array('hello world', 42, TRUE, array('' => 1729)); - $this->redis->delete('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]); - - // lGetRange - $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); - - // lGet - $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); - $this->assertTrue($a[1] === $this->redis->lGet('key', 1)); - $this->assertTrue($a[2] === $this->redis->lGet('key', 2)); - $this->assertTrue($a[3] === $this->redis->lGet('key', 3)); - - // lRemove - $this->assertTrue($this->redis->lRemove('key', $a[3]) === 1); - $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lGetRange('key', 0, -1)); - - // lSet - $a[0] = array('k' => 'v'); // update - $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); - $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); - - // lInsert - $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], array(1,2,3)) === 4); - $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], array(4,5,6)) === 5); - - $a = array(array(1,2,3), $a[0], array(4,5,6), $a[1], $a[2]); - $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); - - // sAdd - $this->redis->delete('key'); - $s = array(1,'a', array(1,2,3), array('k' => 'v')); - - $this->assertTrue(1 === $this->redis->sAdd('key', $s[0])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[1])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[2])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[3])); - - // variadic sAdd - $this->redis->delete('k'); - $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); - $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); - - // sRemove - $this->assertTrue(1 === $this->redis->sRemove('key', $s[3])); - $this->assertTrue(0 === $this->redis->sRemove('key', $s[3])); - // variadic - $this->redis->delete('k'); - $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); - $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); - $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); - $this->assertTrue(FALSE === $this->redis->exists('k')); - - // sContains - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[1])); - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[2])); - $this->assertTrue(FALSE === $this->redis->sContains('key', $s[3])); - unset($s[3]); - - // sMove - $this->redis->delete('tmp'); - $this->redis->sMove('key', 'tmp', $s[0]); - $this->assertTrue(FALSE === $this->redis->sContains('key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sContains('tmp', $s[0])); - unset($s[0]); - - - // sorted sets - $z = array('z0', array('k' => 'v'), FALSE, NULL); - $this->redis->delete('key'); - - // zAdd - $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0])); - $this->assertTrue(1 === $this->redis->zAdd('key', 1, $z[1])); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2])); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3])); - - // zDelete - $this->assertTrue(1 === $this->redis->zDelete('key', $z[3])); - $this->assertTrue(0 === $this->redis->zDelete('key', $z[3])); - unset($z[3]); - - // check that zDelete doesn't crash with a missing parameter (GitHub issue #102): - $this->assertTrue(FALSE === @$this->redis->zDelete('key')); - - // variadic - $this->redis->delete('k'); - $this->redis->zAdd('k', 0, 'a'); - $this->redis->zAdd('k', 1, 'b'); - $this->redis->zAdd('k', 2, 'c'); - $this->assertTrue(2 === $this->redis->zDelete('k', 'a', 'c')); - $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); - $this->assertTrue($this->redis->zRange('k', 0, -1, true) == array('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(0 === $this->redis->zRank('key', $z[0])); - $this->assertTrue(1 === $this->redis->zRank('key', $z[1])); - $this->assertTrue(2 === $this->redis->zRank('key', $z[2])); - - // zRevRank - $this->assertTrue(2 === $this->redis->zRevRank('key', $z[0])); - $this->assertTrue(1 === $this->redis->zRevRank('key', $z[1])); - $this->assertTrue(0 === $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 = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); - $this->assertTrue(TRUE === $this->redis->mset($a)); - foreach($a as $k => $v) { - $this->assertTrue($this->redis->get($k) === $v); - } - - $a = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); - - // hSet - $this->redis->delete('key'); - foreach($a as $k => $v) { - $this->assertTrue(1 === $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->delete('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', array('x' => 'y')); - $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->getMultiple(array('a', 'b', 'c', 'd'))); - - // pipeline - $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', array(new stdClass, new stdClass)); - $x = $this->redis->get('x'); - $this->assertTrue(is_array($x)); - $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_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE); // set ok - $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok - } - - public function testDebug() { - $this->redis->set('foo', 0); - $arr_info = $this->redis->debug('foo'); - $this->assertTrue(isset($arr_info['encoding']) && $arr_info['encoding']=='int'); - } - - public function testDumpRestore() { - - if (version_compare($this->version, "2.5.0", "lt")) { - $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); - - // Throw some invalid lua at redis - $this->redis->eval("not-a-lua-script"); - - // Now we should have an error - $evalError = $this->redis->getLastError(); - $this->assertTrue(strlen($evalError) > 0); - - // test getLastError with a regular command - $this->redis->set('x', 'a'); - $this->assertFalse($this->redis->incr('x')); - $incrError = $this->redis->getLastError(); - $this->assertTrue($incrError !== $evalError); // error has changed - $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 = array(); - - 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", "lt")) { - $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", "lt")) { - $this->markTestSkipped(); - } - - // Basic single line response tests - $this->assertTrue(1 == $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('mylist'); - $this->redis->rpush('mylist', 'a'); - $this->redis->rpush('mylist', 'b'); - $this->redis->rpush('mylist', 'c'); - - // Make a set - $this->redis->del('myset'); - $this->redis->sadd('myset', 'd'); - $this->redis->sadd('myset', 'e'); - $this->redis->sadd('myset', 'f'); - - // Basic keys - $this->redis->set('key1', 'hello, world'); - $this->redis->set('key2', 'hello again!'); - - // Use a script to return our list, and verify its response - $list = $this->redis->eval("return redis.call('lrange', 'mylist', 0, -1)"); - $this->assertTrue($list === Array('a','b','c')); - - // Use a script to return our set - $set = $this->redis->eval("return redis.call('smembers', 'myset')"); - $this->assertTrue($set == Array('d','e','f')); - - // Test an empty MULTI BULK response - $this->redis->del('not-any-kind-of-list'); - $empty_resp = $this->redis->eval("return redis.call('lrange', 'not-any-kind-of-list', 0, -1)"); - $this->assertTrue(is_array($empty_resp) && empty($empty_resp)); - - // Now test a nested reply - $nested_script = " - return { - 1,2,3, { - redis.call('get', 'key1'), - redis.call('get', 'key2'), - redis.call('lrange', 'not-any-kind-of-list', 0, -1), - { - redis.call('smembers','myset'), - redis.call('lrange', 'mylist', 0, -1) - } - } - } - "; - - $expected = Array( - 1, 2, 3, Array( - 'hello, world', - 'hello again!', - Array(), - Array( - Array('d','e','f'), - Array('a','b','c') - ) - ) - ); - - // Now run our script, and check our values against each other - $eval_result = $this->redis->eval($nested_script); - $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; - - foreach(Array(Redis::PIPELINE, Redis::MULTI) as $mode) { - $this->redis->multi($mode); - for($i=0;$i<$num_scripts;$i++) { - $this->redis->eval($nested_script); - } - $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 = Array('k1','k2','k3','v1','v2','v3'); - $args_result = $this->redis->eval($args_script, $args_args, 3); - $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_args, 3); - - // Make sure our first three are prefixed - for($i=0;$iassertTrue($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", "lt")) { - $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(1 === $this->redis->eval($scr)); - $this->assertTrue(1 === $this->redis->evalsha($sha)); - } - - public function testSerialize() { - $vals = Array(1, 1.5, 'one', Array('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()) === 'Array'); - $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object'); - - $arr_serializers = Array(Redis::SERIALIZER_PHP); - if(defined('Redis::SERIALIZER_IGBINARY')) { - $arr_serializers[] = Redis::SERIALIZER_IGBINARY; - } - - foreach($arr_serializers as $mode) { - $arr_enc = Array(); - $arr_dec = Array(); - - 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 = Array( - 1,1.5,'one',Array('this','is','an','array') - ); - - $serializers = Array(Redis::SERIALIZER_PHP); - if(defined('Redis::SERIALIZER_IGBINARY')) { - $serializers[] = Redis::SERIALIZER_IGBINARY; - } - - foreach($serializers as $mode) { - $vals_enc = Array(); - - // 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_SERIALIZER, Redis::SERIALIZER_NONE); - $vals_enc[] = $this->redis->get($key); - } - - // Run through our array comparing values - for($i=0;$iredis->setOption(Redis::OPT_SERIALIZER, $mode); - $this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i])); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); - } - } - } - - 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 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->delete($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", "lt")) { - $this->markTestSkipped(); - } - - $time_arr = $this->redis->time(); - $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && - 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() === self::HOST); - $this->assertTrue($this->redis->getPort() === self::PORT); - $this->assertTrue($this->redis->getAuth() === self::AUTH); - } - - /** - * Scan and variants - */ - - protected function get_keyspace_count($str_db) { - $arr_info = $this->redis->info(); - $arr_info = $arr_info[$str_db]; - $arr_info = explode(',', $arr_info); - $arr_info = explode('=', $arr_info[0]); - return $arr_info[1]; - } - - public function testScan() { - if(version_compare($this->version, "2.8.0", "lt")) { - $this->markTestSkipped(); - return; - } - - // Key count - $i_key_count = $this->get_keyspace_count('db0'); - - // Have scan retry - $this->redis->setOption(Redis::OPT_SCAN, Redis::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); - } - - public function testHScan() { - if(version_compare($this->version, "2.8.0", "lt")) { - $this->markTestSkipped(); - return; - } - - // Never get empty sets - $this->redis->setOption(Redis::OPT_SCAN, Redis::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", "lt")) { - $this->markTestSkipped(); - return; - } - - $this->redis->setOption(Redis::OPT_SCAN, Redis::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", "lt")) { - $this->markTestSkipped(); - return; - } - - $this->redis->setOption(Redis::OPT_SCAN, Redis::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(0.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(0.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_SCAN, Redis::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(0.0, $i_p_score); - $this->assertEquals(0, $i_p_count); - } - - // - // HyperLogLog (PF) commands - // +/* Let the user know this can take a bit of time */ +echo "Note: these tests might take up to a minute. Don't worry :-)\n"; - protected function createPFKey($str_key, $i_count) { - $arr_mems = Array(); - for($i=0;$i<$i_count;$i++) { - $arr_mems[] = uniqid() . '-' . $i; - } +/* Depending on the classes being tested, run our tests on it */ +if ($str_class = 'redis') { + exit(TestSuite::run("Redis_Test")); +} else if ($str_class == 'redisarray') { + global $useIndex; + foreach(array(true, false) as $useIndex) { + echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; - // Estimation by Redis - $this->redis->pfadd($str_key, $i_count); + run_tests('Redis_Array_Test'); + run_tests('Redis_Rehashing_Test'); + run_tests('Redis_Auto_Rehashing_Test'); + run_tests('Redis_Multi_Exec_Test'); + run_tests('Redis_Distributor_Test'); } +} else { - public function testPFCommands() { - // Isn't available until 2.8.9 - if(version_compare($this->version, "2.8.9", "lt")) { - $this->markTestSkipped(); - return; - } - - $str_uniq = uniqid(); - $arr_mems = Array(); - - for($i=0;$i<1000;$i++) { - if($i%2 == 0) { - $arr_mems[] = $str_uniq . '-' . $i; - } else { - $arr_mems[] = $i; - } - } - - // How many keys to create - $i_keys = 10; - - // Iterate prefixing/serialization options - foreach(Array(Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP) as $str_ser) { - foreach(Array('', 'hl-key-prefix:') as $str_prefix) { - $arr_keys = Array(); - - // 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); - - } - - // Make sure we can pass an array of keys into pfCount - $i_card = $this->redis->pfcount($arr_keys); - $this->assertTrue(is_int($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'); - } - } - } } - -$str_test = isset($argv[1]) ? $argv[1] : NULL; -exit(TestSuite::run("Redis_Test", $str_test)); - ?> diff --git a/tests/test.php b/tests/TestSuite.php similarity index 100% rename from tests/test.php rename to tests/TestSuite.php diff --git a/tests/memory.php b/tests/memory.php deleted file mode 100644 index 6b50906aa1..0000000000 --- a/tests/memory.php +++ /dev/null @@ -1,39 +0,0 @@ -mset($data); - foreach($data as $k => $v) { - if($v != $ra->get($k)) { - echo "Expected $v\n"; - die("FAIL"); - } - } - - $ra = ra(); - $data = data(); - if(array_values($data) != $ra->mget(array_keys($data))) { - die("FAIL"); - } -} -?> From 113ceae78c2166288e3eca8e607444578eb02df3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 28 Feb 2015 13:55:06 -0800 Subject: [PATCH 0404/1986] Update tests to stop calling 'incr' and 'decr' with an argument. --- tests/RedisTest.php | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index e92fb71769..afadca3956 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -489,17 +489,14 @@ public function testIncr() $this->redis->incr('key'); $this->assertEquals(2, (int)$this->redis->get('key')); - $this->redis->incr('key', 3); - $this->assertEquals(5, (int)$this->redis->get('key')); - $this->redis->incrBy('key', 3); - $this->assertEquals(8, (int)$this->redis->get('key')); + $this->assertEquals(5, (int)$this->redis->get('key')); $this->redis->incrBy('key', 1); - $this->assertEquals(9, (int)$this->redis->get('key')); + $this->assertEquals(6, (int)$this->redis->get('key')); $this->redis->incrBy('key', -1); - $this->assertEquals(8, (int)$this->redis->get('key')); + $this->assertEquals(5, (int)$this->redis->get('key')); $this->redis->delete('key'); @@ -561,20 +558,14 @@ public function testDecr() $this->redis->decr('key'); $this->assertEquals(3, (int)$this->redis->get('key')); - $this->redis->decr('key', 2); - $this->assertEquals(1, (int)$this->redis->get('key')); - - $this->redis->decr('key', 2); - $this->assertEquals(-1, (int)$this->redis->get('key')); - $this->redis->decrBy('key', 2); - $this->assertEquals(-3, (int)$this->redis->get('key')); + $this->assertEquals(1, (int)$this->redis->get('key')); $this->redis->decrBy('key', 1); - $this->assertEquals(-4, (int)$this->redis->get('key')); + $this->assertEquals(0, (int)$this->redis->get('key')); - $this->redis->decr('key', -10); - $this->assertEquals(6, (int)$this->redis->get('key')); + $this->redis->decrBy('key', -10); + $this->assertEquals(10, (int)$this->redis->get('key')); } public function testExists() From 0c8001d977ae9e915747512f3e6d7fa39e888f74 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 28 Feb 2015 15:38:04 -0800 Subject: [PATCH 0405/1986] Fix double free in redis_sort_cmd --- redis_commands.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 5fa7c64104..3977c54b87 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2034,7 +2034,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); // If we don't have an options array, the command is quite simple - if(!z_opts) { + if(!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) { // Construct command *cmd_len = redis_cmd_format_static(cmd, "SORT", "s", key, key_len); @@ -2051,7 +2051,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, array_init(z_argv); // SORT - add_next_index_stringl(z_argv, key, key_len, 0); + add_next_index_stringl(z_argv, key, key_len, !key_free); // Set slot CMD_SET_SLOT(slot,key,key_len); @@ -2076,7 +2076,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // ... BY add_next_index_stringl(z_argv, "BY", sizeof("BY")-1, 1); - add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),0); + add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),1); } // Handle ASC/DESC option @@ -2085,7 +2085,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_TYPE_PP(z_ele)==IS_STRING) { // 'asc'|'desc' - add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),0); + add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),1); } // STORE option @@ -2108,7 +2108,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // STORE add_next_index_stringl(z_argv,"STORE",sizeof("STORE")-1, 1); - add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),0); + add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),1); // We are using STORE *using_store = 1; @@ -2131,15 +2131,15 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If it's a string just add it if(Z_TYPE_PP(z_ele)==IS_STRING) { - add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,0); + add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,1); add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele), - Z_STRLEN_PP(z_ele), 0); + Z_STRLEN_PP(z_ele), 1); } else { HashTable *ht_keys = Z_ARRVAL_PP(z_ele); int added=0; // Add our "GET" option - add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,0); + add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,1); for(zend_hash_internal_pointer_reset(ht_keys); zend_hash_has_more_elements(ht_keys)==SUCCESS; @@ -2155,7 +2155,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Add this key to our argv array add_next_index_stringl(z_argv, Z_STRVAL_PP(z_key), - Z_STRLEN_PP(z_key), 0); + Z_STRLEN_PP(z_key), 1); added++; } @@ -2241,8 +2241,8 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } - // Free key if we prefixed, destroy argv array - if(key_free) efree(key); + /* Clean up our arguments array. Note we don't have to free any prefixed + * key as that we didn't duplicate the pointer if we prefixed */ zval_dtor(z_argv); efree(z_argv); From b809bd8a473507e86596d122ae3385890ffbb32b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 28 Feb 2015 15:44:59 -0800 Subject: [PATCH 0406/1986] More multiple argument 'incr'/'decr' commands removed from tests --- tests/RedisTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index afadca3956..475e9463e8 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2372,7 +2372,6 @@ public function testHashes() { $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); - $this->assertTrue(FALSE === $this->redis->hIncrBy('h', 'x', "not-a-number")); $this->assertTrue("2" === $this->redis->hGet('h', 'x')); $this->redis->hSet('h', 'y', 'not-a-number'); @@ -2571,9 +2570,9 @@ protected function sequence($mode) { ->get('key3') ->renameNx('key3', 'key1') ->renameKey('key3', 'key2') - ->incr('key2', 5) + ->incrby('key2', 5) ->get('key2') - ->decr('key2', 5) + ->decrby('key2', 5) ->get('key2') ->exec(); From 7453c885ef19c3baed61136f1cf670d606bad2f5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 28 Feb 2015 15:51:45 -0800 Subject: [PATCH 0407/1986] Remove empty diff/union tests Empty sUnionStore/sDiffStore/etc type commands are protected from empty arguments and will throw warnings so those have been removed. --- tests/RedisTest.php | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 475e9463e8..564f56b1d4 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1395,9 +1395,7 @@ public function testsInter() { $xyz = $this->redis->sInter(array('x', 'y', 'z'));// odd prime squares, with an array as a parameter $this->assertTrue($xyz === array('1')); - $nil = $this->redis->sInter(); - $this->assertTrue($nil === FALSE); - $nil = $this->redis->sInter(array()); + $nil = $this->redis->sInter(array()); $this->assertTrue($nil === FALSE); } @@ -1455,8 +1453,6 @@ public function testsInterStore() { $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // x y and z ALL missing, expect 0. $this->assertTrue($xyz === 0); - $o = $this->redis->sInterStore('k'); - $this->assertTrue($o === FALSE); // error, wrong parameter count } public function testsUnion() { @@ -1508,9 +1504,6 @@ public function testsUnion() { $i = (int)$i; $this->assertTrue(in_array($i, array_merge($x, $y, $z))); } - - $nil = $this->redis->sUnion(); - $this->assertTrue($nil === FALSE); } public function testsUnionStore() { @@ -1582,9 +1575,6 @@ public function testsUnionStore() { $this->redis->delete('z'); // x, y, and z ALL missing $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z $this->assertTrue($count === 0); - - $count = $this->redis->sUnionStore('k'); // Union on nothing... - $this->assertTrue($count === FALSE); } public function testsDiff() { @@ -1636,9 +1626,6 @@ public function testsDiff() { $i = (int)$i; $this->assertTrue(in_array($i, array_diff($x, $y, $z))); } - - $nil = $this->redis->sDiff(); - $this->assertTrue($nil === FALSE); } public function testsDiffStore() { @@ -1710,9 +1697,6 @@ public function testsDiffStore() { $this->redis->delete('z'); // x, y, and z ALL missing $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z $this->assertTrue($count === 0); - - $count = $this->redis->sDiffStore('k'); // diff on nothing... - $this->assertTrue($count === FALSE); } public function testlGetRange() { From 4529e6ec12f6ed670c49e369bc46f6cc98e547aa Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 10:14:58 -0800 Subject: [PATCH 0408/1986] Reintroduce the ability to pass an optional long argument to incr/decr It's easy to not break backward compatibility here, so I've reintroduced logic such that INCR and DECR proper can be called with an optional increment value, in which case the call actually made is INCRBY or DECRBY, respectively. --- redis.c | 6 ++-- redis_cluster.c | 4 +-- redis_commands.c | 73 +++++++++++++++++++++++++++++++++++++++++ tests/RedisTest.php | 80 ++++++++++++++++++++++++++++++--------------- 4 files changed, 131 insertions(+), 32 deletions(-) diff --git a/redis.c b/redis.c index 015cc817c1..bdabf23965 100644 --- a/redis.c +++ b/redis.c @@ -918,7 +918,7 @@ PHP_METHOD(Redis, ping) /* {{{ proto boolean Redis::incr(string key [,int value]) */ PHP_METHOD(Redis, incr){ - REDIS_PROCESS_KW_CMD("INCR", redis_key_cmd, redis_long_response); + REDIS_PROCESS_CMD(incr, redis_long_response); } /* }}} */ @@ -940,7 +940,7 @@ PHP_METHOD(Redis, incrByFloat) { /* {{{ proto boolean Redis::decr(string key) */ PHP_METHOD(Redis, decr) { - REDIS_PROCESS_KW_CMD("DECR", redis_key_cmd, redis_long_response); + REDIS_PROCESS_CMD(decr, redis_long_response); } /* }}} */ @@ -1238,7 +1238,7 @@ PHP_METHOD(Redis, listTrim) /* {{{ proto string Redis::lGet(string key , int index) */ PHP_METHOD(Redis, lGet) { - REDIS_PROCESS_KW_CMD("LGET", redis_key_long_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, redis_string_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 0b95faa298..582f0e6907 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1336,7 +1336,7 @@ PHP_METHOD(RedisCluster, dump) { /* {{{ proto long RedisCluster::incr(string key) */ PHP_METHOD(RedisCluster, incr) { - CLUSTER_PROCESS_KW_CMD("INCR", redis_key_cmd, cluster_long_resp, 0); + CLUSTER_PROCESS_CMD(incr, cluster_long_resp, 0); } /* }}} */ @@ -1348,7 +1348,7 @@ PHP_METHOD(RedisCluster, incrby) { /* {{{ proto long RedisCluster::decr(string key) */ PHP_METHOD(RedisCluster, decr) { - CLUSTER_PROCESS_KW_CMD("DECR", redis_key_cmd, cluster_long_resp, 0); + CLUSTER_PROCESS_CMD(decr, cluster_long_resp, 0); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 3977c54b87..fae6d42814 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1220,6 +1220,73 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* To maintain backward compatibility with earlier versions of phpredis, we + * allow for an optional "increment by" argument for INCR and DECR even though + * that's not how Redis proper works */ +#define TYPE_INCR 0 +#define TYPE_DECR 1 + +/* Handle INCR(BY) and DECR(BY) depending on optional increment value */ +static int +redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, + RedisSock *redis_sock, char **cmd, int *cmd_len, + short *slot, void **ctx) +{ + char *key; + int key_free, key_len; + long val = 1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, + &val)==FAILURE) + { + return FAILURE; + } + + /* Prefix the key if required */ + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + /* If our value is 1 we use INCR/DECR. For other values, treat the call as + * an INCRBY or DECRBY call */ + if (type == TYPE_INCR) { + if (val == 1) { + *cmd_len = redis_cmd_format_static(cmd,"INCR","s",key,key_len); + } else { + *cmd_len = redis_cmd_format_static(cmd,"INCRBY","sd",key,key_len,val); + } + } else { + if (val == 1) { + *cmd_len = redis_cmd_format_static(cmd,"DECR","s",key,key_len); + } else { + *cmd_len = redis_cmd_format_static(cmd,"DECRBY","sd",key,key_len,val); + } + } + + /* Set our slot */ + CMD_SET_SLOT(slot,key,key_len); + + /* Free our key if we prefixed */ + if (key_free) efree(key); + + /* Success */ + return SUCCESS; +} + +/* INCR */ +int redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, + TYPE_INCR, redis_sock, cmd, cmd_len, slot, ctx); +} + +/* DECR */ +int redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, + TYPE_DECR, redis_sock, cmd, cmd_len, slot, ctx); +} + /* HINCRBY */ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -1243,6 +1310,9 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set slot CMD_SET_SLOT(slot,key,key_len); + /* Free the key if we prefixed */ + if (key_free) efree(key); + // Success return SUCCESS; } @@ -1271,6 +1341,9 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set slot CMD_SET_SLOT(slot,key,key_len); + /* Free the key if we prefixed */ + if (key_free) efree(key); + // Success return SUCCESS; } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 564f56b1d4..6ec46f7221 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -15,9 +15,9 @@ class Redis_Test extends TestSuite public function setUp() { - $this->redis = $this->newInstance(); - $info = $this->redis->info(); - $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); + $this->redis = $this->newInstance(); + $info = $this->redis->info(); + $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); } protected function newInstance() { @@ -44,10 +44,15 @@ public function reset() $this->tearDown(); } + /* Helper function to determine if the clsas has pipeline support */ + protected function havePipeline() { + return defined('Redis::PIPELINE'); + } + public function testMinimumVersion() { - // Minimum server version required for tests - $this->assertTrue(version_compare($this->version, "2.4.0", "ge")); + // Minimum server version required for tests + $this->assertTrue(version_compare($this->version, "2.4.0", "ge")); } public function testPing() @@ -62,6 +67,9 @@ public function testPing() } public function testPipelinePublish() { + if (!$this->havePipeline()) { + $this->markTestSkipped(); + } $ret = $this->redis->pipeline() ->publish('chan', 'msg') @@ -498,6 +506,9 @@ public function testIncr() $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->delete('key'); $this->redis->set('key', 'abc'); @@ -566,8 +577,12 @@ public function testDecr() $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() { @@ -1272,20 +1287,22 @@ public function testSRandMemberWithCount() { // Test in a pipeline // - $pipe = $this->redis->pipeline(); + if ($this->havePipeline()) { + $pipe = $this->redis->pipeline(); - $pipe->srandmember('set0', 20); - $pipe->srandmember('set0', 200); - $pipe->srandmember('set0', -200); + $pipe->srandmember('set0', 20); + $pipe->srandmember('set0', 200); + $pipe->srandmember('set0', -200); - $ret = $this->redis->exec(); + $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); + $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'); + // Kill the set + $this->redis->del('set0'); + } } public function testsContains() @@ -2511,14 +2528,18 @@ public function testMultiExec() { } public function testPipeline() { - $this->sequence(Redis::PIPELINE); - $this->differentType(Redis::PIPELINE); + if (!$this->havePipeline()) { + $this->markTestSkipped(); + } - // 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, ""); + $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, ""); } protected function sequence($mode) { @@ -2683,7 +2704,7 @@ protected function sequence($mode) { $this->assertTrue(count($ret) == $i); - $ret = $this->redis->multi(Redis::PIPELINE) + $ret = $this->redis->multi($mode) ->delete('lkey', 'lDest') ->rpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') @@ -2742,9 +2763,9 @@ protected function sequence($mode) { ->get('key3') ->renameNx('key3', 'key1') ->renameKey('key3', 'key2') - ->incr('key2', 5) + ->incrby('key2', 5) ->get('key2') - ->decr('key2', 5) + ->decrby('key2', 5) ->get('key2') ->exec(); @@ -4174,7 +4195,9 @@ private function checkSerializer($mode) { $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->getMultiple(array('a', 'b', 'c', 'd'))); // pipeline - $this->sequence(Redis::PIPELINE); + if ($this->havePipeline()) { + $this->sequence(Redis::PIPELINE); + } // multi-exec $this->sequence(Redis::MULTI); @@ -4393,7 +4416,10 @@ public function testEval() { $num_scripts = 10; - foreach(Array(Redis::PIPELINE, Redis::MULTI) as $mode) { + $arr_modes = Array(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); From 92f620bcad134d1d948ec01afaf07308ad11a353 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 12:34:21 -0800 Subject: [PATCH 0409/1986] Incorporate formalized multi-bulk proessing from develop This commit just backports the newer and improved multi-bulk processing for various commands (e.g. zrange, hmget,etc) from develop into feature/redis_cluster Also modified getbit to treat the value as a long --- library.c | 189 ++++++++++++++++++++++++-------------------- library.h | 14 ++-- php_redis.h | 3 - redis.c | 98 +++-------------------- redis_commands.c | 4 +- redis_commands.h | 6 ++ tests/RedisTest.php | 6 +- 7 files changed, 134 insertions(+), 186 deletions(-) diff --git a/library.c b/library.c index 7351c8b656..f38e62cc53 100644 --- a/library.c +++ b/library.c @@ -27,16 +27,10 @@ PHPAPI int usleep(unsigned int useconds); # endif #endif -#ifdef _MSC_VER -#define atoll _atoi64 -#define random rand -#define usleep Sleep -#endif - #define UNSERIALIZE_NONE 0 #define UNSERIALIZE_KEYS 1 #define UNSERIALIZE_VALS 2 -#define UNSERIALIZE_ALL 3 +#define UNSERIALIZE_ALL 3 #define SCORE_DECODE_NONE 0 #define SCORE_DECODE_INT 1 @@ -237,14 +231,17 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, scan command this is. They all come back in slightly different ways */ switch(type) { case TYPE_SCAN: - return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL); case TYPE_SSCAN: return redis_sock_read_multibulk_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); case TYPE_ZSCAN: - return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL); case TYPE_HSCAN: - return redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL); default: return -1; } @@ -443,7 +440,7 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, numElems, UNSERIALIZE_ALL); - + return z_tab; } @@ -1228,10 +1225,68 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, zval *z_ret; HashTable *keytable; -PHPAPI int -redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, - zval *z_tab, int flag) + MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + keytable = Z_ARRVAL_P(z_tab); + + for(zend_hash_internal_pointer_reset(keytable); + zend_hash_has_more_elements(keytable) == SUCCESS; + zend_hash_move_forward(keytable)) { + + char *tablekey, *hkey, *hval; + unsigned int tablekey_len; + int hkey_len; + unsigned long idx; + zval **z_key_pp, **z_value_pp; + + zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { + continue; /* this should never happen, according to the PHP people. */ + } + + /* get current value, a key */ + convert_to_string(*z_key_pp); + hkey = Z_STRVAL_PP(z_key_pp); + hkey_len = Z_STRLEN_PP(z_key_pp); + + /* move forward */ + zend_hash_move_forward(keytable); + + /* fetch again */ + zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); + if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { + continue; /* this should never happen, according to the PHP people. */ + } + + /* get current value, a hash value now. */ + hval = Z_STRVAL_PP(z_value_pp); + + /* Decode the score depending on flag */ + if (decode == SCORE_DECODE_INT && Z_STRLEN_PP(z_value_pp) > 0) { + add_assoc_long_ex(z_ret, hkey, 1+hkey_len, atoi(hval+1)); + } else if (decode == SCORE_DECODE_DOUBLE) { + add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); + } else { + zval *z = NULL; + MAKE_STD_ZVAL(z); + *z = **z_value_pp; + zval_copy_ctor(z); + add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); + } + } + + /* replace */ + zval_dtor(z_tab); + *z_tab = *z_ret; + zval_copy_ctor(z_tab); + zval_dtor(z_ret); + + efree(z_ret); +} + +static int +redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, int unserialize, int decode) { char inbuf[1024]; int numElems; @@ -1246,8 +1301,7 @@ redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, - "read error on connection", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return -1; } @@ -1283,14 +1337,14 @@ redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PARAMETERS, } /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */ -PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHPAPI int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, UNSERIALIZE_NONE, SCORE_DECODE_NONE); } /* Zipped key => value reply unserializing keys and decoding the score as an integer (PUBSUB) */ -PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +PHPAPI int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, @@ -1298,7 +1352,7 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS } /* Zipped key => value reply unserializing keys and decoding the score as a double (ZSET commands) */ -PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +PHPAPI int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, @@ -1306,13 +1360,24 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS } /* Zipped key => value reply where only the values are unserialized (e.g. HMGET) */ -PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +PHPAPI int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } +PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); +} + + +PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) +{ char *response; int response_len; char ret; @@ -1755,7 +1820,7 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_multi_result, numElems, UNSERIALIZE_ALL); - + IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { @@ -1766,13 +1831,9 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, return 0; } -/** - * Like multibulk reply, but don't touch the values, they won't be compressed. - * (this is used by HKEYS). - */ -PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, - zval *z_tab, void *ctx) +/* Like multibulk reply, but don't touch the values, they won't be unserialized + * (this is used by HKEYS). */ +PHPAPI int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; int numElems; @@ -1787,8 +1848,7 @@ PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", - 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return -1; } @@ -1817,7 +1877,7 @@ PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, return 0; } -PHP_REDIS_API void +PHPAPI void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize) { @@ -1850,50 +1910,11 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, count--; } } - - -/* -PHP_REDIS_API int -redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only) -{ - char *response; - int response_len; - - while(numElems > 0) { - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); - if(response != NULL) { - zval *z = NULL; - int can_unserialize = unwrap_key; - if(unserialize_even_only == UNSERIALIZE_ONLY_VALUES && - numElems % 2 == 0) - { - can_unserialize = 0; - } - - if(can_unserialize && redis_unserialize(redis_sock, response, - response_len, &z TSRMLS_CC)==1) - { - efree(response); - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, response, response_len, 0); - } - } else { - add_next_index_bool(z_tab, 0); - } - numElems --; - } - return 0; -} */ -/** - * redis_sock_read_multibulk_reply_assoc - */ -PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, - zval *z_tab, void *ctx) +/* Specialized multibulk processing for HMGET where we need to pair requested + * keys with their returned values */ +PHPAPI int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024], *response; int response_len; @@ -1911,8 +1932,7 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", - 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return -1; } @@ -1932,22 +1952,17 @@ PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { zval *z = NULL; - if(redis_unserialize(redis_sock, response, response_len, &z - TSRMLS_CC) == 1) - { + if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { efree(response); - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), z); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); } } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), 0); + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval_dtor(z_keys[i]); + efree(z_keys[i]); } efree(z_keys); diff --git a/library.h b/library.h index bcd3bd5bbd..37ea22e5c7 100644 --- a/library.h +++ b/library.h @@ -36,11 +36,15 @@ PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); -PHPAPI int redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems, int unwrap_key, int unserialize_even_only); -PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); + +PHPAPI int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHPAPI int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); + PHPAPI int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter); PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, diff --git a/php_redis.h b/php_redis.h index ffa88a2b36..11e7f0dd9c 100644 --- a/php_redis.h +++ b/php_redis.h @@ -252,9 +252,6 @@ PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd); PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd); -PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, - int use_atof TSRMLS_DC); - PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC); PHPAPI int get_flag(zval *object TSRMLS_DC); diff --git a/redis.c b/redis.c index bdabf23965..05c4e74077 100644 --- a/redis.c +++ b/redis.c @@ -1096,8 +1096,7 @@ PHP_METHOD(Redis, unwatch) */ PHP_METHOD(Redis, getKeys) { - REDIS_PROCESS_KW_CMD("KEYS", redis_key_cmd, - redis_sock_read_multibulk_reply_raw); + REDIS_PROCESS_KW_CMD("KEYS", redis_key_cmd, redis_mbulk_reply_raw); } /* }}} */ @@ -1967,10 +1966,9 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(withscores) { IF_ATOMIC() { - redis_sock_read_multibulk_reply_zipped( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); } else { IF_ATOMIC() { if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, @@ -2159,8 +2157,7 @@ PHP_METHOD(Redis, hExists) /* {{{ proto array Redis::hkeys(string key) */ PHP_METHOD(Redis, hKeys) { - REDIS_PROCESS_KW_CMD("HKEYS", redis_key_cmd, - redis_sock_read_multibulk_reply_raw); + REDIS_PROCESS_KW_CMD("HKEYS", redis_key_cmd, redis_mbulk_reply_raw); } /* }}} */ @@ -2173,80 +2170,10 @@ PHP_METHOD(Redis, hVals) /* {{{ proto array Redis::hgetall(string key) */ PHP_METHOD(Redis, hGetAll) { - REDIS_PROCESS_KW_CMD("HGETALL", redis_key_cmd, - redis_sock_read_multibulk_reply_zipped_strings); + REDIS_PROCESS_KW_CMD("HGETALL", redis_key_cmd, redis_mbulk_reply_zipped_vals); } /* }}} */ -/* Turn an array in the form key1, value1, key2, value2 into the form - * key1=>value1, key2=>value2, optionally treating values as doubles. */ -PHPAPI void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, - int use_atof TSRMLS_DC) -{ - - zval *z_ret; - HashTable *keytable; - - MAKE_STD_ZVAL(z_ret); - array_init(z_ret); - keytable = Z_ARRVAL_P(z_tab); - - for(zend_hash_internal_pointer_reset(keytable); - zend_hash_has_more_elements(keytable) == SUCCESS; - zend_hash_move_forward(keytable)) { - - char *tablekey, *hkey, *hval; - unsigned int tablekey_len; - int hkey_len; - unsigned long idx; - zval **z_key_pp, **z_value_pp; - - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, - 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { - /* this should never happen, according to the PHP people */ - continue; - } - - /* get current value, a key */ - convert_to_string(*z_key_pp); - hkey = Z_STRVAL_PP(z_key_pp); - hkey_len = Z_STRLEN_PP(z_key_pp); - - /* move forward */ - zend_hash_move_forward(keytable); - - /* fetch again */ - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, - 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) - { - /* this should never happen, according to the PHP people */ - continue; - } - - /* get current value, a hash value now. */ - hval = Z_STRVAL_PP(z_value_pp); - - if(use_atof) { /* zipping a score */ - add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); - } else { /* add raw copy */ - zval *z = NULL; - MAKE_STD_ZVAL(z); - *z = **z_value_pp; - zval_copy_ctor(z); - add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); - } - } - /* replace */ - zval_dtor(z_tab); - *z_tab = *z_ret; - zval_copy_ctor(z_tab); - zval_dtor(z_ret); - - efree(z_ret); -} - /* {{{ proto double Redis::hIncrByFloat(string k, string me, double v) */ PHP_METHOD(Redis, hIncrByFloat) { @@ -2263,7 +2190,7 @@ PHP_METHOD(Redis, hIncrBy) /* {{{ array Redis::hMget(string hash, array keys) */ PHP_METHOD(Redis, hMget) { - REDIS_PROCESS_CMD(hmget, redis_sock_read_multibulk_reply_assoc); + REDIS_PROCESS_CMD(hmget, redis_mbulk_reply_assoc); } /* }}} */ @@ -2851,10 +2778,9 @@ PHP_METHOD(Redis, config) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) IF_ATOMIC() { - redis_sock_read_multibulk_reply_zipped_strings( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped_strings); + REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw); } else if(mode == CFG_SET && val != NULL) { cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, @@ -3117,9 +3043,8 @@ PHP_METHOD(Redis, pubsub) { if(type == PUBSUB_NUMSUB) { IF_ATOMIC() { - if(redis_sock_read_multibulk_reply_zipped( - INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) + if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL)<0) { RETURN_FALSE; } @@ -3559,8 +3484,7 @@ PHP_METHOD(Redis, clearLastError) { /* {{{ proto Redis::time() */ PHP_METHOD(Redis, time) { - REDIS_PROCESS_KW_CMD("TIME", redis_empty_cmd, - redis_sock_read_multibulk_reply_raw); + REDIS_PROCESS_KW_CMD("TIME", redis_empty_cmd, redis_mbulk_reply_raw); } /* {{{ proto array Redis::role() */ diff --git a/redis_commands.c b/redis_commands.c index fae6d42814..8098241188 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1828,8 +1828,8 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } key_free = redis_key_prefix(redis_sock, &key, &key_len); - *cmd_len = redis_cmd_format_static(cmd, "SETBIT", "sdd", key, key_len, - (int)offset, (int)val); + *cmd_len = redis_cmd_format_static(cmd, "SETBIT", "sld", key, key_len, + offset, (int)val); CMD_SET_SLOT(slot, key, key_len); diff --git a/redis_commands.h b/redis_commands.h index 6ac9abeeaf..f54ea019c3 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -106,6 +106,12 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 6ec46f7221..4d8fb1f080 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -107,7 +107,7 @@ public function testPubSub() { // Make sure the elements are correct, and have zero counts foreach(Array($c1,$c2) as $channel) { $this->assertTrue(isset($result[$channel])); - $this->assertEquals($result[$channel], "0"); + $this->assertEquals($result[$channel], 0); } // PUBSUB NUMPAT @@ -156,7 +156,9 @@ public function testBitsets() { // Verify valid offset ranges $this->assertFalse($this->redis->getBit('key', -1)); - $this->assertTrue($this->redis->getBit('key', 4294967295)); + + $this->redis->setBit('key', 4294967295, 1); + $this->assertTrue(1 === $this->redis->getBit('key', 4294967295)); } public function testBitPos() { From 076d2ed2e81be4df13a7c39244dd4346defb26d5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 12:38:37 -0800 Subject: [PATCH 0410/1986] Use the correct response hanlder for SETEX --- redis.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/redis.c b/redis.c index 05c4e74077..f8c5f6a477 100644 --- a/redis.c +++ b/redis.c @@ -835,8 +835,7 @@ PHP_METHOD(Redis, set) { */ PHP_METHOD(Redis, setex) { - REDIS_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, - redis_string_response); + REDIS_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, redis_boolean_response); } /* {{{ proto boolean Redis::psetex(string key, long expire, string value) From 1c314a664be9ceeda0fa3ea98290c25e46468a0b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 12:48:47 -0800 Subject: [PATCH 0411/1986] Fix reply type for RPUSHX and LPUSHX --- redis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis.c b/redis.c index f8c5f6a477..06f8c0bb2c 100644 --- a/redis.c +++ b/redis.c @@ -1171,14 +1171,14 @@ PHP_METHOD(Redis, lInsert) /* {{{ proto long Redis::lPushx(string key, mixed value) */ PHP_METHOD(Redis, lPushx) { - REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_long_response); } /* }}} */ /* {{{ proto long Redis::rPushx(string key, mixed value) */ PHP_METHOD(Redis, rPushx) { - REDIS_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, redis_string_response); + REDIS_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, redis_long_response); } /* }}} */ From a6d35b15f29896bcef75ce7cb9ff6fba2aee5938 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 13:00:12 -0800 Subject: [PATCH 0412/1986] Make sure we actually send the OBJECT command! --- redis.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/redis.c b/redis.c index 06f8c0bb2c..6d7bcad36c 100644 --- a/redis.c +++ b/redis.c @@ -2700,6 +2700,8 @@ PHP_METHOD(Redis, object) RETURN_FALSE; } + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + if(rtype == TYPE_INT) { IF_ATOMIC() { redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, From 2937b2b228f04d8b8464db7583f9a0c54ce6888d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 13:12:33 -0800 Subject: [PATCH 0413/1986] Adds 'quicklist' as a valid encoding type (new list type) --- tests/RedisTest.php | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 4d8fb1f080..cc777918ce 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2451,20 +2451,33 @@ public function testSetRange() { } public function testObject() { - $this->redis->del('key'); + /* 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", "lt")) { + $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') === "raw"); + $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'); - $this->assertTrue($this->redis->object('encoding', 'key') === "ziplist"); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + + /* 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'); @@ -2482,8 +2495,11 @@ public function testObject() { $this->redis->del('key'); $this->redis->lpush('key', str_repeat('A', pow(10,6))); // 1M elements, too big for a ziplist. - $this->assertTrue($this->redis->object('encoding', 'key') === "linkedlist"); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + + $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); } From 9f3fdfec2dd3bcce52c6ca5aeebf791aaacc74f9 Mon Sep 17 00:00:00 2001 From: ddrager Date: Tue, 20 Jan 2015 15:10:58 -0500 Subject: [PATCH 0414/1986] Small spelling fix --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index 582f0e6907..cf6b910efb 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2570,7 +2570,7 @@ PHP_METHOD(RedisCluster, info) { if(cluster_send_slot(c, slot, cmd, cmd_len, TYPE_BULK TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, - "Unable to send INFO command to spacific node", 0 TSRMLS_CC); + "Unable to send INFO command to specific node", 0 TSRMLS_CC); efree(cmd); RETURN_FALSE; } From f7356527e8b050dcf29c7dfcf67050f87f4a1284 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 13:36:10 -0800 Subject: [PATCH 0415/1986] Don't allow empty hash members, like older versions of phpredis --- redis_commands.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 8098241188..46a00e721e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1386,7 +1386,9 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_hash_move_forward_ex(ht_arr, &ptr)) { // We can only handle string or long values here - if(Z_TYPE_PP(z_mem)==IS_STRING || Z_TYPE_PP(z_mem)==IS_LONG) { + if ((Z_TYPE_PP(z_mem)==IS_STRING && Z_STRLEN_PP(z_mem)>0) + || Z_TYPE_PP(z_mem)==IS_LONG) + { // Copy into our member array MAKE_STD_ZVAL(z_mems[valid]); *z_mems[valid] = **z_mem; From 55084a8ed96386d92b5a058ea1e117b10e5f94b8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 13:43:52 -0800 Subject: [PATCH 0416/1986] Update tests to reflect that ZSCAN will return float scores --- tests/RedisTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index cc777918ce..6d489d3de4 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4782,8 +4782,9 @@ public function testZScan() { $i--; } } + $this->assertEquals(0, $i); - $this->assertEquals(0, $i_tot_score); + $this->assertEquals((float)0, $i_tot_score); // Just scan "pmem" members $it = NULL; @@ -4795,7 +4796,7 @@ public function testZScan() { $i_p_count -= 1; } } - $this->assertEquals(0, $i_p_score); + $this->assertEquals((float)0, $i_p_score); $this->assertEquals(0, $i_p_count); // Turn off retrying and we should get some empty results @@ -4813,7 +4814,7 @@ public function testZScan() { } // We should still get all the keys, just with several empty results $this->assertTrue($i_skips > 0); - $this->assertEquals(0, $i_p_score); + $this->assertEquals((float)0, $i_p_score); $this->assertEquals(0, $i_p_count); } From 0a36429ee7bc37714f11269d5d162b8139ade98b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 14:23:46 -0800 Subject: [PATCH 0417/1986] Add simple options to limit which tests are run --- tests/TestRedis.php | 10 ++++++++-- tests/TestSuite.php | 18 ++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 953fb4484d..3b43296807 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -4,9 +4,15 @@ require_once(dirname($_SERVER['PHP_SELF'])."/RedisArrayTest.php"); require_once(dirname($_SERVER['PHP_SELF'])."/RedisClusterTest.php"); +/* Grab options */ +$arr_args = getopt('', Array('class:', 'test:')); + /* Grab the test the user is trying to run */ $arr_valid_classes = Array('redis', 'redisarray', 'rediscluster'); -$str_class = isset($argv[1]) ? strtolower($argv[1]) : 'redis'; +$str_class = isset($arr_args['class']) ? strtolower($arr_args['class']) : 'redis'; + +/* Get our test filter if provided one */ +$str_filter = isset($arr_args['test']) ? $arr_args['test'] : NULL; /* Validate the class is known */ if (!in_array($str_class, $arr_valid_classes)) { @@ -19,7 +25,7 @@ /* Depending on the classes being tested, run our tests on it */ if ($str_class = 'redis') { - exit(TestSuite::run("Redis_Test")); + exit(TestSuite::run("Redis_Test", $str_filter)); } else if ($str_class == 'redisarray') { global $useIndex; foreach(array(true, false) as $useIndex) { diff --git a/tests/TestSuite.php b/tests/TestSuite.php index a3363bc33a..85996dd07e 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -46,8 +46,11 @@ protected function markTestSkipped($msg='') { throw new Exception($msg); } - public static function run($className, $str_limit) { - $rc = new ReflectionClass($className); + public static function run($className, $str_limit = NULL) { + /* Lowercase our limit arg if we're passed one */ + $str_limit = $str_limit ? strtolower($str_limit) : $str_limit; + + $rc = new ReflectionClass($className); $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC); $i_limit = -1; $i_idx = 0; @@ -66,17 +69,12 @@ public static function run($className, $str_limit) { if(substr($name, 0, 4) !== 'test') continue; - /* If TestRedis.php was envoked with an argument, do a simple - * match against the routine. Useful to limit to one test */ - if ($i_limit == -1 && $str_limit && strpos(strtolower($name),strtolower($str_limit))===false) - continue; - - if ($i_limit > -1 && $i_idx++ >= $i_limit) { + /* If we're trying to limit to a specific test and can't match the + * substring, skip */ + if ($str_limit && strstr(strtolower($name), $str_limit)===FALSE) { continue; } - $arr_ran_methods[] = $name; - $count = count($className::$errors); $rt = new $className; try { From 21f524217492a2f522237b23608d536e400716f5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 14:41:09 -0800 Subject: [PATCH 0418/1986] We need to send GET for each value we're getting --- redis_commands.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 46a00e721e..c82fff583c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2213,9 +2213,6 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_keys = Z_ARRVAL_PP(z_ele); int added=0; - // Add our "GET" option - add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,1); - for(zend_hash_internal_pointer_reset(ht_keys); zend_hash_has_more_elements(ht_keys)==SUCCESS; zend_hash_move_forward(ht_keys)) @@ -2228,6 +2225,9 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(Z_TYPE_PP(z_key)!=IS_STRING) continue; + /* Add get per thing we're getting */ + add_next_index_stringl(z_argv, "GET", sizeof("GET")-1, 1); + // Add this key to our argv array add_next_index_stringl(z_argv, Z_STRVAL_PP(z_key), Z_STRLEN_PP(z_key), 1); From f05d9054e0945e51a928518db414b8709627c5dd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 14:44:28 -0800 Subject: [PATCH 0419/1986] Make sure our options are something valid --- redis_commands.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/redis_commands.c b/redis_commands.c index c82fff583c..63a85bc140 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1101,6 +1101,14 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } + /* Our optional argument can either be a long (to support legacy SETEX */ + /* redirection), or an array with Redis >= 2.6.12 set options */ + if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY + && Z_TYPE_P(z_opts) != IS_NULL) + { + return FAILURE; + } + // Serialize and key prefix if required val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); From 892a322f531fb503b92808b2e0000551a42d962c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 15:29:29 -0800 Subject: [PATCH 0420/1986] Free the correct values in hset --- redis_commands.c | 6 +++--- tests/TestSuite.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 63a85bc140..3ccd64fb13 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1998,9 +1998,9 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set slot CMD_SET_SLOT(slot,key,key_len); - // Cleanup - if(key_free) STR_FREE(val); - if(val_free) efree(key); + /* Cleanup our key and value */ + if (val_free) STR_FREE(val); + if (key_free) efree(key); // Success return SUCCESS; diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 85996dd07e..8c27bd7a85 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -98,7 +98,7 @@ public static function run($className, $str_limit = NULL) { echo " --- fin ---\n"; if(empty($className::$errors)) { - echo "All tests passed.\n"; + echo "All tests passed. \o/\n"; return 0; } From 2f368bfedc18ee95b38cb3cd87e981772c05a8cf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 15:35:38 -0800 Subject: [PATCH 0421/1986] Backport memory leak fix from develop into redis_cluster --- redis.c | 6 +++--- redis_cluster.c | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index 6d7bcad36c..f5cafda372 100644 --- a/redis.c +++ b/redis.c @@ -3787,13 +3787,13 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { */ do { /* Free our previous reply if we're back in the loop. We know we are - * if our return_value is an array. */ - if(Z_TYPE_P(return_value) == IS_ARRAY) { + * if our return_value is an array */ + if (Z_TYPE_P(return_value) == IS_ARRAY) { zval_dtor(return_value); ZVAL_NULL(return_value); } - /* Format our SCAN command */ + // Format our SCAN command cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter, pattern, pattern_len, count); diff --git a/redis_cluster.c b/redis_cluster.c index cf6b910efb..a4ebeb5ece 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2351,6 +2351,12 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, // If SCAN_RETRY is set, loop until we get a zero iterator or until // we get non-zero elements. Otherwise we just send the command once. do { + /* Free our return value if we're back in the loop */ + if (Z_TYPE_P(return_value) == IS_ARRAY) { + zval_dtor(return_value); + ZVAL_NULL(return_value); + } + // Create command cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, it, pat, pat_len, count); @@ -2430,6 +2436,12 @@ PHP_METHOD(RedisCluster, scan) { /* With SCAN_RETRY on, loop until we get some keys, otherwise just return * what Redis does, as it does */ do { + /* Free our return value if we're back in the loop */ + if (Z_TYPE_P(return_value) == IS_ARRAY) { + zval_dtor(return_value); + ZVAL_NULL(return_value); + } + /* Construct our command */ cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, count); From 687a5ad64a6c143dcab4b19a7a27b2834c77e9af Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 30 Nov 2014 12:24:44 -0800 Subject: [PATCH 0422/1986] Implements the getMode() command Rename command to rawCommand() as it's named in phpredis proper This introspection function will inform the caller what mode phpredis is in (atomic, pipeline, multi) Conflicts: php_redis.h --- php_redis.h | 4 ++-- redis.c | 31 +++++++++++++++++++++++++------ redis_cluster.c | 12 ++++++------ redis_cluster.h | 2 +- redis_commands.c | 4 ++-- redis_commands.h | 2 +- 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/php_redis.h b/php_redis.h index 11e7f0dd9c..87339f56c4 100644 --- a/php_redis.h +++ b/php_redis.h @@ -197,7 +197,7 @@ PHP_METHOD(Redis, wait); PHP_METHOD(Redis, pubsub); PHP_METHOD(Redis, client); -PHP_METHOD(Redis, command); +PHP_METHOD(Redis, rawcommand); /* SCAN and friends */ PHP_METHOD(Redis, scan); @@ -220,7 +220,7 @@ PHP_METHOD(Redis, isConnected); PHP_METHOD(Redis, getPersistentID); PHP_METHOD(Redis, getAuth); PHP_METHOD(Redis, getMode); -PHP_METHOD(Redis, rawCommand); +PHP_METHOD(Redis, rawcommand); #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) diff --git a/redis.c b/redis.c index f5cafda372..bd3242835f 100644 --- a/redis.c +++ b/redis.c @@ -266,7 +266,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rawcommand, NULL, ZEND_ACC_PUBLIC) /* SCAN and friends */ PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) @@ -3482,6 +3482,25 @@ PHP_METHOD(Redis, clearLastError) { RETVAL_LONG(redis_sock->mode); } +/* + * {{{ proto long Redis::getMode() + */ +PHP_METHOD(Redis, getMode) { + zval *object; + RedisSock *redis_sock; + + /* Grab our object */ + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { + RETURN_FALSE; + } + + /* Grab socket */ + if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + RETURN_FALSE; + } + + RETVAL_LONG(redis_sock->mode); +} /* {{{ proto Redis::time() */ PHP_METHOD(Redis, time) { @@ -3648,11 +3667,11 @@ PHP_METHOD(Redis, client) { } } -/* proto array Redis::command() - * proto array Redis::command('info', string cmd) - * proto array Redis::command('getkeys', array cmd_args) */ -PHP_METHOD(Redis, command) { - REDIS_PROCESS_CMD(command, redis_read_variant_reply); +/* proto array Redis::rawcommand() + * proto array Redis::rawcommand('info', string cmd) + * proto array Redis::rawcommand('getkeys', array cmd_args) */ +PHP_METHOD(Redis, rawcommand) { + REDIS_PROCESS_CMD(rawcommand, redis_read_variant_reply); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index a4ebeb5ece..d083f7ff69 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -208,7 +208,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rawcommand, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, cluster, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, client, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, config, NULL, ZEND_ACC_PUBLIC) @@ -2714,11 +2714,11 @@ PHP_METHOD(RedisCluster, echo) { } /* }}} */ -/* {{{ proto array RedisCluster::command() - * proto array RedisCluster::command('INFO', string cmd) - * proto array RedisCluster::command('GETKEYS', array cmd_args) */ -PHP_METHOD(RedisCluster, command) { - CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); +/* {{{ proto array RedisCluster::rawcommand() + * proto array RedisCluster::rawcommand('INFO', string cmd) + * proto array RedisCluster::rawcommand('GETKEYS', array cmd_args) */ +PHP_METHOD(RedisCluster, rawcommand) { + CLUSTER_PROCESS_CMD(rawcommand, cluster_variant_resp, 0); } /* }}} */ diff --git a/redis_cluster.h b/redis_cluster.h index a801fc5193..c27494490a 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -264,7 +264,7 @@ PHP_METHOD(RedisCluster, time); PHP_METHOD(RedisCluster, randomkey); PHP_METHOD(RedisCluster, ping); PHP_METHOD(RedisCluster, echo); -PHP_METHOD(RedisCluster, command); +PHP_METHOD(RedisCluster, rawcommand); /* Introspection */ PHP_METHOD(RedisCluster, getlasterror); diff --git a/redis_commands.c b/redis_commands.c index 3ccd64fb13..a535de5d8d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2578,8 +2578,8 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* COMMAND */ -int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) { char *kw=NULL; zval *z_arg; diff --git a/redis_commands.h b/redis_commands.h index f54ea019c3..c2f72bf962 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -209,7 +209,7 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, From 7e1f082054cf7a2c918bfcfab53e110fa07bc6c6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 15:45:27 -0800 Subject: [PATCH 0423/1986] Implement getMode for RedisCluster --- redis_cluster.c | 12 ++++++++++-- redis_cluster.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index d083f7ff69..dd16e538e2 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -177,7 +177,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) - + + PHP_ME(RedisCluster, getmode, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getlasterror, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, clearlasterror, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) @@ -436,7 +437,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { PHP_METHOD(RedisCluster, __construct) { zval *object, *z_seeds=NULL; char *name; - long name_len, tmsec; + long name_len; double timeout = 0.0, read_timeout = 0.0; redisCluster *context = GET_CONTEXT(); @@ -1852,6 +1853,13 @@ PHP_METHOD(RedisCluster, evalsha) { /* Commands that do not interact with Redis, but just report stuff about * various options, etc */ +/* {{{ proto string RedisCluster::getmode() */ +PHP_METHOD(RedisCluster, getmode) { + redisCluster *c = GET_CONTEXT(); + RETURN_LONG(c->flags->mode); +} +/* }}} */ + /* {{{ proto string RedisCluster::getlasterror() */ PHP_METHOD(RedisCluster, getlasterror) { redisCluster *c = GET_CONTEXT(); diff --git a/redis_cluster.h b/redis_cluster.h index c27494490a..83185ef152 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -267,6 +267,7 @@ PHP_METHOD(RedisCluster, echo); PHP_METHOD(RedisCluster, rawcommand); /* Introspection */ +PHP_METHOD(RedisCluster, getmode); PHP_METHOD(RedisCluster, getlasterror); PHP_METHOD(RedisCluster, clearlasterror); PHP_METHOD(RedisCluster, getoption); From 9c67e7a63cbd482b20a62061c3678b5e67335cc4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 15:53:02 -0800 Subject: [PATCH 0424/1986] Capture error for multibulk responses --- library.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index f38e62cc53..88723f5ba7 100644 --- a/library.c +++ b/library.c @@ -1783,7 +1783,7 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, void *ctx) { char inbuf[1024]; - int numElems; + int numElems, err_len; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { @@ -1856,6 +1856,10 @@ PHPAPI int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_ IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); } else { + if (inbuf[0] == '-') { + err_len = strlen(inbuf+1) - 2; + redis_sock_set_err(redis_sock, inbuf+1, err_len); + } RETVAL_FALSE; } return -1; From 0efa2f790989538648d20cc5820608a775fa8805 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 17:13:36 -0800 Subject: [PATCH 0425/1986] Allow pfcount to take a string or array --- redis.c | 2 +- redis_cluster.c | 2 +- redis_commands.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++- redis_commands.h | 3 ++ 4 files changed, 122 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index bd3242835f..1cb720c274 100644 --- a/redis.c +++ b/redis.c @@ -3862,7 +3862,7 @@ PHP_METHOD(Redis, pfadd) { /* {{{ proto Redis::pfCount(string key) }}}*/ PHP_METHOD(Redis, pfcount) { - REDIS_PROCESS_KW_CMD("PFCOUNT", redis_key_cmd, redis_long_response); + REDIS_PROCESS_CMD(pfcount, redis_long_response); } /* {{{ proto Redis::pfMerge(string dstkey, array keys) }}}*/ diff --git a/redis_cluster.c b/redis_cluster.c index dd16e538e2..c4942323e9 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1485,7 +1485,7 @@ PHP_METHOD(RedisCluster, renamenx) { /* {{{ proto long RedisCluster::pfcount(string key) */ PHP_METHOD(RedisCluster, pfcount) { - CLUSTER_PROCESS_KW_CMD("PFCOUNT", redis_key_cmd, cluster_long_resp, 1); + CLUSTER_PROCESS_CMD(pfcount, cluster_long_resp, 1); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index a535de5d8d..1dc0329817 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1759,6 +1759,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Clean up prefixed or serialized data + if(mem_free) { if(!is_keys) { STR_FREE(mem); @@ -1791,7 +1792,122 @@ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "PFMERGE", sizeof("PFMERGE")-1, 1, cmd, cmd_len, slot); } -/* AUTH -- we need to update the password stored in RedisSock */ +/* PFCOUNT */ +int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zval *z_keys, **z_key, *z_tmp = NULL; + HashTable *ht_keys; + HashPosition ptr; + smart_str cmdstr = {0}; + int num_keys, key_len, key_free; + char *key; + short kslot=-1; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"z",&z_keys)==FAILURE) { + return FAILURE; + } + + /* If we were passed an array of keys, iterate through them prefixing if + * required and capturing lengths and if we need to free them. Otherwise + * attempt to treat the argument as a string and just pass one */ + if (Z_TYPE_P(z_keys) == IS_ARRAY) { + /* Grab key hash table and the number of keys */ + ht_keys = Z_ARRVAL_P(z_keys); + num_keys = zend_hash_num_elements(ht_keys); + + /* There is no reason to send zero keys */ + if (num_keys == 0) { + return FAILURE; + } + + /* Initialize the command with our number of arguments */ + redis_cmd_init_sstr(&cmdstr, num_keys, "PFCOUNT", sizeof("PFCOUNT")-1); + + /* Append our key(s) */ + for (zend_hash_internal_pointer_reset_ex(ht_keys, &ptr); + zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &ptr)==SUCCESS; + zend_hash_move_forward_ex(ht_keys, &ptr)) + { + /* Turn our value into a string if it isn't one */ + if (Z_TYPE_PP(z_key) != IS_STRING) { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = **z_key; + zval_copy_ctor(z_tmp); + convert_to_string(z_tmp); + + key = Z_STRVAL_P(z_tmp); + key_len = Z_STRLEN_P(z_tmp); + } else { + key = Z_STRVAL_PP(z_key); + key_len = Z_STRLEN_PP(z_key); + } + + /* Append this key to our command */ + key_free = redis_key_prefix(redis_sock, &key, &key_len); + redis_cmd_append_sstr(&cmdstr, key, key_len); + + /* Protect against CROSSLOT errors */ + if (slot) { + if (kslot == -1) { + kslot = cluster_hash_key(key, key_len); + } else if(cluster_hash_key(key,key_len)!=kslot) { + if (key_free) efree(key); + if (z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + } + efree(cmdstr.c); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Not all keys hash to the same slot!"); + return FAILURE; + } + } + + /* Cleanup */ + if (key_free) efree(key); + if (z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + z_tmp = NULL; + } + } + } else { + /* Turn our key into a string if it's a different type */ + if (Z_TYPE_P(z_keys) != IS_STRING) { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = *z_keys; + zval_copy_ctor(z_tmp); + convert_to_string(z_tmp); + + key = Z_STRVAL_P(z_tmp); + key_len = Z_STRLEN_P(z_tmp); + } else { + key = Z_STRVAL_P(z_keys); + key_len = Z_STRLEN_P(z_keys); + } + + /* Construct our whole command */ + redis_cmd_init_sstr(&cmdstr, 1, "PFCOUNT", sizeof("PFCOUNT")-1); + key_free = redis_key_prefix(redis_sock, &key, &key_len); + redis_cmd_append_sstr(&cmdstr, key, key_len); + + /* Cleanup */ + if (key_free) efree(key); + if (z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + } + } + + /* Push our command and length to the caller */ + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { diff --git a/redis_commands.h b/redis_commands.h index c2f72bf962..1e456b5979 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -133,6 +133,9 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From 887eff0a9ad0f35a982ecacfdfc93e11b3a7e079 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 Mar 2015 17:31:17 -0800 Subject: [PATCH 0426/1986] Fix memory leaks in RedisArray (backport from develop) --- redis_array_impl.c | 67 +++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index feed421ef1..d22ae620b3 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -347,11 +347,11 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev } ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL; - /* Set hash function and distribtor if provided */ + /* Set hash function and distribtor if provided */ ra->z_fun = z_fun; ra->z_dist = z_dist; - - return ra; + + return ra; } @@ -448,40 +448,36 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML zval * ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) { - - uint32_t hash; - char *out; - int pos, out_len; - - /* extract relevant part of the key */ - out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC); - if(!out) - return NULL; - - if(ra->z_dist) { - if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) { - efree(out); - return NULL; - } - } - else { - uint64_t h64; - - /* hash */ - hash = rcrc32(out, out_len); - - /* get position on ring */ - h64 = hash; - h64 *= ra->count; - h64 /= 0xffffffff; - pos = (int)h64; + uint32_t hash; + char *out; + int pos, out_len; + + /* extract relevant part of the key */ + out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC); + if(!out) return NULL; + + if(ra->z_dist) { + if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) { + efree(out); + return NULL; } - if(out_pos) *out_pos = pos; - - /* cleanup */ + } else { + /* hash */ + hash = rcrc32(out, out_len); efree(out); + + /* get position on ring */ + uint64_t h64 = hash; + h64 *= ra->count; + h64 /= 0xffffffff; + pos = (int)h64; + } + if(out_pos) *out_pos = pos; - return ra->redis[pos]; + /* Cleanup */ + efree(out); + + return ra->redis[pos]; } zval * @@ -961,6 +957,9 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS for(i = 0; i < 1 + 2 * count; ++i) { efree(z_zadd_args[i]); } + + /* Free the array itself */ + efree(z_zadd_args); zval_dtor(&z_ret); From 9c4de060e6be42d39e35c70de0cd37456f5587db Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 29 Jan 2015 15:57:17 -0800 Subject: [PATCH 0427/1986] Fix rehashing memory leaks The pattern to move a key for various types (strings, sets, zsets, hashes, etc) used a simple pattern: 1. Construct the call in order to get all of the keys from the source 2. Make a pass through call to the source node to get a response 3. Use the response to make a pass through call to the destination node The issue, however, was that we were using the same return value variable for both source and destination nodes, so we would leak the response from the source node. Conflicts: redis_array_impl.c --- redis_array_impl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index d22ae620b3..98dec588d5 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -957,7 +957,9 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS for(i = 0; i < 1 + 2 * count; ++i) { efree(z_zadd_args[i]); } - + + zval_dtor(&z_ret); + /* Free the array itself */ efree(z_zadd_args); From d7989b4d1bc735f9111e6a2b9998040c0eb10610 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 10:16:48 -0800 Subject: [PATCH 0428/1986] Throw on certain errors, auto reselect db (backport from develop) --- library.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/library.c b/library.c index 88723f5ba7..2f78a83ee6 100644 --- a/library.c +++ b/library.c @@ -123,6 +123,28 @@ static void redis_error_throw(char *err, size_t err_len TSRMLS_DC) { } } +PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { + if (!redis_sock->persistent) { + php_stream_close(redis_sock->stream); + } else { + php_stream_pclose(redis_sock->stream); + } + + efree(cmd); + + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return -1; + } + + if (strncmp(response, "+OK", 3)) { + efree(response); + return -1; + } + + efree(response); + return 0; +} + PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { if (!redis_sock->persistent) { php_stream_close(redis_sock->stream); @@ -178,14 +200,14 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) } } - /* We've reconnected if we have a count */ + /* We've connected if we have a count */ if (count) { /* If we're using a password, attempt a reauthorization */ if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) { return -1; } - /* If we're using a non zero db, reselect it */ + /* If we're using a non-zero db, reselect it */ if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) { return -1; } @@ -514,6 +536,9 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D err_len = strlen(inbuf+1) - 2; redis_sock_set_err(redis_sock, inbuf+1, err_len); + /* Filter our ERROR through the few that should actually throw */ + redis_error_throw(inbuf + 1, err_len TSRMLS_CC); + /* Handle stale data error */ if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { zend_throw_exception(redis_exception_ce, @@ -2262,9 +2287,9 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, return -1; } - /* If this is an error response, filter specific errors that should throw - * an exception, and set our error field in our RedisSock object. */ - if(reply_type == TYPE_ERR) { + // If this is an error response, check if it is a SYNC error, and throw in + // that case + if(reply_type == TYPE_ERR) { /* Handle throwable errors */ redis_error_throw(inbuf, line_size TSRMLS_CC); From b251c187309291f5928e12aec4a139c90d4bff53 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 10:31:07 -0800 Subject: [PATCH 0429/1986] Kill tabs with fire :-) --- tests/RedisTest.php | 4520 +++++++++++++++++++++---------------------- 1 file changed, 2260 insertions(+), 2260 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 6d489d3de4..24c33ca1be 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4,9 +4,9 @@ class Redis_Test extends TestSuite { - const HOST = '127.0.0.1'; - const PORT = 6379; - const AUTH = NULL; //replace with a string to use Redis authentication + const HOST = '127.0.0.1'; + const PORT = 6379; + const AUTH = NULL; //replace with a string to use Redis authentication /** * @var Redis @@ -20,21 +20,21 @@ public function setUp() $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); } - protected function newInstance() { + protected function newInstance() { $r = new Redis(); $r->connect(self::HOST, self::PORT); if(self::AUTH) { - $this->assertTrue($r->auth(self::AUTH)); - } - return $r; + $this->assertTrue($r->auth(self::AUTH)); + } + return $r; } public function tearDown() { - if($this->redis) { - $this->redis->close(); - } + if($this->redis) { + $this->redis->close(); + } // unset($this->redis); } @@ -58,24 +58,24 @@ public function testMinimumVersion() public function testPing() { - $this->assertEquals('+PONG', $this->redis->ping()); + $this->assertEquals('+PONG', $this->redis->ping()); - $count = 1000; - while($count --) { - $this->assertEquals('+PONG', $this->redis->ping()); - } + $count = 1000; + while($count --) { + $this->assertEquals('+PONG', $this->redis->ping()); + } } - public function testPipelinePublish() { + public function testPipelinePublish() { if (!$this->havePipeline()) { $this->markTestSkipped(); } - $ret = $this->redis->pipeline() - ->publish('chan', 'msg') - ->exec(); + $ret = $this->redis->pipeline() + ->publish('chan', 'msg') + ->exec(); - $this->assertTrue(is_array($ret) && count($ret) === 1 && $ret[0] >= 0); + $this->assertTrue(is_array($ret) && count($ret) === 1 && $ret[0] >= 0); } // Run some simple tests against the PUBSUB command. This is problematic, as we @@ -121,37 +121,37 @@ public function testPubSub() { public function testBitsets() { - $this->redis->delete('key'); - $this->assertTrue(0 === $this->redis->getBit('key', 0)); - $this->assertTrue(FALSE === $this->redis->getBit('key', -1)); - $this->assertTrue(0 === $this->redis->getBit('key', 100000)); - - $this->redis->set('key', "\xff"); - for($i = 0; $i < 8; $i++) { - $this->assertTrue(1 === $this->redis->getBit('key', $i)); - } - $this->assertTrue(0 === $this->redis->getBit('key', 8)); - - // change bit 0 - $this->assertTrue(1 === $this->redis->setBit('key', 0, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 0, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 0)); - $this->assertTrue("\x7f" === $this->redis->get('key')); - - // change bit 1 - $this->assertTrue(1 === $this->redis->setBit('key', 1, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 1, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 1)); - $this->assertTrue("\x3f" === $this->redis->get('key')); - - // change bit > 1 - $this->assertTrue(1 === $this->redis->setBit('key', 2, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 2, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 2)); - $this->assertTrue("\x1f" === $this->redis->get('key')); - - // values above 1 are changed to 1 but don't overflow on bits to the right. - $this->assertTrue(0 === $this->redis->setBit('key', 0, 0xff)); + $this->redis->delete('key'); + $this->assertTrue(0 === $this->redis->getBit('key', 0)); + $this->assertTrue(FALSE === $this->redis->getBit('key', -1)); + $this->assertTrue(0 === $this->redis->getBit('key', 100000)); + + $this->redis->set('key', "\xff"); + for($i = 0; $i < 8; $i++) { + $this->assertTrue(1 === $this->redis->getBit('key', $i)); + } + $this->assertTrue(0 === $this->redis->getBit('key', 8)); + + // change bit 0 + $this->assertTrue(1 === $this->redis->setBit('key', 0, 0)); + $this->assertTrue(0 === $this->redis->setBit('key', 0, 0)); + $this->assertTrue(0 === $this->redis->getBit('key', 0)); + $this->assertTrue("\x7f" === $this->redis->get('key')); + + // change bit 1 + $this->assertTrue(1 === $this->redis->setBit('key', 1, 0)); + $this->assertTrue(0 === $this->redis->setBit('key', 1, 0)); + $this->assertTrue(0 === $this->redis->getBit('key', 1)); + $this->assertTrue("\x3f" === $this->redis->get('key')); + + // change bit > 1 + $this->assertTrue(1 === $this->redis->setBit('key', 2, 0)); + $this->assertTrue(0 === $this->redis->setBit('key', 2, 0)); + $this->assertTrue(0 === $this->redis->getBit('key', 2)); + $this->assertTrue("\x1f" === $this->redis->get('key')); + + // values above 1 are changed to 1 but don't overflow on bits to the right. + $this->assertTrue(0 === $this->redis->setBit('key', 0, 0xff)); $this->assertTrue("\x9f" === $this->redis->get('key')); // Verify valid offset ranges @@ -182,25 +182,25 @@ public function testBitPos() { public function test1000() { - $s = str_repeat('A', 1000); - $this->redis->set('x', $s); - $this->assertEquals($s, $this->redis->get('x')); + $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')); + $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 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'); + $this->redis->set('x', '-ERR'); + $this->assertEquals($this->redis->get('x'), '-ERR'); } @@ -232,9 +232,9 @@ public function testSet() $i = 66000; $value2 = 'X'; while($i--) { - $value2 .= 'A'; + $value2 .= 'A'; } - $value2 .= 'X'; + $value2 .= 'X'; $this->redis->set('key', $value2); $this->assertEquals($value2, $this->redis->get('key')); @@ -262,10 +262,10 @@ public function testSet() $this->assertEquals(TRUE, $this->redis->set('key', TRUE)); $this->assertEquals('1', $this->redis->get('key')); - $this->assertEquals(True, $this->redis->set('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', NULL)); - $this->assertEquals('', $this->redis->get('key')); $this->assertEquals(True, $this->redis->set('key', gzcompress('42'))); $this->assertEquals('42', gzuncompress($this->redis->get('key'))); @@ -335,7 +335,7 @@ public function testExtendedSet() { } public function testGetSet() { - $this->redis->delete('key'); + $this->redis->delete('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'); @@ -350,136 +350,136 @@ public function testRandomKey() { public function testRename() { - // strings - $this->redis->delete('key0'); - $this->redis->set('key0', 'val0'); - $this->redis->renameKey('key0', 'key1'); - $this->assertTrue($this->redis->get('key0') === FALSE); - $this->assertTrue($this->redis->get('key1') === 'val0'); - - - // lists - $this->redis->delete('key0'); - $this->redis->lPush('key0', 'val0'); - $this->redis->lPush('key0', 'val1'); - $this->redis->renameKey('key0', 'key1'); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); - $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1', 'val0')); - - // variadic - $this->redis->delete('key0'); - $this->assertTrue(3 === $this->redis->lPush('key0', 'val0', 'val1', 'val2')); - $this->assertTrue(array('val2', 'val1', 'val0') === $this->redis->lrange('key0', 0, -1)); - - $this->redis->delete('key0'); - $this->assertTrue(3 === $this->redis->rPush('key0', 'val0', 'val1', 'val2')); - $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->lrange('key0', 0, -1)); + // strings + $this->redis->delete('key0'); + $this->redis->set('key0', 'val0'); + $this->redis->renameKey('key0', 'key1'); + $this->assertTrue($this->redis->get('key0') === FALSE); + $this->assertTrue($this->redis->get('key1') === 'val0'); + + + // lists + $this->redis->delete('key0'); + $this->redis->lPush('key0', 'val0'); + $this->redis->lPush('key0', 'val1'); + $this->redis->renameKey('key0', 'key1'); + $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); + $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1', 'val0')); + + // variadic + $this->redis->delete('key0'); + $this->assertTrue(3 === $this->redis->lPush('key0', 'val0', 'val1', 'val2')); + $this->assertTrue(array('val2', 'val1', 'val0') === $this->redis->lrange('key0', 0, -1)); + + $this->redis->delete('key0'); + $this->assertTrue(3 === $this->redis->rPush('key0', 'val0', 'val1', 'val2')); + $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->lrange('key0', 0, -1)); } public function testRenameNx() { - // strings - $this->redis->delete('key0', 'key1'); - $this->redis->set('key0', 'val0'); - $this->redis->set('key1', 'val1'); - $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); - $this->assertTrue($this->redis->get('key0') === 'val0'); - $this->assertTrue($this->redis->get('key1') === 'val1'); - - // lists - $this->redis->delete('key0'); - $this->redis->delete('key1'); - $this->redis->lPush('key0', 'val0'); - $this->redis->lPush('key0', 'val1'); - $this->redis->lPush('key1', 'val1-0'); - $this->redis->lPush('key1', 'val1-1'); - $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array('val1', 'val0')); - $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1-1', 'val1-0')); - - $this->redis->delete('key2'); - $this->assertTrue($this->redis->renameNx('key0', 'key2') === TRUE); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); - $this->assertTrue($this->redis->lGetRange('key2', 0, -1) === array('val1', 'val0')); + // strings + $this->redis->delete('key0', 'key1'); + $this->redis->set('key0', 'val0'); + $this->redis->set('key1', 'val1'); + $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); + $this->assertTrue($this->redis->get('key0') === 'val0'); + $this->assertTrue($this->redis->get('key1') === 'val1'); + + // lists + $this->redis->delete('key0'); + $this->redis->delete('key1'); + $this->redis->lPush('key0', 'val0'); + $this->redis->lPush('key0', 'val1'); + $this->redis->lPush('key1', 'val1-0'); + $this->redis->lPush('key1', 'val1-1'); + $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); + $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array('val1', 'val0')); + $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1-1', 'val1-0')); + + $this->redis->delete('key2'); + $this->assertTrue($this->redis->renameNx('key0', 'key2') === TRUE); + $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); + $this->assertTrue($this->redis->lGetRange('key2', 0, -1) === array('val1', 'val0')); } public function testMultiple() { - $this->redis->delete('k1'); - $this->redis->delete('k2'); - $this->redis->delete('k3'); + $this->redis->delete('k1'); + $this->redis->delete('k2'); + $this->redis->delete('k3'); - $this->redis->set('k1', 'v1'); - $this->redis->set('k2', 'v2'); - $this->redis->set('k3', 'v3'); - $this->redis->set(1, 'test'); + $this->redis->set('k1', 'v1'); + $this->redis->set('k2', 'v2'); + $this->redis->set('k3', 'v3'); + $this->redis->set(1, 'test'); - $this->assertEquals(array('v1'), $this->redis->getMultiple(array('k1'))); - $this->assertEquals(array('v1', 'v3', false), $this->redis->getMultiple(array('k1', 'k3', 'NoKey'))); - $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + $this->assertEquals(array('v1'), $this->redis->getMultiple(array('k1'))); + $this->assertEquals(array('v1', 'v3', false), $this->redis->getMultiple(array('k1', 'k3', 'NoKey'))); + $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - $this->redis->set('k5', '$1111111111'); - $this->assertEquals(array(0 => '$1111111111'), $this->redis->getMultiple(array('k5'))); + $this->redis->set('k5', '$1111111111'); + $this->assertEquals(array(0 => '$1111111111'), $this->redis->getMultiple(array('k5'))); - $this->assertEquals(array(0 => 'test'), $this->redis->getMultiple(array(1))); // non-string + $this->assertEquals(array(0 => 'test'), $this->redis->getMultiple(array(1))); // non-string } public function testMultipleBin() { - $this->redis->delete('k1'); - $this->redis->delete('k2'); - $this->redis->delete('k3'); + $this->redis->delete('k1'); + $this->redis->delete('k2'); + $this->redis->delete('k3'); - $this->redis->set('k1', gzcompress('v1')); - $this->redis->set('k2', gzcompress('v2')); - $this->redis->set('k3', gzcompress('v3')); + $this->redis->set('k1', gzcompress('v1')); + $this->redis->set('k2', gzcompress('v2')); + $this->redis->set('k3', gzcompress('v3')); - $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); } public function testSetTimeout() { - $this->redis->delete('key'); + $this->redis->delete('key'); $this->redis->set('key', 'value'); - $this->assertEquals('value', $this->redis->get('key')); - $this->redis->setTimeout('key', 1); - $this->assertEquals('value', $this->redis->get('key')); - sleep(2); - $this->assertEquals(False, $this->redis->get('key')); + $this->assertEquals('value', $this->redis->get('key')); + $this->redis->setTimeout('key', 1); + $this->assertEquals('value', $this->redis->get('key')); + sleep(2); + $this->assertEquals(False, $this->redis->get('key')); } public function testExpireAt() { - $this->redis->delete('key'); + $this->redis->delete('key'); $this->redis->set('key', 'value'); - $now = time(NULL); - $this->redis->expireAt('key', $now + 1); - $this->assertEquals('value', $this->redis->get('key')); - sleep(2); - $this->assertEquals(FALSE, $this->redis->get('key')); + $now = time(NULL); + $this->redis->expireAt('key', $now + 1); + $this->assertEquals('value', $this->redis->get('key')); + sleep(2); + $this->assertEquals(FALSE, $this->redis->get('key')); } public function testSetEx() { - $this->redis->delete('key'); - $this->assertTrue($this->redis->setex('key', 7, 'val') === TRUE); - $this->assertTrue($this->redis->ttl('key') ===7); - $this->assertTrue($this->redis->get('key') === 'val'); + $this->redis->delete('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 testSetNX() { - $this->redis->set('key', 42); - $this->assertTrue($this->redis->setnx('key', 'err') === FALSE); - $this->assertTrue($this->redis->get('key') === '42'); + $this->redis->set('key', 42); + $this->assertTrue($this->redis->setnx('key', 'err') === FALSE); + $this->assertTrue($this->redis->get('key') === '42'); - $this->redis->delete('key'); - $this->assertTrue($this->redis->setnx('key', '42') === TRUE); - $this->assertTrue($this->redis->get('key') === '42'); + $this->redis->delete('key'); + $this->assertTrue($this->redis->setnx('key', '42') === TRUE); + $this->assertTrue($this->redis->get('key') === '42'); } public function testExpireAtWithLong() { @@ -494,38 +494,38 @@ public function testIncr() $this->redis->set('key', 0); $this->redis->incr('key'); - $this->assertEquals(1, (int)$this->redis->get('key')); + $this->assertEquals(1, (int)$this->redis->get('key')); $this->redis->incr('key'); - $this->assertEquals(2, (int)$this->redis->get('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', 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(6, (int)$this->redis->get('key')); - $this->redis->incrBy('key', -1); - $this->assertEquals(5, (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->delete('key'); + $this->redis->delete('key'); - $this->redis->set('key', 'abc'); + $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->incr('key'); - $this->assertTrue("abc" === $this->redis->get('key')); - } + $this->redis->incr('key'); + $this->assertTrue("abc" === $this->redis->get('key')); + } public function testIncrByFloat() { - // incrbyfloat is new in 2.6.0 - if (version_compare($this->version, "2.5.0", "lt")) { + // incrbyfloat is new in 2.6.0 + if (version_compare($this->version, "2.5.0", "lt")) { $this->markTestSkipped(); } @@ -566,19 +566,19 @@ public function testDecr() $this->redis->set('key', 5); $this->redis->decr('key'); - $this->assertEquals(4, (int)$this->redis->get('key')); + $this->assertEquals(4, (int)$this->redis->get('key')); $this->redis->decr('key'); - $this->assertEquals(3, (int)$this->redis->get('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', 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', 1); + $this->assertEquals(0, (int)$this->redis->get('key')); - $this->redis->decrBy('key', -10); - $this->assertEquals(10, (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')); @@ -588,7 +588,7 @@ public function testDecr() public function testExists() { - $this->redis->delete('key'); + $this->redis->delete('key'); $this->assertFalse($this->redis->exists('key')); $this->redis->set('key', 'val'); $this->assertEquals(True, $this->redis->exists('key')); @@ -598,242 +598,242 @@ public function testGetKeys() { $pattern = 'getKeys-test-'; - for($i = 1; $i < 10; $i++) { - $this->redis->set($pattern.$i, $i); + for($i = 1; $i < 10; $i++) { + $this->redis->set($pattern.$i, $i); } $this->redis->delete($pattern.'3'); $keys = $this->redis->getKeys($pattern.'*'); - $this->redis->set($pattern.'3', 'something'); + $this->redis->set($pattern.'3', 'something'); $keys2 = $this->redis->getKeys($pattern.'*'); $this->assertEquals((count($keys) + 1), count($keys2)); - // empty array when no key matches + // empty array when no key matches $this->assertEquals(array(), $this->redis->getKeys(rand().rand().rand().'*')); } public function testDelete() { - $key = 'key' . rand(); + $key = 'key' . rand(); $this->redis->set($key, 'val'); $this->assertEquals('val', $this->redis->get($key)); - $this->assertEquals(1, $this->redis->delete($key)); + $this->assertEquals(1, $this->redis->delete($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->delete('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->delete('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->delete('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->delete(array('x', 'y'))); + // multiple, all existing + $this->redis->set('x', 0); + $this->redis->set('y', 1); + $this->redis->set('z', 2); + $this->assertEquals(3, $this->redis->delete('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->delete('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->delete('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->delete(array('x', 'y'))); } public function testType() { - // 0 => none, (key didn't exist) - // 1=> string, - // 2 => set, - // 3 => list, - // 4 => zset, - // 5 => hash - - // 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->delete('keySet'); - $this->redis->sAdd('keySet', 'val0'); - $this->redis->sAdd('keySet', 'val1'); - $this->assertEquals(Redis::REDIS_SET, $this->redis->type('keySet')); + // 0 => none, (key didn't exist) + // 1=> string, + // 2 => set, + // 3 => list, + // 4 => zset, + // 5 => hash + + // 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->delete('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->delete(123); - $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); - $this->assertTrue(array('val0') === $this->redis->sMembers(123)); - - // zset - $this->redis->delete('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->delete('keyHash'); - $this->redis->hSet('keyHash', 'key0', 'val0'); - $this->redis->hSet('keyHash', 'key1', 'val1'); - $this->assertEquals(Redis::REDIS_HASH, $this->redis->type('keyHash')); - - //None - $this->assertEquals(Redis::REDIS_NOT_FOUND, $this->redis->type('keyNotExists')); + $this->redis->delete(123); + $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); + $this->assertTrue(array('val0') === $this->redis->sMembers(123)); + + // zset + $this->redis->delete('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->delete('keyHash'); + $this->redis->hSet('keyHash', 'key0', 'val0'); + $this->redis->hSet('keyHash', 'key1', 'val1'); + $this->assertEquals(Redis::REDIS_HASH, $this->redis->type('keyHash')); + + //None + $this->assertEquals(Redis::REDIS_NOT_FOUND, $this->redis->type('keyNotExists')); } - public function testStr() { + 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->set('key', 'val1'); + $this->assertTrue($this->redis->append('key', 'val2') === 8); + $this->assertTrue($this->redis->get('key') === 'val1val2'); - $this->assertTrue($this->redis->append('keyNotExist', 'value') === 5); - $this->assertTrue($this->redis->get('keyNotExist') === 'value'); + $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', 0, 3) === 'This'); - $this->assertTrue($this->redis->getRange('key', -6, -1) === 'string'); - $this->assertTrue($this->redis->getRange('key', -6, 100000) === 'string'); - $this->assertTrue($this->redis->get('key') === 'This is a string'); + $this->redis->set('key', 'This is a string') ; + $this->assertTrue($this->redis->getRange('key', 0, 3) === 'This'); + $this->assertTrue($this->redis->getRange('key', -6, -1) === 'string'); + $this->assertTrue($this->redis->getRange('key', -6, 100000) === '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', '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); - } + $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 + // rpush => tail + // lpush => head $this->redis->delete('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); - $this->redis->rPush('list', 'val3'); + $this->redis->rPush('list', 'val3'); - // 'list' = [ 'val2', 'val', 'val3'] + // 'list' = [ 'val2', 'val', 'val3'] - $this->assertEquals('val2', $this->redis->lPop('list')); + $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 + // testing binary data - $this->redis->delete('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->redis->delete('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'))); + $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 + // rpush => tail + // lpush => head $this->redis->delete('list'); $this->redis->rPush('list', 'val'); $this->redis->rPush('list', 'val2'); - $this->redis->lPush('list', 'val3'); + $this->redis->lPush('list', 'val3'); - // 'list' = [ 'val3', 'val', 'val2'] + // 'list' = [ 'val3', 'val', 'val2'] - $this->assertEquals('val2', $this->redis->rPop('list')); + $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 + // testing binary data - $this->redis->delete('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->redis->delete('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'))); + $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() { + public function testblockingPop() { - // non blocking blPop, brPop + // non blocking blPop, brPop $this->redis->delete('list'); $this->redis->lPush('list', 'val1'); $this->redis->lPush('list', 'val2'); - $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val2')); - $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val1')); + $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val2')); + $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val1')); $this->redis->delete('list'); $this->redis->lPush('list', 'val1'); $this->redis->lPush('list', 'val2'); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val1')); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val2')); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val1')); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val2')); - // blocking blpop, brpop + // blocking blpop, brpop $this->redis->delete('list'); - $this->assertTrue($this->redis->blPop(array('list'), 1) === array()); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array()); - - // TODO: fix this broken test. -// $this->redis->delete('list'); -// $params = array( -// 0 => array("pipe", "r"), -// 1 => array("pipe", "w"), -// 2 => array("file", "/dev/null", "w") -// ); -// if(function_exists('proc_open')) { -// $env = array('PHPREDIS_key' =>'list', 'PHPREDIS_value' => 'value'); -// $process = proc_open('php', $params, $pipes, '/tmp', $env); + $this->assertTrue($this->redis->blPop(array('list'), 1) === array()); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array()); + + // TODO: fix this broken test. +// $this->redis->delete('list'); +// $params = array( +// 0 => array("pipe", "r"), +// 1 => array("pipe", "w"), +// 2 => array("file", "/dev/null", "w") +// ); +// if(function_exists('proc_open')) { +// $env = array('PHPREDIS_key' =>'list', 'PHPREDIS_value' => 'value'); +// $process = proc_open('php', $params, $pipes, '/tmp', $env); // -// if (is_resource($process)) { -// fwrite($pipes[0], 'connect("'.self::HOST.'", '.self::PORT.'); -// if("'.addslashes(self::AUTH).'") { -// $r->auth("'.addslashes(self::AUTH).'"); -// } -// $r->lPush($_ENV["PHPREDIS_key"], $_ENV["PHPREDIS_value"]); -// ?' . '>'); +// if (is_resource($process)) { +// fwrite($pipes[0], 'connect("'.self::HOST.'", '.self::PORT.'); +// if("'.addslashes(self::AUTH).'") { +// $r->auth("'.addslashes(self::AUTH).'"); +// } +// $r->lPush($_ENV["PHPREDIS_key"], $_ENV["PHPREDIS_value"]); +// ?' . '>'); // -// fclose($pipes[0]); -// fclose($pipes[1]); -// $re = proc_close($process); +// fclose($pipes[0]); +// fclose($pipes[1]); +// $re = proc_close($process); // -// $this->assertTrue($this->redis->blPop(array('list'), 5) === array("list", "value")); -// } -// } +// $this->assertTrue($this->redis->blPop(array('list'), 5) === array("list", "value")); +// } +// } } @@ -848,17 +848,17 @@ public function testlSize() $this->redis->lPush('list', 'val2'); $this->assertEquals(2, $this->redis->lSize('list')); - $this->assertEquals('val2', $this->redis->lPop('list')); + $this->assertEquals('val2', $this->redis->lPop('list')); $this->assertEquals(1, $this->redis->lSize('list')); - $this->assertEquals('val', $this->redis->lPop('list')); + $this->assertEquals('val', $this->redis->lPop('list')); $this->assertEquals(0, $this->redis->lSize('list')); $this->assertEquals(FALSE, $this->redis->lPop('list')); - $this->assertEquals(0, $this->redis->lSize('list')); // empty returns 0 + $this->assertEquals(0, $this->redis->lSize('list')); // empty returns 0 $this->redis->delete('list'); - $this->assertEquals(0, $this->redis->lSize('list')); // non-existent returns 0 + $this->assertEquals(0, $this->redis->lSize('list')); // non-existent returns 0 $this->redis->set('list', 'actually not a list'); $this->assertEquals(FALSE, $this->redis->lSize('list'));// not a list returns FALSE @@ -866,83 +866,83 @@ public function testlSize() //lInsert, lPopx, rPopx public function testlPopx() { - //test lPushx/rPushx - $this->redis->delete('keyNotExists'); - $this->assertTrue($this->redis->lPushx('keyNotExists', 'value') === 0); - $this->assertTrue($this->redis->rPushx('keyNotExists', 'value') === 0); + //test lPushx/rPushx + $this->redis->delete('keyNotExists'); + $this->assertTrue($this->redis->lPushx('keyNotExists', 'value') === 0); + $this->assertTrue($this->redis->rPushx('keyNotExists', 'value') === 0); - $this->redis->delete('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->lGetRange('key', 0, -1) === array('val1', 'val0', 'val2')); + $this->redis->delete('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->lGetRange('key', 0, -1) === array('val1', 'val0', 'val2')); - //test linsert - $this->redis->delete('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); + //test linsert + $this->redis->delete('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->lGetRange('key', 0, -1) === array('val2', 'val0', 'val1')); + $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->lGetRange('key', 0, -1) === array('val2', 'val0', 'val1')); } // ltrim, lsize, lpop public function testlistTrim() { - $this->redis->delete('list'); + $this->redis->delete('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->listTrim('list', 0, 2)); - $this->assertEquals(3, $this->redis->lSize('list')); + $this->assertEquals(TRUE, $this->redis->listTrim('list', 0, 2)); + $this->assertEquals(3, $this->redis->lSize('list')); $this->redis->listTrim('list', 0, 0); $this->assertEquals(1, $this->redis->lSize('list')); - $this->assertEquals('val4', $this->redis->lPop('list')); + $this->assertEquals('val4', $this->redis->lPop('list')); - $this->assertEquals(TRUE, $this->redis->listTrim('list', 10, 10000)); - $this->assertEquals(TRUE, $this->redis->listTrim('list', 10000, 10)); + $this->assertEquals(TRUE, $this->redis->listTrim('list', 10, 10000)); + $this->assertEquals(TRUE, $this->redis->listTrim('list', 10000, 10)); - // test invalid type - $this->redis->set('list', 'not a list...'); - $this->assertEquals(FALSE, $this->redis->listTrim('list', 0, 2)); + // test invalid type + $this->redis->set('list', 'not a list...'); + $this->assertEquals(FALSE, $this->redis->listTrim('list', 0, 2)); } 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->delete('person:id'); - foreach(array(1,2,3,4) as $id) { - $this->redis->lPush('person:id', $id); - } + // 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->delete('person:id'); + foreach(array(1,2,3,4) as $id) { + $this->redis->lPush('person:id', $id); + } } public function testSortPrefix() { - // Make sure that sorting works with a prefix + // 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); @@ -950,114 +950,114 @@ public function testSortPrefix() { $this->redis->sadd('some-item', 3); $this->assertEquals(array('1','2','3'), $this->redis->sortAsc('some-item')); - $this->assertEquals(array('3','2','1'), $this->redis->sortDesc('some-item')); - $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item')); + $this->assertEquals(array('3','2','1'), $this->redis->sortDesc('some-item')); + $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item')); - // Kill our set/prefix - $this->redis->del('some-item'); - $this->redis->setOption(Redis::OPT_PREFIX, ''); + // Kill our set/prefix + $this->redis->del('some-item'); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testSortAsc() { - $this->setupSort(); - - $this->assertTrue(FALSE === $this->redis->sortAsc(NULL)); - - // sort by age and get IDs - $byAgeAsc = array('3','1','2','4'); - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*')); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'sort' => 'asc'))); - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL)); // check that NULL works. - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sort('person:id', array('sort' => 'asc'))); - - // sort by age and get names - $byAgeAsc = array('Carol','Alice','Bob','Dave'); - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*')); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'))); - - $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 2)); - $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 2), 'sort' => 'asc'))); - - $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); - $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(1, 2), 'sort' => 'asc'))); - $this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 4)))); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, "4")))); // with strings - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array("0", 4)))); - $this->assertEquals(array(), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. - - // sort by salary and get ages - $agesBySalaryAsc = array('34', '27', '25', '41'); - $this->assertEquals($agesBySalaryAsc, $this->redis->sortAsc('person:id', 'person:salary_*', 'person:age_*')); - $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'))); - - $agesAndSalaries = $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => array('person:age_*', 'person:salary_*'), 'sort' => 'asc')); - $this->assertEquals(array('34', '2000', '27', '2500', '25', '2800', '41', '3100'), $agesAndSalaries); - - - // sort non-alpha doesn't change all-string lists - // list → [ghi, def, abc] - $list = array('abc', 'def', 'ghi'); - $this->redis->delete('list'); - foreach($list as $i) { - $this->redis->lPush('list', $i); - } - - // SORT list → [ghi, def, abc] - if (version_compare($this->version, "2.5.0", "lt")) { - $this->assertEquals(array_reverse($list), $this->redis->sortAsc('list')); - $this->assertEquals(array_reverse($list), $this->redis->sort('list', array('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->sortAscAlpha('list')); - $this->assertEquals($list, $this->redis->sort('list', array('sort' => 'asc', 'alpha' => TRUE))); + $this->setupSort(); + + $this->assertTrue(FALSE === $this->redis->sortAsc(NULL)); + + // sort by age and get IDs + $byAgeAsc = array('3','1','2','4'); + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*')); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'sort' => 'asc'))); + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL)); // check that NULL works. + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sort('person:id', array('sort' => 'asc'))); + + // sort by age and get names + $byAgeAsc = array('Carol','Alice','Bob','Dave'); + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*')); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'))); + + $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 2)); + $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 2), 'sort' => 'asc'))); + + $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); + $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(1, 2), 'sort' => 'asc'))); + $this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 4)))); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, "4")))); // with strings + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array("0", 4)))); + $this->assertEquals(array(), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. + + // sort by salary and get ages + $agesBySalaryAsc = array('34', '27', '25', '41'); + $this->assertEquals($agesBySalaryAsc, $this->redis->sortAsc('person:id', 'person:salary_*', 'person:age_*')); + $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'))); + + $agesAndSalaries = $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => array('person:age_*', 'person:salary_*'), 'sort' => 'asc')); + $this->assertEquals(array('34', '2000', '27', '2500', '25', '2800', '41', '3100'), $agesAndSalaries); + + + // sort non-alpha doesn't change all-string lists + // list → [ghi, def, abc] + $list = array('abc', 'def', 'ghi'); + $this->redis->delete('list'); + foreach($list as $i) { + $this->redis->lPush('list', $i); + } + + // SORT list → [ghi, def, abc] + if (version_compare($this->version, "2.5.0", "lt")) { + $this->assertEquals(array_reverse($list), $this->redis->sortAsc('list')); + $this->assertEquals(array_reverse($list), $this->redis->sort('list', array('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->sortAscAlpha('list')); + $this->assertEquals($list, $this->redis->sort('list', array('sort' => 'asc', 'alpha' => TRUE))); } public function testSortDesc() { - $this->setupSort(); + $this->setupSort(); - // sort by age and get IDs - $byAgeDesc = array('4','2','1','3'); - $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*')); + // sort by age and get IDs + $byAgeDesc = array('4','2','1','3'); + $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*')); - // sort by age and get names - $byAgeDesc = array('Dave', 'Bob', 'Alice', 'Carol'); - $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*')); + // sort by age and get names + $byAgeDesc = array('Dave', 'Bob', 'Alice', 'Carol'); + $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*')); - $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 0, 2)); - $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 1, 2)); + $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 0, 2)); + $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 1, 2)); - // sort by salary and get ages - $agesBySalaryDesc = array('41', '25', '27', '34'); - $this->assertEquals($agesBySalaryDesc, $this->redis->sortDesc('person:id', 'person:salary_*', 'person:age_*')); + // sort by salary and get ages + $agesBySalaryDesc = array('41', '25', '27', '34'); + $this->assertEquals($agesBySalaryDesc, $this->redis->sortDesc('person:id', 'person:salary_*', 'person:age_*')); - // sort non-alpha doesn't change all-string lists - $list = array('def', 'abc', 'ghi'); - $this->redis->delete('list'); - foreach($list as $i) { - $this->redis->lPush('list', $i); - } + // sort non-alpha doesn't change all-string lists + $list = array('def', 'abc', 'ghi'); + $this->redis->delete('list'); + foreach($list as $i) { + $this->redis->lPush('list', $i); + } - // SORT list → [ghi, abc, def] - if (version_compare($this->version, "2.5.0", "lt")) { - $this->assertEquals(array_reverse($list), $this->redis->sortDesc('list')); - } 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 → [ghi, abc, def] + if (version_compare($this->version, "2.5.0", "lt")) { + $this->assertEquals(array_reverse($list), $this->redis->sortDesc('list')); + } 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(array('ghi', 'def', 'abc'), $this->redis->sortDescAlpha('list')); + // SORT list ALPHA → [abc, def, ghi] + $this->assertEquals(array('ghi', 'def', 'abc'), $this->redis->sortDescAlpha('list')); } // LINDEX @@ -1069,62 +1069,62 @@ public function testlGet() { $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); - $this->assertEquals('val3', $this->redis->lGet('list', 0)); + $this->assertEquals('val3', $this->redis->lGet('list', 0)); $this->assertEquals('val2', $this->redis->lGet('list', 1)); - $this->assertEquals('val', $this->redis->lGet('list', 2)); - $this->assertEquals('val', $this->redis->lGet('list', -1)); - $this->assertEquals('val2', $this->redis->lGet('list', -2)); - $this->assertEquals('val3', $this->redis->lGet('list', -3)); - $this->assertEquals(FALSE, $this->redis->lGet('list', -4)); + $this->assertEquals('val', $this->redis->lGet('list', 2)); + $this->assertEquals('val', $this->redis->lGet('list', -1)); + $this->assertEquals('val2', $this->redis->lGet('list', -2)); + $this->assertEquals('val3', $this->redis->lGet('list', -3)); + $this->assertEquals(FALSE, $this->redis->lGet('list', -4)); $this->redis->rPush('list', 'val4'); - $this->assertEquals('val4', $this->redis->lGet('list', 3)); - $this->assertEquals('val4', $this->redis->lGet('list', -1)); + $this->assertEquals('val4', $this->redis->lGet('list', 3)); + $this->assertEquals('val4', $this->redis->lGet('list', -1)); } // lRem testing public function testlRemove() { - $this->redis->delete('list'); + $this->redis->delete('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->lRemove('list', 'b', 2); - // ['c', 'c', 'c', 'a'] - $this->assertEquals(2, $return); - $this->assertEquals('c', $this->redis->lGET('list', 0)); - $this->assertEquals('c', $this->redis->lGET('list', 1)); - $this->assertEquals('c', $this->redis->lGET('list', 2)); - $this->assertEquals('a', $this->redis->lGET('list', 3)); - - $this->redis->delete('list'); + // ['c', 'b', 'c', 'c', 'b', 'a'] + $return = $this->redis->lRemove('list', 'b', 2); + // ['c', 'c', 'c', 'a'] + $this->assertEquals(2, $return); + $this->assertEquals('c', $this->redis->lGET('list', 0)); + $this->assertEquals('c', $this->redis->lGET('list', 1)); + $this->assertEquals('c', $this->redis->lGET('list', 2)); + $this->assertEquals('a', $this->redis->lGET('list', 3)); + + $this->redis->delete('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->lRemove('list', 'c', -2); - // ['c', 'b', 'b', 'a'] - $this->assertEquals(2, $return); - $this->assertEquals('c', $this->redis->lGET('list', 0)); - $this->assertEquals('b', $this->redis->lGET('list', 1)); - $this->assertEquals('b', $this->redis->lGET('list', 2)); - $this->assertEquals('a', $this->redis->lGET('list', 3)); - - // remove each element - $this->assertEquals(1, $this->redis->lRemove('list', 'a', 0)); - $this->assertEquals(0, $this->redis->lRemove('list', 'x', 0)); - $this->assertEquals(2, $this->redis->lRemove('list', 'b', 0)); - $this->assertEquals(1, $this->redis->lRemove('list', 'c', 0)); - $this->assertEquals(FALSE, $this->redis->get('list')); - - $this->redis->set('list', 'actually not a list'); - $this->assertEquals(FALSE, $this->redis->lRemove('list', 'x')); + // ['c', 'b', 'c', 'c', 'b', 'a'] + $this->redis->lRemove('list', 'c', -2); + // ['c', 'b', 'b', 'a'] + $this->assertEquals(2, $return); + $this->assertEquals('c', $this->redis->lGET('list', 0)); + $this->assertEquals('b', $this->redis->lGET('list', 1)); + $this->assertEquals('b', $this->redis->lGET('list', 2)); + $this->assertEquals('a', $this->redis->lGET('list', 3)); + + // remove each element + $this->assertEquals(1, $this->redis->lRemove('list', 'a', 0)); + $this->assertEquals(0, $this->redis->lRemove('list', 'x', 0)); + $this->assertEquals(2, $this->redis->lRemove('list', 'b', 0)); + $this->assertEquals(1, $this->redis->lRemove('list', 'c', 0)); + $this->assertEquals(FALSE, $this->redis->get('list')); + + $this->redis->set('list', 'actually not a list'); + $this->assertEquals(FALSE, $this->redis->lRemove('list', 'x')); } @@ -1132,13 +1132,13 @@ public function testsAdd() { $this->redis->delete('set'); - $this->assertEquals(1, $this->redis->sAdd('set', 'val')); - $this->assertEquals(0, $this->redis->sAdd('set', 'val')); + $this->assertEquals(1, $this->redis->sAdd('set', 'val')); + $this->assertEquals(0, $this->redis->sAdd('set', 'val')); $this->assertTrue($this->redis->sContains('set', 'val')); $this->assertFalse($this->redis->sContains('set', 'val2')); - $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); + $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); $this->assertTrue($this->redis->sContains('set', 'val2')); } @@ -1146,11 +1146,11 @@ public function testsSize() { $this->redis->delete('set'); - $this->assertEquals(1, $this->redis->sAdd('set', 'val')); + $this->assertEquals(1, $this->redis->sAdd('set', 'val')); $this->assertEquals(1, $this->redis->sSize('set')); - $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); + $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); $this->assertEquals(2, $this->redis->sSize('set')); } @@ -1186,26 +1186,26 @@ public function testsMove() $this->assertEquals(1, $this->redis->sSize('set0')); $this->assertEquals(1, $this->redis->sSize('set1')); - $this->assertEquals(array('val2'), $this->redis->sGetMembers('set0')); - $this->assertEquals(array('val'), $this->redis->sGetMembers('set1')); + $this->assertEquals(array('val2'), $this->redis->sGetMembers('set0')); + $this->assertEquals(array('val'), $this->redis->sGetMembers('set1')); } public function testsPop() { $this->redis->delete('set0'); - $this->assertTrue($this->redis->sPop('set0') === FALSE); + $this->assertTrue($this->redis->sPop('set0') === FALSE); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); - $v0 = $this->redis->sPop('set0'); - $this->assertTrue(1 === $this->redis->sSize('set0')); - $this->assertTrue($v0 === 'val' || $v0 === 'val2'); - $v1 = $this->redis->sPop('set0'); - $this->assertTrue(0 === $this->redis->sSize('set0')); - $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); + $v0 = $this->redis->sPop('set0'); + $this->assertTrue(1 === $this->redis->sSize('set0')); + $this->assertTrue($v0 === 'val' || $v0 === 'val2'); + $v1 = $this->redis->sPop('set0'); + $this->assertTrue(0 === $this->redis->sSize('set0')); + $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); - $this->assertTrue($this->redis->sPop('set0') === FALSE); + $this->assertTrue($this->redis->sPop('set0') === FALSE); } public function testsRandMember() { @@ -1327,39 +1327,39 @@ public function testsGetMembers() $array = array('val', 'val2', 'val3'); - $sGetMembers = $this->redis->sGetMembers('set'); - sort($sGetMembers); - $this->assertEquals($array, $sGetMembers); + $sGetMembers = $this->redis->sGetMembers('set'); + sort($sGetMembers); + $this->assertEquals($array, $sGetMembers); - $sMembers = $this->redis->sMembers('set'); - sort($sMembers); - $this->assertEquals($array, $sMembers); // test alias + $sMembers = $this->redis->sMembers('set'); + sort($sMembers); + $this->assertEquals($array, $sMembers); // test alias } public function testlSet() { - $this->redis->delete('list'); + $this->redis->delete('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); - $this->redis->lPush('list', 'val3'); + $this->redis->lPush('list', 'val3'); - $this->assertEquals($this->redis->lGet('list', 0), 'val3'); - $this->assertEquals($this->redis->lGet('list', 1), 'val2'); - $this->assertEquals($this->redis->lGet('list', 2), 'val'); + $this->assertEquals($this->redis->lGet('list', 0), 'val3'); + $this->assertEquals($this->redis->lGet('list', 1), 'val2'); + $this->assertEquals($this->redis->lGet('list', 2), 'val'); - $this->assertEquals(TRUE, $this->redis->lSet('list', 1, 'valx')); + $this->assertEquals(TRUE, $this->redis->lSet('list', 1, 'valx')); - $this->assertEquals($this->redis->lGet('list', 0), 'val3'); - $this->assertEquals($this->redis->lGet('list', 1), 'valx'); - $this->assertEquals($this->redis->lGet('list', 2), 'val'); + $this->assertEquals($this->redis->lGet('list', 0), 'val3'); + $this->assertEquals($this->redis->lGet('list', 1), 'valx'); + $this->assertEquals($this->redis->lGet('list', 2), 'val'); } public function testsInter() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1381,48 +1381,48 @@ public function testsInter() { $this->redis->sAdd('t', $i); } - $xy = $this->redis->sInter('x', 'y'); // odd prime numbers - foreach($xy as $i) { - $i = (int)$i; + $xy = $this->redis->sInter('x', 'y'); // odd prime numbers + foreach($xy as $i) { + $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); - } - $xy = $this->redis->sInter(array('x', 'y')); // odd prime numbers, as array. - foreach($xy as $i) { - $i = (int)$i; + } + $xy = $this->redis->sInter(array('x', 'y')); // odd prime numbers, as array. + foreach($xy as $i) { + $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); - } + } - $yz = $this->redis->sInter('y', 'z'); // set of odd squares + $yz = $this->redis->sInter('y', 'z'); // set of odd squares foreach($yz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($y, $z))); } - $yz = $this->redis->sInter(array('y', 'z')); // set of odd squares, as array + $yz = $this->redis->sInter(array('y', 'z')); // set of odd squares, as array foreach($yz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($y, $z))); } - $zt = $this->redis->sInter('z', 't'); // prime squares + $zt = $this->redis->sInter('z', 't'); // prime squares $this->assertTrue($zt === array()); - $zt = $this->redis->sInter(array('z', 't')); // prime squares, as array + $zt = $this->redis->sInter(array('z', 't')); // prime squares, as array $this->assertTrue($zt === array()); $xyz = $this->redis->sInter('x', 'y', 'z');// odd prime squares $this->assertTrue($xyz === array('1')); - $xyz = $this->redis->sInter(array('x', 'y', 'z'));// odd prime squares, with an array as a parameter + $xyz = $this->redis->sInter(array('x', 'y', 'z'));// odd prime squares, with an array as a parameter $this->assertTrue($xyz === array('1')); - $nil = $this->redis->sInter(array()); + $nil = $this->redis->sInter(array()); $this->assertTrue($nil === FALSE); } public function testsInterStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1444,41 +1444,41 @@ public function testsInterStore() { $this->redis->sAdd('t', $i); } - $count = $this->redis->sInterStore('k', 'x', 'y'); // odd prime numbers - $this->assertEquals($count, $this->redis->sSize('k')); + $count = $this->redis->sInterStore('k', 'x', 'y'); // odd prime numbers + $this->assertEquals($count, $this->redis->sSize('k')); foreach(array_intersect($x, $y) as $i) { $this->assertTrue($this->redis->sContains('k', $i)); } - $count = $this->redis->sInterStore('k', 'y', 'z'); // set of odd squares - $this->assertEquals($count, $this->redis->sSize('k')); + $count = $this->redis->sInterStore('k', 'y', 'z'); // set of odd squares + $this->assertEquals($count, $this->redis->sSize('k')); foreach(array_intersect($y, $z) as $i) { $this->assertTrue($this->redis->sContains('k', $i)); } - $count = $this->redis->sInterStore('k', 'z', 't'); // squares of the form n^2 + 1 - $this->assertEquals($count, 0); - $this->assertEquals($count, $this->redis->sSize('k')); + $count = $this->redis->sInterStore('k', 'z', 't'); // squares of the form n^2 + 1 + $this->assertEquals($count, 0); + $this->assertEquals($count, $this->redis->sSize('k')); - $this->redis->delete('z'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // only z missing, expect 0. - $this->assertTrue($xyz === 0); + $this->redis->delete('z'); + $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // only z missing, expect 0. + $this->assertTrue($xyz === 0); - $this->redis->delete('y'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // y and z missing, expect 0. - $this->assertTrue($xyz === 0); + $this->redis->delete('y'); + $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // y and z missing, expect 0. + $this->assertTrue($xyz === 0); - $this->redis->delete('x'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // x y and z ALL missing, expect 0. - $this->assertTrue($xyz === 0); + $this->redis->delete('x'); + $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // x y and z ALL missing, expect 0. + $this->assertTrue($xyz === 0); } public function testsUnion() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1500,36 +1500,36 @@ public function testsUnion() { $this->redis->sAdd('t', $i); } - $xy = $this->redis->sUnion('x', 'y'); // x U y + $xy = $this->redis->sUnion('x', 'y'); // x U y foreach($xy as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_merge($x, $y))); } - $yz = $this->redis->sUnion('y', 'z'); // y U Z + $yz = $this->redis->sUnion('y', 'z'); // y U Z foreach($yz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_merge($y, $z))); } - $zt = $this->redis->sUnion('z', 't'); // z U t + $zt = $this->redis->sUnion('z', 't'); // z U t foreach($zt as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_merge($z, $t))); } $xyz = $this->redis->sUnion('x', 'y', 'z'); // x U y U z foreach($xyz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_merge($x, $y, $z))); } } public function testsUnionStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1551,56 +1551,56 @@ public function testsUnionStore() { $this->redis->sAdd('t', $i); } - $count = $this->redis->sUnionStore('k', 'x', 'y'); // x U y - $xy = array_unique(array_merge($x, $y)); - $this->assertEquals($count, count($xy)); + $count = $this->redis->sUnionStore('k', 'x', 'y'); // x U y + $xy = array_unique(array_merge($x, $y)); + $this->assertEquals($count, count($xy)); foreach($xy as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue($this->redis->sContains('k', $i)); } - $count = $this->redis->sUnionStore('k', 'y', 'z'); // y U z - $yz = array_unique(array_merge($y, $z)); - $this->assertEquals($count, count($yz)); + $count = $this->redis->sUnionStore('k', 'y', 'z'); // y U z + $yz = array_unique(array_merge($y, $z)); + $this->assertEquals($count, count($yz)); foreach($yz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue($this->redis->sContains('k', $i)); } - $count = $this->redis->sUnionStore('k', 'z', 't'); // z U t - $zt = array_unique(array_merge($z, $t)); - $this->assertEquals($count, count($zt)); + $count = $this->redis->sUnionStore('k', 'z', 't'); // z U t + $zt = array_unique(array_merge($z, $t)); + $this->assertEquals($count, count($zt)); foreach($zt as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue($this->redis->sContains('k', $i)); } - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $xyz = array_unique(array_merge($x, $y, $z)); - $this->assertEquals($count, count($xyz)); + $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z + $xyz = array_unique(array_merge($x, $y, $z)); + $this->assertEquals($count, count($xyz)); foreach($xyz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue($this->redis->sContains('k', $i)); } - $this->redis->delete('x'); // x missing now - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === count(array_unique(array_merge($y, $z)))); + $this->redis->delete('x'); // x missing now + $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z + $this->assertTrue($count === count(array_unique(array_merge($y, $z)))); - $this->redis->delete('y'); // x and y missing - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === count(array_unique($z))); + $this->redis->delete('y'); // x and y missing + $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z + $this->assertTrue($count === count(array_unique($z))); - $this->redis->delete('z'); // x, y, and z ALL missing - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === 0); + $this->redis->delete('z'); // x, y, and z ALL missing + $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z + $this->assertTrue($count === 0); } public function testsDiff() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1622,36 +1622,36 @@ public function testsDiff() { $this->redis->sAdd('t', $i); } - $xy = $this->redis->sDiff('x', 'y'); // x U y + $xy = $this->redis->sDiff('x', 'y'); // x U y foreach($xy as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_diff($x, $y))); } - $yz = $this->redis->sDiff('y', 'z'); // y U Z + $yz = $this->redis->sDiff('y', 'z'); // y U Z foreach($yz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_diff($y, $z))); } - $zt = $this->redis->sDiff('z', 't'); // z U t + $zt = $this->redis->sDiff('z', 't'); // z U t foreach($zt as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_diff($z, $t))); } $xyz = $this->redis->sDiff('x', 'y', 'z'); // x U y U z foreach($xyz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_diff($x, $y, $z))); } } public function testsDiffStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->delete('x'); // set of odd numbers + $this->redis->delete('y'); // set of prime numbers + $this->redis->delete('z'); // set of squares + $this->redis->delete('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1673,109 +1673,109 @@ public function testsDiffStore() { $this->redis->sAdd('t', $i); } - $count = $this->redis->sDiffStore('k', 'x', 'y'); // x - y - $xy = array_unique(array_diff($x, $y)); - $this->assertEquals($count, count($xy)); + $count = $this->redis->sDiffStore('k', 'x', 'y'); // x - y + $xy = array_unique(array_diff($x, $y)); + $this->assertEquals($count, count($xy)); foreach($xy as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue($this->redis->sContains('k', $i)); } - $count = $this->redis->sDiffStore('k', 'y', 'z'); // y - z - $yz = array_unique(array_diff($y, $z)); - $this->assertEquals($count, count($yz)); + $count = $this->redis->sDiffStore('k', 'y', 'z'); // y - z + $yz = array_unique(array_diff($y, $z)); + $this->assertEquals($count, count($yz)); foreach($yz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue($this->redis->sContains('k', $i)); } - $count = $this->redis->sDiffStore('k', 'z', 't'); // z - t - $zt = array_unique(array_diff($z, $t)); - $this->assertEquals($count, count($zt)); + $count = $this->redis->sDiffStore('k', 'z', 't'); // z - t + $zt = array_unique(array_diff($z, $t)); + $this->assertEquals($count, count($zt)); foreach($zt as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue($this->redis->sContains('k', $i)); } - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $xyz = array_unique(array_diff($x, $y, $z)); - $this->assertEquals($count, count($xyz)); + $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z + $xyz = array_unique(array_diff($x, $y, $z)); + $this->assertEquals($count, count($xyz)); foreach($xyz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue($this->redis->sContains('k', $i)); } - $this->redis->delete('x'); // x missing now - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); + $this->redis->delete('x'); // x missing now + $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z + $this->assertTrue($count === 0); - $this->redis->delete('y'); // x and y missing - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); + $this->redis->delete('y'); // x and y missing + $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z + $this->assertTrue($count === 0); - $this->redis->delete('z'); // x, y, and z ALL missing - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); + $this->redis->delete('z'); // x, y, and z ALL missing + $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z + $this->assertTrue($count === 0); } public function testlGetRange() { - $this->redis->delete('list'); + $this->redis->delete('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); - $this->redis->lPush('list', 'val3'); + $this->redis->lPush('list', 'val3'); - // pos : 0 1 2 - // pos : -3 -2 -1 - // list: [val3, val2, val] + // pos : 0 1 2 + // pos : -3 -2 -1 + // list: [val3, val2, val] - $this->assertEquals($this->redis->lGetRange('list', 0, 0), array('val3')); - $this->assertEquals($this->redis->lGetRange('list', 0, 1), array('val3', 'val2')); - $this->assertEquals($this->redis->lGetRange('list', 0, 2), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lGetRange('list', 0, 3), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lGetRange('list', 0, 0), array('val3')); + $this->assertEquals($this->redis->lGetRange('list', 0, 1), array('val3', 'val2')); + $this->assertEquals($this->redis->lGetRange('list', 0, 2), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lGetRange('list', 0, 3), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lGetRange('list', 0, -1), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lGetRange('list', 0, -2), array('val3', 'val2')); - $this->assertEquals($this->redis->lGetRange('list', -2, -1), array('val2', 'val')); + $this->assertEquals($this->redis->lGetRange('list', 0, -1), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lGetRange('list', 0, -2), array('val3', 'val2')); + $this->assertEquals($this->redis->lGetRange('list', -2, -1), array('val2', 'val')); - $this->redis->delete('list'); - $this->assertEquals($this->redis->lGetRange('list', 0, -1), array()); + $this->redis->delete('list'); + $this->assertEquals($this->redis->lGetRange('list', 0, -1), array()); } // public function testsave() { -// $this->assertTrue($this->redis->save() === TRUE); // don't really know how else to test this... +// $this->assertTrue($this->redis->save() === TRUE); // don't really know how else to test this... // } // public function testbgSave() { -// // let's try to fill the DB and then bgSave twice. We expect the second one to fail. -// for($i = 0; $i < 10e+4; $i++) { -// $s = md5($i); -// $this->redis->set($s, $s); -// } -// $this->assertTrue($this->redis->bgSave() === TRUE); // the first one should work. -// $this->assertTrue($this->redis->bgSave() === FALSE); // the second one should fail (still working on the first one) +// // let's try to fill the DB and then bgSave twice. We expect the second one to fail. +// for($i = 0; $i < 10e+4; $i++) { +// $s = md5($i); +// $this->redis->set($s, $s); +// } +// $this->assertTrue($this->redis->bgSave() === TRUE); // the first one should work. +// $this->assertTrue($this->redis->bgSave() === FALSE); // the second one should fail (still working on the first one) // } // // public function testlastSave() { -// while(!$this->redis->save()) { -// sleep(1); -// } -// $t_php = microtime(TRUE); -// $t_redis = $this->redis->lastSave(); +// while(!$this->redis->save()) { +// sleep(1); +// } +// $t_php = microtime(TRUE); +// $t_redis = $this->redis->lastSave(); // -// $this->assertTrue($t_php - $t_redis < 10000); // check that it's approximately what we've measured in PHP. +// $this->assertTrue($t_php - $t_redis < 10000); // check that it's approximately what we've measured in PHP. // } // // public function testflushDb() { -// $this->redis->set('x', 'y'); -// $this->assertTrue($this->redis->flushDb()); -// $this->assertTrue($this->redis->getKeys('*') === array()); +// $this->redis->set('x', 'y'); +// $this->assertTrue($this->redis->flushDb()); +// $this->assertTrue($this->redis->getKeys('*') === array()); // } // // public function testflushAll() { -// $this->redis->set('x', 'y'); -// $this->assertTrue($this->redis->flushAll()); -// $this->assertTrue($this->redis->getKeys('*') === array()); +// $this->redis->set('x', 'y'); +// $this->assertTrue($this->redis->flushAll()); +// $this->assertTrue($this->redis->getKeys('*') === array()); // } public function testdbSize() { @@ -1804,13 +1804,13 @@ public function testttl() { } public function testPersist() { - $this->redis->set('x', 'y'); - $this->redis->setTimeout('x', 100); - $this->assertTrue(TRUE === $this->redis->persist('x')); // true if there is a timeout - $this->assertTrue(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. - $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout - $this->redis->delete('x'); - $this->assertTrue(FALSE === $this->redis->persist('x')); // false if the key doesn’t exist. + $this->redis->set('x', 'y'); + $this->redis->setTimeout('x', 100); + $this->assertTrue(TRUE === $this->redis->persist('x')); // true if there is a timeout + $this->assertTrue(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. + $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout + $this->redis->delete('x'); + $this->assertTrue(FALSE === $this->redis->persist('x')); // false if the key doesn’t exist. } public function testClient() { @@ -1878,320 +1878,320 @@ public function testWait() { } public function testinfo() { - $info = $this->redis->info(); - - $keys = array( - "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", "lt")) { - 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($k, array_keys($info))); - } - } + $info = $this->redis->info(); + + $keys = array( + "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", "lt")) { + 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($k, array_keys($info))); + } + } public function testInfoCommandStats() { - // INFO COMMANDSTATS is new in 2.6.0 - if (version_compare($this->version, "2.5.0", "lt")) { - $this->markTestSkipped(); - } + // INFO COMMANDSTATS is new in 2.6.0 + if (version_compare($this->version, "2.5.0", "lt")) { + $this->markTestSkipped(); + } - $info = $this->redis->info("COMMANDSTATS"); + $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); - } - } + $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)); + $this->assertFalse($this->redis->select(-1)); + $this->assertTrue($this->redis->select(0)); } public function testMset() { - $this->redis->delete('x', 'y', 'z'); // remove x y z - $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z + $this->redis->delete('x', 'y', 'z'); // remove x y z + $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - $this->redis->delete('x'); // delete just x - $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z - $this->assertFalse($this->redis->mset(array())); // set ø → FALSE + $this->redis->delete('x'); // delete just x + $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z + $this->assertFalse($this->redis->mset(array())); // set ø → FALSE - /* - * Integer keys - */ - // No prefix - $set_array = Array(-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three'); - $this->redis->delete(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->delete(array_keys($set_array)); + /* + * Integer keys + */ - // With a prefix - $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:'); - $this->redis->delete(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->delete(array_keys($set_array)); - $this->redis->setOption(Redis::OPT_PREFIX, ''); + // No prefix + $set_array = Array(-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three'); + $this->redis->delete(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->delete(array_keys($set_array)); + + // With a prefix + $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:'); + $this->redis->delete(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->delete(array_keys($set_array)); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testMsetNX() { - $this->redis->delete('x', 'y', 'z'); // remove x y z - $this->assertTrue(TRUE === $this->redis->msetnx(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z + $this->redis->delete('x', 'y', 'z'); // remove x y z + $this->assertTrue(TRUE === $this->redis->msetnx(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z - $this->redis->delete('x'); // delete just x - $this->assertTrue(FALSE === $this->redis->msetnx(array('x' => 'A', 'y' => 'B', 'z' => 'C'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array(FALSE, 'b', 'c')); // check x y z + $this->redis->delete('x'); // delete just x + $this->assertTrue(FALSE === $this->redis->msetnx(array('x' => 'A', 'y' => 'B', 'z' => 'C'))); // set x y z + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array(FALSE, 'b', 'c')); // check x y z - $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE + $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE } public function testRpopLpush() { - // standard case. - $this->redis->delete('x', 'y'); - $this->redis->lpush('x', 'abc'); - $this->redis->lpush('x', 'def'); // x = [def, abc] + // standard case. + $this->redis->delete('x', 'y'); + $this->redis->lpush('x', 'abc'); + $this->redis->lpush('x', 'def'); // x = [def, abc] - $this->redis->lpush('y', '123'); - $this->redis->lpush('y', '456'); // y = [456, 123] + $this->redis->lpush('y', '123'); + $this->redis->lpush('y', '456'); // y = [456, 123] - $this->assertEquals($this->redis->rpoplpush('x', 'y'), 'abc'); // we RPOP x, yielding abc. - $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. - $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. + $this->assertEquals($this->redis->rpoplpush('x', 'y'), 'abc'); // we RPOP x, yielding abc. + $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. + $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. - // with an empty source, expecting no change. - $this->redis->delete('x', 'y'); - $this->assertTrue(FALSE === $this->redis->rpoplpush('x', 'y')); - $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); - $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); + // with an empty source, expecting no change. + $this->redis->delete('x', 'y'); + $this->assertTrue(FALSE === $this->redis->rpoplpush('x', 'y')); + $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); + $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); } public function testBRpopLpush() { - // standard case. - $this->redis->delete('x', 'y'); - $this->redis->lpush('x', 'abc'); - $this->redis->lpush('x', 'def'); // x = [def, abc] + // standard case. + $this->redis->delete('x', 'y'); + $this->redis->lpush('x', 'abc'); + $this->redis->lpush('x', 'def'); // x = [def, abc] - $this->redis->lpush('y', '123'); - $this->redis->lpush('y', '456'); // y = [456, 123] + $this->redis->lpush('y', '123'); + $this->redis->lpush('y', '456'); // y = [456, 123] - $this->assertEquals($this->redis->brpoplpush('x', 'y', 1), 'abc'); // we RPOP x, yielding abc. - $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. - $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. + $this->assertEquals($this->redis->brpoplpush('x', 'y', 1), 'abc'); // we RPOP x, yielding abc. + $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. + $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. - // with an empty source, expecting no change. - $this->redis->delete('x', 'y'); - $this->assertTrue(FALSE === $this->redis->brpoplpush('x', 'y', 1)); - $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); - $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); + // with an empty source, expecting no change. + $this->redis->delete('x', 'y'); + $this->assertTrue(FALSE === $this->redis->brpoplpush('x', 'y', 1)); + $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); + $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); } - public function testZAddFirstArg() { + public function testZAddFirstArg() { - $this->redis->delete('key'); + $this->redis->delete('key'); - $zsetName = 100; // not a string! - $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1')); + $zsetName = 100; // not a string! + $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); + $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1')); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1)); - } + $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1)); + } public function testZX() { - $this->redis->delete('key'); - - $this->assertTrue(array() === $this->redis->zRange('key', 0, -1)); - $this->assertTrue(array() === $this->redis->zRange('key', 0, -1, true)); - - $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); - $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); - $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters - - $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); - - // withscores - $ret = $this->redis->zRange('key', 0, -1, true); - $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(0 === $this->redis->zDelete('key', 'valX')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val3')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val4')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val5')); - - $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->zRange('key', 0, -1)); - - // zGetReverseRange - - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); - - $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); - $this->assertTrue(array('val0', 'val1', 'val2', 'aal3', 'val3') === $zero_to_three || array('val0', 'val1', 'val2', 'val3', 'aal3') === $zero_to_three); - - $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); - $this->assertTrue(array_reverse(array('val0', 'val1', 'val2', 'aal3', 'val3')) === $three_to_zero || array_reverse(array('val0', 'val1', 'val2', 'val3', 'aal3')) === $three_to_zero); - - $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); - - // withscores - $this->redis->zRemove('key', 'aal3'); - $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); - $this->assertTrue(array('val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3) == $zero_to_three); - $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); - - // limit - $this->assertTrue(array('val0') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 1)))); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 2)))); - $this->assertTrue(array('val1', 'val2') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 2)))); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => array(0, 100)))); - - $this->assertTrue(array('val3') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 1)))); - $this->assertTrue(array('val3', 'val2') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 2)))); - $this->assertTrue(array('val2', 'val1') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(1, 2)))); - $this->assertTrue(array('val1', 'val0') === $this->redis->zRevRangeByScore('key', 1, 0, array('limit' => array(0, 100)))); + $this->redis->delete('key'); + + $this->assertTrue(array() === $this->redis->zRange('key', 0, -1)); + $this->assertTrue(array() === $this->redis->zRange('key', 0, -1, true)); + + $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); + $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); + $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters + + $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); + + // withscores + $ret = $this->redis->zRange('key', 0, -1, true); + $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(0 === $this->redis->zDelete('key', 'valX')); + $this->assertTrue(1 === $this->redis->zDelete('key', 'val3')); + $this->assertTrue(1 === $this->redis->zDelete('key', 'val4')); + $this->assertTrue(1 === $this->redis->zDelete('key', 'val5')); + + $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->zRange('key', 0, -1)); + + // zGetReverseRange + + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); + + $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); + $this->assertTrue(array('val0', 'val1', 'val2', 'aal3', 'val3') === $zero_to_three || array('val0', 'val1', 'val2', 'val3', 'aal3') === $zero_to_three); + + $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); + $this->assertTrue(array_reverse(array('val0', 'val1', 'val2', 'aal3', 'val3')) === $three_to_zero || array_reverse(array('val0', 'val1', 'val2', 'val3', 'aal3')) === $three_to_zero); + + $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); + + // withscores + $this->redis->zRemove('key', 'aal3'); + $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); + $this->assertTrue(array('val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3) == $zero_to_three); + $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); + + // limit + $this->assertTrue(array('val0') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 1)))); + $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 2)))); + $this->assertTrue(array('val1', 'val2') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 2)))); + $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => array(0, 100)))); + + $this->assertTrue(array('val3') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 1)))); + $this->assertTrue(array('val3', 'val2') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 2)))); + $this->assertTrue(array('val2', 'val1') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(1, 2)))); + $this->assertTrue(array('val1', 'val0') === $this->redis->zRevRangeByScore('key', 1, 0, array('limit' => array(0, 100)))); - $this->assertTrue(4 === $this->redis->zSize('key')); - $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); - $this->assertFalse($this->redis->zScore('key', 'val')); - $this->assertFalse($this->redis->zScore(3, 2)); + $this->assertTrue(4 === $this->redis->zSize('key')); + $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); + $this->assertFalse($this->redis->zScore('key', 'val')); + $this->assertFalse($this->redis->zScore(3, 2)); - // with () and +inf, -inf - $this->redis->delete('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(array('foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4) == $this->redis->zRangeByScore('zset', '-inf', '+inf', array('withscores' => TRUE))); - $this->assertTrue(array('foo' => 1, 'bar' => 2) == $this->redis->zRangeByScore('zset', 1, 2, array('withscores' => TRUE))); - $this->assertTrue(array('bar' => 2) == $this->redis->zRangeByScore('zset', '(1', 2, array('withscores' => TRUE))); - $this->assertTrue(array() == $this->redis->zRangeByScore('zset', '(1', '(2', array('withscores' => TRUE))); + // with () and +inf, -inf + $this->redis->delete('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(array('foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4) == $this->redis->zRangeByScore('zset', '-inf', '+inf', array('withscores' => TRUE))); + $this->assertTrue(array('foo' => 1, 'bar' => 2) == $this->redis->zRangeByScore('zset', 1, 2, array('withscores' => TRUE))); + $this->assertTrue(array('bar' => 2) == $this->redis->zRangeByScore('zset', '(1', 2, array('withscores' => TRUE))); + $this->assertTrue(array() == $this->redis->zRangeByScore('zset', '(1', '(2', array('withscores' => TRUE))); - $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); - $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); - $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); - $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2')); + $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); + $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); + $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); + $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2')); - // zincrby - $this->redis->delete('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')); + // zincrby + $this->redis->delete('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')); - //zUnion - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); - $this->redis->delete('keyU'); + //zUnion + $this->redis->delete('key1'); + $this->redis->delete('key2'); + $this->redis->delete('key3'); + $this->redis->delete('keyU'); - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); + $this->redis->zAdd('key1', 0, 'val0'); + $this->redis->zAdd('key1', 1, 'val1'); - $this->redis->zAdd('key2', 2, 'val2'); - $this->redis->zAdd('key2', 3, 'val3'); - - $this->redis->zAdd('key3', 4, 'val4'); - $this->redis->zAdd('key3', 5, 'val5'); - - $this->assertTrue(4 === $this->redis->zUnion('keyU', array('key1', 'key3'))); - $this->assertTrue(array('val0', 'val1', 'val4', 'val5') === $this->redis->zRange('keyU', 0, -1)); - - // Union on non existing keys - $this->redis->delete('keyU'); - $this->assertTrue(0 === $this->redis->zUnion('keyU', array('X', 'Y'))); - $this->assertTrue(array() === $this->redis->zRange('keyU', 0, -1)); - - // !Exist U Exist → copy of existing zset. - $this->redis->delete('keyU', 'X'); - $this->assertTrue(2 === $this->redis->zUnion('keyU', array('key1', 'X'))); - - // test weighted zUnion - $this->redis->delete('keyZ'); - $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(1, 1))); - $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('keyZ', 0, -1)); + $this->redis->zAdd('key2', 2, 'val2'); + $this->redis->zAdd('key2', 3, 'val3'); + + $this->redis->zAdd('key3', 4, 'val4'); + $this->redis->zAdd('key3', 5, 'val5'); + + $this->assertTrue(4 === $this->redis->zUnion('keyU', array('key1', 'key3'))); + $this->assertTrue(array('val0', 'val1', 'val4', 'val5') === $this->redis->zRange('keyU', 0, -1)); + + // Union on non existing keys + $this->redis->delete('keyU'); + $this->assertTrue(0 === $this->redis->zUnion('keyU', array('X', 'Y'))); + $this->assertTrue(array() === $this->redis->zRange('keyU', 0, -1)); + + // !Exist U Exist → copy of existing zset. + $this->redis->delete('keyU', 'X'); + $this->assertTrue(2 === $this->redis->zUnion('keyU', array('key1', 'X'))); + + // test weighted zUnion + $this->redis->delete('keyZ'); + $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(1, 1))); + $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('keyZ', 0, -1)); - $this->redis->zDeleteRangeByScore('keyZ', 0, 10); - $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(5, 1))); - $this->assertTrue(array('val0', 'val2', 'val3', 'val1') === $this->redis->zRange('keyZ', 0, -1)); + $this->redis->zDeleteRangeByScore('keyZ', 0, 10); + $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(5, 1))); + $this->assertTrue(array('val0', 'val2', 'val3', 'val1') === $this->redis->zRange('keyZ', 0, -1)); - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); + $this->redis->delete('key1'); + $this->redis->delete('key2'); + $this->redis->delete('key3'); - //test zUnion with weights and aggegration function - $this->redis->zadd('key1', 1, 'duplicate'); - $this->redis->zadd('key2', 2, 'duplicate'); - $this->redis->zUnion('keyU', array('key1','key2'), array(1,1), 'MIN'); - $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); - $this->redis->delete('keyU'); - - //now test zUnion *without* weights but with aggregrate function - $this->redis->zUnion('keyU', array('key1','key2'), null, 'MIN'); - $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); - $this->redis->delete('keyU', 'key1', 'key2'); - - - - // test integer and float weights (GitHub issue #109). - $this->redis->del('key1', 'key2', 'key3'); - - $this->redis->zadd('key1', 1, 'one'); - $this->redis->zadd('key1', 2, 'two'); - $this->redis->zadd('key2', 1, 'one'); - $this->redis->zadd('key2', 2, 'two'); - $this->redis->zadd('key2', 3, 'three'); - - $this->assertTrue($this->redis->zunion('key3', array('key1', 'key2'), array(2, 3.0)) === 3); - - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); + //test zUnion with weights and aggegration function + $this->redis->zadd('key1', 1, 'duplicate'); + $this->redis->zadd('key2', 2, 'duplicate'); + $this->redis->zUnion('keyU', array('key1','key2'), array(1,1), 'MIN'); + $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); + $this->redis->delete('keyU'); + + //now test zUnion *without* weights but with aggregrate function + $this->redis->zUnion('keyU', array('key1','key2'), null, 'MIN'); + $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); + $this->redis->delete('keyU', 'key1', 'key2'); + + + + // test integer and float weights (GitHub issue #109). + $this->redis->del('key1', 'key2', 'key3'); + + $this->redis->zadd('key1', 1, 'one'); + $this->redis->zadd('key1', 2, 'two'); + $this->redis->zadd('key2', 1, 'one'); + $this->redis->zadd('key2', 2, 'two'); + $this->redis->zadd('key2', 3, 'three'); + + $this->assertTrue($this->redis->zunion('key3', array('key1', 'key2'), array(2, 3.0)) === 3); + + $this->redis->delete('key1'); + $this->redis->delete('key2'); + $this->redis->delete('key3'); // Test 'inf', '-inf', and '+inf' weights (GitHub issue #336) $this->redis->zadd('key1', 1, 'one', 2, 'two', 3, 'three'); @@ -2216,195 +2216,195 @@ public function testZX() { $this->redis->del('key1','key2','key3'); - $this->redis->zadd('key1', 2000.1, 'one'); - $this->redis->zadd('key1', 3000.1, 'two'); - $this->redis->zadd('key1', 4000.1, 'three'); + $this->redis->zadd('key1', 2000.1, 'one'); + $this->redis->zadd('key1', 3000.1, 'two'); + $this->redis->zadd('key1', 4000.1, 'three'); - $ret = $this->redis->zRange('key1', 0, -1, TRUE); - $this->assertTrue(count($ret) === 3); - $retValues = array_keys($ret); + $ret = $this->redis->zRange('key1', 0, -1, TRUE); + $this->assertTrue(count($ret) === 3); + $retValues = array_keys($ret); - $this->assertTrue(array('one', 'two', 'three') === $retValues); + $this->assertTrue(array('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)); + // + 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->delete('key1'); + $this->redis->delete('key1'); - // ZREMRANGEBYRANK - $this->redis->zAdd('key1', 1, 'one'); - $this->redis->zAdd('key1', 2, 'two'); - $this->redis->zAdd('key1', 3, 'three'); - $this->assertTrue(2 === $this->redis->zremrangebyrank('key1', 0, 1)); - $this->assertTrue(array('three' => 3) == $this->redis->zRange('key1', 0, -1, TRUE)); + // ZREMRANGEBYRANK + $this->redis->zAdd('key1', 1, 'one'); + $this->redis->zAdd('key1', 2, 'two'); + $this->redis->zAdd('key1', 3, 'three'); + $this->assertTrue(2 === $this->redis->zremrangebyrank('key1', 0, 1)); + $this->assertTrue(array('three' => 3) == $this->redis->zRange('key1', 0, -1, TRUE)); - $this->redis->delete('key1'); + $this->redis->delete('key1'); - // zInter + // zInter - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); - $this->redis->zAdd('key1', 3, 'val3'); + $this->redis->zAdd('key1', 0, 'val0'); + $this->redis->zAdd('key1', 1, 'val1'); + $this->redis->zAdd('key1', 3, 'val3'); - $this->redis->zAdd('key2', 2, 'val1'); - $this->redis->zAdd('key2', 3, 'val3'); + $this->redis->zAdd('key2', 2, 'val1'); + $this->redis->zAdd('key2', 3, 'val3'); - $this->redis->zAdd('key3', 4, 'val3'); - $this->redis->zAdd('key3', 5, 'val5'); + $this->redis->zAdd('key3', 4, 'val3'); + $this->redis->zAdd('key3', 5, 'val5'); - $this->redis->delete('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + $this->redis->delete('keyI'); + $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'))); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - // Union on non existing keys - $this->assertTrue(0 === $this->redis->zInter('keyX', array('X', 'Y'))); - $this->assertTrue(array() === $this->redis->zRange('keyX', 0, -1)); + // Union on non existing keys + $this->assertTrue(0 === $this->redis->zInter('keyX', array('X', 'Y'))); + $this->assertTrue(array() === $this->redis->zRange('keyX', 0, -1)); - // !Exist U Exist - $this->assertTrue(0 === $this->redis->zInter('keyY', array('key1', 'X'))); - $this->assertTrue(array() === $this->redis->zRange('keyY', 0, -1)); + // !Exist U Exist + $this->assertTrue(0 === $this->redis->zInter('keyY', array('key1', 'X'))); + $this->assertTrue(array() === $this->redis->zRange('keyY', 0, -1)); - // test weighted zInter - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); + // test weighted zInter + $this->redis->delete('key1'); + $this->redis->delete('key2'); + $this->redis->delete('key3'); - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); - $this->redis->zAdd('key1', 3, 'val3'); + $this->redis->zAdd('key1', 0, 'val0'); + $this->redis->zAdd('key1', 1, 'val1'); + $this->redis->zAdd('key1', 3, 'val3'); - $this->redis->zAdd('key2', 2, 'val1'); - $this->redis->zAdd('key2', 1, 'val3'); + $this->redis->zAdd('key2', 2, 'val1'); + $this->redis->zAdd('key2', 1, 'val3'); - $this->redis->zAdd('key3', 7, 'val1'); - $this->redis->zAdd('key3', 3, 'val3'); + $this->redis->zAdd('key3', 7, 'val1'); + $this->redis->zAdd('key3', 3, 'val3'); - $this->redis->delete('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'), array(1, 1))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + $this->redis->delete('keyI'); + $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'), array(1, 1))); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - $this->redis->delete('keyI'); - $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'min')); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - $this->redis->delete('keyI'); - $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'max')); - $this->assertTrue(array('val3', 'val1') === $this->redis->zRange('keyI', 0, -1)); + $this->redis->delete('keyI'); + $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'min')); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + $this->redis->delete('keyI'); + $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'max')); + $this->assertTrue(array('val3', 'val1') === $this->redis->zRange('keyI', 0, -1)); - $this->redis->delete('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), null, 'max')); - $this->assertTrue($this->redis->zScore('keyI', 'val1') === floatval(7)); + $this->redis->delete('keyI'); + $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), null, 'max')); + $this->assertTrue($this->redis->zScore('keyI', 'val1') === floatval(7)); - // zrank, zrevrank - $this->redis->delete('z'); - $this->redis->zadd('z', 1, 'one'); - $this->redis->zadd('z', 2, 'two'); - $this->redis->zadd('z', 5, 'five'); + // zrank, zrevrank + $this->redis->delete('z'); + $this->redis->zadd('z', 1, 'one'); + $this->redis->zadd('z', 2, 'two'); + $this->redis->zadd('z', 5, 'five'); - $this->assertTrue(0 === $this->redis->zRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRank('z', 'two')); - $this->assertTrue(2 === $this->redis->zRank('z', 'five')); + $this->assertTrue(0 === $this->redis->zRank('z', 'one')); + $this->assertTrue(1 === $this->redis->zRank('z', 'two')); + $this->assertTrue(2 === $this->redis->zRank('z', 'five')); - $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); - $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); + $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); + $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); + $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); } public function testHashes() { - $this->redis->delete('h', 'key'); - - $this->assertTrue(0 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); - $this->assertTrue(1 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); - $this->assertTrue(2 === $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(0 === $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(1 === $this->redis->hDel('h', 'a')); // 1 on success - $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure - - $this->redis->delete('h'); - $this->redis->hSet('h', 'x', 'a'); - $this->redis->hSet('h', 'y', 'b'); - $this->assertTrue(2 === $this->redis->hDel('h', 'x', 'y')); // variadic - - // hsetnx - $this->redis->delete('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 === array('x', 'y') || $keys === array('y', 'x')); - - // values - $values = $this->redis->hVals('h'); - $this->assertTrue($values === array('a', 'b') || $values === array('b', 'a')); - - // keys + values - $all = $this->redis->hGetAll('h'); - $this->assertTrue($all === array('x' => 'a', 'y' => 'b') || $all === array('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->delete('h'); - $this->assertTrue(FALSE === $this->redis->hExists('h', 'x')); - - // hIncrBy - $this->redis->delete('h'); - $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); - $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); - $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); - $this->assertTrue("2" === $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", "ge")) { - // hIncrByFloat - $this->redis->delete('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->redis->hset('h','y','not-a-number'); - $this->assertTrue(FALSE === $this->redis->hIncrByFloat('h', 'y', 1.5)); - } - - // hmset - $this->redis->delete('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', array('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(array('x' => '123', 'y' => '456') === $this->redis->hMget('h', array('x', 'y'))); - $this->assertTrue(array('z' => 'abc') === $this->redis->hMget('h', array('z'))); - $this->assertTrue(array('x' => '123', 't' => FALSE, 'y' => '456') === $this->redis->hMget('h', array('x', 't', 'y'))); - $this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123))); - $this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123))); + $this->redis->delete('h', 'key'); + + $this->assertTrue(0 === $this->redis->hLen('h')); + $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); + $this->assertTrue(1 === $this->redis->hLen('h')); + $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); + $this->assertTrue(2 === $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(0 === $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(1 === $this->redis->hDel('h', 'a')); // 1 on success + $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure + + $this->redis->delete('h'); + $this->redis->hSet('h', 'x', 'a'); + $this->redis->hSet('h', 'y', 'b'); + $this->assertTrue(2 === $this->redis->hDel('h', 'x', 'y')); // variadic + + // hsetnx + $this->redis->delete('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 === array('x', 'y') || $keys === array('y', 'x')); + + // values + $values = $this->redis->hVals('h'); + $this->assertTrue($values === array('a', 'b') || $values === array('b', 'a')); + + // keys + values + $all = $this->redis->hGetAll('h'); + $this->assertTrue($all === array('x' => 'a', 'y' => 'b') || $all === array('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->delete('h'); + $this->assertTrue(FALSE === $this->redis->hExists('h', 'x')); + + // hIncrBy + $this->redis->delete('h'); + $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); + $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); + $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); + $this->assertTrue("2" === $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", "ge")) { + // hIncrByFloat + $this->redis->delete('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->redis->hset('h','y','not-a-number'); + $this->assertTrue(FALSE === $this->redis->hIncrByFloat('h', 'y', 1.5)); + } + + // hmset + $this->redis->delete('h'); + $this->assertTrue(TRUE === $this->redis->hMset('h', array('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(array('x' => '123', 'y' => '456') === $this->redis->hMget('h', array('x', 'y'))); + $this->assertTrue(array('z' => 'abc') === $this->redis->hMget('h', array('z'))); + $this->assertTrue(array('x' => '123', 't' => FALSE, 'y' => '456') === $this->redis->hMget('h', array('x', 't', 'y'))); + $this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123))); + $this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123))); // Test with an array populated with things we can't use as keys $this->assertTrue($this->redis->hmget('h', Array(false,NULL,false)) === FALSE); @@ -2412,42 +2412,42 @@ public function testHashes() { // Test with some invalid keys mixed in (which should just be ignored) $this->assertTrue(array('x'=>'123','y'=>'456','z'=>'abc') === $this->redis->hMget('h',Array('x',null,'y','','z',false))); - // hmget/hmset with numeric fields - $this->redis->del('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', array(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(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); - - // check non-string types. - $this->redis->delete('h1'); - $this->assertTrue(TRUE === $this->redis->hMSet('h1', array('x' => 0, 'y' => array(), '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']); + // hmget/hmset with numeric fields + $this->redis->del('h'); + $this->assertTrue(TRUE === $this->redis->hMset('h', array(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(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); + + // check non-string types. + $this->redis->delete('h1'); + $this->assertTrue(TRUE === $this->redis->hMSet('h1', array('x' => 0, 'y' => array(), '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']); } public function testSetRange() { - $this->redis->delete('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->delete('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')); + $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->delete('key'); - $this->redis->setRange('key', 6, 'foo'); - $this->assertTrue("\x00\x00\x00\x00\x00\x00foo" === $this->redis->get('key')); + // fill with zeros if needed + $this->redis->delete('key'); + $this->redis->setRange('key', 6, 'foo'); + $this->assertTrue("\x00\x00\x00\x00\x00\x00foo" === $this->redis->get('key')); } public function testObject() { @@ -2460,17 +2460,17 @@ public function testObject() { } $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->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->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'); + $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 */ @@ -2478,71 +2478,71 @@ public function testObject() { $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->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', '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->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. + $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); + $this->assertTrue($this->redis->object('idletime', 'key') === 0); } public function testMultiExec() { - $this->sequence(Redis::MULTI); - $this->differentType(Redis::MULTI); + $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, ""); + // 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->redis->set('x', '42'); - $this->assertTrue(TRUE === $this->redis->watch('x')); - $ret = $this->redis->multi() - ->get('x') - ->exec(); + $this->assertTrue(TRUE === $this->redis->watch('x')); + $ret = $this->redis->multi() + ->get('x') + ->exec(); - // successful transaction - $this->assertTrue($ret === array('42')); + // successful transaction + $this->assertTrue($ret === array('42')); - // failed transaction - $this->redis->watch('x'); + // failed transaction + $this->redis->watch('x'); - $r = $this->newInstance(); // new instance, modifying `x'. - $r->incr('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. + $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 + // 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 === array('44')); // succeeded since we've cancel the WATCH command. + $ret = $this->redis->multi() + ->get('x') + ->exec(); + $this->assertTrue($ret === array('44')); // succeeded since we've cancel the WATCH command. } public function testPipeline() { @@ -2551,561 +2551,561 @@ public function testPipeline() { } $this->sequence(Redis::PIPELINE); - $this->differentType(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, ""); + // 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, ""); } protected function sequence($mode) { - $ret = $this->redis->multi($mode) - ->set('x', 42) - ->info() - ->type('x') - ->get('x') - ->exec(); - - $this->assertTrue(is_array($ret)); - $i = 0; - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue(is_array($ret[$i++])); - $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_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer - $ret = $this->redis->multi($mode) - ->delete('key1') - ->set('key1', 'value1') - ->get('key1') - ->getSet('key1', 'value2') - ->get('key1') - ->set('key2', 4) - ->incr('key2') - ->get('key2') - ->decr('key2') - ->get('key2') - ->renameKey('key2', 'key3') - ->get('key3') - ->renameNx('key3', 'key1') - ->renameKey('key3', 'key2') - ->incrby('key2', 5) - ->get('key2') - ->decrby('key2', 5) - ->get('key2') - ->exec(); - - $this->assertTrue(is_array($ret)); - $i = 0; - $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) - ->delete('key1') - ->delete('key2') - ->set('key1', 'val1') - ->setnx('key1', 'valX') - ->setnx('key2', 'valX') - ->exists('key1') - ->exists('key3') - ->ping() - ->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); - $this->assertTrue($ret[7] == '+PONG'); - - $ret = $this->redis->multi($mode) - ->randomKey() - ->exec(); - $ret = $this->redis->multi($mode) - ->exec(); - $this->assertTrue($ret == array()); - - // ttl, mget, mset, msetnx, expire, expireAt - $this->redis->delete('key'); - $ret = $this->redis->multi($mode) - ->ttl('key') - ->mget(array('key1', 'key2', 'key3')) - ->mset(array('key3' => 'value3', 'key4' => '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 === -1 || $ttl === -2); - $this->assertTrue($ret[$i++] === array('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('lkey', 'x') - ->set('lDest', 'y') - ->delete('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) - ->lpop('lkey') - ->llen('lkey') - ->lRemove('lkey', 'lvalue', 3) - ->llen('lkey') - ->lget('lkey', 0) - ->lGetRange('lkey', 0, -1) - ->lSet('lkey', 1, "newValue") // check errors on key not exists - ->lGetRange('lkey', 0, -1) - ->llen('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++] === array('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++] === array("lvalue")); // this is the current list. - $this->assertTrue($ret[$i++] === FALSE); // updating a non-existent element fails. - $this->assertTrue($ret[$i++] === array("lvalue")); // this is the current list. - $this->assertTrue($ret[$i++] === 1); // 1 element left - $this->assertTrue(count($ret) == $i); - - - $ret = $this->redis->multi($mode) - ->delete('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) - ->lpop('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++] === array('lvalue')); // rpoplpush returns the element: "lvalue" - $this->assertTrue($ret[$i++] === 'lvalue'); // pop returns the front element: "lvalue" - $this->assertTrue(count($ret) == $i); - - - // general command - $ret = $this->redis->multi($mode) - ->select(3) - ->set('keyAAA', 'value') - ->set('keyAAB', 'value') - ->dbSize() - ->lastsave() - ->exec(); - - $this->redis->select(0); // back to normal - - $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue($ret[$i++] === TRUE); // select - $this->assertTrue($ret[$i++] === TRUE); // set - $this->assertTrue($ret[$i++] === TRUE); // set - $this->assertTrue(is_long($ret[$i++])); // dbsize - $this->assertTrue(is_long($ret[$i++])); // lastsave - - $this->assertTrue(count($ret) === $i); - - $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer - $ret = $this->redis->multi($mode) - ->delete('key1') - ->set('key1', 'value1') - ->get('key1') - ->getSet('key1', 'value2') - ->get('key1') - ->set('key2', 4) - ->incr('key2') - ->get('key2') - ->decr('key2') - ->get('key2') - ->renameKey('key2', 'key3') - ->get('key3') - ->renameNx('key3', 'key1') - ->renameKey('key3', 'key2') - ->incrby('key2', 5) - ->get('key2') - ->decrby('key2', 5) - ->get('key2') - ->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->redis->setOption(Redis::OPT_SERIALIZER, $serializer); - - $ret = $this->redis->multi($mode) - ->delete('key1') - ->delete('key2') - ->set('key1', 'val1') - ->setnx('key1', 'valX') - ->setnx('key2', 'valX') - ->exists('key1') - ->exists('key3') - ->ping() - ->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); - $this->assertTrue($ret[7] == '+PONG'); - - $ret = $this->redis->multi($mode) - ->randomKey() - ->exec(); - - $this->assertTrue(is_array($ret) && count($ret) === 1); - $this->assertTrue(is_string($ret[0])); - - // ttl, mget, mset, msetnx, expire, expireAt - $ret = $this->redis->multi($mode) - ->ttl('key') - ->mget(array('key1', 'key2', 'key3')) - ->mset(array('key3' => 'value3', 'key4' => '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) - ->delete('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) - ->lpop('lkey') - ->llen('lkey') - ->lRemove('lkey', 'lvalue', 3) - ->llen('lkey') - ->lget('lkey', 0) - ->lGetRange('lkey', 0, -1) - ->lSet('lkey', 1, "newValue") // check errors on missing key - ->lGetRange('lkey', 0, -1) - ->llen('lkey') - ->exec(); - - $this->assertTrue(is_array($ret)); - $i = 0; - $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // delete - $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++] === array('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++] === array('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++] === array('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) - ->delete('skey1', 'skey2', 'skeydest', 'skeyUnion', 'sDiffDest') - ->sadd('skey1', 'sValue1') - ->sadd('skey1', 'sValue2') - ->sadd('skey1', 'sValue3') - ->sadd('skey1', 'sValue4') - - ->sadd('skey2', 'sValue1') - ->sadd('skey2', 'sValue2') - - ->sSize('skey1') - ->sRemove('skey1', 'sValue2') - ->sSize('skey1') - ->sMove('skey1', 'skey2', 'sValue4') - ->sSize('skey2') - ->sContains('skey2', 'sValue4') - ->sMembers('skey1') - ->sMembers('skey2') - ->sInter('skey1', 'skey2') - ->sInterStore('skeydest', 'skey1', 'skey2') - ->sMembers('skeydest') - ->sUnion('skey2', 'skeydest') - ->sUnionStore('skeyUnion', 'skey2', 'skeydest') - ->sMembers('skeyUnion') - ->sDiff('skey1', 'skey2') - ->sDiffStore('sDiffDest', 'skey1', 'skey2') - ->sMembers('sDiffDest') - ->sPop('skey2') - ->sSize('skey2') - ->exec(); - - $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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(array('sValue1', 'sValue3') as $k) { // sKey1 contains sValue1 and sValue3. - $this->assertTrue(in_array($k, $ret[$i])); - } - $this->assertTrue(count($ret[$i++]) === 2); - foreach(array('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++] === array('sValue1')); // intersection - $this->assertTrue($ret[$i++] === 1); // intersection + store → 1 value in the destination set. - $this->assertTrue($ret[$i++] === array('sValue1')); // sinterstore destination contents - - foreach(array('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(array('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++] === array('sValue3')); // diff skey1, skey2 : only sValue3 is not shared. - $this->assertTrue($ret[$i++] === 1); // sdiffstore size == 1 - $this->assertTrue($ret[$i++] === array('sValue3')); // contents of sDiffDest - - $this->assertTrue(in_array($ret[$i++], array('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) - ->delete('zkey1', 'zkey2', 'zkey5', 'zInter', 'zUnion') - ->zadd('zkey1', 1, 'zValue1') - ->zadd('zkey1', 5, 'zValue5') - ->zadd('zkey1', 2, 'zValue2') - ->zRange('zkey1', 0, -1) - ->zDelete('zkey1', 'zValue2') - ->zRange('zkey1', 0, -1) - ->zadd('zkey1', 11, 'zValue11') - ->zadd('zkey1', 12, 'zValue12') - ->zadd('zkey1', 13, 'zValue13') - ->zadd('zkey1', 14, 'zValue14') - ->zadd('zkey1', 15, 'zValue15') - ->zDeleteRangeByScore('zkey1', 11, 13) - ->zrange('zkey1', 0, -1) - ->zReverseRange('zkey1', 0, -1) - ->zRangeByScore('zkey1', 1, 6) - ->zCard('zkey1') - ->zScore('zkey1', 'zValue15') - ->zadd('zkey2', 5, 'zValue5') - ->zadd('zkey2', 2, 'zValue2') - ->zInter('zInter', array('zkey1', 'zkey2')) - ->zRange('zkey1', 0, -1) - ->zRange('zkey2', 0, -1) - ->zRange('zInter', 0, -1) - ->zUnion('zUnion', array('zkey1', 'zkey2')) - ->zRange('zUnion', 0, -1) - ->zadd('zkey5', 5, 'zValue5') - ->zIncrBy('zkey5', 3, 'zValue5') // fix this - ->zScore('zkey5', 'zValue5') - ->zScore('zkey5', 'unknown') - ->exec(); - - $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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++] === array('zValue1', 'zValue2', 'zValue5')); - $this->assertTrue($ret[$i++] === 1); - $this->assertTrue($ret[$i++] === array('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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); - $this->assertTrue($ret[$i++] === array('zValue15', 'zValue14', 'zValue5', 'zValue1')); - $this->assertTrue($ret[$i++] === array('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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); // zkey1 contents - $this->assertTrue($ret[$i++] === array('zValue2', 'zValue5')); // zkey2 contents - $this->assertTrue($ret[$i++] === array('zValue5')); // zinter contents - $this->assertTrue($ret[$i++] === 5); // zUnion has 5 values (1,2,5,14,15) - $this->assertTrue($ret[$i++] === array('zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15')); // zunion contents - $this->assertTrue($ret[$i++] === 1); // added value to zkey5, 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) - ->delete('hkey1') - ->hset('hkey1', 'key1', 'value1') - ->hset('hkey1', 'key2', 'value2') - ->hset('hkey1', 'key3', 'value3') - ->hmget('hkey1', array('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++] === array('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] === array('key1', 'key3') || $ret[$i] === array('key3', 'key1')); $i++; // hkeys - $this->assertTrue($ret[$i] === array('value1', 'value3') || $ret[$i] === array('value3', 'value1')); $i++; // hvals - $this->assertTrue($ret[$i] === array('key1' => 'value1', 'key3' => 'value3') || $ret[$i] === array('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. - ->delete('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); + $ret = $this->redis->multi($mode) + ->set('x', 42) + ->info() + ->type('x') + ->get('x') + ->exec(); + + $this->assertTrue(is_array($ret)); + $i = 0; + $this->assertTrue($ret[$i++] == TRUE); + $this->assertTrue(is_array($ret[$i++])); + $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_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer + $ret = $this->redis->multi($mode) + ->delete('key1') + ->set('key1', 'value1') + ->get('key1') + ->getSet('key1', 'value2') + ->get('key1') + ->set('key2', 4) + ->incr('key2') + ->get('key2') + ->decr('key2') + ->get('key2') + ->renameKey('key2', 'key3') + ->get('key3') + ->renameNx('key3', 'key1') + ->renameKey('key3', 'key2') + ->incrby('key2', 5) + ->get('key2') + ->decrby('key2', 5) + ->get('key2') + ->exec(); + + $this->assertTrue(is_array($ret)); + $i = 0; + $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) + ->delete('key1') + ->delete('key2') + ->set('key1', 'val1') + ->setnx('key1', 'valX') + ->setnx('key2', 'valX') + ->exists('key1') + ->exists('key3') + ->ping() + ->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); + $this->assertTrue($ret[7] == '+PONG'); + + $ret = $this->redis->multi($mode) + ->randomKey() + ->exec(); + $ret = $this->redis->multi($mode) + ->exec(); + $this->assertTrue($ret == array()); + + // ttl, mget, mset, msetnx, expire, expireAt + $this->redis->delete('key'); + $ret = $this->redis->multi($mode) + ->ttl('key') + ->mget(array('key1', 'key2', 'key3')) + ->mset(array('key3' => 'value3', 'key4' => '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 === -1 || $ttl === -2); + $this->assertTrue($ret[$i++] === array('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('lkey', 'x') + ->set('lDest', 'y') + ->delete('lkey', 'lDest') + ->rpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->rpoplpush('lkey', 'lDest') + ->lGetRange('lDest', 0, -1) + ->lpop('lkey') + ->llen('lkey') + ->lRemove('lkey', 'lvalue', 3) + ->llen('lkey') + ->lget('lkey', 0) + ->lGetRange('lkey', 0, -1) + ->lSet('lkey', 1, "newValue") // check errors on key not exists + ->lGetRange('lkey', 0, -1) + ->llen('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++] === array('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++] === array("lvalue")); // this is the current list. + $this->assertTrue($ret[$i++] === FALSE); // updating a non-existent element fails. + $this->assertTrue($ret[$i++] === array("lvalue")); // this is the current list. + $this->assertTrue($ret[$i++] === 1); // 1 element left + $this->assertTrue(count($ret) == $i); + + + $ret = $this->redis->multi($mode) + ->delete('lkey', 'lDest') + ->rpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->rpoplpush('lkey', 'lDest') + ->lGetRange('lDest', 0, -1) + ->lpop('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++] === array('lvalue')); // rpoplpush returns the element: "lvalue" + $this->assertTrue($ret[$i++] === 'lvalue'); // pop returns the front element: "lvalue" + $this->assertTrue(count($ret) == $i); + + + // general command + $ret = $this->redis->multi($mode) + ->select(3) + ->set('keyAAA', 'value') + ->set('keyAAB', 'value') + ->dbSize() + ->lastsave() + ->exec(); + + $this->redis->select(0); // back to normal + + $i = 0; + $this->assertTrue(is_array($ret)); + $this->assertTrue($ret[$i++] === TRUE); // select + $this->assertTrue($ret[$i++] === TRUE); // set + $this->assertTrue($ret[$i++] === TRUE); // set + $this->assertTrue(is_long($ret[$i++])); // dbsize + $this->assertTrue(is_long($ret[$i++])); // lastsave + + $this->assertTrue(count($ret) === $i); + + $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer + $ret = $this->redis->multi($mode) + ->delete('key1') + ->set('key1', 'value1') + ->get('key1') + ->getSet('key1', 'value2') + ->get('key1') + ->set('key2', 4) + ->incr('key2') + ->get('key2') + ->decr('key2') + ->get('key2') + ->renameKey('key2', 'key3') + ->get('key3') + ->renameNx('key3', 'key1') + ->renameKey('key3', 'key2') + ->incrby('key2', 5) + ->get('key2') + ->decrby('key2', 5) + ->get('key2') + ->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->redis->setOption(Redis::OPT_SERIALIZER, $serializer); + + $ret = $this->redis->multi($mode) + ->delete('key1') + ->delete('key2') + ->set('key1', 'val1') + ->setnx('key1', 'valX') + ->setnx('key2', 'valX') + ->exists('key1') + ->exists('key3') + ->ping() + ->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); + $this->assertTrue($ret[7] == '+PONG'); + + $ret = $this->redis->multi($mode) + ->randomKey() + ->exec(); + + $this->assertTrue(is_array($ret) && count($ret) === 1); + $this->assertTrue(is_string($ret[0])); + + // ttl, mget, mset, msetnx, expire, expireAt + $ret = $this->redis->multi($mode) + ->ttl('key') + ->mget(array('key1', 'key2', 'key3')) + ->mset(array('key3' => 'value3', 'key4' => '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) + ->delete('lkey', 'lDest') + ->rpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->lpush('lkey', 'lvalue') + ->rpoplpush('lkey', 'lDest') + ->lGetRange('lDest', 0, -1) + ->lpop('lkey') + ->llen('lkey') + ->lRemove('lkey', 'lvalue', 3) + ->llen('lkey') + ->lget('lkey', 0) + ->lGetRange('lkey', 0, -1) + ->lSet('lkey', 1, "newValue") // check errors on missing key + ->lGetRange('lkey', 0, -1) + ->llen('lkey') + ->exec(); + + $this->assertTrue(is_array($ret)); + $i = 0; + $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // delete + $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++] === array('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++] === array('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++] === array('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) + ->delete('skey1', 'skey2', 'skeydest', 'skeyUnion', 'sDiffDest') + ->sadd('skey1', 'sValue1') + ->sadd('skey1', 'sValue2') + ->sadd('skey1', 'sValue3') + ->sadd('skey1', 'sValue4') + + ->sadd('skey2', 'sValue1') + ->sadd('skey2', 'sValue2') + + ->sSize('skey1') + ->sRemove('skey1', 'sValue2') + ->sSize('skey1') + ->sMove('skey1', 'skey2', 'sValue4') + ->sSize('skey2') + ->sContains('skey2', 'sValue4') + ->sMembers('skey1') + ->sMembers('skey2') + ->sInter('skey1', 'skey2') + ->sInterStore('skeydest', 'skey1', 'skey2') + ->sMembers('skeydest') + ->sUnion('skey2', 'skeydest') + ->sUnionStore('skeyUnion', 'skey2', 'skeydest') + ->sMembers('skeyUnion') + ->sDiff('skey1', 'skey2') + ->sDiffStore('sDiffDest', 'skey1', 'skey2') + ->sMembers('sDiffDest') + ->sPop('skey2') + ->sSize('skey2') + ->exec(); + + $i = 0; + $this->assertTrue(is_array($ret)); + $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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(array('sValue1', 'sValue3') as $k) { // sKey1 contains sValue1 and sValue3. + $this->assertTrue(in_array($k, $ret[$i])); + } + $this->assertTrue(count($ret[$i++]) === 2); + foreach(array('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++] === array('sValue1')); // intersection + $this->assertTrue($ret[$i++] === 1); // intersection + store → 1 value in the destination set. + $this->assertTrue($ret[$i++] === array('sValue1')); // sinterstore destination contents + + foreach(array('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(array('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++] === array('sValue3')); // diff skey1, skey2 : only sValue3 is not shared. + $this->assertTrue($ret[$i++] === 1); // sdiffstore size == 1 + $this->assertTrue($ret[$i++] === array('sValue3')); // contents of sDiffDest + + $this->assertTrue(in_array($ret[$i++], array('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) + ->delete('zkey1', 'zkey2', 'zkey5', 'zInter', 'zUnion') + ->zadd('zkey1', 1, 'zValue1') + ->zadd('zkey1', 5, 'zValue5') + ->zadd('zkey1', 2, 'zValue2') + ->zRange('zkey1', 0, -1) + ->zDelete('zkey1', 'zValue2') + ->zRange('zkey1', 0, -1) + ->zadd('zkey1', 11, 'zValue11') + ->zadd('zkey1', 12, 'zValue12') + ->zadd('zkey1', 13, 'zValue13') + ->zadd('zkey1', 14, 'zValue14') + ->zadd('zkey1', 15, 'zValue15') + ->zDeleteRangeByScore('zkey1', 11, 13) + ->zrange('zkey1', 0, -1) + ->zReverseRange('zkey1', 0, -1) + ->zRangeByScore('zkey1', 1, 6) + ->zCard('zkey1') + ->zScore('zkey1', 'zValue15') + ->zadd('zkey2', 5, 'zValue5') + ->zadd('zkey2', 2, 'zValue2') + ->zInter('zInter', array('zkey1', 'zkey2')) + ->zRange('zkey1', 0, -1) + ->zRange('zkey2', 0, -1) + ->zRange('zInter', 0, -1) + ->zUnion('zUnion', array('zkey1', 'zkey2')) + ->zRange('zUnion', 0, -1) + ->zadd('zkey5', 5, 'zValue5') + ->zIncrBy('zkey5', 3, 'zValue5') // fix this + ->zScore('zkey5', 'zValue5') + ->zScore('zkey5', 'unknown') + ->exec(); + + $i = 0; + $this->assertTrue(is_array($ret)); + $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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++] === array('zValue1', 'zValue2', 'zValue5')); + $this->assertTrue($ret[$i++] === 1); + $this->assertTrue($ret[$i++] === array('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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); + $this->assertTrue($ret[$i++] === array('zValue15', 'zValue14', 'zValue5', 'zValue1')); + $this->assertTrue($ret[$i++] === array('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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); // zkey1 contents + $this->assertTrue($ret[$i++] === array('zValue2', 'zValue5')); // zkey2 contents + $this->assertTrue($ret[$i++] === array('zValue5')); // zinter contents + $this->assertTrue($ret[$i++] === 5); // zUnion has 5 values (1,2,5,14,15) + $this->assertTrue($ret[$i++] === array('zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15')); // zunion contents + $this->assertTrue($ret[$i++] === 1); // added value to zkey5, 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) + ->delete('hkey1') + ->hset('hkey1', 'key1', 'value1') + ->hset('hkey1', 'key2', 'value2') + ->hset('hkey1', 'key3', 'value3') + ->hmget('hkey1', array('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++] === array('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] === array('key1', 'key3') || $ret[$i] === array('key3', 'key1')); $i++; // hkeys + $this->assertTrue($ret[$i] === array('value1', 'value3') || $ret[$i] === array('value3', 'value1')); $i++; // hvals + $this->assertTrue($ret[$i] === array('key1' => 'value1', 'key3' => 'value3') || $ret[$i] === array('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. + ->delete('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'); @@ -4008,494 +4008,494 @@ public function testDifferentTypeHash() { public function testSerializerPHP() { - $this->checkSerializer(Redis::SERIALIZER_PHP); + $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, ""); + // 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); + 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, ""); - } + // with prefix + $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->checkSerializer(Redis::SERIALIZER_IGBINARY); + $this->redis->setOption(Redis::OPT_PREFIX, ""); + } } private function checkSerializer($mode) { - $this->redis->delete('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 = array('hello world', 42, TRUE, array('' => 1729)); - $this->redis->delete('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]); - - // lGetRange - $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); - - // lGet - $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); - $this->assertTrue($a[1] === $this->redis->lGet('key', 1)); - $this->assertTrue($a[2] === $this->redis->lGet('key', 2)); - $this->assertTrue($a[3] === $this->redis->lGet('key', 3)); - - // lRemove - $this->assertTrue($this->redis->lRemove('key', $a[3]) === 1); - $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lGetRange('key', 0, -1)); - - // lSet - $a[0] = array('k' => 'v'); // update - $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); - $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); - - // lInsert - $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], array(1,2,3)) === 4); - $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], array(4,5,6)) === 5); - - $a = array(array(1,2,3), $a[0], array(4,5,6), $a[1], $a[2]); - $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); - - // sAdd - $this->redis->delete('key'); - $s = array(1,'a', array(1,2,3), array('k' => 'v')); - - $this->assertTrue(1 === $this->redis->sAdd('key', $s[0])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[1])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[2])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[3])); - - // variadic sAdd - $this->redis->delete('k'); - $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); - $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); - - // sRemove - $this->assertTrue(1 === $this->redis->sRemove('key', $s[3])); - $this->assertTrue(0 === $this->redis->sRemove('key', $s[3])); - // variadic - $this->redis->delete('k'); - $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); - $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); - $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); - $this->assertTrue(FALSE === $this->redis->exists('k')); - - // sContains - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[1])); - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[2])); - $this->assertTrue(FALSE === $this->redis->sContains('key', $s[3])); - unset($s[3]); - - // sMove - $this->redis->delete('tmp'); - $this->redis->sMove('key', 'tmp', $s[0]); - $this->assertTrue(FALSE === $this->redis->sContains('key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sContains('tmp', $s[0])); - unset($s[0]); - - - // sorted sets - $z = array('z0', array('k' => 'v'), FALSE, NULL); - $this->redis->delete('key'); - - // zAdd - $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0])); - $this->assertTrue(1 === $this->redis->zAdd('key', 1, $z[1])); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2])); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3])); - - // zDelete - $this->assertTrue(1 === $this->redis->zDelete('key', $z[3])); - $this->assertTrue(0 === $this->redis->zDelete('key', $z[3])); - unset($z[3]); - - // check that zDelete doesn't crash with a missing parameter (GitHub issue #102): - $this->assertTrue(FALSE === @$this->redis->zDelete('key')); - - // variadic - $this->redis->delete('k'); - $this->redis->zAdd('k', 0, 'a'); - $this->redis->zAdd('k', 1, 'b'); - $this->redis->zAdd('k', 2, 'c'); - $this->assertTrue(2 === $this->redis->zDelete('k', 'a', 'c')); - $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); - $this->assertTrue($this->redis->zRange('k', 0, -1, true) == array('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(0 === $this->redis->zRank('key', $z[0])); - $this->assertTrue(1 === $this->redis->zRank('key', $z[1])); - $this->assertTrue(2 === $this->redis->zRank('key', $z[2])); - - // zRevRank - $this->assertTrue(2 === $this->redis->zRevRank('key', $z[0])); - $this->assertTrue(1 === $this->redis->zRevRank('key', $z[1])); - $this->assertTrue(0 === $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 = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); - $this->assertTrue(TRUE === $this->redis->mset($a)); - foreach($a as $k => $v) { - $this->assertTrue($this->redis->get($k) === $v); - } - - $a = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); - - // hSet - $this->redis->delete('key'); - foreach($a as $k => $v) { - $this->assertTrue(1 === $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->delete('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', array('x' => 'y')); - $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->getMultiple(array('a', 'b', 'c', 'd'))); - - // pipeline + $this->redis->delete('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 = array('hello world', 42, TRUE, array('' => 1729)); + $this->redis->delete('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]); + + // lGetRange + $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); + + // lGet + $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); + $this->assertTrue($a[1] === $this->redis->lGet('key', 1)); + $this->assertTrue($a[2] === $this->redis->lGet('key', 2)); + $this->assertTrue($a[3] === $this->redis->lGet('key', 3)); + + // lRemove + $this->assertTrue($this->redis->lRemove('key', $a[3]) === 1); + $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lGetRange('key', 0, -1)); + + // lSet + $a[0] = array('k' => 'v'); // update + $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); + $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); + + // lInsert + $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], array(1,2,3)) === 4); + $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], array(4,5,6)) === 5); + + $a = array(array(1,2,3), $a[0], array(4,5,6), $a[1], $a[2]); + $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); + + // sAdd + $this->redis->delete('key'); + $s = array(1,'a', array(1,2,3), array('k' => 'v')); + + $this->assertTrue(1 === $this->redis->sAdd('key', $s[0])); + $this->assertTrue(1 === $this->redis->sAdd('key', $s[1])); + $this->assertTrue(1 === $this->redis->sAdd('key', $s[2])); + $this->assertTrue(1 === $this->redis->sAdd('key', $s[3])); + + // variadic sAdd + $this->redis->delete('k'); + $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); + $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); + + // sRemove + $this->assertTrue(1 === $this->redis->sRemove('key', $s[3])); + $this->assertTrue(0 === $this->redis->sRemove('key', $s[3])); + // variadic + $this->redis->delete('k'); + $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); + $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); + $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); + $this->assertTrue(FALSE === $this->redis->exists('k')); + + // sContains + $this->assertTrue(TRUE === $this->redis->sContains('key', $s[0])); + $this->assertTrue(TRUE === $this->redis->sContains('key', $s[1])); + $this->assertTrue(TRUE === $this->redis->sContains('key', $s[2])); + $this->assertTrue(FALSE === $this->redis->sContains('key', $s[3])); + unset($s[3]); + + // sMove + $this->redis->delete('tmp'); + $this->redis->sMove('key', 'tmp', $s[0]); + $this->assertTrue(FALSE === $this->redis->sContains('key', $s[0])); + $this->assertTrue(TRUE === $this->redis->sContains('tmp', $s[0])); + unset($s[0]); + + + // sorted sets + $z = array('z0', array('k' => 'v'), FALSE, NULL); + $this->redis->delete('key'); + + // zAdd + $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0])); + $this->assertTrue(1 === $this->redis->zAdd('key', 1, $z[1])); + $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2])); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3])); + + // zDelete + $this->assertTrue(1 === $this->redis->zDelete('key', $z[3])); + $this->assertTrue(0 === $this->redis->zDelete('key', $z[3])); + unset($z[3]); + + // check that zDelete doesn't crash with a missing parameter (GitHub issue #102): + $this->assertTrue(FALSE === @$this->redis->zDelete('key')); + + // variadic + $this->redis->delete('k'); + $this->redis->zAdd('k', 0, 'a'); + $this->redis->zAdd('k', 1, 'b'); + $this->redis->zAdd('k', 2, 'c'); + $this->assertTrue(2 === $this->redis->zDelete('k', 'a', 'c')); + $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); + $this->assertTrue($this->redis->zRange('k', 0, -1, true) == array('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(0 === $this->redis->zRank('key', $z[0])); + $this->assertTrue(1 === $this->redis->zRank('key', $z[1])); + $this->assertTrue(2 === $this->redis->zRank('key', $z[2])); + + // zRevRank + $this->assertTrue(2 === $this->redis->zRevRank('key', $z[0])); + $this->assertTrue(1 === $this->redis->zRevRank('key', $z[1])); + $this->assertTrue(0 === $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 = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); + $this->assertTrue(TRUE === $this->redis->mset($a)); + foreach($a as $k => $v) { + $this->assertTrue($this->redis->get($k) === $v); + } + + $a = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); + + // hSet + $this->redis->delete('key'); + foreach($a as $k => $v) { + $this->assertTrue(1 === $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->delete('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', array('x' => 'y')); + $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->getMultiple(array('a', 'b', 'c', 'd'))); + + // pipeline if ($this->havePipeline()) { $this->sequence(Redis::PIPELINE); } - // multi-exec - $this->sequence(Redis::MULTI); + // multi-exec + $this->sequence(Redis::MULTI); - // keys - $this->assertTrue(is_array($this->redis->keys('*'))); + // 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'); + // 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'); + $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', array(new stdClass, new stdClass)); - $x = $this->redis->get('x'); - $this->assertTrue(is_array($x)); - $this->assertTrue(is_object($x[0]) && get_class($x[0]) === 'stdClass'); - $this->assertTrue(is_object($x[1]) && get_class($x[1]) === 'stdClass'); + // issue #145, serializer with objects. + $this->redis->set('x', array(new stdClass, new stdClass)); + $x = $this->redis->get('x'); + $this->assertTrue(is_array($x)); + $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_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE); // set ok - $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok + // revert + $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE); // set ok + $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok } public function testDumpRestore() { - if (version_compare($this->version, "2.5.0", "lt")) { - $this->markTestSkipped(); - } + if (version_compare($this->version, "2.5.0", "lt")) { + $this->markTestSkipped(); + } - $this->redis->del('foo'); - $this->redis->del('bar'); + $this->redis->del('foo'); + $this->redis->del('bar'); - $this->redis->set('foo', 'this-is-foo'); - $this->redis->set('bar', 'this-is-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'); + $d_foo = $this->redis->dump('foo'); + $d_bar = $this->redis->dump('bar'); - $this->redis->del('foo'); - $this->redis->del('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)); + // 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'); + // 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'); + $this->redis->del('foo'); + $this->redis->del('bar'); } public function testGetLastError() { - // We shouldn't have any errors now - $this->assertTrue($this->redis->getLastError() === NULL); - - // Throw some invalid lua at redis - $this->redis->eval("not-a-lua-script"); - - // Now we should have an error - $evalError = $this->redis->getLastError(); - $this->assertTrue(strlen($evalError) > 0); - - // test getLastError with a regular command - $this->redis->set('x', 'a'); - $this->assertFalse($this->redis->incr('x')); - $incrError = $this->redis->getLastError(); - $this->assertTrue($incrError !== $evalError); // error has changed - $this->assertTrue(strlen($incrError) > 0); - - // clear error - $this->redis->clearLastError(); - $this->assertTrue($this->redis->getLastError() === NULL); - } + // We shouldn't have any errors now + $this->assertTrue($this->redis->getLastError() === NULL); + + // Throw some invalid lua at redis + $this->redis->eval("not-a-lua-script"); + + // Now we should have an error + $evalError = $this->redis->getLastError(); + $this->assertTrue(strlen($evalError) > 0); + + // test getLastError with a regular command + $this->redis->set('x', 'a'); + $this->assertFalse($this->redis->incr('x')); + $incrError = $this->redis->getLastError(); + $this->assertTrue($incrError !== $evalError); // error has changed + $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 = array(); - - 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; + $aReturn = array(); + + 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", "lt")) { - $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); + if (version_compare($this->version, "2.5.0", "lt")) { + $this->markTestSkipped(); + } - // 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); + // 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", "lt")) { - $this->markTestSkipped(); - } - - // Basic single line response tests - $this->assertTrue(1 == $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('mylist'); - $this->redis->rpush('mylist', 'a'); - $this->redis->rpush('mylist', 'b'); - $this->redis->rpush('mylist', 'c'); - - // Make a set - $this->redis->del('myset'); - $this->redis->sadd('myset', 'd'); - $this->redis->sadd('myset', 'e'); - $this->redis->sadd('myset', 'f'); - - // Basic keys - $this->redis->set('key1', 'hello, world'); - $this->redis->set('key2', 'hello again!'); - - // Use a script to return our list, and verify its response - $list = $this->redis->eval("return redis.call('lrange', 'mylist', 0, -1)"); - $this->assertTrue($list === Array('a','b','c')); - - // Use a script to return our set - $set = $this->redis->eval("return redis.call('smembers', 'myset')"); - $this->assertTrue($set == Array('d','e','f')); - - // Test an empty MULTI BULK response - $this->redis->del('not-any-kind-of-list'); - $empty_resp = $this->redis->eval("return redis.call('lrange', 'not-any-kind-of-list', 0, -1)"); - $this->assertTrue(is_array($empty_resp) && empty($empty_resp)); - - // Now test a nested reply - $nested_script = " - return { - 1,2,3, { - redis.call('get', 'key1'), - redis.call('get', 'key2'), - redis.call('lrange', 'not-any-kind-of-list', 0, -1), - { - redis.call('smembers','myset'), - redis.call('lrange', 'mylist', 0, -1) - } - } - } - "; - - $expected = Array( - 1, 2, 3, Array( - 'hello, world', - 'hello again!', - Array(), - Array( - Array('d','e','f'), - Array('a','b','c') - ) - ) - ); - - // Now run our script, and check our values against each other - $eval_result = $this->redis->eval($nested_script); - $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; + if (version_compare($this->version, "2.5.0", "lt")) { + $this->markTestSkipped(); + } + + // Basic single line response tests + $this->assertTrue(1 == $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('mylist'); + $this->redis->rpush('mylist', 'a'); + $this->redis->rpush('mylist', 'b'); + $this->redis->rpush('mylist', 'c'); + + // Make a set + $this->redis->del('myset'); + $this->redis->sadd('myset', 'd'); + $this->redis->sadd('myset', 'e'); + $this->redis->sadd('myset', 'f'); + + // Basic keys + $this->redis->set('key1', 'hello, world'); + $this->redis->set('key2', 'hello again!'); + + // Use a script to return our list, and verify its response + $list = $this->redis->eval("return redis.call('lrange', 'mylist', 0, -1)"); + $this->assertTrue($list === Array('a','b','c')); + + // Use a script to return our set + $set = $this->redis->eval("return redis.call('smembers', 'myset')"); + $this->assertTrue($set == Array('d','e','f')); + + // Test an empty MULTI BULK response + $this->redis->del('not-any-kind-of-list'); + $empty_resp = $this->redis->eval("return redis.call('lrange', 'not-any-kind-of-list', 0, -1)"); + $this->assertTrue(is_array($empty_resp) && empty($empty_resp)); + + // Now test a nested reply + $nested_script = " + return { + 1,2,3, { + redis.call('get', 'key1'), + redis.call('get', 'key2'), + redis.call('lrange', 'not-any-kind-of-list', 0, -1), + { + redis.call('smembers','myset'), + redis.call('lrange', 'mylist', 0, -1) + } + } + } + "; + + $expected = Array( + 1, 2, 3, Array( + 'hello, world', + 'hello again!', + Array(), + Array( + Array('d','e','f'), + Array('a','b','c') + ) + ) + ); + + // Now run our script, and check our values against each other + $eval_result = $this->redis->eval($nested_script); + $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 = Array(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); - } - $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 = Array('k1','k2','k3','v1','v2','v3'); - $args_result = $this->redis->eval($args_script, $args_args, 3); - $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_args, 3); - - // Make sure our first three are prefixed - for($i=0;$iassertTrue($args_result[$i] == 'prefix:' . $args_args[$i]); - } else { - // Should not be prefixed - $this->assertTrue($args_result[$i] == $args_args[$i]); - } - } + foreach($arr_modes as $mode) { + $this->redis->multi($mode); + for($i=0;$i<$num_scripts;$i++) { + $this->redis->eval($nested_script); + } + $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 = Array('k1','k2','k3','v1','v2','v3'); + $args_result = $this->redis->eval($args_script, $args_args, 3); + $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_args, 3); + + // Make sure our first three are prefixed + for($i=0;$iassertTrue($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", "lt")) { - $this->markTestSkipped(); - } + if (version_compare($this->version, "2.5.0", "lt")) { + $this->markTestSkipped(); + } - // Flush any loaded scripts - $this->redis->script('flush'); + // 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')); + // 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); + // 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(1 === $this->redis->eval($scr)); - $this->assertTrue(1 === $this->redis->evalsha($sha)); + // 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(1 === $this->redis->eval($scr)); + $this->assertTrue(1 === $this->redis->evalsha($sha)); } public function testSerialize() { @@ -4527,103 +4527,103 @@ public function testSerialize() { } public function testUnserialize() { - $vals = Array( - 1,1.5,'one',Array('this','is','an','array') - ); + $vals = Array( + 1,1.5,'one',Array('this','is','an','array') + ); - $serializers = Array(Redis::SERIALIZER_PHP); - if(defined('Redis::SERIALIZER_IGBINARY')) { - $serializers[] = Redis::SERIALIZER_IGBINARY; - } + $serializers = Array(Redis::SERIALIZER_PHP); + if(defined('Redis::SERIALIZER_IGBINARY')) { + $serializers[] = Redis::SERIALIZER_IGBINARY; + } - foreach($serializers as $mode) { - $vals_enc = Array(); + foreach($serializers as $mode) { + $vals_enc = Array(); - // Pass them through redis so they're serialized - foreach($vals as $key => $val) { - $this->redis->setOption(Redis::OPT_SERIALIZER, $mode); + // 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); + $key = "key" . ++$key; + $this->redis->del($key); + $this->redis->set($key, $val); - // Clear serializer, get serialized value - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); - $vals_enc[] = $this->redis->get($key); - } + // Clear serializer, get serialized value + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); + $vals_enc[] = $this->redis->get($key); + } - // Run through our array comparing values - for($i=0;$iredis->setOption(Redis::OPT_SERIALIZER, $mode); - $this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i])); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); - } - } + // Run through our array comparing values + for($i=0;$iredis->setOption(Redis::OPT_SERIALIZER, $mode); + $this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i])); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); + } + } } public function testPrefix() { - // no prefix - $this->redis->setOption(Redis::OPT_PREFIX, ''); - $this->assertTrue('key' == $this->redis->_prefix('key')); + // 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')); + // 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, ''); + // Clear prefix + $this->redis->setOption(Redis::OPT_PREFIX, ''); } - public function testReconnectSelect() { - $key = 'reconnect-select'; - $value = 'Has been set!'; + public function testReconnectSelect() { + $key = 'reconnect-select'; + $value = 'Has been set!'; - $original_cfg = $this->redis->config('GET', 'timeout'); + $original_cfg = $this->redis->config('GET', 'timeout'); - // Make sure the default DB doesn't have the key. - $this->redis->select(0); - $this->redis->delete($key); + // Make sure the default DB doesn't have the key. + $this->redis->select(0); + $this->redis->delete($key); - // Set the key on a different DB. - $this->redis->select(5); - $this->redis->set($key, $value); + // 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'); + // 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); + // 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)); + // 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']); - } + // Revert the setting. + $this->redis->config('SET', 'timeout', $original_cfg['timeout']); + } - public function testTime() { + public function testTime() { - if (version_compare($this->version, "2.5.0", "lt")) { - $this->markTestSkipped(); - } + if (version_compare($this->version, "2.5.0", "lt")) { + $this->markTestSkipped(); + } - $time_arr = $this->redis->time(); - $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && - strval(intval($time_arr[0])) === strval($time_arr[0]) && - strval(intval($time_arr[1])) === strval($time_arr[1])); - } + $time_arr = $this->redis->time(); + $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && + strval(intval($time_arr[0])) === strval($time_arr[0]) && + strval(intval($time_arr[1])) === strval($time_arr[1])); + } - public function testReadTimeoutOption() { + public function testReadTimeoutOption() { - $this->assertTrue(defined('Redis::OPT_READ_TIMEOUT')); + $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)); - } + $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 From cceb49c5a9e491a112865f49120ca524216b597d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 10:38:41 -0800 Subject: [PATCH 0430/1986] Kill more tabs with fire --- tests/TestSuite.php | 132 ++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 73 deletions(-) diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 8c27bd7a85..b38ea791eb 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -2,21 +2,22 @@ // phpunit is such a pain to install, we're going with pure-PHP here. class TestSuite { - public static $errors = array(); - public static $warnings = array(); - protected function assertFalse($bool) { - $this->assertTrue(!$bool); - } + public static $errors = array(); + public static $warnings = array(); - protected function assertTrue($bool) { - if($bool) - return; + protected function assertFalse($bool) { + $this->assertTrue(!$bool); + } + + protected function assertTrue($bool) { + if($bool) + return; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); - } + $bt = debug_backtrace(false); + self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + } protected function assertLess($a, $b) { if($a < $b) @@ -28,46 +29,35 @@ protected function assertLess($a, $b) { $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); } - protected function assertEquals($a, $b) { - if($a === $b) - return; + protected function assertEquals($a, $b) { + if($a === $b) + return; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed (%s !== %s): %s:%d (%s)\n", - print_r($a, true), print_r($b, true), - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); - } + $bt = debug_backtrace(false); + self::$errors []= sprintf("Assertion failed (%s !== %s): %s:%d (%s)\n", + print_r($a, true), print_r($b, true), + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + } - protected function markTestSkipped($msg='') { - $bt = debug_backtrace(false); - self::$warnings []= sprintf("Skipped test: %s:%d (%s) %s\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); + protected function markTestSkipped($msg='') { + $bt = debug_backtrace(false); + self::$warnings []= sprintf("Skipped test: %s:%d (%s) %s\n", + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); - throw new Exception($msg); - } + throw new Exception($msg); + } - public static function run($className, $str_limit = NULL) { + public static function run($className, $str_limit = NULL) { /* Lowercase our limit arg if we're passed one */ $str_limit = $str_limit ? strtolower($str_limit) : $str_limit; - $rc = new ReflectionClass($className); - $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC); - $i_limit = -1; - $i_idx = 0; - $boo_printed = false; - $arr_ran_methods = Array(); - - if ($str_limit && is_numeric($str_limit)) { - echo "Limiting to $str_limit tests!\n"; - $i_limit = (integer)$str_limit; - } else if ($str_limit) { - echo "Limiting to tests with the substring: '$str_limit'\n"; - } + $rc = new ReflectionClass($className); + $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC); - foreach($methods as $m) { - $name = $m->name; - if(substr($name, 0, 4) !== 'test') - continue; + foreach($methods as $m) { + $name = $m->name; + if(substr($name, 0, 4) !== 'test') + continue; /* If we're trying to limit to a specific test and can't match the * substring, skip */ @@ -75,36 +65,32 @@ public static function run($className, $str_limit = NULL) { continue; } - $count = count($className::$errors); - $rt = new $className; - try { - $rt->setUp(); - $rt->$name(); - echo ($count === count($className::$errors)) ? "." : "F"; - } catch (Exception $e) { - if ($e instanceof RedisException) { - $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n"; - echo 'F'; - } else { - echo 'S'; - } + $count = count($className::$errors); + $rt = new $className; + try { + $rt->setUp(); + $rt->$name(); + echo ($count === count($className::$errors)) ? "." : "F"; + } catch (Exception $e) { + if ($e instanceof RedisException) { + $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n"; + echo 'F'; + } else { + echo 'S'; + } } - } - echo "\n"; - echo implode('', $className::$warnings); - - echo " --- tests run ---\n"; - echo implode("\n", $arr_ran_methods) . "\n" ; - echo " --- fin ---\n"; - - if(empty($className::$errors)) { - echo "All tests passed. \o/\n"; - return 0; - } - - echo implode('', $className::$errors); - return 1; - } + } + echo "\n"; + echo implode('', $className::$warnings); + + if(empty($className::$errors)) { + echo "All tests passed. \o/\n"; + return 0; + } + + echo implode('', $className::$errors); + return 1; + } } ?> From 42b81c4f1c24ab848890feaa36f75b4d9bb550f8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 12:36:07 -0800 Subject: [PATCH 0431/1986] Allow del/mget to either take one array or be treated as variadic --- redis_cluster.c | 75 +++++++++++++++++++++++++++++++++++++++------ tests/RedisTest.php | 65 +++++++++++++++++++-------------------- 2 files changed, 97 insertions(+), 43 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index c4942323e9..f042c952e8 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -459,7 +459,7 @@ PHP_METHOD(RedisCluster, __construct) { /* If we've been passed only one argument, the user is attempting to connect * to a named cluster, stored in php.ini, otherwise we'll need manual seeds */ - if (ZEND_NUM_ARGS() > 2) { + if (ZEND_NUM_ARGS() > 1) { redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout TSRMLS_CC); } else { @@ -612,6 +612,34 @@ static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, return 0; } +/* Turn variable arguments into a HashTable for processing */ +static HashTable *method_args_to_ht(INTERNAL_FUNCTION_PARAMETERS) { + HashTable *ht_ret; + int argc = ZEND_NUM_ARGS(), i; + zval **z_args; + + /* Allocate our hash table */ + ALLOC_HASHTABLE(ht_ret); + zend_hash_init(ht_ret, argc, NULL, NULL, 0); + + /* Allocate storage for our elements */ + z_args = emalloc(sizeof(zval*)*argc); + if (zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + return NULL; + } + + /* Populate our return hash table with our arguments */ + for (i = 0; i < argc; i++) { + zend_hash_next_index_insert(ht_ret, &z_args[i], sizeof(zval*), NULL); + } + + /* Free our argument container array */ + efree(z_args); + + /* Return our hash table */ + return ht_ret; +} + /* Handler for both MGET and DEL */ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zval *z_ret, cluster_cb cb) @@ -622,19 +650,30 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zval *z_arr; HashTable *ht_arr; HashPosition ptr; - int i=1, argc; + int i=1, argc, ht_free=0; short slot; - // Parse our arguments - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { + /* If we're called with one argument, attempt to process it as a single + * array. Otherwise, treat it as variadic and put the function arguments + * into a HashTable that we'll free. */ + if (ZEND_NUM_ARGS() == 1) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"a",&z_arr)==FAILURE) + { + return -1; + } + ht_arr = Z_ARRVAL_P(z_arr); + } else if (ZEND_NUM_ARGS() > 1) { + ht_arr = method_args_to_ht(INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (!ht_arr) return -1; + ht_free = 1; + } else { + /* Zero arguments passed, do nothing */ return -1; } - // No reason to send zero arguments - ht_arr = Z_ARRVAL_P(z_arr); - if((argc = zend_hash_num_elements(ht_arr))==0) { - return -1; - } + /* Grab our argument count and abort if we have zero */ + argc = zend_hash_num_elements(ht_arr); + if (!argc) return -1; /* MGET is readonly, DEL is not */ c->readonly = kw_len == 3 && CLUSTER_IS_ATOMIC(c); @@ -663,6 +702,10 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, while(zend_hash_has_more_elements_ex(ht_arr, &ptr)==SUCCESS) { if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { cluster_multi_free(&mc); + if (ht_free) { + zend_hash_destroy(ht_arr); + efree(ht_arr); + } return -1; } @@ -673,6 +716,10 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, &mc, z_ret, i==argc, cb)<0) { cluster_multi_free(&mc); + if (ht_free) { + zend_hash_destroy(ht_arr); + efree(ht_arr); + } return -1; } } @@ -696,6 +743,10 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, &mc, z_ret, 1, cb)<0) { cluster_multi_free(&mc); + if (ht_free) { + zend_hash_destroy(ht_arr); + efree(ht_arr); + } return -1; } } @@ -703,6 +754,12 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Free our command cluster_multi_free(&mc); + /* Clean up our hash table if we constructed it from variadic args */ + if (ht_free) { + zend_hash_destroy(ht_arr); + efree(ht_arr); + } + if(!CLUSTER_IS_ATOMIC(c)) RETVAL_ZVAL(getThis(), 1, 0); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 24c33ca1be..8ae4893eea 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -13,8 +13,7 @@ class Redis_Test extends TestSuite */ public $redis; - public function setUp() - { + public function setUp() { $this->redis = $this->newInstance(); $info = $this->redis->info(); $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); @@ -30,12 +29,10 @@ protected function newInstance() { return $r; } - public function tearDown() - { - if($this->redis) { - $this->redis->close(); - } - // unset($this->redis); + public function tearDown() { + if($this->redis) { + $this->redis->close(); + } } public function reset() @@ -46,7 +43,8 @@ public function reset() /* Helper function to determine if the clsas has pipeline support */ protected function havePipeline() { - return defined('Redis::PIPELINE'); + $str_constant = get_class($this->redis) . '::PIPELINE'; + return defined($str_constant); } public function testMinimumVersion() @@ -57,13 +55,12 @@ public function testMinimumVersion() public function testPing() { + $this->assertEquals('+PONG', $this->redis->ping()); - $this->assertEquals('+PONG', $this->redis->ping()); - - $count = 1000; - while($count --) { + $count = 1000; + while($count --) { $this->assertEquals('+PONG', $this->redis->ping()); - } + } } public function testPipelinePublish() { @@ -122,43 +119,43 @@ public function testPubSub() { public function testBitsets() { $this->redis->delete('key'); - $this->assertTrue(0 === $this->redis->getBit('key', 0)); - $this->assertTrue(FALSE === $this->redis->getBit('key', -1)); - $this->assertTrue(0 === $this->redis->getBit('key', 100000)); + $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->assertTrue(1 === $this->redis->getBit('key', $i)); + $this->assertEquals(1, $this->redis->getBit('key', $i)); } - $this->assertTrue(0 === $this->redis->getBit('key', 8)); + $this->assertEquals(0, $this->redis->getBit('key', 8)); // change bit 0 - $this->assertTrue(1 === $this->redis->setBit('key', 0, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 0, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 0)); - $this->assertTrue("\x7f" === $this->redis->get('key')); + $this->assertEquals(1, $this->redis->setBit('key', 0, 0)); + $this->assertEquals(0, $this->redis->setBit('key', 0, 0)); + $this->assertEquals(0, $this->redis->getBit('key', 0)); + $this->assertEquals("\x7f", $this->redis->get('key')); // change bit 1 - $this->assertTrue(1 === $this->redis->setBit('key', 1, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 1, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 1)); - $this->assertTrue("\x3f" === $this->redis->get('key')); + $this->assertEquals(1, $this->redis->setBit('key', 1, 0)); + $this->assertEquals(0, $this->redis->setBit('key', 1, 0)); + $this->assertEquals(0, $this->redis->getBit('key', 1)); + $this->assertEquals("\x3f", $this->redis->get('key')); // change bit > 1 - $this->assertTrue(1 === $this->redis->setBit('key', 2, 0)); - $this->assertTrue(0 === $this->redis->setBit('key', 2, 0)); - $this->assertTrue(0 === $this->redis->getBit('key', 2)); - $this->assertTrue("\x1f" === $this->redis->get('key')); + $this->assertEquals(1, $this->redis->setBit('key', 2, 0)); + $this->assertEquals(0, $this->redis->setBit('key', 2, 0)); + $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->assertTrue(0 === $this->redis->setBit('key', 0, 0xff)); - $this->assertTrue("\x9f" === $this->redis->get('key')); + $this->assertEquals(0, $this->redis->setBit('key', 0, 0xff)); + $this->assertEquals("\x9f", $this->redis->get('key')); // Verify valid offset ranges $this->assertFalse($this->redis->getBit('key', -1)); $this->redis->setBit('key', 4294967295, 1); - $this->assertTrue(1 === $this->redis->getBit('key', 4294967295)); + $this->assertEquals(1, $this->redis->getBit('key', 4294967295)); } public function testBitPos() { From 0b269cbb722529bb5a01e0c58f19c5c510f82c1c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 15:24:23 -0800 Subject: [PATCH 0432/1986] More RedisCluster fixes * Properly handle single array as well as variadic arguments for things like RedisCluster::del * Wrapping keys in {} such that Redis and RedisCluster tests can use the same methods (avoiding CROSSSLOT). * Fixed a double-free scenerio in redis_array_impl.c --- cluster_library.c | 2 - redis_array_impl.c | 3 - redis_cluster.c | 56 ++--- tests/RedisClusterTest.php | 35 ++- tests/RedisTest.php | 472 ++++++++++++++++++------------------- tests/TestRedis.php | 7 +- 6 files changed, 289 insertions(+), 286 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 372d17c2cd..a9f06dcaec 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1961,8 +1961,6 @@ PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, // If we had a failure, pad results with FALSE to indicate failure. Non // existant keys (e.g. for MGET will come back as NULL) if(fail) { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Invalid response from Redis for MGET command"); while(mctx->count--) { add_next_index_bool(mctx->z_multi, 0); } diff --git a/redis_array_impl.c b/redis_array_impl.c index 98dec588d5..1c48d7a589 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -474,9 +474,6 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D } if(out_pos) *out_pos = pos; - /* Cleanup */ - efree(out); - return ra->redis[pos]; } diff --git a/redis_cluster.c b/redis_cluster.c index f042c952e8..6a4bcf33ab 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -613,29 +613,19 @@ static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, } /* Turn variable arguments into a HashTable for processing */ -static HashTable *method_args_to_ht(INTERNAL_FUNCTION_PARAMETERS) { +static HashTable *method_args_to_ht(zval **z_args, int argc) { HashTable *ht_ret; - int argc = ZEND_NUM_ARGS(), i; - zval **z_args; + int i; /* Allocate our hash table */ ALLOC_HASHTABLE(ht_ret); zend_hash_init(ht_ret, argc, NULL, NULL, 0); - /* Allocate storage for our elements */ - z_args = emalloc(sizeof(zval*)*argc); - if (zend_get_parameters_array(ht, argc, z_args)==FAILURE) { - return NULL; - } - /* Populate our return hash table with our arguments */ for (i = 0; i < argc; i++) { zend_hash_next_index_insert(ht_ret, &z_args[i], sizeof(zval*), NULL); } - /* Free our argument container array */ - efree(z_args); - /* Return our hash table */ return ht_ret; } @@ -647,33 +637,37 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, redisCluster *c = GET_CONTEXT(); clusterMultiCmd mc = {0}; clusterKeyValHT kv; - zval *z_arr; + zval **z_args; HashTable *ht_arr; HashPosition ptr; - int i=1, argc, ht_free=0; + int i=1, argc = ZEND_NUM_ARGS(), ht_free=0; short slot; - /* If we're called with one argument, attempt to process it as a single - * array. Otherwise, treat it as variadic and put the function arguments - * into a HashTable that we'll free. */ - if (ZEND_NUM_ARGS() == 1) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"a",&z_arr)==FAILURE) - { + /* If we don't have any arguments we're invalid */ + if (!argc) return -1; + + /* Extract our arguments into an array */ + z_args = emalloc(sizeof(zval*)*argc); + if (zend_get_parameters_array(ht, ZEND_NUM_ARGS(), z_args) == FAILURE) { + efree(z_args); + return -1; + } + + /* Determine if we're working with a single array or variadic args */ + if (argc == 1 && Z_TYPE_P(z_args[0]) == IS_ARRAY) { + ht_arr = Z_ARRVAL_P(z_args[0]); + argc = zend_hash_num_elements(ht_arr); + if (!argc) { + efree(z_args); return -1; } - ht_arr = Z_ARRVAL_P(z_arr); - } else if (ZEND_NUM_ARGS() > 1) { - ht_arr = method_args_to_ht(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (!ht_arr) return -1; - ht_free = 1; } else { - /* Zero arguments passed, do nothing */ - return -1; + ht_arr = method_args_to_ht(z_args, argc); + ht_free = 1; } - /* Grab our argument count and abort if we have zero */ - argc = zend_hash_num_elements(ht_arr); - if (!argc) return -1; + /* We no longer need our array args */ + efree(z_args); /* MGET is readonly, DEL is not */ c->readonly = kw_len == 3 && CLUSTER_IS_ATOMIC(c); @@ -2625,7 +2619,7 @@ PHP_METHOD(RedisCluster, info) { zval *z_arg; short slot; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &opt, + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &z_arg, &opt, &opt_len)==FAILURE) { RETURN_FALSE; diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 6eabf9996b..e3a8e1a768 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -17,11 +17,44 @@ public function __construct() { } /* Store our node map */ - $this->_arr_node_map = array_filter(file_get_contents('nodes/nodemap')); + $this->_arr_node_map = array_filter( + explode("\n", file_get_contents('nodes/nodemap') + )); + } + + /* Override setUp to get info from a specific node */ + public function setUp() { + $this->redis = $this->newInstance(); + $info = $this->redis->info(uniqid()); + $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); } /* Override newInstance as we want a RedisCluster object */ protected function newInstance() { return new RedisCluster(NULL, $this->_arr_node_map); } + + /* Overrides for RedisTest where the function signature is different. This + * is only true for a few commands, which by definition have to be directed + * at a specific node */ + + public function testPing() { + for ($i = 0; $i < 100; $i++) { + $this->assertTrue($this->redis->ping("key:$i")); + } + } + + public function testRandomKey() { + for ($i = 0; $i < 1000; $i++) { + $k = $this->redis->randomKey("key:$i"); + $this->assertTrue($this->redis->exists($k)); + } + } + + public function testEcho() { + $this->assertEquals($this->redis->echo('k1', 'hello'), 'hello'); + $this->assertEquals($this->redis->echo('k2', 'world'), 'world'); + $this->assertEquals($this->redis->echo('k3', " 0123 "), " 0123 "); + } } +?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 8ae4893eea..76b87df36c 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -118,7 +118,7 @@ public function testPubSub() { public function testBitsets() { - $this->redis->delete('key'); + $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)); @@ -210,7 +210,7 @@ public function testSet() $this->assertEquals('val', $this->redis->get('key')); $this->assertEquals('val', $this->redis->get('key')); - $this->redis->delete('keyNotExist'); + $this->redis->del('keyNotExist'); $this->assertEquals(FALSE, $this->redis->get('keyNotExist')); $this->redis->set('key2', 'val'); @@ -222,8 +222,8 @@ public function testSet() $this->assertEquals($value, $this->redis->get('key2')); $this->assertEquals($value, $this->redis->get('key2')); - $this->redis->delete('key'); - $this->redis->delete('key2'); + $this->redis->del('key'); + $this->redis->del('key2'); $i = 66000; @@ -235,19 +235,19 @@ public function testSet() $this->redis->set('key', $value2); $this->assertEquals($value2, $this->redis->get('key')); - $this->redis->delete('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->delete('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->delete('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)); @@ -332,7 +332,7 @@ public function testExtendedSet() { } public function testGetSet() { - $this->redis->delete('key'); + $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'); @@ -346,88 +346,66 @@ public function testRandomKey() { } public function testRename() { - - // strings - $this->redis->delete('key0'); - $this->redis->set('key0', 'val0'); - $this->redis->renameKey('key0', 'key1'); - $this->assertTrue($this->redis->get('key0') === FALSE); - $this->assertTrue($this->redis->get('key1') === 'val0'); - - - // lists - $this->redis->delete('key0'); - $this->redis->lPush('key0', 'val0'); - $this->redis->lPush('key0', 'val1'); - $this->redis->renameKey('key0', 'key1'); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); - $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1', 'val0')); - - // variadic - $this->redis->delete('key0'); - $this->assertTrue(3 === $this->redis->lPush('key0', 'val0', 'val1', 'val2')); - $this->assertTrue(array('val2', 'val1', 'val0') === $this->redis->lrange('key0', 0, -1)); - - $this->redis->delete('key0'); - $this->assertTrue(3 === $this->redis->rPush('key0', 'val0', 'val1', 'val2')); - $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->lrange('key0', 0, -1)); + // 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', '{val}0'); + $this->redis->set('{key}1', '{val}1'); + $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'); - // strings - $this->redis->delete('key0', 'key1'); - $this->redis->set('key0', 'val0'); - $this->redis->set('key1', 'val1'); - $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); - $this->assertTrue($this->redis->get('key0') === 'val0'); - $this->assertTrue($this->redis->get('key1') === 'val1'); - - // lists - $this->redis->delete('key0'); - $this->redis->delete('key1'); - $this->redis->lPush('key0', 'val0'); - $this->redis->lPush('key0', 'val1'); - $this->redis->lPush('key1', 'val1-0'); - $this->redis->lPush('key1', 'val1-1'); - $this->assertTrue($this->redis->renameNx('key0', 'key1') === FALSE); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array('val1', 'val0')); - $this->assertTrue($this->redis->lGetRange('key1', 0, -1) === array('val1-1', 'val1-0')); - - $this->redis->delete('key2'); - $this->assertTrue($this->redis->renameNx('key0', 'key2') === TRUE); - $this->assertTrue($this->redis->lGetRange('key0', 0, -1) === array()); - $this->assertTrue($this->redis->lGetRange('key2', 0, -1) === array('val1', 'val0')); - + // 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) === array('val1', 'val0')); + $this->assertTrue($this->redis->lRange('{key}1', 0, -1) === array('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) === array()); + $this->assertTrue($this->redis->lRange('{key}2', 0, -1) === array('val1', 'val0')); } public function testMultiple() { + $this->redis->del('k1'); + $this->redis->del('k2'); + $this->redis->del('k3'); - $this->redis->delete('k1'); - $this->redis->delete('k2'); - $this->redis->delete('k3'); - - $this->redis->set('k1', 'v1'); - $this->redis->set('k2', 'v2'); - $this->redis->set('k3', 'v3'); - $this->redis->set(1, 'test'); + $this->redis->set('k1', 'v1'); + $this->redis->set('k2', 'v2'); + $this->redis->set('k3', 'v3'); + $this->redis->set(1, 'test'); - $this->assertEquals(array('v1'), $this->redis->getMultiple(array('k1'))); - $this->assertEquals(array('v1', 'v3', false), $this->redis->getMultiple(array('k1', 'k3', 'NoKey'))); - $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + $this->assertEquals(array('v1'), $this->redis->mget(array('k1'))); + $this->assertEquals(array('v1', 'v3', false), $this->redis->mget(array('k1', 'k3', 'NoKey'))); + $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->mget(array('k1', 'k2', 'k3'))); + $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->mget(array('k1', 'k2', 'k3'))); - $this->redis->set('k5', '$1111111111'); - $this->assertEquals(array(0 => '$1111111111'), $this->redis->getMultiple(array('k5'))); + $this->redis->set('k5', '$1111111111'); + $this->assertEquals(array(0 => '$1111111111'), $this->redis->mget(array('k5'))); - $this->assertEquals(array(0 => 'test'), $this->redis->getMultiple(array(1))); // non-string + $this->assertEquals(array(0 => 'test'), $this->redis->mget(array(1))); // non-string } public function testMultipleBin() { - $this->redis->delete('k1'); - $this->redis->delete('k2'); - $this->redis->delete('k3'); + $this->redis->del('k1'); + $this->redis->del('k2'); + $this->redis->del('k3'); $this->redis->set('k1', gzcompress('v1')); $this->redis->set('k2', gzcompress('v2')); @@ -440,7 +418,7 @@ public function testMultipleBin() { public function testSetTimeout() { - $this->redis->delete('key'); + $this->redis->del('key'); $this->redis->set('key', 'value'); $this->assertEquals('value', $this->redis->get('key')); $this->redis->setTimeout('key', 1); @@ -451,7 +429,7 @@ public function testSetTimeout() { public function testExpireAt() { - $this->redis->delete('key'); + $this->redis->del('key'); $this->redis->set('key', 'value'); $now = time(NULL); $this->redis->expireAt('key', $now + 1); @@ -462,7 +440,7 @@ public function testExpireAt() { public function testSetEx() { - $this->redis->delete('key'); + $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'); @@ -474,14 +452,14 @@ public function testSetNX() { $this->assertTrue($this->redis->setnx('key', 'err') === FALSE); $this->assertTrue($this->redis->get('key') === '42'); - $this->redis->delete('key'); + $this->redis->del('key'); $this->assertTrue($this->redis->setnx('key', '42') === TRUE); $this->assertTrue($this->redis->get('key') === '42'); } public function testExpireAtWithLong() { $longExpiryTimeExceedingInt = 3153600000; - $this->redis->delete('key'); + $this->redis->del('key'); $this->assertTrue($this->redis->setex('key', $longExpiryTimeExceedingInt, 'val') === TRUE); $this->assertTrue($this->redis->ttl('key') === $longExpiryTimeExceedingInt); } @@ -508,7 +486,7 @@ public function testIncr() $this->redis->incr('key', 5); $this->assertEquals(10, (int)$this->redis->get('key')); - $this->redis->delete('key'); + $this->redis->del('key'); $this->redis->set('key', 'abc'); @@ -526,7 +504,7 @@ public function testIncrByFloat() $this->markTestSkipped(); } - $this->redis->delete('key'); + $this->redis->del('key'); $this->redis->set('key', 0); @@ -585,7 +563,7 @@ public function testDecr() public function testExists() { - $this->redis->delete('key'); + $this->redis->del('key'); $this->assertFalse($this->redis->exists('key')); $this->redis->set('key', 'val'); $this->assertEquals(True, $this->redis->exists('key')); @@ -598,7 +576,7 @@ public function testGetKeys() for($i = 1; $i < 10; $i++) { $this->redis->set($pattern.$i, $i); } - $this->redis->delete($pattern.'3'); + $this->redis->del($pattern.'3'); $keys = $this->redis->getKeys($pattern.'*'); $this->redis->set($pattern.'3', 'something'); @@ -616,32 +594,32 @@ public function testDelete() $key = 'key' . rand(); $this->redis->set($key, 'val'); $this->assertEquals('val', $this->redis->get($key)); - $this->assertEquals(1, $this->redis->delete($key)); + $this->assertEquals(1, $this->redis->del($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->delete('x', 'y', 'z')); + $this->assertEquals(3, $this->redis->del('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->delete('x', 'y', 'z')); + $this->assertEquals(0, $this->redis->del('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->delete('x', 'y', 'z')); + $this->assertEquals(1, $this->redis->del('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->delete(array('x', 'y'))); + $this->assertEquals(2, $this->redis->del(array('x', 'y'))); } @@ -664,24 +642,24 @@ public function testType() $this->assertEquals(Redis::REDIS_LIST, $this->redis->type('keyList')); // set - $this->redis->delete('keySet'); + $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->delete(123); + $this->redis->del(123); $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); $this->assertTrue(array('val0') === $this->redis->sMembers(123)); // zset - $this->redis->delete('keyZSet'); + $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->delete('keyHash'); + $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')); @@ -724,7 +702,7 @@ public function testlPop() // lpush => head - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); @@ -739,7 +717,7 @@ public function testlPop() // testing binary data - $this->redis->delete('list'); + $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'))); @@ -756,7 +734,7 @@ public function testrPop() // rpush => tail // lpush => head - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->rPush('list', 'val'); $this->redis->rPush('list', 'val2'); @@ -771,7 +749,7 @@ public function testrPop() // testing binary data - $this->redis->delete('list'); + $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'))); @@ -785,25 +763,25 @@ public function testrPop() public function testblockingPop() { // non blocking blPop, brPop - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'val1'); $this->redis->lPush('list', 'val2'); $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val2')); $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val1')); - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'val1'); $this->redis->lPush('list', 'val2'); $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val1')); $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val2')); // blocking blpop, brpop - $this->redis->delete('list'); + $this->redis->del('list'); $this->assertTrue($this->redis->blPop(array('list'), 1) === array()); $this->assertTrue($this->redis->brPop(array('list'), 1) === array()); // TODO: fix this broken test. -// $this->redis->delete('list'); +// $this->redis->del('list'); // $params = array( // 0 => array("pipe", "r"), // 1 => array("pipe", "w"), @@ -837,7 +815,7 @@ public function testblockingPop() { public function testlSize() { - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->assertEquals(1, $this->redis->lSize('list')); @@ -854,7 +832,7 @@ public function testlSize() $this->assertEquals(FALSE, $this->redis->lPop('list')); $this->assertEquals(0, $this->redis->lSize('list')); // empty returns 0 - $this->redis->delete('list'); + $this->redis->del('list'); $this->assertEquals(0, $this->redis->lSize('list')); // non-existent returns 0 $this->redis->set('list', 'actually not a list'); @@ -864,18 +842,18 @@ public function testlSize() //lInsert, lPopx, rPopx public function testlPopx() { //test lPushx/rPushx - $this->redis->delete('keyNotExists'); + $this->redis->del('keyNotExists'); $this->assertTrue($this->redis->lPushx('keyNotExists', 'value') === 0); $this->assertTrue($this->redis->rPushx('keyNotExists', 'value') === 0); - $this->redis->delete('key'); + $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->lGetRange('key', 0, -1) === array('val1', 'val0', 'val2')); //test linsert - $this->redis->delete('key'); + $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); @@ -889,7 +867,7 @@ public function testlPopx() { public function testlistTrim() { - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); @@ -931,7 +909,7 @@ public function setupSort() { $this->redis->set('person:salary_4', 3100); // set-up - $this->redis->delete('person:id'); + $this->redis->del('person:id'); foreach(array(1,2,3,4) as $id) { $this->redis->lPush('person:id', $id); } @@ -998,7 +976,7 @@ public function testSortAsc() { // sort non-alpha doesn't change all-string lists // list → [ghi, def, abc] $list = array('abc', 'def', 'ghi'); - $this->redis->delete('list'); + $this->redis->del('list'); foreach($list as $i) { $this->redis->lPush('list', $i); } @@ -1039,7 +1017,7 @@ public function testSortDesc() { // sort non-alpha doesn't change all-string lists $list = array('def', 'abc', 'ghi'); - $this->redis->delete('list'); + $this->redis->del('list'); foreach($list as $i) { $this->redis->lPush('list', $i); } @@ -1060,7 +1038,7 @@ public function testSortDesc() { // LINDEX public function testlGet() { - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); @@ -1081,7 +1059,7 @@ public function testlGet() { // lRem testing public function testlRemove() { - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'a'); $this->redis->lPush('list', 'b'); $this->redis->lPush('list', 'c'); @@ -1097,7 +1075,7 @@ public function testlRemove() { $this->assertEquals('c', $this->redis->lGET('list', 2)); $this->assertEquals('a', $this->redis->lGET('list', 3)); - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'a'); $this->redis->lPush('list', 'b'); $this->redis->lPush('list', 'c'); @@ -1127,7 +1105,7 @@ public function testlRemove() { public function testsAdd() { - $this->redis->delete('set'); + $this->redis->del('set'); $this->assertEquals(1, $this->redis->sAdd('set', 'val')); $this->assertEquals(0, $this->redis->sAdd('set', 'val')); @@ -1141,7 +1119,7 @@ public function testsAdd() } public function testsSize() { - $this->redis->delete('set'); + $this->redis->del('set'); $this->assertEquals(1, $this->redis->sAdd('set', 'val')); @@ -1154,7 +1132,7 @@ public function testsSize() public function testsRemove() { - $this->redis->delete('set'); + $this->redis->del('set'); $this->redis->sAdd('set', 'val'); $this->redis->sAdd('set', 'val2'); @@ -1170,8 +1148,8 @@ public function testsRemove() public function testsMove() { - $this->redis->delete('set0'); - $this->redis->delete('set1'); + $this->redis->del('set0'); + $this->redis->del('set1'); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); @@ -1189,7 +1167,7 @@ public function testsMove() public function testsPop() { - $this->redis->delete('set0'); + $this->redis->del('set0'); $this->assertTrue($this->redis->sPop('set0') === FALSE); $this->redis->sAdd('set0', 'val'); @@ -1206,7 +1184,7 @@ public function testsPop() } public function testsRandMember() { - $this->redis->delete('set0'); + $this->redis->del('set0'); $this->assertTrue($this->redis->sRandMember('set0') === FALSE); $this->redis->sAdd('set0', 'val'); @@ -1228,7 +1206,7 @@ public function testsRandMember() { // With and without count, while serializing // - $this->redis->delete('set0'); + $this->redis->del('set0'); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); for($i=0;$i<5;$i++) { $member = "member:$i"; @@ -1249,7 +1227,7 @@ public function testsRandMember() { public function testSRandMemberWithCount() { // Make sure the set is nuked - $this->redis->delete('set0'); + $this->redis->del('set0'); // Run with a count (positive and negative) on an empty set $ret_pos = $this->redis->sRandMember('set0', 10); @@ -1306,7 +1284,7 @@ public function testSRandMemberWithCount() { public function testsContains() { - $this->redis->delete('set'); + $this->redis->del('set'); $this->redis->sAdd('set', 'val'); @@ -1316,7 +1294,7 @@ public function testsContains() public function testsGetMembers() { - $this->redis->delete('set'); + $this->redis->del('set'); $this->redis->sAdd('set', 'val'); $this->redis->sAdd('set', 'val2'); @@ -1335,7 +1313,7 @@ public function testsGetMembers() public function testlSet() { - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); @@ -1353,10 +1331,10 @@ public function testlSet() { } public function testsInter() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->del('x'); // set of odd numbers + $this->redis->del('y'); // set of prime numbers + $this->redis->del('z'); // set of squares + $this->redis->del('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1416,10 +1394,10 @@ public function testsInter() { } public function testsInterStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->del('x'); // set of odd numbers + $this->redis->del('y'); // set of prime numbers + $this->redis->del('z'); // set of squares + $this->redis->del('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1457,25 +1435,25 @@ public function testsInterStore() { $this->assertEquals($count, 0); $this->assertEquals($count, $this->redis->sSize('k')); - $this->redis->delete('z'); + $this->redis->del('z'); $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // only z missing, expect 0. $this->assertTrue($xyz === 0); - $this->redis->delete('y'); + $this->redis->del('y'); $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // y and z missing, expect 0. $this->assertTrue($xyz === 0); - $this->redis->delete('x'); + $this->redis->del('x'); $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // x y and z ALL missing, expect 0. $this->assertTrue($xyz === 0); } public function testsUnion() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->del('x'); // set of odd numbers + $this->redis->del('y'); // set of prime numbers + $this->redis->del('z'); // set of squares + $this->redis->del('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1523,10 +1501,10 @@ public function testsUnion() { } public function testsUnionStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->del('x'); // set of odd numbers + $this->redis->del('y'); // set of prime numbers + $this->redis->del('z'); // set of squares + $this->redis->del('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1580,24 +1558,24 @@ public function testsUnionStore() { $this->assertTrue($this->redis->sContains('k', $i)); } - $this->redis->delete('x'); // x missing now + $this->redis->del('x'); // x missing now $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z $this->assertTrue($count === count(array_unique(array_merge($y, $z)))); - $this->redis->delete('y'); // x and y missing + $this->redis->del('y'); // x and y missing $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z $this->assertTrue($count === count(array_unique($z))); - $this->redis->delete('z'); // x, y, and z ALL missing + $this->redis->del('z'); // x, y, and z ALL missing $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z $this->assertTrue($count === 0); } public function testsDiff() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->del('x'); // set of odd numbers + $this->redis->del('y'); // set of prime numbers + $this->redis->del('z'); // set of squares + $this->redis->del('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1645,10 +1623,10 @@ public function testsDiff() { } public function testsDiffStore() { - $this->redis->delete('x'); // set of odd numbers - $this->redis->delete('y'); // set of prime numbers - $this->redis->delete('z'); // set of squares - $this->redis->delete('t'); // set of numbers of the form n^2 - 1 + $this->redis->del('x'); // set of odd numbers + $this->redis->del('y'); // set of prime numbers + $this->redis->del('z'); // set of squares + $this->redis->del('t'); // set of numbers of the form n^2 - 1 $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { @@ -1702,22 +1680,22 @@ public function testsDiffStore() { $this->assertTrue($this->redis->sContains('k', $i)); } - $this->redis->delete('x'); // x missing now + $this->redis->del('x'); // x missing now $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z $this->assertTrue($count === 0); - $this->redis->delete('y'); // x and y missing + $this->redis->del('y'); // x and y missing $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z $this->assertTrue($count === 0); - $this->redis->delete('z'); // x, y, and z ALL missing + $this->redis->del('z'); // x, y, and z ALL missing $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z $this->assertTrue($count === 0); } public function testlGetRange() { - $this->redis->delete('list'); + $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); @@ -1735,7 +1713,7 @@ public function testlGetRange() { $this->assertEquals($this->redis->lGetRange('list', 0, -2), array('val3', 'val2')); $this->assertEquals($this->redis->lGetRange('list', -2, -1), array('val2', 'val')); - $this->redis->delete('list'); + $this->redis->del('list'); $this->assertEquals($this->redis->lGetRange('list', 0, -1), array()); } @@ -1806,7 +1784,7 @@ public function testPersist() { $this->assertTrue(TRUE === $this->redis->persist('x')); // true if there is a timeout $this->assertTrue(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout - $this->redis->delete('x'); + $this->redis->del('x'); $this->assertTrue(FALSE === $this->redis->persist('x')); // false if the key doesn’t exist. } @@ -1930,12 +1908,12 @@ public function testSelect() { } public function testMset() { - $this->redis->delete('x', 'y', 'z'); // remove x y z + $this->redis->del('x', 'y', 'z'); // remove x y z $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z - $this->redis->delete('x'); // delete just x + $this->redis->del('x'); // delete just x $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z @@ -1948,27 +1926,27 @@ public function testMset() { // No prefix $set_array = Array(-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three'); - $this->redis->delete(array_keys($set_array)); + $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->delete(array_keys($set_array)); + $this->redis->del(array_keys($set_array)); // With a prefix $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:'); - $this->redis->delete(array_keys($set_array)); + $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->delete(array_keys($set_array)); + $this->redis->del(array_keys($set_array)); $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testMsetNX() { - $this->redis->delete('x', 'y', 'z'); // remove x y z + $this->redis->del('x', 'y', 'z'); // remove x y z $this->assertTrue(TRUE === $this->redis->msetnx(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z - $this->redis->delete('x'); // delete just x + $this->redis->del('x'); // delete just x $this->assertTrue(FALSE === $this->redis->msetnx(array('x' => 'A', 'y' => 'B', 'z' => 'C'))); // set x y z $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array(FALSE, 'b', 'c')); // check x y z @@ -1978,7 +1956,7 @@ public function testMsetNX() { public function testRpopLpush() { // standard case. - $this->redis->delete('x', 'y'); + $this->redis->del('x', 'y'); $this->redis->lpush('x', 'abc'); $this->redis->lpush('x', 'def'); // x = [def, abc] @@ -1990,7 +1968,7 @@ public function testRpopLpush() { $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. // with an empty source, expecting no change. - $this->redis->delete('x', 'y'); + $this->redis->del('x', 'y'); $this->assertTrue(FALSE === $this->redis->rpoplpush('x', 'y')); $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); @@ -2000,7 +1978,7 @@ public function testRpopLpush() { public function testBRpopLpush() { // standard case. - $this->redis->delete('x', 'y'); + $this->redis->del('x', 'y'); $this->redis->lpush('x', 'abc'); $this->redis->lpush('x', 'def'); // x = [def, abc] @@ -2012,7 +1990,7 @@ public function testBRpopLpush() { $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. // with an empty source, expecting no change. - $this->redis->delete('x', 'y'); + $this->redis->del('x', 'y'); $this->assertTrue(FALSE === $this->redis->brpoplpush('x', 'y', 1)); $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); @@ -2021,7 +1999,7 @@ public function testBRpopLpush() { public function testZAddFirstArg() { - $this->redis->delete('key'); + $this->redis->del('key'); $zsetName = 100; // not a string! $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); @@ -2032,7 +2010,7 @@ public function testZAddFirstArg() { public function testZX() { - $this->redis->delete('key'); + $this->redis->del('key'); $this->assertTrue(array() === $this->redis->zRange('key', 0, -1)); $this->assertTrue(array() === $this->redis->zRange('key', 0, -1, true)); @@ -2098,7 +2076,7 @@ public function testZX() { $this->assertFalse($this->redis->zScore(3, 2)); // with () and +inf, -inf - $this->redis->delete('zset'); + $this->redis->del('zset'); $this->redis->zAdd('zset', 1, 'foo'); $this->redis->zAdd('zset', 2, 'bar'); $this->redis->zAdd('zset', 3, 'biz'); @@ -2115,17 +2093,17 @@ public function testZX() { // zincrby - $this->redis->delete('key'); + $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')); //zUnion - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); - $this->redis->delete('keyU'); + $this->redis->del('key1'); + $this->redis->del('key2'); + $this->redis->del('key3'); + $this->redis->del('keyU'); $this->redis->zAdd('key1', 0, 'val0'); $this->redis->zAdd('key1', 1, 'val1'); @@ -2140,16 +2118,16 @@ public function testZX() { $this->assertTrue(array('val0', 'val1', 'val4', 'val5') === $this->redis->zRange('keyU', 0, -1)); // Union on non existing keys - $this->redis->delete('keyU'); + $this->redis->del('keyU'); $this->assertTrue(0 === $this->redis->zUnion('keyU', array('X', 'Y'))); $this->assertTrue(array() === $this->redis->zRange('keyU', 0, -1)); // !Exist U Exist → copy of existing zset. - $this->redis->delete('keyU', 'X'); + $this->redis->del('keyU', 'X'); $this->assertTrue(2 === $this->redis->zUnion('keyU', array('key1', 'X'))); // test weighted zUnion - $this->redis->delete('keyZ'); + $this->redis->del('keyZ'); $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(1, 1))); $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('keyZ', 0, -1)); @@ -2157,21 +2135,21 @@ public function testZX() { $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(5, 1))); $this->assertTrue(array('val0', 'val2', 'val3', 'val1') === $this->redis->zRange('keyZ', 0, -1)); - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); + $this->redis->del('key1'); + $this->redis->del('key2'); + $this->redis->del('key3'); //test zUnion with weights and aggegration function $this->redis->zadd('key1', 1, 'duplicate'); $this->redis->zadd('key2', 2, 'duplicate'); $this->redis->zUnion('keyU', array('key1','key2'), array(1,1), 'MIN'); $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); - $this->redis->delete('keyU'); + $this->redis->del('keyU'); //now test zUnion *without* weights but with aggregrate function $this->redis->zUnion('keyU', array('key1','key2'), null, 'MIN'); $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); - $this->redis->delete('keyU', 'key1', 'key2'); + $this->redis->del('keyU', 'key1', 'key2'); @@ -2186,9 +2164,9 @@ public function testZX() { $this->assertTrue($this->redis->zunion('key3', array('key1', 'key2'), array(2, 3.0)) === 3); - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); + $this->redis->del('key1'); + $this->redis->del('key2'); + $this->redis->del('key3'); // Test 'inf', '-inf', and '+inf' weights (GitHub issue #336) $this->redis->zadd('key1', 1, 'one', 2, 'two', 3, 'three'); @@ -2228,7 +2206,7 @@ public function testZX() { $this->assertTrue(is_float($ret['two'] + 0)); $this->assertTrue(is_float($ret['three'] + 0)); - $this->redis->delete('key1'); + $this->redis->del('key1'); // ZREMRANGEBYRANK $this->redis->zAdd('key1', 1, 'one'); @@ -2237,7 +2215,7 @@ public function testZX() { $this->assertTrue(2 === $this->redis->zremrangebyrank('key1', 0, 1)); $this->assertTrue(array('three' => 3) == $this->redis->zRange('key1', 0, -1, TRUE)); - $this->redis->delete('key1'); + $this->redis->del('key1'); // zInter @@ -2251,7 +2229,7 @@ public function testZX() { $this->redis->zAdd('key3', 4, 'val3'); $this->redis->zAdd('key3', 5, 'val5'); - $this->redis->delete('keyI'); + $this->redis->del('keyI'); $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'))); $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); @@ -2265,9 +2243,9 @@ public function testZX() { // test weighted zInter - $this->redis->delete('key1'); - $this->redis->delete('key2'); - $this->redis->delete('key3'); + $this->redis->del('key1'); + $this->redis->del('key2'); + $this->redis->del('key3'); $this->redis->zAdd('key1', 0, 'val0'); $this->redis->zAdd('key1', 1, 'val1'); @@ -2280,23 +2258,23 @@ public function testZX() { $this->redis->zAdd('key3', 7, 'val1'); $this->redis->zAdd('key3', 3, 'val3'); - $this->redis->delete('keyI'); + $this->redis->del('keyI'); $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'), array(1, 1))); $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - $this->redis->delete('keyI'); + $this->redis->del('keyI'); $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'min')); $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - $this->redis->delete('keyI'); + $this->redis->del('keyI'); $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'max')); $this->assertTrue(array('val3', 'val1') === $this->redis->zRange('keyI', 0, -1)); - $this->redis->delete('keyI'); + $this->redis->del('keyI'); $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), null, 'max')); $this->assertTrue($this->redis->zScore('keyI', 'val1') === floatval(7)); // zrank, zrevrank - $this->redis->delete('z'); + $this->redis->del('z'); $this->redis->zadd('z', 1, 'one'); $this->redis->zadd('z', 2, 'two'); $this->redis->zadd('z', 5, 'five'); @@ -2312,7 +2290,7 @@ public function testZX() { } public function testHashes() { - $this->redis->delete('h', 'key'); + $this->redis->del('h', 'key'); $this->assertTrue(0 === $this->redis->hLen('h')); $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); @@ -2334,13 +2312,13 @@ public function testHashes() { $this->assertTrue(1 === $this->redis->hDel('h', 'a')); // 1 on success $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure - $this->redis->delete('h'); + $this->redis->del('h'); $this->redis->hSet('h', 'x', 'a'); $this->redis->hSet('h', 'y', 'b'); $this->assertTrue(2 === $this->redis->hDel('h', 'x', 'y')); // variadic // hsetnx - $this->redis->delete('h'); + $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', '?')); @@ -2364,11 +2342,11 @@ public function testHashes() { $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->delete('h'); + $this->redis->del('h'); $this->assertTrue(FALSE === $this->redis->hExists('h', 'x')); // hIncrBy - $this->redis->delete('h'); + $this->redis->del('h'); $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); @@ -2379,7 +2357,7 @@ public function testHashes() { if (version_compare($this->version, "2.5.0", "ge")) { // hIncrByFloat - $this->redis->delete('h'); + $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)); @@ -2389,7 +2367,7 @@ public function testHashes() { } // hmset - $this->redis->delete('h'); + $this->redis->del('h'); $this->assertTrue(TRUE === $this->redis->hMset('h', array('x' => 123, 'y' => 456, 'z' => 'abc'))); $this->assertTrue('123' === $this->redis->hGet('h', 'x')); $this->assertTrue('456' === $this->redis->hGet('h', 'y')); @@ -2418,7 +2396,7 @@ public function testHashes() { $this->assertTrue(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); // check non-string types. - $this->redis->delete('h1'); + $this->redis->del('h1'); $this->assertTrue(TRUE === $this->redis->hMSet('h1', array('x' => 0, 'y' => array(), 'z' => new stdclass(), 't' => NULL))); $h1 = $this->redis->hGetAll('h1'); $this->assertTrue('0' === $h1['x']); @@ -2430,7 +2408,7 @@ public function testHashes() { public function testSetRange() { - $this->redis->delete('key'); + $this->redis->del('key'); $this->redis->set('key', 'hello world'); $this->redis->setRange('key', 6, 'redis'); $this->assertTrue('hello redis' === $this->redis->get('key')); @@ -2442,7 +2420,7 @@ public function testSetRange() { // $this->assertTrue('hello redis' === $this->redis->get('key')); // fill with zeros if needed - $this->redis->delete('key'); + $this->redis->del('key'); $this->redis->setRange('key', 6, 'foo'); $this->assertTrue("\x00\x00\x00\x00\x00\x00foo" === $this->redis->get('key')); } @@ -2576,7 +2554,7 @@ protected function sequence($mode) { $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer $ret = $this->redis->multi($mode) - ->delete('key1') + ->del('key1') ->set('key1', 'value1') ->get('key1') ->getSet('key1', 'value2') @@ -2586,10 +2564,10 @@ protected function sequence($mode) { ->get('key2') ->decr('key2') ->get('key2') - ->renameKey('key2', 'key3') + ->rename('key2', 'key3') ->get('key3') ->renameNx('key3', 'key1') - ->renameKey('key3', 'key2') + ->rename('key3', 'key2') ->incrby('key2', 5) ->get('key2') ->decrby('key2', 5) @@ -2621,8 +2599,8 @@ protected function sequence($mode) { $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $ret = $this->redis->multi($mode) - ->delete('key1') - ->delete('key2') + ->del('key1') + ->del('key2') ->set('key1', 'val1') ->setnx('key1', 'valX') ->setnx('key2', 'valX') @@ -2649,7 +2627,7 @@ protected function sequence($mode) { $this->assertTrue($ret == array()); // ttl, mget, mset, msetnx, expire, expireAt - $this->redis->delete('key'); + $this->redis->del('key'); $ret = $this->redis->multi($mode) ->ttl('key') ->mget(array('key1', 'key2', 'key3')) @@ -2674,7 +2652,7 @@ protected function sequence($mode) { $ret = $this->redis->multi($mode) ->set('lkey', 'x') ->set('lDest', 'y') - ->delete('lkey', 'lDest') + ->del('lkey', 'lDest') ->rpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') @@ -2720,7 +2698,7 @@ protected function sequence($mode) { $ret = $this->redis->multi($mode) - ->delete('lkey', 'lDest') + ->del('lkey', 'lDest') ->rpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') @@ -2764,7 +2742,7 @@ protected function sequence($mode) { $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer $ret = $this->redis->multi($mode) - ->delete('key1') + ->del('key1') ->set('key1', 'value1') ->get('key1') ->getSet('key1', 'value2') @@ -2774,10 +2752,10 @@ protected function sequence($mode) { ->get('key2') ->decr('key2') ->get('key2') - ->renameKey('key2', 'key3') + ->rename('key2', 'key3') ->get('key3') ->renameNx('key3', 'key1') - ->renameKey('key3', 'key2') + ->rename('key3', 'key2') ->incrby('key2', 5) ->get('key2') ->decrby('key2', 5) @@ -2807,8 +2785,8 @@ protected function sequence($mode) { $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $ret = $this->redis->multi($mode) - ->delete('key1') - ->delete('key2') + ->del('key1') + ->del('key2') ->set('key1', 'val1') ->setnx('key1', 'valX') ->setnx('key2', 'valX') @@ -2858,7 +2836,7 @@ protected function sequence($mode) { // lists $ret = $this->redis->multi($mode) - ->delete('lkey', 'lDest') + ->del('lkey', 'lDest') ->rpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') @@ -2904,7 +2882,7 @@ protected function sequence($mode) { // sets $ret = $this->redis->multi($mode) - ->delete('skey1', 'skey2', 'skeydest', 'skeyUnion', 'sDiffDest') + ->del('skey1', 'skey2', 'skeydest', 'skeyUnion', 'sDiffDest') ->sadd('skey1', 'sValue1') ->sadd('skey1', 'sValue2') ->sadd('skey1', 'sValue3') @@ -2985,7 +2963,7 @@ protected function sequence($mode) { // sorted sets $ret = $this->redis->multi($mode) - ->delete('zkey1', 'zkey2', 'zkey5', 'zInter', 'zUnion') + ->del('zkey1', 'zkey2', 'zkey5', 'zInter', 'zUnion') ->zadd('zkey1', 1, 'zValue1') ->zadd('zkey1', 5, 'zValue5') ->zadd('zkey1', 2, 'zValue2') @@ -3054,7 +3032,7 @@ protected function sequence($mode) { // hash $ret = $this->redis->multi($mode) - ->delete('hkey1') + ->del('hkey1') ->hset('hkey1', 'key1', 'value1') ->hset('hkey1', 'key2', 'value2') ->hset('hkey1', 'key3', 'value3') @@ -3093,7 +3071,7 @@ protected function sequence($mode) { $this->assertTrue(count($ret) === $i); $ret = $this->redis->multi($mode) // default to MULTI, not PIPELINE. - ->delete('test') + ->del('test') ->set('test', 'xyz') ->get('test') ->exec(); @@ -3124,7 +3102,7 @@ protected function differentType($mode) { // string $key = 'string'; $ret = $this->redis->multi($mode) - ->delete($key) + ->del($key) ->set($key, 'value') // lists I/F @@ -3245,7 +3223,7 @@ protected function differentType($mode) { // list $key = 'list'; $ret = $this->redis->multi($mode) - ->delete($key) + ->del($key) ->lpush($key, 'lvalue') // string I/F @@ -3361,7 +3339,7 @@ protected function differentType($mode) { // set $key = 'set'; $ret = $this->redis->multi($mode) - ->delete($key) + ->del($key) ->sAdd($key, 'sValue') // string I/F @@ -3479,7 +3457,7 @@ protected function differentType($mode) { // sorted set $key = 'sortedset'; $ret = $this->redis->multi($mode) - ->delete($key) + ->del($key) ->zAdd($key, 0, 'zValue') // string I/F @@ -3593,7 +3571,7 @@ protected function differentType($mode) { // hash $key = 'hash'; $ret = $this->redis->multi($mode) - ->delete($key) + ->del($key) ->hset($key, 'key1', 'hValue') // string I/F @@ -4027,7 +4005,7 @@ public function testSerializerIGBinary() { private function checkSerializer($mode) { - $this->redis->delete('key'); + $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 @@ -4035,7 +4013,7 @@ private function checkSerializer($mode) { // lPush, rPush $a = array('hello world', 42, TRUE, array('' => 1729)); - $this->redis->delete('key'); + $this->redis->del('key'); $this->redis->lPush('key', $a[0]); $this->redis->rPush('key', $a[1]); $this->redis->rPush('key', $a[2]); @@ -4067,7 +4045,7 @@ private function checkSerializer($mode) { $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); // sAdd - $this->redis->delete('key'); + $this->redis->del('key'); $s = array(1,'a', array(1,2,3), array('k' => 'v')); $this->assertTrue(1 === $this->redis->sAdd('key', $s[0])); @@ -4076,7 +4054,7 @@ private function checkSerializer($mode) { $this->assertTrue(1 === $this->redis->sAdd('key', $s[3])); // variadic sAdd - $this->redis->delete('k'); + $this->redis->del('k'); $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); @@ -4084,7 +4062,7 @@ private function checkSerializer($mode) { $this->assertTrue(1 === $this->redis->sRemove('key', $s[3])); $this->assertTrue(0 === $this->redis->sRemove('key', $s[3])); // variadic - $this->redis->delete('k'); + $this->redis->del('k'); $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); @@ -4098,7 +4076,7 @@ private function checkSerializer($mode) { unset($s[3]); // sMove - $this->redis->delete('tmp'); + $this->redis->del('tmp'); $this->redis->sMove('key', 'tmp', $s[0]); $this->assertTrue(FALSE === $this->redis->sContains('key', $s[0])); $this->assertTrue(TRUE === $this->redis->sContains('tmp', $s[0])); @@ -4107,7 +4085,7 @@ private function checkSerializer($mode) { // sorted sets $z = array('z0', array('k' => 'v'), FALSE, NULL); - $this->redis->delete('key'); + $this->redis->del('key'); // zAdd $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0])); @@ -4124,7 +4102,7 @@ private function checkSerializer($mode) { $this->assertTrue(FALSE === @$this->redis->zDelete('key')); // variadic - $this->redis->delete('k'); + $this->redis->del('k'); $this->redis->zAdd('k', 0, 'a'); $this->redis->zAdd('k', 1, 'b'); $this->redis->zAdd('k', 2, 'c'); @@ -4170,7 +4148,7 @@ private function checkSerializer($mode) { $a = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); // hSet - $this->redis->delete('key'); + $this->redis->del('key'); foreach($a as $k => $v) { $this->assertTrue(1 === $this->redis->hSet('key', $k, $v)); } @@ -4189,7 +4167,7 @@ private function checkSerializer($mode) { $this->assertTrue(TRUE === $this->redis->hExists('key', 'k4')); // hMSet - $this->redis->delete('key'); + $this->redis->del('key'); $this->redis->hMSet('key', $a); foreach($a as $k => $v) { $this->assertTrue($v === $this->redis->hGet('key', $k)); @@ -4581,7 +4559,7 @@ public function testReconnectSelect() { // Make sure the default DB doesn't have the key. $this->redis->select(0); - $this->redis->delete($key); + $this->redis->del($key); // Set the key on a different DB. $this->redis->select(5); diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 3b43296807..57702994dc 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -24,9 +24,11 @@ echo "Note: these tests might take up to a minute. Don't worry :-)\n"; /* Depending on the classes being tested, run our tests on it */ -if ($str_class = 'redis') { +if ($str_class == 'redis') { + echo "Testing Redis "; exit(TestSuite::run("Redis_Test", $str_filter)); } else if ($str_class == 'redisarray') { + echo "Testing RedisArray "; global $useIndex; foreach(array(true, false) as $useIndex) { echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; @@ -38,6 +40,7 @@ run_tests('Redis_Distributor_Test'); } } else { - + echo "Testing RedisCluster "; + exit(TestSuite::run("Redis_Cluster_Test", $str_filter)); } ?> From 968ba49fa719a40b35dccc3f869f77ee77408f84 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 15:41:50 -0800 Subject: [PATCH 0433/1986] More unit test integration with cluster --- tests/RedisClusterTest.php | 6 +- tests/RedisTest.php | 305 ++++++++++++++++++------------------- 2 files changed, 153 insertions(+), 158 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index e3a8e1a768..6ebb8528bc 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -11,14 +11,16 @@ class Redis_Cluster_Test extends Redis_Test { /* Load our seeds on construction */ public function __construct() { - if (!file_exists('nodes/nodemap')) { + $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; + + if (!file_exists($str_nodemap_file)) { fprintf(STDERR, "Error: Can't find nodemap file for seeds!\n"); exit(1); } /* Store our node map */ $this->_arr_node_map = array_filter( - explode("\n", file_get_contents('nodes/nodemap') + explode("\n", file_get_contents($str_nodemap_file) )); } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 76b87df36c..05072488c1 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -407,24 +407,23 @@ public function testMultipleBin() { $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->redis->set('k1', gzcompress('v1')); + $this->redis->set('k2', gzcompress('v2')); + $this->redis->set('k3', gzcompress('v3')); - $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); - $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->getMultiple(array('k1', 'k2', 'k3'))); + $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->mget(array('k1', 'k2', 'k3'))); + $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->mget(array('k1', 'k2', 'k3'))); } public function testSetTimeout() { - - $this->redis->del('key'); + $this->redis->del('key'); $this->redis->set('key', 'value'); - $this->assertEquals('value', $this->redis->get('key')); - $this->redis->setTimeout('key', 1); - $this->assertEquals('value', $this->redis->get('key')); - sleep(2); - $this->assertEquals(False, $this->redis->get('key')); + $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')); } public function testExpireAt() { @@ -571,22 +570,21 @@ public function testExists() public function testGetKeys() { - $pattern = 'getKeys-test-'; - for($i = 1; $i < 10; $i++) { - $this->redis->set($pattern.$i, $i); + for($i = 1; $i < 10; $i++) { + $this->redis->set($pattern.$i, $i); } $this->redis->del($pattern.'3'); - $keys = $this->redis->getKeys($pattern.'*'); + $keys = $this->redis->keys($pattern.'*'); - $this->redis->set($pattern.'3', 'something'); + $this->redis->set($pattern.'3', 'something'); - $keys2 = $this->redis->getKeys($pattern.'*'); + $keys2 = $this->redis->keys($pattern.'*'); $this->assertEquals((count($keys) + 1), count($keys2)); - // empty array when no key matches - $this->assertEquals(array(), $this->redis->getKeys(rand().rand().rand().'*')); + // empty array when no key matches + $this->assertEquals(array(), $this->redis->keys(rand().rand().rand().'*')); } public function testDelete() @@ -812,31 +810,30 @@ public function testblockingPop() { } - public function testlSize() + public function testllen() { - $this->redis->del('list'); $this->redis->lPush('list', 'val'); - $this->assertEquals(1, $this->redis->lSize('list')); + $this->assertEquals(1, $this->redis->llen('list')); $this->redis->lPush('list', 'val2'); - $this->assertEquals(2, $this->redis->lSize('list')); + $this->assertEquals(2, $this->redis->llen('list')); - $this->assertEquals('val2', $this->redis->lPop('list')); - $this->assertEquals(1, $this->redis->lSize('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->lSize('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->lSize('list')); // empty returns 0 + $this->assertEquals(0, $this->redis->llen('list')); // empty returns 0 $this->redis->del('list'); - $this->assertEquals(0, $this->redis->lSize('list')); // non-existent returns 0 + $this->assertEquals(0, $this->redis->llen('list')); // non-existent returns 0 $this->redis->set('list', 'actually not a list'); - $this->assertEquals(FALSE, $this->redis->lSize('list'));// not a list returns FALSE + $this->assertEquals(FALSE, $this->redis->llen('list'));// not a list returns FALSE } //lInsert, lPopx, rPopx @@ -850,7 +847,7 @@ public function testlPopx() { $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->lGetRange('key', 0, -1) === array('val1', 'val0', 'val2')); + $this->assertTrue($this->redis->lrange('key', 0, -1) === array('val1', 'val0', 'val2')); //test linsert $this->redis->del('key'); @@ -860,11 +857,11 @@ public function testlPopx() { $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->lGetRange('key', 0, -1) === array('val2', 'val0', 'val1')); + $this->assertTrue($this->redis->lrange('key', 0, -1) === array('val2', 'val0', 'val1')); } // ltrim, lsize, lpop - public function testlistTrim() + public function testltrim() { $this->redis->del('list'); @@ -874,50 +871,49 @@ public function testlistTrim() $this->redis->lPush('list', 'val3'); $this->redis->lPush('list', 'val4'); - $this->assertEquals(TRUE, $this->redis->listTrim('list', 0, 2)); - $this->assertEquals(3, $this->redis->lSize('list')); + $this->assertEquals(TRUE, $this->redis->ltrim('list', 0, 2)); + $this->assertEquals(3, $this->redis->llen('list')); - $this->redis->listTrim('list', 0, 0); - $this->assertEquals(1, $this->redis->lSize('list')); + $this->redis->ltrim('list', 0, 0); + $this->assertEquals(1, $this->redis->llen('list')); $this->assertEquals('val4', $this->redis->lPop('list')); - $this->assertEquals(TRUE, $this->redis->listTrim('list', 10, 10000)); - $this->assertEquals(TRUE, $this->redis->listTrim('list', 10000, 10)); + $this->assertEquals(TRUE, $this->redis->ltrim('list', 10, 10000)); + $this->assertEquals(TRUE, $this->redis->ltrim('list', 10000, 10)); // test invalid type $this->redis->set('list', 'not a list...'); - $this->assertEquals(FALSE, $this->redis->listTrim('list', 0, 2)); + $this->assertEquals(FALSE, $this->redis->ltrim('list', 0, 2)); } 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(array(1,2,3,4) as $id) { - $this->redis->lPush('person:id', $id); - } - + // 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(array(1,2,3,4) as $id) { + $this->redis->lPush('person:id', $id); + } } public function testSortPrefix() { - // Make sure that sorting works with a prefix + // 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); @@ -925,75 +921,72 @@ public function testSortPrefix() { $this->redis->sadd('some-item', 3); $this->assertEquals(array('1','2','3'), $this->redis->sortAsc('some-item')); - $this->assertEquals(array('3','2','1'), $this->redis->sortDesc('some-item')); - $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item')); + $this->assertEquals(array('3','2','1'), $this->redis->sortDesc('some-item')); + $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item')); - // Kill our set/prefix - $this->redis->del('some-item'); - $this->redis->setOption(Redis::OPT_PREFIX, ''); + // Kill our set/prefix + $this->redis->del('some-item'); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testSortAsc() { + $this->setupSort(); + $this->assertTrue(FALSE === $this->redis->sortAsc(NULL)); + + // sort by age and get IDs + $byAgeAsc = array('3','1','2','4'); + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*')); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'sort' => 'asc'))); + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL)); // check that NULL works. + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. + $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sort('person:id', array('sort' => 'asc'))); + + // sort by age and get names + $byAgeAsc = array('Carol','Alice','Bob','Dave'); + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*')); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'))); + + $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 2)); + $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 2), 'sort' => 'asc'))); + + $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); + $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(1, 2), 'sort' => 'asc'))); + $ this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. + $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 4)))); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, "4")))); // with strings + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array("0", 4)))); + $this->assertEquals(array(), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. + + // sort by salary and get ages + $agesBySalaryAsc = array('34', '27', '25', '41'); + $this->assertEquals($agesBySalaryAsc, $this->redis->sortAsc('person:id', 'person:salary_*', 'person:age_*')); + $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'))); + + $agesAndSalaries = $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => array('person:age_*', 'person:salary_*'), 'sort' => 'asc')); + $this->assertEquals(array('34', '2000', '27', '2500', '25', '2800', '41', '3100'), $agesAndSalaries); + + // sort non-alpha doesn't change all-string lists + // list → [ghi, def, abc] + $list = array('abc', 'def', 'ghi'); + $this->redis->del('list'); + foreach($list as $i) { + $this->redis->lPush('list', $i); + } - $this->setupSort(); - - $this->assertTrue(FALSE === $this->redis->sortAsc(NULL)); - - // sort by age and get IDs - $byAgeAsc = array('3','1','2','4'); - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*')); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'sort' => 'asc'))); - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL)); // check that NULL works. - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sort('person:id', array('sort' => 'asc'))); - - // sort by age and get names - $byAgeAsc = array('Carol','Alice','Bob','Dave'); - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*')); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'))); - - $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 2)); - $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 2), 'sort' => 'asc'))); - - $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); - $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(1, 2), 'sort' => 'asc'))); - $this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 4)))); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, "4")))); // with strings - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array("0", 4)))); - $this->assertEquals(array(), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. - - // sort by salary and get ages - $agesBySalaryAsc = array('34', '27', '25', '41'); - $this->assertEquals($agesBySalaryAsc, $this->redis->sortAsc('person:id', 'person:salary_*', 'person:age_*')); - $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'))); - - $agesAndSalaries = $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => array('person:age_*', 'person:salary_*'), 'sort' => 'asc')); - $this->assertEquals(array('34', '2000', '27', '2500', '25', '2800', '41', '3100'), $agesAndSalaries); - - - // sort non-alpha doesn't change all-string lists - // list → [ghi, def, abc] - $list = array('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", "lt")) { - $this->assertEquals(array_reverse($list), $this->redis->sortAsc('list')); - $this->assertEquals(array_reverse($list), $this->redis->sort('list', array('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 → [ghi, def, abc] + if (version_compare($this->version, "2.5.0", "lt")) { + $this->assertEquals(array_reverse($list), $this->redis->sortAsc('list')); + $this->assertEquals(array_reverse($list), $this->redis->sort('list', array('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->sortAscAlpha('list')); - $this->assertEquals($list, $this->redis->sort('list', array('sort' => 'asc', 'alpha' => TRUE))); + // SORT list ALPHA → [abc, def, ghi] + $this->assertEquals($list, $this->redis->sortAscAlpha('list')); + $this->assertEquals($list, $this->redis->sort('list', array('sort' => 'asc', 'alpha' => TRUE))); } public function testSortDesc() { @@ -1693,7 +1686,7 @@ public function testsDiffStore() { $this->assertTrue($count === 0); } - public function testlGetRange() { + public function testlrange() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); @@ -1704,17 +1697,17 @@ public function testlGetRange() { // pos : -3 -2 -1 // list: [val3, val2, val] - $this->assertEquals($this->redis->lGetRange('list', 0, 0), array('val3')); - $this->assertEquals($this->redis->lGetRange('list', 0, 1), array('val3', 'val2')); - $this->assertEquals($this->redis->lGetRange('list', 0, 2), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lGetRange('list', 0, 3), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, 0), array('val3')); + $this->assertEquals($this->redis->lrange('list', 0, 1), array('val3', 'val2')); + $this->assertEquals($this->redis->lrange('list', 0, 2), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, 3), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lGetRange('list', 0, -1), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lGetRange('list', 0, -2), array('val3', 'val2')); - $this->assertEquals($this->redis->lGetRange('list', -2, -1), array('val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, -1), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, -2), array('val3', 'val2')); + $this->assertEquals($this->redis->lrange('list', -2, -1), array('val2', 'val')); $this->redis->del('list'); - $this->assertEquals($this->redis->lGetRange('list', 0, -1), array()); + $this->assertEquals($this->redis->lrange('list', 0, -1), array()); } @@ -2660,15 +2653,15 @@ protected function sequence($mode) { ->lpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) + ->lrange('lDest', 0, -1) ->lpop('lkey') ->llen('lkey') ->lRemove('lkey', 'lvalue', 3) ->llen('lkey') ->lget('lkey', 0) - ->lGetRange('lkey', 0, -1) + ->lrange('lkey', 0, -1) ->lSet('lkey', 1, "newValue") // check errors on key not exists - ->lGetRange('lkey', 0, -1) + ->lrange('lkey', 0, -1) ->llen('lkey') ->exec(); @@ -2703,7 +2696,7 @@ protected function sequence($mode) { ->lpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) + ->lrange('lDest', 0, -1) ->lpop('lkey') ->exec(); $this->assertTrue(is_array($ret)); @@ -2844,15 +2837,15 @@ protected function sequence($mode) { ->lpush('lkey', 'lvalue') ->lpush('lkey', 'lvalue') ->rpoplpush('lkey', 'lDest') - ->lGetRange('lDest', 0, -1) + ->lrange('lDest', 0, -1) ->lpop('lkey') ->llen('lkey') ->lRemove('lkey', 'lvalue', 3) ->llen('lkey') ->lget('lkey', 0) - ->lGetRange('lkey', 0, -1) + ->lrange('lkey', 0, -1) ->lSet('lkey', 1, "newValue") // check errors on missing key - ->lGetRange('lkey', 0, -1) + ->lrange('lkey', 0, -1) ->llen('lkey') ->exec(); @@ -3110,7 +3103,7 @@ protected function differentType($mode) { ->lPush($key, 'lvalue') ->lLen($key) ->lPop($key) - ->lGetRange($key, 0, -1) + ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lGet($key, 0) ->lSet($key, 0, "newValue") @@ -3358,7 +3351,7 @@ protected function differentType($mode) { ->lPush($key, 'lvalue') ->lLen($key) ->lPop($key) - ->lGetRange($key, 0, -1) + ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lGet($key, 0) ->lSet($key, 0, "newValue") @@ -3476,7 +3469,7 @@ protected function differentType($mode) { ->lPush($key, 'lvalue') ->lLen($key) ->lPop($key) - ->lGetRange($key, 0, -1) + ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lGet($key, 0) ->lSet($key, 0, "newValue") @@ -3590,7 +3583,7 @@ protected function differentType($mode) { ->lPush($key, 'lvalue') ->lLen($key) ->lPop($key) - ->lGetRange($key, 0, -1) + ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lGet($key, 0) ->lSet($key, 0, "newValue") @@ -3697,7 +3690,7 @@ public function testDifferentTypeString() { $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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); @@ -3827,7 +3820,7 @@ public function testDifferentTypeSet() { $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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); @@ -3886,7 +3879,7 @@ public function testDifferentTypeSortedSet() { $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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); @@ -3943,7 +3936,7 @@ public function testDifferentTypeHash() { $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->lGetRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); @@ -4019,8 +4012,8 @@ private function checkSerializer($mode) { $this->redis->rPush('key', $a[2]); $this->redis->rPush('key', $a[3]); - // lGetRange - $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); + // lrange + $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); // lGet $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); @@ -4030,7 +4023,7 @@ private function checkSerializer($mode) { // lRemove $this->assertTrue($this->redis->lRemove('key', $a[3]) === 1); - $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lGetRange('key', 0, -1)); + $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1)); // lSet $a[0] = array('k' => 'v'); // update @@ -4042,7 +4035,7 @@ private function checkSerializer($mode) { $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], array(4,5,6)) === 5); $a = array(array(1,2,3), $a[0], array(4,5,6), $a[1], $a[2]); - $this->assertTrue($a === $this->redis->lGetRange('key', 0, -1)); + $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); // sAdd $this->redis->del('key'); From 62055044059abb0c64203c7ed4f5cb4353ad178c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 16:26:15 -0800 Subject: [PATCH 0434/1986] Unit test formatting, change to actual Redis commands. --- tests/RedisClusterTest.php | 18 +++ tests/RedisTest.php | 266 +++++++++++++++++++------------------ 2 files changed, 152 insertions(+), 132 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 6ebb8528bc..b282947366 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -58,5 +58,23 @@ public function testEcho() { $this->assertEquals($this->redis->echo('k2', 'world'), 'world'); $this->assertEquals($this->redis->echo('k3', " 0123 "), " 0123 "); } + + public function testSortAsc() { return $this->markTestSkipped(); } + public function testSortDesc() { return $this->markTestSkipped(); } + + public function testSortPrefix() { + $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(array('1','2','3'), $this->redis->sort('some-item')); + + // Kill our set/prefix + $this->redis->del('some-item'); + $this->redis->setOption(Redis::OPT_PREFIX, ''); + } + } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 05072488c1..aaf05e587b 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -951,7 +951,7 @@ public function testSortAsc() { $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(1, 2), 'sort' => 'asc'))); - $ this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. + $this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 4)))); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, "4")))); // with strings @@ -1051,7 +1051,7 @@ public function testlGet() { } // lRem testing - public function testlRemove() { + public function testlrem() { $this->redis->del('list'); $this->redis->lPush('list', 'a'); $this->redis->lPush('list', 'b'); @@ -1060,7 +1060,7 @@ public function testlRemove() { $this->redis->lPush('list', 'b'); $this->redis->lPush('list', 'c'); // ['c', 'b', 'c', 'c', 'b', 'a'] - $return = $this->redis->lRemove('list', 'b', 2); + $return = $this->redis->lrem('list', 'b', 2); // ['c', 'c', 'c', 'a'] $this->assertEquals(2, $return); $this->assertEquals('c', $this->redis->lGET('list', 0)); @@ -1076,7 +1076,7 @@ public function testlRemove() { $this->redis->lPush('list', 'b'); $this->redis->lPush('list', 'c'); // ['c', 'b', 'c', 'c', 'b', 'a'] - $this->redis->lRemove('list', 'c', -2); + $this->redis->lrem('list', 'c', -2); // ['c', 'b', 'b', 'a'] $this->assertEquals(2, $return); $this->assertEquals('c', $this->redis->lGET('list', 0)); @@ -1085,14 +1085,14 @@ public function testlRemove() { $this->assertEquals('a', $this->redis->lGET('list', 3)); // remove each element - $this->assertEquals(1, $this->redis->lRemove('list', 'a', 0)); - $this->assertEquals(0, $this->redis->lRemove('list', 'x', 0)); - $this->assertEquals(2, $this->redis->lRemove('list', 'b', 0)); - $this->assertEquals(1, $this->redis->lRemove('list', 'c', 0)); + $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->lRemove('list', 'x')); + $this->assertEquals(FALSE, $this->redis->lrem('list', 'x')); } @@ -1103,59 +1103,59 @@ public function testsAdd() $this->assertEquals(1, $this->redis->sAdd('set', 'val')); $this->assertEquals(0, $this->redis->sAdd('set', 'val')); - $this->assertTrue($this->redis->sContains('set', 'val')); - $this->assertFalse($this->redis->sContains('set', 'val2')); + $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->sContains('set', 'val2')); + $this->assertTrue($this->redis->sismember('set', 'val2')); } - public function testsSize() + public function testscard() { $this->redis->del('set'); $this->assertEquals(1, $this->redis->sAdd('set', 'val')); - $this->assertEquals(1, $this->redis->sSize('set')); + $this->assertEquals(1, $this->redis->scard('set')); $this->assertEquals(1, $this->redis->sAdd('set', 'val2')); - $this->assertEquals(2, $this->redis->sSize('set')); + $this->assertEquals(2, $this->redis->scard('set')); } - public function testsRemove() + public function testsrem() { $this->redis->del('set'); $this->redis->sAdd('set', 'val'); $this->redis->sAdd('set', 'val2'); - $this->redis->sRemove('set', 'val'); + $this->redis->srem('set', 'val'); - $this->assertEquals(1, $this->redis->sSize('set')); + $this->assertEquals(1, $this->redis->scard('set')); - $this->redis->sRemove('set', 'val2'); + $this->redis->srem('set', 'val2'); - $this->assertEquals(0, $this->redis->sSize('set')); + $this->assertEquals(0, $this->redis->scard('set')); } public function testsMove() { - $this->redis->del('set0'); - $this->redis->del('set1'); + $this->redis->del('{set}0'); + $this->redis->del('{set}1'); - $this->redis->sAdd('set0', 'val'); - $this->redis->sAdd('set0', 'val2'); + $this->redis->sAdd('{set}0', 'val'); + $this->redis->sAdd('{set}0', 'val2'); - $this->assertTrue($this->redis->sMove('set0', 'set1', 'val')); - $this->assertFalse($this->redis->sMove('set0', 'set1', 'val')); - $this->assertFalse($this->redis->sMove('set0', 'set1', 'val-what')); + $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->sSize('set0')); - $this->assertEquals(1, $this->redis->sSize('set1')); + $this->assertEquals(1, $this->redis->scard('{set}0')); + $this->assertEquals(1, $this->redis->scard('{set}1')); - $this->assertEquals(array('val2'), $this->redis->sGetMembers('set0')); - $this->assertEquals(array('val'), $this->redis->sGetMembers('set1')); + $this->assertEquals(array('val2'), $this->redis->smembers('{set}0')); + $this->assertEquals(array('val'), $this->redis->smembers('{set}1')); } public function testsPop() @@ -1167,10 +1167,10 @@ public function testsPop() $this->redis->sAdd('set0', 'val2'); $v0 = $this->redis->sPop('set0'); - $this->assertTrue(1 === $this->redis->sSize('set0')); + $this->assertTrue(1 === $this->redis->scard('set0')); $this->assertTrue($v0 === 'val' || $v0 === 'val2'); $v1 = $this->redis->sPop('set0'); - $this->assertTrue(0 === $this->redis->sSize('set0')); + $this->assertTrue(0 === $this->redis->scard('set0')); $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); $this->assertTrue($this->redis->sPop('set0') === FALSE); @@ -1186,7 +1186,7 @@ public function testsRandMember() { $got = array(); while(true) { $v = $this->redis->sRandMember('set0'); - $this->assertTrue(2 === $this->redis->sSize('set0')); // no change. + $this->assertTrue(2 === $this->redis->scard('set0')); // no change. $this->assertTrue($v === 'val' || $v === 'val2'); $got[$v] = $v; @@ -1275,17 +1275,17 @@ public function testSRandMemberWithCount() { } } - public function testsContains() + public function testsismember() { $this->redis->del('set'); $this->redis->sAdd('set', 'val'); - $this->assertTrue($this->redis->sContains('set', 'val')); - $this->assertFalse($this->redis->sContains('set', 'val2')); + $this->assertTrue($this->redis->sismember('set', 'val')); + $this->assertFalse($this->redis->sismember('set', 'val2')); } - public function testsGetMembers() + public function testsmembers() { $this->redis->del('set'); @@ -1295,9 +1295,9 @@ public function testsGetMembers() $array = array('val', 'val2', 'val3'); - $sGetMembers = $this->redis->sGetMembers('set'); - sort($sGetMembers); - $this->assertEquals($array, $sGetMembers); + $smembers = $this->redis->smembers('set'); + sort($smembers); + $this->assertEquals($array, $smembers); $sMembers = $this->redis->sMembers('set'); sort($sMembers); @@ -1324,62 +1324,64 @@ public function testlSet() { } public function testsInter() { - $this->redis->del('x'); // set of odd numbers - $this->redis->del('y'); // set of prime numbers - $this->redis->del('z'); // set of squares - $this->redis->del('t'); // set of numbers of the form n^2 - 1 + $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 = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { - $this->redis->sAdd('x', $i); + $this->redis->sAdd('{set}odd', $i); } $y = array(1,2,3,5,7,11,13,17,19,23); foreach($y as $i) { - $this->redis->sAdd('y', $i); + $this->redis->sAdd('{set}prime', $i); } $z = array(1,4,9,16,25); foreach($z as $i) { - $this->redis->sAdd('z', $i); + $this->redis->sAdd('{set}square', $i); } $t = array(2,5,10,17,26); foreach($t as $i) { - $this->redis->sAdd('t', $i); + $this->redis->sAdd('{set}seq', $i); } - $xy = $this->redis->sInter('x', 'y'); // odd prime numbers - foreach($xy as $i) { - $i = (int)$i; + $xy = $this->redis->sInter('{set}odd', '{set}prime'); // odd prime numbers + foreach($xy as $i) { + $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); - } - $xy = $this->redis->sInter(array('x', 'y')); // odd prime numbers, as array. - foreach($xy as $i) { - $i = (int)$i; + } + + $xy = $this->redis->sInter(array('{set}odd', '{set}prime')); // odd prime numbers, as array. + foreach($xy as $i) { + $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); - } - - $yz = $this->redis->sInter('y', 'z'); // set of odd squares + } + + $yz = $this->redis->sInter('{set}prime', '{set}square'); // set of prime squares foreach($yz as $i) { - $i = (int)$i; + $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($y, $z))); } - $yz = $this->redis->sInter(array('y', 'z')); // set of odd squares, as array + + $yz = $this->redis->sInter(array('{set}prime', '{set}square')); // set of odd squares, as array foreach($yz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($y, $z))); } - - $zt = $this->redis->sInter('z', 't'); // prime squares + + $zt = $this->redis->sInter('{set}square', '{set}seq'); // prime squares $this->assertTrue($zt === array()); - $zt = $this->redis->sInter(array('z', 't')); // prime squares, as array + $zt = $this->redis->sInter(array('{set}square', '{set}seq')); // prime squares, as array $this->assertTrue($zt === array()); - - $xyz = $this->redis->sInter('x', 'y', 'z');// odd prime squares + + $xyz = $this->redis->sInter('{set}odd', '{set}prime', '{set}square');// odd prime squares $this->assertTrue($xyz === array('1')); - $xyz = $this->redis->sInter(array('x', 'y', 'z'));// odd prime squares, with an array as a parameter + $xyz = $this->redis->sInter(array('{set}odd', '{set}prime', '{set}square'));// odd prime squares, with an array as a parameter $this->assertTrue($xyz === array('1')); $nil = $this->redis->sInter(array()); @@ -1413,20 +1415,20 @@ public function testsInterStore() { } $count = $this->redis->sInterStore('k', 'x', 'y'); // odd prime numbers - $this->assertEquals($count, $this->redis->sSize('k')); + $this->assertEquals($count, $this->redis->scard('k')); foreach(array_intersect($x, $y) as $i) { - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $count = $this->redis->sInterStore('k', 'y', 'z'); // set of odd squares - $this->assertEquals($count, $this->redis->sSize('k')); + $this->assertEquals($count, $this->redis->scard('k')); foreach(array_intersect($y, $z) as $i) { - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $count = $this->redis->sInterStore('k', 'z', 't'); // squares of the form n^2 + 1 $this->assertEquals($count, 0); - $this->assertEquals($count, $this->redis->sSize('k')); + $this->assertEquals($count, $this->redis->scard('k')); $this->redis->del('z'); $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // only z missing, expect 0. @@ -1524,7 +1526,7 @@ public function testsUnionStore() { $this->assertEquals($count, count($xy)); foreach($xy as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $count = $this->redis->sUnionStore('k', 'y', 'z'); // y U z @@ -1532,7 +1534,7 @@ public function testsUnionStore() { $this->assertEquals($count, count($yz)); foreach($yz as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $count = $this->redis->sUnionStore('k', 'z', 't'); // z U t @@ -1540,7 +1542,7 @@ public function testsUnionStore() { $this->assertEquals($count, count($zt)); foreach($zt as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z @@ -1548,7 +1550,7 @@ public function testsUnionStore() { $this->assertEquals($count, count($xyz)); foreach($xyz as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $this->redis->del('x'); // x missing now @@ -1646,7 +1648,7 @@ public function testsDiffStore() { $this->assertEquals($count, count($xy)); foreach($xy as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $count = $this->redis->sDiffStore('k', 'y', 'z'); // y - z @@ -1654,7 +1656,7 @@ public function testsDiffStore() { $this->assertEquals($count, count($yz)); foreach($yz as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $count = $this->redis->sDiffStore('k', 'z', 't'); // z - t @@ -1662,7 +1664,7 @@ public function testsDiffStore() { $this->assertEquals($count, count($zt)); foreach($zt as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z @@ -1670,7 +1672,7 @@ public function testsDiffStore() { $this->assertEquals($count, count($xyz)); foreach($xyz as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sContains('k', $i)); + $this->assertTrue($this->redis->sismember('k', $i)); } $this->redis->del('x'); // x missing now @@ -2656,7 +2658,7 @@ protected function sequence($mode) { ->lrange('lDest', 0, -1) ->lpop('lkey') ->llen('lkey') - ->lRemove('lkey', 'lvalue', 3) + ->lrem('lkey', 'lvalue', 3) ->llen('lkey') ->lget('lkey', 0) ->lrange('lkey', 0, -1) @@ -2840,7 +2842,7 @@ protected function sequence($mode) { ->lrange('lDest', 0, -1) ->lpop('lkey') ->llen('lkey') - ->lRemove('lkey', 'lvalue', 3) + ->lrem('lkey', 'lvalue', 3) ->llen('lkey') ->lget('lkey', 0) ->lrange('lkey', 0, -1) @@ -2884,12 +2886,12 @@ protected function sequence($mode) { ->sadd('skey2', 'sValue1') ->sadd('skey2', 'sValue2') - ->sSize('skey1') - ->sRemove('skey1', 'sValue2') - ->sSize('skey1') + ->scard('skey1') + ->srem('skey1', 'sValue2') + ->scard('skey1') ->sMove('skey1', 'skey2', 'sValue4') - ->sSize('skey2') - ->sContains('skey2', 'sValue4') + ->scard('skey2') + ->sismember('skey2', 'sValue4') ->sMembers('skey1') ->sMembers('skey2') ->sInter('skey1', 'skey2') @@ -2902,7 +2904,7 @@ protected function sequence($mode) { ->sDiffStore('sDiffDest', 'skey1', 'skey2') ->sMembers('sDiffDest') ->sPop('skey2') - ->sSize('skey2') + ->scard('skey2') ->exec(); $i = 0; @@ -3107,18 +3109,18 @@ protected function differentType($mode) { ->lTrim($key, 0, 1) ->lGet($key, 0) ->lSet($key, 0, "newValue") - ->lRemove($key, 'lvalue', 1) + ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) ->rPoplPush($key, __FUNCTION__ . 'lkey1') // sets I/F ->sAdd($key, 'sValue1') - ->sRemove($key, 'sValue1') + ->srem($key, 'sValue1') ->sPop($key) ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') - ->sSize($key) - ->sContains($key, 'sValue1') + ->scard($key) + ->sismember($key, 'sValue1') ->sInter($key, __FUNCTION__ . 'skey2') ->sUnion($key, __FUNCTION__ . 'skey4') ->sDiff($key, __FUNCTION__ . 'skey7') @@ -3232,11 +3234,11 @@ protected function differentType($mode) { // sets I/F ->sAdd($key, 'sValue1') - ->sRemove($key, 'sValue1') + ->srem($key, 'sValue1') ->sPop($key) ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') - ->sSize($key) - ->sContains($key, 'sValue1') + ->scard($key) + ->sismember($key, 'sValue1') ->sInter($key, __FUNCTION__ . 'skey2') ->sUnion($key, __FUNCTION__ . 'skey4') ->sDiff($key, __FUNCTION__ . 'skey7') @@ -3355,7 +3357,7 @@ protected function differentType($mode) { ->lTrim($key, 0, 1) ->lGet($key, 0) ->lSet($key, 0, "newValue") - ->lRemove($key, 'lvalue', 1) + ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) ->rPoplPush($key, __FUNCTION__ . 'lkey1') @@ -3473,18 +3475,18 @@ protected function differentType($mode) { ->lTrim($key, 0, 1) ->lGet($key, 0) ->lSet($key, 0, "newValue") - ->lRemove($key, 'lvalue', 1) + ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) ->rPoplPush($key, __FUNCTION__ . 'lkey1') // sets I/F ->sAdd($key, 'sValue1') - ->sRemove($key, 'sValue1') + ->srem($key, 'sValue1') ->sPop($key) ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') - ->sSize($key) - ->sContains($key, 'sValue1') + ->scard($key) + ->sismember($key, 'sValue1') ->sInter($key, __FUNCTION__ . 'skey2') ->sUnion($key, __FUNCTION__ . 'skey4') ->sDiff($key, __FUNCTION__ . 'skey7') @@ -3587,18 +3589,18 @@ protected function differentType($mode) { ->lTrim($key, 0, 1) ->lGet($key, 0) ->lSet($key, 0, "newValue") - ->lRemove($key, 'lvalue', 1) + ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) ->rPoplPush($key, __FUNCTION__ . 'lkey1') // sets I/F ->sAdd($key, 'sValue1') - ->sRemove($key, 'sValue1') + ->srem($key, 'sValue1') ->sPop($key) ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') - ->sSize($key) - ->sContains($key, 'sValue1') + ->scard($key) + ->sismember($key, 'sValue1') ->sInter($key, __FUNCTION__ . 'skey2') ->sUnion($key, __FUNCTION__ . 'skey4') ->sDiff($key, __FUNCTION__ . 'skey7') @@ -3694,18 +3696,18 @@ public function testDifferentTypeString() { $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $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, __FUNCTION__ . 'lkey1')); // sets I/F $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sPop($key)); $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sSize($key)); - $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->scard($key)); + $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . 'skey7')); @@ -3759,11 +3761,11 @@ public function testDifferentTypeList() { // sets I/F $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sPop($key)); $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sSize($key)); - $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->scard($key)); + $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . 'skey7')); @@ -3824,7 +3826,7 @@ public function testDifferentTypeSet() { $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $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, __FUNCTION__ . 'lkey1')); @@ -3883,18 +3885,18 @@ public function testDifferentTypeSortedSet() { $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $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, __FUNCTION__ . 'lkey1')); // sets I/F $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sPop($key)); $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sSize($key)); - $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->scard($key)); + $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . 'skey7')); @@ -3940,18 +3942,18 @@ public function testDifferentTypeHash() { $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lRemove($key, 'lvalue', 1)); + $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, __FUNCTION__ . 'lkey1')); // sets I/F $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sRemove($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sPop($key)); $this->assertEquals(FALSE, $this->redis->sMove($key, __FUNCTION__ . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sSize($key)); - $this->assertEquals(FALSE, $this->redis->sContains($key, 'sValue1')); + $this->assertEquals(FALSE, $this->redis->scard($key)); + $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); $this->assertEquals(FALSE, $this->redis->sInter($key, __FUNCTION__ . 'skey2')); $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . 'skey7')); @@ -4021,8 +4023,8 @@ private function checkSerializer($mode) { $this->assertTrue($a[2] === $this->redis->lGet('key', 2)); $this->assertTrue($a[3] === $this->redis->lGet('key', 3)); - // lRemove - $this->assertTrue($this->redis->lRemove('key', $a[3]) === 1); + // lrem + $this->assertTrue($this->redis->lrem('key', $a[3]) === 1); $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1)); // lSet @@ -4051,9 +4053,9 @@ private function checkSerializer($mode) { $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); - // sRemove - $this->assertTrue(1 === $this->redis->sRemove('key', $s[3])); - $this->assertTrue(0 === $this->redis->sRemove('key', $s[3])); + // srem + $this->assertTrue(1 === $this->redis->srem('key', $s[3])); + $this->assertTrue(0 === $this->redis->srem('key', $s[3])); // variadic $this->redis->del('k'); $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); @@ -4061,18 +4063,18 @@ private function checkSerializer($mode) { $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); $this->assertTrue(FALSE === $this->redis->exists('k')); - // sContains - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[1])); - $this->assertTrue(TRUE === $this->redis->sContains('key', $s[2])); - $this->assertTrue(FALSE === $this->redis->sContains('key', $s[3])); + // sismember + $this->assertTrue(TRUE === $this->redis->sismember('key', $s[0])); + $this->assertTrue(TRUE === $this->redis->sismember('key', $s[1])); + $this->assertTrue(TRUE === $this->redis->sismember('key', $s[2])); + $this->assertTrue(FALSE === $this->redis->sismember('key', $s[3])); unset($s[3]); // sMove $this->redis->del('tmp'); $this->redis->sMove('key', 'tmp', $s[0]); - $this->assertTrue(FALSE === $this->redis->sContains('key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sContains('tmp', $s[0])); + $this->assertTrue(FALSE === $this->redis->sismember('key', $s[0])); + $this->assertTrue(TRUE === $this->redis->sismember('tmp', $s[0])); unset($s[0]); From b4a1a4e7318e2ffe158162cfeb65b479d8b23eba Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 16:31:13 -0800 Subject: [PATCH 0435/1986] Bomb out on vararg command if we have one empty array (will always fail) --- redis_commands.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis_commands.c b/redis_commands.c index 1dc0329817..d0f24800f4 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -999,6 +999,9 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(has_timeout) argc++; efree(z_args); z_args = NULL; + + /* If the array is empty, we can simply abort */ + if (argc == 0) return FAILURE; } // Begin construction of our command From 501561b25577ee77a7bd64687ca931fb5df04ba2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 17:15:44 -0800 Subject: [PATCH 0436/1986] More unit test formatting, and key renaming so tests work in cluster --- tests/RedisClusterTest.php | 9 ++ tests/RedisTest.php | 298 ++++++++++++++++--------------------- 2 files changed, 139 insertions(+), 168 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index b282947366..8e78d088d5 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -76,5 +76,14 @@ public function testSortPrefix() { $this->redis->setOption(Redis::OPT_PREFIX, ''); } + public function testDBSize() { + for ($i = 0; $i < 10; $i++) { + $str_key = "key:$i"; + $this->assertTrue($this->redis->flushdb($str_key)); + $this->redis->set($str_key, "val:$i"); + $this->assertEquals(1, $this->redis->dbsize($str_key)); + } + } + } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index aaf05e587b..0d8c94cb6a 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1389,106 +1389,105 @@ public function testsInter() { } public function testsInterStore() { - $this->redis->del('x'); // set of odd numbers - $this->redis->del('y'); // set of prime numbers - $this->redis->del('z'); // set of squares - $this->redis->del('t'); // set of numbers of the form n^2 - 1 + $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 = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { - $this->redis->sAdd('x', $i); + $this->redis->sAdd('{set}x', $i); } $y = array(1,2,3,5,7,11,13,17,19,23); foreach($y as $i) { - $this->redis->sAdd('y', $i); + $this->redis->sAdd('{set}y', $i); } $z = array(1,4,9,16,25); foreach($z as $i) { - $this->redis->sAdd('z', $i); + $this->redis->sAdd('{set}z', $i); } $t = array(2,5,10,17,26); foreach($t as $i) { - $this->redis->sAdd('t', $i); + $this->redis->sAdd('{set}t', $i); } - $count = $this->redis->sInterStore('k', 'x', 'y'); // odd prime numbers - $this->assertEquals($count, $this->redis->scard('k')); + $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('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $count = $this->redis->sInterStore('k', 'y', 'z'); // set of odd squares - $this->assertEquals($count, $this->redis->scard('k')); + $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('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $count = $this->redis->sInterStore('k', 'z', 't'); // squares of the form n^2 + 1 - $this->assertEquals($count, 0); - $this->assertEquals($count, $this->redis->scard('k')); + $count = $this->redis->sInterStore('{set}k', '{set}z', '{set}t'); // squares of the form n^2 + 1 + $this->assertEquals($count, 0); + $this->assertEquals($count, $this->redis->scard('{set}k')); - $this->redis->del('z'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // only z missing, expect 0. - $this->assertTrue($xyz === 0); + $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('y'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // y and z missing, expect 0. - $this->assertTrue($xyz === 0); - - $this->redis->del('x'); - $xyz = $this->redis->sInterStore('k', 'x', 'y', 'z'); // x y and z ALL 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('x'); // set of odd numbers - $this->redis->del('y'); // set of prime numbers - $this->redis->del('z'); // set of squares - $this->redis->del('t'); // set of numbers of the form n^2 - 1 + $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 = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { - $this->redis->sAdd('x', $i); + $this->redis->sAdd('{set}x', $i); } $y = array(1,2,3,5,7,11,13,17,19,23); foreach($y as $i) { - $this->redis->sAdd('y', $i); + $this->redis->sAdd('{set}y', $i); } $z = array(1,4,9,16,25); foreach($z as $i) { - $this->redis->sAdd('z', $i); + $this->redis->sAdd('{set}z', $i); } $t = array(2,5,10,17,26); foreach($t as $i) { - $this->redis->sAdd('t', $i); + $this->redis->sAdd('{set}t', $i); } - $xy = $this->redis->sUnion('x', 'y'); // x U y + $xy = $this->redis->sUnion('{set}x', '{set}y'); // x U y foreach($xy as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_merge($x, $y))); } - $yz = $this->redis->sUnion('y', 'z'); // y U Z + $yz = $this->redis->sUnion('{set}y', '{set}z'); // y U Z foreach($yz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_merge($y, $z))); } - $zt = $this->redis->sUnion('z', 't'); // z U t + $zt = $this->redis->sUnion('{set}z', '{set}t'); // z U t foreach($zt as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_merge($z, $t))); } - $xyz = $this->redis->sUnion('x', 'y', 'z'); // x U y U z + $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($i, array_merge($x, $y, $z))); @@ -1496,121 +1495,121 @@ public function testsUnion() { } public function testsUnionStore() { - $this->redis->del('x'); // set of odd numbers - $this->redis->del('y'); // set of prime numbers - $this->redis->del('z'); // set of squares - $this->redis->del('t'); // set of numbers of the form n^2 - 1 + $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 = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { - $this->redis->sAdd('x', $i); + $this->redis->sAdd('{set}x', $i); } $y = array(1,2,3,5,7,11,13,17,19,23); foreach($y as $i) { - $this->redis->sAdd('y', $i); + $this->redis->sAdd('{set}y', $i); } $z = array(1,4,9,16,25); foreach($z as $i) { - $this->redis->sAdd('z', $i); + $this->redis->sAdd('{set}z', $i); } $t = array(2,5,10,17,26); foreach($t as $i) { - $this->redis->sAdd('t', $i); + $this->redis->sAdd('{set}t', $i); } - $count = $this->redis->sUnionStore('k', 'x', 'y'); // x U y - $xy = array_unique(array_merge($x, $y)); - $this->assertEquals($count, count($xy)); + $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y'); // x U y + $xy = array_unique(array_merge($x, $y)); + $this->assertEquals($count, count($xy)); foreach($xy as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sismember('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $count = $this->redis->sUnionStore('k', 'y', 'z'); // y U z - $yz = array_unique(array_merge($y, $z)); - $this->assertEquals($count, count($yz)); + $count = $this->redis->sUnionStore('{set}k', '{set}y', '{set}z'); // y U z + $yz = array_unique(array_merge($y, $z)); + $this->assertEquals($count, count($yz)); foreach($yz as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sismember('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $count = $this->redis->sUnionStore('k', 'z', 't'); // z U t - $zt = array_unique(array_merge($z, $t)); - $this->assertEquals($count, count($zt)); + $count = $this->redis->sUnionStore('{set}k', '{set}z', '{set}t'); // z U t + $zt = array_unique(array_merge($z, $t)); + $this->assertEquals($count, count($zt)); foreach($zt as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sismember('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $xyz = array_unique(array_merge($x, $y, $z)); - $this->assertEquals($count, count($xyz)); + $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($count, count($xyz)); foreach($xyz as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sismember('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $this->redis->del('x'); // x missing now - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === count(array_unique(array_merge($y, $z)))); + $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('y'); // x and y missing - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === count(array_unique($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('z'); // x, y, and z ALL missing - $count = $this->redis->sUnionStore('k', 'x', 'y', 'z'); // x U y U z - $this->assertTrue($count === 0); + $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('x'); // set of odd numbers - $this->redis->del('y'); // set of prime numbers - $this->redis->del('z'); // set of squares - $this->redis->del('t'); // set of numbers of the form n^2 - 1 + $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 = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { - $this->redis->sAdd('x', $i); + $this->redis->sAdd('{set}x', $i); } $y = array(1,2,3,5,7,11,13,17,19,23); foreach($y as $i) { - $this->redis->sAdd('y', $i); + $this->redis->sAdd('{set}y', $i); } $z = array(1,4,9,16,25); foreach($z as $i) { - $this->redis->sAdd('z', $i); + $this->redis->sAdd('{set}z', $i); } $t = array(2,5,10,17,26); foreach($t as $i) { - $this->redis->sAdd('t', $i); + $this->redis->sAdd('{set}t', $i); } - $xy = $this->redis->sDiff('x', 'y'); // x U y + $xy = $this->redis->sDiff('{set}x', '{set}y'); // x U y foreach($xy as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_diff($x, $y))); } - $yz = $this->redis->sDiff('y', 'z'); // y U Z + $yz = $this->redis->sDiff('{set}y', '{set}z'); // y U Z foreach($yz as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_diff($y, $z))); } - $zt = $this->redis->sDiff('z', 't'); // z U t + $zt = $this->redis->sDiff('{set}z', '{set}t'); // z U t foreach($zt as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_diff($z, $t))); } - $xyz = $this->redis->sDiff('x', 'y', 'z'); // x U y U z + $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($i, array_diff($x, $y, $z))); @@ -1618,136 +1617,99 @@ public function testsDiff() { } public function testsDiffStore() { - $this->redis->del('x'); // set of odd numbers - $this->redis->del('y'); // set of prime numbers - $this->redis->del('z'); // set of squares - $this->redis->del('t'); // set of numbers of the form n^2 - 1 + $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 = array(1,3,5,7,9,11,13,15,17,19,21,23,25); foreach($x as $i) { - $this->redis->sAdd('x', $i); + $this->redis->sAdd('{set}x', $i); } $y = array(1,2,3,5,7,11,13,17,19,23); foreach($y as $i) { - $this->redis->sAdd('y', $i); + $this->redis->sAdd('{set}y', $i); } $z = array(1,4,9,16,25); foreach($z as $i) { - $this->redis->sAdd('z', $i); + $this->redis->sAdd('{set}z', $i); } $t = array(2,5,10,17,26); foreach($t as $i) { - $this->redis->sAdd('t', $i); + $this->redis->sAdd('{set}t', $i); } - $count = $this->redis->sDiffStore('k', 'x', 'y'); // x - y - $xy = array_unique(array_diff($x, $y)); - $this->assertEquals($count, count($xy)); + $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y'); // x - y + $xy = array_unique(array_diff($x, $y)); + $this->assertEquals($count, count($xy)); foreach($xy as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('k', $i)); } - $count = $this->redis->sDiffStore('k', 'y', 'z'); // y - z - $yz = array_unique(array_diff($y, $z)); - $this->assertEquals($count, count($yz)); + $count = $this->redis->sDiffStore('{set}k', '{set}y', '{set}z'); // y - z + $yz = array_unique(array_diff($y, $z)); + $this->assertEquals($count, count($yz)); foreach($yz as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sismember('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $count = $this->redis->sDiffStore('k', 'z', 't'); // z - t - $zt = array_unique(array_diff($z, $t)); - $this->assertEquals($count, count($zt)); + $count = $this->redis->sDiffStore('{set}k', '{set}z', '{set}t'); // z - t + $zt = array_unique(array_diff($z, $t)); + $this->assertEquals($count, count($zt)); foreach($zt as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sismember('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $xyz = array_unique(array_diff($x, $y, $z)); - $this->assertEquals($count, count($xyz)); + $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($count, count($xyz)); foreach($xyz as $i) { $i = (int)$i; - $this->assertTrue($this->redis->sismember('k', $i)); + $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $this->redis->del('x'); // x missing now - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); + $this->redis->del('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('y'); // x and y missing - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); + $this->redis->del('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('z'); // x, y, and z ALL missing - $count = $this->redis->sDiffStore('k', 'x', 'y', 'z'); // x - y - z - $this->assertTrue($count === 0); + $this->redis->del('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'); + $this->redis->lPush('list', 'val3'); - // pos : 0 1 2 - // pos : -3 -2 -1 - // list: [val3, val2, val] + // pos : 0 1 2 + // pos : -3 -2 -1 + // list: [val3, val2, val] - $this->assertEquals($this->redis->lrange('list', 0, 0), array('val3')); - $this->assertEquals($this->redis->lrange('list', 0, 1), array('val3', 'val2')); - $this->assertEquals($this->redis->lrange('list', 0, 2), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lrange('list', 0, 3), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, 0), array('val3')); + $this->assertEquals($this->redis->lrange('list', 0, 1), array('val3', 'val2')); + $this->assertEquals($this->redis->lrange('list', 0, 2), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, 3), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lrange('list', 0, -1), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lrange('list', 0, -2), array('val3', 'val2')); - $this->assertEquals($this->redis->lrange('list', -2, -1), array('val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, -1), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, -2), array('val3', 'val2')); + $this->assertEquals($this->redis->lrange('list', -2, -1), array('val2', 'val')); - $this->redis->del('list'); - $this->assertEquals($this->redis->lrange('list', 0, -1), array()); + $this->redis->del('list'); + $this->assertEquals($this->redis->lrange('list', 0, -1), array()); } - -// public function testsave() { -// $this->assertTrue($this->redis->save() === TRUE); // don't really know how else to test this... -// } -// public function testbgSave() { -// // let's try to fill the DB and then bgSave twice. We expect the second one to fail. -// for($i = 0; $i < 10e+4; $i++) { -// $s = md5($i); -// $this->redis->set($s, $s); -// } -// $this->assertTrue($this->redis->bgSave() === TRUE); // the first one should work. -// $this->assertTrue($this->redis->bgSave() === FALSE); // the second one should fail (still working on the first one) -// } -// -// public function testlastSave() { -// while(!$this->redis->save()) { -// sleep(1); -// } -// $t_php = microtime(TRUE); -// $t_redis = $this->redis->lastSave(); -// -// $this->assertTrue($t_php - $t_redis < 10000); // check that it's approximately what we've measured in PHP. -// } -// -// public function testflushDb() { -// $this->redis->set('x', 'y'); -// $this->assertTrue($this->redis->flushDb()); -// $this->assertTrue($this->redis->getKeys('*') === array()); -// } -// -// public function testflushAll() { -// $this->redis->set('x', 'y'); -// $this->assertTrue($this->redis->flushAll()); -// $this->assertTrue($this->redis->getKeys('*') === array()); -// } - public function testdbSize() { $this->assertTrue($this->redis->flushDB()); $this->redis->set('x', 'y'); @@ -1756,7 +1718,7 @@ public function testdbSize() { public function testttl() { $this->redis->set('x', 'y'); - $this->redis->setTimeout('x', 5); + $this->redis->expire('x', 5); for($i = 5; $i > 0; $i--) { $this->assertEquals($i, $this->redis->ttl('x')); sleep(1); @@ -1775,7 +1737,7 @@ public function testttl() { public function testPersist() { $this->redis->set('x', 'y'); - $this->redis->setTimeout('x', 100); + $this->redis->expire('x', 100); $this->assertTrue(TRUE === $this->redis->persist('x')); // true if there is a timeout $this->assertTrue(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout From 88530eed4a0038cd176cd5d8b0a4fe7ecae4e70b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 18:05:49 -0800 Subject: [PATCH 0437/1986] Add cluster specific testInfo() method --- redis_cluster.c | 4 +-- tests/RedisClusterTest.php | 28 +++++++++++++++-- tests/RedisTest.php | 63 +++++++++++++++++++------------------- 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 6a4bcf33ab..59930ef059 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2338,10 +2338,10 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* Initialize our command */ - redis_cmd_init_sstr(&cmd, argc, kw, kw_len); + redis_cmd_init_sstr(&cmd, argc-1, kw, kw_len); /* Iterate, appending args */ - for(i=0;imarkTestSkipped(); } + public function testSortDesc() { return $this->markTestSkipped(); } + public function testWait() { return $this->markTestSkipped(); } + public function testSelect() { return $this->markTestSkipped(); } + /* Load our seeds on construction */ public function __construct() { $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; @@ -59,9 +68,6 @@ public function testEcho() { $this->assertEquals($this->redis->echo('k3', " 0123 "), " 0123 "); } - public function testSortAsc() { return $this->markTestSkipped(); } - public function testSortDesc() { return $this->markTestSkipped(); } - public function testSortPrefix() { $this->redis->setOption(Redis::OPT_PREFIX, 'some-prefix:'); $this->redis->del('some-item'); @@ -85,5 +91,21 @@ public function testDBSize() { } } + public function testInfo() { + $arr_check_keys = Array( + "redis_version", "arch_bits", "uptime_in_seconds", "uptime_in_days", + "connected_clients", "connected_slaves", "used_memory", + "total_connections_received", "total_commands_processed", + "role" + ); + + for ($i = 0; $i < 3; $i++) { + $arr_info = $this->redis->info("k:$i"); + foreach ($arr_check_keys as $str_check_key) { + $this->assertTrue(isset($arr_info[$str_check_key])); + } + } + } + } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 0d8c94cb6a..1035ac1346 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1810,36 +1810,37 @@ public function testWait() { } public function testinfo() { - $info = $this->redis->info(); - - $keys = array( - "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", "lt")) { - 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" - ); - } + $info = $this->redis->info(); - foreach($keys as $k) { - $this->assertTrue(in_array($k, array_keys($info))); - } + $keys = array( + "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", "lt")) { + 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($k, array_keys($info))); + } } public function testInfoCommandStats() { @@ -1860,8 +1861,8 @@ public function testInfoCommandStats() { } public function testSelect() { - $this->assertFalse($this->redis->select(-1)); - $this->assertTrue($this->redis->select(0)); + $this->assertFalse($this->redis->select(-1)); + $this->assertTrue($this->redis->select(0)); } public function testMset() { From 9b68c33f7a1904af1140e449053cf446d7d73849 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 18:53:30 -0800 Subject: [PATCH 0438/1986] More unit test cleanup and key consolidation for multiple key commands --- tests/RedisClusterTest.php | 3 + tests/RedisTest.php | 871 ++++++++++++++++++------------------- 2 files changed, 432 insertions(+), 442 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 72b299b333..f9a8c5865b 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -18,6 +18,9 @@ public function testSortDesc() { return $this->markTestSkipped(); } public function testWait() { return $this->markTestSkipped(); } public function testSelect() { return $this->markTestSkipped(); } + /* Skips for now, which need attention */ + public function testClient() { return $this->markTestSkipped(); } + /* Load our seeds on construction */ public function __construct() { $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 1035ac1346..b3395ee6a1 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1736,13 +1736,13 @@ public function testttl() { } 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(-1 === $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. + $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(-1 === $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() { @@ -1809,7 +1809,7 @@ public function testWait() { $this->assertFalse($this->redis->wait(-1, 20)); } - public function testinfo() { + public function testInfo() { $info = $this->redis->info(); $keys = array( @@ -1912,47 +1912,43 @@ public function testMsetNX() { } public function testRpopLpush() { - - // standard case. - $this->redis->del('x', 'y'); - $this->redis->lpush('x', 'abc'); - $this->redis->lpush('x', 'def'); // x = [def, abc] - - $this->redis->lpush('y', '123'); - $this->redis->lpush('y', '456'); // y = [456, 123] - - $this->assertEquals($this->redis->rpoplpush('x', 'y'), 'abc'); // we RPOP x, yielding abc. - $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. - $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. - - // with an empty source, expecting no change. - $this->redis->del('x', 'y'); - $this->assertTrue(FALSE === $this->redis->rpoplpush('x', 'y')); - $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); - $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); - + // 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), array('def')); // only def remains in x. + $this->assertEquals($this->redis->lrange('{list}y', 0, -1), array('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(array() === $this->redis->lrange('{list}x', 0, -1)); + $this->assertTrue(array() === $this->redis->lrange('{list}y', 0, -1)); } public function testBRpopLpush() { - - // standard case. - $this->redis->del('x', 'y'); - $this->redis->lpush('x', 'abc'); - $this->redis->lpush('x', 'def'); // x = [def, abc] - - $this->redis->lpush('y', '123'); - $this->redis->lpush('y', '456'); // y = [456, 123] - - $this->assertEquals($this->redis->brpoplpush('x', 'y', 1), 'abc'); // we RPOP x, yielding abc. - $this->assertEquals($this->redis->lgetRange('x', 0, -1), array('def')); // only def remains in x. - $this->assertEquals($this->redis->lgetRange('y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. - - // with an empty source, expecting no change. - $this->redis->del('x', 'y'); - $this->assertTrue(FALSE === $this->redis->brpoplpush('x', 'y', 1)); - $this->assertTrue(array() === $this->redis->lgetRange('x', 0, -1)); - $this->assertTrue(array() === $this->redis->lgetRange('y', 0, -1)); - + // 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), array('def')); // only def remains in x. + $this->assertEquals($this->redis->lrange('{list}y', 0, -1), array('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(array() === $this->redis->lrange('x', 0, -1)); + $this->assertTrue(array() === $this->redis->lrange('y', 0, -1)); } public function testZAddFirstArg() { @@ -1967,401 +1963,395 @@ public function testZAddFirstArg() { } public function testZX() { + $this->redis->del('key'); - $this->redis->del('key'); + $this->assertTrue(array() === $this->redis->zRange('key', 0, -1)); + $this->assertTrue(array() === $this->redis->zRange('key', 0, -1, true)); - $this->assertTrue(array() === $this->redis->zRange('key', 0, -1)); - $this->assertTrue(array() === $this->redis->zRange('key', 0, -1, true)); - - $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); - $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); - $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters - - $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); - - // withscores - $ret = $this->redis->zRange('key', 0, -1, true); - $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(0 === $this->redis->zDelete('key', 'valX')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val3')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val4')); - $this->assertTrue(1 === $this->redis->zDelete('key', 'val5')); - - $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->zRange('key', 0, -1)); - - // zGetReverseRange - - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); - - $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); - $this->assertTrue(array('val0', 'val1', 'val2', 'aal3', 'val3') === $zero_to_three || array('val0', 'val1', 'val2', 'val3', 'aal3') === $zero_to_three); - - $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); - $this->assertTrue(array_reverse(array('val0', 'val1', 'val2', 'aal3', 'val3')) === $three_to_zero || array_reverse(array('val0', 'val1', 'val2', 'val3', 'aal3')) === $three_to_zero); - - $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); - - // withscores - $this->redis->zRemove('key', 'aal3'); - $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); - $this->assertTrue(array('val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3) == $zero_to_three); - $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); - - // limit - $this->assertTrue(array('val0') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 1)))); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 2)))); - $this->assertTrue(array('val1', 'val2') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 2)))); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => array(0, 100)))); - - $this->assertTrue(array('val3') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 1)))); - $this->assertTrue(array('val3', 'val2') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 2)))); - $this->assertTrue(array('val2', 'val1') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(1, 2)))); - $this->assertTrue(array('val1', 'val0') === $this->redis->zRevRangeByScore('key', 1, 0, array('limit' => array(0, 100)))); - - $this->assertTrue(4 === $this->redis->zSize('key')); - $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); - $this->assertFalse($this->redis->zScore('key', 'val')); - $this->assertFalse($this->redis->zScore(3, 2)); - - // 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(array('foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4) == $this->redis->zRangeByScore('zset', '-inf', '+inf', array('withscores' => TRUE))); - $this->assertTrue(array('foo' => 1, 'bar' => 2) == $this->redis->zRangeByScore('zset', 1, 2, array('withscores' => TRUE))); - $this->assertTrue(array('bar' => 2) == $this->redis->zRangeByScore('zset', '(1', 2, array('withscores' => TRUE))); - $this->assertTrue(array() == $this->redis->zRangeByScore('zset', '(1', '(2', array('withscores' => TRUE))); - - $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); - $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); - $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); - $this->assertTrue(0 == $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')); - - //zUnion - $this->redis->del('key1'); - $this->redis->del('key2'); - $this->redis->del('key3'); - $this->redis->del('keyU'); - - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); - - $this->redis->zAdd('key2', 2, 'val2'); - $this->redis->zAdd('key2', 3, 'val3'); - - $this->redis->zAdd('key3', 4, 'val4'); - $this->redis->zAdd('key3', 5, 'val5'); - - $this->assertTrue(4 === $this->redis->zUnion('keyU', array('key1', 'key3'))); - $this->assertTrue(array('val0', 'val1', 'val4', 'val5') === $this->redis->zRange('keyU', 0, -1)); - - // Union on non existing keys - $this->redis->del('keyU'); - $this->assertTrue(0 === $this->redis->zUnion('keyU', array('X', 'Y'))); - $this->assertTrue(array() === $this->redis->zRange('keyU', 0, -1)); - - // !Exist U Exist → copy of existing zset. - $this->redis->del('keyU', 'X'); - $this->assertTrue(2 === $this->redis->zUnion('keyU', array('key1', 'X'))); - - // test weighted zUnion - $this->redis->del('keyZ'); - $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(1, 1))); - $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('keyZ', 0, -1)); - - $this->redis->zDeleteRangeByScore('keyZ', 0, 10); - $this->assertTrue(4 === $this->redis->zUnion('keyZ', array('key1', 'key2'), array(5, 1))); - $this->assertTrue(array('val0', 'val2', 'val3', 'val1') === $this->redis->zRange('keyZ', 0, -1)); - - $this->redis->del('key1'); - $this->redis->del('key2'); - $this->redis->del('key3'); - - //test zUnion with weights and aggegration function - $this->redis->zadd('key1', 1, 'duplicate'); - $this->redis->zadd('key2', 2, 'duplicate'); - $this->redis->zUnion('keyU', array('key1','key2'), array(1,1), 'MIN'); - $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); - $this->redis->del('keyU'); - - //now test zUnion *without* weights but with aggregrate function - $this->redis->zUnion('keyU', array('key1','key2'), null, 'MIN'); - $this->assertTrue($this->redis->zScore('keyU', 'duplicate')===1.0); - $this->redis->del('keyU', 'key1', 'key2'); - - - - // test integer and float weights (GitHub issue #109). - $this->redis->del('key1', 'key2', 'key3'); - - $this->redis->zadd('key1', 1, 'one'); - $this->redis->zadd('key1', 2, 'two'); - $this->redis->zadd('key2', 1, 'one'); - $this->redis->zadd('key2', 2, 'two'); - $this->redis->zadd('key2', 3, 'three'); - - $this->assertTrue($this->redis->zunion('key3', array('key1', 'key2'), array(2, 3.0)) === 3); - - $this->redis->del('key1'); - $this->redis->del('key2'); - $this->redis->del('key3'); - - // Test 'inf', '-inf', and '+inf' weights (GitHub issue #336) - $this->redis->zadd('key1', 1, 'one', 2, 'two', 3, 'three'); - $this->redis->zadd('key2', 3, 'three', 4, 'four', 5, 'five'); - - // Make sure phpredis handles these weights - $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, 'inf')) === 5); - $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, '-inf')) === 5); - $this->assertTrue($this->redis->zunion('key3', array('key1','key2'), array(1, '+inf')) === 5); - - // Now, confirm that they're being sent, and that it works - $arr_weights = Array('inf','-inf','+inf'); - - foreach($arr_weights as $str_weight) { - $r = $this->redis->zunionstore('key3', array('key1','key2'), array(1,$str_weight)); - $this->assertTrue($r===5); - $r = $this->redis->zrangebyscore('key3', '(-inf', '(inf',array('withscores'=>true)); - $this->assertTrue(count($r)===2); - $this->assertTrue(isset($r['one'])); - $this->assertTrue(isset($r['two'])); - } + $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); + $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); + $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters - $this->redis->del('key1','key2','key3'); + $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); - $this->redis->zadd('key1', 2000.1, 'one'); - $this->redis->zadd('key1', 3000.1, 'two'); - $this->redis->zadd('key1', 4000.1, 'three'); + // withscores + $ret = $this->redis->zRange('key', 0, -1, true); + $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); - $ret = $this->redis->zRange('key1', 0, -1, TRUE); - $this->assertTrue(count($ret) === 3); - $retValues = array_keys($ret); + $this->assertTrue(0 === $this->redis->zRem('key', 'valX')); + $this->assertTrue(1 === $this->redis->zRem('key', 'val3')); + $this->assertTrue(1 === $this->redis->zRem('key', 'val4')); + $this->assertTrue(1 === $this->redis->zRem('key', 'val5')); - $this->assertTrue(array('one', 'two', 'three') === $retValues); + $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->zRange('key', 0, -1)); - // + 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)); + // zGetReverseRange - $this->redis->del('key1'); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); - // ZREMRANGEBYRANK - $this->redis->zAdd('key1', 1, 'one'); - $this->redis->zAdd('key1', 2, 'two'); - $this->redis->zAdd('key1', 3, 'three'); - $this->assertTrue(2 === $this->redis->zremrangebyrank('key1', 0, 1)); - $this->assertTrue(array('three' => 3) == $this->redis->zRange('key1', 0, -1, TRUE)); + $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); + $this->assertTrue(array('val0', 'val1', 'val2', 'aal3', 'val3') === $zero_to_three || array('val0', 'val1', 'val2', 'val3', 'aal3') === $zero_to_three); - $this->redis->del('key1'); + $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); + $this->assertTrue(array_reverse(array('val0', 'val1', 'val2', 'aal3', 'val3')) === $three_to_zero || array_reverse(array('val0', 'val1', 'val2', 'val3', 'aal3')) === $three_to_zero); - // zInter + $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); - $this->redis->zAdd('key1', 3, 'val3'); + // withscores + $this->redis->zRem('key', 'aal3'); + $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); + $this->assertTrue(array('val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3) == $zero_to_three); + $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); - $this->redis->zAdd('key2', 2, 'val1'); - $this->redis->zAdd('key2', 3, 'val3'); + // limit + $this->assertTrue(array('val0') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 1)))); + $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 2)))); + $this->assertTrue(array('val1', 'val2') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 2)))); + $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => array(0, 100)))); - $this->redis->zAdd('key3', 4, 'val3'); - $this->redis->zAdd('key3', 5, 'val5'); + $this->assertTrue(array('val3') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 1)))); + $this->assertTrue(array('val3', 'val2') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 2)))); + $this->assertTrue(array('val2', 'val1') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(1, 2)))); + $this->assertTrue(array('val1', 'val0') === $this->redis->zRevRangeByScore('key', 1, 0, array('limit' => array(0, 100)))); - $this->redis->del('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + $this->assertTrue(4 === $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(3, 2)); - // Union on non existing keys - $this->assertTrue(0 === $this->redis->zInter('keyX', array('X', 'Y'))); - $this->assertTrue(array() === $this->redis->zRange('keyX', 0, -1)); + // 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(array('foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4) == $this->redis->zRangeByScore('zset', '-inf', '+inf', array('withscores' => TRUE))); + $this->assertTrue(array('foo' => 1, 'bar' => 2) == $this->redis->zRangeByScore('zset', 1, 2, array('withscores' => TRUE))); + $this->assertTrue(array('bar' => 2) == $this->redis->zRangeByScore('zset', '(1', 2, array('withscores' => TRUE))); + $this->assertTrue(array() == $this->redis->zRangeByScore('zset', '(1', '(2', array('withscores' => TRUE))); + + $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); + $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); + $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); + $this->assertTrue(0 == $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(4 === $this->redis->zUnionStore('{zset}U', array('{zset}1', '{zset}3'))); + $this->assertTrue(array('val0', 'val1', 'val4', 'val5') === $this->redis->zRange('{zset}U', 0, -1)); + + // Union on non existing keys + $this->redis->del('{zset}U'); + $this->assertTrue(0 === $this->redis->zUnionStore('{zset}U', array('{zset}X', '{zset}Y'))); + $this->assertTrue(array() === $this->redis->zRange('{zset}U', 0, -1)); + + // !Exist U Exist → copy of existing zset. + $this->redis->del('{zset}U', 'X'); + $this->assertTrue(2 === $this->redis->zUnionStore('{zset}U', array('{zset}1', '{zset}X'))); + + // test weighted zUnion + $this->redis->del('{zset}Z'); + $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', array('{zset}1', '{zset}2'), array(1, 1))); + $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('keyZ', 0, -1)); + + $this->redis->zRemRangeByScore('{zset}Z', 0, 10); + $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', array('{zset}1', '{zset}2'), array(5, 1))); + $this->assertTrue(array('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', array('{zset}1','{zset}2'), array(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', array('{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', array('{zset}1', '{zset}2'), array(2, 3.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', array('{zset}1','{zset}2'), array(1, 'inf')) === 5); + $this->assertTrue($this->redis->zUnionStore('{zset}3', array('{zset}1','{zset}2'), array(1, '-inf')) === 5); + $this->assertTrue($this->redis->zUnionStore('{zset}3', array('{zset}1','{zset}2'), array(1, '+inf')) === 5); + + // Now, confirm that they're being sent, and that it works + $arr_weights = Array('inf','-inf','+inf'); + + foreach($arr_weights as $str_weight) { + $r = $this->redis->zUnionStore('{zset}3', array('{zset}1','{zset}2'), array(1,$str_weight)); + $this->assertTrue($r===5); + $r = $this->redis->zrangebyscore('{zset}3', '(-inf', '(inf',array('withscores'=>true)); + $this->assertTrue(count($r)===2); + $this->assertTrue(isset($r['one'])); + $this->assertTrue(isset($r['two'])); + } - // !Exist U Exist - $this->assertTrue(0 === $this->redis->zInter('keyY', array('key1', 'X'))); - $this->assertTrue(array() === $this->redis->zRange('keyY', 0, -1)); + $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'); - // test weighted zInter - $this->redis->del('key1'); - $this->redis->del('key2'); - $this->redis->del('key3'); + $ret = $this->redis->zRange('{zset}1', 0, -1, TRUE); + $this->assertTrue(count($ret) === 3); + $retValues = array_keys($ret); - $this->redis->zAdd('key1', 0, 'val0'); - $this->redis->zAdd('key1', 1, 'val1'); - $this->redis->zAdd('key1', 3, 'val3'); + $this->assertTrue(array('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->zAdd('key2', 2, 'val1'); - $this->redis->zAdd('key2', 1, 'val3'); + $this->redis->del('{zset}1'); - $this->redis->zAdd('key3', 7, 'val1'); - $this->redis->zAdd('key3', 3, 'val3'); + // ZREMRANGEBYRANK + $this->redis->zAdd('{zset}1', 1, 'one'); + $this->redis->zAdd('{zset}1', 2, 'two'); + $this->redis->zAdd('{zset}1', 3, 'three'); + $this->assertTrue(2 === $this->redis->zremrangebyrank('{zset}1', 0, 1)); + $this->assertTrue(array('three' => 3) == $this->redis->zRange('{zset}1', 0, -1, TRUE)); - $this->redis->del('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2'), array(1, 1))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + $this->redis->del('{zset}1'); - $this->redis->del('keyI'); - $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'min')); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); - $this->redis->del('keyI'); - $this->assertTrue( 2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), array(1, 5, 1), 'max')); - $this->assertTrue(array('val3', 'val1') === $this->redis->zRange('keyI', 0, -1)); + // zInter - $this->redis->del('keyI'); - $this->assertTrue(2 === $this->redis->zInter('keyI', array('key1', 'key2', 'key3'), null, 'max')); - $this->assertTrue($this->redis->zScore('keyI', 'val1') === floatval(7)); + $this->redis->zAdd('{zset}1', 0, 'val0'); + $this->redis->zAdd('{zset}1', 1, 'val1'); + $this->redis->zAdd('{zset]1', 3, 'val3'); - // 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->redis->zAdd('{zset}2', 2, 'val1'); + $this->redis->zAdd('{zset}2', 3, 'val3'); - $this->assertTrue(0 === $this->redis->zRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRank('z', 'two')); - $this->assertTrue(2 === $this->redis->zRank('z', 'five')); + $this->redis->zAdd('{zset}3', 4, 'val3'); + $this->redis->zAdd('{zset}3', 5, 'val5'); - $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); - $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); + $this->redis->del('{zset}I'); + $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2'))); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + // Union on non existing keys + $this->assertTrue(0 === $this->redis->zInterStore('{zset}X', array('{zset}X', '{zset}Y'))); + $this->assertTrue(array() === $this->redis->zRange('{zset}X', 0, -1)); + + // !Exist U Exist + $this->assertTrue(0 === $this->redis->zInterStore('{zset}Y', array('{zset}1', '{zset}X'))); + $this->assertTrue(array() === $this->redis->zRange('keyY', 0, -1)); + + + // test weighted zInter + $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(2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2'), array(1, 1))); + $this->assertTrue(array('{zset}1', '{zset}3') === $this->redis->zRange('{zset}I', 0, -1)); + + $this->redis->del('{zset}I'); + $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2', '{zset}3'), array(1, 5, 1), 'min')); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('{zset}I', 0, -1)); + $this->redis->del('{zset}I'); + $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2', '{zset}3'), array(1, 5, 1), 'max')); + $this->assertTrue(array('val3', 'val1') === $this->redis->zRange('{zset}I', 0, -1)); + + $this->redis->del('{zset}I'); + $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', array('{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(0 === $this->redis->zRank('z', 'one')); + $this->assertTrue(1 === $this->redis->zRank('z', 'two')); + $this->assertTrue(2 === $this->redis->zRank('z', 'five')); + + $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); + $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); + $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); } public function testHashes() { - $this->redis->del('h', 'key'); - - $this->assertTrue(0 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); - $this->assertTrue(1 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); - $this->assertTrue(2 === $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(0 === $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(1 === $this->redis->hDel('h', 'a')); // 1 on success - $this->assertTrue(0 === $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(2 === $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 === array('x', 'y') || $keys === array('y', 'x')); - - // values - $values = $this->redis->hVals('h'); - $this->assertTrue($values === array('a', 'b') || $values === array('b', 'a')); - - // keys + values - $all = $this->redis->hGetAll('h'); - $this->assertTrue($all === array('x' => 'a', 'y' => 'b') || $all === array('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(2 === $this->redis->hIncrBy('h', 'x', 2)); - $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); - $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); - $this->assertTrue("2" === $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", "ge")) { - // 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->redis->hset('h','y','not-a-number'); - $this->assertTrue(FALSE === $this->redis->hIncrByFloat('h', 'y', 1.5)); - } + $this->redis->del('h', 'key'); + $this->assertTrue(0 === $this->redis->hLen('h')); + $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); + $this->assertTrue(1 === $this->redis->hLen('h')); + $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); + $this->assertTrue(2 === $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(0 === $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(1 === $this->redis->hDel('h', 'a')); // 1 on success + $this->assertTrue(0 === $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(2 === $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')); - // hmset - $this->redis->del('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', array('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(array('x' => '123', 'y' => '456') === $this->redis->hMget('h', array('x', 'y'))); - $this->assertTrue(array('z' => 'abc') === $this->redis->hMget('h', array('z'))); - $this->assertTrue(array('x' => '123', 't' => FALSE, 'y' => '456') === $this->redis->hMget('h', array('x', 't', 'y'))); - $this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123))); - $this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123))); - - // Test with an array populated with things we can't use as keys - $this->assertTrue($this->redis->hmget('h', Array(false,NULL,false)) === FALSE); - - // Test with some invalid keys mixed in (which should just be ignored) - $this->assertTrue(array('x'=>'123','y'=>'456','z'=>'abc') === $this->redis->hMget('h',Array('x',null,'y','','z',false))); - - // hmget/hmset with numeric fields - $this->redis->del('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', array(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(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); - - // check non-string types. - $this->redis->del('h1'); - $this->assertTrue(TRUE === $this->redis->hMSet('h1', array('x' => 0, 'y' => array(), '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']); + // keys + $keys = $this->redis->hKeys('h'); + $this->assertTrue($keys === array('x', 'y') || $keys === array('y', 'x')); + + // values + $values = $this->redis->hVals('h'); + $this->assertTrue($values === array('a', 'b') || $values === array('b', 'a')); + + // keys + values + $all = $this->redis->hGetAll('h'); + $this->assertTrue($all === array('x' => 'a', 'y' => 'b') || $all === array('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(2 === $this->redis->hIncrBy('h', 'x', 2)); + $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); + $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); + $this->assertTrue("2" === $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", "ge")) { + // 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->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', array('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(array('x' => '123', 'y' => '456') === $this->redis->hMget('h', array('x', 'y'))); + $this->assertTrue(array('z' => 'abc') === $this->redis->hMget('h', array('z'))); + $this->assertTrue(array('x' => '123', 't' => FALSE, 'y' => '456') === $this->redis->hMget('h', array('x', 't', 'y'))); + $this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123))); + $this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123))); + + // Test with an array populated with things we can't use as keys + $this->assertTrue($this->redis->hmget('h', Array(false,NULL,false)) === FALSE); + + // Test with some invalid keys mixed in (which should just be ignored) + $this->assertTrue(array('x'=>'123','y'=>'456','z'=>'abc') === $this->redis->hMget('h',Array('x',null,'y','','z',false))); + + // hmget/hmset with numeric fields + $this->redis->del('h'); + $this->assertTrue(TRUE === $this->redis->hMset('h', array(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(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); + + // check non-string types. + $this->redis->del('h1'); + $this->assertTrue(TRUE === $this->redis->hMSet('h1', array('x' => 0, 'y' => array(), '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']); } public function testSetRange() { @@ -2494,10 +2484,8 @@ public function testPipeline() { } protected function sequence($mode) { - $ret = $this->redis->multi($mode) ->set('x', 42) - ->info() ->type('x') ->get('x') ->exec(); @@ -2505,7 +2493,6 @@ protected function sequence($mode) { $this->assertTrue(is_array($ret)); $i = 0; $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue(is_array($ret[$i++])); $this->assertTrue($ret[$i++] === Redis::REDIS_STRING); $this->assertTrue($ret[$i] === '42' || $ret[$i] === 42); @@ -2926,14 +2913,14 @@ protected function sequence($mode) { ->zadd('zkey1', 5, 'zValue5') ->zadd('zkey1', 2, 'zValue2') ->zRange('zkey1', 0, -1) - ->zDelete('zkey1', 'zValue2') + ->zRem('zkey1', 'zValue2') ->zRange('zkey1', 0, -1) ->zadd('zkey1', 11, 'zValue11') ->zadd('zkey1', 12, 'zValue12') ->zadd('zkey1', 13, 'zValue13') ->zadd('zkey1', 14, 'zValue14') ->zadd('zkey1', 15, 'zValue15') - ->zDeleteRangeByScore('zkey1', 11, 13) + ->zRemRangeByScore('zkey1', 11, 13) ->zrange('zkey1', 0, -1) ->zReverseRange('zkey1', 0, -1) ->zRangeByScore('zkey1', 1, 6) @@ -2945,7 +2932,7 @@ protected function sequence($mode) { ->zRange('zkey1', 0, -1) ->zRange('zkey2', 0, -1) ->zRange('zInter', 0, -1) - ->zUnion('zUnion', array('zkey1', 'zkey2')) + ->zUnionStore('zUnion', array('zkey1', 'zkey2')) ->zRange('zUnion', 0, -1) ->zadd('zkey5', 5, 'zValue5') ->zIncrBy('zkey5', 3, 'zValue5') // fix this @@ -3092,7 +3079,7 @@ protected function differentType($mode) { // sorted sets I/F ->zAdd($key, 1, 'zValue1') - ->zDelete($key, 'zValue1') + ->zRem($key, 'zValue1') ->zIncrBy($key, 1, 'zValue1') ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') @@ -3102,8 +3089,8 @@ protected function differentType($mode) { ->zCount($key, 0, -1) ->zCard($key) ->zScore($key, 'zValue1') - ->zDeleteRangeByRank($key, 1, 2) - ->zDeleteRangeByScore($key, 1, 2) + ->zRemRangeByRank($key, 1, 2) + ->zRemRangeByScore($key, 1, 2) // hash I/F ->hSet($key, 'key1', 'value1') @@ -3210,7 +3197,7 @@ protected function differentType($mode) { // sorted sets I/F ->zAdd($key, 1, 'zValue1') - ->zDelete($key, 'zValue1') + ->zRem($key, 'zValue1') ->zIncrBy($key, 1, 'zValue1') ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') @@ -3220,8 +3207,8 @@ protected function differentType($mode) { ->zCount($key, 0, -1) ->zCard($key) ->zScore($key, 'zValue1') - ->zDeleteRangeByRank($key, 1, 2) - ->zDeleteRangeByScore($key, 1, 2) + ->zRemRangeByRank($key, 1, 2) + ->zRemRangeByScore($key, 1, 2) // hash I/F ->hSet($key, 'key1', 'value1') @@ -3327,7 +3314,7 @@ protected function differentType($mode) { // sorted sets I/F ->zAdd($key, 1, 'zValue1') - ->zDelete($key, 'zValue1') + ->zRem($key, 'zValue1') ->zIncrBy($key, 1, 'zValue1') ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') @@ -3337,8 +3324,8 @@ protected function differentType($mode) { ->zCount($key, 0, -1) ->zCard($key) ->zScore($key, 'zValue1') - ->zDeleteRangeByRank($key, 1, 2) - ->zDeleteRangeByScore($key, 1, 2) + ->zRemRangeByRank($key, 1, 2) + ->zRemRangeByScore($key, 1, 2) // hash I/F ->hSet($key, 'key1', 'value1') @@ -3572,7 +3559,7 @@ protected function differentType($mode) { // sorted sets I/F ->zAdd($key, 1, 'zValue1') - ->zDelete($key, 'zValue1') + ->zRem($key, 'zValue1') ->zIncrBy($key, 1, 'zValue1') ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') @@ -3582,8 +3569,8 @@ protected function differentType($mode) { ->zCount($key, 0, -1) ->zCard($key) ->zScore($key, 'zValue1') - ->zDeleteRangeByRank($key, 1, 2) - ->zDeleteRangeByScore($key, 1, 2) + ->zRemRangeByRank($key, 1, 2) + ->zRemRangeByScore($key, 1, 2) ->exec(); @@ -3679,7 +3666,7 @@ public function testDifferentTypeString() { // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); @@ -3689,8 +3676,8 @@ public function testDifferentTypeString() { $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); @@ -3737,7 +3724,7 @@ public function testDifferentTypeList() { // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); @@ -3747,8 +3734,8 @@ public function testDifferentTypeList() { $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); @@ -3796,7 +3783,7 @@ public function testDifferentTypeSet() { // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); @@ -3806,8 +3793,8 @@ public function testDifferentTypeSet() { $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); @@ -3925,7 +3912,7 @@ public function testDifferentTypeHash() { // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDelete($key, 'zValue1')); + $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); @@ -3935,8 +3922,8 @@ public function testDifferentTypeHash() { $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zDeleteRangeByScore($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); } public function testSerializerPHP() { @@ -4051,20 +4038,20 @@ private function checkSerializer($mode) { $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2])); $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3])); - // zDelete - $this->assertTrue(1 === $this->redis->zDelete('key', $z[3])); - $this->assertTrue(0 === $this->redis->zDelete('key', $z[3])); + // zRem + $this->assertTrue(1 === $this->redis->zRem('key', $z[3])); + $this->assertTrue(0 === $this->redis->zRem('key', $z[3])); unset($z[3]); - // check that zDelete doesn't crash with a missing parameter (GitHub issue #102): - $this->assertTrue(FALSE === @$this->redis->zDelete('key')); + // 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(2 === $this->redis->zDelete('k', 'a', 'c')); + $this->assertTrue(2 === $this->redis->zRem('k', 'a', 'c')); $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); $this->assertTrue($this->redis->zRange('k', 0, -1, true) == array('b' => 1.0)); From b1cea4d7bfced2682df7798b03dc3c310aa7a56e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 18:55:48 -0800 Subject: [PATCH 0439/1986] RedisCluster::MULTI should return our object --- redis_cluster.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 59930ef059..ff341833b0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2029,11 +2029,11 @@ PHP_METHOD(RedisCluster, multi) { RETURN_FALSE; } - // Go into MULTI mode + /* Flag that we're in MULTI mode */ c->flags->mode = MULTI; - // Success - RETURN_TRUE; + /* Return our object so we can chain MULTI calls */ + RETVAL_ZVAL(getThis(), 1, 0); } /* {{{ proto bool RedisCluster::watch() */ From 47b05f13b9d575c52b1da4b2ff9e66149f6d8087 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Mar 2015 22:08:30 -0800 Subject: [PATCH 0440/1986] Even more unit test integration, mset fix * Continuing to hammer through Redis unit tests such that the tests will work for both Redis and RedisCluster objects * Fixed mset so we return the RedisCluster object when it's executed in multi mode. --- redis_cluster.c | 7 +- tests/RedisTest.php | 334 ++++++++++++++++++++------------------------ 2 files changed, 158 insertions(+), 183 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index ff341833b0..ca7019e798 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -754,7 +754,8 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, efree(ht_arr); } - if(!CLUSTER_IS_ATOMIC(c)) + /* Return our object if we're in MULTI mode */ + if (!CLUSTER_IS_ATOMIC(c)) RETVAL_ZVAL(getThis(), 1, 0); // Success @@ -847,6 +848,10 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Free our command cluster_multi_free(&mc); + /* Return our object if we're in MULTI mode */ + if (!CLUSTER_IS_ATOMIC(c)) + RETVAL_ZVAL(getThis(), 1, 0); + // Success return 0; } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index b3395ee6a1..e3c77d8c25 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2499,24 +2499,24 @@ protected function sequence($mode) { $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer $ret = $this->redis->multi($mode) - ->del('key1') - ->set('key1', 'value1') - ->get('key1') - ->getSet('key1', 'value2') - ->get('key1') - ->set('key2', 4) - ->incr('key2') - ->get('key2') - ->decr('key2') - ->get('key2') - ->rename('key2', 'key3') - ->get('key3') - ->renameNx('key3', 'key1') - ->rename('key3', 'key2') - ->incrby('key2', 5) - ->get('key2') - ->decrby('key2', 5) - ->get('key2') + ->del('{key}1') + ->set('{key1}', '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(); $this->assertTrue(is_array($ret)); @@ -2544,14 +2544,13 @@ protected function sequence($mode) { $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $ret = $this->redis->multi($mode) - ->del('key1') - ->del('key2') - ->set('key1', 'val1') - ->setnx('key1', 'valX') - ->setnx('key2', 'valX') - ->exists('key1') - ->exists('key3') - ->ping() + ->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)); @@ -2562,14 +2561,6 @@ protected function sequence($mode) { $this->assertTrue($ret[4] == TRUE); $this->assertTrue($ret[5] == TRUE); $this->assertTrue($ret[6] == FALSE); - $this->assertTrue($ret[7] == '+PONG'); - - $ret = $this->redis->multi($mode) - ->randomKey() - ->exec(); - $ret = $this->redis->multi($mode) - ->exec(); - $this->assertTrue($ret == array()); // ttl, mget, mset, msetnx, expire, expireAt $this->redis->del('key'); @@ -2595,26 +2586,26 @@ protected function sequence($mode) { $this->assertTrue(count($ret) == $i); $ret = $this->redis->multi($mode) - ->set('lkey', 'x') - ->set('lDest', 'y') - ->del('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lrange('lDest', 0, -1) - ->lpop('lkey') - ->llen('lkey') - ->lrem('lkey', 'lvalue', 3) - ->llen('lkey') - ->lget('lkey', 0) - ->lrange('lkey', 0, -1) - ->lSet('lkey', 1, "newValue") // check errors on key not exists - ->lrange('lkey', 0, -1) - ->llen('lkey') + ->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', 'lDest') + ->lrange('{list}lDest', 0, -1) + ->lpop('{list}lkey') + ->llen('{list}lkey') + ->lrem('{list}lkey', 'lvalue', 3) + ->llen('{list}lkey') + ->lget('{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)); @@ -2643,13 +2634,13 @@ protected function sequence($mode) { $ret = $this->redis->multi($mode) - ->del('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lrange('lDest', 0, -1) - ->lpop('lkey') + ->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; @@ -2663,48 +2654,27 @@ protected function sequence($mode) { $this->assertTrue(count($ret) == $i); - // general command - $ret = $this->redis->multi($mode) - ->select(3) - ->set('keyAAA', 'value') - ->set('keyAAB', 'value') - ->dbSize() - ->lastsave() - ->exec(); - - $this->redis->select(0); // back to normal - - $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue($ret[$i++] === TRUE); // select - $this->assertTrue($ret[$i++] === TRUE); // set - $this->assertTrue($ret[$i++] === TRUE); // set - $this->assertTrue(is_long($ret[$i++])); // dbsize - $this->assertTrue(is_long($ret[$i++])); // lastsave - - $this->assertTrue(count($ret) === $i); - $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer $ret = $this->redis->multi($mode) - ->del('key1') - ->set('key1', 'value1') - ->get('key1') - ->getSet('key1', 'value2') - ->get('key1') - ->set('key2', 4) - ->incr('key2') - ->get('key2') - ->decr('key2') - ->get('key2') - ->rename('key2', 'key3') - ->get('key3') - ->renameNx('key3', 'key1') - ->rename('key3', 'key2') - ->incrby('key2', 5) - ->get('key2') - ->decrby('key2', 5) - ->get('key2') + ->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; @@ -2781,24 +2751,24 @@ protected function sequence($mode) { // lists $ret = $this->redis->multi($mode) - ->del('lkey', 'lDest') - ->rpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->lpush('lkey', 'lvalue') - ->rpoplpush('lkey', 'lDest') - ->lrange('lDest', 0, -1) - ->lpop('lkey') - ->llen('lkey') - ->lrem('lkey', 'lvalue', 3) - ->llen('lkey') - ->lget('lkey', 0) - ->lrange('lkey', 0, -1) - ->lSet('lkey', 1, "newValue") // check errors on missing key - ->lrange('lkey', 0, -1) - ->llen('lkey') + ->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') + ->lget('{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)); @@ -2827,34 +2797,34 @@ protected function sequence($mode) { // sets $ret = $this->redis->multi($mode) - ->del('skey1', 'skey2', 'skeydest', 'skeyUnion', 'sDiffDest') - ->sadd('skey1', 'sValue1') - ->sadd('skey1', 'sValue2') - ->sadd('skey1', 'sValue3') - ->sadd('skey1', 'sValue4') - - ->sadd('skey2', 'sValue1') - ->sadd('skey2', 'sValue2') - - ->scard('skey1') - ->srem('skey1', 'sValue2') - ->scard('skey1') - ->sMove('skey1', 'skey2', 'sValue4') - ->scard('skey2') - ->sismember('skey2', 'sValue4') - ->sMembers('skey1') - ->sMembers('skey2') - ->sInter('skey1', 'skey2') - ->sInterStore('skeydest', 'skey1', 'skey2') - ->sMembers('skeydest') - ->sUnion('skey2', 'skeydest') - ->sUnionStore('skeyUnion', 'skey2', 'skeydest') - ->sMembers('skeyUnion') - ->sDiff('skey1', 'skey2') - ->sDiffStore('sDiffDest', 'skey1', 'skey2') - ->sMembers('sDiffDest') - ->sPop('skey2') - ->scard('skey2') + ->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; @@ -2908,36 +2878,36 @@ protected function sequence($mode) { // sorted sets $ret = $this->redis->multi($mode) - ->del('zkey1', 'zkey2', 'zkey5', 'zInter', 'zUnion') - ->zadd('zkey1', 1, 'zValue1') - ->zadd('zkey1', 5, 'zValue5') - ->zadd('zkey1', 2, 'zValue2') - ->zRange('zkey1', 0, -1) - ->zRem('zkey1', 'zValue2') - ->zRange('zkey1', 0, -1) - ->zadd('zkey1', 11, 'zValue11') - ->zadd('zkey1', 12, 'zValue12') - ->zadd('zkey1', 13, 'zValue13') - ->zadd('zkey1', 14, 'zValue14') - ->zadd('zkey1', 15, 'zValue15') - ->zRemRangeByScore('zkey1', 11, 13) - ->zrange('zkey1', 0, -1) - ->zReverseRange('zkey1', 0, -1) - ->zRangeByScore('zkey1', 1, 6) - ->zCard('zkey1') - ->zScore('zkey1', 'zValue15') - ->zadd('zkey2', 5, 'zValue5') - ->zadd('zkey2', 2, 'zValue2') - ->zInter('zInter', array('zkey1', 'zkey2')) - ->zRange('zkey1', 0, -1) - ->zRange('zkey2', 0, -1) - ->zRange('zInter', 0, -1) - ->zUnionStore('zUnion', array('zkey1', 'zkey2')) - ->zRange('zUnion', 0, -1) - ->zadd('zkey5', 5, 'zValue5') - ->zIncrBy('zkey5', 3, 'zValue5') // fix this - ->zScore('zkey5', 'zValue5') - ->zScore('zkey5', 'unknown') + ->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', 11, 13) + ->zrange('{z}key1', 0, -1) + ->zReverseRange('{z}key1', 0, -1) + ->zRangeByScore('{z}key1', 1, 6) + ->zCard('{z}key1') + ->zScore('{z}key1', 'zValue15') + ->zadd('{z}key2', 5, 'zValue5') + ->zadd('{z}key2', 2, 'zValue2') + ->zInterStore('{z}Inter', array('{z}key1', '{z}key2')) + ->zRange('{z}key1', 0, -1) + ->zRange('{z}key2', 0, -1) + ->zRange('{z}Inter', 0, -1) + ->zUnionStore('{z}Union', array('{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; @@ -2963,12 +2933,12 @@ protected function sequence($mode) { $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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); // zkey1 contents - $this->assertTrue($ret[$i++] === array('zValue2', 'zValue5')); // zkey2 contents - $this->assertTrue($ret[$i++] === array('zValue5')); // zinter contents - $this->assertTrue($ret[$i++] === 5); // zUnion has 5 values (1,2,5,14,15) - $this->assertTrue($ret[$i++] === array('zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15')); // zunion contents - $this->assertTrue($ret[$i++] === 1); // added value to zkey5, with score 5 + $this->assertTrue($ret[$i++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); // {z}key1 contents + $this->assertTrue($ret[$i++] === array('zValue2', 'zValue5')); // {z}key2 contents + $this->assertTrue($ret[$i++] === array('zValue5')); // {z}inter contents + $this->assertTrue($ret[$i++] === 5); // {z}Union has 5 values (1,2,5,14,15) + $this->assertTrue($ret[$i++] === array('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. From 469e8e93774d75c9649075ede1549aeedb68928b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 3 Mar 2015 20:12:34 -0800 Subject: [PATCH 0441/1986] Various cluster fixes and Unit test updates * Updated the unit test suite to print each unit test method name we're running as we do so, and also with fancy colors :-) * Added functionality to handle failed transactions, where Redis will send us a multi bulk length of -1. This can happen because of an EXECABORT error, or because a watched key was modified during the transaction * Initialize serialized return value to NULL to avoid segfault * use strtol not atoi in our long response handler, to handle large values * Fixed our return value in multi responses * Fiexed type() command as cluster doesn't still have the '+' prefix when checking for what TYPE returns * Exists should return a BOOLEAN not 1/0 * Fixed sRandMember to work in the context of a MULTI block * Use "LINDEX" not "LGET" as "LGET" isn't a valid Redis command * Properly set our slot for the PFCOUNT command * Many unit test changes such that Redis and RedisCluster are happy using mostly the same ones. --- cluster_library.c | 56 +++-- cluster_library.h | 4 + redis_cluster.c | 47 +++-- redis_commands.c | 5 +- tests/RedisClusterTest.php | 65 ++++++ tests/RedisTest.php | 406 +++++++++++++++++++------------------ tests/TestRedis.php | 7 +- tests/TestSuite.php | 69 ++++++- 8 files changed, 417 insertions(+), 242 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index a9f06dcaec..d55a77b451 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -307,8 +307,16 @@ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { * failover inside a transaction, as we don't know if the transaction will only * be readonly commands, or contain write commands as well */ PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { - return cluster_send_slot(c, slot, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD)-1, + int retval; + + /* Send exec */ + retval = cluster_send_slot(c, slot, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD)-1, TYPE_MULTIBULK TSRMLS_CC); + + /* We'll either get a length corresponding to the number of commands sent to + * this node, or -1 in the case of EXECABORT or WATCH failure. */ + c->multi_len[slot] = c->reply_len > 0 ? 1 : -1; + } PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { @@ -949,7 +957,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type // For replies that will give us a numberic length, convert it if(*reply_type != TYPE_LINE) { - c->reply_len = atoi(c->line_reply); + c->reply_len = strtol(c->line_reply, NULL, 10); } else { c->reply_len = (long long)sz; } @@ -1373,7 +1381,7 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; - zval *z_ret; + zval *z_ret = NULL; // Make sure we can read the response if(c->reply_type != TYPE_BULK || @@ -1387,7 +1395,7 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, CLUSTER_RETURN_STRING(c, resp, c->reply_len); } else { if(CLUSTER_IS_ATOMIC(c)) { - return_value = z_ret; + *return_value = *z_ret; } else { add_next_index_zval(c->multi_resp, z_ret); } @@ -1476,14 +1484,16 @@ PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } // Switch on the type - if(strncmp(c->line_reply, "+string", 7)==0) { + if(strncmp (c->line_reply, "string", 6)==0) { CLUSTER_RETURN_LONG(c, REDIS_STRING); - } else if(strncmp(c->line_reply, "+set", 4)==0) { + } else if (strncmp(c->line_reply, "set", 3)==0) { CLUSTER_RETURN_LONG(c, REDIS_SET); - } else if(strncmp(c->line_reply, "+list", 5)==0) { + } else if (strncmp(c->line_reply, "list", 4)==0) { CLUSTER_RETURN_LONG(c, REDIS_LIST); - } else if(strncmp(c->line_reply, "+hash", 5)==0) { + } else if (strncmp(c->line_reply, "hash", 4)==0) { CLUSTER_RETURN_LONG(c, REDIS_HASH); + } else if (strncmp(c->line_reply, "zset", 4)==0) { + CLUSTER_RETURN_LONG(c, REDIS_ZSET); } else { CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND); } @@ -1925,19 +1935,25 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, clusterFoldItem *fi = c->multi_head; while(fi) { - /* Set the slot where we should look for responses. We don't allow - * failover inside a transaction, so it will be the master we have - * mapped. */ - c->cmd_slot = fi->slot; - c->cmd_sock = SLOT_SOCK(c, fi->slot); - - if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) { - zval_dtor(c->multi_resp); - efree(c->multi_resp); - RETURN_FALSE; - } + /* Make sure our transaction didn't fail here */ + if (c->multi_len[fi->slot] > 0) { + /* Set the slot where we should look for responses. We don't allow + * failover inside a transaction, so it will be the master we have + * mapped. */ + c->cmd_slot = fi->slot; + c->cmd_sock = SLOT_SOCK(c, fi->slot); + + if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) { + zval_dtor(c->multi_resp); + efree(c->multi_resp); + RETURN_FALSE; + } - fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx); + fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx); + } else { + /* Just add false */ + add_next_index_bool(c->multi_resp, 0); + } fi = fi->next; } diff --git a/cluster_library.h b/cluster_library.h index 457a96942c..18fff7d5b9 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -203,6 +203,10 @@ typedef struct redisCluster { clusterFoldItem *multi_head; clusterFoldItem *multi_curr; + /* When we issue EXEC to nodes, we need to keep track of how many replies + * we have, as this can fail for various reasons (EXECABORT, watch, etc.) */ + char multi_len[REDIS_CLUSTER_SLOTS]; + /* Variable to store MULTI response */ zval *multi_resp; diff --git a/redis_cluster.c b/redis_cluster.c index ca7019e798..cc16d1b82a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -953,7 +953,7 @@ PHP_METHOD(RedisCluster, getset) { /* {{{ proto int RedisCluster::exists(string key) */ PHP_METHOD(RedisCluster, exists) { - CLUSTER_PROCESS_KW_CMD("EXISTS", redis_key_cmd, cluster_long_resp, 1); + CLUSTER_PROCESS_KW_CMD("EXISTS", redis_key_cmd, cluster_1_resp, 1); } /* }}} */ @@ -1062,6 +1062,7 @@ PHP_METHOD(RedisCluster, spop) { /* {{{ proto string|array RedisCluster::srandmember(string key, [long count]) */ PHP_METHOD(RedisCluster, srandmember) { redisCluster *c = GET_CONTEXT(); + cluster_cb cb; char *cmd; int cmd_len; short slot; short have_count; @@ -1083,12 +1084,31 @@ PHP_METHOD(RedisCluster, srandmember) { // Clean up command efree(cmd); + cb = have_count ? cluster_mbulk_resp : cluster_bulk_resp; + if (CLUSTER_IS_ATOMIC(c)) { + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + void *ctx = NULL; + CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + RETURN_ZVAL(getThis(), 1, 0); + } + +/* + * cb = withscores ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp; + if (CLUSTER_IS_ATOMIC(c)) { + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + void *ctx = NULL; + CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + RETURN_ZVAL(getThis(), 1, 0); + }*/ + // Response type differs if we use WITHSCORES or not - if(have_count) { +/* if(have_count) { cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } + }*/ } /* {{{ proto string RedisCluster::strlen(string key) */ @@ -1490,7 +1510,7 @@ PHP_METHOD(RedisCluster, bitpos) { /* {{{ proto string Redis::lget(string key, long index) */ PHP_METHOD(RedisCluster, lget) { - CLUSTER_PROCESS_KW_CMD("LGET", redis_key_long_cmd, cluster_bulk_resp, 1); + CLUSTER_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, cluster_bulk_resp, 1); } /* }}} */ @@ -1577,6 +1597,7 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, zrange_cb fun) { redisCluster *c = GET_CONTEXT(); + cluster_cb cb; char *cmd; int cmd_len; short slot; int withscores; @@ -1594,11 +1615,13 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, efree(cmd); - // Response type differs if we use WITHSCORES or not - if(!withscores) { - cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cb = withscores ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp; + if (CLUSTER_IS_ATOMIC(c)) { + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { - cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + void *ctx = NULL; + CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + RETURN_ZVAL(getThis(), 1, 0); } } @@ -2162,8 +2185,7 @@ PHP_METHOD(RedisCluster, exec) { // Verify we are in fact in multi mode if(CLUSTER_IS_ATOMIC(c)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "RedisCluster is not in MULTI mode"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "RedisCluster is not in MULTI mode"); RETURN_FALSE; } @@ -2173,7 +2195,7 @@ PHP_METHOD(RedisCluster, exec) { if(SLOT_SOCK(c, fi->slot)->mode == MULTI) { if(cluster_send_exec(c, fi->slot TSRMLS_CC)<0) { cluster_abort_exec(c TSRMLS_CC); - + zend_throw_exception(redis_cluster_exception_ce, "Error processing EXEC across the cluster", 0 TSRMLS_CC); @@ -2204,8 +2226,7 @@ PHP_METHOD(RedisCluster, discard) { redisCluster *c = GET_CONTEXT(); if(CLUSTER_IS_ATOMIC(c)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Cluster is not in MULTI mode"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); RETURN_FALSE; } diff --git a/redis_commands.c b/redis_commands.c index d0f24800f4..bb0402b572 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1895,7 +1895,10 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, 1, "PFCOUNT", sizeof("PFCOUNT")-1); key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); - + + /* Hash our key */ + CMD_SET_SLOT(slot, key, key_len); + /* Cleanup */ if (key_free) efree(key); if (z_tmp) { diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index f9a8c5865b..181134eba1 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -17,6 +17,8 @@ public function testSortAsc() { return $this->markTestSkipped(); } public function testSortDesc() { return $this->markTestSkipped(); } public function testWait() { return $this->markTestSkipped(); } public function testSelect() { return $this->markTestSkipped(); } + public function testReconnectSelect() { return $this->markTestSkipped(); } + public function testIntrospection() { return $this->markTestSkipped(); } /* Skips for now, which need attention */ public function testClient() { return $this->markTestSkipped(); } @@ -110,5 +112,68 @@ public function testInfo() { } } + public function testTime() { + $time_arr = $this->redis->time("k:" . rand(1,100)); + $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && + strval(intval($time_arr[0])) === strval($time_arr[0]) && + strval(intval($time_arr[1])) === strval($time_arr[1])); + } + + public function testScan() { + $this->markTestSkipped(); // this will be implemented + } + + // 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() { + // PUBSUB CHANNELS ... + $result = $this->redis->pubsub("somekey", "channels", "*"); + $this->assertTrue(is_array($result)); + $result = $this->redis->pubsub("somekey", "channels"); + $this->assertTrue(is_array($result)); + + // PUBSUB NUMSUB + + $c1 = '{pubsub}-' . rand(1,100); + $c2 = '{pubsub}-' . rand(1,100); + + $result = $this->redis->pubsub("{pubsub}", "numsub", Array($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(Array($c1,$c2) as $channel) { + $this->assertTrue(isset($result[$channel])); + $this->assertEquals($result[$channel], 0); + } + + // PUBSUB NUMPAT + $result = $this->redis->pubsub("somekey", "numpat"); + $this->assertTrue(is_int($result)); + + // Invalid calls + $this->assertFalse($this->redis->pubsub("somekey", "notacommand")); + $this->assertFalse($this->redis->pubsub("somekey", "numsub", "not-an-array")); + } + + /* Unlike Redis proper, MsetNX won't always totally fail if all keys can't + * be set, but rather will only fail per-node when that is the case */ + public function testMSetNX() { + /* All of these keys should get set */ + $this->redis->del('x','y','z'); + $ret = $this->redis->msetnx(Array('x'=>'a','y'=>'b','z'=>'c')); + $this->assertTrue(is_array($ret)); + $this->assertEquals(array_sum($ret),count($ret)); + + /* Delete one key */ + $this->redis->del('x'); + $ret = $this->redis->msetnx(Array('x'=>'a','y'=>'b','z'=>'c')); + $this->assertTrue(is_array($ret)); + $this->assertEquals(array_sum($ret),1); + + $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index e3c77d8c25..ec3129bf78 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -357,8 +357,8 @@ public function testRename() { public function testRenameNx() { // strings $this->redis->del('{key}0', '{key}1'); - $this->redis->set('{key}0', '{val}0'); - $this->redis->set('{key}1', '{val}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'); @@ -427,14 +427,13 @@ public function testSetTimeout() { } public function testExpireAt() { - - $this->redis->del('key'); + $this->redis->del('key'); $this->redis->set('key', 'value'); - $now = time(NULL); - $this->redis->expireAt('key', $now + 1); - $this->assertEquals('value', $this->redis->get('key')); - sleep(2); - $this->assertEquals(FALSE, $this->redis->get('key')); + $now = time(NULL); + $this->redis->expireAt('key', $now + 1); + $this->assertEquals('value', $this->redis->get('key')); + sleep(2); + $this->assertEquals(FALSE, $this->redis->get('key')); } public function testSetEx() { @@ -540,28 +539,27 @@ public function testDecr() $this->redis->set('key', 5); $this->redis->decr('key'); - $this->assertEquals(4, (int)$this->redis->get('key')); + $this->assertEquals(4, (int)$this->redis->get('key')); $this->redis->decr('key'); - $this->assertEquals(3, (int)$this->redis->get('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', 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', 1); + $this->assertEquals(0, (int)$this->redis->get('key')); - $this->redis->decrBy('key', -10); - $this->assertEquals(10, (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')); + $this->redis->decr('key', 10); + $this->assertEquals(0, (int)$this->redis->get('key')); } public function testExists() { - $this->redis->del('key'); $this->assertFalse($this->redis->exists('key')); $this->redis->set('key', 'val'); @@ -1646,8 +1644,8 @@ public function testsDiffStore() { $xy = array_unique(array_diff($x, $y)); $this->assertEquals($count, count($xy)); foreach($xy as $i) { - $i = (int)$i; - $this->assertTrue($this->redis->sismember('k', $i)); + $i = (int)$i; + $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sDiffStore('{set}k', '{set}y', '{set}z'); // y - z @@ -1674,15 +1672,15 @@ public function testsDiffStore() { $this->assertTrue($this->redis->sismember('{set}k', $i)); } - $this->redis->del('x'); // x missing now + $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('y'); // x and y missing + $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('z'); // x, y, and z ALL missing + $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); } @@ -1899,16 +1897,16 @@ public function testMset() { } public function testMsetNX() { - $this->redis->del('x', 'y', 'z'); // remove x y z - $this->assertTrue(TRUE === $this->redis->msetnx(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z + $this->redis->del('x', 'y', 'z'); // remove x y z + $this->assertTrue(TRUE === $this->redis->msetnx(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check x y z - $this->redis->del('x'); // delete just x - $this->assertTrue(FALSE === $this->redis->msetnx(array('x' => 'A', 'y' => 'B', 'z' => 'C'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array(FALSE, 'b', 'c')); // check x y z + $this->redis->del('x'); // delete just x + $this->assertTrue(FALSE === $this->redis->msetnx(array('x' => 'A', 'y' => 'B', 'z' => 'C'))); // set x y z + $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array(FALSE, 'b', 'c')); // check x y z - $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE + $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE } public function testRpopLpush() { @@ -1947,8 +1945,8 @@ public function testBRpopLpush() { // 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(array() === $this->redis->lrange('x', 0, -1)); - $this->assertTrue(array() === $this->redis->lrange('y', 0, -1)); + $this->assertTrue(array() === $this->redis->lrange('{list}x', 0, -1)); + $this->assertTrue(array() === $this->redis->lrange('{list}y', 0, -1)); } public function testZAddFirstArg() { @@ -2082,7 +2080,7 @@ public function testZX() { // test weighted zUnion $this->redis->del('{zset}Z'); $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', array('{zset}1', '{zset}2'), array(1, 1))); - $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('keyZ', 0, -1)); + $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->zRemRangeByScore('{zset}Z', 0, 10); $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', array('{zset}1', '{zset}2'), array(5, 1))); @@ -2172,7 +2170,7 @@ public function testZX() { $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}1', 3, 'val3'); $this->redis->zAdd('{zset}2', 2, 'val1'); $this->redis->zAdd('{zset}2', 3, 'val3'); @@ -2182,7 +2180,7 @@ public function testZX() { $this->redis->del('{zset}I'); $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2'))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('keyI', 0, -1)); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('{zset}I', 0, -1)); // Union on non existing keys $this->assertTrue(0 === $this->redis->zInterStore('{zset}X', array('{zset}X', '{zset}Y'))); @@ -2211,7 +2209,7 @@ public function testZX() { $this->redis->del('{zset}I'); $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2'), array(1, 1))); - $this->assertTrue(array('{zset}1', '{zset}3') === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2', '{zset}3'), array(1, 5, 1), 'min')); @@ -2427,45 +2425,40 @@ public function testObject() { } 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->sequence(Redis::MULTI); + $this->differentType(Redis::MULTI); - $this->redis->set('x', '42'); + // 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->assertTrue(TRUE === $this->redis->watch('x')); - $ret = $this->redis->multi() - ->get('x') - ->exec(); + $this->redis->set('x', '42'); - // successful transaction - $this->assertTrue($ret === array('42')); + $this->assertTrue(TRUE === $this->redis->watch('x')); + $ret = $this->redis->multi()->get('x')->exec(); - // failed transaction - $this->redis->watch('x'); + // successful transaction + $this->assertTrue($ret === array('42')); - $r = $this->newInstance(); // new instance, modifying `x'. - $r->incr('x'); + // 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. + $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 + // 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 === array('44')); // succeeded since we've cancel the WATCH command. + $ret = $this->redis->multi()->get('x')->exec(); + $this->assertTrue($ret === array('44')); // succeeded since we've cancel the WATCH command. } public function testPipeline() { @@ -2500,7 +2493,7 @@ protected function sequence($mode) { $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer $ret = $this->redis->multi($mode) ->del('{key}1') - ->set('{key1}', 'value1') + ->set('{key}1', 'value1') ->get('{key}1') ->getSet('{key}1', 'value2') ->get('{key}1') @@ -2519,8 +2512,8 @@ protected function sequence($mode) { ->get('{key}2') ->exec(); - $this->assertTrue(is_array($ret)); $i = 0; + $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); $this->assertTrue($ret[$i++] == TRUE); $this->assertTrue($ret[$i++] == 'value1'); @@ -2566,8 +2559,8 @@ protected function sequence($mode) { $this->redis->del('key'); $ret = $this->redis->multi($mode) ->ttl('key') - ->mget(array('key1', 'key2', 'key3')) - ->mset(array('key3' => 'value3', 'key4' => 'value4')) + ->mget(array('{key}1', '{key}2', '{key}3')) + ->mset(array('{key}3' => 'value3', 'key4' => 'value4')) ->set('key', 'value') ->expire('key', 5) ->ttl('key') @@ -2595,7 +2588,7 @@ protected function sequence($mode) { ->lpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') ->lpush('{list}lkey', 'lvalue') - ->rpoplpush('{list}lkey', 'lDest') + ->rpoplpush('{list}lkey', '{list}lDest') ->lrange('{list}lDest', 0, -1) ->lpop('{list}lkey') ->llen('{list}lkey') @@ -2675,6 +2668,7 @@ protected function sequence($mode) { ->get('{key}2') ->decrby('{key}2', 5) ->get('{key}2') + ->set('{key}3', 'value3') ->exec(); $i = 0; @@ -2697,41 +2691,35 @@ protected function sequence($mode) { $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('key1') - ->del('key2') - ->set('key1', 'val1') - ->setnx('key1', 'valX') - ->setnx('key2', 'valX') - ->exists('key1') - ->exists('key3') - ->ping() + ->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] == FALSE); - $this->assertTrue($ret[4] == TRUE); + $this->assertTrue($ret[3] == TRUE); + $this->assertTrue($ret[4] == FALSE); $this->assertTrue($ret[5] == TRUE); - $this->assertTrue($ret[6] == FALSE); - $this->assertTrue($ret[7] == '+PONG'); - - $ret = $this->redis->multi($mode) - ->randomKey() - ->exec(); - - $this->assertTrue(is_array($ret) && count($ret) === 1); - $this->assertTrue(is_string($ret[0])); + $this->assertTrue($ret[6] == TRUE); + $this->assertTrue($ret[7] == FALSE); // ttl, mget, mset, msetnx, expire, expireAt $ret = $this->redis->multi($mode) ->ttl('key') - ->mget(array('key1', 'key2', 'key3')) - ->mset(array('key3' => 'value3', 'key4' => 'value4')) + ->mget(array('{key}1', '{key}2', '{key}3')) + ->mset(array('{key}3' => 'value3', '{key}4' => 'value4')) ->set('key', 'value') ->expire('key', 5) ->ttl('key') @@ -2892,7 +2880,7 @@ protected function sequence($mode) { ->zadd('{z}key1', 15, 'zValue15') ->zRemRangeByScore('{z}key1', 11, 13) ->zrange('{z}key1', 0, -1) - ->zReverseRange('{z}key1', 0, -1) + ->zRevRange('{z}key1', 0, -1) ->zRangeByScore('{z}key1', 1, 6) ->zCard('{z}key1') ->zScore('{z}key1', 'zValue15') @@ -3015,7 +3003,9 @@ protected function sequence($mode) { protected function differentType($mode) { // string - $key = 'string'; + $key = '{hash}string'; + $dkey = '{hash}' . __FUNCTION__; + $ret = $this->redis->multi($mode) ->del($key) ->set($key, 'value') @@ -3032,18 +3022,20 @@ protected function differentType($mode) { ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) - ->rPoplPush($key, __FUNCTION__ . 'lkey1') - + ->rPoplPush($key, $dkey . 'lkey1') + // sets I/F ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') ->sPop($key) - ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sMove($key, $dkey . 'skey1', 'sValue1') + ->scard($key) ->sismember($key, 'sValue1') - ->sInter($key, __FUNCTION__ . 'skey2') - ->sUnion($key, __FUNCTION__ . 'skey4') - ->sDiff($key, __FUNCTION__ . 'skey7') + ->sInter($key, $dkey . 'skey2') + + ->sUnion($key, $dkey . 'skey4') + ->sDiff($key, $dkey . 'skey7') ->sMembers($key) ->sRandMember($key) @@ -3054,7 +3046,7 @@ protected function differentType($mode) { ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') ->zRange($key, 0, -1) - ->zReverseRange($key, 0, -1) + ->zRevRange($key, 0, -1) ->zRangeByScore($key, 1, 2) ->zCount($key, 0, -1) ->zCard($key) @@ -3076,7 +3068,7 @@ protected function differentType($mode) { ->hGetAll($key) ->exec(); - + $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); // delete @@ -3136,7 +3128,8 @@ protected function differentType($mode) { $this->assertEquals($i, count($ret)); // list - $key = 'list'; + $key = '{hash}list'; + $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->lpush($key, 'lvalue') @@ -3145,7 +3138,7 @@ protected function differentType($mode) { ->get($key) ->getset($key, 'value2') ->append($key, 'append') - ->substr($key, 0, 8) + ->getRange($key, 0, 8) ->mget(array($key)) ->incr($key) ->incrBy($key, 1) @@ -3156,12 +3149,12 @@ protected function differentType($mode) { ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') ->sPop($key) - ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sMove($key, $dkey . 'skey1', 'sValue1') ->scard($key) ->sismember($key, 'sValue1') - ->sInter($key, __FUNCTION__ . 'skey2') - ->sUnion($key, __FUNCTION__ . 'skey4') - ->sDiff($key, __FUNCTION__ . 'skey7') + ->sInter($key, $dkey . 'skey2') + ->sUnion($key, $dkey . 'skey4') + ->sDiff($key, $dkey . 'skey7') ->sMembers($key) ->sRandMember($key) @@ -3172,7 +3165,7 @@ protected function differentType($mode) { ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') ->zRange($key, 0, -1) - ->zReverseRange($key, 0, -1) + ->zRevRange($key, 0, -1) ->zRangeByScore($key, 1, 2) ->zCount($key, 0, -1) ->zCard($key) @@ -3203,7 +3196,7 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // get $this->assertTrue($ret[$i++] === FALSE); // getset $this->assertTrue($ret[$i++] === FALSE); // append - $this->assertTrue($ret[$i++] === FALSE); // substr + $this->assertTrue($ret[$i++] === FALSE); // getRange $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget $i++; $this->assertTrue($ret[$i++] === FALSE); // incr @@ -3252,7 +3245,8 @@ protected function differentType($mode) { $this->assertEquals($i, count($ret)); // set - $key = 'set'; + $key = '{hash}set'; + $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->sAdd($key, 'sValue') @@ -3261,7 +3255,7 @@ protected function differentType($mode) { ->get($key) ->getset($key, 'value2') ->append($key, 'append') - ->substr($key, 0, 8) + ->getRange($key, 0, 8) ->mget(array($key)) ->incr($key) ->incrBy($key, 1) @@ -3280,7 +3274,7 @@ protected function differentType($mode) { ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) - ->rPoplPush($key, __FUNCTION__ . 'lkey1') + ->rPoplPush($key, $dkey . 'lkey1') // sorted sets I/F ->zAdd($key, 1, 'zValue1') @@ -3289,7 +3283,7 @@ protected function differentType($mode) { ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') ->zRange($key, 0, -1) - ->zReverseRange($key, 0, -1) + ->zRevRange($key, 0, -1) ->zRangeByScore($key, 1, 2) ->zCount($key, 0, -1) ->zCard($key) @@ -3320,7 +3314,7 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // get $this->assertTrue($ret[$i++] === FALSE); // getset $this->assertTrue($ret[$i++] === FALSE); // append - $this->assertTrue($ret[$i++] === FALSE); // substr + $this->assertTrue($ret[$i++] === FALSE); // getRange $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget $i++; $this->assertTrue($ret[$i++] === FALSE); // incr @@ -3370,7 +3364,8 @@ protected function differentType($mode) { $this->assertEquals($i, count($ret)); // sorted set - $key = 'sortedset'; + $key = '{hash}sortedset'; + $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->zAdd($key, 0, 'zValue') @@ -3379,7 +3374,7 @@ protected function differentType($mode) { ->get($key) ->getset($key, 'value2') ->append($key, 'append') - ->substr($key, 0, 8) + ->getRange($key, 0, 8) ->mget(array($key)) ->incr($key) ->incrBy($key, 1) @@ -3398,18 +3393,18 @@ protected function differentType($mode) { ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) - ->rPoplPush($key, __FUNCTION__ . 'lkey1') + ->rPoplPush($key, $dkey . 'lkey1') // sets I/F ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') ->sPop($key) - ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sMove($key, $dkey . 'skey1', 'sValue1') ->scard($key) ->sismember($key, 'sValue1') - ->sInter($key, __FUNCTION__ . 'skey2') - ->sUnion($key, __FUNCTION__ . 'skey4') - ->sDiff($key, __FUNCTION__ . 'skey7') + ->sInter($key, $dkey . 'skey2') + ->sUnion($key, $dkey . 'skey4') + ->sDiff($key, $dkey . 'skey7') ->sMembers($key) ->sRandMember($key) @@ -3436,7 +3431,7 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // get $this->assertTrue($ret[$i++] === FALSE); // getset $this->assertTrue($ret[$i++] === FALSE); // append - $this->assertTrue($ret[$i++] === FALSE); // substr + $this->assertTrue($ret[$i++] === FALSE); // getRange $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget $i++; $this->assertTrue($ret[$i++] === FALSE); // incr @@ -3484,7 +3479,8 @@ protected function differentType($mode) { $this->assertEquals($i, count($ret)); // hash - $key = 'hash'; + $key = '{hash}hash'; + $dkey = '{hash}' . __FUNCTION__; $ret = $this->redis->multi($mode) ->del($key) ->hset($key, 'key1', 'hValue') @@ -3493,7 +3489,7 @@ protected function differentType($mode) { ->get($key) ->getset($key, 'value2') ->append($key, 'append') - ->substr($key, 0, 8) + ->getRange($key, 0, 8) ->mget(array($key)) ->incr($key) ->incrBy($key, 1) @@ -3512,18 +3508,18 @@ protected function differentType($mode) { ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) - ->rPoplPush($key, __FUNCTION__ . 'lkey1') + ->rPoplPush($key, $dkey . 'lkey1') // sets I/F ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') ->sPop($key) - ->sMove($key, __FUNCTION__ . 'skey1', 'sValue1') + ->sMove($key, $dkey . 'skey1', 'sValue1') ->scard($key) ->sismember($key, 'sValue1') - ->sInter($key, __FUNCTION__ . 'skey2') - ->sUnion($key, __FUNCTION__ . 'skey4') - ->sDiff($key, __FUNCTION__ . 'skey7') + ->sInter($key, $dkey . 'skey2') + ->sUnion($key, $dkey . 'skey4') + ->sDiff($key, $dkey . 'skey7') ->sMembers($key) ->sRandMember($key) @@ -3534,7 +3530,7 @@ protected function differentType($mode) { ->zRank($key, 'zValue1') ->zRevRank($key, 'zValue1') ->zRange($key, 0, -1) - ->zReverseRange($key, 0, -1) + ->zRevRange($key, 0, -1) ->zRangeByScore($key, 1, 2) ->zCount($key, 0, -1) ->zCard($key) @@ -3552,7 +3548,7 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // get $this->assertTrue($ret[$i++] === FALSE); // getset $this->assertTrue($ret[$i++] === FALSE); // append - $this->assertTrue($ret[$i++] === FALSE); // substr + $this->assertTrue($ret[$i++] === FALSE); // getRange $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget $i++; $this->assertTrue($ret[$i++] === FALSE); // incr @@ -3603,7 +3599,9 @@ protected function differentType($mode) { } public function testDifferentTypeString() { - $key = 'string'; + $key = '{hash}string'; + $dkey = '{hash}' . __FUNCTION__; + $this->redis->del($key); $this->assertEquals(TRUE, $this->redis->set($key, 'value')); @@ -3619,18 +3617,18 @@ public function testDifferentTypeString() { $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, __FUNCTION__ . 'lkey1')); + $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, __FUNCTION__ . 'skey1', 'sValue1')); + $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, __FUNCTION__ . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . 'skey7')); + $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)); @@ -3641,7 +3639,7 @@ public function testDifferentTypeString() { $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); @@ -3664,7 +3662,9 @@ public function testDifferentTypeString() { } public function testDifferentTypeList() { - $key = 'list'; + $key = '{hash}list'; + $dkey = '{hash}' . __FUNCTION__; + $this->redis->del($key); $this->assertEquals(1, $this->redis->lPush($key, 'value')); @@ -3672,7 +3672,7 @@ public function testDifferentTypeList() { $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->substr($key, 0, 8)); + $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); @@ -3683,12 +3683,12 @@ public function testDifferentTypeList() { $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, __FUNCTION__ . 'skey1', 'sValue1')); + $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, __FUNCTION__ . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . 'skey7')); + $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)); @@ -3699,7 +3699,7 @@ public function testDifferentTypeList() { $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); @@ -3722,7 +3722,8 @@ public function testDifferentTypeList() { } public function testDifferentTypeSet() { - $key = 'set'; + $key = '{hash}set'; + $dkey = '{hash}' . __FUNCTION__; $this->redis->del($key); $this->assertEquals(1, $this->redis->sAdd($key, 'value')); @@ -3730,7 +3731,7 @@ public function testDifferentTypeSet() { $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->substr($key, 0, 8)); + $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); @@ -3749,7 +3750,7 @@ public function testDifferentTypeSet() { $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, __FUNCTION__ . 'lkey1')); + $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); // sorted sets I/F $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); @@ -3758,7 +3759,7 @@ public function testDifferentTypeSet() { $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); @@ -3781,7 +3782,9 @@ public function testDifferentTypeSet() { } public function testDifferentTypeSortedSet() { - $key = 'sortedset'; + $key = '{hash}sortedset'; + $dkey = '{hash}' . __FUNCTION__; + $this->redis->del($key); $this->assertEquals(1, $this->redis->zAdd($key, 0, 'value')); @@ -3789,7 +3792,7 @@ public function testDifferentTypeSortedSet() { $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->substr($key, 0, 8)); + $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); @@ -3808,18 +3811,18 @@ public function testDifferentTypeSortedSet() { $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, __FUNCTION__ . 'lkey1')); + $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, __FUNCTION__ . 'skey1', 'sValue1')); + $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, __FUNCTION__ . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . 'skey7')); + $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)); @@ -3838,7 +3841,9 @@ public function testDifferentTypeSortedSet() { } public function testDifferentTypeHash() { - $key = 'hash'; + $key = '{hash}hash'; + $dkey = '{hash}hash'; + $this->redis->del($key); $this->assertEquals(1, $this->redis->hSet($key, 'key', 'value')); @@ -3846,7 +3851,7 @@ public function testDifferentTypeHash() { $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->substr($key, 0, 8)); + $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); @@ -3865,18 +3870,18 @@ public function testDifferentTypeHash() { $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, __FUNCTION__ . 'lkey1')); + $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, __FUNCTION__ . 'skey1', 'sValue1')); + $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, __FUNCTION__ . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, __FUNCTION__ . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, __FUNCTION__ . 'skey7')); + $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)); @@ -3887,7 +3892,7 @@ public function testDifferentTypeHash() { $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zReverseRange($key, 0, -1)); + $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->zCard($key)); @@ -3899,7 +3904,7 @@ public function testDifferentTypeHash() { public function testSerializerPHP() { $this->checkSerializer(Redis::SERIALIZER_PHP); - + // with prefix $this->redis->setOption(Redis::OPT_PREFIX, "test:"); $this->checkSerializer(Redis::SERIALIZER_PHP); @@ -3933,7 +3938,7 @@ private function checkSerializer($mode) { $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)); @@ -3942,11 +3947,11 @@ private function checkSerializer($mode) { $this->assertTrue($a[1] === $this->redis->lGet('key', 1)); $this->assertTrue($a[2] === $this->redis->lGet('key', 2)); $this->assertTrue($a[3] === $this->redis->lGet('key', 3)); - + // lrem $this->assertTrue($this->redis->lrem('key', $a[3]) === 1); $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1)); - + // lSet $a[0] = array('k' => 'v'); // update $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); @@ -3955,49 +3960,49 @@ private function checkSerializer($mode) { // lInsert $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], array(1,2,3)) === 4); $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], array(4,5,6)) === 5); - + $a = array(array(1,2,3), $a[0], array(4,5,6), $a[1], $a[2]); $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); // sAdd - $this->redis->del('key'); + $this->redis->del('{set}key'); $s = array(1,'a', array(1,2,3), array('k' => 'v')); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[0])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[1])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[2])); - $this->assertTrue(1 === $this->redis->sAdd('key', $s[3])); - + $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[0])); + $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[1])); + $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[2])); + $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[3])); + // variadic sAdd $this->redis->del('k'); $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); // srem - $this->assertTrue(1 === $this->redis->srem('key', $s[3])); - $this->assertTrue(0 === $this->redis->srem('key', $s[3])); + $this->assertTrue(1 === $this->redis->srem('{set}key', $s[3])); + $this->assertTrue(0 === $this->redis->srem('{set}key', $s[3])); + // variadic $this->redis->del('k'); $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); $this->assertTrue(FALSE === $this->redis->exists('k')); - + // sismember - $this->assertTrue(TRUE === $this->redis->sismember('key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sismember('key', $s[1])); - $this->assertTrue(TRUE === $this->redis->sismember('key', $s[2])); - $this->assertTrue(FALSE === $this->redis->sismember('key', $s[3])); + $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('tmp'); - $this->redis->sMove('key', 'tmp', $s[0]); - $this->assertTrue(FALSE === $this->redis->sismember('key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sismember('tmp', $s[0])); + $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 = array('z0', array('k' => 'v'), FALSE, NULL); $this->redis->del('key'); @@ -4094,13 +4099,12 @@ private function checkSerializer($mode) { $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', array('x' => 'y')); - $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->getMultiple(array('a', 'b', 'c', 'd'))); + $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->mGet(array('a', 'b', 'c', 'd'))); // pipeline if ($this->havePipeline()) { @@ -4109,7 +4113,7 @@ private function checkSerializer($mode) { // multi-exec $this->sequence(Redis::MULTI); - + // keys $this->assertTrue(is_array($this->redis->keys('*'))); @@ -4344,7 +4348,7 @@ public function testEval() { */ $args_script = "return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}"; - $args_args = Array('k1','k2','k3','v1','v2','v3'); + $args_args = Array('{k}1','{k}2','{k}3','v1','v2','v3'); $args_result = $this->redis->eval($args_script, $args_args, 3); $this->assertTrue($args_result === $args_args); @@ -4750,7 +4754,7 @@ public function testPFCommands() { // Now add for each key for($i=0;$i<$i_keys;$i++) { - $str_key = "key:$i"; + $str_key = "{key}:$i"; $arr_keys[] = $str_key; // Clean up this key @@ -4771,19 +4775,19 @@ public function testPFCommands() { } // Clean up merge key - $this->redis->del('pf-merge-key'); + $this->redis->del('pf-merge-{key}'); // Merge the counters - $this->assertTrue($this->redis->pfmerge('pf-merge-key', $arr_keys)); + $this->assertTrue($this->redis->pfmerge('pf-merge-{key}', $arr_keys)); // Validate our merged count - $i_redis_card = $this->redis->pfcount('pf-merge-key'); + $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'); + $this->redis->del('pf-merge-{key}'); } } } diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 57702994dc..d452204251 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -24,11 +24,12 @@ echo "Note: these tests might take up to a minute. Don't worry :-)\n"; /* Depending on the classes being tested, run our tests on it */ +echo "Testing class "; if ($str_class == 'redis') { - echo "Testing Redis "; + echo TestSuite::make_bold("Redis") . "\n"; exit(TestSuite::run("Redis_Test", $str_filter)); } else if ($str_class == 'redisarray') { - echo "Testing RedisArray "; + echo TestSuite::make_bold("RedisArray") . "\n"; global $useIndex; foreach(array(true, false) as $useIndex) { echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; @@ -40,7 +41,7 @@ run_tests('Redis_Distributor_Test'); } } else { - echo "Testing RedisCluster "; + echo TestSuite::make_bold("RedisCluster") . "\n"; exit(TestSuite::run("Redis_Cluster_Test", $str_filter)); } ?> diff --git a/tests/TestSuite.php b/tests/TestSuite.php index b38ea791eb..f4f156f339 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -3,9 +3,36 @@ // phpunit is such a pain to install, we're going with pure-PHP here. class TestSuite { + private static $BOLD_ON = "\033[1m"; + private static $BOLD_OFF = "\033[0m"; + + private static $BLACK = "\033[0;30m"; + private static $DARKGRAY = "\033[1;30m"; + private static $BLUE = "\033[0;34m"; + private static $PURPLE = "\033[0;35m"; + private static $GREEN = "\033[0;32m"; + private static $YELLOW = "\033[0;33m"; + private static $RED = "\033[0;31m"; + public static $errors = array(); public static $warnings = array(); + public static function make_bold($str_msg) { + return self::$BOLD_ON . $str_msg . self::$BOLD_OFF; + } + + public static function make_success($str_msg) { + return self::$GREEN . $str_msg . self::$BOLD_OFF; + } + + public static function make_fail($str_msg) { + return self::$RED . $str_msg . self::$BOLD_OFF; + } + + public static function make_warning($str_msg) { + return self::$YELLOW . $str_msg . self::$BOLD_OFF; + } + protected function assertFalse($bool) { $this->assertTrue(!$bool); } @@ -47,6 +74,25 @@ protected function markTestSkipped($msg='') { throw new Exception($msg); } + private function getMaxTestLen($arr_methods, $str_limit) { + $i_result = 0; + + $str_limit = strtolower($str_limit); + foreach ($arr_methods as $obj_method) { + $str_name = strtolower($obj_method->name); + + if (substr($str_name, 0, 4) != 'test') + continue; + if ($str_limit && !strstr($str_name, $str_limit)) + continue; + + if (strlen($str_name) > $i_result) { + $i_result = strlen($str_name); + } + } + return $i_result; + } + public static function run($className, $str_limit = NULL) { /* Lowercase our limit arg if we're passed one */ $str_limit = $str_limit ? strtolower($str_limit) : $str_limit; @@ -54,31 +100,46 @@ public static function run($className, $str_limit = NULL) { $rc = new ReflectionClass($className); $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC); + $i_max_len = self::getMaxTestLen($methods, $str_limit); + foreach($methods as $m) { $name = $m->name; if(substr($name, 0, 4) !== 'test') continue; - + /* If we're trying to limit to a specific test and can't match the * substring, skip */ if ($str_limit && strstr(strtolower($name), $str_limit)===FALSE) { continue; } + $str_out_name = str_pad($name, $i_max_len + 1); + echo self::make_bold($str_out_name); + $count = count($className::$errors); $rt = new $className; try { $rt->setUp(); $rt->$name(); - echo ($count === count($className::$errors)) ? "." : "F"; + + if ($count === count($className::$errors)) { + $str_msg = self::make_success('PASSED'); + } else { + $str_msg = self::make_fail('FAILED'); + } + //echo ($count === count($className::$errors)) ? "." : "F"; } catch (Exception $e) { if ($e instanceof RedisException) { $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n"; - echo 'F'; + $str_msg = self::make_fail('FAILED'); + //echo 'F'; } else { - echo 'S'; + $str_msg = self::make_warning('SKIPPED'); + //echo 'S'; } } + + echo "[" . $str_msg . "]\n"; } echo "\n"; echo implode('', $className::$warnings); From 54a521e2271641dd429c09ef3678e77ae4ab9bc2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 3 Mar 2015 21:02:32 -0800 Subject: [PATCH 0442/1986] Cluster fixes * STRLEN should use cluster_long_response * Updated multibulk processing to return an empty array on *-1 --- cluster_library.c | 20 +++++++++++--------- redis_cluster.c | 2 +- tests/RedisTest.php | 45 +++++++-------------------------------------- 3 files changed, 19 insertions(+), 48 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index d55a77b451..b49afc7209 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1785,21 +1785,23 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, { zval *z_result; - // Verify our reply type byte is correct and that this isn't a NULL - // (e.g. -1 count) multi bulk response. - if(c->reply_type != TYPE_MULTIBULK || c->reply_len == -1) { + /* Return FALSE if we didn't get a multi-bulk response */ + if (c->reply_type != TYPE_MULTIBULK) { CLUSTER_RETURN_FALSE(c); } - // Allocate array + /* Allocate our array */ MAKE_STD_ZVAL(z_result); array_init(z_result); - // Call our specified callback - if(cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC)==FAILURE) { - zval_dtor(z_result); - FREE_ZVAL(z_result); - CLUSTER_RETURN_FALSE(c); + /* Consume replies as long as there are more than zero */ + if (c->reply_len > 0) { + /* Call our specified callback */ + if (cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC)==FAILURE) { + zval_dtor(z_result); + FREE_ZVAL(z_result); + CLUSTER_RETURN_FALSE(c); + } } // Success, make this array our return value diff --git a/redis_cluster.c b/redis_cluster.c index cc16d1b82a..e23f4c275b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1113,7 +1113,7 @@ PHP_METHOD(RedisCluster, srandmember) { /* {{{ proto string RedisCluster::strlen(string key) */ PHP_METHOD(RedisCluster, strlen) { - CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_bulk_resp, 1); + CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_long_resp, 1); } /* {{{ proto long RedisCluster::lpush(string key, string val1, ... valN) */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index ec3129bf78..1280022d92 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -670,6 +670,7 @@ public function testStr() { $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'); @@ -757,8 +758,7 @@ public function testrPop() } public function testblockingPop() { - - // non blocking blPop, brPop + // non blocking blPop, brPop $this->redis->del('list'); $this->redis->lPush('list', 'val1'); $this->redis->lPush('list', 'val2'); @@ -768,44 +768,13 @@ public function testblockingPop() { $this->redis->del('list'); $this->redis->lPush('list', 'val1'); $this->redis->lPush('list', 'val2'); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val1')); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val2')); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val1')); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array('list', 'val2')); - // blocking blpop, brpop + // blocking blpop, brpop $this->redis->del('list'); - $this->assertTrue($this->redis->blPop(array('list'), 1) === array()); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array()); - - // TODO: fix this broken test. -// $this->redis->del('list'); -// $params = array( -// 0 => array("pipe", "r"), -// 1 => array("pipe", "w"), -// 2 => array("file", "/dev/null", "w") -// ); -// if(function_exists('proc_open')) { -// $env = array('PHPREDIS_key' =>'list', 'PHPREDIS_value' => 'value'); -// $process = proc_open('php', $params, $pipes, '/tmp', $env); -// -// if (is_resource($process)) { -// fwrite($pipes[0], 'connect("'.self::HOST.'", '.self::PORT.'); -// if("'.addslashes(self::AUTH).'") { -// $r->auth("'.addslashes(self::AUTH).'"); -// } -// $r->lPush($_ENV["PHPREDIS_key"], $_ENV["PHPREDIS_value"]); -// ?' . '>'); -// -// fclose($pipes[0]); -// fclose($pipes[1]); -// $re = proc_close($process); -// -// $this->assertTrue($this->redis->blPop(array('list'), 5) === array("list", "value")); -// } -// } - + $this->assertTrue($this->redis->blPop(array('list'), 1) === array()); + $this->assertTrue($this->redis->brPop(array('list'), 1) === array()); } public function testllen() From a70ba7f2e9b85db48309eac9f459c4713814e275 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Mar 2015 07:52:33 -0800 Subject: [PATCH 0443/1986] Multi-bulk serialization fixes * Push our 'flag' socket's serialization settings into the multti-bulk reply loop processing mechanism. * Reset our serialization variable to NULL per loop iteration to fix a memory leak. --- cluster_library.c | 29 +++++++++++++++++++++++++---- redis_cluster.c | 17 ----------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index b49afc7209..414a56e50a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1381,7 +1381,6 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; - zval *z_ret = NULL; // Make sure we can read the response if(c->reply_type != TYPE_BULK || @@ -1390,6 +1389,25 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, CLUSTER_RETURN_FALSE(c); } + if (CLUSTER_IS_ATOMIC(c)) { + if (redis_unserialize(c->flags, resp, c->reply_len, &return_value + TSRMLS_CC) == 0) + { + CLUSTER_RETURN_STRING(c, resp, c->reply_len); + } else { + efree(resp); + } + } else { + zval *z = NULL; + if (redis_unserialize(c->flags, resp, c->reply_len, &z TSRMLS_CC)) { + efree(resp); + add_next_index_zval(c->multi_resp, z); + } else { + add_next_index_stringl(c->multi_resp, resp, c->reply_len, 0); + } + } + + /* // Return the string if we can unserialize it if(redis_unserialize(c->flags, resp, c->reply_len, &z_ret TSRMLS_CC)==0) { CLUSTER_RETURN_STRING(c, resp, c->reply_len); @@ -1400,7 +1418,7 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, add_next_index_zval(c->multi_resp, z_ret); } efree(resp); - } + }*/ } /* Bulk response where we expect a double */ @@ -1796,6 +1814,9 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, /* Consume replies as long as there are more than zero */ if (c->reply_len > 0) { + /* Push serialization settings from the cluster into our socket */ + c->cmd_sock->serializer = c->flags->serializer; + /* Call our specified callback */ if (cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC)==FAILURE) { zval_dtor(z_result); @@ -2168,7 +2189,6 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, { char *line; int line_len; - zval *z; // Iterate over the lines we have to process while(count--) { @@ -2177,6 +2197,7 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, if(line == NULL) return FAILURE; if(line_len > 0) { + zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_next_index_zval(z_result, z); efree(line); @@ -2199,7 +2220,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, char *line, *key; int line_len, key_len; long long idx=0; - zval *z; + zval *z = NULL; // Our count wil need to be divisible by 2 if(count % 2 != 0) { diff --git a/redis_cluster.c b/redis_cluster.c index e23f4c275b..6d54f64bea 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1092,23 +1092,6 @@ PHP_METHOD(RedisCluster, srandmember) { CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); RETURN_ZVAL(getThis(), 1, 0); } - -/* - * cb = withscores ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp; - if (CLUSTER_IS_ATOMIC(c)) { - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); - RETURN_ZVAL(getThis(), 1, 0); - }*/ - - // Response type differs if we use WITHSCORES or not -/* if(have_count) { - cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - }*/ } /* {{{ proto string RedisCluster::strlen(string key) */ From 7a106d51d6bd701bd56826f3d23583ce9879004e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Mar 2015 13:54:44 -0800 Subject: [PATCH 0444/1986] Response handler fixes * Fixed serialization for various multi-bulk responses * Properly handle NULL BULK replies for variant replies --- cluster_library.c | 305 ++++++++++++++++++++----------------- cluster_library.h | 4 +- redis_cluster.c | 5 +- redis_commands.c | 5 +- tests/RedisClusterTest.php | 24 +++ tests/RedisTest.php | 135 ++++++++-------- 6 files changed, 263 insertions(+), 215 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 414a56e50a..9b8b52b390 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -12,7 +12,7 @@ extern zend_class_entry *redis_cluster_exception_ce; static void cluster_dump_nodes(redisCluster *c) { redisClusterNode **pp, *p; - + for(zend_hash_internal_pointer_reset(c->nodes); zend_hash_has_more_elements(c->nodes)==SUCCESS; zend_hash_move_forward(c->nodes)) @@ -23,7 +23,7 @@ static void cluster_dump_nodes(redisCluster *c) { const char *slave = (p->slave) ? "slave" : "master"; php_printf("%d %s %d %d", p->sock->port, slave,p->sock->prefix_len, p->slot); - + php_printf("\n"); } } @@ -41,7 +41,7 @@ static void cluster_log(char *fmt, ...) } /* Debug function to dump a clusterReply structure recursively */ -static void dump_reply(clusterReply *reply, int indent) { +static void dump_reply(clusterReply *reply, int indent) { smart_str buf = {0}; int i; @@ -97,7 +97,7 @@ void cluster_free_reply(clusterReply *reply, int free_data) { case TYPE_ERR: case TYPE_LINE: case TYPE_BULK: - if(free_data) + if(free_data) efree(reply->str); break; case TYPE_MULTIBULK: @@ -109,11 +109,11 @@ void cluster_free_reply(clusterReply *reply, int free_data) { default: break; } - efree(reply); + efree(reply); } static void -cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, +cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, clusterReply **element, int *err TSRMLS_DC) { size_t idx = 0; @@ -124,13 +124,14 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, while(elements-- > 0) { element[idx] = ecalloc(1, sizeof(clusterReply)); r = element[idx]; - + // Bomb out, flag error condition on a communication failure if(redis_read_reply_type(sock, &r->type, &len TSRMLS_CC)<0) { - *err = 1; + *err = 1; return; } - + + /* Set our reply len */ r->len = len; switch(r->type) { @@ -145,16 +146,18 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, r->integer = len; break; case TYPE_BULK: - r->str = redis_sock_read_bulk_reply(sock,r->len TSRMLS_CC); - if(!r->str) { - *err = 1; - return; + if (r->len > 0) { + r->str = redis_sock_read_bulk_reply(sock,r->len TSRMLS_CC); + if(!r->str) { + *err = 1; + return; + } } break; case TYPE_MULTIBULK: r->element = ecalloc(r->len,r->len*sizeof(clusterReply*)); r->elements = r->len; - cluster_multibulk_resp_recursive(sock, r->elements, r->element, + cluster_multibulk_resp_recursive(sock, r->elements, r->element, err TSRMLS_CC); if(*err) return; break; @@ -168,8 +171,8 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, } /* Return the socket for a slot and slave index */ -static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, - ulong slaveidx) +static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, + ulong slaveidx) { redisClusterNode **node; @@ -179,7 +182,7 @@ static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, } /* Abort if we can't find this slave */ - if (!SLOT_SLAVES(c, slot) || zend_hash_index_find(SLOT_SLAVES(c,slot), + if (!SLOT_SLAVES(c, slot) || zend_hash_index_find(SLOT_SLAVES(c,slot), slaveidx, (void**)&node)==FAILURE) return NULL; /* Success, return the slave */ @@ -191,11 +194,11 @@ clusterReply *cluster_read_resp(redisCluster *c TSRMLS_DC) { return cluster_read_sock_resp(c->cmd_sock,c->reply_type,c->reply_len TSRMLS_CC); } -/* Read any sort of response from the socket, having already issued the +/* Read any sort of response from the socket, having already issued the * command and consumed the reply type and meta info (length) */ clusterReply* -cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, - size_t len TSRMLS_DC) +cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, + size_t len TSRMLS_DC) { clusterReply *r; @@ -224,7 +227,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, r->elements = len; if(len != (size_t)-1) { r->element = ecalloc(len, sizeof(clusterReply*)*len); - cluster_multibulk_resp_recursive(redis_sock, len, r->element, + cluster_multibulk_resp_recursive(redis_sock, len, r->element, &err TSRMLS_CC); } break; @@ -243,7 +246,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, return r; } -/* +/* * Helpers to send various 'control type commands to a specific node, e.g. * MULTI, ASKING, READONLY, READWRITE, etc */ @@ -267,7 +270,7 @@ static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len, } static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) { - return cluster_send_direct(redis_sock, RESP_ASKING_CMD, + return cluster_send_direct(redis_sock, RESP_ASKING_CMD, sizeof(RESP_ASKING_CMD)-1, TYPE_LINE TSRMLS_CC); } @@ -328,10 +331,10 @@ PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { return -1; } -/* - * Cluster key distribution helpers. For a small handlful of commands, we want - * to distribute them across 1-N nodes. These methods provide simple containers - * for the purposes of splitting keys/values in this way +/* + * Cluster key distribution helpers. For a small handlful of commands, we want + * to distribute them across 1-N nodes. These methods provide simple containers + * for the purposes of splitting keys/values in this way * */ /* Free cluster distribution list inside a HashTable */ @@ -340,9 +343,9 @@ static void cluster_dist_free_ht(void *p) { int i; for(i=0; i < dl->len; i++) { - if(dl->entry[i].key_free) + if(dl->entry[i].key_free) efree(dl->entry[i].key); - if(dl->entry[i].val_free) + if(dl->entry[i].val_free) efree(dl->entry[i].val); } @@ -353,7 +356,7 @@ static void cluster_dist_free_ht(void *p) { /* Spin up a HashTable that will contain distribution lists */ HashTable *cluster_dist_create() { HashTable *ret; - + ALLOC_HASHTABLE(ret); zend_hash_init(ret, 0, NULL, cluster_dist_free_ht, 0); @@ -369,7 +372,7 @@ void cluster_dist_free(HashTable *ht) { /* Create a clusterDistList object */ static clusterDistList *cluster_dl_create() { clusterDistList *dl; - + dl = emalloc(sizeof(clusterDistList)); dl->entry = emalloc(CLUSTER_KEYDIST_ALLOC * sizeof(clusterKeyVal)); dl->size = CLUSTER_KEYDIST_ALLOC; @@ -379,8 +382,8 @@ static clusterDistList *cluster_dl_create() { } /* Add a key to a dist list, returning the keval entry */ -static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, - int key_len, int key_free) +static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, + int key_len, int key_free) { // Reallocate if required if(dl->len==dl->size) { @@ -392,7 +395,7 @@ static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, dl->entry[dl->len].key = key; dl->entry[dl->len].key_len = key_len; dl->entry[dl->len].key_free = key_free; - + // NULL out any values dl->entry[dl->len].val = NULL; dl->entry[dl->len].val_len = 0; @@ -403,7 +406,7 @@ static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, /* Add a key, returning a pointer to the entry where passed for easy adding * of values to match this key */ -int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, +int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, int key_len, clusterKeyVal **kv) { int key_free; @@ -440,7 +443,7 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, } /* Provided a clusterKeyVal, add a value */ -void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val +void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val TSRMLS_DC) { char *val; @@ -515,7 +518,7 @@ unsigned short cluster_hash_key(const char *key, int len) { for(s=0;sslaves = NULL; // Attach socket - node->sock = redis_sock_create(host, host_len, port, c->timeout, - 0, NULL, 0, 1); + node->sock = redis_sock_create(host, host_len, port, c->timeout, + 0, NULL, 0, 1); return node; } /* Attach a slave to a master */ -PHPAPI int +PHPAPI int cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) { ulong index; @@ -689,12 +692,12 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { for(i=0;ielements;i++) { // Inner response r2 = r->element[i]; - + // Validate outer and master slot if(!VALIDATE_SLOTS_OUTER(r2) || !VALIDATE_SLOTS_INNER(r2->element[2])) { return -1; } - + // Master r3 = r2->element[2]; @@ -714,24 +717,24 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { } else { master = *ppnode; } - + // Attach slaves for(j=3;jelements;j++) { r3 = r2->element[j]; if(!VALIDATE_SLOTS_INNER(r3)) { return -1; } - + // Skip slaves where the host is "" if(r3->element[0]->len == 0) continue; - // Attach this node to our slave - slave = cluster_node_create(c, r3->element[0]->str, - (int)r3->element[0]->len, + // Attach this node to our slave + slave = cluster_node_create(c, r3->element[0]->str, + (int)r3->element[0]->len, (unsigned short)r3->element[1]->integer, low, 1); cluster_node_add_slave(master, slave); } - + // Attach this node to each slot in the range for(j=low;j<=high;j++) { c->master[j]=master; @@ -771,7 +774,7 @@ static redisClusterNode *cluster_get_asking_node(redisCluster *c TSRMLS_DC) { c->redir_port, c->redir_slot, 0); /* Return the node */ - return *ppNode; + return *ppNode; } /* Get or create a node at the host:port we were asked to check, and return the @@ -795,14 +798,14 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { { // Grab seed string zend_hash_get_current_data(ht_seeds, (void**)&z_seed); - + // Skip anything that isn't a string if(Z_TYPE_PP(z_seed)!=IS_STRING) continue; - + // Grab a copy of the string str = Z_STRVAL_PP(z_seed); - + // Must be in host:port form if(!(psep = strchr(str, ':'))) continue; @@ -874,7 +877,7 @@ cluster_map_keyspace(redisCluster *c TSRMLS_DC) { static int cluster_set_redirection(redisCluster* c, char *msg, int moved) { char *host, *port; - + /* Move past "MOVED" or "ASK */ msg += moved ? MOVED_LEN : ASK_LEN; @@ -907,21 +910,21 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) * * This function will return -1 on a critical error (e.g. parse/communication * error, 0 if no redirection was encountered, and 1 if the data was moved. */ -static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type +static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type TSRMLS_DC) { size_t sz; // Clear out any prior error state and our last line response CLUSTER_CLEAR_ERROR(c); - CLUSTER_CLEAR_REPLY(c); + CLUSTER_CLEAR_REPLY(c); if(-1 == redis_check_eof(c->cmd_sock, 1 TSRMLS_CC) || - EOF == (*reply_type = php_stream_getc(c->cmd_sock->stream))) + EOF == (*reply_type = php_stream_getc(c->cmd_sock->stream))) { return -1; } - + // In the event of an ERROR, check if it's a MOVED/ASK error if(*reply_type == TYPE_ERR) { char inbuf[1024]; @@ -949,14 +952,14 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type } // Fetch the first line of our response from Redis. - if(redis_sock_gets(c->cmd_sock,c->line_reply,sizeof(c->line_reply), + if(redis_sock_gets(c->cmd_sock,c->line_reply,sizeof(c->line_reply), &sz TSRMLS_CC)<0) { return -1; } // For replies that will give us a numberic length, convert it - if(*reply_type != TYPE_LINE) { + if(*reply_type != TYPE_LINE) { c->reply_len = strtol(c->line_reply, NULL, 10); } else { c->reply_len = (long long)sz; @@ -996,7 +999,7 @@ static void fyshuffle(int *array, size_t len) { /* This method attempts to write our command at random to the master and any * attached slaves, until we either successufly do so, or fail. */ -static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, +static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, int nomaster TSRMLS_DC) { int i, count=1, *nodes; @@ -1008,7 +1011,7 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, } nodes = emalloc(sizeof(int)*count); - /* Populate our array with the master and each of it's slaves, then + /* Populate our array with the master and each of it's slaves, then * randomize them, so we will pick from the master or some slave. */ for (i = 0; i < count; i++) nodes[i] = i; fyshuffle(nodes, count); @@ -1018,13 +1021,13 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, /* Get the slave for this index */ redis_sock = cluster_slot_sock(c, c->cmd_slot, nodes[i]); if (!redis_sock) continue; - + /* Connect to this node if we haven't already */ CLUSTER_LAZY_CONNECT(redis_sock); /* If we're not on the master, attempt to send the READONLY commadn to * this slave, and skip it if that fails */ - if (nodes[i] == 0 || redis_sock->readonly || + if (nodes[i] == 0 || redis_sock->readonly || cluster_send_readonly(redis_sock TSRMLS_CC) == 0) { /* Attempt to send the command */ @@ -1032,7 +1035,7 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, c->cmd_sock = redis_sock; efree(nodes); return 0; - } + } } } @@ -1057,13 +1060,13 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, * If we're unable to communicate with this slot's master, we attempt the query * against any slaves (at random) that this master has. * REDIS_FAILOVER_DISTRIBUTE: - * We pick at random from the master and any slaves it has. This option is + * We pick at random from the master and any slaves it has. This option is * used to load balance read queries against N slaves. * * Once we are able to find a node we can write to, we check for MOVED or - * ASKING redirection, such that the keyspace can be updated. + * ASKING redirection, such that the keyspace can be updated. */ -static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, +static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, int direct TSRMLS_DC) { redisClusterNode **seed_node; @@ -1074,7 +1077,7 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, redis_sock = c->cmd_sock; /* Readonly is irrelevant if we're not configured to failover */ - failover = c->readonly && c->failover != REDIS_FAILOVER_NONE ? + failover = c->readonly && c->failover != REDIS_FAILOVER_NONE ? c->failover : REDIS_FAILOVER_NONE; /* If in ASK redirection, get/create the node for that host:port, otherwise @@ -1099,7 +1102,7 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, /* Try the master, then fall back to any slaves we may have */ CLUSTER_LAZY_CONNECT(redis_sock); if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz) || - !cluster_dist_write(c, cmd, sz, 1 TSRMLS_CC)) return 0; + !cluster_dist_write(c, cmd, sz, 1 TSRMLS_CC)) return 0; } else if (!cluster_dist_write(c, cmd, sz, 0 TSRMLS_CC)) { /* We were able to write to a master or slave at random */ return 0; @@ -1193,7 +1196,7 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { /* Update slot inside of node, so it can be found for command sending */ node->slot = c->redir_slot; - /* Make sure we unflag this node as a slave, as Redis Cluster will only ever + /* Make sure we unflag this node as a slave, as Redis Cluster will only ever * direct us to master nodes. */ node->slave = 0; } @@ -1203,7 +1206,7 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { * to disconnect as it would leave us in an undefined state. */ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { clusterFoldItem *fi = c->multi_head; - + /* Loop through our fold items */ while(fi) { if(SLOT_SOCK(c,fi->slot)->mode == MULTI) { @@ -1216,7 +1219,7 @@ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { } fi = fi->next; } - + /* Update our overall cluster state */ c->flags->mode = ATOMIC; @@ -1224,7 +1227,7 @@ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { return 0; } -/* Iterate through our slots, looking for the host/port in question. This +/* Iterate through our slots, looking for the host/port in question. This * should perform well enough as in almost all situations, a few or a few * dozen servers will map all the slots */ PHPAPI short cluster_find_slot(redisCluster *c, const char *host, @@ -1233,7 +1236,7 @@ PHPAPI short cluster_find_slot(redisCluster *c, const char *host, int i; for(i=0;imaster[i] && c->master[i]->sock && + if(c->master[i] && c->master[i]->sock && c->master[i]->sock->port == port && !strcasecmp(c->master[i]->sock->host, host)) { @@ -1246,13 +1249,13 @@ PHPAPI short cluster_find_slot(redisCluster *c, const char *host, } /* Send a command to a specific slot */ -PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, +PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC) { /* Point our cluster to this slot and it's socket */ c->cmd_slot = slot; c->cmd_sock = SLOT_SOCK(c, slot); - + /* Try the slot */ if(cluster_sock_write(c, cmd, cmd_len, 1 TSRMLS_CC)==-1) { return -1; @@ -1266,9 +1269,9 @@ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, return 0; } -/* Send a command to given slot in our cluster. If we get a MOVED or ASK error +/* Send a command to given slot in our cluster. If we get a MOVED or ASK error * we attempt to send the command to the node as directed. */ -PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, +PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { int resp, timedout=0; @@ -1321,7 +1324,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, 0 TSRMLS_CC); return -1; } - + /* Update mapping if the data has MOVED */ if (c->redir_type == REDIR_MOVED) cluster_update_slot(c TSRMLS_CC); } @@ -1356,7 +1359,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, * consumed. */ /* RAW bulk response handler */ -PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, +PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; @@ -1391,7 +1394,7 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, if (CLUSTER_IS_ATOMIC(c)) { if (redis_unserialize(c->flags, resp, c->reply_len, &return_value - TSRMLS_CC) == 0) + TSRMLS_CC) == 0) { CLUSTER_RETURN_STRING(c, resp, c->reply_len); } else { @@ -1448,8 +1451,8 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { // Check that we have +OK - if(c->reply_type != TYPE_LINE || c->reply_len != 2 || - c->line_reply[0] != 'O' || c->line_reply[1] != 'K') + if(c->reply_type != TYPE_LINE || c->reply_len != 2 || + c->line_reply[0] != 'O' || c->line_reply[1] != 'K') { CLUSTER_RETURN_FALSE(c); } @@ -1507,13 +1510,13 @@ PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } else if (strncmp(c->line_reply, "set", 3)==0) { CLUSTER_RETURN_LONG(c, REDIS_SET); } else if (strncmp(c->line_reply, "list", 4)==0) { - CLUSTER_RETURN_LONG(c, REDIS_LIST); + CLUSTER_RETURN_LONG(c, REDIS_LIST); } else if (strncmp(c->line_reply, "hash", 4)==0) { CLUSTER_RETURN_LONG(c, REDIS_HASH); } else if (strncmp(c->line_reply, "zset", 4)==0) { CLUSTER_RETURN_LONG(c, REDIS_ZSET); } else { - CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND); + CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND); } } @@ -1526,15 +1529,15 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull=0; // Consume each MULTI BULK response (one per channel/pattern) - while(sctx->argc--) { - z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + while(sctx->argc--) { + z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw); - + if(!z_tab) { efree(sctx); RETURN_FALSE; } - + if(zend_hash_index_find(Z_ARRVAL_P(z_tab),0,(void**)&z_tmp)==FAILURE || strcasecmp(Z_STRVAL_PP(z_tmp), sctx->kw) != 0) { @@ -1564,11 +1567,11 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int tab_idx=1, is_pmsg; // Get the next subscribe response - z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, mbulk_resp_loop); - + if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&z_type) - ==FAILURE) + ==FAILURE) { break; } @@ -1584,14 +1587,14 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, continue; } - if(is_pmsg && zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, + if(is_pmsg && zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&z_pat)==FAILURE) { break; } // Extract channel and data - if(zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, + if(zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&z_chan)==FAILURE || zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, (void**)&z_data)==FAILURE) @@ -1617,7 +1620,7 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Execute our callback if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC)!= - SUCCESS) + SUCCESS) { break; } @@ -1628,7 +1631,7 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, zval_dtor(z_tab); efree(z_tab); } - + // We're no longer subscribing, due to an error c->subscribed_slot = -1; @@ -1644,7 +1647,7 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* UNSUBSCRIBE/PUNSUBSCRIBE */ -PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, +PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; @@ -1658,10 +1661,10 @@ PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, while(argc--) { z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw); - + // Fail if we didn't get an array or can't find index 1 - if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, - (void**)&z_chan)==FAILURE) + if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, + (void**)&z_chan)==FAILURE) { if(z_tab) { zval_dtor(z_tab); @@ -1691,7 +1694,7 @@ PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, efree(z_tab); pull = 1; } -} +} /* Recursive MULTI BULK -> PHP style response handling */ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) @@ -1707,7 +1710,10 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) add_next_index_bool(z_ret, 1); break; case TYPE_BULK: - add_next_index_stringl(z_ret, r->str, r->len, 0); + if (r->len > -1) + add_next_index_stringl(z_ret, r->str, r->len, 0); + else + add_next_index_null(z_ret); break; case TYPE_MULTIBULK: MAKE_STD_ZVAL(z_sub_ele); @@ -1725,7 +1731,7 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) /* Variant response handling, for things like EVAL and various other responses * where we just map the replies from Redis type values to PHP ones directly. */ -PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterReply *r; @@ -1755,7 +1761,7 @@ PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, case TYPE_MULTIBULK: MAKE_STD_ZVAL(z_arr); array_init(z_arr); - + for(i=0;ielements;i++) { cluster_mbulk_variant_resp(r->element[i], z_arr); } @@ -1798,7 +1804,7 @@ PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Generic MULTI BULK response processor */ -PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb cb, void *ctx) { zval *z_result; @@ -1824,7 +1830,7 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, CLUSTER_RETURN_FALSE(c); } } - + // Success, make this array our return value if(CLUSTER_IS_ATOMIC(c)) { *return_value = *z_result; @@ -1835,7 +1841,7 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* HSCAN, SSCAN, ZSCAN */ -PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, REDIS_SCAN_TYPE type, long *it) { char *pit; @@ -1854,7 +1860,7 @@ PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } // Read the iterator - if((pit = redis_sock_read_bulk_reply(c->cmd_sock,c->reply_len TSRMLS_CC))==NULL) + if((pit = redis_sock_read_bulk_reply(c->cmd_sock,c->reply_len TSRMLS_CC))==NULL) { return FAILURE; } @@ -1914,7 +1920,7 @@ PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, efree(z_result); } else { add_next_index_zval(c->multi_resp, z_result); - } + } } /* MULTI BULK response loop where we might pull the next one */ @@ -1950,7 +1956,7 @@ PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* MULTI MULTI BULK reply (for EXEC) */ -PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { MAKE_STD_ZVAL(c->multi_resp); @@ -1987,7 +1993,7 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* Generic handler for MGET */ -PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, +PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; @@ -2000,7 +2006,7 @@ PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, // If we had a failure, pad results with FALSE to indicate failure. Non // existant keys (e.g. for MGET will come back as NULL) if(fail) { - while(mctx->count--) { + while(mctx->count--) { add_next_index_bool(mctx->z_multi, 0); } } @@ -2090,7 +2096,7 @@ PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; - // If we get an invalid reply type something very wrong has happened, + // If we get an invalid reply type something very wrong has happened, // and we have to abort. if(c->reply_type != TYPE_LINE) { php_error_docref(0 TSRMLS_CC, E_ERROR, @@ -2115,18 +2121,18 @@ PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Raw MULTI BULK reply */ -PHPAPI void -cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +PHPAPI void +cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_raw, NULL); } /* Unserialize all the things */ PHPAPI void -cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop, NULL); } @@ -2134,18 +2140,18 @@ cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) * we will turn into key => value, key => value. */ PHPAPI void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + void *ctx) { - cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_zipstr, NULL); } /* Handling key,value to key=>value where the values are doubles */ PHPAPI void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + void *ctx) { - cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, mbulk_resp_loop_zipdbl, NULL); } @@ -2158,13 +2164,13 @@ cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_resp_loop_assoc, ctx); } -/* - * Various MULTI BULK reply callback functions +/* + * Various MULTI BULK reply callback functions */ /* MULTI BULK response where we don't touch the values (e.g. KEYS) */ -int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC) +int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, + long long count, void *ctx TSRMLS_DC) { char *line; int line_len; @@ -2184,7 +2190,7 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, } /* MULTI BULK response where we unserialize everything */ -int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, +int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC) { char *line; @@ -2214,7 +2220,7 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, } /* MULTI BULK response where we turn key1,value1 into key1=>value1 */ -int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, +int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC) { char *line, *key; @@ -2242,7 +2248,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval(z_result, key, z); } else { - add_assoc_stringl_ex(z_result, key, 1+key_len, line, + add_assoc_stringl_ex(z_result, key, 1+key_len, line, line_len, 0); } efree(key); @@ -2268,15 +2274,25 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, // While we have elements while(count--) { line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); - if(!line) return -1; + if (line != NULL) { + if(idx++ % 2 == 0) { + key = line; + key_len = line_len; + } else { + zval *z = NULL; + if (redis_unserialize(redis_sock,key,key_len, &z TSRMLS_CC)) { + convert_to_string(z); + add_assoc_double_ex(z_result, Z_STRVAL_P(z), 1+Z_STRLEN_P(z), atof(line)); + zval_dtor(z); + efree(z); + } else { + add_assoc_double_ex(z_result, key, 1+key_len, atof(line)); + } - if(idx++ % 2 == 0) { - key = line; - key_len = line_len; - } else { - add_assoc_double_ex(z_result, key, 1+key_len, atof(line)); - efree(key); - efree(line); + /* Free our key and line */ + efree(key); + efree(line); + } } } @@ -2284,18 +2300,19 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, } /* MULTI BULK where we're passed the keys, and we attach vals */ -int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, +int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC) { char *line; int line_len,i=0; - zval **z_keys = ctx, *z; - + zval **z_keys = ctx; + // Loop while we've got replies while(count--) { line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); - + if(line != NULL) { + zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { efree(line); add_assoc_zval_ex(z_result,Z_STRVAL_P(z_keys[i]), @@ -2312,7 +2329,7 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, // Clean up key context zval_dtor(z_keys[i]); efree(z_keys[i]); - + // Move to the next key i++; } diff --git a/cluster_library.h b/cluster_library.h index 18fff7d5b9..aee2f761e8 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -309,8 +309,8 @@ typedef struct clusterMultiCmd { * we don't encode errors, here as that's handled in the cluster structure. */ typedef struct clusterReply { REDIS_REPLY_TYPE type; /* Our reply type */ - long long integer; /* Integer reply */ - size_t len; /* Length of our string */ + size_t integer; /* Integer reply */ + long long len; /* Length of our string */ char *str; /* String reply */ size_t elements; /* Count of array elements */ struct clusterReply **element; /* Array elements */ diff --git a/redis_cluster.c b/redis_cluster.c index 6d54f64bea..50a0ae7f74 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1574,15 +1574,14 @@ PHP_METHOD(RedisCluster, setrange) { } /* }}} */ -/* Generic implementation for ZRANGE, ZREVRANGE, ZRANGEBYSCORE, - * ZREVRANGEBYSCORE */ +/* Generic implementation for ZRANGE, ZREVRANGE, ZRANGEBYSCORE, ZREVRANGEBYSCORE */ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, zrange_cb fun) { redisCluster *c = GET_CONTEXT(); cluster_cb cb; char *cmd; int cmd_len; short slot; - int withscores; + int withscores=0; if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, &withscores, &slot, NULL)==FAILURE) diff --git a/redis_commands.c b/redis_commands.c index bb0402b572..03da011520 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -419,7 +419,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int key_len, key_free; long start, end; - zend_bool ws; + zend_bool ws=0; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|b", &key, &key_len, &start, &end, &ws)==FAILURE) @@ -938,8 +938,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Add our members for(i=1;iassertFalse($this->redis->msetnx(array())); // set ø → FALSE } + + /* Slowlog needs to take a key or Array(ip, port), to direct it to a node */ + public function testSlowlog() { + $str_key = uniqid() . '-' . rand(1, 1000); + + $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get'))); + $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get', 10))); + $this->assertTrue(is_int($this->redis->slowlog($str_key, 'len'))); + $this->assertTrue($this->redis->slowlog($str_key, 'reset')); + $this->assertFalse($this->redis->slowlog($str_key, 'notvalid')); + } + + /* INFO COMMANDSTATS requires a key or ip:port for node direction */ + public function testInfoCommandStats() { + $str_key = uniqid() . '-' . rand(1,1000); + $arr_info = $this->redis->info($str_key, "COMMANDSTATS"); + + $this->assertTrue(is_array($arr_info)); + if (is_array($arr_info)) { + foreach($arr_info as $k => $str_value) { + $this->assertTrue(strpos($k, 'cmdstat_') !== false); + } + } + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 1280022d92..b2e5745b86 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -91,7 +91,7 @@ public function testPubSub() { $this->assertTrue(is_array($result)); // PUBSUB NUMSUB - + $c1 = uniqid() . '-' . rand(1,100); $c2 = uniqid() . '-' . rand(1,100); @@ -153,7 +153,7 @@ public function testBitsets() { // Verify valid offset ranges $this->assertFalse($this->redis->getBit('key', -1)); - + $this->redis->setBit('key', 4294967295, 1); $this->assertEquals(1, $this->redis->getBit('key', 4294967295)); } @@ -217,7 +217,7 @@ public function testSet() $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')); @@ -408,7 +408,7 @@ public function testMultipleBin() { $this->redis->del('k3'); $this->redis->set('k1', gzcompress('v1')); - $this->redis->set('k2', gzcompress('v2')); + $this->redis->set('k2', gzcompress('v2')); $this->redis->set('k3', gzcompress('v3')); $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->mget(array('k1', 'k2', 'k3'))); @@ -503,7 +503,7 @@ public function testIncrByFloat() } $this->redis->del('key'); - + $this->redis->set('key', 0); $this->redis->incrbyfloat('key', 1.5); @@ -1162,9 +1162,9 @@ public function testsRandMember() { } } - // + // // With and without count, while serializing - // + // $this->redis->del('set0'); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); @@ -1188,11 +1188,11 @@ public function testsRandMember() { 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)); @@ -1321,13 +1321,13 @@ public function testsInter() { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); } - + $xy = $this->redis->sInter(array('{set}odd', '{set}prime')); // odd prime numbers, as array. foreach($xy as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); } - + $yz = $this->redis->sInter('{set}prime', '{set}square'); // set of prime squares foreach($yz as $i) { $i = (int)$i; @@ -1339,12 +1339,12 @@ public function testsInter() { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($y, $z))); } - + $zt = $this->redis->sInter('{set}square', '{set}seq'); // prime squares $this->assertTrue($zt === array()); $zt = $this->redis->sInter(array('{set}square', '{set}seq')); // prime squares, as array $this->assertTrue($zt === array()); - + $xyz = $this->redis->sInter('{set}odd', '{set}prime', '{set}square');// odd prime squares $this->assertTrue($xyz === array('1')); @@ -1730,10 +1730,10 @@ public function testClient() { // 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)); } @@ -1804,7 +1804,7 @@ public function testInfo() { "rdb_last_save_time" ); } - + foreach($keys as $k) { $this->assertTrue(in_array($k, array_keys($info))); } @@ -2366,7 +2366,7 @@ public function testObject() { * 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); @@ -2388,7 +2388,7 @@ public function testObject() { $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); } @@ -2413,12 +2413,12 @@ public function testMultiExec() { // 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 @@ -2662,7 +2662,7 @@ protected function sequence($mode) { $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') @@ -2992,7 +2992,7 @@ protected function differentType($mode) { ->lPop($key) ->rPop($key) ->rPoplPush($key, $dkey . 'lkey1') - + // sets I/F ->sAdd($key, 'sValue1') ->srem($key, 'sValue1') @@ -3037,7 +3037,7 @@ protected function differentType($mode) { ->hGetAll($key) ->exec(); - + $i = 0; $this->assertTrue(is_array($ret)); $this->assertTrue(is_long($ret[$i++])); // delete @@ -3873,7 +3873,7 @@ public function testDifferentTypeHash() { public function testSerializerPHP() { $this->checkSerializer(Redis::SERIALIZER_PHP); - + // with prefix $this->redis->setOption(Redis::OPT_PREFIX, "test:"); $this->checkSerializer(Redis::SERIALIZER_PHP); @@ -3907,7 +3907,7 @@ private function checkSerializer($mode) { $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)); @@ -3916,11 +3916,11 @@ private function checkSerializer($mode) { $this->assertTrue($a[1] === $this->redis->lGet('key', 1)); $this->assertTrue($a[2] === $this->redis->lGet('key', 2)); $this->assertTrue($a[3] === $this->redis->lGet('key', 3)); - + // lrem $this->assertTrue($this->redis->lrem('key', $a[3]) === 1); $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1)); - + // lSet $a[0] = array('k' => 'v'); // update $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); @@ -3929,7 +3929,7 @@ private function checkSerializer($mode) { // lInsert $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], array(1,2,3)) === 4); $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], array(4,5,6)) === 5); - + $a = array(array(1,2,3), $a[0], array(4,5,6), $a[1], $a[2]); $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); @@ -3941,7 +3941,7 @@ private function checkSerializer($mode) { $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[1])); $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[2])); $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[3])); - + // variadic sAdd $this->redis->del('k'); $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); @@ -3950,14 +3950,14 @@ private function checkSerializer($mode) { // srem $this->assertTrue(1 === $this->redis->srem('{set}key', $s[3])); $this->assertTrue(0 === $this->redis->srem('{set}key', $s[3])); - + // variadic $this->redis->del('k'); $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); $this->assertTrue(FALSE === $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])); @@ -4082,7 +4082,7 @@ private function checkSerializer($mode) { // multi-exec $this->sequence(Redis::MULTI); - + // keys $this->assertTrue(is_array($this->redis->keys('*'))); @@ -4230,46 +4230,46 @@ public function testEval() { /* * Keys to be incorporated into lua results */ - // Make a list - $this->redis->del('mylist'); - $this->redis->rpush('mylist', 'a'); - $this->redis->rpush('mylist', 'b'); - $this->redis->rpush('mylist', 'c'); + $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('myset'); - $this->redis->sadd('myset', 'd'); - $this->redis->sadd('myset', 'e'); - $this->redis->sadd('myset', 'f'); + $this->redis->del('{eval-key}-set'); + $this->redis->sadd('{eval-key}-set', 'd'); + $this->redis->sadd('{eval-key}-set', 'e'); + $this->redis->sadd('{eval-key}-set', 'f'); // Basic keys - $this->redis->set('key1', 'hello, world'); - $this->redis->set('key2', 'hello again!'); + $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', 'mylist', 0, -1)"); +/* $list = $this->redis->eval("return redis.call('lrange', KEYS[1], 0, -1)", Array('{eval-key}-list'), 1); $this->assertTrue($list === Array('a','b','c')); // Use a script to return our set - $set = $this->redis->eval("return redis.call('smembers', 'myset')"); + $set = $this->redis->eval("return redis.call('smembers', KEYS[1])", Array('{eval-key}-set'), 1); $this->assertTrue($set == Array('d','e','f')); // Test an empty MULTI BULK response - $this->redis->del('not-any-kind-of-list'); - $empty_resp = $this->redis->eval("return redis.call('lrange', 'not-any-kind-of-list', 0, -1)"); + $this->redis->del('{eval-key}-nolist'); + $empty_resp = $this->redis->eval("return redis.call('lrange', '{eval-key}-nolist', 0, -1)", + Array('{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', 'key1'), - redis.call('get', 'key2'), + redis.call('get', '{eval-key}-str1'), + redis.call('get', '{eval-key}-str2'), redis.call('lrange', 'not-any-kind-of-list', 0, -1), { - redis.call('smembers','myset'), - redis.call('lrange', 'mylist', 0, -1) + redis.call('smembers','{eval-key}-set'), + redis.call('lrange', '{eval-key}-list', 0, -1) } } } @@ -4286,11 +4286,11 @@ public function testEval() { ) ) ); - +/* // Now run our script, and check our values against each other - $eval_result = $this->redis->eval($nested_script); + $eval_result = $this->redis->eval($nested_script, Array('{eval-key}-str1', '{eval-key}-str2', '{eval-key}-set', '{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 */ @@ -4303,11 +4303,20 @@ public function testEval() { foreach($arr_modes as $mode) { $this->redis->multi($mode); for($i=0;$i<$num_scripts;$i++) { - $this->redis->eval($nested_script); + $this->redis->eval($nested_script, Array('{eval-key}-dummy'), 1); } $replies = $this->redis->exec(); +var_dump($replies); die(); + foreach($replies as $reply) { + echo "--expected---\n"; + var_dump($expected); + echo "---reply---\n"; + var_dump($reply); + var_dump($this->array_diff_recursive($reply, $expected)); + die(); + $this->assertTrue(is_array($reply) && count($this->array_diff_recursive($reply, $expected)) == 0); } } @@ -4363,9 +4372,9 @@ public function testEvalSHA() { public function testSerialize() { $vals = Array(1, 1.5, 'one', Array('here','is','an','array')); - + // Test with no serialization at all - $this->assertTrue($this->redis->_serialize('test') === 'test'); + $this->assertTrue($this->redis->_serialize('test') === 'test'); $this->assertTrue($this->redis->_serialize(1) === '1'); $this->assertTrue($this->redis->_serialize(Array()) === 'Array'); $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object'); @@ -4376,7 +4385,7 @@ public function testSerialize() { } foreach($arr_serializers as $mode) { - $arr_enc = Array(); + $arr_enc = Array(); $arr_dec = Array(); foreach($vals as $k => $v) { @@ -4546,7 +4555,7 @@ public function testHScan() { $this->markTestSkipped(); return; } - + // Never get empty sets $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); @@ -4555,7 +4564,7 @@ public function testHScan() { for($i=0;$i<100;$i++) { if($i>3) { - $this->redis->hset('hash', "member:$i", "value:$i"); + $this->redis->hset('hash', "member:$i", "value:$i"); } else { $this->redis->hset('hash', "foomember:$i", "value:$i"); $i_foo_mems++; @@ -4633,7 +4642,7 @@ public function testZScan() { } else { $this->redis->zadd('zset', $i, "mem:$i"); } - + $i_tot_score += $i; } @@ -4745,7 +4754,7 @@ public function testPFCommands() { // Clean up merge key $this->redis->del('pf-merge-{key}'); - + // Merge the counters $this->assertTrue($this->redis->pfmerge('pf-merge-{key}', $arr_keys)); From 2f8a2fc35e66aaa6f55e6c645129087900b68a9c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Mar 2015 14:00:46 -0800 Subject: [PATCH 0445/1986] Fix variant reply handling in MULTI mode --- cluster_library.c | 5 +---- tests/RedisTest.php | 17 ++++------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 9b8b52b390..0ad7422691 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1788,10 +1788,7 @@ PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, add_next_index_stringl(c->multi_resp, r->str, r->len, 0); break; case TYPE_MULTIBULK: - MAKE_STD_ZVAL(z_arr); - array_init(z_arr); - cluster_mbulk_variant_resp(r, z_arr); - add_next_index_zval(c->multi_resp, z_arr); + cluster_mbulk_variant_resp(r, c->multi_resp); break; default: add_next_index_bool(c->multi_resp, 0); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index b2e5745b86..3c6db95178 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4247,7 +4247,7 @@ public function testEval() { $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)", Array('{eval-key}-list'), 1); + $list = $this->redis->eval("return redis.call('lrange', KEYS[1], 0, -1)", Array('{eval-key}-list'), 1); $this->assertTrue($list === Array('a','b','c')); // Use a script to return our set @@ -4259,7 +4259,7 @@ public function testEval() { $empty_resp = $this->redis->eval("return redis.call('lrange', '{eval-key}-nolist', 0, -1)", Array('{eval-key}-nolist'), 1); $this->assertTrue(is_array($empty_resp) && empty($empty_resp)); - */ + // Now test a nested reply $nested_script = " return { @@ -4286,11 +4286,11 @@ public function testEval() { ) ) ); -/* + // Now run our script, and check our values against each other $eval_result = $this->redis->eval($nested_script, Array('{eval-key}-str1', '{eval-key}-str2', '{eval-key}-set', '{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 */ @@ -4307,16 +4307,7 @@ public function testEval() { } $replies = $this->redis->exec(); -var_dump($replies); die(); - foreach($replies as $reply) { - echo "--expected---\n"; - var_dump($expected); - echo "---reply---\n"; - var_dump($reply); - var_dump($this->array_diff_recursive($reply, $expected)); - die(); - $this->assertTrue(is_array($reply) && count($this->array_diff_recursive($reply, $expected)) == 0); } } From cfc557dfc3abd7a0e8043a9cfd913eee707ed625 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Mar 2015 15:15:01 -0800 Subject: [PATCH 0446/1986] Fix typo treating DEL as readonly and MGET as not --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index 50a0ae7f74..125c69f7e7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -670,7 +670,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, efree(z_args); /* MGET is readonly, DEL is not */ - c->readonly = kw_len == 3 && CLUSTER_IS_ATOMIC(c); + c->readonly = kw_len == 4 && CLUSTER_IS_ATOMIC(c); // Initialize our "multi" command handler with command/len CLUSTER_MULTI_INIT(mc, kw, kw_len); From 366363e4fa4f4be11790c433f458c7544c67268d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Mar 2015 17:39:23 -0800 Subject: [PATCH 0447/1986] Properly handle MULTI lines within a MULTI-BULK of -1 (nil) --- cluster_library.c | 17 +++++++++-------- tests/RedisTest.php | 3 ++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0ad7422691..d65793c0dd 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -320,6 +320,8 @@ PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { * this node, or -1 in the case of EXECABORT or WATCH failure. */ c->multi_len[slot] = c->reply_len > 0 ? 1 : -1; + /* Return our retval */ + return retval; } PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { @@ -1962,7 +1964,7 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, clusterFoldItem *fi = c->multi_head; while(fi) { /* Make sure our transaction didn't fail here */ - if (c->multi_len[fi->slot] > 0) { + if (c->multi_len[fi->slot] > -1) { /* Set the slot where we should look for responses. We don't allow * failover inside a transaction, so it will be the master we have * mapped. */ @@ -2193,13 +2195,12 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, char *line; int line_len; - // Iterate over the lines we have to process + /* Iterate over the lines we have to process */ while(count--) { - // Read the line + /* Read our line */ line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); - if(line == NULL) return FAILURE; - - if(line_len > 0) { + + if (line != NULL) { zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_next_index_zval(z_result, z); @@ -2208,8 +2209,8 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, add_next_index_stringl(z_result, line, line_len, 0); } } else { - efree(line); - add_next_index_null(z_result); + if (line) efree(line); + add_next_index_bool(z_result, 0); } } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 3c6db95178..b76d8dea16 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2418,7 +2418,7 @@ public function testMultiExec() { $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 @@ -2535,6 +2535,7 @@ protected function sequence($mode) { ->ttl('key') ->expireAt('key', '0000') ->exec(); + $this->assertTrue(is_array($ret)); $i = 0; $ttl = $ret[$i++]; From 7ef7d0785493fc0d6fafead56c29d66b48c03c70 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Mar 2015 22:29:50 -0800 Subject: [PATCH 0448/1986] A few minor cluster and unit test fixes * Fixed getLastError() to look at redisCluster->err, rather than flags * Fixed serialization bug for zipstr multibulk processing * Added specific RedisClusterTest tests for SCRIPT and EVALSHA commands All unit tests passing now! \o/ --- cluster_library.c | 8 ++-- redis_cluster.c | 20 ++++----- tests/RedisClusterTest.php | 90 ++++++++++++++++++++++++++++++++++++-- tests/RedisTest.php | 18 +++----- tests/TestSuite.php | 2 +- 5 files changed, 106 insertions(+), 32 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index d65793c0dd..d7addf20d0 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -948,7 +948,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type return 1; } else { // Capture the error string Redis returned - cluster_set_err(c, inbuf, strlen(inbuf)-1); + cluster_set_err(c, inbuf, strlen(inbuf)-2); return 0; } } @@ -1999,6 +1999,7 @@ PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, /* Protect against an invalid response type, -1 response length, and failure * to consume the responses. */ + c->cmd_sock->serializer = c->flags->serializer; short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL TSRMLS_CC)==FAILURE; @@ -2224,7 +2225,6 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, char *line, *key; int line_len, key_len; long long idx=0; - zval *z = NULL; // Our count wil need to be divisible by 2 if(count % 2 != 0) { @@ -2242,9 +2242,11 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, key = line; key_len = line_len; } else { - // Attempt unserialization, add value + /* Attempt serialization */ + zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval(z_result, key, z); + efree(line); } else { add_assoc_stringl_ex(z_result, key, 1+key_len, line, line_len, 0); diff --git a/redis_cluster.c b/redis_cluster.c index 125c69f7e7..e054b4e1ca 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1925,8 +1925,8 @@ PHP_METHOD(RedisCluster, getmode) { PHP_METHOD(RedisCluster, getlasterror) { redisCluster *c = GET_CONTEXT(); - if(c->flags->err != NULL && c->flags->err_len > 0) { - RETURN_STRINGL(c->flags->err, c->flags->err_len, 1); + if(c->err != NULL && c->err_len > 0) { + RETURN_STRINGL(c->err, c->err_len, 1); } else { RETURN_NULL(); } @@ -1936,13 +1936,11 @@ PHP_METHOD(RedisCluster, getlasterror) { /* {{{ proto bool RedisCluster::clearlasterror() */ PHP_METHOD(RedisCluster, clearlasterror) { redisCluster *c = GET_CONTEXT(); - - if(c->flags->err) { - efree(c->flags->err); - } - c->flags->err = NULL; - c->flags->err_len = 0; - + + if (c->err) efree(c->err); + c->err = NULL; + c->err_len = 0; + RETURN_TRUE; } /* }}} */ @@ -2116,9 +2114,7 @@ PHP_METHOD(RedisCluster, watch) { } // If we get a failure from this, we have to abort - if((slot = cluster_send_command(c, (short)slot, cmd.c, cmd.len - TSRMLS_CC))==-1) - { + if (cluster_send_command(c,(short)slot,cmd.c,cmd.len TSRMLS_CC)==-1) { RETURN_FALSE; } diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index ba07195ab5..761d4e6a26 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -137,11 +137,17 @@ public function testPubSub() { $c1 = '{pubsub}-' . rand(1,100); $c2 = '{pubsub}-' . rand(1,100); - $result = $this->redis->pubsub("{pubsub}", "numsub", Array($c1, $c2)); + $result = $this->redis->pubsub("{pubsub}", "numsub", $c1, $c2); // Should get an array back, with two elements $this->assertTrue(is_array($result)); - $this->assertEquals(count($result), 2); + $this->assertEquals(count($result), 4); + + $arr_zipped = Array(); + for ($i = 0; $i <= count($result) / 2; $i+=2) { + $arr_zipped[$result[$i]] = $result[$i+1]; + } + $result = $arr_zipped; // Make sure the elements are correct, and have zero counts foreach(Array($c1,$c2) as $channel) { @@ -153,9 +159,8 @@ public function testPubSub() { $result = $this->redis->pubsub("somekey", "numpat"); $this->assertTrue(is_int($result)); - // Invalid calls + // Invalid call $this->assertFalse($this->redis->pubsub("somekey", "notacommand")); - $this->assertFalse($this->redis->pubsub("somekey", "numsub", "not-an-array")); } /* Unlike Redis proper, MsetNX won't always totally fail if all keys can't @@ -199,5 +204,82 @@ public function testInfoCommandStats() { } } } + + /* RedisCluster will always respond with an array, even if transactions + * failed, because the commands could be coming from multiple nodes */ + 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 === Array(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('x'); // cancel transaction watch + + $ret = $this->redis->multi()->get('x')->exec(); + $this->assertTrue($ret === array('44')); // succeeded since we've cancel the WATCH command. + } + + /* RedisCluster::script() is a 'raw' command, which requires a key such that + * we can direct it to a given node */ + public function testScript() { + $str_key = uniqid() . '-' . rand(1,1000); + + // Flush any scripts we have + $this->assertTrue($this->redis->script($str_key, '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($str_key, '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($str_key, 'load', $s1_src) == $s1_sha); + $this->assertTrue($this->redis->script($str_key, 'load', $s2_src) == $s2_sha); + $this->assertTrue($this->redis->script($str_key, 'load', $s3_src) == $s3_sha); + + // They should all exist + $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha); + $this->assertTrue(is_array($result) && count(array_filter($result)) == 3); + } + + /* RedisCluster::EVALSHA needs a 'key' to let us know which node we want to + * direct the command at */ + public function testEvalSHA() { + $str_key = uniqid() . '-' . rand(1,1000); + + // Flush any loaded scripts + $this->redis->script($str_key, 'flush'); + + // Non existant script (but proper sha1), and a random (not) sha1 string + $this->assertFalse($this->redis->evalsha(sha1(uniqid()),Array($str_key), 1)); + $this->assertFalse($this->redis->evalsha('some-random-data'),Array($str_key), 1); + + // 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,Array($str_key), 1)); + $this->assertTrue(1 === $this->redis->eval($scr,Array($str_key), 1)); + $this->assertTrue(1 === $this->redis->evalsha($sha,Array($str_key), 1)); + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index b76d8dea16..a9c94f4691 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2410,7 +2410,11 @@ public function testMultiExec() { // successful transaction $this->assertTrue($ret === array('42')); + } + public function testFailedTransactions() { + $this->redis->set('x', 42); + // failed transaction $this->redis->watch('x'); @@ -2418,7 +2422,6 @@ public function testMultiExec() { $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 @@ -2427,6 +2430,7 @@ public function testMultiExec() { $this->redis->unwatch(); // cancel transaction watch $ret = $this->redis->multi()->get('x')->exec(); + $this->assertTrue($ret === array('44')); // succeeded since we've cancel the WATCH command. } @@ -3872,7 +3876,6 @@ public function testDifferentTypeHash() { } public function testSerializerPHP() { - $this->checkSerializer(Redis::SERIALIZER_PHP); // with prefix @@ -3882,7 +3885,6 @@ public function testSerializerPHP() { } public function testSerializerIGBinary() { - if(defined('Redis::SERIALIZER_IGBINARY')) { $this->checkSerializer(Redis::SERIALIZER_IGBINARY); @@ -4074,6 +4076,7 @@ private function checkSerializer($mode) { $this->redis->set('b', FALSE); $this->redis->set('c', 42); $this->redis->set('d', array('x' => 'y')); + $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->mGet(array('a', 'b', 'c', 'd'))); // pipeline @@ -4142,18 +4145,10 @@ public function testGetLastError() { // We shouldn't have any errors now $this->assertTrue($this->redis->getLastError() === NULL); - // Throw some invalid lua at redis - $this->redis->eval("not-a-lua-script"); - - // Now we should have an error - $evalError = $this->redis->getLastError(); - $this->assertTrue(strlen($evalError) > 0); - // test getLastError with a regular command $this->redis->set('x', 'a'); $this->assertFalse($this->redis->incr('x')); $incrError = $this->redis->getLastError(); - $this->assertTrue($incrError !== $evalError); // error has changed $this->assertTrue(strlen($incrError) > 0); // clear error @@ -4339,7 +4334,6 @@ public function testEval() { } public function testEvalSHA() { - if (version_compare($this->version, "2.5.0", "lt")) { $this->markTestSkipped(); } diff --git a/tests/TestSuite.php b/tests/TestSuite.php index f4f156f339..593a322fd1 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -145,7 +145,7 @@ public static function run($className, $str_limit = NULL) { echo implode('', $className::$warnings); if(empty($className::$errors)) { - echo "All tests passed. \o/\n"; + echo "\nAll tests passed. \o/\n"; return 0; } From 97c9edc3aa164e2f444ee68faed0c691d2ecce2b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 5 Mar 2015 08:54:46 -0800 Subject: [PATCH 0449/1986] Sorted a memory leak in RedisArray and retabbed unit tests --- redis_array_impl.c | 3 +- tests/RedisArrayTest.php | 914 +++++++++++++++++++-------------------- tests/TestRedis.php | 10 +- tests/TestSuite.php | 4 +- 4 files changed, 459 insertions(+), 472 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 1c48d7a589..dc336674ff 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -461,6 +461,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D efree(out); return NULL; } + efree(out); } else { /* hash */ hash = rcrc32(out, out_len); @@ -1019,7 +1020,6 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl static zend_bool ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_hgetall, z_fun_hmset, z_ret, z_ret_dest, *z_args[2]; /* run HGETALL on source */ @@ -1266,7 +1266,6 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool void ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { - int i; /* redistribute the data, server by server. */ diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 2670e2ddf0..c838d870c3 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -4,554 +4,542 @@ define('REDIS_ARRAY_DATA_SIZE', 1000); function custom_hash($str) { - // str has the following format: $APPID_fb$FACEBOOKID_$key. - $pos = strpos($str, '_fb'); - if(preg_match("#\w+_fb(?\d+)_\w+#", $str, $out)) { - return $out['facebook_id']; - } - return $str; + // str has the following format: $APPID_fb$FACEBOOKID_$key. + $pos = strpos($str, '_fb'); + if(preg_match("#\w+_fb(?\d+)_\w+#", $str, $out)) { + return $out['facebook_id']; + } + return $str; } class Redis_Array_Test extends TestSuite { - private $strings; - public $ra = NULL; - private $data = NULL; - - public function setUp() { - - // initialize strings. - $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); - for($i = 0; $i < $n; $i++) { - $this->strings['key-'.$i] = 'val-'.$i; - } - - global $newRing, $oldRing, $useIndex; - $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); - } - - public function testMSet() { - // run mset - $this->assertTrue(TRUE === $this->ra->mset($this->strings)); - - // check each key individually using the array - foreach($this->strings as $k => $v) { - $this->assertTrue($v === $this->ra->get($k)); - } - - // check each key individually using a new connection - foreach($this->strings as $k => $v) { - list($host, $port) = split(':', $this->ra->_target($k)); - - $r = new Redis; - $r->pconnect($host, (int)$port); - $this->assertTrue($v === $r->get($k)); - } - } - - public function testMGet() { - $this->assertTrue(array_values($this->strings) === $this->ra->mget(array_keys($this->strings))); - } - - private function addData($commonString) { - $this->data = array(); - for($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) { - $k = rand().'_'.$commonString.'_'.rand(); - $this->data[$k] = rand(); - } - $this->ra->mset($this->data); - } - - private function checkCommonLocality() { - // check that they're all on the same node. - $lastNode = NULL; - foreach($this->data as $k => $v) { - $node = $this->ra->_target($k); - if($lastNode) { - $this->assertTrue($node === $lastNode); - } - $this->assertTrue($this->ra->get($k) == $v); - $lastNode = $node; - } - } - - public function testKeyLocality() { - - // basic key locality with default hash - $this->addData('{hashed part of the key}'); - $this->checkCommonLocality(); - - // with common hashing function - global $newRing, $oldRing, $useIndex; - $this->ra = new RedisArray($newRing, array('previous' => $oldRing, - 'index' => $useIndex, - 'function' => 'custom_hash')); - - // basic key locality with custom hash - $this->addData('fb'.rand()); - $this->checkCommonLocality(); - } - - public function customDistributor($key) - { - $a = unpack("N*", md5($key, true)); - global $newRing; - $pos = abs($a[1]) % count($newRing); - - return $pos; - } - - public function testKeyDistributor() - { - global $newRing, $useIndex; - $this->ra = new RedisArray($newRing, array( - 'index' => $useIndex, - 'function' => 'custom_hash', - 'distributor' => array($this, "customDistributor"))); - - // custom key distribution function. - $this->addData('fb'.rand()); - - // check that they're all on the expected node. - $lastNode = NULL; - foreach($this->data as $k => $v) { - $node = $this->ra->_target($k); - $pos = $this->customDistributor($k); - $this->assertTrue($node === $newRing[$pos]); - } - } + private $strings; + public $ra = NULL; + private $data = NULL; + + public function setUp() { + + // initialize strings. + $n = REDIS_ARRAY_DATA_SIZE; + $this->strings = array(); + for($i = 0; $i < $n; $i++) { + $this->strings['key-'.$i] = 'val-'.$i; + } + + global $newRing, $oldRing, $useIndex; + $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); + } + + public function testMSet() { + // run mset + $this->assertTrue(TRUE === $this->ra->mset($this->strings)); + + // check each key individually using the array + foreach($this->strings as $k => $v) { + $this->assertTrue($v === $this->ra->get($k)); + } + + // check each key individually using a new connection + foreach($this->strings as $k => $v) { + list($host, $port) = split(':', $this->ra->_target($k)); + + $r = new Redis; + $r->pconnect($host, (int)$port); + $this->assertTrue($v === $r->get($k)); + } + } + + public function testMGet() { + $this->assertTrue(array_values($this->strings) === $this->ra->mget(array_keys($this->strings))); + } + + private function addData($commonString) { + $this->data = array(); + for($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) { + $k = rand().'_'.$commonString.'_'.rand(); + $this->data[$k] = rand(); + } + $this->ra->mset($this->data); + } + + private function checkCommonLocality() { + // check that they're all on the same node. + $lastNode = NULL; + foreach($this->data as $k => $v) { + $node = $this->ra->_target($k); + if($lastNode) { + $this->assertTrue($node === $lastNode); + } + $this->assertTrue($this->ra->get($k) == $v); + $lastNode = $node; + } + } + + public function testKeyLocality() { + + // basic key locality with default hash + $this->addData('{hashed part of the key}'); + $this->checkCommonLocality(); + + // with common hashing function + global $newRing, $oldRing, $useIndex; + $this->ra = new RedisArray($newRing, array('previous' => $oldRing, + 'index' => $useIndex, + 'function' => 'custom_hash')); + + // basic key locality with custom hash + $this->addData('fb'.rand()); + $this->checkCommonLocality(); + } + + public function customDistributor($key) + { + $a = unpack("N*", md5($key, true)); + global $newRing; + $pos = abs($a[1]) % count($newRing); + + return $pos; + } + + public function testKeyDistributor() + { + global $newRing, $useIndex; + $this->ra = new RedisArray($newRing, array( + 'index' => $useIndex, + 'function' => 'custom_hash', + 'distributor' => array($this, "customDistributor"))); + + // custom key distribution function. + $this->addData('fb'.rand()); + + // check that they're all on the expected node. + $lastNode = NULL; + foreach($this->data as $k => $v) { + $node = $this->ra->_target($k); + $pos = $this->customDistributor($k); + $this->assertTrue($node === $newRing[$pos]); + } + } } class Redis_Rehashing_Test extends TestSuite { - public $ra = NULL; - private $useIndex; + public $ra = NULL; + private $useIndex; - // data - private $strings; - private $sets; - private $lists; - private $hashes; - private $zsets; + // data + private $strings; + private $sets; + private $lists; + private $hashes; + private $zsets; - public function setUp() { + public function setUp() { - // initialize strings. - $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); - for($i = 0; $i < $n; $i++) { - $this->strings['key-'.$i] = 'val-'.$i; - } - - // initialize sets - for($i = 0; $i < $n; $i++) { - // each set has 20 elements - $this->sets['set-'.$i] = range($i, $i+20); - } + // initialize strings. + $n = REDIS_ARRAY_DATA_SIZE; + $this->strings = array(); + for($i = 0; $i < $n; $i++) { + $this->strings['key-'.$i] = 'val-'.$i; + } - // initialize lists - for($i = 0; $i < $n; $i++) { - // each list has 20 elements - $this->lists['list-'.$i] = range($i, $i+20); - } - - // initialize hashes - for($i = 0; $i < $n; $i++) { - // each hash has 5 keys - $this->hashes['hash-'.$i] = array('A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4); - } + // initialize sets + for($i = 0; $i < $n; $i++) { + // each set has 20 elements + $this->sets['set-'.$i] = range($i, $i+20); + } - // initialize sorted sets - for($i = 0; $i < $n; $i++) { - // each sorted sets has 5 elements - $this->zsets['zset-'.$i] = array($i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E'); - } - - global $newRing, $oldRing, $useIndex; - - // create array - $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); - } - - public function testFlush() { - - // flush all servers first. - global $serverList; - foreach($serverList as $s) { - list($host, $port) = explode(':', $s); - - $r = new Redis; - $r->pconnect($host, (int)$port); - $r->flushdb(); - } - } - - - private function distributeKeys() { - - // strings - foreach($this->strings as $k => $v) { - $this->ra->set($k, $v); - } - - // sets - foreach($this->sets as $k => $v) { - call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v)); - } - - // lists - foreach($this->lists as $k => $v) { - call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v)); - } - - // hashes - foreach($this->hashes as $k => $v) { - $this->ra->hmset($k, $v); - } - - // sorted sets - foreach($this->zsets as $k => $v) { - call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v)); - } - } - - public function testDistribution() { - - $this->distributeKeys(); - } - - public function testSimpleRead() { - - $this->readAllvalues(); - } - - private function readAllvalues() { - - // strings - foreach($this->strings as $k => $v) { - $this->ra->get($k); - - // $this->assertTrue($this->ra->get($k) === $v); - } - - // sets - foreach($this->sets as $k => $v) { - $ret = $this->ra->smembers($k); // get values - - // sort sets - sort($v); - sort($ret); - - //$this->assertTrue($ret == $v); - } - - // lists - foreach($this->lists as $k => $v) { - $ret = $this->ra->lrange($k, 0, -1); - //$this->assertTrue($ret == $v); - } + // initialize lists + for($i = 0; $i < $n; $i++) { + // each list has 20 elements + $this->lists['list-'.$i] = range($i, $i+20); + } - // hashes - foreach($this->hashes as $k => $v) { - $ret = $this->ra->hgetall($k); // get values - //$this->assertTrue($ret == $v); - } + // initialize hashes + for($i = 0; $i < $n; $i++) { + // each hash has 5 keys + $this->hashes['hash-'.$i] = array('A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4); + } - // sorted sets - foreach($this->zsets as $k => $v) { - $ret = $this->ra->zrange($k, 0, -1, TRUE); // get values with scores + // initialize sorted sets + for($i = 0; $i < $n; $i++) { + // each sorted sets has 5 elements + $this->zsets['zset-'.$i] = array($i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E'); + } - // create assoc array from local dataset - $tmp = array(); - for($i = 0; $i < count($v); $i += 2) { - $tmp[$v[$i+1]] = $v[$i]; - } + global $newRing, $oldRing, $useIndex; - // compare to RA value - //$this->assertTrue($ret == $tmp); - } - } + // create array + $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); + } - // add a new node. - public function testCreateSecondRing() { + public function testFlush() { + // flush all servers first. + global $serverList; + foreach($serverList as $s) { + list($host, $port) = explode(':', $s); - global $newRing, $oldRing, $serverList; - $oldRing = $newRing; // back up the original. - $newRing = $serverList; // add a new node to the main ring. - } + $r = new Redis(); + $r->pconnect($host, (int)$port, 0); + $r->flushdb(); + } + } - public function testReadUsingFallbackMechanism() { - $this->readAllvalues(); // some of the reads will fail and will go to another target node. - } - public function testRehash() { - $this->ra->_rehash(); // this will redistribute the keys - } - - public function testRehashWithCallback() { - $total = 0; - $this->ra->_rehash(function ($host, $count) use (&$total) { - $total += $count; - }); - $this->assertTrue($total > 0); - } - - public function testReadRedistributedKeys() { - $this->readAllvalues(); // we shouldn't have any missed reads now. - } -} + private function distributeKeys() { -// Test auto-migration of keys -class Redis_Auto_Rehashing_Test extends TestSuite { + // strings + foreach($this->strings as $k => $v) { + $this->ra->set($k, $v); + } + + // sets + foreach($this->sets as $k => $v) { + call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v)); + } + + // lists + foreach($this->lists as $k => $v) { + call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v)); + } - public $ra = NULL; + // hashes + foreach($this->hashes as $k => $v) { + $this->ra->hmset($k, $v); + } + + // sorted sets + foreach($this->zsets as $k => $v) { + call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v)); + } + } - // data - private $strings; + public function testDistribution() { + $this->distributeKeys(); + } - public function setUp() { + public function testSimpleRead() { + $this->readAllvalues(); + } - // initialize strings. - $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); - for($i = 0; $i < $n; $i++) { - $this->strings['key-'.$i] = 'val-'.$i; - } + private function readAllvalues() { - global $newRing, $oldRing, $useIndex; + // strings + foreach($this->strings as $k => $v) { + $this->assertTrue($this->ra->get($k) === $v); + } - // create array - $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE)); - } + // sets + foreach($this->sets as $k => $v) { + $ret = $this->ra->smembers($k); // get values - public function testDistribute() { - // strings - foreach($this->strings as $k => $v) { - $this->ra->set($k, $v); - } - } + // sort sets + sort($v); + sort($ret); - private function readAllvalues() { - foreach($this->strings as $k => $v) { - $this->ra->get($k); + $this->assertTrue($ret == $v); + } - //$this->assertTrue($this->ra->get($k) === $v); + // lists + foreach($this->lists as $k => $v) { + $ret = $this->ra->lrange($k, 0, -1); + $this->assertTrue($ret == $v); } - } + // hashes + foreach($this->hashes as $k => $v) { + $ret = $this->ra->hgetall($k); // get values + $this->assertTrue($ret == $v); + } - public function testReadAll() { - $this->readAllvalues(); - } + // sorted sets + foreach($this->zsets as $k => $v) { + $ret = $this->ra->zrange($k, 0, -1, TRUE); // get values with scores - // add a new node. - public function testCreateSecondRing() { + // create assoc array from local dataset + $tmp = array(); + for($i = 0; $i < count($v); $i += 2) { + $tmp[$v[$i+1]] = $v[$i]; + } - global $newRing, $oldRing, $serverList; - $oldRing = $newRing; // back up the original. - $newRing = $serverList; // add a new node to the main ring. - } + // compare to RA value + $this->assertTrue($ret == $tmp); + } + } - // Read and migrate keys on fallback, causing the whole ring to be rehashed. - public function testReadAndMigrateAll() { - $this->readAllvalues(); - } + // add a new node. + public function testCreateSecondRing() { - // Read and migrate keys on fallback, causing the whole ring to be rehashed. - public function testAllKeysHaveBeenMigrated() { - foreach($this->strings as $k => $v) { - // get the target for each key - $target = $this->ra->_target($k); + global $newRing, $oldRing, $serverList; + $oldRing = $newRing; // back up the original. + $newRing = $serverList; // add a new node to the main ring. + } - // connect to the target host - list($host,$port) = split(':', $target); - $r = new Redis; - $r->pconnect($host, $port); + public function testReadUsingFallbackMechanism() { + $this->readAllvalues(); // some of the reads will fail and will go to another target node. + } - $this->assertTrue($v === $r->get($k)); // check that the key has actually been migrated to the new node. - } - } + public function testRehash() { + $this->ra->_rehash(); // this will redistribute the keys + } + + public function testRehashWithCallback() { + $total = 0; + $this->ra->_rehash(function ($host, $count) use (&$total) { + $total += $count; + }); + $this->assertTrue($total > 0); + } + + public function testReadRedistributedKeys() { + $this->readAllvalues(); // we shouldn't have any missed reads now. + } } -// Test node-specific multi/exec -class Redis_Multi_Exec_Test extends TestSuite { +// Test auto-migration of keys +class Redis_Auto_Rehashing_Test extends TestSuite { - public $ra = NULL; + public $ra = NULL; - public function setUp() { + // data + private $strings; - global $newRing, $oldRing, $useIndex; - // create array - $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); - } + public function setUp() { + // initialize strings. + $n = REDIS_ARRAY_DATA_SIZE; + $this->strings = array(); + for($i = 0; $i < $n; $i++) { + $this->strings['key-'.$i] = 'val-'.$i; + } - public function testInit() { - $this->ra->set('{groups}:managers', 2); - $this->ra->set('{groups}:executives', 3); + global $newRing, $oldRing, $useIndex; - $this->ra->set('1_{employee:joe}_name', 'joe'); - $this->ra->set('1_{employee:joe}_group', 2); - $this->ra->set('1_{employee:joe}_salary', 2000); - } + // create array + $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE)); + } - public function testKeyDistribution() { - // check that all of joe's keys are on the same instance - $lastNode = NULL; - foreach(array('name', 'group', 'salary') as $field) { - $node = $this->ra->_target('1_{employee:joe}_'.$field); - if($lastNode) { - $this->assertTrue($node === $lastNode); - } - $lastNode = $node; - } - } + public function testDistribute() { + // strings + foreach($this->strings as $k => $v) { + $this->ra->set($k, $v); + } + } - public function testMultiExec() { + private function readAllvalues() { + foreach($this->strings as $k => $v) { + $this->assertTrue($this->ra->get($k) === $v); + } + } - // Joe gets a promotion - $newGroup = $this->ra->get('{groups}:executives'); - $newSalary = 4000; - // change both in a transaction. - $host = $this->ra->_target('{employee:joe}'); // transactions are per-node, so we need a reference to it. - $tr = $this->ra->multi($host) - ->set('1_{employee:joe}_group', $newGroup) - ->set('1_{employee:joe}_salary', $newSalary) - ->exec(); + public function testReadAll() { + $this->readAllvalues(); + } - // check that the group and salary have been changed - $this->assertTrue($this->ra->get('1_{employee:joe}_group') === $newGroup); - $this->assertTrue($this->ra->get('1_{employee:joe}_salary') == $newSalary); + // add a new node. + public function testCreateSecondRing() { + global $newRing, $oldRing, $serverList; + $oldRing = $newRing; // back up the original. + $newRing = $serverList; // add a new node to the main ring. + } - } + // Read and migrate keys on fallback, causing the whole ring to be rehashed. + public function testReadAndMigrateAll() { + $this->readAllvalues(); + } - public function testMultiExecMSet() { + // Read and migrate keys on fallback, causing the whole ring to be rehashed. + public function testAllKeysHaveBeenMigrated() { + foreach($this->strings as $k => $v) { + // get the target for each key + $target = $this->ra->_target($k); + + // connect to the target host + list($host,$port) = split(':', $target); + $r = new Redis; + $r->pconnect($host, $port); + + $this->assertTrue($v === $r->get($k)); // check that the key has actually been migrated to the new node. + } + } +} + +// Test node-specific multi/exec +class Redis_Multi_Exec_Test extends TestSuite { + public $ra = NULL; + + public function setUp() { + global $newRing, $oldRing, $useIndex; + // create array + $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); + } + + public function testInit() { + $this->ra->set('{groups}:managers', 2); + $this->ra->set('{groups}:executives', 3); + + $this->ra->set('1_{employee:joe}_name', 'joe'); + $this->ra->set('1_{employee:joe}_group', 2); + $this->ra->set('1_{employee:joe}_salary', 2000); + } + + public function testKeyDistribution() { + // check that all of joe's keys are on the same instance + $lastNode = NULL; + foreach(array('name', 'group', 'salary') as $field) { + $node = $this->ra->_target('1_{employee:joe}_'.$field); + if($lastNode) { + $this->assertTrue($node === $lastNode); + } + $lastNode = $node; + } + } + + public function testMultiExec() { + + // Joe gets a promotion + $newGroup = $this->ra->get('{groups}:executives'); + $newSalary = 4000; + + // change both in a transaction. + $host = $this->ra->_target('{employee:joe}'); // transactions are per-node, so we need a reference to it. + $tr = $this->ra->multi($host) + ->set('1_{employee:joe}_group', $newGroup) + ->set('1_{employee:joe}_salary', $newSalary) + ->exec(); - global $newGroup, $newSalary; - $newGroup = 1; - $newSalary = 10000; + // check that the group and salary have been changed + $this->assertTrue($this->ra->get('1_{employee:joe}_group') === $newGroup); + $this->assertTrue($this->ra->get('1_{employee:joe}_salary') == $newSalary); - // test MSET, making Joe a top-level executive - $out = $this->ra->multi($this->ra->_target('{employee:joe}')) - ->mset(array('1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary)) - ->exec(); + } - $this->assertTrue($out[0] === TRUE); - } + public function testMultiExecMSet() { - public function testMultiExecMGet() { + global $newGroup, $newSalary; + $newGroup = 1; + $newSalary = 10000; - global $newGroup, $newSalary; + // test MSET, making Joe a top-level executive + $out = $this->ra->multi($this->ra->_target('{employee:joe}')) + ->mset(array('1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary)) + ->exec(); - // test MGET - $out = $this->ra->multi($this->ra->_target('{employee:joe}')) - ->mget(array('1_{employee:joe}_group', '1_{employee:joe}_salary')) - ->exec(); + $this->assertTrue($out[0] === TRUE); + } - $this->assertTrue($out[0][0] == $newGroup); - $this->assertTrue($out[0][1] == $newSalary); - } + public function testMultiExecMGet() { - public function testMultiExecDel() { + global $newGroup, $newSalary; - // test DEL - $out = $this->ra->multi($this->ra->_target('{employee:joe}')) - ->del('1_{employee:joe}_group', '1_{employee:joe}_salary') - ->exec(); + // test MGET + $out = $this->ra->multi($this->ra->_target('{employee:joe}')) + ->mget(array('1_{employee:joe}_group', '1_{employee:joe}_salary')) + ->exec(); - $this->assertTrue($out[0] === 2); - $this->assertTrue($this->ra->exists('1_{employee:joe}_group') === FALSE); - $this->assertTrue($this->ra->exists('1_{employee:joe}_salary') === FALSE); - } + $this->assertTrue($out[0][0] == $newGroup); + $this->assertTrue($out[0][1] == $newSalary); + } - public function testDiscard() { - /* phpredis issue #87 */ - $key = 'test_err'; + public function testMultiExecDel() { - $this->assertTrue($this->ra->set($key, 'test')); - $this->assertTrue('test' === $this->ra->get($key)); + // test DEL + $out = $this->ra->multi($this->ra->_target('{employee:joe}')) + ->del('1_{employee:joe}_group', '1_{employee:joe}_salary') + ->exec(); - $this->ra->watch($key); + $this->assertTrue($out[0] === 2); + $this->assertTrue($this->ra->exists('1_{employee:joe}_group') === FALSE); + $this->assertTrue($this->ra->exists('1_{employee:joe}_salary') === FALSE); + } - // After watch, same - $this->assertTrue('test' === $this->ra->get($key)); + public function testDiscard() { + /* phpredis issue #87 */ + $key = 'test_err'; - // change in a multi/exec block. - $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test1')->exec(); - $this->assertTrue($ret === array(true)); + $this->assertTrue($this->ra->set($key, 'test')); + $this->assertTrue('test' === $this->ra->get($key)); - // Get after exec, 'test1': - $this->assertTrue($this->ra->get($key) === 'test1'); + $this->ra->watch($key); - $this->ra->watch($key); + // After watch, same + $this->assertTrue('test' === $this->ra->get($key)); - // After second watch, still test1. - $this->assertTrue($this->ra->get($key) === 'test1'); + // change in a multi/exec block. + $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test1')->exec(); + $this->assertTrue($ret === array(true)); - $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test2')->discard(); - // Ret after discard: NULL"; - $this->assertTrue($ret === NULL); + // Get after exec, 'test1': + $this->assertTrue($this->ra->get($key) === 'test1'); - // Get after discard, unchanged: - $this->assertTrue($this->ra->get($key) === 'test1'); - } + $this->ra->watch($key); + + // After second watch, still test1. + $this->assertTrue($this->ra->get($key) === 'test1'); + + $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test2')->discard(); + // Ret after discard: NULL"; + $this->assertTrue($ret === NULL); + + // Get after discard, unchanged: + $this->assertTrue($this->ra->get($key) === 'test1'); + } } // Test custom distribution function class Redis_Distributor_Test extends TestSuite { - public $ra = NULL; - - public function setUp() { - - global $newRing, $oldRing, $useIndex; - // create array - $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'distributor' => array($this, 'distribute'))); - } - - public function testInit() { - $this->ra->set('{uk}test', 'joe'); - $this->ra->set('{us}test', 'bob'); - } - - public function distribute($key) { - $matches = array(); - if (preg_match('/{([^}]+)}.*/', $key, $matches) == 1) { - $countries = array('uk' => 0, 'us' => 1); - if (array_key_exists($matches[1], $countries)) { - return $countries[$matches[1]]; - } - } - return 2; // default server - } - - public function testDistribution() { - $ukServer = $this->ra->_target('{uk}test'); - $usServer = $this->ra->_target('{us}test'); - $deServer = $this->ra->_target('{de}test'); - $defaultServer = $this->ra->_target('unknown'); - - $nodes = $this->ra->_hosts(); - $this->assertTrue($ukServer === $nodes[0]); - $this->assertTrue($usServer === $nodes[1]); - $this->assertTrue($deServer === $nodes[2]); - $this->assertTrue($defaultServer === $nodes[2]); - } + public $ra = NULL; + + public function setUp() { + global $newRing, $oldRing, $useIndex; + // create array + $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'distributor' => array($this, 'distribute'))); + } + + public function testInit() { + $this->ra->set('{uk}test', 'joe'); + $this->ra->set('{us}test', 'bob'); + } + + public function distribute($key) { + $matches = array(); + if (preg_match('/{([^}]+)}.*/', $key, $matches) == 1) { + $countries = array('uk' => 0, 'us' => 1); + if (array_key_exists($matches[1], $countries)) { + return $countries[$matches[1]]; + } + } + return 2; // default server + } + + public function testDistribution() { + $ukServer = $this->ra->_target('{uk}test'); + $usServer = $this->ra->_target('{us}test'); + $deServer = $this->ra->_target('{de}test'); + $defaultServer = $this->ra->_target('unknown'); + + $nodes = $this->ra->_hosts(); + $this->assertTrue($ukServer === $nodes[0]); + $this->assertTrue($usServer === $nodes[1]); + $this->assertTrue($deServer === $nodes[2]); + $this->assertTrue($defaultServer === $nodes[2]); + } } -function run_tests($className, $str_limit) { - // reset rings - global $newRing, $oldRing, $serverList; - $newRing = array('localhost:6379', 'localhost:6380', 'localhost:6381'); - $oldRing = array(); - $serverList = array('localhost:6379', 'localhost:6380', 'localhost:6381', 'localhost:6382'); +function run_tests($className, $str_filter) { + // reset rings + global $newRing, $oldRing, $serverList; + $newRing = array('localhost:6379', 'localhost:6380', 'localhost:6381'); + $oldRing = array(); + $serverList = array('localhost:6379', 'localhost:6380', 'localhost:6381', 'localhost:6382'); - // run - TestSuite::run($className, $str_limit); + // run + TestSuite::run($className, $str_filter); } define('REDIS_ARRAY_DATA_SIZE', 1000); diff --git a/tests/TestRedis.php b/tests/TestRedis.php index d452204251..7e954753ba 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -34,11 +34,11 @@ foreach(array(true, false) as $useIndex) { echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; - run_tests('Redis_Array_Test'); - run_tests('Redis_Rehashing_Test'); - run_tests('Redis_Auto_Rehashing_Test'); - run_tests('Redis_Multi_Exec_Test'); - run_tests('Redis_Distributor_Test'); + run_tests('Redis_Array_Test', $str_filter); + run_tests('Redis_Rehashing_Test', $str_filter); + run_tests('Redis_Auto_Rehashing_Test', $str_filter); + run_tests('Redis_Multi_Exec_Test', $str_filter); + run_tests('Redis_Distributor_Test', $str_filter); } } else { echo TestSuite::make_bold("RedisCluster") . "\n"; diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 593a322fd1..e7c2c4776d 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -117,7 +117,7 @@ public static function run($className, $str_limit = NULL) { echo self::make_bold($str_out_name); $count = count($className::$errors); - $rt = new $className; + $rt = new $className(); try { $rt->setUp(); $rt->$name(); @@ -145,7 +145,7 @@ public static function run($className, $str_limit = NULL) { echo implode('', $className::$warnings); if(empty($className::$errors)) { - echo "\nAll tests passed. \o/\n"; + echo "All tests passed. \o/\n"; return 0; } From 08ecec92a3e3cad1c74bec31e6a74dce03ed3e84 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 6 Mar 2015 12:16:52 -0800 Subject: [PATCH 0450/1986] Unit test suite improvements and cluster failover test * Added a test specifically for RedisCluster to test slave failover settings. * Added an option to specifically disable colorization, as well as a mechanism to determine if the output is being piped, such that we can turn colorization off in that case as well. --- tests/RedisArrayTest.php | 2 +- tests/RedisClusterTest.php | 158 ++++++++++++++++++++++++++++++++++++- tests/RedisTest.php | 2 +- tests/TestRedis.php | 13 ++- tests/TestSuite.php | 29 +++++-- 5 files changed, 189 insertions(+), 15 deletions(-) diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index c838d870c3..86c63b05c4 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -1,4 +1,4 @@ -newInstance(); // new instance, modifying `x'. $r->incr('x'); + // This transaction should fail because the other client changed 'x' $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === Array(FALSE)); // failed because another client changed our watched key between WATCH and EXEC. - + $this->assertTrue($ret === Array(FALSE)); // watch and unwatch $this->redis->watch('x'); $r->incr('x'); // other instance $this->redis->unwatch('x'); // cancel transaction watch + // This should succeed as the watch has been cancelled $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === array('44')); // succeeded since we've cancel the WATCH command. + $this->assertTrue($ret === array('44')); } /* RedisCluster::script() is a 'raw' command, which requires a key such that @@ -281,5 +296,140 @@ public function testEvalSHA() { $this->assertTrue(1 === $this->redis->eval($scr,Array($str_key), 1)); $this->assertTrue(1 === $this->redis->evalsha($sha,Array($str_key), 1)); } + + protected function genKeyName($i_key_idx, $i_type) { + switch ($i_type) { + case Redis::REDIS_STRING: + return "string-$i_key_idx"; + case Redis::REDIS_SET: + return "set-$i_key_idx"; + case Redis::REDIS_LIST: + return "list-$i_key_idx"; + case Redis::REDIS_ZSET: + return "zset-$i_key_idx"; + case Redis::REDIS_HASH: + return "hash-$i_key_idx"; + default: + return "unknown-$i_key_idx"; + } + } + + protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) { + $str_key = $this->genKeyName($i_key_idx, $i_type); + + $this->redis->del($str_key); + + switch ($i_type) { + case Redis::REDIS_STRING: + $value = "$str_key-value"; + $this->redis->set($str_key, $value); + break; + case Redis::REDIS_SET: + $value = Array( + $str_key . '-mem1', $str_key . '-mem2', $str_key . '-mem3', + $str_key . '-mem4', $str_key . '-mem5', $str_key . '-mem6' + ); + $arr_args = $value; + array_unshift($arr_args, $str_key); + call_user_func_array(Array($this->redis, 'sadd'), $arr_args); + break; + case Redis::REDIS_HASH: + $value = Array( + $str_key . '-mem1' => $str_key . '-val1', + $str_key . '-mem2' => $str_key . '-val2', + $str_key . '-mem3' => $str_key . '-val3' + ); + $this->redis->hmset($str_key, $value); + break; + case Redis::REDIS_LIST: + $value = Array( + $str_key . '-ele1', $str_key . '-ele2', $str_key . '-ele3', + $str_key . '-ele4', $str_key . '-ele5', $str_key . '-ele6' + ); + $arr_args = $value; + array_unshift($arr_args, $str_key); + call_user_func_array(Array($this->redis, 'rpush'), $arr_args); + break; + case Redis::REDIS_ZSET: + $i_score = 1; + $value = Array( + $str_key . '-mem1' => 1, $str_key . '-mem2' => 2, + $str_key . '-mem3' => 3, $str_key . '-mem3' => 3 + ); + foreach ($value as $str_mem => $i_score) { + $this->redis->zadd($str_key, $i_score, $str_mem); + } + break; + } + + /* Update our reference array so we can verify values */ + $arr_ref[$str_key] = $value; + return $str_key; + } + + /* Verify that our ZSET values are identical */ + protected function checkZSetEquality($a, $b) { + /* If the count is off, the array keys are different or the sums are + * different, we know there is something off */ + $boo_diff = count($a) != count($b) || + count(array_diff(array_keys($a), array_keys($b))) != 0 || + array_sum($a) != array_sum($b); + + if ($boo_diff) { + $this->assertEquals($a,$b); + return; + } + } + + protected function checkKeyValue($str_key, $i_type, $value) { + switch ($i_type) { + case Redis::REDIS_STRING: + $this->assertEquals($value, $this->redis->get($str_key)); + break; + case Redis::REDIS_SET: + $arr_r_values = $this->redis->sMembers($str_key); + $arr_l_values = $value; + sort($arr_r_values); + sort($arr_l_values); + $this->assertEquals($arr_r_values, $arr_l_values); + break; + case Redis::REDIS_LIST: + $this->assertEquals($value, $this->redis->lrange($str_key,0,-1)); + break; + case Redis::REDIS_HASH: + $this->assertEquals($value, $this->redis->hgetall($str_key)); + break; + case Redis::REDIS_ZSET: + $this->checkZSetEquality($value, $this->redis->zrange($str_key,0,-1,true)); + break; + default: + throw new Exception("Unknown type " . $i_type); + } + } + + /* Test automatic load distributor */ + public function testFailOver() { + $arr_value_ref = Array(); + $arr_type_ref = Array(); + + /* Set a bunch of keys of various redis types*/ + for ($i = 0; $i < 200; $i++) { + foreach ($this->_arr_redis_types as $i_type) { + $str_key = $this->setKeyVals($i, $i_type, $arr_value_ref); + $arr_type_ref[$str_key] = $i_type; + } + } + + /* Iterate over failover options */ + foreach ($this->_arr_failover_types as $i_opt) { + $this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $i_opt); + + foreach ($arr_value_ref as $str_key => $value) { + $this->checkKeyValue($str_key, $arr_type_ref[$str_key], $value); + } + + break; + } + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index a9c94f4691..159436c2c6 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1,4 +1,4 @@ - Date: Fri, 6 Mar 2015 18:40:16 -0800 Subject: [PATCH 0451/1986] Fix INFO command so it works in a MULTi EXEC block --- cluster_library.c | 24 ++++++++-------- redis_cluster.c | 58 ++++++++++++++++++++++++++------------ tests/RedisClusterTest.php | 13 ++++++++- 3 files changed, 63 insertions(+), 32 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index d7addf20d0..6c40e4667a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1258,6 +1258,17 @@ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, c->cmd_slot = slot; c->cmd_sock = SLOT_SOCK(c, slot); + /* Enable multi mode on this slot if we've been directed to but haven't + * send it to this node yet */ + if (c->flags->mode == MULTI && c->cmd_sock->mode != MULTI) { + if (cluster_send_multi(c, slot TSRMLS_CC) == -1) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to enter MULTI mode on requested slot", + 0 TSRMLS_CC); + return -1; + } + } + /* Try the slot */ if(cluster_sock_write(c, cmd, cmd_len, 1 TSRMLS_CC)==-1) { return -1; @@ -1411,19 +1422,6 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, add_next_index_stringl(c->multi_resp, resp, c->reply_len, 0); } } - - /* - // Return the string if we can unserialize it - if(redis_unserialize(c->flags, resp, c->reply_len, &z_ret TSRMLS_CC)==0) { - CLUSTER_RETURN_STRING(c, resp, c->reply_len); - } else { - if(CLUSTER_IS_ATOMIC(c)) { - *return_value = *z_ret; - } else { - add_next_index_zval(c->multi_resp, z_ret); - } - efree(resp); - }*/ } /* Bulk response where we expect a double */ diff --git a/redis_cluster.c b/redis_cluster.c index e054b4e1ca..242432362f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2221,32 +2221,43 @@ PHP_METHOD(RedisCluster, discard) { static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { + int key_len, key_free; + zval **z_host, **z_port, *z_tmp = NULL; short slot; + char *key; /* If it's a string, treat it as a key. Otherwise, look for a two * element array */ - if(Z_TYPE_P(z_arg)==IS_STRING) { - char *key = Z_STRVAL_P(z_arg); - int key_len = Z_STRLEN_P(z_arg), key_free; + if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG || + Z_TYPE_P(z_arg)==IS_DOUBLE) + { + /* Allow for any scalar here */ + if (Z_TYPE_P(z_arg) != IS_STRING) { + MAKE_STD_ZVAL(z_tmp); + *z_tmp = *z_arg; + zval_copy_ctor(z_tmp); + convert_to_string(z_tmp); + z_arg = z_tmp; + } + + key = Z_STRVAL_P(z_arg); + key_len = Z_STRLEN_P(z_arg); /* Hash it */ key_free = redis_key_prefix(c->flags, &key, &key_len); slot = cluster_hash_key(key, key_len); if(key_free) efree(key); - } else { - zval **z_host, **z_port; - - /* We'll need two elements, one string, one long */ - if(Z_TYPE_P(z_arg) != IS_ARRAY || - zend_hash_index_find(Z_ARRVAL_P(z_arg),0,(void**)&z_host)==FAILURE || - zend_hash_index_find(Z_ARRVAL_P(z_arg),1,(void**)&z_port)==FAILURE || - Z_TYPE_PP(z_host)!=IS_STRING || Z_TYPE_PP(z_port)!=IS_LONG) - { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Directed commands must be passed string key or [host,port]"); - return -1; - } + /* Destroy our temp value if we had to convert it */ + if (z_tmp) { + zval_dtor(z_tmp); + efree(z_tmp); + } + } else if (Z_TYPE_P(z_arg) == IS_ARRAY && + zend_hash_index_find(Z_ARRVAL_P(z_arg),0,(void**)&z_host)!=FAILURE && + zend_hash_index_find(Z_ARRVAL_P(z_arg),1,(void**)&z_port)!=FAILURE && + Z_TYPE_PP(z_host)==IS_STRING && Z_TYPE_PP(z_port)==IS_LONG) + { /* Attempt to find this specific node by host:port */ slot = cluster_find_slot(c,(const char *)Z_STRVAL_PP(z_host), (unsigned short)Z_LVAL_PP(z_port)); @@ -2256,6 +2267,10 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port)); } + } else { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Direted commands musty be passed a key or [host,port] array"); + return -1; } return slot; @@ -2618,8 +2633,10 @@ PHP_METHOD(RedisCluster, lastsave) { * proto array RedisCluster::info(array host_port, [string $arg]) */ PHP_METHOD(RedisCluster, info) { redisCluster *c = GET_CONTEXT(); + REDIS_REPLY_TYPE rtype; char *cmd, *opt=NULL; int cmd_len, opt_len; + void *ctx = NULL; zval *z_arg; short slot; @@ -2643,14 +2660,19 @@ PHP_METHOD(RedisCluster, info) { cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); } - if(cluster_send_slot(c, slot, cmd, cmd_len, TYPE_BULK TSRMLS_CC)<0) { + rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; + if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send INFO command to specific node", 0 TSRMLS_CC); efree(cmd); RETURN_FALSE; } - cluster_info_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + if (CLUSTER_IS_ATOMIC(c)) { + cluster_info_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_info_resp, ctx); + } efree(cmd); } diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 690be4186a..acda8fc4e5 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -32,7 +32,6 @@ public function testSortDesc() { return $this->markTestSkipped(); } public function testWait() { return $this->markTestSkipped(); } public function testSelect() { return $this->markTestSkipped(); } public function testReconnectSelect() { return $this->markTestSkipped(); } - public function testIntrospection() { return $this->markTestSkipped(); } /* Skips for now, which need attention */ public function testClient() { return $this->markTestSkipped(); } @@ -297,6 +296,18 @@ public function testEvalSHA() { $this->assertTrue(1 === $this->redis->evalsha($sha,Array($str_key), 1)); } + /* Cluster specific introspection stuff */ + public function testIntrospection() { + $arr_masters = $this->redis->_masters(); + $this->assertTrue(is_array($arr_masters)); + + foreach ($arr_masters as $arr_info) { + $this->assertTrue(is_array($arr_info)); + $this->assertTrue(is_string($arr_info[0])); + $this->assertTrue(is_long($arr_info[1])); + } + } + protected function genKeyName($i_key_idx, $i_type) { switch ($i_type) { case Redis::REDIS_STRING: From 7e7eec6281730163eee5f9e36b0be939b9f12485 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 6 Mar 2015 19:42:32 -0800 Subject: [PATCH 0452/1986] Make RedisCluster::client('list') work like Redis --- cluster_library.c | 25 +++++++++++++++++ cluster_library.h | 4 +++ library.c | 55 ++++++++++++++++++++++---------------- library.h | 1 + redis_cluster.c | 68 ++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 124 insertions(+), 29 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 6c40e4667a..51d2c3d110 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1920,6 +1920,31 @@ PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } } +/* CLIENT LIST response */ +PHPAPI void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + zval *z_result; + char *info; + + /* Read the bulk response */ + info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC); + if (info == NULL) { + CLUSTER_RETURN_FALSE(c); + } + + /* Parse it and free the bulk string */ + z_result = redis_parse_client_list_response(info); + efree(info); + + if (CLUSTER_IS_ATOMIC(c)) { + *return_value = *z_result; + efree(z_result); + } else { + add_next_index_zval(c->multi_resp, z_result); + } +} + /* MULTI BULK response loop where we might pull the next one */ PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb) diff --git a/cluster_library.h b/cluster_library.h index aee2f761e8..d97de2a498 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -436,6 +436,10 @@ PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +/* CLIENT LIST response handler */ +PHPAPI void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); + /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); diff --git a/library.c b/library.c index 2f78a83ee6..e03c0e73a9 100644 --- a/library.c +++ b/library.c @@ -1024,20 +1024,31 @@ PHPAPI zval *redis_parse_info_response(char *response) { PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { char *resp; int resp_len; - zval *z_result, *z_sub_result; + zval *z_ret; - /* Pointers for parsing */ - char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value; + /* Make sure we can read the bulk response from Redis */ + if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } - /* Key length, done flag */ - int klen = 0, done = 0, is_numeric; + /* Parse it out */ + z_ret = redis_parse_client_list_response(resp); - /* Make sure we can read a response from Redis */ - if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { - RETURN_FALSE; + /* Free our response */ + efree(resp); + + /* Return or append depending if we're atomic */ + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_ret); + } else { + RETVAL_ZVAL(z_ret, 0, 1); } +} + +PHPAPI zval* redis_parse_client_list_response(char *response) { + zval *z_result, *z_sub_result; - /* Allocate memory for our response */ + // Allocate memory for our response MAKE_STD_ZVAL(z_result); array_init(z_result); @@ -1045,8 +1056,9 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo ALLOC_INIT_ZVAL(z_sub_result); array_init(z_sub_result); - p = resp; - lpos = resp; + // Pointers for parsing + char *p = response, *lpos = response, *p2, *key; + char *kpos = NULL, *vpos = NULL, *value; /* While we've got more to parse */ while(!done) { @@ -1107,9 +1119,11 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo /* Free our key */ efree(key); } else { - /* Something is wrong */ - efree(resp); - RETURN_FALSE; + // Something is wrong + zval_dtor(z_result); + MAKE_STD_ZVAL(z_result); + ZVAL_BOOL(z_result, 0); + return z_result; } /* Move forward */ @@ -1132,18 +1146,13 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo p++; } - /* Free our respoonse */ - efree(resp); - - IF_MULTI_OR_PIPELINE() { - add_next_index_zval(z_tab, z_result); - } else { - RETVAL_ZVAL(z_result, 0, 1); - } + /* Return our parsed response */ + return z_result; } PHPAPI void -redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, +redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx, SuccessCallback success_callback) { diff --git a/library.h b/library.h index 37ea22e5c7..8de44166f5 100644 --- a/library.h +++ b/library.h @@ -28,6 +28,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI zval *redis_parse_info_response(char *resp); +PHPAPI zval *redis_parse_client_list_response(char *resp); PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis_cluster.c b/redis_cluster.c index 242432362f..63bfeeddaf 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2678,6 +2678,68 @@ PHP_METHOD(RedisCluster, info) { } /* }}} */ +/* {{{ proto array RedisCluster::client('list') + * proto bool RedisCluster::client('kill', $ipport) + * proto bool RedisCluster::client('setname', $name) + * proto string RedisCluster::client('getname') + */ +PHP_METHOD(RedisCluster, client) { + redisCluster *c = GET_CONTEXT(); + char *cmd, *opt=NULL, *arg=NULL; + int cmd_len, opt_len, arg_len; + REDIS_REPLY_TYPE rtype; + zval *z_node; + short slot; + cluster_cb cb; + + /* Parse args */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|s", &z_node, &opt, + &opt_len, &arg, &arg_len)==FAILURE) + { + RETURN_FALSE; + } + + /* Make sure we can properly resolve the slot */ + slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC); + if(slot<0) RETURN_FALSE; + + /* Construct the command */ + if (ZEND_NUM_ARGS() == 3) { + cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len, + arg, arg_len); + } else if(ZEND_NUM_ARGS() == 2) { + cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len); + } else { + zend_wrong_param_count(TSRMLS_C); + RETURN_FALSE; + } + + rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; + if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC)<0) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to send CLIENT command to specific node", 0 TSRMLS_CC); + efree(cmd); + RETURN_FALSE; + } + + /* Handle client list and anything else differently */ + if (opt_len == 4 && !strncasecmp(opt, "list", 4)) { + cb = cluster_client_list_resp; + } else { + cb = cluster_variant_resp; + } + + /* Now enqueue or process response */ + if (CLUSTER_IS_ATOMIC(c)) { + cluster_client_list_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + void *ctx = NULL; + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_client_list_resp, ctx); + } + + efree(cmd); +} + /* {{{ proto mixed RedisCluster::cluster(variant) */ PHP_METHOD(RedisCluster, cluster) { cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CLUSTER", @@ -2685,12 +2747,6 @@ PHP_METHOD(RedisCluster, cluster) { } /* }}} */ -/* {{{ proto mixed RedisCluster::client(string key, ...) - * proto mixed RedisCluster::client(array host_port, ...) */ -PHP_METHOD(RedisCluster, client) { - cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CLIENT", - sizeof("CLIENT")-1); -} /* }}} */ /* {{{ proto mixed RedisCluster::config(string key, ...) From 1725fccf4048133c60c560cf8c540ffe945e584a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 6 Mar 2015 20:17:36 -0800 Subject: [PATCH 0453/1986] Properly handle CLIENT variants :-) --- redis_cluster.c | 31 +++++++++++++++++++++---------- tests/RedisClusterTest.php | 27 ++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 63bfeeddaf..fd9df553b2 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2703,6 +2703,24 @@ PHP_METHOD(RedisCluster, client) { slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC); if(slot<0) RETURN_FALSE; + /* Our return type and reply callback is different for all subcommands */ + if (opt_len == 4 && !strncasecmp(opt, "list", 4)) { + rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; + cb = cluster_client_list_resp; + } else if ((opt_len == 4 && !strncasecmp(opt, "kill", 4)) || + (opt_len == 7 && !strncasecmp(opt, "setname", 7))) + { + rtype = TYPE_LINE; + cb = cluster_bool_resp; + } else if (opt_len == 7 && !strncasecmp(opt, "getname", 7)) { + rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; + cb = cluster_bulk_resp; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Invalid CLIENT subcommand (LIST, KILL, GETNAME, and SETNAME are valid"); + RETURN_FALSE; + } + /* Construct the command */ if (ZEND_NUM_ARGS() == 3) { cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len, @@ -2714,7 +2732,7 @@ PHP_METHOD(RedisCluster, client) { RETURN_FALSE; } - rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; + /* Attempt to write our command */ if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send CLIENT command to specific node", 0 TSRMLS_CC); @@ -2722,19 +2740,12 @@ PHP_METHOD(RedisCluster, client) { RETURN_FALSE; } - /* Handle client list and anything else differently */ - if (opt_len == 4 && !strncasecmp(opt, "list", 4)) { - cb = cluster_client_list_resp; - } else { - cb = cluster_variant_resp; - } - /* Now enqueue or process response */ if (CLUSTER_IS_ATOMIC(c)) { - cluster_client_list_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_client_list_resp, ctx); + CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); } efree(cmd); diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index acda8fc4e5..4315db646f 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -33,9 +33,6 @@ public function testWait() { return $this->markTestSkipped(); } public function testSelect() { return $this->markTestSkipped(); } public function testReconnectSelect() { return $this->markTestSkipped(); } - /* Skips for now, which need attention */ - public function testClient() { return $this->markTestSkipped(); } - /* Load our seeds on construction */ public function __construct() { $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; @@ -125,6 +122,30 @@ public function testInfo() { } } + public function testClient() { + $str_key = 'key-' . rand(1,100); + + $this->assertTrue($this->redis->client($str_key, 'setname', 'cluster_tests')); + + $arr_clients = $this->redis->client($str_key, 'list'); + $this->assertTrue(is_array($arr_clients)); + + /* Find us in the list */ + $str_addr = NULL; + foreach ($arr_clients as $arr_client) { + if ($arr_client['name'] == 'cluster_tests') { + $str_addr = $arr_client['addr']; + break; + } + } + + /* We should be in there */ + $this->assertFalse(empty($str_addr)); + + /* Kill our own client! */ + $this->assertTrue($this->redis->client($str_key, 'kill', $str_addr)); + } + public function testTime() { $time_arr = $this->redis->time("k:" . rand(1,100)); $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && From 489864274bb6119b4a2e00f3432e67cdaae63df4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 6 Mar 2015 20:23:38 -0800 Subject: [PATCH 0454/1986] Properly handle ECHO in MULTI or ATOMIC mode --- redis_cluster.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index fd9df553b2..507892463e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2828,6 +2828,7 @@ PHP_METHOD(RedisCluster, ping) { * proto string RedisCluster::echo(array host_port, string msg) */ PHP_METHOD(RedisCluster, echo) { redisCluster *c = GET_CONTEXT(); + REDIS_REPLY_TYPE rtype; zval *z_arg; char *cmd, *msg; int cmd_len, msg_len; @@ -2852,7 +2853,8 @@ PHP_METHOD(RedisCluster, echo) { cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s", msg, msg_len); /* Send it off */ - if(cluster_send_slot(c,slot,cmd,cmd_len,TYPE_BULK TSRMLS_CC)<0) { + rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; + if(cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send commnad at the specificed node", 0 TSRMLS_CC); efree(cmd); @@ -2860,7 +2862,12 @@ PHP_METHOD(RedisCluster, echo) { } /* Process bulk response */ - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + if (CLUSTER_IS_ATOMIC(c)) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + void *ctx = NULL; + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx); + } efree(cmd); } From 51f5796880cd54cebf0d809382265a1894d62ef0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Mar 2015 16:02:56 -0800 Subject: [PATCH 0455/1986] DBSIZE is a long response, not bool --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index 507892463e..de9dcf0733 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2609,7 +2609,7 @@ PHP_METHOD(RedisCluster, flushall) { * proto RedisCluster::dbsize(string host, long port) */ PHP_METHOD(RedisCluster, dbsize) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DBSIZE", - TYPE_LINE, cluster_bool_resp); + TYPE_INT, cluster_long_resp); } /* }}} */ From ed7e10d621152069e7f450354b33e6644b4de32f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Mar 2015 16:06:10 -0800 Subject: [PATCH 0456/1986] Added cluster specific test for SCAN --- tests/RedisClusterTest.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 4315db646f..0c5b8a4b01 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -154,7 +154,26 @@ public function testTime() { } public function testScan() { - $this->markTestSkipped(); // this will be implemented + $i_key_count = 0; + $i_scan_count = 0; + + /* Have scan retry for us */ + $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); + + /* Iterate over our masters, scanning each one */ + foreach ($this->redis->_masters() as $arr_master) { + /* Grab the number of keys we have */ + $i_key_count += $this->redis->dbsize($arr_master); + + /* Scan the keys here */ + $it = NULL; + while ($arr_keys = $this->redis->scan($it, $arr_master)) { + $i_scan_count += count($arr_keys); + } + } + + /* Our total key count should match */ + $this->assertEquals($i_scan_count, $i_key_count); } // Run some simple tests against the PUBSUB command. This is problematic, as we From 27797a5bd54ec9791356e172a40b1e07e4dea5b8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 8 Mar 2015 13:46:24 -0700 Subject: [PATCH 0457/1986] Initial cluster documentation --- README.markdown | 4 +- cluster.markdown | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 cluster.markdown diff --git a/README.markdown b/README.markdown index d5b741f2db..6c15157018 100644 --- a/README.markdown +++ b/README.markdown @@ -103,9 +103,11 @@ See [instructions from @char101](https://github.com/nicolasff/phpredis/issues/21 ## Distributed Redis Array -See [dedicated page](https://github.com/nicolasff/phpredis/blob/master/arrays.markdown#readme). +See [dedicated page](https://github.com/phpredis/phpredis/blob/master/arrays.markdown#readme). +## Redis Cluster support +See [dedicated page](https://github.com/phpredis/phpredis/blob/feature/redis_cluster/cluster.markdown#readme). # Classes and methods ----- diff --git a/cluster.markdown b/cluster.markdown new file mode 100644 index 0000000000..9746d57730 --- /dev/null +++ b/cluster.markdown @@ -0,0 +1,129 @@ +Redis Cluster +============= + +Redis introduces cluster support as of version 3.0.0, and to communicate with a cluster using phpredis one needs to use the RedisCluster class. For the majority of operations the RedisCluster class can act as a drop-in replacement for the Redis class without needing to modify how it's called. **This feature was added as the result of a generous sponsorship by [Tradsey](www.tradsey.com)** + +## Creating and connecting to a cluster + +To maintain consistency with the RedisArray class, one can create and connect to a cluster either by passing it one or more 'seed' nodes, or by defining these in redis.ini as a 'named' cluster. + +#### Declaring a cluster with an array of seeds +
+$obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003'));
+
+ +#### Loading a cluster configuration by name +In order to load a named array, one must first define the seed nodes in redis.ini. The following lines would define the cluster 'mycluster', and be loaded automatically by phpredis. + +
+# In redis.ini
+redis.clusters.seeds = "mycluster[]=localhost:7000&test[]=localhost:7001"
+redis.clusters.timeout = "mycluster=5"
+redis.clusters.read_timeout = "mycluster=10"
+
+ +Then, this cluster can be loaded by doing the following + +
+$obj_cluster = new RedisCluster('mycluster');
+
+ +## Connection process + +On construction, the RedisCluster class will iterate over the provided seed nodes until it can attain a connection to the cluster and run CLUSTER SLOTS to map every node in the cluster locally. Once the keyspace is mapped, RedisCluster will only connect to nodes when it needs to (e.g. you're getting a key that we believe is on that node.) + +## Keyspace map +As previously described, RedisCluster makes an initial mapping of every master (and any slaves) on initial construction, which it uses to determine which nodes to direct a given command. However, one of the core functionalities of Redis cluster is that this keyspace can change while the cluster is running. + +Because of this, the RedisCluster class will update it's keyspace mapping whenever it receives a MOVED error when requesting data. In the case that we receive ASK redirection, it follows the Redis specification and requests the key from the ASK node, prefixed with an ASKING command. + +## Automatic slave failover / distribution +By default, RedisCluster will only ever send commands to master nodes, but can be configured differently for readonly commands if requested. + +
+// The default option, only send commands to master nodes
+$obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_NONE);
+
+// In the event we can't reach a master, and it has slaves, failover for read commands
+$obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_ERROR);
+
+// Always distribute readonly commands between masters and slaves, at random
+$obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedsiCluster::FAILOVER_DISTRIBUTE);
+
+ +## Main command loop +With the exception of commands that are directed to a specific node, each command executed via RedisCluster is processed through a command loop, where we make the request, handle any MOVED or ASK redirection, and repeat if neccsary. This continues until one of the following conditions is met: + +1. We fail to communicate with *any* node that we are aware of, in which case a ```RedisClusterExecption``` is raised. +2. We have been bounced around longer than the timeout which was set on construction. +3. Redis cluster returns us a ```CLUSTERDOWN``` error, in which case a ```RedisClusterException``` is raised. +4. We receive a valid response, in which case the data is returned to the caller. + +## Transactions +The RedisCluster class fully supports MULTI ... EXEC transactions, including commands such as MGET and MSET which operate on multiple keys. There are considerations that must be taken into account here however. + +When you call ```RedisCluster->multi()```, the cluster is put into a MULTI state, but the MULTI command is not delivered to any nodes until a key is requested on that node. In addition, calls to EXEC will always return an array (even in the event that a transaction to a given node failed), as the commands can be going to any number of nodes depending on what is called. + +Consider the following example: + +
+$obj_cluster->multi(); // Cluster is put into MULTI state locally
+for ($i = 0; $i < 10; $i++) {
+    // MULTI will be delivered only once per node
+    $obj_cluster->get("key:$i");
+}
+// This always returns an array of 10 elements, of which some could be false 
+// (for example if a slot had been migrated)
+print_r($obj_cluster->exec());
+
+ +## Pipelining +The RedisCluster class does not support pipelining as there is no way to detect whether the keys still live where our map indicates that they do and would therefore be inherently unsafe. It would be possible to implement this support as an option if there is demand for such a feature. + +## Multiple key commands +Redis cluster does allow commands that operate on multiple keys, but only if all of those keys hash to the same slot. Note that it is not enough that the keys are all on the same node, but must actually hash to the exact same hash slot. + +For all of these multiple key commands (with the exception of MGET and MSET), the RedisCluster class will verify each key maps to the same hash slot and raise a "CROSSSLOT" warning, returning false if they don't. + +### MGET and MSET +RedisCluster has specialized processing for MGET and MSET which allows you to send any number of keys (hashing to whichever slots) without having to consider where they live. The way this works, is that the RedisCluster class will split the command as it iterates through keys, delivering a subset of commands per each key's slot. + +
+// This will be delivered in two commands.  First for all of the {hash1} keys, 
+// and then to grab 'otherkey'
+$obj_cluster->mget(Array("{hash1}key1","{hash1}key2","{hash1}key3","otherkey"));
+
+ +This operation can also be done in MULTI mode transparently. + +## Directed node commands +There are a variety of commands which have to be directed at a specific node. In the case of these commands, the caller can either pass a key (which will be hashed and used to direct our command), or an array with host:port. + +
+// This will be directed at the slot/node which would store "mykey"
+$obj_cluster->echo("mykey","Hello World!");
+
+// Here we're iterating all of our known masters, and delivering the command there
+foreach ($obj_cluster->_masters() as $arr_master) {
+    $obj_cluster->echo($arr_master, "Hello: " . implode(':', $arr_master));
+}
+
+ +In the case of all commands which need to be directed at a node, the calling convention is identical to the Redis call, except that they require an additional (first) argument in order to deliver the command. Following is a list of each of these commands: + +1. SAVE +2. BGSAVE +3. FLUSHDB +4. FLUSHALL +5. DBSIZE +6. BGREWRITEAOF +7. LASTSAVE +8. INFO +9. CLIENT +10. CLUSTER +11. CONFIG +12. PUBSUB +13. SLOWLOG +14. RANDOMKEY +15. PING + From ab2b972f1dcf909c3e859ef9ee7cf4659bd36da9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 8 Mar 2015 14:03:52 -0700 Subject: [PATCH 0458/1986] Updated cluster documentation --- cluster.markdown | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cluster.markdown b/cluster.markdown index 9746d57730..0a3285401a 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -33,7 +33,7 @@ $obj_cluster = new RedisCluster('mycluster'); On construction, the RedisCluster class will iterate over the provided seed nodes until it can attain a connection to the cluster and run CLUSTER SLOTS to map every node in the cluster locally. Once the keyspace is mapped, RedisCluster will only connect to nodes when it needs to (e.g. you're getting a key that we believe is on that node.) ## Keyspace map -As previously described, RedisCluster makes an initial mapping of every master (and any slaves) on initial construction, which it uses to determine which nodes to direct a given command. However, one of the core functionalities of Redis cluster is that this keyspace can change while the cluster is running. +As previously described, RedisCluster makes an initial mapping of every master (and any slaves) on construction, which it uses to determine which nodes to direct a given command. However, one of the core functionalities of Redis cluster is that this keyspace can change while the cluster is running. Because of this, the RedisCluster class will update it's keyspace mapping whenever it receives a MOVED error when requesting data. In the case that we receive ASK redirection, it follows the Redis specification and requests the key from the ASK node, prefixed with an ASKING command. @@ -48,7 +48,9 @@ $obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_NONE) $obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_ERROR); // Always distribute readonly commands between masters and slaves, at random -$obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedsiCluster::FAILOVER_DISTRIBUTE); +$obj_cluster->setOption( + RedisCluster::OPT_FAILOVER, RedsiCluster::FAILOVER_DISTRIBUTE +);
## Main command loop From 17188e1aed11503eef763c0c6ef497f395bd3e55 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 8 Mar 2015 14:06:05 -0700 Subject: [PATCH 0459/1986] Use proper address for https://www.tradesy.com/ --- cluster.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster.markdown b/cluster.markdown index 0a3285401a..b326250c0b 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -1,7 +1,7 @@ Redis Cluster ============= -Redis introduces cluster support as of version 3.0.0, and to communicate with a cluster using phpredis one needs to use the RedisCluster class. For the majority of operations the RedisCluster class can act as a drop-in replacement for the Redis class without needing to modify how it's called. **This feature was added as the result of a generous sponsorship by [Tradsey](www.tradsey.com)** +Redis introduces cluster support as of version 3.0.0, and to communicate with a cluster using phpredis one needs to use the RedisCluster class. For the majority of operations the RedisCluster class can act as a drop-in replacement for the Redis class without needing to modify how it's called. **This feature was added as the result of a generous sponsorship by [Tradsey](https://www.tradesy.com/)** ## Creating and connecting to a cluster From 308cd2c9dddac2537a31b44374dc65cb2e3d32a5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 8 Mar 2015 14:22:21 -0700 Subject: [PATCH 0460/1986] A note about timeouts --- cluster.markdown | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cluster.markdown b/cluster.markdown index b326250c0b..db1da73388 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -9,7 +9,13 @@ To maintain consistency with the RedisArray class, one can create and connect to #### Declaring a cluster with an array of seeds
+// Create a cluster setting two nodes as seeds
 $obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003'));
+
+// Connect and specify timeout and read_timeout
+$obj_cluster = new RedisCluster(
+    NULL, Array("host:7000", "host:7001", 1.5, 1.5);
+);
 
#### Loading a cluster configuration by name @@ -32,6 +38,11 @@ $obj_cluster = new RedisCluster('mycluster'); On construction, the RedisCluster class will iterate over the provided seed nodes until it can attain a connection to the cluster and run CLUSTER SLOTS to map every node in the cluster locally. Once the keyspace is mapped, RedisCluster will only connect to nodes when it needs to (e.g. you're getting a key that we believe is on that node.) +# Timeouts +Because Redis cluster is intended to provide high availability, timeouts do not work in the same way they do in normal socket communication. It's fully possible to have a timeout or even exception on a gien socket (say in the case that a master node has failed), and continue to serve the request if and when a slave can be promoted as the new master. + +The way RedisCluster handles user specified timeout values is that every time a command is sent to the cluster, we record the the time at the start of the request and then again every time we have to re-issue the command to a different node (either because Redis cluster responded with MOVED/ASK or because we failed to communicate with a given node). Once we detect having been in the command loop for longer than our specified timeout, an error is raised. + ## Keyspace map As previously described, RedisCluster makes an initial mapping of every master (and any slaves) on construction, which it uses to determine which nodes to direct a given command. However, one of the core functionalities of Redis cluster is that this keyspace can change while the cluster is running. From 432bb9500bbe0b394c1ef894c31e4f8f0f35f154 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 8 Mar 2015 15:29:57 -0700 Subject: [PATCH 0461/1986] Spelling and formatting documentation changes --- cluster.markdown | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cluster.markdown b/cluster.markdown index db1da73388..9783b1dc69 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -38,8 +38,8 @@ $obj_cluster = new RedisCluster('mycluster'); On construction, the RedisCluster class will iterate over the provided seed nodes until it can attain a connection to the cluster and run CLUSTER SLOTS to map every node in the cluster locally. Once the keyspace is mapped, RedisCluster will only connect to nodes when it needs to (e.g. you're getting a key that we believe is on that node.) -# Timeouts -Because Redis cluster is intended to provide high availability, timeouts do not work in the same way they do in normal socket communication. It's fully possible to have a timeout or even exception on a gien socket (say in the case that a master node has failed), and continue to serve the request if and when a slave can be promoted as the new master. +## Timeouts +Because Redis cluster is intended to provide high availability, timeouts do not work in the same way they do in normal socket communication. It's fully possible to have a timeout or even exception on a given socket (say in the case that a master node has failed), and continue to serve the request if and when a slave can be promoted as the new master. The way RedisCluster handles user specified timeout values is that every time a command is sent to the cluster, we record the the time at the start of the request and then again every time we have to re-issue the command to a different node (either because Redis cluster responded with MOVED/ASK or because we failed to communicate with a given node). Once we detect having been in the command loop for longer than our specified timeout, an error is raised. @@ -65,7 +65,7 @@ $obj_cluster->setOption(
## Main command loop -With the exception of commands that are directed to a specific node, each command executed via RedisCluster is processed through a command loop, where we make the request, handle any MOVED or ASK redirection, and repeat if neccsary. This continues until one of the following conditions is met: +With the exception of commands that are directed to a specific node, each command executed via RedisCluster is processed through a command loop, where we make the request, handle any MOVED or ASK redirection, and repeat if necessary. This continues until one of the following conditions is met: 1. We fail to communicate with *any* node that we are aware of, in which case a ```RedisClusterExecption``` is raised. 2. We have been bounced around longer than the timeout which was set on construction. @@ -80,11 +80,14 @@ When you call ```RedisCluster->multi()```, the cluster is put into a MULTI state Consider the following example:
-$obj_cluster->multi(); // Cluster is put into MULTI state locally
+// Cluster is put into MULTI state locally
+$obj_cluster->multi();
+
 for ($i = 0; $i < 10; $i++) {
     // MULTI will be delivered only once per node
     $obj_cluster->get("key:$i");
 }
+
 // This always returns an array of 10 elements, of which some could be false 
 // (for example if a slot had been migrated)
 print_r($obj_cluster->exec());

From 16438cb21b4e1d2f0f2c146e3e9b625598fd5e5c Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 9 Mar 2015 12:31:58 -0700
Subject: [PATCH 0462/1986] Better MUTLI EXEC explanation

---
 cluster.markdown | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/cluster.markdown b/cluster.markdown
index 9783b1dc69..5c40eaf3d8 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -83,13 +83,16 @@ Consider the following example:
 // Cluster is put into MULTI state locally
 $obj_cluster->multi();
 
-for ($i = 0; $i < 10; $i++) {
-    // MULTI will be delivered only once per node
-    $obj_cluster->get("key:$i");
-}
+// The cluster will issue MULTI on this node first (and only once)
+$obj_cluster->get("mykey");
+$obj_cluster->set("mykey", "new_value");
+
+// If 'myotherkey' maps to a different node, MULTI will be issued there
+// before requesting the key
+$obj_cluster->get("myotherkey");
 
-// This always returns an array of 10 elements, of which some could be false 
-// (for example if a slot had been migrated)
+// This will always return an array, even in the event of a failed transaction
+// on one of the nodes, in which case that element will be FALSE
 print_r($obj_cluster->exec());
 
From 42535bdba2bd04bee848e7e139aef527cf8ef9f4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 13 Mar 2015 14:57:56 -0700 Subject: [PATCH 0463/1986] Added unit test information to readme --- README.markdown | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.markdown b/README.markdown index 6c15157018..417ce46d22 100644 --- a/README.markdown +++ b/README.markdown @@ -109,6 +109,29 @@ See [dedicated page](https://github.com/phpredis/phpredis/blob/master/arrays.mar See [dedicated page](https://github.com/phpredis/phpredis/blob/feature/redis_cluster/cluster.markdown#readme). +## Running the unit tests +phpredis uses a small custom unit test suite for testing functionality of the various classes. To run tests, simply do the following: + +
+# Run tests for Redis class (note this is the default)
+php tests/TestRedis.php --class Redis
+
+# Run tests for RedisArray class
+tests/mkring.sh
+php tests/TestRedis.php --class RedisArray
+
+# Run tests for the RedisCluster class
+test/make-cluster.sh start
+php tests/TestRedis.php --class RedisCluster
+
+ +Note that it is possible to run only tests which match a substring of the test itself by passing the additional argument '--test ' when invoking. + +
+# Just run the 'echo' test
+php tests/TestRedis.php --class Redis --test echo 
+
+ # Classes and methods ----- From 9499949b1db7dbcf21e03d07110ab8cc4a323e6e Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Tue, 1 Jul 2014 11:29:30 +0200 Subject: [PATCH 0464/1986] fix random() vs php_rand() for portability Conflicts: library.c --- library.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/library.c b/library.c index e03c0e73a9..3ca3e53362 100644 --- a/library.c +++ b/library.c @@ -187,12 +187,9 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) } // Wait for a while before trying to reconnect if (redis_sock->retry_interval) { - // Random factor to avoid having several (or many) concurrent - // connections trying to reconnect at the same time - long retry_interval = (count ? redis_sock->retry_interval - : (random() % redis_sock->retry_interval)); - - usleep(retry_interval); + // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time + long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); + usleep(retry_interval); } redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ if(redis_sock->stream) { /* check for EOF again. */ From 400f11c7abfd407f3553d8d8af3a46576c9b4951 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 13 Mar 2015 15:09:01 -0700 Subject: [PATCH 0465/1986] Fix usleep prototype Pulled from: 792a71da7325a3a1e8b671ad757a2ce513e64dd6 --- library.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library.c b/library.c index 3ca3e53362..a790b4f975 100644 --- a/library.c +++ b/library.c @@ -36,6 +36,13 @@ PHPAPI int usleep(unsigned int useconds); #define SCORE_DECODE_INT 1 #define SCORE_DECODE_DOUBLE 2 +#ifdef PHP_WIN32 + # if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 4 + /* This proto is available from 5.5 on only */ + PHPAPI int usleep(unsigned int useconds); + # endif +#endif + extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; extern zend_class_entry *spl_ce_RuntimeException; From dad6cd9aecc866602613e39819a4371afeac85fc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 13 Mar 2015 15:41:19 -0700 Subject: [PATCH 0466/1986] Fix C89 compatibility Pulled from: 00be281e1b7385db156a1bb51e1716ceecf78d70 --- library.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/library.c b/library.c index a790b4f975..610081b9d0 100644 --- a/library.c +++ b/library.c @@ -850,13 +850,12 @@ int redis_cmd_append_sstr_long(smart_str *str, long append) { */ int redis_cmd_append_sstr_dbl(smart_str *str, double value) { char *dbl_str; - int dbl_len; - int retval; + int dbl_len, retval; /* Convert to double */ REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, value); - /* Append the string */ + // Append the string retval = redis_cmd_append_sstr(str, dbl_str, dbl_len); /* Free our double string */ @@ -870,11 +869,11 @@ int redis_cmd_append_sstr_dbl(smart_str *str, double value) { * Append an integer command to a Redis command */ int redis_cmd_append_int(char **cmd, int cmd_len, int append) { - char int_buf[32]; - int int_len; + char int_buf[32]; + int int_len; - /* Conver to an int, capture length */ - int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); + // Conver to an int, capture length + int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); /* Return the new length */ return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); @@ -1051,6 +1050,8 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHPAPI zval* redis_parse_client_list_response(char *response) { zval *z_result, *z_sub_result; + char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value; + int klen = 0, done = 0, is_numeric; // Allocate memory for our response MAKE_STD_ZVAL(z_result); @@ -1061,8 +1062,8 @@ PHPAPI zval* redis_parse_client_list_response(char *response) { array_init(z_sub_result); // Pointers for parsing - char *p = response, *lpos = response, *p2, *key; - char *kpos = NULL, *vpos = NULL, *value; + p = response; + lpos = response; /* While we've got more to parse */ while(!done) { From 69c6c04eec840389d4af7fddef55778ec3c29bb4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 13 Mar 2015 16:30:33 -0700 Subject: [PATCH 0467/1986] Added RedisArray and RedisCluster to config.w32 and more win32 fixes Manually picked from: 6c377eee18a5a36df71da2498eddbc0cd4f1ba76 --- config.w32 | 55 +++++++++++++++++++++++----------------------- redis_array_impl.c | 5 ++++- redis_array_impl.h | 3 ++- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/config.w32 b/config.w32 index c368d10cbb..2f20213540 100644 --- a/config.w32 +++ b/config.w32 @@ -1,28 +1,27 @@ -// vim: ft=javascript: - -ARG_ENABLE("redis", "whether to enable redis support", "yes"); -ARG_ENABLE("redis-session", "whether to enable sessions", "yes"); -ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no"); - -if (PHP_REDIS != "no") { - var sources = "redis.c library.c redis_array.c redis_array_impl.c"; - if (PHP_REDIS_SESSION != "no") { - ADD_SOURCES(configure_module_dirname, "redis_session.c", "redis"); - ADD_EXTENSION_DEP("redis", "session"); - ADD_FLAG("CFLAGS_REDIS", ' /D PHP_SESSION=1 '); - AC_DEFINE("HAVE_REDIS_SESSION", 1); - } - - if (PHP_REDIS_IGBINARY != "no") { - if (CHECK_HEADER_ADD_INCLUDE("igbinary.h", "CFLAGS_REDIS", configure_module_dirname + "\\..\\igbinary")) { - - ADD_EXTENSION_DEP("redis", "igbinary"); - AC_DEFINE("HAVE_REDIS_IGBINARY", 1); - } else { - WARNING("redis igbinary support not enabled"); - } - } - - EXTENSION("redis", sources); -} - +// vim: ft=javascript: + +ARG_ENABLE("redis", "whether to enable redis support", "yes"); +ARG_ENABLE("redis-session", "whether to enable sessions", "yes"); +ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no"); + +if (PHP_REDIS != "no") { + var sources = "redis.c library.c redis_array.c redis_array_impl.c"; + if (PHP_REDIS_SESSION != "no") { + ADD_SOURCES(configure_module_dirname, "redis_session.c", "redis"); + ADD_EXTENSION_DEP("redis", "session"); + ADD_FLAG("CFLAGS_REDIS", ' /D PHP_SESSION=1 '); + AC_DEFINE("HAVE_REDIS_SESSION", 1); + } + + if (PHP_REDIS_IGBINARY != "no") { + if (CHECK_HEADER_ADD_INCLUDE("igbinary.h", "CFLAGS_REDIS", configure_module_dirname + "\\..\\igbinary")) { + + ADD_EXTENSION_DEP("redis", "igbinary"); + AC_DEFINE("HAVE_REDIS_IGBINARY", 1); + } else { + WARNING("redis igbinary support not enabled"); + } + } + + EXTENSION("redis", sources); +} \ No newline at end of file diff --git a/redis_array_impl.c b/redis_array_impl.c index dc336674ff..ba7ae2a38c 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -463,12 +463,14 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D } efree(out); } else { + uint64_t h64; + /* hash */ hash = rcrc32(out, out_len); efree(out); /* get position on ring */ - uint64_t h64 = hash; + h64 = hash; h64 *= ra->count; h64 /= 0xffffffff; pos = (int)h64; @@ -565,6 +567,7 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { zval *z_keys, **z_entry_pp; HashPosition pos; MAKE_STD_ZVAL(z_keys); + HashPosition pos; #if PHP_VERSION_ID > 50300 array_init_size(z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); #else diff --git a/redis_array_impl.h b/redis_array_impl.h index fa45af8dc8..db8d7aff31 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -2,10 +2,11 @@ #define REDIS_ARRAY_IMPL_H #ifdef PHP_WIN32 -#include "win32/php_stdint.h" +#include #else #include #endif + #include "common.h" #include "redis_array.h" From df1811ed3b48121eca985f3614b17f2b4b6e5cc0 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Tue, 1 Jul 2014 14:00:36 +0200 Subject: [PATCH 0468/1986] reworked config.w32 Conflicts: config.w32 --- config.w32 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config.w32 b/config.w32 index 2f20213540..9c574ccf1e 100644 --- a/config.w32 +++ b/config.w32 @@ -22,6 +22,7 @@ if (PHP_REDIS != "no") { WARNING("redis igbinary support not enabled"); } } + + +} - EXTENSION("redis", sources); -} \ No newline at end of file From 4b974d2873037d6231d7e632c5974754b3baf09c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 13 Mar 2015 19:49:59 -0700 Subject: [PATCH 0469/1986] Use win32 function prototypes for lib functions --- cluster_library.h | 78 ++++++++++++++++++++++---------------------- common.h | 6 ++++ library.h | 83 ++++++++++++++++++++--------------------------- php_redis.h | 22 +++++-------- redis_cluster.h | 2 +- 5 files changed, 90 insertions(+), 101 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index d97de2a498..a5020dc677 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -343,101 +343,101 @@ unsigned short cluster_hash_key(const char *key, int len); /* Get the current time in miliseconds */ long long mstime(void); -PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, +PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC); -PHPAPI void cluster_disconnect(redisCluster *c TSRMLS_DC); +PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC); -PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC); -PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); -PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC); -PHPAPI int cluster_reset_multi(redisCluster *c); +PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC); +PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); +PHP_REDIS_API int cluster_abort_exec(redisCluster *c TSRMLS_DC); +PHP_REDIS_API int cluster_reset_multi(redisCluster *c); -PHPAPI short cluster_find_slot(redisCluster *c, const char *host, +PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, unsigned short port); -PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, +PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC); -PHPAPI int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); -PHPAPI int cluster_map_keyspace(redisCluster *c TSRMLS_DC); -PHPAPI void cluster_free_node(redisClusterNode *node); +PHP_REDIS_API int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); +PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC); +PHP_REDIS_API void cluster_free_node(redisClusterNode *node); -PHPAPI char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, +PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len TSRMLS_DC); /* * Redis Cluster response handlers. Our response handlers generally take the * following form: - * PHPAPI void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + * PHP_REDIS_API void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, * void *ctx) * * Reply handlers are responsible for setting the PHP return value (either to * something valid, or FALSE in the case of some failures). */ -PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* Generic/Variant handler for stuff like EVAL */ -PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* MULTI BULK response functions */ -PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb func, void *ctx); -PHPAPI void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb); /* Handlers for things like DEL/MGET/MSET/MSETNX */ -PHPAPI void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* Response handler for ZSCAN, SSCAN, and HSCAN */ -PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, REDIS_SCAN_TYPE type, long *it); /* INFO response handler */ -PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* CLIENT LIST response handler */ -PHPAPI void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* MULTI BULK processing callbacks */ diff --git a/common.h b/common.h index 124bf02d4c..0809b3d25e 100644 --- a/common.h +++ b/common.h @@ -28,6 +28,12 @@ #define REDIS_ZSET 4 #define REDIS_HASH 5 +#ifdef PHP_WIN32 +#define PHP_REDIS_API __declspec(dllexport) +#else +#define PHP_REDIS_API +#endif + /* reply types */ typedef enum _REDIS_REPLY_TYPE { TYPE_EOF = -1, diff --git a/library.h b/library.h index 8de44166f5..d178285ec7 100644 --- a/library.h +++ b/library.h @@ -14,54 +14,29 @@ int redis_cmd_append_sstr_long(smart_str *str, long append); int redis_cmd_append_int(char **cmd, int cmd_len, int append); int redis_cmd_append_sstr_dbl(smart_str *str, double value); -PHP_REDIS_API char* redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); +PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); +PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len TSRMLS_DC); PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx); -PHP_REDIS_API char* redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); -PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len TSRMLS_DC); - typedef void (*SuccessCallback)(RedisSock *redis_sock); -PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); -PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI zval *redis_parse_info_response(char *resp); -PHPAPI zval *redis_parse_client_list_response(char *resp); -PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); -PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); -PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); -PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); -PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); -PHPAPI char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); -PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); -PHPAPI void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); - -PHPAPI int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); - -PHPAPI int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter); - -PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); - -PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); -PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); -PHPAPI int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC); -PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int nothrow); -PHPAPI void redis_free_socket(RedisSock *redis_sock); -PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); -PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); - +PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); +PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API zval *redis_parse_info_response(char *resp); +PHP_REDIS_API zval *redis_parse_client_list_response(char *resp); +PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); +PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); +PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); +PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); +PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); +PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); + PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); @@ -70,14 +45,28 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter); + +PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); + PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC); -/*PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC);*/ +PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC); +PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); +PHP_REDIS_API int +redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC); +PHP_REDIS_API int +redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len); + +PHP_REDIS_API int +redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC); + PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC); @@ -99,7 +88,7 @@ PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC); -PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); +PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); diff --git a/php_redis.h b/php_redis.h index 87339f56c4..078d3ec5e9 100644 --- a/php_redis.h +++ b/php_redis.h @@ -222,12 +222,6 @@ PHP_METHOD(Redis, getAuth); PHP_METHOD(Redis, getMode); PHP_METHOD(Redis, rawcommand); -#ifdef PHP_WIN32 -#define PHP_REDIS_API __declspec(dllexport) -#else -#define PHP_REDIS_API -#endif - #ifdef ZTS #include "TSRM.h" #endif @@ -242,23 +236,23 @@ PHP_MINFO_FUNCTION(redis); typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); +PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); -PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, +PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha); -PHPAPI void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd); +PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd); -PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd); -PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC); +PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC); -PHPAPI int get_flag(zval *object TSRMLS_DC); +PHP_REDIS_API int get_flag(zval *object TSRMLS_DC); -PHPAPI void set_flag(zval *object, int new_flag TSRMLS_DC); +PHP_REDIS_API void set_flag(zval *object, int new_flag TSRMLS_DC); -PHPAPI int redis_sock_read_multibulk_multi_reply_loop( +PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems); diff --git a/redis_cluster.h b/redis_cluster.h index 83185ef152..a5a0758a03 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -99,7 +99,7 @@ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); /* For the creation of RedisCluster specific exceptions */ -PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); +PHP_REDIS_API zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); /* Create cluster context */ zend_object_value create_cluster_context(zend_class_entry *class_type From f44f5dab61e1b13e4e4e8c44da70976ec5237a00 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 23 Apr 2015 09:20:56 -0700 Subject: [PATCH 0470/1986] Update to newer form of adding class constants This is a backport into cluster from the great pull request from @hylent --- redis.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/redis.c b/redis.c index 1cb720c274..b00cd687b0 100644 --- a/redis.c +++ b/redis.c @@ -494,45 +494,45 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) /* Redis and RedisCluster objects share serialization/prefixing settings so * this is a generic function to add class constants to either */ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) { - add_constant_long(ce, "REDIS_NOT_FOUND", REDIS_NOT_FOUND); - add_constant_long(ce, "REDIS_STRING", REDIS_STRING); - add_constant_long(ce, "REDIS_SET", REDIS_SET); - add_constant_long(ce, "REDIS_LIST", REDIS_LIST); - add_constant_long(ce, "REDIS_ZSET", REDIS_ZSET); - add_constant_long(ce, "REDIS_HASH", REDIS_HASH); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STRING"), REDIS_STRING TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_SET"), REDIS_SET TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH TSRMLS_CC); /* Cluster doesn't support pipelining at this time */ if(!is_cluster) { - add_constant_long(ce, "PIPELINE", PIPELINE); + zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE TSRMLS_CC); } /* Add common mode constants */ - add_constant_long(ce, "ATOMIC", ATOMIC); - add_constant_long(ce, "MULTI", MULTI); + zend_declare_class_constant_long(ce, ZEND_STRL("ATOMIC"), ATOMIC TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("MULTI"), MULTI TSRMLS_CC); /* options */ - add_constant_long(ce, "OPT_SERIALIZER", REDIS_OPT_SERIALIZER); - add_constant_long(ce, "OPT_PREFIX", REDIS_OPT_PREFIX); - add_constant_long(ce, "OPT_READ_TIMEOUT", REDIS_OPT_READ_TIMEOUT); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT TSRMLS_CC); /* serializer */ - add_constant_long(ce, "SERIALIZER_NONE", REDIS_SERIALIZER_NONE); - add_constant_long(ce, "SERIALIZER_PHP", REDIS_SERIALIZER_PHP); + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP TSRMLS_CC); /* scan options*/ - add_constant_long(ce, "OPT_SCAN", REDIS_OPT_SCAN); - add_constant_long(ce, "SCAN_RETRY", REDIS_SCAN_RETRY); - add_constant_long(ce, "SCAN_NORETRY", REDIS_SCAN_NORETRY); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY TSRMLS_CC); /* Cluster option to allow for slave failover */ if (is_cluster) { - add_constant_long(ce, "OPT_SLAVE_FAILOVER", REDIS_OPT_FAILOVER); - add_constant_long(ce, "FAILOVER_NONE", REDIS_FAILOVER_NONE); - add_constant_long(ce, "FAILOVER_ERROR", REDIS_FAILOVER_ERROR); - add_constant_long(ce, "FAILOVER_DISTRIBUTE", REDIS_FAILOVER_DISTRIBUTE); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SLAVE_FAILOVER"), REDIS_OPT_FAILOVER TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_NONE"), REDIS_FAILOVER_NONE TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_ERROR"), REDIS_FAILOVER_ERROR TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE TSRMLS_CC); } #ifdef HAVE_REDIS_IGBINARY - add_constant_long(ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY); + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); #endif zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5 TSRMLS_CC); From 16a9c52bd306ec12a4f34a6d257f3a6af4cd84dc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 23 Apr 2015 09:23:20 -0700 Subject: [PATCH 0471/1986] Adding back the session handler --- redis.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redis.c b/redis.c index b00cd687b0..21ef2dcd34 100644 --- a/redis.c +++ b/redis.c @@ -537,6 +537,10 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5 TSRMLS_CC); zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); + +#ifdef PHP_SESSION + php_session_register_module(&ps_mod_redis); +#endif } /** From 530bf013da2024ca25c1216deef2c7e9157a355e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 24 Apr 2015 20:19:32 -0700 Subject: [PATCH 0472/1986] Convert tabs to spaces (tabs are the devil) :) --- redis_session.c | 464 ++++++++++++++++++++++++------------------------ 1 file changed, 232 insertions(+), 232 deletions(-) diff --git a/redis_session.c b/redis_session.c index 8faf29a778..0416cdc0a0 100644 --- a/redis_session.c +++ b/redis_session.c @@ -42,14 +42,14 @@ #include "ext/standard/url.h" ps_module ps_mod_redis = { - PS_MOD(redis) + PS_MOD(redis) }; typedef struct redis_pool_member_ { - RedisSock *redis_sock; - int weight; - int database; + RedisSock *redis_sock; + int weight; + int database; char *prefix; size_t prefix_len; @@ -57,32 +57,32 @@ typedef struct redis_pool_member_ { char *auth; size_t auth_len; - struct redis_pool_member_ *next; + struct redis_pool_member_ *next; } redis_pool_member; typedef struct { - int totalWeight; - int count; + int totalWeight; + int count; - redis_pool_member *head; + redis_pool_member *head; } redis_pool; PHP_REDIS_API redis_pool* redis_pool_new(TSRMLS_D) { - return ecalloc(1, sizeof(redis_pool)); + return ecalloc(1, sizeof(redis_pool)); } PHP_REDIS_API void redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, int database, char *prefix, char *auth TSRMLS_DC) { - redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); - rpm->redis_sock = redis_sock; - rpm->weight = weight; - rpm->database = database; + redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); + rpm->redis_sock = redis_sock; + rpm->weight = weight; + rpm->database = database; rpm->prefix = prefix; rpm->prefix_len = (prefix?strlen(prefix):0); @@ -90,27 +90,27 @@ redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, rpm->auth = auth; rpm->auth_len = (auth?strlen(auth):0); - rpm->next = pool->head; - pool->head = rpm; + rpm->next = pool->head; + pool->head = rpm; - pool->totalWeight += weight; + pool->totalWeight += weight; } PHP_REDIS_API void redis_pool_free(redis_pool *pool TSRMLS_DC) { - redis_pool_member *rpm, *next; + redis_pool_member *rpm, *next; rpm = pool->head; - while(rpm) { - next = rpm->next; - redis_sock_disconnect(rpm->redis_sock TSRMLS_CC); - efree(rpm->redis_sock); - if(rpm->prefix) efree(rpm->prefix); - if(rpm->auth) efree(rpm->auth); - efree(rpm); - rpm = next; - } - efree(pool); + while(rpm) { + next = rpm->next; + redis_sock_disconnect(rpm->redis_sock TSRMLS_CC); + efree(rpm->redis_sock); + if(rpm->prefix) efree(rpm->prefix); + if(rpm->auth) efree(rpm->auth); + efree(rpm); + rpm = next; + } + efree(pool); } void @@ -151,15 +151,16 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { PHP_REDIS_API redis_pool_member * redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { - redis_pool_member *rpm = pool->head; - unsigned int pos, i; - memcpy(&pos, key, sizeof(pos)); - pos %= pool->totalWeight; + unsigned int pos, i; + memcpy(&pos, key, sizeof(pos)); + pos %= pool->totalWeight; + + redis_pool_member *rpm = pool->head; - for(i = 0; i < pool->totalWeight;) { - if(pos >= i && pos < i + rpm->weight) { - int needs_auth = 0; + for(i = 0; i < pool->totalWeight;) { + if(pos >= i && pos < i + rpm->weight) { + int needs_auth = 0; if(rpm->auth && rpm->auth_len && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } @@ -171,129 +172,128 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { redis_pool_member_select(rpm TSRMLS_CC); } - return rpm; - } - i += rpm->weight; + return rpm; + } + i += rpm->weight; rpm = rpm->next; - } + } - return NULL; + return NULL; } /* {{{ PS_OPEN_FUNC */ PS_OPEN_FUNC(redis) { + php_url *url; + zval *params, **param; + int i, j, path_len; + + redis_pool *pool = redis_pool_new(TSRMLS_C); + + for (i=0,j=0,path_len=strlen(save_path); iquery != NULL) { + MAKE_STD_ZVAL(params); + array_init(params); + + sapi_module.treat_data(PARSE_STRING, estrdup(url->query), params TSRMLS_CC); + + if (zend_hash_find(Z_ARRVAL_P(params), "weight", sizeof("weight"), (void **) ¶m) != FAILURE) { + convert_to_long_ex(param); + weight = Z_LVAL_PP(param); + } + if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { + timeout = atof(Z_STRVAL_PP(param)); + } + if (zend_hash_find(Z_ARRVAL_P(params), "persistent", sizeof("persistent"), (void **) ¶m) != FAILURE) { + persistent = (atol(Z_STRVAL_PP(param)) == 1 ? 1 : 0); + } + if (zend_hash_find(Z_ARRVAL_P(params), "persistent_id", sizeof("persistent_id"), (void **) ¶m) != FAILURE) { + persistent_id = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); + } + if (zend_hash_find(Z_ARRVAL_P(params), "prefix", sizeof("prefix"), (void **) ¶m) != FAILURE) { + prefix = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); + } + if (zend_hash_find(Z_ARRVAL_P(params), "auth", sizeof("auth"), (void **) ¶m) != FAILURE) { + auth = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); + } + if (zend_hash_find(Z_ARRVAL_P(params), "database", sizeof("database"), (void **) ¶m) != FAILURE) { + convert_to_long_ex(param); + database = Z_LVAL_PP(param); + } + if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { + convert_to_long_ex(param); + retry_interval = Z_LVAL_PP(param); + } + + zval_ptr_dtor(¶ms); + } - /* translate unix: into file: */ - if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) { - int len = j-i; - char *path = estrndup(save_path+i, len); - memcpy(path, "file:", sizeof("file:")-1); - url = php_url_parse_ex(path, len); - efree(path); - } else { - url = php_url_parse_ex(save_path+i, j-i); - } - - if (!url) { - char *path = estrndup(save_path+i, j-i); - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Failed to parse session.save_path (error at offset %d, url was '%s')", i, path); - efree(path); - - redis_pool_free(pool TSRMLS_CC); - PS_SET_MOD_DATA(NULL); - return FAILURE; - } - - /* parse parameters */ - if (url->query != NULL) { - MAKE_STD_ZVAL(params); - array_init(params); - - sapi_module.treat_data(PARSE_STRING, estrdup(url->query), params TSRMLS_CC); - - if (zend_hash_find(Z_ARRVAL_P(params), "weight", sizeof("weight"), (void **) ¶m) != FAILURE) { - convert_to_long_ex(param); - weight = Z_LVAL_PP(param); - } - if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { - timeout = atof(Z_STRVAL_PP(param)); - } - if (zend_hash_find(Z_ARRVAL_P(params), "persistent", sizeof("persistent"), (void **) ¶m) != FAILURE) { - persistent = (atol(Z_STRVAL_PP(param)) == 1 ? 1 : 0); - } - if (zend_hash_find(Z_ARRVAL_P(params), "persistent_id", sizeof("persistent_id"), (void **) ¶m) != FAILURE) { - persistent_id = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); - } - if (zend_hash_find(Z_ARRVAL_P(params), "prefix", sizeof("prefix"), (void **) ¶m) != FAILURE) { - prefix = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); - } - if (zend_hash_find(Z_ARRVAL_P(params), "auth", sizeof("auth"), (void **) ¶m) != FAILURE) { - auth = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); - } - if (zend_hash_find(Z_ARRVAL_P(params), "database", sizeof("database"), (void **) ¶m) != FAILURE) { - convert_to_long_ex(param); - database = Z_LVAL_PP(param); - } - if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { - convert_to_long_ex(param); - retry_interval = Z_LVAL_PP(param); - } - - zval_ptr_dtor(¶ms); - } - - if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { - php_url_free(url); - redis_pool_free(pool TSRMLS_CC); - PS_SET_MOD_DATA(NULL); - return FAILURE; - } + if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { + php_url_free(url); + redis_pool_free(pool TSRMLS_CC); + PS_SET_MOD_DATA(NULL); + return FAILURE; + } + RedisSock *redis_sock; if(url->host) { - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval, 0); } else { /* unix */ - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval, 0); } - redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); + redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); - php_url_free(url); - } - } + php_url_free(url); + } + } - if (pool->head) { - PS_SET_MOD_DATA(pool); - return SUCCESS; - } + if (pool->head) { + PS_SET_MOD_DATA(pool); + return SUCCESS; + } - return FAILURE; + return FAILURE; } /* }}} */ @@ -301,20 +301,20 @@ PS_OPEN_FUNC(redis) */ PS_CLOSE_FUNC(redis) { - redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool *pool = PS_GET_MOD_DATA(); - if(pool){ - redis_pool_free(pool TSRMLS_CC); - PS_SET_MOD_DATA(NULL); - } - return SUCCESS; + if(pool){ + redis_pool_free(pool TSRMLS_CC); + PS_SET_MOD_DATA(NULL); + } + return SUCCESS; } /* }}} */ static char * redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *session_len) { - char *session; + char *session; char default_prefix[] = "PHPREDIS_SESSION:"; char *prefix = default_prefix; size_t prefix_len = sizeof(default_prefix)-1; @@ -324,13 +324,13 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses prefix_len = rpm->prefix_len; } - /* build session key */ - *session_len = key_len + prefix_len; - session = emalloc(*session_len); + /* build session key */ + *session_len = key_len + prefix_len; + session = emalloc(*session_len); memcpy(session, prefix, prefix_len); - memcpy(session + prefix_len, key, key_len); + memcpy(session + prefix_len, key, key_len); - return session; + return session; } @@ -338,33 +338,33 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses */ PS_READ_FUNC(redis) { - char *session, *cmd; - int session_len, cmd_len; + char *session, *cmd; + int session_len, cmd_len; - redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); - RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if(!rpm || !redis_sock){ - return FAILURE; - } - - /* send GET command */ - session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, session_len); - + RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; + if(!rpm || !redis_sock){ + return FAILURE; + } + + /* send GET command */ + session = redis_session_key(rpm, key, strlen(key), &session_len); + cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, session_len); + efree(session); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return FAILURE; - } - efree(cmd); - - /* read response */ - if ((*val = redis_sock_read(redis_sock, vallen TSRMLS_CC)) == NULL) { - return FAILURE; - } - - return SUCCESS; + if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return FAILURE; + } + efree(cmd); + + /* read response */ + if ((*val = redis_sock_read(redis_sock, vallen TSRMLS_CC)) == NULL) { + return FAILURE; + } + + return SUCCESS; } /* }}} */ @@ -372,41 +372,41 @@ PS_READ_FUNC(redis) */ PS_WRITE_FUNC(redis) { - char *cmd, *response, *session; - int cmd_len, response_len, session_len; + char *cmd, *response, *session; + int cmd_len, response_len, session_len; - redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); - RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if(!rpm || !redis_sock){ - return FAILURE; - } - - /* send SET command */ - session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, + RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; + if(!rpm || !redis_sock){ + return FAILURE; + } + + /* send SET command */ + session = redis_session_key(rpm, key, strlen(key), &session_len); + cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); - efree(session); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return FAILURE; - } - efree(cmd); - - /* read response */ - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - return FAILURE; - } - - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { - efree(response); - return SUCCESS; - } else { - efree(response); - return FAILURE; - } + efree(session); + if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return FAILURE; + } + efree(cmd); + + /* read response */ + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return FAILURE; + } + + if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + efree(response); + return SUCCESS; + } else { + efree(response); + return FAILURE; + } } /* }}} */ @@ -414,38 +414,38 @@ PS_WRITE_FUNC(redis) */ PS_DESTROY_FUNC(redis) { - char *cmd, *response, *session; - int cmd_len, response_len, session_len; + char *cmd, *response, *session; + int cmd_len, response_len, session_len; - redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); - RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if(!rpm || !redis_sock){ - return FAILURE; - } + RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; + if(!rpm || !redis_sock){ + return FAILURE; + } /* send DEL command */ - session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", session, session_len); - efree(session); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - return FAILURE; - } - efree(cmd); - - /* read response */ - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - return FAILURE; - } - - if(response_len == 2 && response[0] == ':' && (response[1] == '0' || response[1] == '1')) { - efree(response); - return SUCCESS; - } else { - efree(response); - return FAILURE; - } + session = redis_session_key(rpm, key, strlen(key), &session_len); + cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", session, session_len); + efree(session); + if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return FAILURE; + } + efree(cmd); + + /* read response */ + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return FAILURE; + } + + if(response_len == 2 && response[0] == ':' && (response[1] == '0' || response[1] == '1')) { + efree(response); + return SUCCESS; + } else { + efree(response); + return FAILURE; + } } /* }}} */ @@ -453,7 +453,7 @@ PS_DESTROY_FUNC(redis) */ PS_GC_FUNC(redis) { - return SUCCESS; + return SUCCESS; } /* }}} */ From 70cc12483d7d24572c4198a5c4927344e5eae465 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 4 May 2015 14:22:31 -0700 Subject: [PATCH 0473/1986] Use correct return type for PFADD --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 21ef2dcd34..cd8ce4b057 100644 --- a/redis.c +++ b/redis.c @@ -3861,7 +3861,7 @@ PHP_METHOD(Redis, zscan) { /* {{{ proto Redis::pfAdd(string key, array elements) }}} */ PHP_METHOD(Redis, pfadd) { - REDIS_PROCESS_CMD(pfadd, redis_1_response); + REDIS_PROCESS_CMD(pfadd, redis_long_response); } /* {{{ proto Redis::pfCount(string key) }}}*/ From 90a7cf48179e649e0f9f1010d28b52a33cf9cecf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 6 May 2015 12:12:49 -0700 Subject: [PATCH 0474/1986] Updates to fix rebase issues --- library.c | 35 ++--------------------------------- library.h | 9 --------- redis.c | 19 ++++++++++++++++--- redis_array_impl.c | 1 - 4 files changed, 18 insertions(+), 46 deletions(-) diff --git a/library.c b/library.c index 610081b9d0..81a8d920a7 100644 --- a/library.c +++ b/library.c @@ -130,28 +130,6 @@ static void redis_error_throw(char *err, size_t err_len TSRMLS_DC) { } } -PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { - if (!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } else { - php_stream_pclose(redis_sock->stream); - } - - efree(cmd); - - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - return -1; - } - - if (strncmp(response, "+OK", 3)) { - efree(response); - return -1; - } - - efree(response); - return 0; -} - PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { if (!redis_sock->persistent) { php_stream_close(redis_sock->stream); @@ -160,7 +138,7 @@ PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { } } -PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC) +PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) { int eof; int count = 0; @@ -1406,14 +1384,6 @@ PHPAPI int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } -PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx) -{ - return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); -} - - PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -1875,7 +1845,7 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, PHPAPI int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems; + int numElems, err_len; zval *z_multi_result; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { @@ -1953,7 +1923,6 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, count--; } } -*/ /* Specialized multibulk processing for HMGET where we need to pair requested * keys with their returned values */ diff --git a/library.h b/library.h index d178285ec7..46238f13a6 100644 --- a/library.h +++ b/library.h @@ -63,22 +63,13 @@ PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC); PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len); - PHP_REDIS_API int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC); -PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); -PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC); -/* PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC); */ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); -PHP_REDIS_API int redis_serialize(int serializer, zval *z, char **val, int *val_len TSRMLS_DC); -PHP_REDIS_API int redis_unserialize(int serializer, const char *val, int val_len, zval **return_value TSRMLS_DC); -PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC); - /* * Variant Read methods, mostly to implement eval diff --git a/redis.c b/redis.c index cd8ce4b057..6a536e0b38 100644 --- a/redis.c +++ b/redis.c @@ -266,7 +266,6 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rawcommand, NULL, ZEND_ACC_PUBLIC) /* SCAN and friends */ PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) @@ -290,7 +289,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC) /* Send a raw command and read raw results */ - PHP_ME(Redis, rawCommand, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rawcommand, NULL, ZEND_ACC_PUBLIC) /* introspection */ PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC) @@ -3336,12 +3335,20 @@ PHP_METHOD(Redis, script) { PHP_METHOD(Redis, dump) { REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_ping_response); } +/* }}} */ /* {{{ proto Redis::restore(ttl, key, value) */ PHP_METHOD(Redis, restore) { REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_val_cmd, redis_boolean_response); } +/* }}} */ + +/* {{{ proto Redis::debug(string key) */ +PHP_METHOD(Redis, debug) { + REDIS_PROCESS_KW_CMD("DEBUG", redis_key_cmd, redis_string_response); +} +/* }}} */ /* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, * bool replace]) */ @@ -3483,7 +3490,13 @@ PHP_METHOD(Redis, clearLastError) { RETURN_FALSE; } - RETVAL_LONG(redis_sock->mode); + /* Clear error message if we have one */ + if (redis_sock->err) { + efree(redis_sock->err); + redis_sock->err = NULL; + } + + RETURN_TRUE; } /* diff --git a/redis_array_impl.c b/redis_array_impl.c index ba7ae2a38c..280f0d79b0 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -567,7 +567,6 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { zval *z_keys, **z_entry_pp; HashPosition pos; MAKE_STD_ZVAL(z_keys); - HashPosition pos; #if PHP_VERSION_ID > 50300 array_init_size(z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); #else From 91b5a37b8873632fed4bd0949a23cf77319125c5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 6 May 2015 20:29:54 -0700 Subject: [PATCH 0475/1986] Implement rawCommand() properly, as it's not the COMMAND command in Redis. --- php_redis.h | 2 +- redis.c | 50 ++++++++++++++++++--- redis_cluster.c | 111 +++++++++++++++++++++++++++++++++++------------ redis_cluster.h | 1 + redis_commands.c | 49 ++++++++++++++++++++- redis_commands.h | 8 +++- 6 files changed, 184 insertions(+), 37 deletions(-) diff --git a/php_redis.h b/php_redis.h index 078d3ec5e9..aa621f189e 100644 --- a/php_redis.h +++ b/php_redis.h @@ -197,6 +197,7 @@ PHP_METHOD(Redis, wait); PHP_METHOD(Redis, pubsub); PHP_METHOD(Redis, client); +PHP_METHOD(Redis, command); PHP_METHOD(Redis, rawcommand); /* SCAN and friends */ @@ -220,7 +221,6 @@ PHP_METHOD(Redis, isConnected); PHP_METHOD(Redis, getPersistentID); PHP_METHOD(Redis, getAuth); PHP_METHOD(Redis, getMode); -PHP_METHOD(Redis, rawcommand); #ifdef ZTS #include "TSRM.h" diff --git a/redis.c b/redis.c index 6a536e0b38..660798e0c6 100644 --- a/redis.c +++ b/redis.c @@ -266,7 +266,8 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) + /* SCAN and friends */ PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) @@ -3684,11 +3685,50 @@ PHP_METHOD(Redis, client) { } } -/* proto array Redis::rawcommand() - * proto array Redis::rawcommand('info', string cmd) - * proto array Redis::rawcommand('getkeys', array cmd_args) */ +/* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */ PHP_METHOD(Redis, rawcommand) { - REDIS_PROCESS_CMD(rawcommand, redis_read_variant_reply); + int argc = ZEND_NUM_ARGS(), cmd_len; + char *cmd = NULL; + RedisSock *redis_sock; + zval **z_args; + + /* Sanity check on arguments */ + z_args = emalloc(argc * sizeof(zval*)); + if (argc < 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Must pass at least one command keyword"); + efree(z_args); + RETURN_FALSE; + } else if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Internal PHP error parsing arguments"); + efree(z_args); + RETURN_FALSE; + } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len TSRMLS_CC) < 0 || + redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) + { + if (cmd) efree(cmd); + efree(z_args); + RETURN_FALSE; + } + + /* Clean up command array */ + efree(z_args); + + /* Execute our command */ + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL); + } + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); +} +/* }}} */ + +/* {{{ proto array Redis::command() + * proto array Redis::command('info', string cmd) + * proto array Redis::command('getkeys', array cmd_args) */ +PHP_METHOD(Redis, command) { + REDIS_PROCESS_CMD(command, redis_read_variant_reply); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index de9dcf0733..3019b40edb 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -209,6 +209,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rawcommand, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, cluster, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, client, NULL, ZEND_ACC_PUBLIC) @@ -2199,27 +2200,8 @@ PHP_METHOD(RedisCluster, exec) { CLUSTER_RESET_MULTI(c); } -/* {{{ proto bool RedisCluster::discard() */ -PHP_METHOD(RedisCluster, discard) { - redisCluster *c = GET_CONTEXT(); - - if(CLUSTER_IS_ATOMIC(c)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); - RETURN_FALSE; - } - - if(cluster_abort_exec(c TSRMLS_CC)<0) { - CLUSTER_RESET_MULTI(c); - } - - CLUSTER_FREE_QUEUE(c); - - RETURN_TRUE; -} - /* Get a slot either by key (string) or host/port array */ -static short -cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) +static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { int key_len, key_free; zval **z_host, **z_port, *z_tmp = NULL; @@ -2229,7 +2211,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) /* If it's a string, treat it as a key. Otherwise, look for a two * element array */ if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG || - Z_TYPE_P(z_arg)==IS_DOUBLE) + Z_TYPE_P(z_arg)==IS_DOUBLE) { /* Allow for any scalar here */ if (Z_TYPE_P(z_arg) != IS_STRING) { @@ -2253,7 +2235,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) zval_dtor(z_tmp); efree(z_tmp); } - } else if (Z_TYPE_P(z_arg) == IS_ARRAY && + } else if (Z_TYPE_P(z_arg) == IS_ARRAY && zend_hash_index_find(Z_ARRVAL_P(z_arg),0,(void**)&z_host)!=FAILURE && zend_hash_index_find(Z_ARRVAL_P(z_arg),1,(void**)&z_port)!=FAILURE && Z_TYPE_PP(z_host)==IS_STRING && Z_TYPE_PP(z_port)==IS_LONG) @@ -2263,7 +2245,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) (unsigned short)Z_LVAL_PP(z_port)); /* Inform the caller if they've passed bad data */ - if(slot < 0) { + if(slot < 0) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port)); } @@ -2276,6 +2258,24 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) return slot; } +/* {{{ proto bool RedisCluster::discard() */ +PHP_METHOD(RedisCluster, discard) { + redisCluster *c = GET_CONTEXT(); + + if(CLUSTER_IS_ATOMIC(c)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); + RETURN_FALSE; + } + + if(cluster_abort_exec(c TSRMLS_CC)<0) { + CLUSTER_RESET_MULTI(c); + } + + CLUSTER_FREE_QUEUE(c); + + RETURN_TRUE; +} + /* Generic handler for things we want directed at a given node, like SAVE, * BGSAVE, FLUSHDB, FLUSHALL, etc */ static void @@ -2856,7 +2856,7 @@ PHP_METHOD(RedisCluster, echo) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; if(cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, - "Unable to send commnad at the specificed node", 0 TSRMLS_CC); + "Unable to send command at the specificed node", 0 TSRMLS_CC); efree(cmd); RETURN_FALSE; } @@ -2873,11 +2873,66 @@ PHP_METHOD(RedisCluster, echo) { } /* }}} */ -/* {{{ proto array RedisCluster::rawcommand() - * proto array RedisCluster::rawcommand('INFO', string cmd) - * proto array RedisCluster::rawcommand('GETKEYS', array cmd_args) */ +/* {{{ proto mixed RedisCluster::rawcommand(string $key, string $cmd, [ $argv1 .. $argvN]) + * proto mixed RedisCluster::rawcommand(array $host_port, string $cmd, [ $argv1 .. $argvN]) */ PHP_METHOD(RedisCluster, rawcommand) { - CLUSTER_PROCESS_CMD(rawcommand, cluster_variant_resp, 0); + REDIS_REPLY_TYPE rtype; + int argc = ZEND_NUM_ARGS(), cmd_len; + redisCluster *c = GET_CONTEXT(); + char *cmd = NULL; + zval **z_args; + short slot; + + /* Sanity check on our arguments */ + z_args = emalloc(argc * sizeof(zval*)); + if (argc < 2) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "You must pass at least node information as well as at least a command."); + efree(z_args); + RETURN_FALSE; + } else if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Internal PHP error parsing method parameters."); + efree(z_args); + RETURN_FALSE; + } else if (redis_build_raw_cmd(z_args+1, argc-1, &cmd, &cmd_len TSRMLS_CC) || + (slot = cluster_cmd_get_slot(c, z_args[0] TSRMLS_CC))<0) + { + if (cmd) efree(cmd); + efree(z_args); + RETURN_FALSE; + } + + /* Free argument array */ + efree(z_args); + + /* Direct the command */ + rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE; + if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to send command to the specified node", 0 TSRMLS_CC); + efree(cmd); + efree(z_args); + RETURN_FALSE; + } + + /* Process variant response */ + if (CLUSTER_IS_ATOMIC(c)) { + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + void *ctx = NULL; + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); + } + + efree(cmd); +} +/* }}} */ + +/* {{{ proto array RedisCluster::command() + * proto array RedisCluster::command('INFO', string cmd) + * proto array RedisCluster::command('GETKEYS', array cmd_args) */ +PHP_METHOD(RedisCluster, command) { + CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); } /* }}} */ diff --git a/redis_cluster.h b/redis_cluster.h index a5a0758a03..3659d00e9d 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -237,6 +237,7 @@ PHP_METHOD(RedisCluster, config); PHP_METHOD(RedisCluster, pubsub); PHP_METHOD(RedisCluster, script); PHP_METHOD(RedisCluster, slowlog); +PHP_METHOD(RedisCluster, command); /* SCAN and friends */ PHP_METHOD(RedisCluster, scan); diff --git a/redis_commands.c b/redis_commands.c index 03da011520..8d951dda74 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -34,6 +34,51 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Helper to construct a raw command. Given that the cluster and non cluster + * versions are different (RedisCluster needs an additional argument to direct + * the command) we take the start of our array and count */ +int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC) +{ + smart_str cmdstr = {0}; + int i; + + /* Make sure our first argument is a string */ + if (Z_TYPE_P(z_args[0]) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "When sending a 'raw' command, the first argument must be a string!"); + return FAILURE; + } + + /* Initialize our command string */ + redis_cmd_init_sstr(&cmdstr, argc-1, Z_STRVAL_P(z_args[0]), Z_STRLEN_P(z_args[0])); + + for (i = 1; i < argc; i++) { + switch (Z_TYPE_P(z_args[i])) { + case IS_STRING: + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_args[i]), + Z_STRLEN_P(z_args[i])); + break; + case IS_LONG: + redis_cmd_append_sstr_long(&cmdstr,Z_LVAL_P(z_args[i])); + break; + case IS_DOUBLE: + redis_cmd_append_sstr_dbl(&cmdstr,Z_DVAL_P(z_args[i])); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Raw command arguments must be scalar values!"); + efree(cmdstr.c); + return FAILURE; + } + } + + /* Push command and length to caller */ + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* Generic command where we just take a string and do nothing to it*/ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -2699,8 +2744,8 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* COMMAND */ -int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) { char *kw=NULL; zval *z_arg; diff --git a/redis_commands.h b/redis_commands.h index 1e456b5979..053fd65a9f 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -21,6 +21,9 @@ typedef struct subscribeContext { zend_fcall_info_cache cb_cache; } subscribeContext; +/* Construct a raw command */ +int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC); + /* Redis command generics. Many commands share common prototypes meaning that * we can write one function to handle all of them. For example, there are * many COMMAND key value commands, or COMMAND key commands. */ @@ -212,9 +215,12 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); From 9f865c15b4593d44861877b9b8896d63d8afa967 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 7 May 2015 12:44:20 -0700 Subject: [PATCH 0476/1986] Make sure errstr is set before freeing it --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 81a8d920a7..babe86ec67 100644 --- a/library.c +++ b/library.c @@ -1647,7 +1647,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) efree(host); if (!redis_sock->stream) { - efree(errstr); + if (errstr) efree(errstr); return -1; } From ab8fa7dad768f6d955f19298e334a65209a25ff5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 7 May 2015 15:52:55 -0700 Subject: [PATCH 0477/1986] Use a static variable for our nodemap file and only open it once to avoid blowing file descriptors. --- tests/RedisClusterTest.php | 12 +++++++----- tests/TestSuite.php | 2 -- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 0c5b8a4b01..7877144c00 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -7,7 +7,7 @@ * where we're validating specific cluster mechanisms */ class Redis_Cluster_Test extends Redis_Test { - private $_arr_node_map = Array(); + private static $_arr_node_map = Array(); private $_arr_redis_types = Array( Redis::REDIS_STRING, @@ -43,9 +43,11 @@ public function __construct() { } /* Store our node map */ - $this->_arr_node_map = array_filter( - explode("\n", file_get_contents($str_nodemap_file) - )); + if (!self::$_arr_node_map) { + self::$_arr_node_map = array_filter( + explode("\n", file_get_contents($str_nodemap_file) + )); + } } /* Override setUp to get info from a specific node */ @@ -57,7 +59,7 @@ public function setUp() { /* Override newInstance as we want a RedisCluster object */ protected function newInstance() { - return new RedisCluster(NULL, $this->_arr_node_map); + return new RedisCluster(NULL, self::$_arr_node_map); } /* Overrides for RedisTest where the function signature is different. This diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 485ed6655f..ecdefbd0d2 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -147,10 +147,8 @@ public static function run($className, $str_limit = NULL) { if ($e instanceof RedisException) { $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n"; $str_msg = self::make_fail('FAILED'); - //echo 'F'; } else { $str_msg = self::make_warning('SKIPPED'); - //echo 'S'; } } From fb0597992aef850845de53e1073028bcdd38dd12 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 7 May 2015 16:30:11 -0700 Subject: [PATCH 0478/1986] Merge remote --- config.w32 | 2 +- library.c | 82 +++++++++++++++++++--------------------- library.h | 1 + redis.c | 11 +++--- redis_array_impl.c | 7 +--- redis_cluster.c | 48 +++++++++++------------ redis_commands.h | 3 -- tests/RedisArrayTest.php | 13 ------- 8 files changed, 70 insertions(+), 97 deletions(-) diff --git a/config.w32 b/config.w32 index 9c574ccf1e..254122c8ee 100644 --- a/config.w32 +++ b/config.w32 @@ -5,7 +5,7 @@ ARG_ENABLE("redis-session", "whether to enable sessions", "yes"); ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no"); if (PHP_REDIS != "no") { - var sources = "redis.c library.c redis_array.c redis_array_impl.c"; + var sources = "redis.c library.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c"; if (PHP_REDIS_SESSION != "no") { ADD_SOURCES(configure_module_dirname, "redis_session.c", "redis"); ADD_EXTENSION_DEP("redis", "session"); diff --git a/library.c b/library.c index babe86ec67..cfc9f71f67 100644 --- a/library.c +++ b/library.c @@ -20,13 +20,6 @@ #include #include -#ifdef PHP_WIN32 -# if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 4 -/* This proto is available from 5.5 on only */ -PHPAPI int usleep(unsigned int useconds); -# endif -#endif - #define UNSERIALIZE_NONE 0 #define UNSERIALIZE_KEYS 1 #define UNSERIALIZE_VALS 2 @@ -130,12 +123,12 @@ static void redis_error_throw(char *err, size_t err_len TSRMLS_DC) { } } -PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { - if (!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } else { - php_stream_pclose(redis_sock->stream); - } +PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { + if (!redis_sock->persistent) { + php_stream_close(redis_sock->stream); + } else { + php_stream_pclose(redis_sock->stream); + } } PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) @@ -509,7 +502,8 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, "read error on connection", + 0 TSRMLS_CC); return NULL; } @@ -1086,8 +1080,7 @@ PHPAPI zval* redis_parse_client_list_response(char *response) { } else { add_assoc_string(z_sub_result, key, value, 0); } - - /* If we hit a '\n', then we can add this user to our list */ + // If we hit a '\n', then we can add this user to our list if(*p == '\n') { /* Add our user */ add_next_index_zval(z_result, z_sub_result); @@ -1099,7 +1092,7 @@ PHPAPI zval* redis_parse_client_list_response(char *response) { } } - /* Free our key */ + // Free our key efree(key); } else { // Something is wrong @@ -1384,9 +1377,10 @@ PHPAPI int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } -PHPAPI void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) { + char *response; int response_len; char ret; @@ -1765,23 +1759,23 @@ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, redis_sock->err = erealloc(redis_sock->err, msg_len +1); } - /* Copy in our new error message, set new length, and null terminate */ - memcpy(redis_sock->err, msg, msg_len); - redis_sock->err[msg_len] = '\0'; - redis_sock->err_len = msg_len; - } else { - /* Free our last error */ - if(redis_sock->err != NULL) { - efree(redis_sock->err); - } + // Copy in our new error message, set new length, and null terminate + memcpy(redis_sock->err, msg, msg_len); + redis_sock->err[msg_len] = '\0'; + redis_sock->err_len = msg_len; + } else { + // Free our last error + if(redis_sock->err != NULL) { + efree(redis_sock->err); + } - /* Set to null, with zero length */ - redis_sock->err = NULL; - redis_sock->err_len = 0; - } + // Set to null, with zero length + redis_sock->err = NULL; + redis_sock->err_len = 0; + } - /* Success */ - return 0; + // Success + return 0; } /** @@ -1813,12 +1807,10 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); } else { - /* Capture our error if redis has given us one */ if (inbuf[0] == '-') { err_len = strlen(inbuf+1) - 2; redis_sock_set_err(redis_sock, inbuf+1, err_len); } - RETVAL_FALSE; } return -1; @@ -2147,14 +2139,16 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - rv_free = 1; - } - if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, return_value TSRMLS_CC) == 0) { - return 1; - } - if(rv_free==1) efree(*return_value); + if(!*return_value) { + MAKE_STD_ZVAL(*return_value); + rv_free = 1; + } + if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, + return_value TSRMLS_CC) == 0) + { + return 1; + } + if (rv_free==1) efree(*return_value); #endif return 0; break; diff --git a/library.h b/library.h index 46238f13a6..0d79f78541 100644 --- a/library.h +++ b/library.h @@ -63,6 +63,7 @@ PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC); PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len); + PHP_REDIS_API int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC); diff --git a/redis.c b/redis.c index 660798e0c6..39112c2da0 100644 --- a/redis.c +++ b/redis.c @@ -3485,17 +3485,16 @@ PHP_METHOD(Redis, clearLastError) { { RETURN_FALSE; } - - /* Grab socket */ - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + // Grab socket + if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } - /* Clear error message if we have one */ - if (redis_sock->err) { + // Clear error message + if(redis_sock->err) { efree(redis_sock->err); - redis_sock->err = NULL; } + redis_sock->err = NULL; RETURN_TRUE; } diff --git a/redis_array_impl.c b/redis_array_impl.c index 280f0d79b0..c90227a384 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -565,8 +565,8 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { /* Initialize key array */ zval *z_keys, **z_entry_pp; - HashPosition pos; MAKE_STD_ZVAL(z_keys); + HashPosition pos; #if PHP_VERSION_ID > 50300 array_init_size(z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); #else @@ -960,11 +960,6 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS zval_dtor(&z_ret); - /* Free the array itself */ - efree(z_zadd_args); - - zval_dtor(&z_ret); - /* Free the array itself */ efree(z_zadd_args); diff --git a/redis_cluster.c b/redis_cluster.c index 3019b40edb..dddc39ffbe 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2200,8 +2200,27 @@ PHP_METHOD(RedisCluster, exec) { CLUSTER_RESET_MULTI(c); } +/* {{{ proto bool RedisCluster::discard() */ +PHP_METHOD(RedisCluster, discard) { + redisCluster *c = GET_CONTEXT(); + + if(CLUSTER_IS_ATOMIC(c)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); + RETURN_FALSE; + } + + if(cluster_abort_exec(c TSRMLS_CC)<0) { + CLUSTER_RESET_MULTI(c); + } + + CLUSTER_FREE_QUEUE(c); + + RETURN_TRUE; +} + /* Get a slot either by key (string) or host/port array */ -static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) +static short +cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { int key_len, key_free; zval **z_host, **z_port, *z_tmp = NULL; @@ -2211,7 +2230,7 @@ static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) /* If it's a string, treat it as a key. Otherwise, look for a two * element array */ if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG || - Z_TYPE_P(z_arg)==IS_DOUBLE) + Z_TYPE_P(z_arg)==IS_DOUBLE) { /* Allow for any scalar here */ if (Z_TYPE_P(z_arg) != IS_STRING) { @@ -2235,7 +2254,7 @@ static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) zval_dtor(z_tmp); efree(z_tmp); } - } else if (Z_TYPE_P(z_arg) == IS_ARRAY && + } else if (Z_TYPE_P(z_arg) == IS_ARRAY && zend_hash_index_find(Z_ARRVAL_P(z_arg),0,(void**)&z_host)!=FAILURE && zend_hash_index_find(Z_ARRVAL_P(z_arg),1,(void**)&z_port)!=FAILURE && Z_TYPE_PP(z_host)==IS_STRING && Z_TYPE_PP(z_port)==IS_LONG) @@ -2245,7 +2264,7 @@ static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) (unsigned short)Z_LVAL_PP(z_port)); /* Inform the caller if they've passed bad data */ - if(slot < 0) { + if(slot < 0) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port)); } @@ -2258,24 +2277,6 @@ static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) return slot; } -/* {{{ proto bool RedisCluster::discard() */ -PHP_METHOD(RedisCluster, discard) { - redisCluster *c = GET_CONTEXT(); - - if(CLUSTER_IS_ATOMIC(c)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); - RETURN_FALSE; - } - - if(cluster_abort_exec(c TSRMLS_CC)<0) { - CLUSTER_RESET_MULTI(c); - } - - CLUSTER_FREE_QUEUE(c); - - RETURN_TRUE; -} - /* Generic handler for things we want directed at a given node, like SAVE, * BGSAVE, FLUSHDB, FLUSHALL, etc */ static void @@ -2856,7 +2857,7 @@ PHP_METHOD(RedisCluster, echo) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; if(cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, - "Unable to send command at the specificed node", 0 TSRMLS_CC); + "Unable to send commnad at the specificed node", 0 TSRMLS_CC); efree(cmd); RETURN_FALSE; } @@ -2934,6 +2935,5 @@ PHP_METHOD(RedisCluster, rawcommand) { PHP_METHOD(RedisCluster, command) { CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); } -/* }}} */ /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 053fd65a9f..f75eb1953a 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -218,9 +218,6 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 86c63b05c4..831f2d2554 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -544,17 +544,4 @@ function run_tests($className, $str_filter) { define('REDIS_ARRAY_DATA_SIZE', 1000); -global $useIndex; -foreach(array(true, false) as $useIndex) { - $str_limit = isset($argv[1]) ? $argv[1] : NULL; - - echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; - - //run_tests('Redis_Array_Test', $str_limit); - run_tests('Redis_Rehashing_Test', $str_limit); - //run_tests('Redis_Auto_Rehashing_Test', $str_limit); - //run_tests('Redis_Multi_Exec_Test', $str_limit); - //run_tests('Redis_Distributor_Test', $str_limit); -} - ?> From 1cb70999ccdfe16347d44f4d2a5e654dbeb12817 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 7 May 2015 17:29:53 -0700 Subject: [PATCH 0479/1986] Update all occurances of PHPAPI to PHP_REDIS_API (Win32 compat) --- cluster_library.c | 74 +++++++++++++++++++++++------------------------ library.c | 64 ++++++++++++++++++++-------------------- redis.c | 30 +++++++++---------- 3 files changed, 84 insertions(+), 84 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 51d2c3d110..93885c8a5e 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -309,7 +309,7 @@ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { * here because we know we'll only have sent MULTI to the master nodes. We can't * failover inside a transaction, as we don't know if the transaction will only * be readonly commands, or contain write commands as well */ -PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { +PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { int retval; /* Send exec */ @@ -324,7 +324,7 @@ PHPAPI int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { return retval; } -PHPAPI int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { +PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_DISCARD_CMD, sizeof(RESP_DISCARD_CMD)-1, TYPE_LINE TSRMLS_CC)) { @@ -656,7 +656,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, } /* Attach a slave to a master */ -PHPAPI int +PHP_REDIS_API int cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) { ulong index; @@ -748,7 +748,7 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { } /* Free a redisClusterNode structure */ -PHPAPI void cluster_free_node(redisClusterNode *node) { +PHP_REDIS_API void cluster_free_node(redisClusterNode *node) { if(node->slaves) { zend_hash_destroy(node->slaves); efree(node->slaves); @@ -786,7 +786,7 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { } /* Initialize seeds */ -PHPAPI int +PHP_REDIS_API int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { RedisSock *redis_sock; char *str, *psep, key[1024]; @@ -830,7 +830,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { } /* Initial mapping of our cluster keyspace */ -PHPAPI int +PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { RedisSock **seed; clusterReply *slots=NULL; @@ -973,7 +973,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type } /* Disconnect from each node we're connected to */ -PHPAPI void cluster_disconnect(redisCluster *c TSRMLS_DC) { +PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC) { redisClusterNode **node; for(zend_hash_internal_pointer_reset(c->nodes); @@ -1206,7 +1206,7 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { /* Abort any transaction in process, by sending DISCARD to any nodes that * have active transactions in progress. If we can't send DISCARD, we need * to disconnect as it would leave us in an undefined state. */ -PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { +PHP_REDIS_API int cluster_abort_exec(redisCluster *c TSRMLS_DC) { clusterFoldItem *fi = c->multi_head; /* Loop through our fold items */ @@ -1232,7 +1232,7 @@ PHPAPI int cluster_abort_exec(redisCluster *c TSRMLS_DC) { /* Iterate through our slots, looking for the host/port in question. This * should perform well enough as in almost all situations, a few or a few * dozen servers will map all the slots */ -PHPAPI short cluster_find_slot(redisCluster *c, const char *host, +PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, unsigned short port) { int i; @@ -1251,7 +1251,7 @@ PHPAPI short cluster_find_slot(redisCluster *c, const char *host, } /* Send a command to a specific slot */ -PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, +PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC) { /* Point our cluster to this slot and it's socket */ @@ -1284,7 +1284,7 @@ PHPAPI int cluster_send_slot(redisCluster *c, short slot, char *cmd, /* Send a command to given slot in our cluster. If we get a MOVED or ASK error * we attempt to send the command to the node as directed. */ -PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, +PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { int resp, timedout=0; @@ -1372,7 +1372,7 @@ PHPAPI short cluster_send_command(redisCluster *c, short slot, const char *cmd, * consumed. */ /* RAW bulk response handler */ -PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; @@ -1393,7 +1393,7 @@ PHPAPI void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, } /* BULK response handler */ -PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; @@ -1425,7 +1425,7 @@ PHPAPI void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Bulk response where we expect a double */ -PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { char *resp; @@ -1447,7 +1447,7 @@ PHPAPI void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* A boolean response. If we get here, we've consumed the '+' reply * type and will now just verify we can read the OK */ -PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { // Check that we have +OK @@ -1461,7 +1461,7 @@ PHPAPI void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Boolean response, specialized for PING */ -PHPAPI void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if(c->reply_type != TYPE_LINE || c->reply_len != 4 || @@ -1474,7 +1474,7 @@ PHPAPI void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* 1 or 0 response, for things like SETNX */ -PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { // Validate our reply type, and check for a zero @@ -1486,7 +1486,7 @@ PHPAPI void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Generic integer response */ -PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if(c->reply_type != TYPE_INT) { @@ -1496,7 +1496,7 @@ PHPAPI void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* TYPE response handler */ -PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { // Make sure we got the right kind of response @@ -1521,7 +1521,7 @@ PHPAPI void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* SUBSCRIBE/PSCUBSCRIBE handler */ -PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; @@ -1647,7 +1647,7 @@ PHPAPI void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* UNSUBSCRIBE/PUNSUBSCRIBE */ -PHPAPI void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; @@ -1731,7 +1731,7 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) /* Variant response handling, for things like EVAL and various other responses * where we just map the replies from Redis type values to PHP ones directly. */ -PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterReply *r; @@ -1801,7 +1801,7 @@ PHPAPI void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Generic MULTI BULK response processor */ -PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb cb, void *ctx) { zval *z_result; @@ -1838,7 +1838,7 @@ PHPAPI void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* HSCAN, SSCAN, ZSCAN */ -PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, REDIS_SCAN_TYPE type, long *it) { char *pit; @@ -1895,7 +1895,7 @@ PHPAPI int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* INFO response */ -PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval *z_result; @@ -1921,7 +1921,7 @@ PHPAPI void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* CLIENT LIST response */ -PHPAPI void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { zval *z_result; @@ -1946,7 +1946,7 @@ PHPAPI void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } /* MULTI BULK response loop where we might pull the next one */ -PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb) { zval *z_result; @@ -1978,7 +1978,7 @@ PHPAPI zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* MULTI MULTI BULK reply (for EXEC) */ -PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { MAKE_STD_ZVAL(c->multi_resp); @@ -2015,7 +2015,7 @@ PHPAPI void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* Generic handler for MGET */ -PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; @@ -2049,7 +2049,7 @@ PHPAPI void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, } /* Handler for MSETNX */ -PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; @@ -2085,7 +2085,7 @@ PHPAPI void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Handler for DEL */ -PHPAPI void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; @@ -2114,7 +2114,7 @@ PHPAPI void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Handler for MSET */ -PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; @@ -2144,7 +2144,7 @@ PHPAPI void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Raw MULTI BULK reply */ -PHPAPI void +PHP_REDIS_API void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, @@ -2152,7 +2152,7 @@ cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) } /* Unserialize all the things */ -PHPAPI void +PHP_REDIS_API void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, @@ -2161,7 +2161,7 @@ cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) /* For handling responses where we get key, value, key, value that * we will turn into key => value, key => value. */ -PHPAPI void +PHP_REDIS_API void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { @@ -2170,7 +2170,7 @@ cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Handling key,value to key=>value where the values are doubles */ -PHPAPI void +PHP_REDIS_API void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { @@ -2179,7 +2179,7 @@ cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Associate multi bulk response (for HMGET really) */ -PHPAPI void +PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { diff --git a/library.c b/library.c index cfc9f71f67..b5f10de24a 100644 --- a/library.c +++ b/library.c @@ -32,7 +32,7 @@ #ifdef PHP_WIN32 # if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 4 /* This proto is available from 5.5 on only */ - PHPAPI int usleep(unsigned int useconds); + PHP_REDIS_API int usleep(unsigned int useconds); # endif #endif @@ -123,7 +123,7 @@ static void redis_error_throw(char *err, size_t err_len TSRMLS_DC) { } } -PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { +PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { if (!redis_sock->persistent) { php_stream_close(redis_sock->stream); } else { @@ -244,7 +244,7 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } -PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -367,7 +367,7 @@ PHPAPI int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, return -1; } -PHPAPI int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -405,7 +405,7 @@ PHPAPI int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, return 0; } -PHPAPI zval * +PHP_REDIS_API zval * redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char inbuf[1024]; @@ -933,7 +933,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } } -PHPAPI zval *redis_parse_info_response(char *response) { +PHP_REDIS_API zval *redis_parse_info_response(char *response) { zval *z_ret; char *key, *value, *p, *cur, *pos; int is_numeric; @@ -1020,7 +1020,7 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo } } -PHPAPI zval* redis_parse_client_list_response(char *response) { +PHP_REDIS_API zval* redis_parse_client_list_response(char *response) { zval *z_result, *z_sub_result; char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value; int klen = 0, done = 0, is_numeric; @@ -1126,7 +1126,7 @@ PHPAPI zval* redis_parse_client_list_response(char *response) { return z_result; } -PHPAPI void +PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) @@ -1167,7 +1167,7 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } -PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -1175,7 +1175,7 @@ PHPAPI void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, z_tab, ctx, NULL); } -PHPAPI void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * z_tab, void *ctx) { @@ -1347,14 +1347,14 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */ -PHPAPI int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, UNSERIALIZE_NONE, SCORE_DECODE_NONE); } /* Zipped key => value reply unserializing keys and decoding the score as an integer (PUBSUB) */ -PHPAPI int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, @@ -1362,7 +1362,7 @@ PHPAPI int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, Redis } /* Zipped key => value reply unserializing keys and decoding the score as a double (ZSET commands) */ -PHPAPI int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, @@ -1370,7 +1370,7 @@ PHPAPI int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, Redis } /* Zipped key => value reply where only the values are unserialized (e.g. HMGET) */ -PHPAPI int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, @@ -1449,7 +1449,7 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } /* like string response, but never unserialized. */ -PHPAPI void +PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -1538,7 +1538,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock /** * redis_sock_create */ -PHPAPI RedisSock* +PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect) @@ -1667,7 +1667,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) /** * redis_sock_server_open */ -PHPAPI int +PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC) { int res = -1; @@ -1718,7 +1718,7 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) return 0; } -PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *cmd; @@ -1748,7 +1748,7 @@ PHPAPI void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, /** * redis_sock_set_err */ -PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, +PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { // Allocate/Reallocate our last error member @@ -1781,7 +1781,7 @@ PHPAPI int redis_sock_set_err(RedisSock *redis_sock, const char *msg, /** * redis_sock_read_multibulk_reply */ -PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -1834,7 +1834,7 @@ PHPAPI int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, /* Like multibulk reply, but don't touch the values, they won't be unserialized * (this is used by HKEYS). */ -PHPAPI int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; int numElems, err_len; @@ -1882,7 +1882,7 @@ PHPAPI int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_ return 0; } -PHPAPI void +PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize) { @@ -1918,7 +1918,7 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Specialized multibulk processing for HMGET where we need to pair requested * keys with their returned values */ -PHPAPI int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024], *response; int response_len; @@ -1985,7 +1985,7 @@ PHPAPI int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redi /** * redis_sock_write */ -PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz +PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { @@ -2020,7 +2020,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) efree(redis_sock); } -PHPAPI int +PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC) { @@ -2100,7 +2100,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len return 0; } -PHPAPI int +PHP_REDIS_API int redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC) { @@ -2179,7 +2179,7 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len) { * Processing for variant reply types (think EVAL) */ -PHPAPI int +PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size TSRMLS_DC) { @@ -2211,7 +2211,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, return 0; } -PHPAPI int +PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC) { @@ -2251,7 +2251,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, /* * Read a single line response, having already consumed the reply-type byte */ -PHPAPI int +PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC) { @@ -2283,7 +2283,7 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, return 0; } -PHPAPI int +PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC) { @@ -2300,7 +2300,7 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret } } -PHPAPI int +PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { @@ -2361,7 +2361,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret return 0; } -PHPAPI int +PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { diff --git a/redis.c b/redis.c index 39112c2da0..f880598c25 100644 --- a/redis.c +++ b/redis.c @@ -429,7 +429,7 @@ static void redis_destructor_redis_sock(zend_rsrc_list_entry * rsrc TSRMLS_DC) /** * redis_sock_get */ -PHPAPI int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, +PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) { @@ -707,7 +707,7 @@ PHP_METHOD(Redis, pconnect) } /* }}} */ -PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { +PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zval *object; zval **socket; int host_len, id; @@ -1059,7 +1059,7 @@ PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock) redis_sock->watching = 1; } -PHPAPI void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, @@ -1079,7 +1079,7 @@ PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock) redis_sock->watching = 0; } -PHPAPI void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -1407,7 +1407,7 @@ PHP_METHOD(Redis, sort) { } } -PHPAPI void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, +PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int use_alpha) { @@ -1803,7 +1803,7 @@ PHP_METHOD(Redis, move) { } /* }}} */ -PHPAPI void +PHP_REDIS_API void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { zval *object; RedisSock *redis_sock; @@ -2284,7 +2284,7 @@ PHP_METHOD(Redis, discard) redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } -PHPAPI int +PHP_REDIS_API int redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { @@ -2457,7 +2457,7 @@ PHP_METHOD(Redis, exec) } } -PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { +PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { char *resp; int resp_len, ret = 0; @@ -2472,14 +2472,14 @@ PHPAPI int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { return ret; } -PHPAPI void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, +PHP_REDIS_API void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, RedisSock *redis_sock, zval *z_tab) { item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC); } -PHPAPI int +PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems) @@ -2552,7 +2552,7 @@ PHP_METHOD(Redis, subscribe) { * ); **/ -PHPAPI void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd) { zval *object, *array, **data; @@ -2900,7 +2900,7 @@ PHP_METHOD(Redis, wait) { } /* Construct a PUBSUB command */ -PHPAPI int +PHP_REDIS_API int redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, zval *arg TSRMLS_DC) { @@ -3069,7 +3069,7 @@ PHP_METHOD(Redis, pubsub) { // Construct an EVAL or EVALSHA command, with option argument array and number // of arguments that are keys parameter -PHPAPI int +PHP_REDIS_API int redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) @@ -3732,7 +3732,7 @@ PHP_METHOD(Redis, command) { /* }}} */ /* Helper to format any combination of SCAN arguments */ -PHPAPI int +PHP_REDIS_API int redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, int iter, char *pattern, int pattern_len, int count) { @@ -3790,7 +3790,7 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, } /* {{{ proto redis::scan(&$iterator, [pattern, [count]]) */ -PHPAPI void +PHP_REDIS_API void generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { zval *object, *z_iter; RedisSock *redis_sock; From 622709f311896c59a565886d089e13795de2c371 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 7 May 2015 18:12:52 -0700 Subject: [PATCH 0480/1986] Added a test for 'raw' commands --- tests/RedisClusterTest.php | 10 ++++++++++ tests/RedisTest.php | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 7877144c00..82ede4b6d4 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -484,5 +484,15 @@ public function testFailOver() { break; } } + + /* Test a 'raw' command */ + public function testRawCommand() { + $this->redis->rawCommand('mykey', 'set', 'mykey', 'my-value'); + $this->assertEquals($this->redis->get('mykey'), 'my-value'); + + $this->redis->del('mylist'); + $this->redis->rpush('mylist', 'A','B','C','D'); + $this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D')); + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 159436c2c6..00bb8b684f 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4755,5 +4755,16 @@ public function testPFCommands() { } } } + + /* 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), Array('A','B','C','D')); + } } ?> From 3491b188e0022f75b938738f7542603c7aae9077 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 8 May 2015 14:12:34 -0700 Subject: [PATCH 0481/1986] Added a new method sAddArray to both Redis and RedisCluster Presently, the sAdd command is variadic, meaning that it takes a key and then 1 to N additional arguments for the members being added. We need to keep this functionality to avoid breaking existing code, but there are good performance and other reasons to have an sAdd command which takes a key followed by an array of members, which is what the sAddArray method implements. --- php_redis.h | 1 + redis.c | 8 +++++++- redis_cluster.c | 7 +++++++ redis_cluster.h | 1 + redis_commands.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 6 files changed, 65 insertions(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index aa621f189e..b52fceef2a 100644 --- a/php_redis.h +++ b/php_redis.h @@ -80,6 +80,7 @@ PHP_METHOD(Redis, lGetRange); PHP_METHOD(Redis, lSet); PHP_METHOD(Redis, lInsert); PHP_METHOD(Redis, sAdd); +PHP_METHOD(Redis, sAddArray); PHP_METHOD(Redis, sSize); PHP_METHOD(Redis, sRemove); PHP_METHOD(Redis, sMove); diff --git a/redis.c b/redis.c index f880598c25..b338e07082 100644 --- a/redis.c +++ b/redis.c @@ -156,6 +156,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, lSet, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, lInsert, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, sAdd, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sAddArray, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, sSize, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, sRemove, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, sMove, NULL, ZEND_ACC_PUBLIC) @@ -1252,13 +1253,18 @@ PHP_METHOD(Redis, lGetRange) } /* }}} */ -/* {{{ proto boolean Redis::sAdd(string key , mixed value) */ +/* {{{ proto long Redis::sAdd(string key , mixed value) */ PHP_METHOD(Redis, sAdd) { REDIS_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, redis_long_response); } /* }}} */ +/* {{{ proto boolean Redis::sAddArray(string key, array $values) */ +PHP_METHOD(Redis, sAddArray) { + REDIS_PROCESS_KW_CMD("SADD", redis_key_arr_cmd, redis_long_response); +} /* }}} */ + /* {{{ proto int Redis::sSize(string key) */ PHP_METHOD(Redis, sSize) { diff --git a/redis_cluster.c b/redis_cluster.c index dddc39ffbe..a842b7caaa 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -91,6 +91,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, smembers, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sismember, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sadd, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, saddarray, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, srem, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sunion, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sunionstore, NULL, ZEND_ACC_PUBLIC) @@ -1196,6 +1197,12 @@ PHP_METHOD(RedisCluster, sadd) { } /* }}} */ +/* {{{ proto long RedisCluster::saddarray(string key, array values) */ +PHP_METHOD(RedisCluster, saddarray) { + CLUSTER_PROCESS_KW_CMD("SADD", redis_key_arr_cmd, cluster_long_resp, 0); +} +/* }}} */ + /* {{{ proto long RedisCluster::srem(string key, string val1 [, ...]) */ PHP_METHOD(RedisCluster, srem) { CLUSTER_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, cluster_long_resp, 0); diff --git a/redis_cluster.h b/redis_cluster.h index 3659d00e9d..8693d0d4f0 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -149,6 +149,7 @@ PHP_METHOD(RedisCluster, scard); PHP_METHOD(RedisCluster, smembers); PHP_METHOD(RedisCluster, sismember); PHP_METHOD(RedisCluster, sadd); +PHP_METHOD(RedisCluster, saddarray); PHP_METHOD(RedisCluster, srem); PHP_METHOD(RedisCluster, sunion); PHP_METHOD(RedisCluster, sunionstore); diff --git a/redis_commands.c b/redis_commands.c index 8d951dda74..d88e0574ef 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -999,6 +999,52 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Commands that take a key and then an array of values */ +int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + zval *z_key, *z_arr, **z_val; + HashTable *ht_arr; + HashPosition pos; + smart_str cmdstr = {0}; + int key_len, val_len, key_free, val_free, argc = 1; + char *key, *val; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + &z_arr) == FAILURE || + zend_hash_num_elements(Z_ARRVAL_P(z_arr)) == 0) + { + return FAILURE; + } + + /* Start constructing our command */ + ht_arr = Z_ARRVAL_P(z_arr); + argc += zend_hash_num_elements(ht_arr); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + + /* Prefix if required and append the key name */ + key_free = redis_key_prefix(redis_sock, &key, &key_len); + redis_cmd_append_sstr(&cmdstr, key, key_len); + CMD_SET_SLOT(slot, key, key_len); + if (key_free) efree(key); + + /* Iterate our hash table, serializing and appending values */ + for (zend_hash_internal_pointer_reset_ex(ht_arr, &pos); + zend_hash_get_current_data_ex(ht_arr, (void**)&z_val, &pos) == SUCCESS; + zend_hash_move_forward_ex(ht_arr, &pos)) + { + val_free = redis_serialize(redis_sock, *z_val, &val, &val_len TSRMLS_CC); + redis_cmd_append_sstr(&cmdstr, val, val_len); + if (val_free) STR_FREE(val); + } + + *cmd_len = cmdstr.len; + *cmd = cmdstr.c; + + return SUCCESS; +} + /* Generic function that takes a variable number of keys, with an optional * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ diff --git a/redis_commands.h b/redis_commands.h index f75eb1953a..d3950e92f0 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -67,6 +67,9 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Construct SCAN and similar commands, as well as check iterator */ int redis_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, char **cmd, int *cmd_len); From 1710419b4fc3bbfd744b6ac5594cfaf00a9d7802 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 8 May 2015 15:29:19 -0700 Subject: [PATCH 0482/1986] Protect against a memory leak if we're serializing HMSET values --- redis_commands.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index d88e0574ef..d3f878de18 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1596,12 +1596,14 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Serialize value (if directed) - val_free = redis_serialize(redis_sock, *z_val, &val, &val_len - TSRMLS_CC); + val_free = redis_serialize(redis_sock, *z_val, &val, &val_len TSRMLS_CC); // Append the key and value to our command redis_cmd_append_sstr(&cmdstr, mem, mem_len); redis_cmd_append_sstr(&cmdstr, val, val_len); + + // Free our value if we serialized it + if (val_free) STR_FREE(val); } // Set slot if directed From 7175aee5e2b8c231e09182e357119bc8f61c5811 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 8 May 2015 15:31:40 -0700 Subject: [PATCH 0483/1986] Don't double define constant (rebase/merge fail) --- tests/RedisArrayTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 831f2d2554..75f421b81d 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -542,6 +542,4 @@ function run_tests($className, $str_filter) { TestSuite::run($className, $str_filter); } -define('REDIS_ARRAY_DATA_SIZE', 1000); - ?> From 8cc7ab6240fdcee925c0a9510787ab3216f02b04 Mon Sep 17 00:00:00 2001 From: Werner Beroux Date: Mon, 18 May 2015 10:39:06 +0200 Subject: [PATCH 0484/1986] Fix #578 cases inconsistencies. --- README.markdown | 526 ++++++++++++++++++++++++------------------------ 1 file changed, 263 insertions(+), 263 deletions(-) diff --git a/README.markdown b/README.markdown index 417ce46d22..1601b7cba9 100644 --- a/README.markdown +++ b/README.markdown @@ -26,7 +26,7 @@ You can send comments, patches, questions [here on github](https://github.com/ni * [Pub/sub](#pubsub) * [Transactions](#transactions) * [Scripting](#scripting) - * [Introspection](#introspection) + * [Introspection](#introspection) ----- @@ -129,7 +129,7 @@ Note that it is possible to run only tests which match a substring of the test i
 # Just run the 'echo' test
-php tests/TestRedis.php --class Redis --test echo 
+php tests/TestRedis.php --class Redis --test echo
 
# Classes and methods @@ -191,10 +191,10 @@ _**Description**_: Connects to a Redis instance. ##### *Parameters* -*host*: string. can be a host, or the path to a unix domain socket -*port*: int, optional -*timeout*: float, value in seconds (optional, default is 0 meaning unlimited) -*reserved*: should be NULL if retry_interval is specified +*host*: string. can be a host, or the path to a unix domain socket +*port*: int, optional +*timeout*: float, value in seconds (optional, default is 0 meaning unlimited) +*reserved*: should be NULL if retry_interval is specified *retry_interval*: int, value in milliseconds (optional) ##### *Return value* @@ -227,10 +227,10 @@ persistent equivalents. ##### *Parameters* -*host*: string. can be a host, or the path to a unix domain socket -*port*: int, optional -*timeout*: float, value in seconds (optional, default is 0 meaning unlimited) -*persistent_id*: string. identity for the requested persistent connection +*host*: string. can be a host, or the path to a unix domain socket +*port*: int, optional +*timeout*: float, value in seconds (optional, default is 0 meaning unlimited) +*persistent_id*: string. identity for the requested persistent connection *retry_interval*: int, value in milliseconds (optional) ##### *Return value* @@ -284,8 +284,8 @@ _**Description**_: Disconnects from the Redis instance, except when `pconnect` i _**Description**_: Set client option. ##### *Parameters* -*parameter name* -*parameter value* +*parameter name* +*parameter value* ##### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. @@ -314,7 +314,7 @@ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); _**Description**_: Get client option. ##### *Parameters* -*parameter name* +*parameter name* ##### *Return value* Parameter value. @@ -352,21 +352,21 @@ _**Description**_: Sends a string to Redis, which replies with the same string ## Server -1. [bgrewriteaof](#bgrewriteaof) - Asynchronously rewrite the append-only file -1. [bgsave](#bgsave) - Asynchronously save the dataset to disk (in background) +1. [bgRewriteAOF](#bgrewriteaof) - Asynchronously rewrite the append-only file +1. [bgSave](#bgsave) - Asynchronously save the dataset to disk (in background) 1. [config](#config) - Get or Set the Redis server configuration parameters 1. [dbSize](#dbsize) - Return the number of keys in selected database 1. [flushAll](#flushall) - Remove all keys from all databases -1. [flushDB](#flushdb) - Remove all keys from the current database +1. [flushDb](#flushdb) - Remove all keys from the current database 1. [info](#info) - Get information and statistics about the server 1. [lastSave](#lastsave) - Get the timestamp of the last disk save 1. [resetStat](#resetstat) - Reset the stats returned by [info](#info) method. 1. [save](#save) - Synchronously save the dataset to disk (wait to complete) -1. [slaveof](#slaveof) - Make the server a slave of another instance, or promote it to master +1. [slaveOf](#slaveof) - Make the server a slave of another instance, or promote it to master 1. [time](#time) - Return the current server time -1. [slowlog](#slowlog) - Access the Redis slowlog entries +1. [slowLog](#slowlog) - Access the Redis slowLog entries -### bgrewriteaof +### bgRewriteAOF ----- _**Description**_: Start the background rewrite of AOF (Append-Only File) @@ -378,10 +378,10 @@ None. ##### *Example* ~~~ -$redis->bgrewriteaof(); +$redis->bgRewriteAOF(); ~~~ -### bgsave +### bgSave ----- _**Description**_: Asynchronously save the dataset to disk (in background) @@ -401,12 +401,12 @@ $redis->bgSave(); _**Description**_: Get or Set the Redis server configuration parameters. ##### *Parameters* -*operation* (string) either `GET` or `SET` -*key* string for `SET`, glob-pattern for `GET`. See http://redis.io/commands/config-get for examples. +*operation* (string) either `GET` or `SET` +*key* string for `SET`, glob-pattern for `GET`. See http://redis.io/commands/config-get for examples. *value* optional string (only for `SET`) ##### *Return value* -*Associative array* for `GET`, key -> value +*Associative array* for `GET`, key -> value *bool* for `SET` ##### *Examples* @@ -446,7 +446,7 @@ None. $redis->flushAll(); ~~~ -### flushDB +### flushDb ----- _**Description**_: Remove all keys from the current database. @@ -458,7 +458,7 @@ None. ##### *Example* ~~~ -$redis->flushDB(); +$redis->flushDb(); ~~~ ### info @@ -549,7 +549,7 @@ None. $redis->save(); ~~~ -### slaveof +### slaveOf ----- _**Description**_: Changes the slave status @@ -561,9 +561,9 @@ Either host (string) and port (int), or no parameter to stop being a slave. ##### *Example* ~~~ -$redis->slaveof('10.0.1.7', 6379); +$redis->slaveOf('10.0.1.7', 6379); /* ... */ -$redis->slaveof(); +$redis->slaveOf(); ~~~ ### time @@ -571,7 +571,7 @@ $redis->slaveof(); _**Description**_: Return the current server time. ##### *Parameters* -(none) +(none) ##### *Return value* If successfull, the time will come back as an associative array with element zero being @@ -582,34 +582,34 @@ the unix timestamp, and element one being microseconds. $redis->time(); ~~~ -### slowlog +### slowLog ----- -_**Description**_: Access the Redis slowlog +_**Description**_: Access the Redis slowLog ##### *Parameters* -*Operation* (string): This can be either `GET`, `LEN`, or `RESET` +*Operation* (string): This can be either `GET`, `LEN`, or `RESET` *Length* (integer), optional: If executing a `SLOWLOG GET` command, you can pass an optional length. ##### ##### *Return value* The return value of SLOWLOG will depend on which operation was performed. -SLOWLOG GET: Array of slowlog entries, as provided by Redis -SLOGLOG LEN: Integer, the length of the slowlog +SLOWLOG GET: Array of slowLog entries, as provided by Redis +SLOGLOG LEN: Integer, the length of the slowLog SLOWLOG RESET: Boolean, depending on success ##### ##### *Examples* ~~~ -// Get ten slowlog entries -$redis->slowlog('get', 10); -// Get the default number of slowlog entries +// Get ten slowLog entries +$redis->slowLog('get', 10); +// Get the default number of slowLog entries -$redis->slowlog('get'); -// Reset our slowlog -$redis->slowlog('reset'); +$redis->slowLog('get'); +// Reset our slowLog +$redis->slowLog('reset'); -// Retrieve slowlog length -$redis->slowlog('len'); +// Retrieve slowLog length +$redis->slowLog('len'); ~~~ ## Keys and Strings @@ -618,8 +618,8 @@ $redis->slowlog('len'); ----- * [append](#append) - Append a value to a key -* [bitcount](#bitcount) - Count set bits in a string -* [bitop](#bitop) - Perform bitwise operations between strings +* [bitCount](#bitcount) - Count set bits in a string +* [bitOp](#bitop) - Perform bitwise operations between strings * [decr, decrBy](#decr-decrby) - Decrement the value of a key * [get](#get) - Get the value of a key * [getBit](#getbit) - Returns the bit value at offset in the string value stored at key @@ -631,10 +631,10 @@ $redis->slowlog('len'); * [mSet, mSetNX](#mset-msetnx) - Set multiple keys to multiple values * [set](#set) - Set the string value of a key * [setBit](#setbit) - Sets or clears the bit at offset in the string value stored at key -* [setex, psetex](#setex-psetex) - Set the value and expiration of a key -* [setnx](#setnx) - Set the value of a key, only if the key does not exist +* [setEx, pSetEx](#setex-psetex) - Set the value and expiration of a key +* [setNx](#setnx) - Set the value of a key, only if the key does not exist * [setRange](#setrange) - Overwrite part of a string at key starting at the specified offset -* [strlen](#strlen) - Get the length of the value stored in a key +* [strLen](#strlen) - Get the length of the value stored in a key ### Keys ----- @@ -681,8 +681,8 @@ $redis->get('key'); _**Description**_: Set the string value in argument as value of the key. If you're using Redis >= 2.6.12, you can pass extended options as explained below ##### *Parameters* -*Key* -*Value* +*Key* +*Value* *Timeout or Options Array* (optional). If you pass an integer, phpredis will redirect to SETEX, and will try to use Redis >= 2.6.12 extended options if you pass an array with valid values ##### *Return value* @@ -704,7 +704,7 @@ $redis->set('key', 'value', Array('xx', 'px'=>1000)); ~~~ -### setex, psetex +### setEx, pSetEx ----- _**Description**_: Set the string value in argument as value of the key, with a time to live. PSETEX uses a TTL in milliseconds. @@ -719,11 +719,11 @@ _**Description**_: Set the string value in argument as value of the key, with a ##### *Examples* ~~~ -$redis->setex('key', 3600, 'value'); // sets key → value, with 1h TTL. -$redis->psetex('key', 100, 'value'); // sets key → value, with 0.1 sec TTL. +$redis->setEx('key', 3600, 'value'); // sets key → value, with 1h TTL. +$redis->pSetEx('key', 100, 'value'); // sets key → value, with 0.1 sec TTL. ~~~ -### setnx +### setNx ----- _**Description**_: Set the string value in argument as value of the key if the key doesn't already exist in the database. @@ -736,8 +736,8 @@ _**Description**_: Set the string value in argument as value of the key if the k ##### *Examples* ~~~ -$redis->setnx('key', 'value'); /* return TRUE */ -$redis->setnx('key', 'value'); /* return FALSE */ +$redis->setNx('key', 'value'); /* return TRUE */ +$redis->setNx('key', 'value'); /* return FALSE */ ~~~ ### del, delete @@ -784,7 +784,7 @@ $redis->exists('NonExistingKey'); /* FALSE */ _**Description**_: Increment the number stored at key by one. If the second argument is filled, it will be used as the integer value of the increment. ##### *Parameters* -*key* +*key* *value*: value that will be added to key (only for incrBy) ##### *Return value* @@ -806,8 +806,8 @@ $redis->incrBy('key1', 10); /* 14 */ _**Description**_: Increment the key with floating point precision. ##### *Parameters* -*key* -*value*: (float) value that will be added to the key +*key* +*value*: (float) value that will be added to the key ##### *Return value* *FLOAT* the new value @@ -827,7 +827,7 @@ $redis->incrByFloat('key1', 2.5); /* 4 */ _**Description**_: Decrement the number stored at key by one. If the second argument is filled, it will be used as the integer value of the decrement. ##### *Parameters* -*key* +*key* *value*: value that will be substracted to key (only for decrBy) ##### *Return value* @@ -1046,13 +1046,13 @@ _**Description**_: Returns the type of data pointed by a given key. ##### *Return value* -Depending on the type of the data pointed by the key, this method will return the following value: -string: Redis::REDIS_STRING -set: Redis::REDIS_SET -list: Redis::REDIS_LIST -zset: Redis::REDIS_ZSET -hash: Redis::REDIS_HASH -other: Redis::REDIS_NOT_FOUND +Depending on the type of the data pointed by the key, this method will return the following value: +string: Redis::REDIS_STRING +set: Redis::REDIS_SET +list: Redis::REDIS_LIST +zset: Redis::REDIS_ZSET +hash: Redis::REDIS_HASH +other: Redis::REDIS_NOT_FOUND ##### *Example* ~~~ @@ -1079,7 +1079,7 @@ $redis->get('key'); /* 'value1value2' */ ### getRange ----- -_**Description**_: Return a substring of a larger string +_**Description**_: Return a substring of a larger string *Note*: substr also supported but deprecated in redis. @@ -1089,7 +1089,7 @@ _**Description**_: Return a substring of a larger string *end* ##### *Return value* -*STRING*: the substring +*STRING*: the substring ##### *Example* ~~~ @@ -1103,9 +1103,9 @@ $redis->getRange('key', -5, -1); /* 'value' */ _**Description**_: Changes a substring of a larger string. ##### *Parameters* -*key* -*offset* -*value* +*key* +*offset* +*value* ##### *Return value* *STRING*: the length of the string after it was modified. @@ -1117,7 +1117,7 @@ $redis->setRange('key', 6, "redis"); /* returns 11 */ $redis->get('key'); /* "Hello redis" */ ~~~ -### strlen +### strLen ----- _**Description**_: Get the length of a string value. @@ -1138,8 +1138,8 @@ $redis->strlen('key'); /* 5 */ _**Description**_: Return a single bit out of a larger string ##### *Parameters* -*key* -*offset* +*key* +*offset* ##### *Return value* *LONG*: the bit value (0 or 1) @@ -1156,9 +1156,9 @@ $redis->getBit('key', 1); /* 1 */ _**Description**_: Changes a single bit of a string. ##### *Parameters* -*key* -*offset* -*value*: bool or int (1 or 0) +*key* +*offset* +*value*: bool or int (1 or 0) ##### *Return value* *LONG*: 0 or 1, the value of the bit before it was set. @@ -1171,25 +1171,25 @@ $redis->setBit('key', 7, 1); /* returns 0 */ $redis->get('key'); /* chr(0x2f) = "/" = b("0010 1111") */ ~~~ -### bitop +### bitOp ----- _**Description**_: Bitwise operation on multiple keys. ##### *Parameters* -*operation*: either "AND", "OR", "NOT", "XOR" -*ret_key*: return key -*key1* -*key2...* +*operation*: either "AND", "OR", "NOT", "XOR" +*ret_key*: return key +*key1* +*key2...* ##### *Return value* *LONG*: The size of the string stored in the destination key. -### bitcount +### bitCount ----- _**Description**_: Count bits in a string. ##### *Parameters* -*key* +*key* ##### *Return value* *LONG*: The number of bits set to 1 in the value behind the input key. @@ -1215,11 +1215,11 @@ An array of values, or a number corresponding to the number of elements stored i ##### *Example* ~~~ $redis->delete('s'); -$redis->sadd('s', 5); -$redis->sadd('s', 4); -$redis->sadd('s', 2); -$redis->sadd('s', 1); -$redis->sadd('s', 3); +$redis->sAdd('s', 5); +$redis->sAdd('s', 4); +$redis->sAdd('s', 2); +$redis->sAdd('s', 1); +$redis->sAdd('s', 3); var_dump($redis->sort('s')); // 1,2,3,4,5 var_dump($redis->sort('s', array('sort' => 'desc'))); // 5,4,3,2,1 @@ -1259,7 +1259,7 @@ _**Description**_: Remove the expiration timer from a key. $redis->persist('key'); ~~~ -### mset, msetnx +### mSet, mSetNx ----- _**Description**_: Sets multiple key-value pairs in one atomic command. MSETNX only returns TRUE if all the keys were set (see SETNX). @@ -1272,7 +1272,7 @@ _**Description**_: Sets multiple key-value pairs in one atomic command. MSETNX o ##### *Example* ~~~ -$redis->mset(array('key0' => 'value0', 'key1' => 'value1')); +$redis->mSet(array('key0' => 'value0', 'key1' => 'value1')); var_dump($redis->get('key0')); var_dump($redis->get('key1')); @@ -1290,7 +1290,7 @@ string(6) "value1" _**Description**_: Dump a key out of a redis database, the value of which can later be passed into redis using the RESTORE command. The data that comes out of DUMP is a binary representation of the key as Redis stores it. ##### *Parameters* -*key* string +*key* string ##### *Return value* The Redis encoded value of the key, or FALSE if the key doesn't exist ##### *Examples* @@ -1303,9 +1303,9 @@ $val = $redis->dump('foo'); // $val will be the Redis encoded key value ----- _**Description**_: Restore a key from the result of a DUMP operation. ##### *Parameters* -*key* string. The key name -*ttl* integer. How long the key should live (if zero, no expire will be set on the key) -*value* string (binary). The Redis encoded key value (from DUMP) +*key* string. The key name +*ttl* integer. How long the key should live (if zero, no expire will be set on the key) +*value* string (binary). The Redis encoded key value (from DUMP) ##### *Examples* ~~~ $redis->set('foo', 'bar'); @@ -1317,13 +1317,13 @@ $redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key ----- _**Description**_: Migrates a key to a different Redis instance. ##### *Parameters* -*host* string. The destination host -*port* integer. The TCP port to connect to. -*key* string. The key to migrate. -*destination-db* integer. The target DB. -*timeout* integer. The maximum amount of time given to this transfer. +*host* string. The destination host +*port* integer. The TCP port to connect to. +*key* string. The key to migrate. +*destination-db* integer. The target DB. +*timeout* integer. The maximum amount of time given to this transfer. *copy* boolean, optional. Should we send the COPY flag to redis -*replace* boolean, optional. Should we send the REPLACE flag to redis +*replace* boolean, optional. Should we send the REPLACE flag to redis ##### *Examples* ~~~ $redis->migrate('backup', 6379, 'foo', 0, 3600); @@ -1352,11 +1352,11 @@ $redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE fl ### hSet ----- -_**Description**_: Adds a value to the hash stored at key. If this value is already in the hash, `FALSE` is returned. +_**Description**_: Adds a value to the hash stored at key. If this value is already in the hash, `FALSE` is returned. ##### *Parameters* -*key* -*hashKey* -*value* +*key* +*hashKey* +*value* ##### *Return value* *LONG* `1` if value didn't exist and was added successfully, `0` if the value was already present and was replaced, `FALSE` if there was an error. @@ -1387,10 +1387,10 @@ $redis->hSetNx('h', 'key1', 'world'); /* FALSE, 'key1' => 'hello' in the hash at ### hGet ----- -_**Description**_: Gets a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. +_**Description**_: Gets a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* -*key* -*hashKey* +*key* +*hashKey* ##### *Return value* *STRING* The value, if the command executed successfully @@ -1415,10 +1415,10 @@ $redis->hLen('h'); /* returns 2 */ ### hDel ----- -_**Description**_: Removes a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. +_**Description**_: Removes a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* -*key* -*hashKey* +*key* +*hashKey* ##### *Return value* *BOOL* `TRUE` in case of success, `FALSE` in case of failure @@ -1533,7 +1533,7 @@ The order is random and corresponds to redis' own internal representation of the ----- _**Description**_: Verify if the specified member exists in a key. ##### *Parameters* -*key* +*key* *memberKey* ##### *Return value* *BOOL*: If the member exists in the hash table, return `TRUE`, otherwise return `FALSE`. @@ -1548,9 +1548,9 @@ $redis->hExists('h', 'NonExistingKey'); /* FALSE */ ----- _**Description**_: Increments the value of a member from a hash by a given amount. ##### *Parameters* -*key* -*member* -*value*: (integer) value that will be added to the member's value +*key* +*member* +*value*: (integer) value that will be added to the member's value ##### *Return value* *LONG* the new value ##### *Examples* @@ -1564,16 +1564,16 @@ $redis->hIncrBy('h', 'x', 1); /* h[x] ← 2 + 1. Returns 3 */ ----- _**Description**_: Increments the value of a hash member by the provided float value ##### *Parameters* -*key* -*member* -*value*: (float) value that will be added to the member's value +*key* +*member* +*value*: (float) value that will be added to the member's value ##### *Return value* *FLOAT* the new value ##### *Examples* ~~~ $redis->delete('h'); $redis->hIncrByFloat('h','x', 1.5); /* returns 1.5: h[x] = 1.5 now */ -$redis->hIncrByFLoat('h', 'x', 1.5); /* returns 3.0: h[x] = 3.0 now */ +$redis->hIncrByFloat('h', 'x', 1.5); /* returns 3.0: h[x] = 3.0 now */ $redis->hIncrByFloat('h', 'x', -3.0); /* returns 0.0: h[x] = 0.0 now */ ~~~ @@ -1581,14 +1581,14 @@ $redis->hIncrByFloat('h', 'x', -3.0); /* returns 0.0: h[x] = 0.0 now */ ----- _**Description**_: Fills in a whole hash. Non-string values are converted to string, using the standard `(string)` cast. NULL values are stored as empty strings. ##### *Parameters* -*key* -*members*: key → value array +*key* +*members*: key → value array ##### *Return value* -*BOOL* +*BOOL* ##### *Examples* ~~~ $redis->delete('user:1'); -$redis->hMset('user:1', array('name' => 'Joe', 'salary' => 2000)); +$redis->hMSet('user:1', array('name' => 'Joe', 'salary' => 2000)); $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. ~~~ @@ -1596,8 +1596,8 @@ $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. ----- _**Description**_: Retrieve the values associated to the specified fields in the hash. ##### *Parameters* -*key* -*memberKeys* Array +*key* +*memberKeys* Array ##### *Return value* *Array* An array of elements, the values of the specified fields in the hash, with the hash keys as array keys. ##### *Examples* @@ -1605,7 +1605,7 @@ _**Description**_: Retrieve the values associated to the specified fields in the $redis->delete('h'); $redis->hSet('h', 'field1', 'value1'); $redis->hSet('h', 'field2', 'value2'); -$redis->hmGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */ +$redis->hMGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */ ~~~ ### hScan @@ -1616,7 +1616,7 @@ _**Description**_: Scan a HASH value for members, with an optional pattern and *iterator*: Long (reference) *pattern*: Optional pattern to match against *count*: How many keys to return in a go (only a sugestion to Redis) -##### *Return value* +##### *Return value* *Array* An array of members that match our pattern ##### *Examples* @@ -1624,7 +1624,7 @@ _**Description**_: Scan a HASH value for members, with an optional pattern and $it = NULL; /* Don't ever return an empty array until we're done iterating */ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); -while($arr_keys = $redis->hscan('hash', $it)) { +while($arr_keys = $redis->hScan('hash', $it)) { foreach($arr_keys as $str_field => $str_value) { echo "$str_field => $str_value\n"; /* Print the hash member and value */ } @@ -1634,7 +1634,7 @@ while($arr_keys = $redis->hscan('hash', $it)) { ## Lists * [blPop, brPop](#blpop-brpop) - Remove and get the first/last element in a list -* [brpoplpush](#brpoplpush) - Pop a value from a list, push it to another list and return it +* [bRPopLPush](#brpoplpush) - Pop a value from a list, push it to another list and return it * [lIndex, lGet](#lindex-lget) - Get an element from a list by its index * [lInsert](#linsert) - Insert an element before or after another element in a list * [lLen, lSize](#llen-lsize) - Get the length/size of a list @@ -1646,9 +1646,9 @@ while($arr_keys = $redis->hscan('hash', $it)) { * [lSet](#lset) - Set the value of an element in a list by its index * [lTrim, listTrim](#ltrim-listtrim) - Trim a list to the specified range * [rPop](#rpop) - Remove and get the last element in a list -* [rpoplpush](#rpoplpush) - Remove the last element in a list, append it to another list and return it (redis >= 1.1) +* [rPopLPush](#rpoplpush) - Remove the last element in a list, append it to another list and return it (redis >= 1.1) * [rPush](#rpush) - Append one or multiple values to a list -* [rPushx](#rpushx) - Append a value to a list, only if the list exists +* [rPushX](#rpushx) - Append a value to a list, only if the list exists ### blPop, brPop ----- @@ -1697,13 +1697,13 @@ $redis->lPush('key1', 'A'); /* array('key1', 'A') is returned*/ ~~~ -### brpoplpush +### bRPopLPush ----- -_**Description**_: A blocking version of `rpoplpush`, with an integral timeout in the third parameter. +_**Description**_: A blocking version of `rPopLPush`, with an integral timeout in the third parameter. ##### *Parameters* -*Key*: srckey -*Key*: dstkey +*Key*: srckey +*Key*: dstkey *Long*: timeout ##### *Return value* @@ -1723,7 +1723,7 @@ Return `FALSE` in case of a bad index or a key that doesn't point to a list. *index* ##### *Return value* -*String* the element at this index +*String* the element at this index *Bool* `FALSE` if the key identifies a non-string data type, or no value corresponds to this index in the list `Key`. ##### *Example* @@ -1735,7 +1735,7 @@ $redis->lGet('key1', 0); /* 'A' */ $redis->lGet('key1', -1); /* 'C' */ $redis->lGet('key1', 10); /* `FALSE` */ ~~~ - +/ ### lInsert ----- _**Description**_: Insert value in the list before or after the pivot value. @@ -1794,7 +1794,7 @@ $redis->lPop('key1'); /* key1 => [ 'B', 'C' ] */ _**Description**_: Adds the string value to the head (left) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. ##### *Parameters* -*key* +*key* *value* String, value to push in key ##### *Return value* @@ -1814,7 +1814,7 @@ $redis->lPush('key1', 'A'); // returns 3 _**Description**_: Adds the string value to the head (left) of the list if the list exists. ##### *Parameters* -*key* +*key* *value* String, value to push in key ##### *Return value* @@ -1842,7 +1842,7 @@ _**Description**_: Returns the specified elements of the list stored at the spec *end* ##### *Return value* -*Array* containing the values in specified range. +*Array* containing the values in specified range. ##### *Example* ~~~ @@ -1859,21 +1859,21 @@ _**Description**_: Removes the first `count` occurences of the value element fro **Note**: The argument order is not the same as in the Redis documentation. This difference is kept for compatibility reasons. ##### *Parameters* -*key* -*value* -*count* +*key* +*value* +*count* ##### *Return value* -*LONG* the number of elements to remove +*LONG* the number of elements to remove *BOOL* `FALSE` if the value identified by key is not a list. ##### *Example* ~~~ $redis->lPush('key1', 'A'); $redis->lPush('key1', 'B'); -$redis->lPush('key1', 'C'); -$redis->lPush('key1', 'A'); -$redis->lPush('key1', 'A'); +$redis->lPush('key1', 'C'); +$redis->lPush('key1', 'A'); +$redis->lPush('key1', 'A'); $redis->lRange('key1', 0, -1); /* array('A', 'A', 'C', 'B', 'A') */ $redis->lRem('key1', 'A', 2); /* 2 */ @@ -1899,7 +1899,7 @@ $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->lGet('key1', 0); /* 'A' */ $redis->lSet('key1', 0, 'X'); -$redis->lGet('key1', 0); /* 'X' */ +$redis->lGet('key1', 0); /* 'X' */ ~~~ ### lTrim, listTrim @@ -1912,7 +1912,7 @@ _**Description**_: Trims an existing list so that it will contain only a specifi *stop* ##### *Return value* -*Array* +*Array* *Bool* return `FALSE` if the key identify a non-list value. ##### *Example* @@ -1944,12 +1944,12 @@ $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->rPop('key1'); /* key1 => [ 'A', 'B' ] */ ~~~ -### rpoplpush +### rPopLPush ----- _**Description**_: Pops a value from the tail of a list, and pushes it to the front of another list. Also return this value. (redis >= 1.1) ##### *Parameters* -*Key*: srckey +*Key*: srckey *Key*: dstkey ##### *Return value* @@ -1965,7 +1965,7 @@ $redis->lPush('y', '123'); $redis->lPush('y', '456'); // move the last of x to the front of y. -var_dump($redis->rpoplpush('x', 'y')); +var_dump($redis->rPopLPush('x', 'y')); var_dump($redis->lRange('x', 0, -1)); var_dump($redis->lRange('y', 0, -1)); @@ -1992,7 +1992,7 @@ array(3) { _**Description**_: Adds the string value to the tail (right) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. ##### *Parameters* -*key* +*key* *value* String, value to push in key ##### *Return value* @@ -2007,12 +2007,12 @@ $redis->rPush('key1', 'C'); // returns 3 /* key1 now points to the following list: [ 'A', 'B', 'C' ] */ ~~~ -### rPushx +### rPushX ----- _**Description**_: Adds the string value to the tail (right) of the list if the ist exists. `FALSE` in case of Failure. ##### *Parameters* -*key* +*key* *value* String, value to push in key ##### *Return value* @@ -2021,10 +2021,10 @@ _**Description**_: Adds the string value to the tail (right) of the list if the ##### *Examples* ~~~ $redis->delete('key1'); -$redis->rPushx('key1', 'A'); // returns 0 +$redis->rPushX('key1', 'A'); // returns 0 $redis->rPush('key1', 'A'); // returns 1 -$redis->rPushx('key1', 'B'); // returns 2 -$redis->rPushx('key1', 'C'); // returns 3 +$redis->rPushX('key1', 'B'); // returns 2 +$redis->rPushX('key1', 'C'); // returns 3 /* key1 now points to the following list: [ 'A', 'B', 'C' ] */ ~~~ @@ -2038,7 +2038,7 @@ If the list didn't exist or is empty, the command returns 0. If the data type id *Key* ##### *Return value* -*LONG* The size of the list identified by Key exists. +*LONG* The size of the list identified by Key exists. *BOOL* `FALSE` if the data type identified by Key is not list ##### *Example* @@ -2047,7 +2047,7 @@ $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->lSize('key1');/* 3 */ -$redis->rPop('key1'); +$redis->rPop('key1'); $redis->lSize('key1');/* 2 */ ~~~ @@ -2072,7 +2072,7 @@ $redis->lSize('key1');/* 2 */ ### sAdd ----- -_**Description**_: Adds a value to the set value stored at key. If this value is already in the set, `FALSE` is returned. +_**Description**_: Adds a value to the set value stored at key. If this value is already in the set, `FALSE` is returned. ##### *Parameters* *key* *value* @@ -2182,7 +2182,7 @@ is missing, `FALSE` is returned. ##### *Parameters* key1, key2, keyN: keys identifying the different sets on which we will apply the intersection. - + ##### *Return value* Array, contain the result of the intersection between those keys. If the intersection beteen the different sets is empty, the return value will be empty array. @@ -2334,7 +2334,7 @@ _**Description**_: Removes and returns a random element from the set value at Ke ##### *Parameters* *key* ##### *Return value* -*String* "popped" value +*String* "popped" value *Bool* `FALSE` if set identified by key is empty or doesn't exist. ##### *Example* ~~~ @@ -2349,12 +2349,12 @@ $redis->sPop('key1'); /* 'member3', 'key1' => {'member2'} */ ----- _**Description**_: Returns a random element from the set value at Key, without removing it. ##### *Parameters* -*key* -*count* (Integer, optional) +*key* +*count* (Integer, optional) ##### *Return value* If no count is provided, a random *String* value from the set will be returned. If a count is provided, an array of values from the set will be returned. Read about the different -ways to use the count here: [SRANDMEMBER](http://redis.io/commands/srandmember) +ways to use the count here: [SRANDMEMBER](http://redis.io/commands/srandmember) *Bool* `FALSE` if set identified by key is empty or doesn't exist. ##### *Example* ~~~ @@ -2485,7 +2485,7 @@ _**Description**_: Scan a set for members ~~~ $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* don't return empty results until we're done */ -while($arr_mems = $redis->sscan('set', $it, "*pattern*")) { +while($arr_mems = $redis->sScan('set', $it, "*pattern*")) { foreach($arr_mems as $str_mem) { echo "Member: $str_mem\n"; } @@ -2493,7 +2493,7 @@ while($arr_mems = $redis->sscan('set', $it, "*pattern*")) { $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); /* return after each iteration, even if empty */ -while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) { +while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) { if(count($arr_mems) > 0) { foreach($arr_mems as $str_mem) { echo "Member found: $str_mem\n"; @@ -2528,9 +2528,9 @@ while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) { _**Description**_: Add one or more members to a sorted set or update its score if it already exists ##### *Parameters* -*key* -*score* : double -*value*: string +*key* +*score* : double +*value*: string ##### *Return value* *Long* 1 if the element is added. 0 otherwise. @@ -2566,12 +2566,12 @@ $redis->zSize('key'); /* 3 */ _**Description**_: Returns the *number* of elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. ##### *Parameters* -*key* -*start*: string -*end*: string +*key* +*start*: string +*end*: string ##### *Return value* -*LONG* the size of a corresponding zRangeByScore. +*LONG* the size of a corresponding zRangeByScore. ##### *Example* ~~~ @@ -2586,9 +2586,9 @@ $redis->zCount('key', 0, 3); /* 2, corresponding to array('val0', 'val2') */ _**Description**_: Increments the score of a member from a sorted set by a given amount. ##### *Parameters* -*key* -*value*: (double) value that will be added to the member's score -*member* +*key* +*value*: (double) value that will be added to the member's score +*member* ##### *Return value* *DOUBLE* the new value @@ -2609,10 +2609,10 @@ The third optionnel argument defines `weights` to apply to the sorted sets in in The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. ##### *Parameters* -*keyOutput* -*arrayZSetKeys* -*arrayWeights* -*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zInter. +*keyOutput* +*arrayZSetKeys* +*arrayWeights* +*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zInter. ##### *Return value* *LONG* The number of values in the new sorted set. @@ -2652,13 +2652,13 @@ Start and stop are interpreted as zero-based indices: -1 the last element, -2 the penultimate ... ##### *Parameters* -*key* -*start*: long -*end*: long -*withscores*: bool = false +*key* +*start*: long +*end*: long +*withscores*: bool = false ##### *Return value* -*Array* containing the values in specified range. +*Array* containing the values in specified range. ##### *Example* ~~~ @@ -2676,15 +2676,15 @@ $redis->zRange('key1', 0, -1, true); /* array('val0' => 0, 'val2' => 2, 'val10' _**Description**_: Returns the elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. zRevRangeByScore returns the same items in reverse order, when the `start` and `end` parameters are swapped. ##### *Parameters* -*key* -*start*: string -*end*: string -*options*: array +*key* +*start*: string +*end*: string +*options*: array Two options are available: `withscores => TRUE`, and `limit => array($offset, $count)` ##### *Return value* -*Array* containing the values in specified range. +*Array* containing the values in specified range. ##### *Example* ~~~ @@ -2726,8 +2726,8 @@ $redis->zRangeByLex('key','-','[c',1,2) /* Array('b','c') */ _**Description**_: Returns the rank of a given member in the specified sorted set, starting at 0 for the item with the smallest score. zRevRank starts at 0 for the item with the *largest* score. ##### *Parameters* -*key* -*member* +*key* +*member* ##### *Return value* *Long*, the item's score. @@ -2748,8 +2748,8 @@ $redis->zRevRank('key', 'two'); /* 0 */ _**Description**_: Deletes a specified member from the ordered set. ##### *Parameters* -*key* -*member* +*key* +*member* ##### *Return value* *LONG* 1 on success, 0 on failure. @@ -2768,9 +2768,9 @@ $redis->zRange('key', 0, -1); /* array('val0', 'val10') */ _**Description**_: Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end]. ##### *Parameters* -*key* -*start*: LONG -*end*: LONG +*key* +*start*: LONG +*end*: LONG ##### *Return value* *LONG* The number of values deleted from the sorted set @@ -2789,9 +2789,9 @@ $redis->zRange('key', 0, -1, array('withscores' => TRUE)); /* array('three' => 3 _**Description**_: Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end]. ##### *Parameters* -*key* -*start*: double or "+inf" or "-inf" string -*end*: double or "+inf" or "-inf" string +*key* +*start*: double or "+inf" or "-inf" string +*end*: double or "+inf" or "-inf" string ##### *Return value* *LONG* The number of values deleted from the sorted set @@ -2811,13 +2811,13 @@ _**Description**_: Returns the elements of the sorted set stored at the specifie -1 the last element, -2 the penultimate ... ##### *Parameters* -*key* -*start*: long -*end*: long -*withscores*: bool = false +*key* +*start*: long +*end*: long +*withscores*: bool = false ##### *Return value* -*Array* containing the values in specified range. +*Array* containing the values in specified range. ##### *Example* ~~~ @@ -2835,8 +2835,8 @@ $redis->zRevRange('key', 0, -1, true); /* array('val10' => 10, 'val2' => 2, 'val _**Description**_: Returns the score of a given member in the specified sorted set. ##### *Parameters* -*key* -*member* +*key* +*member* ##### *Return value* *Double* @@ -2855,10 +2855,10 @@ The third optionnel argument defines `weights` to apply to the sorted sets in in The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. ##### *Parameters* -*keyOutput* -*arrayZSetKeys* -*arrayWeights* -*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zUnion. +*keyOutput* +*arrayZSetKeys* +*arrayWeights* +*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zUnion. ##### *Return value* *LONG* The number of values in the new sorted set. @@ -2902,7 +2902,7 @@ _**Description**_: Scan a sorted set for members, with optional pattern and coun ~~~ $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); -while($arr_matches = $redis->zscan('zset', $it, '*pattern*')) { +while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { foreach($arr_matches as $str_mem => $f_score) { echo "Key: $str_mem, Score: $f_score\n"; } @@ -2911,12 +2911,12 @@ while($arr_matches = $redis->zscan('zset', $it, '*pattern*')) { ## Pub/sub -* [psubscribe](#psubscribe) - Subscribe to channels by pattern +* [pSubscribe](#psubscribe) - Subscribe to channels by pattern * [publish](#publish) - Post a message to a channel * [subscribe](#subscribe) - Subscribe to channels -* [pubsub](#pubsub) - Introspection into the pub/sub subsystem +* [pubSub](#pubsub) - Introspection into the pub/sub subsystem -### psubscribe +### pSubscribe ----- _**Description**_: Subscribe to channels by pattern @@ -2926,7 +2926,7 @@ _**Description**_: Subscribe to channels by pattern *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~ -function psubscribe($redis, $pattern, $chan, $msg) { +function pSubscribe($redis, $pattern, $chan, $msg) { echo "Pattern: $pattern\n"; echo "Channel: $chan\n"; echo "Payload: $msg\n"; @@ -2938,8 +2938,8 @@ function psubscribe($redis, $pattern, $chan, $msg) { _**Description**_: Publish messages to channels. Warning: this function will probably change in the future. ##### *Parameters* -*channel*: a channel to publish to -*messsage*: string +*channel*: a channel to publish to +*messsage*: string ##### *Example* ~~~ @@ -2951,8 +2951,8 @@ $redis->publish('chan-1', 'hello, world!'); // send message. _**Description**_: Subscribe to channels. Warning: this function will probably change in the future. ##### *Parameters* -*channels*: an array of channels to subscribe to -*callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. +*channels*: an array of channels to subscribe to +*callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~ @@ -2975,7 +2975,7 @@ function f($redis, $chan, $msg) { $redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans ~~~ -### pubsub +### pubSub ----- _**Description**_: A command allowing you to get information on the Redis pub/sub system. @@ -2990,10 +2990,10 @@ _**Description**_: A command allowing you to get information on the Redis pub/su ##### *Example* ~~~ -$redis->pubsub("channels"); /*All channels */ -$redis->pubsub("channels", "*pattern*"); /* Just channels matching your pattern */ -$redis->pubsub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/ -$redsi->pubsub("numpat"); /* Get the number of pattern subscribers */ +$redis->pubSub("channels"); /*All channels */ +$redis->pubSub("channels", "*pattern*"); /* Just channels matching your pattern */ +$redis->pubSub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/ +$redsi->pubSub("numpat"); /* Get the number of pattern subscribers */ ``` ## Transactions @@ -3003,10 +3003,10 @@ $redsi->pubsub("numpat"); /* Get the number of pattern subscribers */ ### multi, exec, discard. ----- -_**Description**_: Enter and exit transactional mode. +_**Description**_: Enter and exit transactional mode. ##### *Parameters* -(optional) `Redis::MULTI` or `Redis::PIPELINE`. Defaults to `Redis::MULTI`. A `Redis::MULTI` block of commands runs as a single transaction; a `Redis::PIPELINE` block is simply transmitted faster to the server, but without any guarantee of atomicity. `discard` cancels a transaction. +(optional) `Redis::MULTI` or `Redis::PIPELINE`. Defaults to `Redis::MULTI`. A `Redis::MULTI` block of commands runs as a single transaction; a `Redis::PIPELINE` block is simply transmitted faster to the server, but without any guarantee of atomicity. `discard` cancels a transaction. ##### *Return value* `multi()` returns the Redis instance and enters multi-mode. Once in multi-mode, all subsequent method calls return the same object until `exec()` is called. @@ -3068,9 +3068,9 @@ $ret = FALSE if x has been modified between the call to WATCH and the call to EX _**Description**_: Evaluate a LUA script serverside ##### *Parameters* -*script* string. -*args* array, optional. -*num_keys* int, optional. +*script* string. +*args* array, optional. +*num_keys* int, optional. ##### *Return value* Mixed. What is returned depends on what the LUA script itself returns, which could be a scalar value (int/string), or an array. @@ -3097,9 +3097,9 @@ In order to run this command Redis will have to have already loaded the script, either by running it or via the SCRIPT LOAD command. ##### *Parameters* -*script_sha* string. The sha1 encoded hash of the script you want to run. -*args* array, optional. Arguments to pass to the LUA script. -*num_keys* int, optional. The number of arguments that should go into the KEYS array, vs. the ARGV array when Redis spins the script +*script_sha* string. The sha1 encoded hash of the script you want to run. +*args* array, optional. Arguments to pass to the LUA script. +*num_keys* int, optional. The number of arguments that should go into the KEYS array, vs. the ARGV array when Redis spins the script ##### *Return value* Mixed. See EVAL @@ -3133,7 +3133,7 @@ $redis->script('exists', $script1, [$script2, $script3, ...]); ----- _**Description**_: Issue the CLIENT command with various arguments. -The Redis CLIENT command can be used in four ways. +The Redis CLIENT command can be used in four ways. * CLIENT LIST * CLIENT GETNAME * CLIENT SETNAME [name] @@ -3147,8 +3147,8 @@ $redis->client('setname', 'somename'); // Set the name of the current connection $redis->client('kill', ); // Kill the process at ip:port ~~~ -##### *Return value* -This will vary depending on which client command was executed. +##### *Return value* +This will vary depending on which client command was executed. * CLIENT LIST will return an array of arrays with client information. * CLIENT GETNAME will return the client name or false if none has been set @@ -3162,7 +3162,7 @@ but may not notice losing it! _**Description**_: The last error message (if any) ##### *Parameters* -*none* +*none* ##### *Return value* A string with the last returned script based error message, or NULL if there is no error @@ -3170,19 +3170,19 @@ A string with the last returned script based error message, or NULL if there is ##### *Examples* ~~~ $redis->eval('this-is-not-lua'); -$err = $redis->getLastError(); +$err = $redis->getLastError(); // "ERR Error compiling script (new function): user_script:1: '=' expected near '-'" ~~~ ### clearLastError ----- -_**Description**_: Clear the last error message +_**Description**_: Clear the last error message ##### *Parameters* -*none* +*none* ##### *Return value* -*BOOL* TRUE +*BOOL* TRUE ##### *Examples* ~~~ @@ -3200,7 +3200,7 @@ $err = $redis->getLastError(); _**Description**_: A utility method to prefix the value with the prefix setting for phpredis. ##### *Parameters* -*value* string. The value you wish to prefix +*value* string. The value you wish to prefix ##### *Return value* If a prefix is set up, the value now prefixed. If there is no prefix, the value will be returned unchanged. @@ -3243,7 +3243,7 @@ and the data passed in is malformed, an exception will be thrown. This can be us serializing values, and you return something from redis in a LUA script that is serialized. ##### *Parameters* -*value* string. The value to be unserialized +*value* string. The value to be unserialized ##### *Examples* ~~~ @@ -3253,85 +3253,85 @@ $redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2 -## Introspection +## Introspection -### IsConnected +### isConnected ----- _**Description**_: A method to determine if a phpredis object thinks it's connected to a server ##### *Parameters* -None +None ##### *Return value* *Boolean* Returns TRUE if phpredis thinks it's connected and FALSE if not -### GetHost +### getHost ----- _**Description**_: Retreive our host or unix socket that we're connected to ##### *Parameters* -None +None ##### *Return value* *Mixed* The host or unix socket we're connected to or FALSE if we're not connected -### GetPort +### getPort ----- _**Description**_: Get the port we're connected to ##### *Parameters* -None +None ##### *Return value* *Mixed* Returns the port we're connected to or FALSE if we're not connected -### getDBNum +### getDbNum ----- _**Description**_: Get the database number phpredis is pointed to ##### *Parameters* -None +None ##### *Return value* *Mixed* Returns the database number (LONG) phpredis thinks it's pointing to or FALSE if we're not connected -### GetTimeout +### getTimeout ----- _**Description**_: Get the (write) timeout in use for phpredis ##### *Parameters* -None +None ##### *Return value* *Mixed* The timeout (DOUBLE) specified in our connect call or FALSE if we're not connected -### GetReadTimeout +### getReadTimeout _**Description**_: Get the read timeout specified to phpredis or FALSE if we're not connected ##### *Parameters* -None +None ##### *Return value* *Mixed* Returns the read timeout (which can be set using setOption and Redis::OPT_READ_TIMEOUT) or FALSE if we're not connected -### GetPersistentID +### getPersistentID ----- _**Description**_: Gets the persistent ID that phpredis is using ##### *Parameters* -None +None ##### *Return value* *Mixed* Returns the persistent id phpredis is using (which will only be set if connected with pconnect), NULL if we're not using a persistent ID, and FALSE if we're not connected -### GetAuth +### getAuth ----- _**Description**_: Get the password used to authenticate the phpredis connection ### *Parameters* -None +None ### *Return value* *Mixed* Returns the password used to authenticate a phpredis session or NULL if none was used, and FALSE if we're not connected From 79d14360d91aa620be93cbfb80e092109da305e2 Mon Sep 17 00:00:00 2001 From: Ilyas Date: Wed, 3 Jun 2015 14:48:28 +0500 Subject: [PATCH 0485/1986] Fix Set igbinary as serializer. Because it is not defined HAVE_REDIS_IGBINARY in 2918 line. --- redis_commands.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redis_commands.c b/redis_commands.c index d3f878de18..0de5b7710d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -18,6 +18,10 @@ +----------------------------------------------------------------------+ */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "redis_commands.h" #include From 3662852ab1e4aaa7dd293a568bcfddf79afa5d4a Mon Sep 17 00:00:00 2001 From: linfangrong Date: Thu, 11 Jun 2015 22:39:50 +0800 Subject: [PATCH 0486/1986] Update redis_cluster.c generic_zrange_cmd is readcmd --- redis_cluster.c | 1 + 1 file changed, 1 insertion(+) diff --git a/redis_cluster.c b/redis_cluster.c index a842b7caaa..b4baf39204 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1587,6 +1587,7 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, zrange_cb fun) { redisCluster *c = GET_CONTEXT(); + c->readonly = CLUSTER_IS_ATOMIC(c); cluster_cb cb; char *cmd; int cmd_len; short slot; int withscores=0; From fcea71f99bb5fc3873539d42e1e29000fa36ec46 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jun 2015 12:45:54 -0700 Subject: [PATCH 0487/1986] Initial commit for cluster-enabled session handling --- cluster_library.c | 64 +++++++++++- cluster_library.h | 3 + redis.c | 2 + redis_session.c | 249 +++++++++++++++++++++++++++++++++++++++++++++- redis_session.h | 6 ++ 5 files changed, 322 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 93885c8a5e..b6a760e336 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -97,7 +97,7 @@ void cluster_free_reply(clusterReply *reply, int free_data) { case TYPE_ERR: case TYPE_LINE: case TYPE_BULK: - if(free_data) + if(free_data && reply->str) efree(reply->str); break; case TYPE_MULTIBULK: @@ -785,6 +785,68 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { return cluster_get_asking_node(c TSRMLS_CC)->sock; } +/* Our context seeds will be a hash table with RedisSock* pointers */ +static void ht_free_seed(void *data) { + RedisSock *redis_sock = *(RedisSock**)data; + if(redis_sock) redis_free_socket(redis_sock); +} + +/* Free redisClusterNode objects we've stored */ +static void ht_free_node(void *data) { + redisClusterNode *node = *(redisClusterNode**)data; + cluster_free_node(node); +} + +/* Construct a redisCluster object */ +PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, + int failover) +{ + redisCluster *c; + + /* Actual our actual cluster structure */ + c = ecalloc(1, sizeof(redisCluster)); + + /* Initialize flags and settings */ + c->flags = ecalloc(1, sizeof(RedisSock)); + c->subscribed_slot = -1; + c->clusterdown = 0; + c->timeout = timeout; + c->read_timeout = read_timeout; + + /* Set up our waitms based on timeout */ + c->waitms = (long)(1000 * timeout); + + /* Allocate our seeds hash table */ + ALLOC_HASHTABLE(c->seeds); + zend_hash_init(c->seeds, 0, NULL, ht_free_seed, 0); + + /* Allocate our nodes HashTable */ + ALLOC_HASHTABLE(c->nodes); + zend_hash_init(c->nodes, 0, NULL, ht_free_node, 0); + + return c; +} + +PHP_REDIS_API void cluster_free(redisCluster *c) { + /* Free any allocated prefix */ + if (c->flags->prefix) efree(c->flags->prefix); + efree(c->flags); + + /* Call hash table destructors */ + zend_hash_destroy(c->seeds); + zend_hash_destroy(c->nodes); + + /* Free hash tables themselves */ + efree(c->seeds); + efree(c->nodes); + + /* Free any error we've got */ + if (c->err) efree(c->err); + + /* Free structure itself */ + efree(c); +} + /* Initialize seeds */ PHP_REDIS_API int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { diff --git a/cluster_library.h b/cluster_library.h index a5020dc677..e83ef2f990 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -358,6 +358,9 @@ PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC); +PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, + int failover); +PHP_REDIS_API void cluster_free(redisCluster *c); PHP_REDIS_API int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC); PHP_REDIS_API void cluster_free_node(redisClusterNode *node); diff --git a/redis.c b/redis.c index b338e07082..313672ca6d 100644 --- a/redis.c +++ b/redis.c @@ -52,6 +52,7 @@ extern int le_redis_array; #ifdef PHP_SESSION extern ps_module ps_mod_redis; +extern ps_module ps_mod_redis_cluster; #endif extern zend_class_entry *redis_array_ce; @@ -541,6 +542,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) #ifdef PHP_SESSION php_session_register_module(&ps_mod_redis); + php_session_register_module(&ps_mod_redis_cluster); #endif } diff --git a/redis_session.c b/redis_session.c index 0416cdc0a0..71834739d7 100644 --- a/redis_session.c +++ b/redis_session.c @@ -34,6 +34,7 @@ #include #include "library.h" +#include "cluster_library.h" #include "php.h" #include "php_ini.h" @@ -44,6 +45,9 @@ ps_module ps_mod_redis = { PS_MOD(redis) }; +ps_module ps_mod_redis_cluster = { + PS_MOD(rediscluster) +}; typedef struct redis_pool_member_ { @@ -333,7 +337,6 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses return session; } - /* {{{ PS_READ_FUNC */ PS_READ_FUNC(redis) @@ -457,5 +460,249 @@ PS_GC_FUNC(redis) } /* }}} */ +/** + * Redis Cluster session handler functions + */ + +/* Helper to extract timeout values */ +static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_len, + double *val) +{ + zval **z_val; + + if (zend_hash_find(ht_conf, key, key_len, (void**)&z_val) == SUCCESS) { + if (Z_TYPE_PP(z_val) == IS_STRING) { + *val = atof(Z_STRVAL_PP(z_val)); + } else { + *val = Z_DVAL_PP(z_val); + } + } +} + +/* Prefix a session key */ +static char *cluster_session_key(redisCluster *c, const char *key, int keylen, + int *skeylen, short *slot) { + char *skey; + + *skeylen = keylen + c->flags->prefix_len; + skey = emalloc(*skeylen); + memcpy(skey, c->flags->prefix, c->flags->prefix_len); + memcpy(skey + c->flags->prefix_len, key, keylen); + + *slot = cluster_hash_key(skey, *skeylen); + + return skey; +} + +PS_OPEN_FUNC(rediscluster) { + redisCluster *c; + zval *z_conf, **z_val; + HashTable *ht_conf, *ht_seeds; + double timeout = 0, read_timeout = 0; + int retval, prefix_len, failover = REDIS_FAILOVER_NONE; + char *prefix; + + /* Parse configuration for session handler */ + MAKE_STD_ZVAL(z_conf); + array_init(z_conf); + sapi_module.treat_data(PARSE_STRING, estrdup(save_path), z_conf TSRMLS_CC); + + /* Sanity check that we're able to parse and have a seeds array */ + if (Z_TYPE_P(z_conf) != IS_ARRAY || + zend_hash_find(Z_ARRVAL_P(z_conf), "seed", sizeof("seed"), (void**)&z_val) == FAILURE || + Z_TYPE_PP(z_val) != IS_ARRAY) + { + zval_dtor(z_conf); + efree(z_conf); + return FAILURE; + } + + /* Grab a copy of our config hash table and keep seeds array */ + ht_conf = Z_ARRVAL_P(z_conf); + ht_seeds = Z_ARRVAL_PP(z_val); + + /* Grab timeouts if they were specified */ + session_conf_timeout(ht_conf, "timeout", sizeof("timeout"), &timeout); + session_conf_timeout(ht_conf, "read_timeout", sizeof("read_timeout"), &read_timeout); + + /* Look for a specific prefix */ + if (zend_hash_find(ht_conf, "prefix", sizeof("prefix"), (void**)&z_val) == SUCCESS && + Z_TYPE_PP(z_val) == IS_STRING && Z_STRLEN_PP(z_val) > 0) + { + prefix = Z_STRVAL_PP(z_val); + prefix_len = Z_STRLEN_PP(z_val); + } else { + prefix = "PHPREDIS_CLUSTER_SESSION:"; + prefix_len = sizeof("PHPREDIS_CLUSTER_SESSION:")-1; + } + + /* Look for a specific failover setting */ + if (zend_hash_find(ht_conf, "failover", sizeof("failover"), (void**)&z_val) == SUCCESS && + Z_TYPE_PP(z_val) == IS_STRING) + { + if (!strcasecmp(Z_STRVAL_PP(z_val), "error")) { + failover = REDIS_FAILOVER_ERROR; + } else if (!strcasecmp(Z_STRVAL_PP(z_val), "distribute")) { + failover = REDIS_FAILOVER_DISTRIBUTE; + } + } + + c = cluster_create(timeout, read_timeout, failover); + if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) { + /* Set up our prefix */ + c->flags->prefix = estrndup(prefix, prefix_len); + c->flags->prefix_len = prefix_len; + + PS_SET_MOD_DATA(c); + retval = SUCCESS; + } else { + cluster_free(c); + retval = FAILURE; + } + + /* Cleanup */ + zval_dtor(z_conf); + efree(z_conf); + + return retval; +} + +/* {{{ PS_READ_FUNC + */ +PS_READ_FUNC(rediscluster) { + redisCluster *c = PS_GET_MOD_DATA(); + clusterReply *reply; + char *cmd, *skey; + int cmdlen, skeylen; + short slot; + + /* Set up our command and slot information */ + skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); + cmdlen = redis_cmd_format_static(&cmd, "GET", "s", skey, skeylen); + efree(skey); + + /* Attempt to kick off our command */ + c->readonly = 1; + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { + efree(cmd); + return FAILURE; + } + + /* Clean up command */ + efree(cmd); + + /* Attempt to read reply */ + reply = cluster_read_resp(c TSRMLS_CC); + if (!reply || c->err) { + if (reply) cluster_free_reply(reply, 1); + return FAILURE; + } + + /* Push reply value to caller */ + *val = reply->str; + *vallen = reply->len; + + /* Clean up */ + cluster_free_reply(reply, 0); + + /* Success! */ + return SUCCESS; +} + +/* {{{ PS_WRITE_FUNC + */ +PS_WRITE_FUNC(rediscluster) { + redisCluster *c = PS_GET_MOD_DATA(); + clusterReply *reply; + char *cmd, *skey; + int cmdlen, skeylen; + short slot; + + /* Set up command and slot info */ + skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); + cmdlen = redis_cmd_format_static(&cmd, "SETEX", "sds", skey, skeylen, + INI_INT("session.gc_maxlifetime"), + val, vallen); + efree(skey); + + /* Attempt to send command */ + c->readonly = 0; + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { + efree(cmd); + return FAILURE; + } + + /* Clean up our command */ + efree(cmd); + + /* Attempt to read reply */ + reply = cluster_read_resp(c TSRMLS_CC); + if (!reply || c->err) { + if (reply) cluster_free_reply(reply, 1); + return FAILURE; + } + + /* Clean up*/ + cluster_free_reply(reply, 1); + + return SUCCESS; +} + +/* {{{ PS_DESTROY_FUNC(rediscluster) + */ +PS_DESTROY_FUNC(rediscluster) { + redisCluster *c = PS_GET_MOD_DATA(); + clusterReply *reply; + char *cmd, *skey; + int cmdlen, skeylen; + short slot; + + /* Set up command and slot info */ + skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); + cmdlen = redis_cmd_format_static(&cmd, "DEL", "s", skey, skeylen); + efree(skey); + + /* Attempt to send command */ + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { + if (reply) cluster_free_reply(reply, 1); + efree(cmd); + return FAILURE; + } + + /* Clean up our command */ + efree(cmd); + + /* Attempt to read reply */ + reply = cluster_read_resp(c TSRMLS_CC); + if (!reply || c->err) { + if (reply) cluster_free_reply(reply, 1); + return FAILURE; + } + + /* Clean up our reply */ + cluster_free_reply(reply, 1); + + return SUCCESS; +} + +/* {{{ PS_CLOSE_FUNC + */ +PS_CLOSE_FUNC(rediscluster) +{ + redisCluster *c = PS_GET_MOD_DATA(); + if (c) { + cluster_free(c); + PS_SET_MOD_DATA(NULL); + } + return SUCCESS; +} + +/* {{{ PS_GC_FUNC + */ +PS_GC_FUNC(rediscluster) { + return SUCCESS; +} + #endif + /* vim: set tabstop=4 expandtab: */ diff --git a/redis_session.h b/redis_session.h index ce74e23f6b..11f861c240 100644 --- a/redis_session.h +++ b/redis_session.h @@ -10,6 +10,12 @@ PS_WRITE_FUNC(redis); PS_DESTROY_FUNC(redis); PS_GC_FUNC(redis); +PS_OPEN_FUNC(rediscluster); +PS_CLOSE_FUNC(rediscluster); +PS_READ_FUNC(rediscluster); +PS_WRITE_FUNC(rediscluster); +PS_DESTROY_FUNC(rediscluster); +PS_GC_FUNC(rediscluster); #endif #endif From a24fa532f237b63e2b3ad247e13efd5c69ae82b0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jun 2015 13:01:28 -0700 Subject: [PATCH 0488/1986] Don't allow negative timeout values --- redis_session.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/redis_session.c b/redis_session.c index 71834739d7..49141addba 100644 --- a/redis_session.c +++ b/redis_session.c @@ -525,6 +525,15 @@ PS_OPEN_FUNC(rediscluster) { session_conf_timeout(ht_conf, "timeout", sizeof("timeout"), &timeout); session_conf_timeout(ht_conf, "read_timeout", sizeof("read_timeout"), &read_timeout); + /* Sanity check on our timeouts */ + if (timeout < 0 || read_timeout < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Can't set negative timeout values in session configuration"); + zval_dtor(z_conf); + efree(z_conf); + return FAILURE; + } + /* Look for a specific prefix */ if (zend_hash_find(ht_conf, "prefix", sizeof("prefix"), (void**)&z_val) == SUCCESS && Z_TYPE_PP(z_val) == IS_STRING && Z_STRLEN_PP(z_val) > 0) From 81cb2bcc2fd47f9976adc41d73d180cab80b20c0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jun 2015 13:34:48 -0700 Subject: [PATCH 0489/1986] Fix memory leak for non cluster session handling --- redis_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index 49141addba..46bc482b68 100644 --- a/redis_session.c +++ b/redis_session.c @@ -108,7 +108,7 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { while(rpm) { next = rpm->next; redis_sock_disconnect(rpm->redis_sock TSRMLS_CC); - efree(rpm->redis_sock); + redis_free_socket(rpm->redis_sock); if(rpm->prefix) efree(rpm->prefix); if(rpm->auth) efree(rpm->auth); efree(rpm); From ece6c1d226ebf851bf247ca1b386d964387d21ef Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 18 Jun 2015 08:09:19 -0700 Subject: [PATCH 0490/1986] Failover fix for cluster sessions and srand migration * We weren't setting the failover member even when specified in the context of a Redis Cluster session. * Moved random number seeding to MINIT so it only ever happens once --- cluster_library.c | 1 + redis.c | 6 ++++++ redis_cluster.c | 4 ---- redis_session.c | 10 ++++------ 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index b6a760e336..6275080e17 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -812,6 +812,7 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, c->clusterdown = 0; c->timeout = timeout; c->read_timeout = read_timeout; + c->failover = failover; /* Set up our waitms based on timeout */ c->waitms = (long)(1000 * timeout); diff --git a/redis.c b/redis.c index 313672ca6d..7539562630 100644 --- a/redis.c +++ b/redis.c @@ -551,12 +551,18 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) */ PHP_MINIT_FUNCTION(redis) { + struct timeval tv; + zend_class_entry redis_class_entry; zend_class_entry redis_array_class_entry; zend_class_entry redis_cluster_class_entry; zend_class_entry redis_exception_class_entry; zend_class_entry redis_cluster_exception_class_entry; + /* Seed random generator (for RedisCluster failover) */ + gettimeofday(&tv, NULL); + srand(tv.tv_usec * tv.tv_sec); + REGISTER_INI_ENTRIES(); /* Redis class */ diff --git a/redis_cluster.c b/redis_cluster.c index a842b7caaa..e51de836f9 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -266,10 +266,6 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { redisCluster *cluster; struct timeval t1; - /* Seed random generator for failover */ - gettimeofday(&t1, NULL); - srand(t1.tv_usec * t1.tv_sec); - // Allocate our actual struct cluster = emalloc(sizeof(redisCluster)); memset(cluster, 0, sizeof(redisCluster)); diff --git a/redis_session.c b/redis_session.c index 46bc482b68..b935f34d42 100644 --- a/redis_session.c +++ b/redis_session.c @@ -470,12 +470,10 @@ static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_le { zval **z_val; - if (zend_hash_find(ht_conf, key, key_len, (void**)&z_val) == SUCCESS) { - if (Z_TYPE_PP(z_val) == IS_STRING) { - *val = atof(Z_STRVAL_PP(z_val)); - } else { - *val = Z_DVAL_PP(z_val); - } + if (zend_hash_find(ht_conf, key, key_len, (void**)&z_val) == SUCCESS && + Z_TYPE_PP(z_val) == IS_STRING) + { + *val = atof(Z_STRVAL_PP(z_val)); } } From c5811b1038649f861a29511b0ef8cf46df34ac66 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 20 Jun 2015 11:29:38 -0700 Subject: [PATCH 0491/1986] Persistent connections for cluster and possible crash fix --- cluster_library.c | 8 +++++--- cluster_library.h | 5 ++++- redis_cluster.c | 33 +++++++++++++++++++++++++-------- redis_session.c | 11 +++++++++-- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 6275080e17..24e656e8f0 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -650,7 +650,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, // Attach socket node->sock = redis_sock_create(host, host_len, port, c->timeout, - 0, NULL, 0, 1); + c->persistent, NULL, 0, 1); return node; } @@ -799,7 +799,7 @@ static void ht_free_node(void *data) { /* Construct a redisCluster object */ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, - int failover) + int failover, int persistent) { redisCluster *c; @@ -813,6 +813,7 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, c->timeout = timeout; c->read_timeout = read_timeout; c->failover = failover; + c->persistent = persistent; /* Set up our waitms based on timeout */ c->waitms = (long)(1000 * timeout); @@ -877,7 +878,8 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { // Allocate a structure for this seed redis_sock = redis_sock_create(str, psep-str, - (unsigned short)atoi(psep+1),cluster->timeout,0,NULL,0,0); + (unsigned short)atoi(psep+1), cluster->timeout, + cluster->persistent, NULL, 0, 0); // Index this seed by host/port key_len = snprintf(key, sizeof(key), "%s:%u", redis_sock->host, diff --git a/cluster_library.h b/cluster_library.h index e83ef2f990..f82667579d 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -180,6 +180,9 @@ typedef struct redisCluster { double timeout; double read_timeout; + /* Are we using persistent connections */ + int persistent; + /* How long in milliseconds should we wait when being bounced around */ long waitms; @@ -359,7 +362,7 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC); PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, - int failover); + int failover, int persistent); PHP_REDIS_API void cluster_free(redisCluster *c); PHP_REDIS_API int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC); diff --git a/redis_cluster.c b/redis_cluster.c index e51de836f9..f47e8ca77b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -264,7 +264,6 @@ zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { zend_object_value retval; redisCluster *cluster; - struct timeval t1; // Allocate our actual struct cluster = emalloc(sizeof(redisCluster)); @@ -333,7 +332,7 @@ void free_cluster_context(void *object TSRMLS_DC) { /* Attempt to connect to a Redis cluster provided seeds and timeout options */ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, - double read_timeout TSRMLS_DC) + double read_timeout, int persistent TSRMLS_DC) { // Validate timeout if(timeout < 0L || timeout > INT_MAX) { @@ -357,6 +356,9 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, * socket type operations */ c->timeout = timeout; c->read_timeout = read_timeout; + + /* Set our option to use or not use persistent connections */ + c->persistent = persistent; /* Calculate the number of miliseconds we will wait when bouncing around, * (e.g. a node goes down), which is not the same as a standard timeout. */ @@ -371,9 +373,10 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, /* Attempt to load a named cluster configured in php.ini */ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { - zval *z_seeds, *z_timeout, *z_read_timeout, **z_value; + zval *z_seeds, *z_timeout, *z_read_timeout, *z_persistent, **z_value; char *iptr; double timeout=0, read_timeout=0; + int persistent = 0; HashTable *ht_seeds = NULL; /* Seeds */ @@ -415,8 +418,21 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { } } + /* Persistent connections */ + MAKE_STD_ZVAL(z_persistent); + array_init(z_persistent); + iptr = estrdup(INI_STR("redis.clusters.persistent")); + sapi_module.treat_data(PARSE_STRING, iptr, z_persistent TSRMLS_CC); + if (zend_hash_find(Z_ARRVAL_P(z_persistent), name, name_len+1, (void**)&z_value) != FAILURE) { + if (Z_TYPE_PP(z_value) == IS_STRING) { + persistent = atoi(Z_STRVAL_PP(z_value)); + } else if (Z_TYPE_PP(z_value) == IS_LONG) { + persistent = Z_LVAL_PP(z_value); + } + } + /* Attempt to create/connect to the cluster */ - redis_cluster_init(c, ht_seeds, timeout, read_timeout TSRMLS_CC); + redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent TSRMLS_CC); /* Clean up our arrays */ zval_dtor(z_seeds); @@ -437,13 +453,14 @@ PHP_METHOD(RedisCluster, __construct) { char *name; long name_len; double timeout = 0.0, read_timeout = 0.0; + zend_bool persistent = 0; redisCluster *context = GET_CONTEXT(); // Parse arguments if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|add", &object, redis_cluster_ce, &name, + "Os|addb", &object, redis_cluster_ce, &name, &name_len, &z_seeds, &timeout, - &read_timeout)==FAILURE) + &read_timeout, &persistent)==FAILURE) { RETURN_FALSE; } @@ -458,8 +475,8 @@ PHP_METHOD(RedisCluster, __construct) { /* If we've been passed only one argument, the user is attempting to connect * to a named cluster, stored in php.ini, otherwise we'll need manual seeds */ if (ZEND_NUM_ARGS() > 1) { - redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout - TSRMLS_CC); + redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout, + persistent TSRMLS_CC); } else { redis_cluster_load(context, name, name_len TSRMLS_CC); } diff --git a/redis_session.c b/redis_session.c index b935f34d42..8ba97335da 100644 --- a/redis_session.c +++ b/redis_session.c @@ -497,6 +497,7 @@ PS_OPEN_FUNC(rediscluster) { zval *z_conf, **z_val; HashTable *ht_conf, *ht_seeds; double timeout = 0, read_timeout = 0; + int persistent = 0; int retval, prefix_len, failover = REDIS_FAILOVER_NONE; char *prefix; @@ -554,7 +555,14 @@ PS_OPEN_FUNC(rediscluster) { } } - c = cluster_create(timeout, read_timeout, failover); + /* Check for persistent option */ + if (zend_hash_find(ht_conf, "persistent", sizeof("persistent"), (void **)&z_val) == SUCCESS && + Z_TYPE_PP(z_val) == IS_STRING) + { + persistent = atoi(z_val); + } + + c = cluster_create(timeout, read_timeout, failover, persistent); if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) { /* Set up our prefix */ c->flags->prefix = estrndup(prefix, prefix_len); @@ -671,7 +679,6 @@ PS_DESTROY_FUNC(rediscluster) { /* Attempt to send command */ if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { - if (reply) cluster_free_reply(reply, 1); efree(cmd); return FAILURE; } From 716536452db463b45cd4708873f997a6b1ff100c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 20 Jun 2015 16:05:38 -0700 Subject: [PATCH 0492/1986] Additional cluster based session additions * Initial documentation for cluster based session handler * Updated how we handle persistent option (so we can use 1/yes/true) --- cluster.markdown | 32 +++++++++++++++++++++++++++++++- redis_session.c | 33 ++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/cluster.markdown b/cluster.markdown index 5c40eaf3d8..c7e3567751 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -14,8 +14,15 @@ $obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003 // Connect and specify timeout and read_timeout $obj_cluster = new RedisCluster( - NULL, Array("host:7000", "host:7001", 1.5, 1.5); + NULL, Array("host:7000", "host:7001"), 1.5, 1.5 ); + +// Connect with read/write timeout as well as specify that phpredis should use +// persistent connections to each node. +$obj_cluster = new RedisCluster( + NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true +); + #### Loading a cluster configuration by name @@ -146,3 +153,26 @@ In the case of all commands which need to be directed at a node, the calling con 14. RANDOMKEY 15. PING +## Session Handler +You can use the cluster functionality of phpredis to store PHP session information in a Redis cluster as you can with a non cluster-enabled Redis instance. + +To do this, you must configure your `session.save_handler` and `session.save_path` INI variables to give phpredis enough information to communicate with the cluster. + +~~~ +session.save_handler = rediscluster +session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1" +~~~ + +### session.session_handler +Set this variable to "rediscluster" to inform phpredis that this is a cluster instance. + +### session.save_path +The save path for cluster based session storage takes the form of a PHP GET request, and requires that you specify at least on `seed` node. Other options you can specify are as follows: + +* _timeout (double)_: The amount of time phpredis will wait when connecting or writing to the cluster. +* _read_timeout (double)_: The amount of time phpredis will wait for a result from the cluster. +* _persistent_: Tells phpredis whether persistent connections should be used. +* _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing). +* _failover (string)_: How phpredis should distribute session reads between master and slave nodes. +* * _none_ : phpredis will only communicate with master nodes +* * _error_: phpredis will communicate with master nodes unless one failes, in which case an attempt will be made to read session information from a slave. \ No newline at end of file diff --git a/redis_session.c b/redis_session.c index 8ba97335da..ab74923a09 100644 --- a/redis_session.c +++ b/redis_session.c @@ -477,6 +477,29 @@ static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_le } } +/* Simple helper to retreive a boolean (0 or 1) value from a string stored in our + * session.save_path variable. This is so the user can use 0, 1, or 'true', + * 'false' */ +static void session_conf_bool(HashTable *ht_conf, char *key, int keylen, + int *retval) { + zval **z_val; + char *str; + int strlen; + + /* See if we have the option, and it's a string */ + if (zend_hash_find(ht_conf, key, keylen, (void**)&z_val) == SUCCESS && + Z_TYPE_PP(z_val) == IS_STRING) + { + str = Z_STRVAL_PP(z_val); + strlen = Z_STRLEN_PP(z_val); + + /* true/yes/1 are treated as true. Everything else is false */ + *retval = (strlen == 4 && !strncasecmp(str, "true", 4)) || + (strlen == 3 && !strncasecmp(str, "yes", 3)) || + (strlen == 1 && !strncasecmp(str, "1", 1)); + } +} + /* Prefix a session key */ static char *cluster_session_key(redisCluster *c, const char *key, int keylen, int *skeylen, short *slot) { @@ -524,6 +547,9 @@ PS_OPEN_FUNC(rediscluster) { session_conf_timeout(ht_conf, "timeout", sizeof("timeout"), &timeout); session_conf_timeout(ht_conf, "read_timeout", sizeof("read_timeout"), &read_timeout); + /* Grab persistent option */ + session_conf_bool(ht_conf, "persistent", sizeof("persistent"), &persistent); + /* Sanity check on our timeouts */ if (timeout < 0 || read_timeout < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -555,13 +581,6 @@ PS_OPEN_FUNC(rediscluster) { } } - /* Check for persistent option */ - if (zend_hash_find(ht_conf, "persistent", sizeof("persistent"), (void **)&z_val) == SUCCESS && - Z_TYPE_PP(z_val) == IS_STRING) - { - persistent = atoi(z_val); - } - c = cluster_create(timeout, read_timeout, failover, persistent); if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) { /* Set up our prefix */ From a5477270cc5a351e53b12cae0e5e0bf7eaf33e61 Mon Sep 17 00:00:00 2001 From: Matt Margolin Date: Wed, 8 Jul 2015 22:31:53 -0700 Subject: [PATCH 0493/1986] Update cluster.markdown --- cluster.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster.markdown b/cluster.markdown index c7e3567751..0cbf45f926 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -1,7 +1,7 @@ Redis Cluster ============= -Redis introduces cluster support as of version 3.0.0, and to communicate with a cluster using phpredis one needs to use the RedisCluster class. For the majority of operations the RedisCluster class can act as a drop-in replacement for the Redis class without needing to modify how it's called. **This feature was added as the result of a generous sponsorship by [Tradsey](https://www.tradesy.com/)** +Redis introduces cluster support as of version 3.0.0, and to communicate with a cluster using phpredis one needs to use the RedisCluster class. For the majority of operations the RedisCluster class can act as a drop-in replacement for the Redis class without needing to modify how it's called. **This feature was added as the result of a generous sponsorship by [Tradesy](https://www.tradesy.com/)** ## Creating and connecting to a cluster @@ -175,4 +175,4 @@ The save path for cluster based session storage takes the form of a PHP GET requ * _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing). * _failover (string)_: How phpredis should distribute session reads between master and slave nodes. * * _none_ : phpredis will only communicate with master nodes -* * _error_: phpredis will communicate with master nodes unless one failes, in which case an attempt will be made to read session information from a slave. \ No newline at end of file +* * _error_: phpredis will communicate with master nodes unless one failes, in which case an attempt will be made to read session information from a slave. From 33196716ed667da877fdacc89dbea85d2ba26802 Mon Sep 17 00:00:00 2001 From: Bryan Nelson Date: Mon, 13 Jul 2015 12:01:36 -0500 Subject: [PATCH 0494/1986] Update README.markdown in mget description, corrected value shown in example. --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 417ce46d22..9e59f934bf 100644 --- a/README.markdown +++ b/README.markdown @@ -859,7 +859,7 @@ $redis->set('key1', 'value1'); $redis->set('key2', 'value2'); $redis->set('key3', 'value3'); $redis->mGet(array('key1', 'key2', 'key3')); /* array('value1', 'value2', 'value3'); -$redis->mGet(array('key0', 'key1', 'key5')); /* array(`FALSE`, 'value2', `FALSE`); +$redis->mGet(array('key0', 'key1', 'key5')); /* array(`FALSE`, 'value1', `FALSE`); ~~~ ### getSet From 01b46d0dc96085e2a6872ac9a3d42e50bab6d7da Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 3 Aug 2015 16:26:48 -0700 Subject: [PATCH 0495/1986] Ensure we don't try and send 0 expires for SET As raised by issues #579, and #640 phpredis could be made to silently revert to a "SET" call (no expiry) if a non long argument was specified for EX or PX options, as well as an invalid base timeout value. This modification changes the code so we don't even send the command to Redis in such cases, which also avoids a round trip. --- redis_commands.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 0de5b7710d..7170936641 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1008,7 +1008,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_key, *z_arr, **z_val; + zval *z_arr, **z_val; HashTable *ht_arr; HashPosition pos; smart_str cmdstr = {0}; @@ -1225,23 +1225,31 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_hash_move_forward(kt)) { // Grab key and value - type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, - NULL); + type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL); zend_hash_get_current_data(kt, (void**)&v); - if(type == HASH_KEY_IS_STRING && (Z_TYPE_PP(v) == IS_LONG) && - (Z_LVAL_PP(v) > 0) && IS_EX_PX_ARG(k)) - { + /* Detect PX or EX argument and validate timeout */ + if (type == HASH_KEY_IS_STRING && IS_EX_PX_ARG(k)) { + /* Set expire type */ exp_type = k; - expire = Z_LVAL_PP(v); - } else if(Z_TYPE_PP(v) == IS_STRING && - IS_NX_XX_ARG(Z_STRVAL_PP(v))) - { + + /* Try to extract timeout */ + if (Z_TYPE_PP(v) == IS_LONG) { + expire = Z_LVAL_PP(v); + } else if (Z_TYPE_PP(v) == IS_STRING) { + expire = atol(Z_STRVAL_PP(v)); + } + + /* Expiry can't be set < 1 */ + if (expire < 1) return FAILURE; + } else if (Z_TYPE_PP(v) == IS_STRING && IS_EX_PX_ARG(k)) { set_type = Z_STRVAL_PP(v); } } } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { + /* Grab expiry and fail if it's < 1 */ expire = Z_LVAL_P(z_opts); + if (expire < 1) return FAILURE; } /* Now let's construct the command we want */ From fc673f59c9994bf4a3846ae84e4a91128ecee594 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 3 Aug 2015 21:21:19 -0700 Subject: [PATCH 0496/1986] Don't try and efree a non allocated variable Addresses #638 --- redis.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index 799160b814..04b41f2b82 100644 --- a/redis.c +++ b/redis.c @@ -5621,10 +5621,9 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub } if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&tmp) == SUCCESS) { - type_response = Z_STRVAL_PP(tmp); + type_response = Z_STRVAL_PP(tmp); if(strcmp(type_response, sub_cmd) != 0) { - efree(tmp); - zval_dtor(z_tab); + zval_dtor(z_tab); efree(z_tab); RETURN_FALSE; } From c18d58b97c875df90e8092cc34a1e51e8bad6d87 Mon Sep 17 00:00:00 2001 From: Shafreeck Sea Date: Wed, 5 Aug 2015 03:03:23 +0000 Subject: [PATCH 0497/1986] Work around PHP bug of liveness checking --- library.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/library.c b/library.c index b5f10de24a..6dd5146942 100644 --- a/library.c +++ b/library.c @@ -140,6 +140,21 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) return -1; } + /* NOITCE: set errno = 0 here + * + * There is a bug in php socket stream to check liveness of a connection: + * if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) { + * alive = 0; + * } + * If last errno is EWOULDBLOCK and recv returns 0 because of connection closed, alive would not be + * set to 0. However, the connection is close indeed. The php_stream_eof is not reliable. This will + * cause a "read error on connection" exception when use a closed persistent connection. + * + * We work around this by set errno = 0 first. + * + * Bug fix of php: https://github.com/php/php-src/pull/1456 + * */ + errno = 0; eof = php_stream_eof(redis_sock->stream); for (; eof; count++) { if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { @@ -171,6 +186,7 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) } redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ if(redis_sock->stream) { /* check for EOF again. */ + errno = 0; eof = php_stream_eof(redis_sock->stream); } } From edd777a6b4202580975447abfc95870572795e36 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 30 Sep 2015 21:39:01 -0700 Subject: [PATCH 0498/1986] Initial support for geo commands This commit adds initial support for the new GEO commands. Presently the more complicated GEORADIUS[BYMEMBER] command doesn't do much post-processing of the response, but will probably be modified in the future to return it in a more php friendly way. --- library.h | 6 + php_redis.h | 8 + redis.c | 422 +++++++++++++++++++++++++---------------------- redis_commands.c | 257 ++++++++++++++++++++++++++--- redis_commands.h | 24 +++ 5 files changed, 500 insertions(+), 217 deletions(-) diff --git a/library.h b/library.h index 0d79f78541..b868ca3349 100644 --- a/library.h +++ b/library.h @@ -1,6 +1,12 @@ #ifndef REDIS_LIBRARY_H #define REDIS_LIBRARY_H +#define REDIS_CMD_APPEND_SSTR_STATIC(sstr, str) \ + redis_cmd_append_sstr(sstr, str, sizeof(str)-1); + +#define REDIS_CMD_INIT_SSTR_STATIC(sstr, argc, keyword) \ + redis_cmd_init_sstr(sstr, argc, keyword, sizeof(keyword)-1); + void add_constant_long(zend_class_entry *ce, char *name, int value); int integer_length(int i); int redis_cmd_format(char **ret, char *format, ...); diff --git a/php_redis.h b/php_redis.h index b52fceef2a..c6146a71ee 100644 --- a/php_redis.h +++ b/php_redis.h @@ -197,6 +197,14 @@ PHP_METHOD(Redis, slowlog); PHP_METHOD(Redis, wait); PHP_METHOD(Redis, pubsub); +/* Geoadd and friends */ +PHP_METHOD(Redis, geoadd); +PHP_METHOD(Redis, geohash); +PHP_METHOD(Redis, geopos); +PHP_METHOD(Redis, geodist); +PHP_METHOD(Redis, georadius); +PHP_METHOD(Redis, georadiusbymember); + PHP_METHOD(Redis, client); PHP_METHOD(Redis, command); PHP_METHOD(Redis, rawcommand); diff --git a/redis.c b/redis.c index 7539562630..55b5c3ae09 100644 --- a/redis.c +++ b/redis.c @@ -269,7 +269,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) - + /* SCAN and friends */ PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) @@ -294,6 +294,14 @@ static zend_function_entry redis_functions[] = { /* Send a raw command and read raw results */ PHP_ME(Redis, rawcommand, NULL, ZEND_ACC_PUBLIC) + /* geoadd and friends */ + PHP_ME(Redis, geoadd, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geohash, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geopos, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geodist, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, georadius, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, georadiusbymember, NULL, ZEND_ACC_PUBLIC) + /* introspection */ PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, getPort, NULL, ZEND_ACC_PUBLIC) @@ -372,8 +380,8 @@ PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) zend_class_entry **pce; if (zend_hash_find(CG(class_table), "runtimeexception", - sizeof("RuntimeException"), - (void**)&pce) == SUCCESS) + sizeof("RuntimeException"), + (void**)&pce) == SUCCESS) { spl_ce_RuntimeException = *pce; return *pce; @@ -402,7 +410,7 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { /* send our DISCARD command */ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && - (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) + (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) { /* success if we get OK */ result = (resp_len == 3 && strncmp(resp,"+OK", 3)==0) ? SUCCESS:FAILURE; @@ -431,7 +439,7 @@ static void redis_destructor_redis_sock(zend_rsrc_list_entry * rsrc TSRMLS_DC) /** * redis_sock_get */ -PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, +PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) { @@ -439,23 +447,23 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int resource_type; if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "socket", - sizeof("socket"), (void **) &socket) == FAILURE) + sizeof("socket"), (void **) &socket) == FAILURE) { // Throw an exception unless we've been requested not to if(!no_throw) { - zend_throw_exception(redis_exception_ce, "Redis server went away", + zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); } return -1; } - *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_PP(socket), + *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_PP(socket), &resource_type); if (!*redis_sock || resource_type != le_redis_sock) { // Throw an exception unless we've been requested not to if(!no_throw) { - zend_throw_exception(redis_exception_ce, "Redis server went away", + zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); } return -1; @@ -479,11 +487,11 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) zval *object; RedisSock *redis_sock; - // If we can't grab our object, or get a socket, or we're not connected, + // If we can't grab our object, or get a socket, or we're not connected, // return NULL - if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) || - (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || + (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { return NULL; @@ -493,7 +501,7 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) return redis_sock; } -/* Redis and RedisCluster objects share serialization/prefixing settings so +/* Redis and RedisCluster objects share serialization/prefixing settings so * this is a generic function to add class constants to either */ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) { zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND TSRMLS_CC); @@ -552,7 +560,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) PHP_MINIT_FUNCTION(redis) { struct timeval tv; - + zend_class_entry redis_class_entry; zend_class_entry redis_array_class_entry; zend_class_entry redis_cluster_class_entry; @@ -570,15 +578,15 @@ PHP_MINIT_FUNCTION(redis) redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC); /* RedisArray class */ - INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", + INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions); - redis_array_ce = zend_register_internal_class(&redis_array_class_entry + redis_array_ce = zend_register_internal_class(&redis_array_class_entry TSRMLS_CC); /* RedisCluster class */ - INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", + INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_functions); - redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry + redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry TSRMLS_CC); redis_cluster_ce->create_object = create_cluster_context; @@ -597,10 +605,10 @@ PHP_MINIT_FUNCTION(redis) ); /* RedisClusterException class */ - INIT_CLASS_ENTRY(redis_cluster_exception_class_entry, + INIT_CLASS_ENTRY(redis_cluster_exception_class_entry, "RedisClusterException", NULL); redis_cluster_exception_ce = zend_register_internal_class_ex( - &redis_cluster_exception_class_entry, + &redis_cluster_exception_class_entry, rediscluster_get_exception_base(0 TSRMLS_CC), NULL TSRMLS_CC ); @@ -679,7 +687,7 @@ PHP_METHOD(Redis,__destruct) { // If we think we're in MULTI mode, send a discard if(redis_sock->mode == MULTI) { - // Discard any multi commands, and free any callbacks that have been + // Discard any multi commands, and free any callbacks that have been // queued send_discard_static(redis_sock TSRMLS_CC); free_reply_callbacks(getThis(), redis_sock); @@ -731,28 +739,28 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { RedisSock *redis_sock = NULL; #ifdef ZTS - /* not sure how in threaded mode this works so disabled persistence at + /* not sure how in threaded mode this works so disabled persistence at * first */ persistent = 0; #endif - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|ldsl", &object, redis_ce, &host, + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|ldsl", &object, redis_ce, &host, &host_len, &port, &timeout, &persistent_id, - &persistent_id_len, &retry_interval) - == FAILURE) + &persistent_id_len, &retry_interval) + == FAILURE) { return FAILURE; } if (timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid timeout", + zend_throw_exception(redis_exception_ce, "Invalid timeout", 0 TSRMLS_CC); return FAILURE; } if (retry_interval < 0L || retry_interval > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid retry interval", + zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC); return FAILURE; } @@ -774,7 +782,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { } } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, + redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0); if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { @@ -855,7 +863,7 @@ PHP_METHOD(Redis, setex) */ PHP_METHOD(Redis, psetex) { - REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, + REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, redis_string_response); } @@ -944,7 +952,7 @@ PHP_METHOD(Redis, incrBy){ /* {{{ proto float Redis::incrByFloat(string key, float value) */ PHP_METHOD(Redis, incrByFloat) { - REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, + REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, redis_bulk_double_response); } /* }}} */ @@ -1068,10 +1076,10 @@ PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock) redis_sock->watching = 1; } -PHP_REDIS_API void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_set_watch); } @@ -1088,11 +1096,11 @@ PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock) redis_sock->watching = 0; } -PHP_REDIS_API void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, +PHP_REDIS_API void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_clear_watch); } @@ -1137,7 +1145,7 @@ PHP_METHOD(Redis, getRange) PHP_METHOD(Redis, setRange) { - REDIS_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, + REDIS_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, redis_long_response); } @@ -1183,7 +1191,7 @@ PHP_METHOD(Redis, lInsert) /* {{{ proto long Redis::lPushx(string key, mixed value) */ PHP_METHOD(Redis, lPushx) -{ +{ REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_long_response); } /* }}} */ @@ -1241,7 +1249,7 @@ PHP_METHOD(Redis, lRemove) /* {{{ proto boolean Redis::listTrim(string key , int start , int end) */ PHP_METHOD(Redis, listTrim) { - REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, + REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, redis_boolean_response); } /* }}} */ @@ -1256,7 +1264,7 @@ PHP_METHOD(Redis, lGet) /* {{{ proto array Redis::lGetRange(string key, int start , int end) */ PHP_METHOD(Redis, lGetRange) { - REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, + REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, redis_sock_read_multibulk_reply); } /* }}} */ @@ -1390,7 +1398,7 @@ PHP_METHOD(Redis, sDiffStore) { /* {{{ proto array Redis::sort(string key, array options) */ PHP_METHOD(Redis, sort) { - char *cmd; + char *cmd; int cmd_len, have_store; RedisSock *redis_sock; @@ -1421,8 +1429,8 @@ PHP_METHOD(Redis, sort) { } } -PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, - int use_alpha) +PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, + int use_alpha) { zval *object; @@ -1439,11 +1447,11 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, int sort_len; int i, pos; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|sslls", &object, redis_ce, &key, - &key_len, &pattern, &pattern_len, &get, - &get_len, &sort_start, &sort_count, &store, - &store_len) == FAILURE) + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|sslls", &object, redis_ce, &key, + &key_len, &pattern, &pattern_len, &get, + &get_len, &sort_start, &sort_count, &store, + &store_len) == FAILURE) { RETURN_FALSE; } @@ -1503,18 +1511,18 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_elements++; /* start */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", integer_length(sort_start)); cmd_elements++; - cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, + cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, "%d", (int)sort_start); cmd_elements++; /* count */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", integer_length(sort_count)); cmd_elements++; - cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, + cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, "%d", (int)sort_count); cmd_elements++; } @@ -1528,7 +1536,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_elements++; /* pattern */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", get_len); cmd_elements++; cmd_lines[cmd_elements] = emalloc(get_len + 1); @@ -1540,7 +1548,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, /* add ASC or DESC */ sort_len = strlen(sort); - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", sort_len); cmd_elements++; cmd_lines[cmd_elements] = emalloc(sort_len + 1); @@ -1568,7 +1576,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_elements++; /* store key */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], + cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", store_len); cmd_elements++; cmd_lines[cmd_elements] = emalloc(store_len + 1); @@ -1612,7 +1620,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, } -/* {{{ proto array Redis::sortAsc(string key, string pattern, string get, +/* {{{ proto array Redis::sortAsc(string key, string pattern, string get, * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortAsc) { @@ -1620,7 +1628,7 @@ PHP_METHOD(Redis, sortAsc) } /* }}} */ -/* {{{ proto array Redis::sortAscAlpha(string key, string pattern, string get, +/* {{{ proto array Redis::sortAscAlpha(string key, string pattern, string get, * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortAscAlpha) { @@ -1628,7 +1636,7 @@ PHP_METHOD(Redis, sortAscAlpha) } /* }}} */ -/* {{{ proto array Redis::sortDesc(string key, string pattern, string get, +/* {{{ proto array Redis::sortDesc(string key, string pattern, string get, * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortDesc) { @@ -1636,7 +1644,7 @@ PHP_METHOD(Redis, sortDesc) } /* }}} */ -/* {{{ proto array Redis::sortDescAlpha(string key, string pattern, string get, +/* {{{ proto array Redis::sortDescAlpha(string key, string pattern, string get, * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortDescAlpha) { @@ -1670,7 +1678,7 @@ PHP_METHOD(Redis, pexpireAt) { /* {{{ proto array Redis::lSet(string key, int index, string value) */ PHP_METHOD(Redis, lSet) { - REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, + REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, redis_boolean_response); } /* }}} */ @@ -1750,9 +1758,9 @@ PHP_METHOD(Redis, info) { char *cmd, *opt = NULL; int cmd_len, opt_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "O|s", &object, redis_ce, &opt, &opt_len) - == FAILURE) + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "O|s", &object, redis_ce, &opt, &opt_len) + == FAILURE) { RETURN_FALSE; } @@ -1771,7 +1779,7 @@ PHP_METHOD(Redis, info) { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, + redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_info_response); @@ -1804,7 +1812,7 @@ PHP_METHOD(Redis, select) { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_boolean_response); @@ -1830,7 +1838,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { HashTable *keytable; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", - &object, redis_ce, &z_array) == FAILURE) + &object, redis_ce, &z_array) == FAILURE) { RETURN_FALSE; } @@ -1853,7 +1861,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { cmd_len += kw_len + 2; p = cmd = emalloc(cmd_len + 1); - p += sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1 + 2 * argc, + p += sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1 + 2 * argc, kw_len, kw); } @@ -1871,17 +1879,17 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { int val_free, key_free; char buf[32]; - type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, + type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) - == FAILURE) + if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) + == FAILURE) { /* Should never happen, according to the PHP people */ continue; } - // If the key isn't a string, use the index value returned when - // grabbing it. We typecast to long, because they could actually + // If the key isn't a string, use the index value returned when + // grabbing it. We typecast to long, because they could actually // be negative. if(type != HASH_KEY_IS_STRING) { // Create string representation of our index @@ -1895,7 +1903,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { if(step == 0) argc++; /* found a valid arg */ - val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len + val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len); @@ -1929,7 +1937,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { /* {{{ proto bool Redis::mset(array (key => value, ...)) */ PHP_METHOD(Redis, mset) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", + generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response); } /* }}} */ @@ -1937,7 +1945,7 @@ PHP_METHOD(Redis, mset) { /* {{{ proto bool Redis::msetnx(array (key => value, ...)) */ PHP_METHOD(Redis, msetnx) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", + generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response); } /* }}} */ @@ -1962,8 +1970,8 @@ PHP_METHOD(Redis, zAdd) { /* }}} */ /* Handle ZRANGE and ZREVRANGE as they're the same except for keyword */ -static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, - zrange_cb fun) +static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, + zrange_cb fun) { char *cmd; int cmd_len; @@ -2030,7 +2038,7 @@ PHP_METHOD(Redis, zRevRangeByScore) { /* {{{ proto array Redis::zRangeByLex(string key, string min, string max, [ * offset, limit]) */ PHP_METHOD(Redis, zRangeByLex) { - REDIS_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, + REDIS_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, redis_sock_read_multibulk_reply); } /* }}} */ @@ -2049,7 +2057,7 @@ PHP_METHOD(Redis, zLexCount) { /* {{{ proto long Redis::zRemRangeByLex(string key, string min, string max) */ PHP_METHOD(Redis, zRemRangeByLex) { - REDIS_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, + REDIS_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, redis_long_response); } /* }}} */ @@ -2094,7 +2102,7 @@ PHP_METHOD(Redis, zCard) /* {{{ proto double Redis::zScore(string key, mixed member) */ PHP_METHOD(Redis, zScore) { - REDIS_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, + REDIS_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, redis_bulk_double_response); } /* }}} */ @@ -2230,14 +2238,14 @@ PHP_METHOD(Redis, multi) zval *object; long multi_value = MULTI; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "O|l", &object, redis_ce, &multi_value) - == FAILURE) + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "O|l", &object, redis_ce, &multi_value) + == FAILURE) { RETURN_FALSE; } - /* if the flag is activated, send the command, the reply will be "QUEUED" + /* if the flag is activated, send the command, the reply will be "QUEUED" * or -ERR */ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -2260,8 +2268,8 @@ PHP_METHOD(Redis, multi) } efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) - == NULL) + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) { RETURN_FALSE; } @@ -2298,8 +2306,8 @@ PHP_METHOD(Redis, discard) redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } -PHP_REDIS_API int -redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int +redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zval *z_tab; @@ -2408,8 +2416,8 @@ PHP_METHOD(Redis, exec) efree(cmd); if(redis_sock_read_multibulk_multi_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock) < 0) + INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock) < 0) { zval_dtor(return_value); free_reply_callbacks(object, redis_sock); @@ -2452,15 +2460,15 @@ PHP_METHOD(Redis, exec) } else { redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); - + /* Empty array when no command was run. */ array_init(return_value); return; } if (redis_sock_read_multibulk_pipeline_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock) < 0) + INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock) < 0) { redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); @@ -2486,23 +2494,23 @@ PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { return ret; } -PHP_REDIS_API void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, - RedisSock *redis_sock, zval *z_tab) +PHP_REDIS_API void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, + RedisSock *redis_sock, zval *z_tab) { - item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx + item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx TSRMLS_CC); } -PHP_REDIS_API int +PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, + RedisSock *redis_sock, zval *z_tab, int numElems) { fold_item *head = redis_sock->head; fold_item *current = redis_sock->current; for(current = head; current; current = current->next) { - fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, + fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, z_tab); } redis_sock->current = current; @@ -2519,7 +2527,7 @@ PHP_METHOD(Redis, pipeline) RETURN_FALSE; } - /* if the flag is activated, send the command, the reply will be "QUEUED" + /* if the flag is activated, send the command, the reply will be "QUEUED" * or -ERR */ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -2527,7 +2535,7 @@ PHP_METHOD(Redis, pipeline) redis_sock->mode = PIPELINE; /* NB : we keep the function fold, to detect the last function. - * We need the response format of the n - 1 command. So, we can delete + * We need the response format of the n - 1 command. So, we can delete * when n > 2, the { 1 .. n - 2} commands */ free_reply_callbacks(getThis(), redis_sock); @@ -2544,7 +2552,7 @@ PHP_METHOD(Redis, publish) /* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN)) */ PHP_METHOD(Redis, psubscribe) { - REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, + REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, redis_subscribe_response); } @@ -2566,7 +2574,7 @@ PHP_METHOD(Redis, subscribe) { * ); **/ -PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd) { zval *object, *array, **data; @@ -2629,8 +2637,8 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); if(Z_TYPE_P(z_tab) == IS_ARRAY) { - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) - == FAILURE) + if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) + == FAILURE) { RETURN_FALSE; } @@ -2660,7 +2668,7 @@ PHP_METHOD(Redis, punsubscribe) /* {{{ proto string Redis::bgrewriteaof() */ PHP_METHOD(Redis, bgrewriteaof) { - REDIS_PROCESS_KW_CMD("BGREWRITEAOF", redis_empty_cmd, + REDIS_PROCESS_KW_CMD("BGREWRITEAOF", redis_empty_cmd, redis_boolean_response); } /* }}} */ @@ -2674,9 +2682,9 @@ PHP_METHOD(Redis, slaveof) int cmd_len, host_len; long port = 6379; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "O|sl", &object, redis_ce, &host, - &host_len, &port) == FAILURE) + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "O|sl", &object, redis_ce, &host, + &host_len, &port) == FAILURE) { RETURN_FALSE; } @@ -2694,7 +2702,7 @@ PHP_METHOD(Redis, slaveof) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_boolean_response); @@ -2718,7 +2726,7 @@ PHP_METHOD(Redis, object) RETURN_FALSE; } - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(rtype == TYPE_INT) { IF_ATOMIC() { @@ -2771,9 +2779,9 @@ PHP_METHOD(Redis, config) int key_len, val_len, cmd_len, op_len; enum {CFG_GET, CFG_SET} mode; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Oss|s", &object, redis_ce, &op, &op_len, - &key, &key_len, &val, &val_len) == FAILURE) + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Oss|s", &object, redis_ce, &op, &op_len, + &key, &key_len, &val, &val_len) == FAILURE) { RETURN_FALSE; } @@ -2792,7 +2800,7 @@ PHP_METHOD(Redis, config) } if (mode == CFG_GET && val == NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, + cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) @@ -2828,8 +2836,8 @@ PHP_METHOD(Redis, slowlog) { enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; // Make sure we can get parameters - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|l", &object, redis_ce, &arg, &arg_len, + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|l", &object, redis_ce, &arg, &arg_len, &option) == FAILURE) { RETURN_FALSE; @@ -2852,7 +2860,7 @@ PHP_METHOD(Redis, slowlog) { RETURN_FALSE; } - // Create our command. For everything except SLOWLOG GET (with an arg) it's + // Create our command. For everything except SLOWLOG GET (with an arg) it's // just two parts if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) { cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl", arg, @@ -2865,8 +2873,8 @@ PHP_METHOD(Redis, slowlog) { /* Kick off our command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -2907,7 +2915,7 @@ PHP_METHOD(Redis, wait) { /* Kick it off */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, + redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_long_response); @@ -2952,7 +2960,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, ht_chan = Z_ARRVAL_P(arg); // Add PUBSUB and NUMSUB bits - redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", + redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1); redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1); @@ -3021,8 +3029,8 @@ PHP_METHOD(Redis, pubsub) { zval *arg=NULL; // Parse arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|z", &object, redis_ce, &keyword, + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|z", &object, redis_ce, &keyword, &kw_len, &arg)==FAILURE) { RETURN_FALSE; @@ -3062,8 +3070,8 @@ PHP_METHOD(Redis, pubsub) { if(type == PUBSUB_NUMSUB) { IF_ATOMIC() { - if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) + if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL)<0) { RETURN_FALSE; } @@ -3071,8 +3079,8 @@ PHP_METHOD(Redis, pubsub) { REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_int); } else { IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL)<0) { RETURN_FALSE; } @@ -3081,12 +3089,12 @@ PHP_METHOD(Redis, pubsub) { } } -// Construct an EVAL or EVALSHA command, with option argument array and number +// Construct an EVAL or EVALSHA command, with option argument array and number // of arguments that are keys parameter PHP_REDIS_API int -redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, - char *value, int val_len, zval *args, int keys_count - TSRMLS_DC) +redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, + char *value, int val_len, zval *args, int keys_count + TSRMLS_DC) { zval **elem; HashTable *args_hash; @@ -3094,7 +3102,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, int cmd_len, args_count = 0; int eval_cmd_count = 2; - // If we've been provided arguments, we'll want to include those in our eval + // If we've been provided arguments, we'll want to include those in our eval // command if(args != NULL) { // Init our hash array value, and grab the count @@ -3104,10 +3112,10 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, // We only need to process the arguments if the array is non empty if(args_count > 0) { // Header for our EVAL command - cmd_len = redis_cmd_format_header(ret, keyword, + cmd_len = redis_cmd_format_header(ret, keyword, eval_cmd_count + args_count); - // Now append the script itself, and the number of arguments to + // Now append the script itself, and the number of arguments to // treat as keys cmd_len = redis_cmd_append_str(ret, cmd_len, value, val_len); cmd_len = redis_cmd_append_int(ret, cmd_len, keys_count); @@ -3139,13 +3147,13 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, /* Keep track of the old command pointer */ old_cmd = *ret; - // If this is still a key argument, prefix it if we've been set + // If this is still a key argument, prefix it if we've been set // up to prefix keys - key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, + key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len) : 0; // Append this key to our EVAL command, free our old command - cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, + cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, cmd_len, key_len, key, key_len); efree(old_cmd); @@ -3161,7 +3169,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, } } - // If there weren't any arguments (none passed, or an empty array), + // If there weren't any arguments (none passed, or an empty array), // construct a standard no args command if(args_count < 1) { cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, @@ -3181,9 +3189,9 @@ PHP_METHOD(Redis, evalsha) long keys_count = 0; RedisSock *redis_sock; - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|al", &object, redis_ce, &sha, &sha_len, - &args, &keys_count) == FAILURE) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|al", &object, redis_ce, &sha, &sha_len, + &args, &keys_count) == FAILURE) { RETURN_FALSE; } @@ -3194,13 +3202,13 @@ PHP_METHOD(Redis, evalsha) } // Construct our EVALSHA command - cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVALSHA", sha, sha_len, + cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVALSHA", sha, sha_len, args, keys_count TSRMLS_CC); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -3218,10 +3226,10 @@ PHP_METHOD(Redis, eval) long keys_count = 0; // Attempt to parse parameters - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|al", &object, redis_ce, &script, - &script_len, &args, &keys_count) - == FAILURE) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|al", &object, redis_ce, &script, + &script_len, &args, &keys_count) + == FAILURE) { RETURN_FALSE; } @@ -3232,13 +3240,13 @@ PHP_METHOD(Redis, eval) } // Construct our EVAL command - cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVAL", script, script_len, + cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVAL", script, script_len, args, keys_count TSRMLS_CC); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -3261,7 +3269,7 @@ redis_build_script_exists_cmd(char **ret, zval **argv, int argc) { convert_to_string(argv[i]); // Append this script sha to our SCRIPT EXISTS command - cmd_len = redis_cmd_append_str(ret, cmd_len, Z_STRVAL_P(argv[i]), + cmd_len = redis_cmd_append_str(ret, cmd_len, Z_STRVAL_P(argv[i]), Z_STRLEN_P(argv[i])); } @@ -3300,8 +3308,8 @@ PHP_METHOD(Redis, script) { } // Branch based on the directive - if(!strcasecmp(Z_STRVAL_P(z_args[0]), "flush") || - !strcasecmp(Z_STRVAL_P(z_args[0]), "kill")) + if(!strcasecmp(Z_STRVAL_P(z_args[0]), "flush") || + !strcasecmp(Z_STRVAL_P(z_args[0]), "kill")) { // Simple SCRIPT FLUSH, or SCRIPT_KILL command cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "s", @@ -3310,8 +3318,8 @@ PHP_METHOD(Redis, script) { } else if(!strcasecmp(Z_STRVAL_P(z_args[0]), "load")) { // Make sure we have a second argument, and it's not empty. If it is // empty, we can just return an empty array (which is what Redis does) - if(argc < 2 || Z_TYPE_P(z_args[1]) != IS_STRING || - Z_STRLEN_P(z_args[1]) < 1) + if(argc < 2 || Z_TYPE_P(z_args[1]) != IS_STRING || + Z_STRLEN_P(z_args[1]) < 1) { // Free our args efree(z_args); @@ -3337,8 +3345,8 @@ PHP_METHOD(Redis, script) { // Kick off our request REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) + if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -3354,7 +3362,7 @@ PHP_METHOD(Redis, dump) { /* {{{ proto Redis::restore(ttl, key, value) */ PHP_METHOD(Redis, restore) { - REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_val_cmd, + REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_val_cmd, redis_boolean_response); } /* }}} */ @@ -3365,7 +3373,7 @@ PHP_METHOD(Redis, debug) { } /* }}} */ -/* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, +/* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, * bool replace]) */ PHP_METHOD(Redis, migrate) { zval *object; @@ -3376,10 +3384,10 @@ PHP_METHOD(Redis, migrate) { long port, dest_db, timeout; // Parse arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Oslsll|bb", &object, redis_ce, &host, - &host_len, &port, &key, &key_len, &dest_db, - &timeout, ©, &replace) == FAILURE) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Oslsll|bb", &object, redis_ce, &host, + &host_len, &port, &key, &key_len, &dest_db, + &timeout, ©, &replace) == FAILURE) { RETURN_FALSE; } @@ -3421,7 +3429,7 @@ PHP_METHOD(Redis, migrate) { // Kick off our MIGRATE request REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_boolean_response); @@ -3453,7 +3461,7 @@ PHP_METHOD(Redis, _serialize) { /* {{{ proto Redis::_unserialize(value) */ PHP_METHOD(Redis, _unserialize) { RedisSock *redis_sock; - + // Grab socket if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -3469,12 +3477,12 @@ PHP_METHOD(Redis, getLastError) { RedisSock *redis_sock; // Grab our object - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_ce) == FAILURE) { RETURN_FALSE; } - + // Grab socket if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -3494,8 +3502,8 @@ PHP_METHOD(Redis, clearLastError) { RedisSock *redis_sock; // Grab our object - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_ce) == FAILURE) { RETURN_FALSE; } @@ -3658,8 +3666,8 @@ PHP_METHOD(Redis, client) { int cmd_len, opt_len, arg_len; // Parse our method parameters - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|s", &object, redis_ce, &opt, &opt_len, + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Os|s", &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE) { RETURN_FALSE; @@ -3718,7 +3726,7 @@ PHP_METHOD(Redis, rawcommand) { efree(z_args); RETURN_FALSE; } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len TSRMLS_CC) < 0 || - redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) + redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { if (cmd) efree(cmd); efree(z_args); @@ -3787,14 +3795,14 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, /* Append COUNT if we've got it */ if(count) { - cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", + cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", sizeof("COUNT")-1); cmd_len = redis_cmd_append_int(cmd, cmd_len, count); } /* Append MATCH if we've got it */ if(pattern_len) { - cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", + cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", sizeof("MATCH")-1); cmd_len = redis_cmd_append_str(cmd, cmd_len, pattern, pattern_len); } @@ -3816,18 +3824,18 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Different prototype depending on if this is a key based scan */ if(type != TYPE_SCAN) { // Requires a key - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Osz/|s!l", &object, redis_ce, &key, - &key_len, &z_iter, &pattern, + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Osz/|s!l", &object, redis_ce, &key, + &key_len, &z_iter, &pattern, &pattern_len, &count)==FAILURE) { RETURN_FALSE; } } else { // Doesn't require a key - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Oz/|s!l", &object, redis_ce, &z_iter, - &pattern, &pattern_len, &count) + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "Oz/|s!l", &object, redis_ce, &z_iter, + &pattern, &pattern_len, &count) == FAILURE) { RETURN_FALSE; @@ -3841,13 +3849,13 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Calling this in a pipeline makes no sense */ IF_NOT_ATOMIC() { - php_error_docref(NULL TSRMLS_CC, E_ERROR, + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't call SCAN commands in multi or pipeline mode!"); RETURN_FALSE; } // The iterator should be passed in as NULL for the first iteration, but we - // can treat any NON LONG value as NULL for these purposes as we've + // can treat any NON LONG value as NULL for these purposes as we've // seperated the variable anyway. if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter)<0) { /* Convert to long */ @@ -3867,11 +3875,11 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } /** - * Redis can return to us empty keys, especially in the case where there - * are a large number of keys to scan, and we're matching against a - * pattern. phpredis can be set up to abstract this from the user, by - * setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty - * keys and the user will need to make subsequent calls with an updated + * Redis can return to us empty keys, especially in the case where there + * are a large number of keys to scan, and we're matching against a + * pattern. phpredis can be set up to abstract this from the user, by + * setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty + * keys and the user will need to make subsequent calls with an updated * iterator. */ do { @@ -3898,7 +3906,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Get the number of elements */ hash = Z_ARRVAL_P(return_value); num_elements = zend_hash_num_elements(hash); - } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && + } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && num_elements == 0); /* Free our key if it was prefixed */ @@ -3921,8 +3929,8 @@ PHP_METHOD(Redis, zscan) { generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN); } -/* - * HyperLogLog based commands +/* + * HyperLogLog based commands */ /* {{{ proto Redis::pfAdd(string key, array elements) }}} */ @@ -3940,4 +3948,32 @@ PHP_METHOD(Redis, pfmerge) { REDIS_PROCESS_CMD(pfmerge, redis_boolean_response); } +/* + * Geo commands + */ + +PHP_METHOD(Redis, geoadd) { + REDIS_PROCESS_KW_CMD("GEOADD", redis_key_varval_cmd, redis_long_response); +} + +PHP_METHOD(Redis, geohash) { + REDIS_PROCESS_KW_CMD("GEOHASH", redis_key_varval_cmd, redis_mbulk_reply_raw); +} + +PHP_METHOD(Redis, geopos) { + REDIS_PROCESS_KW_CMD("GEOPOS", redis_key_varval_cmd, redis_read_variant_reply); +} + +PHP_METHOD(Redis, geodist) { + REDIS_PROCESS_CMD(geodist, redis_bulk_double_response); +} + +PHP_METHOD(Redis, georadius) { + REDIS_PROCESS_CMD(georadius, redis_read_variant_reply); +} + +PHP_METHOD(Redis, georadiusbymember) { + REDIS_PROCESS_CMD(georadiusbymember, redis_read_variant_reply); +} + /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ diff --git a/redis_commands.c b/redis_commands.c index 7170936641..90be1c1160 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -41,7 +41,7 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Helper to construct a raw command. Given that the cluster and non cluster * versions are different (RedisCluster needs an additional argument to direct * the command) we take the start of our array and count */ -int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC) +int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC) { smart_str cmdstr = {0}; int i; @@ -889,10 +889,10 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Construct command */ if(argc == 3) { - *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, + *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, min_len, max, max_len); } else { - *cmd_len = redis_cmd_format_static(cmd, kw, "ssssll", key, key_len, min, + *cmd_len = redis_cmd_format_static(cmd, kw, "ssssll", key, key_len, min, min_len, max, max_len, "LIMIT", sizeof("LIMIT")-1, offset, count); } @@ -907,7 +907,7 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* ZLEXCOUNT/ZREMRANGEBYLEX */ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *min, *max; @@ -933,7 +933,7 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Construct command */ - *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, + *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, min_len, max, max_len); /* set slot */ @@ -1015,7 +1015,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_len, val_len, key_free, val_free, argc = 1; char *key, *val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, &z_arr) == FAILURE || zend_hash_num_elements(Z_ARRVAL_P(z_arr)) == 0) { @@ -1026,7 +1026,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ht_arr = Z_ARRVAL_P(z_arr); argc += zend_hash_num_elements(ht_arr); redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - + /* Prefix if required and append the key name */ key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); @@ -1333,16 +1333,16 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -/* To maintain backward compatibility with earlier versions of phpredis, we +/* To maintain backward compatibility with earlier versions of phpredis, we * allow for an optional "increment by" argument for INCR and DECR even though * that's not how Redis proper works */ #define TYPE_INCR 0 #define TYPE_DECR 1 /* Handle INCR(BY) and DECR(BY) depending on optional increment value */ -static int -redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, - RedisSock *redis_sock, char **cmd, int *cmd_len, +static int +redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, + RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; @@ -1383,7 +1383,7 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, /* Success */ return SUCCESS; } - + /* INCR */ int redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -1499,8 +1499,8 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_hash_move_forward_ex(ht_arr, &ptr)) { // We can only handle string or long values here - if ((Z_TYPE_PP(z_mem)==IS_STRING && Z_STRLEN_PP(z_mem)>0) - || Z_TYPE_PP(z_mem)==IS_LONG) + if ((Z_TYPE_PP(z_mem)==IS_STRING && Z_STRLEN_PP(z_mem)>0) + || Z_TYPE_PP(z_mem)==IS_LONG) { // Copy into our member array MAKE_STD_ZVAL(z_mems[valid]); @@ -1930,7 +1930,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Initialize the command with our number of arguments */ redis_cmd_init_sstr(&cmdstr, num_keys, "PFCOUNT", sizeof("PFCOUNT")-1); - + /* Append our key(s) */ for (zend_hash_internal_pointer_reset_ex(ht_keys, &ptr); zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &ptr)==SUCCESS; @@ -1953,7 +1953,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Append this key to our command */ key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); - + /* Protect against CROSSLOT errors */ if (slot) { if (kslot == -1) { @@ -1965,7 +1965,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, efree(z_tmp); } efree(cmdstr.c); - + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not all keys hash to the same slot!"); return FAILURE; @@ -2723,6 +2723,215 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* GEODIST */ +int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *source, *dest, *unit = NULL; + int keylen, sourcelen, destlen, unitlen; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|s", &key, &keylen, + &source, &sourcelen, &dest, &destlen, &unit, + &unitlen) == FAILURE) + { + return FAILURE; + } + + /* Construct command */ + if (unit != NULL) { + *cmd_len = redis_cmd_format_static(cmd, "GEODIST", "ssss", key, keylen, + source, sourcelen, dest, destlen, unit, unitlen); + } else { + *cmd_len = redis_cmd_format_static(cmd, "GEODIST", "sss", key, keylen, + source, sourcelen, dest, destlen); + } + + /* Set slot */ + CMD_SET_SLOT(slot, key, keylen); + + return SUCCESS; +} + +/* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */ +static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, + int *withhash, long *count, geoSortType *sort) +{ + int type; + unsigned long idx; + unsigned int optkeylen; + char *optkey, *optstr; + zval **optval; + + /* Iterate over our argument array, collating which ones we have */ + for (zend_hash_internal_pointer_reset(ht); + zend_hash_has_more_elements(ht) == SUCCESS; + zend_hash_move_forward(ht)) + { + /* Grab key and value */ + type = zend_hash_get_current_key_ex(ht, &optkey, &optkeylen, &idx, 0, NULL); + zend_hash_get_current_data(ht, (void**)&optval); + + /* If the key is numeric it's a non value option */ + if (type == HASH_KEY_IS_LONG) { + /* Option needs to be a string */ + if (Z_TYPE_PP(optval) != IS_STRING) continue; + + optstr = Z_STRVAL_PP(optval); + + if (!strcasecmp(optstr, "withcoord")) { + *withcoord = 1; + } else if (!strcasecmp(optstr, "withdist")) { + *withdist = 1; + } else if (!strcasecmp(optstr, "withhash")) { + *withhash = 1; + } + } else if (!strcasecmp(optkey, "count") && Z_TYPE_PP(optval) == IS_LONG) { + *count = Z_LVAL_PP(optval); + } + } +} + +/* Helper to append options to a GEORADIUS or GEORADIUSBYMEMBER command */ +void append_georadius_opts(smart_str *str, int withcoord, int withdist, + int withhash, long count, geoSortType sort) +{ + /* WITHCOORD option */ + if (withcoord) + REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHCOORD"); + + /* WITHDIST option */ + if (withdist) + REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHDIST"); + + /* WITHHASH option */ + if (withhash) + REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHHASH"); + + /* Append sort if it's not GEO_NONE */ + if (sort == SORT_ASC) { + REDIS_CMD_APPEND_SSTR_STATIC(str, "ASC"); + } else if (sort == SORT_DESC) { + REDIS_CMD_APPEND_SSTR_STATIC(str, "DESC"); + } + + /* Append our count if we've got one */ + if (count > 0) { + REDIS_CMD_APPEND_SSTR_STATIC(str, "COUNT"); + redis_cmd_append_sstr_long(str, count); + } +} + +/* GEORADIUS */ +int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *unit; + int keylen, keyfree, unitlen; + int withcoord = 0, withdist = 0, withhash = 0; + long count = 0; + geoSortType sort = SORT_NONE; + double lng, lat, radius; + zval *opts = NULL; + smart_str cmdstr = {0}; + int argc; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sddds|a", &key, &keylen, + &lng, &lat, &radius, &unit, &unitlen, &opts) + == FAILURE) + { + return FAILURE; + } + + /* Parse any GEORADIUS options we have */ + if (opts != NULL) { + get_georadius_opts(Z_ARRVAL_P(opts), &withcoord, &withdist, &withhash, + &count, &sort); + } + + /* Calculate the number of arguments we're going to send, five required plus + * options. */ + argc = 5 + withcoord + withdist + withhash + (sort != SORT_NONE); + if (count != 0) argc += 2; + + /* Begin construction of our command */ + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUS"); + + /* Apply any key prefix */ + keyfree = redis_key_prefix(redis_sock, &key, &keylen); + + /* Append required arguments */ + redis_cmd_append_sstr(&cmdstr, key, keylen); + redis_cmd_append_sstr_dbl(&cmdstr, lng); + redis_cmd_append_sstr_dbl(&cmdstr, lat); + redis_cmd_append_sstr_dbl(&cmdstr, radius); + redis_cmd_append_sstr(&cmdstr, unit, unitlen); + + /* Append optional arguments */ + append_georadius_opts(&cmdstr, withcoord, withdist, withhash, count, sort); + + /* Free key if it was prefixed */ + if (keyfree) efree(key); + + /* Set slot, command and len, and return */ + CMD_SET_SLOT(slot, key, keylen); + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +/* GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] */ +int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *mem, *unit; + int keylen, keyfree, memlen, unitlen, argc; + int withcoord = 0, withdist = 0, withhash = 0; + long count = 0; + double radius; + geoSortType sort = SORT_NONE; + zval *opts = NULL; + smart_str cmdstr = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssds|a", &key, &keylen, + &mem, &memlen, &radius, &unit, &unitlen, &opts) == FAILURE) + { + return FAILURE; + } + + if (opts != NULL) { + get_georadius_opts(Z_ARRVAL_P(opts), &withcoord, &withdist, &withhash, &count, &sort); + } + + /* Calculate argc */ + argc = 4 + withcoord + withdist + withhash + (sort != SORT_NONE); + if (count != 0) argc += 2; + + /* Begin command construction*/ + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUSBYMEMBER"); + + /* Prefix our key if we're prefixing */ + keyfree = redis_key_prefix(redis_sock, &key, &keylen); + + /* Append required arguments */ + redis_cmd_append_sstr(&cmdstr, key, keylen); + redis_cmd_append_sstr(&cmdstr, mem, memlen); + redis_cmd_append_sstr_long(&cmdstr, radius); + redis_cmd_append_sstr(&cmdstr, unit, unitlen); + + /* Append options */ + append_georadius_opts(&cmdstr, withcoord, withdist, withhash, count, sort); + + /* Free key if we prefixed */ + if (keyfree) efree(key); + + CMD_SET_SLOT(slot, key, keylen); + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* DEL */ int redis_del_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -2812,8 +3021,8 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int kw_len; /* Parse our args */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sz", &kw, &kw_len, - &z_arg)==FAILURE) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sz", &kw, &kw_len, + &z_arg)==FAILURE) { return FAILURE; } @@ -2823,7 +3032,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd_len = redis_cmd_format_static(cmd, "COMMAND", ""); } else if(kw && !z_arg) { /* Sanity check */ - if(strncasecmp(kw, "info", sizeof("info")-1) || + if(strncasecmp(kw, "info", sizeof("info")-1) || Z_TYPE_P(z_arg)!=IS_STRING) { return FAILURE; @@ -2837,12 +3046,12 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Sanity check on args */ if(strncasecmp(kw, "getkeys", sizeof("getkeys")-1) || - Z_TYPE_P(z_arg)!=IS_ARRAY || + Z_TYPE_P(z_arg)!=IS_ARRAY || (arr_len=zend_hash_num_elements(Z_ARRVAL_P(z_arg)))<1) { return FAILURE; } - + zval **z_ele; HashTable *ht_arr = Z_ARRVAL_P(z_arg); smart_str cmdstr = {0}; @@ -2968,7 +3177,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, break; case REDIS_OPT_FAILOVER: val_long = atol(val_str); - if (val_long == REDIS_FAILOVER_NONE || + if (val_long == REDIS_FAILOVER_NONE || val_long == REDIS_FAILOVER_ERROR || val_long == REDIS_FAILOVER_DISTRIBUTE) { @@ -2976,7 +3185,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_TRUE; } else { RETURN_FALSE; - } + } default: RETURN_FALSE; } diff --git a/redis_commands.h b/redis_commands.h index d3950e92f0..2bcd889393 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -21,6 +21,21 @@ typedef struct subscribeContext { zend_fcall_info_cache cb_cache; } subscribeContext; +/* Georadius sort type */ +typedef enum geoSortType { + SORT_NONE, + SORT_ASC, + SORT_DESC +} geoSortType; + +/* GEORADIUS and GEORADIUSBYMEMBER options */ +/*typedef struct geoRadiusOpts = { + int withcoord; + int withdist; + int withhash; + geoSortType sort; +};*/ + /* Construct a raw command */ int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC); @@ -224,6 +239,15 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); +int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands that don't communicate with Redis at all (such as getOption, * setOption, _prefix, _serialize, etc). These can be handled in one place * with the method of grabbing our RedisSock* object in different ways From 0b7f6d2ba5362e717ca0d225b78c5865b3f5024d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 3 Nov 2015 20:48:23 -0800 Subject: [PATCH 0499/1986] Added a new failover option, which excludes masters for readonly failover --- cluster_library.c | 30 ++++++++++++++++++++++-------- common.h | 10 +++++----- redis.c | 1 + redis_commands.c | 3 ++- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 24e656e8f0..42684dc73a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1072,10 +1072,12 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, int i, count=1, *nodes; RedisSock *redis_sock; - /* Allocate enough memory for the master and all of our slaves */ + /* Determine our overall node count */ if (c->master[c->cmd_slot]->slaves) { count += zend_hash_num_elements(c->master[c->cmd_slot]->slaves); } + + /* Allocate memory for master + slaves or just slaves */ nodes = emalloc(sizeof(int)*count); /* Populate our array with the master and each of it's slaves, then @@ -1084,7 +1086,11 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, fyshuffle(nodes, count); /* Iterate through our nodes until we find one we can write to or fail */ - for (i = nomaster; i < count; i++) { + for (i = 0; i < count; i++) { + /* Skip if this is the master node and we don't want to query that */ + if (nomaster && nodes[i] == 0) + continue; + /* Get the slave for this index */ redis_sock = cluster_slot_sock(c, c->cmd_slot, nodes[i]); if (!redis_sock) continue; @@ -1092,7 +1098,7 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, /* Connect to this node if we haven't already */ CLUSTER_LAZY_CONNECT(redis_sock); - /* If we're not on the master, attempt to send the READONLY commadn to + /* If we're not on the master, attempt to send the READONLY command to * this slave, and skip it if that fails */ if (nodes[i] == 0 || redis_sock->readonly || cluster_send_readonly(redis_sock TSRMLS_CC) == 0) @@ -1127,7 +1133,10 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, * If we're unable to communicate with this slot's master, we attempt the query * against any slaves (at random) that this master has. * REDIS_FAILOVER_DISTRIBUTE: - * We pick at random from the master and any slaves it has. This option is + * We pick at random from the master and any slaves it has. This option will + * load balance between masters and slaves + * REDIS_FAILOVER_DISTRIBUTE_SLAVES: + * We pick at random from slave nodes of a given master. This option is * used to load balance read queries against N slaves. * * Once we are able to find a node we can write to, we check for MOVED or @@ -1138,7 +1147,7 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, { redisClusterNode **seed_node; RedisSock *redis_sock; - int failover; + int failover, nomaster; /* First try the socket requested */ redis_sock = c->cmd_sock; @@ -1170,9 +1179,14 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, CLUSTER_LAZY_CONNECT(redis_sock); if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz) || !cluster_dist_write(c, cmd, sz, 1 TSRMLS_CC)) return 0; - } else if (!cluster_dist_write(c, cmd, sz, 0 TSRMLS_CC)) { - /* We were able to write to a master or slave at random */ - return 0; + } else { + /* Include or exclude master node depending on failover option and + * attempt to make our write */ + nomaster = failover == REDIS_FAILOVER_DISTRIBUTE_SLAVES; + if (!cluster_dist_write(c, cmd, sz, nomaster TSRMLS_CC)) { + /* We were able to write to a master or slave at random */ + return 0; + } } /* Don't fall back if direct communication with this slot is required. */ diff --git a/common.h b/common.h index 0809b3d25e..ed090a7ac0 100644 --- a/common.h +++ b/common.h @@ -66,11 +66,11 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_SCAN 4 /* cluster options */ -#define REDIS_OPT_FAILOVER 5 -#define REDIS_FAILOVER_NONE 0 -#define REDIS_FAILOVER_ERROR 1 -#define REDIS_FAILOVER_DISTRIBUTE 2 - +#define REDIS_OPT_FAILOVER 5 +#define REDIS_FAILOVER_NONE 0 +#define REDIS_FAILOVER_ERROR 1 +#define REDIS_FAILOVER_DISTRIBUTE 2 +#define REDIS_FAILOVER_DISTRIBUTE_SLAVES 3 /* serializers */ #define REDIS_SERIALIZER_NONE 0 #define REDIS_SERIALIZER_PHP 1 diff --git a/redis.c b/redis.c index 7539562630..a3c13455c3 100644 --- a/redis.c +++ b/redis.c @@ -532,6 +532,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_NONE"), REDIS_FAILOVER_NONE TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_ERROR"), REDIS_FAILOVER_ERROR TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES TSRMLS_CC); } #ifdef HAVE_REDIS_IGBINARY zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); diff --git a/redis_commands.c b/redis_commands.c index 7170936641..ff0e169e53 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2970,7 +2970,8 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, val_long = atol(val_str); if (val_long == REDIS_FAILOVER_NONE || val_long == REDIS_FAILOVER_ERROR || - val_long == REDIS_FAILOVER_DISTRIBUTE) + val_long == REDIS_FAILOVER_DISTRIBUTE || + val_long == REDIS_FAILOVER_DISTRIBUTE_SLAVES) { c->failover = val_long; RETURN_TRUE; From a8ae53f45ba25f0ea14f677d21aec4322123eb7e Mon Sep 17 00:00:00 2001 From: Ares Date: Thu, 5 Nov 2015 13:37:03 +0800 Subject: [PATCH 0500/1986] Update README.markdown --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 417ce46d22..169ebfd8b6 100644 --- a/README.markdown +++ b/README.markdown @@ -1653,7 +1653,7 @@ while($arr_keys = $redis->hscan('hash', $it)) { ### blPop, brPop ----- _**Description**_: Is a blocking lPop(rPop) primitive. If at least one of the lists contains at least one element, the element will be popped from the head of the list and returned to the caller. -Il all the list identified by the keys passed in arguments are empty, blPop will block during the specified timeout until an element is pushed to one of those lists. This element will be popped. +If all the list identified by the keys passed in arguments are empty, blPop will block during the specified timeout until an element is pushed to one of those lists. This element will be popped. ##### *Parameters* *ARRAY* Array containing the keys of the lists From 46ae5ba211f5bd3333066cbc910a684f6c0bd14a Mon Sep 17 00:00:00 2001 From: sanpili Date: Wed, 22 Jul 2015 09:43:18 +0000 Subject: [PATCH 0501/1986] fix alignment --- library.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/library.c b/library.c index 6dd5146942..546bb40fe4 100644 --- a/library.c +++ b/library.c @@ -171,19 +171,19 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) 0 TSRMLS_CC); } return -1; - } - if(redis_sock->stream) { /* close existing stream before reconnecting */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; - } - // Wait for a while before trying to reconnect - if (redis_sock->retry_interval) { - // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time - long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); - usleep(retry_interval); - } + } + if(redis_sock->stream) { /* close existing stream before reconnecting */ + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; + } + // Wait for a while before trying to reconnect + if (redis_sock->retry_interval) { + // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time + long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); + usleep(retry_interval); + } redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ if(redis_sock->stream) { /* check for EOF again. */ errno = 0; From 974681ce64c68e2f5fb8af7eb2903b619bb797cd Mon Sep 17 00:00:00 2001 From: Patrick Pokatilo Date: Mon, 13 Jul 2015 03:29:19 +0200 Subject: [PATCH 0502/1986] ZRANGEBYLEX also allows '-' or '+' as either min or max --- redis_commands.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index ff0e169e53..62467f3910 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -875,9 +875,12 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - /* min and max must start with '(' or '[' */ - if(min_len < 1 || max_len < 1 || (min[0] != '(' && min[0] != '[') || - (max[0] != '(' && max[0] != '[')) + /* min and max must start with '(' or '[', or be either '-' or '+' */ + if(min_len < 1 || max_len < 1 || + (min[0] != '(' && min[0] != '[' && + (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || + (max[0] != '(' && max[0] != '[' && + (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) { php_error_docref(0 TSRMLS_CC, E_WARNING, "min and max arguments must start with '[' or '('"); From c432c40c04f938faa8942522197616c9bfec189e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 18 Nov 2015 09:38:38 -0800 Subject: [PATCH 0503/1986] Fix extended set option detection --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 62467f3910..83e7a6e218 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1245,7 +1245,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Expiry can't be set < 1 */ if (expire < 1) return FAILURE; - } else if (Z_TYPE_PP(v) == IS_STRING && IS_EX_PX_ARG(k)) { + } else if (Z_TYPE_PP(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_PP(v))) { set_type = Z_STRVAL_PP(v); } } From 979555df1b757828355c3650876300187a341c1b Mon Sep 17 00:00:00 2001 From: Romero Malaquias Date: Mon, 14 Dec 2015 15:14:56 -0300 Subject: [PATCH 0504/1986] Avoiding conditional directives that split up statements. --- redis_commands.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 83e7a6e218..d3f09255dc 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2913,7 +2913,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, { long option, val_long; char *val_str; - int val_len; + int val_len, test_val; struct timeval read_tv; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &option, @@ -2925,11 +2925,11 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, switch(option) { case REDIS_OPT_SERIALIZER: val_long = atol(val_str); - if(val_long == REDIS_SERIALIZER_NONE + test_val = val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP; #ifdef HAVE_REDIS_IGBINARY - || val_long == REDIS_SERIALIZER_IGBINARY + test_val = test_val || val_long == REDIS_SERIALIZER_IGBINARY; #endif - || val_long == REDIS_SERIALIZER_PHP) + if(test_val) { redis_sock->serializer = val_long; RETURN_TRUE; From e8c3ef997682da3dad958c3079d7329ceca767f2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 17 Dec 2015 19:34:01 -0800 Subject: [PATCH 0505/1986] Fix MOVED redirection bug Addresses #690 and possibly #673 --- cluster_library.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 42684dc73a..c6dfdfc002 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -846,7 +846,7 @@ PHP_REDIS_API void cluster_free(redisCluster *c) { if (c->err) efree(c->err); /* Free structure itself */ - efree(c); + efree(c); } /* Initialize seeds */ @@ -1089,7 +1089,7 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, for (i = 0; i < count; i++) { /* Skip if this is the master node and we don't want to query that */ if (nomaster && nodes[i] == 0) - continue; + continue; /* Get the slave for this index */ redis_sock = cluster_slot_sock(c, c->cmd_slot, nodes[i]); @@ -1240,6 +1240,8 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, * the slot where data was moved, update our node mapping */ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { redisClusterNode *node; + char key[1024]; + size_t klen; /* Do we already have the new slot mapped */ if(c->master[c->redir_slot]) { @@ -1259,6 +1261,11 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { node = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); + /* Our node is new, so keep track of it for cleanup */ + klen = snprintf(key,sizeof(key),"%s:%ld",c->redir_host,c->redir_port); + zend_hash_update(c->nodes, key, klen+1, (void*)&node, + sizeof(redisClusterNode*), NULL); + /* Now point our slot at the node */ c->master[c->redir_slot] = node; } @@ -1418,7 +1425,10 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char } /* Update mapping if the data has MOVED */ - if (c->redir_type == REDIR_MOVED) cluster_update_slot(c TSRMLS_CC); + if (c->redir_type == REDIR_MOVED) { + cluster_update_slot(c TSRMLS_CC); + c->cmd_sock = SLOT_SOCK(c, slot); + } } /* Figure out if we've timed out trying to read or write the data */ @@ -2302,7 +2312,7 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, while(count--) { /* Read our line */ line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); - + if (line != NULL) { zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { From 5aa251fbf24e3c74e35363e1f3c25fc95d575396 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 17 Dec 2015 21:19:02 -0800 Subject: [PATCH 0506/1986] Removes documentation ambiguity Addresses #689 --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 446133e697..10734fb0ab 100644 --- a/README.markdown +++ b/README.markdown @@ -1352,7 +1352,7 @@ $redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE fl ### hSet ----- -_**Description**_: Adds a value to the hash stored at key. If this value is already in the hash, `FALSE` is returned. +_**Description**_: Adds a value to the hash stored at key. ##### *Parameters* *key* *hashKey* From 7f27e2e39c33aba2545b6a8109920111d96393cf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 17 Dec 2015 21:41:24 -0800 Subject: [PATCH 0507/1986] Fixes bug when checking for failure in php_stream_gets Addresses #672 --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 546bb40fe4..daa33a687f 100644 --- a/library.c +++ b/library.c @@ -2252,7 +2252,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, char inbuf[255]; /* Read up to our newline */ - if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { + if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) == NULL) { return -1; } From 3f6469018a80e376b2cee7523cb8ae7a85de37fe Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 24 Dec 2015 09:28:24 -0800 Subject: [PATCH 0508/1986] Fix typo --- cluster.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster.markdown b/cluster.markdown index 0cbf45f926..25f8e32e12 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -67,7 +67,7 @@ $obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_ERROR // Always distribute readonly commands between masters and slaves, at random $obj_cluster->setOption( - RedisCluster::OPT_FAILOVER, RedsiCluster::FAILOVER_DISTRIBUTE + RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE ); From 61b7f6ca4e56ee761c5a5b42d8ca0ed534778f05 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 5 Jan 2016 16:41:35 -0800 Subject: [PATCH 0509/1986] Fix docs --- cluster.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cluster.markdown b/cluster.markdown index 25f8e32e12..2ba3ee0610 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -60,14 +60,14 @@ By default, RedisCluster will only ever send commands to master nodes, but can b
 // The default option, only send commands to master nodes
-$obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_NONE);
+$obj_cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_NONE);
 
 // In the event we can't reach a master, and it has slaves, failover for read commands
-$obj_cluster->setOption(RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_ERROR);
+$obj_cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_ERROR);
 
 // Always distribute readonly commands between masters and slaves, at random
 $obj_cluster->setOption(
-    RedisCluster::OPT_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE
+    RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE
 );
 
From c22b58c6e3ea423bb07d30f13b6db1c69103f5c1 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Sun, 10 Jan 2016 17:19:31 +0100 Subject: [PATCH 0510/1986] Update version To avoid users confusion, this is NOT a released version And use something > last released version. --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index b52fceef2a..0e50a24a03 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "2.2.5" +#define PHP_REDIS_VERSION "2.2.8-dev" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 2633bd8ad9eea8b63a13056a715e5ac4fb602911 Mon Sep 17 00:00:00 2001 From: mixiaojiong Date: Tue, 12 Jan 2016 10:08:38 +0800 Subject: [PATCH 0511/1986] bugfix 718 bugfix 718 --- library.c | 10 +++++----- library.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library.c b/library.c index daa33a687f..67455063a2 100644 --- a/library.c +++ b/library.c @@ -214,7 +214,7 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter) { REDIS_REPLY_TYPE reply_type; - int reply_info; + long int reply_info; char *p_iter; /* Our response should have two multibulk replies */ @@ -2229,7 +2229,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, - int *reply_info TSRMLS_DC) + long int *reply_info TSRMLS_DC) { // Make sure we haven't lost the connection, even trying to reconnect if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { @@ -2257,7 +2257,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, } /* Set our size response */ - *reply_info = atoi(inbuf); + *reply_info = atol(inbuf); } /* Success! */ @@ -2320,7 +2320,7 @@ PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { - int reply_info; + long int reply_info; REDIS_REPLY_TYPE reply_type; zval *z_subelem; @@ -2383,7 +2383,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { // Reply type, and reply size vars REDIS_REPLY_TYPE reply_type; - int reply_info; + long int reply_info; //char *bulk_resp; zval *z_ret; diff --git a/library.h b/library.h index 0d79f78541..2853be72ba 100644 --- a/library.h +++ b/library.h @@ -76,7 +76,7 @@ PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int * Variant Read methods, mostly to implement eval */ -PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info TSRMLS_DC); +PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long int *reply_info TSRMLS_DC); PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC); From 099145c6d18004dfa2c489a1361cac21cca1eb7d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 12 Jan 2016 08:33:11 -0800 Subject: [PATCH 0512/1986] Don't neet 'long int', long is fine --- library.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library.c b/library.c index 67455063a2..9121b196f4 100644 --- a/library.c +++ b/library.c @@ -214,7 +214,7 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter) { REDIS_REPLY_TYPE reply_type; - long int reply_info; + long reply_info; char *p_iter; /* Our response should have two multibulk replies */ @@ -2229,7 +2229,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, - long int *reply_info TSRMLS_DC) + long *reply_info TSRMLS_DC) { // Make sure we haven't lost the connection, even trying to reconnect if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { @@ -2320,7 +2320,7 @@ PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC) { - long int reply_info; + long reply_info; REDIS_REPLY_TYPE reply_type; zval *z_subelem; @@ -2383,7 +2383,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { // Reply type, and reply size vars REDIS_REPLY_TYPE reply_type; - long int reply_info; + long reply_info; //char *bulk_resp; zval *z_ret; From 0ae9731e99de78789da4f8c1ad9972f40cdaaf41 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 12 Jan 2016 08:40:09 -0800 Subject: [PATCH 0513/1986] Just long not long int --- library.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.h b/library.h index 2853be72ba..e9258bf378 100644 --- a/library.h +++ b/library.h @@ -76,7 +76,7 @@ PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int * Variant Read methods, mostly to implement eval */ -PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long int *reply_info TSRMLS_DC); +PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info TSRMLS_DC); PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC); From 92a0e1ef955951db5a85e4b27623e7230979fe04 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 13 Jan 2016 08:20:49 -0800 Subject: [PATCH 0514/1986] Update len to long in various places --- cluster_library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index c6dfdfc002..1e487191c2 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -118,7 +118,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, { size_t idx = 0; clusterReply *r; - int len; + long len; char buf[1024]; while(elements-- > 0) { @@ -614,7 +614,7 @@ clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) { clusterReply *r; REDIS_REPLY_TYPE type; - int len; + long len; // Send the command to the socket and consume reply type if(redis_sock_write(redis_sock, RESP_CLUSTER_SLOTS_CMD, From 0579872b0647e34a871a6481de11aecc23678274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Wed, 13 Jan 2016 19:04:42 +0100 Subject: [PATCH 0515/1986] Make tests actually start in README --- README.markdown | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.markdown b/README.markdown index 10734fb0ab..f7c00b05f8 100644 --- a/README.markdown +++ b/README.markdown @@ -3,7 +3,7 @@ The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). This code has been developed and maintained by Owlient from November 2009 to March 2011. -You can send comments, patches, questions [here on github](https://github.com/nicolasff/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)), or to michael.grunder@gmail.com ([@grumi78](http://twitter.com/grumi78)). +You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)), or to michael.grunder@gmail.com ([@grumi78](http://twitter.com/grumi78)). # Table of contents @@ -110,27 +110,30 @@ See [dedicated page](https://github.com/phpredis/phpredis/blob/master/arrays.mar See [dedicated page](https://github.com/phpredis/phpredis/blob/feature/redis_cluster/cluster.markdown#readme). ## Running the unit tests + phpredis uses a small custom unit test suite for testing functionality of the various classes. To run tests, simply do the following: -
+~~~
 # Run tests for Redis class (note this is the default)
 php tests/TestRedis.php --class Redis
 
 # Run tests for RedisArray class
-tests/mkring.sh
+tests/mkring.sh start
 php tests/TestRedis.php --class RedisArray
+tests/mkring.sh stop
 
 # Run tests for the RedisCluster class
-test/make-cluster.sh start
+tests/make-cluster.sh start
 php tests/TestRedis.php --class RedisCluster
-
+tests/make-cluster.sh stop +~~~ Note that it is possible to run only tests which match a substring of the test itself by passing the additional argument '--test ' when invoking. -
+~~~
 # Just run the 'echo' test
 php tests/TestRedis.php --class Redis --test echo
-
+~~~ # Classes and methods ----- From cc15aae30d0686ded83ac9bbec70fdc2e73c77f5 Mon Sep 17 00:00:00 2001 From: Sergei Lomakov Date: Wed, 27 Jan 2016 08:07:56 +0000 Subject: [PATCH 0516/1986] Fix problem with rediscluster storage --- redis_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index ab74923a09..21d3ff524b 100644 --- a/redis_session.c +++ b/redis_session.c @@ -627,7 +627,7 @@ PS_READ_FUNC(rediscluster) { /* Attempt to read reply */ reply = cluster_read_resp(c TSRMLS_CC); - if (!reply || c->err) { + if (!reply || c->err || reply->str == NULL) { if (reply) cluster_free_reply(reply, 1); return FAILURE; } From 5537b10cfe7a706339bdafec88e2c46599b1a1b3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 11 Feb 2016 19:16:46 -0800 Subject: [PATCH 0517/1986] Add IPv6 support --- cluster_library.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 1e487191c2..1acb71dd20 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -872,8 +872,9 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { // Grab a copy of the string str = Z_STRVAL_PP(z_seed); - // Must be in host:port form - if(!(psep = strchr(str, ':'))) + /* Make sure we have a colon for host:port. Search right to left in the + * case of IPv6 */ + if ((psep = strrchr(str, ':')) == NULL) continue; // Allocate a structure for this seed @@ -948,12 +949,12 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) /* Move past "MOVED" or "ASK */ msg += moved ? MOVED_LEN : ASK_LEN; - // We need a slot seperator - if(!(host = strchr(msg, ' '))) return -1; + /* Make sure we can find host */ + if ((host = strchr(msg, ' ')) == NULL) return -1; *host++ = '\0'; - // We need a : that seperates host from port - if(!(port = strchr(host,':'))) return -1; + /* Find port, searching right to left in case of IPv6 */ + if ((port = strrchr(host, ':')) == NULL) return -1; *port++ = '\0'; // Success, apply it From 8c47186fe5ba83dbb866ca45f913211a3d3adc0d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 11 Feb 2016 19:17:36 -0800 Subject: [PATCH 0518/1986] Add IPv6 support --- library.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 9121b196f4..13ea8efd6c 100644 --- a/library.c +++ b/library.c @@ -1610,6 +1610,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) { struct timeval tv, read_tv, *tv_ptr = NULL; char *host = NULL, *persistent_id = NULL, *errstr = NULL; + const char *fmtstr = "%s:%d"; int host_len, err = 0; php_netstream_data_t *sock; int tcp_flag = 1; @@ -1632,8 +1633,15 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } else { if(redis_sock->port == 0) redis_sock->port = 6379; - host_len = spprintf(&host, 0, "%s:%d", redis_sock->host, - redis_sock->port); + +#ifdef HAVE_IPV6 + /* If we've got IPv6 and find a colon in our address, convert to proper + * IPv6 [host]:port format */ + if (strchr(redis_sock->host, ':') != NULL) { + fmtstr = "[%s]:%d"; + } +#endif + host_len = spprintf(&host, 0, fmtstr, redis_sock->host, redis_sock->port); } if (redis_sock->persistent) { From 3012052849192593baff623a0265c4da860e15a7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 12 Feb 2016 18:10:50 -0800 Subject: [PATCH 0519/1986] More IPv6 additions * Modified RedisArray to support IPv6 * Updated general test suite to take an override host * Updated make-cluster.sh to take an override host --- redis_array_impl.c | 2 +- tests/RedisArrayTest.php | 31 ++++++++++++++++++++----------- tests/RedisTest.php | 6 +++--- tests/TestRedis.php | 19 +++++++++++-------- tests/TestSuite.php | 26 ++++++++++++++++++-------- tests/make-cluster.sh | 22 ++++++++++++++-------- 6 files changed, 67 insertions(+), 39 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index c90227a384..b6fd5f1bd9 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -57,7 +57,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b host_len = Z_STRLEN_PP(zpData); port = 6379; - if((p = strchr(host, ':'))) { /* found port */ + if((p = strrchr(host, ':'))) { /* found port */ host_len = p - host; port = (short)atoi(p+1); } else if(strchr(host,'/') != NULL) { /* unix socket */ diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 75f421b81d..a7fa72828f 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -12,6 +12,12 @@ function custom_hash($str) { return $str; } +function parseHostPort($str, &$host, &$port) { + $pos = strrpos($str, ':'); + $host = substr($str, 0, $pos); + $port = substr($str, $pos+1); +} + class Redis_Array_Test extends TestSuite { private $strings; @@ -19,7 +25,6 @@ class Redis_Array_Test extends TestSuite private $data = NULL; public function setUp() { - // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; $this->strings = array(); @@ -42,7 +47,13 @@ public function testMSet() { // check each key individually using a new connection foreach($this->strings as $k => $v) { - list($host, $port) = split(':', $this->ra->_target($k)); + parseHostPort($this->ra->_target($k), $host, $port); + + $target = $this->ra->_target($k); + $pos = strrpos($target, ':'); + + $host = substr($target, 0, $pos); + $port = substr($target, $pos+1); $r = new Redis; $r->pconnect($host, (int)$port); @@ -180,7 +191,7 @@ public function testFlush() { // flush all servers first. global $serverList; foreach($serverList as $s) { - list($host, $port) = explode(':', $s); + parseHostPort($s, $host, $port); $r = new Redis(); $r->pconnect($host, (int)$port, 0); @@ -354,11 +365,8 @@ public function testReadAndMigrateAll() { // Read and migrate keys on fallback, causing the whole ring to be rehashed. public function testAllKeysHaveBeenMigrated() { foreach($this->strings as $k => $v) { - // get the target for each key - $target = $this->ra->_target($k); + parseHostPort($this->ra->_target($k), $host, $port); - // connect to the target host - list($host,$port) = split(':', $target); $r = new Redis; $r->pconnect($host, $port); @@ -531,12 +539,13 @@ public function testDistribution() { } } -function run_tests($className, $str_filter) { +function run_tests($className, $str_filter, $str_host) { // reset rings global $newRing, $oldRing, $serverList; - $newRing = array('localhost:6379', 'localhost:6380', 'localhost:6381'); - $oldRing = array(); - $serverList = array('localhost:6379', 'localhost:6380', 'localhost:6381', 'localhost:6382'); + + $newRing = Array("$str_host:6379", "$str_host:6380", "$str_host:6381"); + $oldRing = Array(); + $serverList = Array("$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"); // run TestSuite::run($className, $str_filter); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 00bb8b684f..c3b8bd09db 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4,7 +4,6 @@ class Redis_Test extends TestSuite { - const HOST = '127.0.0.1'; const PORT = 6379; const AUTH = NULL; //replace with a string to use Redis authentication @@ -21,7 +20,8 @@ public function setUp() { protected function newInstance() { $r = new Redis(); - $r->connect(self::HOST, self::PORT); + + $r->connect($this->getHost(), self::PORT); if(self::AUTH) { $this->assertTrue($r->auth(self::AUTH)); @@ -4485,7 +4485,7 @@ public function testReadTimeoutOption() { public function testIntrospection() { // Simple introspection tests - $this->assertTrue($this->redis->getHost() === self::HOST); + $this->assertTrue($this->redis->getHost() === $this->getHost()); $this->assertTrue($this->redis->getPort() === self::PORT); $this->assertTrue($this->redis->getAuth() === self::AUTH); } diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 14bab8ae32..d2236372a3 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -10,7 +10,7 @@ ini_set( 'display_errors','1'); /* Grab options */ -$arr_args = getopt('', Array('class:', 'test:', 'nocolors')); +$arr_args = getopt('', Array('host:', 'class:', 'test:', 'nocolors')); /* Grab the test the user is trying to run */ $arr_valid_classes = Array('redis', 'redisarray', 'rediscluster'); @@ -20,6 +20,9 @@ /* Get our test filter if provided one */ $str_filter = isset($arr_args['test']) ? $arr_args['test'] : NULL; +/* Grab override test host if it was passed */ +$str_host = isset($arr_args['host']) ? $arr_args['host'] : '127.0.0.1'; + /* Validate the class is known */ if (!in_array($str_class, $arr_valid_classes)) { echo "Error: Valid test classes are Redis, RedisArray, and RedisCluster!\n"; @@ -36,21 +39,21 @@ echo "Testing class "; if ($str_class == 'redis') { echo TestSuite::make_bold("Redis") . "\n"; - exit(TestSuite::run("Redis_Test", $str_filter)); + exit(TestSuite::run("Redis_Test", $str_filter, $str_host)); } else if ($str_class == 'redisarray') { echo TestSuite::make_bold("RedisArray") . "\n"; global $useIndex; foreach(array(true, false) as $useIndex) { echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; - run_tests('Redis_Array_Test', $str_filter); - run_tests('Redis_Rehashing_Test', $str_filter); - run_tests('Redis_Auto_Rehashing_Test', $str_filter); - run_tests('Redis_Multi_Exec_Test', $str_filter); - run_tests('Redis_Distributor_Test', $str_filter); + run_tests('Redis_Array_Test', $str_filter, $str_host); + run_tests('Redis_Rehashing_Test', $str_filter, $str_host); + run_tests('Redis_Auto_Rehashing_Test', $str_filter, $str_host); + run_tests('Redis_Multi_Exec_Test', $str_filter, $str_host); + run_tests('Redis_Distributor_Test', $str_filter, $str_host); } } else { echo TestSuite::make_bold("RedisCluster") . "\n"; - exit(TestSuite::run("Redis_Cluster_Test", $str_filter)); + exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host)); } ?> diff --git a/tests/TestSuite.php b/tests/TestSuite.php index ecdefbd0d2..5bbd98bca9 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -2,6 +2,9 @@ // phpunit is such a pain to install, we're going with pure-PHP here. class TestSuite { + /* Host the tests will use */ + private $str_host; + private static $_boo_colorize = false; private static $BOLD_ON = "\033[1m"; @@ -18,8 +21,14 @@ class TestSuite { public static $errors = array(); public static $warnings = array(); + public function __construct($str_host) { + $this->str_host = $str_host; + } + + public function getHost() { return $this->str_host; } + public static function make_bold($str_msg) { - return self::$_boo_colorize + return self::$_boo_colorize ? self::$BOLD_ON . $str_msg . self::$BOLD_OFF : $str_msg; } @@ -56,7 +65,7 @@ protected function assertTrue($bool) { } protected function assertLess($a, $b) { - if($a < $b) + if($a < $b) return; $bt = debug_backtrace(false); @@ -85,12 +94,12 @@ protected function markTestSkipped($msg='') { private static function getMaxTestLen($arr_methods, $str_limit) { $i_result = 0; - + $str_limit = strtolower($str_limit); foreach ($arr_methods as $obj_method) { $str_name = strtolower($obj_method->name); - if (substr($str_name, 0, 4) != 'test') + if (substr($str_name, 0, 4) != 'test') continue; if ($str_limit && !strstr($str_name, $str_limit)) continue; @@ -101,14 +110,14 @@ private static function getMaxTestLen($arr_methods, $str_limit) { } return $i_result; } - + /* Flag colorization */ public static function flagColorization($boo_override) { self::$_boo_colorize = $boo_override && function_exists('posix_isatty') && posix_isatty(STDOUT); } - public static function run($className, $str_limit = NULL) { + public static function run($className, $str_limit = NULL, $str_host = NULL) { /* Lowercase our limit arg if we're passed one */ $str_limit = $str_limit ? strtolower($str_limit) : $str_limit; @@ -121,7 +130,7 @@ public static function run($className, $str_limit = NULL) { $name = $m->name; if(substr($name, 0, 4) !== 'test') continue; - + /* If we're trying to limit to a specific test and can't match the * substring, skip */ if ($str_limit && strstr(strtolower($name), $str_limit)===FALSE) { @@ -132,7 +141,8 @@ public static function run($className, $str_limit = NULL) { echo self::make_bold($str_out_name); $count = count($className::$errors); - $rt = new $className(); + $rt = new $className($str_host); + try { $rt->setUp(); $rt->$name(); diff --git a/tests/make-cluster.sh b/tests/make-cluster.sh index 64061486b7..b3a9108668 100755 --- a/tests/make-cluster.sh +++ b/tests/make-cluster.sh @@ -5,15 +5,16 @@ # simplifying the process of running unit tests. # # Usage: -# ./make-cluster.sh start -# ./make-cluster.sh stop +# ./make-cluster.sh start [host] +# ./make-cluster.sh stop [host] # -BASEDIR=`dirname $@` +BASEDIR=`pwd` NODEDIR=$BASEDIR/nodes MAPFILE=$NODEDIR/nodemap -# Nodes, replicas, ports, etc. Change if you want different values +# Host, nodes, replicas, ports, etc. Change if you want different values +HOST="127.0.0.1" NODES=12 REPLICAS=3 START_PORT=7000 @@ -38,7 +39,7 @@ spawnNode() { # Attempt to spawn the node verboseRun redis-server --cluster-enabled yes --dir $NODEDIR --port $PORT \ --cluster-config-file node-$PORT.conf --daemonize yes --save \'\' \ - --bind '127.0.0.1' --dbfilename node-$PORT.rdb + --bind $HOST --dbfilename node-$PORT.rdb # Abort if we can't spin this instance if [ $? -ne 0 ]; then @@ -54,7 +55,7 @@ spawnNodes() { spawnNode $PORT # Add this host:port to our nodemap so the tests can get seeds - echo "127.0.0.1:$PORT" >> $MAPFILE + echo "$HOST:$PORT" >> $MAPFILE done } @@ -85,7 +86,7 @@ cleanConfigInfo() { initCluster() { TRIBARGS="" for PORT in `seq $START_PORT $END_PORT`; do - TRIBARGS="$TRIBARGS 127.0.0.1:$PORT" + TRIBARGS="$TRIBARGS $HOST:$PORT" done verboseRun redis-trib.rb create --replicas $REPLICAS $TRIBARGS @@ -122,6 +123,11 @@ stopCluster() { checkExe redis-server checkExe redis-trib.rb +# Override the host if we've got $2 +if [[ ! -z "$2" ]]; then + HOST=$2 +fi + # Main entry point to start or stop/kill a cluster case "$1" in start) @@ -131,6 +137,6 @@ case "$1" in stopCluster ;; *) - echo "Usage $0 [start|stop]" + echo "Usage $0 [host]" ;; esac From a3e11416b42ca67705d40a3b67f3f44c995b09f9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 12 Feb 2016 22:27:27 -0800 Subject: [PATCH 0520/1986] Move response processing outside of the IF_ATOMIC block Addresses #744 --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index a3c13455c3..400e4703ec 100644 --- a/redis.c +++ b/redis.c @@ -1417,8 +1417,8 @@ PHP_METHOD(Redis, sort) { { RETURN_FALSE; } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); } + REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); } } From 17b1f42770ca82d05d64548f8448ebb977ea833c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 16 Feb 2016 11:29:06 -0800 Subject: [PATCH 0521/1986] Fix a memory leak in discard --- library.c | 1 + 1 file changed, 1 insertion(+) diff --git a/library.c b/library.c index 9121b196f4..a546e5b5e3 100644 --- a/library.c +++ b/library.c @@ -1756,6 +1756,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, } if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + efree(response); RETURN_TRUE; } RETURN_FALSE; From 3266b222531c97f798127121301c9a20c33c1fc2 Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Tue, 15 Mar 2016 17:25:10 +0100 Subject: [PATCH 0522/1986] Before unserializing a Redis value with igbinary, check if it actually contains the correct header --- library.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/library.c b/library.c index a546e5b5e3..8970d7d49b 100644 --- a/library.c +++ b/library.c @@ -2156,6 +2156,27 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY + /* + * Check if the given string starts with an igbinary header. + * + * An igbinary string consists of the following format: + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * | header (4) | type (1) | ... (n) | NUL (1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * + * With header being either 0x00000001 or 0x00000002 + * (encoded as big endian). + */ + if (val_len < 6 + || (memcmp(val, "\x00\x00\x00\x01", 4) != 0 + && memcmp(val, "\x00\x00\x00\x02", 4) != 0)) + { + /* This is most definitely not an igbinary string, so do + not try to unserialize this as one. */ + return 0; + } + if(!*return_value) { MAKE_STD_ZVAL(*return_value); rv_free = 1; From 5528297a3e72cf94958d1eda0290623705e84a62 Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Wed, 16 Mar 2016 10:19:23 +0100 Subject: [PATCH 0523/1986] Not all versions contain the trailing NULL byte, so do not require it --- library.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 8970d7d49b..3742f4df89 100644 --- a/library.c +++ b/library.c @@ -2159,7 +2159,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, /* * Check if the given string starts with an igbinary header. * - * An igbinary string consists of the following format: + * A modern igbinary string consists of the following format: * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- * | header (4) | type (1) | ... (n) | NUL (1) | @@ -2167,8 +2167,11 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, * * With header being either 0x00000001 or 0x00000002 * (encoded as big endian). + * + * Not all versions contain the trailing NULL byte though, so + * do not check for that. */ - if (val_len < 6 + if (val_len < 5 || (memcmp(val, "\x00\x00\x00\x01", 4) != 0 && memcmp(val, "\x00\x00\x00\x02", 4) != 0)) { From 04196aeebd07e52b1c57eeecb9e2ec7733e0d6c8 Mon Sep 17 00:00:00 2001 From: CatKang Date: Fri, 25 Mar 2016 19:51:18 +0800 Subject: [PATCH 0524/1986] Fix bug of unclosed socket stream in RedisCluster's construction --- cluster_library.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cluster_library.c b/cluster_library.c index 1e487191c2..f996642182 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -922,6 +922,7 @@ cluster_map_keyspace(redisCluster *c TSRMLS_DC) { if(!mapped && slots) { memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); } + redis_sock_disconnect(*seed TSRMLS_CC); } // Clean up slots reply if we got one From 1685337ca5a4e57b73df302fe6a9589956f00f56 Mon Sep 17 00:00:00 2001 From: CatKang Date: Fri, 25 Mar 2016 19:51:18 +0800 Subject: [PATCH 0525/1986] Fix bug of unclosed socket stream in RedisCluster's construction --- cluster_library.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cluster_library.c b/cluster_library.c index 1e487191c2..f996642182 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -922,6 +922,7 @@ cluster_map_keyspace(redisCluster *c TSRMLS_DC) { if(!mapped && slots) { memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); } + redis_sock_disconnect(*seed TSRMLS_CC); } // Clean up slots reply if we got one From 799869525b3cac4e18cafc3caf3f8afb3e84e543 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 29 Mar 2016 11:42:23 -0700 Subject: [PATCH 0526/1986] Allow sDiffStore to take one single array argument again --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index d3f09255dc..db0d76f6a5 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2803,7 +2803,7 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 2, 0, cmd, cmd_len, slot); + "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 1, 0, cmd, cmd_len, slot); } /* COMMAND */ From 32eb1c5f7da6ab2008c3f0a0ccbd5bb83cea8f38 Mon Sep 17 00:00:00 2001 From: Vitaliy Stepanyuk Date: Wed, 30 Mar 2016 11:22:02 +0200 Subject: [PATCH 0527/1986] Reduce number of connections to one cluster node by shuffling nodes. --- cluster_library.c | 19 ++++++++++++++----- cluster_library.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index f996642182..e15e351431 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -856,14 +856,21 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { char *str, *psep, key[1024]; int key_len; zval **z_seed; + int *seeds; + size_t i, count; + + count = zend_hash_num_elements(ht_seeds); + seeds = emalloc(sizeof(int) * count); + + for (i = 0; i < count; i++) seeds[i] = i; + fyshuffle(seeds, count); // Iterate our seeds array - for(zend_hash_internal_pointer_reset(ht_seeds); - zend_hash_has_more_elements(ht_seeds)==SUCCESS; - zend_hash_move_forward(ht_seeds)) - { + for (i = 0; i < count; i++) { // Grab seed string - zend_hash_get_current_data(ht_seeds, (void**)&z_seed); + if (zend_hash_index_find(ht_seeds, seeds[i], (void**)&z_seed) != SUCCESS) { + continue; + } // Skip anything that isn't a string if(Z_TYPE_PP(z_seed)!=IS_STRING) @@ -890,6 +897,8 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { sizeof(RedisSock*),NULL); } + efree(seeds); + // Success if at least one seed seems valid return zend_hash_num_elements(cluster->seeds) > 0 ? 0 : -1; } diff --git a/cluster_library.h b/cluster_library.h index f82667579d..a265396ee9 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -458,6 +458,8 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); +static void fyshuffle(int *array, size_t len); + #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From 200afe660cf2fb033f86fa1f3f9d316e40fe68ea Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 31 Mar 2016 05:39:14 -0700 Subject: [PATCH 0528/1986] Merging in logic from #777 to shuffle cluster seeds --- cluster_library.c | 90 ++++++++++++++++++++++++++++++----------------- cluster_library.h | 2 -- 2 files changed, 57 insertions(+), 35 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index e15e351431..db84a536fd 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -608,6 +608,20 @@ static char **split_str_by_delim(char *str, char *delim, int *len) { return array; } +/* Fisher-Yates shuffle for integer array */ +static void fyshuffle(int *array, size_t len) { + int temp, n = len; + size_t r; + + /* Randomize */ + while (n > 1) { + r = ((int)((double)n-- * (rand() / (RAND_MAX+1.0)))); + temp = array[n]; + array[n] = array[r]; + array[r] = temp; + }; +} + /* Execute a CLUSTER SLOTS command against the seed socket, and return the * reply or NULL on failure. */ clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) @@ -849,35 +863,60 @@ PHP_REDIS_API void cluster_free(redisCluster *c) { efree(c); } +/* Takes our input hash table and returns a straigt C array with elements, + * which have been randomized. The return value needs to be freed. */ +static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) { + zval **z_seeds, **z_seed; + int *map, i, count, index=0; + + /* How many */ + count = zend_hash_num_elements(seeds); + + /* Allocate our return value and map */ + z_seeds = ecalloc(count, sizeof(zval*)); + map = emalloc(sizeof(int)*count); + + /* Fill in and shuffle our map */ + for (i = 0; i < count; i++) map[i] = i; + fyshuffle(map, count); + + /* Iterate over our source array and use our map to create a random list */ + for (zend_hash_internal_pointer_reset(seeds); + zend_hash_has_more_elements(seeds) == SUCCESS; + zend_hash_move_forward(seeds)) + { + zend_hash_get_current_data(seeds, (void**)&z_seed); + z_seeds[map[index]] = *z_seed; + index++; + } + + efree(map); + + *len = count; + return z_seeds; +} + /* Initialize seeds */ PHP_REDIS_API int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { RedisSock *redis_sock; char *str, *psep, key[1024]; - int key_len; - zval **z_seed; - int *seeds; - size_t i, count; + int key_len, count, i; + zval **z_seeds, *z_seed; - count = zend_hash_num_elements(ht_seeds); - seeds = emalloc(sizeof(int) * count); - - for (i = 0; i < count; i++) seeds[i] = i; - fyshuffle(seeds, count); + /* Get our seeds in a randomized array */ + z_seeds = cluster_shuffle_seeds(ht_seeds, &count); // Iterate our seeds array for (i = 0; i < count; i++) { - // Grab seed string - if (zend_hash_index_find(ht_seeds, seeds[i], (void**)&z_seed) != SUCCESS) { - continue; - } + z_seed = z_seeds[i]; - // Skip anything that isn't a string - if(Z_TYPE_PP(z_seed)!=IS_STRING) + /* Has to be a string */ + if (z_seed == NULL || Z_TYPE_P(z_seed) != IS_STRING) continue; // Grab a copy of the string - str = Z_STRVAL_PP(z_seed); + str = Z_STRVAL_P(z_seed); // Must be in host:port form if(!(psep = strchr(str, ':'))) @@ -897,15 +936,14 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { sizeof(RedisSock*),NULL); } - efree(seeds); + efree(z_seeds); // Success if at least one seed seems valid return zend_hash_num_elements(cluster->seeds) > 0 ? 0 : -1; } /* Initial mapping of our cluster keyspace */ -PHP_REDIS_API int -cluster_map_keyspace(redisCluster *c TSRMLS_DC) { +PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { RedisSock **seed; clusterReply *slots=NULL; int mapped=0; @@ -1060,20 +1098,6 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC) { } } -/* Fisher-Yates shuffle for integer array */ -static void fyshuffle(int *array, size_t len) { - int temp, n = len; - size_t r; - - /* Randomize */ - while (n > 1) { - r = ((int)((double)n-- * (rand() / (RAND_MAX+1.0)))); - temp = array[n]; - array[n] = array[r]; - array[r] = temp; - }; -} - /* This method attempts to write our command at random to the master and any * attached slaves, until we either successufly do so, or fail. */ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, diff --git a/cluster_library.h b/cluster_library.h index a265396ee9..f82667579d 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -458,8 +458,6 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); -static void fyshuffle(int *array, size_t len); - #endif /* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ From bc60805a4d48ddc1f33fe83efe0f16d80ea0a3b0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 1 May 2016 19:11:37 -0700 Subject: [PATCH 0529/1986] Avoid double free in case of error for DEL and MGET --- redis_cluster.c | 1 - 1 file changed, 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index e819683184..28aff72172 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -526,7 +526,6 @@ distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, { cluster_multi_free(mc); zval_dtor(z_ret); - efree(z_ret); efree(ctx); return -1; } From 07c9f99c9090a7a9b59ccb041f75ef1f5e233e2b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 May 2016 10:34:29 -0700 Subject: [PATCH 0530/1986] Allow zRangeByScore options to be case insensitive Addresses #807 --- redis_commands.c | 57 ++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index db0d76f6a5..352f596173 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -495,17 +495,24 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* ZRANGEBYSCORE/ZREVRANGEBYSCORE */ +#define IS_WITHSCORES_ARG(s, l) \ + (l == sizeof("withscores") && !strncasecmp(s,"withscores",sizeof("withscores"))) +#define IS_LIMIT_ARG(s, l) \ + (l == sizeof("limit") && !strncasecmp(s,"limit",sizeof("limit"))) + int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, void **ctx) { char *key; int key_len, key_free; - char *start, *end; + char *start, *end, *optkey; int start_len, end_len; - int has_limit=0; + int has_limit=0, type; long limit_low, limit_high; zval *z_opt=NULL, **z_ele; + unsigned long idx; + unsigned int optlen; HashTable *ht_opt; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a", &key, &key_len, @@ -518,26 +525,34 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Check for an options array if(z_opt && Z_TYPE_P(z_opt)==IS_ARRAY) { ht_opt = Z_ARRVAL_P(z_opt); - - // Check for WITHSCORES - *withscores = (zend_hash_find(ht_opt,"withscores",sizeof("withscores"), - (void**)&z_ele)==SUCCESS && Z_TYPE_PP(z_ele)==IS_BOOL && - Z_BVAL_PP(z_ele)==1); - - // LIMIT - if(zend_hash_find(ht_opt,"limit",sizeof("limit"),(void**)&z_ele) - ==SUCCESS) + for (zend_hash_internal_pointer_reset(ht_opt); + zend_hash_has_more_elements(ht_opt) == SUCCESS; + zend_hash_move_forward(ht_opt)) { - HashTable *ht_limit = Z_ARRVAL_PP(z_ele); - zval **z_off, **z_cnt; - if(zend_hash_index_find(ht_limit,0,(void**)&z_off)==SUCCESS && - zend_hash_index_find(ht_limit,1,(void**)&z_cnt)==SUCCESS && - Z_TYPE_PP(z_off)==IS_LONG && Z_TYPE_PP(z_cnt)==IS_LONG) - { - has_limit = 1; - limit_low = Z_LVAL_PP(z_off); - limit_high = Z_LVAL_PP(z_cnt); - } + /* Grab current key and value */ + type = zend_hash_get_current_key_ex(ht_opt, &optkey, &optlen, &idx, 0, NULL); + zend_hash_get_current_data(ht_opt, (void**)&z_ele); + + /* All options require a string key type */ + if (type != HASH_KEY_IS_STRING) continue; + + /* Check for withscores and limit */ + if (IS_WITHSCORES_ARG(optkey, optlen)) { + *withscores = Z_TYPE_PP(z_ele)==IS_BOOL && Z_BVAL_PP(z_ele)==1; + } else if(IS_LIMIT_ARG(optkey, optlen) && + Z_TYPE_PP(z_ele) == IS_ARRAY) + { + HashTable *htlimit = Z_ARRVAL_PP(z_ele); + zval **zoff, **zcnt; + if (zend_hash_index_find(htlimit,0,(void**)&zoff)==SUCCESS && + zend_hash_index_find(htlimit,1,(void**)&zcnt)==SUCCESS && + Z_TYPE_PP(zoff) == IS_LONG && Z_TYPE_PP(zcnt) == IS_LONG) + { + has_limit = 1; + limit_low = Z_LVAL_PP(zoff); + limit_high = Z_LVAL_PP(zcnt); + } + } } } From e3fb8436baef3690034baf087e9558b480cdaac8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 2 Jun 2016 07:49:16 -0700 Subject: [PATCH 0531/1986] Comment debug code, fix compiler warnings --- cluster_library.c | 38 +++++++------------------------------- crc16.h | 2 +- redis_cluster.c | 2 +- 3 files changed, 9 insertions(+), 33 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 4dc57de775..a530112ef4 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -8,8 +8,7 @@ extern zend_class_entry *redis_cluster_exception_ce; -/* Some debug methods that will go away when we're through with them */ - +/* Debugging methods/ static void cluster_dump_nodes(redisCluster *c) { redisClusterNode **pp, *p; @@ -40,7 +39,7 @@ static void cluster_log(char *fmt, ...) fprintf(stderr, "%s\n", buffer); } -/* Debug function to dump a clusterReply structure recursively */ +// Debug function to dump a clusterReply structure recursively static void dump_reply(clusterReply *reply, int indent) { smart_str buf = {0}; int i; @@ -87,6 +86,8 @@ static void dump_reply(clusterReply *reply, int indent) { efree(buf.c); } } +*/ + /* Recursively free our reply object. If free_data is non-zero we'll also free * the payload data (strings) themselves. If not, we just free the structs */ @@ -116,7 +117,7 @@ static void cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, clusterReply **element, int *err TSRMLS_DC) { - size_t idx = 0; + size_t idx = 0, sz; clusterReply *r; long len; char buf[1024]; @@ -137,10 +138,11 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, switch(r->type) { case TYPE_ERR: case TYPE_LINE: - if(redis_sock_gets(sock,buf,sizeof(buf),&r->len TSRMLS_CC)<0) { + if(redis_sock_gets(sock,buf,sizeof(buf),&sz TSRMLS_CC)<0) { *err = 1; return; } + r->len = (long long)sz; break; case TYPE_INT: r->integer = len; @@ -582,32 +584,6 @@ unsigned short cluster_hash_key_zval(zval *z_key) { return cluster_hash_key(kptr, klen); } -static char **split_str_by_delim(char *str, char *delim, int *len) { - char **array, *tok, *tok_buf; - int size=16; - - *len = 0; - - // Initial storage - array = emalloc(size * sizeof(char*)); - - tok = php_strtok_r(str, delim, &tok_buf); - - while(tok) { - if(size == *len) { - size *= 2; - array = erealloc(array, size * sizeof(char*)); - } - - array[*len] = tok; - (*len)++; - - tok = php_strtok_r(NULL, delim, &tok_buf); - } - - return array; -} - /* Fisher-Yates shuffle for integer array */ static void fyshuffle(int *array, size_t len) { int temp, n = len; diff --git a/crc16.h b/crc16.h index f7d2aee385..be91ffa055 100644 --- a/crc16.h +++ b/crc16.h @@ -78,7 +78,7 @@ static const uint16_t crc16tab[256]= { 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 }; -static uint16_t crc16(const char *buf, int len) { +static inline uint16_t crc16(const char *buf, int len) { int counter; uint16_t crc = 0; for (counter = 0; counter < len; counter++) diff --git a/redis_cluster.c b/redis_cluster.c index 28aff72172..1bdbc068eb 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2037,7 +2037,7 @@ PHP_METHOD(RedisCluster, _redir) { size_t len; len = snprintf(buf, sizeof(buf), "%s:%d", c->redir_host, c->redir_port); - if(c->redir_host && c->redir_host_len) { + if(*c->redir_host && c->redir_host_len) { RETURN_STRINGL(buf, len, 1); } else { RETURN_NULL(); From 3b021ea70814a2925b972843f237ffe78de943d8 Mon Sep 17 00:00:00 2001 From: Mads Ovesen Date: Mon, 18 Apr 2016 17:28:49 +0100 Subject: [PATCH 0532/1986] Fix to optional parameter (EX/NX) ordering for set command --- redis_commands.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 352f596173..a84f760d45 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1273,9 +1273,9 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Now let's construct the command we want */ if(exp_type && set_type) { /* SET NX|XX PX|EX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "ssssl", key, key_len, - val, val_len, set_type, 2, exp_type, - 2, expire); + *cmd_len = redis_cmd_format_static(cmd, "SET", "sssls", key, key_len, + val, val_len, exp_type, 2, expire, + set_type, 2); } else if(exp_type) { /* SET PX|EX */ *cmd_len = redis_cmd_format_static(cmd, "SET", "sssl", key, key_len, From 4336df3fee3505aa87a263b6545c42d69379bb6b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 2 Jun 2016 09:14:01 -0700 Subject: [PATCH 0533/1986] Fix bug in random key cluster test --- tests/RedisClusterTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 82ede4b6d4..f1cbdd25bd 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -73,6 +73,13 @@ public function testPing() { } public function testRandomKey() { + /* Ensure some keys are present to test */ + for ($i = 0; $i < 1000; $i++) { + if (rand(1, 2) == 1) { + $this->redis->set("key:$i", "val:$i"); + } + } + for ($i = 0; $i < 1000; $i++) { $k = $this->redis->randomKey("key:$i"); $this->assertTrue($this->redis->exists($k)); From 5b2631234779f9aa5fbfd2c4a665ca7573db88ae Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 2 Jun 2016 20:35:49 -0700 Subject: [PATCH 0534/1986] Updated package.xml and proper github url --- README.markdown | 2 +- bybits.php | 25 --------- package.xml | 137 ++++++++++++++++++++++++++++++++++++------------ php_redis.h | 2 +- 4 files changed, 106 insertions(+), 60 deletions(-) delete mode 100644 bybits.php diff --git a/README.markdown b/README.markdown index f7c00b05f8..e9c4925048 100644 --- a/README.markdown +++ b/README.markdown @@ -98,7 +98,7 @@ phpredis can also connect to a unix domain socket: `session.save_path = "unix:// ## Building on Windows -See [instructions from @char101](https://github.com/nicolasff/phpredis/issues/213#issuecomment-11361242) on how to build phpredis on Windows. +See [instructions from @char101](https://github.com/phpredis/phpredis/issues/213#issuecomment-11361242) on how to build phpredis on Windows. ## Distributed Redis Array diff --git a/bybits.php b/bybits.php deleted file mode 100644 index f182990034..0000000000 --- a/bybits.php +++ /dev/null @@ -1,25 +0,0 @@ - $count) { - echo $proto . "\t" . $count . "\n"; -} -?> diff --git a/package.xml b/package.xml index 761bf68c74..921703fc49 100644 --- a/package.xml +++ b/package.xml @@ -21,10 +21,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> michael.grunder@gmail.com yes - 2015-03-03 + 2016-06-02 - 2.2.7 - 2.2.7 + 2.2.8 + 2.2.8 stable @@ -32,36 +32,51 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - phpredis 2.2.7 - - -- Improvements --- + phpredis 2.2.8 + + The main improvement in this version of phpredis is support for Redis + Cluster. This version of phpredis is intended for versions of php older + than 7. + + In addition there have been many bug fixes and improvements to non cluster + related commands, which are listed below. + + I've attempted to include everyone who contribued to the project in each fix + description and have included names or github user ids. + + Thanks to everyone for submitting bug reports and pull requests. A special + thanks to Remi Collet for helping with any and all packaging related issues + + \o/ - * Implemented PFADD, PFMERGE, and PFCOUNT command handling - * Implemented ZRANGEBYLEX command (holding off on ZREVRANGEBYLEX - as that won't be out until 3.0) - * Implemented getMode() so clients can detect whether we're in - ATOMIC/MULTI/PIPELINE mode. - * Implemented rawCommand() so clients can send arbitrary things to - the redis server - * Implemented DEBUG OBJECT (@michael-grunder, @isage) - * Added/abide by connect timeout for RedisArray - * Select to the last selected DB when phpredis reconnects - - -- Fixes --- - - * Fix a possible invalid free in _serialize - * Added SAVE and BGSAVE to "distributable" commands for RedisArray - * @welting -- Fixed invalid "argc" calculation re HLL commands - * Allow clients to break out of the subscribe loop and return context. - * Fixes a memory leak in SCAN when OPT_SCAN_RETRY id. - * @remicollet -- Fix possible segfault when igbinary is enabled. - * Add a couple of cases where we throw on an error (LOADING/NOAUTH/MASTERDOWN) - * Fix several issues with serialization NARY - * @itcom -- Fix missing TSRMLS_CC and a TSRMLS_DC/TSRMLS_CC typo + --- Improvements --- + + * Added randomization to our seed nodes to balance which instance is used + to map the keyspace (Vitaliy Stepanyuk) [32eb1c5f] + * Added support for IPv6 addresses + + --- Fixes --- + + * PHP liveness checking workaround (Shafreeck Sea) [c18d58b9] + * Various documentation and code formatting and style fixes (ares333, + sanpili, Bryan Nelson, linfangrong, Romero Malaquias, Viktor Szépe) + * Fix scan reply processing to use long instead of int to avoid overflow + (mixiaojiong). + * Fix potential segfault in Redis Cluster session storage (Sergei Lomakov) + [cc15aae] + * Fixed memory leak in discard function [17b1f427] + * Sanity check for igbinary unserialization (Maurus Cuelenaere) [3266b222, + 5528297a] + * Fix segfault occuring from unclosed socket connection for Redis Cluster + (CatKang) [04196aee] + * Case insensitive zRangeByScore options + * Fixed dreaded size_t vs long long compiler warning + + @@ -74,15 +89,24 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + + + + - - - - - + + + + + + + @@ -101,6 +125,53 @@ http://pear.php.net/dtd/package-2.0.xsd"> redis + + stablestable + 2.2.82.2.8 + 2016-06-02 + + phpredis 2.2.8 + + The main improvement in this version of phpredis is support for Redis + Cluster. This version of phpredis is intended for versions of php older + than 7. + + In addition there have been many bug fixes and improvements to non cluster + related commands, which are listed below. + + I've attempted to include everyone who contribued to the project in each fix + description and have included names or github user ids. + + Thanks to everyone for submitting bug reports and pull requests. A special + thanks to Remi Collet for helping with any and all packaging related issues + + \o/ + + --- Improvements --- + + * Added randomization to our seed nodes to balance which instance is used + to map the keyspace (Vitaliy Stepanyuk) [32eb1c5f] + * Added support for IPv6 addresses + + --- Fixes --- + + * PHP liveness checking workaround (Shafreeck Sea) [c18d58b9] + * Various documentation and code formatting and style fixes (ares333, + sanpili, Bryan Nelson, linfangrong, Romero Malaquias, Viktor Szépe) + * Fix scan reply processing to use long instead of int to avoid overflow + (mixiaojiong). + * Fix potential segfault in Redis Cluster session storage (Sergei Lomakov) + [cc15aae] + * Fixed memory leak in discard function [17b1f427] + * Sanity check for igbinary unserialization (Maurus Cuelenaere) [3266b222, + 5528297a] + * Fix segfault occuring from unclosed socket connection for Redis Cluster + (CatKang) [04196aee] + * Case insensitive zRangeByScore options + * Fixed dreaded size_t vs long long compiler warning + + + stablestable 2.2.72.2.7 diff --git a/php_redis.h b/php_redis.h index 0e50a24a03..498ba353de 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "2.2.8-dev" +#define PHP_REDIS_VERSION "2.2.8-rc1" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From b249024ae7de8433704f26157c4be3e800a6723b Mon Sep 17 00:00:00 2001 From: Jan-E Date: Sun, 6 Sep 2015 10:15:29 +0200 Subject: [PATCH 0535/1986] Fix config,w32 The 'no' in line 3 is needed to make the extension shared on a snapshot build. The omission at line 25 was really curious. --- config.w32 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.w32 b/config.w32 index 254122c8ee..36844fa7bf 100644 --- a/config.w32 +++ b/config.w32 @@ -1,6 +1,6 @@ // vim: ft=javascript: -ARG_ENABLE("redis", "whether to enable redis support", "yes"); +ARG_ENABLE("redis", "whether to enable redis support", "no"); ARG_ENABLE("redis-session", "whether to enable sessions", "yes"); ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no"); @@ -22,7 +22,7 @@ if (PHP_REDIS != "no") { WARNING("redis igbinary support not enabled"); } } - + EXTENSION("redis", sources); } From 4e2223c02fed50d49341f8111f45a0378c12b2f4 Mon Sep 17 00:00:00 2001 From: Jan-E Date: Sun, 6 Sep 2015 10:57:31 +0200 Subject: [PATCH 0536/1986] Fix sources in config.w32 --- config.w32 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.w32 b/config.w32 index 36844fa7bf..628f87ed03 100644 --- a/config.w32 +++ b/config.w32 @@ -5,7 +5,7 @@ ARG_ENABLE("redis-session", "whether to enable sessions", "yes"); ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no"); if (PHP_REDIS != "no") { - var sources = "redis.c library.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c"; + var sources = "redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c"; if (PHP_REDIS_SESSION != "no") { ADD_SOURCES(configure_module_dirname, "redis_session.c", "redis"); ADD_EXTENSION_DEP("redis", "session"); From b718ce0828d9c0eb9e3a2192f889958447e4c4fa Mon Sep 17 00:00:00 2001 From: Jan-E Date: Thu, 17 Sep 2015 02:16:12 +0200 Subject: [PATCH 0537/1986] Remove second redis_session.c, fix 'too many rules' --- config.w32 | 1 - 1 file changed, 1 deletion(-) diff --git a/config.w32 b/config.w32 index 628f87ed03..11953b1a48 100644 --- a/config.w32 +++ b/config.w32 @@ -7,7 +7,6 @@ ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "n if (PHP_REDIS != "no") { var sources = "redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c"; if (PHP_REDIS_SESSION != "no") { - ADD_SOURCES(configure_module_dirname, "redis_session.c", "redis"); ADD_EXTENSION_DEP("redis", "session"); ADD_FLAG("CFLAGS_REDIS", ' /D PHP_SESSION=1 '); AC_DEFINE("HAVE_REDIS_SESSION", 1); From f4bf4e306a98f89034ea9892392a1f15f6272cab Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 7 Jun 2016 13:09:22 -0700 Subject: [PATCH 0538/1986] Geo* unit tests --- redis_commands.c | 4 ++ tests/RedisTest.php | 145 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 3 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 5e8626d928..8a6167f7d0 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2802,6 +2802,10 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, *withdist = 1; } else if (!strcasecmp(optstr, "withhash")) { *withhash = 1; + } else if (!strcasecmp(optstr, "asc")) { + *sort = SORT_ASC; + } else if (!strcasecmp(optstr, "desc")) { + *sort = SORT_DESC; } } else if (!strcasecmp(optkey, "count") && Z_TYPE_PP(optval) == IS_LONG) { *count = Z_LVAL_PP(optval); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index c3b8bd09db..e69fac4141 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1,4 +1,4 @@ - Array(-121.837478, 39.728494), + 'Sacramento' => Array(-121.494400, 38.581572), + 'Gridley' => Array(-121.693583, 39.363777), + 'Marysville' => Array(-121.591355, 39.145725), + 'Cupertino' => Array(-122.032182, 37.322998) + ); + /** * @var Redis */ @@ -2414,7 +2423,7 @@ public function testMultiExec() { public function testFailedTransactions() { $this->redis->set('x', 42); - + // failed transaction $this->redis->watch('x'); @@ -2539,7 +2548,7 @@ protected function sequence($mode) { ->ttl('key') ->expireAt('key', '0000') ->exec(); - + $this->assertTrue(is_array($ret)); $i = 0; $ttl = $ret[$i++]; @@ -4756,6 +4765,136 @@ public function testPFCommands() { } } + // + // GEO* command tests + // + + 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() { + $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, Array($longlat[0], $longlat[1], $city)); + } + + /* They all exist, should be nothing added */ + $this->assertEquals(call_user_func_array(Array($this->redis, 'geoadd'), $args), 0); + } + + /* GEORADIUS */ + public function genericGeoRadiusTest($cmd) { + /* 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') { + $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 10, 'mi'), Array('Chico')); + $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); + $args = Array('georadius', 'gk', $lng, $lat, 500, 'mi'); + } else { + $this->assertEquals($this->redis->georadiusbymember('gk', $city, 10, 'mi'), Array('Chico')); + $this->assertEquals($this->redis->georadiusbymember('gk', $city, 30, 'mi'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadiusbymember('gk', $city, 50, 'km'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadiusbymember('gk', $city, 50000, 'm'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadiusbymember('gk', $city, 150000, 'ft'), Array('Gridley', 'Chico')); + $args = Array('georadiusbymember', 'gk', $city, 500, 'mi'); + } + + /* Options */ + $opts = Array('WITHCOORD', 'WITHDIST', 'WITHHASH'); + $sortopts = Array('', 'ASC', 'DESC'); + + for ($i = 0; $i < count($opts); $i++) { + $subopts = array_slice($opts, 0, $i); + shuffle($subopts); + + $subargs = $args; + foreach ($subopts as $opt) { + $subargs[] = $opt; + } + + for ($c = 0; $c < 3; $c++) { + /* 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; + } + + $ret1 = call_user_func_array(Array($this->redis, 'rawcommand'), $realargs); + if ($cmd == 'georadius') { + $ret2 = $this->redis->$cmd('gk', $lng, $lat, 500, 'mi', $realopts); + } else { + $ret2 = $this->redis->$cmd('gk', $city, 500, 'mi', $realopts); + } + $this->assertEquals($ret1, $ret2); + } + } + } + } + + public function testGeoRadius() { + $this->genericGeoRadiusTest('georadius'); + } + + public function testGeoRadiusByMember() { + $this->genericGeoRadiusTest('georadiusbymember'); + } + + public function testGeoPos() { + $this->addCities('gk'); + $this->assertEquals($this->redis->geopos('gk', 'Chico', 'Sacramento'), $this->redis->rawCommand('geopos', 'gk', 'Chico', 'Sacramento')); + $this->assertEquals($this->redis->geopos('gk', 'Cupertino'), $this->redis->rawCommand('geopos', 'gk', 'Cupertino')); + } + + public function testGeoHash() { + $this->addCities('gk'); + $this->assertEquals($this->redis->geohash('gk', 'Chico', 'Sacramento'), $this->redis->rawCommand('geohash', 'gk', 'Chico', 'Sacramento')); + $this->assertEquals($this->redis->geohash('gk', 'Chico'), $this->redis->rawCommand('geohash', 'gk', 'Chico')); + } + + public function testGeoDist() { + $this->addCities('gk'); + + $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino'); + $r2 = $this->redis->rawCommand('geodist', 'gk', 'Chico', 'Cupertino'); + $this->assertEquals(round($r1, 8), round($r2, 8)); + + $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino', 'km'); + $r2 = $this->redis->rawCommand('geodist', 'gk', 'Chico', 'Cupertino', 'km'); + $this->assertEquals(round($r1, 8), round($r2, 8)); + } + /* Test a 'raw' command */ public function testRawCommand() { $this->redis->set('mykey','some-value'); From 3a33700c28679f5a42cdd168cddb6cc7d4776e0a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 8 Jun 2016 12:28:57 -0700 Subject: [PATCH 0539/1986] Cluster geo commands and a generic so cluster and redis tests both work --- redis_cluster.c | 35 +++++++++++++++++++++++++++++++++++ redis_cluster.h | 6 ++++++ tests/RedisClusterTest.php | 5 +++++ tests/RedisTest.php | 18 +++++++++++------- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 1bdbc068eb..42809ae754 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -218,6 +218,12 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, pubsub, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, script, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, slowlog, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geoadd, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geohash, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geopos, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geodist, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, georadius, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, georadiusbymember, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -2813,6 +2819,35 @@ PHP_METHOD(RedisCluster, slowlog) { } /* }}} */ +/* {{{ proto int RedisCluster::geoadd(string key, float long float lat string mem, ...) */ +PHP_METHOD(RedisCluster, geoadd) { + CLUSTER_PROCESS_KW_CMD("GEOADD", redis_key_varval_cmd, cluster_long_resp, 0); +} + +/* {{{ proto array RedisCluster::geohash(string key, string mem1, [string mem2...]) */ +PHP_METHOD(RedisCluster, geohash) { + CLUSTER_PROCESS_KW_CMD("GEOHASH", redis_key_varval_cmd, cluster_mbulk_raw_resp, 1); +} + +/* {{{ proto array RedisCluster::geopos(string key, string mem1, [string mem2...]) */ +PHP_METHOD(RedisCluster, geopos) { + CLUSTER_PROCESS_KW_CMD("GEOPOS", redis_key_varval_cmd, cluster_variant_resp, 1); +} + +/* {{{ proto array RedisCluster::geodist(string key, string mem1, string mem2 [string unit]) */ +PHP_METHOD(RedisCluster, geodist) { + CLUSTER_PROCESS_CMD(geodist, cluster_dbl_resp, 1); +} + +/* {{{ proto array RedisCluster::georadius() }}} */ +PHP_METHOD(RedisCluster, georadius) { + CLUSTER_PROCESS_CMD(georadius, cluster_variant_resp, 1); +} + +/* {{{ proto array RedisCluster::georadiusbymember() }}} */ +PHP_METHOD(RedisCluster, georadiusbymember) { + CLUSTER_PROCESS_CMD(georadiusbymember, cluster_variant_resp, 1) +} /* {{{ proto array RedisCluster::role(string key) * proto array RedisCluster::role(array host_port) */ diff --git a/redis_cluster.h b/redis_cluster.h index 8693d0d4f0..6f2a7ca308 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -239,6 +239,12 @@ PHP_METHOD(RedisCluster, pubsub); PHP_METHOD(RedisCluster, script); PHP_METHOD(RedisCluster, slowlog); PHP_METHOD(RedisCluster, command); +PHP_METHOD(RedisCluster, geoadd); +PHP_METHOD(RedisCluster, geohash); +PHP_METHOD(RedisCluster, geopos); +PHP_METHOD(RedisCluster, geodist); +PHP_METHOD(RedisCluster, georadius); +PHP_METHOD(RedisCluster, georadiusbymember); /* SCAN and friends */ PHP_METHOD(RedisCluster, scan); diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index f1cbdd25bd..ffe1723047 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -501,5 +501,10 @@ public function testRawCommand() { $this->redis->rpush('mylist', 'A','B','C','D'); $this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D')); } + + protected function rawCommandArray($key, $args) { + array_unshift($args, $key); + return call_user_func_array(Array($this->redis, 'rawCommand'), $args); + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index e69fac4141..7772244fac 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4769,6 +4769,10 @@ public function testPFCommands() { // GEO* command tests // + protected function rawCommandArray($key, $args) { + return call_user_func_array(Array($this->redis, 'rawCommand'), $args); + } + protected function addCities($key) { $this->redis->del($key); foreach ($this->cities as $city => $longlat) { @@ -4851,7 +4855,7 @@ public function genericGeoRadiusTest($cmd) { $realopts[] = $sortopt; } - $ret1 = call_user_func_array(Array($this->redis, 'rawcommand'), $realargs); + $ret1 = $this->rawCommandArray('gk', $realargs); if ($cmd == 'georadius') { $ret2 = $this->redis->$cmd('gk', $lng, $lat, 500, 'mi', $realopts); } else { @@ -4873,25 +4877,25 @@ public function testGeoRadiusByMember() { public function testGeoPos() { $this->addCities('gk'); - $this->assertEquals($this->redis->geopos('gk', 'Chico', 'Sacramento'), $this->redis->rawCommand('geopos', 'gk', 'Chico', 'Sacramento')); - $this->assertEquals($this->redis->geopos('gk', 'Cupertino'), $this->redis->rawCommand('geopos', 'gk', 'Cupertino')); + $this->assertEquals($this->redis->geopos('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', Array('geopos', 'gk', 'Chico', 'Sacramento'))); + $this->assertEquals($this->redis->geopos('gk', 'Cupertino'), $this->rawCommandArray('gk', Array('geopos', 'gk', 'Cupertino'))); } public function testGeoHash() { $this->addCities('gk'); - $this->assertEquals($this->redis->geohash('gk', 'Chico', 'Sacramento'), $this->redis->rawCommand('geohash', 'gk', 'Chico', 'Sacramento')); - $this->assertEquals($this->redis->geohash('gk', 'Chico'), $this->redis->rawCommand('geohash', 'gk', 'Chico')); + $this->assertEquals($this->redis->geohash('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', Array('geohash', 'gk', 'Chico', 'Sacramento'))); + $this->assertEquals($this->redis->geohash('gk', 'Chico'), $this->rawCommandArray('gk', Array('geohash', 'gk', 'Chico'))); } public function testGeoDist() { $this->addCities('gk'); $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino'); - $r2 = $this->redis->rawCommand('geodist', 'gk', 'Chico', 'Cupertino'); + $r2 = $this->rawCommandArray('gk', Array('geodist', 'gk', 'Chico', 'Cupertino')); $this->assertEquals(round($r1, 8), round($r2, 8)); $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino', 'km'); - $r2 = $this->redis->rawCommand('geodist', 'gk', 'Chico', 'Cupertino', 'km'); + $r2 = $this->rawCommandArray('gk', Array('geodist', 'gk', 'Chico', 'Cupertino', 'km')); $this->assertEquals(round($r1, 8), round($r2, 8)); } From 751ddee2ca81c1d30df858926d3fa2923547d16b Mon Sep 17 00:00:00 2001 From: sergey Date: Fri, 10 Jun 2016 16:14:16 +0300 Subject: [PATCH 0540/1986] Quick fix for PHP-5.2: - rename argument structures for cluster part - get rid of LD errors: - multiple definition of `arginfo_scan' - multiple definition of `arginfo_kscan' --- redis_cluster.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 42809ae754..b0567f65c5 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -39,7 +39,7 @@ zend_class_entry *redis_cluster_exception_ce; zend_class_entry *spl_rte_ce = NULL; /* Argument info for HSCAN, SSCAN, HSCAN */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2) +ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) ZEND_ARG_INFO(0, str_key) ZEND_ARG_INFO(1, i_iterator) ZEND_ARG_INFO(0, str_pattern) @@ -47,7 +47,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2) ZEND_END_ARG_INFO(); /* Argument infor for SCAN */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 2) +ZEND_BEGIN_ARG_INFO_EX(arginfo_scan_cl, 0, 0, 2) ZEND_ARG_INFO(1, i_iterator) ZEND_ARG_INFO(0, str_node) ZEND_ARG_INFO(0, str_pattern) @@ -174,10 +174,10 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, punsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, eval, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, evalsha, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, scan, arginfo_scan, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, scan, arginfo_scan_cl, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getmode, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getlasterror, NULL, ZEND_ACC_PUBLIC) From 68cf720ff6f2fd6169c2544828618b672c2d511f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 10 Jun 2016 11:12:31 -0700 Subject: [PATCH 0541/1986] Skip GEO* tests if version < 3.2.0 --- tests/RedisTest.php | 32 ++++++++++++++++++++++++++++++++ tests/TestRedis.php | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 7772244fac..ec8ad364b8 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -27,6 +27,10 @@ public function setUp() { $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); } + protected function minVersionCheck($version) { + return version_compare($this->version, $version, "ge"); + } + protected function newInstance() { $r = new Redis(); @@ -4782,6 +4786,10 @@ protected function addCities($key) { /* GEOADD */ public function testGeoAdd() { + if (!$this->minVersionCheck("3.2")) { + return $this->markTestSkipped(); + } + $this->redis->del('geokey'); /* Add them one at a time */ @@ -4801,6 +4809,10 @@ public function testGeoAdd() { /* GEORADIUS */ public function genericGeoRadiusTest($cmd) { + if (!$this->minVersionCheck("3.2.0")) { + return $this->markTestSkipped(); + } + /* Chico */ $city = 'Chico'; $lng = -121.837478; @@ -4868,26 +4880,46 @@ public function genericGeoRadiusTest($cmd) { } public function testGeoRadius() { + if (!$this->minVersionCheck("3.2.0")) { + return $this->markTestSkipped(); + } + $this->genericGeoRadiusTest('georadius'); } public function testGeoRadiusByMember() { + if (!$this->minVersionCheck("3.2.0")) { + return $this->markTestSkipped(); + } + $this->genericGeoRadiusTest('georadiusbymember'); } 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', Array('geopos', 'gk', 'Chico', 'Sacramento'))); $this->assertEquals($this->redis->geopos('gk', 'Cupertino'), $this->rawCommandArray('gk', Array('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', Array('geohash', 'gk', 'Chico', 'Sacramento'))); $this->assertEquals($this->redis->geohash('gk', 'Chico'), $this->rawCommandArray('gk', Array('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'); diff --git a/tests/TestRedis.php b/tests/TestRedis.php index d2236372a3..ccb61d9f73 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -6,7 +6,7 @@ require_once(dirname($_SERVER['PHP_SELF'])."/RedisClusterTest.php"); /* Make sure errors go to stdout and are shown */ -error_reporting(E_ALL); +error_reporting(E_ALL); ini_set( 'display_errors','1'); /* Grab options */ From 909f803f5a9d0af114ab86ce47dbcdcd3f387c8b Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 17 Jun 2016 12:34:11 +0200 Subject: [PATCH 0542/1986] use str_efree to fix #742 --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index a84f760d45..2f84577f20 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1298,7 +1298,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, CMD_SET_SLOT(slot,key,key_len); if(key_free) efree(key); - if(val_free) efree(val); + if(val_free) str_efree(val); return SUCCESS; } From 39aad727d9491a338b7ad0ecc33c0283cc0f3801 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 24 Jun 2016 23:56:08 +0300 Subject: [PATCH 0543/1986] hDel() returns LONG and is variadic --- README.markdown | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index e9c4925048..c32f9c23c5 100644 --- a/README.markdown +++ b/README.markdown @@ -1421,10 +1421,12 @@ $redis->hLen('h'); /* returns 2 */ _**Description**_: Removes a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* *key* -*hashKey* +*hashKey1* +*hashKey2* +... ##### *Return value* -*BOOL* `TRUE` in case of success, `FALSE` in case of failure +*LONG* the number of deleted keys, 0 if the key doesn't exist, `FALSE` if the key isn't a hash. ### hKeys From 14a41a6b070bea48f4defaed88c351cfba1b6d27 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 30 Jun 2016 23:35:01 +0300 Subject: [PATCH 0544/1986] Move ra->count initialization to ra_load_hosts --- redis_array_impl.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index b6fd5f1bd9..9b27aee01b 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -46,6 +46,14 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b { if ((zend_hash_get_current_data(hosts, (void **) &zpData) == FAILURE) || (Z_TYPE_PP(zpData) != IS_STRING)) { + for(i=0;icount;i++) { + zval_dtor(ra->redis[i]); + efree(ra->redis[i]); + efree(ra->hosts[i]); + } + efree(ra->redis); + efree(ra->hosts); + zval_dtor(&z_cons); efree(ra); return NULL; } @@ -87,7 +95,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b #endif add_property_resource(ra->redis[i], "socket", id); - i++; + ra->count = ++i; } return ra; @@ -330,7 +338,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev RedisArray *ra = emalloc(sizeof(RedisArray)); ra->hosts = emalloc(count * sizeof(char*)); ra->redis = emalloc(count * sizeof(zval*)); - ra->count = count; + ra->count = 0; ra->z_fun = NULL; ra->z_dist = NULL; ra->z_multi_exec = NULL; From 9df734a288e5eaeee3d2d20a779c09b85d3bea3e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 1 Jul 2016 11:41:53 -0700 Subject: [PATCH 0545/1986] Segfault and memory leak fixes for pull request * We don't need to zval_dtor z_cons as it's a non-duplicated stack allocated string. * Destroy our z_pure_cmds member as it is allocated --- redis_array_impl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 9b27aee01b..93e4724c95 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -53,8 +53,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b } efree(ra->redis); efree(ra->hosts); - zval_dtor(&z_cons); - efree(ra); + zval_dtor(ra->z_pure_cmds); + efree(ra->z_pure_cmds); + efree(ra); return NULL; } From 3a12758a39767e79fd4dcbb2911edb373da5217f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 1 Jul 2016 14:09:34 -0700 Subject: [PATCH 0546/1986] Fix incrby/decrby for large integers --- redis_commands.c | 4 ++-- tests/RedisTest.php | 35 +++++++++++++++++++---------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 69f8939a7c..84e3108c24 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1382,13 +1382,13 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, if (val == 1) { *cmd_len = redis_cmd_format_static(cmd,"INCR","s",key,key_len); } else { - *cmd_len = redis_cmd_format_static(cmd,"INCRBY","sd",key,key_len,val); + *cmd_len = redis_cmd_format_static(cmd,"INCRBY","sl",key,key_len,val); } } else { if (val == 1) { *cmd_len = redis_cmd_format_static(cmd,"DECR","s",key,key_len); } else { - *cmd_len = redis_cmd_format_static(cmd,"DECRBY","sd",key,key_len,val); + *cmd_len = redis_cmd_format_static(cmd,"DECRBY","sl",key,key_len,val); } } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index ec8ad364b8..55705c001f 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -480,32 +480,35 @@ public function testIncr() $this->redis->set('key', 0); $this->redis->incr('key'); - $this->assertEquals(1, (int)$this->redis->get('key')); + $this->assertEquals(1, (int)$this->redis->get('key')); $this->redis->incr('key'); - $this->assertEquals(2, (int)$this->redis->get('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', 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(6, (int)$this->redis->get('key')); - $this->redis->incrBy('key', -1); - $this->assertEquals(5, (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->incr('key', 5); + $this->assertEquals(10, (int)$this->redis->get('key')); - $this->redis->del('key'); + $this->redis->del('key'); + + $this->redis->set('key', 'abc'); - $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->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(2147483648, $this->redis->incrby('key', 2147483648)); } public function testIncrByFloat() From e45e5d05b7f3cbbc8a9e7afd029c4ba18eaf0104 Mon Sep 17 00:00:00 2001 From: Toby Schrapel Date: Mon, 4 Jul 2016 11:39:50 +0100 Subject: [PATCH 0547/1986] Fix typo in README.markdown --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index e9c4925048..de5082edaa 100644 --- a/README.markdown +++ b/README.markdown @@ -246,7 +246,7 @@ persistent equivalents. $redis->pconnect('127.0.0.1', 6379); $redis->pconnect('127.0.0.1'); // port 6379 by default - same connection like before. $redis->pconnect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout and would be another connection than the two before. -$redis->pconnect('127.0.0.1', 6379, 2.5, 'x'); // x is sent as persistent_id and would be another connection the the three before. +$redis->pconnect('127.0.0.1', 6379, 2.5, 'x'); // x is sent as persistent_id and would be another connection than the three before. $redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another connection than the four before. ~~~ From 6fd7bf7bf7d8abf9a2a467ba93744836fa237e69 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 6 Jul 2016 22:23:15 +0300 Subject: [PATCH 0548/1986] This fix issue #867 --- redis_cluster.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index b0567f65c5..3d9facad42 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -388,21 +388,24 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Seeds */ MAKE_STD_ZVAL(z_seeds); array_init(z_seeds); - iptr = estrdup(INI_STR("redis.clusters.seeds")); - sapi_module.treat_data(PARSE_STRING, iptr, z_seeds TSRMLS_CC); + if ((iptr = INI_STR("redis.clusters.seeds")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_seeds TSRMLS_CC); + } if (zend_hash_find(Z_ARRVAL_P(z_seeds), name, name_len+1, (void**)&z_value) != FAILURE) { ht_seeds = Z_ARRVAL_PP(z_value); } else { zval_dtor(z_seeds); efree(z_seeds); zend_throw_exception(redis_cluster_exception_ce, "Couldn't find seeds for cluster", 0 TSRMLS_CC); + return; } /* Connection timeout */ MAKE_STD_ZVAL(z_timeout); array_init(z_timeout); - iptr = estrdup(INI_STR("redis.clusters.timeout")); - sapi_module.treat_data(PARSE_STRING, iptr, z_timeout TSRMLS_CC); + if ((iptr = INI_STR("redis.clusters.timeout")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_timeout TSRMLS_CC); + } if (zend_hash_find(Z_ARRVAL_P(z_timeout), name, name_len+1, (void**)&z_value) != FAILURE) { if (Z_TYPE_PP(z_value) == IS_STRING) { timeout = atof(Z_STRVAL_PP(z_value)); @@ -414,8 +417,9 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Read timeout */ MAKE_STD_ZVAL(z_read_timeout); array_init(z_read_timeout); - iptr = estrdup(INI_STR("redis.clusters.read_timeout")); - sapi_module.treat_data(PARSE_STRING, iptr, z_read_timeout TSRMLS_CC); + if ((iptr = INI_STR("redis.clusters.read_timeout")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_read_timeout TSRMLS_CC); + } if (zend_hash_find(Z_ARRVAL_P(z_read_timeout), name, name_len+1, (void**)&z_value) != FAILURE) { if (Z_TYPE_PP(z_value) == IS_STRING) { read_timeout = atof(Z_STRVAL_PP(z_value)); @@ -427,8 +431,9 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Persistent connections */ MAKE_STD_ZVAL(z_persistent); array_init(z_persistent); - iptr = estrdup(INI_STR("redis.clusters.persistent")); - sapi_module.treat_data(PARSE_STRING, iptr, z_persistent TSRMLS_CC); + if ((iptr = INI_STR("redis.clusters.persistent")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_persistent TSRMLS_CC); + } if (zend_hash_find(Z_ARRVAL_P(z_persistent), name, name_len+1, (void**)&z_value) != FAILURE) { if (Z_TYPE_PP(z_value) == IS_STRING) { persistent = atoi(Z_STRVAL_PP(z_value)); @@ -2991,4 +2996,4 @@ PHP_METHOD(RedisCluster, command) { CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); } -/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ +/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ From c07e86c7399577c0056eaca1ca16f721a6a4dd9d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 6 Jul 2016 23:41:05 +0300 Subject: [PATCH 0549/1986] This fix issue #834 --- redis.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis.c b/redis.c index 6bdf46d498..9def28e7e6 100644 --- a/redis.c +++ b/redis.c @@ -752,6 +752,9 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { == FAILURE) { return FAILURE; + } else if (!persistent) { + persistent_id = NULL; + persistent_id_len = -1; } if (timeout < 0L || timeout > INT_MAX) { From 942f09e4990be59a6a4d0332c5f33464a2aa8026 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 3 Jul 2016 23:48:20 +0300 Subject: [PATCH 0550/1986] Fix memory leeks --- library.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index 19ba71faa3..db3fd56b85 100644 --- a/library.c +++ b/library.c @@ -980,6 +980,7 @@ PHP_REDIS_API zval *redis_parse_info_response(char *response) { cur = pos + 1; pos = strchr(cur, '\r'); if(pos == NULL) { + efree(key); break; } value = emalloc(pos - cur + 1); @@ -1767,6 +1768,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, efree(response); RETURN_TRUE; } + efree(response); RETURN_FALSE; } @@ -2200,7 +2202,6 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, if (rv_free==1) efree(*return_value); #endif return 0; - break; } return 0; } @@ -2454,6 +2455,8 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, break; default: // Protocol error + zval_dtor(z_ret); + efree(z_ret); zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); return FAILURE; From 892e56464b107ebed5dbdad6b8673234778c25e3 Mon Sep 17 00:00:00 2001 From: andyli Date: Fri, 15 Jul 2016 17:28:07 +0800 Subject: [PATCH 0551/1986] support the client to Redis Cluster just having one master --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index a530112ef4..7385f4fe67 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -616,7 +616,7 @@ clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) // Consume the rest of our response if((r = cluster_read_sock_resp(redis_sock, type, len TSRMLS_CC))==NULL || - r->type != TYPE_MULTIBULK || r->elements < 3) + r->type != TYPE_MULTIBULK || r->elements < 1) { if(r) cluster_free_reply(r, 1); return NULL; From 2d6bf938102d7e470d7472bf210396023592df25 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 16 Jul 2016 11:43:35 +0300 Subject: [PATCH 0552/1986] Redis::redis_sock_read_bulk_reply refactoring --- library.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/library.c b/library.c index db3fd56b85..42b49097f2 100644 --- a/library.c +++ b/library.c @@ -465,18 +465,11 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes int offset = 0; size_t got; - char * reply; + char *reply, c[2]; - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { + if (-1 == bytes || -1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return NULL; } - - if (bytes == -1) { - return NULL; - } else { - char c; - int i; - reply = emalloc(bytes+1); while(offset < bytes) { @@ -490,10 +483,7 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes } offset += got; } - for(i = 0; i < 2; i++) { - php_stream_read(redis_sock->stream, &c, 1); - } - } + php_stream_read(redis_sock->stream, c, 2); reply[bytes] = 0; return reply; From a9857d6995a612ce1f5997199724fabb61c4af1b Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 19 Jul 2016 11:11:58 +0200 Subject: [PATCH 0553/1986] Use static declarations for spl_ce_RuntimeException decl This is consistent with the PDO extension, which also resolves the RuntimeException class entry on-demand. Without a static decl, this can conflict with the extern decls in spl_exceptions.h and cause a "missing symbol" error for other extensions depending on those SPL decls. This commit also makes the spl_rte_ce decl in redis_cluster.c static for consistency, although that symbol does not currently conflict with anything in SPL. --- redis.c | 2 +- redis_cluster.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redis.c b/redis.c index 9def28e7e6..e804697531 100644 --- a/redis.c +++ b/redis.c @@ -60,7 +60,7 @@ extern zend_class_entry *redis_cluster_ce; zend_class_entry *redis_ce; zend_class_entry *redis_exception_ce; extern zend_class_entry *redis_cluster_exception_ce; -zend_class_entry *spl_ce_RuntimeException = NULL; +static zend_class_entry *spl_ce_RuntimeException = NULL; extern zend_function_entry redis_array_functions[]; extern zend_function_entry redis_cluster_functions[]; diff --git a/redis_cluster.c b/redis_cluster.c index 3d9facad42..c252122777 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -36,7 +36,7 @@ zend_class_entry *redis_cluster_ce; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; -zend_class_entry *spl_rte_ce = NULL; +static zend_class_entry *spl_rte_ce = NULL; /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) From 6a09e03c8d3fd85ffd4a248c970d4363cd34f9cf Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 19 Jul 2016 11:18:29 +0200 Subject: [PATCH 0554/1986] Remove unused spl_ce_RuntimeException extern --- library.c | 1 - 1 file changed, 1 deletion(-) diff --git a/library.c b/library.c index 42b49097f2..6ac490ef83 100644 --- a/library.c +++ b/library.c @@ -38,7 +38,6 @@ extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; -extern zend_class_entry *spl_ce_RuntimeException; /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { From 24f86c497fb1021bdcd2ae5f8426a81f969e6104 Mon Sep 17 00:00:00 2001 From: cdoco Date: Tue, 19 Jul 2016 17:49:26 +0800 Subject: [PATCH 0555/1986] Fixed method call problem causes session handler to display two times --- redis.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/redis.c b/redis.c index 9def28e7e6..41897e27e1 100644 --- a/redis.c +++ b/redis.c @@ -548,11 +548,6 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5 TSRMLS_CC); zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); - -#ifdef PHP_SESSION - php_session_register_module(&ps_mod_redis); - php_session_register_module(&ps_mod_redis_cluster); -#endif } /** @@ -623,6 +618,11 @@ PHP_MINIT_FUNCTION(redis) /* Add shared class constants to Redis and RedisCluster objects */ add_class_constants(redis_ce, 0 TSRMLS_CC); add_class_constants(redis_cluster_ce, 1 TSRMLS_CC); + +#ifdef PHP_SESSION + php_session_register_module(&ps_mod_redis); + php_session_register_module(&ps_mod_redis_cluster); +#endif return SUCCESS; } From f8200af90df7b66c8479cc699a6eaf0511791bc1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 7 Jul 2016 00:06:24 +0300 Subject: [PATCH 0556/1986] Redis::close refactoring --- redis.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/redis.c b/redis.c index 46decb0240..3ec454f49f 100644 --- a/redis.c +++ b/redis.c @@ -830,22 +830,11 @@ PHP_METHOD(Redis, bitpos) */ PHP_METHOD(Redis, close) { - zval *object; - RedisSock *redis_sock = NULL; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } + RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - - if (redis_sock_disconnect(redis_sock TSRMLS_CC)) { + if (redis_sock && redis_sock_disconnect(redis_sock TSRMLS_CC)) { RETURN_TRUE; } - RETURN_FALSE; } /* }}} */ @@ -3980,4 +3969,4 @@ PHP_METHOD(Redis, georadiusbymember) { REDIS_PROCESS_CMD(georadiusbymember, redis_read_variant_reply); } -/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ +/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ From 15cd86c8a06f3bd6423da1389cce2cb89172eb9a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 20 Jul 2016 23:26:16 +0300 Subject: [PATCH 0557/1986] Partial fix for #836 + additional checking params --- redis.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/redis.c b/redis.c index 3ec454f49f..4a40dc8b4b 100644 --- a/redis.c +++ b/redis.c @@ -1795,7 +1795,7 @@ PHP_METHOD(Redis, select) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if (dbNumber < 0 || redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } @@ -2681,7 +2681,7 @@ PHP_METHOD(Redis, slaveof) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if (port < 0 || redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } @@ -3707,13 +3707,13 @@ PHP_METHOD(Redis, rawcommand) { zval **z_args; /* Sanity check on arguments */ - z_args = emalloc(argc * sizeof(zval*)); if (argc < 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Must pass at least one command keyword"); - efree(z_args); RETURN_FALSE; - } else if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + } + z_args = emalloc(argc * sizeof(zval*)); + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal PHP error parsing arguments"); efree(z_args); From bdcdd2aa33d0a96f973b97f20043690bdc4acbef Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 29 Jul 2016 09:30:20 -0700 Subject: [PATCH 0558/1986] Allow both long and strings that are longs for zrangebyscore offset/limit Fixes #906 --- redis_commands.c | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 84e3108c24..b5469fa563 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -500,6 +500,25 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, #define IS_LIMIT_ARG(s, l) \ (l == sizeof("limit") && !strncasecmp(s,"limit",sizeof("limit"))) +/* Helper to get the long value stored in a zval, whether it's actually stored + * as a long or is a string that contains a long */ +static int zval_get_long(zval *zv, long *lval) +{ + /* If it's already a long, just set and return success */ + if (Z_TYPE_P(zv) == IS_LONG) { + *lval = Z_LVAL_P(zv); + return SUCCESS; + } + + /* If our zval isn't a string, or doesn't translate into a long, fail */ + if (Z_TYPE_P(zv) != IS_STRING || is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), lval, NULL, 0) != IS_LONG) { + return FAILURE; + } + + /* Success */ + return SUCCESS; +} + int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, void **ctx) @@ -509,7 +528,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *start, *end, *optkey; int start_len, end_len; int has_limit=0, type; - long limit_low, limit_high; + long offset, count; zval *z_opt=NULL, **z_ele; unsigned long idx; unsigned int optlen; @@ -544,13 +563,19 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { HashTable *htlimit = Z_ARRVAL_PP(z_ele); zval **zoff, **zcnt; + + /* We need two arguments (offset and count) */ if (zend_hash_index_find(htlimit,0,(void**)&zoff)==SUCCESS && - zend_hash_index_find(htlimit,1,(void**)&zcnt)==SUCCESS && - Z_TYPE_PP(zoff) == IS_LONG && Z_TYPE_PP(zcnt) == IS_LONG) + zend_hash_index_find(htlimit,1,(void**)&zcnt)==SUCCESS) { - has_limit = 1; - limit_low = Z_LVAL_PP(zoff); - limit_high = Z_LVAL_PP(zcnt); + /* Set our limit if we can get valid longs from both args */ + has_limit = zval_get_long(*zoff, &offset) == SUCCESS && + zval_get_long(*zcnt, &count) == SUCCESS; + + /* Inform the user there is a problem if we don't have a limit */ + if (!has_limit) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset and limit must be long values. Ignoring."); + } } } } @@ -564,8 +589,8 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(*withscores) { if(has_limit) { *cmd_len = redis_cmd_format_static(cmd, kw, "ssssdds", key, key_len, - start, start_len, end, end_len, "LIMIT", 5, limit_low, - limit_high, "WITHSCORES", 10); + start, start_len, end, end_len, "LIMIT", 5, offset, + count, "WITHSCORES", 10); } else { *cmd_len = redis_cmd_format_static(cmd, kw, "ssss", key, key_len, start, start_len, end, end_len, "WITHSCORES", 10); @@ -573,8 +598,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else { if(has_limit) { *cmd_len = redis_cmd_format_static(cmd, kw, "ssssdd", key, key_len, - start, start_len, end, end_len, "LIMIT", 5, limit_low, - limit_high); + start, start_len, end, end_len, "LIMIT", 5, offset, count); } else { *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, start, start_len, end, end_len); @@ -3153,7 +3177,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, switch(option) { case REDIS_OPT_SERIALIZER: val_long = atol(val_str); - test_val = val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP; + test_val = val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP; #ifdef HAVE_REDIS_IGBINARY test_val = test_val || val_long == REDIS_SERIALIZER_IGBINARY; #endif From c7a8c3bd8287350d19d2d850706b2633b108f924 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 30 Jul 2016 10:15:35 +0300 Subject: [PATCH 0559/1986] This fixes issue #908 --- redis_array_impl.c | 62 +++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 93e4724c95..e0caffd89a 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -187,6 +187,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zend_bool b_lazy_connect = 0; double d_connect_timeout = 0; HashTable *hHosts = NULL, *hPrev = NULL; + size_t name_len = strlen(name) + 1; + char *iptr; /* find entry */ if(!ra_find_name(name)) @@ -195,24 +197,30 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find hosts */ MAKE_STD_ZVAL(z_params_hosts); array_init(z_params_hosts); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.hosts")), z_params_hosts TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_hosts), name, strlen(name) + 1, (void **) &z_hosts) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.hosts")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_hosts TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_hosts), name, name_len, (void **) &z_hosts) != FAILURE) { hHosts = Z_ARRVAL_PP(z_hosts); } /* find previous hosts */ MAKE_STD_ZVAL(z_params_prev); array_init(z_params_prev); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.previous")), z_params_prev TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_prev), name, strlen(name) + 1, (void **) &z_prev) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.previous")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_prev TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_prev), name, name_len, (void **) &z_prev) != FAILURE) { hPrev = Z_ARRVAL_PP(z_prev); } /* find function */ MAKE_STD_ZVAL(z_params_funs); array_init(z_params_funs); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.functions")), z_params_funs TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_funs), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.functions")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_funs TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_funs), name, name_len, (void **) &z_data_pp) != FAILURE) { MAKE_STD_ZVAL(z_fun); *z_fun = **z_data_pp; zval_copy_ctor(z_fun); @@ -221,8 +229,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find distributor */ MAKE_STD_ZVAL(z_params_funs); array_init(z_params_funs); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.distributor")), z_params_funs TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_funs), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.distributor")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_funs TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_funs), name, name_len, (void **) &z_data_pp) != FAILURE) { MAKE_STD_ZVAL(z_dist); *z_dist = **z_data_pp; zval_copy_ctor(z_dist); @@ -231,8 +241,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find index option */ MAKE_STD_ZVAL(z_params_index); array_init(z_params_index); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.index")), z_params_index TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_index), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.index")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_index TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_index), name, name_len, (void **) &z_data_pp) != FAILURE) { if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { b_index = 1; } @@ -241,8 +253,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find autorehash option */ MAKE_STD_ZVAL(z_params_autorehash); array_init(z_params_autorehash); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.autorehash")), z_params_autorehash TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_autorehash), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.autorehash")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_autorehash TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_autorehash), name, name_len, (void **) &z_data_pp) != FAILURE) { if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { b_autorehash = 1; } @@ -251,8 +265,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find retry interval option */ MAKE_STD_ZVAL(z_params_retry_interval); array_init(z_params_retry_interval); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.retryinterval")), z_params_retry_interval TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_retry_interval), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.retryinterval")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_retry_interval TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_retry_interval), name, name_len, (void **) &z_data_pp) != FAILURE) { if (Z_TYPE_PP(z_data_pp) == IS_LONG || Z_TYPE_PP(z_data_pp) == IS_STRING) { if (Z_TYPE_PP(z_data_pp) == IS_LONG) { l_retry_interval = Z_LVAL_PP(z_data_pp); @@ -266,8 +282,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find pconnect option */ MAKE_STD_ZVAL(z_params_pconnect); array_init(z_params_pconnect); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.pconnect")), z_params_pconnect TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_pconnect), name, strlen(name) + 1, (void**) &z_data_pp) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.pconnect")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_pconnect TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_pconnect), name, name_len, (void**) &z_data_pp) != FAILURE) { if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { b_pconnect = 1; } @@ -276,8 +294,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find lazy connect option */ MAKE_STD_ZVAL(z_params_lazy_connect); array_init(z_params_lazy_connect); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.lazyconnect")), z_params_lazy_connect TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_lazy_connect), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.lazyconnect")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_lazy_connect TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_lazy_connect), name, name_len, (void **) &z_data_pp) != FAILURE) { if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { b_lazy_connect = 1; } @@ -286,8 +306,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find connect timeout option */ MAKE_STD_ZVAL(z_params_connect_timeout); array_init(z_params_connect_timeout); - sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.connecttimeout")), z_params_connect_timeout TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(z_params_connect_timeout), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) { + if ((iptr = INI_STR("redis.arrays.connecttimeout")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_connect_timeout TSRMLS_CC); + } + if (zend_hash_find(Z_ARRVAL_P(z_params_connect_timeout), name, name_len, (void **) &z_data_pp) != FAILURE) { if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE || Z_TYPE_PP(z_data_pp) == IS_STRING || Z_TYPE_PP(z_data_pp) == IS_LONG) From e8fb3b780ee1eeabdc60e28b7c17acbd5411e510 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 30 Jul 2016 23:43:59 +0300 Subject: [PATCH 0560/1986] replace emalloc+memcpy with estrndup --- redis_array_impl.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index e0caffd89a..2b7564ac88 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -413,9 +413,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR } *out_len = Z_STRLEN(z_ret); - out = emalloc(*out_len + 1); - out[*out_len] = 0; - memcpy(out, Z_STRVAL(z_ret), *out_len); + out = estrndup(Z_STRVAL(z_ret), *out_len); zval_dtor(&z_ret); return out; @@ -424,7 +422,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR static char * ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS_DC) { - char *start, *end, *out; + char *start, *end; *out_len = key_len; if(ra->z_fun) @@ -440,11 +438,7 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS /* found substring */ *out_len = end - start - 1; - out = emalloc(*out_len + 1); - out[*out_len] = 0; - memcpy(out, start+1, *out_len); - - return out; + return estrndup(start + 1, *out_len); } /* call userland key distributor function */ @@ -764,10 +758,8 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con key_len = Z_STRLEN_PP(z_data_pp); /* copy key and length */ - (*keys)[i] = emalloc(1 + key_len); - memcpy((*keys)[i], key, key_len); + (*keys)[i] = estrndup(key, key_len); (*key_lens)[i] = key_len; - (*keys)[i][key_len] = 0; /* null-terminate string */ } /* cleanup */ From 5778f03a9e86237592eeaa481e0d5b1253ee737f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 3 Aug 2016 23:43:35 +0300 Subject: [PATCH 0561/1986] memory leaks --- redis_session.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis_session.c b/redis_session.c index 21d3ff524b..fc34b1c1ea 100644 --- a/redis_session.c +++ b/redis_session.c @@ -275,6 +275,9 @@ PS_OPEN_FUNC(redis) if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { php_url_free(url); + if (persistent_id) efree(persistent_id); + if (prefix) efree(prefix); + if (auth) efree(auth); redis_pool_free(pool TSRMLS_CC); PS_SET_MOD_DATA(NULL); return FAILURE; From 9fc094ad07c957273e1bd69a17ba350ddcac403f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 31 Jul 2016 23:32:23 +0300 Subject: [PATCH 0562/1986] Travis CI --- .travis.yml | 12 ++++++++++++ redis_commands.c | 4 ++++ tests/RedisTest.php | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..96c534ac11 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +sudo: required +language: c +addons: + apt: + packages: + - php5-dev + - php5-cli +services: redis-server +before_install: phpize +install: ./configure --prefix=/usr && sudo make install +before_script: echo 'extension = redis.so' | sudo tee -a /etc/php5/cli/php.ini +script: php tests/TestRedis.php diff --git a/redis_commands.c b/redis_commands.c index b5469fa563..8fe71b0b76 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1322,7 +1322,11 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, CMD_SET_SLOT(slot,key,key_len); if(key_free) efree(key); +#if PHP_VERSION_ID >= 50400 if(val_free) str_efree(val); +#else + if(val_free) efree(val); +#endif return SUCCESS; } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 55705c001f..940add7191 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4801,7 +4801,7 @@ public function testGeoAdd() { } /* Add them again, all at once */ - $args = ['geokey']; + $args = Array('geokey'); foreach ($this->cities as $city => $longlat) { $args = array_merge($args, Array($longlat[0], $longlat[1], $city)); } From 97b259a0456ebad100fdd34408ba0fd40ff775bc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Aug 2016 12:40:02 -0700 Subject: [PATCH 0563/1986] Fix documentation typo Document raw command --- README.markdown | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 1dfbef6589..12602fd89b 100644 --- a/README.markdown +++ b/README.markdown @@ -23,6 +23,7 @@ You can send comments, patches, questions [here on github](https://github.com/ph * [Lists](#lists) * [Sets](#sets) * [Sorted sets](#sorted-sets) + * [Geocoding](#geocoding) * [Pub/sub](#pubsub) * [Transactions](#transactions) * [Scripting](#scripting) @@ -2998,7 +2999,37 @@ _**Description**_: A command allowing you to get information on the Redis pub/su $redis->pubSub("channels"); /*All channels */ $redis->pubSub("channels", "*pattern*"); /* Just channels matching your pattern */ $redis->pubSub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/ -$redsi->pubSub("numpat"); /* Get the number of pattern subscribers */ +$redis->pubSub("numpat"); /* Get the number of pattern subscribers */ +``` + +~~~ + +## Generic +1. [rawCommand](#rawcommand) - Execute any generic command against the server. + +### rawCommand +----- +_**Description**_: A method to execute any arbitrary command against the a Redis server + +##### *Parameters* +This method is variadic and takes a dynamic number of arguments of various types (string, long, double), but must be passed at least one argument (the command keyword itself). + +##### *Return value* +The return value can be various types depending on what the server itself returns. No post processing is done to the returned value and must be handled by the client code. + +##### *Example* +```php +/* Returns: true */ +$redis->rawCommand("set", "foo", "bar"); + +/* Returns: "bar" */ +$redis->rawCommand("get", "foo"); + +/* Returns: 3 */ +$redis->rawCommand("rpush", "mylist", "one", 2, 3.5)); + +/* Returns: ["one", "2", "3.5000000000000000"] */ +$redis->rawCommand("lrange", "mylist", 0, -1); ``` ## Transactions From 4b335de369b147b6b904cb53e7e93ab60ba75aaf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Aug 2016 12:46:36 -0700 Subject: [PATCH 0564/1986] Fix formatting --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 12602fd89b..2f0b49d69f 100644 --- a/README.markdown +++ b/README.markdown @@ -3000,7 +3000,7 @@ $redis->pubSub("channels"); /*All channels */ $redis->pubSub("channels", "*pattern*"); /* Just channels matching your pattern */ $redis->pubSub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/ $redis->pubSub("numpat"); /* Get the number of pattern subscribers */ -``` + ~~~ @@ -3019,6 +3019,7 @@ The return value can be various types depending on what the server itself return ##### *Example* ```php + /* Returns: true */ $redis->rawCommand("set", "foo", "bar"); From 141893ea26bc473c315b12411f0708b5d388353e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Aug 2016 12:48:39 -0700 Subject: [PATCH 0565/1986] More formatting :/ --- README.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/README.markdown b/README.markdown index 2f0b49d69f..c82df85acc 100644 --- a/README.markdown +++ b/README.markdown @@ -3019,7 +3019,6 @@ The return value can be various types depending on what the server itself return ##### *Example* ```php - /* Returns: true */ $redis->rawCommand("set", "foo", "bar"); From d67301dafa04202a2d439339f4a9de47b8be2cf7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Aug 2016 12:54:34 -0700 Subject: [PATCH 0566/1986] Travis-CI widget. --- README.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.markdown b/README.markdown index c82df85acc..4eb760f222 100644 --- a/README.markdown +++ b/README.markdown @@ -1,5 +1,7 @@ # PhpRedis +[![Build Status](https://travis-ci.org/phpredis/phpredis.svg?branch=develop)](https://travis-ci.org/phpredis/phpredis) + The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). This code has been developed and maintained by Owlient from November 2009 to March 2011. From a36b72c7d14dc7265a653ed64ddf637be3b392c2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 5 Aug 2016 00:03:56 +0300 Subject: [PATCH 0567/1986] Travis CI: testing on different versions of php --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 96c534ac11..a81420c9a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,12 @@ sudo: required -language: c -addons: - apt: - packages: - - php5-dev - - php5-cli +language: php +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 services: redis-server before_install: phpize install: ./configure --prefix=/usr && sudo make install -before_script: echo 'extension = redis.so' | sudo tee -a /etc/php5/cli/php.ini +before_script: echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini script: php tests/TestRedis.php From 670146b7c8809a5c47b78b3684e0dd2436af8cb4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 8 Aug 2016 10:46:19 +0300 Subject: [PATCH 0568/1986] REDIS_STREAM_CLOSE_MARK_FAILED macro --- common.h | 7 +++++ library.c | 76 +++++++++++-------------------------------------------- 2 files changed, 22 insertions(+), 61 deletions(-) diff --git a/common.h b/common.h index ed090a7ac0..d0007f5963 100644 --- a/common.h +++ b/common.h @@ -228,6 +228,13 @@ typedef enum _PUBSUB_TYPE { } \ REDIS_PROCESS_RESPONSE_CLOSURE(resp_func,ctx); +#define REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock) \ + redis_stream_close(redis_sock TSRMLS_CC); \ + redis_sock->stream = NULL; \ + redis_sock->mode = ATOMIC; \ + redis_sock->status = REDIS_SOCK_STATUS_FAILED; \ + redis_sock->watching = 0 + /* Extended SET argument detection */ #define IS_EX_ARG(a) \ ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') diff --git a/library.c b/library.c index 6ac490ef83..95ea25e722 100644 --- a/library.c +++ b/library.c @@ -159,11 +159,7 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { /* too many failures */ if(redis_sock->stream) { /* close stream if still here */ - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->watching = 0; + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); } if(!no_throw) { zend_throw_exception(redis_exception_ce, "Connection lost", @@ -432,11 +428,7 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return NULL; @@ -502,11 +494,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return NULL; @@ -528,8 +516,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D return NULL; case '$': *buf_len = atoi(inbuf + 1); - resp = redis_sock_read_bulk_reply(redis_sock, *buf_len TSRMLS_CC); - return resp; + return redis_sock_read_bulk_reply(redis_sock, *buf_len TSRMLS_CC); case '*': /* For null multi-bulk replies (like timeouts from brpoplpush): */ @@ -544,10 +531,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D /* :123\r\n */ *buf_len = strlen(inbuf) - 2; if(*buf_len >= 2) { - resp = emalloc(1+*buf_len); - memcpy(resp, inbuf, *buf_len); - resp[*buf_len] = 0; - return resp; + return estrndup(inbuf, *buf_len); } default: zend_throw_exception_ex( @@ -1061,14 +1045,10 @@ PHP_REDIS_API zval* redis_parse_client_list_response(char *response) { have a key and value, but check anyway. */ if(kpos && vpos) { /* Allocate, copy in our key */ - key = emalloc(klen + 1); - strncpy(key, kpos, klen); - key[klen] = 0; + key = estrndup(kpos, klen); /* Allocate, copy in our value */ - value = emalloc(p-lpos+1); - strncpy(value,lpos,p-lpos+1); - value[p-lpos]=0; + value = estrndup(lpos, p - lpos); /* Treat numbers as numbers, strings as strings */ is_numeric = 1; @@ -1312,11 +1292,7 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return -1; } @@ -1560,18 +1536,12 @@ redis_sock_create(char *host, int host_len, unsigned short port, double timeout, redis_sock->retry_interval = retry_interval * 1000; redis_sock->persistent = persistent; redis_sock->lazy_connect = lazy_connect; + redis_sock->persistent_id = NULL; if(persistent_id) { - size_t persistent_id_len = strlen(persistent_id); - redis_sock->persistent_id = ecalloc(persistent_id_len + 1, 1); - memcpy(redis_sock->persistent_id, persistent_id, persistent_id_len); - } else { - redis_sock->persistent_id = NULL; + redis_sock->persistent_id = estrdup(persistent_id); } - memcpy(redis_sock->host, host, host_len); - redis_sock->host[host_len] = '\0'; - redis_sock->port = port; redis_sock->timeout = timeout; redis_sock->read_timeout = timeout; @@ -1809,11 +1779,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return -1; @@ -1860,11 +1826,7 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return -1; } @@ -1947,11 +1909,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc return -1; } if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return -1; } @@ -2231,11 +2189,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, == NULL) { // Close, put our socket state into error - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - redis_sock->status = REDIS_SOCK_STATUS_FAILED; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); // Throw a read error exception zend_throw_exception(redis_exception_ce, "read error on connection", @@ -2465,4 +2419,4 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return 0; } -/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ +/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ From 0b2b809a57a63c297f603251615ec50f72b7bf35 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 14 Aug 2016 19:53:35 +0300 Subject: [PATCH 0569/1986] integer_length optimization Based on the [article](https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920) --- library.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/library.c b/library.c index 95ea25e722..778d72a212 100644 --- a/library.c +++ b/library.c @@ -556,18 +556,20 @@ void add_constant_long(zend_class_entry *ce, char *name, int value) { int integer_length(int i) { - int sz = 0; - int ci = abs(i); - while (ci > 0) { - ci /= 10; + int sz = 1; + + if (i < 0) { /* allow for neg sign as well. */ + i = -i; sz++; } - if (i == 0) { /* log 0 doesn't make sense. */ - sz = 1; - } else if (i < 0) { /* allow for neg sign as well. */ - sz++; + for (;;) { + if (i < 10) return sz; + if (i < 100) return sz + 1; + if (i < 1000) return sz + 2; + // Skip ahead by 4 orders of magnitude + i /= 10000U; + sz += 4; } - return sz; } int From 9950a11991ab4836669e8ea4d4c2d458d2fa7a6b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 15 Aug 2016 23:28:06 +0300 Subject: [PATCH 0570/1986] redis_array_impl.c refactoring --- redis_array_impl.c | 34 ++++++++-------------------------- redis_commands.c | 2 +- redis_session.c | 2 -- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 2b7564ac88..25017a29eb 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -425,17 +425,11 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS char *start, *end; *out_len = key_len; - if(ra->z_fun) + if(ra->z_fun) { return ra_call_extractor(ra, key, key_len, out_len TSRMLS_CC); - - /* look for '{' */ - start = strchr(key, '{'); - if(!start) return estrndup(key, key_len); - - /* look for '}' */ - end = strchr(start + 1, '}'); - if(!end) return estrndup(key, key_len); - + } else if ((start = strchr(key, '{')) == NULL || (end = strchr(start + 1, '}')) == NULL) { + return estrndup(key, key_len); + } /* found substring */ *out_len = end - start - 1; return estrndup(start + 1, *out_len); @@ -768,17 +762,6 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con return count; } -static long -ra_rehash_scan_index(zval *z_redis, char ***keys, int **key_lens TSRMLS_DC) { - return ra_rehash_scan(z_redis, keys, key_lens, "SMEMBERS", PHPREDIS_INDEX_NAME TSRMLS_CC); -} - -/* list keys using KEYS command */ -static long -ra_rehash_scan_keys(zval *z_redis, char ***keys, int **key_lens TSRMLS_DC) { - return ra_rehash_scan(z_redis, keys, key_lens, "KEYS", "*" TSRMLS_CC); -} - /* run TYPE to find the type */ static zend_bool ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long *res TSRMLS_DC) { @@ -1254,9 +1237,9 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool /* list all keys */ if(b_index) { - count = ra_rehash_scan_index(z_redis, &keys, &key_lens TSRMLS_CC); + count = ra_rehash_scan(z_redis, &keys, &key_lens, "SMEMBERS", PHPREDIS_INDEX_NAME TSRMLS_CC); } else { - count = ra_rehash_scan_keys(z_redis, &keys, &key_lens TSRMLS_CC); + count = ra_rehash_scan(z_redis, &keys, &key_lens, "KEYS", "*" TSRMLS_CC); } /* callback */ @@ -1274,12 +1257,11 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool /* php_printf("move [%s] from [%s] to [%s]\n", keys[i], hostname, ra->hosts[target_pos]); */ ra_move_key(keys[i], key_lens[i], z_redis, z_target TSRMLS_CC); } - } - /* cleanup */ - for(i = 0; i < count; ++i) { + /* cleanup */ efree(keys[i]); } + efree(keys); efree(key_lens); } diff --git a/redis_commands.c b/redis_commands.c index 8fe71b0b76..5d72e1824b 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3309,4 +3309,4 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, } } -/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ +/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/redis_session.c b/redis_session.c index fc34b1c1ea..03d0766a5a 100644 --- a/redis_session.c +++ b/redis_session.c @@ -20,8 +20,6 @@ +----------------------------------------------------------------------+ */ -#include "common.h" - #ifdef HAVE_CONFIG_H #include "config.h" #endif From dcca870b1a748b57972c1ab058746cd817ba4b2c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 18 Aug 2016 18:14:27 -0700 Subject: [PATCH 0571/1986] Fix for linker error Reorder inclusion of common.h in redis_session.c. Fix a couple of unused variable warnings Potential fix for #929 --- library.c | 1 - redis_cluster.c | 1 - redis_session.c | 3 ++- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index 778d72a212..4f9c312350 100644 --- a/library.c +++ b/library.c @@ -486,7 +486,6 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) { char inbuf[1024]; - char *resp = NULL; size_t err_len; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { diff --git a/redis_cluster.c b/redis_cluster.c index c252122777..0968b7db46 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -36,7 +36,6 @@ zend_class_entry *redis_cluster_ce; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; -static zend_class_entry *spl_rte_ce = NULL; /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) diff --git a/redis_session.c b/redis_session.c index 03d0766a5a..a698ac16b2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -20,12 +20,13 @@ +----------------------------------------------------------------------+ */ +#include "common.h" + #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef PHP_SESSION -#include "common.h" #include "ext/standard/info.h" #include "php_redis.h" #include "redis_session.h" From afc9d7614f9d929ba0d64c582c2bc67587f02629 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 19 Aug 2016 09:12:12 -0700 Subject: [PATCH 0572/1986] Re add custom exception into develop after fixing linker bug --- redis_cluster.c | 1 + 1 file changed, 1 insertion(+) diff --git a/redis_cluster.c b/redis_cluster.c index 0968b7db46..c252122777 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -36,6 +36,7 @@ zend_class_entry *redis_cluster_ce; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; +static zend_class_entry *spl_rte_ce = NULL; /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) From 6b9c5a5132f8a9784ba253d514164b3ecc421a4c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 22 Aug 2016 15:40:43 +0300 Subject: [PATCH 0573/1986] pipeline/exec refactoring --- common.h | 1 + redis.c | 87 +++++++++++++++++++++----------------------------------- 2 files changed, 34 insertions(+), 54 deletions(-) diff --git a/common.h b/common.h index d0007f5963..2cd22dd0d9 100644 --- a/common.h +++ b/common.h @@ -97,6 +97,7 @@ typedef enum _PUBSUB_TYPE { #define IF_MULTI_OR_PIPELINE() if(redis_sock->mode == MULTI || redis_sock->mode == PIPELINE) #define IF_PIPELINE() if(redis_sock->mode == PIPELINE) +#define IF_NOT_PIPELINE() if(redis_sock->mode != PIPELINE) #define IF_NOT_MULTI() if(redis_sock->mode != MULTI) #define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC) #define IF_ATOMIC() if(redis_sock->mode == ATOMIC) diff --git a/redis.c b/redis.c index 4a40dc8b4b..7c0c2a809e 100644 --- a/redis.c +++ b/redis.c @@ -2389,15 +2389,13 @@ PHP_METHOD(Redis, exec) char *cmd; int cmd_len; zval *object; - struct request_item *ri; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "O", &object, redis_ce) == FAILURE || + redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0 + ) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } IF_MULTI() { cmd_len = redis_cmd_format_static(&cmd, "EXEC", ""); @@ -2425,47 +2423,30 @@ PHP_METHOD(Redis, exec) IF_PIPELINE() { char *request = NULL; - int total = 0; - int offset = 0; + int total = 0, offset = 0; + struct request_item *ri; /* compute the total request size */ for(ri = redis_sock->pipeline_head; ri; ri = ri->next) { total += ri->request_size; } - if(total) { - request = malloc(total); - } - - /* concatenate individual elements one by one in the target buffer */ - for(ri = redis_sock->pipeline_head; ri; ri = ri->next) { - memcpy(request + offset, ri->request_str, ri->request_size); - offset += ri->request_size; - } - - if(request != NULL) { - if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0) { - free(request); - free_reply_callbacks(object, redis_sock); - redis_sock->mode = ATOMIC; - RETURN_FALSE; + if (total) { + request = emalloc(total); + /* concatenate individual elements one by one in the target buffer */ + for (ri = redis_sock->pipeline_head; ri; ri = ri->next) { + memcpy(request + offset, ri->request_str, ri->request_size); + offset += ri->request_size; } - free(request); + if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0 || + redis_sock_read_multibulk_pipeline_reply( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0 + ) { + ZVAL_FALSE(return_value); + } + efree(request); } else { - redis_sock->mode = ATOMIC; - free_reply_callbacks(object, redis_sock); - - /* Empty array when no command was run. */ - array_init(return_value); - return; - } - - if (redis_sock_read_multibulk_pipeline_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock) < 0) - { - redis_sock->mode = ATOMIC; - free_reply_callbacks(object, redis_sock); - RETURN_FALSE; + /* Empty array when no command was run. */ + array_init(return_value); } redis_sock->mode = ATOMIC; free_reply_callbacks(object, redis_sock); @@ -2515,23 +2496,21 @@ PHP_METHOD(Redis, pipeline) RedisSock *redis_sock; zval *object; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_ce) == FAILURE) { - RETURN_FALSE; - } - - /* if the flag is activated, send the command, the reply will be "QUEUED" - * or -ERR */ - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + "O", &object, redis_ce) == FAILURE || + redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0 + ) { RETURN_FALSE; } - redis_sock->mode = PIPELINE; - /* NB : we keep the function fold, to detect the last function. - * We need the response format of the n - 1 command. So, we can delete - * when n > 2, the { 1 .. n - 2} commands */ - free_reply_callbacks(getThis(), redis_sock); + IF_NOT_PIPELINE() { + redis_sock->mode = PIPELINE; + /* NB : we keep the function fold, to detect the last function. + * We need the response format of the n - 1 command. So, we can delete + * when n > 2, the { 1 .. n - 2} commands */ + free_reply_callbacks(getThis(), redis_sock); + } RETURN_ZVAL(getThis(), 1, 0); } @@ -3969,4 +3948,4 @@ PHP_METHOD(Redis, georadiusbymember) { REDIS_PROCESS_CMD(georadiusbymember, redis_read_variant_reply); } -/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ +/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ From 30e69db7cadf31351d016b5476c11420751d6cf8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 24 Aug 2016 22:06:15 +0300 Subject: [PATCH 0574/1986] Remove duplicate prototypes + trivial fixes --- common.h | 14 ++++++-------- library.c | 1 - library.h | 5 ----- redis.c | 3 +-- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/common.h b/common.h index 2cd22dd0d9..28548d65e8 100644 --- a/common.h +++ b/common.h @@ -101,7 +101,7 @@ typedef enum _PUBSUB_TYPE { #define IF_NOT_MULTI() if(redis_sock->mode != MULTI) #define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC) #define IF_ATOMIC() if(redis_sock->mode == ATOMIC) -#define ELSE_IF_MULTI() else if(redis_sock->mode == MULTI) { \ +#define ELSE_IF_MULTI() else IF_MULTI() { \ if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) { \ RETURN_ZVAL(getThis(), 1, 0);\ } else { \ @@ -161,7 +161,7 @@ typedef enum _PUBSUB_TYPE { } #define REDIS_ELSE_IF_MULTI(function, closure_context) \ - else if(redis_sock->mode == MULTI) { \ + else IF_MULTI() { \ if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ REDIS_SAVE_CALLBACK(function, closure_context); \ RETURN_ZVAL(getThis(), 1, 0);\ @@ -177,14 +177,12 @@ typedef enum _PUBSUB_TYPE { } #define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \ - IF_MULTI_OR_ATOMIC() { \ - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \ - efree(cmd); \ - }\ IF_PIPELINE() { \ PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \ - efree(cmd); \ - } + } else { \ + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \ + } \ + efree(cmd); #define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ REDIS_ELSE_IF_MULTI(function, closure_context) \ diff --git a/library.c b/library.c index 778d72a212..fd33bb6ae1 100644 --- a/library.c +++ b/library.c @@ -8,7 +8,6 @@ #include /* TCP_NODELAY */ #include #endif -#include #include #ifdef HAVE_REDIS_IGBINARY #include "igbinary/igbinary.h" diff --git a/library.h b/library.h index f7908d43d1..953f91f858 100644 --- a/library.h +++ b/library.h @@ -73,11 +73,6 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len); PHP_REDIS_API int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC); -PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); -PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); -PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); - - /* * Variant Read methods, mostly to implement eval */ diff --git a/redis.c b/redis.c index 7c0c2a809e..5e49dc3188 100644 --- a/redis.c +++ b/redis.c @@ -37,7 +37,6 @@ #include "ext/session/php_session.h" #endif -#include #include #include @@ -687,7 +686,7 @@ PHP_METHOD(Redis,__destruct) { } // If we think we're in MULTI mode, send a discard - if(redis_sock->mode == MULTI) { + IF_MULTI() { // Discard any multi commands, and free any callbacks that have been // queued send_discard_static(redis_sock TSRMLS_CC); From 7c9824dbbcc599fea6fe29b9077db815587dc990 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 6 Sep 2016 11:54:08 +0300 Subject: [PATCH 0575/1986] ZEND_HASH_FOREACH_VAL macro --- common.h | 9 +++++++ redis.c | 34 ++++++------------------ redis_array.c | 33 +++++++++--------------- redis_array_impl.c | 10 +++----- redis_cluster.c | 8 ++---- redis_commands.c | 64 ++++++++++++---------------------------------- 6 files changed, 51 insertions(+), 107 deletions(-) diff --git a/common.h b/common.h index 28548d65e8..643a565f57 100644 --- a/common.h +++ b/common.h @@ -5,6 +5,15 @@ #ifndef REDIS_COMMON_H #define REDIS_COMMON_H +#define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ + HashPosition _hpos; \ + for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ + zend_hash_get_current_data_ex(ht, (void **) &_val, &_hpos) == SUCCESS; \ + zend_hash_move_forward_ex(ht, &_hpos) \ + ) + +#define ZEND_HASH_FOREACH_END() } while(0) + /* NULL check so Eclipse doesn't go crazy */ #ifndef NULL #define NULL ((void *) 0) diff --git a/redis.c b/redis.c index 5e49dc3188..6351bce972 100644 --- a/redis.c +++ b/redis.c @@ -969,7 +969,6 @@ PHP_METHOD(Redis, getMultiple) { zval *object, *z_args, **z_ele; HashTable *hash; - HashPosition ptr; RedisSock *redis_sock; smart_str cmd = {0}; int arg_count; @@ -997,10 +996,7 @@ PHP_METHOD(Redis, getMultiple) redis_cmd_init_sstr(&cmd, arg_count, "MGET", 4); /* Iterate through and grab our keys */ - for(zend_hash_internal_pointer_reset_ex(hash, &ptr); - zend_hash_get_current_data_ex(hash, (void**)&z_ele, &ptr) == SUCCESS; - zend_hash_move_forward_ex(hash, &ptr)) - { + ZEND_HASH_FOREACH_VAL(hash, z_ele) { char *key; int key_len, key_free; zval *z_tmp = NULL; @@ -1034,7 +1030,7 @@ PHP_METHOD(Redis, getMultiple) efree(z_tmp); z_tmp = NULL; } - } + } ZEND_HASH_FOREACH_END(); /* Kick off our command */ REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); @@ -2550,7 +2546,6 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, { zval *object, *array, **data; HashTable *arr_hash; - HashPosition pointer; RedisSock *redis_sock; char *cmd = "", *old_cmd = NULL; int cmd_len, array_count; @@ -2573,11 +2568,7 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } - for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); - zend_hash_get_current_data_ex(arr_hash, (void**) &data, - &pointer) == SUCCESS; - zend_hash_move_forward_ex(arr_hash, &pointer)) { - + ZEND_HASH_FOREACH_VAL(arr_hash, data) { if (Z_TYPE_PP(data) == IS_STRING) { char *old_cmd = NULL; if(*cmd) { @@ -2588,7 +2579,7 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, efree(old_cmd); } } - } + } ZEND_HASH_FOREACH_END(); old_cmd = cmd; cmd_len = spprintf(&cmd, 0, "%s %s\r\n", unsub_cmd, cmd); @@ -2898,7 +2889,6 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, zval *arg TSRMLS_DC) { HashTable *ht_chan; - HashPosition ptr; zval **z_ele; char *key; int cmd_len, key_len, key_free; @@ -2936,10 +2926,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1); /* Iterate our elements */ - for(zend_hash_internal_pointer_reset_ex(ht_chan, &ptr); - zend_hash_get_current_data_ex(ht_chan,(void**)&z_ele,&ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_chan, &ptr)) - { + ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) { char *key; int key_len, key_free; zval *z_tmp = NULL; @@ -2972,7 +2959,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, efree(z_tmp); z_tmp = NULL; } - } + } ZEND_HASH_FOREACH_END(); /* Set return */ *ret = cmd.c; @@ -3069,7 +3056,6 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, { zval **elem; HashTable *args_hash; - HashPosition hash_pos; int cmd_len, args_count = 0; int eval_cmd_count = 2; @@ -3092,11 +3078,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, cmd_len = redis_cmd_append_int(ret, cmd_len, keys_count); // Iterate the values in our "keys" array - for(zend_hash_internal_pointer_reset_ex(args_hash, &hash_pos); - zend_hash_get_current_data_ex(args_hash,(void **)&elem, - &hash_pos) == SUCCESS; - zend_hash_move_forward_ex(args_hash, &hash_pos)) - { + ZEND_HASH_FOREACH_VAL(args_hash, elem) { zval *z_tmp = NULL; char *key, *old_cmd; int key_len, key_free; @@ -3136,7 +3118,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, zval_dtor(z_tmp); efree(z_tmp); } - } + } ZEND_HASH_FOREACH_END(); } } diff --git a/redis_array.c b/redis_array.c index 052d1d8d05..1d8984395c 100644 --- a/redis_array.c +++ b/redis_array.c @@ -334,7 +334,6 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i int i; zval *redis_inst; zval z_fun, **z_callargs; - HashPosition pointer; HashTable *h_args; int argc; @@ -372,13 +371,11 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i z_callargs = emalloc(argc * sizeof(zval*)); /* copy args to array */ - for (i = 0, zend_hash_internal_pointer_reset_ex(h_args, &pointer); - zend_hash_get_current_data_ex(h_args, (void**) &zp_tmp, - &pointer) == SUCCESS; - ++i, zend_hash_move_forward_ex(h_args, &pointer)) { - + i = 0; + ZEND_HASH_FOREACH_VAL(h_args, zp_tmp) { z_callargs[i] = *zp_tmp; - } + i++; + } ZEND_HASH_FOREACH_END(); /* multi/exec */ if(ra->z_multi_exec) { @@ -845,7 +842,6 @@ PHP_METHOD(RedisArray, mget) RedisArray *ra; int *pos, argc, *argc_each; HashTable *h_keys; - HashPosition pointer; zval **redis_instances, **argv; /* Multi/exec support */ @@ -875,11 +871,8 @@ PHP_METHOD(RedisArray, mget) memset(argc_each, 0, ra->count * sizeof(int)); /* associate each key to a redis node */ - for (i = 0, zend_hash_internal_pointer_reset_ex(h_keys, &pointer); - zend_hash_get_current_data_ex(h_keys, (void**) &data, - &pointer) == SUCCESS; - zend_hash_move_forward_ex(h_keys, &pointer), ++i) - { + i = 0; + ZEND_HASH_FOREACH_VAL(h_keys, data) { /* If we need to represent a long key as a string */ unsigned int key_len; char kbuf[40], *key_lookup; @@ -908,7 +901,8 @@ PHP_METHOD(RedisArray, mget) argc_each[pos[i]]++; /* count number of keys per node */ argv[i] = *data; - } + i++; + } ZEND_HASH_FOREACH_END(); /* prepare return value */ array_init(return_value); @@ -1139,7 +1133,6 @@ PHP_METHOD(RedisArray, del) RedisArray *ra; int *pos, argc, *argc_each; HashTable *h_keys; - HashPosition pointer; zval **redis_instances, *redis_inst, **argv; long total = 0; int free_zkeys = 0; @@ -1193,11 +1186,8 @@ PHP_METHOD(RedisArray, del) memset(argc_each, 0, ra->count * sizeof(int)); /* associate each key to a redis node */ - for (i = 0, zend_hash_internal_pointer_reset_ex(h_keys, &pointer); - zend_hash_get_current_data_ex(h_keys, (void**) &data, - &pointer) == SUCCESS; - zend_hash_move_forward_ex(h_keys, &pointer), ++i) { - + i = 0; + ZEND_HASH_FOREACH_VAL(h_keys, data) { if (Z_TYPE_PP(data) != IS_STRING) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "DEL: all keys must be string."); efree(pos); @@ -1207,7 +1197,8 @@ PHP_METHOD(RedisArray, del) redis_instances[i] = ra_find_node(ra, Z_STRVAL_PP(data), Z_STRLEN_PP(data), &pos[i] TSRMLS_CC); argc_each[pos[i]]++; /* count number of keys per node */ argv[i] = *data; - } + i++; + } ZEND_HASH_FOREACH_END(); /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 25017a29eb..56d93bebdc 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -722,7 +722,6 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con long count, i; zval z_fun_smembers, z_ret, *z_arg, **z_data_pp; HashTable *h_keys; - HashPosition pointer; char *key; int key_len; @@ -744,17 +743,16 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con *keys = emalloc(count * sizeof(char*)); *key_lens = emalloc(count * sizeof(int)); - for (i = 0, zend_hash_internal_pointer_reset_ex(h_keys, &pointer); - zend_hash_get_current_data_ex(h_keys, (void**) &z_data_pp, &pointer) == SUCCESS; - zend_hash_move_forward_ex(h_keys, &pointer), ++i) { - + i = 0; + ZEND_HASH_FOREACH_VAL(h_keys, z_data_pp) { key = Z_STRVAL_PP(z_data_pp); key_len = Z_STRLEN_PP(z_data_pp); /* copy key and length */ (*keys)[i] = estrndup(key, key_len); (*key_lens)[i] = key_len; - } + i++; + } ZEND_HASH_FOREACH_END(); /* cleanup */ zval_dtor(&z_ret); diff --git a/redis_cluster.c b/redis_cluster.c index c252122777..e9e02c5b31 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1852,7 +1852,6 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int key_free, args_count=0, lua_len, key_len; zval *z_arr=NULL, **z_ele; HashTable *ht_arr; - HashPosition ptr; long num_keys = 0; short slot; smart_str cmdstr = {0}; @@ -1877,10 +1876,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Iterate over our args if we have any if(args_count > 0) { - for(zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); - zend_hash_get_current_data_ex(ht_arr, (void**)&z_ele, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_arr, &ptr)) - { + ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { convert_to_string(*z_ele); key = Z_STRVAL_PP(z_ele); key_len = Z_STRLEN_PP(z_ele); @@ -1907,7 +1903,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* Free key if we prefixed */ if(key_free) efree(key); - } + } ZEND_HASH_FOREACH_END(); } else { /* Pick a slot at random, we're being told there are no keys */ slot = rand() % REDIS_CLUSTER_MOD; diff --git a/redis_commands.c b/redis_commands.c index 5d72e1824b..fe84cb0c53 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -620,7 +620,6 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_free, key_len; zval *z_keys, *z_weights=NULL, **z_ele; HashTable *ht_keys, *ht_weights=NULL; - HashPosition ptr; smart_str cmdstr = {0}; int argc = 2, agg_op_len=0, keys_count; @@ -683,10 +682,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(key_free) efree(key); // Process input keys - for(zend_hash_internal_pointer_reset_ex(ht_keys, &ptr); - zend_hash_get_current_data_ex(ht_keys,(void**)&z_ele,&ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_keys, &ptr)) - { + ZEND_HASH_FOREACH_VAL(ht_keys, z_ele) { char *key; int key_free, key_len; zval *z_tmp = NULL; @@ -729,18 +725,14 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, efree(z_tmp); z_tmp = NULL; } - } + } ZEND_HASH_FOREACH_END(); // Weights if(ht_weights != NULL) { redis_cmd_append_sstr(&cmdstr, "WEIGHTS", sizeof("WEIGHTS")-1); // Process our weights - for(zend_hash_internal_pointer_reset_ex(ht_weights,&ptr); - zend_hash_get_current_data_ex(ht_weights,(void**)&z_ele,&ptr) - ==SUCCESS; - zend_hash_move_forward_ex(ht_weights,&ptr)) - { + ZEND_HASH_FOREACH_VAL(ht_weights, z_ele) { // Ignore non numeric args unless they're inf/-inf if(Z_TYPE_PP(z_ele)!=IS_LONG && Z_TYPE_PP(z_ele)!=IS_DOUBLE && strncasecmp(Z_STRVAL_PP(z_ele),"inf",sizeof("inf"))!=0 && @@ -765,7 +757,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_STRLEN_PP(z_ele)); break; } - } + } ZEND_HASH_FOREACH_END(); } // AGGREGATE @@ -788,7 +780,6 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_arr, **z_chan; HashTable *ht_chan; - HashPosition ptr; smart_str cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); int key_len, key_free; @@ -814,10 +805,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); // Iterate over channels - for(zend_hash_internal_pointer_reset_ex(ht_chan, &ptr); - zend_hash_get_current_data_ex(ht_chan, (void**)&z_chan, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_chan, &ptr)) - { + ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { // We want to deal with strings here convert_to_string(*z_chan); @@ -831,7 +819,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Free our key if it was prefixed if(key_free) efree(key); - } + } ZEND_HASH_FOREACH_END(); // Push values out *cmd_len = cmdstr.len; @@ -851,7 +839,6 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_arr, **z_chan; HashTable *ht_arr; - HashPosition ptr; smart_str cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); @@ -870,17 +857,14 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); - for(zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); - zend_hash_get_current_data_ex(ht_arr, (void**)&z_chan, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_arr, &ptr)) - { + ZEND_HASH_FOREACH_VAL(ht_arr, z_chan) { char *key = Z_STRVAL_PP(z_chan); int key_len = Z_STRLEN_PP(z_chan), key_free; key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); if(key_free) efree(key); - } + } ZEND_HASH_FOREACH_END(); // Push out vals *cmd_len = cmdstr.len; @@ -1052,7 +1036,6 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_arr, **z_val; HashTable *ht_arr; - HashPosition pos; smart_str cmdstr = {0}; int key_len, val_len, key_free, val_free, argc = 1; char *key, *val; @@ -1076,14 +1059,11 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (key_free) efree(key); /* Iterate our hash table, serializing and appending values */ - for (zend_hash_internal_pointer_reset_ex(ht_arr, &pos); - zend_hash_get_current_data_ex(ht_arr, (void**)&z_val, &pos) == SUCCESS; - zend_hash_move_forward_ex(ht_arr, &pos)) - { + ZEND_HASH_FOREACH_VAL(ht_arr, z_val) { val_free = redis_serialize(redis_sock, *z_val, &val, &val_len TSRMLS_CC); redis_cmd_append_sstr(&cmdstr, val, val_len); if (val_free) STR_FREE(val); - } + } ZEND_HASH_FOREACH_END(); *cmd_len = cmdstr.len; *cmd = cmdstr.c; @@ -1515,7 +1495,6 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_arr, **z_mems, **z_mem; int i, count, valid=0, key_len, key_free; HashTable *ht_arr; - HashPosition ptr; smart_str cmdstr = {0}; // Parse arguments @@ -1540,10 +1519,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, z_mems = ecalloc(count+1, sizeof(zval*)); // Iterate over our member array - for(zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); - zend_hash_get_current_data_ex(ht_arr, (void**)&z_mem, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_arr, &ptr)) - { + ZEND_HASH_FOREACH_VAL(ht_arr, z_mem) { // We can only handle string or long values here if ((Z_TYPE_PP(z_mem)==IS_STRING && Z_STRLEN_PP(z_mem)>0) || Z_TYPE_PP(z_mem)==IS_LONG) @@ -1557,7 +1533,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Increment the member count to actually send valid++; } - } + } ZEND_HASH_FOREACH_END(); // If nothing was valid, fail if(valid == 0) { @@ -1820,7 +1796,6 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_arr, **z_ele; HashTable *ht_arr; - HashPosition pos; smart_str cmdstr = {0}; char *mem, *key; int key_len, key_free; @@ -1854,10 +1829,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(key_free) efree(key); // Now iterate over the rest of our keys or values - for(zend_hash_internal_pointer_reset_ex(ht_arr, &pos); - zend_hash_get_current_data_ex(ht_arr, (void**)&z_ele, &pos)==SUCCESS; - zend_hash_move_forward_ex(ht_arr, &pos)) - { + ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { zval *z_tmp = NULL; // Prefix keys, serialize values @@ -1920,7 +1892,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, efree(mem); } } - } + } ZEND_HASH_FOREACH_END(); // Push output arguments *cmd = cmdstr.c; @@ -1951,7 +1923,6 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_keys, **z_key, *z_tmp = NULL; HashTable *ht_keys; - HashPosition ptr; smart_str cmdstr = {0}; int num_keys, key_len, key_free; char *key; @@ -1978,10 +1949,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, num_keys, "PFCOUNT", sizeof("PFCOUNT")-1); /* Append our key(s) */ - for (zend_hash_internal_pointer_reset_ex(ht_keys, &ptr); - zend_hash_get_current_data_ex(ht_keys, (void**)&z_key, &ptr)==SUCCESS; - zend_hash_move_forward_ex(ht_keys, &ptr)) - { + ZEND_HASH_FOREACH_VAL(ht_keys, z_key) { /* Turn our value into a string if it isn't one */ if (Z_TYPE_PP(z_key) != IS_STRING) { MAKE_STD_ZVAL(z_tmp); @@ -2025,7 +1993,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, efree(z_tmp); z_tmp = NULL; } - } + } ZEND_HASH_FOREACH_END(); } else { /* Turn our key into a string if it's a different type */ if (Z_TYPE_P(z_keys) != IS_STRING) { From 8ffb99a18a2f486b6de61bae670c0ca4de811d31 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 6 Sep 2016 16:44:35 +0300 Subject: [PATCH 0576/1986] rename smart_str to smart_string --- cluster_library.c | 26 ++++----- cluster_library.h | 6 +- common.h | 12 +++- library.c | 138 +++++++++++++++++++++++----------------------- library.h | 10 ++-- redis.c | 4 +- redis_cluster.c | 6 +- redis_commands.c | 40 +++++++------- 8 files changed, 126 insertions(+), 116 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 7385f4fe67..89e4810c83 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -41,29 +41,29 @@ static void cluster_log(char *fmt, ...) // Debug function to dump a clusterReply structure recursively static void dump_reply(clusterReply *reply, int indent) { - smart_str buf = {0}; + smart_string buf = {0}; int i; switch(reply->type) { case TYPE_ERR: - smart_str_appendl(&buf, "(error) ", sizeof("(error) ")-1); - smart_str_appendl(&buf, reply->str, reply->len); + smart_string_appendl(&buf, "(error) ", sizeof("(error) ")-1); + smart_string_appendl(&buf, reply->str, reply->len); break; case TYPE_LINE: - smart_str_appendl(&buf, reply->str, reply->len); + smart_string_appendl(&buf, reply->str, reply->len); break; case TYPE_INT: - smart_str_appendl(&buf, "(integer) ", sizeof("(integer) ")-1); - smart_str_append_long(&buf, reply->integer); + smart_string_appendl(&buf, "(integer) ", sizeof("(integer) ")-1); + smart_string_append_long(&buf, reply->integer); break; case TYPE_BULK: - smart_str_appendl(&buf,"\"", 1); - smart_str_appendl(&buf, reply->str, reply->len); - smart_str_appendl(&buf, "\"", 1); + smart_string_appendl(&buf,"\"", 1); + smart_string_appendl(&buf, reply->str, reply->len); + smart_string_appendl(&buf, "\"", 1); break; case TYPE_MULTIBULK: if(reply->elements == (size_t)-1) { - smart_str_appendl(&buf, "(nil)", sizeof("(nil)")-1); + smart_string_appendl(&buf, "(nil)", sizeof("(nil)")-1); } else { for(i=0;ielements;i++) { dump_reply(reply->element[i], indent+2); @@ -79,7 +79,7 @@ static void dump_reply(clusterReply *reply, int indent) { php_printf(" "); } - smart_str_0(&buf); + smart_string_0(&buf); php_printf("%s", buf.c); php_printf("\n"); @@ -478,7 +478,7 @@ void cluster_multi_add(clusterMultiCmd *mc, char *data, int data_len) { void cluster_multi_fini(clusterMultiCmd *mc) { mc->cmd.len = 0; redis_cmd_init_sstr(&(mc->cmd), mc->argc, mc->kw, mc->kw_len); - smart_str_appendl(&(mc->cmd), mc->args.c, mc->args.len); + smart_string_appendl(&(mc->cmd), mc->args.c, mc->args.len); } /* Set our last error string encountered */ @@ -2464,4 +2464,4 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, return SUCCESS; } -/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ +/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index f82667579d..4249a7a628 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -303,8 +303,8 @@ typedef struct clusterMultiCmd { int argc; /* The full command, built into cmd, and args as we aggregate */ - smart_str cmd; - smart_str args; + smart_string cmd; + smart_string args; } clusterMultiCmd; /* Hiredis like structure for processing any sort of reply Redis Cluster might @@ -460,4 +460,4 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, #endif -/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */ +/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/common.h b/common.h index 643a565f57..18dd9faa95 100644 --- a/common.h +++ b/common.h @@ -1,10 +1,17 @@ #include "php.h" #include "php_ini.h" -#include #ifndef REDIS_COMMON_H #define REDIS_COMMON_H +#if (PHP_MAJOR_VERSION < 7) +#include +typedef smart_str smart_string; +#define smart_string_0(x) smart_str_0(x) +#define smart_string_appendc(dest, c) smart_str_appendc(dest, c) +#define smart_string_append_long(dest, val) smart_str_append_long(dest, val) +#define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) + #define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ HashPosition _hpos; \ for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ @@ -13,6 +20,9 @@ ) #define ZEND_HASH_FOREACH_END() } while(0) +#else +#include +#endif /* NULL check so Eclipse doesn't go crazy */ #ifndef NULL diff --git a/library.c b/library.c index fd33bb6ae1..925d5a6b93 100644 --- a/library.c +++ b/library.c @@ -574,19 +574,19 @@ integer_length(int i) { int redis_cmd_format_header(char **ret, char *keyword, int arg_count) { /* Our return buffer */ - smart_str buf = {0}; + smart_string buf = {0}; /* Keyword length */ int l = strlen(keyword); - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, arg_count + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendc(&buf, '*'); + smart_string_append_long(&buf, arg_count + 1); + smart_string_appendl(&buf, _NL, sizeof(_NL) -1); + smart_string_appendc(&buf, '$'); + smart_string_append_long(&buf, l); + smart_string_appendl(&buf, _NL, sizeof(_NL) -1); + smart_string_appendl(&buf, keyword, l); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); /* Set our return pointer */ *ret = buf.c; @@ -600,7 +600,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { char *p = format; va_list ap; - smart_str buf = {0}; + smart_string buf = {0}; int l = strlen(keyword); char *dbl_str; int dbl_len; @@ -608,34 +608,34 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) va_start(ap, format); /* add header */ - smart_str_appendc(&buf, '*'); - smart_str_append_long(&buf, strlen(format) + 1); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, keyword, l); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendc(&buf, '*'); + smart_string_append_long(&buf, strlen(format) + 1); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendc(&buf, '$'); + smart_string_append_long(&buf, l); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendl(&buf, keyword, l); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); while (*p) { - smart_str_appendc(&buf, '$'); + smart_string_appendc(&buf, '$'); switch(*p) { case 's': { char *val = va_arg(ap, char*); int val_len = va_arg(ap, int); - smart_str_append_long(&buf, val_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, val, val_len); + smart_string_append_long(&buf, val_len); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendl(&buf, val, val_len); } break; case 'f': case 'F': { double d = va_arg(ap, double); REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); + smart_string_append_long(&buf, dbl_len); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendl(&buf, dbl_str, dbl_len); efree(dbl_str); } break; @@ -645,9 +645,9 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) int i = va_arg(ap, int); char tmp[32]; int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, tmp, tmp_len); + smart_string_append_long(&buf, tmp_len); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendl(&buf, tmp, tmp_len); } break; case 'l': @@ -655,16 +655,16 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) long l = va_arg(ap, long); char tmp[32]; int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); - smart_str_append_long(&buf, tmp_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, tmp, tmp_len); + smart_string_append_long(&buf, tmp_len); + smart_string_appendl(&buf, _NL, sizeof(_NL) -1); + smart_string_appendl(&buf, tmp, tmp_len); } break; } p++; - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); } - smart_str_0(&buf); + smart_string_0(&buf); *ret = buf.c; @@ -680,7 +680,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) int redis_cmd_format(char **ret, char *format, ...) { - smart_str buf = {0}; + smart_string buf = {0}; va_list ap; char *p = format; char *dbl_str; @@ -694,7 +694,7 @@ redis_cmd_format(char **ret, char *format, ...) { case 's': { char *tmp = va_arg(ap, char*); int tmp_len = va_arg(ap, int); - smart_str_appendl(&buf, tmp, tmp_len); + smart_string_appendl(&buf, tmp, tmp_len); } break; @@ -702,9 +702,9 @@ redis_cmd_format(char **ret, char *format, ...) { case 'f': { double d = va_arg(ap, double); REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_str_append_long(&buf, dbl_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_str_appendl(&buf, dbl_str, dbl_len); + smart_string_append_long(&buf, dbl_len); + smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); + smart_string_appendl(&buf, dbl_str, dbl_len); efree(dbl_str); } break; @@ -714,18 +714,18 @@ redis_cmd_format(char **ret, char *format, ...) { int i = va_arg(ap, int); char tmp[32]; int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_str_appendl(&buf, tmp, tmp_len); + smart_string_appendl(&buf, tmp, tmp_len); } break; } } else { - smart_str_appendc(&buf, *p); + smart_string_appendc(&buf, *p); } p++; } - smart_str_0(&buf); + smart_string_0(&buf); *ret = buf.c; @@ -737,17 +737,17 @@ redis_cmd_format(char **ret, char *format, ...) { */ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) { /* Smart string buffer */ - smart_str buf = {0}; + smart_string buf = {0}; - /* Append the current command to our smart_str */ - smart_str_appendl(&buf, *cmd, cmd_len); + /* Append the current command to our smart_string */ + smart_string_appendl(&buf, *cmd, cmd_len); /* Append our new command sequence */ - smart_str_appendc(&buf, '$'); - smart_str_append_long(&buf, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); - smart_str_appendl(&buf, append, append_len); - smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_string_appendc(&buf, '$'); + smart_string_append_long(&buf, append_len); + smart_string_appendl(&buf, _NL, sizeof(_NL) -1); + smart_string_appendl(&buf, append, append_len); + smart_string_appendl(&buf, _NL, sizeof(_NL) -1); /* Free our old command */ efree(*cmd); @@ -763,27 +763,27 @@ int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) * Given a smart string, number of arguments, a keyword, and the length of the keyword * initialize our smart string with the proper Redis header for the command to follow */ -int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len) { - smart_str_appendc(str, '*'); - smart_str_append_long(str, num_args + 1); - smart_str_appendl(str, _NL, sizeof(_NL) -1); - smart_str_appendc(str, '$'); - smart_str_append_long(str, keyword_len); - smart_str_appendl(str, _NL, sizeof(_NL) - 1); - smart_str_appendl(str, keyword, keyword_len); - smart_str_appendl(str, _NL, sizeof(_NL) - 1); +int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len) { + smart_string_appendc(str, '*'); + smart_string_append_long(str, num_args + 1); + smart_string_appendl(str, _NL, sizeof(_NL) -1); + smart_string_appendc(str, '$'); + smart_string_append_long(str, keyword_len); + smart_string_appendl(str, _NL, sizeof(_NL) - 1); + smart_string_appendl(str, keyword, keyword_len); + smart_string_appendl(str, _NL, sizeof(_NL) - 1); return str->len; } /* - * Append a command sequence to a smart_str + * Append a command sequence to a smart_string */ -int redis_cmd_append_sstr(smart_str *str, char *append, int append_len) { - smart_str_appendc(str, '$'); - smart_str_append_long(str, append_len); - smart_str_appendl(str, _NL, sizeof(_NL) - 1); - smart_str_appendl(str, append, append_len); - smart_str_appendl(str, _NL, sizeof(_NL) - 1); +int redis_cmd_append_sstr(smart_string *str, char *append, int append_len) { + smart_string_appendc(str, '$'); + smart_string_append_long(str, append_len); + smart_string_appendl(str, _NL, sizeof(_NL) - 1); + smart_string_appendl(str, append, append_len); + smart_string_appendl(str, _NL, sizeof(_NL) - 1); /* Return our new length */ return str->len; @@ -792,7 +792,7 @@ int redis_cmd_append_sstr(smart_str *str, char *append, int append_len) { /* * Append an integer to a smart string command */ -int redis_cmd_append_sstr_int(smart_str *str, int append) { +int redis_cmd_append_sstr_int(smart_string *str, int append) { char int_buf[32]; int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); return redis_cmd_append_sstr(str, int_buf, int_len); @@ -801,7 +801,7 @@ int redis_cmd_append_sstr_int(smart_str *str, int append) { /* * Append a long to a smart string command */ -int redis_cmd_append_sstr_long(smart_str *str, long append) { +int redis_cmd_append_sstr_long(smart_string *str, long append) { char long_buf[32]; int long_len = snprintf(long_buf, sizeof(long_buf), "%ld", append); return redis_cmd_append_sstr(str, long_buf, long_len); @@ -810,7 +810,7 @@ int redis_cmd_append_sstr_long(smart_str *str, long append) { /* * Append a double to a smart string command */ -int redis_cmd_append_sstr_dbl(smart_str *str, double value) { +int redis_cmd_append_sstr_dbl(smart_string *str, double value) { char *dbl_str; int dbl_len, retval; @@ -2004,7 +2004,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len #else HashTable ht; #endif - smart_str sstr = {0}; + smart_string sstr = {0}; zval *z_copy; #ifdef HAVE_REDIS_IGBINARY size_t sz; diff --git a/library.h b/library.h index 953f91f858..b876238386 100644 --- a/library.h +++ b/library.h @@ -13,12 +13,12 @@ int redis_cmd_format(char **ret, char *format, ...); int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); int redis_cmd_format_header(char **ret, char *keyword, int arg_count); int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len); -int redis_cmd_init_sstr(smart_str *str, int num_args, char *keyword, int keyword_len); -int redis_cmd_append_sstr(smart_str *str, char *append, int append_len); -int redis_cmd_append_sstr_int(smart_str *str, int append); -int redis_cmd_append_sstr_long(smart_str *str, long append); +int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); +int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); +int redis_cmd_append_sstr_int(smart_string *str, int append); +int redis_cmd_append_sstr_long(smart_string *str, long append); int redis_cmd_append_int(char **cmd, int cmd_len, int append); -int redis_cmd_append_sstr_dbl(smart_str *str, double value); +int redis_cmd_append_sstr_dbl(smart_string *str, double value); PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len TSRMLS_DC); diff --git a/redis.c b/redis.c index 6351bce972..41724c1098 100644 --- a/redis.c +++ b/redis.c @@ -970,7 +970,7 @@ PHP_METHOD(Redis, getMultiple) zval *object, *z_args, **z_ele; HashTable *hash; RedisSock *redis_sock; - smart_str cmd = {0}; + smart_string cmd = {0}; int arg_count; /* Make sure we have proper arguments */ @@ -2892,7 +2892,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, zval **z_ele; char *key; int cmd_len, key_len, key_free; - smart_str cmd = {0}; + smart_string cmd = {0}; if(type == PUBSUB_CHANNELS) { if(arg) { diff --git a/redis_cluster.c b/redis_cluster.c index e9e02c5b31..2507f7d9d6 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1854,7 +1854,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, HashTable *ht_arr; long num_keys = 0; short slot; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; /* Parse args */ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, @@ -2077,7 +2077,7 @@ PHP_METHOD(RedisCluster, watch) { redisCluster *c = GET_CONTEXT(); HashTable *ht_dist; clusterDistList **dl; - smart_str cmd = {0}; + smart_string cmd = {0}; zval **z_args; int argc = ZEND_NUM_ARGS(), i; ulong slot; @@ -2351,7 +2351,7 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { redisCluster *c = GET_CONTEXT(); - smart_str cmd = {0}; + smart_string cmd = {0}; zval **z_args; short slot; int i, argc = ZEND_NUM_ARGS(); diff --git a/redis_commands.c b/redis_commands.c index fe84cb0c53..4e2a20d8d8 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -43,7 +43,7 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * the command) we take the start of our array and count */ int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC) { - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; int i; /* Make sure our first argument is a string */ @@ -428,7 +428,7 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, { static char *kw[] = {"SCAN","SSCAN","HSCAN","ZSCAN"}; int argc; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; // Figure out our argument count argc = 1 + (type!=TYPE_SCAN) + (pat_len>0?2:0) + (count>0?2:0); @@ -620,7 +620,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_free, key_len; zval *z_keys, *z_weights=NULL, **z_ele; HashTable *ht_keys, *ht_weights=NULL; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; int argc = 2, agg_op_len=0, keys_count; // Parse args @@ -780,7 +780,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_arr, **z_chan; HashTable *ht_chan; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); int key_len, key_free; char *key; @@ -839,7 +839,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_arr, **z_chan; HashTable *ht_arr; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { @@ -978,7 +978,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { zval **z_args; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; char *arg; int arg_free, arg_len, i; int argc = ZEND_NUM_ARGS(); @@ -1036,7 +1036,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_arr, **z_val; HashTable *ht_arr; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; int key_len, val_len, key_free, val_free, argc = 1; char *key, *val; @@ -1083,7 +1083,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int key_free, key_len, i, tail; int single_array = 0, argc = ZEND_NUM_ARGS(); - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; long timeout; short kslot = -1; @@ -1495,7 +1495,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_arr, **z_mems, **z_mem; int i, count, valid=0, key_len, key_free; HashTable *ht_arr; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; // Parse arguments if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, @@ -1581,7 +1581,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_arr; HashTable *ht_vals; HashPosition pos; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; // Parse args if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, @@ -1702,7 +1702,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval **z_args; char *key; int key_len, i, key_free, argc = ZEND_NUM_ARGS(); - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; short kslot; // Allocate space for args, parse them as an array @@ -1796,7 +1796,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_arr, **z_ele; HashTable *ht_arr; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; char *mem, *key; int key_len, key_free; int mem_len, mem_free, argc=1; @@ -1923,7 +1923,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_keys, **z_key, *z_tmp = NULL; HashTable *ht_keys; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; int num_keys, key_len, key_free; char *key; short kslot=-1; @@ -2341,7 +2341,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_opts=NULL, **z_ele, *z_argv; char *key; HashTable *ht_opts; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; int key_len, key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &key, &key_len, @@ -2582,7 +2582,7 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval **z_args; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; char *arg; int arg_free, arg_len, i; int argc = ZEND_NUM_ARGS(); @@ -2641,7 +2641,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *val; int key_len, key_free, val_len, val_free; int argc = ZEND_NUM_ARGS(), i; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; z_args = emalloc(argc * sizeof(zval*)); if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { @@ -2810,7 +2810,7 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, } /* Helper to append options to a GEORADIUS or GEORADIUSBYMEMBER command */ -void append_georadius_opts(smart_str *str, int withcoord, int withdist, +void append_georadius_opts(smart_string *str, int withcoord, int withdist, int withhash, long count, geoSortType sort) { /* WITHCOORD option */ @@ -2850,7 +2850,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, geoSortType sort = SORT_NONE; double lng, lat, radius; zval *opts = NULL; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; int argc; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sddds|a", &key, &keylen, @@ -2909,7 +2909,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s double radius; geoSortType sort = SORT_NONE; zval *opts = NULL; - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssds|a", &key, &keylen, &mem, &memlen, &radius, &unit, &unitlen, &opts) == FAILURE) @@ -3072,7 +3072,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval **z_ele; HashTable *ht_arr = Z_ARRVAL_P(z_arg); - smart_str cmdstr = {0}; + smart_string cmdstr = {0}; redis_cmd_init_sstr(&cmdstr, 1 + arr_len, "COMMAND", sizeof("COMMAND")-1); redis_cmd_append_sstr(&cmdstr, "GETKEYS", sizeof("GETKEYS")-1); From 12d29886be768b57788863c5c02ff3842ae7528c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 7 Sep 2016 21:13:22 +0300 Subject: [PATCH 0577/1986] using zend_hash_str_find+zend_hash_str_find_ptr instead of zend_hash_find --- cluster_library.c | 24 +++++-------- common.h | 22 ++++++++++++ redis.c | 32 +++++++---------- redis_array.c | 80 ++++++++++++++++++++--------------------- redis_array_impl.c | 70 ++++++++++++++++++------------------ redis_cluster.c | 47 ++++++++++++------------ redis_commands.c | 79 ++++++++++++++++++++-------------------- redis_session.c | 90 +++++++++++++++++++++++----------------------- 8 files changed, 226 insertions(+), 218 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 89e4810c83..58380e3046 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -677,7 +677,7 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { int i,j, hlen, klen; short low, high; clusterReply *r2, *r3; - redisClusterNode **ppnode, *master, *slave; + redisClusterNode *pnode, *master, *slave; unsigned short port; char *host, key[1024]; @@ -702,12 +702,12 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { // If the node is new, create and add to nodes. Otherwise use it. klen = snprintf(key,sizeof(key),"%s:%ld",host,port); - if(zend_hash_find(c->nodes,key,klen+1,(void**)&ppnode)==FAILURE) { + if ((pnode = zend_hash_str_find_ptr(c->nodes, key, klen)) == NULL) { master = cluster_node_create(c, host, hlen, port, low, 0); zend_hash_update(c->nodes, key, klen+1, (void*)&master, sizeof(redisClusterNode*), NULL); } else { - master = *ppnode; + master = pnode; } // Attach slaves @@ -749,7 +749,7 @@ PHP_REDIS_API void cluster_free_node(redisClusterNode *node) { /* Get or create a redisClusterNode that corresponds to the asking redirection */ static redisClusterNode *cluster_get_asking_node(redisCluster *c TSRMLS_DC) { - redisClusterNode **ppNode; + redisClusterNode *pNode; char key[1024]; int key_len; @@ -757,16 +757,16 @@ static redisClusterNode *cluster_get_asking_node(redisCluster *c TSRMLS_DC) { key_len = snprintf(key, sizeof(key), "%s:%u", c->redir_host, c->redir_port); /* See if we've already attached to it */ - if (zend_hash_find(c->nodes, key, key_len+1, (void**)&ppNode) == SUCCESS) { - return *ppNode; + if ((pNode = zend_hash_str_find_ptr(c->nodes, key, key_len)) != NULL) { + return pNode; } /* This host:port is unknown to us, so add it */ - *ppNode = cluster_node_create(c, c->redir_host, c->redir_host_len, + pNode = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); /* Return the node */ - return *ppNode; + return pNode; } /* Get or create a node at the host:port we were asked to check, and return the @@ -1233,18 +1233,12 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, unsigned short port) { - redisClusterNode **ret = NULL; int key_len; char key[1024]; key_len = snprintf(key,sizeof(key),"%s:%d", host, port); - if(zend_hash_find(c->nodes, key, key_len+1, (void**)&ret)==SUCCESS) { - return *ret; - } - - /* Not found */ - return NULL; + return zend_hash_str_find_ptr(c->nodes, key, key_len); } /* Provided a redisCluster object, the slot where we thought data was and diff --git a/common.h b/common.h index 18dd9faa95..6d1aeef99e 100644 --- a/common.h +++ b/common.h @@ -20,6 +20,28 @@ typedef smart_str smart_string; ) #define ZEND_HASH_FOREACH_END() } while(0) + +static zend_always_inline zval * +zend_hash_str_find(const HashTable *ht, const char *key, size_t len) +{ + zval **zv; + + if (zend_hash_find(ht, key, len + 1, (void **)&zv) == SUCCESS) { + return *zv; + } + return NULL; +} + +static zend_always_inline void * +zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) +{ + void **ptr; + + if (zend_hash_find(ht, str, len + 1, (void **)&ptr) == SUCCESS) { + return *ptr; + } + return NULL; +} #else #include #endif diff --git a/redis.c b/redis.c index 41724c1098..c750fb935f 100644 --- a/redis.c +++ b/redis.c @@ -376,14 +376,11 @@ PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) #if HAVE_SPL if (!root) { if (!spl_ce_RuntimeException) { - zend_class_entry **pce; - - if (zend_hash_find(CG(class_table), "runtimeexception", - sizeof("RuntimeException"), - (void**)&pce) == SUCCESS) - { - spl_ce_RuntimeException = *pce; - return *pce; + zend_class_entry *pce; + + if ((pce = zend_hash_str_find_ptr(CG(class_table), "runtimeexception", sizeof("RuntimeException") - 1))) { + spl_ce_RuntimeException = pce; + return pce; } } else { return spl_ce_RuntimeException; @@ -442,12 +439,11 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) { - zval **socket; + zval *socket; int resource_type; - if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "socket", - sizeof("socket"), (void **) &socket) == FAILURE) - { + if (Z_TYPE_P(id) != IS_OBJECT || (socket = zend_hash_str_find(Z_OBJPROP_P(id), + "socket", sizeof("socket") - 1)) == NULL) { // Throw an exception unless we've been requested not to if(!no_throw) { zend_throw_exception(redis_exception_ce, "Redis server went away", @@ -456,7 +452,7 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, return -1; } - *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_PP(socket), + *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_P(socket), &resource_type); if (!*redis_sock || resource_type != le_redis_sock) { @@ -475,7 +471,7 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, } } - return Z_LVAL_PP(socket); + return Z_LVAL_P(socket); } /** @@ -725,8 +721,7 @@ PHP_METHOD(Redis, pconnect) /* }}} */ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { - zval *object; - zval **socket; + zval *object, *socket; int host_len, id; char *host = NULL; long port = -1; @@ -775,13 +770,12 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { /* if there is a redis sock already we have to remove it from the list */ if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) > 0) { - if (zend_hash_find(Z_OBJPROP_P(object), "socket", - sizeof("socket"), (void **) &socket) == FAILURE) + if ((socket = zend_hash_str_find(Z_OBJPROP_P(object), "socket", sizeof("socket") - 1)) == NULL) { /* maybe there is a socket but the id isn't known.. what to do? */ } else { /* The refcount should be decreased and destructor invoked */ - zend_list_delete(Z_LVAL_PP(socket)); + zend_list_delete(Z_LVAL_P(socket)); } } diff --git a/redis_array.c b/redis_array.c index 1d8984395c..9dea53e406 100644 --- a/redis_array.c +++ b/redis_array.c @@ -135,21 +135,21 @@ void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC) PHP_REDIS_API int redis_array_get(zval *id, RedisArray **ra TSRMLS_DC) { - zval **socket; + zval *socket; int resource_type; - if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "socket", - sizeof("socket"), (void **) &socket) == FAILURE) { + if (Z_TYPE_P(id) != IS_OBJECT || (socket = zend_hash_str_find(Z_OBJPROP_P(id), + "socket", sizeof("socket") - 1)) == NULL) { return -1; } - *ra = (RedisArray *) zend_list_find(Z_LVAL_PP(socket), &resource_type); + *ra = (RedisArray *) zend_list_find(Z_LVAL_P(socket), &resource_type); if (!*ra || resource_type != le_redis_array) { return -1; } - return Z_LVAL_PP(socket); + return Z_LVAL_P(socket); } uint32_t rcrc32(const char *s, size_t sz) { @@ -207,7 +207,7 @@ uint32_t rcrc32(const char *s, size_t sz) { Public constructor */ PHP_METHOD(RedisArray, __construct) { - zval *z0, *z_fun = NULL, *z_dist = NULL, **zpData, *z_opts = NULL; + zval *z0, *z_fun = NULL, *z_dist = NULL, *zpData, *z_opts = NULL; int id; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; @@ -222,76 +222,76 @@ PHP_METHOD(RedisArray, __construct) /* extract options */ if(z_opts) { - zval **z_retry_interval_pp; - zval **z_connect_timeout_pp; + zval *z_retry_interval_p; + zval *z_connect_timeout_p; hOpts = Z_ARRVAL_P(z_opts); /* extract previous ring. */ - if(FAILURE != zend_hash_find(hOpts, "previous", sizeof("previous"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_ARRAY - && zend_hash_num_elements(Z_ARRVAL_PP(zpData)) != 0) { + if ((zpData = zend_hash_str_find(hOpts, "previous", sizeof("previous") - 1)) != NULL && Z_TYPE_P(zpData) == IS_ARRAY + && zend_hash_num_elements(Z_ARRVAL_P(zpData)) != 0 + ) { /* consider previous array as non-existent if empty. */ - hPrev = Z_ARRVAL_PP(zpData); + hPrev = Z_ARRVAL_P(zpData); } /* extract function name. */ - if(FAILURE != zend_hash_find(hOpts, "function", sizeof("function"), (void**)&zpData)) { + if ((zpData = zend_hash_str_find(hOpts, "function", sizeof("function") - 1)) != NULL) { MAKE_STD_ZVAL(z_fun); - *z_fun = **zpData; + *z_fun = *zpData; zval_copy_ctor(z_fun); } /* extract function name. */ - if(FAILURE != zend_hash_find(hOpts, "distributor", sizeof("distributor"), (void**)&zpData)) { + if ((zpData = zend_hash_str_find(hOpts, "distributor", sizeof("distributor") - 1)) != NULL) { MAKE_STD_ZVAL(z_dist); - *z_dist = **zpData; + *z_dist = *zpData; zval_copy_ctor(z_dist); } /* extract index option. */ - if(FAILURE != zend_hash_find(hOpts, "index", sizeof("index"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { - b_index = Z_BVAL_PP(zpData); + if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL && Z_TYPE_P(zpData) == IS_BOOL) { + b_index = Z_BVAL_P(zpData); } /* extract autorehash option. */ - if(FAILURE != zend_hash_find(hOpts, "autorehash", sizeof("autorehash"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { - b_autorehash = Z_BVAL_PP(zpData); + if ((zpData = zend_hash_str_find(hOpts, "autorehash", sizeof("autorehash") - 1)) != NULL && Z_TYPE_P(zpData) == IS_BOOL) { + b_autorehash = Z_BVAL_P(zpData); } /* pconnect */ - if(FAILURE != zend_hash_find(hOpts, "pconnect", sizeof("pconnect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { - b_pconnect = Z_BVAL_PP(zpData); + if ((zpData = zend_hash_str_find(hOpts, "pconnect", sizeof("pconnect") - 1)) != NULL && Z_TYPE_P(zpData) == IS_BOOL) { + b_pconnect = Z_BVAL_P(zpData); } /* extract retry_interval option. */ - if (FAILURE != zend_hash_find(hOpts, "retry_interval", sizeof("retry_interval"), (void**)&z_retry_interval_pp)) { - if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG || Z_TYPE_PP(z_retry_interval_pp) == IS_STRING) { - if (Z_TYPE_PP(z_retry_interval_pp) == IS_LONG) { - l_retry_interval = Z_LVAL_PP(z_retry_interval_pp); - } - else { - l_retry_interval = atol(Z_STRVAL_PP(z_retry_interval_pp)); + if ((z_retry_interval_p = zend_hash_str_find(hOpts, "retry_interval", sizeof("retry_interval") - 1)) != NULL) { + if (Z_TYPE_P(z_retry_interval_p) == IS_LONG || Z_TYPE_P(z_retry_interval_p) == IS_STRING) { + if (Z_TYPE_P(z_retry_interval_p) == IS_LONG) { + l_retry_interval = Z_LVAL_P(z_retry_interval_p); + } else { + l_retry_interval = atol(Z_STRVAL_P(z_retry_interval_p)); } } } /* extract lazy connect option. */ - if(FAILURE != zend_hash_find(hOpts, "lazy_connect", sizeof("lazy_connect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) { - b_lazy_connect = Z_BVAL_PP(zpData); + if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL && Z_TYPE_P(zpData) == IS_BOOL) { + b_lazy_connect = Z_BVAL_P(zpData); } /* extract connect_timeout option */ - if (FAILURE != zend_hash_find(hOpts, "connect_timeout", sizeof("connect_timeout"), (void**)&z_connect_timeout_pp)) { - if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE || - Z_TYPE_PP(z_connect_timeout_pp) == IS_STRING || - Z_TYPE_PP(z_connect_timeout_pp) == IS_LONG) - { - if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE) { - d_connect_timeout = Z_DVAL_PP(z_connect_timeout_pp); - } else if (Z_TYPE_PP(z_connect_timeout_pp) == IS_LONG) { - d_connect_timeout = Z_LVAL_PP(z_connect_timeout_pp); + if ((z_connect_timeout_p = zend_hash_str_find(hOpts, "connect_timeout", sizeof("connect_timeout") - 1)) != NULL) { + if (Z_TYPE_P(z_connect_timeout_p) == IS_DOUBLE || + Z_TYPE_P(z_connect_timeout_p) == IS_STRING || + Z_TYPE_P(z_connect_timeout_p) == IS_LONG + ) { + if (Z_TYPE_P(z_connect_timeout_p) == IS_DOUBLE) { + d_connect_timeout = Z_DVAL_P(z_connect_timeout_p); + } else if (Z_TYPE_P(z_connect_timeout_p) == IS_LONG) { + d_connect_timeout = Z_LVAL_P(z_connect_timeout_p); } else { - d_connect_timeout = atof(Z_STRVAL_PP(z_connect_timeout_pp)); + d_connect_timeout = atof(Z_STRVAL_P(z_connect_timeout_p)); } } } diff --git a/redis_array_impl.c b/redis_array_impl.c index 56d93bebdc..9562663a41 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -171,9 +171,9 @@ ra_find_name(const char *name) { /* laod array from INI settings */ RedisArray *ra_load_array(const char *name TSRMLS_DC) { - zval *z_params_hosts, **z_hosts; - zval *z_params_prev, **z_prev; - zval *z_params_funs, **z_data_pp, *z_fun = NULL, *z_dist = NULL; + zval *z_params_hosts, *z_hosts; + zval *z_params_prev, *z_prev; + zval *z_params_funs, *z_data, *z_fun = NULL, *z_dist = NULL; zval *z_params_index; zval *z_params_autorehash; zval *z_params_retry_interval; @@ -187,7 +187,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zend_bool b_lazy_connect = 0; double d_connect_timeout = 0; HashTable *hHosts = NULL, *hPrev = NULL; - size_t name_len = strlen(name) + 1; + size_t name_len = strlen(name); char *iptr; /* find entry */ @@ -200,8 +200,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.hosts")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_hosts TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_hosts), name, name_len, (void **) &z_hosts) != FAILURE) { - hHosts = Z_ARRVAL_PP(z_hosts); + if ((z_hosts = zend_hash_str_find(Z_ARRVAL_P(z_params_hosts), name, name_len)) != NULL) { + hHosts = Z_ARRVAL_P(z_hosts); } /* find previous hosts */ @@ -210,8 +210,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.previous")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_prev TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_prev), name, name_len, (void **) &z_prev) != FAILURE) { - hPrev = Z_ARRVAL_PP(z_prev); + if ((z_prev = zend_hash_str_find(Z_ARRVAL_P(z_params_prev), name, name_len)) != NULL) { + hPrev = Z_ARRVAL_P(z_prev); } /* find function */ @@ -220,9 +220,9 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.functions")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_funs TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_funs), name, name_len, (void **) &z_data_pp) != FAILURE) { + if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_funs), name, name_len)) != NULL) { MAKE_STD_ZVAL(z_fun); - *z_fun = **z_data_pp; + *z_fun = *z_data; zval_copy_ctor(z_fun); } @@ -232,9 +232,9 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.distributor")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_funs TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_funs), name, name_len, (void **) &z_data_pp) != FAILURE) { + if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_funs), name, name_len)) != NULL) { MAKE_STD_ZVAL(z_dist); - *z_dist = **z_data_pp; + *z_dist = *z_data; zval_copy_ctor(z_dist); } @@ -244,8 +244,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.index")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_index TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_index), name, name_len, (void **) &z_data_pp) != FAILURE) { - if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { + if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_index), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_index = 1; } } @@ -256,8 +256,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.autorehash")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_autorehash TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_autorehash), name, name_len, (void **) &z_data_pp) != FAILURE) { - if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { + if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_autorehash), name, name_len)) != NULL) { + if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_autorehash = 1; } } @@ -268,13 +268,13 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.retryinterval")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_retry_interval TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_retry_interval), name, name_len, (void **) &z_data_pp) != FAILURE) { - if (Z_TYPE_PP(z_data_pp) == IS_LONG || Z_TYPE_PP(z_data_pp) == IS_STRING) { - if (Z_TYPE_PP(z_data_pp) == IS_LONG) { - l_retry_interval = Z_LVAL_PP(z_data_pp); + if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_retry_interval), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_LONG || Z_TYPE_P(z_data) == IS_STRING) { + if (Z_TYPE_P(z_data) == IS_LONG) { + l_retry_interval = Z_LVAL_P(z_data); } else { - l_retry_interval = atol(Z_STRVAL_PP(z_data_pp)); + l_retry_interval = atol(Z_STRVAL_P(z_data)); } } } @@ -285,8 +285,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.pconnect")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_pconnect TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_pconnect), name, name_len, (void**) &z_data_pp) != FAILURE) { - if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { + if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_pconnect), name, name_len)) != NULL) { + if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_pconnect = 1; } } @@ -297,8 +297,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.lazyconnect")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_lazy_connect TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_lazy_connect), name, name_len, (void **) &z_data_pp) != FAILURE) { - if(Z_TYPE_PP(z_data_pp) == IS_STRING && strncmp(Z_STRVAL_PP(z_data_pp), "1", 1) == 0) { + if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_lazy_connect), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_lazy_connect = 1; } } @@ -309,17 +309,17 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.connecttimeout")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_connect_timeout TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_params_connect_timeout), name, name_len, (void **) &z_data_pp) != FAILURE) { - if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE || - Z_TYPE_PP(z_data_pp) == IS_STRING || - Z_TYPE_PP(z_data_pp) == IS_LONG) - { - if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE) { - d_connect_timeout = Z_DVAL_PP(z_data_pp); - } else if (Z_TYPE_PP(z_data_pp) == IS_LONG) { - d_connect_timeout = Z_LVAL_PP(z_data_pp); + if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_connect_timeout), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_DOUBLE || + Z_TYPE_P(z_data) == IS_STRING || + Z_TYPE_P(z_data) == IS_LONG + ) { + if (Z_TYPE_P(z_data) == IS_DOUBLE) { + d_connect_timeout = Z_DVAL_P(z_data); + } else if (Z_TYPE_P(z_data) == IS_LONG) { + d_connect_timeout = Z_LVAL_P(z_data); } else { - d_connect_timeout = atof(Z_STRVAL_PP(z_data_pp)); + d_connect_timeout = atof(Z_STRVAL_P(z_data)); } } } diff --git a/redis_cluster.c b/redis_cluster.c index 2507f7d9d6..d4bbed4954 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -244,14 +244,11 @@ PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC) { #if HAVE_SPL if(!root) { if(!spl_rte_ce) { - zend_class_entry **pce; + zend_class_entry *pce; - if(zend_hash_find(CG(class_table), "runtimeexception", - sizeof("runtimeexception"), (void**)&pce) - ==SUCCESS) - { - spl_rte_ce = *pce; - return *pce; + if ((pce = zend_hash_str_find_ptr(CG(class_table), "runtimeexception", sizeof("runtimeexception") - 1))) { + spl_rte_ce = pce; + return pce; } } else { return spl_rte_ce; @@ -379,7 +376,7 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, /* Attempt to load a named cluster configured in php.ini */ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { - zval *z_seeds, *z_timeout, *z_read_timeout, *z_persistent, **z_value; + zval *z_seeds, *z_timeout, *z_read_timeout, *z_persistent, *z_value; char *iptr; double timeout=0, read_timeout=0; int persistent = 0; @@ -391,8 +388,8 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { if ((iptr = INI_STR("redis.clusters.seeds")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_seeds TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_seeds), name, name_len+1, (void**)&z_value) != FAILURE) { - ht_seeds = Z_ARRVAL_PP(z_value); + if ((z_value = zend_hash_str_find(Z_ARRVAL_P(z_seeds), name, name_len)) != NULL) { + ht_seeds = Z_ARRVAL_P(z_value); } else { zval_dtor(z_seeds); efree(z_seeds); @@ -406,11 +403,11 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { if ((iptr = INI_STR("redis.clusters.timeout")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_timeout TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_timeout), name, name_len+1, (void**)&z_value) != FAILURE) { - if (Z_TYPE_PP(z_value) == IS_STRING) { - timeout = atof(Z_STRVAL_PP(z_value)); - } else if (Z_TYPE_PP(z_value) == IS_DOUBLE) { - timeout = Z_DVAL_PP(z_value); + if ((z_value = zend_hash_str_find(Z_ARRVAL_P(z_timeout), name, name_len)) != NULL) { + if (Z_TYPE_P(z_value) == IS_STRING) { + timeout = atof(Z_STRVAL_P(z_value)); + } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { + timeout = Z_DVAL_P(z_value); } } @@ -420,11 +417,11 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { if ((iptr = INI_STR("redis.clusters.read_timeout")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_read_timeout TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_read_timeout), name, name_len+1, (void**)&z_value) != FAILURE) { - if (Z_TYPE_PP(z_value) == IS_STRING) { - read_timeout = atof(Z_STRVAL_PP(z_value)); - } else if (Z_TYPE_PP(z_value) == IS_DOUBLE) { - read_timeout = Z_DVAL_PP(z_value); + if ((z_value = zend_hash_str_find(Z_ARRVAL_P(z_read_timeout), name, name_len)) != NULL) { + if (Z_TYPE_P(z_value) == IS_STRING) { + read_timeout = atof(Z_STRVAL_P(z_value)); + } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { + read_timeout = Z_DVAL_P(z_value); } } @@ -434,11 +431,11 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { if ((iptr = INI_STR("redis.clusters.persistent")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_persistent TSRMLS_CC); } - if (zend_hash_find(Z_ARRVAL_P(z_persistent), name, name_len+1, (void**)&z_value) != FAILURE) { - if (Z_TYPE_PP(z_value) == IS_STRING) { - persistent = atoi(Z_STRVAL_PP(z_value)); - } else if (Z_TYPE_PP(z_value) == IS_LONG) { - persistent = Z_LVAL_PP(z_value); + if ((z_value = zend_hash_str_find(Z_ARRVAL_P(z_persistent), name, name_len)) != NULL) { + if (Z_TYPE_P(z_value) == IS_STRING) { + persistent = atoi(Z_STRVAL_P(z_value)); + } else if (Z_TYPE_P(z_value) == IS_LONG) { + persistent = Z_LVAL_P(z_value); } } diff --git a/redis_commands.c b/redis_commands.c index 4e2a20d8d8..1c216d43c0 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2338,7 +2338,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int *using_store, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_opts=NULL, **z_ele, *z_argv; + zval *z_opts=NULL, *z_ele, *z_argv; char *key; HashTable *ht_opts; smart_string cmdstr = {0}; @@ -2383,10 +2383,10 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ht_opts = Z_ARRVAL_P(z_opts); // Handle BY pattern - if((zend_hash_find(ht_opts, "by", sizeof("by"), (void**)&z_ele)==SUCCESS || - zend_hash_find(ht_opts, "BY", sizeof("BY"), (void**)&z_ele)==SUCCESS) && - Z_TYPE_PP(z_ele)==IS_STRING) - { + if (((z_ele = zend_hash_str_find(ht_opts, "by", sizeof("by") - 1)) != NULL || + (z_ele = zend_hash_str_find(ht_opts, "BY", sizeof("BY") - 1)) != NULL + ) && Z_TYPE_P(z_ele) == IS_STRING + ) { // "BY" option is disabled in cluster if(slot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -2399,26 +2399,26 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // ... BY add_next_index_stringl(z_argv, "BY", sizeof("BY")-1, 1); - add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), 1); } // Handle ASC/DESC option - if((zend_hash_find(ht_opts,"sort",sizeof("sort"),(void**)&z_ele)==SUCCESS || - zend_hash_find(ht_opts,"SORT",sizeof("SORT"),(void**)&z_ele)==SUCCESS) && - Z_TYPE_PP(z_ele)==IS_STRING) - { + if (((z_ele = zend_hash_str_find(ht_opts, "sort", sizeof("sort") - 1)) != NULL || + (z_ele = zend_hash_str_find(ht_opts, "SORT", sizeof("SORT") - 1)) != NULL + ) && Z_TYPE_P(z_ele) == IS_STRING + ) { // 'asc'|'desc' - add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), 1); } // STORE option - if((zend_hash_find(ht_opts,"store",6,(void**)&z_ele)==SUCCESS || - zend_hash_find(ht_opts,"STORE",6,(void**)&z_ele)==SUCCESS) && - Z_TYPE_PP(z_ele)==IS_STRING) - { + if (((z_ele = zend_hash_str_find(ht_opts, "store", sizeof("store") - 1)) != NULL || + (z_ele = zend_hash_str_find(ht_opts, "STORE", sizeof("STORE") - 1)) != NULL + ) && Z_TYPE_P(z_ele) == IS_STRING + ) { // Slot verification int cross_slot = slot && *slot != cluster_hash_key( - Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele)); + Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); if(cross_slot) { php_error_docref(0 TSRMLS_CC, E_WARNING, @@ -2431,17 +2431,17 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // STORE add_next_index_stringl(z_argv,"STORE",sizeof("STORE")-1, 1); - add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele),Z_STRLEN_PP(z_ele),1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), 1); // We are using STORE *using_store = 1; } // GET option - if((zend_hash_find(ht_opts,"get",4,(void**)&z_ele)==SUCCESS || - zend_hash_find(ht_opts,"GET",4,(void**)&z_ele)==SUCCESS) && - (Z_TYPE_PP(z_ele)==IS_STRING || Z_TYPE_PP(z_ele)==IS_ARRAY)) - { + if (((z_ele = zend_hash_str_find(ht_opts, "get", sizeof("get") - 1)) != NULL || + (z_ele = zend_hash_str_find(ht_opts, "GET", sizeof("GET") - 1)) != NULL + ) && (Z_TYPE_P(z_ele) == IS_STRING || Z_TYPE_P(z_ele) == IS_ARRAY) + ) { // Disabled in cluster if(slot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -2453,12 +2453,12 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // If it's a string just add it - if(Z_TYPE_PP(z_ele)==IS_STRING) { + if (Z_TYPE_P(z_ele) == IS_STRING) { add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,1); - add_next_index_stringl(z_argv,Z_STRVAL_PP(z_ele), - Z_STRLEN_PP(z_ele), 1); + add_next_index_stringl(z_argv,Z_STRVAL_P(z_ele), + Z_STRLEN_P(z_ele), 1); } else { - HashTable *ht_keys = Z_ARRVAL_PP(z_ele); + HashTable *ht_keys = Z_ARRVAL_P(z_ele); int added=0; for(zend_hash_internal_pointer_reset(ht_keys); @@ -2495,19 +2495,19 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // ALPHA - if((zend_hash_find(ht_opts,"alpha",6,(void**)&z_ele)==SUCCESS || - zend_hash_find(ht_opts,"ALPHA",6,(void**)&z_ele)==SUCCESS) && - Z_TYPE_PP(z_ele)==IS_BOOL && Z_BVAL_PP(z_ele)==1) - { + if (((z_ele = zend_hash_str_find(ht_opts, "alpha", sizeof("alpha") - 1)) != NULL || + (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL + ) && Z_TYPE_P(z_ele) == IS_BOOL && Z_BVAL_P(z_ele) == 1 + ) { add_next_index_stringl(z_argv, "ALPHA", sizeof("ALPHA")-1,1); } // LIMIT - if((zend_hash_find(ht_opts,"limit",6,(void**)&z_ele)==SUCCESS || - zend_hash_find(ht_opts,"LIMIT",6,(void**)&z_ele)==SUCCESS) && - Z_TYPE_PP(z_ele)==IS_ARRAY) - { - HashTable *ht_off = Z_ARRVAL_PP(z_ele); + if (((z_ele = zend_hash_str_find(ht_opts, "limit", sizeof("limit") - 1)) != NULL || + (z_ele = zend_hash_str_find(ht_opts, "LIMIT", sizeof("LIMIT") - 1)) != NULL + ) && Z_TYPE_P(z_ele) == IS_ARRAY + ) { + HashTable *ht_off = Z_ARRVAL_P(z_ele); zval **z_off, **z_cnt; if(zend_hash_index_find(ht_off, 0, (void**)&z_off)==SUCCESS && @@ -2551,16 +2551,17 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, sizeof("SORT")-1); // Iterate through our arguments + zval **z_ele_pp; for(zend_hash_internal_pointer_reset(ht_argv); - zend_hash_get_current_data(ht_argv, (void**)&z_ele)==SUCCESS; + zend_hash_get_current_data(ht_argv, (void**)&z_ele_pp)==SUCCESS; zend_hash_move_forward(ht_argv)) { // Args are strings or longs - if(Z_TYPE_PP(z_ele)==IS_STRING) { - redis_cmd_append_sstr(&cmdstr,Z_STRVAL_PP(z_ele), - Z_STRLEN_PP(z_ele)); + if(Z_TYPE_PP(z_ele_pp)==IS_STRING) { + redis_cmd_append_sstr(&cmdstr,Z_STRVAL_PP(z_ele_pp), + Z_STRLEN_PP(z_ele_pp)); } else { - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_PP(z_ele)); + redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_PP(z_ele_pp)); } } diff --git a/redis_session.c b/redis_session.c index a698ac16b2..8d145abdc1 100644 --- a/redis_session.c +++ b/redis_session.c @@ -189,7 +189,7 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { PS_OPEN_FUNC(redis) { php_url *url; - zval *params, **param; + zval *params, *param; int i, j, path_len; redis_pool *pool = redis_pool_new(TSRMLS_C); @@ -241,32 +241,32 @@ PS_OPEN_FUNC(redis) sapi_module.treat_data(PARSE_STRING, estrdup(url->query), params TSRMLS_CC); - if (zend_hash_find(Z_ARRVAL_P(params), "weight", sizeof("weight"), (void **) ¶m) != FAILURE) { - convert_to_long_ex(param); - weight = Z_LVAL_PP(param); + if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "weight", sizeof("weight") - 1)) != NULL) { + convert_to_long_ex(¶m); + weight = Z_LVAL_P(param); } - if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { - timeout = atof(Z_STRVAL_PP(param)); + if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout") - 1)) != NULL) { + timeout = atof(Z_STRVAL_P(param)); } - if (zend_hash_find(Z_ARRVAL_P(params), "persistent", sizeof("persistent"), (void **) ¶m) != FAILURE) { - persistent = (atol(Z_STRVAL_PP(param)) == 1 ? 1 : 0); + if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "persistent", sizeof("persistent") - 1)) != NULL) { + persistent = (atol(Z_STRVAL_P(param)) == 1 ? 1 : 0); } - if (zend_hash_find(Z_ARRVAL_P(params), "persistent_id", sizeof("persistent_id"), (void **) ¶m) != FAILURE) { - persistent_id = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); + if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "persistent_id", sizeof("persistent_id") - 1)) != NULL) { + persistent_id = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); } - if (zend_hash_find(Z_ARRVAL_P(params), "prefix", sizeof("prefix"), (void **) ¶m) != FAILURE) { - prefix = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); + if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "prefix", sizeof("prefix") - 1)) != NULL) { + prefix = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); } - if (zend_hash_find(Z_ARRVAL_P(params), "auth", sizeof("auth"), (void **) ¶m) != FAILURE) { - auth = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param)); + if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "auth", sizeof("auth") - 1)) != NULL) { + auth = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); } - if (zend_hash_find(Z_ARRVAL_P(params), "database", sizeof("database"), (void **) ¶m) != FAILURE) { - convert_to_long_ex(param); - database = Z_LVAL_PP(param); + if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "database", sizeof("database") - 1)) != NULL) { + convert_to_long_ex(¶m); + database = Z_LVAL_P(param); } - if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { - convert_to_long_ex(param); - retry_interval = Z_LVAL_PP(param); + if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval") - 1)) != NULL) { + convert_to_long_ex(¶m); + retry_interval = Z_LVAL_P(param); } zval_ptr_dtor(¶ms); @@ -470,12 +470,12 @@ PS_GC_FUNC(redis) static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_len, double *val) { - zval **z_val; + zval *z_val; - if (zend_hash_find(ht_conf, key, key_len, (void**)&z_val) == SUCCESS && - Z_TYPE_PP(z_val) == IS_STRING) - { - *val = atof(Z_STRVAL_PP(z_val)); + if ((z_val = zend_hash_str_find(ht_conf, key, key_len - 1)) != NULL && + Z_TYPE_P(z_val) == IS_STRING + ) { + *val = atof(Z_STRVAL_P(z_val)); } } @@ -484,16 +484,16 @@ static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_le * 'false' */ static void session_conf_bool(HashTable *ht_conf, char *key, int keylen, int *retval) { - zval **z_val; + zval *z_val; char *str; int strlen; /* See if we have the option, and it's a string */ - if (zend_hash_find(ht_conf, key, keylen, (void**)&z_val) == SUCCESS && - Z_TYPE_PP(z_val) == IS_STRING) - { - str = Z_STRVAL_PP(z_val); - strlen = Z_STRLEN_PP(z_val); + if ((z_val = zend_hash_str_find(ht_conf, key, keylen - 1)) != NULL && + Z_TYPE_P(z_val) == IS_STRING + ) { + str = Z_STRVAL_P(z_val); + strlen = Z_STRLEN_P(z_val); /* true/yes/1 are treated as true. Everything else is false */ *retval = (strlen == 4 && !strncasecmp(str, "true", 4)) || @@ -519,7 +519,7 @@ static char *cluster_session_key(redisCluster *c, const char *key, int keylen, PS_OPEN_FUNC(rediscluster) { redisCluster *c; - zval *z_conf, **z_val; + zval *z_conf, *z_val; HashTable *ht_conf, *ht_seeds; double timeout = 0, read_timeout = 0; int persistent = 0; @@ -533,8 +533,8 @@ PS_OPEN_FUNC(rediscluster) { /* Sanity check that we're able to parse and have a seeds array */ if (Z_TYPE_P(z_conf) != IS_ARRAY || - zend_hash_find(Z_ARRVAL_P(z_conf), "seed", sizeof("seed"), (void**)&z_val) == FAILURE || - Z_TYPE_PP(z_val) != IS_ARRAY) + (z_val = zend_hash_str_find(Z_ARRVAL_P(z_conf), "seed", sizeof("seed") - 1)) == NULL || + Z_TYPE_P(z_val) != IS_ARRAY) { zval_dtor(z_conf); efree(z_conf); @@ -543,7 +543,7 @@ PS_OPEN_FUNC(rediscluster) { /* Grab a copy of our config hash table and keep seeds array */ ht_conf = Z_ARRVAL_P(z_conf); - ht_seeds = Z_ARRVAL_PP(z_val); + ht_seeds = Z_ARRVAL_P(z_val); /* Grab timeouts if they were specified */ session_conf_timeout(ht_conf, "timeout", sizeof("timeout"), &timeout); @@ -562,23 +562,23 @@ PS_OPEN_FUNC(rediscluster) { } /* Look for a specific prefix */ - if (zend_hash_find(ht_conf, "prefix", sizeof("prefix"), (void**)&z_val) == SUCCESS && - Z_TYPE_PP(z_val) == IS_STRING && Z_STRLEN_PP(z_val) > 0) - { - prefix = Z_STRVAL_PP(z_val); - prefix_len = Z_STRLEN_PP(z_val); + if ((z_val = zend_hash_str_find(ht_conf, "prefix", sizeof("prefix") - 1)) != NULL && + Z_TYPE_P(z_val) == IS_STRING && Z_STRLEN_P(z_val) > 0 + ) { + prefix = Z_STRVAL_P(z_val); + prefix_len = Z_STRLEN_P(z_val); } else { prefix = "PHPREDIS_CLUSTER_SESSION:"; prefix_len = sizeof("PHPREDIS_CLUSTER_SESSION:")-1; } /* Look for a specific failover setting */ - if (zend_hash_find(ht_conf, "failover", sizeof("failover"), (void**)&z_val) == SUCCESS && - Z_TYPE_PP(z_val) == IS_STRING) - { - if (!strcasecmp(Z_STRVAL_PP(z_val), "error")) { + if ((z_val = zend_hash_str_find(ht_conf, "failover", sizeof("failover") - 1)) != NULL && + Z_TYPE_P(z_val) == IS_STRING + ) { + if (!strcasecmp(Z_STRVAL_P(z_val), "error")) { failover = REDIS_FAILOVER_ERROR; - } else if (!strcasecmp(Z_STRVAL_PP(z_val), "distribute")) { + } else if (!strcasecmp(Z_STRVAL_P(z_val), "distribute")) { failover = REDIS_FAILOVER_DISTRIBUTE; } } From afcd8445f70f8e6dfc555e71da8ffd2b8911d4a9 Mon Sep 17 00:00:00 2001 From: sitri Date: Fri, 9 Sep 2016 21:49:44 +0900 Subject: [PATCH 0578/1986] fix #942 --- redis.c | 3 +-- tests/RedisTest.php | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/redis.c b/redis.c index 41724c1098..0de74bd78e 100644 --- a/redis.c +++ b/redis.c @@ -855,8 +855,7 @@ PHP_METHOD(Redis, setex) */ PHP_METHOD(Redis, psetex) { - REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, - redis_string_response); + REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, redis_boolean_response); } /* {{{ proto boolean Redis::setnx(string key, string value) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 940add7191..e8833dc9f1 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -456,6 +456,13 @@ public function testSetEx() { $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', 7 * 1000, 'val') === TRUE); + $this->assertTrue($this->redis->ttl('key') ===7); + $this->assertTrue($this->redis->get('key') === 'val'); + } public function testSetNX() { From 46c2de1b8a9b6b9eb150f5d48026242e344b77b3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 12 Sep 2016 10:58:36 +0300 Subject: [PATCH 0579/1986] WIP: php7 compatibility Redefine `zend_hash_get_current_data` macro as static inline function + add `zend_hash_get_current_data_ptr` function --- cluster_library.c | 49 ++++++++++++------------ common.h | 24 ++++++++++++ library.c | 18 ++++----- redis.c | 8 ++-- redis_array.c | 6 +-- redis_array_impl.c | 35 ++++++++---------- redis_cluster.c | 32 ++++++++-------- redis_cluster.h | 8 ++-- redis_commands.c | 92 ++++++++++++++++++++++++---------------------- 9 files changed, 149 insertions(+), 123 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 58380e3046..a8c5525a4f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -10,14 +10,15 @@ extern zend_class_entry *redis_cluster_exception_ce; /* Debugging methods/ static void cluster_dump_nodes(redisCluster *c) { - redisClusterNode **pp, *p; + redisClusterNode *p; for(zend_hash_internal_pointer_reset(c->nodes); zend_hash_has_more_elements(c->nodes)==SUCCESS; zend_hash_move_forward(c->nodes)) { - zend_hash_get_current_data(c->nodes, (void**)&pp); - p = *pp; + if ((p = zend_hash_get_current_data_ptr(c->nodes)) == NULL) { + continue; + } const char *slave = (p->slave) ? "slave" : "master"; php_printf("%d %s %d %d", p->sock->port, slave,p->sock->prefix_len, @@ -842,7 +843,7 @@ PHP_REDIS_API void cluster_free(redisCluster *c) { /* Takes our input hash table and returns a straigt C array with elements, * which have been randomized. The return value needs to be freed. */ static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) { - zval **z_seeds, **z_seed; + zval **z_seeds; int *map, i, count, index=0; /* How many */ @@ -861,9 +862,7 @@ static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) { zend_hash_has_more_elements(seeds) == SUCCESS; zend_hash_move_forward(seeds)) { - zend_hash_get_current_data(seeds, (void**)&z_seed); - z_seeds[map[index]] = *z_seed; - index++; + z_seeds[map[index++]] = zend_hash_get_current_data(seeds); } efree(map); @@ -921,7 +920,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { /* Initial mapping of our cluster keyspace */ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { - RedisSock **seed; + RedisSock *seed; clusterReply *slots=NULL; int mapped=0; @@ -931,22 +930,24 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { zend_hash_move_forward(c->seeds)) { // Grab the redis_sock for this seed - zend_hash_get_current_data(c->seeds, (void**)&seed); + if ((seed = zend_hash_get_current_data_ptr(c->seeds)) == NULL) { + continue; + } // Attempt to connect to this seed node - if(redis_sock_connect(*seed TSRMLS_CC)!=0) { + if (redis_sock_connect(seed TSRMLS_CC) != 0) { continue; } // Parse out cluster nodes. Flag mapped if we are valid - slots = cluster_get_slots(*seed TSRMLS_CC); + slots = cluster_get_slots(seed TSRMLS_CC); if(slots) mapped = !cluster_map_slots(c, slots); // Bin anything mapped, if we failed somewhere if(!mapped && slots) { memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); } - redis_sock_disconnect(*seed TSRMLS_CC); + redis_sock_disconnect(seed TSRMLS_CC); } // Clean up slots reply if we got one @@ -1064,14 +1065,14 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type /* Disconnect from each node we're connected to */ PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC) { - redisClusterNode **node; + redisClusterNode *node; for(zend_hash_internal_pointer_reset(c->nodes); - zend_hash_get_current_data(c->nodes, (void**)&node)==SUCCESS; + (node = zend_hash_get_current_data_ptr(c->nodes)) != NULL; zend_hash_move_forward(c->nodes)) { - redis_sock_disconnect((*node)->sock TSRMLS_CC); - (*node)->sock->lazy_connect = 1; + redis_sock_disconnect(node->sock TSRMLS_CC); + node->sock->lazy_connect = 1; } } @@ -1156,7 +1157,7 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, int direct TSRMLS_DC) { - redisClusterNode **seed_node; + redisClusterNode *seed_node; RedisSock *redis_sock; int failover, nomaster; @@ -1209,18 +1210,20 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, zend_hash_move_forward(c->nodes)) { /* Grab node */ - zend_hash_get_current_data(c->nodes, (void**)&seed_node); + if ((seed_node = zend_hash_get_current_data_ptr(c->nodes)) == NULL) { + continue; + } /* Skip this node if it's the one that failed, or if it's a slave */ - if((*seed_node)->sock == redis_sock || (*seed_node)->slave) continue; + if(seed_node->sock == redis_sock || seed_node->slave) continue; /* Connect to this node if we haven't already */ - CLUSTER_LAZY_CONNECT((*seed_node)->sock); + CLUSTER_LAZY_CONNECT(seed_node->sock); /* Attempt to write our request to this node */ - if (CLUSTER_SEND_PAYLOAD((*seed_node)->sock, cmd, sz)) { - c->cmd_slot = (*seed_node)->slot; - c->cmd_sock = (*seed_node)->sock; + if (CLUSTER_SEND_PAYLOAD(seed_node->sock, cmd, sz)) { + c->cmd_slot = seed_node->slot; + c->cmd_sock = seed_node->sock; return 0; } } diff --git a/common.h b/common.h index 6d1aeef99e..01b59ba1d0 100644 --- a/common.h +++ b/common.h @@ -42,6 +42,30 @@ zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) } return NULL; } + +#undef zend_hash_get_current_data +static zend_always_inline zval * +zend_hash_get_current_data(HashTable *ht) +{ + zval **zv; + + if (zend_hash_get_current_data_ex(ht, (void **)&zv, NULL) == SUCCESS) { + return *zv; + } + return NULL; +} + +static zend_always_inline void * +zend_hash_get_current_data_ptr(HashTable *ht) +{ + void **ptr; + + if (zend_hash_get_current_data_ex(ht, (void **)&ptr, NULL) == SUCCESS) { + return *ptr; + } + return NULL; +} + #else #include #endif diff --git a/library.c b/library.c index 91f86c6ba9..f2c9572ffe 100644 --- a/library.c +++ b/library.c @@ -1233,39 +1233,39 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, unsigned int tablekey_len; int hkey_len; unsigned long idx; - zval **z_key_pp, **z_value_pp; + zval *z_key_p, *z_value_p; zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_key_pp) == FAILURE) { + if ((z_key_p = zend_hash_get_current_data(keytable)) == NULL) { continue; /* this should never happen, according to the PHP people. */ } /* get current value, a key */ - convert_to_string(*z_key_pp); - hkey = Z_STRVAL_PP(z_key_pp); - hkey_len = Z_STRLEN_PP(z_key_pp); + convert_to_string(z_key_p); + hkey = Z_STRVAL_P(z_key_p); + hkey_len = Z_STRLEN_P(z_key_p); /* move forward */ zend_hash_move_forward(keytable); /* fetch again */ zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) == FAILURE) { + if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) { continue; /* this should never happen, according to the PHP people. */ } /* get current value, a hash value now. */ - hval = Z_STRVAL_PP(z_value_pp); + hval = Z_STRVAL_P(z_value_p); /* Decode the score depending on flag */ - if (decode == SCORE_DECODE_INT && Z_STRLEN_PP(z_value_pp) > 0) { + if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) { add_assoc_long_ex(z_ret, hkey, 1+hkey_len, atoi(hval+1)); } else if (decode == SCORE_DECODE_DOUBLE) { add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); } else { zval *z = NULL; MAKE_STD_ZVAL(z); - *z = **z_value_pp; + *z = *z_value_p; zval_copy_ctor(z); add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); } diff --git a/redis.c b/redis.c index e475fc1d15..a3b993ba61 100644 --- a/redis.c +++ b/redis.c @@ -1856,15 +1856,13 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { int val_len; unsigned long idx; int type; - zval **z_value_pp; + zval *z_value_p; int val_free, key_free; char buf[32]; type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, 0, NULL); - if(zend_hash_get_current_data(keytable, (void**)&z_value_pp) - == FAILURE) - { + if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) { /* Should never happen, according to the PHP people */ continue; } @@ -1884,7 +1882,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { if(step == 0) argc++; /* found a valid arg */ - val_free = redis_serialize(redis_sock, *z_value_pp, &val, &val_len + val_free = redis_serialize(redis_sock, z_value_p, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len); diff --git a/redis_array.c b/redis_array.c index 9dea53e406..fb4b624b4e 100644 --- a/redis_array.c +++ b/redis_array.c @@ -989,7 +989,7 @@ PHP_METHOD(RedisArray, mget) /* MSET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mset) { - zval *object, *z_keys, z_fun, *z_argarray, **data, z_ret; + zval *object, *z_keys, z_fun, *z_argarray, *data, z_ret; int i, n; RedisArray *ra; int *pos, argc, *argc_each; @@ -1034,7 +1034,7 @@ PHP_METHOD(RedisArray, mset) zend_hash_move_forward(h_keys), i++) { /* We have to skip the element if we can't get the array value */ - if(zend_hash_get_current_data(h_keys, (void**)&data) == FAILURE) { + if ((data = zend_hash_get_current_data(h_keys)) == NULL) { continue; } @@ -1052,7 +1052,7 @@ PHP_METHOD(RedisArray, mset) redis_instances[i] = ra_find_node(ra, key, (int)key_len, &pos[i] TSRMLS_CC); argc_each[pos[i]]++; /* count number of keys per node */ - argv[i] = *data; + argv[i] = data; keys[i] = key; key_lens[i] = (int)key_len; } diff --git a/redis_array_impl.c b/redis_array_impl.c index 9562663a41..9f95abb53b 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -35,7 +35,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b int i = 0, host_len, id; char *host, *p; short port; - zval **zpData, z_cons, z_ret; + zval *zpData, z_cons, z_ret; RedisSock *redis_sock = NULL; /* function calls on the Redis object */ @@ -44,7 +44,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b /* init connections */ for (zend_hash_internal_pointer_reset(hosts); zend_hash_has_more_elements(hosts) == SUCCESS; zend_hash_move_forward(hosts)) { - if ((zend_hash_get_current_data(hosts, (void **) &zpData) == FAILURE) || (Z_TYPE_PP(zpData) != IS_STRING)) + if ((zpData = zend_hash_get_current_data(hosts)) == NULL || Z_TYPE_P(zpData) != IS_STRING) { for(i=0;icount;i++) { zval_dtor(ra->redis[i]); @@ -59,11 +59,11 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b return NULL; } - ra->hosts[i] = estrdup(Z_STRVAL_PP(zpData)); + ra->hosts[i] = estrdup(Z_STRVAL_P(zpData)); /* default values */ - host = Z_STRVAL_PP(zpData); - host_len = Z_STRLEN_PP(zpData); + host = Z_STRVAL_P(zpData); + host_len = Z_STRLEN_P(zpData); port = 6379; if((p = strrchr(host, ':'))) { /* found port */ @@ -766,7 +766,7 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long int i; zval z_fun_type, z_ret, *z_arg; - zval **z_data; + zval *z_data; long success = 1; MAKE_STD_ZVAL(z_arg); @@ -795,17 +795,12 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long zend_hash_has_more_elements(retHash) == SUCCESS; zend_hash_move_forward(retHash)) { - if(zend_hash_get_current_data(retHash, (void**)&z_data) == FAILURE) { - success = 0; - break; - } - if(Z_TYPE_PP(z_data) != IS_LONG) { + if ((z_data = zend_hash_get_current_data(retHash)) == NULL || Z_TYPE_P(z_data) != IS_LONG) { success = 0; break; } /* Get the result - Might change in the future to handle doubles as well */ - res[i] = Z_LVAL_PP(z_data); - i++; + res[i++] = Z_LVAL_P(z_data); } } zval_dtor(&z_ret); @@ -883,7 +878,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { static zend_bool ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_zrange, z_fun_zadd, z_ret, z_ret_dest, *z_args[4], **z_zadd_args, **z_score_pp; + zval z_fun_zrange, z_fun_zadd, z_ret, z_ret_dest, *z_args[4], **z_zadd_args, *z_score_p; int count; HashTable *h_zset_vals; char *val; @@ -923,14 +918,14 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS zend_hash_has_more_elements(h_zset_vals) == SUCCESS; zend_hash_move_forward(h_zset_vals)) { - if(zend_hash_get_current_data(h_zset_vals, (void**)&z_score_pp) == FAILURE) { + if ((z_score_p = zend_hash_get_current_data(h_zset_vals)) == NULL) { continue; } /* add score */ - convert_to_double(*z_score_pp); + convert_to_double(z_score_p); MAKE_STD_ZVAL(z_zadd_args[i]); - ZVAL_DOUBLE(z_zadd_args[i], Z_DVAL_PP(z_score_pp)); + ZVAL_DOUBLE(z_zadd_args[i], Z_DVAL_P(z_score_p)); /* add value */ MAKE_STD_ZVAL(z_zadd_args[i+1]); @@ -1056,7 +1051,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, int list_count, const char **cmd_list, int add_count, const char **cmd_add, long ttl TSRMLS_DC) { - zval z_fun_retrieve, z_fun_sadd, z_ret, **z_retrieve_args, **z_sadd_args, **z_data_pp; + zval z_fun_retrieve, z_fun_sadd, z_ret, **z_retrieve_args, **z_sadd_args, *z_data_p; int count, i; HashTable *h_set_vals; @@ -1099,13 +1094,13 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, zend_hash_has_more_elements(h_set_vals) == SUCCESS; zend_hash_move_forward(h_set_vals), i++) { - if(zend_hash_get_current_data(h_set_vals, (void**)&z_data_pp) == FAILURE) { + if ((z_data_p = zend_hash_get_current_data(h_set_vals)) == NULL) { continue; } /* add set elements */ MAKE_STD_ZVAL(z_sadd_args[i+1]); - *(z_sadd_args[i+1]) = **z_data_pp; + *(z_sadd_args[i+1]) = *z_data_p; zval_copy_ctor(z_sadd_args[i+1]); } diff --git a/redis_cluster.c b/redis_cluster.c index d4bbed4954..799a576ed1 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -982,7 +982,7 @@ PHP_METHOD(RedisCluster, exists) { /* {{{ proto array Redis::keys(string pattern) */ PHP_METHOD(RedisCluster, keys) { redisCluster *c = GET_CONTEXT(); - redisClusterNode **node; + redisClusterNode *node; int pat_len, pat_free, cmd_len; char *pat, *cmd; clusterReply *resp; @@ -1008,14 +1008,14 @@ PHP_METHOD(RedisCluster, keys) { /* Iterate over our known nodes */ for(zend_hash_internal_pointer_reset(c->nodes); - zend_hash_get_current_data(c->nodes, (void**)&node)==SUCCESS; + (node = zend_hash_get_current_data_ptr(c->nodes)) != NULL; zend_hash_move_forward(c->nodes)) { - if(cluster_send_slot(c, (*node)->slot, cmd, cmd_len, TYPE_MULTIBULK + if (cluster_send_slot(c, node->slot, cmd, cmd_len, TYPE_MULTIBULK TSRMLS_CC)<0) { php_error_docref(0 TSRMLS_CC, E_ERROR, "Can't send KEYS to %s:%d", - (*node)->sock->host, (*node)->sock->port); + node->sock->host, node->sock->port); efree(cmd); RETURN_FALSE; } @@ -1024,8 +1024,8 @@ PHP_METHOD(RedisCluster, keys) { resp = cluster_read_resp(c TSRMLS_CC); if(!resp) { php_error_docref(0 TSRMLS_CC, E_WARNING, - "Can't read response from %s:%d", (*node)->sock->host, - (*node)->sock->port); + "Can't read response from %s:%d", node->sock->host, + node->sock->port); continue; } @@ -2009,7 +2009,7 @@ PHP_METHOD(RedisCluster, _unserialize) { PHP_METHOD(RedisCluster, _masters) { redisCluster *c = GET_CONTEXT(); zval *z_ret, *z_sub; - redisClusterNode **node; + redisClusterNode *node; char *host; short port; @@ -2017,11 +2017,11 @@ PHP_METHOD(RedisCluster, _masters) { array_init(z_ret); for(zend_hash_internal_pointer_reset(c->nodes); - zend_hash_get_current_data(c->nodes, (void**)&node)==SUCCESS; + (node = zend_hash_get_current_data_ptr(c->nodes)) != NULL; zend_hash_move_forward(c->nodes)) { - host = (*node)->sock->host; - port = (*node)->sock->port; + host = node->sock->host; + port = node->sock->port; MAKE_STD_ZVAL(z_sub); array_init(z_sub); @@ -2073,7 +2073,7 @@ PHP_METHOD(RedisCluster, multi) { PHP_METHOD(RedisCluster, watch) { redisCluster *c = GET_CONTEXT(); HashTable *ht_dist; - clusterDistList **dl; + clusterDistList *dl; smart_string cmd = {0}; zval **z_args; int argc = ZEND_NUM_ARGS(), i; @@ -2122,7 +2122,7 @@ PHP_METHOD(RedisCluster, watch) { zend_hash_move_forward(ht_dist)) { // Grab the clusterDistList pointer itself - if(zend_hash_get_current_data(ht_dist, (void**)&dl)==FAILURE) { + if ((dl = zend_hash_get_current_data_ptr(ht_dist)) == NULL) { zend_throw_exception(redis_cluster_exception_ce, "Internal error in a PHP HashTable", 0 TSRMLS_CC); cluster_dist_free(ht_dist); @@ -2132,10 +2132,10 @@ PHP_METHOD(RedisCluster, watch) { } // Construct our watch command for this node - redis_cmd_init_sstr(&cmd, (*dl)->len, "WATCH", sizeof("WATCH")-1); - for(i=0;i<(*dl)->len;i++) { - redis_cmd_append_sstr(&cmd, (*dl)->entry[i].key, - (*dl)->entry[i].key_len); + redis_cmd_init_sstr(&cmd, dl->len, "WATCH", sizeof("WATCH")-1); + for (i = 0; i < dl->len; i++) { + redis_cmd_append_sstr(&cmd, dl->entry[i].key, + dl->entry[i].key_len); } // If we get a failure from this, we have to abort diff --git a/redis_cluster.h b/redis_cluster.h index 6f2a7ca308..4a7facfdd6 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -47,13 +47,13 @@ /* Reset anything flagged as MULTI */ #define CLUSTER_RESET_MULTI(c) \ - redisClusterNode **_node; \ + redisClusterNode *_node; \ for(zend_hash_internal_pointer_reset(c->nodes); \ - zend_hash_get_current_data(c->nodes, (void**)&_node); \ + (_node = zend_hash_get_current_data_ptr(c->nodes)) != NULL; \ zend_hash_move_forward(c->nodes)) \ { \ - (*_node)->sock->watching = 0; \ - (*_node)->sock->mode = ATOMIC; \ + _node->sock->watching = 0; \ + _node->sock->mode = ATOMIC; \ } \ c->flags->watching = 0; \ c->flags->mode = ATOMIC; \ diff --git a/redis_commands.c b/redis_commands.c index 1c216d43c0..35235672c6 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -222,6 +222,8 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set slot if directed CMD_SET_SLOT(slot,key,key_len); + if (key_free) efree(key); + return SUCCESS; } @@ -529,7 +531,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int start_len, end_len; int has_limit=0, type; long offset, count; - zval *z_opt=NULL, **z_ele; + zval *z_opt=NULL, *z_ele; unsigned long idx; unsigned int optlen; HashTable *ht_opt; @@ -550,18 +552,18 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { /* Grab current key and value */ type = zend_hash_get_current_key_ex(ht_opt, &optkey, &optlen, &idx, 0, NULL); - zend_hash_get_current_data(ht_opt, (void**)&z_ele); + if ((z_ele = zend_hash_get_current_data(ht_opt)) == NULL) { + continue; + } /* All options require a string key type */ if (type != HASH_KEY_IS_STRING) continue; /* Check for withscores and limit */ if (IS_WITHSCORES_ARG(optkey, optlen)) { - *withscores = Z_TYPE_PP(z_ele)==IS_BOOL && Z_BVAL_PP(z_ele)==1; - } else if(IS_LIMIT_ARG(optkey, optlen) && - Z_TYPE_PP(z_ele) == IS_ARRAY) - { - HashTable *htlimit = Z_ARRVAL_PP(z_ele); + *withscores = (Z_TYPE_P(z_ele) == IS_BOOL && Z_BVAL_P(z_ele) == 1); + } else if (IS_LIMIT_ARG(optkey, optlen) && Z_TYPE_P(z_ele) == IS_ARRAY) { + HashTable *htlimit = Z_ARRVAL_P(z_ele); zval **zoff, **zcnt; /* We need two arguments (offset and count) */ @@ -1078,7 +1080,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, int kw_len, int min_argc, int has_timeout, char **cmd, int *cmd_len, short *slot) { - zval **z_args, **z_ele; + zval **z_args, *z_ele; HashTable *ht_arr; char *key; int key_free, key_len, i, tail; @@ -1125,12 +1127,12 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(single_array) { for(zend_hash_internal_pointer_reset(ht_arr); - zend_hash_get_current_data(ht_arr,(void**)&z_ele)==SUCCESS; + (z_ele = zend_hash_get_current_data(ht_arr)) != NULL; zend_hash_move_forward(ht_arr)) { - convert_to_string(*z_ele); - key = Z_STRVAL_PP(z_ele); - key_len = Z_STRLEN_PP(z_ele); + convert_to_string(z_ele); + key = Z_STRVAL_P(z_ele); + key_len = Z_STRLEN_P(z_ele); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Protect against CROSSLOT errors @@ -1138,6 +1140,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(kslot == -1) { kslot = cluster_hash_key(key, key_len); } else if(cluster_hash_key(key,key_len)!=kslot) { + if(key_free) efree(key); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not all keys hash to the same slot!"); return FAILURE; @@ -1239,7 +1242,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, unsigned int ht_key_len; unsigned long idx; char *k; - zval **v; + zval *v; /* Iterate our option array */ for(zend_hash_internal_pointer_reset(kt); @@ -1248,7 +1251,9 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { // Grab key and value type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL); - zend_hash_get_current_data(kt, (void**)&v); + if ((v = zend_hash_get_current_data(kt)) == NULL) { + continue; + } /* Detect PX or EX argument and validate timeout */ if (type == HASH_KEY_IS_STRING && IS_EX_PX_ARG(k)) { @@ -1256,16 +1261,16 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, exp_type = k; /* Try to extract timeout */ - if (Z_TYPE_PP(v) == IS_LONG) { - expire = Z_LVAL_PP(v); - } else if (Z_TYPE_PP(v) == IS_STRING) { - expire = atol(Z_STRVAL_PP(v)); + if (Z_TYPE_P(v) == IS_LONG) { + expire = Z_LVAL_P(v); + } else if (Z_TYPE_P(v) == IS_STRING) { + expire = atol(Z_STRVAL_P(v)); } /* Expiry can't be set < 1 */ if (expire < 1) return FAILURE; - } else if (Z_TYPE_PP(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_PP(v))) { - set_type = Z_STRVAL_PP(v); + } else if (Z_TYPE_P(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_P(v))) { + set_type = Z_STRVAL_P(v); } } } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { @@ -1692,6 +1697,8 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set our slot CMD_SET_SLOT(slot, key, key_len); + if (key_free) efree(key); + return SUCCESS; } @@ -2465,20 +2472,18 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_hash_has_more_elements(ht_keys)==SUCCESS; zend_hash_move_forward(ht_keys)) { - zval **z_key; + zval *z_key; // If we can't get the data, or it's not a string, skip - if(zend_hash_get_current_data(ht_keys,(void**)&z_key)==FAILURE) - continue; - if(Z_TYPE_PP(z_key)!=IS_STRING) + if ((z_key = zend_hash_get_current_data(ht_keys)) == NULL || Z_TYPE_P(z_key) != IS_STRING) { continue; - + } /* Add get per thing we're getting */ add_next_index_stringl(z_argv, "GET", sizeof("GET")-1, 1); // Add this key to our argv array - add_next_index_stringl(z_argv, Z_STRVAL_PP(z_key), - Z_STRLEN_PP(z_key), 1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_key), + Z_STRLEN_P(z_key), 1); added++; } @@ -2551,17 +2556,16 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, sizeof("SORT")-1); // Iterate through our arguments - zval **z_ele_pp; for(zend_hash_internal_pointer_reset(ht_argv); - zend_hash_get_current_data(ht_argv, (void**)&z_ele_pp)==SUCCESS; + (z_ele = zend_hash_get_current_data(ht_argv)) != NULL; zend_hash_move_forward(ht_argv)) { // Args are strings or longs - if(Z_TYPE_PP(z_ele_pp)==IS_STRING) { - redis_cmd_append_sstr(&cmdstr,Z_STRVAL_PP(z_ele_pp), - Z_STRLEN_PP(z_ele_pp)); + if (Z_TYPE_P(z_ele) == IS_STRING) { + redis_cmd_append_sstr(&cmdstr,Z_STRVAL_P(z_ele), + Z_STRLEN_P(z_ele)); } else { - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_PP(z_ele_pp)); + redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele)); } } @@ -2775,7 +2779,7 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, unsigned long idx; unsigned int optkeylen; char *optkey, *optstr; - zval **optval; + zval *optval; /* Iterate over our argument array, collating which ones we have */ for (zend_hash_internal_pointer_reset(ht); @@ -2784,14 +2788,16 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, { /* Grab key and value */ type = zend_hash_get_current_key_ex(ht, &optkey, &optkeylen, &idx, 0, NULL); - zend_hash_get_current_data(ht, (void**)&optval); + if ((optval = zend_hash_get_current_data(ht)) == NULL) { + continue; + } /* If the key is numeric it's a non value option */ if (type == HASH_KEY_IS_LONG) { /* Option needs to be a string */ - if (Z_TYPE_PP(optval) != IS_STRING) continue; + if (Z_TYPE_P(optval) != IS_STRING) continue; - optstr = Z_STRVAL_PP(optval); + optstr = Z_STRVAL_P(optval); if (!strcasecmp(optstr, "withcoord")) { *withcoord = 1; @@ -2804,8 +2810,8 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, } else if (!strcasecmp(optstr, "desc")) { *sort = SORT_DESC; } - } else if (!strcasecmp(optkey, "count") && Z_TYPE_PP(optval) == IS_LONG) { - *count = Z_LVAL_PP(optval); + } else if (!strcasecmp(optkey, "count") && Z_TYPE_P(optval) == IS_LONG) { + *count = Z_LVAL_P(optval); } } } @@ -3071,7 +3077,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - zval **z_ele; + zval *z_ele; HashTable *ht_arr = Z_ARRVAL_P(z_arg); smart_string cmdstr = {0}; @@ -3079,11 +3085,11 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, "GETKEYS", sizeof("GETKEYS")-1); for(zend_hash_internal_pointer_reset(ht_arr); - zend_hash_get_current_data(ht_arr, (void**)&z_ele)==SUCCESS; + (z_ele = zend_hash_get_current_data(ht_arr)) != NULL; zend_hash_move_forward(ht_arr)) { - convert_to_string(*z_ele); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL_PP(z_ele), Z_STRLEN_PP(z_ele)); + convert_to_string(z_ele); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } *cmd = cmdstr.c; From d7931ab34063d19cded53dbafb9df6eefc5cc341 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 14 Sep 2016 16:46:23 +0300 Subject: [PATCH 0580/1986] WIP: php7 compatibility ZEND_HASH_FOREACH_VAL now works with zval * instead of zval ** --- common.h | 9 +++-- redis.c | 36 +++++++++--------- redis_array.c | 29 +++++++-------- redis_array_impl.c | 8 ++-- redis_cluster.c | 8 ++-- redis_commands.c | 92 +++++++++++++++++++++++----------------------- 6 files changed, 91 insertions(+), 91 deletions(-) diff --git a/common.h b/common.h index 01b59ba1d0..aec483d63e 100644 --- a/common.h +++ b/common.h @@ -13,13 +13,16 @@ typedef smart_str smart_string; #define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) #define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ + zval **_pData; \ HashPosition _hpos; \ for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - zend_hash_get_current_data_ex(ht, (void **) &_val, &_hpos) == SUCCESS; \ + zend_hash_get_current_data_ex(ht, (void **) &_pData, &_hpos) == SUCCESS; \ zend_hash_move_forward_ex(ht, &_hpos) \ - ) + ) { _val = *_pData; -#define ZEND_HASH_FOREACH_END() } while(0) +#define ZEND_HASH_FOREACH_END() \ + } \ + } while(0) static zend_always_inline zval * zend_hash_str_find(const HashTable *ht, const char *key, size_t len) diff --git a/redis.c b/redis.c index a3b993ba61..b86684dd3f 100644 --- a/redis.c +++ b/redis.c @@ -960,7 +960,7 @@ PHP_METHOD(Redis, decrBy){ */ PHP_METHOD(Redis, getMultiple) { - zval *object, *z_args, **z_ele; + zval *object, *z_args, *z_ele; HashTable *hash; RedisSock *redis_sock; smart_string cmd = {0}; @@ -995,12 +995,12 @@ PHP_METHOD(Redis, getMultiple) zval *z_tmp = NULL; /* If the key isn't a string, turn it into one */ - if(Z_TYPE_PP(z_ele) == IS_STRING) { - key = Z_STRVAL_PP(z_ele); - key_len = Z_STRLEN_PP(z_ele); + if (Z_TYPE_P(z_ele) == IS_STRING) { + key = Z_STRVAL_P(z_ele); + key_len = Z_STRLEN_P(z_ele); } else { MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_ele; + *z_tmp = *z_ele; zval_copy_ctor(z_tmp); convert_to_string(z_tmp); @@ -2535,7 +2535,7 @@ PHP_METHOD(Redis, subscribe) { PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd) { - zval *object, *array, **data; + zval *object, *array, *data; HashTable *arr_hash; RedisSock *redis_sock; char *cmd = "", *old_cmd = NULL; @@ -2560,12 +2560,12 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, } ZEND_HASH_FOREACH_VAL(arr_hash, data) { - if (Z_TYPE_PP(data) == IS_STRING) { + if (Z_TYPE_P(data) == IS_STRING) { char *old_cmd = NULL; if(*cmd) { old_cmd = cmd; } - cmd_len = spprintf(&cmd, 0, "%s %s", cmd, Z_STRVAL_PP(data)); + cmd_len = spprintf(&cmd, 0, "%s %s", cmd, Z_STRVAL_P(data)); if(old_cmd) { efree(old_cmd); } @@ -2880,7 +2880,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, zval *arg TSRMLS_DC) { HashTable *ht_chan; - zval **z_ele; + zval *z_ele; char *key; int cmd_len, key_len, key_free; smart_string cmd = {0}; @@ -2922,12 +2922,12 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, int key_len, key_free; zval *z_tmp = NULL; - if(Z_TYPE_PP(z_ele) == IS_STRING) { - key = Z_STRVAL_PP(z_ele); - key_len = Z_STRLEN_PP(z_ele); + if (Z_TYPE_P(z_ele) == IS_STRING) { + key = Z_STRVAL_P(z_ele); + key_len = Z_STRLEN_P(z_ele); } else { MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_ele; + *z_tmp = *z_ele; zval_copy_ctor(z_tmp); convert_to_string(z_tmp); @@ -3045,7 +3045,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) { - zval **elem; + zval *elem; HashTable *args_hash; int cmd_len, args_count = 0; int eval_cmd_count = 2; @@ -3074,13 +3074,13 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *key, *old_cmd; int key_len, key_free; - if(Z_TYPE_PP(elem) == IS_STRING) { - key = Z_STRVAL_PP(elem); - key_len = Z_STRLEN_PP(elem); + if (Z_TYPE_P(elem) == IS_STRING) { + key = Z_STRVAL_P(elem); + key_len = Z_STRLEN_P(elem); } else { /* Convert it to a string */ MAKE_STD_ZVAL(z_tmp); - *z_tmp = **elem; + *z_tmp = *elem; zval_copy_ctor(z_tmp); convert_to_string(z_tmp); diff --git a/redis_array.c b/redis_array.c index fb4b624b4e..c6dcea66e8 100644 --- a/redis_array.c +++ b/redis_array.c @@ -328,7 +328,7 @@ PHP_METHOD(RedisArray, __construct) static void ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, int cmd_len, zval *z_args, zval *z_new_target) { - zval **zp_tmp, z_tmp; + zval *zp_tmp, z_tmp; char *key = NULL; /* set to avoid "unused-but-set-variable" */ int key_len; int i; @@ -373,8 +373,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* copy args to array */ i = 0; ZEND_HASH_FOREACH_VAL(h_args, zp_tmp) { - z_callargs[i] = *zp_tmp; - i++; + z_callargs[i++] = zp_tmp; } ZEND_HASH_FOREACH_END(); /* multi/exec */ @@ -837,7 +836,7 @@ PHP_METHOD(RedisArray, select) /* MGET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mget) { - zval *object, *z_keys, z_fun, *z_argarray, **data, *z_ret, **z_cur, *z_tmp_array, *z_tmp; + zval *object, *z_keys, z_fun, *z_argarray, *data, *z_ret, **z_cur, *z_tmp_array, *z_tmp; int i, j, n; RedisArray *ra; int *pos, argc, *argc_each; @@ -878,7 +877,7 @@ PHP_METHOD(RedisArray, mget) char kbuf[40], *key_lookup; /* phpredis proper can only use string or long keys, so restrict to that here */ - if(Z_TYPE_PP(data) != IS_STRING && Z_TYPE_PP(data) != IS_LONG) { + if (Z_TYPE_P(data) != IS_STRING && Z_TYPE_P(data) != IS_LONG) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs"); efree(argv); efree(pos); @@ -888,11 +887,11 @@ PHP_METHOD(RedisArray, mget) } /* Convert to a string for hash lookup if it isn't one */ - if(Z_TYPE_PP(data) == IS_STRING) { - key_len = Z_STRLEN_PP(data); - key_lookup = Z_STRVAL_PP(data); + if (Z_TYPE_P(data) == IS_STRING) { + key_len = Z_STRLEN_P(data); + key_lookup = Z_STRVAL_P(data); } else { - key_len = snprintf(kbuf, sizeof(kbuf), "%ld", Z_LVAL_PP(data)); + key_len = snprintf(kbuf, sizeof(kbuf), "%ld", Z_LVAL_P(data)); key_lookup = (char*)kbuf; } @@ -900,8 +899,7 @@ PHP_METHOD(RedisArray, mget) redis_instances[i] = ra_find_node(ra, key_lookup, key_len, &pos[i] TSRMLS_CC); argc_each[pos[i]]++; /* count number of keys per node */ - argv[i] = *data; - i++; + argv[i++] = data; } ZEND_HASH_FOREACH_END(); /* prepare return value */ @@ -1128,7 +1126,7 @@ PHP_METHOD(RedisArray, mset) /* DEL will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, del) { - zval *object, *z_keys, z_fun, *z_argarray, **data, *z_ret, *z_tmp, **z_args; + zval *object, *z_keys, z_fun, *z_argarray, *data, *z_ret, *z_tmp, **z_args; int i, n; RedisArray *ra; int *pos, argc, *argc_each; @@ -1188,16 +1186,15 @@ PHP_METHOD(RedisArray, del) /* associate each key to a redis node */ i = 0; ZEND_HASH_FOREACH_VAL(h_keys, data) { - if (Z_TYPE_PP(data) != IS_STRING) { + if (Z_TYPE_P(data) != IS_STRING) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "DEL: all keys must be string."); efree(pos); RETURN_FALSE; } - redis_instances[i] = ra_find_node(ra, Z_STRVAL_PP(data), Z_STRLEN_PP(data), &pos[i] TSRMLS_CC); + redis_instances[i] = ra_find_node(ra, Z_STRVAL_P(data), Z_STRLEN_P(data), &pos[i] TSRMLS_CC); argc_each[pos[i]]++; /* count number of keys per node */ - argv[i] = *data; - i++; + argv[i++] = data; } ZEND_HASH_FOREACH_END(); /* calls */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 9f95abb53b..992ac862cb 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -720,7 +720,7 @@ static long ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, const char *arg TSRMLS_DC) { long count, i; - zval z_fun_smembers, z_ret, *z_arg, **z_data_pp; + zval z_fun_smembers, z_ret, *z_arg, *z_data_p; HashTable *h_keys; char *key; int key_len; @@ -744,9 +744,9 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con *key_lens = emalloc(count * sizeof(int)); i = 0; - ZEND_HASH_FOREACH_VAL(h_keys, z_data_pp) { - key = Z_STRVAL_PP(z_data_pp); - key_len = Z_STRLEN_PP(z_data_pp); + ZEND_HASH_FOREACH_VAL(h_keys, z_data_p) { + key = Z_STRVAL_P(z_data_p); + key_len = Z_STRLEN_P(z_data_p); /* copy key and length */ (*keys)[i] = estrndup(key, key_len); diff --git a/redis_cluster.c b/redis_cluster.c index 799a576ed1..78588f77ba 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1847,7 +1847,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, redisClusterNode *node=NULL; char *lua, *key; int key_free, args_count=0, lua_len, key_len; - zval *z_arr=NULL, **z_ele; + zval *z_arr=NULL, *z_ele; HashTable *ht_arr; long num_keys = 0; short slot; @@ -1874,9 +1874,9 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Iterate over our args if we have any if(args_count > 0) { ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - convert_to_string(*z_ele); - key = Z_STRVAL_PP(z_ele); - key_len = Z_STRLEN_PP(z_ele); + convert_to_string(z_ele); + key = Z_STRVAL_P(z_ele); + key_len = Z_STRLEN_P(z_ele); /* If we're still on a key, prefix it check node */ if(num_keys-- > 0) { diff --git a/redis_commands.c b/redis_commands.c index 35235672c6..12a7b0b213 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -620,7 +620,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *agg_op=NULL; int key_free, key_len; - zval *z_keys, *z_weights=NULL, **z_ele; + zval *z_keys, *z_weights=NULL, *z_ele; HashTable *ht_keys, *ht_weights=NULL; smart_string cmdstr = {0}; int argc = 2, agg_op_len=0, keys_count; @@ -689,12 +689,12 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_free, key_len; zval *z_tmp = NULL; - if(Z_TYPE_PP(z_ele) == IS_STRING) { - key = Z_STRVAL_PP(z_ele); - key_len = Z_STRLEN_PP(z_ele); + if (Z_TYPE_P(z_ele) == IS_STRING) { + key = Z_STRVAL_P(z_ele); + key_len = Z_STRLEN_P(z_ele); } else { MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_ele; + *z_tmp = *z_ele; convert_to_string(z_tmp); key = Z_STRVAL_P(z_tmp); @@ -736,27 +736,27 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Process our weights ZEND_HASH_FOREACH_VAL(ht_weights, z_ele) { // Ignore non numeric args unless they're inf/-inf - if(Z_TYPE_PP(z_ele)!=IS_LONG && Z_TYPE_PP(z_ele)!=IS_DOUBLE && - strncasecmp(Z_STRVAL_PP(z_ele),"inf",sizeof("inf"))!=0 && - strncasecmp(Z_STRVAL_PP(z_ele),"-inf",sizeof("-inf"))!=0 && - strncasecmp(Z_STRVAL_PP(z_ele),"+inf",sizeof("+inf"))!=0) - { + if (Z_TYPE_P(z_ele) != IS_LONG && Z_TYPE_P(z_ele) != IS_DOUBLE && + strncasecmp(Z_STRVAL_P(z_ele), "inf", sizeof("inf")) != 0 && + strncasecmp(Z_STRVAL_P(z_ele), "-inf", sizeof("-inf")) != 0 && + strncasecmp(Z_STRVAL_P(z_ele), "+inf", sizeof("+inf")) != 0 + ) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Weights must be numeric or '-inf','inf','+inf'"); efree(cmdstr.c); return FAILURE; } - switch(Z_TYPE_PP(z_ele)) { + switch (Z_TYPE_P(z_ele)) { case IS_LONG: - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_PP(z_ele)); + redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele)); break; case IS_DOUBLE: - redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_PP(z_ele)); + redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(z_ele)); break; case IS_STRING: - redis_cmd_append_sstr(&cmdstr, Z_STRVAL_PP(z_ele), - Z_STRLEN_PP(z_ele)); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), + Z_STRLEN_P(z_ele)); break; } } ZEND_HASH_FOREACH_END(); @@ -780,7 +780,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_arr, **z_chan; + zval *z_arr, *z_chan; HashTable *ht_chan; smart_string cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); @@ -809,11 +809,11 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate over channels ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { // We want to deal with strings here - convert_to_string(*z_chan); + convert_to_string(z_chan); // Grab channel name, prefix if required - key = Z_STRVAL_PP(z_chan); - key_len = Z_STRLEN_PP(z_chan); + key = Z_STRVAL_P(z_chan); + key_len = Z_STRLEN_P(z_chan); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Add this channel @@ -839,7 +839,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_arr, **z_chan; + zval *z_arr, *z_chan; HashTable *ht_arr; smart_string cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); @@ -860,8 +860,8 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); ZEND_HASH_FOREACH_VAL(ht_arr, z_chan) { - char *key = Z_STRVAL_PP(z_chan); - int key_len = Z_STRLEN_PP(z_chan), key_free; + char *key = Z_STRVAL_P(z_chan); + int key_len = Z_STRLEN_P(z_chan), key_free; key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); @@ -1036,7 +1036,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_arr, **z_val; + zval *z_arr, *z_val; HashTable *ht_arr; smart_string cmdstr = {0}; int key_len, val_len, key_free, val_free, argc = 1; @@ -1062,7 +1062,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Iterate our hash table, serializing and appending values */ ZEND_HASH_FOREACH_VAL(ht_arr, z_val) { - val_free = redis_serialize(redis_sock, *z_val, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); redis_cmd_append_sstr(&cmdstr, val, val_len); if (val_free) STR_FREE(val); } ZEND_HASH_FOREACH_END(); @@ -1497,7 +1497,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - zval *z_arr, **z_mems, **z_mem; + zval *z_arr, **z_mems, *z_mem; int i, count, valid=0, key_len, key_free; HashTable *ht_arr; smart_string cmdstr = {0}; @@ -1526,12 +1526,12 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate over our member array ZEND_HASH_FOREACH_VAL(ht_arr, z_mem) { // We can only handle string or long values here - if ((Z_TYPE_PP(z_mem)==IS_STRING && Z_STRLEN_PP(z_mem)>0) - || Z_TYPE_PP(z_mem)==IS_LONG) - { + if ((Z_TYPE_P(z_mem) == IS_STRING && Z_STRLEN_P(z_mem) > 0) + || Z_TYPE_P(z_mem) == IS_LONG + ) { // Copy into our member array MAKE_STD_ZVAL(z_mems[valid]); - *z_mems[valid] = **z_mem; + *z_mems[valid] = *z_mem; zval_copy_ctor(z_mems[valid]); convert_to_string(z_mems[valid]); @@ -1801,7 +1801,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, int kw_len, int is_keys, char **cmd, int *cmd_len, short *slot) { - zval *z_arr, **z_ele; + zval *z_arr, *z_ele; HashTable *ht_arr; smart_string cmdstr = {0}; char *mem, *key; @@ -1841,14 +1841,14 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix keys, serialize values if(is_keys) { - if(Z_TYPE_PP(z_ele)!=IS_STRING) { + if (Z_TYPE_P(z_ele) != IS_STRING) { MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_ele; + *z_tmp = *z_ele; convert_to_string(z_tmp); - z_ele = &z_tmp; + z_ele = z_tmp; } - mem = Z_STRVAL_PP(z_ele); - mem_len = Z_STRLEN_PP(z_ele); + mem = Z_STRVAL_P(z_ele); + mem_len = Z_STRLEN_P(z_ele); // Key prefix mem_free = redis_key_prefix(redis_sock, &mem, &mem_len); @@ -1865,18 +1865,18 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } } else { - mem_free = redis_serialize(redis_sock, *z_ele, &mem, &mem_len + mem_free = redis_serialize(redis_sock, z_ele, &mem, &mem_len TSRMLS_CC); if(!mem_free) { - if(Z_TYPE_PP(z_ele)!=IS_STRING) { + if (Z_TYPE_P(z_ele) != IS_STRING) { MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_ele; + *z_tmp = *z_ele; convert_to_string(z_tmp); - z_ele = &z_tmp; + z_ele = z_tmp; } - mem = Z_STRVAL_PP(z_ele); - mem_len = Z_STRLEN_PP(z_ele); + mem = Z_STRVAL_P(z_ele); + mem_len = Z_STRLEN_P(z_ele); } } @@ -1928,7 +1928,7 @@ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_keys, **z_key, *z_tmp = NULL; + zval *z_keys, *z_key, *z_tmp = NULL; HashTable *ht_keys; smart_string cmdstr = {0}; int num_keys, key_len, key_free; @@ -1958,17 +1958,17 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Append our key(s) */ ZEND_HASH_FOREACH_VAL(ht_keys, z_key) { /* Turn our value into a string if it isn't one */ - if (Z_TYPE_PP(z_key) != IS_STRING) { + if (Z_TYPE_P(z_key) != IS_STRING) { MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_key; + *z_tmp = *z_key; zval_copy_ctor(z_tmp); convert_to_string(z_tmp); key = Z_STRVAL_P(z_tmp); key_len = Z_STRLEN_P(z_tmp); } else { - key = Z_STRVAL_PP(z_key); - key_len = Z_STRLEN_PP(z_key); + key = Z_STRVAL_P(z_key); + key_len = Z_STRLEN_P(z_key); } /* Append this key to our command */ From 9114530dfcc57b382f2b5fa95d17099d63f9b97f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 16 Sep 2016 09:43:02 +0300 Subject: [PATCH 0581/1986] WIP: php compatibility Redefine zend_get_parameters_array macro for working with `zval *` instead of `zval **` --- common.h | 21 ++++++++++ redis.c | 38 +++++++++--------- redis_array.c | 10 ++--- redis_cluster.c | 42 +++++++++---------- redis_commands.c | 102 +++++++++++++++++++++++------------------------ redis_commands.h | 4 +- 6 files changed, 119 insertions(+), 98 deletions(-) diff --git a/common.h b/common.h index aec483d63e..d8e84f95a9 100644 --- a/common.h +++ b/common.h @@ -69,6 +69,27 @@ zend_hash_get_current_data_ptr(HashTable *ht) return NULL; } +#undef zend_get_parameters_array +#define zend_get_parameters_array(ht, param_count, argument_array) \ + inline_zend_get_parameters_array(ht, param_count, argument_array TSRMLS_CC) + +static zend_always_inline int +inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array TSRMLS_DC) +{ + int i, ret = FAILURE; + zval **zv = ecalloc(param_count, sizeof(zval *)); + + if (_zend_get_parameters_array(ht, param_count, zv TSRMLS_CC) == SUCCESS) { + for (i = 0; i < param_count; i++) { + argument_array[i] = *zv[i]; + zval_copy_ctor(&argument_array[i]); + } + ret = SUCCESS; + } + efree(zv); + return ret; +} + #else #include #endif diff --git a/redis.c b/redis.c index b86684dd3f..c18c759a14 100644 --- a/redis.c +++ b/redis.c @@ -3199,7 +3199,7 @@ PHP_METHOD(Redis, eval) } PHP_REDIS_API int -redis_build_script_exists_cmd(char **ret, zval **argv, int argc) { +redis_build_script_exists_cmd(char **ret, zval *argv, int argc) { /* Our command length and iterator */ int cmd_len = 0, i; @@ -3210,11 +3210,11 @@ redis_build_script_exists_cmd(char **ret, zval **argv, int argc) { /* Iterate our arguments */ for(i=0;i0) convert_to_string(z_args[0]); - if(argc<3 || Z_TYPE_P(z_args[0])!=IS_STRING || (argc-1)%2 != 0) { + if(argc>0) convert_to_string(&z_args[0]); + if(argc<3 || Z_TYPE(z_args[0])!=IS_STRING || (argc-1)%2 != 0) { efree(z_args); return FAILURE; } // Prefix our key - key = Z_STRVAL_P(z_args[0]); - key_len = Z_STRLEN_P(z_args[0]); + key = Z_STRVAL(z_args[0]); + key_len = Z_STRLEN(z_args[0]); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Start command construction @@ -2677,12 +2677,12 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Now the rest of our arguments for(i=1;i Date: Tue, 20 Sep 2016 21:39:11 +0300 Subject: [PATCH 0582/1986] TravisCI: run tests for RedisArray class --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a81420c9a3..36e56e60ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,11 @@ php: - 5.4 - 5.5 - 5.6 -services: redis-server before_install: phpize install: ./configure --prefix=/usr && sudo make install -before_script: echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini -script: php tests/TestRedis.php +before_script: + - for PORT in 6379 6380 6381 6382; do /usr/bin/redis-server --port $PORT --daemonize yes; done + - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini +script: + - php tests/TestRedis.php --class Redis + - php tests/TestRedis.php --class RedisArray From 2c62090a7db367781b4a2001565091725f97867c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 20 Sep 2016 17:13:19 +0300 Subject: [PATCH 0583/1986] WIP: php7 compatibility zend_hash_index_find + zend_hash_index_find_ptr --- cluster_library.c | 97 ++++++++++++++++++++++++++-------------------- common.h | 25 +++++++++++- library.c | 67 +++++++++++++++++++------------- redis.c | 10 ++--- redis_array.c | 21 +++++----- redis_array_impl.c | 24 ++++++------ redis_cluster.c | 22 +++++------ redis_commands.c | 48 +++++++++++------------ 8 files changed, 178 insertions(+), 136 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index a8c5525a4f..6dc7005704 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -177,7 +177,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, ulong slaveidx) { - redisClusterNode **node; + redisClusterNode *node; /* Return the master if we're not looking for a slave */ if (slaveidx == 0) { @@ -185,11 +185,14 @@ static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, } /* Abort if we can't find this slave */ - if (!SLOT_SLAVES(c, slot) || zend_hash_index_find(SLOT_SLAVES(c,slot), - slaveidx, (void**)&node)==FAILURE) return NULL; + if (!SLOT_SLAVES(c, slot) || + (node = zend_hash_index_find_ptr(SLOT_SLAVES(c,slot), slaveidx)) == NULL + ) { + return NULL; + } /* Success, return the slave */ - return (*node)->sock; + return node->sock; } /* Read the response from a cluster */ @@ -416,7 +419,7 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, { int key_free; short slot; - clusterDistList **ppdl, *dl; + clusterDistList *dl; clusterKeyVal *retptr; // Prefix our key and hash it @@ -430,12 +433,10 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, } // Look for this slot - if(zend_hash_index_find(ht, (ulong)slot, (void**)&ppdl)==FAILURE) { + if ((dl = zend_hash_index_find_ptr(ht, (ulong)slot)) == NULL) { dl = cluster_dl_create(); zend_hash_index_update(ht, (ulong)slot, (void**)&dl, sizeof(clusterDistList*), NULL); - } else { - dl = *ppdl; } // Now actually add this key @@ -1622,9 +1623,15 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval *z_tab, **z_tmp, *z_ret, **z_args[4]; + zval *z_tab, *z_tmp, *z_ret; +#if (PHP_MAJOR_VERSION < 7) + zval **z_args[4]; +#else + zval z_args[4]; +#endif int pull=0; + // Consume each MULTI BULK response (one per channel/pattern) while(sctx->argc--) { z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, @@ -1635,9 +1642,9 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * RETURN_FALSE; } - if(zend_hash_index_find(Z_ARRVAL_P(z_tab),0,(void**)&z_tmp)==FAILURE || - strcasecmp(Z_STRVAL_PP(z_tmp), sctx->kw) != 0) - { + if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(z_tab), 0)) == NULL || + strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) != 0 + ) { zval_dtor(z_tab); FREE_ZVAL(z_tab); efree(sctx); @@ -1660,57 +1667,65 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * /* Multibulk response, {[pattern], type, channel, payload} */ while(1) { /* Arguments */ - zval **z_type, **z_chan, **z_pat, **z_data; + zval *z_type, *z_chan, *z_pat, *z_data; int tab_idx=1, is_pmsg; // Get the next subscribe response z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, mbulk_resp_loop); - if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&z_type) - ==FAILURE) - { + if (!z_tab || (z_type = zend_hash_index_find(Z_ARRVAL_P(z_tab), 0)) == NULL) { break; } // Make sure we have a message or pmessage - if(!strncmp(Z_STRVAL_PP(z_type), "message", 7) || - !strncmp(Z_STRVAL_PP(z_type), "pmessage", 8)) - { - is_pmsg = *Z_STRVAL_PP(z_type) == 'p'; + if (!strncmp(Z_STRVAL_P(z_type), "message", 7) || + !strncmp(Z_STRVAL_P(z_type), "pmessage", 8) + ) { + is_pmsg = *Z_STRVAL_P(z_type) == 'p'; } else { zval_dtor(z_tab); efree(z_tab); continue; } - if(is_pmsg && zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, - (void**)&z_pat)==FAILURE) - { + if (is_pmsg && (z_pat = zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++)) == NULL) { break; } // Extract channel and data - if(zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, - (void**)&z_chan)==FAILURE || - zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++, - (void**)&z_data)==FAILURE) - { + if ((z_chan = zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++)) == NULL || + (z_data = zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++)) == NULL + ) { break; } // Always pass our object through +#if (PHP_MAJOR_VERSION < 7) z_args[0] = &getThis(); // Set up calbacks depending on type if(is_pmsg) { - z_args[1] = z_pat; - z_args[2] = z_chan; - z_args[3] = z_data; + z_args[1] = &z_pat; + z_args[2] = &z_chan; + z_args[3] = &z_data; } else { - z_args[1] = z_chan; - z_args[2] = z_data; + z_args[1] = &z_chan; + z_args[2] = &z_data; } +#else + z_args[0] = *getThis(); + + // Set up calbacks depending on type + if(is_pmsg) { + z_args[1] = *z_pat; + z_args[2] = *z_chan; + z_args[3] = *z_data; + } else { + z_args[1] = *z_chan; + z_args[2] = *z_data; + } +#endif // Set arg count sctx->cb.param_count = tab_idx; @@ -1748,7 +1763,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval *z_tab, **z_chan, **z_flag; + zval *z_tab, *z_chan, *z_flag; int pull = 0, argc = sctx->argc; efree(sctx); @@ -1760,9 +1775,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, c, pull, mbulk_resp_loop_raw); // Fail if we didn't get an array or can't find index 1 - if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, - (void**)&z_chan)==FAILURE) - { + if(!z_tab || (z_chan = zend_hash_index_find(Z_ARRVAL_P(z_tab), 1)) == NULL) { if(z_tab) { zval_dtor(z_tab); efree(z_tab); @@ -1772,9 +1785,9 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, } // Find the flag for this channel/pattern - if(zend_hash_index_find(Z_ARRVAL_P(z_tab), 2, (void**)&z_flag) - ==FAILURE || Z_STRLEN_PP(z_flag)!=2) - { + if ((z_flag = zend_hash_index_find(Z_ARRVAL_P(z_tab), 2)) == NULL || + Z_STRLEN_P(z_flag) != 2 + ) { zval_dtor(z_tab); efree(z_tab); zval_dtor(return_value); @@ -1782,10 +1795,10 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, } // Redis will give us either :1 or :0 here - char *flag = Z_STRVAL_PP(z_flag); + char *flag = Z_STRVAL_P(z_flag); // Add result - add_assoc_bool(return_value, Z_STRVAL_PP(z_chan), flag[1]=='1'); + add_assoc_bool(return_value, Z_STRVAL_P(z_chan), flag[1]=='1'); zval_dtor(z_tab); efree(z_tab); diff --git a/common.h b/common.h index d8e84f95a9..941f8c15e0 100644 --- a/common.h +++ b/common.h @@ -69,6 +69,30 @@ zend_hash_get_current_data_ptr(HashTable *ht) return NULL; } +static int (*_zend_hash_index_find)(const HashTable *, ulong, void **) = &zend_hash_index_find; +#define zend_hash_index_find(ht, h) inline_zend_hash_index_find(ht, h) + +static zend_always_inline zval * +inline_zend_hash_index_find(const HashTable *ht, zend_ulong h) +{ + zval **zv; + if (_zend_hash_index_find(ht, h, (void **)&zv) == SUCCESS) { + return *zv; + } + return NULL; +} + +static zend_always_inline void * +zend_hash_index_find_ptr(const HashTable *ht, zend_ulong h) +{ + void **ptr; + + if (_zend_hash_index_find(ht, h, (void **)&ptr) == SUCCESS) { + return *ptr; + } + return NULL; +} + #undef zend_get_parameters_array #define zend_get_parameters_array(ht, param_count, argument_array) \ inline_zend_get_parameters_array(ht, param_count, argument_array TSRMLS_CC) @@ -82,7 +106,6 @@ inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array T if (_zend_get_parameters_array(ht, param_count, zv TSRMLS_CC) == SUCCESS) { for (i = 0; i < param_count; i++) { argument_array[i] = *zv[i]; - zval_copy_ctor(&argument_array[i]); } ret = SUCCESS; } diff --git a/library.c b/library.c index f2c9572ffe..aa7a8c72ba 100644 --- a/library.c +++ b/library.c @@ -259,7 +259,12 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval **z_tmp, *z_ret, **z_args[4]; + zval *z_tmp, *z_ret; +#if (PHP_MAJOR_VERSION < 7) + zval **z_args[4]; +#else + zval z_args[4]; +#endif // Consume response(s) from subscribe, which will vary on argc while(sctx->argc--) { @@ -271,9 +276,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, } // We'll need to find the command response - if(zend_hash_index_find(Z_ARRVAL_P(z_tab), 0, (void**)&z_tmp) - ==FAILURE) - { + if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(z_tab), 0)) == NULL) { zval_dtor(z_tab); efree(z_tab); efree(sctx); @@ -281,7 +284,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, } // Make sure the command response matches the command we called - if(strcasecmp(Z_STRVAL_PP(z_tmp), sctx->kw) !=0) { + if(strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) !=0) { zval_dtor(z_tab); efree(z_tab); efree(sctx); @@ -298,7 +301,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, /* Multibulk response, {[pattern], type, channel, payload } */ while(1) { - zval **z_type, **z_chan, **z_pat, **z_data; + zval *z_type, *z_chan, *z_pat, *z_data; HashTable *ht_tab; int tab_idx=1, is_pmsg; @@ -308,47 +311,57 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, ht_tab = Z_ARRVAL_P(z_tab); - if(zend_hash_index_find(ht_tab, 0, (void**)&z_type)==FAILURE || - Z_TYPE_PP(z_type) != IS_STRING) - { + if ((z_type = zend_hash_index_find(ht_tab, 0)) == NULL || + Z_TYPE_P(z_type) != IS_STRING + ) { break; } // Check for message or pmessage - if(!strncmp(Z_STRVAL_PP(z_type), "message", 7) || - !strncmp(Z_STRVAL_PP(z_type), "pmessage", 8)) + if(!strncmp(Z_STRVAL_P(z_type), "message", 7) || + !strncmp(Z_STRVAL_P(z_type), "pmessage", 8)) { - is_pmsg = *Z_STRVAL_PP(z_type)=='p'; + is_pmsg = *Z_STRVAL_P(z_type)=='p'; } else { break; } // Extract pattern if it's a pmessage if(is_pmsg) { - if(zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_pat) - ==FAILURE) - { + if ((z_pat = zend_hash_index_find(ht_tab, tab_idx++)) == NULL) { break; } } // Extract channel and data - if(zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_chan)==FAILURE || - zend_hash_index_find(ht_tab, tab_idx++, (void**)&z_data)==FAILURE) - { + if ((z_chan = zend_hash_index_find(ht_tab, tab_idx++)) == NULL || + (z_data = zend_hash_index_find(ht_tab, tab_idx++)) == NULL + ) { break; } // Different args for SUBSCRIBE and PSUBSCRIBE +#if (PHP_MAJOR_VERSION < 7) z_args[0] = &getThis(); if(is_pmsg) { - z_args[1] = z_pat; - z_args[2] = z_chan; - z_args[3] = z_data; + z_args[1] = &z_pat; + z_args[2] = &z_chan; + z_args[3] = &z_data; } else { - z_args[1] = z_chan; - z_args[2] = z_data; + z_args[1] = &z_chan; + z_args[2] = &z_data; } +#else + z_args[0] = *getThis(); + if(is_pmsg) { + z_args[1] = *z_pat; + z_args[2] = *z_chan; + z_args[3] = *z_data; + } else { + z_args[1] = *z_chan; + z_args[2] = *z_data; + } +#endif // Set arg count sctx->cb.param_count = tab_idx; @@ -382,7 +395,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval **z_chan, *z_ret; + zval *z_chan, *z_ret; int i=0; MAKE_STD_ZVAL(z_ret); @@ -392,15 +405,13 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, z_tab = redis_sock_read_multibulk_reply_zval( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - if(!z_tab || zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, - (void**)&z_chan)==FAILURE) - { + if (!z_tab || (z_chan = zend_hash_index_find(Z_ARRVAL_P(z_tab), 1)) == NULL) { zval_dtor(z_ret); efree(z_ret); return -1; } - add_assoc_bool(z_ret, Z_STRVAL_PP(z_chan), 1); + add_assoc_bool(z_ret, Z_STRVAL_P(z_chan), 1); zval_dtor(z_tab); efree(z_tab); diff --git a/redis.c b/redis.c index c18c759a14..ba2be5024a 100644 --- a/redis.c +++ b/redis.c @@ -2542,7 +2542,7 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, int cmd_len, array_count; int i; - zval *z_tab, **z_channel; + zval *z_tab, *z_channel; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_ce, &array) == FAILURE) { @@ -2590,12 +2590,10 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); if(Z_TYPE_P(z_tab) == IS_ARRAY) { - if (zend_hash_index_find(Z_ARRVAL_P(z_tab), 1, (void**)&z_channel) - == FAILURE) - { + if ((z_channel = zend_hash_index_find(Z_ARRVAL_P(z_tab), 1)) == NULL) { RETURN_FALSE; } - add_assoc_bool(return_value, Z_STRVAL_PP(z_channel), 1); + add_assoc_bool(return_value, Z_STRVAL_P(z_channel), 1); } else { //error efree(z_tab); @@ -3244,7 +3242,7 @@ PHP_METHOD(Redis, script) { z_args = emalloc(argc * sizeof(zval)); /* Make sure we can grab our arguments, we have a string directive */ - if(zend_get_parameters_array(ht, argc, z_args) == FAILURE || + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING)) { efree(z_args); diff --git a/redis_array.c b/redis_array.c index 3c9b9fd473..fc378f65c0 100644 --- a/redis_array.c +++ b/redis_array.c @@ -836,7 +836,7 @@ PHP_METHOD(RedisArray, select) /* MGET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mget) { - zval *object, *z_keys, z_fun, *z_argarray, *data, *z_ret, **z_cur, *z_tmp_array, *z_tmp; + zval *object, *z_keys, z_fun, *z_argarray, *data, *z_ret, *z_cur, *z_tmp_array, *z_tmp; int i, j, n; RedisArray *ra; int *pos, argc, *argc_each; @@ -951,11 +951,10 @@ PHP_METHOD(RedisArray, mget) if(pos[i] != n) continue; - zend_hash_index_find(Z_ARRVAL_P(z_ret), j, (void**)&z_cur); - j++; + z_cur = zend_hash_index_find(Z_ARRVAL_P(z_ret), j++); MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_cur; + *z_tmp = *z_cur; zval_copy_ctor(z_tmp); INIT_PZVAL(z_tmp); add_index_zval(z_tmp_array, i, z_tmp); @@ -966,10 +965,10 @@ PHP_METHOD(RedisArray, mget) /* copy temp array in the right order to return_value */ for(i = 0; i < argc; ++i) { - zend_hash_index_find(Z_ARRVAL_P(z_tmp_array), i, (void**)&z_cur); + z_cur = zend_hash_index_find(Z_ARRVAL_P(z_tmp_array), i); MAKE_STD_ZVAL(z_tmp); - *z_tmp = **z_cur; + *z_tmp = *z_cur; zval_copy_ctor(z_tmp); INIT_PZVAL(z_tmp); add_next_index_zval(return_value, z_tmp); @@ -1129,7 +1128,7 @@ PHP_METHOD(RedisArray, del) zval *object, *z_keys, z_fun, *z_argarray, *data, *z_ret, *z_tmp, *z_args; int i, n; RedisArray *ra; - int *pos, argc, *argc_each; + int *pos, argc = ZEND_NUM_ARGS(), *argc_each; HashTable *h_keys; zval **redis_instances, *redis_inst, **argv; long total = 0; @@ -1139,21 +1138,21 @@ PHP_METHOD(RedisArray, del) HANDLE_MULTI_EXEC("DEL"); /* get all args in z_args */ - z_args = emalloc(ZEND_NUM_ARGS() * sizeof(zval)); - if(zend_get_parameters_array(ht, ZEND_NUM_ARGS(), z_args) == FAILURE) { + z_args = emalloc(argc * sizeof(zval)); + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); RETURN_FALSE; } /* if single array arg, point z_keys to it. */ - if(ZEND_NUM_ARGS() == 1 && Z_TYPE(z_args[0]) == IS_ARRAY) { + if (argc == 1 && Z_TYPE(z_args[0]) == IS_ARRAY) { z_keys = &z_args[0]; } else { /* copy all elements to z_keys */ MAKE_STD_ZVAL(z_keys); array_init(z_keys); free_zkeys = 1; - for(i = 0; i < ZEND_NUM_ARGS(); ++i) { + for (i = 0; i < argc; ++i) { MAKE_STD_ZVAL(z_tmp); *z_tmp = z_args[i]; zval_copy_ctor(z_tmp); diff --git a/redis_array_impl.c b/redis_array_impl.c index 992ac862cb..0c63262e86 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -515,18 +515,18 @@ ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC) { char * ra_find_key(RedisArray *ra, zval *z_args, const char *cmd, int *key_len) { - zval **zp_tmp; + zval *zp_tmp; int key_pos = 0; /* TODO: change this depending on the command */ - if( zend_hash_num_elements(Z_ARRVAL_P(z_args)) == 0 - || zend_hash_index_find(Z_ARRVAL_P(z_args), key_pos, (void**) &zp_tmp) == FAILURE - || Z_TYPE_PP(zp_tmp) != IS_STRING) { - + if (zend_hash_num_elements(Z_ARRVAL_P(z_args)) == 0 || + (zp_tmp = zend_hash_index_find(Z_ARRVAL_P(z_args), key_pos)) == NULL || + Z_TYPE_P(zp_tmp) != IS_STRING + ) { return NULL; } - *key_len = Z_STRLEN_PP(zp_tmp); - return Z_STRVAL_PP(zp_tmp); + *key_len = Z_STRLEN_P(zp_tmp); + return Z_STRVAL_P(zp_tmp); } void @@ -561,9 +561,7 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { /* prepare keys */ for(i = 0; i < argc - 1; ++i) { - zval **zpp; - zend_hash_index_find(Z_ARRVAL_P(z_keys), i, (void**)&zpp); - z_args[i+1] = *zpp; + z_args[i+1] = zend_hash_index_find(Z_ARRVAL_P(z_keys), i); } /* run cmd */ @@ -649,7 +647,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { void ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { - zval z_fun_exec, z_ret, **zp_tmp; + zval z_fun_exec, z_ret, *zp_tmp; /* run EXEC */ ZVAL_STRING(&z_fun_exec, "EXEC", 0); @@ -662,8 +660,8 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { if(keep_all) { *return_value = z_ret; zval_copy_ctor(return_value); - } else if(zend_hash_index_find(Z_ARRVAL(z_ret), 0, (void**)&zp_tmp) != FAILURE) { - *return_value = **zp_tmp; + } else if ((zp_tmp = zend_hash_index_find(Z_ARRVAL(z_ret), 0)) != NULL) { + *return_value = *zp_tmp; zval_copy_ctor(return_value); } } diff --git a/redis_cluster.c b/redis_cluster.c index e866eef5d0..f86b80348f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2093,8 +2093,8 @@ PHP_METHOD(RedisCluster, watch) { ht_dist = cluster_dist_create(); // Allocate args, and grab them - z_args = emalloc(sizeof(zval)*argc); - if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + z_args = emalloc(sizeof(zval) * argc); + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); cluster_dist_free(ht_dist); RETURN_FALSE; @@ -2247,7 +2247,7 @@ static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { int key_len, key_free; - zval **z_host, **z_port, *z_tmp = NULL; + zval *z_host, *z_port, *z_tmp = NULL; short slot; char *key; @@ -2279,18 +2279,18 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) efree(z_tmp); } } else if (Z_TYPE_P(z_arg) == IS_ARRAY && - zend_hash_index_find(Z_ARRVAL_P(z_arg),0,(void**)&z_host)!=FAILURE && - zend_hash_index_find(Z_ARRVAL_P(z_arg),1,(void**)&z_port)!=FAILURE && - Z_TYPE_PP(z_host)==IS_STRING && Z_TYPE_PP(z_port)==IS_LONG) - { + (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL && + (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL && + Z_TYPE_P(z_host) == IS_STRING && Z_TYPE_P(z_port) == IS_LONG + ) { /* Attempt to find this specific node by host:port */ - slot = cluster_find_slot(c,(const char *)Z_STRVAL_PP(z_host), - (unsigned short)Z_LVAL_PP(z_port)); + slot = cluster_find_slot(c,(const char *)Z_STRVAL_P(z_host), + (unsigned short)Z_LVAL_P(z_port)); /* Inform the caller if they've passed bad data */ if(slot < 0) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", - Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port)); + Z_STRVAL_P(z_host), Z_LVAL_P(z_port)); } } else { php_error_docref(0 TSRMLS_CC, E_WARNING, @@ -2371,7 +2371,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) z_args = emalloc(argc * sizeof(zval)); /* Grab args */ - if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); RETURN_FALSE; } diff --git a/redis_commands.c b/redis_commands.c index ca0b9384f1..9eb17fd5b8 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -564,15 +564,15 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *withscores = (Z_TYPE_P(z_ele) == IS_BOOL && Z_BVAL_P(z_ele) == 1); } else if (IS_LIMIT_ARG(optkey, optlen) && Z_TYPE_P(z_ele) == IS_ARRAY) { HashTable *htlimit = Z_ARRVAL_P(z_ele); - zval **zoff, **zcnt; + zval *zoff, *zcnt; /* We need two arguments (offset and count) */ - if (zend_hash_index_find(htlimit,0,(void**)&zoff)==SUCCESS && - zend_hash_index_find(htlimit,1,(void**)&zcnt)==SUCCESS) - { + if ((zoff = zend_hash_index_find(htlimit, 0)) != NULL && + (zcnt = zend_hash_index_find(htlimit, 1)) != NULL + ) { /* Set our limit if we can get valid longs from both args */ - has_limit = zval_get_long(*zoff, &offset) == SUCCESS && - zval_get_long(*zcnt, &count) == SUCCESS; + has_limit = zval_get_long(zoff, &offset) == SUCCESS && + zval_get_long(zcnt, &count) == SUCCESS; /* Inform the user there is a problem if we don't have a limit */ if (!has_limit) { @@ -992,7 +992,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Make sure we at least have a key, and we can get other args z_args = emalloc(argc * sizeof(zval)); - if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } @@ -1096,7 +1096,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Allocate args z_args = emalloc(argc * sizeof(zval)); - if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } @@ -1714,7 +1714,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Allocate space for args, parse them as an array z_args = emalloc(argc * sizeof(zval)); - if(zend_get_parameters_array(ht, argc, z_args)==FAILURE || + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 3 || Z_TYPE(z_args[0]) != IS_STRING) { efree(z_args); @@ -2513,14 +2513,14 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) && Z_TYPE_P(z_ele) == IS_ARRAY ) { HashTable *ht_off = Z_ARRVAL_P(z_ele); - zval **z_off, **z_cnt; + zval *z_off, *z_cnt; - if(zend_hash_index_find(ht_off, 0, (void**)&z_off)==SUCCESS && - zend_hash_index_find(ht_off, 1, (void**)&z_cnt)==SUCCESS) - { - if((Z_TYPE_PP(z_off)!=IS_STRING && Z_TYPE_PP(z_off)!=IS_LONG) || - (Z_TYPE_PP(z_cnt)!=IS_STRING && Z_TYPE_PP(z_cnt)!=IS_LONG)) - { + if ((z_off = zend_hash_index_find(ht_off, 0)) != NULL && + (z_cnt = zend_hash_index_find(ht_off, 1)) != NULL + ) { + if ((Z_TYPE_P(z_off) != IS_STRING && Z_TYPE_P(z_off) != IS_LONG) || + (Z_TYPE_P(z_cnt) != IS_STRING && Z_TYPE_P(z_cnt) != IS_LONG) + ) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "LIMIT options on SORT command must be longs or strings"); if(key_free) efree(key); @@ -2533,15 +2533,15 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, add_next_index_stringl(z_argv,"LIMIT",sizeof("LIMIT")-1,1); long low, high; - if(Z_TYPE_PP(z_off)==IS_STRING) { - low = atol(Z_STRVAL_PP(z_off)); + if (Z_TYPE_P(z_off) == IS_STRING) { + low = atol(Z_STRVAL_P(z_off)); } else { - low = Z_LVAL_PP(z_off); + low = Z_LVAL_P(z_off); } - if(Z_TYPE_PP(z_cnt)==IS_STRING) { - high = atol(Z_STRVAL_PP(z_cnt)); + if (Z_TYPE_P(z_cnt) == IS_STRING) { + high = atol(Z_STRVAL_P(z_cnt)); } else { - high = Z_LVAL_PP(z_cnt); + high = Z_LVAL_P(z_cnt); } // Add our two LIMIT arguments @@ -2599,7 +2599,7 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Grab arguments as an array z_args = emalloc(argc * sizeof(zval)); - if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } @@ -2649,7 +2649,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; z_args = emalloc(argc * sizeof(zval)); - if(zend_get_parameters_array(ht, argc, z_args)==FAILURE) { + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } From fab2ce43d71bee6310b96a82a69b769199fcfcf4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 22 Sep 2016 21:10:50 +0300 Subject: [PATCH 0584/1986] TravisCI: run tests for RedisCluster class --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 36e56e60ef..59434a961e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,14 @@ php: before_install: phpize install: ./configure --prefix=/usr && sudo make install before_script: - - for PORT in 6379 6380 6381 6382; do /usr/bin/redis-server --port $PORT --daemonize yes; done + - gem install redis + - mkdir -p tests/nodes/ && echo >> tests/nodes/nodemap + - for PORT in $(seq 6379 6382); do redis-server --port $PORT --daemonize yes; done + - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done + - wget https://raw.githubusercontent.com/antirez/redis/unstable/src/redis-trib.rb + - echo yes | ruby redis-trib.rb create --replicas 3 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 127.0.0.1:7007 127.0.0.1:7008 127.0.0.1:7009 127.0.0.1:7010 127.0.0.1:7011 - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini script: - php tests/TestRedis.php --class Redis - php tests/TestRedis.php --class RedisArray + - php tests/TestRedis.php --class RedisCluster From 377dc5019c97050c90322d42d1215c8d6e7491eb Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 23 Sep 2016 08:51:28 +0300 Subject: [PATCH 0585/1986] WIP: php7 compatibility Redefine zend_hash_next_index_insert and zend_hash_get_current_data_ex macroses. Fixed redis cluster crush (#954) --- common.h | 35 +++++++++++++++++++++++++++++------ redis_array_impl.c | 8 ++++---- redis_cluster.c | 29 +++++++++++++++-------------- redis_commands.c | 6 +++--- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/common.h b/common.h index 941f8c15e0..1e0e8894d4 100644 --- a/common.h +++ b/common.h @@ -13,16 +13,13 @@ typedef smart_str smart_string; #define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) #define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ - zval **_pData; \ HashPosition _hpos; \ for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - zend_hash_get_current_data_ex(ht, (void **) &_pData, &_hpos) == SUCCESS; \ + (_val = zend_hash_get_current_data_ex(ht, &_hpos)) != NULL; \ zend_hash_move_forward_ex(ht, &_hpos) \ - ) { _val = *_pData; + ) -#define ZEND_HASH_FOREACH_END() \ - } \ - } while(0) +#define ZEND_HASH_FOREACH_END() } while(0) static zend_always_inline zval * zend_hash_str_find(const HashTable *ht, const char *key, size_t len) @@ -93,6 +90,32 @@ zend_hash_index_find_ptr(const HashTable *ht, zend_ulong h) return NULL; } +static int (*_zend_hash_get_current_data_ex)(HashTable *, void **, HashPosition *) = &zend_hash_get_current_data_ex; +#define zend_hash_get_current_data_ex(ht, pos) inline_zend_hash_get_current_data_ex(ht, pos) +static zend_always_inline zval * +inline_zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos) +{ + zval **zv; + if (_zend_hash_get_current_data_ex(ht, (void **)&zv, pos) == SUCCESS) { + return *zv; + } + return NULL; +} + +#undef zend_hash_next_index_insert +#define zend_hash_next_index_insert(ht, pData) \ + _zend_hash_next_index_insert(ht, pData ZEND_FILE_LINE_CC) +static zend_always_inline zval * +_zend_hash_next_index_insert(HashTable *ht, zval *pData ZEND_FILE_LINE_DC) +{ + if (_zend_hash_index_update_or_next_insert(ht, 0, &pData, sizeof(pData), + NULL, HASH_NEXT_INSERT ZEND_FILE_LINE_CC) == SUCCESS + ) { + return pData; + } + return NULL; +} + #undef zend_get_parameters_array #define zend_get_parameters_array(ht, param_count, argument_array) \ inline_zend_get_parameters_array(ht, param_count, argument_array TSRMLS_CC) diff --git a/redis_array_impl.c b/redis_array_impl.c index 0c63262e86..3f2d36d575 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -581,7 +581,7 @@ void ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { /* Initialize key array */ - zval *z_keys, **z_entry_pp; + zval *z_keys; MAKE_STD_ZVAL(z_keys); HashPosition pos; #if PHP_VERSION_ID > 50300 @@ -592,7 +592,7 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { /* Go through input array and add values to the key array */ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z_pairs), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(z_pairs), (void **)&z_entry_pp, &pos) == SUCCESS) { + while (zend_hash_get_current_data_ex(Z_ARRVAL_P(z_pairs), &pos) != NULL) { char *key; unsigned int key_len; unsigned long num_key; @@ -602,13 +602,13 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(z_pairs), &key, &key_len, &num_key, 1, &pos)) { case HASH_KEY_IS_STRING: ZVAL_STRINGL(z_new, key, (int)key_len - 1, 0); - zend_hash_next_index_insert(Z_ARRVAL_P(z_keys), &z_new, sizeof(zval *), NULL); + zend_hash_next_index_insert(Z_ARRVAL_P(z_keys), z_new); break; case HASH_KEY_IS_LONG: Z_TYPE_P(z_new) = IS_LONG; Z_LVAL_P(z_new) = (long)num_key; - zend_hash_next_index_insert(Z_ARRVAL_P(z_keys), &z_new, sizeof(zval *), NULL); + zend_hash_next_index_insert(Z_ARRVAL_P(z_keys), z_new); break; } zend_hash_move_forward_ex(Z_ARRVAL_P(z_pairs), &pos); diff --git a/redis_cluster.c b/redis_cluster.c index f86b80348f..691e459a0d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -567,7 +567,7 @@ typedef struct clusterKeyValHT { static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, clusterKeyValHT *kv TSRMLS_DC) { - zval **z_val; + zval *z_val; unsigned int key_len; ulong idx; @@ -593,14 +593,14 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, kv->slot = cluster_hash_key(kv->key, kv->key_len); // Now grab our value - if(zend_hash_get_current_data_ex(ht, (void**)&z_val, ptr)==FAILURE) { + if ((z_val = zend_hash_get_current_data_ex(ht, ptr)) == NULL) { zend_throw_exception(redis_cluster_exception_ce, "Internal Zend HashTable error", 0 TSRMLS_CC); return -1; } // Serialize our value if required - kv->val_free = redis_serialize(c->flags,*z_val,&(kv->val),&(kv->val_len) + kv->val_free = redis_serialize(c->flags,z_val,&(kv->val),&(kv->val_len) TSRMLS_CC); // Success @@ -611,9 +611,9 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, clusterKeyValHT *kv TSRMLS_DC) { - zval **z_key; + zval *z_key; - if(zend_hash_get_current_data_ex(ht, (void**)&z_key, ptr)==FAILURE) { + if ((z_key = zend_hash_get_current_data_ex(ht, ptr)) == NULL) { // Shouldn't happen, but check anyway zend_throw_exception(redis_cluster_exception_ce, "Internal Zend HashTable error", 0 TSRMLS_CC); @@ -621,10 +621,10 @@ static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, } // Always want to work with strings - convert_to_string(*z_key); + convert_to_string(z_key); - kv->key = Z_STRVAL_PP(z_key); - kv->key_len = Z_STRLEN_PP(z_key); + kv->key = Z_STRVAL_P(z_key); + kv->key_len = Z_STRLEN_P(z_key); kv->key_free = redis_key_prefix(c->flags, &(kv->key), &(kv->key_len)); // Hash our key @@ -645,7 +645,7 @@ static HashTable *method_args_to_ht(zval *z_args, int argc) { /* Populate our return hash table with our arguments */ for (i = 0; i < argc; i++) { - zend_hash_next_index_insert(ht_ret, &z_args[i], sizeof(zval), NULL); + zend_hash_next_index_insert(ht_ret, &z_args[i]); } /* Return our hash table */ @@ -669,8 +669,8 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, if (!argc) return -1; /* Extract our arguments into an array */ - z_args = emalloc(sizeof(zval)*argc); - if (zend_get_parameters_array(ht, ZEND_NUM_ARGS(), z_args) == FAILURE) { + z_args = ecalloc(argc, sizeof(zval)); + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return -1; } @@ -688,9 +688,6 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, ht_free = 1; } - /* We no longer need our array args */ - efree(z_args); - /* MGET is readonly, DEL is not */ c->readonly = kw_len == 4 && CLUSTER_IS_ATOMIC(c); @@ -701,6 +698,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // it's the first iteration every time, needlessly zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { + efree(z_args); return -1; } @@ -722,6 +720,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zend_hash_destroy(ht_arr); efree(ht_arr); } + efree(z_args); return -1; } @@ -736,6 +735,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zend_hash_destroy(ht_arr); efree(ht_arr); } + efree(z_args); return -1; } } @@ -752,6 +752,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zend_hash_move_forward_ex(ht_arr, &ptr); } + efree(z_args); // If we've got straggler(s) process them if(mc.argc > 0) { diff --git a/redis_commands.c b/redis_commands.c index 9eb17fd5b8..917d149503 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1618,12 +1618,12 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *mem, *val, kbuf[40]; int val_len, val_free; unsigned int mem_len; - zval **z_val; + zval *z_val; // Grab our key, and value for this element in our input ktype = zend_hash_get_current_key_ex(ht_vals, &mem, &mem_len, &idx, 0, &pos); - zend_hash_get_current_data_ex(ht_vals, (void**)&z_val, &pos); + z_val = zend_hash_get_current_data_ex(ht_vals, &pos); // If the hash key is an integer, convert it to a string if(ktype != HASH_KEY_IS_STRING) { @@ -1635,7 +1635,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Serialize value (if directed) - val_free = redis_serialize(redis_sock, *z_val, &val, &val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); // Append the key and value to our command redis_cmd_append_sstr(&cmdstr, mem, mem_len); From 910dd952bfebb872651f0c3b3aba029ecc297c72 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 28 Sep 2016 14:33:18 +0300 Subject: [PATCH 0586/1986] WIP: php7 compatibility Rename zend_rsrc_list_entry to zend_resource --- cluster_library.h | 2 +- common.h | 2 ++ redis.c | 2 +- redis_array.c | 2 +- redis_array.h | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index 4249a7a628..32d248d666 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -151,7 +151,7 @@ typedef enum CLUSTER_REDIR_TYPE { typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void* TSRMLS_DC); /* Specific destructor to free a cluster object */ -// void redis_destructor_redis_cluster(zend_rsrc_list_entry *rsrc TSRMLS_DC); +// void redis_destructor_redis_cluster(zend_resource *rsrc TSRMLS_DC); /* A Redis Cluster master node */ typedef struct redisClusterNode { diff --git a/common.h b/common.h index 1e0e8894d4..fd97c8449f 100644 --- a/common.h +++ b/common.h @@ -136,6 +136,8 @@ inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array T return ret; } +typedef zend_rsrc_list_entry zend_resource; + #else #include #endif diff --git a/redis.c b/redis.c index ba2be5024a..89667edac6 100644 --- a/redis.c +++ b/redis.c @@ -425,7 +425,7 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { /** * redis_destructor_redis_sock */ -static void redis_destructor_redis_sock(zend_rsrc_list_entry * rsrc TSRMLS_DC) +static void redis_destructor_redis_sock(zend_resource * rsrc TSRMLS_DC) { RedisSock *redis_sock = (RedisSock *) rsrc->ptr; redis_sock_disconnect(redis_sock TSRMLS_CC); diff --git a/redis_array.c b/redis_array.c index fc378f65c0..863f562a4e 100644 --- a/redis_array.c +++ b/redis_array.c @@ -117,7 +117,7 @@ static void redis_array_free(RedisArray *ra) { } int le_redis_array; -void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC) +void redis_destructor_redis_array(zend_resource * rsrc TSRMLS_DC) { RedisArray *ra = (RedisArray*)rsrc->ptr; diff --git a/redis_array.h b/redis_array.h index 2455a375bf..e2cce58a9c 100644 --- a/redis_array.h +++ b/redis_array.h @@ -8,7 +8,7 @@ #endif #include "common.h" -void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC); +void redis_destructor_redis_array(zend_resource * rsrc TSRMLS_DC); PHP_METHOD(RedisArray, __construct); PHP_METHOD(RedisArray, __call); From 55540d8808e3db1a95f0ada2f012b2535894c80d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 28 Sep 2016 23:38:31 +0300 Subject: [PATCH 0587/1986] WIP: php7 compatibility redis_sock_get + redis_array_get --- redis.c | 16 ++++++++++------ redis_array.c | 16 +++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/redis.c b/redis.c index 89667edac6..2016f26bb5 100644 --- a/redis.c +++ b/redis.c @@ -438,9 +438,7 @@ static void redis_destructor_redis_sock(zend_resource * rsrc TSRMLS_DC) PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) { - zval *socket; - int resource_type; if (Z_TYPE_P(id) != IS_OBJECT || (socket = zend_hash_str_find(Z_OBJPROP_P(id), "socket", sizeof("socket") - 1)) == NULL) { @@ -452,10 +450,16 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, return -1; } - *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_P(socket), - &resource_type); - +#if (PHP_MAJOR_VERSION < 7) + int resource_type; + *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_P(socket), &resource_type); if (!*redis_sock || resource_type != le_redis_sock) { +#else + if (Z_RES_P(socket) == NULL || + !(*redis_sock = (RedisSock *)Z_RES_P(socket)->ptr) || + Z_RES_P(socket)->type != le_redis_sock + ) { +#endif // Throw an exception unless we've been requested not to if(!no_throw) { zend_throw_exception(redis_exception_ce, "Redis server went away", @@ -471,7 +475,7 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, } } - return Z_LVAL_P(socket); + return 0; } /** diff --git a/redis_array.c b/redis_array.c index 863f562a4e..582984e1fa 100644 --- a/redis_array.c +++ b/redis_array.c @@ -136,20 +136,26 @@ PHP_REDIS_API int redis_array_get(zval *id, RedisArray **ra TSRMLS_DC) { zval *socket; - int resource_type; if (Z_TYPE_P(id) != IS_OBJECT || (socket = zend_hash_str_find(Z_OBJPROP_P(id), "socket", sizeof("socket") - 1)) == NULL) { return -1; } - *ra = (RedisArray *) zend_list_find(Z_LVAL_P(socket), &resource_type); - +#if (PHP_MAJOR_VERSION < 7) + int resource_type; + *ra = (RedisArray *)zend_list_find(Z_LVAL_P(socket), &resource_type); if (!*ra || resource_type != le_redis_array) { - return -1; +#else + if (Z_RES_P(socket) == NULL || + !(*ra = (RedisArray *)Z_RES_P(socket)->ptr) || + Z_RES_P(socket)->type != le_redis_array + ) { +#endif + return -1; } - return Z_LVAL_P(socket); + return 0; } uint32_t rcrc32(const char *s, size_t sz) { From fa8f02946b63d8cb48980529aff34bccceb33c1e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 29 Sep 2016 17:18:05 +0300 Subject: [PATCH 0588/1986] WIP: php7 compatibility Wrap zend_list_delete and add_property_resource. Remove unnecessary zend_hash_get_current_key_ex calls and memmory allocation. --- library.c | 6 +----- redis.c | 13 +++++++++++-- redis_array.c | 36 ++++++++++++++++++------------------ redis_array_impl.c | 4 ++++ 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/library.c b/library.c index aa7a8c72ba..514b06b5f4 100644 --- a/library.c +++ b/library.c @@ -1240,13 +1240,10 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, zend_hash_has_more_elements(keytable) == SUCCESS; zend_hash_move_forward(keytable)) { - char *tablekey, *hkey, *hval; - unsigned int tablekey_len; + char *hkey, *hval; int hkey_len; - unsigned long idx; zval *z_key_p, *z_value_p; - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); if ((z_key_p = zend_hash_get_current_data(keytable)) == NULL) { continue; /* this should never happen, according to the PHP people. */ } @@ -1260,7 +1257,6 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, zend_hash_move_forward(keytable); /* fetch again */ - zend_hash_get_current_key_ex(keytable, &tablekey, &tablekey_len, &idx, 0, NULL); if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) { continue; /* this should never happen, according to the PHP people. */ } diff --git a/redis.c b/redis.c index 2016f26bb5..b90781eab0 100644 --- a/redis.c +++ b/redis.c @@ -726,7 +726,7 @@ PHP_METHOD(Redis, pconnect) PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zval *object, *socket; - int host_len, id; + int host_len; char *host = NULL; long port = -1; long retry_interval = 0; @@ -779,7 +779,11 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { /* maybe there is a socket but the id isn't known.. what to do? */ } else { /* The refcount should be decreased and destructor invoked */ +#if (PHP_MAJOR_VERSION < 7) zend_list_delete(Z_LVAL_P(socket)); +#else + zend_list_delete(Z_RES_P(socket)); +#endif } } @@ -790,13 +794,18 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { redis_free_socket(redis_sock); return FAILURE; } - +#if (PHP_MAJOR_VERSION < 7) + int id; #if PHP_VERSION_ID >= 50400 id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); #else id = zend_list_insert(redis_sock, le_redis_sock); #endif add_property_resource(object, "socket", id); +#else + zval *id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); + add_property_resource(object, "socket", Z_RES_P(id)); +#endif return SUCCESS; } diff --git a/redis_array.c b/redis_array.c index 582984e1fa..f5a1502160 100644 --- a/redis_array.c +++ b/redis_array.c @@ -214,7 +214,6 @@ uint32_t rcrc32(const char *s, size_t sz) { PHP_METHOD(RedisArray, __construct) { zval *z0, *z_fun = NULL, *z_dist = NULL, *zpData, *z_opts = NULL; - int id; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; HashTable *hPrev = NULL, *hOpts = NULL; @@ -322,12 +321,18 @@ PHP_METHOD(RedisArray, __construct) ra->auto_rehash = b_autorehash; ra->connect_timeout = d_connect_timeout; if(ra->prev) ra->prev->auto_rehash = b_autorehash; +#if (PHP_MAJOR_VERSION < 7) + int id; #if PHP_VERSION_ID >= 50400 id = zend_list_insert(ra, le_redis_array TSRMLS_CC); #else id = zend_list_insert(ra, le_redis_array); #endif add_property_resource(getThis(), "socket", id); +#else + zval *id = zend_list_insert(ra, le_redis_array TSRMLS_CC); + add_property_resource(getThis(), "socket", Z_RES_P(id)); +#endif } } @@ -847,7 +852,7 @@ PHP_METHOD(RedisArray, mget) RedisArray *ra; int *pos, argc, *argc_each; HashTable *h_keys; - zval **redis_instances, **argv; + zval **argv; /* Multi/exec support */ HANDLE_MULTI_EXEC("MGET"); @@ -869,8 +874,6 @@ PHP_METHOD(RedisArray, mget) argc = zend_hash_num_elements(h_keys); argv = emalloc(argc * sizeof(zval*)); pos = emalloc(argc * sizeof(int)); - redis_instances = emalloc(argc * sizeof(zval*)); - memset(redis_instances, 0, argc * sizeof(zval*)); argc_each = emalloc(ra->count * sizeof(int)); memset(argc_each, 0, ra->count * sizeof(int)); @@ -887,7 +890,6 @@ PHP_METHOD(RedisArray, mget) php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs"); efree(argv); efree(pos); - efree(redis_instances); efree(argc_each); RETURN_FALSE; } @@ -902,7 +904,9 @@ PHP_METHOD(RedisArray, mget) } /* Find our node */ - redis_instances[i] = ra_find_node(ra, key_lookup, key_len, &pos[i] TSRMLS_CC); + if (ra_find_node(ra, key_lookup, key_len, &pos[i] TSRMLS_CC) == NULL) { + /* TODO: handle */ + } argc_each[pos[i]]++; /* count number of keys per node */ argv[i++] = data; @@ -948,7 +952,6 @@ PHP_METHOD(RedisArray, mget) efree(z_ret); zval_ptr_dtor(&z_tmp_array); efree(pos); - efree(redis_instances); efree(argc_each); /* failure */ @@ -984,7 +987,6 @@ PHP_METHOD(RedisArray, mget) zval_ptr_dtor(&z_tmp_array); efree(argv); efree(pos); - efree(redis_instances); efree(argc_each); } @@ -997,7 +999,7 @@ PHP_METHOD(RedisArray, mset) RedisArray *ra; int *pos, argc, *argc_each; HashTable *h_keys; - zval **redis_instances, *redis_inst, **argv; + zval *redis_inst, **argv; char *key, **keys, **key_free, kbuf[40]; unsigned int key_len, free_idx = 0; int type, *key_lens; @@ -1022,8 +1024,6 @@ PHP_METHOD(RedisArray, mset) pos = emalloc(argc * sizeof(int)); keys = emalloc(argc * sizeof(char*)); key_lens = emalloc(argc * sizeof(int)); - redis_instances = emalloc(argc * sizeof(zval*)); - memset(redis_instances, 0, argc * sizeof(zval*)); /* Allocate an array holding the indexes of any keys that need freeing */ key_free = emalloc(argc * sizeof(char*)); @@ -1053,7 +1053,9 @@ PHP_METHOD(RedisArray, mset) key_len--; /* We don't want the null terminator */ } - redis_instances[i] = ra_find_node(ra, key, (int)key_len, &pos[i] TSRMLS_CC); + if (ra_find_node(ra, key, (int)key_len, &pos[i] TSRMLS_CC) == NULL) { + // TODO: handle + } argc_each[pos[i]]++; /* count number of keys per node */ argv[i] = data; keys[i] = key; @@ -1122,7 +1124,6 @@ PHP_METHOD(RedisArray, mset) efree(key_lens); efree(argv); efree(pos); - efree(redis_instances); efree(argc_each); RETURN_TRUE; @@ -1136,7 +1137,7 @@ PHP_METHOD(RedisArray, del) RedisArray *ra; int *pos, argc = ZEND_NUM_ARGS(), *argc_each; HashTable *h_keys; - zval **redis_instances, *redis_inst, **argv; + zval *redis_inst, **argv; long total = 0; int free_zkeys = 0; @@ -1182,8 +1183,6 @@ PHP_METHOD(RedisArray, del) argc = zend_hash_num_elements(h_keys); argv = emalloc(argc * sizeof(zval*)); pos = emalloc(argc * sizeof(int)); - redis_instances = emalloc(argc * sizeof(zval*)); - memset(redis_instances, 0, argc * sizeof(zval*)); argc_each = emalloc(ra->count * sizeof(int)); memset(argc_each, 0, ra->count * sizeof(int)); @@ -1197,7 +1196,9 @@ PHP_METHOD(RedisArray, del) RETURN_FALSE; } - redis_instances[i] = ra_find_node(ra, Z_STRVAL_P(data), Z_STRLEN_P(data), &pos[i] TSRMLS_CC); + if (ra_find_node(ra, Z_STRVAL_P(data), Z_STRLEN_P(data), &pos[i] TSRMLS_CC) == NULL) { + // TODO: handle + } argc_each[pos[i]]++; /* count number of keys per node */ argv[i++] = data; } ZEND_HASH_FOREACH_END(); @@ -1256,7 +1257,6 @@ PHP_METHOD(RedisArray, del) /* cleanup */ efree(argv); efree(pos); - efree(redis_instances); efree(argc_each); if(free_zkeys) { diff --git a/redis_array_impl.c b/redis_array_impl.c index 3f2d36d575..aca4280db9 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -94,7 +94,11 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b #else id = zend_list_insert(redis_sock, le_redis_sock); #endif +#if (PHP_MAJOR_VERSION < 7) add_property_resource(ra->redis[i], "socket", id); +#else + add_property_resource(ra->redis[i], "socket", Z_RES_P(id)); +#endif ra->count = ++i; } From febf8bd89bf912d47f8a00d65360f92fb5e65669 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 3 Oct 2016 18:00:06 +0300 Subject: [PATCH 0589/1986] WIP: php7 compatibility Partially avoid using MAKE_STD_ZVAL --- library.c | 31 +++++++++------------- redis.c | 79 ++++++++++++++++--------------------------------------- 2 files changed, 35 insertions(+), 75 deletions(-) diff --git a/library.c b/library.c index 514b06b5f4..3a2f4dd662 100644 --- a/library.c +++ b/library.c @@ -2011,7 +2011,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len HashTable ht; #endif smart_string sstr = {0}; - zval *z_copy; + zval z_copy; #ifdef HAVE_REDIS_IGBINARY size_t sz; uint8_t *val8; @@ -2027,27 +2027,22 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len return 0; case IS_OBJECT: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Object", 6, 1); - break; + *val = "Object"; + *val_len = 6; + return 0; case IS_ARRAY: - MAKE_STD_ZVAL(z_copy); - ZVAL_STRINGL(z_copy, "Array", 5, 1); - break; + *val = "Array"; + *val_len = 5; + return 0; default: /* copy */ - MAKE_STD_ZVAL(z_copy); - *z_copy = *z; - zval_copy_ctor(z_copy); - break; + z_copy = *z; + zval_copy_ctor(&z_copy); + convert_to_string(&z_copy); + *val = Z_STRVAL(z_copy); + *val_len = Z_STRLEN(z_copy); } - - /* return string */ - convert_to_string(z_copy); - *val = Z_STRVAL_P(z_copy); - *val_len = Z_STRLEN_P(z_copy); - efree(z_copy); return 1; case REDIS_SERIALIZER_PHP: @@ -2076,7 +2071,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len return 1; } #endif - return 0; + break; } return 0; } diff --git a/redis.c b/redis.c index b90781eab0..e655ff1ab2 100644 --- a/redis.c +++ b/redis.c @@ -1005,20 +1005,19 @@ PHP_METHOD(Redis, getMultiple) ZEND_HASH_FOREACH_VAL(hash, z_ele) { char *key; int key_len, key_free; - zval *z_tmp = NULL; + zval z_tmp; /* If the key isn't a string, turn it into one */ if (Z_TYPE_P(z_ele) == IS_STRING) { key = Z_STRVAL_P(z_ele); key_len = Z_STRLEN_P(z_ele); } else { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_ele; - zval_copy_ctor(z_tmp); - convert_to_string(z_tmp); + z_tmp = *z_ele; + zval_copy_ctor(&z_tmp); + convert_to_string(&z_tmp); - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); + key = Z_STRVAL(z_tmp); + key_len = Z_STRLEN(z_tmp); } /* Apply key prefix if necissary */ @@ -1029,13 +1028,6 @@ PHP_METHOD(Redis, getMultiple) /* Free our key if it was prefixed */ if(key_free) efree(key); - - /* Free oour temporary ZVAL if we converted from a non-string */ - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } } ZEND_HASH_FOREACH_END(); /* Kick off our command */ @@ -2302,15 +2294,10 @@ PHP_REDIS_API int redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - zval *z_tab; - MAKE_STD_ZVAL(z_tab); - array_init(z_tab); + array_init(return_value); redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, z_tab, 0); - - *return_value = *z_tab; - efree(z_tab); + redis_sock, return_value, 0); /* free allocated function/request memory */ free_reply_callbacks(getThis(), redis_sock); @@ -2326,7 +2313,6 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME char inbuf[1024]; int numElems; - zval *z_tab; redis_check_eof(redis_sock, 0 TSRMLS_CC); @@ -2342,16 +2328,10 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME return -1; } - zval_dtor(return_value); - - MAKE_STD_ZVAL(z_tab); - array_init(z_tab); + array_init(return_value); redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, z_tab, numElems); - - *return_value = *z_tab; - efree(z_tab); + redis_sock, return_value, numElems); return 0; } @@ -2931,19 +2911,18 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) { char *key; int key_len, key_free; - zval *z_tmp = NULL; + zval z_tmp; if (Z_TYPE_P(z_ele) == IS_STRING) { key = Z_STRVAL_P(z_ele); key_len = Z_STRLEN_P(z_ele); } else { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_ele; - zval_copy_ctor(z_tmp); - convert_to_string(z_tmp); + z_tmp = *z_ele; + zval_copy_ctor(&z_tmp); + convert_to_string(&z_tmp); - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); + key = Z_STRVAL(z_tmp); + key_len = Z_STRLEN(z_tmp); } /* Apply prefix if required */ @@ -2954,13 +2933,6 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Free key if prefixed */ if(key_free) efree(key); - - // Free our temp var if we used it - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } } ZEND_HASH_FOREACH_END(); /* Set return */ @@ -3081,7 +3053,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, // Iterate the values in our "keys" array ZEND_HASH_FOREACH_VAL(args_hash, elem) { - zval *z_tmp = NULL; + zval z_tmp; char *key, *old_cmd; int key_len, key_free; @@ -3090,13 +3062,12 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, key_len = Z_STRLEN_P(elem); } else { /* Convert it to a string */ - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *elem; - zval_copy_ctor(z_tmp); - convert_to_string(z_tmp); + z_tmp = *elem; + zval_copy_ctor(&z_tmp); + convert_to_string(&z_tmp); - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); + key = Z_STRVAL(z_tmp); + key_len = Z_STRLEN(z_tmp); } /* Keep track of the old command pointer */ @@ -3114,12 +3085,6 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, /* Free our key, old command if we need to */ if(key_free) efree(key); - - // Free our temporary arg if we created one - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - } } ZEND_HASH_FOREACH_END(); } } From 84d9c27813a57119b95a52b121946e1194b6a1b0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 6 Oct 2016 17:13:50 +0300 Subject: [PATCH 0590/1986] WIP: php7 compatibility Define zend_string type + ZEND_HASH_FOREACH_KEY_VAL macro --- common.h | 33 +++++++++++++- redis.c | 31 ++++--------- redis_array.c | 49 ++++++++------------- redis_array_impl.c | 36 +++++---------- redis_commands.c | 106 +++++++++++++++------------------------------ 5 files changed, 104 insertions(+), 151 deletions(-) diff --git a/common.h b/common.h index fd97c8449f..9a01608067 100644 --- a/common.h +++ b/common.h @@ -12,14 +12,43 @@ typedef smart_str smart_string; #define smart_string_append_long(dest, val) smart_str_append_long(dest, val) #define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) +typedef struct { + size_t len; + char *val; +} zend_string; + +#define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) do { \ + HashPosition _hpos; \ + for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ + (_val = zend_hash_get_current_data_ex(ht, &_hpos)) != NULL; \ + zend_hash_move_forward_ex(ht, &_hpos) \ + ) { \ + zend_string _zstr = {0}; \ + char *_str_index; uint _str_length; ulong _num_index; \ + switch (zend_hash_get_current_key_ex(ht, &_str_index, &_str_length, &_num_index, 0, &_hpos)) { \ + case HASH_KEY_IS_STRING: \ + _zstr.len = _str_length; \ + _zstr.val = _str_index; \ + _key = &_zstr; \ + break; \ + case HASH_KEY_IS_LONG: \ + _key = NULL; \ + _h = _num_index; \ + break; \ + default: \ + continue; \ + } + #define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ HashPosition _hpos; \ for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ (_val = zend_hash_get_current_data_ex(ht, &_hpos)) != NULL; \ zend_hash_move_forward_ex(ht, &_hpos) \ - ) + ) { -#define ZEND_HASH_FOREACH_END() } while(0) +#define ZEND_HASH_FOREACH_END() \ + } \ +} while(0) static zend_always_inline zval * zend_hash_str_find(const HashTable *ht, const char *key, size_t len) diff --git a/redis.c b/redis.c index e655ff1ab2..cba470cc18 100644 --- a/redis.c +++ b/redis.c @@ -1851,37 +1851,24 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { kw_len, kw); } + ulong idx; + zend_string *zkey; + zval *z_value_p; keytable = Z_ARRVAL_P(z_array); - for(zend_hash_internal_pointer_reset(keytable); - zend_hash_has_more_elements(keytable) == SUCCESS; - zend_hash_move_forward(keytable)) { - + ZEND_HASH_FOREACH_KEY_VAL(keytable, idx, zkey, z_value_p) { char *key, *val; unsigned int key_len; int val_len; - unsigned long idx; - int type; - zval *z_value_p; int val_free, key_free; char buf[32]; - type = zend_hash_get_current_key_ex(keytable, &key, &key_len, &idx, - 0, NULL); - if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) { - /* Should never happen, according to the PHP people */ - continue; - } - - // If the key isn't a string, use the index value returned when - // grabbing it. We typecast to long, because they could actually - // be negative. - if(type != HASH_KEY_IS_STRING) { + if (zkey) { + key = zkey->val; + key_len = zkey->len - 1; + } else { // Create string representation of our index key_len = snprintf(buf, sizeof(buf), "%ld", (long)idx); key = (char*)buf; - } else if(key_len > 0) { - // When not an integer key, the length will include the \0 - key_len--; } if(step == 0) @@ -1908,7 +1895,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { if(val_free) STR_FREE(val); if(key_free) efree(key); - } + } ZEND_HASH_FOREACH_END(); } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); diff --git a/redis_array.c b/redis_array.c index f5a1502160..514a8d3fb3 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1000,10 +1000,10 @@ PHP_METHOD(RedisArray, mset) int *pos, argc, *argc_each; HashTable *h_keys; zval *redis_inst, **argv; - char *key, **keys, **key_free, kbuf[40]; - unsigned int key_len, free_idx = 0; - int type, *key_lens; - unsigned long idx; + char *key, **keys, kbuf[40]; + int key_len, *key_lens; + zend_string *zkey; + ulong idx; /* Multi/exec support */ HANDLE_MULTI_EXEC("MSET"); @@ -1025,42 +1025,30 @@ PHP_METHOD(RedisArray, mset) keys = emalloc(argc * sizeof(char*)); key_lens = emalloc(argc * sizeof(int)); - /* Allocate an array holding the indexes of any keys that need freeing */ - key_free = emalloc(argc * sizeof(char*)); - argc_each = emalloc(ra->count * sizeof(int)); memset(argc_each, 0, ra->count * sizeof(int)); /* associate each key to a redis node */ - for(i = 0, zend_hash_internal_pointer_reset(h_keys); - zend_hash_has_more_elements(h_keys) == SUCCESS; - zend_hash_move_forward(h_keys), i++) - { - /* We have to skip the element if we can't get the array value */ - if ((data = zend_hash_get_current_data(h_keys)) == NULL) { - continue; - } - - /* Grab our key */ - type = zend_hash_get_current_key_ex(h_keys, &key, &key_len, &idx, 0, NULL); - + i = 0; + ZEND_HASH_FOREACH_KEY_VAL(h_keys, idx, zkey, data) { /* If the key isn't a string, make a string representation of it */ - if(type != HASH_KEY_IS_STRING) { + if (zkey) { + key_len = zkey->len - 1; + key = zkey->val; + } else { key_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); - key = estrndup(kbuf, key_len); - key_free[free_idx++]=key; - } else { - key_len--; /* We don't want the null terminator */ - } + key = kbuf; + } if (ra_find_node(ra, key, (int)key_len, &pos[i] TSRMLS_CC) == NULL) { // TODO: handle } argc_each[pos[i]]++; /* count number of keys per node */ - argv[i] = data; - keys[i] = key; + keys[i] = estrndup(key, key_len); key_lens[i] = (int)key_len; - } + argv[i] = data; + i++; + } ZEND_HASH_FOREACH_END(); /* calls */ @@ -1114,13 +1102,12 @@ PHP_METHOD(RedisArray, mset) } /* Free any keys that we needed to allocate memory for, because they weren't strings */ - for(i=0; ival, zkey->len - 1, 0); + } else { + ZVAL_LONG(z_zadd_args[i+1], (long)idx); + } i += 2; - } + } ZEND_HASH_FOREACH_END(); /* run ZADD on target */ ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4, 0); diff --git a/redis_commands.c b/redis_commands.c index 917d149503..2b1beddf15 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -527,13 +527,13 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key; int key_len, key_free; - char *start, *end, *optkey; + char *start, *end; int start_len, end_len; - int has_limit=0, type; + int has_limit=0; long offset, count; zval *z_opt=NULL, *z_ele; - unsigned long idx; - unsigned int optlen; + zend_string *zkey; + ulong idx; HashTable *ht_opt; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a", &key, &key_len, @@ -546,23 +546,14 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Check for an options array if(z_opt && Z_TYPE_P(z_opt)==IS_ARRAY) { ht_opt = Z_ARRVAL_P(z_opt); - for (zend_hash_internal_pointer_reset(ht_opt); - zend_hash_has_more_elements(ht_opt) == SUCCESS; - zend_hash_move_forward(ht_opt)) - { - /* Grab current key and value */ - type = zend_hash_get_current_key_ex(ht_opt, &optkey, &optlen, &idx, 0, NULL); - if ((z_ele = zend_hash_get_current_data(ht_opt)) == NULL) { - continue; - } - + ZEND_HASH_FOREACH_KEY_VAL(ht_opt, idx, zkey, z_ele) { /* All options require a string key type */ - if (type != HASH_KEY_IS_STRING) continue; + if (!zkey) continue; /* Check for withscores and limit */ - if (IS_WITHSCORES_ARG(optkey, optlen)) { + if (IS_WITHSCORES_ARG(zkey->val, zkey->len)) { *withscores = (Z_TYPE_P(z_ele) == IS_BOOL && Z_BVAL_P(z_ele) == 1); - } else if (IS_LIMIT_ARG(optkey, optlen) && Z_TYPE_P(z_ele) == IS_ARRAY) { + } else if (IS_LIMIT_ARG(zkey->val, zkey->len) && Z_TYPE_P(z_ele) == IS_ARRAY) { HashTable *htlimit = Z_ARRVAL_P(z_ele); zval *zoff, *zcnt; @@ -580,7 +571,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } } - } + } ZEND_HASH_FOREACH_END(); } // Prefix our key, set slot @@ -1238,27 +1229,16 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Check for an options array if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { HashTable *kt = Z_ARRVAL_P(z_opts); - int type; - unsigned int ht_key_len; - unsigned long idx; - char *k; + zend_string *zkey; + ulong idx; zval *v; /* Iterate our option array */ - for(zend_hash_internal_pointer_reset(kt); - zend_hash_has_more_elements(kt) == SUCCESS; - zend_hash_move_forward(kt)) - { - // Grab key and value - type = zend_hash_get_current_key_ex(kt, &k, &ht_key_len, &idx, 0, NULL); - if ((v = zend_hash_get_current_data(kt)) == NULL) { - continue; - } - + ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, v) { /* Detect PX or EX argument and validate timeout */ - if (type == HASH_KEY_IS_STRING && IS_EX_PX_ARG(k)) { + if (zkey && IS_EX_PX_ARG(zkey->val)) { /* Set expire type */ - exp_type = k; + exp_type = zkey->val; /* Try to extract timeout */ if (Z_TYPE_P(v) == IS_LONG) { @@ -1272,7 +1252,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else if (Z_TYPE_P(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_P(v))) { set_type = Z_STRVAL_P(v); } - } + } ZEND_HASH_FOREACH_END(); } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { /* Grab expiry and fail if it's < 1 */ expire = Z_LVAL_P(z_opts); @@ -1581,12 +1561,13 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - int key_len, key_free, count, ktype; - unsigned long idx; + int key_len, key_free, count; + ulong idx; zval *z_arr; HashTable *ht_vals; - HashPosition pos; smart_string cmdstr = {0}; + zend_string *zkey; + zval *z_val; // Parse args if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, @@ -1611,27 +1592,18 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, key, key_len); // Start traversing our key => value array - for(zend_hash_internal_pointer_reset_ex(ht_vals, &pos); - zend_hash_has_more_elements_ex(ht_vals, &pos)==SUCCESS; - zend_hash_move_forward_ex(ht_vals, &pos)) - { + ZEND_HASH_FOREACH_KEY_VAL(ht_vals, idx, zkey, z_val) { char *mem, *val, kbuf[40]; int val_len, val_free; unsigned int mem_len; - zval *z_val; - - // Grab our key, and value for this element in our input - ktype = zend_hash_get_current_key_ex(ht_vals, &mem, - &mem_len, &idx, 0, &pos); - z_val = zend_hash_get_current_data_ex(ht_vals, &pos); // If the hash key is an integer, convert it to a string - if(ktype != HASH_KEY_IS_STRING) { + if (zkey) { + mem_len = zkey->len - 1; + mem = zkey->val; + } else { mem_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); mem = (char*)kbuf; - } else { - // Length returned includes the \0 - mem_len--; } // Serialize value (if directed) @@ -1643,7 +1615,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Free our value if we serialized it if (val_free) STR_FREE(val); - } + } ZEND_HASH_FOREACH_END(); // Set slot if directed CMD_SET_SLOT(slot,key,key_len); @@ -2775,25 +2747,19 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, int *withhash, long *count, geoSortType *sort) { - int type; - unsigned long idx; - unsigned int optkeylen; - char *optkey, *optstr; + ulong idx; + char *optstr; + zend_string *zkey; zval *optval; /* Iterate over our argument array, collating which ones we have */ - for (zend_hash_internal_pointer_reset(ht); - zend_hash_has_more_elements(ht) == SUCCESS; - zend_hash_move_forward(ht)) - { - /* Grab key and value */ - type = zend_hash_get_current_key_ex(ht, &optkey, &optkeylen, &idx, 0, NULL); - if ((optval = zend_hash_get_current_data(ht)) == NULL) { - continue; - } - + ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, optval) { /* If the key is numeric it's a non value option */ - if (type == HASH_KEY_IS_LONG) { + if (zkey) { + if (zkey->len == 6 && !strcasecmp(zkey->val, "count") && Z_TYPE_P(optval) == IS_LONG) { + *count = Z_LVAL_P(optval); + } + } else { /* Option needs to be a string */ if (Z_TYPE_P(optval) != IS_STRING) continue; @@ -2810,10 +2776,8 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, } else if (!strcasecmp(optstr, "desc")) { *sort = SORT_DESC; } - } else if (!strcasecmp(optkey, "count") && Z_TYPE_P(optval) == IS_LONG) { - *count = Z_LVAL_P(optval); } - } + } ZEND_HASH_FOREACH_END(); } /* Helper to append options to a GEORADIUS or GEORADIUSBYMEMBER command */ From fa2dc4e642a777aa39bb74ecb628f398cb821447 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 9 Oct 2016 21:22:57 +0300 Subject: [PATCH 0591/1986] WIP: php7 compatibility RETVAL_STRING and RETVAL_STRINGL always duplicate value --- cluster_library.c | 2 +- cluster_library.h | 2 +- common.h | 9 +++++++++ library.c | 10 +++++----- redis.c | 8 ++++---- redis_cluster.c | 4 ++-- redis_commands.c | 11 ++++++----- 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 6dc7005704..aaae63afd7 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1866,7 +1866,7 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust RETVAL_TRUE; break; case TYPE_BULK: - RETVAL_STRINGL(r->str, r->len, 0); + RETVAL_STRINGL(r->str, r->len); break; case TYPE_MULTIBULK: MAKE_STD_ZVAL(z_arr); diff --git a/cluster_library.h b/cluster_library.h index 32d248d666..fc017c4f5c 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -116,7 +116,7 @@ /* Helper to return a string value */ #define CLUSTER_RETURN_STRING(c, str, len) \ if(CLUSTER_IS_ATOMIC(c)) { \ - RETURN_STRINGL(str, len, 0); \ + RETURN_STRINGL(str, len); \ } else { \ add_next_index_stringl(c->multi_resp, str, len, 0); \ } \ diff --git a/common.h b/common.h index 9a01608067..3439ca8945 100644 --- a/common.h +++ b/common.h @@ -167,6 +167,15 @@ inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array T typedef zend_rsrc_list_entry zend_resource; +#undef RETVAL_STRING +#define RETVAL_STRING(s) ZVAL_STRING(return_value, s, 1) +#undef RETURN_STRING +#define RETURN_STRING(s) { RETVAL_STRING(s); return; } +#undef RETVAL_STRINGL +#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l, 1) +#undef RETURN_STRINGL +#define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; } + #else #include #endif diff --git a/library.c b/library.c index 3a2f4dd662..ae95b4d014 100644 --- a/library.c +++ b/library.c @@ -1206,7 +1206,7 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, } } else { if(ret > LONG_MAX) { /* overflow */ - RETURN_STRINGL(response+1, response_len-1, 1); + RETURN_STRINGL(response+1, response_len-1); } else { efree(response); RETURN_LONG((long)ret); @@ -1430,10 +1430,9 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock if(redis_unserialize(redis_sock, response, response_len, &return_value TSRMLS_CC) == 0) { - RETURN_STRINGL(response, response_len, 0); - } else { - efree(response); + RETVAL_STRINGL(response, response_len); } + efree(response); } } @@ -1458,7 +1457,8 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, IF_MULTI_OR_PIPELINE() { add_next_index_stringl(z_tab, response, response_len, 0); } else { - RETURN_STRINGL(response, response_len, 0); + RETVAL_STRINGL(response, response_len); + efree(response); } } diff --git a/redis.c b/redis.c index cba470cc18..c45999cc1c 100644 --- a/redis.c +++ b/redis.c @@ -3397,7 +3397,7 @@ PHP_METHOD(Redis, getLastError) { /* Return our last error or NULL if we don't have one */ if(redis_sock->err != NULL && redis_sock->err_len > 0) { - RETURN_STRINGL(redis_sock->err, redis_sock->err_len, 1); + RETURN_STRINGL(redis_sock->err, redis_sock->err_len); } else { RETURN_NULL(); } @@ -3478,7 +3478,7 @@ PHP_METHOD(Redis, getHost) { RedisSock *redis_sock; if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { - RETURN_STRING(redis_sock->host, 1); + RETURN_STRING(redis_sock->host); } else { RETURN_FALSE; } @@ -3536,7 +3536,7 @@ PHP_METHOD(Redis, getPersistentID) { if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { if(redis_sock->persistent_id != NULL) { - RETURN_STRING(redis_sock->persistent_id, 1); + RETURN_STRING(redis_sock->persistent_id); } else { RETURN_NULL(); } @@ -3551,7 +3551,7 @@ PHP_METHOD(Redis, getAuth) { if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { if(redis_sock->auth != NULL) { - RETURN_STRING(redis_sock->auth, 1); + RETURN_STRING(redis_sock->auth); } else { RETURN_NULL(); } diff --git a/redis_cluster.c b/redis_cluster.c index 691e459a0d..1e0682a447 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1952,7 +1952,7 @@ PHP_METHOD(RedisCluster, getlasterror) { redisCluster *c = GET_CONTEXT(); if(c->err != NULL && c->err_len > 0) { - RETURN_STRINGL(c->err, c->err_len, 1); + RETURN_STRINGL(c->err, c->err_len); } else { RETURN_NULL(); } @@ -2043,7 +2043,7 @@ PHP_METHOD(RedisCluster, _redir) { len = snprintf(buf, sizeof(buf), "%s:%d", c->redir_host, c->redir_port); if(*c->redir_host && c->redir_host_len) { - RETURN_STRINGL(buf, len, 1); + RETURN_STRINGL(buf, len); } else { RETURN_NULL(); } diff --git a/redis_commands.c b/redis_commands.c index 2b1beddf15..5c665abdeb 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3089,7 +3089,7 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_LONG(redis_sock->serializer); case REDIS_OPT_PREFIX: if(redis_sock->prefix) { - RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len, 1); + RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len); } RETURN_NULL(); case REDIS_OPT_READ_TIMEOUT: @@ -3193,9 +3193,10 @@ void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { if(redis_sock->prefix != NULL && redis_sock->prefix_len>0) { redis_key_prefix(redis_sock, &key, &key_len); - RETURN_STRINGL(key, key_len, 0); + RETVAL_STRINGL(key, key_len); + efree(key); } else { - RETURN_STRINGL(key, key_len, 1); + RETURN_STRINGL(key, key_len); } } @@ -3212,7 +3213,7 @@ void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, int val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - RETVAL_STRINGL(val, val_len, 1); + RETVAL_STRINGL(val, val_len); if(val_free) STR_FREE(val); } @@ -3244,7 +3245,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_ZVAL(z_ret, 0, 1); } else { // Just return the value that was passed to us - RETURN_STRINGL(value, value_len, 1); + RETURN_STRINGL(value, value_len); } } From 33a8843c44d9a32cdcc8497422b794699cb6d9d0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 10 Oct 2016 21:51:18 +0300 Subject: [PATCH 0592/1986] RedisArray refactoring --- redis_array.c | 64 ++++++++++++++++++++++++---------------------- redis_array.h | 2 +- redis_array_impl.c | 27 ++++++++++--------- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/redis_array.c b/redis_array.c index 514a8d3fb3..17a588de44 100644 --- a/redis_array.c +++ b/redis_array.c @@ -89,8 +89,7 @@ static void redis_array_free(RedisArray *ra) { /* Redis objects */ for(i=0;icount;i++) { - zval_dtor(ra->redis[i]); - efree(ra->redis[i]); + zval_dtor(&ra->redis[i]); efree(ra->hosts[i]); } efree(ra->redis); @@ -488,7 +487,7 @@ PHP_METHOD(RedisArray, _target) redis_inst = ra_find_node(ra, key, key_len, &i TSRMLS_CC); if(redis_inst) { - ZVAL_STRING(return_value, ra->hosts[i], 1); + RETURN_STRING(ra->hosts[i]); } else { RETURN_NULL(); } @@ -588,7 +587,7 @@ PHP_METHOD(RedisArray, _rehash) static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) { - zval *object, z_fun, *z_tmp; + zval *object, z_fun, *z_tmp, *redis_inst; int i; RedisArray *ra; @@ -610,8 +609,9 @@ static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *metho MAKE_STD_ZVAL(z_tmp); /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], - &z_fun, z_tmp, 0, NULL TSRMLS_CC); + redis_inst = &ra->redis[i]; + call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, + z_tmp, 0, NULL TSRMLS_CC); add_assoc_zval(return_value, ra->hosts[i], z_tmp); } @@ -650,7 +650,7 @@ PHP_METHOD(RedisArray, bgsave) PHP_METHOD(RedisArray, keys) { - zval *object, *z_args[1], *z_tmp, z_fun; + zval *object, *z_args[1], *z_tmp, z_fun, *redis_inst; RedisArray *ra; char *pattern; int pattern_len, i; @@ -683,7 +683,9 @@ PHP_METHOD(RedisArray, keys) MAKE_STD_ZVAL(z_tmp); /* Call KEYS on each node */ - call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args TSRMLS_CC); + redis_inst = &ra->redis[i]; + call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, + z_tmp, 1, z_args TSRMLS_CC); /* Add the result for this host */ add_assoc_zval(return_value, ra->hosts[i], z_tmp); @@ -695,7 +697,7 @@ PHP_METHOD(RedisArray, keys) PHP_METHOD(RedisArray, getOption) { - zval *object, z_fun, *z_tmp, *z_args[1]; + zval *object, z_fun, *z_tmp, *z_args[1], *redis_inst; int i; RedisArray *ra; long opt; @@ -722,8 +724,9 @@ PHP_METHOD(RedisArray, getOption) MAKE_STD_ZVAL(z_tmp); /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], - &z_fun, z_tmp, 1, z_args TSRMLS_CC); + redis_inst = &ra->redis[i]; + call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, + z_tmp, 1, z_args TSRMLS_CC); add_assoc_zval(return_value, ra->hosts[i], z_tmp); } @@ -734,7 +737,7 @@ PHP_METHOD(RedisArray, getOption) PHP_METHOD(RedisArray, setOption) { - zval *object, z_fun, *z_tmp, *z_args[2]; + zval *object, z_fun, *z_tmp, *z_args[2], *redis_inst; int i; RedisArray *ra; long opt; @@ -765,8 +768,9 @@ PHP_METHOD(RedisArray, setOption) MAKE_STD_ZVAL(z_tmp); /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], - &z_fun, z_tmp, 2, z_args TSRMLS_CC); + redis_inst = &ra->redis[i]; + call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, + z_tmp, 2, z_args TSRMLS_CC); add_assoc_zval(return_value, ra->hosts[i], z_tmp); } @@ -778,7 +782,7 @@ PHP_METHOD(RedisArray, setOption) PHP_METHOD(RedisArray, select) { - zval *object, z_fun, *z_tmp, *z_args[2]; + zval *object, z_fun, *z_tmp, *z_args[2], *redis_inst; int i; RedisArray *ra; long opt; @@ -801,12 +805,12 @@ PHP_METHOD(RedisArray, select) array_init(return_value); for(i = 0; i < ra->count; ++i) { - MAKE_STD_ZVAL(z_tmp); /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], - &z_fun, z_tmp, 1, z_args TSRMLS_CC); + redis_inst = &ra->redis[i]; + call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, + z_tmp, 1, z_args TSRMLS_CC); add_assoc_zval(return_value, ra->hosts[i], z_tmp); } @@ -852,7 +856,7 @@ PHP_METHOD(RedisArray, mget) RedisArray *ra; int *pos, argc, *argc_each; HashTable *h_keys; - zval **argv; + zval **argv, *redis_inst; /* Multi/exec support */ HANDLE_MULTI_EXEC("MGET"); @@ -938,8 +942,9 @@ PHP_METHOD(RedisArray, mget) /* call MGET on the node */ MAKE_STD_ZVAL(z_ret); - call_user_function(&redis_ce->function_table, &ra->redis[n], - &z_fun, z_ret, 1, &z_argarray TSRMLS_CC); + redis_inst = &ra->redis[n]; + call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, + z_ret, 1, &z_argarray TSRMLS_CC); /* cleanup args array */ zval_ptr_dtor(&z_argarray); @@ -1055,10 +1060,6 @@ PHP_METHOD(RedisArray, mset) for(n = 0; n < ra->count; ++n) { /* for each node */ int found = 0; - /* prepare call */ - ZVAL_STRING(&z_fun, "MSET", 0); - redis_inst = ra->redis[n]; - /* copy args */ MAKE_STD_ZVAL(z_argarray); array_init(z_argarray); @@ -1083,13 +1084,16 @@ PHP_METHOD(RedisArray, mset) continue; /* don't run empty MSETs */ } + /* prepare call */ + ZVAL_STRING(&z_fun, "MSET", 0); + redis_inst = &ra->redis[n]; if(ra->index) { /* add MULTI */ ra_index_multi(redis_inst, MULTI TSRMLS_CC); } /* call */ - call_user_function(&redis_ce->function_table, &ra->redis[n], - &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); + call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, + &z_ret, 1, &z_argarray TSRMLS_CC); if(ra->index) { ra_index_keys(z_argarray, redis_inst TSRMLS_CC); /* use SADD to add keys to node index */ @@ -1194,7 +1198,6 @@ PHP_METHOD(RedisArray, del) for(n = 0; n < ra->count; ++n) { /* for each node */ int found = 0; - redis_inst = ra->redis[n]; /* copy args */ MAKE_STD_ZVAL(z_argarray); @@ -1217,14 +1220,15 @@ PHP_METHOD(RedisArray, del) continue; } + redis_inst = &ra->redis[n]; if(ra->index) { /* add MULTI */ ra_index_multi(redis_inst, MULTI TSRMLS_CC); } /* call */ MAKE_STD_ZVAL(z_ret); - call_user_function(&redis_ce->function_table, &redis_inst, - &z_fun, z_ret, 1, &z_argarray TSRMLS_CC); + call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, + z_ret, 1, &z_argarray TSRMLS_CC); if(ra->index) { ra_index_del(z_argarray, redis_inst TSRMLS_CC); /* use SREM to remove keys from node index */ diff --git a/redis_array.h b/redis_array.h index e2cce58a9c..7a318b6784 100644 --- a/redis_array.h +++ b/redis_array.h @@ -43,7 +43,7 @@ typedef struct RedisArray_ { int count; char **hosts; /* array of host:port strings */ - zval **redis; /* array of Redis instances */ + zval *redis; /* array of Redis instances */ zval *z_multi_exec; /* Redis instance to be used in multi-exec */ zend_bool index; /* use per-node index */ zend_bool auto_rehash; /* migrate keys on read operations */ diff --git a/redis_array_impl.c b/redis_array_impl.c index c9e8afc241..2c122c5ad8 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -35,7 +35,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b int i = 0, host_len, id; char *host, *p; short port; - zval *zpData, z_cons, z_ret; + zval *zpData, z_cons, z_ret, *redis_inst; RedisSock *redis_sock = NULL; /* function calls on the Redis object */ @@ -47,8 +47,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b if ((zpData = zend_hash_get_current_data(hosts)) == NULL || Z_TYPE_P(zpData) != IS_STRING) { for(i=0;icount;i++) { - zval_dtor(ra->redis[i]); - efree(ra->redis[i]); + zval_dtor(&ra->redis[i]); efree(ra->hosts[i]); } efree(ra->redis); @@ -74,10 +73,10 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b } /* create Redis object */ - MAKE_STD_ZVAL(ra->redis[i]); - object_init_ex(ra->redis[i], redis_ce); - INIT_PZVAL(ra->redis[i]); - call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC); + redis_inst = &ra->redis[i]; + object_init_ex(redis_inst, redis_ce); + INIT_PZVAL(redis_inst); + call_user_function(&redis_ce->function_table, &redis_inst, &z_cons, &z_ret, 0, NULL TSRMLS_CC); /* create socket */ redis_sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); @@ -95,9 +94,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b id = zend_list_insert(redis_sock, le_redis_sock); #endif #if (PHP_MAJOR_VERSION < 7) - add_property_resource(ra->redis[i], "socket", id); + add_property_resource(&ra->redis[i], "socket", id); #else - add_property_resource(ra->redis[i], "socket", Z_RES_P(id)); + add_property_resource(&ra->redis[i], "socket", Z_RES_P(id)); #endif ra->count = ++i; @@ -363,8 +362,8 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* create object */ RedisArray *ra = emalloc(sizeof(RedisArray)); - ra->hosts = emalloc(count * sizeof(char*)); - ra->redis = emalloc(count * sizeof(zval*)); + ra->hosts = ecalloc(count, sizeof(char *)); + ra->redis = ecalloc(count, sizeof(zval)); ra->count = 0; ra->z_fun = NULL; ra->z_dist = NULL; @@ -500,7 +499,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D } if(out_pos) *out_pos = pos; - return ra->redis[pos]; + return &ra->redis[pos]; } zval * @@ -509,7 +508,7 @@ ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC) { int i; for(i = 0; i < ra->count; ++i) { if(strncmp(ra->hosts[i], host, host_len) == 0) { - return ra->redis[i]; + return &ra->redis[i]; } } return NULL; @@ -1256,7 +1255,7 @@ ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cac return; /* TODO: compare the two rings for equality */ for(i = 0; i < ra->prev->count; ++i) { - ra_rehash_server(ra, ra->prev->redis[i], ra->prev->hosts[i], ra->index, z_cb, z_cb_cache TSRMLS_CC); + ra_rehash_server(ra, &ra->prev->redis[i], ra->prev->hosts[i], ra->index, z_cb, z_cb_cache TSRMLS_CC); } } From 2f2b3b319334e575eab1fa34a9734700af0a735b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 10 Oct 2016 23:10:00 +0300 Subject: [PATCH 0593/1986] ra_index_keys refactoring --- redis_array_impl.c | 48 +++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 2c122c5ad8..65529f0818 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -583,46 +583,34 @@ ra_index_del(zval *z_keys, zval *z_redis TSRMLS_DC) { void ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { + zval z_keys, *z_val; + zend_string *zkey; + ulong idx; /* Initialize key array */ - zval *z_keys; - MAKE_STD_ZVAL(z_keys); - HashPosition pos; #if PHP_VERSION_ID > 50300 - array_init_size(z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); + array_init_size(&z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); #else - array_init(z_keys); + array_init(&z_keys); #endif /* Go through input array and add values to the key array */ - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z_pairs), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(z_pairs), &pos) != NULL) { - char *key; - unsigned int key_len; - unsigned long num_key; - zval *z_new; - MAKE_STD_ZVAL(z_new); - - switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(z_pairs), &key, &key_len, &num_key, 1, &pos)) { - case HASH_KEY_IS_STRING: - ZVAL_STRINGL(z_new, key, (int)key_len - 1, 0); - zend_hash_next_index_insert(Z_ARRVAL_P(z_keys), z_new); - break; - - case HASH_KEY_IS_LONG: - Z_TYPE_P(z_new) = IS_LONG; - Z_LVAL_P(z_new) = (long)num_key; - zend_hash_next_index_insert(Z_ARRVAL_P(z_keys), z_new); - break; - } - zend_hash_move_forward_ex(Z_ARRVAL_P(z_pairs), &pos); - } + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_pairs), idx, zkey, z_val) { + zval *z_new; + MAKE_STD_ZVAL(z_new); + + if (zkey) { + ZVAL_STRINGL(z_new, zkey->val, zkey->len, 1); + } else { + ZVAL_LONG(z_new, idx); + } + zend_hash_next_index_insert(Z_ARRVAL(z_keys), z_new); + } ZEND_HASH_FOREACH_END(); /* add keys to index */ - ra_index_change_keys("SADD", z_keys, z_redis TSRMLS_CC); + ra_index_change_keys("SADD", &z_keys, z_redis TSRMLS_CC); /* cleanup */ - zval_dtor(z_keys); - efree(z_keys); + zval_dtor(&z_keys); } void From 75183064b541ddbc948843ca48b46614a500a43b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 11 Oct 2016 22:29:18 +0300 Subject: [PATCH 0594/1986] WIP: php7 compatibility Wrap zend_register_internal_class_ex calls + redis-cluster GET_CONTEXT --- redis.c | 8 ++++++++ redis_cluster.h | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/redis.c b/redis.c index c45999cc1c..fd21aad8b5 100644 --- a/redis.c +++ b/redis.c @@ -595,8 +595,12 @@ PHP_MINIT_FUNCTION(redis) INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL); redis_exception_ce = zend_register_internal_class_ex( &redis_exception_class_entry, +#if (PHP_MAJOR_VERSION < 7) redis_get_exception_base(0 TSRMLS_CC), NULL TSRMLS_CC +#else + redis_get_exception_base(0) +#endif ); /* RedisClusterException class */ @@ -604,8 +608,12 @@ PHP_MINIT_FUNCTION(redis) "RedisClusterException", NULL); redis_cluster_exception_ce = zend_register_internal_class_ex( &redis_cluster_exception_class_entry, +#if (PHP_MAJOR_VERSION < 7) rediscluster_get_exception_base(0 TSRMLS_CC), NULL TSRMLS_CC +#else + rediscluster_get_exception_base(0) +#endif ); le_redis_sock = zend_register_list_destructors_ex( diff --git a/redis_cluster.h b/redis_cluster.h index 4a7facfdd6..f6be80ba54 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -10,8 +10,13 @@ #define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) /* Get attached object context */ +#if (PHP_MAJOR_VERSION < 7) #define GET_CONTEXT() \ ((redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC)) +#else +#define GET_CONTEXT() \ + ((redisCluster *)((char *)getThis() - XtOffsetOf(redisCluster, std))) +#endif /* Command building/processing is identical for every command */ #define CLUSTER_BUILD_CMD(name, c, cmd, cmd_len, slot) \ From c2ae622ed8b4bb0d91fd26155faba413516ab6b7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 11 Oct 2016 23:32:19 +0300 Subject: [PATCH 0595/1986] WIP: php7 compatibility Redefine add_next_index_stringl --- cluster_library.c | 25 ++++++++++++++----------- cluster_library.h | 4 ++-- common.h | 3 +++ library.c | 15 +++++++-------- redis_cluster.c | 4 ++-- redis_commands.c | 27 +++++++++++++-------------- 6 files changed, 41 insertions(+), 37 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index aaae63afd7..4c321900ba 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1488,6 +1488,7 @@ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, // Return our response raw CLUSTER_RETURN_STRING(c, resp, c->reply_len); + efree(resp); } /* BULK response handler */ @@ -1508,18 +1509,16 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster TSRMLS_CC) == 0) { CLUSTER_RETURN_STRING(c, resp, c->reply_len); - } else { - efree(resp); } } else { zval *z = NULL; if (redis_unserialize(c->flags, resp, c->reply_len, &z TSRMLS_CC)) { - efree(resp); add_next_index_zval(c->multi_resp, z); } else { - add_next_index_stringl(c->multi_resp, resp, c->reply_len, 0); + add_next_index_stringl(c->multi_resp, resp, c->reply_len); } } + efree(resp); } /* Bulk response where we expect a double */ @@ -1820,10 +1819,12 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) add_next_index_bool(z_ret, 1); break; case TYPE_BULK: - if (r->len > -1) - add_next_index_stringl(z_ret, r->str, r->len, 0); - else + if (r->len > -1) { + add_next_index_stringl(z_ret, r->str, r->len); + efree(r->str); + } else { add_next_index_null(z_ret); + } break; case TYPE_MULTIBULK: MAKE_STD_ZVAL(z_sub_ele); @@ -1895,7 +1896,8 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust add_next_index_bool(c->multi_resp, 1); break; case TYPE_BULK: - add_next_index_stringl(c->multi_resp, r->str, r->len, 0); + add_next_index_stringl(c->multi_resp, r->str, r->len); + efree(r->str); break; case TYPE_MULTIBULK: cluster_mbulk_variant_resp(r, c->multi_resp); @@ -2315,7 +2317,8 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, if(line == NULL) return FAILURE; // Add to our result array - add_next_index_stringl(z_result, line, line_len, 0); + add_next_index_stringl(z_result, line, line_len); + efree(line); } // Success! @@ -2338,10 +2341,10 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_next_index_zval(z_result, z); - efree(line); } else { - add_next_index_stringl(z_result, line, line_len, 0); + add_next_index_stringl(z_result, line, line_len); } + efree(line); } else { if (line) efree(line); add_next_index_bool(z_result, 0); diff --git a/cluster_library.h b/cluster_library.h index fc017c4f5c..f49475110b 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -116,9 +116,9 @@ /* Helper to return a string value */ #define CLUSTER_RETURN_STRING(c, str, len) \ if(CLUSTER_IS_ATOMIC(c)) { \ - RETURN_STRINGL(str, len); \ + RETVAL_STRINGL(str, len); \ } else { \ - add_next_index_stringl(c->multi_resp, str, len, 0); \ + add_next_index_stringl(c->multi_resp, str, len); \ } \ /* Return a LONG value */ diff --git a/common.h b/common.h index 3439ca8945..2731ee76d9 100644 --- a/common.h +++ b/common.h @@ -167,6 +167,9 @@ inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array T typedef zend_rsrc_list_entry zend_resource; +static int (*_add_next_index_stringl)(zval *, const char *, uint, int) = &add_next_index_stringl; +#define add_next_index_stringl(arg, str, length) _add_next_index_stringl(arg, str, length, 1); + #undef RETVAL_STRING #define RETVAL_STRING(s) ZVAL_STRING(return_value, s, 1) #undef RETURN_STRING diff --git a/library.c b/library.c index ae95b4d014..8d7d490782 100644 --- a/library.c +++ b/library.c @@ -1199,7 +1199,7 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, #endif IF_MULTI_OR_PIPELINE() { if(ret > LONG_MAX) { /* overflow */ - add_next_index_stringl(z_tab, response+1, response_len-1, 1); + add_next_index_stringl(z_tab, response + 1, response_len - 1); } else { efree(response); add_next_index_long(z_tab, (long)ret); @@ -1421,10 +1421,9 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); add_next_index_zval(z_tab, z); } else { - add_next_index_stringl(z_tab, response, response_len, 0); + add_next_index_stringl(z_tab, response, response_len); } } else { if(redis_unserialize(redis_sock, response, response_len, @@ -1432,8 +1431,8 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock { RETVAL_STRINGL(response, response_len); } - efree(response); } + efree(response); } /* like string response, but never unserialized. */ @@ -1455,11 +1454,11 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - add_next_index_stringl(z_tab, response, response_len, 0); + add_next_index_stringl(z_tab, response, response_len); } else { RETVAL_STRINGL(response, response_len); - efree(response); } + efree(response); } /* Response for DEBUG object which is a formatted single line reply */ @@ -1888,11 +1887,11 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, (unserialize == UNSERIALIZE_VALS && count % 2 != 0); if (unwrap && redis_unserialize(redis_sock, line, len, &z TSRMLS_CC)) { - efree(line); add_next_index_zval(z_tab, z); } else { - add_next_index_stringl(z_tab, line, len, 0); + add_next_index_stringl(z_tab, line, len); } + efree(line); } else { add_next_index_bool(z_tab, 0); } diff --git a/redis_cluster.c b/redis_cluster.c index 1e0682a447..5ffbd9ea3c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1038,7 +1038,7 @@ PHP_METHOD(RedisCluster, keys) { } add_next_index_stringl(z_ret, resp->element[i]->str, - resp->element[i]->len, 0); + resp->element[i]->len); } /* Free response, don't free data */ @@ -2027,7 +2027,7 @@ PHP_METHOD(RedisCluster, _masters) { MAKE_STD_ZVAL(z_sub); array_init(z_sub); - add_next_index_stringl(z_sub, host, strlen(host), 1); + add_next_index_stringl(z_sub, host, strlen(host)); add_next_index_long(z_sub, port); add_next_index_zval(z_ret, z_sub); } diff --git a/redis_commands.c b/redis_commands.c index 5c665abdeb..8af7670de0 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2353,7 +2353,8 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, array_init(z_argv); // SORT - add_next_index_stringl(z_argv, key, key_len, !key_free); + add_next_index_stringl(z_argv, key, key_len); + if (key_free) efree(key); // Set slot CMD_SET_SLOT(slot,key,key_len); @@ -2377,8 +2378,8 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // ... BY - add_next_index_stringl(z_argv, "BY", sizeof("BY")-1, 1); - add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), 1); + add_next_index_stringl(z_argv, "BY", sizeof("BY") - 1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } // Handle ASC/DESC option @@ -2387,7 +2388,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) && Z_TYPE_P(z_ele) == IS_STRING ) { // 'asc'|'desc' - add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), 1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } // STORE option @@ -2409,8 +2410,8 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // STORE - add_next_index_stringl(z_argv,"STORE",sizeof("STORE")-1, 1); - add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), 1); + add_next_index_stringl(z_argv, "STORE", sizeof("STORE") - 1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); // We are using STORE *using_store = 1; @@ -2433,9 +2434,8 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If it's a string just add it if (Z_TYPE_P(z_ele) == IS_STRING) { - add_next_index_stringl(z_argv,"GET",sizeof("GET")-1,1); - add_next_index_stringl(z_argv,Z_STRVAL_P(z_ele), - Z_STRLEN_P(z_ele), 1); + add_next_index_stringl(z_argv, "GET", sizeof("GET") - 1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { HashTable *ht_keys = Z_ARRVAL_P(z_ele); int added=0; @@ -2451,11 +2451,10 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, continue; } /* Add get per thing we're getting */ - add_next_index_stringl(z_argv, "GET", sizeof("GET")-1, 1); + add_next_index_stringl(z_argv, "GET", sizeof("GET") - 1); // Add this key to our argv array - add_next_index_stringl(z_argv, Z_STRVAL_P(z_key), - Z_STRLEN_P(z_key), 1); + add_next_index_stringl(z_argv, Z_STRVAL_P(z_key), Z_STRLEN_P(z_key)); added++; } @@ -2476,7 +2475,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL ) && Z_TYPE_P(z_ele) == IS_BOOL && Z_BVAL_P(z_ele) == 1 ) { - add_next_index_stringl(z_argv, "ALPHA", sizeof("ALPHA")-1,1); + add_next_index_stringl(z_argv, "ALPHA", sizeof("ALPHA") - 1); } // LIMIT @@ -2502,7 +2501,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Add LIMIT argument - add_next_index_stringl(z_argv,"LIMIT",sizeof("LIMIT")-1,1); + add_next_index_stringl(z_argv, "LIMIT", sizeof("LIMIT") - 1); long low, high; if (Z_TYPE_P(z_off) == IS_STRING) { From 5f9cc57f8b86326cbf6d8654992ad8135512cfbc Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 17 Oct 2016 17:50:17 +0300 Subject: [PATCH 0596/1986] Revert "integer_length optimization" This reverts commit 0b2b809a57a63c297f603251615ec50f72b7bf35. --- library.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/library.c b/library.c index 8d7d490782..b382abfd2c 100644 --- a/library.c +++ b/library.c @@ -565,20 +565,18 @@ void add_constant_long(zend_class_entry *ce, char *name, int value) { int integer_length(int i) { - int sz = 1; - - if (i < 0) { /* allow for neg sign as well. */ - i = -i; + int sz = 0; + int ci = abs(i); + while (ci > 0) { + ci /= 10; sz++; } - for (;;) { - if (i < 10) return sz; - if (i < 100) return sz + 1; - if (i < 1000) return sz + 2; - // Skip ahead by 4 orders of magnitude - i /= 10000U; - sz += 4; + if (i == 0) { /* log 0 doesn't make sense. */ + sz = 1; + } else if (i < 0) { /* allow for neg sign as well. */ + sz++; } + return sz; } int From 8f006d70f29390589293385a5fa0372602ec1e14 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 19 Oct 2016 16:15:45 +0300 Subject: [PATCH 0597/1986] WIP: php7 compatibility Redefine call_user_function --- .travis.yml | 2 +- common.h | 21 ++++ redis_array.c | 84 +++++--------- redis_array_impl.c | 273 +++++++++++++++++++-------------------------- 4 files changed, 165 insertions(+), 215 deletions(-) diff --git a/.travis.yml b/.travis.yml index 59434a961e..7676c5e3fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ php: - 5.5 - 5.6 before_install: phpize -install: ./configure --prefix=/usr && sudo make install +install: ./configure CFLAGS=-Wall --prefix=/usr && sudo make install before_script: - gem install redis - mkdir -p tests/nodes/ && echo >> tests/nodes/nodemap diff --git a/common.h b/common.h index 2731ee76d9..8941493176 100644 --- a/common.h +++ b/common.h @@ -179,6 +179,27 @@ static int (*_add_next_index_stringl)(zval *, const char *, uint, int) = &add_ne #undef RETURN_STRINGL #define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; } +static int (*_call_user_function)(HashTable *, zval **, zval *, zval *, zend_uint, zval *[] TSRMLS_DC) = &call_user_function; +#define call_user_function(function_table, object, function_name, retval_ptr, param_count, params) \ + inline_call_user_function(function_table, object, function_name, retval_ptr, param_count, params TSRMLS_CC) + +static zend_always_inline int +inline_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, zend_uint param_count, zval params[] TSRMLS_DC) +{ + int i, ret; + zval **_params = NULL; + if (!params) param_count = 0; + if (param_count > 0) { + _params = ecalloc(param_count, sizeof(zval *)); + for (i = 0; i < param_count; i++) { + _params[i] = ¶ms[i]; + } + } + ret = _call_user_function(function_table, &object, function_name, retval_ptr, param_count, _params TSRMLS_CC); + if (_params) efree(_params); + return ret; +} + #else #include #endif diff --git a/redis_array.c b/redis_array.c index 17a588de44..22f7509f54 100644 --- a/redis_array.c +++ b/redis_array.c @@ -343,7 +343,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i int key_len; int i; zval *redis_inst; - zval z_fun, **z_callargs; + zval z_fun, *z_callargs; HashTable *h_args; int argc; @@ -378,17 +378,19 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* pass call through */ ZVAL_STRING(&z_fun, cmd, 0); /* method name */ - z_callargs = emalloc(argc * sizeof(zval*)); + z_callargs = ecalloc(argc + 1, sizeof(zval)); /* copy args to array */ i = 0; ZEND_HASH_FOREACH_VAL(h_args, zp_tmp) { - z_callargs[i++] = zp_tmp; + INIT_ZVAL(z_callargs[i]); + z_callargs[i] = *zp_tmp; + i++; } ZEND_HASH_FOREACH_END(); /* multi/exec */ if(ra->z_multi_exec) { - call_user_function(&redis_ce->function_table, &ra->z_multi_exec, &z_fun, return_value, argc, z_callargs TSRMLS_CC); + call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); efree(z_callargs); RETURN_ZVAL(getThis(), 1, 0); } @@ -396,7 +398,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* CALL! */ if(ra->index && b_write_cmd) { /* call using discarded temp value and extract exec results after. */ - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, &z_tmp, argc, z_callargs TSRMLS_CC); + call_user_function(&redis_ce->function_table, redis_inst, &z_fun, &z_tmp, argc, z_callargs); zval_dtor(&z_tmp); /* add keys to index. */ @@ -405,7 +407,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* call EXEC */ ra_index_exec(redis_inst, return_value, 0 TSRMLS_CC); } else { /* call directly through. */ - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC); + call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); /* check if we have an error. */ if(RA_CALL_FAILED(return_value,cmd) && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */ @@ -587,7 +589,7 @@ PHP_METHOD(RedisArray, _rehash) static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) { - zval *object, z_fun, *z_tmp, *redis_inst; + zval *object, z_fun, *z_tmp; int i; RedisArray *ra; @@ -609,9 +611,7 @@ static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *metho MAKE_STD_ZVAL(z_tmp); /* Call each node in turn */ - redis_inst = &ra->redis[i]; - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, - z_tmp, 0, NULL TSRMLS_CC); + call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 0, NULL); add_assoc_zval(return_value, ra->hosts[i], z_tmp); } @@ -650,7 +650,7 @@ PHP_METHOD(RedisArray, bgsave) PHP_METHOD(RedisArray, keys) { - zval *object, *z_args[1], *z_tmp, z_fun, *redis_inst; + zval *object, z_args[1], *z_tmp, z_fun; RedisArray *ra; char *pattern; int pattern_len, i; @@ -671,8 +671,7 @@ PHP_METHOD(RedisArray, keys) ZVAL_STRING(&z_fun, "KEYS", 0); /* We will be passing with one string argument (the pattern) */ - MAKE_STD_ZVAL(z_args[0]); - ZVAL_STRINGL(z_args[0], pattern, pattern_len, 0); + ZVAL_STRINGL(z_args, pattern, pattern_len, 0); /* Init our array return */ array_init(return_value); @@ -683,21 +682,16 @@ PHP_METHOD(RedisArray, keys) MAKE_STD_ZVAL(z_tmp); /* Call KEYS on each node */ - redis_inst = &ra->redis[i]; - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, - z_tmp, 1, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); /* Add the result for this host */ add_assoc_zval(return_value, ra->hosts[i], z_tmp); } - - /* Free arg array */ - efree(z_args[0]); } PHP_METHOD(RedisArray, getOption) { - zval *object, z_fun, *z_tmp, *z_args[1], *redis_inst; + zval *object, z_fun, *z_tmp, z_args[1]; int i; RedisArray *ra; long opt; @@ -715,8 +709,7 @@ PHP_METHOD(RedisArray, getOption) ZVAL_STRING(&z_fun, "getOption", 0); /* copy arg */ - MAKE_STD_ZVAL(z_args[0]); - ZVAL_LONG(z_args[0], opt); + ZVAL_LONG(&z_args[0], opt); array_init(return_value); for(i = 0; i < ra->count; ++i) { @@ -724,20 +717,15 @@ PHP_METHOD(RedisArray, getOption) MAKE_STD_ZVAL(z_tmp); /* Call each node in turn */ - redis_inst = &ra->redis[i]; - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, - z_tmp, 1, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); add_assoc_zval(return_value, ra->hosts[i], z_tmp); } - - /* cleanup */ - efree(z_args[0]); } PHP_METHOD(RedisArray, setOption) { - zval *object, z_fun, *z_tmp, *z_args[2], *redis_inst; + zval *object, z_fun, *z_tmp, z_args[2]; int i; RedisArray *ra; long opt; @@ -757,10 +745,8 @@ PHP_METHOD(RedisArray, setOption) ZVAL_STRING(&z_fun, "setOption", 0); /* copy args */ - MAKE_STD_ZVAL(z_args[0]); - ZVAL_LONG(z_args[0], opt); - MAKE_STD_ZVAL(z_args[1]); - ZVAL_STRINGL(z_args[1], val_str, val_len, 0); + ZVAL_LONG(&z_args[0], opt); + ZVAL_STRINGL(&z_args[1], val_str, val_len, 0); array_init(return_value); for(i = 0; i < ra->count; ++i) { @@ -768,21 +754,15 @@ PHP_METHOD(RedisArray, setOption) MAKE_STD_ZVAL(z_tmp); /* Call each node in turn */ - redis_inst = &ra->redis[i]; - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, - z_tmp, 2, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 2, z_args); add_assoc_zval(return_value, ra->hosts[i], z_tmp); } - - /* cleanup */ - efree(z_args[0]); - efree(z_args[1]); } PHP_METHOD(RedisArray, select) { - zval *object, z_fun, *z_tmp, *z_args[2], *redis_inst; + zval *object, z_fun, *z_tmp, z_args[1]; int i; RedisArray *ra; long opt; @@ -800,23 +780,17 @@ PHP_METHOD(RedisArray, select) ZVAL_STRING(&z_fun, "select", 0); /* copy args */ - MAKE_STD_ZVAL(z_args[0]); - ZVAL_LONG(z_args[0], opt); + ZVAL_LONG(&z_args[0], opt); array_init(return_value); for(i = 0; i < ra->count; ++i) { MAKE_STD_ZVAL(z_tmp); /* Call each node in turn */ - redis_inst = &ra->redis[i]; - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, - z_tmp, 1, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); add_assoc_zval(return_value, ra->hosts[i], z_tmp); } - - /* cleanup */ - efree(z_args[0]); } #define HANDLE_MULTI_EXEC(cmd) do {\ @@ -856,7 +830,7 @@ PHP_METHOD(RedisArray, mget) RedisArray *ra; int *pos, argc, *argc_each; HashTable *h_keys; - zval **argv, *redis_inst; + zval **argv; /* Multi/exec support */ HANDLE_MULTI_EXEC("MGET"); @@ -942,9 +916,7 @@ PHP_METHOD(RedisArray, mget) /* call MGET on the node */ MAKE_STD_ZVAL(z_ret); - redis_inst = &ra->redis[n]; - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, - z_ret, 1, &z_argarray TSRMLS_CC); + call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, z_ret, 1, z_argarray); /* cleanup args array */ zval_ptr_dtor(&z_argarray); @@ -1092,8 +1064,7 @@ PHP_METHOD(RedisArray, mset) } /* call */ - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, - &z_ret, 1, &z_argarray TSRMLS_CC); + call_user_function(&redis_ce->function_table, redis_inst, &z_fun, &z_ret, 1, z_argarray); if(ra->index) { ra_index_keys(z_argarray, redis_inst TSRMLS_CC); /* use SADD to add keys to node index */ @@ -1227,8 +1198,7 @@ PHP_METHOD(RedisArray, del) /* call */ MAKE_STD_ZVAL(z_ret); - call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, - z_ret, 1, &z_argarray TSRMLS_CC); + call_user_function(&redis_ce->function_table, redis_inst, &z_fun, z_ret, 1, z_argarray); if(ra->index) { ra_index_del(z_argarray, redis_inst TSRMLS_CC); /* use SREM to remove keys from node index */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 65529f0818..17c8c4ca75 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -76,7 +76,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b redis_inst = &ra->redis[i]; object_init_ex(redis_inst, redis_ce); INIT_PZVAL(redis_inst); - call_user_function(&redis_ce->function_table, &redis_inst, &z_cons, &z_ret, 0, NULL TSRMLS_CC); + call_user_function(&redis_ce->function_table, redis_inst, &z_cons, &z_ret, 0, NULL); /* create socket */ redis_sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); @@ -407,7 +407,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR /* call extraction function */ MAKE_STD_ZVAL(z_argv0); ZVAL_STRINGL(z_argv0, key, key_len, 0); - call_user_function(EG(function_table), NULL, ra->z_fun, &z_ret, 1, &z_argv0 TSRMLS_CC); + call_user_function(EG(function_table), NULL, ra->z_fun, &z_ret, 1, z_argv0); efree(z_argv0); if(Z_TYPE(z_ret) != IS_STRING) { @@ -443,7 +443,7 @@ zend_bool ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRMLS_DC) { zval z_ret; - zval *z_argv0; + zval z_argv[1]; /* check that we can call the extractor function */ if(!zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { @@ -453,10 +453,9 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML /* convert_to_string(ra->z_fun); */ /* call extraction function */ - MAKE_STD_ZVAL(z_argv0); - ZVAL_STRINGL(z_argv0, key, key_len, 0); - call_user_function(EG(function_table), NULL, ra->z_dist, &z_ret, 1, &z_argv0 TSRMLS_CC); - efree(z_argv0); + INIT_ZVAL(z_argv[0]); + ZVAL_STRINGL(&z_argv[0], key, key_len, 0); + call_user_function(EG(function_table), NULL, ra->z_dist, &z_ret, 1, z_argv); if(Z_TYPE(z_ret) != IS_LONG) { zval_dtor(&z_ret); @@ -536,42 +535,40 @@ void ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { zval z_fun_multi, z_ret; - zval *z_args[1]; + zval z_args[1]; /* run MULTI */ ZVAL_STRING(&z_fun_multi, "MULTI", 0); - MAKE_STD_ZVAL(z_args[0]); - ZVAL_LONG(z_args[0], multi_value); - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_multi, &z_ret, 1, z_args TSRMLS_CC); - efree(z_args[0]); - /* zval_dtor(&z_ret); */ + INIT_ZVAL(z_args[0]); + ZVAL_LONG(&z_args[0], multi_value); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); } static void ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { int i, argc; - zval z_fun, z_ret, **z_args; + zval z_fun, z_ret, *z_args; /* alloc */ argc = 1 + zend_hash_num_elements(Z_ARRVAL_P(z_keys)); - z_args = emalloc(argc * sizeof(zval*)); + z_args = ecalloc(argc, sizeof(zval)); /* prepare first parameters */ ZVAL_STRING(&z_fun, cmd, 0); - MAKE_STD_ZVAL(z_args[0]); - ZVAL_STRING(z_args[0], PHPREDIS_INDEX_NAME, 0); + INIT_ZVAL(z_args[0]); + ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1, 0); /* prepare keys */ for(i = 0; i < argc - 1; ++i) { - z_args[i+1] = zend_hash_index_find(Z_ARRVAL_P(z_keys), i); + INIT_ZVAL(z_args[i+1]); + z_args[i+1] = *zend_hash_index_find(Z_ARRVAL_P(z_keys), i); } /* run cmd */ - call_user_function(&redis_ce->function_table, &z_redis, &z_fun, &z_ret, argc, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); /* don't dtor z_ret, since we're returning z_redis */ - efree(z_args[0]); /* free index name zval */ efree(z_args); /* free container */ } @@ -616,23 +613,18 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { void ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { - zval z_fun_sadd, z_ret, *z_args[2]; - MAKE_STD_ZVAL(z_args[0]); - MAKE_STD_ZVAL(z_args[1]); + zval z_fun_sadd, z_ret, z_args[2]; /* prepare args */ ZVAL_STRINGL(&z_fun_sadd, "SADD", 4, 0); - ZVAL_STRING(z_args[0], PHPREDIS_INDEX_NAME, 0); - ZVAL_STRINGL(z_args[1], key, key_len, 1); + INIT_ZVAL(z_args[0]); + ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1, 0); + INIT_ZVAL(z_args[1]); + ZVAL_STRINGL(&z_args[1], key, key_len, 0); /* run SADD */ - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_sadd, &z_ret, 2, z_args TSRMLS_CC); - - /* don't dtor z_ret, since we're returning z_redis */ - efree(z_args[0]); - zval_dtor(z_args[1]); - efree(z_args[1]); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args); } void @@ -642,7 +634,7 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { /* run EXEC */ ZVAL_STRING(&z_fun_exec, "EXEC", 0); - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_exec, &z_ret, 0, NULL TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); /* extract first element of exec array and put into return_value. */ @@ -670,7 +662,7 @@ ra_index_discard(zval *z_redis, zval *return_value TSRMLS_DC) { /* run DISCARD */ ZVAL_STRING(&z_fun_discard, "DISCARD", 0); - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_discard, &z_ret, 0, NULL TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); zval_dtor(&z_ret); } @@ -682,7 +674,7 @@ ra_index_unwatch(zval *z_redis, zval *return_value TSRMLS_DC) { /* run UNWATCH */ ZVAL_STRING(&z_fun_unwatch, "UNWATCH", 0); - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_unwatch, &z_ret, 0, NULL TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); zval_dtor(&z_ret); } @@ -715,13 +707,14 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con int key_len; /* arg */ - MAKE_STD_ZVAL(z_arg); - ZVAL_STRING(z_arg, arg, 0); + MAKE_STD_ZVAL(z_arg); + ZVAL_STRING(z_arg, arg, 1); /* run SMEMBERS */ ZVAL_STRING(&z_fun_smembers, cmd, 0); - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_smembers, &z_ret, 1, &z_arg TSRMLS_CC); - efree(z_arg); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_smembers, &z_ret, 1, z_arg); + zval_dtor(z_arg); + efree(z_arg); if(Z_TYPE(z_ret) != IS_ARRAY) { /* failure */ return -1; /* TODO: log error. */ } @@ -758,23 +751,22 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long zval *z_data; long success = 1; - MAKE_STD_ZVAL(z_arg); /* Pipelined */ ra_index_multi(z_from, PIPELINE TSRMLS_CC); /* prepare args */ - ZVAL_STRINGL(&z_fun_type, "TYPE", 4, 0); + MAKE_STD_ZVAL(z_arg); ZVAL_STRINGL(z_arg, key, key_len, 0); + + ZVAL_STRINGL(&z_fun_type, "TYPE", 4, 0); /* run TYPE */ - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_type, &z_ret, 1, &z_arg TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); ZVAL_STRINGL(&z_fun_type, "TTL", 3, 0); - ZVAL_STRINGL(z_arg, key, key_len, 0); /* run TYPE */ - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_type, &z_ret, 1, &z_arg TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); - /* cleanup */ - efree(z_arg); + efree(z_arg); /* Get the result from the pipeline. */ ra_index_exec(z_from, &z_ret, 1 TSRMLS_CC); @@ -800,20 +792,23 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long static void ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { - zval z_fun_srem, z_ret, *z_args[2]; + zval z_fun_srem, z_ret, *z_args; + + z_args = ecalloc(2, sizeof(zval)); + INIT_ZVAL(z_args[0]); + INIT_ZVAL(z_args[1]); /* run SREM on source index */ ZVAL_STRINGL(&z_fun_srem, "SREM", 4, 0); - MAKE_STD_ZVAL(z_args[0]); - ZVAL_STRING(z_args[0], PHPREDIS_INDEX_NAME, 0); - MAKE_STD_ZVAL(z_args[1]); - ZVAL_STRINGL(z_args[1], key, key_len, 0); + ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1, 1); + ZVAL_STRINGL(&z_args[1], key, key_len, 1); - call_user_function(&redis_ce->function_table, &z_redis, &z_fun_srem, &z_ret, 2, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args); - /* cleanup */ - efree(z_args[0]); - efree(z_args[1]); + /* cleanup */ + zval_dtor(&z_args[0]); + zval_dtor(&z_args[1]); + efree(z_args); } @@ -830,7 +825,7 @@ ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { MAKE_STD_ZVAL(z_args); ZVAL_STRINGL(&z_fun_del, "DEL", 3, 0); ZVAL_STRINGL(z_args, key, key_len, 0); - call_user_function(&redis_ce->function_table, &z_from, &z_fun_del, &z_ret, 1, &z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); efree(z_args); /* remove key from index */ @@ -845,20 +840,15 @@ ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { static zend_bool ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_expire, z_ret, *z_args[2]; + zval z_fun_expire, z_ret, z_args[2]; if (ttl > 0) { /* run EXPIRE on target */ - MAKE_STD_ZVAL(z_args[0]); - MAKE_STD_ZVAL(z_args[1]); ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6, 0); - ZVAL_STRINGL(z_args[0], key, key_len, 0); - ZVAL_LONG(z_args[1], ttl); - call_user_function(&redis_ce->function_table, &z_to, &z_fun_expire, &z_ret, 2, z_args TSRMLS_CC); - /* cleanup */ - efree(z_args[0]); - efree(z_args[1]); + ZVAL_STRINGL(&z_args[0], key, key_len, 0); + ZVAL_LONG(&z_args[1], ttl); + call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); } return 1; @@ -867,7 +857,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { static zend_bool ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_zrange, z_fun_zadd, z_ret, z_ret_dest, *z_args[4], **z_zadd_args, *z_score_p; + zval z_fun_zrange, z_fun_zadd, z_ret, z_ret_dest, z_args[4], *z_zadd_args, *z_score_p; int i, count; HashTable *h_zset_vals; zend_string *zkey; @@ -876,18 +866,13 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run ZRANGE key 0 -1 WITHSCORES on source */ ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6, 0); for(i = 0; i < 4; ++i) { - MAKE_STD_ZVAL(z_args[i]); - } - ZVAL_STRINGL(z_args[0], key, key_len, 0); - ZVAL_STRINGL(z_args[1], "0", 1, 0); - ZVAL_STRINGL(z_args[2], "-1", 2, 0); - ZVAL_BOOL(z_args[3], 1); - call_user_function(&redis_ce->function_table, &z_from, &z_fun_zrange, &z_ret, 4, z_args TSRMLS_CC); - - /* cleanup zrange args */ - for(i = 0; i < 4; ++i) { - efree(z_args[i]); /* FIXME */ + INIT_ZVAL(z_args[i]); } + ZVAL_STRINGL(&z_args[0], key, key_len, 0); + ZVAL_STRINGL(&z_args[1], "0", 1, 0); + ZVAL_STRINGL(&z_args[2], "-1", 2, 0); + ZVAL_BOOL(&z_args[3], 1); + call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ @@ -899,39 +884,35 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* allocate argument array for ZADD */ count = zend_hash_num_elements(h_zset_vals); - z_zadd_args = emalloc((1 + 2*count) * sizeof(zval*)); + z_zadd_args = ecalloc((1 + 2*count), sizeof(zval)); + + INIT_ZVAL(z_zadd_args[0]); + ZVAL_STRINGL(&z_zadd_args[0], key, key_len, 0); i = 1; ZEND_HASH_FOREACH_KEY_VAL(h_zset_vals, idx, zkey, z_score_p) { /* add score */ - convert_to_double(z_score_p); - MAKE_STD_ZVAL(z_zadd_args[i]); - ZVAL_DOUBLE(z_zadd_args[i], Z_DVAL_P(z_score_p)); + INIT_ZVAL(z_zadd_args[i]); + ZVAL_DOUBLE(&z_zadd_args[i], Z_DVAL_P(z_score_p)); /* add value */ - MAKE_STD_ZVAL(z_zadd_args[i+1]); + INIT_ZVAL(z_zadd_args[i+1]); if (zkey) { - ZVAL_STRINGL(z_zadd_args[i+1], zkey->val, zkey->len - 1, 0); + ZVAL_STRINGL(&z_zadd_args[i+1], zkey->val, zkey->len - 1, 0); } else { - ZVAL_LONG(z_zadd_args[i+1], (long)idx); + ZVAL_LONG(&z_zadd_args[i+1], (long)idx); } i += 2; } ZEND_HASH_FOREACH_END(); /* run ZADD on target */ ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4, 0); - MAKE_STD_ZVAL(z_zadd_args[0]); - ZVAL_STRINGL(z_zadd_args[0], key, key_len, 0); - call_user_function(&redis_ce->function_table, &z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); /* cleanup */ - for(i = 0; i < 1 + 2 * count; ++i) { - efree(z_zadd_args[i]); - } - zval_dtor(&z_ret); /* Free the array itself */ @@ -943,79 +924,65 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS static zend_bool ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_get, z_fun_set, z_ret, *z_args[3]; + zval z_fun_get, z_fun_set, z_ret, z_args[3]; /* run GET on source */ - MAKE_STD_ZVAL(z_args[0]); ZVAL_STRINGL(&z_fun_get, "GET", 3, 0); - ZVAL_STRINGL(z_args[0], key, key_len, 0); - call_user_function(&redis_ce->function_table, &z_from, &z_fun_get, &z_ret, 1, z_args TSRMLS_CC); + ZVAL_STRINGL(&z_args[0], key, key_len, 0); + call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args); if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ /* TODO: report? */ - efree(z_args[0]); return 0; } /* run SET on target */ - MAKE_STD_ZVAL(z_args[1]); if (ttl > 0) { - MAKE_STD_ZVAL(z_args[2]); - ZVAL_STRINGL(&z_fun_set, "SETEX", 5, 0); - ZVAL_STRINGL(z_args[0], key, key_len, 0); - ZVAL_LONG(z_args[1], ttl); - ZVAL_STRINGL(z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */ + ZVAL_STRINGL(&z_fun_set, "SETEX", 5, 0); + ZVAL_STRINGL(&z_args[0], key, key_len, 0); + ZVAL_LONG(&z_args[1], ttl); + ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous call */ - call_user_function(&redis_ce->function_table, &z_to, &z_fun_set, &z_ret, 3, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args); /* cleanup */ - efree(z_args[1]); - zval_dtor(z_args[2]); - efree(z_args[2]); - } - else { - ZVAL_STRINGL(&z_fun_set, "SET", 3, 0); - ZVAL_STRINGL(z_args[0], key, key_len, 0); - ZVAL_STRINGL(z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */ + zval_dtor(&z_args[2]); + } else { + ZVAL_STRINGL(&z_fun_set, "SET", 3, 0); + ZVAL_STRINGL(&z_args[0], key, key_len, 0); + ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous return value */ - call_user_function(&redis_ce->function_table, &z_to, &z_fun_set, &z_ret, 2, z_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args); /* cleanup */ - zval_dtor(z_args[1]); - efree(z_args[1]); + zval_dtor(&z_args[1]); } - /* cleanup */ - efree(z_args[0]); - return 1; } static zend_bool ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_hgetall, z_fun_hmset, z_ret, z_ret_dest, *z_args[2]; + zval z_fun_hgetall, z_fun_hmset, z_ret, z_ret_dest, z_args[2]; /* run HGETALL on source */ - MAKE_STD_ZVAL(z_args[0]); ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7, 0); - ZVAL_STRINGL(z_args[0], key, key_len, 0); - call_user_function(&redis_ce->function_table, &z_from, &z_fun_hgetall, &z_ret, 1, z_args TSRMLS_CC); + ZVAL_STRINGL(&z_args[0], key, key_len, 0); + call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_ret, 1, z_args); if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ - efree(z_args[0]); return 0; } /* run HMSET on target */ ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5, 0); - ZVAL_STRINGL(z_args[0], key, key_len, 0); - z_args[1] = &z_ret; /* copy z_ret to arg 1 */ - call_user_function(&redis_ce->function_table, &z_to, &z_fun_hmset, &z_ret_dest, 2, z_args TSRMLS_CC); + ZVAL_STRINGL(&z_args[0], key, key_len, 0); + z_args[1] = z_ret; /* copy z_ret to arg 1 */ + call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); /* cleanup */ - efree(z_args[0]); zval_dtor(&z_ret); return 1; @@ -1026,29 +993,29 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, int list_count, const char **cmd_list, int add_count, const char **cmd_add, long ttl TSRMLS_DC) { - zval z_fun_retrieve, z_fun_sadd, z_ret, **z_retrieve_args, **z_sadd_args, *z_data_p; + zval z_fun_retrieve, z_fun_sadd, z_ret, *z_retrieve_args, *z_sadd_args, *z_data_p; int count, i; HashTable *h_set_vals; /* run retrieval command on source */ - z_retrieve_args = emalloc((1+list_count) * sizeof(zval*)); ZVAL_STRING(&z_fun_retrieve, cmd_list[0], 0); /* set the command */ + z_retrieve_args = ecalloc(list_count, sizeof(zval)); /* set the key */ - MAKE_STD_ZVAL(z_retrieve_args[0]); - ZVAL_STRINGL(z_retrieve_args[0], key, key_len, 0); + INIT_ZVAL(z_retrieve_args[0]); + ZVAL_STRINGL(&z_retrieve_args[0], key, key_len, 1); /* possibly add some other args if they were provided. */ for(i = 1; i < list_count; ++i) { - MAKE_STD_ZVAL(z_retrieve_args[i]); - ZVAL_STRING(z_retrieve_args[i], cmd_list[i], 0); + INIT_ZVAL(z_retrieve_args[i]); + ZVAL_STRING(&z_retrieve_args[i], cmd_list[i], 1); } - call_user_function(&redis_ce->function_table, &z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args); /* cleanup */ for(i = 0; i < list_count; ++i) { - efree(z_retrieve_args[i]); + zval_dtor(&z_retrieve_args[i]); } efree(z_retrieve_args); @@ -1059,41 +1026,33 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, /* run SADD/RPUSH on target */ h_set_vals = Z_ARRVAL(z_ret); - count = zend_hash_num_elements(h_set_vals); - z_sadd_args = emalloc((1 + count) * sizeof(zval*)); + count = 1 + zend_hash_num_elements(h_set_vals); ZVAL_STRING(&z_fun_sadd, cmd_add[0], 0); - MAKE_STD_ZVAL(z_sadd_args[0]); /* add key */ - ZVAL_STRINGL(z_sadd_args[0], key, key_len, 0); - - for(i = 0, zend_hash_internal_pointer_reset(h_set_vals); - zend_hash_has_more_elements(h_set_vals) == SUCCESS; - zend_hash_move_forward(h_set_vals), i++) { - - if ((z_data_p = zend_hash_get_current_data(h_set_vals)) == NULL) { - continue; - } + z_sadd_args = ecalloc(count, sizeof(zval)); + INIT_ZVAL(z_sadd_args[0]); + ZVAL_STRINGL(&z_sadd_args[0], key, key_len, 1); - /* add set elements */ - MAKE_STD_ZVAL(z_sadd_args[i+1]); - *(z_sadd_args[i+1]) = *z_data_p; - zval_copy_ctor(z_sadd_args[i+1]); - } + i = 1; + ZEND_HASH_FOREACH_VAL(h_set_vals, z_data_p) { + /* add set elements */ + INIT_ZVAL(z_sadd_args[i]); + z_sadd_args[i] = *z_data_p; + zval_copy_ctor(&z_sadd_args[i]); + i++; + } ZEND_HASH_FOREACH_END(); /* Clean up our input return value */ zval_dtor(&z_ret); - call_user_function(&redis_ce->function_table, &z_to, &z_fun_sadd, &z_ret, count+1, z_sadd_args TSRMLS_CC); + call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); /* cleanup */ - efree(z_sadd_args[0]); /* no dtor at [0] */ - - for(i = 0; i < count; ++i) { - zval_dtor(z_sadd_args[i + 1]); - efree(z_sadd_args[i + 1]); - } + for (i = 0; i < count; i++) { + zval_dtor(&z_sadd_args[i]); + } efree(z_sadd_args); /* Clean up our output return value */ From a2cda39fdc3136feee4137be77aeb6bcf33c1b8e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 23 Oct 2016 21:10:33 +0300 Subject: [PATCH 0598/1986] RedisArray refactoring --- redis_array.c | 14 +++++++------- redis_array_impl.c | 44 +++++++++++++++++++------------------------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/redis_array.c b/redis_array.c index 22f7509f54..2036d4b02d 100644 --- a/redis_array.c +++ b/redis_array.c @@ -668,7 +668,7 @@ PHP_METHOD(RedisArray, keys) } /* Set up our function call (KEYS) */ - ZVAL_STRING(&z_fun, "KEYS", 0); + ZVAL_STRINGL(&z_fun, "KEYS", 4, 0); /* We will be passing with one string argument (the pattern) */ ZVAL_STRINGL(z_args, pattern, pattern_len, 0); @@ -706,7 +706,7 @@ PHP_METHOD(RedisArray, getOption) } /* prepare call */ - ZVAL_STRING(&z_fun, "getOption", 0); + ZVAL_STRINGL(&z_fun, "getOption", 9, 0); /* copy arg */ ZVAL_LONG(&z_args[0], opt); @@ -742,7 +742,7 @@ PHP_METHOD(RedisArray, setOption) } /* prepare call */ - ZVAL_STRING(&z_fun, "setOption", 0); + ZVAL_STRINGL(&z_fun, "setOption", 9, 0); /* copy args */ ZVAL_LONG(&z_args[0], opt); @@ -777,7 +777,7 @@ PHP_METHOD(RedisArray, select) } /* prepare call */ - ZVAL_STRING(&z_fun, "select", 0); + ZVAL_STRINGL(&z_fun, "select", 6, 0); /* copy args */ ZVAL_LONG(&z_args[0], opt); @@ -845,7 +845,7 @@ PHP_METHOD(RedisArray, mget) } /* prepare call */ - ZVAL_STRING(&z_fun, "MGET", 0); + ZVAL_STRINGL(&z_fun, "MGET", 4, 0); /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); @@ -1057,7 +1057,7 @@ PHP_METHOD(RedisArray, mset) } /* prepare call */ - ZVAL_STRING(&z_fun, "MSET", 0); + ZVAL_STRINGL(&z_fun, "MSET", 4, 0); redis_inst = &ra->redis[n]; if(ra->index) { /* add MULTI */ ra_index_multi(redis_inst, MULTI TSRMLS_CC); @@ -1138,7 +1138,7 @@ PHP_METHOD(RedisArray, del) } /* prepare call */ - ZVAL_STRING(&z_fun, "DEL", 0); + ZVAL_STRINGL(&z_fun, "DEL", 3, 0); /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); diff --git a/redis_array_impl.c b/redis_array_impl.c index 17c8c4ca75..a700e9c01c 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -39,7 +39,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b RedisSock *redis_sock = NULL; /* function calls on the Redis object */ - ZVAL_STRING(&z_cons, "__construct", 0); + ZVAL_STRINGL(&z_cons, "__construct", 11, 0); /* init connections */ for (zend_hash_internal_pointer_reset(hosts); zend_hash_has_more_elements(hosts) == SUCCESS; zend_hash_move_forward(hosts)) @@ -394,8 +394,7 @@ char * ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS_DC) { char *out; - zval z_ret; - zval *z_argv0; + zval z_ret, z_argv[1]; /* check that we can call the extractor function */ if(!zend_is_callable_ex(ra->z_fun, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { @@ -405,10 +404,9 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR /* convert_to_string(ra->z_fun); */ /* call extraction function */ - MAKE_STD_ZVAL(z_argv0); - ZVAL_STRINGL(z_argv0, key, key_len, 0); - call_user_function(EG(function_table), NULL, ra->z_fun, &z_ret, 1, z_argv0); - efree(z_argv0); + INIT_ZVAL(z_argv[0]); + ZVAL_STRINGL(&z_argv[0], key, key_len, 0); + call_user_function(EG(function_table), NULL, ra->z_fun, &z_ret, 1, z_argv); if(Z_TYPE(z_ret) != IS_STRING) { zval_dtor(&z_ret); @@ -538,7 +536,7 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { zval z_args[1]; /* run MULTI */ - ZVAL_STRING(&z_fun_multi, "MULTI", 0); + ZVAL_STRINGL(&z_fun_multi, "MULTI", 5, 0); INIT_ZVAL(z_args[0]); ZVAL_LONG(&z_args[0], multi_value); call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); @@ -633,7 +631,7 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { zval z_fun_exec, z_ret, *zp_tmp; /* run EXEC */ - ZVAL_STRING(&z_fun_exec, "EXEC", 0); + ZVAL_STRINGL(&z_fun_exec, "EXEC", 4, 0); call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); @@ -661,7 +659,7 @@ ra_index_discard(zval *z_redis, zval *return_value TSRMLS_DC) { zval z_fun_discard, z_ret; /* run DISCARD */ - ZVAL_STRING(&z_fun_discard, "DISCARD", 0); + ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7, 0); call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); zval_dtor(&z_ret); @@ -673,7 +671,7 @@ ra_index_unwatch(zval *z_redis, zval *return_value TSRMLS_DC) { zval z_fun_unwatch, z_ret; /* run UNWATCH */ - ZVAL_STRING(&z_fun_unwatch, "UNWATCH", 0); + ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7, 0); call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); zval_dtor(&z_ret); @@ -701,20 +699,19 @@ static long ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, const char *arg TSRMLS_DC) { long count, i; - zval z_fun_smembers, z_ret, *z_arg, *z_data_p; + zval z_fun_smembers, z_ret, z_arg[1], *z_data_p; HashTable *h_keys; char *key; int key_len; /* arg */ - MAKE_STD_ZVAL(z_arg); - ZVAL_STRING(z_arg, arg, 1); + INIT_ZVAL(z_arg[0]); + ZVAL_STRING(&z_arg[0], arg, 1); /* run SMEMBERS */ ZVAL_STRING(&z_fun_smembers, cmd, 0); call_user_function(&redis_ce->function_table, z_redis, &z_fun_smembers, &z_ret, 1, z_arg); - zval_dtor(z_arg); - efree(z_arg); + zval_dtor(&z_arg[0]); if(Z_TYPE(z_ret) != IS_ARRAY) { /* failure */ return -1; /* TODO: log error. */ } @@ -747,7 +744,7 @@ static zend_bool ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long *res TSRMLS_DC) { int i; - zval z_fun_type, z_ret, *z_arg; + zval z_fun_type, z_ret, z_arg[1]; zval *z_data; long success = 1; @@ -755,8 +752,8 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long ra_index_multi(z_from, PIPELINE TSRMLS_CC); /* prepare args */ - MAKE_STD_ZVAL(z_arg); - ZVAL_STRINGL(z_arg, key, key_len, 0); + INIT_ZVAL(z_arg[0]); + ZVAL_STRINGL(&z_arg[0], key, key_len, 0); ZVAL_STRINGL(&z_fun_type, "TYPE", 4, 0); /* run TYPE */ @@ -766,8 +763,6 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long /* run TYPE */ call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); - efree(z_arg); - /* Get the result from the pipeline. */ ra_index_exec(z_from, &z_ret, 1 TSRMLS_CC); if(Z_TYPE(z_ret) == IS_ARRAY) { @@ -816,17 +811,16 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { static zend_bool ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { - zval z_fun_del, z_ret, *z_args; + zval z_fun_del, z_ret, z_args[1]; /* in a transaction */ ra_index_multi(z_from, MULTI TSRMLS_CC); /* run DEL on source */ - MAKE_STD_ZVAL(z_args); + INIT_ZVAL(z_args[0]); ZVAL_STRINGL(&z_fun_del, "DEL", 3, 0); - ZVAL_STRINGL(z_args, key, key_len, 0); + ZVAL_STRINGL(&z_args[0], key, key_len, 0); call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); - efree(z_args); /* remove key from index */ ra_remove_from_index(z_from, key, key_len TSRMLS_CC); From 750ec8bbdc58b243171b88fcfb1c37d337890780 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 23 Oct 2016 23:04:59 +0300 Subject: [PATCH 0599/1986] WIP: php7 compatibility Redefine ZVAL_STRING + ZVAL_STRINGL macro --- common.h | 17 ++++- library.c | 6 +- redis_array.c | 35 ++++++---- redis_array_impl.c | 158 ++++++++++++++++++++++++++++----------------- 4 files changed, 140 insertions(+), 76 deletions(-) diff --git a/common.h b/common.h index 8941493176..205470d4a3 100644 --- a/common.h +++ b/common.h @@ -170,12 +170,25 @@ typedef zend_rsrc_list_entry zend_resource; static int (*_add_next_index_stringl)(zval *, const char *, uint, int) = &add_next_index_stringl; #define add_next_index_stringl(arg, str, length) _add_next_index_stringl(arg, str, length, 1); +#undef ZVAL_STRING +#define ZVAL_STRING(z, s) do { \ + const char *_s=(s); \ + ZVAL_STRINGL(z, _s, strlen(_s)); \ +} while (0) #undef RETVAL_STRING -#define RETVAL_STRING(s) ZVAL_STRING(return_value, s, 1) +#define RETVAL_STRING(s) ZVAL_STRING(return_value, s) #undef RETURN_STRING #define RETURN_STRING(s) { RETVAL_STRING(s); return; } +#undef ZVAL_STRINGL +#define ZVAL_STRINGL(z, s, l) do { \ + const char *__s=(s); int __l=l; \ + zval *__z = (z); \ + Z_STRLEN_P(__z) = __l; \ + Z_STRVAL_P(__z) = estrndup(__s, __l); \ + Z_TYPE_P(__z) = IS_STRING; \ +} while(0) #undef RETVAL_STRINGL -#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l, 1) +#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l) #undef RETURN_STRINGL #define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; } diff --git a/library.c b/library.c index b382abfd2c..99aa737aa9 100644 --- a/library.c +++ b/library.c @@ -2286,10 +2286,10 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret if(bulk_resp == NULL) { ZVAL_FALSE(*z_ret); return -1; - } else { - ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); - return 0; } + ZVAL_STRINGL(*z_ret, bulk_resp, size); + efree(bulk_resp); + return 0; } PHP_REDIS_API int diff --git a/redis_array.c b/redis_array.c index 2036d4b02d..bcfe7c9ab9 100644 --- a/redis_array.c +++ b/redis_array.c @@ -377,7 +377,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i } /* pass call through */ - ZVAL_STRING(&z_fun, cmd, 0); /* method name */ + ZVAL_STRING(&z_fun, cmd); /* method name */ z_callargs = ecalloc(argc + 1, sizeof(zval)); /* copy args to array */ @@ -391,6 +391,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* multi/exec */ if(ra->z_multi_exec) { call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); + zval_dtor(&z_fun); efree(z_callargs); RETURN_ZVAL(getThis(), 1, 0); } @@ -425,6 +426,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i } /* cleanup */ + zval_dtor(&z_fun); efree(z_callargs); } @@ -603,7 +605,7 @@ static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *metho } /* prepare call */ - ZVAL_STRING(&z_fun, method_name, 0); + ZVAL_STRING(&z_fun, method_name); array_init(return_value); for(i = 0; i < ra->count; ++i) { @@ -615,6 +617,7 @@ static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *metho add_assoc_zval(return_value, ra->hosts[i], z_tmp); } + zval_dtor(&z_fun); } PHP_METHOD(RedisArray, info) @@ -668,10 +671,10 @@ PHP_METHOD(RedisArray, keys) } /* Set up our function call (KEYS) */ - ZVAL_STRINGL(&z_fun, "KEYS", 4, 0); + ZVAL_STRINGL(&z_fun, "KEYS", 4); /* We will be passing with one string argument (the pattern) */ - ZVAL_STRINGL(z_args, pattern, pattern_len, 0); + ZVAL_STRINGL(z_args, pattern, pattern_len); /* Init our array return */ array_init(return_value); @@ -687,6 +690,8 @@ PHP_METHOD(RedisArray, keys) /* Add the result for this host */ add_assoc_zval(return_value, ra->hosts[i], z_tmp); } + zval_dtor(&z_args[0]); + zval_dtor(&z_fun); } PHP_METHOD(RedisArray, getOption) @@ -706,7 +711,7 @@ PHP_METHOD(RedisArray, getOption) } /* prepare call */ - ZVAL_STRINGL(&z_fun, "getOption", 9, 0); + ZVAL_STRINGL(&z_fun, "getOption", 9); /* copy arg */ ZVAL_LONG(&z_args[0], opt); @@ -721,6 +726,7 @@ PHP_METHOD(RedisArray, getOption) add_assoc_zval(return_value, ra->hosts[i], z_tmp); } + zval_dtor(&z_fun); } PHP_METHOD(RedisArray, setOption) @@ -742,11 +748,11 @@ PHP_METHOD(RedisArray, setOption) } /* prepare call */ - ZVAL_STRINGL(&z_fun, "setOption", 9, 0); + ZVAL_STRINGL(&z_fun, "setOption", 9); /* copy args */ ZVAL_LONG(&z_args[0], opt); - ZVAL_STRINGL(&z_args[1], val_str, val_len, 0); + ZVAL_STRINGL(&z_args[1], val_str, val_len); array_init(return_value); for(i = 0; i < ra->count; ++i) { @@ -758,6 +764,8 @@ PHP_METHOD(RedisArray, setOption) add_assoc_zval(return_value, ra->hosts[i], z_tmp); } + zval_dtor(&z_args[1]); + zval_dtor(&z_fun); } PHP_METHOD(RedisArray, select) @@ -777,7 +785,7 @@ PHP_METHOD(RedisArray, select) } /* prepare call */ - ZVAL_STRINGL(&z_fun, "select", 6, 0); + ZVAL_STRINGL(&z_fun, "select", 6); /* copy args */ ZVAL_LONG(&z_args[0], opt); @@ -791,6 +799,7 @@ PHP_METHOD(RedisArray, select) add_assoc_zval(return_value, ra->hosts[i], z_tmp); } + zval_dtor(&z_fun); } #define HANDLE_MULTI_EXEC(cmd) do {\ @@ -845,7 +854,7 @@ PHP_METHOD(RedisArray, mget) } /* prepare call */ - ZVAL_STRINGL(&z_fun, "MGET", 4, 0); + ZVAL_STRINGL(&z_fun, "MGET", 4); /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); @@ -928,6 +937,7 @@ PHP_METHOD(RedisArray, mget) zval_dtor(z_ret); efree(z_ret); zval_ptr_dtor(&z_tmp_array); + zval_dtor(&z_fun); efree(pos); efree(argc_each); @@ -962,6 +972,7 @@ PHP_METHOD(RedisArray, mget) /* cleanup */ zval_ptr_dtor(&z_tmp_array); + zval_dtor(&z_fun); efree(argv); efree(pos); efree(argc_each); @@ -1057,7 +1068,7 @@ PHP_METHOD(RedisArray, mset) } /* prepare call */ - ZVAL_STRINGL(&z_fun, "MSET", 4, 0); + ZVAL_STRINGL(&z_fun, "MSET", 4); redis_inst = &ra->redis[n]; if(ra->index) { /* add MULTI */ ra_index_multi(redis_inst, MULTI TSRMLS_CC); @@ -1071,6 +1082,7 @@ PHP_METHOD(RedisArray, mset) ra_index_exec(redis_inst, NULL, 0 TSRMLS_CC); /* run EXEC */ } + zval_dtor(&z_fun); zval_dtor(&z_ret); zval_ptr_dtor(&z_argarray); @@ -1138,7 +1150,7 @@ PHP_METHOD(RedisArray, del) } /* prepare call */ - ZVAL_STRINGL(&z_fun, "DEL", 3, 0); + ZVAL_STRINGL(&z_fun, "DEL", 3); /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); @@ -1216,6 +1228,7 @@ PHP_METHOD(RedisArray, del) } /* cleanup */ + zval_dtor(&z_fun); efree(argv); efree(pos); efree(argc_each); diff --git a/redis_array_impl.c b/redis_array_impl.c index a700e9c01c..44738d310e 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -39,13 +39,14 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b RedisSock *redis_sock = NULL; /* function calls on the Redis object */ - ZVAL_STRINGL(&z_cons, "__construct", 11, 0); + ZVAL_STRINGL(&z_cons, "__construct", 11); /* init connections */ for (zend_hash_internal_pointer_reset(hosts); zend_hash_has_more_elements(hosts) == SUCCESS; zend_hash_move_forward(hosts)) { if ((zpData = zend_hash_get_current_data(hosts)) == NULL || Z_TYPE_P(zpData) != IS_STRING) { + zval_dtor(&z_cons); for(i=0;icount;i++) { zval_dtor(&ra->redis[i]); efree(ra->hosts[i]); @@ -102,6 +103,8 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b ra->count = ++i; } + zval_dtor(&z_cons); + return ra; } @@ -405,10 +408,11 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR /* call extraction function */ INIT_ZVAL(z_argv[0]); - ZVAL_STRINGL(&z_argv[0], key, key_len, 0); + ZVAL_STRINGL(&z_argv[0], key, key_len); call_user_function(EG(function_table), NULL, ra->z_fun, &z_ret, 1, z_argv); if(Z_TYPE(z_ret) != IS_STRING) { + zval_dtor(&z_argv[0]); zval_dtor(&z_ret); return NULL; } @@ -416,6 +420,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR *out_len = Z_STRLEN(z_ret); out = estrndup(Z_STRVAL(z_ret), *out_len); + zval_dtor(&z_argv[0]); zval_dtor(&z_ret); return out; } @@ -452,15 +457,17 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML /* call extraction function */ INIT_ZVAL(z_argv[0]); - ZVAL_STRINGL(&z_argv[0], key, key_len, 0); + ZVAL_STRINGL(&z_argv[0], key, key_len); call_user_function(EG(function_table), NULL, ra->z_dist, &z_ret, 1, z_argv); if(Z_TYPE(z_ret) != IS_LONG) { + zval_dtor(&z_argv[0]); zval_dtor(&z_ret); return 0; } *pos = Z_LVAL(z_ret); + zval_dtor(&z_argv[0]); zval_dtor(&z_ret); return 1; } @@ -536,10 +543,11 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { zval z_args[1]; /* run MULTI */ - ZVAL_STRINGL(&z_fun_multi, "MULTI", 5, 0); + ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); INIT_ZVAL(z_args[0]); ZVAL_LONG(&z_args[0], multi_value); call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); + zval_dtor(&z_fun_multi); } static void @@ -553,9 +561,9 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { z_args = ecalloc(argc, sizeof(zval)); /* prepare first parameters */ - ZVAL_STRING(&z_fun, cmd, 0); + ZVAL_STRING(&z_fun, cmd); INIT_ZVAL(z_args[0]); - ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1, 0); + ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); /* prepare keys */ for(i = 0; i < argc - 1; ++i) { @@ -567,6 +575,8 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); /* don't dtor z_ret, since we're returning z_redis */ + zval_dtor(&z_args[0]); + zval_dtor(&z_fun); efree(z_args); /* free container */ } @@ -594,7 +604,7 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { MAKE_STD_ZVAL(z_new); if (zkey) { - ZVAL_STRINGL(z_new, zkey->val, zkey->len, 1); + ZVAL_STRINGL(z_new, zkey->val, zkey->len); } else { ZVAL_LONG(z_new, idx); } @@ -614,15 +624,18 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { zval z_fun_sadd, z_ret, z_args[2]; /* prepare args */ - ZVAL_STRINGL(&z_fun_sadd, "SADD", 4, 0); + ZVAL_STRINGL(&z_fun_sadd, "SADD", 4); INIT_ZVAL(z_args[0]); - ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1, 0); + ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); INIT_ZVAL(z_args[1]); - ZVAL_STRINGL(&z_args[1], key, key_len, 0); + ZVAL_STRINGL(&z_args[1], key, key_len); /* run SADD */ call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args); + zval_dtor(&z_fun_sadd); + zval_dtor(&z_args[1]); + zval_dtor(&z_args[0]); } void @@ -631,9 +644,9 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { zval z_fun_exec, z_ret, *zp_tmp; /* run EXEC */ - ZVAL_STRINGL(&z_fun_exec, "EXEC", 4, 0); + ZVAL_STRINGL(&z_fun_exec, "EXEC", 4); call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); - + zval_dtor(&z_fun_exec); /* extract first element of exec array and put into return_value. */ if(Z_TYPE(z_ret) == IS_ARRAY) { @@ -659,9 +672,10 @@ ra_index_discard(zval *z_redis, zval *return_value TSRMLS_DC) { zval z_fun_discard, z_ret; /* run DISCARD */ - ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7, 0); + ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7); call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); + zval_dtor(&z_fun_discard); zval_dtor(&z_ret); } @@ -671,9 +685,10 @@ ra_index_unwatch(zval *z_redis, zval *return_value TSRMLS_DC) { zval z_fun_unwatch, z_ret; /* run UNWATCH */ - ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7, 0); + ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7); call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); + zval_dtor(&z_fun_unwatch); zval_dtor(&z_ret); } @@ -706,11 +721,12 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con /* arg */ INIT_ZVAL(z_arg[0]); - ZVAL_STRING(&z_arg[0], arg, 1); + ZVAL_STRING(&z_arg[0], arg); /* run SMEMBERS */ - ZVAL_STRING(&z_fun_smembers, cmd, 0); + ZVAL_STRING(&z_fun_smembers, cmd); call_user_function(&redis_ce->function_table, z_redis, &z_fun_smembers, &z_ret, 1, z_arg); + zval_dtor(&z_fun_smembers); zval_dtor(&z_arg[0]); if(Z_TYPE(z_ret) != IS_ARRAY) { /* failure */ return -1; /* TODO: log error. */ @@ -753,15 +769,17 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long /* prepare args */ INIT_ZVAL(z_arg[0]); - ZVAL_STRINGL(&z_arg[0], key, key_len, 0); + ZVAL_STRINGL(&z_arg[0], key, key_len); - ZVAL_STRINGL(&z_fun_type, "TYPE", 4, 0); + ZVAL_STRINGL(&z_fun_type, "TYPE", 4); /* run TYPE */ call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); + zval_dtor(&z_fun_type); - ZVAL_STRINGL(&z_fun_type, "TTL", 3, 0); + ZVAL_STRINGL(&z_fun_type, "TTL", 3); /* run TYPE */ call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); + zval_dtor(&z_fun_type); /* Get the result from the pipeline. */ ra_index_exec(z_from, &z_ret, 1 TSRMLS_CC); @@ -779,6 +797,7 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long res[i++] = Z_LVAL_P(z_data); } } + zval_dtor(&z_arg[0]); zval_dtor(&z_ret); return success; } @@ -787,23 +806,22 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long static void ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { - zval z_fun_srem, z_ret, *z_args; + zval z_fun_srem, z_ret, z_args[2]; - z_args = ecalloc(2, sizeof(zval)); INIT_ZVAL(z_args[0]); INIT_ZVAL(z_args[1]); /* run SREM on source index */ - ZVAL_STRINGL(&z_fun_srem, "SREM", 4, 0); - ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1, 1); - ZVAL_STRINGL(&z_args[1], key, key_len, 1); + ZVAL_STRINGL(&z_fun_srem, "SREM", 4); + ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); + ZVAL_STRINGL(&z_args[1], key, key_len); call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args); /* cleanup */ - zval_dtor(&z_args[0]); + zval_dtor(&z_fun_srem); zval_dtor(&z_args[1]); - efree(z_args); + zval_dtor(&z_args[0]); } @@ -818,9 +836,11 @@ ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { /* run DEL on source */ INIT_ZVAL(z_args[0]); - ZVAL_STRINGL(&z_fun_del, "DEL", 3, 0); - ZVAL_STRINGL(&z_args[0], key, key_len, 0); + ZVAL_STRINGL(&z_fun_del, "DEL", 3); + ZVAL_STRINGL(&z_args[0], key, key_len); call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); + zval_dtor(&z_fun_del); + zval_dtor(&z_args[0]); /* remove key from index */ ra_remove_from_index(z_from, key, key_len TSRMLS_CC); @@ -839,10 +859,12 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { if (ttl > 0) { /* run EXPIRE on target */ - ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6, 0); - ZVAL_STRINGL(&z_args[0], key, key_len, 0); + ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6); + ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_LONG(&z_args[1], ttl); call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); + zval_dtor(&z_fun_expire); + zval_dtor(&z_args[0]); } return 1; @@ -858,15 +880,20 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS ulong idx; /* run ZRANGE key 0 -1 WITHSCORES on source */ - ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6, 0); + ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6); for(i = 0; i < 4; ++i) { INIT_ZVAL(z_args[i]); } - ZVAL_STRINGL(&z_args[0], key, key_len, 0); - ZVAL_STRINGL(&z_args[1], "0", 1, 0); - ZVAL_STRINGL(&z_args[2], "-1", 2, 0); + ZVAL_STRINGL(&z_args[0], key, key_len); + ZVAL_STRINGL(&z_args[1], "0", 1); + ZVAL_STRINGL(&z_args[2], "-1", 2); ZVAL_BOOL(&z_args[3], 1); call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); + zval_dtor(&z_fun_zrange); + zval_dtor(&z_args[2]); + zval_dtor(&z_args[1]); + zval_dtor(&z_args[0]); + if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ @@ -881,7 +908,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS z_zadd_args = ecalloc((1 + 2*count), sizeof(zval)); INIT_ZVAL(z_zadd_args[0]); - ZVAL_STRINGL(&z_zadd_args[0], key, key_len, 0); + ZVAL_STRINGL(&z_zadd_args[0], key, key_len); i = 1; ZEND_HASH_FOREACH_KEY_VAL(h_zset_vals, idx, zkey, z_score_p) { @@ -892,7 +919,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* add value */ INIT_ZVAL(z_zadd_args[i+1]); if (zkey) { - ZVAL_STRINGL(&z_zadd_args[i+1], zkey->val, zkey->len - 1, 0); + ZVAL_STRINGL(&z_zadd_args[i+1], zkey->val, zkey->len - 1); } else { ZVAL_LONG(&z_zadd_args[i+1], (long)idx); } @@ -900,16 +927,20 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS } ZEND_HASH_FOREACH_END(); /* run ZADD on target */ - ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4, 0); + ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4); call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); /* cleanup */ + zval_dtor(&z_fun_zadd); zval_dtor(&z_ret); /* Free the array itself */ + for (i = 0; i < 1 + 2 * count; i++) { + zval_dtor(&z_zadd_args[i]); + } efree(z_zadd_args); return 1; @@ -921,9 +952,10 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl zval z_fun_get, z_fun_set, z_ret, z_args[3]; /* run GET on source */ - ZVAL_STRINGL(&z_fun_get, "GET", 3, 0); - ZVAL_STRINGL(&z_args[0], key, key_len, 0); + ZVAL_STRINGL(&z_fun_get, "GET", 3); + ZVAL_STRINGL(&z_args[0], key, key_len); call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args); + zval_dtor(&z_fun_get); if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ /* TODO: report? */ @@ -932,52 +964,55 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl /* run SET on target */ if (ttl > 0) { - ZVAL_STRINGL(&z_fun_set, "SETEX", 5, 0); - ZVAL_STRINGL(&z_args[0], key, key_len, 0); + ZVAL_STRINGL(&z_fun_set, "SETEX", 5); ZVAL_LONG(&z_args[1], ttl); - ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */ + ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous call */ call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args); /* cleanup */ zval_dtor(&z_args[2]); } else { - ZVAL_STRINGL(&z_fun_set, "SET", 3, 0); - ZVAL_STRINGL(&z_args[0], key, key_len, 0); - ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */ + ZVAL_STRINGL(&z_fun_set, "SET", 3); + ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous return value */ call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args); /* cleanup */ zval_dtor(&z_args[1]); } + zval_dtor(&z_fun_set); + zval_dtor(&z_args[0]); return 1; } static zend_bool ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_hgetall, z_fun_hmset, z_ret, z_ret_dest, z_args[2]; + zval z_fun_hgetall, z_fun_hmset, z_ret_dest, z_args[2]; /* run HGETALL on source */ - ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7, 0); - ZVAL_STRINGL(&z_args[0], key, key_len, 0); - call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_ret, 1, z_args); + ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); + ZVAL_STRINGL(&z_args[0], key, key_len); + call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); + zval_dtor(&z_fun_hgetall); - if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ + if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ + zval_dtor(&z_args[1]); + zval_dtor(&z_args[0]); return 0; } /* run HMSET on target */ - ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5, 0); - ZVAL_STRINGL(&z_args[0], key, key_len, 0); - z_args[1] = z_ret; /* copy z_ret to arg 1 */ + ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); + zval_dtor(&z_fun_hmset); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); /* cleanup */ - zval_dtor(&z_ret); + zval_dtor(&z_args[1]); + zval_dtor(&z_args[0]); return 1; } @@ -992,22 +1027,23 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, HashTable *h_set_vals; /* run retrieval command on source */ - ZVAL_STRING(&z_fun_retrieve, cmd_list[0], 0); /* set the command */ + ZVAL_STRING(&z_fun_retrieve, cmd_list[0]); /* set the command */ z_retrieve_args = ecalloc(list_count, sizeof(zval)); /* set the key */ INIT_ZVAL(z_retrieve_args[0]); - ZVAL_STRINGL(&z_retrieve_args[0], key, key_len, 1); + ZVAL_STRINGL(&z_retrieve_args[0], key, key_len); /* possibly add some other args if they were provided. */ for(i = 1; i < list_count; ++i) { INIT_ZVAL(z_retrieve_args[i]); - ZVAL_STRING(&z_retrieve_args[i], cmd_list[i], 1); + ZVAL_STRING(&z_retrieve_args[i], cmd_list[i]); } call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args); /* cleanup */ + zval_dtor(&z_fun_retrieve); for(i = 0; i < list_count; ++i) { zval_dtor(&z_retrieve_args[i]); } @@ -1021,10 +1057,10 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, /* run SADD/RPUSH on target */ h_set_vals = Z_ARRVAL(z_ret); count = 1 + zend_hash_num_elements(h_set_vals); - ZVAL_STRING(&z_fun_sadd, cmd_add[0], 0); + ZVAL_STRING(&z_fun_sadd, cmd_add[0]); z_sadd_args = ecalloc(count, sizeof(zval)); INIT_ZVAL(z_sadd_args[0]); - ZVAL_STRINGL(&z_sadd_args[0], key, key_len, 1); + ZVAL_STRINGL(&z_sadd_args[0], key, key_len); i = 1; ZEND_HASH_FOREACH_VAL(h_set_vals, z_data_p) { @@ -1044,6 +1080,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); /* cleanup */ + zval_dtor(&z_fun_sadd); for (i = 0; i < count; i++) { zval_dtor(&z_sadd_args[i]); } @@ -1131,7 +1168,7 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z /* run cb(hostname, count) */ MAKE_STD_ZVAL(z_host); - ZVAL_STRING(z_host, hostname, 0); + ZVAL_STRING(z_host, hostname); z_args[0] = &z_host; MAKE_STD_ZVAL(z_count); ZVAL_LONG(z_count, count); @@ -1140,6 +1177,7 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z zend_call_function(z_cb, z_cb_cache TSRMLS_CC); /* cleanup */ + zval_dtor(z_host); efree(z_host); efree(z_count); if(z_ret) From 88658b5e9bf2b40e769169e7e77e19486286373d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 25 Oct 2016 11:04:01 +0300 Subject: [PATCH 0600/1986] WIP: php7 compatibility Wrap create_cluster_context --- redis_array_impl.c | 7 ++++--- redis_cluster.c | 28 ++++++++++++++++++++++++++-- redis_cluster.h | 7 +++++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 44738d310e..a2f8e790e9 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -990,9 +990,10 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS zval z_fun_hgetall, z_fun_hmset, z_ret_dest, z_args[2]; /* run HGETALL on source */ - ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); - ZVAL_STRINGL(&z_args[0], key, key_len); - call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); + INIT_ZVAL(z_args[0]); + ZVAL_STRINGL(&z_args[0], key, key_len); + ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); + call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); zval_dtor(&z_fun_hgetall); if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ diff --git a/redis_cluster.c b/redis_cluster.c index 5ffbd9ea3c..8fdd7453e4 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -36,7 +36,13 @@ zend_class_entry *redis_cluster_ce; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; + +#if (PHP_MAJOR_VERSION < 7) static zend_class_entry *spl_rte_ce = NULL; +#else +/* Handlers for RedisCluster */ +zend_object_handlers RedisCluster_handlers; +#endif /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) @@ -263,14 +269,22 @@ PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC) { } /* Create redisCluster context */ +#if (PHP_MAJOR_VERSION < 7) zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { - zend_object_value retval; redisCluster *cluster; // Allocate our actual struct cluster = emalloc(sizeof(redisCluster)); memset(cluster, 0, sizeof(redisCluster)); +#else +zend_object * +create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { + redisCluster *cluster; + + // Allocate our actual struct + cluster = emalloc(sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); +#endif // We're not currently subscribed anywhere cluster->subscribed_slot = -1; @@ -291,7 +305,8 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { // Initialize it zend_object_std_init(&cluster->std, class_type TSRMLS_CC); - +#if (PHP_MAJOR_VERSION < 7) + zend_object_value retval; #if PHP_VERSION_ID < 50399 zval *tmp; @@ -306,6 +321,15 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { retval.handlers = zend_get_std_object_handlers(); return retval; +#else + object_properties_init(&cluster->std, class_type); + memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); + RedisCluster_handlers.free_obj = free_cluster_context; + + cluster->std.handlers = &RedisCluster_handlers; + + return &cluster->std; +#endif } /* Free redisCluster context */ diff --git a/redis_cluster.h b/redis_cluster.h index f6be80ba54..1ff465b282 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -107,8 +107,11 @@ PHP_REDIS_API zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); /* Create cluster context */ -zend_object_value create_cluster_context(zend_class_entry *class_type - TSRMLS_DC); +#if (PHP_MAJOR_VERSION < 7) +zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC); +#else +zend_object *create_cluster_context(zend_class_entry *class_type TSRMLS_DC); +#endif /* Free cluster context struct */ void free_cluster_context(void *object TSRMLS_DC); From e9c992e425d3745d65468a43618b5ee18e258ff0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 26 Oct 2016 12:15:23 +0300 Subject: [PATCH 0601/1986] WIP: php7 compatibility Define ZEND_SAME_FAKE_TYPE macro and use it instead of Z_TYPE == IS_BOOL Redefine add_assoc_stringl_ex as macro --- cluster_library.c | 9 ++++----- common.h | 6 ++++++ library.c | 4 ++-- redis_array.c | 23 ++++++++++++----------- redis_cluster.c | 3 --- redis_commands.c | 6 +++--- 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 4c321900ba..fd987aa0ab 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2382,11 +2382,10 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval(z_result, key, z); - efree(line); } else { - add_assoc_stringl_ex(z_result, key, 1+key_len, line, - line_len, 0); + add_assoc_stringl_ex(z_result, key, 1+key_len, line, line_len); } + efree(line); efree(key); } } @@ -2450,13 +2449,13 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, if(line != NULL) { zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { - efree(line); add_assoc_zval_ex(z_result,Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); } else { add_assoc_stringl_ex(z_result, Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), line, line_len, 0); + 1+Z_STRLEN_P(z_keys[i]), line, line_len); } + efree(line); } else { add_assoc_bool_ex(z_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); diff --git a/common.h b/common.h index 205470d4a3..dd535c376f 100644 --- a/common.h +++ b/common.h @@ -213,6 +213,12 @@ inline_call_user_function(HashTable *function_table, zval *object, zval *functio return ret; } +#define _IS_BOOL IS_BOOL +#define ZEND_SAME_FAKE_TYPE(faketype, realtype) ((faketype) == (realtype)) + +static int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int) = &add_assoc_stringl_ex; +#define add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length) _add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length, 1) + #else #include #endif diff --git a/library.c b/library.c index 99aa737aa9..0442de3538 100644 --- a/library.c +++ b/library.c @@ -1935,11 +1935,11 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc if(response != NULL) { zval *z = NULL; if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - efree(response); add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len, 0); + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len); } + efree(response); } else { add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); } diff --git a/redis_array.c b/redis_array.c index bcfe7c9ab9..fcb773fbd2 100644 --- a/redis_array.c +++ b/redis_array.c @@ -33,10 +33,11 @@ #include "redis_array_impl.h" /* Simple macro to detect failure in a RedisArray call */ -#define RA_CALL_FAILED(rv, cmd) \ - ((Z_TYPE_P(rv) == IS_BOOL && Z_BVAL_P(rv) == 0) || \ +#define RA_CALL_FAILED(rv, cmd) ( \ + (ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(rv)) && !Z_LVAL_P(rv)) || \ (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \ - (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE"))) \ + (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE")) \ +) extern zend_class_entry *redis_ce; zend_class_entry *redis_array_ce; @@ -254,18 +255,18 @@ PHP_METHOD(RedisArray, __construct) } /* extract index option. */ - if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL && Z_TYPE_P(zpData) == IS_BOOL) { - b_index = Z_BVAL_P(zpData); + if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL && ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(zpData))) { + b_index = Z_LVAL_P(zpData); } /* extract autorehash option. */ - if ((zpData = zend_hash_str_find(hOpts, "autorehash", sizeof("autorehash") - 1)) != NULL && Z_TYPE_P(zpData) == IS_BOOL) { - b_autorehash = Z_BVAL_P(zpData); + if ((zpData = zend_hash_str_find(hOpts, "autorehash", sizeof("autorehash") - 1)) != NULL && ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(zpData))) { + b_autorehash = Z_LVAL_P(zpData); } /* pconnect */ - if ((zpData = zend_hash_str_find(hOpts, "pconnect", sizeof("pconnect") - 1)) != NULL && Z_TYPE_P(zpData) == IS_BOOL) { - b_pconnect = Z_BVAL_P(zpData); + if ((zpData = zend_hash_str_find(hOpts, "pconnect", sizeof("pconnect") - 1)) != NULL && ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(zpData))) { + b_pconnect = Z_LVAL_P(zpData); } /* extract retry_interval option. */ @@ -280,8 +281,8 @@ PHP_METHOD(RedisArray, __construct) } /* extract lazy connect option. */ - if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL && Z_TYPE_P(zpData) == IS_BOOL) { - b_lazy_connect = Z_BVAL_P(zpData); + if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL && ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(zpData))) { + b_lazy_connect = Z_LVAL_P(zpData); } /* extract connect_timeout option */ diff --git a/redis_cluster.c b/redis_cluster.c index 8fdd7453e4..925d3af1c7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -37,12 +37,9 @@ zend_class_entry *redis_cluster_ce; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; -#if (PHP_MAJOR_VERSION < 7) static zend_class_entry *spl_rte_ce = NULL; -#else /* Handlers for RedisCluster */ zend_object_handlers RedisCluster_handlers; -#endif /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) diff --git a/redis_commands.c b/redis_commands.c index 8af7670de0..ddc4f8727b 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -552,7 +552,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Check for withscores and limit */ if (IS_WITHSCORES_ARG(zkey->val, zkey->len)) { - *withscores = (Z_TYPE_P(z_ele) == IS_BOOL && Z_BVAL_P(z_ele) == 1); + *withscores = (ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(z_ele)) && Z_LVAL_P(z_ele)); } else if (IS_LIMIT_ARG(zkey->val, zkey->len) && Z_TYPE_P(z_ele) == IS_ARRAY) { HashTable *htlimit = Z_ARRVAL_P(z_ele); zval *zoff, *zcnt; @@ -2472,8 +2472,8 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // ALPHA if (((z_ele = zend_hash_str_find(ht_opts, "alpha", sizeof("alpha") - 1)) != NULL || - (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL - ) && Z_TYPE_P(z_ele) == IS_BOOL && Z_BVAL_P(z_ele) == 1 + (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL) && + (ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(z_ele)) && Z_LVAL_P(z_ele)) ) { add_next_index_stringl(z_argv, "ALPHA", sizeof("ALPHA") - 1); } From a1e698873a4f0c60df7aaaa65fa2b8c1d30540ac Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 26 Oct 2016 17:35:58 +0300 Subject: [PATCH 0602/1986] WIP: php7 compatibility Redefine add_next_index_string. Fix zend_fcall_info retval and params. --- cluster_library.c | 11 ++++++----- common.h | 2 ++ library.c | 11 ++++++----- redis_array.c | 2 +- redis_array_impl.c | 28 ++++++++++++++-------------- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index fd987aa0ab..334f762ab8 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1623,11 +1623,6 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * { subscribeContext *sctx = (subscribeContext*)ctx; zval *z_tab, *z_tmp, *z_ret; -#if (PHP_MAJOR_VERSION < 7) - zval **z_args[4]; -#else - zval z_args[4]; -#endif int pull=0; @@ -1656,7 +1651,13 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * } // Set up our callback pointers +#if (PHP_MAJOR_VERSION < 7) + zval **z_args[4]; sctx->cb.retval_ptr_ptr = &z_ret; +#else + zval z_args[4]; + sctx->cb.retval = z_ret; +#endif sctx->cb.params = z_args; sctx->cb.no_separation = 0; diff --git a/common.h b/common.h index dd535c376f..1cd0ddcd4f 100644 --- a/common.h +++ b/common.h @@ -167,6 +167,8 @@ inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array T typedef zend_rsrc_list_entry zend_resource; +static int (*_add_next_index_string)(zval *, const char *, int) = &add_next_index_string; +#define add_next_index_string(arg, str) _add_next_index_string(arg, str, 1); static int (*_add_next_index_stringl)(zval *, const char *, uint, int) = &add_next_index_stringl; #define add_next_index_stringl(arg, str, length) _add_next_index_stringl(arg, str, length, 1); diff --git a/library.c b/library.c index 0442de3538..124b11b886 100644 --- a/library.c +++ b/library.c @@ -260,11 +260,6 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, { subscribeContext *sctx = (subscribeContext*)ctx; zval *z_tmp, *z_ret; -#if (PHP_MAJOR_VERSION < 7) - zval **z_args[4]; -#else - zval z_args[4]; -#endif // Consume response(s) from subscribe, which will vary on argc while(sctx->argc--) { @@ -295,7 +290,13 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, efree(z_tab); } +#if (PHP_MAJOR_VERSION < 7) + zval **z_args[4]; sctx->cb.retval_ptr_ptr = &z_ret; +#else + zval z_args[4]; + sctx->cb.retval = z_ret; +#endif sctx->cb.params = z_args; sctx->cb.no_separation = 0; diff --git a/redis_array.c b/redis_array.c index fcb773fbd2..2787ab00a0 100644 --- a/redis_array.c +++ b/redis_array.c @@ -469,7 +469,7 @@ PHP_METHOD(RedisArray, _hosts) array_init(return_value); for(i = 0; i < ra->count; ++i) { - add_next_index_string(return_value, ra->hosts[i], 1); + add_next_index_string(return_value, ra->hosts[i]); } } diff --git a/redis_array_impl.c b/redis_array_impl.c index a2f8e790e9..336b56bace 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -1159,28 +1159,28 @@ ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, const char *hostname, long count TSRMLS_DC) { - zval *z_ret = NULL, **z_args[2]; - zval *z_host, *z_count; + zval *z_ret = NULL, z_args[2]; + zval *z_host = &z_args[0], *z_count = &z_args[1]; - z_cb->retval_ptr_ptr = &z_ret; - z_cb->params = (struct _zval_struct ***)&z_args; + ZVAL_STRING(z_host, hostname); + ZVAL_LONG(z_count, count); + +#if (PHP_MAJOR_VERSION < 7) + zval **z_args_pp[2] = { &z_host, &z_count }; + z_cb->params = z_args_pp; + z_cb->retval_ptr_ptr = &z_ret; +#else + z_cb->params = z_args; + z_cb->retval = z_ret; +#endif z_cb->param_count = 2; z_cb->no_separation = 0; /* run cb(hostname, count) */ - MAKE_STD_ZVAL(z_host); - ZVAL_STRING(z_host, hostname); - z_args[0] = &z_host; - MAKE_STD_ZVAL(z_count); - ZVAL_LONG(z_count, count); - z_args[1] = &z_count; - zend_call_function(z_cb, z_cb_cache TSRMLS_CC); /* cleanup */ - zval_dtor(z_host); - efree(z_host); - efree(z_count); + zval_dtor(z_host); if(z_ret) efree(z_ret); } From 90d64687ae1ba769e47cbf5641b3f47d2ed40f79 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 26 Oct 2016 23:14:27 +0300 Subject: [PATCH 0603/1986] REDIS_DOUBLE_TO_STRING --- common.h | 4 ++++ library.c | 41 +++++++++++++++-------------------------- library.h | 30 ++++++++++++++++++++---------- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/common.h b/common.h index 1cd0ddcd4f..fba35b47c7 100644 --- a/common.h +++ b/common.h @@ -17,6 +17,10 @@ typedef struct { char *val; } zend_string; +#define zend_string_release(s) do { \ + if ((s) && (s)->val) efree((s)->val); \ +} while (0) + #define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) do { \ HashPosition _hpos; \ for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ diff --git a/library.c b/library.c index 124b11b886..9a8f38f846 100644 --- a/library.c +++ b/library.c @@ -555,15 +555,6 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D return NULL; } -void add_constant_long(zend_class_entry *ce, char *name, int value) { - zval *constval; - constval = pemalloc(sizeof(zval), 1); - INIT_PZVAL(constval); - ZVAL_LONG(constval, value); - zend_hash_add(&ce->constants_table, name, 1 + strlen(name), - (void*)&constval, sizeof(zval*), NULL); -} - int integer_length(int i) { int sz = 0; @@ -611,8 +602,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) va_list ap; smart_string buf = {0}; int l = strlen(keyword); - char *dbl_str; - int dbl_len; + zend_string *dbl_str; va_start(ap, format); @@ -641,11 +631,11 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) case 'f': case 'F': { double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_string_append_long(&buf, dbl_len); + REDIS_DOUBLE_TO_STRING(dbl_str, d); + smart_string_append_long(&buf, dbl_str->len); smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); + smart_string_appendl(&buf, dbl_str->val, dbl_str->len); + zend_string_release(dbl_str); } break; @@ -692,8 +682,7 @@ redis_cmd_format(char **ret, char *format, ...) { smart_string buf = {0}; va_list ap; char *p = format; - char *dbl_str; - int dbl_len; + zend_string *dbl_str; va_start(ap, format); @@ -710,11 +699,11 @@ redis_cmd_format(char **ret, char *format, ...) { case 'F': case 'f': { double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d) - smart_string_append_long(&buf, dbl_len); + REDIS_DOUBLE_TO_STRING(dbl_str, d); + smart_string_append_long(&buf, dbl_str->len); smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, dbl_str, dbl_len); - efree(dbl_str); + smart_string_appendl(&buf, dbl_str->val, dbl_str->len); + zend_string_release(dbl_str); } break; @@ -820,17 +809,17 @@ int redis_cmd_append_sstr_long(smart_string *str, long append) { * Append a double to a smart string command */ int redis_cmd_append_sstr_dbl(smart_string *str, double value) { - char *dbl_str; - int dbl_len, retval; + zend_string *dbl_str; + int retval; /* Convert to double */ - REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, value); + REDIS_DOUBLE_TO_STRING(dbl_str, value); // Append the string - retval = redis_cmd_append_sstr(str, dbl_str, dbl_len); + retval = redis_cmd_append_sstr(str, dbl_str->val, dbl_str->len); /* Free our double string */ - efree(dbl_str); + zend_string_release(dbl_str); /* Return new length */ return retval; diff --git a/library.h b/library.h index b876238386..ec4abc2c72 100644 --- a/library.h +++ b/library.h @@ -7,7 +7,6 @@ #define REDIS_CMD_INIT_SSTR_STATIC(sstr, argc, keyword) \ redis_cmd_init_sstr(sstr, argc, keyword, sizeof(keyword)-1); -void add_constant_long(zend_class_entry *ce, char *name, int value); int integer_length(int i); int redis_cmd_format(char **ret, char *format, ...); int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); @@ -85,17 +84,28 @@ PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); +#if (PHP_MAJOR_VERSION < 7) #if ZEND_MODULE_API_NO >= 20100000 -#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) do { \ - char dbl_decsep; \ - dbl_decsep = '.'; \ - dbl_str = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \ - dbl_len = strlen(dbl_str); \ - } while (0); +#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl) do { \ + char dbl_decsep = '.'; \ + zend_string _zstr = {0}; \ + _zstr.val = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \ + _zstr.len = strlen(_zstr.val); \ + dbl_str = &_zstr; \ +} while (0); +#else +#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl) do { \ + zend_string _zstr = {0}; \ + _zstr.val = _php_math_number_format(dbl, 16, '.', '\x00'); \ + _zstr.len = strlen(_zstr.val); \ + dbl_str = &_zstr; \ +} while (0) +#endif #else -#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \ - dbl_str = _php_math_number_format(dbl, 16, '.', '\x00'); \ - dbl_len = strlen(dbl_str); +#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl) do { \ + char dbl_decsep = '.'; \ + dbl_str = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \ +} while (0); #endif #endif From 8cfebfd98be8559a78ea033d864008317a73c837 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 27 Oct 2016 12:43:57 +0300 Subject: [PATCH 0604/1986] WIP: php7 compatibility zend_list_insert + add_assoc_string + add_assoc_zval --- cluster_library.c | 6 +++--- common.h | 12 +++++++++++- library.c | 18 +++++++++--------- redis_array.c | 2 +- redis_array_impl.c | 7 +++++-- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 334f762ab8..9a51eb990c 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2384,7 +2384,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval(z_result, key, z); } else { - add_assoc_stringl_ex(z_result, key, 1+key_len, line, line_len); + add_assoc_stringl_ex(z_result, key, key_len, line, line_len); } efree(line); efree(key); @@ -2451,10 +2451,10 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval_ex(z_result,Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), z); + Z_STRLEN_P(z_keys[i]), z); } else { add_assoc_stringl_ex(z_result, Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), line, line_len); + Z_STRLEN_P(z_keys[i]), line, line_len); } efree(line); } else { diff --git a/common.h b/common.h index fba35b47c7..7b326cb9cc 100644 --- a/common.h +++ b/common.h @@ -222,8 +222,18 @@ inline_call_user_function(HashTable *function_table, zval *object, zval *functio #define _IS_BOOL IS_BOOL #define ZEND_SAME_FAKE_TYPE(faketype, realtype) ((faketype) == (realtype)) +#undef add_assoc_string +#define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str) +static int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int) = &add_assoc_string_ex; +#define add_assoc_string_ex(_arg, _key, _key_len, _str) _add_assoc_string_ex(_arg, _key, _key_len + 1, _str, 1) + static int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int) = &add_assoc_stringl_ex; -#define add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length) _add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length, 1) +#define add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length) _add_assoc_stringl_ex(_arg, _key, _key_len + 1, _str, _length, 1) + +#undef add_assoc_zval +#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key), __value) +static int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *) = &add_assoc_zval_ex; +#define add_assoc_zval_ex(_arg, _key, _key_len, _value) _add_assoc_zval_ex(_arg, _key, _key_len + 1, _value); #else #include diff --git a/library.c b/library.c index 9a8f38f846..1ed90d86fb 100644 --- a/library.c +++ b/library.c @@ -971,10 +971,10 @@ PHP_REDIS_API zval *redis_parse_info_response(char *response) { if(is_numeric == 1) { add_assoc_long(z_ret, key, atol(value)); - efree(value); } else { - add_assoc_string(z_ret, key, value, 0); + add_assoc_string(z_ret, key, value); } + efree(value); efree(key); } @@ -1061,10 +1061,10 @@ PHP_REDIS_API zval* redis_parse_client_list_response(char *response) { /* Add as a long or string, depending */ if(is_numeric == 1) { add_assoc_long(z_sub_result, key, atol(value)); - efree(value); } else { - add_assoc_string(z_sub_result, key, value, 0); + add_assoc_string(z_sub_result, key, value); } + efree(value); // If we hit a '\n', then we can add this user to our list if(*p == '\n') { /* Add our user */ @@ -1262,7 +1262,7 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, MAKE_STD_ZVAL(z); *z = *z_value_p; zval_copy_ctor(z); - add_assoc_zval_ex(z_ret, hkey, 1+hkey_len, z); + add_assoc_zval_ex(z_ret, hkey, hkey_len, z); } } @@ -1496,7 +1496,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock if(is_numeric) { add_assoc_long(z_result, p, atol(p2)); } else { - add_assoc_string(z_result, p, p2, 1); + add_assoc_string(z_result, p, p2); } p = p3; @@ -1609,7 +1609,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } redis_sock->stream = php_stream_xport_create(host, host_len, - ENFORCE_SAFE_MODE, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, + 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, tv_ptr, NULL, &errstr, &err); if (persistent_id) { @@ -1925,9 +1925,9 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc if(response != NULL) { zval *z = NULL; if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), z); + add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), Z_STRLEN_P(z_keys[i]), z); } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), response, response_len); + add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), Z_STRLEN_P(z_keys[i]), response, response_len); } efree(response); } else { diff --git a/redis_array.c b/redis_array.c index 2787ab00a0..b3078683cf 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1057,7 +1057,7 @@ PHP_METHOD(RedisArray, mset) zval_copy_ctor(z_tmp); INIT_PZVAL(z_tmp); - add_assoc_zval_ex(z_argarray, keys[i], key_lens[i] + 1, z_tmp); /* +1 to count the \0 here */ + add_assoc_zval_ex(z_argarray, keys[i], key_lens[i], z_tmp); found++; } diff --git a/redis_array_impl.c b/redis_array_impl.c index 336b56bace..e137fa5f70 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -32,7 +32,7 @@ extern zend_class_entry *redis_ce; RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) { - int i = 0, host_len, id; + int i = 0, host_len; char *host, *p; short port; zval *zpData, z_cons, z_ret, *redis_inst; @@ -89,14 +89,16 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b } /* attach */ +#if (PHP_MAJOR_VERSION < 7) + int id; #if PHP_VERSION_ID >= 50400 id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); #else id = zend_list_insert(redis_sock, le_redis_sock); #endif -#if (PHP_MAJOR_VERSION < 7) add_property_resource(&ra->redis[i], "socket", id); #else + zval *id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); add_property_resource(&ra->redis[i], "socket", Z_RES_P(id)); #endif @@ -1162,6 +1164,7 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z zval *z_ret = NULL, z_args[2]; zval *z_host = &z_args[0], *z_count = &z_args[1]; + INIT_ZVAL(z_args[0]); ZVAL_STRING(z_host, hostname); ZVAL_LONG(z_count, count); From 56abe197215e6ec64e63f65c6b8c1e97db5cbedf Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 28 Oct 2016 10:36:05 +0300 Subject: [PATCH 0605/1986] WIP: php7 compatibility Wrap zend_hash_init destructor. Add implementation of zend_hash_str_exists, zend_hash_str_update_ptr, zend_hash_index_update_ptr and zval_get_long functions. --- cluster_library.c | 45 +++++++++++++++++++++++++++++--------------- common.h | 47 ++++++++++++++++++++++++++++++++++++++++++++++ redis_array_impl.c | 2 +- redis_cluster.c | 14 ++++++++++++-- redis_commands.c | 29 +++------------------------- redis_session.c | 9 +++------ 6 files changed, 96 insertions(+), 50 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 9a51eb990c..3a9816314d 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -346,7 +346,12 @@ PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { * */ /* Free cluster distribution list inside a HashTable */ -static void cluster_dist_free_ht(void *p) { +#if (PHP_MAJOR_VERSION < 7) +static void cluster_dist_free_ht(void *p) +#else +static void cluster_dist_free_ht(zval *p) +#endif +{ clusterDistList *dl = *(clusterDistList**)p; int i; @@ -433,10 +438,9 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, } // Look for this slot - if ((dl = zend_hash_index_find_ptr(ht, (ulong)slot)) == NULL) { + if ((dl = zend_hash_index_find_ptr(ht, (zend_ulong)slot)) == NULL) { dl = cluster_dl_create(); - zend_hash_index_update(ht, (ulong)slot, (void**)&dl, - sizeof(clusterDistList*), NULL); + zend_hash_index_update_ptr(ht, (zend_ulong)slot, dl); } // Now actually add this key @@ -510,7 +514,12 @@ static void cluster_set_err(redisCluster *c, char *err, int err_len) } /* Destructor for slaves */ -static void ht_free_slave(void *data) { +#if (PHP_MAJOR_VERSION < 7) +static void ht_free_slave(void *data) +#else +static void ht_free_slave(zval *data) +#endif +{ if(*(redisClusterNode**)data) { cluster_free_node(*(redisClusterNode**)data); } @@ -662,8 +671,7 @@ cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) index = master->slaves->nNextFreeElement; } - return zend_hash_index_update(master->slaves, index, (void*)&slave, - sizeof(redisClusterNode*), NULL) != SUCCESS; + return zend_hash_index_update_ptr(master->slaves, index, slave) != NULL; } /* Sanity check/validation for CLUSTER SLOTS command */ @@ -706,8 +714,7 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { klen = snprintf(key,sizeof(key),"%s:%ld",host,port); if ((pnode = zend_hash_str_find_ptr(c->nodes, key, klen)) == NULL) { master = cluster_node_create(c, host, hlen, port, low, 0); - zend_hash_update(c->nodes, key, klen+1, (void*)&master, - sizeof(redisClusterNode*), NULL); + zend_hash_str_update_ptr(c->nodes, key, klen, master); } else { master = pnode; } @@ -778,13 +785,23 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { } /* Our context seeds will be a hash table with RedisSock* pointers */ -static void ht_free_seed(void *data) { +#if (PHP_MAJOR_VERSION < 7) +static void ht_free_seed(void *data) +#else +static void ht_free_seed(zval *data) +#endif +{ RedisSock *redis_sock = *(RedisSock**)data; if(redis_sock) redis_free_socket(redis_sock); } /* Free redisClusterNode objects we've stored */ -static void ht_free_node(void *data) { +#if (PHP_MAJOR_VERSION < 7) +static void ht_free_node(void *data) +#else +static void ht_free_node(zval *data) +#endif +{ redisClusterNode *node = *(redisClusterNode**)data; cluster_free_node(node); } @@ -909,8 +926,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { redis_sock->port); // Add to our seed HashTable - zend_hash_update(cluster->seeds, key, key_len+1, (void*)&redis_sock, - sizeof(RedisSock*),NULL); + zend_hash_str_update_ptr(cluster->seeds, key, key_len, redis_sock); } efree(z_seeds); @@ -1272,8 +1288,7 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { /* Our node is new, so keep track of it for cleanup */ klen = snprintf(key,sizeof(key),"%s:%ld",c->redir_host,c->redir_port); - zend_hash_update(c->nodes, key, klen+1, (void*)&node, - sizeof(redisClusterNode*), NULL); + zend_hash_str_update_ptr(c->nodes, key, klen, node); /* Now point our slot at the node */ c->master[c->redir_slot] = node; diff --git a/common.h b/common.h index 7b326cb9cc..b9155f59db 100644 --- a/common.h +++ b/common.h @@ -54,6 +54,8 @@ typedef struct { } \ } while(0) +#define zend_hash_str_exists(ht, str, len) zend_hash_exists(ht, str, len + 1) + static zend_always_inline zval * zend_hash_str_find(const HashTable *ht, const char *key, size_t len) { @@ -76,6 +78,24 @@ zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) return NULL; } +static zend_always_inline void * +zend_hash_str_update_ptr(HashTable *ht, const char *str, size_t len, void *pData) +{ + if (zend_hash_update(ht, str, len + 1, (void *)&pData, sizeof(void *), NULL) == SUCCESS) { + return pData; + } + return NULL; +} + +static zend_always_inline void * +zend_hash_index_update_ptr(HashTable *ht, zend_ulong h, void *pData) +{ + if (zend_hash_index_update(ht, h, (void **)&pData, sizeof(void *), NULL) == SUCCESS) { + return pData; + } + return NULL; +} + #undef zend_hash_get_current_data static zend_always_inline zval * zend_hash_get_current_data(HashTable *ht) @@ -235,6 +255,33 @@ static int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, in static int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *) = &add_assoc_zval_ex; #define add_assoc_zval_ex(_arg, _key, _key_len, _value) _add_assoc_zval_ex(_arg, _key, _key_len + 1, _value); +typedef long zend_long; +static zend_always_inline zend_long +zval_get_long(zval *op) +{ + switch (Z_TYPE_P(op)) { + case IS_BOOL: + case IS_LONG: + return Z_LVAL_P(op); + case IS_DOUBLE: + return zend_dval_to_lval(Z_DVAL_P(op)); + case IS_STRING: + { + double dval; + zend_long lval; + zend_uchar type = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, 0); + if (type == IS_LONG) { + return lval; + } else if (type == IS_DOUBLE) { + return zend_dval_to_lval(dval); + } + } + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + return 0; +} + #else #include #endif diff --git a/redis_array_impl.c b/redis_array_impl.c index e137fa5f70..01c405802f 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -705,7 +705,7 @@ ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len) { cmd_up[i] = toupper(cmd[i]); cmd_up[cmd_len] = 0; - ret = zend_hash_exists(Z_ARRVAL_P(ra->z_pure_cmds), cmd_up, cmd_len+1); + ret = zend_hash_str_exists(Z_ARRVAL_P(ra->z_pure_cmds), cmd_up, cmd_len); efree(cmd_up); return !ret; diff --git a/redis_cluster.c b/redis_cluster.c index 925d3af1c7..a39eed3d8e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -231,13 +231,23 @@ zend_function_entry redis_cluster_functions[] = { }; /* Our context seeds will be a hash table with RedisSock* pointers */ -static void ht_free_seed(void *data) { +#if (PHP_MAJOR_VERSION < 7) +static void ht_free_seed(void *data) +#else +static void ht_free_seed(zval *data) +#endif +{ RedisSock *redis_sock = *(RedisSock**)data; if(redis_sock) redis_free_socket(redis_sock); } /* Free redisClusterNode objects we've stored */ -static void ht_free_node(void *data) { +#if (PHP_MAJOR_VERSION < 7) +static void ht_free_node(void *data) +#else +static void ht_free_node(zval *data) +#endif +{ redisClusterNode *node = *(redisClusterNode**)data; cluster_free_node(node); } diff --git a/redis_commands.c b/redis_commands.c index ddc4f8727b..dbf0a9523a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -502,25 +502,6 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, #define IS_LIMIT_ARG(s, l) \ (l == sizeof("limit") && !strncasecmp(s,"limit",sizeof("limit"))) -/* Helper to get the long value stored in a zval, whether it's actually stored - * as a long or is a string that contains a long */ -static int zval_get_long(zval *zv, long *lval) -{ - /* If it's already a long, just set and return success */ - if (Z_TYPE_P(zv) == IS_LONG) { - *lval = Z_LVAL_P(zv); - return SUCCESS; - } - - /* If our zval isn't a string, or doesn't translate into a long, fail */ - if (Z_TYPE_P(zv) != IS_STRING || is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), lval, NULL, 0) != IS_LONG) { - return FAILURE; - } - - /* Success */ - return SUCCESS; -} - int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, void **ctx) @@ -562,13 +543,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, (zcnt = zend_hash_index_find(htlimit, 1)) != NULL ) { /* Set our limit if we can get valid longs from both args */ - has_limit = zval_get_long(zoff, &offset) == SUCCESS && - zval_get_long(zcnt, &count) == SUCCESS; - - /* Inform the user there is a problem if we don't have a limit */ - if (!has_limit) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset and limit must be long values. Ignoring."); - } + offset = zval_get_long(zoff); + count = zval_get_long(zcnt); + has_limit = 1; } } } ZEND_HASH_FOREACH_END(); diff --git a/redis_session.c b/redis_session.c index 8d145abdc1..ff3ba08756 100644 --- a/redis_session.c +++ b/redis_session.c @@ -242,8 +242,7 @@ PS_OPEN_FUNC(redis) sapi_module.treat_data(PARSE_STRING, estrdup(url->query), params TSRMLS_CC); if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "weight", sizeof("weight") - 1)) != NULL) { - convert_to_long_ex(¶m); - weight = Z_LVAL_P(param); + weight = zval_get_long(param); } if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout") - 1)) != NULL) { timeout = atof(Z_STRVAL_P(param)); @@ -261,12 +260,10 @@ PS_OPEN_FUNC(redis) auth = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); } if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "database", sizeof("database") - 1)) != NULL) { - convert_to_long_ex(¶m); - database = Z_LVAL_P(param); + database = zval_get_long(param); } if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval") - 1)) != NULL) { - convert_to_long_ex(¶m); - retry_interval = Z_LVAL_P(param); + retry_interval = zval_get_long(param); } zval_ptr_dtor(¶ms); From ea2b533683791afa4d612c28052dcdd04423acbe Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 28 Oct 2016 17:13:16 +0300 Subject: [PATCH 0606/1986] add_assoc_bool --- cluster_library.c | 2 +- common.h | 5 +++++ library.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 3a9816314d..f64ee2fb77 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2474,7 +2474,7 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, efree(line); } else { add_assoc_bool_ex(z_result, Z_STRVAL_P(z_keys[i]), - 1+Z_STRLEN_P(z_keys[i]), 0); + Z_STRLEN_P(z_keys[i]), 0); } // Clean up key context diff --git a/common.h b/common.h index b9155f59db..2c0090576c 100644 --- a/common.h +++ b/common.h @@ -242,6 +242,11 @@ inline_call_user_function(HashTable *function_table, zval *object, zval *functio #define _IS_BOOL IS_BOOL #define ZEND_SAME_FAKE_TYPE(faketype, realtype) ((faketype) == (realtype)) +#undef add_assoc_bool +#define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key), __b) +static int (*_add_assoc_bool_ex)(zval *, const char *, uint, int) = &add_assoc_bool_ex; +#define add_assoc_bool_ex(_arg, _key, _key_len, _b) _add_assoc_bool_ex(_arg, _key, _key_len + 1, _b) + #undef add_assoc_string #define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str) static int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int) = &add_assoc_string_ex; diff --git a/library.c b/library.c index 1ed90d86fb..097d5ec694 100644 --- a/library.c +++ b/library.c @@ -1931,7 +1931,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc } efree(response); } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), 1+Z_STRLEN_P(z_keys[i]), 0); + add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), Z_STRLEN_P(z_keys[i]), 0); } zval_dtor(z_keys[i]); efree(z_keys[i]); From 19306d43b1cb25c6ab070d2ce648b6a4fb8e2e07 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 29 Oct 2016 23:04:41 +0300 Subject: [PATCH 0607/1986] WIP: php7 compatibility php_var_serialize + php_var_unserialize --- common.h | 6 ++++++ library.c | 22 +++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/common.h b/common.h index 2c0090576c..581b11b354 100644 --- a/common.h +++ b/common.h @@ -4,6 +4,7 @@ #ifndef REDIS_COMMON_H #define REDIS_COMMON_H +#include #if (PHP_MAJOR_VERSION < 7) #include typedef smart_str smart_string; @@ -287,6 +288,11 @@ zval_get_long(zval *op) return 0; } +static void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC) = &php_var_serialize; +#define php_var_serialize(buf, struc, data) _php_var_serialize(buf, &struc, data TSRMLS_CC) +static int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; +#define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) + #else #include #endif diff --git a/library.c b/library.c index 097d5ec694..fc71be50ec 100644 --- a/library.c +++ b/library.c @@ -8,7 +8,6 @@ #include /* TCP_NODELAY */ #include #endif -#include #ifdef HAVE_REDIS_IGBINARY #include "igbinary/igbinary.h" #endif @@ -1563,7 +1562,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, double timeout, PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) { struct timeval tv, read_tv, *tv_ptr = NULL; - char *host = NULL, *persistent_id = NULL, *errstr = NULL; + char *host = NULL, *persistent_id = NULL; const char *fmtstr = "%s:%d"; int host_len, err = 0; php_netstream_data_t *sock; @@ -1610,7 +1609,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock->stream = php_stream_xport_create(host, host_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &errstr, &err); + persistent_id, tv_ptr, NULL, NULL, &err); if (persistent_id) { efree(persistent_id); @@ -1619,7 +1618,6 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) efree(host); if (!redis_sock->stream) { - if (errstr) efree(errstr); return -1; } @@ -1997,7 +1995,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len #else HashTable ht; #endif - smart_string sstr = {0}; + smart_str sstr = {0}; zval z_copy; #ifdef HAVE_REDIS_IGBINARY size_t sz; @@ -2039,9 +2037,15 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len #else zend_hash_init(&ht, 10, NULL, NULL, 0); #endif - php_var_serialize(&sstr, &z, &ht TSRMLS_CC); - *val = sstr.c; + php_var_serialize(&sstr, z, &ht); +#if (PHP_MAJOR_VERSION < 7) + *val = estrndup(sstr.c, sstr.len); *val_len = (int)sstr.len; +#else + *val = estrndup(sstr.s->val, sstr.s->len); + *val_len = sstr.s->len; +#endif + smart_str_free(&sstr); #if ZEND_MODULE_API_NO >= 20100000 PHP_VAR_SERIALIZE_DESTROY(ht); #else @@ -2085,8 +2089,8 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, #else memset(&var_hash, 0, sizeof(var_hash)); #endif - if(!php_var_unserialize(return_value, (const unsigned char**)&val, - (const unsigned char*)val + val_len, &var_hash TSRMLS_CC)) { + if(!php_var_unserialize(*return_value, (const unsigned char**)&val, + (const unsigned char*)val + val_len, &var_hash)) { if(rv_free==1) efree(*return_value); ret = 0; } else { From 7919b5c0aa1502608ecf997fdd984ab28eec4481 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 31 Oct 2016 17:11:36 +0200 Subject: [PATCH 0608/1986] WIP: php7 compatibility Wrap zend_is_callable_ex + session handlers --- redis_array_impl.c | 8 ++++++ redis_session.c | 70 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 01c405802f..7e814fef23 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -402,7 +402,11 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR zval z_ret, z_argv[1]; /* check that we can call the extractor function */ +#if (PHP_MAJOR_VERSION < 7) if(!zend_is_callable_ex(ra->z_fun, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { +#else + if (!zend_is_callable_ex(ra->z_fun, NULL, 0, NULL, NULL, NULL)) { +#endif php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function"); return NULL; } @@ -451,7 +455,11 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML zval z_argv[1]; /* check that we can call the extractor function */ +#if (PHP_MAJOR_VERSION < 7) if(!zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { +#else + if (!zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL)) { +#endif php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); return 0; } diff --git a/redis_session.c b/redis_session.c index ff3ba08756..17fe2b0b69 100644 --- a/redis_session.c +++ b/redis_session.c @@ -340,21 +340,29 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses */ PS_READ_FUNC(redis) { - char *session, *cmd; - int session_len, cmd_len; + char *resp, *cmd; + int resp_len, cmd_len; redis_pool *pool = PS_GET_MOD_DATA(); +#if (PHP_MAJOR_VERSION < 7) redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); +#else + redis_pool_member *rpm = redis_pool_get_sock(pool, key->val TSRMLS_CC); +#endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ return FAILURE; } /* send GET command */ - session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, session_len); +#if (PHP_MAJOR_VERSION < 7) + resp = redis_session_key(rpm, key, strlen(key), &resp_len); +#else + resp = redis_session_key(rpm, key->val, key->len, &resp_len); +#endif + cmd_len = redis_cmd_format_static(&cmd, "GET", "s", resp, resp_len); - efree(session); + efree(resp); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; @@ -362,9 +370,16 @@ PS_READ_FUNC(redis) efree(cmd); /* read response */ - if ((*val = redis_sock_read(redis_sock, vallen TSRMLS_CC)) == NULL) { + if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { return FAILURE; } +#if (PHP_MAJOR_VERSION < 7) + *val = resp; + *vallen = resp_len; +#else + *val = zend_string_init(resp, resp_len, 0); + efree(resp); +#endif return SUCCESS; } @@ -378,18 +393,24 @@ PS_WRITE_FUNC(redis) int cmd_len, response_len, session_len; redis_pool *pool = PS_GET_MOD_DATA(); +#if (PHP_MAJOR_VERSION < 7) redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); +#else + redis_pool_member *rpm = redis_pool_get_sock(pool, key->val TSRMLS_CC); +#endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ return FAILURE; } /* send SET command */ +#if (PHP_MAJOR_VERSION < 7) session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, - session_len, - INI_INT("session.gc_maxlifetime"), - val, vallen); + cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); +#else + session = redis_session_key(rpm, key->val, key->len, &session_len); + cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val->val, val->len); +#endif efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -420,14 +441,22 @@ PS_DESTROY_FUNC(redis) int cmd_len, response_len, session_len; redis_pool *pool = PS_GET_MOD_DATA(); +#if (PHP_MAJOR_VERSION < 7) redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); +#else + redis_pool_member *rpm = redis_pool_get_sock(pool, key->val TSRMLS_CC); +#endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ return FAILURE; } /* send DEL command */ +#if (PHP_MAJOR_VERSION < 7) session = redis_session_key(rpm, key, strlen(key), &session_len); +#else + session = redis_session_key(rpm, key->val, key->len, &session_len); +#endif cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", session, session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { @@ -610,7 +639,11 @@ PS_READ_FUNC(rediscluster) { short slot; /* Set up our command and slot information */ +#if (PHP_MAJOR_VERSION < 7) skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); +#else + skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); +#endif cmdlen = redis_cmd_format_static(&cmd, "GET", "s", skey, skeylen); efree(skey); @@ -632,8 +665,12 @@ PS_READ_FUNC(rediscluster) { } /* Push reply value to caller */ +#if (PHP_MAJOR_VERSION < 7) *val = reply->str; *vallen = reply->len; +#else + *val = zend_string_init(reply->str, reply->len, 0); +#endif /* Clean up */ cluster_free_reply(reply, 0); @@ -652,10 +689,13 @@ PS_WRITE_FUNC(rediscluster) { short slot; /* Set up command and slot info */ +#if (PHP_MAJOR_VERSION < 7) skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); - cmdlen = redis_cmd_format_static(&cmd, "SETEX", "sds", skey, skeylen, - INI_INT("session.gc_maxlifetime"), - val, vallen); + cmdlen = redis_cmd_format_static(&cmd, "SETEX", "sds", skey, skeylen, INI_INT("session.gc_maxlifetime"), val, vallen); +#else + skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); + cmdlen = redis_cmd_format_static(&cmd, "SETEX", "sds", skey, skeylen, INI_INT("session.gc_maxlifetime"), val->val, val->len); +#endif efree(skey); /* Attempt to send command */ @@ -691,7 +731,11 @@ PS_DESTROY_FUNC(rediscluster) { short slot; /* Set up command and slot info */ +#if (PHP_MAJOR_VERSION < 7) skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); +#else + skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); +#endif cmdlen = redis_cmd_format_static(&cmd, "DEL", "s", skey, skeylen); efree(skey); From 88301fa1f7e1f0857fe588afe766fe973d7ce1e3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 1 Nov 2016 14:35:18 +0200 Subject: [PATCH 0609/1986] WIP: php7 compatibility Redefine zend_hash_get_current_key + wrap zend_hash_get_current_key_ex --- common.h | 4 ++++ redis_cluster.c | 23 +++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/common.h b/common.h index 581b11b354..36f5fd0a8e 100644 --- a/common.h +++ b/common.h @@ -55,6 +55,10 @@ typedef struct { } \ } while(0) +#undef zend_hash_get_current_key +#define zend_hash_get_current_key(ht, str_index, num_index) \ + zend_hash_get_current_key_ex(ht, str_index, NULL, num_index, 0, NULL) + #define zend_hash_str_exists(ht, str, len) zend_hash_exists(ht, str, len + 1) static zend_always_inline zval * diff --git a/redis_cluster.c b/redis_cluster.c index a39eed3d8e..5ff2c87513 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -599,20 +599,27 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, clusterKeyValHT *kv TSRMLS_DC) { zval *z_val; - unsigned int key_len; - ulong idx; + zend_ulong idx; // Grab the key, convert it to a string using provided kbuf buffer if it's // a LONG style key - switch(zend_hash_get_current_key_ex(ht, &(kv->key), &key_len, &idx, 0, ptr)) - { +#if (PHP_MAJOR_VERSION < 7) + int key_len; + switch(zend_hash_get_current_key_ex(ht, &(kv->key), &key_len, &idx, 0, ptr)) { + case HASH_KEY_IS_STRING: + kv->key_len = (int)(key_len-1); +#else + zend_string *zkey; + switch (zend_hash_get_current_key_ex(ht, &zkey, &idx, ptr)) { + case HASH_KEY_IS_STRING: + kv->key_len = zkey->len; + kv->key = zkey->val; +#endif + break; case HASH_KEY_IS_LONG: kv->key_len = snprintf(kv->kbuf,sizeof(kv->kbuf),"%ld",(long)idx); kv->key = kv->kbuf; break; - case HASH_KEY_IS_STRING: - kv->key_len = (int)(key_len-1); - break; default: zend_throw_exception(redis_cluster_exception_ce, "Internal Zend HashTable error", 0 TSRMLS_CC); @@ -2150,7 +2157,7 @@ PHP_METHOD(RedisCluster, watch) { // Iterate over each node we'll be sending commands to for(zend_hash_internal_pointer_reset(ht_dist); - zend_hash_get_current_key(ht_dist,NULL,&slot, 0)==HASH_KEY_IS_LONG; + zend_hash_get_current_key(ht_dist, NULL, &slot) == HASH_KEY_IS_LONG; zend_hash_move_forward(ht_dist)) { // Grab the clusterDistList pointer itself From 082a1d6d88fe5ccb7ce296d4fab720d49ed82a0f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 2 Nov 2016 10:56:56 +0200 Subject: [PATCH 0610/1986] WIP: php7 compatibility Fix info command + add php7 for testing by travis --- .travis.yml | 8 ++++++++ cluster_library.c | 7 +++---- library.c | 12 +++++------- library.h | 2 +- redis.c | 5 ++--- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7676c5e3fd..ba8f957a65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,14 @@ php: - 5.4 - 5.5 - 5.6 + - 7.0 + - 7.1 + - nightly +matrix: + allow_failures: + - php: 7.0 + - php: 7.1 + - php: nightly before_install: phpize install: ./configure CFLAGS=-Wall --prefix=/usr && sudo make install before_script: diff --git a/cluster_library.c b/cluster_library.c index f64ee2fb77..e8ccbe6604 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2026,7 +2026,7 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - zval *z_result; + zval zv, *z_result = &zv; char *info; // Read our bulk response @@ -2036,13 +2036,12 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } /* Parse response, free memory */ - z_result = redis_parse_info_response(info); + redis_parse_info_response(info, z_result); efree(info); // Return our array if(CLUSTER_IS_ATOMIC(c)) { - *return_value = *z_result; - efree(z_result); + RETVAL_ZVAL(z_result, 0, 1); } else { add_next_index_zval(c->multi_resp, z_result); } diff --git a/library.c b/library.c index fc71be50ec..29f4b0db5a 100644 --- a/library.c +++ b/library.c @@ -900,7 +900,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - zval *z_ret; + zval zv, *z_ret = &zv; /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { @@ -908,7 +908,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } /* Parse it into a zval array */ - z_ret = redis_parse_info_response(response); + redis_parse_info_response(response, z_ret); /* Free source response */ efree(response); @@ -920,12 +920,12 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } } -PHP_REDIS_API zval *redis_parse_info_response(char *response) { - zval *z_ret; +PHP_REDIS_API void +redis_parse_info_response(char *response, zval *z_ret) +{ char *key, *value, *p, *cur, *pos; int is_numeric; - MAKE_STD_ZVAL(z_ret); array_init(z_ret); cur = response; @@ -976,8 +976,6 @@ PHP_REDIS_API zval *redis_parse_info_response(char *response) { efree(value); efree(key); } - - return z_ret; } /* diff --git a/library.h b/library.h index ec4abc2c72..ed8428e8ef 100644 --- a/library.h +++ b/library.h @@ -30,7 +30,7 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API zval *redis_parse_info_response(char *resp); +PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API zval *redis_parse_client_list_response(char *resp); PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); diff --git a/redis.c b/redis.c index fd21aad8b5..ef55d1f584 100644 --- a/redis.c +++ b/redis.c @@ -26,7 +26,6 @@ #include "common.h" #include "ext/standard/info.h" -#include "php_ini.h" #include "php_redis.h" #include "redis_commands.h" #include "redis_array.h" @@ -1901,8 +1900,8 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { memcpy(p, _NL, 2); p += 2; } - if(val_free) STR_FREE(val); - if(key_free) efree(key); + if (val_free) efree(val); + if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); } From 6cccbd1309b26432148f53cd3213a1539f2be91a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 2 Nov 2016 18:13:33 +0200 Subject: [PATCH 0611/1986] WIP: php7 compatibility Redefine add_assoc_long + add_assoc_double --- cluster_library.c | 4 ++-- common.h | 10 ++++++++++ library.c | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index e8ccbe6604..c429096875 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2432,11 +2432,11 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, zval *z = NULL; if (redis_unserialize(redis_sock,key,key_len, &z TSRMLS_CC)) { convert_to_string(z); - add_assoc_double_ex(z_result, Z_STRVAL_P(z), 1+Z_STRLEN_P(z), atof(line)); + add_assoc_double_ex(z_result, Z_STRVAL_P(z), Z_STRLEN_P(z), atof(line)); zval_dtor(z); efree(z); } else { - add_assoc_double_ex(z_result, key, 1+key_len, atof(line)); + add_assoc_double_ex(z_result, key, key_len, atof(line)); } /* Free our key and line */ diff --git a/common.h b/common.h index 36f5fd0a8e..7d943184a7 100644 --- a/common.h +++ b/common.h @@ -252,6 +252,16 @@ inline_call_user_function(HashTable *function_table, zval *object, zval *functio static int (*_add_assoc_bool_ex)(zval *, const char *, uint, int) = &add_assoc_bool_ex; #define add_assoc_bool_ex(_arg, _key, _key_len, _b) _add_assoc_bool_ex(_arg, _key, _key_len + 1, _b) +#undef add_assoc_long +#define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key), __n) +static int (*_add_assoc_long_ex)(zval *, const char *, uint, long) = &add_assoc_long_ex; +#define add_assoc_long_ex(_arg, _key, _key_len, _n) _add_assoc_long_ex(_arg, _key, _key_len + 1, _n) + +#undef add_assoc_double +#define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key), __d) +static int (*_add_assoc_double_ex)(zval *, const char *, uint, double) = &add_assoc_double_ex; +#define add_assoc_double_ex(_arg, _key, _key_len, _d) _add_assoc_double_ex(_arg, _key, _key_len + 1, _d) + #undef add_assoc_string #define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str) static int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int) = &add_assoc_string_ex; diff --git a/library.c b/library.c index 29f4b0db5a..7dad9904c5 100644 --- a/library.c +++ b/library.c @@ -1251,9 +1251,9 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, /* Decode the score depending on flag */ if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) { - add_assoc_long_ex(z_ret, hkey, 1+hkey_len, atoi(hval+1)); + add_assoc_long_ex(z_ret, hkey, hkey_len, atoi(hval+1)); } else if (decode == SCORE_DECODE_DOUBLE) { - add_assoc_double_ex(z_ret, hkey, 1+hkey_len, atof(hval)); + add_assoc_double_ex(z_ret, hkey, hkey_len, atof(hval)); } else { zval *z = NULL; MAKE_STD_ZVAL(z); From 36121f195b04dc66e15a9128ab2be267285a134b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 3 Nov 2016 09:47:39 +0200 Subject: [PATCH 0612/1986] WIP: php7 compatibility Replace STR_FREE with efree. Redis::hmget using zval* instead of zval**. --- cluster_library.c | 17 +++++++-------- library.c | 11 +++++----- redis_cluster.c | 6 +++--- redis_commands.c | 55 ++++++++++++++++++++--------------------------- 4 files changed, 39 insertions(+), 50 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index c429096875..2181826e6b 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2455,7 +2455,7 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, { char *line; int line_len,i=0; - zval **z_keys = ctx; + zval *z_keys = ctx; // Loop while we've got replies while(count--) { @@ -2464,21 +2464,20 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, if(line != NULL) { zval *z = NULL; if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { - add_assoc_zval_ex(z_result,Z_STRVAL_P(z_keys[i]), - Z_STRLEN_P(z_keys[i]), z); + add_assoc_zval_ex(z_result,Z_STRVAL(z_keys[i]), + Z_STRLEN(z_keys[i]), z); } else { - add_assoc_stringl_ex(z_result, Z_STRVAL_P(z_keys[i]), - Z_STRLEN_P(z_keys[i]), line, line_len); + add_assoc_stringl_ex(z_result, Z_STRVAL(z_keys[i]), + Z_STRLEN(z_keys[i]), line, line_len); } efree(line); } else { - add_assoc_bool_ex(z_result, Z_STRVAL_P(z_keys[i]), - Z_STRLEN_P(z_keys[i]), 0); + add_assoc_bool_ex(z_result, Z_STRVAL(z_keys[i]), + Z_STRLEN(z_keys[i]), 0); } // Clean up key context - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval_dtor(&z_keys[i]); // Move to the next key i++; diff --git a/library.c b/library.c index 7dad9904c5..00a619b6d7 100644 --- a/library.c +++ b/library.c @@ -1893,7 +1893,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc int i, numElems; zval *z_multi_result; - zval **z_keys = ctx; + zval *z_keys = ctx; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; @@ -1921,16 +1921,15 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc if(response != NULL) { zval *z = NULL; if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { - add_assoc_zval_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), Z_STRLEN_P(z_keys[i]), z); + add_assoc_zval_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), z); } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), Z_STRLEN_P(z_keys[i]), response, response_len); + add_assoc_stringl_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), response, response_len); } efree(response); } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL_P(z_keys[i]), Z_STRLEN_P(z_keys[i]), 0); + add_assoc_bool_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), 0); } - zval_dtor(z_keys[i]); - efree(z_keys[i]); + zval_dtor(&z_keys[i]); } efree(z_keys); diff --git a/redis_cluster.c b/redis_cluster.c index 5ff2c87513..b5be177673 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -604,7 +604,7 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, // Grab the key, convert it to a string using provided kbuf buffer if it's // a LONG style key #if (PHP_MAJOR_VERSION < 7) - int key_len; + uint key_len; switch(zend_hash_get_current_key_ex(ht, &(kv->key), &key_len, &idx, 0, ptr)) { case HASH_KEY_IS_STRING: kv->key_len = (int)(key_len-1); @@ -862,7 +862,7 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, cluster_multi_add(&mc, kv.key, kv.key_len); cluster_multi_add(&mc, kv.val, kv.val_len); if(kv.key_free) efree(kv.key); - if(kv.val_free) STR_FREE(kv.val); + if(kv.val_free) efree(kv.val); // While we've got more keys to set slot = kv.slot; @@ -887,7 +887,7 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Free our key and value if we need to if(kv.key_free) efree(kv.key); - if(kv.val_free) STR_FREE(kv.val); + if(kv.val_free) efree(kv.val); // Update our slot, increment position slot = kv.slot; diff --git a/redis_commands.c b/redis_commands.c index dbf0a9523a..1eae714e59 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -130,7 +130,7 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set the slot if directed CMD_SET_SLOT(slot,key,key_len); - if(val_free) STR_FREE(val); + if(val_free) efree(val); if(key_free) efree(key); return SUCCESS; @@ -192,7 +192,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set our slot if directed CMD_SET_SLOT(slot,key,key_len); - if(val_free) STR_FREE(val); + if(val_free) efree(val); if(key_free) efree(key); return SUCCESS; @@ -985,7 +985,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, for(i=1;i= 50400 - if(val_free) str_efree(val); -#else if(val_free) efree(val); -#endif return SUCCESS; } @@ -1454,7 +1450,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - zval *z_arr, **z_mems, *z_mem; + zval *z_arr, *z_mems, *z_mem; int i, count, valid=0, key_len, key_free; HashTable *ht_arr; smart_string cmdstr = {0}; @@ -1478,7 +1474,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); // Allocate memory for mems+1 so we can have a sentinel - z_mems = ecalloc(count+1, sizeof(zval*)); + z_mems = ecalloc(count + 1, sizeof(zval)); // Iterate over our member array ZEND_HASH_FOREACH_VAL(ht_arr, z_mem) { @@ -1487,10 +1483,9 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, || Z_TYPE_P(z_mem) == IS_LONG ) { // Copy into our member array - MAKE_STD_ZVAL(z_mems[valid]); - *z_mems[valid] = *z_mem; - zval_copy_ctor(z_mems[valid]); - convert_to_string(z_mems[valid]); + z_mems[valid] = *z_mem; + zval_copy_ctor(&z_mems[valid]); + convert_to_string(&z_mems[valid]); // Increment the member count to actually send valid++; @@ -1506,7 +1501,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Sentinel so we can free this even if it's used and then we discard // the transaction manually or there is a transaction failure - z_mems[valid]=NULL; + ZVAL_NULL(&z_mems[valid]); // Start command construction redis_cmd_init_sstr(&cmdstr, valid+1, "HMGET", sizeof("HMGET")-1); @@ -1514,8 +1509,8 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate over members, appending as arguments for(i=0;i Date: Thu, 3 Nov 2016 21:12:03 +0200 Subject: [PATCH 0613/1986] redis_sock_read_multibulk_reply_zval --- library.c | 57 ++++++++++++++++++++++--------------------------------- library.h | 2 +- redis.c | 19 ++++++++----------- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/library.c b/library.c index 00a619b6d7..33d07006d5 100644 --- a/library.c +++ b/library.c @@ -258,35 +258,32 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval *z_tmp, *z_ret; + zval *z_tmp, *z_ret, z_resp; // Consume response(s) from subscribe, which will vary on argc while(sctx->argc--) { - z_tab = redis_sock_read_multibulk_reply_zval( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - if(!z_tab) { + if (!redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp) + ) { efree(sctx); return -1; } // We'll need to find the command response - if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(z_tab), 0)) == NULL) { - zval_dtor(z_tab); - efree(z_tab); + if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_resp), 0)) == NULL) { + zval_dtor(&z_resp); efree(sctx); return -1; } // Make sure the command response matches the command we called if(strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) !=0) { - zval_dtor(z_tab); - efree(z_tab); + zval_dtor(&z_resp); efree(sctx); return -1; } - zval_dtor(z_tab); - efree(z_tab); + zval_dtor(&z_resp); } #if (PHP_MAJOR_VERSION < 7) @@ -305,11 +302,10 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, HashTable *ht_tab; int tab_idx=1, is_pmsg; - z_tab = redis_sock_read_multibulk_reply_zval( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - if(z_tab == NULL) break; + if (!redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)) break; - ht_tab = Z_ARRVAL_P(z_tab); + ht_tab = Z_ARRVAL(z_resp); if ((z_type = zend_hash_index_find(ht_tab, 0)) == NULL || Z_TYPE_P(z_type) != IS_STRING @@ -376,15 +372,11 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, // If we have a return value free it if(z_ret) zval_ptr_dtor(&z_ret); - zval_dtor(z_tab); - efree(z_tab); + zval_dtor(&z_resp); } // This is an error state, clean up - if(z_tab) { - zval_dtor(z_tab); - efree(z_tab); - } + zval_dtor(&z_resp); efree(sctx); return -1; @@ -395,17 +387,17 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval *z_chan, *z_ret; - int i=0; + zval *z_chan, *z_ret, z_resp; + int i; MAKE_STD_ZVAL(z_ret); array_init(z_ret); - while(iargc) { - z_tab = redis_sock_read_multibulk_reply_zval( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - - if (!z_tab || (z_chan = zend_hash_index_find(Z_ARRVAL_P(z_tab), 1)) == NULL) { + for (i = 0; i < sctx->argc; i++) { + if (!redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp) || + (z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL + ) { zval_dtor(z_ret); efree(z_ret); return -1; @@ -413,9 +405,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, add_assoc_bool(z_ret, Z_STRVAL_P(z_chan), 1); - zval_dtor(z_tab); - efree(z_tab); - i++; + zval_dtor(&z_resp); } efree(sctx); @@ -428,10 +418,10 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API zval * redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock) { + RedisSock *redis_sock, zval *z_tab) +{ char inbuf[1024]; int numElems; - zval *z_tab; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return NULL; @@ -449,7 +439,6 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, } numElems = atoi(inbuf+1); - MAKE_STD_ZVAL(z_tab); array_init(z_tab); redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, diff --git a/library.h b/library.h index ed8428e8ef..417eb3f830 100644 --- a/library.h +++ b/library.h @@ -37,7 +37,7 @@ PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned sh PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); diff --git a/redis.c b/redis.c index ef55d1f584..6a6fd5e5a4 100644 --- a/redis.c +++ b/redis.c @@ -2529,7 +2529,7 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, int cmd_len, array_count; int i; - zval *z_tab, *z_channel; + zval z_tab, *z_channel; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_ce, &array) == FAILURE) { @@ -2569,25 +2569,22 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, } efree(cmd); - i = 1; array_init(return_value); + for (i = 1; i <= array_count; i++) { + redis_sock_read_multibulk_reply_zval( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_tab); - while( i <= array_count) { - z_tab = redis_sock_read_multibulk_reply_zval( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); - - if(Z_TYPE_P(z_tab) == IS_ARRAY) { - if ((z_channel = zend_hash_index_find(Z_ARRVAL_P(z_tab), 1)) == NULL) { + if (Z_TYPE(z_tab) == IS_ARRAY) { + if ((z_channel = zend_hash_index_find(Z_ARRVAL(z_tab), 1)) == NULL) { RETURN_FALSE; } add_assoc_bool(return_value, Z_STRVAL_P(z_channel), 1); } else { //error - efree(z_tab); + zval_dtor(&z_tab); RETURN_FALSE; } - efree(z_tab); - i++; + zval_dtor(&z_tab); } } From 8f3deb7f34765a477fba5cc367460758e9c07e16 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 4 Nov 2016 17:58:34 +0200 Subject: [PATCH 0614/1986] Fix static analyzer warnings --- cluster_library.c | 4 ++++ library.c | 5 +++-- redis.c | 7 +++---- redis_array.c | 3 +-- redis_array_impl.c | 4 ++-- redis_cluster.c | 2 +- redis_commands.c | 13 +++++++++---- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 2181826e6b..a3dc209809 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -589,6 +589,9 @@ unsigned short cluster_hash_key_zval(zval *z_key) { kptr = "Object"; klen = sizeof("Object")-1; break; + default: + kptr = ""; + klen = 0; } // Hash the string representation @@ -1498,6 +1501,7 @@ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } else { add_next_index_bool(c->multi_resp, 0); + return; } } diff --git a/library.c b/library.c index 33d07006d5..41f69412a3 100644 --- a/library.c +++ b/library.c @@ -836,10 +836,10 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); + return; } else { RETURN_FALSE; } - return; } ret = atof(response); @@ -859,6 +859,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { IF_MULTI_OR_PIPELINE() { add_next_index_bool(z_tab, 0); + return; } else { RETURN_FALSE; } @@ -1940,7 +1941,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { - if(redis_sock && redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { + if (!redis_sock || redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); return -1; diff --git a/redis.c b/redis.c index 6a6fd5e5a4..8358e81067 100644 --- a/redis.c +++ b/redis.c @@ -2454,9 +2454,8 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, int numElems) { - fold_item *head = redis_sock->head; - fold_item *current = redis_sock->current; - for(current = head; current; current = current->next) { + fold_item *current; + for (current = redis_sock->head; current; current = current->next) { fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, z_tab); } @@ -2552,7 +2551,7 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, if(*cmd) { old_cmd = cmd; } - cmd_len = spprintf(&cmd, 0, "%s %s", cmd, Z_STRVAL_P(data)); + spprintf(&cmd, 0, "%s %s", cmd, Z_STRVAL_P(data)); if(old_cmd) { efree(old_cmd); } diff --git a/redis_array.c b/redis_array.c index b3078683cf..33ec8b08d1 100644 --- a/redis_array.c +++ b/redis_array.c @@ -341,8 +341,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i zval *zp_tmp, z_tmp; char *key = NULL; /* set to avoid "unused-but-set-variable" */ - int key_len; - int i; + int i, key_len = 0; zval *redis_inst; zval z_fun, *z_callargs; HashTable *h_args; diff --git a/redis_array_impl.c b/redis_array_impl.c index 7e814fef23..f076c1f2ce 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -1122,7 +1122,7 @@ ra_move_list(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS void ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { - long res[2], type, ttl; + long res[2] = {0}, type, ttl; zend_bool success = 0; if (ra_get_key_type(z_from, key, key_len, z_from, res TSRMLS_CC)) { type = res[0]; @@ -1224,7 +1224,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool /* check that we're not moving to the same node. */ z_target = ra_find_node(ra, keys[i], key_lens[i], &target_pos TSRMLS_CC); - if(strcmp(hostname, ra->hosts[target_pos])) { /* different host */ + if (z_target && strcmp(hostname, ra->hosts[target_pos])) { /* different host */ /* php_printf("move [%s] from [%s] to [%s]\n", keys[i], hostname, ra->hosts[target_pos]); */ ra_move_key(keys[i], key_lens[i], z_redis, z_target TSRMLS_CC); } diff --git a/redis_cluster.c b/redis_cluster.c index b5be177673..92af79be91 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1889,7 +1889,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, zval *z_arr=NULL, *z_ele; HashTable *ht_arr; long num_keys = 0; - short slot; + short slot = 0; smart_string cmdstr = {0}; /* Parse args */ diff --git a/redis_commands.c b/redis_commands.c index 1eae714e59..4454159ea5 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2986,11 +2986,16 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Construct our command */ if(!kw) { *cmd_len = redis_cmd_format_static(cmd, "COMMAND", ""); - } else if(kw && !z_arg) { + } else if (!z_arg) { /* Sanity check */ - if(strncasecmp(kw, "info", sizeof("info")-1) || - Z_TYPE_P(z_arg)!=IS_STRING) - { + if (strncasecmp(kw, "count", sizeof("count") - 1)) { + return FAILURE; + } + /* COMMAND COUNT */ + *cmd_len = redis_cmd_format_static(cmd, "COMMAND", "s", "COUNT", sizeof("COUNT") - 1); + } else if (Z_TYPE_P(z_arg) == IS_STRING) { + /* Sanity check */ + if (strncasecmp(kw, "info", sizeof("info") - 1)) { return FAILURE; } From 2aa64eb68d944237179dee4ce081557bff0e37d1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 5 Nov 2016 16:38:04 +0200 Subject: [PATCH 0615/1986] refactoring Using zval* instead of zval** in redis_read_variant_line, redis_read_variant_bulk and redis_read_multibulk_recursive --- library.c | 34 +++++++++++++++++----------------- library.h | 6 +++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/library.c b/library.c index 41f69412a3..259cd88070 100644 --- a/library.c +++ b/library.c @@ -2226,7 +2226,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, */ PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, - zval **z_ret TSRMLS_DC) + zval *z_ret TSRMLS_DC) { // Buffer to read our single line reply char inbuf[1024]; @@ -2247,17 +2247,17 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, redis_sock_set_err(redis_sock, inbuf, line_size); /* Set our response to FALSE */ - ZVAL_FALSE(*z_ret); + ZVAL_FALSE(z_ret); } else { /* Set our response to TRUE */ - ZVAL_TRUE(*z_ret); + ZVAL_TRUE(z_ret); } return 0; } PHP_REDIS_API int -redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret +redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret TSRMLS_DC) { // Attempt to read the bulk reply @@ -2265,16 +2265,16 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret /* Set our reply to FALSE on failure, and the string on success */ if(bulk_resp == NULL) { - ZVAL_FALSE(*z_ret); + ZVAL_FALSE(z_ret); return -1; } - ZVAL_STRINGL(*z_ret, bulk_resp, size); + ZVAL_STRINGL(z_ret, bulk_resp, size); efree(bulk_resp); return 0; } PHP_REDIS_API int -redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret +redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret TSRMLS_DC) { long reply_info; @@ -2298,29 +2298,29 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret case TYPE_ERR: case TYPE_LINE: ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_line(redis_sock, reply_type, &z_subelem + redis_read_variant_line(redis_sock, reply_type, z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); + add_next_index_zval(z_ret, z_subelem); break; case TYPE_INT: // Add our long value - add_next_index_long(*z_ret, reply_info); + add_next_index_long(z_ret, reply_info); break; case TYPE_BULK: // Init a zval for our bulk response, read and add it ALLOC_INIT_ZVAL(z_subelem); - redis_read_variant_bulk(redis_sock, reply_info, &z_subelem + redis_read_variant_bulk(redis_sock, reply_info, z_subelem TSRMLS_CC); - add_next_index_zval(*z_ret, z_subelem); + add_next_index_zval(z_ret, z_subelem); break; case TYPE_MULTIBULK: // Construct an array for our sub element, and add it, // and recurse ALLOC_INIT_ZVAL(z_subelem); array_init(z_subelem); - add_next_index_zval(*z_ret, z_subelem); + add_next_index_zval(z_ret, z_subelem); redis_read_multibulk_recursive(redis_sock, reply_info, - &z_subelem TSRMLS_CC); + z_subelem TSRMLS_CC); break; default: // Stop the compiler from whinging @@ -2357,13 +2357,13 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, switch(reply_type) { case TYPE_ERR: case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, &z_ret TSRMLS_CC); + redis_read_variant_line(redis_sock, reply_type, z_ret TSRMLS_CC); break; case TYPE_INT: ZVAL_LONG(z_ret, reply_info); break; case TYPE_BULK: - redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); + redis_read_variant_bulk(redis_sock, reply_info, z_ret TSRMLS_CC); break; case TYPE_MULTIBULK: /* Initialize an array for our multi-bulk response */ @@ -2372,7 +2372,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If we've got more than zero elements, parse our multi bulk // response recursively if(reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret + redis_read_multibulk_recursive(redis_sock, reply_info, z_ret TSRMLS_CC); } break; diff --git a/library.h b/library.h index 417eb3f830..a139a810c6 100644 --- a/library.h +++ b/library.h @@ -77,9 +77,9 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **re */ PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info TSRMLS_DC); -PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret TSRMLS_DC); -PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret TSRMLS_DC); -PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC); +PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); From f5024a59deab2d61aed7f64ee91cc926651e3d01 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 5 Nov 2016 17:09:28 +0200 Subject: [PATCH 0616/1986] WIP: php7 compatibility Try to not use MAKE_STD_ZVAL --- common.h | 1 + library.c | 83 +++++++++++++++++++++++------------------------- redis_commands.c | 61 ++++++++++++++++------------------- 3 files changed, 68 insertions(+), 77 deletions(-) diff --git a/common.h b/common.h index 7d943184a7..9df136a9c3 100644 --- a/common.h +++ b/common.h @@ -308,6 +308,7 @@ static int (*_php_var_unserialize)(zval **, const unsigned char **, const unsign #define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) #else +#include #include #endif diff --git a/library.c b/library.c index 259cd88070..3128005121 100644 --- a/library.c +++ b/library.c @@ -387,10 +387,9 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval *z_chan, *z_ret, z_resp; + zval *z_chan, zv, *z_ret = &zv, z_resp; int i; - MAKE_STD_ZVAL(z_ret); array_init(z_ret); for (i = 0; i < sctx->argc; i++) { @@ -399,7 +398,6 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, (z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL ) { zval_dtor(z_ret); - efree(z_ret); return -1; } @@ -1204,10 +1202,9 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int decode TSRMLS_DC) { - zval *z_ret; + zval zv, *z_ret = &zv; HashTable *keytable; - MAKE_STD_ZVAL(z_ret); array_init(z_ret); keytable = Z_ARRVAL_P(z_tab); @@ -1255,11 +1252,7 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, /* replace */ zval_dtor(z_tab); - *z_tab = *z_ret; - zval_copy_ctor(z_tab); - zval_dtor(z_ret); - - efree(z_ret); + ZVAL_ZVAL(z_tab, z_ret, 1, 0); } static int @@ -1268,7 +1261,6 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char inbuf[1024]; int numElems; - zval *z_multi_result; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; @@ -1288,7 +1280,12 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } numElems = atoi(inbuf+1); +#if (PHP_MAJOR_VERSION < 7) + zval *z_multi_result; MAKE_STD_ZVAL(z_multi_result); +#else + zval zv, *z_multi_result = &zv; +#endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ /* Grab our key, value, key, value array */ @@ -1301,10 +1298,7 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); + RETVAL_ZVAL(z_multi_result, 0, 1); } return 0; @@ -1753,7 +1747,6 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, { char inbuf[1024]; int numElems, err_len; - zval *z_multi_result; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; @@ -1778,7 +1771,12 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, return -1; } numElems = atoi(inbuf+1); +#if (PHP_MAJOR_VERSION < 7) + zval *z_multi_result; MAKE_STD_ZVAL(z_multi_result); +#else + zval zv, *z_multi_result = &zv; +#endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, @@ -1787,8 +1785,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - efree(z_multi_result); + RETVAL_ZVAL(z_multi_result, 0, 1); } /*zval_copy_ctor(return_value); */ return 0; @@ -1800,7 +1797,6 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock { char inbuf[1024]; int numElems, err_len; - zval *z_multi_result; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; @@ -1824,7 +1820,12 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock return -1; } numElems = atoi(inbuf+1); +#if (PHP_MAJOR_VERSION < 7) + zval *z_multi_result; MAKE_STD_ZVAL(z_multi_result); +#else + zval zv, *z_multi_result = &zv; +#endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, @@ -1833,8 +1834,7 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - efree(z_multi_result); + RETVAL_ZVAL(z_multi_result, 0, 1); } /*zval_copy_ctor(return_value); */ return 0; @@ -1881,7 +1881,6 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc char inbuf[1024], *response; int response_len; int i, numElems; - zval *z_multi_result; zval *z_keys = ctx; @@ -1903,7 +1902,12 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc return -1; } numElems = atoi(inbuf+1); +#if (PHP_MAJOR_VERSION < 7) + zval *z_multi_result; MAKE_STD_ZVAL(z_multi_result); +#else + zval zv, *z_multi_result = &zv; +#endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ for(i = 0; i < numElems; ++i) { @@ -1926,11 +1930,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc IF_MULTI_OR_PIPELINE() { add_next_index_zval(z_tab, z_multi_result); } else { - *return_value = *z_multi_result; - zval_copy_ctor(return_value); - INIT_PZVAL(return_value); - zval_dtor(z_multi_result); - efree(z_multi_result); + RETVAL_ZVAL(z_multi_result, 0, 1); } return 0; } @@ -1996,27 +1996,25 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len case IS_STRING: *val = Z_STRVAL_P(z); *val_len = Z_STRLEN_P(z); - return 0; + break; case IS_OBJECT: *val = "Object"; *val_len = 6; - return 0; + break; case IS_ARRAY: *val = "Array"; *val_len = 5; - return 0; + break; default: /* copy */ - z_copy = *z; - zval_copy_ctor(&z_copy); + ZVAL_ZVAL(&z_copy, z, 1, 0); convert_to_string(&z_copy); *val = Z_STRVAL(z_copy); *val_len = Z_STRLEN(z_copy); } - return 1; - + break; case REDIS_SERIALIZER_PHP: #if ZEND_MODULE_API_NO >= 20100000 @@ -2342,7 +2340,6 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type; long reply_info; //char *bulk_resp; - zval *z_ret; // Attempt to read our header if(redis_read_reply_type(redis_sock,&reply_type,&reply_info TSRMLS_CC) < 0) @@ -2350,9 +2347,12 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } - /* Our return ZVAL */ - MAKE_STD_ZVAL(z_ret); - +#if (PHP_MAJOR_VERSION < 7) + zval *z_ret; + MAKE_STD_ZVAL(z_ret); +#else + zval zv, *z_ret = &zv; +#endif /* Switch based on our top level reply type */ switch(reply_type) { case TYPE_ERR: @@ -2378,8 +2378,6 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, break; default: // Protocol error - zval_dtor(z_ret); - efree(z_ret); zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); return FAILURE; @@ -2389,10 +2387,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, add_next_index_zval(z_tab, z_ret); } else { /* Set our return value */ - *return_value = *z_ret; - zval_copy_ctor(return_value); - zval_dtor(z_ret); - efree(z_ret); + RETVAL_ZVAL(z_ret, 0, 1); } /* Success */ diff --git a/redis_commands.c b/redis_commands.c index 4454159ea5..165fc10cf4 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -88,7 +88,7 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *arg; - int arg_len; + size_t arg_len; // Parse args if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) @@ -1181,8 +1181,9 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_value, *z_opts=NULL; char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; - int key_len, val_len, key_free, val_free; + int key_free, val_free; long expire = -1; + size_t key_len, val_len; // Make sure the function is being called correctly if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, @@ -1608,8 +1609,9 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - int argc, key_len, key_free; + int argc, key_free; long bit, start, end; + size_t key_len; argc = ZEND_NUM_ARGS(); if(zend_parse_parameters(argc TSRMLS_CC, "sl|ll", &key, &key_len, &bit, @@ -2285,7 +2287,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int *using_store, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_opts=NULL, *z_ele, *z_argv; + zval *z_opts=NULL, *z_ele, z_argv; char *key; HashTable *ht_opts; smart_string cmdstr = {0}; @@ -2317,11 +2319,10 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Create our hash table to hold our sort arguments - ALLOC_INIT_ZVAL(z_argv); - array_init(z_argv); + array_init(&z_argv); // SORT - add_next_index_stringl(z_argv, key, key_len); + add_next_index_stringl(&z_argv, key, key_len); if (key_free) efree(key); // Set slot @@ -2340,14 +2341,13 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, php_error_docref(NULL TSRMLS_CC, E_WARNING, "SORT BY option is not allowed in Redis Cluster"); if(key_free) efree(key); - zval_dtor(z_argv); - efree(z_argv); + zval_dtor(&z_argv); return FAILURE; } // ... BY - add_next_index_stringl(z_argv, "BY", sizeof("BY") - 1); - add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + add_next_index_stringl(&z_argv, "BY", sizeof("BY") - 1); + add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } // Handle ASC/DESC option @@ -2356,7 +2356,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) && Z_TYPE_P(z_ele) == IS_STRING ) { // 'asc'|'desc' - add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } // STORE option @@ -2372,14 +2372,13 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, php_error_docref(0 TSRMLS_CC, E_WARNING, "Error, SORT key and STORE key have different slots!"); if(key_free) efree(key); - zval_dtor(z_argv); - efree(z_argv); + zval_dtor(&z_argv); return FAILURE; } // STORE - add_next_index_stringl(z_argv, "STORE", sizeof("STORE") - 1); - add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + add_next_index_stringl(&z_argv, "STORE", sizeof("STORE") - 1); + add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); // We are using STORE *using_store = 1; @@ -2395,15 +2394,14 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, php_error_docref(NULL TSRMLS_CC, E_WARNING, "GET option for SORT disabled in Redis Cluster"); if(key_free) efree(key); - zval_dtor(z_argv); - efree(z_argv); + zval_dtor(&z_argv); return FAILURE; } // If it's a string just add it if (Z_TYPE_P(z_ele) == IS_STRING) { - add_next_index_stringl(z_argv, "GET", sizeof("GET") - 1); - add_next_index_stringl(z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + add_next_index_stringl(&z_argv, "GET", sizeof("GET") - 1); + add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { HashTable *ht_keys = Z_ARRVAL_P(z_ele); int added=0; @@ -2419,10 +2417,10 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, continue; } /* Add get per thing we're getting */ - add_next_index_stringl(z_argv, "GET", sizeof("GET") - 1); + add_next_index_stringl(&z_argv, "GET", sizeof("GET") - 1); // Add this key to our argv array - add_next_index_stringl(z_argv, Z_STRVAL_P(z_key), Z_STRLEN_P(z_key)); + add_next_index_stringl(&z_argv, Z_STRVAL_P(z_key), Z_STRLEN_P(z_key)); added++; } @@ -2431,8 +2429,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array of GET values requested, but none are valid"); if(key_free) efree(key); - zval_dtor(z_argv); - efree(z_argv); + zval_dtor(&z_argv); return FAILURE; } } @@ -2443,7 +2440,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL) && (ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(z_ele)) && Z_LVAL_P(z_ele)) ) { - add_next_index_stringl(z_argv, "ALPHA", sizeof("ALPHA") - 1); + add_next_index_stringl(&z_argv, "ALPHA", sizeof("ALPHA") - 1); } // LIMIT @@ -2463,13 +2460,12 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, php_error_docref(NULL TSRMLS_CC, E_WARNING, "LIMIT options on SORT command must be longs or strings"); if(key_free) efree(key); - zval_dtor(z_argv); - efree(z_argv); + zval_dtor(&z_argv); return FAILURE; } // Add LIMIT argument - add_next_index_stringl(z_argv, "LIMIT", sizeof("LIMIT") - 1); + add_next_index_stringl(&z_argv, "LIMIT", sizeof("LIMIT") - 1); long low, high; if (Z_TYPE_P(z_off) == IS_STRING) { @@ -2484,13 +2480,13 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Add our two LIMIT arguments - add_next_index_long(z_argv, low); - add_next_index_long(z_argv, high); + add_next_index_long(&z_argv, low); + add_next_index_long(&z_argv, high); } } // Start constructing our command - HashTable *ht_argv = Z_ARRVAL_P(z_argv); + HashTable *ht_argv = Z_ARRVAL_P(&z_argv); redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(ht_argv), "SORT", sizeof("SORT")-1); @@ -2510,8 +2506,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Clean up our arguments array. Note we don't have to free any prefixed * key as that we didn't duplicate the pointer if we prefixed */ - zval_dtor(z_argv); - efree(z_argv); + zval_dtor(&z_argv); // Push our length and command *cmd_len = cmdstr.len; From 5de84b3d01e36d899f21ea5aa487f657a253c12f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 7 Nov 2016 09:02:44 +0200 Subject: [PATCH 0617/1986] WIP: php7 compatibility --- cluster_library.c | 13 +++-- common.h | 2 +- library.c | 74 +++++++++++++++++------- library.h | 2 +- redis.c | 2 +- redis_array.c | 2 +- redis_array_impl.c | 2 +- redis_commands.c | 139 +++++++++++++++++++-------------------------- 8 files changed, 126 insertions(+), 110 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index a3dc209809..fb0b2b8ad7 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2055,7 +2055,6 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - zval *z_result; char *info; /* Read the bulk response */ @@ -2064,13 +2063,19 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC CLUSTER_RETURN_FALSE(c); } +#if (PHP_MAJOR_VERSION < 7) + zval *z_result; + MAKE_STD_ZVAL(z_result); +#else + zval zv, *z_result = &zv; +#endif + /* Parse it and free the bulk string */ - z_result = redis_parse_client_list_response(info); + redis_parse_client_list_response(info, z_result); efree(info); if (CLUSTER_IS_ATOMIC(c)) { - *return_value = *z_result; - efree(z_result); + RETVAL_ZVAL(z_result, 0, 1); } else { add_next_index_zval(c->multi_resp, z_result); } diff --git a/common.h b/common.h index 9df136a9c3..b21b72298b 100644 --- a/common.h +++ b/common.h @@ -32,7 +32,7 @@ typedef struct { char *_str_index; uint _str_length; ulong _num_index; \ switch (zend_hash_get_current_key_ex(ht, &_str_index, &_str_length, &_num_index, 0, &_hpos)) { \ case HASH_KEY_IS_STRING: \ - _zstr.len = _str_length; \ + _zstr.len = _str_length - 1; \ _zstr.val = _str_index; \ _key = &_zstr; \ break; \ diff --git a/library.c b/library.c index 3128005121..4b01eb54d7 100644 --- a/library.c +++ b/library.c @@ -973,15 +973,21 @@ redis_parse_info_response(char *response, zval *z_ret) PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { char *resp; int resp_len; - zval *z_ret; /* Make sure we can read the bulk response from Redis */ if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { RETURN_FALSE; } +#if (PHP_MAJOR_VERSION < 7) + zval *z_ret; + MAKE_STD_ZVAL(z_ret); +#else + zval zv, *z_ret = &zv; +#endif + /* Parse it out */ - z_ret = redis_parse_client_list_response(resp); + redis_parse_client_list_response(resp, z_ret); /* Free our response */ efree(resp); @@ -994,17 +1000,22 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo } } -PHP_REDIS_API zval* redis_parse_client_list_response(char *response) { - zval *z_result, *z_sub_result; +PHP_REDIS_API void +redis_parse_client_list_response(char *response, zval *z_ret) +{ char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value; int klen = 0, done = 0, is_numeric; // Allocate memory for our response - MAKE_STD_ZVAL(z_result); - array_init(z_result); + array_init(z_ret); /* Allocate memory for one user (there should be at least one, namely us!) */ +#if (PHP_MAJOR_VERSION < 7) + zval *z_sub_result; ALLOC_INIT_ZVAL(z_sub_result); +#else + zval zv, *z_sub_result = &zv; +#endif array_init(z_sub_result); // Pointers for parsing @@ -1053,11 +1064,13 @@ PHP_REDIS_API zval* redis_parse_client_list_response(char *response) { // If we hit a '\n', then we can add this user to our list if(*p == '\n') { /* Add our user */ - add_next_index_zval(z_result, z_sub_result); + add_next_index_zval(z_ret, z_sub_result); /* If we have another user, make another one */ if(*(p+1) != '\0') { +#if (PHP_MAJOR_VERSION < 7) ALLOC_INIT_ZVAL(z_sub_result); +#endif array_init(z_sub_result); } } @@ -1066,10 +1079,9 @@ PHP_REDIS_API zval* redis_parse_client_list_response(char *response) { efree(key); } else { // Something is wrong - zval_dtor(z_result); - MAKE_STD_ZVAL(z_result); - ZVAL_BOOL(z_result, 0); - return z_result; + zval_dtor(z_ret); + ZVAL_BOOL(z_ret, 0); + return; } /* Move forward */ @@ -1091,9 +1103,6 @@ PHP_REDIS_API zval* redis_parse_client_list_response(char *response) { /* Increment */ p++; } - - /* Return our parsed response */ - return z_result; } PHP_REDIS_API void @@ -1242,10 +1251,11 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, } else if (decode == SCORE_DECODE_DOUBLE) { add_assoc_double_ex(z_ret, hkey, hkey_len, atof(hval)); } else { - zval *z = NULL; + zval zv, *z = &zv; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); - *z = *z_value_p; - zval_copy_ctor(z); +#endif + ZVAL_ZVAL(z, z_value_p, 1, 0); add_assoc_zval_ex(z_ret, hkey, hkey_len, z); } } @@ -1253,6 +1263,7 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, /* replace */ zval_dtor(z_tab); ZVAL_ZVAL(z_tab, z_ret, 1, 0); + zval_dtor(z_ret); } static int @@ -1386,7 +1397,10 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock RETURN_FALSE; } IF_MULTI_OR_PIPELINE() { - zval *z = NULL; + zval zv, *z = &zv; +#if (PHP_MAJOR_VERSION < 7) + z = NULL; +#endif if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { @@ -1436,7 +1450,6 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock { char *resp, *p, *p2, *p3, *p4; int is_numeric, resp_len; - zval *z_result; /* Add or return false if we can't read from the socket */ if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC))==NULL) { @@ -1447,7 +1460,12 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock RETURN_FALSE; } +#if (PHP_MAJOR_VERSION < 7) + zval *z_result; MAKE_STD_ZVAL(z_result); +#else + zval zv, *z_result = &zv; +#endif array_init(z_result); /* Skip the '+' */ @@ -1850,7 +1868,10 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, while(count > 0) { line = redis_sock_read(redis_sock, &len TSRMLS_CC); if (line != NULL) { - zval *z = NULL; + zval zv, *z = &zv; +#if (PHP_MAJOR_VERSION < 7) + z = NULL; +#endif int unwrap; /* We will attempt unserialization, if we're unserializing everything, @@ -1913,7 +1934,10 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval *z = NULL; + zval zv, *z = &zv; +#if (PHP_MAJOR_VERSION < 7) + z = NULL; +#endif if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { add_assoc_zval_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), z); } else { @@ -2277,7 +2301,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret { long reply_info; REDIS_REPLY_TYPE reply_type; - zval *z_subelem; + zval zv, *z_subelem = &zv; // Iterate while we have elements while(elements > 0) { @@ -2295,7 +2319,9 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret switch(reply_type) { case TYPE_ERR: case TYPE_LINE: +#if (PHP_MAJOR_VERSION < 7) ALLOC_INIT_ZVAL(z_subelem); +#endif redis_read_variant_line(redis_sock, reply_type, z_subelem TSRMLS_CC); add_next_index_zval(z_ret, z_subelem); @@ -2306,7 +2332,9 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret break; case TYPE_BULK: // Init a zval for our bulk response, read and add it +#if (PHP_MAJOR_VERSION < 7) ALLOC_INIT_ZVAL(z_subelem); +#endif redis_read_variant_bulk(redis_sock, reply_info, z_subelem TSRMLS_CC); add_next_index_zval(z_ret, z_subelem); @@ -2314,7 +2342,9 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret case TYPE_MULTIBULK: // Construct an array for our sub element, and add it, // and recurse +#if (PHP_MAJOR_VERSION < 7) ALLOC_INIT_ZVAL(z_subelem); +#endif array_init(z_subelem); add_next_index_zval(z_ret, z_subelem); redis_read_multibulk_recursive(redis_sock, reply_info, diff --git a/library.h b/library.h index a139a810c6..86f1266311 100644 --- a/library.h +++ b/library.h @@ -31,7 +31,7 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); -PHP_REDIS_API zval *redis_parse_client_list_response(char *resp); +PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index 8358e81067..d75a3baa4b 100644 --- a/redis.c +++ b/redis.c @@ -1871,7 +1871,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { if (zkey) { key = zkey->val; - key_len = zkey->len - 1; + key_len = zkey->len; } else { // Create string representation of our index key_len = snprintf(buf, sizeof(buf), "%ld", (long)idx); diff --git a/redis_array.c b/redis_array.c index 33ec8b08d1..842a7c8917 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1021,7 +1021,7 @@ PHP_METHOD(RedisArray, mset) ZEND_HASH_FOREACH_KEY_VAL(h_keys, idx, zkey, data) { /* If the key isn't a string, make a string representation of it */ if (zkey) { - key_len = zkey->len - 1; + key_len = zkey->len; key = zkey->val; } else { key_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); diff --git a/redis_array_impl.c b/redis_array_impl.c index f076c1f2ce..ed703c46b5 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -929,7 +929,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* add value */ INIT_ZVAL(z_zadd_args[i+1]); if (zkey) { - ZVAL_STRINGL(&z_zadd_args[i+1], zkey->val, zkey->len - 1); + ZVAL_STRINGL(&z_zadd_args[i+1], zkey->val, zkey->len); } else { ZVAL_LONG(&z_zadd_args[i+1], (long)idx); } diff --git a/redis_commands.c b/redis_commands.c index 165fc10cf4..fb73846a05 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -498,9 +498,9 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* ZRANGEBYSCORE/ZREVRANGEBYSCORE */ #define IS_WITHSCORES_ARG(s, l) \ - (l == sizeof("withscores") && !strncasecmp(s,"withscores",sizeof("withscores"))) + (l == sizeof("withscores") - 1 && !strncasecmp(s, "withscores", l)) #define IS_LIMIT_ARG(s, l) \ - (l == sizeof("limit") && !strncasecmp(s,"limit",sizeof("limit"))) + (l == sizeof("limit") - 1 && !strncasecmp(s,"limit", l)) int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, @@ -655,18 +655,18 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_keys, z_ele) { char *key; int key_free, key_len; - zval *z_tmp = NULL; + zval z_tmp; + ZVAL_NULL(&z_tmp); if (Z_TYPE_P(z_ele) == IS_STRING) { key = Z_STRVAL_P(z_ele); key_len = Z_STRLEN_P(z_ele); } else { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_ele; - convert_to_string(z_tmp); + ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); + convert_to_string(&z_tmp); - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); + key = Z_STRVAL(z_tmp); + key_len = Z_STRLEN(z_tmp); } // Prefix key if necissary @@ -678,10 +678,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "All keys don't hash to the same slot!"); efree(cmdstr.c); if(key_free) efree(key); - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - } + zval_dtor(&z_tmp); return FAILURE; } @@ -690,11 +687,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Cleanup if(key_free) efree(key); - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } + zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); // Weights @@ -1572,7 +1565,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If the hash key is an integer, convert it to a string if (zkey) { - mem_len = zkey->len - 1; + mem_len = zkey->len; mem = zkey->val; } else { mem_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); @@ -1783,18 +1776,20 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Now iterate over the rest of our keys or values ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - zval *z_tmp = NULL; + zval z_tmp; + ZVAL_NULL(&z_tmp); // Prefix keys, serialize values if(is_keys) { - if (Z_TYPE_P(z_ele) != IS_STRING) { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_ele; - convert_to_string(z_tmp); - z_ele = z_tmp; + if (Z_TYPE_P(z_ele) == IS_STRING) { + mem = Z_STRVAL_P(z_ele); + mem_len = Z_STRLEN_P(z_ele); + } else { + ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); + convert_to_string(&z_tmp); + mem = Z_STRVAL(z_tmp); + mem_len = Z_STRLEN(z_tmp); } - mem = Z_STRVAL_P(z_ele); - mem_len = Z_STRLEN_P(z_ele); // Key prefix mem_free = redis_key_prefix(redis_sock, &mem, &mem_len); @@ -1804,10 +1799,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, php_error_docref(0 TSRMLS_CC, E_WARNING, "All keys must hash to the same slot!"); if(key_free) efree(key); - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - } + zval_dtor(&z_tmp); return FAILURE; } } else { @@ -1815,14 +1807,15 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, TSRMLS_CC); if(!mem_free) { - if (Z_TYPE_P(z_ele) != IS_STRING) { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_ele; - convert_to_string(z_tmp); - z_ele = z_tmp; + if (Z_TYPE_P(z_ele) == IS_STRING) { + mem = Z_STRVAL_P(z_ele); + mem_len = Z_STRLEN_P(z_ele); + } else { + ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); + convert_to_string(&z_tmp); + mem = Z_STRVAL(z_tmp); + mem_len = Z_STRLEN(z_tmp); } - mem = Z_STRVAL_P(z_ele); - mem_len = Z_STRLEN_P(z_ele); } } @@ -1830,11 +1823,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, mem, mem_len); // Clean up our temp val if it was used - if(z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } + zval_dtor(&z_tmp); // Clean up prefixed or serialized data @@ -1870,7 +1859,7 @@ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_keys, *z_key, *z_tmp = NULL; + zval *z_keys, *z_key, z_tmp; HashTable *ht_keys; smart_string cmdstr = {0}; int num_keys, key_len, key_free; @@ -1899,18 +1888,17 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Append our key(s) */ ZEND_HASH_FOREACH_VAL(ht_keys, z_key) { + ZVAL_NULL(&z_tmp); /* Turn our value into a string if it isn't one */ - if (Z_TYPE_P(z_key) != IS_STRING) { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_key; - zval_copy_ctor(z_tmp); - convert_to_string(z_tmp); - - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); - } else { + if (Z_TYPE_P(z_key) == IS_STRING) { key = Z_STRVAL_P(z_key); key_len = Z_STRLEN_P(z_key); + } else { + ZVAL_ZVAL(&z_tmp, z_key, 1, 0); + convert_to_string(&z_tmp); + + key = Z_STRVAL(z_tmp); + key_len = Z_STRLEN(z_tmp); } /* Append this key to our command */ @@ -1923,10 +1911,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, kslot = cluster_hash_key(key, key_len); } else if(cluster_hash_key(key,key_len)!=kslot) { if (key_free) efree(key); - if (z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - } + zval_dtor(&z_tmp); efree(cmdstr.c); php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -1937,25 +1922,20 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Cleanup */ if (key_free) efree(key); - if (z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - z_tmp = NULL; - } + zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); } else { + ZVAL_NULL(&z_tmp); /* Turn our key into a string if it's a different type */ - if (Z_TYPE_P(z_keys) != IS_STRING) { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_keys; - zval_copy_ctor(z_tmp); - convert_to_string(z_tmp); - - key = Z_STRVAL_P(z_tmp); - key_len = Z_STRLEN_P(z_tmp); - } else { + if (Z_TYPE_P(z_keys) == IS_STRING) { key = Z_STRVAL_P(z_keys); key_len = Z_STRLEN_P(z_keys); + } else { + ZVAL_ZVAL(&z_tmp, z_keys, 1, 0); + convert_to_string(&z_tmp); + + key = Z_STRVAL(z_tmp); + key_len = Z_STRLEN(z_tmp); } /* Construct our whole command */ @@ -1968,10 +1948,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Cleanup */ if (key_free) efree(key); - if (z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - } + zval_dtor(&z_tmp); } /* Push our command and length to the caller */ @@ -2639,7 +2616,8 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *slot, void **ctx) { char *key, *subcmd; - int key_len, key_free, subcmd_len; + int key_free, subcmd_len; + size_t key_len; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &subcmd, &subcmd_len, &key, &key_len)==FAILURE) @@ -2681,7 +2659,7 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *source, *dest, *unit = NULL; - int keylen, sourcelen, destlen, unitlen; + size_t keylen, sourcelen, destlen, unitlen; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|s", &key, &keylen, &source, &sourcelen, &dest, &destlen, &unit, @@ -2718,7 +2696,7 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, optval) { /* If the key is numeric it's a non value option */ if (zkey) { - if (zkey->len == 6 && !strcasecmp(zkey->val, "count") && Z_TYPE_P(optval) == IS_LONG) { + if (zkey->len == 5 && !strcasecmp(zkey->val, "count") && Z_TYPE_P(optval) == IS_LONG) { *count = Z_LVAL_P(optval); } } else { @@ -3150,7 +3128,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *key; - int key_len; + size_t key_len; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) ==FAILURE) @@ -3199,7 +3177,10 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, // We only need to attempt unserialization if we have a serializer running if(redis_sock->serializer != REDIS_SERIALIZER_NONE) { - zval *z_ret = NULL; + zval zv, *z_ret = &zv; +#if (PHP_MAJOR_VERSION < 7) + z_ret = NULL; +#endif if(redis_unserialize(redis_sock, value, value_len, &z_ret TSRMLS_CC) == 0) { From 04a7327af877ef89cfd46c7a6aa6cc335e0d7270 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 8 Nov 2016 11:02:51 +0200 Subject: [PATCH 0618/1986] WIP: php7 compatibility --- common.h | 1 + redis.c | 3 +- redis_array.c | 129 ++++++++++++++++++++++----------------------- redis_array.h | 2 +- redis_array_impl.c | 116 +++++++++++++++++----------------------- 5 files changed, 112 insertions(+), 139 deletions(-) diff --git a/common.h b/common.h index b21b72298b..29b3e6c20e 100644 --- a/common.h +++ b/common.h @@ -237,6 +237,7 @@ inline_call_user_function(HashTable *function_table, zval *object, zval *functio _params = ecalloc(param_count, sizeof(zval *)); for (i = 0; i < param_count; i++) { _params[i] = ¶ms[i]; + INIT_PZVAL(_params[i]); } } ret = _call_user_function(function_table, &object, function_name, retval_ptr, param_count, _params TSRMLS_CC); diff --git a/redis.c b/redis.c index d75a3baa4b..585417930c 100644 --- a/redis.c +++ b/redis.c @@ -1019,8 +1019,7 @@ PHP_METHOD(Redis, getMultiple) key = Z_STRVAL_P(z_ele); key_len = Z_STRLEN_P(z_ele); } else { - z_tmp = *z_ele; - zval_copy_ctor(&z_tmp); + ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); convert_to_string(&z_tmp); key = Z_STRVAL(z_tmp); diff --git a/redis_array.c b/redis_array.c index 842a7c8917..3fba143259 100644 --- a/redis_array.c +++ b/redis_array.c @@ -109,8 +109,7 @@ static void redis_array_free(RedisArray *ra) { } /* Delete pur commands */ - zval_dtor(ra->z_pure_cmds); - efree(ra->z_pure_cmds); + zval_dtor(&ra->z_pure_cmds); /* Free structure itself */ efree(ra); @@ -383,7 +382,6 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* copy args to array */ i = 0; ZEND_HASH_FOREACH_VAL(h_args, zp_tmp) { - INIT_ZVAL(z_callargs[i]); z_callargs[i] = *zp_tmp; i++; } ZEND_HASH_FOREACH_END(); @@ -437,7 +435,7 @@ PHP_METHOD(RedisArray, __call) zval *z_args; char *cmd; - int cmd_len; + size_t cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, redis_array_ce, &cmd, &cmd_len, &z_args) == FAILURE) { @@ -834,7 +832,7 @@ PHP_METHOD(RedisArray, select) /* MGET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mget) { - zval *object, *z_keys, z_fun, *z_argarray, *data, *z_ret, *z_cur, *z_tmp_array, *z_tmp; + zval *object, *z_keys, z_fun, z_argarray, *data, z_ret, *z_cur, z_tmp_array, *z_tmp; int i, j, n; RedisArray *ra; int *pos, argc, *argc_each; @@ -899,79 +897,81 @@ PHP_METHOD(RedisArray, mget) argv[i++] = data; } ZEND_HASH_FOREACH_END(); - /* prepare return value */ - array_init(return_value); - MAKE_STD_ZVAL(z_tmp_array); - array_init(z_tmp_array); - + array_init(&z_tmp_array); /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ /* We don't even need to make a call to this node if no keys go there */ if(!argc_each[n]) continue; /* copy args for MGET call on node. */ - MAKE_STD_ZVAL(z_argarray); - array_init(z_argarray); + array_init(&z_argarray); for(i = 0; i < argc; ++i) { if(pos[i] != n) continue; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); - *z_tmp = *argv[i]; - zval_copy_ctor(z_tmp); - INIT_PZVAL(z_tmp); - add_next_index_zval(z_argarray, z_tmp); +#else + zval zv; + z_tmp = &zv; +#endif + ZVAL_ZVAL(z_tmp, argv[i], 1, 0); + add_next_index_zval(&z_argarray, z_tmp); } /* call MGET on the node */ - MAKE_STD_ZVAL(z_ret); - call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, z_ret, 1, z_argarray); + call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); /* cleanup args array */ - zval_ptr_dtor(&z_argarray); + zval_dtor(&z_argarray); + + /* Error out if we didn't get a proper response */ + if (Z_TYPE(z_ret) != IS_ARRAY) { + /* cleanup */ + zval_dtor(&z_ret); + zval_dtor(&z_tmp_array); + zval_dtor(&z_fun); + efree(pos); + efree(argc_each); + + /* failure */ + RETURN_FALSE; + } for(i = 0, j = 0; i < argc; ++i) { - /* Error out if we didn't get a proper response */ - if(Z_TYPE_P(z_ret) != IS_ARRAY) { - /* cleanup */ - zval_dtor(z_ret); - efree(z_ret); - zval_ptr_dtor(&z_tmp_array); - zval_dtor(&z_fun); - efree(pos); - efree(argc_each); - - /* failure */ - RETURN_FALSE; - } - if(pos[i] != n) continue; - z_cur = zend_hash_index_find(Z_ARRVAL_P(z_ret), j++); + z_cur = zend_hash_index_find(Z_ARRVAL(z_ret), j++); +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_cur; - zval_copy_ctor(z_tmp); - INIT_PZVAL(z_tmp); - add_index_zval(z_tmp_array, i, z_tmp); +#else + zval zv; + z_tmp = &zv; +#endif + ZVAL_ZVAL(z_tmp, z_cur, 1, 0); + add_index_zval(&z_tmp_array, i, z_tmp); } - zval_dtor(z_ret); - efree(z_ret); + zval_dtor(&z_ret); } + array_init(return_value); /* copy temp array in the right order to return_value */ for(i = 0; i < argc; ++i) { - z_cur = zend_hash_index_find(Z_ARRVAL_P(z_tmp_array), i); + z_cur = zend_hash_index_find(Z_ARRVAL(z_tmp_array), i); +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_cur; - zval_copy_ctor(z_tmp); - INIT_PZVAL(z_tmp); +#else + zval zv; + z_tmp = &zv; +#endif + ZVAL_ZVAL(z_tmp, z_cur, 1, 0); add_next_index_zval(return_value, z_tmp); } /* cleanup */ - zval_ptr_dtor(&z_tmp_array); + zval_dtor(&z_tmp_array); zval_dtor(&z_fun); efree(argv); efree(pos); @@ -982,12 +982,11 @@ PHP_METHOD(RedisArray, mget) /* MSET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mset) { - zval *object, *z_keys, z_fun, *z_argarray, *data, z_ret; + zval *object, *z_keys, z_fun, z_argarray, *data, z_ret, **argv; int i, n; RedisArray *ra; int *pos, argc, *argc_each; HashTable *h_keys; - zval *redis_inst, **argv; char *key, **keys, kbuf[40]; int key_len, *key_lens; zend_string *zkey; @@ -1024,7 +1023,7 @@ PHP_METHOD(RedisArray, mset) key_len = zkey->len; key = zkey->val; } else { - key_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); + key_len = snprintf(kbuf, sizeof(kbuf), "%lu", idx); key = kbuf; } @@ -1044,48 +1043,44 @@ PHP_METHOD(RedisArray, mset) int found = 0; /* copy args */ - MAKE_STD_ZVAL(z_argarray); - array_init(z_argarray); + array_init(&z_argarray); for(i = 0; i < argc; ++i) { - zval *z_tmp; - if(pos[i] != n) continue; - - ALLOC_ZVAL(z_tmp); - *z_tmp = *argv[i]; - zval_copy_ctor(z_tmp); - INIT_PZVAL(z_tmp); - - add_assoc_zval_ex(z_argarray, keys[i], key_lens[i], z_tmp); +#if (PHP_MAJOR_VERSION < 7) + zval *z_tmp; + MAKE_STD_ZVAL(z_tmp); +#else + zval zv, *z_tmp = &zv; +#endif + ZVAL_ZVAL(z_tmp, argv[i], 1, 0); + add_assoc_zval_ex(&z_argarray, keys[i], key_lens[i], z_tmp); found++; } if(!found) { - zval_dtor(z_argarray); - efree(z_argarray); + zval_dtor(&z_argarray); continue; /* don't run empty MSETs */ } /* prepare call */ ZVAL_STRINGL(&z_fun, "MSET", 4); - redis_inst = &ra->redis[n]; if(ra->index) { /* add MULTI */ - ra_index_multi(redis_inst, MULTI TSRMLS_CC); + ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); } /* call */ - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, &z_ret, 1, z_argarray); + call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); if(ra->index) { - ra_index_keys(z_argarray, redis_inst TSRMLS_CC); /* use SADD to add keys to node index */ - ra_index_exec(redis_inst, NULL, 0 TSRMLS_CC); /* run EXEC */ + ra_index_keys(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SADD to add keys to node index */ + ra_index_exec(&ra->redis[n], NULL, 0 TSRMLS_CC); /* run EXEC */ } zval_dtor(&z_fun); zval_dtor(&z_ret); - zval_ptr_dtor(&z_argarray); + zval_dtor(&z_argarray); } /* Free any keys that we needed to allocate memory for, because they weren't strings */ diff --git a/redis_array.h b/redis_array.h index 7a318b6784..013938c3ca 100644 --- a/redis_array.h +++ b/redis_array.h @@ -50,7 +50,7 @@ typedef struct RedisArray_ { zend_bool pconnect; /* should we use pconnect */ zval *z_fun; /* key extractor, callable */ zval *z_dist; /* key distributor, callable */ - zval *z_pure_cmds; /* hash table */ + zval z_pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ struct RedisArray_ *prev; diff --git a/redis_array_impl.c b/redis_array_impl.c index ed703c46b5..62575eaa01 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -35,7 +35,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b int i = 0, host_len; char *host, *p; short port; - zval *zpData, z_cons, z_ret, *redis_inst; + zval *zpData, z_cons, z_ret; RedisSock *redis_sock = NULL; /* function calls on the Redis object */ @@ -53,8 +53,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b } efree(ra->redis); efree(ra->hosts); - zval_dtor(ra->z_pure_cmds); - efree(ra->z_pure_cmds); + zval_dtor(&ra->z_pure_cmds); efree(ra); return NULL; } @@ -74,10 +73,11 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b } /* create Redis object */ - redis_inst = &ra->redis[i]; - object_init_ex(redis_inst, redis_ce); - INIT_PZVAL(redis_inst); - call_user_function(&redis_ce->function_table, redis_inst, &z_cons, &z_ret, 0, NULL); +#if (PHP_MAJOR_VERSION < 7) + INIT_PZVAL(&ra->redis[i]); +#endif + object_init_ex(&ra->redis[i], redis_ce); + call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL); /* create socket */ redis_sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); @@ -113,42 +113,41 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b /* List pure functions */ void ra_init_function_table(RedisArray *ra) { - MAKE_STD_ZVAL(ra->z_pure_cmds); - array_init(ra->z_pure_cmds); - - add_assoc_bool(ra->z_pure_cmds, "HGET", 1); - add_assoc_bool(ra->z_pure_cmds, "HGETALL", 1); - add_assoc_bool(ra->z_pure_cmds, "HKEYS", 1); - add_assoc_bool(ra->z_pure_cmds, "HLEN", 1); - add_assoc_bool(ra->z_pure_cmds, "SRANDMEMBER", 1); - add_assoc_bool(ra->z_pure_cmds, "HMGET", 1); - add_assoc_bool(ra->z_pure_cmds, "STRLEN", 1); - add_assoc_bool(ra->z_pure_cmds, "SUNION", 1); - add_assoc_bool(ra->z_pure_cmds, "HVALS", 1); - add_assoc_bool(ra->z_pure_cmds, "TYPE", 1); - add_assoc_bool(ra->z_pure_cmds, "EXISTS", 1); - add_assoc_bool(ra->z_pure_cmds, "LINDEX", 1); - add_assoc_bool(ra->z_pure_cmds, "SCARD", 1); - add_assoc_bool(ra->z_pure_cmds, "LLEN", 1); - add_assoc_bool(ra->z_pure_cmds, "SDIFF", 1); - add_assoc_bool(ra->z_pure_cmds, "ZCARD", 1); - add_assoc_bool(ra->z_pure_cmds, "ZCOUNT", 1); - add_assoc_bool(ra->z_pure_cmds, "LRANGE", 1); - add_assoc_bool(ra->z_pure_cmds, "ZRANGE", 1); - add_assoc_bool(ra->z_pure_cmds, "ZRANK", 1); - add_assoc_bool(ra->z_pure_cmds, "GET", 1); - add_assoc_bool(ra->z_pure_cmds, "GETBIT", 1); - add_assoc_bool(ra->z_pure_cmds, "SINTER", 1); - add_assoc_bool(ra->z_pure_cmds, "GETRANGE", 1); - add_assoc_bool(ra->z_pure_cmds, "ZREVRANGE", 1); - add_assoc_bool(ra->z_pure_cmds, "SISMEMBER", 1); - add_assoc_bool(ra->z_pure_cmds, "ZREVRANGEBYSCORE", 1); - add_assoc_bool(ra->z_pure_cmds, "ZREVRANK", 1); - add_assoc_bool(ra->z_pure_cmds, "HEXISTS", 1); - add_assoc_bool(ra->z_pure_cmds, "ZSCORE", 1); - add_assoc_bool(ra->z_pure_cmds, "HGET", 1); - add_assoc_bool(ra->z_pure_cmds, "OBJECT", 1); - add_assoc_bool(ra->z_pure_cmds, "SMEMBERS", 1); + array_init(&ra->z_pure_cmds); + + add_assoc_bool(&ra->z_pure_cmds, "HGET", 1); + add_assoc_bool(&ra->z_pure_cmds, "HGETALL", 1); + add_assoc_bool(&ra->z_pure_cmds, "HKEYS", 1); + add_assoc_bool(&ra->z_pure_cmds, "HLEN", 1); + add_assoc_bool(&ra->z_pure_cmds, "SRANDMEMBER", 1); + add_assoc_bool(&ra->z_pure_cmds, "HMGET", 1); + add_assoc_bool(&ra->z_pure_cmds, "STRLEN", 1); + add_assoc_bool(&ra->z_pure_cmds, "SUNION", 1); + add_assoc_bool(&ra->z_pure_cmds, "HVALS", 1); + add_assoc_bool(&ra->z_pure_cmds, "TYPE", 1); + add_assoc_bool(&ra->z_pure_cmds, "EXISTS", 1); + add_assoc_bool(&ra->z_pure_cmds, "LINDEX", 1); + add_assoc_bool(&ra->z_pure_cmds, "SCARD", 1); + add_assoc_bool(&ra->z_pure_cmds, "LLEN", 1); + add_assoc_bool(&ra->z_pure_cmds, "SDIFF", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZCARD", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZCOUNT", 1); + add_assoc_bool(&ra->z_pure_cmds, "LRANGE", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZRANGE", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZRANK", 1); + add_assoc_bool(&ra->z_pure_cmds, "GET", 1); + add_assoc_bool(&ra->z_pure_cmds, "GETBIT", 1); + add_assoc_bool(&ra->z_pure_cmds, "SINTER", 1); + add_assoc_bool(&ra->z_pure_cmds, "GETRANGE", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZREVRANGE", 1); + add_assoc_bool(&ra->z_pure_cmds, "SISMEMBER", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZREVRANGEBYSCORE", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZREVRANK", 1); + add_assoc_bool(&ra->z_pure_cmds, "HEXISTS", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZSCORE", 1); + add_assoc_bool(&ra->z_pure_cmds, "HGET", 1); + add_assoc_bool(&ra->z_pure_cmds, "OBJECT", 1); + add_assoc_bool(&ra->z_pure_cmds, "SMEMBERS", 1); } static int @@ -413,7 +412,6 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR /* convert_to_string(ra->z_fun); */ /* call extraction function */ - INIT_ZVAL(z_argv[0]); ZVAL_STRINGL(&z_argv[0], key, key_len); call_user_function(EG(function_table), NULL, ra->z_fun, &z_ret, 1, z_argv); @@ -466,7 +464,6 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML /* convert_to_string(ra->z_fun); */ /* call extraction function */ - INIT_ZVAL(z_argv[0]); ZVAL_STRINGL(&z_argv[0], key, key_len); call_user_function(EG(function_table), NULL, ra->z_dist, &z_ret, 1, z_argv); @@ -554,7 +551,6 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { /* run MULTI */ ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); - INIT_ZVAL(z_args[0]); ZVAL_LONG(&z_args[0], multi_value); call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); zval_dtor(&z_fun_multi); @@ -572,12 +568,10 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { /* prepare first parameters */ ZVAL_STRING(&z_fun, cmd); - INIT_ZVAL(z_args[0]); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); /* prepare keys */ for(i = 0; i < argc - 1; ++i) { - INIT_ZVAL(z_args[i+1]); z_args[i+1] = *zend_hash_index_find(Z_ARRVAL_P(z_keys), i); } @@ -610,8 +604,12 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { /* Go through input array and add values to the key array */ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_pairs), idx, zkey, z_val) { +#if (PHP_MAJOR_VERSION < 7) zval *z_new; MAKE_STD_ZVAL(z_new); +#else + zval zv, *z_new = &zv; +#endif if (zkey) { ZVAL_STRINGL(z_new, zkey->val, zkey->len); @@ -636,9 +634,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { /* prepare args */ ZVAL_STRINGL(&z_fun_sadd, "SADD", 4); - INIT_ZVAL(z_args[0]); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); - INIT_ZVAL(z_args[1]); ZVAL_STRINGL(&z_args[1], key, key_len); /* run SADD */ @@ -713,7 +709,7 @@ ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len) { cmd_up[i] = toupper(cmd[i]); cmd_up[cmd_len] = 0; - ret = zend_hash_str_exists(Z_ARRVAL_P(ra->z_pure_cmds), cmd_up, cmd_len); + ret = zend_hash_str_exists(Z_ARRVAL(ra->z_pure_cmds), cmd_up, cmd_len); efree(cmd_up); return !ret; @@ -730,7 +726,6 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con int key_len; /* arg */ - INIT_ZVAL(z_arg[0]); ZVAL_STRING(&z_arg[0], arg); /* run SMEMBERS */ @@ -778,7 +773,6 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long ra_index_multi(z_from, PIPELINE TSRMLS_CC); /* prepare args */ - INIT_ZVAL(z_arg[0]); ZVAL_STRINGL(&z_arg[0], key, key_len); ZVAL_STRINGL(&z_fun_type, "TYPE", 4); @@ -818,9 +812,6 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { zval z_fun_srem, z_ret, z_args[2]; - INIT_ZVAL(z_args[0]); - INIT_ZVAL(z_args[1]); - /* run SREM on source index */ ZVAL_STRINGL(&z_fun_srem, "SREM", 4); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); @@ -845,7 +836,6 @@ ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { ra_index_multi(z_from, MULTI TSRMLS_CC); /* run DEL on source */ - INIT_ZVAL(z_args[0]); ZVAL_STRINGL(&z_fun_del, "DEL", 3); ZVAL_STRINGL(&z_args[0], key, key_len); call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); @@ -891,9 +881,6 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run ZRANGE key 0 -1 WITHSCORES on source */ ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6); - for(i = 0; i < 4; ++i) { - INIT_ZVAL(z_args[i]); - } ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_args[1], "0", 1); ZVAL_STRINGL(&z_args[2], "-1", 2); @@ -917,17 +904,14 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS count = zend_hash_num_elements(h_zset_vals); z_zadd_args = ecalloc((1 + 2*count), sizeof(zval)); - INIT_ZVAL(z_zadd_args[0]); ZVAL_STRINGL(&z_zadd_args[0], key, key_len); i = 1; ZEND_HASH_FOREACH_KEY_VAL(h_zset_vals, idx, zkey, z_score_p) { /* add score */ - INIT_ZVAL(z_zadd_args[i]); ZVAL_DOUBLE(&z_zadd_args[i], Z_DVAL_P(z_score_p)); /* add value */ - INIT_ZVAL(z_zadd_args[i+1]); if (zkey) { ZVAL_STRINGL(&z_zadd_args[i+1], zkey->val, zkey->len); } else { @@ -1000,7 +984,6 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS zval z_fun_hgetall, z_fun_hmset, z_ret_dest, z_args[2]; /* run HGETALL on source */ - INIT_ZVAL(z_args[0]); ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); @@ -1042,12 +1025,10 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, z_retrieve_args = ecalloc(list_count, sizeof(zval)); /* set the key */ - INIT_ZVAL(z_retrieve_args[0]); ZVAL_STRINGL(&z_retrieve_args[0], key, key_len); /* possibly add some other args if they were provided. */ for(i = 1; i < list_count; ++i) { - INIT_ZVAL(z_retrieve_args[i]); ZVAL_STRING(&z_retrieve_args[i], cmd_list[i]); } @@ -1070,13 +1051,11 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, count = 1 + zend_hash_num_elements(h_set_vals); ZVAL_STRING(&z_fun_sadd, cmd_add[0]); z_sadd_args = ecalloc(count, sizeof(zval)); - INIT_ZVAL(z_sadd_args[0]); ZVAL_STRINGL(&z_sadd_args[0], key, key_len); i = 1; ZEND_HASH_FOREACH_VAL(h_set_vals, z_data_p) { /* add set elements */ - INIT_ZVAL(z_sadd_args[i]); z_sadd_args[i] = *z_data_p; zval_copy_ctor(&z_sadd_args[i]); i++; @@ -1172,7 +1151,6 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z zval *z_ret = NULL, z_args[2]; zval *z_host = &z_args[0], *z_count = &z_args[1]; - INIT_ZVAL(z_args[0]); ZVAL_STRING(z_host, hostname); ZVAL_LONG(z_count, count); From 5f15ce222f6966d4dfdf326f2c688ab38baed738 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 9 Nov 2016 10:44:11 +0200 Subject: [PATCH 0619/1986] WIP: php7 compatibility --- redis_array.c | 105 +++++++++++++++++++++++++++------------------ redis_array.h | 4 +- redis_array_impl.c | 47 ++++++++++---------- 3 files changed, 90 insertions(+), 66 deletions(-) diff --git a/redis_array.c b/redis_array.c index 3fba143259..3a89820b5c 100644 --- a/redis_array.c +++ b/redis_array.c @@ -97,16 +97,10 @@ static void redis_array_free(RedisArray *ra) { efree(ra->hosts); /* delete hash function */ - if(ra->z_fun) { - zval_dtor(ra->z_fun); - efree(ra->z_fun); - } + zval_dtor(&ra->z_fun); /* Distributor */ - if(ra->z_dist) { - zval_dtor(ra->z_dist); - efree(ra->z_dist); - } + zval_dtor(&ra->z_dist); /* Delete pur commands */ zval_dtor(&ra->z_pure_cmds); @@ -212,7 +206,7 @@ uint32_t rcrc32(const char *s, size_t sz) { Public constructor */ PHP_METHOD(RedisArray, __construct) { - zval *z0, *z_fun = NULL, *z_dist = NULL, *zpData, *z_opts = NULL; + zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; HashTable *hPrev = NULL, *hOpts = NULL; @@ -240,17 +234,17 @@ PHP_METHOD(RedisArray, __construct) } /* extract function name. */ - if ((zpData = zend_hash_str_find(hOpts, "function", sizeof("function") - 1)) != NULL) { - MAKE_STD_ZVAL(z_fun); - *z_fun = *zpData; - zval_copy_ctor(z_fun); + if ((zpData = zend_hash_str_find(hOpts, "function", sizeof("function") - 1)) == NULL) { + ZVAL_NULL(&z_fun); + } else { + ZVAL_ZVAL(&z_fun, zpData, 1, 0); } /* extract function name. */ - if ((zpData = zend_hash_str_find(hOpts, "distributor", sizeof("distributor") - 1)) != NULL) { - MAKE_STD_ZVAL(z_dist); - *z_dist = *zpData; - zval_copy_ctor(z_dist); + if ((zpData = zend_hash_str_find(hOpts, "distributor", sizeof("distributor") - 1)) == NULL) { + ZVAL_NULL(&z_dist); + } else { + ZVAL_ZVAL(&z_dist, zpData, 1, 0); } /* extract index option. */ @@ -308,13 +302,15 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); break; default: WRONG_PARAM_COUNT; break; } + zval_dtor(&z_dist); + zval_dtor(&z_fun); if(ra) { ra->auto_rehash = b_autorehash; @@ -522,7 +518,7 @@ PHP_METHOD(RedisArray, _instance) PHP_METHOD(RedisArray, _function) { - zval *object; + zval *object, *z_fun; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", @@ -534,17 +530,13 @@ PHP_METHOD(RedisArray, _function) RETURN_FALSE; } - if(ra->z_fun) { - *return_value = *ra->z_fun; - zval_copy_ctor(return_value); - } else { - RETURN_NULL(); - } + z_fun = &ra->z_fun; + RETURN_ZVAL(z_fun, 1, 0); } PHP_METHOD(RedisArray, _distributor) { - zval *object; + zval *object, *z_dist; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", @@ -556,12 +548,8 @@ PHP_METHOD(RedisArray, _distributor) RETURN_FALSE; } - if(ra->z_fun) { - *return_value = *ra->z_fun; - zval_copy_ctor(return_value); - } else { - RETURN_NULL(); - } + z_dist = &ra->z_dist; + RETURN_ZVAL(z_dist, 1, 0); } PHP_METHOD(RedisArray, _rehash) @@ -589,7 +577,7 @@ PHP_METHOD(RedisArray, _rehash) static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) { - zval *object, z_fun, *z_tmp; + zval *object, z_fun; int i; RedisArray *ra; @@ -607,8 +595,10 @@ static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *metho array_init(return_value); for(i = 0; i < ra->count; ++i) { - + zval zv, *z_tmp = &zv; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); +#endif /* Call each node in turn */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 0, NULL); @@ -651,7 +641,7 @@ PHP_METHOD(RedisArray, bgsave) PHP_METHOD(RedisArray, keys) { - zval *object, z_args[1], *z_tmp, z_fun; + zval *object, z_args[1], z_fun; RedisArray *ra; char *pattern; int pattern_len, i; @@ -679,8 +669,11 @@ PHP_METHOD(RedisArray, keys) /* Iterate our RedisArray nodes */ for(i=0; icount; ++i) { + zval zv, *z_tmp = &zv; +#if (PHP_MAJOR_VERSION < 7) /* Return for this node */ MAKE_STD_ZVAL(z_tmp); +#endif /* Call KEYS on each node */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); @@ -694,7 +687,7 @@ PHP_METHOD(RedisArray, keys) PHP_METHOD(RedisArray, getOption) { - zval *object, z_fun, *z_tmp, z_args[1]; + zval *object, z_fun, z_args[1]; int i; RedisArray *ra; long opt; @@ -716,8 +709,10 @@ PHP_METHOD(RedisArray, getOption) array_init(return_value); for(i = 0; i < ra->count; ++i) { - + zval zv, *z_tmp = &zv; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); +#endif /* Call each node in turn */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); @@ -729,7 +724,7 @@ PHP_METHOD(RedisArray, getOption) PHP_METHOD(RedisArray, setOption) { - zval *object, z_fun, *z_tmp, z_args[2]; + zval *object, z_fun, z_args[2]; int i; RedisArray *ra; long opt; @@ -754,8 +749,10 @@ PHP_METHOD(RedisArray, setOption) array_init(return_value); for(i = 0; i < ra->count; ++i) { - + zval zv, *z_tmp = &zv; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); +#endif /* Call each node in turn */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 2, z_args); @@ -768,7 +765,7 @@ PHP_METHOD(RedisArray, setOption) PHP_METHOD(RedisArray, select) { - zval *object, z_fun, *z_tmp, z_args[1]; + zval *object, z_fun, z_args[1]; int i; RedisArray *ra; long opt; @@ -790,7 +787,10 @@ PHP_METHOD(RedisArray, select) array_init(return_value); for(i = 0; i < ra->count; ++i) { + zval zv, *z_tmp = &zv; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); +#endif /* Call each node in turn */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); @@ -799,7 +799,7 @@ PHP_METHOD(RedisArray, select) } zval_dtor(&z_fun); } - +#if (PHP_MAJOR_VERSION < 7) #define HANDLE_MULTI_EXEC(cmd) do {\ if (redis_array_get(getThis(), &ra TSRMLS_CC) >= 0 && ra->z_multi_exec) {\ int i, num_varargs;\ @@ -828,6 +828,29 @@ PHP_METHOD(RedisArray, select) return;\ }\ }while(0) +#else +#define HANDLE_MULTI_EXEC(cmd) do { \ + if (redis_array_get(getThis(), &ra TSRMLS_CC) >= 0 && ra->z_multi_exec) { \ + int i, num_varargs; \ + zval *varargs = NULL, z_arg_array; \ + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O*", \ + &object, redis_array_ce, &varargs, &num_varargs) == FAILURE) { \ + RETURN_FALSE;\ + } \ + /* copy all args into a zval hash table */\ + array_init(&z_arg_array); \ + for (i = 0; i < num_varargs; i++) { \ + zval z_tmp; \ + ZVAL_ZVAL(&z_tmp, &varargs[i], 1, 0); \ + add_next_index_zval(&z_arg_array, &z_tmp); \ + } \ + /* call */\ + ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, &z_arg_array, NULL); \ + zval_dtor(&z_arg_array); \ + return; \ + } \ +} while(0) +#endif /* MGET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mget) diff --git a/redis_array.h b/redis_array.h index 013938c3ca..b9593f0c29 100644 --- a/redis_array.h +++ b/redis_array.h @@ -48,8 +48,8 @@ typedef struct RedisArray_ { zend_bool index; /* use per-node index */ zend_bool auto_rehash; /* migrate keys on read operations */ zend_bool pconnect; /* should we use pconnect */ - zval *z_fun; /* key extractor, callable */ - zval *z_dist; /* key distributor, callable */ + zval z_fun; /* key extractor, callable */ + zval z_dist; /* key distributor, callable */ zval z_pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 62575eaa01..250f33ec8a 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -369,8 +369,6 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->hosts = ecalloc(count, sizeof(char *)); ra->redis = ecalloc(count, sizeof(zval)); ra->count = 0; - ra->z_fun = NULL; - ra->z_dist = NULL; ra->z_multi_exec = NULL; ra->index = b_index; ra->auto_rehash = 0; @@ -386,8 +384,8 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL; /* Set hash function and distribtor if provided */ - ra->z_fun = z_fun; - ra->z_dist = z_dist; + ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); + ZVAL_ZVAL(&ra->z_dist, z_dist, 1, 0); return ra; } @@ -402,9 +400,9 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR /* check that we can call the extractor function */ #if (PHP_MAJOR_VERSION < 7) - if(!zend_is_callable_ex(ra->z_fun, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { + if (!zend_is_callable_ex(&ra->z_fun, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { #else - if (!zend_is_callable_ex(ra->z_fun, NULL, 0, NULL, NULL, NULL)) { + if (!zend_is_callable_ex(&ra->z_fun, NULL, 0, NULL, NULL, NULL)) { #endif php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function"); return NULL; @@ -413,7 +411,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR /* call extraction function */ ZVAL_STRINGL(&z_argv[0], key, key_len); - call_user_function(EG(function_table), NULL, ra->z_fun, &z_ret, 1, z_argv); + call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, z_argv); if(Z_TYPE(z_ret) != IS_STRING) { zval_dtor(&z_argv[0]); @@ -435,7 +433,7 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS char *start, *end; *out_len = key_len; - if(ra->z_fun) { + if (Z_TYPE(ra->z_fun) != IS_NULL) { return ra_call_extractor(ra, key, key_len, out_len TSRMLS_CC); } else if ((start = strchr(key, '{')) == NULL || (end = strchr(start + 1, '}')) == NULL) { return estrndup(key, key_len); @@ -454,9 +452,9 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML /* check that we can call the extractor function */ #if (PHP_MAJOR_VERSION < 7) - if(!zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { + if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { #else - if (!zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL)) { + if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL)) { #endif php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); return 0; @@ -465,7 +463,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML /* call extraction function */ ZVAL_STRINGL(&z_argv[0], key, key_len); - call_user_function(EG(function_table), NULL, ra->z_dist, &z_ret, 1, z_argv); + call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, z_argv); if(Z_TYPE(z_ret) != IS_LONG) { zval_dtor(&z_argv[0]); @@ -489,7 +487,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC); if(!out) return NULL; - if(ra->z_dist) { + if (Z_TYPE(ra->z_dist) != IS_NULL) { if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) { efree(out); return NULL; @@ -658,11 +656,10 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { if(Z_TYPE(z_ret) == IS_ARRAY) { if(return_value) { if(keep_all) { - *return_value = z_ret; - zval_copy_ctor(return_value); + zp_tmp = &z_ret; + RETVAL_ZVAL(zp_tmp, 1, 0); } else if ((zp_tmp = zend_hash_index_find(Z_ARRVAL(z_ret), 0)) != NULL) { - *return_value = *zp_tmp; - zval_copy_ctor(return_value); + RETVAL_ZVAL(zp_tmp, 1, 0); } } zval_dtor(&z_ret); @@ -1148,19 +1145,21 @@ ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, const char *hostname, long count TSRMLS_DC) { - zval *z_ret = NULL, z_args[2]; - zval *z_host = &z_args[0], *z_count = &z_args[1]; + zval z_args[2]; - ZVAL_STRING(z_host, hostname); - ZVAL_LONG(z_count, count); + ZVAL_STRING(&z_args[0], hostname); + ZVAL_LONG(&z_args[1], count); #if (PHP_MAJOR_VERSION < 7) - zval **z_args_pp[2] = { &z_host, &z_count }; + zval *z_ret = NULL, + *z_host = &z_args[0], *z_count = &z_args[1], + **z_args_pp[2] = { &z_host, &z_count }; z_cb->params = z_args_pp; z_cb->retval_ptr_ptr = &z_ret; #else + zval z_ret; z_cb->params = z_args; - z_cb->retval = z_ret; + z_cb->retval = &z_ret; #endif z_cb->param_count = 2; z_cb->no_separation = 0; @@ -1169,9 +1168,11 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z zend_call_function(z_cb, z_cb_cache TSRMLS_CC); /* cleanup */ - zval_dtor(z_host); + zval_dtor(&z_args[0]); +#if (PHP_MAJOR_VERSION < 7) if(z_ret) efree(z_ret); +#endif } static void From 8a4eeecaf3220ed941c0489d439c52754e0da00b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 10 Nov 2016 09:37:02 +0200 Subject: [PATCH 0620/1986] WIP: php7 compatibility --- cluster_library.c | 10 +++---- redis_array.c | 71 +++++++++++++++++++++++------------------------ redis_cluster.c | 12 +++++--- redis_cluster.h | 11 +++++--- redis_session.c | 47 ++++++++++++++----------------- 5 files changed, 76 insertions(+), 75 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index fb0b2b8ad7..cd6ecfc759 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1936,7 +1936,7 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb cb, void *ctx) { - zval *z_result; + zval zv, *z_result = &zv; /* Return FALSE if we didn't get a multi-bulk response */ if (c->reply_type != TYPE_MULTIBULK) { @@ -1944,7 +1944,9 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* Allocate our array */ +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_result); +#endif array_init(z_result); /* Consume replies as long as there are more than zero */ @@ -1954,16 +1956,14 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, /* Call our specified callback */ if (cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC)==FAILURE) { - zval_dtor(z_result); - FREE_ZVAL(z_result); + zval_ptr_dtor(z_result); CLUSTER_RETURN_FALSE(c); } } // Success, make this array our return value if(CLUSTER_IS_ATOMIC(c)) { - *return_value = *z_result; - efree(z_result); + RETVAL_ZVAL(z_result, 0, 1); } else { add_next_index_zval(c->multi_resp, z_result); } diff --git a/redis_array.c b/redis_array.c index 3a89820b5c..8b0f1014eb 100644 --- a/redis_array.c +++ b/redis_array.c @@ -218,6 +218,8 @@ PHP_METHOD(RedisArray, __construct) RETURN_FALSE; } + ZVAL_NULL(&z_fun); + ZVAL_NULL(&z_dist); /* extract options */ if(z_opts) { zval *z_retry_interval_p; @@ -234,16 +236,12 @@ PHP_METHOD(RedisArray, __construct) } /* extract function name. */ - if ((zpData = zend_hash_str_find(hOpts, "function", sizeof("function") - 1)) == NULL) { - ZVAL_NULL(&z_fun); - } else { + if ((zpData = zend_hash_str_find(hOpts, "function", sizeof("function") - 1)) != NULL) { ZVAL_ZVAL(&z_fun, zpData, 1, 0); } /* extract function name. */ - if ((zpData = zend_hash_str_find(hOpts, "distributor", sizeof("distributor") - 1)) == NULL) { - ZVAL_NULL(&z_dist); - } else { + if ((zpData = zend_hash_str_find(hOpts, "distributor", sizeof("distributor") - 1)) != NULL) { ZVAL_ZVAL(&z_dist, zpData, 1, 0); } @@ -1124,7 +1122,7 @@ PHP_METHOD(RedisArray, mset) /* DEL will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, del) { - zval *object, *z_keys, z_fun, *z_argarray, *data, *z_ret, *z_tmp, *z_args; + zval *object, z_keys, z_fun, *data, z_ret, *z_tmp, *z_args; int i, n; RedisArray *ra; int *pos, argc = ZEND_NUM_ARGS(), *argc_each; @@ -1145,25 +1143,28 @@ PHP_METHOD(RedisArray, del) /* if single array arg, point z_keys to it. */ if (argc == 1 && Z_TYPE(z_args[0]) == IS_ARRAY) { - z_keys = &z_args[0]; + z_keys = z_args[0]; } else { /* copy all elements to z_keys */ - MAKE_STD_ZVAL(z_keys); - array_init(z_keys); - free_zkeys = 1; + array_init(&z_keys); for (i = 0; i < argc; ++i) { + zval *z_arg = &z_args[i]; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); - *z_tmp = z_args[i]; - zval_copy_ctor(z_tmp); - INIT_PZVAL(z_tmp); - +#else + zval zv; + z_tmp = &zv; +#endif + ZVAL_ZVAL(z_tmp, z_arg, 1, 0); /* add copy to z_keys */ - add_next_index_zval(z_keys, z_tmp); + add_next_index_zval(&z_keys, z_tmp); } + free_zkeys = 1; } if (redis_array_get(getThis(), &ra TSRMLS_CC) < 0) { + efree(z_args); RETURN_FALSE; } @@ -1171,7 +1172,7 @@ PHP_METHOD(RedisArray, del) ZVAL_STRINGL(&z_fun, "DEL", 3); /* init data structures */ - h_keys = Z_ARRVAL_P(z_keys); + h_keys = Z_ARRVAL(z_keys); argc = zend_hash_num_elements(h_keys); argv = emalloc(argc * sizeof(zval*)); pos = emalloc(argc * sizeof(int)); @@ -1184,6 +1185,7 @@ PHP_METHOD(RedisArray, del) ZEND_HASH_FOREACH_VAL(h_keys, data) { if (Z_TYPE_P(data) != IS_STRING) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "DEL: all keys must be string."); + efree(argv); efree(pos); RETURN_FALSE; } @@ -1199,25 +1201,26 @@ PHP_METHOD(RedisArray, del) for(n = 0; n < ra->count; ++n) { /* for each node */ int found = 0; + zval z_argarray; /* copy args */ - MAKE_STD_ZVAL(z_argarray); - array_init(z_argarray); + array_init(&z_argarray); for(i = 0; i < argc; ++i) { if(pos[i] != n) continue; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); - *z_tmp = *argv[i]; - zval_copy_ctor(z_tmp); - INIT_PZVAL(z_tmp); - - add_next_index_zval(z_argarray, z_tmp); +#else + zval zv; + z_tmp = &zv; +#endif + ZVAL_ZVAL(z_tmp, argv[i], 1, 0); + add_next_index_zval(&z_argarray, z_tmp); found++; } if(!found) { /* don't run empty DELs */ - zval_dtor(z_argarray); - efree(z_argarray); + zval_dtor(&z_argarray); continue; } @@ -1227,22 +1230,19 @@ PHP_METHOD(RedisArray, del) } /* call */ - MAKE_STD_ZVAL(z_ret); - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, z_ret, 1, z_argarray); + call_user_function(&redis_ce->function_table, redis_inst, &z_fun, &z_ret, 1, &z_argarray); if(ra->index) { - ra_index_del(z_argarray, redis_inst TSRMLS_CC); /* use SREM to remove keys from node index */ + ra_index_del(&z_argarray, redis_inst TSRMLS_CC); /* use SREM to remove keys from node index */ ra_index_exec(redis_inst, z_tmp, 0 TSRMLS_CC); /* run EXEC */ total += Z_LVAL_P(z_tmp); /* increment total from multi/exec block */ } else { - total += Z_LVAL_P(z_ret); /* increment total from single command */ + total += Z_LVAL(z_ret); /* increment total from single command */ } - zval_dtor(z_ret); - efree(z_ret); + zval_dtor(&z_ret); - zval_dtor(z_argarray); - efree(z_argarray); + zval_dtor(&z_argarray); } /* cleanup */ @@ -1252,8 +1252,7 @@ PHP_METHOD(RedisArray, del) efree(argc_each); if(free_zkeys) { - zval_dtor(z_keys); - efree(z_keys); + zval_dtor(&z_keys); } efree(z_args); diff --git a/redis_cluster.c b/redis_cluster.c index 92af79be91..cd3381afe5 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -340,7 +340,13 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { } /* Free redisCluster context */ -void free_cluster_context(void *object TSRMLS_DC) { +void +#if (PHP_MAJOR_VERSION < 7) +free_cluster_context(void *object TSRMLS_DC) +#else +free_cluster_context(zend_object *object) +#endif +{ redisCluster *cluster; // Grab context @@ -385,7 +391,6 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, zend_throw_exception(redis_cluster_exception_ce, "Must pass seeds", 0 TSRMLS_CC); } - /* Set our timeout and read_timeout which we'll pass through to the * socket type operations */ c->timeout = timeout; @@ -919,10 +924,9 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, /* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */ PHP_METHOD(RedisCluster, del) { - zval *z_ret; + zval *z_ret = emalloc(sizeof(zval)); // Initialize a LONG value to zero for our return - MAKE_STD_ZVAL(z_ret); ZVAL_LONG(z_ret, 0); // Parse args, process diff --git a/redis_cluster.h b/redis_cluster.h index 1ff465b282..3468942982 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -15,7 +15,7 @@ ((redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC)) #else #define GET_CONTEXT() \ - ((redisCluster *)((char *)getThis() - XtOffsetOf(redisCluster, std))) + ((redisCluster *)((char *)Z_OBJ_P(getThis()) - XtOffsetOf(redisCluster, std))) #endif /* Command building/processing is identical for every command */ @@ -106,15 +106,18 @@ /* For the creation of RedisCluster specific exceptions */ PHP_REDIS_API zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); -/* Create cluster context */ #if (PHP_MAJOR_VERSION < 7) +/* Create cluster context */ zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC); +/* Free cluster context struct */ +void free_cluster_context(void *object TSRMLS_DC); #else +/* Create cluster context */ zend_object *create_cluster_context(zend_class_entry *class_type TSRMLS_DC); +/* Free cluster context struct */ +void free_cluster_context(zend_object *object); #endif -/* Free cluster context struct */ -void free_cluster_context(void *object TSRMLS_DC); /* Inittialize our class with PHP */ void init_rediscluster(TSRMLS_D); diff --git a/redis_session.c b/redis_session.c index 17fe2b0b69..6faae20e6b 100644 --- a/redis_session.c +++ b/redis_session.c @@ -189,7 +189,7 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { PS_OPEN_FUNC(redis) { php_url *url; - zval *params, *param; + zval params, *param; int i, j, path_len; redis_pool *pool = redis_pool_new(TSRMLS_C); @@ -236,37 +236,36 @@ PS_OPEN_FUNC(redis) /* parse parameters */ if (url->query != NULL) { - MAKE_STD_ZVAL(params); - array_init(params); + array_init(¶ms); - sapi_module.treat_data(PARSE_STRING, estrdup(url->query), params TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(url->query), ¶ms TSRMLS_CC); - if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "weight", sizeof("weight") - 1)) != NULL) { + if ((param = zend_hash_str_find(Z_ARRVAL(params), "weight", sizeof("weight") - 1)) != NULL) { weight = zval_get_long(param); } - if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout") - 1)) != NULL) { + if ((param = zend_hash_str_find(Z_ARRVAL(params), "timeout", sizeof("timeout") - 1)) != NULL) { timeout = atof(Z_STRVAL_P(param)); } - if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "persistent", sizeof("persistent") - 1)) != NULL) { + if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent", sizeof("persistent") - 1)) != NULL) { persistent = (atol(Z_STRVAL_P(param)) == 1 ? 1 : 0); } - if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "persistent_id", sizeof("persistent_id") - 1)) != NULL) { + if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent_id", sizeof("persistent_id") - 1)) != NULL) { persistent_id = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); } - if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "prefix", sizeof("prefix") - 1)) != NULL) { + if ((param = zend_hash_str_find(Z_ARRVAL(params), "prefix", sizeof("prefix") - 1)) != NULL) { prefix = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); } - if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "auth", sizeof("auth") - 1)) != NULL) { + if ((param = zend_hash_str_find(Z_ARRVAL(params), "auth", sizeof("auth") - 1)) != NULL) { auth = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); } - if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "database", sizeof("database") - 1)) != NULL) { + if ((param = zend_hash_str_find(Z_ARRVAL(params), "database", sizeof("database") - 1)) != NULL) { database = zval_get_long(param); } - if ((param = zend_hash_str_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval") - 1)) != NULL) { + if ((param = zend_hash_str_find(Z_ARRVAL(params), "retry_interval", sizeof("retry_interval") - 1)) != NULL) { retry_interval = zval_get_long(param); } - zval_ptr_dtor(¶ms); + zval_dtor(¶ms); } if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { @@ -545,7 +544,7 @@ static char *cluster_session_key(redisCluster *c, const char *key, int keylen, PS_OPEN_FUNC(rediscluster) { redisCluster *c; - zval *z_conf, *z_val; + zval z_conf, *z_val; HashTable *ht_conf, *ht_seeds; double timeout = 0, read_timeout = 0; int persistent = 0; @@ -553,22 +552,20 @@ PS_OPEN_FUNC(rediscluster) { char *prefix; /* Parse configuration for session handler */ - MAKE_STD_ZVAL(z_conf); - array_init(z_conf); - sapi_module.treat_data(PARSE_STRING, estrdup(save_path), z_conf TSRMLS_CC); + array_init(&z_conf); + sapi_module.treat_data(PARSE_STRING, estrdup(save_path), &z_conf TSRMLS_CC); /* Sanity check that we're able to parse and have a seeds array */ - if (Z_TYPE_P(z_conf) != IS_ARRAY || - (z_val = zend_hash_str_find(Z_ARRVAL_P(z_conf), "seed", sizeof("seed") - 1)) == NULL || + if (Z_TYPE(z_conf) != IS_ARRAY || + (z_val = zend_hash_str_find(Z_ARRVAL(z_conf), "seed", sizeof("seed") - 1)) == NULL || Z_TYPE_P(z_val) != IS_ARRAY) { - zval_dtor(z_conf); - efree(z_conf); + zval_dtor(&z_conf); return FAILURE; } /* Grab a copy of our config hash table and keep seeds array */ - ht_conf = Z_ARRVAL_P(z_conf); + ht_conf = Z_ARRVAL(z_conf); ht_seeds = Z_ARRVAL_P(z_val); /* Grab timeouts if they were specified */ @@ -582,8 +579,7 @@ PS_OPEN_FUNC(rediscluster) { if (timeout < 0 || read_timeout < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't set negative timeout values in session configuration"); - zval_dtor(z_conf); - efree(z_conf); + zval_dtor(&z_conf); return FAILURE; } @@ -623,8 +619,7 @@ PS_OPEN_FUNC(rediscluster) { } /* Cleanup */ - zval_dtor(z_conf); - efree(z_conf); + zval_dtor(&z_conf); return retval; } From 19f5f708c266558e9a848534d340ff8010751a9c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 11 Nov 2016 10:06:20 +0200 Subject: [PATCH 0621/1986] WIP: php7 compatibility --- cluster_library.c | 139 +++++++++++++++++----------------------- cluster_library.h | 14 ++--- common.h | 3 - library.c | 8 +-- redis_array.c | 42 ++++++++++--- redis_array_impl.c | 154 +++++++++++++++++++++------------------------ redis_cluster.c | 57 ++++++++--------- redis_commands.c | 12 +++- 8 files changed, 207 insertions(+), 222 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index cd6ecfc759..8d9b9b6d07 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1500,7 +1500,7 @@ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, if(c->flags->mode != MULTI) { RETURN_FALSE; } else { - add_next_index_bool(c->multi_resp, 0); + add_next_index_bool(&c->multi_resp, 0); return; } } @@ -1530,11 +1530,14 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster CLUSTER_RETURN_STRING(c, resp, c->reply_len); } } else { - zval *z = NULL; + zval zv, *z = &zv; +#if (PHP_MAJOR_VERSION < 7) + z = NULL; +#endif if (redis_unserialize(c->flags, resp, c->reply_len, &z TSRMLS_CC)) { - add_next_index_zval(c->multi_resp, z); + add_next_index_zval(&c->multi_resp, z); } else { - add_next_index_stringl(c->multi_resp, resp, c->reply_len); + add_next_index_stringl(&c->multi_resp, resp, c->reply_len); } } efree(resp); @@ -1641,31 +1644,28 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval *z_tab, *z_tmp, *z_ret; + zval z_tab, *z_tmp, *z_ret; int pull=0; // Consume each MULTI BULK response (one per channel/pattern) while(sctx->argc--) { - z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - pull, mbulk_resp_loop_raw); - - if(!z_tab) { + if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + pull, mbulk_resp_loop_raw, &z_tab) + ) { efree(sctx); RETURN_FALSE; } - if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(z_tab), 0)) == NULL || + if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_tab), 0)) == NULL || strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) != 0 ) { - zval_dtor(z_tab); - FREE_ZVAL(z_tab); + zval_dtor(&z_tab); efree(sctx); RETURN_FALSE; } - zval_dtor(z_tab); - efree(z_tab); + zval_dtor(&z_tab); pull = 1; } @@ -1690,10 +1690,9 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * int tab_idx=1, is_pmsg; // Get the next subscribe response - z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - 1, mbulk_resp_loop); - - if (!z_tab || (z_type = zend_hash_index_find(Z_ARRVAL_P(z_tab), 0)) == NULL) { + if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, mbulk_resp_loop, &z_tab) || + (z_type = zend_hash_index_find(Z_ARRVAL(z_tab), 0)) == NULL + ) { break; } @@ -1703,18 +1702,17 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * ) { is_pmsg = *Z_STRVAL_P(z_type) == 'p'; } else { - zval_dtor(z_tab); - efree(z_tab); + zval_dtor(&z_tab); continue; } - if (is_pmsg && (z_pat = zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++)) == NULL) { + if (is_pmsg && (z_pat = zend_hash_index_find(Z_ARRVAL(z_tab), tab_idx++)) == NULL) { break; } // Extract channel and data - if ((z_chan = zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++)) == NULL || - (z_data = zend_hash_index_find(Z_ARRVAL_P(z_tab), tab_idx++)) == NULL + if ((z_chan = zend_hash_index_find(Z_ARRVAL(z_tab), tab_idx++)) == NULL || + (z_data = zend_hash_index_find(Z_ARRVAL(z_tab), tab_idx++)) == NULL ) { break; } @@ -1759,19 +1757,15 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * // If we have a return value, free it if(z_ret) zval_ptr_dtor(&z_ret); - zval_dtor(z_tab); - efree(z_tab); + zval_dtor(&z_tab); } // We're no longer subscribing, due to an error c->subscribed_slot = -1; // Cleanup + zval_dtor(&z_tab); efree(sctx); - if(z_tab) { - zval_dtor(z_tab); - efree(z_tab); - } // Failure RETURN_FALSE; @@ -1782,7 +1776,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval *z_tab, *z_chan, *z_flag; + zval z_tab, *z_chan, *z_flag; int pull = 0, argc = sctx->argc; efree(sctx); @@ -1790,25 +1784,20 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, // Consume each response while(argc--) { - z_tab = cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, - c, pull, mbulk_resp_loop_raw); - // Fail if we didn't get an array or can't find index 1 - if(!z_tab || (z_chan = zend_hash_index_find(Z_ARRVAL_P(z_tab), 1)) == NULL) { - if(z_tab) { - zval_dtor(z_tab); - efree(z_tab); - } + if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw, &z_tab) || + (z_chan = zend_hash_index_find(Z_ARRVAL(z_tab), 1)) == NULL + ) { + zval_dtor(&z_tab); zval_dtor(return_value); RETURN_FALSE; } // Find the flag for this channel/pattern - if ((z_flag = zend_hash_index_find(Z_ARRVAL_P(z_tab), 2)) == NULL || + if ((z_flag = zend_hash_index_find(Z_ARRVAL(z_tab), 2)) == NULL || Z_STRLEN_P(z_flag) != 2 ) { - zval_dtor(z_tab); - efree(z_tab); + zval_dtor(&z_tab); zval_dtor(return_value); RETURN_FALSE; } @@ -1819,8 +1808,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, // Add result add_assoc_bool(return_value, Z_STRVAL_P(z_chan), flag[1]=='1'); - zval_dtor(z_tab); - efree(z_tab); + zval_dtor(&z_tab); pull = 1; } } @@ -1866,7 +1854,7 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust void *ctx) { clusterReply *r; - zval *z_arr; + zval zv, *z_arr = &zv; int i; // Make sure we can read it @@ -1890,15 +1878,12 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust RETVAL_STRINGL(r->str, r->len); break; case TYPE_MULTIBULK: - MAKE_STD_ZVAL(z_arr); array_init(z_arr); for(i=0;ielements;i++) { cluster_mbulk_variant_resp(r->element[i], z_arr); } - - *return_value = *z_arr; - efree(z_arr); + RETVAL_ZVAL(z_arr, 1, 0); break; default: RETVAL_FALSE; @@ -1907,23 +1892,23 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust } else { switch(r->type) { case TYPE_INT: - add_next_index_long(c->multi_resp, r->integer); + add_next_index_long(&c->multi_resp, r->integer); break; case TYPE_ERR: - add_next_index_bool(c->multi_resp, 0); + add_next_index_bool(&c->multi_resp, 0); break; case TYPE_LINE: - add_next_index_bool(c->multi_resp, 1); + add_next_index_bool(&c->multi_resp, 1); break; case TYPE_BULK: - add_next_index_stringl(c->multi_resp, r->str, r->len); + add_next_index_stringl(&c->multi_resp, r->str, r->len); efree(r->str); break; case TYPE_MULTIBULK: - cluster_mbulk_variant_resp(r, c->multi_resp); + cluster_mbulk_variant_resp(r, &c->multi_resp); break; default: - add_next_index_bool(c->multi_resp, 0); + add_next_index_bool(&c->multi_resp, 0); break; } } @@ -1965,7 +1950,7 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, if(CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(z_result, 0, 1); } else { - add_next_index_zval(c->multi_resp, z_result); + add_next_index_zval(&c->multi_resp, z_result); } } @@ -2047,7 +2032,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster if(CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(z_result, 0, 1); } else { - add_next_index_zval(c->multi_resp, z_result); + add_next_index_zval(&c->multi_resp, z_result); } } @@ -2056,6 +2041,7 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC void *ctx) { char *info; + zval zv, *z_result = &zv; /* Read the bulk response */ info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC); @@ -2064,10 +2050,7 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC } #if (PHP_MAJOR_VERSION < 7) - zval *z_result; MAKE_STD_ZVAL(z_result); -#else - zval zv, *z_result = &zv; #endif /* Parse it and free the bulk string */ @@ -2077,16 +2060,14 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(z_result, 0, 1); } else { - add_next_index_zval(c->multi_resp, z_result); + add_next_index_zval(&c->multi_resp, z_result); } } /* MULTI BULK response loop where we might pull the next one */ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, int pull, mbulk_cb cb) + redisCluster *c, int pull, mbulk_cb cb, zval *z_ret) { - zval *z_result; - // Pull our next response if directed if(pull) { if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) @@ -2100,25 +2081,23 @@ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, return NULL; } - MAKE_STD_ZVAL(z_result); - array_init(z_result); + array_init(z_ret); // Call our callback - if(cb(c->cmd_sock, z_result, c->reply_len, NULL TSRMLS_CC)==FAILURE) { - zval_dtor(z_result); - FREE_ZVAL(z_result); + if(cb(c->cmd_sock, z_ret, c->reply_len, NULL TSRMLS_CC)==FAILURE) { + zval_dtor(z_ret); return NULL; } - return z_result; + return z_ret; } /* MULTI MULTI BULK reply (for EXEC) */ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - MAKE_STD_ZVAL(c->multi_resp); - array_init(c->multi_resp); + zval *multi_resp = &c->multi_resp; + array_init(multi_resp); clusterFoldItem *fi = c->multi_head; while(fi) { @@ -2131,23 +2110,21 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, c->cmd_sock = SLOT_SOCK(c, fi->slot); if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) { - zval_dtor(c->multi_resp); - efree(c->multi_resp); + zval_dtor(multi_resp); RETURN_FALSE; } fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx); } else { /* Just add false */ - add_next_index_bool(c->multi_resp, 0); + add_next_index_bool(multi_resp, 0); } fi = fi->next; } // Set our return array zval_dtor(return_value); - *return_value = *c->multi_resp; - efree(c->multi_resp); + RETVAL_ZVAL(multi_resp, 0, 1); } /* Generic handler for MGET */ @@ -2176,7 +2153,7 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, *return_value = *(mctx->z_multi); efree(mctx->z_multi); } else { - add_next_index_zval(c->multi_resp, mctx->z_multi); + add_next_index_zval(&c->multi_resp, mctx->z_multi); } } @@ -2212,7 +2189,7 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluste *return_value = *(mctx->z_multi); efree(mctx->z_multi); } else { - add_next_index_zval(c->multi_resp, mctx->z_multi); + add_next_index_zval(&c->multi_resp, mctx->z_multi); } } @@ -2241,7 +2218,7 @@ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * if(CLUSTER_IS_ATOMIC(c)) { ZVAL_LONG(return_value, Z_LVAL_P(mctx->z_multi)); } else { - add_next_index_long(c->multi_resp, Z_LVAL_P(mctx->z_multi)); + add_next_index_long(&c->multi_resp, Z_LVAL_P(mctx->z_multi)); } efree(mctx->z_multi); } @@ -2269,9 +2246,9 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // Set our return if it's the last call if(mctx->last) { if(CLUSTER_IS_ATOMIC(c)) { - ZVAL_BOOL(return_value, Z_BVAL_P(mctx->z_multi)); + ZVAL_BOOL(return_value, Z_LVAL_P(mctx->z_multi)); } else { - add_next_index_bool(c->multi_resp, Z_BVAL_P(mctx->z_multi)); + add_next_index_bool(&c->multi_resp, Z_LVAL_P(mctx->z_multi)); } efree(mctx->z_multi); } diff --git a/cluster_library.h b/cluster_library.h index f49475110b..938deaf22e 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -89,7 +89,7 @@ if(CLUSTER_IS_ATOMIC(c)) { \ RETURN_FALSE; \ } else { \ - add_next_index_bool(c->multi_resp, 0); \ + add_next_index_bool(&c->multi_resp, 0); \ return; \ } @@ -102,7 +102,7 @@ RETURN_FALSE; \ } \ } else { \ - add_next_index_bool(c->multi_resp, b); \ + add_next_index_bool(&c->multi_resp, b); \ } /* Helper to respond with a double or add it to our MULTI response */ @@ -110,7 +110,7 @@ if(CLUSTER_IS_ATOMIC(c)) { \ RETURN_DOUBLE(d); \ } else { \ - add_next_index_double(c->multi_resp, d); \ + add_next_index_double(&c->multi_resp, d); \ } /* Helper to return a string value */ @@ -118,7 +118,7 @@ if(CLUSTER_IS_ATOMIC(c)) { \ RETVAL_STRINGL(str, len); \ } else { \ - add_next_index_stringl(c->multi_resp, str, len); \ + add_next_index_stringl(&c->multi_resp, str, len); \ } \ /* Return a LONG value */ @@ -126,7 +126,7 @@ if(CLUSTER_IS_ATOMIC(c)) { \ RETURN_LONG(val); \ } else { \ - add_next_index_long(c->multi_resp, val); \ + add_next_index_long(&c->multi_resp, val); \ } /* Macro to clear out a clusterMultiCmd structure */ @@ -211,7 +211,7 @@ typedef struct redisCluster { char multi_len[REDIS_CLUSTER_SLOTS]; /* Variable to store MULTI response */ - zval *multi_resp; + zval multi_resp; /* Flag for when we get a CLUSTERDOWN error */ short clusterdown; @@ -422,7 +422,7 @@ PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, int pull, mbulk_cb cb); + redisCluster *c, int pull, mbulk_cb cb, zval *z_ret); /* Handlers for things like DEL/MGET/MSET/MSETNX */ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, diff --git a/common.h b/common.h index 29b3e6c20e..7b63f9e06a 100644 --- a/common.h +++ b/common.h @@ -245,9 +245,6 @@ inline_call_user_function(HashTable *function_table, zval *object, zval *functio return ret; } -#define _IS_BOOL IS_BOOL -#define ZEND_SAME_FAKE_TYPE(faketype, realtype) ((faketype) == (realtype)) - #undef add_assoc_bool #define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key), __b) static int (*_add_assoc_bool_ex)(zval *, const char *, uint, int) = &add_assoc_bool_ex; diff --git a/library.c b/library.c index 4b01eb54d7..3dd19a0f4d 100644 --- a/library.c +++ b/library.c @@ -1562,7 +1562,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, double timeout, PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) { struct timeval tv, read_tv, *tv_ptr = NULL; - char *host = NULL, *persistent_id = NULL; + char host[1024], *persistent_id = NULL; const char *fmtstr = "%s:%d"; int host_len, err = 0; php_netstream_data_t *sock; @@ -1582,7 +1582,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) read_tv.tv_usec = (int)((redis_sock->read_timeout-read_tv.tv_sec)*1000000); if(redis_sock->host[0] == '/' && redis_sock->port < 1) { - host_len = spprintf(&host, 0, "unix://%s", redis_sock->host); + host_len = snprintf(host, sizeof(host), "unix://%s", redis_sock->host); } else { if(redis_sock->port == 0) redis_sock->port = 6379; @@ -1594,7 +1594,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) fmtstr = "[%s]:%d"; } #endif - host_len = spprintf(&host, 0, fmtstr, redis_sock->host, redis_sock->port); + host_len = snprintf(host, sizeof(host), fmtstr, redis_sock->host, redis_sock->port); } if (redis_sock->persistent) { @@ -1615,8 +1615,6 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) efree(persistent_id); } - efree(host); - if (!redis_sock->stream) { return -1; } diff --git a/redis_array.c b/redis_array.c index 8b0f1014eb..d3a5db05fb 100644 --- a/redis_array.c +++ b/redis_array.c @@ -33,11 +33,19 @@ #include "redis_array_impl.h" /* Simple macro to detect failure in a RedisArray call */ +#if (PHP_MAJOR_VERSION < 7) +#define RA_CALL_FAILED(rv, cmd) ( \ + (Z_TYPE_P(rv) == IS_BOOL && !Z_LVAL_P(rv)) || \ + (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \ + (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE")) \ +) +#else #define RA_CALL_FAILED(rv, cmd) ( \ - (ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(rv)) && !Z_LVAL_P(rv)) || \ + (Z_TYPE_P(rv) == IS_FALSE) || \ (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \ (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE")) \ ) +#endif extern zend_class_entry *redis_ce; zend_class_entry *redis_array_ce; @@ -246,18 +254,30 @@ PHP_METHOD(RedisArray, __construct) } /* extract index option. */ - if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL && ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(zpData))) { - b_index = Z_LVAL_P(zpData); + if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL) { +#if (PHP_MAJOR_VERSION < 7) + b_index = (Z_TYPE_P(zpData) == IS_BOOL && Z_LVAL_P(zpData)); +#else + b_index = (Z_TYPE_P(zpData) == IS_TRUE); +#endif } /* extract autorehash option. */ - if ((zpData = zend_hash_str_find(hOpts, "autorehash", sizeof("autorehash") - 1)) != NULL && ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(zpData))) { - b_autorehash = Z_LVAL_P(zpData); + if ((zpData = zend_hash_str_find(hOpts, "autorehash", sizeof("autorehash") - 1)) != NULL) { +#if (PHP_MAJOR_VERSION < 7) + b_autorehash = (Z_TYPE_P(zpData) == IS_BOOL && Z_LVAL_P(zpData)); +#else + b_autorehash = (Z_TYPE_P(zpData) == IS_TRUE); +#endif } /* pconnect */ - if ((zpData = zend_hash_str_find(hOpts, "pconnect", sizeof("pconnect") - 1)) != NULL && ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(zpData))) { - b_pconnect = Z_LVAL_P(zpData); + if ((zpData = zend_hash_str_find(hOpts, "pconnect", sizeof("pconnect") - 1)) != NULL) { +#if (PHP_MAJOR_VERSION < 7) + b_pconnect = (Z_TYPE_P(zpData) == IS_BOOL && Z_LVAL_P(zpData)); +#else + b_pconnect = (Z_TYPE_P(zpData) == IS_TRUE); +#endif } /* extract retry_interval option. */ @@ -272,8 +292,12 @@ PHP_METHOD(RedisArray, __construct) } /* extract lazy connect option. */ - if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL && ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(zpData))) { - b_lazy_connect = Z_LVAL_P(zpData); + if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL) { +#if (PHP_MAJOR_VERSION < 7) + b_lazy_connect = (Z_TYPE_P(zpData) == IS_BOOL && Z_LVAL_P(zpData)); +#else + b_lazy_connect = (Z_TYPE_P(zpData) == IS_TRUE); +#endif } /* extract connect_timeout option */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 250f33ec8a..68238f8102 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -54,6 +54,8 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b efree(ra->redis); efree(ra->hosts); zval_dtor(&ra->z_pure_cmds); + zval_dtor(&ra->z_dist); + zval_dtor(&ra->z_fun); efree(ra); return NULL; } @@ -178,15 +180,17 @@ ra_find_name(const char *name) { /* laod array from INI settings */ RedisArray *ra_load_array(const char *name TSRMLS_DC) { - zval *z_params_hosts, *z_hosts; - zval *z_params_prev, *z_prev; - zval *z_params_funs, *z_data, *z_fun = NULL, *z_dist = NULL; - zval *z_params_index; - zval *z_params_autorehash; - zval *z_params_retry_interval; - zval *z_params_pconnect; - zval *z_params_connect_timeout; - zval *z_params_lazy_connect; + zval *z_data, z_fun, z_dist; + zval z_params_hosts; + zval z_params_prev; + zval z_params_funs; + zval z_params_dist; + zval z_params_index; + zval z_params_autorehash; + zval z_params_retry_interval; + zval z_params_pconnect; + zval z_params_connect_timeout; + zval z_params_lazy_connect; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; @@ -202,80 +206,71 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { return ra; /* find hosts */ - MAKE_STD_ZVAL(z_params_hosts); - array_init(z_params_hosts); + array_init(&z_params_hosts); if ((iptr = INI_STR("redis.arrays.hosts")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_hosts TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_hosts TSRMLS_CC); } - if ((z_hosts = zend_hash_str_find(Z_ARRVAL_P(z_params_hosts), name, name_len)) != NULL) { - hHosts = Z_ARRVAL_P(z_hosts); + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_hosts), name, name_len)) != NULL) { + hHosts = Z_ARRVAL_P(z_data); } /* find previous hosts */ - MAKE_STD_ZVAL(z_params_prev); - array_init(z_params_prev); + array_init(&z_params_prev); if ((iptr = INI_STR("redis.arrays.previous")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_prev TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_prev TSRMLS_CC); } - if ((z_prev = zend_hash_str_find(Z_ARRVAL_P(z_params_prev), name, name_len)) != NULL) { - hPrev = Z_ARRVAL_P(z_prev); + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_prev), name, name_len)) != NULL) { + hPrev = Z_ARRVAL_P(z_data); } /* find function */ - MAKE_STD_ZVAL(z_params_funs); - array_init(z_params_funs); + array_init(&z_params_funs); if ((iptr = INI_STR("redis.arrays.functions")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_funs TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_funs TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_funs), name, name_len)) != NULL) { - MAKE_STD_ZVAL(z_fun); - *z_fun = *z_data; - zval_copy_ctor(z_fun); + ZVAL_NULL(&z_fun); + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_funs), name, name_len)) != NULL) { + ZVAL_ZVAL(&z_fun, z_data, 1, 0); } /* find distributor */ - MAKE_STD_ZVAL(z_params_funs); - array_init(z_params_funs); + array_init(&z_params_dist); if ((iptr = INI_STR("redis.arrays.distributor")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_funs TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_dist TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_funs), name, name_len)) != NULL) { - MAKE_STD_ZVAL(z_dist); - *z_dist = *z_data; - zval_copy_ctor(z_dist); + ZVAL_NULL(&z_dist); + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_dist), name, name_len)) != NULL) { + ZVAL_ZVAL(&z_dist, z_data, 1, 0); } /* find index option */ - MAKE_STD_ZVAL(z_params_index); - array_init(z_params_index); + array_init(&z_params_index); if ((iptr = INI_STR("redis.arrays.index")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_index TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_index TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_index), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_index), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_index = 1; } } /* find autorehash option */ - MAKE_STD_ZVAL(z_params_autorehash); - array_init(z_params_autorehash); + array_init(&z_params_autorehash); if ((iptr = INI_STR("redis.arrays.autorehash")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_autorehash TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_autorehash TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_autorehash), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_autorehash), name, name_len)) != NULL) { if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_autorehash = 1; } } /* find retry interval option */ - MAKE_STD_ZVAL(z_params_retry_interval); - array_init(z_params_retry_interval); + array_init(&z_params_retry_interval); if ((iptr = INI_STR("redis.arrays.retryinterval")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_retry_interval TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_retry_interval TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_retry_interval), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_retry_interval), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_LONG || Z_TYPE_P(z_data) == IS_STRING) { if (Z_TYPE_P(z_data) == IS_LONG) { l_retry_interval = Z_LVAL_P(z_data); @@ -287,36 +282,33 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } /* find pconnect option */ - MAKE_STD_ZVAL(z_params_pconnect); - array_init(z_params_pconnect); + array_init(&z_params_pconnect); if ((iptr = INI_STR("redis.arrays.pconnect")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_pconnect TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_pconnect TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_pconnect), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_pconnect), name, name_len)) != NULL) { if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_pconnect = 1; } } /* find lazy connect option */ - MAKE_STD_ZVAL(z_params_lazy_connect); - array_init(z_params_lazy_connect); + array_init(&z_params_lazy_connect); if ((iptr = INI_STR("redis.arrays.lazyconnect")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_lazy_connect TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_lazy_connect TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_lazy_connect), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_lazy_connect), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_lazy_connect = 1; } } /* find connect timeout option */ - MAKE_STD_ZVAL(z_params_connect_timeout); - array_init(z_params_connect_timeout); + array_init(&z_params_connect_timeout); if ((iptr = INI_STR("redis.arrays.connecttimeout")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_params_connect_timeout TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_connect_timeout TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL_P(z_params_connect_timeout), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_connect_timeout), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_DOUBLE || Z_TYPE_P(z_data) == IS_STRING || Z_TYPE_P(z_data) == IS_LONG @@ -332,29 +324,23 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } /* create RedisArray object */ - ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); - ra->auto_rehash = b_autorehash; - if(ra->prev) ra->prev->auto_rehash = b_autorehash; + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); + if (ra) { + ra->auto_rehash = b_autorehash; + if(ra->prev) ra->prev->auto_rehash = b_autorehash; + } /* cleanup */ - zval_dtor(z_params_hosts); - efree(z_params_hosts); - zval_dtor(z_params_prev); - efree(z_params_prev); - zval_dtor(z_params_funs); - efree(z_params_funs); - zval_dtor(z_params_index); - efree(z_params_index); - zval_dtor(z_params_autorehash); - efree(z_params_autorehash); - zval_dtor(z_params_retry_interval); - efree(z_params_retry_interval); - zval_dtor(z_params_pconnect); - efree(z_params_pconnect); - zval_dtor(z_params_connect_timeout); - efree(z_params_connect_timeout); - zval_dtor(z_params_lazy_connect); - efree(z_params_lazy_connect); + zval_dtor(&z_params_hosts); + zval_dtor(&z_params_prev); + zval_dtor(&z_params_funs); + zval_dtor(&z_params_dist); + zval_dtor(&z_params_index); + zval_dtor(&z_params_autorehash); + zval_dtor(&z_params_retry_interval); + zval_dtor(&z_params_pconnect); + zval_dtor(&z_params_connect_timeout); + zval_dtor(&z_params_lazy_connect); return ra; } @@ -362,10 +348,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { RedisArray * ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC) { - int count = zend_hash_num_elements(hosts); + int count; + RedisArray *ra; + + if (!hosts) return NULL; + count = zend_hash_num_elements(hosts); /* create object */ - RedisArray *ra = emalloc(sizeof(RedisArray)); + ra = emalloc(sizeof(RedisArray)); ra->hosts = ecalloc(count, sizeof(char *)); ra->redis = ecalloc(count, sizeof(zval)); ra->count = 0; @@ -602,11 +592,9 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { /* Go through input array and add values to the key array */ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_pairs), idx, zkey, z_val) { + zval zv, *z_new = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_new; MAKE_STD_ZVAL(z_new); -#else - zval zv, *z_new = &zv; #endif if (zkey) { diff --git a/redis_cluster.c b/redis_cluster.c index cd3381afe5..9ccfc55478 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -412,34 +412,31 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, /* Attempt to load a named cluster configured in php.ini */ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { - zval *z_seeds, *z_timeout, *z_read_timeout, *z_persistent, *z_value; + zval z_seeds, z_timeout, z_read_timeout, z_persistent, *z_value; char *iptr; double timeout=0, read_timeout=0; int persistent = 0; HashTable *ht_seeds = NULL; /* Seeds */ - MAKE_STD_ZVAL(z_seeds); - array_init(z_seeds); + array_init(&z_seeds); if ((iptr = INI_STR("redis.clusters.seeds")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_seeds TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_seeds TSRMLS_CC); } - if ((z_value = zend_hash_str_find(Z_ARRVAL_P(z_seeds), name, name_len)) != NULL) { + if ((z_value = zend_hash_str_find(Z_ARRVAL(z_seeds), name, name_len)) != NULL) { ht_seeds = Z_ARRVAL_P(z_value); } else { - zval_dtor(z_seeds); - efree(z_seeds); + zval_dtor(&z_seeds); zend_throw_exception(redis_cluster_exception_ce, "Couldn't find seeds for cluster", 0 TSRMLS_CC); return; } /* Connection timeout */ - MAKE_STD_ZVAL(z_timeout); - array_init(z_timeout); + array_init(&z_timeout); if ((iptr = INI_STR("redis.clusters.timeout")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_timeout TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_timeout TSRMLS_CC); } - if ((z_value = zend_hash_str_find(Z_ARRVAL_P(z_timeout), name, name_len)) != NULL) { + if ((z_value = zend_hash_str_find(Z_ARRVAL(z_timeout), name, name_len)) != NULL) { if (Z_TYPE_P(z_value) == IS_STRING) { timeout = atof(Z_STRVAL_P(z_value)); } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { @@ -448,12 +445,11 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { } /* Read timeout */ - MAKE_STD_ZVAL(z_read_timeout); - array_init(z_read_timeout); + array_init(&z_read_timeout); if ((iptr = INI_STR("redis.clusters.read_timeout")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_read_timeout TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_read_timeout TSRMLS_CC); } - if ((z_value = zend_hash_str_find(Z_ARRVAL_P(z_read_timeout), name, name_len)) != NULL) { + if ((z_value = zend_hash_str_find(Z_ARRVAL(z_read_timeout), name, name_len)) != NULL) { if (Z_TYPE_P(z_value) == IS_STRING) { read_timeout = atof(Z_STRVAL_P(z_value)); } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { @@ -462,12 +458,11 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { } /* Persistent connections */ - MAKE_STD_ZVAL(z_persistent); - array_init(z_persistent); + array_init(&z_persistent); if ((iptr = INI_STR("redis.clusters.persistent")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), z_persistent TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_persistent TSRMLS_CC); } - if ((z_value = zend_hash_str_find(Z_ARRVAL_P(z_persistent), name, name_len)) != NULL) { + if ((z_value = zend_hash_str_find(Z_ARRVAL(z_persistent), name, name_len)) != NULL) { if (Z_TYPE_P(z_value) == IS_STRING) { persistent = atoi(Z_STRVAL_P(z_value)); } else if (Z_TYPE_P(z_value) == IS_LONG) { @@ -479,12 +474,10 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent TSRMLS_CC); /* Clean up our arrays */ - zval_dtor(z_seeds); - efree(z_seeds); - zval_dtor(z_timeout); - efree(z_timeout); - zval_dtor(z_read_timeout); - efree(z_read_timeout); + zval_dtor(&z_seeds); + zval_dtor(&z_timeout); + zval_dtor(&z_read_timeout); + zval_dtor(&z_persistent); } /* @@ -975,10 +968,9 @@ PHP_METHOD(RedisCluster, mset) { /* {{{ proto array RedisCluster::msetnx(array keyvalues) */ PHP_METHOD(RedisCluster, msetnx) { - zval *z_ret; + zval *z_ret = emalloc(sizeof(zval)); // Array response - MAKE_STD_ZVAL(z_ret); array_init(z_ret); // Parse args and process. If we get a failure, free mem and return FALSE @@ -2051,12 +2043,11 @@ PHP_METHOD(RedisCluster, _unserialize) { /* {{{ proto array RedisCluster::_masters() */ PHP_METHOD(RedisCluster, _masters) { redisCluster *c = GET_CONTEXT(); - zval *z_ret, *z_sub; redisClusterNode *node; + zval zv, *z_ret = &zv; char *host; short port; - MAKE_STD_ZVAL(z_ret); array_init(z_ret); for(zend_hash_internal_pointer_reset(c->nodes); @@ -2066,7 +2057,10 @@ PHP_METHOD(RedisCluster, _masters) { host = node->sock->host; port = node->sock->port; + zval z, *z_sub = &z; +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_sub); +#endif array_init(z_sub); add_next_index_stringl(z_sub, host, strlen(host)); @@ -2074,8 +2068,7 @@ PHP_METHOD(RedisCluster, _masters) { add_next_index_zval(z_ret, z_sub); } - *return_value = *z_ret; - efree(z_ret); + RETVAL_ZVAL(z_ret, 0, 1); } PHP_METHOD(RedisCluster, _redir) { @@ -3032,4 +3025,4 @@ PHP_METHOD(RedisCluster, command) { CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); } -/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ +/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/redis_commands.c b/redis_commands.c index fb73846a05..5c747bc41c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -533,7 +533,11 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Check for withscores and limit */ if (IS_WITHSCORES_ARG(zkey->val, zkey->len)) { - *withscores = (ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(z_ele)) && Z_LVAL_P(z_ele)); +#if (PHP_MAJOR_VERSION < 7) + *withscores = (Z_TYPE_P(z_ele) == IS_BOOL && Z_LVAL_P(z_ele)); +#else + *withscores = (Z_TYPE_P(z_ele) == IS_TRUE); +#endif } else if (IS_LIMIT_ARG(zkey->val, zkey->len) && Z_TYPE_P(z_ele) == IS_ARRAY) { HashTable *htlimit = Z_ARRVAL_P(z_ele); zval *zoff, *zcnt; @@ -2415,7 +2419,11 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // ALPHA if (((z_ele = zend_hash_str_find(ht_opts, "alpha", sizeof("alpha") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL) && - (ZEND_SAME_FAKE_TYPE(_IS_BOOL, Z_TYPE_P(z_ele)) && Z_LVAL_P(z_ele)) +#if (PHP_MAJOR_VERSION < 7) + (Z_TYPE_P(z_ele) == IS_BOOL && Z_LVAL_P(z_ele)) +#else + (Z_TYPE_P(z_ele) == IS_TRUE) +#endif ) { add_next_index_stringl(&z_argv, "ALPHA", sizeof("ALPHA") - 1); } From 98737bc02db8eb13a8298cf242b5f673eac5cabe Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 12 Nov 2016 13:52:34 +0200 Subject: [PATCH 0622/1986] WIP: php7 compatibility --- cluster_library.c | 4 +++- library.c | 8 ++------ redis_cluster.c | 51 ++++++++++++++++++++++++++++++----------------- redis_commands.c | 3 ++- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 8d9b9b6d07..0f2f958308 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1816,7 +1816,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, /* Recursive MULTI BULK -> PHP style response handling */ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) { - zval *z_sub_ele; + zval zv, *z_sub_ele = &zv; int i; switch(r->type) { @@ -1835,7 +1835,9 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) } break; case TYPE_MULTIBULK: +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_sub_ele); +#endif array_init(z_sub_ele); for(i=0;ielements;i++) { cluster_mbulk_variant_resp(r->element[i], z_sub_ele); diff --git a/library.c b/library.c index 3dd19a0f4d..be02cf9e10 100644 --- a/library.c +++ b/library.c @@ -931,9 +931,7 @@ redis_parse_info_response(char *response, zval *z_ret) if(pos == NULL) { break; } - key = emalloc(pos - cur + 1); - memcpy(key, cur, pos-cur); - key[pos-cur] = 0; + key = estrndup(cur, pos - cur); /* value */ cur = pos + 1; @@ -942,9 +940,7 @@ redis_parse_info_response(char *response, zval *z_ret) efree(key); break; } - value = emalloc(pos - cur + 1); - memcpy(value, cur, pos-cur); - value[pos-cur] = 0; + value = estrndup(cur, pos - cur); pos += 2; /* \r, \n */ cur = pos; diff --git a/redis_cluster.c b/redis_cluster.c index 9ccfc55478..1c43f6f663 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -917,7 +917,13 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, /* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */ PHP_METHOD(RedisCluster, del) { - zval *z_ret = emalloc(sizeof(zval)); + zval *z_ret; + +#if (PHP_MAJOR_VERSION < 7) + MAKE_STD_ZVAL(z_ret); +#else + z_ret = emalloc(sizeof(zval)); +#endif // Initialize a LONG value to zero for our return ZVAL_LONG(z_ret, 0); @@ -936,7 +942,11 @@ PHP_METHOD(RedisCluster, mget) { zval *z_ret; // Array response +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_ret); +#else + z_ret = emalloc(sizeof(zval)); +#endif array_init(z_ret); // Parse args, process @@ -954,7 +964,11 @@ PHP_METHOD(RedisCluster, mset) { zval *z_ret; // Response, defaults to TRUE +#if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_ret); +#else + z_ret = emalloc(sizeof(zval)); +#endif ZVAL_TRUE(z_ret); // Parse args and process. If we get a failure, free zval and return FALSE. @@ -968,9 +982,14 @@ PHP_METHOD(RedisCluster, mset) { /* {{{ proto array RedisCluster::msetnx(array keyvalues) */ PHP_METHOD(RedisCluster, msetnx) { - zval *z_ret = emalloc(sizeof(zval)); + zval *z_ret; // Array response +#if (PHP_MAJOR_VERSION < 7) + MAKE_STD_ZVAL(z_ret); +#else + z_ret = emalloc(sizeof(zval)); +#endif array_init(z_ret); // Parse args and process. If we get a failure, free mem and return FALSE @@ -1021,7 +1040,7 @@ PHP_METHOD(RedisCluster, keys) { int pat_len, pat_free, cmd_len; char *pat, *cmd; clusterReply *resp; - zval *z_ret; + zval zv, *z_ret = &zv; int i; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pat, &pat_len) @@ -1035,7 +1054,6 @@ PHP_METHOD(RedisCluster, keys) { cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pat, pat_len); if(pat_free) efree(pat); - MAKE_STD_ZVAL(z_ret); array_init(z_ret); /* Treat as readonly */ @@ -2283,7 +2301,7 @@ static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { int key_len, key_free; - zval *z_host, *z_port, *z_tmp = NULL; + zval *z_host, *z_port, z_tmp; short slot; char *key; @@ -2292,28 +2310,25 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG || Z_TYPE_P(z_arg)==IS_DOUBLE) { + ZVAL_NULL(&z_tmp); /* Allow for any scalar here */ - if (Z_TYPE_P(z_arg) != IS_STRING) { - MAKE_STD_ZVAL(z_tmp); - *z_tmp = *z_arg; - zval_copy_ctor(z_tmp); - convert_to_string(z_tmp); - z_arg = z_tmp; + if (Z_TYPE_P(z_arg) == IS_STRING) { + key = Z_STRVAL_P(z_arg); + key_len = Z_STRLEN_P(z_arg); + } else { + ZVAL_ZVAL(&z_tmp, z_arg, 1, 0); + convert_to_string(&z_tmp); + key = Z_STRVAL(z_tmp); + key_len = Z_STRLEN(z_tmp); } - key = Z_STRVAL_P(z_arg); - key_len = Z_STRLEN_P(z_arg); - /* Hash it */ key_free = redis_key_prefix(c->flags, &key, &key_len); slot = cluster_hash_key(key, key_len); if(key_free) efree(key); /* Destroy our temp value if we had to convert it */ - if (z_tmp) { - zval_dtor(z_tmp); - efree(z_tmp); - } + zval_dtor(&z_tmp); } else if (Z_TYPE_P(z_arg) == IS_ARRAY && (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL && (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL && diff --git a/redis_commands.c b/redis_commands.c index 5c747bc41c..e808f271c6 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -373,7 +373,8 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - int key_len, key_free; + size_t key_len; + int key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) ==FAILURE) From ca4dc3b8c7e1cc59d019bb9b8664cb6f397bc6b4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 12 Nov 2016 19:52:03 +0200 Subject: [PATCH 0623/1986] php7 compatibility All tests passed. \o/ --- .travis.yml | 2 -- cluster_library.c | 32 +++++++++++++++++++------------- library.c | 28 +++++++--------------------- php_redis.h | 2 +- redis_cluster.c | 3 ++- 5 files changed, 29 insertions(+), 38 deletions(-) diff --git a/.travis.yml b/.travis.yml index ba8f957a65..75c63e4dab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ php: - nightly matrix: allow_failures: - - php: 7.0 - - php: 7.1 - php: nightly before_install: phpize install: ./configure CFLAGS=-Wall --prefix=/usr && sudo make install diff --git a/cluster_library.c b/cluster_library.c index 0f2f958308..8ac9971f51 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -118,14 +118,14 @@ static void cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, clusterReply **element, int *err TSRMLS_DC) { - size_t idx = 0, sz; + int i; + size_t sz; clusterReply *r; long len; char buf[1024]; - while(elements-- > 0) { - element[idx] = ecalloc(1, sizeof(clusterReply)); - r = element[idx]; + for (i = 0; i < elements; i++) { + r = element[i] = ecalloc(1, sizeof(clusterReply)); // Bomb out, flag error condition on a communication failure if(redis_read_reply_type(sock, &r->type, &len TSRMLS_CC)<0) { @@ -158,7 +158,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, } break; case TYPE_MULTIBULK: - r->element = ecalloc(r->len,r->len*sizeof(clusterReply*)); + r->element = ecalloc(r->len,sizeof(clusterReply*)); r->elements = r->len; cluster_multibulk_resp_recursive(sock, r->elements, r->element, err TSRMLS_CC); @@ -168,8 +168,6 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, *err = 1; return; } - - idx++; } } @@ -2032,7 +2030,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // Return our array if(CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_result, 0, 1); + RETVAL_ZVAL(z_result, 1, 0); } else { add_next_index_zval(&c->multi_resp, z_result); } @@ -2341,7 +2339,10 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); if (line != NULL) { - zval *z = NULL; + zval zv, *z = &zv; +#if (PHP_MAJOR_VERSION < 7) + z = NULL; +#endif if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_next_index_zval(z_result, z); } else { @@ -2382,7 +2383,10 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, key_len = line_len; } else { /* Attempt serialization */ - zval *z = NULL; + zval zv, *z = &zv; +#if (PHP_MAJOR_VERSION < 7) + z = NULL; +#endif if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval(z_result, key, z); } else { @@ -2417,12 +2421,11 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, key = line; key_len = line_len; } else { - zval *z = NULL; + zval zv, *z = &zv; if (redis_unserialize(redis_sock,key,key_len, &z TSRMLS_CC)) { convert_to_string(z); add_assoc_double_ex(z_result, Z_STRVAL_P(z), Z_STRLEN_P(z), atof(line)); zval_dtor(z); - efree(z); } else { add_assoc_double_ex(z_result, key, key_len, atof(line)); } @@ -2450,7 +2453,10 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); if(line != NULL) { - zval *z = NULL; + zval zv, *z = &zv; +#if (PHP_MAJOR_VERSION < 7) + z = NULL; +#endif if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval_ex(z_result,Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), z); diff --git a/library.c b/library.c index be02cf9e10..b855e9e3e6 100644 --- a/library.c +++ b/library.c @@ -975,11 +975,9 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo RETURN_FALSE; } + zval zv, *z_ret = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_ret; MAKE_STD_ZVAL(z_ret); -#else - zval zv, *z_ret = &zv; #endif /* Parse it out */ @@ -1287,11 +1285,9 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } numElems = atoi(inbuf+1); + zval zv, *z_multi_result = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_multi_result; MAKE_STD_ZVAL(z_multi_result); -#else - zval zv, *z_multi_result = &zv; #endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ @@ -1456,11 +1452,9 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock RETURN_FALSE; } + zval zv, *z_result = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_result; MAKE_STD_ZVAL(z_result); -#else - zval zv, *z_result = &zv; #endif array_init(z_result); @@ -1783,11 +1777,9 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, return -1; } numElems = atoi(inbuf+1); + zval zv, *z_multi_result = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_multi_result; MAKE_STD_ZVAL(z_multi_result); -#else - zval zv, *z_multi_result = &zv; #endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ @@ -1832,11 +1824,9 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock return -1; } numElems = atoi(inbuf+1); + zval zv, *z_multi_result = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_multi_result; MAKE_STD_ZVAL(z_multi_result); -#else - zval zv, *z_multi_result = &zv; #endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ @@ -1917,11 +1907,9 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc return -1; } numElems = atoi(inbuf+1); + zval zv, *z_multi_result = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_multi_result; MAKE_STD_ZVAL(z_multi_result); -#else - zval zv, *z_multi_result = &zv; #endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ @@ -2371,11 +2359,9 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } + zval zv, *z_ret = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_ret; MAKE_STD_ZVAL(z_ret); -#else - zval zv, *z_ret = &zv; #endif /* Switch based on our top level reply type */ switch(reply_type) { diff --git a/php_redis.h b/php_redis.h index b3cd4c9a1c..01e71ed0cc 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "2.2.8-rc1" +#define PHP_REDIS_VERSION "3.1.0-rc1" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); diff --git a/redis_cluster.c b/redis_cluster.c index 1c43f6f663..e4622b94a3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2711,7 +2711,8 @@ PHP_METHOD(RedisCluster, info) { redisCluster *c = GET_CONTEXT(); REDIS_REPLY_TYPE rtype; char *cmd, *opt=NULL; - int cmd_len, opt_len; + int cmd_len; + size_t opt_len; void *ctx = NULL; zval *z_arg; short slot; From 843128386fc25005495af2ad8b324c06f918d7a9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 12 Nov 2016 23:10:16 +0200 Subject: [PATCH 0624/1986] php7 compatibility zval_ptr_dtor --- cluster_library.c | 15 +++++++++------ library.c | 11 +++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 8ac9971f51..2b3a31b74e 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1642,7 +1642,7 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval z_tab, *z_tmp, *z_ret; + zval z_tab, *z_tmp; int pull=0; @@ -1669,11 +1669,11 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * // Set up our callback pointers #if (PHP_MAJOR_VERSION < 7) - zval **z_args[4]; + zval *z_ret, **z_args[4]; sctx->cb.retval_ptr_ptr = &z_ret; #else - zval z_args[4]; - sctx->cb.retval = z_ret; + zval z_ret, z_args[4]; + sctx->cb.retval = &z_ret; #endif sctx->cb.params = z_args; sctx->cb.no_separation = 0; @@ -1753,7 +1753,7 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * } // If we have a return value, free it - if(z_ret) zval_ptr_dtor(&z_ret); + zval_ptr_dtor(&z_ret); zval_dtor(&z_tab); } @@ -1941,7 +1941,10 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, /* Call our specified callback */ if (cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC)==FAILURE) { - zval_ptr_dtor(z_result); + zval_dtor(z_result); +#if (PHP_MAJOR_VERSION < 7) + efree(z_result); +#endif CLUSTER_RETURN_FALSE(c); } } diff --git a/library.c b/library.c index b855e9e3e6..262e5ee741 100644 --- a/library.c +++ b/library.c @@ -258,7 +258,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; - zval *z_tmp, *z_ret, z_resp; + zval *z_tmp, z_resp; // Consume response(s) from subscribe, which will vary on argc while(sctx->argc--) { @@ -287,11 +287,11 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, } #if (PHP_MAJOR_VERSION < 7) - zval **z_args[4]; + zval *z_ret, **z_args[4]; sctx->cb.retval_ptr_ptr = &z_ret; #else - zval z_args[4]; - sctx->cb.retval = z_ret; + zval z_ret, z_args[4]; + sctx->cb.retval = &z_ret; #endif sctx->cb.params = z_args; sctx->cb.no_separation = 0; @@ -370,8 +370,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, } // If we have a return value free it - if(z_ret) zval_ptr_dtor(&z_ret); - + zval_ptr_dtor(&z_ret); zval_dtor(&z_resp); } From d7d18bfa05d34b16941410bd2d45e326ab1c84d9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 13 Nov 2016 14:59:54 +0200 Subject: [PATCH 0625/1986] refactoring Use estrndup instead os estrdup/emalloc+memcpy --- redis.c | 44 +++++++++++++++++--------------------------- redis_array_impl.c | 2 +- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/redis.c b/redis.c index 585417930c..67a1db0f71 100644 --- a/redis.c +++ b/redis.c @@ -1456,9 +1456,9 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, } /* first line, sort. */ - cmd_lines[1] = estrdup("$4"); + cmd_lines[1] = estrndup("$4", 2); cmd_sizes[1] = 2; - cmd_lines[2] = estrdup("SORT"); + cmd_lines[2] = estrndup("SORT", 4); cmd_sizes[2] = 4; /* Prefix our key if we need to */ @@ -1466,9 +1466,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, /* second line, key */ cmd_sizes[3] = redis_cmd_format(&cmd_lines[3], "$%d", key_len); - cmd_lines[4] = emalloc(key_len + 1); - memcpy(cmd_lines[4], key, key_len); - cmd_lines[4][key_len] = 0; + cmd_lines[4] = estrndup(key, key_len); cmd_sizes[4] = key_len; /* If we prefixed our key, free it */ @@ -1477,28 +1475,26 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_elements = 5; if(pattern && pattern_len) { /* BY */ - cmd_lines[cmd_elements] = estrdup("$2"); + cmd_lines[cmd_elements] = estrndup("$2", 2); cmd_sizes[cmd_elements] = 2; cmd_elements++; - cmd_lines[cmd_elements] = estrdup("BY"); + cmd_lines[cmd_elements] = estrndup("BY", 2); cmd_sizes[cmd_elements] = 2; cmd_elements++; /* pattern */ cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", pattern_len); cmd_elements++; - cmd_lines[cmd_elements] = emalloc(pattern_len + 1); - memcpy(cmd_lines[cmd_elements], pattern, pattern_len); - cmd_lines[cmd_elements][pattern_len] = 0; + cmd_lines[cmd_elements] = estrndup(pattern, pattern_len); cmd_sizes[cmd_elements] = pattern_len; cmd_elements++; } if(sort_start >= 0 && sort_count >= 0) { /* LIMIT */ - cmd_lines[cmd_elements] = estrdup("$5"); + cmd_lines[cmd_elements] = estrndup("$5", 2); cmd_sizes[cmd_elements] = 2; cmd_elements++; - cmd_lines[cmd_elements] = estrdup("LIMIT"); + cmd_lines[cmd_elements] = estrndup("LIMIT", 5); cmd_sizes[cmd_elements] = 5; cmd_elements++; @@ -1520,10 +1516,10 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, } if(get && get_len) { /* GET */ - cmd_lines[cmd_elements] = estrdup("$3"); + cmd_lines[cmd_elements] = estrndup("$3", 2); cmd_sizes[cmd_elements] = 2; cmd_elements++; - cmd_lines[cmd_elements] = estrdup("GET"); + cmd_lines[cmd_elements] = estrndup("GET", 3); cmd_sizes[cmd_elements] = 3; cmd_elements++; @@ -1531,9 +1527,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", get_len); cmd_elements++; - cmd_lines[cmd_elements] = emalloc(get_len + 1); - memcpy(cmd_lines[cmd_elements], get, get_len); - cmd_lines[cmd_elements][get_len] = 0; + cmd_lines[cmd_elements] = estrndup(get, get_len); cmd_sizes[cmd_elements] = get_len; cmd_elements++; } @@ -1543,27 +1537,25 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", sort_len); cmd_elements++; - cmd_lines[cmd_elements] = emalloc(sort_len + 1); - memcpy(cmd_lines[cmd_elements], sort, sort_len); - cmd_lines[cmd_elements][sort_len] = 0; + cmd_lines[cmd_elements] = estrndup(sort, sort_len); cmd_sizes[cmd_elements] = sort_len; cmd_elements++; if(use_alpha) { /* ALPHA */ - cmd_lines[cmd_elements] = estrdup("$5"); + cmd_lines[cmd_elements] = estrndup("$5", 2); cmd_sizes[cmd_elements] = 2; cmd_elements++; - cmd_lines[cmd_elements] = estrdup("ALPHA"); + cmd_lines[cmd_elements] = estrndup("ALPHA", 5); cmd_sizes[cmd_elements] = 5; cmd_elements++; } if(store && store_len) { /* STORE */ - cmd_lines[cmd_elements] = estrdup("$5"); + cmd_lines[cmd_elements] = estrndup("$5", 2); cmd_sizes[cmd_elements] = 2; cmd_elements++; - cmd_lines[cmd_elements] = estrdup("STORE"); + cmd_lines[cmd_elements] = estrndup("STORE", 5); cmd_sizes[cmd_elements] = 5; cmd_elements++; @@ -1571,9 +1563,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", store_len); cmd_elements++; - cmd_lines[cmd_elements] = emalloc(store_len + 1); - memcpy(cmd_lines[cmd_elements], store, store_len); - cmd_lines[cmd_elements][store_len] = 0; + cmd_lines[cmd_elements] = estrndup(store, store_len); cmd_sizes[cmd_elements] = store_len; cmd_elements++; } diff --git a/redis_array_impl.c b/redis_array_impl.c index 68238f8102..5d20441492 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -60,11 +60,11 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b return NULL; } - ra->hosts[i] = estrdup(Z_STRVAL_P(zpData)); /* default values */ host = Z_STRVAL_P(zpData); host_len = Z_STRLEN_P(zpData); + ra->hosts[i] = estrndup(host, host_len); port = 6379; if((p = strrchr(host, ':'))) { /* found port */ From af8e9a3f46557b03e21b2be433eda2655d7f5718 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 13 Nov 2016 21:03:17 +0200 Subject: [PATCH 0626/1986] php7 compatibility Define strlen_t (int for php5 and size_t for php7) and use it as the length argument of the 's' specifier for zend_parse_method_parameters --- common.h | 3 +- redis.c | 51 ++++++----- redis_array.c | 14 +-- redis_cluster.c | 27 +++--- redis_commands.c | 229 +++++++++++++++++++++++++---------------------- 5 files changed, 178 insertions(+), 146 deletions(-) diff --git a/common.h b/common.h index 7b63f9e06a..21532cc2c8 100644 --- a/common.h +++ b/common.h @@ -304,10 +304,11 @@ static void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * T #define php_var_serialize(buf, struc, data) _php_var_serialize(buf, &struc, data TSRMLS_CC) static int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; #define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) - +typedef int strlen_t; #else #include #include +typedef size_t strlen_t; #endif /* NULL check so Eclipse doesn't go crazy */ diff --git a/redis.c b/redis.c index 67a1db0f71..f42f0bb386 100644 --- a/redis.c +++ b/redis.c @@ -733,14 +733,9 @@ PHP_METHOD(Redis, pconnect) PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zval *object, *socket; - int host_len; - char *host = NULL; - long port = -1; - long retry_interval = 0; - - char *persistent_id = NULL; - int persistent_id_len = -1; - + char *host = NULL, *persistent_id = NULL; + long port = -1, retry_interval = 0; + strlen_t host_len, persistent_id_len; double timeout = 0.0; RedisSock *redis_sock = NULL; @@ -759,7 +754,6 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { return FAILURE; } else if (!persistent) { persistent_id = NULL; - persistent_id_len = -1; } if (timeout < 0L || timeout > INT_MAX) { @@ -1428,8 +1422,9 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, zval *object; RedisSock *redis_sock; char *key = NULL, *pattern = NULL, *get = NULL, *store = NULL, *cmd; - int key_len, pattern_len=-1, get_len=-1, store_len=-1, cmd_len, key_free; + int cmd_len, key_free; long sort_start = -1, sort_count = -1; + strlen_t key_len, pattern_len, get_len, store_len; int cmd_elements; @@ -1462,7 +1457,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_sizes[2] = 4; /* Prefix our key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); /* second line, key */ cmd_sizes[3] = redis_cmd_format(&cmd_lines[3], "$%d", key_len); @@ -1738,7 +1733,8 @@ PHP_METHOD(Redis, info) { zval *object; RedisSock *redis_sock; char *cmd, *opt = NULL; - int cmd_len, opt_len; + strlen_t opt_len; + int cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|s", &object, redis_ce, &opt, &opt_len) @@ -2602,8 +2598,9 @@ PHP_METHOD(Redis, slaveof) zval *object; RedisSock *redis_sock; char *cmd = "", *host = NULL; - int cmd_len, host_len; + strlen_t host_len; long port = 6379; + int cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sl", &object, redis_ce, &host, @@ -2699,8 +2696,9 @@ PHP_METHOD(Redis, config) zval *object; RedisSock *redis_sock; char *key = NULL, *val = NULL, *cmd, *op = NULL; - int key_len, val_len, cmd_len, op_len; + strlen_t key_len, val_len, op_len; enum {CFG_GET, CFG_SET} mode; + int cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss|s", &object, redis_ce, &op, &op_len, @@ -2754,7 +2752,8 @@ PHP_METHOD(Redis, slowlog) { zval *object; RedisSock *redis_sock; char *arg, *cmd; - int arg_len, cmd_len; + int cmd_len; + strlen_t arg_len; long option; enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; @@ -2935,7 +2934,8 @@ PHP_METHOD(Redis, pubsub) { zval *object; RedisSock *redis_sock; char *keyword, *cmd; - int kw_len, cmd_len; + int cmd_len; + strlen_t kw_len; PUBSUB_TYPE type; zval *arg=NULL; @@ -3084,7 +3084,8 @@ PHP_METHOD(Redis, evalsha) { zval *object, *args= NULL; char *cmd, *sha; - int cmd_len, sha_len; + int cmd_len; + strlen_t sha_len; long keys_count = 0; RedisSock *redis_sock; @@ -3121,7 +3122,8 @@ PHP_METHOD(Redis, eval) zval *object, *args = NULL; RedisSock *redis_sock; char *script, *cmd = ""; - int script_len, cmd_len; + int cmd_len; + strlen_t script_len; long keys_count = 0; // Attempt to parse parameters @@ -3278,7 +3280,8 @@ PHP_METHOD(Redis, migrate) { zval *object; RedisSock *redis_sock; char *cmd, *host, *key; - int cmd_len, host_len, key_len, key_free; + int cmd_len, key_free; + strlen_t host_len, key_len; zend_bool copy=0, replace=0; long port, dest_db, timeout; @@ -3297,7 +3300,7 @@ PHP_METHOD(Redis, migrate) { } // Prefix our key if we need to, build our command - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); /* Construct our command */ if(copy && replace) { @@ -3562,7 +3565,8 @@ PHP_METHOD(Redis, client) { zval *object; RedisSock *redis_sock; char *cmd, *opt=NULL, *arg=NULL; - int cmd_len, opt_len, arg_len; + strlen_t opt_len, arg_len; + int cmd_len; // Parse our method parameters if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), @@ -3717,7 +3721,8 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { RedisSock *redis_sock; HashTable *hash; char *pattern=NULL, *cmd, *key=NULL; - int cmd_len, key_len=0, pattern_len=0, num_elements, key_free=0; + int cmd_len, num_elements, key_free=0; + strlen_t key_len = 0, pattern_len = 0; long count=0, iter; /* Different prototype depending on if this is a key based scan */ @@ -3770,7 +3775,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Prefix our key if we've got one and we have a prefix set */ if(key_len) { - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); } /** diff --git a/redis_array.c b/redis_array.c index d3a5db05fb..a8bf7419d7 100644 --- a/redis_array.c +++ b/redis_array.c @@ -453,7 +453,7 @@ PHP_METHOD(RedisArray, __call) zval *z_args; char *cmd; - size_t cmd_len; + strlen_t cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, redis_array_ce, &cmd, &cmd_len, &z_args) == FAILURE) { @@ -493,8 +493,9 @@ PHP_METHOD(RedisArray, _target) zval *object; RedisArray *ra; char *key; - int key_len, i; + strlen_t key_len; zval *redis_inst; + int i; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, redis_array_ce, &key, &key_len) == FAILURE) { @@ -518,7 +519,7 @@ PHP_METHOD(RedisArray, _instance) zval *object; RedisArray *ra; char *target; - int target_len; + strlen_t target_len; zval *z_redis; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", @@ -666,7 +667,8 @@ PHP_METHOD(RedisArray, keys) zval *object, z_args[1], z_fun; RedisArray *ra; char *pattern; - int pattern_len, i; + strlen_t pattern_len; + int i; /* Make sure the prototype is correct */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", @@ -751,7 +753,7 @@ PHP_METHOD(RedisArray, setOption) RedisArray *ra; long opt; char *val_str; - int val_len; + strlen_t val_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", &object, redis_array_ce, &opt, &val_str, &val_len) == FAILURE) { @@ -1289,7 +1291,7 @@ PHP_METHOD(RedisArray, multi) RedisArray *ra; zval *z_redis; char *host; - int host_len; + strlen_t host_len; long multi_value = MULTI; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", diff --git a/redis_cluster.c b/redis_cluster.c index e4622b94a3..a288deeb8c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -488,7 +488,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { PHP_METHOD(RedisCluster, __construct) { zval *object, *z_seeds=NULL; char *name; - long name_len; + strlen_t name_len; double timeout = 0.0, read_timeout = 0.0; zend_bool persistent = 0; redisCluster *context = GET_CONTEXT(); @@ -1037,11 +1037,11 @@ PHP_METHOD(RedisCluster, exists) { PHP_METHOD(RedisCluster, keys) { redisCluster *c = GET_CONTEXT(); redisClusterNode *node; - int pat_len, pat_free, cmd_len; + strlen_t pat_len; char *pat, *cmd; clusterReply *resp; zval zv, *z_ret = &zv; - int i; + int i, pat_free, cmd_len; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pat, &pat_len) ==FAILURE) @@ -1050,7 +1050,7 @@ PHP_METHOD(RedisCluster, keys) { } /* Prefix and then build our command */ - pat_free = redis_key_prefix(c->flags, &pat, &pat_len); + pat_free = redis_key_prefix(c->flags, &pat, (int *)&pat_len); cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pat, pat_len); if(pat_free) efree(pat); @@ -1899,12 +1899,13 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, { redisClusterNode *node=NULL; char *lua, *key; - int key_free, args_count=0, lua_len, key_len; + int key_free, args_count=0, key_len; zval *z_arr=NULL, *z_ele; HashTable *ht_arr; long num_keys = 0; short slot = 0; smart_string cmdstr = {0}; + strlen_t lua_len; /* Parse args */ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, @@ -2464,7 +2465,8 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, { redisCluster *c = GET_CONTEXT(); char *cmd, *pat=NULL, *key=NULL; - int cmd_len, key_len=0, pat_len=0, key_free=0; + strlen_t key_len = 0, pat_len = 0; + int cmd_len, key_free=0; short slot; zval *z_it; HashTable *hash; @@ -2499,7 +2501,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, } // Apply any key prefix we have, get the slot - key_free = redis_key_prefix(c->flags, &key, &key_len); + key_free = redis_key_prefix(c->flags, &key, (int *)&key_len); slot = cluster_hash_key(key, key_len); // If SCAN_RETRY is set, loop until we get a zero iterator or until @@ -2555,7 +2557,8 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, PHP_METHOD(RedisCluster, scan) { redisCluster *c = GET_CONTEXT(); char *cmd, *pat=NULL; - int pat_len=0, cmd_len; + strlen_t pat_len = 0; + int cmd_len; short slot; zval *z_it, *z_node; long it, num_ele, count=0; @@ -2712,7 +2715,7 @@ PHP_METHOD(RedisCluster, info) { REDIS_REPLY_TYPE rtype; char *cmd, *opt=NULL; int cmd_len; - size_t opt_len; + strlen_t opt_len; void *ctx = NULL; zval *z_arg; short slot; @@ -2763,7 +2766,8 @@ PHP_METHOD(RedisCluster, info) { PHP_METHOD(RedisCluster, client) { redisCluster *c = GET_CONTEXT(); char *cmd, *opt=NULL, *arg=NULL; - int cmd_len, opt_len, arg_len; + int cmd_len; + strlen_t opt_len, arg_len; REDIS_REPLY_TYPE rtype; zval *z_node; short slot; @@ -2937,7 +2941,8 @@ PHP_METHOD(RedisCluster, echo) { REDIS_REPLY_TYPE rtype; zval *z_arg; char *cmd, *msg; - int cmd_len, msg_len; + int cmd_len; + strlen_t msg_len; short slot; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &msg, diff --git a/redis_commands.c b/redis_commands.c index e808f271c6..a3f2b9ff39 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -88,7 +88,7 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *arg; - size_t arg_len; + strlen_t arg_len; // Parse args if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) @@ -109,7 +109,8 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key = NULL, *val=NULL; - int key_len, val_len, val_free, key_free; + int val_len, val_free, key_free; + strlen_t key_len; long expire; zval *z_val; @@ -121,7 +122,7 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Serialize value, prefix key val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "sls", key, key_len, expire, @@ -142,7 +143,8 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *val; - int key_len, val_len, key_free; + strlen_t key_len, val_len; + int key_free; long lval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &key, &key_len, @@ -152,7 +154,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key if requested - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sds", key, key_len, (int)lval, @@ -173,7 +175,8 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *val; - int key_len, val_len, key_free, val_free; + int key_free, val_free; + strlen_t key_len, val_len; zval *z_val; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, @@ -182,8 +185,8 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); + val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, @@ -204,7 +207,8 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *val; - int key_len, val_len, key_free; + strlen_t key_len, val_len; + int key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &val, &val_len)==FAILURE) @@ -213,7 +217,7 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, @@ -233,7 +237,8 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *val1, *val2; - int key_len, val1_len, val2_len, key_free; + strlen_t key_len, val1_len, val2_len; + int key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, &val1, &val1_len, &val2, &val2_len)==FAILURE) @@ -242,7 +247,7 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, val1, @@ -264,7 +269,7 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key1, *key2; - int key1_len, key2_len; + strlen_t key1_len, key2_len; int key1_free, key2_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key1, &key1_len, @@ -274,8 +279,8 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix both keys - key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); - key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); + key1_free = redis_key_prefix(redis_sock, &key1, (int *)&key1_len); + key2_free = redis_key_prefix(redis_sock, &key2, (int *)&key2_len); // If a slot is requested, we can test that they hash the same if(slot) { @@ -309,7 +314,8 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - int key_len, key_free; + int key_free; + strlen_t key_len; long lval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &key, &key_len, @@ -319,7 +325,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Disallow zero length keys (for now) if(key_len == 0) { @@ -343,7 +349,8 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - int key_len, key_free; + int key_free; + strlen_t key_len; long val1, val2; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &key, &key_len, @@ -353,7 +360,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sll", key, key_len, val1, @@ -373,7 +380,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - size_t key_len; + strlen_t key_len; int key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) @@ -383,7 +390,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "s", key, key_len); @@ -402,7 +409,8 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - int key_len, key_free; + strlen_t key_len; + int key_free; double val; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &key, &key_len, @@ -412,7 +420,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "sf", key, key_len, val); @@ -469,7 +477,8 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *slot, void **ctx) { char *key; - int key_len, key_free; + int key_free; + strlen_t key_len; long start, end; zend_bool ws=0; @@ -479,7 +488,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); if(ws) { *cmd_len = redis_cmd_format_static(cmd, kw, "sdds", key, key_len, start, end, "WITHSCORES", sizeof("WITHSCORES")-1); @@ -507,12 +516,10 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, void **ctx) { - char *key; - int key_len, key_free; - char *start, *end; - int start_len, end_len; - int has_limit=0; + char *key, *start, *end; + int key_free, has_limit=0; long offset, count; + strlen_t key_len, start_len, end_len; zval *z_opt=NULL, *z_ele; zend_string *zkey; ulong idx; @@ -557,7 +564,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key, set slot - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); CMD_SET_SLOT(slot,key,key_len); // Construct our command @@ -592,11 +599,11 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *agg_op=NULL; - int key_free, key_len; + int key_free, argc = 2, keys_count; + strlen_t key_len, agg_op_len = 0; zval *z_keys, *z_weights=NULL, *z_ele; HashTable *ht_keys, *ht_weights=NULL; smart_string cmdstr = {0}; - int argc = 2, agg_op_len=0, keys_count; // Parse args if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a!s", &key, @@ -645,7 +652,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Start building our command redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); @@ -848,9 +855,9 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *min, *max; - int key_len, min_len, max_len, key_free; + strlen_t key_len, min_len, max_len; + int key_free, argc = ZEND_NUM_ARGS(); long offset, count; - int argc = ZEND_NUM_ARGS(); /* We need either 3 or 5 arguments for this to be valid */ if(argc != 3 && argc != 5) { @@ -879,7 +886,7 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Prefix key */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); /* Construct command */ if(argc == 3) { @@ -905,7 +912,8 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *min, *max; - int key_len, min_len, max_len, key_free; + strlen_t key_len, min_len, max_len; + int key_free; /* Parse args */ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, @@ -924,7 +932,7 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Prefix key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); /* Construct command */ *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, @@ -1005,7 +1013,8 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_arr, *z_val; HashTable *ht_arr; smart_string cmdstr = {0}; - int key_len, val_len, key_free, val_free, argc = 1; + int val_len, key_free, val_free, argc = 1; + strlen_t key_len; char *key, *val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, @@ -1021,7 +1030,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); /* Prefix if required and append the key name */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); CMD_SET_SLOT(slot, key, key_len); if (key_free) efree(key); @@ -1181,7 +1190,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key = NULL, *val = NULL, *exp_type = NULL, *set_type = NULL; int key_free, val_free; long expire = -1; - size_t key_len, val_len; + strlen_t key_len, val_len; // Make sure the function is being called correctly if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, @@ -1199,8 +1208,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Serialize and key prefix if required - val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); + val_free = redis_serialize(redis_sock, z_value, &val, (int *)&val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Check for an options array if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { @@ -1273,7 +1282,7 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key1, *key2; - int key1_len, key2_len; + strlen_t key1_len, key2_len; int key1_free, key2_free; short slot1, slot2; long timeout; @@ -1285,8 +1294,8 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Key prefixing - key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); - key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); + key1_free = redis_key_prefix(redis_sock, &key1, (int *)&key1_len); + key2_free = redis_key_prefix(redis_sock, &key2, (int *)&key2_len); // In cluster mode, verify the slots match if(slot) { @@ -1329,7 +1338,8 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, short *slot, void **ctx) { char *key; - int key_free, key_len; + int key_free; + strlen_t key_len; long val = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, @@ -1339,7 +1349,7 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, } /* Prefix the key if required */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); /* If our value is 1 we use INCR/DECR. For other values, treat the call as * an INCRBY or DECRBY call */ @@ -1388,7 +1398,8 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; - int key_len, mem_len, key_free; + strlen_t key_len, mem_len; + int key_free; long byval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key, &key_len, @@ -1398,7 +1409,7 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key if necissary - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, "HINCRBY", "ssd", key, key_len, mem, @@ -1418,7 +1429,8 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; - int key_len, mem_len, key_free; + strlen_t key_len, mem_len; + int key_free; double byval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssd", &key, &key_len, @@ -1428,7 +1440,7 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, "HINCRBYFLOAT", "ssf", key, key_len, @@ -1450,7 +1462,8 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key; zval *z_arr, *z_mems, *z_mem; - int i, count, valid=0, key_len, key_free; + int i, count, valid=0, key_free; + strlen_t key_len; HashTable *ht_arr; smart_string cmdstr = {0}; @@ -1470,7 +1483,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Allocate memory for mems+1 so we can have a sentinel z_mems = ecalloc(count + 1, sizeof(zval)); @@ -1532,7 +1545,8 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - int key_len, key_free, count; + int key_free, count; + strlen_t key_len; ulong idx; zval *z_arr; HashTable *ht_vals; @@ -1553,7 +1567,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Grab our array as a HashTable ht_vals = Z_ARRVAL_P(z_arr); @@ -1609,7 +1623,7 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int argc, key_free; long bit, start, end; - size_t key_len; + strlen_t key_len; argc = ZEND_NUM_ARGS(); if(zend_parse_parameters(argc TSRMLS_CC, "sl|ll", &key, &key_len, &bit, @@ -1624,7 +1638,7 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command based on arg count if(argc == 2) { @@ -1716,7 +1730,8 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - int key_len, key_free; + int key_free; + strlen_t key_len; long start = 0, end = -1; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &key, &key_len, @@ -1726,7 +1741,7 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, construct command - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); *cmd_len = redis_cmd_format_static(cmd, "BITCOUNT", "sdd", key, key_len, (int)start, (int)end); @@ -1749,8 +1764,8 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_arr; smart_string cmdstr = {0}; char *mem, *key; - int key_len, key_free; - int mem_len, mem_free, argc=1; + int key_free, mem_len, mem_free, argc=1; + strlen_t key_len; // Parse arguments if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, @@ -1769,7 +1784,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, set initial hash slot - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); if(slot) *slot = cluster_hash_key(key, key_len); // Start command construction @@ -1967,7 +1982,7 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *pw; - int pw_len; + strlen_t pw_len; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pw, &pw_len) ==FAILURE) @@ -1991,7 +2006,8 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - int key_len, key_free; + strlen_t key_len; + int key_free; long offset; zend_bool val; @@ -2008,7 +2024,7 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); *cmd_len = redis_cmd_format_static(cmd, "SETBIT", "sld", key, key_len, offset, (int)val); @@ -2024,7 +2040,7 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *pivot, *pos, *val; - int key_len, pivot_len, pos_len, val_len; + strlen_t key_len, pivot_len, pos_len, val_len; int key_free, pivot_free, val_free; zval *z_val, *z_pivot; @@ -2042,9 +2058,9 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, serialize value and position - key_free = redis_key_prefix(redis_sock, &key, &key_len); - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); + pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, (int *)&pivot_len TSRMLS_CC); // Construct command @@ -2068,7 +2084,8 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *val; - int key_len, val_len, key_free, val_free; + strlen_t key_len, val_len; + int key_free, val_free; long count = 0; zval *z_val; @@ -2079,8 +2096,8 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, serialize value - key_free = redis_key_prefix(redis_sock, &key, &key_len); - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); // Construct command *cmd_len = redis_cmd_format_static(cmd, "LREM", "sds", key, key_len, count, @@ -2101,7 +2118,7 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *src, *dst, *val; - int src_len, dst_len, val_len; + strlen_t src_len, dst_len, val_len; int val_free, src_free, dst_free; zval *z_val; @@ -2111,9 +2128,9 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - src_free = redis_key_prefix(redis_sock, &src, &src_len); - dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); + val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); + src_free = redis_key_prefix(redis_sock, &src, (int *)&src_len); + dst_free = redis_key_prefix(redis_sock, &dst, (int *)&dst_len); // Protect against a CROSSSLOT error if(slot) { @@ -2148,7 +2165,7 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { char *key, *mem, *val; - int key_len, mem_len, val_len; + strlen_t key_len, mem_len, val_len; int val_free, key_free; zval *z_val; @@ -2159,8 +2176,8 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix/serialize - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); + val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, mem, @@ -2199,7 +2216,8 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *have_count) { char *key; - int key_len, key_free; + strlen_t key_len; + int key_free; long count; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, @@ -2209,7 +2227,7 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key if requested - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Set our have count flag *have_count = ZEND_NUM_ARGS() == 2; @@ -2237,7 +2255,7 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; - int key_len, mem_len; + strlen_t key_len, mem_len; int key_free, mem_free; double incrby; zval *z_val; @@ -2249,8 +2267,8 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, serialize - key_free = redis_key_prefix(redis_sock, &key, &key_len); - mem_free = redis_serialize(redis_sock, z_val, &mem, &mem_len TSRMLS_CC); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + mem_free = redis_serialize(redis_sock, z_val, &mem, (int *)&mem_len TSRMLS_CC); *cmd_len = redis_cmd_format_static(cmd, "ZINCRBY", "sfs", key, key_len, incrby, mem, mem_len); @@ -2273,7 +2291,8 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; HashTable *ht_opts; smart_string cmdstr = {0}; - int key_len, key_free; + strlen_t key_len; + int key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &key, &key_len, &z_opts)==FAILURE) @@ -2285,7 +2304,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *using_store = 0; // Handle key prefixing - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // If we don't have an options array, the command is quite simple if(!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) { @@ -2625,8 +2644,8 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *slot, void **ctx) { char *key, *subcmd; - int key_free, subcmd_len; - size_t key_len; + strlen_t key_len, subcmd_len; + int key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &subcmd, &subcmd_len, &key, &key_len)==FAILURE) @@ -2635,7 +2654,7 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Format our command *cmd_len = redis_cmd_format_static(cmd, "OBJECT", "ss", subcmd, subcmd_len, @@ -2668,7 +2687,7 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *source, *dest, *unit = NULL; - size_t keylen, sourcelen, destlen, unitlen; + strlen_t keylen, sourcelen, destlen, unitlen; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|s", &key, &keylen, &source, &sourcelen, &dest, &destlen, &unit, @@ -2764,8 +2783,8 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *unit; - int keylen, keyfree, unitlen; - int withcoord = 0, withdist = 0, withhash = 0; + strlen_t keylen, unitlen; + int keyfree, withcoord = 0, withdist = 0, withhash = 0; long count = 0; geoSortType sort = SORT_NONE; double lng, lat, radius; @@ -2795,7 +2814,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUS"); /* Apply any key prefix */ - keyfree = redis_key_prefix(redis_sock, &key, &keylen); + keyfree = redis_key_prefix(redis_sock, &key, (int *)&keylen); /* Append required arguments */ redis_cmd_append_sstr(&cmdstr, key, keylen); @@ -2823,8 +2842,8 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem, *unit; - int keylen, keyfree, memlen, unitlen, argc; - int withcoord = 0, withdist = 0, withhash = 0; + strlen_t keylen, memlen, unitlen; + int keyfree, argc, withcoord = 0, withdist = 0, withhash = 0; long count = 0; double radius; geoSortType sort = SORT_NONE; @@ -2849,7 +2868,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUSBYMEMBER"); /* Prefix our key if we're prefixing */ - keyfree = redis_key_prefix(redis_sock, &key, &keylen); + keyfree = redis_key_prefix(redis_sock, &key, (int *)&keylen); /* Append required arguments */ redis_cmd_append_sstr(&cmdstr, key, keylen); @@ -2956,7 +2975,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *kw=NULL; zval *z_arg; - int kw_len; + strlen_t kw_len; /* Parse our args */ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sz", &kw, &kw_len, @@ -3062,8 +3081,9 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, { long option, val_long; char *val_str; - int val_len, test_val; struct timeval read_tv; + strlen_t val_len; + int test_val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &option, &val_str, &val_len) == FAILURE) @@ -3094,9 +3114,8 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, redis_sock->prefix = NULL; redis_sock->prefix_len = 0; } else { + redis_sock->prefix = estrndup(val_str, val_len); redis_sock->prefix_len = val_len; - redis_sock->prefix = ecalloc(1+val_len, 1); - memcpy(redis_sock->prefix, val_str, val_len); } RETURN_TRUE; case REDIS_OPT_READ_TIMEOUT: @@ -3137,7 +3156,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *key; - size_t key_len; + strlen_t key_len; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) ==FAILURE) @@ -3146,7 +3165,7 @@ void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { } if(redis_sock->prefix != NULL && redis_sock->prefix_len>0) { - redis_key_prefix(redis_sock, &key, &key_len); + redis_key_prefix(redis_sock, &key, (int *)&key_len); RETVAL_STRINGL(key, key_len); efree(key); } else { @@ -3175,7 +3194,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_class_entry *ex) { char *value; - int value_len; + strlen_t value_len; // Parse our arguments if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) From f5ddf53b79f3bf61b66ce800a119fcf61c3762c9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 14 Nov 2016 14:03:26 +0200 Subject: [PATCH 0627/1986] php7 compatibility The 'l' specifier now expects a zend_long instead of a long for zend_parse_parameters. --- cluster_library.c | 11 ++++------- redis.c | 20 ++++++++++---------- redis_array.c | 10 +++++----- redis_cluster.c | 8 +++++--- redis_commands.c | 33 +++++++++++++++++---------------- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 2b3a31b74e..9901754528 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -862,7 +862,7 @@ PHP_REDIS_API void cluster_free(redisCluster *c) { /* Takes our input hash table and returns a straigt C array with elements, * which have been randomized. The return value needs to be freed. */ static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) { - zval **z_seeds; + zval **z_seeds, *z_ele; int *map, i, count, index=0; /* How many */ @@ -877,12 +877,9 @@ static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) { fyshuffle(map, count); /* Iterate over our source array and use our map to create a random list */ - for (zend_hash_internal_pointer_reset(seeds); - zend_hash_has_more_elements(seeds) == SUCCESS; - zend_hash_move_forward(seeds)) - { - z_seeds[map[index++]] = zend_hash_get_current_data(seeds); - } + ZEND_HASH_FOREACH_VAL(seeds, z_ele) { + z_seeds[map[index++]] = z_ele; + } ZEND_HASH_FOREACH_END(); efree(map); diff --git a/redis.c b/redis.c index f42f0bb386..c9388e2324 100644 --- a/redis.c +++ b/redis.c @@ -1423,7 +1423,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, RedisSock *redis_sock; char *key = NULL, *pattern = NULL, *get = NULL, *store = NULL, *cmd; int cmd_len, key_free; - long sort_start = -1, sort_count = -1; + zend_long sort_start = -1, sort_count = -1; strlen_t key_len, pattern_len, get_len, store_len; int cmd_elements; @@ -1773,7 +1773,7 @@ PHP_METHOD(Redis, select) { char *cmd; int cmd_len; - long dbNumber; + zend_long dbNumber; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, redis_ce, &dbNumber) == FAILURE) { @@ -2199,7 +2199,7 @@ PHP_METHOD(Redis, multi) int response_len, cmd_len; char * response; zval *object; - long multi_value = MULTI; + zend_long multi_value = MULTI; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &object, redis_ce, &multi_value) @@ -2599,7 +2599,7 @@ PHP_METHOD(Redis, slaveof) RedisSock *redis_sock; char *cmd = "", *host = NULL; strlen_t host_len; - long port = 6379; + zend_long port = 6379; int cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), @@ -2754,7 +2754,7 @@ PHP_METHOD(Redis, slowlog) { char *arg, *cmd; int cmd_len; strlen_t arg_len; - long option; + zend_long option; enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; // Make sure we can get parameters @@ -2808,7 +2808,7 @@ PHP_METHOD(Redis, slowlog) { PHP_METHOD(Redis, wait) { zval *object; RedisSock *redis_sock; - long num_slaves, timeout; + zend_long num_slaves, timeout; char *cmd; int cmd_len; @@ -3086,7 +3086,7 @@ PHP_METHOD(Redis, evalsha) char *cmd, *sha; int cmd_len; strlen_t sha_len; - long keys_count = 0; + zend_long keys_count = 0; RedisSock *redis_sock; if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), @@ -3124,7 +3124,7 @@ PHP_METHOD(Redis, eval) char *script, *cmd = ""; int cmd_len; strlen_t script_len; - long keys_count = 0; + zend_long keys_count = 0; // Attempt to parse parameters if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), @@ -3283,7 +3283,7 @@ PHP_METHOD(Redis, migrate) { int cmd_len, key_free; strlen_t host_len, key_len; zend_bool copy=0, replace=0; - long port, dest_db, timeout; + zend_long port, dest_db, timeout; // Parse arguments if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), @@ -3723,7 +3723,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { char *pattern=NULL, *cmd, *key=NULL; int cmd_len, num_elements, key_free=0; strlen_t key_len = 0, pattern_len = 0; - long count=0, iter; + zend_long count=0, iter; /* Different prototype depending on if this is a key based scan */ if(type != TYPE_SCAN) { diff --git a/redis_array.c b/redis_array.c index a8bf7419d7..be2278adc2 100644 --- a/redis_array.c +++ b/redis_array.c @@ -394,7 +394,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i } /* pass call through */ - ZVAL_STRING(&z_fun, cmd); /* method name */ + ZVAL_STRINGL(&z_fun, cmd, cmd_len); /* method name */ z_callargs = ecalloc(argc + 1, sizeof(zval)); /* copy args to array */ @@ -714,7 +714,7 @@ PHP_METHOD(RedisArray, getOption) zval *object, z_fun, z_args[1]; int i; RedisArray *ra; - long opt; + zend_long opt; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, redis_array_ce, &opt) == FAILURE) { @@ -751,7 +751,7 @@ PHP_METHOD(RedisArray, setOption) zval *object, z_fun, z_args[2]; int i; RedisArray *ra; - long opt; + zend_long opt; char *val_str; strlen_t val_len; @@ -792,7 +792,7 @@ PHP_METHOD(RedisArray, select) zval *object, z_fun, z_args[1]; int i; RedisArray *ra; - long opt; + zend_long opt; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, redis_array_ce, &opt) == FAILURE) { @@ -1292,7 +1292,7 @@ PHP_METHOD(RedisArray, multi) zval *z_redis; char *host; strlen_t host_len; - long multi_value = MULTI; + zend_long multi_value = MULTI; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", &object, redis_array_ce, &host, &host_len, &multi_value) == FAILURE) { diff --git a/redis_cluster.c b/redis_cluster.c index a288deeb8c..dfb6c12906 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1902,7 +1902,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int key_free, args_count=0, key_len; zval *z_arr=NULL, *z_ele; HashTable *ht_arr; - long num_keys = 0; + zend_long num_keys = 0; short slot = 0; smart_string cmdstr = {0}; strlen_t lua_len; @@ -2470,7 +2470,8 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, short slot; zval *z_it; HashTable *hash; - long it, num_ele, count=0; + long it, num_ele; + zend_long count = 0; // Can't be in MULTI mode if(!CLUSTER_IS_ATOMIC(c)) { @@ -2561,7 +2562,8 @@ PHP_METHOD(RedisCluster, scan) { int cmd_len; short slot; zval *z_it, *z_node; - long it, num_ele, count=0; + long it, num_ele; + zend_long count = 0; /* Treat as read-only */ c->readonly = CLUSTER_IS_ATOMIC(c); diff --git a/redis_commands.c b/redis_commands.c index a3f2b9ff39..d163f0bab3 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -111,7 +111,7 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key = NULL, *val=NULL; int val_len, val_free, key_free; strlen_t key_len; - long expire; + zend_long expire; zval *z_val; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &key, &key_len, @@ -145,7 +145,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *val; strlen_t key_len, val_len; int key_free; - long lval; + zend_long lval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &key, &key_len, &lval, &val, &val_len)==FAILURE) @@ -316,7 +316,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int key_free; strlen_t key_len; - long lval; + zend_long lval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &key, &key_len, &lval)==FAILURE) @@ -351,7 +351,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int key_free; strlen_t key_len; - long val1, val2; + zend_long val1, val2; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &key, &key_len, &val1, &val2)==FAILURE) @@ -479,7 +479,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int key_free; strlen_t key_len; - long start, end; + zend_long start, end; zend_bool ws=0; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|b", &key, &key_len, @@ -857,7 +857,7 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *min, *max; strlen_t key_len, min_len, max_len; int key_free, argc = ZEND_NUM_ARGS(); - long offset, count; + zend_long offset, count; /* We need either 3 or 5 arguments for this to be valid */ if(argc != 3 && argc != 5) { @@ -1285,7 +1285,7 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key1_len, key2_len; int key1_free, key2_free; short slot1, slot2; - long timeout; + zend_long timeout; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key1, &key1_len, &key2, &key2_len, &timeout)==FAILURE) @@ -1340,7 +1340,7 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, char *key; int key_free; strlen_t key_len; - long val = 1; + zend_long val = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, &val)==FAILURE) @@ -1400,7 +1400,7 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *mem; strlen_t key_len, mem_len; int key_free; - long byval; + zend_long byval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key, &key_len, &mem, &mem_len, &byval)==FAILURE) @@ -1622,7 +1622,7 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key; int argc, key_free; - long bit, start, end; + zend_long bit, start, end; strlen_t key_len; argc = ZEND_NUM_ARGS(); @@ -1732,7 +1732,7 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int key_free; strlen_t key_len; - long start = 0, end = -1; + zend_long start = 0, end = -1; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &key, &key_len, &start, &end)==FAILURE) @@ -2008,7 +2008,7 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; strlen_t key_len; int key_free; - long offset; + zend_long offset; zend_bool val; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slb", &key, &key_len, @@ -2086,7 +2086,7 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *val; strlen_t key_len, val_len; int key_free, val_free; - long count = 0; + zend_long count = 0; zval *z_val; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &key, &key_len, @@ -2218,7 +2218,7 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; strlen_t key_len; int key_free; - long count; + zend_long count; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, &count)==FAILURE) @@ -3048,7 +3048,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redisCluster *c) { - long option; + zend_long option; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &option) == FAILURE) @@ -3079,7 +3079,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redisCluster *c) { - long option, val_long; + long val_long; + zend_long option; char *val_str; struct timeval read_tv; strlen_t val_len; From eab93272dbbeaff94a1ed7119c6b46a7c1d87f36 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 15 Nov 2016 17:26:48 +0200 Subject: [PATCH 0628/1986] refactoring --- cluster_library.c | 19 +++++++++++-------- library.c | 17 +++++++---------- redis.c | 2 +- redis_array.c | 26 ++++++++++---------------- 4 files changed, 29 insertions(+), 35 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 9901754528..0759cd6575 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2150,8 +2150,7 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, // If this is the tail of our multi command, we can set our returns if(mctx->last) { if(CLUSTER_IS_ATOMIC(c)) { - *return_value = *(mctx->z_multi); - efree(mctx->z_multi); + RETVAL_ZVAL(mctx->z_multi, 0, 1); } else { add_next_index_zval(&c->multi_resp, mctx->z_multi); } @@ -2186,8 +2185,7 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluste // Set return value if it's our last response if(mctx->last) { if(CLUSTER_IS_ATOMIC(c)) { - *return_value = *(mctx->z_multi); - efree(mctx->z_multi); + RETVAL_ZVAL(mctx->z_multi, 0, 1); } else { add_next_index_zval(&c->multi_resp, mctx->z_multi); } @@ -2237,18 +2235,23 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster if(c->reply_type != TYPE_LINE) { php_error_docref(0 TSRMLS_CC, E_ERROR, "Invalid reply type returned for MSET command"); - ZVAL_FALSE(return_value); + zval_dtor(mctx->z_multi); efree(mctx->z_multi); efree(mctx); - return; + RETURN_FALSE; } // Set our return if it's the last call if(mctx->last) { +#if (PHP_MAJOR_VERSION < 7) + zend_bool bval = Z_LVAL_P(mctx->z_multi); +#else + zend_bool bval = (Z_TYPE_P(mctx->z_multi) == IS_TRUE); +#endif if(CLUSTER_IS_ATOMIC(c)) { - ZVAL_BOOL(return_value, Z_LVAL_P(mctx->z_multi)); + ZVAL_BOOL(return_value, bval); } else { - add_next_index_bool(&c->multi_resp, Z_LVAL_P(mctx->z_multi)); + add_next_index_bool(&c->multi_resp, bval); } efree(mctx->z_multi); } diff --git a/library.c b/library.c index 262e5ee741..e82106f1f1 100644 --- a/library.c +++ b/library.c @@ -1003,11 +1003,9 @@ redis_parse_client_list_response(char *response, zval *z_ret) array_init(z_ret); /* Allocate memory for one user (there should be at least one, namely us!) */ + zval zv, *z_sub_result = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_sub_result; ALLOC_INIT_ZVAL(z_sub_result); -#else - zval zv, *z_sub_result = &zv; #endif array_init(z_sub_result); @@ -1244,7 +1242,7 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, } else if (decode == SCORE_DECODE_DOUBLE) { add_assoc_double_ex(z_ret, hkey, hkey_len, atof(hval)); } else { - zval zv, *z = &zv; + zval zv0, *z = &zv0; #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); #endif @@ -1702,12 +1700,8 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RETURN_FALSE; } - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { - efree(response); - RETURN_TRUE; - } + RETVAL_BOOL(response_len == 3 && strncmp(response, "+OK", 3) == 0); efree(response); - RETURN_FALSE; } /** @@ -1915,7 +1909,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc for(i = 0; i < numElems; ++i) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval zv, *z = &zv; + zval zv0, *z = &zv0; #if (PHP_MAJOR_VERSION < 7) z = NULL; #endif @@ -2386,6 +2380,9 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } break; default: +#if (PHP_MAJOR_VERSION < 7) + efree(z_ret); +#endif // Protocol error zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); diff --git a/redis.c b/redis.c index c9388e2324..0c88d15782 100644 --- a/redis.c +++ b/redis.c @@ -3257,7 +3257,7 @@ PHP_METHOD(Redis, script) { /* {{{ proto DUMP key */ PHP_METHOD(Redis, dump) { - REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_ping_response); + REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_string_response); } /* }}} */ diff --git a/redis_array.c b/redis_array.c index be2278adc2..4e2c06c9b2 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1085,6 +1085,8 @@ PHP_METHOD(RedisArray, mset) } ZEND_HASH_FOREACH_END(); + /* prepare call */ + ZVAL_STRINGL(&z_fun, "MSET", 4); /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ int found = 0; @@ -1093,11 +1095,9 @@ PHP_METHOD(RedisArray, mset) array_init(&z_argarray); for(i = 0; i < argc; ++i) { if(pos[i] != n) continue; + zval zv, *z_tmp = &zv; #if (PHP_MAJOR_VERSION < 7) - zval *z_tmp; MAKE_STD_ZVAL(z_tmp); -#else - zval zv, *z_tmp = &zv; #endif ZVAL_ZVAL(z_tmp, argv[i], 1, 0); add_assoc_zval_ex(&z_argarray, keys[i], key_lens[i], z_tmp); @@ -1110,25 +1110,21 @@ PHP_METHOD(RedisArray, mset) continue; /* don't run empty MSETs */ } - /* prepare call */ - ZVAL_STRINGL(&z_fun, "MSET", 4); if(ra->index) { /* add MULTI */ ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); } /* call */ call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + zval_dtor(&z_ret); if(ra->index) { ra_index_keys(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SADD to add keys to node index */ ra_index_exec(&ra->redis[n], NULL, 0 TSRMLS_CC); /* run EXEC */ } - - zval_dtor(&z_fun); - zval_dtor(&z_ret); - zval_dtor(&z_argarray); } + zval_dtor(&z_fun); /* Free any keys that we needed to allocate memory for, because they weren't strings */ for(i = 0; i < argc; i++) { @@ -1162,7 +1158,9 @@ PHP_METHOD(RedisArray, del) /* get all args in z_args */ z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || + redis_array_get(getThis(), &ra TSRMLS_CC) < 0 + ) { efree(z_args); RETURN_FALSE; } @@ -1188,12 +1186,6 @@ PHP_METHOD(RedisArray, del) free_zkeys = 1; } - - if (redis_array_get(getThis(), &ra TSRMLS_CC) < 0) { - efree(z_args); - RETURN_FALSE; - } - /* prepare call */ ZVAL_STRINGL(&z_fun, "DEL", 3); @@ -1211,6 +1203,8 @@ PHP_METHOD(RedisArray, del) ZEND_HASH_FOREACH_VAL(h_keys, data) { if (Z_TYPE_P(data) != IS_STRING) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "DEL: all keys must be string."); + if (free_zkeys) zval_dtor(&z_keys); + efree(z_args); efree(argv); efree(pos); RETURN_FALSE; From b2d598c8517920f43fe0e31f5bc0d5a077ce6a1a Mon Sep 17 00:00:00 2001 From: iyesin Date: Tue, 15 Nov 2016 20:16:24 +0000 Subject: [PATCH 0629/1986] Update redis_commands.c --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index d163f0bab3..dfeeed9828 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1412,7 +1412,7 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); // Construct command - *cmd_len = redis_cmd_format_static(cmd, "HINCRBY", "ssd", key, key_len, mem, + *cmd_len = redis_cmd_format_static(cmd, "HINCRBY", "ssl", key, key_len, mem, mem_len, byval); // Set slot CMD_SET_SLOT(slot,key,key_len); From 8b312f8e68508d770de079974919546ce5954afd Mon Sep 17 00:00:00 2001 From: iyesin Date: Tue, 15 Nov 2016 20:21:59 +0000 Subject: [PATCH 0630/1986] Make test for correct long reply --- tests/RedisTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index e8833dc9f1..9fc37adcd8 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2290,6 +2290,8 @@ public function testHashes() { $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); $this->assertTrue("2" === $this->redis->hGet('h', 'x')); + $this->assertTrue( 1000000000002 === $this->redis->hIncrBy('h', 'x', 1000000000000)); + $this->assertTrue( "1000000000002" === $this->redis->hGet('h', 'x')); $this->redis->hSet('h', 'y', 'not-a-number'); $this->assertTrue(FALSE === $this->redis->hIncrBy('h', 'y', 1)); @@ -2300,6 +2302,7 @@ public function testHashes() { $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)); From aaffa7c2de7e1a4eb609c44314d9fc085c836eaf Mon Sep 17 00:00:00 2001 From: iyesin Date: Wed, 16 Nov 2016 10:42:55 +0000 Subject: [PATCH 0631/1986] Update RedisTest.php --- tests/RedisTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 9fc37adcd8..75fb7f4c96 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2290,8 +2290,8 @@ public function testHashes() { $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); $this->assertTrue("2" === $this->redis->hGet('h', 'x')); - $this->assertTrue( 1000000000002 === $this->redis->hIncrBy('h', 'x', 1000000000000)); - $this->assertTrue( "1000000000002" === $this->redis->hGet('h', 'x')); + $this->assertTrue(1000000000002 === $this->redis->hIncrBy('h', 'x', 1000000000000)); + $this->assertTrue("1000000000002" === $this->redis->hGet('h', 'x')); $this->redis->hSet('h', 'y', 'not-a-number'); $this->assertTrue(FALSE === $this->redis->hIncrBy('h', 'y', 1)); From 14c19fa3a005ecbd819a35b7792301b10d9e6bda Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 16 Nov 2016 15:10:50 +0200 Subject: [PATCH 0632/1986] refactoring --- redis_array.c | 56 +++++++++++++++++++++++++--------------------- redis_array_impl.c | 32 +++++++++++++++----------- redis_commands.c | 21 ++++++++--------- 3 files changed, 59 insertions(+), 50 deletions(-) diff --git a/redis_array.c b/redis_array.c index 4e2c06c9b2..0a6e0548b6 100644 --- a/redis_array.c +++ b/redis_array.c @@ -406,8 +406,9 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* multi/exec */ if(ra->z_multi_exec) { - call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); + call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, &z_tmp, argc, z_callargs); zval_dtor(&z_fun); + zval_dtor(&z_tmp); efree(z_callargs); RETURN_ZVAL(getThis(), 1, 0); } @@ -824,8 +825,8 @@ PHP_METHOD(RedisArray, select) zval_dtor(&z_fun); } #if (PHP_MAJOR_VERSION < 7) -#define HANDLE_MULTI_EXEC(cmd) do {\ - if (redis_array_get(getThis(), &ra TSRMLS_CC) >= 0 && ra->z_multi_exec) {\ +#define HANDLE_MULTI_EXEC(ra, cmd) do { \ + if (ra && ra->z_multi_exec) { \ int i, num_varargs;\ zval ***varargs = NULL;\ zval z_arg_array;\ @@ -853,8 +854,8 @@ PHP_METHOD(RedisArray, select) }\ }while(0) #else -#define HANDLE_MULTI_EXEC(cmd) do { \ - if (redis_array_get(getThis(), &ra TSRMLS_CC) >= 0 && ra->z_multi_exec) { \ +#define HANDLE_MULTI_EXEC(ra, cmd) do { \ + if (ra && ra->z_multi_exec) { \ int i, num_varargs; \ zval *varargs = NULL, z_arg_array; \ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O*", \ @@ -886,18 +887,18 @@ PHP_METHOD(RedisArray, mget) HashTable *h_keys; zval **argv; + if (redis_array_get(getThis(), &ra TSRMLS_CC) < 0) { + RETURN_FALSE; + } + /* Multi/exec support */ - HANDLE_MULTI_EXEC("MGET"); + HANDLE_MULTI_EXEC(ra, "MGET"); if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_array_ce, &z_keys) == FAILURE) { RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { - RETURN_FALSE; - } - /* prepare call */ ZVAL_STRINGL(&z_fun, "MGET", 4); @@ -978,6 +979,7 @@ PHP_METHOD(RedisArray, mget) zval_dtor(&z_ret); zval_dtor(&z_tmp_array); zval_dtor(&z_fun); + efree(argv); efree(pos); efree(argc_each); @@ -1039,18 +1041,18 @@ PHP_METHOD(RedisArray, mset) zend_string *zkey; ulong idx; + if (redis_array_get(getThis(), &ra TSRMLS_CC) < 0) { + RETURN_FALSE; + } + /* Multi/exec support */ - HANDLE_MULTI_EXEC("MSET"); + HANDLE_MULTI_EXEC(ra, "MSET"); if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_array_ce, &z_keys) == FAILURE) { RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { - RETURN_FALSE; - } - /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); argc = zend_hash_num_elements(h_keys); @@ -1089,6 +1091,9 @@ PHP_METHOD(RedisArray, mset) ZVAL_STRINGL(&z_fun, "MSET", 4); /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ + /* We don't even need to make a call to this node if no keys go there */ + if(!argc_each[n]) continue; + int found = 0; /* copy args */ @@ -1153,14 +1158,15 @@ PHP_METHOD(RedisArray, del) long total = 0; int free_zkeys = 0; + if (redis_array_get(getThis(), &ra TSRMLS_CC) < 0) { + RETURN_FALSE; + } /* Multi/exec support */ - HANDLE_MULTI_EXEC("DEL"); + HANDLE_MULTI_EXEC(ra, "DEL"); /* get all args in z_args */ z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || - redis_array_get(getThis(), &ra TSRMLS_CC) < 0 - ) { + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); RETURN_FALSE; } @@ -1219,6 +1225,8 @@ PHP_METHOD(RedisArray, del) /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ + /* We don't even need to make a call to this node if no keys go there */ + if(!argc_each[n]) continue; int found = 0; zval z_argarray; @@ -1253,16 +1261,14 @@ PHP_METHOD(RedisArray, del) call_user_function(&redis_ce->function_table, redis_inst, &z_fun, &z_ret, 1, &z_argarray); if(ra->index) { + zval_dtor(&z_ret); ra_index_del(&z_argarray, redis_inst TSRMLS_CC); /* use SREM to remove keys from node index */ - ra_index_exec(redis_inst, z_tmp, 0 TSRMLS_CC); /* run EXEC */ - total += Z_LVAL_P(z_tmp); /* increment total from multi/exec block */ - } else { - total += Z_LVAL(z_ret); /* increment total from single command */ + ra_index_exec(redis_inst, &z_ret, 0 TSRMLS_CC); /* run EXEC */ } - - zval_dtor(&z_ret); + total += Z_LVAL(z_ret); /* increment total */ zval_dtor(&z_argarray); + zval_dtor(&z_ret); } /* cleanup */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 5d20441492..9f6556461f 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -542,6 +542,7 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { ZVAL_LONG(&z_args[0], multi_value); call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); zval_dtor(&z_fun_multi); + zval_dtor(&z_ret); } static void @@ -566,9 +567,9 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { /* run cmd */ call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); - /* don't dtor z_ret, since we're returning z_redis */ zval_dtor(&z_args[0]); zval_dtor(&z_fun); + zval_dtor(&z_ret); efree(z_args); /* free container */ } @@ -628,6 +629,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { zval_dtor(&z_fun_sadd); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); + zval_dtor(&z_ret); } void @@ -650,8 +652,8 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { RETVAL_ZVAL(zp_tmp, 1, 0); } } - zval_dtor(&z_ret); } + zval_dtor(&z_ret); /* zval *zptr = &z_ret; */ /* php_var_dump(&zptr, 0 TSRMLS_CC); */ @@ -719,6 +721,7 @@ ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, con zval_dtor(&z_fun_smembers); zval_dtor(&z_arg[0]); if(Z_TYPE(z_ret) != IS_ARRAY) { /* failure */ + zval_dtor(&z_ret); return -1; /* TODO: log error. */ } h_keys = Z_ARRVAL(z_ret); @@ -760,15 +763,17 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long /* prepare args */ ZVAL_STRINGL(&z_arg[0], key, key_len); - ZVAL_STRINGL(&z_fun_type, "TYPE", 4); /* run TYPE */ + ZVAL_STRINGL(&z_fun_type, "TYPE", 4); call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); zval_dtor(&z_fun_type); + zval_dtor(&z_ret); - ZVAL_STRINGL(&z_fun_type, "TTL", 3); /* run TYPE */ + ZVAL_STRINGL(&z_fun_type, "TTL", 3); call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); zval_dtor(&z_fun_type); + zval_dtor(&z_ret); /* Get the result from the pipeline. */ ra_index_exec(z_from, &z_ret, 1 TSRMLS_CC); @@ -808,6 +813,7 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { zval_dtor(&z_fun_srem); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); + zval_dtor(&z_ret); } @@ -826,6 +832,7 @@ ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); zval_dtor(&z_fun_del); zval_dtor(&z_args[0]); + zval_dtor(&z_ret); /* remove key from index */ ra_remove_from_index(z_from, key, key_len TSRMLS_CC); @@ -850,6 +857,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); zval_dtor(&z_fun_expire); zval_dtor(&z_args[0]); + zval_dtor(&z_ret); } return 1; @@ -879,6 +887,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ + zval_dtor(&z_ret); return 0; } @@ -985,6 +994,7 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); zval_dtor(&z_fun_hmset); + zval_dtor(&z_ret_dest); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); @@ -1041,8 +1051,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, i = 1; ZEND_HASH_FOREACH_VAL(h_set_vals, z_data_p) { /* add set elements */ - z_sadd_args[i] = *z_data_p; - zval_copy_ctor(&z_sadd_args[i]); + ZVAL_ZVAL(&z_sadd_args[i], z_data_p, 1, 0); i++; } ZEND_HASH_FOREACH_END(); @@ -1051,9 +1060,6 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args); - /* Expire if needed */ - ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); - /* cleanup */ zval_dtor(&z_fun_sadd); for (i = 0; i < count; i++) { @@ -1064,6 +1070,9 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, /* Clean up our output return value */ zval_dtor(&z_ret); + /* Expire if needed */ + ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); + return 1; } @@ -1157,10 +1166,7 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z /* cleanup */ zval_dtor(&z_args[0]); -#if (PHP_MAJOR_VERSION < 7) - if(z_ret) - efree(z_ret); -#endif + zval_ptr_dtor(&z_ret); } static void diff --git a/redis_commands.c b/redis_commands.c index dfeeed9828..6b00555600 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -305,6 +305,9 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key1, key1_len, key2, key2_len); + if (key1_free) efree(key1); + if (key2_free) efree(key2); + return SUCCESS; } @@ -339,6 +342,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set slot if directed CMD_SET_SLOT(slot, key, key_len); + if (key_free) efree(key); // Success! return SUCCESS; } @@ -1101,10 +1105,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); if(single_array) { - for(zend_hash_internal_pointer_reset(ht_arr); - (z_ele = zend_hash_get_current_data(ht_arr)) != NULL; - zend_hash_move_forward(ht_arr)) - { + ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { convert_to_string(z_ele); key = Z_STRVAL_P(z_ele); key_len = Z_STRLEN_P(z_ele); @@ -1125,7 +1126,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Append this key, free it if we prefixed redis_cmd_append_sstr(&cmdstr, key, key_len); if(key_free) efree(key); - } + } ZEND_HASH_FOREACH_END(); if(has_timeout) { redis_cmd_append_sstr_long(&cmdstr, timeout); } @@ -1495,8 +1496,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, || Z_TYPE_P(z_mem) == IS_LONG ) { // Copy into our member array - z_mems[valid] = *z_mem; - zval_copy_ctor(&z_mems[valid]); + ZVAL_ZVAL(&z_mems[valid], z_mem, 1, 0); convert_to_string(&z_mems[valid]); // Increment the member count to actually send @@ -3021,13 +3021,10 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, 1 + arr_len, "COMMAND", sizeof("COMMAND")-1); redis_cmd_append_sstr(&cmdstr, "GETKEYS", sizeof("GETKEYS")-1); - for(zend_hash_internal_pointer_reset(ht_arr); - (z_ele = zend_hash_get_current_data(ht_arr)) != NULL; - zend_hash_move_forward(ht_arr)) - { + ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { convert_to_string(z_ele); redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); - } + } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; *cmd_len = cmdstr.len; From 0dec2285bbfb05cb9c3d14cfa838b3555d3b1a78 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 16 Nov 2016 22:45:35 +0200 Subject: [PATCH 0633/1986] redis_unserialize_handler refactoring --- redis_commands.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 6b00555600..ba5ebd7fa3 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3202,25 +3202,17 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, } // We only need to attempt unserialization if we have a serializer running - if(redis_sock->serializer != REDIS_SERIALIZER_NONE) { - zval zv, *z_ret = &zv; -#if (PHP_MAJOR_VERSION < 7) - z_ret = NULL; -#endif - if(redis_unserialize(redis_sock, value, value_len, &z_ret - TSRMLS_CC) == 0) - { - // Badly formed input, throw an execption - zend_throw_exception(ex, - "Invalid serialized data, or unserialization error", - 0 TSRMLS_CC); - RETURN_FALSE; - } - RETURN_ZVAL(z_ret, 0, 1); - } else { + if (redis_sock->serializer == REDIS_SERIALIZER_NONE) { // Just return the value that was passed to us RETURN_STRINGL(value, value_len); } + zval zv, *z_ret = &zv; + if (redis_unserialize(redis_sock, value, value_len, &z_ret TSRMLS_CC) == 0) { + // Badly formed input, throw an execption + zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0 TSRMLS_CC); + RETURN_FALSE; + } + RETURN_ZVAL(z_ret, 1, 0); } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ From 4857693868eceaf961553ffff09696314834fc85 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 17 Nov 2016 00:05:57 +0200 Subject: [PATCH 0634/1986] redis_unserialize --- cluster_library.c | 26 ++++++++++++++------------ library.c | 46 ++++++++++++++++------------------------------ library.h | 2 +- redis_commands.c | 2 +- 4 files changed, 32 insertions(+), 44 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0759cd6575..feb4f09ee5 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1519,17 +1519,16 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } if (CLUSTER_IS_ATOMIC(c)) { - if (redis_unserialize(c->flags, resp, c->reply_len, &return_value - TSRMLS_CC) == 0) - { + if (!redis_unserialize(c->flags, resp, c->reply_len, return_value TSRMLS_CC)) { CLUSTER_RETURN_STRING(c, resp, c->reply_len); } } else { zval zv, *z = &zv; + if (redis_unserialize(c->flags, resp, c->reply_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) - z = NULL; + MAKE_STD_ZVAL(z); + *z = zv; #endif - if (redis_unserialize(c->flags, resp, c->reply_len, &z TSRMLS_CC)) { add_next_index_zval(&c->multi_resp, z); } else { add_next_index_stringl(&c->multi_resp, resp, c->reply_len); @@ -2343,10 +2342,11 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, if (line != NULL) { zval zv, *z = &zv; + if (redis_unserialize(redis_sock, line, line_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) - z = NULL; + MAKE_STD_ZVAL(z); + *z = zv; #endif - if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_next_index_zval(z_result, z); } else { add_next_index_stringl(z_result, line, line_len); @@ -2387,10 +2387,11 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, } else { /* Attempt serialization */ zval zv, *z = &zv; + if (redis_unserialize(redis_sock, line, line_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) - z = NULL; + MAKE_STD_ZVAL(z); + *z = zv; #endif - if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval(z_result, key, z); } else { add_assoc_stringl_ex(z_result, key, key_len, line, line_len); @@ -2425,7 +2426,7 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, key_len = line_len; } else { zval zv, *z = &zv; - if (redis_unserialize(redis_sock,key,key_len, &z TSRMLS_CC)) { + if (redis_unserialize(redis_sock,key,key_len, z TSRMLS_CC)) { convert_to_string(z); add_assoc_double_ex(z_result, Z_STRVAL_P(z), Z_STRLEN_P(z), atof(line)); zval_dtor(z); @@ -2457,10 +2458,11 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, if(line != NULL) { zval zv, *z = &zv; + if (redis_unserialize(redis_sock, line, line_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) - z = NULL; + MAKE_STD_ZVAL(z); + *z = zv; #endif - if(redis_unserialize(redis_sock, line, line_len, &z TSRMLS_CC)==1) { add_assoc_zval_ex(z_result,Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), z); } else { diff --git a/library.c b/library.c index e82106f1f1..1b4ac3bcaf 100644 --- a/library.c +++ b/library.c @@ -1387,20 +1387,17 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } IF_MULTI_OR_PIPELINE() { zval zv, *z = &zv; + if (redis_unserialize(redis_sock, response, response_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) - z = NULL; + MAKE_STD_ZVAL(z); + *z = zv; #endif - if(redis_unserialize(redis_sock, response, response_len, - &z TSRMLS_CC) == 1) - { add_next_index_zval(z_tab, z); } else { add_next_index_stringl(z_tab, response, response_len); } } else { - if(redis_unserialize(redis_sock, response, response_len, - &return_value TSRMLS_CC) == 0) - { + if (!redis_unserialize(redis_sock, response, response_len, return_value TSRMLS_CC)) { RETVAL_STRINGL(response, response_len); } } @@ -1846,9 +1843,6 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, line = redis_sock_read(redis_sock, &len TSRMLS_CC); if (line != NULL) { zval zv, *z = &zv; -#if (PHP_MAJOR_VERSION < 7) - z = NULL; -#endif int unwrap; /* We will attempt unserialization, if we're unserializing everything, @@ -1858,7 +1852,11 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, (unserialize == UNSERIALIZE_KEYS && count % 2 == 0) || (unserialize == UNSERIALIZE_VALS && count % 2 != 0); - if (unwrap && redis_unserialize(redis_sock, line, len, &z TSRMLS_CC)) { + if (unwrap && redis_unserialize(redis_sock, line, len, z TSRMLS_CC)) { +#if (PHP_MAJOR_VERSION < 7) + MAKE_STD_ZVAL(z); + *z = zv; +#endif add_next_index_zval(z_tab, z); } else { add_next_index_stringl(z_tab, line, len); @@ -1910,10 +1908,11 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { zval zv0, *z = &zv0; + if (redis_unserialize(redis_sock, response, response_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) - z = NULL; + MAKE_STD_ZVAL(z); + *z = zv0; #endif - if(redis_unserialize(redis_sock, response, response_len, &z TSRMLS_CC) == 1) { add_assoc_zval_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), z); } else { add_assoc_stringl_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), response, response_len); @@ -2053,29 +2052,21 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len PHP_REDIS_API int redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, - zval **return_value TSRMLS_DC) + zval *z_ret TSRMLS_DC) { php_unserialize_data_t var_hash; - int ret, rv_free = 0; + int ret; switch(redis_sock->serializer) { - case REDIS_SERIALIZER_NONE: - return 0; - case REDIS_SERIALIZER_PHP: - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - rv_free = 1; - } #if ZEND_MODULE_API_NO >= 20100000 PHP_VAR_UNSERIALIZE_INIT(var_hash); #else memset(&var_hash, 0, sizeof(var_hash)); #endif - if(!php_var_unserialize(*return_value, (const unsigned char**)&val, + if (!php_var_unserialize(z_ret, (const unsigned char**)&val, (const unsigned char*)val + val_len, &var_hash)) { - if(rv_free==1) efree(*return_value); ret = 0; } else { ret = 1; @@ -2114,16 +2105,11 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, return 0; } - if(!*return_value) { - MAKE_STD_ZVAL(*return_value); - rv_free = 1; - } if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, - return_value TSRMLS_CC) == 0) + z_ret TSRMLS_CC) == 0) { return 1; } - if (rv_free==1) efree(*return_value); #endif return 0; } diff --git a/library.h b/library.h index 86f1266311..f5f06c8ad7 100644 --- a/library.h +++ b/library.h @@ -70,7 +70,7 @@ PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len); PHP_REDIS_API int -redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC); +redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); /* * Variant Read methods, mostly to implement eval diff --git a/redis_commands.c b/redis_commands.c index ba5ebd7fa3..3410b6144a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3207,7 +3207,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_STRINGL(value, value_len); } zval zv, *z_ret = &zv; - if (redis_unserialize(redis_sock, value, value_len, &z_ret TSRMLS_CC) == 0) { + if (!redis_unserialize(redis_sock, value, value_len, z_ret TSRMLS_CC)) { // Badly formed input, throw an execption zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0 TSRMLS_CC); RETURN_FALSE; From 00b4864701d834d7ea5014b95edb49dca755685c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 17 Nov 2016 11:00:03 +0200 Subject: [PATCH 0635/1986] memory leaks --- library.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index 1b4ac3bcaf..e01bf0edf7 100644 --- a/library.c +++ b/library.c @@ -2009,8 +2009,10 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len default: /* copy */ ZVAL_ZVAL(&z_copy, z, 1, 0); convert_to_string(&z_copy); - *val = Z_STRVAL(z_copy); + *val = estrndup(Z_STRVAL(z_copy), Z_STRLEN(z_copy)); *val_len = Z_STRLEN(z_copy); + zval_dtor(&z_copy); + return 1; } break; case REDIS_SERIALIZER_PHP: From 6f6c5d9c52bd64b470dc48213ea4ca860c5b891f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 17 Nov 2016 14:55:48 +0200 Subject: [PATCH 0636/1986] refactoring --- redis.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/redis.c b/redis.c index 0c88d15782..51a8383def 100644 --- a/redis.c +++ b/redis.c @@ -1008,6 +1008,7 @@ PHP_METHOD(Redis, getMultiple) int key_len, key_free; zval z_tmp; + ZVAL_NULL(&z_tmp); /* If the key isn't a string, turn it into one */ if (Z_TYPE_P(z_ele) == IS_STRING) { key = Z_STRVAL_P(z_ele); @@ -1015,7 +1016,6 @@ PHP_METHOD(Redis, getMultiple) } else { ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); convert_to_string(&z_tmp); - key = Z_STRVAL(z_tmp); key_len = Z_STRLEN(z_tmp); } @@ -1028,6 +1028,7 @@ PHP_METHOD(Redis, getMultiple) /* Free our key if it was prefixed */ if(key_free) efree(key); + zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); /* Kick off our command */ @@ -2891,14 +2892,13 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, int key_len, key_free; zval z_tmp; + ZVAL_NULL(&z_tmp); if (Z_TYPE_P(z_ele) == IS_STRING) { key = Z_STRVAL_P(z_ele); key_len = Z_STRLEN_P(z_ele); } else { - z_tmp = *z_ele; - zval_copy_ctor(&z_tmp); + ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); convert_to_string(&z_tmp); - key = Z_STRVAL(z_tmp); key_len = Z_STRLEN(z_tmp); } @@ -2911,6 +2911,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Free key if prefixed */ if(key_free) efree(key); + zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); /* Set return */ @@ -3036,15 +3037,14 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *key, *old_cmd; int key_len, key_free; + ZVAL_NULL(&z_tmp); if (Z_TYPE_P(elem) == IS_STRING) { key = Z_STRVAL_P(elem); key_len = Z_STRLEN_P(elem); } else { /* Convert it to a string */ - z_tmp = *elem; - zval_copy_ctor(&z_tmp); + ZVAL_ZVAL(&z_tmp, elem, 1, 0); convert_to_string(&z_tmp); - key = Z_STRVAL(z_tmp); key_len = Z_STRLEN(z_tmp); } @@ -3064,6 +3064,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, /* Free our key, old command if we need to */ if(key_free) efree(key); + zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); } } From 6d6deae573658048e33219c73cc07624f6dfdbb7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 17 Nov 2016 14:55:48 +0200 Subject: [PATCH 0637/1986] TravisCI: clang + refactoring --- .travis.yml | 11 +++++++++++ cluster_library.c | 9 ++------- redis_array.c | 25 ++++--------------------- redis_commands.c | 6 +----- 4 files changed, 18 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index 75c63e4dab..32d4df09f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,9 +8,20 @@ php: - 7.0 - 7.1 - nightly +env: CC=gcc matrix: + include: + - env: CC=clang + php: 7.0 + - env: CC=clang + php: 7.1 + - env: CC=clang + php: nightly allow_failures: - php: nightly +addons: + apt: + packages: clang before_install: phpize install: ./configure CFLAGS=-Wall --prefix=/usr && sudo make install before_script: diff --git a/cluster_library.c b/cluster_library.c index feb4f09ee5..6a70444c32 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2242,15 +2242,10 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // Set our return if it's the last call if(mctx->last) { -#if (PHP_MAJOR_VERSION < 7) - zend_bool bval = Z_LVAL_P(mctx->z_multi); -#else - zend_bool bval = (Z_TYPE_P(mctx->z_multi) == IS_TRUE); -#endif if(CLUSTER_IS_ATOMIC(c)) { - ZVAL_BOOL(return_value, bval); + ZVAL_BOOL(return_value, zval_is_true(mctx->z_multi)); } else { - add_next_index_bool(&c->multi_resp, bval); + add_next_index_bool(&c->multi_resp, zval_is_true(mctx->z_multi)); } efree(mctx->z_multi); } diff --git a/redis_array.c b/redis_array.c index 0a6e0548b6..98050e6d78 100644 --- a/redis_array.c +++ b/redis_array.c @@ -25,7 +25,6 @@ #include "ext/standard/info.h" #include "php_ini.h" #include "php_redis.h" -#include "redis_array.h" #include #include "library.h" @@ -255,29 +254,17 @@ PHP_METHOD(RedisArray, __construct) /* extract index option. */ if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL) { -#if (PHP_MAJOR_VERSION < 7) - b_index = (Z_TYPE_P(zpData) == IS_BOOL && Z_LVAL_P(zpData)); -#else - b_index = (Z_TYPE_P(zpData) == IS_TRUE); -#endif + b_index = zval_is_true(zpData); } /* extract autorehash option. */ if ((zpData = zend_hash_str_find(hOpts, "autorehash", sizeof("autorehash") - 1)) != NULL) { -#if (PHP_MAJOR_VERSION < 7) - b_autorehash = (Z_TYPE_P(zpData) == IS_BOOL && Z_LVAL_P(zpData)); -#else - b_autorehash = (Z_TYPE_P(zpData) == IS_TRUE); -#endif + b_autorehash = zval_is_true(zpData); } /* pconnect */ if ((zpData = zend_hash_str_find(hOpts, "pconnect", sizeof("pconnect") - 1)) != NULL) { -#if (PHP_MAJOR_VERSION < 7) - b_pconnect = (Z_TYPE_P(zpData) == IS_BOOL && Z_LVAL_P(zpData)); -#else - b_pconnect = (Z_TYPE_P(zpData) == IS_TRUE); -#endif + b_pconnect = zval_is_true(zpData); } /* extract retry_interval option. */ @@ -293,11 +280,7 @@ PHP_METHOD(RedisArray, __construct) /* extract lazy connect option. */ if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL) { -#if (PHP_MAJOR_VERSION < 7) - b_lazy_connect = (Z_TYPE_P(zpData) == IS_BOOL && Z_LVAL_P(zpData)); -#else - b_lazy_connect = (Z_TYPE_P(zpData) == IS_TRUE); -#endif + b_lazy_connect = zval_is_true(zpData); } /* extract connect_timeout option */ diff --git a/redis_commands.c b/redis_commands.c index 3410b6144a..ef3564f1f6 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2439,11 +2439,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // ALPHA if (((z_ele = zend_hash_str_find(ht_opts, "alpha", sizeof("alpha") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL) && -#if (PHP_MAJOR_VERSION < 7) - (Z_TYPE_P(z_ele) == IS_BOOL && Z_LVAL_P(z_ele)) -#else - (Z_TYPE_P(z_ele) == IS_TRUE) -#endif + zval_is_true(z_ele) ) { add_next_index_stringl(&z_argv, "ALPHA", sizeof("ALPHA") - 1); } From 3d7da1fdb6d1f8ee96385b0ff144c2c8829a32ba Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 20 Nov 2016 19:27:51 +0200 Subject: [PATCH 0638/1986] refactoring --- library.c | 22 ++++++++++------------ redis.c | 3 ++- redis_array.c | 18 +++++++++--------- redis_array_impl.c | 7 +++++++ redis_commands.c | 33 ++++++++++++++++----------------- 5 files changed, 44 insertions(+), 39 deletions(-) diff --git a/library.c b/library.c index e01bf0edf7..763c5b3a05 100644 --- a/library.c +++ b/library.c @@ -2058,7 +2058,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, { php_unserialize_data_t var_hash; - int ret; + int ret = 0; switch(redis_sock->serializer) { case REDIS_SERIALIZER_PHP: @@ -2067,10 +2067,9 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, #else memset(&var_hash, 0, sizeof(var_hash)); #endif - if (!php_var_unserialize(z_ret, (const unsigned char**)&val, - (const unsigned char*)val + val_len, &var_hash)) { - ret = 0; - } else { + if (php_var_unserialize(z_ret, (const unsigned char**)&val, + (const unsigned char*)val + val_len, &var_hash) + ) { ret = 1; } #if ZEND_MODULE_API_NO >= 20100000 @@ -2078,8 +2077,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, #else var_destroy(&var_hash); #endif - - return ret; + break; case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY @@ -2108,14 +2106,14 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, } if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, - z_ret TSRMLS_CC) == 0) - { - return 1; + z_ret TSRMLS_CC) == 0 + ) { + ret = 1; } #endif - return 0; + break; } - return 0; + return ret; } PHP_REDIS_API int diff --git a/redis.c b/redis.c index 51a8383def..0049484e7b 100644 --- a/redis.c +++ b/redis.c @@ -2390,12 +2390,13 @@ PHP_METHOD(Redis, exec) total += ri->request_size; } if (total) { - request = emalloc(total); + request = emalloc(total + 1); /* concatenate individual elements one by one in the target buffer */ for (ri = redis_sock->pipeline_head; ri; ri = ri->next) { memcpy(request + offset, ri->request_str, ri->request_size); offset += ri->request_size; } + request[total] = '\0'; if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0 || redis_sock_read_multibulk_pipeline_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0 diff --git a/redis_array.c b/redis_array.c index 98050e6d78..62a9e246d3 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1137,7 +1137,7 @@ PHP_METHOD(RedisArray, del) RedisArray *ra; int *pos, argc = ZEND_NUM_ARGS(), *argc_each; HashTable *h_keys; - zval *redis_inst, **argv; + zval **argv; long total = 0; int free_zkeys = 0; @@ -1175,9 +1175,6 @@ PHP_METHOD(RedisArray, del) free_zkeys = 1; } - /* prepare call */ - ZVAL_STRINGL(&z_fun, "DEL", 3); - /* init data structures */ h_keys = Z_ARRVAL(z_keys); argc = zend_hash_num_elements(h_keys); @@ -1196,6 +1193,7 @@ PHP_METHOD(RedisArray, del) efree(z_args); efree(argv); efree(pos); + efree(argc_each); RETURN_FALSE; } @@ -1206,6 +1204,9 @@ PHP_METHOD(RedisArray, del) argv[i++] = data; } ZEND_HASH_FOREACH_END(); + /* prepare call */ + ZVAL_STRINGL(&z_fun, "DEL", 3); + /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ /* We don't even need to make a call to this node if no keys go there */ @@ -1235,18 +1236,17 @@ PHP_METHOD(RedisArray, del) continue; } - redis_inst = &ra->redis[n]; if(ra->index) { /* add MULTI */ - ra_index_multi(redis_inst, MULTI TSRMLS_CC); + ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); } /* call */ - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, &z_ret, 1, &z_argarray); + call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); if(ra->index) { zval_dtor(&z_ret); - ra_index_del(&z_argarray, redis_inst TSRMLS_CC); /* use SREM to remove keys from node index */ - ra_index_exec(redis_inst, &z_ret, 0 TSRMLS_CC); /* run EXEC */ + ra_index_del(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SREM to remove keys from node index */ + ra_index_exec(&ra->redis[n], &z_ret, 0 TSRMLS_CC); /* run EXEC */ } total += Z_LVAL(z_ret); /* increment total */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 9f6556461f..2bdba84292 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -80,6 +80,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b #endif object_init_ex(&ra->redis[i], redis_ce); call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL); + zval_dtor(&z_ret); /* create socket */ redis_sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); @@ -341,6 +342,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_pconnect); zval_dtor(&z_params_connect_timeout); zval_dtor(&z_params_lazy_connect); + zval_dtor(&z_dist); + zval_dtor(&z_fun); return ra; } @@ -923,6 +926,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* cleanup */ zval_dtor(&z_fun_zadd); + zval_dtor(&z_ret_dest); zval_dtor(&z_ret); /* Free the array itself */ @@ -947,6 +951,8 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ /* TODO: report? */ + zval_dtor(&z_args[0]); + zval_dtor(&z_ret); return 0; } @@ -1038,6 +1044,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ + zval_dtor(&z_ret); return 0; } diff --git a/redis_commands.c b/redis_commands.c index ef3564f1f6..c2c438c350 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -545,11 +545,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Check for withscores and limit */ if (IS_WITHSCORES_ARG(zkey->val, zkey->len)) { -#if (PHP_MAJOR_VERSION < 7) - *withscores = (Z_TYPE_P(z_ele) == IS_BOOL && Z_LVAL_P(z_ele)); -#else - *withscores = (Z_TYPE_P(z_ele) == IS_TRUE); -#endif + *withscores = zval_is_true(z_ele); } else if (IS_LIMIT_ARG(zkey->val, zkey->len) && Z_TYPE_P(z_ele) == IS_ARRAY) { HashTable *htlimit = Z_ARRVAL_P(z_ele); zval *zoff, *zcnt; @@ -1234,7 +1230,11 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Expiry can't be set < 1 */ - if (expire < 1) return FAILURE; + if (expire < 1) { + if (key_free) efree(key); + if (val_free) efree(val); + return FAILURE; + } } else if (Z_TYPE_P(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_P(v))) { set_type = Z_STRVAL_P(v); } @@ -1242,7 +1242,11 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { /* Grab expiry and fail if it's < 1 */ expire = Z_LVAL_P(z_opts); - if (expire < 1) return FAILURE; + if (expire < 1) { + if (key_free) efree(key); + if (val_free) efree(val); + return FAILURE; + } } /* Now let's construct the command we want */ @@ -1323,6 +1327,8 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key1_len, key2, key2_len, timeout); } + if (key1_free) efree(key1); + if (key2_free) efree(key2); return SUCCESS; } @@ -1707,6 +1713,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, php_error_docref(NULL TSRMLS_CC, E_WARNING, "Warning, not all keys hash to the same slot!"); if(key_free) efree(key); + efree(z_args); return FAILURE; } *slot = kslot; @@ -2341,7 +2348,6 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(slot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "SORT BY option is not allowed in Redis Cluster"); - if(key_free) efree(key); zval_dtor(&z_argv); return FAILURE; } @@ -2372,7 +2378,6 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(cross_slot) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Error, SORT key and STORE key have different slots!"); - if(key_free) efree(key); zval_dtor(&z_argv); return FAILURE; } @@ -2394,7 +2399,6 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(slot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "GET option for SORT disabled in Redis Cluster"); - if(key_free) efree(key); zval_dtor(&z_argv); return FAILURE; } @@ -2429,7 +2433,6 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(added==0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array of GET values requested, but none are valid"); - if(key_free) efree(key); zval_dtor(&z_argv); return FAILURE; } @@ -2460,7 +2463,6 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "LIMIT options on SORT command must be longs or strings"); - if(key_free) efree(key); zval_dtor(&z_argv); return FAILURE; } @@ -2492,10 +2494,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, sizeof("SORT")-1); // Iterate through our arguments - for(zend_hash_internal_pointer_reset(ht_argv); - (z_ele = zend_hash_get_current_data(ht_argv)) != NULL; - zend_hash_move_forward(ht_argv)) - { + ZEND_HASH_FOREACH_VAL(ht_argv, z_ele) { // Args are strings or longs if (Z_TYPE_P(z_ele) == IS_STRING) { redis_cmd_append_sstr(&cmdstr,Z_STRVAL_P(z_ele), @@ -2503,7 +2502,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else { redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele)); } - } + } ZEND_HASH_FOREACH_END(); /* Clean up our arguments array. Note we don't have to free any prefixed * key as that we didn't duplicate the pointer if we prefixed */ From ad9f4ea4f6f8000d023f7b9badf8249bbb1c05f7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 21 Nov 2016 09:47:20 +0200 Subject: [PATCH 0639/1986] fix crash on FreeBSD and CentOS --- redis_array_impl.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 2bdba84292..25e1056710 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -1147,7 +1147,7 @@ ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { /* callback with the current progress, with hostname and count */ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, - const char *hostname, long count TSRMLS_DC) { + const char *hostname, long count, zval *z_ret TSRMLS_DC) { zval z_args[2]; @@ -1155,15 +1155,13 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z ZVAL_LONG(&z_args[1], count); #if (PHP_MAJOR_VERSION < 7) - zval *z_ret = NULL, - *z_host = &z_args[0], *z_count = &z_args[1], + zval *z_host = &z_args[0], *z_count = &z_args[1], **z_args_pp[2] = { &z_host, &z_count }; z_cb->params = z_args_pp; z_cb->retval_ptr_ptr = &z_ret; #else - zval z_ret; z_cb->params = z_args; - z_cb->retval = &z_ret; + z_cb->retval = z_ret; #endif z_cb->param_count = 2; z_cb->no_separation = 0; @@ -1173,7 +1171,6 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z /* cleanup */ zval_dtor(&z_args[0]); - zval_ptr_dtor(&z_ret); } static void @@ -1181,10 +1178,9 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { char **keys; - int *key_lens; long count, i; - int target_pos; - zval *z_target; + int *key_lens, target_pos; + zval *z_target, z_ret; /* list all keys */ if(b_index) { @@ -1195,7 +1191,8 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool /* callback */ if(z_cb && z_cb_cache) { - zval_rehash_callback(z_cb, z_cb_cache, hostname, count TSRMLS_CC); + zval_rehash_callback(z_cb, z_cb_cache, hostname, count, &z_ret TSRMLS_CC); + zval_dtor(&z_ret); } /* for each key, redistribute */ From 433caf478854efcd17a3bd39e3b8685e0a320572 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 21 Nov 2016 13:32:53 +0200 Subject: [PATCH 0640/1986] refactoring --- redis_array.c | 42 +++++++++--------------- redis_array_impl.c | 81 ++++++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 72 deletions(-) diff --git a/redis_array.c b/redis_array.c index 62a9e246d3..11bef6ca63 100644 --- a/redis_array.c +++ b/redis_array.c @@ -229,9 +229,6 @@ PHP_METHOD(RedisArray, __construct) ZVAL_NULL(&z_dist); /* extract options */ if(z_opts) { - zval *z_retry_interval_p; - zval *z_connect_timeout_p; - hOpts = Z_ARRVAL_P(z_opts); /* extract previous ring. */ @@ -268,15 +265,13 @@ PHP_METHOD(RedisArray, __construct) } /* extract retry_interval option. */ - if ((z_retry_interval_p = zend_hash_str_find(hOpts, "retry_interval", sizeof("retry_interval") - 1)) != NULL) { - if (Z_TYPE_P(z_retry_interval_p) == IS_LONG || Z_TYPE_P(z_retry_interval_p) == IS_STRING) { - if (Z_TYPE_P(z_retry_interval_p) == IS_LONG) { - l_retry_interval = Z_LVAL_P(z_retry_interval_p); - } else { - l_retry_interval = atol(Z_STRVAL_P(z_retry_interval_p)); - } - } - } + if ((zpData = zend_hash_str_find(hOpts, "retry_interval", sizeof("retry_interval") - 1)) != NULL) { + if (Z_TYPE_P(zpData) == IS_LONG) { + l_retry_interval = Z_LVAL_P(zpData); + } else if (Z_TYPE_P(zpData) == IS_STRING) { + l_retry_interval = atol(Z_STRVAL_P(zpData)); + } + } /* extract lazy connect option. */ if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL) { @@ -284,20 +279,15 @@ PHP_METHOD(RedisArray, __construct) } /* extract connect_timeout option */ - if ((z_connect_timeout_p = zend_hash_str_find(hOpts, "connect_timeout", sizeof("connect_timeout") - 1)) != NULL) { - if (Z_TYPE_P(z_connect_timeout_p) == IS_DOUBLE || - Z_TYPE_P(z_connect_timeout_p) == IS_STRING || - Z_TYPE_P(z_connect_timeout_p) == IS_LONG - ) { - if (Z_TYPE_P(z_connect_timeout_p) == IS_DOUBLE) { - d_connect_timeout = Z_DVAL_P(z_connect_timeout_p); - } else if (Z_TYPE_P(z_connect_timeout_p) == IS_LONG) { - d_connect_timeout = Z_LVAL_P(z_connect_timeout_p); - } else { - d_connect_timeout = atof(Z_STRVAL_P(z_connect_timeout_p)); - } - } - } + if ((zpData = zend_hash_str_find(hOpts, "connect_timeout", sizeof("connect_timeout") - 1)) != NULL) { + if (Z_TYPE_P(zpData) == IS_DOUBLE) { + d_connect_timeout = Z_DVAL_P(zpData); + } else if (Z_TYPE_P(zpData) == IS_LONG) { + d_connect_timeout = Z_LVAL_P(zpData); + } else if (Z_TYPE_P(zpData) == IS_STRING) { + d_connect_timeout = atof(Z_STRVAL_P(zpData)); + } + } } /* extract either name of list of hosts from z0 */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 25e1056710..47369f3817 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -42,24 +42,14 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b ZVAL_STRINGL(&z_cons, "__construct", 11); /* init connections */ - for (zend_hash_internal_pointer_reset(hosts); zend_hash_has_more_elements(hosts) == SUCCESS; zend_hash_move_forward(hosts)) - { - if ((zpData = zend_hash_get_current_data(hosts)) == NULL || Z_TYPE_P(zpData) != IS_STRING) - { + for (zend_hash_internal_pointer_reset(hosts); + zend_hash_has_more_elements(hosts) == SUCCESS; + zend_hash_move_forward(hosts) + ) { + if ((zpData = zend_hash_get_current_data(hosts)) == NULL || Z_TYPE_P(zpData) != IS_STRING) { zval_dtor(&z_cons); - for(i=0;icount;i++) { - zval_dtor(&ra->redis[i]); - efree(ra->hosts[i]); - } - efree(ra->redis); - efree(ra->hosts); - zval_dtor(&ra->z_pure_cmds); - zval_dtor(&ra->z_dist); - zval_dtor(&ra->z_fun); - efree(ra); - return NULL; - } - + return NULL; + } /* default values */ host = Z_STRVAL_P(zpData); @@ -272,15 +262,12 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_retry_interval TSRMLS_CC); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_retry_interval), name, name_len)) != NULL) { - if (Z_TYPE_P(z_data) == IS_LONG || Z_TYPE_P(z_data) == IS_STRING) { - if (Z_TYPE_P(z_data) == IS_LONG) { - l_retry_interval = Z_LVAL_P(z_data); - } - else { - l_retry_interval = atol(Z_STRVAL_P(z_data)); - } - } - } + if (Z_TYPE_P(z_data) == IS_LONG) { + l_retry_interval = Z_LVAL_P(z_data); + } else if (Z_TYPE_P(z_data) == IS_STRING) { + l_retry_interval = atol(Z_STRVAL_P(z_data)); + } + } /* find pconnect option */ array_init(&z_params_pconnect); @@ -310,19 +297,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_connect_timeout TSRMLS_CC); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_connect_timeout), name, name_len)) != NULL) { - if (Z_TYPE_P(z_data) == IS_DOUBLE || - Z_TYPE_P(z_data) == IS_STRING || - Z_TYPE_P(z_data) == IS_LONG - ) { - if (Z_TYPE_P(z_data) == IS_DOUBLE) { - d_connect_timeout = Z_DVAL_P(z_data); - } else if (Z_TYPE_P(z_data) == IS_LONG) { - d_connect_timeout = Z_LVAL_P(z_data); - } else { - d_connect_timeout = atof(Z_STRVAL_P(z_data)); - } - } - } + if (Z_TYPE_P(z_data) == IS_DOUBLE) { + d_connect_timeout = Z_DVAL_P(z_data); + } else if (Z_TYPE_P(z_data) == IS_STRING) { + d_connect_timeout = atof(Z_STRVAL_P(z_data)); + } else if (Z_TYPE_P(z_data) == IS_LONG) { + d_connect_timeout = Z_LVAL_P(z_data); + } + } /* create RedisArray object */ ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); @@ -351,7 +333,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { RedisArray * ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC) { - int count; + int i, count; RedisArray *ra; if (!hosts) return NULL; @@ -368,13 +350,20 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->pconnect = b_pconnect; ra->connect_timeout = connect_timeout; - /* init array data structures */ - ra_init_function_table(ra); + if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL) { + for (i = 0; i < ra->count; ++i) { + zval_dtor(&ra->redis[i]); + efree(ra->hosts[i]); + } + efree(ra->redis); + efree(ra->hosts); + efree(ra); + return NULL; + } + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL; - if(NULL == ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC)) { - return NULL; - } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL; + /* init array data structures */ + ra_init_function_table(ra); /* Set hash function and distribtor if provided */ ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); From 34107966a2a7ddfb6ea5a8a667499b587def7cd3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 22 Nov 2016 17:42:45 -0800 Subject: [PATCH 0641/1986] Move zend_object handler to the end In php7 the zend_object handler structure is inlined (is no longer a pointer, but rather variable sized depending on various things, so it needs to be at the end of the container class. This is mentioned in the extensive /s upgrading documentation from Zend See "custom objects": https://wiki.php.net/phpng-upgrading In addition I believe that the zend library now takes care of freeing the overall structure, so that shouldn't be done anymore if running php >= 7.0.0. --- cluster_library.h | 6 +++--- redis_cluster.c | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index 938deaf22e..31ad8a5f24 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -173,9 +173,6 @@ typedef struct clusterFoldItem clusterFoldItem; /* RedisCluster implementation structure */ typedef struct redisCluster { - /* Object reference for Zend */ - zend_object std; - /* Timeout and read timeout (for normal operations) */ double timeout; double read_timeout; @@ -244,6 +241,9 @@ typedef struct redisCluster { int redir_host_len; unsigned short redir_slot; unsigned short redir_port; + + /* Zend object handler */ + zend_object std; } redisCluster; /* RedisCluster response processing callback */ diff --git a/redis_cluster.c b/redis_cluster.c index dfb6c12906..44f5c3e649 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -331,7 +331,8 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { #else object_properties_init(&cluster->std, class_type); memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); - RedisCluster_handlers.free_obj = free_cluster_context; + RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std); + RedisCluster_handlers.free_obj = free_cluster_context; cluster->std.handlers = &RedisCluster_handlers; @@ -339,6 +340,11 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { #endif } +/* Helper to retrieve the redisCluster object from the zend_object member */ +static redisCluster *getClusterObject(zend_object *object) { + return (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std)); +} + /* Free redisCluster context */ void #if (PHP_MAJOR_VERSION < 7) @@ -347,10 +353,7 @@ free_cluster_context(void *object TSRMLS_DC) free_cluster_context(zend_object *object) #endif { - redisCluster *cluster; - - // Grab context - cluster = (redisCluster*)object; + redisCluster *cluster = getClusterObject(object); // Free any allocated prefix, as well as the struct if(cluster->flags->prefix) efree(cluster->flags->prefix); @@ -366,8 +369,11 @@ free_cluster_context(zend_object *object) if(cluster->err) efree(cluster->err); - // Finally, free the redisCluster structure itself + zend_object_std_dtor(&cluster->std TSRMLS_CC); + +#if (PHP_MAJOR_VERSION < 7) efree(cluster); +#endif } /* Attempt to connect to a Redis cluster provided seeds and timeout options */ From d22e7f5a5cb7ebb783b885912b0bf1677c4c74bc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 22 Nov 2016 18:05:13 -0800 Subject: [PATCH 0642/1986] Unbreak php7 fix for php <= 7.0 --- cluster_library.h | 6 ++++++ redis_cluster.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/cluster_library.h b/cluster_library.h index 31ad8a5f24..4d1d532aa0 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -173,6 +173,10 @@ typedef struct clusterFoldItem clusterFoldItem; /* RedisCluster implementation structure */ typedef struct redisCluster { +#if (PHP_MAJOR_VERSION < 7) + zend_object std; +#endif + /* Timeout and read timeout (for normal operations) */ double timeout; double read_timeout; @@ -242,8 +246,10 @@ typedef struct redisCluster { unsigned short redir_slot; unsigned short redir_port; +#if (PHP_MAJOR_VERSION >= 7) /* Zend object handler */ zend_object std; +#endif } redisCluster; /* RedisCluster response processing callback */ diff --git a/redis_cluster.c b/redis_cluster.c index 44f5c3e649..ba312a67b7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -342,7 +342,11 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { /* Helper to retrieve the redisCluster object from the zend_object member */ static redisCluster *getClusterObject(zend_object *object) { +#if (PHP_MAJOR_VERSION < 7) + return (redisCluster*)object; +#else return (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std)); +#endif } /* Free redisCluster context */ From c7ab55a341121b7ba1f33e199b2aceaef3ef1fdb Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 23 Nov 2016 22:40:50 +0200 Subject: [PATCH 0643/1986] Issue #504 --- redis.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/redis.c b/redis.c index 0049484e7b..0b684f931e 100644 --- a/redis.c +++ b/redis.c @@ -431,11 +431,8 @@ static void redis_destructor_redis_sock(zend_resource * rsrc TSRMLS_DC) redis_free_socket(redis_sock); } -/** - * redis_sock_get - */ -PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, - int no_throw) +static zend_always_inline int +redis_sock_get_instance(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) { zval *socket; @@ -466,6 +463,19 @@ PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, } return -1; } + return 0; +} + +/** + * redis_sock_get + */ +PHP_REDIS_API int +redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) +{ + if (redis_sock_get_instance(id, redis_sock TSRMLS_CC, no_throw) < 0) { + return -1; + } + if ((*redis_sock)->lazy_connect) { (*redis_sock)->lazy_connect = 0; @@ -2670,7 +2680,7 @@ PHP_METHOD(Redis, object) PHP_METHOD(Redis, getOption) { RedisSock *redis_sock; - if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + if (redis_sock_get_instance(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } @@ -2683,7 +2693,7 @@ PHP_METHOD(Redis, getOption) { PHP_METHOD(Redis, setOption) { RedisSock *redis_sock; - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { + if (redis_sock_get_instance(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; } From 48c8f2a30a254b28194047c3720950fe53289210 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 24 Nov 2016 12:11:05 +0200 Subject: [PATCH 0644/1986] refactoring --- redis_cluster.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index ba312a67b7..304886ce58 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -340,25 +340,15 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { #endif } -/* Helper to retrieve the redisCluster object from the zend_object member */ -static redisCluster *getClusterObject(zend_object *object) { -#if (PHP_MAJOR_VERSION < 7) - return (redisCluster*)object; -#else - return (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std)); -#endif -} - /* Free redisCluster context */ void #if (PHP_MAJOR_VERSION < 7) -free_cluster_context(void *object TSRMLS_DC) +free_cluster_context(void *object TSRMLS_DC) { + redisCluster *cluster = (redisCluster*)object; #else -free_cluster_context(zend_object *object) +free_cluster_context(zend_object *object) { + redisCluster *cluster = (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std)); #endif -{ - redisCluster *cluster = getClusterObject(object); - // Free any allocated prefix, as well as the struct if(cluster->flags->prefix) efree(cluster->flags->prefix); efree(cluster->flags); @@ -1949,6 +1939,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* validate that this key maps to the same node */ if(node && c->master[slot] != node) { + if (key_free) efree(key); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Keys appear to map to different nodes"); RETURN_FALSE; @@ -1980,7 +1971,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } else { void *ctx = NULL; CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); - RETURN_ZVAL(getThis(), 1, 0); + RETVAL_ZVAL(getThis(), 1, 0); } efree(cmdstr.c); From 71c9f7c84c65c10dbe0ded9740f7dcce00da650c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 28 Nov 2016 18:00:01 +0200 Subject: [PATCH 0645/1986] Issue #760 --- common.h | 16 +++++++++++ redis_commands.c | 75 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/common.h b/common.h index 21532cc2c8..9852afb7ba 100644 --- a/common.h +++ b/common.h @@ -300,6 +300,22 @@ zval_get_long(zval *op) return 0; } +static zend_always_inline double +zval_get_double(zval *op) +{ + switch (Z_TYPE_P(op)) { + case IS_BOOL: + case IS_LONG: + return (double)Z_LVAL_P(op); + case IS_DOUBLE: + return Z_DVAL_P(op); + case IS_STRING: + return zend_strtod(Z_STRVAL_P(op), NULL); + EMPTY_SWITCH_DEFAULT_CASE() + } + return 0.0; +} + static void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC) = &php_var_serialize; #define php_var_serialize(buf, struc, data) _php_var_serialize(buf, &struc, data TSRMLS_CC) static int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; diff --git a/redis_commands.c b/redis_commands.c index c2c438c350..717b69e610 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2577,25 +2577,57 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_args; - char *key, *val; + char *key, *val, *exp_type = NULL; int key_len, key_free, val_len, val_free; - int argc = ZEND_NUM_ARGS(), i; + int num = ZEND_NUM_ARGS(), i = 1, argc; + zend_bool ch = 0, incr = 0; smart_string cmdstr = {0}; - z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + if (num < 3) return FAILURE; + z_args = ecalloc(num, sizeof(zval)); + if (zend_get_parameters_array(ht, num, z_args) == FAILURE) { efree(z_args); return FAILURE; } - // Need key, score, value, [score, value...] */ - if(argc>0) convert_to_string(&z_args[0]); - if(argc<3 || Z_TYPE(z_args[0])!=IS_STRING || (argc-1)%2 != 0) { - efree(z_args); - return FAILURE; + // Need key, [NX|XX] [CH] [INCR] score, value, [score, value...] */ + if (num % 2 == 0) { + if (Z_TYPE(z_args[1]) != IS_ARRAY) { + efree(z_args); + return FAILURE; + } + zval *z_opt; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[1]), z_opt) { + if (Z_TYPE_P(z_opt) == IS_STRING) { + if (Z_STRLEN_P(z_opt) == 2) { + if (IS_NX_XX_ARG(Z_STRVAL_P(z_opt))) { + exp_type = Z_STRVAL_P(z_opt); + } else if (strncasecmp(Z_STRVAL_P(z_opt), "ch", 2) == 0) { + ch = 1; + } + } else if (Z_STRLEN_P(z_opt) == 4 && + strncasecmp(Z_STRVAL_P(z_opt), "incr", 4) == 0 + ) { + if (num > 4) { + // Only one score-element pair can be specified in this mode. + efree(z_args); + return FAILURE; + } + incr = 1; + } + + } + } ZEND_HASH_FOREACH_END(); + argc = num - 1; + if (exp_type) argc++; + argc += ch + incr; + i++; + } else { + argc = num; } // Prefix our key + convert_to_string(&z_args[0]); key = Z_STRVAL(z_args[0]); key_len = Z_STRLEN(z_args[0]); key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -2608,19 +2640,32 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, CMD_SET_SLOT(slot,key,key_len); if(key_free) efree(key); + if (exp_type) redis_cmd_append_sstr(&cmdstr, exp_type, 2); + if (ch) redis_cmd_append_sstr(&cmdstr, "CH", 2); + if (incr) redis_cmd_append_sstr(&cmdstr, "INCR", 4); + // Now the rest of our arguments - for(i=1;i Date: Tue, 29 Nov 2016 15:41:49 +0200 Subject: [PATCH 0646/1986] Issue #1036 --- cluster_library.c | 26 +++++++++++--------------- cluster_library.h | 8 +++----- library.c | 29 +++++++++-------------------- library.h | 2 +- 4 files changed, 24 insertions(+), 41 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 6a70444c32..3d8e5731a8 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -486,26 +486,22 @@ void cluster_multi_fini(clusterMultiCmd *mc) { } /* Set our last error string encountered */ -static void cluster_set_err(redisCluster *c, char *err, int err_len) +static void +cluster_set_err(redisCluster *c, char *err, int err_len) { - if(err && err_len>0) { - if(err_len >= sizeof("CLUSTERDOWN")-1 && - !memcmp(err, "CLUSTERDOWN", sizeof("CLUSTERDOWN")-1)) - { + // Free our last error + if (c->err != NULL) { + efree(c->err); + } + if (err != NULL && err_len > 0) { + if (err_len >= sizeof("CLUSTERDOWN") - 1 && + !memcmp(err, "CLUSTERDOWN", sizeof("CLUSTERDOWN") - 1) + ) { c->clusterdown = 1; - return; - } - - if(c->err == NULL) { - c->err = emalloc(err_len+1); - } else if(err_len > c->err_len) { - c->err = erealloc(c->err, err_len + 1); } - memcpy(c->err,err,err_len); - c->err[err_len]='\0'; + c->err = estrndup(err, err_len); c->err_len = err_len; } else { - if(c->err) efree(c->err); c->err = NULL; c->err_len = 0; } diff --git a/cluster_library.h b/cluster_library.h index 4d1d532aa0..90eb97201c 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -60,11 +60,9 @@ /* Clear out our "last error" */ #define CLUSTER_CLEAR_ERROR(c) \ - if(c->err) { \ - efree(c->err); \ - c->err = NULL; \ - c->err_len = 0; \ - } \ + if(c->err) efree(c->err); \ + c->err = NULL; \ + c->err_len = 0; \ c->clusterdown = 0; /* Protected sending of data down the wire to a RedisSock->stream */ diff --git a/library.c b/library.c index 763c5b3a05..fbd5f30616 100644 --- a/library.c +++ b/library.c @@ -1704,34 +1704,23 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, /** * redis_sock_set_err */ -PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, - int msg_len) +PHP_REDIS_API void +redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { - // Allocate/Reallocate our last error member - if(msg != NULL && msg_len > 0) { - if(redis_sock->err == NULL) { - redis_sock->err = emalloc(msg_len + 1); - } else if(msg_len > redis_sock->err_len) { - redis_sock->err = erealloc(redis_sock->err, msg_len +1); - } + // Free our last error + if (redis_sock->err != NULL) { + efree(redis_sock->err); + } - // Copy in our new error message, set new length, and null terminate - memcpy(redis_sock->err, msg, msg_len); - redis_sock->err[msg_len] = '\0'; + if (msg != NULL && msg_len > 0) { + // Copy in our new error message + redis_sock->err = estrndup(msg, msg_len); redis_sock->err_len = msg_len; } else { - // Free our last error - if(redis_sock->err != NULL) { - efree(redis_sock->err); - } - // Set to null, with zero length redis_sock->err = NULL; redis_sock->err_len = 0; } - - // Success - return 0; } /** diff --git a/library.h b/library.h index f5f06c8ad7..f3125778ca 100644 --- a/library.h +++ b/library.h @@ -62,7 +62,7 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); -PHP_REDIS_API int redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); +PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC); From 6dacd60c0cc9acc8256566e6000b6027f47df5ef Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 30 Nov 2016 18:17:50 +0200 Subject: [PATCH 0647/1986] Issue #1036 --- cluster_library.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cluster_library.c b/cluster_library.c index 3d8e5731a8..44bfe67372 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -820,6 +820,8 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, c->read_timeout = read_timeout; c->failover = failover; c->persistent = persistent; + c->err = NULL; + c->err_len = 0; /* Set up our waitms based on timeout */ c->waitms = (long)(1000 * timeout); From 64a1e592572c11cd9a8f532dccf3462850b2efcf Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 30 Nov 2016 23:45:35 +0200 Subject: [PATCH 0648/1986] TravisCI: php5.6 + clang --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 32d4df09f5..f838abb038 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,8 @@ php: env: CC=gcc matrix: include: + - env: CC=clang + php: 5.6 - env: CC=clang php: 7.0 - env: CC=clang From a23e36f208bc2598abe3074071e8b72ac913cb01 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 2 Dec 2016 12:18:52 +0200 Subject: [PATCH 0649/1986] zval_get_string Added gc attribute to zend_string (for zend_string_release). Added function zval_get_string and replace convert_to_string with it. --- cluster_library.c | 5 +- common.h | 41 ++++++++++++- library.c | 29 +++++----- library.h | 2 + redis.c | 77 +++++++----------------- redis_array_impl.c | 2 - redis_cluster.c | 38 ++++++------ redis_commands.c | 138 ++++++++++++++++++++------------------------ tests/RedisTest.php | 5 +- 9 files changed, 161 insertions(+), 176 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 44bfe67372..d85c654f73 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2420,8 +2420,9 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, } else { zval zv, *z = &zv; if (redis_unserialize(redis_sock,key,key_len, z TSRMLS_CC)) { - convert_to_string(z); - add_assoc_double_ex(z_result, Z_STRVAL_P(z), Z_STRLEN_P(z), atof(line)); + zend_string *zstr = zval_get_string(z); + add_assoc_double_ex(z_result, zstr->val, zstr->len, atof(line)); + zend_string_release(zstr); zval_dtor(z); } else { add_assoc_double_ex(z_result, key, key_len, atof(line)); diff --git a/common.h b/common.h index 9852afb7ba..f828f411ce 100644 --- a/common.h +++ b/common.h @@ -5,6 +5,7 @@ #define REDIS_COMMON_H #include +#include #if (PHP_MAJOR_VERSION < 7) #include typedef smart_str smart_string; @@ -14,12 +15,16 @@ typedef smart_str smart_string; #define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) typedef struct { + short gc; size_t len; char *val; } zend_string; #define zend_string_release(s) do { \ - if ((s) && (s)->val) efree((s)->val); \ + if ((s) && (s)->gc) { \ + if ((s)->gc & 0x10 && (s)->val) efree((s)->val); \ + if ((s)->gc & 0x01) efree((s)); \ + } \ } while (0) #define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) do { \ @@ -316,6 +321,40 @@ zval_get_double(zval *op) return 0.0; } +static zend_always_inline zend_string * +zval_get_string(zval *op) +{ + zend_string *zstr = ecalloc(1, sizeof(zend_string)); + + zstr->val = ""; + zstr->len = 0; + switch (Z_TYPE_P(op)) { + case IS_STRING: + zstr->val = Z_STRVAL_P(op); + zstr->len = Z_STRLEN_P(op); + break; + case IS_BOOL: + if (Z_LVAL_P(op)) { + zstr->val = "1"; + zstr->len = 1; + } + break; + case IS_LONG: { + zstr->gc = 0x10; + zstr->len = spprintf(&zstr->val, 0, "%ld", Z_LVAL_P(op)); + break; + } + case IS_DOUBLE: { + zstr->gc = 0x10; + zstr->len = spprintf(&zstr->val, 0, "%g", Z_DVAL_P(op)); + break; + } + EMPTY_SWITCH_DEFAULT_CASE() + } + zstr->gc |= 0x01; + return zstr; +} + static void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC) = &php_var_serialize; #define php_var_serialize(buf, struc, data) _php_var_serialize(buf, &struc, data TSRMLS_CC) static int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; diff --git a/library.c b/library.c index fbd5f30616..175b766761 100644 --- a/library.c +++ b/library.c @@ -15,7 +15,6 @@ #include "php_redis.h" #include "library.h" #include "redis_commands.h" -#include #include #define UNSERIALIZE_NONE 0 @@ -1212,8 +1211,6 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, zend_hash_has_more_elements(keytable) == SUCCESS; zend_hash_move_forward(keytable)) { - char *hkey, *hval; - int hkey_len; zval *z_key_p, *z_value_p; if ((z_key_p = zend_hash_get_current_data(keytable)) == NULL) { @@ -1221,34 +1218,34 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, } /* get current value, a key */ - convert_to_string(z_key_p); - hkey = Z_STRVAL_P(z_key_p); - hkey_len = Z_STRLEN_P(z_key_p); + zend_string *hkey = zval_get_string(z_key_p); /* move forward */ zend_hash_move_forward(keytable); /* fetch again */ if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) { + zend_string_release(hkey); continue; /* this should never happen, according to the PHP people. */ } /* get current value, a hash value now. */ - hval = Z_STRVAL_P(z_value_p); + char *hval = Z_STRVAL_P(z_value_p); /* Decode the score depending on flag */ if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) { - add_assoc_long_ex(z_ret, hkey, hkey_len, atoi(hval+1)); + add_assoc_long_ex(z_ret, hkey->val, hkey->len, atoi(hval+1)); } else if (decode == SCORE_DECODE_DOUBLE) { - add_assoc_double_ex(z_ret, hkey, hkey_len, atof(hval)); + add_assoc_double_ex(z_ret, hkey->val, hkey->len, atof(hval)); } else { zval zv0, *z = &zv0; #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); #endif ZVAL_ZVAL(z, z_value_p, 1, 0); - add_assoc_zval_ex(z_ret, hkey, hkey_len, z); + add_assoc_zval_ex(z_ret, hkey->val, hkey->len, z); } + zend_string_release(hkey); } /* replace */ @@ -1995,13 +1992,13 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len *val_len = 5; break; - default: /* copy */ - ZVAL_ZVAL(&z_copy, z, 1, 0); - convert_to_string(&z_copy); - *val = estrndup(Z_STRVAL(z_copy), Z_STRLEN(z_copy)); - *val_len = Z_STRLEN(z_copy); - zval_dtor(&z_copy); + default: { /* copy */ + zend_string *zstr = zval_get_string(z); + *val = estrndup(zstr->val, zstr->len); + *val_len = zstr->len; + zend_string_release(zstr); return 1; + } } break; case REDIS_SERIALIZER_PHP: diff --git a/library.h b/library.h index f3125778ca..8eb67083c2 100644 --- a/library.h +++ b/library.h @@ -91,6 +91,7 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo zend_string _zstr = {0}; \ _zstr.val = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \ _zstr.len = strlen(_zstr.val); \ + _zstr.gc = 0x10; \ dbl_str = &_zstr; \ } while (0); #else @@ -98,6 +99,7 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo zend_string _zstr = {0}; \ _zstr.val = _php_math_number_format(dbl, 16, '.', '\x00'); \ _zstr.len = strlen(_zstr.val); \ + _zstr.gc = 0x10; \ dbl_str = &_zstr; \ } while (0) #endif diff --git a/redis.c b/redis.c index 0b684f931e..db3b3c8e8f 100644 --- a/redis.c +++ b/redis.c @@ -1014,31 +1014,17 @@ PHP_METHOD(Redis, getMultiple) /* Iterate through and grab our keys */ ZEND_HASH_FOREACH_VAL(hash, z_ele) { - char *key; - int key_len, key_free; - zval z_tmp; - - ZVAL_NULL(&z_tmp); - /* If the key isn't a string, turn it into one */ - if (Z_TYPE_P(z_ele) == IS_STRING) { - key = Z_STRVAL_P(z_ele); - key_len = Z_STRLEN_P(z_ele); - } else { - ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); - convert_to_string(&z_tmp); - key = Z_STRVAL(z_tmp); - key_len = Z_STRLEN(z_tmp); - } - + zend_string *zstr = zval_get_string(z_ele); + char *key = zstr->val; + int key_len = zstr->len; /* Apply key prefix if necissary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - + int key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Append this key to our command */ redis_cmd_append_sstr(&cmd, key, key_len); - + /* release zend_string */ + zend_string_release(zstr); /* Free our key if it was prefixed */ if(key_free) efree(key); - zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); /* Kick off our command */ @@ -2899,20 +2885,9 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Iterate our elements */ ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) { - char *key; - int key_len, key_free; - zval z_tmp; - - ZVAL_NULL(&z_tmp); - if (Z_TYPE_P(z_ele) == IS_STRING) { - key = Z_STRVAL_P(z_ele); - key_len = Z_STRLEN_P(z_ele); - } else { - ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); - convert_to_string(&z_tmp); - key = Z_STRVAL(z_tmp); - key_len = Z_STRLEN(z_tmp); - } + zend_string *zstr = zval_get_string(z_ele); + char *key = zstr->val; + int key_len = zstr->len; /* Apply prefix if required */ key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -2920,9 +2895,9 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Append this channel */ redis_cmd_append_sstr(&cmd, key, key_len); + zend_string_release(zstr); /* Free key if prefixed */ if(key_free) efree(key); - zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); /* Set return */ @@ -3044,28 +3019,16 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, // Iterate the values in our "keys" array ZEND_HASH_FOREACH_VAL(args_hash, elem) { - zval z_tmp; - char *key, *old_cmd; - int key_len, key_free; - - ZVAL_NULL(&z_tmp); - if (Z_TYPE_P(elem) == IS_STRING) { - key = Z_STRVAL_P(elem); - key_len = Z_STRLEN_P(elem); - } else { - /* Convert it to a string */ - ZVAL_ZVAL(&z_tmp, elem, 1, 0); - convert_to_string(&z_tmp); - key = Z_STRVAL(z_tmp); - key_len = Z_STRLEN(z_tmp); - } + zend_string *zstr = zval_get_string(elem); + char *key = zstr->val; + int key_len = zstr->len; /* Keep track of the old command pointer */ - old_cmd = *ret; + char *old_cmd = *ret; // If this is still a key argument, prefix it if we've been set // up to prefix keys - key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, + int key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, &key, &key_len) : 0; // Append this key to our EVAL command, free our old command @@ -3073,9 +3036,9 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, cmd_len, key_len, key, key_len); efree(old_cmd); + zend_string_release(zstr); /* Free our key, old command if we need to */ if(key_free) efree(key); - zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); } } @@ -3178,12 +3141,12 @@ redis_build_script_exists_cmd(char **ret, zval *argv, int argc) { /* Iterate our arguments */ for(i=0;ival, zstr->len); + + zend_string_release(zstr); } /* Success */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 47369f3817..2eddc4a08e 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -389,7 +389,6 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function"); return NULL; } - /* convert_to_string(ra->z_fun); */ /* call extraction function */ ZVAL_STRINGL(&z_argv[0], key, key_len); @@ -441,7 +440,6 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); return 0; } - /* convert_to_string(ra->z_fun); */ /* call extraction function */ ZVAL_STRINGL(&z_argv[0], key, key_len); diff --git a/redis_cluster.c b/redis_cluster.c index 304886ce58..56ad0cfc05 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1906,6 +1906,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot = 0; smart_string cmdstr = {0}; strlen_t lua_len; + zend_string *zstr; /* Parse args */ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, @@ -1928,9 +1929,9 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Iterate over our args if we have any if(args_count > 0) { ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - convert_to_string(z_ele); - key = Z_STRVAL_P(z_ele); - key_len = Z_STRLEN_P(z_ele); + zstr = zval_get_string(z_ele); + key = zstr->val; + key_len = zstr->len; /* If we're still on a key, prefix it check node */ if(num_keys-- > 0) { @@ -1939,6 +1940,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* validate that this key maps to the same node */ if(node && c->master[slot] != node) { + zend_string_release(zstr); if (key_free) efree(key); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Keys appear to map to different nodes"); @@ -1953,6 +1955,7 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* Append this key/argument */ redis_cmd_append_sstr(&cmdstr, key, key_len); + zend_string_release(zstr); /* Free key if we prefixed */ if(key_free) efree(key); } ZEND_HASH_FOREACH_END(); @@ -2134,6 +2137,7 @@ PHP_METHOD(RedisCluster, watch) { zval *z_args; int argc = ZEND_NUM_ARGS(), i; ulong slot; + zend_string *zstr; // Disallow in MULTI mode if(c->flags->mode == MULTI) { @@ -2159,17 +2163,17 @@ PHP_METHOD(RedisCluster, watch) { // Loop through arguments, prefixing if needed for(i=0;ival, zstr->len, NULL) == FAILURE) { zend_throw_exception(redis_cluster_exception_ce, "Can't issue WATCH command as the keyspace isn't fully mapped", 0 TSRMLS_CC); + zend_string_release(zstr); RETURN_FALSE; } + zend_string_release(zstr); } // Iterate over each node we'll be sending commands to @@ -2303,34 +2307,26 @@ static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { int key_len, key_free; - zval *z_host, *z_port, z_tmp; + zval *z_host, *z_port; short slot; char *key; + zend_string *zstr; /* If it's a string, treat it as a key. Otherwise, look for a two * element array */ if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG || Z_TYPE_P(z_arg)==IS_DOUBLE) { - ZVAL_NULL(&z_tmp); /* Allow for any scalar here */ - if (Z_TYPE_P(z_arg) == IS_STRING) { - key = Z_STRVAL_P(z_arg); - key_len = Z_STRLEN_P(z_arg); - } else { - ZVAL_ZVAL(&z_tmp, z_arg, 1, 0); - convert_to_string(&z_tmp); - key = Z_STRVAL(z_tmp); - key_len = Z_STRLEN(z_tmp); - } + zstr = zval_get_string(z_arg); + key = zstr->val; + key_len = zstr->len; /* Hash it */ key_free = redis_key_prefix(c->flags, &key, &key_len); slot = cluster_hash_key(key, key_len); + zend_string_release(zstr); if(key_free) efree(key); - - /* Destroy our temp value if we had to convert it */ - zval_dtor(&z_tmp); } else if (Z_TYPE_P(z_arg) == IS_ARRAY && (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL && (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL && diff --git a/redis_commands.c b/redis_commands.c index 717b69e610..1470e8f6d2 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -665,32 +665,20 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Process input keys ZEND_HASH_FOREACH_VAL(ht_keys, z_ele) { - char *key; - int key_free, key_len; - zval z_tmp; - - ZVAL_NULL(&z_tmp); - if (Z_TYPE_P(z_ele) == IS_STRING) { - key = Z_STRVAL_P(z_ele); - key_len = Z_STRLEN_P(z_ele); - } else { - ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); - convert_to_string(&z_tmp); - - key = Z_STRVAL(z_tmp); - key_len = Z_STRLEN(z_tmp); - } + zend_string *zstr = zval_get_string(z_ele); + char *key = zstr->val; + int key_len = zstr->len; // Prefix key if necissary - key_free = redis_key_prefix(redis_sock, &key, &key_len); + int key_free = redis_key_prefix(redis_sock, &key, &key_len); // If we're in Cluster mode, verify the slot is the same if(slot && *slot != cluster_hash_key(key,key_len)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "All keys don't hash to the same slot!"); efree(cmdstr.c); + zend_string_release(zstr); if(key_free) efree(key); - zval_dtor(&z_tmp); return FAILURE; } @@ -698,8 +686,8 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, key, key_len); // Cleanup + zend_string_release(zstr); if(key_free) efree(key); - zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); // Weights @@ -782,16 +770,17 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate over channels ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { // We want to deal with strings here - convert_to_string(z_chan); + zend_string *zstr = zval_get_string(z_chan); // Grab channel name, prefix if required - key = Z_STRVAL_P(z_chan); - key_len = Z_STRLEN_P(z_chan); + key = zstr->val; + key_len = zstr->len; key_free = redis_key_prefix(redis_sock, &key, &key_len); // Add this channel redis_cmd_append_sstr(&cmdstr, key, key_len); + zend_string_release(zstr); // Free our key if it was prefixed if(key_free) efree(key); } ZEND_HASH_FOREACH_END(); @@ -972,9 +961,9 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Grab the first argument (our key) as a string - convert_to_string(&z_args[0]); - arg = Z_STRVAL(z_args[0]); - arg_len = Z_STRLEN(z_args[0]); + zend_string *zstr = zval_get_string(&z_args[0]); + arg = zstr->val; + arg_len = zstr->len; // Prefix if required arg_free = redis_key_prefix(redis_sock, &arg, &arg_len); @@ -985,6 +974,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set our slot, free key prefix if we prefixed it CMD_SET_SLOT(slot,arg,arg_len); + zend_string_release(zstr); if(arg_free) efree(arg); // Add our members @@ -1063,6 +1053,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; long timeout; short kslot = -1; + zend_string *zstr; if(argc < min_argc) { zend_wrong_param_count(TSRMLS_C); @@ -1102,9 +1093,9 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(single_array) { ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - convert_to_string(z_ele); - key = Z_STRVAL_P(z_ele); - key_len = Z_STRLEN_P(z_ele); + zstr = zval_get_string(z_ele); + key = zstr->val; + key_len = zstr->len; key_free = redis_key_prefix(redis_sock, &key, &key_len); // Protect against CROSSLOT errors @@ -1112,6 +1103,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(kslot == -1) { kslot = cluster_hash_key(key, key_len); } else if(cluster_hash_key(key,key_len)!=kslot) { + zend_string_release(zstr); if(key_free) efree(key); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not all keys hash to the same slot!"); @@ -1121,6 +1113,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Append this key, free it if we prefixed redis_cmd_append_sstr(&cmdstr, key, key_len); + zend_string_release(zstr); if(key_free) efree(key); } ZEND_HASH_FOREACH_END(); if(has_timeout) { @@ -1136,9 +1129,9 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, tail = has_timeout ? argc-1 : argc; for(i=0;ival; + key_len = zstr->len; key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -1149,6 +1142,8 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else if(cluster_hash_key(key,key_len)!=kslot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not all keys hash to the same slot"); + zend_string_release(zstr); + if(key_free) efree(key); efree(z_args); return FAILURE; } @@ -1156,6 +1151,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Append this key redis_cmd_append_sstr(&cmdstr, key, key_len); + zend_string_release(zstr); if(key_free) efree(key); } if(has_timeout) { @@ -1489,9 +1485,6 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); - // Allocate memory for mems+1 so we can have a sentinel z_mems = ecalloc(count + 1, sizeof(zval)); @@ -1512,7 +1505,6 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If nothing was valid, fail if(valid == 0) { - if(key_free) efree(key); efree(z_mems); return FAILURE; } @@ -1523,6 +1515,9 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Start command construction redis_cmd_init_sstr(&cmdstr, valid+1, "HMGET", sizeof("HMGET")-1); + // Prefix our key + key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + redis_cmd_append_sstr(&cmdstr, key, key_len); // Iterate over members, appending as arguments @@ -1675,6 +1670,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_len, i, key_free, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; short kslot; + zend_string *zstr; // Allocate space for args, parse them as an array z_args = emalloc(argc * sizeof(zval)); @@ -1696,11 +1692,11 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Now iterate over our keys argument for(i=1;ival; + key_len = zstr->len; // Prefix key, append key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -1712,6 +1708,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(*slot == -1 || kslot != *slot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Warning, not all keys hash to the same slot!"); + zend_string_release(zstr); if(key_free) efree(key); efree(z_args); return FAILURE; @@ -1719,6 +1716,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *slot = kslot; } + zend_string_release(zstr); if(key_free) efree(key); } @@ -1892,6 +1890,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int num_keys, key_len, key_free; char *key; short kslot=-1; + zend_string *zstr; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"z",&z_keys)==FAILURE) { return FAILURE; @@ -1915,18 +1914,10 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Append our key(s) */ ZEND_HASH_FOREACH_VAL(ht_keys, z_key) { - ZVAL_NULL(&z_tmp); /* Turn our value into a string if it isn't one */ - if (Z_TYPE_P(z_key) == IS_STRING) { - key = Z_STRVAL_P(z_key); - key_len = Z_STRLEN_P(z_key); - } else { - ZVAL_ZVAL(&z_tmp, z_key, 1, 0); - convert_to_string(&z_tmp); - - key = Z_STRVAL(z_tmp); - key_len = Z_STRLEN(z_tmp); - } + zstr = zval_get_string(z_key); + key = zstr->val; + key_len = zstr->len; /* Append this key to our command */ key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -1937,8 +1928,8 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (kslot == -1) { kslot = cluster_hash_key(key, key_len); } else if(cluster_hash_key(key,key_len)!=kslot) { + zend_string_release(zstr); if (key_free) efree(key); - zval_dtor(&z_tmp); efree(cmdstr.c); php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -1948,25 +1939,17 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Cleanup */ + zend_string_release(zstr); if (key_free) efree(key); - zval_dtor(&z_tmp); } ZEND_HASH_FOREACH_END(); } else { - ZVAL_NULL(&z_tmp); - /* Turn our key into a string if it's a different type */ - if (Z_TYPE_P(z_keys) == IS_STRING) { - key = Z_STRVAL_P(z_keys); - key_len = Z_STRLEN_P(z_keys); - } else { - ZVAL_ZVAL(&z_tmp, z_keys, 1, 0); - convert_to_string(&z_tmp); - - key = Z_STRVAL(z_tmp); - key_len = Z_STRLEN(z_tmp); - } - /* Construct our whole command */ redis_cmd_init_sstr(&cmdstr, 1, "PFCOUNT", sizeof("PFCOUNT")-1); + + /* Turn our key into a string if it's a different type */ + zstr = zval_get_string(z_keys); + key = zstr->val; + key_len = zstr->len; key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); @@ -1974,8 +1957,8 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, CMD_SET_SLOT(slot, key, key_len); /* Cleanup */ + zend_string_release(zstr); if (key_free) efree(key); - zval_dtor(&z_tmp); } /* Push our command and length to the caller */ @@ -2525,6 +2508,7 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *arg; int arg_free, arg_len, i; int argc = ZEND_NUM_ARGS(); + zend_string *zstr; // We need at least KEY and one member if(argc < 2) { @@ -2539,9 +2523,9 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Get first argument (the key) as a string - convert_to_string(&z_args[0]); - arg = Z_STRVAL(z_args[0]); - arg_len = Z_STRLEN(z_args[0]); + zstr = zval_get_string(&z_args[0]); + arg = zstr->val; + arg_len = zstr->len; // Prefix arg_free = redis_key_prefix(redis_sock, &arg, &arg_len); @@ -2552,13 +2536,14 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set our slot, free key if we prefixed it CMD_SET_SLOT(slot,arg,arg_len); + zend_string_release(zstr); if(arg_free) efree(arg); // Iterate through the members we're removing for(i=1;ival, zstr->len); + zend_string_release(zstr); } // Push out values @@ -2582,6 +2567,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int num = ZEND_NUM_ARGS(), i = 1, argc; zend_bool ch = 0, incr = 0; smart_string cmdstr = {0}; + zend_string *zstr; if (num < 3) return FAILURE; z_args = ecalloc(num, sizeof(zval)); @@ -2627,9 +2613,9 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - convert_to_string(&z_args[0]); - key = Z_STRVAL(z_args[0]); - key_len = Z_STRLEN(z_args[0]); + zstr = zval_get_string(&z_args[0]); + key = zstr->val; + key_len = zstr->len; key_free = redis_key_prefix(redis_sock, &key, &key_len); // Start command construction @@ -2638,6 +2624,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set our slot, free key if we prefixed it CMD_SET_SLOT(slot,key,key_len); + zend_string_release(zstr); if(key_free) efree(key); if (exp_type) redis_cmd_append_sstr(&cmdstr, exp_type, 2); @@ -3062,8 +3049,9 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, "GETKEYS", sizeof("GETKEYS")-1); ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - convert_to_string(z_ele); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + zend_string *zstr = zval_get_string(z_ele); + redis_cmd_append_sstr(&cmdstr, zstr->val, zstr->len); + zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 75fb7f4c96..0fc7809ac3 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1960,8 +1960,9 @@ public function testZX() { $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); - $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertTrue(1 === $this->redis->zAdd('key', array(), 1, 'val1')); // empty options + $this->assertTrue(1 === $this->redis->zAdd('key', array('nx'), 3, 'val3')); // nx option + $this->assertTrue(0 === $this->redis->zAdd('key', array('xx'), 3, 'val3')); // xx option $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); From c799d256ce13d1d17f3e5fbc89e9fabc78a3af9e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 2 Dec 2016 16:56:16 +0200 Subject: [PATCH 0650/1986] zval_get_string + zAdd tests fix --- library.c | 1 - redis_cluster.c | 6 +++--- redis_commands.c | 35 +++++++++++------------------------ tests/RedisTest.php | 11 ++++++++--- 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/library.c b/library.c index 175b766761..a48fc41acb 100644 --- a/library.c +++ b/library.c @@ -1967,7 +1967,6 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len HashTable ht; #endif smart_str sstr = {0}; - zval z_copy; #ifdef HAVE_REDIS_IGBINARY size_t sz; uint8_t *val8; diff --git a/redis_cluster.c b/redis_cluster.c index 56ad0cfc05..1a5e1898c7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2435,9 +2435,9 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) /* Iterate, appending args */ for(i=1;ival, zstr->len); + zend_string_release(zstr); } /* Send it off */ diff --git a/redis_commands.c b/redis_commands.c index 1470e8f6d2..ec4686d626 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1771,6 +1771,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *mem, *key; int key_free, mem_len, mem_free, argc=1; strlen_t key_len; + zend_string *zstr; // Parse arguments if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, @@ -1801,20 +1802,11 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Now iterate over the rest of our keys or values ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - zval z_tmp; - - ZVAL_NULL(&z_tmp); // Prefix keys, serialize values if(is_keys) { - if (Z_TYPE_P(z_ele) == IS_STRING) { - mem = Z_STRVAL_P(z_ele); - mem_len = Z_STRLEN_P(z_ele); - } else { - ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); - convert_to_string(&z_tmp); - mem = Z_STRVAL(z_tmp); - mem_len = Z_STRLEN(z_tmp); - } + zstr = zval_get_string(z_ele); + mem = zstr->val; + mem_len = zstr->len; // Key prefix mem_free = redis_key_prefix(redis_sock, &mem, &mem_len); @@ -1823,24 +1815,19 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(slot && *slot != cluster_hash_key(mem, mem_len)) { php_error_docref(0 TSRMLS_CC, E_WARNING, "All keys must hash to the same slot!"); + zend_string_release(zstr); if(key_free) efree(key); - zval_dtor(&z_tmp); return FAILURE; } } else { mem_free = redis_serialize(redis_sock, z_ele, &mem, &mem_len TSRMLS_CC); + zstr = NULL; if(!mem_free) { - if (Z_TYPE_P(z_ele) == IS_STRING) { - mem = Z_STRVAL_P(z_ele); - mem_len = Z_STRLEN_P(z_ele); - } else { - ZVAL_ZVAL(&z_tmp, z_ele, 1, 0); - convert_to_string(&z_tmp); - mem = Z_STRVAL(z_tmp); - mem_len = Z_STRLEN(z_tmp); - } + zstr = zval_get_string(z_ele); + mem = zstr->val; + mem_len = zstr->len; } } @@ -1848,7 +1835,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, mem, mem_len); // Clean up our temp val if it was used - zval_dtor(&z_tmp); + if (zstr) zend_string_release(zstr); // Clean up prefixed or serialized data @@ -1884,7 +1871,7 @@ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_keys, *z_key, z_tmp; + zval *z_keys, *z_key; HashTable *ht_keys; smart_string cmdstr = {0}; int num_keys, key_len, key_free; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 0fc7809ac3..80ea674b19 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1960,10 +1960,15 @@ public function testZX() { $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); - $this->assertTrue(1 === $this->redis->zAdd('key', array(), 1, 'val1')); // empty options - $this->assertTrue(1 === $this->redis->zAdd('key', array('nx'), 3, 'val3')); // nx option - $this->assertTrue(0 === $this->redis->zAdd('key', array('xx'), 3, 'val3')); // xx option $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters + if (version_compare($this->version, "3.0.2", "lt")) { + $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); + $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + } else { + $this->assertTrue(1 === $this->redis->zAdd('key', array(), 1, 'val1')); // empty options + $this->assertTrue(1 === $this->redis->zAdd('key', array('nx'), 3, 'val3')); // nx option + $this->assertTrue(0 === $this->redis->zAdd('key', array('xx'), 3, 'val3')); // xx option + } $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); From cb4fd619c01636bcd373aaa01fce4f5f9940ef6c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 5 Dec 2016 16:53:24 +0200 Subject: [PATCH 0651/1986] memory leak --- redis.c | 3 --- redis_array_impl.c | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index db3b3c8e8f..2830747ab0 100644 --- a/redis.c +++ b/redis.c @@ -36,9 +36,6 @@ #include "ext/session/php_session.h" #endif -#include -#include - #include "library.h" #define R_SUB_CALLBACK_CLASS_TYPE 1 diff --git a/redis_array_impl.c b/redis_array_impl.c index 2eddc4a08e..142371ae22 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -962,6 +962,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl } zval_dtor(&z_fun_set); zval_dtor(&z_args[0]); + zval_dtor(&z_ret); return 1; } @@ -1178,6 +1179,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool /* callback */ if(z_cb && z_cb_cache) { + ZVAL_NULL(&z_ret); zval_rehash_callback(z_cb, z_cb_cache, hostname, count, &z_ret TSRMLS_CC); zval_dtor(&z_ret); } From 5a35e7edd444f80f9dbd2d3fdd3c26c22b99977e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 5 Dec 2016 23:42:18 +0200 Subject: [PATCH 0652/1986] memory leak --- redis_array_impl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis_array_impl.c b/redis_array_impl.c index 142371ae22..1b14089f63 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -1159,6 +1159,9 @@ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z /* cleanup */ zval_dtor(&z_args[0]); +#if (PHP_MAJOR_VERSION < 7) + zval_ptr_dtor(&z_ret); +#endif } static void From 0a886fef224a6e8880c4eb5706550d2a36af1fe9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 6 Dec 2016 11:08:14 +0200 Subject: [PATCH 0653/1986] TravisCI: make clang happy with older php versions --- .travis.yml | 4 ++++ redis_array.c | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index f838abb038..4faf42801b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ php: env: CC=gcc matrix: include: + - env: CC=clang + php: 5.4 + - env: CC=clang + php: 5.5 - env: CC=clang php: 5.6 - env: CC=clang diff --git a/redis_array.c b/redis_array.c index 11bef6ca63..bf6d1a9601 100644 --- a/redis_array.c +++ b/redis_array.c @@ -853,7 +853,7 @@ PHP_METHOD(RedisArray, select) /* MGET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mget) { - zval *object, *z_keys, z_fun, z_argarray, *data, z_ret, *z_cur, z_tmp_array, *z_tmp; + zval *object, *z_keys, z_argarray, *data, z_ret, *z_cur, z_tmp_array, *z_tmp; int i, j, n; RedisArray *ra; int *pos, argc, *argc_each; @@ -872,8 +872,6 @@ PHP_METHOD(RedisArray, mget) RETURN_FALSE; } - /* prepare call */ - ZVAL_STRINGL(&z_fun, "MGET", 4); /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); @@ -940,8 +938,12 @@ PHP_METHOD(RedisArray, mget) add_next_index_zval(&z_argarray, z_tmp); } + zval z_fun; + /* prepare call */ + ZVAL_STRINGL(&z_fun, "MGET", 4); /* call MGET on the node */ call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + zval_dtor(&z_fun); /* cleanup args array */ zval_dtor(&z_argarray); @@ -951,7 +953,6 @@ PHP_METHOD(RedisArray, mget) /* cleanup */ zval_dtor(&z_ret); zval_dtor(&z_tmp_array); - zval_dtor(&z_fun); efree(argv); efree(pos); efree(argc_each); @@ -994,7 +995,6 @@ PHP_METHOD(RedisArray, mget) /* cleanup */ zval_dtor(&z_tmp_array); - zval_dtor(&z_fun); efree(argv); efree(pos); efree(argc_each); @@ -1004,7 +1004,7 @@ PHP_METHOD(RedisArray, mget) /* MSET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mset) { - zval *object, *z_keys, z_fun, z_argarray, *data, z_ret, **argv; + zval *object, *z_keys, z_argarray, *data, z_ret, **argv; int i, n; RedisArray *ra; int *pos, argc, *argc_each; @@ -1060,8 +1060,6 @@ PHP_METHOD(RedisArray, mset) } ZEND_HASH_FOREACH_END(); - /* prepare call */ - ZVAL_STRINGL(&z_fun, "MSET", 4); /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ /* We don't even need to make a call to this node if no keys go there */ @@ -1092,8 +1090,12 @@ PHP_METHOD(RedisArray, mset) ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); } + zval z_fun; + /* prepare call */ + ZVAL_STRINGL(&z_fun, "MSET", 4); /* call */ call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + zval_dtor(&z_fun); zval_dtor(&z_ret); if(ra->index) { @@ -1102,7 +1104,6 @@ PHP_METHOD(RedisArray, mset) } zval_dtor(&z_argarray); } - zval_dtor(&z_fun); /* Free any keys that we needed to allocate memory for, because they weren't strings */ for(i = 0; i < argc; i++) { From 4377c97ee0adb55b33015681598b6198cc0b232e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 6 Dec 2016 14:19:06 +0200 Subject: [PATCH 0654/1986] Issue #1006 --- redis_cluster.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 1a5e1898c7..9d8955f4ce 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2994,13 +2994,13 @@ PHP_METHOD(RedisCluster, rawcommand) { short slot; /* Sanity check on our arguments */ - z_args = emalloc(argc * sizeof(zval)); if (argc < 2) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must pass at least node information as well as at least a command."); - efree(z_args); RETURN_FALSE; - } else if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + } + z_args = emalloc(argc * sizeof(zval)); + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal PHP error parsing method parameters."); efree(z_args); @@ -3022,7 +3022,6 @@ PHP_METHOD(RedisCluster, rawcommand) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send command to the specified node", 0 TSRMLS_CC); efree(cmd); - efree(z_args); RETURN_FALSE; } From 37d8bfde060247e695e6c2bd04bc12e8a6efc094 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 13 Dec 2016 15:47:35 +0200 Subject: [PATCH 0655/1986] refactoring --- redis.c | 42 ++++++++++++++++++------------------------ redis_array.c | 32 +++++++++++++++----------------- 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/redis.c b/redis.c index 2830747ab0..2b27b5faf1 100644 --- a/redis.c +++ b/redis.c @@ -432,35 +432,29 @@ static zend_always_inline int redis_sock_get_instance(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) { zval *socket; + int resource_type = 0; - if (Z_TYPE_P(id) != IS_OBJECT || (socket = zend_hash_str_find(Z_OBJPROP_P(id), - "socket", sizeof("socket") - 1)) == NULL) { - // Throw an exception unless we've been requested not to - if(!no_throw) { - zend_throw_exception(redis_exception_ce, "Redis server went away", - 0 TSRMLS_CC); - } - return -1; - } - + if (Z_TYPE_P(id) == IS_OBJECT && + (socket = zend_hash_str_find(Z_OBJPROP_P(id), "socket", sizeof("socket") - 1)) != NULL + ) { #if (PHP_MAJOR_VERSION < 7) - int resource_type; - *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_P(socket), &resource_type); - if (!*redis_sock || resource_type != le_redis_sock) { + *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_P(socket), &resource_type); #else - if (Z_RES_P(socket) == NULL || - !(*redis_sock = (RedisSock *)Z_RES_P(socket)->ptr) || - Z_RES_P(socket)->type != le_redis_sock - ) { + *redis_sock = NULL; + if (Z_RES_P(socket) != NULL) { + *redis_sock = (RedisSock *)Z_RES_P(socket)->ptr; + resource_type = Z_RES_P(socket)->type; + } #endif - // Throw an exception unless we've been requested not to - if(!no_throw) { - zend_throw_exception(redis_exception_ce, "Redis server went away", - 0 TSRMLS_CC); + if (*redis_sock && resource_type == le_redis_sock) { + return 0; } - return -1; } - return 0; + // Throw an exception unless we've been requested not to + if (!no_throw) { + zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); + } + return -1; } /** @@ -781,7 +775,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { } /* if there is a redis sock already we have to remove it from the list */ - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) > 0) { + if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) >= 0) { if ((socket = zend_hash_str_find(Z_OBJPROP_P(object), "socket", sizeof("socket") - 1)) == NULL) { /* maybe there is a socket but the id isn't known.. what to do? */ diff --git a/redis_array.c b/redis_array.c index bf6d1a9601..fad1828456 100644 --- a/redis_array.c +++ b/redis_array.c @@ -329,14 +329,10 @@ PHP_METHOD(RedisArray, __construct) static void ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, int cmd_len, zval *z_args, zval *z_new_target) { - zval *zp_tmp, z_tmp; + zval z_tmp, z_fun, *redis_inst, *z_callargs, *zp_tmp; char *key = NULL; /* set to avoid "unused-but-set-variable" */ - int i, key_len = 0; - zval *redis_inst; - zval z_fun, *z_callargs; + int i, key_len = 0, argc; HashTable *h_args; - - int argc; zend_bool b_write_cmd = 0; h_args = Z_ARRVAL_P(z_args); @@ -400,19 +396,21 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i } else { /* call directly through. */ call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); - /* check if we have an error. */ - if(RA_CALL_FAILED(return_value,cmd) && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */ - /* Free previous return value */ - zval_dtor(return_value); + if (!b_write_cmd) { + /* check if we have an error. */ + if (ra->prev && RA_CALL_FAILED(return_value, cmd)) { /* there was an error reading, try with prev ring. */ + /* Free previous return value */ + zval_dtor(return_value); - /* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */ - ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra->prev, cmd, cmd_len, z_args, z_new_target?z_new_target:redis_inst); - } + /* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */ + ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra->prev, cmd, cmd_len, z_args, z_new_target ? z_new_target : redis_inst); + } - /* Autorehash if the key was found on the previous node if this is a read command and auto rehashing is on */ - if(!RA_CALL_FAILED(return_value,cmd) && !b_write_cmd && z_new_target && ra->auto_rehash) { /* move key from old ring to new ring */ - ra_move_key(key, key_len, redis_inst, z_new_target TSRMLS_CC); - } + /* Autorehash if the key was found on the previous node if this is a read command and auto rehashing is on */ + if (ra->auto_rehash && z_new_target && !RA_CALL_FAILED(return_value, cmd)) { /* move key from old ring to new ring */ + ra_move_key(key, key_len, redis_inst, z_new_target TSRMLS_CC); + } + } } /* cleanup */ From 92a3a52942d770697d0891cc14e24d534a4ab9f4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 14 Dec 2016 23:06:31 +0200 Subject: [PATCH 0656/1986] release 3.1.0 --- package.xml | 88 +++++++++++++++++++++++++++++++---------------------- php_redis.h | 2 +- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/package.xml b/package.xml index 921703fc49..4d41fed711 100644 --- a/package.xml +++ b/package.xml @@ -21,10 +21,16 @@ http://pear.php.net/dtd/package-2.0.xsd"> michael.grunder@gmail.com yes - 2016-06-02 + + Pavlo Yatsukhnenko + yatsukhnenko + p.yatsukhnenko@gmail.com + yes + + 2016-12-14 - 2.2.8 - 2.2.8 + 3.1.0 + 3.1.0 stable @@ -32,45 +38,26 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - phpredis 2.2.8 - - The main improvement in this version of phpredis is support for Redis - Cluster. This version of phpredis is intended for versions of php older - than 7. - - In addition there have been many bug fixes and improvements to non cluster - related commands, which are listed below. - - I've attempted to include everyone who contribued to the project in each fix - description and have included names or github user ids. - - Thanks to everyone for submitting bug reports and pull requests. A special - thanks to Remi Collet for helping with any and all packaging related issues + phpredis 3.1.0 - \o/ + In this version of phpredis codebase was unified to work with all versions of php \o/ + Also many bug fixes and some improvements has been made. --- Improvements --- - * Added randomization to our seed nodes to balance which instance is used - to map the keyspace (Vitaliy Stepanyuk) [32eb1c5f] - * Added support for IPv6 addresses + * Support the client to Redis Cluster just having one master (andyli) [892e5646] + * Allow both long and strings that are longs for zrangebyscore offset/limit (Michael Grunder) [bdcdd2aa] + * Process NX|XX, CH and INCR options in zAdd command (Pavlo Yatsukhnenko) [71c9f7c8] --- Fixes --- - * PHP liveness checking workaround (Shafreeck Sea) [c18d58b9] - * Various documentation and code formatting and style fixes (ares333, - sanpili, Bryan Nelson, linfangrong, Romero Malaquias, Viktor Szépe) - * Fix scan reply processing to use long instead of int to avoid overflow - (mixiaojiong). - * Fix potential segfault in Redis Cluster session storage (Sergei Lomakov) - [cc15aae] - * Fixed memory leak in discard function [17b1f427] - * Sanity check for igbinary unserialization (Maurus Cuelenaere) [3266b222, - 5528297a] - * Fix segfault occuring from unclosed socket connection for Redis Cluster - (CatKang) [04196aee] - * Case insensitive zRangeByScore options - * Fixed dreaded size_t vs long long compiler warning + * Fix incrby/decrby for large integers (Michael Grunder) [3a12758a] + * Use static declarations for spl_ce_RuntimeException decl (Jeremy Mikola) [a9857d69] + * Fixed method call problem causes session handler to display two times (ZiHang Gao) [24f86c49] + * psetex method returns '+OK' on success, not true (sitri@ndxbn) [afcd8445] + * Fix integer overflow for long (>32bit) increments in hIncrBy (iyesin) [58e1d799] + * Move zend_object handler to the end (Michael Grunder) [34107966] + * Using setOption on redis array causes immediate connection (Pavlo Yatsukhnenko) [f1a85b38] @@ -114,8 +101,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> 5.2.0 - 6.0.0 - 6.0.0 + 7.1.0 1.4.0b1 @@ -125,6 +111,34 @@ http://pear.php.net/dtd/package-2.0.xsd"> redis + + stablestable + 3.1.03.1.0 + 2016-12-14 + + phpredis 3.1.0 + + In this version of phpredis codebase was unified to work with all versions of php \o/ + Also many bug fixes and some improvements has been made. + + --- Improvements --- + + * Support the client to Redis Cluster just having one master (andyli) [892e5646] + * Allow both long and strings that are longs for zrangebyscore offset/limit (Michael Grunder) [bdcdd2aa] + * Process NX|XX, CH and INCR options in zAdd command (Pavlo Yatsukhnenko) [71c9f7c8] + + --- Fixes --- + + * Fix incrby/decrby for large integers (Michael Grunder) [3a12758a] + * Use static declarations for spl_ce_RuntimeException decl (Jeremy Mikola) [a9857d69] + * Fixed method call problem causes session handler to display two times (ZiHang Gao) [24f86c49] + * psetex method returns '+OK' on success, not true (sitri@ndxbn) [afcd8445] + * Fix integer overflow for long (>32bit) increments in hIncrBy (iyesin) [58e1d799] + * Move zend_object handler to the end (Michael Grunder) [34107966] + * Using setOption on redis array causes immediate connection (Pavlo Yatsukhnenko) [f1a85b38] + + + stablestable 2.2.82.2.8 diff --git a/php_redis.h b/php_redis.h index 01e71ed0cc..c0a0015258 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "3.1.0-rc1" +#define PHP_REDIS_VERSION "3.1.0" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 61c02e2985494b3de6ebbbd25f622085ca4b1103 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 15 Dec 2016 15:08:17 +0200 Subject: [PATCH 0657/1986] Fix segfault in testSerializerIGBinary --- library.c | 14 ++++++++------ redis.c | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/library.c b/library.c index a48fc41acb..4182d24ec8 100644 --- a/library.c +++ b/library.c @@ -2087,14 +2087,16 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, { /* This is most definitely not an igbinary string, so do not try to unserialize this as one. */ - return 0; + break; } - if(igbinary_unserialize((const uint8_t *)val, (size_t)val_len, - z_ret TSRMLS_CC) == 0 - ) { - ret = 1; - } +#if (PHP_MAJOR_VERSION < 7) + INIT_PZVAL(z_ret); + ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, &z_ret TSRMLS_CC); +#else + ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, z_ret TSRMLS_CC); +#endif + #endif break; } diff --git a/redis.c b/redis.c index 2b27b5faf1..25297d62dd 100644 --- a/redis.c +++ b/redis.c @@ -527,6 +527,9 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) /* serializer */ zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP TSRMLS_CC); +#ifdef HAVE_REDIS_IGBINARY + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); +#endif /* scan options*/ zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN TSRMLS_CC); @@ -541,9 +544,6 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES TSRMLS_CC); } -#ifdef HAVE_REDIS_IGBINARY - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); -#endif zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5 TSRMLS_CC); zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); From 43e5bf325adb0ea7cfc2f029c874da0b557147e7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 15 Dec 2016 23:12:44 +0200 Subject: [PATCH 0658/1986] TravisCI: igbinary --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4faf42801b..7052747e45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,8 +28,10 @@ matrix: addons: apt: packages: clang -before_install: phpize -install: ./configure CFLAGS=-Wall --prefix=/usr && sudo make install +before_install: + - phpize + - if [ $(phpenv version-name) lt 7 ]; then pecl install igbinary-1.2.1; ./configure --enable-redis-igbinary CFLAGS=-Wall; else ./configure CFLAGS=-Wall; fi +install: make install before_script: - gem install redis - mkdir -p tests/nodes/ && echo >> tests/nodes/nodemap From 18c7ab007649f068075880984e59938b849b5ab5 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 16 Dec 2016 15:29:05 +0100 Subject: [PATCH 0659/1986] fix #1062 restore 3.0.0 session behavior for PHP 7.1 --- redis_session.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/redis_session.c b/redis_session.c index 6faae20e6b..2ac287223c 100644 --- a/redis_session.c +++ b/redis_session.c @@ -360,7 +360,7 @@ PS_READ_FUNC(redis) resp = redis_session_key(rpm, key->val, key->len, &resp_len); #endif cmd_len = redis_cmd_format_static(&cmd, "GET", "s", resp, resp_len); - + efree(resp); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -368,15 +368,26 @@ PS_READ_FUNC(redis) } efree(cmd); - /* read response */ - if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { + /* Read response from Redis. If we get a NULL response from redis_sock_read + * this can indicate an error, OR a "NULL bulk" reply (empty session data) + * in which case we can reply with success. */ + if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL && resp_len != -1) { return FAILURE; } #if (PHP_MAJOR_VERSION < 7) - *val = resp; - *vallen = resp_len; + if (resp_len < 0) { + *val = STR_EMPTY_ALLOC(); + *vallen = 0; + } else { + *val = resp; + *vallen = resp_len; + } #else - *val = zend_string_init(resp, resp_len, 0); + if (resp_len < 0) { + *val = ZSTR_EMPTY_ALLOC(); + } else { + *val = zend_string_init(resp, resp_len, 0); + } efree(resp); #endif From 70c4d9bac261ca2101b1213e85a6d262499e19c9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 17 Dec 2016 10:35:09 +0200 Subject: [PATCH 0660/1986] Fix broken REDIS_DOUBLE_TO_STRING implementation --- library.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/library.h b/library.h index 8eb67083c2..9f9b6b9433 100644 --- a/library.h +++ b/library.h @@ -88,19 +88,17 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo #if ZEND_MODULE_API_NO >= 20100000 #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl) do { \ char dbl_decsep = '.'; \ - zend_string _zstr = {0}; \ - _zstr.val = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \ - _zstr.len = strlen(_zstr.val); \ - _zstr.gc = 0x10; \ - dbl_str = &_zstr; \ + dbl_str = emalloc(sizeof(zend_string)); \ + dbl_str->val = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \ + dbl_str->len = strlen(dbl_str->val); \ + dbl_str->gc = 0x11; \ } while (0); #else #define REDIS_DOUBLE_TO_STRING(dbl_str, dbl) do { \ - zend_string _zstr = {0}; \ - _zstr.val = _php_math_number_format(dbl, 16, '.', '\x00'); \ - _zstr.len = strlen(_zstr.val); \ - _zstr.gc = 0x10; \ - dbl_str = &_zstr; \ + dbl_str = emalloc(sizeof(zend_string)); \ + dbl_str->val = _php_math_number_format(dbl, 16, '.', '\x00'); \ + dbl_str->len = strlen(dbl_str->val); \ + dbl_str->gc = 0x11; \ } while (0) #endif #else From 81d2cba5575b6a5705cf851251738f542c135594 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 17 Dec 2016 22:51:04 +0200 Subject: [PATCH 0661/1986] TravisCI: session --- tests/RedisClusterTest.php | 11 +++++++++++ tests/RedisTest.php | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index ffe1723047..2fa79e673a 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -506,5 +506,16 @@ protected function rawCommandArray($key, $args) { array_unshift($args, $key); return call_user_func_array(Array($this->redis, 'rawCommand'), $args); } + + public function testSession() + { + ini_set('session.save_handler', 'rediscluster'); + ini_set('session.save_path', implode('&', array_map(function ($seed) { + return 'seed[]=' . $seed; + }, self::$_arr_node_map)) . '&failover=error'); + @session_start(); + session_write_close(); + $this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id())); + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 80ea674b19..b8ccbb9169 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4960,5 +4960,14 @@ public function testRawCommand() { $this->redis->rpush('mylist', 'A', 'B', 'C', 'D'); $this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D')); } + + public function testSession() + { + ini_set('session.save_handler', 'redis'); + ini_set('session.save_path', 'tcp://localhost:6379'); + @session_start(); + session_write_close(); + $this->assertTrue($this->redis->exists('PHPREDIS_SESSION:' . session_id())); + } } ?> From a90bac2cffe50722b376b4b79697c2cb4fb468a9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 18 Dec 2016 23:07:36 +0200 Subject: [PATCH 0662/1986] TravisCI: fix session test --- library.c | 1 + tests/RedisClusterTest.php | 4 +++- tests/RedisTest.php | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 4182d24ec8..7bb0b263cf 100644 --- a/library.c +++ b/library.c @@ -483,6 +483,7 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D char inbuf[1024]; size_t err_len; + *buf_len = 0; if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return NULL; } diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 2fa79e673a..313d8c750a 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -513,7 +513,9 @@ public function testSession() ini_set('session.save_path', implode('&', array_map(function ($seed) { return 'seed[]=' . $seed; }, self::$_arr_node_map)) . '&failover=error'); - @session_start(); + if (!@session_start()) { + return $this->markTestSkipped(); + } session_write_close(); $this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id())); } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index b8ccbb9169..8c99b04677 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4965,7 +4965,9 @@ public function testSession() { ini_set('session.save_handler', 'redis'); ini_set('session.save_path', 'tcp://localhost:6379'); - @session_start(); + if (!@session_start()) { + return $this->markTestSkipped(); + } session_write_close(); $this->assertTrue($this->redis->exists('PHPREDIS_SESSION:' . session_id())); } From d0b8011a9adcf2d0af2d7cead42c043b0d267d12 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 18 Dec 2016 21:06:38 -0800 Subject: [PATCH 0663/1986] Fix compiler warnings. Changed zend hash function overrides to extern in the header, and then defined them in library.c Created a simple macro to suppress "set but not used" warnings, which differ depending on build environment (php 5 vs php 7). --- cluster_library.c | 10 +++++----- common.h | 23 +++++++++++++---------- library.c | 15 ++++++++++++++- redis.c | 5 +++-- redis_array_impl.c | 2 ++ redis_commands.c | 8 +++++++- 6 files changed, 44 insertions(+), 19 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index d85c654f73..9ea41676ac 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1678,7 +1678,7 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * /* Multibulk response, {[pattern], type, channel, payload} */ while(1) { /* Arguments */ - zval *z_type, *z_chan, *z_pat, *z_data; + zval *z_type, *z_chan, *z_pat=NULL, *z_data; int tab_idx=1, is_pmsg; // Get the next subscribe response @@ -2358,8 +2358,8 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC) { - char *line, *key; - int line_len, key_len; + char *line, *key = NULL; + int line_len, key_len=0; long long idx=0; // Our count wil need to be divisible by 2 @@ -2401,8 +2401,8 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC) { - char *line, *key; - int line_len, key_len; + char *line, *key=NULL; + int line_len, key_len=0; long long idx=0; // Our context will need to be divisible by 2 diff --git a/common.h b/common.h index f828f411ce..6e6a0b5a74 100644 --- a/common.h +++ b/common.h @@ -4,6 +4,8 @@ #ifndef REDIS_COMMON_H #define REDIS_COMMON_H +#define PHPREDIS_NOTUSED(v) ((void)v) + #include #include #if (PHP_MAJOR_VERSION < 7) @@ -20,6 +22,7 @@ typedef struct { char *val; } zend_string; + #define zend_string_release(s) do { \ if ((s) && (s)->gc) { \ if ((s)->gc & 0x10 && (s)->val) efree((s)->val); \ @@ -201,9 +204,9 @@ inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array T typedef zend_rsrc_list_entry zend_resource; -static int (*_add_next_index_string)(zval *, const char *, int) = &add_next_index_string; +extern int (*_add_next_index_string)(zval *, const char *, int); #define add_next_index_string(arg, str) _add_next_index_string(arg, str, 1); -static int (*_add_next_index_stringl)(zval *, const char *, uint, int) = &add_next_index_stringl; +extern int (*_add_next_index_stringl)(zval *, const char *, uint, int); #define add_next_index_stringl(arg, str, length) _add_next_index_stringl(arg, str, length, 1); #undef ZVAL_STRING @@ -252,30 +255,30 @@ inline_call_user_function(HashTable *function_table, zval *object, zval *functio #undef add_assoc_bool #define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key), __b) -static int (*_add_assoc_bool_ex)(zval *, const char *, uint, int) = &add_assoc_bool_ex; +extern int (*_add_assoc_bool_ex)(zval *, const char *, uint, int); #define add_assoc_bool_ex(_arg, _key, _key_len, _b) _add_assoc_bool_ex(_arg, _key, _key_len + 1, _b) #undef add_assoc_long #define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key), __n) -static int (*_add_assoc_long_ex)(zval *, const char *, uint, long) = &add_assoc_long_ex; +extern int (*_add_assoc_long_ex)(zval *, const char *, uint, long); #define add_assoc_long_ex(_arg, _key, _key_len, _n) _add_assoc_long_ex(_arg, _key, _key_len + 1, _n) #undef add_assoc_double #define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key), __d) -static int (*_add_assoc_double_ex)(zval *, const char *, uint, double) = &add_assoc_double_ex; +extern int (*_add_assoc_double_ex)(zval *, const char *, uint, double); #define add_assoc_double_ex(_arg, _key, _key_len, _d) _add_assoc_double_ex(_arg, _key, _key_len + 1, _d) #undef add_assoc_string #define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str) -static int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int) = &add_assoc_string_ex; +extern int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int); #define add_assoc_string_ex(_arg, _key, _key_len, _str) _add_assoc_string_ex(_arg, _key, _key_len + 1, _str, 1) -static int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int) = &add_assoc_stringl_ex; +extern int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int); #define add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length) _add_assoc_stringl_ex(_arg, _key, _key_len + 1, _str, _length, 1) #undef add_assoc_zval #define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key), __value) -static int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *) = &add_assoc_zval_ex; +extern int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *); #define add_assoc_zval_ex(_arg, _key, _key_len, _value) _add_assoc_zval_ex(_arg, _key, _key_len + 1, _value); typedef long zend_long; @@ -355,9 +358,9 @@ zval_get_string(zval *op) return zstr; } -static void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC) = &php_var_serialize; +extern void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC); #define php_var_serialize(buf, struc, data) _php_var_serialize(buf, &struc, data TSRMLS_CC) -static int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; +extern int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC); #define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) typedef int strlen_t; #else diff --git a/library.c b/library.c index 4182d24ec8..8f5a305a3d 100644 --- a/library.c +++ b/library.c @@ -33,6 +33,19 @@ # endif #endif +#if (PHP_MAJOR_VERSION < 7) + int (*_add_next_index_string)(zval *, const char *, int) = &add_next_index_string; + int (*_add_next_index_stringl)(zval *, const char *, uint, int) = &add_next_index_stringl; + int (*_add_assoc_bool_ex)(zval *, const char *, uint, int) = &add_assoc_bool_ex; + int (*_add_assoc_long_ex)(zval *, const char *, uint, long) = &add_assoc_long_ex; + int (*_add_assoc_double_ex)(zval *, const char *, uint, double) = &add_assoc_double_ex; + int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int) = &add_assoc_string_ex; + int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int) = &add_assoc_stringl_ex; + int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *) = &add_assoc_zval_ex; + void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC) = &php_var_serialize; + int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; +#endif + extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; @@ -297,7 +310,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, /* Multibulk response, {[pattern], type, channel, payload } */ while(1) { - zval *z_type, *z_chan, *z_pat, *z_data; + zval *z_type, *z_chan, *z_pat = NULL, *z_data; HashTable *ht_tab; int tab_idx=1, is_pmsg; diff --git a/redis.c b/redis.c index 25297d62dd..e802ff242b 100644 --- a/redis.c +++ b/redis.c @@ -441,6 +441,7 @@ redis_sock_get_instance(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_P(socket), &resource_type); #else *redis_sock = NULL; + if (Z_RES_P(socket) != NULL) { *redis_sock = (RedisSock *)Z_RES_P(socket)->ptr; resource_type = Z_RES_P(socket)->type; @@ -2655,7 +2656,7 @@ PHP_METHOD(Redis, object) /* {{{ proto string Redis::getOption($option) */ PHP_METHOD(Redis, getOption) { - RedisSock *redis_sock; + RedisSock *redis_sock = NULL; if (redis_sock_get_instance(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; @@ -2668,7 +2669,7 @@ PHP_METHOD(Redis, getOption) { /* {{{ proto string Redis::setOption(string $option, mixed $value) */ PHP_METHOD(Redis, setOption) { - RedisSock *redis_sock; + RedisSock *redis_sock = NULL; if (redis_sock_get_instance(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { RETURN_FALSE; diff --git a/redis_array_impl.c b/redis_array_impl.c index 1b14089f63..177368ae0e 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -586,6 +586,8 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { zval zv, *z_new = &zv; #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_new); +#else + PHPREDIS_NOTUSED(z_val); #endif if (zkey) { diff --git a/redis_commands.c b/redis_commands.c index ec4686d626..6dc4528e62 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -529,6 +529,8 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ulong idx; HashTable *ht_opt; + PHPREDIS_NOTUSED(idx); + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) ==FAILURE) @@ -1051,7 +1053,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_free, key_len, i, tail; int single_array = 0, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; - long timeout; + long timeout = 0; short kslot = -1; zend_string *zstr; @@ -1211,6 +1213,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ulong idx; zval *v; + PHPREDIS_NOTUSED(idx); + /* Iterate our option array */ ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, v) { /* Detect PX or EX argument and validate timeout */ @@ -2734,6 +2738,8 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, zend_string *zkey; zval *optval; + PHPREDIS_NOTUSED(idx); + /* Iterate over our argument array, collating which ones we have */ ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, optval) { /* If the key is numeric it's a non value option */ From 3baccddd11e4c8526f46884d73c44a63b02ecf6f Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 19 Dec 2016 20:28:22 +0100 Subject: [PATCH 0664/1986] add module dependency on igbinary --- redis.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 25297d62dd..2ba13c14ed 100644 --- a/redis.c +++ b/redis.c @@ -346,9 +346,18 @@ static zend_function_entry redis_functions[] = { {NULL, NULL, NULL} }; +static const zend_module_dep redis_deps[] = { +#ifdef HAVE_REDIS_IGBINARY + ZEND_MOD_REQUIRED("igbinary") +#endif + ZEND_MOD_END +}; + zend_module_entry redis_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 - STANDARD_MODULE_HEADER, + STANDARD_MODULE_HEADER_EX, + NULL, + redis_deps, #endif "redis", NULL, From 5625b99819f134b5ad1e6dbf406c9276c20c88a2 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 19 Dec 2016 20:28:41 +0100 Subject: [PATCH 0665/1986] add serializers info in phpinfo --- redis.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/redis.c b/redis.c index 2ba13c14ed..c04c0685f2 100644 --- a/redis.c +++ b/redis.c @@ -675,6 +675,11 @@ PHP_MINFO_FUNCTION(redis) php_info_print_table_start(); php_info_print_table_header(2, "Redis Support", "enabled"); php_info_print_table_row(2, "Redis Version", PHP_REDIS_VERSION); +#ifdef HAVE_REDIS_IGBINARY + php_info_print_table_row(2, "Available serializers", "php, igbinary"); +#else + php_info_print_table_row(2, "Available serializers", "php"); +#endif php_info_print_table_end(); } From e0273773c814ea4c9f0b056419e730579a07f382 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 19 Dec 2016 20:29:03 +0100 Subject: [PATCH 0666/1986] use PHP_FE_END when available --- redis.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/redis.c b/redis.c index c04c0685f2..a36c632a8f 100644 --- a/redis.c +++ b/redis.c @@ -343,7 +343,12 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC) +/* PHP_FE_END exists since 5.3.7 */ +#ifdef PHP_FE_END + PHP_FE_END +#else {NULL, NULL, NULL} +#endif }; static const zend_module_dep redis_deps[] = { From 721c272995273699eabb54e1eee19570fd181c47 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 19 Dec 2016 11:46:19 -0800 Subject: [PATCH 0667/1986] Use PHP_FE_END when available for RedisArray and RedisCluster --- redis_array.c | 5 +++++ redis_cluster.c | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/redis_array.c b/redis_array.c index fad1828456..88589c8841 100644 --- a/redis_array.c +++ b/redis_array.c @@ -89,7 +89,12 @@ zend_function_entry redis_array_functions[] = { /* Aliases */ PHP_MALIAS(RedisArray, delete, del, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(RedisArray, getMultiple, mget, NULL, ZEND_ACC_PUBLIC) +/* PHP_FE_END exists since 5.3.7 */ +#ifdef PHP_FE_END + PHP_FE_END +#else {NULL, NULL, NULL} +#endif }; static void redis_array_free(RedisArray *ra) { diff --git a/redis_cluster.c b/redis_cluster.c index 9d8955f4ce..c9a82c50ff 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -227,7 +227,12 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, geodist, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, georadius, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, georadiusbymember, NULL, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} +/* PHP_FE_END exists since 5.3.7 */ +#ifdef PHP_FE_END + PHP_FE_END +#else + {NULL, NULL, NULL} +#endif }; /* Our context seeds will be a hash table with RedisSock* pointers */ From 9deafa1a382f770e98830daa0f88e19e583d0993 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 19 Dec 2016 12:05:55 -0800 Subject: [PATCH 0668/1986] Allow higher max php version --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index 4d41fed711..1c7f63eea7 100644 --- a/package.xml +++ b/package.xml @@ -101,7 +101,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> 5.2.0 - 7.1.0 + 7.1.99 1.4.0b1 From f1d914e7f89075627b3e893f22d3ef4bf865b8ac Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 19 Dec 2016 14:01:08 -0800 Subject: [PATCH 0669/1986] Even higher max version. --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index 1c7f63eea7..db039594e3 100644 --- a/package.xml +++ b/package.xml @@ -101,7 +101,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> 5.2.0 - 7.1.99 + 7.9.99 1.4.0b1 From 2b91d5823920889f2ff5f64d15a353a8dafe4adf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 19 Dec 2016 14:14:41 -0800 Subject: [PATCH 0670/1986] OCD spacing FTW --- cluster_library.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 9ea41676ac..ba3875cdf1 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1678,7 +1678,7 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * /* Multibulk response, {[pattern], type, channel, payload} */ while(1) { /* Arguments */ - zval *z_type, *z_chan, *z_pat=NULL, *z_data; + zval *z_type, *z_chan, *z_pat = NULL, *z_data; int tab_idx=1, is_pmsg; // Get the next subscribe response @@ -2359,8 +2359,8 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC) { char *line, *key = NULL; - int line_len, key_len=0; - long long idx=0; + int line_len, key_len = 0; + long long idx = 0; // Our count wil need to be divisible by 2 if(count % 2 != 0) { @@ -2401,9 +2401,9 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC) { - char *line, *key=NULL; - int line_len, key_len=0; - long long idx=0; + char *line, *key = NULL; + int line_len, key_len = 0; + long long idx = 0; // Our context will need to be divisible by 2 if(count %2 != 0) { From 5ee32baee60d2ed64a723fd20c3b7943732a33ad Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 20 Dec 2016 10:55:56 +0200 Subject: [PATCH 0671/1986] PHP_FE_END + ZEND_MOD_END --- common.h | 11 +++++++++++ redis.c | 5 ----- redis_array.c | 5 ----- redis_cluster.c | 7 +------ 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/common.h b/common.h index 6e6a0b5a74..86162c0e0d 100644 --- a/common.h +++ b/common.h @@ -363,6 +363,17 @@ extern void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * T extern int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC); #define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) typedef int strlen_t; + +/* PHP_MOD_END exists since 5.3.7 */ +#ifndef ZEND_MOD_END +#define ZEND_MOD_END { NULL, NULL, NULL, 0 } +#endif + +/* PHP_FE_END exists since 5.3.7 */ +#ifndef PHP_FE_END +#define PHP_FE_END { NULL, NULL, NULL } +#endif + #else #include #include diff --git a/redis.c b/redis.c index bf2af26075..dbdd55b57e 100644 --- a/redis.c +++ b/redis.c @@ -343,12 +343,7 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC) -/* PHP_FE_END exists since 5.3.7 */ -#ifdef PHP_FE_END PHP_FE_END -#else - {NULL, NULL, NULL} -#endif }; static const zend_module_dep redis_deps[] = { diff --git a/redis_array.c b/redis_array.c index 88589c8841..8d8a296108 100644 --- a/redis_array.c +++ b/redis_array.c @@ -89,12 +89,7 @@ zend_function_entry redis_array_functions[] = { /* Aliases */ PHP_MALIAS(RedisArray, delete, del, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(RedisArray, getMultiple, mget, NULL, ZEND_ACC_PUBLIC) -/* PHP_FE_END exists since 5.3.7 */ -#ifdef PHP_FE_END PHP_FE_END -#else - {NULL, NULL, NULL} -#endif }; static void redis_array_free(RedisArray *ra) { diff --git a/redis_cluster.c b/redis_cluster.c index c9a82c50ff..ccf326189c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -227,12 +227,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, geodist, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, georadius, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, georadiusbymember, NULL, ZEND_ACC_PUBLIC) -/* PHP_FE_END exists since 5.3.7 */ -#ifdef PHP_FE_END - PHP_FE_END -#else - {NULL, NULL, NULL} -#endif + PHP_FE_END }; /* Our context seeds will be a hash table with RedisSock* pointers */ From 1fb6a24556abed23fcdf68f35c511339aebfb3ec Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 20 Dec 2016 16:26:21 +0200 Subject: [PATCH 0672/1986] fix 'optional var not initialized' warnings --- redis.c | 4 ++-- redis_array.c | 4 ++-- redis_cluster.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/redis.c b/redis.c index dbdd55b57e..61b66699be 100644 --- a/redis.c +++ b/redis.c @@ -750,7 +750,7 @@ PHP_METHOD(Redis, pconnect) PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zval *object, *socket; char *host = NULL, *persistent_id = NULL; - long port = -1, retry_interval = 0; + zend_long port = -1, retry_interval = 0; strlen_t host_len, persistent_id_len; double timeout = 0.0; RedisSock *redis_sock = NULL; @@ -2758,7 +2758,7 @@ PHP_METHOD(Redis, slowlog) { char *arg, *cmd; int cmd_len; strlen_t arg_len; - zend_long option; + zend_long option = 0; enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; // Make sure we can get parameters diff --git a/redis_array.c b/redis_array.c index 8d8a296108..1d33d92ef2 100644 --- a/redis_array.c +++ b/redis_array.c @@ -551,8 +551,8 @@ PHP_METHOD(RedisArray, _rehash) { zval *object; RedisArray *ra; - zend_fcall_info z_cb; - zend_fcall_info_cache z_cb_cache; + zend_fcall_info z_cb = {0}; + zend_fcall_info_cache z_cb_cache = {0}; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|f", &object, redis_array_ce, &z_cb, &z_cb_cache) == FAILURE) { diff --git a/redis_cluster.c b/redis_cluster.c index ccf326189c..bf9bf1d00c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2714,7 +2714,7 @@ PHP_METHOD(RedisCluster, info) { REDIS_REPLY_TYPE rtype; char *cmd, *opt=NULL; int cmd_len; - strlen_t opt_len; + strlen_t opt_len = 0; void *ctx = NULL; zval *z_arg; short slot; @@ -2766,7 +2766,7 @@ PHP_METHOD(RedisCluster, client) { redisCluster *c = GET_CONTEXT(); char *cmd, *opt=NULL, *arg=NULL; int cmd_len; - strlen_t opt_len, arg_len; + strlen_t opt_len, arg_len = 0; REDIS_REPLY_TYPE rtype; zval *z_node; short slot; From 8ed844e2ea10739cf1b38a3b6a97e7ad604b8387 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 20 Dec 2016 17:29:47 +0200 Subject: [PATCH 0673/1986] fix 'this decimal constant is unsigned only in ISO C90' warning --- common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.h b/common.h index 86162c0e0d..68d753daa2 100644 --- a/common.h +++ b/common.h @@ -457,7 +457,7 @@ typedef enum _PUBSUB_TYPE { /* GETBIT/SETBIT offset range limits */ #define BITOP_MIN_OFFSET 0 -#define BITOP_MAX_OFFSET 4294967295 +#define BITOP_MAX_OFFSET 4294967295U /* Specific error messages we want to throw against */ #define REDIS_ERR_LOADING_MSG "LOADING Redis is loading the dataset in memory" From 04f1a40b98128636a4d5ec51f33f5b97f2a7459a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 20 Dec 2016 23:13:12 +0200 Subject: [PATCH 0674/1986] Update contacts information --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 4eb760f222..e66f58999f 100644 --- a/README.markdown +++ b/README.markdown @@ -5,7 +5,7 @@ The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). This code has been developed and maintained by Owlient from November 2009 to March 2011. -You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)), or to michael.grunder@gmail.com ([@grumi78](http://twitter.com/grumi78)). +You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)) or to p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)). # Table of contents From c9825cc8c8d444446c868837e1a786d123e10fd3 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 21 Dec 2016 09:13:36 +0100 Subject: [PATCH 0675/1986] fix #1074 change redis_key_prefix to accept strlen_t --- cluster_library.c | 2 +- library.c | 2 +- library.h | 2 +- redis.c | 19 ++++---- redis_cluster.c | 13 +++--- redis_commands.c | 112 +++++++++++++++++++++++++--------------------- 6 files changed, 81 insertions(+), 69 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index ba3875cdf1..24f4062e69 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -418,7 +418,7 @@ static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, /* Add a key, returning a pointer to the entry where passed for easy adding * of values to match this key */ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, - int key_len, clusterKeyVal **kv) + strlen_t key_len, clusterKeyVal **kv) { int key_free; short slot; diff --git a/library.c b/library.c index bd064005cc..23391aa74a 100644 --- a/library.c +++ b/library.c @@ -2118,7 +2118,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, } PHP_REDIS_API int -redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len) { +redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len) { int ret_len; char *ret; diff --git a/library.h b/library.h index 9f9b6b9433..64cfb2c569 100644 --- a/library.h +++ b/library.h @@ -67,7 +67,7 @@ PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, in PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC); PHP_REDIS_API int -redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len); +redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len); PHP_REDIS_API int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); diff --git a/redis.c b/redis.c index 61b66699be..71538cc5b0 100644 --- a/redis.c +++ b/redis.c @@ -1022,7 +1022,7 @@ PHP_METHOD(Redis, getMultiple) ZEND_HASH_FOREACH_VAL(hash, z_ele) { zend_string *zstr = zval_get_string(z_ele); char *key = zstr->val; - int key_len = zstr->len; + strlen_t key_len = zstr->len; /* Apply key prefix if necissary */ int key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Append this key to our command */ @@ -1460,7 +1460,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, cmd_sizes[2] = 4; /* Prefix our key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* second line, key */ cmd_sizes[3] = redis_cmd_format(&cmd_lines[3], "$%d", key_len); @@ -1852,7 +1852,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { keytable = Z_ARRVAL_P(z_array); ZEND_HASH_FOREACH_KEY_VAL(keytable, idx, zkey, z_value_p) { char *key, *val; - unsigned int key_len; + strlen_t key_len; int val_len; int val_free, key_free; char buf[32]; @@ -1871,7 +1871,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { val_free = redis_serialize(redis_sock, z_value_p, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, (int*)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); if(step == 0) { /* counting */ cmd_len += 1 + integer_length(key_len) + 2 @@ -2855,7 +2855,8 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, HashTable *ht_chan; zval *z_ele; char *key; - int cmd_len, key_len, key_free; + int cmd_len, key_free; + strlen_t key_len; smart_string cmd = {0}; if(type == PUBSUB_CHANNELS) { @@ -2893,7 +2894,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) { zend_string *zstr = zval_get_string(z_ele); char *key = zstr->val; - int key_len = zstr->len; + strlen_t key_len = zstr->len; /* Apply prefix if required */ key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -3027,7 +3028,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, ZEND_HASH_FOREACH_VAL(args_hash, elem) { zend_string *zstr = zval_get_string(elem); char *key = zstr->val; - int key_len = zstr->len; + strlen_t key_len = zstr->len; /* Keep track of the old command pointer */ char *old_cmd = *ret; @@ -3281,7 +3282,7 @@ PHP_METHOD(Redis, migrate) { } // Prefix our key if we need to, build our command - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Construct our command */ if(copy && replace) { @@ -3756,7 +3757,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Prefix our key if we've got one and we have a prefix set */ if(key_len) { - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); } /** diff --git a/redis_cluster.c b/redis_cluster.c index bf9bf1d00c..fa63ae0dd1 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -585,7 +585,8 @@ typedef struct clusterKeyValHT { char kbuf[22]; char *key; - int key_len, key_free; + strlen_t key_len; + int key_free; short slot; char *val; @@ -1050,7 +1051,7 @@ PHP_METHOD(RedisCluster, keys) { } /* Prefix and then build our command */ - pat_free = redis_key_prefix(c->flags, &pat, (int *)&pat_len); + pat_free = redis_key_prefix(c->flags, &pat, &pat_len); cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pat, pat_len); if(pat_free) efree(pat); @@ -1899,7 +1900,8 @@ static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, { redisClusterNode *node=NULL; char *lua, *key; - int key_free, args_count=0, key_len; + int key_free, args_count=0; + strlen_t key_len; zval *z_arr=NULL, *z_ele; HashTable *ht_arr; zend_long num_keys = 0; @@ -2306,7 +2308,8 @@ PHP_METHOD(RedisCluster, discard) { static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { - int key_len, key_free; + strlen_t key_len; + int key_free; zval *z_host, *z_port; short slot; char *key; @@ -2499,7 +2502,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, } // Apply any key prefix we have, get the slot - key_free = redis_key_prefix(c->flags, &key, (int *)&key_len); + key_free = redis_key_prefix(c->flags, &key, &key_len); slot = cluster_hash_key(key, key_len); // If SCAN_RETRY is set, loop until we get a zero iterator or until diff --git a/redis_commands.c b/redis_commands.c index 6dc4528e62..067e583b49 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -122,7 +122,7 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Serialize value, prefix key val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "sls", key, key_len, expire, @@ -154,7 +154,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key if requested - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sds", key, key_len, (int)lval, @@ -186,7 +186,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, @@ -217,7 +217,7 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, @@ -247,7 +247,7 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, val1, @@ -279,8 +279,8 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix both keys - key1_free = redis_key_prefix(redis_sock, &key1, (int *)&key1_len); - key2_free = redis_key_prefix(redis_sock, &key2, (int *)&key2_len); + key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); + key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); // If a slot is requested, we can test that they hash the same if(slot) { @@ -328,7 +328,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Disallow zero length keys (for now) if(key_len == 0) { @@ -364,7 +364,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sll", key, key_len, val1, @@ -394,7 +394,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "s", key, key_len); @@ -424,7 +424,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct our command *cmd_len = redis_cmd_format_static(cmd, kw, "sf", key, key_len, val); @@ -492,7 +492,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); if(ws) { *cmd_len = redis_cmd_format_static(cmd, kw, "sdds", key, key_len, start, end, "WITHSCORES", sizeof("WITHSCORES")-1); @@ -566,7 +566,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key, set slot - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); CMD_SET_SLOT(slot,key,key_len); // Construct our command @@ -654,7 +654,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Start building our command redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); @@ -669,7 +669,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_keys, z_ele) { zend_string *zstr = zval_get_string(z_ele); char *key = zstr->val; - int key_len = zstr->len; + strlen_t key_len = zstr->len; // Prefix key if necissary int key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -747,7 +747,8 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_chan; smart_string cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); - int key_len, key_free; + strlen_t key_len; + int key_free; char *key; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &z_arr, @@ -825,7 +826,8 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_arr, z_chan) { char *key = Z_STRVAL_P(z_chan); - int key_len = Z_STRLEN_P(z_chan), key_free; + strlen_t key_len = Z_STRLEN_P(z_chan); + int key_free; key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); @@ -877,7 +879,7 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Prefix key */ - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Construct command */ if(argc == 3) { @@ -923,7 +925,7 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Prefix key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Construct command */ *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, @@ -947,7 +949,8 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_args; smart_string cmdstr = {0}; char *arg; - int arg_free, arg_len, i; + int arg_free; + strlen_t arg_len, i; int argc = ZEND_NUM_ARGS(); // We at least need a key and one value @@ -1022,7 +1025,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); /* Prefix if required and append the key name */ - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); CMD_SET_SLOT(slot, key, key_len); if (key_free) efree(key); @@ -1050,7 +1053,8 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_args, *z_ele; HashTable *ht_arr; char *key; - int key_free, key_len, i, tail; + int key_free, i, tail; + strlen_t key_len; int single_array = 0, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; long timeout = 0; @@ -1204,7 +1208,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Serialize and key prefix if required val_free = redis_serialize(redis_sock, z_value, &val, (int *)&val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Check for an options array if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { @@ -1299,8 +1303,8 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Key prefixing - key1_free = redis_key_prefix(redis_sock, &key1, (int *)&key1_len); - key2_free = redis_key_prefix(redis_sock, &key2, (int *)&key2_len); + key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); + key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); // In cluster mode, verify the slots match if(slot) { @@ -1356,7 +1360,7 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, } /* Prefix the key if required */ - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); /* If our value is 1 we use INCR/DECR. For other values, treat the call as * an INCRBY or DECRBY call */ @@ -1416,7 +1420,7 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key if necissary - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, "HINCRBY", "ssl", key, key_len, mem, @@ -1447,7 +1451,7 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, "HINCRBYFLOAT", "ssf", key, key_len, @@ -1520,7 +1524,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Start command construction redis_cmd_init_sstr(&cmdstr, valid+1, "HMGET", sizeof("HMGET")-1); // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); @@ -1572,7 +1576,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Grab our array as a HashTable ht_vals = Z_ARRVAL_P(z_arr); @@ -1643,7 +1647,7 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command based on arg count if(argc == 2) { @@ -1671,7 +1675,8 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_args; char *key; - int key_len, i, key_free, argc = ZEND_NUM_ARGS(); + strlen_t key_len; + int i, key_free, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; short kslot; zend_string *zstr; @@ -1750,7 +1755,7 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, construct command - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); *cmd_len = redis_cmd_format_static(cmd, "BITCOUNT", "sdd", key, key_len, (int)start, (int)end); @@ -1773,8 +1778,8 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_arr; smart_string cmdstr = {0}; char *mem, *key; - int key_free, mem_len, mem_free, argc=1; - strlen_t key_len; + int key_free, mem_free, argc=1; + strlen_t key_len, mem_len; zend_string *zstr; // Parse arguments @@ -1794,7 +1799,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, set initial hash slot - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); if(slot) *slot = cluster_hash_key(key, key_len); // Start command construction @@ -1878,7 +1883,8 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_keys, *z_key; HashTable *ht_keys; smart_string cmdstr = {0}; - int num_keys, key_len, key_free; + int num_keys, key_free; + strlen_t key_len; char *key; short kslot=-1; zend_string *zstr; @@ -2005,7 +2011,7 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); *cmd_len = redis_cmd_format_static(cmd, "SETBIT", "sld", key, key_len, offset, (int)val); @@ -2039,7 +2045,7 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, serialize value and position - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, (int *)&pivot_len TSRMLS_CC); @@ -2077,7 +2083,7 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, serialize value - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); // Construct command @@ -2110,8 +2116,8 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); - src_free = redis_key_prefix(redis_sock, &src, (int *)&src_len); - dst_free = redis_key_prefix(redis_sock, &dst, (int *)&dst_len); + src_free = redis_key_prefix(redis_sock, &src, &src_len); + dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); // Protect against a CROSSSLOT error if(slot) { @@ -2158,7 +2164,7 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix/serialize val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, mem, @@ -2208,7 +2214,7 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key if requested - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Set our have count flag *have_count = ZEND_NUM_ARGS() == 2; @@ -2248,7 +2254,7 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix key, serialize - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); mem_free = redis_serialize(redis_sock, z_val, &mem, (int *)&mem_len TSRMLS_CC); *cmd_len = redis_cmd_format_static(cmd, "ZINCRBY", "sfs", key, key_len, @@ -2285,7 +2291,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *using_store = 0; // Handle key prefixing - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // If we don't have an options array, the command is quite simple if(!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) { @@ -2497,7 +2503,8 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_args; smart_string cmdstr = {0}; char *arg; - int arg_free, arg_len, i; + int arg_free, i; + strlen_t arg_len; int argc = ZEND_NUM_ARGS(); zend_string *zstr; @@ -2554,7 +2561,8 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_args; char *key, *val, *exp_type = NULL; - int key_len, key_free, val_len, val_free; + strlen_t key_len; + int key_free, val_len, val_free; int num = ZEND_NUM_ARGS(), i = 1, argc; zend_bool ch = 0, incr = 0; smart_string cmdstr = {0}; @@ -2672,7 +2680,7 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, (int *)&key_len); + key_free = redis_key_prefix(redis_sock, &key, &key_len); // Format our command *cmd_len = redis_cmd_format_static(cmd, "OBJECT", "ss", subcmd, subcmd_len, @@ -2834,7 +2842,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUS"); /* Apply any key prefix */ - keyfree = redis_key_prefix(redis_sock, &key, (int *)&keylen); + keyfree = redis_key_prefix(redis_sock, &key, &keylen); /* Append required arguments */ redis_cmd_append_sstr(&cmdstr, key, keylen); @@ -2888,7 +2896,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUSBYMEMBER"); /* Prefix our key if we're prefixing */ - keyfree = redis_key_prefix(redis_sock, &key, (int *)&keylen); + keyfree = redis_key_prefix(redis_sock, &key, &keylen); /* Append required arguments */ redis_cmd_append_sstr(&cmdstr, key, keylen); @@ -3184,7 +3192,7 @@ void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { } if(redis_sock->prefix != NULL && redis_sock->prefix_len>0) { - redis_key_prefix(redis_sock, &key, (int *)&key_len); + redis_key_prefix(redis_sock, &key, &key_len); RETVAL_STRINGL(key, key_len); efree(key); } else { From 1435d4540a21df8f02f11466bdfc0d3ee0485f3e Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 21 Dec 2016 09:23:56 +0100 Subject: [PATCH 0676/1986] change redis_serialize to accept strlen_t --- cluster_library.c | 3 ++- cluster_library.h | 2 +- library.c | 6 +++--- library.h | 2 +- redis.c | 2 +- redis_cluster.c | 3 ++- redis_commands.c | 33 +++++++++++++++++---------------- 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 24f4062e69..280d94bbcf 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -455,7 +455,8 @@ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val TSRMLS_DC) { char *val; - int val_len, val_free; + strlen_t val_len; + int val_free; // Serialize our value val_free = redis_serialize(c->flags, z_val, &val, &val_len TSRMLS_CC); diff --git a/cluster_library.h b/cluster_library.h index 90eb97201c..3a58f13a0a 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -333,7 +333,7 @@ void cluster_free_reply(clusterReply *reply, int free_data); HashTable *cluster_dist_create(); void cluster_dist_free(HashTable *ht); int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, - int key_len, clusterKeyVal **kv); + strlen_t key_len, clusterKeyVal **kv); void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val TSRMLS_DC); diff --git a/library.c b/library.c index 23391aa74a..57d1a176f4 100644 --- a/library.c +++ b/library.c @@ -1972,7 +1972,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) } PHP_REDIS_API int -redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len +redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC) { #if ZEND_MODULE_API_NO >= 20100000 @@ -2024,7 +2024,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len php_var_serialize(&sstr, z, &ht); #if (PHP_MAJOR_VERSION < 7) *val = estrndup(sstr.c, sstr.len); - *val_len = (int)sstr.len; + *val_len = sstr.len; #else *val = estrndup(sstr.s->val, sstr.s->len); *val_len = sstr.s->len; @@ -2042,7 +2042,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len #ifdef HAVE_REDIS_IGBINARY if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { *val = (char*)val8; - *val_len = (int)sz; + *val_len = sz; return 1; } #endif diff --git a/library.h b/library.h index 64cfb2c569..b3b8a4061d 100644 --- a/library.h +++ b/library.h @@ -65,7 +65,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); PHP_REDIS_API int -redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_DC); +redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC); PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len); diff --git a/redis.c b/redis.c index 71538cc5b0..bc7c57a0cc 100644 --- a/redis.c +++ b/redis.c @@ -1853,7 +1853,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { ZEND_HASH_FOREACH_KEY_VAL(keytable, idx, zkey, z_value_p) { char *key, *val; strlen_t key_len; - int val_len; + strlen_t val_len; int val_free, key_free; char buf[32]; diff --git a/redis_cluster.c b/redis_cluster.c index fa63ae0dd1..beb476b1f1 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -590,7 +590,8 @@ typedef struct clusterKeyValHT { short slot; char *val; - int val_len, val_free; + strlen_t val_len; + int val_free; } clusterKeyValHT; /* Helper to pull a key/value pair from a HashTable */ diff --git a/redis_commands.c b/redis_commands.c index 067e583b49..d63332d1ca 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -109,8 +109,8 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key = NULL, *val=NULL; - int val_len, val_free, key_free; - strlen_t key_len; + int val_free, key_free; + strlen_t key_len, val_len; zend_long expire; zval *z_val; @@ -185,7 +185,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct our command @@ -1008,8 +1008,8 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_arr, *z_val; HashTable *ht_arr; smart_string cmdstr = {0}; - int val_len, key_free, val_free, argc = 1; - strlen_t key_len; + int key_free, val_free, argc = 1; + strlen_t val_len, key_len; char *key, *val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, @@ -1207,7 +1207,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Serialize and key prefix if required - val_free = redis_serialize(redis_sock, z_value, &val, (int *)&val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Check for an options array @@ -1588,7 +1588,8 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Start traversing our key => value array ZEND_HASH_FOREACH_KEY_VAL(ht_vals, idx, zkey, z_val) { char *mem, *val, kbuf[40]; - int val_len, val_free; + strlen_t val_len; + int val_free; unsigned int mem_len; // If the hash key is an integer, convert it to a string @@ -2046,8 +2047,8 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix key, serialize value and position key_free = redis_key_prefix(redis_sock, &key, &key_len); - val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); - pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, (int *)&pivot_len + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC); // Construct command @@ -2084,7 +2085,7 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix key, serialize value key_free = redis_key_prefix(redis_sock, &key, &key_len); - val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); // Construct command *cmd_len = redis_cmd_format_static(cmd, "LREM", "sds", key, key_len, count, @@ -2115,7 +2116,7 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); src_free = redis_key_prefix(redis_sock, &src, &src_len); dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); @@ -2163,7 +2164,7 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Prefix/serialize - val_free = redis_serialize(redis_sock, z_val, &val, (int *)&val_len TSRMLS_CC); + val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Construct command @@ -2255,7 +2256,7 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix key, serialize key_free = redis_key_prefix(redis_sock, &key, &key_len); - mem_free = redis_serialize(redis_sock, z_val, &mem, (int *)&mem_len TSRMLS_CC); + mem_free = redis_serialize(redis_sock, z_val, &mem, &mem_len TSRMLS_CC); *cmd_len = redis_cmd_format_static(cmd, "ZINCRBY", "sfs", key, key_len, incrby, mem, mem_len); @@ -2561,8 +2562,8 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_args; char *key, *val, *exp_type = NULL; - strlen_t key_len; - int key_free, val_len, val_free; + strlen_t key_len, val_len; + int key_free, val_free; int num = ZEND_NUM_ARGS(), i = 1, argc; zend_bool ch = 0, incr = 0; smart_string cmdstr = {0}; @@ -3205,7 +3206,7 @@ void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, { zval *z_val; char *val; - int val_len; + strlen_t val_len; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_val)==FAILURE) { RETURN_FALSE; From 37c754a44a071786911513107740c7ee4a4c1f94 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 21 Dec 2016 09:48:39 +0100 Subject: [PATCH 0677/1986] change redis_sock_read_scan_reply to use zend_long --- library.c | 2 +- library.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 57d1a176f4..f1d7da2784 100644 --- a/library.c +++ b/library.c @@ -216,7 +216,7 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - REDIS_SCAN_TYPE type, long *iter) + REDIS_SCAN_TYPE type, zend_long *iter) { REDIS_REPLY_TYPE reply_type; long reply_info; diff --git a/library.h b/library.h index b3b8a4061d..7e68fba21a 100644 --- a/library.h +++ b/library.h @@ -49,7 +49,7 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter); +PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, zend_long *iter); PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); From 2411ec9fa72e8c1802c50112e4b43a699cb7fd29 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 21 Dec 2016 09:59:07 +0100 Subject: [PATCH 0678/1986] fix last -Wincompatible-pointer-types warning --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index beb476b1f1..a33b4c00a3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2139,7 +2139,7 @@ PHP_METHOD(RedisCluster, watch) { smart_string cmd = {0}; zval *z_args; int argc = ZEND_NUM_ARGS(), i; - ulong slot; + zend_ulong slot; zend_string *zstr; // Disallow in MULTI mode From ccd11e869b9a0a38dbc3c282c9008c63050c14c3 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 21 Dec 2016 09:59:25 +0100 Subject: [PATCH 0679/1986] disply PHP version in test output --- tests/TestRedis.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/TestRedis.php b/tests/TestRedis.php index ccb61d9f73..794ea8843c 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -34,6 +34,7 @@ /* Let the user know this can take a bit of time */ echo "Note: these tests might take up to a minute. Don't worry :-)\n"; +echo "Using PHP version " . PHP_VERSION . "\n"; /* Depending on the classes being tested, run our tests on it */ echo "Testing class "; From b7aea0187c560efb0ec3a7f4a7f0a42aa356ac33 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 21 Dec 2016 16:30:18 -0800 Subject: [PATCH 0680/1986] Bamp 3.1.1-rc1 --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index c0a0015258..5baa3be95f 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "3.1.0" +#define PHP_REDIS_VERSION "3.1.1-RC1" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 61a7e483927c2b20ed4dac680b9d9bbe1583662b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 21 Dec 2016 22:08:13 -0800 Subject: [PATCH 0681/1986] Use correct legacy definition for ZEND_MOD_END --- common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index 68d753daa2..3c53ed532e 100644 --- a/common.h +++ b/common.h @@ -364,9 +364,9 @@ extern int (*_php_var_unserialize)(zval **, const unsigned char **, const unsign #define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) typedef int strlen_t; -/* PHP_MOD_END exists since 5.3.7 */ +/* If ZEND_MOD_END isn't defined, use legacy version */ #ifndef ZEND_MOD_END -#define ZEND_MOD_END { NULL, NULL, NULL, 0 } +#define ZEND_MOD_END { NULL, NULL, NULL } #endif /* PHP_FE_END exists since 5.3.7 */ From 811bcc1275c82070a6b85a18295898f2d7e39cdd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 22 Dec 2016 00:12:04 -0800 Subject: [PATCH 0682/1986] Bamp 3.1.1RC1 --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index 5baa3be95f..eb701f5a7a 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "3.1.1-RC1" +#define PHP_REDIS_VERSION "3.1.1RC1" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From f4cfc0ae19bf27d8fb84a9dba74a2e8f05e268e2 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 22 Dec 2016 11:00:59 +0100 Subject: [PATCH 0683/1986] Fix #1056 failed test on 32bits --- tests/RedisTest.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 8c99b04677..c79ab88f57 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -167,8 +167,8 @@ public function testBitsets() { // Verify valid offset ranges $this->assertFalse($this->redis->getBit('key', -1)); - $this->redis->setBit('key', 4294967295, 1); - $this->assertEquals(1, $this->redis->getBit('key', 4294967295)); + $this->redis->setBit('key', 0x7fffffff, 1); + $this->assertEquals(1, $this->redis->getBit('key', 0x7fffffff)); } public function testBitPos() { @@ -476,6 +476,9 @@ public function testSetNX() { } 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); @@ -515,7 +518,7 @@ public function testIncr() $this->assertTrue("abc" === $this->redis->get('key')); $this->redis->set('key', 0); - $this->assertEquals(2147483648, $this->redis->incrby('key', 2147483648)); + $this->assertEquals(PHP_INT_MAX, $this->redis->incrby('key', PHP_INT_MAX)); } public function testIncrByFloat() @@ -2296,8 +2299,8 @@ public function testHashes() { $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); $this->assertTrue("2" === $this->redis->hGet('h', 'x')); - $this->assertTrue(1000000000002 === $this->redis->hIncrBy('h', 'x', 1000000000000)); - $this->assertTrue("1000000000002" === $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)); From 52dbe95c293806c63c375bdc4057e440e17ca4e2 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 22 Dec 2016 11:54:54 +0100 Subject: [PATCH 0684/1986] fix rounding issue on arm --- tests/RedisTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index c79ab88f57..e0867cfca9 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -553,7 +553,7 @@ public function testIncrByFloat() $this->redis->setOption(Redis::OPT_PREFIX, 'someprefix:'); $this->redis->del('key'); $this->redis->incrbyfloat('key',1.8); - $this->assertEquals('1.8', $this->redis->get('key')); + $this->assertEquals(1.8, floatval($this->redis->get('key'))); // convert to float to avoid rounding issue on arm $this->redis->setOption(Redis::OPT_PREFIX, ''); $this->assertTrue($this->redis->exists('someprefix:key')); $this->redis->del('someprefix:key'); From 28be4a8d28cec56b1d2347ca8a9630a056bdfdbd Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 22 Dec 2016 11:55:21 +0100 Subject: [PATCH 0685/1986] display integer size in test suite output --- tests/TestRedis.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 794ea8843c..7f73d258ec 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -34,7 +34,7 @@ /* Let the user know this can take a bit of time */ echo "Note: these tests might take up to a minute. Don't worry :-)\n"; -echo "Using PHP version " . PHP_VERSION . "\n"; +echo "Using PHP version " . PHP_VERSION . " (" . (PHP_INT_SIZE*8) . " bits)\n"; /* Depending on the classes being tested, run our tests on it */ echo "Testing class "; From 335c05847c325099a13bd4e7d912575452f23015 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jan 2017 17:43:38 -0800 Subject: [PATCH 0686/1986] Use zend_list_close instead of zend_list_delete when reconnecting. In php7 zend_list_close handles closing the connection and frees resource structures but doesn't decrease refcount (and therefore remove the element from the list). This segfault could be triggered in php7 by simply doing this: ```php $r = new Redis(); $r->connect('localhost', 6379); $r->connect('localhost', 6379); $r->ping(); // or any other command ``` What appears to have been happening here, is that phpredis would delete any existing socket resource on a connect call to an already existing object, but then the call to add_property_resource would trigger the destructor of the socket (as the Zend library already saw a "socket" property). This would set redis_sock->stream->abstract (the actual socket) to NULL, causing any subsequent communication to segfault or issue a bus error. Fixes #1076 --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index bc7c57a0cc..98bd68551f 100644 --- a/redis.c +++ b/redis.c @@ -799,7 +799,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { #if (PHP_MAJOR_VERSION < 7) zend_list_delete(Z_LVAL_P(socket)); #else - zend_list_delete(Z_RES_P(socket)); + zend_list_close(Z_RES_P(socket)); #endif } } From 6c1101f0e76aa95c6b54a2d4bf6ee82700d63b87 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 2 Jan 2017 17:54:01 -0800 Subject: [PATCH 0687/1986] Test for calling connect multiple times on the same object --- tests/RedisClusterTest.php | 1 + tests/RedisTest.php | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 313d8c750a..1ccbf47b59 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -32,6 +32,7 @@ public function testSortDesc() { return $this->markTestSkipped(); } public function testWait() { return $this->markTestSkipped(); } public function testSelect() { return $this->markTestSkipped(); } public function testReconnectSelect() { return $this->markTestSkipped(); } + public function testMultipleConnect() { return $this->markTestSkipped(); } /* Load our seeds on construction */ public function __construct() { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index e0867cfca9..4494a61986 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4974,5 +4974,15 @@ public function testSession() session_write_close(); $this->assertTrue($this->redis->exists('PHPREDIS_SESSION:' . session_id())); } + + public function testMultipleConnect() { + $host = $this->redis->GetHost(); + $port = $this->redis->GetPort(); + + for($i = 0; $i < 5; $i++) { + $this->redis->connect($host, $port); + $this->assertEquals($this->redis->ping(), "+PONG"); + } + } } ?> From c90649a6d548533f72aae762dd96dac049d77463 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 4 Jan 2017 23:32:29 +0200 Subject: [PATCH 0688/1986] redis_boolean_response_impl refactoring --- library.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/library.c b/library.c index f1d7da2784..15cb1444c5 100644 --- a/library.c +++ b/library.c @@ -1117,36 +1117,20 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *response; int response_len; - char ret; + zend_bool ret = 0; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - return; - } - RETURN_FALSE; + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) != NULL) { + ret = (*response == '+'); + efree(response); } - ret = response[0]; - efree(response); + if (ret && success_callback != NULL) { + success_callback(redis_sock); + } IF_MULTI_OR_PIPELINE() { - if (ret == '+') { - if (success_callback != NULL) { - success_callback(redis_sock); - } - add_next_index_bool(z_tab, 1); - } else { - add_next_index_bool(z_tab, 0); - } + add_next_index_bool(z_tab, ret); } else { - if (ret == '+') { - if (success_callback != NULL) { - success_callback(redis_sock); - } - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(ret); } } From 12a333534f411a22f817ea21dec4fb45861fbeb8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 8 Jan 2017 00:22:12 +0200 Subject: [PATCH 0689/1986] redis_sock_write refactoring Check the number of bytes returned by `php_stream_write`. Use SOCKET_WRITE_COMMAND macro. --- library.c | 19 ++++++++----------- redis.c | 15 +++------------ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/library.c b/library.c index 15cb1444c5..cd722350a3 100644 --- a/library.c +++ b/library.c @@ -1680,10 +1680,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - RETURN_FALSE; - } + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) @@ -1920,18 +1917,18 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc /** * redis_sock_write */ -PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz - TSRMLS_DC) +PHP_REDIS_API int +redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { if (!redis_sock || redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); - return -1; + } else if (redis_check_eof(redis_sock, 0 TSRMLS_CC) == 0 && + php_stream_write(redis_sock->stream, cmd, sz) == sz + ) { + return sz; } - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { - return -1; - } - return php_stream_write(redis_sock->stream, cmd, sz); + return -1; } /** diff --git a/redis.c b/redis.c index 98bd68551f..311d9f2da1 100644 --- a/redis.c +++ b/redis.c @@ -2228,10 +2228,7 @@ PHP_METHOD(Redis, multi) IF_MULTI() { cmd_len = redis_cmd_format_static(&cmd, "MULTI", ""); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - RETURN_FALSE; - } + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) @@ -2361,10 +2358,7 @@ PHP_METHOD(Redis, exec) IF_MULTI() { cmd_len = redis_cmd_format_static(&cmd, "EXEC", ""); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - RETURN_FALSE; - } + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); if(redis_sock_read_multibulk_multi_reply( @@ -2551,10 +2545,7 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, cmd_len = spprintf(&cmd, 0, "%s %s\r\n", unsub_cmd, cmd); efree(old_cmd); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { - efree(cmd); - RETURN_FALSE; - } + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); array_init(return_value); From 6096b5e351a0baeae268b2dcfdc48014eda0fdff Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 16 Jan 2017 10:00:51 -0800 Subject: [PATCH 0690/1986] Ready 3.1.1.RC2 --- package.xml | 87 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/package.xml b/package.xml index db039594e3..5850a963a8 100644 --- a/package.xml +++ b/package.xml @@ -27,37 +27,41 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2016-12-14 + 2017-01-16 - 3.1.0 - 3.1.0 + 3.1.1RC2 + 3.1.1RC2 - stable - stable + beta + beta PHP - phpredis 3.1.0 - - In this version of phpredis codebase was unified to work with all versions of php \o/ - Also many bug fixes and some improvements has been made. - - --- Improvements --- - - * Support the client to Redis Cluster just having one master (andyli) [892e5646] - * Allow both long and strings that are longs for zrangebyscore offset/limit (Michael Grunder) [bdcdd2aa] - * Process NX|XX, CH and INCR options in zAdd command (Pavlo Yatsukhnenko) [71c9f7c8] - - --- Fixes --- - - * Fix incrby/decrby for large integers (Michael Grunder) [3a12758a] - * Use static declarations for spl_ce_RuntimeException decl (Jeremy Mikola) [a9857d69] - * Fixed method call problem causes session handler to display two times (ZiHang Gao) [24f86c49] - * psetex method returns '+OK' on success, not true (sitri@ndxbn) [afcd8445] - * Fix integer overflow for long (>32bit) increments in hIncrBy (iyesin) [58e1d799] - * Move zend_object handler to the end (Michael Grunder) [34107966] - * Using setOption on redis array causes immediate connection (Pavlo Yatsukhnenko) [f1a85b38] + phpredis 3.1.1RC2 + + * Additional test updates for 32 bit systems (@remicollet) + * ARM rounding issue in tests (@remicollet) + * Use new zend_list_close instead of zend_list_delete when reconnecting. + * Refactoring of redis_boolean_response_impl and redis_sock_write (@yatsukhnenko) + + phpredis 3.1.1.RC1 + + This release contains mostly fixes for issues introduced when merging + the php 5 and 7 codebase into a single branch. + + * Fixed a segfault in igbinary serialization (@yatsukhnenko) + * Restore 2.2.8/3.0.0 functionality to distinguish between an error + and simply empty session data. (@remicollet) + * Fix double to string conversion function (@yatsukhnenko) + * Use PHP_FE_END definition when available (@remicollet) + * Fixed various 'static function declared but not used' warnings + * Fixes to various calls which were typecasting pointers to the + wrong size. (@remicollet) + + * Added php session unit test (@yatsukhnenko) + * Added explicit module dependancy for igbinary (@remicollet) + * Added phpinfo serialization information (@remicollet) @@ -111,6 +115,39 @@ http://pear.php.net/dtd/package-2.0.xsd"> redis + + betabeta + 3.1.1RC23.1.1RC2 + 2017-01-16 + + + phpredis 3.1.1RC2 + + * Additional test updates for 32 bit systems (@remicollet) + * ARM rounding issue in tests (@remicollet) + * Use new zend_list_close instead of zend_list_delete when reconnecting. + * Refactoring of redis_boolean_response_impl and redis_sock_write (@yatsukhnenko) + + phpredis 3.1.1.RC1 + + This release contains mostly fixes for issues introduced when merging + the php 5 and 7 codebase into a single branch. + + * Fixed a segfault in igbinary serialization (@yatsukhnenko) + * Restore 2.2.8/3.0.0 functionality to distinguish between an error + and simply empty session data. (@remicollet) + * Fix double to string conversion function (@yatsukhnenko) + * Use PHP_FE_END definition when available (@remicollet) + * Fixed various 'static function declared but not used' warnings + * Fixes to various calls which were typecasting pointers to the + wrong size. (@remicollet) + + * Added php session unit test (@yatsukhnenko) + * Added explicit module dependancy for igbinary (@remicollet) + * Added phpinfo serialization information (@remicollet) + + + stablestable 3.1.03.1.0 From ede43864a63db547319a01d14a8b272efd77de01 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 16 Jan 2017 10:39:56 -0800 Subject: [PATCH 0691/1986] Fix version in source --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index eb701f5a7a..bfc7a5ff84 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "3.1.1RC1" +#define PHP_REDIS_VERSION "3.1.1RC2" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 495e5df4f76678f7a221842687b075c8ec409357 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 19 Jan 2017 00:10:13 +0200 Subject: [PATCH 0692/1986] refactoring --- redis_array.c | 6 ++---- redis_array_impl.c | 52 ++++++++++++++++++++-------------------------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/redis_array.c b/redis_array.c index 1d33d92ef2..dc4213321f 100644 --- a/redis_array.c +++ b/redis_array.c @@ -358,10 +358,6 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* check if write cmd */ b_write_cmd = ra_is_write_cmd(ra, cmd, cmd_len); - if(ra->index && b_write_cmd && !ra->z_multi_exec) { /* add MULTI + SADD */ - ra_index_multi(redis_inst, MULTI TSRMLS_CC); - } - /* pass call through */ ZVAL_STRINGL(&z_fun, cmd, cmd_len); /* method name */ z_callargs = ecalloc(argc + 1, sizeof(zval)); @@ -384,6 +380,8 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* CALL! */ if(ra->index && b_write_cmd) { + /* add MULTI + SADD */ + ra_index_multi(redis_inst, MULTI TSRMLS_CC); /* call using discarded temp value and extract exec results after. */ call_user_function(&redis_ce->function_table, redis_inst, &z_fun, &z_tmp, argc, z_callargs); zval_dtor(&z_tmp); diff --git a/redis_array_impl.c b/redis_array_impl.c index 177368ae0e..40d0c25280 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -375,10 +375,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* call userland key extraction function */ char * -ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS_DC) { - - char *out; - zval z_ret, z_argv[1]; +ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS_DC) +{ + char *out = NULL; + zval z_ret, z_argv; /* check that we can call the extractor function */ #if (PHP_MAJOR_VERSION < 7) @@ -391,19 +391,15 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR } /* call extraction function */ - ZVAL_STRINGL(&z_argv[0], key, key_len); - call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, z_argv); - - if(Z_TYPE(z_ret) != IS_STRING) { - zval_dtor(&z_argv[0]); - zval_dtor(&z_ret); - return NULL; - } + ZVAL_STRINGL(&z_argv, key, key_len); + call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); - *out_len = Z_STRLEN(z_ret); - out = estrndup(Z_STRVAL(z_ret), *out_len); + if (Z_TYPE(z_ret) == IS_STRING) { + *out_len = Z_STRLEN(z_ret); + out = estrndup(Z_STRVAL(z_ret), *out_len); + } - zval_dtor(&z_argv[0]); + zval_dtor(&z_argv); zval_dtor(&z_ret); return out; } @@ -426,10 +422,10 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS /* call userland key distributor function */ zend_bool -ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRMLS_DC) { - - zval z_ret; - zval z_argv[1]; +ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRMLS_DC) +{ + zend_bool ret = 0; + zval z_ret, z_argv; /* check that we can call the extractor function */ #if (PHP_MAJOR_VERSION < 7) @@ -442,19 +438,17 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML } /* call extraction function */ - ZVAL_STRINGL(&z_argv[0], key, key_len); - call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, z_argv); + ZVAL_STRINGL(&z_argv, key, key_len); + call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv); - if(Z_TYPE(z_ret) != IS_LONG) { - zval_dtor(&z_argv[0]); - zval_dtor(&z_ret); - return 0; - } + if (Z_TYPE(z_ret) == IS_LONG) { + *pos = Z_LVAL(z_ret); + ret = 1; + } - *pos = Z_LVAL(z_ret); - zval_dtor(&z_argv[0]); + zval_dtor(&z_argv); zval_dtor(&z_ret); - return 1; + return ret; } zval * From 26aec4dd079358a3a90fe62b26464d3d4f25352e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 8 Feb 2016 09:46:04 -0800 Subject: [PATCH 0693/1986] Allow sInterStore to take one arg (as it could be one array) Addresses #748 --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index d63332d1ca..1566c0e550 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2963,7 +2963,7 @@ int redis_sinterstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - "SINTERSTORE", sizeof("SINTERSTORE")-1, 2, 0, cmd, cmd_len, slot); + "SINTERSTORE", sizeof("SINTERSTORE")-1, 1, 0, cmd, cmd_len, slot); } /* SUNION */ From e37c08083cb97aea46231407093a082e4fbef1cf Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 26 Jan 2017 00:16:03 +0200 Subject: [PATCH 0694/1986] TravisCI: allow_failures fix + igbinary testing --- .travis.yml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7052747e45..2e4c5bd335 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,27 +10,26 @@ php: - nightly env: CC=gcc matrix: - include: - - env: CC=clang - php: 5.4 - - env: CC=clang - php: 5.5 - - env: CC=clang - php: 5.6 - - env: CC=clang - php: 7.0 - - env: CC=clang - php: 7.1 - - env: CC=clang - php: nightly allow_failures: - php: nightly + include: + - php: 5.4 + env: CC=clang + - php: 5.5 + env: CC=clang + - php: 5.6 + env: CC=clang + - php: 7.0 + env: CC=clang + - php: 7.1 + env: CC=clang addons: apt: packages: clang before_install: - phpize - - if [ $(phpenv version-name) lt 7 ]; then pecl install igbinary-1.2.1; ./configure --enable-redis-igbinary CFLAGS=-Wall; else ./configure CFLAGS=-Wall; fi + - pecl install igbinary + - ./configure --enable-redis-igbinary CFLAGS=-Wall install: make install before_script: - gem install redis From 4cd06b2908c3790c77de65dcba13e65f69bad688 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 27 Jan 2017 12:17:54 -0800 Subject: [PATCH 0695/1986] Add a test for passing a single array into sInterStore --- tests/RedisTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 4494a61986..132a894ecb 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1407,6 +1407,9 @@ public function testsInterStore() { $this->redis->sAdd('{set}t', $i); } + /* Regression test for passing a single array */ + $this->assertEquals($this->redis->sInterStore(Array('{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) { From a765f8d4b55b7660f5b6abcf0364b0ad30697a21 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 29 Jan 2017 22:39:17 +0200 Subject: [PATCH 0696/1986] redis_array_object --- redis.c | 20 ++----- redis_array.c | 128 ++++++++++++++++++++++++++++++++------------- redis_array.h | 8 +++ redis_array_impl.c | 2 + 4 files changed, 107 insertions(+), 51 deletions(-) diff --git a/redis.c b/redis.c index 311d9f2da1..40bbef3a3e 100644 --- a/redis.c +++ b/redis.c @@ -43,7 +43,6 @@ #define R_SUB_CLOSURE_TYPE 3 int le_redis_sock; -extern int le_redis_array; #ifdef PHP_SESSION extern ps_module ps_mod_redis; @@ -583,24 +582,15 @@ PHP_MINIT_FUNCTION(redis) redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC); /* RedisArray class */ - INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", - redis_array_functions); - redis_array_ce = zend_register_internal_class(&redis_array_class_entry - TSRMLS_CC); + INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions); + redis_array_ce = zend_register_internal_class(&redis_array_class_entry TSRMLS_CC); + redis_array_ce->create_object = create_redis_array_object; /* RedisCluster class */ - INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", - redis_cluster_functions); - redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry - TSRMLS_CC); + INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_functions); + redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry TSRMLS_CC); redis_cluster_ce->create_object = create_cluster_context; - le_redis_array = zend_register_list_destructors_ex( - redis_destructor_redis_array, - NULL, - "Redis Array", module_number - ); - /* RedisException class */ INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL); redis_exception_ce = zend_register_internal_class_ex( diff --git a/redis_array.c b/redis_array.c index dc4213321f..de516b0039 100644 --- a/redis_array.c +++ b/redis_array.c @@ -92,7 +92,9 @@ zend_function_entry redis_array_functions[] = { PHP_FE_END }; -static void redis_array_free(RedisArray *ra) { +static void +redis_array_free(RedisArray *ra) +{ int i; /* Redis objects */ @@ -116,46 +118,106 @@ static void redis_array_free(RedisArray *ra) { efree(ra); } -int le_redis_array; -void redis_destructor_redis_array(zend_resource * rsrc TSRMLS_DC) +#if (PHP_MAJOR_VERSION < 7) +typedef struct { + zend_object std; + RedisArray *ra; +} redis_array_object; + +void +free_redis_array_object(void *object TSRMLS_DC) +{ + redis_array_object *obj = (redis_array_object *)object; + + zend_object_std_dtor(&obj->std TSRMLS_CC); + if (obj->ra) { + if (obj->ra->prev) redis_array_free(obj->ra->prev); + redis_array_free(obj->ra); + } + efree(obj); +} + +zend_object_value +create_redis_array_object(zend_class_entry *ce TSRMLS_DC) { - RedisArray *ra = (RedisArray*)rsrc->ptr; + zend_object_value retval; + redis_array_object *obj = ecalloc(1, sizeof(redis_array_object)); + memset(obj, 0, sizeof(redis_array_object)); - /* Free previous ring if it's set */ - if(ra->prev) redis_array_free(ra->prev); + zend_object_std_init(&obj->std, ce TSRMLS_CC); - /* Free parent array */ - redis_array_free(ra); +#if PHP_VERSION_ID < 50399 + zval *tmp; + zend_hash_copy(obj->std.properties, &ce->default_properties, + (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); +#endif + + retval.handle = zend_objects_store_put(obj, + (zend_objects_store_dtor_t)zend_objects_destroy_object, + (zend_objects_free_object_storage_t)free_redis_array_object, + NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + + return retval; } +#else +typedef struct { + RedisArray *ra; + zend_object std; +} redis_array_object; +zend_object_handlers redis_array_object_handlers; + +void +free_redis_array_object(zend_object *object) +{ + redis_array_object *obj = (redis_array_object *)((char *)(object) - XtOffsetOf(redis_array_object, std)); + + if (obj->ra) { + if (obj->ra->prev) redis_array_free(obj->ra->prev); + redis_array_free(obj->ra); + } + zend_object_std_dtor(&obj->std TSRMLS_CC); +} + +zend_object * +create_redis_array_object(zend_class_entry *ce TSRMLS_DC) +{ + redis_array_object *obj = ecalloc(1, sizeof(redis_array_object) + zend_object_properties_size(ce)); + + memset(obj, 0, sizeof(redis_array_object)); + zend_object_std_init(&obj->std, ce TSRMLS_CC); + object_properties_init(&obj->std, ce); + + memcpy(&redis_array_object_handlers, zend_get_std_object_handlers(), sizeof(redis_array_object_handlers)); + redis_array_object_handlers.offset = XtOffsetOf(redis_array_object, std); + redis_array_object_handlers.free_obj = free_redis_array_object; + obj->std.handlers = &redis_array_object_handlers; + + return &obj->std; +} +#endif /** * redis_array_get */ -PHP_REDIS_API int redis_array_get(zval *id, RedisArray **ra TSRMLS_DC) +PHP_REDIS_API int +redis_array_get(zval *id, RedisArray **ra TSRMLS_DC) { + redis_array_object *obj; - zval *socket; - - if (Z_TYPE_P(id) != IS_OBJECT || (socket = zend_hash_str_find(Z_OBJPROP_P(id), - "socket", sizeof("socket") - 1)) == NULL) { - return -1; - } - + if (Z_TYPE_P(id) == IS_OBJECT) { #if (PHP_MAJOR_VERSION < 7) - int resource_type; - *ra = (RedisArray *)zend_list_find(Z_LVAL_P(socket), &resource_type); - if (!*ra || resource_type != le_redis_array) { + obj = (redis_array_object *)zend_objects_get_address(id TSRMLS_CC); #else - if (Z_RES_P(socket) == NULL || - !(*ra = (RedisArray *)Z_RES_P(socket)->ptr) || - Z_RES_P(socket)->type != le_redis_array - ) { + obj = (redis_array_object *)((char *)Z_OBJ_P(id) - XtOffsetOf(redis_array_object, std)); #endif - return -1; + if (obj->ra) { + *ra = obj->ra; + return 0; + } } - - return 0; + return -1; } uint32_t rcrc32(const char *s, size_t sz) { @@ -220,6 +282,7 @@ PHP_METHOD(RedisArray, __construct) long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0; + redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { RETURN_FALSE; @@ -302,7 +365,6 @@ PHP_METHOD(RedisArray, __construct) default: WRONG_PARAM_COUNT; - break; } zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -312,17 +374,11 @@ PHP_METHOD(RedisArray, __construct) ra->connect_timeout = d_connect_timeout; if(ra->prev) ra->prev->auto_rehash = b_autorehash; #if (PHP_MAJOR_VERSION < 7) - int id; -#if PHP_VERSION_ID >= 50400 - id = zend_list_insert(ra, le_redis_array TSRMLS_CC); -#else - id = zend_list_insert(ra, le_redis_array); -#endif - add_property_resource(getThis(), "socket", id); + obj = (redis_array_object *)zend_objects_get_address(getThis() TSRMLS_CC); #else - zval *id = zend_list_insert(ra, le_redis_array TSRMLS_CC); - add_property_resource(getThis(), "socket", Z_RES_P(id)); + obj = (redis_array_object *)((char *)Z_OBJ_P(getThis()) - XtOffsetOf(redis_array_object, std)); #endif + obj->ra = ra; } } diff --git a/redis_array.h b/redis_array.h index b9593f0c29..400ea00984 100644 --- a/redis_array.h +++ b/redis_array.h @@ -58,5 +58,13 @@ typedef struct RedisArray_ { uint32_t rcrc32(const char *s, size_t sz); +#if (PHP_MAJOR_VERSION < 7) +zend_object_value create_redis_array_object(zend_class_entry *ce TSRMLS_DC); +void free_redis_array_object(void *object TSRMLS_DC); +#else +zend_object *create_redis_array_object(zend_class_entry *ce TSRMLS_DC); +void free_redis_array_object(zend_object *object); +#endif + #endif diff --git a/redis_array_impl.c b/redis_array_impl.c index 40d0c25280..860b44a83a 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -1176,6 +1176,8 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool count = ra_rehash_scan(z_redis, &keys, &key_lens, "KEYS", "*" TSRMLS_CC); } + if (count < 0) return; + /* callback */ if(z_cb && z_cb_cache) { ZVAL_NULL(&z_ret); From 058753e4c9da0b931a24b4f33b0c191187d817a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Fern=C3=A1ndez?= Date: Thu, 9 Feb 2017 13:56:34 +0100 Subject: [PATCH 0697/1986] Fix Null Bulk String response parsing in cluster library (#1104) * Failing test case when running LUA with bulk empty response * Fix issue when parsing bulk array response from eval commands * Added test for bulk LUA responses and changed condition * Added multi tests and fixes in C code format --- cluster_library.c | 14 +++++++-- tests/RedisClusterTest.php | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 280d94bbcf..923dda4e45 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1870,7 +1870,11 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust RETVAL_TRUE; break; case TYPE_BULK: - RETVAL_STRINGL(r->str, r->len); + if (r->len < 0) { + RETVAL_NULL(); + } else { + RETVAL_STRINGL(r->str, r->len); + } break; case TYPE_MULTIBULK: array_init(z_arr); @@ -1896,8 +1900,12 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust add_next_index_bool(&c->multi_resp, 1); break; case TYPE_BULK: - add_next_index_stringl(&c->multi_resp, r->str, r->len); - efree(r->str); + if (r->len < 0) { + add_next_index_null(&c->multi_resp); + } else { + add_next_index_stringl(&c->multi_resp, r->str, r->len); + efree(r->str); + } break; case TYPE_MULTIBULK: cluster_mbulk_variant_resp(r, &c->multi_resp); diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 1ccbf47b59..36f65f84a6 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -345,6 +345,69 @@ public function testEvalSHA() { $this->assertTrue(1 === $this->redis->eval($scr,Array($str_key), 1)); $this->assertTrue(1 === $this->redis->evalsha($sha,Array($str_key), 1)); } + + public function testEvalBulkResponse() { + $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + + $this->redis->script($str_key1, 'flush'); + $this->redis->script($str_key2, 'flush'); + + $scr = "return {KEYS[1],KEYS[2]}"; + + $result = $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + + $this->assertTrue($str_key1 === $result[0]); + $this->assertTrue($str_key2 === $result[1]); + } + + public function testEvalBulkResponseMulti() { + $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + + $this->redis->script($str_key1, 'flush'); + $this->redis->script($str_key2, 'flush'); + + $scr = "return {KEYS[1],KEYS[2]}"; + + $this->redis->multi(); + $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + + $result = $this->redis->exec(); + + $this->assertTrue($str_key1 === $result[0][0]); + $this->assertTrue($str_key2 === $result[0][1]); + } + + public function testEvalBulkEmptyResponse() { + $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + + $this->redis->script($str_key1, 'flush'); + $this->redis->script($str_key2, 'flush'); + + $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; + + $result = $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + + $this->assertTrue(null === $result); + } + + public function testEvalBulkEmptyResponseMulti() { + $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + + $this->redis->script($str_key1, 'flush'); + $this->redis->script($str_key2, 'flush'); + + $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; + + $this->redis->multi(); + $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + $result = $this->redis->exec(); + + $this->assertTrue(null === $result[0]); + } /* Cluster specific introspection stuff */ public function testIntrospection() { From 388e4a84563233c7e436feea44ccbdd47027db1d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 9 Feb 2017 22:12:59 +0200 Subject: [PATCH 0698/1986] refactoring New implementation of redis_parse_info_response function without estrndup using and with long/double/string values --- library.c | 52 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/library.c b/library.c index cd722350a3..7bf10bef56 100644 --- a/library.c +++ b/library.c @@ -923,54 +923,50 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret) { - char *key, *value, *p, *cur, *pos; - int is_numeric; + char *cur, *pos; array_init(z_ret); cur = response; while(1) { /* skip comments and empty lines */ - if(*cur == '#' || *cur == '\r') { - if(!(cur = strchr(cur, '\n'))) + if (*cur == '#' || *cur == '\r') { + if ((cur = strstr(cur, _NL)) == NULL) { break; - cur++; + } + cur += 2; continue; } /* key */ - pos = strchr(cur, ':'); - if(pos == NULL) { + if ((pos = strchr(cur, ':')) == NULL) { break; } - key = estrndup(cur, pos - cur); + char *key = cur; + int key_len = pos - cur; + key[key_len] = '\0'; /* value */ cur = pos + 1; - pos = strchr(cur, '\r'); - if(pos == NULL) { - efree(key); + if ((pos = strstr(cur, _NL)) == NULL) { break; } - value = estrndup(cur, pos - cur); - pos += 2; /* \r, \n */ - cur = pos; - - is_numeric = 1; - for(p = value; *p; ++p) { - if(*p < '0' || *p > '9') { - is_numeric = 0; - break; - } - } - - if(is_numeric == 1) { - add_assoc_long(z_ret, key, atol(value)); + char *value = cur; + int value_len = pos - cur; + value[value_len] = '\0'; + + double dval; + zend_long lval; + zend_uchar type = is_numeric_string(value, value_len, &lval, &dval, 0); + if (type == IS_LONG) { + add_assoc_long_ex(z_ret, key, key_len, lval); + } else if (type == IS_DOUBLE) { + add_assoc_double_ex(z_ret, key, key_len, dval); } else { - add_assoc_string(z_ret, key, value); + add_assoc_stringl_ex(z_ret, key, key_len, value, value_len); } - efree(value); - efree(key); + + cur = pos + 2; /* \r, \n */ } } From 8fa85afa7ab786ba124a86c7b9fdf34ecaff5aae Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 29 Jan 2017 23:33:05 +0200 Subject: [PATCH 0699/1986] redis_object --- common.h | 12 ++++ redis.c | 145 ++++++++++++++++++++++++++++----------------- redis_array_impl.c | 27 +++------ redis_array_impl.h | 1 - 4 files changed, 110 insertions(+), 75 deletions(-) diff --git a/common.h b/common.h index 3c53ed532e..ff1d6717a5 100644 --- a/common.h +++ b/common.h @@ -680,6 +680,18 @@ typedef struct { } RedisSock; /* }}} */ +#if (PHP_MAJOR_VERSION < 7) +typedef struct { + zend_object std; + RedisSock *sock; +} redis_object; +#else +typedef struct { + RedisSock *sock; + zend_object std; +} redis_object; +#endif + void free_reply_callbacks(zval *z_this, RedisSock *redis_sock); diff --git a/redis.c b/redis.c index 40bbef3a3e..16bb7d5e17 100644 --- a/redis.c +++ b/redis.c @@ -42,8 +42,6 @@ #define R_SUB_CALLBACK_FT_TYPE 2 #define R_SUB_CLOSURE_TYPE 3 -int le_redis_sock; - #ifdef PHP_SESSION extern ps_module ps_mod_redis; extern ps_module ps_mod_redis_cluster; @@ -426,36 +424,90 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { return result; } -/** - * redis_destructor_redis_sock - */ -static void redis_destructor_redis_sock(zend_resource * rsrc TSRMLS_DC) +#if (PHP_MAJOR_VERSION < 7) +void +free_redis_object(void *object TSRMLS_DC) +{ + redis_object *redis = (redis_object *)object; + + zend_object_std_dtor(&redis->std TSRMLS_CC); + if (redis->sock) { + redis_sock_disconnect(redis->sock TSRMLS_CC); + redis_free_socket(redis->sock); + } + efree(redis); +} + +zend_object_value +create_redis_object(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + redis_object *redis = ecalloc(1, sizeof(redis_object)); + + memset(redis, 0, sizeof(redis_object)); + zend_object_std_init(&redis->std, ce TSRMLS_CC); + +#if PHP_VERSION_ID < 50399 + zval *tmp; + zend_hash_copy(redis->std.properties, &ce->default_properties, + (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); +#endif + + retval.handle = zend_objects_store_put(redis, + (zend_objects_store_dtor_t)zend_objects_destroy_object, + (zend_objects_free_object_storage_t)free_redis_object, + NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + + return retval; +} +#else +zend_object_handlers redis_object_handlers; + +void +free_redis_object(zend_object *object) +{ + redis_object *redis = (redis_object *)((char *)(object) - XtOffsetOf(redis_object, std)); + + zend_object_std_dtor(&redis->std TSRMLS_CC); + if (redis->sock) { + redis_sock_disconnect(redis->sock TSRMLS_CC); + redis_free_socket(redis->sock); + } +} + +zend_object * +create_redis_object(zend_class_entry *ce TSRMLS_DC) { - RedisSock *redis_sock = (RedisSock *) rsrc->ptr; - redis_sock_disconnect(redis_sock TSRMLS_CC); - redis_free_socket(redis_sock); + redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(ce)); + + redis->sock = NULL; + + zend_object_std_init(&redis->std, ce TSRMLS_CC); + object_properties_init(&redis->std, ce); + + memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers)); + redis_object_handlers.offset = XtOffsetOf(redis_object, std); + redis_object_handlers.free_obj = free_redis_object; + redis->std.handlers = &redis_object_handlers; + + return &redis->std; } +#endif static zend_always_inline int redis_sock_get_instance(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) { - zval *socket; - int resource_type = 0; + redis_object *redis; - if (Z_TYPE_P(id) == IS_OBJECT && - (socket = zend_hash_str_find(Z_OBJPROP_P(id), "socket", sizeof("socket") - 1)) != NULL - ) { + if (Z_TYPE_P(id) == IS_OBJECT) { #if (PHP_MAJOR_VERSION < 7) - *redis_sock = (RedisSock *)zend_list_find(Z_LVAL_P(socket), &resource_type); + redis = (redis_object *)zend_objects_get_address(id TSRMLS_CC); #else - *redis_sock = NULL; - - if (Z_RES_P(socket) != NULL) { - *redis_sock = (RedisSock *)Z_RES_P(socket)->ptr; - resource_type = Z_RES_P(socket)->type; - } + redis = (redis_object *)((char *)Z_OBJ_P(id) - XtOffsetOf(redis_object, std)); #endif - if (*redis_sock && resource_type == le_redis_sock) { + if (redis->sock) { + *redis_sock = redis->sock; return 0; } } @@ -580,6 +632,7 @@ PHP_MINIT_FUNCTION(redis) /* Redis class */ INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions); redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC); + redis_ce->create_object = create_redis_object; /* RedisArray class */ INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions); @@ -616,12 +669,6 @@ PHP_MINIT_FUNCTION(redis) #endif ); - le_redis_sock = zend_register_list_destructors_ex( - redis_destructor_redis_sock, - NULL, - redis_sock_name, module_number - ); - /* Add shared class constants to Redis and RedisCluster objects */ add_class_constants(redis_ce, 0 TSRMLS_CC); add_class_constants(redis_cluster_ce, 1 TSRMLS_CC); @@ -737,13 +784,15 @@ PHP_METHOD(Redis, pconnect) } /* }}} */ -PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { - zval *object, *socket; +PHP_REDIS_API int +redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) +{ + zval *object; char *host = NULL, *persistent_id = NULL; zend_long port = -1, retry_interval = 0; strlen_t host_len, persistent_id_len; double timeout = 0.0; - RedisSock *redis_sock = NULL; + redis_object *redis; #ifdef ZTS /* not sure how in threaded mode this works so disabled persistence at @@ -779,40 +828,24 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { port = 6379; } - /* if there is a redis sock already we have to remove it from the list */ - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) >= 0) { - if ((socket = zend_hash_str_find(Z_OBJPROP_P(object), "socket", sizeof("socket") - 1)) == NULL) - { - /* maybe there is a socket but the id isn't known.. what to do? */ - } else { - /* The refcount should be decreased and destructor invoked */ #if (PHP_MAJOR_VERSION < 7) - zend_list_delete(Z_LVAL_P(socket)); + redis = (redis_object *)zend_objects_get_address(object TSRMLS_CC); #else - zend_list_close(Z_RES_P(socket)); + redis = (redis_object *)((char *)Z_OBJ_P(object) - XtOffsetOf(redis_object, std)); #endif - } + /* if there is a redis sock already we have to remove it */ + if (redis->sock) { + redis_sock_disconnect(redis->sock TSRMLS_CC); + redis_free_socket(redis->sock); } - redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, + redis->sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0); - if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { - redis_free_socket(redis_sock); + if (redis_sock_server_open(redis->sock, 1 TSRMLS_CC) < 0) { + redis_free_socket(redis->sock); return FAILURE; } -#if (PHP_MAJOR_VERSION < 7) - int id; -#if PHP_VERSION_ID >= 50400 - id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); -#else - id = zend_list_insert(redis_sock, le_redis_sock); -#endif - add_property_resource(object, "socket", id); -#else - zval *id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); - add_property_resource(object, "socket", Z_RES_P(id)); -#endif return SUCCESS; } diff --git a/redis_array_impl.c b/redis_array_impl.c index 860b44a83a..01f586ca32 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -26,7 +26,6 @@ #define PHPREDIS_INDEX_NAME "__phpredis_array_index__" -extern int le_redis_sock; extern zend_class_entry *redis_ce; RedisArray* @@ -36,7 +35,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b char *host, *p; short port; zval *zpData, z_cons, z_ret; - RedisSock *redis_sock = NULL; + redis_object *redis; /* function calls on the Redis object */ ZVAL_STRINGL(&z_cons, "__construct", 11); @@ -72,29 +71,21 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL); zval_dtor(&z_ret); +#if (PHP_MAJOR_VERSION < 7) + redis = (redis_object *)zend_objects_get_address(&ra->redis[i] TSRMLS_CC); +#else + redis = (redis_object *)((char *)Z_OBJ_P(&ra->redis[i]) - XtOffsetOf(redis_object, std)); +#endif + /* create socket */ - redis_sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); + redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); if (!b_lazy_connect) { /* connect */ - redis_sock_server_open(redis_sock, 1 TSRMLS_CC); + redis_sock_server_open(redis->sock, 1 TSRMLS_CC); } - /* attach */ -#if (PHP_MAJOR_VERSION < 7) - int id; -#if PHP_VERSION_ID >= 50400 - id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); -#else - id = zend_list_insert(redis_sock, le_redis_sock); -#endif - add_property_resource(&ra->redis[i], "socket", id); -#else - zval *id = zend_list_insert(redis_sock, le_redis_sock TSRMLS_CC); - add_property_resource(&ra->redis[i], "socket", Z_RES_P(id)); -#endif - ra->count = ++i; } diff --git a/redis_array_impl.h b/redis_array_impl.h index db8d7aff31..b385986949 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -7,7 +7,6 @@ #include #endif -#include "common.h" #include "redis_array.h" RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); From 88d870e0d9c532fb095678271e06c679b0b3fc83 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 10 Feb 2017 23:12:22 +0200 Subject: [PATCH 0700/1986] refactoring redis_sock_get and redis_array_get return pointer to RedisSock/RedisArray structure instead of assign by pointer pointer --- common.h | 4 +- library.h | 2 +- redis.c | 125 ++++++++++++++++++++++++-------------------------- redis_array.c | 50 ++++++++++---------- 4 files changed, 89 insertions(+), 92 deletions(-) diff --git a/common.h b/common.h index ff1d6717a5..bf5e4cc53c 100644 --- a/common.h +++ b/common.h @@ -576,7 +576,7 @@ typedef enum _PUBSUB_TYPE { * function is redis__cmd */ #define REDIS_PROCESS_CMD(cmdname, resp_func) \ RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || \ + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || \ redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, \ &cmd, &cmd_len, NULL, &ctx)==FAILURE) { \ RETURN_FALSE; \ @@ -591,7 +591,7 @@ typedef enum _PUBSUB_TYPE { * and keyword which is passed to us*/ #define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || \ + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || \ cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, \ &cmd_len, NULL, &ctx)==FAILURE) { \ RETURN_FALSE; \ diff --git a/library.h b/library.h index 7e68fba21a..de4991fad6 100644 --- a/library.h +++ b/library.h @@ -59,7 +59,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC); -PHP_REDIS_API int redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int nothrow); +PHP_REDIS_API RedisSock *redis_sock_get(zval *id TSRMLS_DC, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); diff --git a/redis.c b/redis.c index 16bb7d5e17..b27e1dbbef 100644 --- a/redis.c +++ b/redis.c @@ -495,8 +495,8 @@ create_redis_object(zend_class_entry *ce TSRMLS_DC) } #endif -static zend_always_inline int -redis_sock_get_instance(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) +static zend_always_inline RedisSock * +redis_sock_get_instance(zval *id TSRMLS_DC, int no_throw) { redis_object *redis; @@ -507,36 +507,36 @@ redis_sock_get_instance(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw redis = (redis_object *)((char *)Z_OBJ_P(id) - XtOffsetOf(redis_object, std)); #endif if (redis->sock) { - *redis_sock = redis->sock; - return 0; + return redis->sock; } } // Throw an exception unless we've been requested not to if (!no_throw) { zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); } - return -1; + return NULL; } /** * redis_sock_get */ -PHP_REDIS_API int -redis_sock_get(zval *id, RedisSock **redis_sock TSRMLS_DC, int no_throw) +PHP_REDIS_API RedisSock * +redis_sock_get(zval *id TSRMLS_DC, int no_throw) { - if (redis_sock_get_instance(id, redis_sock TSRMLS_CC, no_throw) < 0) { - return -1; + RedisSock *redis_sock; + + if ((redis_sock = redis_sock_get_instance(id TSRMLS_CC, no_throw)) == NULL) { + return NULL; } - if ((*redis_sock)->lazy_connect) - { - (*redis_sock)->lazy_connect = 0; - if (redis_sock_server_open(*redis_sock, 1 TSRMLS_CC) < 0) { - return -1; + if (redis_sock->lazy_connect) { + redis_sock->lazy_connect = 0; + if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { + return NULL; } } - return 0; + return redis_sock; } /** @@ -551,7 +551,7 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) // return NULL if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) || - (redis_sock_get(object, &redis_sock TSRMLS_CC, 1) < 0) || + (redis_sock = redis_sock_get(object TSRMLS_CC, 1)) == NULL || redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { return NULL; @@ -741,7 +741,7 @@ PHP_METHOD(Redis,__destruct) { // Grab our socket RedisSock *redis_sock; - if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 1) < 0) { + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 1)) == NULL) { RETURN_FALSE; } @@ -775,7 +775,7 @@ PHP_METHOD(Redis, pconnect) } else { /* reset multi/exec state if there is one. */ RedisSock *redis_sock; - if (redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -1026,7 +1026,7 @@ PHP_METHOD(Redis, getMultiple) } /* We'll need the socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -1330,7 +1330,7 @@ PHP_METHOD(Redis, sRandMember) RedisSock *redis_sock; // Grab our socket, validate call - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &cmd, &cmd_len, NULL, NULL, &have_count)==FAILURE) { @@ -1415,7 +1415,7 @@ PHP_METHOD(Redis, sort) { RedisSock *redis_sock; // Grab socket, handle command construction - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0 || + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &have_store, &cmd, &cmd_len, NULL, NULL)==FAILURE) { @@ -1469,10 +1469,7 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { - RETURN_FALSE; - } - if(key_len == 0) { + if (key_len == 0 || (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -1769,7 +1766,7 @@ PHP_METHOD(Redis, info) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -1806,7 +1803,7 @@ PHP_METHOD(Redis, select) { RETURN_FALSE; } - if (dbNumber < 0 || redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if (dbNumber < 0 || (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -1847,7 +1844,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -1967,7 +1964,7 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, RedisSock *redis_sock; int withscores=0; - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2236,7 +2233,7 @@ PHP_METHOD(Redis, multi) /* if the flag is activated, send the command, the reply will be "QUEUED" * or -ERR */ - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2284,7 +2281,7 @@ PHP_METHOD(Redis, discard) RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2373,7 +2370,7 @@ PHP_METHOD(Redis, exec) if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE || - redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0 + (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL ) { RETURN_FALSE; } @@ -2476,7 +2473,7 @@ PHP_METHOD(Redis, pipeline) if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE || - redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0 + (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL ) { RETURN_FALSE; } @@ -2540,7 +2537,7 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, &object, redis_ce, &array) == FAILURE) { RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2626,7 +2623,7 @@ PHP_METHOD(Redis, slaveof) { RETURN_FALSE; } - if (port < 0 || redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if (port < 0 || (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2654,7 +2651,7 @@ PHP_METHOD(Redis, object) char *cmd; int cmd_len; REDIS_REPLY_TYPE rtype; - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2683,28 +2680,28 @@ PHP_METHOD(Redis, object) /* }}} */ /* {{{ proto string Redis::getOption($option) */ -PHP_METHOD(Redis, getOption) { - RedisSock *redis_sock = NULL; +PHP_METHOD(Redis, getOption) +{ + RedisSock *redis_sock; - if (redis_sock_get_instance(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } - redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL); + redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL); } /* }}} */ /* {{{ proto string Redis::setOption(string $option, mixed $value) */ -PHP_METHOD(Redis, setOption) { - RedisSock *redis_sock = NULL; +PHP_METHOD(Redis, setOption) +{ + RedisSock *redis_sock; - if (redis_sock_get_instance(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } - redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL); + redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL); } /* }}} */ @@ -2734,7 +2731,7 @@ PHP_METHOD(Redis, config) RETURN_FALSE; } - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2796,7 +2793,7 @@ PHP_METHOD(Redis, slowlog) { } /* Make sure we can grab our redis socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2844,7 +2841,7 @@ PHP_METHOD(Redis, wait) { } /* Grab our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -2978,7 +2975,7 @@ PHP_METHOD(Redis, pubsub) { } /* Grab our socket context object */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3093,7 +3090,7 @@ PHP_METHOD(Redis, evalsha) } /* Attempt to grab socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3132,7 +3129,7 @@ PHP_METHOD(Redis, eval) } /* Attempt to grab socket */ - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3186,7 +3183,7 @@ PHP_METHOD(Redis, script) { char *cmd; /* Attempt to grab our socket */ - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3291,7 +3288,7 @@ PHP_METHOD(Redis, migrate) { } /* Grabg our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3337,7 +3334,7 @@ PHP_METHOD(Redis, migrate) { PHP_METHOD(Redis, _prefix) { RedisSock *redis_sock; - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0)<0) { + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3349,7 +3346,7 @@ PHP_METHOD(Redis, _serialize) { RedisSock *redis_sock; // Grab socket - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3361,7 +3358,7 @@ PHP_METHOD(Redis, _unserialize) { RedisSock *redis_sock; // Grab socket - if(redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3382,7 +3379,7 @@ PHP_METHOD(Redis, getLastError) { } // Grab socket - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3406,7 +3403,7 @@ PHP_METHOD(Redis, clearLastError) { RETURN_FALSE; } // Grab socket - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3432,7 +3429,7 @@ PHP_METHOD(Redis, getMode) { } /* Grab socket */ - if (redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3573,7 +3570,7 @@ PHP_METHOD(Redis, client) { } /* Grab our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3625,8 +3622,8 @@ PHP_METHOD(Redis, rawcommand) { efree(z_args); RETURN_FALSE; } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len TSRMLS_CC) < 0 || - redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) - { + (redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL + ) { if (cmd) efree(cmd); efree(z_args); RETURN_FALSE; @@ -3743,7 +3740,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } /* Grab our socket */ - if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) { + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } diff --git a/redis_array.c b/redis_array.c index de516b0039..132bb01a2c 100644 --- a/redis_array.c +++ b/redis_array.c @@ -185,7 +185,8 @@ create_redis_array_object(zend_class_entry *ce TSRMLS_DC) { redis_array_object *obj = ecalloc(1, sizeof(redis_array_object) + zend_object_properties_size(ce)); - memset(obj, 0, sizeof(redis_array_object)); + obj->ra = NULL; + zend_object_std_init(&obj->std, ce TSRMLS_CC); object_properties_init(&obj->std, ce); @@ -201,8 +202,8 @@ create_redis_array_object(zend_class_entry *ce TSRMLS_DC) /** * redis_array_get */ -PHP_REDIS_API int -redis_array_get(zval *id, RedisArray **ra TSRMLS_DC) +PHP_REDIS_API RedisArray * +redis_array_get(zval *id TSRMLS_DC) { redis_array_object *obj; @@ -213,11 +214,10 @@ redis_array_get(zval *id, RedisArray **ra TSRMLS_DC) obj = (redis_array_object *)((char *)Z_OBJ_P(id) - XtOffsetOf(redis_array_object, std)); #endif if (obj->ra) { - *ra = obj->ra; - return 0; + return obj->ra; } } - return -1; + return NULL; } uint32_t rcrc32(const char *s, size_t sz) { @@ -486,7 +486,7 @@ PHP_METHOD(RedisArray, __call) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -504,7 +504,7 @@ PHP_METHOD(RedisArray, _hosts) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -528,7 +528,7 @@ PHP_METHOD(RedisArray, _target) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -553,7 +553,7 @@ PHP_METHOD(RedisArray, _instance) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -575,7 +575,7 @@ PHP_METHOD(RedisArray, _function) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -593,7 +593,7 @@ PHP_METHOD(RedisArray, _distributor) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -613,7 +613,7 @@ PHP_METHOD(RedisArray, _rehash) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -635,7 +635,7 @@ static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *metho RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -704,7 +704,7 @@ PHP_METHOD(RedisArray, keys) } /* Make sure we can grab our RedisArray object */ - if(redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -747,7 +747,7 @@ PHP_METHOD(RedisArray, getOption) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -786,7 +786,7 @@ PHP_METHOD(RedisArray, setOption) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -825,7 +825,7 @@ PHP_METHOD(RedisArray, select) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -912,7 +912,7 @@ PHP_METHOD(RedisArray, mget) HashTable *h_keys; zval **argv; - if (redis_array_get(getThis(), &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -1066,7 +1066,7 @@ PHP_METHOD(RedisArray, mset) zend_string *zkey; ulong idx; - if (redis_array_get(getThis(), &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -1184,7 +1184,7 @@ PHP_METHOD(RedisArray, del) long total = 0; int free_zkeys = 0; - if (redis_array_get(getThis(), &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { RETURN_FALSE; } /* Multi/exec support */ @@ -1325,7 +1325,7 @@ PHP_METHOD(RedisArray, multi) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { RETURN_FALSE; } @@ -1359,7 +1359,7 @@ PHP_METHOD(RedisArray, exec) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0 || !ra->z_multi_exec) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } @@ -1380,7 +1380,7 @@ PHP_METHOD(RedisArray, discard) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0 || !ra->z_multi_exec) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } @@ -1401,7 +1401,7 @@ PHP_METHOD(RedisArray, unwatch) RETURN_FALSE; } - if (redis_array_get(object, &ra TSRMLS_CC) < 0 || !ra->z_multi_exec) { + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } From 162d8820bc6e179419fe5db593ec47122e8d6129 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 14 Feb 2017 16:34:26 -0800 Subject: [PATCH 0701/1986] Make sure redisCluster members are all initialized on (re)creation Fixes #1105 --- redis_cluster.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index a33b4c00a3..44c5e0e267 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -282,23 +282,19 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { redisCluster *cluster; // Allocate our actual struct - cluster = emalloc(sizeof(redisCluster)); - memset(cluster, 0, sizeof(redisCluster)); + cluster = ecalloc(1, sizeof(redisCluster)); #else zend_object * create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { redisCluster *cluster; // Allocate our actual struct - cluster = emalloc(sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); + cluster = ecalloc(1, sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); #endif // We're not currently subscribed anywhere cluster->subscribed_slot = -1; - // Assume we're up initially - cluster->clusterdown = 0; - // Allocate our RedisSock we'll use to store prefix/serialization flags cluster->flags = ecalloc(1, sizeof(RedisSock)); From aa6ff77a3e546defae5a208032c62a3138a4ab2c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 14 Feb 2017 17:56:48 -0800 Subject: [PATCH 0702/1986] Fix memory leak --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 923dda4e45..a40c00d256 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2036,7 +2036,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // Return our array if(CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_result, 1, 0); + RETVAL_ZVAL(z_result, 0, 1); } else { add_next_index_zval(&c->multi_resp, z_result); } From 43da980da96fdcf1771505dc0781f2c9fa9d5c80 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 17 Feb 2017 00:12:12 +0200 Subject: [PATCH 0703/1986] redis_check_eof refactoring --- library.c | 76 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/library.c b/library.c index 7bf10bef56..12c729e8ba 100644 --- a/library.c +++ b/library.c @@ -140,10 +140,10 @@ PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { } } -PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) +PHP_REDIS_API int +redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) { - int eof; - int count = 0; + int count; if (!redis_sock->stream) { return -1; @@ -164,24 +164,24 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) * Bug fix of php: https://github.com/php/php-src/pull/1456 * */ errno = 0; - eof = php_stream_eof(redis_sock->stream); - for (; eof; count++) { - if((MULTI == redis_sock->mode) || redis_sock->watching || count == 10) { - /* too many failures */ - if(redis_sock->stream) { /* close stream if still here */ - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - } - if(!no_throw) { - zend_throw_exception(redis_exception_ce, "Connection lost", - 0 TSRMLS_CC); - } - return -1; + if (php_stream_eof(redis_sock->stream) == 0) { + /* Success */ + return 0; + } else if (redis_sock->mode == MULTI || redis_sock->watching) { + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); + if (!no_throw) { + zend_throw_exception(redis_exception_ce, + "Connection lost and socket is in MULTI/watching mode", + 0 TSRMLS_CC); } - if(redis_sock->stream) { /* close existing stream before reconnecting */ + return -1; + } + /* TODO: configurable max retry count */ + for (count = 0; count < 10; ++count) { + /* close existing stream before reconnecting */ + if (redis_sock->stream) { redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; } // Wait for a while before trying to reconnect if (redis_sock->retry_interval) { @@ -189,28 +189,32 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); usleep(retry_interval); } - redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */ - if(redis_sock->stream) { /* check for EOF again. */ + /* reconnect */ + if (redis_sock_connect(redis_sock TSRMLS_CC) == 0) { + /* check for EOF again. */ errno = 0; - eof = php_stream_eof(redis_sock->stream); + if (php_stream_eof(redis_sock->stream) == 0) { + /* If we're using a password, attempt a reauthorization */ + if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) { + break; + } + /* If we're using a non-zero db, reselect it */ + if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) { + break; + } + /* Success */ + return 0; + } } } - - /* We've connected if we have a count */ - if (count) { - /* If we're using a password, attempt a reauthorization */ - if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) { - return -1; - } - - /* If we're using a non-zero db, reselect it */ - if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) { - return -1; - } + /* close stream if still here */ + if (redis_sock->stream) { + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); } - - /* Success */ - return 0; + if (!no_throw) { + zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); + } + return -1; } From be5c1f58d100bc37d47183603c3cc994fa651552 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 17 Feb 2017 09:53:32 +0200 Subject: [PATCH 0704/1986] segfault fix --- redis.c | 1 + 1 file changed, 1 insertion(+) diff --git a/redis.c b/redis.c index b27e1dbbef..f7b52113d1 100644 --- a/redis.c +++ b/redis.c @@ -844,6 +844,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) if (redis_sock_server_open(redis->sock, 1 TSRMLS_CC) < 0) { redis_free_socket(redis->sock); + redis->sock = NULL; return FAILURE; } From 635c3a651ba6db8796942a2dc25bfa358a573282 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 23 Feb 2017 13:39:05 +0200 Subject: [PATCH 0705/1986] RedisArray segfault fix --- redis_array_impl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 01f586ca32..0277742624 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -341,7 +341,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->pconnect = b_pconnect; ra->connect_timeout = connect_timeout; - if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL) { + if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { zval_dtor(&ra->redis[i]); efree(ra->hosts[i]); From 55a2a06e813cdc01c70a00c2ab30d544430c4e60 Mon Sep 17 00:00:00 2001 From: doodoori2 Date: Mon, 27 Feb 2017 22:37:18 +0900 Subject: [PATCH 0706/1986] Remove dupliate header --- php_redis.h | 1 - 1 file changed, 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index bfc7a5ff84..ef665ea6bb 100644 --- a/php_redis.h +++ b/php_redis.h @@ -114,7 +114,6 @@ PHP_METHOD(Redis, zDelete); PHP_METHOD(Redis, zRange); PHP_METHOD(Redis, zRevRange); PHP_METHOD(Redis, zRangeByScore); -PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zRevRangeByScore); PHP_METHOD(Redis, zRangeByLex); PHP_METHOD(Redis, zRevRangeByLex); From c4b11a9368fa26ba108892ba7eb0da115f2b613a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 27 Feb 2017 16:41:16 +0200 Subject: [PATCH 0707/1986] put macros in order --- common.h | 38 +++++++------------------------------- library.c | 54 +++++++++++++++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 58 deletions(-) diff --git a/common.h b/common.h index bf5e4cc53c..681460aa1b 100644 --- a/common.h +++ b/common.h @@ -467,36 +467,12 @@ typedef enum _PUBSUB_TYPE { #define REDIS_ERR_SYNC_MSG "MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'" #define REDIS_ERR_SYNC_KW "MASTERDOWN" -#define IF_MULTI() if(redis_sock->mode == MULTI) -#define IF_MULTI_OR_ATOMIC() if(redis_sock->mode == MULTI || redis_sock->mode == ATOMIC)\ - -#define IF_MULTI_OR_PIPELINE() if(redis_sock->mode == MULTI || redis_sock->mode == PIPELINE) -#define IF_PIPELINE() if(redis_sock->mode == PIPELINE) -#define IF_NOT_PIPELINE() if(redis_sock->mode != PIPELINE) -#define IF_NOT_MULTI() if(redis_sock->mode != MULTI) -#define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC) -#define IF_ATOMIC() if(redis_sock->mode == ATOMIC) -#define ELSE_IF_MULTI() else IF_MULTI() { \ - if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) { \ - RETURN_ZVAL(getThis(), 1, 0);\ - } else { \ - RETURN_FALSE; \ - } \ -} - -#define ELSE_IF_PIPELINE() else IF_PIPELINE() { \ - RETURN_ZVAL(getThis(), 1, 0);\ -} - -#define MULTI_RESPONSE(callback) IF_MULTI_OR_PIPELINE() { \ - fold_item *f1, *current; \ - f1 = malloc(sizeof(fold_item)); \ - f1->fun = (void *)callback; \ - f1->next = NULL; \ - current = redis_sock->current;\ - if(current) current->next = f1; \ - redis_sock->current = f1; \ - } +#define IF_ATOMIC() if (redis_sock->mode == ATOMIC) +#define IF_NOT_ATOMIC() if (redis_sock->mode != ATOMIC) +#define IF_MULTI() if (redis_sock->mode == MULTI) +#define IF_NOT_MULTI() if (redis_sock->mode != MULTI) +#define IF_PIPELINE() if (redis_sock->mode == PIPELINE) +#define IF_NOT_PIPELINE() if (redis_sock->mode != PIPELINE) #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) request_item *tmp; \ struct request_item *current_request;\ @@ -521,7 +497,7 @@ typedef enum _PUBSUB_TYPE { } #define REDIS_SAVE_CALLBACK(callback, closure_context) \ - IF_MULTI_OR_PIPELINE() { \ + IF_NOT_ATOMIC() { \ fold_item *f1, *current; \ f1 = malloc(sizeof(fold_item)); \ f1->fun = (void *)callback; \ diff --git a/library.c b/library.c index 12c729e8ba..497037bc3b 100644 --- a/library.c +++ b/library.c @@ -848,7 +848,7 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi double ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); return; } else { @@ -858,7 +858,7 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi ret = atof(response); efree(response); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_double(z_tab, ret); } else { RETURN_DOUBLE(ret); @@ -871,7 +871,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * long l; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); return; } else { @@ -894,7 +894,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } efree(response); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_long(z_tab, l); } else { RETURN_LONG(l); @@ -917,7 +917,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * /* Free source response */ efree(response); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_ret); } else { RETVAL_ZVAL(z_ret, 0, 1); @@ -999,7 +999,7 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo efree(resp); /* Return or append depending if we're atomic */ - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_ret); } else { RETVAL_ZVAL(z_ret, 0, 1); @@ -1127,7 +1127,7 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (ret && success_callback != NULL) { success_callback(redis_sock); } - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, ret); } else { RETURN_BOOL(ret); @@ -1153,7 +1153,7 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); return; } else { @@ -1167,7 +1167,7 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, #else long long ret = atoll(response + 1); #endif - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { if(ret > LONG_MAX) { /* overflow */ add_next_index_stringl(z_tab, response + 1, response_len - 1); } else { @@ -1184,7 +1184,7 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, } } else { efree(response); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_null(z_tab); } else { RETURN_FALSE; @@ -1269,7 +1269,7 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } if(inbuf[0] != '*') { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); } else { RETVAL_FALSE; @@ -1290,7 +1290,7 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Zip keys and values */ array_zip_values_and_scores(redis_sock, z_multi_result, decode TSRMLS_CC); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_multi_result); } else { RETVAL_ZVAL(z_multi_result, 0, 1); @@ -1341,7 +1341,7 @@ PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); return; } else { @@ -1351,7 +1351,7 @@ PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, ret = response[1]; efree(response); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { if(ret == '1') { add_next_index_bool(z_tab, 1); } else { @@ -1374,13 +1374,13 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); return; } RETURN_FALSE; } - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { zval zv, *z = &zv; if (redis_unserialize(redis_sock, response, response_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) @@ -1411,13 +1411,13 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); return; } RETURN_FALSE; } - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_stringl(z_tab, response, response_len); } else { RETVAL_STRINGL(response, response_len); @@ -1434,7 +1434,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock /* Add or return false if we can't read from the socket */ if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC))==NULL) { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); return; } @@ -1482,7 +1482,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock efree(resp); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_result); } else { RETVAL_ZVAL(z_result, 0, 1); @@ -1736,7 +1736,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, } if(inbuf[0] != '*') { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); } else { if (inbuf[0] == '-') { @@ -1757,7 +1757,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_multi_result, numElems, UNSERIALIZE_ALL); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_multi_result); } else { RETVAL_ZVAL(z_multi_result, 0, 1); @@ -1783,7 +1783,7 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock } if(inbuf[0] != '*') { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); } else { if (inbuf[0] == '-') { @@ -1804,7 +1804,7 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_multi_result, numElems, UNSERIALIZE_NONE); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_multi_result); } else { RETVAL_ZVAL(z_multi_result, 0, 1); @@ -1871,7 +1871,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc } if(inbuf[0] != '*') { - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); } else { RETVAL_FALSE; @@ -1906,7 +1906,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc } efree(z_keys); - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_multi_result); } else { RETVAL_ZVAL(z_multi_result, 0, 1); @@ -2357,7 +2357,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - IF_MULTI_OR_PIPELINE() { + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_ret); } else { /* Set our return value */ From d4c2663f3947a86035bef59aaafeb6b1a4c2abfd Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 28 Feb 2017 18:05:49 +0200 Subject: [PATCH 0708/1986] remove fold_this_item --- redis.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/redis.c b/redis.c index f7b52113d1..10e78df49f 100644 --- a/redis.c +++ b/redis.c @@ -2445,25 +2445,18 @@ PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { return ret; } -PHP_REDIS_API void fold_this_item(INTERNAL_FUNCTION_PARAMETERS, fold_item *item, - RedisSock *redis_sock, zval *z_tab) -{ - item->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, item->ctx - TSRMLS_CC); -} - PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems) { + fold_item *fi; - fold_item *current; - for (current = redis_sock->head; current; current = current->next) { - fold_this_item(INTERNAL_FUNCTION_PARAM_PASSTHRU, current, redis_sock, - z_tab); + for (fi = redis_sock->head; fi; fi = fi->next) { + fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, + fi->ctx TSRMLS_CC); } - redis_sock->current = current; + redis_sock->current = fi; return 0; } From fc2453c8b62db7659949d8b35eeb72a25e1b610f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 1 Mar 2017 23:08:16 +0200 Subject: [PATCH 0709/1986] refactoring Modify redis_response_enqueued function. Wrap macros into do/while blocks. --- common.h | 51 ++++++++++++++++++++++++--------------------------- redis.c | 18 +++++++++--------- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/common.h b/common.h index 681460aa1b..d6b25af857 100644 --- a/common.h +++ b/common.h @@ -474,21 +474,20 @@ typedef enum _PUBSUB_TYPE { #define IF_PIPELINE() if (redis_sock->mode == PIPELINE) #define IF_NOT_PIPELINE() if (redis_sock->mode != PIPELINE) -#define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) request_item *tmp; \ - struct request_item *current_request;\ - tmp = malloc(sizeof(request_item));\ +#define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \ + request_item *tmp = malloc(sizeof(request_item)); \ tmp->request_str = calloc(cmd_len, 1);\ memcpy(tmp->request_str, cmd, cmd_len);\ tmp->request_size = cmd_len;\ tmp->next = NULL;\ - current_request = redis_sock->pipeline_current; \ - if(current_request) {\ - current_request->next = tmp;\ + if (redis_sock->pipeline_current) { \ + redis_sock->pipeline_current->next = tmp; \ } \ redis_sock->pipeline_current = tmp; \ if(NULL == redis_sock->pipeline_head) { \ redis_sock->pipeline_head = redis_sock->pipeline_current;\ - } + } \ +} while (0) #define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) \ if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { \ @@ -496,29 +495,27 @@ typedef enum _PUBSUB_TYPE { RETURN_FALSE; \ } -#define REDIS_SAVE_CALLBACK(callback, closure_context) \ - IF_NOT_ATOMIC() { \ - fold_item *f1, *current; \ - f1 = malloc(sizeof(fold_item)); \ - f1->fun = (void *)callback; \ - f1->ctx = closure_context; \ - f1->next = NULL; \ - current = redis_sock->current;\ - if(current) current->next = f1; \ - redis_sock->current = f1; \ - if(NULL == redis_sock->head) { \ - redis_sock->head = redis_sock->current;\ - }\ -} +#define REDIS_SAVE_CALLBACK(callback, closure_context) do { \ + fold_item *f1 = malloc(sizeof(fold_item)); \ + f1->fun = (void *)callback; \ + f1->ctx = closure_context; \ + f1->next = NULL; \ + if (redis_sock->current) { \ + redis_sock->current->next = f1; \ + } \ + redis_sock->current = f1; \ + if (NULL == redis_sock->head) { \ + redis_sock->head = redis_sock->current; \ + } \ +} while (0) #define REDIS_ELSE_IF_MULTI(function, closure_context) \ else IF_MULTI() { \ - if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\ - REDIS_SAVE_CALLBACK(function, closure_context); \ - RETURN_ZVAL(getThis(), 1, 0);\ - } else {\ - RETURN_FALSE;\ - }\ + if (redis_response_enqueued(redis_sock TSRMLS_CC) != SUCCESS) { \ + RETURN_FALSE; \ + } \ + REDIS_SAVE_CALLBACK(function, closure_context); \ + RETURN_ZVAL(getThis(), 1, 0);\ } #define REDIS_ELSE_IF_PIPELINE(function, closure_context) \ diff --git a/redis.c b/redis.c index 10e78df49f..a02f789c66 100644 --- a/redis.c +++ b/redis.c @@ -2430,18 +2430,18 @@ PHP_METHOD(Redis, exec) } } -PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) { +PHP_REDIS_API int +redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) +{ char *resp; - int resp_len, ret = 0; - - if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { - return 0; - } + int resp_len, ret = FAILURE; - if(strncmp(resp, "+QUEUED", 7) == 0) { - ret = 1; + if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) != NULL) { + if (strncmp(resp, "+QUEUED", 7) == 0) { + ret = SUCCESS; + } + efree(resp); } - efree(resp); return ret; } From 65e579821b50b7288596d59aa6fcc7b034eb4cf6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 3 Mar 2017 15:17:51 +0200 Subject: [PATCH 0710/1986] REDIS_PROCESS_RESPONSE_CLOSURE --- common.h | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/common.h b/common.h index d6b25af857..528f91a532 100644 --- a/common.h +++ b/common.h @@ -509,21 +509,6 @@ typedef enum _PUBSUB_TYPE { } \ } while (0) -#define REDIS_ELSE_IF_MULTI(function, closure_context) \ - else IF_MULTI() { \ - if (redis_response_enqueued(redis_sock TSRMLS_CC) != SUCCESS) { \ - RETURN_FALSE; \ - } \ - REDIS_SAVE_CALLBACK(function, closure_context); \ - RETURN_ZVAL(getThis(), 1, 0);\ -} - -#define REDIS_ELSE_IF_PIPELINE(function, closure_context) \ - else IF_PIPELINE() { \ - REDIS_SAVE_CALLBACK(function, closure_context); \ - RETURN_ZVAL(getThis(), 1, 0); \ -} - #define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \ IF_PIPELINE() { \ PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \ @@ -533,11 +518,17 @@ typedef enum _PUBSUB_TYPE { efree(cmd); #define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ - REDIS_ELSE_IF_MULTI(function, closure_context) \ - REDIS_ELSE_IF_PIPELINE(function, closure_context); + IF_MULTI() { \ + if (redis_response_enqueued(redis_sock TSRMLS_CC) != SUCCESS) { \ + RETURN_FALSE; \ + } \ + } \ + REDIS_SAVE_CALLBACK(function, closure_context); \ + RETURN_ZVAL(getThis(), 1, 0); \ -#define REDIS_PROCESS_RESPONSE(function) \ - REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) +#define REDIS_PROCESS_RESPONSE(function) else { \ + REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) \ +} /* Clear redirection info */ #define REDIS_MOVED_CLEAR(redis_sock) \ @@ -557,8 +548,9 @@ typedef enum _PUBSUB_TYPE { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ IF_ATOMIC() { \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \ - } \ - REDIS_PROCESS_RESPONSE_CLOSURE(resp_func,ctx); + } else { \ + REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \ + } /* Process a command but with a specific command building function * and keyword which is passed to us*/ @@ -572,8 +564,9 @@ typedef enum _PUBSUB_TYPE { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ IF_ATOMIC() { \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \ - } \ - REDIS_PROCESS_RESPONSE_CLOSURE(resp_func,ctx); + } else { \ + REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \ + } #define REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock) \ redis_stream_close(redis_sock TSRMLS_CC); \ From 542fc30d1e0c0872388211945a38994aea31aeed Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 3 Mar 2017 16:42:11 +0200 Subject: [PATCH 0711/1986] free_reply_callbacks --- common.h | 3 --- redis.c | 62 ++++++++++++++++++++++++++++---------------------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/common.h b/common.h index 528f91a532..1772538f7a 100644 --- a/common.h +++ b/common.h @@ -658,7 +658,4 @@ typedef struct { } redis_object; #endif -void -free_reply_callbacks(zval *z_this, RedisSock *redis_sock); - #endif diff --git a/redis.c b/redis.c index a02f789c66..6c8845347c 100644 --- a/redis.c +++ b/redis.c @@ -424,6 +424,30 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { return result; } +static void +free_reply_callbacks(RedisSock *redis_sock) +{ + fold_item *fi; + request_item *ri; + + for (fi = redis_sock->head; fi; ) { + fold_item *fi_next = fi->next; + free(fi); + fi = fi_next; + } + redis_sock->head = NULL; + redis_sock->current = NULL; + + for (ri = redis_sock->pipeline_head; ri; ) { + struct request_item *ri_next = ri->next; + free(ri->request_str); + free(ri); + ri = ri_next; + } + redis_sock->pipeline_head = NULL; + redis_sock->pipeline_current = NULL; +} + #if (PHP_MAJOR_VERSION < 7) void free_redis_object(void *object TSRMLS_DC) @@ -750,7 +774,7 @@ PHP_METHOD(Redis,__destruct) { // Discard any multi commands, and free any callbacks that have been // queued send_discard_static(redis_sock TSRMLS_CC); - free_reply_callbacks(getThis(), redis_sock); + free_reply_callbacks(redis_sock); } } @@ -2266,7 +2290,7 @@ PHP_METHOD(Redis, multi) RETURN_FALSE; } IF_PIPELINE() { - free_reply_callbacks(getThis(), redis_sock); + free_reply_callbacks(redis_sock); RETURN_ZVAL(getThis(), 1, 0); } } @@ -2300,7 +2324,7 @@ redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, redis_sock, return_value, 0); /* free allocated function/request memory */ - free_reply_callbacks(getThis(), redis_sock); + free_reply_callbacks(redis_sock); return 0; @@ -2336,30 +2360,6 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME return 0; } -void -free_reply_callbacks(zval *z_this, RedisSock *redis_sock) { - - fold_item *fi; - fold_item *head = redis_sock->head; - request_item *ri; - - for(fi = head; fi; ) { - fold_item *fi_next = fi->next; - free(fi); - fi = fi_next; - } - redis_sock->head = NULL; - redis_sock->current = NULL; - - for(ri = redis_sock->pipeline_head; ri; ) { - struct request_item *ri_next = ri->next; - free(ri->request_str); - free(ri); - ri = ri_next; - } - redis_sock->pipeline_head = NULL; - redis_sock->pipeline_current = NULL; -} /* exec */ PHP_METHOD(Redis, exec) @@ -2387,12 +2387,12 @@ PHP_METHOD(Redis, exec) redis_sock) < 0) { zval_dtor(return_value); - free_reply_callbacks(object, redis_sock); + free_reply_callbacks(redis_sock); redis_sock->mode = ATOMIC; redis_sock->watching = 0; RETURN_FALSE; } - free_reply_callbacks(object, redis_sock); + free_reply_callbacks(redis_sock); redis_sock->mode = ATOMIC; redis_sock->watching = 0; } @@ -2426,7 +2426,7 @@ PHP_METHOD(Redis, exec) array_init(return_value); } redis_sock->mode = ATOMIC; - free_reply_callbacks(object, redis_sock); + free_reply_callbacks(redis_sock); } } @@ -2478,7 +2478,7 @@ PHP_METHOD(Redis, pipeline) /* NB : we keep the function fold, to detect the last function. * We need the response format of the n - 1 command. So, we can delete * when n > 2, the { 1 .. n - 2} commands */ - free_reply_callbacks(getThis(), redis_sock); + free_reply_callbacks(redis_sock); } RETURN_ZVAL(getThis(), 1, 0); } From d6756ca77fb7c5c60d3fccad396dc15610330d4c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 4 Mar 2017 00:02:18 +0200 Subject: [PATCH 0712/1986] Remove redis_sock_read_multibulk_pipeline_reply function --- redis.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/redis.c b/redis.c index 6c8845347c..7fc759c387 100644 --- a/redis.c +++ b/redis.c @@ -2314,22 +2314,6 @@ PHP_METHOD(Redis, discard) redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } -PHP_REDIS_API int -redis_sock_read_multibulk_pipeline_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock) -{ - array_init(return_value); - - redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, return_value, 0); - - /* free allocated function/request memory */ - free_reply_callbacks(redis_sock); - - return 0; - -} - /* redis_sock_read_multibulk_multi_reply */ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) @@ -2378,8 +2362,7 @@ PHP_METHOD(Redis, exec) IF_MULTI() { cmd_len = redis_cmd_format_static(&cmd, "EXEC", ""); - - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); if(redis_sock_read_multibulk_multi_reply( @@ -2414,11 +2397,12 @@ PHP_METHOD(Redis, exec) offset += ri->request_size; } request[total] = '\0'; - if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0 || - redis_sock_read_multibulk_pipeline_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0 - ) { + if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0) { ZVAL_FALSE(return_value); + } else { + array_init(return_value); + redis_sock_read_multibulk_multi_reply_loop( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, return_value, 0); } efree(request); } else { From c52077b7b04927c95b450494f39135cae11b58d9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 11 Jan 2017 23:09:10 +0200 Subject: [PATCH 0713/1986] Issue #1087 `hStrLen` command --- README.markdown | 10 ++++++++++ php_redis.h | 1 + redis.c | 7 +++++++ redis_cluster.c | 8 ++++++++ redis_cluster.h | 1 + redis_commands.c | 27 +++++++++++++++++++++++++++ tests/RedisTest.php | 9 +++++++++ 7 files changed, 63 insertions(+) diff --git a/README.markdown b/README.markdown index e66f58999f..5431e32ce5 100644 --- a/README.markdown +++ b/README.markdown @@ -1355,6 +1355,7 @@ $redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE fl * [hSetNx](#hsetnx) - Set the value of a hash field, only if the field does not exist * [hVals](#hvals) - Get all the values in a hash * [hScan](#hscan) - Scan a hash key for members +* [hStrLen](#hstrlen) - Get the string length of the value associated with field in the hash ### hSet ----- @@ -1639,6 +1640,15 @@ while($arr_keys = $redis->hScan('hash', $it)) { } ~~~ +##### hStrLen +----- + **Description**_: Get the string length of the value associated with field in the hash stored at key. +##### *Parameters* +*key*: String +*field*: String +##### *Return value* +*LONG* the string length of the value associated with field, or zero when field is not present in the hash or key does not exist at all. + ## Lists * [blPop, brPop](#blpop-brpop) - Remove and get the first/last element in a list diff --git a/php_redis.h b/php_redis.h index eb701f5a7a..dd0f552821 100644 --- a/php_redis.h +++ b/php_redis.h @@ -174,6 +174,7 @@ PHP_METHOD(Redis, hIncrBy); PHP_METHOD(Redis, hIncrByFloat); PHP_METHOD(Redis, hMset); PHP_METHOD(Redis, hMget); +PHP_METHOD(Redis, hStrLen); PHP_METHOD(Redis, multi); PHP_METHOD(Redis, discard); diff --git a/redis.c b/redis.c index 311d9f2da1..72dedd45c4 100644 --- a/redis.c +++ b/redis.c @@ -230,6 +230,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, hIncrByFloat, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, hMset, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, hMget, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hStrLen, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, multi, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, discard, NULL, ZEND_ACC_PUBLIC) @@ -2192,6 +2193,12 @@ PHP_METHOD(Redis, hMset) } /* }}} */ +/* {{{ proto long Redis::hstrlen(string key, string field) */ +PHP_METHOD(Redis, hStrLen) { + REDIS_PROCESS_CMD(hstrlen, redis_long_response); +} +/* }}} */ + /* flag : get, set {ATOMIC, MULTI, PIPELINE} */ PHP_METHOD(Redis, multi) diff --git a/redis_cluster.c b/redis_cluster.c index a33b4c00a3..ee32d66787 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -126,6 +126,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hmset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hdel, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrbyfloat, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hstrlen, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrank, NULL, ZEND_ACC_PUBLIC) @@ -1472,6 +1473,13 @@ PHP_METHOD(RedisCluster, hmget) { } /* }}} */ +/* {{{ proto array RedisCluster::hstrlen(string key, string field) */ +PHP_METHOD(RedisCluster, hstrlen) { + CLUSTER_PROCESS_CMD(hstrlen, cluster_long_resp, 1); +} +/* }}} */ + + /* {{{ proto string RedisCluster::dump(string key) */ PHP_METHOD(RedisCluster, dump) { CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp, 1); diff --git a/redis_cluster.h b/redis_cluster.h index 3468942982..6b4bb0d80b 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -193,6 +193,7 @@ PHP_METHOD(RedisCluster, hincrby); PHP_METHOD(RedisCluster, hincrbyfloat); PHP_METHOD(RedisCluster, hset); PHP_METHOD(RedisCluster, hsetnx); +PHP_METHOD(RedisCluster, hstrlen); PHP_METHOD(RedisCluster, incr); PHP_METHOD(RedisCluster, decr); PHP_METHOD(RedisCluster, incrby); diff --git a/redis_commands.c b/redis_commands.c index d63332d1ca..d6da762426 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1626,6 +1626,33 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* HSTRLEN */ +int +redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *field; + strlen_t key_len, field_len; + int key_free; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, + &field, &field_len) == FAILURE + ) { + return FAILURE; + } + // Prefix key + key_free = redis_key_prefix(redis_sock, &key, &key_len); + + *cmd_len = redis_cmd_format_static(cmd, "HSTRLEN", "ss", key, key_len, field, field_len); + + // Set our slot + CMD_SET_SLOT(slot, key, key_len); + + if (key_free) efree(key); + + return SUCCESS; +} + /* BITPOS */ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 4494a61986..75a52ec655 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2354,6 +2354,15 @@ public function testHashes() { $this->assertTrue('Array' === $h1['y']); $this->assertTrue('Object' === $h1['z']); $this->assertTrue('' === $h1['t']); + + // hstrlen + if (version_compare($this->version, '3.2.0', 'ge')) { + $this->redis->del('h'); + $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // key doesn't exist + $this->redis->hSet('h', 'foo', 'bar'); + $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // field is not present in the hash + $this->assertTrue(3 === $this->redis->hStrLen('h', 'foo')); + } } public function testSetRange() { From 9310b25ded03f0efd49c893a9ecf9ab4c82a620d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 8 Mar 2017 12:31:19 +0200 Subject: [PATCH 0714/1986] refactoring Change multi, pipeline and remove unused prototypes --- php_redis.h | 16 ------------ redis.c | 74 +++++++++++++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/php_redis.h b/php_redis.h index 635e42e1bc..12b244043b 100644 --- a/php_redis.h +++ b/php_redis.h @@ -265,27 +265,11 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems); -/* pipeline */ -PHP_REDIS_API request_item* get_pipeline_head(zval *object); -PHP_REDIS_API void set_pipeline_head(zval *object, request_item *head); -PHP_REDIS_API request_item* get_pipeline_current(zval *object); -PHP_REDIS_API void set_pipeline_current(zval *object, request_item *current); - #ifndef _MSC_VER ZEND_BEGIN_MODULE_GLOBALS(redis) ZEND_END_MODULE_GLOBALS(redis) #endif -struct redis_queued_item { - /* reading function */ - zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ...); - - char *cmd; - int cmd_len; - - struct redis_queued_item *next; -}; - extern zend_module_entry redis_module_entry; #define redis_module_ptr &redis_module_entry diff --git a/redis.c b/redis.c index 1123add5b9..948247ee97 100644 --- a/redis.c +++ b/redis.c @@ -2250,9 +2250,8 @@ PHP_METHOD(Redis, multi) { RedisSock *redis_sock; - char *cmd; - int response_len, cmd_len; - char * response; + char *resp, *cmd; + int resp_len, cmd_len; zval *object; zend_long multi_value = MULTI; @@ -2269,37 +2268,44 @@ PHP_METHOD(Redis, multi) RETURN_FALSE; } - if(multi_value == MULTI || multi_value == PIPELINE) { - redis_sock->mode = multi_value; - } else { - RETURN_FALSE; - } - - redis_sock->current = NULL; - - IF_MULTI() { - cmd_len = redis_cmd_format_static(&cmd, "MULTI", ""); - - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) - efree(cmd); - - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) - == NULL) - { + if (multi_value == PIPELINE) { + IF_PIPELINE() { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Already in pipeline mode"); + } else IF_MULTI() { + php_error_docref(NULL TSRMLS_CC, E_ERROR, + "Can't activate pipeline in multi mode!"); RETURN_FALSE; + } else { + free_reply_callbacks(redis_sock); + redis_sock->mode = PIPELINE; } + } else if (multi_value == MULTI) { + IF_MULTI() { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Already in multi mode"); + } else IF_PIPELINE() { + php_error_docref(NULL TSRMLS_CC, E_ERROR, + "Can't activate multi in pipeline mode!"); + RETURN_FALSE; + } else { + cmd_len = redis_cmd_format_static(&cmd, "MULTI", ""); + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) + efree(cmd); - if(strncmp(response, "+OK", 3) == 0) { - efree(response); - RETURN_ZVAL(getThis(), 1, 0); + if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } else if (strncmp(resp, "+OK", 3) != 0) { + efree(resp); + RETURN_FALSE; + } + efree(resp); + redis_sock->mode = MULTI; } - efree(response); + } else { RETURN_FALSE; } - IF_PIPELINE() { - free_reply_callbacks(redis_sock); - RETURN_ZVAL(getThis(), 1, 0); - } + RETURN_ZVAL(getThis(), 1, 0); } /* discard */ @@ -2463,13 +2469,19 @@ PHP_METHOD(Redis, pipeline) RETURN_FALSE; } - IF_NOT_PIPELINE() { - redis_sock->mode = PIPELINE; - + IF_MULTI() { + php_error_docref(NULL TSRMLS_CC, E_ERROR, + "Can't activate pipeline in multi mode!"); + RETURN_FALSE; + } else IF_PIPELINE() { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Already in pipeline mode"); + } else { /* NB : we keep the function fold, to detect the last function. * We need the response format of the n - 1 command. So, we can delete * when n > 2, the { 1 .. n - 2} commands */ free_reply_callbacks(redis_sock); + redis_sock->mode = PIPELINE; } RETURN_ZVAL(getThis(), 1, 0); } From a070a43fcb4fa3f960adcbf80afaad1ce4c0e121 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 9 Mar 2017 23:48:32 +0200 Subject: [PATCH 0715/1986] refactoring In pipeline mode all commands are now stored in the buffer the memory for which is allocated with emalloc. There is no need to use a linked list. --- common.h | 24 ++++++++++-------------- library.c | 8 ++++++-- redis.c | 42 ++++++++++-------------------------------- 3 files changed, 26 insertions(+), 48 deletions(-) diff --git a/common.h b/common.h index 1772538f7a..dc76d9977d 100644 --- a/common.h +++ b/common.h @@ -22,7 +22,6 @@ typedef struct { char *val; } zend_string; - #define zend_string_release(s) do { \ if ((s) && (s)->gc) { \ if ((s)->gc & 0x10 && (s)->val) efree((s)->val); \ @@ -475,18 +474,15 @@ typedef enum _PUBSUB_TYPE { #define IF_NOT_PIPELINE() if (redis_sock->mode != PIPELINE) #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \ - request_item *tmp = malloc(sizeof(request_item)); \ - tmp->request_str = calloc(cmd_len, 1);\ - memcpy(tmp->request_str, cmd, cmd_len);\ - tmp->request_size = cmd_len;\ - tmp->next = NULL;\ - if (redis_sock->pipeline_current) { \ - redis_sock->pipeline_current->next = tmp; \ - } \ - redis_sock->pipeline_current = tmp; \ - if(NULL == redis_sock->pipeline_head) { \ - redis_sock->pipeline_head = redis_sock->pipeline_current;\ + if (redis_sock->pipeline_cmd == NULL) { \ + redis_sock->pipeline_cmd = estrndup(cmd, cmd_len); \ + } else { \ + redis_sock->pipeline_cmd = erealloc(redis_sock->pipeline_cmd, \ + redis_sock->pipeline_len + cmd_len); \ + memcpy(&redis_sock->pipeline_cmd[redis_sock->pipeline_len], \ + cmd, cmd_len); \ } \ + redis_sock->pipeline_len += cmd_len; \ } while (0) #define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) \ @@ -633,8 +629,8 @@ typedef struct { fold_item *head; fold_item *current; - request_item *pipeline_head; - request_item *pipeline_current; + char *pipeline_cmd; + size_t pipeline_len; char *err; int err_len; diff --git a/library.c b/library.c index 497037bc3b..e3aa4117b0 100644 --- a/library.c +++ b/library.c @@ -1522,8 +1522,9 @@ redis_sock_create(char *host, int host_len, unsigned short port, double timeout, redis_sock->mode = ATOMIC; redis_sock->head = NULL; redis_sock->current = NULL; - redis_sock->pipeline_head = NULL; - redis_sock->pipeline_current = NULL; + + redis_sock->pipeline_cmd = NULL; + redis_sock->pipeline_len = 0; redis_sock->err = NULL; redis_sock->err_len = 0; @@ -1939,6 +1940,9 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) if(redis_sock->prefix) { efree(redis_sock->prefix); } + if (redis_sock->pipeline_cmd) { + efree(redis_sock->pipeline_cmd); + } if(redis_sock->err) { efree(redis_sock->err); } diff --git a/redis.c b/redis.c index 948247ee97..6871963e88 100644 --- a/redis.c +++ b/redis.c @@ -429,7 +429,6 @@ static void free_reply_callbacks(RedisSock *redis_sock) { fold_item *fi; - request_item *ri; for (fi = redis_sock->head; fi; ) { fold_item *fi_next = fi->next; @@ -438,15 +437,6 @@ free_reply_callbacks(RedisSock *redis_sock) } redis_sock->head = NULL; redis_sock->current = NULL; - - for (ri = redis_sock->pipeline_head; ri; ) { - struct request_item *ri_next = ri->next; - free(ri->request_str); - free(ri); - ri = ri_next; - } - redis_sock->pipeline_head = NULL; - redis_sock->pipeline_current = NULL; } #if (PHP_MAJOR_VERSION < 7) @@ -2394,36 +2384,24 @@ PHP_METHOD(Redis, exec) } IF_PIPELINE() { - char *request = NULL; - int total = 0, offset = 0; - struct request_item *ri; - - /* compute the total request size */ - for(ri = redis_sock->pipeline_head; ri; ri = ri->next) { - total += ri->request_size; - } - if (total) { - request = emalloc(total + 1); - /* concatenate individual elements one by one in the target buffer */ - for (ri = redis_sock->pipeline_head; ri; ri = ri->next) { - memcpy(request + offset, ri->request_str, ri->request_size); - offset += ri->request_size; - } - request[total] = '\0'; - if (redis_sock_write(redis_sock, request, total TSRMLS_CC) < 0) { + if (redis_sock->pipeline_cmd == NULL) { + /* Empty array when no command was run. */ + array_init(return_value); + } else { + if (redis_sock_write(redis_sock, redis_sock->pipeline_cmd, + redis_sock->pipeline_len TSRMLS_CC) < 0) { ZVAL_FALSE(return_value); } else { array_init(return_value); redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, return_value, 0); } - efree(request); - } else { - /* Empty array when no command was run. */ - array_init(return_value); + efree(redis_sock->pipeline_cmd); + redis_sock->pipeline_cmd = NULL; + redis_sock->pipeline_len = 0; } - redis_sock->mode = ATOMIC; free_reply_callbacks(redis_sock); + redis_sock->mode = ATOMIC; } } From ac4ea8ef4faa43b39bba5918e26155748520d7bf Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 12 Mar 2017 22:52:25 +0200 Subject: [PATCH 0716/1986] Add free_reply_callbacks call to discard method This is related to issue #479 and PR #489 --- README.markdown | 4 ++-- redis.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 5431e32ce5..444512705f 100644 --- a/README.markdown +++ b/README.markdown @@ -1640,9 +1640,9 @@ while($arr_keys = $redis->hScan('hash', $it)) { } ~~~ -##### hStrLen +### hStrLen ----- - **Description**_: Get the string length of the value associated with field in the hash stored at key. +_**Description**_: Get the string length of the value associated with field in the hash stored at key. ##### *Parameters* *key*: String *field*: String diff --git a/redis.c b/redis.c index 6871963e88..d0429d279a 100644 --- a/redis.c +++ b/redis.c @@ -2314,6 +2314,7 @@ PHP_METHOD(Redis, discard) } redis_sock->mode = ATOMIC; + free_reply_callbacks(redis_sock); redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } From fb88e1df5f9cb97040000523c0891b8e10381029 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 14 Mar 2017 17:12:01 +0200 Subject: [PATCH 0717/1986] remove unused struct request_item + add redis_hstrlen_cmd proto --- common.h | 6 ------ redis_commands.h | 3 +++ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/common.h b/common.h index dc76d9977d..2658c8bfc1 100644 --- a/common.h +++ b/common.h @@ -598,12 +598,6 @@ typedef struct fold_item { struct fold_item *next; } fold_item; -typedef struct request_item { - char *request_str; - int request_size; /* size_t */ - struct request_item *next; -} request_item; - /* {{{ struct RedisSock */ typedef struct { php_stream *stream; diff --git a/redis_commands.h b/redis_commands.h index f8760c52bd..bc5aa9d9aa 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -145,6 +145,9 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From c03cf746849ba6afa055ba65d696e5f6e5dcb3fa Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 15 Mar 2017 09:54:37 -0700 Subject: [PATCH 0718/1986] Add function prototype to remove warning --- redis_commands.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis_commands.h b/redis_commands.h index f8760c52bd..bc5aa9d9aa 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -145,6 +145,9 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From d01966b5acd9c4f7b9ed4284ff80edbf211b8c43 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 8 Feb 2016 09:46:04 -0800 Subject: [PATCH 0719/1986] Allow sInterStore to take one arg (as it could be one array) Addresses #748 --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index d63332d1ca..1566c0e550 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2963,7 +2963,7 @@ int redis_sinterstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - "SINTERSTORE", sizeof("SINTERSTORE")-1, 2, 0, cmd, cmd_len, slot); + "SINTERSTORE", sizeof("SINTERSTORE")-1, 1, 0, cmd, cmd_len, slot); } /* SUNION */ From 4fd2f6138c7673cb725e2e6e2b92041c12924bbb Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 26 Jan 2017 00:16:03 +0200 Subject: [PATCH 0720/1986] TravisCI: allow_failures fix + igbinary testing --- .travis.yml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7052747e45..2e4c5bd335 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,27 +10,26 @@ php: - nightly env: CC=gcc matrix: - include: - - env: CC=clang - php: 5.4 - - env: CC=clang - php: 5.5 - - env: CC=clang - php: 5.6 - - env: CC=clang - php: 7.0 - - env: CC=clang - php: 7.1 - - env: CC=clang - php: nightly allow_failures: - php: nightly + include: + - php: 5.4 + env: CC=clang + - php: 5.5 + env: CC=clang + - php: 5.6 + env: CC=clang + - php: 7.0 + env: CC=clang + - php: 7.1 + env: CC=clang addons: apt: packages: clang before_install: - phpize - - if [ $(phpenv version-name) lt 7 ]; then pecl install igbinary-1.2.1; ./configure --enable-redis-igbinary CFLAGS=-Wall; else ./configure CFLAGS=-Wall; fi + - pecl install igbinary + - ./configure --enable-redis-igbinary CFLAGS=-Wall install: make install before_script: - gem install redis From 6ef0c2648aedfb36dadd28b968e45b2eb0f89471 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 27 Jan 2017 12:17:54 -0800 Subject: [PATCH 0721/1986] Add a test for passing a single array into sInterStore --- tests/RedisTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 4494a61986..132a894ecb 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1407,6 +1407,9 @@ public function testsInterStore() { $this->redis->sAdd('{set}t', $i); } + /* Regression test for passing a single array */ + $this->assertEquals($this->redis->sInterStore(Array('{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) { From 4121c40199043e12c495d5466e4613ddf31c70c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Fern=C3=A1ndez?= Date: Thu, 9 Feb 2017 13:56:34 +0100 Subject: [PATCH 0722/1986] Fix Null Bulk String response parsing in cluster library (#1104) * Failing test case when running LUA with bulk empty response * Fix issue when parsing bulk array response from eval commands * Added test for bulk LUA responses and changed condition * Added multi tests and fixes in C code format --- cluster_library.c | 14 +++++++-- tests/RedisClusterTest.php | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 280d94bbcf..923dda4e45 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1870,7 +1870,11 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust RETVAL_TRUE; break; case TYPE_BULK: - RETVAL_STRINGL(r->str, r->len); + if (r->len < 0) { + RETVAL_NULL(); + } else { + RETVAL_STRINGL(r->str, r->len); + } break; case TYPE_MULTIBULK: array_init(z_arr); @@ -1896,8 +1900,12 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust add_next_index_bool(&c->multi_resp, 1); break; case TYPE_BULK: - add_next_index_stringl(&c->multi_resp, r->str, r->len); - efree(r->str); + if (r->len < 0) { + add_next_index_null(&c->multi_resp); + } else { + add_next_index_stringl(&c->multi_resp, r->str, r->len); + efree(r->str); + } break; case TYPE_MULTIBULK: cluster_mbulk_variant_resp(r, &c->multi_resp); diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 1ccbf47b59..36f65f84a6 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -345,6 +345,69 @@ public function testEvalSHA() { $this->assertTrue(1 === $this->redis->eval($scr,Array($str_key), 1)); $this->assertTrue(1 === $this->redis->evalsha($sha,Array($str_key), 1)); } + + public function testEvalBulkResponse() { + $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + + $this->redis->script($str_key1, 'flush'); + $this->redis->script($str_key2, 'flush'); + + $scr = "return {KEYS[1],KEYS[2]}"; + + $result = $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + + $this->assertTrue($str_key1 === $result[0]); + $this->assertTrue($str_key2 === $result[1]); + } + + public function testEvalBulkResponseMulti() { + $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + + $this->redis->script($str_key1, 'flush'); + $this->redis->script($str_key2, 'flush'); + + $scr = "return {KEYS[1],KEYS[2]}"; + + $this->redis->multi(); + $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + + $result = $this->redis->exec(); + + $this->assertTrue($str_key1 === $result[0][0]); + $this->assertTrue($str_key2 === $result[0][1]); + } + + public function testEvalBulkEmptyResponse() { + $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + + $this->redis->script($str_key1, 'flush'); + $this->redis->script($str_key2, 'flush'); + + $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; + + $result = $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + + $this->assertTrue(null === $result); + } + + public function testEvalBulkEmptyResponseMulti() { + $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + + $this->redis->script($str_key1, 'flush'); + $this->redis->script($str_key2, 'flush'); + + $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; + + $this->redis->multi(); + $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + $result = $this->redis->exec(); + + $this->assertTrue(null === $result[0]); + } /* Cluster specific introspection stuff */ public function testIntrospection() { From abf7d4dee13d0a290ee4a3dff6d3bf0f064944ab Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 14 Feb 2017 16:34:26 -0800 Subject: [PATCH 0723/1986] Make sure redisCluster members are all initialized on (re)creation Fixes #1105 --- redis_cluster.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index a33b4c00a3..44c5e0e267 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -282,23 +282,19 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { redisCluster *cluster; // Allocate our actual struct - cluster = emalloc(sizeof(redisCluster)); - memset(cluster, 0, sizeof(redisCluster)); + cluster = ecalloc(1, sizeof(redisCluster)); #else zend_object * create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { redisCluster *cluster; // Allocate our actual struct - cluster = emalloc(sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); + cluster = ecalloc(1, sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); #endif // We're not currently subscribed anywhere cluster->subscribed_slot = -1; - // Assume we're up initially - cluster->clusterdown = 0; - // Allocate our RedisSock we'll use to store prefix/serialization flags cluster->flags = ecalloc(1, sizeof(RedisSock)); From 645888b778dfebd3080f667ea9f60ac0ee7f9a90 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 14 Feb 2017 17:56:48 -0800 Subject: [PATCH 0724/1986] Fix memory leak --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 923dda4e45..a40c00d256 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2036,7 +2036,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // Return our array if(CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_result, 1, 0); + RETVAL_ZVAL(z_result, 0, 1); } else { add_next_index_zval(&c->multi_resp, z_result); } From 564ce3a1e5362209a6bf0e35ac0999a561d36a9e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 23 Feb 2017 13:39:05 +0200 Subject: [PATCH 0725/1986] RedisArray segfault fix --- redis_array_impl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 177368ae0e..878780a078 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -350,7 +350,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->pconnect = b_pconnect; ra->connect_timeout = connect_timeout; - if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL) { + if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { zval_dtor(&ra->redis[i]); efree(ra->hosts[i]); From 029475318cdfe67d646cd92c120d8732d335ab3f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 16 Mar 2017 08:57:57 -0700 Subject: [PATCH 0726/1986] Bamp 3.1.2 --- package.xml | 61 +++++++++++++++++++++++++++-------------------------- php_redis.h | 2 +- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/package.xml b/package.xml index 5850a963a8..b3dd1977ad 100644 --- a/package.xml +++ b/package.xml @@ -27,41 +27,25 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2017-01-16 + 2017-03-16 - 3.1.1RC2 - 3.1.1RC2 + 3.1.2 + 3.1.2 - beta - beta + stable + stable PHP - phpredis 3.1.1RC2 - - * Additional test updates for 32 bit systems (@remicollet) - * ARM rounding issue in tests (@remicollet) - * Use new zend_list_close instead of zend_list_delete when reconnecting. - * Refactoring of redis_boolean_response_impl and redis_sock_write (@yatsukhnenko) - - phpredis 3.1.1.RC1 - - This release contains mostly fixes for issues introduced when merging - the php 5 and 7 codebase into a single branch. - - * Fixed a segfault in igbinary serialization (@yatsukhnenko) - * Restore 2.2.8/3.0.0 functionality to distinguish between an error - and simply empty session data. (@remicollet) - * Fix double to string conversion function (@yatsukhnenko) - * Use PHP_FE_END definition when available (@remicollet) - * Fixed various 'static function declared but not used' warnings - * Fixes to various calls which were typecasting pointers to the - wrong size. (@remicollet) - - * Added php session unit test (@yatsukhnenko) - * Added explicit module dependancy for igbinary (@remicollet) - * Added phpinfo serialization information (@remicollet) + phpredis 3.1.2 + + * RedisArray segfault fix [564ce3] (Pavlo Yatsukhnenko) + * Small memory leak fix [645888b] (Mike Grunder) + * Segfault fix when recreating RedisCluster objects [abf7d4] (Michael Grunder) + * Fix for RedisCluster bulk response parsing [4121c4] (Alberto Fernández) + * Re allow single array for sInterStore [6ef0c2, d01966] (Michael Grunder) + * Better TravisCI integration [4fd2f6] (Pavlo Yatsukhnenko) @@ -115,6 +99,23 @@ http://pear.php.net/dtd/package-2.0.xsd"> redis + + + stablestable + 3.1.23.1.2 + 2017-03-16 + + phpredis 3.1.2 + + * RedisArray segfault fix [564ce3] (Pavlo Yatsukhnenko) + * Small memory leak fix [645888b] (Mike Grunder) + * Segfault fix when recreating RedisCluster objects [abf7d4] (Michael Grunder) + * Fix for RedisCluster bulk response parsing [4121c4] (Alberto Fernández) + * Re allow single array for sInterStore [6ef0c2, d01966] (Michael Grunder) + * Better TravisCI integration [4fd2f6] (Pavlo Yatsukhnenko) + + + betabeta 3.1.1RC23.1.1RC2 @@ -222,7 +223,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fixed dreaded size_t vs long long compiler warning - + stablestable 2.2.72.2.7 diff --git a/php_redis.h b/php_redis.h index bfc7a5ff84..45c279d938 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "3.1.1RC2" +#define PHP_REDIS_VERSION "3.1.2" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 970ca3dc5b8413c7316bf7d00d27bdc22e061be1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 21 Mar 2017 09:42:18 -0700 Subject: [PATCH 0727/1986] Remove support for php 5.2.x --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index b3dd1977ad..bc8e024814 100644 --- a/package.xml +++ b/package.xml @@ -88,7 +88,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> - 5.2.0 + 5.3.0 7.9.99 From e875166d1b8c1f732856648e90b34611c88677fe Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 21 Mar 2017 12:53:26 -0700 Subject: [PATCH 0728/1986] Fixes potential (but unreported) segfault in release code. --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index cd722350a3..d03cdcd6b0 100644 --- a/library.c +++ b/library.c @@ -900,7 +900,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - zval zv, *z_ret = &zv; + zval zv = {0}, *z_ret = &zv; /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { From 88efaa89a176855975896eee5af842706ee35e03 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 21 Mar 2017 12:53:26 -0700 Subject: [PATCH 0729/1986] Fixes potential (but unreported) segfault in release code. --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index e3aa4117b0..de4a33be8e 100644 --- a/library.c +++ b/library.c @@ -904,7 +904,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - zval zv, *z_ret = &zv; + zval zv = {0}, *z_ret = &zv; /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { From dcf655ccc7b05b5a648bf2870ea147e873caceca Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 24 Mar 2017 12:34:30 -0700 Subject: [PATCH 0730/1986] Pedantic fix for zval changes in 5/7 --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index d03cdcd6b0..ca8f20e4a7 100644 --- a/library.c +++ b/library.c @@ -900,7 +900,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - zval zv = {0}, *z_ret = &zv; + zval zv = {{0}}, *z_ret = &zv; /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { From 83ac4baef101f00046a1e1d4c2d06251a5cce07f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 24 Mar 2017 12:34:30 -0700 Subject: [PATCH 0731/1986] Pedantic fix for zval changes in 5/7 --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index de4a33be8e..5358363a9c 100644 --- a/library.c +++ b/library.c @@ -904,7 +904,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - zval zv = {0}, *z_ret = &zv; + zval zv = {{0}}, *z_ret = &zv; /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { From a33ca4f8c583783aeae1aa1a69428bdbe7d451c2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 24 Mar 2017 13:11:38 -0700 Subject: [PATCH 0732/1986] Fix release date --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index bc8e024814..483d2a58e6 100644 --- a/package.xml +++ b/package.xml @@ -27,7 +27,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2017-03-16 + 2017-03-24 3.1.2 3.1.2 From 4aff3f9c0070f6f6a367c67929ea9b45b8249c1a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 25 Mar 2017 14:28:29 -0700 Subject: [PATCH 0733/1986] Make RedisArray tests exit with a nonzero exit code on failure --- tests/RedisArrayTest.php | 2 +- tests/TestRedis.php | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index a7fa72828f..647a0129d1 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -548,7 +548,7 @@ function run_tests($className, $str_filter, $str_host) { $serverList = Array("$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"); // run - TestSuite::run($className, $str_filter); + return TestSuite::run($className, $str_filter); } ?> diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 7f73d258ec..971bd93950 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -47,11 +47,14 @@ foreach(array(true, false) as $useIndex) { echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; - run_tests('Redis_Array_Test', $str_filter, $str_host); - run_tests('Redis_Rehashing_Test', $str_filter, $str_host); - run_tests('Redis_Auto_Rehashing_Test', $str_filter, $str_host); - run_tests('Redis_Multi_Exec_Test', $str_filter, $str_host); - run_tests('Redis_Distributor_Test', $str_filter, $str_host); + /* The various RedisArray subtests we can run */ + $arr_ra_tests = ['Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test']; + foreach ($arr_ra_tests as $str_test) { + /* Run until we encounter a failure */ + if (run_tests($str_test, $str_filter, $str_host) != 0) { + exit(1); + } + } } } else { echo TestSuite::make_bold("RedisCluster") . "\n"; From 74ba34bc71e1b91584f47000994b0ad457794230 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 23 Mar 2017 23:54:24 +0200 Subject: [PATCH 0734/1986] refactor redis_sock_read_bulk_reply Throws error and returns NULL when we read less bytes than expected. This patch probably fixes "protocol error, got '' as reply type byte" error. --- library.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/library.c b/library.c index 5358363a9c..33d499dcd3 100644 --- a/library.c +++ b/library.c @@ -463,7 +463,8 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, /** * redis_sock_read_bulk_reply */ -PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) +PHP_REDIS_API char * +redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) { int offset = 0; size_t got; @@ -478,17 +479,19 @@ PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes while(offset < bytes) { got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); - if (got <= 0) { - /* Error or EOF */ - zend_throw_exception(redis_exception_ce, - "socket error on read socket", 0 TSRMLS_CC); - break; - } + if (got == 0) break; offset += got; } + if (offset < bytes) { + /* Error or EOF */ + zend_throw_exception(redis_exception_ce, + "socket error on read socket", 0 TSRMLS_CC); + efree(reply); + return NULL; + } php_stream_read(redis_sock->stream, c, 2); - reply[bytes] = 0; + reply[bytes] = '\0'; return reply; } From bd16da641cddae8ac7aca69b36dfc91b1ea5c892 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 25 Mar 2017 15:14:01 -0700 Subject: [PATCH 0735/1986] Small formatting fix --- library.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/library.c b/library.c index 33d499dcd3..0557e55a4b 100644 --- a/library.c +++ b/library.c @@ -467,21 +467,24 @@ PHP_REDIS_API char * redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) { int offset = 0; - size_t got; - char *reply, c[2]; + size_t got; if (-1 == bytes || -1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return NULL; } - reply = emalloc(bytes+1); - while(offset < bytes) { - got = php_stream_read(redis_sock->stream, reply + offset, - bytes-offset); - if (got == 0) break; - offset += got; - } + /* Allocate memory for string */ + reply = emalloc(bytes+1); + + /* Consume bulk string */ + while(offset < bytes) { + got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); + if (got == 0) break; + offset += got; + } + + /* Protect against reading too few bytes */ if (offset < bytes) { /* Error or EOF */ zend_throw_exception(redis_exception_ce, @@ -489,9 +492,11 @@ redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) efree(reply); return NULL; } - php_stream_read(redis_sock->stream, c, 2); + /* Consume \r\n and null terminate reply string */ + php_stream_read(redis_sock->stream, c, 2); reply[bytes] = '\0'; + return reply; } From 9ac9845d32b316d5f454cf383819ecf1f9eda47c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 25 Mar 2017 16:19:11 -0700 Subject: [PATCH 0736/1986] Use old style array declaration --- tests/TestRedis.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 971bd93950..4348c7d861 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -48,7 +48,7 @@ echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; /* The various RedisArray subtests we can run */ - $arr_ra_tests = ['Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test']; + $arr_ra_tests = Array('Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'); foreach ($arr_ra_tests as $str_test) { /* Run until we encounter a failure */ if (run_tests($str_test, $str_filter, $str_host) != 0) { From f5d9ac2a14d2876dfccb61b7bde75ae4b51e94ff Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 25 Mar 2017 16:19:11 -0700 Subject: [PATCH 0737/1986] Use old style array declaration --- tests/TestRedis.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 971bd93950..4348c7d861 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -48,7 +48,7 @@ echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; /* The various RedisArray subtests we can run */ - $arr_ra_tests = ['Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test']; + $arr_ra_tests = Array('Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'); foreach ($arr_ra_tests as $str_test) { /* Run until we encounter a failure */ if (run_tests($str_test, $str_filter, $str_host) != 0) { From 8324a46b17bc1686e7834db36f96eebdfa1ec99e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 29 Mar 2017 23:19:24 +0300 Subject: [PATCH 0738/1986] remove unused constants --- common.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/common.h b/common.h index 2658c8bfc1..dc51517b9e 100644 --- a/common.h +++ b/common.h @@ -384,14 +384,11 @@ typedef size_t strlen_t; #define NULL ((void *) 0) #endif -#define redis_sock_name "Redis Socket Buffer" #define REDIS_SOCK_STATUS_FAILED 0 #define REDIS_SOCK_STATUS_DISCONNECTED 1 #define REDIS_SOCK_STATUS_UNKNOWN 2 #define REDIS_SOCK_STATUS_CONNECTED 3 -#define redis_multi_access_type_name "Redis Multi type access" - #define _NL "\r\n" /* properties */ From 434bb5ad5ceab2542ffc8b965d05acc660cfda98 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 29 Mar 2017 23:28:56 +0300 Subject: [PATCH 0739/1986] Remove REDIS_SOCK_STATUS_UNKNOWN because no way to reach it. --- common.h | 3 +-- library.c | 9 --------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/common.h b/common.h index dc51517b9e..8bc911eefe 100644 --- a/common.h +++ b/common.h @@ -386,8 +386,7 @@ typedef size_t strlen_t; #define REDIS_SOCK_STATUS_FAILED 0 #define REDIS_SOCK_STATUS_DISCONNECTED 1 -#define REDIS_SOCK_STATUS_UNKNOWN 2 -#define REDIS_SOCK_STATUS_CONNECTED 3 +#define REDIS_SOCK_STATUS_CONNECTED 2 #define _NL "\r\n" diff --git a/library.c b/library.c index 0557e55a4b..eb24dde578 100644 --- a/library.c +++ b/library.c @@ -1640,15 +1640,6 @@ redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC) case REDIS_SOCK_STATUS_CONNECTED: res = 0; break; - case REDIS_SOCK_STATUS_UNKNOWN: - if (force_connect > 0 && redis_sock_connect(redis_sock TSRMLS_CC) < 0) { - res = -1; - } else { - res = 0; - - redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; - } - break; } return res; From e7ad31e515a9c1a7d661e189d5869315d4555cac Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 29 Mar 2017 23:33:53 +0300 Subject: [PATCH 0740/1986] Remove unused force_connect parameter from redis_sock_server_open function. --- cluster_library.h | 2 +- library.c | 2 +- library.h | 2 +- redis.c | 4 ++-- redis_array_impl.c | 2 +- redis_session.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index 3a58f13a0a..1b18107790 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -55,7 +55,7 @@ #define CLUSTER_LAZY_CONNECT(s) \ if(s->lazy_connect) { \ s->lazy_connect = 0; \ - redis_sock_server_open(s, 1 TSRMLS_CC); \ + redis_sock_server_open(s TSRMLS_CC); \ } /* Clear out our "last error" */ diff --git a/library.c b/library.c index eb24dde578..dc3b2e760b 100644 --- a/library.c +++ b/library.c @@ -1630,7 +1630,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) * redis_sock_server_open */ PHP_REDIS_API int -redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC) +redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC) { int res = -1; diff --git a/library.h b/library.h index de4991fad6..62a38be441 100644 --- a/library.h +++ b/library.h @@ -35,7 +35,7 @@ PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret) PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC); +PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); diff --git a/redis.c b/redis.c index d0429d279a..9d232d0f3c 100644 --- a/redis.c +++ b/redis.c @@ -546,7 +546,7 @@ redis_sock_get(zval *id TSRMLS_DC, int no_throw) if (redis_sock->lazy_connect) { redis_sock->lazy_connect = 0; - if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) { + if (redis_sock_server_open(redis_sock TSRMLS_CC) < 0) { return NULL; } } @@ -857,7 +857,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) redis->sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval, 0); - if (redis_sock_server_open(redis->sock, 1 TSRMLS_CC) < 0) { + if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { redis_free_socket(redis->sock); redis->sock = NULL; return FAILURE; diff --git a/redis_array_impl.c b/redis_array_impl.c index 0277742624..37fbb028de 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -83,7 +83,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b if (!b_lazy_connect) { /* connect */ - redis_sock_server_open(redis->sock, 1 TSRMLS_CC); + redis_sock_server_open(redis->sock TSRMLS_CC); } ra->count = ++i; diff --git a/redis_session.c b/redis_session.c index 2ac287223c..a753d198e7 100644 --- a/redis_session.c +++ b/redis_session.c @@ -167,7 +167,7 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { if(rpm->auth && rpm->auth_len && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } - redis_sock_server_open(rpm->redis_sock, 0 TSRMLS_CC); + redis_sock_server_open(rpm->redis_sock TSRMLS_CC); if(needs_auth) { redis_pool_member_auth(rpm TSRMLS_CC); } From ef6e0876be5a8aaa45b5eeb3861f8499a9edaadb Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 30 Mar 2017 10:22:34 +0300 Subject: [PATCH 0741/1986] Use redis_sock_gets instread of redis_sock_eof + php_stream_gets - redis_mbulk_reply_assoc - redis_mbulk_reply_raw - redis_mbulk_reply_zipped - redis_sock_read - redis_sock_read_multibulk_multi_reply - redis_sock_read_multibulk_reply - redis_sock_read_multibulk_reply_zval --- library.c | 83 +++++++++++++++++-------------------------------------- redis.c | 8 ++---- 2 files changed, 29 insertions(+), 62 deletions(-) diff --git a/library.c b/library.c index dc3b2e760b..1bb82d2264 100644 --- a/library.c +++ b/library.c @@ -435,15 +435,9 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, { char inbuf[1024]; int numElems; + size_t len; - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { - return NULL; - } - - if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - zend_throw_exception(redis_exception_ce, - "read error on connection", 0 TSRMLS_CC); + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { return NULL; } @@ -503,30 +497,23 @@ redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) /** * redis_sock_read */ -PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) +PHP_REDIS_API char * +redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) { char inbuf[1024]; - size_t err_len; + size_t len; *buf_len = 0; - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { - return NULL; - } - - if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - zend_throw_exception(redis_exception_ce, "read error on connection", - 0 TSRMLS_CC); + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { return NULL; } switch(inbuf[0]) { case '-': - err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); + redis_sock_set_err(redis_sock, inbuf+1, len); /* Filter our ERROR through the few that should actually throw */ - redis_error_throw(inbuf + 1, err_len TSRMLS_CC); + redis_error_throw(inbuf + 1, len TSRMLS_CC); /* Handle stale data error */ if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { @@ -547,10 +534,10 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D case '+': case ':': - /* Single Line Reply */ - /* :123\r\n */ - *buf_len = strlen(inbuf) - 2; - if(*buf_len >= 2) { + /* Single Line Reply */ + /* +OK or :123 */ + if (len > 1) { + *buf_len = len; return estrndup(inbuf, *buf_len); } default: @@ -1266,13 +1253,9 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char inbuf[1024]; int numElems; + size_t len; - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { - return -1; - } - if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { return -1; } @@ -1723,15 +1706,10 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, void *ctx) { char inbuf[1024]; - int numElems, err_len; + int numElems; + size_t len; - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { - return -1; - } - if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - zend_throw_exception(redis_exception_ce, "read error on connection", 0 - TSRMLS_CC); + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { return -1; } @@ -1740,8 +1718,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, add_next_index_bool(z_tab, 0); } else { if (inbuf[0] == '-') { - err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); + redis_sock_set_err(redis_sock, inbuf+1, len); } RETVAL_FALSE; } @@ -1768,17 +1745,14 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, /* Like multibulk reply, but don't touch the values, they won't be unserialized * (this is used by HKEYS). */ -PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API int +redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[1024]; - int numElems, err_len; + int numElems; + size_t len; - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { - return -1; - } - if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { return -1; } @@ -1787,8 +1761,7 @@ PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_bool(z_tab, 0); } else { if (inbuf[0] == '-') { - err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); + redis_sock_set_err(redis_sock, inbuf+1, len); } RETVAL_FALSE; } @@ -1858,15 +1831,11 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc char inbuf[1024], *response; int response_len; int i, numElems; + size_t len; zval *z_keys = ctx; - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { - return -1; - } - if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { return -1; } diff --git a/redis.c b/redis.c index 9d232d0f3c..88e0980615 100644 --- a/redis.c +++ b/redis.c @@ -2325,12 +2325,10 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME char inbuf[1024]; int numElems; + size_t len; - redis_check_eof(redis_sock, 0 TSRMLS_CC); - - php_stream_gets(redis_sock->stream, inbuf, 1024); - if(inbuf[0] != '*') { - return -1; + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + return - 1; } /* number of responses */ From bf499ee31894ee983f07192db8bd87685a2f7f33 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 30 Mar 2017 14:12:00 +0300 Subject: [PATCH 0742/1986] Return -1 from redis_sock_gets on error. --- library.c | 1 + 1 file changed, 1 insertion(+) diff --git a/library.c b/library.c index 1bb82d2264..9256005da6 100644 --- a/library.c +++ b/library.c @@ -2111,6 +2111,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, // Throw a read error exception zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + return -1; } /* We don't need \r\n */ From 18149e34e497539163c1f01034cd55375109a005 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 1 Apr 2017 12:12:47 +0300 Subject: [PATCH 0743/1986] refactoring (#1140) * refactoring * Fix read_timeout --- library.c | 51 +++++++++++++++++++++++------------------------- redis.c | 1 + redis_commands.c | 26 ++++++++++-------------- 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/library.c b/library.c index 9256005da6..70846c0e5c 100644 --- a/library.c +++ b/library.c @@ -1597,7 +1597,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) php_stream_auto_cleanup(redis_sock->stream); - if(tv.tv_sec != 0 || tv.tv_usec != 0) { + if (read_tv.tv_sec != 0 || read_tv.tv_usec != 0) { php_stream_set_option(redis_sock->stream,PHP_STREAM_OPTION_READ_TIMEOUT, 0, &read_tv); } @@ -1791,36 +1791,33 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize) { char *line; - int len; - - while(count > 0) { - line = redis_sock_read(redis_sock, &len TSRMLS_CC); - if (line != NULL) { - zval zv, *z = &zv; - int unwrap; - - /* We will attempt unserialization, if we're unserializing everything, - * or if we're unserializing keys and we're on a key, or we're - * unserializing values and we're on a value! */ - unwrap = unserialize == UNSERIALIZE_ALL || - (unserialize == UNSERIALIZE_KEYS && count % 2 == 0) || - (unserialize == UNSERIALIZE_VALS && count % 2 != 0); - - if (unwrap && redis_unserialize(redis_sock, line, len, z TSRMLS_CC)) { + int i, len; + + for (i = 0; i < count; ++i) { + if ((line = redis_sock_read(redis_sock, &len TSRMLS_CC)) == NULL) { + add_next_index_bool(z_tab, 0); + continue; + } + + /* We will attempt unserialization, if we're unserializing everything, + * or if we're unserializing keys and we're on a key, or we're + * unserializing values and we're on a value! */ + int unwrap = ( + (unserialize == UNSERIALIZE_ALL) || + (unserialize == UNSERIALIZE_KEYS && i % 2 == 0) || + (unserialize == UNSERIALIZE_VALS && i % 2 != 0) + ); + zval zv, *z = &zv; + if (unwrap && redis_unserialize(redis_sock, line, len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); - *z = zv; + MAKE_STD_ZVAL(z); + *z = zv; #endif - add_next_index_zval(z_tab, z); - } else { - add_next_index_stringl(z_tab, line, len); - } - efree(line); + add_next_index_zval(z_tab, z); } else { - add_next_index_bool(z_tab, 0); + add_next_index_stringl(z_tab, line, len); } - - count--; + efree(line); } } diff --git a/redis.c b/redis.c index 88e0980615..9187afec58 100644 --- a/redis.c +++ b/redis.c @@ -788,6 +788,7 @@ PHP_METHOD(Redis, pconnect) if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1) == FAILURE) { RETURN_FALSE; } else { + /* FIXME: should we remove whole `else` block? */ /* reset multi/exec state if there is one. */ RedisSock *redis_sock; if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { diff --git a/redis_commands.c b/redis_commands.c index 6e8a10862f..887fc9d779 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3138,7 +3138,6 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, char *val_str; struct timeval read_tv; strlen_t val_len; - int test_val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &option, &val_str, &val_len) == FAILURE) @@ -3149,18 +3148,15 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, switch(option) { case REDIS_OPT_SERIALIZER: val_long = atol(val_str); - test_val = val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP; + if (val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP #ifdef HAVE_REDIS_IGBINARY - test_val = test_val || val_long == REDIS_SERIALIZER_IGBINARY; + || val_long == REDIS_SERIALIZER_IGBINARY #endif - if(test_val) - { - redis_sock->serializer = val_long; - RETURN_TRUE; - } else { - RETURN_FALSE; - } - break; + ) { + redis_sock->serializer = val_long; + RETURN_TRUE; + } + break; case REDIS_OPT_PREFIX: if(redis_sock->prefix) { efree(redis_sock->prefix); @@ -3190,7 +3186,6 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, redis_sock->scan = val_long; RETURN_TRUE; } - RETURN_FALSE; break; case REDIS_OPT_FAILOVER: val_long = atol(val_str); @@ -3201,12 +3196,11 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, { c->failover = val_long; RETURN_TRUE; - } else { - RETURN_FALSE; } - default: - RETURN_FALSE; + break; + EMPTY_SWITCH_DEFAULT_CASE() } + RETURN_FALSE; } void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { From 1f8dde4a992c843e1e234731c712e4f955163fa2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 1 Apr 2017 23:39:58 +0300 Subject: [PATCH 0744/1986] refactor redis_1_response + fix memory leak in redis_long_response --- library.c | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/library.c b/library.c index 70846c0e5c..fdb87cc352 100644 --- a/library.c +++ b/library.c @@ -1171,11 +1171,11 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, } } else { if(ret > LONG_MAX) { /* overflow */ - RETURN_STRINGL(response+1, response_len-1); + RETVAL_STRINGL(response + 1, response_len - 1); } else { - efree(response); - RETURN_LONG((long)ret); + RETVAL_LONG((long)ret); } + efree(response); } } else { efree(response); @@ -1321,39 +1321,22 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, Re z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } -PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API void +redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - char *response; int response_len; - char ret; + zend_bool ret = 0; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) - == NULL) - { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - return; - } else { - RETURN_FALSE; - } + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) != NULL) { + ret = (response[1] == '1'); + efree(response); } - ret = response[1]; - efree(response); IF_NOT_ATOMIC() { - if(ret == '1') { - add_next_index_bool(z_tab, 1); - } else { - add_next_index_bool(z_tab, 0); - } + add_next_index_bool(z_tab, ret); } else { - if (ret == '1') { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(ret); } } From f81694e69c22d40babe16c6f153ea7a4c10f0e15 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 3 Apr 2017 21:55:05 +0300 Subject: [PATCH 0745/1986] Use crc32 table from PHP distro (#1144) * Use crc32 table from PHP distro * Remove rcrc32 function --- redis_array.c | 51 ---------------------------------------------- redis_array.h | 2 -- redis_array_impl.c | 8 +++++++- 3 files changed, 7 insertions(+), 54 deletions(-) diff --git a/redis_array.c b/redis_array.c index 132bb01a2c..6e3b0a234d 100644 --- a/redis_array.c +++ b/redis_array.c @@ -220,57 +220,6 @@ redis_array_get(zval *id TSRMLS_DC) return NULL; } -uint32_t rcrc32(const char *s, size_t sz) { - - static const uint32_t table[256] = { - 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535, - 0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD, - 0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D, - 0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC, - 0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4, - 0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C, - 0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,0x26D930AC, - 0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, - 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB, - 0xB6662D3D,0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F, - 0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB, - 0x086D3D2D,0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E, - 0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA, - 0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,0x4DB26158,0x3AB551CE, - 0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A, - 0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, - 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409, - 0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81, - 0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739, - 0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8, - 0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0xF00F9344,0x8708A3D2,0x1E01F268, - 0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0, - 0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8, - 0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, - 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF, - 0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703, - 0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7, - 0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A, - 0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE, - 0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242, - 0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6, - 0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, - 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D, - 0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5, - 0x47B2CF7F,0x30B5FFE9,0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605, - 0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94, - 0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D}; - - unsigned long ret = 0xffffffff; - size_t i; - - for (i = 0; i < sz; i++) { - ret = (ret >> 8) ^ table[ (ret ^ ((unsigned char)s[i])) & 0xFF ]; - } - return (ret ^ 0xFFFFFFFF); - -} - /* {{{ proto RedisArray RedisArray::__construct() Public constructor */ PHP_METHOD(RedisArray, __construct) diff --git a/redis_array.h b/redis_array.h index 400ea00984..fe5eefa767 100644 --- a/redis_array.h +++ b/redis_array.h @@ -56,8 +56,6 @@ typedef struct RedisArray_ { struct RedisArray_ *prev; } RedisArray; -uint32_t rcrc32(const char *s, size_t sz); - #if (PHP_MAJOR_VERSION < 7) zend_object_value create_redis_array_object(zend_class_entry *ce TSRMLS_DC); void free_redis_array_object(void *object TSRMLS_DC); diff --git a/redis_array_impl.c b/redis_array_impl.c index 37fbb028de..bb803c0207 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -23,6 +23,7 @@ #include "php_variables.h" #include "SAPI.h" #include "ext/standard/url.h" +#include "ext/standard/crc32.h" #define PHPREDIS_INDEX_NAME "__phpredis_array_index__" @@ -460,9 +461,14 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D efree(out); } else { uint64_t h64; + unsigned long ret = 0xffffffff; + size_t i; /* hash */ - hash = rcrc32(out, out_len); + for (i = 0; i < out_len; ++i) { + CRC32(ret, (unsigned char)out[i]); + } + hash = (ret ^ 0xffffffff); efree(out); /* get position on ring */ From 8978b3aaf2961cc7fca2228950df3da5549c7e8a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 3 Apr 2017 12:29:42 -0700 Subject: [PATCH 0746/1986] Tabs are evil --- redis_array.c | 140 ++++++++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 68 deletions(-) diff --git a/redis_array.c b/redis_array.c index 6e3b0a234d..038a7e1a11 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1005,11 +1005,11 @@ PHP_METHOD(RedisArray, mget) /* MSET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mset) { - zval *object, *z_keys, z_argarray, *data, z_ret, **argv; - int i, n; - RedisArray *ra; - int *pos, argc, *argc_each; - HashTable *h_keys; + zval *object, *z_keys, z_argarray, *data, z_ret, **argv; + int i = 0, n; + RedisArray *ra; + int *pos, argc, *argc_each; + HashTable *h_keys; char *key, **keys, kbuf[40]; int key_len, *key_lens; zend_string *zkey; @@ -1019,106 +1019,110 @@ PHP_METHOD(RedisArray, mset) RETURN_FALSE; } - /* Multi/exec support */ + /* Multi/exec support */ HANDLE_MULTI_EXEC(ra, "MSET"); - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", - &object, redis_array_ce, &z_keys) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + &object, redis_array_ce, &z_keys) == FAILURE) + { + RETURN_FALSE; + } - /* init data structures */ - h_keys = Z_ARRVAL_P(z_keys); - argc = zend_hash_num_elements(h_keys); - argv = emalloc(argc * sizeof(zval*)); - pos = emalloc(argc * sizeof(int)); - keys = emalloc(argc * sizeof(char*)); - key_lens = emalloc(argc * sizeof(int)); + /* init data structures */ + h_keys = Z_ARRVAL_P(z_keys); + argc = zend_hash_num_elements(h_keys); + argv = emalloc(argc * sizeof(zval*)); + pos = emalloc(argc * sizeof(int)); + keys = emalloc(argc * sizeof(char*)); + key_lens = emalloc(argc * sizeof(int)); - argc_each = emalloc(ra->count * sizeof(int)); - memset(argc_each, 0, ra->count * sizeof(int)); + argc_each = emalloc(ra->count * sizeof(int)); + memset(argc_each, 0, ra->count * sizeof(int)); - /* associate each key to a redis node */ - i = 0; + /* associate each key to a redis node */ ZEND_HASH_FOREACH_KEY_VAL(h_keys, idx, zkey, data) { - /* If the key isn't a string, make a string representation of it */ + /* If the key isn't a string, make a string representation of it */ if (zkey) { key_len = zkey->len; key = zkey->val; } else { - key_len = snprintf(kbuf, sizeof(kbuf), "%lu", idx); + key_len = snprintf(kbuf, sizeof(kbuf), "%lu", idx); key = kbuf; } if (ra_find_node(ra, key, (int)key_len, &pos[i] TSRMLS_CC) == NULL) { // TODO: handle } - argc_each[pos[i]]++; /* count number of keys per node */ - keys[i] = estrndup(key, key_len); - key_lens[i] = (int)key_len; - argv[i] = data; + + argc_each[pos[i]]++; /* count number of keys per node */ + keys[i] = estrndup(key, key_len); + key_lens[i] = (int)key_len; + argv[i] = data; i++; - } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); - /* calls */ - for(n = 0; n < ra->count; ++n) { /* for each node */ - /* We don't even need to make a call to this node if no keys go there */ - if(!argc_each[n]) continue; + /* calls */ + for (n = 0; n < ra->count; ++n) { /* for each node */ + /* We don't even need to make a call to this node if no keys go there */ + if(!argc_each[n]) continue; - int found = 0; + int found = 0; + + /* copy args */ + array_init(&z_argarray); + for(i = 0; i < argc; ++i) { + if(pos[i] != n) continue; - /* copy args */ - array_init(&z_argarray); - for(i = 0; i < argc; ++i) { - if(pos[i] != n) continue; zval zv, *z_tmp = &zv; #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); #endif ZVAL_ZVAL(z_tmp, argv[i], 1, 0); - add_assoc_zval_ex(&z_argarray, keys[i], key_lens[i], z_tmp); - found++; - } + add_assoc_zval_ex(&z_argarray, keys[i], key_lens[i], z_tmp); + found++; + } - if(!found) - { - zval_dtor(&z_argarray); - continue; /* don't run empty MSETs */ - } + if(!found) { + zval_dtor(&z_argarray); + continue; /* don't run empty MSETs */ + } - if(ra->index) { /* add MULTI */ - ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); - } + if(ra->index) { /* add MULTI */ + ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); + } zval z_fun; + /* prepare call */ ZVAL_STRINGL(&z_fun, "MSET", 4); - /* call */ + + /* call */ call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); - zval_dtor(&z_fun); - zval_dtor(&z_ret); + zval_dtor(&z_fun); + zval_dtor(&z_ret); - if(ra->index) { - ra_index_keys(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SADD to add keys to node index */ - ra_index_exec(&ra->redis[n], NULL, 0 TSRMLS_CC); /* run EXEC */ - } - zval_dtor(&z_argarray); - } + if(ra->index) { + ra_index_keys(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SADD to add keys to node index */ + ra_index_exec(&ra->redis[n], NULL, 0 TSRMLS_CC); /* run EXEC */ + } - /* Free any keys that we needed to allocate memory for, because they weren't strings */ - for(i = 0; i < argc; i++) { - efree(keys[i]); - } + zval_dtor(&z_argarray); + } - /* cleanup */ - efree(keys); - efree(key_lens); - efree(argv); - efree(pos); - efree(argc_each); + /* Free any keys that we needed to allocate memory for, because they weren't strings */ + for(i = 0; i < argc; i++) { + efree(keys[i]); + } + + /* cleanup */ + efree(keys); + efree(key_lens); + efree(argv); + efree(pos); + efree(argc_each); - RETURN_TRUE; + RETURN_TRUE; } /* DEL will distribute the call to several nodes and regroup the values. */ From 520e06a8a9f65b05bd6361a344ae40e777407afb Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 5 Apr 2017 14:30:40 +0300 Subject: [PATCH 0747/1986] Increasing read buffers size. When Redis server is running in protected mode it returns error message more than 1024 bites long so first call of redis_sock_gets doesn't read whole message and next call causes "protocol error, got '%c' as reply type byte" exception. --- cluster_library.c | 2 +- library.c | 14 +++++++------- redis.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index a40c00d256..d37f29ef20 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1034,7 +1034,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type // In the event of an ERROR, check if it's a MOVED/ASK error if(*reply_type == TYPE_ERR) { - char inbuf[1024]; + char inbuf[4096]; int moved; // Attempt to read the error diff --git a/library.c b/library.c index fdb87cc352..09cd7d3adc 100644 --- a/library.c +++ b/library.c @@ -433,7 +433,7 @@ PHP_REDIS_API zval * redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { - char inbuf[1024]; + char inbuf[4096]; int numElems; size_t len; @@ -500,7 +500,7 @@ redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) { - char inbuf[1024]; + char inbuf[4096]; size_t len; *buf_len = 0; @@ -1251,7 +1251,7 @@ static int redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int unserialize, int decode) { - char inbuf[1024]; + char inbuf[4096]; int numElems; size_t len; @@ -1688,7 +1688,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - char inbuf[1024]; + char inbuf[4096]; int numElems; size_t len; @@ -1731,7 +1731,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - char inbuf[1024]; + char inbuf[4096]; int numElems; size_t len; @@ -1808,7 +1808,7 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * keys with their returned values */ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - char inbuf[1024], *response; + char inbuf[4096], *response; int response_len; int i, numElems; size_t len; @@ -2147,7 +2147,7 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval *z_ret TSRMLS_DC) { // Buffer to read our single line reply - char inbuf[1024]; + char inbuf[4096]; size_t line_size; /* Attempt to read our single line reply */ diff --git a/redis.c b/redis.c index 9187afec58..46d99c65f4 100644 --- a/redis.c +++ b/redis.c @@ -2324,7 +2324,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME RedisSock *redis_sock) { - char inbuf[1024]; + char inbuf[4096]; int numElems; size_t len; From f0c25a21ac6489e74d827b6c196790d4d926d461 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 10 Apr 2017 10:29:17 -0700 Subject: [PATCH 0748/1986] Better ducmentation for SCAN Fixes #1151 --- README.markdown | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index 444512705f..9571afa707 100644 --- a/README.markdown +++ b/README.markdown @@ -1013,13 +1013,14 @@ _**Description**_: Scan the keyspace for keys ##### *Example* ~~~ $it = NULL; /* Initialize our iterator to NULL */ -$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* retry when we get no keys back */ -while($arr_keys = $redis->scan($it)) { +do { + // Use global option $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); to ignore empty results + $arr_keys = $redis->scan($it); foreach($arr_keys as $str_key) { echo "Here is a key: $str_key\n"; } - echo "No more keys to scan!\n"; -} +} while ($it > 0); +echo "No more keys to scan!\n"; ~~~ ### object From c5991fe7d122ca0d74c8dcbd51db369bc9e3c2f3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 10 Apr 2017 12:46:46 -0700 Subject: [PATCH 0749/1986] Show examples for SCAN_RETRY and SCAN_NORETRY --- README.markdown | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/README.markdown b/README.markdown index 9571afa707..98df2278a8 100644 --- a/README.markdown +++ b/README.markdown @@ -1008,19 +1008,38 @@ _**Description**_: Scan the keyspace for keys *LONG, Optional*: Count of keys per iteration (only a suggestion to Redis) ##### *Return value* -*Array, boolean*: This function will return an array of keys or FALSE if there are no more keys +*Array, boolean*: This function will return an array of keys or FALSE if Redis returned zero keys ##### *Example* ~~~ -$it = NULL; /* Initialize our iterator to NULL */ + +/* Without enabling Redis::SCAN_RETRY (default condition) */ +$it = NULL; do { - // Use global option $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); to ignore empty results + // Scan for some keys $arr_keys = $redis->scan($it); - foreach($arr_keys as $str_key) { - echo "Here is a key: $str_key\n"; + + // Redis may return empty results, so protect against that + if ($arr_keys !== FALSE) { + foreach($arr_keys as $str_key) { + echo "Here is a key: $str_key\n"; + } } } while ($it > 0); echo "No more keys to scan!\n"; + +/* With Redis::SCAN_RETRY enabled */ +$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); +$it = NULL; + +/* phpredis will retry the SCAN command if empty results are returned from the + server, so no empty results check is required. */ +while ($arr_keys = $redis->scan($it)) { + foreach ($arr_keys as $str_key) { + echo "Here is a key: $str_key\n"; + } +} +echo "No more keys to scan!\n"; ~~~ ### object From b56dc49eec94bbb9b0e26aa3d16582c3590029d6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 8 Apr 2017 22:56:21 +0300 Subject: [PATCH 0750/1986] Processing `read_timeout` parameter + update docs --- README.markdown | 4 +++- arrays.markdown | 7 +++++++ cluster.markdown | 8 ++------ cluster_library.c | 4 ++-- library.c | 9 +++++---- library.h | 2 +- redis.c | 20 +++++++++++++------- redis_array.c | 15 +++++++++++++-- redis_array.h | 1 + redis_array_impl.c | 29 ++++++++++++++++++++++++----- redis_array_impl.h | 2 +- redis_cluster.c | 4 ++++ redis_session.c | 9 ++++++--- 13 files changed, 82 insertions(+), 32 deletions(-) diff --git a/README.markdown b/README.markdown index 98df2278a8..7c8e2fbaca 100644 --- a/README.markdown +++ b/README.markdown @@ -82,7 +82,7 @@ You can install install it using Homebrew: phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions: ~~~ session.save_handler = redis -session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2" +session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2&read_timeout=2.5" ~~~ `session.save_path` can have a simple `host:port` format too, but you need to provide the `tcp://` scheme if you want to use the parameters. The following parameters are available: @@ -202,6 +202,7 @@ _**Description**_: Connects to a Redis instance. *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) *reserved*: should be NULL if retry_interval is specified *retry_interval*: int, value in milliseconds (optional) +*read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited) ##### *Return value* @@ -238,6 +239,7 @@ persistent equivalents. *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) *persistent_id*: string. identity for the requested persistent connection *retry_interval*: int, value in milliseconds (optional) +*read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited) ##### *Return value* diff --git a/arrays.markdown b/arrays.markdown index efc3f05db4..c41ef616cb 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -55,6 +55,13 @@ The connect_timeout value is a double and is used to specify a timeout in number $ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("connect_timeout" => 0.5)); +#### Specifying the "read_timeout" parameter +The read_timeout value is a double and is used to specify a timeout in number of seconds when waiting response from the server. +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("read_timeout" => 0.5));
+
+ + #### Defining arrays in Redis.ini Because php.ini parameters must be pre-defined, Redis Arrays must all share the same .ini settings. diff --git a/cluster.markdown b/cluster.markdown index 2ba3ee0610..336fcc4986 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -13,15 +13,11 @@ To maintain consistency with the RedisArray class, one can create and connect to $obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003')); // Connect and specify timeout and read_timeout -$obj_cluster = new RedisCluster( - NULL, Array("host:7000", "host:7001"), 1.5, 1.5 -); +$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5); // Connect with read/write timeout as well as specify that phpredis should use // persistent connections to each node. -$obj_cluster = new RedisCluster( - NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true -); +$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true); diff --git a/cluster_library.c b/cluster_library.c index d37f29ef20..c1efd0db6a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -649,7 +649,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, // Attach socket node->sock = redis_sock_create(host, host_len, port, c->timeout, - c->persistent, NULL, 0, 1); + c->read_timeout, c->persistent, NULL, 0, 1); return node; } @@ -916,7 +916,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { // Allocate a structure for this seed redis_sock = redis_sock_create(str, psep-str, (unsigned short)atoi(psep+1), cluster->timeout, - cluster->persistent, NULL, 0, 0); + cluster->read_timeout, cluster->persistent, NULL, 0, 0); // Index this seed by host/port key_len = snprintf(key, sizeof(key), "%s:%u", redis_sock->host, diff --git a/library.c b/library.c index 09cd7d3adc..4f3ac4ed4a 100644 --- a/library.c +++ b/library.c @@ -1467,9 +1467,10 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * redis_sock_create */ PHP_REDIS_API RedisSock* -redis_sock_create(char *host, int host_len, unsigned short port, double timeout, - int persistent, char *persistent_id, long retry_interval, - zend_bool lazy_connect) +redis_sock_create(char *host, int host_len, unsigned short port, + double timeout, double read_timeout, + int persistent, char *persistent_id, + long retry_interval, zend_bool lazy_connect) { RedisSock *redis_sock; @@ -1490,7 +1491,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, double timeout, redis_sock->port = port; redis_sock->timeout = timeout; - redis_sock->read_timeout = timeout; + redis_sock->read_timeout = read_timeout; redis_sock->serializer = REDIS_SERIALIZER_NONE; redis_sock->mode = ATOMIC; diff --git a/library.h b/library.h index 62a38be441..0fa42dfe6d 100644 --- a/library.h +++ b/library.h @@ -33,7 +33,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); +PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index 46d99c65f4..c9996bd006 100644 --- a/redis.c +++ b/redis.c @@ -807,7 +807,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) char *host = NULL, *persistent_id = NULL; zend_long port = -1, retry_interval = 0; strlen_t host_len, persistent_id_len; - double timeout = 0.0; + double timeout = 0.0, read_timeout = 0.0; redis_object *redis; #ifdef ZTS @@ -817,10 +817,10 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #endif if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|ldsl", &object, redis_ce, &host, + "Os|ldsld", &object, redis_ce, &host, &host_len, &port, &timeout, &persistent_id, - &persistent_id_len, &retry_interval) - == FAILURE) + &persistent_id_len, &retry_interval, + &read_timeout) == FAILURE) { return FAILURE; } else if (!persistent) { @@ -828,8 +828,14 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid timeout", - 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, + "Invalid connect timeout", 0 TSRMLS_CC); + return FAILURE; + } + + if (read_timeout < 0L || read_timeout > INT_MAX) { + zend_throw_exception(redis_exception_ce, + "Invalid read timeout", 0 TSRMLS_CC); return FAILURE; } @@ -855,7 +861,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) redis_free_socket(redis->sock); } - redis->sock = redis_sock_create(host, host_len, port, timeout, persistent, + redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { diff --git a/redis_array.c b/redis_array.c index 038a7e1a11..2cf2303d3b 100644 --- a/redis_array.c +++ b/redis_array.c @@ -230,7 +230,7 @@ PHP_METHOD(RedisArray, __construct) HashTable *hPrev = NULL, *hOpts = NULL; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; - double d_connect_timeout = 0; + double d_connect_timeout = 0, read_timeout = 0.0; redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { @@ -300,6 +300,17 @@ PHP_METHOD(RedisArray, __construct) d_connect_timeout = atof(Z_STRVAL_P(zpData)); } } + + /* extract read_timeout option */ + if ((zpData = zend_hash_str_find(hOpts, "read_timeout", sizeof("read_timeout") - 1)) != NULL) { + if (Z_TYPE_P(zpData) == IS_DOUBLE) { + read_timeout = Z_DVAL_P(zpData); + } else if (Z_TYPE_P(zpData) == IS_LONG) { + read_timeout = Z_LVAL_P(zpData); + } else if (Z_TYPE_P(zpData) == IS_STRING) { + read_timeout = atof(Z_STRVAL_P(zpData)); + } + } } /* extract either name of list of hosts from z0 */ @@ -309,7 +320,7 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); break; default: diff --git a/redis_array.h b/redis_array.h index fe5eefa767..cab0a26e4d 100644 --- a/redis_array.h +++ b/redis_array.h @@ -52,6 +52,7 @@ typedef struct RedisArray_ { zval z_dist; /* key distributor, callable */ zval z_pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ + double read_timeout; /* socket read timeout */ struct RedisArray_ *prev; } RedisArray; diff --git a/redis_array_impl.c b/redis_array_impl.c index bb803c0207..4918d81453 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -79,7 +79,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b #endif /* create socket */ - redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); + redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); if (!b_lazy_connect) { @@ -173,13 +173,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_retry_interval; zval z_params_pconnect; zval z_params_connect_timeout; + zval z_params_read_timeout; zval z_params_lazy_connect; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; - double d_connect_timeout = 0; + double d_connect_timeout = 0, read_timeout = 0.0; HashTable *hHosts = NULL, *hPrev = NULL; size_t name_len = strlen(name); char *iptr; @@ -297,9 +298,25 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { d_connect_timeout = Z_LVAL_P(z_data); } } + + /* find read timeout option */ + array_init(&z_params_connect_timeout); + if ((iptr = INI_STR("redis.arrays.readtimeout")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_read_timeout TSRMLS_CC); + } + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_read_timeout), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_DOUBLE) { + read_timeout = Z_DVAL_P(z_data); + } else if (Z_TYPE_P(z_data) == IS_STRING) { + read_timeout = atof(Z_STRVAL_P(z_data)); + } else if (Z_TYPE_P(z_data) == IS_LONG) { + read_timeout = Z_LVAL_P(z_data); + } + } + /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; @@ -315,6 +332,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_retry_interval); zval_dtor(&z_params_pconnect); zval_dtor(&z_params_connect_timeout); + zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -323,7 +341,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout TSRMLS_DC) { int i, count; RedisArray *ra; @@ -341,6 +359,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->auto_rehash = 0; ra->pconnect = b_pconnect; ra->connect_timeout = connect_timeout; + ra->read_timeout = read_timeout; if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { @@ -352,7 +371,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); diff --git a/redis_array_impl.h b/redis_array_impl.h index b385986949..082dc17206 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -11,7 +11,7 @@ RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); diff --git a/redis_cluster.c b/redis_cluster.c index 869434e473..8508da0651 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -438,6 +438,8 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { timeout = atof(Z_STRVAL_P(z_value)); } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { timeout = Z_DVAL_P(z_value); + } else if (Z_TYPE_P(z_value) == IS_LONG) { + timeout = Z_LVAL_P(z_value); } } @@ -451,6 +453,8 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { read_timeout = atof(Z_STRVAL_P(z_value)); } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { read_timeout = Z_DVAL_P(z_value); + } else if (Z_TYPE_P(z_value) == IS_LONG) { + timeout = Z_LVAL_P(z_value); } } diff --git a/redis_session.c b/redis_session.c index a753d198e7..607fc8f0ce 100644 --- a/redis_session.c +++ b/redis_session.c @@ -206,7 +206,7 @@ PS_OPEN_FUNC(redis) if (i < j) { int weight = 1; - double timeout = 86400.0; + double timeout = 86400.0, read_timeout = 0.0; int persistent = 0; int database = -1; char *prefix = NULL, *auth = NULL, *persistent_id = NULL; @@ -246,6 +246,9 @@ PS_OPEN_FUNC(redis) if ((param = zend_hash_str_find(Z_ARRVAL(params), "timeout", sizeof("timeout") - 1)) != NULL) { timeout = atof(Z_STRVAL_P(param)); } + if ((param = zend_hash_str_find(Z_ARRVAL(params), "read_timeout", sizeof("read_timeout") - 1)) != NULL) { + read_timeout = atof(Z_STRVAL_P(param)); + } if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent", sizeof("persistent") - 1)) != NULL) { persistent = (atol(Z_STRVAL_P(param)) == 1 ? 1 : 0); } @@ -280,9 +283,9 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if(url->host) { - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); } else { /* unix */ - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); From e3723983446328bc5cb89c2b6f1de2666891f356 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 11 Apr 2017 18:04:53 +0300 Subject: [PATCH 0751/1986] Modifying `redis_error_throw` to throw exception for all non recoverable errors. --- common.h | 8 -------- library.c | 39 +++++++++++++-------------------------- 2 files changed, 13 insertions(+), 34 deletions(-) diff --git a/common.h b/common.h index 8bc911eefe..84f39c2758 100644 --- a/common.h +++ b/common.h @@ -454,14 +454,6 @@ typedef enum _PUBSUB_TYPE { #define BITOP_MIN_OFFSET 0 #define BITOP_MAX_OFFSET 4294967295U -/* Specific error messages we want to throw against */ -#define REDIS_ERR_LOADING_MSG "LOADING Redis is loading the dataset in memory" -#define REDIS_ERR_LOADING_KW "LOADING" -#define REDIS_ERR_AUTH_MSG "NOAUTH Authentication required." -#define REDIS_ERR_AUTH_KW "NOAUTH" -#define REDIS_ERR_SYNC_MSG "MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'" -#define REDIS_ERR_SYNC_KW "MASTERDOWN" - #define IF_ATOMIC() if (redis_sock->mode == ATOMIC) #define IF_NOT_ATOMIC() if (redis_sock->mode != ATOMIC) #define IF_MULTI() if (redis_sock->mode == MULTI) diff --git a/library.c b/library.c index 4f3ac4ed4a..d144ecfa98 100644 --- a/library.c +++ b/library.c @@ -112,23 +112,15 @@ static int resend_auth(RedisSock *redis_sock TSRMLS_DC) { * 2) AUTH * 3) LOADING */ -static void redis_error_throw(char *err, size_t err_len TSRMLS_DC) { - /* Handle stale data error (slave syncing with master) */ - if (err_len == sizeof(REDIS_ERR_SYNC_MSG) - 1 && - !memcmp(err,REDIS_ERR_SYNC_KW,sizeof(REDIS_ERR_SYNC_KW)-1)) - { - zend_throw_exception(redis_exception_ce, - "SYNC with master in progress or master down!", 0 TSRMLS_CC); - } else if (err_len == sizeof(REDIS_ERR_LOADING_MSG) - 1 && - !memcmp(err,REDIS_ERR_LOADING_KW,sizeof(REDIS_ERR_LOADING_KW)-1)) - { - zend_throw_exception(redis_exception_ce, - "Redis is LOADING the dataset", 0 TSRMLS_CC); - } else if (err_len == sizeof(REDIS_ERR_AUTH_MSG) -1 && - !memcmp(err,REDIS_ERR_AUTH_KW,sizeof(REDIS_ERR_AUTH_KW)-1)) - { - zend_throw_exception(redis_exception_ce, - "Failed to AUTH connection", 0 TSRMLS_CC); +static void +redis_error_throw(RedisSock *redis_sock TSRMLS_DC) +{ + if (redis_sock != NULL && redis_sock->err != NULL && + memcmp(redis_sock->err, "ERR", sizeof("ERR") - 1) != 0 && + memcmp(redis_sock->err, "NOSCRIPT", sizeof("NOSCRIPT") - 1) != 0 && + memcmp(redis_sock->err, "WRONGTYPE", sizeof("WRONGTYPE") - 1) != 0 + ) { + zend_throw_exception(redis_exception_ce, redis_sock->err, 0 TSRMLS_CC); } } @@ -513,13 +505,8 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) redis_sock_set_err(redis_sock, inbuf+1, len); /* Filter our ERROR through the few that should actually throw */ - redis_error_throw(inbuf + 1, len TSRMLS_CC); + redis_error_throw(redis_sock TSRMLS_CC); - /* Handle stale data error */ - if(memcmp(inbuf + 1, "-ERR SYNC ", 10) == 0) { - zend_throw_exception(redis_exception_ce, - "SYNC with master in progress", 0 TSRMLS_CC); - } return NULL; case '$': *buf_len = atoi(inbuf + 1); @@ -2159,12 +2146,12 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, // If this is an error response, check if it is a SYNC error, and throw in // that case if(reply_type == TYPE_ERR) { - /* Handle throwable errors */ - redis_error_throw(inbuf, line_size TSRMLS_CC); - /* Set our last error */ redis_sock_set_err(redis_sock, inbuf, line_size); + /* Handle throwable errors */ + redis_error_throw(redis_sock TSRMLS_CC); + /* Set our response to FALSE */ ZVAL_FALSE(z_ret); } else { From d2e203a630437f4de657c9b0b34d689970a56b21 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 16 Apr 2017 21:21:31 -0700 Subject: [PATCH 0752/1986] Adds optional COUNT argument to sPop Fixes #1145 --- README.markdown | 12 ++++++++++-- redis.c | 9 ++++++++- redis_cluster.c | 8 +++++++- redis_commands.c | 1 + tests/RedisTest.php | 43 +++++++++++++++++++++++++++++++++++-------- tests/TestSuite.php | 6 ++++-- 6 files changed, 65 insertions(+), 14 deletions(-) diff --git a/README.markdown b/README.markdown index 7c8e2fbaca..decac6812f 100644 --- a/README.markdown +++ b/README.markdown @@ -2103,7 +2103,7 @@ $redis->lSize('key1');/* 2 */ * [sIsMember, sContains](#sismember-scontains) - Determine if a given value is a member of a set * [sMembers, sGetMembers](#smembers-sgetmembers) - Get all the members in a set * [sMove](#smove) - Move a member from one set to another -* [sPop](#spop) - Remove and return a random member from a set +* [sPop](#spop) - Remove and return one or more members of a set at random * [sRandMember](#srandmember) - Get one or multiple random members from a set * [sRem, sRemove](#srem-sremove) - Remove one or more members from a set * [sUnion](#sunion) - Add multiple sets @@ -2373,9 +2373,13 @@ $redis->sMove('key1', 'key2', 'member13'); /* 'key1' => {'member11', 'member12' _**Description**_: Removes and returns a random element from the set value at Key. ##### *Parameters* *key* -##### *Return value* +*count*: Integer, optional +##### *Return value (without count argument)* *String* "popped" value *Bool* `FALSE` if set identified by key is empty or doesn't exist. +##### *Return value (with count argument)* +*Array*: Member(s) returned or an empty array if the set doesn't exist +*Bool*: `FALSE` on error if the key is not a set ##### *Example* ~~~ $redis->sAdd('key1' , 'member1'); @@ -2383,6 +2387,10 @@ $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/ $redis->sPop('key1'); /* 'member1', 'key1' => {'member3', 'member2'} */ $redis->sPop('key1'); /* 'member3', 'key1' => {'member2'} */ + +/* With count */ +$redis->sAdd('key2', 'member1', 'member2', 'member3'); +$redis->sPop('key2', 3); /* Will return all members but in no particular order */ ~~~ ### sRandMember diff --git a/redis.c b/redis.c index c9996bd006..5c5be352af 100644 --- a/redis.c +++ b/redis.c @@ -1340,7 +1340,14 @@ PHP_METHOD(Redis, sMove) /* {{{ proto string Redis::sPop(string key) */ PHP_METHOD(Redis, sPop) { - REDIS_PROCESS_KW_CMD("SPOP", redis_key_cmd, redis_string_response); + if (ZEND_NUM_ARGS() == 1) { + REDIS_PROCESS_KW_CMD("SPOP", redis_key_cmd, redis_string_response); + } else if (ZEND_NUM_ARGS() == 2) { + REDIS_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, redis_sock_read_multibulk_reply); + } else { + ZEND_WRONG_PARAM_COUNT(); + } + } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 8508da0651..969430bc9c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1133,7 +1133,13 @@ PHP_METHOD(RedisCluster, lset) { /* {{{ proto string RedisCluster::spop(string key) */ PHP_METHOD(RedisCluster, spop) { - CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_cmd, cluster_bulk_resp, 0); + if (ZEND_NUM_ARGS() == 1) { + CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_cmd, cluster_bulk_resp, 0); + } else if (ZEND_NUM_ARGS() == 2) { + CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, cluster_mbulk_resp, 0); + } else { + ZEND_WRONG_PARAM_COUNT(); + } } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 887fc9d779..5a5fd9636d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -343,6 +343,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, CMD_SET_SLOT(slot, key, key_len); if (key_free) efree(key); + // Success! return SUCCESS; } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 53cf6e9def..3309914cde 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1154,19 +1154,46 @@ public function testsMove() public function testsPop() { $this->redis->del('set0'); - $this->assertTrue($this->redis->sPop('set0') === FALSE); + $this->assertTrue($this->redis->sPop('set0') === FALSE); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); - $v0 = $this->redis->sPop('set0'); - $this->assertTrue(1 === $this->redis->scard('set0')); - $this->assertTrue($v0 === 'val' || $v0 === 'val2'); - $v1 = $this->redis->sPop('set0'); - $this->assertTrue(0 === $this->redis->scard('set0')); - $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); + $v0 = $this->redis->sPop('set0'); + $this->assertTrue(1 === $this->redis->scard('set0')); + $this->assertTrue($v0 === 'val' || $v0 === 'val2'); + $v1 = $this->redis->sPop('set0'); + $this->assertTrue(0 === $this->redis->scard('set0')); + $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); - $this->assertTrue($this->redis->sPop('set0') === FALSE); + $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() { diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 5bbd98bca9..03186bf983 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -52,16 +52,18 @@ public static function make_warning($str_msg) { } protected function assertFalse($bool) { - $this->assertTrue(!$bool); + return $this->assertTrue(!$bool); } protected function assertTrue($bool) { if($bool) - return; + return true; $bt = debug_backtrace(false); self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + + return false; } protected function assertLess($a, $b) { From 4e56ba804cdfcaae36b98f1bbdcf9ffb4e77b6e2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Apr 2017 23:43:29 +0300 Subject: [PATCH 0753/1986] Fix #1156 --- common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.h b/common.h index 84f39c2758..6cfa7d0cd0 100644 --- a/common.h +++ b/common.h @@ -348,7 +348,7 @@ zval_get_string(zval *op) } case IS_DOUBLE: { zstr->gc = 0x10; - zstr->len = spprintf(&zstr->val, 0, "%g", Z_DVAL_P(op)); + zstr->len = spprintf(&zstr->val, 0, "%.16g", Z_DVAL_P(op)); break; } EMPTY_SWITCH_DEFAULT_CASE() From 43e1e0faffcec9118fdaa65c592f91cb0d38725e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 21 Apr 2017 00:30:37 +0300 Subject: [PATCH 0754/1986] refactoring (#1155) Fix memory leak in `redis_long_response` when LONG_MAX overflow. Add return -1 in `redis_read_reply_type` when `php_stream_getc` returns EOF. Code formatting in `PHP_METHOD(Redis, exec)`. --- common.h | 2 +- library.c | 9 ++++----- redis.c | 22 +++++++--------------- redis_array.h | 2 -- 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/common.h b/common.h index 84f39c2758..786a972c4d 100644 --- a/common.h +++ b/common.h @@ -434,9 +434,9 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_PREFIX 2 #define REDIS_OPT_READ_TIMEOUT 3 #define REDIS_OPT_SCAN 4 +#define REDIS_OPT_FAILOVER 5 /* cluster options */ -#define REDIS_OPT_FAILOVER 5 #define REDIS_FAILOVER_NONE 0 #define REDIS_FAILOVER_ERROR 1 #define REDIS_FAILOVER_DISTRIBUTE 2 diff --git a/library.c b/library.c index d144ecfa98..1035c7667c 100644 --- a/library.c +++ b/library.c @@ -1153,7 +1153,6 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, if(ret > LONG_MAX) { /* overflow */ add_next_index_stringl(z_tab, response + 1, response_len - 1); } else { - efree(response); add_next_index_long(z_tab, (long)ret); } } else { @@ -1162,16 +1161,15 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, } else { RETVAL_LONG((long)ret); } - efree(response); } } else { - efree(response); IF_NOT_ATOMIC() { add_next_index_null(z_tab); } else { - RETURN_FALSE; + RETVAL_FALSE; } } + efree(response); } /* Helper method to convert [key, value, key, value] into [key => value, @@ -1614,7 +1612,7 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) redis_sock->watching = 0; /* Stil valid? */ - if(redis_sock->stream && !redis_sock->persistent) { + if (!redis_sock->persistent) { php_stream_close(redis_sock->stream); } redis_sock->stream = NULL; @@ -2104,6 +2102,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + return -1; } // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can diff --git a/redis.c b/redis.c index 5c5be352af..8b7f56a65f 100644 --- a/redis.c +++ b/redis.c @@ -38,10 +38,6 @@ #include "library.h" -#define R_SUB_CALLBACK_CLASS_TYPE 1 -#define R_SUB_CALLBACK_FT_TYPE 2 -#define R_SUB_CLOSURE_TYPE 3 - #ifdef PHP_SESSION extern ps_module ps_mod_redis; extern ps_module ps_mod_redis_cluster; @@ -2366,7 +2362,7 @@ PHP_METHOD(Redis, exec) { RedisSock *redis_sock; char *cmd; - int cmd_len; + int cmd_len, ret; zval *object; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), @@ -2381,19 +2377,15 @@ PHP_METHOD(Redis, exec) SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); - if(redis_sock_read_multibulk_multi_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock) < 0) - { - zval_dtor(return_value); - free_reply_callbacks(redis_sock); - redis_sock->mode = ATOMIC; - redis_sock->watching = 0; - RETURN_FALSE; - } + ret = redis_sock_read_multibulk_multi_reply( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); free_reply_callbacks(redis_sock); redis_sock->mode = ATOMIC; redis_sock->watching = 0; + if (ret < 0) { + zval_dtor(return_value); + RETURN_FALSE; + } } IF_PIPELINE() { diff --git a/redis_array.h b/redis_array.h index cab0a26e4d..0f47e6cb69 100644 --- a/redis_array.h +++ b/redis_array.h @@ -8,8 +8,6 @@ #endif #include "common.h" -void redis_destructor_redis_array(zend_resource * rsrc TSRMLS_DC); - PHP_METHOD(RedisArray, __construct); PHP_METHOD(RedisArray, __call); PHP_METHOD(RedisArray, _hosts); From c8079eec835fa09afc2880ad01331984d55756c5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 21 Apr 2017 07:38:00 +0300 Subject: [PATCH 0755/1986] Remove REDIS_DOUBLE_TO_STRING macros --- library.c | 35 ++++++++++++++++------------------- library.h | 24 ------------------------ 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/library.c b/library.c index 1035c7667c..a54f771afc 100644 --- a/library.c +++ b/library.c @@ -586,7 +586,6 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) va_list ap; smart_string buf = {0}; int l = strlen(keyword); - zend_string *dbl_str; va_start(ap, format); @@ -615,11 +614,11 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) case 'f': case 'F': { double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, d); - smart_string_append_long(&buf, dbl_str->len); + char tmp[64]; + int len = snprintf(tmp, sizeof(tmp), "%.16g", d); + smart_string_append_long(&buf, len); smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, dbl_str->val, dbl_str->len); - zend_string_release(dbl_str); + smart_string_appendl(&buf, tmp, len); } break; @@ -666,7 +665,6 @@ redis_cmd_format(char **ret, char *format, ...) { smart_string buf = {0}; va_list ap; char *p = format; - zend_string *dbl_str; va_start(ap, format); @@ -683,11 +681,11 @@ redis_cmd_format(char **ret, char *format, ...) { case 'F': case 'f': { double d = va_arg(ap, double); - REDIS_DOUBLE_TO_STRING(dbl_str, d); - smart_string_append_long(&buf, dbl_str->len); + char tmp[64]; + int len = snprintf(tmp, sizeof(tmp), "%.16g", d); + smart_string_append_long(&buf, len); smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, dbl_str->val, dbl_str->len); - zend_string_release(dbl_str); + smart_string_appendl(&buf, tmp, len); } break; @@ -792,18 +790,17 @@ int redis_cmd_append_sstr_long(smart_string *str, long append) { /* * Append a double to a smart string command */ -int redis_cmd_append_sstr_dbl(smart_string *str, double value) { - zend_string *dbl_str; - int retval; +int +redis_cmd_append_sstr_dbl(smart_string *str, double value) +{ + char tmp[64]; + int len, retval; - /* Convert to double */ - REDIS_DOUBLE_TO_STRING(dbl_str, value); + /* Convert to string */ + len = snprintf(tmp, sizeof(tmp), "%.16g", value); // Append the string - retval = redis_cmd_append_sstr(str, dbl_str->val, dbl_str->len); - - /* Free our double string */ - zend_string_release(dbl_str); + retval = redis_cmd_append_sstr(str, tmp, len); /* Return new length */ return retval; diff --git a/library.h b/library.h index 0fa42dfe6d..2b7e458423 100644 --- a/library.h +++ b/library.h @@ -84,28 +84,4 @@ PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); -#if (PHP_MAJOR_VERSION < 7) -#if ZEND_MODULE_API_NO >= 20100000 -#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl) do { \ - char dbl_decsep = '.'; \ - dbl_str = emalloc(sizeof(zend_string)); \ - dbl_str->val = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \ - dbl_str->len = strlen(dbl_str->val); \ - dbl_str->gc = 0x11; \ -} while (0); -#else -#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl) do { \ - dbl_str = emalloc(sizeof(zend_string)); \ - dbl_str->val = _php_math_number_format(dbl, 16, '.', '\x00'); \ - dbl_str->len = strlen(dbl_str->val); \ - dbl_str->gc = 0x11; \ -} while (0) -#endif -#else -#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl) do { \ - char dbl_decsep = '.'; \ - dbl_str = _php_math_number_format_ex(dbl, 16, &dbl_decsep, 1, NULL, 0); \ -} while (0); -#endif - #endif From 9aa3dbfc3d28d34b17c22bb733068fad1d5fa85d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 22 Apr 2017 21:45:08 -0700 Subject: [PATCH 0756/1986] Allow MIGRATE to accept multiple keys --- library.h | 3 ++ redis.c | 59 +-------------------------------- redis_commands.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 ++ 4 files changed, 93 insertions(+), 58 deletions(-) diff --git a/library.h b/library.h index 2b7e458423..872a8b80d8 100644 --- a/library.h +++ b/library.h @@ -4,6 +4,9 @@ #define REDIS_CMD_APPEND_SSTR_STATIC(sstr, str) \ redis_cmd_append_sstr(sstr, str, sizeof(str)-1); +#define REDIS_CMD_APPEND_SSTR_OPT_STATIC(sstr, opt, str) \ + if (opt) REDIS_CMD_APPEND_SSTR_STATIC(sstr, str); + #define REDIS_CMD_INIT_SSTR_STATIC(sstr, argc, keyword) \ redis_cmd_init_sstr(sstr, argc, keyword, sizeof(keyword)-1); diff --git a/redis.c b/redis.c index 8b7f56a65f..1090809f6f 100644 --- a/redis.c +++ b/redis.c @@ -3250,64 +3250,7 @@ PHP_METHOD(Redis, debug) { /* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, * bool replace]) */ PHP_METHOD(Redis, migrate) { - zval *object; - RedisSock *redis_sock; - char *cmd, *host, *key; - int cmd_len, key_free; - strlen_t host_len, key_len; - zend_bool copy=0, replace=0; - zend_long port, dest_db, timeout; - - // Parse arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Oslsll|bb", &object, redis_ce, &host, - &host_len, &port, &key, &key_len, &dest_db, - &timeout, ©, &replace) == FAILURE) - { - RETURN_FALSE; - } - - /* Grabg our socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { - RETURN_FALSE; - } - - // Prefix our key if we need to, build our command - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* Construct our command */ - if(copy && replace) { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", - host, host_len, port, key, key_len, - dest_db, timeout, "COPY", - sizeof("COPY")-1, "REPLACE", - sizeof("REPLACE")-1); - } else if(copy) { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", - host, host_len, port, key, key_len, - dest_db, timeout, "COPY", - sizeof("COPY")-1); - } else if(replace) { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", - host, host_len, port, key, key_len, - dest_db, timeout, "REPLACE", - sizeof("REPLACE")-1); - } else { - cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", - host, host_len, port, key, key_len, - dest_db, timeout); - } - - /* Free our key if we prefixed it */ - if(key_free) efree(key); - - // Kick off our MIGRATE request - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_CMD(migrate, redis_boolean_response); } /* {{{ proto Redis::_prefix(key) */ diff --git a/redis_commands.c b/redis_commands.c index 5a5fd9636d..7e2ac76d1c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2946,6 +2946,92 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s return SUCCESS; } +/* MIGRATE */ +int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + char *host, *key; + int argc, keyfree; + zval *z_keys, *z_key; + strlen_t hostlen, keylen; + zend_long destdb, port, timeout; + zend_bool copy = 0, replace = 0; + zend_string *zstr; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slzll|bb", &host, &hostlen, &port, + &z_keys, &destdb, &timeout, ©, &replace) == FAILURE) + { + return FAILURE; + } + + /* Protect against being passed an array with zero elements */ + if (Z_TYPE_P(z_keys) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(z_keys)) == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Keys array cannot be empty"); + return FAILURE; + } + + /* host, port, key|"", dest-db, timeout, [copy, replace] [KEYS key1..keyN] */ + argc = 5 + copy + replace; + if (Z_TYPE_P(z_keys) == IS_ARRAY) { + /* +1 for the "KEYS" argument itself */ + argc += 1 + zend_hash_num_elements(Z_ARRVAL_P(z_keys)); + } + + /* Initialize MIGRATE command with host and port */ + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "MIGRATE"); + redis_cmd_append_sstr(&cmdstr, host, hostlen); + redis_cmd_append_sstr_long(&cmdstr, port); + + /* If passed a keys array the keys come later, otherwise pass the key to + * migrate here */ + if (Z_TYPE_P(z_keys) == IS_ARRAY) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, ""); + } else { + /* Grab passed value as a string */ + zstr = zval_get_string(z_keys); + + /* We may need to prefix our string */ + key = zstr->val; + keylen = zstr->len; + keyfree = redis_key_prefix(redis_sock, &key, &keylen); + + /* Add key to migrate */ + redis_cmd_append_sstr(&cmdstr, key, keylen); + + zend_string_release(zstr); + if (keyfree) efree(key); + } + + redis_cmd_append_sstr_long(&cmdstr, destdb); + redis_cmd_append_sstr_long(&cmdstr, timeout); + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, copy, "COPY"); + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, replace, "REPLACE"); + + /* Append actual keys if we've got a keys array */ + if (Z_TYPE_P(z_keys) == IS_ARRAY) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "KEYS"); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_key) { + zstr = zval_get_string(z_key); + + key = zstr->val; + keylen = zstr->len; + keyfree = redis_key_prefix(redis_sock, &key, &keylen); + + /* Append the key */ + redis_cmd_append_sstr(&cmdstr, zstr->val, zstr->len); + + zend_string_release(zstr); + if (keyfree) efree(key); + } ZEND_HASH_FOREACH_END(); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + /* DEL */ int redis_del_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index bc5aa9d9aa..e1cd7fcfc3 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -251,6 +251,9 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands that don't communicate with Redis at all (such as getOption, * setOption, _prefix, _serialize, etc). These can be handled in one place * with the method of grabbing our RedisSock* object in different ways From 30462b86f68130bfb239de8c21d93ab8e9b95f03 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 22 Apr 2017 21:56:51 -0700 Subject: [PATCH 0757/1986] Pass prefixed key not original --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 7e2ac76d1c..fef68228d7 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3020,7 +3020,7 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, keyfree = redis_key_prefix(redis_sock, &key, &keylen); /* Append the key */ - redis_cmd_append_sstr(&cmdstr, zstr->val, zstr->len); + redis_cmd_append_sstr(&cmdstr, key, keylen); zend_string_release(zstr); if (keyfree) efree(key); From 9ec9aedea803ebc7b55fca026f3be69471d0e964 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 23 Apr 2017 13:22:16 -0700 Subject: [PATCH 0758/1986] Update documentation of MIGRATE method --- README.markdown | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/README.markdown b/README.markdown index decac6812f..70caab0fe9 100644 --- a/README.markdown +++ b/README.markdown @@ -1344,19 +1344,26 @@ $redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key ### migrate ----- _**Description**_: Migrates a key to a different Redis instance. + +**Note:**: Redis introduced migrating multiple keys in 3.0.6, so you must have at least +that version in order to call `migrate` with an array of keys. + ##### *Parameters* -*host* string. The destination host -*port* integer. The TCP port to connect to. -*key* string. The key to migrate. -*destination-db* integer. The target DB. -*timeout* integer. The maximum amount of time given to this transfer. -*copy* boolean, optional. Should we send the COPY flag to redis -*replace* boolean, optional. Should we send the REPLACE flag to redis +*host* string. The destination host +*port* integer. The TCP port to connect to. +*key(s)* string or array. +*destination-db* integer. The target DB. +*timeout* integer. The maximum amount of time given to this transfer. +*copy* boolean, optional. Should we send the COPY flag to redis. +*replace* boolean, optional. Should we send the REPLACE flag to redis ##### *Examples* ~~~ $redis->migrate('backup', 6379, 'foo', 0, 3600); $redis->migrate('backup', 6379, 'foo', 0, 3600, true, true); /* copy and replace */ $redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE flag */ + +/* Migrate multiple keys (requires Redis >= 3.0.6) +$redis->migrate('backup', 6379, ['key1', 'key2', 'key3'], 0, 3600); ~~~ From a4a0ed5f8a8fd55e0970c140eb1e7c8133144fc7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 26 Apr 2017 16:55:20 -0700 Subject: [PATCH 0759/1986] Initial commit of refactored command construction This commit adds a new printf like command construction function with additionaly format specifiers specific to phpredis. Because phpredis can be configured to automatically prefix keys and/or automatically serialize values we had a great deal of redundant boilerplate code. zend_parse_paramaters(..., "sz", &key, &keylen, &z_arg); keyfree = redis_key_prefix(redis_sock, &key, &keylen); valfree = redis_serialize(redis_sock, z_val, &realval, &reallen); /* More processing */ if (keyfree) efree(key); if (valfree) efree(val); Now it is possible to use redis_spprintf and use format specifiers specific to these tasks, which will handle prefixing or serialization (as well as memory cleanup) automatically: /* The library function will automatically prefix and serialize values if phpredis has been configured to do that */ len = phpredis_spprintf(redis_sock, slot TRMLS_CC, "SET", "kv", key, key_len, z_value); --- library.c | 266 +++++++++++++++------ library.h | 7 + redis.c | 55 +---- redis_commands.c | 600 +++++++++++------------------------------------ redis_commands.h | 2 +- 5 files changed, 354 insertions(+), 576 deletions(-) diff --git a/library.c b/library.c index a54f771afc..9b3d6bf11a 100644 --- a/library.c +++ b/library.c @@ -261,8 +261,8 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } -PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, +PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) { subscribeContext *sctx = (subscribeContext*)ctx; @@ -314,13 +314,13 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)) break; ht_tab = Z_ARRVAL(z_resp); - + if ((z_type = zend_hash_index_find(ht_tab, 0)) == NULL || Z_TYPE_P(z_type) != IS_STRING ) { break; } - + // Check for message or pmessage if(!strncmp(Z_STRVAL_P(z_type), "message", 7) || !strncmp(Z_STRVAL_P(z_type), "pmessage", 8)) @@ -422,7 +422,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, } PHP_REDIS_API zval * -redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, +redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { char inbuf[4096]; @@ -442,7 +442,7 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, numElems, UNSERIALIZE_ALL); - + return z_tab; } @@ -579,6 +579,99 @@ redis_cmd_format_header(char **ret, char *keyword, int arg_count) { return buf.len; } +/* A simple union to store the various arg types we might handle in our + * redis_spprintf command formatting function */ +union resparg { + char *str; + zend_string *zstr; + zval *zv; + int ival; + long lval; + double dval; +}; + +/* A printf like method to construct a Redis RESP command. It has been extended + * to take a few different format specifiers that are convienient to phpredis. + * + * s - C string followed by length as a strlen_t + * S - Pointer to a zend_string + * k - Same as 's' but the value will be prefixed if phpredis is set up do do + * that and the working slot will be set if it has been passed. + * v - A z_val which will be serialized if phpredis is configured to serialize. + * f - A double value + * F - Alias to 'f' + * i - An integer + * d - Alias to 'i' + * l - A long + * L - Alias to 'l' + */ +PHP_REDIS_API int +redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *kw, char *fmt, ...) { + smart_string cmd = {0}; + va_list ap; + union resparg arg; + char *dup; + int free; + strlen_t arglen; + + va_start(ap, fmt); + + /* Header */ + redis_cmd_init_sstr(&cmd, strlen(fmt), kw, strlen(kw)); + + while (*fmt) { + switch (*fmt) { + case 's': + arg.str = va_arg(ap, char*); + arglen = va_arg(ap, strlen_t); + redis_cmd_append_sstr(&cmd, arg.str, arglen); + break; + case 'S': + arg.zstr = va_arg(ap, zend_string*); + redis_cmd_append_sstr(&cmd, arg.zstr->val, arg.zstr->len); + break; + case 'k': + arg.str = va_arg(ap, char*); + arglen = va_arg(ap, strlen_t); + free = redis_key_prefix(redis_sock, &arg.str, &arglen); + redis_cmd_append_sstr(&cmd, arg.str, arglen); + if (slot) *slot = cluster_hash_key(arg.str, arglen); + if (free) efree(arg.str); + break; + case 'v': + arg.zv = va_arg(ap, zval*); + free = redis_serialize(redis_sock, arg.zv, &dup, &arglen TSRMLS_CC); + redis_cmd_append_sstr(&cmd, dup, arglen); + if (free) efree(dup); + break; + case 'f': + case 'F': + arg.dval = va_arg(ap, double); + redis_cmd_append_sstr_dbl(&cmd, arg.dval); + break; + case 'i': + case 'd': + arg.ival = va_arg(ap, int); + redis_cmd_append_sstr_int(&cmd, arg.ival); + break; + case 'l': + case 'L': + arg.lval = va_arg(ap, long); + redis_cmd_append_sstr_long(&cmd, arg.lval); + break; + } + + fmt++; + } + + /* Null terminate */ + smart_string_0(&cmd); + + /* Push command string, return length */ + *ret = cmd.c; + return cmd.len; +} + int redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { @@ -654,7 +747,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) } /** - * This command behave somehow like printf, except that strings need 2 + * This command behave somehow like printf, except that strings need 2 * arguments: * Their data and their size (strlen). * Supported formats are: %d, %i, %s, %l @@ -806,6 +899,33 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value) return retval; } +/* Append a zval to a redis command. The value will be serialized if we are + * configured to do that */ +int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC) { + char *val; + strlen_t vallen; + int free, retval; + + free = redis_serialize(redis_sock, z, &val, &vallen TSRMLS_CC); + retval = redis_cmd_append_sstr(str, val, vallen); + if (free) efree(val); + + return retval; +} + +/* Append a string key to a redis command. This function takes care of prefixing the key + * for the caller and setting the slot argument if it is passed non null */ +int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot) { + int free, retval; + + free = redis_key_prefix(redis_sock, &key, &len); + if (slot) *slot = cluster_hash_key(key, len); + retval = redis_cmd_append_sstr(str, key, len); + if (free) efree(key); + + return retval; +} + /* * Append an integer command to a Redis command */ @@ -1088,10 +1208,10 @@ redis_parse_client_list_response(char *response, zval *z_ret) } } -PHP_REDIS_API void +PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx, - SuccessCallback success_callback) + zval *z_tab, void *ctx, + SuccessCallback success_callback) { char *response; @@ -1113,24 +1233,24 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } -PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, - void *ctx) +PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + void *ctx) { - redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, NULL); } -PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval * z_tab, - void *ctx) +PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval * z_tab, + void *ctx) { char *response; int response_len; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) - == NULL) + == NULL) { IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); @@ -1222,7 +1342,7 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, } zend_string_release(hkey); } - + /* replace */ zval_dtor(z_tab); ZVAL_ZVAL(z_tab, z_ret, 1, 0); @@ -1327,8 +1447,8 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) - == NULL) + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) { IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); @@ -1356,16 +1476,16 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } /* like string response, but never unserialized. */ -PHP_REDIS_API void -redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +PHP_REDIS_API void +redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) { char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) - == NULL) + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) { IF_NOT_ATOMIC() { add_next_index_bool(z_tab, 0); @@ -1448,7 +1568,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock /** * redis_sock_create */ -PHP_REDIS_API RedisSock* +PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, @@ -1487,7 +1607,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->err_len = 0; redis_sock->scan = REDIS_SCAN_NORETRY; - + redis_sock->readonly = 0; return redis_sock; @@ -1536,15 +1656,15 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (redis_sock->persistent_id) { - spprintf(&persistent_id, 0, "phpredis:%s:%s", host, + spprintf(&persistent_id, 0, "phpredis:%s:%s", host, redis_sock->persistent_id); } else { - spprintf(&persistent_id, 0, "phpredis:%s:%f", host, + spprintf(&persistent_id, 0, "phpredis:%s:%f", host, redis_sock->timeout); } } - redis_sock->stream = php_stream_xport_create(host, host_len, + redis_sock->stream = php_stream_xport_create(host, host_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, tv_ptr, NULL, NULL, &err); @@ -1558,7 +1678,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) /* set TCP_NODELAY */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; - setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, + setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); php_stream_auto_cleanup(redis_sock->stream); @@ -1578,7 +1698,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) /** * redis_sock_server_open */ -PHP_REDIS_API int +PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC) { int res = -1; @@ -1620,7 +1740,7 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) return 0; } -PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *cmd; @@ -1632,8 +1752,8 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) - == NULL) + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + == NULL) { RETURN_FALSE; } @@ -1667,8 +1787,8 @@ redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) /** * redis_sock_read_multibulk_reply */ -PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, +PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) { char inbuf[4096]; @@ -1699,7 +1819,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_multi_result, numElems, UNSERIALIZE_ALL); - + IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_multi_result); } else { @@ -1853,7 +1973,7 @@ PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { if (!redis_sock || redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { - zend_throw_exception(redis_exception_ce, "Connection closed", + zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); } else if (redis_check_eof(redis_sock, 0 TSRMLS_CC) == 0 && php_stream_write(redis_sock->stream, cmd, sz) == sz @@ -1889,7 +2009,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len - TSRMLS_DC) + TSRMLS_DC) { #if ZEND_MODULE_API_NO >= 20100000 php_serialize_data_t ht; @@ -2041,7 +2161,7 @@ redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len) { if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { return 0; } - + ret_len = redis_sock->prefix_len + *key_len; ret = ecalloc(1 + ret_len, 1); memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); @@ -2057,22 +2177,22 @@ redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len) { */ PHP_REDIS_API int -redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, - size_t *line_size TSRMLS_DC) +redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, + size_t *line_size TSRMLS_DC) { // Handle EOF if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return -1; } - if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) - == NULL) + if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) + == NULL) { // Close, put our socket state into error REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); // Throw a read error exception - zend_throw_exception(redis_exception_ce, "read error on connection", + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); return -1; } @@ -2086,7 +2206,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, } PHP_REDIS_API int -redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, +redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info TSRMLS_DC) { // Make sure we haven't lost the connection, even trying to reconnect @@ -2097,15 +2217,15 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, // Attempt to read the reply-type byte if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { - zend_throw_exception(redis_exception_ce, "socket error on read socket", + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); return -1; } - // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can + // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can // extract the value or size info here - if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || - *reply_type == TYPE_MULTIBULK) + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || + *reply_type == TYPE_MULTIBULK) { // Buffer to hold size information char inbuf[255]; @@ -2127,8 +2247,8 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, * Read a single line response, having already consumed the reply-type byte */ PHP_REDIS_API int -redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, - zval *z_ret TSRMLS_DC) +redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, + zval *z_ret TSRMLS_DC) { // Buffer to read our single line reply char inbuf[4096]; @@ -2139,7 +2259,7 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, return -1; } - // If this is an error response, check if it is a SYNC error, and throw in + // If this is an error response, check if it is a SYNC error, and throw in // that case if(reply_type == TYPE_ERR) { /* Set our last error */ @@ -2159,8 +2279,8 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, } PHP_REDIS_API int -redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret - TSRMLS_DC) +redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret + TSRMLS_DC) { // Attempt to read the bulk reply char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); @@ -2176,8 +2296,8 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret } PHP_REDIS_API int -redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret - TSRMLS_DC) +redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret + TSRMLS_DC) { long reply_info; REDIS_REPLY_TYPE reply_type; @@ -2186,11 +2306,11 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret // Iterate while we have elements while(elements > 0) { // Attempt to read our reply type - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info - TSRMLS_CC) < 0) + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info + TSRMLS_CC) < 0) { - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, - "protocol error, couldn't parse MULTI-BULK response\n", + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, + "protocol error, couldn't parse MULTI-BULK response\n", reply_type); return -1; } @@ -2202,7 +2322,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret #if (PHP_MAJOR_VERSION < 7) ALLOC_INIT_ZVAL(z_subelem); #endif - redis_read_variant_line(redis_sock, reply_type, z_subelem + redis_read_variant_line(redis_sock, reply_type, z_subelem TSRMLS_CC); add_next_index_zval(z_ret, z_subelem); break; @@ -2215,23 +2335,23 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret #if (PHP_MAJOR_VERSION < 7) ALLOC_INIT_ZVAL(z_subelem); #endif - redis_read_variant_bulk(redis_sock, reply_info, z_subelem + redis_read_variant_bulk(redis_sock, reply_info, z_subelem TSRMLS_CC); add_next_index_zval(z_ret, z_subelem); break; case TYPE_MULTIBULK: - // Construct an array for our sub element, and add it, + // Construct an array for our sub element, and add it, // and recurse #if (PHP_MAJOR_VERSION < 7) ALLOC_INIT_ZVAL(z_subelem); #endif array_init(z_subelem); add_next_index_zval(z_ret, z_subelem); - redis_read_multibulk_recursive(redis_sock, reply_info, + redis_read_multibulk_recursive(redis_sock, reply_info, z_subelem TSRMLS_CC); break; default: - // Stop the compiler from whinging + // Stop the compiler from whinging break; } @@ -2243,8 +2363,8 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret } PHP_REDIS_API int -redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) { // Reply type, and reply size vars REDIS_REPLY_TYPE reply_type; @@ -2277,10 +2397,10 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Initialize an array for our multi-bulk response */ array_init(z_ret); - // If we've got more than zero elements, parse our multi bulk + // If we've got more than zero elements, parse our multi bulk // response recursively if(reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, z_ret + redis_read_multibulk_recursive(redis_sock, reply_info, z_ret TSRMLS_CC); } break; @@ -2289,7 +2409,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, efree(z_ret); #endif // Protocol error - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); return FAILURE; } diff --git a/library.h b/library.h index 872a8b80d8..f4a4cbc7ec 100644 --- a/library.h +++ b/library.h @@ -1,6 +1,9 @@ #ifndef REDIS_LIBRARY_H #define REDIS_LIBRARY_H +#define REDIS_SPPRINTF(ret, kw, fmt, ...) \ + redis_spprintf(redis_sock, slot TSRMLS_CC, ret, kw, fmt, __VA_ARGS__) + #define REDIS_CMD_APPEND_SSTR_STATIC(sstr, str) \ redis_cmd_append_sstr(sstr, str, sizeof(str)-1); @@ -21,6 +24,10 @@ int redis_cmd_append_sstr_int(smart_string *str, int append); int redis_cmd_append_sstr_long(smart_string *str, long append); int redis_cmd_append_int(char **cmd, int cmd_len, int append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); +int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC); +int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot); + +PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *kw, char *fmt, ...); PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len TSRMLS_DC); diff --git a/redis.c b/redis.c index 1090809f6f..3a7937102b 100644 --- a/redis.c +++ b/redis.c @@ -395,7 +395,6 @@ PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) /* Send a static DISCARD in case we're in MULTI mode. */ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { - int result = FAILURE; char *cmd, *resp; int resp_len, cmd_len; @@ -683,7 +682,7 @@ PHP_MINIT_FUNCTION(redis) /* Add shared class constants to Redis and RedisCluster objects */ add_class_constants(redis_ce, 0 TSRMLS_CC); add_class_constants(redis_cluster_ce, 1 TSRMLS_CC); - + #ifdef PHP_SESSION php_session_register_module(&ps_mod_redis); php_session_register_module(&ps_mod_redis_cluster); @@ -1063,16 +1062,8 @@ PHP_METHOD(Redis, getMultiple) /* Iterate through and grab our keys */ ZEND_HASH_FOREACH_VAL(hash, z_ele) { zend_string *zstr = zval_get_string(z_ele); - char *key = zstr->val; - strlen_t key_len = zstr->len; - /* Apply key prefix if necissary */ - int key_free = redis_key_prefix(redis_sock, &key, &key_len); - /* Append this key to our command */ - redis_cmd_append_sstr(&cmd, key, key_len); - /* release zend_string */ + redis_cmd_append_sstr_key(&cmd, zstr->val, zstr->len, redis_sock, NULL); zend_string_release(zstr); - /* Free our key if it was prefixed */ - if(key_free) efree(key); } ZEND_HASH_FOREACH_END(); /* Kick off our command */ @@ -1797,9 +1788,8 @@ PHP_METHOD(Redis, info) { } /* Build a standalone INFO command or one with an option */ - if(opt != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, - opt_len); + if (opt != NULL) { + cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len); } else { cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); } @@ -2845,33 +2835,21 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, { HashTable *ht_chan; zval *z_ele; - char *key; - int cmd_len, key_free; - strlen_t key_len; + int cmd_len; smart_string cmd = {0}; if(type == PUBSUB_CHANNELS) { if(arg) { - /* Get string argument and length. */ - key = Z_STRVAL_P(arg); - key_len = Z_STRLEN_P(arg); - - /* Prefix if necissary */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - // With a pattern - cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", - "CHANNELS", sizeof("CHANNELS")-1, key, key_len); - - /* Free the channel name if we prefixed it */ - if(key_free) efree(key); + cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, ret, "PUBSUB", + "sk", "CHANNELS", sizeof("CHANNELS") - 1, + Z_STRVAL_P(arg), Z_STRLEN_P(arg)); /* Return command length */ return cmd_len; } else { // No pattern - return redis_cmd_format_static(ret, "PUBSUB", "s", - "CHANNELS", sizeof("CHANNELS")-1); + return redis_cmd_format_static(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS")-1); } } else if(type == PUBSUB_NUMSUB) { ht_chan = Z_ARRVAL_P(arg); @@ -2884,18 +2862,8 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Iterate our elements */ ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) { zend_string *zstr = zval_get_string(z_ele); - char *key = zstr->val; - strlen_t key_len = zstr->len; - - /* Apply prefix if required */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* Append this channel */ - redis_cmd_append_sstr(&cmd, key, key_len); - + redis_cmd_append_sstr_key(&cmd, zstr->val, zstr->len, redis_sock, NULL); zend_string_release(zstr); - /* Free key if prefixed */ - if(key_free) efree(key); } ZEND_HASH_FOREACH_END(); /* Set return */ @@ -3044,8 +3012,7 @@ redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, // If there weren't any arguments (none passed, or an empty array), // construct a standard no args command if(args_count < 1) { - cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, - val_len, 0); + cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, val_len, 0); } /* Return our command length */ diff --git a/redis_commands.c b/redis_commands.c index fef68228d7..7564a99eec 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -108,9 +108,8 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key = NULL, *val=NULL; - int val_free, key_free; - strlen_t key_len, val_len; + char *key = NULL; + strlen_t key_len; zend_long expire; zval *z_val; @@ -120,19 +119,7 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Serialize value, prefix key - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Construct our command - *cmd_len = redis_cmd_format_static(cmd, kw, "sls", key, key_len, expire, - val, val_len); - - // Set the slot if directed - CMD_SET_SLOT(slot,key,key_len); - - if(val_free) efree(val); - if(key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "klv", key, key_len, expire, z_val); return SUCCESS; } @@ -144,7 +131,6 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *val; strlen_t key_len, val_len; - int key_free; zend_long lval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &key, &key_len, @@ -153,18 +139,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix our key if requested - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Construct command - *cmd_len = redis_cmd_format_static(cmd, kw, "sds", key, key_len, (int)lval, - val, val_len); - - // Set slot - CMD_SET_SLOT(slot,key,key_len); - - // Free our key if we prefixed - if(key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kds", key, key_len, (int)lval, val, val_len); return SUCCESS; } @@ -174,9 +149,8 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key, *val; - int key_free, val_free; - strlen_t key_len, val_len; + char *key; + strlen_t key_len; zval *z_val; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, @@ -185,18 +159,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Construct our command - *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, - val_len); - - // Set our slot if directed - CMD_SET_SLOT(slot,key,key_len); - - if(val_free) efree(val); - if(key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kv", key, key_len, z_val); return SUCCESS; } @@ -208,7 +171,6 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *val; strlen_t key_len, val_len; - int key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &val, &val_len)==FAILURE) @@ -216,17 +178,8 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - // Construct command - *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key, key_len, val, - val_len); - - // Set slot if directed - CMD_SET_SLOT(slot,key,key_len); - - if (key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "ks", key, key_len, val, val_len); return SUCCESS; } @@ -236,28 +189,16 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key, *val1, *val2; - strlen_t key_len, val1_len, val2_len; - int key_free; + char *k, *v1, *v2; + strlen_t klen, v1len, v2len; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, - &val1, &val1_len, &val2, &val2_len)==FAILURE) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &k, &klen, + &v1, &v1len, &v2, &v2len)==FAILURE) { return FAILURE; } - // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Construct command - *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, val1, - val1_len, val2, val2_len); - - // Set slot - CMD_SET_SLOT(slot,key,key_len); - - // Free key if prefixed - if(key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kss", k, klen, v1, v1len, v2, v2len); // Success! return SUCCESS; @@ -268,32 +209,31 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key1, *key2; - strlen_t key1_len, key2_len; - int key1_free, key2_free; + char *k1, *k2; + strlen_t k1len, k2len; + int k1free, k2free; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key1, &key1_len, - &key2, &key2_len)==FAILURE) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &k1, &k1len, + &k2, &k2len)==FAILURE) { return FAILURE; } // Prefix both keys - key1_free = redis_key_prefix(redis_sock, &key1, &key1_len); - key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); + k1free = redis_key_prefix(redis_sock, &k1, &k1len); + k2free = redis_key_prefix(redis_sock, &k2, &k2len); // If a slot is requested, we can test that they hash the same if(slot) { // Slots where these keys resolve - short slot1 = cluster_hash_key(key1, key1_len); - short slot2 = cluster_hash_key(key2, key2_len); + short slot1 = cluster_hash_key(k1, k1len); + short slot2 = cluster_hash_key(k2, k2len); // Check if Redis would give us a CROSSLOT error if(slot1 != slot2) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Keys don't hash to the same slot"); - if(key1_free) efree(key1); - if(key2_free) efree(key2); + php_error_docref(0 TSRMLS_CC, E_WARNING, "Keys don't hash to the same slot"); + if(k1free) efree(k1); + if(k2free) efree(k2); return FAILURE; } @@ -301,12 +241,13 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *slot = slot1; } - // Construct our command - *cmd_len = redis_cmd_format_static(cmd, kw, "ss", key1, key1_len, key2, - key2_len); + /* Send keys as normal strings because we manually prefixed to check against + * cross slot error. */ + *cmd_len = REDIS_SPPRINTF(cmd, kw, "ss", k1, k1len, k2, k2len); - if (key1_free) efree(key1); - if (key2_free) efree(key2); + /* Clean keys up if we prefixed */ + if (k1free) efree(k1); + if (k2free) efree(k2); return SUCCESS; } @@ -317,32 +258,16 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - int key_free; - strlen_t key_len; + strlen_t keylen; zend_long lval; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &key, &key_len, - &lval)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &key, &keylen, &lval) + ==FAILURE) { return FAILURE; } - // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Disallow zero length keys (for now) - if(key_len == 0) { - if(key_free) efree(key); - return FAILURE; - } - - // Construct our command - *cmd_len = redis_cmd_format_static(cmd, kw, "sl", key, key_len, lval); - - // Set slot if directed - CMD_SET_SLOT(slot, key, key_len); - - if (key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kl", key, keylen, lval); // Success! return SUCCESS; @@ -354,7 +279,6 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - int key_free; strlen_t key_len; zend_long val1, val2; @@ -364,17 +288,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Construct command - *cmd_len = redis_cmd_format_static(cmd, kw, "sll", key, key_len, val1, - val2); - - // Set slot - CMD_SET_SLOT(slot,key,key_len); - - if(key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kll", key, key_len, val1, val2); return SUCCESS; } @@ -386,7 +300,6 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key; strlen_t key_len; - int key_free; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) ==FAILURE) @@ -394,16 +307,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Construct our command - *cmd_len = redis_cmd_format_static(cmd, kw, "s", key, key_len); - - // Set slot if directed - CMD_SET_SLOT(slot,key,key_len); - - if(key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "k", key, key_len); return SUCCESS; } @@ -415,7 +319,6 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key; strlen_t key_len; - int key_free; double val; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &key, &key_len, @@ -424,16 +327,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Construct our command - *cmd_len = redis_cmd_format_static(cmd, kw, "sf", key, key_len, val); - - // Set slot if directed - CMD_SET_SLOT(slot,key,key_len); - - if(key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kf", key, key_len, val); return SUCCESS; } @@ -482,7 +376,6 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *slot, void **ctx) { char *key; - int key_free; strlen_t key_len; zend_long start, end; zend_bool ws=0; @@ -493,19 +386,14 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - key_free = redis_key_prefix(redis_sock, &key, &key_len); if(ws) { - *cmd_len = redis_cmd_format_static(cmd, kw, "sdds", key, key_len, start, - end, "WITHSCORES", sizeof("WITHSCORES")-1); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kdds", key, key_len, start, end, + "WITHSCORES", sizeof("WITHSCORES") - 1); } else { - *cmd_len = redis_cmd_format_static(cmd, kw, "sdd", key, key_len, start, - end); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kdd", key, key_len, start, end); } - CMD_SET_SLOT(slot, key, key_len); - - // Free key, push out WITHSCORES option - if(key_free) efree(key); + // Push out WITHSCORES option *withscores = ws; return SUCCESS; @@ -522,7 +410,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *slot, void **ctx) { char *key, *start, *end; - int key_free, has_limit=0; + int has_limit=0; long offset, count; strlen_t key_len, start_len, end_len; zval *z_opt=NULL, *z_ele; @@ -566,33 +454,26 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); } - // Prefix our key, set slot - key_free = redis_key_prefix(redis_sock, &key, &key_len); - CMD_SET_SLOT(slot,key,key_len); - // Construct our command - if(*withscores) { - if(has_limit) { - *cmd_len = redis_cmd_format_static(cmd, kw, "ssssdds", key, key_len, - start, start_len, end, end_len, "LIMIT", 5, offset, - count, "WITHSCORES", 10); + if (*withscores) { + if (has_limit) { + *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksssdds", key, key_len, + start, start_len, end, end_len, "LIMIT", 5, offset, count, + "WITHSCORES", 10); } else { - *cmd_len = redis_cmd_format_static(cmd, kw, "ssss", key, key_len, - start, start_len, end, end_len, "WITHSCORES", 10); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksss", key, key_len, start, + start_len, end, end_len, "WITHSCORES", 10); } } else { - if(has_limit) { - *cmd_len = redis_cmd_format_static(cmd, kw, "ssssdd", key, key_len, - start, start_len, end, end_len, "LIMIT", 5, offset, count); + if (has_limit) { + *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksssdd", key, key_len, start, + start_len, end, end_len, "LIMIT", 5, offset, count); } else { - *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, - start, start_len, end, end_len); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kss", key, key_len, start, + start_len, end, end_len); } } - // Free our key if we prefixed - if(key_free) efree(key); - return SUCCESS; } @@ -850,25 +731,23 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *min, *max; strlen_t key_len, min_len, max_len; - int key_free, argc = ZEND_NUM_ARGS(); + int argc = ZEND_NUM_ARGS(); zend_long offset, count; /* We need either 3 or 5 arguments for this to be valid */ - if(argc != 3 && argc != 5) { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Must pass either 3 or 5 arguments"); + if (argc != 3 && argc != 5) { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Must pass either 3 or 5 arguments"); return FAILURE; } - if(zend_parse_parameters(argc TSRMLS_CC, "sss|ll", &key, - &key_len, &min, &min_len, &max, &max_len, - &offset, &count)==FAILURE) + if(zend_parse_parameters(argc TSRMLS_CC, "sss|ll", &key, &key_len, &min, &min_len, + &max, &max_len, &offset, &count)==FAILURE) { return FAILURE; } /* min and max must start with '(' or '[', or be either '-' or '+' */ - if(min_len < 1 || max_len < 1 || + if (min_len < 1 || max_len < 1 || (min[0] != '(' && min[0] != '[' && (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || (max[0] != '(' && max[0] != '[' && @@ -879,24 +758,15 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - /* Prefix key */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - /* Construct command */ - if(argc == 3) { - *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, - min_len, max, max_len); + if (argc == 3) { + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, + max, max_len); } else { - *cmd_len = redis_cmd_format_static(cmd, kw, "ssssll", key, key_len, min, - min_len, max, max_len, "LIMIT", sizeof("LIMIT")-1, offset, count); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksssll", key, key_len, min, min_len, + max, max_len, "LIMIT", 5, offset, count); } - /* Pick our slot */ - CMD_SET_SLOT(slot,key,key_len); - - /* Free key if we prefixed */ - if(key_free) efree(key); - return SUCCESS; } @@ -907,7 +777,6 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *min, *max; strlen_t key_len, min_len, max_len; - int key_free; /* Parse args */ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, @@ -925,18 +794,9 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - /* Prefix key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - /* Construct command */ - *cmd_len = redis_cmd_format_static(cmd, kw, "sss", key, key_len, min, - min_len, max, max_len); - - /* set slot */ - CMD_SET_SLOT(slot,key,key_len); - - /* Free key if prefixed */ - if(key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, + max, max_len); return SUCCESS; } @@ -966,28 +826,17 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Grab the first argument (our key) as a string - zend_string *zstr = zval_get_string(&z_args[0]); - arg = zstr->val; - arg_len = zstr->len; - - // Prefix if required - arg_free = redis_key_prefix(redis_sock, &arg, &arg_len); - - // Start command construction + /* Initialize our command */ redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - redis_cmd_append_sstr(&cmdstr, arg, arg_len); - // Set our slot, free key prefix if we prefixed it - CMD_SET_SLOT(slot,arg,arg_len); + /* Append key */ + zend_string *zstr = zval_get_string(&z_args[0]); + redis_cmd_append_sstr_key(&cmdstr, zstr->val, zstr->len, redis_sock, slot); zend_string_release(zstr); - if(arg_free) efree(arg); - // Add our members - for(i=1;i NX|XX PX|EX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "sssls", key, key_len, - val, val_len, exp_type, 2, expire, - set_type, 2); + *cmd_len = REDIS_SPPRINTF(cmd, "SET", "kvsls", key, key_len, z_value, + exp_type, 2, expire, set_type, 2); } else if(exp_type) { /* SET PX|EX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "sssl", key, key_len, - val, val_len, exp_type, 2, expire); + *cmd_len = REDIS_SPPRINTF(cmd, "SET", "kvsl", key, key_len, z_value, + exp_type, 2, expire); } else if(set_type) { /* SET NX|XX */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "sss", key, key_len, val, - val_len, set_type, 2); + *cmd_len = REDIS_SPPRINTF(cmd, "SET", "kvs", key, key_len, z_value, + set_type, 2); } else if(expire > 0) { /* Backward compatible SETEX redirection */ - *cmd_len = redis_cmd_format_static(cmd, "SETEX", "sls", key, key_len, - expire, val, val_len); + *cmd_len = REDIS_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, + z_value); } else { /* SET */ - *cmd_len = redis_cmd_format_static(cmd, "SET", "ss", key, key_len, val, - val_len); + *cmd_len = REDIS_SPPRINTF(cmd, "SET", "kv", key, key_len, z_value); } - // If we've been passed a slot pointer, return the key's slot - CMD_SET_SLOT(slot,key,key_len); - - if(key_free) efree(key); - if(val_free) efree(val); - return SUCCESS; } @@ -1308,7 +1140,7 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key2_free = redis_key_prefix(redis_sock, &key2, &key2_len); // In cluster mode, verify the slots match - if(slot) { + if (slot) { slot1 = cluster_hash_key(key1, key1_len); slot2 = cluster_hash_key(key2, key2_len); if(slot1 != slot2) { @@ -1325,11 +1157,11 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Consistency with Redis, if timeout < 0 use RPOPLPUSH if(timeout < 0) { - *cmd_len = redis_cmd_format_static(cmd, "RPOPLPUSH", "ss", key1, - key1_len, key2, key2_len); + *cmd_len = REDIS_SPPRINTF(cmd, "RPOPLPUSH", "ss", key1, key1_len, + key2, key2_len); } else { - *cmd_len = redis_cmd_format_static(cmd, "BRPOPLPUSH", "ssd", key1, - key1_len, key2, key2_len, timeout); + *cmd_len = REDIS_SPPRINTF(cmd, "BRPOPLPUSH", "ssd", key1, key1_len, + key2, key2_len, timeout); } if (key1_free) efree(key1); @@ -1350,7 +1182,6 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, short *slot, void **ctx) { char *key; - int key_free; strlen_t key_len; zend_long val = 1; @@ -1360,31 +1191,22 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, return FAILURE; } - /* Prefix the key if required */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - /* If our value is 1 we use INCR/DECR. For other values, treat the call as * an INCRBY or DECRBY call */ if (type == TYPE_INCR) { if (val == 1) { - *cmd_len = redis_cmd_format_static(cmd,"INCR","s",key,key_len); + *cmd_len = REDIS_SPPRINTF(cmd, "INCR", "k", key, key_len); } else { - *cmd_len = redis_cmd_format_static(cmd,"INCRBY","sl",key,key_len,val); + *cmd_len = REDIS_SPPRINTF(cmd, "INCRBY", "kl", key, key_len, val); } } else { if (val == 1) { - *cmd_len = redis_cmd_format_static(cmd,"DECR","s",key,key_len); + *cmd_len = REDIS_SPPRINTF(cmd, "DECR", "k", key, key_len); } else { - *cmd_len = redis_cmd_format_static(cmd,"DECRBY","sl",key,key_len,val); + *cmd_len = REDIS_SPPRINTF(cmd, "DECRBY", "kl", key, key_len, val); } } - /* Set our slot */ - CMD_SET_SLOT(slot,key,key_len); - - /* Free our key if we prefixed */ - if (key_free) efree(key); - /* Success */ return SUCCESS; } @@ -1411,7 +1233,6 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *mem; strlen_t key_len, mem_len; - int key_free; zend_long byval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key, &key_len, @@ -1420,17 +1241,8 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix our key if necissary - key_free = redis_key_prefix(redis_sock, &key, &key_len); - // Construct command - *cmd_len = redis_cmd_format_static(cmd, "HINCRBY", "ssl", key, key_len, mem, - mem_len, byval); - // Set slot - CMD_SET_SLOT(slot,key,key_len); - - /* Free the key if we prefixed */ - if (key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, "HINCRBY", "ksl", key, key_len, mem, mem_len, byval); // Success return SUCCESS; @@ -1442,7 +1254,6 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *mem; strlen_t key_len, mem_len; - int key_free; double byval; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssd", &key, &key_len, @@ -1451,18 +1262,9 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - // Construct command - *cmd_len = redis_cmd_format_static(cmd, "HINCRBYFLOAT", "ssf", key, key_len, - mem, mem_len, byval); - - // Set slot - CMD_SET_SLOT(slot,key,key_len); - - /* Free the key if we prefixed */ - if (key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, "HINCRBYFLOAT", "ksf", key, key_len, mem, + mem_len, byval); // Success return SUCCESS; @@ -1524,6 +1326,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Start command construction redis_cmd_init_sstr(&cmdstr, valid+1, "HMGET", sizeof("HMGET")-1); + // Prefix our key key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -1634,22 +1437,14 @@ redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *field; strlen_t key_len, field_len; - int key_free; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &field, &field_len) == FAILURE ) { return FAILURE; } - // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - *cmd_len = redis_cmd_format_static(cmd, "HSTRLEN", "ss", key, key_len, field, field_len); - // Set our slot - CMD_SET_SLOT(slot, key, key_len); - - if (key_free) efree(key); + *cmd_len = REDIS_SPPRINTF(cmd, "HSTRLEN", "ks", key, key_len, field, field_len); return SUCCESS; } @@ -1659,7 +1454,7 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - int argc, key_free; + int argc; zend_long bit, start, end; strlen_t key_len; @@ -1675,26 +1470,17 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - // Construct command based on arg count if(argc == 2) { - *cmd_len = redis_cmd_format_static(cmd, "BITPOS", "sd", key, key_len, - bit); + *cmd_len = REDIS_SPPRINTF(cmd, "BITPOS", "kd", key, key_len, bit); } else if(argc == 3) { - *cmd_len = redis_cmd_format_static(cmd, "BITPOS", "sdd", key, key_len, - bit, start); + *cmd_len = REDIS_SPPRINTF(cmd, "BITPOS", "kdd", key, key_len, bit, + start); } else { - *cmd_len = redis_cmd_format_static(cmd, "BITPOS", "sddd", key, key_len, - bit, start, end); + *cmd_len = REDIS_SPPRINTF(cmd, "BITPOS", "kddd", key, key_len, bit, + start, end); } - // Set our slot - CMD_SET_SLOT(slot, key, key_len); - - if (key_free) efree(key); - return SUCCESS; } @@ -1724,8 +1510,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Initialize command construction, add our operation argument redis_cmd_init_sstr(&cmdstr, argc, "BITOP", sizeof("BITOP")-1); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), - Z_STRLEN(z_args[0])); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); // Now iterate over our keys argument for(i=1;i Date: Wed, 26 Apr 2017 22:20:11 -0700 Subject: [PATCH 0760/1986] Rewrite generic mset to use newer methods --- redis.c | 110 +++++++++++++---------------------------------- redis_commands.c | 4 +- 2 files changed, 32 insertions(+), 82 deletions(-) diff --git a/redis.c b/redis.c index 3a7937102b..c1147a984b 100644 --- a/redis.c +++ b/redis.c @@ -1634,7 +1634,6 @@ PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, } } REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); - } /* {{{ proto array Redis::sortAsc(string key, string pattern, string get, @@ -1842,17 +1841,18 @@ PHP_METHOD(Redis, move) { } /* }}} */ -PHP_REDIS_API void -generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { - zval *object; +static +void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) +{ RedisSock *redis_sock; - - char *cmd = NULL, *p = NULL; - int cmd_len = 0, argc = 0, kw_len = strlen(kw); - int step = 0; // 0: compute size; 1: copy strings. - zval *z_array; - - HashTable *keytable; + smart_string cmd = {0}; + zval *object, *z_array; + HashTable *htargs; + zend_string *zkey; + zval *zmem; + char buf[64]; + size_t keylen; + ulong idx; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_ce, &z_array) == FAILURE) @@ -1860,77 +1860,31 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { RETURN_FALSE; } - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { - RETURN_FALSE; - } - - if(zend_hash_num_elements(Z_ARRVAL_P(z_array)) == 0) { + /* Make sure we can get our socket, and we were not passed an empty array */ + if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL || + zend_hash_num_elements(Z_ARRVAL_P(z_array)) == 0) + { RETURN_FALSE; } - for(step = 0; step < 2; ++step) { - if(step == 1) { - /* '*' + arg count + NL */ - cmd_len += 1 + integer_length(1 + 2 * argc) + 2; - /* '$' + strlen(kw) + NL */ - cmd_len += 1 + integer_length(kw_len) + 2; - /* kw + NL */ - cmd_len += kw_len + 2; + /* Initialize our command */ + htargs = Z_ARRVAL_P(z_array); + redis_cmd_init_sstr(&cmd, zend_hash_num_elements(htargs) * 2, kw, strlen(kw)); - p = cmd = emalloc(cmd_len + 1); - p += sprintf(cmd, "*%d" _NL "$%d" _NL "%s" _NL, 1 + 2 * argc, - kw_len, kw); + ZEND_HASH_FOREACH_KEY_VAL(htargs, idx, zkey, zmem) { + /* Handle string or numeric keys */ + if (zkey) { + redis_cmd_append_sstr_key(&cmd, zkey->val, zkey->len, redis_sock, NULL); + } else { + keylen = snprintf(buf, sizeof(buf), "%ld", (long)idx); + redis_cmd_append_sstr_key(&cmd, buf, (strlen_t)keylen, redis_sock, NULL); } - ulong idx; - zend_string *zkey; - zval *z_value_p; - keytable = Z_ARRVAL_P(z_array); - ZEND_HASH_FOREACH_KEY_VAL(keytable, idx, zkey, z_value_p) { - char *key, *val; - strlen_t key_len; - strlen_t val_len; - int val_free, key_free; - char buf[32]; - - if (zkey) { - key = zkey->val; - key_len = zkey->len; - } else { - // Create string representation of our index - key_len = snprintf(buf, sizeof(buf), "%ld", (long)idx); - key = (char*)buf; - } - - if(step == 0) - argc++; /* found a valid arg */ - - val_free = redis_serialize(redis_sock, z_value_p, &val, &val_len - TSRMLS_CC); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - if(step == 0) { /* counting */ - cmd_len += 1 + integer_length(key_len) + 2 - + key_len + 2 - + 1 + integer_length(val_len) + 2 - + val_len + 2; - } else { - p += sprintf(p, "$%d" _NL, key_len); /* key len */ - memcpy(p, key, key_len); p += key_len; /* key */ - memcpy(p, _NL, 2); p += 2; - - p += sprintf(p, "$%d" _NL, val_len); /* val len */ - memcpy(p, val, val_len); p += val_len; /* val */ - memcpy(p, _NL, 2); p += 2; - } - - if (val_free) efree(val); - if (key_free) efree(key); - } ZEND_HASH_FOREACH_END(); - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + /* Append our value */ + redis_cmd_append_sstr_zval(&cmd, zmem, redis_sock TSRMLS_CC); + } ZEND_HASH_FOREACH_END(); + REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); IF_ATOMIC() { fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } @@ -1939,16 +1893,14 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) { /* {{{ proto bool Redis::mset(array (key => value, ...)) */ PHP_METHOD(Redis, mset) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", - redis_boolean_response); + generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response); } /* }}} */ /* {{{ proto bool Redis::msetnx(array (key => value, ...)) */ PHP_METHOD(Redis, msetnx) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", - redis_1_response); + generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 7564a99eec..1c8b91306a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -809,9 +809,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_args; smart_string cmdstr = {0}; - char *arg; - int arg_free; - strlen_t arg_len, i; + strlen_t i; int argc = ZEND_NUM_ARGS(); // We at least need a key and one value From ba0070dbf9214eb13d1fef94932c72e0676112f2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 27 Apr 2017 17:28:43 +0300 Subject: [PATCH 0761/1986] Allow using numeric string in `zinter` command --- redis_commands.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index fef68228d7..a9c66fb598 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -700,16 +700,6 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Process our weights ZEND_HASH_FOREACH_VAL(ht_weights, z_ele) { // Ignore non numeric args unless they're inf/-inf - if (Z_TYPE_P(z_ele) != IS_LONG && Z_TYPE_P(z_ele) != IS_DOUBLE && - strncasecmp(Z_STRVAL_P(z_ele), "inf", sizeof("inf")) != 0 && - strncasecmp(Z_STRVAL_P(z_ele), "-inf", sizeof("-inf")) != 0 && - strncasecmp(Z_STRVAL_P(z_ele), "+inf", sizeof("+inf")) != 0 - ) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Weights must be numeric or '-inf','inf','+inf'"); - efree(cmdstr.c); - return FAILURE; - } switch (Z_TYPE_P(z_ele)) { case IS_LONG: @@ -718,10 +708,30 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, case IS_DOUBLE: redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(z_ele)); break; - case IS_STRING: - redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), - Z_STRLEN_P(z_ele)); - break; + case IS_STRING: { + double dval; + zend_long lval; + zend_uchar type = is_numeric_string(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &lval, &dval, 0); + if (type == IS_LONG) { + redis_cmd_append_sstr_long(&cmdstr, lval); + break; + } else if (type == IS_DOUBLE) { + redis_cmd_append_sstr_dbl(&cmdstr, dval); + break; + } else if (strncasecmp(Z_STRVAL_P(z_ele), "-inf", sizeof("-inf") - 1) == 0 || + strncasecmp(Z_STRVAL_P(z_ele), "+inf", sizeof("+inf") - 1) == 0 || + strncasecmp(Z_STRVAL_P(z_ele), "inf", sizeof("inf") - 1) == 0 + ) { + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + break; + } + // fall through + } + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Weights must be numeric or '-inf','inf','+inf'"); + efree(cmdstr.c); + return FAILURE; } } ZEND_HASH_FOREACH_END(); } From 47d3722dec9661b9820c2a9fae3f379998f64a6b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 27 Apr 2017 14:37:13 +0300 Subject: [PATCH 0762/1986] Remove `ra_find_key` + use return_value instead of stack allocated z_tmp in`ra_forward_call` --- redis_array.c | 22 ++++++++++++---------- redis_array_impl.c | 20 ++------------------ redis_array_impl.h | 1 - 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/redis_array.c b/redis_array.c index 2cf2303d3b..ce3a6eb149 100644 --- a/redis_array.c +++ b/redis_array.c @@ -345,7 +345,7 @@ PHP_METHOD(RedisArray, __construct) static void ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, int cmd_len, zval *z_args, zval *z_new_target) { - zval z_tmp, z_fun, *redis_inst, *z_callargs, *zp_tmp; + zval z_fun, *redis_inst, *z_callargs, *zp_tmp; char *key = NULL; /* set to avoid "unused-but-set-variable" */ int i, key_len = 0, argc; HashTable *h_args; @@ -358,10 +358,12 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i redis_inst = ra->z_multi_exec; /* we already have the instance */ } else { /* extract key and hash it. */ - if(!(key = ra_find_key(ra, z_args, cmd, &key_len))) { + if ((zp_tmp = zend_hash_index_find(h_args, 0)) == NULL || Z_TYPE_P(zp_tmp) != IS_STRING) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not find key"); RETURN_FALSE; - } + } + key = Z_STRVAL_P(zp_tmp); + key_len = Z_STRLEN_P(zp_tmp); /* find node */ redis_inst = ra_find_node(ra, key, key_len, NULL TSRMLS_CC); @@ -371,9 +373,6 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i } } - /* check if write cmd */ - b_write_cmd = ra_is_write_cmd(ra, cmd, cmd_len); - /* pass call through */ ZVAL_STRINGL(&z_fun, cmd, cmd_len); /* method name */ z_callargs = ecalloc(argc + 1, sizeof(zval)); @@ -387,20 +386,23 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* multi/exec */ if(ra->z_multi_exec) { - call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, &z_tmp, argc, z_callargs); + call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); + zval_dtor(return_value); zval_dtor(&z_fun); - zval_dtor(&z_tmp); efree(z_callargs); RETURN_ZVAL(getThis(), 1, 0); } + /* check if write cmd */ + b_write_cmd = ra_is_write_cmd(ra, cmd, cmd_len); + /* CALL! */ if(ra->index && b_write_cmd) { /* add MULTI + SADD */ ra_index_multi(redis_inst, MULTI TSRMLS_CC); /* call using discarded temp value and extract exec results after. */ - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, &z_tmp, argc, z_callargs); - zval_dtor(&z_tmp); + call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); + zval_dtor(return_value); /* add keys to index. */ ra_index_key(key, key_len, redis_inst TSRMLS_CC); diff --git a/redis_array_impl.c b/redis_array_impl.c index 4918d81453..2795491c6b 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -401,6 +401,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR return NULL; } + ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); @@ -448,6 +449,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML return 0; } + ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv); @@ -513,24 +515,6 @@ ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC) { return NULL; } - -char * -ra_find_key(RedisArray *ra, zval *z_args, const char *cmd, int *key_len) { - - zval *zp_tmp; - int key_pos = 0; /* TODO: change this depending on the command */ - - if (zend_hash_num_elements(Z_ARRVAL_P(z_args)) == 0 || - (zp_tmp = zend_hash_index_find(Z_ARRVAL_P(z_args), key_pos)) == NULL || - Z_TYPE_P(zp_tmp) != IS_STRING - ) { - return NULL; - } - - *key_len = Z_STRLEN_P(zp_tmp); - return Z_STRVAL_P(zp_tmp); -} - void ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { diff --git a/redis_array_impl.h b/redis_array_impl.h index 082dc17206..385daadf80 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -17,7 +17,6 @@ zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TS void ra_init_function_table(RedisArray *ra); void ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC); -char * ra_find_key(RedisArray *ra, zval *z_args, const char *cmd, int *key_len); void ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC); void ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC); From bdd287e9f7467656eef65dccefc6f389007b9468 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 27 Apr 2017 15:18:01 -0700 Subject: [PATCH 0763/1986] Clean up generic sort method --- php_redis.h | 3 - redis.c | 256 +++++++++++++++++----------------------------------- 2 files changed, 83 insertions(+), 176 deletions(-) diff --git a/php_redis.h b/php_redis.h index 12b244043b..c635b927e8 100644 --- a/php_redis.h +++ b/php_redis.h @@ -247,9 +247,6 @@ typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); -PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, - int use_alpha); - PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd); PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, diff --git a/redis.c b/redis.c index c1147a984b..e24b5c1fc0 100644 --- a/redis.c +++ b/redis.c @@ -1440,207 +1440,117 @@ PHP_METHOD(Redis, sort) { } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - if(have_store) { - IF_ATOMIC() { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - } else { - IF_ATOMIC() { - if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) - { - RETURN_FALSE; - } + IF_ATOMIC() { + if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL) < 0) + { + RETURN_FALSE; } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); } + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } -PHP_REDIS_API void generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sort, - int use_alpha) +static void +generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) { - - zval *object; + zval *object, *zele, *zget = NULL; RedisSock *redis_sock; - char *key = NULL, *pattern = NULL, *get = NULL, *store = NULL, *cmd; - int cmd_len, key_free; - zend_long sort_start = -1, sort_count = -1; - strlen_t key_len, pattern_len, get_len, store_len; - - int cmd_elements; - - char *cmd_lines[30]; - int cmd_sizes[30]; - - int sort_len; - int i, pos; + zend_string *zpattern; + char *key = NULL, *pattern = NULL, *store = NULL; + strlen_t keylen, patternlen, storelen; + zend_long offset = -1, count = -1; + int argc = 1; /* SORT key is the simplest SORT command */ + smart_string cmd = {0}; + /* Parse myriad of sort arguments */ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|sslls", &object, redis_ce, &key, - &key_len, &pattern, &pattern_len, &get, - &get_len, &sort_start, &sort_count, &store, - &store_len) == FAILURE) + "Os|s!z!lls", &object, redis_ce, &key, + &keylen, &pattern, &patternlen, &zget, + &offset, &count, &store, &storelen) + == FAILURE) { RETURN_FALSE; } - if (key_len == 0 || (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + /* Ensure we're sorting something, and we can get context */ + if (keylen == 0 || !(redis_sock = redis_sock_get(object TSRMLS_CC, 0))) RETURN_FALSE; + + /* Start calculating argc depending on input arguments */ + if (pattern && patternlen) argc += 2; /* BY pattern */ + if (offset >= 0 && count >= 0) argc += 3; /* LIMIT offset count */ + if (alpha) argc += 1; /* ALPHA */ + if (store) argc += 2; /* STORE destination */ + if (desc) argc += 1; /* DESC (ASC is the default) */ + + /* GET is special. It can be 0 .. N arguments depending what we have */ + if (zget) { + if (Z_TYPE_P(zget) == IS_ARRAY) + argc += zend_hash_num_elements(Z_ARRVAL_P(zget)); + else if (Z_STRLEN_P(zget) > 0) { + argc += 2; /* GET pattern */ + } } - /* first line, sort. */ - cmd_lines[1] = estrndup("$4", 2); - cmd_sizes[1] = 2; - cmd_lines[2] = estrndup("SORT", 4); - cmd_sizes[2] = 4; + /* Start constructing final command and append key */ + redis_cmd_init_sstr(&cmd, argc, "SORT", 4); + redis_cmd_append_sstr_key(&cmd, key, keylen, redis_sock, NULL); - /* Prefix our key if we need to */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); + /* BY pattern */ + if (pattern && patternlen) { + redis_cmd_append_sstr(&cmd, "BY", sizeof("BY") - 1); + redis_cmd_append_sstr(&cmd, pattern, patternlen); + } - /* second line, key */ - cmd_sizes[3] = redis_cmd_format(&cmd_lines[3], "$%d", key_len); - cmd_lines[4] = estrndup(key, key_len); - cmd_sizes[4] = key_len; + /* LIMIT offset count */ + if (offset >= 0 && count >= 0) { + redis_cmd_append_sstr(&cmd, "LIMIT", sizeof("LIMIT") - 1); + redis_cmd_append_sstr_long(&cmd, offset); + redis_cmd_append_sstr_long(&cmd, count); + } - /* If we prefixed our key, free it */ - if(key_free) efree(key); + /* Handle any number of GET pattern arguments we've been passed */ + if (zget != NULL) { + if (Z_TYPE_P(zget) == IS_ARRAY) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zget), zele) { + zpattern = zval_get_string(zele); + redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1); + redis_cmd_append_sstr(&cmd, zpattern->val, zpattern->len); + } ZEND_HASH_FOREACH_END(); + } else { + zpattern = zval_get_string(zget); + redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1); + redis_cmd_append_sstr(&cmd, zpattern->val, zpattern->len); + zend_string_release(zpattern); + } + } + + /* Append optional DESC and ALPHA modifiers */ + if (desc) redis_cmd_append_sstr(&cmd, "DESC", sizeof("DESC") - 1); + if (alpha) redis_cmd_append_sstr(&cmd, "ALPHA", sizeof("ALPHA") - 1); - cmd_elements = 5; - if(pattern && pattern_len) { - /* BY */ - cmd_lines[cmd_elements] = estrndup("$2", 2); - cmd_sizes[cmd_elements] = 2; - cmd_elements++; - cmd_lines[cmd_elements] = estrndup("BY", 2); - cmd_sizes[cmd_elements] = 2; - cmd_elements++; - - /* pattern */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", pattern_len); - cmd_elements++; - cmd_lines[cmd_elements] = estrndup(pattern, pattern_len); - cmd_sizes[cmd_elements] = pattern_len; - cmd_elements++; - } - if(sort_start >= 0 && sort_count >= 0) { - /* LIMIT */ - cmd_lines[cmd_elements] = estrndup("$5", 2); - cmd_sizes[cmd_elements] = 2; - cmd_elements++; - cmd_lines[cmd_elements] = estrndup("LIMIT", 5); - cmd_sizes[cmd_elements] = 5; - cmd_elements++; - - /* start */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], - "$%d", integer_length(sort_start)); - cmd_elements++; - cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, - "%d", (int)sort_start); - cmd_elements++; - - /* count */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], - "$%d", integer_length(sort_count)); - cmd_elements++; - cmd_sizes[cmd_elements] = spprintf(&cmd_lines[cmd_elements], 0, - "%d", (int)sort_count); - cmd_elements++; - } - if(get && get_len) { - /* GET */ - cmd_lines[cmd_elements] = estrndup("$3", 2); - cmd_sizes[cmd_elements] = 2; - cmd_elements++; - cmd_lines[cmd_elements] = estrndup("GET", 3); - cmd_sizes[cmd_elements] = 3; - cmd_elements++; - - /* pattern */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], - "$%d", get_len); - cmd_elements++; - cmd_lines[cmd_elements] = estrndup(get, get_len); - cmd_sizes[cmd_elements] = get_len; - cmd_elements++; - } - - /* add ASC or DESC */ - sort_len = strlen(sort); - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], "$%d", - sort_len); - cmd_elements++; - cmd_lines[cmd_elements] = estrndup(sort, sort_len); - cmd_sizes[cmd_elements] = sort_len; - cmd_elements++; - - if(use_alpha) { - /* ALPHA */ - cmd_lines[cmd_elements] = estrndup("$5", 2); - cmd_sizes[cmd_elements] = 2; - cmd_elements++; - cmd_lines[cmd_elements] = estrndup("ALPHA", 5); - cmd_sizes[cmd_elements] = 5; - cmd_elements++; - } - if(store && store_len) { - /* STORE */ - cmd_lines[cmd_elements] = estrndup("$5", 2); - cmd_sizes[cmd_elements] = 2; - cmd_elements++; - cmd_lines[cmd_elements] = estrndup("STORE", 5); - cmd_sizes[cmd_elements] = 5; - cmd_elements++; - - /* store key */ - cmd_sizes[cmd_elements] = redis_cmd_format(&cmd_lines[cmd_elements], - "$%d", store_len); - cmd_elements++; - cmd_lines[cmd_elements] = estrndup(store, store_len); - cmd_sizes[cmd_elements] = store_len; - cmd_elements++; - } - - /* first line has the star */ - cmd_sizes[0] = spprintf(&cmd_lines[0], 0, "*%d", (cmd_elements-1)/2); - - /* compute the command size */ - cmd_len = 0; - for(i = 0; i < cmd_elements; ++i) { - /* Each line followed by a _NL (\r\n) */ - cmd_len += cmd_sizes[i] + sizeof(_NL) - 1; - } - - /* copy all lines into the final command. */ - cmd = emalloc(1 + cmd_len); - pos = 0; - for(i = 0; i < cmd_elements; ++i) { - memcpy(cmd + pos, cmd_lines[i], cmd_sizes[i]); - pos += cmd_sizes[i]; - memcpy(cmd + pos, _NL, sizeof(_NL) - 1); - pos += sizeof(_NL) - 1; - - /* free every line */ - efree(cmd_lines[i]); + /* Finally append STORE if we've got it */ + if (store && storelen) { + redis_cmd_append_sstr(&cmd, "STORE", sizeof("STORE") - 1); + redis_cmd_append_sstr_key(&cmd, store, storelen, redis_sock, NULL); } - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); IF_ATOMIC() { - if (redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { + if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, NULL, NULL) < 0) + { RETURN_FALSE; } } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } /* {{{ proto array Redis::sortAsc(string key, string pattern, string get, * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortAsc) { - generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ASC", 0); + generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); } /* }}} */ @@ -1648,7 +1558,7 @@ PHP_METHOD(Redis, sortAsc) * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortAscAlpha) { - generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ASC", 1); + generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1); } /* }}} */ @@ -1656,7 +1566,7 @@ PHP_METHOD(Redis, sortAscAlpha) * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortDesc) { - generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DESC", 0); + generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0); } /* }}} */ @@ -1664,7 +1574,7 @@ PHP_METHOD(Redis, sortDesc) * int start, int end, bool getList]) */ PHP_METHOD(Redis, sortDescAlpha) { - generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DESC", 1); + generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1); } /* }}} */ From a5a0b08f4e9ed9c4bc05c06a08908a49391079e0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 27 Apr 2017 22:30:47 -0700 Subject: [PATCH 0764/1986] Refactore EVAL command This commit moves EVAL and EVALSHA command construction to our redis_commands.c file because as with many other commands we can share the logic between Redis and RedisCluster. In addition it removes the last call to the legacy formatting function redis_cmd_format() which can now be removed. --- redis.c | 141 ++--------------------------------------------- redis_cluster.c | 99 +-------------------------------- redis_commands.c | 66 ++++++++++++++++++++++ redis_commands.h | 13 ++--- 4 files changed, 77 insertions(+), 242 deletions(-) diff --git a/redis.c b/redis.c index e24b5c1fc0..e3a1892820 100644 --- a/redis.c +++ b/redis.c @@ -2815,146 +2815,15 @@ PHP_METHOD(Redis, pubsub) { } } -// Construct an EVAL or EVALSHA command, with option argument array and number -// of arguments that are keys parameter -PHP_REDIS_API int -redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, - char *value, int val_len, zval *args, int keys_count - TSRMLS_DC) -{ - zval *elem; - HashTable *args_hash; - int cmd_len, args_count = 0; - int eval_cmd_count = 2; - - // If we've been provided arguments, we'll want to include those in our eval - // command - if(args != NULL) { - // Init our hash array value, and grab the count - args_hash = Z_ARRVAL_P(args); - args_count = zend_hash_num_elements(args_hash); - - // We only need to process the arguments if the array is non empty - if(args_count > 0) { - // Header for our EVAL command - cmd_len = redis_cmd_format_header(ret, keyword, - eval_cmd_count + args_count); - - // Now append the script itself, and the number of arguments to - // treat as keys - cmd_len = redis_cmd_append_str(ret, cmd_len, value, val_len); - cmd_len = redis_cmd_append_int(ret, cmd_len, keys_count); - - // Iterate the values in our "keys" array - ZEND_HASH_FOREACH_VAL(args_hash, elem) { - zend_string *zstr = zval_get_string(elem); - char *key = zstr->val; - strlen_t key_len = zstr->len; - - /* Keep track of the old command pointer */ - char *old_cmd = *ret; - - // If this is still a key argument, prefix it if we've been set - // up to prefix keys - int key_free = keys_count-- > 0 ? redis_key_prefix(redis_sock, - &key, &key_len) : 0; - - // Append this key to our EVAL command, free our old command - cmd_len = redis_cmd_format(ret, "%s$%d" _NL "%s" _NL, *ret, - cmd_len, key_len, key, key_len); - efree(old_cmd); - - zend_string_release(zstr); - /* Free our key, old command if we need to */ - if(key_free) efree(key); - } ZEND_HASH_FOREACH_END(); - } - } - - // If there weren't any arguments (none passed, or an empty array), - // construct a standard no args command - if(args_count < 1) { - cmd_len = redis_cmd_format_static(ret, keyword, "sd", value, val_len, 0); - } - - /* Return our command length */ - return cmd_len; -} - -/* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */ -PHP_METHOD(Redis, evalsha) -{ - zval *object, *args= NULL; - char *cmd, *sha; - int cmd_len; - strlen_t sha_len; - zend_long keys_count = 0; - RedisSock *redis_sock; - - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|al", &object, redis_ce, &sha, &sha_len, - &args, &keys_count) == FAILURE) - { - RETURN_FALSE; - } - - /* Attempt to grab socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { - RETURN_FALSE; - } - - // Construct our EVALSHA command - cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVALSHA", sha, sha_len, - args, keys_count TSRMLS_CC); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) - { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_read_variant_reply); -} - /* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */ PHP_METHOD(Redis, eval) { - zval *object, *args = NULL; - RedisSock *redis_sock; - char *script, *cmd = ""; - int cmd_len; - strlen_t script_len; - zend_long keys_count = 0; - - // Attempt to parse parameters - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|al", &object, redis_ce, &script, - &script_len, &args, &keys_count) - == FAILURE) - { - RETURN_FALSE; - } - - /* Attempt to grab socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { - RETURN_FALSE; - } - - // Construct our EVAL command - cmd_len = redis_build_eval_cmd(redis_sock, &cmd, "EVAL", script, script_len, - args, keys_count TSRMLS_CC); + REDIS_PROCESS_KW_CMD("EVAL", redis_eval_cmd, redis_read_variant_reply); +} - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) - { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_read_variant_reply); +/* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */ +PHP_METHOD(Redis, evalsha) { + REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_variant_reply); } PHP_REDIS_API int diff --git a/redis_cluster.c b/redis_cluster.c index 969430bc9c..16a8960854 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1904,110 +1904,15 @@ PHP_METHOD(RedisCluster, punsubscribe) { } /* }}} */ -/* Parse arguments for EVAL or EVALSHA in the context of cluster. If we aren't - * provided any "keys" as arguments, the only choice is to send the command to - * a random node in the cluster. If we are passed key arguments the best we - * can do is make sure they all map to the same "node", as we don't know what - * the user is actually doing in the LUA source itself. */ -/* EVAL/EVALSHA */ -static void cluster_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - char *kw, int kw_len) -{ - redisClusterNode *node=NULL; - char *lua, *key; - int key_free, args_count=0; - strlen_t key_len; - zval *z_arr=NULL, *z_ele; - HashTable *ht_arr; - zend_long num_keys = 0; - short slot = 0; - smart_string cmdstr = {0}; - strlen_t lua_len; - zend_string *zstr; - - /* Parse args */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, - &z_arr, &num_keys)==FAILURE) - { - RETURN_FALSE; - } - - /* Grab arg count */ - if(z_arr != NULL) { - ht_arr = Z_ARRVAL_P(z_arr); - args_count = zend_hash_num_elements(ht_arr); - } - - /* Format header, add script or SHA, and the number of args which are keys */ - redis_cmd_init_sstr(&cmdstr, 2 + args_count, kw, kw_len); - redis_cmd_append_sstr(&cmdstr, lua, lua_len); - redis_cmd_append_sstr_long(&cmdstr, num_keys); - - // Iterate over our args if we have any - if(args_count > 0) { - ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - zstr = zval_get_string(z_ele); - key = zstr->val; - key_len = zstr->len; - - /* If we're still on a key, prefix it check node */ - if(num_keys-- > 0) { - key_free = redis_key_prefix(c->flags, &key, &key_len); - slot = cluster_hash_key(key, key_len); - - /* validate that this key maps to the same node */ - if(node && c->master[slot] != node) { - zend_string_release(zstr); - if (key_free) efree(key); - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Keys appear to map to different nodes"); - RETURN_FALSE; - } - - node = c->master[slot]; - } else { - key_free = 0; - } - - /* Append this key/argument */ - redis_cmd_append_sstr(&cmdstr, key, key_len); - - zend_string_release(zstr); - /* Free key if we prefixed */ - if(key_free) efree(key); - } ZEND_HASH_FOREACH_END(); - } else { - /* Pick a slot at random, we're being told there are no keys */ - slot = rand() % REDIS_CLUSTER_MOD; - } - - if(cluster_send_command(c, slot, cmdstr.c, cmdstr.len TSRMLS_CC)<0) { - efree(cmdstr.c); - RETURN_FALSE; - } - - if(CLUSTER_IS_ATOMIC(c)) { - cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); - RETVAL_ZVAL(getThis(), 1, 0); - } - - efree(cmdstr.c); -} - /* {{{ proto mixed RedisCluster::eval(string script, [array args, int numkeys) */ PHP_METHOD(RedisCluster, eval) { - cluster_eval_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), - "EVAL", 4); + CLUSTER_PROCESS_KW_CMD("EVAL", redis_eval_cmd, cluster_variant_resp, 0); } /* }}} */ /* {{{ proto mixed RedisCluster::evalsha(string sha, [array args, int numkeys]) */ PHP_METHOD(RedisCluster, evalsha) { - cluster_eval_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), - "EVALSHA", 7); + CLUSTER_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, cluster_variant_resp, 0); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 1c8b91306a..329dea113e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -801,6 +801,72 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* EVAL and EVALSHA */ +int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *lua; + int argc = 0; + zval *z_arr = NULL, *z_ele; + HashTable *ht_arr; + zend_long num_keys = 0; + smart_string cmdstr = {0}; + strlen_t lua_len; + zend_string *zstr; + short prevslot = -1; + + /* Parse args */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, + &z_arr, &num_keys)==FAILURE) + { + return FAILURE; + } + + /* Grab arg count */ + if (z_arr != NULL) { + ht_arr = Z_ARRVAL_P(z_arr); + argc = zend_hash_num_elements(ht_arr); + } + + /* EVAL[SHA] {script || sha1} {num keys} */ + redis_cmd_init_sstr(&cmdstr, 2 + argc, kw, strlen(kw)); + redis_cmd_append_sstr(&cmdstr, lua, lua_len); + redis_cmd_append_sstr_long(&cmdstr, num_keys); + + // Iterate over our args if we have any + if (argc > 0) { + ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { + zstr = zval_get_string(z_ele); + + /* If we're still on a key, prefix it check slot */ + if (num_keys-- > 0) { + redis_cmd_append_sstr_key(&cmdstr, zstr->val, zstr->len, redis_sock, slot); + + /* If we have been passed a slot, all keys must match */ + if (slot) { + if (prevslot != -1 && prevslot != *slot) { + zend_string_release(zstr); + php_error_docref(0 TSRMLS_CC, E_WARNING, "All keys do not map to the same slot"); + return FAILURE; + } + prevslot = *slot; + } + } else { + redis_cmd_append_sstr(&cmdstr, zstr->val, zstr->len); + } + + zend_string_release(zstr); + } ZEND_HASH_FOREACH_END(); + } else { + /* Any slot will do */ + CMD_RAND_SLOT(slot); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + /* Commands that take a key followed by a variable list of serializable * values (RPUSH, LPUSH, SADD, SREM, etc...) */ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, diff --git a/redis_commands.h b/redis_commands.h index 0f61d5ad3d..018348d42b 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -28,14 +28,6 @@ typedef enum geoSortType { SORT_DESC } geoSortType; -/* GEORADIUS and GEORADIUSBYMEMBER options */ -/*typedef struct geoRadiusOpts = { - int withcoord; - int withdist; - int withhash; - geoSortType sort; -};*/ - /* Construct a raw command */ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC); @@ -116,6 +108,9 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them @@ -240,7 +235,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, - long it, char *pat, int pat_len, long count); + long it, char *pat, int pat_len, long count); int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From f880655faa37f026a879d5853b7e1a07b310af57 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 27 Apr 2017 22:35:46 -0700 Subject: [PATCH 0765/1986] Removes legacy function redis_cmd_format() --- library.c | 59 ------------------------------------------------------- library.h | 1 - 2 files changed, 60 deletions(-) diff --git a/library.c b/library.c index 9b3d6bf11a..750411c32e 100644 --- a/library.c +++ b/library.c @@ -746,65 +746,6 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) return buf.len; } -/** - * This command behave somehow like printf, except that strings need 2 - * arguments: - * Their data and their size (strlen). - * Supported formats are: %d, %i, %s, %l - */ -int -redis_cmd_format(char **ret, char *format, ...) { - - smart_string buf = {0}; - va_list ap; - char *p = format; - - va_start(ap, format); - - while (*p) { - if (*p == '%') { - switch (*(++p)) { - case 's': { - char *tmp = va_arg(ap, char*); - int tmp_len = va_arg(ap, int); - smart_string_appendl(&buf, tmp, tmp_len); - } - break; - - case 'F': - case 'f': { - double d = va_arg(ap, double); - char tmp[64]; - int len = snprintf(tmp, sizeof(tmp), "%.16g", d); - smart_string_append_long(&buf, len); - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, tmp, len); - } - break; - - case 'd': - case 'i': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_string_appendl(&buf, tmp, tmp_len); - } - break; - } - } else { - smart_string_appendc(&buf, *p); - } - - p++; - } - - smart_string_0(&buf); - - *ret = buf.c; - - return buf.len; -} - /* * Append a command sequence to a Redis command */ diff --git a/library.h b/library.h index f4a4cbc7ec..f5005ae36f 100644 --- a/library.h +++ b/library.h @@ -14,7 +14,6 @@ redis_cmd_init_sstr(sstr, argc, keyword, sizeof(keyword)-1); int integer_length(int i); -int redis_cmd_format(char **ret, char *format, ...); int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); int redis_cmd_format_header(char **ret, char *keyword, int arg_count); int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len); From 97b0be9812df1764ca5dd2eddcacb4be460c58fe Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 28 Apr 2017 08:17:52 -0700 Subject: [PATCH 0766/1986] Modern command construction for SCRIPT --- redis.c | 52 ++++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/redis.c b/redis.c index e3a1892820..adb2c35dae 100644 --- a/redis.c +++ b/redis.c @@ -2828,25 +2828,23 @@ PHP_METHOD(Redis, evalsha) { PHP_REDIS_API int redis_build_script_exists_cmd(char **ret, zval *argv, int argc) { - /* Our command length and iterator */ - int cmd_len = 0, i; + smart_string cmd = {0}; + zend_string *zstr; + int i; // Start building our command - cmd_len = redis_cmd_format_header(ret, "SCRIPT", argc + 1); - cmd_len = redis_cmd_append_str(ret, cmd_len, "EXISTS", 6); - - /* Iterate our arguments */ - for(i=0;ival, zstr->len); + REDIS_CMD_INIT_SSTR_STATIC(&cmd, 1 + argc, "SCRIPT"); + redis_cmd_append_sstr(&cmd, "EXISTS", 6); + for (i = 0; i < argc; i++) { + zstr = zval_get_string(&argv[i]); + redis_cmd_append_sstr(&cmd, zstr->val, zstr->len); zend_string_release(zstr); } /* Success */ - return cmd_len; + *ret = cmd.c; + return cmd.len; } /* {{{ proto status Redis::script('flush') @@ -3275,12 +3273,13 @@ PHP_REDIS_API int redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, int iter, char *pattern, int pattern_len, int count) { + smart_string cmdstr = {0}; char *keyword; - int arg_count, cmd_len; + int argc; /* Count our arguments +1 for key if it's got one, and + 2 for pattern */ /* or count given that they each carry keywords with them. */ - arg_count = 1 + (key_len>0) + (pattern_len>0?2:0) + (count>0?2:0); + argc = 1 + (key_len > 0) + (pattern_len > 0 ? 2 : 0) + (count > 0 ? 2 : 0); /* Turn our type into a keyword */ switch(type) { @@ -3300,32 +3299,25 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, } /* Start the command */ - cmd_len = redis_cmd_format_header(cmd, keyword, arg_count); - - /* Add the key in question if we have one */ - if(key_len) { - cmd_len = redis_cmd_append_str(cmd, cmd_len, key, key_len); - } - - /* Add our iterator */ - cmd_len = redis_cmd_append_int(cmd, cmd_len, iter); + redis_cmd_init_sstr(&cmdstr, argc, keyword, strlen(keyword)); + if (key_len) redis_cmd_append_sstr(&cmdstr, key, key_len); + redis_cmd_append_sstr_int(&cmdstr, iter); /* Append COUNT if we've got it */ if(count) { - cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", - sizeof("COUNT")-1); - cmd_len = redis_cmd_append_int(cmd, cmd_len, count); + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); + redis_cmd_append_sstr_int(&cmdstr, count); } /* Append MATCH if we've got it */ if(pattern_len) { - cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", - sizeof("MATCH")-1); - cmd_len = redis_cmd_append_str(cmd, cmd_len, pattern, pattern_len); + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MATCH"); + redis_cmd_append_sstr(&cmdstr, pattern, pattern_len); } /* Return our command length */ - return cmd_len; + *cmd = cmdstr.c; + return cmdstr.len; } /* {{{ proto redis::scan(&$iterator, [pattern, [count]]) */ From 0970ddef039c8c4e9cd753d4d1814204f36467e9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 28 Apr 2017 08:21:00 -0700 Subject: [PATCH 0767/1986] Remove redis_cmd_format_header command completely --- library.c | 24 ------------------------ library.h | 1 - 2 files changed, 25 deletions(-) diff --git a/library.c b/library.c index 750411c32e..f16d7757e4 100644 --- a/library.c +++ b/library.c @@ -555,30 +555,6 @@ integer_length(int i) { return sz; } -int -redis_cmd_format_header(char **ret, char *keyword, int arg_count) { - /* Our return buffer */ - smart_string buf = {0}; - - /* Keyword length */ - int l = strlen(keyword); - - smart_string_appendc(&buf, '*'); - smart_string_append_long(&buf, arg_count + 1); - smart_string_appendl(&buf, _NL, sizeof(_NL) -1); - smart_string_appendc(&buf, '$'); - smart_string_append_long(&buf, l); - smart_string_appendl(&buf, _NL, sizeof(_NL) -1); - smart_string_appendl(&buf, keyword, l); - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - - /* Set our return pointer */ - *ret = buf.c; - - /* Return the length */ - return buf.len; -} - /* A simple union to store the various arg types we might handle in our * redis_spprintf command formatting function */ union resparg { diff --git a/library.h b/library.h index f5005ae36f..b002bd2a37 100644 --- a/library.h +++ b/library.h @@ -15,7 +15,6 @@ int integer_length(int i); int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); -int redis_cmd_format_header(char **ret, char *keyword, int arg_count); int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len); int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); From 5d35acd246fe19c11b5f601a9147b390632beb82 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 28 Apr 2017 08:23:13 -0700 Subject: [PATCH 0768/1986] Remove redis_cmd_append_str and redis_cmd_append_int --- library.c | 41 ----------------------------------------- library.h | 2 -- 2 files changed, 43 deletions(-) diff --git a/library.c b/library.c index f16d7757e4..157a043c8b 100644 --- a/library.c +++ b/library.c @@ -722,33 +722,6 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) return buf.len; } -/* - * Append a command sequence to a Redis command - */ -int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) { - /* Smart string buffer */ - smart_string buf = {0}; - - /* Append the current command to our smart_string */ - smart_string_appendl(&buf, *cmd, cmd_len); - - /* Append our new command sequence */ - smart_string_appendc(&buf, '$'); - smart_string_append_long(&buf, append_len); - smart_string_appendl(&buf, _NL, sizeof(_NL) -1); - smart_string_appendl(&buf, append, append_len); - smart_string_appendl(&buf, _NL, sizeof(_NL) -1); - - /* Free our old command */ - efree(*cmd); - - /* Set our return pointer */ - *cmd = buf.c; - - /* Return new command length */ - return buf.len; -} - /* * Given a smart string, number of arguments, a keyword, and the length of the keyword * initialize our smart string with the proper Redis header for the command to follow @@ -843,20 +816,6 @@ int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisS return retval; } -/* - * Append an integer command to a Redis command - */ -int redis_cmd_append_int(char **cmd, int cmd_len, int append) { - char int_buf[32]; - int int_len; - - // Conver to an int, capture length - int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); - - /* Return the new length */ - return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); -} - PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; diff --git a/library.h b/library.h index b002bd2a37..589e7441a2 100644 --- a/library.h +++ b/library.h @@ -15,12 +15,10 @@ int integer_length(int i); int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); -int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len); int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); int redis_cmd_append_sstr_int(smart_string *str, int append); int redis_cmd_append_sstr_long(smart_string *str, long append); -int redis_cmd_append_int(char **cmd, int cmd_len, int append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC); int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot); From 0eaeae0f4678243a158287582ad09f68b756846b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 28 Apr 2017 12:30:58 -0700 Subject: [PATCH 0769/1986] Replace redis_cmd_format_static with redis_spprintf --- library.c | 9 +++++---- library.h | 2 +- redis_cluster.c | 25 ++++++++++++------------- redis_commands.c | 26 +++++++++++--------------- 4 files changed, 29 insertions(+), 33 deletions(-) diff --git a/library.c b/library.c index 157a043c8b..d396892f4e 100644 --- a/library.c +++ b/library.c @@ -54,7 +54,8 @@ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; int cmd_len, response_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", redis_sock->dbNumber); + cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, &cmd, "SELECT", "d", + redis_sock->dbNumber); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -81,8 +82,8 @@ static int resend_auth(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; int cmd_len, response_len; - cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", redis_sock->auth, - strlen(redis_sock->auth)); + cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, &cmd, "AUTH", "s", + redis_sock->auth, strlen(redis_sock->auth)); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -1623,7 +1624,7 @@ PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, int response_len, cmd_len; char * response; - cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); + cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, &cmd, "DISCARD", ""); SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); diff --git a/library.h b/library.h index 589e7441a2..6078b5faf4 100644 --- a/library.h +++ b/library.h @@ -2,7 +2,7 @@ #define REDIS_LIBRARY_H #define REDIS_SPPRINTF(ret, kw, fmt, ...) \ - redis_spprintf(redis_sock, slot TSRMLS_CC, ret, kw, fmt, __VA_ARGS__) + redis_spprintf(redis_sock, slot TSRMLS_CC, ret, kw, fmt, ##__VA_ARGS__) #define REDIS_CMD_APPEND_SSTR_STATIC(sstr, str) \ redis_cmd_append_sstr(sstr, str, sizeof(str)-1); diff --git a/redis_cluster.c b/redis_cluster.c index 16a8960854..d3f42ebfac 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1044,7 +1044,7 @@ PHP_METHOD(RedisCluster, keys) { char *pat, *cmd; clusterReply *resp; zval zv, *z_ret = &zv; - int i, pat_free, cmd_len; + int i, cmd_len; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pat, &pat_len) ==FAILURE) @@ -1053,9 +1053,7 @@ PHP_METHOD(RedisCluster, keys) { } /* Prefix and then build our command */ - pat_free = redis_key_prefix(c->flags, &pat, &pat_len); - cmd_len = redis_cmd_format_static(&cmd, "KEYS", "s", pat, pat_len); - if(pat_free) efree(pat); + cmd_len = redis_spprintf(c->flags, NULL TSRMLS_CC, &cmd, "KEYS", "k", pat, pat_len); array_init(z_ret); @@ -2297,7 +2295,7 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, } // Construct our command - cmd_len = redis_cmd_format_static(&cmd, kw, ""); + cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, kw, ""); // Kick off our command if(cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC)<0) { @@ -2652,14 +2650,14 @@ PHP_METHOD(RedisCluster, info) { c->readonly = 0; slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); - if(slot<0) { + if (slot < 0) { RETURN_FALSE; } - if(opt != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len); + if (opt != NULL) { + cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "INFO", "s", opt, opt_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); + cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "INFO", ""); } rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; @@ -2726,10 +2724,11 @@ PHP_METHOD(RedisCluster, client) { /* Construct the command */ if (ZEND_NUM_ARGS() == 3) { - cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len, - arg, arg_len); + cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "CLIENT", "ss", + opt, opt_len, arg, arg_len); } else if(ZEND_NUM_ARGS() == 2) { - cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len); + cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "CLIENT", "s", + opt, opt_len); } else { zend_wrong_param_count(TSRMLS_C); RETURN_FALSE; @@ -2883,7 +2882,7 @@ PHP_METHOD(RedisCluster, echo) { } /* Construct our command */ - cmd_len = redis_cmd_format_static(&cmd, "ECHO", "s", msg, msg_len); + cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "ECHO", "s", msg, msg_len); /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; diff --git a/redis_commands.c b/redis_commands.c index 329dea113e..d1a69ab673 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -34,7 +34,7 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - *cmd_len = redis_cmd_format_static(cmd, kw, ""); + *cmd_len = REDIS_SPPRINTF(cmd, kw, ""); return SUCCESS; } @@ -98,7 +98,7 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, } // Build the command without molesting the string - *cmd_len = redis_cmd_format_static(cmd, kw, "s", arg, arg_len); + *cmd_len = REDIS_SPPRINTF(cmd, kw, "s", arg, arg_len); return SUCCESS; } @@ -1842,7 +1842,7 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Construct our AUTH command - *cmd_len = redis_cmd_format_static(cmd, "AUTH", "s", pw, pw_len); + *cmd_len = REDIS_SPPRINTF(cmd, "AUTH", "s", pw, pw_len); // Free previously allocated password, and update if(redis_sock->auth) efree(redis_sock->auth); @@ -2079,18 +2079,13 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Default that we're not using store *using_store = 0; - // Handle key prefixing - key_free = redis_key_prefix(redis_sock, &key, &key_len); - // If we don't have an options array, the command is quite simple - if(!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) { + if (!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) { // Construct command - *cmd_len = redis_cmd_format_static(cmd, "SORT", "s", key, key_len); + *cmd_len = REDIS_SPPRINTF(cmd, "SORT", "k", key, key_len); - // Push out slot, store flag, and clean up + /* Not storing */ *using_store = 0; - CMD_SET_SLOT(slot,key,key_len); - if(key_free) efree(key); return SUCCESS; } @@ -2099,6 +2094,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, array_init(&z_argv); // SORT + key_free = redis_key_prefix(redis_sock, &key, &key_len); add_next_index_stringl(&z_argv, key, key_len); if (key_free) efree(key); @@ -2877,14 +2873,14 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Construct our command */ if(!kw) { - *cmd_len = redis_cmd_format_static(cmd, "COMMAND", ""); + *cmd_len = REDIS_SPPRINTF(cmd, "COMMAND", ""); } else if (!z_arg) { /* Sanity check */ if (strncasecmp(kw, "count", sizeof("count") - 1)) { return FAILURE; } /* COMMAND COUNT */ - *cmd_len = redis_cmd_format_static(cmd, "COMMAND", "s", "COUNT", sizeof("COUNT") - 1); + *cmd_len = REDIS_SPPRINTF(cmd, "COMMAND", "s", "COUNT", sizeof("COUNT") - 1); } else if (Z_TYPE_P(z_arg) == IS_STRING) { /* Sanity check */ if (strncasecmp(kw, "info", sizeof("info") - 1)) { @@ -2892,8 +2888,8 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* COMMAND INFO */ - *cmd_len = redis_cmd_format_static(cmd, "COMMAND", "ss", "INFO", - sizeof("INFO")-1, Z_STRVAL_P(z_arg), Z_STRLEN_P(z_arg)); + *cmd_len = REDIS_SPPRINTF(cmd, "COMMAND", "ss", "INFO", sizeof("INFO") - 1, + Z_STRVAL_P(z_arg), Z_STRLEN_P(z_arg)); } else { int arr_len; From b3d00dd3902eac1a76404329974e90207e2ff181 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 28 Apr 2017 15:56:07 -0700 Subject: [PATCH 0770/1986] Replace redis_cmd_format_static with redis_spprintf --- library.h | 3 +- redis.c | 124 +++++++++++++++++++---------------------------- redis_commands.c | 116 +++++++++++++++++++++++--------------------- redis_session.c | 42 +++++++++------- 4 files changed, 138 insertions(+), 147 deletions(-) diff --git a/library.h b/library.h index 6078b5faf4..ee64cedee8 100644 --- a/library.h +++ b/library.h @@ -1,8 +1,9 @@ #ifndef REDIS_LIBRARY_H #define REDIS_LIBRARY_H +/* Non cluster command helper */ #define REDIS_SPPRINTF(ret, kw, fmt, ...) \ - redis_spprintf(redis_sock, slot TSRMLS_CC, ret, kw, fmt, ##__VA_ARGS__) + redis_spprintf(redis_sock, NULL TSRMLS_CC, ret, kw, fmt, ##__VA_ARGS__) #define REDIS_CMD_APPEND_SSTR_STATIC(sstr, str) \ redis_cmd_append_sstr(sstr, str, sizeof(str)-1); diff --git a/redis.c b/redis.c index adb2c35dae..99a792394e 100644 --- a/redis.c +++ b/redis.c @@ -373,23 +373,25 @@ ZEND_GET_MODULE(redis) PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) { #if HAVE_SPL - if (!root) { - if (!spl_ce_RuntimeException) { - zend_class_entry *pce; - - if ((pce = zend_hash_str_find_ptr(CG(class_table), "runtimeexception", sizeof("RuntimeException") - 1))) { - spl_ce_RuntimeException = pce; - return pce; - } - } else { - return spl_ce_RuntimeException; - } + if (!root) { + if (!spl_ce_RuntimeException) { + zend_class_entry *pce; + + if ((pce = zend_hash_str_find_ptr(CG(class_table), "runtimeexception", + sizeof("RuntimeException") - 1))) + { + spl_ce_RuntimeException = pce; + return pce; + } + } else { + return spl_ce_RuntimeException; } + } #endif #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) - return zend_exception_get_default(); + return zend_exception_get_default(); #else - return zend_exception_get_default(TSRMLS_C); + return zend_exception_get_default(TSRMLS_C); #endif } @@ -400,7 +402,7 @@ static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { int resp_len, cmd_len; /* format our discard command */ - cmd_len = redis_cmd_format_static(&cmd, "DISCARD", ""); + cmd_len = REDIS_SPPRINTF(&cmd, "DISCARD", ""); /* send our DISCARD command */ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && @@ -1698,9 +1700,9 @@ PHP_METHOD(Redis, info) { /* Build a standalone INFO command or one with an option */ if (opt != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "INFO", "s", opt, opt_len); + cmd_len = REDIS_SPPRINTF(&cmd, "INFO", "s", opt, opt_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "INFO", ""); + cmd_len = REDIS_SPPRINTF(&cmd, "INFO", ""); } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -1733,8 +1735,7 @@ PHP_METHOD(Redis, select) { } redis_sock->dbNumber = dbNumber; - - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", dbNumber); + cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", dbNumber); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); IF_ATOMIC() { @@ -2122,11 +2123,9 @@ PHP_METHOD(Redis, multi) if (multi_value == PIPELINE) { IF_PIPELINE() { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Already in pipeline mode"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Already in pipeline mode"); } else IF_MULTI() { - php_error_docref(NULL TSRMLS_CC, E_ERROR, - "Can't activate pipeline in multi mode!"); + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate pipeline in multi mode!"); RETURN_FALSE; } else { free_reply_callbacks(redis_sock); @@ -2134,14 +2133,12 @@ PHP_METHOD(Redis, multi) } } else if (multi_value == MULTI) { IF_MULTI() { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Already in multi mode"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Already in multi mode"); } else IF_PIPELINE() { - php_error_docref(NULL TSRMLS_CC, E_ERROR, - "Can't activate multi in pipeline mode!"); + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate multi in pipeline mode!"); RETURN_FALSE; } else { - cmd_len = redis_cmd_format_static(&cmd, "MULTI", ""); + cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", ""); SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); @@ -2225,7 +2222,7 @@ PHP_METHOD(Redis, exec) } IF_MULTI() { - cmd_len = redis_cmd_format_static(&cmd, "EXEC", ""); + cmd_len = REDIS_SPPRINTF(&cmd, "EXEC", ""); SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); @@ -2459,12 +2456,10 @@ PHP_METHOD(Redis, slaveof) RETURN_FALSE; } - if(host && host_len) { - cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "sd", host, - host_len, (int)port); + if (host && host_len) { + cmd_len = REDIS_SPPRINTF(&cmd, "SLAVEOF", "sd", host, host_len, (int)port); } else { - cmd_len = redis_cmd_format_static(&cmd, "SLAVEOF", "ss", "NO", - 2, "ONE", 3); + cmd_len = REDIS_SPPRINTF(&cmd, "SLAVEOF", "ss", "NO", 2, "ONE", 3); } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2568,8 +2563,7 @@ PHP_METHOD(Redis, config) } if (mode == CFG_GET && val == NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "ss", op, op_len, - key, key_len); + cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "ss", op, op_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) IF_ATOMIC() { @@ -2578,8 +2572,7 @@ PHP_METHOD(Redis, config) REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw); } else if(mode == CFG_SET && val != NULL) { - cmd_len = redis_cmd_format_static(&cmd, "CONFIG", "sss", op, - op_len, key, key_len, val, val_len); + cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) IF_ATOMIC() { @@ -2631,12 +2624,10 @@ PHP_METHOD(Redis, slowlog) { // Create our command. For everything except SLOWLOG GET (with an arg) it's // just two parts - if(mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) { - cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "sl", arg, - arg_len, option); + if (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) { + cmd_len = REDIS_SPPRINTF(&cmd, "SLOWLOG", "sl", arg, arg_len, option); } else { - cmd_len = redis_cmd_format_static(&cmd, "SLOWLOG", "s", arg, - arg_len); + cmd_len = REDIS_SPPRINTF(&cmd, "SLOWLOG", "s", arg, arg_len); } /* Kick off our command */ @@ -2678,8 +2669,7 @@ PHP_METHOD(Redis, wait) { } // Construct the command - cmd_len = redis_cmd_format_static(&cmd, "WAIT", "ll", num_slaves, - timeout); + cmd_len = REDIS_SPPRINTF(&cmd, "WAIT", "ll", num_slaves, timeout); /* Kick it off */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -2697,28 +2687,22 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, { HashTable *ht_chan; zval *z_ele; - int cmd_len; smart_string cmd = {0}; - if(type == PUBSUB_CHANNELS) { - if(arg) { - // With a pattern - cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, ret, "PUBSUB", - "sk", "CHANNELS", sizeof("CHANNELS") - 1, - Z_STRVAL_P(arg), Z_STRLEN_P(arg)); - - /* Return command length */ - return cmd_len; + if (type == PUBSUB_CHANNELS) { + if (arg) { + /* With a pattern */ + return REDIS_SPPRINTF(ret, "PUBSUB", "sk", "CHANNELS", sizeof("CHANNELS") - 1, + Z_STRVAL_P(arg), Z_STRLEN_P(arg)); } else { - // No pattern - return redis_cmd_format_static(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS")-1); + /* No pattern */ + return REDIS_SPPRINTF(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS") - 1); } - } else if(type == PUBSUB_NUMSUB) { + } else if (type == PUBSUB_NUMSUB) { ht_chan = Z_ARRVAL_P(arg); // Add PUBSUB and NUMSUB bits - redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", - sizeof("PUBSUB")-1); + redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1); redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1); /* Iterate our elements */ @@ -2731,9 +2715,8 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Set return */ *ret = cmd.c; return cmd.len; - } else if(type == PUBSUB_NUMPAT) { - return redis_cmd_format_static(ret, "PUBSUB", "s", "NUMPAT", - sizeof("NUMPAT")-1); + } else if (type == PUBSUB_NUMPAT) { + return REDIS_SPPRINTF(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT") - 1); } /* Shouldn't ever happen */ @@ -2882,9 +2865,7 @@ PHP_METHOD(Redis, script) { !strcasecmp(Z_STRVAL(z_args[0]), "kill")) { // Simple SCRIPT FLUSH, or SCRIPT_KILL command - cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "s", - Z_STRVAL(z_args[0]), - Z_STRLEN(z_args[0])); + cmd_len = REDIS_SPPRINTF(&cmd, "SCRIPT", "s", Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } else if(!strcasecmp(Z_STRVAL(z_args[0]), "load")) { // Make sure we have a second argument, and it's not empty. If it is // empty, we can just return an empty array (which is what Redis does) @@ -2897,9 +2878,8 @@ PHP_METHOD(Redis, script) { } // Format our SCRIPT LOAD command - cmd_len = redis_cmd_format_static(&cmd, "SCRIPT", "ss", - "LOAD", 4, Z_STRVAL(z_args[1]), - Z_STRLEN(z_args[1])); + cmd_len = REDIS_SPPRINTF(&cmd, "SCRIPT", "ss", "LOAD", 4, Z_STRVAL(z_args[1]), + Z_STRLEN(z_args[1])); } else if(!strcasecmp(Z_STRVAL(z_args[0]), "exists")) { /* Construct our SCRIPT EXISTS command */ cmd_len = redis_build_script_exists_cmd(&cmd, &(z_args[1]), argc-1); @@ -3194,12 +3174,10 @@ PHP_METHOD(Redis, client) { } /* Build our CLIENT command */ - if(ZEND_NUM_ARGS() == 2) { - cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", - opt, opt_len, arg, arg_len); + if (ZEND_NUM_ARGS() == 2) { + cmd_len = REDIS_SPPRINTF(&cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len); } else { - cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", - opt, opt_len); + cmd_len = REDIS_SPPRINTF(&cmd, "CLIENT", "s", opt, opt_len); } /* Execute our queue command */ diff --git a/redis_commands.c b/redis_commands.c index d1a69ab673..dbe7b7032d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -25,6 +25,12 @@ #include "redis_commands.h" #include +/* Local passthrough macro for command construction. Given that these methods + * are generic (so they work whether the caller is Redis or RedisCluster) we + * will always have redis_sock, slot*, and TSRMLS_CC */ +#define REDIS_CMD_SPPRINTF(ret, kw, fmt, ...) \ + redis_spprintf(redis_sock, slot TSRMLS_CC, ret, kw, fmt, ##__VA_ARGS__) + /* Generic commands based on method signature and what kind of things we're * processing. Lots of Redis commands take something like key, value, or * key, value long. Each unique signature like this is written only once */ @@ -34,7 +40,7 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - *cmd_len = REDIS_SPPRINTF(cmd, kw, ""); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, ""); return SUCCESS; } @@ -98,7 +104,7 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, } // Build the command without molesting the string - *cmd_len = REDIS_SPPRINTF(cmd, kw, "s", arg, arg_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", arg, arg_len); return SUCCESS; } @@ -119,7 +125,7 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, kw, "klv", key, key_len, expire, z_val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "klv", key, key_len, expire, z_val); return SUCCESS; } @@ -139,7 +145,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kds", key, key_len, (int)lval, val, val_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kds", key, key_len, (int)lval, val, val_len); return SUCCESS; } @@ -159,7 +165,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kv", key, key_len, z_val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kv", key, key_len, z_val); return SUCCESS; } @@ -179,7 +185,7 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Construct command - *cmd_len = REDIS_SPPRINTF(cmd, kw, "ks", key, key_len, val, val_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ks", key, key_len, val, val_len); return SUCCESS; } @@ -198,7 +204,7 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kss", k, klen, v1, v1len, v2, v2len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", k, klen, v1, v1len, v2, v2len); // Success! return SUCCESS; @@ -243,7 +249,7 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Send keys as normal strings because we manually prefixed to check against * cross slot error. */ - *cmd_len = REDIS_SPPRINTF(cmd, kw, "ss", k1, k1len, k2, k2len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ss", k1, k1len, k2, k2len); /* Clean keys up if we prefixed */ if (k1free) efree(k1); @@ -267,7 +273,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kl", key, keylen, lval); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kl", key, keylen, lval); // Success! return SUCCESS; @@ -288,7 +294,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kll", key, key_len, val1, val2); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kll", key, key_len, val1, val2); return SUCCESS; } @@ -307,7 +313,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, kw, "k", key, key_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "k", key, key_len); return SUCCESS; } @@ -327,7 +333,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kf", key, key_len, val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kf", key, key_len, val); return SUCCESS; } @@ -387,10 +393,10 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } if(ws) { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kdds", key, key_len, start, end, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdds", key, key_len, start, end, "WITHSCORES", sizeof("WITHSCORES") - 1); } else { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kdd", key, key_len, start, end); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdd", key, key_len, start, end); } // Push out WITHSCORES option @@ -457,19 +463,19 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Construct our command if (*withscores) { if (has_limit) { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksssdds", key, key_len, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssdds", key, key_len, start, start_len, end, end_len, "LIMIT", 5, offset, count, "WITHSCORES", 10); } else { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksss", key, key_len, start, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksss", key, key_len, start, start_len, end, end_len, "WITHSCORES", 10); } } else { if (has_limit) { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksssdd", key, key_len, start, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssdd", key, key_len, start, start_len, end, end_len, "LIMIT", 5, offset, count); } else { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kss", key, key_len, start, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, start, start_len, end, end_len); } } @@ -760,10 +766,10 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Construct command */ if (argc == 3) { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, max, max_len); } else { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksssll", key, key_len, min, min_len, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssll", key, key_len, min, min_len, max, max_len, "LIMIT", 5, offset, count); } @@ -795,7 +801,7 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Construct command */ - *cmd_len = REDIS_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, max, max_len); return SUCCESS; @@ -1161,23 +1167,23 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Now let's construct the command we want */ if(exp_type && set_type) { /* SET NX|XX PX|EX */ - *cmd_len = REDIS_SPPRINTF(cmd, "SET", "kvsls", key, key_len, z_value, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvsls", key, key_len, z_value, exp_type, 2, expire, set_type, 2); } else if(exp_type) { /* SET PX|EX */ - *cmd_len = REDIS_SPPRINTF(cmd, "SET", "kvsl", key, key_len, z_value, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvsl", key, key_len, z_value, exp_type, 2, expire); } else if(set_type) { /* SET NX|XX */ - *cmd_len = REDIS_SPPRINTF(cmd, "SET", "kvs", key, key_len, z_value, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvs", key, key_len, z_value, set_type, 2); } else if(expire > 0) { /* Backward compatible SETEX redirection */ - *cmd_len = REDIS_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, z_value); } else { /* SET */ - *cmd_len = REDIS_SPPRINTF(cmd, "SET", "kv", key, key_len, z_value); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kv", key, key_len, z_value); } return SUCCESS; @@ -1221,10 +1227,10 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Consistency with Redis, if timeout < 0 use RPOPLPUSH if(timeout < 0) { - *cmd_len = REDIS_SPPRINTF(cmd, "RPOPLPUSH", "ss", key1, key1_len, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "RPOPLPUSH", "ss", key1, key1_len, key2, key2_len); } else { - *cmd_len = REDIS_SPPRINTF(cmd, "BRPOPLPUSH", "ssd", key1, key1_len, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BRPOPLPUSH", "ssd", key1, key1_len, key2, key2_len, timeout); } @@ -1259,15 +1265,15 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, * an INCRBY or DECRBY call */ if (type == TYPE_INCR) { if (val == 1) { - *cmd_len = REDIS_SPPRINTF(cmd, "INCR", "k", key, key_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "INCR", "k", key, key_len); } else { - *cmd_len = REDIS_SPPRINTF(cmd, "INCRBY", "kl", key, key_len, val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "INCRBY", "kl", key, key_len, val); } } else { if (val == 1) { - *cmd_len = REDIS_SPPRINTF(cmd, "DECR", "k", key, key_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "DECR", "k", key, key_len); } else { - *cmd_len = REDIS_SPPRINTF(cmd, "DECRBY", "kl", key, key_len, val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "DECRBY", "kl", key, key_len, val); } } @@ -1306,7 +1312,7 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Construct command - *cmd_len = REDIS_SPPRINTF(cmd, "HINCRBY", "ksl", key, key_len, mem, mem_len, byval); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HINCRBY", "ksl", key, key_len, mem, mem_len, byval); // Success return SUCCESS; @@ -1327,7 +1333,7 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Construct command - *cmd_len = REDIS_SPPRINTF(cmd, "HINCRBYFLOAT", "ksf", key, key_len, mem, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HINCRBYFLOAT", "ksf", key, key_len, mem, mem_len, byval); // Success @@ -1508,7 +1514,7 @@ redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, "HSTRLEN", "ks", key, key_len, field, field_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HSTRLEN", "ks", key, key_len, field, field_len); return SUCCESS; } @@ -1536,12 +1542,12 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Construct command based on arg count if(argc == 2) { - *cmd_len = REDIS_SPPRINTF(cmd, "BITPOS", "kd", key, key_len, bit); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITPOS", "kd", key, key_len, bit); } else if(argc == 3) { - *cmd_len = REDIS_SPPRINTF(cmd, "BITPOS", "kdd", key, key_len, bit, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITPOS", "kdd", key, key_len, bit, start); } else { - *cmd_len = REDIS_SPPRINTF(cmd, "BITPOS", "kddd", key, key_len, bit, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITPOS", "kddd", key, key_len, bit, start, end); } @@ -1631,7 +1637,7 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, "BITCOUNT", "kdd", key, key_len, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITCOUNT", "kdd", key, key_len, (int)start, (int)end); return SUCCESS; @@ -1842,7 +1848,7 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Construct our AUTH command - *cmd_len = REDIS_SPPRINTF(cmd, "AUTH", "s", pw, pw_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "s", pw, pw_len); // Free previously allocated password, and update if(redis_sock->auth) efree(redis_sock->auth); @@ -1874,7 +1880,7 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, "SETBIT", "kld", key, key_len, offset, (int)val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETBIT", "kld", key, key_len, offset, (int)val); return SUCCESS; } @@ -1901,7 +1907,7 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Construct command */ - *cmd_len = REDIS_SPPRINTF(cmd, "LINSERT", "ksvv", key, key_len, pos, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "LINSERT", "ksvv", key, key_len, pos, pos_len, z_pivot, z_val); // Success @@ -1924,7 +1930,7 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Construct command */ - *cmd_len = REDIS_SPPRINTF(cmd, "LREM", "kdv", key, key_len, count, z_val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "LREM", "kdv", key, key_len, count, z_val); // Success! return SUCCESS; @@ -1962,7 +1968,7 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Construct command - *cmd_len = REDIS_SPPRINTF(cmd, "SMOVE", "ssv", src, src_len, dst, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SMOVE", "ssv", src, src_len, dst, dst_len, z_val); // Cleanup @@ -1988,7 +1994,7 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Construct command */ - *cmd_len = REDIS_SPPRINTF(cmd, kw, "ksv", key, key_len, mem, mem_len, z_val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksv", key, key_len, mem, mem_len, z_val); // Success return SUCCESS; @@ -2030,9 +2036,9 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Two args means we have the optional COUNT if (*have_count) { - *cmd_len = REDIS_SPPRINTF(cmd, "SRANDMEMBER", "kl", key, key_len, count); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SRANDMEMBER", "kl", key, key_len, count); } else { - *cmd_len = REDIS_SPPRINTF(cmd, "SRANDMEMBER", "k", key, key_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SRANDMEMBER", "k", key, key_len); } return SUCCESS; @@ -2053,7 +2059,7 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - *cmd_len = REDIS_SPPRINTF(cmd, "ZINCRBY", "kfv", key, key_len, incrby, z_val); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "ZINCRBY", "kfv", key, key_len, incrby, z_val); return SUCCESS; } @@ -2082,7 +2088,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If we don't have an options array, the command is quite simple if (!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) { // Construct command - *cmd_len = REDIS_SPPRINTF(cmd, "SORT", "k", key, key_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SORT", "k", key, key_len); /* Not storing */ *using_store = 0; @@ -2464,7 +2470,7 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Format our command - *cmd_len = REDIS_SPPRINTF(cmd, "OBJECT", "sk", subcmd, subcmd_len, key, key_len); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "OBJECT", "sk", subcmd, subcmd_len, key, key_len); // Push the reply type to our caller if(subcmd_len == 8 && (!strncasecmp(subcmd,"refcount",8) || @@ -2500,10 +2506,10 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Construct command */ if (unit != NULL) { - *cmd_len = REDIS_SPPRINTF(cmd, "GEODIST", "ksss", key, keylen, source, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "GEODIST", "ksss", key, keylen, source, sourcelen, dest, destlen, unit, unitlen); } else { - *cmd_len = REDIS_SPPRINTF(cmd, "GEODIST", "kss", key, keylen, source, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "GEODIST", "kss", key, keylen, source, sourcelen, dest, destlen); } @@ -2873,14 +2879,14 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Construct our command */ if(!kw) { - *cmd_len = REDIS_SPPRINTF(cmd, "COMMAND", ""); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "COMMAND", ""); } else if (!z_arg) { /* Sanity check */ if (strncasecmp(kw, "count", sizeof("count") - 1)) { return FAILURE; } /* COMMAND COUNT */ - *cmd_len = REDIS_SPPRINTF(cmd, "COMMAND", "s", "COUNT", sizeof("COUNT") - 1); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "COMMAND", "s", "COUNT", sizeof("COUNT") - 1); } else if (Z_TYPE_P(z_arg) == IS_STRING) { /* Sanity check */ if (strncasecmp(kw, "info", sizeof("info") - 1)) { @@ -2888,7 +2894,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* COMMAND INFO */ - *cmd_len = REDIS_SPPRINTF(cmd, "COMMAND", "ss", "INFO", sizeof("INFO") - 1, + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "COMMAND", "ss", "INFO", sizeof("INFO") - 1, Z_STRVAL_P(z_arg), Z_STRLEN_P(z_arg)); } else { int arr_len; diff --git a/redis_session.c b/redis_session.c index 607fc8f0ce..aa6434db90 100644 --- a/redis_session.c +++ b/redis_session.c @@ -116,22 +116,22 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { efree(pool); } -void +static void redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { RedisSock *redis_sock = rpm->redis_sock; char *response, *cmd; int response_len, cmd_len; - if(!rpm->auth || !rpm->auth_len) { /* no password given. */ - return; + /* Short circuit if we don't have a password */ + if(!rpm->auth || !rpm->auth_len) { + return; } - cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", rpm->auth, - rpm->auth_len); + cmd_len = REDIS_SPPRINTF(&cmd, "AUTH", "s", rpm->auth, rpm->auth_len); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { - efree(response); - } + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { + efree(response); + } } efree(cmd); } @@ -142,8 +142,7 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { char *response, *cmd; int response_len, cmd_len; - cmd_len = redis_cmd_format_static(&cmd, "SELECT", "d", rpm->database); - + cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", rpm->database); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { efree(response); @@ -362,7 +361,7 @@ PS_READ_FUNC(redis) #else resp = redis_session_key(rpm, key->val, key->len, &resp_len); #endif - cmd_len = redis_cmd_format_static(&cmd, "GET", "s", resp, resp_len); + cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); efree(resp); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { @@ -419,10 +418,13 @@ PS_WRITE_FUNC(redis) /* send SET command */ #if (PHP_MAJOR_VERSION < 7) session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); + cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, + INI_INT("session.gc_maxlifetime"), val, vallen); #else session = redis_session_key(rpm, key->val, key->len, &session_len); - cmd_len = redis_cmd_format_static(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val->val, val->len); + cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, + INI_INT("session.gc_maxlifetime"), val->val, + val->len); #endif efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { @@ -470,7 +472,7 @@ PS_DESTROY_FUNC(redis) #else session = redis_session_key(rpm, key->val, key->len, &session_len); #endif - cmd_len = redis_cmd_format_static(&cmd, "DEL", "s", session, session_len); + cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "s", session, session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -653,7 +655,7 @@ PS_READ_FUNC(rediscluster) { #else skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); #endif - cmdlen = redis_cmd_format_static(&cmd, "GET", "s", skey, skeylen); + cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "GET", "s", skey, skeylen); efree(skey); /* Attempt to kick off our command */ @@ -700,10 +702,14 @@ PS_WRITE_FUNC(rediscluster) { /* Set up command and slot info */ #if (PHP_MAJOR_VERSION < 7) skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); - cmdlen = redis_cmd_format_static(&cmd, "SETEX", "sds", skey, skeylen, INI_INT("session.gc_maxlifetime"), val, vallen); + cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "SETEX", "sds", skey, + skeylen, INI_INT("session.gc_maxlifetime"), val, + vallen); #else skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); - cmdlen = redis_cmd_format_static(&cmd, "SETEX", "sds", skey, skeylen, INI_INT("session.gc_maxlifetime"), val->val, val->len); + cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "SETEX", "sds", skey, + skeylen, INI_INT("session.gc_maxlifetime"), + val->val, val->len); #endif efree(skey); @@ -745,7 +751,7 @@ PS_DESTROY_FUNC(rediscluster) { #else skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); #endif - cmdlen = redis_cmd_format_static(&cmd, "DEL", "s", skey, skeylen); + cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "DEL", "s", skey, skeylen); efree(skey); /* Attempt to send command */ From f2d1ccf7c6ec245ecb76a861772f62717f2d87f9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 28 Apr 2017 15:57:59 -0700 Subject: [PATCH 0771/1986] Actual removal of redis_cmd_format_static function --- library.c | 74 ------------------------------------------------------- library.h | 1 - 2 files changed, 75 deletions(-) diff --git a/library.c b/library.c index d396892f4e..a11672a2ec 100644 --- a/library.c +++ b/library.c @@ -649,80 +649,6 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k return cmd.len; } -int -redis_cmd_format_static(char **ret, char *keyword, char *format, ...) -{ - char *p = format; - va_list ap; - smart_string buf = {0}; - int l = strlen(keyword); - - va_start(ap, format); - - /* add header */ - smart_string_appendc(&buf, '*'); - smart_string_append_long(&buf, strlen(format) + 1); - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendc(&buf, '$'); - smart_string_append_long(&buf, l); - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, keyword, l); - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - - while (*p) { - smart_string_appendc(&buf, '$'); - - switch(*p) { - case 's': { - char *val = va_arg(ap, char*); - int val_len = va_arg(ap, int); - smart_string_append_long(&buf, val_len); - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, val, val_len); - } - break; - case 'f': - case 'F': { - double d = va_arg(ap, double); - char tmp[64]; - int len = snprintf(tmp, sizeof(tmp), "%.16g", d); - smart_string_append_long(&buf, len); - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, tmp, len); - } - break; - - case 'i': - case 'd': { - int i = va_arg(ap, int); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%d", i); - smart_string_append_long(&buf, tmp_len); - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - smart_string_appendl(&buf, tmp, tmp_len); - } - break; - case 'l': - case 'L': { - long l = va_arg(ap, long); - char tmp[32]; - int tmp_len = snprintf(tmp, sizeof(tmp), "%ld", l); - smart_string_append_long(&buf, tmp_len); - smart_string_appendl(&buf, _NL, sizeof(_NL) -1); - smart_string_appendl(&buf, tmp, tmp_len); - } - break; - } - p++; - smart_string_appendl(&buf, _NL, sizeof(_NL) - 1); - } - smart_string_0(&buf); - - *ret = buf.c; - - return buf.len; -} - /* * Given a smart string, number of arguments, a keyword, and the length of the keyword * initialize our smart string with the proper Redis header for the command to follow diff --git a/library.h b/library.h index ee64cedee8..73a775cf1b 100644 --- a/library.h +++ b/library.h @@ -15,7 +15,6 @@ redis_cmd_init_sstr(sstr, argc, keyword, sizeof(keyword)-1); int integer_length(int i); -int redis_cmd_format_static(char **ret, char *keyword, char *format, ...); int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); int redis_cmd_append_sstr_int(smart_string *str, int append); From 6a74b959b38a5706d90cfe618e7ea1eaf2f9a5dd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 28 Apr 2017 16:02:04 -0700 Subject: [PATCH 0772/1986] Remove integer_length function --- library.c | 16 ---------------- library.h | 1 - 2 files changed, 17 deletions(-) diff --git a/library.c b/library.c index a11672a2ec..c35b161302 100644 --- a/library.c +++ b/library.c @@ -540,22 +540,6 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) return NULL; } -int -integer_length(int i) { - int sz = 0; - int ci = abs(i); - while (ci > 0) { - ci /= 10; - sz++; - } - if (i == 0) { /* log 0 doesn't make sense. */ - sz = 1; - } else if (i < 0) { /* allow for neg sign as well. */ - sz++; - } - return sz; -} - /* A simple union to store the various arg types we might handle in our * redis_spprintf command formatting function */ union resparg { diff --git a/library.h b/library.h index 73a775cf1b..e0496f9789 100644 --- a/library.h +++ b/library.h @@ -14,7 +14,6 @@ #define REDIS_CMD_INIT_SSTR_STATIC(sstr, argc, keyword) \ redis_cmd_init_sstr(sstr, argc, keyword, sizeof(keyword)-1); -int integer_length(int i); int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); int redis_cmd_append_sstr_int(smart_string *str, int append); From d84914f065dbe3f72153166c23c9ceb28efe2f01 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 29 Apr 2017 14:42:10 -0700 Subject: [PATCH 0773/1986] Fix potential memory leak --- redis.c | 1 + 1 file changed, 1 insertion(+) diff --git a/redis.c b/redis.c index 99a792394e..29ce3a8b62 100644 --- a/redis.c +++ b/redis.c @@ -1518,6 +1518,7 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) zpattern = zval_get_string(zele); redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1); redis_cmd_append_sstr(&cmd, zpattern->val, zpattern->len); + zend_string_release(zpattern); } ZEND_HASH_FOREACH_END(); } else { zpattern = zval_get_string(zget); From 7807e875fbe883d1eb50677645b1f15133634061 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 3 May 2017 14:35:17 +0300 Subject: [PATCH 0774/1986] Don't use `free` as variable name. --- library.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/library.c b/library.c index c35b161302..3d970b3a7b 100644 --- a/library.c +++ b/library.c @@ -572,7 +572,7 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k va_list ap; union resparg arg; char *dup; - int free; + int argfree; strlen_t arglen; va_start(ap, fmt); @@ -594,16 +594,16 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k case 'k': arg.str = va_arg(ap, char*); arglen = va_arg(ap, strlen_t); - free = redis_key_prefix(redis_sock, &arg.str, &arglen); + argfree = redis_key_prefix(redis_sock, &arg.str, &arglen); redis_cmd_append_sstr(&cmd, arg.str, arglen); if (slot) *slot = cluster_hash_key(arg.str, arglen); - if (free) efree(arg.str); + if (argfree) efree(arg.str); break; case 'v': arg.zv = va_arg(ap, zval*); - free = redis_serialize(redis_sock, arg.zv, &dup, &arglen TSRMLS_CC); + argfree = redis_serialize(redis_sock, arg.zv, &dup, &arglen TSRMLS_CC); redis_cmd_append_sstr(&cmd, dup, arglen); - if (free) efree(dup); + if (argfree) efree(dup); break; case 'f': case 'F': @@ -705,11 +705,11 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value) int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC) { char *val; strlen_t vallen; - int free, retval; + int valfree, retval; - free = redis_serialize(redis_sock, z, &val, &vallen TSRMLS_CC); + valfree = redis_serialize(redis_sock, z, &val, &vallen TSRMLS_CC); retval = redis_cmd_append_sstr(str, val, vallen); - if (free) efree(val); + if (valfree) efree(val); return retval; } @@ -717,12 +717,12 @@ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock /* Append a string key to a redis command. This function takes care of prefixing the key * for the caller and setting the slot argument if it is passed non null */ int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot) { - int free, retval; + int valfree, retval; - free = redis_key_prefix(redis_sock, &key, &len); + valfree = redis_key_prefix(redis_sock, &key, &len); if (slot) *slot = cluster_hash_key(key, len); retval = redis_cmd_append_sstr(str, key, len); - if (free) efree(key); + if (valfree) efree(key); return retval; } From 561fca59d9319c53589e91e30132b144eb329750 Mon Sep 17 00:00:00 2001 From: Yongfu Date: Fri, 5 May 2017 09:41:16 +0800 Subject: [PATCH 0775/1986] fixed duplicate 'install' typo and update the homebrew-php link --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 70caab0fe9..97ba85079e 100644 --- a/README.markdown +++ b/README.markdown @@ -72,9 +72,9 @@ Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tag See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports). -You can install install it using Homebrew: +You can install it using Homebrew: -- [Get homebrew-php](https://github.com/josegonzalez/homebrew-php) +- [Get homebrew-php](https://github.com/Homebrew/homebrew-php) - `brew install php55-redis` (or php53-redis, php54-redis) ## PHP Session handler From ad45964150d0f8e96d2290479e49e1225d6db75c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 9 May 2017 00:05:41 +0300 Subject: [PATCH 0776/1986] Using ZVAL_DEREF macros for dereference input variables. This PR fixes issues #946 and #1166. --- common.h | 3 +++ redis.c | 1 + redis_commands.c | 7 +++++-- tests/RedisTest.php | 10 ++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index 029d912bf0..1ed857c338 100644 --- a/common.h +++ b/common.h @@ -373,6 +373,9 @@ typedef int strlen_t; #define PHP_FE_END { NULL, NULL, NULL } #endif +/* References don't need any actions */ +#define ZVAL_DEREF(v) PHPREDIS_NOTUSED(v) + #else #include #include diff --git a/redis.c b/redis.c index 29ce3a8b62..2505295ed1 100644 --- a/redis.c +++ b/redis.c @@ -2379,6 +2379,7 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, } ZEND_HASH_FOREACH_VAL(arr_hash, data) { + ZVAL_DEREF(data); if (Z_TYPE_P(data) == IS_STRING) { char *old_cmd = NULL; if(*cmd) { diff --git a/redis_commands.c b/redis_commands.c index ef21f5354b..a9d0878ca0 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -439,7 +439,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_KEY_VAL(ht_opt, idx, zkey, z_ele) { /* All options require a string key type */ if (!zkey) continue; - + ZVAL_DEREF(z_ele); /* Check for withscores and limit */ if (IS_WITHSCORES_ARG(zkey->val, zkey->len)) { *withscores = zval_is_true(z_ele); @@ -587,7 +587,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Process our weights ZEND_HASH_FOREACH_VAL(ht_weights, z_ele) { // Ignore non numeric args unless they're inf/-inf - + ZVAL_DEREF(z_ele); switch (Z_TYPE_P(z_ele)) { case IS_LONG: redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele)); @@ -1146,6 +1146,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Iterate our option array */ ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, v) { + ZVAL_DEREF(v); /* Detect PX or EX argument and validate timeout */ if (zkey && IS_EX_PX_ARG(zkey->val)) { /* Set expire type */ @@ -1381,6 +1382,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate over our member array ZEND_HASH_FOREACH_VAL(ht_arr, z_mem) { + ZVAL_DEREF(z_mem); // We can only handle string or long values here if ((Z_TYPE_P(z_mem) == IS_STRING && Z_STRLEN_P(z_mem) > 0) || Z_TYPE_P(z_mem) == IS_LONG @@ -2539,6 +2541,7 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, /* Iterate over our argument array, collating which ones we have */ ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, optval) { + ZVAL_DEREF(optval); /* If the key is numeric it's a non value option */ if (zkey) { if (zkey->len == 5 && !strcasecmp(zkey->val, "count") && Z_TYPE_P(optval) == IS_LONG) { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 3309914cde..a518a57eea 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2047,6 +2047,11 @@ public function testZX() { $this->assertTrue(array('val1', 'val2') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 2)))); $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => array(0, 100)))); + // limits as references + $limit = array(0, 100); + foreach ($limit as &$val) {} + $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => $limit))); + $this->assertTrue(array('val3') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 1)))); $this->assertTrue(array('val3', 'val2') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 2)))); $this->assertTrue(array('val2', 'val1') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(1, 2)))); @@ -2376,6 +2381,11 @@ public function testHashes() { $this->assertTrue('456' === $this->redis->hGet('h', 'y')); $this->assertTrue(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); + // references + $keys = array(123, 'y'); + foreach ($keys as &$key) {} + $this->assertTrue(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', $keys)); + // check non-string types. $this->redis->del('h1'); $this->assertTrue(TRUE === $this->redis->hMSet('h1', array('x' => 0, 'y' => array(), 'z' => new stdclass(), 't' => NULL))); From 0fef41ac963b1fccee35e9ddf9e43978543c4475 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 23 May 2017 23:24:53 +0300 Subject: [PATCH 0777/1986] Issue #1176 Adding `configureoption` tag to package.xml. --- package.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.xml b/package.xml index 483d2a58e6..0f15f31c14 100644 --- a/package.xml +++ b/package.xml @@ -97,7 +97,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
redis - + + + From d9bcd4ad8c6a6ff131b4dec0fd55d989ec5d7a5c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 12 Jan 2017 23:16:01 +0200 Subject: [PATCH 0778/1986] Issue #1055 --- redis.c | 328 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 247 insertions(+), 81 deletions(-) diff --git a/redis.c b/redis.c index 2505295ed1..2598376348 100644 --- a/redis.c +++ b/redis.c @@ -68,6 +68,172 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL) PHP_INI_END() +ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 2) + ZEND_ARG_INFO(0, host) + ZEND_ARG_INFO(0, port) + ZEND_ARG_INFO(0, timeout) + ZEND_ARG_INFO(0, retry_interval) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 2) + ZEND_ARG_INFO(0, host) + ZEND_ARG_INFO(0, port) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_echo, 0, 0, 1) + ZEND_ARG_INFO(0, msg) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key, 0, 0, 1) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, timeout) + ZEND_ARG_INFO(0, opt) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_setex, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, expire) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_expire_value, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, expire) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_value, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_newkey, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, newkey) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, keys, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) + ZEND_ARG_INFO(0, ...) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_start_end, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset_value, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sort, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, options, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_generic_sort, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, pattern) + ZEND_ARG_INFO(0, get) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) + ZEND_ARG_INFO(0, getList) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_blrpop, 0, 0, 2) + ZEND_ARG_INFO(0, ...) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lrem, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, count) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ltrim, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, stop) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lget, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, index) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lset, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_linsert, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, position) + ZEND_ARG_INFO(0, pivot) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sadd_array, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, options, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_smove, 0, 0, 3) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_srand_member, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, count) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_nkeys, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, ...) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dst_nkeys, 0, 0, 2) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, ...) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_set_timeout, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_auth, 0, 0, 1) + ZEND_ARG_INFO(0, password) +ZEND_END_ARG_INFO() /** * Argument info for the SCAN proper */ @@ -75,7 +241,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 1) ZEND_ARG_INFO(1, i_iterator) ZEND_ARG_INFO(0, str_pattern) ZEND_ARG_INFO(0, i_count) -ZEND_END_ARG_INFO(); +ZEND_END_ARG_INFO() /** * Argument info for key scanning @@ -85,95 +251,95 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2) ZEND_ARG_INFO(1, i_iterator) ZEND_ARG_INFO(0, str_pattern) ZEND_ARG_INFO(0, i_count) -ZEND_END_ARG_INFO(); +ZEND_END_ARG_INFO() #ifdef ZTS ZEND_DECLARE_MODULE_GLOBALS(redis) #endif static zend_function_entry redis_functions[] = { - PHP_ME(Redis, __construct, NULL, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - PHP_ME(Redis, __destruct, NULL, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC) - PHP_ME(Redis, connect, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pconnect, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, close, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, ping, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, echo, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, get, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, set, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, psetex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setnx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getSet, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, randomKey, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, renameKey, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, renameNx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getMultiple, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, exists, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, delete, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, incr, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, incrBy, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, incrByFloat, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, decr, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, decrBy, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, type, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, append, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getRange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setRange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getBit, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setBit, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, strlen, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getKeys, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sort, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortAsc, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortAscAlpha, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortDesc, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortDescAlpha, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lPush, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rPush, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lPushx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rPushx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lPop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rPop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, blPop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, brPop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lSize, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lRemove, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, listTrim, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lGet, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lGetRange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lSet, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lInsert, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sAdd, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sAddArray, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sSize, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sRemove, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sMove, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sPop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sRandMember, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sContains, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sMembers, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sInter, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sInterStore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sUnion, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sUnionStore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sDiff, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sDiffStore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setTimeout, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, save, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bgSave, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lastSave, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, flushDB, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, flushAll, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, dbSize, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, auth, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, ttl, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pttl, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, persist, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC) + PHP_ME(Redis, connect, arginfo_connect, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) + PHP_ME(Redis, close, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, ping, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, echo, arginfo_echo, ZEND_ACC_PUBLIC) + PHP_ME(Redis, get, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, set, arginfo_set, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setnx, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getSet, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, randomKey, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(Redis, renameNx, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) + PHP_ME(Redis, exists, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, delete, arginfo_del, ZEND_ACC_PUBLIC) + PHP_ME(Redis, incr, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, incrBy, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, incrByFloat, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, decr, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, decrBy, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, append, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setRange, arginfo_key_offset_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getBit, arginfo_key_offset, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setBit, arginfo_key_offset_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getKeys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sort, arginfo_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortAsc, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lPush, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rPush, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lPushx, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rPushx, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lPop, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rPop, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, blPop, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(Redis, brPop, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lSize, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) + PHP_ME(Redis, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lGet, arginfo_lget, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lSet, arginfo_lset, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sAdd, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sAddArray, arginfo_sadd_array, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sSize, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sContains, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sMembers, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sInter, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sInterStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sUnion, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sUnionStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sDiff, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sDiffStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setTimeout, arginfo_set_timeout, ZEND_ACC_PUBLIC) + PHP_ME(Redis, save, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bgSave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lastSave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, flushDB, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, flushAll, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, dbSize, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, auth, arginfo_auth, ZEND_ACC_PUBLIC) + PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pttl, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, persist, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, info, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, select, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, move, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bgrewriteaof, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, slaveof, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, object, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, bitop, NULL, ZEND_ACC_PUBLIC) From 3b0b065b2a62b0e667bd01e7f851dfaf0f5caf66 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 6 Jun 2017 14:12:37 +0300 Subject: [PATCH 0779/1986] Issue #1192 --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 97ba85079e..4ca1da8541 100644 --- a/README.markdown +++ b/README.markdown @@ -110,7 +110,7 @@ See [dedicated page](https://github.com/phpredis/phpredis/blob/master/arrays.mar ## Redis Cluster support -See [dedicated page](https://github.com/phpredis/phpredis/blob/feature/redis_cluster/cluster.markdown#readme). +See [dedicated page](https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#readme). ## Running the unit tests From c760bf613e1f792e4f403ffef4d21bc10a439946 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 6 Jun 2017 22:01:38 +0300 Subject: [PATCH 0780/1986] Remove PHP_RINIT and PHP_RSHUTDOWN Since we don't do anything while request start/end we may safely remove PHP_RINIT and PHP_RSHUTDOWN functions. --- php_redis.h | 2 -- redis.c | 20 ++------------------ 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/php_redis.h b/php_redis.h index f20c739721..4cc8c67428 100644 --- a/php_redis.h +++ b/php_redis.h @@ -237,8 +237,6 @@ PHP_METHOD(Redis, getMode); PHP_MINIT_FUNCTION(redis); PHP_MSHUTDOWN_FUNCTION(redis); -PHP_RINIT_FUNCTION(redis); -PHP_RSHUTDOWN_FUNCTION(redis); PHP_MINFO_FUNCTION(redis); /* Redis response handler function callback prototype */ diff --git a/redis.c b/redis.c index 2598376348..8e389bc1b7 100644 --- a/redis.c +++ b/redis.c @@ -523,8 +523,8 @@ zend_module_entry redis_module_entry = { NULL, PHP_MINIT(redis), PHP_MSHUTDOWN(redis), - PHP_RINIT(redis), - PHP_RSHUTDOWN(redis), + NULL, + NULL, PHP_MINFO(redis), #if ZEND_MODULE_API_NO >= 20010901 PHP_REDIS_VERSION, @@ -867,22 +867,6 @@ PHP_MSHUTDOWN_FUNCTION(redis) return SUCCESS; } -/** - * PHP_RINIT_FUNCTION - */ -PHP_RINIT_FUNCTION(redis) -{ - return SUCCESS; -} - -/** - * PHP_RSHUTDOWN_FUNCTION - */ -PHP_RSHUTDOWN_FUNCTION(redis) -{ - return SUCCESS; -} - /** * PHP_MINFO_FUNCTION */ From a09d0e65ab412959e5aa733b4b5938eb7108fbf2 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Sun, 23 Apr 2017 17:03:44 -0700 Subject: [PATCH 0781/1986] Add arginfo for ReflectionMethod to redis for a few more commands This is incomplete, writing arginfo for everything in a single PR would be difficult for reviewers. This focuses on string operations and some common commands for redis 1.0. The macro's arguments are: ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) This is based on what was written in README, not 100% sure if it matches the code. --- redis.c | 147 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 106 insertions(+), 41 deletions(-) diff --git a/redis.c b/redis.c index 8e389bc1b7..5c74f2c718 100644 --- a/redis.c +++ b/redis.c @@ -68,9 +68,11 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL) PHP_INI_END() +/** Argument info for any function expecting 0 args */ ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) ZEND_END_ARG_INFO() +/** {{{ Argument info for commands in redis 1.0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 2) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) @@ -99,12 +101,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) ZEND_ARG_INFO(0, opt) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_setex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expire) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_key_expire_value, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expire) @@ -126,7 +122,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) - ZEND_ARG_INFO(0, ...) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_key_start_end, 0, 0, 3) @@ -165,8 +164,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_generic_sort, 0, 0, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_blrpop, 0, 0, 2) - ZEND_ARG_INFO(0, ...) - ZEND_ARG_INFO(0, timeout) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, timeout_or_key) +// Can't have variadic keys before timeout. +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, extra_args) +#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_lrem, 0, 0, 3) @@ -217,13 +220,17 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_nkeys, 0, 0, 1) ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, ...) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_dst_nkeys, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, ...) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_set_timeout, 0, 0, 2) @@ -234,6 +241,59 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_auth, 0, 0, 1) ZEND_ARG_INFO(0, password) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1) + ZEND_ARG_INFO(0, dbindex) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_move, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, dbindex) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_slaveof, 0, 0, 0) + ZEND_ARG_INFO(0, host) + ZEND_ARG_INFO(0, port) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_object, 0, 0, 2) + ZEND_ARG_INFO(0, field) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_bitop, 0, 0, 3) + ZEND_ARG_INFO(0, operation) + ZEND_ARG_INFO(0, ret_key) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_bitpos, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, bit) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_eval, 0, 0, 1) + ZEND_ARG_INFO(0, script) + ZEND_ARG_INFO(0, args) + ZEND_ARG_INFO(0, num_keys) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_evalsha, 0, 0, 1) + ZEND_ARG_INFO(0, script_sha) + ZEND_ARG_INFO(0, args) + ZEND_ARG_INFO(0, num_keys) +ZEND_END_ARG_INFO() +/* }}} */ + /** * Argument info for the SCAN proper */ @@ -336,15 +396,15 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, pttl, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, persist, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, info, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, select, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, move, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, info, arginfo_info, ZEND_ACC_PUBLIC) + PHP_ME(Redis, select, arginfo_select, ZEND_ACC_PUBLIC) + PHP_ME(Redis, move, arginfo_move, ZEND_ACC_PUBLIC) PHP_ME(Redis, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, slaveof, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, object, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bitop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bitcount, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bitpos, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, slaveof, arginfo_slaveof, ZEND_ACC_PUBLIC) + PHP_ME(Redis, object, arginfo_object, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bitop, arginfo_bitop, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bitcount, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC) /* 1.1 */ PHP_ME(Redis, mset, NULL, ZEND_ACC_PUBLIC) @@ -404,10 +464,10 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, unsubscribe, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, punsubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, time, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, role, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, eval, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, evalsha, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, eval, arginfo_eval, ZEND_ACC_PUBLIC) + PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) PHP_ME(Redis, script, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, debug, NULL, ZEND_ACC_PUBLIC) @@ -415,8 +475,8 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, restore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, migrate, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getLastError, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, clearLastError, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getLastError, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, clearLastError, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, _serialize, NULL, ZEND_ACC_PUBLIC) @@ -458,24 +518,25 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, georadiusbymember, NULL, ZEND_ACC_PUBLIC) /* introspection */ - PHP_ME(Redis, getHost, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getPort, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getDBNum, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getTimeout, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getReadTimeout, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getPersistentID, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getAuth, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getHost, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getPort, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getDBNum, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getTimeout, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getReadTimeout, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getPersistentID, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getAuth, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, isConnected, arginfo_void, ZEND_ACC_PUBLIC) + /* TODO: document getMode() and wait() in README? */ PHP_ME(Redis, getMode, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, pubsub, NULL, ZEND_ACC_PUBLIC) /* aliases */ - PHP_MALIAS(Redis, open, connect, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, popen, pconnect, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, lLen, lSize, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sGetMembers, sMembers, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, mget, getMultiple, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, mget, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, expire, setTimeout, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zunionstore, zUnion, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zinterstore, zInter, NULL, ZEND_ACC_PUBLIC) @@ -486,9 +547,9 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, zRemRangeByScore, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zSize, zCard, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, substr, getRange, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, rename, renameKey, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, del, delete, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, rename, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, del, delete, arginfo_del, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, keys, getKeys, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, lrem, lRemove, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, ltrim, listTrim, NULL, ZEND_ACC_PUBLIC) @@ -499,10 +560,10 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zReverseRange, zRevRange, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sendEcho, echo, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, evaluate, eval, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, evaluateSha, evalsha, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -1317,11 +1378,13 @@ PHP_METHOD(Redis, getRange) } /* }}} */ +/* {{{ proto string Redis::setRange(string key, long start, string value) */ PHP_METHOD(Redis, setRange) { REDIS_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd, redis_long_response); } +/* }}} */ /* {{{ proto long Redis::getbit(string key, long idx) */ PHP_METHOD(Redis, getBit) @@ -1330,10 +1393,12 @@ PHP_METHOD(Redis, getBit) } /* }}} */ +/* {{{ proto long Redis::setbit(string key, long idx, bool|int value) */ PHP_METHOD(Redis, setBit) { REDIS_PROCESS_CMD(setbit, redis_long_response); } +/* }}} */ /* {{{ proto long Redis::strlen(string key) */ PHP_METHOD(Redis, strlen) From 503f26570a633990a2bdbc340968a0bf829a6050 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Sun, 4 Jun 2017 13:07:21 -0700 Subject: [PATCH 0782/1986] Address review comment --- redis.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/redis.c b/redis.c index 5c74f2c718..f20d9f0a7a 100644 --- a/redis.c +++ b/redis.c @@ -125,6 +125,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) ZEND_ARG_INFO(0, key) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) #endif ZEND_END_ARG_INFO() From f576fab2c05db4d7c84e41c98d10c00b98f53ccd Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 8 Jun 2017 23:13:37 +0300 Subject: [PATCH 0783/1986] Add arginfo for some commands --- redis.c | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/redis.c b/redis.c index f20d9f0a7a..f286d0f099 100644 --- a/redis.c +++ b/redis.c @@ -296,6 +296,41 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_evalsha, 0, 0, 1) ZEND_END_ARG_INFO() /* }}} */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_pairs, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, pairs, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_rpoplpush, 0, 0, 2) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_brpoplpush, 0, 0, 3) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zadd, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, score) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zrem, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_members) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zrange, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) + ZEND_ARG_INFO(0, scores) +ZEND_END_ARG_INFO() /** * Argument info for the SCAN proper */ @@ -409,14 +444,14 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC) /* 1.1 */ - PHP_ME(Redis, mset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, msetnx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rpoplpush, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, brpoplpush, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zAdd, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDelete, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRevRange, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, mset, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(Redis, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC) + PHP_ME(Redis, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zAdd, arginfo_zadd, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zDelete, arginfo_zrem, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRangeByScore, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByLex, NULL, ZEND_ACC_PUBLIC) From d0b9c5fd4ef7914ae03504d5135a33db9d53ef12 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 9 Jun 2017 23:22:23 +0300 Subject: [PATCH 0784/1986] Refactor ra_init_function_table Remove re-adding HGET element. Reorder commands alphabetically. Replace tabs with spaces. --- redis_array_impl.c | 74 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 2795491c6b..0735ab7e1f 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -96,43 +96,43 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b } /* List pure functions */ -void ra_init_function_table(RedisArray *ra) { - - array_init(&ra->z_pure_cmds); - - add_assoc_bool(&ra->z_pure_cmds, "HGET", 1); - add_assoc_bool(&ra->z_pure_cmds, "HGETALL", 1); - add_assoc_bool(&ra->z_pure_cmds, "HKEYS", 1); - add_assoc_bool(&ra->z_pure_cmds, "HLEN", 1); - add_assoc_bool(&ra->z_pure_cmds, "SRANDMEMBER", 1); - add_assoc_bool(&ra->z_pure_cmds, "HMGET", 1); - add_assoc_bool(&ra->z_pure_cmds, "STRLEN", 1); - add_assoc_bool(&ra->z_pure_cmds, "SUNION", 1); - add_assoc_bool(&ra->z_pure_cmds, "HVALS", 1); - add_assoc_bool(&ra->z_pure_cmds, "TYPE", 1); - add_assoc_bool(&ra->z_pure_cmds, "EXISTS", 1); - add_assoc_bool(&ra->z_pure_cmds, "LINDEX", 1); - add_assoc_bool(&ra->z_pure_cmds, "SCARD", 1); - add_assoc_bool(&ra->z_pure_cmds, "LLEN", 1); - add_assoc_bool(&ra->z_pure_cmds, "SDIFF", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZCARD", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZCOUNT", 1); - add_assoc_bool(&ra->z_pure_cmds, "LRANGE", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZRANGE", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZRANK", 1); - add_assoc_bool(&ra->z_pure_cmds, "GET", 1); - add_assoc_bool(&ra->z_pure_cmds, "GETBIT", 1); - add_assoc_bool(&ra->z_pure_cmds, "SINTER", 1); - add_assoc_bool(&ra->z_pure_cmds, "GETRANGE", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZREVRANGE", 1); - add_assoc_bool(&ra->z_pure_cmds, "SISMEMBER", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZREVRANGEBYSCORE", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZREVRANK", 1); - add_assoc_bool(&ra->z_pure_cmds, "HEXISTS", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZSCORE", 1); - add_assoc_bool(&ra->z_pure_cmds, "HGET", 1); - add_assoc_bool(&ra->z_pure_cmds, "OBJECT", 1); - add_assoc_bool(&ra->z_pure_cmds, "SMEMBERS", 1); +void +ra_init_function_table(RedisArray *ra) +{ + array_init(&ra->z_pure_cmds); + + add_assoc_bool(&ra->z_pure_cmds, "EXISTS", 1); + add_assoc_bool(&ra->z_pure_cmds, "GET", 1); + add_assoc_bool(&ra->z_pure_cmds, "GETBIT", 1); + add_assoc_bool(&ra->z_pure_cmds, "GETRANGE", 1); + add_assoc_bool(&ra->z_pure_cmds, "HEXISTS", 1); + add_assoc_bool(&ra->z_pure_cmds, "HGET", 1); + add_assoc_bool(&ra->z_pure_cmds, "HGETALL", 1); + add_assoc_bool(&ra->z_pure_cmds, "HKEYS", 1); + add_assoc_bool(&ra->z_pure_cmds, "HLEN", 1); + add_assoc_bool(&ra->z_pure_cmds, "HMGET", 1); + add_assoc_bool(&ra->z_pure_cmds, "HVALS", 1); + add_assoc_bool(&ra->z_pure_cmds, "LINDEX", 1); + add_assoc_bool(&ra->z_pure_cmds, "LLEN", 1); + add_assoc_bool(&ra->z_pure_cmds, "LRANGE", 1); + add_assoc_bool(&ra->z_pure_cmds, "OBJECT", 1); + add_assoc_bool(&ra->z_pure_cmds, "SCARD", 1); + add_assoc_bool(&ra->z_pure_cmds, "SDIFF", 1); + add_assoc_bool(&ra->z_pure_cmds, "SINTER", 1); + add_assoc_bool(&ra->z_pure_cmds, "SISMEMBER", 1); + add_assoc_bool(&ra->z_pure_cmds, "SMEMBERS", 1); + add_assoc_bool(&ra->z_pure_cmds, "SRANDMEMBER", 1); + add_assoc_bool(&ra->z_pure_cmds, "STRLEN", 1); + add_assoc_bool(&ra->z_pure_cmds, "SUNION", 1); + add_assoc_bool(&ra->z_pure_cmds, "TYPE", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZCARD", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZCOUNT", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZRANGE", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZRANK", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZREVRANGE", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZREVRANGEBYSCORE", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZREVRANK", 1); + add_assoc_bool(&ra->z_pure_cmds, "ZSCORE", 1); } static int From 3643ab61aefc6b14abc693331a6ab797d336ec0e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 12 Jun 2017 17:28:04 +0300 Subject: [PATCH 0785/1986] Add arginfo for some commands --- redis.c | 53 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/redis.c b/redis.c index f286d0f099..a395344e5b 100644 --- a/redis.c +++ b/redis.c @@ -331,6 +331,33 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zrange, 0, 0, 3) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, scores) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zrangebyscore, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) + ZEND_ARG_ARRAY_INFO(0, options, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zrangebylex, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, min) + ZEND_ARG_INFO(0, max) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, limit) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_min_max, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, min) + ZEND_ARG_INFO(0, max) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) +ZEND_END_ARG_INFO() + /** * Argument info for the SCAN proper */ @@ -452,19 +479,19 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zDelete, arginfo_zrem, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRangeByScore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRevRangeByScore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRangeByLex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRevRangeByLex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zLexCount, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRemRangeByLex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zCount, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zCard, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zScore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRank, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRevRank, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zLexCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRemRangeByLex, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zDeleteRangeByRank, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zCard, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRank, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRank, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, zInter, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zUnion, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, zIncrBy, NULL, ZEND_ACC_PUBLIC) From 024e593b4ad2f4f0de275dd0695cbddd73e571d5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 13 Jun 2017 16:27:11 +0300 Subject: [PATCH 0786/1986] Add arginfo for some commands --- redis.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/redis.c b/redis.c index a395344e5b..374694c93f 100644 --- a/redis.c +++ b/redis.c @@ -358,6 +358,24 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member, 0, 0, 2) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_zstore, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, keys, 0) + ZEND_ARG_ARRAY_INFO(0, weights, 0) + ZEND_ARG_INFO(0, aggregate) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zincrby, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, member) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_timestamp, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, timestamp) +ZEND_END_ARG_INFO() + /** * Argument info for the SCAN proper */ @@ -492,12 +510,12 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRank, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRank, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zInter, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zUnion, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zIncrBy, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, expireAt, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pexpire, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pexpireAt, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zIncrBy, arginfo_zincrby, ZEND_ACC_PUBLIC) + PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pexpireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) /* 1.2 */ PHP_ME(Redis, hGet, NULL, ZEND_ACC_PUBLIC) From 68598286dd57e2aa21b0f48a3fab39b341a9fa56 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 15 Jun 2017 16:42:48 +0300 Subject: [PATCH 0787/1986] Add arginfo for some commands --- redis.c | 52 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/redis.c b/redis.c index 374694c93f..82d25236f2 100644 --- a/redis.c +++ b/redis.c @@ -358,6 +358,30 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member, 0, 0, 2) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member_value, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_hdel, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_members) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_hmget, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, keys, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_hmset, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, pairs, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_zstore, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_ARRAY_INFO(0, keys, 0) @@ -518,20 +542,20 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, pexpireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) /* 1.2 */ - PHP_ME(Redis, hGet, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hSet, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hSetNx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hDel, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hLen, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hKeys, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hVals, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hGetAll, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hExists, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hIncrBy, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hIncrByFloat, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hMset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hMget, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hStrLen, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hGet, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hSet, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hSetNx, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hDel, arginfo_hdel, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hLen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hKeys, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hVals, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hGetAll, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hExists, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hIncrBy, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hIncrByFloat, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hMset, arginfo_hmset, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hMget, arginfo_hmget, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hStrLen, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, multi, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, discard, NULL, ZEND_ACC_PUBLIC) From 61aba96cc7dc125ce9430285ec4add04003d9af1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 15 Jun 2017 23:26:33 +0300 Subject: [PATCH 0788/1986] Create ISSUE_TEMPLATE.md --- ISSUE_TEMPLATE.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 ISSUE_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..acd04b76d0 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,15 @@ +### Expected behaviour + +### Actual behaviour + +### I'm seeing this behaviour on +- OS: +- Redis: +- PHP: +- phpredis: + +### Steps to reproduce, backtrace or example script + +### I've checked +- [ ] There is no similar issue from other users +- [ ] Issue isn't fixed in `develop` branch From 0d696502929abdba5f06e8c4b19d93d82a867719 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 15 Jun 2017 23:32:29 +0300 Subject: [PATCH 0789/1986] Add arginfo for some commands --- redis.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/redis.c b/redis.c index 82d25236f2..0f931dae28 100644 --- a/redis.c +++ b/redis.c @@ -400,6 +400,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key_timestamp, 0, 0, 2) ZEND_ARG_INFO(0, timestamp) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_watch, 0, 0, 1) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#endif +ZEND_END_ARG_INFO() + /** * Argument info for the SCAN proper */ @@ -557,12 +564,12 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, hMget, arginfo_hmget, ZEND_ACC_PUBLIC) PHP_ME(Redis, hStrLen, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(Redis, multi, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, discard, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, exec, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pipeline, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, watch, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, unwatch, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, multi, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, discard, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pipeline, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, watch, arginfo_watch, ZEND_ACC_PUBLIC) + PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, publish, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, subscribe, NULL, ZEND_ACC_PUBLIC) From 659450a6fc5d103731e889e1832f1d4192e836cd Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 16 Jun 2017 16:51:56 +0300 Subject: [PATCH 0790/1986] Issue #1199 Assume "NULL bulk" reply as success (empty session data). --- redis_session.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/redis_session.c b/redis_session.c index aa6434db90..8a418e3dd2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -670,17 +670,26 @@ PS_READ_FUNC(rediscluster) { /* Attempt to read reply */ reply = cluster_read_resp(c TSRMLS_CC); - if (!reply || c->err || reply->str == NULL) { + if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; } /* Push reply value to caller */ #if (PHP_MAJOR_VERSION < 7) - *val = reply->str; - *vallen = reply->len; + if (reply->str == NULL) { + *val = STR_EMPTY_ALLOC(); + *vallen = 0; + } else { + *val = reply->str; + *vallen = reply->len; + } #else - *val = zend_string_init(reply->str, reply->len, 0); + if (reply->str == NULL) { + *val = ZSTR_EMPTY_ALLOC(); + } else { + *val = zend_string_init(reply->str, reply->len, 0); + } #endif /* Clean up */ From 9caa029c32b48a0c287f439c7aaf365e12a4231f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 17 Jun 2017 22:37:46 +0300 Subject: [PATCH 0791/1986] Add arginfo for some commands --- redis.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/redis.c b/redis.c index 0f931dae28..a39a270fe2 100644 --- a/redis.c +++ b/redis.c @@ -94,6 +94,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_value, 0, 0, 1) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) @@ -171,6 +175,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_blrpop, 0, 0, 2) // Can't have variadic keys before timeout. #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, extra_args) +#else + ZEND_ARG_INFO(0, ...) #endif ZEND_END_ARG_INFO() @@ -224,6 +230,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_nkeys, 0, 0, 1) ZEND_ARG_INFO(0, key) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) #endif ZEND_END_ARG_INFO() @@ -232,6 +240,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_dst_nkeys, 0, 0, 2) ZEND_ARG_INFO(0, key) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) #endif ZEND_END_ARG_INFO() @@ -273,6 +283,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_bitop, 0, 0, 3) ZEND_ARG_INFO(0, key) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) #endif ZEND_END_ARG_INFO() @@ -322,6 +334,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zrem, 0, 0, 2) ZEND_ARG_INFO(0, member) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_members) +#else + ZEND_ARG_INFO(0, ...) #endif ZEND_END_ARG_INFO() @@ -369,6 +383,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_hdel, 0, 0, 2) ZEND_ARG_INFO(0, member) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_members) +#else + ZEND_ARG_INFO(0, ...) #endif ZEND_END_ARG_INFO() @@ -404,9 +420,67 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_watch, 0, 0, 1) ZEND_ARG_INFO(0, key) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_publish, 0, 0, 2) + ZEND_ARG_INFO(0, channel) + ZEND_ARG_INFO(0, message) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_subscribe, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, channels, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_psubscribe, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, patterns, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_unsubscribe, 0, 0, 1) + ZEND_ARG_INFO(0, channel) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_channels) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_punsubscribe, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_patterns) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) #endif ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_restore, 0, 0, 3) + ZEND_ARG_INFO(0, ttl) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_migrate, 0, 0, 5) + ZEND_ARG_INFO(0, host) + ZEND_ARG_INFO(0, port) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, db) + ZEND_ARG_INFO(0, timeout) + ZEND_ARG_INFO(0, copy) + ZEND_ARG_INFO(0, replace) +ZEND_END_ARG_INFO() + /** * Argument info for the SCAN proper */ @@ -571,29 +645,29 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, watch, arginfo_watch, ZEND_ACC_PUBLIC) PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, publish, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, subscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, psubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, unsubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, punsubscribe, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, publish, arginfo_publish, ZEND_ACC_PUBLIC) + PHP_ME(Redis, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC) + PHP_ME(Redis, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC) + PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC) + PHP_ME(Redis, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC) PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, role, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, role, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, eval, arginfo_eval, ZEND_ACC_PUBLIC) PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) - PHP_ME(Redis, script, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, script, arginfo_script, ZEND_ACC_PUBLIC) - PHP_ME(Redis, debug, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, dump, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, restore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, migrate, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, debug, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, dump, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, restore, arginfo_restore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, migrate, arginfo_migrate, ZEND_ACC_PUBLIC) PHP_ME(Redis, getLastError, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, clearLastError, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, _serialize, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) From 5c111ddcba87236286b30678fa189b2fefe2e17c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 18 Jun 2017 23:09:55 +0300 Subject: [PATCH 0792/1986] Add arginfo for some commands --- redis.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/redis.c b/redis.c index a39a270fe2..9576a8cb25 100644 --- a/redis.c +++ b/redis.c @@ -481,6 +481,62 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_migrate, 0, 0, 5) ZEND_ARG_INFO(0, replace) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_command, 0, 0, 0) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pfadd, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, elements, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pfmerge, 0, 0, 2) + ZEND_ARG_INFO(0, dstkey) + ZEND_ARG_ARRAY_INFO(0, keys, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_getoption, 0, 0, 1) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_setoption, 0, 0, 2) + ZEND_ARG_INFO(0, option) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2) + ZEND_ARG_INFO(0, cmd) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1) + ZEND_ARG_INFO(0, arg) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_rawcommand, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + /** * Argument info for the SCAN proper */ @@ -669,8 +725,8 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, client, arginfo_client, ZEND_ACC_PUBLIC) + PHP_ME(Redis, command, arginfo_command, ZEND_ACC_PUBLIC) /* SCAN and friends */ PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) @@ -679,22 +735,22 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) /* HyperLogLog commands */ - PHP_ME(Redis, pfadd, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pfcount, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pfmerge, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pfcount, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC) /* options */ - PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setOption, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getOption, arginfo_getoption, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setOption, arginfo_setoption, ZEND_ACC_PUBLIC) /* config */ - PHP_ME(Redis, config, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, config, arginfo_config, ZEND_ACC_PUBLIC) /* slowlog */ - PHP_ME(Redis, slowlog, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) /* Send a raw command and read raw results */ - PHP_ME(Redis, rawcommand, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) /* geoadd and friends */ PHP_ME(Redis, geoadd, NULL, ZEND_ACC_PUBLIC) From f116be96af4469a1864078d0e2af946a21a8f012 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 22 Jun 2017 23:14:02 +0300 Subject: [PATCH 0793/1986] Add arginfo for some commands --- redis.c | 64 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/redis.c b/redis.c index 9576a8cb25..2565a06b71 100644 --- a/redis.c +++ b/redis.c @@ -401,7 +401,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_zstore, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_ARRAY_INFO(0, keys, 0) - ZEND_ARG_ARRAY_INFO(0, weights, 0) + ZEND_ARG_ARRAY_INFO(0, weights, 1) ZEND_ARG_INFO(0, aggregate) ZEND_END_ARG_INFO() @@ -537,6 +537,20 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_rawcommand, 0, 0, 1) #endif ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_wait, 0, 0, 2) + ZEND_ARG_INFO(0, numslaves) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + /** * Argument info for the SCAN proper */ @@ -770,38 +784,38 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, getAuth, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, isConnected, arginfo_void, ZEND_ACC_PUBLIC) /* TODO: document getMode() and wait() in README? */ - PHP_ME(Redis, getMode, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pubsub, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getMode, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC) /* aliases */ PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lLen, lSize, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sGetMembers, sMembers, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lLen, lSize, arginfo_key, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, mget, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, expire, setTimeout, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zunionstore, zUnion, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zinterstore, zInter, NULL, ZEND_ACC_PUBLIC) - - PHP_MALIAS(Redis, zRemove, zDelete, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRem, zDelete, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemoveRangeByScore, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemRangeByScore, zDeleteRangeByScore, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zSize, zCard, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, expire, setTimeout, arginfo_set_timeout, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zunionstore, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zinterstore, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) + + PHP_MALIAS(Redis, zRemove, zDelete, arginfo_zrem, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRem, zDelete, arginfo_zrem, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemoveRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, rename, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, del, delete, arginfo_del, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, keys, getKeys, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lrem, lRemove, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, ltrim, listTrim, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lindex, lGet, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lrange, lGetRange, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, scard, sSize, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, srem, sRemove, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sismember, sContains, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zReverseRange, zRevRange, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, keys, getKeys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lrem, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, ltrim, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lindex, lGet, arginfo_lget, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lrange, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, scard, sSize, arginfo_key, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, srem, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, sismember, sContains, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zReverseRange, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC) From 4ef3acdd9e0304aed2c6564ac90a27ea396fa6e4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 26 Jun 2017 17:21:21 +0300 Subject: [PATCH 0794/1986] Add arginfo for some commands --- redis.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/redis.c b/redis.c index 2565a06b71..1c176bed6d 100644 --- a/redis.c +++ b/redis.c @@ -551,6 +551,62 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_wait, 0, 0, 2) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_geoadd, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, lng) + ZEND_ARG_INFO(0, lat) + ZEND_ARG_INFO(0, member) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_triples) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_geohash, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_members) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_geopos, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_members) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_geodist, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, unit) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_georadius, 0, 0, 5) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, lng) + ZEND_ARG_INFO(0, lan) + ZEND_ARG_INFO(0, radius) + ZEND_ARG_INFO(0, unit) + ZEND_ARG_ARRAY_INFO(0, opts, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_georadiusbymember, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, radius) + ZEND_ARG_INFO(0, unit) + ZEND_ARG_ARRAY_INFO(0, opts, 0) +ZEND_END_ARG_INFO() + /** * Argument info for the SCAN proper */ @@ -767,12 +823,12 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) /* geoadd and friends */ - PHP_ME(Redis, geoadd, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, geohash, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, geopos, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, geodist, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, georadius, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Redis, georadiusbymember, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geohash, arginfo_geohash, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geopos, arginfo_geopos, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) + PHP_ME(Redis, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) + PHP_ME(Redis, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) /* introspection */ PHP_ME(Redis, getHost, arginfo_void, ZEND_ACC_PUBLIC) From f8de7020982ee154579c728b60b49a7bdddc18d8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 26 Jun 2017 17:35:23 +0300 Subject: [PATCH 0795/1986] Remove arginfo duplicates --- redis.c | 56 +++++++++++++------------------------------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/redis.c b/redis.c index 1c176bed6d..cca62543f5 100644 --- a/redis.c +++ b/redis.c @@ -329,16 +329,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zadd, 0, 0, 3) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_zrem, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_members) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_zrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) @@ -372,13 +362,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member, 0, 0, 2) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member_value, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_hdel, 0, 0, 2) +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_members, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) #if PHP_VERSION_ID >= 50600 @@ -388,6 +372,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_hdel, 0, 0, 2) #endif ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member_value, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_hmget, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_ARRAY_INFO(0, keys, 0) @@ -563,26 +553,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_geoadd, 0, 0, 4) #endif ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_geohash, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_members) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_geopos, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_members) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_geodist, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, src) @@ -725,7 +695,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC) PHP_ME(Redis, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) PHP_ME(Redis, zAdd, arginfo_zadd, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDelete, arginfo_zrem, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) @@ -752,7 +722,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, hGet, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, hSet, arginfo_key_member_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, hSetNx, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hDel, arginfo_hdel, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hDel, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, hLen, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, hKeys, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, hVals, arginfo_key, ZEND_ACC_PUBLIC) @@ -824,8 +794,8 @@ static zend_function_entry redis_functions[] = { /* geoadd and friends */ PHP_ME(Redis, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) - PHP_ME(Redis, geohash, arginfo_geohash, ZEND_ACC_PUBLIC) - PHP_ME(Redis, geopos, arginfo_geopos, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geopos, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) PHP_ME(Redis, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) PHP_ME(Redis, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) @@ -854,8 +824,8 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, zunionstore, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zinterstore, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemove, zDelete, arginfo_zrem, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRem, zDelete, arginfo_zrem, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemove, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRem, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zRemoveRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zRemRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC) From ea15ce24cd45072702a7c841593d14e800a1aeac Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 13 Jul 2017 00:13:19 +0300 Subject: [PATCH 0796/1986] Remove redis_send_discard duplication --- library.c | 22 ---------------------- library.h | 1 - redis.c | 8 +++++--- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/library.c b/library.c index 3d970b3a7b..6abb202a7b 100644 --- a/library.c +++ b/library.c @@ -1527,28 +1527,6 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) return 0; } -PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock) -{ - char *cmd; - int response_len, cmd_len; - char * response; - - cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, &cmd, "DISCARD", ""); - - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) - efree(cmd); - - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) - == NULL) - { - RETURN_FALSE; - } - - RETVAL_BOOL(response_len == 3 && strncmp(response, "+OK", 3) == 0); - efree(response); -} - /** * redis_sock_set_err */ diff --git a/library.h b/library.h index e0496f9789..0dc527b060 100644 --- a/library.h +++ b/library.h @@ -66,7 +66,6 @@ PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC); PHP_REDIS_API RedisSock *redis_sock_get(zval *id TSRMLS_DC, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); -PHP_REDIS_API void redis_send_discard(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); PHP_REDIS_API int diff --git a/redis.c b/redis.c index cca62543f5..93d27b9698 100644 --- a/redis.c +++ b/redis.c @@ -906,7 +906,9 @@ PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) } /* Send a static DISCARD in case we're in MULTI mode. */ -static int send_discard_static(RedisSock *redis_sock TSRMLS_DC) { +static int +redis_send_discard(RedisSock *redis_sock TSRMLS_DC) +{ int result = FAILURE; char *cmd, *resp; int resp_len, cmd_len; @@ -1255,7 +1257,7 @@ PHP_METHOD(Redis,__destruct) { IF_MULTI() { // Discard any multi commands, and free any callbacks that have been // queued - send_discard_static(redis_sock TSRMLS_CC); + redis_send_discard(redis_sock TSRMLS_CC); free_reply_callbacks(redis_sock); } } @@ -2673,7 +2675,7 @@ PHP_METHOD(Redis, discard) redis_sock->mode = ATOMIC; free_reply_callbacks(redis_sock); - redis_send_discard(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + RETURN_BOOL(redis_send_discard(redis_sock TSRMLS_CC) == SUCCESS); } /* redis_sock_read_multibulk_multi_reply */ From 735025d700d2091c92146cb28fa1d630a38df456 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 19 Jul 2017 15:13:33 +0300 Subject: [PATCH 0797/1986] Fix some static analyzer warnings Two types of warnings were fixed in this commit: 1. The left operand of '<=' is a garbage value 2. Function call argument is an uninitialized value --- cluster_library.c | 1 + library.c | 4 ++++ redis_array_impl.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index c1efd0db6a..34c3442e99 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2074,6 +2074,7 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb, zval *z_ret) { + ZVAL_NULL(z_ret); // Pull our next response if directed if(pull) { if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) diff --git a/library.c b/library.c index 6abb202a7b..5f4cc74104 100644 --- a/library.c +++ b/library.c @@ -430,6 +430,7 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, int numElems; size_t len; + ZVAL_NULL(z_tab); if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { return NULL; } @@ -1787,6 +1788,8 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len uint8_t *val8; #endif + *val = NULL; + *val_len = 0; switch(redis_sock->serializer) { case REDIS_SERIALIZER_NONE: switch(Z_TYPE_P(z)) { @@ -1977,6 +1980,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, // Make sure we haven't lost the connection, even trying to reconnect if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { // Failure + *reply_type = EOF; return -1; } diff --git a/redis_array_impl.c b/redis_array_impl.c index 0735ab7e1f..2d48214427 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -300,7 +300,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } /* find read timeout option */ - array_init(&z_params_connect_timeout); + array_init(&z_params_read_timeout); if ((iptr = INI_STR("redis.arrays.readtimeout")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_read_timeout TSRMLS_CC); } From 26eeda5b36a163d907886663c8d388506e6d8caa Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Jul 2017 23:26:52 +0300 Subject: [PATCH 0798/1986] CID 157208 --- library.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 5f4cc74104..c12f21ab7e 100644 --- a/library.c +++ b/library.c @@ -1466,8 +1466,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) /* set TCP_NODELAY */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; - setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, - sizeof(int)); + if (setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)) < 0) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate TCP_NODELAY option!"); + } php_stream_auto_cleanup(redis_sock->stream); From 733732a94b221f69daf4fd3b758c43f8711e93db Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Jul 2017 23:30:28 +0300 Subject: [PATCH 0799/1986] CID 157207 --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 34c3442e99..344b5400c6 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -998,7 +998,7 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) // Success, apply it c->redir_type = moved ? REDIR_MOVED : REDIR_ASK; - strncpy(c->redir_host, host, sizeof(c->redir_host)); + strncpy(c->redir_host, host, sizeof(c->redir_host) - 1); c->redir_host_len = port - host - 1; c->redir_slot = (unsigned short)atoi(msg); c->redir_port = (unsigned short)atoi(port); From 1e0b06546882fd74d75fc4efb61b67b5bfd32293 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Jul 2017 23:31:50 +0300 Subject: [PATCH 0800/1986] CID 157206 --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index d3f42ebfac..bd27a04130 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -454,7 +454,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { read_timeout = Z_DVAL_P(z_value); } else if (Z_TYPE_P(z_value) == IS_LONG) { - timeout = Z_LVAL_P(z_value); + read_timeout = Z_LVAL_P(z_value); } } From 0438ab4200f08d44809634765a3669bd11599a0e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Jul 2017 23:34:09 +0300 Subject: [PATCH 0801/1986] CID 157204 --- library.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library.c b/library.c index c12f21ab7e..957d62510b 100644 --- a/library.c +++ b/library.c @@ -625,6 +625,8 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k fmt++; } + /* varargs cleanup */ + va_end(ap); /* Null terminate */ smart_string_0(&cmd); From 4766c257a31a7faf66a8ec0913b057d88ad47195 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Jul 2017 23:38:38 +0300 Subject: [PATCH 0802/1986] CID 157199 --- redis_commands.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index a9d0878ca0..70689a9bf3 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3064,9 +3064,9 @@ void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { } if(redis_sock->prefix != NULL && redis_sock->prefix_len>0) { - redis_key_prefix(redis_sock, &key, &key_len); + int keyfree = redis_key_prefix(redis_sock, &key, &key_len); RETVAL_STRINGL(key, key_len); - efree(key); + if (keyfree) efree(key); } else { RETURN_STRINGL(key, key_len); } From 1a7a259111ec6cadcd43bda4fee6fca1e129657c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Jul 2017 23:45:37 +0300 Subject: [PATCH 0803/1986] Coverity widget --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index 4ca1da8541..d2532ed546 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,7 @@ # PhpRedis [![Build Status](https://travis-ci.org/phpredis/phpredis.svg?branch=develop)](https://travis-ci.org/phpredis/phpredis) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis) The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). This code has been developed and maintained by Owlient from November 2009 to March 2011. From be599147adf2b8671bedf43b2b01dc0cb9ed0e2d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 25 Jul 2017 15:52:02 +0300 Subject: [PATCH 0804/1986] runtime exteption --- library.c | 19 +++++++++++++++++++ library.h | 1 + redis.c | 34 ++++------------------------------ redis_cluster.c | 24 ------------------------ 4 files changed, 24 insertions(+), 54 deletions(-) diff --git a/library.c b/library.c index 957d62510b..d98ed5b493 100644 --- a/library.c +++ b/library.c @@ -49,6 +49,25 @@ extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; +static zend_class_entry *runtime_exception_ce = NULL; + +PHP_REDIS_API zend_class_entry * +redis_get_exception_base(TSRMLS_D) +{ + if (runtime_exception_ce = NULL) { +#if HAVE_SPL + runtime_exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1); +#else + #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) + runtime_exception_ce = zend_exception_get_default(); + #else + runtime_exception_ce = zend_exception_get_default(TSRMLS_C); + #endif +#endif + } + return runtime_exception_ce; +} + /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; diff --git a/library.h b/library.h index 0dc527b060..2816af73cd 100644 --- a/library.h +++ b/library.h @@ -88,4 +88,5 @@ PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); +PHP_REDIS_API zend_class_entry *redis_get_exception_base(TSRMLS_D); #endif diff --git a/redis.c b/redis.c index 93d27b9698..9c90abeb11 100644 --- a/redis.c +++ b/redis.c @@ -48,7 +48,6 @@ extern zend_class_entry *redis_cluster_ce; zend_class_entry *redis_ce; zend_class_entry *redis_exception_ce; extern zend_class_entry *redis_cluster_exception_ce; -static zend_class_entry *spl_ce_RuntimeException = NULL; extern zend_function_entry redis_array_functions[]; extern zend_function_entry redis_cluster_functions[]; @@ -880,31 +879,6 @@ zend_module_entry redis_module_entry = { ZEND_GET_MODULE(redis) #endif -PHP_REDIS_API zend_class_entry *redis_get_exception_base(int root TSRMLS_DC) -{ -#if HAVE_SPL - if (!root) { - if (!spl_ce_RuntimeException) { - zend_class_entry *pce; - - if ((pce = zend_hash_str_find_ptr(CG(class_table), "runtimeexception", - sizeof("RuntimeException") - 1))) - { - spl_ce_RuntimeException = pce; - return pce; - } - } else { - return spl_ce_RuntimeException; - } - } -#endif -#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) - return zend_exception_get_default(); -#else - return zend_exception_get_default(TSRMLS_C); -#endif -} - /* Send a static DISCARD in case we're in MULTI mode. */ static int redis_send_discard(RedisSock *redis_sock TSRMLS_DC) @@ -1173,10 +1147,10 @@ PHP_MINIT_FUNCTION(redis) redis_exception_ce = zend_register_internal_class_ex( &redis_exception_class_entry, #if (PHP_MAJOR_VERSION < 7) - redis_get_exception_base(0 TSRMLS_CC), + redis_get_exception_base(TSRMLS_C), NULL TSRMLS_CC #else - redis_get_exception_base(0) + redis_get_exception_base(TSRMLS_C) #endif ); @@ -1186,10 +1160,10 @@ PHP_MINIT_FUNCTION(redis) redis_cluster_exception_ce = zend_register_internal_class_ex( &redis_cluster_exception_class_entry, #if (PHP_MAJOR_VERSION < 7) - rediscluster_get_exception_base(0 TSRMLS_CC), + redis_get_exception_base(TSRMLS_C), NULL TSRMLS_CC #else - rediscluster_get_exception_base(0) + redis_get_exception_base(TSRMLS_C) #endif ); diff --git a/redis_cluster.c b/redis_cluster.c index bd27a04130..1d3c81399d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -37,7 +37,6 @@ zend_class_entry *redis_cluster_ce; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; -static zend_class_entry *spl_rte_ce = NULL; /* Handlers for RedisCluster */ zend_object_handlers RedisCluster_handlers; @@ -253,29 +252,6 @@ static void ht_free_node(zval *data) cluster_free_node(node); } -/* Initialize/Register our RedisCluster exceptions */ -PHPAPI zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC) { -#if HAVE_SPL - if(!root) { - if(!spl_rte_ce) { - zend_class_entry *pce; - - if ((pce = zend_hash_str_find_ptr(CG(class_table), "runtimeexception", sizeof("runtimeexception") - 1))) { - spl_rte_ce = pce; - return pce; - } - } else { - return spl_rte_ce; - } - } -#endif -#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) - return zend_exception_get_default(); -#else - return zend_exception_get_default(TSRMLS_C); -#endif -} - /* Create redisCluster context */ #if (PHP_MAJOR_VERSION < 7) zend_object_value From 37f5693bb7d73349462fe84764589de7c98f3a2b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 28 Jul 2017 11:07:59 +0300 Subject: [PATCH 0805/1986] Init gc value for php5 zval_get_string --- common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common.h b/common.h index 1ed857c338..dc82e87a17 100644 --- a/common.h +++ b/common.h @@ -328,6 +328,7 @@ zval_get_string(zval *op) { zend_string *zstr = ecalloc(1, sizeof(zend_string)); + zstr->gc = 0; zstr->val = ""; zstr->len = 0; switch (Z_TYPE_P(op)) { From eff7398606c43e639db79056a69edc3e6e981322 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 28 Jul 2017 13:45:16 +0300 Subject: [PATCH 0806/1986] CID 158614, 158615 --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index d98ed5b493..a327eb68e3 100644 --- a/library.c +++ b/library.c @@ -54,7 +54,7 @@ static zend_class_entry *runtime_exception_ce = NULL; PHP_REDIS_API zend_class_entry * redis_get_exception_base(TSRMLS_D) { - if (runtime_exception_ce = NULL) { + if (runtime_exception_ce == NULL) { #if HAVE_SPL runtime_exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1); #else From faac8b06000508a6a6e4be94019d2fda030a92c7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 28 Jul 2017 14:18:26 +0300 Subject: [PATCH 0807/1986] CID 158616 --- redis_array.c | 6 ++---- redis_array_impl.c | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/redis_array.c b/redis_array.c index ce3a6eb149..d88bd2d073 100644 --- a/redis_array.c +++ b/redis_array.c @@ -976,9 +976,7 @@ PHP_METHOD(RedisArray, mget) } for(i = 0, j = 0; i < argc; ++i) { - if(pos[i] != n) continue; - - z_cur = zend_hash_index_find(Z_ARRVAL(z_ret), j++); + if (pos[i] != n || (z_cur = zend_hash_index_find(Z_ARRVAL(z_ret), j++)) == NULL) continue; #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); @@ -995,7 +993,7 @@ PHP_METHOD(RedisArray, mget) array_init(return_value); /* copy temp array in the right order to return_value */ for(i = 0; i < argc; ++i) { - z_cur = zend_hash_index_find(Z_ARRVAL(z_tmp_array), i); + if ((z_cur = zend_hash_index_find(Z_ARRVAL(z_tmp_array), i)) == NULL) continue; #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); diff --git a/redis_array_impl.c b/redis_array_impl.c index 2d48214427..a551c9ab55 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -545,7 +545,12 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { /* prepare keys */ for(i = 0; i < argc - 1; ++i) { - z_args[i+1] = *zend_hash_index_find(Z_ARRVAL_P(z_keys), i); + zval *zv = zend_hash_index_find(Z_ARRVAL_P(z_keys), i); + if (zv == NULL) { + ZVAL_NULL(&z_args[i+1]); + } else { + z_args[i+1] = *zv; + } } /* run cmd */ From 4f290ea986978aaf52a2b71059816642e4633bb5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 28 Jul 2017 15:16:10 +0300 Subject: [PATCH 0808/1986] TravisCI: dist precise --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2e4c5bd335..6701b311d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: precise sudo: required language: php php: From b624a8bfd6dad951ac319563a77cdb90559f294e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 31 Jul 2017 18:19:09 +0300 Subject: [PATCH 0809/1986] Fix valgrind warnings --- cluster_library.c | 2 +- redis_cluster.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 344b5400c6..1b4eb35b43 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2036,7 +2036,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // Return our array if(CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_result, 0, 1); + RETVAL_ZVAL(z_result, 1, 0); } else { add_next_index_zval(&c->multi_resp, z_result); } diff --git a/redis_cluster.c b/redis_cluster.c index 1d3c81399d..ca67a57041 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1077,7 +1077,7 @@ PHP_METHOD(RedisCluster, keys) { efree(cmd); /* Return our keys */ - RETURN_ZVAL(z_ret, 0, 1); + RETURN_ZVAL(z_ret, 1, 0); } /* }}} */ @@ -1987,7 +1987,7 @@ PHP_METHOD(RedisCluster, _masters) { add_next_index_zval(z_ret, z_sub); } - RETVAL_ZVAL(z_ret, 0, 1); + RETVAL_ZVAL(z_ret, 1, 0); } PHP_METHOD(RedisCluster, _redir) { From 1ab89e132058c73bb08d39d30390086bc2b82260 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 1 Aug 2017 14:23:53 +0300 Subject: [PATCH 0810/1986] Fix valgrind warnings Duplicate (MAKE_STD_ZVAL + ZVAL_ZVAL) params in php7 compatible implementation of call_user_function. --- common.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/common.h b/common.h index dc82e87a17..ce7383e699 100644 --- a/common.h +++ b/common.h @@ -242,13 +242,19 @@ inline_call_user_function(HashTable *function_table, zval *object, zval *functio if (!params) param_count = 0; if (param_count > 0) { _params = ecalloc(param_count, sizeof(zval *)); - for (i = 0; i < param_count; i++) { - _params[i] = ¶ms[i]; - INIT_PZVAL(_params[i]); + for (i = 0; i < param_count; ++i) { + zval *zv = ¶ms[i]; + MAKE_STD_ZVAL(_params[i]); + ZVAL_ZVAL(_params[i], zv, 1, 0); } } ret = _call_user_function(function_table, &object, function_name, retval_ptr, param_count, _params TSRMLS_CC); - if (_params) efree(_params); + if (_params) { + for (i = 0; i < param_count; ++i) { + zval_ptr_dtor(&_params[i]); + } + efree(_params); + } return ret; } From 7c140714e3d3083162fdaea09fbd7e080ee156ac Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 1 Aug 2017 18:07:33 +0300 Subject: [PATCH 0811/1986] Remove redis_get_exception_base --- library.c | 19 ------------------- library.h | 1 - redis.c | 25 +++++++++++++++++++------ 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/library.c b/library.c index a327eb68e3..957d62510b 100644 --- a/library.c +++ b/library.c @@ -49,25 +49,6 @@ extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; -static zend_class_entry *runtime_exception_ce = NULL; - -PHP_REDIS_API zend_class_entry * -redis_get_exception_base(TSRMLS_D) -{ - if (runtime_exception_ce == NULL) { -#if HAVE_SPL - runtime_exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1); -#else - #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) - runtime_exception_ce = zend_exception_get_default(); - #else - runtime_exception_ce = zend_exception_get_default(TSRMLS_C); - #endif -#endif - } - return runtime_exception_ce; -} - /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; diff --git a/library.h b/library.h index 2816af73cd..0dc527b060 100644 --- a/library.h +++ b/library.h @@ -88,5 +88,4 @@ PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); -PHP_REDIS_API zend_class_entry *redis_get_exception_base(TSRMLS_D); #endif diff --git a/redis.c b/redis.c index 9c90abeb11..6b178216d5 100644 --- a/redis.c +++ b/redis.c @@ -1121,6 +1121,8 @@ PHP_MINIT_FUNCTION(redis) zend_class_entry redis_exception_class_entry; zend_class_entry redis_cluster_exception_class_entry; + zend_class_entry *exception_ce = NULL; + /* Seed random generator (for RedisCluster failover) */ gettimeofday(&tv, NULL); srand(tv.tv_usec * tv.tv_sec); @@ -1142,15 +1144,27 @@ PHP_MINIT_FUNCTION(redis) redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry TSRMLS_CC); redis_cluster_ce->create_object = create_cluster_context; + + /* Base Exception class */ +#if HAVE_SPL + exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1); +#endif + if (exception_ce == NULL) { +#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) + exception_ce = zend_exception_get_default(); +#else + exception_ce = zend_exception_get_default(TSRMLS_C); +#endif + } + /* RedisException class */ INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL); redis_exception_ce = zend_register_internal_class_ex( &redis_exception_class_entry, #if (PHP_MAJOR_VERSION < 7) - redis_get_exception_base(TSRMLS_C), - NULL TSRMLS_CC + exception_ce, NULL TSRMLS_CC #else - redis_get_exception_base(TSRMLS_C) + exception_ce #endif ); @@ -1160,10 +1174,9 @@ PHP_MINIT_FUNCTION(redis) redis_cluster_exception_ce = zend_register_internal_class_ex( &redis_cluster_exception_class_entry, #if (PHP_MAJOR_VERSION < 7) - redis_get_exception_base(TSRMLS_C), - NULL TSRMLS_CC + exception_ce, NULL TSRMLS_CC #else - redis_get_exception_base(TSRMLS_C) + exception_ce #endif ); From 4e32254ec22c37d74a3a22a03a7cf981c1126b60 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 2 Aug 2017 13:02:07 +0300 Subject: [PATCH 0812/1986] Use ZSTR_VAL and ZSTR_LEN macroses to access zend_string fields --- cluster_library.c | 2 +- common.h | 28 +++++++++-------- library.c | 16 +++++----- redis.c | 12 ++++---- redis_array.c | 4 +-- redis_array_impl.c | 4 +-- redis_cluster.c | 12 ++++---- redis_commands.c | 76 +++++++++++++++++++++++----------------------- redis_session.c | 24 +++++++-------- 9 files changed, 91 insertions(+), 87 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 1b4eb35b43..987142e81e 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2431,7 +2431,7 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, zval zv, *z = &zv; if (redis_unserialize(redis_sock,key,key_len, z TSRMLS_CC)) { zend_string *zstr = zval_get_string(z); - add_assoc_double_ex(z_result, zstr->val, zstr->len, atof(line)); + add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line)); zend_string_release(zstr); zval_dtor(z); } else { diff --git a/common.h b/common.h index ce7383e699..ed47b92db2 100644 --- a/common.h +++ b/common.h @@ -22,9 +22,12 @@ typedef struct { char *val; } zend_string; +#define ZSTR_VAL(s) (s)->val +#define ZSTR_LEN(s) (s)->len + #define zend_string_release(s) do { \ if ((s) && (s)->gc) { \ - if ((s)->gc & 0x10 && (s)->val) efree((s)->val); \ + if ((s)->gc & 0x10 && ZSTR_VAL(s)) efree(ZSTR_VAL(s)); \ if ((s)->gc & 0x01) efree((s)); \ } \ } while (0) @@ -210,21 +213,22 @@ extern int (*_add_next_index_stringl)(zval *, const char *, uint, int); #undef ZVAL_STRING #define ZVAL_STRING(z, s) do { \ - const char *_s=(s); \ + const char *_s = (s); \ ZVAL_STRINGL(z, _s, strlen(_s)); \ } while (0) #undef RETVAL_STRING #define RETVAL_STRING(s) ZVAL_STRING(return_value, s) #undef RETURN_STRING #define RETURN_STRING(s) { RETVAL_STRING(s); return; } + #undef ZVAL_STRINGL #define ZVAL_STRINGL(z, s, l) do { \ - const char *__s=(s); int __l=l; \ + const char *__s = (s); int __l = l; \ zval *__z = (z); \ Z_STRLEN_P(__z) = __l; \ Z_STRVAL_P(__z) = estrndup(__s, __l); \ Z_TYPE_P(__z) = IS_STRING; \ -} while(0) +} while (0) #undef RETVAL_STRINGL #define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l) #undef RETURN_STRINGL @@ -335,27 +339,27 @@ zval_get_string(zval *op) zend_string *zstr = ecalloc(1, sizeof(zend_string)); zstr->gc = 0; - zstr->val = ""; - zstr->len = 0; + ZSTR_VAL(zstr) = ""; + ZSTR_LEN(zstr) = 0; switch (Z_TYPE_P(op)) { case IS_STRING: - zstr->val = Z_STRVAL_P(op); - zstr->len = Z_STRLEN_P(op); + ZSTR_VAL(zstr) = Z_STRVAL_P(op); + ZSTR_LEN(zstr) = Z_STRLEN_P(op); break; case IS_BOOL: if (Z_LVAL_P(op)) { - zstr->val = "1"; - zstr->len = 1; + ZSTR_VAL(zstr) = "1"; + ZSTR_LEN(zstr) = 1; } break; case IS_LONG: { zstr->gc = 0x10; - zstr->len = spprintf(&zstr->val, 0, "%ld", Z_LVAL_P(op)); + ZSTR_LEN(zstr) = spprintf(&ZSTR_VAL(zstr), 0, "%ld", Z_LVAL_P(op)); break; } case IS_DOUBLE: { zstr->gc = 0x10; - zstr->len = spprintf(&zstr->val, 0, "%.16g", Z_DVAL_P(op)); + ZSTR_LEN(zstr) = spprintf(&ZSTR_VAL(zstr), 0, "%.16g", Z_DVAL_P(op)); break; } EMPTY_SWITCH_DEFAULT_CASE() diff --git a/library.c b/library.c index 957d62510b..62312c0ce7 100644 --- a/library.c +++ b/library.c @@ -590,7 +590,7 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k break; case 'S': arg.zstr = va_arg(ap, zend_string*); - redis_cmd_append_sstr(&cmd, arg.zstr->val, arg.zstr->len); + redis_cmd_append_sstr(&cmd, ZSTR_VAL(arg.zstr), ZSTR_LEN(arg.zstr)); break; case 'k': arg.str = va_arg(ap, char*); @@ -1119,16 +1119,16 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, /* Decode the score depending on flag */ if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) { - add_assoc_long_ex(z_ret, hkey->val, hkey->len, atoi(hval+1)); + add_assoc_long_ex(z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atoi(hval+1)); } else if (decode == SCORE_DECODE_DOUBLE) { - add_assoc_double_ex(z_ret, hkey->val, hkey->len, atof(hval)); + add_assoc_double_ex(z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atof(hval)); } else { zval zv0, *z = &zv0; #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); #endif ZVAL_ZVAL(z, z_value_p, 1, 0); - add_assoc_zval_ex(z_ret, hkey->val, hkey->len, z); + add_assoc_zval_ex(z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), z); } zend_string_release(hkey); } @@ -1814,8 +1814,8 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len default: { /* copy */ zend_string *zstr = zval_get_string(z); - *val = estrndup(zstr->val, zstr->len); - *val_len = zstr->len; + *val = estrndup(ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + *val_len = ZSTR_LEN(zstr); zend_string_release(zstr); return 1; } @@ -1833,8 +1833,8 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len *val = estrndup(sstr.c, sstr.len); *val_len = sstr.len; #else - *val = estrndup(sstr.s->val, sstr.s->len); - *val_len = sstr.s->len; + *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); + *val_len = ZSTR_LEN(sstr.s); #endif smart_str_free(&sstr); #if ZEND_MODULE_API_NO >= 20100000 diff --git a/redis.c b/redis.c index 6b178216d5..9d6d535f2b 100644 --- a/redis.c +++ b/redis.c @@ -1547,7 +1547,7 @@ PHP_METHOD(Redis, getMultiple) /* Iterate through and grab our keys */ ZEND_HASH_FOREACH_VAL(hash, z_ele) { zend_string *zstr = zval_get_string(z_ele); - redis_cmd_append_sstr_key(&cmd, zstr->val, zstr->len, redis_sock, NULL); + redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, NULL); zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); @@ -2004,13 +2004,13 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zget), zele) { zpattern = zval_get_string(zele); redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1); - redis_cmd_append_sstr(&cmd, zpattern->val, zpattern->len); + redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern)); zend_string_release(zpattern); } ZEND_HASH_FOREACH_END(); } else { zpattern = zval_get_string(zget); redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1); - redis_cmd_append_sstr(&cmd, zpattern->val, zpattern->len); + redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern)); zend_string_release(zpattern); } } @@ -2273,7 +2273,7 @@ void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) ZEND_HASH_FOREACH_KEY_VAL(htargs, idx, zkey, zmem) { /* Handle string or numeric keys */ if (zkey) { - redis_cmd_append_sstr_key(&cmd, zkey->val, zkey->len, redis_sock, NULL); + redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, NULL); } else { keylen = snprintf(buf, sizeof(buf), "%ld", (long)idx); redis_cmd_append_sstr_key(&cmd, buf, (strlen_t)keylen, redis_sock, NULL); @@ -3197,7 +3197,7 @@ redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, /* Iterate our elements */ ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) { zend_string *zstr = zval_get_string(z_ele); - redis_cmd_append_sstr_key(&cmd, zstr->val, zstr->len, redis_sock, NULL); + redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, NULL); zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); @@ -3310,7 +3310,7 @@ redis_build_script_exists_cmd(char **ret, zval *argv, int argc) { for (i = 0; i < argc; i++) { zstr = zval_get_string(&argv[i]); - redis_cmd_append_sstr(&cmd, zstr->val, zstr->len); + redis_cmd_append_sstr(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } diff --git a/redis_array.c b/redis_array.c index d88bd2d073..bdb65cadf4 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1054,8 +1054,8 @@ PHP_METHOD(RedisArray, mset) ZEND_HASH_FOREACH_KEY_VAL(h_keys, idx, zkey, data) { /* If the key isn't a string, make a string representation of it */ if (zkey) { - key_len = zkey->len; - key = zkey->val; + key_len = ZSTR_LEN(zkey); + key = ZSTR_VAL(zkey); } else { key_len = snprintf(kbuf, sizeof(kbuf), "%lu", idx); key = kbuf; diff --git a/redis_array_impl.c b/redis_array_impl.c index a551c9ab55..a929fa20ce 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -590,7 +590,7 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { #endif if (zkey) { - ZVAL_STRINGL(z_new, zkey->val, zkey->len); + ZVAL_STRINGL(z_new, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); } else { ZVAL_LONG(z_new, idx); } @@ -898,7 +898,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* add value */ if (zkey) { - ZVAL_STRINGL(&z_zadd_args[i+1], zkey->val, zkey->len); + ZVAL_STRINGL(&z_zadd_args[i+1], ZSTR_VAL(zkey), ZSTR_LEN(zkey)); } else { ZVAL_LONG(&z_zadd_args[i+1], (long)idx); } diff --git a/redis_cluster.c b/redis_cluster.c index ca67a57041..299eef9e8c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -589,8 +589,8 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, zend_string *zkey; switch (zend_hash_get_current_key_ex(ht, &zkey, &idx, ptr)) { case HASH_KEY_IS_STRING: - kv->key_len = zkey->len; - kv->key = zkey->val; + kv->key_len = ZSTR_LEN(zkey); + kv->key = ZSTR_VAL(zkey); #endif break; case HASH_KEY_IS_LONG: @@ -2062,7 +2062,7 @@ PHP_METHOD(RedisCluster, watch) { zstr = zval_get_string(&z_args[i]); // Add this key to our distribution handler - if (cluster_dist_add_key(c, ht_dist, zstr->val, zstr->len, NULL) == FAILURE) { + if (cluster_dist_add_key(c, ht_dist, ZSTR_VAL(zstr), ZSTR_LEN(zstr), NULL) == FAILURE) { zend_throw_exception(redis_cluster_exception_ce, "Can't issue WATCH command as the keyspace isn't fully mapped", 0 TSRMLS_CC); @@ -2216,8 +2216,8 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { /* Allow for any scalar here */ zstr = zval_get_string(z_arg); - key = zstr->val; - key_len = zstr->len; + key = ZSTR_VAL(zstr); + key_len = ZSTR_LEN(zstr); /* Hash it */ key_free = redis_key_prefix(c->flags, &key, &key_len); @@ -2333,7 +2333,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) /* Iterate, appending args */ for(i=1;ival, zstr->len); + redis_cmd_append_sstr(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } diff --git a/redis_commands.c b/redis_commands.c index 70689a9bf3..2564c0f58a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -441,9 +441,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (!zkey) continue; ZVAL_DEREF(z_ele); /* Check for withscores and limit */ - if (IS_WITHSCORES_ARG(zkey->val, zkey->len)) { + if (IS_WITHSCORES_ARG(ZSTR_VAL(zkey), ZSTR_LEN(zkey))) { *withscores = zval_is_true(z_ele); - } else if (IS_LIMIT_ARG(zkey->val, zkey->len) && Z_TYPE_P(z_ele) == IS_ARRAY) { + } else if (IS_LIMIT_ARG(ZSTR_VAL(zkey), ZSTR_LEN(zkey)) && Z_TYPE_P(z_ele) == IS_ARRAY) { HashTable *htlimit = Z_ARRVAL_P(z_ele); zval *zoff, *zcnt; @@ -556,8 +556,8 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Process input keys ZEND_HASH_FOREACH_VAL(ht_keys, z_ele) { zend_string *zstr = zval_get_string(z_ele); - char *key = zstr->val; - strlen_t key_len = zstr->len; + char *key = ZSTR_VAL(zstr); + strlen_t key_len = ZSTR_LEN(zstr); // Prefix key if necissary int key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -674,8 +674,8 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string *zstr = zval_get_string(z_chan); // Grab channel name, prefix if required - key = zstr->val; - key_len = zstr->len; + key = ZSTR_VAL(zstr); + key_len = ZSTR_LEN(zstr); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Add this channel @@ -856,7 +856,7 @@ int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw /* If we're still on a key, prefix it check slot */ if (num_keys-- > 0) { - redis_cmd_append_sstr_key(&cmdstr, zstr->val, zstr->len, redis_sock, slot); + redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot); /* If we have been passed a slot, all keys must match */ if (slot) { @@ -868,7 +868,7 @@ int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw prevslot = *slot; } } else { - redis_cmd_append_sstr(&cmdstr, zstr->val, zstr->len); + redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); } zend_string_release(zstr); @@ -911,7 +911,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Append key */ zend_string *zstr = zval_get_string(&z_args[0]); - redis_cmd_append_sstr_key(&cmdstr, zstr->val, zstr->len, redis_sock, slot); + redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot); zend_string_release(zstr); /* Add members */ @@ -1030,8 +1030,8 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if(single_array) { ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { zstr = zval_get_string(z_ele); - key = zstr->val; - key_len = zstr->len; + key = ZSTR_VAL(zstr); + key_len = ZSTR_LEN(zstr); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Protect against CROSSLOT errors @@ -1066,8 +1066,8 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, tail = has_timeout ? argc-1 : argc; for(i=0;ival; - key_len = zstr->len; + key = ZSTR_VAL(zstr); + key_len = ZSTR_LEN(zstr); key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -1148,9 +1148,9 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, v) { ZVAL_DEREF(v); /* Detect PX or EX argument and validate timeout */ - if (zkey && IS_EX_PX_ARG(zkey->val)) { + if (zkey && IS_EX_PX_ARG(ZSTR_VAL(zkey))) { /* Set expire type */ - exp_type = zkey->val; + exp_type = ZSTR_VAL(zkey); /* Try to extract timeout */ if (Z_TYPE_P(v) == IS_LONG) { @@ -1480,8 +1480,8 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If the hash key is an integer, convert it to a string if (zkey) { - mem_len = zkey->len; - mem = zkey->val; + mem_len = ZSTR_LEN(zkey); + mem = ZSTR_VAL(zkey); } else { mem_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); mem = (char*)kbuf; @@ -1600,8 +1600,8 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zstr = zval_get_string(&z_args[i]); // Grab this key and length - key = zstr->val; - key_len = zstr->len; + key = ZSTR_VAL(zstr); + key_len = ZSTR_LEN(zstr); // Prefix key, append key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -1701,8 +1701,8 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix keys, serialize values if(is_keys) { zstr = zval_get_string(z_ele); - mem = zstr->val; - mem_len = zstr->len; + mem = ZSTR_VAL(zstr); + mem_len = ZSTR_LEN(zstr); // Key prefix mem_free = redis_key_prefix(redis_sock, &mem, &mem_len); @@ -1722,8 +1722,8 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zstr = NULL; if(!mem_free) { zstr = zval_get_string(z_ele); - mem = zstr->val; - mem_len = zstr->len; + mem = ZSTR_VAL(zstr); + mem_len = ZSTR_LEN(zstr); } } @@ -1795,8 +1795,8 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_keys, z_key) { /* Turn our value into a string if it isn't one */ zstr = zval_get_string(z_key); - key = zstr->val; - key_len = zstr->len; + key = ZSTR_VAL(zstr); + key_len = ZSTR_LEN(zstr); /* Append this key to our command */ key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -1827,8 +1827,8 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Turn our key into a string if it's a different type */ zstr = zval_get_string(z_keys); - key = zstr->val; - key_len = zstr->len; + key = ZSTR_VAL(zstr); + key_len = ZSTR_LEN(zstr); key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); @@ -2325,8 +2325,8 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Get first argument (the key) as a string zstr = zval_get_string(&z_args[0]); - arg = zstr->val; - arg_len = zstr->len; + arg = ZSTR_VAL(zstr); + arg_len = ZSTR_LEN(zstr); // Prefix arg_free = redis_key_prefix(redis_sock, &arg, &arg_len); @@ -2343,7 +2343,7 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate through the members we're removing for(i=1;ival, zstr->len); + redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } @@ -2416,8 +2416,8 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Prefix our key zstr = zval_get_string(&z_args[0]); - key = zstr->val; - key_len = zstr->len; + key = ZSTR_VAL(zstr); + key_len = ZSTR_LEN(zstr); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Start command construction @@ -2544,7 +2544,7 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, ZVAL_DEREF(optval); /* If the key is numeric it's a non value option */ if (zkey) { - if (zkey->len == 5 && !strcasecmp(zkey->val, "count") && Z_TYPE_P(optval) == IS_LONG) { + if (ZSTR_LEN(zkey) == 5 && !strcasecmp(ZSTR_VAL(zkey), "count") && Z_TYPE_P(optval) == IS_LONG) { *count = Z_LVAL_P(optval); } } else { @@ -2755,8 +2755,8 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zstr = zval_get_string(z_keys); /* We may need to prefix our string */ - key = zstr->val; - keylen = zstr->len; + key = ZSTR_VAL(zstr); + keylen = ZSTR_LEN(zstr); keyfree = redis_key_prefix(redis_sock, &key, &keylen); /* Add key to migrate */ @@ -2778,8 +2778,8 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_key) { zstr = zval_get_string(z_key); - key = zstr->val; - keylen = zstr->len; + key = ZSTR_VAL(zstr); + keylen = ZSTR_LEN(zstr); keyfree = redis_key_prefix(redis_sock, &key, &keylen); /* Append the key */ @@ -2929,7 +2929,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { zend_string *zstr = zval_get_string(z_ele); - redis_cmd_append_sstr(&cmdstr, zstr->val, zstr->len); + redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); diff --git a/redis_session.c b/redis_session.c index 8a418e3dd2..fd7a5e2f53 100644 --- a/redis_session.c +++ b/redis_session.c @@ -348,7 +348,7 @@ PS_READ_FUNC(redis) #if (PHP_MAJOR_VERSION < 7) redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); #else - redis_pool_member *rpm = redis_pool_get_sock(pool, key->val TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(key) TSRMLS_CC); #endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ @@ -359,7 +359,7 @@ PS_READ_FUNC(redis) #if (PHP_MAJOR_VERSION < 7) resp = redis_session_key(rpm, key, strlen(key), &resp_len); #else - resp = redis_session_key(rpm, key->val, key->len, &resp_len); + resp = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &resp_len); #endif cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); @@ -408,7 +408,7 @@ PS_WRITE_FUNC(redis) #if (PHP_MAJOR_VERSION < 7) redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); #else - redis_pool_member *rpm = redis_pool_get_sock(pool, key->val TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(key) TSRMLS_CC); #endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ @@ -421,10 +421,10 @@ PS_WRITE_FUNC(redis) cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); #else - session = redis_session_key(rpm, key->val, key->len, &session_len); + session = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &session_len); cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, - INI_INT("session.gc_maxlifetime"), val->val, - val->len); + INI_INT("session.gc_maxlifetime"), + ZSTR_VAL(val), ZSTR_LEN(val)); #endif efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { @@ -459,7 +459,7 @@ PS_DESTROY_FUNC(redis) #if (PHP_MAJOR_VERSION < 7) redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); #else - redis_pool_member *rpm = redis_pool_get_sock(pool, key->val TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(key) TSRMLS_CC); #endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ @@ -470,7 +470,7 @@ PS_DESTROY_FUNC(redis) #if (PHP_MAJOR_VERSION < 7) session = redis_session_key(rpm, key, strlen(key), &session_len); #else - session = redis_session_key(rpm, key->val, key->len, &session_len); + session = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &session_len); #endif cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "s", session, session_len); efree(session); @@ -653,7 +653,7 @@ PS_READ_FUNC(rediscluster) { #if (PHP_MAJOR_VERSION < 7) skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); #else - skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); + skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); #endif cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "GET", "s", skey, skeylen); efree(skey); @@ -715,10 +715,10 @@ PS_WRITE_FUNC(rediscluster) { skeylen, INI_INT("session.gc_maxlifetime"), val, vallen); #else - skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); + skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "SETEX", "sds", skey, skeylen, INI_INT("session.gc_maxlifetime"), - val->val, val->len); + ZSTR_VAL(val), ZSTR_LEN(val)); #endif efree(skey); @@ -758,7 +758,7 @@ PS_DESTROY_FUNC(rediscluster) { #if (PHP_MAJOR_VERSION < 7) skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); #else - skey = cluster_session_key(c, key->val, key->len, &skeylen, &slot); + skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); #endif cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "DEL", "s", skey, skeylen); efree(skey); From 471ce0724d2e4aeffe7f326ae474be36cf8716ab Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 2 Aug 2017 17:42:34 +0300 Subject: [PATCH 0813/1986] Fix valgrind warnings --- redis_array_impl.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index a929fa20ce..ceab1884e0 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -1135,34 +1135,49 @@ ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { } /* callback with the current progress, with hostname and count */ -static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, - const char *hostname, long count, zval *z_ret TSRMLS_DC) { - - zval z_args[2]; +static void +zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, + const char *hostname, long count TSRMLS_DC) { - ZVAL_STRING(&z_args[0], hostname); - ZVAL_LONG(&z_args[1], count); + zval zv, *z_ret = &zv; + ZVAL_NULL(z_ret); #if (PHP_MAJOR_VERSION < 7) - zval *z_host = &z_args[0], *z_count = &z_args[1], - **z_args_pp[2] = { &z_host, &z_count }; + zval *z_host, *z_count, **z_args_pp[2]; + + MAKE_STD_ZVAL(z_host); + ZVAL_STRING(z_host, hostname); + z_args_pp[0] = &z_host; + + MAKE_STD_ZVAL(z_count); + ZVAL_LONG(z_count, count); + z_args_pp[1] = &z_count; + z_cb->params = z_args_pp; z_cb->retval_ptr_ptr = &z_ret; #else + zval z_args[2]; + + ZVAL_STRING(&z_args[0], hostname); + ZVAL_LONG(&z_args[1], count); + z_cb->params = z_args; z_cb->retval = z_ret; #endif - z_cb->param_count = 2; z_cb->no_separation = 0; + z_cb->param_count = 2; /* run cb(hostname, count) */ zend_call_function(z_cb, z_cb_cache TSRMLS_CC); /* cleanup */ - zval_dtor(&z_args[0]); #if (PHP_MAJOR_VERSION < 7) - zval_ptr_dtor(&z_ret); + zval_ptr_dtor(&z_host); + zval_ptr_dtor(&z_count); +#else + zval_dtor(&z_args[0]); #endif + zval_dtor(z_ret); } static void @@ -1172,7 +1187,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool char **keys; long count, i; int *key_lens, target_pos; - zval *z_target, z_ret; + zval *z_target; /* list all keys */ if(b_index) { @@ -1185,9 +1200,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool /* callback */ if(z_cb && z_cb_cache) { - ZVAL_NULL(&z_ret); - zval_rehash_callback(z_cb, z_cb_cache, hostname, count, &z_ret TSRMLS_CC); - zval_dtor(&z_ret); + zval_rehash_callback(z_cb, z_cb_cache, hostname, count TSRMLS_CC); } /* for each key, redistribute */ From e47e446f3724c52544c769394e0877e5ac52afda Mon Sep 17 00:00:00 2001 From: Mark Shehata Date: Wed, 2 Aug 2017 15:31:26 -0400 Subject: [PATCH 0814/1986] Update README.markdown --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index d2532ed546..f0da191094 100644 --- a/README.markdown +++ b/README.markdown @@ -224,7 +224,7 @@ $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay _**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. The connection will not be closed on `close` or end of request until the php process ends. -So be patient on to many open FD's (specially on redis server side) when using persistent +So be patient on too many open FD's (specially on redis server side) when using persistent connections on many servers connecting to one redis server. Also more than one persistent connection can be made identified by either host + port + timeout From 577a91909989924c841ed45f9e57a0518aa3ab30 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 4 Aug 2017 09:57:47 +0300 Subject: [PATCH 0815/1986] Remove ra_rehash_scan --- redis_array_impl.c | 106 +++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 71 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index ceab1884e0..ce2ceb19f1 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -693,52 +693,6 @@ ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len) { return !ret; } -/* list keys from array index */ -static long -ra_rehash_scan(zval *z_redis, char ***keys, int **key_lens, const char *cmd, const char *arg TSRMLS_DC) { - - long count, i; - zval z_fun_smembers, z_ret, z_arg[1], *z_data_p; - HashTable *h_keys; - char *key; - int key_len; - - /* arg */ - ZVAL_STRING(&z_arg[0], arg); - - /* run SMEMBERS */ - ZVAL_STRING(&z_fun_smembers, cmd); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_smembers, &z_ret, 1, z_arg); - zval_dtor(&z_fun_smembers); - zval_dtor(&z_arg[0]); - if(Z_TYPE(z_ret) != IS_ARRAY) { /* failure */ - zval_dtor(&z_ret); - return -1; /* TODO: log error. */ - } - h_keys = Z_ARRVAL(z_ret); - - /* allocate key array */ - count = zend_hash_num_elements(h_keys); - *keys = emalloc(count * sizeof(char*)); - *key_lens = emalloc(count * sizeof(int)); - - i = 0; - ZEND_HASH_FOREACH_VAL(h_keys, z_data_p) { - key = Z_STRVAL_P(z_data_p); - key_len = Z_STRLEN_P(z_data_p); - - /* copy key and length */ - (*keys)[i] = estrndup(key, key_len); - (*key_lens)[i] = key_len; - i++; - } ZEND_HASH_FOREACH_END(); - - /* cleanup */ - zval_dtor(&z_ret); - - return count; -} - /* run TYPE to find the type */ static zend_bool ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long *res TSRMLS_DC) { @@ -1184,42 +1138,52 @@ static void ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool b_index, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { - char **keys; - long count, i; - int *key_lens, target_pos; - zval *z_target; + HashTable *h_keys; + long count = 0; + zval z_fun, z_ret, z_argv, *z_ele; - /* list all keys */ - if(b_index) { - count = ra_rehash_scan(z_redis, &keys, &key_lens, "SMEMBERS", PHPREDIS_INDEX_NAME TSRMLS_CC); - } else { - count = ra_rehash_scan(z_redis, &keys, &key_lens, "KEYS", "*" TSRMLS_CC); - } + /* list all keys */ + if (b_index) { + ZVAL_STRING(&z_fun, "SMEMBERS"); + ZVAL_STRING(&z_argv, PHPREDIS_INDEX_NAME); + } else { + ZVAL_STRING(&z_fun, "KEYS"); + ZVAL_STRING(&z_argv, "*"); + } + ZVAL_NULL(&z_ret); + call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv); + zval_dtor(&z_argv); + zval_dtor(&z_fun); - if (count < 0) return; + if (Z_TYPE(z_ret) == IS_ARRAY) { + h_keys = Z_ARRVAL(z_ret); + count = zend_hash_num_elements(h_keys); + } + + if (!count) { + zval_dtor(&z_ret); + return; + } /* callback */ if(z_cb && z_cb_cache) { zval_rehash_callback(z_cb, z_cb_cache, hostname, count TSRMLS_CC); } - /* for each key, redistribute */ - for(i = 0; i < count; ++i) { + /* for each key, redistribute */ + ZEND_HASH_FOREACH_VAL(h_keys, z_ele) { + int pos = 0; + /* check that we're not moving to the same node. */ + zval *z_target = ra_find_node(ra, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &pos TSRMLS_CC); - /* check that we're not moving to the same node. */ - z_target = ra_find_node(ra, keys[i], key_lens[i], &target_pos TSRMLS_CC); - - if (z_target && strcmp(hostname, ra->hosts[target_pos])) { /* different host */ - /* php_printf("move [%s] from [%s] to [%s]\n", keys[i], hostname, ra->hosts[target_pos]); */ - ra_move_key(keys[i], key_lens[i], z_redis, z_target TSRMLS_CC); - } + if (z_target && strcmp(hostname, ra->hosts[pos])) { /* different host */ + ra_move_key(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), z_redis, z_target TSRMLS_CC); + } - /* cleanup */ - efree(keys[i]); - } + } ZEND_HASH_FOREACH_END(); - efree(keys); - efree(key_lens); + /* cleanup */ + zval_dtor(&z_ret); } void From 89f9c066a2b401189486f15b04ed9412bb66af45 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 4 Aug 2017 13:17:05 +0300 Subject: [PATCH 0816/1986] Set PHP_REDIS_VERSION to 'develop' --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index 4cc8c67428..895ad710f7 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "3.1.2" +#define PHP_REDIS_VERSION "develop" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From a56ed7f9f943394069019afa8a429e7aa04246b2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 4 Aug 2017 13:56:48 +0300 Subject: [PATCH 0817/1986] Change type of RedisArray pure_cmds to HashTable --- redis_array.c | 7 ++--- redis_array.h | 2 +- redis_array_impl.c | 71 +++++++++++++++++++++++----------------------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/redis_array.c b/redis_array.c index bdb65cadf4..d7da74904d 100644 --- a/redis_array.c +++ b/redis_array.c @@ -112,7 +112,8 @@ redis_array_free(RedisArray *ra) zval_dtor(&ra->z_dist); /* Delete pur commands */ - zval_dtor(&ra->z_pure_cmds); + zend_hash_destroy(ra->pure_cmds); + FREE_HASHTABLE(ra->pure_cmds); /* Free structure itself */ efree(ra); @@ -213,9 +214,7 @@ redis_array_get(zval *id TSRMLS_DC) #else obj = (redis_array_object *)((char *)Z_OBJ_P(id) - XtOffsetOf(redis_array_object, std)); #endif - if (obj->ra) { - return obj->ra; - } + return obj->ra; } return NULL; } diff --git a/redis_array.h b/redis_array.h index 0f47e6cb69..f036c2647e 100644 --- a/redis_array.h +++ b/redis_array.h @@ -48,7 +48,7 @@ typedef struct RedisArray_ { zend_bool pconnect; /* should we use pconnect */ zval z_fun; /* key extractor, callable */ zval z_dist; /* key distributor, callable */ - zval z_pure_cmds; /* hash table */ + HashTable *pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ double read_timeout; /* socket read timeout */ diff --git a/redis_array_impl.c b/redis_array_impl.c index ce2ceb19f1..f16b00405c 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -99,40 +99,41 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b void ra_init_function_table(RedisArray *ra) { - array_init(&ra->z_pure_cmds); - - add_assoc_bool(&ra->z_pure_cmds, "EXISTS", 1); - add_assoc_bool(&ra->z_pure_cmds, "GET", 1); - add_assoc_bool(&ra->z_pure_cmds, "GETBIT", 1); - add_assoc_bool(&ra->z_pure_cmds, "GETRANGE", 1); - add_assoc_bool(&ra->z_pure_cmds, "HEXISTS", 1); - add_assoc_bool(&ra->z_pure_cmds, "HGET", 1); - add_assoc_bool(&ra->z_pure_cmds, "HGETALL", 1); - add_assoc_bool(&ra->z_pure_cmds, "HKEYS", 1); - add_assoc_bool(&ra->z_pure_cmds, "HLEN", 1); - add_assoc_bool(&ra->z_pure_cmds, "HMGET", 1); - add_assoc_bool(&ra->z_pure_cmds, "HVALS", 1); - add_assoc_bool(&ra->z_pure_cmds, "LINDEX", 1); - add_assoc_bool(&ra->z_pure_cmds, "LLEN", 1); - add_assoc_bool(&ra->z_pure_cmds, "LRANGE", 1); - add_assoc_bool(&ra->z_pure_cmds, "OBJECT", 1); - add_assoc_bool(&ra->z_pure_cmds, "SCARD", 1); - add_assoc_bool(&ra->z_pure_cmds, "SDIFF", 1); - add_assoc_bool(&ra->z_pure_cmds, "SINTER", 1); - add_assoc_bool(&ra->z_pure_cmds, "SISMEMBER", 1); - add_assoc_bool(&ra->z_pure_cmds, "SMEMBERS", 1); - add_assoc_bool(&ra->z_pure_cmds, "SRANDMEMBER", 1); - add_assoc_bool(&ra->z_pure_cmds, "STRLEN", 1); - add_assoc_bool(&ra->z_pure_cmds, "SUNION", 1); - add_assoc_bool(&ra->z_pure_cmds, "TYPE", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZCARD", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZCOUNT", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZRANGE", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZRANK", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZREVRANGE", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZREVRANGEBYSCORE", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZREVRANK", 1); - add_assoc_bool(&ra->z_pure_cmds, "ZSCORE", 1); + ALLOC_HASHTABLE(ra->pure_cmds); + zend_hash_init(ra->pure_cmds, 0, NULL, NULL, 0); + + zend_hash_str_update_ptr(ra->pure_cmds, "EXISTS", sizeof("EXISTS") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "GET", sizeof("GET") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "GETBIT", sizeof("GETBIT") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "GETRANGE", sizeof("GETRANGE") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "HEXISTS", sizeof("HEXISTS") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "HGET", sizeof("HGET") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "HGETALL", sizeof("HGETALL") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "HKEYS", sizeof("HKEYS") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "HLEN", sizeof("HLEN") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "HMGET", sizeof("HMGET") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "HVALS", sizeof("HVALS") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "LINDEX", sizeof("LINDEX") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "LLEN", sizeof("LLEN") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "LRANGE", sizeof("LRANGE") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "OBJECT", sizeof("OBJECT") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "SCARD", sizeof("SCARD") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "SDIFF", sizeof("SDIFF") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "SINTER", sizeof("SINTER") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "SISMEMBER", sizeof("SISMEMBER") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "SMEMBERS", sizeof("SMEMBERS") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "SRANDMEMBER", sizeof("SRANDMEMBER") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "STRLEN", sizeof("STRLEN") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "SUNION", sizeof("SUNION") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "TYPE", sizeof("TYPE") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "ZCARD", sizeof("ZCARD") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "ZCOUNT", sizeof("ZCOUNT") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "ZRANGE", sizeof("ZRANGE") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "ZRANK", sizeof("ZRANK") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANGE", sizeof("ZREVRANGE") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANGEBYSCORE", sizeof("ZREVRANGEBYSCORE") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANK", sizeof("ZREVRANK") - 1, NULL); + zend_hash_str_update_ptr(ra->pure_cmds, "ZSCORE", sizeof("ZSCORE") - 1, NULL); } static int @@ -687,7 +688,7 @@ ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len) { cmd_up[i] = toupper(cmd[i]); cmd_up[cmd_len] = 0; - ret = zend_hash_str_exists(Z_ARRVAL(ra->z_pure_cmds), cmd_up, cmd_len); + ret = zend_hash_str_exists(ra->pure_cmds, cmd_up, cmd_len); efree(cmd_up); return !ret; From 5874b0775b338a4e57c0f850fd3e25a19735fd6a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 20 Feb 2017 22:51:16 +0200 Subject: [PATCH 0818/1986] Allow mixing multi and pipeline modes --- common.h | 32 ++++++++------ redis.c | 102 ++++++++++++++++++++++++++++++++------------ tests/RedisTest.php | 21 +++++++++ 3 files changed, 114 insertions(+), 41 deletions(-) diff --git a/common.h b/common.h index ed47b92db2..4322dedd38 100644 --- a/common.h +++ b/common.h @@ -468,12 +468,17 @@ typedef enum _PUBSUB_TYPE { #define BITOP_MIN_OFFSET 0 #define BITOP_MAX_OFFSET 4294967295U +/* Transaction modes */ +#define ATOMIC 0 +#define MULTI 1 +#define PIPELINE 2 + #define IF_ATOMIC() if (redis_sock->mode == ATOMIC) #define IF_NOT_ATOMIC() if (redis_sock->mode != ATOMIC) -#define IF_MULTI() if (redis_sock->mode == MULTI) -#define IF_NOT_MULTI() if (redis_sock->mode != MULTI) -#define IF_PIPELINE() if (redis_sock->mode == PIPELINE) -#define IF_NOT_PIPELINE() if (redis_sock->mode != PIPELINE) +#define IF_MULTI() if (redis_sock->mode & MULTI) +#define IF_NOT_MULTI() if (!(redis_sock->mode & MULTI)) +#define IF_PIPELINE() if (redis_sock->mode & PIPELINE) +#define IF_NOT_PIPELINE() if (!(redis_sock->mode & PIPELINE)) #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \ if (redis_sock->pipeline_cmd == NULL) { \ @@ -494,14 +499,14 @@ typedef enum _PUBSUB_TYPE { } #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \ - fold_item *f1 = malloc(sizeof(fold_item)); \ - f1->fun = (void *)callback; \ - f1->ctx = closure_context; \ - f1->next = NULL; \ + fold_item *fi = malloc(sizeof(fold_item)); \ + fi->fun = (void *)callback; \ + fi->ctx = closure_context; \ + fi->next = NULL; \ if (redis_sock->current) { \ - redis_sock->current->next = f1; \ + redis_sock->current->next = fi; \ } \ - redis_sock->current = f1; \ + redis_sock->current = fi; \ if (NULL == redis_sock->head) { \ redis_sock->head = redis_sock->current; \ } \ @@ -516,7 +521,7 @@ typedef enum _PUBSUB_TYPE { efree(cmd); #define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ - IF_MULTI() { \ + IF_NOT_PIPELINE() { \ if (redis_response_enqueued(redis_sock TSRMLS_CC) != SUCCESS) { \ RETURN_FALSE; \ } \ @@ -592,7 +597,8 @@ typedef enum _PUBSUB_TYPE { #define IS_LEX_ARG(s,l) \ (l>0 && (*s=='(' || *s=='[' || (l==1 && (*s=='+' || *s=='-')))) -typedef enum {ATOMIC, MULTI, PIPELINE} redis_mode; +#define REDIS_ENABLE_MODE(redis_sock, m) (redis_sock->mode |= m) +#define REDIS_DISABLE_MODE(redis_sock, m) (redis_sock->mode &= ~m) typedef struct fold_item { zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, void *, ...); @@ -621,7 +627,7 @@ typedef struct { char *prefix; int prefix_len; - redis_mode mode; + short mode; fold_item *head; fold_item *current; diff --git a/redis.c b/redis.c index 9d6d535f2b..082ec35b93 100644 --- a/redis.c +++ b/redis.c @@ -1242,9 +1242,11 @@ PHP_METHOD(Redis,__destruct) { // If we think we're in MULTI mode, send a discard IF_MULTI() { - // Discard any multi commands, and free any callbacks that have been - // queued - redis_send_discard(redis_sock TSRMLS_CC); + IF_NOT_PIPELINE() { + // Discard any multi commands, and free any callbacks that have been + // queued + redis_send_discard(redis_sock TSRMLS_CC); + } free_reply_callbacks(redis_sock); } } @@ -2617,27 +2619,30 @@ PHP_METHOD(Redis, multi) RETURN_FALSE; } else { free_reply_callbacks(redis_sock); - redis_sock->mode = PIPELINE; + REDIS_ENABLE_MODE(redis_sock, PIPELINE); } } else if (multi_value == MULTI) { IF_MULTI() { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Already in multi mode"); - } else IF_PIPELINE() { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate multi in pipeline mode!"); - RETURN_FALSE; } else { cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", ""); - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) - efree(cmd); - - if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } else if (strncmp(resp, "+OK", 3) != 0) { + IF_PIPELINE() { + PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); + efree(cmd); + REDIS_SAVE_CALLBACK(NULL, NULL); + REDIS_ENABLE_MODE(redis_sock, MULTI); + } else { + SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) + efree(cmd); + if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } else if (strncmp(resp, "+OK", 3) != 0) { + efree(resp); + RETURN_FALSE; + } efree(resp); - RETURN_FALSE; + REDIS_ENABLE_MODE(redis_sock, MULTI); } - efree(resp); - redis_sock->mode = MULTI; } } else { RETURN_FALSE; @@ -2711,13 +2716,20 @@ PHP_METHOD(Redis, exec) IF_MULTI() { cmd_len = REDIS_SPPRINTF(&cmd, "EXEC", ""); + IF_PIPELINE() { + PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); + efree(cmd); + REDIS_SAVE_CALLBACK(NULL, NULL); + REDIS_DISABLE_MODE(redis_sock, MULTI); + RETURN_ZVAL(getThis(), 1, 0); + } SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); ret = redis_sock_read_multibulk_multi_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); free_reply_callbacks(redis_sock); - redis_sock->mode = ATOMIC; + REDIS_DISABLE_MODE(redis_sock, MULTI); redis_sock->watching = 0; if (ret < 0) { zval_dtor(return_value); @@ -2743,7 +2755,7 @@ PHP_METHOD(Redis, exec) redis_sock->pipeline_len = 0; } free_reply_callbacks(redis_sock); - redis_sock->mode = ATOMIC; + REDIS_DISABLE_MODE(redis_sock, PIPELINE); } } @@ -2770,8 +2782,35 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, fold_item *fi; for (fi = redis_sock->head; fi; fi = fi->next) { - fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, - fi->ctx TSRMLS_CC); + if (fi->fun) { + fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, + fi->ctx TSRMLS_CC); + continue; + } + size_t len; + char inbuf[255]; + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + } else if (strncmp(inbuf, "+OK", 3) != 0) { + } + while ((fi = fi->next) && fi->fun) { + if (redis_response_enqueued(redis_sock TSRMLS_CC) == SUCCESS) { + } else { + } + } + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + } +#if (PHP_MAJOR_VERSION < 7) + zval *z_ret; + MAKE_STD_ZVAL(z_ret); +#else + zval zv, *z_ret = &zv; +#endif + array_init(z_ret); + add_next_index_zval(z_tab, z_ret); + + int num = atol(inbuf + 1); + if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, z_ret TSRMLS_CC) < 0) { + } } redis_sock->current = fi; return 0; @@ -2789,19 +2828,20 @@ PHP_METHOD(Redis, pipeline) RETURN_FALSE; } - IF_MULTI() { - php_error_docref(NULL TSRMLS_CC, E_ERROR, - "Can't activate pipeline in multi mode!"); - RETURN_FALSE; - } else IF_PIPELINE() { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + IF_PIPELINE() { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Already in pipeline mode"); } else { + IF_MULTI() { + php_error_docref(NULL TSRMLS_CC, E_ERROR, + "Can't activate pipeline in multi mode!"); + RETURN_FALSE; + } /* NB : we keep the function fold, to detect the last function. * We need the response format of the n - 1 command. So, we can delete * when n > 2, the { 1 .. n - 2} commands */ free_reply_callbacks(redis_sock); - redis_sock->mode = PIPELINE; + REDIS_ENABLE_MODE(redis_sock, PIPELINE); } RETURN_ZVAL(getThis(), 1, 0); } @@ -3521,7 +3561,13 @@ PHP_METHOD(Redis, getMode) { RETURN_FALSE; } - RETVAL_LONG(redis_sock->mode); + IF_PIPELINE() { + RETVAL_LONG(PIPELINE); + } else IF_MULTI() { + RETVAL_LONG(MULTI); + } else { + RETVAL_LONG(ATOMIC); + } } /* {{{ proto Redis::time() */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index a518a57eea..2a15f0a256 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2533,6 +2533,27 @@ public function testPipeline() { $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(1, count($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(5, count($ret)); // should be 5 atomic operations + } + protected function sequence($mode) { $ret = $this->redis->multi($mode) ->set('x', 42) From 2c8de47f78af8e354bf023ad9a7fb23579e8ba7b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 9 Aug 2017 16:30:18 +0300 Subject: [PATCH 0819/1986] Use ZEND_HASH_FOREACH_VAL in ra_load_hosts. --- redis_array_impl.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index f16b00405c..5669815769 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -42,11 +42,8 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b ZVAL_STRINGL(&z_cons, "__construct", 11); /* init connections */ - for (zend_hash_internal_pointer_reset(hosts); - zend_hash_has_more_elements(hosts) == SUCCESS; - zend_hash_move_forward(hosts) - ) { - if ((zpData = zend_hash_get_current_data(hosts)) == NULL || Z_TYPE_P(zpData) != IS_STRING) { + ZEND_HASH_FOREACH_VAL(hosts, zpData) { + if (Z_TYPE_P(zpData) != IS_STRING) { zval_dtor(&z_cons); return NULL; } @@ -88,7 +85,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b } ra->count = ++i; - } + } ZEND_HASH_FOREACH_END(); zval_dtor(&z_cons); From 142b51ded98a7f899bc0c5a93b22a5c04a466642 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 11 Aug 2017 16:20:40 +0300 Subject: [PATCH 0820/1986] refactoring Small change php5 implementation of ZEND_HASH_FOREACH_* macroses. Use ZEND_HASH_FOREACH_VAL in ra_get_key_type. Allocate array via MAKE_STD_ZVAL in HANDLE_MULTI_EXEC macro. --- common.h | 10 ++++++---- redis.c | 7 ------- redis_array.c | 18 ++++++++---------- redis_array_impl.c | 38 +++++++++++++++++--------------------- 4 files changed, 31 insertions(+), 42 deletions(-) diff --git a/common.h b/common.h index 4322dedd38..1b60da1115 100644 --- a/common.h +++ b/common.h @@ -35,11 +35,12 @@ typedef struct { #define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) do { \ HashPosition _hpos; \ for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - (_val = zend_hash_get_current_data_ex(ht, &_hpos)) != NULL; \ + zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ zend_hash_move_forward_ex(ht, &_hpos) \ ) { \ zend_string _zstr = {0}; \ char *_str_index; uint _str_length; ulong _num_index; \ + _val = zend_hash_get_current_data_ex(ht, &_hpos); \ switch (zend_hash_get_current_key_ex(ht, &_str_index, &_str_length, &_num_index, 0, &_hpos)) { \ case HASH_KEY_IS_STRING: \ _zstr.len = _str_length - 1; \ @@ -51,15 +52,16 @@ typedef struct { _h = _num_index; \ break; \ default: \ - continue; \ + /* noop */ break; \ } #define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ HashPosition _hpos; \ for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - (_val = zend_hash_get_current_data_ex(ht, &_hpos)) != NULL; \ + zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ zend_hash_move_forward_ex(ht, &_hpos) \ - ) { + ) { \ + _val = zend_hash_get_current_data_ex(ht, &_hpos); \ #define ZEND_HASH_FOREACH_END() \ } \ diff --git a/redis.c b/redis.c index 082ec35b93..e659aa990e 100644 --- a/redis.c +++ b/redis.c @@ -1270,13 +1270,6 @@ PHP_METHOD(Redis, pconnect) if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1) == FAILURE) { RETURN_FALSE; } else { - /* FIXME: should we remove whole `else` block? */ - /* reset multi/exec state if there is one. */ - RedisSock *redis_sock; - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { - RETURN_FALSE; - } - RETURN_TRUE; } } diff --git a/redis_array.c b/redis_array.c index d7da74904d..311b34bcc1 100644 --- a/redis_array.c +++ b/redis_array.c @@ -814,25 +814,23 @@ PHP_METHOD(RedisArray, select) #define HANDLE_MULTI_EXEC(ra, cmd) do { \ if (ra && ra->z_multi_exec) { \ int i, num_varargs;\ - zval ***varargs = NULL;\ - zval z_arg_array;\ + zval ***varargs = NULL, *z_arg_array; \ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O*",\ &object, redis_array_ce, &varargs, &num_varargs) == FAILURE) {\ RETURN_FALSE;\ }\ /* copy all args into a zval hash table */\ - array_init(&z_arg_array);\ + MAKE_STD_ZVAL(z_arg_array); \ + array_init(z_arg_array);\ for(i = 0; i < num_varargs; ++i) {\ zval *z_tmp;\ - MAKE_STD_ZVAL(z_tmp);\ - *z_tmp = **varargs[i];\ - zval_copy_ctor(z_tmp);\ - INIT_PZVAL(z_tmp);\ - add_next_index_zval(&z_arg_array, z_tmp);\ + MAKE_STD_ZVAL(z_tmp); \ + ZVAL_ZVAL(z_tmp, *varargs[i], 1, 0); \ + add_next_index_zval(z_arg_array, z_tmp); \ }\ /* call */\ - ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd)-1, &z_arg_array, NULL);\ - zval_dtor(&z_arg_array);\ + ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, z_arg_array, NULL); \ + zval_ptr_dtor(&z_arg_array); \ if(varargs) {\ efree(varargs);\ }\ diff --git a/redis_array_impl.c b/redis_array_impl.c index 5669815769..9ccfffe1a0 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -583,9 +583,8 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { zval zv, *z_new = &zv; #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_new); -#else - PHPREDIS_NOTUSED(z_val); #endif + PHPREDIS_NOTUSED(z_val); if (zkey) { ZVAL_STRINGL(z_new, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); @@ -695,46 +694,43 @@ ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len) { static zend_bool ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long *res TSRMLS_DC) { - int i; - zval z_fun_type, z_ret, z_arg[1]; - zval *z_data; + int i = 0; + zval z_fun, z_ret, z_arg, *z_data; long success = 1; /* Pipelined */ ra_index_multi(z_from, PIPELINE TSRMLS_CC); /* prepare args */ - ZVAL_STRINGL(&z_arg[0], key, key_len); + ZVAL_STRINGL(&z_arg, key, key_len); /* run TYPE */ - ZVAL_STRINGL(&z_fun_type, "TYPE", 4); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); - zval_dtor(&z_fun_type); + ZVAL_NULL(&z_ret); + ZVAL_STRINGL(&z_fun, "TYPE", 4); + call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); + zval_dtor(&z_fun); zval_dtor(&z_ret); /* run TYPE */ - ZVAL_STRINGL(&z_fun_type, "TTL", 3); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_type, &z_ret, 1, z_arg); - zval_dtor(&z_fun_type); + ZVAL_NULL(&z_ret); + ZVAL_STRINGL(&z_fun, "TTL", 3); + call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); + zval_dtor(&z_fun); zval_dtor(&z_ret); /* Get the result from the pipeline. */ ra_index_exec(z_from, &z_ret, 1 TSRMLS_CC); - if(Z_TYPE(z_ret) == IS_ARRAY) { - HashTable *retHash = Z_ARRVAL(z_ret); - for(i = 0, zend_hash_internal_pointer_reset(retHash); - zend_hash_has_more_elements(retHash) == SUCCESS; - zend_hash_move_forward(retHash)) { - - if ((z_data = zend_hash_get_current_data(retHash)) == NULL || Z_TYPE_P(z_data) != IS_LONG) { + if (Z_TYPE(z_ret) == IS_ARRAY) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_ret), z_data) { + if (z_data == NULL || Z_TYPE_P(z_data) != IS_LONG) { success = 0; break; } /* Get the result - Might change in the future to handle doubles as well */ res[i++] = Z_LVAL_P(z_data); - } + } ZEND_HASH_FOREACH_END(); } - zval_dtor(&z_arg[0]); + zval_dtor(&z_arg); zval_dtor(&z_ret); return success; } From 4452f6858b3310aeb8bab2d0011117ba63d68e8f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 11 Aug 2017 16:45:21 +0300 Subject: [PATCH 0821/1986] Use ZEND_HASH_FOREACH_VAL in redis_sort_cmd --- redis_commands.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 2564c0f58a..f5c51ade3c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2191,17 +2191,12 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, add_next_index_stringl(&z_argv, "GET", sizeof("GET") - 1); add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { - HashTable *ht_keys = Z_ARRVAL_P(z_ele); int added=0; + zval *z_key; - for(zend_hash_internal_pointer_reset(ht_keys); - zend_hash_has_more_elements(ht_keys)==SUCCESS; - zend_hash_move_forward(ht_keys)) - { - zval *z_key; - + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_ele), z_key) { // If we can't get the data, or it's not a string, skip - if ((z_key = zend_hash_get_current_data(ht_keys)) == NULL || Z_TYPE_P(z_key) != IS_STRING) { + if (z_key == NULL || Z_TYPE_P(z_key) != IS_STRING) { continue; } /* Add get per thing we're getting */ @@ -2210,7 +2205,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Add this key to our argv array add_next_index_stringl(&z_argv, Z_STRVAL_P(z_key), Z_STRLEN_P(z_key)); added++; - } + } ZEND_HASH_FOREACH_END(); // Make sure we were able to add at least one if(added==0) { From e672f40bda2fbdc50b82977665c14e595c0fc990 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 11 Aug 2017 23:50:29 +0300 Subject: [PATCH 0822/1986] Duplicate incoming params via ZVAL_ZVAL in ra_forward_call --- redis_array.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/redis_array.c b/redis_array.c index 311b34bcc1..7d3af374ab 100644 --- a/redis_array.c +++ b/redis_array.c @@ -374,12 +374,12 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* pass call through */ ZVAL_STRINGL(&z_fun, cmd, cmd_len); /* method name */ - z_callargs = ecalloc(argc + 1, sizeof(zval)); + z_callargs = ecalloc(argc, sizeof(zval)); /* copy args to array */ i = 0; ZEND_HASH_FOREACH_VAL(h_args, zp_tmp) { - z_callargs[i] = *zp_tmp; + ZVAL_ZVAL(&z_callargs[i], zp_tmp, 1, 0); i++; } ZEND_HASH_FOREACH_END(); @@ -388,6 +388,9 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); zval_dtor(return_value); zval_dtor(&z_fun); + for (i = 0; i < argc; ++i) { + zval_dtor(&z_callargs[i]); + } efree(z_callargs); RETURN_ZVAL(getThis(), 1, 0); } @@ -430,6 +433,9 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* cleanup */ zval_dtor(&z_fun); + for (i = 0; i < argc; ++i) { + zval_dtor(&z_callargs[i]); + } efree(z_callargs); } From d11798e18b3cee01135c867ae30aade59cf1a866 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 14 Aug 2017 12:36:34 -0700 Subject: [PATCH 0823/1986] * Only attempt to set TCP_NODELAY on TCP sockets and also don't abort if we can't. * Stop throwing warnings if users double up on pipeline or multi calls reverting to previous behavior. --- library.c | 9 +++++---- redis.c | 36 ++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/library.c b/library.c index 62312c0ce7..4cee8d163a 100644 --- a/library.c +++ b/library.c @@ -1411,7 +1411,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) struct timeval tv, read_tv, *tv_ptr = NULL; char host[1024], *persistent_id = NULL; const char *fmtstr = "%s:%d"; - int host_len, err = 0; + int host_len, usocket = 0, err = 0; php_netstream_data_t *sock; int tcp_flag = 1; @@ -1430,6 +1430,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if(redis_sock->host[0] == '/' && redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", redis_sock->host); + usocket = 1; } else { if(redis_sock->port == 0) redis_sock->port = 6379; @@ -1466,10 +1467,10 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) return -1; } - /* set TCP_NODELAY */ + /* Attempt to set TCP_NODELAY if we're not using a unix socket */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; - if (setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)) < 0) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate TCP_NODELAY option!"); + if (!usocket) { + setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); } php_stream_auto_cleanup(redis_sock->stream); diff --git a/redis.c b/redis.c index e659aa990e..6db327e82f 100644 --- a/redis.c +++ b/redis.c @@ -2605,19 +2605,20 @@ PHP_METHOD(Redis, multi) } if (multi_value == PIPELINE) { - IF_PIPELINE() { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Already in pipeline mode"); - } else IF_MULTI() { + /* Cannot enter pipeline mode in a MULTI block */ + IF_MULTI() { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate pipeline in multi mode!"); RETURN_FALSE; - } else { + } + + /* Enable PIPELINE if we're not already in one */ + IF_ATOMIC() { free_reply_callbacks(redis_sock); REDIS_ENABLE_MODE(redis_sock, PIPELINE); } } else if (multi_value == MULTI) { - IF_MULTI() { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Already in multi mode"); - } else { + /* Don't want to do anything if we're alredy in MULTI mode */ + IF_NOT_MULTI() { cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", ""); IF_PIPELINE() { PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); @@ -2638,8 +2639,10 @@ PHP_METHOD(Redis, multi) } } } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown mode sent to Redis::multi"); RETURN_FALSE; } + RETURN_ZVAL(getThis(), 1, 0); } @@ -2821,21 +2824,22 @@ PHP_METHOD(Redis, pipeline) RETURN_FALSE; } - IF_PIPELINE() { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Already in pipeline mode"); - } else { - IF_MULTI() { - php_error_docref(NULL TSRMLS_CC, E_ERROR, - "Can't activate pipeline in multi mode!"); - RETURN_FALSE; - } + /* User cannot enter MULTI mode if already in a pipeline */ + IF_MULTI() { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate pipeline in multi mode!"); + RETURN_FALSE; + } + + /* Enable pipeline mode unless we're already in that mode in which case this + * is just a NO OP */ + IF_ATOMIC() { /* NB : we keep the function fold, to detect the last function. * We need the response format of the n - 1 command. So, we can delete * when n > 2, the { 1 .. n - 2} commands */ free_reply_callbacks(redis_sock); REDIS_ENABLE_MODE(redis_sock, PIPELINE); } + RETURN_ZVAL(getThis(), 1, 0); } From 77aebadc73a0b4989f0a7f6472e11c2b36971327 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 14 Aug 2017 15:06:57 -0700 Subject: [PATCH 0824/1986] Tests for doubling up on pipeline/multi calls --- tests/RedisClusterTest.php | 1 + tests/RedisTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 36f65f84a6..d03d3591dd 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -33,6 +33,7 @@ public function testWait() { return $this->markTestSkipped(); } public function testSelect() { return $this->markTestSkipped(); } public function testReconnectSelect() { return $this->markTestSkipped(); } public function testMultipleConnect() { return $this->markTestSkipped(); } + public function testDoublePipeNoOp() { return $this->markTestSkipped(); } /* Load our seeds on construction */ public function __construct() { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 2a15f0a256..24f01d79a2 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2554,6 +2554,31 @@ public function testPipelineMultiExec() $this->assertEquals(5, count($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(Array(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(Array(true, 'over9000'), $data); + } + protected function sequence($mode) { $ret = $this->redis->multi($mode) ->set('x', 42) From 658ee37410310a21ab972f175ec9aa088787c23e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 15 Aug 2017 11:32:16 +0300 Subject: [PATCH 0825/1986] ZEND_HASH_FOREACH_PTR --- cluster_library.c | 57 ++++++++++++++++------------------------------- common.h | 13 +++++++++-- redis_cluster.c | 27 +++++++++------------- redis_cluster.h | 8 +++---- 4 files changed, 44 insertions(+), 61 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 987142e81e..7f6921ed8d 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -12,11 +12,8 @@ extern zend_class_entry *redis_cluster_exception_ce; static void cluster_dump_nodes(redisCluster *c) { redisClusterNode *p; - for(zend_hash_internal_pointer_reset(c->nodes); - zend_hash_has_more_elements(c->nodes)==SUCCESS; - zend_hash_move_forward(c->nodes)) - { - if ((p = zend_hash_get_current_data_ptr(c->nodes)) == NULL) { + ZEND_HASH_FOREACH_PTR(c->nodes, p) { + if (p == NULL) { continue; } @@ -25,7 +22,7 @@ static void cluster_dump_nodes(redisCluster *c) { p->slot); php_printf("\n"); - } + } ZEND_HASH_FOREACH_END(); } static void cluster_log(char *fmt, ...) @@ -939,30 +936,24 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { int mapped=0; // Iterate over seeds until we can get slots - for(zend_hash_internal_pointer_reset(c->seeds); - !mapped && zend_hash_has_more_elements(c->seeds) == SUCCESS; - zend_hash_move_forward(c->seeds)) - { - // Grab the redis_sock for this seed - if ((seed = zend_hash_get_current_data_ptr(c->seeds)) == NULL) { - continue; - } - + ZEND_HASH_FOREACH_PTR(c->seeds, seed) { // Attempt to connect to this seed node - if (redis_sock_connect(seed TSRMLS_CC) != 0) { + if (seed == NULL || redis_sock_connect(seed TSRMLS_CC) != 0) { continue; } // Parse out cluster nodes. Flag mapped if we are valid slots = cluster_get_slots(seed TSRMLS_CC); - if(slots) mapped = !cluster_map_slots(c, slots); - - // Bin anything mapped, if we failed somewhere - if(!mapped && slots) { - memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); + if (slots) { + mapped = !cluster_map_slots(c, slots); + // Bin anything mapped, if we failed somewhere + if (!mapped) { + memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); + } } redis_sock_disconnect(seed TSRMLS_CC); - } + if (mapped) break; + } ZEND_HASH_FOREACH_END(); // Clean up slots reply if we got one if(slots) cluster_free_reply(slots, 1); @@ -1081,13 +1072,11 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC) { redisClusterNode *node; - for(zend_hash_internal_pointer_reset(c->nodes); - (node = zend_hash_get_current_data_ptr(c->nodes)) != NULL; - zend_hash_move_forward(c->nodes)) - { + ZEND_HASH_FOREACH_PTR(c->nodes, node) { + if (node == NULL) break; redis_sock_disconnect(node->sock TSRMLS_CC); node->sock->lazy_connect = 1; - } + } ZEND_HASH_FOREACH_END(); } /* This method attempts to write our command at random to the master and any @@ -1219,17 +1208,9 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, if(direct) return -1; /* Fall back by attempting the request against every known node */ - for(zend_hash_internal_pointer_reset(c->nodes); - zend_hash_has_more_elements(c->nodes)==SUCCESS; - zend_hash_move_forward(c->nodes)) - { - /* Grab node */ - if ((seed_node = zend_hash_get_current_data_ptr(c->nodes)) == NULL) { - continue; - } - + ZEND_HASH_FOREACH_PTR(c->nodes, seed_node) { /* Skip this node if it's the one that failed, or if it's a slave */ - if(seed_node->sock == redis_sock || seed_node->slave) continue; + if (seed_node == NULL || seed_node->sock == redis_sock || seed_node->slave) continue; /* Connect to this node if we haven't already */ CLUSTER_LAZY_CONNECT(seed_node->sock); @@ -1240,7 +1221,7 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, c->cmd_sock = seed_node->sock; return 0; } - } + } ZEND_HASH_FOREACH_END(); /* We were unable to write to any node in our cluster */ return -1; diff --git a/common.h b/common.h index 1b60da1115..51f4055c7d 100644 --- a/common.h +++ b/common.h @@ -63,6 +63,14 @@ typedef struct { ) { \ _val = zend_hash_get_current_data_ex(ht, &_hpos); \ +#define ZEND_HASH_FOREACH_PTR(ht, _ptr) do { \ + HashPosition _hpos; \ + for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ + zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ + zend_hash_move_forward_ex(ht, &_hpos) \ + ) { \ + _ptr = zend_hash_get_current_data_ptr_ex(ht, &_hpos); \ + #define ZEND_HASH_FOREACH_END() \ } \ } while(0) @@ -126,15 +134,16 @@ zend_hash_get_current_data(HashTable *ht) } static zend_always_inline void * -zend_hash_get_current_data_ptr(HashTable *ht) +zend_hash_get_current_data_ptr_ex(HashTable *ht, HashPosition *pos) { void **ptr; - if (zend_hash_get_current_data_ex(ht, (void **)&ptr, NULL) == SUCCESS) { + if (zend_hash_get_current_data_ex(ht, (void **)&ptr, pos) == SUCCESS) { return *ptr; } return NULL; } +#define zend_hash_get_current_data_ptr(ht) zend_hash_get_current_data_ptr_ex(ht, NULL) static int (*_zend_hash_index_find)(const HashTable *, ulong, void **) = &zend_hash_index_find; #define zend_hash_index_find(ht, h) inline_zend_hash_index_find(ht, h) diff --git a/redis_cluster.c b/redis_cluster.c index 299eef9e8c..be3097e6af 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1037,10 +1037,8 @@ PHP_METHOD(RedisCluster, keys) { c->readonly = CLUSTER_IS_ATOMIC(c); /* Iterate over our known nodes */ - for(zend_hash_internal_pointer_reset(c->nodes); - (node = zend_hash_get_current_data_ptr(c->nodes)) != NULL; - zend_hash_move_forward(c->nodes)) - { + ZEND_HASH_FOREACH_PTR(c->nodes, node) { + if (node == NULL) break; if (cluster_send_slot(c, node->slot, cmd, cmd_len, TYPE_MULTIBULK TSRMLS_CC)<0) { @@ -1072,7 +1070,7 @@ PHP_METHOD(RedisCluster, keys) { /* Free response, don't free data */ cluster_free_reply(resp, 0); - } + } ZEND_HASH_FOREACH_END(); efree(cmd); @@ -1969,10 +1967,8 @@ PHP_METHOD(RedisCluster, _masters) { array_init(z_ret); - for(zend_hash_internal_pointer_reset(c->nodes); - (node = zend_hash_get_current_data_ptr(c->nodes)) != NULL; - zend_hash_move_forward(c->nodes)) - { + ZEND_HASH_FOREACH_PTR(c->nodes, node) { + if (node == NULL) break; host = node->sock->host; port = node->sock->port; @@ -1985,7 +1981,7 @@ PHP_METHOD(RedisCluster, _masters) { add_next_index_stringl(z_sub, host, strlen(host)); add_next_index_long(z_sub, port); add_next_index_zval(z_ret, z_sub); - } + } ZEND_HASH_FOREACH_END(); RETVAL_ZVAL(z_ret, 1, 0); } @@ -2073,18 +2069,17 @@ PHP_METHOD(RedisCluster, watch) { } // Iterate over each node we'll be sending commands to - for(zend_hash_internal_pointer_reset(ht_dist); - zend_hash_get_current_key(ht_dist, NULL, &slot) == HASH_KEY_IS_LONG; - zend_hash_move_forward(ht_dist)) - { + ZEND_HASH_FOREACH_PTR(ht_dist, dl) { // Grab the clusterDistList pointer itself - if ((dl = zend_hash_get_current_data_ptr(ht_dist)) == NULL) { + if (dl == NULL) { zend_throw_exception(redis_cluster_exception_ce, "Internal error in a PHP HashTable", 0 TSRMLS_CC); cluster_dist_free(ht_dist); efree(z_args); efree(cmd.c); RETURN_FALSE; + } else if (zend_hash_get_current_key(ht_dist, NULL, &slot) != HASH_KEY_IS_LONG) { + break; } // Construct our watch command for this node @@ -2104,7 +2099,7 @@ PHP_METHOD(RedisCluster, watch) { // Zero out our command buffer cmd.len = 0; - } + } ZEND_HASH_FOREACH_END(); // Cleanup cluster_dist_free(ht_dist); diff --git a/redis_cluster.h b/redis_cluster.h index 6b4bb0d80b..5ecd820419 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -53,13 +53,11 @@ /* Reset anything flagged as MULTI */ #define CLUSTER_RESET_MULTI(c) \ redisClusterNode *_node; \ - for(zend_hash_internal_pointer_reset(c->nodes); \ - (_node = zend_hash_get_current_data_ptr(c->nodes)) != NULL; \ - zend_hash_move_forward(c->nodes)) \ - { \ + ZEND_HASH_FOREACH_PTR(c->nodes, _node) { \ + if (_node == NULL) break; \ _node->sock->watching = 0; \ _node->sock->mode = ATOMIC; \ - } \ + } ZEND_HASH_FOREACH_END(); \ c->flags->watching = 0; \ c->flags->mode = ATOMIC; \ From 42f1c9779b969c76cec9c780a4cdf6b36e1eab50 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 15 Aug 2017 14:35:02 +0300 Subject: [PATCH 0826/1986] Fix static analyzer warnings --- common.h | 3 +-- redis.c | 4 +++- redis_array.c | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/common.h b/common.h index 51f4055c7d..c9c78ac45f 100644 --- a/common.h +++ b/common.h @@ -40,7 +40,7 @@ typedef struct { ) { \ zend_string _zstr = {0}; \ char *_str_index; uint _str_length; ulong _num_index; \ - _val = zend_hash_get_current_data_ex(ht, &_hpos); \ + _h = 0; _key = NULL; _val = zend_hash_get_current_data_ex(ht, &_hpos); \ switch (zend_hash_get_current_key_ex(ht, &_str_index, &_str_length, &_num_index, 0, &_hpos)) { \ case HASH_KEY_IS_STRING: \ _zstr.len = _str_length - 1; \ @@ -48,7 +48,6 @@ typedef struct { _key = &_zstr; \ break; \ case HASH_KEY_IS_LONG: \ - _key = NULL; \ _h = _num_index; \ break; \ default: \ diff --git a/redis.c b/redis.c index e659aa990e..b31d666e95 100644 --- a/redis.c +++ b/redis.c @@ -2774,10 +2774,11 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, { fold_item *fi; - for (fi = redis_sock->head; fi; fi = fi->next) { + for (fi = redis_sock->head; fi; /* void */) { if (fi->fun) { fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi->ctx TSRMLS_CC); + fi = fi->next; continue; } size_t len; @@ -2804,6 +2805,7 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, int num = atol(inbuf + 1); if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, z_ret TSRMLS_CC) < 0) { } + if (fi) fi = fi->next; } redis_sock->current = fi; return 0; diff --git a/redis_array.c b/redis_array.c index 7d3af374ab..ec2a03632e 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1092,7 +1092,11 @@ PHP_METHOD(RedisArray, mset) #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_tmp); #endif - ZVAL_ZVAL(z_tmp, argv[i], 1, 0); + if (argv[i] == NULL) { + ZVAL_NULL(z_tmp); + } else { + ZVAL_ZVAL(z_tmp, argv[i], 1, 0); + } add_assoc_zval_ex(&z_argarray, keys[i], key_lens[i], z_tmp); found++; } From af71d4226869af69342afd866d7b557e79506757 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 15 Aug 2017 14:45:41 -0700 Subject: [PATCH 0827/1986] Avoid a coverty warning for failure to check return value. --- library.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 4cee8d163a..2aec71528d 100644 --- a/library.c +++ b/library.c @@ -1467,10 +1467,11 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) return -1; } - /* Attempt to set TCP_NODELAY if we're not using a unix socket */ + /* Attempt to set TCP_NODELAY if we're not using a unix socket. */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; if (!usocket) { - setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); + err = setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); + PHPREDIS_NOTUSED(err); } php_stream_auto_cleanup(redis_sock->stream); From 2bf7b2f7142f3fc14ba83260c065ef98e6499b8b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 1 Aug 2017 23:41:48 +0300 Subject: [PATCH 0828/1986] Use zend_string to store strings in RedisSock Following fields were changed: err, prefix, persistent_id, auth and host --- cluster_library.c | 14 ++++------ cluster_library.h | 19 +++++++------- common.h | 26 ++++++++++++++----- library.c | 65 ++++++++++++++++++++++------------------------- redis.c | 42 +++++++++++++----------------- redis_cluster.c | 28 +++++++++----------- redis_commands.c | 23 ++++++++--------- redis_session.c | 9 +++---- 8 files changed, 109 insertions(+), 117 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 7f6921ed8d..5a72dc3603 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -489,19 +489,16 @@ cluster_set_err(redisCluster *c, char *err, int err_len) { // Free our last error if (c->err != NULL) { - efree(c->err); + zend_string_release(c->err); + c->err = NULL; } if (err != NULL && err_len > 0) { + c->err = zend_string_init(err, err_len, 0); if (err_len >= sizeof("CLUSTERDOWN") - 1 && !memcmp(err, "CLUSTERDOWN", sizeof("CLUSTERDOWN") - 1) ) { c->clusterdown = 1; } - c->err = estrndup(err, err_len); - c->err_len = err_len; - } else { - c->err = NULL; - c->err_len = 0; } } @@ -819,7 +816,6 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, c->failover = failover; c->persistent = persistent; c->err = NULL; - c->err_len = 0; /* Set up our waitms based on timeout */ c->waitms = (long)(1000 * timeout); @@ -849,7 +845,7 @@ PHP_REDIS_API void cluster_free(redisCluster *c) { efree(c->nodes); /* Free any error we've got */ - if (c->err) efree(c->err); + if (c->err) zend_string_release(c->err); /* Free structure itself */ efree(c); @@ -1328,7 +1324,7 @@ PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, for(i=0;imaster[i] && c->master[i]->sock && c->master[i]->sock->port == port && - !strcasecmp(c->master[i]->sock->host, host)) + !strcasecmp(ZSTR_VAL(c->master[i]->sock->host), host)) { return i; } diff --git a/cluster_library.h b/cluster_library.h index 1b18107790..b3abd7e144 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -48,8 +48,8 @@ /* Compare redirection slot information with what we have */ #define CLUSTER_REDIR_CMP(c) \ (SLOT_SOCK(c,c->redir_slot)->port != c->redir_port || \ - strlen(SLOT_SOCK(c,c->redir_slot)->host) != c->redir_host_len || \ - memcmp(SLOT_SOCK(c,c->redir_slot)->host,c->redir_host,c->redir_host_len)) + ZSTR_LEN(SLOT_SOCK(c,c->redir_slot)->host) != c->redir_host_len || \ + memcmp(ZSTR_VAL(SLOT_SOCK(c,c->redir_slot)->host),c->redir_host,c->redir_host_len)) /* Lazy connect logic */ #define CLUSTER_LAZY_CONNECT(s) \ @@ -59,11 +59,13 @@ } /* Clear out our "last error" */ -#define CLUSTER_CLEAR_ERROR(c) \ - if(c->err) efree(c->err); \ - c->err = NULL; \ - c->err_len = 0; \ - c->clusterdown = 0; +#define CLUSTER_CLEAR_ERROR(c) do { \ + if (c->err) { \ + zend_string_release(c->err); \ + c->err = NULL; \ + } \ + c->clusterdown = 0; \ +} while (0) /* Protected sending of data down the wire to a RedisSock->stream */ #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \ @@ -216,8 +218,7 @@ typedef struct redisCluster { short clusterdown; /* The last ERROR we encountered */ - char *err; - int err_len; + zend_string *err; /* The slot our command is operating on, as well as it's socket */ unsigned short cmd_slot; diff --git a/common.h b/common.h index c9c78ac45f..133b4e8a10 100644 --- a/common.h +++ b/common.h @@ -25,6 +25,19 @@ typedef struct { #define ZSTR_VAL(s) (s)->val #define ZSTR_LEN(s) (s)->len +static zend_always_inline zend_string * +zend_string_init(const char *str, size_t len, int persistent) +{ + zend_string *zstr = emalloc(sizeof(zend_string) + len + 1); + + ZSTR_VAL(zstr) = (char *)zstr + sizeof(zend_string); + memcpy(ZSTR_VAL(zstr), str, len); + ZSTR_VAL(zstr)[len] = '\0'; + zstr->len = len; + zstr->gc = 0x01; + return zstr; +} + #define zend_string_release(s) do { \ if ((s) && (s)->gc) { \ if ((s)->gc & 0x10 && ZSTR_VAL(s)) efree(ZSTR_VAL(s)); \ @@ -619,9 +632,9 @@ typedef struct fold_item { /* {{{ struct RedisSock */ typedef struct { php_stream *stream; - char *host; + zend_string *host; short port; - char *auth; + zend_string *auth; double timeout; double read_timeout; long retry_interval; @@ -629,13 +642,12 @@ typedef struct { int status; int persistent; int watching; - char *persistent_id; + zend_string *persistent_id; int serializer; long dbNumber; - char *prefix; - int prefix_len; + zend_string *prefix; short mode; fold_item *head; @@ -644,8 +656,8 @@ typedef struct { char *pipeline_cmd; size_t pipeline_len; - char *err; - int err_len; + zend_string *err; + zend_bool lazy_connect; int scan; diff --git a/library.c b/library.c index 2aec71528d..84ed318bfe 100644 --- a/library.c +++ b/library.c @@ -83,7 +83,7 @@ static int resend_auth(RedisSock *redis_sock TSRMLS_DC) { int cmd_len, response_len; cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, &cmd, "AUTH", "s", - redis_sock->auth, strlen(redis_sock->auth)); + ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -117,11 +117,11 @@ static void redis_error_throw(RedisSock *redis_sock TSRMLS_DC) { if (redis_sock != NULL && redis_sock->err != NULL && - memcmp(redis_sock->err, "ERR", sizeof("ERR") - 1) != 0 && - memcmp(redis_sock->err, "NOSCRIPT", sizeof("NOSCRIPT") - 1) != 0 && - memcmp(redis_sock->err, "WRONGTYPE", sizeof("WRONGTYPE") - 1) != 0 + memcmp(ZSTR_VAL(redis_sock->err), "ERR", sizeof("ERR") - 1) != 0 && + memcmp(ZSTR_VAL(redis_sock->err), "NOSCRIPT", sizeof("NOSCRIPT") - 1) != 0 && + memcmp(ZSTR_VAL(redis_sock->err), "WRONGTYPE", sizeof("WRONGTYPE") - 1) != 0 ) { - zend_throw_exception(redis_exception_ce, redis_sock->err, 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, ZSTR_VAL(redis_sock->err), 0 TSRMLS_CC); } } @@ -1367,7 +1367,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, RedisSock *redis_sock; redis_sock = ecalloc(1, sizeof(RedisSock)); - redis_sock->host = estrndup(host, host_len); + redis_sock->host = zend_string_init(host, host_len, 0); redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; @@ -1377,8 +1377,8 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->lazy_connect = lazy_connect; redis_sock->persistent_id = NULL; - if(persistent_id) { - redis_sock->persistent_id = estrdup(persistent_id); + if (persistent_id) { + redis_sock->persistent_id = zend_string_init(persistent_id, strlen(persistent_id), 0); } redis_sock->port = port; @@ -1394,7 +1394,6 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->pipeline_len = 0; redis_sock->err = NULL; - redis_sock->err_len = 0; redis_sock->scan = REDIS_SCAN_NORETRY; @@ -1428,8 +1427,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) read_tv.tv_sec = (time_t)redis_sock->read_timeout; read_tv.tv_usec = (int)((redis_sock->read_timeout-read_tv.tv_sec)*1000000); - if(redis_sock->host[0] == '/' && redis_sock->port < 1) { - host_len = snprintf(host, sizeof(host), "unix://%s", redis_sock->host); + if (ZSTR_VAL(redis_sock->host)[0] == '/' && redis_sock->port < 1) { + host_len = snprintf(host, sizeof(host), "unix://%s", ZSTR_VAL(redis_sock->host)); usocket = 1; } else { if(redis_sock->port == 0) @@ -1438,17 +1437,17 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) #ifdef HAVE_IPV6 /* If we've got IPv6 and find a colon in our address, convert to proper * IPv6 [host]:port format */ - if (strchr(redis_sock->host, ':') != NULL) { + if (strchr(ZSTR_VAL(redis_sock->host), ':') != NULL) { fmtstr = "[%s]:%d"; } #endif - host_len = snprintf(host, sizeof(host), fmtstr, redis_sock->host, redis_sock->port); + host_len = snprintf(host, sizeof(host), fmtstr, ZSTR_VAL(redis_sock->host), redis_sock->port); } if (redis_sock->persistent) { if (redis_sock->persistent_id) { spprintf(&persistent_id, 0, "phpredis:%s:%s", host, - redis_sock->persistent_id); + ZSTR_VAL(redis_sock->persistent_id)); } else { spprintf(&persistent_id, 0, "phpredis:%s:%f", host, redis_sock->timeout); @@ -1541,17 +1540,13 @@ redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { // Free our last error if (redis_sock->err != NULL) { - efree(redis_sock->err); + zend_string_release(redis_sock->err); + redis_sock->err = NULL; } if (msg != NULL && msg_len > 0) { // Copy in our new error message - redis_sock->err = estrndup(msg, msg_len); - redis_sock->err_len = msg_len; - } else { - // Set to null, with zero length - redis_sock->err = NULL; - redis_sock->err_len = 0; + redis_sock->err = zend_string_init(msg, msg_len, 0); } } @@ -1759,22 +1754,24 @@ redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) */ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) { - if(redis_sock->prefix) { - efree(redis_sock->prefix); + if (redis_sock->prefix) { + zend_string_release(redis_sock->prefix); } if (redis_sock->pipeline_cmd) { efree(redis_sock->pipeline_cmd); } - if(redis_sock->err) { - efree(redis_sock->err); + if (redis_sock->err) { + zend_string_release(redis_sock->err); + } + if (redis_sock->auth) { + zend_string_release(redis_sock->auth); } - if(redis_sock->auth) { - efree(redis_sock->auth); + if (redis_sock->persistent_id) { + zend_string_release(redis_sock->persistent_id); } - if(redis_sock->persistent_id) { - efree(redis_sock->persistent_id); + if (redis_sock->host) { + zend_string_release(redis_sock->host); } - efree(redis_sock->host); efree(redis_sock); } @@ -1931,14 +1928,14 @@ redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len) { int ret_len; char *ret; - if(redis_sock->prefix == NULL || redis_sock->prefix_len == 0) { + if (redis_sock->prefix == NULL) { return 0; } - ret_len = redis_sock->prefix_len + *key_len; + ret_len = ZSTR_LEN(redis_sock->prefix) + *key_len; ret = ecalloc(1 + ret_len, 1); - memcpy(ret, redis_sock->prefix, redis_sock->prefix_len); - memcpy(ret + redis_sock->prefix_len, *key, *key_len); + memcpy(ret, ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix)); + memcpy(ret + ZSTR_LEN(redis_sock->prefix), *key, *key_len); *key = ret; *key_len = ret_len; diff --git a/redis.c b/redis.c index bccc320f9a..f4e30e27ce 100644 --- a/redis.c +++ b/redis.c @@ -45,9 +45,10 @@ extern ps_module ps_mod_redis_cluster; extern zend_class_entry *redis_array_ce; extern zend_class_entry *redis_cluster_ce; +extern zend_class_entry *redis_cluster_exception_ce; + zend_class_entry *redis_ce; zend_class_entry *redis_exception_ce; -extern zend_class_entry *redis_cluster_exception_ce; extern zend_function_entry redis_array_functions[]; extern zend_function_entry redis_cluster_functions[]; @@ -3511,11 +3512,10 @@ PHP_METHOD(Redis, getLastError) { } /* Return our last error or NULL if we don't have one */ - if(redis_sock->err != NULL && redis_sock->err_len > 0) { - RETURN_STRINGL(redis_sock->err, redis_sock->err_len); - } else { - RETURN_NULL(); - } + if (redis_sock->err) { + RETURN_STRINGL(ZSTR_VAL(redis_sock->err), ZSTR_LEN(redis_sock->err)); + } + RETURN_NULL(); } /* {{{ proto Redis::clearLastError() */ @@ -3535,10 +3535,10 @@ PHP_METHOD(Redis, clearLastError) { } // Clear error message - if(redis_sock->err) { - efree(redis_sock->err); + if (redis_sock->err) { + zend_string_release(redis_sock->err); + redis_sock->err = NULL; } - redis_sock->err = NULL; RETURN_TRUE; } @@ -3599,7 +3599,7 @@ PHP_METHOD(Redis, getHost) { RedisSock *redis_sock; if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { - RETURN_STRING(redis_sock->host); + RETURN_STRINGL(ZSTR_VAL(redis_sock->host), ZSTR_LEN(redis_sock->host)); } else { RETURN_FALSE; } @@ -3655,30 +3655,24 @@ PHP_METHOD(Redis, getReadTimeout) { PHP_METHOD(Redis, getPersistentID) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { - if(redis_sock->persistent_id != NULL) { - RETURN_STRING(redis_sock->persistent_id); - } else { - RETURN_NULL(); - } - } else { + if ((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) { RETURN_FALSE; + } else if (redis_sock->persistent_id == NULL) { + RETURN_NULL(); } + RETURN_STRINGL(ZSTR_VAL(redis_sock->persistent_id), ZSTR_LEN(redis_sock->persistent_id)); } /* {{{ proto Redis::getAuth */ PHP_METHOD(Redis, getAuth) { RedisSock *redis_sock; - if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) { - if(redis_sock->auth != NULL) { - RETURN_STRING(redis_sock->auth); - } else { - RETURN_NULL(); - } - } else { + if ((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) { RETURN_FALSE; + } else if (redis_sock->auth == NULL) { + RETURN_NULL(); } + RETURN_STRINGL(ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); } /* diff --git a/redis_cluster.c b/redis_cluster.c index be3097e6af..3449e70681 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -334,7 +334,7 @@ free_cluster_context(zend_object *object) { zend_hash_destroy(cluster->nodes); efree(cluster->nodes); - if(cluster->err) efree(cluster->err); + if (cluster->err) zend_string_release(cluster->err); zend_object_std_dtor(&cluster->std TSRMLS_CC); @@ -1043,7 +1043,7 @@ PHP_METHOD(RedisCluster, keys) { TSRMLS_CC)<0) { php_error_docref(0 TSRMLS_CC, E_ERROR, "Can't send KEYS to %s:%d", - node->sock->host, node->sock->port); + ZSTR_VAL(node->sock->host), node->sock->port); efree(cmd); RETURN_FALSE; } @@ -1052,7 +1052,7 @@ PHP_METHOD(RedisCluster, keys) { resp = cluster_read_resp(c TSRMLS_CC); if(!resp) { php_error_docref(0 TSRMLS_CC, E_WARNING, - "Can't read response from %s:%d", node->sock->host, + "Can't read response from %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); continue; } @@ -1902,11 +1902,10 @@ PHP_METHOD(RedisCluster, getmode) { PHP_METHOD(RedisCluster, getlasterror) { redisCluster *c = GET_CONTEXT(); - if(c->err != NULL && c->err_len > 0) { - RETURN_STRINGL(c->err, c->err_len); - } else { - RETURN_NULL(); + if (c->err) { + RETURN_STRINGL(ZSTR_VAL(c->err), ZSTR_LEN(c->err)); } + RETURN_NULL(); } /* }}} */ @@ -1914,9 +1913,10 @@ PHP_METHOD(RedisCluster, getlasterror) { PHP_METHOD(RedisCluster, clearlasterror) { redisCluster *c = GET_CONTEXT(); - if (c->err) efree(c->err); - c->err = NULL; - c->err_len = 0; + if (c->err) { + zend_string_release(c->err); + c->err = NULL; + } RETURN_TRUE; } @@ -1962,15 +1962,11 @@ PHP_METHOD(RedisCluster, _masters) { redisCluster *c = GET_CONTEXT(); redisClusterNode *node; zval zv, *z_ret = &zv; - char *host; - short port; array_init(z_ret); ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) break; - host = node->sock->host; - port = node->sock->port; zval z, *z_sub = &z; #if (PHP_MAJOR_VERSION < 7) @@ -1978,8 +1974,8 @@ PHP_METHOD(RedisCluster, _masters) { #endif array_init(z_sub); - add_next_index_stringl(z_sub, host, strlen(host)); - add_next_index_long(z_sub, port); + add_next_index_stringl(z_sub, ZSTR_VAL(node->sock->host), ZSTR_LEN(node->sock->host)); + add_next_index_long(z_sub, node->sock->port); add_next_index_zval(z_ret, z_sub); } ZEND_HASH_FOREACH_END(); diff --git a/redis_commands.c b/redis_commands.c index f5c51ade3c..cb51760089 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1863,8 +1863,8 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "s", pw, pw_len); // Free previously allocated password, and update - if(redis_sock->auth) efree(redis_sock->auth); - redis_sock->auth = estrndup(pw, pw_len); + if (redis_sock->auth) zend_string_release(redis_sock->auth); + redis_sock->auth = zend_string_init(pw, pw_len, 0); // Success return SUCCESS; @@ -2960,8 +2960,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, case REDIS_OPT_SERIALIZER: RETURN_LONG(redis_sock->serializer); case REDIS_OPT_PREFIX: - if(redis_sock->prefix) { - RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len); + if (redis_sock->prefix) { + RETURN_STRINGL(ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix)); } RETURN_NULL(); case REDIS_OPT_READ_TIMEOUT: @@ -3003,15 +3003,12 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, } break; case REDIS_OPT_PREFIX: - if(redis_sock->prefix) { - efree(redis_sock->prefix); - } - if(val_len == 0) { + if (redis_sock->prefix) { + zend_string_release(redis_sock->prefix); redis_sock->prefix = NULL; - redis_sock->prefix_len = 0; - } else { - redis_sock->prefix = estrndup(val_str, val_len); - redis_sock->prefix_len = val_len; + } + if (val_str && val_len > 0) { + redis_sock->prefix = zend_string_init(val_str, val_len, 0); } RETURN_TRUE; case REDIS_OPT_READ_TIMEOUT: @@ -3058,7 +3055,7 @@ void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { RETURN_FALSE; } - if(redis_sock->prefix != NULL && redis_sock->prefix_len>0) { + if (redis_sock->prefix) { int keyfree = redis_key_prefix(redis_sock, &key, &key_len); RETVAL_STRINGL(key, key_len); if (keyfree) efree(key); diff --git a/redis_session.c b/redis_session.c index fd7a5e2f53..fe753e7bf2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -548,10 +548,10 @@ static char *cluster_session_key(redisCluster *c, const char *key, int keylen, int *skeylen, short *slot) { char *skey; - *skeylen = keylen + c->flags->prefix_len; + *skeylen = keylen + ZSTR_LEN(c->flags->prefix); skey = emalloc(*skeylen); - memcpy(skey, c->flags->prefix, c->flags->prefix_len); - memcpy(skey + c->flags->prefix_len, key, keylen); + memcpy(skey, ZSTR_VAL(c->flags->prefix), ZSTR_LEN(c->flags->prefix)); + memcpy(skey + ZSTR_LEN(c->flags->prefix), key, keylen); *slot = cluster_hash_key(skey, *skeylen); @@ -624,8 +624,7 @@ PS_OPEN_FUNC(rediscluster) { c = cluster_create(timeout, read_timeout, failover, persistent); if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) { /* Set up our prefix */ - c->flags->prefix = estrndup(prefix, prefix_len); - c->flags->prefix_len = prefix_len; + c->flags->prefix = zend_string_init(prefix, prefix_len, 0); PS_SET_MOD_DATA(c); retval = SUCCESS; From cccc3997e960472e54075b59acc2563cb1eb207e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 20 Aug 2017 13:28:00 -0700 Subject: [PATCH 0829/1986] Update EXISTS to handle multiple keys Fixes #1223 --- redis.c | 2 +- redis_cluster.c | 2 +- redis_commands.c | 8 ++++++++ redis_commands.h | 3 +++ tests/RedisArrayTest.php | 4 ++-- tests/RedisTest.php | 25 ++++++++++++++++++++----- 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/redis.c b/redis.c index f4e30e27ce..399097719a 100644 --- a/redis.c +++ b/redis.c @@ -1562,7 +1562,7 @@ PHP_METHOD(Redis, getMultiple) */ PHP_METHOD(Redis, exists) { - REDIS_PROCESS_KW_CMD("EXISTS", redis_key_cmd, redis_1_response); + REDIS_PROCESS_CMD(exists, redis_long_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 3449e70681..ec7e2920e0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1008,7 +1008,7 @@ PHP_METHOD(RedisCluster, getset) { /* {{{ proto int RedisCluster::exists(string key) */ PHP_METHOD(RedisCluster, exists) { - CLUSTER_PROCESS_KW_CMD("EXISTS", redis_key_cmd, cluster_1_resp, 1); + CLUSTER_PROCESS_CMD(exists, cluster_long_resp, 1); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index cb51760089..7cf274c065 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2790,6 +2790,14 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* EXISTS */ +int redis_exists_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + "EXISTS", sizeof("EXISTS") - 1, 0, 0, cmd, cmd_len, slot); +} + /* DEL */ int redis_del_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 018348d42b..73b599cde8 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -201,6 +201,9 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_REPLY_TYPE *rtype, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_exists_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_del_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 647a0129d1..0233b6d267 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -460,8 +460,8 @@ public function testMultiExecDel() { ->exec(); $this->assertTrue($out[0] === 2); - $this->assertTrue($this->ra->exists('1_{employee:joe}_group') === FALSE); - $this->assertTrue($this->ra->exists('1_{employee:joe}_salary') === FALSE); + $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_group')); + $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary')); } public function testDiscard() { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 24f01d79a2..fc380b7d4c 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -354,7 +354,7 @@ public function testGetSet() { public function testRandomKey() { for($i = 0; $i < 1000; $i++) { $k = $this->redis->randomKey(); - $this->assertTrue($this->redis->exists($k)); + $this->assertEquals($this->redis->exists($k), 1); } } @@ -555,7 +555,7 @@ public function testIncrByFloat() $this->redis->incrbyfloat('key',1.8); $this->assertEquals(1.8, floatval($this->redis->get('key'))); // convert to float to avoid rounding issue on arm $this->redis->setOption(Redis::OPT_PREFIX, ''); - $this->assertTrue($this->redis->exists('someprefix:key')); + $this->assertEquals(1, $this->redis->exists('someprefix:key')); $this->redis->del('someprefix:key'); } @@ -586,10 +586,25 @@ public function testDecr() public function testExists() { + /* Single key */ $this->redis->del('key'); - $this->assertFalse($this->redis->exists('key')); + $this->assertEquals(0, $this->redis->exists('key')); $this->redis->set('key', 'val'); - $this->assertEquals(True, $this->redis->exists('key')); + $this->assertEquals(1, $this->redis->exists('key')); + + /* Add multiple keys */ + $mkeys = []; + for ($i = 0; $i < 10; $i++) { + if (rand(1, 2) == 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 testGetKeys() @@ -4089,7 +4104,7 @@ private function checkSerializer($mode) { $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); - $this->assertTrue(FALSE === $this->redis->exists('k')); + $this->assertEquals(0, $this->redis->exists('k')); // sismember $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[0])); From caf5c9984cd835572ddbc8805a237aedf06de0a8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 20 Aug 2017 13:45:10 -0700 Subject: [PATCH 0830/1986] Use old style array syntax --- tests/RedisTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index fc380b7d4c..fed7915c11 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -593,7 +593,7 @@ public function testExists() $this->assertEquals(1, $this->redis->exists('key')); /* Add multiple keys */ - $mkeys = []; + $mkeys = Array(); for ($i = 0; $i < 10; $i++) { if (rand(1, 2) == 1) { $mkey = "{exists}key:$i"; @@ -604,7 +604,7 @@ public function testExists() /* 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)); + $this->assertEquals(count($mkeys), call_user_func_array(Array($this->redis, 'exists'), $mkeys)); } public function testGetKeys() From 682593db69ef7def0cdd25570348c56812f9cea9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 21 Aug 2017 11:06:43 -0700 Subject: [PATCH 0831/1986] arginfo for updated exists --- redis.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/redis.c b/redis.c index 399097719a..e22915c934 100644 --- a/redis.c +++ b/redis.c @@ -125,6 +125,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_exists, 0, 0, 1) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) ZEND_ARG_INFO(0, key) #if PHP_VERSION_ID >= 50600 From e23be2c8305af2be8fc13f7cd6f036e409de11f4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 22 Aug 2017 17:11:06 +0300 Subject: [PATCH 0832/1986] Add arginfo for RedisArray --- common.h | 3 +++ redis_array.c | 62 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/common.h b/common.h index 133b4e8a10..11620e93dd 100644 --- a/common.h +++ b/common.h @@ -397,6 +397,8 @@ extern int (*_php_var_unserialize)(zval **, const unsigned char **, const unsign #define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) typedef int strlen_t; +#define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_BOOL && !Z_BVAL_P(z)) + /* If ZEND_MOD_END isn't defined, use legacy version */ #ifndef ZEND_MOD_END #define ZEND_MOD_END { NULL, NULL, NULL } @@ -414,6 +416,7 @@ typedef int strlen_t; #include #include typedef size_t strlen_t; +#define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_FALSE) #endif /* NULL check so Eclipse doesn't go crazy */ diff --git a/redis_array.c b/redis_array.c index ec2a03632e..ad3f50b02d 100644 --- a/redis_array.c +++ b/redis_array.c @@ -32,45 +32,61 @@ #include "redis_array_impl.h" /* Simple macro to detect failure in a RedisArray call */ -#if (PHP_MAJOR_VERSION < 7) -#define RA_CALL_FAILED(rv, cmd) ( \ - (Z_TYPE_P(rv) == IS_BOOL && !Z_LVAL_P(rv)) || \ - (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \ - (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE")) \ -) -#else #define RA_CALL_FAILED(rv, cmd) ( \ - (Z_TYPE_P(rv) == IS_FALSE) || \ + PHPREDIS_ZVAL_IS_STRICT_FALSE(rv) || \ (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \ (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE")) \ ) -#endif extern zend_class_entry *redis_ce; zend_class_entry *redis_array_ce; -ZEND_BEGIN_ARG_INFO_EX(__redis_array_call_args, 0, 0, 2) +ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, hosts, 0) + ZEND_ARG_ARRAY_INFO(0, options, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_call, 0, 0, 2) ZEND_ARG_INFO(0, function_name) ZEND_ARG_INFO(0, arguments) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_target, 0, 0, 1) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_instance, 0, 0, 1) + ZEND_ARG_INFO(0, host) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_rehash, 0, 0, 0) + ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1) + ZEND_ARG_INFO(0, index) +ZEND_END_ARG_INFO() + zend_function_entry redis_array_functions[] = { - PHP_ME(RedisArray, __construct, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, __call, __redis_array_call_args, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, __construct, arginfo_ctor, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, __call, arginfo_call, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _hosts, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _target, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _instance, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _function, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _distributor, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _rehash, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _hosts, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _target, arginfo_target, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _instance, arginfo_instance, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _function, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _distributor, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _rehash, arginfo_rehash, ZEND_ACC_PUBLIC) /* special implementation for a few functions */ - PHP_ME(RedisArray, select, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, info, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, ping, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, flushdb, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, flushall, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, select, arginfo_select, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, info, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, ping, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, flushdb, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, flushall, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, mset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, del, NULL, ZEND_ACC_PUBLIC) From 212e323f5b21c2cfcc226c3ec67f2608caa613cb Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 22 Aug 2017 17:23:19 +0300 Subject: [PATCH 0833/1986] More arginfo for RedisArray --- redis_array.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redis_array.c b/redis_array.c index ad3f50b02d..d12cb13ba4 100644 --- a/redis_array.c +++ b/redis_array.c @@ -45,7 +45,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, hosts, 0) + ZEND_ARG_INFO(0, name_or_hosts) ZEND_ARG_ARRAY_INFO(0, options, 0) ZEND_END_ARG_INFO() @@ -98,9 +98,9 @@ zend_function_entry redis_array_functions[] = { /* Multi/Exec */ PHP_ME(RedisArray, multi, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, exec, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, discard, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, unwatch, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, exec, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, discard, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, unwatch, arginfo_void, ZEND_ACC_PUBLIC) /* Aliases */ PHP_MALIAS(RedisArray, delete, del, NULL, ZEND_ACC_PUBLIC) From 6c2c6faa1b132ce6c5634d7209e1626797ac86f8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 23 Aug 2017 09:26:12 +0300 Subject: [PATCH 0834/1986] More arginfo for RedisArray --- redis_array.c | 52 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/redis_array.c b/redis_array.c index d12cb13ba4..9a5c19ae5f 100644 --- a/redis_array.c +++ b/redis_array.c @@ -70,6 +70,36 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1) ZEND_ARG_INFO(0, index) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1) + ZEND_ARG_INFO(0, keys) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_mset, 0, 0, 1) + ZEND_ARG_INFO(0, pairs) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) + ZEND_ARG_INFO(0, keys) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_getopt, 0, 0, 1) + ZEND_ARG_INFO(0, opt) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_setopt, 0, 0, 2) + ZEND_ARG_INFO(0, opt) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_multi, 0, 0, 1) + ZEND_ARG_INFO(0, host) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, __construct, arginfo_ctor, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, __call, arginfo_call, ZEND_ACC_PUBLIC) @@ -87,24 +117,24 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, ping, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, flushdb, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, flushall, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, mget, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, mset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, del, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, getOption, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, setOption, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, keys, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, save, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, bgsave, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, mget, arginfo_mget, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, mset, arginfo_mset, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, del, arginfo_del, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, getOption, arginfo_getopt, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, setOption,arginfo_setopt, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, keys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, save, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, bgsave, arginfo_void, ZEND_ACC_PUBLIC) /* Multi/Exec */ - PHP_ME(RedisArray, multi, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, multi, arginfo_multi, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, exec, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, discard, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, unwatch, arginfo_void, ZEND_ACC_PUBLIC) /* Aliases */ - PHP_MALIAS(RedisArray, delete, del, NULL, ZEND_ACC_PUBLIC) - PHP_MALIAS(RedisArray, getMultiple, mget, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(RedisArray, delete, del, arginfo_del, ZEND_ACC_PUBLIC) + PHP_MALIAS(RedisArray, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC) PHP_FE_END }; From c9df77db57494c70143eff99341b9e36152a1df8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 23 Aug 2017 15:24:02 +0300 Subject: [PATCH 0835/1986] ra_call_distributor returns position or -1 in case of error --- redis_array_impl.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 9ccfffe1a0..902831c2a4 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -431,10 +431,10 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS } /* call userland key distributor function */ -zend_bool -ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRMLS_DC) +int +ra_call_distributor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) { - zend_bool ret = 0; + int ret; zval z_ret, z_argv; /* check that we can call the extractor function */ @@ -444,7 +444,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL)) { #endif php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); - return 0; + return -1; } ZVAL_NULL(&z_ret); @@ -452,10 +452,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRML ZVAL_STRINGL(&z_argv, key, key_len); call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv); - if (Z_TYPE(z_ret) == IS_LONG) { - *pos = Z_LVAL(z_ret); - ret = 1; - } + ret = (Z_TYPE(z_ret) == IS_LONG) ? Z_LVAL(z_ret) : -1; zval_dtor(&z_argv); zval_dtor(&z_ret); @@ -473,13 +470,12 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D if(!out) return NULL; if (Z_TYPE(ra->z_dist) != IS_NULL) { - if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) { + pos = ra_call_distributor(ra, key, key_len TSRMLS_CC); + if (pos < 0 || pos >= ra->count) { efree(out); return NULL; } - efree(out); } else { - uint64_t h64; unsigned long ret = 0xffffffff; size_t i; @@ -488,14 +484,12 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D CRC32(ret, (unsigned char)out[i]); } hash = (ret ^ 0xffffffff); - efree(out); /* get position on ring */ - h64 = hash; - h64 *= ra->count; - h64 /= 0xffffffff; - pos = (int)h64; + pos = (int)(hash * ra->count / 0xffffffff); } + efree(out); + if(out_pos) *out_pos = pos; return &ra->redis[pos]; From 50dcb153c577231712c65fe582e688c34ad14fa3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 27 Aug 2017 14:16:05 +0300 Subject: [PATCH 0836/1986] Add arginfo for RedisCluster --- redis_cluster.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index ec7e2920e0..c42b98d9f9 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -40,6 +40,26 @@ zend_class_entry *redis_cluster_exception_ce; /* Handlers for RedisCluster */ zend_object_handlers RedisCluster_handlers; +ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key, 0, 0, 1) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_value, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_ARG_ARRAY_INFO(0, seeds, 0) + ZEND_ARG_INFO(0, timeout) + ZEND_ARG_INFO(0, read_timeout) + ZEND_ARG_INFO(0, persistent) +ZEND_END_ARG_INFO(); + /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) ZEND_ARG_INFO(0, str_key) @@ -58,10 +78,10 @@ ZEND_END_ARG_INFO(); /* Function table */ zend_function_entry redis_cluster_functions[] = { - PHP_ME(RedisCluster, __construct, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, close, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, get, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, set, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, __construct, arginfo_ctor, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, close, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, get, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, set, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, mget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, mset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, msetnx, NULL, ZEND_ACC_PUBLIC) From 3c60e1f8bfdbd2edab89803c3ab173109e951af1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 28 Aug 2017 12:41:19 +0300 Subject: [PATCH 0837/1986] More arginfo for RedisCluster --- redis_cluster.c | 98 ++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index c42b98d9f9..b723a84e9b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -88,33 +88,33 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, del, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setex, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, psetex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setnx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, exists, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setnx, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getset, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, exists, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, keys, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, type, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lpop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rpop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lpop, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lset, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lpush, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rpush, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lpush, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpush, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, blpop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, brpop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rpushx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lpushx, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpushx, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lpushx, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, linsert, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lindex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lrem, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lrem, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, brpoplpush, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpoplpush, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, llen, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, llen, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, scard, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, smembers, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sismember, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sadd, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, smembers, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sismember, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sadd, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, saddarray, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, srem, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, srem, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sunion, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sunionstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sinter, NULL, ZEND_ACC_PUBLIC) @@ -122,21 +122,21 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, sdiff, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sdiffstore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, srandmember, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, strlen, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, persist, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, ttl, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pttl, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zcard, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, strlen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, persist, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pttl, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zcard, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcount, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zremrangebyscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zadd, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zincrby, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hlen, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hkeys, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hvals, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hlen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hkeys, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hvals, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hget, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hgetall, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hgetall, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrby, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hset, NULL, ZEND_ACC_PUBLIC) @@ -146,24 +146,24 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, hdel, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hincrbyfloat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hstrlen, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, dump, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, dump, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrank, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, incr, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, decr, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, incrby, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, decrby, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, incrbyfloat, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incr, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, decr, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incrby, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, decrby, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incrbyfloat, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, expire, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pexpire, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, expireat, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pexpireat, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, append, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getbit, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setbit, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitop, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitpos, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bitcount, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitcount, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lget, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getrange, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ltrim, NULL, ZEND_ACC_PUBLIC) @@ -202,9 +202,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getmode, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getlasterror, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, clearlasterror, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getmode, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getlasterror, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, clearlasterror, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) @@ -214,24 +214,24 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, _masters, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _redir, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, multi, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, exec, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, discard, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, multi, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, exec, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, discard, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, watch, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, unwatch, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, save, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bgsave, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, flushdb, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, flushall, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, dbsize, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, save, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bgsave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushdb, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushall, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, dbsize, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bgrewriteaof, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lastsave, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lastsave, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, info, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, role, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, time, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, role, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, time, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, randomkey, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ping, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rawcommand, NULL, ZEND_ACC_PUBLIC) From e5660be4c62d8afbae1fd2305e674f7711926098 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 28 Aug 2017 13:36:37 +0300 Subject: [PATCH 0838/1986] More arginfo for RedisCluster + remove duplicates --- common.h | 17 +++++++++++++++++ redis.c | 17 ----------------- redis_array.c | 3 --- redis_cluster.c | 22 +++++----------------- 4 files changed, 22 insertions(+), 37 deletions(-) diff --git a/common.h b/common.h index 11620e93dd..63f48acc4b 100644 --- a/common.h +++ b/common.h @@ -681,4 +681,21 @@ typedef struct { } redis_object; #endif +/** Argument info for any function expecting 0 args */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key, 0, 0, 1) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_value, 0, 0, 1) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_value, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + #endif diff --git a/redis.c b/redis.c index e22915c934..622f6282f9 100644 --- a/redis.c +++ b/redis.c @@ -68,10 +68,6 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL) PHP_INI_END() -/** Argument info for any function expecting 0 args */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) -ZEND_END_ARG_INFO() - /** {{{ Argument info for commands in redis 1.0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 2) ZEND_ARG_INFO(0, host) @@ -90,14 +86,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_echo, 0, 0, 1) ZEND_ARG_INFO(0, msg) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_key, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_value, 0, 0, 1) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) @@ -111,11 +99,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key_expire_value, 0, 0, 3) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_value, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_key_newkey, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, newkey) diff --git a/redis_array.c b/redis_array.c index 9a5c19ae5f..ead1da2a65 100644 --- a/redis_array.c +++ b/redis_array.c @@ -41,9 +41,6 @@ extern zend_class_entry *redis_ce; zend_class_entry *redis_array_ce; -ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) ZEND_ARG_INFO(0, name_or_hosts) ZEND_ARG_ARRAY_INFO(0, options, 0) diff --git a/redis_cluster.c b/redis_cluster.c index b723a84e9b..1a98cc93c1 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -40,18 +40,6 @@ zend_class_entry *redis_cluster_exception_ce; /* Handlers for RedisCluster */ zend_object_handlers RedisCluster_handlers; -ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_value, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) ZEND_ARG_INFO(0, name) ZEND_ARG_ARRAY_INFO(0, seeds, 0) @@ -208,11 +196,11 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _prefix, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _serialize, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _unserialize, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _masters, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _redir, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _prefix, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _masters, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _redir, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, multi, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exec, arginfo_void, ZEND_ACC_PUBLIC) From 4a0a46b4cba01e870638abf095a47cb429e9c756 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 28 Aug 2017 16:14:42 +0300 Subject: [PATCH 0839/1986] Check number of elements in incoming array-argument --- redis_array.c | 18 ++++++++++++++---- redis_array_impl.c | 3 +-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/redis_array.c b/redis_array.c index ead1da2a65..73dad31177 100644 --- a/redis_array.c +++ b/redis_array.c @@ -394,7 +394,9 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i zend_bool b_write_cmd = 0; h_args = Z_ARRVAL_P(z_args); - argc = zend_hash_num_elements(h_args); + if ((argc = zend_hash_num_elements(h_args)) == 0) { + RETURN_FALSE; + } if(ra->z_multi_exec) { redis_inst = ra->z_multi_exec; /* we already have the instance */ @@ -935,7 +937,9 @@ PHP_METHOD(RedisArray, mget) /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); - argc = zend_hash_num_elements(h_keys); + if ((argc = zend_hash_num_elements(h_keys)) == 0) { + RETURN_FALSE; + } argv = emalloc(argc * sizeof(zval*)); pos = emalloc(argc * sizeof(int)); @@ -1087,7 +1091,9 @@ PHP_METHOD(RedisArray, mset) /* init data structures */ h_keys = Z_ARRVAL_P(z_keys); - argc = zend_hash_num_elements(h_keys); + if ((argc = zend_hash_num_elements(h_keys)) == 0) { + RETURN_FALSE; + } argv = emalloc(argc * sizeof(zval*)); pos = emalloc(argc * sizeof(int)); keys = emalloc(argc * sizeof(char*)); @@ -1234,7 +1240,11 @@ PHP_METHOD(RedisArray, del) /* init data structures */ h_keys = Z_ARRVAL(z_keys); - argc = zend_hash_num_elements(h_keys); + if ((argc = zend_hash_num_elements(h_keys)) == 0) { + if (free_zkeys) zval_dtor(&z_keys); + efree(z_args); + RETURN_FALSE; + } argv = emalloc(argc * sizeof(zval*)); pos = emalloc(argc * sizeof(int)); diff --git a/redis_array_impl.c b/redis_array_impl.c index 902831c2a4..0d740a0ffe 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -344,8 +344,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev int i, count; RedisArray *ra; - if (!hosts) return NULL; - count = zend_hash_num_elements(hosts); + if (!hosts || (count = zend_hash_num_elements(hosts)) == 0) return NULL; /* create object */ ra = emalloc(sizeof(RedisArray)); From d5609fc58b93521d811301c23c44d7804495e60c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 29 Aug 2017 09:40:09 +0300 Subject: [PATCH 0840/1986] More arginfo for RedisCluster --- common.h | 90 ++++++++++++++++++++++++++++++++++++++++++++++ redis.c | 94 ++----------------------------------------------- redis_cluster.c | 71 +++++++++++++++++++++++-------------- 3 files changed, 136 insertions(+), 119 deletions(-) diff --git a/common.h b/common.h index 63f48acc4b..0bdcc6dace 100644 --- a/common.h +++ b/common.h @@ -698,4 +698,94 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key_value, 0, 0, 2) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_expire_value, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, expire) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pairs, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, pairs, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_nkeys, 0, 0, 1) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_dst_nkeys, 0, 0, 2) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_echo, 0, 0, 1) + ZEND_ARG_INFO(0, msg) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, timeout) + ZEND_ARG_INFO(0, opt) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lset, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_blrpop, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, timeout_or_key) +// Can't have variadic keys before timeout. +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, extra_args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_linsert, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, position) + ZEND_ARG_INFO(0, pivot) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lindex, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, index) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_brpoplpush, 0, 0, 3) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_rpoplpush, 0, 0, 2) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sadd_array, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, options, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_srand_member, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, count) +ZEND_END_ARG_INFO() + #endif diff --git a/redis.c b/redis.c index 622f6282f9..e2e3dcc076 100644 --- a/redis.c +++ b/redis.c @@ -82,23 +82,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 2) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_echo, 0, 0, 1) - ZEND_ARG_INFO(0, msg) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, opt) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_expire_value, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expire) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_key_newkey, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, newkey) @@ -161,17 +144,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_generic_sort, 0, 0, 1) ZEND_ARG_INFO(0, getList) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_blrpop, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) -// Can't have variadic keys before timeout. -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, extra_args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_lrem, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) @@ -184,59 +156,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ltrim, 0, 0, 3) ZEND_ARG_INFO(0, stop) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_lget, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, index) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_lset, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, index) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_linsert, 0, 0, 4) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, position) - ZEND_ARG_INFO(0, pivot) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_sadd_array, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, options, 0) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_smove, 0, 0, 3) ZEND_ARG_INFO(0, src) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_srand_member, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_nkeys, 0, 0, 1) - ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_dst_nkeys, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_set_timeout, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timeout) @@ -300,21 +225,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_evalsha, 0, 0, 1) ZEND_END_ARG_INFO() /* }}} */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_pairs, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, pairs, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_rpoplpush, 0, 0, 2) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_brpoplpush, 0, 0, 3) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_zadd, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, score) @@ -641,7 +551,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, lSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) PHP_ME(Redis, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lGet, arginfo_lget, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lGet, arginfo_lindex, ZEND_ACC_PUBLIC) PHP_ME(Redis, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) PHP_ME(Redis, lSet, arginfo_lset, ZEND_ACC_PUBLIC) PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC) @@ -828,7 +738,7 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, keys, getKeys, arginfo_keys, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, lrem, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, ltrim, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lindex, lGet, arginfo_lget, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lindex, lGet, arginfo_lindex, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, lrange, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, scard, sSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, srem, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index 1a98cc93c1..b4d4068e18 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -48,6 +48,23 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) ZEND_ARG_INFO(0, persistent) ZEND_END_ARG_INFO(); +ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, keys, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) +ZEND_END_ARG_INFO() + /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) ZEND_ARG_INFO(0, str_key) @@ -69,47 +86,47 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, __construct, arginfo_ctor, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, close, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, get, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, set, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, mget, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, mset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, msetnx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, del, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, psetex, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, set, arginfo_set, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, mget, arginfo_mget, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, mset, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, del, arginfo_del, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, setnx, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getset, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exists, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, keys, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, type, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, keys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpop, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, spop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lset, arginfo_lset, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, spop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpush, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpush, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, blpop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, brpop, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, blpop, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, brpop, arginfo_blrpop, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpushx, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, linsert, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lindex, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, linsert, arginfo_linsert, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lindex, arginfo_lindex, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lrem, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, brpoplpush, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rpoplpush, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, llen, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, scard, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, scard, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, smembers, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sismember, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sadd, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, saddarray, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, saddarray, arginfo_sadd_array, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, srem, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sunion, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sunionstore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sinter, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sinterstore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sdiff, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sdiffstore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, srandmember, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sunion, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sunionstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sinter, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sinterstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sdiff, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sdiffstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, srandmember, arginfo_srand_member, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, persist, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC) @@ -220,7 +237,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, time, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, randomkey, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ping, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, echo, arginfo_echo, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rawcommand, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, cluster, NULL, ZEND_ACC_PUBLIC) From 81a03035cadbc1e9189a0b17b22c02a32eb18b15 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 29 Aug 2017 10:40:38 +0300 Subject: [PATCH 0841/1986] More arginfo for RedisCluster This commit finally fixes issue #1055 --- common.h | 322 +++++++++++++++++++++++++++++++++++++++++++++++ redis.c | 325 +----------------------------------------------- redis_cluster.c | 163 ++++++++++++------------ 3 files changed, 410 insertions(+), 400 deletions(-) diff --git a/common.h b/common.h index 0bdcc6dace..2bc34e7b74 100644 --- a/common.h +++ b/common.h @@ -704,6 +704,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key_expire_value, 0, 0, 3) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_newkey, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, newkey) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_pairs, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, pairs, 0) ZEND_END_ARG_INFO() @@ -727,10 +732,64 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_dst_nkeys, 0, 0, 2) #endif ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_min_max, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, min) + ZEND_ARG_INFO(0, max) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member_value, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_members, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_members) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_timestamp, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, timestamp) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset_value, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_start_end, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_echo, 0, 0, 1) ZEND_ARG_INFO(0, msg) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_expire, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) @@ -788,4 +847,267 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_srand_member, 0, 0, 1) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_zadd, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, score) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zincrby, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, member) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_hmget, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, keys, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_hmset, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, pairs, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_bitop, 0, 0, 3) + ZEND_ARG_INFO(0, operation) + ZEND_ARG_INFO(0, ret_key) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_bitpos, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, bit) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ltrim, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, stop) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_publish, 0, 0, 2) + ZEND_ARG_INFO(0, channel) + ZEND_ARG_INFO(0, message) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pfadd, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, elements, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pfmerge, 0, 0, 2) + ZEND_ARG_INFO(0, dstkey) + ZEND_ARG_ARRAY_INFO(0, keys, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_restore, 0, 0, 3) + ZEND_ARG_INFO(0, ttl) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_smove, 0, 0, 3) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zrange, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) + ZEND_ARG_INFO(0, scores) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zrangebyscore, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) + ZEND_ARG_ARRAY_INFO(0, options, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zrangebylex, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, min) + ZEND_ARG_INFO(0, max) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, limit) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zstore, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, keys, 0) + ZEND_ARG_ARRAY_INFO(0, weights, 1) + ZEND_ARG_INFO(0, aggregate) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sort, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, options, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_object, 0, 0, 2) + ZEND_ARG_INFO(0, field) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_subscribe, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, channels, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_psubscribe, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, patterns, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_unsubscribe, 0, 0, 1) + ZEND_ARG_INFO(0, channel) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_channels) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_punsubscribe, 0, 0, 1) + ZEND_ARG_INFO(0, pattern) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_patterns) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_eval, 0, 0, 1) + ZEND_ARG_INFO(0, script) + ZEND_ARG_INFO(0, args) + ZEND_ARG_INFO(0, num_keys) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_evalsha, 0, 0, 1) + ZEND_ARG_INFO(0, script_sha) + ZEND_ARG_INFO(0, args) + ZEND_ARG_INFO(0, num_keys) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_getoption, 0, 0, 1) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_setoption, 0, 0, 2) + ZEND_ARG_INFO(0, option) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_watch, 0, 0, 1) + ZEND_ARG_INFO(0, key) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_keys) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_command, 0, 0, 0) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_rawcommand, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2) + ZEND_ARG_INFO(0, cmd) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1) + ZEND_ARG_INFO(0, arg) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_geoadd, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, lng) + ZEND_ARG_INFO(0, lat) + ZEND_ARG_INFO(0, member) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_triples) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_geodist, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, unit) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_georadius, 0, 0, 5) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, lng) + ZEND_ARG_INFO(0, lan) + ZEND_ARG_INFO(0, radius) + ZEND_ARG_INFO(0, unit) + ZEND_ARG_ARRAY_INFO(0, opts, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_georadiusbymember, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, radius) + ZEND_ARG_INFO(0, unit) + ZEND_ARG_ARRAY_INFO(0, opts, 0) +ZEND_END_ARG_INFO() + #endif diff --git a/redis.c b/redis.c index e2e3dcc076..c4b26788e6 100644 --- a/redis.c +++ b/redis.c @@ -82,11 +82,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 2) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_newkey, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_END_ARG_INFO() @@ -109,32 +104,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) #endif ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_start_end, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset_value, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1) ZEND_ARG_INFO(0, pattern) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_sort, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, options, 0) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_generic_sort, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, pattern) @@ -150,31 +123,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_lrem, 0, 0, 3) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_ltrim, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, stop) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_smove, 0, 0, 3) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_set_timeout, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_auth, 0, 0, 1) ZEND_ARG_INFO(0, password) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0) - ZEND_ARG_INFO(0, option) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1) ZEND_ARG_INFO(0, dbindex) ZEND_END_ARG_INFO() @@ -189,180 +141,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_slaveof, 0, 0, 0) ZEND_ARG_INFO(0, port) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_object, 0, 0, 2) - ZEND_ARG_INFO(0, field) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_bitop, 0, 0, 3) - ZEND_ARG_INFO(0, operation) - ZEND_ARG_INFO(0, ret_key) - ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_bitpos, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, bit) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_eval, 0, 0, 1) - ZEND_ARG_INFO(0, script) - ZEND_ARG_INFO(0, args) - ZEND_ARG_INFO(0, num_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_evalsha, 0, 0, 1) - ZEND_ARG_INFO(0, script_sha) - ZEND_ARG_INFO(0, args) - ZEND_ARG_INFO(0, num_keys) -ZEND_END_ARG_INFO() /* }}} */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_zadd, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, score) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_zrange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, scores) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_zrangebyscore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_ARRAY_INFO(0, options, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_zrangebylex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_min_max, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_members, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_members) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member_value, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_hmget, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, keys, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_hmset, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, pairs, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_zstore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, keys, 0) - ZEND_ARG_ARRAY_INFO(0, weights, 1) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_zincrby, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_key_timestamp, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_watch, 0, 0, 1) - ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_publish, 0, 0, 2) - ZEND_ARG_INFO(0, channel) - ZEND_ARG_INFO(0, message) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_subscribe, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, channels, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_psubscribe, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, patterns, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_unsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, channel) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_channels) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_punsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, pattern) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_patterns) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_restore, 0, 0, 3) - ZEND_ARG_INFO(0, ttl) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_migrate, 0, 0, 5) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) @@ -373,112 +153,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_migrate, 0, 0, 5) ZEND_ARG_INFO(0, replace) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_command, 0, 0, 0) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pfadd, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, elements, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pfmerge, 0, 0, 2) - ZEND_ARG_INFO(0, dstkey) - ZEND_ARG_ARRAY_INFO(0, keys, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_getoption, 0, 0, 1) - ZEND_ARG_INFO(0, option) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_setoption, 0, 0, 2) - ZEND_ARG_INFO(0, option) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2) - ZEND_ARG_INFO(0, cmd) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1) - ZEND_ARG_INFO(0, arg) - ZEND_ARG_INFO(0, option) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_rawcommand, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_wait, 0, 0, 2) ZEND_ARG_INFO(0, numslaves) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_geoadd, 0, 0, 4) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, lng) - ZEND_ARG_INFO(0, lat) - ZEND_ARG_INFO(0, member) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, other_triples) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_geodist, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, unit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_georadius, 0, 0, 5) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, lng) - ZEND_ARG_INFO(0, lan) - ZEND_ARG_INFO(0, radius) - ZEND_ARG_INFO(0, unit) - ZEND_ARG_ARRAY_INFO(0, opts, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_georadiusbymember, 0, 0, 4) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, radius) - ZEND_ARG_INFO(0, unit) - ZEND_ARG_ARRAY_INFO(0, opts, 0) -ZEND_END_ARG_INFO() - /** * Argument info for the SCAN proper */ @@ -570,7 +249,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, sUnionStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sDiff, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sDiffStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setTimeout, arginfo_set_timeout, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setTimeout, arginfo_expire, ZEND_ACC_PUBLIC) PHP_ME(Redis, save, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, bgSave, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, lastSave, arginfo_void, ZEND_ACC_PUBLIC) @@ -722,7 +401,7 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, lLen, lSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, mget, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, expire, setTimeout, arginfo_set_timeout, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, expire, setTimeout, arginfo_expire, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zunionstore, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zinterstore, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index b4d4068e18..a431dc32a9 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -65,6 +65,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1) ZEND_ARG_INFO(0, pattern) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_cluster, 0, 0, 1) + ZEND_ARG_INFO(0, arg) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, other_args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) ZEND_ARG_INFO(0, str_key) @@ -132,76 +141,76 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pttl, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcard, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zcount, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zremrangebyscore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zscore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zadd, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zincrby, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zcount, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zremrangebyscore, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zscore, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zincrby, arginfo_zincrby, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hlen, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hkeys, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hvals, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hget, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hget, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, hgetall, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hexists, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hincrby, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hsetnx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hmget, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hmset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hdel, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hincrbyfloat, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hstrlen, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hexists, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hincrby, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hset, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hsetnx, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hmget, arginfo_hmget, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hmset, arginfo_hmset, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hdel, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hincrbyfloat, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hstrlen, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dump, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrank, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrevrank, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrank, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrank, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, incr, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, decr, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, incrby, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, decrby, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, incrbyfloat, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, expire, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pexpire, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, expireat, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pexpireat, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, expire, arginfo_expire, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, expireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pexpireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getbit, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setbit, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bitop, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bitpos, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getbit, arginfo_key_offset, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setbit, arginfo_key_offset_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitop, arginfo_bitop, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitcount, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lget, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getrange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, ltrim, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lrange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zremrangebyrank, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, publish, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rename, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, renamenx, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pfcount, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pfadd, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pfmerge, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setrange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, restore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, smove, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrevrange, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrangebyscore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrevrangebyscore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrangebylex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrevrangebylex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zlexcount, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zremrangebylex, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zunionstore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zinterstore, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrem, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sort, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, object, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, subscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, psubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, unsubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, punsubscribe, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, eval, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, evalsha, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lget, arginfo_lindex, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zremrangebyrank, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, publish, arginfo_publish, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, renamenx, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfcount, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setrange, arginfo_key_offset_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, restore, arginfo_restore, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, smove, arginfo_smove, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrange, arginfo_zrange, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrange, arginfo_zrange, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zlexcount, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zremrangebylex, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrem, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sort, arginfo_sort, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, object, arginfo_object, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, eval, arginfo_eval, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, scan, arginfo_scan_cl, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) @@ -210,8 +219,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, getmode, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getlasterror, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, clearlasterror, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getoption, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setoption, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getoption, arginfo_getoption, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setoption, arginfo_setoption, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _prefix, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC) @@ -222,7 +231,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, multi, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exec, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, discard, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, watch, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, save, arginfo_void, ZEND_ACC_PUBLIC) @@ -230,28 +239,28 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, flushdb, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, flushall, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, dbsize, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bgrewriteaof, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lastsave, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, info, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, info, arginfo_info, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, role, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, time, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, randomkey, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ping, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, echo, arginfo_echo, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rawcommand, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, cluster, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, client, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, config, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pubsub, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, script, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, slowlog, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, geoadd, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, geohash, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, geopos, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, geodist, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, georadius, NULL, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, georadiusbymember, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, command, arginfo_command, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, cluster, arginfo_cluster, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, client, arginfo_client, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, config, arginfo_config, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, script, arginfo_script, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geopos, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) PHP_FE_END }; From 3832939df1167255abe7f7c5a82fc2c9636d53ae Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 30 Aug 2017 14:50:14 -0700 Subject: [PATCH 0842/1986] Prepare for 3.1.4RC1 release --- package.xml | 120 ++++++++++++++++++++++++++++++++++++++++++++++------ php_redis.h | 2 +- 2 files changed, 109 insertions(+), 13 deletions(-) diff --git a/package.xml b/package.xml index 0f15f31c14..cbcdd1dc9d 100644 --- a/package.xml +++ b/package.xml @@ -27,25 +27,47 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2017-03-24 + 2017-08-30 - 3.1.2 - 3.1.2 + 3.1.4 + 3.1.4 stable - stable + beta PHP - phpredis 3.1.2 - - * RedisArray segfault fix [564ce3] (Pavlo Yatsukhnenko) - * Small memory leak fix [645888b] (Mike Grunder) - * Segfault fix when recreating RedisCluster objects [abf7d4] (Michael Grunder) - * Fix for RedisCluster bulk response parsing [4121c4] (Alberto Fernández) - * Re allow single array for sInterStore [6ef0c2, d01966] (Michael Grunder) - * Better TravisCI integration [4fd2f6] (Pavlo Yatsukhnenko) + phpredis 3.1.4 + + The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are + also many bugfixes and minor improvements to the api, listed below: + + * Allow mixing MULTI and PIPELINE modes! [5874b0] (Pavlo Yatsukhnenko) + * Let EXISTS take multiple keys [cccc39] (Michael Grunder) + + * Added integration for coverty static analysis and fixed several warnings + [faac8b0, eff7398, 4766c25, 0438ab4, 1e0b065, 733732a, 26eeda5, 735025, 42f1c9, af71d4] (Pavlo Yatsukhnenko) + * Added arginfo inrospection structures [81a0303, d5609fc, e5660be, 3c60e1f, 50dcb15, 6c2c6fa, + 212e323, e23be2c, 682593d, f8de702, 4ef3acd, f116be9, 5c111dd, 9caa029, 0d69650, 6859828, 024e593, + 3643ab6, f576fab, 122d41f, a09d0e6] (Tyson Andre, Pavlo Yatsukhnenko) + * Fixed link to redis cluster documentation [3b0b06] (Pavlo Yatsukhnenko) + * Remove unused PHP_RINIT and PHP_RSHUTDOWN functions [c760bf] (Pavlo Yatsukhnenko) + * Removed duplicate HGET in redis array hash table, formatting [d0b9c5] (Pavlo Yatsukhnenko) + * Treat NULL bulk as success for session read [659450] (Pavlo Yatsukhnenko) + * Refactor redis_send_discard [ea15ce] (Pavlo Yatsukhnenko) + * Updated runtime exception handling [8dcaa4, 7c1407] (Pavlo Yatsukhnenko) + * Added a github issue template [61aba9] (Pavlo Yatsukhnenko) + * Initialize gc member of zend_string [37f569) (Pavlo Yatsukhnenko) + * Fix valgrind warnings [471ce07, 1ab89e1, b624a8b] (Pavlo Yatsukhnenko) + * Fix php5/php7 compatibility layer [1ab89e, 4e3225] (Pavlo Yatsukhnenko) + * Fix typo in README.markdown [e47e44] (Mark Shehata) + * Improve redis array rehash [577a91] (Pavlo Yatsukhnenko) + * Change redis array pure_cmds from zval to hashtable [a56ed7] (Pavlo Yatsukhnenko) + * Don't try to set TCP_NODELAY on a unix socket and don't warn on multiple + calls to pipeline [d11798, 77aeba] (Michael Grunder) + * Use zend_string rather than char* for various context fields (err, prefix, etc) [2bf7b2] (Pavlo Yatsukhnenko) + * Various other library fixes [142b51, 4452f6, e672f4, 658ee3, c9df77, 4a0a46] (Pavlo Yatsukhnenko) @@ -101,6 +123,80 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + stablebeta + 3.1.4RC13.1.4RC1 + 2017-08-30 + + phpredis 3.1.4 + + The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are + also many bugfixes and minor improvements to the api, listed below: + + * Allow mixing MULTI and PIPELINE modes! [5874b0] (Pavlo Yatsukhnenko) + * Let EXISTS take multiple keys [cccc39] (Michael Grunder) + + * Added integration for coverty static analysis and fixed several warnings + [faac8b0, eff7398, 4766c25, 0438ab4, 1e0b065, 733732a, 26eeda5, 735025, 42f1c9, af71d4] (Pavlo Yatsukhnenko) + * Added arginfo inrospection structures [81a0303, d5609fc, e5660be, 3c60e1f, 50dcb15, 6c2c6fa, + 212e323, e23be2c, 682593d, f8de702, 4ef3acd, f116be9, 5c111dd, 9caa029, 0d69650, 6859828, 024e593, + 3643ab6, f576fab, 122d41f, a09d0e6] (Tyson Andre, Pavlo Yatsukhnenko) + * Fixed link to redis cluster documentation [3b0b06] (Pavlo Yatsukhnenko) + * Remove unused PHP_RINIT and PHP_RSHUTDOWN functions [c760bf] (Pavlo Yatsukhnenko) + * Removed duplicate HGET in redis array hash table, formatting [d0b9c5] (Pavlo Yatsukhnenko) + * Treat NULL bulk as success for session read [659450] (Pavlo Yatsukhnenko) + * Refactor redis_send_discard [ea15ce] (Pavlo Yatsukhnenko) + * Updated runtime exception handling [8dcaa4, 7c1407] (Pavlo Yatsukhnenko) + * Added a github issue template [61aba9] (Pavlo Yatsukhnenko) + * Initialize gc member of zend_string [37f569) (Pavlo Yatsukhnenko) + * Fix valgrind warnings [471ce07, 1ab89e1, b624a8b] (Pavlo Yatsukhnenko) + * Fix php5/php7 compatibility layer [1ab89e, 4e3225] (Pavlo Yatsukhnenko) + * Fix typo in README.markdown [e47e44] (Mark Shehata) + * Improve redis array rehash [577a91] (Pavlo Yatsukhnenko) + * Change redis array pure_cmds from zval to hashtable [a56ed7] (Pavlo Yatsukhnenko) + * Don't try to set TCP_NODELAY on a unix socket and don't warn on multiple + calls to pipeline [d11798, 77aeba] (Michael Grunder) + * Use zend_string rather than char* for various context fields (err, prefix, etc) [2bf7b2] (Pavlo Yatsukhnenko) + * Various other library fixes [142b51, 4452f6, e672f4, 658ee3, c9df77, 4a0a46] (Pavlo Yatsukhnenko) + + + + + stablestable + 3.1.33.1.3 + 2017-07-15 + + phpredis 3.1.3 + + This release contains two big improvements: + 1. Adding a new printf like command construction function with additionaly format specifiers specific to phpredis. + 2. Implementation of custom objects for Redis and RedisArray wich eliminates double hash lookup. + Also many small improvements and bug fixes were made. + + * A printf like method to construct a Redis RESP command [a4a0ed, d75081, bdd287, 0eaeae, b3d00d] (Michael Grunder) + * Use custom objects instead of zend_list for storing Redis/RedisArray [a765f8, 8fa85a] (Pavlo Yatsukhnenko) + * Make sure redisCluster members are all initialized on (re)creation [162d88] (Michael Grunder) + * Fix Null Bulk String response parsing in cluster library [058753] (Alberto Fernández) + * Add hStrLen command [c52077, fb88e1] (Pavlo Yatsukhnenko) + * Add optional COUNT argument to sPop [d2e203] (Michael Grunder) + * Allow sInterStore to take one arg [26aec4, 4cd06b] (Michael Grunder) + * Allow MIGRATE to accept multiple keys [9aa3db] (Michael Grunder) + * Allow using numeric string in zInter command [ba0070] (Pavlo Yatsukhnenko) + * Use crc32 table from PHP distro [f81694] (Pavlo Yatsukhnenko) + * Use ZVAL_DEREF macros for dereference input variables [ad4596] (Pavlo Yatsukhnenko) + * Add configureoption tag to package.xml [750963] (Pavlo Yatsukhnenko) + * Fix read_timeout [18149e, b56dc4] (Pavlo Yatsukhnenko) + * Fix zval_get_string impl for PHP5 [4e56ba] (Pavlo Yatsukhnenko) + * Fix Redis/RedisArray segfaults [be5c1f, 635c3a, 1f8dde, 43e1e0] (Pavlo Yatsukhnenko) + * Fix memory leak and potential segfault [aa6ff7, 88efaa] (Michael Grunder) + * Throw exception for all non recoverable errors [e37239] (Pavlo Yatsukhnenko) + * Assume "NULL bulk" reply as success (empty session data) [4a81e1] (Pavlo Yatsukhnenko) + * Increase read buffers size [520e06] (Pavlo Yatsukhnenko) + * Better documentation [f0c25a, c5991f, 9ec9ae] (Michael Grunder) + * Better TravisCI integration [e37c08] (Pavlo Yatsukhnenko) + * Refactoring (Pavlo Yatsukhnenko, Michael Grunder) + + stablestable diff --git a/php_redis.h b/php_redis.h index 895ad710f7..0daf159424 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "develop" +#define PHP_REDIS_VERSION "3.1.4RC1" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 5d1813f1b7bcbbb0764a7e56a9d82d7f8f40e218 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 30 Aug 2017 19:53:40 -0700 Subject: [PATCH 0843/1986] Pecl wants the date in GMT --- package.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.xml b/package.xml index cbcdd1dc9d..12c2863adb 100644 --- a/package.xml +++ b/package.xml @@ -27,7 +27,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2017-08-30 + 2017-08-31 3.1.4 3.1.4 @@ -126,7 +126,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> stablebeta 3.1.4RC13.1.4RC1 - 2017-08-30 + 2017-08-31 phpredis 3.1.4 From 0141aae2684eae5b0643eecade6e7038a74ac003 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 30 Aug 2017 20:25:13 -0700 Subject: [PATCH 0844/1986] Use correct RC specifier/beta stability everywhere for this release --- package.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.xml b/package.xml index 12c2863adb..fad5f6c8be 100644 --- a/package.xml +++ b/package.xml @@ -29,16 +29,16 @@ http://pear.php.net/dtd/package-2.0.xsd"> 2017-08-31 - 3.1.4 - 3.1.4 + 3.1.4RC1 + 3.1.4RC1 - stable + beta beta PHP - phpredis 3.1.4 + phpredis 3.1.4RC1 The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are also many bugfixes and minor improvements to the api, listed below: @@ -124,11 +124,11 @@ http://pear.php.net/dtd/package-2.0.xsd"> - stablebeta + betabeta 3.1.4RC13.1.4RC1 2017-08-31 - phpredis 3.1.4 + phpredis 3.1.4RC1 The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are also many bugfixes and minor improvements to the api, listed below: From 837b1ae51fb2e79849f35cc21f373a4c3187f828 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2017 16:47:47 +0300 Subject: [PATCH 0845/1986] Add missed ini params and sort them alphabetically --- redis.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/redis.c b/redis.c index c4b26788e6..fffe7674fa 100644 --- a/redis.c +++ b/redis.c @@ -55,17 +55,24 @@ extern zend_function_entry redis_cluster_functions[]; PHP_INI_BEGIN() /* redis arrays */ - PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.connecttimeout", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.index", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.lazyconnect", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.pconnect", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.readtimeout", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.retryinterval", "", PHP_INI_ALL, NULL) /* redis cluster */ + PHP_INI_ENTRY("redis.clusters.persistent", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.timeout", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL) PHP_INI_END() /** {{{ Argument info for commands in redis 1.0 */ From 3ec05496f2f7dffc3f231e0b530702015731a82e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 27 Sep 2017 10:03:32 +0300 Subject: [PATCH 0846/1986] Sort arginfos alphabetically --- redis.c | 383 ++++++++++++++++++++++-------------------------- redis_array.c | 39 ++--- redis_cluster.c | 284 ++++++++++++++++++----------------- 3 files changed, 331 insertions(+), 375 deletions(-) diff --git a/redis.c b/redis.c index fffe7674fa..70000a2b6c 100644 --- a/redis.c +++ b/redis.c @@ -191,250 +191,217 @@ ZEND_DECLARE_MODULE_GLOBALS(redis) static zend_function_entry redis_functions[] = { PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC) - PHP_ME(Redis, connect, arginfo_connect, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) + PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, append, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, auth, arginfo_auth, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bgSave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bitcount, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bitop, arginfo_bitop, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC) + PHP_ME(Redis, blPop, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(Redis, brPop, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(Redis, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) + PHP_ME(Redis, clearLastError, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, client, arginfo_client, ZEND_ACC_PUBLIC) PHP_ME(Redis, close, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, ping, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, command, arginfo_command, ZEND_ACC_PUBLIC) + PHP_ME(Redis, config, arginfo_config, ZEND_ACC_PUBLIC) + PHP_ME(Redis, connect, arginfo_connect, ZEND_ACC_PUBLIC) + PHP_ME(Redis, dbSize, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, debug, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, decr, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, decrBy, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, delete, arginfo_del, ZEND_ACC_PUBLIC) + PHP_ME(Redis, discard, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, dump, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, echo, arginfo_echo, ZEND_ACC_PUBLIC) + PHP_ME(Redis, eval, arginfo_eval, ZEND_ACC_PUBLIC) + PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) + PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, exists, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(Redis, flushAll, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, flushDB, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(Redis, geopos, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(Redis, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) + PHP_ME(Redis, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) PHP_ME(Redis, get, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, set, arginfo_set, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setnx, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getSet, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, randomKey, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) - PHP_ME(Redis, renameNx, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getAuth, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getBit, arginfo_key_offset, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getDBNum, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getHost, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getKeys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getLastError, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getMode, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) - PHP_ME(Redis, exists, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, delete, arginfo_del, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getOption, arginfo_getoption, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getPersistentID, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getPort, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getReadTimeout, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getSet, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, getTimeout, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hDel, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hExists, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hGet, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hGetAll, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hIncrBy, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hIncrByFloat, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hKeys, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hLen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hMget, arginfo_hmget, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hMset, arginfo_hmset, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hSet, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hSetNx, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hStrLen, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hVals, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(Redis, incr, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, incrBy, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, incrByFloat, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, decr, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, decrBy, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, append, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setRange, arginfo_key_offset_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getBit, arginfo_key_offset, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setBit, arginfo_key_offset_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getKeys, arginfo_keys, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sort, arginfo_sort, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortAsc, arginfo_generic_sort, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, info, arginfo_info, ZEND_ACC_PUBLIC) + PHP_ME(Redis, isConnected, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lGet, arginfo_lindex, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lPop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, lPush, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rPush, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, lPushx, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rPushx, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lPop, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rPop, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, blPop, arginfo_blrpop, ZEND_ACC_PUBLIC) - PHP_ME(Redis, brPop, arginfo_blrpop, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) - PHP_ME(Redis, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lGet, arginfo_lindex, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) PHP_ME(Redis, lSet, arginfo_lset, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lSize, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lastSave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_ME(Redis, migrate, arginfo_migrate, ZEND_ACC_PUBLIC) + PHP_ME(Redis, move, arginfo_move, ZEND_ACC_PUBLIC) + PHP_ME(Redis, mset, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(Redis, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(Redis, multi, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, object, arginfo_object, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) + PHP_ME(Redis, persist, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pexpireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pfcount, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC) + PHP_ME(Redis, ping, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pipeline, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pttl, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, publish, arginfo_publish, ZEND_ACC_PUBLIC) + PHP_ME(Redis, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC) + PHP_ME(Redis, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rPop, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rPush, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rPushx, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, randomKey, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) + PHP_ME(Redis, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(Redis, renameNx, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(Redis, restore, arginfo_restore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, role, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC) PHP_ME(Redis, sAdd, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, sAddArray, arginfo_sadd_array, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sSize, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, sContains, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sMembers, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sDiff, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sDiffStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sInter, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sInterStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sMembers, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, sUnion, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sUnionStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sDiff, arginfo_nkeys, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sDiffStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setTimeout, arginfo_expire, ZEND_ACC_PUBLIC) PHP_ME(Redis, save, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bgSave, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lastSave, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, flushDB, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, flushAll, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, dbSize, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, auth, arginfo_auth, ZEND_ACC_PUBLIC) - PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pttl, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, persist, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, info, arginfo_info, ZEND_ACC_PUBLIC) + PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) + PHP_ME(Redis, script, arginfo_script, ZEND_ACC_PUBLIC) PHP_ME(Redis, select, arginfo_select, ZEND_ACC_PUBLIC) - PHP_ME(Redis, move, arginfo_move, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, set, arginfo_set, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setBit, arginfo_key_offset_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setOption, arginfo_setoption, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setRange, arginfo_key_offset_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setTimeout, arginfo_expire, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, setnx, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, slaveof, arginfo_slaveof, ZEND_ACC_PUBLIC) - PHP_ME(Redis, object, arginfo_object, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bitop, arginfo_bitop, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bitcount, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC) - - /* 1.1 */ - PHP_ME(Redis, mset, arginfo_pairs, ZEND_ACC_PUBLIC) - PHP_ME(Redis, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC) - PHP_ME(Redis, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC) - PHP_ME(Redis, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) + PHP_ME(Redis, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sort, arginfo_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortAsc, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) + PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC) + PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC) + PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC) + PHP_ME(Redis, watch, arginfo_watch, ZEND_ACC_PUBLIC) PHP_ME(Redis, zAdd, arginfo_zadd, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zCard, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_ME(Redis, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zDeleteRangeByRank, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zIncrBy, arginfo_zincrby, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zLexCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRevRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRevRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zLexCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zRemRangeByLex, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDeleteRangeByRank, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zCard, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRank, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRemRangeByLex, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRevRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRank, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zIncrBy, arginfo_zincrby, ZEND_ACC_PUBLIC) - PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pexpireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - - /* 1.2 */ - PHP_ME(Redis, hGet, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hSet, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hSetNx, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hDel, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hLen, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hKeys, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hVals, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hGetAll, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hExists, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hIncrBy, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hIncrByFloat, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hMset, arginfo_hmset, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hMget, arginfo_hmget, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hStrLen, arginfo_key_member, ZEND_ACC_PUBLIC) - - PHP_ME(Redis, multi, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, discard, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pipeline, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, watch, arginfo_watch, ZEND_ACC_PUBLIC) - PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC) - - PHP_ME(Redis, publish, arginfo_publish, ZEND_ACC_PUBLIC) - PHP_ME(Redis, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC) - PHP_ME(Redis, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC) - PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(Redis, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC) - - PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, role, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, eval, arginfo_eval, ZEND_ACC_PUBLIC) - PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) - PHP_ME(Redis, script, arginfo_script, ZEND_ACC_PUBLIC) - - PHP_ME(Redis, debug, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, dump, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, restore, arginfo_restore, ZEND_ACC_PUBLIC) - PHP_ME(Redis, migrate, arginfo_migrate, ZEND_ACC_PUBLIC) - - PHP_ME(Redis, getLastError, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, clearLastError, arginfo_void, ZEND_ACC_PUBLIC) - - PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) - - PHP_ME(Redis, client, arginfo_client, ZEND_ACC_PUBLIC) - PHP_ME(Redis, command, arginfo_command, ZEND_ACC_PUBLIC) - - /* SCAN and friends */ - PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) - PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) - - /* HyperLogLog commands */ - PHP_ME(Redis, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pfcount, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC) - - /* options */ - PHP_ME(Redis, getOption, arginfo_getoption, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setOption, arginfo_setoption, ZEND_ACC_PUBLIC) - - /* config */ - PHP_ME(Redis, config, arginfo_config, ZEND_ACC_PUBLIC) - - /* slowlog */ - PHP_ME(Redis, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) - - /* Send a raw command and read raw results */ - PHP_ME(Redis, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) - - /* geoadd and friends */ - PHP_ME(Redis, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) - PHP_ME(Redis, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(Redis, geopos, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(Redis, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) - PHP_ME(Redis, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) - PHP_ME(Redis, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) - - /* introspection */ - PHP_ME(Redis, getHost, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getPort, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getDBNum, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getTimeout, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getReadTimeout, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getPersistentID, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getAuth, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, isConnected, arginfo_void, ZEND_ACC_PUBLIC) - /* TODO: document getMode() and wait() in README? */ - PHP_ME(Redis, getMode, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC) - PHP_ME(Redis, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC) - - /* aliases */ - PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lLen, lSize, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, mget, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, expire, setTimeout, arginfo_expire, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zunionstore, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zinterstore, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) - - PHP_MALIAS(Redis, zRemove, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRem, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemoveRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, rename, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, del, delete, arginfo_del, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, expire, setTimeout, arginfo_expire, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, keys, getKeys, arginfo_keys, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lrem, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, ltrim, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lLen, lSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, lindex, lGet, arginfo_lindex, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, lrange, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lrem, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, ltrim, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, mget, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, rename, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, scard, sSize, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, srem, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sismember, sContains, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, srem, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRem, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemove, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemoveRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zReverseRange, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) - - PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC) - - PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zinterstore, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zunionstore, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_FE_END }; diff --git a/redis_array.c b/redis_array.c index 73dad31177..cd2f2453dc 100644 --- a/redis_array.c +++ b/redis_array.c @@ -98,38 +98,31 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_multi, 0, 0, 1) ZEND_END_ARG_INFO() zend_function_entry redis_array_functions[] = { - PHP_ME(RedisArray, __construct, arginfo_ctor, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, __call, arginfo_call, ZEND_ACC_PUBLIC) - + PHP_ME(RedisArray, __construct, arginfo_ctor, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _distributor, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _function, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, _hosts, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _target, arginfo_target, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, _instance, arginfo_instance, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _function, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, _distributor, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, _rehash, arginfo_rehash, ZEND_ACC_PUBLIC) - - /* special implementation for a few functions */ - PHP_ME(RedisArray, select, arginfo_select, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, info, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, ping, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, flushdb, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, flushall, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, mget, arginfo_mget, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, mset, arginfo_mset, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _target, arginfo_target, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, bgsave, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, del, arginfo_del, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, discard, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, exec, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, flushall, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, flushdb, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, getOption, arginfo_getopt, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, setOption,arginfo_setopt, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, info, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, keys, arginfo_keys, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, save, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, bgsave, arginfo_void, ZEND_ACC_PUBLIC) - - /* Multi/Exec */ + PHP_ME(RedisArray, mget, arginfo_mget, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, mset, arginfo_mset, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, multi, arginfo_multi, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, exec, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, discard, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, ping, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, save, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, select, arginfo_select, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, setOption,arginfo_setopt, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, unwatch, arginfo_void, ZEND_ACC_PUBLIC) - - /* Aliases */ PHP_MALIAS(RedisArray, delete, del, arginfo_del, ZEND_ACC_PUBLIC) PHP_MALIAS(RedisArray, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC) PHP_FE_END diff --git a/redis_cluster.c b/redis_cluster.c index a431dc32a9..b241e561cf 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -93,174 +93,170 @@ ZEND_END_ARG_INFO(); /* Function table */ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, __construct, arginfo_ctor, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _masters, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _prefix, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _redir, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bgsave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitcount, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitop, arginfo_bitop, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, blpop, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, brpop, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, clearlasterror, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, client, arginfo_client, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, close, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, get, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, set, arginfo_set, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, mget, arginfo_mget, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, mset, arginfo_pairs, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, cluster, arginfo_cluster, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, command, arginfo_command, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, config, arginfo_config, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, dbsize, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, decr, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, decrby, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, del, arginfo_del, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setnx, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getset, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, discard, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, dump, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, echo, arginfo_echo, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, eval, arginfo_eval, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, exec, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, exists, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, expire, arginfo_expire, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, expireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushall, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushdb, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, geopos, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, get, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getbit, arginfo_key_offset, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getlasterror, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getmode, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getoption, arginfo_getoption, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getset, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hdel, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hexists, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hget, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hgetall, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hincrby, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hincrbyfloat, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hkeys, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hlen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hmget, arginfo_hmget, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hmset, arginfo_hmset, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hset, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hsetnx, arginfo_key_member_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hstrlen, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, hvals, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incr, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incrby, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, incrbyfloat, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, info, arginfo_info, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, keys, arginfo_keys, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lastsave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lget, arginfo_lindex, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lindex, arginfo_lindex, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, linsert, arginfo_linsert, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, llen, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpop, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rpop, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lset, arginfo_lset, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, spop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpush, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rpush, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, blpop, arginfo_blrpop, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, brpop, arginfo_blrpop, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rpushx, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lpushx, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, linsert, arginfo_linsert, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lindex, arginfo_lindex, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lrem, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lset, arginfo_lset, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, mget, arginfo_mget, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, mset, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, multi, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, object, arginfo_object, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, persist, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pexpireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfcount, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ping, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pttl, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, publish, arginfo_publish, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, randomkey, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, renamenx, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, restore, arginfo_restore, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, role, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, llen, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, scard, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, smembers, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sismember, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpush, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, rpushx, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sadd, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, saddarray, arginfo_sadd_array, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, srem, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sunion, arginfo_nkeys, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sunionstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sinter, arginfo_nkeys, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sinterstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, save, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, scan, arginfo_scan_cl, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, scard, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, script, arginfo_script, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sdiff, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sdiffstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, set, arginfo_set, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setbit, arginfo_key_offset_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setnx, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setoption, arginfo_setoption, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, setrange, arginfo_key_offset_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sinter, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sinterstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sismember, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, smembers, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, smove, arginfo_smove, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sort, arginfo_sort, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, spop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, srandmember, arginfo_srand_member, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, srem, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, strlen, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, persist, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sunion, arginfo_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, sunionstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, time, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pttl, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcard, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcount, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zremrangebyscore, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zscore, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zincrby, arginfo_zincrby, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hlen, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hkeys, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hvals, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hget, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hgetall, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hexists, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hincrby, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hset, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hsetnx, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hmget, arginfo_hmget, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hmset, arginfo_hmset, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hdel, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hincrbyfloat, arginfo_key_member_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hstrlen, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, dump, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zlexcount, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrange, arginfo_zrange, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrank, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrevrank, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, incr, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, decr, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, incrby, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, decrby, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, incrbyfloat, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, expire, arginfo_expire, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, expireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pexpireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getbit, arginfo_key_offset, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setbit, arginfo_key_offset_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bitop, arginfo_bitop, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bitcount, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lget, arginfo_lindex, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrem, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zremrangebylex, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zremrangebyrank, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, publish, arginfo_publish, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, renamenx, arginfo_key_newkey, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pfcount, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setrange, arginfo_key_offset_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, restore, arginfo_restore, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, smove, arginfo_smove, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrange, arginfo_zrange, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zremrangebyscore, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrange, arginfo_zrange, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrevrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrevrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zlexcount, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zremrangebylex, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, zrem, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sort, arginfo_sort, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, object, arginfo_object, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, eval, arginfo_eval, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, scan, arginfo_scan_cl, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, sscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zrevrank, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, hscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC) - - PHP_ME(RedisCluster, getmode, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getlasterror, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, clearlasterror, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getoption, arginfo_getoption, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, setoption, arginfo_setoption, ZEND_ACC_PUBLIC) - - PHP_ME(RedisCluster, _prefix, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _masters, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, _redir, arginfo_void, ZEND_ACC_PUBLIC) - - PHP_ME(RedisCluster, multi, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, exec, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, discard, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC) - - PHP_ME(RedisCluster, save, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bgsave, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, flushdb, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, flushall, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, dbsize, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lastsave, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, info, arginfo_info, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, role, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, time, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, randomkey, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, ping, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, echo, arginfo_echo, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, command, arginfo_command, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, cluster, arginfo_cluster, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, client, arginfo_client, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, config, arginfo_config, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, script, arginfo_script, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, geopos, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zscore, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_FE_END }; From 309cb64332374b1721d912ca8e128305592ceb25 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 28 Sep 2017 10:09:20 +0300 Subject: [PATCH 0847/1986] Issue #1193 --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index b241e561cf..b6b4963871 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -502,7 +502,7 @@ PHP_METHOD(RedisCluster, __construct) { // Parse arguments if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|addb", &object, redis_cluster_ce, &name, + "Os!|addb", &object, redis_cluster_ce, &name, &name_len, &z_seeds, &timeout, &read_timeout, &persistent)==FAILURE) { From 5241bcb883c0409777170fc8f424ef1e4172b594 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 28 Sep 2017 16:53:00 +0300 Subject: [PATCH 0848/1986] Fix RA memory leak --- redis_array.c | 1 + 1 file changed, 1 insertion(+) diff --git a/redis_array.c b/redis_array.c index cd2f2453dc..9a2e064ac2 100644 --- a/redis_array.c +++ b/redis_array.c @@ -215,6 +215,7 @@ free_redis_array_object(zend_object *object) redis_array_free(obj->ra); } zend_object_std_dtor(&obj->std TSRMLS_CC); + zend_object_release(object); } zend_object * From 6a53cb99baa45309ab509d4f2076b9c086ff1b6b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 28 Sep 2017 22:33:31 +0300 Subject: [PATCH 0849/1986] Revert "Fix RA memory leak" This reverts commit 5241bcb883c0409777170fc8f424ef1e4172b594. --- redis_array.c | 1 - 1 file changed, 1 deletion(-) diff --git a/redis_array.c b/redis_array.c index 9a2e064ac2..cd2f2453dc 100644 --- a/redis_array.c +++ b/redis_array.c @@ -215,7 +215,6 @@ free_redis_array_object(zend_object *object) redis_array_free(obj->ra); } zend_object_std_dtor(&obj->std TSRMLS_CC); - zend_object_release(object); } zend_object * From 345fc7fc9f3296f35709070fa2de1d92a6da0628 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 28 Sep 2017 15:44:18 -0700 Subject: [PATCH 0850/1986] Destroy all tabs :-) --- library.c | 129 ++--- redis.c | 58 +-- redis_array.c | 1150 ++++++++++++++++++++++---------------------- redis_array.h | 26 +- redis_array_impl.c | 812 +++++++++++++++---------------- redis_cluster.c | 46 +- 6 files changed, 1111 insertions(+), 1110 deletions(-) diff --git a/library.c b/library.c index 84ed318bfe..e91ab0ab8e 100644 --- a/library.c +++ b/library.c @@ -1517,16 +1517,17 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) redis_sock->dbNumber = 0; if (redis_sock->stream != NULL) { - redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; - redis_sock->watching = 0; + redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; + redis_sock->watching = 0; - /* Stil valid? */ - if (!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } - redis_sock->stream = NULL; + /* Stil valid? */ + if (!redis_sock->persistent) { + php_stream_close(redis_sock->stream); + } - return 1; + redis_sock->stream = NULL; + + return 1; } return 0; @@ -1967,12 +1968,12 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, return -1; } - /* We don't need \r\n */ - *line_size-=2; - buf[*line_size]='\0'; + /* We don't need \r\n */ + *line_size-=2; + buf[*line_size]='\0'; - /* Success! */ - return 0; + /* Success! */ + return 0; } PHP_REDIS_API int @@ -2001,17 +2002,17 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, // Buffer to hold size information char inbuf[255]; - /* Read up to our newline */ - if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) == NULL) { - return -1; - } + /* Read up to our newline */ + if (php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) == NULL) { + return -1; + } - /* Set our size response */ - *reply_info = atol(inbuf); - } + /* Set our size response */ + *reply_info = atol(inbuf); + } - /* Success! */ - return 0; + /* Success! */ + return 0; } /* @@ -2025,26 +2026,26 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, char inbuf[4096]; size_t line_size; - /* Attempt to read our single line reply */ - if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { - return -1; - } + /* Attempt to read our single line reply */ + if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { + return -1; + } // If this is an error response, check if it is a SYNC error, and throw in // that case if(reply_type == TYPE_ERR) { - /* Set our last error */ - redis_sock_set_err(redis_sock, inbuf, line_size); + /* Set our last error */ + redis_sock_set_err(redis_sock, inbuf, line_size); /* Handle throwable errors */ redis_error_throw(redis_sock TSRMLS_CC); - /* Set our response to FALSE */ - ZVAL_FALSE(z_ret); - } else { - /* Set our response to TRUE */ - ZVAL_TRUE(z_ret); - } + /* Set our response to FALSE */ + ZVAL_FALSE(z_ret); + } else { + /* Set our response to TRUE */ + ZVAL_TRUE(z_ret); + } return 0; } @@ -2056,11 +2057,11 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret // Attempt to read the bulk reply char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); - /* Set our reply to FALSE on failure, and the string on success */ - if(bulk_resp == NULL) { - ZVAL_FALSE(z_ret); - return -1; - } + /* Set our reply to FALSE on failure, and the string on success */ + if(bulk_resp == NULL) { + ZVAL_FALSE(z_ret); + return -1; + } ZVAL_STRINGL(z_ret, bulk_resp, size); efree(bulk_resp); return 0; @@ -2126,9 +2127,9 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret break; } - /* Decrement our element counter */ - elements--; - } + /* Decrement our element counter */ + elements--; + } return 0; } @@ -2152,21 +2153,21 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_ret); #endif - /* Switch based on our top level reply type */ - switch(reply_type) { - case TYPE_ERR: - case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, z_ret TSRMLS_CC); - break; - case TYPE_INT: - ZVAL_LONG(z_ret, reply_info); - break; - case TYPE_BULK: - redis_read_variant_bulk(redis_sock, reply_info, z_ret TSRMLS_CC); - break; - case TYPE_MULTIBULK: - /* Initialize an array for our multi-bulk response */ - array_init(z_ret); + /* Switch based on our top level reply type */ + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + redis_read_variant_line(redis_sock, reply_type, z_ret TSRMLS_CC); + break; + case TYPE_INT: + ZVAL_LONG(z_ret, reply_info); + break; + case TYPE_BULK: + redis_read_variant_bulk(redis_sock, reply_info, z_ret TSRMLS_CC); + break; + case TYPE_MULTIBULK: + /* Initialize an array for our multi-bulk response */ + array_init(z_ret); // If we've got more than zero elements, parse our multi bulk // response recursively @@ -2185,15 +2186,15 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_ret); - } else { - /* Set our return value */ + IF_NOT_ATOMIC() { + add_next_index_zval(z_tab, z_ret); + } else { + /* Set our return value */ RETVAL_ZVAL(z_ret, 0, 1); - } + } - /* Success */ - return 0; + /* Success */ + return 0; } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/redis.c b/redis.c index 70000a2b6c..e2432005a8 100644 --- a/redis.c +++ b/redis.c @@ -2895,7 +2895,7 @@ PHP_METHOD(Redis, evalsha) { PHP_REDIS_API int redis_build_script_exists_cmd(char **ret, zval *argv, int argc) { - smart_string cmd = {0}; + smart_string cmd = {0}; zend_string *zstr; int i; @@ -2909,9 +2909,9 @@ redis_build_script_exists_cmd(char **ret, zval *argv, int argc) { zend_string_release(zstr); } - /* Success */ + /* Success */ *ret = cmd.c; - return cmd.len; + return cmd.len; } /* {{{ proto status Redis::script('flush') @@ -2925,24 +2925,24 @@ PHP_METHOD(Redis, script) { int cmd_len, argc; char *cmd; - /* Attempt to grab our socket */ + /* Attempt to grab our socket */ if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { - RETURN_FALSE; - } + RETURN_FALSE; + } - /* Grab the number of arguments */ - argc = ZEND_NUM_ARGS(); + /* Grab the number of arguments */ + argc = ZEND_NUM_ARGS(); - /* Allocate an array big enough to store our arguments */ - z_args = emalloc(argc * sizeof(zval)); + /* Allocate an array big enough to store our arguments */ + z_args = emalloc(argc * sizeof(zval)); - /* Make sure we can grab our arguments, we have a string directive */ - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || - (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING)) - { - efree(z_args); - RETURN_FALSE; - } + /* Make sure we can grab our arguments, we have a string directive */ + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || + (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING)) + { + efree(z_args); + RETURN_FALSE; + } // Branch based on the directive if(!strcasecmp(Z_STRVAL(z_args[0]), "flush") || @@ -2964,17 +2964,17 @@ PHP_METHOD(Redis, script) { // Format our SCRIPT LOAD command cmd_len = REDIS_SPPRINTF(&cmd, "SCRIPT", "ss", "LOAD", 4, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); - } else if(!strcasecmp(Z_STRVAL(z_args[0]), "exists")) { - /* Construct our SCRIPT EXISTS command */ - cmd_len = redis_build_script_exists_cmd(&cmd, &(z_args[1]), argc-1); - } else { - /* Unknown directive */ - efree(z_args); - RETURN_FALSE; - } - - /* Free our alocated arguments */ - efree(z_args); + } else if(!strcasecmp(Z_STRVAL(z_args[0]), "exists")) { + /* Construct our SCRIPT EXISTS command */ + cmd_len = redis_build_script_exists_cmd(&cmd, &(z_args[1]), argc-1); + } else { + /* Unknown directive */ + efree(z_args); + RETURN_FALSE; + } + + /* Free our alocated arguments */ + efree(z_args); // Kick off our request REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3066,7 +3066,7 @@ PHP_METHOD(Redis, getLastError) { RETURN_FALSE; } - /* Return our last error or NULL if we don't have one */ + /* Return our last error or NULL if we don't have one */ if (redis_sock->err) { RETURN_STRINGL(ZSTR_VAL(redis_sock->err), ZSTR_LEN(redis_sock->err)); } diff --git a/redis_array.c b/redis_array.c index cd2f2453dc..52713063a2 100644 --- a/redis_array.c +++ b/redis_array.c @@ -47,24 +47,24 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_call, 0, 0, 2) - ZEND_ARG_INFO(0, function_name) - ZEND_ARG_INFO(0, arguments) + ZEND_ARG_INFO(0, function_name) + ZEND_ARG_INFO(0, arguments) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_target, 0, 0, 1) - ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_instance, 0, 0, 1) - ZEND_ARG_INFO(0, host) + ZEND_ARG_INFO(0, host) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_rehash, 0, 0, 0) - ZEND_ARG_INFO(0, callable) + ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1) - ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(0, index) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1) @@ -259,59 +259,59 @@ redis_array_get(zval *id TSRMLS_DC) Public constructor */ PHP_METHOD(RedisArray, __construct) { - zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; - RedisArray *ra = NULL; - zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; - HashTable *hPrev = NULL, *hOpts = NULL; - long l_retry_interval = 0; - zend_bool b_lazy_connect = 0; - double d_connect_timeout = 0, read_timeout = 0.0; + zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; + RedisArray *ra = NULL; + zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; + HashTable *hPrev = NULL, *hOpts = NULL; + long l_retry_interval = 0; + zend_bool b_lazy_connect = 0; + double d_connect_timeout = 0, read_timeout = 0.0; redis_array_object *obj; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { + RETURN_FALSE; + } ZVAL_NULL(&z_fun); ZVAL_NULL(&z_dist); - /* extract options */ - if(z_opts) { - hOpts = Z_ARRVAL_P(z_opts); + /* extract options */ + if(z_opts) { + hOpts = Z_ARRVAL_P(z_opts); - /* extract previous ring. */ + /* extract previous ring. */ if ((zpData = zend_hash_str_find(hOpts, "previous", sizeof("previous") - 1)) != NULL && Z_TYPE_P(zpData) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(zpData)) != 0 ) { - /* consider previous array as non-existent if empty. */ + /* consider previous array as non-existent if empty. */ hPrev = Z_ARRVAL_P(zpData); - } + } - /* extract function name. */ + /* extract function name. */ if ((zpData = zend_hash_str_find(hOpts, "function", sizeof("function") - 1)) != NULL) { ZVAL_ZVAL(&z_fun, zpData, 1, 0); - } + } - /* extract function name. */ + /* extract function name. */ if ((zpData = zend_hash_str_find(hOpts, "distributor", sizeof("distributor") - 1)) != NULL) { ZVAL_ZVAL(&z_dist, zpData, 1, 0); - } + } - /* extract index option. */ + /* extract index option. */ if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL) { b_index = zval_is_true(zpData); - } + } - /* extract autorehash option. */ + /* extract autorehash option. */ if ((zpData = zend_hash_str_find(hOpts, "autorehash", sizeof("autorehash") - 1)) != NULL) { b_autorehash = zval_is_true(zpData); - } + } - /* pconnect */ + /* pconnect */ if ((zpData = zend_hash_str_find(hOpts, "pconnect", sizeof("pconnect") - 1)) != NULL) { b_pconnect = zval_is_true(zpData); - } + } - /* extract retry_interval option. */ + /* extract retry_interval option. */ if ((zpData = zend_hash_str_find(hOpts, "retry_interval", sizeof("retry_interval") - 1)) != NULL) { if (Z_TYPE_P(zpData) == IS_LONG) { l_retry_interval = Z_LVAL_P(zpData); @@ -320,12 +320,12 @@ PHP_METHOD(RedisArray, __construct) } } - /* extract lazy connect option. */ + /* extract lazy connect option. */ if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL) { b_lazy_connect = zval_is_true(zpData); - } - - /* extract connect_timeout option */ + } + + /* extract connect_timeout option */ if ((zpData = zend_hash_str_find(hOpts, "connect_timeout", sizeof("connect_timeout") - 1)) != NULL) { if (Z_TYPE_P(zpData) == IS_DOUBLE) { d_connect_timeout = Z_DVAL_P(zpData); @@ -346,111 +346,111 @@ PHP_METHOD(RedisArray, __construct) read_timeout = atof(Z_STRVAL_P(zpData)); } } - } + } - /* extract either name of list of hosts from z0 */ - switch(Z_TYPE_P(z0)) { - case IS_STRING: - ra = ra_load_array(Z_STRVAL_P(z0) TSRMLS_CC); - break; + /* extract either name of list of hosts from z0 */ + switch(Z_TYPE_P(z0)) { + case IS_STRING: + ra = ra_load_array(Z_STRVAL_P(z0) TSRMLS_CC); + break; - case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); - break; + case IS_ARRAY: + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); + break; - default: - WRONG_PARAM_COUNT; - } + default: + WRONG_PARAM_COUNT; + } zval_dtor(&z_dist); zval_dtor(&z_fun); - if(ra) { - ra->auto_rehash = b_autorehash; - ra->connect_timeout = d_connect_timeout; - if(ra->prev) ra->prev->auto_rehash = b_autorehash; + if(ra) { + ra->auto_rehash = b_autorehash; + ra->connect_timeout = d_connect_timeout; + if(ra->prev) ra->prev->auto_rehash = b_autorehash; #if (PHP_MAJOR_VERSION < 7) obj = (redis_array_object *)zend_objects_get_address(getThis() TSRMLS_CC); #else obj = (redis_array_object *)((char *)Z_OBJ_P(getThis()) - XtOffsetOf(redis_array_object, std)); #endif obj->ra = ra; - } + } } static void ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, int cmd_len, zval *z_args, zval *z_new_target) { - zval z_fun, *redis_inst, *z_callargs, *zp_tmp; - char *key = NULL; /* set to avoid "unused-but-set-variable" */ - int i, key_len = 0, argc; - HashTable *h_args; - zend_bool b_write_cmd = 0; + zval z_fun, *redis_inst, *z_callargs, *zp_tmp; + char *key = NULL; /* set to avoid "unused-but-set-variable" */ + int i, key_len = 0, argc; + HashTable *h_args; + zend_bool b_write_cmd = 0; - h_args = Z_ARRVAL_P(z_args); + h_args = Z_ARRVAL_P(z_args); if ((argc = zend_hash_num_elements(h_args)) == 0) { RETURN_FALSE; } - if(ra->z_multi_exec) { - redis_inst = ra->z_multi_exec; /* we already have the instance */ - } else { - /* extract key and hash it. */ + if(ra->z_multi_exec) { + redis_inst = ra->z_multi_exec; /* we already have the instance */ + } else { + /* extract key and hash it. */ if ((zp_tmp = zend_hash_index_find(h_args, 0)) == NULL || Z_TYPE_P(zp_tmp) != IS_STRING) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not find key"); - RETURN_FALSE; + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not find key"); + RETURN_FALSE; } key = Z_STRVAL_P(zp_tmp); key_len = Z_STRLEN_P(zp_tmp); - /* find node */ - redis_inst = ra_find_node(ra, key, key_len, NULL TSRMLS_CC); - if(!redis_inst) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not find any redis servers for this key."); - RETURN_FALSE; - } - } + /* find node */ + redis_inst = ra_find_node(ra, key, key_len, NULL TSRMLS_CC); + if(!redis_inst) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not find any redis servers for this key."); + RETURN_FALSE; + } + } - /* pass call through */ - ZVAL_STRINGL(&z_fun, cmd, cmd_len); /* method name */ - z_callargs = ecalloc(argc, sizeof(zval)); + /* pass call through */ + ZVAL_STRINGL(&z_fun, cmd, cmd_len); /* method name */ + z_callargs = ecalloc(argc, sizeof(zval)); - /* copy args to array */ + /* copy args to array */ i = 0; ZEND_HASH_FOREACH_VAL(h_args, zp_tmp) { ZVAL_ZVAL(&z_callargs[i], zp_tmp, 1, 0); i++; } ZEND_HASH_FOREACH_END(); - /* multi/exec */ - if(ra->z_multi_exec) { + /* multi/exec */ + if(ra->z_multi_exec) { call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); zval_dtor(return_value); zval_dtor(&z_fun); for (i = 0; i < argc; ++i) { zval_dtor(&z_callargs[i]); } - efree(z_callargs); - RETURN_ZVAL(getThis(), 1, 0); - } + efree(z_callargs); + RETURN_ZVAL(getThis(), 1, 0); + } /* check if write cmd */ b_write_cmd = ra_is_write_cmd(ra, cmd, cmd_len); - /* CALL! */ - if(ra->index && b_write_cmd) { + /* CALL! */ + if(ra->index && b_write_cmd) { /* add MULTI + SADD */ ra_index_multi(redis_inst, MULTI TSRMLS_CC); - /* call using discarded temp value and extract exec results after. */ + /* call using discarded temp value and extract exec results after. */ call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); zval_dtor(return_value); - /* add keys to index. */ - ra_index_key(key, key_len, redis_inst TSRMLS_CC); + /* add keys to index. */ + ra_index_key(key, key_len, redis_inst TSRMLS_CC); - /* call EXEC */ - ra_index_exec(redis_inst, return_value, 0 TSRMLS_CC); - } else { /* call directly through. */ - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); + /* call EXEC */ + ra_index_exec(redis_inst, return_value, 0 TSRMLS_CC); + } else { /* call directly through. */ + call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); if (!b_write_cmd) { /* check if we have an error. */ @@ -467,122 +467,122 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i ra_move_key(key, key_len, redis_inst, z_new_target TSRMLS_CC); } } - } + } - /* cleanup */ + /* cleanup */ zval_dtor(&z_fun); for (i = 0; i < argc; ++i) { zval_dtor(&z_callargs[i]); } - efree(z_callargs); + efree(z_callargs); } PHP_METHOD(RedisArray, __call) { - zval *object; - RedisArray *ra; - zval *z_args; + zval *object; + RedisArray *ra; + zval *z_args; - char *cmd; - strlen_t cmd_len; + char *cmd; + strlen_t cmd_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", - &object, redis_array_ce, &cmd, &cmd_len, &z_args) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", + &object, redis_array_ce, &cmd, &cmd_len, &z_args) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } - ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmd_len, z_args, NULL); + ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmd_len, z_args, NULL); } PHP_METHOD(RedisArray, _hosts) { - zval *object; - int i; - RedisArray *ra; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_array_ce) == FAILURE) { - RETURN_FALSE; - } - - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } - - array_init(return_value); - for(i = 0; i < ra->count; ++i) { - add_next_index_string(return_value, ra->hosts[i]); - } + zval *object; + int i; + RedisArray *ra; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_array_ce) == FAILURE) { + RETURN_FALSE; + } + + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } + + array_init(return_value); + for(i = 0; i < ra->count; ++i) { + add_next_index_string(return_value, ra->hosts[i]); + } } PHP_METHOD(RedisArray, _target) { - zval *object; - RedisArray *ra; - char *key; - strlen_t key_len; - zval *redis_inst; + zval *object; + RedisArray *ra; + char *key; + strlen_t key_len; + zval *redis_inst; int i; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_array_ce, &key, &key_len) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + &object, redis_array_ce, &key, &key_len) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } - redis_inst = ra_find_node(ra, key, key_len, &i TSRMLS_CC); - if(redis_inst) { + redis_inst = ra_find_node(ra, key, key_len, &i TSRMLS_CC); + if(redis_inst) { RETURN_STRING(ra->hosts[i]); - } else { - RETURN_NULL(); - } + } else { + RETURN_NULL(); + } } PHP_METHOD(RedisArray, _instance) { - zval *object; - RedisArray *ra; - char *target; - strlen_t target_len; - zval *z_redis; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_array_ce, &target, &target_len) == FAILURE) { - RETURN_FALSE; - } - - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } - - z_redis = ra_find_node_by_name(ra, target, target_len TSRMLS_CC); - if(z_redis) { - RETURN_ZVAL(z_redis, 1, 0); - } else { - RETURN_NULL(); - } + zval *object; + RedisArray *ra; + char *target; + strlen_t target_len; + zval *z_redis; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + &object, redis_array_ce, &target, &target_len) == FAILURE) { + RETURN_FALSE; + } + + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } + + z_redis = ra_find_node_by_name(ra, target, target_len TSRMLS_CC); + if(z_redis) { + RETURN_ZVAL(z_redis, 1, 0); + } else { + RETURN_NULL(); + } } PHP_METHOD(RedisArray, _function) { - zval *object, *z_fun; - RedisArray *ra; + zval *object, *z_fun; + RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_array_ce) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_array_ce) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } z_fun = &ra->z_fun; RETURN_ZVAL(z_fun, 1, 0); @@ -590,17 +590,17 @@ PHP_METHOD(RedisArray, _function) PHP_METHOD(RedisArray, _distributor) { - zval *object, *z_dist; - RedisArray *ra; + zval *object, *z_dist; + RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_array_ce) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_array_ce) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } z_dist = &ra->z_dist; RETURN_ZVAL(z_dist, 1, 0); @@ -608,81 +608,81 @@ PHP_METHOD(RedisArray, _distributor) PHP_METHOD(RedisArray, _rehash) { - zval *object; - RedisArray *ra; - zend_fcall_info z_cb = {0}; - zend_fcall_info_cache z_cb_cache = {0}; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|f", - &object, redis_array_ce, &z_cb, &z_cb_cache) == FAILURE) { - RETURN_FALSE; - } - - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } - - if (ZEND_NUM_ARGS() == 0) { - ra_rehash(ra, NULL, NULL TSRMLS_CC); - } else { - ra_rehash(ra, &z_cb, &z_cb_cache TSRMLS_CC); - } + zval *object; + RedisArray *ra; + zend_fcall_info z_cb = {0}; + zend_fcall_info_cache z_cb_cache = {0}; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|f", + &object, redis_array_ce, &z_cb, &z_cb_cache) == FAILURE) { + RETURN_FALSE; + } + + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } + + if (ZEND_NUM_ARGS() == 0) { + ra_rehash(ra, NULL, NULL TSRMLS_CC); + } else { + ra_rehash(ra, &z_cb, &z_cb_cache TSRMLS_CC); + } } static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) { - zval *object, z_fun; - int i; - RedisArray *ra; + zval *object, z_fun; + int i; + RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_array_ce) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_array_ce) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } - /* prepare call */ - ZVAL_STRING(&z_fun, method_name); + /* prepare call */ + ZVAL_STRING(&z_fun, method_name); - array_init(return_value); - for(i = 0; i < ra->count; ++i) { + array_init(return_value); + for(i = 0; i < ra->count; ++i) { zval zv, *z_tmp = &zv; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #endif - /* Call each node in turn */ + /* Call each node in turn */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 0, NULL); - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } zval_dtor(&z_fun); } PHP_METHOD(RedisArray, info) { - multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INFO"); + multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INFO"); } PHP_METHOD(RedisArray, ping) { - multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PING"); + multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PING"); } PHP_METHOD(RedisArray, flushdb) { - multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB"); + multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB"); } PHP_METHOD(RedisArray, flushall) { - multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL"); + multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL"); } -PHP_METHOD(RedisArray, save) +PHP_METHOD(RedisArray, save) { multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE"); } @@ -695,125 +695,125 @@ PHP_METHOD(RedisArray, bgsave) PHP_METHOD(RedisArray, keys) { - zval *object, z_args[1], z_fun; - RedisArray *ra; - char *pattern; - strlen_t pattern_len; + zval *object, z_args[1], z_fun; + RedisArray *ra; + char *pattern; + strlen_t pattern_len; int i; - /* Make sure the prototype is correct */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", - &object, redis_array_ce, &pattern, &pattern_len) == FAILURE) - { - RETURN_FALSE; - } + /* Make sure the prototype is correct */ + if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + &object, redis_array_ce, &pattern, &pattern_len) == FAILURE) + { + RETURN_FALSE; + } - /* Make sure we can grab our RedisArray object */ + /* Make sure we can grab our RedisArray object */ if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + RETURN_FALSE; + } - /* Set up our function call (KEYS) */ - ZVAL_STRINGL(&z_fun, "KEYS", 4); + /* Set up our function call (KEYS) */ + ZVAL_STRINGL(&z_fun, "KEYS", 4); - /* We will be passing with one string argument (the pattern) */ - ZVAL_STRINGL(z_args, pattern, pattern_len); + /* We will be passing with one string argument (the pattern) */ + ZVAL_STRINGL(z_args, pattern, pattern_len); - /* Init our array return */ - array_init(return_value); + /* Init our array return */ + array_init(return_value); - /* Iterate our RedisArray nodes */ - for(i=0; icount; ++i) { + /* Iterate our RedisArray nodes */ + for(i=0; icount; ++i) { zval zv, *z_tmp = &zv; #if (PHP_MAJOR_VERSION < 7) - /* Return for this node */ - MAKE_STD_ZVAL(z_tmp); + /* Return for this node */ + MAKE_STD_ZVAL(z_tmp); #endif - /* Call KEYS on each node */ + /* Call KEYS on each node */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); - /* Add the result for this host */ - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } + /* Add the result for this host */ + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } zval_dtor(&z_args[0]); zval_dtor(&z_fun); } PHP_METHOD(RedisArray, getOption) { - zval *object, z_fun, z_args[1]; - int i; - RedisArray *ra; - zend_long opt; + zval *object, z_fun, z_args[1]; + int i; + RedisArray *ra; + zend_long opt; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", - &object, redis_array_ce, &opt) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, redis_array_ce, &opt) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } - /* prepare call */ - ZVAL_STRINGL(&z_fun, "getOption", 9); + /* prepare call */ + ZVAL_STRINGL(&z_fun, "getOption", 9); - /* copy arg */ - ZVAL_LONG(&z_args[0], opt); + /* copy arg */ + ZVAL_LONG(&z_args[0], opt); - array_init(return_value); - for(i = 0; i < ra->count; ++i) { + array_init(return_value); + for(i = 0; i < ra->count; ++i) { zval zv, *z_tmp = &zv; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #endif - /* Call each node in turn */ + /* Call each node in turn */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } zval_dtor(&z_fun); } PHP_METHOD(RedisArray, setOption) { - zval *object, z_fun, z_args[2]; - int i; - RedisArray *ra; - zend_long opt; - char *val_str; - strlen_t val_len; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", - &object, redis_array_ce, &opt, &val_str, &val_len) == FAILURE) { - RETURN_FALSE; - } - - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } - - /* prepare call */ - ZVAL_STRINGL(&z_fun, "setOption", 9); - - /* copy args */ - ZVAL_LONG(&z_args[0], opt); - ZVAL_STRINGL(&z_args[1], val_str, val_len); - - array_init(return_value); - for(i = 0; i < ra->count; ++i) { + zval *object, z_fun, z_args[2]; + int i; + RedisArray *ra; + zend_long opt; + char *val_str; + strlen_t val_len; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", + &object, redis_array_ce, &opt, &val_str, &val_len) == FAILURE) { + RETURN_FALSE; + } + + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } + + /* prepare call */ + ZVAL_STRINGL(&z_fun, "setOption", 9); + + /* copy args */ + ZVAL_LONG(&z_args[0], opt); + ZVAL_STRINGL(&z_args[1], val_str, val_len); + + array_init(return_value); + for(i = 0; i < ra->count; ++i) { zval zv, *z_tmp = &zv; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #endif - /* Call each node in turn */ + /* Call each node in turn */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 2, z_args); - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } zval_dtor(&z_args[1]); zval_dtor(&z_fun); } @@ -821,65 +821,65 @@ PHP_METHOD(RedisArray, setOption) PHP_METHOD(RedisArray, select) { zval *object, z_fun, z_args[1]; - int i; - RedisArray *ra; - zend_long opt; + int i; + RedisArray *ra; + zend_long opt; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", - &object, redis_array_ce, &opt) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, redis_array_ce, &opt) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } - /* prepare call */ - ZVAL_STRINGL(&z_fun, "select", 6); + /* prepare call */ + ZVAL_STRINGL(&z_fun, "select", 6); - /* copy args */ + /* copy args */ ZVAL_LONG(&z_args[0], opt); - array_init(return_value); - for(i = 0; i < ra->count; ++i) { + array_init(return_value); + for(i = 0; i < ra->count; ++i) { zval zv, *z_tmp = &zv; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #endif - /* Call each node in turn */ + /* Call each node in turn */ call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } zval_dtor(&z_fun); } #if (PHP_MAJOR_VERSION < 7) #define HANDLE_MULTI_EXEC(ra, cmd) do { \ if (ra && ra->z_multi_exec) { \ - int i, num_varargs;\ - zval ***varargs = NULL, *z_arg_array; \ - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O*",\ - &object, redis_array_ce, &varargs, &num_varargs) == FAILURE) {\ - RETURN_FALSE;\ - }\ - /* copy all args into a zval hash table */\ + int i, num_varargs;\ + zval ***varargs = NULL, *z_arg_array; \ + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O*",\ + &object, redis_array_ce, &varargs, &num_varargs) == FAILURE) {\ + RETURN_FALSE;\ + }\ + /* copy all args into a zval hash table */\ MAKE_STD_ZVAL(z_arg_array); \ - array_init(z_arg_array);\ - for(i = 0; i < num_varargs; ++i) {\ - zval *z_tmp;\ + array_init(z_arg_array);\ + for(i = 0; i < num_varargs; ++i) {\ + zval *z_tmp;\ MAKE_STD_ZVAL(z_tmp); \ ZVAL_ZVAL(z_tmp, *varargs[i], 1, 0); \ add_next_index_zval(z_arg_array, z_tmp); \ - }\ - /* call */\ + }\ + /* call */\ ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, z_arg_array, NULL); \ zval_ptr_dtor(&z_arg_array); \ - if(varargs) {\ - efree(varargs);\ - }\ - return;\ - }\ + if(varargs) {\ + efree(varargs);\ + }\ + return;\ + }\ }while(0) #else #define HANDLE_MULTI_EXEC(ra, cmd) do { \ @@ -908,109 +908,109 @@ PHP_METHOD(RedisArray, select) /* MGET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mget) { - zval *object, *z_keys, z_argarray, *data, z_ret, *z_cur, z_tmp_array, *z_tmp; - int i, j, n; - RedisArray *ra; - int *pos, argc, *argc_each; - HashTable *h_keys; - zval **argv; + zval *object, *z_keys, z_argarray, *data, z_ret, *z_cur, z_tmp_array, *z_tmp; + int i, j, n; + RedisArray *ra; + int *pos, argc, *argc_each; + HashTable *h_keys; + zval **argv; if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { RETURN_FALSE; } - /* Multi/exec support */ + /* Multi/exec support */ HANDLE_MULTI_EXEC(ra, "MGET"); - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", - &object, redis_array_ce, &z_keys) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + &object, redis_array_ce, &z_keys) == FAILURE) { + RETURN_FALSE; + } - /* init data structures */ - h_keys = Z_ARRVAL_P(z_keys); + /* init data structures */ + h_keys = Z_ARRVAL_P(z_keys); if ((argc = zend_hash_num_elements(h_keys)) == 0) { RETURN_FALSE; } - argv = emalloc(argc * sizeof(zval*)); - pos = emalloc(argc * sizeof(int)); + argv = emalloc(argc * sizeof(zval*)); + pos = emalloc(argc * sizeof(int)); - argc_each = emalloc(ra->count * sizeof(int)); - memset(argc_each, 0, ra->count * sizeof(int)); + argc_each = emalloc(ra->count * sizeof(int)); + memset(argc_each, 0, ra->count * sizeof(int)); - /* associate each key to a redis node */ + /* associate each key to a redis node */ i = 0; ZEND_HASH_FOREACH_VAL(h_keys, data) { - /* If we need to represent a long key as a string */ - unsigned int key_len; - char kbuf[40], *key_lookup; - - /* phpredis proper can only use string or long keys, so restrict to that here */ - if (Z_TYPE_P(data) != IS_STRING && Z_TYPE_P(data) != IS_LONG) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs"); - efree(argv); - efree(pos); - efree(argc_each); - RETURN_FALSE; - } - - /* Convert to a string for hash lookup if it isn't one */ - if (Z_TYPE_P(data) == IS_STRING) { - key_len = Z_STRLEN_P(data); + /* If we need to represent a long key as a string */ + unsigned int key_len; + char kbuf[40], *key_lookup; + + /* phpredis proper can only use string or long keys, so restrict to that here */ + if (Z_TYPE_P(data) != IS_STRING && Z_TYPE_P(data) != IS_LONG) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs"); + efree(argv); + efree(pos); + efree(argc_each); + RETURN_FALSE; + } + + /* Convert to a string for hash lookup if it isn't one */ + if (Z_TYPE_P(data) == IS_STRING) { + key_len = Z_STRLEN_P(data); key_lookup = Z_STRVAL_P(data); - } else { - key_len = snprintf(kbuf, sizeof(kbuf), "%ld", Z_LVAL_P(data)); - key_lookup = (char*)kbuf; - } + } else { + key_len = snprintf(kbuf, sizeof(kbuf), "%ld", Z_LVAL_P(data)); + key_lookup = (char*)kbuf; + } - /* Find our node */ + /* Find our node */ if (ra_find_node(ra, key_lookup, key_len, &pos[i] TSRMLS_CC) == NULL) { /* TODO: handle */ } - argc_each[pos[i]]++; /* count number of keys per node */ - argv[i++] = data; - } ZEND_HASH_FOREACH_END(); + argc_each[pos[i]]++; /* count number of keys per node */ + argv[i++] = data; + } ZEND_HASH_FOREACH_END(); - array_init(&z_tmp_array); - /* calls */ - for(n = 0; n < ra->count; ++n) { /* for each node */ - /* We don't even need to make a call to this node if no keys go there */ - if(!argc_each[n]) continue; + array_init(&z_tmp_array); + /* calls */ + for(n = 0; n < ra->count; ++n) { /* for each node */ + /* We don't even need to make a call to this node if no keys go there */ + if(!argc_each[n]) continue; - /* copy args for MGET call on node. */ - array_init(&z_argarray); + /* copy args for MGET call on node. */ + array_init(&z_argarray); - for(i = 0; i < argc; ++i) { - if(pos[i] != n) continue; + for(i = 0; i < argc; ++i) { + if(pos[i] != n) continue; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #else zval zv; z_tmp = &zv; #endif ZVAL_ZVAL(z_tmp, argv[i], 1, 0); - add_next_index_zval(&z_argarray, z_tmp); - } + add_next_index_zval(&z_argarray, z_tmp); + } zval z_fun; /* prepare call */ ZVAL_STRINGL(&z_fun, "MGET", 4); - /* call MGET on the node */ + /* call MGET on the node */ call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); zval_dtor(&z_fun); - /* cleanup args array */ - zval_dtor(&z_argarray); + /* cleanup args array */ + zval_dtor(&z_argarray); /* Error out if we didn't get a proper response */ if (Z_TYPE(z_ret) != IS_ARRAY) { /* cleanup */ zval_dtor(&z_ret); zval_dtor(&z_tmp_array); - efree(argv); + efree(argv); efree(pos); efree(argc_each); @@ -1018,41 +1018,41 @@ PHP_METHOD(RedisArray, mget) RETURN_FALSE; } - for(i = 0, j = 0; i < argc; ++i) { + for(i = 0, j = 0; i < argc; ++i) { if (pos[i] != n || (z_cur = zend_hash_index_find(Z_ARRVAL(z_ret), j++)) == NULL) continue; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #else zval zv; z_tmp = &zv; #endif ZVAL_ZVAL(z_tmp, z_cur, 1, 0); - add_index_zval(&z_tmp_array, i, z_tmp); - } - zval_dtor(&z_ret); - } - - array_init(return_value); - /* copy temp array in the right order to return_value */ - for(i = 0; i < argc; ++i) { + add_index_zval(&z_tmp_array, i, z_tmp); + } + zval_dtor(&z_ret); + } + + array_init(return_value); + /* copy temp array in the right order to return_value */ + for(i = 0; i < argc; ++i) { if ((z_cur = zend_hash_index_find(Z_ARRVAL(z_tmp_array), i)) == NULL) continue; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #else zval zv; z_tmp = &zv; #endif ZVAL_ZVAL(z_tmp, z_cur, 1, 0); - add_next_index_zval(return_value, z_tmp); - } - - /* cleanup */ - zval_dtor(&z_tmp_array); - efree(argv); - efree(pos); - efree(argc_each); + add_next_index_zval(return_value, z_tmp); + } + + /* cleanup */ + zval_dtor(&z_tmp_array); + efree(argv); + efree(pos); + efree(argc_each); } @@ -1110,7 +1110,7 @@ PHP_METHOD(RedisArray, mset) // TODO: handle } - argc_each[pos[i]]++; /* count number of keys per node */ + argc_each[pos[i]]++; /* count number of keys per node */ keys[i] = estrndup(key, key_len); key_lens[i] = (int)key_len; argv[i] = data; @@ -1188,240 +1188,240 @@ PHP_METHOD(RedisArray, mset) /* DEL will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, del) { - zval *object, z_keys, z_fun, *data, z_ret, *z_tmp, *z_args; - int i, n; - RedisArray *ra; - int *pos, argc = ZEND_NUM_ARGS(), *argc_each; - HashTable *h_keys; - zval **argv; - long total = 0; - int free_zkeys = 0; + zval *object, z_keys, z_fun, *data, z_ret, *z_tmp, *z_args; + int i, n; + RedisArray *ra; + int *pos, argc = ZEND_NUM_ARGS(), *argc_each; + HashTable *h_keys; + zval **argv; + long total = 0; + int free_zkeys = 0; if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { RETURN_FALSE; } - /* Multi/exec support */ + /* Multi/exec support */ HANDLE_MULTI_EXEC(ra, "DEL"); - /* get all args in z_args */ - z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - /* if single array arg, point z_keys to it. */ - if (argc == 1 && Z_TYPE(z_args[0]) == IS_ARRAY) { - z_keys = z_args[0]; - } else { - /* copy all elements to z_keys */ - array_init(&z_keys); - for (i = 0; i < argc; ++i) { + /* get all args in z_args */ + z_args = emalloc(argc * sizeof(zval)); + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + efree(z_args); + RETURN_FALSE; + } + + /* if single array arg, point z_keys to it. */ + if (argc == 1 && Z_TYPE(z_args[0]) == IS_ARRAY) { + z_keys = z_args[0]; + } else { + /* copy all elements to z_keys */ + array_init(&z_keys); + for (i = 0; i < argc; ++i) { zval *z_arg = &z_args[i]; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #else zval zv; z_tmp = &zv; #endif ZVAL_ZVAL(z_tmp, z_arg, 1, 0); - /* add copy to z_keys */ - add_next_index_zval(&z_keys, z_tmp); - } - free_zkeys = 1; - } - - /* init data structures */ - h_keys = Z_ARRVAL(z_keys); + /* add copy to z_keys */ + add_next_index_zval(&z_keys, z_tmp); + } + free_zkeys = 1; + } + + /* init data structures */ + h_keys = Z_ARRVAL(z_keys); if ((argc = zend_hash_num_elements(h_keys)) == 0) { if (free_zkeys) zval_dtor(&z_keys); efree(z_args); RETURN_FALSE; } - argv = emalloc(argc * sizeof(zval*)); - pos = emalloc(argc * sizeof(int)); + argv = emalloc(argc * sizeof(zval*)); + pos = emalloc(argc * sizeof(int)); - argc_each = emalloc(ra->count * sizeof(int)); - memset(argc_each, 0, ra->count * sizeof(int)); + argc_each = emalloc(ra->count * sizeof(int)); + memset(argc_each, 0, ra->count * sizeof(int)); - /* associate each key to a redis node */ + /* associate each key to a redis node */ i = 0; ZEND_HASH_FOREACH_VAL(h_keys, data) { - if (Z_TYPE_P(data) != IS_STRING) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "DEL: all keys must be string."); + if (Z_TYPE_P(data) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "DEL: all keys must be string."); if (free_zkeys) zval_dtor(&z_keys); efree(z_args); efree(argv); - efree(pos); - efree(argc_each); - RETURN_FALSE; - } + efree(pos); + efree(argc_each); + RETURN_FALSE; + } if (ra_find_node(ra, Z_STRVAL_P(data), Z_STRLEN_P(data), &pos[i] TSRMLS_CC) == NULL) { // TODO: handle } - argc_each[pos[i]]++; /* count number of keys per node */ - argv[i++] = data; - } ZEND_HASH_FOREACH_END(); + argc_each[pos[i]]++; /* count number of keys per node */ + argv[i++] = data; + } ZEND_HASH_FOREACH_END(); - /* prepare call */ - ZVAL_STRINGL(&z_fun, "DEL", 3); + /* prepare call */ + ZVAL_STRINGL(&z_fun, "DEL", 3); - /* calls */ - for(n = 0; n < ra->count; ++n) { /* for each node */ - /* We don't even need to make a call to this node if no keys go there */ - if(!argc_each[n]) continue; + /* calls */ + for(n = 0; n < ra->count; ++n) { /* for each node */ + /* We don't even need to make a call to this node if no keys go there */ + if(!argc_each[n]) continue; - int found = 0; + int found = 0; zval z_argarray; - /* copy args */ - array_init(&z_argarray); - for(i = 0; i < argc; ++i) { - if(pos[i] != n) continue; + /* copy args */ + array_init(&z_argarray); + for(i = 0; i < argc; ++i) { + if(pos[i] != n) continue; #if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); + MAKE_STD_ZVAL(z_tmp); #else zval zv; z_tmp = &zv; #endif ZVAL_ZVAL(z_tmp, argv[i], 1, 0); - add_next_index_zval(&z_argarray, z_tmp); - found++; - } + add_next_index_zval(&z_argarray, z_tmp); + found++; + } - if(!found) { /* don't run empty DELs */ - zval_dtor(&z_argarray); - continue; - } + if(!found) { /* don't run empty DELs */ + zval_dtor(&z_argarray); + continue; + } - if(ra->index) { /* add MULTI */ - ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); - } + if(ra->index) { /* add MULTI */ + ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); + } - /* call */ + /* call */ call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); - if(ra->index) { + if(ra->index) { zval_dtor(&z_ret); - ra_index_del(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SREM to remove keys from node index */ - ra_index_exec(&ra->redis[n], &z_ret, 0 TSRMLS_CC); /* run EXEC */ - } - total += Z_LVAL(z_ret); /* increment total */ - - zval_dtor(&z_argarray); - zval_dtor(&z_ret); - } - - /* cleanup */ - zval_dtor(&z_fun); - efree(argv); - efree(pos); - efree(argc_each); - - if(free_zkeys) { - zval_dtor(&z_keys); - } - - efree(z_args); - RETURN_LONG(total); + ra_index_del(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SREM to remove keys from node index */ + ra_index_exec(&ra->redis[n], &z_ret, 0 TSRMLS_CC); /* run EXEC */ + } + total += Z_LVAL(z_ret); /* increment total */ + + zval_dtor(&z_argarray); + zval_dtor(&z_ret); + } + + /* cleanup */ + zval_dtor(&z_fun); + efree(argv); + efree(pos); + efree(argc_each); + + if(free_zkeys) { + zval_dtor(&z_keys); + } + + efree(z_args); + RETURN_LONG(total); } PHP_METHOD(RedisArray, multi) { - zval *object; - RedisArray *ra; - zval *z_redis; - char *host; - strlen_t host_len; - zend_long multi_value = MULTI; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", - &object, redis_array_ce, &host, &host_len, &multi_value) == FAILURE) { - RETURN_FALSE; - } - - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { - RETURN_FALSE; - } - - /* find node */ - z_redis = ra_find_node_by_name(ra, host, host_len TSRMLS_CC); - if(!z_redis) { - RETURN_FALSE; - } - - if(multi_value != MULTI && multi_value != PIPELINE) { - RETURN_FALSE; - } - - /* save multi object */ - ra->z_multi_exec = z_redis; - - /* switch redis instance to multi/exec mode. */ - ra_index_multi(z_redis, multi_value TSRMLS_CC); - - /* return this. */ - RETURN_ZVAL(object, 1, 0); + zval *object; + RedisArray *ra; + zval *z_redis; + char *host; + strlen_t host_len; + zend_long multi_value = MULTI; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", + &object, redis_array_ce, &host, &host_len, &multi_value) == FAILURE) { + RETURN_FALSE; + } + + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } + + /* find node */ + z_redis = ra_find_node_by_name(ra, host, host_len TSRMLS_CC); + if(!z_redis) { + RETURN_FALSE; + } + + if(multi_value != MULTI && multi_value != PIPELINE) { + RETURN_FALSE; + } + + /* save multi object */ + ra->z_multi_exec = z_redis; + + /* switch redis instance to multi/exec mode. */ + ra_index_multi(z_redis, multi_value TSRMLS_CC); + + /* return this. */ + RETURN_ZVAL(object, 1, 0); } PHP_METHOD(RedisArray, exec) { - zval *object; - RedisArray *ra; + zval *object; + RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_array_ce) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_array_ce) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { + RETURN_FALSE; + } - /* switch redis instance out of multi/exec mode. */ - ra_index_exec(ra->z_multi_exec, return_value, 1 TSRMLS_CC); + /* switch redis instance out of multi/exec mode. */ + ra_index_exec(ra->z_multi_exec, return_value, 1 TSRMLS_CC); - /* remove multi object */ - ra->z_multi_exec = NULL; + /* remove multi object */ + ra->z_multi_exec = NULL; } PHP_METHOD(RedisArray, discard) { - zval *object; - RedisArray *ra; + zval *object; + RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_array_ce) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_array_ce) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { + RETURN_FALSE; + } - /* switch redis instance out of multi/exec mode. */ - ra_index_discard(ra->z_multi_exec, return_value TSRMLS_CC); + /* switch redis instance out of multi/exec mode. */ + ra_index_discard(ra->z_multi_exec, return_value TSRMLS_CC); - /* remove multi object */ - ra->z_multi_exec = NULL; + /* remove multi object */ + ra->z_multi_exec = NULL; } PHP_METHOD(RedisArray, unwatch) { - zval *object; - RedisArray *ra; + zval *object; + RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", - &object, redis_array_ce) == FAILURE) { - RETURN_FALSE; - } + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_array_ce) == FAILURE) { + RETURN_FALSE; + } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { - RETURN_FALSE; - } + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { + RETURN_FALSE; + } - /* unwatch keys, stay in multi/exec mode. */ - ra_index_unwatch(ra->z_multi_exec, return_value TSRMLS_CC); + /* unwatch keys, stay in multi/exec mode. */ + ra_index_unwatch(ra->z_multi_exec, return_value TSRMLS_CC); } diff --git a/redis_array.h b/redis_array.h index f036c2647e..d6a00fa096 100644 --- a/redis_array.h +++ b/redis_array.h @@ -39,20 +39,20 @@ PHP_METHOD(RedisArray, unwatch); typedef struct RedisArray_ { - int count; - char **hosts; /* array of host:port strings */ - zval *redis; /* array of Redis instances */ - zval *z_multi_exec; /* Redis instance to be used in multi-exec */ - zend_bool index; /* use per-node index */ - zend_bool auto_rehash; /* migrate keys on read operations */ - zend_bool pconnect; /* should we use pconnect */ - zval z_fun; /* key extractor, callable */ - zval z_dist; /* key distributor, callable */ - HashTable *pure_cmds; /* hash table */ - double connect_timeout; /* socket connect timeout */ - double read_timeout; /* socket read timeout */ + int count; + char **hosts; /* array of host:port strings */ + zval *redis; /* array of Redis instances */ + zval *z_multi_exec; /* Redis instance to be used in multi-exec */ + zend_bool index; /* use per-node index */ + zend_bool auto_rehash; /* migrate keys on read operations */ + zend_bool pconnect; /* should we use pconnect */ + zval z_fun; /* key extractor, callable */ + zval z_dist; /* key distributor, callable */ + HashTable *pure_cmds; /* hash table */ + double connect_timeout; /* socket connect timeout */ + double read_timeout; /* socket read timeout */ - struct RedisArray_ *prev; + struct RedisArray_ *prev; } RedisArray; #if (PHP_MAJOR_VERSION < 7) diff --git a/redis_array_impl.c b/redis_array_impl.c index 0d740a0ffe..ebbaef5b40 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -25,43 +25,43 @@ #include "ext/standard/url.h" #include "ext/standard/crc32.h" -#define PHPREDIS_INDEX_NAME "__phpredis_array_index__" +#define PHPREDIS_INDEX_NAME "__phpredis_array_index__" extern zend_class_entry *redis_ce; RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) { - int i = 0, host_len; - char *host, *p; - short port; - zval *zpData, z_cons, z_ret; + int i = 0, host_len; + char *host, *p; + short port; + zval *zpData, z_cons, z_ret; redis_object *redis; - /* function calls on the Redis object */ - ZVAL_STRINGL(&z_cons, "__construct", 11); + /* function calls on the Redis object */ + ZVAL_STRINGL(&z_cons, "__construct", 11); - /* init connections */ + /* init connections */ ZEND_HASH_FOREACH_VAL(hosts, zpData) { if (Z_TYPE_P(zpData) != IS_STRING) { zval_dtor(&z_cons); return NULL; } - /* default values */ - host = Z_STRVAL_P(zpData); - host_len = Z_STRLEN_P(zpData); - ra->hosts[i] = estrndup(host, host_len); - port = 6379; + /* default values */ + host = Z_STRVAL_P(zpData); + host_len = Z_STRLEN_P(zpData); + ra->hosts[i] = estrndup(host, host_len); + port = 6379; - if((p = strrchr(host, ':'))) { /* found port */ - host_len = p - host; - port = (short)atoi(p+1); - } else if(strchr(host,'/') != NULL) { /* unix socket */ + if((p = strrchr(host, ':'))) { /* found port */ + host_len = p - host; + port = (short)atoi(p+1); + } else if(strchr(host,'/') != NULL) { /* unix socket */ port = -1; } - /* create Redis object */ + /* create Redis object */ #if (PHP_MAJOR_VERSION < 7) INIT_PZVAL(&ra->redis[i]); #endif @@ -75,21 +75,21 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b redis = (redis_object *)((char *)Z_OBJ_P(&ra->redis[i]) - XtOffsetOf(redis_object, std)); #endif - /* create socket */ - redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); + /* create socket */ + redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); - if (!b_lazy_connect) - { - /* connect */ - redis_sock_server_open(redis->sock TSRMLS_CC); - } + if (!b_lazy_connect) + { + /* connect */ + redis_sock_server_open(redis->sock TSRMLS_CC); + } - ra->count = ++i; - } ZEND_HASH_FOREACH_END(); + ra->count = ++i; + } ZEND_HASH_FOREACH_END(); zval_dtor(&z_cons); - return ra; + return ra; } /* List pure functions */ @@ -136,26 +136,26 @@ ra_init_function_table(RedisArray *ra) static int ra_find_name(const char *name) { - const char *ini_names, *p, *next; - /* php_printf("Loading redis array with name=[%s]\n", name); */ - - ini_names = INI_STR("redis.arrays.names"); - for(p = ini_names; p;) { - next = strchr(p, ','); - if(next) { - if(strncmp(p, name, next - p) == 0) { - return 1; - } - } else { - if(strcmp(p, name) == 0) { - return 1; - } - break; - } - p = next + 1; - } - - return 0; + const char *ini_names, *p, *next; + /* php_printf("Loading redis array with name=[%s]\n", name); */ + + ini_names = INI_STR("redis.arrays.names"); + for(p = ini_names; p;) { + next = strchr(p, ','); + if(next) { + if(strncmp(p, name, next - p) == 0) { + return 1; + } + } else { + if(strcmp(p, name) == 0) { + return 1; + } + break; + } + p = next + 1; + } + + return 0; } /* laod array from INI settings */ @@ -173,86 +173,86 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_connect_timeout; zval z_params_read_timeout; zval z_params_lazy_connect; - RedisArray *ra = NULL; + RedisArray *ra = NULL; - zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; - long l_retry_interval = 0; - zend_bool b_lazy_connect = 0; - double d_connect_timeout = 0, read_timeout = 0.0; - HashTable *hHosts = NULL, *hPrev = NULL; + zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; + long l_retry_interval = 0; + zend_bool b_lazy_connect = 0; + double d_connect_timeout = 0, read_timeout = 0.0; + HashTable *hHosts = NULL, *hPrev = NULL; size_t name_len = strlen(name); char *iptr; - /* find entry */ - if(!ra_find_name(name)) - return ra; + /* find entry */ + if(!ra_find_name(name)) + return ra; - /* find hosts */ - array_init(&z_params_hosts); + /* find hosts */ + array_init(&z_params_hosts); if ((iptr = INI_STR("redis.arrays.hosts")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_hosts TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_hosts), name, name_len)) != NULL) { - hHosts = Z_ARRVAL_P(z_data); - } + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_hosts), name, name_len)) != NULL) { + hHosts = Z_ARRVAL_P(z_data); + } - /* find previous hosts */ - array_init(&z_params_prev); + /* find previous hosts */ + array_init(&z_params_prev); if ((iptr = INI_STR("redis.arrays.previous")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_prev TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_prev), name, name_len)) != NULL) { - hPrev = Z_ARRVAL_P(z_data); - } + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_prev), name, name_len)) != NULL) { + hPrev = Z_ARRVAL_P(z_data); + } - /* find function */ - array_init(&z_params_funs); + /* find function */ + array_init(&z_params_funs); if ((iptr = INI_STR("redis.arrays.functions")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_funs TSRMLS_CC); } ZVAL_NULL(&z_fun); - if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_funs), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_funs), name, name_len)) != NULL) { ZVAL_ZVAL(&z_fun, z_data, 1, 0); - } + } - /* find distributor */ - array_init(&z_params_dist); + /* find distributor */ + array_init(&z_params_dist); if ((iptr = INI_STR("redis.arrays.distributor")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_dist TSRMLS_CC); } ZVAL_NULL(&z_dist); - if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_dist), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_dist), name, name_len)) != NULL) { ZVAL_ZVAL(&z_dist, z_data, 1, 0); - } + } - /* find index option */ - array_init(&z_params_index); + /* find index option */ + array_init(&z_params_index); if ((iptr = INI_STR("redis.arrays.index")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_index TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_index), name, name_len)) != NULL) { - if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { - b_index = 1; - } - } - - /* find autorehash option */ - array_init(&z_params_autorehash); + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_index), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { + b_index = 1; + } + } + + /* find autorehash option */ + array_init(&z_params_autorehash); if ((iptr = INI_STR("redis.arrays.autorehash")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_autorehash TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_autorehash), name, name_len)) != NULL) { - if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { - b_autorehash = 1; - } - } - - /* find retry interval option */ - array_init(&z_params_retry_interval); + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_autorehash), name, name_len)) != NULL) { + if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { + b_autorehash = 1; + } + } + + /* find retry interval option */ + array_init(&z_params_retry_interval); if ((iptr = INI_STR("redis.arrays.retryinterval")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_retry_interval TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_retry_interval), name, name_len)) != NULL) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_retry_interval), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_LONG) { l_retry_interval = Z_LVAL_P(z_data); } else if (Z_TYPE_P(z_data) == IS_STRING) { @@ -260,7 +260,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } - /* find pconnect option */ + /* find pconnect option */ array_init(&z_params_pconnect); if ((iptr = INI_STR("redis.arrays.pconnect")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_pconnect TSRMLS_CC); @@ -270,20 +270,20 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { b_pconnect = 1; } } - + /* find lazy connect option */ - array_init(&z_params_lazy_connect); + array_init(&z_params_lazy_connect); if ((iptr = INI_STR("redis.arrays.lazyconnect")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_lazy_connect TSRMLS_CC); } - if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_lazy_connect), name, name_len)) != NULL) { - if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_lazy_connect), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { b_lazy_connect = 1; } } - + /* find connect timeout option */ - array_init(&z_params_connect_timeout); + array_init(&z_params_connect_timeout); if ((iptr = INI_STR("redis.arrays.connecttimeout")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_connect_timeout TSRMLS_CC); } @@ -298,7 +298,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } /* find read timeout option */ - array_init(&z_params_read_timeout); + array_init(&z_params_read_timeout); if ((iptr = INI_STR("redis.arrays.readtimeout")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_read_timeout TSRMLS_CC); } @@ -312,15 +312,15 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } - - /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); + + /* create RedisArray object */ + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; } - /* cleanup */ + /* cleanup */ zval_dtor(&z_params_hosts); zval_dtor(&z_params_prev); zval_dtor(&z_params_funs); @@ -335,7 +335,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_dist); zval_dtor(&z_fun); - return ra; + return ra; } RedisArray * @@ -346,17 +346,17 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev if (!hosts || (count = zend_hash_num_elements(hosts)) == 0) return NULL; - /* create object */ - ra = emalloc(sizeof(RedisArray)); - ra->hosts = ecalloc(count, sizeof(char *)); - ra->redis = ecalloc(count, sizeof(zval)); - ra->count = 0; - ra->z_multi_exec = NULL; - ra->index = b_index; - ra->auto_rehash = 0; - ra->pconnect = b_pconnect; - ra->connect_timeout = connect_timeout; - ra->read_timeout = read_timeout; + /* create object */ + ra = emalloc(sizeof(RedisArray)); + ra->hosts = ecalloc(count, sizeof(char *)); + ra->redis = ecalloc(count, sizeof(zval)); + ra->count = 0; + ra->z_multi_exec = NULL; + ra->index = b_index; + ra->auto_rehash = 0; + ra->pconnect = b_pconnect; + ra->connect_timeout = connect_timeout; + ra->read_timeout = read_timeout; if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { @@ -373,10 +373,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* init array data structures */ ra_init_function_table(ra); - /* Set hash function and distribtor if provided */ + /* Set hash function and distribtor if provided */ ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); ZVAL_ZVAL(&ra->z_dist, z_dist, 1, 0); - + return ra; } @@ -388,18 +388,18 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR char *out = NULL; zval z_ret, z_argv; - /* check that we can call the extractor function */ + /* check that we can call the extractor function */ #if (PHP_MAJOR_VERSION < 7) if (!zend_is_callable_ex(&ra->z_fun, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { #else if (!zend_is_callable_ex(&ra->z_fun, NULL, 0, NULL, NULL, NULL)) { #endif - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function"); - return NULL; - } + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function"); + return NULL; + } ZVAL_NULL(&z_ret); - /* call extraction function */ + /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); @@ -408,24 +408,24 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR out = estrndup(Z_STRVAL(z_ret), *out_len); } - zval_dtor(&z_argv); - zval_dtor(&z_ret); - return out; + zval_dtor(&z_argv); + zval_dtor(&z_ret); + return out; } static char * ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS_DC) { - char *start, *end; - *out_len = key_len; + char *start, *end; + *out_len = key_len; - if (Z_TYPE(ra->z_fun) != IS_NULL) { - return ra_call_extractor(ra, key, key_len, out_len TSRMLS_CC); + if (Z_TYPE(ra->z_fun) != IS_NULL) { + return ra_call_extractor(ra, key, key_len, out_len TSRMLS_CC); } else if ((start = strchr(key, '{')) == NULL || (end = strchr(start + 1, '}')) == NULL) { return estrndup(key, key_len); } - /* found substring */ - *out_len = end - start - 1; + /* found substring */ + *out_len = end - start - 1; return estrndup(start + 1, *out_len); } @@ -436,25 +436,25 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) int ret; zval z_ret, z_argv; - /* check that we can call the extractor function */ + /* check that we can call the extractor function */ #if (PHP_MAJOR_VERSION < 7) if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { #else if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL)) { #endif - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); - return -1; - } + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); + return -1; + } ZVAL_NULL(&z_ret); - /* call extraction function */ + /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv); ret = (Z_TYPE(z_ret) == IS_LONG) ? Z_LVAL(z_ret) : -1; zval_dtor(&z_argv); - zval_dtor(&z_ret); + zval_dtor(&z_ret); return ret; } @@ -483,7 +483,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D CRC32(ret, (unsigned char)out[i]); } hash = (ret ^ 0xffffffff); - + /* get position on ring */ pos = (int)(hash * ra->count / 0xffffffff); } @@ -497,23 +497,23 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D zval * ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC) { - int i; - for(i = 0; i < ra->count; ++i) { - if(strncmp(ra->hosts[i], host, host_len) == 0) { - return &ra->redis[i]; - } - } - return NULL; + int i; + for(i = 0; i < ra->count; ++i) { + if(strncmp(ra->hosts[i], host, host_len) == 0) { + return &ra->redis[i]; + } + } + return NULL; } void ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { - zval z_fun_multi, z_ret; - zval z_args[1]; + zval z_fun_multi, z_ret; + zval z_args[1]; - /* run MULTI */ - ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); + /* run MULTI */ + ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); ZVAL_LONG(&z_args[0], multi_value); call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); zval_dtor(&z_fun_multi); @@ -523,39 +523,39 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { static void ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { - int i, argc; + int i, argc; zval z_fun, z_ret, *z_args; - /* alloc */ - argc = 1 + zend_hash_num_elements(Z_ARRVAL_P(z_keys)); + /* alloc */ + argc = 1 + zend_hash_num_elements(Z_ARRVAL_P(z_keys)); z_args = ecalloc(argc, sizeof(zval)); - /* prepare first parameters */ - ZVAL_STRING(&z_fun, cmd); + /* prepare first parameters */ + ZVAL_STRING(&z_fun, cmd); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); - /* prepare keys */ - for(i = 0; i < argc - 1; ++i) { + /* prepare keys */ + for(i = 0; i < argc - 1; ++i) { zval *zv = zend_hash_index_find(Z_ARRVAL_P(z_keys), i); if (zv == NULL) { ZVAL_NULL(&z_args[i+1]); } else { z_args[i+1] = *zv; } - } + } - /* run cmd */ + /* run cmd */ call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); zval_dtor(&z_args[0]); zval_dtor(&z_fun); zval_dtor(&z_ret); - efree(z_args); /* free container */ + efree(z_args); /* free container */ } void ra_index_del(zval *z_keys, zval *z_redis TSRMLS_DC) { - ra_index_change_keys("SREM", z_keys, z_redis TSRMLS_CC); + ra_index_change_keys("SREM", z_keys, z_redis TSRMLS_CC); } void @@ -564,14 +564,14 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { zval z_keys, *z_val; zend_string *zkey; ulong idx; - /* Initialize key array */ + /* Initialize key array */ #if PHP_VERSION_ID > 50300 - array_init_size(&z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); + array_init_size(&z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); #else - array_init(&z_keys); + array_init(&z_keys); #endif - /* Go through input array and add values to the key array */ + /* Go through input array and add values to the key array */ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_pairs), idx, zkey, z_val) { zval zv, *z_new = &zv; #if (PHP_MAJOR_VERSION < 7) @@ -587,11 +587,11 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { zend_hash_next_index_insert(Z_ARRVAL(z_keys), z_new); } ZEND_HASH_FOREACH_END(); - /* add keys to index */ - ra_index_change_keys("SADD", &z_keys, z_redis TSRMLS_CC); + /* add keys to index */ + ra_index_change_keys("SADD", &z_keys, z_redis TSRMLS_CC); - /* cleanup */ - zval_dtor(&z_keys); + /* cleanup */ + zval_dtor(&z_keys); } void @@ -599,13 +599,13 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { zval z_fun_sadd, z_ret, z_args[2]; - /* prepare args */ - ZVAL_STRINGL(&z_fun_sadd, "SADD", 4); + /* prepare args */ + ZVAL_STRINGL(&z_fun_sadd, "SADD", 4); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); ZVAL_STRINGL(&z_args[1], key, key_len); - /* run SADD */ + /* run SADD */ call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args); zval_dtor(&z_fun_sadd); zval_dtor(&z_args[1]); @@ -616,126 +616,126 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { void ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { - zval z_fun_exec, z_ret, *zp_tmp; + zval z_fun_exec, z_ret, *zp_tmp; - /* run EXEC */ - ZVAL_STRINGL(&z_fun_exec, "EXEC", 4); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); + /* run EXEC */ + ZVAL_STRINGL(&z_fun_exec, "EXEC", 4); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); zval_dtor(&z_fun_exec); - /* extract first element of exec array and put into return_value. */ - if(Z_TYPE(z_ret) == IS_ARRAY) { - if(return_value) { - if(keep_all) { + /* extract first element of exec array and put into return_value. */ + if(Z_TYPE(z_ret) == IS_ARRAY) { + if(return_value) { + if(keep_all) { zp_tmp = &z_ret; RETVAL_ZVAL(zp_tmp, 1, 0); - } else if ((zp_tmp = zend_hash_index_find(Z_ARRVAL(z_ret), 0)) != NULL) { + } else if ((zp_tmp = zend_hash_index_find(Z_ARRVAL(z_ret), 0)) != NULL) { RETVAL_ZVAL(zp_tmp, 1, 0); - } - } - } - zval_dtor(&z_ret); + } + } + } + zval_dtor(&z_ret); - /* zval *zptr = &z_ret; */ - /* php_var_dump(&zptr, 0 TSRMLS_CC); */ + /* zval *zptr = &z_ret; */ + /* php_var_dump(&zptr, 0 TSRMLS_CC); */ } void ra_index_discard(zval *z_redis, zval *return_value TSRMLS_DC) { - zval z_fun_discard, z_ret; + zval z_fun_discard, z_ret; - /* run DISCARD */ - ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); + /* run DISCARD */ + ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); - zval_dtor(&z_fun_discard); - zval_dtor(&z_ret); + zval_dtor(&z_fun_discard); + zval_dtor(&z_ret); } void ra_index_unwatch(zval *z_redis, zval *return_value TSRMLS_DC) { - zval z_fun_unwatch, z_ret; + zval z_fun_unwatch, z_ret; - /* run UNWATCH */ - ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); + /* run UNWATCH */ + ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7); + call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); - zval_dtor(&z_fun_unwatch); - zval_dtor(&z_ret); + zval_dtor(&z_fun_unwatch); + zval_dtor(&z_ret); } zend_bool ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len) { - zend_bool ret; - int i; - char *cmd_up = emalloc(1 + cmd_len); - /* convert to uppercase */ - for(i = 0; i < cmd_len; ++i) - cmd_up[i] = toupper(cmd[i]); - cmd_up[cmd_len] = 0; + zend_bool ret; + int i; + char *cmd_up = emalloc(1 + cmd_len); + /* convert to uppercase */ + for(i = 0; i < cmd_len; ++i) + cmd_up[i] = toupper(cmd[i]); + cmd_up[cmd_len] = 0; - ret = zend_hash_str_exists(ra->pure_cmds, cmd_up, cmd_len); + ret = zend_hash_str_exists(ra->pure_cmds, cmd_up, cmd_len); - efree(cmd_up); - return !ret; + efree(cmd_up); + return !ret; } /* run TYPE to find the type */ static zend_bool ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long *res TSRMLS_DC) { - int i = 0; - zval z_fun, z_ret, z_arg, *z_data; - long success = 1; + int i = 0; + zval z_fun, z_ret, z_arg, *z_data; + long success = 1; - /* Pipelined */ - ra_index_multi(z_from, PIPELINE TSRMLS_CC); + /* Pipelined */ + ra_index_multi(z_from, PIPELINE TSRMLS_CC); - /* prepare args */ - ZVAL_STRINGL(&z_arg, key, key_len); + /* prepare args */ + ZVAL_STRINGL(&z_arg, key, key_len); - /* run TYPE */ + /* run TYPE */ ZVAL_NULL(&z_ret); - ZVAL_STRINGL(&z_fun, "TYPE", 4); - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); + ZVAL_STRINGL(&z_fun, "TYPE", 4); + call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); zval_dtor(&z_fun); - zval_dtor(&z_ret); + zval_dtor(&z_ret); - /* run TYPE */ + /* run TYPE */ ZVAL_NULL(&z_ret); - ZVAL_STRINGL(&z_fun, "TTL", 3); - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); + ZVAL_STRINGL(&z_fun, "TTL", 3); + call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); zval_dtor(&z_fun); - zval_dtor(&z_ret); + zval_dtor(&z_ret); - /* Get the result from the pipeline. */ - ra_index_exec(z_from, &z_ret, 1 TSRMLS_CC); + /* Get the result from the pipeline. */ + ra_index_exec(z_from, &z_ret, 1 TSRMLS_CC); if (Z_TYPE(z_ret) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_ret), z_data) { if (z_data == NULL || Z_TYPE_P(z_data) != IS_LONG) { - success = 0; - break; - } - /* Get the result - Might change in the future to handle doubles as well */ - res[i++] = Z_LVAL_P(z_data); + success = 0; + break; + } + /* Get the result - Might change in the future to handle doubles as well */ + res[i++] = Z_LVAL_P(z_data); } ZEND_HASH_FOREACH_END(); - } - zval_dtor(&z_arg); - zval_dtor(&z_ret); - return success; + } + zval_dtor(&z_arg); + zval_dtor(&z_ret); + return success; } /* delete key from source server index during rehashing */ static void ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { - zval z_fun_srem, z_ret, z_args[2]; + zval z_fun_srem, z_ret, z_args[2]; - /* run SREM on source index */ - ZVAL_STRINGL(&z_fun_srem, "SREM", 4); + /* run SREM on source index */ + ZVAL_STRINGL(&z_fun_srem, "SREM", 4); ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); ZVAL_STRINGL(&z_args[1], key, key_len); @@ -753,26 +753,26 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { static zend_bool ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { - zval z_fun_del, z_ret, z_args[1]; + zval z_fun_del, z_ret, z_args[1]; - /* in a transaction */ - ra_index_multi(z_from, MULTI TSRMLS_CC); + /* in a transaction */ + ra_index_multi(z_from, MULTI TSRMLS_CC); - /* run DEL on source */ - ZVAL_STRINGL(&z_fun_del, "DEL", 3); - ZVAL_STRINGL(&z_args[0], key, key_len); - call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); + /* run DEL on source */ + ZVAL_STRINGL(&z_fun_del, "DEL", 3); + ZVAL_STRINGL(&z_args[0], key, key_len); + call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); zval_dtor(&z_fun_del); zval_dtor(&z_args[0]); zval_dtor(&z_ret); - /* remove key from index */ - ra_remove_from_index(z_from, key, key_len TSRMLS_CC); + /* remove key from index */ + ra_remove_from_index(z_from, key, key_len TSRMLS_CC); - /* close transaction */ - ra_index_exec(z_from, NULL, 0 TSRMLS_CC); + /* close transaction */ + ra_index_exec(z_from, NULL, 0 TSRMLS_CC); - return 1; + return 1; } static zend_bool @@ -780,80 +780,80 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { zval z_fun_expire, z_ret, z_args[2]; - if (ttl > 0) - { - /* run EXPIRE on target */ - ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6); + if (ttl > 0) + { + /* run EXPIRE on target */ + ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6); ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_LONG(&z_args[1], ttl); call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); zval_dtor(&z_fun_expire); zval_dtor(&z_args[0]); zval_dtor(&z_ret); - } + } - return 1; + return 1; } static zend_bool ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { zval z_fun_zrange, z_fun_zadd, z_ret, z_ret_dest, z_args[4], *z_zadd_args, *z_score_p; - int i, count; - HashTable *h_zset_vals; + int i, count; + HashTable *h_zset_vals; zend_string *zkey; - ulong idx; - - /* run ZRANGE key 0 -1 WITHSCORES on source */ - ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6); - ZVAL_STRINGL(&z_args[0], key, key_len); - ZVAL_STRINGL(&z_args[1], "0", 1); - ZVAL_STRINGL(&z_args[2], "-1", 2); - ZVAL_BOOL(&z_args[3], 1); - call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); + ulong idx; + + /* run ZRANGE key 0 -1 WITHSCORES on source */ + ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6); + ZVAL_STRINGL(&z_args[0], key, key_len); + ZVAL_STRINGL(&z_args[1], "0", 1); + ZVAL_STRINGL(&z_args[2], "-1", 2); + ZVAL_BOOL(&z_args[3], 1); + call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); zval_dtor(&z_fun_zrange); zval_dtor(&z_args[2]); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); - if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ - /* TODO: report? */ + if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ + /* TODO: report? */ zval_dtor(&z_ret); - return 0; - } + return 0; + } - /* we now have an array of value → score pairs in z_ret. */ - h_zset_vals = Z_ARRVAL(z_ret); + /* we now have an array of value → score pairs in z_ret. */ + h_zset_vals = Z_ARRVAL(z_ret); - /* allocate argument array for ZADD */ - count = zend_hash_num_elements(h_zset_vals); + /* allocate argument array for ZADD */ + count = zend_hash_num_elements(h_zset_vals); z_zadd_args = ecalloc((1 + 2*count), sizeof(zval)); ZVAL_STRINGL(&z_zadd_args[0], key, key_len); i = 1; ZEND_HASH_FOREACH_KEY_VAL(h_zset_vals, idx, zkey, z_score_p) { - /* add score */ + /* add score */ ZVAL_DOUBLE(&z_zadd_args[i], Z_DVAL_P(z_score_p)); - /* add value */ + /* add value */ if (zkey) { ZVAL_STRINGL(&z_zadd_args[i+1], ZSTR_VAL(zkey), ZSTR_LEN(zkey)); } else { ZVAL_LONG(&z_zadd_args[i+1], (long)idx); } - i += 2; - } ZEND_HASH_FOREACH_END(); + i += 2; + } ZEND_HASH_FOREACH_END(); - /* run ZADD on target */ - ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4); + /* run ZADD on target */ + ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4); call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args); - /* Expire if needed */ - ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); + /* Expire if needed */ + ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); - /* cleanup */ + /* cleanup */ zval_dtor(&z_fun_zadd); zval_dtor(&z_ret_dest); zval_dtor(&z_ret); @@ -864,7 +864,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS } efree(z_zadd_args); - return 1; + return 1; } static zend_bool @@ -872,116 +872,116 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl zval z_fun_get, z_fun_set, z_ret, z_args[3]; - /* run GET on source */ - ZVAL_STRINGL(&z_fun_get, "GET", 3); + /* run GET on source */ + ZVAL_STRINGL(&z_fun_get, "GET", 3); ZVAL_STRINGL(&z_args[0], key, key_len); call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args); zval_dtor(&z_fun_get); - if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ - /* TODO: report? */ + if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ + /* TODO: report? */ zval_dtor(&z_args[0]); zval_dtor(&z_ret); - return 0; - } + return 0; + } - /* run SET on target */ - if (ttl > 0) { + /* run SET on target */ + if (ttl > 0) { ZVAL_STRINGL(&z_fun_set, "SETEX", 5); ZVAL_LONG(&z_args[1], ttl); ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ - zval_dtor(&z_ret); /* free memory from our previous call */ + zval_dtor(&z_ret); /* free memory from our previous call */ call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args); - /* cleanup */ + /* cleanup */ zval_dtor(&z_args[2]); } else { ZVAL_STRINGL(&z_fun_set, "SET", 3); ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ - zval_dtor(&z_ret); /* free memory from our previous return value */ + zval_dtor(&z_ret); /* free memory from our previous return value */ call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args); - /* cleanup */ + /* cleanup */ zval_dtor(&z_args[1]); - } + } zval_dtor(&z_fun_set); zval_dtor(&z_args[0]); zval_dtor(&z_ret); - return 1; + return 1; } static zend_bool ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - zval z_fun_hgetall, z_fun_hmset, z_ret_dest, z_args[2]; + zval z_fun_hgetall, z_fun_hmset, z_ret_dest, z_args[2]; - /* run HGETALL on source */ + /* run HGETALL on source */ ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); zval_dtor(&z_fun_hgetall); - if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ - /* TODO: report? */ + if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ + /* TODO: report? */ zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); - return 0; - } + return 0; + } - /* run HMSET on target */ - ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); - call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); + /* run HMSET on target */ + ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); + call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); zval_dtor(&z_fun_hmset); zval_dtor(&z_ret_dest); - /* Expire if needed */ - ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); + /* Expire if needed */ + ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); - /* cleanup */ + /* cleanup */ zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); - return 1; + return 1; } static zend_bool ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, - int list_count, const char **cmd_list, - int add_count, const char **cmd_add, long ttl TSRMLS_DC) { + int list_count, const char **cmd_list, + int add_count, const char **cmd_add, long ttl TSRMLS_DC) { zval z_fun_retrieve, z_fun_sadd, z_ret, *z_retrieve_args, *z_sadd_args, *z_data_p; - int count, i; - HashTable *h_set_vals; + int count, i; + HashTable *h_set_vals; - /* run retrieval command on source */ - ZVAL_STRING(&z_fun_retrieve, cmd_list[0]); /* set the command */ + /* run retrieval command on source */ + ZVAL_STRING(&z_fun_retrieve, cmd_list[0]); /* set the command */ z_retrieve_args = ecalloc(list_count, sizeof(zval)); - /* set the key */ + /* set the key */ ZVAL_STRINGL(&z_retrieve_args[0], key, key_len); - /* possibly add some other args if they were provided. */ - for(i = 1; i < list_count; ++i) { + /* possibly add some other args if they were provided. */ + for(i = 1; i < list_count; ++i) { ZVAL_STRING(&z_retrieve_args[i], cmd_list[i]); - } + } call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args); - /* cleanup */ + /* cleanup */ zval_dtor(&z_fun_retrieve); - for(i = 0; i < list_count; ++i) { + for(i = 0; i < list_count; ++i) { zval_dtor(&z_retrieve_args[i]); - } - efree(z_retrieve_args); + } + efree(z_retrieve_args); - if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ - /* TODO: report? */ + if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ + /* TODO: report? */ zval_dtor(&z_ret); - return 0; - } + return 0; + } - /* run SADD/RPUSH on target */ - h_set_vals = Z_ARRVAL(z_ret); - count = 1 + zend_hash_num_elements(h_set_vals); - ZVAL_STRING(&z_fun_sadd, cmd_add[0]); + /* run SADD/RPUSH on target */ + h_set_vals = Z_ARRVAL(z_ret); + count = 1 + zend_hash_num_elements(h_set_vals); + ZVAL_STRING(&z_fun_sadd, cmd_add[0]); z_sadd_args = ecalloc(count, sizeof(zval)); ZVAL_STRINGL(&z_sadd_args[0], key, key_len); @@ -997,88 +997,88 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args); - /* cleanup */ + /* cleanup */ zval_dtor(&z_fun_sadd); for (i = 0; i < count; i++) { zval_dtor(&z_sadd_args[i]); } - efree(z_sadd_args); + efree(z_sadd_args); /* Clean up our output return value */ zval_dtor(&z_ret); - /* Expire if needed */ - ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); + /* Expire if needed */ + ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); - return 1; + return 1; } static zend_bool ra_move_set(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - const char *cmd_list[] = {"SMEMBERS"}; - const char *cmd_add[] = {"SADD"}; - return ra_move_collection(key, key_len, z_from, z_to, 1, cmd_list, 1, cmd_add, ttl TSRMLS_CC); + const char *cmd_list[] = {"SMEMBERS"}; + const char *cmd_add[] = {"SADD"}; + return ra_move_collection(key, key_len, z_from, z_to, 1, cmd_list, 1, cmd_add, ttl TSRMLS_CC); } static zend_bool ra_move_list(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { - const char *cmd_list[] = {"LRANGE", "0", "-1"}; - const char *cmd_add[] = {"RPUSH"}; - return ra_move_collection(key, key_len, z_from, z_to, 3, cmd_list, 1, cmd_add, ttl TSRMLS_CC); + const char *cmd_list[] = {"LRANGE", "0", "-1"}; + const char *cmd_add[] = {"RPUSH"}; + return ra_move_collection(key, key_len, z_from, z_to, 3, cmd_list, 1, cmd_add, ttl TSRMLS_CC); } void ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { - long res[2] = {0}, type, ttl; - zend_bool success = 0; - if (ra_get_key_type(z_from, key, key_len, z_from, res TSRMLS_CC)) { - type = res[0]; - ttl = res[1]; - /* open transaction on target server */ - ra_index_multi(z_to, MULTI TSRMLS_CC); - switch(type) { - case REDIS_STRING: - success = ra_move_string(key, key_len, z_from, z_to, ttl TSRMLS_CC); - break; - - case REDIS_SET: - success = ra_move_set(key, key_len, z_from, z_to, ttl TSRMLS_CC); - break; - - case REDIS_LIST: - success = ra_move_list(key, key_len, z_from, z_to, ttl TSRMLS_CC); - break; - - case REDIS_ZSET: - success = ra_move_zset(key, key_len, z_from, z_to, ttl TSRMLS_CC); - break; - - case REDIS_HASH: - success = ra_move_hash(key, key_len, z_from, z_to, ttl TSRMLS_CC); - break; - - default: - /* TODO: report? */ - break; - } - } - - if(success) { - ra_del_key(key, key_len, z_from TSRMLS_CC); - ra_index_key(key, key_len, z_to TSRMLS_CC); - } - - /* close transaction */ - ra_index_exec(z_to, NULL, 0 TSRMLS_CC); + long res[2] = {0}, type, ttl; + zend_bool success = 0; + if (ra_get_key_type(z_from, key, key_len, z_from, res TSRMLS_CC)) { + type = res[0]; + ttl = res[1]; + /* open transaction on target server */ + ra_index_multi(z_to, MULTI TSRMLS_CC); + switch(type) { + case REDIS_STRING: + success = ra_move_string(key, key_len, z_from, z_to, ttl TSRMLS_CC); + break; + + case REDIS_SET: + success = ra_move_set(key, key_len, z_from, z_to, ttl TSRMLS_CC); + break; + + case REDIS_LIST: + success = ra_move_list(key, key_len, z_from, z_to, ttl TSRMLS_CC); + break; + + case REDIS_ZSET: + success = ra_move_zset(key, key_len, z_from, z_to, ttl TSRMLS_CC); + break; + + case REDIS_HASH: + success = ra_move_hash(key, key_len, z_from, z_to, ttl TSRMLS_CC); + break; + + default: + /* TODO: report? */ + break; + } + } + + if(success) { + ra_del_key(key, key_len, z_from TSRMLS_CC); + ra_index_key(key, key_len, z_to TSRMLS_CC); + } + + /* close transaction */ + ra_index_exec(z_to, NULL, 0 TSRMLS_CC); } /* callback with the current progress, with hostname and count */ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, - const char *hostname, long count TSRMLS_DC) { + const char *hostname, long count TSRMLS_DC) { zval zv, *z_ret = &zv; @@ -1105,13 +1105,13 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, z_cb->params = z_args; z_cb->retval = z_ret; #endif - z_cb->no_separation = 0; - z_cb->param_count = 2; + z_cb->no_separation = 0; + z_cb->param_count = 2; - /* run cb(hostname, count) */ - zend_call_function(z_cb, z_cb_cache TSRMLS_CC); + /* run cb(hostname, count) */ + zend_call_function(z_cb, z_cb_cache TSRMLS_CC); - /* cleanup */ + /* cleanup */ #if (PHP_MAJOR_VERSION < 7) zval_ptr_dtor(&z_host); zval_ptr_dtor(&z_count); @@ -1123,7 +1123,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, static void ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool b_index, - zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { + zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { HashTable *h_keys; long count = 0; @@ -1152,10 +1152,10 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool return; } - /* callback */ - if(z_cb && z_cb_cache) { - zval_rehash_callback(z_cb, z_cb_cache, hostname, count TSRMLS_CC); - } + /* callback */ + if(z_cb && z_cb_cache) { + zval_rehash_callback(z_cb, z_cb_cache, hostname, count TSRMLS_CC); + } /* for each key, redistribute */ ZEND_HASH_FOREACH_VAL(h_keys, z_ele) { @@ -1175,14 +1175,14 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool void ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { - int i; + int i; - /* redistribute the data, server by server. */ - if(!ra->prev) - return; /* TODO: compare the two rings for equality */ + /* redistribute the data, server by server. */ + if(!ra->prev) + return; /* TODO: compare the two rings for equality */ - for(i = 0; i < ra->prev->count; ++i) { - ra_rehash_server(ra, &ra->prev->redis[i], ra->prev->hosts[i], ra->index, z_cb, z_cb_cache TSRMLS_CC); - } + for(i = 0; i < ra->prev->count; ++i) { + ra_rehash_server(ra, &ra->prev->redis[i], ra->prev->hosts[i], ra->index, z_cb, z_cb_cache TSRMLS_CC); + } } diff --git a/redis_cluster.c b/redis_cluster.c index b241e561cf..0dacd926c0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -332,14 +332,14 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { return retval; #else - object_properties_init(&cluster->std, class_type); - memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); - RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std); + object_properties_init(&cluster->std, class_type); + memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); + RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std); RedisCluster_handlers.free_obj = free_cluster_context; - cluster->std.handlers = &RedisCluster_handlers; + cluster->std.handlers = &RedisCluster_handlers; - return &cluster->std; + return &cluster->std; #endif } @@ -606,21 +606,21 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, clusterKeyValHT *kv TSRMLS_DC) { zval *z_val; - zend_ulong idx; + zend_ulong idx; // Grab the key, convert it to a string using provided kbuf buffer if it's // a LONG style key #if (PHP_MAJOR_VERSION < 7) - uint key_len; - switch(zend_hash_get_current_key_ex(ht, &(kv->key), &key_len, &idx, 0, ptr)) { - case HASH_KEY_IS_STRING: - kv->key_len = (int)(key_len-1); + uint key_len; + switch(zend_hash_get_current_key_ex(ht, &(kv->key), &key_len, &idx, 0, ptr)) { + case HASH_KEY_IS_STRING: + kv->key_len = (int)(key_len-1); #else - zend_string *zkey; - switch (zend_hash_get_current_key_ex(ht, &zkey, &idx, ptr)) { - case HASH_KEY_IS_STRING: - kv->key_len = ZSTR_LEN(zkey); - kv->key = ZSTR_VAL(zkey); + zend_string *zkey; + switch (zend_hash_get_current_key_ex(ht, &zkey, &idx, ptr)) { + case HASH_KEY_IS_STRING: + kv->key_len = ZSTR_LEN(zkey); + kv->key = ZSTR_VAL(zkey); #endif break; case HASH_KEY_IS_LONG: @@ -743,7 +743,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // it's the first iteration every time, needlessly zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { - efree(z_args); + efree(z_args); return -1; } @@ -765,7 +765,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zend_hash_destroy(ht_arr); efree(ht_arr); } - efree(z_args); + efree(z_args); return -1; } @@ -780,7 +780,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zend_hash_destroy(ht_arr); efree(ht_arr); } - efree(z_args); + efree(z_args); return -1; } } @@ -797,7 +797,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zend_hash_move_forward_ex(ht_arr, &ptr); } - efree(z_args); + efree(z_args); // If we've got straggler(s) process them if(mc.argc > 0) { @@ -2246,10 +2246,10 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) zend_string_release(zstr); if(key_free) efree(key); } else if (Z_TYPE_P(z_arg) == IS_ARRAY && - (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL && - (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL && - Z_TYPE_P(z_host) == IS_STRING && Z_TYPE_P(z_port) == IS_LONG - ) { + (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL && + (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL && + Z_TYPE_P(z_host) == IS_STRING && Z_TYPE_P(z_port) == IS_LONG + ) { /* Attempt to find this specific node by host:port */ slot = cluster_find_slot(c,(const char *)Z_STRVAL_P(z_host), (unsigned short)Z_LVAL_P(z_port)); From 99335d6081576f0e8403492a1ee1637d34bdd0ff Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 29 Sep 2017 17:04:05 +0300 Subject: [PATCH 0851/1986] Don't use convert_to_string in redis_hmget_cmd --- cluster_library.c | 11 +++++------ library.c | 10 ++++++---- redis_commands.c | 6 +++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 5a72dc3603..3e532b77f8 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2435,6 +2435,7 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, // Loop while we've got replies while(count--) { + zend_string *zstr = zval_get_string(&z_keys[i]); line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); if(line != NULL) { @@ -2444,19 +2445,17 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, MAKE_STD_ZVAL(z); *z = zv; #endif - add_assoc_zval_ex(z_result,Z_STRVAL(z_keys[i]), - Z_STRLEN(z_keys[i]), z); + add_assoc_zval_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), z); } else { - add_assoc_stringl_ex(z_result, Z_STRVAL(z_keys[i]), - Z_STRLEN(z_keys[i]), line, line_len); + add_assoc_stringl_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), line, line_len); } efree(line); } else { - add_assoc_bool_ex(z_result, Z_STRVAL(z_keys[i]), - Z_STRLEN(z_keys[i]), 0); + add_assoc_bool_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0); } // Clean up key context + zend_string_release(zstr); zval_dtor(&z_keys[i]); // Move to the next key diff --git a/library.c b/library.c index e91ab0ab8e..ccc8ae6f23 100644 --- a/library.c +++ b/library.c @@ -1705,6 +1705,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc array_init(z_multi_result); /* pre-allocate array for multi's results. */ for(i = 0; i < numElems; ++i) { + zend_string *zstr = zval_get_string(&z_keys[i]); response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { zval zv0, *z = &zv0; @@ -1713,14 +1714,15 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc MAKE_STD_ZVAL(z); *z = zv0; #endif - add_assoc_zval_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), z); + add_assoc_zval_ex(z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), z); } else { - add_assoc_stringl_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), response, response_len); + add_assoc_stringl_ex(z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), response, response_len); } efree(response); } else { - add_assoc_bool_ex(z_multi_result, Z_STRVAL(z_keys[i]), Z_STRLEN(z_keys[i]), 0); + add_assoc_bool_ex(z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0); } + zend_string_release(zstr); zval_dtor(&z_keys[i]); } efree(z_keys); @@ -1728,7 +1730,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_multi_result); } else { - RETVAL_ZVAL(z_multi_result, 0, 1); + RETVAL_ZVAL(z_multi_result, 1, 0); } return 0; } diff --git a/redis_commands.c b/redis_commands.c index 7cf274c065..a7d00b51f7 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1389,7 +1389,6 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) { // Copy into our member array ZVAL_ZVAL(&z_mems[valid], z_mem, 1, 0); - convert_to_string(&z_mems[valid]); // Increment the member count to actually send valid++; @@ -1416,8 +1415,9 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate over members, appending as arguments for(i=0;i Date: Wed, 4 Oct 2017 14:29:20 +0300 Subject: [PATCH 0852/1986] Issue #1245 Move building `script` command logic to `redis_build_script_cmd` and use it in Redis and RedisCluster objects. Fix arginfo for `RedisCluster::script`. Fix memory leak in `cluster_raw_cmd` when `cluster_cmd_get_slot` fails. --- common.h | 9 ------ redis.c | 76 ++++++++++-------------------------------------- redis_cluster.c | 59 +++++++++++++++++++++++++++++++++++-- redis_commands.c | 45 ++++++++++++++++++++++++++++ redis_commands.h | 2 ++ 5 files changed, 120 insertions(+), 71 deletions(-) diff --git a/common.h b/common.h index 2bc34e7b74..3d80d063da 100644 --- a/common.h +++ b/common.h @@ -1060,15 +1060,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1) #endif ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1) ZEND_ARG_INFO(0, arg) ZEND_ARG_INFO(0, option) diff --git a/redis.c b/redis.c index e2432005a8..f008c0c569 100644 --- a/redis.c +++ b/redis.c @@ -165,6 +165,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_wait, 0, 0, 2) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + /** * Argument info for the SCAN proper */ @@ -2893,27 +2902,6 @@ PHP_METHOD(Redis, evalsha) { REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_variant_reply); } -PHP_REDIS_API int -redis_build_script_exists_cmd(char **ret, zval *argv, int argc) { - smart_string cmd = {0}; - zend_string *zstr; - int i; - - // Start building our command - REDIS_CMD_INIT_SSTR_STATIC(&cmd, 1 + argc, "SCRIPT"); - redis_cmd_append_sstr(&cmd, "EXISTS", 6); - - for (i = 0; i < argc; i++) { - zstr = zval_get_string(&argv[i]); - redis_cmd_append_sstr(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_string_release(zstr); - } - - /* Success */ - *ret = cmd.c; - return cmd.len; -} - /* {{{ proto status Redis::script('flush') * {{{ proto status Redis::script('kill') * {{{ proto string Redis::script('load', lua_script) @@ -2922,53 +2910,21 @@ redis_build_script_exists_cmd(char **ret, zval *argv, int argc) { PHP_METHOD(Redis, script) { zval *z_args; RedisSock *redis_sock; - int cmd_len, argc; - char *cmd; + smart_string cmd = {0}; + int argc = ZEND_NUM_ARGS(); /* Attempt to grab our socket */ - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { + if (argc < 1 || (redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } - /* Grab the number of arguments */ - argc = ZEND_NUM_ARGS(); - /* Allocate an array big enough to store our arguments */ - z_args = emalloc(argc * sizeof(zval)); + z_args = ecalloc(argc, sizeof(zval)); /* Make sure we can grab our arguments, we have a string directive */ if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || - (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING)) - { - efree(z_args); - RETURN_FALSE; - } - - // Branch based on the directive - if(!strcasecmp(Z_STRVAL(z_args[0]), "flush") || - !strcasecmp(Z_STRVAL(z_args[0]), "kill")) - { - // Simple SCRIPT FLUSH, or SCRIPT_KILL command - cmd_len = REDIS_SPPRINTF(&cmd, "SCRIPT", "s", Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); - } else if(!strcasecmp(Z_STRVAL(z_args[0]), "load")) { - // Make sure we have a second argument, and it's not empty. If it is - // empty, we can just return an empty array (which is what Redis does) - if(argc < 2 || Z_TYPE(z_args[1]) != IS_STRING || - Z_STRLEN(z_args[1]) < 1) - { - // Free our args - efree(z_args); - RETURN_FALSE; - } - - // Format our SCRIPT LOAD command - cmd_len = REDIS_SPPRINTF(&cmd, "SCRIPT", "ss", "LOAD", 4, Z_STRVAL(z_args[1]), - Z_STRLEN(z_args[1])); - } else if(!strcasecmp(Z_STRVAL(z_args[0]), "exists")) { - /* Construct our SCRIPT EXISTS command */ - cmd_len = redis_build_script_exists_cmd(&cmd, &(z_args[1]), argc-1); - } else { - /* Unknown directive */ + redis_build_script_cmd(&cmd, argc, z_args) == NULL + ) { efree(z_args); RETURN_FALSE; } @@ -2977,7 +2933,7 @@ PHP_METHOD(Redis, script) { efree(z_args); // Kick off our request - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); IF_ATOMIC() { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) diff --git a/redis_cluster.c b/redis_cluster.c index 9658f8d217..550b23c9f6 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -74,6 +74,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_cluster, 0, 0, 1) #endif ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 2) + ZEND_ARG_INFO(0, key_or_address) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) ZEND_ARG_INFO(0, str_key) @@ -2345,6 +2355,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) /* First argument needs to be the "where" */ if((slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC))<0) { + efree(z_args); RETURN_FALSE; } @@ -2778,8 +2789,52 @@ PHP_METHOD(RedisCluster, pubsub) { /* {{{ proto mixed RedisCluster::script(string key, ...) * proto mixed RedisCluster::script(array host_port, ...) */ PHP_METHOD(RedisCluster, script) { - cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SCRIPT", - sizeof("SCRIPT")-1); + redisCluster *c = GET_CONTEXT(); + smart_string cmd = {0}; + zval *z_args; + short slot; + int i, argc = ZEND_NUM_ARGS(); + + /* Commands using this pass-thru don't need to be enabled in MULTI mode */ + if (!CLUSTER_IS_ATOMIC(c)) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Command can't be issued in MULTI mode"); + RETURN_FALSE; + } + + /* We at least need the key or [host,port] argument */ + if (argc < 2) { + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Command requires at least an argument to direct to a node"); + RETURN_FALSE; + } + + /* Allocate an array to process arguments */ + z_args = ecalloc(argc, sizeof(zval)); + + /* Grab args */ + if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || + (slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC)) < 0 || + redis_build_script_cmd(&cmd, argc - 1, &z_args[1]) == NULL + ) { + efree(z_args); + RETURN_FALSE; + } + + /* Send it off */ + if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF TSRMLS_CC) < 0) { + zend_throw_exception(redis_cluster_exception_ce, + "Couldn't send command to node", 0 TSRMLS_CC); + efree(cmd.c); + efree(z_args); + RETURN_FALSE; + } + + /* Read the response variant */ + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + + efree(cmd.c); + efree(z_args); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index a7d00b51f7..02317f592a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -89,6 +89,51 @@ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len TSRMLS_ return SUCCESS; } +smart_string * +redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args) +{ + int i; + zend_string *zstr; + + if (Z_TYPE(z_args[0]) != IS_STRING) { + return NULL; + } + // Branch based on the directive + if (!strcasecmp(Z_STRVAL(z_args[0]), "flush") || !strcasecmp(Z_STRVAL(z_args[0]), "kill")) { + // Simple SCRIPT FLUSH, or SCRIPT_KILL command + REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); + redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + } else if (!strcasecmp(Z_STRVAL(z_args[0]), "load")) { + // Make sure we have a second argument, and it's not empty. If it is + // empty, we can just return an empty array (which is what Redis does) + if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING || Z_STRLEN(z_args[1]) < 1) { + return NULL; + } + // Format our SCRIPT LOAD command + REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); + redis_cmd_append_sstr(cmd, "LOAD", sizeof("LOAD") - 1); + redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); + } else if (!strcasecmp(Z_STRVAL(z_args[0]), "exists")) { + // Make sure we have a second argument + if (argc < 2) { + return NULL; + } + /* Construct our SCRIPT EXISTS command */ + REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); + redis_cmd_append_sstr(cmd, "EXISTS", sizeof("EXISTS") - 1); + + for (i = 1; i < argc; ++i) { + zstr = zval_get_string(&z_args[i]); + redis_cmd_append_sstr(cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + zend_string_release(zstr); + } + } else { + /* Unknown directive */ + return NULL; + } + return cmd; +} + /* Generic command where we just take a string and do nothing to it*/ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 73b599cde8..51d0b9f1ef 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -30,6 +30,8 @@ typedef enum geoSortType { /* Construct a raw command */ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC); +/* Construct a script command */ +smart_string *redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args); /* Redis command generics. Many commands share common prototypes meaning that * we can write one function to handle all of them. For example, there are From 0115666e59f1348d983872bd5488f52018a472c1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 5 Oct 2017 15:27:18 +0300 Subject: [PATCH 0853/1986] Add PHPREDIS_GET_OBJECT macros --- common.h | 3 +++ redis.c | 12 ++---------- redis_array.c | 12 ++---------- redis_array_impl.c | 6 +----- 4 files changed, 8 insertions(+), 25 deletions(-) diff --git a/common.h b/common.h index 3d80d063da..bbb747b75f 100644 --- a/common.h +++ b/common.h @@ -412,11 +412,14 @@ typedef int strlen_t; /* References don't need any actions */ #define ZVAL_DEREF(v) PHPREDIS_NOTUSED(v) +#define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)zend_objects_get_address(z TSRMLS_CC) + #else #include #include typedef size_t strlen_t; #define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_FALSE) +#define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)((char *)Z_OBJ_P(z) - XtOffsetOf(class_entry, std)) #endif /* NULL check so Eclipse doesn't go crazy */ diff --git a/redis.c b/redis.c index f008c0c569..8475385d0d 100644 --- a/redis.c +++ b/redis.c @@ -564,11 +564,7 @@ redis_sock_get_instance(zval *id TSRMLS_DC, int no_throw) redis_object *redis; if (Z_TYPE_P(id) == IS_OBJECT) { -#if (PHP_MAJOR_VERSION < 7) - redis = (redis_object *)zend_objects_get_address(id TSRMLS_CC); -#else - redis = (redis_object *)((char *)Z_OBJ_P(id) - XtOffsetOf(redis_object, std)); -#endif + redis = PHPREDIS_GET_OBJECT(redis_object, id); if (redis->sock) { return redis->sock; } @@ -890,11 +886,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) port = 6379; } -#if (PHP_MAJOR_VERSION < 7) - redis = (redis_object *)zend_objects_get_address(object TSRMLS_CC); -#else - redis = (redis_object *)((char *)Z_OBJ_P(object) - XtOffsetOf(redis_object, std)); -#endif + redis = PHPREDIS_GET_OBJECT(redis_object, object); /* if there is a redis sock already we have to remove it */ if (redis->sock) { redis_sock_disconnect(redis->sock TSRMLS_CC); diff --git a/redis_array.c b/redis_array.c index 52713063a2..b293253ffc 100644 --- a/redis_array.c +++ b/redis_array.c @@ -245,11 +245,7 @@ redis_array_get(zval *id TSRMLS_DC) redis_array_object *obj; if (Z_TYPE_P(id) == IS_OBJECT) { -#if (PHP_MAJOR_VERSION < 7) - obj = (redis_array_object *)zend_objects_get_address(id TSRMLS_CC); -#else - obj = (redis_array_object *)((char *)Z_OBJ_P(id) - XtOffsetOf(redis_array_object, std)); -#endif + obj = PHPREDIS_GET_OBJECT(redis_array_object, id); return obj->ra; } return NULL; @@ -368,11 +364,7 @@ PHP_METHOD(RedisArray, __construct) ra->auto_rehash = b_autorehash; ra->connect_timeout = d_connect_timeout; if(ra->prev) ra->prev->auto_rehash = b_autorehash; -#if (PHP_MAJOR_VERSION < 7) - obj = (redis_array_object *)zend_objects_get_address(getThis() TSRMLS_CC); -#else - obj = (redis_array_object *)((char *)Z_OBJ_P(getThis()) - XtOffsetOf(redis_array_object, std)); -#endif + obj = PHPREDIS_GET_OBJECT(redis_array_object, getThis()); obj->ra = ra; } } diff --git a/redis_array_impl.c b/redis_array_impl.c index ebbaef5b40..1a5aa7bac2 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -69,11 +69,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL); zval_dtor(&z_ret); -#if (PHP_MAJOR_VERSION < 7) - redis = (redis_object *)zend_objects_get_address(&ra->redis[i] TSRMLS_CC); -#else - redis = (redis_object *)((char *)Z_OBJ_P(&ra->redis[i]) - XtOffsetOf(redis_object, std)); -#endif + redis = PHPREDIS_GET_OBJECT(redis_object, &ra->redis[i]); /* create socket */ redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); From ec4fd1bde055e32ea6b6f20d56d16cb7c21d6e88 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 9 Oct 2017 15:39:08 +0300 Subject: [PATCH 0854/1986] Issue #1238 This change allows to use empty string as persistant_id --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index ccc8ae6f23..a3eea4e4ae 100644 --- a/library.c +++ b/library.c @@ -1377,7 +1377,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->lazy_connect = lazy_connect; redis_sock->persistent_id = NULL; - if (persistent_id) { + if (persistent && persistent_id != NULL) { redis_sock->persistent_id = zend_string_init(persistent_id, strlen(persistent_id), 0); } From 9019463e70c384c51f96cb3ffe8c23744392c9f0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 13 Oct 2017 13:33:45 +0300 Subject: [PATCH 0855/1986] fix arginfo for directed node commands --- common.h | 33 ------------------------------ redis.c | 35 +++++++++++++++++++++++++++++++- redis_cluster.c | 54 ++++++++++++++++++++++++------------------------- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/common.h b/common.h index bbb747b75f..f3d4af53ef 100644 --- a/common.h +++ b/common.h @@ -1018,10 +1018,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_watch, 0, 0, 1) #endif ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0) - ZEND_ARG_INFO(0, option) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_command, 0, 0, 0) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, args) @@ -1039,35 +1035,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_rawcommand, 0, 0, 1) #endif ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2) - ZEND_ARG_INFO(0, cmd) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1) - ZEND_ARG_INFO(0, arg) - ZEND_ARG_INFO(0, option) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(arginfo_geoadd, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, lng) diff --git a/redis.c b/redis.c index 8475385d0d..33804dd238 100644 --- a/redis.c +++ b/redis.c @@ -83,6 +83,39 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 2) ZEND_ARG_INFO(0, retry_interval) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2) + ZEND_ARG_INFO(0, cmd) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1) + ZEND_ARG_INFO(0, cmd) +#if PHP_VERSION_ID >= 50600 + ZEND_ARG_VARIADIC_INFO(0, args) +#else + ZEND_ARG_INFO(0, ...) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1) + ZEND_ARG_INFO(0, arg) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 2) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) @@ -230,7 +263,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, eval, arginfo_eval, ZEND_ACC_PUBLIC) PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, exists, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, exists, arginfo_exists, ZEND_ACC_PUBLIC) PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) PHP_ME(Redis, flushAll, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, flushDB, arginfo_void, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index 550b23c9f6..0256dcbe3f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -46,7 +46,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, read_timeout) ZEND_ARG_INFO(0, persistent) -ZEND_END_ARG_INFO(); +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) ZEND_ARG_INFO(0, key) @@ -65,7 +65,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1) ZEND_ARG_INFO(0, pattern) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_cluster, 0, 0, 1) +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_or_address, 0, 0, 1) + ZEND_ARG_INFO(0, key_or_address) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_key_or_address_variadic, 0, 0, 1) + ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, arg) #if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_args) @@ -74,14 +79,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_cluster, 0, 0, 1) #endif ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 2) +ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) - ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 - ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif + ZEND_ARG_INFO(0, option) ZEND_END_ARG_INFO() /* Argument info for HSCAN, SSCAN, HSCAN */ @@ -90,7 +90,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) ZEND_ARG_INFO(1, i_iterator) ZEND_ARG_INFO(0, str_pattern) ZEND_ARG_INFO(0, i_count) -ZEND_END_ARG_INFO(); +ZEND_END_ARG_INFO() /* Argument infor for SCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_scan_cl, 0, 0, 2) @@ -98,7 +98,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_scan_cl, 0, 0, 2) ZEND_ARG_INFO(0, str_node) ZEND_ARG_INFO(0, str_pattern) ZEND_ARG_INFO(0, i_count) -ZEND_END_ARG_INFO(); +ZEND_END_ARG_INFO() /* Function table */ zend_function_entry redis_cluster_functions[] = { @@ -109,8 +109,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, bgsave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bgrewriteaof, arginfo_key_or_address, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bgsave, arginfo_key_or_address, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitcount, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitop, arginfo_bitop, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC) @@ -118,12 +118,12 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, brpop, arginfo_blrpop, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, clearlasterror, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, client, arginfo_client, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, client, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, close, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, cluster, arginfo_cluster, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, cluster, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, command, arginfo_command, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, config, arginfo_config, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, dbsize, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, config, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, dbsize, arginfo_key_or_address, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, decr, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, decrby, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, del, arginfo_del, ZEND_ACC_PUBLIC) @@ -136,8 +136,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, exists, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, expire, arginfo_expire, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, expireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, flushall, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, flushdb, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushall, arginfo_key_or_address, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushdb, arginfo_key_or_address, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) @@ -171,7 +171,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, incrbyfloat, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, info, arginfo_info, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, keys, arginfo_keys, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, lastsave, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, lastsave, arginfo_key_or_address, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lget, arginfo_lindex, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, lindex, arginfo_lindex, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, linsert, arginfo_linsert, ZEND_ACC_PUBLIC) @@ -194,14 +194,14 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pfcount, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, ping, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, ping, arginfo_key_or_address, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, pttl, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, publish, arginfo_publish, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, pubsub, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, randomkey, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, randomkey, arginfo_key_or_address, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, renamenx, arginfo_key_newkey, ZEND_ACC_PUBLIC) @@ -213,10 +213,10 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, rpushx, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sadd, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, saddarray, arginfo_sadd_array, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, save, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, save, arginfo_key_or_address, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, scan, arginfo_scan_cl, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, scard, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, script, arginfo_script, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, script, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sdiff, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sdiffstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, set, arginfo_set, ZEND_ACC_PUBLIC) @@ -228,7 +228,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, sinter, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sinterstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sismember, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, slowlog, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, smembers, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, smove, arginfo_smove, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, sort, arginfo_sort, ZEND_ACC_PUBLIC) @@ -2793,7 +2793,7 @@ PHP_METHOD(RedisCluster, script) { smart_string cmd = {0}; zval *z_args; short slot; - int i, argc = ZEND_NUM_ARGS(); + int argc = ZEND_NUM_ARGS(); /* Commands using this pass-thru don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { From 0b7ac2bde89863b8a761383f6e30c32f2af2ccd6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 14 Oct 2017 22:33:22 +0300 Subject: [PATCH 0856/1986] Sync package.xml and update PHP_REDIS_VERSION --- package.xml | 29 ++++++++++++----------------- php_redis.h | 2 +- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/package.xml b/package.xml index fad5f6c8be..d5c3873fd3 100644 --- a/package.xml +++ b/package.xml @@ -27,30 +27,26 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2017-08-31 + 2017-09-27 - 3.1.4RC1 - 3.1.4RC1 + 3.1.4 + 3.1.4 - beta - beta + stable + stable PHP - phpredis 3.1.4RC1 + phpredis 3.1.4 The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are also many bugfixes and minor improvements to the api, listed below: - * Allow mixing MULTI and PIPELINE modes! [5874b0] (Pavlo Yatsukhnenko) - * Let EXISTS take multiple keys [cccc39] (Michael Grunder) + * Allow mixing MULTI and PIPELINE modes (experimental)! [5874b0] (Pavlo Yatsukhnenko) * Added integration for coverty static analysis and fixed several warnings [faac8b0, eff7398, 4766c25, 0438ab4, 1e0b065, 733732a, 26eeda5, 735025, 42f1c9, af71d4] (Pavlo Yatsukhnenko) - * Added arginfo inrospection structures [81a0303, d5609fc, e5660be, 3c60e1f, 50dcb15, 6c2c6fa, - 212e323, e23be2c, 682593d, f8de702, 4ef3acd, f116be9, 5c111dd, 9caa029, 0d69650, 6859828, 024e593, - 3643ab6, f576fab, 122d41f, a09d0e6] (Tyson Andre, Pavlo Yatsukhnenko) * Fixed link to redis cluster documentation [3b0b06] (Pavlo Yatsukhnenko) * Remove unused PHP_RINIT and PHP_RSHUTDOWN functions [c760bf] (Pavlo Yatsukhnenko) * Removed duplicate HGET in redis array hash table, formatting [d0b9c5] (Pavlo Yatsukhnenko) @@ -124,17 +120,16 @@ http://pear.php.net/dtd/package-2.0.xsd"> - betabeta - 3.1.4RC13.1.4RC1 - 2017-08-31 + stablestable + 3.1.43.1.4 + 2017-09-27 - phpredis 3.1.4RC1 + phpredis 3.1.4 The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are also many bugfixes and minor improvements to the api, listed below: - * Allow mixing MULTI and PIPELINE modes! [5874b0] (Pavlo Yatsukhnenko) - * Let EXISTS take multiple keys [cccc39] (Michael Grunder) + * Allow mixing MULTI and PIPELINE modes (experimental)! [5874b0] (Pavlo Yatsukhnenko) * Added integration for coverty static analysis and fixed several warnings [faac8b0, eff7398, 4766c25, 0438ab4, 1e0b065, 733732a, 26eeda5, 735025, 42f1c9, af71d4] (Pavlo Yatsukhnenko) diff --git a/php_redis.h b/php_redis.h index 0daf159424..895ad710f7 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "3.1.4RC1" +#define PHP_REDIS_VERSION "develop" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 49b7f41d51122786cb1d754f98da331a8ca74dde Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 14 Oct 2017 22:41:35 +0300 Subject: [PATCH 0857/1986] Issue #1238 --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 33804dd238..038349e1ea 100644 --- a/redis.c +++ b/redis.c @@ -873,7 +873,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zval *object; - char *host = NULL, *persistent_id = NULL; + char *host = NULL, *persistent_id = ""; zend_long port = -1, retry_interval = 0; strlen_t host_len, persistent_id_len; double timeout = 0.0, read_timeout = 0.0; From b9ca16cc5ba42d74d4e4bceb5806efb376841f29 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 17 Oct 2017 16:25:26 +0300 Subject: [PATCH 0858/1986] fix memory leak added in PR #1247 --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index a3eea4e4ae..63c0c25170 100644 --- a/library.c +++ b/library.c @@ -1730,7 +1730,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc IF_NOT_ATOMIC() { add_next_index_zval(z_tab, z_multi_result); } else { - RETVAL_ZVAL(z_multi_result, 1, 0); + RETVAL_ZVAL(z_multi_result, 0, 1); } return 0; } From 8cb2d5bd94a17c046a50cd4b626d6cae1c41e0f6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 3 May 2017 15:54:28 +0300 Subject: [PATCH 0859/1986] Issue #1048 This commit is adding support of data compression. LZF was choosen because it small and fast and Redis server uses it. Since [pecl package](https://pecl.php.net/package/lzf) doesn't provide lzf.h file after installing, LZF library was added as submodule. Another algorythms may be easely added by analogy with serializers. TODO: unit-tests for different data types. --- .gitmodules | 3 ++ .travis.yml | 4 +-- cluster_library.c | 14 ++++----- common.h | 5 ++++ config.m4 | 17 ++++++++--- liblzf | 1 + library.c | 73 +++++++++++++++++++++++++++++++++++++++++---- library.h | 3 ++ package.xml | 4 +++ redis.c | 10 +++++++ redis_cluster.c | 3 +- redis_commands.c | 23 ++++++++++---- tests/RedisTest.php | 18 +++++++++++ 13 files changed, 151 insertions(+), 27 deletions(-) create mode 100644 .gitmodules create mode 160000 liblzf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..948e9ec398 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "liblzf"] + path = liblzf + url = https://github.com/nemequ/liblzf.git diff --git a/.travis.yml b/.travis.yml index 6701b311d5..eaeda8b39c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,11 +30,11 @@ addons: before_install: - phpize - pecl install igbinary - - ./configure --enable-redis-igbinary CFLAGS=-Wall + - ./configure --enable-redis-igbinary --enable-redis-lzf CFLAGS=-Wall install: make install before_script: - gem install redis - - mkdir -p tests/nodes/ && echo >> tests/nodes/nodemap + - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap - for PORT in $(seq 6379 6382); do redis-server --port $PORT --daemonize yes; done - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done - wget https://raw.githubusercontent.com/antirez/redis/unstable/src/redis-trib.rb diff --git a/cluster_library.c b/cluster_library.c index 3e532b77f8..84f5ff28ca 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -456,7 +456,7 @@ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val int val_free; // Serialize our value - val_free = redis_serialize(c->flags, z_val, &val, &val_len TSRMLS_CC); + val_free = redis_pack(c->flags, z_val, &val, &val_len TSRMLS_CC); // Attach it to the provied keyval entry kv->val = val; @@ -1495,12 +1495,12 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } if (CLUSTER_IS_ATOMIC(c)) { - if (!redis_unserialize(c->flags, resp, c->reply_len, return_value TSRMLS_CC)) { + if (!redis_unpack(c->flags, resp, c->reply_len, return_value TSRMLS_CC)) { CLUSTER_RETURN_STRING(c, resp, c->reply_len); } } else { zval zv, *z = &zv; - if (redis_unserialize(c->flags, resp, c->reply_len, z TSRMLS_CC)) { + if (redis_unpack(c->flags, resp, c->reply_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); *z = zv; @@ -2322,7 +2322,7 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, if (line != NULL) { zval zv, *z = &zv; - if (redis_unserialize(redis_sock, line, line_len, z TSRMLS_CC)) { + if (redis_unpack(redis_sock, line, line_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); *z = zv; @@ -2367,7 +2367,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, } else { /* Attempt serialization */ zval zv, *z = &zv; - if (redis_unserialize(redis_sock, line, line_len, z TSRMLS_CC)) { + if (redis_unpack(redis_sock, line, line_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); *z = zv; @@ -2406,7 +2406,7 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, key_len = line_len; } else { zval zv, *z = &zv; - if (redis_unserialize(redis_sock,key,key_len, z TSRMLS_CC)) { + if (redis_unpack(redis_sock,key,key_len, z TSRMLS_CC)) { zend_string *zstr = zval_get_string(z); add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line)); zend_string_release(zstr); @@ -2440,7 +2440,7 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, if(line != NULL) { zval zv, *z = &zv; - if (redis_unserialize(redis_sock, line, line_len, z TSRMLS_CC)) { + if (redis_unpack(redis_sock, line, line_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); *z = zv; diff --git a/common.h b/common.h index f3d4af53ef..bdb99b097f 100644 --- a/common.h +++ b/common.h @@ -478,6 +478,7 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_READ_TIMEOUT 3 #define REDIS_OPT_SCAN 4 #define REDIS_OPT_FAILOVER 5 +#define REDIS_OPT_COMPRESSION 6 /* cluster options */ #define REDIS_FAILOVER_NONE 0 @@ -488,6 +489,9 @@ typedef enum _PUBSUB_TYPE { #define REDIS_SERIALIZER_NONE 0 #define REDIS_SERIALIZER_PHP 1 #define REDIS_SERIALIZER_IGBINARY 2 +/* compression */ +#define REDIS_COMPRESSION_NONE 0 +#define REDIS_COMPRESSION_LZF 1 /* SCAN options */ #define REDIS_SCAN_NORETRY 0 @@ -651,6 +655,7 @@ typedef struct { zend_string *persistent_id; int serializer; + int compression; long dbNumber; zend_string *prefix; diff --git a/config.m4 b/config.m4 index 3a80badf09..93d5732200 100755 --- a/config.m4 +++ b/config.m4 @@ -3,14 +3,16 @@ dnl config.m4 for extension redis PHP_ARG_ENABLE(redis, whether to enable redis support, dnl Make sure that the comment is aligned: -[ --enable-redis Enable redis support]) +[ --enable-redis Enable redis support]) PHP_ARG_ENABLE(redis-session, whether to enable sessions, -[ --disable-redis-session Disable session support], yes, no) +[ --disable-redis-session Disable session support], yes, no) PHP_ARG_ENABLE(redis-igbinary, whether to enable igbinary serializer support, -[ --enable-redis-igbinary Enable igbinary serializer support], no, no) +[ --enable-redis-igbinary Enable igbinary serializer support], no, no) +PHP_ARG_ENABLE(redis-lzf, whether to enable lzf compression, +[ --enable-redis-lzf Enable lzf compression support], no, no) if test "$PHP_REDIS" != "no"; then @@ -60,6 +62,13 @@ dnl Check for igbinary AC_MSG_RESULT([disabled]) fi + if test "$PHP_REDIS_LZF" != "no"; then + PHP_ADD_INCLUDE(liblzf) + PHP_ADD_BUILD_DIR(liblzf) + lzf_sources="liblzf/lzf_c.c liblzf/lzf_d.c" + AC_DEFINE(HAVE_REDIS_LZF, 1, [ ]) + fi + dnl # --with-redis -> check with-path dnl SEARCH_PATH="/usr/local /usr" # you might want to change this dnl SEARCH_FOR="/include/redis.h" # you most likely want to change this @@ -99,5 +108,5 @@ dnl Check for igbinary dnl dnl PHP_SUBST(REDIS_SHARED_LIBADD) - PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c, $ext_shared) + PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c $lzf_sources, $ext_shared) fi diff --git a/liblzf b/liblzf new file mode 160000 index 0000000000..fb25820c3c --- /dev/null +++ b/liblzf @@ -0,0 +1 @@ +Subproject commit fb25820c3c0aeafd127956ae6c115063b47e459a diff --git a/library.c b/library.c index 63c0c25170..ca147b45b1 100644 --- a/library.c +++ b/library.c @@ -11,6 +11,9 @@ #ifdef HAVE_REDIS_IGBINARY #include "igbinary/igbinary.h" #endif +#ifdef HAVE_REDIS_LZF +#include "lzf.h" +#endif #include #include "php_redis.h" #include "library.h" @@ -602,7 +605,7 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k break; case 'v': arg.zv = va_arg(ap, zval*); - argfree = redis_serialize(redis_sock, arg.zv, &dup, &arglen TSRMLS_CC); + argfree = redis_pack(redis_sock, arg.zv, &dup, &arglen TSRMLS_CC); redis_cmd_append_sstr(&cmd, dup, arglen); if (argfree) efree(dup); break; @@ -710,7 +713,7 @@ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock strlen_t vallen; int valfree, retval; - valfree = redis_serialize(redis_sock, z, &val, &vallen TSRMLS_CC); + valfree = redis_pack(redis_sock, z, &val, &vallen TSRMLS_CC); retval = redis_cmd_append_sstr(str, val, vallen); if (valfree) efree(val); @@ -1248,7 +1251,7 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } IF_NOT_ATOMIC() { zval zv, *z = &zv; - if (redis_unserialize(redis_sock, response, response_len, z TSRMLS_CC)) { + if (redis_unpack(redis_sock, response, response_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); *z = zv; @@ -1258,7 +1261,7 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock add_next_index_stringl(z_tab, response, response_len); } } else { - if (!redis_unserialize(redis_sock, response, response_len, return_value TSRMLS_CC)) { + if (!redis_unpack(redis_sock, response, response_len, return_value TSRMLS_CC)) { RETVAL_STRINGL(response, response_len); } } @@ -1386,6 +1389,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->read_timeout = read_timeout; redis_sock->serializer = REDIS_SERIALIZER_NONE; + redis_sock->compression = REDIS_COMPRESSION_NONE; redis_sock->mode = ATOMIC; redis_sock->head = NULL; redis_sock->current = NULL; @@ -1661,7 +1665,7 @@ redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, (unserialize == UNSERIALIZE_VALS && i % 2 != 0) ); zval zv, *z = &zv; - if (unwrap && redis_unserialize(redis_sock, line, len, z TSRMLS_CC)) { + if (unwrap && redis_unpack(redis_sock, line, len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); *z = zv; @@ -1709,7 +1713,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { zval zv0, *z = &zv0; - if (redis_unserialize(redis_sock, response, response_len, z TSRMLS_CC)) { + if (redis_unpack(redis_sock, response, response_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z); *z = zv0; @@ -1778,6 +1782,63 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) efree(redis_sock); } +PHP_REDIS_API int +redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC) +{ + char *buf, *data; + int valfree; + strlen_t len; + uint32_t res; + + valfree = redis_serialize(redis_sock, z, &buf, &len TSRMLS_CC); + switch (redis_sock->compression) { + case REDIS_COMPRESSION_LZF: +#ifdef HAVE_REDIS_LZF + data = emalloc(len); + res = lzf_compress(buf, len, data, len - 1); + if (res > 0 && res < len) { + if (valfree) efree(buf); + *val = data; + *val_len = res; + return 1; + } + efree(data); +#endif + break; + } + *val = buf; + *val_len = len; + return valfree; +} + +PHP_REDIS_API int +redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC) +{ + char *data; + int i, ret; + uint32_t res; + + switch (redis_sock->compression) { + case REDIS_COMPRESSION_LZF: +#ifdef HAVE_REDIS_LZF + errno = E2BIG; + for (i = 1; errno == E2BIG; ++i) { + data = emalloc(i * val_len); + if ((res = lzf_decompress(val, val_len, data, i * val_len)) == 0) { + efree(data); + continue; + } else if (redis_unserialize(redis_sock, data, res, z_ret TSRMLS_CC) == 0) { + ZVAL_STRINGL(z_ret, data, res); + } + efree(data); + return 1; + } +#endif + break; + } + return redis_unserialize(redis_sock, val, val_len, z_ret TSRMLS_CC); +} + PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC) diff --git a/library.h b/library.h index 0dc527b060..e19848b702 100644 --- a/library.h +++ b/library.h @@ -76,6 +76,9 @@ redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len); PHP_REDIS_API int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC); +PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); + /* * Variant Read methods, mostly to implement eval */ diff --git a/package.xml b/package.xml index d5c3873fd3..724861b93b 100644 --- a/package.xml +++ b/package.xml @@ -92,6 +92,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + @@ -117,6 +120,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> redis + diff --git a/redis.c b/redis.c index 038349e1ea..ff83159fd2 100644 --- a/redis.c +++ b/redis.c @@ -676,6 +676,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION TSRMLS_CC); /* serializer */ zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE TSRMLS_CC); @@ -684,6 +685,12 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); #endif + /* compression */ + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_NONE"), REDIS_COMPRESSION_NONE TSRMLS_CC); +#ifdef HAVE_REDIS_LZF + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZF"), REDIS_COMPRESSION_LZF TSRMLS_CC); +#endif + /* scan options*/ zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY TSRMLS_CC); @@ -806,6 +813,9 @@ PHP_MINFO_FUNCTION(redis) php_info_print_table_row(2, "Available serializers", "php, igbinary"); #else php_info_print_table_row(2, "Available serializers", "php"); +#endif +#ifdef HAVE_REDIS_LZF + php_info_print_table_row(2, "Available compression", "lzf"); #endif php_info_print_table_end(); } diff --git a/redis_cluster.c b/redis_cluster.c index 0256dcbe3f..94b0077cf9 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -655,8 +655,7 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, } // Serialize our value if required - kv->val_free = redis_serialize(c->flags,z_val,&(kv->val),&(kv->val_len) - TSRMLS_CC); + kv->val_free = redis_pack(c->flags,z_val,&(kv->val),&(kv->val_len) TSRMLS_CC); // Success return 0; diff --git a/redis_commands.c b/redis_commands.c index 02317f592a..4e88b2f051 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1007,7 +1007,7 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Iterate our hash table, serializing and appending values */ ZEND_HASH_FOREACH_VAL(ht_arr, z_val) { - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + val_free = redis_pack(redis_sock, z_val, &val, &val_len TSRMLS_CC); redis_cmd_append_sstr(&cmdstr, val, val_len); if (val_free) efree(val); } ZEND_HASH_FOREACH_END(); @@ -1533,7 +1533,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Serialize value (if directed) - val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + val_free = redis_pack(redis_sock, z_val, &val, &val_len TSRMLS_CC); // Append the key and value to our command redis_cmd_append_sstr(&cmdstr, mem, mem_len); @@ -1761,8 +1761,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } } else { - mem_free = redis_serialize(redis_sock, z_ele, &mem, &mem_len - TSRMLS_CC); + mem_free = redis_pack(redis_sock, z_ele, &mem, &mem_len TSRMLS_CC); zstr = NULL; if(!mem_free) { @@ -2488,8 +2487,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(&z_args[i])); } // serialize value if requested - val_free = redis_serialize(redis_sock, &z_args[i+1], &val, &val_len - TSRMLS_CC); + val_free = redis_pack(redis_sock, &z_args[i+1], &val, &val_len TSRMLS_CC); redis_cmd_append_sstr(&cmdstr, val, val_len); // Free value if we serialized @@ -3012,6 +3010,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, switch(option) { case REDIS_OPT_SERIALIZER: RETURN_LONG(redis_sock->serializer); + case REDIS_OPT_COMPRESSION: + RETURN_LONG(redis_sock->compression); case REDIS_OPT_PREFIX: if (redis_sock->prefix) { RETURN_STRINGL(ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix)); @@ -3055,6 +3055,17 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_TRUE; } break; + case REDIS_OPT_COMPRESSION: + val_long = atol(val_str); + if (val_long == REDIS_COMPRESSION_NONE +#ifdef HAVE_REDIS_LZF + || val_long == REDIS_COMPRESSION_LZF +#endif + ) { + redis_sock->compression = val_long; + RETURN_TRUE; + } + break; case REDIS_OPT_PREFIX: if (redis_sock->prefix) { zend_string_release(redis_sock->prefix); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index fed7915c11..86292d9146 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4256,6 +4256,24 @@ private function checkSerializer($mode) { $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok } + public function testCompressionLZF() + { + if (!defined('Redis::COMPRESSION_LZF')) { + $this->markTestSkipped(); + } + $this->checkCompression(Redis::COMPRESSION_LZF); + } + + private function checkCompression($mode) + { + $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION, $mode) === TRUE); // set ok + $this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION) === $mode); // get ok + + $val = 'xxxxxxxxxx'; + $this->redis->set('key', $val); + $this->assertEquals($val, $this->redis->get('key')); + } + public function testDumpRestore() { if (version_compare($this->version, "2.5.0", "lt")) { From e2c51251d54ebffccd603e7c37c17b9114d9edfe Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 17 Oct 2017 10:32:11 +0300 Subject: [PATCH 0860/1986] Issue #1048 Add --with-liblzf configure option to use system liblzf. Use exponentially growing buffer for lzf_decompress. Move liblzf files to separeted dir in package.xml. --- config.m4 | 34 +++++++++++++++++++++++++++++++--- library.c | 7 +++++-- package.xml | 24 ++++++++++++++---------- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/config.m4 b/config.m4 index 93d5732200..5f2586fa3e 100755 --- a/config.m4 +++ b/config.m4 @@ -14,6 +14,9 @@ PHP_ARG_ENABLE(redis-igbinary, whether to enable igbinary serializer support, PHP_ARG_ENABLE(redis-lzf, whether to enable lzf compression, [ --enable-redis-lzf Enable lzf compression support], no, no) +PHP_ARG_WITH(liblzf, use system liblzf, +[ --with-liblzf[=DIR] Use system liblzf], no, no) + if test "$PHP_REDIS" != "no"; then if test "$PHP_REDIS_SESSION" != "no"; then @@ -63,10 +66,35 @@ dnl Check for igbinary fi if test "$PHP_REDIS_LZF" != "no"; then - PHP_ADD_INCLUDE(liblzf) - PHP_ADD_BUILD_DIR(liblzf) - lzf_sources="liblzf/lzf_c.c liblzf/lzf_d.c" AC_DEFINE(HAVE_REDIS_LZF, 1, [ ]) + if test "$PHP_LIBLZF" != "no"; then + AC_MSG_CHECKING(for liblzf files in default path) + for i in $PHP_LIBLZF /usr/local /usr; do + if test -r $i/include/lzf.h; then + AC_MSG_RESULT(found in $i) + LIBLZF_DIR=$i + break + fi + done + if test -z "$LIBLZF_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Please reinstall the liblzf distribution]) + fi + PHP_CHECK_LIBRARY(lzf, lzf_compress, + [ + PHP_ADD_LIBRARY_WITH_PATH(lzf, $LIBLZF_DIR/lib, LZF_SHARED_LIBADD) + ], [ + AC_MSG_ERROR([could not find usable liblzf]) + ], [ + -L$LIBLZF_DIR/lib + ]) + PHP_SUBST(LZF_SHARED_LIBADD) + else + PHP_ADD_INCLUDE(liblzf) + PHP_ADD_INCLUDE($ext_srcdir/liblzf) + PHP_ADD_BUILD_DIR(liblzf) + lzf_sources="liblzf/lzf_c.c liblzf/lzf_d.c" + fi fi dnl # --with-redis -> check with-path diff --git a/library.c b/library.c index ca147b45b1..959a7aded4 100644 --- a/library.c +++ b/library.c @@ -12,7 +12,7 @@ #include "igbinary/igbinary.h" #endif #ifdef HAVE_REDIS_LZF -#include "lzf.h" +#include #endif #include #include "php_redis.h" @@ -1822,9 +1822,12 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TS case REDIS_COMPRESSION_LZF: #ifdef HAVE_REDIS_LZF errno = E2BIG; - for (i = 1; errno == E2BIG; ++i) { + /* start from two-times bigger buffer and + * increase it exponentially if needed */ + for (i = 2; errno == E2BIG; i *= 2) { data = emalloc(i * val_len); if ((res = lzf_decompress(val, val_len, data, i * val_len)) == 0) { + /* errno != E2BIG will brake for loop */ efree(data); continue; } else if (redis_unserialize(redis_sock, data, res, z_ret TSRMLS_CC) == 0) { diff --git a/package.xml b/package.xml index 724861b93b..bec916c118 100644 --- a/package.xml +++ b/package.xml @@ -67,34 +67,38 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + - - + + + - + + - - - - - - - + + + + + + + From 81859f1c7842184efde9fc839fb1164325784687 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 17 Oct 2017 10:54:56 +0300 Subject: [PATCH 0861/1986] =?UTF-8?q?Ignore=20all=20changes=20to=20the=20l?= =?UTF-8?q?iblzf=20submodule=E2=80=99s=20work=20tree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 948e9ec398..e75b96dd5b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "liblzf"] - path = liblzf - url = https://github.com/nemequ/liblzf.git + path = liblzf + url = https://github.com/nemequ/liblzf.git + ignore = dirty From 8657557fb114c0d4a4e9a03296e41038d639cdc2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 17 Oct 2017 17:01:51 +0300 Subject: [PATCH 0862/1986] fix config.m4 --- config.m4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.m4 b/config.m4 index 5f2586fa3e..ef4d9e0919 100755 --- a/config.m4 +++ b/config.m4 @@ -82,13 +82,13 @@ dnl Check for igbinary fi PHP_CHECK_LIBRARY(lzf, lzf_compress, [ - PHP_ADD_LIBRARY_WITH_PATH(lzf, $LIBLZF_DIR/lib, LZF_SHARED_LIBADD) + PHP_ADD_LIBRARY_WITH_PATH(lzf, $LIBLZF_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD) ], [ AC_MSG_ERROR([could not find usable liblzf]) ], [ - -L$LIBLZF_DIR/lib + -L$LIBLZF_DIR/$PHP_LIBDIR ]) - PHP_SUBST(LZF_SHARED_LIBADD) + PHP_SUBST(REDIS_SHARED_LIBADD) else PHP_ADD_INCLUDE(liblzf) PHP_ADD_INCLUDE($ext_srcdir/liblzf) From 621afa6c6d9f0d71a68361e7ae40c75fce1fa382 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 19 Oct 2017 16:11:26 +0300 Subject: [PATCH 0863/1986] Refactor ra_extract_key We don't need to copy extracted key part since we don't change it. --- redis_array_impl.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 1a5aa7bac2..4a60fdde4e 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -401,7 +401,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR if (Z_TYPE(z_ret) == IS_STRING) { *out_len = Z_STRLEN(z_ret); - out = estrndup(Z_STRVAL(z_ret), *out_len); + out = Z_STRVAL(z_ret); } zval_dtor(&z_argv); @@ -418,11 +418,11 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS if (Z_TYPE(ra->z_fun) != IS_NULL) { return ra_call_extractor(ra, key, key_len, out_len TSRMLS_CC); } else if ((start = strchr(key, '{')) == NULL || (end = strchr(start + 1, '}')) == NULL) { - return estrndup(key, key_len); + return (char *)key; } /* found substring */ *out_len = end - start - 1; - return estrndup(start + 1, *out_len); + return start + 1; } /* call userland key distributor function */ @@ -461,13 +461,13 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D int pos, out_len; /* extract relevant part of the key */ - out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC); - if(!out) return NULL; + if ((out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC)) == NULL) { + return NULL; + } if (Z_TYPE(ra->z_dist) != IS_NULL) { pos = ra_call_distributor(ra, key, key_len TSRMLS_CC); if (pos < 0 || pos >= ra->count) { - efree(out); return NULL; } } else { @@ -483,7 +483,6 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D /* get position on ring */ pos = (int)(hash * ra->count / 0xffffffff); } - efree(out); if(out_pos) *out_pos = pos; From 75e7c81dad243eda06c332dcd4c5ecfe2e0c4a68 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 19 Oct 2017 16:46:32 +0300 Subject: [PATCH 0864/1986] Revert "Refactor ra_extract_key" This reverts commit 621afa6c6d9f0d71a68361e7ae40c75fce1fa382. --- redis_array_impl.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 4a60fdde4e..1a5aa7bac2 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -401,7 +401,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR if (Z_TYPE(z_ret) == IS_STRING) { *out_len = Z_STRLEN(z_ret); - out = Z_STRVAL(z_ret); + out = estrndup(Z_STRVAL(z_ret), *out_len); } zval_dtor(&z_argv); @@ -418,11 +418,11 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS if (Z_TYPE(ra->z_fun) != IS_NULL) { return ra_call_extractor(ra, key, key_len, out_len TSRMLS_CC); } else if ((start = strchr(key, '{')) == NULL || (end = strchr(start + 1, '}')) == NULL) { - return (char *)key; + return estrndup(key, key_len); } /* found substring */ *out_len = end - start - 1; - return start + 1; + return estrndup(start + 1, *out_len); } /* call userland key distributor function */ @@ -461,13 +461,13 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D int pos, out_len; /* extract relevant part of the key */ - if ((out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC)) == NULL) { - return NULL; - } + out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC); + if(!out) return NULL; if (Z_TYPE(ra->z_dist) != IS_NULL) { pos = ra_call_distributor(ra, key, key_len TSRMLS_CC); if (pos < 0 || pos >= ra->count) { + efree(out); return NULL; } } else { @@ -483,6 +483,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D /* get position on ring */ pos = (int)(hash * ra->count / 0xffffffff); } + efree(out); if(out_pos) *out_pos = pos; From 9d15f07adb0de7ae02111f942bf7d0957bbc25d0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 20 Oct 2017 16:37:11 +0300 Subject: [PATCH 0865/1986] Issue #1224 --- redis.c | 2 ++ redis_array.c | 2 ++ redis_cluster.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/redis.c b/redis.c index ff83159fd2..18e7ef909d 100644 --- a/redis.c +++ b/redis.c @@ -547,6 +547,8 @@ create_redis_object(zend_class_entry *ce TSRMLS_DC) zval *tmp; zend_hash_copy(redis->std.properties, &ce->default_properties, (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); +#else + object_properties_init(&redis->std, ce); #endif retval.handle = zend_objects_store_put(redis, diff --git a/redis_array.c b/redis_array.c index b293253ffc..01a48e5fcf 100644 --- a/redis_array.c +++ b/redis_array.c @@ -187,6 +187,8 @@ create_redis_array_object(zend_class_entry *ce TSRMLS_DC) zval *tmp; zend_hash_copy(obj->std.properties, &ce->default_properties, (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); +#else + object_properties_init(&obj->std, ce); #endif retval.handle = zend_objects_store_put(obj, diff --git a/redis_cluster.c b/redis_cluster.c index 94b0077cf9..f113647167 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -332,6 +332,8 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { zend_hash_copy(cluster->std.properties, &class_type->default_properties, (copy_ctor_func_t)zval_add_ref, (void*)&tmp, sizeof(zval*)); +#else + object_properties_init(&cluster->std, class_type); #endif retval.handle = zend_objects_store_put(cluster, From a8859b458d917f4289bd32a26330e954e5e59dd2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 22 Oct 2017 17:22:21 -0700 Subject: [PATCH 0866/1986] Document GEO* commands --- README.markdown | 295 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 289 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index f0da191094..e9c724da7e 100644 --- a/README.markdown +++ b/README.markdown @@ -2965,6 +2965,289 @@ while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { } ~~~ +## Geocoding + +### geoAdd +----- + +##### *Prototype* +~~~ +$redis->geoAdd($key, $longitude, $latitude, $member [, $longitude, $lattitude, $member, ...]); +~~~ + +_**Description**_: Add one or more geospacial items to the specified key. This function must be called with at least one _longitude, latitude, member_ triplet. + +##### *Return value* +*Integer*: The number of elements added to the geospacial key. + +##### *Example* +~~~ +$redis->del("myplaces"); + +/* Since the key will be new, $result will be 2 */ +$result = $redis->geoAdd( + "myplaces", + 37.773, -122.431, "San Francisco", + -157.858, 21.315, "Honolulu" +); +~~~ + +### geoHash +----- + +##### *Prototype* +~~~ +$redis->geoHash($key, $member [, $member, $member, ...]); +~~~ + +_**Description**_: Retreive Geohash strings for one or more elements of a geospacial index. + +##### *Return value* +*Array*: One or more Redis Geohash encoded strings. + +##### *Example* +~~~ +$redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); +$hashes = $redis->geoHash("hawaii", "Honolulu", "Maui"); +var_dump($hashes); +~~~ + +##### *Output* +~~~ +array(2) { + [0]=> + string(11) "87z9pyek3y0" + [1]=> + string(11) "8e8y6d5jps0" +} +~~~ + +### geoPos +----- + +##### *Prototype* +~~~ +$redis->geoPos($key, $member [, $member, $member, ...]); +~~~ + +_**Description**_: Return longitude, lattitude positions for each requested member. + +##### *Return value* +*Array*: One or more longitude/latitude positions + +##### *Example* +~~~ +$redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); +$positions = $redis->geoPos("hawaii", "Honolulu", "Maui"); +var_dump($positions); +~~~ + +##### *Output* +~~~ +array(2) { + [0]=> + array(2) { + [0]=> + string(22) "-157.85800248384475708" + [1]=> + string(19) "21.3060004581273077" + } + [1]=> + array(2) { + [0]=> + string(22) "-156.33099943399429321" + [1]=> + string(20) "20.79799924753607598" + } +} +~~~ + +### GeoDist +----- + +##### *Prototype* +~~~ +$redis->geoDist($key, $member1, $member2 [, $unit]); +~~~ + + +_**Description**_: Return the distance between two members in a geospacial set. If units are passed it must be one of the following values: + +* 'm' => Meters +* 'km' => Kilometers +* 'mi' => Miles +* 'ft' => Feet + +##### *Return value* +*Double*: The distance between the two passed members in the units requested (meters by default). + +##### *Example* +~~~ +$redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); + +$meters = $redis->geoDist("hawaii", "Honolulu", "Maui"); +$kilometers = $redis->geoDist("hawaii", "Honolulu", "Maui", 'km'); +$miles = $redis->geoDist("hawaii", "Honolulu", "Maui", 'mi'); +$feet = $redis->geoDist("hawaii", "Honolulu", "Maui", 'ft'); + +echo "Distance between Honolulu and Maui:\n"; +echo " meters : $meters\n"; +echo " kilometers: $kilometers\n"; +echo " miles : $miles\n"; +echo " feet : $feet\n"; + +/* Bad unit */ +$inches = $redis->geoDist("hawaii", "Honolulu", "Maui", 'in'); +echo "Invalid unit returned:\n"; +var_dump($inches); +~~~ + +##### *Output* +~~~ +Distance between Honolulu and Maui: + meters : 168275.204 + kilometers: 168.2752 + miles : 104.5616 + feet : 552084.0028 +Invalid unit returned: +bool(false) +~~~ + +### geoRadius +----- + +##### *Prototype* +~~~ +$redis->geoRadius($key, $longitude, $latitude, $radius, $unit [, Array $options]); +~~~ + +_**Description**_: Return members of a set with geospacial information that are within the radius specified by the caller. + +##### *Options Array* +The georadius command can be called with various options that control how Redis returns results. The following table describes the options phpredis supports. All options are case insensitive. + +Key | Value | Description +--- | --- | ---- | +COUNT | `integer $count` | Limit how many results are returned + | WITHCOORD | Return longitude and latitude of matching members + | WITHDIST | Return the distance from the center + | WITHHASH | Return the raw geohash-encoded score + | ASC | Sort results in ascending order + | DESC | Sort results in descending order + + *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will win! + *Note*: PhpRedis does not currently support the `STORE` or `STOREDIST` options but will be added to future versions. + +##### *Return value* +*Array*: Zero or more entries that are within the provided radius. + +##### *Example* +~~~ +/* Add some cities */ +$redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); + +echo "Within 300 miles of Honolulu:\n"; +var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi')); + +echo "\nWithin 300 miles of Honolulu with distances:\n"; +$options = ['WITHDIST']; +var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options)); + +echo "\nFirst result within 300 miles of Honolulu with distances:\n"; +$options['count'] = 1; +var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options)); + +echo "\nFirst result within 300 miles of Honolulu with distances in descending sort order:\n"; +$options[] = 'DESC'; +var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options)); +~~~ + +##### *Output* +~~~ +Within 300 miles of Honolulu: +array(2) { + [0]=> + string(8) "Honolulu" + [1]=> + string(4) "Maui" +} + +Within 300 miles of Honolulu with distances: +array(2) { + [0]=> + array(2) { + [0]=> + string(8) "Honolulu" + [1]=> + string(6) "0.0002" + } + [1]=> + array(2) { + [0]=> + string(4) "Maui" + [1]=> + string(8) "104.5615" + } +} + +First result within 300 miles of Honolulu with distances: +array(1) { + [0]=> + array(2) { + [0]=> + string(8) "Honolulu" + [1]=> + string(6) "0.0002" + } +} + +First result within 300 miles of Honolulu with distances in descending sort order: +array(1) { + [0]=> + array(2) { + [0]=> + string(4) "Maui" + [1]=> + string(8) "104.5615" + } +} +~~~ + +### geoRadiusByMember + +##### *Prototype* +~~~ +$redis->geoRadiusByMember($key, $member, $radius, $units [, array $options]); +~~~ + +_**Description**_: This method is identical to `geoRadius` except that instead of passing a longitude and latitude as the "source" you pass an existing member in the geospacial set. + +##### *Options Array* +See [geoRadius](#georadius) command for options array. + +##### *Return value* +*Array*: The zero or more entries that are close enough to the member given the distance and radius specified. + +##### *Example* +~~~ +$redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); + +echo "Within 300,000 miles of Honolulu:\n"; +var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi')); +~~~ + +##### *Output* +~~~ +Within 300,000 meters of Honolulu: +array(2) { + [0]=> + string(8) "Honolulu" + [1]=> + string(4) "Maui" +} +~~~ + + ## Pub/sub * [pSubscribe](#psubscribe) - Subscribe to channels by pattern @@ -2977,9 +3260,9 @@ while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { _**Description**_: Subscribe to channels by pattern ##### *Parameters* -*patterns*: An array of patterns to match -*callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) -*return value*: Mixed. Any non-null return value in the callback will be returned to the caller. +*patterns*: An array of patterns to match +*callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) +*return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~ function pSubscribe($redis, $pattern, $chan, $msg) { @@ -3070,13 +3353,13 @@ The return value can be various types depending on what the server itself return ##### *Example* ```php /* Returns: true */ -$redis->rawCommand("set", "foo", "bar"); +$redis->rawCommand("set", "foo", "bar"); /* Returns: "bar" */ -$redis->rawCommand("get", "foo"); +$redis->rawCommand("get", "foo"); /* Returns: 3 */ -$redis->rawCommand("rpush", "mylist", "one", 2, 3.5)); +$redis->rawCommand("rpush", "mylist", "one", 2, 3.5)); /* Returns: ["one", "2", "3.5000000000000000"] */ $redis->rawCommand("lrange", "mylist", 0, -1); From f7edee5d8bcc6ab6bab72a5a5fa8423a2ac90ad1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 22 Oct 2017 18:48:02 -0700 Subject: [PATCH 0867/1986] More robust GEORADIUS COUNT validation While testing example code for Geo codes I discovered that passing a negative count would cause PhpRedis to hang as it would send an invalid number of arguments to the server. --- redis_commands.c | 37 ++++++++++++++++++++++++++++--------- tests/RedisTest.php | 10 ++++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 4e88b2f051..26cd9f9da1 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2567,8 +2567,9 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */ -static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, - int *withhash, long *count, geoSortType *sort) +static int get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, + int *withhash, long *count, geoSortType *sort + TSRMLS_DC) { ulong idx; char *optstr; @@ -2582,7 +2583,13 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, ZVAL_DEREF(optval); /* If the key is numeric it's a non value option */ if (zkey) { - if (ZSTR_LEN(zkey) == 5 && !strcasecmp(ZSTR_VAL(zkey), "count") && Z_TYPE_P(optval) == IS_LONG) { + if (ZSTR_LEN(zkey) == 5 && !strcasecmp(ZSTR_VAL(zkey), "count")) { + if (Z_TYPE_P(optval) != IS_LONG || Z_LVAL_P(optval) <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "COUNT must be an integer > 0!"); + return FAILURE; + } + + /* Set our count */ *count = Z_LVAL_P(optval); } } else { @@ -2604,6 +2611,9 @@ static void get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, } } } ZEND_HASH_FOREACH_END(); + + /* Success */ + return SUCCESS; } /* Helper to append options to a GEORADIUS or GEORADIUSBYMEMBER command */ @@ -2630,7 +2640,7 @@ void append_georadius_opts(smart_string *str, int withcoord, int withdist, } /* Append our count if we've got one */ - if (count > 0) { + if (count) { REDIS_CMD_APPEND_SSTR_STATIC(str, "COUNT"); redis_cmd_append_sstr_long(str, count); } @@ -2659,14 +2669,18 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Parse any GEORADIUS options we have */ if (opts != NULL) { - get_georadius_opts(Z_ARRVAL_P(opts), &withcoord, &withdist, &withhash, - &count, &sort); + /* Attempt to parse our options array */ + if (get_georadius_opts(Z_ARRVAL_P(opts), &withcoord, &withdist, + &withhash, &count, &sort TSRMLS_CC) != SUCCESS) + { + return FAILURE; + } } /* Calculate the number of arguments we're going to send, five required plus * options. */ argc = 5 + withcoord + withdist + withhash + (sort != SORT_NONE); - if (count != 0) argc += 2; + if (count) argc += 2; /* Begin construction of our command */ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUS"); @@ -2715,12 +2729,17 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s } if (opts != NULL) { - get_georadius_opts(Z_ARRVAL_P(opts), &withcoord, &withdist, &withhash, &count, &sort); + /* Attempt to parse our options array */ + if (get_georadius_opts(Z_ARRVAL_P(opts), &withcoord, &withdist, + &withhash, &count, &sort TSRMLS_CC) != SUCCESS) + { + return FAILURE; + } } /* Calculate argc */ argc = 4 + withcoord + withdist + withhash + (sort != SORT_NONE); - if (count != 0) argc += 2; + if (count) argc += 2; /* Begin command construction*/ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUSBYMEMBER"); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 86292d9146..0386f5d631 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4978,6 +4978,11 @@ public function genericGeoRadiusTest($cmd) { $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); $args = Array('georadius', 'gk', $lng, $lat, 500, 'mi'); + + /* Test a bad COUNT argument */ + foreach (Array(-1, 0, 'notanumber') as $count) { + $this->assertFalse(@$this->redis->georadius('gk', $lng, $lat, 10, 'mi', Array('count' => $count))); + } } else { $this->assertEquals($this->redis->georadiusbymember('gk', $city, 10, 'mi'), Array('Chico')); $this->assertEquals($this->redis->georadiusbymember('gk', $city, 30, 'mi'), Array('Gridley','Chico')); @@ -4985,6 +4990,11 @@ public function genericGeoRadiusTest($cmd) { $this->assertEquals($this->redis->georadiusbymember('gk', $city, 50000, 'm'), Array('Gridley','Chico')); $this->assertEquals($this->redis->georadiusbymember('gk', $city, 150000, 'ft'), Array('Gridley', 'Chico')); $args = Array('georadiusbymember', 'gk', $city, 500, 'mi'); + + /* Test a bad COUNT argument */ + foreach (Array(-1, 0, 'notanumber') as $count) { + $this->assertFalse(@$this->redis->georadiusbymember('gk', $city, 10, 'mi', Array('count' => -1))); + } } /* Options */ From 41bcf355c0bd2e8cd1fbeaf17f17cc55cbe103cf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 22 Oct 2017 19:47:36 -0700 Subject: [PATCH 0868/1986] Fix typo and align table columns --- README.markdown | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/README.markdown b/README.markdown index e9c724da7e..4ba141d5cc 100644 --- a/README.markdown +++ b/README.markdown @@ -3126,15 +3126,15 @@ _**Description**_: Return members of a set with geospacial information that are ##### *Options Array* The georadius command can be called with various options that control how Redis returns results. The following table describes the options phpredis supports. All options are case insensitive. -Key | Value | Description ---- | --- | ---- | -COUNT | `integer $count` | Limit how many results are returned - | WITHCOORD | Return longitude and latitude of matching members - | WITHDIST | Return the distance from the center - | WITHHASH | Return the raw geohash-encoded score - | ASC | Sort results in ascending order - | DESC | Sort results in descending order - +| Key | Value | Description +| :--- | :--- | :---- | +| COUNT | integer > 0 | Limit how many results are returned +| | WITHCOORD | Return longitude and latitude of matching members +| | WITHDIST | Return the distance from the center +| | WITHHASH | Return the raw geohash-encoded score +| | ASC | Sort results in ascending order +| | DESC | Sort results in descending order + *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will win! *Note*: PhpRedis does not currently support the `STORE` or `STOREDIST` options but will be added to future versions. @@ -3217,10 +3217,10 @@ array(1) { ##### *Prototype* ~~~ -$redis->geoRadiusByMember($key, $member, $radius, $units [, array $options]); +$redis->geoRadiusByMember($key, $member, $radius, $units [, Array $options]); ~~~ -_**Description**_: This method is identical to `geoRadius` except that instead of passing a longitude and latitude as the "source" you pass an existing member in the geospacial set. +_**Description**_: This method is identical to [geoRadius](#georadius) except that instead of passing a longitude and latitude as the "source" you pass an existing member in the geospacial set. ##### *Options Array* See [geoRadius](#georadius) command for options array. @@ -3232,19 +3232,28 @@ See [geoRadius](#georadius) command for options array. ~~~ $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); -echo "Within 300,000 miles of Honolulu:\n"; +echo "Within 300 miles of Honolulu:\n"; var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi')); + +echo "\nFirst match within 300 miles of Honolulu:\n"; +var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi', Array('count' => 1))); ~~~ ##### *Output* ~~~ -Within 300,000 meters of Honolulu: +Within 300 miles of Honolulu: array(2) { [0]=> string(8) "Honolulu" [1]=> string(4) "Maui" } + +First match within 300 miles of Honolulu: +array(1) { + [0]=> + string(8) "Honolulu" +} ~~~ From dff7e15ed6d69e09bd43ef27522f5348d2412eaa Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 22 Oct 2017 23:27:57 -0700 Subject: [PATCH 0869/1986] Actually use each potential invalid count in test --- tests/RedisTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 0386f5d631..1a1044893c 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4993,7 +4993,7 @@ public function genericGeoRadiusTest($cmd) { /* Test a bad COUNT argument */ foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->georadiusbymember('gk', $city, 10, 'mi', Array('count' => -1))); + $this->assertFalse(@$this->redis->georadiusbymember('gk', $city, 10, 'mi', Array('count' => $count))); } } From 68c58513183eaf71dd984870e7b180265323c2bc Mon Sep 17 00:00:00 2001 From: git-hulk Date: Tue, 24 Oct 2017 13:19:08 +0800 Subject: [PATCH 0870/1986] MOD: Add tcp_keepalive option to redis sock --- common.h | 4 +++- library.c | 5 ++++- redis.c | 1 + redis_commands.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index bdb99b097f..708b1e1eb9 100644 --- a/common.h +++ b/common.h @@ -478,7 +478,8 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_READ_TIMEOUT 3 #define REDIS_OPT_SCAN 4 #define REDIS_OPT_FAILOVER 5 -#define REDIS_OPT_COMPRESSION 6 +#define REDIS_OPT_TCP_KEEPALIVE 6 +#define REDIS_OPT_COMPRESSION 7 /* cluster options */ #define REDIS_FAILOVER_NONE 0 @@ -674,6 +675,7 @@ typedef struct { int scan; int readonly; + int tcp_keepalive; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index 959a7aded4..b5940196c2 100644 --- a/library.c +++ b/library.c @@ -1402,6 +1402,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->scan = REDIS_SCAN_NORETRY; redis_sock->readonly = 0; + redis_sock->tcp_keepalive = 0; return redis_sock; } @@ -1470,11 +1471,13 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) return -1; } - /* Attempt to set TCP_NODELAY if we're not using a unix socket. */ + /* Attempt to set TCP_NODELAY/TCP_KEEPALIVE if we're not using a unix socket. */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; if (!usocket) { err = setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); PHPREDIS_NOTUSED(err); + err = setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (const void *) &redis_sock->tcp_keepalive, sizeof(int)); + PHPREDIS_NOTUSED(err); } php_stream_auto_cleanup(redis_sock->stream); diff --git a/redis.c b/redis.c index ff83159fd2..0b6e490f35 100644 --- a/redis.c +++ b/redis.c @@ -676,6 +676,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_TCP_KEEPALIVE"), REDIS_OPT_TCP_KEEPALIVE TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION TSRMLS_CC); /* serializer */ diff --git a/redis_commands.c b/redis_commands.c index 4e88b2f051..434eb215df 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -23,6 +23,10 @@ #endif #include "redis_commands.h" + +#include "php_network.h" +#include /* TCP_KEEPALIVE */ + #include /* Local passthrough macro for command construction. Given that these methods @@ -3019,6 +3023,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_NULL(); case REDIS_OPT_READ_TIMEOUT: RETURN_DOUBLE(redis_sock->read_timeout); + case REDIS_OPT_TCP_KEEPALIVE: + RETURN_LONG(redis_sock->tcp_keepalive); case REDIS_OPT_SCAN: RETURN_LONG(redis_sock->scan); case REDIS_OPT_FAILOVER: @@ -3036,6 +3042,8 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, char *val_str; struct timeval read_tv; strlen_t val_len; + int tcp_keepalive = 0; + php_netstream_data_t *sock; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &option, &val_str, &val_len) == FAILURE) @@ -3086,6 +3094,26 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, &read_tv); } RETURN_TRUE; + case REDIS_OPT_TCP_KEEPALIVE: + + /* Don't set TCP_KEEPALIVE if we're using a unix socket. */ + if (ZSTR_VAL(redis_sock->host)[0] == '/' && redis_sock->port < 1) { + RETURN_FALSE; + } + tcp_keepalive = atol(val_str) > 0 ? 1 : 0; + if (redis_sock->tcp_keepalive == tcp_keepalive) { + RETURN_TRUE; + } + if(redis_sock->stream) { + /* set TCP_KEEPALIVE */ + sock = (php_netstream_data_t*)redis_sock->stream->abstract; + if (setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (const void*) &tcp_keepalive, + sizeof(int)) == -1) { + RETURN_FALSE; + } + redis_sock->tcp_keepalive = tcp_keepalive; + } + RETURN_TRUE; case REDIS_OPT_SCAN: val_long = atol(val_str); if(val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) { From 5101172ac77966a36238629455e4bd677d8c759a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 24 Oct 2017 13:54:45 -0700 Subject: [PATCH 0871/1986] Minor change to setsockopt --- library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index b5940196c2..3d18c7f920 100644 --- a/library.c +++ b/library.c @@ -1474,9 +1474,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) /* Attempt to set TCP_NODELAY/TCP_KEEPALIVE if we're not using a unix socket. */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; if (!usocket) { - err = setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_flag, sizeof(int)); + err = setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char*) &tcp_flag, sizeof(tcp_flag)); PHPREDIS_NOTUSED(err); - err = setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (const void *) &redis_sock->tcp_keepalive, sizeof(int)); + err = setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char*) &redis_sock->tcp_keepalive, sizeof(redis_sock->tcp_keepalive)); PHPREDIS_NOTUSED(err); } From 010336d5a741a74fc43d03df6b3916ba35979db6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 24 Oct 2017 14:57:03 -0700 Subject: [PATCH 0872/1986] Tweak call to setsockopt in redis_setoption_handler --- redis_commands.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 5e25a3feb8..3635d4aea2 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3126,8 +3126,8 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, if(redis_sock->stream) { /* set TCP_KEEPALIVE */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; - if (setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (const void*) &tcp_keepalive, - sizeof(int)) == -1) { + if (setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&tcp_keepalive, + sizeof(tcp_keepalive)) == -1) { RETURN_FALSE; } redis_sock->tcp_keepalive = tcp_keepalive; From 51e48729dfb1371955fbfc032d8bd74cbe37f294 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 26 Oct 2017 09:27:46 -0700 Subject: [PATCH 0873/1986] Shot in the dark fix for TCP_KEEPALIVE in Windows --- library.c | 15 ++++++++++----- redis_commands.c | 7 ++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/library.c b/library.c index 3d18c7f920..5d53319b0b 100644 --- a/library.c +++ b/library.c @@ -1,19 +1,19 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif + #include "common.h" #include "php_network.h" #include -#ifndef _MSC_VER -#include /* TCP_NODELAY */ -#include -#endif + #ifdef HAVE_REDIS_IGBINARY #include "igbinary/igbinary.h" #endif + #ifdef HAVE_REDIS_LZF #include #endif + #include #include "php_redis.h" #include "library.h" @@ -29,7 +29,12 @@ #define SCORE_DECODE_INT 1 #define SCORE_DECODE_DOUBLE 2 -#ifdef PHP_WIN32 +#ifndef PHP_WIN32 + #include /* TCP_NODELAY */ + #include /* SO_KEEPALIVE */ +#else + #include + # if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 4 /* This proto is available from 5.5 on only */ PHP_REDIS_API int usleep(unsigned int useconds); diff --git a/redis_commands.c b/redis_commands.c index 3635d4aea2..ac0ed30dda 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -25,7 +25,12 @@ #include "redis_commands.h" #include "php_network.h" -#include /* TCP_KEEPALIVE */ + +#ifndef PHP_WIN32 +#include /* TCP_KEEPALIVE */ +#else +#include +#endif #include From 17cad8b34e9e9ac057ad62694a15d5099f9a5490 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 26 Oct 2017 11:32:59 -0700 Subject: [PATCH 0874/1986] Remove unused variable --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 3d18c7f920..ab1975f789 100644 --- a/library.c +++ b/library.c @@ -1818,7 +1818,7 @@ PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC) { char *data; - int i, ret; + int i; uint32_t res; switch (redis_sock->compression) { From abb862d2f50f7f3059d68ef4028685566f865aa7 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Mon, 30 Oct 2017 11:28:33 +0100 Subject: [PATCH 0875/1986] PHPREDIS-37: Add locking functionality --- .gitignore | 1 + common.h | 1 + php_redis.h | 10 +- redis.c | 21 +++- redis_session.c | 197 +++++++++++++++++++++++++++-- redis_session.h | 15 ++- tests/RedisClusterTest.php | 20 +++ tests/RedisTest.php | 252 +++++++++++++++++++++++++++++++++++-- tests/getSessionData.php | 17 +++ tests/startSession.php | 36 ++++++ 10 files changed, 548 insertions(+), 22 deletions(-) create mode 100644 tests/getSessionData.php create mode 100644 tests/startSession.php diff --git a/.gitignore b/.gitignore index 046241a943..0462ff3707 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ missing autom4te.cache mkinstalldirs run-tests.php +idea/* \ No newline at end of file diff --git a/common.h b/common.h index 708b1e1eb9..f2c1c247a9 100644 --- a/common.h +++ b/common.h @@ -15,6 +15,7 @@ typedef smart_str smart_string; #define smart_string_appendc(dest, c) smart_str_appendc(dest, c) #define smart_string_append_long(dest, val) smart_str_append_long(dest, val) #define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) +#define smart_string_free(str) smart_str_free(str) typedef struct { short gc; diff --git a/php_redis.h b/php_redis.h index 895ad710f7..930346ad52 100644 --- a/php_redis.h +++ b/php_redis.h @@ -260,9 +260,17 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems); -#ifndef _MSC_VER ZEND_BEGIN_MODULE_GLOBALS(redis) + int lock_release_lua_script_uploaded; + char lock_release_lua_script_hash[41]; ZEND_END_MODULE_GLOBALS(redis) + +ZEND_EXTERN_MODULE_GLOBALS(redis); + +#ifdef ZTS +#define REDIS_G(v) TSRMG(redis_globals_id, zend_redis_globals *, v) +#else +#define REDIS_G(v) (redis_globals.v) #endif extern zend_module_entry redis_module_entry; diff --git a/redis.c b/redis.c index df18decddf..6914e12db9 100644 --- a/redis.c +++ b/redis.c @@ -73,6 +73,12 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.timeout", "", PHP_INI_ALL, NULL) + + /* redis session */ + PHP_INI_ENTRY("redis.session.locking_enabled", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_wait_time", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_retries", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_expire", "", PHP_INI_ALL, NULL) PHP_INI_END() /** {{{ Argument info for commands in redis 1.0 */ @@ -226,9 +232,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2) ZEND_ARG_INFO(0, i_count) ZEND_END_ARG_INFO() -#ifdef ZTS ZEND_DECLARE_MODULE_GLOBALS(redis) -#endif static zend_function_entry redis_functions[] = { PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) @@ -454,6 +458,13 @@ static const zend_module_dep redis_deps[] = { ZEND_MOD_END }; +static +PHP_GINIT_FUNCTION(redis) +{ + redis_globals->lock_release_lua_script_uploaded = 0; + memset(redis_globals->lock_release_lua_script_hash, 0, 41); +} + zend_module_entry redis_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER_EX, @@ -470,7 +481,11 @@ zend_module_entry redis_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 PHP_REDIS_VERSION, #endif - STANDARD_MODULE_PROPERTIES + PHP_MODULE_GLOBALS(redis), + PHP_GINIT(redis), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX, }; #ifdef COMPILE_DL_REDIS diff --git a/redis_session.c b/redis_session.c index fe753e7bf2..3be978cb13 100644 --- a/redis_session.c +++ b/redis_session.c @@ -70,6 +70,7 @@ typedef struct { int count; redis_pool_member *head; + redis_session_lock_status *lock_status; } redis_pool; @@ -113,6 +114,7 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { efree(rpm); rpm = next; } + efree(pool->lock_status); efree(pool); } @@ -183,6 +185,162 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { return NULL; } +int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +{ + if (lock_status->is_locked || !INI_INT("redis.session.locking_enabled")) return SUCCESS; + + char *cmd, *response; + int response_len, cmd_len, lock_wait_time, max_lock_retries, i_lock_retry, lock_expire; + calculate_lock_secret(lock_status); + + lock_wait_time = INI_INT("redis.session.lock_wait_time"); + if (lock_wait_time == 0) { + lock_wait_time = 2000; + } + + max_lock_retries = INI_INT("redis.session.lock_retries"); + if (max_lock_retries == 0) { + max_lock_retries = 10; + } + + lock_expire = INI_INT("redis.session.lock_expire"); + if (lock_expire == 0) { + lock_expire = INI_INT("max_execution_time"); + } + + // Building the redis lock key + smart_string_appendl(&lock_status->lock_key, lock_status->session_key, strlen(lock_status->session_key)); + smart_string_appendl(&lock_status->lock_key, "_LOCK", strlen("_LOCK")); + smart_string_0(&lock_status->lock_key); + + if (lock_expire > 0) { + cmd_len = REDIS_SPPRINTF(&cmd, "SET", "ssssd", lock_status->lock_key.c, lock_status->lock_key.len, lock_status->lock_secret.c, lock_status->lock_secret.len, "NX", 2, "PX", 2, lock_expire * 1000); + } else { + cmd_len = REDIS_SPPRINTF(&cmd, "SET", "sss", lock_status->lock_key.c, lock_status->lock_key.len, lock_status->lock_secret.c, lock_status->lock_secret.len, "NX", 2); + } + + for (i_lock_retry = 0; !lock_status->is_locked && (max_lock_retries == -1 || i_lock_retry <= max_lock_retries); i_lock_retry++) { + if(!(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) + && ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) != NULL) + && response_len == 3 + && strncmp(response, "+OK", 3) == 0) { + lock_status->is_locked = 1; + } else if (max_lock_retries == -1 || i_lock_retry < max_lock_retries) { + usleep(lock_wait_time); + } + } + + if (response != NULL) { + efree(response); + } + efree(cmd); + + if (lock_status->is_locked) { + return SUCCESS; + } else { + return FAILURE; + } +} + +void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +{ + if (!lock_status->is_locked) return; + // If redis.session.lock_expire is not set => TTL=max_execution_time + // Therefore it is guaranteed that the current process is still holding the lock + if (lock_status->is_locked && INI_INT("redis.session.lock_expire") == 0) return; + + char *cmd, *response; + int response_len, cmd_len; + + cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key.c, lock_status->lock_key.len); + + redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC); + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + + if (response != NULL) { + lock_status->is_locked = (strcmp(response, lock_status->lock_secret.c) == 0); + efree(response); + } else { + lock_status->is_locked = 0; + } + efree(cmd); +} + +int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +{ + if (!INI_INT("redis.session.locking_enabled")) return 1; + + refresh_lock_status(redis_sock, lock_status TSRMLS_CC); + return lock_status->is_locked; +} + +void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +{ + if (lock_status->is_locked) { + char *cmd, *response; + int response_len, cmd_len; + + upload_lock_release_script(redis_sock TSRMLS_CC); + cmd_len = REDIS_SPPRINTF(&cmd, "EVALSHA", "sdss", REDIS_G(lock_release_lua_script_hash), strlen(REDIS_G(lock_release_lua_script_hash)), 1, lock_status->lock_key.c, lock_status->lock_key.len, lock_status->lock_secret.c, lock_status->lock_secret.len); + + redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC); + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + + // in case of redis script cache has been flushed + if (response == NULL) { + REDIS_G(lock_release_lua_script_uploaded) = 0; + upload_lock_release_script(redis_sock TSRMLS_CC); + redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC); + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + lock_status->is_locked = 0; + } + + if (response != NULL) { + efree(response); + } + + efree(cmd); + } + smart_string_free(&lock_status->lock_key); + smart_string_free(&lock_status->lock_secret); +} + +void upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) +{ + if (REDIS_G(lock_release_lua_script_uploaded)) return; + + char *cmd, *response, *release_script; + int response_len, cmd_len; + release_script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"; + + cmd_len = REDIS_SPPRINTF(&cmd, "SCRIPT", "ss", "LOAD", strlen("LOAD"), release_script, strlen(release_script)); + + redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC); + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + + if (response != NULL) { + memset(REDIS_G(lock_release_lua_script_hash), 0, 41); + strncpy(REDIS_G(lock_release_lua_script_hash), response, strlen(response)); + + REDIS_G(lock_release_lua_script_uploaded) = 1; + efree(response); + } + + efree(cmd); +} + +void calculate_lock_secret(redis_session_lock_status *lock_status) +{ + char hostname[64] = {0}; + gethostname(hostname, 64); + + // Concatenating the redis lock secret + smart_string_appendl(&lock_status->lock_secret, hostname, strlen(hostname)); + smart_string_appendc(&lock_status->lock_secret, '|'); + smart_string_append_long(&lock_status->lock_secret, getpid()); + smart_string_0(&lock_status->lock_secret); +} + /* {{{ PS_OPEN_FUNC */ PS_OPEN_FUNC(redis) @@ -192,6 +350,9 @@ PS_OPEN_FUNC(redis) int i, j, path_len; redis_pool *pool = redis_pool_new(TSRMLS_C); + redis_session_lock_status *lock_status = ecalloc(1, sizeof(redis_session_lock_status)); + lock_status->is_locked = 0; + pool->lock_status = lock_status; for (i=0,j=0,path_len=strlen(save_path); ilock_status->session_key TSRMLS_CC); + + RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; + if (redis_sock) { + lock_release(redis_sock, pool->lock_status TSRMLS_CC); + } + redis_pool_free(pool TSRMLS_CC); PS_SET_MOD_DATA(NULL); } + return SUCCESS; } /* }}} */ @@ -361,9 +530,14 @@ PS_READ_FUNC(redis) #else resp = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &resp_len); #endif - cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); + pool->lock_status->session_key = (char *) emalloc(resp_len + 1); + memset(pool->lock_status->session_key, 0, resp_len + 1); + strncpy(pool->lock_status->session_key, resp, resp_len); + cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); efree(resp); + + lock_acquire(redis_sock, pool->lock_status TSRMLS_CC); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; @@ -427,7 +601,8 @@ PS_WRITE_FUNC(redis) ZSTR_VAL(val), ZSTR_LEN(val)); #endif efree(session); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + + if(!write_allowed(redis_sock, pool->lock_status TSRMLS_CC) || redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; } @@ -466,6 +641,11 @@ PS_DESTROY_FUNC(redis) return FAILURE; } + /* Release lock */ + if (redis_sock) { + lock_release(redis_sock, pool->lock_status TSRMLS_CC); + } + /* send DEL command */ #if (PHP_MAJOR_VERSION < 7) session = redis_session_key(rpm, key, strlen(key), &session_len); @@ -521,7 +701,7 @@ static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_le } /* Simple helper to retreive a boolean (0 or 1) value from a string stored in our - * session.save_path variable. This is so the user can use 0, 1, or 'true', + * session.save_path variable. This is so the user can use 0, 1, or 'true', * 'false' */ static void session_conf_bool(HashTable *ht_conf, char *key, int keylen, int *retval) { @@ -529,7 +709,7 @@ static void session_conf_bool(HashTable *ht_conf, char *key, int keylen, char *str; int strlen; - /* See if we have the option, and it's a string */ + /* See if we have the option, and it's a string */ if ((z_val = zend_hash_str_find(ht_conf, key, keylen - 1)) != NULL && Z_TYPE_P(z_val) == IS_STRING ) { @@ -544,7 +724,7 @@ static void session_conf_bool(HashTable *ht_conf, char *key, int keylen, } /* Prefix a session key */ -static char *cluster_session_key(redisCluster *c, const char *key, int keylen, +static char *cluster_session_key(redisCluster *c, const char *key, int keylen, int *skeylen, short *slot) { char *skey; @@ -552,7 +732,6 @@ static char *cluster_session_key(redisCluster *c, const char *key, int keylen, skey = emalloc(*skeylen); memcpy(skey, ZSTR_VAL(c->flags->prefix), ZSTR_LEN(c->flags->prefix)); memcpy(skey + ZSTR_LEN(c->flags->prefix), key, keylen); - *slot = cluster_hash_key(skey, *skeylen); return skey; @@ -590,7 +769,7 @@ PS_OPEN_FUNC(rediscluster) { /* Grab persistent option */ session_conf_bool(ht_conf, "persistent", sizeof("persistent"), &persistent); - + /* Sanity check on our timeouts */ if (timeout < 0 || read_timeout < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -635,7 +814,7 @@ PS_OPEN_FUNC(rediscluster) { /* Cleanup */ zval_dtor(&z_conf); - + return retval; } @@ -775,7 +954,7 @@ PS_DESTROY_FUNC(rediscluster) { reply = cluster_read_resp(c TSRMLS_CC); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); - return FAILURE; + return FAILURE; } /* Clean up our reply */ diff --git a/redis_session.h b/redis_session.h index 11f861c240..42ad1e2041 100644 --- a/redis_session.h +++ b/redis_session.h @@ -3,6 +3,20 @@ #ifdef PHP_SESSION #include "ext/session/php_session.h" +typedef struct { + zend_bool is_locked; + char *session_key; + smart_string lock_key; + smart_string lock_secret; +} redis_session_lock_status; + +int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); +void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); +void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); +int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); +void upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC); +void calculate_lock_secret(redis_session_lock_status *lock_status); + PS_OPEN_FUNC(redis); PS_CLOSE_FUNC(redis); PS_READ_FUNC(redis); @@ -19,4 +33,3 @@ PS_GC_FUNC(rediscluster); #endif #endif - diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index d03d3591dd..12e40aa005 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -23,6 +23,11 @@ class Redis_Cluster_Test extends Redis_Test { RedisCluster::FAILOVER_DISTRIBUTE ); + /** + * @var string + */ + protected $sessionPrefix = 'PHPREDIS_CLUSTER_SESSION:'; + /* Tests we'll skip all together in the context of RedisCluster. The * RedisCluster class doesn't implement specialized (non-redis) commands * such as sortAsc, or sortDesc and other commands such as SELECT are @@ -35,6 +40,21 @@ public function testReconnectSelect() { return $this->markTestSkipped(); } public function testMultipleConnect() { return $this->markTestSkipped(); } public function testDoublePipeNoOp() { return $this->markTestSkipped(); } + /* Session locking feature is currently not supported in in context of Redis Cluster. + The biggest issue for this is the distribution nature of Redis cluster */ + public function testSession_savedToRedis() { return $this->markTestSkipped(); } + public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); } + public function testSession_lockingDisabledByDefault() { return $this->markTestSkipped(); } + public function testSession_lockReleasedOnClose() { return $this->markTestSkipped(); } + public function testSession_ttlMaxExecutionTime() { return $this->markTestSkipped(); } + public function testSession_ttlLockExpire() { return $this->markTestSkipped(); } + public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { return $this->markTestSkipped(); } + public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { return $this->markTestSkipped(); } + public function testSession_correctLockRetryCount() { return $this->markTestSkipped(); } + public function testSession_defaultLockRetryCount() { return $this->markTestSkipped(); } + public function testSession_noUnlockOfOtherProcess() { return $this->markTestSkipped(); } + public function testSession_lockWaitTime() { return $this->markTestSkipped(); } + /* Load our seeds on construction */ public function __construct() { $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 1a1044893c..a675092c1e 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -21,6 +21,11 @@ class Redis_Test extends TestSuite */ public $redis; + /** + * @var string + */ + protected $sessionPrefix = 'PHPREDIS_SESSION:'; + public function setUp() { $this->redis = $this->newInstance(); $info = $this->redis->info(); @@ -5102,15 +5107,175 @@ public function testRawCommand() { $this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D')); } - public function testSession() + public function testSession_savedToRedis() { - ini_set('session.save_handler', 'redis'); - ini_set('session.save_path', 'tcp://localhost:6379'); - if (!@session_start()) { - return $this->markTestSkipped(); - } - session_write_close(); - $this->assertTrue($this->redis->exists('PHPREDIS_SESSION:' . session_id())); + $this->setSessionHandler(); + + $sessionId = $this->generateSessionId(); + $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); + + $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId)); + $this->assertTrue($sessionSuccessful); + } + + public function testSession_lockKeyCorrect() + { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 5, true); + usleep(100000); + + $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); + } + + public function testSession_lockingDisabledByDefault() + { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 5, true, 300, false); + usleep(100000); + + $start = microtime(true); + $sessionSuccessful = $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, false); + $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($sessionId, 1, true); + usleep(1100000); + + $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); + } + + public function testSession_ttlMaxExecutionTime() + { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 10, true, 2); + usleep(100000); + + $start = microtime(true); + $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); + $end = microtime(true); + $elapsedTime = $end - $start; + + $this->assertTrue($elapsedTime < 3); + $this->assertTrue($sessionSuccessful); + } + + public function testSession_ttlLockExpire() + { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 10, true, 300, true, null, -1, 2); + usleep(100000); + + $start = microtime(true); + $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); + $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($sessionId, 2, true, 300, true, null, -1, 1, 'firstProcess'); + usleep(1500000); // 1.5 sec + $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 10, '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($sessionId, 2, false, 300, true, null, -1, 1, 'firstProcess'); + + $this->assertFalse($writeSuccessful); + $this->assertTrue('firstProcess' !== $this->getSessionData($sessionId)); + } + + public function testSession_correctLockRetryCount() + { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 10, true); + usleep(100000); + + $start = microtime(true); + $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 1000000, 3); + $end = microtime(true); + $elapsedTime = $end - $start; + + $this->assertTrue($elapsedTime > 3 && $elapsedTime < 4); + $this->assertFalse($sessionSuccessful); + } + + public function testSession_defaultLockRetryCount() + { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 10, true); + usleep(100000); + + $start = microtime(true); + $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 200000, 0); + $end = microtime(true); + $elapsedTime = $end - $start; + + $this->assertTrue($elapsedTime > 2 && $elapsedTime < 3); + $this->assertFalse($sessionSuccessful); + } + + public function testSession_noUnlockOfOtherProcess() + { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 3, true, 1); // Process 1 + usleep(100000); + $this->startSessionProcess($sessionId, 5, true); // Process 2 + + $start = microtime(true); + // Waiting until TTL of process 1 ended and process 2 locked the session, + // because is not guaranteed which waiting process gets the next lock + sleep(1); + $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); + $end = microtime(true); + $elapsedTime = $end - $start; + + $this->assertTrue($elapsedTime > 5); + $this->assertTrue($sessionSuccessful); + } + + public function testSession_lockWaitTime() + { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 1, true, 300); + usleep(100000); + + $start = microtime(true); + $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, 3000000); + $end = microtime(true); + $elapsedTime = $end - $start; + + $this->assertTrue($elapsedTime > 2.5 && $elapsedTime < 3.5); + $this->assertTrue($sessionSuccessful); } public function testMultipleConnect() { @@ -5122,5 +5287,76 @@ public function testMultipleConnect() { $this->assertEquals($this->redis->ping(), "+PONG"); } } + + 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 { + $encoded = bin2hex(openssl_random_pseudo_bytes(8)); + return $encoded; + } + } + + /** + * @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 + * + * @return bool + */ + private function startSessionProcess($sessionId, $sleepTime, $background, $maxExecutionTime = 300, $locking_enabled = true, $lock_wait_time = null, $lock_retries = -1, $lock_expires = 0, $sessionData = '') + { + if (substr(php_uname(), 0, 7) == "Windows"){ + $this->markTestSkipped(); + return true; + } else { + $commandParameters = array($this->getHost(), $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData); + if ($locking_enabled) { + $commandParameters[] = '1'; + + if ($lock_wait_time != null) { + $commandParameters[] = $lock_wait_time; + } + } + $commandParameters = array_map('escapeshellarg', $commandParameters); + + $command = 'php ' . __DIR__ . '/startSession.php ' . implode(' ', $commandParameters); + $command .= $background ? ' 2>/dev/null > /dev/null &' : ' 2>&1'; + + exec($command, $output); + return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')) ? true : false; + } + } + + /** + * @param string $sessionId + * + * @return string + */ + private function getSessionData($sessionId) + { + $command = 'php ' . __DIR__ . '/getSessionData.php ' . escapeshellarg($this->getHost()) . ' ' . escapeshellarg($sessionId); + exec($command, $output); + + return $output[0]; + } } ?> diff --git a/tests/getSessionData.php b/tests/getSessionData.php new file mode 100644 index 0000000000..5f993ebcb8 --- /dev/null +++ b/tests/getSessionData.php @@ -0,0 +1,17 @@ + Date: Thu, 2 Nov 2017 11:13:03 +0100 Subject: [PATCH 0876/1986] PHPREDIS-37: Sorted php.ini parameters alphabetically --- redis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis.c b/redis.c index 6914e12db9..88c49c08f4 100644 --- a/redis.c +++ b/redis.c @@ -76,9 +76,9 @@ PHP_INI_BEGIN() /* redis session */ PHP_INI_ENTRY("redis.session.locking_enabled", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_expire", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_retries", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_wait_time", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.session.lock_retries", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.session.lock_expire", "", PHP_INI_ALL, NULL) PHP_INI_END() /** {{{ Argument info for commands in redis 1.0 */ From 64b66c0e59dec26b4e9b8242bdb0e016cb698366 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 11:14:53 +0100 Subject: [PATCH 0877/1986] PHPREDIS-37: Handle return value of redis_socket_write in refresh_lock_status --- redis_session.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index 3be978cb13..2b23e4e0a2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -254,8 +254,11 @@ void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_ cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key.c, lock_status->lock_key.len); - redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC); - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + } else { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to refresh sessiong locking status (socket write failed)"); + } if (response != NULL) { lock_status->is_locked = (strcmp(response, lock_status->lock_secret.c) == 0); From 3c2d3fa7bb6587d88baec8d28d00eca524933608 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 11:55:38 +0100 Subject: [PATCH 0878/1986] PHPREDIS-37: Handle return value of redis_socket_write in lock_release and upload_lock_release_script --- redis_session.c | 33 +++++++++++++++++++++++++-------- redis_session.h | 2 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/redis_session.c b/redis_session.c index 2b23e4e0a2..db584d257c 100644 --- a/redis_session.c +++ b/redis_session.c @@ -286,15 +286,22 @@ void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status upload_lock_release_script(redis_sock TSRMLS_CC); cmd_len = REDIS_SPPRINTF(&cmd, "EVALSHA", "sdss", REDIS_G(lock_release_lua_script_hash), strlen(REDIS_G(lock_release_lua_script_hash)), 1, lock_status->lock_key.c, lock_status->lock_key.len, lock_status->lock_secret.c, lock_status->lock_secret.len); - redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC); - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + } else { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to release session lock (socket write failed)"); + } // in case of redis script cache has been flushed if (response == NULL) { REDIS_G(lock_release_lua_script_uploaded) = 0; - upload_lock_release_script(redis_sock TSRMLS_CC); - redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC); - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + int upload_successful = upload_lock_release_script(redis_sock TSRMLS_CC); + + if (upload_successful && redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + } else { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to release session lock (socket write failed)"); + } lock_status->is_locked = 0; } @@ -308,28 +315,38 @@ void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status smart_string_free(&lock_status->lock_secret); } -void upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) +int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) { if (REDIS_G(lock_release_lua_script_uploaded)) return; char *cmd, *response, *release_script; int response_len, cmd_len; + int upload_result = 0; release_script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"; cmd_len = REDIS_SPPRINTF(&cmd, "SCRIPT", "ss", "LOAD", strlen("LOAD"), release_script, strlen(release_script)); - redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC); - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { + response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + + if (response == NULL) { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to upload LUA script for releasing session lock (SCRIPT LOAD failed)"); + } + } else { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to upload LUA script for releasing session lock (socket write failed)"); + } if (response != NULL) { memset(REDIS_G(lock_release_lua_script_hash), 0, 41); strncpy(REDIS_G(lock_release_lua_script_hash), response, strlen(response)); REDIS_G(lock_release_lua_script_uploaded) = 1; + upload_result = 1; efree(response); } efree(cmd); + return upload_result; } void calculate_lock_secret(redis_session_lock_status *lock_status) diff --git a/redis_session.h b/redis_session.h index 42ad1e2041..6b61ef8acf 100644 --- a/redis_session.h +++ b/redis_session.h @@ -14,7 +14,7 @@ int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status T void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); -void upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC); +int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC); void calculate_lock_secret(redis_session_lock_status *lock_status); PS_OPEN_FUNC(redis); From c8ca74d4137fd0e1b0fdd20ac1865660324950e4 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 12:41:54 +0100 Subject: [PATCH 0879/1986] PHPREDIS-37: Converted global lock_release_lua_script_hash to pointer and use estrdup for filling --- php_redis.h | 2 +- redis.c | 3 ++- redis_session.c | 6 ++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/php_redis.h b/php_redis.h index 930346ad52..9d47e058bc 100644 --- a/php_redis.h +++ b/php_redis.h @@ -262,7 +262,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( ZEND_BEGIN_MODULE_GLOBALS(redis) int lock_release_lua_script_uploaded; - char lock_release_lua_script_hash[41]; + char *lock_release_lua_script_hash; ZEND_END_MODULE_GLOBALS(redis) ZEND_EXTERN_MODULE_GLOBALS(redis); diff --git a/redis.c b/redis.c index 88c49c08f4..6a49c15419 100644 --- a/redis.c +++ b/redis.c @@ -462,7 +462,7 @@ static PHP_GINIT_FUNCTION(redis) { redis_globals->lock_release_lua_script_uploaded = 0; - memset(redis_globals->lock_release_lua_script_hash, 0, 41); + redis_globals->lock_release_lua_script_hash = NULL; } zend_module_entry redis_module_entry = { @@ -816,6 +816,7 @@ PHP_MINIT_FUNCTION(redis) */ PHP_MSHUTDOWN_FUNCTION(redis) { + efree(REDIS_G(lock_release_lua_script_hash)); return SUCCESS; } diff --git a/redis_session.c b/redis_session.c index db584d257c..7d9e566f06 100644 --- a/redis_session.c +++ b/redis_session.c @@ -317,7 +317,7 @@ void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) { - if (REDIS_G(lock_release_lua_script_uploaded)) return; + if (REDIS_G(lock_release_lua_script_uploaded)) return 1; char *cmd, *response, *release_script; int response_len, cmd_len; @@ -337,9 +337,7 @@ int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) } if (response != NULL) { - memset(REDIS_G(lock_release_lua_script_hash), 0, 41); - strncpy(REDIS_G(lock_release_lua_script_hash), response, strlen(response)); - + REDIS_G(lock_release_lua_script_hash) = estrdup(response); REDIS_G(lock_release_lua_script_uploaded) = 1; upload_result = 1; efree(response); From bc26ccaca945f17629a7a4895947ca2ae9c59aab Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 12:43:26 +0100 Subject: [PATCH 0880/1986] PHPREDIS-37: Replaced fixed hostname length by HOST_NAME_MAX --- redis_session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index 7d9e566f06..92bb392206 100644 --- a/redis_session.c +++ b/redis_session.c @@ -349,8 +349,8 @@ int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) void calculate_lock_secret(redis_session_lock_status *lock_status) { - char hostname[64] = {0}; - gethostname(hostname, 64); + char hostname[HOST_NAME_MAX] = {0}; + gethostname(hostname, HOST_NAME_MAX); // Concatenating the redis lock secret smart_string_appendl(&lock_status->lock_secret, hostname, strlen(hostname)); From 99a81d38ec9513138eccacb1d68702c6dca0a5c6 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 13:00:26 +0100 Subject: [PATCH 0881/1986] PHPREDIS-37: Replaced smart_string operations by spprintf --- redis_session.c | 25 +++++++++---------------- redis_session.h | 4 ++-- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/redis_session.c b/redis_session.c index 92bb392206..88ea6dcd3c 100644 --- a/redis_session.c +++ b/redis_session.c @@ -208,15 +208,12 @@ int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status T lock_expire = INI_INT("max_execution_time"); } - // Building the redis lock key - smart_string_appendl(&lock_status->lock_key, lock_status->session_key, strlen(lock_status->session_key)); - smart_string_appendl(&lock_status->lock_key, "_LOCK", strlen("_LOCK")); - smart_string_0(&lock_status->lock_key); + spprintf(&lock_status->lock_key, 0, "%s%s", lock_status->session_key, "_LOCK"); if (lock_expire > 0) { - cmd_len = REDIS_SPPRINTF(&cmd, "SET", "ssssd", lock_status->lock_key.c, lock_status->lock_key.len, lock_status->lock_secret.c, lock_status->lock_secret.len, "NX", 2, "PX", 2, lock_expire * 1000); + cmd_len = REDIS_SPPRINTF(&cmd, "SET", "ssssd", lock_status->lock_key, strlen(lock_status->lock_key), lock_status->lock_secret, strlen(lock_status->lock_secret), "NX", 2, "PX", 2, lock_expire * 1000); } else { - cmd_len = REDIS_SPPRINTF(&cmd, "SET", "sss", lock_status->lock_key.c, lock_status->lock_key.len, lock_status->lock_secret.c, lock_status->lock_secret.len, "NX", 2); + cmd_len = REDIS_SPPRINTF(&cmd, "SET", "sss", lock_status->lock_key, strlen(lock_status->lock_key), lock_status->lock_secret, strlen(lock_status->lock_secret), "NX", 2); } for (i_lock_retry = 0; !lock_status->is_locked && (max_lock_retries == -1 || i_lock_retry <= max_lock_retries); i_lock_retry++) { @@ -252,7 +249,7 @@ void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_ char *cmd, *response; int response_len, cmd_len; - cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key.c, lock_status->lock_key.len); + cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key, strlen(lock_status->lock_key)); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); @@ -261,7 +258,7 @@ void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_ } if (response != NULL) { - lock_status->is_locked = (strcmp(response, lock_status->lock_secret.c) == 0); + lock_status->is_locked = (strcmp(response, lock_status->lock_secret) == 0); efree(response); } else { lock_status->is_locked = 0; @@ -284,7 +281,7 @@ void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status int response_len, cmd_len; upload_lock_release_script(redis_sock TSRMLS_CC); - cmd_len = REDIS_SPPRINTF(&cmd, "EVALSHA", "sdss", REDIS_G(lock_release_lua_script_hash), strlen(REDIS_G(lock_release_lua_script_hash)), 1, lock_status->lock_key.c, lock_status->lock_key.len, lock_status->lock_secret.c, lock_status->lock_secret.len); + cmd_len = REDIS_SPPRINTF(&cmd, "EVALSHA", "sdss", REDIS_G(lock_release_lua_script_hash), strlen(REDIS_G(lock_release_lua_script_hash)), 1, lock_status->lock_key, strlen(lock_status->lock_key), lock_status->lock_secret, strlen(lock_status->lock_secret)); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); @@ -311,8 +308,8 @@ void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status efree(cmd); } - smart_string_free(&lock_status->lock_key); - smart_string_free(&lock_status->lock_secret); + efree(lock_status->lock_key); + efree(lock_status->lock_secret); } int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) @@ -352,11 +349,7 @@ void calculate_lock_secret(redis_session_lock_status *lock_status) char hostname[HOST_NAME_MAX] = {0}; gethostname(hostname, HOST_NAME_MAX); - // Concatenating the redis lock secret - smart_string_appendl(&lock_status->lock_secret, hostname, strlen(hostname)); - smart_string_appendc(&lock_status->lock_secret, '|'); - smart_string_append_long(&lock_status->lock_secret, getpid()); - smart_string_0(&lock_status->lock_secret); + spprintf(&lock_status->lock_secret, 0, "%s|%c", hostname, getpid()); } /* {{{ PS_OPEN_FUNC diff --git a/redis_session.h b/redis_session.h index 6b61ef8acf..ad346a1434 100644 --- a/redis_session.h +++ b/redis_session.h @@ -6,8 +6,8 @@ typedef struct { zend_bool is_locked; char *session_key; - smart_string lock_key; - smart_string lock_secret; + char *lock_key; + char *lock_secret; } redis_session_lock_status; int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); From 6aa84c0304ee640015658d3769b16a0898479575 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 13:47:37 +0100 Subject: [PATCH 0882/1986] PHPREDIS-37: Using estrndup for session_key --- redis_session.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/redis_session.c b/redis_session.c index 88ea6dcd3c..2fb11f7f3a 100644 --- a/redis_session.c +++ b/redis_session.c @@ -541,9 +541,7 @@ PS_READ_FUNC(redis) #else resp = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &resp_len); #endif - pool->lock_status->session_key = (char *) emalloc(resp_len + 1); - memset(pool->lock_status->session_key, 0, resp_len + 1); - strncpy(pool->lock_status->session_key, resp, resp_len); + pool->lock_status->session_key = estrndup(resp, resp_len); cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); efree(resp); From 056cfa3256d2018adbd77cbfd49f30f52fb528f1 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 14:14:31 +0100 Subject: [PATCH 0883/1986] PHPREDIS-37: Notice if lock acquire was not succesful --- redis_session.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index 2fb11f7f3a..8876df2d5c 100644 --- a/redis_session.c +++ b/redis_session.c @@ -546,7 +546,10 @@ PS_READ_FUNC(redis) cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); efree(resp); - lock_acquire(redis_sock, pool->lock_status TSRMLS_CC); + if (!lock_acquire(redis_sock, pool->lock_status TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Acquire of session lock was not successful"); + } + if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; From e2964295fb10e116aba30f4b99b8f54f99485a39 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 14:16:02 +0100 Subject: [PATCH 0884/1986] PHPREDIS-37: Adjusted code style of php_error_docref() usage --- redis_session.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/redis_session.c b/redis_session.c index 8876df2d5c..e0feb11f46 100644 --- a/redis_session.c +++ b/redis_session.c @@ -254,7 +254,8 @@ void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_ if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to refresh sessiong locking status (socket write failed)"); + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Unable to refresh sessiong locking status (socket write failed)"); } if (response != NULL) { @@ -286,7 +287,8 @@ void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to release session lock (socket write failed)"); + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Unable to release session lock (socket write failed)"); } // in case of redis script cache has been flushed @@ -297,7 +299,8 @@ void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status if (upload_successful && redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to release session lock (socket write failed)"); + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Unable to release session lock (socket write failed)"); } lock_status->is_locked = 0; } @@ -327,10 +330,12 @@ int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if (response == NULL) { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to upload LUA script for releasing session lock (SCRIPT LOAD failed)"); + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Unable to upload LUA script for releasing session lock (SCRIPT LOAD failed)"); } } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Unable to upload LUA script for releasing session lock (socket write failed)"); + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Unable to upload LUA script for releasing session lock (socket write failed)"); } if (response != NULL) { @@ -547,7 +552,8 @@ PS_READ_FUNC(redis) efree(resp); if (!lock_acquire(redis_sock, pool->lock_status TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Acquire of session lock was not successful"); + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "Acquire of session lock was not successful"); } if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { From 97e034efb0bd439df473e1a2ec7eba66249be4bc Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 2 Nov 2017 14:24:51 +0100 Subject: [PATCH 0885/1986] PHPREDIS-37: Disabled error_reporting for session tests, to avoid failing tests caused by correct thrown warnings/notices --- tests/getSessionData.php | 2 ++ tests/startSession.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/getSessionData.php b/tests/getSessionData.php index 5f993ebcb8..2554238ef1 100644 --- a/tests/getSessionData.php +++ b/tests/getSessionData.php @@ -1,4 +1,6 @@ Date: Thu, 2 Nov 2017 15:28:28 +0100 Subject: [PATCH 0886/1986] PHPREDIS-37: Adjusted error reporting to show warnings but no notices --- tests/getSessionData.php | 2 +- tests/startSession.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/getSessionData.php b/tests/getSessionData.php index 2554238ef1..b5bea74a42 100644 --- a/tests/getSessionData.php +++ b/tests/getSessionData.php @@ -1,5 +1,5 @@ Date: Thu, 2 Nov 2017 16:36:03 +0100 Subject: [PATCH 0887/1986] PHPREDIS-37: Removed obsolete smart_string free wrapper definition --- common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/common.h b/common.h index f2c1c247a9..708b1e1eb9 100644 --- a/common.h +++ b/common.h @@ -15,7 +15,6 @@ typedef smart_str smart_string; #define smart_string_appendc(dest, c) smart_str_appendc(dest, c) #define smart_string_append_long(dest, val) smart_str_append_long(dest, val) #define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) -#define smart_string_free(str) smart_str_free(str) typedef struct { short gc; From b63d79958e0f6b5abc6eaa6f720b4bfb17eb2e8f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 10 Nov 2017 13:46:48 -0800 Subject: [PATCH 0888/1986] Refactored session locking logic * Use a precalculated SHA1 of our lock release script and first attempt to EVALSHA the script while falling back to EVAL if that fails. This means that the very first time we attempt to release a lock after server restart or script cache will fail but subsequent attempts will succeed as Redis will cache the script automatically. * Reorganized the new locking prototypes by making them static and removing them from the header file as they never need to be called from outside of redis_session.c anyway. Did the same for the lock_secret structure as we don't need to expose the structure externally. * Consolidated allocation and deallocation of lock pointers such that our redis_pool "constructor" and "desctructor" handle that as well. * Updating how we release the lock means we no longer need the new REDIS_G module globals, so those were removed. * HOST_NAME_MAX doesn't exist on OSX so added some preprocessor logic around that Still a WIP as it needs more testing as I'm sure I broke *something* :-) --- php_redis.h | 13 -- redis.c | 16 +-- redis_session.c | 318 ++++++++++++++++++++++++++++-------------------- redis_session.h | 14 --- 4 files changed, 188 insertions(+), 173 deletions(-) diff --git a/php_redis.h b/php_redis.h index 9d47e058bc..d327246510 100644 --- a/php_redis.h +++ b/php_redis.h @@ -260,19 +260,6 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems); -ZEND_BEGIN_MODULE_GLOBALS(redis) - int lock_release_lua_script_uploaded; - char *lock_release_lua_script_hash; -ZEND_END_MODULE_GLOBALS(redis) - -ZEND_EXTERN_MODULE_GLOBALS(redis); - -#ifdef ZTS -#define REDIS_G(v) TSRMG(redis_globals_id, zend_redis_globals *, v) -#else -#define REDIS_G(v) (redis_globals.v) -#endif - extern zend_module_entry redis_module_entry; #define redis_module_ptr &redis_module_entry diff --git a/redis.c b/redis.c index 6a49c15419..0f1eee68c0 100644 --- a/redis.c +++ b/redis.c @@ -232,8 +232,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2) ZEND_ARG_INFO(0, i_count) ZEND_END_ARG_INFO() -ZEND_DECLARE_MODULE_GLOBALS(redis) - static zend_function_entry redis_functions[] = { PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC) @@ -458,13 +456,6 @@ static const zend_module_dep redis_deps[] = { ZEND_MOD_END }; -static -PHP_GINIT_FUNCTION(redis) -{ - redis_globals->lock_release_lua_script_uploaded = 0; - redis_globals->lock_release_lua_script_hash = NULL; -} - zend_module_entry redis_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER_EX, @@ -481,11 +472,7 @@ zend_module_entry redis_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 PHP_REDIS_VERSION, #endif - PHP_MODULE_GLOBALS(redis), - PHP_GINIT(redis), - NULL, - NULL, - STANDARD_MODULE_PROPERTIES_EX, + STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_REDIS @@ -816,7 +803,6 @@ PHP_MINIT_FUNCTION(redis) */ PHP_MSHUTDOWN_FUNCTION(redis) { - efree(REDIS_G(lock_release_lua_script_hash)); return SUCCESS; } diff --git a/redis_session.c b/redis_session.c index e0feb11f46..bbcd1d988b 100644 --- a/redis_session.c +++ b/redis_session.c @@ -41,6 +41,26 @@ #include "SAPI.h" #include "ext/standard/url.h" +/* HOST_NAME_MAX doesn't exist everywhere */ +#ifndef HOST_NAME_MAX + #if defined(_POSIX_HOST_NAME_MAX) + #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX + #elif defined(MAXHOSTNAMELEN) + #define HOST_NAME_MAX MAXHOSTNAMELEN + #else + #define HOST_NAME_MAX 255 + #endif +#endif + +/* Session lock LUA as well as its SHA1 hash */ +#define LOCK_RELEASE_LUA_STR "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end" +#define LOCK_RELEASE_LUA_LEN (sizeof(LOCK_RELEASE_LUA_STR) - 1) +#define LOCK_RELEASE_SHA_STR "b70c2384248f88e6b75b9f89241a180f856ad852" +#define LOCK_RELEASE_SHA_LEN (sizeof(LOCK_RELEASE_SHA_STR) - 1) + +/* Check if a response is the Redis +OK status response */ +#define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3)) + ps_module ps_mod_redis = { PS_MOD(redis) }; @@ -48,6 +68,13 @@ ps_module ps_mod_redis_cluster = { PS_MOD(rediscluster) }; +typedef struct { + zend_bool is_locked; + char *session_key; + char *lock_key; + char *lock_secret; +} redis_session_lock_status; + typedef struct redis_pool_member_ { RedisSock *redis_sock; @@ -76,7 +103,12 @@ typedef struct { PHP_REDIS_API redis_pool* redis_pool_new(TSRMLS_D) { - return ecalloc(1, sizeof(redis_pool)); + redis_pool *pool; + + pool = ecalloc(1, sizeof(*pool)); + pool->lock_status = ecalloc(1, sizeof(*pool->lock_status)); + + return pool; } PHP_REDIS_API void @@ -114,10 +146,36 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { efree(rpm); rpm = next; } + + /* Cleanup after our lock */ + if (pool->lock_status->session_key) + efree(pool->lock_status->session_key); + if (pool->lock_status->lock_secret) + efree(pool->lock_status->lock_secret); + if (pool->lock_status->lock_key) + efree(pool->lock_status->lock_key); + + /* Cleanup lock status struct and pool itself */ efree(pool->lock_status); efree(pool); } +/* Send a command to Redis. Returns reply on success and NULL on failure */ +static char *redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, + int *replylen TSRMLS_DC) +{ + char *reply; + + if (redis_sock_write(redis_sock, cmd, cmdlen TSRMLS_CC) >= 0) { + if ((reply = redis_sock_read(redis_sock, replylen TSRMLS_CC)) != NULL) { + return reply; + } + } + + /* Failed to send or receive command */ + return NULL; +} + static void redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { RedisSock *redis_sock = rpm->redis_sock; @@ -185,176 +243,177 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { return NULL; } -int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +/* Helper to set our session lock key */ +static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len + TSRMLS_DC) +{ + char *reply; + int reply_len; + + reply = redis_simple_cmd(redis_sock, cmd, cmd_len, &reply_len TSRMLS_CC); + if (reply) { + if (IS_REDIS_OK(reply, reply_len)) { + efree(reply); + return SUCCESS; + } + + efree(reply); + } + + return FAILURE; +} + +static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status + TSRMLS_DC) { - if (lock_status->is_locked || !INI_INT("redis.session.locking_enabled")) return SUCCESS; + char *cmd, hostname[HOST_NAME_MAX] = {0}; + int cmd_len, lock_wait_time, retries, i, expiry; - char *cmd, *response; - int response_len, cmd_len, lock_wait_time, max_lock_retries, i_lock_retry, lock_expire; - calculate_lock_secret(lock_status); + /* Short circuit if we are already locked or not using session locks */ + if (lock_status->is_locked || !INI_INT("redis.session.locking_enabled")) + return SUCCESS; + /* How long to wait between attempts to acquire lock */ lock_wait_time = INI_INT("redis.session.lock_wait_time"); if (lock_wait_time == 0) { - lock_wait_time = 2000; + lock_wait_time = 2000; } - max_lock_retries = INI_INT("redis.session.lock_retries"); - if (max_lock_retries == 0) { - max_lock_retries = 10; + /* Maximum number of times to retry (-1 means infinite) */ + retries = INI_INT("redis.session.lock_retries"); + if (retries == 0) { + retries = 10; } - lock_expire = INI_INT("redis.session.lock_expire"); - if (lock_expire == 0) { - lock_expire = INI_INT("max_execution_time"); + /* How long should the lock live (in seconds) */ + expiry = INI_INT("redis.session.lock_expire"); + if (expiry == 0) { + expiry = INI_INT("max_execution_time"); } + /* Generate our qualified lock key */ spprintf(&lock_status->lock_key, 0, "%s%s", lock_status->session_key, "_LOCK"); - if (lock_expire > 0) { - cmd_len = REDIS_SPPRINTF(&cmd, "SET", "ssssd", lock_status->lock_key, strlen(lock_status->lock_key), lock_status->lock_secret, strlen(lock_status->lock_secret), "NX", 2, "PX", 2, lock_expire * 1000); + /* Calculate lock secret */ + gethostname(hostname, HOST_NAME_MAX); + spprintf(&lock_status->lock_secret, 0, "%s|%ld", hostname, (long)getpid()); + + if (expiry > 0) { + cmd_len = REDIS_SPPRINTF(&cmd, "SET", "ssssd", lock_status->lock_key, + strlen(lock_status->lock_key), lock_status->lock_secret, + strlen(lock_status->lock_secret), "NX", 2, + "PX", 2, expiry * 1000); } else { - cmd_len = REDIS_SPPRINTF(&cmd, "SET", "sss", lock_status->lock_key, strlen(lock_status->lock_key), lock_status->lock_secret, strlen(lock_status->lock_secret), "NX", 2); + cmd_len = REDIS_SPPRINTF(&cmd, "SET", "sss", lock_status->lock_key, + strlen(lock_status->lock_key), lock_status->lock_secret, + strlen(lock_status->lock_secret), "NX", 2); } - for (i_lock_retry = 0; !lock_status->is_locked && (max_lock_retries == -1 || i_lock_retry <= max_lock_retries); i_lock_retry++) { - if(!(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) - && ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) != NULL) - && response_len == 3 - && strncmp(response, "+OK", 3) == 0) { + /* Attempt to get our lock */ + for (i = 0; retries == -1 || i <= retries; i++) { + if (set_session_lock_key(redis_sock, cmd, cmd_len TSRMLS_CC) == SUCCESS) { lock_status->is_locked = 1; - } else if (max_lock_retries == -1 || i_lock_retry < max_lock_retries) { - usleep(lock_wait_time); - } - } + break; + } - if (response != NULL) { - efree(response); + /* Sleep unless we're done making attempts */ + if (retries == -1 || i < retries) { + usleep(lock_wait_time); + } } + + /* Cleanup SET command */ efree(cmd); - if (lock_status->is_locked) { - return SUCCESS; - } else { - return FAILURE; - } + /* Success if we're locked */ + return lock_status->is_locked ? SUCCESS : FAILURE; } -void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +#define IS_LOCK_SECRET(reply, len, secret) (len == strlen(secret) && !strncmp(reply, secret, len)) +static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) { - if (!lock_status->is_locked) return; - // If redis.session.lock_expire is not set => TTL=max_execution_time - // Therefore it is guaranteed that the current process is still holding the lock - if (lock_status->is_locked && INI_INT("redis.session.lock_expire") == 0) return; + char *cmd, *reply = NULL; + int replylen, cmdlen; - char *cmd, *response; - int response_len, cmd_len; + /* Return early if we're not locked */ + if (!lock_status->is_locked) + return; - cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key, strlen(lock_status->lock_key)); + /* If redis.session.lock_expire is not set => TTL=max_execution_time + Therefore it is guaranteed that the current process is still holding + the lock */ + if (lock_status->is_locked && INI_INT("redis.session.lock_expire") == 0) + return; - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); - } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Unable to refresh sessiong locking status (socket write failed)"); + cmdlen = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key, + strlen(lock_status->lock_key)); + + /* Attempt to refresh the lock */ + reply = redis_simple_cmd(redis_sock, cmd, cmdlen, &replylen TSRMLS_CC); + if (reply != NULL) { + lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret); + efree(reply); } - if (response != NULL) { - lock_status->is_locked = (strcmp(response, lock_status->lock_secret) == 0); - efree(response); - } else { - lock_status->is_locked = 0; + /* Issue a warning if we're not locked. We don't attempt to refresh the lock + * if we aren't flagged as locked, so if we're not flagged here something + * failed */ + if (!lock_status->is_locked) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to refresh session lock"); } + + /* Cleanup */ efree(cmd); } -int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) { - if (!INI_INT("redis.session.locking_enabled")) return 1; + if (!INI_INT("redis.session.locking_enabled")) + return 1; refresh_lock_status(redis_sock, lock_status TSRMLS_CC); + return lock_status->is_locked; } -void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +/* Release any session lock we hold and cleanup allocated lock data. This function + * first attempts to use EVALSHA and then falls back to EVAL if EVALSHA fails. This + * will cause Redis to cache the script, so subsequent calls should then succeed + * using EVALSHA. */ +static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) { - if (lock_status->is_locked) { - char *cmd, *response; - int response_len, cmd_len; - - upload_lock_release_script(redis_sock TSRMLS_CC); - cmd_len = REDIS_SPPRINTF(&cmd, "EVALSHA", "sdss", REDIS_G(lock_release_lua_script_hash), strlen(REDIS_G(lock_release_lua_script_hash)), 1, lock_status->lock_key, strlen(lock_status->lock_key), lock_status->lock_secret, strlen(lock_status->lock_secret)); - - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); - } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Unable to release session lock (socket write failed)"); - } - - // in case of redis script cache has been flushed - if (response == NULL) { - REDIS_G(lock_release_lua_script_uploaded) = 0; - int upload_successful = upload_lock_release_script(redis_sock TSRMLS_CC); - - if (upload_successful && redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); - } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Unable to release session lock (socket write failed)"); - } + char *cmd, *reply; + int i, cmdlen, replylen; + + /* Keywords, command, and length fallbacks */ + const char *kwd[] = {"EVALSHA", "EVAL"}; + const char *lua[] = {LOCK_RELEASE_SHA_STR, LOCK_RELEASE_LUA_STR}; + int len[] = {LOCK_RELEASE_SHA_LEN, LOCK_RELEASE_LUA_LEN}; + + /* We first want to try EVALSHA and then fall back to EVAL */ + for (i = 0; lock_status->is_locked && i < sizeof(kwd)/sizeof(*kwd); i++) { + /* Construct our command */ + cmdlen = REDIS_SPPRINTF(&cmd, (char*)kwd[i], "sdss", lua[i], len[i], 1, + lock_status->lock_key, strlen(lock_status->lock_key), + lock_status->lock_secret, strlen(lock_status->lock_secret)); + + /* Send it off */ + reply = redis_simple_cmd(redis_sock, cmd, cmdlen, &replylen TSRMLS_CC); + + /* Release lock and cleanup reply if we got one */ + if (reply != NULL) { lock_status->is_locked = 0; + efree(reply); } - if (response != NULL) { - efree(response); - } - + /* Cleanup command */ efree(cmd); } - efree(lock_status->lock_key); - efree(lock_status->lock_secret); -} - -int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC) -{ - if (REDIS_G(lock_release_lua_script_uploaded)) return 1; - - char *cmd, *response, *release_script; - int response_len, cmd_len; - int upload_result = 0; - release_script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"; - - cmd_len = REDIS_SPPRINTF(&cmd, "SCRIPT", "ss", "LOAD", strlen("LOAD"), release_script, strlen(release_script)); - - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC)) { - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); - - if (response == NULL) { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Unable to upload LUA script for releasing session lock (SCRIPT LOAD failed)"); - } - } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Unable to upload LUA script for releasing session lock (socket write failed)"); - } - if (response != NULL) { - REDIS_G(lock_release_lua_script_hash) = estrdup(response); - REDIS_G(lock_release_lua_script_uploaded) = 1; - upload_result = 1; - efree(response); + /* Something has failed if we are still locked */ + if (lock_status->is_locked) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to release session lock"); } - - efree(cmd); - return upload_result; -} - -void calculate_lock_secret(redis_session_lock_status *lock_status) -{ - char hostname[HOST_NAME_MAX] = {0}; - gethostname(hostname, HOST_NAME_MAX); - - spprintf(&lock_status->lock_secret, 0, "%s|%c", hostname, getpid()); } /* {{{ PS_OPEN_FUNC @@ -366,9 +425,6 @@ PS_OPEN_FUNC(redis) int i, j, path_len; redis_pool *pool = redis_pool_new(TSRMLS_C); - redis_session_lock_status *lock_status = ecalloc(1, sizeof(redis_session_lock_status)); - lock_status->is_locked = 0; - pool->lock_status = lock_status; for (i=0,j=0,path_len=strlen(save_path); ilock_status->session_key TSRMLS_CC); - RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; + RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (redis_sock) { lock_release(redis_sock, pool->lock_status TSRMLS_CC); } @@ -551,7 +607,7 @@ PS_READ_FUNC(redis) cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); efree(resp); - if (!lock_acquire(redis_sock, pool->lock_status TSRMLS_CC)) { + if (lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) != SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Acquire of session lock was not successful"); } @@ -631,7 +687,7 @@ PS_WRITE_FUNC(redis) return FAILURE; } - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + if(IS_REDIS_OK(response, response_len)) { efree(response); return SUCCESS; } else { diff --git a/redis_session.h b/redis_session.h index ad346a1434..02cc6c2ff3 100644 --- a/redis_session.h +++ b/redis_session.h @@ -3,20 +3,6 @@ #ifdef PHP_SESSION #include "ext/session/php_session.h" -typedef struct { - zend_bool is_locked; - char *session_key; - char *lock_key; - char *lock_secret; -} redis_session_lock_status; - -int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); -void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); -void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); -int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC); -int upload_lock_release_script(RedisSock *redis_sock TSRMLS_DC); -void calculate_lock_secret(redis_session_lock_status *lock_status); - PS_OPEN_FUNC(redis); PS_CLOSE_FUNC(redis); PS_READ_FUNC(redis); From 5a3f76247d72aa4d7c671dbd7f657b4085ee6a17 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 11 Nov 2017 11:21:44 -0800 Subject: [PATCH 0889/1986] Actually change whether we're locked if we can't read the lock key --- redis_session.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis_session.c b/redis_session.c index bbcd1d988b..1ad5b49aae 100644 --- a/redis_session.c +++ b/redis_session.c @@ -345,6 +345,7 @@ static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status if (lock_status->is_locked && INI_INT("redis.session.lock_expire") == 0) return; + /* Command to get our lock key value and compare secrets */ cmdlen = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key, strlen(lock_status->lock_key)); @@ -353,6 +354,8 @@ static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status if (reply != NULL) { lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret); efree(reply); + } else { + lock_status->is_locked = 0; } /* Issue a warning if we're not locked. We don't attempt to refresh the lock From 479420f4922f8cb4777ee696807b62d4a95e2887 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 14 Nov 2017 16:08:02 +0200 Subject: [PATCH 0890/1986] TravisCI: refactor call of redis-trib.rb --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eaeda8b39c..899311ed58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ before_script: - for PORT in $(seq 6379 6382); do redis-server --port $PORT --daemonize yes; done - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done - wget https://raw.githubusercontent.com/antirez/redis/unstable/src/redis-trib.rb - - echo yes | ruby redis-trib.rb create --replicas 3 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 127.0.0.1:7007 127.0.0.1:7008 127.0.0.1:7009 127.0.0.1:7010 127.0.0.1:7011 + - echo yes | ruby redis-trib.rb create --replicas 3 $(seq -f 127.0.0.1:%g 7000 7011) - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini script: - php tests/TestRedis.php --class Redis From 7c5a68c88651ed8f91071b9ddf148a1036171486 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 17 Nov 2017 09:41:12 +0200 Subject: [PATCH 0891/1986] Refactoring Avoid socket connection in destructor Replace IF_ATOMIC/IF_MULTI/IF_PIPELINE macroses with IS_ATOMIC/IS_MULTI/IS_PIPELINE respectively. Remove IF_NOT_* macroses --- common.h | 17 +++--- library.c | 173 +++++++++++++++++++++++++++--------------------------- redis.c | 76 ++++++++++++------------ 3 files changed, 130 insertions(+), 136 deletions(-) diff --git a/common.h b/common.h index 708b1e1eb9..4d3977f855 100644 --- a/common.h +++ b/common.h @@ -507,12 +507,9 @@ typedef enum _PUBSUB_TYPE { #define MULTI 1 #define PIPELINE 2 -#define IF_ATOMIC() if (redis_sock->mode == ATOMIC) -#define IF_NOT_ATOMIC() if (redis_sock->mode != ATOMIC) -#define IF_MULTI() if (redis_sock->mode & MULTI) -#define IF_NOT_MULTI() if (!(redis_sock->mode & MULTI)) -#define IF_PIPELINE() if (redis_sock->mode & PIPELINE) -#define IF_NOT_PIPELINE() if (!(redis_sock->mode & PIPELINE)) +#define IS_ATOMIC(redis_sock) (redis_sock->mode == ATOMIC) +#define IS_MULTI(redis_sock) (redis_sock->mode & MULTI) +#define IS_PIPELINE(redis_sock) (redis_sock->mode & PIPELINE) #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \ if (redis_sock->pipeline_cmd == NULL) { \ @@ -547,7 +544,7 @@ typedef enum _PUBSUB_TYPE { } while (0) #define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \ - IF_PIPELINE() { \ + if (IS_PIPELINE(redis_sock)) { \ PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \ } else { \ SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \ @@ -555,7 +552,7 @@ typedef enum _PUBSUB_TYPE { efree(cmd); #define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ - IF_NOT_PIPELINE() { \ + if (!IS_PIPELINE(redis_sock)) { \ if (redis_response_enqueued(redis_sock TSRMLS_CC) != SUCCESS) { \ RETURN_FALSE; \ } \ @@ -583,7 +580,7 @@ typedef enum _PUBSUB_TYPE { RETURN_FALSE; \ } \ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ - IF_ATOMIC() { \ + if (IS_ATOMIC(redis_sock)) { \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \ } else { \ REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \ @@ -599,7 +596,7 @@ typedef enum _PUBSUB_TYPE { RETURN_FALSE; \ } \ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \ - IF_ATOMIC() { \ + if (IS_ATOMIC(redis_sock)) { \ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \ } else { \ REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \ diff --git a/library.c b/library.c index b9eb581014..aff99057a0 100644 --- a/library.c +++ b/library.c @@ -745,20 +745,19 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi double ret; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - return; - } else { + if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } + add_next_index_bool(z_tab, 0); + return; } ret = atof(response); efree(response); - IF_NOT_ATOMIC() { - add_next_index_double(z_tab, ret); - } else { + if (IS_ATOMIC(redis_sock)) { RETURN_DOUBLE(ret); + } else { + add_next_index_double(z_tab, ret); } } @@ -768,12 +767,11 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * long l; if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - return; - } else { + if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } + add_next_index_bool(z_tab, 0); + return; } if (strncmp(response, "+string", 7) == 0) { @@ -791,10 +789,10 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } efree(response); - IF_NOT_ATOMIC() { - add_next_index_long(z_tab, l); - } else { + if (IS_ATOMIC(redis_sock)) { RETURN_LONG(l); + } else { + add_next_index_long(z_tab, l); } } @@ -814,10 +812,10 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * /* Free source response */ efree(response); - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_ret); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, z_ret); } } @@ -896,10 +894,10 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo efree(resp); /* Return or append depending if we're atomic */ - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_ret); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, z_ret); } } @@ -1024,10 +1022,10 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (ret && success_callback != NULL) { success_callback(redis_sock); } - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, ret); - } else { + if (IS_ATOMIC(redis_sock)) { RETURN_BOOL(ret); + } else { + add_next_index_bool(z_tab, ret); } } @@ -1050,12 +1048,11 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - return; - } else { + if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } + add_next_index_bool(z_tab, 0); + return; } if(response[0] == ':') { @@ -1064,24 +1061,24 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, #else long long ret = atoll(response + 1); #endif - IF_NOT_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if(ret > LONG_MAX) { /* overflow */ - add_next_index_stringl(z_tab, response + 1, response_len - 1); + RETVAL_STRINGL(response + 1, response_len - 1); } else { - add_next_index_long(z_tab, (long)ret); + RETVAL_LONG((long)ret); } } else { if(ret > LONG_MAX) { /* overflow */ - RETVAL_STRINGL(response + 1, response_len - 1); + add_next_index_stringl(z_tab, response + 1, response_len - 1); } else { - RETVAL_LONG((long)ret); + add_next_index_long(z_tab, (long)ret); } } } else { - IF_NOT_ATOMIC() { - add_next_index_null(z_tab); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; + } else { + add_next_index_null(z_tab); } } efree(response); @@ -1160,10 +1157,10 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } if(inbuf[0] != '*') { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } return -1; } @@ -1181,10 +1178,10 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Zip keys and values */ array_zip_values_and_scores(redis_sock, z_multi_result, decode TSRMLS_CC); - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_multi_result); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_multi_result, 0, 1); + } else { + add_next_index_zval(z_tab, z_multi_result); } return 0; @@ -1233,10 +1230,10 @@ redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ta efree(response); } - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, ret); - } else { + if (IS_ATOMIC(redis_sock)) { RETURN_BOOL(ret); + } else { + add_next_index_bool(z_tab, ret); } } @@ -1248,13 +1245,17 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - return; + if (IS_ATOMIC(redis_sock)) { + RETURN_FALSE; } - RETURN_FALSE; + add_next_index_bool(z_tab, 0); + return; } - IF_NOT_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { + if (!redis_unpack(redis_sock, response, response_len, return_value TSRMLS_CC)) { + RETVAL_STRINGL(response, response_len); + } + } else { zval zv, *z = &zv; if (redis_unpack(redis_sock, response, response_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) @@ -1265,10 +1266,6 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } else { add_next_index_stringl(z_tab, response, response_len); } - } else { - if (!redis_unpack(redis_sock, response, response_len, return_value TSRMLS_CC)) { - RETVAL_STRINGL(response, response_len); - } } efree(response); } @@ -1285,16 +1282,16 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); + if (IS_ATOMIC(redis_sock)) { + RETURN_FALSE; + } + add_next_index_bool(z_tab, 0); return; - } - RETURN_FALSE; } - IF_NOT_ATOMIC() { - add_next_index_stringl(z_tab, response, response_len); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_STRINGL(response, response_len); + } else { + add_next_index_stringl(z_tab, response, response_len); } efree(response); } @@ -1308,11 +1305,11 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock /* Add or return false if we can't read from the socket */ if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC))==NULL) { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - return; + if (IS_ATOMIC(redis_sock)) { + RETURN_FALSE; } - RETURN_FALSE; + add_next_index_bool(z_tab, 0); + return; } zval zv, *z_result = &zv; @@ -1356,10 +1353,10 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock efree(resp); - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_result); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_result, 0, 1); + } else { + add_next_index_zval(z_tab, z_result); } } @@ -1579,13 +1576,13 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, } if(inbuf[0] != '*') { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - } else { + if (IS_ATOMIC(redis_sock)) { if (inbuf[0] == '-') { redis_sock_set_err(redis_sock, inbuf+1, len); } RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } return -1; } @@ -1599,10 +1596,10 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_multi_result, numElems, UNSERIALIZE_ALL); - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_multi_result); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_multi_result, 0, 1); + } else { + add_next_index_zval(z_tab, z_multi_result); } /*zval_copy_ctor(return_value); */ return 0; @@ -1622,13 +1619,13 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval } if(inbuf[0] != '*') { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - } else { + if (IS_ATOMIC(redis_sock)) { if (inbuf[0] == '-') { redis_sock_set_err(redis_sock, inbuf+1, len); } RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } return -1; } @@ -1642,10 +1639,10 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_multi_result, numElems, UNSERIALIZE_NONE); - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_multi_result); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_multi_result, 0, 1); + } else { + add_next_index_zval(z_tab, z_multi_result); } /*zval_copy_ctor(return_value); */ return 0; @@ -1702,10 +1699,10 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc } if(inbuf[0] != '*') { - IF_NOT_ATOMIC() { - add_next_index_bool(z_tab, 0); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); } return -1; } @@ -1739,10 +1736,10 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc } efree(z_keys); - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_multi_result); - } else { + if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_multi_result, 0, 1); + } else { + add_next_index_zval(z_tab, z_multi_result); } return 0; } @@ -2260,11 +2257,11 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - IF_NOT_ATOMIC() { - add_next_index_zval(z_tab, z_ret); - } else { + if (IS_ATOMIC(redis_sock)) { /* Set our return value */ RETVAL_ZVAL(z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, z_ret); } /* Success */ diff --git a/redis.c b/redis.c index df18decddf..4370297a96 100644 --- a/redis.c +++ b/redis.c @@ -843,13 +843,13 @@ PHP_METHOD(Redis,__destruct) { // Grab our socket RedisSock *redis_sock; - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 1)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 1)) == NULL) { RETURN_FALSE; } // If we think we're in MULTI mode, send a discard - IF_MULTI() { - IF_NOT_PIPELINE() { + if (IS_MULTI(redis_sock)) { + if (!IS_PIPELINE(redis_sock) && redis_sock->stream) { // Discard any multi commands, and free any callbacks that have been // queued redis_send_discard(redis_sock TSRMLS_CC); @@ -1151,7 +1151,7 @@ PHP_METHOD(Redis, getMultiple) /* Kick off our command */ REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { RETURN_FALSE; @@ -1443,7 +1443,7 @@ PHP_METHOD(Redis, sRandMember) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(have_count) { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) { @@ -1452,7 +1452,7 @@ PHP_METHOD(Redis, sRandMember) } REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); } else { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } @@ -1527,7 +1527,7 @@ PHP_METHOD(Redis, sort) { } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { @@ -1624,7 +1624,7 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) } REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { @@ -1792,7 +1792,7 @@ PHP_METHOD(Redis, info) { } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } @@ -1824,7 +1824,7 @@ PHP_METHOD(Redis, select) { cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", dbNumber); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } @@ -1882,7 +1882,7 @@ void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) } ZEND_HASH_FOREACH_END(); REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(fun); @@ -1941,12 +1941,12 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(withscores) { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); } else { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) { @@ -2209,21 +2209,21 @@ PHP_METHOD(Redis, multi) if (multi_value == PIPELINE) { /* Cannot enter pipeline mode in a MULTI block */ - IF_MULTI() { + if (IS_MULTI(redis_sock)) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate pipeline in multi mode!"); RETURN_FALSE; } /* Enable PIPELINE if we're not already in one */ - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { free_reply_callbacks(redis_sock); REDIS_ENABLE_MODE(redis_sock, PIPELINE); } } else if (multi_value == MULTI) { /* Don't want to do anything if we're alredy in MULTI mode */ - IF_NOT_MULTI() { + if (!IS_MULTI(redis_sock)) { cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", ""); - IF_PIPELINE() { + if (IS_PIPELINE(redis_sock)) { PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); efree(cmd); REDIS_SAVE_CALLBACK(NULL, NULL); @@ -2313,9 +2313,9 @@ PHP_METHOD(Redis, exec) RETURN_FALSE; } - IF_MULTI() { + if (IS_MULTI(redis_sock)) { cmd_len = REDIS_SPPRINTF(&cmd, "EXEC", ""); - IF_PIPELINE() { + if (IS_PIPELINE(redis_sock)) { PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); efree(cmd); REDIS_SAVE_CALLBACK(NULL, NULL); @@ -2336,7 +2336,7 @@ PHP_METHOD(Redis, exec) } } - IF_PIPELINE() { + if (IS_PIPELINE(redis_sock)) { if (redis_sock->pipeline_cmd == NULL) { /* Empty array when no command was run. */ array_init(return_value); @@ -2430,14 +2430,14 @@ PHP_METHOD(Redis, pipeline) } /* User cannot enter MULTI mode if already in a pipeline */ - IF_MULTI() { + if (IS_MULTI(redis_sock)) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate pipeline in multi mode!"); RETURN_FALSE; } /* Enable pipeline mode unless we're already in that mode in which case this * is just a NO OP */ - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { /* NB : we keep the function fold, to detect the last function. * We need the response format of the n - 1 command. So, we can delete * when n > 2, the { 1 .. n - 2} commands */ @@ -2594,7 +2594,7 @@ PHP_METHOD(Redis, slaveof) } REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } @@ -2622,13 +2622,13 @@ PHP_METHOD(Redis, object) REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(rtype == TYPE_INT) { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_long_response); } else { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } @@ -2697,7 +2697,7 @@ PHP_METHOD(Redis, config) cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "ss", op, op_len, key, key_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw); @@ -2706,7 +2706,7 @@ PHP_METHOD(Redis, config) cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_boolean_response( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } @@ -2763,7 +2763,7 @@ PHP_METHOD(Redis, slowlog) { /* Kick off our command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { @@ -2804,7 +2804,7 @@ PHP_METHOD(Redis, wait) { /* Kick it off */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); } @@ -2909,7 +2909,7 @@ PHP_METHOD(Redis, pubsub) { REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(type == PUBSUB_NUMSUB) { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) { @@ -2918,7 +2918,7 @@ PHP_METHOD(Redis, pubsub) { } REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_int); } else { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) { @@ -2972,7 +2972,7 @@ PHP_METHOD(Redis, script) { // Kick off our request REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { @@ -3109,9 +3109,9 @@ PHP_METHOD(Redis, getMode) { RETURN_FALSE; } - IF_PIPELINE() { + if (IS_PIPELINE(redis_sock)) { RETVAL_LONG(PIPELINE); - } else IF_MULTI() { + } else if (IS_MULTI(redis_sock)) { RETVAL_LONG(MULTI); } else { RETVAL_LONG(ATOMIC); @@ -3262,13 +3262,13 @@ PHP_METHOD(Redis, client) { /* We handle CLIENT LIST with a custom response function */ if(!strncasecmp(opt, "list", 4)) { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, NULL); } REDIS_PROCESS_RESPONSE(redis_client_list_reply); } else { - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,NULL,NULL); } @@ -3308,7 +3308,7 @@ PHP_METHOD(Redis, rawcommand) { /* Execute our command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - IF_ATOMIC() { + if (IS_ATOMIC(redis_sock)) { redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL); } REDIS_PROCESS_RESPONSE(redis_read_variant_reply); @@ -3413,7 +3413,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } /* Calling this in a pipeline makes no sense */ - IF_NOT_ATOMIC() { + if (!IS_ATOMIC(redis_sock)) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't call SCAN commands in multi or pipeline mode!"); RETURN_FALSE; From 67babe15d39202ad25d8b0ddbd5a18f097a7241e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 17 Nov 2017 11:02:41 +0200 Subject: [PATCH 0892/1986] Remove trailing spaces --- cluster_library.c | 2 +- library.c | 2 +- redis_cluster.c | 136 +++++++++++++++++++++++----------------------- redis_session.c | 14 ++--- 4 files changed, 77 insertions(+), 77 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 84f5ff28ca..e1af22e4f3 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -37,7 +37,7 @@ static void cluster_log(char *fmt, ...) fprintf(stderr, "%s\n", buffer); } -// Debug function to dump a clusterReply structure recursively +// Debug function to dump a clusterReply structure recursively static void dump_reply(clusterReply *reply, int indent) { smart_string buf = {0}; int i; diff --git a/library.c b/library.c index aff99057a0..0358af8a14 100644 --- a/library.c +++ b/library.c @@ -1284,7 +1284,7 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; - } + } add_next_index_bool(z_tab, 0); return; } diff --git a/redis_cluster.c b/redis_cluster.c index f113647167..95bcba40f7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -147,7 +147,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, get, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getbit, arginfo_key_offset, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getlasterror, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, getmode, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, getmode, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getoption, arginfo_getoption, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getset, arginfo_key_value, ZEND_ACC_PUBLIC) @@ -308,7 +308,7 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { // Allocate our actual struct cluster = ecalloc(1, sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); #endif - + // We're not currently subscribed anywhere cluster->subscribed_slot = -1; @@ -391,7 +391,7 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, { // Validate timeout if(timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, + zend_throw_exception(redis_cluster_exception_ce, "Invalid timeout", 0 TSRMLS_CC); } @@ -410,14 +410,14 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, * socket type operations */ c->timeout = timeout; c->read_timeout = read_timeout; - + /* Set our option to use or not use persistent connections */ c->persistent = persistent; /* Calculate the number of miliseconds we will wait when bouncing around, * (e.g. a node goes down), which is not the same as a standard timeout. */ c->waitms = (long)(timeout * 1000); - + // Initialize our RedisSock "seed" objects cluster_init_seeds(c, ht_seeds); @@ -445,7 +445,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't find seeds for cluster", 0 TSRMLS_CC); return; } - + /* Connection timeout */ array_init(&z_timeout); if ((iptr = INI_STR("redis.clusters.timeout")) != NULL) { @@ -490,7 +490,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { } /* Attempt to create/connect to the cluster */ - redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent TSRMLS_CC); + redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent TSRMLS_CC); /* Clean up our arrays */ zval_dtor(&z_seeds); @@ -538,7 +538,7 @@ PHP_METHOD(RedisCluster, __construct) { } } -/* +/* * RedisCluster method implementation */ @@ -561,8 +561,8 @@ PHP_METHOD(RedisCluster, set) { /* }}} */ /* Generic handler for MGET/MSET/MSETNX */ -static int -distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, +static int +distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, clusterMultiCmd *mc, zval *z_ret, int last, cluster_cb cb) { clusterMultiCtx *ctx; @@ -614,7 +614,7 @@ typedef struct clusterKeyValHT { } clusterKeyValHT; /* Helper to pull a key/value pair from a HashTable */ -static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, +static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, clusterKeyValHT *kv TSRMLS_DC) { zval *z_val; @@ -779,11 +779,11 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, efree(z_args); return -1; } - + // If the slots have changed, kick off the keys we've aggregated if(slot != kv.slot) { // Process this batch of MGET keys - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, i==argc, cb)<0) { cluster_multi_free(&mc); @@ -803,7 +803,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, if(kv.key_free) efree(kv.key); // Update the last slot we encountered, and the key we're on - slot = kv.slot; + slot = kv.slot; i++; zend_hash_move_forward_ex(ht_arr, &ptr); @@ -812,7 +812,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // If we've got straggler(s) process them if(mc.argc > 0) { - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, 1, cb)<0) { cluster_multi_free(&mc); @@ -892,7 +892,7 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // If the slots have changed, process responses if(slot != kv.slot) { - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, z_ret, i==argc, cb)<0) { return -1; @@ -917,8 +917,8 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // If we've got stragglers, process them too if(mc.argc > 0) { - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, - z_ret, 1, cb)<0) + if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, + z_ret, 1, cb)<0) { return -1; } @@ -949,7 +949,7 @@ PHP_METHOD(RedisCluster, del) { ZVAL_LONG(z_ret, 0); // Parse args, process - if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", + if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL")-1, z_ret, cluster_del_resp)<0) { efree(z_ret); @@ -992,7 +992,7 @@ PHP_METHOD(RedisCluster, mset) { ZVAL_TRUE(z_ret); // Parse args and process. If we get a failure, free zval and return FALSE. - if(cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", + if(cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", sizeof("MSET")-1, z_ret, cluster_mset_resp)==-1) { efree(z_ret); @@ -1004,7 +1004,7 @@ PHP_METHOD(RedisCluster, mset) { PHP_METHOD(RedisCluster, msetnx) { zval *z_ret; - // Array response + // Array response #if (PHP_MAJOR_VERSION < 7) MAKE_STD_ZVAL(z_ret); #else @@ -1092,8 +1092,8 @@ PHP_METHOD(RedisCluster, keys) { /* Ensure we can get a response */ resp = cluster_read_resp(c TSRMLS_CC); if(!resp) { - php_error_docref(0 TSRMLS_CC, E_WARNING, - "Can't read response from %s:%d", ZSTR_VAL(node->sock->host), + php_error_docref(0 TSRMLS_CC, E_WARNING, + "Can't read response from %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); continue; } @@ -1770,10 +1770,10 @@ PHP_METHOD(RedisCluster, zrevrangebyscore) { } /* }}} */ -/* {{{ proto array RedisCluster::zrangebylex(string key, string min, string max, +/* {{{ proto array RedisCluster::zrangebylex(string key, string min, string max, * [offset, count]) */ PHP_METHOD(RedisCluster, zrangebylex) { - CLUSTER_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, + CLUSTER_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd, cluster_mbulk_resp, 1); } /* }}} */ @@ -1794,7 +1794,7 @@ PHP_METHOD(RedisCluster, zlexcount) { /* {{{ proto long RedisCluster::zremrangebylex(string key, string min, string max) */ PHP_METHOD(RedisCluster, zremrangebylex) { - CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, + CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd, cluster_long_resp, 0); } /* }}} */ @@ -1880,7 +1880,7 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } // Call directly because we're going to set the slot manually - if(redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, + if(redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, &slot, &ctx) ==FAILURE) { @@ -1888,7 +1888,7 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } // This has to operate on our subscribe slot - if(cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK + if(cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK TSRMLS_CC) ==FAILURE) { zend_throw_exception(redis_cluster_exception_ce, @@ -1905,7 +1905,7 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* {{{ proto array RedisCluster::unsubscribe(array chans) */ PHP_METHOD(RedisCluster, unsubscribe) { - generic_unsub_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), + generic_unsub_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), "UNSUBSCRIBE"); } /* }}} */ @@ -1953,33 +1953,33 @@ PHP_METHOD(RedisCluster, getlasterror) { /* {{{ proto bool RedisCluster::clearlasterror() */ PHP_METHOD(RedisCluster, clearlasterror) { redisCluster *c = GET_CONTEXT(); - + if (c->err) { zend_string_release(c->err); c->err = NULL; } - + RETURN_TRUE; } /* }}} */ /* {{{ proto long RedisCluster::getOption(long option */ PHP_METHOD(RedisCluster, getoption) { - redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT()->flags, GET_CONTEXT()); } /* }}} */ /* {{{ proto bool RedisCluster::setOption(long option, mixed value) */ PHP_METHOD(RedisCluster, setoption) { - redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT()->flags, GET_CONTEXT()); } /* }}} */ /* {{{ proto string RedisCluster::_prefix(string key) */ PHP_METHOD(RedisCluster, _prefix) { - redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT()->flags); } /* }}} */ @@ -2122,7 +2122,7 @@ PHP_METHOD(RedisCluster, watch) { // Construct our watch command for this node redis_cmd_init_sstr(&cmd, dl->len, "WATCH", sizeof("WATCH")-1); for (i = 0; i < dl->len; i++) { - redis_cmd_append_sstr(&cmd, dl->entry[i].key, + redis_cmd_append_sstr(&cmd, dl->entry[i].key, dl->entry[i].key_len); } @@ -2154,7 +2154,7 @@ PHP_METHOD(RedisCluster, unwatch) { // Send UNWATCH to nodes that need it for(slot=0;slotmaster[slot] && SLOT_SOCK(c,slot)->watching) { - if(cluster_send_slot(c, slot, RESP_UNWATCH_CMD, + if(cluster_send_slot(c, slot, RESP_UNWATCH_CMD, sizeof(RESP_UNWATCH_CMD)-1, TYPE_LINE TSRMLS_CC)==-1) { @@ -2194,7 +2194,7 @@ PHP_METHOD(RedisCluster, exec) { // Free our queue, reset MULTI state CLUSTER_FREE_QUEUE(c); CLUSTER_RESET_MULTI(c); - + RETURN_FALSE; } SLOT_SOCK(c, fi->slot)->mode = ATOMIC; @@ -2220,7 +2220,7 @@ PHP_METHOD(RedisCluster, discard) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); RETURN_FALSE; } - + if(cluster_abort_exec(c TSRMLS_CC)<0) { CLUSTER_RESET_MULTI(c); } @@ -2232,7 +2232,7 @@ PHP_METHOD(RedisCluster, discard) { /* Get a slot either by key (string) or host/port array */ static short -cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) +cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { strlen_t key_len; int key_free; @@ -2244,7 +2244,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) /* If it's a string, treat it as a key. Otherwise, look for a two * element array */ if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG || - Z_TYPE_P(z_arg)==IS_DOUBLE) + Z_TYPE_P(z_arg)==IS_DOUBLE) { /* Allow for any scalar here */ zstr = zval_get_string(z_arg); @@ -2256,7 +2256,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) slot = cluster_hash_key(key, key_len); zend_string_release(zstr); if(key_free) efree(key); - } else if (Z_TYPE_P(z_arg) == IS_ARRAY && + } else if (Z_TYPE_P(z_arg) == IS_ARRAY && (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL && (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL && Z_TYPE_P(z_host) == IS_STRING && Z_TYPE_P(z_port) == IS_LONG @@ -2266,7 +2266,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) (unsigned short)Z_LVAL_P(z_port)); /* Inform the caller if they've passed bad data */ - if(slot < 0) { + if(slot < 0) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", Z_STRVAL_P(z_host), Z_LVAL_P(z_port)); } @@ -2281,12 +2281,12 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) /* Generic handler for things we want directed at a given node, like SAVE, * BGSAVE, FLUSHDB, FLUSHALL, etc */ -static void -cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, +static void +cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply_type, cluster_cb cb) { redisCluster *c = GET_CONTEXT(); - char *cmd; + char *cmd; int cmd_len; zval *z_arg; short slot; @@ -2323,7 +2323,7 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, /* Generic routine for handling various commands which need to be directed at * a node, but have complex syntax. We simply parse out the arguments and send * the command as constructed by the caller */ -static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) +static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { redisCluster *c = GET_CONTEXT(); smart_string cmd = {0}; @@ -2387,11 +2387,11 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* Generic method for HSCAN, SSCAN, and ZSCAN */ -static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, +static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { redisCluster *c = GET_CONTEXT(); - char *cmd, *pat=NULL, *key=NULL; + char *cmd, *pat=NULL, *key=NULL; strlen_t key_len = 0, pat_len = 0; int cmd_len, key_free=0; short slot; @@ -2408,7 +2408,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, } /* Parse arguments */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|s!l", &key, + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|s!l", &key, &key_len, &z_it, &pat, &pat_len, &count)==FAILURE) { RETURN_FALSE; @@ -2439,11 +2439,11 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, if (Z_TYPE_P(return_value) == IS_ARRAY) { zval_dtor(return_value); ZVAL_NULL(return_value); - } - + } + // Create command cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, it, pat, pat_len, - count); + count); // Send it off if(cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC)==FAILURE) @@ -2456,7 +2456,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, } // Read response - if(cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, + if(cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, &it)==FAILURE) { zend_throw_exception(redis_cluster_exception_ce, @@ -2503,7 +2503,7 @@ PHP_METHOD(RedisCluster, scan) { } /* Parse arguments */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z|s!l", &z_it, + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z|s!l", &z_it, &z_node, &pat, &pat_len, &count)==FAILURE) { RETURN_FALSE; @@ -2527,17 +2527,17 @@ PHP_METHOD(RedisCluster, scan) { zval_dtor(return_value); ZVAL_NULL(return_value); } - + /* Construct our command */ cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, count); - + if((slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC))<0) { RETURN_FALSE; } // Send it to the node in question - if(cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC)<0) + if(cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't send SCAN to node", 0 TSRMLS_CC); @@ -2589,10 +2589,10 @@ PHP_METHOD(RedisCluster, save) { } /* }}} */ -/* {{{ proto RedisCluster::bgsave(string key) +/* {{{ proto RedisCluster::bgsave(string key) * proto RedisCluster::bgsave(string host, long port) */ PHP_METHOD(RedisCluster, bgsave) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE", + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE", TYPE_LINE, cluster_bool_resp); } /* }}} */ @@ -2703,12 +2703,12 @@ PHP_METHOD(RedisCluster, client) { cluster_cb cb; /* Parse args */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|s", &z_node, &opt, + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|s", &z_node, &opt, &opt_len, &arg, &arg_len)==FAILURE) { RETURN_FALSE; } - + /* Make sure we can properly resolve the slot */ slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC); if(slot<0) RETURN_FALSE; @@ -2718,7 +2718,7 @@ PHP_METHOD(RedisCluster, client) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; cb = cluster_client_list_resp; } else if ((opt_len == 4 && !strncasecmp(opt, "kill", 4)) || - (opt_len == 7 && !strncasecmp(opt, "setname", 7))) + (opt_len == 7 && !strncasecmp(opt, "setname", 7))) { rtype = TYPE_LINE; cb = cluster_bool_resp; @@ -2764,14 +2764,14 @@ PHP_METHOD(RedisCluster, client) { /* {{{ proto mixed RedisCluster::cluster(variant) */ PHP_METHOD(RedisCluster, cluster) { - cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CLUSTER", + cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CLUSTER", sizeof("CLUSTER")-1); } /* }}} */ /* }}} */ -/* {{{ proto mixed RedisCluster::config(string key, ...) +/* {{{ proto mixed RedisCluster::config(string key, ...) * proto mixed RedisCluster::config(array host_port, ...) */ PHP_METHOD(RedisCluster, config) { cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CONFIG", @@ -2787,7 +2787,7 @@ PHP_METHOD(RedisCluster, pubsub) { } /* }}} */ -/* {{{ proto mixed RedisCluster::script(string key, ...) +/* {{{ proto mixed RedisCluster::script(string key, ...) * proto mixed RedisCluster::script(array host_port, ...) */ PHP_METHOD(RedisCluster, script) { redisCluster *c = GET_CONTEXT(); @@ -2918,7 +2918,7 @@ PHP_METHOD(RedisCluster, echo) { int cmd_len; strlen_t msg_len; short slot; - + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &msg, &msg_len)==FAILURE) { @@ -2952,7 +2952,7 @@ PHP_METHOD(RedisCluster, echo) { } else { void *ctx = NULL; CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx); - } + } efree(cmd); } @@ -2981,7 +2981,7 @@ PHP_METHOD(RedisCluster, rawcommand) { efree(z_args); RETURN_FALSE; } else if (redis_build_raw_cmd(&z_args[1], argc-1, &cmd, &cmd_len TSRMLS_CC) || - (slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC))<0) + (slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC))<0) { if (cmd) efree(cmd); efree(z_args); diff --git a/redis_session.c b/redis_session.c index fe753e7bf2..a704f4f501 100644 --- a/redis_session.c +++ b/redis_session.c @@ -521,7 +521,7 @@ static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_le } /* Simple helper to retreive a boolean (0 or 1) value from a string stored in our - * session.save_path variable. This is so the user can use 0, 1, or 'true', + * session.save_path variable. This is so the user can use 0, 1, or 'true', * 'false' */ static void session_conf_bool(HashTable *ht_conf, char *key, int keylen, int *retval) { @@ -529,7 +529,7 @@ static void session_conf_bool(HashTable *ht_conf, char *key, int keylen, char *str; int strlen; - /* See if we have the option, and it's a string */ + /* See if we have the option, and it's a string */ if ((z_val = zend_hash_str_find(ht_conf, key, keylen - 1)) != NULL && Z_TYPE_P(z_val) == IS_STRING ) { @@ -544,7 +544,7 @@ static void session_conf_bool(HashTable *ht_conf, char *key, int keylen, } /* Prefix a session key */ -static char *cluster_session_key(redisCluster *c, const char *key, int keylen, +static char *cluster_session_key(redisCluster *c, const char *key, int keylen, int *skeylen, short *slot) { char *skey; @@ -552,7 +552,7 @@ static char *cluster_session_key(redisCluster *c, const char *key, int keylen, skey = emalloc(*skeylen); memcpy(skey, ZSTR_VAL(c->flags->prefix), ZSTR_LEN(c->flags->prefix)); memcpy(skey + ZSTR_LEN(c->flags->prefix), key, keylen); - + *slot = cluster_hash_key(skey, *skeylen); return skey; @@ -590,7 +590,7 @@ PS_OPEN_FUNC(rediscluster) { /* Grab persistent option */ session_conf_bool(ht_conf, "persistent", sizeof("persistent"), &persistent); - + /* Sanity check on our timeouts */ if (timeout < 0 || read_timeout < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -635,7 +635,7 @@ PS_OPEN_FUNC(rediscluster) { /* Cleanup */ zval_dtor(&z_conf); - + return retval; } @@ -775,7 +775,7 @@ PS_DESTROY_FUNC(rediscluster) { reply = cluster_read_resp(c TSRMLS_CC); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); - return FAILURE; + return FAILURE; } /* Clean up our reply */ From 6202047f5f179ef94cbf9db77117d8d7a149d63b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 20 Nov 2017 09:51:30 +0200 Subject: [PATCH 0893/1986] Fix cluster_init_seeds. Thanks @adlagares! --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index e1af22e4f3..ca20581812 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -912,7 +912,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { cluster->read_timeout, cluster->persistent, NULL, 0, 0); // Index this seed by host/port - key_len = snprintf(key, sizeof(key), "%s:%u", redis_sock->host, + key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(redis_sock->host), redis_sock->port); // Add to our seed HashTable From 8605590d561a62926c0096550ae4db9c68f9d689 Mon Sep 17 00:00:00 2001 From: Tom Arbesser-Rastburg Date: Tue, 28 Nov 2017 23:26:15 -0500 Subject: [PATCH 0894/1986] Markdown fixes --- README.markdown | 352 ++++++++++++++++++++++++------------------------ 1 file changed, 176 insertions(+), 176 deletions(-) diff --git a/README.markdown b/README.markdown index 4ba141d5cc..e69fad87f2 100644 --- a/README.markdown +++ b/README.markdown @@ -198,11 +198,11 @@ _**Description**_: Connects to a Redis instance. ##### *Parameters* -*host*: string. can be a host, or the path to a unix domain socket -*port*: int, optional -*timeout*: float, value in seconds (optional, default is 0 meaning unlimited) -*reserved*: should be NULL if retry_interval is specified -*retry_interval*: int, value in milliseconds (optional) +*host*: string. can be a host, or the path to a unix domain socket +*port*: int, optional +*timeout*: float, value in seconds (optional, default is 0 meaning unlimited) +*reserved*: should be NULL if retry_interval is specified +*retry_interval*: int, value in milliseconds (optional) *read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited) ##### *Return value* @@ -235,11 +235,11 @@ persistent equivalents. ##### *Parameters* -*host*: string. can be a host, or the path to a unix domain socket -*port*: int, optional -*timeout*: float, value in seconds (optional, default is 0 meaning unlimited) -*persistent_id*: string. identity for the requested persistent connection -*retry_interval*: int, value in milliseconds (optional) +*host*: string. can be a host, or the path to a unix domain socket +*port*: int, optional +*timeout*: float, value in seconds (optional, default is 0 meaning unlimited) +*persistent_id*: string. identity for the requested persistent connection +*retry_interval*: int, value in milliseconds (optional) *read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited) ##### *Return value* @@ -293,7 +293,7 @@ _**Description**_: Disconnects from the Redis instance, except when `pconnect` i _**Description**_: Set client option. ##### *Parameters* -*parameter name* +*parameter name* *parameter value* ##### *Return value* @@ -410,12 +410,12 @@ $redis->bgSave(); _**Description**_: Get or Set the Redis server configuration parameters. ##### *Parameters* -*operation* (string) either `GET` or `SET` -*key* string for `SET`, glob-pattern for `GET`. See http://redis.io/commands/config-get for examples. +*operation* (string) either `GET` or `SET` +*key* string for `SET`, glob-pattern for `GET`. See http://redis.io/commands/config-get for examples. *value* optional string (only for `SET`) ##### *Return value* -*Associative array* for `GET`, key -> value +*Associative array* for `GET`, key -> value *bool* for `SET` ##### *Examples* @@ -583,7 +583,7 @@ _**Description**_: Return the current server time. (none) ##### *Return value* -If successfull, the time will come back as an associative array with element zero being +If successful, the time will come back as an associative array with element zero being the unix timestamp, and element one being microseconds. ##### *Examples* @@ -596,7 +596,7 @@ $redis->time(); _**Description**_: Access the Redis slowLog ##### *Parameters* -*Operation* (string): This can be either `GET`, `LEN`, or `RESET` +*Operation* (string): This can be either `GET`, `LEN`, or `RESET` *Length* (integer), optional: If executing a `SLOWLOG GET` command, you can pass an optional length. ##### @@ -690,8 +690,8 @@ $redis->get('key'); _**Description**_: Set the string value in argument as value of the key. If you're using Redis >= 2.6.12, you can pass extended options as explained below ##### *Parameters* -*Key* -*Value* +*Key* +*Value* *Timeout or Options Array* (optional). If you pass an integer, phpredis will redirect to SETEX, and will try to use Redis >= 2.6.12 extended options if you pass an array with valid values ##### *Return value* @@ -793,7 +793,7 @@ $redis->exists('NonExistingKey'); /* FALSE */ _**Description**_: Increment the number stored at key by one. If the second argument is filled, it will be used as the integer value of the increment. ##### *Parameters* -*key* +*key* *value*: value that will be added to key (only for incrBy) ##### *Return value* @@ -815,7 +815,7 @@ $redis->incrBy('key1', 10); /* 14 */ _**Description**_: Increment the key with floating point precision. ##### *Parameters* -*key* +*key* *value*: (float) value that will be added to the key ##### *Return value* @@ -836,8 +836,8 @@ $redis->incrByFloat('key1', 2.5); /* 4 */ _**Description**_: Decrement the number stored at key by one. If the second argument is filled, it will be used as the integer value of the decrement. ##### *Parameters* -*key* -*value*: value that will be substracted to key (only for decrBy) +*key* +*value*: value that will be subtracted to key (only for decrBy) ##### *Return value* *INT* the new value @@ -854,7 +854,7 @@ $redis->decrBy('key1', 10); /* -13 */ ### mGet, getMultiple ----- -_**Description**_: Get the values of all the specified keys. If one or more keys dont exist, the array will contain `FALSE` at the position of the key. +_**Description**_: Get the values of all the specified keys. If one or more keys don't exist, the array will contain `FALSE` at the position of the key. ##### *Parameters* *Array*: Array containing the list of the keys @@ -1075,12 +1075,12 @@ _**Description**_: Returns the type of data pointed by a given key. ##### *Return value* -Depending on the type of the data pointed by the key, this method will return the following value: -string: Redis::REDIS_STRING -set: Redis::REDIS_SET -list: Redis::REDIS_LIST -zset: Redis::REDIS_ZSET -hash: Redis::REDIS_HASH +Depending on the type of the data pointed by the key, this method will return the following value: +string: Redis::REDIS_STRING +set: Redis::REDIS_SET +list: Redis::REDIS_LIST +zset: Redis::REDIS_ZSET +hash: Redis::REDIS_HASH other: Redis::REDIS_NOT_FOUND ##### *Example* @@ -1113,8 +1113,8 @@ _**Description**_: Return a substring of a larger string *Note*: substr also supported but deprecated in redis. ##### *Parameters* -*key* -*start* +*key* +*start* *end* ##### *Return value* @@ -1167,7 +1167,7 @@ $redis->strlen('key'); /* 5 */ _**Description**_: Return a single bit out of a larger string ##### *Parameters* -*key* +*key* *offset* ##### *Return value* @@ -1185,8 +1185,8 @@ $redis->getBit('key', 1); /* 1 */ _**Description**_: Changes a single bit of a string. ##### *Parameters* -*key* -*offset* +*key* +*offset* *value*: bool or int (1 or 0) ##### *Return value* @@ -1205,9 +1205,9 @@ $redis->get('key'); /* chr(0x2f) = "/" = b("0010 1111") */ _**Description**_: Bitwise operation on multiple keys. ##### *Parameters* -*operation*: either "AND", "OR", "NOT", "XOR" -*ret_key*: return key -*key1* +*operation*: either "AND", "OR", "NOT", "XOR" +*ret_key*: return key +*key1* *key2...* ##### *Return value* @@ -1332,8 +1332,8 @@ $val = $redis->dump('foo'); // $val will be the Redis encoded key value ----- _**Description**_: Restore a key from the result of a DUMP operation. ##### *Parameters* -*key* string. The key name -*ttl* integer. How long the key should live (if zero, no expire will be set on the key) +*key* string. The key name +*ttl* integer. How long the key should live (if zero, no expire will be set on the key) *value* string (binary). The Redis encoded key value (from DUMP) ##### *Examples* ~~~ @@ -1391,8 +1391,8 @@ $redis->migrate('backup', 6379, ['key1', 'key2', 'key3'], 0, 3600); ----- _**Description**_: Adds a value to the hash stored at key. ##### *Parameters* -*key* -*hashKey* +*key* +*hashKey* *value* ##### *Return value* @@ -1426,11 +1426,11 @@ $redis->hSetNx('h', 'key1', 'world'); /* FALSE, 'key1' => 'hello' in the hash at ----- _**Description**_: Gets a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* -*key* +*key* *hashKey* ##### *Return value* -*STRING* The value, if the command executed successfully +*STRING* The value, if the command executed successfully *BOOL* `FALSE` in case of failure @@ -1454,9 +1454,9 @@ $redis->hLen('h'); /* returns 2 */ ----- _**Description**_: Removes a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. ##### *Parameters* -*key* -*hashKey1* -*hashKey2* +*key* +*hashKey1* +*hashKey2* ... ##### *Return value* @@ -1572,7 +1572,7 @@ The order is random and corresponds to redis' own internal representation of the ----- _**Description**_: Verify if the specified member exists in a key. ##### *Parameters* -*key* +*key* *memberKey* ##### *Return value* *BOOL*: If the member exists in the hash table, return `TRUE`, otherwise return `FALSE`. @@ -1587,8 +1587,8 @@ $redis->hExists('h', 'NonExistingKey'); /* FALSE */ ----- _**Description**_: Increments the value of a member from a hash by a given amount. ##### *Parameters* -*key* -*member* +*key* +*member* *value*: (integer) value that will be added to the member's value ##### *Return value* *LONG* the new value @@ -1603,8 +1603,8 @@ $redis->hIncrBy('h', 'x', 1); /* h[x] ← 2 + 1. Returns 3 */ ----- _**Description**_: Increments the value of a hash member by the provided float value ##### *Parameters* -*key* -*member* +*key* +*member* *value*: (float) value that will be added to the member's value ##### *Return value* *FLOAT* the new value @@ -1620,7 +1620,7 @@ $redis->hIncrByFloat('h', 'x', -3.0); /* returns 0.0: h[x] = 0.0 now */ ----- _**Description**_: Fills in a whole hash. Non-string values are converted to string, using the standard `(string)` cast. NULL values are stored as empty strings. ##### *Parameters* -*key* +*key* *members*: key → value array ##### *Return value* *BOOL* @@ -1635,7 +1635,7 @@ $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. ----- _**Description**_: Retrieve the values associated to the specified fields in the hash. ##### *Parameters* -*key* +*key* *memberKeys* Array ##### *Return value* *Array* An array of elements, the values of the specified fields in the hash, with the hash keys as array keys. @@ -1651,10 +1651,10 @@ $redis->hMGet('h', array('field1', 'field2')); /* returns array('field1' => 'val ----- _**Description**_: Scan a HASH value for members, with an optional pattern and count ##### *Parameters* -*key*: String -*iterator*: Long (reference) -*pattern*: Optional pattern to match against -*count*: How many keys to return in a go (only a sugestion to Redis) +*key*: String +*iterator*: Long (reference) +*pattern*: Optional pattern to match against +*count*: How many keys to return in a go (only a suggestion to Redis) ##### *Return value* *Array* An array of members that match our pattern @@ -1674,7 +1674,7 @@ while($arr_keys = $redis->hScan('hash', $it)) { ----- _**Description**_: Get the string length of the value associated with field in the hash stored at key. ##### *Parameters* -*key*: String +*key*: String *field*: String ##### *Return value* *LONG* the string length of the value associated with field, or zero when field is not present in the hash or key does not exist at all. @@ -1704,14 +1704,14 @@ _**Description**_: Is a blocking lPop(rPop) primitive. If at least one of the li If all the list identified by the keys passed in arguments are empty, blPop will block during the specified timeout until an element is pushed to one of those lists. This element will be popped. ##### *Parameters* -*ARRAY* Array containing the keys of the lists -*INTEGER* Timeout -Or -*STRING* Key1 -*STRING* Key2 -*STRING* Key3 -... -*STRING* Keyn +*ARRAY* Array containing the keys of the lists +*INTEGER* Timeout +Or +*STRING* Key1 +*STRING* Key2 +*STRING* Key3 +... +*STRING* Keyn *INTEGER* Timeout ##### *Return value* @@ -1750,8 +1750,8 @@ $redis->lPush('key1', 'A'); _**Description**_: A blocking version of `rPopLPush`, with an integral timeout in the third parameter. ##### *Parameters* -*Key*: srckey -*Key*: dstkey +*Key*: srckey +*Key*: dstkey *Long*: timeout ##### *Return value* @@ -1761,17 +1761,17 @@ _**Description**_: A blocking version of `rPopLPush`, with an integral timeout i ----- _**Description**_: Return the specified element of the list stored at the specified key. -0 the first element, 1 the second ... +0 the first element, 1 the second ... -1 the last element, -2 the penultimate ... Return `FALSE` in case of a bad index or a key that doesn't point to a list. ##### *Parameters* -*key* +*key* *index* ##### *Return value* -*String* the element at this index +*String* the element at this index *Bool* `FALSE` if the key identifies a non-string data type, or no value corresponds to this index in the list `Key`. ##### *Example* @@ -1792,9 +1792,9 @@ The parameter options specify the position of the insert (before or after). If the list didn't exists, or the pivot didn't exists, the value is not inserted. ##### *Parameters* -*key* -*position* Redis::BEFORE | Redis::AFTER -*pivot* +*key* +*position* Redis::BEFORE | Redis::AFTER +*pivot* *value* ##### *Return value* @@ -1826,7 +1826,7 @@ _**Description**_: Return and remove the first element of the list. *key* ##### *Return value* -*STRING* if command executed successfully +*STRING* if command executed successfully *BOOL* `FALSE` in case of failure (empty list) ##### *Example* @@ -1842,7 +1842,7 @@ $redis->lPop('key1'); /* key1 => [ 'B', 'C' ] */ _**Description**_: Adds the string value to the head (left) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. ##### *Parameters* -*key* +*key* *value* String, value to push in key ##### *Return value* @@ -1862,7 +1862,7 @@ $redis->lPush('key1', 'A'); // returns 3 _**Description**_: Adds the string value to the head (left) of the list if the list exists. ##### *Parameters* -*key* +*key* *value* String, value to push in key ##### *Return value* @@ -1880,13 +1880,13 @@ $redis->lPushx('key1', 'C'); // returns 3 ### lRange, lGetRange ----- -_**Description**_: Returns the specified elements of the list stored at the specified key in the range [start, end]. start and stop are interpretated as indices: -0 the first element, 1 the second ... +_**Description**_: Returns the specified elements of the list stored at the specified key in the range [start, end]. start and stop are interpreted as indices: +0 the first element, 1 the second ... -1 the last element, -2 the penultimate ... ##### *Parameters* -*key* -*start* +*key* +*start* *end* ##### *Return value* @@ -1902,17 +1902,17 @@ $redis->lRange('key1', 0, -1); /* array('A', 'B', 'C') */ ### lRem, lRemove ----- -_**Description**_: Removes the first `count` occurences of the value element from the list. If count is zero, all the matching elements are removed. If count is negative, elements are removed from tail to head. +_**Description**_: Removes the first `count` occurrences of the value element from the list. If count is zero, all the matching elements are removed. If count is negative, elements are removed from tail to head. **Note**: The argument order is not the same as in the Redis documentation. This difference is kept for compatibility reasons. ##### *Parameters* -*key* -*value* +*key* +*value* *count* ##### *Return value* -*LONG* the number of elements to remove +*LONG* the number of elements to remove *BOOL* `FALSE` if the value identified by key is not a list. ##### *Example* @@ -1933,12 +1933,12 @@ $redis->lRange('key1', 0, -1); /* array('C', 'B', 'A') */ _**Description**_: Set the list at index with the new value. ##### *Parameters* -*key* -*index* +*key* +*index* *value* ##### *Return value* -*BOOL* `TRUE` if the new value is setted. `FALSE` if the index is out of range, or data type identified by key is not a list. +*BOOL* `TRUE` if the new value was set. `FALSE` if the index is out of range, or data type identified by key is not a list. ##### *Example* ~~~ @@ -1955,12 +1955,12 @@ $redis->lGet('key1', 0); /* 'X' */ _**Description**_: Trims an existing list so that it will contain only a specified range of elements. ##### *Parameters* -*key* -*start* +*key* +*start* *stop* ##### *Return value* -*Array* +*Array* *Bool* return `FALSE` if the key identify a non-list value. ##### *Example* @@ -1981,7 +1981,7 @@ _**Description**_: Returns and removes the last element of the list. *key* ##### *Return value* -*STRING* if command executed successfully +*STRING* if command executed successfully *BOOL* `FALSE` in case of failure (empty list) ##### *Example* @@ -1997,7 +1997,7 @@ $redis->rPop('key1'); /* key1 => [ 'A', 'B' ] */ _**Description**_: Pops a value from the tail of a list, and pushes it to the front of another list. Also return this value. (redis >= 1.1) ##### *Parameters* -*Key*: srckey +*Key*: srckey *Key*: dstkey ##### *Return value* @@ -2040,7 +2040,7 @@ array(3) { _**Description**_: Adds the string value to the tail (right) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. ##### *Parameters* -*key* +*key* *value* String, value to push in key ##### *Return value* @@ -2060,7 +2060,7 @@ $redis->rPush('key1', 'C'); // returns 3 _**Description**_: Adds the string value to the tail (right) of the list if the ist exists. `FALSE` in case of Failure. ##### *Parameters* -*key* +*key* *value* String, value to push in key ##### *Return value* @@ -2086,7 +2086,7 @@ If the list didn't exist or is empty, the command returns 0. If the data type id *Key* ##### *Return value* -*LONG* The size of the list identified by Key exists. +*LONG* The size of the list identified by Key exists. *BOOL* `FALSE` if the data type identified by Key is not list ##### *Example* @@ -2122,7 +2122,7 @@ $redis->lSize('key1');/* 2 */ ----- _**Description**_: Adds a value to the set value stored at key. If this value is already in the set, `FALSE` is returned. ##### *Parameters* -*key* +*key* *value* ##### *Return value* @@ -2233,7 +2233,7 @@ key1, key2, keyN: keys identifying the different sets on which we will apply the ##### *Return value* -Array, contain the result of the intersection between those keys. If the intersection beteen the different sets is empty, the return value will be empty array. +Array, contain the result of the intersection between those keys. If the intersection between the different sets is empty, the return value will be empty array. ##### *Examples* ~~~ @@ -2307,7 +2307,7 @@ array(2) { ----- _**Description**_: Checks if `value` is a member of the set stored at the key `key`. ##### *Parameters* -*key* +*key* *value* ##### *Return value* @@ -2359,8 +2359,8 @@ The order is random and corresponds to redis' own internal representation of the ----- _**Description**_: Moves the specified member from the set at srcKey to the set at dstKey. ##### *Parameters* -*srcKey* -*dstKey* +*srcKey* +*dstKey* *member* ##### *Return value* *BOOL* If the operation is successful, return `TRUE`. If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, `FALSE` is returned. @@ -2380,13 +2380,13 @@ $redis->sMove('key1', 'key2', 'member13'); /* 'key1' => {'member11', 'member12' ----- _**Description**_: Removes and returns a random element from the set value at Key. ##### *Parameters* -*key* +*key* *count*: Integer, optional ##### *Return value (without count argument)* -*String* "popped" value +*String* "popped" value *Bool* `FALSE` if set identified by key is empty or doesn't exist. ##### *Return value (with count argument)* -*Array*: Member(s) returned or an empty array if the set doesn't exist +*Array*: Member(s) returned or an empty array if the set doesn't exist *Bool*: `FALSE` on error if the key is not a set ##### *Example* ~~~ @@ -2405,7 +2405,7 @@ $redis->sPop('key2', 3); /* Will return all members but in no particular order * ----- _**Description**_: Returns a random element from the set value at Key, without removing it. ##### *Parameters* -*key* +*key* *count* (Integer, optional) ##### *Return value* If no count is provided, a random *String* value from the set will be returned. If a count @@ -2434,7 +2434,7 @@ $redis->sRandMember('not-a-set', 100); // Will return FALSE ----- _**Description**_: Removes the specified member from the set value stored at key. ##### *Parameters* -*key* +*key* *member* ##### *Return value* *LONG* The number of elements removed from the set. @@ -2529,9 +2529,9 @@ array(4) { _**Description**_: Scan a set for members ##### *Parameters* -*Key*: The set to search -*iterator*: LONG (reference) to the iterator as we go -*pattern*: String, optional pattern to match against +*Key*: The set to search +*iterator*: LONG (reference) to the iterator as we go +*pattern*: String, optional pattern to match against *count*: How many members to return at a time (Redis might return a different amount) ##### *Return value* @@ -2569,7 +2569,7 @@ while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) { * [zInter](#zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key * [zRange](#zrange) - Return a range of members in a sorted set, by index * [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score -* [zRangeByLex](#zrangebylex) - Return a lexigraphical range from members that share the same score +* [zRangeByLex](#zrangebylex) - Return a lexicographical range from members that share the same score * [zRank, zRevRank](#zrank-zrevrank) - Determine the index of a member in a sorted set * [zRem, zDelete](#zrem-zdelete) - Remove one or more members from a sorted set * [zRemRangeByRank, zDeleteRangeByRank](#zremrangebyrank-zdeleterangebyrank) - Remove all members in a sorted set within the given indexes @@ -2585,7 +2585,7 @@ _**Description**_: Add one or more members to a sorted set or update its score i ##### *Parameters* *key* -*score* : double +*score*: double *value*: string ##### *Return value* @@ -2622,8 +2622,8 @@ $redis->zSize('key'); /* 3 */ _**Description**_: Returns the *number* of elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. ##### *Parameters* -*key* -*start*: string +*key* +*start*: string *end*: string ##### *Return value* @@ -2642,8 +2642,8 @@ $redis->zCount('key', 0, 3); /* 2, corresponding to array('val0', 'val2') */ _**Description**_: Increments the score of a member from a sorted set by a given amount. ##### *Parameters* -*key* -*value*: (double) value that will be added to the member's score +*key* +*value*: (double) value that will be added to the member's score *member* ##### *Return value* @@ -2661,13 +2661,13 @@ $redis->zIncrBy('key', 1, 'member1'); /* 3.5 */ ----- _**Description**_: Creates an intersection of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. -The third optionnel argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. +The third optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. ##### *Parameters* -*keyOutput* -*arrayZSetKeys* -*arrayWeights* +*keyOutput* +*arrayZSetKeys* +*arrayWeights* *aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zInter. ##### *Return value* @@ -2703,14 +2703,14 @@ $redis->zInter('ko4', array('k1', 'k2'), array(1, 5), 'max'); /* 2, 'ko4' => arr ----- _**Description**_: Returns a range of elements from the ordered set stored at the specified key, with values in the range [start, end]. -Start and stop are interpreted as zero-based indices: -0 the first element, 1 the second ... --1 the last element, -2 the penultimate ... +Start and stop are interpreted as zero-based indices: +`0` the first element, `1` the second ... +`-1` the last element, `-2` the penultimate ... ##### *Parameters* *key* -*start*: long -*end*: long +*start*: long +*end*: long *withscores*: bool = false ##### *Return value* @@ -2732,9 +2732,9 @@ $redis->zRange('key1', 0, -1, true); /* array('val0' => 0, 'val2' => 2, 'val10' _**Description**_: Returns the elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. zRevRangeByScore returns the same items in reverse order, when the `start` and `end` parameters are swapped. ##### *Parameters* -*key* -*start*: string -*end*: string +*key* +*start*: string +*end*: string *options*: array Two options are available: `withscores => TRUE`, and `limit => array($offset, $count)` @@ -2755,13 +2755,13 @@ $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array( ### zRangeByLex ----- -_**Description**_: Returns a lexigraphical range of members in a sorted set, assuming the members have the same score. The min and max values are required to start with '(' (exclusive), '[' (inclusive), or be exactly the values '-' (negative inf) or '+' (positive inf). The command must be called with either three *or* five arguments or will return FALSE. +_**Description**_: Returns a lexicographical range of members in a sorted set, assuming the members have the same score. The min and max values are required to start with '(' (exclusive), '[' (inclusive), or be exactly the values '-' (negative inf) or '+' (positive inf). The command must be called with either three *or* five arguments or will return FALSE. ##### *Parameters* -*key*: The ZSET you wish to run against -*min*: The minimum alphanumeric value you wish to get -*max*: The maximum alphanumeric value you wish to get -*offset*: Optional argument if you wish to start somewhere other than the first element. +*key*: The ZSET you wish to run against +*min*: The minimum alphanumeric value you wish to get +*max*: The maximum alphanumeric value you wish to get +*offset*: Optional argument if you wish to start somewhere other than the first element. *limit*: Optional argument if you wish to limit the number of elements returned. ##### *Return value* @@ -2782,7 +2782,7 @@ $redis->zRangeByLex('key','-','[c',1,2) /* Array('b','c') */ _**Description**_: Returns the rank of a given member in the specified sorted set, starting at 0 for the item with the smallest score. zRevRank starts at 0 for the item with the *largest* score. ##### *Parameters* -*key* +*key* *member* ##### *Return value* @@ -2804,7 +2804,7 @@ $redis->zRevRank('key', 'two'); /* 0 */ _**Description**_: Deletes a specified member from the ordered set. ##### *Parameters* -*key* +*key* *member* ##### *Return value* @@ -2824,8 +2824,8 @@ $redis->zRange('key', 0, -1); /* array('val0', 'val10') */ _**Description**_: Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end]. ##### *Parameters* -*key* -*start*: LONG +*key* +*start*: LONG *end*: LONG ##### *Return value* @@ -2845,8 +2845,8 @@ $redis->zRange('key', 0, -1, array('withscores' => TRUE)); /* array('three' => 3 _**Description**_: Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end]. ##### *Parameters* -*key* -*start*: double or "+inf" or "-inf" string +*key* +*start*: double or "+inf" or "-inf" string *end*: double or "+inf" or "-inf" string ##### *Return value* @@ -2862,14 +2862,14 @@ $redis->zRemRangeByScore('key', 0, 3); /* 2 */ ### zRevRange ----- -_**Description**_: Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpretated as zero-based indices: -0 the first element, 1 the second ... --1 the last element, -2 the penultimate ... +_**Description**_: Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpreted as zero-based indices: +`0` the first element, `1` the second ... +`-1` the last element, `-2` the penultimate ... ##### *Parameters* -*key* -*start*: long -*end*: long +*key* +*start*: long +*end*: long *withscores*: bool = false ##### *Return value* @@ -2891,7 +2891,7 @@ $redis->zRevRange('key', 0, -1, true); /* array('val10' => 10, 'val2' => 2, 'val _**Description**_: Returns the score of a given member in the specified sorted set. ##### *Parameters* -*key* +*key* *member* ##### *Return value* @@ -2907,13 +2907,13 @@ $redis->zScore('key', 'val2'); /* 2.5 */ ----- _**Description**_: Creates an union of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. -The third optionnel argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. +The third optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. ##### *Parameters* -*keyOutput* -*arrayZSetKeys* -*arrayWeights* +*keyOutput* +*arrayZSetKeys* +*arrayWeights* *aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zUnion. ##### *Return value* @@ -2946,9 +2946,9 @@ $redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val _**Description**_: Scan a sorted set for members, with optional pattern and count ##### *Parameters* -*key*: String, the set to scan -*iterator*: Long (reference), initialized to NULL -*pattern*: String (optional), the pattern to match +*key*: String, the set to scan +*iterator*: Long (reference), initialized to NULL +*pattern*: String (optional), the pattern to match *count*: How many keys to return per iteration (Redis might return a different number) ##### *Return value* @@ -2972,15 +2972,15 @@ while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { ##### *Prototype* ~~~ -$redis->geoAdd($key, $longitude, $latitude, $member [, $longitude, $lattitude, $member, ...]); +$redis->geoAdd($key, $longitude, $latitude, $member [, $longitude, $latitude, $member, ...]); ~~~ -_**Description**_: Add one or more geospacial items to the specified key. This function must be called with at least one _longitude, latitude, member_ triplet. +_**Description**_: Add one or more geospatial items to the specified key. This function must be called with at least one _longitude, latitude, member_ triplet. -##### *Return value* -*Integer*: The number of elements added to the geospacial key. +##### *Return value* +*Integer*: The number of elements added to the geospatial key. -##### *Example* +##### *Example* ~~~ $redis->del("myplaces"); @@ -2995,12 +2995,12 @@ $result = $redis->geoAdd( ### geoHash ----- -##### *Prototype* +##### *Prototype* ~~~ $redis->geoHash($key, $member [, $member, $member, ...]); ~~~ -_**Description**_: Retreive Geohash strings for one or more elements of a geospacial index. +_**Description**_: Retrieve Geohash strings for one or more elements of a geospatial index. ##### *Return value* *Array*: One or more Redis Geohash encoded strings. @@ -3030,7 +3030,7 @@ array(2) { $redis->geoPos($key, $member [, $member, $member, ...]); ~~~ -_**Description**_: Return longitude, lattitude positions for each requested member. +_**Description**_: Return longitude, latitude positions for each requested member. ##### *Return value* *Array*: One or more longitude/latitude positions @@ -3071,7 +3071,7 @@ $redis->geoDist($key, $member1, $member2 [, $unit]); ~~~ -_**Description**_: Return the distance between two members in a geospacial set. If units are passed it must be one of the following values: +_**Description**_: Return the distance between two members in a geospatial set. If units are passed it must be one of the following values: * 'm' => Meters * 'km' => Kilometers @@ -3121,7 +3121,7 @@ bool(false) $redis->geoRadius($key, $longitude, $latitude, $radius, $unit [, Array $options]); ~~~ -_**Description**_: Return members of a set with geospacial information that are within the radius specified by the caller. +_**Description**_: Return members of a set with geospatial information that are within the radius specified by the caller. ##### *Options Array* The georadius command can be called with various options that control how Redis returns results. The following table describes the options phpredis supports. All options are case insensitive. @@ -3135,7 +3135,7 @@ The georadius command can be called with various options that control how Redis | | ASC | Sort results in ascending order | | DESC | Sort results in descending order - *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will win! + *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will be used. *Note*: PhpRedis does not currently support the `STORE` or `STOREDIST` options but will be added to future versions. ##### *Return value* @@ -3220,7 +3220,7 @@ array(1) { $redis->geoRadiusByMember($key, $member, $radius, $units [, Array $options]); ~~~ -_**Description**_: This method is identical to [geoRadius](#georadius) except that instead of passing a longitude and latitude as the "source" you pass an existing member in the geospacial set. +_**Description**_: This method is identical to [geoRadius](#georadius) except that instead of passing a longitude and latitude as the "source" you pass an existing member in the geospatial set. ##### *Options Array* See [geoRadius](#georadius) command for options array. @@ -3286,8 +3286,8 @@ function pSubscribe($redis, $pattern, $chan, $msg) { _**Description**_: Publish messages to channels. Warning: this function will probably change in the future. ##### *Parameters* -*channel*: a channel to publish to -*messsage*: string +*channel*: a channel to publish to +*message*: string ##### *Example* ~~~ @@ -3299,8 +3299,8 @@ $redis->publish('chan-1', 'hello, world!'); // send message. _**Description**_: Subscribe to channels. Warning: this function will probably change in the future. ##### *Parameters* -*channels*: an array of channels to subscribe to -*callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. +*channels*: an array of channels to subscribe to +*callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~ @@ -3328,12 +3328,12 @@ $redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 c _**Description**_: A command allowing you to get information on the Redis pub/sub system. ##### *Parameters* -*keyword*: String, which can be: "channels", "numsub", or "numpat" +*keyword*: String, which can be: "channels", "numsub", or "numpat" *argument*: Optional, variant. For the "channels" subcommand, you can pass a string pattern. For "numsub" an array of channel names. ##### *Return value* -*CHANNELS*: Returns an array where the members are the matching channels. -*NUMSUB*: Returns a key/value array where the keys are channel names and values are their counts. +*CHANNELS*: Returns an array where the members are the matching channels. +*NUMSUB*: Returns a key/value array where the keys are channel names and values are their counts. *NUMPAT*: Integer return containing the number active pattern subscriptions ##### *Example* @@ -3446,8 +3446,8 @@ $ret = FALSE if x has been modified between the call to WATCH and the call to EX _**Description**_: Evaluate a LUA script serverside ##### *Parameters* -*script* string. -*args* array, optional. +*script* string. +*args* array, optional. *num_keys* int, optional. ##### *Return value* @@ -3475,8 +3475,8 @@ In order to run this command Redis will have to have already loaded the script, either by running it or via the SCRIPT LOAD command. ##### *Parameters* -*script_sha* string. The sha1 encoded hash of the script you want to run. -*args* array, optional. Arguments to pass to the LUA script. +*script_sha* string. The sha1 encoded hash of the script you want to run. +*args* array, optional. Arguments to pass to the LUA script. *num_keys* int, optional. The number of arguments that should go into the KEYS array, vs. the ARGV array when Redis spins the script ##### *Return value* @@ -3645,7 +3645,7 @@ None ### getHost ----- -_**Description**_: Retreive our host or unix socket that we're connected to +_**Description**_: Retrieve our host or unix socket that we're connected to ##### *Parameters* None From 91e9cfe1b5ada4de0e7d170071a090acb20625b5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 29 Nov 2017 17:49:32 +0200 Subject: [PATCH 0895/1986] Avoid connection in helper methods --- redis.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/redis.c b/redis.c index 4370297a96..c226a48d35 100644 --- a/redis.c +++ b/redis.c @@ -3011,7 +3011,7 @@ PHP_METHOD(Redis, migrate) { PHP_METHOD(Redis, _prefix) { RedisSock *redis_sock; - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3023,7 +3023,7 @@ PHP_METHOD(Redis, _serialize) { RedisSock *redis_sock; // Grab socket - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3035,7 +3035,7 @@ PHP_METHOD(Redis, _unserialize) { RedisSock *redis_sock; // Grab socket - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3056,7 +3056,7 @@ PHP_METHOD(Redis, getLastError) { } // Grab socket - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3079,7 +3079,7 @@ PHP_METHOD(Redis, clearLastError) { RETURN_FALSE; } // Grab socket - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } @@ -3105,7 +3105,7 @@ PHP_METHOD(Redis, getMode) { } /* Grab socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(object TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } From 5107d3c78af84cd2380de016f947ccb100da0e10 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 30 Nov 2017 16:20:49 +0200 Subject: [PATCH 0896/1986] TravisCI: php 7.2 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 899311ed58..7fd3fe7398 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ php: - 5.6 - 7.0 - 7.1 + - 7.2 - nightly env: CC=gcc matrix: @@ -24,6 +25,8 @@ matrix: env: CC=clang - php: 7.1 env: CC=clang + - php: 7.2 + env: CC=clang addons: apt: packages: clang From 1e63717a7d7562433ca10d14620bdd1c9431a5d7 Mon Sep 17 00:00:00 2001 From: chenjian Date: Thu, 7 Dec 2017 01:58:49 +0800 Subject: [PATCH 0897/1986] the element of z_seeds may be a reference on php7 --- cluster_library.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cluster_library.c b/cluster_library.c index ca20581812..8479d88a4f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -894,6 +894,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { for (i = 0; i < count; i++) { z_seed = z_seeds[i]; + ZVAL_DEREF(z_seed); /* Has to be a string */ if (z_seed == NULL || Z_TYPE_P(z_seed) != IS_STRING) continue; From 367bc6aac9321f564ecd7ad3462ed3fa5a17896e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 6 Dec 2017 22:32:54 +0200 Subject: [PATCH 0898/1986] Prepare to merge #1280 --- cluster_library.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index ca20581812..da2a0e1d8d 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -892,11 +892,10 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { // Iterate our seeds array for (i = 0; i < count; i++) { - z_seed = z_seeds[i]; + if ((z_seed = z_seeds[i]) == NULL) continue; /* Has to be a string */ - if (z_seed == NULL || Z_TYPE_P(z_seed) != IS_STRING) - continue; + if (Z_TYPE_P(z_seed) != IS_STRING) continue; // Grab a copy of the string str = Z_STRVAL_P(z_seed); From 4b8336f7374d91ca5d30c0bf47951bd9ad44e08d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 8 Dec 2017 11:36:11 +0200 Subject: [PATCH 0899/1986] Use zend_string for storing auth and prefix members --- redis_session.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/redis_session.c b/redis_session.c index a704f4f501..0320147446 100644 --- a/redis_session.c +++ b/redis_session.c @@ -53,13 +53,8 @@ typedef struct redis_pool_member_ { RedisSock *redis_sock; int weight; int database; - - char *prefix; - size_t prefix_len; - - char *auth; - size_t auth_len; - + zend_string *prefix; + zend_string *auth; struct redis_pool_member_ *next; } redis_pool_member; @@ -80,7 +75,7 @@ redis_pool_new(TSRMLS_D) { PHP_REDIS_API void redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, - int database, char *prefix, char *auth TSRMLS_DC) { + int database, zend_string *prefix, zend_string *auth TSRMLS_DC) { redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); rpm->redis_sock = redis_sock; @@ -88,10 +83,8 @@ redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, rpm->database = database; rpm->prefix = prefix; - rpm->prefix_len = (prefix?strlen(prefix):0); rpm->auth = auth; - rpm->auth_len = (auth?strlen(auth):0); rpm->next = pool->head; pool->head = rpm; @@ -108,8 +101,8 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { next = rpm->next; redis_sock_disconnect(rpm->redis_sock TSRMLS_CC); redis_free_socket(rpm->redis_sock); - if(rpm->prefix) efree(rpm->prefix); - if(rpm->auth) efree(rpm->auth); + if(rpm->prefix) zend_string_release(rpm->prefix); + if(rpm->auth) zend_string_release(rpm->auth); efree(rpm); rpm = next; } @@ -123,11 +116,11 @@ redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { int response_len, cmd_len; /* Short circuit if we don't have a password */ - if(!rpm->auth || !rpm->auth_len) { + if (!rpm->auth) { return; } - cmd_len = REDIS_SPPRINTF(&cmd, "AUTH", "s", rpm->auth, rpm->auth_len); + cmd_len = REDIS_SPPRINTF(&cmd, "AUTH", "S", rpm->auth); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { efree(response); @@ -163,7 +156,7 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { for(i = 0; i < pool->totalWeight;) { if(pos >= i && pos < i + rpm->weight) { int needs_auth = 0; - if(rpm->auth && rpm->auth_len && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { + if (rpm->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } redis_sock_server_open(rpm->redis_sock TSRMLS_CC); @@ -208,7 +201,8 @@ PS_OPEN_FUNC(redis) double timeout = 86400.0, read_timeout = 0.0; int persistent = 0; int database = -1; - char *prefix = NULL, *auth = NULL, *persistent_id = NULL; + char *persistent_id = NULL; + zend_string *prefix = NULL, *auth = NULL; long retry_interval = 0; /* translate unix: into file: */ @@ -255,10 +249,10 @@ PS_OPEN_FUNC(redis) persistent_id = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); } if ((param = zend_hash_str_find(Z_ARRVAL(params), "prefix", sizeof("prefix") - 1)) != NULL) { - prefix = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); + prefix = zend_string_init(Z_STRVAL_P(param), Z_STRLEN_P(param), 0); } if ((param = zend_hash_str_find(Z_ARRVAL(params), "auth", sizeof("auth") - 1)) != NULL) { - auth = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param)); + auth = zend_string_init(Z_STRVAL_P(param), Z_STRLEN_P(param), 0); } if ((param = zend_hash_str_find(Z_ARRVAL(params), "database", sizeof("database") - 1)) != NULL) { database = zval_get_long(param); @@ -273,8 +267,8 @@ PS_OPEN_FUNC(redis) if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { php_url_free(url); if (persistent_id) efree(persistent_id); - if (prefix) efree(prefix); - if (auth) efree(auth); + if (prefix) zend_string_release(prefix); + if (auth) zend_string_release(auth); redis_pool_free(pool TSRMLS_CC); PS_SET_MOD_DATA(NULL); return FAILURE; @@ -324,8 +318,8 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses size_t prefix_len = sizeof(default_prefix)-1; if(rpm->prefix) { - prefix = rpm->prefix; - prefix_len = rpm->prefix_len; + prefix = ZSTR_VAL(rpm->prefix); + prefix_len = ZSTR_LEN(rpm->prefix); } /* build session key */ From 485db46fecea5683a83c09fad2f3adbd89d84e1d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 8 Dec 2017 13:42:50 +0200 Subject: [PATCH 0900/1986] Issue #88 Disallow using empty string as session name. --- redis_session.c | 60 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/redis_session.c b/redis_session.c index 0320147446..60d5170ae5 100644 --- a/redis_session.c +++ b/redis_session.c @@ -337,24 +337,25 @@ PS_READ_FUNC(redis) { char *resp, *cmd; int resp_len, cmd_len; - - redis_pool *pool = PS_GET_MOD_DATA(); #if (PHP_MAJOR_VERSION < 7) - redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); + const char *skey = key; + size_t skeylen = strlen(key); #else - redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(key) TSRMLS_CC); + const char *skey = ZSTR_VAL(key); + size_t skeylen = ZSTR_LEN(key); #endif + + if (!skeylen) return FAILURE; + + redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ return FAILURE; } /* send GET command */ -#if (PHP_MAJOR_VERSION < 7) - resp = redis_session_key(rpm, key, strlen(key), &resp_len); -#else - resp = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &resp_len); -#endif + resp = redis_session_key(rpm, skey, skeylen, &resp_len); cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); efree(resp); @@ -397,29 +398,27 @@ PS_WRITE_FUNC(redis) { char *cmd, *response, *session; int cmd_len, response_len, session_len; - - redis_pool *pool = PS_GET_MOD_DATA(); #if (PHP_MAJOR_VERSION < 7) - redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); + const char *skey = key, *sval = val; + size_t skeylen = strlen(key), svallen = vallen; #else - redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(key) TSRMLS_CC); + const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); + size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); #endif + + if (!skeylen) return FAILURE; + + redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ return FAILURE; } /* send SET command */ -#if (PHP_MAJOR_VERSION < 7) - session = redis_session_key(rpm, key, strlen(key), &session_len); - cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, - INI_INT("session.gc_maxlifetime"), val, vallen); -#else - session = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &session_len); + session = redis_session_key(rpm, skey, skeylen, &session_len); cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, - INI_INT("session.gc_maxlifetime"), - ZSTR_VAL(val), ZSTR_LEN(val)); -#endif + INI_INT("session.gc_maxlifetime"), sval, svallen); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -448,24 +447,23 @@ PS_DESTROY_FUNC(redis) { char *cmd, *response, *session; int cmd_len, response_len, session_len; - - redis_pool *pool = PS_GET_MOD_DATA(); #if (PHP_MAJOR_VERSION < 7) - redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC); + const char *skey = key; + size_t skeylen = strlen(key); #else - redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(key) TSRMLS_CC); + const char *skey = ZSTR_VAL(key); + size_t skeylen = ZSTR_LEN(key); #endif + + redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if(!rpm || !redis_sock){ return FAILURE; } /* send DEL command */ -#if (PHP_MAJOR_VERSION < 7) - session = redis_session_key(rpm, key, strlen(key), &session_len); -#else - session = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &session_len); -#endif + session = redis_session_key(rpm, skey, skeylen, &session_len); cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "s", session, session_len); efree(session); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { From a99c450e8201642376006aebb1cdf5989e472f87 Mon Sep 17 00:00:00 2001 From: ndxbn Date: Fri, 15 Dec 2017 21:39:29 +0900 Subject: [PATCH 0901/1986] add test for zRangeByLex method --- tests/RedisTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 1a1044893c..28b01adc71 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2288,6 +2288,20 @@ public function testZX() { $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); } + public function testZRangeByLex() { + $this->redis->del('key'); + foreach(range('a', 'g') as $c) { + $this->redis->zAdd('key', 0, $c); + } + + $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c'), Array('a', 'b', 'c')); + $this->assertEquals($this->redis->zRangeByLex('key', '(e', '+'), Array('f', 'g')); + + // with limit offset + $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c', 1, 2), Array('b', 'c') ); + $this->assertEquals($this->redis->zRangeByLex('key', '-', '(c', 1, 2), Array('b')); + } + public function testHashes() { $this->redis->del('h', 'key'); $this->assertTrue(0 === $this->redis->hLen('h')); From 81f44dcc00581331a36bce598463f8a1ea846094 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 15 Dec 2017 08:13:57 -0800 Subject: [PATCH 0902/1986] Skip ZRANGEBYLEX check if use is running Redis < 2.8.9 --- tests/RedisTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 28b01adc71..f2ec30154e 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2289,6 +2289,12 @@ public function testZX() { } public function testZRangeByLex() { + /* ZRANGEBYLEX available on versions >= 2.8.9 */ + if(version_compare($this->version, "2.8.9", "lt")) { + $this->MarkTestSkipped(); + return; + } + $this->redis->del('key'); foreach(range('a', 'g') as $c) { $this->redis->zAdd('key', 0, $c); From b566fb44efec4450d3ae53802ddd6ea17c70f27b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 20 Dec 2017 17:05:22 +0200 Subject: [PATCH 0903/1986] PR #1283. Thanks @fmk! PHP >=7.3.0 uses zend_string to store `php_url` elements. --- redis_session.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/redis_session.c b/redis_session.c index 60d5170ae5..86004b7f4c 100644 --- a/redis_session.c +++ b/redis_session.c @@ -231,7 +231,11 @@ PS_OPEN_FUNC(redis) if (url->query != NULL) { array_init(¶ms); +#if (PHP_VERSION_ID < 70300) sapi_module.treat_data(PARSE_STRING, estrdup(url->query), ¶ms TSRMLS_CC); +#else + sapi_module.treat_data(PARSE_STRING, estrndup(ZSTR_VAL(url->query), ZSTR_LEN(url->query)), ¶ms TSRMLS_CC); +#endif if ((param = zend_hash_str_find(Z_ARRVAL(params), "weight", sizeof("weight") - 1)) != NULL) { weight = zval_get_long(param); @@ -276,9 +280,17 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if(url->host) { +#if (PHP_VERSION_ID < 70300) redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); +#else + redis_sock = redis_sock_create(ZSTR_VAL(url->host), ZSTR_LEN(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); +#endif } else { /* unix */ +#if (PHP_VERSION_ID < 70300) redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); +#else + redis_sock = redis_sock_create(ZSTR_VAL(url->path), ZSTR_LEN(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); +#endif } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); From 4530944ed0df888c1e7e8fefeca50cedba726b6b Mon Sep 17 00:00:00 2001 From: Andrew Sharpe Date: Sun, 24 Dec 2017 15:28:41 +1000 Subject: [PATCH 0904/1986] Allow session locking to work with session_regenerate_id (see #1267) --- redis_session.c | 51 +++++++++++++++++++++++++++++- redis_session.h | 1 + tests/RedisTest.php | 58 +++++++++++++++++++++++++++++++++++ tests/regenerateSessionId.php | 31 +++++++++++++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 tests/regenerateSessionId.php diff --git a/redis_session.c b/redis_session.c index 1ad5b49aae..db6c3c6bc6 100644 --- a/redis_session.c +++ b/redis_session.c @@ -62,7 +62,7 @@ #define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3)) ps_module ps_mod_redis = { - PS_MOD(redis) + PS_MOD_SID(redis) }; ps_module ps_mod_redis_cluster = { PS_MOD(rediscluster) @@ -581,6 +581,55 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses return session; } +/* {{{ PS_CREATE_SID_FUNC + */ +PS_CREATE_SID_FUNC(redis) +{ + char *sid; + int retries = 3; + + redis_pool *pool = PS_GET_MOD_DATA(); + + while (retries-- > 0) { +#if (PHP_MAJOR_VERSION < 7) + sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, sid TSRMLS_CC); +#else + sid = php_session_create_id((void **) &pool TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid) TSRMLS_CC); +#endif + RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; + + if (!rpm || !redis_sock) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "Redis not available while creating session_id"); + + efree(sid); +#if (PHP_MAJOR_VERSION < 7) + return php_session_create_id(NULL, newlen TSRMLS_CC); +#else + return php_session_create_id(NULL TSRMLS_CC); +#endif + } + + pool->lock_status->session_key = sid; + if (lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) == SUCCESS) { + break; + } + + efree(sid); + sid = NULL; + } + + if (sid == NULL) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "Acquiring session lock failed while creating session_id"); + } + + return sid; +} +/* }}} */ + /* {{{ PS_READ_FUNC */ PS_READ_FUNC(redis) diff --git a/redis_session.h b/redis_session.h index 02cc6c2ff3..9ab3e5f1b2 100644 --- a/redis_session.h +++ b/redis_session.h @@ -9,6 +9,7 @@ PS_READ_FUNC(redis); PS_WRITE_FUNC(redis); PS_DESTROY_FUNC(redis); PS_GC_FUNC(redis); +PS_CREATE_SID_FUNC(redis); PS_OPEN_FUNC(rediscluster); PS_CLOSE_FUNC(rediscluster); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index a675092c1e..9fd8a46c12 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5288,6 +5288,46 @@ public function testMultipleConnect() { } } + public function testSession_regenerateSessionId_noLock_noDestroy() { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + + $newSessionId = $this->regenerateSessionId($sessionId); + + $this->assertEquals('bar', $this->getSessionData($newSessionId)); + } + + public function testSession_regenerateSessionId_noLock_withDestroy() { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + + $newSessionId = $this->regenerateSessionId($sessionId, false, true); + + $this->assertEquals('bar', $this->getSessionData($newSessionId)); + } + + public function testSession_regenerateSessionId_withLock_noDestroy() { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + + $newSessionId = $this->regenerateSessionId($sessionId, true); + + $this->assertEquals('bar', $this->getSessionData($newSessionId)); + } + + public function testSession_regenerateSessionId_withLock_withDestroy() { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + + $newSessionId = $this->regenerateSessionId($sessionId, true, true); + + $this->assertEquals('bar', $this->getSessionData($newSessionId)); + } + private function setSessionHandler() { $host = $this->getHost() ?: 'localhost'; @@ -5358,5 +5398,23 @@ private function getSessionData($sessionId) return $output[0]; } + + /** + * @param string $sessionId + * + * @return string + */ + private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false) + { + $args = array_map('escapeshellarg', array($sessionId, $locking, $destroyPrevious)); + + $command = 'php --no-php-ini --define extension=igbinary.so --define extension=' . __DIR__ . '/../modules/redis.so ' . __DIR__ . '/regenerateSessionId.php ' . escapeshellarg($this->getHost()) . ' ' . implode(' ', $args); + + $command .= ' 2>&1'; + + exec($command, $output); + + return $output[0]; + } } ?> diff --git a/tests/regenerateSessionId.php b/tests/regenerateSessionId.php new file mode 100644 index 0000000000..c232c8c795 --- /dev/null +++ b/tests/regenerateSessionId.php @@ -0,0 +1,31 @@ + Date: Sun, 24 Dec 2017 16:46:41 +1000 Subject: [PATCH 0905/1986] isolate tests to find timing issue --- tests/RedisTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 9fd8a46c12..6b8858c738 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5274,7 +5274,8 @@ public function testSession_lockWaitTime() $end = microtime(true); $elapsedTime = $end - $start; - $this->assertTrue($elapsedTime > 2.5 && $elapsedTime < 3.5); + $this->assertTrue($elapsedTime > 2.5); + $this->assertTrue($elapsedTime < 3.5); $this->assertTrue($sessionSuccessful); } From eb435546d89c4fdebd578a69a69a9001ac254f9b Mon Sep 17 00:00:00 2001 From: Andrew Sharpe Date: Sun, 24 Dec 2017 16:58:17 +1000 Subject: [PATCH 0906/1986] use correct types for php 7 --- redis_session.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/redis_session.c b/redis_session.c index db6c3c6bc6..4ce3a92ef6 100644 --- a/redis_session.c +++ b/redis_session.c @@ -585,17 +585,16 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses */ PS_CREATE_SID_FUNC(redis) { - char *sid; int retries = 3; redis_pool *pool = PS_GET_MOD_DATA(); while (retries-- > 0) { #if (PHP_MAJOR_VERSION < 7) - sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); + char *sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, sid TSRMLS_CC); #else - sid = php_session_create_id((void **) &pool TSRMLS_CC); + zend_string *sid = php_session_create_id((void **) &pool TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid) TSRMLS_CC); #endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; From 02f4bd318bd9f39b11fb1af07a96fbe5a8591884 Mon Sep 17 00:00:00 2001 From: Andrew Sharpe Date: Sun, 24 Dec 2017 17:15:14 +1000 Subject: [PATCH 0907/1986] declare sid in the right scope --- redis_session.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index 4ce3a92ef6..e67a16900b 100644 --- a/redis_session.c +++ b/redis_session.c @@ -586,15 +586,20 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses PS_CREATE_SID_FUNC(redis) { int retries = 3; +#if (PHP_MAJOR_VERSION < 7) + char *sid; +#else + zend_string *sid; +#endif redis_pool *pool = PS_GET_MOD_DATA(); while (retries-- > 0) { #if (PHP_MAJOR_VERSION < 7) - char *sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); + sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, sid TSRMLS_CC); #else - zend_string *sid = php_session_create_id((void **) &pool TSRMLS_CC); + sid = php_session_create_id((void **) &pool TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid) TSRMLS_CC); #endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; From a2f9aae206c83c28d50548145cdcccc12dc67fe8 Mon Sep 17 00:00:00 2001 From: Andrew Sharpe Date: Tue, 26 Dec 2017 08:01:28 +1000 Subject: [PATCH 0908/1986] fixes for PHP 7 --- redis_session.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/redis_session.c b/redis_session.c index e67a16900b..1bc3449a79 100644 --- a/redis_session.c +++ b/redis_session.c @@ -586,20 +586,22 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses PS_CREATE_SID_FUNC(redis) { int retries = 3; + redis_pool *pool = PS_GET_MOD_DATA(); + + if (!pool) { #if (PHP_MAJOR_VERSION < 7) - char *sid; + return php_session_create_id(NULL, newlen TSRMLS_CC); #else - zend_string *sid; + return php_session_create_id(NULL TSRMLS_CC); #endif - - redis_pool *pool = PS_GET_MOD_DATA(); + } while (retries-- > 0) { #if (PHP_MAJOR_VERSION < 7) - sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); + char* sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, sid TSRMLS_CC); #else - sid = php_session_create_id((void **) &pool TSRMLS_CC); + zend_string sid = php_session_create_id((void **) &pool TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid) TSRMLS_CC); #endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; @@ -608,20 +610,29 @@ PS_CREATE_SID_FUNC(redis) php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Redis not available while creating session_id"); - efree(sid); #if (PHP_MAJOR_VERSION < 7) + efree(sid); return php_session_create_id(NULL, newlen TSRMLS_CC); #else + zend_string_release(sid); return php_session_create_id(NULL TSRMLS_CC); #endif } +#if (PHP_MAJOR_VERSION < 7) pool->lock_status->session_key = sid; +#else + pool->lock_status->session_key = estrdup($TR_VAL(sid)); +#endif if (lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) == SUCCESS) { - break; + return sid; } +#if (PHP_MAJOR_VERSION < 7) efree(sid); +#else + zend_string_release(sid); +#endif sid = NULL; } @@ -630,7 +641,7 @@ PS_CREATE_SID_FUNC(redis) "Acquiring session lock failed while creating session_id"); } - return sid; + return NULL; } /* }}} */ From 70d8c1962d0d136beaf00d572b452e05f0c8ee5f Mon Sep 17 00:00:00 2001 From: Andrew Sharpe Date: Tue, 26 Dec 2017 08:11:43 +1000 Subject: [PATCH 0909/1986] don't refer to sid when it's undefined --- redis_session.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/redis_session.c b/redis_session.c index 1bc3449a79..b7b3206dd4 100644 --- a/redis_session.c +++ b/redis_session.c @@ -636,10 +636,8 @@ PS_CREATE_SID_FUNC(redis) sid = NULL; } - if (sid == NULL) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, - "Acquiring session lock failed while creating session_id"); - } + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "Acquiring session lock failed while creating session_id"); return NULL; } From a5fab9dbd7e504002bb146d3108bc963b02fcab3 Mon Sep 17 00:00:00 2001 From: Andrew Sharpe Date: Tue, 26 Dec 2017 08:38:34 +1000 Subject: [PATCH 0910/1986] correct type for php_session_create_id --- redis_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index b7b3206dd4..01d386ad0b 100644 --- a/redis_session.c +++ b/redis_session.c @@ -601,7 +601,7 @@ PS_CREATE_SID_FUNC(redis) char* sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, sid TSRMLS_CC); #else - zend_string sid = php_session_create_id((void **) &pool TSRMLS_CC); + zend_string* sid = php_session_create_id((void **) &pool TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid) TSRMLS_CC); #endif RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; From 2d86a5944531e9088bf7407cf8a88021e679607c Mon Sep 17 00:00:00 2001 From: Andrew Sharpe Date: Tue, 26 Dec 2017 09:06:13 +1000 Subject: [PATCH 0911/1986] typo --- redis_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index 01d386ad0b..5abda35afb 100644 --- a/redis_session.c +++ b/redis_session.c @@ -622,7 +622,7 @@ PS_CREATE_SID_FUNC(redis) #if (PHP_MAJOR_VERSION < 7) pool->lock_status->session_key = sid; #else - pool->lock_status->session_key = estrdup($TR_VAL(sid)); + pool->lock_status->session_key = estrdup(ZSTR_VAL(sid)); #endif if (lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) == SUCCESS) { return sid; From 837dee471ccac86581d306594ee945e82027572f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 2 Jan 2018 13:35:08 +0200 Subject: [PATCH 0912/1986] Issue #1292 Revert broken in c9df77d RA hashing impl. --- redis_array_impl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 1a5aa7bac2..fbaa6bbd05 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -471,6 +471,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D return NULL; } } else { + uint64_t h64; unsigned long ret = 0xffffffff; size_t i; @@ -481,7 +482,10 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D hash = (ret ^ 0xffffffff); /* get position on ring */ - pos = (int)(hash * ra->count / 0xffffffff); + h64 = hash; + h64 *= ra->count; + h64 /= 0xffffffff; + pos = (int)h64; } efree(out); From 5b9c0c60618db70cd42e0e24d58bce74471916a6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 5 Jan 2018 17:12:50 +0200 Subject: [PATCH 0913/1986] Issue #1287 --- library.c | 16 +++++++++++++++- redis.c | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index 0358af8a14..d6314dff52 100644 --- a/library.c +++ b/library.c @@ -1420,6 +1420,11 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) int host_len, usocket = 0, err = 0; php_netstream_data_t *sock; int tcp_flag = 1; +#if (PHP_MAJOR_VERSION < 7) + char *estr = NULL; +#else + zend_string *estr = NULL; +#endif if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock TSRMLS_CC); @@ -1463,13 +1468,22 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock->stream = php_stream_xport_create(host, host_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, NULL, &err); + persistent_id, tv_ptr, NULL, &estr, &err); if (persistent_id) { efree(persistent_id); } if (!redis_sock->stream) { + if (estr) { +#if (PHP_MAJOR_VERSION < 7) + redis_sock_set_err(redis_sock, estr, strlen(estr)); + efree(estr); +#else + redis_sock_set_err(redis_sock, ZSTR_VAL(estr), ZSTR_LEN(estr)); + zend_string_release(estr); +#endif + } return -1; } diff --git a/redis.c b/redis.c index c226a48d35..c56b1985b0 100644 --- a/redis.c +++ b/redis.c @@ -943,6 +943,9 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) persistent_id, retry_interval, 0); if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { + if (redis->sock->err) { + zend_throw_exception(redis_exception_ce, ZSTR_VAL(redis->sock->err), 0 TSRMLS_CC); + } redis_free_socket(redis->sock); redis->sock = NULL; return FAILURE; From 4090af15b8cfe87da12ed60b644b3cf7739bbfe0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 11 Jan 2018 19:29:14 -0800 Subject: [PATCH 0914/1986] Adds a test for throwing a connect exception --- tests/RedisTest.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index f2ec30154e..c16335e1ee 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5142,5 +5142,39 @@ public function testMultipleConnect() { $this->assertEquals($this->redis->ping(), "+PONG"); } } + + /* If we're running tests against localhost attempt to find a high port (16379 - 16383) with + * nothing listening and then trying to connect. We should get an exception and + * 'connection refused' message. */ + public function testConnectException() { + /* Skip this test if we're not running against localhost */ + if ($this->redis->GetHost() != 'localhost' && $this->redis->GetHost() != '127.0.0.1') { + $this->MarkTestSkipped(); + return; + } + + $port = $this->redis->GetPort(); + for ($p = $port + 10000; $p < $port + 10005; $p++) { + $con = @fsockopen($this->redis->GetHost(), $p); + if ( ! is_resource($con)) { + $obj_r = new Redis(); + + try { + if ( ! $obj_r->connect($this->redis->getHost(), $p)) { + /* Trigger an error as we shouldn't be here */ + $this->assertFalse(true); + } + } catch (Exception $ex) { + $msg = strtolower($ex->getMessage()); + $this->assertTrue(strpos($msg, "refused") !== false); + } + + return; + } + } + + /* If we got here we didn't find a vacant port, so just mark skipped */ + $this->MarkTestSkipped(); + } } ?> From a0dc22220cfc49b28f56a6799f4138370b477fc5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 11 Jan 2018 19:33:01 -0800 Subject: [PATCH 0915/1986] Actually commit the cluster code to skip the exception test --- tests/RedisClusterTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index d03d3591dd..ff45dfc72c 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -34,6 +34,7 @@ public function testSelect() { return $this->markTestSkipped(); } public function testReconnectSelect() { return $this->markTestSkipped(); } public function testMultipleConnect() { return $this->markTestSkipped(); } public function testDoublePipeNoOp() { return $this->markTestSkipped(); } + public function testConnectException() { return $this->markTestSkipped(); } /* Load our seeds on construction */ public function __construct() { From 815d2013b4bbd2509fbfe60e7d68b074e02b08b3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 12 Jan 2018 09:01:06 -0800 Subject: [PATCH 0916/1986] Much simpler exception test --- tests/RedisTest.php | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index c16335e1ee..86fa9635d2 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5143,38 +5143,13 @@ public function testMultipleConnect() { } } - /* If we're running tests against localhost attempt to find a high port (16379 - 16383) with - * nothing listening and then trying to connect. We should get an exception and - * 'connection refused' message. */ public function testConnectException() { - /* Skip this test if we're not running against localhost */ - if ($this->redis->GetHost() != 'localhost' && $this->redis->GetHost() != '127.0.0.1') { - $this->MarkTestSkipped(); - return; - } - - $port = $this->redis->GetPort(); - for ($p = $port + 10000; $p < $port + 10005; $p++) { - $con = @fsockopen($this->redis->GetHost(), $p); - if ( ! is_resource($con)) { - $obj_r = new Redis(); - - try { - if ( ! $obj_r->connect($this->redis->getHost(), $p)) { - /* Trigger an error as we shouldn't be here */ - $this->assertFalse(true); - } - } catch (Exception $ex) { - $msg = strtolower($ex->getMessage()); - $this->assertTrue(strpos($msg, "refused") !== false); - } - - return; - } + $redis = new Redis(); + try { + $redis->connect('github.com', 6379, 0.01); + } catch (Exception $e) { + $this->assertTrue(strpos($e, "timed out") !== false); } - - /* If we got here we didn't find a vacant port, so just mark skipped */ - $this->MarkTestSkipped(); } } ?> From 9e65c429318d93bd37c9d42abf79709395e6e805 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jan 2018 09:36:38 -0800 Subject: [PATCH 0917/1986] Implement UNLINK command This commit implements UNLINK for Redis, RedisCluster, and RedisArray. To a client library UNLINK behaves identically to DEL so we can use the same handlers for both. --- php_redis.h | 1 + redis.c | 9 ++++++ redis_array.c | 33 +++++++++++++------- redis_array.h | 1 + redis_cluster.c | 24 +++++++++++---- redis_cluster.h | 1 + redis_commands.c | 8 +++++ redis_commands.h | 3 ++ tests/RedisArrayTest.php | 55 ++++++++++++++++++++++++++++++++-- tests/RedisTest.php | 65 +++++++++++++++++++++++----------------- 10 files changed, 154 insertions(+), 46 deletions(-) diff --git a/php_redis.h b/php_redis.h index 895ad710f7..d1989cfc93 100644 --- a/php_redis.h +++ b/php_redis.h @@ -46,6 +46,7 @@ PHP_METHOD(Redis, renameNx); PHP_METHOD(Redis, getMultiple); PHP_METHOD(Redis, exists); PHP_METHOD(Redis, delete); +PHP_METHOD(Redis, unlink); PHP_METHOD(Redis, incr); PHP_METHOD(Redis, incrBy); PHP_METHOD(Redis, incrByFloat); diff --git a/redis.c b/redis.c index c226a48d35..15f32f6dad 100644 --- a/redis.c +++ b/redis.c @@ -390,6 +390,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, unlink, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC) PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC) @@ -1176,6 +1177,13 @@ PHP_METHOD(Redis, delete) } /* }}} */ +/* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}} + * {{{ proto long Redis::unlink(array $keys) */ +PHP_METHOD(Redis, unlink) + + REDIS_PROCESS_CMD(unlink, redis_long_response); +} + PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock) { redis_sock->watching = 1; @@ -1512,6 +1520,7 @@ PHP_METHOD(Redis, sDiffStore) { } /* }}} */ + /* {{{ proto array Redis::sort(string key, array options) */ PHP_METHOD(Redis, sort) { char *cmd; diff --git a/redis_array.c b/redis_array.c index 01a48e5fcf..4d70b50a16 100644 --- a/redis_array.c +++ b/redis_array.c @@ -122,6 +122,7 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, save, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, select, arginfo_select, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, setOption,arginfo_setopt, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, unlink, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, unwatch, arginfo_void, ZEND_ACC_PUBLIC) PHP_MALIAS(RedisArray, delete, del, arginfo_del, ZEND_ACC_PUBLIC) PHP_MALIAS(RedisArray, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC) @@ -849,7 +850,7 @@ PHP_METHOD(RedisArray, select) zval_dtor(&z_fun); } #if (PHP_MAJOR_VERSION < 7) -#define HANDLE_MULTI_EXEC(ra, cmd) do { \ +#define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \ if (ra && ra->z_multi_exec) { \ int i, num_varargs;\ zval ***varargs = NULL, *z_arg_array; \ @@ -867,7 +868,7 @@ PHP_METHOD(RedisArray, select) add_next_index_zval(z_arg_array, z_tmp); \ }\ /* call */\ - ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, z_arg_array, NULL); \ + ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, z_arg_array, NULL); \ zval_ptr_dtor(&z_arg_array); \ if(varargs) {\ efree(varargs);\ @@ -892,7 +893,7 @@ PHP_METHOD(RedisArray, select) add_next_index_zval(&z_arg_array, &z_tmp); \ } \ /* call */\ - ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, &z_arg_array, NULL); \ + ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, &z_arg_array, NULL); \ zval_dtor(&z_arg_array); \ return; \ } \ @@ -914,7 +915,7 @@ PHP_METHOD(RedisArray, mget) } /* Multi/exec support */ - HANDLE_MULTI_EXEC(ra, "MGET"); + HANDLE_MULTI_EXEC(ra, "MGET", sizeof("MGET") - 1); if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_array_ce, &z_keys) == FAILURE) { @@ -1068,7 +1069,7 @@ PHP_METHOD(RedisArray, mset) } /* Multi/exec support */ - HANDLE_MULTI_EXEC(ra, "MSET"); + HANDLE_MULTI_EXEC(ra, "MSET", sizeof("MSET") - 1); if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_array_ce, &z_keys) == FAILURE) @@ -1179,9 +1180,8 @@ PHP_METHOD(RedisArray, mset) RETURN_TRUE; } -/* DEL will distribute the call to several nodes and regroup the values. */ -PHP_METHOD(RedisArray, del) -{ +/* Generic handler for DEL or UNLINK which behave identically to phpredis */ +static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { zval *object, z_keys, z_fun, *data, z_ret, *z_tmp, *z_args; int i, n; RedisArray *ra; @@ -1194,8 +1194,9 @@ PHP_METHOD(RedisArray, del) if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { RETURN_FALSE; } + /* Multi/exec support */ - HANDLE_MULTI_EXEC(ra, "DEL"); + HANDLE_MULTI_EXEC(ra, kw, kw_len); /* get all args in z_args */ z_args = emalloc(argc * sizeof(zval)); @@ -1259,7 +1260,7 @@ PHP_METHOD(RedisArray, del) } ZEND_HASH_FOREACH_END(); /* prepare call */ - ZVAL_STRINGL(&z_fun, "DEL", 3); + ZVAL_STRINGL(&z_fun, kw, kw_len); /* calls */ for(n = 0; n < ra->count; ++n) { /* for each node */ @@ -1285,7 +1286,7 @@ PHP_METHOD(RedisArray, del) found++; } - if(!found) { /* don't run empty DELs */ + if(!found) { /* don't run empty DEL or UNLINK commands */ zval_dtor(&z_argarray); continue; } @@ -1322,6 +1323,16 @@ PHP_METHOD(RedisArray, del) RETURN_LONG(total); } +/* DEL will distribute the call to several nodes and regroup the values. */ +PHP_METHOD(RedisArray, del) +{ + ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL")-1); +} + +PHP_METHOD(RedisArray, unlink) { + ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1); +} + PHP_METHOD(RedisArray, multi) { zval *object; diff --git a/redis_array.h b/redis_array.h index d6a00fa096..7bacef4151 100644 --- a/redis_array.h +++ b/redis_array.h @@ -25,6 +25,7 @@ PHP_METHOD(RedisArray, flushall); PHP_METHOD(RedisArray, mget); PHP_METHOD(RedisArray, mset); PHP_METHOD(RedisArray, del); +PHP_METHOD(RedisArray, unlink); PHP_METHOD(RedisArray, keys); PHP_METHOD(RedisArray, getOption); PHP_METHOD(RedisArray, setOption); diff --git a/redis_cluster.c b/redis_cluster.c index 95bcba40f7..ede16b947d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -244,6 +244,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, unlink, arginfo_del, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC) @@ -708,7 +709,8 @@ static HashTable *method_args_to_ht(zval *z_args, int argc) { return ht_ret; } -/* Handler for both MGET and DEL */ +/* Convienience handler for commands that take multiple keys such as + * MGET, DEL, and UNLINK */ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zval *z_ret, cluster_cb cb) { @@ -935,8 +937,10 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, return 0; } -/* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */ -PHP_METHOD(RedisCluster, del) { +/* Generic passthru for DEL and UNLINK which act identically */ +static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS, + char *kw, int kw_len) +{ zval *z_ret; #if (PHP_MAJOR_VERSION < 7) @@ -949,14 +953,24 @@ PHP_METHOD(RedisCluster, del) { ZVAL_LONG(z_ret, 0); // Parse args, process - if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", - sizeof("DEL")-1, z_ret, cluster_del_resp)<0) + if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, kw_len, z_ret, + cluster_del_resp) < 0) { efree(z_ret); RETURN_FALSE; } } +/* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */ +PHP_METHOD(RedisCluster, del) { + cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL") - 1); +} + +/* {{{ proto array RedisCluster::unlink(string key1, string key2, ... keyN) */ +PHP_METHOD(RedisCluster, unlink) { + cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1); +} + /* {{{ proto array RedisCluster::mget(array keys) */ PHP_METHOD(RedisCluster, mget) { zval *z_ret; diff --git a/redis_cluster.h b/redis_cluster.h index 5ecd820419..74903e67c6 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -130,6 +130,7 @@ PHP_METHOD(RedisCluster, mset); PHP_METHOD(RedisCluster, msetnx); PHP_METHOD(RedisCluster, mset); PHP_METHOD(RedisCluster, del); +PHP_METHOD(RedisCluster, unlink); PHP_METHOD(RedisCluster, dump); PHP_METHOD(RedisCluster, setex); PHP_METHOD(RedisCluster, psetex); diff --git a/redis_commands.c b/redis_commands.c index ac0ed30dda..e2db37c658 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2949,6 +2949,14 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 1, 0, cmd, cmd_len, slot); } +/* UNLINK */ +int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + "UNLINK", sizeof("UNLINK")-1, 1, 0, cmd, cmd_len, slot); +} + /* COMMAND */ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 51d0b9f1ef..b31753643b 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -236,6 +236,9 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 0233b6d267..01e1c2f23b 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -18,8 +18,30 @@ function parseHostPort($str, &$host, &$port) { $port = substr($str, $pos+1); } +function getRedisVersion($obj_r) { + $arr_info = $obj_r->info(); + if (!$arr_info || !isset($arr_info['redis_version'])) { + return "0.0.0"; + } + return $arr_info['redis_version']; +} + +/* Determine the lowest redis version attached to this RedisArray object */ +function getMinVersion($obj_ra) { + $min_version = "0.0.0"; + foreach ($obj_ra->_hosts() as $host) { + $version = getRedisVersion($obj_ra->_instance($host)); + if (version_compare($version, $min_version) > 0) { + $min_version = $version; + } + } + + return $min_version; +} + class Redis_Array_Test extends TestSuite { + private $min_version; private $strings; public $ra = NULL; private $data = NULL; @@ -34,6 +56,7 @@ public function setUp() { global $newRing, $oldRing, $useIndex; $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); + $this->min_version = getMinVersion($this->ra); } public function testMSet() { @@ -141,6 +164,8 @@ class Redis_Rehashing_Test extends TestSuite public $ra = NULL; private $useIndex; + private $min_version; + // data private $strings; private $sets; @@ -185,6 +210,7 @@ public function setUp() { // create array $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); + $this->min_version = getMinVersion($this->ra); } public function testFlush() { @@ -206,12 +232,12 @@ private function distributeKeys() { foreach($this->strings as $k => $v) { $this->ra->set($k, $v); } - + // sets foreach($this->sets as $k => $v) { call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v)); } - + // lists foreach($this->lists as $k => $v) { call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v)); @@ -221,7 +247,7 @@ private function distributeKeys() { foreach($this->hashes as $k => $v) { $this->ra->hmset($k, $v); } - + // sorted sets foreach($this->zsets as $k => $v) { call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v)); @@ -314,6 +340,7 @@ public function testReadRedistributedKeys() { class Redis_Auto_Rehashing_Test extends TestSuite { public $ra = NULL; + private $min_version; // data private $strings; @@ -330,6 +357,7 @@ public function setUp() { // create array $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE)); + $this->min_version = getMinVersion($this->ra); } public function testDistribute() { @@ -378,11 +406,14 @@ public function testAllKeysHaveBeenMigrated() { // Test node-specific multi/exec class Redis_Multi_Exec_Test extends TestSuite { public $ra = NULL; + private $min_version; public function setUp() { global $newRing, $oldRing, $useIndex; + // create array $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex)); + $this->min_version = getMinVersion($this->ra); } public function testInit() { @@ -464,6 +495,22 @@ public function testMultiExecDel() { $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary')); } + public function testMutliExecUnlink() { + if (version_compare($this->min_version, "4.0.0", "lt")) { + var_dump($this->min_version); + $this->markTestSkipped(); + } + + $this->ra->set('{unlink}:key1', 'bar'); + $this->ra->set('{unlink}:key2', 'bar'); + + $out = $this->ra->multi($this->ra->_target('{unlink}')) + ->del('{unlink}:key1', '{unlink}:key2') + ->exec(); + + $this->assertTrue($out[0] === 2); + } + public function testDiscard() { /* phpredis issue #87 */ $key = 'test_err'; @@ -502,11 +549,13 @@ public function testDiscard() { class Redis_Distributor_Test extends TestSuite { public $ra = NULL; + private $min_version; public function setUp() { global $newRing, $oldRing, $useIndex; // create array $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'distributor' => array($this, 'distribute'))); + $this->min_version = getMinVersion($this->ra); } public function testInit() { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index f2ec30154e..b7a0ba2bad 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -456,7 +456,7 @@ public function testSetEx() { $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', 7 * 1000, 'val') === TRUE); @@ -626,38 +626,49 @@ public function testGetKeys() $this->assertEquals(array(), $this->redis->keys(rand().rand().rand().'*')); } - public function testDelete() - { + protected function genericDelUnlink($cmd) { $key = 'key' . rand(); $this->redis->set($key, 'val'); $this->assertEquals('val', $this->redis->get($key)); - $this->assertEquals(1, $this->redis->del($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->del('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->del('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->del('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->del(array('x', 'y'))); + // 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(array('x', 'y'))); + } + + public function testDelete() { + $this->genericDelUnlink("DEL"); + } + + public function testUnlink() { + if (version_compare($this->version, "4.0.0", "lt")) { + $this->markTestSkipped(); + return; + } + $this->genericDelUnlink("UNLINK"); } public function testType() From 04f62acbc8f83ddd64f746f4f97253bbf73d0df7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jan 2018 10:34:08 -0800 Subject: [PATCH 0918/1986] Syntax fixes and prototype consolidation Fix PHP_METHOD syntax fail for unlink Move redis_unlink_cmd by redis_del_cmd as they're the same --- redis.c | 2 +- redis_array.c | 2 +- redis_commands.c | 16 ++++++++-------- redis_commands.h | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/redis.c b/redis.c index 15f32f6dad..60081ef596 100644 --- a/redis.c +++ b/redis.c @@ -1180,7 +1180,7 @@ PHP_METHOD(Redis, delete) /* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}} * {{{ proto long Redis::unlink(array $keys) */ PHP_METHOD(Redis, unlink) - +{ REDIS_PROCESS_CMD(unlink, redis_long_response); } diff --git a/redis_array.c b/redis_array.c index 4d70b50a16..684d54cab6 100644 --- a/redis_array.c +++ b/redis_array.c @@ -877,7 +877,7 @@ PHP_METHOD(RedisArray, select) }\ }while(0) #else -#define HANDLE_MULTI_EXEC(ra, cmd) do { \ +#define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \ if (ra && ra->z_multi_exec) { \ int i, num_varargs; \ zval *varargs = NULL, z_arg_array; \ diff --git a/redis_commands.c b/redis_commands.c index e2db37c658..f54db468b0 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2877,6 +2877,14 @@ int redis_del_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "DEL", sizeof("DEL")-1, 1, 0, cmd, cmd_len, slot); } +/* UNLINK */ +int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + "UNLINK", sizeof("UNLINK")-1, 1, 0, cmd, cmd_len, slot); +} + /* WATCH */ int redis_watch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -2949,14 +2957,6 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 1, 0, cmd, cmd_len, slot); } -/* UNLINK */ -int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - "UNLINK", sizeof("UNLINK")-1, 1, 0, cmd, cmd_len, slot); -} - /* COMMAND */ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index b31753643b..0d510f1e4a 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -209,6 +209,9 @@ int redis_exists_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_del_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_watch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -236,9 +239,6 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From 6028df52265a9d07c321b5382f40062504ccd6d3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jan 2018 14:40:32 -0800 Subject: [PATCH 0919/1986] Remove debug line in unit tests --- tests/RedisArrayTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 01e1c2f23b..49c71a9860 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -497,7 +497,6 @@ public function testMultiExecDel() { public function testMutliExecUnlink() { if (version_compare($this->min_version, "4.0.0", "lt")) { - var_dump($this->min_version); $this->markTestSkipped(); } From 84f1f28ba2a8db539f4cc98b7dd3bb6474007cf0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 18 Jan 2018 09:04:25 -0800 Subject: [PATCH 0920/1986] Implement SWAPDB command --- common.h | 5 +++++ php_redis.h | 1 + redis.c | 6 ++++++ redis_commands.c | 18 ++++++++++++++++++ redis_commands.h | 3 +++ tests/RedisClusterTest.php | 1 + tests/RedisTest.php | 5 +++++ 7 files changed, 39 insertions(+) diff --git a/common.h b/common.h index 4d3977f855..4b1342dfb1 100644 --- a/common.h +++ b/common.h @@ -782,6 +782,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset_value, 0, 0, 3) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_swapdb, 0, 0, 2) + ZEND_ARG_INFO(0, srcdb) + ZEND_ARG_INFO(0, dstdb) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_key_start_end, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) diff --git a/php_redis.h b/php_redis.h index 895ad710f7..2d62d81d71 100644 --- a/php_redis.h +++ b/php_redis.h @@ -108,6 +108,7 @@ PHP_METHOD(Redis, pttl); PHP_METHOD(Redis, persist); PHP_METHOD(Redis, info); PHP_METHOD(Redis, select); +PHP_METHOD(Redis, swapdb); PHP_METHOD(Redis, move); PHP_METHOD(Redis, zAdd); PHP_METHOD(Redis, zDelete); diff --git a/redis.c b/redis.c index c226a48d35..e267d2c86e 100644 --- a/redis.c +++ b/redis.c @@ -387,6 +387,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC) + PHP_ME(Redis, swapdb, arginfo_swapdb, ZEND_ACC_PUBLIC) PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC) @@ -1832,6 +1833,11 @@ PHP_METHOD(Redis, select) { } /* }}} */ +/* {{{ proto bool Redis::swapdb(long srcdb, long dstdb) */ +PHP_METHOD(Redis, swapdb) { + REDIS_PROCESS_KW_CMD("SWAPDB", redis_long_long_cmd, redis_boolean_response); +} + /* {{{ proto bool Redis::move(string key, long dbindex) */ PHP_METHOD(Redis, move) { REDIS_PROCESS_KW_CMD("MOVE", redis_key_long_cmd, redis_1_response); diff --git a/redis_commands.c b/redis_commands.c index ac0ed30dda..708d9361b0 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -333,6 +333,24 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* long, long */ +int redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + zend_long v1, v2; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &v1, &v2) + == FAILURE) + { + return FAILURE; + } + + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ll", v1, v2); + + return SUCCESS; +} + /* key, long, long */ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, diff --git a/redis_commands.h b/redis_commands.h index 51d0b9f1ef..255a992e33 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -64,6 +64,9 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index d03d3591dd..4e0c15eb7d 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -34,6 +34,7 @@ public function testSelect() { return $this->markTestSkipped(); } public function testReconnectSelect() { return $this->markTestSkipped(); } public function testMultipleConnect() { return $this->markTestSkipped(); } public function testDoublePipeNoOp() { return $this->markTestSkipped(); } + public function testSwapDB() { return $this->markTestSkipped(); } /* Load our seeds on construction */ public function __construct() { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index f2ec30154e..2dafb58119 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1903,6 +1903,11 @@ public function testSelect() { $this->assertTrue($this->redis->select(0)); } + public function testSwapDB() { + $this->assertTrue($this->redis->swapdb(0, 1)); + $this->assertTrue($this->redis->swapdb(0, 1)); + } + public function testMset() { $this->redis->del('x', 'y', 'z'); // remove x y z $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z From 03f77018cef9ff541ea9db26dd7a1bc45775abcb Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 18 Jan 2018 11:01:53 -0800 Subject: [PATCH 0921/1986] Add a test for SWAPDB --- tests/RedisTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 2dafb58119..c1d3b8194b 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1904,6 +1904,10 @@ public function testSelect() { } public function testSwapDB() { + if (version_compare($this->version, "4.0.0", "lt")) { + $this->markTestSkipped(); + } + $this->assertTrue($this->redis->swapdb(0, 1)); $this->assertTrue($this->redis->swapdb(0, 1)); } From d3b6c3c2bb10c5efb1b2f7bc7638e682ac8ec65f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 18 Jan 2018 18:32:03 -0800 Subject: [PATCH 0922/1986] Documentation for UNLINK and SWAPDB --- README.markdown | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index e69fad87f2..14392dcb9e 100644 --- a/README.markdown +++ b/README.markdown @@ -186,6 +186,7 @@ Redis::REDIS_NOT_FOUND - Not found / other 1. [pconnect, popen](#pconnect-popen) - Connect to a server (persistent) 1. [auth](#auth) - Authenticate to the server 1. [select](#select) - Change the selected database for the current connection +1. [swapdb](#swapdb) - Swaps two Redis databases 1. [close](#close) - Close the connection 1. [setOption](#setoption) - Set client option 1. [getOption](#getoption) - Get client option @@ -284,6 +285,24 @@ _**Description**_: Change the selected database for the current connection. ##### *Example* See method for example: [move](#move) +### swapdb +----- +_**Description**_: Swap one Redis database with another atomically + +##### *Parameters* +*INTEGER*: db1 +*INTEGER*: db2 + +##### *Return value* +`TRUE` on success and `FALSE` on failure. + +*note*: Requires Redis >= 4.0.0 + +##### *Example* +~~~ +$redis->swapdb(0, 1); /* Swaps DB 0 with DB 1 atomically */ +~~~ + ### close ----- _**Description**_: Disconnects from the Redis instance, except when `pconnect` is used. @@ -648,7 +667,7 @@ $redis->slowLog('len'); ### Keys ----- -* [del, delete](#del-delete) - Delete a key +* [del, delete, unlink](#del-delete-unlink) - Delete a key * [dump](#dump) - Return a serialized version of the value stored at the specified key. * [exists](#exists) - Determine if a key exists * [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds @@ -749,13 +768,15 @@ $redis->setNx('key', 'value'); /* return TRUE */ $redis->setNx('key', 'value'); /* return FALSE */ ~~~ -### del, delete +### del, delete, unlink ----- _**Description**_: Remove specified keys. ##### *Parameters* An array of keys, or an undefined number of parameters, each a key: *key1* *key2* *key3* ... *keyN* +*Note*: If you are connecting to Redis server >= 4.0.0 you can remove a key with the `unlink` method in the exact same way you would use `del`. The Redis [unlink](https://redis.io/commands/unlink) command is non-blocking and will perform the actual deletion asynchronously. + ##### *Return value* *Long* Number of keys deleted. @@ -768,6 +789,10 @@ $redis->set('key4', 'val4'); $redis->delete('key1', 'key2'); /* return 2 */ $redis->delete(array('key3', 'key4')); /* return 2 */ + +/* If using Redis >= 4.0.0 you can call unlink */ +$redis->unlink('key1', 'key2'); +$redis->unlink(Array('key1', 'key2')); ~~~ From 9cd059117a91c4011181b7499ccf21c844d0f0f1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 29 Jan 2018 22:25:33 +0200 Subject: [PATCH 0923/1986] Refactor RedisArray Change type of returning value from `char *` to `zend_string *` for `ra_extract_key` and `ra_call_extractor` functions. Store keys as `zend_string *` in RedisArray::mset. --- redis_array.c | 17 +++++------- redis_array_impl.c | 69 ++++++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 46 deletions(-) diff --git a/redis_array.c b/redis_array.c index 684d54cab6..7fbbc6b4d5 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1059,9 +1059,9 @@ PHP_METHOD(RedisArray, mset) RedisArray *ra; int *pos, argc, *argc_each; HashTable *h_keys; - char *key, **keys, kbuf[40]; - int key_len, *key_lens; - zend_string *zkey; + char *key, kbuf[40]; + int key_len; + zend_string **keys, *zkey; ulong idx; if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { @@ -1084,8 +1084,7 @@ PHP_METHOD(RedisArray, mset) } argv = emalloc(argc * sizeof(zval*)); pos = emalloc(argc * sizeof(int)); - keys = emalloc(argc * sizeof(char*)); - key_lens = emalloc(argc * sizeof(int)); + keys = ecalloc(argc, sizeof(zend_string *)); argc_each = emalloc(ra->count * sizeof(int)); memset(argc_each, 0, ra->count * sizeof(int)); @@ -1106,8 +1105,7 @@ PHP_METHOD(RedisArray, mset) } argc_each[pos[i]]++; /* count number of keys per node */ - keys[i] = estrndup(key, key_len); - key_lens[i] = (int)key_len; + keys[i] = zend_string_init(key, key_len, 0); argv[i] = data; i++; } ZEND_HASH_FOREACH_END(); @@ -1134,7 +1132,7 @@ PHP_METHOD(RedisArray, mset) } else { ZVAL_ZVAL(z_tmp, argv[i], 1, 0); } - add_assoc_zval_ex(&z_argarray, keys[i], key_lens[i], z_tmp); + add_assoc_zval_ex(&z_argarray, ZSTR_VAL(keys[i]), ZSTR_LEN(keys[i]), z_tmp); found++; } @@ -1167,12 +1165,11 @@ PHP_METHOD(RedisArray, mset) /* Free any keys that we needed to allocate memory for, because they weren't strings */ for(i = 0; i < argc; i++) { - efree(keys[i]); + zend_string_release(keys[i]); } /* cleanup */ efree(keys); - efree(key_lens); efree(argv); efree(pos); efree(argc_each); diff --git a/redis_array_impl.c b/redis_array_impl.c index fbaa6bbd05..3aca8c7611 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -378,10 +378,10 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* call userland key extraction function */ -char * -ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS_DC) +zend_string * +ra_call_extractor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) { - char *out = NULL; + zend_string *out = NULL; zval z_ret, z_argv; /* check that we can call the extractor function */ @@ -400,8 +400,11 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); if (Z_TYPE(z_ret) == IS_STRING) { - *out_len = Z_STRLEN(z_ret); - out = estrndup(Z_STRVAL(z_ret), *out_len); +#if (PHP_MAJOR_VERSION < 7) + out = zend_string_init(Z_STRVAL(z_ret), Z_STRLEN(z_ret), 0); +#else + out = zval_get_string(&z_ret); +#endif } zval_dtor(&z_argv); @@ -409,20 +412,18 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len, int *out_len TSR return out; } -static char * -ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS_DC) { - +static zend_string * +ra_extract_key(RedisArray *ra, const char *key, int key_len TSRMLS_DC) +{ char *start, *end; - *out_len = key_len; if (Z_TYPE(ra->z_fun) != IS_NULL) { - return ra_call_extractor(ra, key, key_len, out_len TSRMLS_CC); + return ra_call_extractor(ra, key, key_len TSRMLS_CC); } else if ((start = strchr(key, '{')) == NULL || (end = strchr(start + 1, '}')) == NULL) { - return estrndup(key, key_len); + return zend_string_init(key, key_len, 0); } /* found substring */ - *out_len = end - start - 1; - return estrndup(start + 1, *out_len); + return zend_string_init(start + 1, end - start - 1, 0); } /* call userland key distributor function */ @@ -455,39 +456,35 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) } zval * -ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) { - uint32_t hash; - char *out; - int pos, out_len; +ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) +{ + int pos; + zend_string *out; /* extract relevant part of the key */ - out = ra_extract_key(ra, key, key_len, &out_len TSRMLS_CC); - if(!out) return NULL; + if ((out = ra_extract_key(ra, key, key_len TSRMLS_CC)) == NULL) { + return NULL; + } - if (Z_TYPE(ra->z_dist) != IS_NULL) { - pos = ra_call_distributor(ra, key, key_len TSRMLS_CC); - if (pos < 0 || pos >= ra->count) { - efree(out); - return NULL; - } - } else { - uint64_t h64; + if (Z_TYPE(ra->z_dist) == IS_NULL) { + int i; unsigned long ret = 0xffffffff; - size_t i; /* hash */ - for (i = 0; i < out_len; ++i) { - CRC32(ret, (unsigned char)out[i]); + for (i = 0; i < ZSTR_LEN(out); ++i) { + CRC32(ret, ZSTR_VAL(out)[i]); } - hash = (ret ^ 0xffffffff); /* get position on ring */ - h64 = hash; - h64 *= ra->count; - h64 /= 0xffffffff; - pos = (int)h64; + pos = (int)((ret ^ 0xffffffff) * ra->count / 0xffffffff); + } else { + pos = ra_call_distributor(ra, key, key_len TSRMLS_CC); + if (pos < 0 || pos >= ra->count) { + zend_string_release(out); + return NULL; + } } - efree(out); + zend_string_release(out); if(out_pos) *out_pos = pos; From 627bd89b45ba09b689e692a23370e5d82d9829a1 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 8 Feb 2018 05:47:28 +0100 Subject: [PATCH 0924/1986] skip online test --- tests/RedisTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 4c0b78840c..4d807a7885 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5164,9 +5164,13 @@ public function testMultipleConnect() { } public function testConnectException() { + $host = 'github.com'; + if (gethostbyname($host) === $host) { + return $this->markTestSkipped('online test'); + } $redis = new Redis(); try { - $redis->connect('github.com', 6379, 0.01); + $redis->connect($host, 6379, 0.01); } catch (Exception $e) { $this->assertTrue(strpos($e, "timed out") !== false); } From 3cec66a2c0c177d52f7090fa7374079396889618 Mon Sep 17 00:00:00 2001 From: Alexey Kopytko Date: Mon, 1 Jan 2018 22:10:05 +0900 Subject: [PATCH 0925/1986] Update README.markdown --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 14392dcb9e..34ddf88297 100644 --- a/README.markdown +++ b/README.markdown @@ -225,7 +225,7 @@ $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay _**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. The connection will not be closed on `close` or end of request until the php process ends. -So be patient on too many open FD's (specially on redis server side) when using persistent +So be prepared for too many open FD's errors (specially on redis server side) when using persistent connections on many servers connecting to one redis server. Also more than one persistent connection can be made identified by either host + port + timeout From 1886140338acd3db1ccd4dff987226b2cd84b07b Mon Sep 17 00:00:00 2001 From: ftwbzhao Date: Wed, 13 May 2015 16:38:32 +0800 Subject: [PATCH 0926/1986] update INCR && DECR --- README.markdown | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 34ddf88297..387ca7e568 100644 --- a/README.markdown +++ b/README.markdown @@ -832,7 +832,11 @@ $redis->incr('key1'); /* key1 didn't exists, set to 0 before the increment */ $redis->incr('key1'); /* 2 */ $redis->incr('key1'); /* 3 */ $redis->incr('key1'); /* 4 */ -$redis->incrBy('key1', 10); /* 14 */ + +// Will redirect, and actually make an INCRBY call +$redis->incr('key1', 10); /* 14 */ + +$redis->incrBy('key1', 10); /* 24 */ ~~~ ### incrByFloat @@ -874,7 +878,11 @@ $redis->decr('key1'); /* key1 didn't exists, set to 0 before the increment */ $redis->decr('key1'); /* -2 */ $redis->decr('key1'); /* -3 */ -$redis->decrBy('key1', 10); /* -13 */ + +// Will redirect, and actually make an DECRBY call +$redis->decr('key1', 10); /* -13 */ + +$redis->decrBy('key1', 10); /* -23 */ ~~~ ### mGet, getMultiple From 36349da78fe50a5ebc29d39b45502b1de7734eee Mon Sep 17 00:00:00 2001 From: Yusril Herlian Syah Date: Sun, 11 Feb 2018 00:16:32 +0700 Subject: [PATCH 0927/1986] Remove unwanted slash in README.markdown --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 387ca7e568..4006616b51 100644 --- a/README.markdown +++ b/README.markdown @@ -1816,7 +1816,7 @@ $redis->lGet('key1', 0); /* 'A' */ $redis->lGet('key1', -1); /* 'C' */ $redis->lGet('key1', 10); /* `FALSE` */ ~~~ -/ + ### lInsert ----- _**Description**_: Insert value in the list before or after the pivot value. From 5b29036e65a57278ca5132a3ff7ba259ee66f9a6 Mon Sep 17 00:00:00 2001 From: Yevgen Kovalienia Date: Wed, 13 Sep 2017 08:52:01 +0200 Subject: [PATCH 0928/1986] Typo fixed in cluster.markdown --- cluster.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster.markdown b/cluster.markdown index 336fcc4986..0a8ae1a167 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -163,7 +163,7 @@ session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&ti Set this variable to "rediscluster" to inform phpredis that this is a cluster instance. ### session.save_path -The save path for cluster based session storage takes the form of a PHP GET request, and requires that you specify at least on `seed` node. Other options you can specify are as follows: +The save path for cluster based session storage takes the form of a PHP GET request, and requires that you specify at least one `seed` node. Other options you can specify are as follows: * _timeout (double)_: The amount of time phpredis will wait when connecting or writing to the cluster. * _read_timeout (double)_: The amount of time phpredis will wait for a result from the cluster. From 919b1d19e99ada78791b64d212de96e9337d7d7d Mon Sep 17 00:00:00 2001 From: Barbery <380032007@qq.com> Date: Tue, 21 Mar 2017 18:23:27 +0800 Subject: [PATCH 0929/1986] Update cluster.markdown add RedisCluster::FAILOVER_DISTRIBUTE_SLAVES option --- cluster.markdown | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cluster.markdown b/cluster.markdown index 0a8ae1a167..52591d8f70 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -65,6 +65,11 @@ $obj_cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER $obj_cluster->setOption( RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE ); + +// Always distribute readonly commands to the slaves, at random +$obj_cluster->setOption( + RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE_SLAVES +); ## Main command loop From a4afe919094cdc2f71fa59d76f4c7f5a08bd1d00 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 17 Feb 2018 09:47:28 -0800 Subject: [PATCH 0930/1986] Some style normalization --- cluster_library.c | 304 ++++++++++++++++++------------------- redis.c | 42 +++--- redis_array.c | 4 +- redis_cluster.c | 288 +++++++++++++++++------------------ redis_commands.c | 374 +++++++++++++++++++++++----------------------- redis_session.c | 48 +++--- 6 files changed, 530 insertions(+), 530 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0a3adf2d9d..2a44bb2d95 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -60,10 +60,10 @@ static void dump_reply(clusterReply *reply, int indent) { smart_string_appendl(&buf, "\"", 1); break; case TYPE_MULTIBULK: - if(reply->elements == (size_t)-1) { + if (reply->elements == (size_t)-1) { smart_string_appendl(&buf, "(nil)", sizeof("(nil)")-1); } else { - for(i=0;ielements;i++) { + for (i = 0; i < reply->elements; i++) { dump_reply(reply->element[i], indent+2); } } @@ -72,8 +72,8 @@ static void dump_reply(clusterReply *reply, int indent) { break; } - if(buf.len > 0) { - for(i=0;i 0) { + for (i = 0; i < indent; i++) { php_printf(" "); } @@ -96,11 +96,11 @@ void cluster_free_reply(clusterReply *reply, int free_data) { case TYPE_ERR: case TYPE_LINE: case TYPE_BULK: - if(free_data && reply->str) + if (free_data && reply->str) efree(reply->str); break; case TYPE_MULTIBULK: - for(i=0;ielements && reply->element[i]; i++) { + for (i = 0; i < reply->elements && reply->element[i]; i++) { cluster_free_reply(reply->element[i], free_data); } efree(reply->element); @@ -125,7 +125,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, r = element[i] = ecalloc(1, sizeof(clusterReply)); // Bomb out, flag error condition on a communication failure - if(redis_read_reply_type(sock, &r->type, &len TSRMLS_CC)<0) { + if (redis_read_reply_type(sock, &r->type, &len TSRMLS_CC) < 0) { *err = 1; return; } @@ -136,7 +136,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, switch(r->type) { case TYPE_ERR: case TYPE_LINE: - if(redis_sock_gets(sock,buf,sizeof(buf),&sz TSRMLS_CC)<0) { + if (redis_sock_gets(sock,buf,sizeof(buf),&sz TSRMLS_CC) < 0) { *err = 1; return; } @@ -148,7 +148,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, case TYPE_BULK: if (r->len > 0) { r->str = redis_sock_read_bulk_reply(sock,r->len TSRMLS_CC); - if(!r->str) { + if (!r->str) { *err = 1; return; } @@ -159,7 +159,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, r->elements = r->len; cluster_multibulk_resp_recursive(sock, r->elements, r->element, err TSRMLS_CC); - if(*err) return; + if (*err) return; break; default: *err = 1; @@ -219,14 +219,14 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, case TYPE_BULK: r->len = len; r->str = redis_sock_read_bulk_reply(redis_sock, len TSRMLS_CC); - if(r->len != -1 && !r->str) { + if (r->len != -1 && !r->str) { cluster_free_reply(r, 1); return NULL; } break; case TYPE_MULTIBULK: r->elements = len; - if(len != (size_t)-1) { + if (len != (size_t)-1) { r->element = ecalloc(len, sizeof(clusterReply*)*len); cluster_multibulk_resp_recursive(redis_sock, len, r->element, &err TSRMLS_CC); @@ -238,7 +238,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, } // Free/return null on communication error - if(err) { + if (err) { cluster_free_reply(r,1); return NULL; } @@ -286,7 +286,7 @@ static int cluster_send_readonly(RedisSock *redis_sock TSRMLS_DC) { /* Return success if we can send it */ ret = cluster_send_direct(redis_sock, RESP_READONLY_CMD, - sizeof(RESP_READONLY_CMD)-1, TYPE_LINE TSRMLS_CC); + sizeof(RESP_READONLY_CMD) - 1, TYPE_LINE TSRMLS_CC); /* Flag this socket as READONLY if our command worked */ redis_sock->readonly = !ret; @@ -298,7 +298,7 @@ static int cluster_send_readonly(RedisSock *redis_sock TSRMLS_DC) { /* Send MULTI to a specific ReidsSock */ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_MULTI_CMD, - sizeof(RESP_MULTI_CMD)-1, TYPE_LINE TSRMLS_CC)==0) + sizeof(RESP_MULTI_CMD) - 1, TYPE_LINE TSRMLS_CC) == 0) { c->cmd_sock->mode = MULTI; return 0; @@ -350,10 +350,10 @@ static void cluster_dist_free_ht(zval *p) clusterDistList *dl = *(clusterDistList**)p; int i; - for(i=0; i < dl->len; i++) { - if(dl->entry[i].key_free) + for (i = 0; i < dl->len; i++) { + if (dl->entry[i].key_free) efree(dl->entry[i].key); - if(dl->entry[i].val_free) + if (dl->entry[i].val_free) efree(dl->entry[i].val); } @@ -394,7 +394,7 @@ static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, int key_len, int key_free) { // Reallocate if required - if(dl->len==dl->size) { + if (dl->len == dl->size) { dl->entry = erealloc(dl->entry, sizeof(clusterKeyVal) * dl->size * 2); dl->size *= 2; } @@ -427,8 +427,8 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, slot = cluster_hash_key(key, key_len); // We can't do this if we don't fully understand the keyspace - if(c->master[slot] == NULL) { - if(key_free) efree(key); + if (c->master[slot] == NULL) { + if (key_free) efree(key); return FAILURE; } @@ -442,7 +442,7 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, retptr = cluster_dl_add_key(dl, key, key_len, key_free); // Push our return pointer if requested - if(kv) *kv = retptr; + if (kv) *kv = retptr; return SUCCESS; } @@ -509,7 +509,7 @@ static void ht_free_slave(void *data) static void ht_free_slave(zval *data) #endif { - if(*(redisClusterNode**)data) { + if (*(redisClusterNode**)data) { cluster_free_node(*(redisClusterNode**)data); } } @@ -519,20 +519,20 @@ unsigned short cluster_hash_key(const char *key, int len) { int s, e; // Find first occurrence of {, if any - for(s=0;stype != TYPE_MULTIBULK || r->elements < 1) { - if(r) cluster_free_reply(r, 1); + if (r) cluster_free_reply(r, 1); return NULL; } @@ -655,7 +655,7 @@ cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) ulong index; // Allocate our slaves hash table if we haven't yet - if(!master->slaves) { + if (!master->slaves) { ALLOC_HASHTABLE(master->slaves); zend_hash_init(master->slaves, 0, NULL, ht_free_slave, 0); index = 1; @@ -668,11 +668,11 @@ cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) /* Sanity check/validation for CLUSTER SLOTS command */ #define VALIDATE_SLOTS_OUTER(r) \ - (r->elements>=3 && r2->element[0]->type == TYPE_INT && \ - r->element[1]->type==TYPE_INT) + (r->elements >= 3 && r2->element[0]->type == TYPE_INT && \ + r->element[1]->type == TYPE_INT) #define VALIDATE_SLOTS_INNER(r) \ - (r->type == TYPE_MULTIBULK && r->elements>=2 && \ - r->element[0]->type == TYPE_BULK && r->element[1]->type==TYPE_INT) + (r->type == TYPE_MULTIBULK && r->elements >= 2 && \ + r->element[0]->type == TYPE_BULK && r->element[1]->type == TYPE_INT) /* Use the output of CLUSTER SLOTS to map our nodes */ static int cluster_map_slots(redisCluster *c, clusterReply *r) { @@ -683,12 +683,12 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { unsigned short port; char *host, key[1024]; - for(i=0;ielements;i++) { + for (i = 0; i < r->elements; i++) { // Inner response r2 = r->element[i]; // Validate outer and master slot - if(!VALIDATE_SLOTS_OUTER(r2) || !VALIDATE_SLOTS_INNER(r2->element[2])) { + if (!VALIDATE_SLOTS_OUTER(r2) || !VALIDATE_SLOTS_INNER(r2->element[2])) { return -1; } @@ -712,14 +712,14 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { } // Attach slaves - for(j=3;jelements;j++) { + for (j = 3; j< r2->elements; j++) { r3 = r2->element[j]; - if(!VALIDATE_SLOTS_INNER(r3)) { + if (!VALIDATE_SLOTS_INNER(r3)) { return -1; } // Skip slaves where the host is "" - if(r3->element[0]->len == 0) continue; + if (r3->element[0]->len == 0) continue; // Attach this node to our slave slave = cluster_node_create(c, r3->element[0]->str, @@ -729,8 +729,8 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { } // Attach this node to each slot in the range - for(j=low;j<=high;j++) { - c->master[j]=master; + for (j = low; j<= high; j++) { + c->master[j] = master; } } @@ -740,7 +740,7 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { /* Free a redisClusterNode structure */ PHP_REDIS_API void cluster_free_node(redisClusterNode *node) { - if(node->slaves) { + if (node->slaves) { zend_hash_destroy(node->slaves); efree(node->slaves); } @@ -784,7 +784,7 @@ static void ht_free_seed(zval *data) #endif { RedisSock *redis_sock = *(RedisSock**)data; - if(redis_sock) redis_free_socket(redis_sock); + if (redis_sock) redis_free_socket(redis_sock); } /* Free redisClusterNode objects we've stored */ @@ -855,7 +855,7 @@ PHP_REDIS_API void cluster_free(redisCluster *c) { * which have been randomized. The return value needs to be freed. */ static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) { zval **z_seeds, *z_ele; - int *map, i, count, index=0; + int *map, i, count, index = 0; /* How many */ count = zend_hash_num_elements(seeds); @@ -928,8 +928,8 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { /* Initial mapping of our cluster keyspace */ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { RedisSock *seed; - clusterReply *slots=NULL; - int mapped=0; + clusterReply *slots = NULL; + int mapped = 0; // Iterate over seeds until we can get slots ZEND_HASH_FOREACH_PTR(c->seeds, seed) { @@ -952,10 +952,10 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { } ZEND_HASH_FOREACH_END(); // Clean up slots reply if we got one - if(slots) cluster_free_reply(slots, 1); + if (slots) cluster_free_reply(slots, 1); // Throw an exception if we couldn't map - if(!mapped) { + if (!mapped) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't map cluster keyspace using any provided seed", 0 TSRMLS_CC); @@ -1013,26 +1013,26 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type CLUSTER_CLEAR_ERROR(c); CLUSTER_CLEAR_REPLY(c); - if(-1 == redis_check_eof(c->cmd_sock, 1 TSRMLS_CC) || + if (-1 == redis_check_eof(c->cmd_sock, 1 TSRMLS_CC) || EOF == (*reply_type = php_stream_getc(c->cmd_sock->stream))) { return -1; } // In the event of an ERROR, check if it's a MOVED/ASK error - if(*reply_type == TYPE_ERR) { + if (*reply_type == TYPE_ERR) { char inbuf[4096]; int moved; // Attempt to read the error - if(!php_stream_gets(c->cmd_sock->stream, inbuf, sizeof(inbuf))) { + if (!php_stream_gets(c->cmd_sock->stream, inbuf, sizeof(inbuf))) { return -1; } // Check for MOVED or ASK redirection - if((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { + if ((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { // Set our redirection information - if(cluster_set_redirection(c,inbuf,moved)<0) { + if (cluster_set_redirection(c,inbuf,moved) < 0) { return -1; } @@ -1046,14 +1046,14 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type } // Fetch the first line of our response from Redis. - if(redis_sock_gets(c->cmd_sock,c->line_reply,sizeof(c->line_reply), - &sz TSRMLS_CC)<0) + if (redis_sock_gets(c->cmd_sock,c->line_reply,sizeof(c->line_reply), + &sz TSRMLS_CC) < 0) { return -1; } // For replies that will give us a numberic length, convert it - if(*reply_type != TYPE_LINE) { + if (*reply_type != TYPE_LINE) { c->reply_len = strtol(c->line_reply, NULL, 10); } else { c->reply_len = (long long)sz; @@ -1080,7 +1080,7 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC) { static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, int nomaster TSRMLS_DC) { - int i, count=1, *nodes; + int i, count = 1, *nodes; RedisSock *redis_sock; /* Determine our overall node count */ @@ -1169,9 +1169,9 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, /* If in ASK redirection, get/create the node for that host:port, otherwise * just use the command socket. */ - if(c->redir_type == REDIR_ASK) { + if (c->redir_type == REDIR_ASK) { redis_sock = cluster_get_asking_sock(c TSRMLS_CC); - if(cluster_send_asking(redis_sock TSRMLS_CC)<0) { + if (cluster_send_asking(redis_sock TSRMLS_CC) < 0) { return -1; } } @@ -1201,7 +1201,7 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, } /* Don't fall back if direct communication with this slot is required. */ - if(direct) return -1; + if (direct) return -1; /* Fall back by attempting the request against every known node */ ZEND_HASH_FOREACH_PTR(c->nodes, seed_node) { @@ -1243,16 +1243,16 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { size_t klen; /* Do we already have the new slot mapped */ - if(c->master[c->redir_slot]) { + if (c->master[c->redir_slot]) { /* No need to do anything if it's the same node */ - if(!CLUSTER_REDIR_CMP(c)) { + if (!CLUSTER_REDIR_CMP(c)) { return; } /* Check to see if we have this new node mapped */ node = cluster_find_node(c, c->redir_host, c->redir_port); - if(node) { + if (node) { /* Just point to this slot */ c->master[c->redir_slot] = node; } else { @@ -1270,7 +1270,7 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { } else { /* Check to see if the ip and port are mapped */ node = cluster_find_node(c, c->redir_host, c->redir_port); - if(!node) { + if (!node) { node = cluster_node_create(c, c->redir_host, c->redir_host_len, c->redir_port, c->redir_slot, 0); } @@ -1294,9 +1294,9 @@ PHP_REDIS_API int cluster_abort_exec(redisCluster *c TSRMLS_DC) { clusterFoldItem *fi = c->multi_head; /* Loop through our fold items */ - while(fi) { - if(SLOT_SOCK(c,fi->slot)->mode == MULTI) { - if(cluster_send_discard(c, fi->slot TSRMLS_CC)<0) { + while (fi) { + if (SLOT_SOCK(c,fi->slot)->mode == MULTI) { + if (cluster_send_discard(c, fi->slot TSRMLS_CC) < 0) { cluster_disconnect(c TSRMLS_CC); return -1; } @@ -1321,8 +1321,8 @@ PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, { int i; - for(i=0;imaster[i] && c->master[i]->sock && + for (i = 0; i < REDIS_CLUSTER_SLOTS; i++) { + if (c->master[i] && c->master[i]->sock && c->master[i]->sock->port == port && !strcasecmp(ZSTR_VAL(c->master[i]->sock->host), host)) { @@ -1354,12 +1354,12 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, } /* Try the slot */ - if(cluster_sock_write(c, cmd, cmd_len, 1 TSRMLS_CC)==-1) { + if (cluster_sock_write(c, cmd, cmd_len, 1 TSRMLS_CC) == -1) { return -1; } /* Check our response */ - if(cluster_check_response(c, &c->reply_type TSRMLS_CC)!=0 || + if (cluster_check_response(c, &c->reply_type TSRMLS_CC) != 0 || (rtype != TYPE_EOF && rtype != c->reply_type)) return -1; /* Success */ @@ -1371,7 +1371,7 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC) { - int resp, timedout=0; + int resp, timedout = 0; long msstart; /* Set the slot we're operating against as well as it's socket. These can @@ -1431,10 +1431,10 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char /* Figure out if we've timed out trying to read or write the data */ timedout = resp && c->waitms ? mstime() - msstart >= c->waitms : 0; - } while(resp != 0 && !c->clusterdown && !timedout); + } while (resp != 0 && !c->clusterdown && !timedout); // If we've detected the cluster is down, throw an exception - if(c->clusterdown) { + if (c->clusterdown) { zend_throw_exception(redis_cluster_exception_ce, "The Redis Cluster is down (CLUSTERDOWN)", 0 TSRMLS_CC); return -1; @@ -1465,10 +1465,10 @@ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, char *resp; // Make sure we can read the response - if(c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC))==NULL) + if (c->reply_type != TYPE_BULK || + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC)) == NULL) { - if(c->flags->mode != MULTI) { + if (c->flags->mode != MULTI) { RETURN_FALSE; } else { add_next_index_bool(&c->multi_resp, 0); @@ -1488,8 +1488,8 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster char *resp; // Make sure we can read the response - if(c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC))==NULL) + if (c->reply_type != TYPE_BULK || + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC)) == NULL) { CLUSTER_RETURN_FALSE(c); } @@ -1521,8 +1521,8 @@ PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * double dbl; // Make sure we can read the response - if(c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC))==NULL) + if (c->reply_type != TYPE_BULK || + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC)) == NULL) { CLUSTER_RETURN_FALSE(c); } @@ -1540,7 +1540,7 @@ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster void *ctx) { // Check that we have +OK - if(c->reply_type != TYPE_LINE || c->reply_len != 2 || + if (c->reply_type != TYPE_LINE || c->reply_len != 2 || c->line_reply[0] != 'O' || c->line_reply[1] != 'K') { CLUSTER_RETURN_FALSE(c); @@ -1553,7 +1553,7 @@ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - if(c->reply_type != TYPE_LINE || c->reply_len != 4 || + if (c->reply_type != TYPE_LINE || c->reply_len != 4 || memcmp(c->line_reply,"PONG",sizeof("PONG")-1)) { CLUSTER_RETURN_FALSE(c); @@ -1567,7 +1567,7 @@ PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { // Validate our reply type, and check for a zero - if(c->reply_type != TYPE_INT || c->reply_len == 0) { + if (c->reply_type != TYPE_INT || c->reply_len == 0) { CLUSTER_RETURN_FALSE(c); } @@ -1578,7 +1578,7 @@ PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - if(c->reply_type != TYPE_INT) { + if (c->reply_type != TYPE_INT) { CLUSTER_RETURN_FALSE(c); } CLUSTER_RETURN_LONG(c, c->reply_len); @@ -1589,20 +1589,20 @@ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster void *ctx) { // Make sure we got the right kind of response - if(c->reply_type != TYPE_LINE) { + if (c->reply_type != TYPE_LINE) { CLUSTER_RETURN_FALSE(c); } // Switch on the type - if(strncmp (c->line_reply, "string", 6)==0) { + if (strncmp (c->line_reply, "string", 6) == 0) { CLUSTER_RETURN_LONG(c, REDIS_STRING); - } else if (strncmp(c->line_reply, "set", 3)==0) { + } else if (strncmp(c->line_reply, "set", 3) == 0) { CLUSTER_RETURN_LONG(c, REDIS_SET); - } else if (strncmp(c->line_reply, "list", 4)==0) { + } else if (strncmp(c->line_reply, "list", 4) == 0) { CLUSTER_RETURN_LONG(c, REDIS_LIST); - } else if (strncmp(c->line_reply, "hash", 4)==0) { + } else if (strncmp(c->line_reply, "hash", 4) == 0) { CLUSTER_RETURN_LONG(c, REDIS_HASH); - } else if (strncmp(c->line_reply, "zset", 4)==0) { + } else if (strncmp(c->line_reply, "zset", 4) == 0) { CLUSTER_RETURN_LONG(c, REDIS_ZSET); } else { CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND); @@ -1615,11 +1615,11 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * { subscribeContext *sctx = (subscribeContext*)ctx; zval z_tab, *z_tmp; - int pull=0; + int pull = 0; // Consume each MULTI BULK response (one per channel/pattern) - while(sctx->argc--) { + while (sctx->argc--) { if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw, &z_tab) ) { @@ -1654,10 +1654,10 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * c->subscribed_slot = c->cmd_slot; /* Multibulk response, {[pattern], type, channel, payload} */ - while(1) { + while (1) { /* Arguments */ zval *z_type, *z_chan, *z_pat = NULL, *z_data; - int tab_idx=1, is_pmsg; + int tab_idx = 1, is_pmsg; // Get the next subscribe response if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, mbulk_resp_loop, &z_tab) || @@ -1692,7 +1692,7 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * z_args[0] = &getThis(); // Set up calbacks depending on type - if(is_pmsg) { + if (is_pmsg) { z_args[1] = &z_pat; z_args[2] = &z_chan; z_args[3] = &z_data; @@ -1704,7 +1704,7 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * z_args[0] = *getThis(); // Set up calbacks depending on type - if(is_pmsg) { + if (is_pmsg) { z_args[1] = *z_pat; z_args[2] = *z_chan; z_args[3] = *z_data; @@ -1718,7 +1718,7 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * sctx->cb.param_count = tab_idx; // Execute our callback - if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC)!= + if (zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC) != SUCCESS) { break; @@ -1753,7 +1753,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, array_init(return_value); // Consume each response - while(argc--) { + while (argc--) { // Fail if we didn't get an array or can't find index 1 if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw, &z_tab) || (z_chan = zend_hash_index_find(Z_ARRVAL(z_tab), 1)) == NULL @@ -1776,7 +1776,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, char *flag = Z_STRVAL_P(z_flag); // Add result - add_assoc_bool(return_value, Z_STRVAL_P(z_chan), flag[1]=='1'); + add_assoc_bool(return_value, Z_STRVAL_P(z_chan), flag[1] == '1'); zval_dtor(&z_tab); pull = 1; @@ -1809,7 +1809,7 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) MAKE_STD_ZVAL(z_sub_ele); #endif array_init(z_sub_ele); - for(i=0;ielements;i++) { + for (i = 0; i < r->elements; i++) { cluster_mbulk_variant_resp(r->element[i], z_sub_ele); } add_next_index_zval(z_ret, z_sub_ele); @@ -1830,12 +1830,12 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust int i; // Make sure we can read it - if((r = cluster_read_resp(c TSRMLS_CC))==NULL) { + if ((r = cluster_read_resp(c TSRMLS_CC)) == NULL) { CLUSTER_RETURN_FALSE(c); } // Handle ATOMIC vs. MULTI mode in a seperate switch - if(CLUSTER_IS_ATOMIC(c)) { + if (CLUSTER_IS_ATOMIC(c)) { switch(r->type) { case TYPE_INT: RETVAL_LONG(r->integer); @@ -1856,7 +1856,7 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust case TYPE_MULTIBULK: array_init(z_arr); - for(i=0;ielements;i++) { + for (i = 0; i < r->elements; i++) { cluster_mbulk_variant_resp(r->element[i], z_arr); } RETVAL_ZVAL(z_arr, 1, 0); @@ -1920,7 +1920,7 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, c->cmd_sock->serializer = c->flags->serializer; /* Call our specified callback */ - if (cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC)==FAILURE) { + if (cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC) == FAILURE) { zval_dtor(z_result); #if (PHP_MAJOR_VERSION < 7) efree(z_result); @@ -1930,7 +1930,7 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } // Success, make this array our return value - if(CLUSTER_IS_ATOMIC(c)) { + if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(z_result, 0, 1); } else { add_next_index_zval(&c->multi_resp, z_result); @@ -1944,20 +1944,20 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * char *pit; // We always want to see a MULTIBULK response with two elements - if(c->reply_type != TYPE_MULTIBULK || c->reply_len != 2) + if (c->reply_type != TYPE_MULTIBULK || c->reply_len != 2) { return FAILURE; } // Read the BULK size - if(cluster_check_response(c, &c->reply_type TSRMLS_CC),0 || + if (cluster_check_response(c, &c->reply_type TSRMLS_CC),0 || c->reply_type != TYPE_BULK) { return FAILURE; } // Read the iterator - if((pit = redis_sock_read_bulk_reply(c->cmd_sock,c->reply_len TSRMLS_CC))==NULL) + if ((pit = redis_sock_read_bulk_reply(c->cmd_sock,c->reply_len TSRMLS_CC)) == NULL) { return FAILURE; } @@ -1967,7 +1967,7 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * efree(pit); // We'll need another MULTIBULK response for the payload - if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) + if (cluster_check_response(c, &c->reply_type TSRMLS_CC) < 0) { return FAILURE; } @@ -2002,7 +2002,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster char *info; // Read our bulk response - if((info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC))==NULL) + if ((info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC)) == NULL) { CLUSTER_RETURN_FALSE(c); } @@ -2012,7 +2012,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster efree(info); // Return our array - if(CLUSTER_IS_ATOMIC(c)) { + if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(z_result, 1, 0); } else { add_next_index_zval(&c->multi_resp, z_result); @@ -2053,22 +2053,22 @@ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, { ZVAL_NULL(z_ret); // Pull our next response if directed - if(pull) { - if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) + if (pull) { + if (cluster_check_response(c, &c->reply_type TSRMLS_CC) < 0) { return NULL; } } // Validate reply type and length - if(c->reply_type != TYPE_MULTIBULK || c->reply_len == -1) { + if (c->reply_type != TYPE_MULTIBULK || c->reply_len == -1) { return NULL; } array_init(z_ret); // Call our callback - if(cb(c->cmd_sock, z_ret, c->reply_len, NULL TSRMLS_CC)==FAILURE) { + if (cb(c->cmd_sock, z_ret, c->reply_len, NULL TSRMLS_CC) == FAILURE) { zval_dtor(z_ret); return NULL; } @@ -2084,7 +2084,7 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, array_init(multi_resp); clusterFoldItem *fi = c->multi_head; - while(fi) { + while (fi) { /* Make sure our transaction didn't fail here */ if (c->multi_len[fi->slot] > -1) { /* Set the slot where we should look for responses. We don't allow @@ -2093,7 +2093,7 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, c->cmd_slot = fi->slot; c->cmd_sock = SLOT_SOCK(c, fi->slot); - if(cluster_check_response(c, &c->reply_type TSRMLS_CC)<0) { + if (cluster_check_response(c, &c->reply_type TSRMLS_CC) < 0) { zval_dtor(multi_resp); RETURN_FALSE; } @@ -2121,19 +2121,19 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, * to consume the responses. */ c->cmd_sock->serializer = c->flags->serializer; short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || - mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL TSRMLS_CC)==FAILURE; + mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL TSRMLS_CC) == FAILURE; // If we had a failure, pad results with FALSE to indicate failure. Non // existant keys (e.g. for MGET will come back as NULL) - if(fail) { - while(mctx->count--) { + if (fail) { + while (mctx->count--) { add_next_index_bool(mctx->z_multi, 0); } } // If this is the tail of our multi command, we can set our returns - if(mctx->last) { - if(CLUSTER_IS_ATOMIC(c)) { + if (mctx->last) { + if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(mctx->z_multi, 0, 1); } else { add_next_index_zval(&c->multi_resp, mctx->z_multi); @@ -2152,23 +2152,23 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluste int real_argc = mctx->count/2; // Protect against an invalid response type - if(c->reply_type != TYPE_INT) { + if (c->reply_type != TYPE_INT) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Invalid response type for MSETNX"); - while(real_argc--) { + while (real_argc--) { add_next_index_bool(mctx->z_multi, 0); } return; } // Response will be 1/0 per key, so the client can match them up - while(real_argc--) { + while (real_argc--) { add_next_index_long(mctx->z_multi, c->reply_len); } // Set return value if it's our last response - if(mctx->last) { - if(CLUSTER_IS_ATOMIC(c)) { + if (mctx->last) { + if (CLUSTER_IS_ATOMIC(c)) { RETVAL_ZVAL(mctx->z_multi, 0, 1); } else { add_next_index_zval(&c->multi_resp, mctx->z_multi); @@ -2186,7 +2186,7 @@ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * clusterMultiCtx *mctx = (clusterMultiCtx*)ctx; // If we get an invalid reply, inform the client - if(c->reply_type != TYPE_INT) { + if (c->reply_type != TYPE_INT) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Invalid reply type returned for DEL command"); efree(mctx); @@ -2196,8 +2196,8 @@ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * // Increment by the number of keys deleted Z_LVAL_P(mctx->z_multi) += c->reply_len; - if(mctx->last) { - if(CLUSTER_IS_ATOMIC(c)) { + if (mctx->last) { + if (CLUSTER_IS_ATOMIC(c)) { ZVAL_LONG(return_value, Z_LVAL_P(mctx->z_multi)); } else { add_next_index_long(&c->multi_resp, Z_LVAL_P(mctx->z_multi)); @@ -2216,7 +2216,7 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // If we get an invalid reply type something very wrong has happened, // and we have to abort. - if(c->reply_type != TYPE_LINE) { + if (c->reply_type != TYPE_LINE) { php_error_docref(0 TSRMLS_CC, E_ERROR, "Invalid reply type returned for MSET command"); zval_dtor(mctx->z_multi); @@ -2226,8 +2226,8 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } // Set our return if it's the last call - if(mctx->last) { - if(CLUSTER_IS_ATOMIC(c)) { + if (mctx->last) { + if (CLUSTER_IS_ATOMIC(c)) { ZVAL_BOOL(return_value, zval_is_true(mctx->z_multi)); } else { add_next_index_bool(&c->multi_resp, zval_is_true(mctx->z_multi)); @@ -2294,10 +2294,10 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, int line_len; // Iterate over the number we have - while(count--) { + while (count--) { // Read the line, which should never come back null line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); - if(line == NULL) return FAILURE; + if (line == NULL) return FAILURE; // Add to our result array add_next_index_stringl(z_result, line, line_len); @@ -2316,7 +2316,7 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, int line_len; /* Iterate over the lines we have to process */ - while(count--) { + while (count--) { /* Read our line */ line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); @@ -2350,17 +2350,17 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long idx = 0; // Our count wil need to be divisible by 2 - if(count % 2 != 0) { + if (count % 2 != 0) { return -1; } // Iterate through our elements - while(count--) { + while (count--) { // Grab our line, bomb out on failure line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); - if(!line) return -1; + if (!line) return -1; - if(idx++ % 2 == 0) { + if (idx++ % 2 == 0) { // Save our key and length key = line; key_len = line_len; @@ -2393,15 +2393,15 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, long long idx = 0; // Our context will need to be divisible by 2 - if(count %2 != 0) { + if (count %2 != 0) { return -1; } // While we have elements - while(count--) { + while (count--) { line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); if (line != NULL) { - if(idx++ % 2 == 0) { + if (idx++ % 2 == 0) { key = line; key_len = line_len; } else { @@ -2430,15 +2430,15 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC) { char *line; - int line_len,i=0; + int line_len,i = 0; zval *z_keys = ctx; // Loop while we've got replies - while(count--) { + while (count--) { zend_string *zstr = zval_get_string(&z_keys[i]); line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); - if(line != NULL) { + if (line != NULL) { zval zv, *z = &zv; if (redis_unpack(redis_sock, line, line_len, z TSRMLS_CC)) { #if (PHP_MAJOR_VERSION < 7) diff --git a/redis.c b/redis.c index c00f9e8763..710ed969dc 100644 --- a/redis.c +++ b/redis.c @@ -495,7 +495,7 @@ redis_send_discard(RedisSock *redis_sock TSRMLS_DC) (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) { /* success if we get OK */ - result = (resp_len == 3 && strncmp(resp,"+OK", 3)==0) ? SUCCESS:FAILURE; + result = (resp_len == 3 && strncmp(resp,"+OK", 3) == 0) ? SUCCESS:FAILURE; /* free our response */ efree(resp); @@ -1448,7 +1448,7 @@ PHP_METHOD(Redis, sRandMember) // Grab our socket, validate call if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - &cmd, &cmd_len, NULL, NULL, &have_count)==FAILURE) + &cmd, &cmd_len, NULL, NULL, &have_count) == FAILURE) { RETURN_FALSE; } @@ -1457,7 +1457,7 @@ PHP_METHOD(Redis, sRandMember) if(have_count) { if (IS_ATOMIC(redis_sock)) { if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -1534,7 +1534,7 @@ PHP_METHOD(Redis, sort) { // Grab socket, handle command construction if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &have_store, - &cmd, &cmd_len, NULL, NULL)==FAILURE) + &cmd, &cmd_len, NULL, NULL) == FAILURE) { RETURN_FALSE; } @@ -1945,14 +1945,14 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, char *cmd; int cmd_len; RedisSock *redis_sock; - int withscores=0; + int withscores = 0; if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { RETURN_FALSE; } if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, - &cmd_len, &withscores, NULL, NULL)==FAILURE) + &cmd_len, &withscores, NULL, NULL) == FAILURE) { RETURN_FALSE; } @@ -1966,7 +1966,7 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, } else { if (IS_ATOMIC(redis_sock)) { if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -1975,14 +1975,14 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, } } -/* {{{ proto array Redis::zRange(string key,int start,int end,bool scores=0) */ +/* {{{ proto array Redis::zRange(string key,int start,int end,bool scores = 0) */ PHP_METHOD(Redis, zRange) { generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE", redis_zrange_cmd); } -/* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores=0) */ +/* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores = 0) */ PHP_METHOD(Redis, zRevRange) { generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE", redis_zrange_cmd); @@ -2491,7 +2491,7 @@ PHP_METHOD(Redis, subscribe) { * [p]unsubscribe(array(channel_0, channel_1, ..., channel_n)) * response format : * array( - * channel_0 => TRUE|FALSE, + * channel_0 => TRUE|FALSE, * channel_1 => TRUE|FALSE, * ... * channel_n => TRUE|FALSE @@ -2884,7 +2884,7 @@ PHP_METHOD(Redis, pubsub) { int cmd_len; strlen_t kw_len; PUBSUB_TYPE type; - zval *arg=NULL; + zval *arg = NULL; // Parse arguments if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), @@ -2904,7 +2904,7 @@ PHP_METHOD(Redis, pubsub) { } else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) { /* One array argument */ if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY || - zend_hash_num_elements(Z_ARRVAL_P(arg))==0) + zend_hash_num_elements(Z_ARRVAL_P(arg)) == 0) { RETURN_FALSE; } @@ -2929,7 +2929,7 @@ PHP_METHOD(Redis, pubsub) { if(type == PUBSUB_NUMSUB) { if (IS_ATOMIC(redis_sock)) { if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -2938,7 +2938,7 @@ PHP_METHOD(Redis, pubsub) { } else { if (IS_ATOMIC(redis_sock)) { if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL)<0) + redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } @@ -3251,7 +3251,7 @@ PHP_METHOD(Redis, getAuth) { PHP_METHOD(Redis, client) { zval *object; RedisSock *redis_sock; - char *cmd, *opt=NULL, *arg=NULL; + char *cmd, *opt = NULL, *arg = NULL; strlen_t opt_len, arg_len; int cmd_len; @@ -3399,10 +3399,10 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { zval *object, *z_iter; RedisSock *redis_sock; HashTable *hash; - char *pattern=NULL, *cmd, *key=NULL; - int cmd_len, num_elements, key_free=0; + char *pattern = NULL, *cmd, *key = NULL; + int cmd_len, num_elements, key_free = 0; strlen_t key_len = 0, pattern_len = 0; - zend_long count=0, iter; + zend_long count = 0, iter; /* Different prototype depending on if this is a key based scan */ if(type != TYPE_SCAN) { @@ -3440,11 +3440,11 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { // The iterator should be passed in as NULL for the first iteration, but we // can treat any NON LONG value as NULL for these purposes as we've // seperated the variable anyway. - if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter)<0) { + if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter) < 0) { /* Convert to long */ convert_to_long(z_iter); iter = 0; - } else if(Z_LVAL_P(z_iter)!=0) { + } else if(Z_LVAL_P(z_iter) != 0) { /* Update our iterator value for the next passthru */ iter = Z_LVAL_P(z_iter); } else { @@ -3480,7 +3480,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Execute our command getting our new iterator value */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock,type,&iter)<0) + redis_sock,type,&iter) < 0) { if(key_free) efree(key); RETURN_FALSE; diff --git a/redis_array.c b/redis_array.c index 7fbbc6b4d5..26d8f34381 100644 --- a/redis_array.c +++ b/redis_array.c @@ -135,7 +135,7 @@ redis_array_free(RedisArray *ra) int i; /* Redis objects */ - for(i=0;icount;i++) { + for(i = 0; i< ra->count; i++) { zval_dtor(&ra->redis[i]); efree(ra->hosts[i]); } @@ -718,7 +718,7 @@ PHP_METHOD(RedisArray, keys) array_init(return_value); /* Iterate our RedisArray nodes */ - for(i=0; icount; ++i) { + for(i = 0; i < ra->count; ++i) { zval zv, *z_tmp = &zv; #if (PHP_MAJOR_VERSION < 7) /* Return for this node */ diff --git a/redis_cluster.c b/redis_cluster.c index ede16b947d..8b802011de 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -279,7 +279,7 @@ static void ht_free_seed(zval *data) #endif { RedisSock *redis_sock = *(RedisSock**)data; - if(redis_sock) redis_free_socket(redis_sock); + if (redis_sock) redis_free_socket(redis_sock); } /* Free redisClusterNode objects we've stored */ @@ -366,7 +366,7 @@ free_cluster_context(zend_object *object) { redisCluster *cluster = (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std)); #endif // Free any allocated prefix, as well as the struct - if(cluster->flags->prefix) efree(cluster->flags->prefix); + if (cluster->flags->prefix) efree(cluster->flags->prefix); efree(cluster->flags); // Free seeds HashTable itself @@ -391,19 +391,19 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, double read_timeout, int persistent TSRMLS_DC) { // Validate timeout - if(timeout < 0L || timeout > INT_MAX) { + if (timeout < 0L || timeout > INT_MAX) { zend_throw_exception(redis_cluster_exception_ce, "Invalid timeout", 0 TSRMLS_CC); } // Validate our read timeout - if(read_timeout < 0L || read_timeout > INT_MAX) { + if (read_timeout < 0L || read_timeout > INT_MAX) { zend_throw_exception(redis_cluster_exception_ce, "Invalid read timeout", 0 TSRMLS_CC); } /* Make sure there are some seeds */ - if(zend_hash_num_elements(ht_seeds)==0) { + if (zend_hash_num_elements(ht_seeds) == 0) { zend_throw_exception(redis_cluster_exception_ce, "Must pass seeds", 0 TSRMLS_CC); } @@ -430,7 +430,7 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { zval z_seeds, z_timeout, z_read_timeout, z_persistent, *z_value; char *iptr; - double timeout=0, read_timeout=0; + double timeout = 0, read_timeout = 0; int persistent = 0; HashTable *ht_seeds = NULL; @@ -506,7 +506,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Create a RedisCluster Object */ PHP_METHOD(RedisCluster, __construct) { - zval *object, *z_seeds=NULL; + zval *object, *z_seeds = NULL; char *name; strlen_t name_len; double timeout = 0.0, read_timeout = 0.0; @@ -514,16 +514,16 @@ PHP_METHOD(RedisCluster, __construct) { redisCluster *context = GET_CONTEXT(); // Parse arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os!|addb", &object, redis_cluster_ce, &name, &name_len, &z_seeds, &timeout, - &read_timeout, &persistent)==FAILURE) + &read_timeout, &persistent) == FAILURE) { RETURN_FALSE; } // Require a name - if(name_len == 0 && ZEND_NUM_ARGS() < 2) { + if (name_len == 0 && ZEND_NUM_ARGS() < 2) { zend_throw_exception(redis_cluster_exception_ce, "You must specify a name or pass seeds!", 0 TSRMLS_CC); @@ -578,8 +578,8 @@ distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, ctx->last = last; // Attempt to send the command - if(cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len TSRMLS_CC)<0 || - c->err!=NULL) + if (cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len TSRMLS_CC) < 0 || + c->err != NULL) { cluster_multi_free(mc); zval_dtor(z_ret); @@ -587,7 +587,7 @@ distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, return -1; } - if(CLUSTER_IS_ATOMIC(c)) { + if (CLUSTER_IS_ATOMIC(c)) { // Process response now cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx); } else { @@ -720,7 +720,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zval *z_args; HashTable *ht_arr; HashPosition ptr; - int i=1, argc = ZEND_NUM_ARGS(), ht_free=0; + int i = 1, argc = ZEND_NUM_ARGS(), ht_free = 0; short slot; /* If we don't have any arguments we're invalid */ @@ -755,7 +755,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Process the first key outside of our loop, so we don't have to check if // it's the first iteration every time, needlessly zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); - if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { + if (get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC) < 0) { efree(z_args); return -1; } @@ -764,15 +764,15 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, cluster_multi_add(&mc, kv.key, kv.key_len); // Free key if we prefixed - if(kv.key_free) efree(kv.key); + if (kv.key_free) efree(kv.key); // Move to the next key zend_hash_move_forward_ex(ht_arr, &ptr); // Iterate over keys 2...N slot = kv.slot; - while(zend_hash_has_more_elements_ex(ht_arr, &ptr)==SUCCESS) { - if(get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)<0) { + while (zend_hash_has_more_elements_ex(ht_arr, &ptr) ==SUCCESS) { + if (get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC) < 0) { cluster_multi_free(&mc); if (ht_free) { zend_hash_destroy(ht_arr); @@ -783,10 +783,10 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, } // If the slots have changed, kick off the keys we've aggregated - if(slot != kv.slot) { + if (slot != kv.slot) { // Process this batch of MGET keys - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, - &mc, z_ret, i==argc, cb)<0) + if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, + &mc, z_ret, i == argc, cb) < 0) { cluster_multi_free(&mc); if (ht_free) { @@ -802,7 +802,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, cluster_multi_add(&mc, kv.key, kv.key_len); // Free key if we prefixed - if(kv.key_free) efree(kv.key); + if (kv.key_free) efree(kv.key); // Update the last slot we encountered, and the key we're on slot = kv.slot; @@ -813,9 +813,9 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, efree(z_args); // If we've got straggler(s) process them - if(mc.argc > 0) { - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, - &mc, z_ret, 1, cb)<0) + if (mc.argc > 0) { + if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, + &mc, z_ret, 1, cb) < 0) { cluster_multi_free(&mc); if (ht_free) { @@ -853,17 +853,17 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, zval *z_arr; HashTable *ht_arr; HashPosition ptr; - int i=1, argc; + int i = 1, argc; short slot; // Parse our arguments - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr) == FAILURE) { return -1; } // No reason to send zero args ht_arr = Z_ARRVAL_P(z_arr); - if((argc = zend_hash_num_elements(ht_arr))==0) { + if ((argc = zend_hash_num_elements(ht_arr)) == 0) { return -1; } @@ -875,27 +875,27 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Process the first key/value pair outside of our loop zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); - if(get_key_val_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)==-1) return -1; + if (get_key_val_ht(c, ht_arr, &ptr, &kv TSRMLS_CC) ==-1) return -1; zend_hash_move_forward_ex(ht_arr, &ptr); // Add this to our multi cmd, set slot, free key if we prefixed cluster_multi_add(&mc, kv.key, kv.key_len); cluster_multi_add(&mc, kv.val, kv.val_len); - if(kv.key_free) efree(kv.key); - if(kv.val_free) efree(kv.val); + if (kv.key_free) efree(kv.key); + if (kv.val_free) efree(kv.val); // While we've got more keys to set slot = kv.slot; - while(zend_hash_has_more_elements_ex(ht_arr, &ptr)==SUCCESS) { + while (zend_hash_has_more_elements_ex(ht_arr, &ptr) ==SUCCESS) { // Pull the next key/value pair - if(get_key_val_ht(c, ht_arr, &ptr, &kv TSRMLS_CC)==-1) { + if (get_key_val_ht(c, ht_arr, &ptr, &kv TSRMLS_CC) ==-1) { return -1; } // If the slots have changed, process responses - if(slot != kv.slot) { - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - slot, &mc, z_ret, i==argc, cb)<0) + if (slot != kv.slot) { + if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + slot, &mc, z_ret, i == argc, cb) < 0) { return -1; } @@ -906,8 +906,8 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, cluster_multi_add(&mc, kv.val, kv.val_len); // Free our key and value if we need to - if(kv.key_free) efree(kv.key); - if(kv.val_free) efree(kv.val); + if (kv.key_free) efree(kv.key); + if (kv.val_free) efree(kv.val); // Update our slot, increment position slot = kv.slot; @@ -918,9 +918,9 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, } // If we've got stragglers, process them too - if(mc.argc > 0) { - if(distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, - z_ret, 1, cb)<0) + if (mc.argc > 0) { + if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc, + z_ret, 1, cb) < 0) { return -1; } @@ -953,7 +953,7 @@ static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS, ZVAL_LONG(z_ret, 0); // Parse args, process - if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, kw_len, z_ret, + if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, kw_len, z_ret, cluster_del_resp) < 0) { efree(z_ret); @@ -984,8 +984,8 @@ PHP_METHOD(RedisCluster, mget) { array_init(z_ret); // Parse args, process - if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MGET", - sizeof("MGET")-1, z_ret, cluster_mbulk_mget_resp)<0) + if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MGET", + sizeof("MGET")-1, z_ret, cluster_mbulk_mget_resp) < 0) { zval_dtor(z_ret); efree(z_ret); @@ -1006,8 +1006,8 @@ PHP_METHOD(RedisCluster, mset) { ZVAL_TRUE(z_ret); // Parse args and process. If we get a failure, free zval and return FALSE. - if(cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", - sizeof("MSET")-1, z_ret, cluster_mset_resp)==-1) + if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", + sizeof("MSET")-1, z_ret, cluster_mset_resp) ==-1) { efree(z_ret); RETURN_FALSE; @@ -1027,8 +1027,8 @@ PHP_METHOD(RedisCluster, msetnx) { array_init(z_ret); // Parse args and process. If we get a failure, free mem and return FALSE - if(cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", - sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp)==-1) + if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", + sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp) ==-1) { zval_dtor(z_ret); efree(z_ret); @@ -1077,8 +1077,8 @@ PHP_METHOD(RedisCluster, keys) { zval zv, *z_ret = &zv; int i, cmd_len; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pat, &pat_len) - ==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pat, &pat_len) + == FAILURE) { RETURN_FALSE; } @@ -1095,7 +1095,7 @@ PHP_METHOD(RedisCluster, keys) { ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) break; if (cluster_send_slot(c, node->slot, cmd, cmd_len, TYPE_MULTIBULK - TSRMLS_CC)<0) + TSRMLS_CC) < 0) { php_error_docref(0 TSRMLS_CC, E_ERROR, "Can't send KEYS to %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); @@ -1105,7 +1105,7 @@ PHP_METHOD(RedisCluster, keys) { /* Ensure we can get a response */ resp = cluster_read_resp(c TSRMLS_CC); - if(!resp) { + if (!resp) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Can't read response from %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); @@ -1113,9 +1113,9 @@ PHP_METHOD(RedisCluster, keys) { } /* Iterate keys, adding to our big array */ - for(i=0;ielements;i++) { + for(i = 0; i < resp->elements; i++) { /* Skip non bulk responses, they should all be bulk */ - if(resp->element[i]->type != TYPE_BULK) { + if (resp->element[i]->type != TYPE_BULK) { continue; } @@ -1180,14 +1180,14 @@ PHP_METHOD(RedisCluster, srandmember) { /* Treat as readonly */ c->readonly = CLUSTER_IS_ATOMIC(c); - if(redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, + if (redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, &cmd_len, &slot, NULL, &have_count) - ==FAILURE) + == FAILURE) { RETURN_FALSE; } - if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { + if (cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC) < 0 || c->err != NULL) { efree(cmd); RETURN_FALSE; } @@ -1706,16 +1706,16 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, c->readonly = CLUSTER_IS_ATOMIC(c); cluster_cb cb; char *cmd; int cmd_len; short slot; - int withscores=0; + int withscores = 0; - if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, - &withscores, &slot, NULL)==FAILURE) + if (fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, + &withscores, &slot, NULL) == FAILURE) { efree(cmd); RETURN_FALSE; } - if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { + if (cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC) < 0 || c->err != NULL) { efree(cmd); RETURN_FALSE; } @@ -1733,7 +1733,7 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, } /* {{{ proto - * array RedisCluster::zrange(string k, long s, long e, bool score=0) */ + * array RedisCluster::zrange(string k, long s, long e, bool score = 0) */ PHP_METHOD(RedisCluster, zrange) { generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE", redis_zrange_cmd); @@ -1741,7 +1741,7 @@ PHP_METHOD(RedisCluster, zrange) { /* }}} */ /* {{{ proto - * array RedisCluster::zrevrange(string k,long s,long e,bool scores=0) */ + * array RedisCluster::zrevrange(string k,long s,long e,bool scores = 0) */ PHP_METHOD(RedisCluster, zrevrange) { generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE", redis_zrange_cmd); @@ -1818,13 +1818,13 @@ PHP_METHOD(RedisCluster, sort) { redisCluster *c = GET_CONTEXT(); char *cmd; int cmd_len, have_store; short slot; - if(redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &have_store, - &cmd, &cmd_len, &slot, NULL)==FAILURE) + if (redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &have_store, + &cmd, &cmd_len, &slot, NULL) == FAILURE) { RETURN_FALSE; } - if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { + if (cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC) < 0 || c->err != NULL) { efree(cmd); RETURN_FALSE; } @@ -1832,7 +1832,7 @@ PHP_METHOD(RedisCluster, sort) { efree(cmd); // Response type differs based on presence of STORE argument - if(!have_store) { + if (!have_store) { cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); @@ -1845,13 +1845,13 @@ PHP_METHOD(RedisCluster, object) { char *cmd; int cmd_len; short slot; REDIS_REPLY_TYPE rtype; - if(redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &rtype, - &cmd, &cmd_len, &slot, NULL)==FAILURE) + if (redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &rtype, + &cmd, &cmd_len, &slot, NULL) == FAILURE) { RETURN_FALSE; } - if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { + if (cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC) < 0 || c->err != NULL) { efree(cmd); RETURN_FALSE; } @@ -1859,7 +1859,7 @@ PHP_METHOD(RedisCluster, object) { efree(cmd); // Use the correct response type - if(rtype == TYPE_INT) { + if (rtype == TYPE_INT) { cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); @@ -1887,23 +1887,23 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot; // There is not reason to unsubscribe outside of a subscribe loop - if(c->subscribed_slot == -1) { + if (c->subscribed_slot == -1) { php_error_docref(0 TSRMLS_CC, E_WARNING, "You can't unsubscribe outside of a subscribe loop"); RETURN_FALSE; } // Call directly because we're going to set the slot manually - if(redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, + if (redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, &slot, &ctx) - ==FAILURE) + == FAILURE) { RETURN_FALSE; } // This has to operate on our subscribe slot - if(cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK - TSRMLS_CC) ==FAILURE) + if (cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK + TSRMLS_CC) == FAILURE) { zend_throw_exception(redis_cluster_exception_ce, "Failed to UNSUBSCRIBE within our subscribe loop!", 0 TSRMLS_CC); @@ -2043,7 +2043,7 @@ PHP_METHOD(RedisCluster, _redir) { size_t len; len = snprintf(buf, sizeof(buf), "%s:%d", c->redir_host, c->redir_port); - if(*c->redir_host && c->redir_host_len) { + if (*c->redir_host && c->redir_host_len) { RETURN_STRINGL(buf, len); } else { RETURN_NULL(); @@ -2058,7 +2058,7 @@ PHP_METHOD(RedisCluster, _redir) { PHP_METHOD(RedisCluster, multi) { redisCluster *c = GET_CONTEXT(); - if(c->flags->mode == MULTI) { + if (c->flags->mode == MULTI) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "RedisCluster is already in MULTI mode, ignoring"); RETURN_FALSE; @@ -2083,14 +2083,14 @@ PHP_METHOD(RedisCluster, watch) { zend_string *zstr; // Disallow in MULTI mode - if(c->flags->mode == MULTI) { + if (c->flags->mode == MULTI) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "WATCH command not allowed in MULTI mode"); RETURN_FALSE; } // Don't need to process zero arguments - if(!argc) RETURN_FALSE; + if (!argc) RETURN_FALSE; // Create our distribution HashTable ht_dist = cluster_dist_create(); @@ -2104,7 +2104,7 @@ PHP_METHOD(RedisCluster, watch) { } // Loop through arguments, prefixing if needed - for(i=0;imaster[slot] && SLOT_SOCK(c,slot)->watching) { - if(cluster_send_slot(c, slot, RESP_UNWATCH_CMD, + for(slot = 0; slot < REDIS_CLUSTER_SLOTS; slot++) { + if (c->master[slot] && SLOT_SOCK(c,slot)->watching) { + if (cluster_send_slot(c, slot, RESP_UNWATCH_CMD, sizeof(RESP_UNWATCH_CMD)-1, - TYPE_LINE TSRMLS_CC)==-1) + TYPE_LINE TSRMLS_CC) ==-1) { CLUSTER_RETURN_BOOL(c, 0); } @@ -2189,16 +2189,16 @@ PHP_METHOD(RedisCluster, exec) { clusterFoldItem *fi; // Verify we are in fact in multi mode - if(CLUSTER_IS_ATOMIC(c)) { + if (CLUSTER_IS_ATOMIC(c)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "RedisCluster is not in MULTI mode"); RETURN_FALSE; } // First pass, send EXEC and abort on failure fi = c->multi_head; - while(fi) { - if(SLOT_SOCK(c, fi->slot)->mode == MULTI) { - if(cluster_send_exec(c, fi->slot TSRMLS_CC)<0) { + while (fi) { + if (SLOT_SOCK(c, fi->slot)->mode == MULTI) { + if ( cluster_send_exec(c, fi->slot TSRMLS_CC) < 0) { cluster_abort_exec(c TSRMLS_CC); zend_throw_exception(redis_cluster_exception_ce, @@ -2230,12 +2230,12 @@ PHP_METHOD(RedisCluster, exec) { PHP_METHOD(RedisCluster, discard) { redisCluster *c = GET_CONTEXT(); - if(CLUSTER_IS_ATOMIC(c)) { + if (CLUSTER_IS_ATOMIC(c)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); RETURN_FALSE; } - if(cluster_abort_exec(c TSRMLS_CC)<0) { + if (cluster_abort_exec(c TSRMLS_CC) < 0) { CLUSTER_RESET_MULTI(c); } @@ -2257,8 +2257,8 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) /* If it's a string, treat it as a key. Otherwise, look for a two * element array */ - if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG || - Z_TYPE_P(z_arg)==IS_DOUBLE) + if (Z_TYPE_P(z_arg) ==IS_STRING || Z_TYPE_P(z_arg) ==IS_LONG || + Z_TYPE_P(z_arg) ==IS_DOUBLE) { /* Allow for any scalar here */ zstr = zval_get_string(z_arg); @@ -2269,7 +2269,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) key_free = redis_key_prefix(c->flags, &key, &key_len); slot = cluster_hash_key(key, key_len); zend_string_release(zstr); - if(key_free) efree(key); + if (key_free) efree(key); } else if (Z_TYPE_P(z_arg) == IS_ARRAY && (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL && (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL && @@ -2280,7 +2280,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) (unsigned short)Z_LVAL_P(z_port)); /* Inform the caller if they've passed bad data */ - if(slot < 0) { + if (slot < 0) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", Z_STRVAL_P(z_host), Z_LVAL_P(z_port)); } @@ -2305,14 +2305,14 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, zval *z_arg; short slot; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_arg)==FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_arg) == FAILURE) { RETURN_FALSE; } // One argument means find the node (treated like a key), and two means // send the command to a specific host and port slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); - if(slot<0) { + if (slot < 0) { RETURN_FALSE; } @@ -2320,7 +2320,7 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, kw, ""); // Kick off our command - if(cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC)<0) { + if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC) < 0) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send command at a specific node", 0 TSRMLS_CC); efree(cmd); @@ -2346,14 +2346,14 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) int i, argc = ZEND_NUM_ARGS(); /* Commands using this pass-thru don't need to be enabled in MULTI mode */ - if(!CLUSTER_IS_ATOMIC(c)) { + if (!CLUSTER_IS_ATOMIC(c)) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Command can't be issued in MULTI mode"); RETURN_FALSE; } /* We at least need the key or [host,port] argument */ - if(argc<1) { + if (argc<1) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Command requires at least an argument to direct to a node"); RETURN_FALSE; @@ -2369,7 +2369,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* First argument needs to be the "where" */ - if((slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC))<0) { + if ((slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC)) < 0) { efree(z_args); RETURN_FALSE; } @@ -2378,14 +2378,14 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) redis_cmd_init_sstr(&cmd, argc-1, kw, kw_len); /* Iterate, appending args */ - for(i=1;i0, and finish if it's back to zero - if(Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it)<0) { + if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) { convert_to_long(z_it); it = 0; - } else if(Z_LVAL_P(z_it)!=0) { + } else if (Z_LVAL_P(z_it) != 0) { it = Z_LVAL_P(z_it); } else { RETURN_FALSE; @@ -2460,22 +2460,22 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, count); // Send it off - if(cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC)==FAILURE) + if (cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC) == FAILURE) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't send SCAN command", 0 TSRMLS_CC); - if(key_free) efree(key); + if (key_free) efree(key); efree(cmd); RETURN_FALSE; } // Read response - if(cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, - &it)==FAILURE) + if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, + &it) == FAILURE) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't read SCAN response", 0 TSRMLS_CC); - if(key_free) efree(key); + if (key_free) efree(key); efree(cmd); RETURN_FALSE; } @@ -2486,10 +2486,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, // Free our command efree(cmd); - } while(c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0); + } while (c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0); // Free our key - if(key_free) efree(key); + if (key_free) efree(key); // Update iterator reference Z_LVAL_P(z_it) = it; @@ -2498,7 +2498,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, /* {{{ proto RedisCluster::scan(string master, long it [, string pat, long cnt]) */ PHP_METHOD(RedisCluster, scan) { redisCluster *c = GET_CONTEXT(); - char *cmd, *pat=NULL; + char *cmd, *pat = NULL; strlen_t pat_len = 0; int cmd_len; short slot; @@ -2510,24 +2510,24 @@ PHP_METHOD(RedisCluster, scan) { c->readonly = CLUSTER_IS_ATOMIC(c); /* Can't be in MULTI mode */ - if(!CLUSTER_IS_ATOMIC(c)) { + if (!CLUSTER_IS_ATOMIC(c)) { zend_throw_exception(redis_cluster_exception_ce, "SCAN type commands can't be called in MULTI mode", 0 TSRMLS_CC); RETURN_FALSE; } /* Parse arguments */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z|s!l", &z_it, - &z_node, &pat, &pat_len, &count)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z|s!l", &z_it, + &z_node, &pat, &pat_len, &count) == FAILURE) { RETURN_FALSE; } /* Convert or update iterator */ - if(Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it)<0) { + if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) { convert_to_long(z_it); it = 0; - } else if(Z_LVAL_P(z_it)!=0) { + } else if (Z_LVAL_P(z_it) != 0) { it = Z_LVAL_P(z_it); } else { RETURN_FALSE; @@ -2546,12 +2546,12 @@ PHP_METHOD(RedisCluster, scan) { cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, count); - if((slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC))<0) { + if ((slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC)) < 0) { RETURN_FALSE; } // Send it to the node in question - if(cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC)<0) + if (cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC) < 0) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't send SCAN to node", 0 TSRMLS_CC); @@ -2559,8 +2559,8 @@ PHP_METHOD(RedisCluster, scan) { RETURN_FALSE; } - if(cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, TYPE_SCAN, - &it)==FAILURE || Z_TYPE_P(return_value)!=IS_ARRAY) + if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, TYPE_SCAN, + &it) == FAILURE || Z_TYPE_P(return_value)!=IS_ARRAY) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't process SCAN response from node", 0 TSRMLS_CC); @@ -2571,7 +2571,7 @@ PHP_METHOD(RedisCluster, scan) { efree(cmd); num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value)); - } while(c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0); + } while (c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0); Z_LVAL_P(z_it) = it; } @@ -2656,15 +2656,15 @@ PHP_METHOD(RedisCluster, lastsave) { PHP_METHOD(RedisCluster, info) { redisCluster *c = GET_CONTEXT(); REDIS_REPLY_TYPE rtype; - char *cmd, *opt=NULL; + char *cmd, *opt = NULL; int cmd_len; strlen_t opt_len = 0; void *ctx = NULL; zval *z_arg; short slot; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &z_arg, &opt, - &opt_len)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &z_arg, &opt, + &opt_len) == FAILURE) { RETURN_FALSE; } @@ -2684,7 +2684,7 @@ PHP_METHOD(RedisCluster, info) { } rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; - if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC)<0) { + if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC) < 0) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send INFO command to specific node", 0 TSRMLS_CC); efree(cmd); @@ -2708,7 +2708,7 @@ PHP_METHOD(RedisCluster, info) { */ PHP_METHOD(RedisCluster, client) { redisCluster *c = GET_CONTEXT(); - char *cmd, *opt=NULL, *arg=NULL; + char *cmd, *opt = NULL, *arg = NULL; int cmd_len; strlen_t opt_len, arg_len = 0; REDIS_REPLY_TYPE rtype; @@ -2718,14 +2718,14 @@ PHP_METHOD(RedisCluster, client) { /* Parse args */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|s", &z_node, &opt, - &opt_len, &arg, &arg_len)==FAILURE) + &opt_len, &arg, &arg_len) == FAILURE) { RETURN_FALSE; } /* Make sure we can properly resolve the slot */ slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC); - if(slot<0) RETURN_FALSE; + if (slot < 0) RETURN_FALSE; /* Our return type and reply callback is different for all subcommands */ if (opt_len == 4 && !strncasecmp(opt, "list", 4)) { @@ -2749,7 +2749,7 @@ PHP_METHOD(RedisCluster, client) { if (ZEND_NUM_ARGS() == 3) { cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len); - } else if(ZEND_NUM_ARGS() == 2) { + } else if (ZEND_NUM_ARGS() == 2) { cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "CLIENT", "s", opt, opt_len); } else { @@ -2758,7 +2758,7 @@ PHP_METHOD(RedisCluster, client) { } /* Attempt to write our command */ - if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC)<0) { + if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC) < 0) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send CLIENT command to specific node", 0 TSRMLS_CC); efree(cmd); @@ -2933,8 +2933,8 @@ PHP_METHOD(RedisCluster, echo) { strlen_t msg_len; short slot; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &msg, - &msg_len)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &msg, + &msg_len) == FAILURE) { RETURN_FALSE; } @@ -2944,7 +2944,7 @@ PHP_METHOD(RedisCluster, echo) { /* Grab slot either by key or host/port */ slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); - if(slot<0) { + if (slot < 0) { RETURN_FALSE; } @@ -2953,7 +2953,7 @@ PHP_METHOD(RedisCluster, echo) { /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; - if(cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) { + if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC) < 0) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send commnad at the specificed node", 0 TSRMLS_CC); efree(cmd); @@ -2995,7 +2995,7 @@ PHP_METHOD(RedisCluster, rawcommand) { efree(z_args); RETURN_FALSE; } else if (redis_build_raw_cmd(&z_args[1], argc-1, &cmd, &cmd_len TSRMLS_CC) || - (slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC))<0) + (slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC)) < 0) { if (cmd) efree(cmd); efree(z_args); @@ -3007,7 +3007,7 @@ PHP_METHOD(RedisCluster, rawcommand) { /* Direct the command */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE; - if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) { + if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC) < 0) { zend_throw_exception(redis_cluster_exception_ce, "Unable to send command to the specified node", 0 TSRMLS_CC); efree(cmd); diff --git a/redis_commands.c b/redis_commands.c index be331fad55..7de086418b 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -151,7 +151,7 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, strlen_t arg_len; // Parse args - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) ==FAILURE) { return FAILURE; @@ -173,8 +173,8 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long expire; zval *z_val; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &key, &key_len, - &expire, &z_val)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &key, &key_len, + &expire, &z_val) == FAILURE) { return FAILURE; } @@ -193,8 +193,8 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len, val_len; zend_long lval; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &key, &key_len, - &lval, &val, &val_len)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &key, &key_len, + &lval, &val, &val_len) == FAILURE) { return FAILURE; } @@ -213,8 +213,8 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len; zval *z_val; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, - &z_val)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, + &z_val) == FAILURE) { return FAILURE; } @@ -232,8 +232,8 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *val; strlen_t key_len, val_len; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, - &val, &val_len)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, + &val, &val_len) == FAILURE) { return FAILURE; } @@ -252,8 +252,8 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *k, *v1, *v2; strlen_t klen, v1len, v2len; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &k, &klen, - &v1, &v1len, &v2, &v2len)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &k, &klen, + &v1, &v1len, &v2, &v2len) == FAILURE) { return FAILURE; } @@ -273,8 +273,8 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t k1len, k2len; int k1free, k2free; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &k1, &k1len, - &k2, &k2len)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &k1, &k1len, + &k2, &k2len) == FAILURE) { return FAILURE; } @@ -284,16 +284,16 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, k2free = redis_key_prefix(redis_sock, &k2, &k2len); // If a slot is requested, we can test that they hash the same - if(slot) { + if (slot) { // Slots where these keys resolve short slot1 = cluster_hash_key(k1, k1len); short slot2 = cluster_hash_key(k2, k2len); // Check if Redis would give us a CROSSLOT error - if(slot1 != slot2) { + if (slot1 != slot2) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Keys don't hash to the same slot"); - if(k1free) efree(k1); - if(k2free) efree(k2); + if (k1free) efree(k1); + if (k2free) efree(k2); return FAILURE; } @@ -360,8 +360,8 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len; zend_long val1, val2; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &key, &key_len, - &val1, &val2)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &key, &key_len, + &val1, &val2) == FAILURE) { return FAILURE; } @@ -379,7 +379,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; strlen_t key_len; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) ==FAILURE) { return FAILURE; @@ -399,8 +399,8 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len; double val; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &key, &key_len, - &val)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &key, &key_len, + &val) == FAILURE) { return FAILURE; } @@ -424,7 +424,7 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, redis_cmd_init_sstr(&cmdstr, argc, kw[type], strlen(kw[type])); // Append our key if it's not a regular SCAN command - if(type != TYPE_SCAN) { + if (type != TYPE_SCAN) { redis_cmd_append_sstr(&cmdstr, key, key_len); } @@ -432,13 +432,13 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, redis_cmd_append_sstr_long(&cmdstr, it); // Append count if we've got one - if(count) { + if (count) { redis_cmd_append_sstr(&cmdstr,"COUNT",sizeof("COUNT")-1); redis_cmd_append_sstr_long(&cmdstr, count); } // Append pattern if we've got one - if(pat_len) { + if (pat_len) { redis_cmd_append_sstr(&cmdstr,"MATCH",sizeof("MATCH")-1); redis_cmd_append_sstr(&cmdstr,pat,pat_len); } @@ -456,15 +456,15 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; strlen_t key_len; zend_long start, end; - zend_bool ws=0; + zend_bool ws = 0; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|b", &key, &key_len, - &start, &end, &ws)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|b", &key, &key_len, + &start, &end, &ws) == FAILURE) { return FAILURE; } - if(ws) { + if (ws) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdds", key, key_len, start, end, "WITHSCORES", sizeof("WITHSCORES") - 1); } else { @@ -488,7 +488,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *slot, void **ctx) { char *key, *start, *end; - int has_limit=0; + int has_limit = 0; long offset, count; strlen_t key_len, start_len, end_len; zval *z_opt=NULL, *z_ele; @@ -498,7 +498,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, PHPREDIS_NOTUSED(idx); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) ==FAILURE) { @@ -506,7 +506,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Check for an options array - if(z_opt && Z_TYPE_P(z_opt)==IS_ARRAY) { + if (z_opt && Z_TYPE_P(z_opt) == IS_ARRAY) { ht_opt = Z_ARRVAL_P(z_opt); ZEND_HASH_FOREACH_KEY_VAL(ht_opt, idx, zkey, z_ele) { /* All options require a string key type */ @@ -568,9 +568,9 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; // Parse args - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a!s", &key, + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a!s", &key, &key_len, &z_keys, &z_weights, &agg_op, - &agg_op_len)==FAILURE) + &agg_op_len) == FAILURE) { return FAILURE; } @@ -579,16 +579,16 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ht_keys = Z_ARRVAL_P(z_keys); // Nothing to do if there aren't any - if((keys_count = zend_hash_num_elements(ht_keys))==0) { + if ((keys_count = zend_hash_num_elements(ht_keys)) == 0) { return FAILURE; } else { argc += keys_count; } // Handle WEIGHTS - if(z_weights != NULL) { + if (z_weights != NULL) { ht_weights = Z_ARRVAL_P(z_weights); - if(zend_hash_num_elements(ht_weights) != keys_count) { + if (zend_hash_num_elements(ht_weights) != keys_count) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "WEIGHTS and keys array should be the same size!"); return FAILURE; @@ -599,8 +599,8 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // AGGREGATE option - if(agg_op_len != 0) { - if(strncasecmp(agg_op, "SUM", sizeof("SUM")) && + if (agg_op_len != 0) { + if (strncasecmp(agg_op, "SUM", sizeof("SUM")) && strncasecmp(agg_op, "MIN", sizeof("MIN")) && strncasecmp(agg_op, "MAX", sizeof("MAX"))) { @@ -623,7 +623,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set our slot, free the key if we prefixed it CMD_SET_SLOT(slot,key,key_len); - if(key_free) efree(key); + if (key_free) efree(key); // Process input keys ZEND_HASH_FOREACH_VAL(ht_keys, z_ele) { @@ -635,12 +635,12 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_free = redis_key_prefix(redis_sock, &key, &key_len); // If we're in Cluster mode, verify the slot is the same - if(slot && *slot != cluster_hash_key(key,key_len)) { + if (slot && *slot != cluster_hash_key(key,key_len)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "All keys don't hash to the same slot!"); efree(cmdstr.c); zend_string_release(zstr); - if(key_free) efree(key); + if (key_free) efree(key); return FAILURE; } @@ -649,11 +649,11 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Cleanup zend_string_release(zstr); - if(key_free) efree(key); + if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); // Weights - if(ht_weights != NULL) { + if (ht_weights != NULL) { redis_cmd_append_sstr(&cmdstr, "WEIGHTS", sizeof("WEIGHTS")-1); // Process our weights @@ -696,7 +696,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // AGGREGATE - if(agg_op_len != 0) { + if (agg_op_len != 0) { redis_cmd_append_sstr(&cmdstr, "AGGREGATE", sizeof("AGGREGATE")-1); redis_cmd_append_sstr(&cmdstr, agg_op, agg_op_len); } @@ -721,8 +721,8 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_free; char *key; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &z_arr, - &(sctx->cb), &(sctx->cb_cache))==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &z_arr, + &(sctx->cb), &(sctx->cb_cache)) == FAILURE) { efree(sctx); return FAILURE; @@ -732,7 +732,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, sctx->kw = kw; sctx->argc = zend_hash_num_elements(ht_chan); - if(sctx->argc==0) { + if (sctx->argc == 0) { efree(sctx); return FAILURE; } @@ -755,7 +755,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string_release(zstr); // Free our key if it was prefixed - if(key_free) efree(key); + if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); // Push values out @@ -779,7 +779,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr)==FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr) == FAILURE) { efree(sctx); return FAILURE; } @@ -787,7 +787,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ht_arr = Z_ARRVAL_P(z_arr); sctx->argc = zend_hash_num_elements(ht_arr); - if(sctx->argc == 0) { + if (sctx->argc == 0) { efree(sctx); return FAILURE; } @@ -801,7 +801,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); redis_cmd_append_sstr(&cmdstr, key, key_len); - if(key_free) efree(key); + if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); // Push out vals @@ -828,8 +828,8 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - if(zend_parse_parameters(argc TSRMLS_CC, "sss|ll", &key, &key_len, &min, &min_len, - &max, &max_len, &offset, &count)==FAILURE) + if (zend_parse_parameters(argc TSRMLS_CC, "sss|ll", &key, &key_len, &min, &min_len, + &max, &max_len, &offset, &count) == FAILURE) { return FAILURE; } @@ -867,14 +867,14 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len, min_len, max_len; /* Parse args */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, - &min, &min_len, &max, &max_len)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, + &min, &min_len, &max, &max_len) == FAILURE) { return FAILURE; } /* Quick sanity check on min/max */ - if(min_len<1 || max_len<1 || (min[0]!='(' && min[0]!='[') || + if (min_len<1 || max_len<1 || (min[0]!='(' && min[0]!='[') || (max[0]!='(' && max[0]!='[')) { php_error_docref(NULL TSRMLS_CC, E_WARNING, @@ -904,8 +904,8 @@ int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw short prevslot = -1; /* Parse args */ - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, - &z_arr, &num_keys)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, + &z_arr, &num_keys) == FAILURE) { return FAILURE; } @@ -967,7 +967,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int argc = ZEND_NUM_ARGS(); // We at least need a key and one value - if(argc < 2) { + if (argc < 2) { return FAILURE; } @@ -1063,7 +1063,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short kslot = -1; zend_string *zstr; - if(argc < min_argc) { + if (argc < min_argc) { zend_wrong_param_count(TSRMLS_C); return FAILURE; } @@ -1076,19 +1076,19 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Handle our "single array" case - if(has_timeout == 0) { - single_array = argc==1 && Z_TYPE(z_args[0])==IS_ARRAY; + if (has_timeout == 0) { + single_array = argc==1 && Z_TYPE(z_args[0]) == IS_ARRAY; } else { - single_array = argc==2 && Z_TYPE(z_args[0])==IS_ARRAY && - Z_TYPE(z_args[1])==IS_LONG; + single_array = argc==2 && Z_TYPE(z_args[0]) == IS_ARRAY && + Z_TYPE(z_args[1]) == IS_LONG; timeout = Z_LVAL(z_args[1]); } // If we're running a single array, rework args - if(single_array) { + if (single_array) { ht_arr = Z_ARRVAL(z_args[0]); argc = zend_hash_num_elements(ht_arr); - if(has_timeout) argc++; + if (has_timeout) argc++; efree(z_args); z_args = NULL; @@ -1099,7 +1099,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Begin construction of our command redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); - if(single_array) { + if (single_array) { ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { zstr = zval_get_string(z_ele); key = ZSTR_VAL(zstr); @@ -1107,12 +1107,12 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, key_free = redis_key_prefix(redis_sock, &key, &key_len); // Protect against CROSSLOT errors - if(slot) { - if(kslot == -1) { + if (slot) { + if (kslot == -1) { kslot = cluster_hash_key(key, key_len); - } else if(cluster_hash_key(key,key_len)!=kslot) { + } else if (cluster_hash_key(key,key_len)!=kslot) { zend_string_release(zstr); - if(key_free) efree(key); + if (key_free) efree(key); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not all keys hash to the same slot!"); return FAILURE; @@ -1122,13 +1122,13 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Append this key, free it if we prefixed redis_cmd_append_sstr(&cmdstr, key, key_len); zend_string_release(zstr); - if(key_free) efree(key); + if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); - if(has_timeout) { + if (has_timeout) { redis_cmd_append_sstr_long(&cmdstr, timeout); } } else { - if(has_timeout && Z_TYPE(z_args[argc-1])!=IS_LONG) { + if (has_timeout && Z_TYPE(z_args[argc-1])!=IS_LONG) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Timeout value must be a LONG"); efree(z_args); @@ -1136,7 +1136,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } tail = has_timeout ? argc-1 : argc; - for(i=0;i= 2.6.12 set options */ - if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY + if (z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY && Z_TYPE_P(z_opts) != IS_NULL) { return FAILURE; } // Check for an options array - if(z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { + if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { HashTable *kt = Z_ARRVAL_P(z_opts); zend_string *zkey; ulong idx; @@ -1239,7 +1239,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, set_type = Z_STRVAL_P(v); } } ZEND_HASH_FOREACH_END(); - } else if(z_opts && Z_TYPE_P(z_opts) == IS_LONG) { + } else if (z_opts && Z_TYPE_P(z_opts) == IS_LONG) { /* Grab expiry and fail if it's < 1 */ expire = Z_LVAL_P(z_opts); if (expire < 1) { @@ -1248,19 +1248,19 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Now let's construct the command we want */ - if(exp_type && set_type) { + if (exp_type && set_type) { /* SET NX|XX PX|EX */ *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvsls", key, key_len, z_value, exp_type, 2, expire, set_type, 2); - } else if(exp_type) { + } else if (exp_type) { /* SET PX|EX */ *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvsl", key, key_len, z_value, exp_type, 2, expire); - } else if(set_type) { + } else if (set_type) { /* SET NX|XX */ *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvs", key, key_len, z_value, set_type, 2); - } else if(expire > 0) { + } else if (expire > 0) { /* Backward compatible SETEX redirection */ *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, z_value); @@ -1282,8 +1282,8 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short slot1, slot2; zend_long timeout; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key1, &key1_len, - &key2, &key2_len, &timeout)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key1, &key1_len, + &key2, &key2_len, &timeout) == FAILURE) { return FAILURE; } @@ -1296,11 +1296,11 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (slot) { slot1 = cluster_hash_key(key1, key1_len); slot2 = cluster_hash_key(key2, key2_len); - if(slot1 != slot2) { + if (slot1 != slot2) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Keys hash to different slots!"); - if(key1_free) efree(key1); - if(key2_free) efree(key2); + if (key1_free) efree(key1); + if (key2_free) efree(key2); return FAILURE; } @@ -1309,7 +1309,7 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Consistency with Redis, if timeout < 0 use RPOPLPUSH - if(timeout < 0) { + if (timeout < 0) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "RPOPLPUSH", "ss", key1, key1_len, key2, key2_len); } else { @@ -1339,7 +1339,7 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, zend_long val = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, - &val)==FAILURE) + &val) == FAILURE) { return FAILURE; } @@ -1388,8 +1388,8 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len, mem_len; zend_long byval; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key, &key_len, - &mem, &mem_len, &byval)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key, &key_len, + &mem, &mem_len, &byval) == FAILURE) { return FAILURE; } @@ -1409,8 +1409,8 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len, mem_len; double byval; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssd", &key, &key_len, - &mem, &mem_len, &byval)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssd", &key, &key_len, + &mem, &mem_len, &byval) == FAILURE) { return FAILURE; } @@ -1429,14 +1429,14 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key; zval *z_arr, *z_mems, *z_mem; - int i, count, valid=0, key_free; + int i, count, valid = 0, key_free; strlen_t key_len; HashTable *ht_arr; smart_string cmdstr = {0}; // Parse arguments - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, - &z_arr)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + &z_arr) == FAILURE) { return FAILURE; } @@ -1445,7 +1445,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ht_arr = Z_ARRVAL_P(z_arr); // We can abort if we have no elements - if((count = zend_hash_num_elements(ht_arr))==0) { + if ((count = zend_hash_num_elements(ht_arr)) == 0) { return FAILURE; } @@ -1468,7 +1468,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); // If nothing was valid, fail - if(valid == 0) { + if (valid == 0) { efree(z_mems); return FAILURE; } @@ -1486,7 +1486,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, key, key_len); // Iterate over members, appending as arguments - for(i=0;i BITOP_MAX_OFFSET) { + if (offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Invalid OFFSET for bitop command (must be between 0-2^32-1)"); return FAILURE; @@ -1976,14 +1976,14 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len, pos_len; zval *z_val, *z_pivot; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sszz", &key, &key_len, - &pos, &pos_len, &z_pivot, &z_val)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sszz", &key, &key_len, + &pos, &pos_len, &z_pivot, &z_val) == FAILURE) { return FAILURE; } // Validate position - if(strncasecmp(pos, "after", 5) && strncasecmp(pos, "before", 6)) { + if (strncasecmp(pos, "after", 5) && strncasecmp(pos, "before", 6)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Position must be either 'BEFORE' or 'AFTER'"); return FAILURE; @@ -2006,8 +2006,8 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long count = 0; zval *z_val; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &key, &key_len, - &z_val, &count)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &key, &key_len, + &z_val, &count) == FAILURE) { return FAILURE; } @@ -2027,8 +2027,8 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int src_free, dst_free; zval *z_val; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &src, &src_len, - &dst, &dst_len, &z_val)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &src, &src_len, + &dst, &dst_len, &z_val) == FAILURE) { return FAILURE; } @@ -2040,11 +2040,11 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (slot) { short slot1 = cluster_hash_key(src, src_len); short slot2 = cluster_hash_key(dst, dst_len); - if(slot1 != slot2) { + if (slot1 != slot2) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Source and destination keys don't hash to the same slot!"); - if(src_free) efree(src); - if(dst_free) efree(dst); + if (src_free) efree(src); + if (dst_free) efree(dst); return FAILURE; } *slot = slot1; @@ -2055,8 +2055,8 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, dst_len, z_val); // Cleanup - if(src_free) efree(src); - if(dst_free) efree(dst); + if (src_free) efree(src); + if (dst_free) efree(dst); // Succcess! return SUCCESS; @@ -2070,8 +2070,8 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len, mem_len; zval *z_val; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &key, &key_len, - &mem, &mem_len, &z_val)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &key, &key_len, + &mem, &mem_len, &z_val) == FAILURE) { return FAILURE; } @@ -2108,8 +2108,8 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len; zend_long count; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, - &count)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, + &count) == FAILURE) { return FAILURE; } @@ -2136,8 +2136,8 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, double incrby; zval *z_val; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sdz", &key, &key_len, - &incrby, &z_val)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sdz", &key, &key_len, + &incrby, &z_val) == FAILURE) { return FAILURE; } @@ -2159,8 +2159,8 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strlen_t key_len; int key_free; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &key, &key_len, - &z_opts)==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &key, &key_len, + &z_opts) == FAILURE) { return FAILURE; } @@ -2199,7 +2199,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) && Z_TYPE_P(z_ele) == IS_STRING ) { // "BY" option is disabled in cluster - if(slot) { + if (slot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "SORT BY option is not allowed in Redis Cluster"); zval_dtor(&z_argv); @@ -2229,7 +2229,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int cross_slot = slot && *slot != cluster_hash_key( Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); - if(cross_slot) { + if (cross_slot) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Error, SORT key and STORE key have different slots!"); zval_dtor(&z_argv); @@ -2250,7 +2250,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) && (Z_TYPE_P(z_ele) == IS_STRING || Z_TYPE_P(z_ele) == IS_ARRAY) ) { // Disabled in cluster - if(slot) { + if (slot) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "GET option for SORT disabled in Redis Cluster"); zval_dtor(&z_argv); @@ -2262,7 +2262,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, add_next_index_stringl(&z_argv, "GET", sizeof("GET") - 1); add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { - int added=0; + int added = 0; zval *z_key; ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_ele), z_key) { @@ -2279,7 +2279,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); // Make sure we were able to add at least one - if(added==0) { + if (added == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array of GET values requested, but none are valid"); zval_dtor(&z_argv); @@ -2378,7 +2378,7 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string *zstr; // We need at least KEY and one member - if(argc < 2) { + if (argc < 2) { return FAILURE; } @@ -2404,10 +2404,10 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Set our slot, free key if we prefixed it CMD_SET_SLOT(slot,arg,arg_len); zend_string_release(zstr); - if(arg_free) efree(arg); + if (arg_free) efree(arg); // Iterate through the members we're removing - for(i=1;iread_timeout = atof(val_str); - if(redis_sock->stream) { + if (redis_sock->stream) { read_tv.tv_sec = (time_t)redis_sock->read_timeout; read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); @@ -3154,7 +3154,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, if (redis_sock->tcp_keepalive == tcp_keepalive) { RETURN_TRUE; } - if(redis_sock->stream) { + if (redis_sock->stream) { /* set TCP_KEEPALIVE */ sock = (php_netstream_data_t*)redis_sock->stream->abstract; if (setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&tcp_keepalive, @@ -3166,7 +3166,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_TRUE; case REDIS_OPT_SCAN: val_long = atol(val_str); - if(val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) { + if (val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) { redis_sock->scan = val_long; RETURN_TRUE; } @@ -3191,7 +3191,7 @@ void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *key; strlen_t key_len; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) ==FAILURE) { RETURN_FALSE; @@ -3213,14 +3213,14 @@ void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, char *val; strlen_t val_len; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_val)==FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_val) == FAILURE) { RETURN_FALSE; } int val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); RETVAL_STRINGL(val, val_len); - if(val_free) efree(val); + if (val_free) efree(val); } void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, @@ -3230,7 +3230,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, strlen_t value_len; // Parse our arguments - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE) { RETURN_FALSE; diff --git a/redis_session.c b/redis_session.c index 86004b7f4c..2a36c625a9 100644 --- a/redis_session.c +++ b/redis_session.c @@ -97,12 +97,12 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { redis_pool_member *rpm, *next; rpm = pool->head; - while(rpm) { + while (rpm) { next = rpm->next; redis_sock_disconnect(rpm->redis_sock TSRMLS_CC); redis_free_socket(rpm->redis_sock); - if(rpm->prefix) zend_string_release(rpm->prefix); - if(rpm->auth) zend_string_release(rpm->auth); + if (rpm->prefix) zend_string_release(rpm->prefix); + if (rpm->auth) zend_string_release(rpm->auth); efree(rpm); rpm = next; } @@ -121,7 +121,7 @@ redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { } cmd_len = REDIS_SPPRINTF(&cmd, "AUTH", "S", rpm->auth); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { efree(response); } @@ -136,7 +136,7 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { int response_len, cmd_len; cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", rpm->database); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { efree(response); } @@ -154,16 +154,16 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { redis_pool_member *rpm = pool->head; for(i = 0; i < pool->totalWeight;) { - if(pos >= i && pos < i + rpm->weight) { + if (pos >= i && pos < i + rpm->weight) { int needs_auth = 0; if (rpm->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } redis_sock_server_open(rpm->redis_sock TSRMLS_CC); - if(needs_auth) { + if (needs_auth) { redis_pool_member_auth(rpm TSRMLS_CC); } - if(rpm->database >= 0) { /* default is -1 which leaves the choice to redis. */ + if (rpm->database >= 0) { /* default is -1 which leaves the choice to redis. */ redis_pool_member_select(rpm TSRMLS_CC); } @@ -186,9 +186,9 @@ PS_OPEN_FUNC(redis) redis_pool *pool = redis_pool_new(TSRMLS_C); - for (i=0,j=0,path_len=strlen(save_path); ihost) { + if (url->host) { #if (PHP_VERSION_ID < 70300) redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); #else @@ -313,7 +313,7 @@ PS_CLOSE_FUNC(redis) { redis_pool *pool = PS_GET_MOD_DATA(); - if(pool){ + if (pool){ redis_pool_free(pool TSRMLS_CC); PS_SET_MOD_DATA(NULL); } @@ -329,7 +329,7 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses char *prefix = default_prefix; size_t prefix_len = sizeof(default_prefix)-1; - if(rpm->prefix) { + if (rpm->prefix) { prefix = ZSTR_VAL(rpm->prefix); prefix_len = ZSTR_LEN(rpm->prefix); } @@ -362,7 +362,7 @@ PS_READ_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if(!rpm || !redis_sock){ + if (!rpm || !redis_sock){ return FAILURE; } @@ -371,7 +371,7 @@ PS_READ_FUNC(redis) cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); efree(resp); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; } @@ -423,7 +423,7 @@ PS_WRITE_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if(!rpm || !redis_sock){ + if (!rpm || !redis_sock){ return FAILURE; } @@ -432,7 +432,7 @@ PS_WRITE_FUNC(redis) cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), sval, svallen); efree(session); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; } @@ -443,7 +443,7 @@ PS_WRITE_FUNC(redis) return FAILURE; } - if(response_len == 3 && strncmp(response, "+OK", 3) == 0) { + if (response_len == 3 && strncmp(response, "+OK", 3) == 0) { efree(response); return SUCCESS; } else { @@ -470,7 +470,7 @@ PS_DESTROY_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if(!rpm || !redis_sock){ + if (!rpm || !redis_sock){ return FAILURE; } @@ -478,7 +478,7 @@ PS_DESTROY_FUNC(redis) session = redis_session_key(rpm, skey, skeylen, &session_len); cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "s", session, session_len); efree(session); - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; } @@ -489,7 +489,7 @@ PS_DESTROY_FUNC(redis) return FAILURE; } - if(response_len == 2 && response[0] == ':' && (response[1] == '0' || response[1] == '1')) { + if (response_len == 2 && response[0] == ':' && (response[1] == '0' || response[1] == '1')) { efree(response); return SUCCESS; } else { @@ -663,7 +663,7 @@ PS_READ_FUNC(rediscluster) { /* Attempt to kick off our command */ c->readonly = 1; - if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC) < 0 || c->err) { efree(cmd); return FAILURE; } @@ -727,7 +727,7 @@ PS_WRITE_FUNC(rediscluster) { /* Attempt to send command */ c->readonly = 0; - if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC) < 0 || c->err) { efree(cmd); return FAILURE; } @@ -767,7 +767,7 @@ PS_DESTROY_FUNC(rediscluster) { efree(skey); /* Attempt to send command */ - if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) { + if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC) < 0 || c->err) { efree(cmd); return FAILURE; } From f6e1eb85f7917b0bde9b0a56679589fcca738225 Mon Sep 17 00:00:00 2001 From: 719media <719media@users.noreply.github.com> Date: Tue, 20 Feb 2018 14:57:26 -0800 Subject: [PATCH 0931/1986] clarify mathematical expression --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 4006616b51..a3d9541687 100644 --- a/README.markdown +++ b/README.markdown @@ -88,7 +88,7 @@ session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeou `session.save_path` can have a simple `host:port` format too, but you need to provide the `tcp://` scheme if you want to use the parameters. The following parameters are available: -* weight (integer): the weight of a host is used in comparison with the others in order to customize the session distribution on several hosts. If host A has twice the weight of host B, it will get twice the amount of sessions. In the example, *host1* stores 20% of all the sessions (1/(1+2+2)) while *host2* and *host3* each store 40% (2/1+2+2). The target host is determined once and for all at the start of the session, and doesn't change. The default weight is 1. +* weight (integer): the weight of a host is used in comparison with the others in order to customize the session distribution on several hosts. If host A has twice the weight of host B, it will get twice the amount of sessions. In the example, *host1* stores 20% of all the sessions (1/(1+2+2)) while *host2* and *host3* each store 40% (2/(1+2+2)). The target host is determined once and for all at the start of the session, and doesn't change. The default weight is 1. * timeout (float): the connection timeout to a redis host, expressed in seconds. If the host is unreachable in that amount of time, the session storage will be unavailable for the client. The default timeout is very high (86400 seconds). * persistent (integer, should be 1 or 0): defines if a persistent connection should be used. **(experimental setting)** * prefix (string, defaults to "PHPREDIS_SESSION:"): used as a prefix to the Redis key in which the session is stored. The key is composed of the prefix followed by the session ID. From 9240293546765386b827d01739f899e60045b78a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 21 Feb 2018 10:11:13 +0200 Subject: [PATCH 0932/1986] Merge branch 'issue.1306' into develop --- package.xml | 114 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 31 deletions(-) diff --git a/package.xml b/package.xml index bec916c118..b921107f46 100644 --- a/package.xml +++ b/package.xml @@ -27,43 +27,38 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2017-09-27 + 2018-02-07 - 3.1.4 - 3.1.4 + 4.0.0RC1 + 4.0.0RC1 - stable - stable + alpha + alpha PHP - phpredis 3.1.4 - - The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are - also many bugfixes and minor improvements to the api, listed below: - - * Allow mixing MULTI and PIPELINE modes (experimental)! [5874b0] (Pavlo Yatsukhnenko) - - * Added integration for coverty static analysis and fixed several warnings - [faac8b0, eff7398, 4766c25, 0438ab4, 1e0b065, 733732a, 26eeda5, 735025, 42f1c9, af71d4] (Pavlo Yatsukhnenko) - * Fixed link to redis cluster documentation [3b0b06] (Pavlo Yatsukhnenko) - * Remove unused PHP_RINIT and PHP_RSHUTDOWN functions [c760bf] (Pavlo Yatsukhnenko) - * Removed duplicate HGET in redis array hash table, formatting [d0b9c5] (Pavlo Yatsukhnenko) - * Treat NULL bulk as success for session read [659450] (Pavlo Yatsukhnenko) - * Refactor redis_send_discard [ea15ce] (Pavlo Yatsukhnenko) - * Updated runtime exception handling [8dcaa4, 7c1407] (Pavlo Yatsukhnenko) - * Added a github issue template [61aba9] (Pavlo Yatsukhnenko) - * Initialize gc member of zend_string [37f569) (Pavlo Yatsukhnenko) - * Fix valgrind warnings [471ce07, 1ab89e1, b624a8b] (Pavlo Yatsukhnenko) - * Fix php5/php7 compatibility layer [1ab89e, 4e3225] (Pavlo Yatsukhnenko) - * Fix typo in README.markdown [e47e44] (Mark Shehata) - * Improve redis array rehash [577a91] (Pavlo Yatsukhnenko) - * Change redis array pure_cmds from zval to hashtable [a56ed7] (Pavlo Yatsukhnenko) - * Don't try to set TCP_NODELAY on a unix socket and don't warn on multiple - calls to pipeline [d11798, 77aeba] (Michael Grunder) - * Use zend_string rather than char* for various context fields (err, prefix, etc) [2bf7b2] (Pavlo Yatsukhnenko) - * Various other library fixes [142b51, 4452f6, e672f4, 658ee3, c9df77, 4a0a46] (Pavlo Yatsukhnenko) + phpredis 4.0.0RC1 + + *** WARNING! THIS RELEASE CONTAINS BRAKING API CHANGES! *** + + * Add proper ARGINFO for all methods. (Pavlo Yatsukhnenko, Michael Grunder) + * Let EXISTS take multiple keys [cccc39] (Michael Grunder) + * Use zend_string as returning value for ra_extract_key and ra_call_extractor [9cd05911] (Pavlo Yatsukhnenko) + * Implement SWAPDB and UNLINK commands [84f1f28b, 9e65c429] (Michael Grunder) + * Return real connection error as exception [5b9c0c60] (Pavlo Yatsukhnenko, Michael Grunder) + * Disallow using empty string as session name. [485db46f] (Pavlo Yatsukhnenko) + * Use zend_string for storing auth and prefix members [4b8336f7] (Pavlo Yatsukhnenko) + * The element of z_seeds may be a reference on php7 [367bc6aa, 1e63717a] (@janic716) + * Avoid connection in helper methods [91e9cfe1] (Pavlo Yatsukhnenko) + * Add tcp_keepalive option to redis sock [68c58513, 5101172a, 010336d5, 51e48729] (@git-hulk, Michael Grunder) + * More robust GEORADIUS COUNT validation [f7edee5d] (Michael Grunder) + * Add LZF compression (experimental) [e2c51251, 8cb2d5bd, 8657557] (Pavlo Yatsukhnenko) + * Allow to use empty string as persistant_id [ec4fd1bd] (Pavlo Yatsukhnenko) + * Don't use convert_to_string in redis_hmget_cmd [99335d6] (Pavlo Yatsukhnenko) + * Allow mixing MULTI and PIPELINE modes (experimental) [5874b0] (Pavlo Yatsukhnenko) + * PHP >=7.3.0 uses zend_string to store `php_url` elements [b566fb44] (@fmk) + * Documentation improvements (Michael Grunder, @TomA-R) @@ -96,6 +91,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + @@ -127,6 +123,62 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + alphaalpha + 4.0.0RC14.0.0RC1 + 2018-02-07 + + phpredis 4.0.0RC1 + + *** WARNING! THIS RELEASE CONTAINS BRAKING API CHANGES! *** + + * Add proper ARGINFO for all methods. (Pavlo Yatsukhnenko, Michael Grunder) + * Let EXISTS take multiple keys [cccc39] (Michael Grunder) + * Use zend_string as returning value for ra_extract_key and ra_call_extractor [9cd05911] (Pavlo Yatsukhnenko) + * Implement SWAPDB and UNLINK commands [84f1f28b, 9e65c429] (Michael Grunder) + * Return real connection error as exception [5b9c0c60] (Pavlo Yatsukhnenko, Michael Grunder) + * Disallow using empty string as session name. [485db46f] (Pavlo Yatsukhnenko) + * Use zend_string for storing auth and prefix members [4b8336f7] (Pavlo Yatsukhnenko) + * Avoid connection in helper methods [91e9cfe1] (Pavlo Yatsukhnenko) + * Add tcp_keepalive option to redis sock [68c58513, 5101172a, 010336d5, 51e48729] (@git-hulk, Michael Grunder) + * More robust GEORADIUS COUNT validation [f7edee5d] (Michael Grunder) + * Add LZF compression (experimental) [e2c51251, 8cb2d5bd, 8657557] (Pavlo Yatsukhnenko) + * Allow to use empty string as persistant_id [ec4fd1bd] (Pavlo Yatsukhnenko) + * Don't use convert_to_string in redis_hmget_cmd [99335d6] (Pavlo Yatsukhnenko) + * Documentation improvements (Michael Grunder, @TomA-R) + + + + + stablestable + 3.1.63.1.6 + 2018-01-03 + + phpredis 3.1.6 + + This release conains only fix of RedisArray distributor hashing function + which was broken in 3.1.4. Huge thanks to @rexchen123 + + + + + stablestable + 3.1.53.1.5 + 2017-12-20 + + phpredis 3.1.5 + + This is interim release which contains only bug fixes. + + * Fix segfault when extending Redis class in PHP 5 [d23eff] (Pavlo Yatsukhnenko) + * Fix RedisCluster constructor with PHP 7 strict scalar type [5c21d7] (Pavlo Yatsukhnenko) + * Allow to use empty string as persistant_id [344de5] (Pavlo Yatsukhnenko) + * Fix cluster_init_seeds. [db1347] (@adlagares) + * Fix z_seeds may be a reference [42581a] (@janic716) + * PHP >=7.3 uses zend_string for php_url elements [b566fb] (@fmk) + + + stablestable 3.1.43.1.4 From fcc91a293a13b3a9f721f65da0595dc3e5eae3ca Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 26 Feb 2018 10:36:15 +0200 Subject: [PATCH 0933/1986] Show git revision in phpinfo --- config.m4 | 5 +++++ redis.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/config.m4 b/config.m4 index ef4d9e0919..f8703037c8 100755 --- a/config.m4 +++ b/config.m4 @@ -97,6 +97,11 @@ dnl Check for igbinary fi fi + AC_CHECK_PROG([GIT], [git], [yes], [no]) + if test "$GIT" == "yes" && test -d "$srcdir/.git"; then + AC_DEFINE_UNQUOTED(GIT_REVISION, ["$(git log -1 --format=%H)"], [ ]) + fi + dnl # --with-redis -> check with-path dnl SEARCH_PATH="/usr/local /usr" # you might want to change this dnl SEARCH_FOR="/include/redis.h" # you most likely want to change this diff --git a/redis.c b/redis.c index 710ed969dc..4c645ca63c 100644 --- a/redis.c +++ b/redis.c @@ -814,6 +814,9 @@ PHP_MINFO_FUNCTION(redis) php_info_print_table_start(); php_info_print_table_header(2, "Redis Support", "enabled"); php_info_print_table_row(2, "Redis Version", PHP_REDIS_VERSION); +#ifdef GIT_REVISION + php_info_print_table_row(2, "Git revision", "$Id: " GIT_REVISION " $"); +#endif #ifdef HAVE_REDIS_IGBINARY php_info_print_table_row(2, "Available serializers", "php, igbinary"); #else From f4cb95ceef6e90375e7248e7419ee60f0226a6fd Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 1 Mar 2018 11:06:41 +0100 Subject: [PATCH 0934/1986] Fixed non fully qualified session key on regenerate --- redis_session.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index 5abda35afb..4cc7b2eb50 100644 --- a/redis_session.c +++ b/redis_session.c @@ -619,11 +619,14 @@ PS_CREATE_SID_FUNC(redis) #endif } + int resp_len; #if (PHP_MAJOR_VERSION < 7) - pool->lock_status->session_key = sid; + char *full_session_key = redis_session_key(rpm, sid, strlen(sid), &resp_len); #else - pool->lock_status->session_key = estrdup(ZSTR_VAL(sid)); + char *full_session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid), &resp_len); #endif + pool->lock_status->session_key = full_session_key; + if (lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) == SUCCESS) { return sid; } From d8a44723b5a6ab177ea93fff22b6ea4609e28fee Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 1 Mar 2018 11:14:38 +0100 Subject: [PATCH 0935/1986] Removed unused variables + added assertion if session key changed --- tests/RedisTest.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 6b8858c738..38a747cf0f 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5292,40 +5292,44 @@ public function testMultipleConnect() { public function testSession_regenerateSessionId_noLock_noDestroy() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); - $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, '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(); - $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, false, true); - + + $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_withLock_noDestroy() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); - $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, true); - + + $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } public function testSession_regenerateSessionId_withLock_withDestroy() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); - $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); $newSessionId = $this->regenerateSessionId($sessionId, true, true); + $this->assertTrue($newSessionId !== $sessionId); $this->assertEquals('bar', $this->getSessionData($newSessionId)); } From 948e4b8dc398b24fe5b66404e7d76a510bedba94 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 1 Mar 2018 11:30:24 +0100 Subject: [PATCH 0936/1986] Added tests for using session proxy handler --- tests/RedisTest.php | 51 +++++++++++++++++++++++++++++++++-- tests/regenerateSessionId.php | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 38a747cf0f..7189e52bc2 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5333,6 +5333,50 @@ public function testSession_regenerateSessionId_withLock_withDestroy() { $this->assertEquals('bar', $this->getSessionData($newSessionId)); } + public function testSession_regenerateSessionId_noLock_noDestroy_withProxy() { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + + $newSessionId = $this->regenerateSessionId($sessionId, false, false, true); + + $this->assertTrue($newSessionId !== $sessionId); + $this->assertEquals('bar', $this->getSessionData($newSessionId)); + } + + public function testSession_regenerateSessionId_noLock_withDestroy_withProxy() { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + + $newSessionId = $this->regenerateSessionId($sessionId, false, true, true); + + $this->assertTrue($newSessionId !== $sessionId); + $this->assertEquals('bar', $this->getSessionData($newSessionId)); + } + + public function testSession_regenerateSessionId_withLock_noDestroy_withProxy() { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + + $newSessionId = $this->regenerateSessionId($sessionId, true, false, true); + + $this->assertTrue($newSessionId !== $sessionId); + $this->assertEquals('bar', $this->getSessionData($newSessionId)); + } + + public function testSession_regenerateSessionId_withLock_withDestroy_withProxy() { + $this->setSessionHandler(); + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + + $newSessionId = $this->regenerateSessionId($sessionId, true, true, true); + + $this->assertTrue($newSessionId !== $sessionId); + $this->assertEquals('bar', $this->getSessionData($newSessionId)); + } + private function setSessionHandler() { $host = $this->getHost() ?: 'localhost'; @@ -5406,12 +5450,15 @@ private function getSessionData($sessionId) /** * @param string $sessionId + * @param bool $locking + * @param bool $destroyPrevious + * @param bool $sessionProxy * * @return string */ - private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false) + private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false, $sessionProxy = false) { - $args = array_map('escapeshellarg', array($sessionId, $locking, $destroyPrevious)); + $args = array_map('escapeshellarg', array($sessionId, $locking, $destroyPrevious, $sessionProxy)); $command = 'php --no-php-ini --define extension=igbinary.so --define extension=' . __DIR__ . '/../modules/redis.so ' . __DIR__ . '/regenerateSessionId.php ' . escapeshellarg($this->getHost()) . ' ' . implode(' ', $args); diff --git a/tests/regenerateSessionId.php b/tests/regenerateSessionId.php index c232c8c795..33b641dbde 100644 --- a/tests/regenerateSessionId.php +++ b/tests/regenerateSessionId.php @@ -5,6 +5,7 @@ $sessionId = $argv[2]; $locking = !!$argv[3]; $destroyPrevious = !!$argv[4]; +$sessionProxy = !!$argv[5]; if (empty($redisHost)) { $redisHost = 'localhost'; @@ -17,6 +18,54 @@ ini_set('redis.session.locking_enabled', true); } +class TestHandler implements SessionHandlerInterface +{ + /** + * @var SessionHandler + */ + private $handler; + + public function __construct() + { + $this->handler = new SessionHandler(); + } + + public function close() + { + return $this->handler->close(); + } + + public function destroy($session_id) + { + return $this->handler->destroy($session_id); + } + + public function gc($maxlifetime) + { + return $this->handler->gc($maxlifetime); + } + + public function open($save_path, $name) + { + return $this->handler->open($save_path, $name); + } + + public function read($session_id) + { + return $this->handler->read($session_id); + } + + public function write($session_id, $session_data) + { + return $this->handler->write($session_id, $session_data); + } +} + +if ($sessionProxy) { + $handler = new TestHandler(); + session_set_save_handler($handler); +} + session_id($sessionId); if (!session_start()) { $result = "FAILED: session_start()"; @@ -27,5 +76,6 @@ else { $result = session_id(); } +session_write_close(); echo $result; From caa2902a53fd51c0e26f58b3f4b1e8c4801d303b Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 1 Mar 2018 12:29:16 +0100 Subject: [PATCH 0937/1986] Added check on write operations if session key changed (PHP 5 bug) --- redis_session.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index 4cc7b2eb50..8784a9874f 100644 --- a/redis_session.c +++ b/redis_session.c @@ -625,7 +625,9 @@ PS_CREATE_SID_FUNC(redis) #else char *full_session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid), &resp_len); #endif - pool->lock_status->session_key = full_session_key; + char *full_session_key_nt = estrndup(full_session_key, resp_len); + efree(full_session_key); + pool->lock_status->session_key = full_session_key_nt; if (lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) == SUCCESS) { return sid; @@ -731,9 +733,25 @@ PS_WRITE_FUNC(redis) return FAILURE; } + /* We need to check for PHP5 if the session key changes (a bug with session_regenerate_id() is causing a missing PS_CREATE_SID call)*/ + #if (PHP_MAJOR_VERSION < 7) + session = redis_session_key(rpm, key, strlen(key), &session_len); + char *session_key_nt = estrndup(session, session_len); + + int session_key_changed = strcmp(pool->lock_status->session_key, session_key_nt) != 0; + if (session_key_changed) { + efree(pool->lock_status->session_key); + pool->lock_status->session_key = session_key_nt; + } + + if (session_key_changed && lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) != SUCCESS) { + efree(session); + return FAILURE; + } + #endif + /* send SET command */ #if (PHP_MAJOR_VERSION < 7) - session = redis_session_key(rpm, key, strlen(key), &session_len); cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); #else From 1fb97aa51135c88a84ebbe352f8c5b336c35451b Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 1 Mar 2018 12:47:46 +0100 Subject: [PATCH 0938/1986] Removed background operator, which could lead to race conditions --- tests/RedisTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 7189e52bc2..239c04c06f 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5462,8 +5462,6 @@ private function regenerateSessionId($sessionId, $locking = false, $destroyPrevi $command = 'php --no-php-ini --define extension=igbinary.so --define extension=' . __DIR__ . '/../modules/redis.so ' . __DIR__ . '/regenerateSessionId.php ' . escapeshellarg($this->getHost()) . ' ' . implode(' ', $args); - $command .= ' 2>&1'; - exec($command, $output); return $output[0]; From 517644a27657aa924c2428aecd09ff00bd7d270b Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 1 Mar 2018 12:53:38 +0100 Subject: [PATCH 0939/1986] Fixed failing Travis builds caused by missing session handler interface on PHP < 5.4 --- tests/RedisTest.php | 16 +++++++++ tests/regenerateSessionId.php | 68 ++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 239c04c06f..0c4e450c9a 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5334,6 +5334,10 @@ public function testSession_regenerateSessionId_withLock_withDestroy() { } 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($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); @@ -5345,6 +5349,10 @@ public function testSession_regenerateSessionId_noLock_noDestroy_withProxy() { } 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($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); @@ -5356,6 +5364,10 @@ public function testSession_regenerateSessionId_noLock_withDestroy_withProxy() } 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($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); @@ -5367,6 +5379,10 @@ public function testSession_regenerateSessionId_withLock_noDestroy_withProxy() } 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($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); diff --git a/tests/regenerateSessionId.php b/tests/regenerateSessionId.php index 33b641dbde..6d49c0ab9d 100644 --- a/tests/regenerateSessionId.php +++ b/tests/regenerateSessionId.php @@ -18,46 +18,48 @@ ini_set('redis.session.locking_enabled', true); } -class TestHandler implements SessionHandlerInterface -{ - /** - * @var SessionHandler - */ - private $handler; - - public function __construct() +if (interface_exists('SessionHandlerInterface')) { + class TestHandler implements SessionHandlerInterface { - $this->handler = new SessionHandler(); - } + /** + * @var SessionHandler + */ + private $handler; - public function close() - { - return $this->handler->close(); - } + public function __construct() + { + $this->handler = new SessionHandler(); + } - public function destroy($session_id) - { - return $this->handler->destroy($session_id); - } + public function close() + { + return $this->handler->close(); + } - public function gc($maxlifetime) - { - return $this->handler->gc($maxlifetime); - } + public function destroy($session_id) + { + return $this->handler->destroy($session_id); + } - public function open($save_path, $name) - { - return $this->handler->open($save_path, $name); - } + public function gc($maxlifetime) + { + return $this->handler->gc($maxlifetime); + } - public function read($session_id) - { - return $this->handler->read($session_id); - } + public function open($save_path, $name) + { + return $this->handler->open($save_path, $name); + } - public function write($session_id, $session_data) - { - return $this->handler->write($session_id, $session_data); + public function read($session_id) + { + return $this->handler->read($session_id); + } + + public function write($session_id, $session_data) + { + return $this->handler->write($session_id, $session_data); + } } } From 92d90950b6393e710534acb6ced2b0b1fab04c8b Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Fri, 2 Mar 2018 11:38:12 +0100 Subject: [PATCH 0940/1986] PR-1312: Refactored code structure of session key check --- redis_session.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/redis_session.c b/redis_session.c index 8784a9874f..053888bfc0 100644 --- a/redis_session.c +++ b/redis_session.c @@ -733,25 +733,24 @@ PS_WRITE_FUNC(redis) return FAILURE; } + /* send SET command */ +#if (PHP_MAJOR_VERSION < 7) + session = redis_session_key(rpm, key, strlen(key), &session_len); + /* We need to check for PHP5 if the session key changes (a bug with session_regenerate_id() is causing a missing PS_CREATE_SID call)*/ - #if (PHP_MAJOR_VERSION < 7) - session = redis_session_key(rpm, key, strlen(key), &session_len); - char *session_key_nt = estrndup(session, session_len); - - int session_key_changed = strcmp(pool->lock_status->session_key, session_key_nt) != 0; - if (session_key_changed) { - efree(pool->lock_status->session_key); - pool->lock_status->session_key = session_key_nt; - } + char *session_key_nt = estrndup(session, session_len); - if (session_key_changed && lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) != SUCCESS) { - efree(session); - return FAILURE; - } - #endif + int session_key_changed = strcmp(pool->lock_status->session_key, session_key_nt) != 0; + if (session_key_changed) { + efree(pool->lock_status->session_key); + pool->lock_status->session_key = session_key_nt; + } + + if (session_key_changed && lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) != SUCCESS) { + efree(session); + return FAILURE; + } - /* send SET command */ -#if (PHP_MAJOR_VERSION < 7) cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, INI_INT("session.gc_maxlifetime"), val, vallen); #else From e99e69cec524982b3bd3e33f1ffbfc91869043f4 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 2 Mar 2018 14:24:37 +0100 Subject: [PATCH 0941/1986] split INSTALL and add more instructions --- INSTALL.markdown | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ README.markdown | 53 ++++------------------------ package.xml | 1 + 3 files changed, 97 insertions(+), 47 deletions(-) create mode 100644 INSTALL.markdown diff --git a/INSTALL.markdown b/INSTALL.markdown new file mode 100644 index 0000000000..2187e4fa0c --- /dev/null +++ b/INSTALL.markdown @@ -0,0 +1,90 @@ +# Installation from pecl + +To pull latest stable released version, from [pecl](https://pecl.php.net/package/redis): + +~~~ +pecl install redis +~~~ + +# Installation from sources + +To build this extension for the sources tree: + +~~~ +phpize +./configure [--enable-redis-igbinary] +make && make install +~~~ + +If you would like phpredis to serialize your data using the igbinary library, run configure with `--enable-redis-igbinary`. +`make install` copies `redis.so` to an appropriate location, but you still need to enable the module in the PHP config file. To do so, either edit your php.ini or add a redis.ini file in `/etc/php5/conf.d` with the following contents: `extension=redis.so`. + +You can generate a debian package for PHP5, accessible from Apache 2 by running `./mkdeb-apache2.sh` or with `dpkg-buildpackage` or `svn-buildpackage`. + +This extension exports a single class, [Redis](#class-redis) (and [RedisException](#class-redisexception) used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion. + + +# Binary packages + +Most distributions provides pre-build binary packages of this extension. + +## Windows: + +Follow the DLL link on the [https://pecl.php.net/package/redis](https://pecl.php.net/package/redis) page. + +## Fedora + +Fedora users can install the package from the official repositor. + +**Fedora ≤ 28, Version 3 ** + +Installation of the [php-pecl-redis](https://apps.fedoraproject.org/packages/php-pecl-redis) package: + +~~~ +dnf install php-pecl-redis +~~~ + +**Fedora ≥ 27, Version 4 ** + +Installation of the [php-pecl-redis4](https://apps.fedoraproject.org/packages/php-pecl-redis4) package: + +~~~ +dnf install php-pecl-redis4 +~~~ + +## RHEL / CentOS + +Installation of the [php-pecl-redis](https://apps.fedoraproject.org/packages/php-pecl-redis) package, from the [EPEL repository](https://fedoraproject.org/wiki/EPEL): + +~~~ +yum install php-pecl-redis +~~~ + + +# Installation on OSX + +If the install fails on OSX, type the following commands in your shell before trying again: +~~~ +MACOSX_DEPLOYMENT_TARGET=10.6 +CFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp" +CCFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" +CXXFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" +LDFLAGS="-arch i386 -arch x86_64 -bind_at_load" +export CFLAGS CXXFLAGS LDFLAGS CCFLAGS MACOSX_DEPLOYMENT_TARGET +~~~ + +If that still fails and you are running Zend Server CE, try this right before "make": `./configure CFLAGS="-arch i386"`. + +Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tagged/phpredis). + +See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports). + +You can install it using Homebrew: + +- [Get homebrew-php](https://github.com/Homebrew/homebrew-php) +- `brew install php55-redis` (or php53-redis, php54-redis) + +# Building on Windows + +See [instructions from @char101](https://github.com/phpredis/phpredis/issues/213#issuecomment-11361242) on how to build phpredis on Windows. + diff --git a/README.markdown b/README.markdown index a3d9541687..b55f2a9654 100644 --- a/README.markdown +++ b/README.markdown @@ -34,49 +34,13 @@ You can send comments, patches, questions [here on github](https://github.com/ph ----- -# Installing/Configuring +# Installation ----- -Everything you should need to install PhpRedis on your system. +For everything you should need to install PhpRedis on your system, +see the [INSTALL.markdown](./INSTALL.markdown) page. -## Installation - -~~~ -phpize -./configure [--enable-redis-igbinary] -make && make install -~~~ - -If you would like phpredis to serialize your data using the igbinary library, run configure with `--enable-redis-igbinary`. -`make install` copies `redis.so` to an appropriate location, but you still need to enable the module in the PHP config file. To do so, either edit your php.ini or add a redis.ini file in `/etc/php5/conf.d` with the following contents: `extension=redis.so`. - -You can generate a debian package for PHP5, accessible from Apache 2 by running `./mkdeb-apache2.sh` or with `dpkg-buildpackage` or `svn-buildpackage`. - -This extension exports a single class, [Redis](#class-redis) (and [RedisException](#class-redisexception) used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion. - - -## Installation on OSX - -If the install fails on OSX, type the following commands in your shell before trying again: -~~~ -MACOSX_DEPLOYMENT_TARGET=10.6 -CFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp" -CCFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" -CXXFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" -LDFLAGS="-arch i386 -arch x86_64 -bind_at_load" -export CFLAGS CXXFLAGS LDFLAGS CCFLAGS MACOSX_DEPLOYMENT_TARGET -~~~ - -If that still fails and you are running Zend Server CE, try this right before "make": `./configure CFLAGS="-arch i386"`. - -Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tagged/phpredis). - -See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports). - -You can install it using Homebrew: - -- [Get homebrew-php](https://github.com/Homebrew/homebrew-php) -- `brew install php55-redis` (or php53-redis, php54-redis) +# Configuration ## PHP Session handler @@ -100,18 +64,13 @@ The session handler requires a version of Redis with the `SETEX` command (at lea phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0`. -## Building on Windows - -See [instructions from @char101](https://github.com/phpredis/phpredis/issues/213#issuecomment-11361242) on how to build phpredis on Windows. - - ## Distributed Redis Array -See [dedicated page](https://github.com/phpredis/phpredis/blob/master/arrays.markdown#readme). +See [dedicated page](./arrays.markdown#readme). ## Redis Cluster support -See [dedicated page](https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#readme). +See [dedicated page](./cluster.markdown#readme). ## Running the unit tests diff --git a/package.xml b/package.xml index b921107f46..8c13dc0377 100644 --- a/package.xml +++ b/package.xml @@ -65,6 +65,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + From 0a799af914cc1db953f5a56d66f9b788285b01c5 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Mon, 5 Mar 2018 15:06:06 +0100 Subject: [PATCH 0942/1986] PR-1312: Only copy sesseion key if it changed --- redis_session.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/redis_session.c b/redis_session.c index 053888bfc0..41177e73cb 100644 --- a/redis_session.c +++ b/redis_session.c @@ -738,12 +738,10 @@ PS_WRITE_FUNC(redis) session = redis_session_key(rpm, key, strlen(key), &session_len); /* We need to check for PHP5 if the session key changes (a bug with session_regenerate_id() is causing a missing PS_CREATE_SID call)*/ - char *session_key_nt = estrndup(session, session_len); - - int session_key_changed = strcmp(pool->lock_status->session_key, session_key_nt) != 0; + int session_key_changed = strlen(pool->lock_status->session_key) != session_len || strncmp(pool->lock_status->session_key, session, session_len) != 0; if (session_key_changed) { efree(pool->lock_status->session_key); - pool->lock_status->session_key = session_key_nt; + pool->lock_status->session_key = estrndup(session, session_len); } if (session_key_changed && lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) != SUCCESS) { From 65fffad365b48ab9bb1afc3e8b7f569c271fd7d4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 5 Mar 2018 22:28:48 +0200 Subject: [PATCH 0943/1986] Refactor redis session * Use zend_string for storing auth and prefix members * Allocate redis_session_lock_status structure jointly with redis_pool * Remove redis_pool_new function --- redis_session.c | 81 ++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/redis_session.c b/redis_session.c index 1ad5b49aae..84d7962fa8 100644 --- a/redis_session.c +++ b/redis_session.c @@ -81,11 +81,8 @@ typedef struct redis_pool_member_ { int weight; int database; - char *prefix; - size_t prefix_len; - - char *auth; - size_t auth_len; + zend_string *auth; + zend_string *prefix; struct redis_pool_member_ *next; @@ -97,23 +94,14 @@ typedef struct { int count; redis_pool_member *head; - redis_session_lock_status *lock_status; + redis_session_lock_status lock_status; } redis_pool; -PHP_REDIS_API redis_pool* -redis_pool_new(TSRMLS_D) { - redis_pool *pool; - - pool = ecalloc(1, sizeof(*pool)); - pool->lock_status = ecalloc(1, sizeof(*pool->lock_status)); - - return pool; -} PHP_REDIS_API void redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, - int database, char *prefix, char *auth TSRMLS_DC) { + int database, zend_string *prefix, zend_string *auth TSRMLS_DC) { redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); rpm->redis_sock = redis_sock; @@ -121,10 +109,7 @@ redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, rpm->database = database; rpm->prefix = prefix; - rpm->prefix_len = (prefix?strlen(prefix):0); - rpm->auth = auth; - rpm->auth_len = (auth?strlen(auth):0); rpm->next = pool->head; pool->head = rpm; @@ -141,22 +126,21 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { next = rpm->next; redis_sock_disconnect(rpm->redis_sock TSRMLS_CC); redis_free_socket(rpm->redis_sock); - if(rpm->prefix) efree(rpm->prefix); - if(rpm->auth) efree(rpm->auth); + if (rpm->prefix) zend_string_release(rpm->prefix); + if (rpm->auth) zend_string_release(rpm->auth); efree(rpm); rpm = next; } /* Cleanup after our lock */ - if (pool->lock_status->session_key) - efree(pool->lock_status->session_key); - if (pool->lock_status->lock_secret) - efree(pool->lock_status->lock_secret); - if (pool->lock_status->lock_key) - efree(pool->lock_status->lock_key); - - /* Cleanup lock status struct and pool itself */ - efree(pool->lock_status); + if (pool->lock_status.session_key) + efree(pool->lock_status.session_key); + if (pool->lock_status.lock_secret) + efree(pool->lock_status.lock_secret); + if (pool->lock_status.lock_key) + efree(pool->lock_status.lock_key); + + /* Cleanup pool itself */ efree(pool); } @@ -183,11 +167,11 @@ redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { int response_len, cmd_len; /* Short circuit if we don't have a password */ - if(!rpm->auth || !rpm->auth_len) { + if (!rpm->auth) { return; } - cmd_len = REDIS_SPPRINTF(&cmd, "AUTH", "s", rpm->auth, rpm->auth_len); + cmd_len = REDIS_SPPRINTF(&cmd, "AUTH", "S", rpm->auth); if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { efree(response); @@ -223,7 +207,7 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { for(i = 0; i < pool->totalWeight;) { if(pos >= i && pos < i + rpm->weight) { int needs_auth = 0; - if(rpm->auth && rpm->auth_len && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { + if (rpm->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } redis_sock_server_open(rpm->redis_sock TSRMLS_CC); @@ -427,7 +411,7 @@ PS_OPEN_FUNC(redis) zval params, *param; int i, j, path_len; - redis_pool *pool = redis_pool_new(TSRMLS_C); + redis_pool *pool = ecalloc(1, sizeof(*pool)); for (i=0,j=0,path_len=strlen(save_path); ipath == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { php_url_free(url); if (persistent_id) efree(persistent_id); - if (prefix) efree(prefix); - if (auth) efree(auth); + if (prefix) zend_string_release(prefix); + if (auth) zend_string_release(auth); redis_pool_free(pool TSRMLS_CC); PS_SET_MOD_DATA(NULL); return FAILURE; @@ -544,11 +529,11 @@ PS_CLOSE_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); if(pool){ - redis_pool_member *rpm = redis_pool_get_sock(pool, pool->lock_status->session_key TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, pool->lock_status.session_key TSRMLS_CC); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (redis_sock) { - lock_release(redis_sock, pool->lock_status TSRMLS_CC); + lock_release(redis_sock, &pool->lock_status TSRMLS_CC); } redis_pool_free(pool TSRMLS_CC); @@ -567,9 +552,9 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses char *prefix = default_prefix; size_t prefix_len = sizeof(default_prefix)-1; - if(rpm->prefix) { - prefix = rpm->prefix; - prefix_len = rpm->prefix_len; + if (rpm->prefix) { + prefix = ZSTR_VAL(rpm->prefix); + prefix_len = ZSTR_LEN(rpm->prefix); } /* build session key */ @@ -605,12 +590,12 @@ PS_READ_FUNC(redis) #else resp = redis_session_key(rpm, ZSTR_VAL(key), ZSTR_LEN(key), &resp_len); #endif - pool->lock_status->session_key = estrndup(resp, resp_len); + pool->lock_status.session_key = estrndup(resp, resp_len); cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); efree(resp); - if (lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) != SUCCESS) { + if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Acquire of session lock was not successful"); } @@ -679,7 +664,7 @@ PS_WRITE_FUNC(redis) #endif efree(session); - if(!write_allowed(redis_sock, pool->lock_status TSRMLS_CC) || redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (!write_allowed(redis_sock, &pool->lock_status TSRMLS_CC) || redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; } @@ -720,7 +705,7 @@ PS_DESTROY_FUNC(redis) /* Release lock */ if (redis_sock) { - lock_release(redis_sock, pool->lock_status TSRMLS_CC); + lock_release(redis_sock, &pool->lock_status TSRMLS_CC); } /* send DEL command */ From fd93e26fc42ae0be6c5d5510516933e7d9350722 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 7 Mar 2018 19:13:24 +0200 Subject: [PATCH 0944/1986] Remove redis_pool_new --- redis_session.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/redis_session.c b/redis_session.c index 2a36c625a9..6a1dce785f 100644 --- a/redis_session.c +++ b/redis_session.c @@ -68,11 +68,6 @@ typedef struct { } redis_pool; -PHP_REDIS_API redis_pool* -redis_pool_new(TSRMLS_D) { - return ecalloc(1, sizeof(redis_pool)); -} - PHP_REDIS_API void redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, int database, zend_string *prefix, zend_string *auth TSRMLS_DC) { @@ -184,7 +179,7 @@ PS_OPEN_FUNC(redis) zval params, *param; int i, j, path_len; - redis_pool *pool = redis_pool_new(TSRMLS_C); + redis_pool *pool = ecalloc(1, sizeof(*pool)); for (i = 0, j = 0, path_len = strlen(save_path); i < path_len; i = j + 1) { /* find beginning of url */ From 040ee6421da35d66795b031673838f6f799fdd64 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 24 Mar 2018 14:50:47 +0200 Subject: [PATCH 0945/1986] Fixes after merge --- redis_session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index d82f334d2a..49d0dc69d5 100644 --- a/redis_session.c +++ b/redis_session.c @@ -612,9 +612,9 @@ PS_CREATE_SID_FUNC(redis) #endif char *full_session_key_nt = estrndup(full_session_key, resp_len); efree(full_session_key); - pool->lock_status->session_key = full_session_key_nt; + pool->lock_status.session_key = full_session_key_nt; - if (lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) == SUCCESS) { + if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) == SUCCESS) { return sid; } From 29edc7db158412239aa5f36a63f342fdcac6c13c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 24 Mar 2018 14:59:43 +0200 Subject: [PATCH 0946/1986] Fixes after merge for php5 --- redis_session.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/redis_session.c b/redis_session.c index 49d0dc69d5..699818f364 100644 --- a/redis_session.c +++ b/redis_session.c @@ -713,8 +713,8 @@ PS_WRITE_FUNC(redis) #else redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(key) TSRMLS_CC); #endif - RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if(!rpm || !redis_sock){ + RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; + if (!redis_sock) { return FAILURE; } @@ -723,13 +723,13 @@ PS_WRITE_FUNC(redis) session = redis_session_key(rpm, key, strlen(key), &session_len); /* We need to check for PHP5 if the session key changes (a bug with session_regenerate_id() is causing a missing PS_CREATE_SID call)*/ - int session_key_changed = strlen(pool->lock_status->session_key) != session_len || strncmp(pool->lock_status->session_key, session, session_len) != 0; + int session_key_changed = strlen(pool->lock_status.session_key) != session_len || strncmp(pool->lock_status.session_key, session, session_len) != 0; if (session_key_changed) { - efree(pool->lock_status->session_key); - pool->lock_status->session_key = estrndup(session, session_len); + efree(pool->lock_status.session_key); + pool->lock_status.session_key = estrndup(session, session_len); } - if (session_key_changed && lock_acquire(redis_sock, pool->lock_status TSRMLS_CC) != SUCCESS) { + if (session_key_changed && lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { efree(session); return FAILURE; } From 5f75efcf95aab3b7387ba06e8c2a8f2d2a3da1ed Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 24 Mar 2018 21:48:55 +0200 Subject: [PATCH 0947/1986] Extra memory duplication --- redis_session.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/redis_session.c b/redis_session.c index 6a03f853de..d010039a0f 100644 --- a/redis_session.c +++ b/redis_session.c @@ -615,13 +615,10 @@ PS_CREATE_SID_FUNC(redis) int resp_len; #if (PHP_MAJOR_VERSION < 7) - char *full_session_key = redis_session_key(rpm, sid, strlen(sid), &resp_len); + pool->lock_status.session_key = redis_session_key(rpm, sid, strlen(sid), &resp_len); #else - char *full_session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid), &resp_len); + pool->lock_status.session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid), &resp_len); #endif - char *full_session_key_nt = estrndup(full_session_key, resp_len); - efree(full_session_key); - pool->lock_status.session_key = full_session_key_nt; if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) == SUCCESS) { return sid; From 07c16e51b081b12bd19ae77af1143149fae5a760 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 26 Mar 2018 14:24:33 +0300 Subject: [PATCH 0948/1986] Apply zend_string API for redis_session_key --- common.h | 16 +++++++++---- redis_session.c | 62 ++++++++++++++++++++++++------------------------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/common.h b/common.h index 4b1342dfb1..11c243e400 100644 --- a/common.h +++ b/common.h @@ -25,16 +25,24 @@ typedef struct { #define ZSTR_VAL(s) (s)->val #define ZSTR_LEN(s) (s)->len +static zend_always_inline zend_string * +zend_string_alloc(size_t len, int persistent) +{ + zend_string *zstr = emalloc(sizeof(*zstr) + len + 1); + + ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); + zstr->len = len; + zstr->gc = 0x01; + return zstr; +} + static zend_always_inline zend_string * zend_string_init(const char *str, size_t len, int persistent) { - zend_string *zstr = emalloc(sizeof(zend_string) + len + 1); + zend_string *zstr = zend_string_alloc(len, persistent); - ZSTR_VAL(zstr) = (char *)zstr + sizeof(zend_string); memcpy(ZSTR_VAL(zstr), str, len); ZSTR_VAL(zstr)[len] = '\0'; - zstr->len = len; - zstr->gc = 0x01; return zstr; } diff --git a/redis_session.c b/redis_session.c index d010039a0f..281c0d729f 100644 --- a/redis_session.c +++ b/redis_session.c @@ -553,10 +553,10 @@ PS_CLOSE_FUNC(redis) } /* }}} */ -static char * -redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *session_len) { - - char *session; +static zend_string * +redis_session_key(redis_pool_member *rpm, const char *key, int key_len) +{ + zend_string *session; char default_prefix[] = "PHPREDIS_SESSION:"; char *prefix = default_prefix; size_t prefix_len = sizeof(default_prefix)-1; @@ -567,10 +567,9 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses } /* build session key */ - *session_len = key_len + prefix_len; - session = emalloc(*session_len); - memcpy(session, prefix, prefix_len); - memcpy(session + prefix_len, key, key_len); + session = zend_string_alloc(key_len + prefix_len, 0); + memcpy(ZSTR_VAL(session), prefix, prefix_len); + memcpy(ZSTR_VAL(session) + prefix_len, key, key_len); return session; } @@ -613,17 +612,19 @@ PS_CREATE_SID_FUNC(redis) #endif } - int resp_len; #if (PHP_MAJOR_VERSION < 7) - pool->lock_status.session_key = redis_session_key(rpm, sid, strlen(sid), &resp_len); + zend_string *session = redis_session_key(rpm, sid, strlen(sid)); #else - pool->lock_status.session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid), &resp_len); + zend_string *session = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid)); #endif + pool->lock_status.session_key = estrndup(ZSTR_VAL(session), ZSTR_LEN(session)); + zend_string_release(session); if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) == SUCCESS) { return sid; } + efree(pool->lock_status.session_key); #if (PHP_MAJOR_VERSION < 7) efree(sid); #else @@ -663,10 +664,10 @@ PS_READ_FUNC(redis) } /* send GET command */ - resp = redis_session_key(rpm, skey, skeylen, &resp_len); - pool->lock_status.session_key = estrndup(resp, resp_len); - cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len); - efree(resp); + zend_string *session = redis_session_key(rpm, skey, skeylen); + pool->lock_status.session_key = estrndup(ZSTR_VAL(session), ZSTR_LEN(session)); + cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", session); + zend_string_release(session); if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, @@ -710,8 +711,8 @@ PS_READ_FUNC(redis) */ PS_WRITE_FUNC(redis) { - char *cmd, *response, *session; - int cmd_len, response_len, session_len; + char *cmd, *response; + int cmd_len, response_len; #if (PHP_MAJOR_VERSION < 7) const char *skey = key, *sval = val; size_t skeylen = strlen(key), svallen = vallen; @@ -730,23 +731,22 @@ PS_WRITE_FUNC(redis) } /* send SET command */ - session = redis_session_key(rpm, skey, skeylen, &session_len); + zend_string *session = redis_session_key(rpm, skey, skeylen); #if (PHP_MAJOR_VERSION < 7) /* We need to check for PHP5 if the session key changes (a bug with session_regenerate_id() is causing a missing PS_CREATE_SID call)*/ - int session_key_changed = strlen(pool->lock_status.session_key) != session_len || strncmp(pool->lock_status.session_key, session, session_len) != 0; + int session_key_changed = strlen(pool->lock_status.session_key) != ZSTR_LEN(session) || strncmp(pool->lock_status.session_key, ZSTR_VAL(session), ZSTR_LEN(session)) != 0; if (session_key_changed) { efree(pool->lock_status.session_key); - pool->lock_status.session_key = estrndup(session, session_len); + pool->lock_status.session_key = estrndup(ZSTR_VAL(session), ZSTR_LEN(session)); } if (session_key_changed && lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { - efree(session); + zend_string_release(session); return FAILURE; } #endif - cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len, - INI_INT("session.gc_maxlifetime"), sval, svallen); - efree(session); + cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, INI_INT("session.gc_maxlifetime"), sval, svallen); + zend_string_release(session); if (!write_allowed(redis_sock, &pool->lock_status TSRMLS_CC) || redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); @@ -773,8 +773,8 @@ PS_WRITE_FUNC(redis) */ PS_DESTROY_FUNC(redis) { - char *cmd, *response, *session; - int cmd_len, response_len, session_len; + char *cmd, *response; + int cmd_len, response_len; #if (PHP_MAJOR_VERSION < 7) const char *skey = key; size_t skeylen = strlen(key); @@ -785,8 +785,8 @@ PS_DESTROY_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); - RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if (!rpm || !redis_sock){ + RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; + if (!redis_sock) { return FAILURE; } @@ -796,9 +796,9 @@ PS_DESTROY_FUNC(redis) } /* send DEL command */ - session = redis_session_key(rpm, skey, skeylen, &session_len); - cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "s", session, session_len); - efree(session); + zend_string *session = redis_session_key(rpm, skey, skeylen); + cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "S", session); + zend_string_release(session); if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; From e17e65d531a2fd4b2315d657c591e4bb26e6437c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 26 Mar 2018 14:47:58 +0300 Subject: [PATCH 0949/1986] Use zend_string for all lock_status members --- common.h | 4 +++ redis_session.c | 83 ++++++++++++++++++++++--------------------------- 2 files changed, 42 insertions(+), 45 deletions(-) diff --git a/common.h b/common.h index 11c243e400..f2062a9e9a 100644 --- a/common.h +++ b/common.h @@ -46,6 +46,10 @@ zend_string_init(const char *str, size_t len, int persistent) return zstr; } +#define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) +#define zend_string_equal_content(s1, s2) (ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2)) +#define zend_string_equals(s1, s2) (s1 == s2 || zend_string_equal_content(s1, s2)) + #define zend_string_release(s) do { \ if ((s) && (s)->gc) { \ if ((s)->gc & 0x10 && ZSTR_VAL(s)) efree(ZSTR_VAL(s)); \ diff --git a/redis_session.c b/redis_session.c index 281c0d729f..4ce88e07df 100644 --- a/redis_session.c +++ b/redis_session.c @@ -70,9 +70,9 @@ ps_module ps_mod_redis_cluster = { typedef struct { zend_bool is_locked; - char *session_key; - char *lock_key; - char *lock_secret; + zend_string *session_key; + zend_string *lock_key; + zend_string *lock_secret; } redis_session_lock_status; typedef struct redis_pool_member_ { @@ -130,12 +130,9 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { } /* Cleanup after our lock */ - if (pool->lock_status.session_key) - efree(pool->lock_status.session_key); - if (pool->lock_status.lock_secret) - efree(pool->lock_status.lock_secret); - if (pool->lock_status.lock_key) - efree(pool->lock_status.lock_key); + if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); + if (pool->lock_status.lock_secret) zend_string_release(pool->lock_status.lock_secret); + if (pool->lock_status.lock_key) zend_string_release(pool->lock_status.lock_key); /* Cleanup pool itself */ efree(pool); @@ -247,7 +244,7 @@ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) { - char *cmd, hostname[HOST_NAME_MAX] = {0}; + char *cmd, hostname[HOST_NAME_MAX] = {0}, suffix[] = "_LOCK", pid[32]; int cmd_len, lock_wait_time, retries, i, expiry; /* Short circuit if we are already locked or not using session locks */ @@ -273,21 +270,25 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s } /* Generate our qualified lock key */ - spprintf(&lock_status->lock_key, 0, "%s%s", lock_status->session_key, "_LOCK"); + lock_status->lock_key = zend_string_alloc(ZSTR_LEN(lock_status->session_key) + sizeof(suffix) - 1, 0); + memcpy(ZSTR_VAL(lock_status->lock_key), ZSTR_VAL(lock_status->session_key), ZSTR_LEN(lock_status->session_key)); + memcpy(ZSTR_VAL(lock_status->lock_key) + ZSTR_LEN(lock_status->session_key), suffix, sizeof(suffix) - 1); /* Calculate lock secret */ gethostname(hostname, HOST_NAME_MAX); - spprintf(&lock_status->lock_secret, 0, "%s|%ld", hostname, (long)getpid()); + size_t hostname_len = strlen(hostname); + size_t pid_len = snprintf(pid, sizeof(pid), "|%ld", (long)getpid()); + lock_status->lock_secret = zend_string_alloc(hostname_len + pid_len, 0); + memcpy(ZSTR_VAL(lock_status->lock_secret), hostname, hostname_len); + memcpy(ZSTR_VAL(lock_status->lock_secret) + hostname_len, pid, pid_len); if (expiry > 0) { - cmd_len = REDIS_SPPRINTF(&cmd, "SET", "ssssd", lock_status->lock_key, - strlen(lock_status->lock_key), lock_status->lock_secret, - strlen(lock_status->lock_secret), "NX", 2, - "PX", 2, expiry * 1000); + cmd_len = REDIS_SPPRINTF(&cmd, "SET", "SSssd", lock_status->lock_key, + lock_status->lock_secret, "NX", 2, "PX", 2, + expiry * 1000); } else { - cmd_len = REDIS_SPPRINTF(&cmd, "SET", "sss", lock_status->lock_key, - strlen(lock_status->lock_key), lock_status->lock_secret, - strlen(lock_status->lock_secret), "NX", 2); + cmd_len = REDIS_SPPRINTF(&cmd, "SET", "SSs", lock_status->lock_key, + lock_status->lock_secret, "NX", 2); } /* Attempt to get our lock */ @@ -310,7 +311,7 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s return lock_status->is_locked ? SUCCESS : FAILURE; } -#define IS_LOCK_SECRET(reply, len, secret) (len == strlen(secret) && !strncmp(reply, secret, len)) +#define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !strncmp(reply, ZSTR_VAL(secret), len)) static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) { char *cmd, *reply = NULL; @@ -327,8 +328,7 @@ static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status return; /* Command to get our lock key value and compare secrets */ - cmdlen = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key, - strlen(lock_status->lock_key)); + cmdlen = REDIS_SPPRINTF(&cmd, "GET", "S", lock_status->lock_key); /* Attempt to refresh the lock */ reply = redis_simple_cmd(redis_sock, cmd, cmdlen, &replylen TSRMLS_CC); @@ -377,9 +377,8 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_ /* We first want to try EVALSHA and then fall back to EVAL */ for (i = 0; lock_status->is_locked && i < sizeof(kwd)/sizeof(*kwd); i++) { /* Construct our command */ - cmdlen = REDIS_SPPRINTF(&cmd, (char*)kwd[i], "sdss", lua[i], len[i], 1, - lock_status->lock_key, strlen(lock_status->lock_key), - lock_status->lock_secret, strlen(lock_status->lock_secret)); + cmdlen = REDIS_SPPRINTF(&cmd, (char*)kwd[i], "sdSS", lua[i], len[i], 1, + lock_status->lock_key, lock_status->lock_secret); /* Send it off */ reply = redis_simple_cmd(redis_sock, cmd, cmdlen, &replylen TSRMLS_CC); @@ -538,7 +537,7 @@ PS_CLOSE_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); if (pool) { - redis_pool_member *rpm = redis_pool_get_sock(pool, pool->lock_status.session_key TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(pool->lock_status.session_key) TSRMLS_CC); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (redis_sock) { @@ -613,18 +612,15 @@ PS_CREATE_SID_FUNC(redis) } #if (PHP_MAJOR_VERSION < 7) - zend_string *session = redis_session_key(rpm, sid, strlen(sid)); + pool->lock_status.session_key = redis_session_key(rpm, sid, strlen(sid)); #else - zend_string *session = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid)); + pool->lock_status.session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid)); #endif - pool->lock_status.session_key = estrndup(ZSTR_VAL(session), ZSTR_LEN(session)); - zend_string_release(session); - if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) == SUCCESS) { return sid; } - efree(pool->lock_status.session_key); + zend_string_release(pool->lock_status.session_key); #if (PHP_MAJOR_VERSION < 7) efree(sid); #else @@ -664,10 +660,8 @@ PS_READ_FUNC(redis) } /* send GET command */ - zend_string *session = redis_session_key(rpm, skey, skeylen); - pool->lock_status.session_key = estrndup(ZSTR_VAL(session), ZSTR_LEN(session)); - cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", session); - zend_string_release(session); + pool->lock_status.session_key = redis_session_key(rpm, skey, skeylen); + cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, @@ -734,15 +728,14 @@ PS_WRITE_FUNC(redis) zend_string *session = redis_session_key(rpm, skey, skeylen); #if (PHP_MAJOR_VERSION < 7) /* We need to check for PHP5 if the session key changes (a bug with session_regenerate_id() is causing a missing PS_CREATE_SID call)*/ - int session_key_changed = strlen(pool->lock_status.session_key) != ZSTR_LEN(session) || strncmp(pool->lock_status.session_key, ZSTR_VAL(session), ZSTR_LEN(session)) != 0; - if (session_key_changed) { - efree(pool->lock_status.session_key); - pool->lock_status.session_key = estrndup(ZSTR_VAL(session), ZSTR_LEN(session)); - } - - if (session_key_changed && lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { - zend_string_release(session); - return FAILURE; + if (!zend_string_equals(pool->lock_status.session_key, session)) { + zend_string_release(pool->lock_status.session_key); + pool->lock_status.session_key = zend_string_init(ZSTR_VAL(session), ZSTR_LEN(session), 0); + if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { + zend_string_release(pool->lock_status.session_key); + zend_string_release(session); + return FAILURE; + } } #endif cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, INI_INT("session.gc_maxlifetime"), sval, svallen); From 87f1c3920502a529d088b38c1a5fb5ade74cd64e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 31 Mar 2018 11:31:41 -0700 Subject: [PATCH 0950/1986] Updates EXISTS documentation and notes change in 4.0.0 --- README.markdown | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index a3d9541687..460f910477 100644 --- a/README.markdown +++ b/README.markdown @@ -804,15 +804,21 @@ _**Description**_: Verify if the specified key exists. *key* ##### *Return value* -*BOOL*: If the key exists, return `TRUE`, otherwise return `FALSE`. +*long*: The number of keys tested that do exist. ##### *Examples* ~~~ $redis->set('key', 'value'); -$redis->exists('key'); /* TRUE */ -$redis->exists('NonExistingKey'); /* FALSE */ +$redis->exists('key'); /* 1 */ +$redis->exists('NonExistingKey'); /* 0 */ + +$redis->mset(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz']); +$redis->exists(['foo', 'bar', 'baz]); /* 3 */ +$redis->exists('foo', 'bar', 'baz'); /* 3 */ ~~~ +**Note**: This function took a single argument and returned TRUE or FALSE in phpredis versions < 4.0.0. + ### incr, incrBy ----- _**Description**_: Increment the number stored at key by one. If the second argument is filled, it will be used as the integer value of the increment. From a4ccbfdaa55c6cf4b13360ef9db411367b7e2d22 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 4 Apr 2018 17:01:20 +0300 Subject: [PATCH 0951/1986] Issue #1325 --- config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index f8703037c8..8a9072f169 100755 --- a/config.m4 +++ b/config.m4 @@ -91,7 +91,7 @@ dnl Check for igbinary PHP_SUBST(REDIS_SHARED_LIBADD) else PHP_ADD_INCLUDE(liblzf) - PHP_ADD_INCLUDE($ext_srcdir/liblzf) + PHP_ADD_INCLUDE($srcdir/liblzf) PHP_ADD_BUILD_DIR(liblzf) lzf_sources="liblzf/lzf_c.c liblzf/lzf_d.c" fi From 74ca5adbd7bcca353b65b47866863f9e96c45ad2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 8 Apr 2018 06:50:49 -0700 Subject: [PATCH 0952/1986] Don't leak a ZVAL --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 2a44bb2d95..65c7f349be 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1859,7 +1859,7 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust for (i = 0; i < r->elements; i++) { cluster_mbulk_variant_resp(r->element[i], z_arr); } - RETVAL_ZVAL(z_arr, 1, 0); + RETVAL_ZVAL(z_arr, 0, 0); break; default: RETVAL_FALSE; From e599ecf186e5acbc5c7e479f2a51abfc1440925d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 8 Apr 2018 07:24:51 -0700 Subject: [PATCH 0953/1986] Fallback if we don't have openssl --- tests/RedisTest.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 0475f867b8..c7aa345448 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5461,8 +5461,17 @@ 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 { - $encoded = bin2hex(openssl_random_pseudo_bytes(8)); + /* Not cryptographically secure, but it doesn't need to be + * for the test. We just want an ID */ + $encoded = ''; + for ($c = 0; $c < 8; $c++) { + $encoded .= chr(rand(65, 90)); + } return $encoded; } } From dba618a1e06ad2a99463631cbad5911b9fb410da Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Apr 2018 06:40:42 -0700 Subject: [PATCH 0954/1986] Use uniqid() --- tests/RedisTest.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index c7aa345448..45d2c1efbe 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5466,13 +5466,7 @@ private function generateSessionId() } else if (function_exists('openssl_random_pseudo_bytes')) { return bin2hex(openssl_random_pseudo_bytes(8)); } else { - /* Not cryptographically secure, but it doesn't need to be - * for the test. We just want an ID */ - $encoded = ''; - for ($c = 0; $c < 8; $c++) { - $encoded .= chr(rand(65, 90)); - } - return $encoded; + return uniqid(); } } From 411b6aa5b197b79dec1aea99af912ac4950062a6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Apr 2018 08:23:30 -0700 Subject: [PATCH 0955/1986] Adds STORE and STOREDIST to GEORADIUS[BYMEMBER] * Adds the STORE and STOREDIST options to GEORADIUS[BYMEMBER] for both Redis and RedisCluster classes. We attempt to protect the caller from CROSSLOT errors on the client side so as to make it obvious when that is happening. * Created a struct to carry around GEORADIUS options as there are now two more and there were already a lot. * Moved the structs/enums to handle GEORADIUS into the source file instead of header as they aren't needed outside of redis_commands.c --- redis_commands.c | 182 ++++++++++++++++++++++++++++++++------------ redis_commands.h | 7 -- tests/RedisTest.php | 97 ++++++++++++++--------- 3 files changed, 194 insertions(+), 92 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 7de086418b..91369586f8 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -34,6 +34,31 @@ #include +/* Georadius sort type */ +typedef enum geoSortType { + SORT_NONE, + SORT_ASC, + SORT_DESC +} geoSortType; + +/* Georadius store type */ +typedef enum geoStoreType { + STORE_NONE, + STORE_COORD, + STORE_DIST +} geoStoreType; + +/* Georadius options structure */ +typedef struct geoOptions { + int withcoord; + int withdist; + int withhash; + long count; + geoSortType sort; + geoStoreType store; + zend_string *key; +} geoOptions; + /* Local passthrough macro for command construction. Given that these methods * are generic (so they work whether the caller is Redis or RedisCluster) we * will always have redis_sock, slot*, and TSRMLS_CC */ @@ -2593,11 +2618,18 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +geoStoreType get_georadius_store_type(zend_string *key) { + if (ZSTR_LEN(key) == 5 && !strcasecmp(ZSTR_VAL(key), "store")) { + return STORE_COORD; + } else if (ZSTR_LEN(key) == 9 && !strcasecmp(ZSTR_VAL(key), "storedist")) { + return STORE_DIST; + } + + return STORE_NONE; +} + /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */ -static int get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, - int *withhash, long *count, geoSortType *sort - TSRMLS_DC) -{ +static int get_georadius_opts(HashTable *ht, geoOptions *o TSRMLS_DC) { ulong idx; char *optstr; zend_string *zkey; @@ -2608,16 +2640,24 @@ static int get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, /* Iterate over our argument array, collating which ones we have */ ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, optval) { ZVAL_DEREF(optval); + /* If the key is numeric it's a non value option */ if (zkey) { if (ZSTR_LEN(zkey) == 5 && !strcasecmp(ZSTR_VAL(zkey), "count")) { if (Z_TYPE_P(optval) != IS_LONG || Z_LVAL_P(optval) <= 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "COUNT must be an integer > 0!"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "COUNT must be an integer > 0!"); + if (o->key) zend_string_release(o->key); return FAILURE; } /* Set our count */ - *count = Z_LVAL_P(optval); + o->count = Z_LVAL_P(optval); + } else if (o->store == STORE_NONE) { + o->store = get_georadius_store_type(zkey); + if (o->store != STORE_NONE) { + o->key = zval_get_string(optval); + } } } else { /* Option needs to be a string */ @@ -2626,50 +2666,77 @@ static int get_georadius_opts(HashTable *ht, int *withcoord, int *withdist, optstr = Z_STRVAL_P(optval); if (!strcasecmp(optstr, "withcoord")) { - *withcoord = 1; + o->withcoord = 1; } else if (!strcasecmp(optstr, "withdist")) { - *withdist = 1; + o->withdist = 1; } else if (!strcasecmp(optstr, "withhash")) { - *withhash = 1; + o->withhash = 1; } else if (!strcasecmp(optstr, "asc")) { - *sort = SORT_ASC; + o->sort = SORT_ASC; } else if (!strcasecmp(optstr, "desc")) { - *sort = SORT_DESC; + o->sort = SORT_DESC; } } } ZEND_HASH_FOREACH_END(); + /* STORE and STOREDIST are not compatible with the WITH* options */ + if (o->key != NULL && (o->withcoord || o->withdist || o->withhash)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "STORE[DIST] is not compatible with WITHCOORD, WITHDIST or WITHHASH"); + + if (o->key) zend_string_release(o->key); + return FAILURE; + } + /* Success */ return SUCCESS; } /* Helper to append options to a GEORADIUS or GEORADIUSBYMEMBER command */ -void append_georadius_opts(smart_string *str, int withcoord, int withdist, - int withhash, long count, geoSortType sort) +void append_georadius_opts(RedisSock *redis_sock, smart_string *str, short *slot, + geoOptions *opt) { - /* WITHCOORD option */ - if (withcoord) - REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHCOORD"); + char *key; + strlen_t keylen; + int keyfree; - /* WITHDIST option */ - if (withdist) + if (opt->withcoord) + REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHCOORD"); + if (opt->withdist) REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHDIST"); - - /* WITHHASH option */ - if (withhash) + if (opt->withhash) REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHHASH"); /* Append sort if it's not GEO_NONE */ - if (sort == SORT_ASC) { + if (opt->sort == SORT_ASC) { REDIS_CMD_APPEND_SSTR_STATIC(str, "ASC"); - } else if (sort == SORT_DESC) { + } else if (opt->sort == SORT_DESC) { REDIS_CMD_APPEND_SSTR_STATIC(str, "DESC"); } /* Append our count if we've got one */ - if (count) { + if (opt->count) { REDIS_CMD_APPEND_SSTR_STATIC(str, "COUNT"); - redis_cmd_append_sstr_long(str, count); + redis_cmd_append_sstr_long(str, opt->count); + } + + /* Append store options if we've got them */ + if (opt->store != STORE_NONE && opt->key != NULL) { + /* Grab string bits and prefix if requested */ + key = ZSTR_VAL(opt->key); + keylen = ZSTR_LEN(opt->key); + keyfree = redis_key_prefix(redis_sock, &key, &keylen); + + if (opt->store == STORE_COORD) { + REDIS_CMD_APPEND_SSTR_STATIC(str, "STORE"); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(str, "STOREDIST"); + } + + redis_cmd_append_sstr(str, key, keylen); + + CMD_SET_SLOT(slot, key, keylen); + if (keyfree) free(key); } } @@ -2678,14 +2745,13 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *unit; + short store_slot; strlen_t keylen, unitlen; - int keyfree, withcoord = 0, withdist = 0, withhash = 0; - long count = 0; - geoSortType sort = SORT_NONE; + int argc = 5, keyfree; double lng, lat, radius; zval *opts = NULL; + geoOptions gopts = {0}; smart_string cmdstr = {0}; - int argc; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sddds|a", &key, &keylen, &lng, &lat, &radius, &unit, &unitlen, &opts) @@ -2697,23 +2763,23 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Parse any GEORADIUS options we have */ if (opts != NULL) { /* Attempt to parse our options array */ - if (get_georadius_opts(Z_ARRVAL_P(opts), &withcoord, &withdist, - &withhash, &count, &sort TSRMLS_CC) != SUCCESS) + if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts TSRMLS_CC) != SUCCESS) { return FAILURE; } } - /* Calculate the number of arguments we're going to send, five required plus - * options. */ - argc = 5 + withcoord + withdist + withhash + (sort != SORT_NONE); - if (count) argc += 2; + /* Increment argc depending on options */ + argc += gopts.withcoord + gopts.withdist + gopts.withhash + + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0) + + (gopts.store != STORE_NONE ? 2 : 0); /* Begin construction of our command */ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUS"); - /* Apply any key prefix */ + /* Prefix and set slot */ keyfree = redis_key_prefix(redis_sock, &key, &keylen); + CMD_SET_SLOT(slot, key, keylen); /* Append required arguments */ redis_cmd_append_sstr(&cmdstr, key, keylen); @@ -2723,13 +2789,21 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, unit, unitlen); /* Append optional arguments */ - append_georadius_opts(&cmdstr, withcoord, withdist, withhash, count, sort); + append_georadius_opts(redis_sock, &cmdstr, slot ? &store_slot : NULL, &gopts); /* Free key if it was prefixed */ if (keyfree) efree(key); + if (gopts.key) zend_string_release(gopts.key); + + /* Protect the user from CROSSSLOT if we're in cluster */ + if (slot && gopts.store != STORE_NONE && *slot != store_slot) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Key and STORE[DIST] key must hash to the same slot"); + efree(cmdstr.c); + return FAILURE; + } /* Set slot, command and len, and return */ - CMD_SET_SLOT(slot, key, keylen); *cmd = cmdstr.c; *cmd_len = cmdstr.len; @@ -2742,10 +2816,10 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s { char *key, *mem, *unit; strlen_t keylen, memlen, unitlen; - int keyfree, argc, withcoord = 0, withdist = 0, withhash = 0; - long count = 0; + short store_slot; + int keyfree, argc = 4; double radius; - geoSortType sort = SORT_NONE; + geoOptions gopts = {0}; zval *opts = NULL; smart_string cmdstr = {0}; @@ -2757,22 +2831,22 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s if (opts != NULL) { /* Attempt to parse our options array */ - if (get_georadius_opts(Z_ARRVAL_P(opts), &withcoord, &withdist, - &withhash, &count, &sort TSRMLS_CC) != SUCCESS) - { + if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts TSRMLS_CC) == FAILURE) { return FAILURE; } } - /* Calculate argc */ - argc = 4 + withcoord + withdist + withhash + (sort != SORT_NONE); - if (count) argc += 2; + /* Increment argc based on options */ + argc += gopts.withcoord + gopts.withdist + gopts.withhash + + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0) + + (gopts.store != STORE_NONE ? 2 : 0); /* Begin command construction*/ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUSBYMEMBER"); - /* Prefix our key if we're prefixing */ + /* Prefix our key if we're prefixing and set the slot */ keyfree = redis_key_prefix(redis_sock, &key, &keylen); + CMD_SET_SLOT(slot, key, keylen); /* Append required arguments */ redis_cmd_append_sstr(&cmdstr, key, keylen); @@ -2781,12 +2855,20 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s redis_cmd_append_sstr(&cmdstr, unit, unitlen); /* Append options */ - append_georadius_opts(&cmdstr, withcoord, withdist, withhash, count, sort); + append_georadius_opts(redis_sock, &cmdstr, slot ? &store_slot : NULL, &gopts); /* Free key if we prefixed */ if (keyfree) efree(key); + if (gopts.key) zend_string_release(gopts.key); + + /* Protect the user from CROSSSLOT if we're in cluster */ + if (slot && gopts.store != STORE_NONE && *slot != store_slot) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Key and STORE[DIST] key must hash to the same slot"); + efree(cmdstr.c); + return FAILURE; + } - CMD_SET_SLOT(slot, key, keylen); *cmd = cmdstr.c; *cmd_len = cmdstr.len; diff --git a/redis_commands.h b/redis_commands.h index f2e9e4fa48..978c102606 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -21,13 +21,6 @@ typedef struct subscribeContext { zend_fcall_info_cache cb_cache; } subscribeContext; -/* Georadius sort type */ -typedef enum geoSortType { - SORT_NONE, - SORT_ASC, - SORT_DESC -} geoSortType; - /* Construct a raw command */ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC); /* Construct a script command */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 45d2c1efbe..fecec1e4b4 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5013,38 +5013,39 @@ public function genericGeoRadiusTest($cmd) { $lng = -121.837478; $lat = 39.728494; - $this->addCities('gk'); + $this->addCities('{gk}'); /* Pre tested with redis-cli. We're just verifying proper delivery of distance and unit */ if ($cmd == 'georadius') { - $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('gk', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array('georadius', 'gk', $lng, $lat, 500, 'mi'); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 10, 'mi'), Array('Chico')); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); + $args = Array('georadius', '{gk}', $lng, $lat, 500, 'mi'); /* Test a bad COUNT argument */ foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->georadius('gk', $lng, $lat, 10, 'mi', Array('count' => $count))); + $this->assertFalse(@$this->redis->georadius('{gk}', $lng, $lat, 10, 'mi', Array('count' => $count))); } } else { - $this->assertEquals($this->redis->georadiusbymember('gk', $city, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->georadiusbymember('gk', $city, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('gk', $city, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('gk', $city, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('gk', $city, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array('georadiusbymember', 'gk', $city, 500, 'mi'); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 10, 'mi'), Array('Chico')); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 30, 'mi'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 50, 'km'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 50000, 'm'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 150000, 'ft'), Array('Gridley', 'Chico')); + $args = Array('georadiusbymember', '{gk}', $city, 500, 'mi'); /* Test a bad COUNT argument */ foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->georadiusbymember('gk', $city, 10, 'mi', Array('count' => $count))); + $this->assertFalse(@$this->redis->georadiusbymember('{gk}', $city, 10, 'mi', Array('count' => $count))); } } /* Options */ $opts = Array('WITHCOORD', 'WITHDIST', 'WITHHASH'); $sortopts = Array('', 'ASC', 'DESC'); + $storeopts = Array('', 'STORE', 'STOREDIST'); for ($i = 0; $i < count($opts); $i++) { $subopts = array_slice($opts, 0, $i); @@ -5055,30 +5056,56 @@ public function genericGeoRadiusTest($cmd) { $subargs[] = $opt; } - for ($c = 0; $c < 3; $c++) { - /* Add a count if we're past first iteration */ - if ($c > 0) { - $subopts['count'] = $c; - $subargs[] = 'count'; - $subargs[] = $c; - } + /* Cannot mix STORE[DIST] with the WITH* arguments */ + $realstoreopts = count($subopts) == 0 ? $storeopts : Array(); + + $base_subargs = $subargs; + $base_subopts = $subopts; + + foreach ($realstoreopts as $store_type) { + + for ($c = 0; $c < 3; $c++) { + $subargs = $base_subargs; + $subopts = $base_subopts; - /* Adding optional sort */ - foreach ($sortopts as $sortopt) { - $realargs = $subargs; - $realopts = $subopts; - if ($sortopt) { - $realargs[] = $sortopt; - $realopts[] = $sortopt; + /* Add a count if we're past first iteration */ + if ($c > 0) { + $subopts['count'] = $c; + $subargs[] = 'count'; + $subargs[] = $c; } - $ret1 = $this->rawCommandArray('gk', $realargs); - if ($cmd == 'georadius') { - $ret2 = $this->redis->$cmd('gk', $lng, $lat, 500, 'mi', $realopts); - } else { - $ret2 = $this->redis->$cmd('gk', $city, 500, 'mi', $realopts); + /* 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') { + $ret2 = $this->redis->$cmd('{gk}', $lng, $lat, 500, 'mi', $realopts); + if ($ret2 == false) { + echo "BAD: " . implode(' ', ['{gk}', $lng, $lat, 500, 'mi']) . "\n"; + print_r($realopts); + die(); + } + } else { + $ret2 = $this->redis->$cmd('{gk}', $city, 500, 'mi', $realopts); + } + + if ($ret1 != $ret2) die(); + $this->assertEquals($ret1, $ret2); } - $this->assertEquals($ret1, $ret2); } } } From 42c6abd6e162de2a10460ede7e05c313f2fd1fb9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Apr 2018 12:24:13 -0700 Subject: [PATCH 0956/1986] Update documentation of GEORADIUS --- README.markdown | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 460f910477..6c1c6db751 100644 --- a/README.markdown +++ b/README.markdown @@ -3173,12 +3173,14 @@ The georadius command can be called with various options that control how Redis | | WITHHASH | Return the raw geohash-encoded score | | ASC | Sort results in ascending order | | DESC | Sort results in descending order +| STORE | _key_ | Store results in _key_ +| STOREDIST | _key_ | Store the results as distances in _key_ *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will be used. - *Note*: PhpRedis does not currently support the `STORE` or `STOREDIST` options but will be added to future versions. + *Note*: When using `STORE[DIST]` in Redis Cluster, the store key must has to the same slot as the query key or you will get a `CROSSLOT` error. ##### *Return value* -*Array*: Zero or more entries that are within the provided radius. +*Mixed*: When no `STORE` option is passed, this function returns an array of results. If it is passed this function returns the number of stored entries. ##### *Example* ~~~ From 368975bc30dc228d1cbe004ad5587c732da3f9d0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 7 Apr 2018 12:37:49 -0700 Subject: [PATCH 0957/1986] Remove debug code! :) --- tests/RedisTest.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index fecec1e4b4..15c216a00e 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5094,11 +5094,6 @@ public function genericGeoRadiusTest($cmd) { $ret1 = $this->rawCommandArray('{gk}', $realargs); if ($cmd == 'georadius') { $ret2 = $this->redis->$cmd('{gk}', $lng, $lat, 500, 'mi', $realopts); - if ($ret2 == false) { - echo "BAD: " . implode(' ', ['{gk}', $lng, $lat, 500, 'mi']) . "\n"; - print_r($realopts); - die(); - } } else { $ret2 = $this->redis->$cmd('{gk}', $city, 500, 'mi', $realopts); } From 1d7f899e784526d555c055878fbcd3deb9563228 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 9 Apr 2018 23:16:33 -0700 Subject: [PATCH 0958/1986] Alignment :) --- README.markdown | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.markdown b/README.markdown index 6c1c6db751..bbbeb0315d 100644 --- a/README.markdown +++ b/README.markdown @@ -3165,16 +3165,16 @@ _**Description**_: Return members of a set with geospatial information that are ##### *Options Array* The georadius command can be called with various options that control how Redis returns results. The following table describes the options phpredis supports. All options are case insensitive. -| Key | Value | Description -| :--- | :--- | :---- | -| COUNT | integer > 0 | Limit how many results are returned -| | WITHCOORD | Return longitude and latitude of matching members -| | WITHDIST | Return the distance from the center -| | WITHHASH | Return the raw geohash-encoded score -| | ASC | Sort results in ascending order -| | DESC | Sort results in descending order -| STORE | _key_ | Store results in _key_ -| STOREDIST | _key_ | Store the results as distances in _key_ +| Key | Value | Description +| :--- | :--- | :---- | +| COUNT | integer > 0 | Limit how many results are returned +| | WITHCOORD | Return longitude and latitude of matching members +| | WITHDIST | Return the distance from the center +| | WITHHASH | Return the raw geohash-encoded score +| | ASC | Sort results in ascending order +| | DESC | Sort results in descending order +| STORE | _key_ | Store results in _key_ +| STOREDIST | _key_ | Store the results as distances in _key_ *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will be used. *Note*: When using `STORE[DIST]` in Redis Cluster, the store key must has to the same slot as the query key or you will get a `CROSSLOT` error. From a53d5db6226b2409eed3e75459f522d34313b70f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Apr 2018 06:39:06 -0700 Subject: [PATCH 0959/1986] Consistent options variable name --- redis_commands.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 91369586f8..629ec1058e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2629,7 +2629,7 @@ geoStoreType get_georadius_store_type(zend_string *key) { } /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */ -static int get_georadius_opts(HashTable *ht, geoOptions *o TSRMLS_DC) { +static int get_georadius_opts(HashTable *ht, geoOptions *opts TSRMLS_DC) { ulong idx; char *optstr; zend_string *zkey; @@ -2647,16 +2647,16 @@ static int get_georadius_opts(HashTable *ht, geoOptions *o TSRMLS_DC) { if (Z_TYPE_P(optval) != IS_LONG || Z_LVAL_P(optval) <= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "COUNT must be an integer > 0!"); - if (o->key) zend_string_release(o->key); + if (opts->key) zend_string_release(opts->key); return FAILURE; } /* Set our count */ - o->count = Z_LVAL_P(optval); - } else if (o->store == STORE_NONE) { - o->store = get_georadius_store_type(zkey); - if (o->store != STORE_NONE) { - o->key = zval_get_string(optval); + opts->count = Z_LVAL_P(optval); + } else if (opts->store == STORE_NONE) { + opts->store = get_georadius_store_type(zkey); + if (opts->store != STORE_NONE) { + opts->key = zval_get_string(optval); } } } else { @@ -2666,25 +2666,25 @@ static int get_georadius_opts(HashTable *ht, geoOptions *o TSRMLS_DC) { optstr = Z_STRVAL_P(optval); if (!strcasecmp(optstr, "withcoord")) { - o->withcoord = 1; + opts->withcoord = 1; } else if (!strcasecmp(optstr, "withdist")) { - o->withdist = 1; + opts->withdist = 1; } else if (!strcasecmp(optstr, "withhash")) { - o->withhash = 1; + opts->withhash = 1; } else if (!strcasecmp(optstr, "asc")) { - o->sort = SORT_ASC; + opts->sort = SORT_ASC; } else if (!strcasecmp(optstr, "desc")) { - o->sort = SORT_DESC; + opts->sort = SORT_DESC; } } } ZEND_HASH_FOREACH_END(); /* STORE and STOREDIST are not compatible with the WITH* options */ - if (o->key != NULL && (o->withcoord || o->withdist || o->withhash)) { + if (opts->key != NULL && (opts->withcoord || opts->withdist || opts->withhash)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "STORE[DIST] is not compatible with WITHCOORD, WITHDIST or WITHHASH"); - if (o->key) zend_string_release(o->key); + if (opts->key) zend_string_release(opts->key); return FAILURE; } From 16893180210114dd82063652064b052cbaf457ce Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 11 Apr 2018 00:03:34 +0300 Subject: [PATCH 0960/1986] Refactor RedisArray Add `multihost_distribute_call` function for iterating over RA hosts and store results of functions into `return_value` array. --- redis_array.c | 88 ++++++++++++++++----------------------------------- 1 file changed, 28 insertions(+), 60 deletions(-) diff --git a/redis_array.c b/redis_array.c index 26d8f34381..93361107a0 100644 --- a/redis_array.c +++ b/redis_array.c @@ -624,6 +624,28 @@ PHP_METHOD(RedisArray, _rehash) } } +static void +multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int argc, zval *argv TSRMLS_DC) +{ + int i; + + /* Init our array return */ + array_init(return_value); + + /* Iterate our RedisArray nodes */ + for (i = 0; i < ra->count; ++i) { + zval zv, *z_tmp = &zv; +#if (PHP_MAJOR_VERSION < 7) + MAKE_STD_ZVAL(z_tmp); +#endif + /* Call each node in turn */ + call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, z_tmp, argc, argv); + + /* Add the result for this host */ + add_assoc_zval(return_value, ra->hosts[i], z_tmp); + } +} + static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) { zval *object, z_fun; @@ -642,18 +664,8 @@ static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *metho /* prepare call */ ZVAL_STRING(&z_fun, method_name); - array_init(return_value); - for(i = 0; i < ra->count; ++i) { - zval zv, *z_tmp = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#endif - - /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 0, NULL); + multihost_distribute_call(ra, return_value, &z_fun, 0, NULL TSRMLS_CC); - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } zval_dtor(&z_fun); } @@ -714,23 +726,8 @@ PHP_METHOD(RedisArray, keys) /* We will be passing with one string argument (the pattern) */ ZVAL_STRINGL(z_args, pattern, pattern_len); - /* Init our array return */ - array_init(return_value); + multihost_distribute_call(ra, return_value, &z_fun, 1, z_args TSRMLS_CC); - /* Iterate our RedisArray nodes */ - for(i = 0; i < ra->count; ++i) { - zval zv, *z_tmp = &zv; -#if (PHP_MAJOR_VERSION < 7) - /* Return for this node */ - MAKE_STD_ZVAL(z_tmp); -#endif - - /* Call KEYS on each node */ - call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); - - /* Add the result for this host */ - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } zval_dtor(&z_args[0]); zval_dtor(&z_fun); } @@ -757,18 +754,8 @@ PHP_METHOD(RedisArray, getOption) /* copy arg */ ZVAL_LONG(&z_args[0], opt); - array_init(return_value); - for(i = 0; i < ra->count; ++i) { - zval zv, *z_tmp = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#endif - - /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); + multihost_distribute_call(ra, return_value, &z_fun, 1, z_args TSRMLS_CC); - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } zval_dtor(&z_fun); } @@ -797,18 +784,8 @@ PHP_METHOD(RedisArray, setOption) ZVAL_LONG(&z_args[0], opt); ZVAL_STRINGL(&z_args[1], val_str, val_len); - array_init(return_value); - for(i = 0; i < ra->count; ++i) { - zval zv, *z_tmp = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#endif - - /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 2, z_args); + multihost_distribute_call(ra, return_value, &z_fun, 2, z_args TSRMLS_CC); - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } zval_dtor(&z_args[1]); zval_dtor(&z_fun); } @@ -835,20 +812,11 @@ PHP_METHOD(RedisArray, select) /* copy args */ ZVAL_LONG(&z_args[0], opt); - array_init(return_value); - for(i = 0; i < ra->count; ++i) { - zval zv, *z_tmp = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#endif - - /* Call each node in turn */ - call_user_function(&redis_ce->function_table, &ra->redis[i], &z_fun, z_tmp, 1, z_args); + multihost_distribute_call(ra, return_value, &z_fun, 1, z_args TSRMLS_CC); - add_assoc_zval(return_value, ra->hosts[i], z_tmp); - } zval_dtor(&z_fun); } + #if (PHP_MAJOR_VERSION < 7) #define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \ if (ra && ra->z_multi_exec) { \ From 65cf660188872f303741345d85a37f18e6e6eaa0 Mon Sep 17 00:00:00 2001 From: Mathieu Rochette Date: Wed, 11 Apr 2018 14:33:46 +0200 Subject: [PATCH 0961/1986] only the first arg of connect and pconnect is required see #1337 --- redis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis.c b/redis.c index 79640f2d38..7aaed838c3 100644 --- a/redis.c +++ b/redis.c @@ -82,7 +82,7 @@ PHP_INI_BEGIN() PHP_INI_END() /** {{{ Argument info for commands in redis 1.0 */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 2) +ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 1) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) ZEND_ARG_INFO(0, timeout) @@ -122,7 +122,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1) ZEND_ARG_INFO(0, option) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 2) +ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 1) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) ZEND_ARG_INFO(0, timeout) From 18d03fdb171e5dca7a261e67d71264deac2eaedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Tue, 17 Apr 2018 10:28:07 +0200 Subject: [PATCH 0962/1986] Typo in notes brEaking --- package.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.xml b/package.xml index b921107f46..fa7e5cc1b1 100644 --- a/package.xml +++ b/package.xml @@ -40,7 +40,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> phpredis 4.0.0RC1 - *** WARNING! THIS RELEASE CONTAINS BRAKING API CHANGES! *** + *** WARNING! THIS RELEASE CONTAINS BREAKING API CHANGES! *** * Add proper ARGINFO for all methods. (Pavlo Yatsukhnenko, Michael Grunder) * Let EXISTS take multiple keys [cccc39] (Michael Grunder) @@ -130,7 +130,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> phpredis 4.0.0RC1 - *** WARNING! THIS RELEASE CONTAINS BRAKING API CHANGES! *** + *** WARNING! THIS RELEASE CONTAINS BREAKING API CHANGES! *** * Add proper ARGINFO for all methods. (Pavlo Yatsukhnenko, Michael Grunder) * Let EXISTS take multiple keys [cccc39] (Michael Grunder) From 80d2a91744712c6026bf2707861510a4ba319ade Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 18 Apr 2018 15:41:08 +0300 Subject: [PATCH 0963/1986] Add instructions about lzf installation --- INSTALL.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/INSTALL.markdown b/INSTALL.markdown index 2187e4fa0c..1a56933120 100644 --- a/INSTALL.markdown +++ b/INSTALL.markdown @@ -12,11 +12,12 @@ To build this extension for the sources tree: ~~~ phpize -./configure [--enable-redis-igbinary] +./configure [--enable-redis-igbinary] [--enable-redis-lzf [--with-liblzf[=DIR]]] make && make install ~~~ If you would like phpredis to serialize your data using the igbinary library, run configure with `--enable-redis-igbinary`. +The extension also may compress data before sending it to Redis server, if you run configure with `--enable-redis-lzf`. If you want to use lzf library pre-installed into your system use `--with-liblzf` configuration option to specify the path where to search files. `make install` copies `redis.so` to an appropriate location, but you still need to enable the module in the PHP config file. To do so, either edit your php.ini or add a redis.ini file in `/etc/php5/conf.d` with the following contents: `extension=redis.so`. You can generate a debian package for PHP5, accessible from Apache 2 by running `./mkdeb-apache2.sh` or with `dpkg-buildpackage` or `svn-buildpackage`. From beb6e8f3aba298f79d23730a2ce95cef47698bd1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 22 Mar 2018 22:57:32 +0200 Subject: [PATCH 0964/1986] Issue #1302 Handle async parameter for Redis::flushDb and Redis::flushAll TODO: implementation for RedisArray and RedisCluster --- common.h | 4 ++++ redis.c | 12 ++++++------ redis_commands.c | 18 ++++++++++++++++++ redis_commands.h | 3 +++ 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/common.h b/common.h index f2062a9e9a..1843e85a6a 100644 --- a/common.h +++ b/common.h @@ -1092,4 +1092,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_georadiusbymember, 0, 0, 4) ZEND_ARG_ARRAY_INFO(0, opts, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0) + ZEND_ARG_INFO(0, async) +ZEND_END_ARG_INFO() + #endif diff --git a/redis.c b/redis.c index 7aaed838c3..afbe7d8656 100644 --- a/redis.c +++ b/redis.c @@ -267,8 +267,8 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, exists, arginfo_exists, ZEND_ACC_PUBLIC) PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - PHP_ME(Redis, flushAll, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, flushDB, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, flushAll, arginfo_flush, ZEND_ACC_PUBLIC) + PHP_ME(Redis, flushDB, arginfo_flush, ZEND_ACC_PUBLIC) PHP_ME(Redis, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) PHP_ME(Redis, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) PHP_ME(Redis, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) @@ -1736,17 +1736,17 @@ PHP_METHOD(Redis, lastSave) } /* }}} */ -/* {{{ proto bool Redis::flushDB() */ +/* {{{ proto bool Redis::flushDB([bool async]) */ PHP_METHOD(Redis, flushDB) { - REDIS_PROCESS_KW_CMD("FLUSHDB", redis_empty_cmd, redis_boolean_response); + REDIS_PROCESS_KW_CMD("FLUSHDB", redis_flush_cmd, redis_boolean_response); } /* }}} */ -/* {{{ proto bool Redis::flushAll() */ +/* {{{ proto bool Redis::flushAll([bool async]) */ PHP_METHOD(Redis, flushAll) { - REDIS_PROCESS_KW_CMD("FLUSHALL", redis_empty_cmd, redis_boolean_response); + REDIS_PROCESS_KW_CMD("FLUSHALL", redis_flush_cmd, redis_boolean_response); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 629ec1058e..739142e986 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -415,6 +415,24 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zend_bool async = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &async) == FAILURE) { + return FAILURE; + } + + if (async) { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1); + } else { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, ""); + } + + return SUCCESS; +} + /* Generic command where we take a key and a double */ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, diff --git a/redis_commands.h b/redis_commands.h index 978c102606..413b868e22 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -109,6 +109,9 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them From acd10409bc84f9d179281bcc9acd2c9f131578a7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 10 Apr 2018 23:45:13 +0300 Subject: [PATCH 0965/1986] Issue #1302 Handle async parameter for RedisArray::flushDb and RedisArray::flushAll TODO: implementation for RedisCluster and unit-tests --- redis_array.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/redis_array.c b/redis_array.c index 93361107a0..3d7f93665c 100644 --- a/redis_array.c +++ b/redis_array.c @@ -110,8 +110,8 @@ zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, del, arginfo_del, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, discard, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, exec, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, flushall, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(RedisArray, flushdb, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, flushall, arginfo_flush, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, flushdb, arginfo_flush, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, getOption, arginfo_getopt, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, info, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, keys, arginfo_keys, ZEND_ACC_PUBLIC) @@ -646,10 +646,10 @@ multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int a } } -static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) +static void +multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) { zval *object, z_fun; - int i; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", @@ -669,6 +669,31 @@ static void multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *metho zval_dtor(&z_fun); } +static void +multihost_distribute_flush(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) +{ + zval *object, z_fun, z_args[1]; + zend_bool async = 0; + RedisArray *ra; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|b", + &object, redis_array_ce, &async) == FAILURE) { + RETURN_FALSE; + } + + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } + + /* prepare call */ + ZVAL_STRING(&z_fun, method_name); + ZVAL_BOOL(&z_args[0], async); + + multihost_distribute_call(ra, return_value, &z_fun, 1, z_args TSRMLS_CC); + + zval_dtor(&z_fun); +} + PHP_METHOD(RedisArray, info) { multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "INFO"); @@ -681,12 +706,12 @@ PHP_METHOD(RedisArray, ping) PHP_METHOD(RedisArray, flushdb) { - multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB"); + multihost_distribute_flush(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB"); } PHP_METHOD(RedisArray, flushall) { - multihost_distribute(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL"); + multihost_distribute_flush(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL"); } PHP_METHOD(RedisArray, save) @@ -702,7 +727,7 @@ PHP_METHOD(RedisArray, bgsave) PHP_METHOD(RedisArray, keys) { - zval *object, z_args[1], z_fun; + zval *object, z_fun, z_args[1]; RedisArray *ra; char *pattern; strlen_t pattern_len; From 742cdd05940815b3ed41d5d1db1182d6eaa783b0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 22 Apr 2018 22:22:09 +0300 Subject: [PATCH 0966/1986] Issue #1302 Handle async parameter for RedisCluster::flushDb and RedisCluster::flushAll --- common.h | 4 --- redis.c | 4 +++ redis_array.c | 4 +++ redis_cluster.c | 65 +++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/common.h b/common.h index 1843e85a6a..f2062a9e9a 100644 --- a/common.h +++ b/common.h @@ -1092,8 +1092,4 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_georadiusbymember, 0, 0, 4) ZEND_ARG_ARRAY_INFO(0, opts, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0) - ZEND_ARG_INFO(0, async) -ZEND_END_ARG_INFO() - #endif diff --git a/redis.c b/redis.c index afbe7d8656..f40dc0d17a 100644 --- a/redis.c +++ b/redis.c @@ -108,6 +108,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0) + ZEND_ARG_INFO(0, async) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1) ZEND_ARG_INFO(0, cmd) #if PHP_VERSION_ID >= 50600 diff --git a/redis_array.c b/redis_array.c index 3d7f93665c..9f00a74c01 100644 --- a/redis_array.c +++ b/redis_array.c @@ -97,6 +97,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_multi, 0, 0, 1) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0) + ZEND_ARG_INFO(0, async) +ZEND_END_ARG_INFO() + zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, __call, arginfo_call, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, __construct, arginfo_ctor, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index 8b802011de..9b33d435b1 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -84,6 +84,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 1) ZEND_ARG_INFO(0, option) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 1) + ZEND_ARG_INFO(0, key_or_address) + ZEND_ARG_INFO(0, async) +ZEND_END_ARG_INFO() + /* Argument info for HSCAN, SSCAN, HSCAN */ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2) ZEND_ARG_INFO(0, str_key) @@ -136,8 +141,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, exists, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, expire, arginfo_expire, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, expireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, flushall, arginfo_key_or_address, ZEND_ACC_PUBLIC) - PHP_ME(RedisCluster, flushdb, arginfo_key_or_address, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushall, arginfo_flush, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, flushdb, arginfo_flush, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, geodist, arginfo_geodist, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) @@ -2334,6 +2339,50 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, efree(cmd); } +static void +cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply_type, cluster_cb cb) +{ + redisCluster *c = GET_CONTEXT(); + char *cmd; + int cmd_len; + zval *z_arg; + zend_bool async = 0; + short slot; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &z_arg, &async) == FAILURE) { + RETURN_FALSE; + } + + // One argument means find the node (treated like a key), and two means + // send the command to a specific host and port + slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); + if (slot < 0) { + RETURN_FALSE; + } + + // Construct our command + if (async) { + cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1); + } else { + cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, kw, ""); + } + + + // Kick off our command + if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC) < 0) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to send command at a specific node", 0 TSRMLS_CC); + efree(cmd); + RETURN_FALSE; + } + + // Our response callback + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + + // Free our command + efree(cmd); +} + /* Generic routine for handling various commands which need to be directed at * a node, but have complex syntax. We simply parse out the arguments and send * the command as constructed by the caller */ @@ -2611,18 +2660,18 @@ PHP_METHOD(RedisCluster, bgsave) { } /* }}} */ -/* {{{ proto RedisCluster::flushdb(string key) - * proto RedisCluster::flushdb(string host, long port) */ +/* {{{ proto RedisCluster::flushdb(string key, [bool async]) + * proto RedisCluster::flushdb(array host_port, [bool async]) */ PHP_METHOD(RedisCluster, flushdb) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB", + cluster_flush_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB", TYPE_LINE, cluster_bool_resp); } /* }}} */ -/* {{{ proto RedisCluster::flushall(string key) - * proto RedisCluster::flushall(string host, long port) */ +/* {{{ proto RedisCluster::flushall(string key, [bool async]) + * proto RedisCluster::flushall(array host_port, [bool async]) */ PHP_METHOD(RedisCluster, flushall) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL", + cluster_flush_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL", TYPE_LINE, cluster_bool_resp); } /* }}} */ From 97bb6bd85e3fa38dbb13b97c50bd315490265a80 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 17 May 2018 07:38:26 -0700 Subject: [PATCH 0967/1986] Bulk strings can be zero length Addresses #1349 --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 65c7f349be..09d71602a9 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -146,7 +146,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, r->integer = len; break; case TYPE_BULK: - if (r->len > 0) { + if (r->len >= 0) { r->str = redis_sock_read_bulk_reply(sock,r->len TSRMLS_CC); if (!r->str) { *err = 1; From e2d5b5ea495b93e40392b6c694cb240f2a4dd486 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Tue, 22 May 2018 17:24:47 +0200 Subject: [PATCH 0968/1986] PHPREDIS-1354: Added session TTL tests --- tests/RedisTest.php | 42 ++++++++++++++++++++++++++++++++++++++---- tests/startSession.php | 6 ++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 15c216a00e..da4e2523a0 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5217,7 +5217,7 @@ public function testSession_lockReleasedOnClose() $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); } - public function testSession_ttlMaxExecutionTime() + public function testSession_lock_ttlMaxExecutionTime() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); @@ -5233,7 +5233,7 @@ public function testSession_ttlMaxExecutionTime() $this->assertTrue($sessionSuccessful); } - public function testSession_ttlLockExpire() + public function testSession_lock_ttlLockExpire() { $this->setSessionHandler(); $sessionId = $this->generateSessionId(); @@ -5468,6 +5468,37 @@ public function testSession_regenerateSessionId_withLock_withDestroy_withProxy( $this->assertEquals('bar', $this->getSessionData($newSessionId)); } + public function testSession_ttl_equalsToSessionLifetime() + { + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); + $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); + + $this->assertEquals(600, $ttl); + } + + public function testSession_ttl_resetOnWrite() + { + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); + $this->redis->expire($this->sessionPrefix . $sessionId, 9999); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); + $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); + + $this->assertEquals(600, $ttl); + } + + public function testSession_ttl_resetOnRead() + { + $sessionId = $this->generateSessionId(); + $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); + $this->redis->expire($this->sessionPrefix . $sessionId, 9999); + $this->getSessionData($sessionId); + $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); + + $this->assertEquals(600, $ttl); + } + private function setSessionHandler() { $host = $this->getHost() ?: 'localhost'; @@ -5503,15 +5534,18 @@ private function generateSessionId() * @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 = '') + 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(), 0, 7) == "Windows"){ $this->markTestSkipped(); return true; } else { - $commandParameters = array($this->getHost(), $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData); + $commandParameters = array($this->getHost(), $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData, $sessionLifetime); if ($locking_enabled) { $commandParameters[] = '1'; diff --git a/tests/startSession.php b/tests/startSession.php index 081a6951db..4317f7474f 100644 --- a/tests/startSession.php +++ b/tests/startSession.php @@ -8,6 +8,7 @@ $lock_retries = $argv[5]; $lock_expire = $argv[6]; $sessionData = $argv[7]; +$sessionLifetime = $argv[8]; if (empty($redisHost)) { $redisHost = 'localhost'; @@ -18,13 +19,14 @@ ini_set('max_execution_time', $maxExecutionTime); ini_set('redis.session.lock_retries', $lock_retries); ini_set('redis.session.lock_expire', $lock_expire); +ini_set('session.gc_maxlifetime', $sessionLifetime); if (isset($argv[8])) { - ini_set('redis.session.locking_enabled', $argv[8]); + ini_set('redis.session.locking_enabled', $argv[9]); } if (isset($argv[9])) { - ini_set('redis.session.lock_wait_time', $argv[9]); + ini_set('redis.session.lock_wait_time', $argv[10]); } session_id($sessionId); From 21436f61dd969350a75817a70f4678edc70bad29 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Wed, 23 May 2018 12:13:13 +0200 Subject: [PATCH 0969/1986] PHPREDIS-1354: Added session lifetime parameter for reading session data --- tests/RedisTest.php | 7 ++++--- tests/getSessionData.php | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index da4e2523a0..03db4fc759 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5493,7 +5493,7 @@ public function testSession_ttl_resetOnRead() $sessionId = $this->generateSessionId(); $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); $this->redis->expire($this->sessionPrefix . $sessionId, 9999); - $this->getSessionData($sessionId); + $this->getSessionData($sessionId, 600); $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); $this->assertEquals(600, $ttl); @@ -5565,12 +5565,13 @@ private function startSessionProcess($sessionId, $sleepTime, $background, $maxEx /** * @param string $sessionId + * @param int $sessionLifetime * * @return string */ - private function getSessionData($sessionId) + private function getSessionData($sessionId, $sessionLifetime = 1440) { - $command = 'php ' . __DIR__ . '/getSessionData.php ' . escapeshellarg($this->getHost()) . ' ' . escapeshellarg($sessionId); + $command = 'php ' . __DIR__ . '/getSessionData.php ' . escapeshellarg($this->getHost()) . ' ' . escapeshellarg($sessionId) . ' ' . escapeshellarg($sessionLifetime); exec($command, $output); return $output[0]; diff --git a/tests/getSessionData.php b/tests/getSessionData.php index b5bea74a42..587fdd26ea 100644 --- a/tests/getSessionData.php +++ b/tests/getSessionData.php @@ -3,6 +3,7 @@ $redisHost = $argv[1]; $sessionId = $argv[2]; +$sessionLifetime = $argv[3]; if (empty($redisHost)) { $redisHost = 'localhost'; @@ -10,6 +11,7 @@ ini_set('session.save_handler', 'redis'); ini_set('session.save_path', 'tcp://' . $redisHost . ':6379'); +ini_set('session.gc_maxlifetime', $sessionLifetime); session_id($sessionId); if (!session_start()) { From 28ec432247f597216846593d62b21e2eef8461c5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 31 May 2018 11:02:39 +0300 Subject: [PATCH 0970/1986] Issue #1358 Use `cluster_free` in `free_cluster_context` to free cluster object. Call `cluster_disconnect` before destroying cluster object. --- cluster_library.c | 13 +++++++++---- cluster_library.h | 2 +- redis_cluster.c | 34 +++++++++++++--------------------- redis_session.c | 4 ++-- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 09d71602a9..305db2ab5a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -831,9 +831,14 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, return c; } -PHP_REDIS_API void cluster_free(redisCluster *c) { +PHP_REDIS_API void +cluster_free(redisCluster *c, int free_ctx TSRMLS_DC) +{ + /* Disconnect from each node we're connected to */ + cluster_disconnect(c TSRMLS_CC); + /* Free any allocated prefix */ - if (c->flags->prefix) efree(c->flags->prefix); + if (c->flags->prefix) zend_string_release(c->flags->prefix); efree(c->flags); /* Call hash table destructors */ @@ -848,7 +853,7 @@ PHP_REDIS_API void cluster_free(redisCluster *c) { if (c->err) zend_string_release(c->err); /* Free structure itself */ - efree(c); + if (free_ctx) efree(c); } /* Takes our input hash table and returns a straigt C array with elements, @@ -1069,7 +1074,7 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC) { redisClusterNode *node; ZEND_HASH_FOREACH_PTR(c->nodes, node) { - if (node == NULL) break; + if (node == NULL) continue; redis_sock_disconnect(node->sock TSRMLS_CC); node->sock->lazy_connect = 1; } ZEND_HASH_FOREACH_END(); diff --git a/cluster_library.h b/cluster_library.h index b3abd7e144..2ac2c796e8 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -368,7 +368,7 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, int failover, int persistent); -PHP_REDIS_API void cluster_free(redisCluster *c); +PHP_REDIS_API void cluster_free(redisCluster *c, int free_ctx TSRMLS_DC); PHP_REDIS_API int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC); PHP_REDIS_API void cluster_free_node(redisClusterNode *node); diff --git a/redis_cluster.c b/redis_cluster.c index 9b33d435b1..d2788a7d97 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -362,34 +362,26 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { } /* Free redisCluster context */ -void #if (PHP_MAJOR_VERSION < 7) -free_cluster_context(void *object TSRMLS_DC) { +void +free_cluster_context(void *object TSRMLS_DC) +{ redisCluster *cluster = (redisCluster*)object; + + cluster_free(cluster, 0 TSRMLS_CC); + zend_object_std_dtor(&cluster->std TSRMLS_CC); + efree(cluster); +} #else -free_cluster_context(zend_object *object) { +void +free_cluster_context(zend_object *object) +{ redisCluster *cluster = (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std)); -#endif - // Free any allocated prefix, as well as the struct - if (cluster->flags->prefix) efree(cluster->flags->prefix); - efree(cluster->flags); - - // Free seeds HashTable itself - zend_hash_destroy(cluster->seeds); - efree(cluster->seeds); - - // Destroy all Redis objects and free our nodes HashTable - zend_hash_destroy(cluster->nodes); - efree(cluster->nodes); - - if (cluster->err) zend_string_release(cluster->err); + cluster_free(cluster, 0 TSRMLS_CC); zend_object_std_dtor(&cluster->std TSRMLS_CC); - -#if (PHP_MAJOR_VERSION < 7) - efree(cluster); -#endif } +#endif /* Attempt to connect to a Redis cluster provided seeds and timeout options */ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, diff --git a/redis_session.c b/redis_session.c index 4ce88e07df..06e7993a53 100644 --- a/redis_session.c +++ b/redis_session.c @@ -947,7 +947,7 @@ PS_OPEN_FUNC(rediscluster) { PS_SET_MOD_DATA(c); retval = SUCCESS; } else { - cluster_free(c); + cluster_free(c, 1 TSRMLS_CC); retval = FAILURE; } @@ -1108,7 +1108,7 @@ PS_CLOSE_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); if (c) { - cluster_free(c); + cluster_free(c, 1 TSRMLS_CC); PS_SET_MOD_DATA(NULL); } return SUCCESS; From aaaf0f233afe404bc1d8c6a600af860448ee0bd6 Mon Sep 17 00:00:00 2001 From: Hongbo Liu Date: Fri, 18 May 2018 05:41:47 +0800 Subject: [PATCH 0971/1986] Add callbacks validate_sid() & update_timestamp() to session handler Newer session handler API(PS_MOD_UPDATE_TIMESTAMP) supports 2 more callbacks: * validate_sid() is used by session.use_strict_mode, which provides better security. * update_timestamp() is used by session.lazy_write, which can improve performance in some situations. --- redis_session.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ redis_session.h | 5 +++ 2 files changed, 111 insertions(+) diff --git a/redis_session.c b/redis_session.c index 06e7993a53..9063226917 100644 --- a/redis_session.c +++ b/redis_session.c @@ -61,9 +61,16 @@ /* Check if a response is the Redis +OK status response */ #define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3)) +#if (PHP_MAJOR_VERSION < 7) ps_module ps_mod_redis = { PS_MOD_SID(redis) }; +#else +ps_module ps_mod_redis = { + PS_MOD_UPDATE_TIMESTAMP(redis) +}; +#endif + ps_module ps_mod_redis_cluster = { PS_MOD(rediscluster) }; @@ -636,6 +643,105 @@ PS_CREATE_SID_FUNC(redis) } /* }}} */ +#if (PHP_MAJOR_VERSION >= 7) +/* {{{ PS_VALIDATE_SID_FUNC + */ +PS_VALIDATE_SID_FUNC(redis) +{ + char *cmd, *response; + int cmd_len, response_len; +#if (PHP_MAJOR_VERSION < 7) + const char *skey = key; + size_t skeylen = strlen(key); +#else + const char *skey = ZSTR_VAL(key); + size_t skeylen = ZSTR_LEN(key); +#endif + + if (!skeylen) return FAILURE; + + redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); + RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; + if (!redis_sock) { + return FAILURE; + } + + /* send EXISTS command */ + zend_string *session = redis_session_key(rpm, skey, skeylen); + cmd_len = REDIS_SPPRINTF(&cmd, "EXISTS", "S", session); + zend_string_release(session); + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return FAILURE; + } + efree(cmd); + + /* read response */ + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return FAILURE; + } + + if (response_len == 2 && response[0] == ':' && response[1] == '1') { + efree(response); + return SUCCESS; + } else { + efree(response); + return FAILURE; + } +} +/* }}} */ + +/* {{{ PS_UPDATE_TIMESTAMP_FUNC + */ +PS_UPDATE_TIMESTAMP_FUNC(redis) +{ + char *cmd, *response; + int cmd_len, response_len; +#if (PHP_MAJOR_VERSION < 7) + const char *skey = key, *sval = val; + size_t skeylen = strlen(key), svallen = vallen; +#else + const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); + size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); +#endif + + if (!skeylen) return FAILURE; + + redis_pool *pool = PS_GET_MOD_DATA(); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); + RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; + if (!redis_sock) { + return FAILURE; + } + + /* send EXPIRE command */ + zend_string *session = redis_session_key(rpm, skey, skeylen); + cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, INI_INT("session.gc_maxlifetime")); + zend_string_release(session); + + if (!write_allowed(redis_sock, &pool->lock_status TSRMLS_CC) || redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + efree(cmd); + return FAILURE; + } + efree(cmd); + + /* read response */ + if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + return FAILURE; + } + + if (response_len == 2 && response[0] == ':' && response[1] == '1') { + efree(response); + return SUCCESS; + } else { + efree(response); + return FAILURE; + } +} +/* }}} */ +#endif + /* {{{ PS_READ_FUNC */ PS_READ_FUNC(redis) diff --git a/redis_session.h b/redis_session.h index 9ab3e5f1b2..6c6b101fc5 100644 --- a/redis_session.h +++ b/redis_session.h @@ -11,6 +11,11 @@ PS_DESTROY_FUNC(redis); PS_GC_FUNC(redis); PS_CREATE_SID_FUNC(redis); +#if (PHP_MAJOR_VERSION >= 7) +PS_VALIDATE_SID_FUNC(redis); +PS_UPDATE_TIMESTAMP_FUNC(redis); +#endif + PS_OPEN_FUNC(rediscluster); PS_CLOSE_FUNC(rediscluster); PS_READ_FUNC(rediscluster); From 1bb7fe48fa7a102776eef1d4b557cae63d0990b3 Mon Sep 17 00:00:00 2001 From: Hongbo Liu Date: Tue, 29 May 2018 22:07:02 +0800 Subject: [PATCH 0972/1986] Minor refactoring --- redis_session.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/redis_session.c b/redis_session.c index 9063226917..099f353ac3 100644 --- a/redis_session.c +++ b/redis_session.c @@ -61,15 +61,13 @@ /* Check if a response is the Redis +OK status response */ #define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3)) -#if (PHP_MAJOR_VERSION < 7) ps_module ps_mod_redis = { +#if (PHP_MAJOR_VERSION < 7) PS_MOD_SID(redis) -}; #else -ps_module ps_mod_redis = { PS_MOD_UPDATE_TIMESTAMP(redis) -}; #endif +}; ps_module ps_mod_redis_cluster = { PS_MOD(rediscluster) @@ -650,13 +648,9 @@ PS_VALIDATE_SID_FUNC(redis) { char *cmd, *response; int cmd_len, response_len; -#if (PHP_MAJOR_VERSION < 7) - const char *skey = key; - size_t skeylen = strlen(key); -#else + const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key); -#endif if (!skeylen) return FAILURE; @@ -691,27 +685,25 @@ PS_VALIDATE_SID_FUNC(redis) } } /* }}} */ +#endif +#if (PHP_MAJOR_VERSION >= 7) /* {{{ PS_UPDATE_TIMESTAMP_FUNC */ PS_UPDATE_TIMESTAMP_FUNC(redis) { char *cmd, *response; int cmd_len, response_len; -#if (PHP_MAJOR_VERSION < 7) - const char *skey = key, *sval = val; - size_t skeylen = strlen(key), svallen = vallen; -#else + const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); -#endif if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; - if (!redis_sock) { + if (!redis_sock || !write_allowed(redis_sock, &pool->lock_status TSRMLS_CC)) { return FAILURE; } @@ -720,7 +712,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, INI_INT("session.gc_maxlifetime")); zend_string_release(session); - if (!write_allowed(redis_sock, &pool->lock_status TSRMLS_CC) || redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { efree(cmd); return FAILURE; } From d0cada2268a807af255255a53e5f3d79bda644b9 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Wed, 6 Jun 2018 09:55:50 +0200 Subject: [PATCH 0973/1986] PHPREDIS-1354: Refactored tests to work also with Redis cluster --- tests/RedisClusterTest.php | 22 +++++++++++++++++++--- tests/RedisTest.php | 15 ++++++++++----- tests/TestSuite.php | 13 +++++++++++++ tests/getSessionData.php | 11 ++++++----- tests/regenerateSessionId.php | 15 ++++++++------- tests/startSession.php | 25 +++++++++++++------------ 6 files changed, 69 insertions(+), 32 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index f841fa2692..74515d4fd8 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -28,6 +28,11 @@ class Redis_Cluster_Test extends Redis_Test { */ protected $sessionPrefix = 'PHPREDIS_CLUSTER_SESSION:'; + /** + * @var string + */ + protected $sessionSaveHandler = 'rediscluster'; + /* Tests we'll skip all together in the context of RedisCluster. The * RedisCluster class doesn't implement specialized (non-redis) commands * such as sortAsc, or sortDesc and other commands such as SELECT are @@ -44,7 +49,6 @@ public function testConnectException() { return $this->markTestSkipped(); } /* Session locking feature is currently not supported in in context of Redis Cluster. The biggest issue for this is the distribution nature of Redis cluster */ - public function testSession_savedToRedis() { return $this->markTestSkipped(); } public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); } public function testSession_lockingDisabledByDefault() { return $this->markTestSkipped(); } public function testSession_lockReleasedOnClose() { return $this->markTestSkipped(); } @@ -596,8 +600,8 @@ protected function rawCommandArray($key, $args) { public function testSession() { - ini_set('session.save_handler', 'rediscluster'); - ini_set('session.save_path', implode('&', array_map(function ($seed) { + @ini_set('session.save_handler', 'rediscluster'); + @ini_set('session.save_path', implode('&', array_map(function ($seed) { return 'seed[]=' . $seed; }, self::$_arr_node_map)) . '&failover=error'); if (!@session_start()) { @@ -606,5 +610,17 @@ public function testSession() session_write_close(); $this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id())); } + + /** + * @inheritdoc + */ + protected function getFullHostPath() + { + $hosts = array_map(function ($host) { + return 'seed[]=' . $host . ''; + }, self::$_arr_node_map); + + return implode('&', $hosts); + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 03db4fc759..ff0b753768 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -26,6 +26,11 @@ class Redis_Test extends TestSuite */ protected $sessionPrefix = 'PHPREDIS_SESSION:'; + /** + * @var string + */ + protected $sessionSaveHandler = 'redis'; + public function setUp() { $this->redis = $this->newInstance(); $info = $this->redis->info(); @@ -5503,8 +5508,8 @@ private function setSessionHandler() { $host = $this->getHost() ?: 'localhost'; - ini_set('session.save_handler', 'redis'); - ini_set('session.save_path', 'tcp://' . $host . ':6379'); + @ini_set('session.save_handler', 'redis'); + @ini_set('session.save_path', 'tcp://' . $host . ':6379'); } /** @@ -5545,7 +5550,7 @@ private function startSessionProcess($sessionId, $sleepTime, $background, $maxEx $this->markTestSkipped(); return true; } else { - $commandParameters = array($this->getHost(), $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData, $sessionLifetime); + $commandParameters = array($this->getFullHostPath(), $this->sessionSaveHandler, $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData, $sessionLifetime); if ($locking_enabled) { $commandParameters[] = '1'; @@ -5571,7 +5576,7 @@ private function startSessionProcess($sessionId, $sleepTime, $background, $maxEx */ private function getSessionData($sessionId, $sessionLifetime = 1440) { - $command = 'php ' . __DIR__ . '/getSessionData.php ' . escapeshellarg($this->getHost()) . ' ' . escapeshellarg($sessionId) . ' ' . escapeshellarg($sessionLifetime); + $command = 'php ' . __DIR__ . '/getSessionData.php ' . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . escapeshellarg($sessionId) . ' ' . escapeshellarg($sessionLifetime); exec($command, $output); return $output[0]; @@ -5589,7 +5594,7 @@ private function regenerateSessionId($sessionId, $locking = false, $destroyPrevi { $args = array_map('escapeshellarg', array($sessionId, $locking, $destroyPrevious, $sessionProxy)); - $command = 'php --no-php-ini --define extension=igbinary.so --define extension=' . __DIR__ . '/../modules/redis.so ' . __DIR__ . '/regenerateSessionId.php ' . escapeshellarg($this->getHost()) . ' ' . implode(' ', $args); + $command = 'php --no-php-ini --define extension=igbinary.so --define extension=' . __DIR__ . '/../modules/redis.so ' . __DIR__ . '/regenerateSessionId.php ' . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . implode(' ', $args); exec($command, $output); diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 03186bf983..461ac7dc9f 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -27,6 +27,19 @@ public function __construct($str_host) { public function getHost() { return $this->str_host; } + /** + * Returns the fully qualified host path, + * which may be used directly for php.ini parameters like session.save_path + * + * @return null|string + */ + protected function getFullHostPath() + { + return $this->str_host + ? 'tcp://' . $this->str_host . ':6379' + : null; + } + public static function make_bold($str_msg) { return self::$_boo_colorize ? self::$BOLD_ON . $str_msg . self::$BOLD_OFF diff --git a/tests/getSessionData.php b/tests/getSessionData.php index 587fdd26ea..d49256c218 100644 --- a/tests/getSessionData.php +++ b/tests/getSessionData.php @@ -2,15 +2,16 @@ error_reporting(E_ERROR | E_WARNING); $redisHost = $argv[1]; -$sessionId = $argv[2]; -$sessionLifetime = $argv[3]; +$saveHandler = $argv[2]; +$sessionId = $argv[3]; +$sessionLifetime = $argv[4]; if (empty($redisHost)) { - $redisHost = 'localhost'; + $redisHost = 'tcp://localhost:6379'; } -ini_set('session.save_handler', 'redis'); -ini_set('session.save_path', 'tcp://' . $redisHost . ':6379'); +ini_set('session.save_handler', $saveHandler); +ini_set('session.save_path', $redisHost); ini_set('session.gc_maxlifetime', $sessionLifetime); session_id($sessionId); diff --git a/tests/regenerateSessionId.php b/tests/regenerateSessionId.php index 6d49c0ab9d..f0e4c4e0fd 100644 --- a/tests/regenerateSessionId.php +++ b/tests/regenerateSessionId.php @@ -2,17 +2,18 @@ error_reporting(E_ERROR | E_WARNING); $redisHost = $argv[1]; -$sessionId = $argv[2]; -$locking = !!$argv[3]; -$destroyPrevious = !!$argv[4]; -$sessionProxy = !!$argv[5]; +$saveHandler = $argv[2]; +$sessionId = $argv[3]; +$locking = !!$argv[4]; +$destroyPrevious = !!$argv[5]; +$sessionProxy = !!$argv[6]; if (empty($redisHost)) { - $redisHost = 'localhost'; + $redisHost = 'tcp://localhost:6379'; } -ini_set('session.save_handler', 'redis'); -ini_set('session.save_path', 'tcp://' . $redisHost . ':6379'); +ini_set('session.save_handler', $saveHandler); +ini_set('session.save_path', $redisHost); if ($locking) { ini_set('redis.session.locking_enabled', true); diff --git a/tests/startSession.php b/tests/startSession.php index 4317f7474f..2149da684b 100644 --- a/tests/startSession.php +++ b/tests/startSession.php @@ -2,31 +2,32 @@ error_reporting(E_ERROR | E_WARNING); $redisHost = $argv[1]; -$sessionId = $argv[2]; -$sleepTime = $argv[3]; -$maxExecutionTime = $argv[4]; -$lock_retries = $argv[5]; -$lock_expire = $argv[6]; -$sessionData = $argv[7]; -$sessionLifetime = $argv[8]; +$saveHandler = $argv[2]; +$sessionId = $argv[3]; +$sleepTime = $argv[4]; +$maxExecutionTime = $argv[5]; +$lock_retries = $argv[6]; +$lock_expire = $argv[7]; +$sessionData = $argv[8]; +$sessionLifetime = $argv[9]; if (empty($redisHost)) { - $redisHost = 'localhost'; + $redisHost = 'tcp://localhost:6379'; } -ini_set('session.save_handler', 'redis'); -ini_set('session.save_path', 'tcp://' . $redisHost . ':6379'); +ini_set('session.save_handler', $saveHandler); +ini_set('session.save_path', $redisHost); ini_set('max_execution_time', $maxExecutionTime); ini_set('redis.session.lock_retries', $lock_retries); ini_set('redis.session.lock_expire', $lock_expire); ini_set('session.gc_maxlifetime', $sessionLifetime); if (isset($argv[8])) { - ini_set('redis.session.locking_enabled', $argv[9]); + ini_set('redis.session.locking_enabled', $argv[10]); } if (isset($argv[9])) { - ini_set('redis.session.lock_wait_time', $argv[10]); + ini_set('redis.session.lock_wait_time', $argv[11]); } session_id($sessionId); From c4e4248dbe3352274304e766d6020a4c319fdef6 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 8 Jun 2018 10:46:04 +0200 Subject: [PATCH 0974/1986] use PHP_BINARY instead of php and allow override honours TEST_PHP_EXECUTABLE and TEST_PHP_ARGS (as standard php tests) --- tests/RedisTest.php | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index ff0b753768..cdfa5fd3af 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5560,9 +5560,8 @@ private function startSessionProcess($sessionId, $sleepTime, $background, $maxEx } $commandParameters = array_map('escapeshellarg', $commandParameters); - $command = 'php ' . __DIR__ . '/startSession.php ' . implode(' ', $commandParameters); + $command = self::getPhpCommand('startSession.php') . implode(' ', $commandParameters); $command .= $background ? ' 2>/dev/null > /dev/null &' : ' 2>&1'; - exec($command, $output); return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')) ? true : false; } @@ -5576,7 +5575,7 @@ private function startSessionProcess($sessionId, $sleepTime, $background, $maxEx */ private function getSessionData($sessionId, $sessionLifetime = 1440) { - $command = 'php ' . __DIR__ . '/getSessionData.php ' . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . escapeshellarg($sessionId) . ' ' . escapeshellarg($sessionLifetime); + $command = self::getPhpCommand('getSessionData.php') . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . escapeshellarg($sessionId) . ' ' . escapeshellarg($sessionLifetime); exec($command, $output); return $output[0]; @@ -5594,11 +5593,31 @@ private function regenerateSessionId($sessionId, $locking = false, $destroyPrevi { $args = array_map('escapeshellarg', array($sessionId, $locking, $destroyPrevious, $sessionProxy)); - $command = 'php --no-php-ini --define extension=igbinary.so --define extension=' . __DIR__ . '/../modules/redis.so ' . __DIR__ . '/regenerateSessionId.php ' . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . implode(' ', $args); + $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 function getPhpCommand($script) + { + static $cmd = NULL; + + if (!$cmd) { + $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: (defined('PHP_BINARY') ? PHP_BINARY : 'php')); // PHP_BINARY is 5.4+ + $cmd .= ' '; + $cmd .= (getenv('TEST_PHP_ARGS') ?: '--no-php-ini --define extension=igbinary.so --define extension=' . dirname(__DIR__) . '/modules/redis.so'); + } + return $cmd . ' ' . __DIR__ . '/' . $script . ' '; + } } ?> From ff32a634151fd471cbd5b39e26cd56af63fe7ea5 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 8 Jun 2018 16:16:53 +0200 Subject: [PATCH 0975/1986] Fix: Warning: time() expects exactly 0 parameters, 1 given ... (php 7.3) --- tests/RedisTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index ff0b753768..e48309d550 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -452,7 +452,7 @@ public function testSetTimeout() { public function testExpireAt() { $this->redis->del('key'); $this->redis->set('key', 'value'); - $now = time(NULL); + $now = time(); $this->redis->expireAt('key', $now + 1); $this->assertEquals('value', $this->redis->get('key')); sleep(2); From a53e1a340069ef1c14026a31b6f97d40c01eb7c3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 3 Jul 2018 08:57:05 +0300 Subject: [PATCH 0976/1986] Issue #1370 --- cluster_library.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cluster_library.c b/cluster_library.c index 305db2ab5a..0a93c4e6a8 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2125,6 +2125,7 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, /* Protect against an invalid response type, -1 response length, and failure * to consume the responses. */ c->cmd_sock->serializer = c->flags->serializer; + c->cmd_sock->compression = c->flags->compression; short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL TSRMLS_CC) == FAILURE; From c3b023b0a111b61e2a2763ea63034138007944a9 Mon Sep 17 00:00:00 2001 From: "akihito.nakano" Date: Sat, 7 Jul 2018 22:22:54 +0900 Subject: [PATCH 0977/1986] Highlight php codes --- README.markdown | 276 ++++++++++++++++++++++++------------------------ 1 file changed, 138 insertions(+), 138 deletions(-) diff --git a/README.markdown b/README.markdown index 5dd2f095cd..3a441e20e7 100644 --- a/README.markdown +++ b/README.markdown @@ -113,7 +113,7 @@ _**Description**_: Creates a Redis client ##### *Example* -~~~ +~~~php $redis = new Redis(); ~~~ @@ -171,7 +171,7 @@ _**Description**_: Connects to a Redis instance. ##### *Example* -~~~ +~~~php $redis->connect('127.0.0.1', 6379); $redis->connect('127.0.0.1'); // port 6379 by default $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout. @@ -208,7 +208,7 @@ persistent equivalents. ##### *Example* -~~~ +~~~php $redis->pconnect('127.0.0.1', 6379); $redis->pconnect('127.0.0.1'); // port 6379 by default - same connection like before. $redis->pconnect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout and would be another connection than the two before. @@ -228,7 +228,7 @@ _**Description**_: Authenticate the connection using a password. *BOOL*: `TRUE` if the connection is authenticated, `FALSE` otherwise. ##### *Example* -~~~ +~~~php $redis->auth('foobared'); ~~~ @@ -258,7 +258,7 @@ _**Description**_: Swap one Redis database with another atomically *note*: Requires Redis >= 4.0.0 ##### *Example* -~~~ +~~~php $redis->swapdb(0, 1); /* Swaps DB 0 with DB 1 atomically */ ~~~ @@ -278,7 +278,7 @@ _**Description**_: Set client option. *BOOL*: `TRUE` on success, `FALSE` on error. ##### *Example* -~~~ +~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // don't serialize data $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // use built-in serialize/unserialize $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize @@ -307,7 +307,7 @@ _**Description**_: Get client option. Parameter value. ##### *Example* -~~~ +~~~php $redis->getOption(Redis::OPT_SERIALIZER); // return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, or Redis::SERIALIZER_IGBINARY. ~~~ @@ -364,7 +364,7 @@ None. *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->bgRewriteAOF(); ~~~ @@ -379,7 +379,7 @@ None. *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. ##### *Example* -~~~ +~~~php $redis->bgSave(); ~~~ @@ -397,7 +397,7 @@ _**Description**_: Get or Set the Redis server configuration parameters. *bool* for `SET` ##### *Examples* -~~~ +~~~php $redis->config("GET", "*max-*-entries*"); $redis->config("SET", "dir", "/var/run/redis/dumps/"); ~~~ @@ -413,7 +413,7 @@ None. *INTEGER*: DB size, in number of keys. ##### *Example* -~~~ +~~~php $count = $redis->dbSize(); echo "Redis has $count keys\n"; ~~~ @@ -429,7 +429,7 @@ None. *BOOL*: Always `TRUE`. ##### *Example* -~~~ +~~~php $redis->flushAll(); ~~~ @@ -444,7 +444,7 @@ None. *BOOL*: Always `TRUE`. ##### *Example* -~~~ +~~~php $redis->flushDb(); ~~~ @@ -476,7 +476,7 @@ which will modify what is returned. *option*: The option to provide redis (e.g. "COMMANDSTATS", "CPU") ##### *Example* -~~~ +~~~php $redis->info(); /* standard redis INFO command */ $redis->info("COMMANDSTATS"); /* Information on the commands that have been run (>=2.6 only) $redis->info("CPU"); /* just CPU information from Redis INFO */ @@ -493,7 +493,7 @@ None. *INT*: timestamp. ##### *Example* -~~~ +~~~php $redis->lastSave(); ~~~ @@ -517,7 +517,7 @@ None. *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->resetStat(); ~~~ @@ -532,7 +532,7 @@ None. *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. ##### *Example* -~~~ +~~~php $redis->save(); ~~~ @@ -547,7 +547,7 @@ Either host (string) and port (int), or no parameter to stop being a slave. *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->slaveOf('10.0.1.7', 6379); /* ... */ $redis->slaveOf(); @@ -565,7 +565,7 @@ If successful, the time will come back as an associative array with element zero the unix timestamp, and element one being microseconds. ##### *Examples* -~~~ +~~~php $redis->time(); ~~~ @@ -586,7 +586,7 @@ SLOWLOG RESET: Boolean, depending on success ##### ##### *Examples* -~~~ +~~~php // Get ten slowLog entries $redis->slowLog('get', 10); // Get the default number of slowLog entries @@ -659,7 +659,7 @@ _**Description**_: Get the value related to the specified key ##### *Examples* -~~~ +~~~php $redis->get('key'); ~~~ @@ -676,7 +676,7 @@ _**Description**_: Set the string value in argument as value of the key. If you *Bool* `TRUE` if the command is successful. ##### *Examples* -~~~ +~~~php // Simple key -> value set $redis->set('key', 'value'); @@ -705,7 +705,7 @@ _**Description**_: Set the string value in argument as value of the key, with a ##### *Examples* -~~~ +~~~php $redis->setEx('key', 3600, 'value'); // sets key → value, with 1h TTL. $redis->pSetEx('key', 100, 'value'); // sets key → value, with 0.1 sec TTL. ~~~ @@ -722,7 +722,7 @@ _**Description**_: Set the string value in argument as value of the key if the k *Bool* `TRUE` in case of success, `FALSE` in case of failure. ##### *Examples* -~~~ +~~~php $redis->setNx('key', 'value'); /* return TRUE */ $redis->setNx('key', 'value'); /* return FALSE */ ~~~ @@ -740,7 +740,7 @@ An array of keys, or an undefined number of parameters, each a key: *key1* *key2 *Long* Number of keys deleted. ##### *Examples* -~~~ +~~~php $redis->set('key1', 'val1'); $redis->set('key2', 'val2'); $redis->set('key3', 'val3'); @@ -766,7 +766,7 @@ _**Description**_: Verify if the specified key exists. *long*: The number of keys tested that do exist. ##### *Examples* -~~~ +~~~php $redis->set('key', 'value'); $redis->exists('key'); /* 1 */ $redis->exists('NonExistingKey'); /* 0 */ @@ -790,7 +790,7 @@ _**Description**_: Increment the number stored at key by one. If the second argu *INT* the new value ##### *Examples* -~~~ +~~~php $redis->incr('key1'); /* key1 didn't exists, set to 0 before the increment */ /* and now has the value 1 */ @@ -816,7 +816,7 @@ _**Description**_: Increment the key with floating point precision. *FLOAT* the new value ##### *Examples* -~~~ +~~~php $redis->incrByFloat('key1', 1.5); /* key1 didn't exist, so it will now be 1.5 */ @@ -837,7 +837,7 @@ _**Description**_: Decrement the number stored at key by one. If the second argu *INT* the new value ##### *Examples* -~~~ +~~~php $redis->decr('key1'); /* key1 didn't exists, set to 0 before the increment */ /* and now has the value -1 */ @@ -861,7 +861,7 @@ _**Description**_: Get the values of all the specified keys. If one or more keys *Array*: Array containing the values related to keys in argument ##### *Examples* -~~~ +~~~php $redis->set('key1', 'value1'); $redis->set('key2', 'value2'); $redis->set('key3', 'value3'); @@ -880,7 +880,7 @@ _**Description**_: Sets a value and returns the previous entry at that key. ##### *Return value* A string, the previous value located at this key. ##### *Example* -~~~ +~~~php $redis->set('x', '42'); $exValue = $redis->getSet('x', 'lol'); // return '42', replaces x by 'lol' $newValue = $redis->get('x')' // return 'lol' @@ -896,7 +896,7 @@ None. *STRING*: an existing key in redis. ##### *Example* -~~~ +~~~php $key = $redis->randomKey(); $surprise = $redis->get($key); // who knows what's in there. ~~~ @@ -914,7 +914,7 @@ _**Description**_: Moves a key to a different database. *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->select(0); // switch to DB 0 $redis->set('x', '42'); // write 42 to x $redis->move('x', 1); // move to DB 1 @@ -933,7 +933,7 @@ _**Description**_: Renames a key. ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->set('x', '42'); $redis->rename('x', 'y'); $redis->get('y'); // → 42 @@ -956,7 +956,7 @@ _**Description**_: Sets an expiration date (a timeout) on an item. pexpire requi ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->set('x', '42'); $redis->setTimeout('x', 3); // x will disappear in 3 seconds. sleep(5); // wait 5 seconds @@ -975,7 +975,7 @@ _**Description**_: Sets an expiration date (a timestamp) on an item. pexpireAt r ##### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->set('x', '42'); $now = time(NULL); // current timestamp $redis->expireAt('x', $now + 3); // x will disappear in 3 seconds. @@ -994,7 +994,7 @@ _**Description**_: Returns the keys that match a certain pattern. *Array of STRING*: The keys that match a certain pattern. ##### *Example* -~~~ +~~~php $allKeys = $redis->keys('*'); // all keys will match this. $keyWithUserPrefix = $redis->keys('user*'); ~~~ @@ -1012,7 +1012,7 @@ _**Description**_: Scan the keyspace for keys *Array, boolean*: This function will return an array of keys or FALSE if Redis returned zero keys ##### *Example* -~~~ +~~~php /* Without enabling Redis::SCAN_RETRY (default condition) */ $it = NULL; @@ -1058,7 +1058,7 @@ The information to retrieve (string) and the key (string). Info can be one of th *STRING* for "encoding", *LONG* for "refcount" and "idletime", `FALSE` if the key doesn't exist. ##### *Example* -~~~ +~~~php $redis->object("encoding", "l"); // → ziplist $redis->object("refcount", "l"); // → 1 $redis->object("idletime", "l"); // → 400 (in seconds, with a precision of 10 seconds). @@ -1082,7 +1082,7 @@ hash: Redis::REDIS_HASH other: Redis::REDIS_NOT_FOUND ##### *Example* -~~~ +~~~php $redis->type('key'); ~~~ @@ -1098,7 +1098,7 @@ _**Description**_: Append specified string to the string stored in specified key *INTEGER*: Size of the value after the append ##### *Example* -~~~ +~~~php $redis->set('key', 'value1'); $redis->append('key', 'value2'); /* 12 */ $redis->get('key'); /* 'value1value2' */ @@ -1119,7 +1119,7 @@ _**Description**_: Return a substring of a larger string *STRING*: the substring ##### *Example* -~~~ +~~~php $redis->set('key', 'string value'); $redis->getRange('key', 0, 5); /* 'string' */ $redis->getRange('key', -5, -1); /* 'value' */ @@ -1138,7 +1138,7 @@ _**Description**_: Changes a substring of a larger string. *STRING*: the length of the string after it was modified. ##### *Example* -~~~ +~~~php $redis->set('key', 'Hello world'); $redis->setRange('key', 6, "redis"); /* returns 11 */ $redis->get('key'); /* "Hello redis" */ @@ -1155,7 +1155,7 @@ _**Description**_: Get the length of a string value. *INTEGER* ##### *Example* -~~~ +~~~php $redis->set('key', 'value'); $redis->strlen('key'); /* 5 */ ~~~ @@ -1172,7 +1172,7 @@ _**Description**_: Return a single bit out of a larger string *LONG*: the bit value (0 or 1) ##### *Example* -~~~ +~~~php $redis->set('key', "\x7f"); // this is 0111 1111 $redis->getBit('key', 0); /* 0 */ $redis->getBit('key', 1); /* 1 */ @@ -1191,7 +1191,7 @@ _**Description**_: Changes a single bit of a string. *LONG*: 0 or 1, the value of the bit before it was set. ##### *Example* -~~~ +~~~php $redis->set('key', "*"); // ord("*") = 42 = 0x2f = "0010 1010" $redis->setBit('key', 5, 1); /* returns 0 */ $redis->setBit('key', 7, 1); /* returns 0 */ @@ -1240,7 +1240,7 @@ _**Description**_: Sort the elements in a list, set or sorted set. An array of values, or a number corresponding to the number of elements stored if that was used. ##### *Example* -~~~ +~~~php $redis->delete('s'); $redis->sAdd('s', 5); $redis->sAdd('s', 4); @@ -1267,7 +1267,7 @@ _**Description**_: Returns the time to live left for a given key in seconds (ttl *LONG*: The time to live in seconds. If the key has no ttl, `-1` will be returned, and `-2` if the key doesn't exist. ##### *Example* -~~~ +~~~php $redis->ttl('key'); ~~~ @@ -1282,7 +1282,7 @@ _**Description**_: Remove the expiration timer from a key. *BOOL*: `TRUE` if a timeout was removed, `FALSE` if the key didn’t exist or didn’t have an expiration timer. ##### *Example* -~~~ +~~~php $redis->persist('key'); ~~~ @@ -1297,7 +1297,7 @@ _**Description**_: Sets multiple key-value pairs in one atomic command. MSETNX o *Bool* `TRUE` in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->mSet(array('key0' => 'value0', 'key1' => 'value1')); var_dump($redis->get('key0')); @@ -1321,7 +1321,7 @@ that comes out of DUMP is a binary representation of the key as Redis stores it. ##### *Return value* The Redis encoded value of the key, or FALSE if the key doesn't exist ##### *Examples* -~~~ +~~~php $redis->set('foo', 'bar'); $val = $redis->dump('foo'); // $val will be the Redis encoded key value ~~~ @@ -1334,7 +1334,7 @@ _**Description**_: Restore a key from the result of a DUMP operation. *ttl* integer. How long the key should live (if zero, no expire will be set on the key) *value* string (binary). The Redis encoded key value (from DUMP) ##### *Examples* -~~~ +~~~php $redis->set('foo', 'bar'); $val = $redis->dump('foo'); $redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key 'foo' @@ -1356,7 +1356,7 @@ that version in order to call `migrate` with an array of keys. *copy* boolean, optional. Should we send the COPY flag to redis. *replace* boolean, optional. Should we send the REPLACE flag to redis ##### *Examples* -~~~ +~~~php $redis->migrate('backup', 6379, 'foo', 0, 3600); $redis->migrate('backup', 6379, 'foo', 0, 3600, true, true); /* copy and replace */ $redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE flag */ @@ -1396,7 +1396,7 @@ _**Description**_: Adds a value to the hash stored at key. ##### *Return value* *LONG* `1` if value didn't exist and was added successfully, `0` if the value was already present and was replaced, `FALSE` if there was an error. ##### *Example* -~~~ +~~~php $redis->delete('h') $redis->hSet('h', 'key1', 'hello'); /* 1, 'key1' => 'hello' in the hash at "h" */ $redis->hGet('h', 'key1'); /* returns "hello" */ @@ -1413,7 +1413,7 @@ _**Description**_: Adds a value to the hash stored at key only if this field isn *BOOL* `TRUE` if the field was set, `FALSE` if it was already present. ##### *Example* -~~~ +~~~php $redis->delete('h') $redis->hSetNx('h', 'key1', 'hello'); /* TRUE, 'key1' => 'hello' in the hash at "h" */ $redis->hSetNx('h', 'key1', 'world'); /* FALSE, 'key1' => 'hello' in the hash at "h". No change since the field wasn't replaced. */ @@ -1441,7 +1441,7 @@ _**Description**_: Returns the length of a hash, in number of items ##### *Return value* *LONG* the number of items in a hash, `FALSE` if the key doesn't exist or isn't a hash. ##### *Example* -~~~ +~~~php $redis->delete('h') $redis->hSet('h', 'key1', 'hello'); $redis->hSet('h', 'key2', 'plop'); @@ -1472,7 +1472,7 @@ _**Description**_: Returns the keys in a hash, as an array of strings. An array of elements, the keys of the hash. This works like PHP's array_keys(). ##### *Example* -~~~ +~~~php $redis->delete('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); @@ -1507,7 +1507,7 @@ _**Description**_: Returns the values in a hash, as an array of strings. An array of elements, the values of the hash. This works like PHP's array_values(). ##### *Example* -~~~ +~~~php $redis->delete('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); @@ -1542,7 +1542,7 @@ _**Description**_: Returns the whole hash, as an array of strings indexed by str An array of elements, the contents of the hash. ##### *Example* -~~~ +~~~php $redis->delete('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); @@ -1575,7 +1575,7 @@ _**Description**_: Verify if the specified member exists in a key. ##### *Return value* *BOOL*: If the member exists in the hash table, return `TRUE`, otherwise return `FALSE`. ##### *Examples* -~~~ +~~~php $redis->hSet('h', 'a', 'x'); $redis->hExists('h', 'a'); /* TRUE */ $redis->hExists('h', 'NonExistingKey'); /* FALSE */ @@ -1591,7 +1591,7 @@ _**Description**_: Increments the value of a member from a hash by a given amoun ##### *Return value* *LONG* the new value ##### *Examples* -~~~ +~~~php $redis->delete('h'); $redis->hIncrBy('h', 'x', 2); /* returns 2: h[x] = 2 now. */ $redis->hIncrBy('h', 'x', 1); /* h[x] ← 2 + 1. Returns 3 */ @@ -1607,7 +1607,7 @@ _**Description**_: Increments the value of a hash member by the provided float v ##### *Return value* *FLOAT* the new value ##### *Examples* -~~~ +~~~php $redis->delete('h'); $redis->hIncrByFloat('h','x', 1.5); /* returns 1.5: h[x] = 1.5 now */ $redis->hIncrByFloat('h', 'x', 1.5); /* returns 3.0: h[x] = 3.0 now */ @@ -1623,7 +1623,7 @@ _**Description**_: Fills in a whole hash. Non-string values are converted to str ##### *Return value* *BOOL* ##### *Examples* -~~~ +~~~php $redis->delete('user:1'); $redis->hMSet('user:1', array('name' => 'Joe', 'salary' => 2000)); $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. @@ -1638,7 +1638,7 @@ _**Description**_: Retrieve the values associated to the specified fields in the ##### *Return value* *Array* An array of elements, the values of the specified fields in the hash, with the hash keys as array keys. ##### *Examples* -~~~ +~~~php $redis->delete('h'); $redis->hSet('h', 'field1', 'value1'); $redis->hSet('h', 'field2', 'value2'); @@ -1657,7 +1657,7 @@ _**Description**_: Scan a HASH value for members, with an optional pattern and *Array* An array of members that match our pattern ##### *Examples* -~~~ +~~~php $it = NULL; /* Don't ever return an empty array until we're done iterating */ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); @@ -1716,7 +1716,7 @@ Or *ARRAY* array('listName', 'element') ##### *Example* -~~~ +~~~php /* Non blocking feature */ $redis->lPush('key1', 'A'); $redis->delete('key2'); @@ -1773,7 +1773,7 @@ Return `FALSE` in case of a bad index or a key that doesn't point to a list. *Bool* `FALSE` if the key identifies a non-string data type, or no value corresponds to this index in the list `Key`. ##### *Example* -~~~ +~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ @@ -1799,7 +1799,7 @@ If the list didn't exists, or the pivot didn't exists, the value is not inserted The number of the elements in the list, -1 if the pivot didn't exists. ##### *Example* -~~~ +~~~php $redis->delete('key1'); $redis->lInsert('key1', Redis::AFTER, 'A', 'X'); /* 0 */ @@ -1828,7 +1828,7 @@ _**Description**_: Return and remove the first element of the list. *BOOL* `FALSE` in case of failure (empty list) ##### *Example* -~~~ +~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ @@ -1847,7 +1847,7 @@ _**Description**_: Adds the string value to the head (left) of the list. Creates *LONG* The new length of the list in case of success, `FALSE` in case of Failure. ##### *Examples* -~~~ +~~~php $redis->delete('key1'); $redis->lPush('key1', 'C'); // returns 1 $redis->lPush('key1', 'B'); // returns 2 @@ -1867,7 +1867,7 @@ _**Description**_: Adds the string value to the head (left) of the list if the l *LONG* The new length of the list in case of success, `FALSE` in case of Failure. ##### *Examples* -~~~ +~~~php $redis->delete('key1'); $redis->lPushx('key1', 'A'); // returns 0 $redis->lPush('key1', 'A'); // returns 1 @@ -1891,7 +1891,7 @@ _**Description**_: Returns the specified elements of the list stored at the spec *Array* containing the values in specified range. ##### *Example* -~~~ +~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); @@ -1914,7 +1914,7 @@ _**Description**_: Removes the first `count` occurrences of the value element fr *BOOL* `FALSE` if the value identified by key is not a list. ##### *Example* -~~~ +~~~php $redis->lPush('key1', 'A'); $redis->lPush('key1', 'B'); $redis->lPush('key1', 'C'); @@ -1939,7 +1939,7 @@ _**Description**_: Set the list at index with the new value. *BOOL* `TRUE` if the new value was set. `FALSE` if the index is out of range, or data type identified by key is not a list. ##### *Example* -~~~ +~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ @@ -1962,7 +1962,7 @@ _**Description**_: Trims an existing list so that it will contain only a specifi *Bool* return `FALSE` if the key identify a non-list value. ##### *Example* -~~~ +~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); @@ -1983,7 +1983,7 @@ _**Description**_: Returns and removes the last element of the list. *BOOL* `FALSE` in case of failure (empty list) ##### *Example* -~~~ +~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ @@ -2002,7 +2002,7 @@ _**Description**_: Pops a value from the tail of a list, and pushes it to the fr *STRING* The element that was moved in case of success, `FALSE` in case of failure. ##### *Example* -~~~ +~~~php $redis->delete('x', 'y'); $redis->lPush('x', 'abc'); @@ -2045,7 +2045,7 @@ _**Description**_: Adds the string value to the tail (right) of the list. Create *LONG* The new length of the list in case of success, `FALSE` in case of Failure. ##### *Examples* -~~~ +~~~php $redis->delete('key1'); $redis->rPush('key1', 'A'); // returns 1 $redis->rPush('key1', 'B'); // returns 2 @@ -2065,7 +2065,7 @@ _**Description**_: Adds the string value to the tail (right) of the list if the *LONG* The new length of the list in case of success, `FALSE` in case of Failure. ##### *Examples* -~~~ +~~~php $redis->delete('key1'); $redis->rPushX('key1', 'A'); // returns 0 $redis->rPush('key1', 'A'); // returns 1 @@ -2088,7 +2088,7 @@ If the list didn't exist or is empty, the command returns 0. If the data type id *BOOL* `FALSE` if the data type identified by Key is not list ##### *Example* -~~~ +~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ @@ -2126,7 +2126,7 @@ _**Description**_: Adds a value to the set value stored at key. If this value is ##### *Return value* *LONG* the number of elements added to the set. ##### *Example* -~~~ +~~~php $redis->sAdd('key1' , 'member1'); /* 1, 'key1' => {'member1'} */ $redis->sAdd('key1' , 'member2', 'member3'); /* 2, 'key1' => {'member1', 'member2', 'member3'}*/ $redis->sAdd('key1' , 'member2'); /* 0, 'key1' => {'member1', 'member2', 'member3'}*/ @@ -2140,7 +2140,7 @@ _**Description**_: Returns the cardinality of the set identified by key. ##### *Return value* *LONG* the cardinality of the set identified by key, 0 if the set doesn't exist. ##### *Example* -~~~ +~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ @@ -2159,7 +2159,7 @@ _**Description**_: Performs the difference between N sets and returns it. *Array of strings*: The difference of the first set will all the others. ##### *Example* -~~~ +~~~php $redis->delete('s0', 's1', 's2'); $redis->sAdd('s0', '1'); @@ -2193,7 +2193,7 @@ _**Description**_: Performs the same action as sDiff, but stores the result in t *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* -~~~ +~~~php $redis->delete('s0', 's1', 's2'); $redis->sAdd('s0', '1'); @@ -2234,7 +2234,7 @@ key1, key2, keyN: keys identifying the different sets on which we will apply the Array, contain the result of the intersection between those keys. If the intersection between the different sets is empty, the return value will be empty array. ##### *Examples* -~~~ +~~~php $redis->sAdd('key1', 'val1'); $redis->sAdd('key1', 'val2'); $redis->sAdd('key1', 'val3'); @@ -2272,7 +2272,7 @@ _**Description**_: Performs a sInter command and stores the result in a new set. *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* -~~~ +~~~php $redis->sAdd('key1', 'val1'); $redis->sAdd('key1', 'val2'); $redis->sAdd('key1', 'val3'); @@ -2311,7 +2311,7 @@ _**Description**_: Checks if `value` is a member of the set stored at the key `k ##### *Return value* *BOOL* `TRUE` if `value` is a member of the set at key `key`, `FALSE` otherwise. ##### *Example* -~~~ +~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ @@ -2331,7 +2331,7 @@ _**Description**_: Returns the contents of a set. An array of elements, the contents of the set. ##### *Example* -~~~ +~~~php $redis->delete('s'); $redis->sAdd('s', 'a'); $redis->sAdd('s', 'b'); @@ -2363,7 +2363,7 @@ _**Description**_: Moves the specified member from the set at srcKey to the set ##### *Return value* *BOOL* If the operation is successful, return `TRUE`. If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, `FALSE` is returned. ##### *Example* -~~~ +~~~php $redis->sAdd('key1' , 'member11'); $redis->sAdd('key1' , 'member12'); $redis->sAdd('key1' , 'member13'); /* 'key1' => {'member11', 'member12', 'member13'}*/ @@ -2387,7 +2387,7 @@ _**Description**_: Removes and returns a random element from the set value at Ke *Array*: Member(s) returned or an empty array if the set doesn't exist *Bool*: `FALSE` on error if the key is not a set ##### *Example* -~~~ +~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/ @@ -2411,7 +2411,7 @@ is provided, an array of values from the set will be returned. Read about the d ways to use the count here: [SRANDMEMBER](http://redis.io/commands/srandmember) *Bool* `FALSE` if set identified by key is empty or doesn't exist. ##### *Example* -~~~ +~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member3', 'member1', 'member2'}*/ @@ -2437,7 +2437,7 @@ _**Description**_: Removes the specified member from the set value stored at key ##### *Return value* *LONG* The number of elements removed from the set. ##### *Example* -~~~ +~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}*/ @@ -2455,7 +2455,7 @@ _**Description**_: Performs the union between N sets and returns it. *Array of strings*: The union of all these sets. ##### *Example* -~~~ +~~~php $redis->delete('s0', 's1', 's2'); $redis->sAdd('s0', '1'); @@ -2494,7 +2494,7 @@ _**Description**_: Performs the same action as sUnion, but stores the result in *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. ##### *Example* -~~~ +~~~php $redis->delete('s0', 's1', 's2'); $redis->sAdd('s0', '1'); @@ -2536,7 +2536,7 @@ _**Description**_: Scan a set for members *Array, boolean*: PHPRedis will return an array of keys or FALSE when we're done iterating ##### *Example* -~~~ +~~~php $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* don't return empty results until we're done */ while($arr_mems = $redis->sScan('set', $it, "*pattern*")) { @@ -2590,7 +2590,7 @@ _**Description**_: Add one or more members to a sorted set or update its score i *Long* 1 if the element is added. 0 otherwise. ##### *Example* -~~~ +~~~php $redis->zAdd('key', 1, 'val1'); $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 5, 'val5'); @@ -2608,7 +2608,7 @@ _**Description**_: Returns the cardinality of an ordered set. *Long*, the set's cardinality ##### *Example* -~~~ +~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); @@ -2628,7 +2628,7 @@ _**Description**_: Returns the *number* of elements of the sorted set stored at *LONG* the size of a corresponding zRangeByScore. ##### *Example* -~~~ +~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); @@ -2648,7 +2648,7 @@ _**Description**_: Increments the score of a member from a sorted set by a given *DOUBLE* the new value ##### *Examples* -~~~ +~~~php $redis->delete('key'); $redis->zIncrBy('key', 2.5, 'member1'); /* key or member1 didn't exist, so member1's score is to 0 before the increment */ /* and now has the value 2.5 */ @@ -2672,7 +2672,7 @@ The forth argument defines the `AGGREGATE` option which specify how the results *LONG* The number of values in the new sorted set. ##### *Example* -~~~ +~~~php $redis->delete('k1'); $redis->delete('k2'); $redis->delete('k3'); @@ -2715,7 +2715,7 @@ Start and stop are interpreted as zero-based indices: *Array* containing the values in specified range. ##### *Example* -~~~ +~~~php $redis->zAdd('key1', 0, 'val0'); $redis->zAdd('key1', 2, 'val2'); $redis->zAdd('key1', 10, 'val10'); @@ -2741,7 +2741,7 @@ Two options are available: `withscores => TRUE`, and `limit => array($offset, $c *Array* containing the values in specified range. ##### *Example* -~~~ +~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); @@ -2766,7 +2766,7 @@ _**Description**_: Returns a lexicographical range of members in a sorted set, *Array* containing the values in the specified range. ##### *Example* -~~~ +~~~php foreach(Array('a','b','c','d','e','f','g') as $c) $redis->zAdd('key',0,$c); @@ -2787,7 +2787,7 @@ _**Description**_: Returns the rank of a given member in the specified sorted se *Long*, the item's score. ##### *Example* -~~~ +~~~php $redis->delete('z'); $redis->zAdd('key', 1, 'one'); $redis->zAdd('key', 2, 'two'); @@ -2809,7 +2809,7 @@ _**Description**_: Deletes a specified member from the ordered set. *LONG* 1 on success, 0 on failure. ##### *Example* -~~~ +~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); @@ -2830,7 +2830,7 @@ _**Description**_: Deletes the elements of the sorted set stored at the specifie *LONG* The number of values deleted from the sorted set ##### *Example* -~~~ +~~~php $redis->zAdd('key', 1, 'one'); $redis->zAdd('key', 2, 'two'); $redis->zAdd('key', 3, 'three'); @@ -2851,7 +2851,7 @@ _**Description**_: Deletes the elements of the sorted set stored at the specifie *LONG* The number of values deleted from the sorted set ##### *Example* -~~~ +~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); @@ -2874,7 +2874,7 @@ _**Description**_: Returns the elements of the sorted set stored at the specifie *Array* containing the values in specified range. ##### *Example* -~~~ +~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); @@ -2896,7 +2896,7 @@ _**Description**_: Returns the score of a given member in the specified sorted s *Double* ##### *Example* -~~~ +~~~php $redis->zAdd('key', 2.5, 'val2'); $redis->zScore('key', 'val2'); /* 2.5 */ ~~~ @@ -2918,7 +2918,7 @@ The forth argument defines the `AGGREGATE` option which specify how the results *LONG* The number of values in the new sorted set. ##### *Example* -~~~ +~~~php $redis->delete('k1'); $redis->delete('k2'); $redis->delete('k3'); @@ -2953,7 +2953,7 @@ _**Description**_: Scan a sorted set for members, with optional pattern and coun *Array, boolean* PHPRedis will return matching keys from Redis, or FALSE when iteration is complete ##### *Example* -~~~ +~~~php $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { @@ -2969,7 +2969,7 @@ while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { ----- ##### *Prototype* -~~~ +~~~php $redis->geoAdd($key, $longitude, $latitude, $member [, $longitude, $latitude, $member, ...]); ~~~ @@ -2979,7 +2979,7 @@ _**Description**_: Add one or more geospatial items to the specified key. This *Integer*: The number of elements added to the geospatial key. ##### *Example* -~~~ +~~~php $redis->del("myplaces"); /* Since the key will be new, $result will be 2 */ @@ -2994,7 +2994,7 @@ $result = $redis->geoAdd( ----- ##### *Prototype* -~~~ +~~~php $redis->geoHash($key, $member [, $member, $member, ...]); ~~~ @@ -3004,7 +3004,7 @@ _**Description**_: Retrieve Geohash strings for one or more elements of a geosp *Array*: One or more Redis Geohash encoded strings. ##### *Example* -~~~ +~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); $hashes = $redis->geoHash("hawaii", "Honolulu", "Maui"); var_dump($hashes); @@ -3024,7 +3024,7 @@ array(2) { ----- ##### *Prototype* -~~~ +~~~php $redis->geoPos($key, $member [, $member, $member, ...]); ~~~ @@ -3034,7 +3034,7 @@ _**Description**_: Return longitude, latitude positions for each requested memb *Array*: One or more longitude/latitude positions ##### *Example* -~~~ +~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); $positions = $redis->geoPos("hawaii", "Honolulu", "Maui"); var_dump($positions); @@ -3064,7 +3064,7 @@ array(2) { ----- ##### *Prototype* -~~~ +~~~php $redis->geoDist($key, $member1, $member2 [, $unit]); ~~~ @@ -3080,7 +3080,7 @@ _**Description**_: Return the distance between two members in a geospatial set. *Double*: The distance between the two passed members in the units requested (meters by default). ##### *Example* -~~~ +~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); $meters = $redis->geoDist("hawaii", "Honolulu", "Maui"); @@ -3115,7 +3115,7 @@ bool(false) ----- ##### *Prototype* -~~~ +~~~php $redis->geoRadius($key, $longitude, $latitude, $radius, $unit [, Array $options]); ~~~ @@ -3142,7 +3142,7 @@ The georadius command can be called with various options that control how Redis *Mixed*: When no `STORE` option is passed, this function returns an array of results. If it is passed this function returns the number of stored entries. ##### *Example* -~~~ +~~~php /* Add some cities */ $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); @@ -3216,7 +3216,7 @@ array(1) { ### geoRadiusByMember ##### *Prototype* -~~~ +~~~php $redis->geoRadiusByMember($key, $member, $radius, $units [, Array $options]); ~~~ @@ -3229,7 +3229,7 @@ See [geoRadius](#georadius) command for options array. *Array*: The zero or more entries that are close enough to the member given the distance and radius specified. ##### *Example* -~~~ +~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); echo "Within 300 miles of Honolulu:\n"; @@ -3273,7 +3273,7 @@ _**Description**_: Subscribe to channels by pattern *callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* -~~~ +~~~php function pSubscribe($redis, $pattern, $chan, $msg) { echo "Pattern: $pattern\n"; echo "Channel: $chan\n"; @@ -3290,7 +3290,7 @@ _**Description**_: Publish messages to channels. Warning: this function will pro *message*: string ##### *Example* -~~~ +~~~php $redis->publish('chan-1', 'hello, world!'); // send message. ~~~ @@ -3303,7 +3303,7 @@ _**Description**_: Subscribe to channels. Warning: this function will probably c *callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* -~~~ +~~~php function f($redis, $chan, $msg) { switch($chan) { case 'chan-1': @@ -3337,7 +3337,7 @@ _**Description**_: A command allowing you to get information on the Redis pub/su *NUMPAT*: Integer return containing the number active pattern subscriptions ##### *Example* -~~~ +~~~php $redis->pubSub("channels"); /*All channels */ $redis->pubSub("channels", "*pattern*"); /* Just channels matching your pattern */ $redis->pubSub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/ @@ -3390,7 +3390,7 @@ _**Description**_: Enter and exit transactional mode. `multi()` returns the Redis instance and enters multi-mode. Once in multi-mode, all subsequent method calls return the same object until `exec()` is called. ##### *Example* -~~~ +~~~php $ret = $redis->multi() ->set('key1', 'val1') ->get('key1') @@ -3417,7 +3417,7 @@ If the key is modified between `WATCH` and `EXEC`, the MULTI/EXEC transaction wi *keys*: string for one key or array for a list of keys ##### *Example* -~~~ +~~~php $redis->watch('x'); // or for a list of keys: $redis->watch(array('x','another key')); /* long code here during the execution of which other clients could well modify `x` */ $ret = $redis->multi() @@ -3456,7 +3456,7 @@ Arrays that are returned can also contain other arrays, if that's how it was set executing the LUA script, the getLastError() function can tell you the message that came back from Redis (e.g. compile error). ##### *Examples* -~~~ +~~~php $redis->eval("return 1"); // Returns an integer: 1 $redis->eval("return {1,2,3}"); // Returns Array(1,2,3) $redis->del('mylist'); @@ -3483,7 +3483,7 @@ either by running it or via the SCRIPT LOAD command. Mixed. See EVAL ##### *Examples* -~~~ +~~~php $script = 'return 1'; $sha = $redis->script('load', $script); $redis->evalSha($sha); // Returns 1 @@ -3494,7 +3494,7 @@ $redis->evalSha($sha); // Returns 1 _**Description**_: Execute the Redis SCRIPT command to perform various operations on the scripting subsystem. ##### *Usage* -~~~ +~~~php $redis->script('load', $script); $redis->script('flush'); $redis->script('kill'); @@ -3518,7 +3518,7 @@ The Redis CLIENT command can be used in four ways. * CLIENT KILL [ip:port] ##### *Usage* -~~~ +~~~php $redis->client('list'); // Get a list of clients $redis->client('getname'); // Get the name of the current connection $redis->client('setname', 'somename'); // Set the name of the current connection @@ -3546,7 +3546,7 @@ _**Description**_: The last error message (if any) A string with the last returned script based error message, or NULL if there is no error ##### *Examples* -~~~ +~~~php $redis->eval('this-is-not-lua'); $err = $redis->getLastError(); // "ERR Error compiling script (new function): user_script:1: '=' expected near '-'" @@ -3563,7 +3563,7 @@ _**Description**_: Clear the last error message *BOOL* TRUE ##### *Examples* -~~~ +~~~php $redis->set('x', 'a'); $redis->incr('x'); $err = $redis->getLastError(); @@ -3584,7 +3584,7 @@ _**Description**_: A utility method to prefix the value with the prefix setting If a prefix is set up, the value now prefixed. If there is no prefix, the value will be returned unchanged. ##### *Examples* -~~~ +~~~php $redis->setOption(Redis::OPT_PREFIX, 'my-prefix:'); $redis->_prefix('my-value'); // Will return 'my-prefix:my-value' ~~~ @@ -3602,7 +3602,7 @@ will change Array values to 'Array', and Objects to 'Object'. *value*: Mixed. The value to be serialized ##### *Examples* -~~~ +~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); $redis->_serialize("foo"); // returns "foo" $redis->_serialize(Array()); // Returns "Array" @@ -3624,7 +3624,7 @@ serializing values, and you return something from redis in a LUA script that is *value* string. The value to be unserialized ##### *Examples* -~~~ +~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); $redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2,3) ~~~ From 9bf32d304482fa5b9e4ae942c37009b43c6a8904 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Fri, 27 Jul 2018 09:18:35 -0400 Subject: [PATCH 0978/1986] travis: Use default dist; fix redis-trib.rb error --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7fd3fe7398..0953a77d5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ -dist: precise sudo: required language: php php: - - 5.3 - 5.4 - 5.5 - 5.6 @@ -15,6 +13,9 @@ matrix: allow_failures: - php: nightly include: + # php 5.3 is only available on precise + - php: 5.3 + dist: precise - php: 5.4 env: CC=clang - php: 5.5 @@ -40,7 +41,9 @@ before_script: - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap - for PORT in $(seq 6379 6382); do redis-server --port $PORT --daemonize yes; done - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done - - wget https://raw.githubusercontent.com/antirez/redis/unstable/src/redis-trib.rb + - wget https://raw.githubusercontent.com/antirez/redis/1673a3f32ce22498bcb60d73ee254e61e323dda5/src/redis-trib.rb + # Upstream suggests: redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 + # but --cluster is an unknown option for travis trusty - echo yes | ruby redis-trib.rb create --replicas 3 $(seq -f 127.0.0.1:%g 7000 7011) - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini script: From 0c5e7f4df9f09d85d89db683289d64f3daa13434 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 29 Jul 2018 22:33:25 +0300 Subject: [PATCH 0979/1986] Issue #1347 Fix arginfo for set command --- common.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common.h b/common.h index f2062a9e9a..da9f3665b7 100644 --- a/common.h +++ b/common.h @@ -817,8 +817,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, opt) + ZEND_ARG_INFO(0, opts) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_lset, 0, 0, 3) From 7171aceaaa8d29081264b4006a5caf8124164933 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 29 Jul 2018 22:57:03 +0300 Subject: [PATCH 0980/1986] Issue #1367 Use `zval_get_long` instead of `Z_STRVAL_P` + `atof` for parsing timeout and read_timeout params. --- redis_session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index 099f353ac3..2fb49a0974 100644 --- a/redis_session.c +++ b/redis_session.c @@ -469,10 +469,10 @@ PS_OPEN_FUNC(redis) weight = zval_get_long(param); } if ((param = zend_hash_str_find(Z_ARRVAL(params), "timeout", sizeof("timeout") - 1)) != NULL) { - timeout = atof(Z_STRVAL_P(param)); + timeout = zval_get_double(param); } if ((param = zend_hash_str_find(Z_ARRVAL(params), "read_timeout", sizeof("read_timeout") - 1)) != NULL) { - read_timeout = atof(Z_STRVAL_P(param)); + read_timeout = zval_get_double(param); } if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent", sizeof("persistent") - 1)) != NULL) { persistent = (atol(Z_STRVAL_P(param)) == 1 ? 1 : 0); From d943a447bfb72ccf069d7730ad4e93361dc77bb0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 1 Aug 2018 09:34:56 +0300 Subject: [PATCH 0981/1986] Update package.xml --- package.xml | 106 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/package.xml b/package.xml index 3f3c6d8b7b..88bd62562b 100644 --- a/package.xml +++ b/package.xml @@ -27,38 +27,30 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2018-02-07 + 2018-07-10 - 4.0.0RC1 - 4.0.0RC1 + 4.1.0 + 4.1.0 - alpha - alpha + stable + stable PHP - phpredis 4.0.0RC1 - - *** WARNING! THIS RELEASE CONTAINS BREAKING API CHANGES! *** - - * Add proper ARGINFO for all methods. (Pavlo Yatsukhnenko, Michael Grunder) - * Let EXISTS take multiple keys [cccc39] (Michael Grunder) - * Use zend_string as returning value for ra_extract_key and ra_call_extractor [9cd05911] (Pavlo Yatsukhnenko) - * Implement SWAPDB and UNLINK commands [84f1f28b, 9e65c429] (Michael Grunder) - * Return real connection error as exception [5b9c0c60] (Pavlo Yatsukhnenko, Michael Grunder) - * Disallow using empty string as session name. [485db46f] (Pavlo Yatsukhnenko) - * Use zend_string for storing auth and prefix members [4b8336f7] (Pavlo Yatsukhnenko) - * The element of z_seeds may be a reference on php7 [367bc6aa, 1e63717a] (@janic716) - * Avoid connection in helper methods [91e9cfe1] (Pavlo Yatsukhnenko) - * Add tcp_keepalive option to redis sock [68c58513, 5101172a, 010336d5, 51e48729] (@git-hulk, Michael Grunder) - * More robust GEORADIUS COUNT validation [f7edee5d] (Michael Grunder) - * Add LZF compression (experimental) [e2c51251, 8cb2d5bd, 8657557] (Pavlo Yatsukhnenko) - * Allow to use empty string as persistant_id [ec4fd1bd] (Pavlo Yatsukhnenko) - * Don't use convert_to_string in redis_hmget_cmd [99335d6] (Pavlo Yatsukhnenko) - * Allow mixing MULTI and PIPELINE modes (experimental) [5874b0] (Pavlo Yatsukhnenko) - * PHP >=7.3.0 uses zend_string to store `php_url` elements [b566fb44] (@fmk) - * Documentation improvements (Michael Grunder, @TomA-R) + phpredis 4.1.0 + + The primary new feature of this release is session locking functionality. Thanks to @SkydiveMarius! + + * Add callbacks validate_sid and update_timestamp to session handler [aaaf0f23] (@hongboliu) + * Call cluster_disconnect before destroying cluster object. [28ec4322] (Pavlo Yatsukhnenko) + * Bulk strings can be zero length. (Michael Grunder) + * Handle async parameter for flushDb and flushAll [beb6e8f3,acd10409,742cdd05] (Pavlo Yatsukhnenko) + * Split INSTALL and add more instructions [43613d9e,80d2a917] (@remicollet, Pavlo Yatsukhnenko) + * Only the first arg of connect and pconnect is required [063b5c1a] (@mathroc) + * Add session locking functionality [300c7251] (@SkydiveMarius, Michael Grunder, Pavlo Yatsukhnenko) + * Fix compression in RedisCluster [1aed74b4] (Pavlo Yatsukhnenko) + * Refactor geo* commands + documentation improvements (Michael Grunder) @@ -102,6 +94,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + @@ -125,11 +120,59 @@ http://pear.php.net/dtd/package-2.0.xsd"> - alphaalpha - 4.0.0RC14.0.0RC1 - 2018-02-07 + stablestable + 4.1.04.1.0 + 2018-01-10 + + phpredis 4.1.0 + + The primary new feature of this release is session locking functionality. Thanks to @SkydiveMarius! + + * Add callbacks validate_sid and update_timestamp to session handler [aaaf0f23] (@hongboliu) + * Call cluster_disconnect before destroying cluster object. [28ec4322] (Pavlo Yatsukhnenko) + * Bulk strings can be zero length. (Michael Grunder) + * Handle async parameter for flushDb and flushAll [beb6e8f3,acd10409,742cdd05] (Pavlo Yatsukhnenko) + * Split INSTALL and add more instructions [43613d9e,80d2a917] (@remicollet, Pavlo Yatsukhnenko) + * Only the first arg of connect and pconnect is required [063b5c1a] (@mathroc) + * Add session locking functionality [300c7251] (@SkydiveMarius, Michael Grunder, Pavlo Yatsukhnenko) + * Fix compression in RedisCluster [1aed74b4] (Pavlo Yatsukhnenko) + * Refactor geo* commands + documentation improvements (Michael Grunder) + + + + + stablestable + 4.0.24.0.2 + 2018-04-25 + + phpredis 4.0.2 + + This release contains only fix of exists method to take multiple keys + and return integer value (was broken in 4.0.1) Thanks @RanjanRohit! + + + + + stablestable + 4.0.14.0.1 + 2018-04-18 - phpredis 4.0.0RC1 + phpredis 4.0.1 + + * Fix arginfo for connect/pconnect issue #1337 [c3b228] (@mathroc) + * Don't leak a ZVAL [278232] (Michael Grunder) + * Fix config.m4 for lzf issue #1325 [20e173] (Pavlo Yatsukhnenko) + * Updates EXISTS documentation and notes change in 4.0.0 [bed186] (Michael Grunder) + * Fix typo in notes [0bed36] (@szepeviktor) + + + + + stablestable + 4.0.04.0.0 + 2018-03-17 + + phpredis 4.0.0 *** WARNING! THIS RELEASE CONTAINS BREAKING API CHANGES! *** @@ -140,12 +183,15 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Return real connection error as exception [5b9c0c60] (Pavlo Yatsukhnenko, Michael Grunder) * Disallow using empty string as session name. [485db46f] (Pavlo Yatsukhnenko) * Use zend_string for storing auth and prefix members [4b8336f7] (Pavlo Yatsukhnenko) + * The element of z_seeds may be a reference on php7 [367bc6aa, 1e63717a] (@janic716) * Avoid connection in helper methods [91e9cfe1] (Pavlo Yatsukhnenko) * Add tcp_keepalive option to redis sock [68c58513, 5101172a, 010336d5, 51e48729] (@git-hulk, Michael Grunder) * More robust GEORADIUS COUNT validation [f7edee5d] (Michael Grunder) * Add LZF compression (experimental) [e2c51251, 8cb2d5bd, 8657557] (Pavlo Yatsukhnenko) * Allow to use empty string as persistant_id [ec4fd1bd] (Pavlo Yatsukhnenko) * Don't use convert_to_string in redis_hmget_cmd [99335d6] (Pavlo Yatsukhnenko) + * Allow mixing MULTI and PIPELINE modes (experimental) [5874b0] (Pavlo Yatsukhnenko) + * PHP >=7.3.0 uses zend_string to store `php_url` elements [b566fb44] (@fmk) * Documentation improvements (Michael Grunder, @TomA-R) From 842da336d237a7537077554174c5b41b19ed8fa1 Mon Sep 17 00:00:00 2001 From: mg Date: Mon, 6 Aug 2018 16:19:33 +0200 Subject: [PATCH 0982/1986] Ddded subsection of PHP Session handler: "Session locking". --- README.markdown | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.markdown b/README.markdown index 3a441e20e7..56bdd7547e 100644 --- a/README.markdown +++ b/README.markdown @@ -63,6 +63,18 @@ Sessions have a lifetime expressed in seconds and stored in the INI variable "se The session handler requires a version of Redis with the `SETEX` command (at least 2.0). phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0`. +### Session locking +Following INI variables can be used to configure session locking: +~~~ +# Should the locking be enabled? Defaults to: 0. +redis.session.locking_enabled: 1 +# How long should the lock live (in seconds)? Defaults to: value of max_execution_time. +redis.session.lock_expire: 60 +# How long to wait between attempts to acquire lock, in microseconds (µs)?. Defaults to: 2000 +redis.session.lock_wait_time: 50000 +# Maximum number of times to retry (-1 means infinite). Defaults to: 10 +redis.session.lock_retries: 10 +~~~ ## Distributed Redis Array From 1d997873750e5fdef1fe368b5296f4427dab3ea0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 21 Aug 2018 16:50:53 +0300 Subject: [PATCH 0983/1986] Change connect/reconnect logic Persistant connections can be closed via close method. Connection marked as failed only after reconnection attempts. --- cluster_library.c | 10 ++-- cluster_library.h | 2 +- common.h | 7 --- library.c | 131 +++++++++++++++++++++------------------------- library.h | 3 +- redis.c | 8 +-- redis_cluster.c | 2 +- redis_session.c | 2 +- 8 files changed, 72 insertions(+), 93 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0a93c4e6a8..0f4c915ad0 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -835,7 +835,7 @@ PHP_REDIS_API void cluster_free(redisCluster *c, int free_ctx TSRMLS_DC) { /* Disconnect from each node we're connected to */ - cluster_disconnect(c TSRMLS_CC); + cluster_disconnect(c, 0 TSRMLS_CC); /* Free any allocated prefix */ if (c->flags->prefix) zend_string_release(c->flags->prefix); @@ -952,7 +952,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); } } - redis_sock_disconnect(seed TSRMLS_CC); + redis_sock_disconnect(seed, 0 TSRMLS_CC); if (mapped) break; } ZEND_HASH_FOREACH_END(); @@ -1070,12 +1070,12 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type } /* Disconnect from each node we're connected to */ -PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC) { +PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC) { redisClusterNode *node; ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) continue; - redis_sock_disconnect(node->sock TSRMLS_CC); + redis_sock_disconnect(node->sock, force TSRMLS_CC); node->sock->lazy_connect = 1; } ZEND_HASH_FOREACH_END(); } @@ -1302,7 +1302,7 @@ PHP_REDIS_API int cluster_abort_exec(redisCluster *c TSRMLS_DC) { while (fi) { if (SLOT_SOCK(c,fi->slot)->mode == MULTI) { if (cluster_send_discard(c, fi->slot TSRMLS_CC) < 0) { - cluster_disconnect(c TSRMLS_CC); + cluster_disconnect(c, 0 TSRMLS_CC); return -1; } SLOT_SOCK(c,fi->slot)->mode = ATOMIC; diff --git a/cluster_library.h b/cluster_library.h index 2ac2c796e8..49285da7e9 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -354,7 +354,7 @@ long long mstime(void); PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC); -PHP_REDIS_API void cluster_disconnect(redisCluster *c TSRMLS_DC); +PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC); PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC); PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); diff --git a/common.h b/common.h index da9f3665b7..c52dd6d833 100644 --- a/common.h +++ b/common.h @@ -614,13 +614,6 @@ typedef enum _PUBSUB_TYPE { REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \ } -#define REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock) \ - redis_stream_close(redis_sock TSRMLS_CC); \ - redis_sock->stream = NULL; \ - redis_sock->mode = ATOMIC; \ - redis_sock->status = REDIS_SOCK_STATUS_FAILED; \ - redis_sock->watching = 0 - /* Extended SET argument detection */ #define IS_EX_ARG(a) \ ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') diff --git a/library.c b/library.c index d6314dff52..6fc88dc89a 100644 --- a/library.c +++ b/library.c @@ -133,20 +133,16 @@ redis_error_throw(RedisSock *redis_sock TSRMLS_DC) } } -PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC) { - if (!redis_sock->persistent) { - php_stream_close(redis_sock->stream); - } else { - php_stream_pclose(redis_sock->stream); - } -} - PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) { int count; + char *errmsg; - if (!redis_sock->stream) { + if (!redis_sock || !redis_sock->stream || redis_sock->status == REDIS_SOCK_STATUS_FAILED) { + if (!no_throw) { + zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); + } return -1; } @@ -169,51 +165,47 @@ redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) /* Success */ return 0; } else if (redis_sock->mode == MULTI || redis_sock->watching) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - if (!no_throw) { - zend_throw_exception(redis_exception_ce, - "Connection lost and socket is in MULTI/watching mode", - 0 TSRMLS_CC); - } - return -1; - } - /* TODO: configurable max retry count */ - for (count = 0; count < 10; ++count) { - /* close existing stream before reconnecting */ - if (redis_sock->stream) { - redis_stream_close(redis_sock TSRMLS_CC); - redis_sock->stream = NULL; - } - // Wait for a while before trying to reconnect - if (redis_sock->retry_interval) { - // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time - long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); - usleep(retry_interval); - } - /* reconnect */ - if (redis_sock_connect(redis_sock TSRMLS_CC) == 0) { - /* check for EOF again. */ - errno = 0; - if (php_stream_eof(redis_sock->stream) == 0) { - /* If we're using a password, attempt a reauthorization */ - if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) { - break; - } - /* If we're using a non-zero db, reselect it */ - if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) { - break; + errmsg = "Connection lost and socket is in MULTI/watching mode"; + } else { + errmsg = "Connection lost"; + /* TODO: configurable max retry count */ + for (count = 0; count < 10; ++count) { + /* close existing stream before reconnecting */ + if (redis_sock->stream) { + redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); + } + // Wait for a while before trying to reconnect + if (redis_sock->retry_interval) { + // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time + long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); + usleep(retry_interval); + } + /* reconnect */ + if (redis_sock_connect(redis_sock TSRMLS_CC) == 0) { + /* check for EOF again. */ + errno = 0; + if (php_stream_eof(redis_sock->stream) == 0) { + /* If we're using a password, attempt a reauthorization */ + if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) { + errmsg = "AUTH failed while reconnecting"; + break; + } + /* If we're using a non-zero db, reselect it */ + if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) { + errmsg = "SELECT failed while reconnecting"; + break; + } + /* Success */ + return 0; } - /* Success */ - return 0; } } } - /* close stream if still here */ - if (redis_sock->stream) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - } + /* close stream and mark socket as failed */ + redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); + redis_sock->status = REDIS_SOCK_STATUS_FAILED; if (!no_throw) { - zend_throw_exception(redis_exception_ce, "Connection lost", 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, errmsg, 0 TSRMLS_CC); } return -1; } @@ -1427,7 +1419,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) #endif if (redis_sock->stream != NULL) { - redis_sock_disconnect(redis_sock TSRMLS_CC); + redis_sock_disconnect(redis_sock, 0 TSRMLS_CC); } tv.tv_sec = (time_t)redis_sock->timeout; @@ -1532,28 +1524,26 @@ redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC) /** * redis_sock_disconnect */ -PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC) +PHP_REDIS_API int +redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) { if (redis_sock == NULL) { - return 1; - } - - redis_sock->dbNumber = 0; - if (redis_sock->stream != NULL) { - redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; - redis_sock->watching = 0; - - /* Stil valid? */ - if (!redis_sock->persistent) { + return FAILURE; + } else if (redis_sock->stream) { + if (redis_sock->persistent) { + if (force) { + php_stream_pclose(redis_sock->stream); + } + } else { php_stream_close(redis_sock->stream); } - redis_sock->stream = NULL; - - return 1; } + redis_sock->mode = ATOMIC; + redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; + redis_sock->watching = 0; - return 0; + return SUCCESS; } /** @@ -1764,11 +1754,8 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) { - if (!redis_sock || redis_sock->status == REDIS_SOCK_STATUS_DISCONNECTED) { - zend_throw_exception(redis_exception_ce, "Connection closed", - 0 TSRMLS_CC); - } else if (redis_check_eof(redis_sock, 0 TSRMLS_CC) == 0 && - php_stream_write(redis_sock->stream, cmd, sz) == sz + if (redis_check_eof(redis_sock, 0 TSRMLS_CC) == 0 && + php_stream_write(redis_sock->stream, cmd, sz) == sz ) { return sz; } @@ -2044,8 +2031,8 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { - // Close, put our socket state into error - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); + // Close our socket + redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); // Throw a read error exception zend_throw_exception(redis_exception_ce, "read error on connection", diff --git a/library.h b/library.h index e19848b702..2c3e5da734 100644 --- a/library.h +++ b/library.h @@ -41,7 +41,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); +PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); @@ -62,7 +62,6 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); -PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC); PHP_REDIS_API RedisSock *redis_sock_get(zval *id TSRMLS_DC, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); diff --git a/redis.c b/redis.c index f40dc0d17a..51933c1bee 100644 --- a/redis.c +++ b/redis.c @@ -536,7 +536,7 @@ free_redis_object(void *object TSRMLS_DC) zend_object_std_dtor(&redis->std TSRMLS_CC); if (redis->sock) { - redis_sock_disconnect(redis->sock TSRMLS_CC); + redis_sock_disconnect(redis->sock, 0 TSRMLS_CC); redis_free_socket(redis->sock); } efree(redis); @@ -577,7 +577,7 @@ free_redis_object(zend_object *object) zend_object_std_dtor(&redis->std TSRMLS_CC); if (redis->sock) { - redis_sock_disconnect(redis->sock TSRMLS_CC); + redis_sock_disconnect(redis->sock, 0 TSRMLS_CC); redis_free_socket(redis->sock); } } @@ -946,7 +946,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) redis = PHPREDIS_GET_OBJECT(redis_object, object); /* if there is a redis sock already we have to remove it */ if (redis->sock) { - redis_sock_disconnect(redis->sock TSRMLS_CC); + redis_sock_disconnect(redis->sock, 0 TSRMLS_CC); redis_free_socket(redis->sock); } @@ -993,7 +993,7 @@ PHP_METHOD(Redis, close) { RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (redis_sock && redis_sock_disconnect(redis_sock TSRMLS_CC)) { + if (redis_sock && redis_sock_disconnect(redis_sock, 1 TSRMLS_CC)) { RETURN_TRUE; } RETURN_FALSE; diff --git a/redis_cluster.c b/redis_cluster.c index d2788a7d97..4271b9d993 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -542,7 +542,7 @@ PHP_METHOD(RedisCluster, __construct) { /* {{{ proto bool RedisCluster::close() */ PHP_METHOD(RedisCluster, close) { - cluster_disconnect(GET_CONTEXT() TSRMLS_CC); + cluster_disconnect(GET_CONTEXT(), 1 TSRMLS_CC); RETURN_TRUE; } diff --git a/redis_session.c b/redis_session.c index 2fb49a0974..08022cb1c9 100644 --- a/redis_session.c +++ b/redis_session.c @@ -126,7 +126,7 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { rpm = pool->head; while (rpm) { next = rpm->next; - redis_sock_disconnect(rpm->redis_sock TSRMLS_CC); + redis_sock_disconnect(rpm->redis_sock, 0 TSRMLS_CC); redis_free_socket(rpm->redis_sock); if (rpm->prefix) zend_string_release(rpm->prefix); if (rpm->auth) zend_string_release(rpm->auth); From eaf7e9771e5d8b8258b62ff89e6f3735a3112b36 Mon Sep 17 00:00:00 2001 From: Miroslav Koula Date: Wed, 29 Aug 2018 18:16:04 +0200 Subject: [PATCH 0984/1986] There are two big package managers on OS X Except for Homebrew (Linux like a clone), there's also package manager MacPorts (FreeBSD like a clone) which is having the package for php-redis --- INSTALL.markdown | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/INSTALL.markdown b/INSTALL.markdown index 1a56933120..8680b1970e 100644 --- a/INSTALL.markdown +++ b/INSTALL.markdown @@ -85,6 +85,11 @@ You can install it using Homebrew: - [Get homebrew-php](https://github.com/Homebrew/homebrew-php) - `brew install php55-redis` (or php53-redis, php54-redis) +You can install it using MacPorts: + +- [Get macports-php](https://www.macports.org/) +- `sudo port install php56-redis` (or php53-redis, php54-redis, php55-redis, php70-redis, php71-redis, php72-redis) + # Building on Windows See [instructions from @char101](https://github.com/phpredis/phpredis/issues/213#issuecomment-11361242) on how to build phpredis on Windows. From 908ac4b35e2e9d4454c0d53b3d9c9a74e91cf43c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 5 Sep 2018 09:53:27 +0300 Subject: [PATCH 0985/1986] Issue #1388 Display ini entries in output of phpinfo() --- redis.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/redis.c b/redis.c index 51933c1bee..5c7eaabdba 100644 --- a/redis.c +++ b/redis.c @@ -832,6 +832,8 @@ PHP_MINFO_FUNCTION(redis) php_info_print_table_row(2, "Available compression", "lzf"); #endif php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); } /* {{{ proto Redis Redis::__construct() From e206ce9c1209d0a0a1544d848486c806399ae243 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 5 Sep 2018 10:30:09 +0300 Subject: [PATCH 0986/1986] Issue #1388 Set default values for ini entries --- redis.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/redis.c b/redis.c index 5c7eaabdba..b62591b452 100644 --- a/redis.c +++ b/redis.c @@ -55,30 +55,30 @@ extern zend_function_entry redis_cluster_functions[]; PHP_INI_BEGIN() /* redis arrays */ - PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.connecttimeout", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.autorehash", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.connecttimeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.index", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.lazyconnect", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.index", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.lazyconnect", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.pconnect", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.pconnect", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.readtimeout", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.arrays.retryinterval", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.readtimeout", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.retryinterval", "0", PHP_INI_ALL, NULL) /* redis cluster */ - PHP_INI_ENTRY("redis.clusters.persistent", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.clusters.read_timeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.clusters.timeout", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL) /* redis session */ - PHP_INI_ENTRY("redis.session.locking_enabled", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.session.lock_expire", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.session.lock_retries", "", PHP_INI_ALL, NULL) - PHP_INI_ENTRY("redis.session.lock_wait_time", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_retries", "10", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_wait_time", "2000", PHP_INI_ALL, NULL) PHP_INI_END() /** {{{ Argument info for commands in redis 1.0 */ From 58bd8cc8f5b044a77881d9a8b31caf536698c93d Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 5 Sep 2018 14:13:23 +0200 Subject: [PATCH 0987/1986] session is required --- redis.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis.c b/redis.c index b62591b452..f44004fcb1 100644 --- a/redis.c +++ b/redis.c @@ -458,6 +458,9 @@ static zend_function_entry redis_functions[] = { static const zend_module_dep redis_deps[] = { #ifdef HAVE_REDIS_IGBINARY ZEND_MOD_REQUIRED("igbinary") +#endif +#ifdef PHP_SESSION + ZEND_MOD_REQUIRED("session") #endif ZEND_MOD_END }; From dcde9331670ee65f29e30665fa1b233e473cfade Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 5 Sep 2018 23:24:13 +0300 Subject: [PATCH 0988/1986] Issue #1399 Fix printf format warnings --- cluster_library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0f4c915ad0..8f15787375 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -703,7 +703,7 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { port = (unsigned short)r3->element[1]->integer; // If the node is new, create and add to nodes. Otherwise use it. - klen = snprintf(key,sizeof(key),"%s:%ld",host,port); + klen = snprintf(key, sizeof(key), "%s:%d", host, port); if ((pnode = zend_hash_str_find_ptr(c->nodes, key, klen)) == NULL) { master = cluster_node_create(c, host, hlen, port, low, 0); zend_hash_str_update_ptr(c->nodes, key, klen, master); @@ -1266,7 +1266,7 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { c->redir_port, c->redir_slot, 0); /* Our node is new, so keep track of it for cleanup */ - klen = snprintf(key,sizeof(key),"%s:%ld",c->redir_host,c->redir_port); + klen = snprintf(key, sizeof(key), "%s:%d", c->redir_host, c->redir_port); zend_hash_str_update_ptr(c->nodes, key, klen, node); /* Now point our slot at the node */ From d4a086979fa38b087934918c5efa187d9ef31345 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 18 Sep 2018 17:00:21 -0700 Subject: [PATCH 0989/1986] Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex --- redis_commands.c | 12 +++++++---- tests/RedisTest.php | 51 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 739142e986..c47669adb4 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -901,6 +901,12 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Validate ZLEX* min/max argument strings */ +static int validate_zlex_arg(const char *arg, strlen_t len) { + return (len > 1 && (*arg == '[' || *arg == '(')) || + (len == 1 && (*arg == '+' || *arg == '-')); +} + /* ZLEXCOUNT/ZREMRANGEBYLEX */ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, @@ -917,11 +923,9 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Quick sanity check on min/max */ - if (min_len<1 || max_len<1 || (min[0]!='(' && min[0]!='[') || - (max[0]!='(' && max[0]!='[')) - { + if (!validate_zlex_arg(min, min_len) || !validate_zlex_arg(max, max_len)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Min and Max arguments must begin with '(' or '['"); + "Min/Max args can be '-' or '+', or start with '[' or '('"); return FAILURE; } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 0e6cbdfea4..e021805c2c 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2333,11 +2333,60 @@ public function testZRangeByLex() { $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c'), Array('a', 'b', 'c')); $this->assertEquals($this->redis->zRangeByLex('key', '(e', '+'), Array('f', 'g')); - // with limit offset + // with limit offset $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c', 1, 2), Array('b', 'c') ); $this->assertEquals($this->redis->zRangeByLex('key', '-', '(c', 1, 2), Array('b')); } + public function testZLexCount() { + if (version_compare($this->version, "2.8.9", "lt")) { + $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", "lt")) { + $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 testHashes() { $this->redis->del('h', 'key'); $this->assertTrue(0 === $this->redis->hLen('h')); From 2f694e1089f94078c815a8ec3ce2f86acce843b4 Mon Sep 17 00:00:00 2001 From: Lucas Courot Date: Wed, 19 Sep 2018 15:09:23 +0200 Subject: [PATCH 0990/1986] Fix typo --- cluster.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster.markdown b/cluster.markdown index 52591d8f70..4dcbc2ba19 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -75,7 +75,7 @@ $obj_cluster->setOption( ## Main command loop With the exception of commands that are directed to a specific node, each command executed via RedisCluster is processed through a command loop, where we make the request, handle any MOVED or ASK redirection, and repeat if necessary. This continues until one of the following conditions is met: -1. We fail to communicate with *any* node that we are aware of, in which case a ```RedisClusterExecption``` is raised. +1. We fail to communicate with *any* node that we are aware of, in which case a ```RedisClusterException``` is raised. 2. We have been bounced around longer than the timeout which was set on construction. 3. Redis cluster returns us a ```CLUSTERDOWN``` error, in which case a ```RedisClusterException``` is raised. 4. We receive a valid response, in which case the data is returned to the caller. From bfd274712eeb372926d1106b3da3c4fc19c0a48a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 20 Sep 2018 22:23:40 -0700 Subject: [PATCH 0991/1986] Modify session testing logic In the event that the test PHP executable has been built with redis and/or igbinary support built statically, we don't need to try and find it by adding --no-php-ini and the extension directives themselves. This fixes the test execution when php has the required extensions already. --- tests/RedisTest.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index e021805c2c..b5da01e831 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5663,9 +5663,27 @@ private function getPhpCommand($script) if (!$cmd) { $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: (defined('PHP_BINARY') ? PHP_BINARY : 'php')); // PHP_BINARY is 5.4+ - $cmd .= ' '; - $cmd .= (getenv('TEST_PHP_ARGS') ?: '--no-php-ini --define extension=igbinary.so --define extension=' . dirname(__DIR__) . '/modules/redis.so'); + + if ($test_args = getenv('TEST_PHP_ARGS')) { + $cmd .= $test_args; + } else { + /* Only append specific extension directives if PHP hasn't been compiled with what we need statically */ + $result = shell_exec("$cmd --no-php-ini -m"); + $redis = strpos($result, 'redis') !== false; + $igbinary = strpos($result, 'igbinary') !== false; + + if (!$redis || !$igbinary) { + $cmd .= ' --no-php-ini'; + if (!$igbinary) { + $cmd .= ' --define extension=igbinary.so'; + } + if (!$redis) { + $cmd .= ' --define extension=' . dirname(__DIR__) . '/modules/redis.so'; + } + } + } } + return $cmd . ' ' . __DIR__ . '/' . $script . ' '; } } From 2c9e0572361d5131f24fbc81a8f7baaafb671994 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Sat, 29 Sep 2018 11:59:01 -0700 Subject: [PATCH 0992/1986] Streams (#1413) Streams API --- .gitignore | 3 +- README.markdown | 322 ++++++++++++++++++++++ cluster_library.c | 165 ++++++++++-- cluster_library.h | 67 +++-- common.h | 84 ++++++ library.c | 402 ++++++++++++++++++++++++--- library.h | 31 ++- php_redis.h | 15 ++ redis.c | 73 ++++- redis_array.c | 4 - redis_cluster.c | 76 +++++- redis_cluster.h | 15 ++ redis_commands.c | 643 +++++++++++++++++++++++++++++++++++++++++++- redis_commands.h | 35 ++- redis_session.c | 14 +- tests/RedisTest.php | 444 ++++++++++++++++++++++++++++++ tests/TestSuite.php | 18 +- 17 files changed, 2296 insertions(+), 115 deletions(-) diff --git a/.gitignore b/.gitignore index 0462ff3707..f672fcee2a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ missing autom4te.cache mkinstalldirs run-tests.php -idea/* \ No newline at end of file +idea/* +.cquery diff --git a/README.markdown b/README.markdown index 56bdd7547e..1bce4964c9 100644 --- a/README.markdown +++ b/README.markdown @@ -3268,6 +3268,328 @@ array(1) { } ~~~ +## Streams + +* [xAck](#xack) - Acknowledge one or more pending messages +* [xAdd](#xadd) - Add a message to a stream +* [xClaim](#xclaim) - Acquire ownership of a pending message +* [xDel](#xdel) - Remove a message from a stream +* [xGroup](#xgroup) - Manage consumer groups +* [xInfo](#xinfo) - Get information about a stream +* [xLen](#xlen) - Get the length of a stream +* [xPending](#xpending) - Inspect pending messages in a stream +* [xRange](#xrange) - Query a range of messages from a stream +* [xRead](#xread) - Read message(s) from a stream +* [xReadGroup](#xreadgroup) - Read stream messages with a group and consumer +* [xRevRange](#xrevrange) - Query one or more messages from end to start +* [xTrim](#xtrim) - Trim a stream's size + +### xAck +----- + +##### *Prototype* +~~~php +$obj_redis->xAck($stream, $group, $arr_messages); +~~~ + +_**Description**_: Acknowledge one or more messages on behalf of a consumer group. + +##### *Return value* +*long*: The number of messages Redis reports as acknowledged. + +##### *Example* +~~~php +$obj_redis->xAck('stream', 'group1', ['1530063064286-0', '1530063064286-1']); +~~~ + +### xAdd +----- + +##### *Prototype* +~~~php +$obj_redis->xAdd($str_key, $str_id, $arr_message); +~~~ + +_**Description**_: Add a message to a stream + +##### *Return value* +*String*: The added message ID + +##### *Example* +~~~php +$obj_redis->xAdd('mystream', "\*", ['field' => 'value']); +~~~ + +### xClaim +----- + +##### *Prototype* +~~~php +$obj_redis->($str_key, $str_group, $str_consumer, $min_idle_time, [$arr_options]); +~~~ + +_**Description**_: Claim ownership of one or more pending messages. + +#### *Options Array* +~~~php +$options = [ + /* Note: 'TIME', and 'IDLE' are mutually exclusive */ + 'IDLE' => $value, /* Set the idle time to $value ms */, + 'TIME' => $value, /* Set the idle time to now - $value */ + 'RETRYCOUNT' => $value, /* Update message retrycount to $value */ + 'FORCE', /* Claim the message(s) even if they're not pending anywhere */ + 'JUSTID', /* Instruct Redis to only return IDs */ +]; +~~~ + +##### *Return value* +*Array*: Either an array of message IDs along with corresponding data, or just an array of IDs (if the 'JUSTID' option was passed). + +##### *Example* +~~~php +$ids = ['1530113681011-0', '1530113681011-1', '1530113681011-2']; + +/* Without any options */ +$obj_redis->xClaim( + 'mystream', 'group1', 'myconsumer1', $ids +); + +/* With options */ +$obj_redis->xClaim( + 'mystream', 'group1', 'myconsumer2', $ids, + [ + 'IDLE' => time() * 1000, + 'RETRYCOUNT' => 5, + 'FORCE', + 'JUSTID' + ] +); +~~~ + +### xDel +----- + +##### *Prototype* +~~~php +$obj_redis->xDel($str_key, $arr_ids); +~~~ + +_**Description**_: Delete one or more messages from a stream. + +##### *Return value* +*long*: The number of messages removed + +##### *Example* +~~~php +$obj_redis->xDel('mystream', ['1530115304877-0', '1530115305731-0']); +~~~ + +### xGroup +----- + +##### *Prototype* +~~~php +$obj_redis->xGroup('HELP'); +$obj_redis->xGroup('SETID', $str_key, $str_group, $str_msg_id); +$obj_redis->xGroup('DELGROUP', $str_key, $str_group); +$obj_redis->xGroup('CREATE', $str_key, $str_group, $str_msg_id); +$obj_redis->xGroup('DELCONSUMER', $str_key, $str_group, $str_consumer_name); +~~~ + +_**Description**_: This command is used in order to create, destroy, or manage consumer groups. + +##### *Return value* +*Mixed*: This command returns different types depending on the specific XGROUP command executed. + +##### *Example* +~~~php +$obj_redis->xGroup('CREATE', 'mystream', 'mygroup'); +$obj_redis->xGroup('DELGROUP', 'mystream', 'mygroup'); +~~~ + +### xInfo +----- + +##### *Prototype* +~~~php +$obj_redis->xInfo('CONSUMERS', $str_stream, $str_group); +$obj_redis->xInfo('GROUPS', $str_stream); +$obj_redis->xInfo('STREAM', $str_stream); +$obj_redis->xInfo('HELP'); +~~~ + +_**Description**_: Get information about a stream or consumer groups. + +##### *Return value* +*Mixed*: This command returns different types depending on which subcommand is used. + +##### *Example* +~~~php +$obj_redis->xInfo('STREAM', 'mystream'); +~~~ + +### xLen +----- + +##### *Prototype* +~~~php +$obj_redis->xLen($str_stream); +~~~ + +_**Description**_: Get the length of a given stream + +##### *Return value* +*Long*: The number of messages in the stream. + +##### *Example* +~~~php +$obj_redis->xLen('mystream'); +~~~ + +### xPending +----- + +##### *Prototype* +~~~php +$obj_redis->xPending($str_stream, $str_group [, $i_start, $i_end, $i_count, $str_consumer]); +~~~ + +_**Description**_: Get information about pending messages in a given stream. + +##### *Return value* +*Array*: Information about the pending messages, in various forms depending on the specific invocation of XPENDING. + +##### *Examples* +~~~php +$obj_redis->xPending('mystream', 'mygroup'); +$obj_redis->xPending('mystream', 'mygroup', 0, '+', 1, 'consumer-1'); +~~~ + +### xRange +----- + +##### *Prototype* +~~~php +$obj_redis->xRange($str_stream, $i_start, $i_end [, $i_count]); +~~~ + +_**Description**_: Get a range of messages from a given stream. + +##### *Return value* +*Array*: The messages in the stream within the requested range. + +##### *Example* +~~~php +/* Get everything in this stream */ +$obj_redis->xRange('mystream', '-', '+'); + +/* Only the first two messages */ +$obj_redis->xRange('mystream', '-', '+', 2); +~~~ + +### xRead +----- + +##### *Prototype* +~~~php +$obj_redis->xRead($arr_streams [, $i_count, $i_block); +~~~ + +_**Description**_: Read data from one or more streams and only return IDs greater than sent in the command. + +##### *Return value* +*Array*: The messages in the stream newer than the IDs passed to Redis (if any). + +##### *Example* +~~~php +$obj_redis->xRead(['stream1' => '1535222584555-0', 'stream2' => '1535222584555-0']); + +/* --- Possible output --- +Array +( + [stream1] => Array + ( + [1535222584555-1] => Array + ( + [key:1] => val:1 + ) + + ) + + [stream2] => Array + ( + [1535222584555-1] => Array + ( + [key:1] => val:1 + ) + + ) + +) +*/ +~~~ + +### xReadGroup +----- + +##### *Prototype* +~~~php +$obj_redis->xReadGroup($str_group, $str_consumer, $arr_streams [, $i_count, $i_block]); +~~~ + +_**Description**_: This method is similar to xRead except that it supports reading messages for a specific consumer group. + +##### *Return value* +*Array*: The messages delivered to this consumer group (if any). + +##### *Examples* +~~~php +/* Consume messages for 'mygroup', 'consumer1' */ +$obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => 0, 's2' => 0]); + +/* Read a single message as 'consumer2' for up to a second until a message arrives. */ +$obj_redis->xReadGroup('mygroup', 'consumer2', ['s1' => 0, 's2' => 0], 1, 1000); +~~~ + +### xRevRange +----- + +##### *Prototype* +~~~php +$obj_redis->xRevRange($str_stream, $i_end, $i_start [, $i_count]); +~~~ + +_**Description**_: This is identical to xRange except the results come back in reverse order. Also note that Redis reverses the order of "start" and "end". + +##### *Return value* +*Array*: The messages in the range specified. + +##### *Example* +~~~php +$obj_redis->xRevRange('mystream', '+', '-'); +~~~ + +### xTrim +----- + +##### *Prototype* +~~~php +$obj_redis->xTrim($str_stream, $i_max_len [, $boo_approximate]); +~~~ + +_**Description**_: Trim the stream length to a given maximum. If the "approximate" flag is pasesed, Redis will use your size as a hint but only trim trees in whole nodes (this is more efficient). + +##### *Return value* +*long*: The number of messages trimed from the stream. + +##### *Example* +~~~php +/* Trim to exactly 100 messages */ +$obj_redis->xTrim('mystream', 100); + +/* Let Redis approximate the trimming */ +$obj_redis->xTrim('mystream', 100, true); +~~~ ## Pub/sub diff --git a/cluster_library.c b/cluster_library.c index 8f15787375..5f710c5e92 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -103,7 +103,7 @@ void cluster_free_reply(clusterReply *reply, int free_data) { for (i = 0; i < reply->elements && reply->element[i]; i++) { cluster_free_reply(reply->element[i], free_data); } - efree(reply->element); + if (reply->element) efree(reply->element); break; default: break; @@ -113,7 +113,8 @@ void cluster_free_reply(clusterReply *reply, int free_data) { static void cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, - clusterReply **element, int *err TSRMLS_DC) + clusterReply **element, int status_strings, + int *err TSRMLS_DC) { int i; size_t sz; @@ -141,6 +142,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, return; } r->len = (long long)sz; + if (status_strings) r->str = estrndup(buf, r->len); break; case TYPE_INT: r->integer = len; @@ -155,11 +157,15 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, } break; case TYPE_MULTIBULK: - r->element = ecalloc(r->len,sizeof(clusterReply*)); - r->elements = r->len; - cluster_multibulk_resp_recursive(sock, r->elements, r->element, - err TSRMLS_CC); - if (*err) return; + if (r->len >= 0) { + r->elements = r->len; + if (r->len > 0) { + r->element = ecalloc(r->len,sizeof(clusterReply*)); + cluster_multibulk_resp_recursive(sock, r->elements, r->element, + status_strings, err TSRMLS_CC); + } + if (*err) return; + } break; default: *err = 1; @@ -191,15 +197,17 @@ static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, } /* Read the response from a cluster */ -clusterReply *cluster_read_resp(redisCluster *c TSRMLS_DC) { - return cluster_read_sock_resp(c->cmd_sock,c->reply_type,c->reply_len TSRMLS_CC); +clusterReply *cluster_read_resp(redisCluster *c, int status_strings TSRMLS_DC) { + return cluster_read_sock_resp(c->cmd_sock, c->reply_type, + status_strings ? c->line_reply : NULL, + c->reply_len TSRMLS_CC); } /* Read any sort of response from the socket, having already issued the * command and consumed the reply type and meta info (length) */ clusterReply* cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, - size_t len TSRMLS_DC) + char *line_reply, size_t len TSRMLS_DC) { clusterReply *r; @@ -214,6 +222,10 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, r->integer = len; break; case TYPE_LINE: + if (line_reply) { + r->str = estrndup(line_reply, len); + r->len = len; + } case TYPE_ERR: return r; case TYPE_BULK: @@ -229,7 +241,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, if (len != (size_t)-1) { r->element = ecalloc(len, sizeof(clusterReply*)*len); cluster_multibulk_resp_recursive(redis_sock, len, r->element, - &err TSRMLS_CC); + line_reply != NULL, &err TSRMLS_CC); } break; default: @@ -618,7 +630,7 @@ clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) } // Consume the rest of our response - if ((r = cluster_read_sock_resp(redis_sock, type, len TSRMLS_CC)) == NULL || + if ((r = cluster_read_sock_resp(redis_sock, type, NULL, len TSRMLS_CC)) == NULL || r->type != TYPE_MULTIBULK || r->elements < 1) { if (r) cluster_free_reply(r, 1); @@ -1486,6 +1498,24 @@ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, efree(resp); } +PHP_REDIS_API void +cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +{ + char *p; + + /* Cluster already has the reply so abort if this isn't a LINE response *or* if for + * some freaky reason we don't detect a null terminator */ + if (c->reply_type != TYPE_LINE || !(p = memchr(c->line_reply,'\0',sizeof(c->line_reply)))) { + CLUSTER_RETURN_FALSE(c); + } + + if (CLUSTER_IS_ATOMIC(c)) { + CLUSTER_RETURN_STRING(c, c->line_reply, p - c->line_reply); + } else { + add_next_index_stringl(&c->multi_resp, c->line_reply, p - c->line_reply); + } +} + /* BULK response handler */ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) @@ -1799,12 +1829,15 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) add_next_index_long(z_ret, r->integer); break; case TYPE_LINE: - add_next_index_bool(z_ret, 1); + if (r->str) { + add_next_index_stringl(z_ret, r->str, r->len); + } else { + add_next_index_bool(z_ret, 1); + } break; case TYPE_BULK: if (r->len > -1) { add_next_index_stringl(z_ret, r->str, r->len); - efree(r->str); } else { add_next_index_null(z_ret); } @@ -1827,15 +1860,16 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) /* Variant response handling, for things like EVAL and various other responses * where we just map the replies from Redis type values to PHP ones directly. */ -PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +static void +cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + int status_strings, void *ctx) { clusterReply *r; zval zv, *z_arr = &zv; int i; // Make sure we can read it - if ((r = cluster_read_resp(c TSRMLS_CC)) == NULL) { + if ((r = cluster_read_resp(c, status_strings TSRMLS_CC)) == NULL) { CLUSTER_RETURN_FALSE(c); } @@ -1849,7 +1883,11 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust RETVAL_FALSE; break; case TYPE_LINE: - RETVAL_TRUE; + if (status_strings) { + RETVAL_STRINGL(r->str, r->len); + } else { + RETVAL_TRUE; + } break; case TYPE_BULK: if (r->len < 0) { @@ -1879,14 +1917,17 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust add_next_index_bool(&c->multi_resp, 0); break; case TYPE_LINE: - add_next_index_bool(&c->multi_resp, 1); + if (status_strings) { + add_next_index_stringl(&c->multi_resp, r->str, r->len); + } else { + add_next_index_bool(&c->multi_resp, 1); + } break; case TYPE_BULK: if (r->len < 0) { add_next_index_null(&c->multi_resp); } else { add_next_index_stringl(&c->multi_resp, r->str, r->len); - efree(r->str); } break; case TYPE_MULTIBULK: @@ -1899,7 +1940,19 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust } // Free our response structs, but not allocated data itself - cluster_free_reply(r, 0); + cluster_free_reply(r, 1); +} + +PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 0, ctx); +} + +PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, ctx); } /* Generic MULTI BULK response processor */ @@ -2052,6 +2105,76 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC } } +/* XRANGE */ +PHP_REDIS_API void +cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + zval zv, *z_messages = &zv; + + REDIS_MAKE_STD_ZVAL(z_messages); + array_init(z_messages); + + c->cmd_sock->serializer = c->flags->serializer; + c->cmd_sock->compression = c->flags->compression; + + if (redis_read_stream_messages(c->cmd_sock, c->reply_len, z_messages TSRMLS_CC) < 0) { + zval_dtor(z_messages); + REDIS_FREE_ZVAL(z_messages); + CLUSTER_RETURN_FALSE(c); + } + + if (CLUSTER_IS_ATOMIC(c)) { + RETVAL_ZVAL(z_messages, 0, 1); + } else { + add_next_index_zval(&c->multi_resp, z_messages); + } +} + +/* XREAD */ +PHP_REDIS_API void +cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + zval zv, *z_streams = &zv; + + REDIS_MAKE_STD_ZVAL(z_streams); + array_init(z_streams); + + c->cmd_sock->serializer = c->flags->serializer; + c->cmd_sock->compression = c->flags->compression; + + if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, z_streams TSRMLS_CC) < 0) { + zval_dtor(z_streams); + REDIS_FREE_ZVAL(z_streams); + CLUSTER_RETURN_FALSE(c); + } + + if (CLUSTER_IS_ATOMIC(c)) { + RETVAL_ZVAL(z_streams, 0, 1); + } else { + add_next_index_zval(&c->multi_resp, z_streams); + } +} + +/* XCLAIM */ +PHP_REDIS_API void +cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + zval zv, *z_msg = &zv; + + REDIS_MAKE_STD_ZVAL(z_msg); + array_init(z_msg); + + if (redis_read_xclaim_response(c->cmd_sock, c->reply_len, z_msg TSRMLS_CC) < 0) { + zval_dtor(z_msg); + REDIS_FREE_ZVAL(z_msg); + CLUSTER_RETURN_FALSE(c); + } + + if (CLUSTER_IS_ATOMIC(c)) { + RETVAL_ZVAL(z_msg, 0, 1); + } else { + add_next_index_zval(&c->multi_resp, z_msg); + } + +} + /* MULTI BULK response loop where we might pull the next one */ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb, zval *z_ret) diff --git a/cluster_library.h b/cluster_library.h index 49285da7e9..0145783257 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -230,7 +230,7 @@ typedef struct redisCluster { /* One RedisSock struct for serialization and prefix information */ RedisSock *flags; - /* The first line of our last reply, not including our reply type byte + /* The first line of our last reply, not including our reply type byte * or the trailing \r\n */ char line_reply[1024]; @@ -283,7 +283,7 @@ typedef struct clusterDistList { size_t len, size; } clusterDistList; -/* Context for things like MGET/MSET/MSETNX. When executing in MULTI mode, +/* Context for things like MGET/MSET/MSETNX. When executing in MULTI mode, * we'll want to re-integrate into one running array, except for the last * command execution, in which we'll want to return the value (or add it) */ typedef struct clusterMultiCtx { @@ -325,17 +325,17 @@ typedef struct clusterReply { } clusterReply; /* Direct variant response handler */ -clusterReply *cluster_read_resp(redisCluster *c TSRMLS_DC); -clusterReply *cluster_read_sock_resp(RedisSock *redis_sock, - REDIS_REPLY_TYPE type, size_t reply_len TSRMLS_DC); +clusterReply *cluster_read_resp(redisCluster *c, int status_strings TSRMLS_DC); +clusterReply *cluster_read_sock_resp(RedisSock *redis_sock, + REDIS_REPLY_TYPE type, char *line_reply, size_t reply_len TSRMLS_DC); void cluster_free_reply(clusterReply *reply, int free_data); /* Cluster distribution helpers for WATCH */ HashTable *cluster_dist_create(); void cluster_dist_free(HashTable *ht); -int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, +int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, strlen_t key_len, clusterKeyVal **kv); -void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val +void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val TSRMLS_DC); /* Aggregation for multi commands like MGET, MSET, and MSETNX */ @@ -351,7 +351,7 @@ unsigned short cluster_hash_key(const char *key, int len); /* Get the current time in miliseconds */ long long mstime(void); -PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, +PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, int cmd_len TSRMLS_DC); PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC); @@ -363,7 +363,7 @@ PHP_REDIS_API int cluster_reset_multi(redisCluster *c); PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, unsigned short port); -PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, +PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC); PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, @@ -379,28 +379,30 @@ PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, /* * Redis Cluster response handlers. Our response handlers generally take the * following form: - * PHP_REDIS_API void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + * PHP_REDIS_API void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, * void *ctx) * * Reply handlers are responsible for setting the PHP return value (either to * something valid, or FALSE in the case of some failures). */ -PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, +PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); +PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); @@ -411,28 +413,31 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); + /* MULTI BULK response functions */ -PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb func, void *ctx); -PHP_REDIS_API void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb, zval *z_ret); /* Handlers for things like DEL/MGET/MSET/MSETNX */ -PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); @@ -448,13 +453,21 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* CLIENT LIST response handler */ -PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); + +/* Custom STREAM handlers */ +PHP_REDIS_API void cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); /* MULTI BULK processing callbacks */ -int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, +int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); -int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, +int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count, void *ctx TSRMLS_DC); diff --git a/common.h b/common.h index c52dd6d833..a1dbf28a23 100644 --- a/common.h +++ b/common.h @@ -22,6 +22,9 @@ typedef struct { char *val; } zend_string; +#define REDIS_MAKE_STD_ZVAL(zv) MAKE_STD_ZVAL(zv) +#define REDIS_FREE_ZVAL(zv) (efree(zv)) + #define ZSTR_VAL(s) (s)->val #define ZSTR_LEN(s) (s)->len @@ -432,6 +435,9 @@ typedef int strlen_t; typedef size_t strlen_t; #define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_FALSE) #define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)((char *)Z_OBJ_P(z) - XtOffsetOf(class_entry, std)) + +#define REDIS_MAKE_STD_ZVAL(zv) do {} while(0) +#define REDIS_FREE_ZVAL(zv) do {} while(0) #endif /* NULL check so Eclipse doesn't go crazy */ @@ -1084,4 +1090,82 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_georadiusbymember, 0, 0, 4) ZEND_ARG_ARRAY_INFO(0, opts, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_xadd, 0, 0, 3) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(0, str_id) + ZEND_ARG_ARRAY_INFO(0, arr_fields, 0) + ZEND_ARG_INFO(0, i_maxlen) + ZEND_ARG_INFO(0, boo_approximate) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xpending, 0, 0, 2) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(0, str_group) + ZEND_ARG_INFO(0, str_start) + ZEND_ARG_INFO(0, str_end) + ZEND_ARG_INFO(0, i_count) + ZEND_ARG_INFO(0, str_consumer) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xrange, 0, 0, 3) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(0, str_start) + ZEND_ARG_INFO(0, str_end) + ZEND_ARG_INFO(0, i_count) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xread, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, arr_streams, 0) + ZEND_ARG_INFO(0, i_count) + ZEND_ARG_INFO(0, i_block) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xreadgroup, 0, 0, 3) + ZEND_ARG_INFO(0, str_group) + ZEND_ARG_INFO(0, str_consumer) + ZEND_ARG_ARRAY_INFO(0, arr_streams, 0) + ZEND_ARG_INFO(0, i_count) + ZEND_ARG_INFO(0, i_block) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xack, 0, 0, 3) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(0, str_group) + ZEND_ARG_ARRAY_INFO(0, arr_ids, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xclaim, 0, 0, 5) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(0, str_group) + ZEND_ARG_INFO(0, str_consumer) + ZEND_ARG_INFO(0, i_min_idle) + ZEND_ARG_ARRAY_INFO(0, arr_ids, 0) + ZEND_ARG_ARRAY_INFO(0, arr_opts, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xgroup, 0, 0, 1) + ZEND_ARG_INFO(0, str_operation) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(0, str_arg1) + ZEND_ARG_INFO(0, str_arg2) + ZEND_ARG_INFO(0, str_arg3) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xinfo, 0, 0, 1) + ZEND_ARG_INFO(0, str_cmd) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(0, str_group) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xtrim, 0, 0, 2) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_INFO(0, i_maxlen) + ZEND_ARG_INFO(0, boo_approximate) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xdel, 0, 0, 2) + ZEND_ARG_INFO(0, str_key) + ZEND_ARG_ARRAY_INFO(0, arr_ids, 0) +ZEND_END_ARG_INFO() + #endif diff --git a/library.c b/library.c index 6fc88dc89a..1d9f2ec479 100644 --- a/library.c +++ b/library.c @@ -114,6 +114,13 @@ static int resend_auth(RedisSock *redis_sock TSRMLS_DC) { return 0; } +/* Helper function and macro to test a RedisSock error prefix. */ +#define REDIS_SOCK_ERRCMP_STATIC(rs, s) redis_sock_errcmp(rs, s, sizeof(s)-1) +static int redis_sock_errcmp(RedisSock *redis_sock, const char *err, size_t errlen) { + return ZSTR_LEN(redis_sock->err) >= errlen && + memcmp(ZSTR_VAL(redis_sock->err), err, errlen) == 0; +} + /* Helper function that will throw an exception for a small number of ERR codes * returned by Redis. Typically we just return FALSE to the caller in the event * of an ERROR reply, but for the following error types: @@ -124,11 +131,19 @@ static int resend_auth(RedisSock *redis_sock TSRMLS_DC) { static void redis_error_throw(RedisSock *redis_sock TSRMLS_DC) { - if (redis_sock != NULL && redis_sock->err != NULL && - memcmp(ZSTR_VAL(redis_sock->err), "ERR", sizeof("ERR") - 1) != 0 && - memcmp(ZSTR_VAL(redis_sock->err), "NOSCRIPT", sizeof("NOSCRIPT") - 1) != 0 && - memcmp(ZSTR_VAL(redis_sock->err), "WRONGTYPE", sizeof("WRONGTYPE") - 1) != 0 - ) { + /* Short circuit if we have no redis_sock or any error */ + if (redis_sock == NULL || redis_sock->err == NULL) + return; + + /* We may want to flip this logic and check for MASTERDOWN, AUTH, + * and LOADING but that may have side effects (esp for things like + * Disque) */ + if (!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR") && + !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOSCRIPT") && + !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "WRONGTYPE") && + !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "BUSYGROUP") && + !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGROUP")) + { zend_throw_exception(redis_exception_ce, ZSTR_VAL(redis_sock->err), 0 TSRMLS_CC); } } @@ -442,8 +457,7 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, array_init(z_tab); - redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, - numElems, UNSERIALIZE_ALL); + redis_mbulk_reply_loop(redis_sock, z_tab, numElems, UNSERIALIZE_ALL TSRMLS_CC); return z_tab; } @@ -730,6 +744,24 @@ int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisS return retval; } +/* Append an array key to a redis smart string command. This function + * handles the boilerplate conditionals around string or integer keys */ +int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, ulong idx) +{ + char *arg, kbuf[128]; + int len; + + if (kstr) { + len = ZSTR_LEN(kstr); + arg = ZSTR_VAL(kstr); + } else { + len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); + arg = (char*)kbuf; + } + + return redis_cmd_append_sstr(cmd, arg, len); +} + PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; @@ -1136,6 +1168,30 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, zval_dtor(z_ret); } +static int +read_mbulk_header(RedisSock *redis_sock, int *nelem TSRMLS_DC) +{ + char line[4096]; + size_t len; + + /* Throws exception on failure */ + if (redis_sock_gets(redis_sock, line, sizeof(line)-1, &len TSRMLS_CC) < 0) + return -1; + + if (line[0] != '*') { + if (IS_ATOMIC(redis_sock)) { + if (line[0] == '-') { + redis_sock_set_err(redis_sock, line+1, len-1); + } + } + return -1; + } + + *nelem = atoi(line+1); + + return 0; +} + static int redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int unserialize, int decode) @@ -1164,8 +1220,7 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, array_init(z_multi_result); /* pre-allocate array for multi's results. */ /* Grab our key, value, key, value array */ - redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_multi_result, numElems, unserialize); + redis_mbulk_reply_loop(redis_sock, z_multi_result, numElems, unserialize TSRMLS_CC); /* Zip keys and values */ array_zip_values_and_scores(redis_sock, z_multi_result, decode TSRMLS_CC); @@ -1179,6 +1234,243 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return 0; } +/* Consume message ID */ +PHP_REDIS_API int +redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen, + size_t *linelen, int set_err TSRMLS_DC) +{ + REDIS_REPLY_TYPE type; + long info; + + if (redis_read_reply_type(redis_sock, &type, &info TSRMLS_CC) < 0 || + (type != TYPE_LINE && type != TYPE_ERR)) + { + return -1; + } + + if (redis_sock_gets(redis_sock, buffer, buflen, linelen TSRMLS_CC) < 0) { + return -1; + } + + if (set_err && type == TYPE_ERR) { + if (IS_ATOMIC(redis_sock)) { + redis_sock_set_err(redis_sock, buffer, *linelen); + } + } + + return type == TYPE_LINE ? 0 : -1; +} + +/* Helper function to consume Redis stream message data. This is useful for + * multiple stream callers (e.g. XREAD[GROUP], and X[REV]RANGE handlers). */ +PHP_REDIS_API int +redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret + TSRMLS_DC) +{ + zval zv, *z_message = &zv; + int i, mhdr, fields; + char id[1024]; + size_t idlen; + + /* Iterate over each message */ + for (i = 0; i < count; i++) { + /* Consume inner multi-bulk header, message ID itself and finaly + * the multi-bulk header for field and values */ + if ((read_mbulk_header(redis_sock, &mhdr TSRMLS_CC) < 0 || mhdr != 2) || + redis_sock_read_single_line(redis_sock, id, sizeof(id), &idlen, 0 TSRMLS_CC) < 0 || + (read_mbulk_header(redis_sock, &fields TSRMLS_CC) < 0 || fields % 2 != 0)) + { + return -1; + } + + REDIS_MAKE_STD_ZVAL(z_message); + array_init(z_message); + + redis_mbulk_reply_loop(redis_sock, z_message, fields, UNSERIALIZE_VALS TSRMLS_CC); + array_zip_values_and_scores(redis_sock, z_message, SCORE_DECODE_NONE TSRMLS_CC); + add_assoc_zval_ex(z_ret, id, idlen, z_message); + } + + return 0; +} + +PHP_REDIS_API int +redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + int messages; + zval zv, *z_messages = &zv; + + REDIS_MAKE_STD_ZVAL(z_messages); + array_init(z_messages); + + if (read_mbulk_header(redis_sock, &messages TSRMLS_CC) < 0 || + redis_read_stream_messages(redis_sock, messages, z_messages TSRMLS_CC) < 0) + { + zval_dtor(z_messages); + REDIS_FREE_ZVAL(z_messages); + if (IS_ATOMIC(redis_sock)) { + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); + } + return -1; + } + + if (IS_ATOMIC(redis_sock)) { + RETVAL_ZVAL(z_messages, 0, 1); + } else { + add_next_index_zval(z_tab, z_messages); + } + + return 0; +} + +PHP_REDIS_API int +redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_streams + TSRMLS_DC) +{ + zval zv, *z_messages = &zv; + int i, shdr, messages; + char *id; + int idlen; + + for (i = 0; i < count; i++) { + if ((read_mbulk_header(redis_sock, &shdr TSRMLS_CC) < 0 || shdr != 2) || + (id = redis_sock_read(redis_sock, &idlen TSRMLS_CC)) == NULL || + read_mbulk_header(redis_sock, &messages TSRMLS_CC) < 0) + { + if (id) efree(id); + return -1; + } + + REDIS_MAKE_STD_ZVAL(z_messages); + array_init(z_messages); + + if (redis_read_stream_messages(redis_sock, messages, z_messages TSRMLS_CC) < 0) + goto failure; + + add_assoc_zval_ex(z_streams, id, idlen, z_messages); + efree(id); + } + + return 0; +failure: + efree(id); + zval_dtor(z_messages); + REDIS_FREE_ZVAL(z_messages); + return -1; +} + +PHP_REDIS_API int +redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval zv, *z_rv = &zv; + int streams; + + if (read_mbulk_header(redis_sock, &streams TSRMLS_CC) < 0) + goto failure; + + REDIS_MAKE_STD_ZVAL(z_rv); + array_init(z_rv); + + if (redis_read_stream_messages_multi(redis_sock, streams, z_rv TSRMLS_CC) < 0) + goto cleanup; + + if (IS_ATOMIC(redis_sock)) { + RETVAL_ZVAL(z_rv, 0, 1); + } else { + add_next_index_zval(z_tab, z_rv); + } + return 0; + +cleanup: + zval_dtor(z_rv); + REDIS_FREE_ZVAL(z_rv); +failure: + if (IS_ATOMIC(redis_sock)) { + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); + } + return -1; +} + +/* This helper function does that actual XCLAIM response handling, which can be used by both + * Redis and RedisCluster. Note that XCLAIM is somewhat unique in that its reply type depends + * on whether or not it was called with the JUSTID option */ +PHP_REDIS_API int +redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) { + zval zv, *z_msg = &zv; + REDIS_REPLY_TYPE type; + char id[1024]; + int i, fields; + long li; + size_t idlen; + + for (i = 0; i < count; i++) { + /* Consume inner reply type */ + if (redis_read_reply_type(redis_sock, &type, &li TSRMLS_CC) < 0 || + (type != TYPE_LINE && type != TYPE_MULTIBULK)) return -1; + + if (type == TYPE_LINE) { + /* JUSTID variant */ + if (redis_sock_gets(redis_sock, id, sizeof(id), &idlen TSRMLS_CC) < 0) + return -1; + add_next_index_stringl(rv, id, idlen); + } else { + if (li != 2 || redis_sock_read_single_line(redis_sock, id, sizeof(id), &idlen, 0 TSRMLS_CC) < 0 || + (read_mbulk_header(redis_sock, &fields TSRMLS_CC) < 0 || fields % 2 != 0)) return -1; + + REDIS_MAKE_STD_ZVAL(z_msg); + array_init(z_msg); + + redis_mbulk_reply_loop(redis_sock, z_msg, fields, UNSERIALIZE_VALS TSRMLS_CC); + array_zip_values_and_scores(redis_sock, z_msg, SCORE_DECODE_NONE TSRMLS_CC); + add_assoc_zval_ex(rv, id, idlen, z_msg); + } + } + + return 0; +} + +PHP_REDIS_API int +redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval zv, *z_ret = &zv; + int messages; + + /* All XCLAIM responses start multibulk */ + if (read_mbulk_header(redis_sock, &messages TSRMLS_CC) < 0) + goto failure; + + REDIS_MAKE_STD_ZVAL(z_ret); + array_init(z_ret); + + if (redis_read_xclaim_response(redis_sock, messages, z_ret TSRMLS_CC) < 0) { + zval_dtor(z_ret); + REDIS_FREE_ZVAL(z_ret); + goto failure; + } + + if (IS_ATOMIC(redis_sock)) { + RETVAL_ZVAL(z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, z_ret); + } + return 0; + +failure: + if (IS_ATOMIC(redis_sock)) { + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); + } + return -1; +} + /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */ PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -1262,6 +1554,30 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock efree(response); } +PHP_REDIS_API +void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + char buffer[4096]; + size_t len; + + if (redis_sock_read_single_line(redis_sock, buffer, sizeof(buffer), &len, 1 TSRMLS_CC) < 0) { + if (IS_ATOMIC(redis_sock)) { + RETURN_FALSE; + } else { + add_next_index_bool(z_tab, 0); + } + return; + } + + //str = estrndup(buffer, len); + if (IS_ATOMIC(redis_sock)) { + RETVAL_STRINGL(buffer, len); + } else { + add_next_index_stringl(z_tab, buffer, len); + } +} + /* like string response, but never unserialized. */ PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, @@ -1597,8 +1913,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, #endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ - redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_multi_result, numElems, UNSERIALIZE_ALL); + redis_mbulk_reply_loop(redis_sock, z_multi_result, numElems, UNSERIALIZE_ALL TSRMLS_CC); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_multi_result, 0, 1); @@ -1640,8 +1955,7 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval #endif array_init(z_multi_result); /* pre-allocate array for multi's results. */ - redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_multi_result, numElems, UNSERIALIZE_NONE); + redis_mbulk_reply_loop(redis_sock, z_multi_result, numElems, UNSERIALIZE_NONE TSRMLS_CC); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(z_multi_result, 0, 1); @@ -1653,8 +1967,8 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval } PHP_REDIS_API void -redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, int count, int unserialize) +redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, + int unserialize TSRMLS_DC) { char *line; int i, len; @@ -2090,32 +2404,27 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, /* * Read a single line response, having already consumed the reply-type byte */ -PHP_REDIS_API int +static int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, - zval *z_ret TSRMLS_DC) + int as_string, zval *z_ret TSRMLS_DC) { // Buffer to read our single line reply char inbuf[4096]; - size_t line_size; + size_t len; /* Attempt to read our single line reply */ - if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size TSRMLS_CC) < 0) { + if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len TSRMLS_CC) < 0) { return -1; } - // If this is an error response, check if it is a SYNC error, and throw in - // that case + /* Throw exception on SYNC error otherwise just set error string */ if(reply_type == TYPE_ERR) { - /* Set our last error */ - redis_sock_set_err(redis_sock, inbuf, line_size); - - /* Handle throwable errors */ + redis_sock_set_err(redis_sock, inbuf, len); redis_error_throw(redis_sock TSRMLS_CC); - - /* Set our response to FALSE */ ZVAL_FALSE(z_ret); + } else if (as_string) { + ZVAL_STRINGL(z_ret, inbuf, len); } else { - /* Set our response to TRUE */ ZVAL_TRUE(z_ret); } @@ -2140,8 +2449,8 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret } PHP_REDIS_API int -redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret - TSRMLS_DC) +redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_strings, + zval *z_ret TSRMLS_DC) { long reply_info; REDIS_REPLY_TYPE reply_type; @@ -2166,8 +2475,8 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret #if (PHP_MAJOR_VERSION < 7) ALLOC_INIT_ZVAL(z_subelem); #endif - redis_read_variant_line(redis_sock, reply_type, z_subelem - TSRMLS_CC); + redis_read_variant_line(redis_sock, reply_type, status_strings, + z_subelem TSRMLS_CC); add_next_index_zval(z_ret, z_subelem); break; case TYPE_INT: @@ -2192,7 +2501,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret array_init(z_subelem); add_next_index_zval(z_ret, z_subelem); redis_read_multibulk_recursive(redis_sock, reply_info, - z_subelem TSRMLS_CC); + status_strings, z_subelem TSRMLS_CC); break; default: // Stop the compiler from whinging @@ -2206,9 +2515,9 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret return 0; } -PHP_REDIS_API int -redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +static int +variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + int status_strings, zval *z_tab, void *ctx) { // Reply type, and reply size vars REDIS_REPLY_TYPE reply_type; @@ -2229,7 +2538,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, switch(reply_type) { case TYPE_ERR: case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, z_ret TSRMLS_CC); + redis_read_variant_line(redis_sock, reply_type, status_strings, z_ret TSRMLS_CC); break; case TYPE_INT: ZVAL_LONG(z_ret, reply_info); @@ -2243,9 +2552,8 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If we've got more than zero elements, parse our multi bulk // response recursively - if(reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, z_ret - TSRMLS_CC); + if (reply_info > -1) { + redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, z_ret TSRMLS_CC); } break; default: @@ -2269,4 +2577,18 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return 0; } +PHP_REDIS_API int +redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 0, z_tab, ctx); +} + +PHP_REDIS_API int +redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, z_tab, ctx); +} + /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/library.h b/library.h index 2c3e5da734..1c936dd5d2 100644 --- a/library.h +++ b/library.h @@ -21,6 +21,7 @@ int redis_cmd_append_sstr_long(smart_string *str, long append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC); int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot); +int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, ulong idx); PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *kw, char *fmt, ...); @@ -33,6 +34,8 @@ PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, Red PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx); PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); @@ -43,9 +46,13 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); +PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, + size_t buflen, size_t *linelen, int set_err TSRMLS_DC); PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); -PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); +//PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); +PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize TSRMLS_DC); + PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); @@ -56,7 +63,15 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, zend_long *iter); -PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, + +PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); + +PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); @@ -78,16 +93,22 @@ redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_r PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC); PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int +redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int +redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int +redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC); + /* * Variant Read methods, mostly to implement eval */ PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info TSRMLS_DC); -PHP_REDIS_API int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval *z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret TSRMLS_DC); -PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_strings, zval *z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); - +PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); #endif diff --git a/php_redis.h b/php_redis.h index b076e51b0c..d85c13ffb7 100644 --- a/php_redis.h +++ b/php_redis.h @@ -222,6 +222,21 @@ PHP_METHOD(Redis, pfadd); PHP_METHOD(Redis, pfcount); PHP_METHOD(Redis, pfmerge); +/* STREAMS */ +PHP_METHOD(Redis, xack); +PHP_METHOD(Redis, xadd); +PHP_METHOD(Redis, xclaim); +PHP_METHOD(Redis, xdel); +PHP_METHOD(Redis, xgroup); +PHP_METHOD(Redis, xinfo); +PHP_METHOD(Redis, xlen); +PHP_METHOD(Redis, xpending); +PHP_METHOD(Redis, xrange); +PHP_METHOD(Redis, xread); +PHP_METHOD(Redis, xreadgroup); +PHP_METHOD(Redis, xrevrange); +PHP_METHOD(Redis, xtrim); + /* Reflection */ PHP_METHOD(Redis, getHost); PHP_METHOD(Redis, getPort); diff --git a/redis.c b/redis.c index f44004fcb1..e25b65d749 100644 --- a/redis.c +++ b/redis.c @@ -402,6 +402,19 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC) PHP_ME(Redis, watch, arginfo_watch, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xack, arginfo_xack, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xadd, arginfo_xadd, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xclaim, arginfo_xclaim, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xdel, arginfo_xdel, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xgroup, arginfo_xgroup, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xinfo, arginfo_xinfo, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xlen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xpending, arginfo_xpending, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xrange, arginfo_xrange, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xread, arginfo_xread, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xreadgroup, arginfo_xreadgroup, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xrevrange, arginfo_xrange, ZEND_ACC_PUBLIC) + PHP_ME(Redis, xtrim, arginfo_xtrim, ZEND_ACC_PUBLIC) PHP_ME(Redis, zAdd, arginfo_zadd, ZEND_ACC_PUBLIC) PHP_ME(Redis, zCard, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) @@ -1413,7 +1426,7 @@ PHP_METHOD(Redis, sAdd) /* {{{ proto boolean Redis::sAddArray(string key, array $values) */ PHP_METHOD(Redis, sAddArray) { - REDIS_PROCESS_KW_CMD("SADD", redis_key_arr_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, redis_long_response); } /* }}} */ /* {{{ proto int Redis::sSize(string key) */ @@ -2441,7 +2454,7 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, add_next_index_zval(z_tab, z_ret); int num = atol(inbuf + 1); - if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, z_ret TSRMLS_CC) < 0) { + if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, z_ret TSRMLS_CC) < 0) { } if (fi) fi = fi->next; } @@ -3573,4 +3586,60 @@ PHP_METHOD(Redis, georadiusbymember) { REDIS_PROCESS_CMD(georadiusbymember, redis_read_variant_reply); } +/* + * Streams + */ + +PHP_METHOD(Redis, xack) { + REDIS_PROCESS_CMD(xack, redis_long_response); +} + +PHP_METHOD(Redis, xadd) { + REDIS_PROCESS_CMD(xadd, redis_single_line_reply); +} + +PHP_METHOD(Redis, xclaim) { + REDIS_PROCESS_CMD(xclaim, redis_xclaim_reply); +} + +PHP_METHOD(Redis, xdel) { + REDIS_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, redis_long_response); +} + +PHP_METHOD(Redis, xgroup) { + REDIS_PROCESS_CMD(xgroup, redis_read_variant_reply); +} + +PHP_METHOD(Redis, xinfo) { + REDIS_PROCESS_CMD(xinfo, redis_read_variant_reply); +} + +PHP_METHOD(Redis, xlen) { + REDIS_PROCESS_KW_CMD("XLEN", redis_key_cmd, redis_long_response); +} + +PHP_METHOD(Redis, xpending) { + REDIS_PROCESS_CMD(xpending, redis_read_variant_reply_strings); +} + +PHP_METHOD(Redis, xrange) { + REDIS_PROCESS_KW_CMD("XRANGE", redis_xrange_cmd, redis_xrange_reply); +} + +PHP_METHOD(Redis, xread) { + REDIS_PROCESS_CMD(xread, redis_xread_reply); +} + +PHP_METHOD(Redis, xreadgroup) { + REDIS_PROCESS_CMD(xreadgroup, redis_xread_reply); +} + +PHP_METHOD(Redis, xrevrange) { + REDIS_PROCESS_KW_CMD("XREVRANGE", redis_xrange_cmd, redis_xrange_reply); +} + +PHP_METHOD(Redis, xtrim) { + REDIS_PROCESS_CMD(xtrim, redis_long_response); +} + /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/redis_array.c b/redis_array.c index 9f00a74c01..217ab63e15 100644 --- a/redis_array.c +++ b/redis_array.c @@ -735,7 +735,6 @@ PHP_METHOD(RedisArray, keys) RedisArray *ra; char *pattern; strlen_t pattern_len; - int i; /* Make sure the prototype is correct */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", @@ -764,7 +763,6 @@ PHP_METHOD(RedisArray, keys) PHP_METHOD(RedisArray, getOption) { zval *object, z_fun, z_args[1]; - int i; RedisArray *ra; zend_long opt; @@ -791,7 +789,6 @@ PHP_METHOD(RedisArray, getOption) PHP_METHOD(RedisArray, setOption) { zval *object, z_fun, z_args[2]; - int i; RedisArray *ra; zend_long opt; char *val_str; @@ -822,7 +819,6 @@ PHP_METHOD(RedisArray, setOption) PHP_METHOD(RedisArray, select) { zval *object, z_fun, z_args[1]; - int i; RedisArray *ra; zend_long opt; diff --git a/redis_cluster.c b/redis_cluster.c index 4271b9d993..130b961a4c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -252,6 +252,19 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, unlink, arginfo_del, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xack, arginfo_xack, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xadd, arginfo_xadd, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xclaim, arginfo_xclaim, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xdel, arginfo_xdel, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xgroup, arginfo_xgroup, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xinfo, arginfo_xinfo, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xlen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xpending, arginfo_xpending, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xrange, arginfo_xrange, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xread, arginfo_xread, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xreadgroup, arginfo_xreadgroup, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xrevrange, arginfo_xrange, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, xtrim, arginfo_xtrim, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcard, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zcount, arginfo_key_min_max, ZEND_ACC_PUBLIC) @@ -1101,7 +1114,7 @@ PHP_METHOD(RedisCluster, keys) { } /* Ensure we can get a response */ - resp = cluster_read_resp(c TSRMLS_CC); + resp = cluster_read_resp(c, 0 TSRMLS_CC); if (!resp) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Can't read response from %s:%d", ZSTR_VAL(node->sock->host), @@ -1305,7 +1318,7 @@ PHP_METHOD(RedisCluster, sadd) { /* {{{ proto long RedisCluster::saddarray(string key, array values) */ PHP_METHOD(RedisCluster, saddarray) { - CLUSTER_PROCESS_KW_CMD("SADD", redis_key_arr_cmd, cluster_long_resp, 0); + CLUSTER_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, cluster_long_resp, 0); } /* }}} */ @@ -2963,6 +2976,65 @@ PHP_METHOD(RedisCluster, ping) { } /* }}} */ +/* {{{ proto long RedisCluster::xack(string key, string group, array ids) }}} */ +PHP_METHOD(RedisCluster, xack) { + CLUSTER_PROCESS_CMD(xack, cluster_long_resp, 0); +} + +/* {{{ proto string RedisCluster::xadd(string key, string id, array field_values) }}} */ +PHP_METHOD(RedisCluster, xadd) { + CLUSTER_PROCESS_CMD(xadd, cluster_single_line_resp, 0); +} + +/* {{{ proto array RedisCluster::xclaim(string key, string group, string consumer, + * long min_idle_time, array ids, array options) */ +PHP_METHOD(RedisCluster, xclaim) { + CLUSTER_PROCESS_CMD(xclaim, cluster_xclaim_resp, 0); +} + +PHP_METHOD(RedisCluster, xdel) { + CLUSTER_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, cluster_long_resp, 0); +} + +/* {{{ proto variant RedisCluster::xgroup(string op, [string key, string arg1, string arg2]) }}} */ +PHP_METHOD(RedisCluster, xgroup) { + CLUSTER_PROCESS_CMD(xgroup, cluster_variant_resp, 0); +} + +/* {{{ proto variant RedisCluster::xinfo(string op, [string arg1, string arg2]); */ +PHP_METHOD(RedisCluster, xinfo) { + CLUSTER_PROCESS_CMD(xinfo, cluster_variant_resp, 0); +} + +/* {{{ proto string RedisCluster::xlen(string key) }}} */ +PHP_METHOD(RedisCluster, xlen) { + CLUSTER_PROCESS_KW_CMD("XLEN", redis_key_cmd, cluster_long_resp, 1); +} + +PHP_METHOD(RedisCluster, xpending) { + CLUSTER_PROCESS_CMD(xpending, cluster_variant_resp_strings, 1); +} + +PHP_METHOD(RedisCluster, xrange) { + CLUSTER_PROCESS_KW_CMD("XRANGE", redis_xrange_cmd, cluster_xrange_resp, 1); +} + +PHP_METHOD(RedisCluster, xrevrange) { + CLUSTER_PROCESS_KW_CMD("XREVRANGE", redis_xrange_cmd, cluster_xrange_resp, 1); +} + +PHP_METHOD(RedisCluster, xread) { + CLUSTER_PROCESS_CMD(xread, cluster_xread_resp, 1); +} + +PHP_METHOD(RedisCluster, xreadgroup) { + CLUSTER_PROCESS_CMD(xreadgroup, cluster_xread_resp, 0); +} + +PHP_METHOD(RedisCluster, xtrim) { + CLUSTER_PROCESS_CMD(xtrim, cluster_long_resp, 0); +} + /* {{{ proto string RedisCluster::echo(string key, string msg) * proto string RedisCluster::echo(array host_port, string msg) */ PHP_METHOD(RedisCluster, echo) { diff --git a/redis_cluster.h b/redis_cluster.h index 74903e67c6..b4d0a8bab5 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -263,6 +263,21 @@ PHP_METHOD(RedisCluster, zscan); PHP_METHOD(RedisCluster, hscan); PHP_METHOD(RedisCluster, sscan); +/* STREAMS */ +PHP_METHOD(RedisCluster, xack); +PHP_METHOD(RedisCluster, xadd); +PHP_METHOD(RedisCluster, xclaim); +PHP_METHOD(RedisCluster, xdel); +PHP_METHOD(RedisCluster, xgroup); +PHP_METHOD(RedisCluster, xinfo); +PHP_METHOD(RedisCluster, xlen); +PHP_METHOD(RedisCluster, xpending); +PHP_METHOD(RedisCluster, xrange); +PHP_METHOD(RedisCluster, xread); +PHP_METHOD(RedisCluster, xreadgroup); +PHP_METHOD(RedisCluster, xrevrange); +PHP_METHOD(RedisCluster, xtrim); + /* Transactions */ PHP_METHOD(RedisCluster, multi); PHP_METHOD(RedisCluster, exec); diff --git a/redis_commands.c b/redis_commands.c index c47669adb4..e9d26c3d2e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1050,13 +1050,16 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Commands that take a key and then an array of values */ -int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +#define VAL_TYPE_VALUES 1 +#define VAL_TYPE_STRINGS 2 +static int gen_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, int valtype, char **cmd, int *cmd_len, + short *slot, void **ctx) { zval *z_arr, *z_val; HashTable *ht_arr; smart_string cmdstr = {0}; + zend_string *zstr; int key_free, val_free, argc = 1; strlen_t val_len, key_len; char *key, *val; @@ -1080,10 +1083,17 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (key_free) efree(key); /* Iterate our hash table, serializing and appending values */ + assert(valtype == VAL_TYPE_VALUES || valtype == VAL_TYPE_STRINGS); ZEND_HASH_FOREACH_VAL(ht_arr, z_val) { - val_free = redis_pack(redis_sock, z_val, &val, &val_len TSRMLS_CC); - redis_cmd_append_sstr(&cmdstr, val, val_len); - if (val_free) efree(val); + if (valtype == VAL_TYPE_VALUES) { + val_free = redis_pack(redis_sock, z_val, &val, &val_len TSRMLS_CC); + redis_cmd_append_sstr(&cmdstr, val, val_len); + if (val_free) efree(val); + } else { + zstr = zval_get_string(z_val); + redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + zend_string_release(zstr); + } } ZEND_HASH_FOREACH_END(); *cmd_len = cmdstr.len; @@ -1092,6 +1102,22 @@ int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, + VAL_TYPE_VALUES, cmd, cmd_len, slot, ctx); +} + +int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, + VAL_TYPE_STRINGS, cmd, cmd_len, slot, ctx); +} + /* Generic function that takes a variable number of keys, with an optional * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ @@ -3147,6 +3173,611 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* XADD */ +int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_string *arrkey; + zval *z_fields, *value; + zend_long maxlen = 0; + zend_bool approx = 0; + ulong idx; + HashTable *ht_fields; + int fcount, argc; + char *key, *id; + strlen_t keylen, idlen; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa|lb", &key, &keylen, + &id, &idlen, &z_fields, &maxlen, &approx) == FAILURE) + { + return FAILURE; + } + + /* At least one field and string are required */ + ht_fields = Z_ARRVAL_P(z_fields); + if ((fcount = zend_hash_num_elements(ht_fields)) == 0) { + return FAILURE; + } + + if (maxlen < 0 || (maxlen == 0 && approx != 0)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Warning: Invalid MAXLEN argument or approximate flag"); + } + + + /* Calculate argc for XADD. It's a bit complex because we've got + * an optional MAXLEN argument which can either take the form MAXLEN N + * or MAXLEN ~ N */ + argc = 2 + (fcount*2) + (maxlen > 0 ? (approx ? 3 : 2) : 0); + + /* XADD key ID field string [field string ...] */ + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XADD"); + redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); + + /* Now append our MAXLEN bits if we've got them */ + if (maxlen > 0) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN"); + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, approx, "~"); + redis_cmd_append_sstr_long(&cmdstr, maxlen); + } + + /* Now append ID and field(s) */ + redis_cmd_append_sstr(&cmdstr, id, idlen); + ZEND_HASH_FOREACH_KEY_VAL(ht_fields, idx, arrkey, value) { + redis_cmd_append_sstr_arrkey(&cmdstr, arrkey, idx); + redis_cmd_append_sstr_zval(&cmdstr, value, redis_sock TSRMLS_CC); + } ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +// XPENDING key group [start end count] [consumer] +int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + char *key, *group, *start = NULL, *end = NULL, *consumer = NULL; + strlen_t keylen, grouplen, startlen, endlen, consumerlen; + int argc; + zend_long count = -1; + + // XPENDING mystream group55 - + 10 consumer-123 + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ssls", &key, + &keylen, &group, &grouplen, &start, &startlen, + &end, &endlen, &count, &consumer, &consumerlen) + == FAILURE) + { + return FAILURE; + } + + /* If we've been passed a start argument, we also need end and count */ + if (start != NULL && (end == NULL || count < 0)) { + return FAILURE; + } + + /* Calculate argc. It's either 2, 5, or 6 */ + argc = 2 + (start != NULL ? 3 + (consumer != NULL) : 0); + + /* Construct command and add required arguments */ + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XPENDING"); + redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); + redis_cmd_append_sstr(&cmdstr, group, grouplen); + + /* Add optional argumentst */ + if (start) { + redis_cmd_append_sstr(&cmdstr, start, startlen); + redis_cmd_append_sstr(&cmdstr, end, endlen); + redis_cmd_append_sstr_long(&cmdstr, (long)count); + + /* Finally add consumer if we have it */ + if (consumer) redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +/* X[REV]RANGE key start end [COUNT count] */ +int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + smart_string cmdstr = {0}; + char *key, *start, *end; + strlen_t keylen, startlen, endlen; + zend_long count = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|l", &key, &keylen, + &start, &startlen, &end, &endlen, &count) + == FAILURE) + { + return FAILURE; + } + + redis_cmd_init_sstr(&cmdstr, 3 + (2 * (count > -1)), kw, strlen(kw)); + redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); + redis_cmd_append_sstr(&cmdstr, start, startlen); + redis_cmd_append_sstr(&cmdstr, end, endlen); + + if (count > -1) { + redis_cmd_append_sstr(&cmdstr, "COUNT", sizeof("COUNT")-1); + redis_cmd_append_sstr_long(&cmdstr, count); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +/* Helper function to take an associative array and append the Redis + * STREAMS stream [stream...] id [id ...] arguments to a command string. */ +static int +append_stream_args(smart_string *cmdstr, HashTable *ht, RedisSock *redis_sock, + short *slot TSRMLS_DC) +{ + char *kptr, kbuf[40]; + int klen, i, pos = 0; + zend_string *key, *idstr; + short oldslot; + zval **id; + ulong idx; + + /* Append STREAM qualifier */ + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "STREAMS"); + + /* Sentinel slot */ + if (slot) oldslot = -1; + + /* Allocate memory to keep IDs */ + id = emalloc(sizeof(*id) * zend_hash_num_elements(ht)); + + /* Iterate over our stream => id array appending streams and retaining each + * value for final arguments */ + ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, id[pos++]) { + if (key) { + klen = ZSTR_LEN(key); + kptr = ZSTR_VAL(key); + } else { + klen = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); + kptr = (char*)kbuf; + } + + /* Append stream key */ + redis_cmd_append_sstr_key(cmdstr, kptr, klen, redis_sock, slot); + + /* Protect the user against CROSSSLOT to avoid confusion */ + if (slot) { + if (oldslot != -1 && *slot != oldslot) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Warning, not all keys hash to the same slot!"); + efree(id); + return FAILURE; + } + oldslot = *slot; + } + } ZEND_HASH_FOREACH_END(); + + /* Add our IDs */ + for (i = 0; i < pos; i++) { + idstr = zval_get_string(id[i]); + redis_cmd_append_sstr(cmdstr, ZSTR_VAL(idstr), ZSTR_LEN(idstr)); + zend_string_release(idstr); + } + + /* Clean up ID container array */ + efree(id); + + return 0; +} + +/* XREAD [COUNT count] [BLOCK ms] STREAMS key [key ...] id [id ...] */ +int redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_long count = -1, block = -1; + zval *z_streams; + int argc, scount; + HashTable *kt; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|ll", &z_streams, + &count, &block) == FAILURE) + { + return FAILURE; + } + + /* At least one stream and ID is required */ + kt = Z_ARRVAL_P(z_streams); + if ((scount = zend_hash_num_elements(kt)) < 1) { + return FAILURE; + } + + /* Calculate argc and start constructing command */ + argc = 1 + (2 * scount) + (2 * (count > -1)) + (2 * (block > -1)); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XREAD"); + + /* Append COUNT if we have it */ + if (count > -1) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); + redis_cmd_append_sstr_long(&cmdstr, count); + } + + /* Append BLOCK if we have it */ + if (block > -1) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BLOCK"); + redis_cmd_append_sstr_long(&cmdstr, block); + } + + /* Append final STREAM key [key ...] id [id ...] arguments */ + if (append_stream_args(&cmdstr, kt, redis_sock, slot TSRMLS_CC) < 0) { + efree(cmdstr.c); + return FAILURE; + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +/* XREADGROUP GROUP group consumer [COUNT count] [BLOCK ms] + * STREAMS key [key ...] id [id ...] */ +int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zval *z_streams; + HashTable *kt; + char *group, *consumer; + strlen_t grouplen, consumerlen; + int scount, argc; + zend_long count = -1, block = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa|ll", &group, + &grouplen, &consumer, &consumerlen, &z_streams, + &count, &block) == FAILURE) + { + return FAILURE; + } + + /* Redis requires at least one stream */ + kt = Z_ARRVAL_P(z_streams); + if ((scount = zend_hash_num_elements(kt)) < 1) { + return FAILURE; + } + + /* Calculate argc and start constructing commands */ + argc = 4 + (2 * scount) + (2 * (count > -1)) + (2 * (block > -1)); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XREADGROUP"); + + /* Group and consumer */ + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GROUP"); + redis_cmd_append_sstr(&cmdstr, group, grouplen); + redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); + + /* Append COUNT if we have it */ + if (count > -1) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); + redis_cmd_append_sstr_long(&cmdstr, count); + } + + /* Append BLOCK argument if we have it */ + if (block > -1) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BLOCK"); + redis_cmd_append_sstr_long(&cmdstr, block); + } + + /* Finally append stream and id args */ + if (append_stream_args(&cmdstr, kt, redis_sock, slot TSRMLS_CC) < 0) { + efree(cmdstr.c); + return FAILURE; + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +/* XACK key group id [id ...] */ +int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + char *key, *group; + strlen_t keylen, grouplen; + zend_string *idstr; + zval *z_ids, *z_id; + HashTable *ht_ids; + int idcount; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa", &key, &keylen, + &group, &grouplen, &z_ids) == FAILURE) + { + return FAILURE; + } + + ht_ids = Z_ARRVAL_P(z_ids); + if ((idcount = zend_hash_num_elements(ht_ids)) < 1) { + return FAILURE; + } + + /* Create command and add initial arguments */ + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + idcount, "XACK"); + redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); + redis_cmd_append_sstr(&cmdstr, group, grouplen); + + /* Append IDs */ + ZEND_HASH_FOREACH_VAL(ht_ids, z_id) { + idstr = zval_get_string(z_id); + redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(idstr), ZSTR_LEN(idstr)); + zend_string_release(idstr); + } ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +/* XCLAIM options container */ +typedef struct xclaimOptions { + struct { + char *type; + zend_long time; + } idle; + zend_long retrycount; + int force; + int justid; +} xclaimOptions; + +/* Helper to extract XCLAIM options */ +static void get_xclaim_options(zval *z_arr, xclaimOptions *opt TSRMLS_DC) { + HashTable *ht; + zend_string *zkey; + char *kval; + strlen_t klen; + ulong idx; + zval *zv; + + PHPREDIS_NOTUSED(idx); + + /* Initialize options array to sane defaults */ + memset(opt, 0, sizeof(*opt)); + opt->retrycount = -1; + opt->idle.time = -1; + + /* Early return if we don't have any options */ + if (z_arr == NULL) + return; + + /* Iterate over our options array */ + ht = Z_ARRVAL_P(z_arr); + ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, zv) { + if (zkey) { + /* Every key => value xclaim option requires a long and Redis + * treats -1 as not being passed so skip negative values too. */ + if (Z_TYPE_P(zv) != IS_LONG || Z_LVAL_P(zv) < 0) + continue; + + kval = ZSTR_VAL(zkey); + klen = ZSTR_LEN(zkey); + if (klen == 4) { + if (!strncasecmp(kval, "TIME", 4)) { + opt->idle.type = "TIME"; + opt->idle.time = Z_LVAL_P(zv); + } else if (!strncasecmp(kval, "IDLE", 4)) { + opt->idle.type = "IDLE"; + opt->idle.time = Z_LVAL_P(zv); + } + } else if (klen == 10 && !strncasecmp(kval, "RETRYCOUNT", 10)) { + opt->retrycount = Z_LVAL_P(zv); + } + } else { + if (Z_TYPE_P(zv) == IS_STRING) { + kval = Z_STRVAL_P(zv); + klen = Z_STRLEN_P(zv); + if (klen == 5 && !strncasecmp(kval, "FORCE", 5)) { + opt->force = 1; + } else if (klen == 6 && !strncasecmp(kval, "JUSTID", 6)) { + opt->justid = 1; + } + } + } + } ZEND_HASH_FOREACH_END(); +} + +/* Count argc for any options we may have */ +static int xclaim_options_argc(xclaimOptions *opt) { + int argc = 0; + + if (opt->idle.type != NULL && opt->idle.time != -1) + argc += 2; + if (opt->retrycount != -1) + argc += 2; + if (opt->force) + argc++; + if (opt->justid) + argc++; + + return argc; +} + +/* Append XCLAIM options */ +static void append_xclaim_options(smart_string *cmd, xclaimOptions *opt) { + /* IDLE/TIME long */ + if (opt->idle.type != NULL && opt->idle.time != -1) { + redis_cmd_append_sstr(cmd, opt->idle.type, strlen(opt->idle.type)); + redis_cmd_append_sstr_long(cmd, opt->idle.time); + } + + /* RETRYCOUNT */ + if (opt->retrycount != -1) { + REDIS_CMD_APPEND_SSTR_STATIC(cmd, "RETRYCOUNT"); + redis_cmd_append_sstr_long(cmd, opt->retrycount); + } + + /* FORCE and JUSTID */ + if (opt->force) + REDIS_CMD_APPEND_SSTR_STATIC(cmd, "FORCE"); + if (opt->justid) + REDIS_CMD_APPEND_SSTR_STATIC(cmd, "JUSTID"); +} + +/* XCLAIM + [IDLE ] [TIME ] [RETRYCOUNT ] + [FORCE] [JUSTID] */ +int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + char *key, *group, *consumer; + strlen_t keylen, grouplen, consumerlen; + zend_long min_idle; + int argc, id_count; + zval *z_ids, *z_id, *z_opts = NULL; + zend_string *zstr; + HashTable *ht_ids; + xclaimOptions opts; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sssla|a", &key, &keylen, + &group, &grouplen, &consumer, &consumerlen, &min_idle, + &z_ids, &z_opts) == FAILURE) + { + return FAILURE; + } + + /* At least one id is required */ + ht_ids = Z_ARRVAL_P(z_ids); + if ((id_count = zend_hash_num_elements(ht_ids)) < 1) { + return FAILURE; + } + + /* Extract options array if we've got them */ + get_xclaim_options(z_opts, &opts TSRMLS_CC); + + /* Now we have enough information to calculate argc */ + argc = 4 + id_count + xclaim_options_argc(&opts); + + /* Start constructing our command */ + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XCLAIM"); + redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); + redis_cmd_append_sstr(&cmdstr, group, grouplen); + redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); + redis_cmd_append_sstr_long(&cmdstr, min_idle); + + /* Add IDs */ + ZEND_HASH_FOREACH_VAL(ht_ids, z_id) { + zstr = zval_get_string(z_id); + redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + zend_string_release(zstr); + } ZEND_HASH_FOREACH_END(); + + /* Finally add our options */ + append_xclaim_options(&cmdstr, &opts); + + /* Success */ + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +/* XGROUP HELP + * XGROUP SETID key group id + * XGROUP DELGROUP key groupname + * XGROUP CREATE key groupname id + * XGROUP DELCONSUMER key groupname consumername */ +int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *op, *key = NULL, *arg1 = NULL, *arg2 = NULL; + strlen_t oplen, keylen, arg1len, arg2len; + int argc = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ssss", &op, &oplen, + &key, &keylen, &arg1, &arg1len, &arg2, &arg2len) + == FAILURE) + { + return FAILURE; + } + + if (argc == 1 && oplen == 4 && !strncasecmp(op, "HELP", 4)) { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "s", "HELP", 4); + return SUCCESS; + } else if (argc == 4 && ((oplen == 5 && !strncasecmp(op, "SETID", 5)) || + (oplen == 6 && !strncasecmp(op, "CREATE", 6)) || + (oplen == 11 && !strncasecmp(op, "DELCONSUMER", 11)))) + { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "skss", op, oplen, key, keylen, + arg1, arg1len, arg2, arg2len); + return SUCCESS; + } else if (argc == 3 && ((oplen == 7 && !strncasecmp(op, "DELGROUP", 7)))) { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "sks", op, oplen, key, + keylen, arg1, arg1len); + return SUCCESS; + } + + /* Didn't detect any valid XGROUP command pattern */ + return FAILURE; +} + + + +/* XINFO CONSUMERS key group + * XINFO GROUPS key + * XINFO STREAM key + * XINFO HELP */ +int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *op, *key, *arg; + strlen_t oplen, keylen, arglen; + char fmt[4]; + int argc = ZEND_NUM_ARGS(); + + if (argc > 3 || zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ss", + &op, &oplen, &key, &keylen, &arg, + &arglen) == FAILURE) + { + return FAILURE; + } + + /* Our format is simply "s", "sk" or "sks" depending on argc */ + memcpy(fmt, "sks", sizeof("sks")-1); + fmt[argc] = '\0'; + + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XINFO", fmt, op, oplen, key, keylen, + arg, arglen); + return SUCCESS; +} + +/* XTRIM MAXLEN [~] count */ +int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key; + strlen_t keylen; + zend_long maxlen; + zend_bool approx = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|b", &key, &keylen, + &maxlen, &approx) == FAILURE) + { + return FAILURE; + } + + if (approx) { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XTRIM", "kssl", key, keylen, + "MAXLEN", 6, "~", 1, maxlen); + } else { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XTRIM", "ksl", key, keylen, + "MAXLEN", 6, maxlen); + } + + return SUCCESS; +} + /* * Redis commands that don't deal with the server at all. The RedisSock* * pointer is the only thing retreived differently, so we just take that diff --git a/redis_commands.h b/redis_commands.h index 413b868e22..2f3e53deda 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -72,7 +72,10 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); /* Construct SCAN and similar commands, as well as check iterator */ @@ -112,6 +115,9 @@ int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them @@ -256,6 +262,33 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands that don't communicate with Redis at all (such as getOption, * setOption, _prefix, _serialize, etc). These can be handled in one place * with the method of grabbing our RedisSock* object in different ways diff --git a/redis_session.c b/redis_session.c index 08022cb1c9..7f1a26568a 100644 --- a/redis_session.c +++ b/redis_session.c @@ -1061,7 +1061,7 @@ PS_READ_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; char *cmd, *skey; - int cmdlen, skeylen; + int cmdlen, skeylen, free_flag; short slot; /* Set up our command and slot information */ @@ -1084,7 +1084,7 @@ PS_READ_FUNC(rediscluster) { efree(cmd); /* Attempt to read reply */ - reply = cluster_read_resp(c TSRMLS_CC); + reply = cluster_read_resp(c, 0 TSRMLS_CC); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; @@ -1099,16 +1099,20 @@ PS_READ_FUNC(rediscluster) { *val = reply->str; *vallen = reply->len; } + + free_flag = 0; #else if (reply->str == NULL) { *val = ZSTR_EMPTY_ALLOC(); } else { *val = zend_string_init(reply->str, reply->len, 0); } + + free_flag = 1; #endif /* Clean up */ - cluster_free_reply(reply, 0); + cluster_free_reply(reply, free_flag); /* Success! */ return SUCCESS; @@ -1148,7 +1152,7 @@ PS_WRITE_FUNC(rediscluster) { efree(cmd); /* Attempt to read reply */ - reply = cluster_read_resp(c TSRMLS_CC); + reply = cluster_read_resp(c, 0 TSRMLS_CC); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; @@ -1188,7 +1192,7 @@ PS_DESTROY_FUNC(rediscluster) { efree(cmd); /* Attempt to read reply */ - reply = cluster_read_resp(c TSRMLS_CC); + reply = cluster_read_resp(c, 0 TSRMLS_CC); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index b5da01e831..df650ee595 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -16,6 +16,11 @@ class Redis_Test extends TestSuite 'Cupertino' => Array(-122.032182, 37.322998) ); + protected $serializers = Array( + Redis::SERIALIZER_NONE, + Redis::SERIALIZER_PHP, + ); + /** * @var Redis */ @@ -35,12 +40,20 @@ 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, "ge"); } + protected function mstime() { + return round(microtime(true)*1000); + } + protected function newInstance() { $r = new Redis(); @@ -5223,6 +5236,437 @@ public function testRawCommand() { $this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D')); } + /* STREAMS */ + + protected function addStreamEntries($key, $count) { + $ids = Array(); + + $this->redis->del($key); + + for ($i = 0; $i < $count; $i++) { + $ids[] = $this->redis->xAdd($key, '*', Array('field' => "value:$i")); + } + + return $ids; + } + + protected function addStreamsAndGroups($arr_streams, $count, $arr_groups) { + $ids = Array(); + + 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", '*', Array('k1' => 'v1', 'k2' => 'v2')); + $this->assertEquals($this->redis->xLen('stream'), $i+1); + + /* Redis should return - */ + $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', '*', Array('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', '*', Array('k' => 'v'), 10, true); + $this->assertEquals(count(explode('-', $id)), 2); + + /* Empty message should fail */ + $this->redis->xAdd('stream', '*', Array()); + } + + protected function doXRangeTest($reverse) { + $key = '{stream}'; + + if ($reverse) { + list($cmd,$a1,$a2) = Array('xRevRange', '+', 0); + } else { + list($cmd,$a1,$a2) = Array('xRange', 0, '+'); + } + + $this->redis->del($key); + for ($i = 0; $i < 3; $i++) { + $msg = Array('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 ? 2 : 0; + foreach ($messages as $seq => $v) { + $this->assertEquals(count(explode('-', $seq)), 2); + $this->assertEquals($v, Array('field' => "value:$i")); + $i += $reverse ? -1 : 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 (Array(false, true) as $reverse) { + foreach ($this->serializers as $serializer) { + foreach (Array(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}', '*', Array('foo' => 'bar')); + $this->assertEquals($this->redis->xLen('{stream}'), $i+1); + } + } + + public function testXGroup() { + if (!$this->minVersionCheck("5.0")) + return $this->markTestSkipped(); + + $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); + + /* DELGROUP not yet implemented in Redis */ + } + + public function testXAck() { + if (!$this->minVersionCheck("5.0")) + return $this->markTestSkipped(); + + for ($n = 1; $n <= 3; $n++) { + $this->addStreamsAndGroups(Array('{s}'), 3, Array('g1' => 0)); + $msg = $this->redis->xReadGroup('g1', 'c1', Array('{s}' => 0)); + + /* Extract IDs */ + $smsg = array_shift($msg); + $ids = array_keys($smsg); + + /* Now ACK $n messages */ + $ids = array_slice($ids, 0, $n); + $this->assertEquals($this->redis->xAck('{s}', 'g1', $ids), $n); + } + + /* Verify sending no IDs is a failure */ + $this->assertFalse($this->redis->xAck('{s}', 'g1', Array())); + } + + protected function doXReadTest() { + if (!$this->minVersionCheck("5.0")) + return $this->markTestSkipped(); + + $row = Array('f1' => 'v1', 'f2' => 'v2'); + $msgdata = Array( + '{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', '*', Array('final' => 'row')); + $rmsg = $this->redis->xRead(Array('{stream}-1' => $id)); + $this->assertEquals( + $this->redis->xRead(Array('{stream}-1' => $id)), + Array('{stream}-1' => Array($new_id => Array('final' => 'row'))) + ); + + /* Emtpy query should fail */ + $this->assertFalse($this->redis->xRead(Array())); + } + + 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(Array('somestream' => '$'), -1, 100); + $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 = Array('{s}-1', '{s}-2'); + $qstreams = Array('{s}-1' => 0, '{s}-2' => 0); + $groups = Array('g1' => 0, 'g2' => 0); + + $ids = $this->addStreamsAndGroups($streams, 3, $groups); + + /* Test that we get get the IDs we should */ + foreach (Array('g1', 'g2') as $group) { + foreach ($ids as $stream => $messages) { + while ($ids[$stream]) { + /* Read more messages */ + $resp = $this->redis->xReadGroup($group, 'consumer', $qstreams); + + /* 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, Array($id)); + } + } + } + + /* Test COUNT option */ + for ($c = 1; $c <= 3; $c++) { + $this->addStreamsAndGroups($streams, 3, $groups); + $resp = $this->redis->xReadGroup('g1', 'consumer', $qstreams, $c); + + foreach ($resp as $stream => $smsg) { + $this->assertEquals(count($smsg), $c); + } + } + + /* Finally test BLOCK with a sloppy timing test */ + $t1 = $this->mstime(); + $qnew = Array('{s}-1' => '>', '{s}-2' => '>'); + $this->redis->xReadGroup('g1', 'c1', $qnew, -1, 100); + $t2 = $this->mstime(); + $this->assertTrue($t2 - $t1 >= 100); + } + + public function testXPending() { + if (!$this->minVersionCheck("5.0")) { + return $this->markTestSkipped(); + } + + $rows = 5; + $this->addStreamsAndGroups(Array('s'), $rows, Array('group' => 0)); + + $msg = $this->redis->xReadGroup('group', 'consumer', Array('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', Array($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($ids, 0, $n); + $this->assertEquals($this->redis->xDel('s', $todel), count($todel)); + } + + /* Empty array should fail */ + $this->assertFalse($this->redis->xDel('s', Array())); + } + + 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($trimmed, 100 - $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', 1, true) === 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 (Array(0, 100) as $min_idle_time) { + foreach (Array(false, true) as $justid) { + foreach (Array(0, 10) as $retrycount) { + /* We need to test not passing TIME/IDLE as well as passing either */ + if ($min_idle_time == 0) { + $topts = Array(Array(), Array('IDLE', 1000000), Array('TIME', time() * 1000)); + } else { + $topts = Array(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(Array('s'), 10, Array('group1' => 0)); + + /* Create a second stream we can FORCE ownership on */ + $fids = $this->addStreamsAndGroups(Array('f'), 10, Array('group1' => 0)); + $fids = $fids['f']; + + /* Have consumer 'Mike' read the messages */ + $oids = $this->redis->xReadGroup('group1', 'Mike', Array('s' => 0)); + $oids = array_keys($oids['s']); /* We're only dealing with stream 's' */ + + /* Construct our options array */ + $opts = Array(); + 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, Array()); + } + } + } + } + } + } + public function testSession_savedToRedis() { $this->setSessionHandler(); diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 461ac7dc9f..c2d682599c 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -65,7 +65,14 @@ public static function make_warning($str_msg) { } protected function assertFalse($bool) { - return $this->assertTrue(!$bool); + if(!$bool) + return true; + + $bt = debug_backtrace(false); + self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + + return false; } protected function assertTrue($bool) { @@ -99,6 +106,15 @@ protected function assertEquals($a, $b) { $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); } + protected function assertPatternMatch($str_test, $str_regex) { + if (preg_match($str_regex, $str_test)) + return; + + $bt = debug_backtrace(false); + self::$errors []= sprintf("Assertion failed ('%s' doesnt match '%s'): %s:%d (%s)\n", + $str_test, $str_regex, $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + } + protected function markTestSkipped($msg='') { $bt = debug_backtrace(false); self::$warnings []= sprintf("Skipped test: %s:%d (%s) %s\n", From 431b8d0317f571d63ba24380886576d1738d8489 Mon Sep 17 00:00:00 2001 From: nolimitdev Date: Wed, 12 Sep 2018 16:27:01 +0200 Subject: [PATCH 0993/1986] Update README.markdown Fix obsolete links and structure in first chapter of table of contents --- README.markdown | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.markdown b/README.markdown index 1bce4964c9..13e06176a0 100644 --- a/README.markdown +++ b/README.markdown @@ -13,10 +13,10 @@ You can send comments, patches, questions [here on github](https://github.com/ph ----- 1. [Installing/Configuring](#installingconfiguring) * [Installation](#installation) - * [Installation on OSX](#installation-on-osx) - * [Building on Windows](#building-on-windows) * [PHP Session handler](#php-session-handler) * [Distributed Redis Array](#distributed-redis-array) + * [Redis Cluster support](#redis-cluster-support) + * [Running the unit tests](#running-the-unit-tests) 1. [Classes and methods](#classes-and-methods) * [Usage](#usage) * [Connection](#connection) @@ -34,14 +34,14 @@ You can send comments, patches, questions [here on github](https://github.com/ph ----- -# Installation +# Installing/Configuring ----- +## Installation + For everything you should need to install PhpRedis on your system, see the [INSTALL.markdown](./INSTALL.markdown) page. -# Configuration - ## PHP Session handler phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions: From bedf3e8f682c819a29d11c9c5cfc094b0c1676b9 Mon Sep 17 00:00:00 2001 From: nolimitdev Date: Wed, 12 Sep 2018 16:51:10 +0200 Subject: [PATCH 0994/1986] Update README.markdown Fix incorrect syntax of php directives for session locking --- README.markdown | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index 13e06176a0..23c9b25757 100644 --- a/README.markdown +++ b/README.markdown @@ -66,14 +66,14 @@ phpredis can also connect to a unix domain socket: `session.save_path = "unix:// ### Session locking Following INI variables can be used to configure session locking: ~~~ -# Should the locking be enabled? Defaults to: 0. -redis.session.locking_enabled: 1 -# How long should the lock live (in seconds)? Defaults to: value of max_execution_time. -redis.session.lock_expire: 60 -# How long to wait between attempts to acquire lock, in microseconds (µs)?. Defaults to: 2000 -redis.session.lock_wait_time: 50000 -# Maximum number of times to retry (-1 means infinite). Defaults to: 10 -redis.session.lock_retries: 10 +; Should the locking be enabled? Defaults to: 0. +redis.session.locking_enabled = 1 +; How long should the lock live (in seconds)? Defaults to: value of max_execution_time. +redis.session.lock_expire = 60 +; How long to wait between attempts to acquire lock, in microseconds (µs)?. Defaults to: 2000 +redis.session.lock_wait_time = 50000 +; Maximum number of times to retry (-1 means infinite). Defaults to: 10 +redis.session.lock_retries = 10 ~~~ ## Distributed Redis Array From bfa61700010ffe5d556c476de4ead0377e2183c0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 1 Oct 2018 09:44:28 +0300 Subject: [PATCH 0995/1986] Fix scan-build warnings --- library.c | 2 +- redis_commands.c | 4 ++-- redis_session.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library.c b/library.c index 1d9f2ec479..9a1b7782e7 100644 --- a/library.c +++ b/library.c @@ -1332,7 +1332,7 @@ redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_strea { zval zv, *z_messages = &zv; int i, shdr, messages; - char *id; + char *id = NULL; int idlen; for (i = 0; i < count; i++) { diff --git a/redis_commands.c b/redis_commands.c index e9d26c3d2e..14fb56f258 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2793,7 +2793,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *unit; - short store_slot; + short store_slot = 0; strlen_t keylen, unitlen; int argc = 5, keyfree; double lng, lat, radius; @@ -2864,7 +2864,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s { char *key, *mem, *unit; strlen_t keylen, memlen, unitlen; - short store_slot; + short store_slot = 0; int keyfree, argc = 4; double radius; geoOptions gopts = {0}; diff --git a/redis_session.c b/redis_session.c index 7f1a26568a..51bebfd464 100644 --- a/redis_session.c +++ b/redis_session.c @@ -695,8 +695,8 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) char *cmd, *response; int cmd_len, response_len; - const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); - size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); + const char *skey = ZSTR_VAL(key); + size_t skeylen = ZSTR_LEN(key); if (!skeylen) return FAILURE; From 5b483b701ca00d2419c83eae3f7e34f822ed8c84 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Oct 2018 09:59:29 -0700 Subject: [PATCH 0996/1986] Commit accidentally missed no-op macro change --- common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index a1dbf28a23..d2fc98f990 100644 --- a/common.h +++ b/common.h @@ -436,8 +436,8 @@ typedef size_t strlen_t; #define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_FALSE) #define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)((char *)Z_OBJ_P(z) - XtOffsetOf(class_entry, std)) -#define REDIS_MAKE_STD_ZVAL(zv) do {} while(0) -#define REDIS_FREE_ZVAL(zv) do {} while(0) +#define REDIS_MAKE_STD_ZVAL(zv) PHPREDIS_NOTUSED +#define REDIS_FREE_ZVAL(zv) do PHPREDIS_NOTUSED #endif /* NULL check so Eclipse doesn't go crazy */ From 4ffd75425cc972342c7ce90d026af7f64f486ef5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Oct 2018 11:42:28 -0700 Subject: [PATCH 0997/1986] It's preferable to commit code that compiles --- common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.h b/common.h index d2fc98f990..9a92c72b37 100644 --- a/common.h +++ b/common.h @@ -437,7 +437,7 @@ typedef size_t strlen_t; #define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)((char *)Z_OBJ_P(z) - XtOffsetOf(class_entry, std)) #define REDIS_MAKE_STD_ZVAL(zv) PHPREDIS_NOTUSED -#define REDIS_FREE_ZVAL(zv) do PHPREDIS_NOTUSED +#define REDIS_FREE_ZVAL(zv) PHPREDIS_NOTUSED #endif /* NULL check so Eclipse doesn't go crazy */ From 1b7fe98ab7a9c5589d1511ff4cdece85058b8877 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Oct 2018 11:56:46 -0700 Subject: [PATCH 0998/1986] Undo breaking changes --- common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index 9a92c72b37..a1dbf28a23 100644 --- a/common.h +++ b/common.h @@ -436,8 +436,8 @@ typedef size_t strlen_t; #define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_FALSE) #define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)((char *)Z_OBJ_P(z) - XtOffsetOf(class_entry, std)) -#define REDIS_MAKE_STD_ZVAL(zv) PHPREDIS_NOTUSED -#define REDIS_FREE_ZVAL(zv) PHPREDIS_NOTUSED +#define REDIS_MAKE_STD_ZVAL(zv) do {} while(0) +#define REDIS_FREE_ZVAL(zv) do {} while(0) #endif /* NULL check so Eclipse doesn't go crazy */ From cd6ebc6d7f56ef652cb1dbe5e02126e95aa30156 Mon Sep 17 00:00:00 2001 From: Marc de Jonge Date: Mon, 8 Oct 2018 07:57:51 +0200 Subject: [PATCH 0999/1986] Reset the socket after a timeout to make sure no wrong data is received (#1417) * Reset the socket after a timeout to make sure no wrong data is received * Remove the lazy_connect completely * Missing TSRMLS_CC * Remove redundant check if the stream exists * Add the redis_sock_server_open to the CLUSTER_SEND_PAYLOAD macro --- cluster_library.c | 20 ++++++++------------ cluster_library.h | 9 +-------- common.h | 2 -- library.c | 1 - redis.c | 7 ++----- 5 files changed, 11 insertions(+), 28 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 5f710c5e92..224932bf19 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -270,10 +270,7 @@ static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len, { char buf[1024]; - /* Connect to the socket if we aren't yet */ - CLUSTER_LAZY_CONNECT(redis_sock); - - /* Send our command, validate the reply type, and consume the first line */ + /* Connect to the socket if we aren't yet and send our command, validate the reply type, and consume the first line */ if (!CLUSTER_SEND_PAYLOAD(redis_sock,cmd,cmd_len) || !CLUSTER_VALIDATE_REPLY_TYPE(redis_sock, type) || !php_stream_gets(redis_sock->stream, buf, sizeof(buf))) return -1; @@ -1088,7 +1085,6 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC) { ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) continue; redis_sock_disconnect(node->sock, force TSRMLS_CC); - node->sock->lazy_connect = 1; } ZEND_HASH_FOREACH_END(); } @@ -1124,7 +1120,9 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, if (!redis_sock) continue; /* Connect to this node if we haven't already */ - CLUSTER_LAZY_CONNECT(redis_sock); + if(redis_sock_server_open(redis_sock TSRMLS_CC)) { + continue; + } /* If we're not on the master, attempt to send the READONLY command to * this slave, and skip it if that fails */ @@ -1200,11 +1198,9 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, * at random. */ if (failover == REDIS_FAILOVER_NONE) { /* Success if we can send our payload to the master */ - CLUSTER_LAZY_CONNECT(redis_sock); if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz)) return 0; } else if (failover == REDIS_FAILOVER_ERROR) { /* Try the master, then fall back to any slaves we may have */ - CLUSTER_LAZY_CONNECT(redis_sock); if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz) || !cluster_dist_write(c, cmd, sz, 1 TSRMLS_CC)) return 0; } else { @@ -1225,10 +1221,7 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, /* Skip this node if it's the one that failed, or if it's a slave */ if (seed_node == NULL || seed_node->sock == redis_sock || seed_node->slave) continue; - /* Connect to this node if we haven't already */ - CLUSTER_LAZY_CONNECT(seed_node->sock); - - /* Attempt to write our request to this node */ + /* Connect to this node if we haven't already and attempt to write our request to this node */ if (CLUSTER_SEND_PAYLOAD(seed_node->sock, cmd, sz)) { c->cmd_slot = seed_node->slot; c->cmd_sock = seed_node->sock; @@ -1456,6 +1449,9 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char "The Redis Cluster is down (CLUSTERDOWN)", 0 TSRMLS_CC); return -1; } else if (timedout) { + // Make sure the socket is reconnected, it such that it is in a clean state + redis_sock_disconnect(c->cmd_sock, 1 TSRMLS_CC); + zend_throw_exception(redis_cluster_exception_ce, "Timed out attempting to find data in the correct node!", 0 TSRMLS_CC); } diff --git a/cluster_library.h b/cluster_library.h index 0145783257..979b228ddc 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -51,13 +51,6 @@ ZSTR_LEN(SLOT_SOCK(c,c->redir_slot)->host) != c->redir_host_len || \ memcmp(ZSTR_VAL(SLOT_SOCK(c,c->redir_slot)->host),c->redir_host,c->redir_host_len)) -/* Lazy connect logic */ -#define CLUSTER_LAZY_CONNECT(s) \ - if(s->lazy_connect) { \ - s->lazy_connect = 0; \ - redis_sock_server_open(s TSRMLS_CC); \ - } - /* Clear out our "last error" */ #define CLUSTER_CLEAR_ERROR(c) do { \ if (c->err) { \ @@ -69,7 +62,7 @@ /* Protected sending of data down the wire to a RedisSock->stream */ #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \ - (sock && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \ + (sock && !redis_sock_server_open(sock TSRMLS_CC) && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \ php_stream_write(sock->stream, buf, len)==len) /* Macro to read our reply type character */ diff --git a/common.h b/common.h index a1dbf28a23..0771cfd732 100644 --- a/common.h +++ b/common.h @@ -678,8 +678,6 @@ typedef struct { zend_string *err; - zend_bool lazy_connect; - int scan; int readonly; diff --git a/library.c b/library.c index 9a1b7782e7..8fecaef163 100644 --- a/library.c +++ b/library.c @@ -1687,7 +1687,6 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->dbNumber = 0; redis_sock->retry_interval = retry_interval * 1000; redis_sock->persistent = persistent; - redis_sock->lazy_connect = lazy_connect; redis_sock->persistent_id = NULL; if (persistent && persistent_id != NULL) { diff --git a/redis.c b/redis.c index e25b65d749..b8244718cf 100644 --- a/redis.c +++ b/redis.c @@ -647,11 +647,8 @@ redis_sock_get(zval *id TSRMLS_DC, int no_throw) return NULL; } - if (redis_sock->lazy_connect) { - redis_sock->lazy_connect = 0; - if (redis_sock_server_open(redis_sock TSRMLS_CC) < 0) { - return NULL; - } + if (redis_sock_server_open(redis_sock TSRMLS_CC) < 0) { + return NULL; } return redis_sock; From e26fdd43539aeadaa30e17be694dec029b08b275 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 11 Oct 2018 23:10:33 +0300 Subject: [PATCH 1000/1986] 4.2.0RC1 --- package.xml | 75 ++++++++++++++++++++++++++++++++++++++++------------- php_redis.h | 2 +- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/package.xml b/package.xml index 88bd62562b..34af5a5e32 100644 --- a/package.xml +++ b/package.xml @@ -27,30 +27,31 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2018-07-10 + 2018-10-11 - 4.1.0 - 4.1.0 + 4.2.0RC1 + 4.2.0RC1 - stable - stable + alpha + alpha PHP - phpredis 4.1.0 - - The primary new feature of this release is session locking functionality. Thanks to @SkydiveMarius! - - * Add callbacks validate_sid and update_timestamp to session handler [aaaf0f23] (@hongboliu) - * Call cluster_disconnect before destroying cluster object. [28ec4322] (Pavlo Yatsukhnenko) - * Bulk strings can be zero length. (Michael Grunder) - * Handle async parameter for flushDb and flushAll [beb6e8f3,acd10409,742cdd05] (Pavlo Yatsukhnenko) - * Split INSTALL and add more instructions [43613d9e,80d2a917] (@remicollet, Pavlo Yatsukhnenko) - * Only the first arg of connect and pconnect is required [063b5c1a] (@mathroc) - * Add session locking functionality [300c7251] (@SkydiveMarius, Michael Grunder, Pavlo Yatsukhnenko) - * Fix compression in RedisCluster [1aed74b4] (Pavlo Yatsukhnenko) - * Refactor geo* commands + documentation improvements (Michael Grunder) + phpredis 4.2.0RC1 + + The main feature of this release is new Streams API implemented by Michael Grunder. + + * Streams API [2c9e0572] (Michael Grunder) + * Reset the socket after a timeout to make sure no wrong data is received [cd6ebc6d] (@marcdejonge) + * Modify session testing logic [bfd27471] (Michael Grunder) + * Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex [d4a08697] (Michael Grunder) + * Fix printf format warnings [dcde9331] (Pavlo Yatsukhnenko) + * Session module is required [58bd8cc8] (@remicollet) + * Set default values for ini entries [e206ce9c] (Pavlo Yatsukhnenko) + * Display ini entries in output of phpinfo [908ac4b3] (Pavlo Yatsukhnenko) + * Persistant connections can be closed via close method + change reconnection logic [1d997873] (Pavlo Yatsukhnenko) + * Documentation improvements (@mg, @elcheco, @lucascourot, @nolimitdev, Michael Grunder) @@ -119,6 +120,44 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + alphaalpha + 4.2.0RC14.2.0RC1 + 2018-10-11 + + phpredis 4.2.0RC1 + + The main feature of this release is new Streams API implemented by Michael Grunder. + + * Streams API [2c9e0572] (Michael Grunder) + * Reset the socket after a timeout to make sure no wrong data is received [cd6ebc6d] (@marcdejonge) + * Modify session testing logic [bfd27471] (Michael Grunder) + * Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex [d4a08697] (Michael Grunder) + * Fix printf format warnings [dcde9331] (Pavlo Yatsukhnenko) + * Session module is required [58bd8cc8] (@remicollet) + * Set default values for ini entries [e206ce9c] (Pavlo Yatsukhnenko) + * Display ini entries in output of phpinfo [908ac4b3] (Pavlo Yatsukhnenko) + * Persistant connections can be closed via close method + change reconnection logic [1d997873] (Pavlo Yatsukhnenko) + * Documentation improvements (@mg, @elcheco, @lucascourot, @nolimitdev, Michael Grunder) + + + + + stablestable + 4.1.14.1.1 + 2018-08-01 + + phpredis 4.1.1 + + This release contains only bugfixes and documentation improvements + + * Fix arginfo for Redis::set method [0c5e7f4d] (Pavlo Yatsukhnenko) + * Fix compression in RedisCluster [a53e1a34] (Pavlo Yatsukhnenko) + * Fix TravisCI builds [9bf32d30] (@jrchamp) + * Highlight php codes in documentation [c3b023b0] (@ackintosh) + + + stablestable 4.1.04.1.0 diff --git a/php_redis.h b/php_redis.h index d85c13ffb7..88dd1121cf 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "develop" +#define PHP_REDIS_VERSION "4.2.0RC1" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 0af2a7fe06eb4e6c3140fb2636fb1caaf78e96b3 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 12 Oct 2018 08:02:51 +0200 Subject: [PATCH 1001/1986] missing space between command and args --- tests/RedisTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index df650ee595..28faafa844 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -6109,6 +6109,7 @@ private function getPhpCommand($script) $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: (defined('PHP_BINARY') ? PHP_BINARY : 'php')); // PHP_BINARY is 5.4+ 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 */ From 2e412373c47a574a1215756e79e6512a46e30cdc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 12 Oct 2018 08:36:48 -0700 Subject: [PATCH 1002/1986] Use a ZSET insted of SET for EVAL tests Redis SET type is unordered which could break our eval tests so use a ZSET instead where the order is deterministic. --- tests/RedisTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 28faafa844..e353829d7f 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4508,10 +4508,10 @@ public function testEval() { $this->redis->rpush('{eval-key}-list', 'c'); // Make a set - $this->redis->del('{eval-key}-set'); - $this->redis->sadd('{eval-key}-set', 'd'); - $this->redis->sadd('{eval-key}-set', 'e'); - $this->redis->sadd('{eval-key}-set', 'f'); + $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'); @@ -4521,9 +4521,9 @@ public function testEval() { $list = $this->redis->eval("return redis.call('lrange', KEYS[1], 0, -1)", Array('{eval-key}-list'), 1); $this->assertTrue($list === Array('a','b','c')); - // Use a script to return our set - $set = $this->redis->eval("return redis.call('smembers', KEYS[1])", Array('{eval-key}-set'), 1); - $this->assertTrue($set == Array('d','e','f')); + // Use a script to return our zset + $zset = $this->redis->eval("return redis.call('zrange', KEYS[1], 0, -1)", Array('{eval-key}-zset'), 1); + $this->assertTrue($zset == Array('d','e','f')); // Test an empty MULTI BULK response $this->redis->del('{eval-key}-nolist'); @@ -4539,7 +4539,7 @@ public function testEval() { redis.call('get', '{eval-key}-str2'), redis.call('lrange', 'not-any-kind-of-list', 0, -1), { - redis.call('smembers','{eval-key}-set'), + redis.call('zrange','{eval-key}-zset', 0, -1), redis.call('lrange', '{eval-key}-list', 0, -1) } } @@ -4559,7 +4559,7 @@ public function testEval() { ); // Now run our script, and check our values against each other - $eval_result = $this->redis->eval($nested_script, Array('{eval-key}-str1', '{eval-key}-str2', '{eval-key}-set', '{eval-key}-list'), 4); + $eval_result = $this->redis->eval($nested_script, Array('{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); /* From bc9b55975abb459ae41a626f11c985ef840fa4a3 Mon Sep 17 00:00:00 2001 From: twosee Date: Sat, 13 Oct 2018 10:37:59 +0800 Subject: [PATCH 1003/1986] Remove useless ZEND_ACC_[C|D]TOR. #ref: https://github.com/php/php-src/commit/8939c4d96b8382abe84f35e69f4f6ebd6f0f749d#r30609734 --- redis.c | 4 ++-- redis_cluster.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index b8244718cf..b178924cf5 100644 --- a/redis.c +++ b/redis.c @@ -237,8 +237,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2) ZEND_END_ARG_INFO() static zend_function_entry redis_functions[] = { - PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC) + PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index 130b961a4c..38ee47e40e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -107,7 +107,7 @@ ZEND_END_ARG_INFO() /* Function table */ zend_function_entry redis_cluster_functions[] = { - PHP_ME(RedisCluster, __construct, arginfo_ctor, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, __construct, arginfo_ctor, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _masters, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _prefix, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, _redir, arginfo_void, ZEND_ACC_PUBLIC) From 8b0f28cdce08cd170a490ffc72d793ae4fe4ffc0 Mon Sep 17 00:00:00 2001 From: twosee Date: Sat, 13 Oct 2018 12:59:35 +0800 Subject: [PATCH 1004/1986] Fix warning. --- library.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index 8fecaef163..644a0d4c03 100644 --- a/library.c +++ b/library.c @@ -2462,9 +2462,8 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s TSRMLS_CC) < 0) { zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, - "protocol error, couldn't parse MULTI-BULK response\n", - reply_type); - return -1; + "protocol error, couldn't parse MULTI-BULK response\n"); + return FAILURE; } // Switch on our reply-type byte From 27df92208a6a7c712ae08407a430059ab8b0d1ad Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 14 Oct 2018 11:50:47 -0700 Subject: [PATCH 1005/1986] Treat a -1 response from cluster_check_response as a timeout. When cluster_check_response returns -1 this can be treated as a timeout. Note that there is one non-timout condition which can cause a -1 response, but that is a corrupted MOVE/ASK reply which can *probably* be treated as a timeout as well, because it means that something has gone horribly wrong with the connection. Addresses #1425 --- cluster_library.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 224932bf19..4c785703b5 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1439,8 +1439,9 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char } } - /* Figure out if we've timed out trying to read or write the data */ - timedout = resp && c->waitms ? mstime() - msstart >= c->waitms : 0; + /* We're timed out if cluster_check_response returned -1, or if the + * response was non-zero and we've been in the loop too long */ + timedout = resp == -1 || (resp && c->waitms ? mstime() - msstart >= c->waitms : 0); } while (resp != 0 && !c->clusterdown && !timedout); // If we've detected the cluster is down, throw an exception From 7e3362c7ff646177c4f423b697ee088b6f4b3ba6 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Mon, 15 Oct 2018 10:24:32 +0200 Subject: [PATCH 1006/1986] PHPREDIS-1422: Added hint for session locking support --- README.markdown | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.markdown b/README.markdown index 23c9b25757..5dbea00f78 100644 --- a/README.markdown +++ b/README.markdown @@ -64,6 +64,10 @@ The session handler requires a version of Redis with the `SETEX` command (at lea phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0`. ### Session locking + +**Support**: Locking feature is currently only supported for Redis setup with single master instance (e.g. classic master/slave Sentinel environment). +So locking may not work properly in RedisArray or RedisCluster environments. + Following INI variables can be used to configure session locking: ~~~ ; Should the locking be enabled? Defaults to: 0. From 07ef7f4e6599e42a5d92f6a1bca5e45290dd745c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 16 Oct 2018 21:31:52 -0700 Subject: [PATCH 1007/1986] Make our timeout or response error handling more explicit. Although a -1 return value from cluster_check_response is likely a timeout, it is not the only possibility, so handle the loop timeout and error response in distinct ways. --- cluster_library.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 4c785703b5..2d0cc996b0 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1419,8 +1419,11 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char return -1; } - /* Now check the response from the node we queried. */ + /* Check response and short-circuit on success or communication error */ resp = cluster_check_response(c, &c->reply_type TSRMLS_CC); + if (resp == 0 || resp == -1) { + break; + } /* Handle MOVED or ASKING redirection */ if (resp == 1) { @@ -1439,22 +1442,28 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char } } - /* We're timed out if cluster_check_response returned -1, or if the - * response was non-zero and we've been in the loop too long */ - timedout = resp == -1 || (resp && c->waitms ? mstime() - msstart >= c->waitms : 0); - } while (resp != 0 && !c->clusterdown && !timedout); + /* See if we've timed out in the command loop */ + timedout = c->waitms ? mstime() - msstart >= c->waitms : 0; + } while (!c->clusterdown && !timedout); // If we've detected the cluster is down, throw an exception if (c->clusterdown) { zend_throw_exception(redis_cluster_exception_ce, "The Redis Cluster is down (CLUSTERDOWN)", 0 TSRMLS_CC); return -1; - } else if (timedout) { + } else if (timedout || resp == -1) { // Make sure the socket is reconnected, it such that it is in a clean state redis_sock_disconnect(c->cmd_sock, 1 TSRMLS_CC); - zend_throw_exception(redis_cluster_exception_ce, - "Timed out attempting to find data in the correct node!", 0 TSRMLS_CC); + if (timedout) { + zend_throw_exception(redis_cluster_exception_ce, + "Timed out attempting to find data in the correct node!", 0 TSRMLS_CC); + } else { + zend_throw_exception(redis_cluster_exception_ce, + "Error processing response from Redis node!", 0 TSRMLS_CC); + } + + return -1; } /* Clear redirection flag */ From d11724260f6d50bbadb1c9e76c89b010d88f7178 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Oct 2018 05:14:02 -0700 Subject: [PATCH 1008/1986] Simplify short-circuit logic --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 2d0cc996b0..2159a2e6af 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1421,7 +1421,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char /* Check response and short-circuit on success or communication error */ resp = cluster_check_response(c, &c->reply_type TSRMLS_CC); - if (resp == 0 || resp == -1) { + if (resp <= 0) { break; } From 0b97ec3739d99f0778ff827cb58c011b92d27a74 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 18 Oct 2018 09:47:10 -0700 Subject: [PATCH 1009/1986] Update STREAM API to handle STATUS -> BULK reply change Right before Redis 5.0 was released, the api was changed to send message ids as BULK instead of STATUS replies. --- library.c | 35 ++++++++++++++++++++++------------- redis.c | 2 +- redis_cluster.c | 2 +- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/library.c b/library.c index 8fecaef163..d5752f0dad 100644 --- a/library.c +++ b/library.c @@ -1269,17 +1269,18 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret { zval zv, *z_message = &zv; int i, mhdr, fields; - char id[1024]; - size_t idlen; + char *id = NULL; + int idlen; /* Iterate over each message */ for (i = 0; i < count; i++) { /* Consume inner multi-bulk header, message ID itself and finaly * the multi-bulk header for field and values */ if ((read_mbulk_header(redis_sock, &mhdr TSRMLS_CC) < 0 || mhdr != 2) || - redis_sock_read_single_line(redis_sock, id, sizeof(id), &idlen, 0 TSRMLS_CC) < 0 || + ((id = redis_sock_read(redis_sock, &idlen TSRMLS_CC)) == NULL) || (read_mbulk_header(redis_sock, &fields TSRMLS_CC) < 0 || fields % 2 != 0)) { + if (id) efree(id); return -1; } @@ -1289,6 +1290,7 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret redis_mbulk_reply_loop(redis_sock, z_message, fields, UNSERIALIZE_VALS TSRMLS_CC); array_zip_values_and_scores(redis_sock, z_message, SCORE_DECODE_NONE TSRMLS_CC); add_assoc_zval_ex(z_ret, id, idlen, z_message); + efree(id); } return 0; @@ -1404,24 +1406,30 @@ PHP_REDIS_API int redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) { zval zv, *z_msg = &zv; REDIS_REPLY_TYPE type; - char id[1024]; - int i, fields; + char *id; + int i, fields, idlen; long li; - size_t idlen; for (i = 0; i < count; i++) { /* Consume inner reply type */ if (redis_read_reply_type(redis_sock, &type, &li TSRMLS_CC) < 0 || - (type != TYPE_LINE && type != TYPE_MULTIBULK)) return -1; + (type != TYPE_BULK && type != TYPE_MULTIBULK) || + (type == TYPE_BULK && li <= 0)) return -1; - if (type == TYPE_LINE) { - /* JUSTID variant */ - if (redis_sock_gets(redis_sock, id, sizeof(id), &idlen TSRMLS_CC) < 0) + /* TYPE_BULK is the JUSTID variant, otherwise it's standard xclaim response */ + if (type == TYPE_BULK) { + if ((id = redis_sock_read_bulk_reply(redis_sock, (size_t)li TSRMLS_CC)) == NULL) return -1; - add_next_index_stringl(rv, id, idlen); + + add_next_index_stringl(rv, id, li); + efree(id); } else { - if (li != 2 || redis_sock_read_single_line(redis_sock, id, sizeof(id), &idlen, 0 TSRMLS_CC) < 0 || - (read_mbulk_header(redis_sock, &fields TSRMLS_CC) < 0 || fields % 2 != 0)) return -1; + if ((li != 2 || (id = redis_sock_read(redis_sock, &idlen TSRMLS_CC)) == NULL) || + (read_mbulk_header(redis_sock, &fields TSRMLS_CC) < 0 || fields % 2 != 0)) + { + if (id) efree(id); + return -1; + } REDIS_MAKE_STD_ZVAL(z_msg); array_init(z_msg); @@ -1429,6 +1437,7 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) redis_mbulk_reply_loop(redis_sock, z_msg, fields, UNSERIALIZE_VALS TSRMLS_CC); array_zip_values_and_scores(redis_sock, z_msg, SCORE_DECODE_NONE TSRMLS_CC); add_assoc_zval_ex(rv, id, idlen, z_msg); + efree(id); } } diff --git a/redis.c b/redis.c index b8244718cf..2718997cde 100644 --- a/redis.c +++ b/redis.c @@ -3592,7 +3592,7 @@ PHP_METHOD(Redis, xack) { } PHP_METHOD(Redis, xadd) { - REDIS_PROCESS_CMD(xadd, redis_single_line_reply); + REDIS_PROCESS_CMD(xadd, redis_read_variant_reply); } PHP_METHOD(Redis, xclaim) { diff --git a/redis_cluster.c b/redis_cluster.c index 130b961a4c..8f10bcb06e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2983,7 +2983,7 @@ PHP_METHOD(RedisCluster, xack) { /* {{{ proto string RedisCluster::xadd(string key, string id, array field_values) }}} */ PHP_METHOD(RedisCluster, xadd) { - CLUSTER_PROCESS_CMD(xadd, cluster_single_line_resp, 0); + CLUSTER_PROCESS_CMD(xadd, cluster_bulk_raw_resp, 0); } /* {{{ proto array RedisCluster::xclaim(string key, string group, string consumer, From 25b043ce16d01977e73cabdaa0d3a7948a764ec5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 23 Oct 2018 09:01:18 +0300 Subject: [PATCH 1010/1986] Issue #1434 Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` --- redis.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index 2718997cde..f8290750db 100644 --- a/redis.c +++ b/redis.c @@ -93,6 +93,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0) ZEND_ARG_INFO(0, option) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_multi, 0, 0, 0) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1) ZEND_ARG_INFO(0, cmd) #if PHP_VERSION_ID >= 50600 @@ -330,7 +334,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, move, arginfo_move, ZEND_ACC_PUBLIC) PHP_ME(Redis, mset, arginfo_pairs, ZEND_ACC_PUBLIC) PHP_ME(Redis, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC) - PHP_ME(Redis, multi, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(Redis, multi, arginfo_multi, ZEND_ACC_PUBLIC) PHP_ME(Redis, object, arginfo_object, ZEND_ACC_PUBLIC) PHP_ME(Redis, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) PHP_ME(Redis, persist, arginfo_key, ZEND_ACC_PUBLIC) @@ -368,7 +372,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC) PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sRemove, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, sSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, sUnion, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sUnionStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) @@ -454,7 +458,7 @@ static zend_function_entry redis_functions[] = { PHP_MALIAS(Redis, scard, sSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sismember, sContains, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, srem, sRemove, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, srem, sRemove, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zRem, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC) From c886366c6f923251cac189178879f1a3440d9800 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 26 Oct 2018 11:11:37 +0300 Subject: [PATCH 1011/1986] 4.2.0RC2 --- package.xml | 30 ++++++++++++++++++++---------- php_redis.h | 2 +- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/package.xml b/package.xml index 34af5a5e32..c88fabba84 100644 --- a/package.xml +++ b/package.xml @@ -27,22 +27,27 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2018-10-11 + 2018-10-26 - 4.2.0RC1 - 4.2.0RC1 + 4.2.0RC2 + 4.2.0RC2 - alpha - alpha + beta + beta PHP - phpredis 4.2.0RC1 + phpredis 4.2.0RC2 The main feature of this release is new Streams API implemented by Michael Grunder. * Streams API [2c9e0572] (Michael Grunder) + * Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce] (Pavlo Yatsukhnenko) + * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) + * Treat a -1 response from cluster_check_response as a timeout. [27df9220, 07ef7f4e, d1172426] (Michael Grunder) + * Use a ZSET insted of SET for EVAL tests [2e412373] (Michael Grunder) + * Missing space between command and args [0af2a7fe] (@remicollet) * Reset the socket after a timeout to make sure no wrong data is received [cd6ebc6d] (@marcdejonge) * Modify session testing logic [bfd27471] (Michael Grunder) * Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex [d4a08697] (Michael Grunder) @@ -121,15 +126,20 @@ http://pear.php.net/dtd/package-2.0.xsd"> - alphaalpha - 4.2.0RC14.2.0RC1 - 2018-10-11 + betabeta + 4.2.0RC24.2.0RC2 + 2018-10-26 - phpredis 4.2.0RC1 + phpredis 4.2.0RC2 The main feature of this release is new Streams API implemented by Michael Grunder. * Streams API [2c9e0572] (Michael Grunder) + * Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce] (Pavlo Yatsukhnenko) + * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) + * Treat a -1 response from cluster_check_response as a timeout. [27df9220, 07ef7f4e, d1172426] (Michael Grunder) + * Use a ZSET insted of SET for EVAL tests [2e412373] (Michael Grunder) + * Missing space between command and args [0af2a7fe] (@remicollet) * Reset the socket after a timeout to make sure no wrong data is received [cd6ebc6d] (@marcdejonge) * Modify session testing logic [bfd27471] (Michael Grunder) * Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex [d4a08697] (Michael Grunder) diff --git a/php_redis.h b/php_redis.h index 88dd1121cf..ed880d0db5 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "4.2.0RC1" +#define PHP_REDIS_VERSION "4.2.0RC2" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 51027044f74598b7e5604ad95b005d1e89e28347 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 26 Oct 2018 12:30:03 +0200 Subject: [PATCH 1012/1986] fix some build warnings --- library.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index d5752f0dad..20da9524fb 100644 --- a/library.c +++ b/library.c @@ -2113,10 +2113,13 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC) { - char *buf, *data; + char *buf; int valfree; strlen_t len; +#ifdef HAVE_REDIS_LZF + char *data; uint32_t res; +#endif valfree = redis_serialize(redis_sock, z, &buf, &len TSRMLS_CC); switch (redis_sock->compression) { @@ -2142,9 +2145,11 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_ PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC) { +#ifdef HAVE_REDIS_LZF char *data; int i; uint32_t res; +#endif switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: From b5093910b94a2795088a8417946b294f9e780087 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 26 Oct 2018 14:25:09 +0300 Subject: [PATCH 1013/1986] Fix warning: 'id' may be used uninitialized in this function --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 0bb7f2c09b..a6c93f9d88 100644 --- a/library.c +++ b/library.c @@ -1406,7 +1406,7 @@ PHP_REDIS_API int redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) { zval zv, *z_msg = &zv; REDIS_REPLY_TYPE type; - char *id; + char *id = NULL; int i, fields, idlen; long li; From f21b3a18bad997dc469ddc366cee83bcb023ec4e Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Sat, 27 Oct 2018 12:51:17 +0100 Subject: [PATCH 1014/1986] Test on PHP 5.3 too --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0953a77d5f..a344efe04a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ php: - 7.0 - 7.1 - 7.2 + - 7.3 - nightly env: CC=gcc matrix: @@ -28,6 +29,8 @@ matrix: env: CC=clang - php: 7.2 env: CC=clang + - php: 7.3 + env: CC=clang addons: apt: packages: clang From 18dc2aacd6ab25c2aaf92a43d32d8eaf4235dcc0 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Tue, 6 Nov 2018 08:46:02 -0800 Subject: [PATCH 1015/1986] 32bit xclaim fix (#1444) This should fix the XCLAIM issue on 32-bit PHP installs. This change will allow the user to pass the XCLAIM TIME option pretty much any way they want (string, long, or float) and it should work. Note that in 32-bit PHP they will only be able to pass exact values <= 2^53 as PHP will use a double precision floating point for integer overflows. --- common.h | 2 ++ library.c | 16 +++++++++---- library.h | 1 + redis_commands.c | 59 ++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/common.h b/common.h index 0771cfd732..adbe3035dd 100644 --- a/common.h +++ b/common.h @@ -461,8 +461,10 @@ typedef size_t strlen_t; #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) +#define phpredis_atoi64(p) _atoi64((p)) #else #define PHP_REDIS_API +#define phpredis_atoi64(p) atoll((p)) #endif /* reply types */ diff --git a/library.c b/library.c index a6c93f9d88..00d2f7abc4 100644 --- a/library.c +++ b/library.c @@ -698,6 +698,15 @@ int redis_cmd_append_sstr_long(smart_string *str, long append) { return redis_cmd_append_sstr(str, long_buf, long_len); } +/* + * Append a 64-bit integer to our command + */ +int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) { + char nbuf[64]; + int len = snprintf(nbuf, sizeof(nbuf), "%lld", append); + return redis_cmd_append_sstr(str, nbuf, len); +} + /* * Append a double to a smart string command */ @@ -1080,11 +1089,8 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, } if(response[0] == ':') { -#ifdef PHP_WIN32 - __int64 ret = _atoi64(response + 1); -#else - long long ret = atoll(response + 1); -#endif + int64_t ret = phpredis_atoi64(response + 1); + if (IS_ATOMIC(redis_sock)) { if(ret > LONG_MAX) { /* overflow */ RETVAL_STRINGL(response + 1, response_len - 1); diff --git a/library.h b/library.h index 1c936dd5d2..80b12d0383 100644 --- a/library.h +++ b/library.h @@ -18,6 +18,7 @@ int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyw int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); int redis_cmd_append_sstr_int(smart_string *str, int append); int redis_cmd_append_sstr_long(smart_string *str, long append); +int redis_cmd_append_sstr_i64(smart_string *str, int64_t append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC); int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot); diff --git a/redis_commands.c b/redis_commands.c index 14fb56f258..1ef216d404 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3525,13 +3525,56 @@ int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, typedef struct xclaimOptions { struct { char *type; - zend_long time; + int64_t time; } idle; zend_long retrycount; int force; int justid; } xclaimOptions; +/* Attempt to extract an int64_t from the provided zval */ +static int zval_get_i64(zval *zv, int64_t *retval) { + if (Z_TYPE_P(zv) == IS_LONG) { + *retval = (int64_t)Z_LVAL_P(zv); + return SUCCESS; + } else if (Z_TYPE_P(zv) == IS_DOUBLE) { + *retval = (int64_t)Z_DVAL_P(zv); + return SUCCESS; + } else if (Z_TYPE_P(zv) == IS_STRING) { + zend_long lval; + double dval; + + switch (is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &lval, &dval, 1)) { + case IS_LONG: + *retval = (int64_t)lval; + return SUCCESS; + case IS_DOUBLE: + *retval = (int64_t)dval; + return SUCCESS; + } + } + + /* If we make it here we have failed */ + return FAILURE; +} + +/* Helper function to get an integer XCLAIM argument. This can overflow a + * 32-bit PHP long so we have to extract it as an int64_t. If the value is + * not a valid number or negative, we'll inform the user of the problem and + * that the argument is being ignored. */ +static int64_t get_xclaim_i64_arg(const char *key, zval *zv TSRMLS_DC) { + int64_t retval = -1; + + /* Extract an i64, and if we can't let the user know there is an issue. */ + if (zval_get_i64(zv, &retval) == FAILURE || retval < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Invalid XCLAIM option '%s' will be ignored", key); + } + + /* Success */ + return retval; +} + /* Helper to extract XCLAIM options */ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt TSRMLS_DC) { HashTable *ht; @@ -3556,23 +3599,19 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt TSRMLS_DC) { ht = Z_ARRVAL_P(z_arr); ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, zv) { if (zkey) { - /* Every key => value xclaim option requires a long and Redis - * treats -1 as not being passed so skip negative values too. */ - if (Z_TYPE_P(zv) != IS_LONG || Z_LVAL_P(zv) < 0) - continue; - kval = ZSTR_VAL(zkey); klen = ZSTR_LEN(zkey); + if (klen == 4) { if (!strncasecmp(kval, "TIME", 4)) { opt->idle.type = "TIME"; - opt->idle.time = Z_LVAL_P(zv); + opt->idle.time = get_xclaim_i64_arg("TIME", zv TSRMLS_CC); } else if (!strncasecmp(kval, "IDLE", 4)) { opt->idle.type = "IDLE"; - opt->idle.time = Z_LVAL_P(zv); + opt->idle.time = get_xclaim_i64_arg("IDLE", zv TSRMLS_CC); } } else if (klen == 10 && !strncasecmp(kval, "RETRYCOUNT", 10)) { - opt->retrycount = Z_LVAL_P(zv); + opt->retrycount = zval_get_long(zv); } } else { if (Z_TYPE_P(zv) == IS_STRING) { @@ -3609,7 +3648,7 @@ static void append_xclaim_options(smart_string *cmd, xclaimOptions *opt) { /* IDLE/TIME long */ if (opt->idle.type != NULL && opt->idle.time != -1) { redis_cmd_append_sstr(cmd, opt->idle.type, strlen(opt->idle.type)); - redis_cmd_append_sstr_long(cmd, opt->idle.time); + redis_cmd_append_sstr_i64(cmd, opt->idle.time); } /* RETRYCOUNT */ From eb8bcc1de86240fd9b7094994065fbf1975beabe Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 6 Nov 2018 09:00:55 -0800 Subject: [PATCH 1016/1986] Comment fix: No longer always success --- redis_commands.c | 1 - 1 file changed, 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 1ef216d404..7548d7423d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3571,7 +3571,6 @@ static int64_t get_xclaim_i64_arg(const char *key, zval *zv TSRMLS_DC) { "Invalid XCLAIM option '%s' will be ignored", key); } - /* Success */ return retval; } From 92f14b1480b73333db595821f8335ea5ee300113 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Tue, 6 Nov 2018 21:20:06 -0800 Subject: [PATCH 1017/1986] Fix a memory leak when regenerating IDs (#1445) Make sure we free any existing session lock key if we're going to update it. Otherwise we will leak that memory. --- redis_session.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/redis_session.c b/redis_session.c index 51bebfd464..7922507152 100644 --- a/redis_session.c +++ b/redis_session.c @@ -616,6 +616,7 @@ PS_CREATE_SID_FUNC(redis) #endif } + if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); #if (PHP_MAJOR_VERSION < 7) pool->lock_status.session_key = redis_session_key(rpm, sid, strlen(sid)); #else @@ -758,6 +759,7 @@ PS_READ_FUNC(redis) } /* send GET command */ + if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); pool->lock_status.session_key = redis_session_key(rpm, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); From 071a1d547682b927b8f658d7c1a727e6dd9286b5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 7 Nov 2018 10:17:09 +0200 Subject: [PATCH 1018/1986] Fix memory leak when aquiring lock --- redis_session.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/redis_session.c b/redis_session.c index 7922507152..71b66b5b98 100644 --- a/redis_session.c +++ b/redis_session.c @@ -275,6 +275,7 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s } /* Generate our qualified lock key */ + if (lock_status->lock_key) zend_string_release(lock_status->lock_key); lock_status->lock_key = zend_string_alloc(ZSTR_LEN(lock_status->session_key) + sizeof(suffix) - 1, 0); memcpy(ZSTR_VAL(lock_status->lock_key), ZSTR_VAL(lock_status->session_key), ZSTR_LEN(lock_status->session_key)); memcpy(ZSTR_VAL(lock_status->lock_key) + ZSTR_LEN(lock_status->session_key), suffix, sizeof(suffix) - 1); @@ -283,6 +284,7 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s gethostname(hostname, HOST_NAME_MAX); size_t hostname_len = strlen(hostname); size_t pid_len = snprintf(pid, sizeof(pid), "|%ld", (long)getpid()); + if (lock_status->lock_secret) zend_string_release(lock_status->lock_secret); lock_status->lock_secret = zend_string_alloc(hostname_len + pid_len, 0); memcpy(ZSTR_VAL(lock_status->lock_secret), hostname, hostname_len); memcpy(ZSTR_VAL(lock_status->lock_secret) + hostname_len, pid, pid_len); From 6f7ddd275a008ed0beb995d3f9f80756af02a370 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 7 Nov 2018 14:03:35 +0200 Subject: [PATCH 1019/1986] Fix coverity scan warnings --- redis_array_impl.c | 6 +++++- redis_session.c | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 3aca8c7611..16d8b17c17 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -77,7 +77,11 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b if (!b_lazy_connect) { /* connect */ - redis_sock_server_open(redis->sock TSRMLS_CC); + if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { + zval_dtor(&z_cons); + ra->count = ++i; + return NULL; + } } ra->count = ++i; diff --git a/redis_session.c b/redis_session.c index 71b66b5b98..bb62ae8eae 100644 --- a/redis_session.c +++ b/redis_session.c @@ -209,7 +209,9 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { if (rpm->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } - redis_sock_server_open(rpm->redis_sock TSRMLS_CC); + if (redis_sock_server_open(rpm->redis_sock TSRMLS_CC) < 0) { + continue; + } if (needs_auth) { redis_pool_member_auth(rpm TSRMLS_CC); } From 4e2de1581f93bfaa790eea91afcbf2fca6324037 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 7 Nov 2018 17:48:58 +0200 Subject: [PATCH 1020/1986] Fix redis_session Prevent infinite loop when redis_sock_server_open failed. Check pool->lock_status.session_key is not NULL in PS_CLOSE_FUNC. --- redis_session.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/redis_session.c b/redis_session.c index bb62ae8eae..6369e134d5 100644 --- a/redis_session.c +++ b/redis_session.c @@ -209,17 +209,16 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { if (rpm->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } - if (redis_sock_server_open(rpm->redis_sock TSRMLS_CC) < 0) { - continue; - } - if (needs_auth) { - redis_pool_member_auth(rpm TSRMLS_CC); - } - if (rpm->database >= 0) { /* default is -1 which leaves the choice to redis. */ - redis_pool_member_select(rpm TSRMLS_CC); - } + if (redis_sock_server_open(rpm->redis_sock TSRMLS_CC) == 0) { + if (needs_auth) { + redis_pool_member_auth(rpm TSRMLS_CC); + } + if (rpm->database >= 0) { /* default is -1 which leaves the choice to redis. */ + redis_pool_member_select(rpm TSRMLS_CC); + } - return rpm; + return rpm; + } } i += rpm->weight; rpm = rpm->next; @@ -546,11 +545,13 @@ PS_CLOSE_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); if (pool) { - redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(pool->lock_status.session_key) TSRMLS_CC); + if (pool->lock_status.session_key) { + redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(pool->lock_status.session_key) TSRMLS_CC); - RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; - if (redis_sock) { - lock_release(redis_sock, &pool->lock_status TSRMLS_CC); + RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; + if (redis_sock) { + lock_release(redis_sock, &pool->lock_status TSRMLS_CC); + } } redis_pool_free(pool TSRMLS_CC); From 86ecbb7a21947c27ae5b874dcdc60f76c705e020 Mon Sep 17 00:00:00 2001 From: fanjiapeng Date: Thu, 8 Nov 2018 11:19:25 +0800 Subject: [PATCH 1021/1986] Fix function close() return value --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index f8290750db..5862eecf8b 100644 --- a/redis.c +++ b/redis.c @@ -1012,7 +1012,7 @@ PHP_METHOD(Redis, close) { RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (redis_sock && redis_sock_disconnect(redis_sock, 1 TSRMLS_CC)) { + if (redis_sock && redis_sock_disconnect(redis_sock, 1 TSRMLS_CC) == SUCCESS) { RETURN_TRUE; } RETURN_FALSE; From 2a1ef961a2fd6551798c63deceeada257fdac575 Mon Sep 17 00:00:00 2001 From: fanjiapeng Date: Thu, 8 Nov 2018 15:18:46 +0800 Subject: [PATCH 1022/1986] Close() Optimization --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 5862eecf8b..5242d97c50 100644 --- a/redis.c +++ b/redis.c @@ -1012,7 +1012,7 @@ PHP_METHOD(Redis, close) { RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (redis_sock && redis_sock_disconnect(redis_sock, 1 TSRMLS_CC) == SUCCESS) { + if (redis_sock_disconnect(redis_sock, 1 TSRMLS_CC) == SUCCESS) { RETURN_TRUE; } RETURN_FALSE; From 517de7d12208831351aa35853ee7343f9de6c322 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 8 Nov 2018 12:43:55 -0800 Subject: [PATCH 1023/1986] 4.2.0RC3 --- package.xml | 64 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/package.xml b/package.xml index c88fabba84..749f84265b 100644 --- a/package.xml +++ b/package.xml @@ -9,12 +9,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> This extension provides an API for communicating with Redis servers. - - Nicolas Favre-Felix - nff - n.favrefelix@gmail.com - yes - Michael Grunder mgrunder @@ -27,10 +21,16 @@ http://pear.php.net/dtd/package-2.0.xsd"> p.yatsukhnenko@gmail.com yes - 2018-10-26 + + Nicolas Favre-Felix + nff + n.favrefelix@gmail.com + yes + + 2018-11-08 - 4.2.0RC2 - 4.2.0RC2 + 4.2.0RC3 + 4.2.0RC3 beta @@ -38,16 +38,30 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - phpredis 4.2.0RC2 + phpredis 4.2.0RC3 The main feature of this release is new Streams API implemented by Michael Grunder. - * Streams API [2c9e0572] (Michael Grunder) + 4.2.0RC3: + + * Optimize close method [2a1ef961] (fanjiapeng) + * Prevent potential infinite loop for sessions [4e2de158] (Pavlo Yatsukhnenko) + * Fix coverty warnings [6f7ddd27] (Pavlo Yatsukhnenko) + * Fix session memory leaks [071a1d54, 92f14b14] (Pavlo Yatsukhnenko, Michael Grunder) + * Fix XCLAIM on 32-bit installs [18dc2aac] (Michael Grunder) + * Build warning fixes [b5093910, 51027044, 8b0f28cd] (Pavlo Yatsukhnenko, Remi Collet, twosee) + + 4.2.0RC2: + * Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce] (Pavlo Yatsukhnenko) * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) * Treat a -1 response from cluster_check_response as a timeout. [27df9220, 07ef7f4e, d1172426] (Michael Grunder) * Use a ZSET insted of SET for EVAL tests [2e412373] (Michael Grunder) * Missing space between command and args [0af2a7fe] (@remicollet) + + 4.2.0RC1: + + * Streams API [2c9e0572] (Michael Grunder) * Reset the socket after a timeout to make sure no wrong data is received [cd6ebc6d] (@marcdejonge) * Modify session testing logic [bfd27471] (Michael Grunder) * Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex [d4a08697] (Michael Grunder) @@ -127,19 +141,33 @@ http://pear.php.net/dtd/package-2.0.xsd"> betabeta - 4.2.0RC24.2.0RC2 - 2018-10-26 + 4.2.0RC34.2.0RC3 + 2018-11-08 - phpredis 4.2.0RC2 + phpredis 4.2.0RC3 The main feature of this release is new Streams API implemented by Michael Grunder. - * Streams API [2c9e0572] (Michael Grunder) + 4.2.0RC3: + + * Optimize close method [2a1ef961] (fanjiapeng) + * Prevent potential infinite loop for sessions [4e2de158] (Pavlo Yatsukhnenko) + * Fix coverty warnings [6f7ddd27] (Pavlo Yatsukhnenko) + * Fix session memory leaks [071a1d54, 92f14b14] (Pavlo Yatsukhnenko, Michael Grunder) + * Fix XCLAIM on 32-bit installs [18dc2aac] (Michael Grunder) + * Build warning fixes [b5093910, 51027044, 8b0f28cd] (Pavlo Yatsukhnenko, Remi Collet, twosee) + + 4.2.0RC2: + * Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce] (Pavlo Yatsukhnenko) * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) * Treat a -1 response from cluster_check_response as a timeout. [27df9220, 07ef7f4e, d1172426] (Michael Grunder) * Use a ZSET insted of SET for EVAL tests [2e412373] (Michael Grunder) * Missing space between command and args [0af2a7fe] (@remicollet) + + 4.2.0RC1: + + * Streams API [2c9e0572] (Michael Grunder) * Reset the socket after a timeout to make sure no wrong data is received [cd6ebc6d] (@marcdejonge) * Modify session testing logic [bfd27471] (Michael Grunder) * Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex [d4a08697] (Michael Grunder) @@ -287,10 +315,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Allow mixing MULTI and PIPELINE modes (experimental)! [5874b0] (Pavlo Yatsukhnenko) - * Added integration for coverty static analysis and fixed several warnings + * Added integration for coverty static analysis and fixed several warnings [faac8b0, eff7398, 4766c25, 0438ab4, 1e0b065, 733732a, 26eeda5, 735025, 42f1c9, af71d4] (Pavlo Yatsukhnenko) - * Added arginfo inrospection structures [81a0303, d5609fc, e5660be, 3c60e1f, 50dcb15, 6c2c6fa, - 212e323, e23be2c, 682593d, f8de702, 4ef3acd, f116be9, 5c111dd, 9caa029, 0d69650, 6859828, 024e593, + * Added arginfo inrospection structures [81a0303, d5609fc, e5660be, 3c60e1f, 50dcb15, 6c2c6fa, + 212e323, e23be2c, 682593d, f8de702, 4ef3acd, f116be9, 5c111dd, 9caa029, 0d69650, 6859828, 024e593, 3643ab6, f576fab, 122d41f, a09d0e6] (Tyson Andre, Pavlo Yatsukhnenko) * Fixed link to redis cluster documentation [3b0b06] (Pavlo Yatsukhnenko) * Remove unused PHP_RINIT and PHP_RSHUTDOWN functions [c760bf] (Pavlo Yatsukhnenko) From 5e720d8d10eac74128c6a3c88dc6aa85d130125f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 8 Nov 2018 12:50:26 -0800 Subject: [PATCH 1024/1986] 4.2.0RC3 --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index ed880d0db5..526e0fbb9b 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "4.2.0RC2" +#define PHP_REDIS_VERSION "4.2.0RC3" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From da2139dca6db90d99b47b327c6092c3fb87e13cb Mon Sep 17 00:00:00 2001 From: fanjiapeng Date: Fri, 16 Nov 2018 16:54:33 +0800 Subject: [PATCH 1025/1986] Update README.markdown --- README.markdown | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 5dbea00f78..2e3a671725 100644 --- a/README.markdown +++ b/README.markdown @@ -271,7 +271,7 @@ _**Description**_: Swap one Redis database with another atomically ##### *Return value* `TRUE` on success and `FALSE` on failure. -*note*: Requires Redis >= 4.0.0 +*Note*: Requires Redis >= 4.0.0 ##### *Example* ~~~php @@ -280,7 +280,15 @@ $redis->swapdb(0, 1); /* Swaps DB 0 with DB 1 atomically */ ### close ----- -_**Description**_: Disconnects from the Redis instance, except when `pconnect` is used. +_**Description**_: Disconnects from the Redis instance, include when `pconnect` is used. + +*Note*: Requires Redis >= 4.2.0, phpredis can closed the persistent connection. + +##### *Parameters* +None. + +##### *Return value* +*BOOL*: `TRUE` on success, `FALSE` on failure. ### setOption ----- From e1c9b3efcc0a8ae23a09ebdaeb54c82dee775259 Mon Sep 17 00:00:00 2001 From: fanjiapeng Date: Fri, 16 Nov 2018 17:04:56 +0800 Subject: [PATCH 1026/1986] Update README.markdown --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 2e3a671725..4f28360a38 100644 --- a/README.markdown +++ b/README.markdown @@ -282,7 +282,7 @@ $redis->swapdb(0, 1); /* Swaps DB 0 with DB 1 atomically */ ----- _**Description**_: Disconnects from the Redis instance, include when `pconnect` is used. -*Note*: Requires Redis >= 4.2.0, phpredis can closed the persistent connection. +*Note*: Requires Redis >= 4.2.0, phpredis can close the persistent connection. ##### *Parameters* None. From 292bd031f1b0453860d2c041a5d51d56f8c98bf3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 16 Nov 2018 10:07:34 -0800 Subject: [PATCH 1027/1986] Small wording update --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 4f28360a38..fbbe01b1e1 100644 --- a/README.markdown +++ b/README.markdown @@ -280,9 +280,9 @@ $redis->swapdb(0, 1); /* Swaps DB 0 with DB 1 atomically */ ### close ----- -_**Description**_: Disconnects from the Redis instance, include when `pconnect` is used. +_**Description**_: Disconnects from the Redis instance. -*Note*: Requires Redis >= 4.2.0, phpredis can close the persistent connection. +*Note*: Closing a persistent connection requires PhpRedis >= 4.2.0. ##### *Parameters* None. From 64a213e94f2c175620623611d890a167ae16897a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 17 Nov 2018 15:32:54 -0800 Subject: [PATCH 1028/1986] 4.2.0 --- package.xml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/package.xml b/package.xml index 749f84265b..fdc51faa3c 100644 --- a/package.xml +++ b/package.xml @@ -27,21 +27,23 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com yes - 2018-11-08 + 2018-11-17 - 4.2.0RC3 - 4.2.0RC3 + 4.2.0 + 4.2.0 - beta - beta + stable + stable PHP - phpredis 4.2.0RC3 + phpredis 4.2.0 The main feature of this release is new Streams API implemented by Michael Grunder. + Note: There are no changes between 4.2.0RC3 and 4.2.0. + 4.2.0RC3: * Optimize close method [2a1ef961] (fanjiapeng) From b8118b0991f37e237f645e93cafaecdb6e8cf44f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 17 Nov 2018 16:16:39 -0800 Subject: [PATCH 1029/1986] 4.2.0 --- package.xml | 2 +- php_redis.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.xml b/package.xml index fdc51faa3c..d2a790deaa 100644 --- a/package.xml +++ b/package.xml @@ -27,7 +27,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com yes - 2018-11-17 + 2018-11-18 4.2.0 4.2.0 diff --git a/php_redis.h b/php_redis.h index 526e0fbb9b..655f9730cd 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "4.2.0RC3" +#define PHP_REDIS_VERSION "4.2.0" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From ab651db2b4ecbfe49fa42088d5c22fc0360bfd9a Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Sun, 18 Nov 2018 07:05:52 +0200 Subject: [PATCH 1030/1986] Add streams to table of content --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index fbbe01b1e1..8f4e564d4e 100644 --- a/README.markdown +++ b/README.markdown @@ -27,6 +27,7 @@ You can send comments, patches, questions [here on github](https://github.com/ph * [Sets](#sets) * [Sorted sets](#sorted-sets) * [Geocoding](#geocoding) + * [Streams](#streams) * [Pub/sub](#pubsub) * [Transactions](#transactions) * [Scripting](#scripting) From 808a63d5ed2af1f3898e7e4b9ad38d2313f16f97 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 19 Nov 2018 17:06:46 +0100 Subject: [PATCH 1031/1986] fixed invalid stream id error in example --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 8f4e564d4e..b6a18b78d8 100644 --- a/README.markdown +++ b/README.markdown @@ -3330,7 +3330,7 @@ _**Description**_: Add a message to a stream ##### *Example* ~~~php -$obj_redis->xAdd('mystream', "\*", ['field' => 'value']); +$obj_redis->xAdd('mystream', "*", ['field' => 'value']); ~~~ ### xClaim From 7ca7520428fc582a8411172adca0989f402b07c4 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 19 Nov 2018 15:38:11 +0100 Subject: [PATCH 1032/1986] fixed xclaim documentation --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index b6a18b78d8..ae02204d9f 100644 --- a/README.markdown +++ b/README.markdown @@ -3338,7 +3338,7 @@ $obj_redis->xAdd('mystream', "*", ['field' => 'value']); ##### *Prototype* ~~~php -$obj_redis->($str_key, $str_group, $str_consumer, $min_idle_time, [$arr_options]); +$obj_redis->($str_key, $str_group, $str_consumer, $arr_ids, [$arr_options]); ~~~ _**Description**_: Claim ownership of one or more pending messages. From ffb6be330d3aaecceaa061431c815a4d5a942208 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 19 Nov 2018 11:29:06 -0800 Subject: [PATCH 1033/1986] Add min_idle_time back to prototype --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index ae02204d9f..34eb3cc68a 100644 --- a/README.markdown +++ b/README.markdown @@ -3338,7 +3338,7 @@ $obj_redis->xAdd('mystream', "*", ['field' => 'value']); ##### *Prototype* ~~~php -$obj_redis->($str_key, $str_group, $str_consumer, $arr_ids, [$arr_options]); +$obj_redis->xClaim($str_key, $str_group, $str_consumer, $min_idle_time, $arr_ids, [$arr_options]); ~~~ _**Description**_: Claim ownership of one or more pending messages. From 1c8af747bd45823298ae2b26d1d83a7ea3ee7c2c Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 19 Nov 2018 20:40:15 +0100 Subject: [PATCH 1034/1986] fixed xclaim example min idle parameter --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 34eb3cc68a..c3d3f29b62 100644 --- a/README.markdown +++ b/README.markdown @@ -3364,12 +3364,12 @@ $ids = ['1530113681011-0', '1530113681011-1', '1530113681011-2']; /* Without any options */ $obj_redis->xClaim( - 'mystream', 'group1', 'myconsumer1', $ids + 'mystream', 'group1', 'myconsumer1', 0, $ids ); /* With options */ $obj_redis->xClaim( - 'mystream', 'group1', 'myconsumer2', $ids, + 'mystream', 'group1', 'myconsumer2', 0, $ids, [ 'IDLE' => time() * 1000, 'RETRYCOUNT' => 5, From c0793e8be49323e84e5116e20001620f0ab4f903 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 21 Nov 2018 11:51:03 +0200 Subject: [PATCH 1035/1986] Remove unused parameter lazy_connect from redis_sock_create --- cluster_library.c | 4 ++-- library.c | 2 +- library.h | 2 +- redis.c | 2 +- redis_array_impl.c | 2 +- redis_session.c | 8 ++++---- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 2159a2e6af..efda062af7 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -652,7 +652,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, // Attach socket node->sock = redis_sock_create(host, host_len, port, c->timeout, - c->read_timeout, c->persistent, NULL, 0, 1); + c->read_timeout, c->persistent, NULL, 0); return node; } @@ -923,7 +923,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { // Allocate a structure for this seed redis_sock = redis_sock_create(str, psep-str, (unsigned short)atoi(psep+1), cluster->timeout, - cluster->read_timeout, cluster->persistent, NULL, 0, 0); + cluster->read_timeout, cluster->persistent, NULL, 0); // Index this seed by host/port key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(redis_sock->host), diff --git a/library.c b/library.c index 00d2f7abc4..f9084a732b 100644 --- a/library.c +++ b/library.c @@ -1690,7 +1690,7 @@ PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, - long retry_interval, zend_bool lazy_connect) + long retry_interval) { RedisSock *redis_sock; diff --git a/library.h b/library.h index 80b12d0383..381a63bc14 100644 --- a/library.h +++ b/library.h @@ -42,7 +42,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); +PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC); diff --git a/redis.c b/redis.c index 5242d97c50..5dad4917ad 100644 --- a/redis.c +++ b/redis.c @@ -970,7 +970,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) } redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent, - persistent_id, retry_interval, 0); + persistent_id, retry_interval); if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { if (redis->sock->err) { diff --git a/redis_array_impl.c b/redis_array_impl.c index 16d8b17c17..c3f89cc321 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -72,7 +72,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b redis = PHPREDIS_GET_OBJECT(redis_object, &ra->redis[i]); /* create socket */ - redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); + redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval); if (!b_lazy_connect) { diff --git a/redis_session.c b/redis_session.c index 6369e134d5..e1f79c1702 100644 --- a/redis_session.c +++ b/redis_session.c @@ -512,15 +512,15 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if (url->host) { #if (PHP_VERSION_ID < 70300) - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval); #else - redis_sock = redis_sock_create(ZSTR_VAL(url->host), ZSTR_LEN(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(ZSTR_VAL(url->host), ZSTR_LEN(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval); #endif } else { /* unix */ #if (PHP_VERSION_ID < 70300) - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval); #else - redis_sock = redis_sock_create(ZSTR_VAL(url->path), ZSTR_LEN(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(ZSTR_VAL(url->path), ZSTR_LEN(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval); #endif } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); From bc4dbc4b8895850d989fe5c98383e9da8f4f23f9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 21 Nov 2018 14:17:06 +0200 Subject: [PATCH 1036/1986] Refactor redis_sock_read_bulk_reply --- library.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library.c b/library.c index f9084a732b..3684e04a2b 100644 --- a/library.c +++ b/library.c @@ -468,26 +468,27 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API char * redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) { - int offset = 0; - char *reply, c[2]; + int offset = 0, nbytes; + char *reply; size_t got; if (-1 == bytes || -1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { return NULL; } + nbytes = bytes + 2; /* Allocate memory for string */ - reply = emalloc(bytes+1); + reply = emalloc(nbytes); /* Consume bulk string */ - while(offset < bytes) { - got = php_stream_read(redis_sock->stream, reply + offset, bytes-offset); - if (got == 0) break; + while (offset < nbytes) { + got = php_stream_read(redis_sock->stream, reply + offset, nbytes - offset); + if (got == 0 && php_stream_eof(redis_sock->stream)) break; offset += got; } /* Protect against reading too few bytes */ - if (offset < bytes) { + if (offset < nbytes) { /* Error or EOF */ zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); @@ -495,8 +496,7 @@ redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) return NULL; } - /* Consume \r\n and null terminate reply string */ - php_stream_read(redis_sock->stream, c, 2); + /* Null terminate reply string */ reply[bytes] = '\0'; return reply; From 91bd7426d539f48900ed545a105c78f02b1927b8 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Thu, 22 Nov 2018 11:37:18 -0800 Subject: [PATCH 1037/1986] Masters info leakfix (#1462) Fix for memory leaks in `RedisCluster->_masters()` and `RedisCluster->info()` --- cluster_library.c | 3 ++- library.c | 1 + redis_cluster.c | 13 ++++------ tests/RedisTest.php | 63 ++++++++++++++++++++++++++------------------- 4 files changed, 44 insertions(+), 36 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index efda062af7..780c36154f 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2072,12 +2072,13 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } /* Parse response, free memory */ + REDIS_MAKE_STD_ZVAL(z_result); redis_parse_info_response(info, z_result); efree(info); // Return our array if (CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_result, 1, 0); + RETVAL_ZVAL(z_result, 0, 1); } else { add_next_index_zval(&c->multi_resp, z_result); } diff --git a/library.c b/library.c index 3684e04a2b..6bcbea1d0c 100644 --- a/library.c +++ b/library.c @@ -840,6 +840,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } /* Parse it into a zval array */ + REDIS_MAKE_STD_ZVAL(z_ret); redis_parse_info_response(response, z_ret); /* Free source response */ diff --git a/redis_cluster.c b/redis_cluster.c index 8f10bcb06e..e08f1f5cea 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2026,25 +2026,21 @@ PHP_METHOD(RedisCluster, _unserialize) { PHP_METHOD(RedisCluster, _masters) { redisCluster *c = GET_CONTEXT(); redisClusterNode *node; - zval zv, *z_ret = &zv; - array_init(z_ret); + array_init(return_value); ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) break; zval z, *z_sub = &z; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_sub); -#endif + + REDIS_MAKE_STD_ZVAL(z_sub); array_init(z_sub); add_next_index_stringl(z_sub, ZSTR_VAL(node->sock->host), ZSTR_LEN(node->sock->host)); add_next_index_long(z_sub, node->sock->port); - add_next_index_zval(z_ret, z_sub); + add_next_index_zval(return_value, z_sub); } ZEND_HASH_FOREACH_END(); - - RETVAL_ZVAL(z_ret, 1, 0); } PHP_METHOD(RedisCluster, _redir) { @@ -2714,6 +2710,7 @@ PHP_METHOD(RedisCluster, info) { int cmd_len; strlen_t opt_len = 0; void *ctx = NULL; + zval *z_arg; short slot; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index e353829d7f..5f23a76a48 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1882,36 +1882,45 @@ public function testWait() { } public function testInfo() { - $info = $this->redis->info(); + foreach (Array(false, true) 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 = array( - "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", "lt")) { - array_push($keys, - "changes_since_last_save", - "bgsave_in_progress", - "last_save_time" + $keys = array( + "redis_version", + "arch_bits", + "uptime_in_seconds", + "uptime_in_days", + "connected_clients", + "connected_slaves", + "used_memory", + "total_connections_received", + "total_commands_processed", + "role" ); - } else { - array_push($keys, - "rdb_changes_since_last_save", - "rdb_bgsave_in_progress", - "rdb_last_save_time" - ); - } + if (version_compare($this->version, "2.5.0", "lt")) { + 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($k, array_keys($info))); + foreach($keys as $k) { + $this->assertTrue(in_array($k, array_keys($info))); + } } } From 72749916140e8a0480efdea60c457f502885e6c0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 22 Nov 2018 22:52:39 +0200 Subject: [PATCH 1038/1986] Issue #1464 --- cluster_library.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cluster_library.c b/cluster_library.c index 780c36154f..3dfcbc9dcd 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1384,6 +1384,11 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char int resp, timedout = 0; long msstart; + if (!SLOT(c, slot)) { + zend_throw_exception_ex(redis_cluster_exception_ce, 0, + "The slot %d is not covered by any node in this cluster", slot); + return -1; + } /* Set the slot we're operating against as well as it's socket. These can * change during our request loop if we have a master failure and are * configured to fall back to slave nodes, or if we have to fall back to From 6e455e2e17ccf22ea18d5bce93f64f58bd59c47d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 22 Nov 2018 23:12:35 +0200 Subject: [PATCH 1039/1986] Fix build warning for PHP 5 --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 3dfcbc9dcd..1f6d416043 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1385,7 +1385,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char long msstart; if (!SLOT(c, slot)) { - zend_throw_exception_ex(redis_cluster_exception_ce, 0, + zend_throw_exception_ex(redis_cluster_exception_ce, 0 TSRMLS_CC, "The slot %d is not covered by any node in this cluster", slot); return -1; } From ad10a49eb841f57d866cfd08eb9a12ad2ca918a3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 23 Nov 2018 09:02:22 +0200 Subject: [PATCH 1040/1986] Directly use return_value in RedisCluster::keys method --- redis_cluster.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index e08f1f5cea..7869efb4d6 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1084,7 +1084,6 @@ PHP_METHOD(RedisCluster, keys) { strlen_t pat_len; char *pat, *cmd; clusterReply *resp; - zval zv, *z_ret = &zv; int i, cmd_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pat, &pat_len) @@ -1096,19 +1095,20 @@ PHP_METHOD(RedisCluster, keys) { /* Prefix and then build our command */ cmd_len = redis_spprintf(c->flags, NULL TSRMLS_CC, &cmd, "KEYS", "k", pat, pat_len); - array_init(z_ret); + array_init(return_value); /* Treat as readonly */ c->readonly = CLUSTER_IS_ATOMIC(c); /* Iterate over our known nodes */ ZEND_HASH_FOREACH_PTR(c->nodes, node) { - if (node == NULL) break; + if (node == NULL) continue; if (cluster_send_slot(c, node->slot, cmd, cmd_len, TYPE_MULTIBULK TSRMLS_CC) < 0) { php_error_docref(0 TSRMLS_CC, E_ERROR, "Can't send KEYS to %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); + zval_dtor(return_value); efree(cmd); RETURN_FALSE; } @@ -1129,7 +1129,7 @@ PHP_METHOD(RedisCluster, keys) { continue; } - add_next_index_stringl(z_ret, resp->element[i]->str, + add_next_index_stringl(return_value, resp->element[i]->str, resp->element[i]->len); } @@ -1138,9 +1138,6 @@ PHP_METHOD(RedisCluster, keys) { } ZEND_HASH_FOREACH_END(); efree(cmd); - - /* Return our keys */ - RETURN_ZVAL(z_ret, 1, 0); } /* }}} */ From 3b56b7db33343f0852e41db9bff5a7880a4cc688 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Sun, 25 Nov 2018 13:14:28 -0800 Subject: [PATCH 1041/1986] Fix RedisCluster keys memory leak (#1466) Free redis response since adding it to our array duplicates the data anyway. Addresses #1460 --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index 7869efb4d6..fa64ac4862 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1134,7 +1134,7 @@ PHP_METHOD(RedisCluster, keys) { } /* Free response, don't free data */ - cluster_free_reply(resp, 0); + cluster_free_reply(resp, 1); } ZEND_HASH_FOREACH_END(); efree(cmd); From 74abde303edf558f72ed82e4f9c1eb72560d3055 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 26 Nov 2018 18:32:56 +0100 Subject: [PATCH 1042/1986] updated xrange, xrevrange, xpending types (#1468) --- README.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index c3d3f29b62..e7c90a18c2 100644 --- a/README.markdown +++ b/README.markdown @@ -3464,7 +3464,7 @@ $obj_redis->xLen('mystream'); ##### *Prototype* ~~~php -$obj_redis->xPending($str_stream, $str_group [, $i_start, $i_end, $i_count, $str_consumer]); +$obj_redis->xPending($str_stream, $str_group [, $str_start, $str_end, $i_count, $str_consumer]); ~~~ _**Description**_: Get information about pending messages in a given stream. @@ -3475,7 +3475,7 @@ _**Description**_: Get information about pending messages in a given stream. ##### *Examples* ~~~php $obj_redis->xPending('mystream', 'mygroup'); -$obj_redis->xPending('mystream', 'mygroup', 0, '+', 1, 'consumer-1'); +$obj_redis->xPending('mystream', 'mygroup', '-', '+', 1, 'consumer-1'); ~~~ ### xRange @@ -3483,7 +3483,7 @@ $obj_redis->xPending('mystream', 'mygroup', 0, '+', 1, 'consumer-1'); ##### *Prototype* ~~~php -$obj_redis->xRange($str_stream, $i_start, $i_end [, $i_count]); +$obj_redis->xRange($str_stream, $str_start, $str_end [, $i_count]); ~~~ _**Description**_: Get a range of messages from a given stream. @@ -3569,7 +3569,7 @@ $obj_redis->xReadGroup('mygroup', 'consumer2', ['s1' => 0, 's2' => 0], 1, 1000); ##### *Prototype* ~~~php -$obj_redis->xRevRange($str_stream, $i_end, $i_start [, $i_count]); +$obj_redis->xRevRange($str_stream, $str_end, $str_start [, $i_count]); ~~~ _**Description**_: This is identical to xRange except the results come back in reverse order. Also note that Redis reverses the order of "start" and "end". From 30d5ed63dbdd4c9e0d3771054216bc906a46eed2 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 26 Nov 2018 19:53:00 +0100 Subject: [PATCH 1043/1986] add example for $ = last_id and > for not consumed messages yet --- README.markdown | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index e7c90a18c2..0d683ad774 100644 --- a/README.markdown +++ b/README.markdown @@ -3540,6 +3540,9 @@ Array ) */ + +// Receive only new message ($ = last id) and wait for one new message unlimited +$obj_redis->xRead(['stream1' => '$'], 1, 0); ~~~ ### xReadGroup @@ -3560,7 +3563,10 @@ _**Description**_: This method is similar to xRead except that it supports read /* Consume messages for 'mygroup', 'consumer1' */ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => 0, 's2' => 0]); -/* Read a single message as 'consumer2' for up to a second until a message arrives. */ +/* Consume messages for 'mygroup', 'consumer1' which where not consumed yet */ +$obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => '>', 's2' => '>']); + +/* Read a single message as 'consumer2' wait for up to a second until a message arrives. */ $obj_redis->xReadGroup('mygroup', 'consumer2', ['s1' => 0, 's2' => 0], 1, 1000); ~~~ From 7c0f77c7a8be0ceaa355d3a9974957803c2426bb Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 26 Nov 2018 20:03:23 +0100 Subject: [PATCH 1044/1986] fixed typo --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 0d683ad774..ef22a6a5bc 100644 --- a/README.markdown +++ b/README.markdown @@ -3541,7 +3541,7 @@ Array ) */ -// Receive only new message ($ = last id) and wait for one new message unlimited +// Receive only new message ($ = last id) and wait for one new message unlimited time $obj_redis->xRead(['stream1' => '$'], 1, 0); ~~~ From ed284722611498abc04979b4166b35ea7350baac Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 26 Nov 2018 20:03:50 +0100 Subject: [PATCH 1045/1986] fix typo --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index ef22a6a5bc..f5f1e92c02 100644 --- a/README.markdown +++ b/README.markdown @@ -3563,7 +3563,7 @@ _**Description**_: This method is similar to xRead except that it supports read /* Consume messages for 'mygroup', 'consumer1' */ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => 0, 's2' => 0]); -/* Consume messages for 'mygroup', 'consumer1' which where not consumed yet */ +/* Consume messages for 'mygroup', 'consumer1' which where not consumed yet by the group */ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => '>', 's2' => '>']); /* Read a single message as 'consumer2' wait for up to a second until a message arrives. */ From 5f26dd1a5332bd70ec4d15c639d5e78349418f40 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 26 Nov 2018 11:36:10 -0800 Subject: [PATCH 1046/1986] Fix documentation typo --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index f5f1e92c02..22593facaf 100644 --- a/README.markdown +++ b/README.markdown @@ -3563,7 +3563,7 @@ _**Description**_: This method is similar to xRead except that it supports read /* Consume messages for 'mygroup', 'consumer1' */ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => 0, 's2' => 0]); -/* Consume messages for 'mygroup', 'consumer1' which where not consumed yet by the group */ +/* Consume messages for 'mygroup', 'consumer1' which were not consumed yet by the group */ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => '>', 's2' => '>']); /* Read a single message as 'consumer2' wait for up to a second until a message arrives. */ From 3ea8a210d41f09be194757bd760ac7fbd2e80b68 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 8 Dec 2018 15:44:07 +0200 Subject: [PATCH 1047/1986] Fix broken links in INSTALL.markdown --- INSTALL.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.markdown b/INSTALL.markdown index 8680b1970e..967c988929 100644 --- a/INSTALL.markdown +++ b/INSTALL.markdown @@ -22,7 +22,7 @@ The extension also may compress data before sending it to Redis server, if you r You can generate a debian package for PHP5, accessible from Apache 2 by running `./mkdeb-apache2.sh` or with `dpkg-buildpackage` or `svn-buildpackage`. -This extension exports a single class, [Redis](#class-redis) (and [RedisException](#class-redisexception) used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion. +This extension exports a single class, [Redis](./README.markdown#class-redis) (and [RedisException](./README.markdown#class-redisexception) used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion. # Binary packages From bb6599e47e088d8bc0fa89eb856eed1143a71ece Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 8 Dec 2018 16:58:29 +0200 Subject: [PATCH 1048/1986] TravisCI: allow failing on PHP 7.3 + clang Because clang hasn't supported 'asm goto' yet --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a344efe04a..8c84d018a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,8 @@ php: env: CC=gcc matrix: allow_failures: + - php: 7.3 + env: CC=clang - php: nightly include: # php 5.3 is only available on precise From 7b8b7b01a2b2af2e646ca89802910ac216c476dc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 8 Dec 2018 15:10:34 -0800 Subject: [PATCH 1049/1986] Fix unit tests for Redis 5.0.2 Addresses issue #1472 --- tests/RedisTest.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 5f23a76a48..8c358b6e43 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5392,7 +5392,7 @@ public function testXAck() { for ($n = 1; $n <= 3; $n++) { $this->addStreamsAndGroups(Array('{s}'), 3, Array('g1' => 0)); - $msg = $this->redis->xReadGroup('g1', 'c1', Array('{s}' => 0)); + $msg = $this->redis->xReadGroup('g1', 'c1', Array('{s}' => '>')); /* Extract IDs */ $smsg = array_shift($msg); @@ -5494,17 +5494,25 @@ public function testXReadGroup() { /* Create some streams and groups */ $streams = Array('{s}-1', '{s}-2'); - $qstreams = Array('{s}-1' => 0, '{s}-2' => 0); $groups = Array('g1' => 0, 'g2' => 0); - $ids = $this->addStreamsAndGroups($streams, 3, $groups); + /* 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 = Array('{s}-1' => '>', '{s}-2' => '>'); + $query2 = Array('{s}-1' => '0', '{s}-2' => '0'); + + $ids = $this->addStreamsAndGroups($streams, 1, $groups); /* Test that we get get the IDs we should */ foreach (Array('g1', 'g2') as $group) { foreach ($ids as $stream => $messages) { while ($ids[$stream]) { /* Read more messages */ - $resp = $this->redis->xReadGroup($group, 'consumer', $qstreams); + $query = !$qcount++ ? $query1 : $query2; + $resp = $this->redis->xReadGroup($group, 'consumer', $query); /* They should match with our local control array */ $this->compareStreamIds($resp, $ids); @@ -5519,7 +5527,7 @@ public function testXReadGroup() { /* Test COUNT option */ for ($c = 1; $c <= 3; $c++) { $this->addStreamsAndGroups($streams, 3, $groups); - $resp = $this->redis->xReadGroup('g1', 'consumer', $qstreams, $c); + $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, $c); foreach ($resp as $stream => $smsg) { $this->assertEquals(count($smsg), $c); @@ -5624,7 +5632,7 @@ public function testXClaim() { $fids = $fids['f']; /* Have consumer 'Mike' read the messages */ - $oids = $this->redis->xReadGroup('group1', 'Mike', Array('s' => 0)); + $oids = $this->redis->xReadGroup('group1', 'Mike', Array('s' => '>')); $oids = array_keys($oids['s']); /* We're only dealing with stream 's' */ /* Construct our options array */ From bf8ceae774f51628b83d76ef79a396661928038a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 13 Dec 2018 17:26:00 +0200 Subject: [PATCH 1050/1986] Issue #1477 --- library.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/library.c b/library.c index 6bcbea1d0c..e49890f67b 100644 --- a/library.c +++ b/library.c @@ -2126,21 +2126,27 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_ #ifdef HAVE_REDIS_LZF char *data; uint32_t res; + double size; #endif valfree = redis_serialize(redis_sock, z, &buf, &len TSRMLS_CC); switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: #ifdef HAVE_REDIS_LZF - data = emalloc(len); - res = lzf_compress(buf, len, data, len - 1); - if (res > 0 && res < len) { - if (valfree) efree(buf); - *val = data; - *val_len = res; - return 1; + /** + * output buffer might be considerably more than in_len + * (but less than 104% of the original size) + */ + if ((size = ceil(len * 1.04)) < UINT_MAX) { + data = emalloc(size); + if ((res = lzf_compress(buf, len, data, size)) > 0) { + if (valfree) efree(buf); + *val = data; + *val_len = res; + return 1; + } + efree(data); } - efree(data); #endif break; } From 602740d35a5bcbad785ebaff328bfb3ec2ecd36f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 19 Dec 2018 10:16:54 +0200 Subject: [PATCH 1051/1986] Use zend_string for storing RedisArray hosts --- redis_array.c | 8 ++++---- redis_array.h | 2 +- redis_array_impl.c | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/redis_array.c b/redis_array.c index 217ab63e15..bdb9fd66c5 100644 --- a/redis_array.c +++ b/redis_array.c @@ -141,7 +141,7 @@ redis_array_free(RedisArray *ra) /* Redis objects */ for(i = 0; i< ra->count; i++) { zval_dtor(&ra->redis[i]); - efree(ra->hosts[i]); + zend_string_release(ra->hosts[i]); } efree(ra->redis); efree(ra->hosts); @@ -514,7 +514,7 @@ PHP_METHOD(RedisArray, _hosts) array_init(return_value); for(i = 0; i < ra->count; ++i) { - add_next_index_string(return_value, ra->hosts[i]); + add_next_index_stringl(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i])); } } @@ -538,7 +538,7 @@ PHP_METHOD(RedisArray, _target) redis_inst = ra_find_node(ra, key, key_len, &i TSRMLS_CC); if(redis_inst) { - RETURN_STRING(ra->hosts[i]); + RETURN_STRINGL(ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i])); } else { RETURN_NULL(); } @@ -646,7 +646,7 @@ multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int a call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, z_tmp, argc, argv); /* Add the result for this host */ - add_assoc_zval(return_value, ra->hosts[i], z_tmp); + add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), z_tmp); } } diff --git a/redis_array.h b/redis_array.h index 7bacef4151..458d814ab1 100644 --- a/redis_array.h +++ b/redis_array.h @@ -41,7 +41,7 @@ PHP_METHOD(RedisArray, unwatch); typedef struct RedisArray_ { int count; - char **hosts; /* array of host:port strings */ + zend_string **hosts; /* array of host:port strings */ zval *redis; /* array of Redis instances */ zval *z_multi_exec; /* Redis instance to be used in multi-exec */ zend_bool index; /* use per-node index */ diff --git a/redis_array_impl.c b/redis_array_impl.c index c3f89cc321..09afa3169b 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -51,7 +51,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b /* default values */ host = Z_STRVAL_P(zpData); host_len = Z_STRLEN_P(zpData); - ra->hosts[i] = estrndup(host, host_len); + ra->hosts[i] = zend_string_init(host, host_len, 0); port = 6379; if((p = strrchr(host, ':'))) { /* found port */ @@ -348,7 +348,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* create object */ ra = emalloc(sizeof(RedisArray)); - ra->hosts = ecalloc(count, sizeof(char *)); + ra->hosts = ecalloc(count, sizeof(*ra->hosts)); ra->redis = ecalloc(count, sizeof(zval)); ra->count = 0; ra->z_multi_exec = NULL; @@ -361,7 +361,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { zval_dtor(&ra->redis[i]); - efree(ra->hosts[i]); + zend_string_release(ra->hosts[i]); } efree(ra->redis); efree(ra->hosts); @@ -500,7 +500,7 @@ ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC) { int i; for(i = 0; i < ra->count; ++i) { - if(strncmp(ra->hosts[i], host, host_len) == 0) { + if (ZSTR_LEN(ra->hosts[i]) == host_len && strcmp(ZSTR_VAL(ra->hosts[i]), host) == 0) { return &ra->redis[i]; } } @@ -1079,7 +1079,7 @@ ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { /* callback with the current progress, with hostname and count */ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, - const char *hostname, long count TSRMLS_DC) { + zend_string *hostname, long count TSRMLS_DC) { zval zv, *z_ret = &zv; @@ -1088,7 +1088,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, zval *z_host, *z_count, **z_args_pp[2]; MAKE_STD_ZVAL(z_host); - ZVAL_STRING(z_host, hostname); + ZVAL_STRINGL(z_host, ZSTR_VAL(hostname), ZSTR_LEN(hostname)); z_args_pp[0] = &z_host; MAKE_STD_ZVAL(z_count); @@ -1100,7 +1100,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, #else zval z_args[2]; - ZVAL_STRING(&z_args[0], hostname); + ZVAL_STRINGL(&z_args[0], ZSTR_VAL(hostname), ZSTR_LEN(hostname)); ZVAL_LONG(&z_args[1], count); z_cb->params = z_args; @@ -1123,7 +1123,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, } static void -ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool b_index, +ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool b_index, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { HashTable *h_keys; @@ -1164,7 +1164,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, const char *hostname, zend_bool /* check that we're not moving to the same node. */ zval *z_target = ra_find_node(ra, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &pos TSRMLS_CC); - if (z_target && strcmp(hostname, ra->hosts[pos])) { /* different host */ + if (z_target && zend_string_equals(hostname, ra->hosts[pos])) { /* different host */ ra_move_key(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), z_redis, z_target TSRMLS_CC); } From b27fd43032203fa36adec5264f5e95da2fefde5e Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Wed, 19 Dec 2018 09:33:07 -0500 Subject: [PATCH 1052/1986] Issue #1477: lzf_compress margin compatibility --- library.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/library.c b/library.c index e49890f67b..d3278a871c 100644 --- a/library.c +++ b/library.c @@ -12,6 +12,10 @@ #ifdef HAVE_REDIS_LZF #include + + #ifndef LZF_MARGIN + #define LZF_MARGIN 128 + #endif #endif #include @@ -2133,20 +2137,16 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_ switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: #ifdef HAVE_REDIS_LZF - /** - * output buffer might be considerably more than in_len - * (but less than 104% of the original size) - */ - if ((size = ceil(len * 1.04)) < UINT_MAX) { - data = emalloc(size); - if ((res = lzf_compress(buf, len, data, size)) > 0) { - if (valfree) efree(buf); - *val = data; - *val_len = res; - return 1; - } - efree(data); + /* preserve compatibility with PECL lzf_compress margin (greater of 4% and LZF_MARGIN) */ + size = len + MIN(UINT_MAX - len, MAX(LZF_MARGIN, len / 25)); + data = emalloc(size); + if ((res = lzf_compress(buf, len, data, size)) > 0) { + if (valfree) efree(buf); + *val = data; + *val_len = res; + return 1; } + efree(data); #endif break; } From 3e7e1c833d08aac4e0eeb4e37dcc44768c8b117c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 22 Dec 2018 15:30:46 +0200 Subject: [PATCH 1053/1986] Fix regression added in 602740d3 --- redis_array_impl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 09afa3169b..6280b4cabd 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -1164,7 +1164,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool /* check that we're not moving to the same node. */ zval *z_target = ra_find_node(ra, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &pos TSRMLS_CC); - if (z_target && zend_string_equals(hostname, ra->hosts[pos])) { /* different host */ + if (z_target && !zend_string_equals(hostname, ra->hosts[pos])) { /* different host */ ra_move_key(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), z_redis, z_target TSRMLS_CC); } From bb32e6f3a0e93b1de9235de2db496fb1bfc400d0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Dec 2018 15:04:13 +0200 Subject: [PATCH 1054/1986] Implement consistent hashing algorithm for RedisArray --- common.h | 11 ++++++ redis.c | 1 + redis_array.c | 15 ++++++-- redis_array.h | 11 +++++- redis_array_impl.c | 86 +++++++++++++++++++++++++++++++++++++++++++--- redis_array_impl.h | 2 +- redis_session.c | 11 ------ 7 files changed, 117 insertions(+), 20 deletions(-) diff --git a/common.h b/common.h index adbe3035dd..122c9627a7 100644 --- a/common.h +++ b/common.h @@ -644,6 +644,17 @@ typedef enum _PUBSUB_TYPE { #define REDIS_ENABLE_MODE(redis_sock, m) (redis_sock->mode |= m) #define REDIS_DISABLE_MODE(redis_sock, m) (redis_sock->mode &= ~m) +/* HOST_NAME_MAX doesn't exist everywhere */ +#ifndef HOST_NAME_MAX + #if defined(_POSIX_HOST_NAME_MAX) + #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX + #elif defined(MAXHOSTNAMELEN) + #define HOST_NAME_MAX MAXHOSTNAMELEN + #else + #define HOST_NAME_MAX 255 + #endif +#endif + typedef struct fold_item { zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, void *, ...); void *ctx; diff --git a/redis.c b/redis.c index 5dad4917ad..cf08d762d7 100644 --- a/redis.c +++ b/redis.c @@ -67,6 +67,7 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.readtimeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.retryinterval", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.consistent", "0", PHP_INI_ALL, NULL) /* redis cluster */ PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL) diff --git a/redis_array.c b/redis_array.c index bdb9fd66c5..df661a9f29 100644 --- a/redis_array.c +++ b/redis_array.c @@ -138,6 +138,12 @@ redis_array_free(RedisArray *ra) { int i; + /* continuum */ + if (ra->continuum) { + efree(ra->continuum->points); + efree(ra->continuum); + } + /* Redis objects */ for(i = 0; i< ra->count; i++) { zval_dtor(&ra->redis[i]); @@ -264,7 +270,7 @@ PHP_METHOD(RedisArray, __construct) { zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; RedisArray *ra = NULL; - zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; + zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; HashTable *hPrev = NULL, *hOpts = NULL; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; @@ -349,6 +355,11 @@ PHP_METHOD(RedisArray, __construct) read_timeout = atof(Z_STRVAL_P(zpData)); } } + + /* consistent */ + if ((zpData = zend_hash_str_find(hOpts, "consistent", sizeof("consistent") - 1)) != NULL) { + consistent = zval_is_true(zpData); + } } /* extract either name of list of hosts from z0 */ @@ -358,7 +369,7 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); break; default: diff --git a/redis_array.h b/redis_array.h index 458d814ab1..ba53fadcc9 100644 --- a/redis_array.h +++ b/redis_array.h @@ -37,6 +37,15 @@ PHP_METHOD(RedisArray, exec); PHP_METHOD(RedisArray, discard); PHP_METHOD(RedisArray, unwatch); +typedef struct { + uint32_t value; + int index; +} ContinuumPoint; + +typedef struct { + size_t nb_points; + ContinuumPoint *points; +} Continuum; typedef struct RedisArray_ { @@ -52,7 +61,7 @@ typedef struct RedisArray_ { HashTable *pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ double read_timeout; /* socket read timeout */ - + Continuum *continuum; struct RedisArray_ *prev; } RedisArray; diff --git a/redis_array_impl.c b/redis_array_impl.c index 6280b4cabd..c5d4796c3e 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -24,6 +24,7 @@ #include "SAPI.h" #include "ext/standard/url.h" #include "ext/standard/crc32.h" +#include "ext/standard/md5.h" #define PHPREDIS_INDEX_NAME "__phpredis_array_index__" @@ -173,9 +174,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_connect_timeout; zval z_params_read_timeout; zval z_params_lazy_connect; + zval z_params_consistent; RedisArray *ra = NULL; - zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; + zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0, read_timeout = 0.0; @@ -312,9 +314,20 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } + /* find consistent option */ + array_init(&z_params_consistent); + if ((iptr = INI_STR("redis.arrays.consistent")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_consistent TSRMLS_CC); + } + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_consistent), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { + consistent = 1; + } + } + /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; @@ -332,14 +345,55 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_connect_timeout); zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); + zval_dtor(&z_params_consistent); zval_dtor(&z_dist); zval_dtor(&z_fun); return ra; } +static int +ra_points_cmp(const void *v1, const void *v2) +{ + const ContinuumPoint *p1 = v1, *p2 = v2; + + return p1->value < p2->value ? - 1 : p1->value > p2->value; +} + +static Continuum * +ra_make_continuum(zend_string **hosts, int nb_hosts) +{ + int i, j, k, len, idx = 0; + char host[HOST_NAME_MAX]; + unsigned char digest[16]; + PHP_MD5_CTX ctx; + Continuum *c; + + c = ecalloc(1, sizeof(*c)); + c->nb_points = nb_hosts * 160; /* 40 hashes, 4 numbers per hash = 160 points per server */ + c->points = ecalloc(c->nb_points, sizeof(*c->points)); + + for (i = 0; i < nb_hosts; ++i) { + for (j = 0; j < 40; ++j) { + len = snprintf(host, sizeof(host), "%.*s-%u", ZSTR_LEN(hosts[i]), ZSTR_VAL(hosts[i]), j); + PHP_MD5Init(&ctx); + PHP_MD5Update(&ctx, host, len); + PHP_MD5Final(digest, &ctx); + for (k = 0; k < 4; ++k) { + c->points[idx].index = i; + c->points[idx++].value = (digest[3 + k * 4] << 24) + | (digest[2 + k * 4] << 16) + | (digest[1 + k * 4] << 8) + | (digest[k * 4]); + } + } + } + qsort(c->points, c->nb_points, sizeof(*c->points), ra_points_cmp); + return c; +} + RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC) { int i, count; RedisArray *ra; @@ -357,6 +411,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->pconnect = b_pconnect; ra->connect_timeout = connect_timeout; ra->read_timeout = read_timeout; + ra->continuum = NULL; if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { @@ -368,7 +423,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); @@ -377,6 +432,11 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); ZVAL_ZVAL(&ra->z_dist, z_dist, 1, 0); + /* init continuum */ + if (consistent) { + ra->continuum = ra_make_continuum(ra->hosts, ra->count); + } + return ra; } @@ -480,7 +540,23 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D } /* get position on ring */ - pos = (int)((ret ^ 0xffffffff) * ra->count / 0xffffffff); + if (ra->continuum) { + int left = 0, right = ra->continuum->nb_points; + while (left < right) { + i = (int)((left + right) / 2); + if (ra->continuum->points[i].value < ret) { + left = i + 1; + } else { + right = i; + } + } + if (right == ra->continuum->nb_points) { + right = 0; + } + pos = ra->continuum->points[right].index; + } else { + pos = (int)((ret ^ 0xffffffff) * ra->count / 0xffffffff); + } } else { pos = ra_call_distributor(ra, key, key_len TSRMLS_CC); if (pos < 0 || pos >= ra->count) { diff --git a/redis_array_impl.h b/redis_array_impl.h index 385daadf80..fa5fd848b4 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -11,7 +11,7 @@ RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); diff --git a/redis_session.c b/redis_session.c index e1f79c1702..1948c6c8a2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -41,17 +41,6 @@ #include "SAPI.h" #include "ext/standard/url.h" -/* HOST_NAME_MAX doesn't exist everywhere */ -#ifndef HOST_NAME_MAX - #if defined(_POSIX_HOST_NAME_MAX) - #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX - #elif defined(MAXHOSTNAMELEN) - #define HOST_NAME_MAX MAXHOSTNAMELEN - #else - #define HOST_NAME_MAX 255 - #endif -#endif - /* Session lock LUA as well as its SHA1 hash */ #define LOCK_RELEASE_LUA_STR "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end" #define LOCK_RELEASE_LUA_LEN (sizeof(LOCK_RELEASE_LUA_STR) - 1) From 71922bf1dd30af17cf2939b1c5e9b33f7fe2a5c4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 21 Dec 2018 15:11:48 +0200 Subject: [PATCH 1055/1986] Add RedisArray::_continuum method --- redis_array.c | 33 +++++++++++++++++++++++++++++++++ redis_array.h | 1 + 2 files changed, 34 insertions(+) diff --git a/redis_array.c b/redis_array.c index df661a9f29..e4c62b4859 100644 --- a/redis_array.c +++ b/redis_array.c @@ -104,6 +104,7 @@ ZEND_END_ARG_INFO() zend_function_entry redis_array_functions[] = { PHP_ME(RedisArray, __call, arginfo_call, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, __construct, arginfo_ctor, ZEND_ACC_PUBLIC) + PHP_ME(RedisArray, _continuum, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, _distributor, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, _function, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisArray, _hosts, arginfo_void, ZEND_ACC_PUBLIC) @@ -639,6 +640,38 @@ PHP_METHOD(RedisArray, _rehash) } } +PHP_METHOD(RedisArray, _continuum) +{ + int i; + zval *object; + RedisArray *ra; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, redis_array_ce) == FAILURE) { + RETURN_FALSE; + } + + if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + RETURN_FALSE; + } + + array_init(return_value); + if (ra->continuum) { + for (i = 0; i < ra->continuum->nb_points; ++i) { + zval zv, *z_tmp = &zv; +#if (PHP_MAJOR_VERSION < 7) + MAKE_STD_ZVAL(z_tmp); +#endif + + array_init(z_tmp); + add_assoc_long(z_tmp, "index", ra->continuum->points[i].index); + add_assoc_long(z_tmp, "value", ra->continuum->points[i].value); + add_next_index_zval(return_value, z_tmp); + } + } +} + + static void multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int argc, zval *argv TSRMLS_DC) { diff --git a/redis_array.h b/redis_array.h index ba53fadcc9..9ccd6f6835 100644 --- a/redis_array.h +++ b/redis_array.h @@ -16,6 +16,7 @@ PHP_METHOD(RedisArray, _instance); PHP_METHOD(RedisArray, _function); PHP_METHOD(RedisArray, _distributor); PHP_METHOD(RedisArray, _rehash); +PHP_METHOD(RedisArray, _continuum); PHP_METHOD(RedisArray, select); PHP_METHOD(RedisArray, info); From 61889cd7ba9906f5016e8d492041c3c4415fe417 Mon Sep 17 00:00:00 2001 From: Marius Meissner Date: Thu, 27 Dec 2018 10:30:39 +0100 Subject: [PATCH 1056/1986] PHPREDIS-1412: Breaking the lock acquire loop in case of network problems --- redis_session.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/redis_session.c b/redis_session.c index e1f79c1702..2127f3e683 100644 --- a/redis_session.c +++ b/redis_session.c @@ -60,6 +60,7 @@ /* Check if a response is the Redis +OK status response */ #define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3)) +#define NEGATIVE_LOCK_RESPONSE 1 ps_module ps_mod_redis = { #if (PHP_MAJOR_VERSION < 7) @@ -143,20 +144,18 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { efree(pool); } -/* Send a command to Redis. Returns reply on success and NULL on failure */ -static char *redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, - int *replylen TSRMLS_DC) +/* Send a command to Redis. Returns byte count written to socket (-1 on failure) */ +static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, + char **reply, int *replylen TSRMLS_DC) { - char *reply; + *reply = NULL; + int len_written = redis_sock_write(redis_sock, cmd, cmdlen TSRMLS_CC); - if (redis_sock_write(redis_sock, cmd, cmdlen TSRMLS_CC) >= 0) { - if ((reply = redis_sock_read(redis_sock, replylen TSRMLS_CC)) != NULL) { - return reply; - } + if (len_written >= 0) { + *reply = redis_sock_read(redis_sock, replylen TSRMLS_CC); } - /* Failed to send or receive command */ - return NULL; + return len_written; } static void @@ -232,9 +231,9 @@ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len TSRMLS_DC) { char *reply; - int reply_len; + int sent_len, reply_len; - reply = redis_simple_cmd(redis_sock, cmd, cmd_len, &reply_len TSRMLS_CC); + sent_len = redis_simple_cmd(redis_sock, cmd, cmd_len, &reply, &reply_len TSRMLS_CC); if (reply) { if (IS_REDIS_OK(reply, reply_len)) { efree(reply); @@ -244,14 +243,15 @@ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len efree(reply); } - return FAILURE; + /* Return FAILURE in case of network problems */ + return sent_len >= 0 ? NEGATIVE_LOCK_RESPONSE : FAILURE; } static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) { char *cmd, hostname[HOST_NAME_MAX] = {0}, suffix[] = "_LOCK", pid[32]; - int cmd_len, lock_wait_time, retries, i, expiry; + int cmd_len, lock_wait_time, retries, i, set_lock_key_result, expiry; /* Short circuit if we are already locked or not using session locks */ if (lock_status->is_locked || !INI_INT("redis.session.locking_enabled")) @@ -301,9 +301,15 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s /* Attempt to get our lock */ for (i = 0; retries == -1 || i <= retries; i++) { - if (set_session_lock_key(redis_sock, cmd, cmd_len TSRMLS_CC) == SUCCESS) { + set_lock_key_result = set_session_lock_key(redis_sock, cmd, cmd_len TSRMLS_CC); + + if (set_lock_key_result == SUCCESS) { lock_status->is_locked = 1; break; + } else if (set_lock_key_result == FAILURE) { + /* In case of network problems, break the loop and report to userland */ + lock_status->is_locked = 0; + break; } /* Sleep unless we're done making attempts */ @@ -339,7 +345,7 @@ static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status cmdlen = REDIS_SPPRINTF(&cmd, "GET", "S", lock_status->lock_key); /* Attempt to refresh the lock */ - reply = redis_simple_cmd(redis_sock, cmd, cmdlen, &replylen TSRMLS_CC); + redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen TSRMLS_CC); if (reply != NULL) { lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret); efree(reply); @@ -389,7 +395,7 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_ lock_status->lock_key, lock_status->lock_secret); /* Send it off */ - reply = redis_simple_cmd(redis_sock, cmd, cmdlen, &replylen TSRMLS_CC); + redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen TSRMLS_CC); /* Release lock and cleanup reply if we got one */ if (reply != NULL) { From 850027ffd36ce224846d142fd3fb5344f08aef16 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 3 Jan 2019 16:08:09 +0200 Subject: [PATCH 1057/1986] Different key hashing algorithms from hash extension. --- redis.c | 1 + redis_array.c | 14 ++++++++++++-- redis_array.h | 1 + redis_array_impl.c | 44 ++++++++++++++++++++++++++++++++++++++------ redis_array_impl.h | 2 +- 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/redis.c b/redis.c index cf08d762d7..627f3e16df 100644 --- a/redis.c +++ b/redis.c @@ -55,6 +55,7 @@ extern zend_function_entry redis_cluster_functions[]; PHP_INI_BEGIN() /* redis arrays */ + PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.autorehash", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.connecttimeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL) diff --git a/redis_array.c b/redis_array.c index e4c62b4859..79cf0804a8 100644 --- a/redis_array.c +++ b/redis_array.c @@ -159,6 +159,9 @@ redis_array_free(RedisArray *ra) /* Distributor */ zval_dtor(&ra->z_dist); + /* Hashing algorithm */ + zval_dtor(&ra->z_algo); + /* Delete pur commands */ zend_hash_destroy(ra->pure_cmds); FREE_HASHTABLE(ra->pure_cmds); @@ -269,7 +272,7 @@ redis_array_get(zval *id TSRMLS_DC) Public constructor */ PHP_METHOD(RedisArray, __construct) { - zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; + zval *z0, z_fun, z_dist, z_algo, *zpData, *z_opts = NULL; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; HashTable *hPrev = NULL, *hOpts = NULL; @@ -284,6 +287,7 @@ PHP_METHOD(RedisArray, __construct) ZVAL_NULL(&z_fun); ZVAL_NULL(&z_dist); + ZVAL_NULL(&z_algo); /* extract options */ if(z_opts) { hOpts = Z_ARRVAL_P(z_opts); @@ -306,6 +310,11 @@ PHP_METHOD(RedisArray, __construct) ZVAL_ZVAL(&z_dist, zpData, 1, 0); } + /* extract function name. */ + if ((zpData = zend_hash_str_find(hOpts, "algorithm", sizeof("algorithm") - 1)) != NULL && Z_TYPE_P(zpData) == IS_STRING) { + ZVAL_ZVAL(&z_algo, zpData, 1, 0); + } + /* extract index option. */ if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL) { b_index = zval_is_true(zpData); @@ -370,12 +379,13 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, &z_algo, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); break; default: WRONG_PARAM_COUNT; } + zval_dtor(&z_algo); zval_dtor(&z_dist); zval_dtor(&z_fun); diff --git a/redis_array.h b/redis_array.h index 9ccd6f6835..de44465426 100644 --- a/redis_array.h +++ b/redis_array.h @@ -59,6 +59,7 @@ typedef struct RedisArray_ { zend_bool pconnect; /* should we use pconnect */ zval z_fun; /* key extractor, callable */ zval z_dist; /* key distributor, callable */ + zval z_algo; /* key hashing algorithm name */ HashTable *pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ double read_timeout; /* socket read timeout */ diff --git a/redis_array_impl.c b/redis_array_impl.c index c5d4796c3e..8505129387 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -26,6 +26,8 @@ #include "ext/standard/crc32.h" #include "ext/standard/md5.h" +#include "ext/hash/php_hash.h" + #define PHPREDIS_INDEX_NAME "__phpredis_array_index__" extern zend_class_entry *redis_ce; @@ -162,11 +164,12 @@ ra_find_name(const char *name) { /* laod array from INI settings */ RedisArray *ra_load_array(const char *name TSRMLS_DC) { - zval *z_data, z_fun, z_dist; + zval *z_data, z_fun, z_dist, z_algo; zval z_params_hosts; zval z_params_prev; zval z_params_funs; zval z_params_dist; + zval z_params_algo; zval z_params_index; zval z_params_autorehash; zval z_params_retry_interval; @@ -227,6 +230,16 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { ZVAL_ZVAL(&z_dist, z_data, 1, 0); } + /* find hash algorithm */ + array_init(&z_params_algo); + if ((iptr = INI_STR("redis.arrays.algorithm")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_algo TSRMLS_CC); + } + ZVAL_NULL(&z_algo); + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_algo), name, name_len)) != NULL) { + ZVAL_ZVAL(&z_algo, z_data, 1, 0); + } + /* find index option */ array_init(&z_params_index); if ((iptr = INI_STR("redis.arrays.index")) != NULL) { @@ -327,7 +340,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, &z_algo, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; @@ -338,6 +351,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_prev); zval_dtor(&z_params_funs); zval_dtor(&z_params_dist); + zval_dtor(&z_params_algo); zval_dtor(&z_params_index); zval_dtor(&z_params_autorehash); zval_dtor(&z_params_retry_interval); @@ -346,6 +360,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); zval_dtor(&z_params_consistent); + zval_dtor(&z_algo); zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -393,7 +408,7 @@ ra_make_continuum(zend_string **hosts, int nb_hosts) } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC) { int i, count; RedisArray *ra; @@ -423,7 +438,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, z_algo, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); @@ -431,6 +446,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* Set hash function and distribtor if provided */ ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); ZVAL_ZVAL(&ra->z_dist, z_dist, 1, 0); + ZVAL_ZVAL(&ra->z_algo, z_algo, 1, 0); /* init continuum */ if (consistent) { @@ -533,10 +549,26 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D if (Z_TYPE(ra->z_dist) == IS_NULL) { int i; unsigned long ret = 0xffffffff; + const php_hash_ops *ops; /* hash */ - for (i = 0; i < ZSTR_LEN(out); ++i) { - CRC32(ret, ZSTR_VAL(out)[i]); + if (Z_TYPE(ra->z_algo) == IS_STRING && (ops = php_hash_fetch_ops(Z_STRVAL(ra->z_algo), Z_STRLEN(ra->z_algo))) != NULL) { + void *ctx = emalloc(ops->context_size); + unsigned char *digest = emalloc(ops->digest_size); + + ops->hash_init(ctx); + ops->hash_update(ctx, ZSTR_VAL(out), ZSTR_LEN(out)); + ops->hash_final(digest, ctx); + + memcpy(&ret, digest, MIN(sizeof(ret), ops->digest_size)); + ret %= 0xffffffff; + + efree(digest); + efree(ctx); + } else { + for (i = 0; i < ZSTR_LEN(out); ++i) { + CRC32(ret, ZSTR_VAL(out)[i]); + } } /* get position on ring */ diff --git a/redis_array_impl.h b/redis_array_impl.h index fa5fd848b4..43705ee591 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -11,7 +11,7 @@ RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); From e98f51165bba319e27003af1a21022cffcc285de Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 9 Jan 2019 10:43:19 +0200 Subject: [PATCH 1058/1986] Use zend_string for pipeline_cmd --- common.h | 34 +++++++++++++++++++++++++--------- library.c | 3 +-- redis.c | 7 +++---- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/common.h b/common.h index adbe3035dd..9642fdd45b 100644 --- a/common.h +++ b/common.h @@ -34,7 +34,7 @@ zend_string_alloc(size_t len, int persistent) zend_string *zstr = emalloc(sizeof(*zstr) + len + 1); ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); - zstr->len = len; + ZSTR_LEN(zstr) = len; zstr->gc = 0x01; return zstr; } @@ -49,6 +49,25 @@ zend_string_init(const char *str, size_t len, int persistent) return zstr; } +static zend_always_inline zend_string * +zend_string_realloc(zend_string *s, size_t len, int persistent) +{ + zend_string *zstr; + + if (!s->gc) { + zstr = zend_string_init(ZSTR_VAL(s), len, 0); + } else if (s->gc & 0x10) { + ZSTR_VAL(s) = erealloc(ZSTR_VAL(s), len + 1); + ZSTR_LEN(s) = len; + zstr = s; + } else { + zstr = erealloc(s, sizeof(*zstr) + len + 1); + ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); + ZSTR_LEN(zstr) = len; + } + return zstr; +} + #define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) #define zend_string_equal_content(s1, s2) (ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2)) #define zend_string_equals(s1, s2) (s1 == s2 || zend_string_equal_content(s1, s2)) @@ -533,14 +552,12 @@ typedef enum _PUBSUB_TYPE { #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \ if (redis_sock->pipeline_cmd == NULL) { \ - redis_sock->pipeline_cmd = estrndup(cmd, cmd_len); \ + redis_sock->pipeline_cmd = zend_string_init(cmd, cmd_len, 0); \ } else { \ - redis_sock->pipeline_cmd = erealloc(redis_sock->pipeline_cmd, \ - redis_sock->pipeline_len + cmd_len); \ - memcpy(&redis_sock->pipeline_cmd[redis_sock->pipeline_len], \ - cmd, cmd_len); \ + size_t pipeline_len = ZSTR_LEN(redis_sock->pipeline_cmd); \ + redis_sock->pipeline_cmd = zend_string_realloc(redis_sock->pipeline_cmd, pipeline_len + cmd_len, 0); \ + memcpy(&ZSTR_VAL(redis_sock->pipeline_cmd)[pipeline_len], cmd, cmd_len); \ } \ - redis_sock->pipeline_len += cmd_len; \ } while (0) #define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) \ @@ -675,8 +692,7 @@ typedef struct { fold_item *head; fold_item *current; - char *pipeline_cmd; - size_t pipeline_len; + zend_string *pipeline_cmd; zend_string *err; diff --git a/library.c b/library.c index d3278a871c..79015d4b84 100644 --- a/library.c +++ b/library.c @@ -1724,7 +1724,6 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->current = NULL; redis_sock->pipeline_cmd = NULL; - redis_sock->pipeline_len = 0; redis_sock->err = NULL; @@ -2104,7 +2103,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) zend_string_release(redis_sock->prefix); } if (redis_sock->pipeline_cmd) { - efree(redis_sock->pipeline_cmd); + zend_string_release(redis_sock->pipeline_cmd); } if (redis_sock->err) { zend_string_release(redis_sock->err); diff --git a/redis.c b/redis.c index 6f712d75a4..e12a59c5e6 100644 --- a/redis.c +++ b/redis.c @@ -2387,17 +2387,16 @@ PHP_METHOD(Redis, exec) /* Empty array when no command was run. */ array_init(return_value); } else { - if (redis_sock_write(redis_sock, redis_sock->pipeline_cmd, - redis_sock->pipeline_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd), + ZSTR_LEN(redis_sock->pipeline_cmd) TSRMLS_CC) < 0) { ZVAL_FALSE(return_value); } else { array_init(return_value); redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, return_value, 0); } - efree(redis_sock->pipeline_cmd); + zend_string_release(redis_sock->pipeline_cmd); redis_sock->pipeline_cmd = NULL; - redis_sock->pipeline_len = 0; } free_reply_callbacks(redis_sock); REDIS_DISABLE_MODE(redis_sock, PIPELINE); From 789256d705becd8bc876ffd5569a6edbe2b53fc4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 8 Jan 2019 13:00:07 +0200 Subject: [PATCH 1059/1986] Issue #1492 --- redis.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index e12a59c5e6..42cdbc6c5a 100644 --- a/redis.c +++ b/redis.c @@ -2298,6 +2298,7 @@ PHP_METHOD(Redis, multi) /* discard */ PHP_METHOD(Redis, discard) { + int ret = FAILURE; RedisSock *redis_sock; zval *object; @@ -2310,9 +2311,21 @@ PHP_METHOD(Redis, discard) RETURN_FALSE; } - redis_sock->mode = ATOMIC; - free_reply_callbacks(redis_sock); - RETURN_BOOL(redis_send_discard(redis_sock TSRMLS_CC) == SUCCESS); + if (IS_PIPELINE(redis_sock)) { + ret = SUCCESS; + if (redis_sock->pipeline_cmd) { + zend_string_release(redis_sock->pipeline_cmd); + redis_sock->pipeline_cmd = NULL; + } + } else if (IS_MULTI(redis_sock)) { + ret = redis_send_discard(redis_sock TSRMLS_CC); + } + if (ret == SUCCESS) { + free_reply_callbacks(redis_sock); + redis_sock->mode = ATOMIC; + RETURN_TRUE; + } + RETURN_FALSE; } /* redis_sock_read_multibulk_multi_reply */ From 018ec177a7b8fccac286e769eb63f0feb31f4f41 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 10 Jan 2019 21:55:57 +0200 Subject: [PATCH 1060/1986] Add testDiscard --- tests/RedisClusterTest.php | 11 +++++++++++ tests/RedisTest.php | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 74515d4fd8..ccd8927ac5 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -319,6 +319,17 @@ public function testFailedTransactions() { $this->assertTrue($ret === array('44')); } + public function testDiscard() + { + /* start transaction */ + $this->redis->multi(); + + /* Set and get in our transaction */ + $this->redis->set('pipecount','over9000')->get('pipecount'); + + $this->assertTrue($this->redis->discard()); + } + /* RedisCluster::script() is a 'raw' command, which requires a key such that * we can direct it to a given node */ public function testScript() { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 8c358b6e43..7f6adb0753 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2715,6 +2715,23 @@ public function testDoublePipeNoOp() { $this->assertEquals(Array(true, 'over9000'), $data); } + public function testDiscard() + { + foreach (Array(Redis::PIPELINE, Redis::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) From f6380cb828133488a50e173b6f91a303635810f3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 11 Jan 2019 10:04:56 +0200 Subject: [PATCH 1061/1986] Add documentation --- arrays.markdown | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arrays.markdown b/arrays.markdown index c41ef616cb..cd6394d159 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -61,6 +61,19 @@ The read_timeout value is a double and is used to specify a timeout in number of $ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("read_timeout" => 0.5)); +#### Specifying the "algorithm" parameter +The algorithm value is a string and is used to specify the name of key hashing algorithm. The list of possible values may be found using PHP function `hash_algos`. +If algorithm is not supported by PHP `hash` function default algorithm will be used (CRC32 with 0xffffffff initial value). +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("algorithm" => "md5"));
+
+ +#### Specifying the "consistent" parameter +The value is boolean. When enabled RedisArray uses "ketama" distribution algorithm (currently without ability to set weight to each server). +This option applies to main and previous ring if specified. +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("consistent" => true));
+
#### Defining arrays in Redis.ini @@ -152,6 +165,7 @@ RedisArray objects provide several methods to help understand the state of the c * `$ra->_function()` → returns the name of the function used to extract key parts during consistent hashing. * `$ra->_target($key)` → returns the host to be used for a certain key. * `$ra->_instance($host)` → returns a redis instance connected to a specific node; use with `_target` to get a single Redis object. +* `$ra->_continuum()` → returns a list of points on continuum; may be useful with custom distributor function. ## Running the unit tests

From 5c8e59c4ee6ba3b4eb527298af415b80dd0d55fd Mon Sep 17 00:00:00 2001
From: hmc 
Date: Wed, 16 Jan 2019 01:40:13 +0800
Subject: [PATCH 1062/1986] Syntax error in zRangeByScore method example

Missing parentheses in the zRangeByScore method example
---
 README.markdown | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.markdown b/README.markdown
index 22593facaf..372a9ae1db 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2771,9 +2771,9 @@ $redis->zAdd('key', 0, 'val0');
 $redis->zAdd('key', 2, 'val2');
 $redis->zAdd('key', 10, 'val10');
 $redis->zRangeByScore('key', 0, 3); /* array('val0', 'val2') */
-$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE); /* array('val0' => 0, 'val2' => 2) */
-$redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2') */
-$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1)); /* array('val2' => 2) */
+$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); /* array('val0' => 0, 'val2' => 2) */
+$redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1))); /* array('val2') */
+$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1))); /* array('val2' => 2) */
 ~~~
 
 ### zRangeByLex

From c5994f2a42b8a348af92d3acb4edff1328ad8ce1 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 15 Jan 2019 10:07:30 +0200
Subject: [PATCH 1063/1986] RedisCluster auth

---
 cluster.markdown  |  7 ++++++-
 cluster_library.c | 31 +++++++++++++++++++++++++------
 cluster_library.h |  4 +++-
 common.h          |  2 ++
 library.c         |  6 ++++--
 library.h         |  1 +
 redis.c           |  1 +
 redis_cluster.c   | 44 +++++++++++++++++++++++++++++++++-----------
 redis_session.c   | 19 +++++++++++++++----
 9 files changed, 90 insertions(+), 25 deletions(-)

diff --git a/cluster.markdown b/cluster.markdown
index 4dcbc2ba19..3260bb7953 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -19,6 +19,9 @@ $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5)
 // persistent connections to each node.
 $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true);
 
+// Connect with cluster using password.
+$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, "password");
+
 
#### Loading a cluster configuration by name @@ -29,6 +32,7 @@ In order to load a named array, one must first define the seed nodes in redis.in redis.clusters.seeds = "mycluster[]=localhost:7000&test[]=localhost:7001" redis.clusters.timeout = "mycluster=5" redis.clusters.read_timeout = "mycluster=10" +redis.clusters.auth = "mycluster=password" Then, this cluster can be loaded by doing the following @@ -161,7 +165,7 @@ To do this, you must configure your `session.save_handler` and `session.save_pat ~~~ session.save_handler = rediscluster -session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1" +session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1&auth=password" ~~~ ### session.session_handler @@ -175,5 +179,6 @@ The save path for cluster based session storage takes the form of a PHP GET requ * _persistent_: Tells phpredis whether persistent connections should be used. * _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing). * _failover (string)_: How phpredis should distribute session reads between master and slave nodes. +* _auth (string, empty by default)_: The password used to authenticate with the server prior to sending commands. * * _none_ : phpredis will only communicate with master nodes * * _error_: phpredis will communicate with master nodes unless one failes, in which case an attempt will be made to read session information from a slave. diff --git a/cluster_library.c b/cluster_library.c index 1f6d416043..b0f7b53a0c 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -259,6 +259,17 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, return r; } +/* Helper to open connection and send AUTH if necessary */ +static zend_always_inline int +cluster_sock_open(RedisSock *redis_sock TSRMLS_DC) +{ + zend_bool need_auth = (redis_sock->auth && redis_sock->status != REDIS_SOCK_STATUS_CONNECTED); + if (!redis_sock_server_open(redis_sock TSRMLS_CC) && (!need_auth || !redis_sock_auth(redis_sock TSRMLS_CC))) { + return SUCCESS; + } + return FAILURE; +} + /* * Helpers to send various 'control type commands to a specific node, e.g. * MULTI, ASKING, READONLY, READWRITE, etc @@ -654,6 +665,10 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, node->sock = redis_sock_create(host, host_len, port, c->timeout, c->read_timeout, c->persistent, NULL, 0); + if (c->auth) { + node->sock->auth = zend_string_copy(c->auth); + } + return node; } @@ -824,6 +839,7 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, c->read_timeout = read_timeout; c->failover = failover; c->persistent = persistent; + c->auth = NULL; c->err = NULL; /* Set up our waitms based on timeout */ @@ -858,6 +874,9 @@ cluster_free(redisCluster *c, int free_ctx TSRMLS_DC) efree(c->seeds); efree(c->nodes); + /* Free auth info we've got */ + if (c->auth) zend_string_release(c->auth); + /* Free any error we've got */ if (c->err) zend_string_release(c->err); @@ -925,6 +944,11 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { (unsigned short)atoi(psep+1), cluster->timeout, cluster->read_timeout, cluster->persistent, NULL, 0); + // Set auth information if specified + if (cluster->auth) { + redis_sock->auth = zend_string_copy(cluster->auth); + } + // Index this seed by host/port key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(redis_sock->host), redis_sock->port); @@ -948,7 +972,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { // Iterate over seeds until we can get slots ZEND_HASH_FOREACH_PTR(c->seeds, seed) { // Attempt to connect to this seed node - if (seed == NULL || redis_sock_connect(seed TSRMLS_CC) != 0) { + if (seed == NULL || cluster_sock_open(seed TSRMLS_CC) != 0) { continue; } @@ -1119,11 +1143,6 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, redis_sock = cluster_slot_sock(c, c->cmd_slot, nodes[i]); if (!redis_sock) continue; - /* Connect to this node if we haven't already */ - if(redis_sock_server_open(redis_sock TSRMLS_CC)) { - continue; - } - /* If we're not on the master, attempt to send the READONLY command to * this slave, and skip it if that fails */ if (nodes[i] == 0 || redis_sock->readonly || diff --git a/cluster_library.h b/cluster_library.h index 979b228ddc..366b395a66 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -62,7 +62,7 @@ /* Protected sending of data down the wire to a RedisSock->stream */ #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \ - (sock && !redis_sock_server_open(sock TSRMLS_CC) && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \ + (sock && !cluster_sock_open(sock TSRMLS_CC) && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \ php_stream_write(sock->stream, buf, len)==len) /* Macro to read our reply type character */ @@ -170,6 +170,8 @@ typedef struct redisCluster { zend_object std; #endif + zend_string *auth; + /* Timeout and read timeout (for normal operations) */ double timeout; double read_timeout; diff --git a/common.h b/common.h index 9642fdd45b..e17d760f09 100644 --- a/common.h +++ b/common.h @@ -68,6 +68,8 @@ zend_string_realloc(zend_string *s, size_t len, int persistent) return zstr; } +#define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0) + #define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) #define zend_string_equal_content(s1, s2) (ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2)) #define zend_string_equals(s1, s2) (s1 == s2 || zend_string_equal_content(s1, s2)) diff --git a/library.c b/library.c index 79015d4b84..93a4460069 100644 --- a/library.c +++ b/library.c @@ -90,7 +90,9 @@ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { } /* Helper to resend AUTH in the case of a reconnect */ -static int resend_auth(RedisSock *redis_sock TSRMLS_DC) { +PHP_REDIS_API int +redis_sock_auth(RedisSock *redis_sock TSRMLS_DC) +{ char *cmd, *response; int cmd_len, response_len; @@ -205,7 +207,7 @@ redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) errno = 0; if (php_stream_eof(redis_sock->stream) == 0) { /* If we're using a password, attempt a reauthorization */ - if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) { + if (redis_sock->auth && redis_sock_auth(redis_sock TSRMLS_CC) != 0) { errmsg = "AUTH failed while reconnecting"; break; } diff --git a/library.h b/library.h index 381a63bc14..8e82081e88 100644 --- a/library.h +++ b/library.h @@ -45,6 +45,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC); +PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, diff --git a/redis.c b/redis.c index 42cdbc6c5a..e88eb57245 100644 --- a/redis.c +++ b/redis.c @@ -69,6 +69,7 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.arrays.retryinterval", "0", PHP_INI_ALL, NULL) /* redis cluster */ + PHP_INI_ENTRY("redis.clusters.auth", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.read_timeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) diff --git a/redis_cluster.c b/redis_cluster.c index e8cd4038df..9bc4254707 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -46,6 +46,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, read_timeout) ZEND_ARG_INFO(0, persistent) + ZEND_ARG_INFO(0, auth) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) @@ -397,8 +398,10 @@ free_cluster_context(zend_object *object) #endif /* Attempt to connect to a Redis cluster provided seeds and timeout options */ -void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, - double read_timeout, int persistent TSRMLS_DC) +static void +redis_cluster_init(redisCluster *c, HashTable *ht_seeds, + double timeout, double read_timeout, int persistent, + char *auth, strlen_t auth_len TSRMLS_DC) { // Validate timeout if (timeout < 0L || timeout > INT_MAX) { @@ -417,6 +420,11 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, zend_throw_exception(redis_cluster_exception_ce, "Must pass seeds", 0 TSRMLS_CC); } + + if (auth && auth_len > 0) { + c->auth = zend_string_init(auth, auth_len, 0); + } + /* Set our timeout and read_timeout which we'll pass through to the * socket type operations */ c->timeout = timeout; @@ -438,8 +446,9 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, /* Attempt to load a named cluster configured in php.ini */ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { - zval z_seeds, z_timeout, z_read_timeout, z_persistent, *z_value; - char *iptr; + zval z_seeds, z_timeout, z_read_timeout, z_persistent, z_auth, *z_value; + char *iptr, *auth = NULL; + strlen_t auth_len = 0; double timeout = 0, read_timeout = 0; int persistent = 0; HashTable *ht_seeds = NULL; @@ -500,14 +509,27 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { } } + /* Cluster auth */ + array_init(&z_auth); + if ((iptr = INI_STR("redis.clusters.auth")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_auth TSRMLS_CC); + } + if ((z_value = zend_hash_str_find(Z_ARRVAL(z_auth), name, name_len)) != NULL && + Z_TYPE_P(z_value) == IS_STRING && Z_STRLEN_P(z_value) > 0 + ) { + auth = Z_STRVAL_P(z_value); + auth_len = Z_STRLEN_P(z_value); + } + /* Attempt to create/connect to the cluster */ - redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent TSRMLS_CC); + redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, auth, auth_len TSRMLS_CC); /* Clean up our arrays */ zval_dtor(&z_seeds); zval_dtor(&z_timeout); zval_dtor(&z_read_timeout); zval_dtor(&z_persistent); + zval_dtor(&z_auth); } /* @@ -517,17 +539,17 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Create a RedisCluster Object */ PHP_METHOD(RedisCluster, __construct) { zval *object, *z_seeds = NULL; - char *name; - strlen_t name_len; + char *name, *auth = NULL; + strlen_t name_len, auth_len = 0; double timeout = 0.0, read_timeout = 0.0; zend_bool persistent = 0; redisCluster *context = GET_CONTEXT(); // Parse arguments if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os!|addb", &object, redis_cluster_ce, &name, - &name_len, &z_seeds, &timeout, - &read_timeout, &persistent) == FAILURE) + "Os!|addbs", &object, redis_cluster_ce, &name, + &name_len, &z_seeds, &timeout, &read_timeout, + &persistent, &auth, &auth_len) == FAILURE) { RETURN_FALSE; } @@ -543,7 +565,7 @@ PHP_METHOD(RedisCluster, __construct) { * to a named cluster, stored in php.ini, otherwise we'll need manual seeds */ if (ZEND_NUM_ARGS() > 1) { redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout, - persistent TSRMLS_CC); + persistent, auth, auth_len TSRMLS_CC); } else { redis_cluster_load(context, name, name_len TSRMLS_CC); } diff --git a/redis_session.c b/redis_session.c index 2127f3e683..31f6b149dc 100644 --- a/redis_session.c +++ b/redis_session.c @@ -992,9 +992,9 @@ PS_OPEN_FUNC(rediscluster) { zval z_conf, *z_val; HashTable *ht_conf, *ht_seeds; double timeout = 0, read_timeout = 0; - int persistent = 0; - int retval, prefix_len, failover = REDIS_FAILOVER_NONE; - char *prefix; + int retval, persistent = 0, failover = REDIS_FAILOVER_NONE; + strlen_t prefix_len, auth_len = 0; + char *prefix, *auth = NULL; /* Parse configuration for session handler */ array_init(&z_conf); @@ -1041,7 +1041,7 @@ PS_OPEN_FUNC(rediscluster) { /* Look for a specific failover setting */ if ((z_val = zend_hash_str_find(ht_conf, "failover", sizeof("failover") - 1)) != NULL && - Z_TYPE_P(z_val) == IS_STRING + Z_TYPE_P(z_val) == IS_STRING && Z_STRLEN_P(z_val) > 0 ) { if (!strcasecmp(Z_STRVAL_P(z_val), "error")) { failover = REDIS_FAILOVER_ERROR; @@ -1050,7 +1050,18 @@ PS_OPEN_FUNC(rediscluster) { } } + /* Look for a specific auth setting */ + if ((z_val = zend_hash_str_find(ht_conf, "auth", sizeof("auth") - 1)) != NULL && + Z_TYPE_P(z_val) == IS_STRING && Z_STRLEN_P(z_val) > 0 + ) { + auth = Z_STRVAL_P(z_val); + auth_len = Z_STRLEN_P(z_val); + } + c = cluster_create(timeout, read_timeout, failover, persistent); + if (auth && auth_len > 0) { + c->auth = zend_string_init(auth, auth_len, 0); + } if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) { /* Set up our prefix */ c->flags->prefix = zend_string_init(prefix, prefix_len, 0); From e9e47834560fd2e2b118150572eae3379a245cb3 Mon Sep 17 00:00:00 2001 From: Pavel Yatsukhnenko Date: Mon, 21 Jan 2019 11:56:15 +0200 Subject: [PATCH 1064/1986] Fix build warning --- redis_array_impl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index 8505129387..4ee72d8a0a 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -557,7 +557,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D unsigned char *digest = emalloc(ops->digest_size); ops->hash_init(ctx); - ops->hash_update(ctx, ZSTR_VAL(out), ZSTR_LEN(out)); + ops->hash_update(ctx, (const unsigned char *)ZSTR_VAL(out), ZSTR_LEN(out)); ops->hash_final(digest, ctx); memcpy(&ret, digest, MIN(sizeof(ret), ops->digest_size)); From 15995c06e39fee6249a0fc584b50e8893521fe52 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Thu, 24 Jan 2019 08:36:58 -0800 Subject: [PATCH 1065/1986] Xgroup updates (#1499) Adds support for XGROUP CREATE ... MKSTREAM, and support at all for XGROUP DESTROY. --- redis_commands.c | 24 +++++++++++++++++------- tests/RedisTest.php | 11 +++++++++-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 7548d7423d..9dbb40f7c6 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3722,20 +3722,21 @@ int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* XGROUP HELP + * XGROUP CREATE key groupname id [MKSTREAM] * XGROUP SETID key group id - * XGROUP DELGROUP key groupname - * XGROUP CREATE key groupname id + * XGROUP DESTROY key groupname * XGROUP DELCONSUMER key groupname consumername */ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *op, *key = NULL, *arg1 = NULL, *arg2 = NULL; strlen_t oplen, keylen, arg1len, arg2len; + zend_bool mkstream = 0; int argc = ZEND_NUM_ARGS(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ssss", &op, &oplen, - &key, &keylen, &arg1, &arg1len, &arg2, &arg2len) - == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sssb", &op, &oplen, + &key, &keylen, &arg1, &arg1len, &arg2, &arg2len, + &mkstream) == FAILURE) { return FAILURE; } @@ -3743,14 +3744,23 @@ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (argc == 1 && oplen == 4 && !strncasecmp(op, "HELP", 4)) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "s", "HELP", 4); return SUCCESS; + } else if (argc >= 4 && (oplen == 6 && !strncasecmp(op, "CREATE", 6))) { + if (mkstream) { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "sksss", op, oplen, key, keylen, + arg1, arg1len, arg2, arg2len, "MKSTREAM", + sizeof("MKSTREAM") - 1); + } else { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "skss", op, oplen, key, keylen, + arg1, arg1len, arg2, arg2len); + } + return SUCCESS; } else if (argc == 4 && ((oplen == 5 && !strncasecmp(op, "SETID", 5)) || - (oplen == 6 && !strncasecmp(op, "CREATE", 6)) || (oplen == 11 && !strncasecmp(op, "DELCONSUMER", 11)))) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "skss", op, oplen, key, keylen, arg1, arg1len, arg2, arg2len); return SUCCESS; - } else if (argc == 3 && ((oplen == 7 && !strncasecmp(op, "DELGROUP", 7)))) { + } else if (argc == 3 && ((oplen == 7 && !strncasecmp(op, "DESTROY", 7)))) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "sks", op, oplen, key, keylen, arg1, arg1len); return SUCCESS; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 7f6adb0753..9887daac54 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5384,6 +5384,15 @@ 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', 0, true)); + + /* XGROUP DESTROY */ + $this->assertEquals($this->redis->xGroup('DESTROY', $str_key, 'g1'), 1); + + /* Populate some entries in stream 's' */ $this->addStreamEntries('s', 2); /* CREATE */ @@ -5399,8 +5408,6 @@ public function testXGroup() { $this->assertFalse($this->redis->xGroup('SETID', 's', 'mygroup', 'BAD_ID')); $this->assertEquals($this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer'),0); - - /* DELGROUP not yet implemented in Redis */ } public function testXAck() { From 627be58e7386b611e5db63c38362c04a2791d656 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 24 Jan 2019 08:41:27 -0800 Subject: [PATCH 1066/1986] Update XGROUP documentation --- README.markdown | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index 372a9ae1db..7f92fbe5b5 100644 --- a/README.markdown +++ b/README.markdown @@ -3403,9 +3403,9 @@ $obj_redis->xDel('mystream', ['1530115304877-0', '1530115305731-0']); ##### *Prototype* ~~~php $obj_redis->xGroup('HELP'); +$obj_redis->xGroup('CREATE', $str_key, $str_group, $str_msg_id, [$boo_mkstream]); $obj_redis->xGroup('SETID', $str_key, $str_group, $str_msg_id); -$obj_redis->xGroup('DELGROUP', $str_key, $str_group); -$obj_redis->xGroup('CREATE', $str_key, $str_group, $str_msg_id); +$obj_redis->xGroup('DESTROY', $str_key, $str_group); $obj_redis->xGroup('DELCONSUMER', $str_key, $str_group, $str_consumer_name); ~~~ @@ -3417,7 +3417,8 @@ _**Description**_: This command is used in order to create, destroy, or manage ##### *Example* ~~~php $obj_redis->xGroup('CREATE', 'mystream', 'mygroup'); -$obj_redis->xGroup('DELGROUP', 'mystream', 'mygroup'); +$obj_redis->xGroup('CREATE', 'mystream', 'mygroup2', true); /* Create stream if non-existent. */ +$obj_redis->xGroup('DESTROY', 'mystream', 'mygroup'); ~~~ ### xInfo From f9928642b5e539bbdca43ec51ed9c9642cb42ded Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 26 Jan 2019 20:04:23 -0800 Subject: [PATCH 1067/1986] PHP 5 is dead, long live PHP 7 This commit removes support for PHP 5 by getting rid of all of our Zend compatibility layer methods, as well as any call checking against PHP_MAJOR_VERSION or ZEND_MODULE_API_NO. Unit tests are all passing for Redis, RedisCluster, and RedisArray but this should still be considered a work in progress until more testing can be done. Addresses issue #1448 --- .travis.yml | 12 -- cluster_library.c | 186 +++++------------ cluster_library.h | 8 +- common.h | 501 +-------------------------------------------- library.c | 414 +++++++++++++------------------------ library.h | 8 +- package.xml | 2 +- redis.c | 111 +++------- redis_array.c | 179 +++------------- redis_array.h | 7 - redis_array_impl.c | 54 +---- redis_cluster.c | 144 +++---------- redis_cluster.h | 13 +- redis_commands.c | 140 ++++++------- redis_session.c | 103 +--------- redis_session.h | 2 - 16 files changed, 378 insertions(+), 1506 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c84d018a1..4829561f76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,6 @@ sudo: required language: php php: - - 5.4 - - 5.5 - - 5.6 - 7.0 - 7.1 - 7.2 @@ -16,15 +13,6 @@ matrix: env: CC=clang - php: nightly include: - # php 5.3 is only available on precise - - php: 5.3 - dist: precise - - php: 5.4 - env: CC=clang - - php: 5.5 - env: CC=clang - - php: 5.6 - env: CC=clang - php: 7.0 env: CC=clang - php: 7.1 diff --git a/cluster_library.c b/cluster_library.c index b0f7b53a0c..bcc5fb0782 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -361,12 +361,7 @@ PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { * */ /* Free cluster distribution list inside a HashTable */ -#if (PHP_MAJOR_VERSION < 7) -static void cluster_dist_free_ht(void *p) -#else -static void cluster_dist_free_ht(zval *p) -#endif -{ +static void cluster_dist_free_ht(zval *p) { clusterDistList *dl = *(clusterDistList**)p; int i; @@ -435,7 +430,7 @@ static clusterKeyVal *cluster_dl_add_key(clusterDistList *dl, char *key, /* Add a key, returning a pointer to the entry where passed for easy adding * of values to match this key */ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, - strlen_t key_len, clusterKeyVal **kv) + size_t key_len, clusterKeyVal **kv) { int key_free; short slot; @@ -472,7 +467,7 @@ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val TSRMLS_DC) { char *val; - strlen_t val_len; + size_t val_len; int val_free; // Serialize our value @@ -523,12 +518,7 @@ cluster_set_err(redisCluster *c, char *err, int err_len) } /* Destructor for slaves */ -#if (PHP_MAJOR_VERSION < 7) -static void ht_free_slave(void *data) -#else -static void ht_free_slave(zval *data) -#endif -{ +static void ht_free_slave(zval *data) { if (*(redisClusterNode**)data) { cluster_free_node(*(redisClusterNode**)data); } @@ -801,23 +791,13 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { } /* Our context seeds will be a hash table with RedisSock* pointers */ -#if (PHP_MAJOR_VERSION < 7) -static void ht_free_seed(void *data) -#else -static void ht_free_seed(zval *data) -#endif -{ +static void ht_free_seed(zval *data) { RedisSock *redis_sock = *(RedisSock**)data; if (redis_sock) redis_free_socket(redis_sock); } /* Free redisClusterNode objects we've stored */ -#if (PHP_MAJOR_VERSION < 7) -static void ht_free_node(void *data) -#else -static void ht_free_node(zval *data) -#endif -{ +static void ht_free_node(zval *data) { redisClusterNode *node = *(redisClusterNode**)data; cluster_free_node(node); } @@ -1564,13 +1544,9 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster CLUSTER_RETURN_STRING(c, resp, c->reply_len); } } else { - zval zv, *z = &zv; - if (redis_unpack(c->flags, resp, c->reply_len, z TSRMLS_CC)) { -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); - *z = zv; -#endif - add_next_index_zval(&c->multi_resp, z); + zval z_unpacked; + if (redis_unpack(c->flags, resp, c->reply_len, &z_unpacked TSRMLS_CC)) { + add_next_index_zval(&c->multi_resp, &z_unpacked); } else { add_next_index_stringl(&c->multi_resp, resp, c->reply_len); } @@ -1705,13 +1681,8 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * } // Set up our callback pointers -#if (PHP_MAJOR_VERSION < 7) - zval *z_ret, **z_args[4]; - sctx->cb.retval_ptr_ptr = &z_ret; -#else zval z_ret, z_args[4]; sctx->cb.retval = &z_ret; -#endif sctx->cb.params = z_args; sctx->cb.no_separation = 0; @@ -1753,19 +1724,6 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * } // Always pass our object through -#if (PHP_MAJOR_VERSION < 7) - z_args[0] = &getThis(); - - // Set up calbacks depending on type - if (is_pmsg) { - z_args[1] = &z_pat; - z_args[2] = &z_chan; - z_args[3] = &z_data; - } else { - z_args[1] = &z_chan; - z_args[2] = &z_data; - } -#else z_args[0] = *getThis(); // Set up calbacks depending on type @@ -1777,7 +1735,6 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * z_args[1] = *z_chan; z_args[2] = *z_data; } -#endif // Set arg count sctx->cb.param_count = tab_idx; @@ -1851,7 +1808,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, /* Recursive MULTI BULK -> PHP style response handling */ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) { - zval zv, *z_sub_ele = &zv; + zval z_sub_ele; int i; switch(r->type) { @@ -1873,14 +1830,11 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret) } break; case TYPE_MULTIBULK: -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_sub_ele); -#endif - array_init(z_sub_ele); + array_init(&z_sub_ele); for (i = 0; i < r->elements; i++) { - cluster_mbulk_variant_resp(r->element[i], z_sub_ele); + cluster_mbulk_variant_resp(r->element[i], &z_sub_ele); } - add_next_index_zval(z_ret, z_sub_ele); + add_next_index_zval(z_ret, &z_sub_ele); break; default: add_next_index_bool(z_ret, 0); @@ -1989,7 +1943,7 @@ PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, re PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_cb cb, void *ctx) { - zval zv, *z_result = &zv; + zval z_result; /* Return FALSE if we didn't get a multi-bulk response */ if (c->reply_type != TYPE_MULTIBULK) { @@ -1997,10 +1951,7 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* Allocate our array */ -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_result); -#endif - array_init(z_result); + array_init(&z_result); /* Consume replies as long as there are more than zero */ if (c->reply_len > 0) { @@ -2008,20 +1959,17 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, c->cmd_sock->serializer = c->flags->serializer; /* Call our specified callback */ - if (cb(c->cmd_sock, z_result, c->reply_len, ctx TSRMLS_CC) == FAILURE) { - zval_dtor(z_result); -#if (PHP_MAJOR_VERSION < 7) - efree(z_result); -#endif + if (cb(c->cmd_sock, &z_result, c->reply_len, ctx TSRMLS_CC) == FAILURE) { + zval_dtor(&z_result); CLUSTER_RETURN_FALSE(c); } } // Success, make this array our return value if (CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_result, 0, 1); + RETVAL_ZVAL(&z_result, 0, 1); } else { - add_next_index_zval(&c->multi_resp, z_result); + add_next_index_zval(&c->multi_resp, &z_result); } } @@ -2086,7 +2034,7 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - zval zv, *z_result = &zv; + zval z_result; char *info; // Read our bulk response @@ -2096,15 +2044,14 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } /* Parse response, free memory */ - REDIS_MAKE_STD_ZVAL(z_result); - redis_parse_info_response(info, z_result); + redis_parse_info_response(info, &z_result); efree(info); // Return our array if (CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_result, 0, 1); + RETVAL_ZVAL(&z_result, 0, 1); } else { - add_next_index_zval(&c->multi_resp, z_result); + add_next_index_zval(&c->multi_resp, &z_result); } } @@ -2113,7 +2060,7 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC void *ctx) { char *info; - zval zv, *z_result = &zv; + zval z_result; /* Read the bulk response */ info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC); @@ -2121,87 +2068,77 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC CLUSTER_RETURN_FALSE(c); } -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_result); -#endif - /* Parse it and free the bulk string */ - redis_parse_client_list_response(info, z_result); + redis_parse_client_list_response(info, &z_result); efree(info); if (CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_result, 0, 1); + RETVAL_ZVAL(&z_result, 0, 1); } else { - add_next_index_zval(&c->multi_resp, z_result); + add_next_index_zval(&c->multi_resp, &z_result); } } /* XRANGE */ PHP_REDIS_API void cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - zval zv, *z_messages = &zv; + zval z_messages; - REDIS_MAKE_STD_ZVAL(z_messages); - array_init(z_messages); + array_init(&z_messages); c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; - if (redis_read_stream_messages(c->cmd_sock, c->reply_len, z_messages TSRMLS_CC) < 0) { - zval_dtor(z_messages); - REDIS_FREE_ZVAL(z_messages); + if (redis_read_stream_messages(c->cmd_sock, c->reply_len, &z_messages TSRMLS_CC) < 0) { + zval_dtor(&z_messages); CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_messages, 0, 1); + RETVAL_ZVAL(&z_messages, 0, 1); } else { - add_next_index_zval(&c->multi_resp, z_messages); + add_next_index_zval(&c->multi_resp, &z_messages); } } /* XREAD */ PHP_REDIS_API void cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - zval zv, *z_streams = &zv; + zval z_streams; - REDIS_MAKE_STD_ZVAL(z_streams); - array_init(z_streams); + array_init(&z_streams); c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; - if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, z_streams TSRMLS_CC) < 0) { - zval_dtor(z_streams); - REDIS_FREE_ZVAL(z_streams); + if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, &z_streams TSRMLS_CC) < 0) { + zval_dtor(&z_streams); CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_streams, 0, 1); + RETVAL_ZVAL(&z_streams, 0, 1); } else { - add_next_index_zval(&c->multi_resp, z_streams); + add_next_index_zval(&c->multi_resp, &z_streams); } } /* XCLAIM */ PHP_REDIS_API void cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - zval zv, *z_msg = &zv; + zval z_msg; - REDIS_MAKE_STD_ZVAL(z_msg); - array_init(z_msg); + array_init(&z_msg); - if (redis_read_xclaim_response(c->cmd_sock, c->reply_len, z_msg TSRMLS_CC) < 0) { - zval_dtor(z_msg); - REDIS_FREE_ZVAL(z_msg); + if (redis_read_xclaim_response(c->cmd_sock, c->reply_len, &z_msg TSRMLS_CC) < 0) { + zval_dtor(&z_msg); CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(z_msg, 0, 1); + RETVAL_ZVAL(&z_msg, 0, 1); } else { - add_next_index_zval(&c->multi_resp, z_msg); + add_next_index_zval(&c->multi_resp, &z_msg); } } @@ -2481,13 +2418,9 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); if (line != NULL) { - zval zv, *z = &zv; - if (redis_unpack(redis_sock, line, line_len, z TSRMLS_CC)) { -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); - *z = zv; -#endif - add_next_index_zval(z_result, z); + zval z_unpacked; + if (redis_unpack(redis_sock, line, line_len, &z_unpacked TSRMLS_CC)) { + add_next_index_zval(z_result, &z_unpacked); } else { add_next_index_stringl(z_result, line, line_len); } @@ -2525,17 +2458,14 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, key = line; key_len = line_len; } else { - /* Attempt serialization */ - zval zv, *z = &zv; - if (redis_unpack(redis_sock, line, line_len, z TSRMLS_CC)) { -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); - *z = zv; -#endif - add_assoc_zval(z_result, key, z); + /* Attempt unpacking */ + zval z_unpacked; + if (redis_unpack(redis_sock, line, line_len, &z_unpacked TSRMLS_CC)) { + add_assoc_zval(z_result, key, &z_unpacked); } else { add_assoc_stringl_ex(z_result, key, key_len, line, line_len); } + efree(line); efree(key); } @@ -2599,13 +2529,9 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); if (line != NULL) { - zval zv, *z = &zv; - if (redis_unpack(redis_sock, line, line_len, z TSRMLS_CC)) { -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); - *z = zv; -#endif - add_assoc_zval_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), z); + zval z_unpacked; + if (redis_unpack(redis_sock, line, line_len, &z_unpacked TSRMLS_CC)) { + add_assoc_zval_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked); } else { add_assoc_stringl_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), line, line_len); } diff --git a/cluster_library.h b/cluster_library.h index 366b395a66..62f7f9dabf 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -166,10 +166,6 @@ typedef struct clusterFoldItem clusterFoldItem; /* RedisCluster implementation structure */ typedef struct redisCluster { -#if (PHP_MAJOR_VERSION < 7) - zend_object std; -#endif - zend_string *auth; /* Timeout and read timeout (for normal operations) */ @@ -240,10 +236,8 @@ typedef struct redisCluster { unsigned short redir_slot; unsigned short redir_port; -#if (PHP_MAJOR_VERSION >= 7) /* Zend object handler */ zend_object std; -#endif } redisCluster; /* RedisCluster response processing callback */ @@ -329,7 +323,7 @@ void cluster_free_reply(clusterReply *reply, int free_data); HashTable *cluster_dist_create(); void cluster_dist_free(HashTable *ht); int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, - strlen_t key_len, clusterKeyVal **kv); + size_t key_len, clusterKeyVal **kv); void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val TSRMLS_DC); diff --git a/common.h b/common.h index 65b497b610..34f8441698 100644 --- a/common.h +++ b/common.h @@ -8,459 +8,12 @@ #include #include -#if (PHP_MAJOR_VERSION < 7) -#include -typedef smart_str smart_string; -#define smart_string_0(x) smart_str_0(x) -#define smart_string_appendc(dest, c) smart_str_appendc(dest, c) -#define smart_string_append_long(dest, val) smart_str_append_long(dest, val) -#define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) - -typedef struct { - short gc; - size_t len; - char *val; -} zend_string; - -#define REDIS_MAKE_STD_ZVAL(zv) MAKE_STD_ZVAL(zv) -#define REDIS_FREE_ZVAL(zv) (efree(zv)) - -#define ZSTR_VAL(s) (s)->val -#define ZSTR_LEN(s) (s)->len - -static zend_always_inline zend_string * -zend_string_alloc(size_t len, int persistent) -{ - zend_string *zstr = emalloc(sizeof(*zstr) + len + 1); - - ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); - ZSTR_LEN(zstr) = len; - zstr->gc = 0x01; - return zstr; -} - -static zend_always_inline zend_string * -zend_string_init(const char *str, size_t len, int persistent) -{ - zend_string *zstr = zend_string_alloc(len, persistent); - - memcpy(ZSTR_VAL(zstr), str, len); - ZSTR_VAL(zstr)[len] = '\0'; - return zstr; -} - -static zend_always_inline zend_string * -zend_string_realloc(zend_string *s, size_t len, int persistent) -{ - zend_string *zstr; - - if (!s->gc) { - zstr = zend_string_init(ZSTR_VAL(s), len, 0); - } else if (s->gc & 0x10) { - ZSTR_VAL(s) = erealloc(ZSTR_VAL(s), len + 1); - ZSTR_LEN(s) = len; - zstr = s; - } else { - zstr = erealloc(s, sizeof(*zstr) + len + 1); - ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); - ZSTR_LEN(zstr) = len; - } - return zstr; -} - -#define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0) - -#define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) -#define zend_string_equal_content(s1, s2) (ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2)) -#define zend_string_equals(s1, s2) (s1 == s2 || zend_string_equal_content(s1, s2)) - -#define zend_string_release(s) do { \ - if ((s) && (s)->gc) { \ - if ((s)->gc & 0x10 && ZSTR_VAL(s)) efree(ZSTR_VAL(s)); \ - if ((s)->gc & 0x01) efree((s)); \ - } \ -} while (0) - -#define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) do { \ - HashPosition _hpos; \ - for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ - zend_hash_move_forward_ex(ht, &_hpos) \ - ) { \ - zend_string _zstr = {0}; \ - char *_str_index; uint _str_length; ulong _num_index; \ - _h = 0; _key = NULL; _val = zend_hash_get_current_data_ex(ht, &_hpos); \ - switch (zend_hash_get_current_key_ex(ht, &_str_index, &_str_length, &_num_index, 0, &_hpos)) { \ - case HASH_KEY_IS_STRING: \ - _zstr.len = _str_length - 1; \ - _zstr.val = _str_index; \ - _key = &_zstr; \ - break; \ - case HASH_KEY_IS_LONG: \ - _h = _num_index; \ - break; \ - default: \ - /* noop */ break; \ - } - -#define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ - HashPosition _hpos; \ - for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ - zend_hash_move_forward_ex(ht, &_hpos) \ - ) { \ - _val = zend_hash_get_current_data_ex(ht, &_hpos); \ - -#define ZEND_HASH_FOREACH_PTR(ht, _ptr) do { \ - HashPosition _hpos; \ - for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ - zend_hash_move_forward_ex(ht, &_hpos) \ - ) { \ - _ptr = zend_hash_get_current_data_ptr_ex(ht, &_hpos); \ - -#define ZEND_HASH_FOREACH_END() \ - } \ -} while(0) - -#undef zend_hash_get_current_key -#define zend_hash_get_current_key(ht, str_index, num_index) \ - zend_hash_get_current_key_ex(ht, str_index, NULL, num_index, 0, NULL) - -#define zend_hash_str_exists(ht, str, len) zend_hash_exists(ht, str, len + 1) - -static zend_always_inline zval * -zend_hash_str_find(const HashTable *ht, const char *key, size_t len) -{ - zval **zv; - - if (zend_hash_find(ht, key, len + 1, (void **)&zv) == SUCCESS) { - return *zv; - } - return NULL; -} - -static zend_always_inline void * -zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) -{ - void **ptr; - - if (zend_hash_find(ht, str, len + 1, (void **)&ptr) == SUCCESS) { - return *ptr; - } - return NULL; -} - -static zend_always_inline void * -zend_hash_str_update_ptr(HashTable *ht, const char *str, size_t len, void *pData) -{ - if (zend_hash_update(ht, str, len + 1, (void *)&pData, sizeof(void *), NULL) == SUCCESS) { - return pData; - } - return NULL; -} - -static zend_always_inline void * -zend_hash_index_update_ptr(HashTable *ht, zend_ulong h, void *pData) -{ - if (zend_hash_index_update(ht, h, (void **)&pData, sizeof(void *), NULL) == SUCCESS) { - return pData; - } - return NULL; -} - -#undef zend_hash_get_current_data -static zend_always_inline zval * -zend_hash_get_current_data(HashTable *ht) -{ - zval **zv; - - if (zend_hash_get_current_data_ex(ht, (void **)&zv, NULL) == SUCCESS) { - return *zv; - } - return NULL; -} - -static zend_always_inline void * -zend_hash_get_current_data_ptr_ex(HashTable *ht, HashPosition *pos) -{ - void **ptr; - - if (zend_hash_get_current_data_ex(ht, (void **)&ptr, pos) == SUCCESS) { - return *ptr; - } - return NULL; -} -#define zend_hash_get_current_data_ptr(ht) zend_hash_get_current_data_ptr_ex(ht, NULL) - -static int (*_zend_hash_index_find)(const HashTable *, ulong, void **) = &zend_hash_index_find; -#define zend_hash_index_find(ht, h) inline_zend_hash_index_find(ht, h) - -static zend_always_inline zval * -inline_zend_hash_index_find(const HashTable *ht, zend_ulong h) -{ - zval **zv; - if (_zend_hash_index_find(ht, h, (void **)&zv) == SUCCESS) { - return *zv; - } - return NULL; -} - -static zend_always_inline void * -zend_hash_index_find_ptr(const HashTable *ht, zend_ulong h) -{ - void **ptr; - - if (_zend_hash_index_find(ht, h, (void **)&ptr) == SUCCESS) { - return *ptr; - } - return NULL; -} - -static int (*_zend_hash_get_current_data_ex)(HashTable *, void **, HashPosition *) = &zend_hash_get_current_data_ex; -#define zend_hash_get_current_data_ex(ht, pos) inline_zend_hash_get_current_data_ex(ht, pos) -static zend_always_inline zval * -inline_zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos) -{ - zval **zv; - if (_zend_hash_get_current_data_ex(ht, (void **)&zv, pos) == SUCCESS) { - return *zv; - } - return NULL; -} - -#undef zend_hash_next_index_insert -#define zend_hash_next_index_insert(ht, pData) \ - _zend_hash_next_index_insert(ht, pData ZEND_FILE_LINE_CC) -static zend_always_inline zval * -_zend_hash_next_index_insert(HashTable *ht, zval *pData ZEND_FILE_LINE_DC) -{ - if (_zend_hash_index_update_or_next_insert(ht, 0, &pData, sizeof(pData), - NULL, HASH_NEXT_INSERT ZEND_FILE_LINE_CC) == SUCCESS - ) { - return pData; - } - return NULL; -} - -#undef zend_get_parameters_array -#define zend_get_parameters_array(ht, param_count, argument_array) \ - inline_zend_get_parameters_array(ht, param_count, argument_array TSRMLS_CC) - -static zend_always_inline int -inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array TSRMLS_DC) -{ - int i, ret = FAILURE; - zval **zv = ecalloc(param_count, sizeof(zval *)); - - if (_zend_get_parameters_array(ht, param_count, zv TSRMLS_CC) == SUCCESS) { - for (i = 0; i < param_count; i++) { - argument_array[i] = *zv[i]; - } - ret = SUCCESS; - } - efree(zv); - return ret; -} - -typedef zend_rsrc_list_entry zend_resource; - -extern int (*_add_next_index_string)(zval *, const char *, int); -#define add_next_index_string(arg, str) _add_next_index_string(arg, str, 1); -extern int (*_add_next_index_stringl)(zval *, const char *, uint, int); -#define add_next_index_stringl(arg, str, length) _add_next_index_stringl(arg, str, length, 1); - -#undef ZVAL_STRING -#define ZVAL_STRING(z, s) do { \ - const char *_s = (s); \ - ZVAL_STRINGL(z, _s, strlen(_s)); \ -} while (0) -#undef RETVAL_STRING -#define RETVAL_STRING(s) ZVAL_STRING(return_value, s) -#undef RETURN_STRING -#define RETURN_STRING(s) { RETVAL_STRING(s); return; } - -#undef ZVAL_STRINGL -#define ZVAL_STRINGL(z, s, l) do { \ - const char *__s = (s); int __l = l; \ - zval *__z = (z); \ - Z_STRLEN_P(__z) = __l; \ - Z_STRVAL_P(__z) = estrndup(__s, __l); \ - Z_TYPE_P(__z) = IS_STRING; \ -} while (0) -#undef RETVAL_STRINGL -#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l) -#undef RETURN_STRINGL -#define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; } - -static int (*_call_user_function)(HashTable *, zval **, zval *, zval *, zend_uint, zval *[] TSRMLS_DC) = &call_user_function; -#define call_user_function(function_table, object, function_name, retval_ptr, param_count, params) \ - inline_call_user_function(function_table, object, function_name, retval_ptr, param_count, params TSRMLS_CC) - -static zend_always_inline int -inline_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, zend_uint param_count, zval params[] TSRMLS_DC) -{ - int i, ret; - zval **_params = NULL; - if (!params) param_count = 0; - if (param_count > 0) { - _params = ecalloc(param_count, sizeof(zval *)); - for (i = 0; i < param_count; ++i) { - zval *zv = ¶ms[i]; - MAKE_STD_ZVAL(_params[i]); - ZVAL_ZVAL(_params[i], zv, 1, 0); - } - } - ret = _call_user_function(function_table, &object, function_name, retval_ptr, param_count, _params TSRMLS_CC); - if (_params) { - for (i = 0; i < param_count; ++i) { - zval_ptr_dtor(&_params[i]); - } - efree(_params); - } - return ret; -} - -#undef add_assoc_bool -#define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key), __b) -extern int (*_add_assoc_bool_ex)(zval *, const char *, uint, int); -#define add_assoc_bool_ex(_arg, _key, _key_len, _b) _add_assoc_bool_ex(_arg, _key, _key_len + 1, _b) - -#undef add_assoc_long -#define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key), __n) -extern int (*_add_assoc_long_ex)(zval *, const char *, uint, long); -#define add_assoc_long_ex(_arg, _key, _key_len, _n) _add_assoc_long_ex(_arg, _key, _key_len + 1, _n) - -#undef add_assoc_double -#define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key), __d) -extern int (*_add_assoc_double_ex)(zval *, const char *, uint, double); -#define add_assoc_double_ex(_arg, _key, _key_len, _d) _add_assoc_double_ex(_arg, _key, _key_len + 1, _d) - -#undef add_assoc_string -#define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str) -extern int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int); -#define add_assoc_string_ex(_arg, _key, _key_len, _str) _add_assoc_string_ex(_arg, _key, _key_len + 1, _str, 1) - -extern int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int); -#define add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length) _add_assoc_stringl_ex(_arg, _key, _key_len + 1, _str, _length, 1) - -#undef add_assoc_zval -#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key), __value) -extern int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *); -#define add_assoc_zval_ex(_arg, _key, _key_len, _value) _add_assoc_zval_ex(_arg, _key, _key_len + 1, _value); - -typedef long zend_long; -static zend_always_inline zend_long -zval_get_long(zval *op) -{ - switch (Z_TYPE_P(op)) { - case IS_BOOL: - case IS_LONG: - return Z_LVAL_P(op); - case IS_DOUBLE: - return zend_dval_to_lval(Z_DVAL_P(op)); - case IS_STRING: - { - double dval; - zend_long lval; - zend_uchar type = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, 0); - if (type == IS_LONG) { - return lval; - } else if (type == IS_DOUBLE) { - return zend_dval_to_lval(dval); - } - } - break; - EMPTY_SWITCH_DEFAULT_CASE() - } - return 0; -} - -static zend_always_inline double -zval_get_double(zval *op) -{ - switch (Z_TYPE_P(op)) { - case IS_BOOL: - case IS_LONG: - return (double)Z_LVAL_P(op); - case IS_DOUBLE: - return Z_DVAL_P(op); - case IS_STRING: - return zend_strtod(Z_STRVAL_P(op), NULL); - EMPTY_SWITCH_DEFAULT_CASE() - } - return 0.0; -} - -static zend_always_inline zend_string * -zval_get_string(zval *op) -{ - zend_string *zstr = ecalloc(1, sizeof(zend_string)); - - zstr->gc = 0; - ZSTR_VAL(zstr) = ""; - ZSTR_LEN(zstr) = 0; - switch (Z_TYPE_P(op)) { - case IS_STRING: - ZSTR_VAL(zstr) = Z_STRVAL_P(op); - ZSTR_LEN(zstr) = Z_STRLEN_P(op); - break; - case IS_BOOL: - if (Z_LVAL_P(op)) { - ZSTR_VAL(zstr) = "1"; - ZSTR_LEN(zstr) = 1; - } - break; - case IS_LONG: { - zstr->gc = 0x10; - ZSTR_LEN(zstr) = spprintf(&ZSTR_VAL(zstr), 0, "%ld", Z_LVAL_P(op)); - break; - } - case IS_DOUBLE: { - zstr->gc = 0x10; - ZSTR_LEN(zstr) = spprintf(&ZSTR_VAL(zstr), 0, "%.16g", Z_DVAL_P(op)); - break; - } - EMPTY_SWITCH_DEFAULT_CASE() - } - zstr->gc |= 0x01; - return zstr; -} - -extern void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC); -#define php_var_serialize(buf, struc, data) _php_var_serialize(buf, &struc, data TSRMLS_CC) -extern int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC); -#define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) -typedef int strlen_t; - -#define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_BOOL && !Z_BVAL_P(z)) - -/* If ZEND_MOD_END isn't defined, use legacy version */ -#ifndef ZEND_MOD_END -#define ZEND_MOD_END { NULL, NULL, NULL } -#endif - -/* PHP_FE_END exists since 5.3.7 */ -#ifndef PHP_FE_END -#define PHP_FE_END { NULL, NULL, NULL } -#endif - -/* References don't need any actions */ -#define ZVAL_DEREF(v) PHPREDIS_NOTUSED(v) - -#define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)zend_objects_get_address(z TSRMLS_CC) - -#else #include #include -typedef size_t strlen_t; + #define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_FALSE) #define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)((char *)Z_OBJ_P(z) - XtOffsetOf(class_entry, std)) -#define REDIS_MAKE_STD_ZVAL(zv) do {} while(0) -#define REDIS_FREE_ZVAL(zv) do {} while(0) -#endif - /* NULL check so Eclipse doesn't go crazy */ #ifndef NULL #define NULL ((void *) 0) @@ -716,17 +269,10 @@ typedef struct { } RedisSock; /* }}} */ -#if (PHP_MAJOR_VERSION < 7) -typedef struct { - zend_object std; - RedisSock *sock; -} redis_object; -#else typedef struct { RedisSock *sock; zend_object std; } redis_object; -#endif /** Argument info for any function expecting 0 args */ ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) @@ -762,21 +308,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_nkeys, 0, 0, 1) ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_dst_nkeys, 0, 0, 2) ZEND_ARG_INFO(0, dst) ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_key_min_max, 0, 0, 3) @@ -799,11 +337,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_key_members, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_members) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_key_timestamp, 0, 0, 2) @@ -857,12 +391,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_blrpop, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, timeout_or_key) -// Can't have variadic keys before timeout. -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, extra_args) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_linsert, 0, 0, 4) @@ -924,11 +453,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_bitop, 0, 0, 3) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, ret_key) ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_bitpos, 0, 0, 2) @@ -1020,20 +545,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_unsubscribe, 0, 0, 1) ZEND_ARG_INFO(0, channel) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_channels) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_punsubscribe, 0, 0, 1) ZEND_ARG_INFO(0, pattern) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_patterns) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_eval, 0, 0, 1) @@ -1059,28 +576,16 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_watch, 0, 0, 1) ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_command, 0, 0, 0) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_rawcommand, 0, 0, 1) ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_geoadd, 0, 0, 4) @@ -1088,11 +593,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_geoadd, 0, 0, 4) ZEND_ARG_INFO(0, lng) ZEND_ARG_INFO(0, lat) ZEND_ARG_INFO(0, member) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_triples) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_geodist, 0, 0, 3) diff --git a/library.c b/library.c index 93a4460069..26358e114a 100644 --- a/library.c +++ b/library.c @@ -38,24 +38,6 @@ #include /* SO_KEEPALIVE */ #else #include - - # if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 4 - /* This proto is available from 5.5 on only */ - PHP_REDIS_API int usleep(unsigned int useconds); - # endif -#endif - -#if (PHP_MAJOR_VERSION < 7) - int (*_add_next_index_string)(zval *, const char *, int) = &add_next_index_string; - int (*_add_next_index_stringl)(zval *, const char *, uint, int) = &add_next_index_stringl; - int (*_add_assoc_bool_ex)(zval *, const char *, uint, int) = &add_assoc_bool_ex; - int (*_add_assoc_long_ex)(zval *, const char *, uint, long) = &add_assoc_long_ex; - int (*_add_assoc_double_ex)(zval *, const char *, uint, double) = &add_assoc_double_ex; - int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int) = &add_assoc_string_ex; - int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int) = &add_assoc_stringl_ex; - int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *) = &add_assoc_zval_ex; - void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC) = &php_var_serialize; - int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; #endif extern zend_class_entry *redis_ce; @@ -316,13 +298,8 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, zval_dtor(&z_resp); } -#if (PHP_MAJOR_VERSION < 7) - zval *z_ret, **z_args[4]; - sctx->cb.retval_ptr_ptr = &z_ret; -#else zval z_ret, z_args[4]; sctx->cb.retval = &z_ret; -#endif sctx->cb.params = z_args; sctx->cb.no_separation = 0; @@ -367,17 +344,6 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, } // Different args for SUBSCRIBE and PSUBSCRIBE -#if (PHP_MAJOR_VERSION < 7) - z_args[0] = &getThis(); - if(is_pmsg) { - z_args[1] = &z_pat; - z_args[2] = &z_chan; - z_args[3] = &z_data; - } else { - z_args[1] = &z_chan; - z_args[2] = &z_data; - } -#else z_args[0] = *getThis(); if(is_pmsg) { z_args[1] = *z_pat; @@ -387,7 +353,6 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, z_args[1] = *z_chan; z_args[2] = *z_data; } -#endif // Set arg count sctx->cb.param_count = tab_idx; @@ -575,7 +540,7 @@ union resparg { /* A printf like method to construct a Redis RESP command. It has been extended * to take a few different format specifiers that are convienient to phpredis. * - * s - C string followed by length as a strlen_t + * s - C string followed by length as a * S - Pointer to a zend_string * k - Same as 's' but the value will be prefixed if phpredis is set up do do * that and the working slot will be set if it has been passed. @@ -594,7 +559,7 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k union resparg arg; char *dup; int argfree; - strlen_t arglen; + size_t arglen; va_start(ap, fmt); @@ -605,7 +570,7 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k switch (*fmt) { case 's': arg.str = va_arg(ap, char*); - arglen = va_arg(ap, strlen_t); + arglen = va_arg(ap, size_t); redis_cmd_append_sstr(&cmd, arg.str, arglen); break; case 'S': @@ -614,7 +579,7 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k break; case 'k': arg.str = va_arg(ap, char*); - arglen = va_arg(ap, strlen_t); + arglen = va_arg(ap, size_t); argfree = redis_key_prefix(redis_sock, &arg.str, &arglen); redis_cmd_append_sstr(&cmd, arg.str, arglen); if (slot) *slot = cluster_hash_key(arg.str, arglen); @@ -736,7 +701,7 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value) * configured to do that */ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC) { char *val; - strlen_t vallen; + size_t vallen; int valfree, retval; valfree = redis_pack(redis_sock, z, &val, &vallen TSRMLS_CC); @@ -748,7 +713,7 @@ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock /* Append a string key to a redis command. This function takes care of prefixing the key * for the caller and setting the slot argument if it is passed non null */ -int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot) { +int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot) { int valfree, retval; valfree = redis_key_prefix(redis_sock, &key, &len); @@ -838,7 +803,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; - zval zv = {{0}}, *z_ret = &zv; + zval z_ret; /* Read bulk response */ if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { @@ -846,16 +811,16 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * } /* Parse it into a zval array */ - REDIS_MAKE_STD_ZVAL(z_ret); - redis_parse_info_response(response, z_ret); + ZVAL_UNDEF(&z_ret); + redis_parse_info_response(response, &z_ret); /* Free source response */ efree(response); if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_ret, 0, 1); + RETVAL_ZVAL(&z_ret, 0, 1); } else { - add_next_index_zval(z_tab, z_ret); + add_next_index_zval(z_tab, &z_ret); } } @@ -916,28 +881,24 @@ redis_parse_info_response(char *response, zval *z_ret) PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { char *resp; int resp_len; + zval z_ret; /* Make sure we can read the bulk response from Redis */ if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { RETURN_FALSE; } - zval zv, *z_ret = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_ret); -#endif - /* Parse it out */ - redis_parse_client_list_response(resp, z_ret); + redis_parse_client_list_response(resp, &z_ret); /* Free our response */ efree(resp); /* Return or append depending if we're atomic */ if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_ret, 0, 1); + RETVAL_ZVAL(&z_ret, 0, 1); } else { - add_next_index_zval(z_tab, z_ret); + add_next_index_zval(z_tab, &z_ret); } } @@ -946,16 +907,11 @@ redis_parse_client_list_response(char *response, zval *z_ret) { char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value; int klen = 0, done = 0, is_numeric; + zval z_sub_result; - // Allocate memory for our response + /* Allocate for response and our user */ array_init(z_ret); - - /* Allocate memory for one user (there should be at least one, namely us!) */ - zval zv, *z_sub_result = &zv; -#if (PHP_MAJOR_VERSION < 7) - ALLOC_INIT_ZVAL(z_sub_result); -#endif - array_init(z_sub_result); + array_init(&z_sub_result); // Pointers for parsing p = response; @@ -995,22 +951,19 @@ redis_parse_client_list_response(char *response, zval *z_ret) /* Add as a long or string, depending */ if(is_numeric == 1) { - add_assoc_long(z_sub_result, key, atol(value)); + add_assoc_long(&z_sub_result, key, atol(value)); } else { - add_assoc_string(z_sub_result, key, value); + add_assoc_string(&z_sub_result, key, value); } efree(value); // If we hit a '\n', then we can add this user to our list if(*p == '\n') { /* Add our user */ - add_next_index_zval(z_ret, z_sub_result); + add_next_index_zval(z_ret, &z_sub_result); /* If we have another user, make another one */ if(*(p+1) != '\0') { -#if (PHP_MAJOR_VERSION < 7) - ALLOC_INIT_ZVAL(z_sub_result); -#endif - array_init(z_sub_result); + array_init(&z_sub_result); } } @@ -1128,10 +1081,10 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int decode TSRMLS_DC) { - zval zv, *z_ret = &zv; + zval z_ret, z_sub; HashTable *keytable; - array_init(z_ret); + array_init(&z_ret); keytable = Z_ARRVAL_P(z_tab); for(zend_hash_internal_pointer_reset(keytable); @@ -1161,24 +1114,20 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, /* Decode the score depending on flag */ if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) { - add_assoc_long_ex(z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atoi(hval+1)); + add_assoc_long_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atoi(hval+1)); } else if (decode == SCORE_DECODE_DOUBLE) { - add_assoc_double_ex(z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atof(hval)); + add_assoc_double_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atof(hval)); } else { - zval zv0, *z = &zv0; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); -#endif - ZVAL_ZVAL(z, z_value_p, 1, 0); - add_assoc_zval_ex(z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), z); + ZVAL_ZVAL(&z_sub, z_value_p, 1, 0); + add_assoc_zval_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), &z_sub); } zend_string_release(hkey); } /* replace */ zval_dtor(z_tab); - ZVAL_ZVAL(z_tab, z_ret, 1, 0); - zval_dtor(z_ret); + ZVAL_ZVAL(z_tab, &z_ret, 1, 0); + zval_dtor(&z_ret); } static int @@ -1226,22 +1175,19 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } numElems = atoi(inbuf+1); - zval zv, *z_multi_result = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_multi_result); -#endif - array_init(z_multi_result); /* pre-allocate array for multi's results. */ + zval z_multi_result; + array_init(&z_multi_result); /* pre-allocate array for multi's results. */ /* Grab our key, value, key, value array */ - redis_mbulk_reply_loop(redis_sock, z_multi_result, numElems, unserialize TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize TSRMLS_CC); /* Zip keys and values */ - array_zip_values_and_scores(redis_sock, z_multi_result, decode TSRMLS_CC); + array_zip_values_and_scores(redis_sock, &z_multi_result, decode TSRMLS_CC); if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_multi_result, 0, 1); + RETVAL_ZVAL(&z_multi_result, 0, 1); } else { - add_next_index_zval(z_tab, z_multi_result); + add_next_index_zval(z_tab, &z_multi_result); } return 0; @@ -1280,7 +1226,7 @@ PHP_REDIS_API int redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret TSRMLS_DC) { - zval zv, *z_message = &zv; + zval z_message; int i, mhdr, fields; char *id = NULL; int idlen; @@ -1297,12 +1243,11 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret return -1; } - REDIS_MAKE_STD_ZVAL(z_message); - array_init(z_message); + array_init(&z_message); - redis_mbulk_reply_loop(redis_sock, z_message, fields, UNSERIALIZE_VALS TSRMLS_CC); - array_zip_values_and_scores(redis_sock, z_message, SCORE_DECODE_NONE TSRMLS_CC); - add_assoc_zval_ex(z_ret, id, idlen, z_message); + redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS TSRMLS_CC); + array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE TSRMLS_CC); + add_assoc_zval_ex(z_ret, id, idlen, &z_message); efree(id); } @@ -1313,17 +1258,15 @@ PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { + zval z_messages; int messages; - zval zv, *z_messages = &zv; - REDIS_MAKE_STD_ZVAL(z_messages); - array_init(z_messages); + array_init(&z_messages); if (read_mbulk_header(redis_sock, &messages TSRMLS_CC) < 0 || - redis_read_stream_messages(redis_sock, messages, z_messages TSRMLS_CC) < 0) + redis_read_stream_messages(redis_sock, messages, &z_messages TSRMLS_CC) < 0) { - zval_dtor(z_messages); - REDIS_FREE_ZVAL(z_messages); + zval_dtor(&z_messages); if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { @@ -1333,9 +1276,9 @@ redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_messages, 0, 1); + RETVAL_ZVAL(&z_messages, 0, 1); } else { - add_next_index_zval(z_tab, z_messages); + add_next_index_zval(z_tab, &z_messages); } return 0; @@ -1345,7 +1288,7 @@ PHP_REDIS_API int redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_streams TSRMLS_DC) { - zval zv, *z_messages = &zv; + zval z_messages; int i, shdr, messages; char *id = NULL; int idlen; @@ -1359,21 +1302,19 @@ redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_strea return -1; } - REDIS_MAKE_STD_ZVAL(z_messages); - array_init(z_messages); + array_init(&z_messages); - if (redis_read_stream_messages(redis_sock, messages, z_messages TSRMLS_CC) < 0) + if (redis_read_stream_messages(redis_sock, messages, &z_messages TSRMLS_CC) < 0) goto failure; - add_assoc_zval_ex(z_streams, id, idlen, z_messages); + add_assoc_zval_ex(z_streams, id, idlen, &z_messages); efree(id); } return 0; failure: efree(id); - zval_dtor(z_messages); - REDIS_FREE_ZVAL(z_messages); + zval_dtor(&z_messages); return -1; } @@ -1381,28 +1322,26 @@ PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - zval zv, *z_rv = &zv; + zval z_rv; int streams; if (read_mbulk_header(redis_sock, &streams TSRMLS_CC) < 0) goto failure; - REDIS_MAKE_STD_ZVAL(z_rv); - array_init(z_rv); + array_init(&z_rv); - if (redis_read_stream_messages_multi(redis_sock, streams, z_rv TSRMLS_CC) < 0) + if (redis_read_stream_messages_multi(redis_sock, streams, &z_rv TSRMLS_CC) < 0) goto cleanup; if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_rv, 0, 1); + RETVAL_ZVAL(&z_rv, 0, 1); } else { - add_next_index_zval(z_tab, z_rv); + add_next_index_zval(z_tab, &z_rv); } return 0; cleanup: - zval_dtor(z_rv); - REDIS_FREE_ZVAL(z_rv); + zval_dtor(&z_rv); failure: if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; @@ -1417,7 +1356,7 @@ redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * on whether or not it was called with the JUSTID option */ PHP_REDIS_API int redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) { - zval zv, *z_msg = &zv; + zval z_msg; REDIS_REPLY_TYPE type; char *id = NULL; int i, fields, idlen; @@ -1444,12 +1383,11 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) return -1; } - REDIS_MAKE_STD_ZVAL(z_msg); - array_init(z_msg); + array_init(&z_msg); - redis_mbulk_reply_loop(redis_sock, z_msg, fields, UNSERIALIZE_VALS TSRMLS_CC); - array_zip_values_and_scores(redis_sock, z_msg, SCORE_DECODE_NONE TSRMLS_CC); - add_assoc_zval_ex(rv, id, idlen, z_msg); + redis_mbulk_reply_loop(redis_sock, &z_msg, fields, UNSERIALIZE_VALS TSRMLS_CC); + array_zip_values_and_scores(redis_sock, &z_msg, SCORE_DECODE_NONE TSRMLS_CC); + add_assoc_zval_ex(rv, id, idlen, &z_msg); efree(id); } } @@ -1461,26 +1399,24 @@ PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - zval zv, *z_ret = &zv; + zval z_ret; int messages; /* All XCLAIM responses start multibulk */ if (read_mbulk_header(redis_sock, &messages TSRMLS_CC) < 0) goto failure; - REDIS_MAKE_STD_ZVAL(z_ret); - array_init(z_ret); + array_init(&z_ret); - if (redis_read_xclaim_response(redis_sock, messages, z_ret TSRMLS_CC) < 0) { - zval_dtor(z_ret); - REDIS_FREE_ZVAL(z_ret); + if (redis_read_xclaim_response(redis_sock, messages, &z_ret TSRMLS_CC) < 0) { + zval_dtor(&z_ret); goto failure; } if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_ret, 0, 1); + RETVAL_ZVAL(&z_ret, 0, 1); } else { - add_next_index_zval(z_tab, z_ret); + add_next_index_zval(z_tab, &z_ret); } return 0; @@ -1562,13 +1498,9 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock RETVAL_STRINGL(response, response_len); } } else { - zval zv, *z = &zv; - if (redis_unpack(redis_sock, response, response_len, z TSRMLS_CC)) { -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); - *z = zv; -#endif - add_next_index_zval(z_tab, z); + zval z_unpacked; + if (redis_unpack(redis_sock, response, response_len, &z_unpacked TSRMLS_CC)) { + add_next_index_zval(z_tab, &z_unpacked); } else { add_next_index_stringl(z_tab, response, response_len); } @@ -1642,11 +1574,8 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock return; } - zval zv, *z_result = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_result); -#endif - array_init(z_result); + zval z_result; + array_init(&z_result); /* Skip the '+' */ p = resp + 1; @@ -1673,9 +1602,9 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock /* Add our value */ if(is_numeric) { - add_assoc_long(z_result, p, atol(p2)); + add_assoc_long(&z_result, p, atol(p2)); } else { - add_assoc_string(z_result, p, p2); + add_assoc_string(&z_result, p, p2); } p = p3; @@ -1684,9 +1613,9 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock efree(resp); if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_result, 0, 1); + RETVAL_ZVAL(&z_result, 0, 1); } else { - add_next_index_zval(z_tab, z_result); + add_next_index_zval(z_tab, &z_result); } } @@ -1748,11 +1677,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) int host_len, usocket = 0, err = 0; php_netstream_data_t *sock; int tcp_flag = 1; -#if (PHP_MAJOR_VERSION < 7) - char *estr = NULL; -#else zend_string *estr = NULL; -#endif if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock, 0 TSRMLS_CC); @@ -1804,13 +1729,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (!redis_sock->stream) { if (estr) { -#if (PHP_MAJOR_VERSION < 7) - redis_sock_set_err(redis_sock, estr, strlen(estr)); - efree(estr); -#else redis_sock_set_err(redis_sock, ZSTR_VAL(estr), ZSTR_LEN(estr)); zend_string_release(estr); -#endif } return -1; } @@ -1927,19 +1847,17 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, return -1; } numElems = atoi(inbuf+1); - zval zv, *z_multi_result = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_multi_result); -#endif - array_init(z_multi_result); /* pre-allocate array for multi's results. */ + zval z_multi_result; + array_init(&z_multi_result); /* pre-allocate array for multi's results. */ - redis_mbulk_reply_loop(redis_sock, z_multi_result, numElems, UNSERIALIZE_ALL TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL TSRMLS_CC); if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_multi_result, 0, 1); + RETVAL_ZVAL(&z_multi_result, 0, 1); } else { - add_next_index_zval(z_tab, z_multi_result); + add_next_index_zval(z_tab, &z_multi_result); } + /*zval_copy_ctor(return_value); */ return 0; } @@ -1969,18 +1887,15 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval return -1; } numElems = atoi(inbuf+1); - zval zv, *z_multi_result = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_multi_result); -#endif - array_init(z_multi_result); /* pre-allocate array for multi's results. */ + zval z_multi_result; + array_init(&z_multi_result); /* pre-allocate array for multi's results. */ - redis_mbulk_reply_loop(redis_sock, z_multi_result, numElems, UNSERIALIZE_NONE TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE TSRMLS_CC); if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_multi_result, 0, 1); + RETVAL_ZVAL(&z_multi_result, 0, 1); } else { - add_next_index_zval(z_tab, z_multi_result); + add_next_index_zval(z_tab, &z_multi_result); } /*zval_copy_ctor(return_value); */ return 0; @@ -1990,6 +1905,7 @@ PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize TSRMLS_DC) { + zval z_unpacked; char *line; int i, len; @@ -2007,13 +1923,9 @@ redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, (unserialize == UNSERIALIZE_KEYS && i % 2 == 0) || (unserialize == UNSERIALIZE_VALS && i % 2 != 0) ); - zval zv, *z = &zv; - if (unwrap && redis_unpack(redis_sock, line, len, z TSRMLS_CC)) { -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); - *z = zv; -#endif - add_next_index_zval(z_tab, z); + + if (unwrap && redis_unpack(redis_sock, line, len, &z_unpacked TSRMLS_CC)) { + add_next_index_zval(z_tab, &z_unpacked); } else { add_next_index_stringl(z_tab, line, len); } @@ -2045,29 +1957,22 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc return -1; } numElems = atoi(inbuf+1); - zval zv, *z_multi_result = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_multi_result); -#endif - array_init(z_multi_result); /* pre-allocate array for multi's results. */ + zval z_multi_result; + array_init(&z_multi_result); /* pre-allocate array for multi's results. */ for(i = 0; i < numElems; ++i) { zend_string *zstr = zval_get_string(&z_keys[i]); response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); if(response != NULL) { - zval zv0, *z = &zv0; - if (redis_unpack(redis_sock, response, response_len, z TSRMLS_CC)) { -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z); - *z = zv0; -#endif - add_assoc_zval_ex(z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), z); + zval z_unpacked; + if (redis_unpack(redis_sock, response, response_len, &z_unpacked TSRMLS_CC)) { + add_assoc_zval_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked); } else { - add_assoc_stringl_ex(z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), response, response_len); + add_assoc_stringl_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), response, response_len); } efree(response); } else { - add_assoc_bool_ex(z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0); + add_assoc_bool_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0); } zend_string_release(zstr); zval_dtor(&z_keys[i]); @@ -2075,9 +1980,9 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc efree(z_keys); if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(z_multi_result, 0, 1); + RETVAL_ZVAL(&z_multi_result, 0, 1); } else { - add_next_index_zval(z_tab, z_multi_result); + add_next_index_zval(z_tab, &z_multi_result); } return 0; } @@ -2123,11 +2028,11 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) } PHP_REDIS_API int -redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC) +redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC) { char *buf; int valfree; - strlen_t len; + size_t len; #ifdef HAVE_REDIS_LZF char *data; uint32_t res; @@ -2190,14 +2095,11 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TS } PHP_REDIS_API int -redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len +redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC) { -#if ZEND_MODULE_API_NO >= 20100000 php_serialize_data_t ht; -#else - HashTable ht; -#endif + smart_str sstr = {0}; #ifdef HAVE_REDIS_IGBINARY size_t sz; @@ -2235,29 +2137,16 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len } break; case REDIS_SERIALIZER_PHP: - -#if ZEND_MODULE_API_NO >= 20100000 PHP_VAR_SERIALIZE_INIT(ht); -#else - zend_hash_init(&ht, 10, NULL, NULL, 0); -#endif php_var_serialize(&sstr, z, &ht); -#if (PHP_MAJOR_VERSION < 7) - *val = estrndup(sstr.c, sstr.len); - *val_len = sstr.len; -#else + *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); *val_len = ZSTR_LEN(sstr.s); -#endif + smart_str_free(&sstr); -#if ZEND_MODULE_API_NO >= 20100000 PHP_VAR_SERIALIZE_DESTROY(ht); -#else - zend_hash_destroy(&ht); -#endif return 1; - case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { @@ -2281,21 +2170,19 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, switch(redis_sock->serializer) { case REDIS_SERIALIZER_PHP: -#if ZEND_MODULE_API_NO >= 20100000 PHP_VAR_UNSERIALIZE_INIT(var_hash); -#else - memset(&var_hash, 0, sizeof(var_hash)); -#endif - if (php_var_unserialize(z_ret, (const unsigned char**)&val, - (const unsigned char*)val + val_len, &var_hash) - ) { - ret = 1; - } -#if ZEND_MODULE_API_NO >= 20100000 + + ret = php_var_unserialize(z_ret, (const unsigned char **)&val, + (const unsigned char *)val + val_len, + &var_hash); + + //if (php_var_unserialize(z_ret, (const unsigned char**)&val, + // (const unsigned char*)val + val_len, &var_hash) + //) { + // ret = 1; + //} + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); -#else - var_destroy(&var_hash); -#endif break; case REDIS_SERIALIZER_IGBINARY: @@ -2324,13 +2211,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, break; } -#if (PHP_MAJOR_VERSION < 7) - INIT_PZVAL(z_ret); - ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, &z_ret TSRMLS_CC); -#else ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, z_ret TSRMLS_CC); -#endif - #endif break; } @@ -2338,7 +2219,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, } PHP_REDIS_API int -redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len) { +redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len) { int ret_len; char *ret; @@ -2481,7 +2362,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s { long reply_info; REDIS_REPLY_TYPE reply_type; - zval zv, *z_subelem = &zv; + zval z_subelem; // Iterate while we have elements while(elements > 0) { @@ -2498,12 +2379,9 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s switch(reply_type) { case TYPE_ERR: case TYPE_LINE: -#if (PHP_MAJOR_VERSION < 7) - ALLOC_INIT_ZVAL(z_subelem); -#endif redis_read_variant_line(redis_sock, reply_type, status_strings, - z_subelem TSRMLS_CC); - add_next_index_zval(z_ret, z_subelem); + &z_subelem TSRMLS_CC); + add_next_index_zval(z_ret, &z_subelem); break; case TYPE_INT: // Add our long value @@ -2511,23 +2389,15 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s break; case TYPE_BULK: // Init a zval for our bulk response, read and add it -#if (PHP_MAJOR_VERSION < 7) - ALLOC_INIT_ZVAL(z_subelem); -#endif - redis_read_variant_bulk(redis_sock, reply_info, z_subelem - TSRMLS_CC); - add_next_index_zval(z_ret, z_subelem); + redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); + add_next_index_zval(z_ret, &z_subelem); break; case TYPE_MULTIBULK: - // Construct an array for our sub element, and add it, - // and recurse -#if (PHP_MAJOR_VERSION < 7) - ALLOC_INIT_ZVAL(z_subelem); -#endif - array_init(z_subelem); - add_next_index_zval(z_ret, z_subelem); - redis_read_multibulk_recursive(redis_sock, reply_info, - status_strings, z_subelem TSRMLS_CC); + // Construct an array for our sub element, and add it, and recurse + array_init(&z_subelem); + add_next_index_zval(z_ret, &z_subelem); + redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, + &z_subelem TSRMLS_CC); break; default: // Stop the compiler from whinging @@ -2548,7 +2418,7 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Reply type, and reply size vars REDIS_REPLY_TYPE reply_type; long reply_info; - //char *bulk_resp; + zval z_ret; // Attempt to read our header if(redis_read_reply_type(redis_sock,&reply_type,&reply_info TSRMLS_CC) < 0) @@ -2556,37 +2426,29 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } - zval zv, *z_ret = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_ret); -#endif /* Switch based on our top level reply type */ switch(reply_type) { case TYPE_ERR: case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, status_strings, z_ret TSRMLS_CC); + redis_read_variant_line(redis_sock, reply_type, status_strings, &z_ret TSRMLS_CC); break; case TYPE_INT: - ZVAL_LONG(z_ret, reply_info); + ZVAL_LONG(&z_ret, reply_info); break; case TYPE_BULK: - redis_read_variant_bulk(redis_sock, reply_info, z_ret TSRMLS_CC); + redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); break; case TYPE_MULTIBULK: /* Initialize an array for our multi-bulk response */ - array_init(z_ret); + array_init(&z_ret); // If we've got more than zero elements, parse our multi bulk // response recursively if (reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, z_ret TSRMLS_CC); + redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, &z_ret TSRMLS_CC); } break; default: -#if (PHP_MAJOR_VERSION < 7) - efree(z_ret); -#endif - // Protocol error zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); return FAILURE; @@ -2594,9 +2456,9 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (IS_ATOMIC(redis_sock)) { /* Set our return value */ - RETVAL_ZVAL(z_ret, 0, 1); + RETVAL_ZVAL(&z_ret, 0, 1); } else { - add_next_index_zval(z_tab, z_ret); + add_next_index_zval(z_tab, &z_ret); } /* Success */ diff --git a/library.h b/library.h index 8e82081e88..a7a2f060e1 100644 --- a/library.h +++ b/library.h @@ -21,7 +21,7 @@ int redis_cmd_append_sstr_long(smart_string *str, long append); int redis_cmd_append_sstr_i64(smart_string *str, int64_t append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC); -int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot); +int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, ulong idx); PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *kw, char *fmt, ...); @@ -85,14 +85,14 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); PHP_REDIS_API int -redis_serialize(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC); +redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC); PHP_REDIS_API int -redis_key_prefix(RedisSock *redis_sock, char **key, strlen_t *key_len); +redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len); PHP_REDIS_API int redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); -PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, strlen_t *val_len TSRMLS_DC); +PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC); PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); PHP_REDIS_API int diff --git a/package.xml b/package.xml index d2a790deaa..c331de776b 100644 --- a/package.xml +++ b/package.xml @@ -127,7 +127,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> - 5.3.0 + 7.0.0 7.9.99 diff --git a/redis.c b/redis.c index 5e076a9073..ec743733bc 100644 --- a/redis.c +++ b/redis.c @@ -486,11 +486,9 @@ static const zend_module_dep redis_deps[] = { }; zend_module_entry redis_module_entry = { -#if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER_EX, NULL, redis_deps, -#endif "redis", NULL, PHP_MINIT(redis), @@ -498,9 +496,7 @@ zend_module_entry redis_module_entry = { NULL, NULL, PHP_MINFO(redis), -#if ZEND_MODULE_API_NO >= 20010901 PHP_REDIS_VERSION, -#endif STANDARD_MODULE_PROPERTIES }; @@ -508,6 +504,8 @@ zend_module_entry redis_module_entry = { ZEND_GET_MODULE(redis) #endif +zend_object_handlers redis_object_handlers; + /* Send a static DISCARD in case we're in MULTI mode. */ static int redis_send_discard(RedisSock *redis_sock TSRMLS_DC) @@ -551,48 +549,6 @@ free_reply_callbacks(RedisSock *redis_sock) redis_sock->current = NULL; } -#if (PHP_MAJOR_VERSION < 7) -void -free_redis_object(void *object TSRMLS_DC) -{ - redis_object *redis = (redis_object *)object; - - zend_object_std_dtor(&redis->std TSRMLS_CC); - if (redis->sock) { - redis_sock_disconnect(redis->sock, 0 TSRMLS_CC); - redis_free_socket(redis->sock); - } - efree(redis); -} - -zend_object_value -create_redis_object(zend_class_entry *ce TSRMLS_DC) -{ - zend_object_value retval; - redis_object *redis = ecalloc(1, sizeof(redis_object)); - - memset(redis, 0, sizeof(redis_object)); - zend_object_std_init(&redis->std, ce TSRMLS_CC); - -#if PHP_VERSION_ID < 50399 - zval *tmp; - zend_hash_copy(redis->std.properties, &ce->default_properties, - (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); -#else - object_properties_init(&redis->std, ce); -#endif - - retval.handle = zend_objects_store_put(redis, - (zend_objects_store_dtor_t)zend_objects_destroy_object, - (zend_objects_free_object_storage_t)free_redis_object, - NULL TSRMLS_CC); - retval.handlers = zend_get_std_object_handlers(); - - return retval; -} -#else -zend_object_handlers redis_object_handlers; - void free_redis_object(zend_object *object) { @@ -622,7 +578,6 @@ create_redis_object(zend_class_entry *ce TSRMLS_DC) return &redis->std; } -#endif static zend_always_inline RedisSock * redis_sock_get_instance(zval *id TSRMLS_DC, int no_throw) @@ -782,35 +737,20 @@ PHP_MINIT_FUNCTION(redis) exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1); #endif if (exception_ce == NULL) { -#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) - exception_ce = zend_exception_get_default(); -#else exception_ce = zend_exception_get_default(TSRMLS_C); -#endif } /* RedisException class */ INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL); redis_exception_ce = zend_register_internal_class_ex( &redis_exception_class_entry, -#if (PHP_MAJOR_VERSION < 7) - exception_ce, NULL TSRMLS_CC -#else - exception_ce -#endif - ); + exception_ce); /* RedisClusterException class */ INIT_CLASS_ENTRY(redis_cluster_exception_class_entry, "RedisClusterException", NULL); redis_cluster_exception_ce = zend_register_internal_class_ex( - &redis_cluster_exception_class_entry, -#if (PHP_MAJOR_VERSION < 7) - exception_ce, NULL TSRMLS_CC -#else - exception_ce -#endif - ); + &redis_cluster_exception_class_entry, exception_ce); /* Add shared class constants to Redis and RedisCluster objects */ add_class_constants(redis_ce, 0 TSRMLS_CC); @@ -921,7 +861,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) zval *object; char *host = NULL, *persistent_id = ""; zend_long port = -1, retry_interval = 0; - strlen_t host_len, persistent_id_len; + size_t host_len, persistent_id_len; double timeout = 0.0, read_timeout = 0.0; redis_object *redis; @@ -1588,7 +1528,7 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) RedisSock *redis_sock; zend_string *zpattern; char *key = NULL, *pattern = NULL, *store = NULL; - strlen_t keylen, patternlen, storelen; + size_t keylen, patternlen, storelen; zend_long offset = -1, count = -1; int argc = 1; /* SORT key is the simplest SORT command */ smart_string cmd = {0}; @@ -1814,7 +1754,7 @@ PHP_METHOD(Redis, info) { zval *object; RedisSock *redis_sock; char *cmd, *opt = NULL; - strlen_t opt_len; + size_t opt_len; int cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), @@ -1898,7 +1838,7 @@ void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) zval *zmem; char buf[64]; size_t keylen; - ulong idx; + zend_ulong idx; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", &object, redis_ce, &z_array) == FAILURE) @@ -1923,7 +1863,7 @@ void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, NULL); } else { keylen = snprintf(buf, sizeof(buf), "%ld", (long)idx); - redis_cmd_append_sstr_key(&cmd, buf, (strlen_t)keylen, redis_sock, NULL); + redis_cmd_append_sstr_key(&cmd, buf, keylen, redis_sock, NULL); } /* Append our value */ @@ -2434,6 +2374,8 @@ redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) return ret; } +/* TODO: Investigate/fix the odd logic going on in here. Looks like previous abort + * condidtions that are now simply empty if { } { } blocks. */ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, @@ -2450,28 +2392,29 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, } size_t len; char inbuf[255]; + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { } else if (strncmp(inbuf, "+OK", 3) != 0) { } + while ((fi = fi->next) && fi->fun) { if (redis_response_enqueued(redis_sock TSRMLS_CC) == SUCCESS) { } else { } } + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { } -#if (PHP_MAJOR_VERSION < 7) - zval *z_ret; - MAKE_STD_ZVAL(z_ret); -#else - zval zv, *z_ret = &zv; -#endif - array_init(z_ret); - add_next_index_zval(z_tab, z_ret); + + zval z_ret; + array_init(&z_ret); + add_next_index_zval(z_tab, &z_ret); int num = atol(inbuf + 1); - if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, z_ret TSRMLS_CC) < 0) { + + if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret TSRMLS_CC) < 0) { } + if (fi) fi = fi->next; } redis_sock->current = fi; @@ -2634,7 +2577,7 @@ PHP_METHOD(Redis, slaveof) zval *object; RedisSock *redis_sock; char *cmd = "", *host = NULL; - strlen_t host_len; + size_t host_len; zend_long port = 6379; int cmd_len; @@ -2730,7 +2673,7 @@ PHP_METHOD(Redis, config) zval *object; RedisSock *redis_sock; char *key = NULL, *val = NULL, *cmd, *op = NULL; - strlen_t key_len, val_len, op_len; + size_t key_len, val_len, op_len; enum {CFG_GET, CFG_SET} mode; int cmd_len; @@ -2785,7 +2728,7 @@ PHP_METHOD(Redis, slowlog) { RedisSock *redis_sock; char *arg, *cmd; int cmd_len; - strlen_t arg_len; + size_t arg_len; zend_long option = 0; enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; @@ -2925,7 +2868,7 @@ PHP_METHOD(Redis, pubsub) { RedisSock *redis_sock; char *keyword, *cmd; int cmd_len; - strlen_t kw_len; + size_t kw_len; PUBSUB_TYPE type; zval *arg = NULL; @@ -3295,7 +3238,7 @@ PHP_METHOD(Redis, client) { zval *object; RedisSock *redis_sock; char *cmd, *opt = NULL, *arg = NULL; - strlen_t opt_len, arg_len; + size_t opt_len, arg_len; int cmd_len; // Parse our method parameters @@ -3444,7 +3387,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { HashTable *hash; char *pattern = NULL, *cmd, *key = NULL; int cmd_len, num_elements, key_free = 0; - strlen_t key_len = 0, pattern_len = 0; + size_t key_len = 0, pattern_len = 0; zend_long count = 0, iter; /* Different prototype depending on if this is a key based scan */ diff --git a/redis_array.c b/redis_array.c index 79cf0804a8..1d989cb981 100644 --- a/redis_array.c +++ b/redis_array.c @@ -170,51 +170,6 @@ redis_array_free(RedisArray *ra) efree(ra); } -#if (PHP_MAJOR_VERSION < 7) -typedef struct { - zend_object std; - RedisArray *ra; -} redis_array_object; - -void -free_redis_array_object(void *object TSRMLS_DC) -{ - redis_array_object *obj = (redis_array_object *)object; - - zend_object_std_dtor(&obj->std TSRMLS_CC); - if (obj->ra) { - if (obj->ra->prev) redis_array_free(obj->ra->prev); - redis_array_free(obj->ra); - } - efree(obj); -} - -zend_object_value -create_redis_array_object(zend_class_entry *ce TSRMLS_DC) -{ - zend_object_value retval; - redis_array_object *obj = ecalloc(1, sizeof(redis_array_object)); - memset(obj, 0, sizeof(redis_array_object)); - - zend_object_std_init(&obj->std, ce TSRMLS_CC); - -#if PHP_VERSION_ID < 50399 - zval *tmp; - zend_hash_copy(obj->std.properties, &ce->default_properties, - (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); -#else - object_properties_init(&obj->std, ce); -#endif - - retval.handle = zend_objects_store_put(obj, - (zend_objects_store_dtor_t)zend_objects_destroy_object, - (zend_objects_free_object_storage_t)free_redis_array_object, - NULL TSRMLS_CC); - retval.handlers = zend_get_std_object_handlers(); - - return retval; -} -#else typedef struct { RedisArray *ra; zend_object std; @@ -251,7 +206,6 @@ create_redis_array_object(zend_class_entry *ce TSRMLS_DC) return &obj->std; } -#endif /** * redis_array_get @@ -505,7 +459,7 @@ PHP_METHOD(RedisArray, __call) zval *z_args; char *cmd; - strlen_t cmd_len; + size_t cmd_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, redis_array_ce, &cmd, &cmd_len, &z_args) == FAILURE) { @@ -545,7 +499,7 @@ PHP_METHOD(RedisArray, _target) zval *object; RedisArray *ra; char *key; - strlen_t key_len; + size_t key_len; zval *redis_inst; int i; @@ -571,7 +525,7 @@ PHP_METHOD(RedisArray, _instance) zval *object; RedisArray *ra; char *target; - strlen_t target_len; + size_t target_len; zval *z_redis; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", @@ -653,7 +607,7 @@ PHP_METHOD(RedisArray, _rehash) PHP_METHOD(RedisArray, _continuum) { int i; - zval *object; + zval *object, z_ret; RedisArray *ra; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", @@ -668,15 +622,10 @@ PHP_METHOD(RedisArray, _continuum) array_init(return_value); if (ra->continuum) { for (i = 0; i < ra->continuum->nb_points; ++i) { - zval zv, *z_tmp = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#endif - - array_init(z_tmp); - add_assoc_long(z_tmp, "index", ra->continuum->points[i].index); - add_assoc_long(z_tmp, "value", ra->continuum->points[i].value); - add_next_index_zval(return_value, z_tmp); + array_init(&z_ret); + add_assoc_long(&z_ret, "index", ra->continuum->points[i].index); + add_assoc_long(&z_ret, "value", ra->continuum->points[i].value); + add_next_index_zval(return_value, &z_ret); } } } @@ -685,6 +634,7 @@ PHP_METHOD(RedisArray, _continuum) static void multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int argc, zval *argv TSRMLS_DC) { + zval z_arg; int i; /* Init our array return */ @@ -692,15 +642,11 @@ multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int a /* Iterate our RedisArray nodes */ for (i = 0; i < ra->count; ++i) { - zval zv, *z_tmp = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#endif /* Call each node in turn */ - call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, z_tmp, argc, argv); + call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_arg, argc, argv); /* Add the result for this host */ - add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), z_tmp); + add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), &z_arg); } } @@ -788,7 +734,7 @@ PHP_METHOD(RedisArray, keys) zval *object, z_fun, z_args[1]; RedisArray *ra; char *pattern; - strlen_t pattern_len; + size_t pattern_len; /* Make sure the prototype is correct */ if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", @@ -846,7 +792,7 @@ PHP_METHOD(RedisArray, setOption) RedisArray *ra; zend_long opt; char *val_str; - strlen_t val_len; + size_t val_len; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", &object, redis_array_ce, &opt, &val_str, &val_len) == FAILURE) { @@ -896,34 +842,6 @@ PHP_METHOD(RedisArray, select) zval_dtor(&z_fun); } -#if (PHP_MAJOR_VERSION < 7) -#define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \ - if (ra && ra->z_multi_exec) { \ - int i, num_varargs;\ - zval ***varargs = NULL, *z_arg_array; \ - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O*",\ - &object, redis_array_ce, &varargs, &num_varargs) == FAILURE) {\ - RETURN_FALSE;\ - }\ - /* copy all args into a zval hash table */\ - MAKE_STD_ZVAL(z_arg_array); \ - array_init(z_arg_array);\ - for(i = 0; i < num_varargs; ++i) {\ - zval *z_tmp;\ - MAKE_STD_ZVAL(z_tmp); \ - ZVAL_ZVAL(z_tmp, *varargs[i], 1, 0); \ - add_next_index_zval(z_arg_array, z_tmp); \ - }\ - /* call */\ - ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, z_arg_array, NULL); \ - zval_ptr_dtor(&z_arg_array); \ - if(varargs) {\ - efree(varargs);\ - }\ - return;\ - }\ -}while(0) -#else #define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \ if (ra && ra->z_multi_exec) { \ int i, num_varargs; \ @@ -945,12 +863,11 @@ PHP_METHOD(RedisArray, select) return; \ } \ } while(0) -#endif /* MGET will distribute the call to several nodes and regroup the values. */ PHP_METHOD(RedisArray, mget) { - zval *object, *z_keys, z_argarray, *data, z_ret, *z_cur, z_tmp_array, *z_tmp; + zval *object, *z_keys, z_argarray, *data, z_ret, *z_cur, z_tmp_array; int i, j, n; RedisArray *ra; int *pos, argc, *argc_each; @@ -1027,14 +944,9 @@ PHP_METHOD(RedisArray, mget) for(i = 0; i < argc; ++i) { if(pos[i] != n) continue; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#else - zval zv; - z_tmp = &zv; -#endif - ZVAL_ZVAL(z_tmp, argv[i], 1, 0); - add_next_index_zval(&z_argarray, z_tmp); + zval z_ret; + ZVAL_ZVAL(&z_ret, argv[i], 1, 0); + add_next_index_zval(&z_argarray, &z_ret); } zval z_fun; @@ -1063,14 +975,9 @@ PHP_METHOD(RedisArray, mget) for(i = 0, j = 0; i < argc; ++i) { if (pos[i] != n || (z_cur = zend_hash_index_find(Z_ARRVAL(z_ret), j++)) == NULL) continue; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#else - zval zv; - z_tmp = &zv; -#endif - ZVAL_ZVAL(z_tmp, z_cur, 1, 0); - add_index_zval(&z_tmp_array, i, z_tmp); + zval z_ret; + ZVAL_ZVAL(&z_ret, z_cur, 1, 0); + add_index_zval(&z_tmp_array, i, &z_ret); } zval_dtor(&z_ret); } @@ -1080,14 +987,9 @@ PHP_METHOD(RedisArray, mget) for(i = 0; i < argc; ++i) { if ((z_cur = zend_hash_index_find(Z_ARRVAL(z_tmp_array), i)) == NULL) continue; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#else - zval zv; - z_tmp = &zv; -#endif - ZVAL_ZVAL(z_tmp, z_cur, 1, 0); - add_next_index_zval(return_value, z_tmp); + zval z_ret; + ZVAL_ZVAL(&z_ret, z_cur, 1, 0); + add_next_index_zval(return_value, &z_ret); } /* cleanup */ @@ -1170,16 +1072,13 @@ PHP_METHOD(RedisArray, mset) for(i = 0; i < argc; ++i) { if(pos[i] != n) continue; - zval zv, *z_tmp = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#endif + zval z_ret; if (argv[i] == NULL) { - ZVAL_NULL(z_tmp); + ZVAL_NULL(&z_ret); } else { - ZVAL_ZVAL(z_tmp, argv[i], 1, 0); + ZVAL_ZVAL(&z_ret, argv[i], 1, 0); } - add_assoc_zval_ex(&z_argarray, ZSTR_VAL(keys[i]), ZSTR_LEN(keys[i]), z_tmp); + add_assoc_zval_ex(&z_argarray, ZSTR_VAL(keys[i]), ZSTR_LEN(keys[i]), &z_ret); found++; } @@ -1257,15 +1156,10 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { array_init(&z_keys); for (i = 0; i < argc; ++i) { zval *z_arg = &z_args[i]; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#else - zval zv; - z_tmp = &zv; -#endif - ZVAL_ZVAL(z_tmp, z_arg, 1, 0); + zval z_ret; + ZVAL_ZVAL(&z_ret, z_arg, 1, 0); /* add copy to z_keys */ - add_next_index_zval(&z_keys, z_tmp); + add_next_index_zval(&z_keys, &z_ret); } free_zkeys = 1; } @@ -1319,14 +1213,9 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { for(i = 0; i < argc; ++i) { if(pos[i] != n) continue; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_tmp); -#else - zval zv; - z_tmp = &zv; -#endif - ZVAL_ZVAL(z_tmp, argv[i], 1, 0); - add_next_index_zval(&z_argarray, z_tmp); + zval z_ret; + ZVAL_ZVAL(&z_ret, argv[i], 1, 0); + add_next_index_zval(&z_argarray, &z_ret); found++; } @@ -1383,7 +1272,7 @@ PHP_METHOD(RedisArray, multi) RedisArray *ra; zval *z_redis; char *host; - strlen_t host_len; + size_t host_len; zend_long multi_value = MULTI; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", diff --git a/redis_array.h b/redis_array.h index de44465426..1ee192fae7 100644 --- a/redis_array.h +++ b/redis_array.h @@ -49,7 +49,6 @@ typedef struct { } Continuum; typedef struct RedisArray_ { - int count; zend_string **hosts; /* array of host:port strings */ zval *redis; /* array of Redis instances */ @@ -67,13 +66,7 @@ typedef struct RedisArray_ { struct RedisArray_ *prev; } RedisArray; -#if (PHP_MAJOR_VERSION < 7) -zend_object_value create_redis_array_object(zend_class_entry *ce TSRMLS_DC); -void free_redis_array_object(void *object TSRMLS_DC); -#else zend_object *create_redis_array_object(zend_class_entry *ce TSRMLS_DC); void free_redis_array_object(zend_object *object); -#endif - #endif diff --git a/redis_array_impl.c b/redis_array_impl.c index 4ee72d8a0a..aeb08e206b 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -65,9 +65,6 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b } /* create Redis object */ -#if (PHP_MAJOR_VERSION < 7) - INIT_PZVAL(&ra->redis[i]); -#endif object_init_ex(&ra->redis[i], redis_ce); call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL); zval_dtor(&z_ret); @@ -465,11 +462,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) zval z_ret, z_argv; /* check that we can call the extractor function */ -#if (PHP_MAJOR_VERSION < 7) - if (!zend_is_callable_ex(&ra->z_fun, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { -#else if (!zend_is_callable_ex(&ra->z_fun, NULL, 0, NULL, NULL, NULL)) { -#endif php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function"); return NULL; } @@ -480,11 +473,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); if (Z_TYPE(z_ret) == IS_STRING) { -#if (PHP_MAJOR_VERSION < 7) - out = zend_string_init(Z_STRVAL(z_ret), Z_STRLEN(z_ret), 0); -#else out = zval_get_string(&z_ret); -#endif } zval_dtor(&z_argv); @@ -514,11 +503,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) zval z_ret, z_argv; /* check that we can call the extractor function */ -#if (PHP_MAJOR_VERSION < 7) - if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL, NULL TSRMLS_CC)) { -#else if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL)) { -#endif php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); return -1; } @@ -672,28 +657,23 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { zval z_keys, *z_val; zend_string *zkey; - ulong idx; + zend_ulong idx; + /* Initialize key array */ -#if PHP_VERSION_ID > 50300 array_init_size(&z_keys, zend_hash_num_elements(Z_ARRVAL_P(z_pairs))); -#else - array_init(&z_keys); -#endif /* Go through input array and add values to the key array */ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_pairs), idx, zkey, z_val) { - zval zv, *z_new = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_new); -#endif + zval z_new; + PHPREDIS_NOTUSED(z_val); if (zkey) { - ZVAL_STRINGL(z_new, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); + ZVAL_STRINGL(&z_new, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); } else { - ZVAL_LONG(z_new, idx); + ZVAL_LONG(&z_new, idx); } - zend_hash_next_index_insert(Z_ARRVAL(z_keys), z_new); + zend_hash_next_index_insert(Z_ARRVAL(z_keys), &z_new); } ZEND_HASH_FOREACH_END(); /* add keys to index */ @@ -1192,20 +1172,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, zval zv, *z_ret = &zv; ZVAL_NULL(z_ret); -#if (PHP_MAJOR_VERSION < 7) - zval *z_host, *z_count, **z_args_pp[2]; - - MAKE_STD_ZVAL(z_host); - ZVAL_STRINGL(z_host, ZSTR_VAL(hostname), ZSTR_LEN(hostname)); - z_args_pp[0] = &z_host; - - MAKE_STD_ZVAL(z_count); - ZVAL_LONG(z_count, count); - z_args_pp[1] = &z_count; - z_cb->params = z_args_pp; - z_cb->retval_ptr_ptr = &z_ret; -#else zval z_args[2]; ZVAL_STRINGL(&z_args[0], ZSTR_VAL(hostname), ZSTR_LEN(hostname)); @@ -1213,7 +1180,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, z_cb->params = z_args; z_cb->retval = z_ret; -#endif + z_cb->no_separation = 0; z_cb->param_count = 2; @@ -1221,12 +1188,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, zend_call_function(z_cb, z_cb_cache TSRMLS_CC); /* cleanup */ -#if (PHP_MAJOR_VERSION < 7) - zval_ptr_dtor(&z_host); - zval_ptr_dtor(&z_count); -#else zval_dtor(&z_args[0]); -#endif zval_dtor(z_ret); } diff --git a/redis_cluster.c b/redis_cluster.c index 9bc4254707..fd42db36b8 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -291,43 +291,23 @@ zend_function_entry redis_cluster_functions[] = { }; /* Our context seeds will be a hash table with RedisSock* pointers */ -#if (PHP_MAJOR_VERSION < 7) -static void ht_free_seed(void *data) -#else -static void ht_free_seed(zval *data) -#endif -{ +static void ht_free_seed(zval *data) { RedisSock *redis_sock = *(RedisSock**)data; if (redis_sock) redis_free_socket(redis_sock); } /* Free redisClusterNode objects we've stored */ -#if (PHP_MAJOR_VERSION < 7) -static void ht_free_node(void *data) -#else -static void ht_free_node(zval *data) -#endif -{ +static void ht_free_node(zval *data) { redisClusterNode *node = *(redisClusterNode**)data; cluster_free_node(node); } /* Create redisCluster context */ -#if (PHP_MAJOR_VERSION < 7) -zend_object_value -create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { - redisCluster *cluster; - - // Allocate our actual struct - cluster = ecalloc(1, sizeof(redisCluster)); -#else -zend_object * -create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { +zend_object * create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { redisCluster *cluster; // Allocate our actual struct cluster = ecalloc(1, sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); -#endif // We're not currently subscribed anywhere cluster->subscribed_slot = -1; @@ -345,25 +325,7 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { // Initialize it zend_object_std_init(&cluster->std, class_type TSRMLS_CC); -#if (PHP_MAJOR_VERSION < 7) - zend_object_value retval; -#if PHP_VERSION_ID < 50399 - zval *tmp; - zend_hash_copy(cluster->std.properties, &class_type->default_properties, - (copy_ctor_func_t)zval_add_ref, (void*)&tmp, sizeof(zval*)); -#else - object_properties_init(&cluster->std, class_type); -#endif - - retval.handle = zend_objects_store_put(cluster, - (zend_objects_store_dtor_t)zend_objects_destroy_object, - free_cluster_context, NULL TSRMLS_CC); - - retval.handlers = zend_get_std_object_handlers(); - - return retval; -#else object_properties_init(&cluster->std, class_type); memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std); @@ -372,36 +334,20 @@ create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { cluster->std.handlers = &RedisCluster_handlers; return &cluster->std; -#endif } /* Free redisCluster context */ -#if (PHP_MAJOR_VERSION < 7) -void -free_cluster_context(void *object TSRMLS_DC) -{ - redisCluster *cluster = (redisCluster*)object; - - cluster_free(cluster, 0 TSRMLS_CC); - zend_object_std_dtor(&cluster->std TSRMLS_CC); - efree(cluster); -} -#else -void -free_cluster_context(zend_object *object) -{ +void free_cluster_context(zend_object *object) { redisCluster *cluster = (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std)); cluster_free(cluster, 0 TSRMLS_CC); zend_object_std_dtor(&cluster->std TSRMLS_CC); } -#endif /* Attempt to connect to a Redis cluster provided seeds and timeout options */ -static void -redis_cluster_init(redisCluster *c, HashTable *ht_seeds, - double timeout, double read_timeout, int persistent, - char *auth, strlen_t auth_len TSRMLS_DC) +static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, + double read_timeout, int persistent, char *auth, + size_t auth_len TSRMLS_DC) { // Validate timeout if (timeout < 0L || timeout > INT_MAX) { @@ -448,7 +394,7 @@ redis_cluster_init(redisCluster *c, HashTable *ht_seeds, void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { zval z_seeds, z_timeout, z_read_timeout, z_persistent, z_auth, *z_value; char *iptr, *auth = NULL; - strlen_t auth_len = 0; + size_t auth_len = 0; double timeout = 0, read_timeout = 0; int persistent = 0; HashTable *ht_seeds = NULL; @@ -540,7 +486,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { PHP_METHOD(RedisCluster, __construct) { zval *object, *z_seeds = NULL; char *name, *auth = NULL; - strlen_t name_len, auth_len = 0; + size_t name_len, auth_len = 0; double timeout = 0.0, read_timeout = 0.0; zend_bool persistent = 0; redisCluster *context = GET_CONTEXT(); @@ -637,12 +583,12 @@ typedef struct clusterKeyValHT { char kbuf[22]; char *key; - strlen_t key_len; + size_t key_len; int key_free; short slot; char *val; - strlen_t val_len; + size_t val_len; int val_free; } clusterKeyValHT; @@ -655,18 +601,11 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, // Grab the key, convert it to a string using provided kbuf buffer if it's // a LONG style key -#if (PHP_MAJOR_VERSION < 7) - uint key_len; - switch(zend_hash_get_current_key_ex(ht, &(kv->key), &key_len, &idx, 0, ptr)) { - case HASH_KEY_IS_STRING: - kv->key_len = (int)(key_len-1); -#else zend_string *zkey; switch (zend_hash_get_current_key_ex(ht, &zkey, &idx, ptr)) { case HASH_KEY_IS_STRING: kv->key_len = ZSTR_LEN(zkey); kv->key = ZSTR_VAL(zkey); -#endif break; case HASH_KEY_IS_LONG: kv->key_len = snprintf(kv->kbuf,sizeof(kv->kbuf),"%ld",(long)idx); @@ -973,14 +912,8 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { - zval *z_ret; - -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_ret); -#else - z_ret = emalloc(sizeof(zval)); -#endif - + zval *z_ret = emalloc(sizeof(*z_ret)); + // Initialize a LONG value to zero for our return ZVAL_LONG(z_ret, 0); @@ -1005,14 +938,8 @@ PHP_METHOD(RedisCluster, unlink) { /* {{{ proto array RedisCluster::mget(array keys) */ PHP_METHOD(RedisCluster, mget) { - zval *z_ret; + zval *z_ret = emalloc(sizeof(*z_ret)); - // Array response -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_ret); -#else - z_ret = emalloc(sizeof(zval)); -#endif array_init(z_ret); // Parse args, process @@ -1027,14 +954,8 @@ PHP_METHOD(RedisCluster, mget) { /* {{{ proto bool RedisCluster::mset(array keyvalues) */ PHP_METHOD(RedisCluster, mset) { - zval *z_ret; + zval *z_ret = emalloc(sizeof(*z_ret)); - // Response, defaults to TRUE -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_ret); -#else - z_ret = emalloc(sizeof(zval)); -#endif ZVAL_TRUE(z_ret); // Parse args and process. If we get a failure, free zval and return FALSE. @@ -1048,19 +969,13 @@ PHP_METHOD(RedisCluster, mset) { /* {{{ proto array RedisCluster::msetnx(array keyvalues) */ PHP_METHOD(RedisCluster, msetnx) { - zval *z_ret; + zval *z_ret = emalloc(sizeof(*z_ret)); - // Array response -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_ret); -#else - z_ret = emalloc(sizeof(zval)); -#endif array_init(z_ret); // Parse args and process. If we get a failure, free mem and return FALSE if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", - sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp) ==-1) + sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp) ==-1) { zval_dtor(z_ret); efree(z_ret); @@ -1103,7 +1018,7 @@ PHP_METHOD(RedisCluster, exists) { PHP_METHOD(RedisCluster, keys) { redisCluster *c = GET_CONTEXT(); redisClusterNode *node; - strlen_t pat_len; + size_t pat_len; char *pat, *cmd; clusterReply *resp; int i, cmd_len; @@ -2051,14 +1966,13 @@ PHP_METHOD(RedisCluster, _masters) { ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) break; - zval z, *z_sub = &z; + zval z_sub; - REDIS_MAKE_STD_ZVAL(z_sub); - array_init(z_sub); + array_init(&z_sub); - add_next_index_stringl(z_sub, ZSTR_VAL(node->sock->host), ZSTR_LEN(node->sock->host)); - add_next_index_long(z_sub, node->sock->port); - add_next_index_zval(return_value, z_sub); + add_next_index_stringl(&z_sub, ZSTR_VAL(node->sock->host), ZSTR_LEN(node->sock->host)); + add_next_index_long(&z_sub, node->sock->port); + add_next_index_zval(return_value, &z_sub); } ZEND_HASH_FOREACH_END(); } @@ -2273,7 +2187,7 @@ PHP_METHOD(RedisCluster, discard) { static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { - strlen_t key_len; + size_t key_len; int key_free; zval *z_host, *z_port; short slot; @@ -2475,7 +2389,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, { redisCluster *c = GET_CONTEXT(); char *cmd, *pat = NULL, *key = NULL; - strlen_t key_len = 0, pat_len = 0; + size_t key_len = 0, pat_len = 0; int cmd_len, key_free = 0; short slot; zval *z_it; @@ -2568,7 +2482,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, PHP_METHOD(RedisCluster, scan) { redisCluster *c = GET_CONTEXT(); char *cmd, *pat = NULL; - strlen_t pat_len = 0; + size_t pat_len = 0; int cmd_len; short slot; zval *z_it, *z_node; @@ -2727,7 +2641,7 @@ PHP_METHOD(RedisCluster, info) { REDIS_REPLY_TYPE rtype; char *cmd, *opt = NULL; int cmd_len; - strlen_t opt_len = 0; + size_t opt_len = 0; void *ctx = NULL; zval *z_arg; @@ -2780,7 +2694,7 @@ PHP_METHOD(RedisCluster, client) { redisCluster *c = GET_CONTEXT(); char *cmd, *opt = NULL, *arg = NULL; int cmd_len; - strlen_t opt_len, arg_len = 0; + size_t opt_len, arg_len = 0; REDIS_REPLY_TYPE rtype; zval *z_node; short slot; @@ -3059,7 +2973,7 @@ PHP_METHOD(RedisCluster, echo) { zval *z_arg; char *cmd, *msg; int cmd_len; - strlen_t msg_len; + size_t msg_len; short slot; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &msg, diff --git a/redis_cluster.h b/redis_cluster.h index b4d0a8bab5..9e5366785a 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -10,13 +10,8 @@ #define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) /* Get attached object context */ -#if (PHP_MAJOR_VERSION < 7) -#define GET_CONTEXT() \ - ((redisCluster*)zend_object_store_get_object(getThis() TSRMLS_CC)) -#else #define GET_CONTEXT() \ ((redisCluster *)((char *)Z_OBJ_P(getThis()) - XtOffsetOf(redisCluster, std))) -#endif /* Command building/processing is identical for every command */ #define CLUSTER_BUILD_CMD(name, c, cmd, cmd_len, slot) \ @@ -104,17 +99,11 @@ /* For the creation of RedisCluster specific exceptions */ PHP_REDIS_API zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); -#if (PHP_MAJOR_VERSION < 7) -/* Create cluster context */ -zend_object_value create_cluster_context(zend_class_entry *class_type TSRMLS_DC); -/* Free cluster context struct */ -void free_cluster_context(void *object TSRMLS_DC); -#else /* Create cluster context */ zend_object *create_cluster_context(zend_class_entry *class_type TSRMLS_DC); + /* Free cluster context struct */ void free_cluster_context(zend_object *object); -#endif /* Inittialize our class with PHP */ diff --git a/redis_commands.c b/redis_commands.c index 9dbb40f7c6..078d12c01e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -173,7 +173,7 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { char *arg; - strlen_t arg_len; + size_t arg_len; // Parse args if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) @@ -194,7 +194,7 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key = NULL; - strlen_t key_len; + size_t key_len; zend_long expire; zval *z_val; @@ -215,7 +215,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *val; - strlen_t key_len, val_len; + size_t key_len, val_len; zend_long lval; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &key, &key_len, @@ -235,7 +235,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; zval *z_val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, @@ -255,7 +255,7 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *val; - strlen_t key_len, val_len; + size_t key_len, val_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &val, &val_len) == FAILURE) @@ -275,7 +275,7 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *k, *v1, *v2; - strlen_t klen, v1len, v2len; + size_t klen, v1len, v2len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &k, &klen, &v1, &v1len, &v2, &v2len) == FAILURE) @@ -295,7 +295,7 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *k1, *k2; - strlen_t k1len, k2len; + size_t k1len, k2len; int k1free, k2free; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &k1, &k1len, @@ -343,7 +343,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - strlen_t keylen; + size_t keylen; zend_long lval; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &key, &keylen, &lval) @@ -382,7 +382,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; zend_long val1, val2; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &key, &key_len, @@ -402,7 +402,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) ==FAILURE) @@ -439,7 +439,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; double val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &key, &key_len, @@ -497,7 +497,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *slot, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; zend_long start, end; zend_bool ws = 0; @@ -533,7 +533,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *start, *end; int has_limit = 0; long offset, count; - strlen_t key_len, start_len, end_len; + size_t key_len, start_len, end_len; zval *z_opt=NULL, *z_ele; zend_string *zkey; ulong idx; @@ -605,7 +605,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *agg_op=NULL; int key_free, argc = 2, keys_count; - strlen_t key_len, agg_op_len = 0; + size_t key_len, agg_op_len = 0; zval *z_keys, *z_weights=NULL, *z_ele; HashTable *ht_keys, *ht_weights=NULL; smart_string cmdstr = {0}; @@ -672,7 +672,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_keys, z_ele) { zend_string *zstr = zval_get_string(z_ele); char *key = ZSTR_VAL(zstr); - strlen_t key_len = ZSTR_LEN(zstr); + size_t key_len = ZSTR_LEN(zstr); // Prefix key if necissary int key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -760,7 +760,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_chan; smart_string cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); - strlen_t key_len; + size_t key_len; int key_free; char *key; @@ -839,7 +839,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_arr, z_chan) { char *key = Z_STRVAL_P(z_chan); - strlen_t key_len = Z_STRLEN_P(z_chan); + size_t key_len = Z_STRLEN_P(z_chan); int key_free; key_free = redis_key_prefix(redis_sock, &key, &key_len); @@ -861,7 +861,7 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *min, *max; - strlen_t key_len, min_len, max_len; + size_t key_len, min_len, max_len; int argc = ZEND_NUM_ARGS(); zend_long offset, count; @@ -902,7 +902,7 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Validate ZLEX* min/max argument strings */ -static int validate_zlex_arg(const char *arg, strlen_t len) { +static int validate_zlex_arg(const char *arg, size_t len) { return (len > 1 && (*arg == '[' || *arg == '(')) || (len == 1 && (*arg == '+' || *arg == '-')); } @@ -913,7 +913,7 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { char *key, *min, *max; - strlen_t key_len, min_len, max_len; + size_t key_len, min_len, max_len; /* Parse args */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, @@ -946,7 +946,7 @@ int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw HashTable *ht_arr; zend_long num_keys = 0; smart_string cmdstr = {0}; - strlen_t lua_len; + size_t lua_len; zend_string *zstr; short prevslot = -1; @@ -1010,7 +1010,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_args; smart_string cmdstr = {0}; - strlen_t i; + size_t i; int argc = ZEND_NUM_ARGS(); // We at least need a key and one value @@ -1061,7 +1061,7 @@ static int gen_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; zend_string *zstr; int key_free, val_free, argc = 1; - strlen_t val_len, key_len; + size_t val_len, key_len; char *key, *val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, @@ -1129,7 +1129,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_arr; char *key; int key_free, i, tail; - strlen_t key_len; + size_t key_len; int single_array = 0, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; long timeout = 0; @@ -1263,7 +1263,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_value, *z_opts=NULL; char *key = NULL, *exp_type = NULL, *set_type = NULL; long expire = -1; - strlen_t key_len; + size_t key_len; // Make sure the function is being called correctly if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, @@ -1350,7 +1350,7 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key1, *key2; - strlen_t key1_len, key2_len; + size_t key1_len, key2_len; int key1_free, key2_free; short slot1, slot2; zend_long timeout; @@ -1408,7 +1408,7 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, short *slot, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; zend_long val = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, @@ -1458,7 +1458,7 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; - strlen_t key_len, mem_len; + size_t key_len, mem_len; zend_long byval; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key, &key_len, @@ -1479,7 +1479,7 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem; - strlen_t key_len, mem_len; + size_t key_len, mem_len; double byval; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssd", &key, &key_len, @@ -1503,7 +1503,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; zval *z_arr, *z_mems, *z_mem; int i, count, valid = 0, key_free; - strlen_t key_len; + size_t key_len; HashTable *ht_arr; smart_string cmdstr = {0}; @@ -1586,7 +1586,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key; int key_free, count; - strlen_t key_len; + size_t key_len; ulong idx; zval *z_arr; HashTable *ht_vals; @@ -1619,7 +1619,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Start traversing our key => value array ZEND_HASH_FOREACH_KEY_VAL(ht_vals, idx, zkey, z_val) { char *mem, *val, kbuf[40]; - strlen_t val_len; + size_t val_len; int val_free; unsigned int mem_len; @@ -1663,7 +1663,7 @@ redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *field; - strlen_t key_len, field_len; + size_t key_len, field_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &field, &field_len) == FAILURE @@ -1683,7 +1683,7 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int argc; zend_long bit, start, end; - strlen_t key_len; + size_t key_len; argc = ZEND_NUM_ARGS(); if (zend_parse_parameters(argc TSRMLS_CC, "sl|ll", &key, &key_len, &bit, @@ -1717,7 +1717,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_args; char *key; - strlen_t key_len; + size_t key_len; int i, key_free, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; short kslot; @@ -1785,7 +1785,7 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; zend_long start = 0, end = -1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &key, &key_len, @@ -1811,7 +1811,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; char *mem, *key; int key_free, mem_free, argc=1; - strlen_t key_len, mem_len; + size_t key_len, mem_len; zend_string *zstr; // Parse arguments @@ -1910,7 +1910,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_keys; smart_string cmdstr = {0}; int num_keys, key_free; - strlen_t key_len; + size_t key_len; char *key; short kslot=-1; zend_string *zstr; @@ -1995,7 +1995,7 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *pw; - strlen_t pw_len; + size_t pw_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pw, &pw_len) ==FAILURE) @@ -2019,7 +2019,7 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; zend_long offset; zend_bool val; @@ -2046,7 +2046,7 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *pos; - strlen_t key_len, pos_len; + size_t key_len, pos_len; zval *z_val, *z_pivot; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sszz", &key, &key_len, @@ -2075,7 +2075,7 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; zend_long count = 0; zval *z_val; @@ -2096,7 +2096,7 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *src, *dst; - strlen_t src_len, dst_len; + size_t src_len, dst_len; int src_free, dst_free; zval *z_val; @@ -2140,7 +2140,7 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot) { char *key, *mem; - strlen_t key_len, mem_len; + size_t key_len, mem_len; zval *z_val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &key, &key_len, @@ -2178,7 +2178,7 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *have_count) { char *key; - strlen_t key_len; + size_t key_len; zend_long count; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, @@ -2205,7 +2205,7 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - strlen_t key_len; + size_t key_len; double incrby; zval *z_val; @@ -2229,7 +2229,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; HashTable *ht_opts; smart_string cmdstr = {0}; - strlen_t key_len; + size_t key_len; int key_free; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &key, &key_len, @@ -2446,7 +2446,7 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; char *arg; int arg_free, i; - strlen_t arg_len; + size_t arg_len; int argc = ZEND_NUM_ARGS(); zend_string *zstr; @@ -2503,7 +2503,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zval *z_args; char *key, *val, *exp_type = NULL; - strlen_t key_len, val_len; + size_t key_len, val_len; int key_free, val_free; int num = ZEND_NUM_ARGS(), i = 1, argc; zend_bool ch = 0, incr = 0; @@ -2611,7 +2611,7 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short *slot, void **ctx) { char *key, *subcmd; - strlen_t key_len, subcmd_len; + size_t key_len, subcmd_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &subcmd, &subcmd_len, &key, &key_len) == FAILURE) @@ -2645,7 +2645,7 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *source, *dest, *unit = NULL; - strlen_t keylen, sourcelen, destlen, unitlen; + size_t keylen, sourcelen, destlen, unitlen; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|s", &key, &keylen, &source, &sourcelen, &dest, &destlen, &unit, @@ -2745,7 +2745,7 @@ void append_georadius_opts(RedisSock *redis_sock, smart_string *str, short *slot geoOptions *opt) { char *key; - strlen_t keylen; + size_t keylen; int keyfree; if (opt->withcoord) @@ -2794,7 +2794,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *key, *unit; short store_slot = 0; - strlen_t keylen, unitlen; + size_t keylen, unitlen; int argc = 5, keyfree; double lng, lat, radius; zval *opts = NULL; @@ -2863,7 +2863,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *mem, *unit; - strlen_t keylen, memlen, unitlen; + size_t keylen, memlen, unitlen; short store_slot = 0; int keyfree, argc = 4; double radius; @@ -2931,7 +2931,7 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *host, *key; int argc, keyfree; zval *z_keys, *z_key; - strlen_t hostlen, keylen; + size_t hostlen, keylen; zend_long destdb, port, timeout; zend_bool copy = 0, replace = 0; zend_string *zstr; @@ -3111,7 +3111,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { char *kw=NULL; zval *z_arg; - strlen_t kw_len; + size_t kw_len; /* Parse our args */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sz", &kw, &kw_len, @@ -3186,7 +3186,7 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_fields; int fcount, argc; char *key, *id; - strlen_t keylen, idlen; + size_t keylen, idlen; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa|lb", &key, &keylen, &id, &idlen, &z_fields, &maxlen, &approx) == FAILURE) @@ -3240,7 +3240,7 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { smart_string cmdstr = {0}; char *key, *group, *start = NULL, *end = NULL, *consumer = NULL; - strlen_t keylen, grouplen, startlen, endlen, consumerlen; + size_t keylen, grouplen, startlen, endlen, consumerlen; int argc; zend_long count = -1; @@ -3288,7 +3288,7 @@ int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { smart_string cmdstr = {0}; char *key, *start, *end; - strlen_t keylen, startlen, endlen; + size_t keylen, startlen, endlen; zend_long count = -1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|l", &key, &keylen, @@ -3432,7 +3432,7 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_streams; HashTable *kt; char *group, *consumer; - strlen_t grouplen, consumerlen; + size_t grouplen, consumerlen; int scount, argc; zend_long count = -1, block = -1; @@ -3487,7 +3487,7 @@ int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { smart_string cmdstr = {0}; char *key, *group; - strlen_t keylen, grouplen; + size_t keylen, grouplen; zend_string *idstr; zval *z_ids, *z_id; HashTable *ht_ids; @@ -3579,7 +3579,7 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt TSRMLS_DC) { HashTable *ht; zend_string *zkey; char *kval; - strlen_t klen; + size_t klen; ulong idx; zval *zv; @@ -3671,7 +3671,7 @@ int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { smart_string cmdstr = {0}; char *key, *group, *consumer; - strlen_t keylen, grouplen, consumerlen; + size_t keylen, grouplen, consumerlen; zend_long min_idle; int argc, id_count; zval *z_ids, *z_id, *z_opts = NULL; @@ -3730,7 +3730,7 @@ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *op, *key = NULL, *arg1 = NULL, *arg2 = NULL; - strlen_t oplen, keylen, arg1len, arg2len; + size_t oplen, keylen, arg1len, arg2len; zend_bool mkstream = 0; int argc = ZEND_NUM_ARGS(); @@ -3780,7 +3780,7 @@ int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *op, *key, *arg; - strlen_t oplen, keylen, arglen; + size_t oplen, keylen, arglen; char fmt[4]; int argc = ZEND_NUM_ARGS(); @@ -3805,7 +3805,7 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key; - strlen_t keylen; + size_t keylen; zend_long maxlen; zend_bool approx = 0; @@ -3874,7 +3874,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, zend_long option; char *val_str; struct timeval read_tv; - strlen_t val_len; + size_t val_len; int tcp_keepalive = 0; php_netstream_data_t *sock; @@ -3972,7 +3972,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *key; - strlen_t key_len; + size_t key_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) ==FAILURE) @@ -3994,7 +3994,7 @@ void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, { zval *z_val; char *val; - strlen_t val_len; + size_t val_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_val) == FAILURE) { RETURN_FALSE; @@ -4010,7 +4010,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_class_entry *ex) { char *value; - strlen_t value_len; + size_t value_len; // Parse our arguments if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) diff --git a/redis_session.c b/redis_session.c index a02c1897d8..f43021e761 100644 --- a/redis_session.c +++ b/redis_session.c @@ -52,11 +52,7 @@ #define NEGATIVE_LOCK_RESPONSE 1 ps_module ps_mod_redis = { -#if (PHP_MAJOR_VERSION < 7) - PS_MOD_SID(redis) -#else PS_MOD_UPDATE_TIMESTAMP(redis) -#endif }; ps_module ps_mod_redis_cluster = { @@ -586,52 +582,33 @@ PS_CREATE_SID_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); if (!pool) { -#if (PHP_MAJOR_VERSION < 7) - return php_session_create_id(NULL, newlen TSRMLS_CC); -#else return php_session_create_id(NULL TSRMLS_CC); -#endif } while (retries-- > 0) { -#if (PHP_MAJOR_VERSION < 7) - char* sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC); - redis_pool_member *rpm = redis_pool_get_sock(pool, sid TSRMLS_CC); -#else zend_string* sid = php_session_create_id((void **) &pool TSRMLS_CC); redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid) TSRMLS_CC); -#endif + RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if (!rpm || !redis_sock) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Redis not available while creating session_id"); -#if (PHP_MAJOR_VERSION < 7) - efree(sid); - return php_session_create_id(NULL, newlen TSRMLS_CC); -#else zend_string_release(sid); return php_session_create_id(NULL TSRMLS_CC); -#endif } if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); -#if (PHP_MAJOR_VERSION < 7) - pool->lock_status.session_key = redis_session_key(rpm, sid, strlen(sid)); -#else pool->lock_status.session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid)); -#endif + if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) == SUCCESS) { return sid; } zend_string_release(pool->lock_status.session_key); -#if (PHP_MAJOR_VERSION < 7) - efree(sid); -#else zend_string_release(sid); -#endif + sid = NULL; } @@ -642,7 +619,6 @@ PS_CREATE_SID_FUNC(redis) } /* }}} */ -#if (PHP_MAJOR_VERSION >= 7) /* {{{ PS_VALIDATE_SID_FUNC */ PS_VALIDATE_SID_FUNC(redis) @@ -686,9 +662,7 @@ PS_VALIDATE_SID_FUNC(redis) } } /* }}} */ -#endif -#if (PHP_MAJOR_VERSION >= 7) /* {{{ PS_UPDATE_TIMESTAMP_FUNC */ PS_UPDATE_TIMESTAMP_FUNC(redis) @@ -733,7 +707,6 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) } } /* }}} */ -#endif /* {{{ PS_READ_FUNC */ @@ -741,13 +714,8 @@ PS_READ_FUNC(redis) { char *resp, *cmd; int resp_len, cmd_len; -#if (PHP_MAJOR_VERSION < 7) - const char *skey = key; - size_t skeylen = strlen(key); -#else const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key); -#endif if (!skeylen) return FAILURE; @@ -780,22 +748,13 @@ PS_READ_FUNC(redis) if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL && resp_len != -1) { return FAILURE; } -#if (PHP_MAJOR_VERSION < 7) - if (resp_len < 0) { - *val = STR_EMPTY_ALLOC(); - *vallen = 0; - } else { - *val = resp; - *vallen = resp_len; - } -#else + if (resp_len < 0) { *val = ZSTR_EMPTY_ALLOC(); } else { *val = zend_string_init(resp, resp_len, 0); } efree(resp); -#endif return SUCCESS; } @@ -807,13 +766,8 @@ PS_WRITE_FUNC(redis) { char *cmd, *response; int cmd_len, response_len; -#if (PHP_MAJOR_VERSION < 7) - const char *skey = key, *sval = val; - size_t skeylen = strlen(key), svallen = vallen; -#else const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); -#endif if (!skeylen) return FAILURE; @@ -826,18 +780,7 @@ PS_WRITE_FUNC(redis) /* send SET command */ zend_string *session = redis_session_key(rpm, skey, skeylen); -#if (PHP_MAJOR_VERSION < 7) - /* We need to check for PHP5 if the session key changes (a bug with session_regenerate_id() is causing a missing PS_CREATE_SID call)*/ - if (!zend_string_equals(pool->lock_status.session_key, session)) { - zend_string_release(pool->lock_status.session_key); - pool->lock_status.session_key = zend_string_init(ZSTR_VAL(session), ZSTR_LEN(session), 0); - if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { - zend_string_release(pool->lock_status.session_key); - zend_string_release(session); - return FAILURE; - } - } -#endif + cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, INI_INT("session.gc_maxlifetime"), sval, svallen); zend_string_release(session); @@ -868,13 +811,8 @@ PS_DESTROY_FUNC(redis) { char *cmd, *response; int cmd_len, response_len; -#if (PHP_MAJOR_VERSION < 7) - const char *skey = key; - size_t skeylen = strlen(key); -#else const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key); -#endif redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); @@ -982,7 +920,7 @@ PS_OPEN_FUNC(rediscluster) { HashTable *ht_conf, *ht_seeds; double timeout = 0, read_timeout = 0; int retval, persistent = 0, failover = REDIS_FAILOVER_NONE; - strlen_t prefix_len, auth_len = 0; + size_t prefix_len, auth_len = 0; char *prefix, *auth = NULL; /* Parse configuration for session handler */ @@ -1078,11 +1016,8 @@ PS_READ_FUNC(rediscluster) { short slot; /* Set up our command and slot information */ -#if (PHP_MAJOR_VERSION < 7) - skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); -#else skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); -#endif + cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "GET", "s", skey, skeylen); efree(skey); @@ -1104,17 +1039,6 @@ PS_READ_FUNC(rediscluster) { } /* Push reply value to caller */ -#if (PHP_MAJOR_VERSION < 7) - if (reply->str == NULL) { - *val = STR_EMPTY_ALLOC(); - *vallen = 0; - } else { - *val = reply->str; - *vallen = reply->len; - } - - free_flag = 0; -#else if (reply->str == NULL) { *val = ZSTR_EMPTY_ALLOC(); } else { @@ -1122,7 +1046,6 @@ PS_READ_FUNC(rediscluster) { } free_flag = 1; -#endif /* Clean up */ cluster_free_reply(reply, free_flag); @@ -1141,17 +1064,10 @@ PS_WRITE_FUNC(rediscluster) { short slot; /* Set up command and slot info */ -#if (PHP_MAJOR_VERSION < 7) - skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "SETEX", "sds", skey, - skeylen, INI_INT("session.gc_maxlifetime"), val, - vallen); -#else skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "SETEX", "sds", skey, skeylen, INI_INT("session.gc_maxlifetime"), ZSTR_VAL(val), ZSTR_LEN(val)); -#endif efree(skey); /* Attempt to send command */ @@ -1187,11 +1103,8 @@ PS_DESTROY_FUNC(rediscluster) { short slot; /* Set up command and slot info */ -#if (PHP_MAJOR_VERSION < 7) - skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot); -#else skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); -#endif + cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "DEL", "s", skey, skeylen); efree(skey); diff --git a/redis_session.h b/redis_session.h index 6c6b101fc5..1529c05d4e 100644 --- a/redis_session.h +++ b/redis_session.h @@ -11,10 +11,8 @@ PS_DESTROY_FUNC(redis); PS_GC_FUNC(redis); PS_CREATE_SID_FUNC(redis); -#if (PHP_MAJOR_VERSION >= 7) PS_VALIDATE_SID_FUNC(redis); PS_UPDATE_TIMESTAMP_FUNC(redis); -#endif PS_OPEN_FUNC(rediscluster); PS_CLOSE_FUNC(rediscluster); From 57eea8e5d600eb86cc3182f73d499bd51570fd48 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 27 Jan 2019 11:39:26 -0800 Subject: [PATCH 1068/1986] Add a way for people to donate to the project if they so choose. --- README.markdown | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.markdown b/README.markdown index 7f92fbe5b5..985403c3ad 100644 --- a/README.markdown +++ b/README.markdown @@ -8,6 +8,12 @@ This code has been developed and maintained by Owlient from November 2009 to Mar You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)) or to p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)). +## Donating to the project +If you've found phpredis useful and would like to buy the maintainers a coffee (or a Tesla, we're not picky), feel free to do so. + +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5) +[![Donate with Bitcoin](https://en.cryptobadges.io/badge/micro/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)](https://en.cryptobadges.io/donate/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG) +[![Donate with Ethereum](https://en.cryptobadges.io/badge/micro/0x4345D9f767f877646587B24004652cb106f8F9ED)](https://en.cryptobadges.io/donate/0x4345D9f767f877646587B24004652cb106f8F9ED) # Table of contents ----- From a1bfc83b00c94b2c7fb9bceb161ae5df6a431ad2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 27 Jan 2019 12:04:33 -0800 Subject: [PATCH 1069/1986] Use the correct ETH address --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 985403c3ad..40611bcf2a 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,8 @@ If you've found phpredis useful and would like to buy the maintainers a coffee ( [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5) [![Donate with Bitcoin](https://en.cryptobadges.io/badge/micro/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)](https://en.cryptobadges.io/donate/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG) -[![Donate with Ethereum](https://en.cryptobadges.io/badge/micro/0x4345D9f767f877646587B24004652cb106f8F9ED)](https://en.cryptobadges.io/donate/0x4345D9f767f877646587B24004652cb106f8F9ED) +[![Donate with Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1) + # Table of contents ----- From 8838f534e9016cf8c900e79752c81c238d1d9a42 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 28 Jan 2019 09:29:13 -0800 Subject: [PATCH 1070/1986] Add a note that scan is a directed node command. --- README.markdown | 2 ++ cluster.markdown | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index 40611bcf2a..11d071970b 100644 --- a/README.markdown +++ b/README.markdown @@ -1043,6 +1043,8 @@ _**Description**_: Scan the keyspace for keys ##### *Return value* *Array, boolean*: This function will return an array of keys or FALSE if Redis returned zero keys +*Note*: SCAN is a "directed node" command in [RedisCluster](cluster.markdown#directed_node_commands). + ##### *Example* ~~~php diff --git a/cluster.markdown b/cluster.markdown index 3260bb7953..96cb13705b 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -130,15 +130,16 @@ This operation can also be done in MULTI mode transparently. ## Directed node commands There are a variety of commands which have to be directed at a specific node. In the case of these commands, the caller can either pass a key (which will be hashed and used to direct our command), or an array with host:port. -
+~~~php
 // This will be directed at the slot/node which would store "mykey"
 $obj_cluster->echo("mykey","Hello World!");
 
 // Here we're iterating all of our known masters, and delivering the command there
 foreach ($obj_cluster->_masters() as $arr_master) {
-    $obj_cluster->echo($arr_master, "Hello: " . implode(':', $arr_master));
+	$obj_cluster->echo($arr_master, "Hello: " . implode(':', $arr_master));
 }
-
+ +~~~ In the case of all commands which need to be directed at a node, the calling convention is identical to the Redis call, except that they require an additional (first) argument in order to deliver the command. Following is a list of each of these commands: @@ -157,6 +158,7 @@ In the case of all commands which need to be directed at a node, the calling con 13. SLOWLOG 14. RANDOMKEY 15. PING +16. SCAN ## Session Handler You can use the cluster functionality of phpredis to store PHP session information in a Redis cluster as you can with a non cluster-enabled Redis instance. From fe21524e6ba2a9a8e194f4e3996c79d408e6e35d Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Mon, 28 Jan 2019 09:33:44 -0800 Subject: [PATCH 1071/1986] Try relative link again (#1500) --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 11d071970b..dd48a42c46 100644 --- a/README.markdown +++ b/README.markdown @@ -1043,7 +1043,7 @@ _**Description**_: Scan the keyspace for keys ##### *Return value* *Array, boolean*: This function will return an array of keys or FALSE if Redis returned zero keys -*Note*: SCAN is a "directed node" command in [RedisCluster](cluster.markdown#directed_node_commands). +*Note*: SCAN is a "directed node" command in [RedisCluster](cluster.markdown#directed-node-commands) ##### *Example* ~~~php From c35499a1a78253f00275ef87ad13929bafee3733 Mon Sep 17 00:00:00 2001 From: Dusk Date: Mon, 28 Jan 2019 21:47:20 -0800 Subject: [PATCH 1072/1986] Remove outdated homebrew instructions (#1501) The homebrew-php tap no longer exists. Best option for homebrew users is PECL. --- INSTALL.markdown | 5 ----- 1 file changed, 5 deletions(-) diff --git a/INSTALL.markdown b/INSTALL.markdown index 967c988929..64ca1ad988 100644 --- a/INSTALL.markdown +++ b/INSTALL.markdown @@ -80,11 +80,6 @@ Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tag See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports). -You can install it using Homebrew: - -- [Get homebrew-php](https://github.com/Homebrew/homebrew-php) -- `brew install php55-redis` (or php53-redis, php54-redis) - You can install it using MacPorts: - [Get macports-php](https://www.macports.org/) From 9a1b7bd4bace1141b62195c0a54ba8d5aa7b96e8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 1 Feb 2019 11:28:29 +0200 Subject: [PATCH 1073/1986] Don't check lock status in PS_UPDATE_TIMESTAMP_FUNC --- redis_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index a02c1897d8..f5bb7d67de 100644 --- a/redis_session.c +++ b/redis_session.c @@ -704,7 +704,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; - if (!redis_sock || !write_allowed(redis_sock, &pool->lock_status TSRMLS_CC)) { + if (!redis_sock) { return FAILURE; } From 9f0d7bc0a4d3bbf5a539b855a5d1c32abf9f2300 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 8 Feb 2019 18:08:17 -0800 Subject: [PATCH 1074/1986] WIP: Reimplementation of cluster slot caching RedisCluster currently has a high construction overhead because every request has to issue a CLUSTER SLOTS command to map the keyspace. The issue is especially evident when a request only does a few commands. --- .gitignore | 1 + cluster_library.c | 177 ++++++++++++++++++++++++++++++++++++++++++++-- cluster_library.h | 59 ++++++++++++---- common.h | 1 + redis.c | 13 ++++ redis_cluster.c | 154 +++++++++++++++++++++++++++++++++------- 6 files changed, 362 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index f672fcee2a..965c536206 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ mkinstalldirs run-tests.php idea/* .cquery +tags diff --git a/cluster_library.c b/cluster_library.c index bcc5fb0782..8682929dda 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -651,6 +651,9 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, node->slave = slave; node->slaves = NULL; + /* Initialize our list of slot ranges */ + zend_llist_init(&node->slots, sizeof(redisSlotRange), NULL, 0); + // Attach socket node->sock = redis_sock_create(host, host_len, port, c->timeout, c->read_timeout, c->persistent, NULL, 0); @@ -690,10 +693,11 @@ cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) /* Use the output of CLUSTER SLOTS to map our nodes */ static int cluster_map_slots(redisCluster *c, clusterReply *r) { + redisClusterNode *pnode, *master, *slave; + redisSlotRange range; int i,j, hlen, klen; short low, high; clusterReply *r2, *r3; - redisClusterNode *pnode, *master, *slave; unsigned short port; char *host, key[1024]; @@ -746,6 +750,10 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { for (j = low; j<= high; j++) { c->master[j] = master; } + + /* Append to our list of slot ranges */ + range.low = low; range.high = high; + zend_llist_add_element(&master->slots, &range); } // Success @@ -758,7 +766,10 @@ PHP_REDIS_API void cluster_free_node(redisClusterNode *node) { zend_hash_destroy(node->slaves); efree(node->slaves); } + + zend_llist_destroy(&node->slots); redis_free_socket(node->sock); + efree(node); } @@ -802,6 +813,23 @@ static void ht_free_node(zval *data) { cluster_free_node(node); } +/* zend_llist of slot ranges -> persistent array */ +static redisSlotRange *slot_range_list_clone(zend_llist *src, size_t *count) { + redisSlotRange *dst, *range; + size_t i = 0; + + *count = zend_llist_count(src); + dst = pemalloc(*count * sizeof(*dst), 1); + + range = zend_llist_get_first(src); + while (range) { + memcpy(&dst[i++], range, sizeof(*range)); + range = zend_llist_get_next(src); + } + + return dst; +} + /* Construct a redisCluster object */ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, int failover, int persistent) @@ -860,10 +888,49 @@ cluster_free(redisCluster *c, int free_ctx TSRMLS_DC) /* Free any error we've got */ if (c->err) zend_string_release(c->err); + /* Invalidate our cache if we were redirected during operation */ + if (c->cache_key) { + if (c->redirections) { + zend_hash_del(&EG(persistent_list), c->cache_key); + } + zend_string_release(c->cache_key); + } + /* Free structure itself */ if (free_ctx) efree(c); } +/* Create a cluster slot cache structure */ +PHP_REDIS_API +redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes) { + redisCachedCluster *cc; + redisCachedMaster *cm; + redisClusterNode *node; + + cc = pecalloc(1, sizeof(*cc), 1); + cc->hash = zend_string_dup(hash, 1); + + /* Copy nodes */ + cc->master = pecalloc(zend_hash_num_elements(nodes), sizeof(*cc->master), 1); + ZEND_HASH_FOREACH_PTR(nodes, node) { + /* Skip slaves */ + if (node->slave) continue; + + cm = &cc->master[cc->count]; + + /* Duplicate host/port and clone slot ranges */ + cm->host.addr = zend_string_dup(node->sock->host, 1); + cm->host.port = node->sock->port; + + /* Copy over slot ranges */ + cm->slot = slot_range_list_clone(&node->slots, &cm->slots); + + cc->count++; + } ZEND_HASH_FOREACH_END(); + + return cc; +} + /* Takes our input hash table and returns a straigt C array with elements, * which have been randomized. The return value needs to be freed. */ static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) { @@ -892,6 +959,107 @@ static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) { return z_seeds; } +static void cluster_free_cached_master(redisCachedMaster *cm) { + size_t i; + + /* Free each slave entry */ + for (i = 0; i < cm->slaves; i++) { + zend_string_release(cm->slave[i].addr); + } + + /* Free other elements */ + zend_string_release(cm->host.addr); + pefree(cm->slave, 1); + pefree(cm->slot, 1); +} + +static redisClusterNode* +cached_master_clone(redisCluster *c, redisCachedMaster *cm) { + redisClusterNode *node; + size_t i; + + node = cluster_node_create(c, ZSTR_VAL(cm->host.addr), ZSTR_LEN(cm->host.addr), + cm->host.port, cm->slot[0].low, 0); + + /* Now copy in our slot ranges */ + for (i = 0; i < cm->slots; i++) { + zend_llist_add_element(&node->slots, &cm->slot[i]); + } + + return node; +} + +/* Destroy a persistent cached cluster */ +PHP_REDIS_API void cluster_cache_free(redisCachedCluster *rcc) { + size_t i; + + /* Free masters */ + for (i = 0; i < rcc->count; i++) { + cluster_free_cached_master(&rcc->master[i]); + } + + /* Free hash key */ + zend_string_release(rcc->hash); + pefree(rcc->master, 1); + pefree(rcc, 1); +} + +/* Initialize cluster from cached slots */ +PHP_REDIS_API +void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) { + RedisSock *sock; + redisClusterNode *mnode, *slave; + redisCachedMaster *cm; + char key[HOST_NAME_MAX]; + size_t keylen, i, j, s; + int *map; + + /* Randomize seeds */ + map = emalloc(sizeof(*map) * cc->count); + for (i = 0; i < cc->count; i++) map[i] = i; + fyshuffle(map, cc->count); + + /* Iterate over masters */ + for (i = 0; i < cc->count; i++) { + /* Grab the next master */ + cm = &cc->master[map[i]]; + + /* Hash our host and port */ + keylen = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(cm->host.addr), + cm->host.port); + + /* Create socket */ + sock = redis_sock_create(ZSTR_VAL(cm->host.addr), ZSTR_LEN(cm->host.addr), cm->host.port, + c->timeout, c->read_timeout, c->persistent, + NULL, 0); + + /* Add to seed nodes */ + zend_hash_str_update_ptr(c->seeds, key, keylen, sock); + + /* Create master node */ + mnode = cached_master_clone(c, cm); + + /* Add our master */ + zend_hash_str_update_ptr(c->nodes, key, keylen, mnode); + + /* Attach any slaves */ + for (s = 0; s < cm->slaves; s++) { + zend_string *host = cm->slave[s].addr; + slave = cluster_node_create(c, ZSTR_VAL(host), ZSTR_LEN(host), cm->slave[s].port, 0, 1); + cluster_node_add_slave(mnode, slave); + } + + /* Hook up direct slot access */ + for (j = 0; j < cm->slots; j++) { + for (s = cm->slot[j].low; s <= cm->slot[j].high; s++) { + c->master[s] = mnode; + } + } + } + + efree(map); +} + /* Initialize seeds */ PHP_REDIS_API int cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { @@ -908,6 +1076,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { if ((z_seed = z_seeds[i]) == NULL) continue; ZVAL_DEREF(z_seed); + /* Has to be a string */ if (Z_TYPE_P(z_seed) != IS_STRING) continue; @@ -940,7 +1109,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { efree(z_seeds); // Success if at least one seed seems valid - return zend_hash_num_elements(cluster->seeds) > 0 ? 0 : -1; + return zend_hash_num_elements(cluster->seeds) > 0 ? SUCCESS : FAILURE; } /* Initial mapping of our cluster keyspace */ @@ -977,10 +1146,10 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { zend_throw_exception(redis_cluster_exception_ce, "Couldn't map cluster keyspace using any provided seed", 0 TSRMLS_CC); - return -1; + return FAILURE; } - return 0; + return SUCCESS; } /* Parse the MOVED OR ASK redirection payload when we get such a response diff --git a/cluster_library.h b/cluster_library.h index 62f7f9dabf..4522330864 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -143,22 +143,43 @@ typedef enum CLUSTER_REDIR_TYPE { /* MULTI BULK response callback typedef */ typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void* TSRMLS_DC); -/* Specific destructor to free a cluster object */ -// void redis_destructor_redis_cluster(zend_resource *rsrc TSRMLS_DC); +/* A list of covered slot ranges */ +typedef struct redisSlotRange { + unsigned short low; + unsigned short high; +} redisSlotRange; + +/* Simple host/port information for our cache */ +typedef struct redisCachedHost { + zend_string *addr; + unsigned short port; +} redisCachedHost; + +/* Storage for a cached master node */ +typedef struct redisCachedMaster { + redisCachedHost host; + + redisSlotRange *slot; /* Slots and count */ + size_t slots; + + redisCachedHost *slave; /* Slaves and their count */ + size_t slaves; +} redisCachedMaster; + +typedef struct redisCachedCluster { + // int rsrc_id; /* Zend resource ID */ + zend_string *hash; /* What we're cached by */ + redisCachedMaster *master; /* Array of masters */ + size_t count; /* Number of masters */ +} redisCachedCluster; /* A Redis Cluster master node */ typedef struct redisClusterNode { - /* Our Redis socket in question */ - RedisSock *sock; - - /* A slot where one of these lives */ - short slot; - - /* Is this a slave node */ - unsigned short slave; - - /* A HashTable containing any slaves */ - HashTable *slaves; + RedisSock *sock; /* Our Redis socket in question */ + short slot; /* One slot we believe this node serves */ + zend_llist slots; /* List of all slots we believe this node serves */ + unsigned short slave; /* Are we a slave */ + HashTable *slaves; /* Hash table of slaves */ } redisClusterNode; /* Forward declarations */ @@ -208,6 +229,11 @@ typedef struct redisCluster { /* Flag for when we get a CLUSTERDOWN error */ short clusterdown; + /* Key to our persistent list cache and number of redirections we've + * received since construction */ + zend_string *cache_key; + uint64_t redirections; + /* The last ERROR we encountered */ zend_string *err; @@ -362,6 +388,13 @@ PHP_REDIS_API int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC); PHP_REDIS_API void cluster_free_node(redisClusterNode *node); +/* Functions for interacting with cached slots maps */ +PHP_REDIS_API redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes); +PHP_REDIS_API void cluster_cache_free(redisCachedCluster *rcc); +PHP_REDIS_API void cluster_init_cache(redisCluster *c, redisCachedCluster *rcc); + +/* Functions to facilitate cluster slot caching */ + PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len TSRMLS_DC); diff --git a/common.h b/common.h index 34f8441698..fbe44460eb 100644 --- a/common.h +++ b/common.h @@ -6,6 +6,7 @@ #define PHPREDIS_NOTUSED(v) ((void)v) +#include "zend_llist.h" #include #include #include diff --git a/redis.c b/redis.c index ec743733bc..46d6d444bf 100644 --- a/redis.c +++ b/redis.c @@ -50,6 +50,8 @@ extern zend_class_entry *redis_cluster_exception_ce; zend_class_entry *redis_ce; zend_class_entry *redis_exception_ce; +extern int le_cluster_slot_cache; + extern zend_function_entry redis_array_functions[]; extern zend_function_entry redis_cluster_functions[]; @@ -71,6 +73,7 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.arrays.consistent", "0", PHP_INI_ALL, NULL) /* redis cluster */ + PHP_INI_ENTRY("redis.clusters.cache_slots", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.auth", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.read_timeout", "0", PHP_INI_ALL, NULL) @@ -549,6 +552,12 @@ free_reply_callbacks(RedisSock *redis_sock) redis_sock->current = NULL; } +/* Passthru for destroying cluster cache */ +static void cluster_cache_dtor(zend_resource *rsrc) { + redisCachedCluster *rcc = (redisCachedCluster*)rsrc->ptr; + cluster_cache_free(rcc); +} + void free_redis_object(zend_object *object) { @@ -731,6 +740,10 @@ PHP_MINIT_FUNCTION(redis) redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry TSRMLS_CC); redis_cluster_ce->create_object = create_cluster_context; + /* Register our cluster cache list item */ + le_cluster_slot_cache = zend_register_list_destructors_ex(NULL, cluster_cache_dtor, + "Redis cluster slot cache", + module_number); /* Base Exception class */ #if HAVE_SPL diff --git a/redis_cluster.c b/redis_cluster.c index fd42db36b8..f7695f976a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -33,6 +33,7 @@ #include zend_class_entry *redis_cluster_ce; +int le_cluster_slot_cache; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; @@ -40,6 +41,10 @@ zend_class_entry *redis_cluster_exception_ce; /* Handlers for RedisCluster */ zend_object_handlers RedisCluster_handlers; +/* Helper when throwing normal cluster exceptions */ +#define CLUSTER_THROW_EXCEPTION(msg) \ + zend_throw_exception(redis_cluster_exception_ce, msg, 0 TSRMLS_CC); + ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1) ZEND_ARG_INFO(0, name) ZEND_ARG_ARRAY_INFO(0, seeds, 0) @@ -344,50 +349,146 @@ void free_cluster_context(zend_object *object) { zend_object_std_dtor(&cluster->std TSRMLS_CC); } -/* Attempt to connect to a Redis cluster provided seeds and timeout options */ -static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, - double read_timeout, int persistent, char *auth, - size_t auth_len TSRMLS_DC) -{ - // Validate timeout - if (timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, - "Invalid timeout", 0 TSRMLS_CC); +/* Turn a seed array into a zend_string we can use to look up a slot cache */ +static zend_string *cluster_hash_seeds(HashTable *ht) { + smart_str hash = {0}; + zend_string *zstr; + zval *z_seed; + + ZEND_HASH_FOREACH_VAL(ht, z_seed) { + zstr = zval_get_string(z_seed); + smart_str_appendc(&hash, '['); + smart_str_appendl(&hash, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + smart_str_appendc(&hash, ']'); + zend_string_release(zstr); + } ZEND_HASH_FOREACH_END(); + + /* Not strictly needed but null terminate anyway */ + smart_str_0(&hash); + + /* smart_str is a zend_string internally */ + return hash.s; +} + +#define CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1) +static redisCachedCluster *cluster_cache_load(HashTable *ht_seeds TSRMLS_DC) { + zend_resource *le; + zend_string *h; + + /* Short circuit if we're not caching slots or if our seeds don't have any + * elements, since it doesn't make sense to cache an empty string */ + if (!CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) + return NULL; + + /* Look for cached slot information */ + h = cluster_hash_seeds(ht_seeds); + le = zend_hash_str_find_ptr(&EG(persistent_list), ZSTR_VAL(h), ZSTR_LEN(h)); + zend_string_release(h); + + if (le != NULL) { + /* Sanity check on our list type */ + if (le->type != le_cluster_slot_cache) { + php_error_docref(0 TSRMLS_CC, E_WARNING, "Invalid slot cache resource"); + return NULL; + } + + /* Success, return the cached entry */ + return le->ptr; } - // Validate our read timeout + /* Not found */ + return NULL; +} + +/* Cache a cluster's slot information in persistent_list if it's enabled */ +static int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes TSRMLS_DC) { + redisCachedCluster *cc; + zend_string *hash; + + /* Short circuit if caching is disabled or there aren't any seeds */ + if (!CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) + return !CACHING_ENABLED() ? SUCCESS : FAILURE; + + /* Construct our cache */ + hash = cluster_hash_seeds(ht_seeds); + cc = cluster_cache_create(hash, nodes); + zend_string_release(hash); + + /* Set up our resource */ +#if PHP_VERSION_ID < 70300 + zend_resource le; + le.type = le_cluster_slot_cache; + le.ptr = cc; + + zend_hash_update_mem(&EG(persistent_list), cc->hash, (void*)&le, sizeof(zend_resource)); +#else + zend_register_persistent_resource_ex(cc->hash, cc, le_cluster_slot_cache); +#endif + + return SUCCESS; +} + +/* Validate redis cluster construction arguments */ +static int +cluster_validate_args(double timeout, double read_timeout, HashTable *seeds) { + if (timeout < 0L || timeout > INT_MAX) { + CLUSTER_THROW_EXCEPTION("Invalid timeout"); + return FAILURE; + } if (read_timeout < 0L || read_timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, - "Invalid read timeout", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Invalid read timeout"); + return FAILURE; + } + if (zend_hash_num_elements(seeds) == 0) { + CLUSTER_THROW_EXCEPTION("Must pass seeds"); + return FAILURE; } - /* Make sure there are some seeds */ - if (zend_hash_num_elements(ht_seeds) == 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Must pass seeds", 0 TSRMLS_CC); + return SUCCESS; +} + +static int cluster_init_from_seeds(redisCluster *c, HashTable *seeds TSRMLS_DC) { + int rv1 = cluster_init_seeds(c, seeds); + int rv2 = cluster_map_keyspace(c TSRMLS_CC); + if (rv1 == SUCCESS && rv2 == SUCCESS) { + return SUCCESS; + } else { + return FAILURE; } +} + +//static int cluster_init_from_cache(redisCluster *c, redisCachedCluster *rcc TSRMLS_DC) { +// cluster_init_cache(c, rcc); +// return cluster_map_keyspace(c TSRMLS_CC); +//} + +/* Attempt to connect to a Redis cluster provided seeds and timeout options */ +static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, + double read_timeout, int persistent, char *auth, + size_t auth_len TSRMLS_DC) +{ + redisCachedCluster *cc; + + cluster_validate_args(timeout, read_timeout, ht_seeds); if (auth && auth_len > 0) { c->auth = zend_string_init(auth, auth_len, 0); } - /* Set our timeout and read_timeout which we'll pass through to the - * socket type operations */ c->timeout = timeout; c->read_timeout = read_timeout; - - /* Set our option to use or not use persistent connections */ c->persistent = persistent; /* Calculate the number of miliseconds we will wait when bouncing around, * (e.g. a node goes down), which is not the same as a standard timeout. */ c->waitms = (long)(timeout * 1000); - // Initialize our RedisSock "seed" objects - cluster_init_seeds(c, ht_seeds); - - // Create and map our key space - cluster_map_keyspace(c TSRMLS_CC); + /* Attempt to load from cache */ + if ((cc = cluster_cache_load(ht_seeds TSRMLS_CC))) { + cluster_init_cache(c, cc); + } else if (cluster_init_from_seeds(c, ht_seeds) == SUCCESS) { + cluster_cache_store(ht_seeds, c->nodes TSRMLS_CC); + } } /* Attempt to load a named cluster configured in php.ini */ @@ -913,7 +1014,7 @@ static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { zval *z_ret = emalloc(sizeof(*z_ret)); - + // Initialize a LONG value to zero for our return ZVAL_LONG(z_ret, 0); @@ -3077,3 +3178,4 @@ PHP_METHOD(RedisCluster, command) { } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ + From fa130a4bd46200d799f0659b709c8062c87f692b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 8 Feb 2019 21:31:47 -0800 Subject: [PATCH 1075/1986] PHP 7 exception and compiler warning fixes PHP 7 removed TSRMLS_CC from zend_throw_exception* routines. Additionally this commit creates two simple wrapper macros for throwing Redis or RedisCluster exceptions so we don't duplicate as much code. Additionally there were a couple of minor compiler warnings printf type correctness fixed in this commit. --- cluster_library.c | 33 +++++++------------ library.c | 25 ++++++-------- library.h | 6 ++++ redis.c | 13 +++----- redis_array.c | 2 +- redis_array_impl.c | 2 +- redis_cluster.c | 81 +++++++++++++++------------------------------- redis_commands.c | 2 +- 8 files changed, 61 insertions(+), 103 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index bcc5fb0782..43e0c1cfd0 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -974,9 +974,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { // Throw an exception if we couldn't map if (!mapped) { - zend_throw_exception(redis_cluster_exception_ce, - "Couldn't map cluster keyspace using any provided seed", 0 - TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Couldn't map cluster keyspace using any provided seed", 0); return -1; } @@ -1355,9 +1353,7 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, * send it to this node yet */ if (c->flags->mode == MULTI && c->cmd_sock->mode != MULTI) { if (cluster_send_multi(c, slot TSRMLS_CC) == -1) { - zend_throw_exception(redis_cluster_exception_ce, - "Unable to enter MULTI mode on requested slot", - 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Unable to enter MULTI mode on requested slot", 0); return -1; } } @@ -1384,7 +1380,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char long msstart; if (!SLOT(c, slot)) { - zend_throw_exception_ex(redis_cluster_exception_ce, 0 TSRMLS_CC, + zend_throw_exception_ex(redis_cluster_exception_ce, 0, "The slot %d is not covered by any node in this cluster", slot); return -1; } @@ -1406,9 +1402,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char if (c->flags->mode == MULTI && CMD_SOCK(c)->mode != MULTI) { /* We have to fail if we can't send MULTI to the node */ if (cluster_send_multi(c, slot TSRMLS_CC) == -1) { - zend_throw_exception(redis_cluster_exception_ce, - "Unable to enter MULTI mode on requested slot", - 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Unable to enter MULTI mode on requested slot", 0); return -1; } } @@ -1417,9 +1411,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char * node until we find one that is available. */ if (cluster_sock_write(c, cmd, cmd_len, 0 TSRMLS_CC) == -1) { /* We have to abort, as no nodes are reachable */ - zend_throw_exception(redis_cluster_exception_ce, - "Can't communicate with any node in the cluster", - 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Can't communicate with any node in the cluster", 0); return -1; } @@ -1433,9 +1425,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char if (resp == 1) { /* Abort if we're in a transaction as it will be invalid */ if (c->flags->mode == MULTI) { - zend_throw_exception(redis_cluster_exception_ce, - "Can't process MULTI sequence when cluster is resharding", - 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Can't process MULTI sequence when cluster is resharding", 0); return -1; } @@ -1452,19 +1442,18 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char // If we've detected the cluster is down, throw an exception if (c->clusterdown) { - zend_throw_exception(redis_cluster_exception_ce, - "The Redis Cluster is down (CLUSTERDOWN)", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("The Redis Cluster is down (CLUSTERDOWN)", 0); return -1; } else if (timedout || resp == -1) { // Make sure the socket is reconnected, it such that it is in a clean state redis_sock_disconnect(c->cmd_sock, 1 TSRMLS_CC); if (timedout) { - zend_throw_exception(redis_cluster_exception_ce, - "Timed out attempting to find data in the correct node!", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION( + "Timed out attempting to find data in the correct node!", 0); } else { - zend_throw_exception(redis_cluster_exception_ce, - "Error processing response from Redis node!", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION( + "Error processing response from Redis node!", 0); } return -1; diff --git a/library.c b/library.c index 26358e114a..a58b7ee4d6 100644 --- a/library.c +++ b/library.c @@ -132,7 +132,7 @@ redis_error_throw(RedisSock *redis_sock TSRMLS_DC) !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "BUSYGROUP") && !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGROUP")) { - zend_throw_exception(redis_exception_ce, ZSTR_VAL(redis_sock->err), 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION( ZSTR_VAL(redis_sock->err), 0); } } @@ -144,7 +144,7 @@ redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) if (!redis_sock || !redis_sock->stream || redis_sock->status == REDIS_SOCK_STATUS_FAILED) { if (!no_throw) { - zend_throw_exception(redis_exception_ce, "Connection closed", 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION( "Connection closed", 0); } return -1; } @@ -208,7 +208,7 @@ redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); redis_sock->status = REDIS_SOCK_STATUS_FAILED; if (!no_throw) { - zend_throw_exception(redis_exception_ce, errmsg, 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION( errmsg, 0); } return -1; } @@ -461,8 +461,7 @@ redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) /* Protect against reading too few bytes */ if (offset < nbytes) { /* Error or EOF */ - zend_throw_exception(redis_exception_ce, - "socket error on read socket", 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION("socket error on read socket", 0); efree(reply); return NULL; } @@ -515,9 +514,7 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) return estrndup(inbuf, *buf_len); } default: - zend_throw_exception_ex( - redis_exception_ce, - 0 TSRMLS_CC, + zend_throw_exception_ex(redis_exception_ce, 0, "protocol error, got '%c' as reply type byte\n", inbuf[0] ); @@ -674,7 +671,7 @@ int redis_cmd_append_sstr_long(smart_string *str, long append) { */ int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) { char nbuf[64]; - int len = snprintf(nbuf, sizeof(nbuf), "%lld", append); + int len = snprintf(nbuf, sizeof(nbuf), PRId64, append); return redis_cmd_append_sstr(str, nbuf, len); } @@ -2257,8 +2254,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); // Throw a read error exception - zend_throw_exception(redis_exception_ce, "read error on connection", - 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION( "read error on connection", 0); return -1; } @@ -2283,8 +2279,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, // Attempt to read the reply-type byte if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { - zend_throw_exception(redis_exception_ce, "socket error on read socket", - 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION( "socket error on read socket", 0); return -1; } @@ -2370,7 +2365,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC) < 0) { - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, + zend_throw_exception_ex(redis_exception_ce, 0, "protocol error, couldn't parse MULTI-BULK response\n"); return FAILURE; } @@ -2449,7 +2444,7 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } break; default: - zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, + zend_throw_exception_ex(redis_exception_ce, 0, "protocol error, got '%c' as reply-type byte\n", reply_type); return FAILURE; } diff --git a/library.h b/library.h index a7a2f060e1..3028711c1e 100644 --- a/library.h +++ b/library.h @@ -14,6 +14,12 @@ #define REDIS_CMD_INIT_SSTR_STATIC(sstr, argc, keyword) \ redis_cmd_init_sstr(sstr, argc, keyword, sizeof(keyword)-1); +#define REDIS_THROW_EXCEPTION(msg, code) \ + zend_throw_exception(redis_exception_ce, (msg), code) + +#define CLUSTER_THROW_EXCEPTION(msg, code) \ + zend_throw_exception(redis_cluster_exception_ce, (msg), code) + int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); int redis_cmd_append_sstr_int(smart_string *str, int append); diff --git a/redis.c b/redis.c index ec743733bc..e99527fa7b 100644 --- a/redis.c +++ b/redis.c @@ -592,7 +592,7 @@ redis_sock_get_instance(zval *id TSRMLS_DC, int no_throw) } // Throw an exception unless we've been requested not to if (!no_throw) { - zend_throw_exception(redis_exception_ce, "Redis server went away", 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION("Redis server went away", 0); } return NULL; } @@ -883,20 +883,17 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_exception_ce, - "Invalid connect timeout", 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION("Invalid connect timeout", 0); return FAILURE; } if (read_timeout < 0L || read_timeout > INT_MAX) { - zend_throw_exception(redis_exception_ce, - "Invalid read timeout", 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION("Invalid read timeout", 0); return FAILURE; } if (retry_interval < 0L || retry_interval > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid retry interval", - 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION("Invalid retry interval", 0); return FAILURE; } @@ -917,7 +914,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { if (redis->sock->err) { - zend_throw_exception(redis_exception_ce, ZSTR_VAL(redis->sock->err), 0 TSRMLS_CC); + REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0); } redis_free_socket(redis->sock); redis->sock = NULL; diff --git a/redis_array.c b/redis_array.c index 1d989cb981..37b31484d1 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1125,7 +1125,7 @@ PHP_METHOD(RedisArray, mset) /* Generic handler for DEL or UNLINK which behave identically to phpredis */ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { - zval *object, z_keys, z_fun, *data, z_ret, *z_tmp, *z_args; + zval *object, z_keys, z_fun, *data, z_ret, *z_args; int i, n; RedisArray *ra; int *pos, argc = ZEND_NUM_ARGS(), *argc_each; diff --git a/redis_array_impl.c b/redis_array_impl.c index aeb08e206b..918359c5e5 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -387,7 +387,7 @@ ra_make_continuum(zend_string **hosts, int nb_hosts) for (i = 0; i < nb_hosts; ++i) { for (j = 0; j < 40; ++j) { - len = snprintf(host, sizeof(host), "%.*s-%u", ZSTR_LEN(hosts[i]), ZSTR_VAL(hosts[i]), j); + len = snprintf(host, sizeof(host), "%.*s-%u", (int)ZSTR_LEN(hosts[i]), ZSTR_VAL(hosts[i]), j); PHP_MD5Init(&ctx); PHP_MD5Update(&ctx, host, len); PHP_MD5Final(digest, &ctx); diff --git a/redis_cluster.c b/redis_cluster.c index fd42db36b8..63e4bdcfd2 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -351,20 +351,17 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time { // Validate timeout if (timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, - "Invalid timeout", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Invalid timeout", 0); } // Validate our read timeout if (read_timeout < 0L || read_timeout > INT_MAX) { - zend_throw_exception(redis_cluster_exception_ce, - "Invalid read timeout", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Invalid read timeout", 0); } /* Make sure there are some seeds */ if (zend_hash_num_elements(ht_seeds) == 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Must pass seeds", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Must pass seeds", 0); } if (auth && auth_len > 0) { @@ -408,7 +405,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { ht_seeds = Z_ARRVAL_P(z_value); } else { zval_dtor(&z_seeds); - zend_throw_exception(redis_cluster_exception_ce, "Couldn't find seeds for cluster", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Couldn't find seeds for cluster", 0); return; } @@ -502,9 +499,7 @@ PHP_METHOD(RedisCluster, __construct) { // Require a name if (name_len == 0 && ZEND_NUM_ARGS() < 2) { - zend_throw_exception(redis_cluster_exception_ce, - "You must specify a name or pass seeds!", - 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0); } /* If we've been passed only one argument, the user is attempting to connect @@ -612,8 +607,7 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, kv->key = kv->kbuf; break; default: - zend_throw_exception(redis_cluster_exception_ce, - "Internal Zend HashTable error", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0); return -1; } @@ -623,8 +617,7 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, // Now grab our value if ((z_val = zend_hash_get_current_data_ex(ht, ptr)) == NULL) { - zend_throw_exception(redis_cluster_exception_ce, - "Internal Zend HashTable error", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0); return -1; } @@ -643,8 +636,7 @@ static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, if ((z_key = zend_hash_get_current_data_ex(ht, ptr)) == NULL) { // Shouldn't happen, but check anyway - zend_throw_exception(redis_cluster_exception_ce, - "Internal Zend HashTable error", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0); return -1; } @@ -1849,8 +1841,7 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, if (cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK TSRMLS_CC) == FAILURE) { - zend_throw_exception(redis_cluster_exception_ce, - "Failed to UNSUBSCRIBE within our subscribe loop!", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Failed to UNSUBSCRIBE within our subscribe loop!", 0); RETURN_FALSE; } @@ -2049,9 +2040,7 @@ PHP_METHOD(RedisCluster, watch) { // Add this key to our distribution handler if (cluster_dist_add_key(c, ht_dist, ZSTR_VAL(zstr), ZSTR_LEN(zstr), NULL) == FAILURE) { - zend_throw_exception(redis_cluster_exception_ce, - "Can't issue WATCH command as the keyspace isn't fully mapped", - 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Can't issue WATCH command as the keyspace isn't fully mapped", 0); zend_string_release(zstr); RETURN_FALSE; } @@ -2062,8 +2051,7 @@ PHP_METHOD(RedisCluster, watch) { ZEND_HASH_FOREACH_PTR(ht_dist, dl) { // Grab the clusterDistList pointer itself if (dl == NULL) { - zend_throw_exception(redis_cluster_exception_ce, - "Internal error in a PHP HashTable", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Internal error in a PHP HashTable", 0); cluster_dist_free(ht_dist); efree(z_args); efree(cmd.c); @@ -2139,10 +2127,7 @@ PHP_METHOD(RedisCluster, exec) { if (SLOT_SOCK(c, fi->slot)->mode == MULTI) { if ( cluster_send_exec(c, fi->slot TSRMLS_CC) < 0) { cluster_abort_exec(c TSRMLS_CC); - - zend_throw_exception(redis_cluster_exception_ce, - "Error processing EXEC across the cluster", - 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Error processing EXEC across the cluster", 0); // Free our queue, reset MULTI state CLUSTER_FREE_QUEUE(c); @@ -2260,8 +2245,7 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, // Kick off our command if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Unable to send command at a specific node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0); efree(cmd); RETURN_FALSE; } @@ -2304,8 +2288,7 @@ cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply // Kick off our command if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Unable to send command at a specific node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0); efree(cmd); RETURN_FALSE; } @@ -2369,8 +2352,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) /* Send it off */ if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Couldn't send command to node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0); efree(cmd.c); efree(z_args); RETURN_FALSE; @@ -2399,8 +2381,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, // Can't be in MULTI mode if (!CLUSTER_IS_ATOMIC(c)) { - zend_throw_exception(redis_cluster_exception_ce, - "SCAN type commands can't be called in MULTI mode!", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("SCAN type commands can't be called in MULTI mode!", 0); RETURN_FALSE; } @@ -2445,8 +2426,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, // Send it off if (cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC) == FAILURE) { - zend_throw_exception(redis_cluster_exception_ce, - "Couldn't send SCAN command", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Couldn't send SCAN command", 0); if (key_free) efree(key); efree(cmd); RETURN_FALSE; @@ -2456,8 +2436,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, &it) == FAILURE) { - zend_throw_exception(redis_cluster_exception_ce, - "Couldn't read SCAN response", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Couldn't read SCAN response", 0); if (key_free) efree(key); efree(cmd); RETURN_FALSE; @@ -2494,8 +2473,7 @@ PHP_METHOD(RedisCluster, scan) { /* Can't be in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { - zend_throw_exception(redis_cluster_exception_ce, - "SCAN type commands can't be called in MULTI mode", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("SCAN type commands can't be called in MULTI mode", 0); RETURN_FALSE; } @@ -2536,8 +2514,7 @@ PHP_METHOD(RedisCluster, scan) { // Send it to the node in question if (cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Couldn't send SCAN to node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Couldn't send SCAN to node", 0); efree(cmd); RETURN_FALSE; } @@ -2545,8 +2522,7 @@ PHP_METHOD(RedisCluster, scan) { if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, TYPE_SCAN, &it) == FAILURE || Z_TYPE_P(return_value)!=IS_ARRAY) { - zend_throw_exception(redis_cluster_exception_ce, - "Couldn't process SCAN response from node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Couldn't process SCAN response from node", 0); efree(cmd); RETURN_FALSE; } @@ -2669,8 +2645,7 @@ PHP_METHOD(RedisCluster, info) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Unable to send INFO command to specific node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Unable to send INFO command to specific node", 0); efree(cmd); RETURN_FALSE; } @@ -2743,8 +2718,7 @@ PHP_METHOD(RedisCluster, client) { /* Attempt to write our command */ if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Unable to send CLIENT command to specific node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Unable to send CLIENT command to specific node", 0); efree(cmd); RETURN_FALSE; } @@ -2822,8 +2796,7 @@ PHP_METHOD(RedisCluster, script) { /* Send it off */ if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Couldn't send command to node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0); efree(cmd.c); efree(z_args); RETURN_FALSE; @@ -2997,8 +2970,7 @@ PHP_METHOD(RedisCluster, echo) { /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Unable to send commnad at the specificed node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Unable to send commnad at the specificed node", 0); efree(cmd); RETURN_FALSE; } @@ -3051,8 +3023,7 @@ PHP_METHOD(RedisCluster, rawcommand) { /* Direct the command */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE; if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC) < 0) { - zend_throw_exception(redis_cluster_exception_ce, - "Unable to send command to the specified node", 0 TSRMLS_CC); + CLUSTER_THROW_EXCEPTION("Unable to send command to the specified node", 0); efree(cmd); RETURN_FALSE; } diff --git a/redis_commands.c b/redis_commands.c index 078d12c01e..263e3516f1 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -4027,7 +4027,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, zval zv, *z_ret = &zv; if (!redis_unserialize(redis_sock, value, value_len, z_ret TSRMLS_CC)) { // Badly formed input, throw an execption - zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0 TSRMLS_CC); + zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0); RETURN_FALSE; } RETURN_ZVAL(z_ret, 1, 0); From 9440f05e19a1ab7c06563e59dc0c54db94d6d9c8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 9 Feb 2019 14:10:52 -0800 Subject: [PATCH 1076/1986] Switch everything to new array syntax --- README.markdown | 126 +++--- tests/RedisArrayTest.php | 6 +- tests/RedisClusterTest.php | 72 ++-- tests/RedisTest.php | 834 ++++++++++++++++++------------------- tests/TestRedis.php | 6 +- tests/TestSuite.php | 4 +- 6 files changed, 522 insertions(+), 526 deletions(-) diff --git a/README.markdown b/README.markdown index 7f92fbe5b5..1240a86ef5 100644 --- a/README.markdown +++ b/README.markdown @@ -709,10 +709,10 @@ $redis->set('key', 'value'); $redis->set('key','value', 10); // Will set the key, if it doesn't exist, with a ttl of 10 seconds -$redis->set('key', 'value', Array('nx', 'ex'=>10)); +$redis->set('key', 'value', ['nx', 'ex'=>10]); // Will set a key, if it does exist, with a ttl of 1000 miliseconds -$redis->set('key', 'value', Array('xx', 'px'=>1000)); +$redis->set('key', 'value', ['xx', 'px'=>1000]); ~~~ @@ -772,11 +772,11 @@ $redis->set('key3', 'val3'); $redis->set('key4', 'val4'); $redis->delete('key1', 'key2'); /* return 2 */ -$redis->delete(array('key3', 'key4')); /* return 2 */ +$redis->delete(['key3', 'key4']); /* return 2 */ /* If using Redis >= 4.0.0 you can call unlink */ $redis->unlink('key1', 'key2'); -$redis->unlink(Array('key1', 'key2')); +$redis->unlink(['key1', 'key2']); ~~~ @@ -890,8 +890,8 @@ _**Description**_: Get the values of all the specified keys. If one or more keys $redis->set('key1', 'value1'); $redis->set('key2', 'value2'); $redis->set('key3', 'value3'); -$redis->mGet(array('key1', 'key2', 'key3')); /* array('value1', 'value2', 'value3'); -$redis->mGet(array('key0', 'key1', 'key5')); /* array(`FALSE`, 'value1', `FALSE`); +$redis->mGet(['key1', 'key2', 'key3']); /* ['value1', 'value2', 'value3']; +$redis->mGet(['key0', 'key1', 'key5']); /* [`FALSE`, 'value1', `FALSE`]; ~~~ ### getSet @@ -1252,10 +1252,10 @@ _**Description**_: Sort the elements in a list, set or sorted set. ##### *Parameters* *Key*: key -*Options*: array(key => value, ...) - optional, with the following keys and values: +*Options*: [key => value, ...] - optional, with the following keys and values: ~~~ 'by' => 'some_pattern_*', - 'limit' => array(0, 1), + 'limit' => [0, 1], 'get' => 'some_other_pattern_*' or an array of patterns, 'sort' => 'asc' or 'desc', 'alpha' => TRUE, @@ -1274,8 +1274,8 @@ $redis->sAdd('s', 1); $redis->sAdd('s', 3); var_dump($redis->sort('s')); // 1,2,3,4,5 -var_dump($redis->sort('s', array('sort' => 'desc'))); // 5,4,3,2,1 -var_dump($redis->sort('s', array('sort' => 'desc', 'store' => 'out'))); // (int)5 +var_dump($redis->sort('s', ['sort' => 'desc'])); // 5,4,3,2,1 +var_dump($redis->sort('s', ['sort' => 'desc', 'store' => 'out'])); // (int)5 ~~~ @@ -1316,7 +1316,7 @@ $redis->persist('key'); _**Description**_: Sets multiple key-value pairs in one atomic command. MSETNX only returns TRUE if all the keys were set (see SETNX). ##### *Parameters* -*Pairs*: array(key => value, ...) +*Pairs*: [key => value, ...] ##### *Return value* *Bool* `TRUE` in case of success, `FALSE` in case of failure. @@ -1324,7 +1324,7 @@ _**Description**_: Sets multiple key-value pairs in one atomic command. MSETNX o ##### *Example* ~~~php -$redis->mSet(array('key0' => 'value0', 'key1' => 'value1')); +$redis->mSet(['key0' => 'value0', 'key1' => 'value1']); var_dump($redis->get('key0')); var_dump($redis->get('key1')); @@ -1650,7 +1650,7 @@ _**Description**_: Fills in a whole hash. Non-string values are converted to str ##### *Examples* ~~~php $redis->delete('user:1'); -$redis->hMSet('user:1', array('name' => 'Joe', 'salary' => 2000)); +$redis->hMSet('user:1', ['name' => 'Joe', 'salary' => 2000]); $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. ~~~ @@ -1667,7 +1667,7 @@ _**Description**_: Retrieve the values associated to the specified fields in the $redis->delete('h'); $redis->hSet('h', 'field1', 'value1'); $redis->hSet('h', 'field2', 'value2'); -$redis->hMGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */ +$redis->hMGet('h', ['field1', 'field2']); /* returns ['field1' => 'value1', 'field2' => 'value2'] */ ~~~ ### hScan @@ -1738,7 +1738,7 @@ Or *INTEGER* Timeout ##### *Return value* -*ARRAY* array('listName', 'element') +*ARRAY* ['listName', 'element'] ##### *Example* ~~~php @@ -1746,13 +1746,13 @@ Or $redis->lPush('key1', 'A'); $redis->delete('key2'); -$redis->blPop('key1', 'key2', 10); /* array('key1', 'A') */ +$redis->blPop('key1', 'key2', 10); /* ['key1', 'A'] */ /* OR */ -$redis->blPop(array('key1', 'key2'), 10); /* array('key1', 'A') */ +$redis->blPop(['key1', 'key2'], 10); /* ['key1', 'A'] */ -$redis->brPop('key1', 'key2', 10); /* array('key1', 'A') */ +$redis->brPop('key1', 'key2', 10); /* ['key1', 'A'] */ /* OR */ -$redis->brPop(array('key1', 'key2'), 10); /* array('key1', 'A') */ +$redis->brPop(['key1', 'key2'], 10); /* ['key1', 'A'] */ /* Blocking feature */ @@ -1765,7 +1765,7 @@ $redis->blPop('key1', 10); $redis->lPush('key1', 'A'); /* process 1 */ -/* array('key1', 'A') is returned*/ +/* ['key1', 'A'] is returned*/ ~~~ ### bRPopLPush @@ -1833,10 +1833,10 @@ $redis->lPush('key1', 'B'); $redis->lPush('key1', 'C'); $redis->lInsert('key1', Redis::BEFORE, 'C', 'X'); /* 4 */ -$redis->lRange('key1', 0, -1); /* array('A', 'B', 'X', 'C') */ +$redis->lRange('key1', 0, -1); /* ['A', 'B', 'X', 'C'] */ $redis->lInsert('key1', Redis::AFTER, 'C', 'Y'); /* 5 */ -$redis->lRange('key1', 0, -1); /* array('A', 'B', 'X', 'C', 'Y') */ +$redis->lRange('key1', 0, -1); /* ['A', 'B', 'X', 'C', 'Y'] */ $redis->lInsert('key1', Redis::AFTER, 'W', 'value'); /* -1 */ ~~~ @@ -1920,7 +1920,7 @@ _**Description**_: Returns the specified elements of the list stored at the spec $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); -$redis->lRange('key1', 0, -1); /* array('A', 'B', 'C') */ +$redis->lRange('key1', 0, -1); /* ['A', 'B', 'C'] */ ~~~ ### lRem, lRemove @@ -1946,9 +1946,9 @@ $redis->lPush('key1', 'C'); $redis->lPush('key1', 'A'); $redis->lPush('key1', 'A'); -$redis->lRange('key1', 0, -1); /* array('A', 'A', 'C', 'B', 'A') */ +$redis->lRange('key1', 0, -1); /* ['A', 'A', 'C', 'B', 'A'] */ $redis->lRem('key1', 'A', 2); /* 2 */ -$redis->lRange('key1', 0, -1); /* array('C', 'B', 'A') */ +$redis->lRange('key1', 0, -1); /* ['C', 'B', 'A'] */ ~~~ ### lSet @@ -1991,9 +1991,9 @@ _**Description**_: Trims an existing list so that it will contain only a specifi $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); -$redis->lRange('key1', 0, -1); /* array('A', 'B', 'C') */ +$redis->lRange('key1', 0, -1); /* ['A', 'B', 'C'] */ $redis->lTrim('key1', 0, 1); -$redis->lRange('key1', 0, -1); /* array('A', 'B') */ +$redis->lRange('key1', 0, -1); /* ['A', 'B'] */ ~~~ ### rPop @@ -2619,7 +2619,7 @@ _**Description**_: Add one or more members to a sorted set or update its score i $redis->zAdd('key', 1, 'val1'); $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 5, 'val5'); -$redis->zRange('key', 0, -1); // array(val0, val1, val5) +$redis->zRange('key', 0, -1); // [val0, val1, val5] ~~~ ### zCard, zSize @@ -2657,7 +2657,7 @@ _**Description**_: Returns the *number* of elements of the sorted set stored at $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); -$redis->zCount('key', 0, 3); /* 2, corresponding to array('val0', 'val2') */ +$redis->zCount('key', 0, 3); /* 2, corresponding to ['val0', 'val2'] */ ~~~ ### zIncrBy @@ -2714,12 +2714,12 @@ $redis->zAdd('k1', 3, 'val3'); $redis->zAdd('k2', 2, 'val1'); $redis->zAdd('k2', 3, 'val3'); -$redis->zInter('ko1', array('k1', 'k2')); /* 2, 'ko1' => array('val1', 'val3') */ -$redis->zInter('ko2', array('k1', 'k2'), array(1, 1)); /* 2, 'ko2' => array('val1', 'val3') */ +$redis->zInter('ko1', ['k1', 'k2']); /* 2, 'ko1' => ['val1', 'val3'] */ +$redis->zInter('ko2', ['k1', 'k2'], [1, 1]); /* 2, 'ko2' => ['val1', 'val3'] */ /* Weighted zInter */ -$redis->zInter('ko3', array('k1', 'k2'), array(1, 5), 'min'); /* 2, 'ko3' => array('val1', 'val3') */ -$redis->zInter('ko4', array('k1', 'k2'), array(1, 5), 'max'); /* 2, 'ko4' => array('val3', 'val1') */ +$redis->zInter('ko3', ['k1', 'k2'], [1, 5], 'min'); /* 2, 'ko3' => ['val1', 'val3'] */ +$redis->zInter('ko4', ['k1', 'k2'], [1, 5], 'max'); /* 2, 'ko4' => ['val3', 'val1'] */ ~~~ ### zRange @@ -2744,10 +2744,10 @@ Start and stop are interpreted as zero-based indices: $redis->zAdd('key1', 0, 'val0'); $redis->zAdd('key1', 2, 'val2'); $redis->zAdd('key1', 10, 'val10'); -$redis->zRange('key1', 0, -1); /* array('val0', 'val2', 'val10') */ +$redis->zRange('key1', 0, -1); /* ['val0', 'val2', 'val10'] */ // with scores -$redis->zRange('key1', 0, -1, true); /* array('val0' => 0, 'val2' => 2, 'val10' => 10) */ +$redis->zRange('key1', 0, -1, true); /* ['val0' => 0, 'val2' => 2, 'val10' => 10] */ ~~~ ### zRangeByScore, zRevRangeByScore @@ -2760,7 +2760,7 @@ _**Description**_: Returns the elements of the sorted set stored at the specifie *end*: string *options*: array -Two options are available: `withscores => TRUE`, and `limit => array($offset, $count)` +Two options are available: `withscores => TRUE`, and `limit => [$offset, $count]` ##### *Return value* *Array* containing the values in specified range. @@ -2770,10 +2770,10 @@ Two options are available: `withscores => TRUE`, and `limit => array($offset, $c $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); -$redis->zRangeByScore('key', 0, 3); /* array('val0', 'val2') */ -$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); /* array('val0' => 0, 'val2' => 2) */ -$redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1))); /* array('val2') */ -$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1))); /* array('val2' => 2) */ +$redis->zRangeByScore('key', 0, 3); /* ['val0', 'val2'] */ +$redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]); /* ['val0' => 0, 'val2' => 2] */ +$redis->zRangeByScore('key', 0, 3, ['limit' => [1, 1]]); /* ['val2'] */ +$redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE, 'limit' => [1, 1]]); /* ['val2' => 2] */ ~~~ ### zRangeByLex @@ -2792,12 +2792,12 @@ _**Description**_: Returns a lexicographical range of members in a sorted set, ##### *Example* ~~~php -foreach(Array('a','b','c','d','e','f','g') as $c) +foreach(['a','b','c','d','e','f','g'] as $c) $redis->zAdd('key',0,$c); -$redis->zRangeByLex('key','-','[c') /* Array('a','b','c'); */ -$redis->zRangeByLex('key','-','(c') /* Array('a','b') */ -$redis->zRangeByLex('key','-','[c',1,2) /* Array('b','c') */ +$redis->zRangeByLex('key','-','[c') /* ['a','b','c']; */ +$redis->zRangeByLex('key','-','(c') /* ['a','b'] */ +$redis->zRangeByLex('key','-','[c',1,2) /* ['b','c'] */ ~~~ ### zRank, zRevRank @@ -2839,7 +2839,7 @@ $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); $redis->zDelete('key', 'val2'); -$redis->zRange('key', 0, -1); /* array('val0', 'val10') */ +$redis->zRange('key', 0, -1); /* ['val0', 'val10'] */ ~~~ ### zRemRangeByRank, zDeleteRangeByRank @@ -2860,7 +2860,7 @@ $redis->zAdd('key', 1, 'one'); $redis->zAdd('key', 2, 'two'); $redis->zAdd('key', 3, 'three'); $redis->zRemRangeByRank('key', 0, 1); /* 2 */ -$redis->zRange('key', 0, -1, array('withscores' => TRUE)); /* array('three' => 3) */ +$redis->zRange('key', 0, -1, ['withscores' => TRUE]); /* ['three' => 3] */ ~~~ ### zRemRangeByScore, zDeleteRangeByScore @@ -2903,10 +2903,10 @@ _**Description**_: Returns the elements of the sorted set stored at the specifie $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); $redis->zAdd('key', 10, 'val10'); -$redis->zRevRange('key', 0, -1); /* array('val10', 'val2', 'val0') */ +$redis->zRevRange('key', 0, -1); /* ['val10', 'val2', 'val0'] */ // with scores -$redis->zRevRange('key', 0, -1, true); /* array('val10' => 10, 'val2' => 2, 'val0' => 0) */ +$redis->zRevRange('key', 0, -1, true); /* ['val10' => 10, 'val2' => 2, 'val0' => 0] */ ~~~ ### zScore @@ -2957,11 +2957,11 @@ $redis->zAdd('k1', 1, 'val1'); $redis->zAdd('k2', 2, 'val2'); $redis->zAdd('k2', 3, 'val3'); -$redis->zUnion('ko1', array('k1', 'k2')); /* 4, 'ko1' => array('val0', 'val1', 'val2', 'val3') */ +$redis->zUnion('ko1', ['k1', 'k2']); /* 4, 'ko1' => ['val0', 'val1', 'val2', 'val3'] */ /* Weighted zUnion */ -$redis->zUnion('ko2', array('k1', 'k2'), array(1, 1)); /* 4, 'ko2' => array('val0', 'val1', 'val2', 'val3') */ -$redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val0', 'val2', 'val3', 'val1') */ +$redis->zUnion('ko2', ['k1', 'k2'], [1, 1]); /* 4, 'ko2' => ['val0', 'val1', 'val2', 'val3'] */ +$redis->zUnion('ko3', ['k1', 'k2'], [5, 1]); /* 4, 'ko3' => ['val0', 'val2', 'val3', 'val1'] */ ~~~ ### zScan @@ -3261,7 +3261,7 @@ echo "Within 300 miles of Honolulu:\n"; var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi')); echo "\nFirst match within 300 miles of Honolulu:\n"; -var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi', Array('count' => 1))); +var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi', ['count' => 1])); ~~~ ##### *Output* @@ -3654,7 +3654,7 @@ _**Description**_: Subscribe to channels. Warning: this function will probably c ##### *Parameters* *channels*: an array of channels to subscribe to -*callback*: either a string or an array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. +*callback*: either a string or an Array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~php @@ -3674,7 +3674,7 @@ function f($redis, $chan, $msg) { } } -$redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans +$redis->subscribe(['chan-1', 'chan-2', 'chan-3'], 'f'); // subscribe to 3 chans ~~~ ### pubSub @@ -3694,7 +3694,7 @@ _**Description**_: A command allowing you to get information on the Redis pub/su ~~~php $redis->pubSub("channels"); /*All channels */ $redis->pubSub("channels", "*pattern*"); /* Just channels matching your pattern */ -$redis->pubSub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/ +$redis->pubSub("numsub", ["chan1", "chan2"]); /*Get subscriber counts for 'chan1' and 'chan2'*/ $redis->pubSub("numpat"); /* Get the number of pattern subscribers */ @@ -3753,11 +3753,7 @@ $ret = $redis->multi() ->exec(); /* -$ret == array( - 0 => TRUE, - 1 => 'val1', - 2 => TRUE, - 3 => 'val2'); +$ret == Array(0 => TRUE, 1 => 'val1', 2 => TRUE, 3 => 'val2'); */ ~~~ @@ -3772,7 +3768,7 @@ If the key is modified between `WATCH` and `EXEC`, the MULTI/EXEC transaction wi ##### *Example* ~~~php -$redis->watch('x'); // or for a list of keys: $redis->watch(array('x','another key')); +$redis->watch('x'); // or for a list of keys: $redis->watch(['x','another key']); /* long code here during the execution of which other clients could well modify `x` */ $ret = $redis->multi() ->incr('x') @@ -3812,12 +3808,12 @@ executing the LUA script, the getLastError() function can tell you the message t ##### *Examples* ~~~php $redis->eval("return 1"); // Returns an integer: 1 -$redis->eval("return {1,2,3}"); // Returns Array(1,2,3) +$redis->eval("return {1,2,3}"); // Returns [1,2,3] $redis->del('mylist'); $redis->rpush('mylist','a'); $redis->rpush('mylist','b'); $redis->rpush('mylist','c'); -// Nested response: Array(1,2,3,Array('a','b','c')); +// Nested response: [1,2,3,['a','b','c']]; $redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}"); ~~~ @@ -3959,7 +3955,7 @@ will change Array values to 'Array', and Objects to 'Object'. ~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); $redis->_serialize("foo"); // returns "foo" -$redis->_serialize(Array()); // Returns "Array" +$redis->_serialize([]); // Returns "Array" $redis->_serialize(new stdClass()); // Returns "Object" $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); @@ -3980,7 +3976,7 @@ serializing values, and you return something from redis in a LUA script that is ##### *Examples* ~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); -$redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2,3) +$redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return [1,2,3] ~~~ diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 49c71a9860..b46a06ee92 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -591,9 +591,9 @@ function run_tests($className, $str_filter, $str_host) { // reset rings global $newRing, $oldRing, $serverList; - $newRing = Array("$str_host:6379", "$str_host:6380", "$str_host:6381"); - $oldRing = Array(); - $serverList = Array("$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"); + $newRing = ["$str_host:6379", "$str_host:6380", "$str_host:6381"]; + $oldRing = []; + $serverList = ["$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"]; // run return TestSuite::run($className, $str_filter); diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index ccd8927ac5..02e78209f3 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -7,21 +7,21 @@ * where we're validating specific cluster mechanisms */ class Redis_Cluster_Test extends Redis_Test { - private static $_arr_node_map = Array(); + private static $_arr_node_map = []; - private $_arr_redis_types = Array( + private $_arr_redis_types = [ Redis::REDIS_STRING, Redis::REDIS_SET, Redis::REDIS_LIST, Redis::REDIS_ZSET, Redis::REDIS_HASH - ); + ]; - private $_arr_failover_types = Array( + private $_arr_failover_types = [ RedisCluster::FAILOVER_NONE, RedisCluster::FAILOVER_ERROR, RedisCluster::FAILOVER_DISTRIBUTE - ); + ]; /** * @var string @@ -144,12 +144,12 @@ public function testDBSize() { } public function testInfo() { - $arr_check_keys = Array( + $arr_check_keys = [ "redis_version", "arch_bits", "uptime_in_seconds", "uptime_in_days", "connected_clients", "connected_slaves", "used_memory", "total_connections_received", "total_commands_processed", "role" - ); + ]; for ($i = 0; $i < 3; $i++) { $arr_info = $this->redis->info("k:$i"); @@ -233,14 +233,14 @@ public function testPubSub() { $this->assertTrue(is_array($result)); $this->assertEquals(count($result), 4); - $arr_zipped = Array(); + $arr_zipped = []; for ($i = 0; $i <= count($result) / 2; $i+=2) { $arr_zipped[$result[$i]] = $result[$i+1]; } $result = $arr_zipped; // Make sure the elements are correct, and have zero counts - foreach(Array($c1,$c2) as $channel) { + foreach([$c1,$c2] as $channel) { $this->assertTrue(isset($result[$channel])); $this->assertEquals($result[$channel], 0); } @@ -258,20 +258,20 @@ public function testPubSub() { public function testMSetNX() { /* All of these keys should get set */ $this->redis->del('x','y','z'); - $ret = $this->redis->msetnx(Array('x'=>'a','y'=>'b','z'=>'c')); + $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']); $this->assertTrue(is_array($ret)); $this->assertEquals(array_sum($ret),count($ret)); /* Delete one key */ $this->redis->del('x'); - $ret = $this->redis->msetnx(Array('x'=>'a','y'=>'b','z'=>'c')); + $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']); $this->assertTrue(is_array($ret)); $this->assertEquals(array_sum($ret),1); $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE } - /* Slowlog needs to take a key or Array(ip, port), to direct it to a node */ + /* Slowlog needs to take a key or [ip, port], to direct it to a node */ public function testSlowlog() { $str_key = uniqid() . '-' . rand(1, 1000); @@ -308,7 +308,7 @@ public function testFailedTransactions() { // This transaction should fail because the other client changed 'x' $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === Array(FALSE)); + $this->assertTrue($ret === [false]); // watch and unwatch $this->redis->watch('x'); $r->incr('x'); // other instance @@ -370,8 +370,8 @@ public function testEvalSHA() { $this->redis->script($str_key, 'flush'); // Non existant script (but proper sha1), and a random (not) sha1 string - $this->assertFalse($this->redis->evalsha(sha1(uniqid()),Array($str_key), 1)); - $this->assertFalse($this->redis->evalsha('some-random-data'),Array($str_key), 1); + $this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$str_key], 1)); + $this->assertFalse($this->redis->evalsha('some-random-data'),[$str_key], 1); // Load a script $cb = uniqid(); // To ensure the script is new @@ -379,9 +379,9 @@ public function testEvalSHA() { $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,Array($str_key), 1)); - $this->assertTrue(1 === $this->redis->eval($scr,Array($str_key), 1)); - $this->assertTrue(1 === $this->redis->evalsha($sha,Array($str_key), 1)); + $this->assertTrue(false === $this->redis->evalsha($scr,[$str_key], 1)); + $this->assertTrue(1 === $this->redis->eval($scr,[$str_key], 1)); + $this->assertTrue(1 === $this->redis->evalsha($sha,[$str_key], 1)); } public function testEvalBulkResponse() { @@ -393,7 +393,7 @@ public function testEvalBulkResponse() { $scr = "return {KEYS[1],KEYS[2]}"; - $result = $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + $result = $this->redis->eval($scr,[$str_key1, $str_key2], 2); $this->assertTrue($str_key1 === $result[0]); $this->assertTrue($str_key2 === $result[1]); @@ -409,7 +409,7 @@ public function testEvalBulkResponseMulti() { $scr = "return {KEYS[1],KEYS[2]}"; $this->redis->multi(); - $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + $this->redis->eval($scr, [$str_key1, $str_key2], 2); $result = $this->redis->exec(); @@ -426,7 +426,7 @@ public function testEvalBulkEmptyResponse() { $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; - $result = $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + $result = $this->redis->eval($scr, [$str_key1, $str_key2], 2); $this->assertTrue(null === $result); } @@ -441,7 +441,7 @@ public function testEvalBulkEmptyResponseMulti() { $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; $this->redis->multi(); - $this->redis->eval($scr,Array($str_key1, $str_key2), 2); + $this->redis->eval($scr, [$str_key1, $str_key2], 2); $result = $this->redis->exec(); $this->assertTrue(null === $result[0]); @@ -487,37 +487,37 @@ protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) { $this->redis->set($str_key, $value); break; case Redis::REDIS_SET: - $value = Array( + $value = [ $str_key . '-mem1', $str_key . '-mem2', $str_key . '-mem3', $str_key . '-mem4', $str_key . '-mem5', $str_key . '-mem6' - ); + ]; $arr_args = $value; array_unshift($arr_args, $str_key); - call_user_func_array(Array($this->redis, 'sadd'), $arr_args); + call_user_func_array([$this->redis, 'sadd'], $arr_args); break; case Redis::REDIS_HASH: - $value = Array( + $value = [ $str_key . '-mem1' => $str_key . '-val1', $str_key . '-mem2' => $str_key . '-val2', $str_key . '-mem3' => $str_key . '-val3' - ); + ]; $this->redis->hmset($str_key, $value); break; case Redis::REDIS_LIST: - $value = Array( + $value = [ $str_key . '-ele1', $str_key . '-ele2', $str_key . '-ele3', $str_key . '-ele4', $str_key . '-ele5', $str_key . '-ele6' - ); + ]; $arr_args = $value; array_unshift($arr_args, $str_key); - call_user_func_array(Array($this->redis, 'rpush'), $arr_args); + call_user_func_array([$this->redis, 'rpush'], $arr_args); break; case Redis::REDIS_ZSET: $i_score = 1; - $value = Array( + $value = [ $str_key . '-mem1' => 1, $str_key . '-mem2' => 2, $str_key . '-mem3' => 3, $str_key . '-mem3' => 3 - ); + ]; foreach ($value as $str_mem => $i_score) { $this->redis->zadd($str_key, $i_score, $str_mem); } @@ -571,8 +571,8 @@ protected function checkKeyValue($str_key, $i_type, $value) { /* Test automatic load distributor */ public function testFailOver() { - $arr_value_ref = Array(); - $arr_type_ref = Array(); + $arr_value_ref = []; + $arr_type_ref = []; /* Set a bunch of keys of various redis types*/ for ($i = 0; $i < 200; $i++) { @@ -601,12 +601,12 @@ public function testRawCommand() { $this->redis->del('mylist'); $this->redis->rpush('mylist', 'A','B','C','D'); - $this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D')); + $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']); } protected function rawCommandArray($key, $args) { array_unshift($args, $key); - return call_user_func_array(Array($this->redis, 'rawCommand'), $args); + return call_user_func_array([$this->redis, 'rawCommand'], $args); } public function testSession() diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 9887daac54..03f6e0e826 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -8,18 +8,18 @@ class Redis_Test extends TestSuite const AUTH = NULL; //replace with a string to use Redis authentication /* City lat/long */ - protected $cities = Array( - 'Chico' => Array(-121.837478, 39.728494), - 'Sacramento' => Array(-121.494400, 38.581572), - 'Gridley' => Array(-121.693583, 39.363777), - 'Marysville' => Array(-121.591355, 39.145725), - 'Cupertino' => Array(-122.032182, 37.322998) - ); - - protected $serializers = Array( + protected $cities = [ + 'Chico' => [-121.837478, 39.728494], + 'Sacramento' => [-121.494400, 38.581572], + 'Gridley' => [-121.693583, 39.363777], + 'Marysville' => [-121.591355, 39.145725], + 'Cupertino' => [-122.032182, 37.322998] + ]; + + protected $serializers = [ Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, - ); + ]; /** * @var Redis @@ -131,14 +131,14 @@ public function testPubSub() { $c1 = uniqid() . '-' . rand(1,100); $c2 = uniqid() . '-' . rand(1,100); - $result = $this->redis->pubsub("numsub", Array($c1, $c2)); + $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(Array($c1,$c2) as $channel) { + foreach([$c1,$c2] as $channel) { $this->assertTrue(isset($result[$channel])); $this->assertEquals($result[$channel], 0); } @@ -324,39 +324,39 @@ public function testExtendedSet() { /* Set if not exist */ $this->redis->del('foo'); - $this->assertTrue($this->redis->set('foo','bar',Array('nx'))); + $this->assertTrue($this->redis->set('foo','bar', ['nx'])); $this->assertEquals($this->redis->get('foo'), 'bar'); - $this->assertFalse($this->redis->set('foo','bar',Array('nx'))); + $this->assertFalse($this->redis->set('foo','bar', ['nx'])); /* Set if exists */ - $this->assertTrue($this->redis->set('foo','bar',Array('xx'))); + $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',Array('xx'))); + $this->assertFalse($this->redis->set('foo','bar', ['xx'])); /* Set with a TTL */ - $this->assertTrue($this->redis->set('foo','bar',Array('ex'=>100))); + $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',Array('px'=>100000))); + $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',Array('xx','ex'=>105))); + $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', Array('nx', 'ex'=>110))); + $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', Array('nx', 'ex'=>110))); + $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', Array('not-valid','nx','invalid','ex'=>200))); + $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'); @@ -407,13 +407,13 @@ public function testRenameNx() { $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) === array('val1', 'val0')); - $this->assertTrue($this->redis->lRange('{key}1', 0, -1) === array('val1-1', 'val1-0')); + $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) === array()); - $this->assertTrue($this->redis->lRange('{key}2', 0, -1) === array('val1', 'val0')); + $this->assertTrue($this->redis->lRange('{key}0', 0, -1) === []); + $this->assertTrue($this->redis->lRange('{key}2', 0, -1) === ['val1', 'val0']); } public function testMultiple() { @@ -426,15 +426,15 @@ public function testMultiple() { $this->redis->set('k3', 'v3'); $this->redis->set(1, 'test'); - $this->assertEquals(array('v1'), $this->redis->mget(array('k1'))); - $this->assertEquals(array('v1', 'v3', false), $this->redis->mget(array('k1', 'k3', 'NoKey'))); - $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->mget(array('k1', 'k2', 'k3'))); - $this->assertEquals(array('v1', 'v2', 'v3'), $this->redis->mget(array('k1', 'k2', 'k3'))); + $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(array(0 => '$1111111111'), $this->redis->mget(array('k5'))); + $this->assertEquals([0 => '$1111111111'], $this->redis->mget(['k5'])); - $this->assertEquals(array(0 => 'test'), $this->redis->mget(array(1))); // non-string + $this->assertEquals([0 => 'test'], $this->redis->mget([1])); // non-string } public function testMultipleBin() { @@ -447,8 +447,8 @@ public function testMultipleBin() { $this->redis->set('k2', gzcompress('v2')); $this->redis->set('k3', gzcompress('v3')); - $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->mget(array('k1', 'k2', 'k3'))); - $this->assertEquals(array(gzcompress('v1'), gzcompress('v2'), gzcompress('v3')), $this->redis->mget(array('k1', 'k2', 'k3'))); + $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'])); } @@ -616,7 +616,7 @@ public function testExists() $this->assertEquals(1, $this->redis->exists('key')); /* Add multiple keys */ - $mkeys = Array(); + $mkeys = []; for ($i = 0; $i < 10; $i++) { if (rand(1, 2) == 1) { $mkey = "{exists}key:$i"; @@ -627,7 +627,7 @@ public function testExists() /* 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(Array($this->redis, 'exists'), $mkeys)); + $this->assertEquals(count($mkeys), call_user_func_array([$this->redis, 'exists'], $mkeys)); } public function testGetKeys() @@ -646,7 +646,7 @@ public function testGetKeys() $this->assertEquals((count($keys) + 1), count($keys2)); // empty array when no key matches - $this->assertEquals(array(), $this->redis->keys(rand().rand().rand().'*')); + $this->assertEquals([], $this->redis->keys(rand().rand().rand().'*')); } protected function genericDelUnlink($cmd) { @@ -678,7 +678,7 @@ protected function genericDelUnlink($cmd) { $this->redis->set('x', 0); $this->redis->set('y', 1); - $this->assertEquals(2, $this->redis->$cmd(array('x', 'y'))); + $this->assertEquals(2, $this->redis->$cmd(['x', 'y'])); } public function testDelete() { @@ -721,7 +721,7 @@ public function testType() // sadd with numeric key $this->redis->del(123); $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); - $this->assertTrue(array('val0') === $this->redis->sMembers(123)); + $this->assertTrue(['val0'] === $this->redis->sMembers(123)); // zset $this->redis->del('keyZSet'); @@ -837,19 +837,19 @@ public function testblockingPop() { $this->redis->del('list'); $this->redis->lPush('list', 'val1'); $this->redis->lPush('list', 'val2'); - $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val2')); - $this->assertTrue($this->redis->blPop(array('list'), 2) === array('list', 'val1')); + $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(array('list'), 1) === array('list', 'val1')); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array('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'); - $this->assertTrue($this->redis->blPop(array('list'), 1) === array()); - $this->assertTrue($this->redis->brPop(array('list'), 1) === array()); + $this->assertTrue($this->redis->blPop(['list'], 1) === []); + $this->assertTrue($this->redis->brPop(['list'], 1) === []); } public function testllen() @@ -889,7 +889,7 @@ public function testlPopx() { $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) === array('val1', 'val0', 'val2')); + $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val1', 'val0', 'val2']); //test linsert $this->redis->del('key'); @@ -899,7 +899,7 @@ public function testlPopx() { $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) === array('val2', 'val0', 'val1')); + $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val2', 'val0', 'val1']); } // ltrim, lsize, lpop @@ -949,7 +949,7 @@ public function setupSort() { // set-up $this->redis->del('person:id'); - foreach(array(1,2,3,4) as $id) { + foreach([1,2,3,4] as $id) { $this->redis->lPush('person:id', $id); } } @@ -962,9 +962,9 @@ public function testSortPrefix() { $this->redis->sadd('some-item', 2); $this->redis->sadd('some-item', 3); - $this->assertEquals(array('1','2','3'), $this->redis->sortAsc('some-item')); - $this->assertEquals(array('3','2','1'), $this->redis->sortDesc('some-item')); - $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item')); + $this->assertEquals(['1','2','3'], $this->redis->sortAsc('some-item')); + $this->assertEquals(['3','2','1'], $this->redis->sortDesc('some-item')); + $this->assertEquals(['1','2','3'], $this->redis->sort('some-item')); // Kill our set/prefix $this->redis->del('some-item'); @@ -976,41 +976,41 @@ public function testSortAsc() { $this->assertTrue(FALSE === $this->redis->sortAsc(NULL)); // sort by age and get IDs - $byAgeAsc = array('3','1','2','4'); + $byAgeAsc = ['3','1','2','4']; $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*')); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'sort' => 'asc'))); - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL)); // check that NULL works. - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. - $this->assertEquals(array('1', '2', '3', '4'), $this->redis->sort('person:id', array('sort' => 'asc'))); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'sort' => 'asc'])); + $this->assertEquals(['1', '2', '3', '4'], $this->redis->sortAsc('person:id', NULL)); // check that NULL works. + $this->assertEquals(['1', '2', '3', '4'], $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. + $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['sort' => 'asc'])); // sort by age and get names - $byAgeAsc = array('Carol','Alice','Bob','Dave'); + $byAgeAsc = ['Carol','Alice','Bob','Dave']; $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*')); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'))); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'])); $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 2)); - $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 2), 'sort' => 'asc'))); + $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'asc'])); $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); - $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(1, 2), 'sort' => 'asc'))); + $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'asc'])); $this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, 4)))); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array(0, "4")))); // with strings - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', array('by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => array("0", 4)))); - $this->assertEquals(array(), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 4]])); + $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]])); + $this->assertEquals([], $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. // sort by salary and get ages - $agesBySalaryAsc = array('34', '27', '25', '41'); + $agesBySalaryAsc = ['34', '27', '25', '41']; $this->assertEquals($agesBySalaryAsc, $this->redis->sortAsc('person:id', 'person:salary_*', 'person:age_*')); - $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'))); + $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc'])); - $agesAndSalaries = $this->redis->sort('person:id', array('by' => 'person:salary_*', 'get' => array('person:age_*', 'person:salary_*'), 'sort' => 'asc')); - $this->assertEquals(array('34', '2000', '27', '2500', '25', '2800', '41', '3100'), $agesAndSalaries); + $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 = array('abc', 'def', 'ghi'); + $list = ['abc', 'def', 'ghi']; $this->redis->del('list'); foreach($list as $i) { $this->redis->lPush('list', $i); @@ -1019,7 +1019,7 @@ public function testSortAsc() { // SORT list → [ghi, def, abc] if (version_compare($this->version, "2.5.0", "lt")) { $this->assertEquals(array_reverse($list), $this->redis->sortAsc('list')); - $this->assertEquals(array_reverse($list), $this->redis->sort('list', array('sort' => 'asc'))); + $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 @@ -1028,7 +1028,7 @@ public function testSortAsc() { // SORT list ALPHA → [abc, def, ghi] $this->assertEquals($list, $this->redis->sortAscAlpha('list')); - $this->assertEquals($list, $this->redis->sort('list', array('sort' => 'asc', 'alpha' => TRUE))); + $this->assertEquals($list, $this->redis->sort('list', ['sort' => 'asc', 'alpha' => TRUE])); } public function testSortDesc() { @@ -1036,22 +1036,22 @@ public function testSortDesc() { $this->setupSort(); // sort by age and get IDs - $byAgeDesc = array('4','2','1','3'); + $byAgeDesc = ['4','2','1','3']; $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*')); // sort by age and get names - $byAgeDesc = array('Dave', 'Bob', 'Alice', 'Carol'); + $byAgeDesc = ['Dave', 'Bob', 'Alice', 'Carol']; $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*')); $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 0, 2)); $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 1, 2)); // sort by salary and get ages - $agesBySalaryDesc = array('41', '25', '27', '34'); + $agesBySalaryDesc = ['41', '25', '27', '34']; $this->assertEquals($agesBySalaryDesc, $this->redis->sortDesc('person:id', 'person:salary_*', 'person:age_*')); // sort non-alpha doesn't change all-string lists - $list = array('def', 'abc', 'ghi'); + $list = ['def', 'abc', 'ghi']; $this->redis->del('list'); foreach($list as $i) { $this->redis->lPush('list', $i); @@ -1067,7 +1067,7 @@ public function testSortDesc() { } // SORT list ALPHA → [abc, def, ghi] - $this->assertEquals(array('ghi', 'def', 'abc'), $this->redis->sortDescAlpha('list')); + $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sortDescAlpha('list')); } // LINDEX @@ -1196,8 +1196,8 @@ public function testsMove() $this->assertEquals(1, $this->redis->scard('{set}0')); $this->assertEquals(1, $this->redis->scard('{set}1')); - $this->assertEquals(array('val2'), $this->redis->smembers('{set}0')); - $this->assertEquals(array('val'), $this->redis->smembers('{set}1')); + $this->assertEquals(['val2'], $this->redis->smembers('{set}0')); + $this->assertEquals(['val'], $this->redis->smembers('{set}1')); } public function testsPop() @@ -1252,7 +1252,7 @@ public function testsRandMember() { $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); - $got = array(); + $got = []; while(true) { $v = $this->redis->sRandMember('set0'); $this->assertTrue(2 === $this->redis->scard('set0')); // no change. @@ -1362,7 +1362,7 @@ public function testsmembers() $this->redis->sAdd('set', 'val2'); $this->redis->sAdd('set', 'val3'); - $array = array('val', 'val2', 'val3'); + $array = ['val', 'val2', 'val3']; $smembers = $this->redis->smembers('set'); sort($smembers); @@ -1398,22 +1398,22 @@ public function testsInter() { $this->redis->del('{set}square'); // set of squares $this->redis->del('{set}seq'); // set of numbers of the form n^2 - 1 - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + $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 = array(1,2,3,5,7,11,13,17,19,23); + $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}prime', $i); } - $z = array(1,4,9,16,25); + $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}square', $i); } - $t = array(2,5,10,17,26); + $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}seq', $i); } @@ -1424,7 +1424,7 @@ public function testsInter() { $this->assertTrue(in_array($i, array_intersect($x, $y))); } - $xy = $this->redis->sInter(array('{set}odd', '{set}prime')); // odd prime numbers, as array. + $xy = $this->redis->sInter(['{set}odd', '{set}prime']); // odd prime numbers, as array. foreach($xy as $i) { $i = (int)$i; $this->assertTrue(in_array($i, array_intersect($x, $y))); @@ -1436,24 +1436,24 @@ public function testsInter() { $this->assertTrue(in_array($i, array_intersect($y, $z))); } - $yz = $this->redis->sInter(array('{set}prime', '{set}square')); // set of odd squares, as array + $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($i, array_intersect($y, $z))); } $zt = $this->redis->sInter('{set}square', '{set}seq'); // prime squares - $this->assertTrue($zt === array()); - $zt = $this->redis->sInter(array('{set}square', '{set}seq')); // prime squares, as array - $this->assertTrue($zt === array()); + $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 === array('1')); + $this->assertTrue($xyz === ['1']); - $xyz = $this->redis->sInter(array('{set}odd', '{set}prime', '{set}square'));// odd prime squares, with an array as a parameter - $this->assertTrue($xyz === array('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(array()); + $nil = $this->redis->sInter([]); $this->assertTrue($nil === FALSE); } @@ -1463,28 +1463,28 @@ public function testsInterStore() { $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + $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 = array(1,2,3,5,7,11,13,17,19,23); + $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = array(1,4,9,16,25); + $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = array(2,5,10,17,26); + $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(Array('{set}k', '{set}x', '{set}y')), count(array_intersect($x,$y))); + $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')); @@ -1521,22 +1521,22 @@ public function testsUnion() { $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + $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 = array(1,2,3,5,7,11,13,17,19,23); + $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = array(1,4,9,16,25); + $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = array(2,5,10,17,26); + $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } @@ -1572,22 +1572,22 @@ public function testsUnionStore() { $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + $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 = array(1,2,3,5,7,11,13,17,19,23); + $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = array(1,4,9,16,25); + $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = array(2,5,10,17,26); + $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } @@ -1643,22 +1643,22 @@ public function testsDiff() { $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + $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 = array(1,2,3,5,7,11,13,17,19,23); + $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = array(1,4,9,16,25); + $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = array(2,5,10,17,26); + $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } @@ -1694,22 +1694,22 @@ public function testsDiffStore() { $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 - $x = array(1,3,5,7,9,11,13,15,17,19,21,23,25); + $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 = array(1,2,3,5,7,11,13,17,19,23); + $y = [1,2,3,5,7,11,13,17,19,23]; foreach($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = array(1,4,9,16,25); + $z = [1,4,9,16,25]; foreach($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = array(2,5,10,17,26); + $t = [2,5,10,17,26]; foreach($t as $i) { $this->redis->sAdd('{set}t', $i); } @@ -1769,17 +1769,17 @@ public function testlrange() { // pos : -3 -2 -1 // list: [val3, val2, val] - $this->assertEquals($this->redis->lrange('list', 0, 0), array('val3')); - $this->assertEquals($this->redis->lrange('list', 0, 1), array('val3', 'val2')); - $this->assertEquals($this->redis->lrange('list', 0, 2), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lrange('list', 0, 3), array('val3', 'val2', 'val')); + $this->assertEquals($this->redis->lrange('list', 0, 0), ['val3']); + $this->assertEquals($this->redis->lrange('list', 0, 1), ['val3', 'val2']); + $this->assertEquals($this->redis->lrange('list', 0, 2), ['val3', 'val2', 'val']); + $this->assertEquals($this->redis->lrange('list', 0, 3), ['val3', 'val2', 'val']); - $this->assertEquals($this->redis->lrange('list', 0, -1), array('val3', 'val2', 'val')); - $this->assertEquals($this->redis->lrange('list', 0, -2), array('val3', 'val2')); - $this->assertEquals($this->redis->lrange('list', -2, -1), array('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), array()); + $this->assertEquals($this->redis->lrange('list', 0, -1), []); } public function testdbSize() { @@ -1882,7 +1882,7 @@ public function testWait() { } public function testInfo() { - foreach (Array(false, true) as $boo_multi) { + foreach ([false, true] as $boo_multi) { if ($boo_multi) { $this->redis->multi(); $this->redis->info(); @@ -1892,7 +1892,7 @@ public function testInfo() { $info = $this->redis->info(); } - $keys = array( + $keys = [ "redis_version", "arch_bits", "uptime_in_seconds", @@ -1903,7 +1903,7 @@ public function testInfo() { "total_connections_received", "total_commands_processed", "role" - ); + ]; if (version_compare($this->version, "2.5.0", "lt")) { array_push($keys, "changes_since_last_save", @@ -1957,15 +1957,15 @@ public function testSwapDB() { public function testMset() { $this->redis->del('x', 'y', 'z'); // remove x y z - $this->assertTrue($this->redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z + $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check 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(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check 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->assertFalse($this->redis->mset(array())); // set ø → FALSE + $this->assertFalse($this->redis->mset([])); // set ø → FALSE /* @@ -1973,7 +1973,7 @@ public function testMset() { */ // No prefix - $set_array = Array(-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three'); + $set_array = [-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => '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)); @@ -1990,15 +1990,15 @@ public function testMset() { public function testMsetNX() { $this->redis->del('x', 'y', 'z'); // remove x y z - $this->assertTrue(TRUE === $this->redis->msetnx(array('x' => 'a', 'y' => 'b', 'z' => 'c'))); // set x y z + $this->assertTrue(TRUE === $this->redis->msetnx(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array('a', 'b', 'c')); // check 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(array('x' => 'A', 'y' => 'B', 'z' => 'C'))); // set x y z - $this->assertEquals($this->redis->mget(array('x', 'y', 'z')), array(FALSE, 'b', 'c')); // check x y z + $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(array())); // set ø → FALSE + $this->assertFalse($this->redis->msetnx([])); // set ø → FALSE } public function testRpopLpush() { @@ -2011,14 +2011,14 @@ public function testRpopLpush() { $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), array('def')); // only def remains in x. - $this->assertEquals($this->redis->lrange('{list}y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. + $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(array() === $this->redis->lrange('{list}x', 0, -1)); - $this->assertTrue(array() === $this->redis->lrange('{list}y', 0, -1)); + $this->assertTrue([] === $this->redis->lrange('{list}x', 0, -1)); + $this->assertTrue([] === $this->redis->lrange('{list}y', 0, -1)); } public function testBRpopLpush() { @@ -2031,14 +2031,14 @@ public function testBRpopLpush() { $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), array('def')); // only def remains in x. - $this->assertEquals($this->redis->lrange('{list}y', 0, -1), array('abc', '456', '123')); // abc has been lpushed to y. + $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(array() === $this->redis->lrange('{list}x', 0, -1)); - $this->assertTrue(array() === $this->redis->lrange('{list}y', 0, -1)); + $this->assertTrue([] === $this->redis->lrange('{list}x', 0, -1)); + $this->assertTrue([] === $this->redis->lrange('{list}y', 0, -1)); } public function testZAddFirstArg() { @@ -2049,14 +2049,14 @@ public function testZAddFirstArg() { $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1')); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRange($zsetName, 0, -1)); + $this->assertTrue(['val0', 'val1'] === $this->redis->zRange($zsetName, 0, -1)); } public function testZX() { $this->redis->del('key'); - $this->assertTrue(array() === $this->redis->zRange('key', 0, -1)); - $this->assertTrue(array() === $this->redis->zRange('key', 0, -1, true)); + $this->assertTrue([] === $this->redis->zRange('key', 0, -1)); + $this->assertTrue([] === $this->redis->zRange('key', 0, -1, true)); $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); @@ -2065,12 +2065,12 @@ public function testZX() { $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); } else { - $this->assertTrue(1 === $this->redis->zAdd('key', array(), 1, 'val1')); // empty options - $this->assertTrue(1 === $this->redis->zAdd('key', array('nx'), 3, 'val3')); // nx option - $this->assertTrue(0 === $this->redis->zAdd('key', array('xx'), 3, 'val3')); // xx option + $this->assertTrue(1 === $this->redis->zAdd('key', [], 1, 'val1')); // empty options + $this->assertTrue(1 === $this->redis->zAdd('key', ['nx'], 3, 'val3')); // nx option + $this->assertTrue(0 === $this->redis->zAdd('key', ['xx'], 3, 'val3')); // xx option } - $this->assertTrue(array('val0', 'val1', 'val2', 'val3', 'val4', 'val5') === $this->redis->zRange('key', 0, -1)); + $this->assertTrue(['val0', 'val1', 'val2', 'val3', 'val4', 'val5'] === $this->redis->zRange('key', 0, -1)); // withscores $ret = $this->redis->zRange('key', 0, -1, true); @@ -2087,7 +2087,7 @@ public function testZX() { $this->assertTrue(1 === $this->redis->zRem('key', 'val4')); $this->assertTrue(1 === $this->redis->zRem('key', 'val5')); - $this->assertTrue(array('val0', 'val1', 'val2') === $this->redis->zRange('key', 0, -1)); + $this->assertTrue(['val0', 'val1', 'val2'] === $this->redis->zRange('key', 0, -1)); // zGetReverseRange @@ -2095,34 +2095,34 @@ public function testZX() { $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); - $this->assertTrue(array('val0', 'val1', 'val2', 'aal3', 'val3') === $zero_to_three || array('val0', 'val1', 'val2', 'val3', 'aal3') === $zero_to_three); + $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', 3, 0); - $this->assertTrue(array_reverse(array('val0', 'val1', 'val2', 'aal3', 'val3')) === $three_to_zero || array_reverse(array('val0', 'val1', 'val2', 'val3', 'aal3')) === $three_to_zero); + $this->assertTrue(array_reverse(['val0', 'val1', 'val2', 'aal3', 'val3']) === $three_to_zero || array_reverse(['val0', 'val1', 'val2', 'val3', 'aal3']) === $three_to_zero); $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); // withscores $this->redis->zRem('key', 'aal3'); - $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE)); - $this->assertTrue(array('val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3) == $zero_to_three); + $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]); + $this->assertTrue(['val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3] == $zero_to_three); $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); // limit - $this->assertTrue(array('val0') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 1)))); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(0, 2)))); - $this->assertTrue(array('val1', 'val2') === $this->redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 2)))); - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => array(0, 100)))); + $this->assertTrue(['val0'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 1]])); + $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 2]])); + $this->assertTrue(['val1', 'val2'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]])); + $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]])); // limits as references - $limit = array(0, 100); + $limit = [0, 100]; foreach ($limit as &$val) {} - $this->assertTrue(array('val0', 'val1') === $this->redis->zRangeByScore('key', 0, 1, array('limit' => $limit))); + $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => $limit])); - $this->assertTrue(array('val3') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 1)))); - $this->assertTrue(array('val3', 'val2') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(0, 2)))); - $this->assertTrue(array('val2', 'val1') === $this->redis->zRevRangeByScore('key', 3, 0, array('limit' => array(1, 2)))); - $this->assertTrue(array('val1', 'val0') === $this->redis->zRevRangeByScore('key', 1, 0, array('limit' => array(0, 100)))); + $this->assertTrue(['val3'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 1]])); + $this->assertTrue(['val3', 'val2'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 2]])); + $this->assertTrue(['val2', 'val1'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]])); + $this->assertTrue(['val1', 'val0'] === $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]])); $this->assertTrue(4 === $this->redis->zCard('key')); $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); @@ -2135,10 +2135,10 @@ public function testZX() { $this->redis->zAdd('zset', 2, 'bar'); $this->redis->zAdd('zset', 3, 'biz'); $this->redis->zAdd('zset', 4, 'foz'); - $this->assertTrue(array('foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4) == $this->redis->zRangeByScore('zset', '-inf', '+inf', array('withscores' => TRUE))); - $this->assertTrue(array('foo' => 1, 'bar' => 2) == $this->redis->zRangeByScore('zset', 1, 2, array('withscores' => TRUE))); - $this->assertTrue(array('bar' => 2) == $this->redis->zRangeByScore('zset', '(1', 2, array('withscores' => TRUE))); - $this->assertTrue(array() == $this->redis->zRangeByScore('zset', '(1', '(2', array('withscores' => TRUE))); + $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', 1, 2, ['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(4 == $this->redis->zCount('zset', '-inf', '+inf')); $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); @@ -2168,26 +2168,26 @@ public function testZX() { $this->redis->zAdd('{zset}3', 4, 'val4'); $this->redis->zAdd('{zset}3', 5, 'val5'); - $this->assertTrue(4 === $this->redis->zUnionStore('{zset}U', array('{zset}1', '{zset}3'))); - $this->assertTrue(array('val0', 'val1', 'val4', 'val5') === $this->redis->zRange('{zset}U', 0, -1)); + $this->assertTrue(4 === $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(0 === $this->redis->zUnionStore('{zset}U', array('{zset}X', '{zset}Y'))); - $this->assertTrue(array() === $this->redis->zRange('{zset}U', 0, -1)); + $this->assertTrue(0 === $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(2 === $this->redis->zUnionStore('{zset}U', array('{zset}1', '{zset}X'))); + $this->assertTrue(2 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}X'])); // test weighted zUnion $this->redis->del('{zset}Z'); - $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', array('{zset}1', '{zset}2'), array(1, 1))); - $this->assertTrue(array('val0', 'val1', 'val2', 'val3') === $this->redis->zRange('{zset}Z', 0, -1)); + $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [1, 1])); + $this->assertTrue(['val0', 'val1', 'val2', 'val3'] === $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->zRemRangeByScore('{zset}Z', 0, 10); - $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', array('{zset}1', '{zset}2'), array(5, 1))); - $this->assertTrue(array('val0', 'val2', 'val3', 'val1') === $this->redis->zRange('{zset}Z', 0, -1)); + $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [5, 1])); + $this->assertTrue(['val0', 'val2', 'val3', 'val1'] === $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); @@ -2196,12 +2196,12 @@ public function testZX() { //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', array('{zset}1','{zset}2'), array(1,1), 'MIN'); + $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', array('{zset}1','{zset}2'), null, 'MIN'); + $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'); @@ -2214,7 +2214,7 @@ public function testZX() { $this->redis->zadd('{zset}2', 2, 'two'); $this->redis->zadd('{zset}2', 3, 'three'); - $this->assertTrue($this->redis->zUnionStore('{zset}3', array('{zset}1', '{zset}2'), array(2, 3.0)) === 3); + $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [2, 3.0]) === 3); $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); @@ -2225,17 +2225,17 @@ public function testZX() { $this->redis->zadd('{zset}2', 3, 'three', 4, 'four', 5, 'five'); // Make sure phpredis handles these weights - $this->assertTrue($this->redis->zUnionStore('{zset}3', array('{zset}1','{zset}2'), array(1, 'inf')) === 5); - $this->assertTrue($this->redis->zUnionStore('{zset}3', array('{zset}1','{zset}2'), array(1, '-inf')) === 5); - $this->assertTrue($this->redis->zUnionStore('{zset}3', array('{zset}1','{zset}2'), array(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); + $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 = Array('inf','-inf','+inf'); + $arr_weights = ['inf','-inf','+inf']; foreach($arr_weights as $str_weight) { - $r = $this->redis->zUnionStore('{zset}3', array('{zset}1','{zset}2'), array(1,$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',array('withscores'=>true)); + $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'])); @@ -2251,7 +2251,7 @@ public function testZX() { $this->assertTrue(count($ret) === 3); $retValues = array_keys($ret); - $this->assertTrue(array('one', 'two', 'three') === $retValues); + $this->assertTrue(['one', 'two', 'three'] === $retValues); // + 0 converts from string to float OR integer $this->assertTrue(is_float($ret['one'] + 0)); @@ -2265,7 +2265,7 @@ public function testZX() { $this->redis->zAdd('{zset}1', 2, 'two'); $this->redis->zAdd('{zset}1', 3, 'three'); $this->assertTrue(2 === $this->redis->zremrangebyrank('{zset}1', 0, 1)); - $this->assertTrue(array('three' => 3) == $this->redis->zRange('{zset}1', 0, -1, TRUE)); + $this->assertTrue(['three' => 3] == $this->redis->zRange('{zset}1', 0, -1, TRUE)); $this->redis->del('{zset}1'); @@ -2282,16 +2282,16 @@ public function testZX() { $this->redis->zAdd('{zset}3', 5, 'val5'); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2'))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertTrue(2 === $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(0 === $this->redis->zInterStore('{zset}X', array('{zset}X', '{zset}Y'))); - $this->assertTrue(array() === $this->redis->zRange('{zset}X', 0, -1)); + $this->assertTrue(0 === $this->redis->zInterStore('{zset}X', ['{zset}X', '{zset}Y'])); + $this->assertTrue([] === $this->redis->zRange('{zset}X', 0, -1)); // !Exist U Exist - $this->assertTrue(0 === $this->redis->zInterStore('{zset}Y', array('{zset}1', '{zset}X'))); - $this->assertTrue(array() === $this->redis->zRange('keyY', 0, -1)); + $this->assertTrue(0 === $this->redis->zInterStore('{zset}Y', ['{zset}1', '{zset}X'])); + $this->assertTrue([] === $this->redis->zRange('keyY', 0, -1)); // test weighted zInter @@ -2311,18 +2311,18 @@ public function testZX() { $this->redis->zAdd('{zset}3', 3, 'val3'); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2'), array(1, 1))); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'], [1, 1])); + $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2', '{zset}3'), array(1, 5, 1), 'min')); - $this->assertTrue(array('val1', 'val3') === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'min')); + $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2', '{zset}3'), array(1, 5, 1), 'max')); - $this->assertTrue(array('val3', 'val1') === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'max')); + $this->assertTrue(['val3', 'val1'] === $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', array('{zset}1', '{zset}2', '{zset}3'), null, 'max')); + $this->assertTrue(2 === $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 @@ -2352,12 +2352,12 @@ public function testZRangeByLex() { $this->redis->zAdd('key', 0, $c); } - $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c'), Array('a', 'b', 'c')); - $this->assertEquals($this->redis->zRangeByLex('key', '(e', '+'), Array('f', 'g')); + $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', 1, 2), Array('b', 'c') ); - $this->assertEquals($this->redis->zRangeByLex('key', '-', '(c', 1, 2), Array('b')); + $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c', 1, 2), ['b', 'c'] ); + $this->assertEquals($this->redis->zRangeByLex('key', '-', '(c', 1, 2), ['b']); } public function testZLexCount() { @@ -2447,15 +2447,15 @@ public function testHashes() { // keys $keys = $this->redis->hKeys('h'); - $this->assertTrue($keys === array('x', 'y') || $keys === array('y', 'x')); + $this->assertTrue($keys === ['x', 'y'] || $keys === ['y', 'x']); // values $values = $this->redis->hVals('h'); - $this->assertTrue($values === array('a', 'b') || $values === array('b', 'a')); + $this->assertTrue($values === ['a', 'b'] || $values === ['b', 'a']); // keys + values $all = $this->redis->hGetAll('h'); - $this->assertTrue($all === array('x' => 'a', 'y' => 'b') || $all === array('y' => 'b', 'x' => 'a')); + $this->assertTrue($all === ['x' => 'a', 'y' => 'b'] || $all === ['y' => 'b', 'x' => 'a']); // hExists $this->assertTrue(TRUE === $this->redis->hExists('h', 'x')); @@ -2490,41 +2490,41 @@ public function testHashes() { // hmset $this->redis->del('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', array('x' => 123, 'y' => 456, 'z' => 'abc'))); + $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(array('x' => '123', 'y' => '456') === $this->redis->hMget('h', array('x', 'y'))); - $this->assertTrue(array('z' => 'abc') === $this->redis->hMget('h', array('z'))); - $this->assertTrue(array('x' => '123', 't' => FALSE, 'y' => '456') === $this->redis->hMget('h', array('x', 't', 'y'))); - $this->assertFalse(array(123 => 'x') === $this->redis->hMget('h', array(123))); - $this->assertTrue(array(123 => FALSE) === $this->redis->hMget('h', array(123))); + $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', Array(false,NULL,false)) === FALSE); + $this->assertTrue($this->redis->hmget('h', [false,NULL,false]) === FALSE); // Test with some invalid keys mixed in (which should just be ignored) - $this->assertTrue(array('x'=>'123','y'=>'456','z'=>'abc') === $this->redis->hMget('h',Array('x',null,'y','','z',false))); + $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', array(123 => 'x', 'y' => 456))); + $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(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', array('123', 'y'))); + $this->assertTrue([123 => 'x', 'y' => '456'] === $this->redis->hMget('h', ['123', 'y'])); // references - $keys = array(123, 'y'); + $keys = [123, 'y']; foreach ($keys as &$key) {} - $this->assertTrue(array(123 => 'x', 'y' => '456') === $this->redis->hMget('h', $keys)); + $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', array('x' => 0, 'y' => array(), 'z' => new stdclass(), 't' => NULL))); + $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']); @@ -2629,7 +2629,7 @@ public function testMultiExec() { $ret = $this->redis->multi()->get('x')->exec(); // successful transaction - $this->assertTrue($ret === array('42')); + $this->assertTrue($ret === ['42']); } public function testFailedTransactions() { @@ -2651,7 +2651,7 @@ public function testFailedTransactions() { $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === array('44')); // succeeded since we've cancel the WATCH command. + $this->assertTrue($ret === ['44']); // succeeded since we've cancel the WATCH command. } public function testPipeline() { @@ -2701,7 +2701,7 @@ public function testDoublePipeNoOp() { $this->redis->set('pipecount','over9000')->get('pipecount'); $data = $this->redis->exec(); - $this->assertEquals(Array(true,'over9000'), $data); + $this->assertEquals([true,'over9000'], $data); /* Only the first MULTI should be honored */ for ($i = 0; $i < 6; $i++) { @@ -2712,12 +2712,12 @@ public function testDoublePipeNoOp() { $this->redis->set('multicount', 'over9000')->get('multicount'); $data = $this->redis->exec(); - $this->assertEquals(Array(true, 'over9000'), $data); + $this->assertEquals([true, 'over9000'], $data); } public function testDiscard() { - foreach (Array(Redis::PIPELINE, Redis::MULTI) as $mode) { + foreach ([Redis::PIPELINE, Redis::MULTI] as $mode) { /* start transaction */ $this->redis->multi($mode); @@ -2815,8 +2815,8 @@ protected function sequence($mode) { $this->redis->del('key'); $ret = $this->redis->multi($mode) ->ttl('key') - ->mget(array('{key}1', '{key}2', '{key}3')) - ->mset(array('{key}3' => 'value3', 'key4' => 'value4')) + ->mget(['{key}1', '{key}2', '{key}3']) + ->mset(['{key}3' => 'value3', 'key4' => 'value4']) ->set('key', 'value') ->expire('key', 5) ->ttl('key') @@ -2827,7 +2827,7 @@ protected function sequence($mode) { $i = 0; $ttl = $ret[$i++]; $this->assertTrue($ttl === -1 || $ttl === -2); - $this->assertTrue($ret[$i++] === array('val1', 'valX', FALSE)); // mget + $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 @@ -2870,15 +2870,15 @@ protected function sequence($mode) { $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++] === array('lvalue')); // lDest contains only that one element. + $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++] === array("lvalue")); // this is the current list. + $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++] === array("lvalue")); // this is the current list. + $this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list. $this->assertTrue($ret[$i++] === 1); // 1 element left $this->assertTrue(count($ret) == $i); @@ -2899,7 +2899,7 @@ protected function sequence($mode) { $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++] === array('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); @@ -2975,8 +2975,8 @@ protected function sequence($mode) { // ttl, mget, mset, msetnx, expire, expireAt $ret = $this->redis->multi($mode) ->ttl('key') - ->mget(array('{key}1', '{key}2', '{key}3')) - ->mset(array('{key}3' => 'value3', '{key}4' => 'value4')) + ->mget(['{key}1', '{key}2', '{key}3']) + ->mset(['{key}3' => 'value3', '{key}4' => 'value4']) ->set('key', 'value') ->expire('key', 5) ->ttl('key') @@ -3027,15 +3027,15 @@ protected function sequence($mode) { $this->assertTrue($ret[$i++] === 5); // 5 values $this->assertTrue($ret[$i++] === 6); // 6 values $this->assertTrue($ret[$i++] === 'lvalue'); - $this->assertTrue($ret[$i++] === array('lvalue')); // 1 value only in lDest + $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++] === array('lvalue')); // 1 value only in lkey + $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++] === array('lvalue')); // the previous error didn't touch anything. + $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); @@ -3089,34 +3089,34 @@ protected function sequence($mode) { $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(array('sValue1', 'sValue3') as $k) { // sKey1 contains sValue1 and sValue3. + foreach(['sValue1', 'sValue3'] as $k) { // sKey1 contains sValue1 and sValue3. $this->assertTrue(in_array($k, $ret[$i])); } $this->assertTrue(count($ret[$i++]) === 2); - foreach(array('sValue1', 'sValue2', 'sValue4') as $k) { // sKey2 contains sValue1, sValue2, and sValue4. + 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++] === array('sValue1')); // intersection + $this->assertTrue($ret[$i++] === ['sValue1']); // intersection $this->assertTrue($ret[$i++] === 1); // intersection + store → 1 value in the destination set. - $this->assertTrue($ret[$i++] === array('sValue1')); // sinterstore destination contents + $this->assertTrue($ret[$i++] === ['sValue1']); // sinterstore destination contents - foreach(array('sValue1', 'sValue2', 'sValue4') as $k) { // (skeydest U sKey2) contains sValue1, sValue2, and sValue4. + 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(array('sValue1', 'sValue2', 'sValue4') as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4. + 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++] === array('sValue3')); // diff skey1, skey2 : only sValue3 is not shared. + $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++] === array('sValue3')); // contents of sDiffDest + $this->assertTrue($ret[$i++] === ['sValue3']); // contents of sDiffDest - $this->assertTrue(in_array($ret[$i++], array('sValue1', 'sValue2', 'sValue4'))); // we removed an element from sKey2 + $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); @@ -3143,11 +3143,11 @@ protected function sequence($mode) { ->zScore('{z}key1', 'zValue15') ->zadd('{z}key2', 5, 'zValue5') ->zadd('{z}key2', 2, 'zValue2') - ->zInterStore('{z}Inter', array('{z}key1', '{z}key2')) + ->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', array('{z}key1', '{z}key2')) + ->zUnionStore('{z}Union', ['{z}key1', '{z}key2']) ->zRange('{z}Union', 0, -1) ->zadd('{z}key5', 5, 'zValue5') ->zIncrBy('{z}key5', 3, 'zValue5') // fix this @@ -3161,28 +3161,28 @@ protected function sequence($mode) { $this->assertTrue($ret[$i++] === 1); $this->assertTrue($ret[$i++] === 1); $this->assertTrue($ret[$i++] === 1); - $this->assertTrue($ret[$i++] === array('zValue1', 'zValue2', 'zValue5')); + $this->assertTrue($ret[$i++] === ['zValue1', 'zValue2', 'zValue5']); $this->assertTrue($ret[$i++] === 1); - $this->assertTrue($ret[$i++] === array('zValue1', 'zValue5')); + $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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); - $this->assertTrue($ret[$i++] === array('zValue15', 'zValue14', 'zValue5', 'zValue1')); - $this->assertTrue($ret[$i++] === array('zValue1', 'zValue5')); + $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++] === array('zValue1', 'zValue5', 'zValue14', 'zValue15')); // {z}key1 contents - $this->assertTrue($ret[$i++] === array('zValue2', 'zValue5')); // {z}key2 contents - $this->assertTrue($ret[$i++] === array('zValue5')); // {z}inter contents + $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++] === array('zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15')); // {z}Union contents + $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. @@ -3196,7 +3196,7 @@ protected function sequence($mode) { ->hset('hkey1', 'key1', 'value1') ->hset('hkey1', 'key2', 'value2') ->hset('hkey1', 'key3', 'value3') - ->hmget('hkey1', array('key1', 'key2', 'key3')) + ->hmget('hkey1', ['key1', 'key2', 'key3']) ->hget('hkey1', 'key1') ->hlen('hkey1') ->hdel('hkey1', 'key2') @@ -3216,15 +3216,15 @@ protected function sequence($mode) { $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++] === array('key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3')); // hmget, 3 elements + $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] === array('key1', 'key3') || $ret[$i] === array('key3', 'key1')); $i++; // hkeys - $this->assertTrue($ret[$i] === array('value1', 'value3') || $ret[$i] === array('value3', 'value1')); $i++; // hvals - $this->assertTrue($ret[$i] === array('key1' => 'value1', 'key3' => 'value3') || $ret[$i] === array('key3' => 'value3', 'key1' => 'value1')); $i++; // hgetall + $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 @@ -3254,7 +3254,7 @@ protected function sequence($mode) { ->zscore('test', "2") ->exec(); - $this->assertTrue($result === array(1.0, FALSE, FALSE, 2.0)); + $this->assertTrue($result === [1.0, FALSE, FALSE, 2.0]); } protected function differentType($mode) { @@ -3314,8 +3314,8 @@ protected function differentType($mode) { // hash I/F ->hSet($key, 'key1', 'value1') ->hGet($key, 'key1') - ->hMGet($key, array('key1')) - ->hMSet($key, array('key1' => 'value1')) + ->hMGet($key, ['key1']) + ->hMSet($key, ['key1' => 'value1']) ->hIncrBy($key, 'key2', 1) ->hExists($key, 'key2') ->hDel($key, 'key2') @@ -3396,7 +3396,7 @@ protected function differentType($mode) { ->getset($key, 'value2') ->append($key, 'append') ->getRange($key, 0, 8) - ->mget(array($key)) + ->mget([$key]) ->incr($key) ->incrBy($key, 1) ->decr($key) @@ -3433,8 +3433,8 @@ protected function differentType($mode) { // hash I/F ->hSet($key, 'key1', 'value1') ->hGet($key, 'key1') - ->hMGet($key, array('key1')) - ->hMSet($key, array('key1' => 'value1')) + ->hMGet($key, ['key1']) + ->hMSet($key, ['key1' => 'value1']) ->hIncrBy($key, 'key2', 1) ->hExists($key, 'key2') ->hDel($key, 'key2') @@ -3513,7 +3513,7 @@ protected function differentType($mode) { ->getset($key, 'value2') ->append($key, 'append') ->getRange($key, 0, 8) - ->mget(array($key)) + ->mget([$key]) ->incr($key) ->incrBy($key, 1) ->decr($key) @@ -3551,8 +3551,8 @@ protected function differentType($mode) { // hash I/F ->hSet($key, 'key1', 'value1') ->hGet($key, 'key1') - ->hMGet($key, array('key1')) - ->hMSet($key, array('key1' => 'value1')) + ->hMGet($key, ['key1']) + ->hMSet($key, ['key1' => 'value1']) ->hIncrBy($key, 'key2', 1) ->hExists($key, 'key2') ->hDel($key, 'key2') @@ -3632,7 +3632,7 @@ protected function differentType($mode) { ->getset($key, 'value2') ->append($key, 'append') ->getRange($key, 0, 8) - ->mget(array($key)) + ->mget([$key]) ->incr($key) ->incrBy($key, 1) ->decr($key) @@ -3668,8 +3668,8 @@ protected function differentType($mode) { // hash I/F ->hSet($key, 'key1', 'value1') ->hGet($key, 'key1') - ->hMGet($key, array('key1')) - ->hMSet($key, array('key1' => 'value1')) + ->hMGet($key, ['key1']) + ->hMSet($key, ['key1' => 'value1']) ->hIncrBy($key, 'key2', 1) ->hExists($key, 'key2') ->hDel($key, 'key2') @@ -3747,7 +3747,7 @@ protected function differentType($mode) { ->getset($key, 'value2') ->append($key, 'append') ->getRange($key, 0, 8) - ->mget(array($key)) + ->mget([$key]) ->incr($key) ->incrBy($key, 1) ->decr($key) @@ -3907,8 +3907,8 @@ public function testDifferentTypeString() { // 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, array('key1'))); - $this->assertEquals(FALSE, $this->redis->hMSet($key, array('key1' => 'value1'))); + $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')); @@ -3930,7 +3930,7 @@ public function testDifferentTypeList() { $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); $this->assertEquals(FALSE, $this->redis->append($key, 'append')); $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); - $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals([FALSE], $this->redis->mget([$key])); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); $this->assertEquals(FALSE, $this->redis->decr($key)); @@ -3967,8 +3967,8 @@ public function testDifferentTypeList() { // 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, array('key1'))); - $this->assertEquals(FALSE, $this->redis->hMSet($key, array('key1' => 'value1'))); + $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')); @@ -3989,7 +3989,7 @@ public function testDifferentTypeSet() { $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); $this->assertEquals(FALSE, $this->redis->append($key, 'append')); $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); - $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals([FALSE], $this->redis->mget([$key])); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); $this->assertEquals(FALSE, $this->redis->decr($key)); @@ -4027,8 +4027,8 @@ public function testDifferentTypeSet() { // 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, array('key1'))); - $this->assertEquals(FALSE, $this->redis->hMSet($key, array('key1' => 'value1'))); + $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')); @@ -4050,7 +4050,7 @@ public function testDifferentTypeSortedSet() { $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); $this->assertEquals(FALSE, $this->redis->append($key, 'append')); $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); - $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals([FALSE], $this->redis->mget([$key])); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); $this->assertEquals(FALSE, $this->redis->decr($key)); @@ -4086,8 +4086,8 @@ public function testDifferentTypeSortedSet() { // 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, array('key1'))); - $this->assertEquals(FALSE, $this->redis->hMSet($key, array('key1' => 'value1'))); + $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')); @@ -4109,7 +4109,7 @@ public function testDifferentTypeHash() { $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); $this->assertEquals(FALSE, $this->redis->append($key, 'append')); $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); - $this->assertEquals(array(FALSE), $this->redis->mget(array($key))); + $this->assertEquals([FALSE], $this->redis->mget([$key])); $this->assertEquals(FALSE, $this->redis->incr($key)); $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); $this->assertEquals(FALSE, $this->redis->decr($key)); @@ -4187,7 +4187,7 @@ private function checkSerializer($mode) { $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === $mode); // get ok // lPush, rPush - $a = array('hello world', 42, TRUE, array('' => 1729)); + $a = ['hello world', 42, TRUE, ['' => 1729]]; $this->redis->del('key'); $this->redis->lPush('key', $a[0]); $this->redis->rPush('key', $a[1]); @@ -4208,20 +4208,20 @@ private function checkSerializer($mode) { $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1)); // lSet - $a[0] = array('k' => 'v'); // update + $a[0] = ['k' => 'v']; // update $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); // lInsert - $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], array(1,2,3)) === 4); - $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], array(4,5,6)) === 5); + $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 = array(array(1,2,3), $a[0], array(4,5,6), $a[1], $a[2]); + $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 = array(1,'a', array(1,2,3), array('k' => 'v')); + $s = [1,'a', [1,2,3], ['k' => 'v']]; $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[0])); $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[1])); @@ -4259,7 +4259,7 @@ private function checkSerializer($mode) { unset($s[0]); // sorted sets - $z = array('z0', array('k' => 'v'), FALSE, NULL); + $z = ['z0', ['k' => 'v'], FALSE, NULL]; $this->redis->del('key'); // zAdd @@ -4283,7 +4283,7 @@ private function checkSerializer($mode) { $this->redis->zAdd('k', 2, 'c'); $this->assertTrue(2 === $this->redis->zRem('k', 'a', 'c')); $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); - $this->assertTrue($this->redis->zRange('k', 0, -1, true) == array('b' => 1.0)); + $this->assertTrue($this->redis->zRange('k', 0, -1, true) == ['b' => 1.0]); // zRange $this->assertTrue($z === $this->redis->zRange('key', 0, -1)); @@ -4314,13 +4314,13 @@ private function checkSerializer($mode) { $this->assertTrue(2.0 === $this->redis->zScore('key', $z[2])); // mset - $a = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); + $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 = array('k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => array('a' => 'b')); + $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']]; // hSet $this->redis->del('key'); @@ -4358,9 +4358,9 @@ private function checkSerializer($mode) { $this->redis->set('a', NULL); $this->redis->set('b', FALSE); $this->redis->set('c', 42); - $this->redis->set('d', array('x' => 'y')); + $this->redis->set('d', ['x' => 'y']); - $this->assertTrue(array(NULL, FALSE, 42, array('x' => 'y')) === $this->redis->mGet(array('a', 'b', 'c', 'd'))); + $this->assertTrue([NULL, FALSE, 42, ['x' => 'y']] === $this->redis->mGet(['a', 'b', 'c', 'd'])); // pipeline if ($this->havePipeline()) { @@ -4383,7 +4383,7 @@ private function checkSerializer($mode) { $this->assertTrue($data['session_id'] === 'test 2'); // issue #145, serializer with objects. - $this->redis->set('x', array(new stdClass, new stdClass)); + $this->redis->set('x', [new stdClass, new stdClass]); $x = $this->redis->get('x'); $this->assertTrue(is_array($x)); $this->assertTrue(is_object($x[0]) && get_class($x[0]) === 'stdClass'); @@ -4459,7 +4459,7 @@ public function testGetLastError() { // Helper function to compare nested results -- from the php.net array_diff page, I believe private function array_diff_recursive($aArray1, $aArray2) { - $aReturn = array(); + $aReturn = []; foreach ($aArray1 as $mKey => $mValue) { if (array_key_exists($mKey, $aArray2)) { @@ -4544,17 +4544,17 @@ public function testEval() { $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)", Array('{eval-key}-list'), 1); - $this->assertTrue($list === Array('a','b','c')); + $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)", Array('{eval-key}-zset'), 1); - $this->assertTrue($zset == Array('d','e','f')); + $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)", - Array('{eval-key}-nolist'), 1); + ['{eval-key}-nolist'], 1); $this->assertTrue(is_array($empty_resp) && empty($empty_resp)); // Now test a nested reply @@ -4572,20 +4572,20 @@ public function testEval() { } "; - $expected = Array( - 1, 2, 3, Array( + $expected = [ + 1, 2, 3, [ 'hello, world', 'hello again!', - Array(), - Array( - Array('d','e','f'), - Array('a','b','c') - ) - ) - ); + [], + [ + ['d','e','f'], + ['a','b','c'] + ] + ] + ]; // Now run our script, and check our values against each other - $eval_result = $this->redis->eval($nested_script, Array('{eval-key}-str1', '{eval-key}-str2', '{eval-key}-zset', '{eval-key}-list'), 4); + $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); /* @@ -4594,13 +4594,13 @@ public function testEval() { $num_scripts = 10; - $arr_modes = Array(Redis::MULTI); + $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, Array('{eval-key}-dummy'), 1); + $this->redis->eval($nested_script, ['{eval-key}-dummy'], 1); } $replies = $this->redis->exec(); @@ -4614,7 +4614,7 @@ public function testEval() { */ $args_script = "return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}"; - $args_args = Array('{k}1','{k}2','{k}3','v1','v2','v3'); + $args_args = ['{k}1','{k}2','{k}3','v1','v2','v3']; $args_result = $this->redis->eval($args_script, $args_args, 3); $this->assertTrue($args_result === $args_args); @@ -4658,22 +4658,22 @@ public function testEvalSHA() { } public function testSerialize() { - $vals = Array(1, 1.5, 'one', Array('here','is','an','array')); + $vals = [1, 1.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()) === 'Array'); + $this->assertTrue($this->redis->_serialize([]) === 'Array'); $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object'); - $arr_serializers = Array(Redis::SERIALIZER_PHP); + $arr_serializers = [Redis::SERIALIZER_PHP]; if(defined('Redis::SERIALIZER_IGBINARY')) { $arr_serializers[] = Redis::SERIALIZER_IGBINARY; } foreach($arr_serializers as $mode) { - $arr_enc = Array(); - $arr_dec = Array(); + $arr_enc = []; + $arr_dec = []; foreach($vals as $k => $v) { $enc = $this->redis->_serialize($v); @@ -4686,17 +4686,17 @@ public function testSerialize() { } public function testUnserialize() { - $vals = Array( - 1,1.5,'one',Array('this','is','an','array') - ); + $vals = [ + 1,1.5,'one',['this','is','an','array'] + ]; - $serializers = Array(Redis::SERIALIZER_PHP); + $serializers = [Redis::SERIALIZER_PHP]; if(defined('Redis::SERIALIZER_IGBINARY')) { $serializers[] = Redis::SERIALIZER_IGBINARY; } foreach($serializers as $mode) { - $vals_enc = Array(); + $vals_enc = []; // Pass them through redis so they're serialized foreach($vals as $key => $val) { @@ -4982,7 +4982,7 @@ public function testZScan() { // protected function createPFKey($str_key, $i_count) { - $arr_mems = Array(); + $arr_mems = []; for($i=0;$i<$i_count;$i++) { $arr_mems[] = uniqid() . '-' . $i; } @@ -4999,7 +4999,7 @@ public function testPFCommands() { } $str_uniq = uniqid(); - $arr_mems = Array(); + $arr_mems = []; for($i=0;$i<1000;$i++) { if($i%2 == 0) { @@ -5013,9 +5013,9 @@ public function testPFCommands() { $i_keys = 10; // Iterate prefixing/serialization options - foreach(Array(Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP) as $str_ser) { - foreach(Array('', 'hl-key-prefix:') as $str_prefix) { - $arr_keys = Array(); + foreach([Redis::SERIALIZER_NONE, Redis::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++) { @@ -5062,7 +5062,7 @@ public function testPFCommands() { // protected function rawCommandArray($key, $args) { - return call_user_func_array(Array($this->redis, 'rawCommand'), $args); + return call_user_func_array([$this->redis, 'rawCommand'], $args); } protected function addCities($key) { @@ -5086,13 +5086,13 @@ public function testGeoAdd() { } /* Add them again, all at once */ - $args = Array('geokey'); + $args = ['geokey']; foreach ($this->cities as $city => $longlat) { - $args = array_merge($args, Array($longlat[0], $longlat[1], $city)); + $args = array_merge($args, [$longlat[0], $longlat[1], $city]); } /* They all exist, should be nothing added */ - $this->assertEquals(call_user_func_array(Array($this->redis, 'geoadd'), $args), 0); + $this->assertEquals(call_user_func_array([$this->redis, 'geoadd'], $args), 0); } /* GEORADIUS */ @@ -5110,35 +5110,35 @@ public function genericGeoRadiusTest($cmd) { /* Pre tested with redis-cli. We're just verifying proper delivery of distance and unit */ if ($cmd == 'georadius') { - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array('georadius', '{gk}', $lng, $lat, 500, 'mi'); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 10, 'mi'), ['Chico']); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 30, 'mi'), ['Gridley','Chico']); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 50, 'km'), ['Gridley','Chico']); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 50000, 'm'), ['Gridley','Chico']); + $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 150000, 'ft'), ['Gridley', 'Chico']); + $args = ['georadius', '{gk}', $lng, $lat, 500, 'mi']; /* Test a bad COUNT argument */ - foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->georadius('{gk}', $lng, $lat, 10, 'mi', Array('count' => $count))); + foreach ([-1, 0, 'notanumber'] as $count) { + $this->assertFalse(@$this->redis->georadius('{gk}', $lng, $lat, 10, 'mi', ['count' => $count])); } } else { - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array('georadiusbymember', '{gk}', $city, 500, 'mi'); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 10, 'mi'), ['Chico']); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 30, 'mi'), ['Gridley','Chico']); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 50, 'km'), ['Gridley','Chico']); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 50000, 'm'), ['Gridley','Chico']); + $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 150000, 'ft'), ['Gridley', 'Chico']); + $args = ['georadiusbymember', '{gk}', $city, 500, 'mi']; /* Test a bad COUNT argument */ - foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->georadiusbymember('{gk}', $city, 10, 'mi', Array('count' => $count))); + foreach ([-1, 0, 'notanumber'] as $count) { + $this->assertFalse(@$this->redis->georadiusbymember('{gk}', $city, 10, 'mi', ['count' => $count])); } } /* Options */ - $opts = Array('WITHCOORD', 'WITHDIST', 'WITHHASH'); - $sortopts = Array('', 'ASC', 'DESC'); - $storeopts = Array('', 'STORE', 'STOREDIST'); + $opts = ['WITHCOORD', 'WITHDIST', 'WITHHASH']; + $sortopts = ['', 'ASC', 'DESC']; + $storeopts = ['', 'STORE', 'STOREDIST']; for ($i = 0; $i < count($opts); $i++) { $subopts = array_slice($opts, 0, $i); @@ -5150,7 +5150,7 @@ public function genericGeoRadiusTest($cmd) { } /* Cannot mix STORE[DIST] with the WITH* arguments */ - $realstoreopts = count($subopts) == 0 ? $storeopts : Array(); + $realstoreopts = count($subopts) == 0 ? $storeopts : []; $base_subargs = $subargs; $base_subopts = $subopts; @@ -5221,8 +5221,8 @@ public function testGeoPos() { } $this->addCities('gk'); - $this->assertEquals($this->redis->geopos('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', Array('geopos', 'gk', 'Chico', 'Sacramento'))); - $this->assertEquals($this->redis->geopos('gk', 'Cupertino'), $this->rawCommandArray('gk', Array('geopos', 'gk', 'Cupertino'))); + $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() { @@ -5231,8 +5231,8 @@ public function testGeoHash() { } $this->addCities('gk'); - $this->assertEquals($this->redis->geohash('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', Array('geohash', 'gk', 'Chico', 'Sacramento'))); - $this->assertEquals($this->redis->geohash('gk', 'Chico'), $this->rawCommandArray('gk', Array('geohash', 'gk', 'Chico'))); + $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() { @@ -5243,11 +5243,11 @@ public function testGeoDist() { $this->addCities('gk'); $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino'); - $r2 = $this->rawCommandArray('gk', Array('geodist', 'gk', 'Chico', 'Cupertino')); + $r2 = $this->rawCommandArray('gk', ['geodist', 'gk', 'Chico', 'Cupertino']); $this->assertEquals(round($r1, 8), round($r2, 8)); $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino', 'km'); - $r2 = $this->rawCommandArray('gk', Array('geodist', 'gk', 'Chico', 'Cupertino', 'km')); + $r2 = $this->rawCommandArray('gk', ['geodist', 'gk', 'Chico', 'Cupertino', 'km']); $this->assertEquals(round($r1, 8), round($r2, 8)); } @@ -5259,25 +5259,25 @@ public function testRawCommand() { $this->redis->del('mylist'); $this->redis->rpush('mylist', 'A', 'B', 'C', 'D'); - $this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D')); + $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']); } /* STREAMS */ protected function addStreamEntries($key, $count) { - $ids = Array(); + $ids = []; $this->redis->del($key); for ($i = 0; $i < $count; $i++) { - $ids[] = $this->redis->xAdd($key, '*', Array('field' => "value:$i")); + $ids[] = $this->redis->xAdd($key, '*', ['field' => "value:$i"]); } return $ids; } protected function addStreamsAndGroups($arr_streams, $count, $arr_groups) { - $ids = Array(); + $ids = []; foreach ($arr_streams as $str_stream) { $ids[$str_stream] = $this->addStreamEntries($str_stream, $count); @@ -5295,7 +5295,7 @@ public function testXAdd() { $this->redis->del('stream'); for ($i = 0; $i < 5; $i++) { - $id = $this->redis->xAdd("stream", '*', Array('k1' => 'v1', 'k2' => 'v2')); + $id = $this->redis->xAdd("stream", '*', ['k1' => 'v1', 'k2' => 'v2']); $this->assertEquals($this->redis->xLen('stream'), $i+1); /* Redis should return - */ @@ -5307,32 +5307,32 @@ public function testXAdd() { /* Test an absolute maximum length */ for ($i = 0; $i < 100; $i++) { - $this->redis->xAdd('stream', '*', Array('k' => 'v'), 10); + $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', '*', Array('k' => 'v'), 10, true); + $id = $this->redis->xAdd('stream', '*', ['k' => 'v'], 10, true); $this->assertEquals(count(explode('-', $id)), 2); /* Empty message should fail */ - $this->redis->xAdd('stream', '*', Array()); + $this->redis->xAdd('stream', '*', []); } protected function doXRangeTest($reverse) { $key = '{stream}'; if ($reverse) { - list($cmd,$a1,$a2) = Array('xRevRange', '+', 0); + list($cmd,$a1,$a2) = ['xRevRange', '+', 0]; } else { - list($cmd,$a1,$a2) = Array('xRange', 0, '+'); + list($cmd,$a1,$a2) = ['xRange', 0, '+']; } $this->redis->del($key); for ($i = 0; $i < 3; $i++) { - $msg = Array('field' => "value:$i"); + $msg = ['field' => "value:$i"]; $id = $this->redis->xAdd($key, '*', $msg); $rows[$id] = $msg; } @@ -5343,7 +5343,7 @@ protected function doXRangeTest($reverse) { $i = $reverse ? 2 : 0; foreach ($messages as $seq => $v) { $this->assertEquals(count(explode('-', $seq)), 2); - $this->assertEquals($v, Array('field' => "value:$i")); + $this->assertEquals($v, ['field' => "value:$i"]); $i += $reverse ? -1 : 1; } @@ -5358,9 +5358,9 @@ public function testXRange() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); - foreach (Array(false, true) as $reverse) { + foreach ([false, true] as $reverse) { foreach ($this->serializers as $serializer) { - foreach (Array(NULL, 'prefix:') as $prefix) { + foreach ([NULL, 'prefix:'] as $prefix) { $this->redis->setOption(Redis::OPT_PREFIX, $prefix); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $this->doXRangeTest($reverse); @@ -5375,7 +5375,7 @@ protected function testXLen() { $this->redis->del('{stream}'); for ($i = 0; $i < 5; $i++) { - $this->redis->xadd('{stream}', '*', Array('foo' => 'bar')); + $this->redis->xadd('{stream}', '*', ['foo' => 'bar']); $this->assertEquals($this->redis->xLen('{stream}'), $i+1); } } @@ -5415,8 +5415,8 @@ public function testXAck() { return $this->markTestSkipped(); for ($n = 1; $n <= 3; $n++) { - $this->addStreamsAndGroups(Array('{s}'), 3, Array('g1' => 0)); - $msg = $this->redis->xReadGroup('g1', 'c1', Array('{s}' => '>')); + $this->addStreamsAndGroups(['{s}'], 3, ['g1' => 0]); + $msg = $this->redis->xReadGroup('g1', 'c1', ['{s}' => '>']); /* Extract IDs */ $smsg = array_shift($msg); @@ -5428,18 +5428,18 @@ public function testXAck() { } /* Verify sending no IDs is a failure */ - $this->assertFalse($this->redis->xAck('{s}', 'g1', Array())); + $this->assertFalse($this->redis->xAck('{s}', 'g1', [])); } protected function doXReadTest() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); - $row = Array('f1' => 'v1', 'f2' => 'v2'); - $msgdata = Array( + $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)); @@ -5470,15 +5470,15 @@ protected function doXReadTest() { /* Test against a specific ID */ $id = $this->redis->xAdd('{stream}-1', '*', $row); - $new_id = $this->redis->xAdd('{stream}-1', '*', Array('final' => 'row')); - $rmsg = $this->redis->xRead(Array('{stream}-1' => $id)); + $new_id = $this->redis->xAdd('{stream}-1', '*', ['final' => 'row']); + $rmsg = $this->redis->xRead(['{stream}-1' => $id]); $this->assertEquals( - $this->redis->xRead(Array('{stream}-1' => $id)), - Array('{stream}-1' => Array($new_id => Array('final' => 'row'))) + $this->redis->xRead(['{stream}-1' => $id]), + ['{stream}-1' => [$new_id => ['final' => 'row']]] ); /* Emtpy query should fail */ - $this->assertFalse($this->redis->xRead(Array())); + $this->assertFalse($this->redis->xRead([])); } public function testXRead() { @@ -5492,7 +5492,7 @@ public function testXRead() { /* Don't need to test BLOCK multiple times */ $m1 = round(microtime(true)*1000); - $this->redis->xRead(Array('somestream' => '$'), -1, 100); + $this->redis->xRead(['somestream' => '$'], -1, 100); $m2 = round(microtime(true)*1000); $this->assertTrue($m2 - $m1 >= 100); } @@ -5517,21 +5517,21 @@ public function testXReadGroup() { return $this->markTestSkipped(); /* Create some streams and groups */ - $streams = Array('{s}-1', '{s}-2'); - $groups = Array('g1' => 0, 'g2' => 0); + $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 = Array('{s}-1' => '>', '{s}-2' => '>'); - $query2 = Array('{s}-1' => '0', '{s}-2' => '0'); + $query1 = ['{s}-1' => '>', '{s}-2' => '>']; + $query2 = ['{s}-1' => '0', '{s}-2' => '0']; $ids = $this->addStreamsAndGroups($streams, 1, $groups); /* Test that we get get the IDs we should */ - foreach (Array('g1', 'g2') as $group) { + foreach (['g1', 'g2'] as $group) { foreach ($ids as $stream => $messages) { while ($ids[$stream]) { /* Read more messages */ @@ -5543,7 +5543,7 @@ public function testXReadGroup() { /* Remove a message from our control *and* XACK it in Redis */ $id = array_shift($ids[$stream]); - $this->redis->xAck($stream, $group, Array($id)); + $this->redis->xAck($stream, $group, [$id]); } } } @@ -5560,7 +5560,7 @@ public function testXReadGroup() { /* Finally test BLOCK with a sloppy timing test */ $t1 = $this->mstime(); - $qnew = Array('{s}-1' => '>', '{s}-2' => '>'); + $qnew = ['{s}-1' => '>', '{s}-2' => '>']; $this->redis->xReadGroup('g1', 'c1', $qnew, -1, 100); $t2 = $this->mstime(); $this->assertTrue($t2 - $t1 >= 100); @@ -5572,9 +5572,9 @@ public function testXPending() { } $rows = 5; - $this->addStreamsAndGroups(Array('s'), $rows, Array('group' => 0)); + $this->addStreamsAndGroups(['s'], $rows, ['group' => 0]); - $msg = $this->redis->xReadGroup('group', 'consumer', Array('s' => 0)); + $msg = $this->redis->xReadGroup('group', 'consumer', ['s' => 0]); $ids = array_keys($msg['s']); for ($n = count($ids); $n >= 0; $n--) { @@ -5590,7 +5590,7 @@ public function testXPending() { if ($ids) { $id = array_shift($ids); - $this->redis->xAck('s', 'group', Array($id)); + $this->redis->xAck('s', 'group', [$id]); } } } @@ -5606,7 +5606,7 @@ public function testXDel() { } /* Empty array should fail */ - $this->assertFalse($this->redis->xDel('s', Array())); + $this->assertFalse($this->redis->xDel('s', [])); } public function testXTrim() { @@ -5631,14 +5631,14 @@ public function testXClaim() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); - foreach (Array(0, 100) as $min_idle_time) { - foreach (Array(false, true) as $justid) { - foreach (Array(0, 10) as $retrycount) { + foreach ([0, 100] as $min_idle_time) { + foreach ([false, true] as $justid) { + foreach ([0, 10] as $retrycount) { /* We need to test not passing TIME/IDLE as well as passing either */ if ($min_idle_time == 0) { - $topts = Array(Array(), Array('IDLE', 1000000), Array('TIME', time() * 1000)); + $topts = [[], ['IDLE', 1000000], ['TIME', time() * 1000]]; } else { - $topts = Array(NULL); + $topts = [NULL]; } foreach ($topts as $tinfo) { @@ -5649,18 +5649,18 @@ public function testXClaim() { } /* Add some messages and create a group */ - $this->addStreamsAndGroups(Array('s'), 10, Array('group1' => 0)); + $this->addStreamsAndGroups(['s'], 10, ['group1' => 0]); /* Create a second stream we can FORCE ownership on */ - $fids = $this->addStreamsAndGroups(Array('f'), 10, Array('group1' => 0)); + $fids = $this->addStreamsAndGroups(['f'], 10, ['group1' => 0]); $fids = $fids['f']; /* Have consumer 'Mike' read the messages */ - $oids = $this->redis->xReadGroup('group1', 'Mike', Array('s' => '>')); + $oids = $this->redis->xReadGroup('group1', 'Mike', ['s' => '>']); $oids = array_keys($oids['s']); /* We're only dealing with stream 's' */ /* Construct our options array */ - $opts = Array(); + $opts = []; if ($justid) $opts[] = 'JUSTID'; if ($retrycount) $opts['RETRYCOUNT'] = $retrycount; if ($tvalue !== NULL) $opts[$ttype] = $tvalue; @@ -5700,7 +5700,7 @@ public function testXClaim() { } 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, Array()); + $this->assertEquals($cids, []); } } } @@ -6084,7 +6084,7 @@ private function startSessionProcess($sessionId, $sleepTime, $background, $maxEx $this->markTestSkipped(); return true; } else { - $commandParameters = array($this->getFullHostPath(), $this->sessionSaveHandler, $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData, $sessionLifetime); + $commandParameters = [$this->getFullHostPath(), $this->sessionSaveHandler, $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData, $sessionLifetime]; if ($locking_enabled) { $commandParameters[] = '1'; @@ -6125,7 +6125,7 @@ private function getSessionData($sessionId, $sessionLifetime = 1440) */ private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false, $sessionProxy = false) { - $args = array_map('escapeshellarg', array($sessionId, $locking, $destroyPrevious, $sessionProxy)); + $args = array_map('escapeshellarg', [$sessionId, $locking, $destroyPrevious, $sessionProxy]); $command = self::getPhpCommand('regenerateSessionId.php') . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . implode(' ', $args); diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 4348c7d861..9da9ab8639 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -10,10 +10,10 @@ ini_set( 'display_errors','1'); /* Grab options */ -$arr_args = getopt('', Array('host:', 'class:', 'test:', 'nocolors')); +$arr_args = getopt('', ['host:', 'class:', 'test:', 'nocolors']); /* Grab the test the user is trying to run */ -$arr_valid_classes = Array('redis', 'redisarray', 'rediscluster'); +$arr_valid_classes = ['redis', 'redisarray', 'rediscluster']; $str_class = isset($arr_args['class']) ? strtolower($arr_args['class']) : 'redis'; $boo_colorize = !isset($arr_args['nocolors']); @@ -48,7 +48,7 @@ echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; /* The various RedisArray subtests we can run */ - $arr_ra_tests = Array('Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'); + $arr_ra_tests = ['Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test']; foreach ($arr_ra_tests as $str_test) { /* Run until we encounter a failure */ if (run_tests($str_test, $str_filter, $str_host) != 0) { diff --git a/tests/TestSuite.php b/tests/TestSuite.php index c2d682599c..78fc87290e 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -18,8 +18,8 @@ class TestSuite { private static $YELLOW = "\033[0;33m"; private static $RED = "\033[0;31m"; - public static $errors = array(); - public static $warnings = array(); + public static $errors = []; + public static $warnings = []; public function __construct($str_host) { $this->str_host = $str_host; From a1a854782e4e7f47bd1c8a6faf7160d939643ffd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 9 Feb 2019 14:42:26 -0800 Subject: [PATCH 1077/1986] Add a format specifier. --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index a58b7ee4d6..d3b59d7456 100644 --- a/library.c +++ b/library.c @@ -671,7 +671,7 @@ int redis_cmd_append_sstr_long(smart_string *str, long append) { */ int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) { char nbuf[64]; - int len = snprintf(nbuf, sizeof(nbuf), PRId64, append); + int len = snprintf(nbuf, sizeof(nbuf), "%" PRId64, append); return redis_cmd_append_sstr(str, nbuf, len); } From a08d51fa3eb516458715c29d35d93fdb68f5ed68 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 9 Feb 2019 17:02:15 -0800 Subject: [PATCH 1078/1986] Attach slot cache key and mechanism for invalidation --- cluster_library.c | 11 ++++++++--- redis_cluster.c | 1 - 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 6a8220671c..cc1ef16ebb 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1021,6 +1021,9 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) { /* Iterate over masters */ for (i = 0; i < cc->count; i++) { + /* Attach cache key */ + c->cache_key = cc->hash; + /* Grab the next master */ cm = &cc->master[map[i]]; @@ -1215,8 +1218,10 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type } // Check for MOVED or ASK redirection - if ((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { - // Set our redirection information + if ((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { // Set our redirection information + /* We'll want to invalidate slot cache if we're using one */ + c->redirections++; + if (cluster_set_redirection(c,inbuf,moved) < 0) { return -1; } @@ -1543,7 +1548,7 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, /* Send a command to given slot in our cluster. If we get a MOVED or ASK error * we attempt to send the command to the node as directed. */ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, - int cmd_len TSRMLS_DC) + int cmd_len TSRMLS_DC) { int resp, timedout = 0; long msstart; diff --git a/redis_cluster.c b/redis_cluster.c index 0c6b7cb8d0..e2ea55e3a0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -451,7 +451,6 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time { redisCachedCluster *cc; - /* TODO: Safe short circuit here */ cluster_validate_args(timeout, read_timeout, ht_seeds); if (auth && auth_len > 0) { From b00060ce4d67abc7a407b434b0ccb0b241c12ca8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 11 Feb 2019 09:10:17 +0200 Subject: [PATCH 1079/1986] Don't check the number affected keys in PS_UPDATE_TIMESTAMP_FUNC --- redis_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index f5bb7d67de..8ab393718d 100644 --- a/redis_session.c +++ b/redis_session.c @@ -724,7 +724,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) return FAILURE; } - if (response_len == 2 && response[0] == ':' && response[1] == '1') { + if (response_len == 2 && response[0] == ':') { efree(response); return SUCCESS; } else { From 0653ff3119448f955f7949de58d6f8a2529c8ed2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 11 Feb 2019 09:29:13 +0200 Subject: [PATCH 1080/1986] Add callback parameter to subscribe/psubscribe arginfo. This PR fixes issue #1504. --- common.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index 65b497b610..34c84f5053 100644 --- a/common.h +++ b/common.h @@ -1010,12 +1010,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_object, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_subscribe, 0, 0, 1) +ZEND_BEGIN_ARG_INFO_EX(arginfo_subscribe, 0, 0, 2) ZEND_ARG_ARRAY_INFO(0, channels, 0) + ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_psubscribe, 0, 0, 1) +ZEND_BEGIN_ARG_INFO_EX(arginfo_psubscribe, 0, 0, 2) ZEND_ARG_ARRAY_INFO(0, patterns, 0) + ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_unsubscribe, 0, 0, 1) From 7bc845e8835fdbf14193efe9f076b738082852d5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 11 Feb 2019 09:38:56 +0200 Subject: [PATCH 1081/1986] Add documentation about async parameter for flushAll/flushDb. This commit fixes issue #1503. --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index dd48a42c46..3ea45ba66f 100644 --- a/README.markdown +++ b/README.markdown @@ -455,7 +455,7 @@ echo "Redis has $count keys\n"; _**Description**_: Remove all keys from all databases. ##### *Parameters* -None. +*async* (bool) requires server version 4.0.0 or greater ##### *Return value* *BOOL*: Always `TRUE`. @@ -470,7 +470,7 @@ $redis->flushAll(); _**Description**_: Remove all keys from the current database. ##### *Parameters* -None. +*async* (bool) requires server version 4.0.0 or greater ##### *Return value* *BOOL*: Always `TRUE`. From 50881ddd69b32d452e0732fda7241b37fbb87091 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 11 Feb 2019 09:10:17 +0200 Subject: [PATCH 1082/1986] Don't check the number affected keys in PS_UPDATE_TIMESTAMP_FUNC --- redis_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index f43021e761..d58b53bdee 100644 --- a/redis_session.c +++ b/redis_session.c @@ -698,7 +698,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) return FAILURE; } - if (response_len == 2 && response[0] == ':' && response[1] == '1') { + if (response_len == 2 && response[0] == ':') { efree(response); return SUCCESS; } else { From e833d07685803e7482f6a1e5aca0c90664df00c1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 27 Jan 2019 11:39:26 -0800 Subject: [PATCH 1083/1986] Add a way for people to donate to the project if they so choose. --- README.markdown | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.markdown b/README.markdown index 1240a86ef5..c67a481e26 100644 --- a/README.markdown +++ b/README.markdown @@ -8,6 +8,12 @@ This code has been developed and maintained by Owlient from November 2009 to Mar You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)) or to p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)). +## Donating to the project +If you've found phpredis useful and would like to buy the maintainers a coffee (or a Tesla, we're not picky), feel free to do so. + +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5) +[![Donate with Bitcoin](https://en.cryptobadges.io/badge/micro/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)](https://en.cryptobadges.io/donate/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG) +[![Donate with Ethereum](https://en.cryptobadges.io/badge/micro/0x4345D9f767f877646587B24004652cb106f8F9ED)](https://en.cryptobadges.io/donate/0x4345D9f767f877646587B24004652cb106f8F9ED) # Table of contents ----- From 0f4f6e31529c102151bc8bc49882e1a6d6239ac4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 27 Jan 2019 12:04:33 -0800 Subject: [PATCH 1084/1986] Use the correct ETH address --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index c67a481e26..750fc2d92c 100644 --- a/README.markdown +++ b/README.markdown @@ -13,7 +13,8 @@ If you've found phpredis useful and would like to buy the maintainers a coffee ( [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5) [![Donate with Bitcoin](https://en.cryptobadges.io/badge/micro/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)](https://en.cryptobadges.io/donate/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG) -[![Donate with Ethereum](https://en.cryptobadges.io/badge/micro/0x4345D9f767f877646587B24004652cb106f8F9ED)](https://en.cryptobadges.io/donate/0x4345D9f767f877646587B24004652cb106f8F9ED) +[![Donate with Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1) + # Table of contents ----- From aba65bc2ad3bbe2c1381d90a0fb8c8803c70ace1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 28 Jan 2019 09:29:13 -0800 Subject: [PATCH 1085/1986] Add a note that scan is a directed node command. --- README.markdown | 2 ++ cluster.markdown | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index 750fc2d92c..0fa27ecf21 100644 --- a/README.markdown +++ b/README.markdown @@ -1043,6 +1043,8 @@ _**Description**_: Scan the keyspace for keys ##### *Return value* *Array, boolean*: This function will return an array of keys or FALSE if Redis returned zero keys +*Note*: SCAN is a "directed node" command in [RedisCluster](cluster.markdown#directed_node_commands). + ##### *Example* ~~~php diff --git a/cluster.markdown b/cluster.markdown index 3260bb7953..96cb13705b 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -130,15 +130,16 @@ This operation can also be done in MULTI mode transparently. ## Directed node commands There are a variety of commands which have to be directed at a specific node. In the case of these commands, the caller can either pass a key (which will be hashed and used to direct our command), or an array with host:port. -
+~~~php
 // This will be directed at the slot/node which would store "mykey"
 $obj_cluster->echo("mykey","Hello World!");
 
 // Here we're iterating all of our known masters, and delivering the command there
 foreach ($obj_cluster->_masters() as $arr_master) {
-    $obj_cluster->echo($arr_master, "Hello: " . implode(':', $arr_master));
+	$obj_cluster->echo($arr_master, "Hello: " . implode(':', $arr_master));
 }
-
+ +~~~ In the case of all commands which need to be directed at a node, the calling convention is identical to the Redis call, except that they require an additional (first) argument in order to deliver the command. Following is a list of each of these commands: @@ -157,6 +158,7 @@ In the case of all commands which need to be directed at a node, the calling con 13. SLOWLOG 14. RANDOMKEY 15. PING +16. SCAN ## Session Handler You can use the cluster functionality of phpredis to store PHP session information in a Redis cluster as you can with a non cluster-enabled Redis instance. From 6cf1ca47010cfe3c1936a3989bc63757670f5f9f Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Mon, 28 Jan 2019 09:33:44 -0800 Subject: [PATCH 1086/1986] Try relative link again (#1500) --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 0fa27ecf21..c1d934f72a 100644 --- a/README.markdown +++ b/README.markdown @@ -1043,7 +1043,7 @@ _**Description**_: Scan the keyspace for keys ##### *Return value* *Array, boolean*: This function will return an array of keys or FALSE if Redis returned zero keys -*Note*: SCAN is a "directed node" command in [RedisCluster](cluster.markdown#directed_node_commands). +*Note*: SCAN is a "directed node" command in [RedisCluster](cluster.markdown#directed-node-commands) ##### *Example* ~~~php From 3bec3db215b8fb33ca50ccaa071ab72e13f98ca1 Mon Sep 17 00:00:00 2001 From: Dusk Date: Mon, 28 Jan 2019 21:47:20 -0800 Subject: [PATCH 1087/1986] Remove outdated homebrew instructions (#1501) The homebrew-php tap no longer exists. Best option for homebrew users is PECL. --- INSTALL.markdown | 5 ----- 1 file changed, 5 deletions(-) diff --git a/INSTALL.markdown b/INSTALL.markdown index 967c988929..64ca1ad988 100644 --- a/INSTALL.markdown +++ b/INSTALL.markdown @@ -80,11 +80,6 @@ Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tag See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports). -You can install it using Homebrew: - -- [Get homebrew-php](https://github.com/Homebrew/homebrew-php) -- `brew install php55-redis` (or php53-redis, php54-redis) - You can install it using MacPorts: - [Get macports-php](https://www.macports.org/) From 5ab0e71c7621ab117bff31ab89a2dedab7786f7e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 1 Feb 2019 11:28:29 +0200 Subject: [PATCH 1088/1986] Don't check lock status in PS_UPDATE_TIMESTAMP_FUNC --- redis_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index d58b53bdee..ed0e187ab5 100644 --- a/redis_session.c +++ b/redis_session.c @@ -678,7 +678,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; - if (!redis_sock || !write_allowed(redis_sock, &pool->lock_status TSRMLS_CC)) { + if (!redis_sock) { return FAILURE; } From fec7a7f3977f751d7c3e9896be8ac2c64e0629e1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 11 Feb 2019 09:29:13 +0200 Subject: [PATCH 1089/1986] Add callback parameter to subscribe/psubscribe arginfo. This PR fixes issue #1504. --- common.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index 34f8441698..884e97642e 100644 --- a/common.h +++ b/common.h @@ -535,12 +535,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_object, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_subscribe, 0, 0, 1) +ZEND_BEGIN_ARG_INFO_EX(arginfo_subscribe, 0, 0, 2) ZEND_ARG_ARRAY_INFO(0, channels, 0) + ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_psubscribe, 0, 0, 1) +ZEND_BEGIN_ARG_INFO_EX(arginfo_psubscribe, 0, 0, 2) ZEND_ARG_ARRAY_INFO(0, patterns, 0) + ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_unsubscribe, 0, 0, 1) From f90ba7c8e0add275b2308ff05d483d2ae1ba5efb Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 11 Feb 2019 09:38:56 +0200 Subject: [PATCH 1090/1986] Add documentation about async parameter for flushAll/flushDb. This commit fixes issue #1503. --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index c1d934f72a..111461d3cf 100644 --- a/README.markdown +++ b/README.markdown @@ -455,7 +455,7 @@ echo "Redis has $count keys\n"; _**Description**_: Remove all keys from all databases. ##### *Parameters* -None. +*async* (bool) requires server version 4.0.0 or greater ##### *Return value* *BOOL*: Always `TRUE`. @@ -470,7 +470,7 @@ $redis->flushAll(); _**Description**_: Remove all keys from the current database. ##### *Parameters* -None. +*async* (bool) requires server version 4.0.0 or greater ##### *Return value* *BOOL*: Always `TRUE`. From 22d81a94eee2ea613fc515e1d714b73142d46241 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 13 Feb 2019 12:37:27 -0800 Subject: [PATCH 1091/1986] Implement GEORADIUS_RO and GEORADIUSBYMEMBER_RO This addresses #1502, #1487 --- php_redis.h | 2 ++ redis.c | 14 ++++++++++++-- redis_cluster.c | 16 ++++++++++++++-- redis_cluster.h | 2 ++ redis_commands.c | 15 +++++++++------ redis_commands.h | 12 ++++++------ tests/RedisTest.php | 34 ++++++++++++++++++---------------- 7 files changed, 63 insertions(+), 32 deletions(-) diff --git a/php_redis.h b/php_redis.h index 655f9730cd..8dc2f5c6cc 100644 --- a/php_redis.h +++ b/php_redis.h @@ -205,7 +205,9 @@ PHP_METHOD(Redis, geohash); PHP_METHOD(Redis, geopos); PHP_METHOD(Redis, geodist); PHP_METHOD(Redis, georadius); +PHP_METHOD(Redis, georadius_ro); PHP_METHOD(Redis, georadiusbymember); +PHP_METHOD(Redis, georadiusbymember_ro); PHP_METHOD(Redis, client); PHP_METHOD(Redis, command); diff --git a/redis.c b/redis.c index 5e076a9073..539889799a 100644 --- a/redis.c +++ b/redis.c @@ -285,7 +285,9 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, geopos, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) + PHP_ME(Redis, georadius_ro, arginfo_georadius, ZEND_ACC_PUBLIC) PHP_ME(Redis, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) + PHP_ME(Redis, georadiusbymember_ro, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) PHP_ME(Redis, get, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, getAuth, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, getBit, arginfo_key_offset, ZEND_ACC_PUBLIC) @@ -3595,11 +3597,19 @@ PHP_METHOD(Redis, geodist) { } PHP_METHOD(Redis, georadius) { - REDIS_PROCESS_CMD(georadius, redis_read_variant_reply); + REDIS_PROCESS_KW_CMD("GEORADIUS", redis_georadius_cmd, redis_read_variant_reply); +} + +PHP_METHOD(Redis, georadius_ro) { + REDIS_PROCESS_KW_CMD("GEORADIUS_RO", redis_georadius_cmd, redis_read_variant_reply); } PHP_METHOD(Redis, georadiusbymember) { - REDIS_PROCESS_CMD(georadiusbymember, redis_read_variant_reply); + REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER", redis_georadiusbymember_cmd, redis_read_variant_reply); +} + +PHP_METHOD(Redis, georadiusbymember_ro) { + REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, redis_read_variant_reply); } /* diff --git a/redis_cluster.c b/redis_cluster.c index 9bc4254707..eb4726a34e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -149,7 +149,9 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, geohash, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, geopos, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, georadius, arginfo_georadius, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, georadius_ro, arginfo_georadius, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, georadiusbymember_ro, arginfo_georadiusbymember, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, get, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getbit, arginfo_key_offset, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, getlasterror, arginfo_void, ZEND_ACC_PUBLIC) @@ -2953,12 +2955,22 @@ PHP_METHOD(RedisCluster, geodist) { /* {{{ proto array RedisCluster::georadius() }}} */ PHP_METHOD(RedisCluster, georadius) { - CLUSTER_PROCESS_CMD(georadius, cluster_variant_resp, 1); + CLUSTER_PROCESS_KW_CMD("GEORADIUS", redis_georadius_cmd, cluster_variant_resp, 1); +} + +/* {{{ proto array RedisCluster::georadius() }}} */ +PHP_METHOD(RedisCluster, georadius_ro) { + CLUSTER_PROCESS_KW_CMD("GEORADIUS_RO", redis_georadius_cmd, cluster_variant_resp, 1); } /* {{{ proto array RedisCluster::georadiusbymember() }}} */ PHP_METHOD(RedisCluster, georadiusbymember) { - CLUSTER_PROCESS_CMD(georadiusbymember, cluster_variant_resp, 1) + CLUSTER_PROCESS_KW_CMD("GEORADIUSBYMEMBER", redis_georadiusbymember_cmd, cluster_variant_resp, 1); +} + +/* {{{ proto array RedisCluster::georadiusbymember() }}} */ +PHP_METHOD(RedisCluster, georadiusbymember_ro) { + CLUSTER_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, cluster_variant_resp, 1); } /* {{{ proto array RedisCluster::role(string key) diff --git a/redis_cluster.h b/redis_cluster.h index b4d0a8bab5..8995cc1932 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -255,7 +255,9 @@ PHP_METHOD(RedisCluster, geohash); PHP_METHOD(RedisCluster, geopos); PHP_METHOD(RedisCluster, geodist); PHP_METHOD(RedisCluster, georadius); +PHP_METHOD(RedisCluster, georadius_ro); PHP_METHOD(RedisCluster, georadiusbymember); +PHP_METHOD(RedisCluster, georadiusbymember_ro); /* SCAN and friends */ PHP_METHOD(RedisCluster, scan); diff --git a/redis_commands.c b/redis_commands.c index 9dbb40f7c6..db8547389c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2788,9 +2788,10 @@ void append_georadius_opts(RedisSock *redis_sock, smart_string *str, short *slot } } -/* GEORADIUS */ +/* GEORADIUS / GEORADIUS_RO */ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key, *unit; short store_slot = 0; @@ -2823,7 +2824,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, (gopts.store != STORE_NONE ? 2 : 0); /* Begin construction of our command */ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUS"); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); /* Prefix and set slot */ keyfree = redis_key_prefix(redis_sock, &key, &keylen); @@ -2858,9 +2859,11 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -/* GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] */ +/* GEORADIUSBYMEMBER/GEORADIUSBYMEMBER_RO + * key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] */ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { char *key, *mem, *unit; strlen_t keylen, memlen, unitlen; @@ -2890,7 +2893,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s (gopts.store != STORE_NONE ? 2 : 0); /* Begin command construction*/ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEORADIUSBYMEMBER"); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); /* Prefix our key if we're prefixing and set the slot */ keyfree = redis_key_prefix(redis_sock, &key, &keylen); diff --git a/redis_commands.h b/redis_commands.h index 2f3e53deda..1ef1712cf6 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -118,6 +118,12 @@ int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them @@ -253,12 +259,6 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 9887daac54..75fade20f5 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5109,29 +5109,29 @@ public function genericGeoRadiusTest($cmd) { $this->addCities('{gk}'); /* Pre tested with redis-cli. We're just verifying proper delivery of distance and unit */ - if ($cmd == 'georadius') { - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadius('{gk}', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array('georadius', '{gk}', $lng, $lat, 500, 'mi'); + if ($cmd == 'georadius' || $cmd == 'georadius_ro') { + $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi'), Array('Chico')); + $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); + $args = Array($cmd, '{gk}', $lng, $lat, 500, 'mi'); /* Test a bad COUNT argument */ foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->georadius('{gk}', $lng, $lat, 10, 'mi', Array('count' => $count))); + $this->assertFalse(@$this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi', Array('count' => $count))); } } else { - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->georadiusbymember('{gk}', $city, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array('georadiusbymember', '{gk}', $city, 500, 'mi'); + $this->assertEquals($this->redis->$cmd('{gk}', $city, 10, 'mi'), Array('Chico')); + $this->assertEquals($this->redis->$cmd('{gk}', $city, 30, 'mi'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->$cmd('{gk}', $city, 50, 'km'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->$cmd('{gk}', $city, 50000, 'm'), Array('Gridley','Chico')); + $this->assertEquals($this->redis->$cmd('{gk}', $city, 150000, 'ft'), Array('Gridley', 'Chico')); + $args = Array($cmd, '{gk}', $city, 500, 'mi'); /* Test a bad COUNT argument */ foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->georadiusbymember('{gk}', $city, 10, 'mi', Array('count' => $count))); + $this->assertFalse(@$this->redis->$cmd('{gk}', $city, 10, 'mi', Array('count' => $count))); } } @@ -5185,7 +5185,7 @@ public function genericGeoRadiusTest($cmd) { } $ret1 = $this->rawCommandArray('{gk}', $realargs); - if ($cmd == 'georadius') { + if ($cmd == 'georadius' || $cmd == 'georadius_ro') { $ret2 = $this->redis->$cmd('{gk}', $lng, $lat, 500, 'mi', $realopts); } else { $ret2 = $this->redis->$cmd('{gk}', $city, 500, 'mi', $realopts); @@ -5205,6 +5205,7 @@ public function testGeoRadius() { } $this->genericGeoRadiusTest('georadius'); + $this->genericGeoRadiusTest('georadius_ro'); } public function testGeoRadiusByMember() { @@ -5213,6 +5214,7 @@ public function testGeoRadiusByMember() { } $this->genericGeoRadiusTest('georadiusbymember'); + $this->genericGeoRadiusTest('georadiusbymember_ro'); } public function testGeoPos() { From 46f035615ecaf839cfe515dac39f209d50689461 Mon Sep 17 00:00:00 2001 From: Marin Bezhanov Date: Sun, 17 Feb 2019 13:05:58 +0200 Subject: [PATCH 1092/1986] Add ZPOPMAX and ZPOPMIN support --- php_redis.h | 2 ++ redis.c | 28 ++++++++++++++++++++++++++++ redis_cluster.c | 26 ++++++++++++++++++++++++++ redis_cluster.h | 2 ++ tests/RedisTest.php | 23 +++++++++++++++++++++++ 5 files changed, 81 insertions(+) diff --git a/php_redis.h b/php_redis.h index 8dc2f5c6cc..1f1eb1acd4 100644 --- a/php_redis.h +++ b/php_redis.h @@ -131,6 +131,8 @@ PHP_METHOD(Redis, zRevRank); PHP_METHOD(Redis, zIncrBy); PHP_METHOD(Redis, zInter); PHP_METHOD(Redis, zUnion); +PHP_METHOD(Redis, zPopMax); +PHP_METHOD(Redis, zPopMin); PHP_METHOD(Redis, expireAt); PHP_METHOD(Redis, pexpireAt); PHP_METHOD(Redis, bgrewriteaof); diff --git a/redis.c b/redis.c index 539889799a..bb9f420316 100644 --- a/redis.c +++ b/redis.c @@ -445,6 +445,8 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zPopMax, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zPopMin, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, del, delete, arginfo_del, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) @@ -2138,6 +2140,32 @@ PHP_METHOD(Redis, zUnion) { REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, redis_long_response); } +/* {{{ proto array Redis::zPopMax(string key) */ +PHP_METHOD(Redis, zPopMax) +{ + if (ZEND_NUM_ARGS() == 1) { + REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, redis_sock_read_multibulk_reply); + } else if (ZEND_NUM_ARGS() == 2) { + REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, redis_sock_read_multibulk_reply); + } else { + ZEND_WRONG_PARAM_COUNT(); + } +} +/* }}} */ + +/* {{{ proto array Redis::zPopMin(string key) */ +PHP_METHOD(Redis, zPopMin) +{ + if (ZEND_NUM_ARGS() == 1) { + REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, redis_sock_read_multibulk_reply); + } else if (ZEND_NUM_ARGS() == 2) { + REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, redis_sock_read_multibulk_reply); + } else { + ZEND_WRONG_PARAM_COUNT(); + } +} +/* }}} */ + /* hashes */ /* {{{ proto long Redis::hset(string key, string mem, string val) */ diff --git a/redis_cluster.c b/redis_cluster.c index eb4726a34e..7087e478bf 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -274,6 +274,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, zincrby, arginfo_zincrby, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zlexcount, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zpopmax, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, zpopmin, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, zrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) @@ -1844,6 +1846,30 @@ PHP_METHOD(RedisCluster, zremrangebylex) { } /* }}} */ +/* {{{ proto array RedisCluster::zpopmax(string key) */ +PHP_METHOD(RedisCluster, zpopmax) { + if (ZEND_NUM_ARGS() == 1) { + CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, cluster_mbulk_resp, 0); + } else if (ZEND_NUM_ARGS() == 2) { + CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, cluster_mbulk_resp, 0); + } else { + ZEND_WRONG_PARAM_COUNT(); + } +} +/* }}} */ + +/* {{{ proto array RedisCluster::zpopmin(string key) */ +PHP_METHOD(RedisCluster, zpopmin) { + if (ZEND_NUM_ARGS() == 1) { + CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, cluster_mbulk_resp, 0); + } else if (ZEND_NUM_ARGS() == 2) { + CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, cluster_mbulk_resp, 0); + } else { + ZEND_WRONG_PARAM_COUNT(); + } +} +/* }}} */ + /* {{{ proto RedisCluster::sort(string key, array options) */ PHP_METHOD(RedisCluster, sort) { redisCluster *c = GET_CONTEXT(); diff --git a/redis_cluster.h b/redis_cluster.h index 8995cc1932..5e92817470 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -224,6 +224,8 @@ PHP_METHOD(RedisCluster, restore); PHP_METHOD(RedisCluster, setrange); PHP_METHOD(RedisCluster, smove); PHP_METHOD(RedisCluster, srandmember); +PHP_METHOD(RedisCluster, zpopmin); +PHP_METHOD(RedisCluster, zpopmax); PHP_METHOD(RedisCluster, zrange); PHP_METHOD(RedisCluster, zrevrange); PHP_METHOD(RedisCluster, zrangebyscore); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 75fade20f5..e666b1fc8a 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2409,6 +2409,29 @@ public function testZRemRangeByLex() { $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 2); } + public function testZPop() { + if (version_compare($this->version, "5.0.0", "lt")) { + $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') === $this->redis->zPopMax('key')); + $this->assertTrue(array('a', '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', 'd', '3', 'c', '2') === $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', 'b', '1', 'c', '2') === $this->redis->zPopMin('key', 3)); + } + public function testHashes() { $this->redis->del('h', 'key'); $this->assertTrue(0 === $this->redis->hLen('h')); From f89e941a8891ede511b1bac80fd13d0af4d58a71 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 18 Feb 2019 20:00:08 -0800 Subject: [PATCH 1093/1986] Change ZPOP* return type and implement blocking variants This commit updates ZPOPMIN/ZPOPMAX to return the same format that zRange WITHSCORES and zRangeByScore WITHSCORES does. In addition the blocking variants BZPOPMIN and BZPOPMAX are implemented. --- .gitignore | 1 + php_redis.h | 2 ++ redis.c | 26 ++++++++++++++++++++------ redis_cluster.c | 24 ++++++++++++++++++------ redis_cluster.h | 2 ++ redis_commands.c | 17 +++++++++-------- redis_commands.h | 9 +++------ tests/RedisTest.php | 30 ++++++++++++++++++++++++++---- 8 files changed, 81 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index f672fcee2a..965c536206 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ mkinstalldirs run-tests.php idea/* .cquery +tags diff --git a/php_redis.h b/php_redis.h index 1f1eb1acd4..d6240ec10a 100644 --- a/php_redis.h +++ b/php_redis.h @@ -133,6 +133,8 @@ PHP_METHOD(Redis, zInter); PHP_METHOD(Redis, zUnion); PHP_METHOD(Redis, zPopMax); PHP_METHOD(Redis, zPopMin); +PHP_METHOD(Redis, bzPopMax); +PHP_METHOD(Redis, bzPopMin); PHP_METHOD(Redis, expireAt); PHP_METHOD(Redis, pexpireAt); PHP_METHOD(Redis, bgrewriteaof); diff --git a/redis.c b/redis.c index bb9f420316..52924b1b0d 100644 --- a/redis.c +++ b/redis.c @@ -259,6 +259,8 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, blPop, arginfo_blrpop, ZEND_ACC_PUBLIC) PHP_ME(Redis, brPop, arginfo_blrpop, ZEND_ACC_PUBLIC) PHP_ME(Redis, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bzPopMax, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(Redis, bzPopMin, arginfo_blrpop, ZEND_ACC_PUBLIC) PHP_ME(Redis, clearLastError, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, client, arginfo_client, ZEND_ACC_PUBLIC) PHP_ME(Redis, close, arginfo_void, ZEND_ACC_PUBLIC) @@ -1376,14 +1378,14 @@ PHP_METHOD(Redis, rPop) /* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout) */ PHP_METHOD(Redis, blPop) { - REDIS_PROCESS_CMD(blpop, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("BLPOP", redis_varkey_timeout_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout) */ PHP_METHOD(Redis, brPop) { - REDIS_PROCESS_CMD(brpop, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("BRPOP", redis_varkey_timeout_cmd, redis_sock_read_multibulk_reply); } /* }}} */ @@ -2144,9 +2146,9 @@ PHP_METHOD(Redis, zUnion) { PHP_METHOD(Redis, zPopMax) { if (ZEND_NUM_ARGS() == 1) { - REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl); } else if (ZEND_NUM_ARGS() == 2) { - REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl); } else { ZEND_WRONG_PARAM_COUNT(); } @@ -2157,15 +2159,27 @@ PHP_METHOD(Redis, zPopMax) PHP_METHOD(Redis, zPopMin) { if (ZEND_NUM_ARGS() == 1) { - REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl); } else if (ZEND_NUM_ARGS() == 2) { - REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl); } else { ZEND_WRONG_PARAM_COUNT(); } } /* }}} */ +/* {{{ proto Redis::bzPopMax(Array(keys) [, timeout]): Array */ +PHP_METHOD(Redis, bzPopMax) { + REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_varkey_timeout_cmd, redis_sock_read_multibulk_reply); +} +/* }}} */ + +/* {{{ proto Redis::bzPopMin(Array(keys) [, timeout]): Array */ +PHP_METHOD(Redis, bzPopMin) { + REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_varkey_timeout_cmd, redis_sock_read_multibulk_reply); +} +/* }}} */ + /* hashes */ /* {{{ proto long Redis::hset(string key, string mem, string val) */ diff --git a/redis_cluster.c b/redis_cluster.c index 7087e478bf..f9da1cc09e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -124,6 +124,8 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, brpop, arginfo_blrpop, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, clearlasterror, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bzpopmax, arginfo_blrpop, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, bzpopmin, arginfo_blrpop, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, client, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, close, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, cluster, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC) @@ -1257,13 +1259,13 @@ PHP_METHOD(RedisCluster, rpush) { /* {{{ proto array RedisCluster::blpop(string key1, ... keyN, long timeout) */ PHP_METHOD(RedisCluster, blpop) { - CLUSTER_PROCESS_CMD(blpop, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("BLPOP", redis_varkey_timeout_cmd, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::brpop(string key1, ... keyN, long timeout */ PHP_METHOD(RedisCluster, brpop) { - CLUSTER_PROCESS_CMD(brpop, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("BRPOP", redis_varkey_timeout_cmd, cluster_mbulk_resp, 0); } /* }}} */ @@ -1849,9 +1851,9 @@ PHP_METHOD(RedisCluster, zremrangebylex) { /* {{{ proto array RedisCluster::zpopmax(string key) */ PHP_METHOD(RedisCluster, zpopmax) { if (ZEND_NUM_ARGS() == 1) { - CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, cluster_mbulk_zipdbl_resp, 0); } else if (ZEND_NUM_ARGS() == 2) { - CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, cluster_mbulk_zipdbl_resp, 0); } else { ZEND_WRONG_PARAM_COUNT(); } @@ -1861,15 +1863,25 @@ PHP_METHOD(RedisCluster, zpopmax) { /* {{{ proto array RedisCluster::zpopmin(string key) */ PHP_METHOD(RedisCluster, zpopmin) { if (ZEND_NUM_ARGS() == 1) { - CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, cluster_mbulk_zipdbl_resp, 0); } else if (ZEND_NUM_ARGS() == 2) { - CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, cluster_mbulk_zipdbl_resp, 0); } else { ZEND_WRONG_PARAM_COUNT(); } } /* }}} */ +/* {{{ proto array RedisCluster::bzPopMin(Array keys [, timeout]) }}} */ +PHP_METHOD(RedisCluster, bzpopmax) { + CLUSTER_PROCESS_KW_CMD("BZPOPMAX", redis_varkey_timeout_cmd, cluster_mbulk_resp, 0); +} + +/* {{{ proto array RedisCluster::bzPopMax(Array keys [, timeout]) }}} */ +PHP_METHOD(RedisCluster, bzpopmin) { + CLUSTER_PROCESS_KW_CMD("BZPOPMIN", redis_varkey_timeout_cmd, cluster_mbulk_resp, 0); +} + /* {{{ proto RedisCluster::sort(string key, array options) */ PHP_METHOD(RedisCluster, sort) { redisCluster *c = GET_CONTEXT(); diff --git a/redis_cluster.h b/redis_cluster.h index 5e92817470..6935450654 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -226,6 +226,8 @@ PHP_METHOD(RedisCluster, smove); PHP_METHOD(RedisCluster, srandmember); PHP_METHOD(RedisCluster, zpopmin); PHP_METHOD(RedisCluster, zpopmax); +PHP_METHOD(RedisCluster, bzpopmax); +PHP_METHOD(RedisCluster, bzpopmin); PHP_METHOD(RedisCluster, zrange); PHP_METHOD(RedisCluster, zrevrange); PHP_METHOD(RedisCluster, zrangebyscore); diff --git a/redis_commands.c b/redis_commands.c index db8547389c..c77ff31e72 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1251,6 +1251,15 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Generic handling of every blocking pop command (BLPOP, BZPOP[MIN/MAX], etc */ +int redis_varkey_timeout_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, + strlen(kw), 2, 1, cmd, cmd_len, slot); +} + /* * Commands with specific signatures or that need unique functions because they * have specific processing (argument validation, etc) that make them unique @@ -3052,14 +3061,6 @@ int redis_blpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "BLPOP", sizeof("BLPOP")-1, 2, 1, cmd, cmd_len, slot); } -/* BRPOP */ -int redis_brpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - "BRPOP", sizeof("BRPOP")-1, 1, 1, cmd, cmd_len, slot); -} - /* SINTER */ int redis_sinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 1ef1712cf6..11fbd6951a 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -78,6 +78,9 @@ int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_varkey_timeout_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + /* Construct SCAN and similar commands, as well as check iterator */ int redis_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, char **cmd, int *cmd_len); @@ -226,12 +229,6 @@ int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_watch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_blpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_brpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_sinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index e666b1fc8a..7dcd17fef2 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2409,6 +2409,28 @@ public function testZRemRangeByLex() { $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 2); } + public function testBZPop() { + if (version_compare($this->version, "5.0.0", "lt")) { + $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", "lt")) { $this->MarkTestSkipped(); @@ -2418,18 +2440,18 @@ public function testZPop() { // 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') === $this->redis->zPopMax('key')); - $this->assertTrue(array('a', '0') === $this->redis->zPopMin('key')); + $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', 'd', '3', 'c', '2') === $this->redis->zPopMax('key', 3)); + $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', 'b', '1', 'c', '2') === $this->redis->zPopMin('key', 3)); + $this->assertTrue(array('a' => 0.0, 'b' => 1.0, 'c' => 2.0) === $this->redis->zPopMin('key', 3)); } public function testHashes() { From 8887cd76f7fcc08ef59be110e215254a5d35f5fa Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 10:35:29 +0200 Subject: [PATCH 1094/1986] Travis CI: run tests without igbinary if it can't be installed --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c84d018a1..0aad3ed9b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,8 +38,7 @@ addons: packages: clang before_install: - phpize - - pecl install igbinary - - ./configure --enable-redis-igbinary --enable-redis-lzf CFLAGS=-Wall + - pecl install igbinary && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf install: make install before_script: - gem install redis From ae5d7b1e5b5aa23db367611f960472e64f882875 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 13:32:44 +0200 Subject: [PATCH 1095/1986] Travis CI: igbinary 2.0.8 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0aad3ed9b9..513f367aee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ addons: packages: clang before_install: - phpize - - pecl install igbinary && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf + - pecl install igbinary-2.0.8 && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf install: make install before_script: - gem install redis From 8cd165dfc19732ef2851ce44871e12db3ef04b1e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 18 Feb 2019 14:33:29 +0200 Subject: [PATCH 1096/1986] Use zend_string for storing key hashing algorithm --- redis_array.c | 12 ++++++------ redis_array.h | 2 +- redis_array_impl.c | 22 ++++++++++++---------- redis_array_impl.h | 2 +- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/redis_array.c b/redis_array.c index 79cf0804a8..db00c10b93 100644 --- a/redis_array.c +++ b/redis_array.c @@ -160,7 +160,7 @@ redis_array_free(RedisArray *ra) zval_dtor(&ra->z_dist); /* Hashing algorithm */ - zval_dtor(&ra->z_algo); + if (ra->algorithm) zend_string_release(ra->algorithm); /* Delete pur commands */ zend_hash_destroy(ra->pure_cmds); @@ -272,13 +272,14 @@ redis_array_get(zval *id TSRMLS_DC) Public constructor */ PHP_METHOD(RedisArray, __construct) { - zval *z0, z_fun, z_dist, z_algo, *zpData, *z_opts = NULL; + zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; HashTable *hPrev = NULL, *hOpts = NULL; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0, read_timeout = 0.0; + zend_string *algorithm = NULL; redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { @@ -287,7 +288,6 @@ PHP_METHOD(RedisArray, __construct) ZVAL_NULL(&z_fun); ZVAL_NULL(&z_dist); - ZVAL_NULL(&z_algo); /* extract options */ if(z_opts) { hOpts = Z_ARRVAL_P(z_opts); @@ -312,7 +312,7 @@ PHP_METHOD(RedisArray, __construct) /* extract function name. */ if ((zpData = zend_hash_str_find(hOpts, "algorithm", sizeof("algorithm") - 1)) != NULL && Z_TYPE_P(zpData) == IS_STRING) { - ZVAL_ZVAL(&z_algo, zpData, 1, 0); + algorithm = zval_get_string(zpData); } /* extract index option. */ @@ -379,13 +379,13 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, &z_algo, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); break; default: WRONG_PARAM_COUNT; } - zval_dtor(&z_algo); + if (algorithm) zend_string_release(algorithm); zval_dtor(&z_dist); zval_dtor(&z_fun); diff --git a/redis_array.h b/redis_array.h index de44465426..3035a07135 100644 --- a/redis_array.h +++ b/redis_array.h @@ -59,7 +59,7 @@ typedef struct RedisArray_ { zend_bool pconnect; /* should we use pconnect */ zval z_fun; /* key extractor, callable */ zval z_dist; /* key distributor, callable */ - zval z_algo; /* key hashing algorithm name */ + zend_string *algorithm; /* key hashing algorithm name */ HashTable *pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ double read_timeout; /* socket read timeout */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 4ee72d8a0a..0ec7755c31 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -164,7 +164,7 @@ ra_find_name(const char *name) { /* laod array from INI settings */ RedisArray *ra_load_array(const char *name TSRMLS_DC) { - zval *z_data, z_fun, z_dist, z_algo; + zval *z_data, z_fun, z_dist; zval z_params_hosts; zval z_params_prev; zval z_params_funs; @@ -180,6 +180,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_consistent; RedisArray *ra = NULL; + zend_string *algorithm= NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; @@ -235,9 +236,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.algorithm")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_algo TSRMLS_CC); } - ZVAL_NULL(&z_algo); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_algo), name, name_len)) != NULL) { - ZVAL_ZVAL(&z_algo, z_data, 1, 0); + algorithm = zval_get_string(z_data); } /* find index option */ @@ -340,13 +340,15 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, &z_algo, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; } /* cleanup */ + if (algorithm) zend_string_release(algorithm); + zval_dtor(&z_params_hosts); zval_dtor(&z_params_prev); zval_dtor(&z_params_funs); @@ -360,7 +362,6 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); zval_dtor(&z_params_consistent); - zval_dtor(&z_algo); zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -408,7 +409,7 @@ ra_make_continuum(zend_string **hosts, int nb_hosts) } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC) { int i, count; RedisArray *ra; @@ -418,7 +419,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab /* create object */ ra = emalloc(sizeof(RedisArray)); ra->hosts = ecalloc(count, sizeof(*ra->hosts)); - ra->redis = ecalloc(count, sizeof(zval)); + ra->redis = ecalloc(count, sizeof(*ra->redis)); ra->count = 0; ra->z_multi_exec = NULL; ra->index = b_index; @@ -427,6 +428,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab ra->connect_timeout = connect_timeout; ra->read_timeout = read_timeout; ra->continuum = NULL; + ra->algorithm = NULL; if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { @@ -438,7 +440,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, z_algo, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); @@ -446,7 +448,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab /* Set hash function and distribtor if provided */ ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); ZVAL_ZVAL(&ra->z_dist, z_dist, 1, 0); - ZVAL_ZVAL(&ra->z_algo, z_algo, 1, 0); + if (algorithm) ra->algorithm = zend_string_copy(algorithm); /* init continuum */ if (consistent) { @@ -552,7 +554,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D const php_hash_ops *ops; /* hash */ - if (Z_TYPE(ra->z_algo) == IS_STRING && (ops = php_hash_fetch_ops(Z_STRVAL(ra->z_algo), Z_STRLEN(ra->z_algo))) != NULL) { + if (ra->algorithm && (ops = php_hash_fetch_ops(ZSTR_VAL(ra->algorithm), ZSTR_LEN(ra->algorithm))) != NULL) { void *ctx = emalloc(ops->context_size); unsigned char *digest = emalloc(ops->digest_size); diff --git a/redis_array_impl.h b/redis_array_impl.h index 43705ee591..8159d6ed00 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -11,7 +11,7 @@ RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); From 2ec7d91a7be50eac09567420e2a41e2f3f3f005c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Feb 2019 07:55:34 -0800 Subject: [PATCH 1097/1986] Remove dead code, fix min_argc for blocking commands --- redis.c | 8 ++++---- redis_cluster.c | 8 ++++---- redis_commands.c | 16 ++++------------ redis_commands.h | 2 +- 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/redis.c b/redis.c index 52924b1b0d..6265ac8062 100644 --- a/redis.c +++ b/redis.c @@ -1378,14 +1378,14 @@ PHP_METHOD(Redis, rPop) /* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout) */ PHP_METHOD(Redis, blPop) { - REDIS_PROCESS_KW_CMD("BLPOP", redis_varkey_timeout_cmd, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("BLPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout) */ PHP_METHOD(Redis, brPop) { - REDIS_PROCESS_KW_CMD("BRPOP", redis_varkey_timeout_cmd, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("BRPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ @@ -2170,13 +2170,13 @@ PHP_METHOD(Redis, zPopMin) /* {{{ proto Redis::bzPopMax(Array(keys) [, timeout]): Array */ PHP_METHOD(Redis, bzPopMax) { - REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_varkey_timeout_cmd, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ /* {{{ proto Redis::bzPopMin(Array(keys) [, timeout]): Array */ PHP_METHOD(Redis, bzPopMin) { - REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_varkey_timeout_cmd, redis_sock_read_multibulk_reply); + REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index f9da1cc09e..3eb267c40a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1259,13 +1259,13 @@ PHP_METHOD(RedisCluster, rpush) { /* {{{ proto array RedisCluster::blpop(string key1, ... keyN, long timeout) */ PHP_METHOD(RedisCluster, blpop) { - CLUSTER_PROCESS_KW_CMD("BLPOP", redis_varkey_timeout_cmd, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("BLPOP", redis_blocking_pop_cmd, cluster_mbulk_resp, 0); } /* }}} */ /* {{{ proto array RedisCluster::brpop(string key1, ... keyN, long timeout */ PHP_METHOD(RedisCluster, brpop) { - CLUSTER_PROCESS_KW_CMD("BRPOP", redis_varkey_timeout_cmd, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("BRPOP", redis_blocking_pop_cmd, cluster_mbulk_resp, 0); } /* }}} */ @@ -1874,12 +1874,12 @@ PHP_METHOD(RedisCluster, zpopmin) { /* {{{ proto array RedisCluster::bzPopMin(Array keys [, timeout]) }}} */ PHP_METHOD(RedisCluster, bzpopmax) { - CLUSTER_PROCESS_KW_CMD("BZPOPMAX", redis_varkey_timeout_cmd, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, cluster_mbulk_resp, 0); } /* {{{ proto array RedisCluster::bzPopMax(Array keys [, timeout]) }}} */ PHP_METHOD(RedisCluster, bzpopmin) { - CLUSTER_PROCESS_KW_CMD("BZPOPMIN", redis_varkey_timeout_cmd, cluster_mbulk_resp, 0); + CLUSTER_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, cluster_mbulk_resp, 0); } /* {{{ proto RedisCluster::sort(string key, array options) */ diff --git a/redis_commands.c b/redis_commands.c index c77ff31e72..26df9b5983 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1252,12 +1252,12 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Generic handling of every blocking pop command (BLPOP, BZPOP[MIN/MAX], etc */ -int redis_varkey_timeout_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, - strlen(kw), 2, 1, cmd, cmd_len, slot); + strlen(kw), 2, 2, cmd, cmd_len, slot); } /* @@ -3053,14 +3053,6 @@ int redis_watch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "WATCH", sizeof("WATCH")-1, 1, 0, cmd, cmd_len, slot); } -/* BLPOP */ -int redis_blpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - "BLPOP", sizeof("BLPOP")-1, 2, 1, cmd, cmd_len, slot); -} - /* SINTER */ int redis_sinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 11fbd6951a..9a9c777d69 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -78,7 +78,7 @@ int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_varkey_timeout_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); /* Construct SCAN and similar commands, as well as check iterator */ From b5549cffd2fa57126af102fa7fd902644ed90743 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 17 Feb 2019 21:49:25 +0200 Subject: [PATCH 1098/1986] RedisArray auth. Issue #1508 --- redis.c | 1 + redis_array.c | 10 ++++++++-- redis_array_impl.c | 30 ++++++++++++++++++++++-------- redis_array_impl.h | 3 +-- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/redis.c b/redis.c index 539889799a..b7f0a90bec 100644 --- a/redis.c +++ b/redis.c @@ -56,6 +56,7 @@ extern zend_function_entry redis_cluster_functions[]; PHP_INI_BEGIN() /* redis arrays */ PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.auth", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.autorehash", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.connecttimeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL) diff --git a/redis_array.c b/redis_array.c index db00c10b93..e0a3c9ffde 100644 --- a/redis_array.c +++ b/redis_array.c @@ -279,7 +279,7 @@ PHP_METHOD(RedisArray, __construct) long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0, read_timeout = 0.0; - zend_string *algorithm = NULL; + zend_string *algorithm = NULL, *auth = NULL; redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { @@ -370,6 +370,11 @@ PHP_METHOD(RedisArray, __construct) if ((zpData = zend_hash_str_find(hOpts, "consistent", sizeof("consistent") - 1)) != NULL) { consistent = zval_is_true(zpData); } + + /* auth */ + if ((zpData = zend_hash_str_find(hOpts, "auth", sizeof("auth") - 1)) != NULL) { + auth = zval_get_string(zpData); + } } /* extract either name of list of hosts from z0 */ @@ -379,13 +384,14 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC); break; default: WRONG_PARAM_COUNT; } if (algorithm) zend_string_release(algorithm); + if (auth) zend_string_release(auth); zval_dtor(&z_dist); zval_dtor(&z_fun); diff --git a/redis_array_impl.c b/redis_array_impl.c index 0ec7755c31..c7e5c2b922 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -32,8 +32,8 @@ extern zend_class_entry *redis_ce; -RedisArray* -ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) +static RedisArray * +ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) { int i = 0, host_len; char *host, *p; @@ -77,6 +77,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b /* create socket */ redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval); + /* copy password is specified */ + if (auth) redis->sock->auth = zend_string_copy(auth); + if (!b_lazy_connect) { /* connect */ @@ -178,9 +181,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_read_timeout; zval z_params_lazy_connect; zval z_params_consistent; + zval z_params_auth; RedisArray *ra = NULL; - zend_string *algorithm= NULL; + zend_string *algorithm = NULL, *auth = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; @@ -338,9 +342,17 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } + /* find auth option */ + array_init(&z_params_auth); + if ((iptr = INI_STR("redis.arrays.auth")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_auth TSRMLS_CC); + } + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_auth), name, name_len)) != NULL) { + auth = zval_get_string(z_data); + } /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; @@ -348,6 +360,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* cleanup */ if (algorithm) zend_string_release(algorithm); + if (auth) zend_string_release(auth); zval_dtor(&z_params_hosts); zval_dtor(&z_params_prev); @@ -362,6 +375,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); zval_dtor(&z_params_consistent); + zval_dtor(&z_params_auth); zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -409,8 +423,8 @@ ra_make_continuum(zend_string **hosts, int nb_hosts) } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC) { - +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth TSRMLS_DC) +{ int i, count; RedisArray *ra; @@ -430,7 +444,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->continuum = NULL; ra->algorithm = NULL; - if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { + if (ra_load_hosts(ra, hosts, auth, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { zval_dtor(&ra->redis[i]); zend_string_release(ra->hosts[i]); @@ -440,7 +454,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); diff --git a/redis_array_impl.h b/redis_array_impl.h index 8159d6ed00..877b22f8d8 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -9,9 +9,8 @@ #include "redis_array.h" -RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); From a37038203cb988eaff36f139a205409a005c3748 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 16 Feb 2019 19:48:02 +0200 Subject: [PATCH 1099/1986] Persistent connections pool --- common.h | 24 +++++++++++++++-- library.c | 80 ++++++++++++++++++++++++++++++++++++++++--------------- redis.c | 28 +++++++++++++++++++ 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/common.h b/common.h index 34c84f5053..e5ec3b5997 100644 --- a/common.h +++ b/common.h @@ -68,6 +68,22 @@ zend_string_realloc(zend_string *s, size_t len, int persistent) return zstr; } +#define strpprintf zend_strpprintf + +static zend_string * +zend_strpprintf(size_t max_len, const char *format, ...) +{ + va_list ap; + zend_string *zstr; + + va_start(ap, format); + zstr = ecalloc(1, sizeof(*zstr)); + ZSTR_LEN(zstr) = vspprintf(&ZSTR_VAL(zstr), max_len, format, ap); + zstr->gc = 0x11; + va_end(ap); + return zstr; +} + #define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0) #define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) @@ -140,6 +156,8 @@ zend_hash_str_find(const HashTable *ht, const char *key, size_t len) return NULL; } +#define zend_hash_find_ptr(ht, s) zend_hash_str_find_ptr(ht, ZSTR_VAL(s), ZSTR_LEN(s)) + static zend_always_inline void * zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) { @@ -151,10 +169,12 @@ zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) return NULL; } +#define zend_hash_str_update_ptr(ht, str, len, pData) zend_hash_str_update_mem(ht, str, len, pData, sizeof(void *)) + static zend_always_inline void * -zend_hash_str_update_ptr(HashTable *ht, const char *str, size_t len, void *pData) +zend_hash_str_update_mem(HashTable *ht, const char *str, size_t len, void *pData, size_t size) { - if (zend_hash_update(ht, str, len + 1, (void *)&pData, sizeof(void *), NULL) == SUCCESS) { + if (zend_hash_update(ht, str, len + 1, (void *)&pData, size, NULL) == SUCCESS) { return pData; } return NULL; diff --git a/library.c b/library.c index 93a4460069..d799d87dc1 100644 --- a/library.c +++ b/library.c @@ -61,6 +61,8 @@ extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; +extern int le_redis_pconnect; + /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; @@ -1743,10 +1745,10 @@ redis_sock_create(char *host, int host_len, unsigned short port, PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) { struct timeval tv, read_tv, *tv_ptr = NULL; - char host[1024], *persistent_id = NULL; + zend_string *persistent_id = NULL; + char host[1024]; const char *fmtstr = "%s:%d"; int host_len, usocket = 0, err = 0; - php_netstream_data_t *sock; int tcp_flag = 1; #if (PHP_MAJOR_VERSION < 7) char *estr = NULL; @@ -1758,15 +1760,6 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock_disconnect(redis_sock, 0 TSRMLS_CC); } - tv.tv_sec = (time_t)redis_sock->timeout; - tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); - if(tv.tv_sec != 0 || tv.tv_usec != 0) { - tv_ptr = &tv; - } - - read_tv.tv_sec = (time_t)redis_sock->read_timeout; - read_tv.tv_usec = (int)((redis_sock->read_timeout-read_tv.tv_sec)*1000000); - if (ZSTR_VAL(redis_sock->host)[0] == '/' && redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", ZSTR_VAL(redis_sock->host)); usocket = 1; @@ -1785,21 +1778,46 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } if (redis_sock->persistent) { - if (redis_sock->persistent_id) { - spprintf(&persistent_id, 0, "phpredis:%s:%s", host, - ZSTR_VAL(redis_sock->persistent_id)); + if (INI_INT("redis.pconnect.pooling_enabled")) { + persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (le && zend_llist_count(le->ptr) > 0) { + redis_sock->stream = *(php_stream **)zend_llist_get_last(le->ptr); + zend_llist_remove_tail(le->ptr); + /* Check socket liveness using 0 second timeout */ + if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { + redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; + zend_string_release(persistent_id); + return SUCCESS; + } + php_stream_pclose(redis_sock->stream); + } + zend_string_release(persistent_id); + + gettimeofday(&tv, NULL); + persistent_id = strpprintf(0, "phpredis_%d%d", tv.tv_sec, tv.tv_usec); } else { - spprintf(&persistent_id, 0, "phpredis:%s:%f", host, - redis_sock->timeout); + if (redis_sock->persistent_id) { + persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id)); + } else { + persistent_id = strpprintf(0, "phpredis:%s:%f", host, redis_sock->timeout); + } } } + tv.tv_sec = (time_t)redis_sock->timeout; + tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); + if (tv.tv_sec != 0 || tv.tv_usec != 0) { + tv_ptr = &tv; + } + redis_sock->stream = php_stream_xport_create(host, host_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &estr, &err); + persistent_id ? ZSTR_VAL(persistent_id) : NULL, + tv_ptr, NULL, &estr, &err); if (persistent_id) { - efree(persistent_id); + zend_string_release(persistent_id); } if (!redis_sock->stream) { @@ -1812,12 +1830,12 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) zend_string_release(estr); #endif } - return -1; + return FAILURE; } /* Attempt to set TCP_NODELAY/TCP_KEEPALIVE if we're not using a unix socket. */ - sock = (php_netstream_data_t*)redis_sock->stream->abstract; if (!usocket) { + php_netstream_data_t *sock = (php_netstream_data_t*)redis_sock->stream->abstract; err = setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char*) &tcp_flag, sizeof(tcp_flag)); PHPREDIS_NOTUSED(err); err = setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char*) &redis_sock->tcp_keepalive, sizeof(redis_sock->tcp_keepalive)); @@ -1826,6 +1844,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) php_stream_auto_cleanup(redis_sock->stream); + read_tv.tv_sec = (time_t)redis_sock->read_timeout; + read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); + if (read_tv.tv_sec != 0 || read_tv.tv_usec != 0) { php_stream_set_option(redis_sock->stream,PHP_STREAM_OPTION_READ_TIMEOUT, 0, &read_tv); @@ -1835,7 +1856,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; - return 0; + return SUCCESS; } /** @@ -1869,6 +1890,23 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (redis_sock->persistent) { if (force) { php_stream_pclose(redis_sock->stream); + } else if (INI_INT("redis.pconnect.pooling_enabled")) { + zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (!le) { +#if (PHP_MAJOR_VERSION < 7) + le = ecalloc(1, sizeof(*le)); +#else + zend_resource res; + le = &res; +#endif + le->type = le_redis_pconnect; + le->ptr = pecalloc(1, sizeof(zend_llist), 1); + zend_llist_init(le->ptr, sizeof(void *), NULL, 1); + zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + } + zend_llist_prepend_element(le->ptr, &redis_sock->stream); + zend_string_release(persistent_id); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index 539889799a..1caac57e6b 100644 --- a/redis.c +++ b/redis.c @@ -53,6 +53,8 @@ zend_class_entry *redis_exception_ce; extern zend_function_entry redis_array_functions[]; extern zend_function_entry redis_cluster_functions[]; +int le_redis_pconnect; + PHP_INI_BEGIN() /* redis arrays */ PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL) @@ -77,6 +79,9 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL) + /* redis pconnect */ + PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "0", PHP_INI_ALL, NULL) + /* redis session */ PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL) @@ -742,6 +747,25 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } +static void +redis_pconnect_dtor(void *ptr TSRMLS_DC) +{ + php_stream_pclose(*(php_stream **)ptr); +} + +static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) +{ +#if (PHP_MAJOR_VERSION < 7) + zend_resource *res = rsrc; +#endif + + if (res->ptr) { + zend_llist_apply(res->ptr, redis_pconnect_dtor TSRMLS_CC); + zend_llist_destroy(res->ptr); + pefree(res->ptr, 1); + } +} + /** * PHP_MINIT_FUNCTION */ @@ -823,6 +847,10 @@ PHP_MINIT_FUNCTION(redis) php_session_register_module(&ps_mod_redis_cluster); #endif + /* Register resource destructors */ + le_redis_pconnect = zend_register_list_destructors_ex(NULL, redis_connections_pool_dtor, + "phpredis persistent connections pool", module_number); + return SUCCESS; } From c76e00fb25a005dc80d3e1d1ea2d64626ab155c2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 21:43:10 +0200 Subject: [PATCH 1100/1986] Fix memory leak on PHP 5 --- library.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/library.c b/library.c index d799d87dc1..73f22da609 100644 --- a/library.c +++ b/library.c @@ -1894,15 +1894,11 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { -#if (PHP_MAJOR_VERSION < 7) - le = ecalloc(1, sizeof(*le)); -#else - zend_resource res; - le = &res; -#endif + zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); + zend_llist_init(l, sizeof(void *), NULL, 1); + le = (void *)l + sizeof(*l); le->type = le_redis_pconnect; - le->ptr = pecalloc(1, sizeof(zend_llist), 1); - zend_llist_init(le->ptr, sizeof(void *), NULL, 1); + le->ptr = l; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); } zend_llist_prepend_element(le->ptr, &redis_sock->stream); From 40f79e43a45d6ecd7f24f24e8fa5908376014fb7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 20 Feb 2019 09:59:17 +0200 Subject: [PATCH 1101/1986] Update documentation. --- README.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 3ea45ba66f..4f4f835939 100644 --- a/README.markdown +++ b/README.markdown @@ -207,13 +207,15 @@ $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay ----- _**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. -The connection will not be closed on `close` or end of request until the php process ends. +The connection will not be closed on end of request until the php process ends. So be prepared for too many open FD's errors (specially on redis server side) when using persistent connections on many servers connecting to one redis server. Also more than one persistent connection can be made identified by either host + port + timeout or host + persistent_id or unix socket + timeout. +Starting from version 4.2.1, it became possible to use connection pooling by setting INI variable `redis.pconnect.pooling_enabled` to 1. + This feature is not available in threaded versions. `pconnect` and `popen` then working like their non persistent equivalents. From 0433dc03e87589c3a8b3bb7e630769ae02b78537 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 09:09:49 +0200 Subject: [PATCH 1102/1986] Issue #1514 `sizeof(void *)` isn't standard so change it to `sizeof(char *)` --- library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 73f22da609..c3e3307586 100644 --- a/library.c +++ b/library.c @@ -1895,8 +1895,8 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); - zend_llist_init(l, sizeof(void *), NULL, 1); - le = (void *)l + sizeof(*l); + zend_llist_init(l, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)l + sizeof(*l)); le->type = le_redis_pconnect; le->ptr = l; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); From 53f5cb438732ff1e37adc63ae87e72eedc0e2b29 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 09:42:50 +0200 Subject: [PATCH 1103/1986] Refactor connection pooling code. Move logic of searching+creating connection pool resource to `redis_sock_get_connection_pool`. Don't close streams in ZEND_RSRC_DTOR_FUNC because all of them stored in `EG(persistent_list)` and will be destroyed by PHP engine. --- library.c | 42 +++++++++++++++++++++++------------------- redis.c | 7 ------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/library.c b/library.c index c3e3307586..c55dc0ad6b 100644 --- a/library.c +++ b/library.c @@ -63,6 +63,23 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; +static zend_llist * +redis_sock_get_connection_pool(RedisSock *redis_sock) +{ + zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (!le) { + zend_llist *list = pecalloc(1, sizeof(*list) + sizeof(*le), 1); + zend_llist_init(list, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)list + sizeof(*list)); + le->type = le_redis_pconnect; + le->ptr = list; + zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + } + zend_string_release(persistent_id); + return le->ptr; +} + /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; @@ -1779,20 +1796,17 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); - zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); - if (le && zend_llist_count(le->ptr) > 0) { - redis_sock->stream = *(php_stream **)zend_llist_get_last(le->ptr); - zend_llist_remove_tail(le->ptr); + zend_llist *list = redis_sock_get_connection_pool(redis_sock); + if (zend_llist_count(list) > 0) { + redis_sock->stream = *(php_stream **)zend_llist_get_last(list); + zend_llist_remove_tail(list); /* Check socket liveness using 0 second timeout */ if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; - zend_string_release(persistent_id); return SUCCESS; } php_stream_pclose(redis_sock->stream); } - zend_string_release(persistent_id); gettimeofday(&tv, NULL); persistent_id = strpprintf(0, "phpredis_%d%d", tv.tv_sec, tv.tv_usec); @@ -1891,18 +1905,8 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); - zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); - if (!le) { - zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); - zend_llist_init(l, sizeof(php_stream *), NULL, 1); - le = (zend_resource *)((char *)l + sizeof(*l)); - le->type = le_redis_pconnect; - le->ptr = l; - zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); - } - zend_llist_prepend_element(le->ptr, &redis_sock->stream); - zend_string_release(persistent_id); + zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist_prepend_element(list, &redis_sock->stream); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index 1caac57e6b..5fce491700 100644 --- a/redis.c +++ b/redis.c @@ -747,12 +747,6 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } -static void -redis_pconnect_dtor(void *ptr TSRMLS_DC) -{ - php_stream_pclose(*(php_stream **)ptr); -} - static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) { #if (PHP_MAJOR_VERSION < 7) @@ -760,7 +754,6 @@ static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) #endif if (res->ptr) { - zend_llist_apply(res->ptr, redis_pconnect_dtor TSRMLS_CC); zend_llist_destroy(res->ptr); pefree(res->ptr, 1); } From 6cbe2a6df2374d4da5bda1c68f30fd6aae439dad Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 10:02:12 +0200 Subject: [PATCH 1104/1986] Fix compilation error on PHP5 --- library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index c55dc0ad6b..62d9df0dc9 100644 --- a/library.c +++ b/library.c @@ -64,7 +64,7 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; static zend_llist * -redis_sock_get_connection_pool(RedisSock *redis_sock) +redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) { zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); @@ -1796,7 +1796,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); if (zend_llist_count(list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(list); zend_llist_remove_tail(list); @@ -1905,7 +1905,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); zend_llist_prepend_element(list, &redis_sock->stream); } } else { From ef5972bb4d844d7d1765864e86a647ec6b1997b4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 10:35:29 +0200 Subject: [PATCH 1105/1986] Travis CI: run tests without igbinary if it can't be installed --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c84d018a1..0aad3ed9b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,8 +38,7 @@ addons: packages: clang before_install: - phpize - - pecl install igbinary - - ./configure --enable-redis-igbinary --enable-redis-lzf CFLAGS=-Wall + - pecl install igbinary && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf install: make install before_script: - gem install redis From 5de7a26179c911db0535883720e53e9dfc40bd4c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 13:32:44 +0200 Subject: [PATCH 1106/1986] Travis CI: igbinary 2.0.8 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0aad3ed9b9..513f367aee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ addons: packages: clang before_install: - phpize - - pecl install igbinary && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf + - pecl install igbinary-2.0.8 && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf install: make install before_script: - gem install redis From 64e6a57f5ff7cdf934661a8030101acdf69a4708 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 18 Feb 2019 14:33:29 +0200 Subject: [PATCH 1107/1986] Use zend_string for storing key hashing algorithm --- redis_array.c | 12 ++++++------ redis_array.h | 2 +- redis_array_impl.c | 22 ++++++++++++---------- redis_array_impl.h | 2 +- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/redis_array.c b/redis_array.c index 79cf0804a8..db00c10b93 100644 --- a/redis_array.c +++ b/redis_array.c @@ -160,7 +160,7 @@ redis_array_free(RedisArray *ra) zval_dtor(&ra->z_dist); /* Hashing algorithm */ - zval_dtor(&ra->z_algo); + if (ra->algorithm) zend_string_release(ra->algorithm); /* Delete pur commands */ zend_hash_destroy(ra->pure_cmds); @@ -272,13 +272,14 @@ redis_array_get(zval *id TSRMLS_DC) Public constructor */ PHP_METHOD(RedisArray, __construct) { - zval *z0, z_fun, z_dist, z_algo, *zpData, *z_opts = NULL; + zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; HashTable *hPrev = NULL, *hOpts = NULL; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0, read_timeout = 0.0; + zend_string *algorithm = NULL; redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { @@ -287,7 +288,6 @@ PHP_METHOD(RedisArray, __construct) ZVAL_NULL(&z_fun); ZVAL_NULL(&z_dist); - ZVAL_NULL(&z_algo); /* extract options */ if(z_opts) { hOpts = Z_ARRVAL_P(z_opts); @@ -312,7 +312,7 @@ PHP_METHOD(RedisArray, __construct) /* extract function name. */ if ((zpData = zend_hash_str_find(hOpts, "algorithm", sizeof("algorithm") - 1)) != NULL && Z_TYPE_P(zpData) == IS_STRING) { - ZVAL_ZVAL(&z_algo, zpData, 1, 0); + algorithm = zval_get_string(zpData); } /* extract index option. */ @@ -379,13 +379,13 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, &z_algo, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); break; default: WRONG_PARAM_COUNT; } - zval_dtor(&z_algo); + if (algorithm) zend_string_release(algorithm); zval_dtor(&z_dist); zval_dtor(&z_fun); diff --git a/redis_array.h b/redis_array.h index de44465426..3035a07135 100644 --- a/redis_array.h +++ b/redis_array.h @@ -59,7 +59,7 @@ typedef struct RedisArray_ { zend_bool pconnect; /* should we use pconnect */ zval z_fun; /* key extractor, callable */ zval z_dist; /* key distributor, callable */ - zval z_algo; /* key hashing algorithm name */ + zend_string *algorithm; /* key hashing algorithm name */ HashTable *pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ double read_timeout; /* socket read timeout */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 4ee72d8a0a..0ec7755c31 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -164,7 +164,7 @@ ra_find_name(const char *name) { /* laod array from INI settings */ RedisArray *ra_load_array(const char *name TSRMLS_DC) { - zval *z_data, z_fun, z_dist, z_algo; + zval *z_data, z_fun, z_dist; zval z_params_hosts; zval z_params_prev; zval z_params_funs; @@ -180,6 +180,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_consistent; RedisArray *ra = NULL; + zend_string *algorithm= NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; @@ -235,9 +236,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.algorithm")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_algo TSRMLS_CC); } - ZVAL_NULL(&z_algo); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_algo), name, name_len)) != NULL) { - ZVAL_ZVAL(&z_algo, z_data, 1, 0); + algorithm = zval_get_string(z_data); } /* find index option */ @@ -340,13 +340,15 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, &z_algo, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; } /* cleanup */ + if (algorithm) zend_string_release(algorithm); + zval_dtor(&z_params_hosts); zval_dtor(&z_params_prev); zval_dtor(&z_params_funs); @@ -360,7 +362,6 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); zval_dtor(&z_params_consistent); - zval_dtor(&z_algo); zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -408,7 +409,7 @@ ra_make_continuum(zend_string **hosts, int nb_hosts) } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC) { int i, count; RedisArray *ra; @@ -418,7 +419,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab /* create object */ ra = emalloc(sizeof(RedisArray)); ra->hosts = ecalloc(count, sizeof(*ra->hosts)); - ra->redis = ecalloc(count, sizeof(zval)); + ra->redis = ecalloc(count, sizeof(*ra->redis)); ra->count = 0; ra->z_multi_exec = NULL; ra->index = b_index; @@ -427,6 +428,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab ra->connect_timeout = connect_timeout; ra->read_timeout = read_timeout; ra->continuum = NULL; + ra->algorithm = NULL; if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { @@ -438,7 +440,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, z_algo, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); @@ -446,7 +448,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab /* Set hash function and distribtor if provided */ ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); ZVAL_ZVAL(&ra->z_dist, z_dist, 1, 0); - ZVAL_ZVAL(&ra->z_algo, z_algo, 1, 0); + if (algorithm) ra->algorithm = zend_string_copy(algorithm); /* init continuum */ if (consistent) { @@ -552,7 +554,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D const php_hash_ops *ops; /* hash */ - if (Z_TYPE(ra->z_algo) == IS_STRING && (ops = php_hash_fetch_ops(Z_STRVAL(ra->z_algo), Z_STRLEN(ra->z_algo))) != NULL) { + if (ra->algorithm && (ops = php_hash_fetch_ops(ZSTR_VAL(ra->algorithm), ZSTR_LEN(ra->algorithm))) != NULL) { void *ctx = emalloc(ops->context_size); unsigned char *digest = emalloc(ops->digest_size); diff --git a/redis_array_impl.h b/redis_array_impl.h index 43705ee591..8159d6ed00 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -11,7 +11,7 @@ RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); From 1b212690db4bae1eea68d50ff7fb7411e5faa36c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 16 Feb 2019 19:48:02 +0200 Subject: [PATCH 1108/1986] Persistent connections pool --- common.h | 24 +++++++++++++++-- library.c | 80 ++++++++++++++++++++++++++++++++++++++++--------------- redis.c | 28 +++++++++++++++++++ 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/common.h b/common.h index 34c84f5053..e5ec3b5997 100644 --- a/common.h +++ b/common.h @@ -68,6 +68,22 @@ zend_string_realloc(zend_string *s, size_t len, int persistent) return zstr; } +#define strpprintf zend_strpprintf + +static zend_string * +zend_strpprintf(size_t max_len, const char *format, ...) +{ + va_list ap; + zend_string *zstr; + + va_start(ap, format); + zstr = ecalloc(1, sizeof(*zstr)); + ZSTR_LEN(zstr) = vspprintf(&ZSTR_VAL(zstr), max_len, format, ap); + zstr->gc = 0x11; + va_end(ap); + return zstr; +} + #define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0) #define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) @@ -140,6 +156,8 @@ zend_hash_str_find(const HashTable *ht, const char *key, size_t len) return NULL; } +#define zend_hash_find_ptr(ht, s) zend_hash_str_find_ptr(ht, ZSTR_VAL(s), ZSTR_LEN(s)) + static zend_always_inline void * zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) { @@ -151,10 +169,12 @@ zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) return NULL; } +#define zend_hash_str_update_ptr(ht, str, len, pData) zend_hash_str_update_mem(ht, str, len, pData, sizeof(void *)) + static zend_always_inline void * -zend_hash_str_update_ptr(HashTable *ht, const char *str, size_t len, void *pData) +zend_hash_str_update_mem(HashTable *ht, const char *str, size_t len, void *pData, size_t size) { - if (zend_hash_update(ht, str, len + 1, (void *)&pData, sizeof(void *), NULL) == SUCCESS) { + if (zend_hash_update(ht, str, len + 1, (void *)&pData, size, NULL) == SUCCESS) { return pData; } return NULL; diff --git a/library.c b/library.c index 93a4460069..d799d87dc1 100644 --- a/library.c +++ b/library.c @@ -61,6 +61,8 @@ extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; +extern int le_redis_pconnect; + /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; @@ -1743,10 +1745,10 @@ redis_sock_create(char *host, int host_len, unsigned short port, PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) { struct timeval tv, read_tv, *tv_ptr = NULL; - char host[1024], *persistent_id = NULL; + zend_string *persistent_id = NULL; + char host[1024]; const char *fmtstr = "%s:%d"; int host_len, usocket = 0, err = 0; - php_netstream_data_t *sock; int tcp_flag = 1; #if (PHP_MAJOR_VERSION < 7) char *estr = NULL; @@ -1758,15 +1760,6 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock_disconnect(redis_sock, 0 TSRMLS_CC); } - tv.tv_sec = (time_t)redis_sock->timeout; - tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); - if(tv.tv_sec != 0 || tv.tv_usec != 0) { - tv_ptr = &tv; - } - - read_tv.tv_sec = (time_t)redis_sock->read_timeout; - read_tv.tv_usec = (int)((redis_sock->read_timeout-read_tv.tv_sec)*1000000); - if (ZSTR_VAL(redis_sock->host)[0] == '/' && redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", ZSTR_VAL(redis_sock->host)); usocket = 1; @@ -1785,21 +1778,46 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } if (redis_sock->persistent) { - if (redis_sock->persistent_id) { - spprintf(&persistent_id, 0, "phpredis:%s:%s", host, - ZSTR_VAL(redis_sock->persistent_id)); + if (INI_INT("redis.pconnect.pooling_enabled")) { + persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (le && zend_llist_count(le->ptr) > 0) { + redis_sock->stream = *(php_stream **)zend_llist_get_last(le->ptr); + zend_llist_remove_tail(le->ptr); + /* Check socket liveness using 0 second timeout */ + if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { + redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; + zend_string_release(persistent_id); + return SUCCESS; + } + php_stream_pclose(redis_sock->stream); + } + zend_string_release(persistent_id); + + gettimeofday(&tv, NULL); + persistent_id = strpprintf(0, "phpredis_%d%d", tv.tv_sec, tv.tv_usec); } else { - spprintf(&persistent_id, 0, "phpredis:%s:%f", host, - redis_sock->timeout); + if (redis_sock->persistent_id) { + persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id)); + } else { + persistent_id = strpprintf(0, "phpredis:%s:%f", host, redis_sock->timeout); + } } } + tv.tv_sec = (time_t)redis_sock->timeout; + tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); + if (tv.tv_sec != 0 || tv.tv_usec != 0) { + tv_ptr = &tv; + } + redis_sock->stream = php_stream_xport_create(host, host_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &estr, &err); + persistent_id ? ZSTR_VAL(persistent_id) : NULL, + tv_ptr, NULL, &estr, &err); if (persistent_id) { - efree(persistent_id); + zend_string_release(persistent_id); } if (!redis_sock->stream) { @@ -1812,12 +1830,12 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) zend_string_release(estr); #endif } - return -1; + return FAILURE; } /* Attempt to set TCP_NODELAY/TCP_KEEPALIVE if we're not using a unix socket. */ - sock = (php_netstream_data_t*)redis_sock->stream->abstract; if (!usocket) { + php_netstream_data_t *sock = (php_netstream_data_t*)redis_sock->stream->abstract; err = setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char*) &tcp_flag, sizeof(tcp_flag)); PHPREDIS_NOTUSED(err); err = setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char*) &redis_sock->tcp_keepalive, sizeof(redis_sock->tcp_keepalive)); @@ -1826,6 +1844,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) php_stream_auto_cleanup(redis_sock->stream); + read_tv.tv_sec = (time_t)redis_sock->read_timeout; + read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); + if (read_tv.tv_sec != 0 || read_tv.tv_usec != 0) { php_stream_set_option(redis_sock->stream,PHP_STREAM_OPTION_READ_TIMEOUT, 0, &read_tv); @@ -1835,7 +1856,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; - return 0; + return SUCCESS; } /** @@ -1869,6 +1890,23 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (redis_sock->persistent) { if (force) { php_stream_pclose(redis_sock->stream); + } else if (INI_INT("redis.pconnect.pooling_enabled")) { + zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (!le) { +#if (PHP_MAJOR_VERSION < 7) + le = ecalloc(1, sizeof(*le)); +#else + zend_resource res; + le = &res; +#endif + le->type = le_redis_pconnect; + le->ptr = pecalloc(1, sizeof(zend_llist), 1); + zend_llist_init(le->ptr, sizeof(void *), NULL, 1); + zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + } + zend_llist_prepend_element(le->ptr, &redis_sock->stream); + zend_string_release(persistent_id); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index 6265ac8062..1f793ec364 100644 --- a/redis.c +++ b/redis.c @@ -53,6 +53,8 @@ zend_class_entry *redis_exception_ce; extern zend_function_entry redis_array_functions[]; extern zend_function_entry redis_cluster_functions[]; +int le_redis_pconnect; + PHP_INI_BEGIN() /* redis arrays */ PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL) @@ -77,6 +79,9 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL) + /* redis pconnect */ + PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "0", PHP_INI_ALL, NULL) + /* redis session */ PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL) @@ -746,6 +751,25 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } +static void +redis_pconnect_dtor(void *ptr TSRMLS_DC) +{ + php_stream_pclose(*(php_stream **)ptr); +} + +static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) +{ +#if (PHP_MAJOR_VERSION < 7) + zend_resource *res = rsrc; +#endif + + if (res->ptr) { + zend_llist_apply(res->ptr, redis_pconnect_dtor TSRMLS_CC); + zend_llist_destroy(res->ptr); + pefree(res->ptr, 1); + } +} + /** * PHP_MINIT_FUNCTION */ @@ -827,6 +851,10 @@ PHP_MINIT_FUNCTION(redis) php_session_register_module(&ps_mod_redis_cluster); #endif + /* Register resource destructors */ + le_redis_pconnect = zend_register_list_destructors_ex(NULL, redis_connections_pool_dtor, + "phpredis persistent connections pool", module_number); + return SUCCESS; } From 5fa8f21b07013784d0ecdc3f9eb64e6cc22b7380 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 21:43:10 +0200 Subject: [PATCH 1109/1986] Fix memory leak on PHP 5 --- library.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/library.c b/library.c index d799d87dc1..73f22da609 100644 --- a/library.c +++ b/library.c @@ -1894,15 +1894,11 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { -#if (PHP_MAJOR_VERSION < 7) - le = ecalloc(1, sizeof(*le)); -#else - zend_resource res; - le = &res; -#endif + zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); + zend_llist_init(l, sizeof(void *), NULL, 1); + le = (void *)l + sizeof(*l); le->type = le_redis_pconnect; - le->ptr = pecalloc(1, sizeof(zend_llist), 1); - zend_llist_init(le->ptr, sizeof(void *), NULL, 1); + le->ptr = l; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); } zend_llist_prepend_element(le->ptr, &redis_sock->stream); From 992cd0ef86d44dcbb61e78b364b8268464a457b4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 20 Feb 2019 09:59:17 +0200 Subject: [PATCH 1110/1986] Update documentation. --- README.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 3ea45ba66f..4f4f835939 100644 --- a/README.markdown +++ b/README.markdown @@ -207,13 +207,15 @@ $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay ----- _**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. -The connection will not be closed on `close` or end of request until the php process ends. +The connection will not be closed on end of request until the php process ends. So be prepared for too many open FD's errors (specially on redis server side) when using persistent connections on many servers connecting to one redis server. Also more than one persistent connection can be made identified by either host + port + timeout or host + persistent_id or unix socket + timeout. +Starting from version 4.2.1, it became possible to use connection pooling by setting INI variable `redis.pconnect.pooling_enabled` to 1. + This feature is not available in threaded versions. `pconnect` and `popen` then working like their non persistent equivalents. From b957532d597431f4b5c4b3a1b97fb59848965dfa Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 09:09:49 +0200 Subject: [PATCH 1111/1986] Issue #1514 `sizeof(void *)` isn't standard so change it to `sizeof(char *)` --- library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 73f22da609..c3e3307586 100644 --- a/library.c +++ b/library.c @@ -1895,8 +1895,8 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); - zend_llist_init(l, sizeof(void *), NULL, 1); - le = (void *)l + sizeof(*l); + zend_llist_init(l, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)l + sizeof(*l)); le->type = le_redis_pconnect; le->ptr = l; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); From 376a202061c2a7364655f68f3851ba240a7097be Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 09:42:50 +0200 Subject: [PATCH 1112/1986] Refactor connection pooling code. Move logic of searching+creating connection pool resource to `redis_sock_get_connection_pool`. Don't close streams in ZEND_RSRC_DTOR_FUNC because all of them stored in `EG(persistent_list)` and will be destroyed by PHP engine. --- library.c | 42 +++++++++++++++++++++++------------------- redis.c | 7 ------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/library.c b/library.c index c3e3307586..c55dc0ad6b 100644 --- a/library.c +++ b/library.c @@ -63,6 +63,23 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; +static zend_llist * +redis_sock_get_connection_pool(RedisSock *redis_sock) +{ + zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (!le) { + zend_llist *list = pecalloc(1, sizeof(*list) + sizeof(*le), 1); + zend_llist_init(list, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)list + sizeof(*list)); + le->type = le_redis_pconnect; + le->ptr = list; + zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + } + zend_string_release(persistent_id); + return le->ptr; +} + /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; @@ -1779,20 +1796,17 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); - zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); - if (le && zend_llist_count(le->ptr) > 0) { - redis_sock->stream = *(php_stream **)zend_llist_get_last(le->ptr); - zend_llist_remove_tail(le->ptr); + zend_llist *list = redis_sock_get_connection_pool(redis_sock); + if (zend_llist_count(list) > 0) { + redis_sock->stream = *(php_stream **)zend_llist_get_last(list); + zend_llist_remove_tail(list); /* Check socket liveness using 0 second timeout */ if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; - zend_string_release(persistent_id); return SUCCESS; } php_stream_pclose(redis_sock->stream); } - zend_string_release(persistent_id); gettimeofday(&tv, NULL); persistent_id = strpprintf(0, "phpredis_%d%d", tv.tv_sec, tv.tv_usec); @@ -1891,18 +1905,8 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); - zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); - if (!le) { - zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); - zend_llist_init(l, sizeof(php_stream *), NULL, 1); - le = (zend_resource *)((char *)l + sizeof(*l)); - le->type = le_redis_pconnect; - le->ptr = l; - zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); - } - zend_llist_prepend_element(le->ptr, &redis_sock->stream); - zend_string_release(persistent_id); + zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist_prepend_element(list, &redis_sock->stream); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index 1f793ec364..dba9c8a62e 100644 --- a/redis.c +++ b/redis.c @@ -751,12 +751,6 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } -static void -redis_pconnect_dtor(void *ptr TSRMLS_DC) -{ - php_stream_pclose(*(php_stream **)ptr); -} - static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) { #if (PHP_MAJOR_VERSION < 7) @@ -764,7 +758,6 @@ static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) #endif if (res->ptr) { - zend_llist_apply(res->ptr, redis_pconnect_dtor TSRMLS_CC); zend_llist_destroy(res->ptr); pefree(res->ptr, 1); } From b773ec8a37bde27c02c7cc42033f50037480db84 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 10:02:12 +0200 Subject: [PATCH 1113/1986] Fix compilation error on PHP5 --- library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index c55dc0ad6b..62d9df0dc9 100644 --- a/library.c +++ b/library.c @@ -64,7 +64,7 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; static zend_llist * -redis_sock_get_connection_pool(RedisSock *redis_sock) +redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) { zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); @@ -1796,7 +1796,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); if (zend_llist_count(list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(list); zend_llist_remove_tail(list); @@ -1905,7 +1905,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); zend_llist_prepend_element(list, &redis_sock->stream); } } else { From c75b3b93a046a8a1b3ebc637d77867e8be2438cd Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 22:25:15 +0200 Subject: [PATCH 1114/1986] Connection limit for pool. --- common.h | 5 +++++ library.c | 42 ++++++++++++++++++++++++++++-------------- redis.c | 4 +++- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/common.h b/common.h index e5ec3b5997..bd3d696525 100644 --- a/common.h +++ b/common.h @@ -736,6 +736,11 @@ typedef struct { } RedisSock; /* }}} */ +typedef struct { + zend_llist list; + int nb_active; +} ConnectionPool; + #if (PHP_MAJOR_VERSION < 7) typedef struct { zend_object std; diff --git a/library.c b/library.c index 62d9df0dc9..bd46daded7 100644 --- a/library.c +++ b/library.c @@ -63,18 +63,19 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; -static zend_llist * +static ConnectionPool * redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) { zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { - zend_llist *list = pecalloc(1, sizeof(*list) + sizeof(*le), 1); - zend_llist_init(list, sizeof(php_stream *), NULL, 1); - le = (zend_resource *)((char *)list + sizeof(*list)); + ConnectionPool *p = pecalloc(1, sizeof(*p) + sizeof(*le), 1); + zend_llist_init(&p->list, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)p + sizeof(*p)); le->type = le_redis_pconnect; - le->ptr = list; + le->ptr = p; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + p->nb_active = 0; } zend_string_release(persistent_id); return le->ptr; @@ -1765,8 +1766,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) zend_string *persistent_id = NULL; char host[1024]; const char *fmtstr = "%s:%d"; - int host_len, usocket = 0, err = 0; - int tcp_flag = 1; + int host_len, usocket = 0, err = 0, tcp_flag = 1; + ConnectionPool *p = NULL; #if (PHP_MAJOR_VERSION < 7) char *estr = NULL; #else @@ -1796,16 +1797,23 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); - if (zend_llist_count(list) > 0) { - redis_sock->stream = *(php_stream **)zend_llist_get_last(list); - zend_llist_remove_tail(list); + p = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); + if (zend_llist_count(&p->list) > 0) { + redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list); + zend_llist_remove_tail(&p->list); /* Check socket liveness using 0 second timeout */ if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; return SUCCESS; } php_stream_pclose(redis_sock->stream); + p->nb_active--; + } + + int limit = INI_INT("redis.pconnect.connection_limit"); + if (limit > 0 && p->nb_active >= limit) { + redis_sock_set_err(redis_sock, "Connection limit reached", sizeof("Connection limit reached") - 1); + return FAILURE; } gettimeofday(&tv, NULL); @@ -1847,6 +1855,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) return FAILURE; } + if (p) p->nb_active++; + /* Attempt to set TCP_NODELAY/TCP_KEEPALIVE if we're not using a unix socket. */ if (!usocket) { php_netstream_data_t *sock = (php_netstream_data_t*)redis_sock->stream->abstract; @@ -1902,11 +1912,15 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) return FAILURE; } else if (redis_sock->stream) { if (redis_sock->persistent) { + ConnectionPool *p = NULL; + if (INI_INT("redis.pconnect.pooling_enabled")) { + p = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); + } if (force) { php_stream_pclose(redis_sock->stream); - } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); - zend_llist_prepend_element(list, &redis_sock->stream); + if (p) p->nb_active--; + } else if (p) { + zend_llist_prepend_element(&p->list, &redis_sock->stream); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index 5fce491700..53dc4ce9ed 100644 --- a/redis.c +++ b/redis.c @@ -81,6 +81,7 @@ PHP_INI_BEGIN() /* redis pconnect */ PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL) /* redis session */ PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL) @@ -754,7 +755,8 @@ static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) #endif if (res->ptr) { - zend_llist_destroy(res->ptr); + ConnectionPool *p = res->ptr; + zend_llist_destroy(&p->list); pefree(res->ptr, 1); } } From 6f70bcda0006d598846658d98c656c2964f1e476 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Feb 2019 23:57:18 +0200 Subject: [PATCH 1115/1986] TravisCI: enable connection pooling --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 513f367aee..64f5f9e032 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,7 @@ before_script: # but --cluster is an unknown option for travis trusty - echo yes | ruby redis-trib.rb create --replicas 3 $(seq -f 127.0.0.1:%g 7000 7011) - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - echo 'redis.pconnect.pooling_enabled = 1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini script: - php tests/TestRedis.php --class Redis - php tests/TestRedis.php --class RedisArray From aa9c44cc64d71ac87b8430f055a1b7b98cdc56bf Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 24 Feb 2019 21:43:44 +0200 Subject: [PATCH 1116/1986] Fix review comments --- common.h | 16 ---------------- library.c | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/common.h b/common.h index bd3d696525..31bf3b0f2d 100644 --- a/common.h +++ b/common.h @@ -68,22 +68,6 @@ zend_string_realloc(zend_string *s, size_t len, int persistent) return zstr; } -#define strpprintf zend_strpprintf - -static zend_string * -zend_strpprintf(size_t max_len, const char *format, ...) -{ - va_list ap; - zend_string *zstr; - - va_start(ap, format); - zstr = ecalloc(1, sizeof(*zstr)); - ZSTR_LEN(zstr) = vspprintf(&ZSTR_VAL(zstr), max_len, format, ap); - zstr->gc = 0x11; - va_end(ap); - return zstr; -} - #define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0) #define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) diff --git a/library.c b/library.c index bd46daded7..10ddae749c 100644 --- a/library.c +++ b/library.c @@ -56,6 +56,23 @@ int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *) = &add_assoc_zval_ex; void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC) = &php_var_serialize; int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; + +#define strpprintf zend_strpprintf + +static zend_string * +zend_strpprintf(size_t max_len, const char *format, ...) +{ + va_list ap; + zend_string *zstr; + + va_start(ap, format); + zstr = ecalloc(1, sizeof(*zstr)); + ZSTR_LEN(zstr) = vspprintf(&ZSTR_VAL(zstr), max_len, format, ap); + zstr->gc = 0x11; + va_end(ap); + return zstr; +} + #endif extern zend_class_entry *redis_ce; @@ -75,7 +92,6 @@ redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) le->type = le_redis_pconnect; le->ptr = p; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); - p->nb_active = 0; } zend_string_release(persistent_id); return le->ptr; From af7528d8e7f4a99ffd3a272f80caa2048ef8421c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 26 Feb 2019 10:05:55 +0200 Subject: [PATCH 1117/1986] Add documentation for RedisArray auth --- arrays.markdown | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arrays.markdown b/arrays.markdown index cd6394d159..7171c2a261 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -75,6 +75,12 @@ This option applies to main and previous ring if specified. $ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("consistent" => true)); +#### Specifying the "auth" parameter +The value is string and used to specify the password for authenticate with the server prior to sending commands +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("auth" => "mysecretpassword"));
+
+ #### Defining arrays in Redis.ini Because php.ini parameters must be pre-defined, Redis Arrays must all share the same .ini settings. @@ -91,6 +97,9 @@ ini_set('redis.arrays.functions', 'users=user_hash'); // use index only for users ini_set('redis.arrays.index', 'users=1,friends=0'); + +// use password for authentication +ini_set('redis.arrays.auth', 'users=mysecretpassword') ## Usage From 339cfa2bb6da64afc70958503a203f21db145cad Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 26 Feb 2019 17:44:08 +0200 Subject: [PATCH 1118/1986] Fix RedisArray authentication --- redis_array_impl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index c7e5c2b922..b52d5b03d4 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -83,7 +83,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in if (!b_lazy_connect) { /* connect */ - if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { + if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0 || (auth && redis_sock_auth(redis->sock TSRMLS_CC) < 0)) { zval_dtor(&z_cons); ra->count = ++i; return NULL; From 6b411aa88b9c993627b00c9dca84a3449f5f07ca Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 27 Feb 2019 15:23:25 +0200 Subject: [PATCH 1119/1986] Issue #1508 Wrap all calls of `call_user_function` into `ra_call_user_function` where AUTH command sended before function call. --- redis_array.c | 27 ++++++++++++++++++++------- redis_array.h | 1 + redis_array_impl.c | 46 +++++++++++++++++++++++----------------------- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/redis_array.c b/redis_array.c index e0a3c9ffde..b07e2349a5 100644 --- a/redis_array.c +++ b/redis_array.c @@ -268,6 +268,19 @@ redis_array_get(zval *id TSRMLS_DC) return NULL; } +PHP_REDIS_API int +ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[] TSRMLS_DC) +{ + if (object) { + redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object); + if (redis->sock->auth && redis->sock->status != REDIS_SOCK_STATUS_CONNECTED) { + redis_sock_server_open(redis->sock TSRMLS_CC); + redis_sock_auth(redis->sock TSRMLS_CC); + } + } + return call_user_function(function_table, object, function_name, retval_ptr, param_count, params); +} + /* {{{ proto RedisArray RedisArray::__construct() Public constructor */ PHP_METHOD(RedisArray, __construct) @@ -450,7 +463,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* multi/exec */ if(ra->z_multi_exec) { - call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); + ra_call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs TSRMLS_CC); zval_dtor(return_value); zval_dtor(&z_fun); for (i = 0; i < argc; ++i) { @@ -468,7 +481,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* add MULTI + SADD */ ra_index_multi(redis_inst, MULTI TSRMLS_CC); /* call using discarded temp value and extract exec results after. */ - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); + ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC); zval_dtor(return_value); /* add keys to index. */ @@ -477,7 +490,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* call EXEC */ ra_index_exec(redis_inst, return_value, 0 TSRMLS_CC); } else { /* call directly through. */ - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); + ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC); if (!b_write_cmd) { /* check if we have an error. */ @@ -703,7 +716,7 @@ multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int a MAKE_STD_ZVAL(z_tmp); #endif /* Call each node in turn */ - call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, z_tmp, argc, argv); + ra_call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, z_tmp, argc, argv TSRMLS_CC); /* Add the result for this host */ add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), z_tmp); @@ -1047,7 +1060,7 @@ PHP_METHOD(RedisArray, mget) /* prepare call */ ZVAL_STRINGL(&z_fun, "MGET", 4); /* call MGET on the node */ - call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); zval_dtor(&z_fun); /* cleanup args array */ @@ -1204,7 +1217,7 @@ PHP_METHOD(RedisArray, mset) ZVAL_STRINGL(&z_fun, "MSET", 4); /* call */ - call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); zval_dtor(&z_fun); zval_dtor(&z_ret); @@ -1346,7 +1359,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { } /* call */ - call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); if(ra->index) { zval_dtor(&z_ret); diff --git a/redis_array.h b/redis_array.h index 3035a07135..120d067298 100644 --- a/redis_array.h +++ b/redis_array.h @@ -75,5 +75,6 @@ zend_object *create_redis_array_object(zend_class_entry *ce TSRMLS_DC); void free_redis_array_object(zend_object *object); #endif +PHP_REDIS_API int ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[] TSRMLS_DC); #endif diff --git a/redis_array_impl.c b/redis_array_impl.c index b52d5b03d4..edb0f0063a 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -493,7 +493,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); - call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); + ra_call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv TSRMLS_CC); if (Z_TYPE(z_ret) == IS_STRING) { #if (PHP_MAJOR_VERSION < 7) @@ -542,7 +542,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); - call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv); + ra_call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv TSRMLS_CC); ret = (Z_TYPE(z_ret) == IS_LONG) ? Z_LVAL(z_ret) : -1; @@ -640,7 +640,7 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { /* run MULTI */ ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); ZVAL_LONG(&z_args[0], multi_value); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args TSRMLS_CC); zval_dtor(&z_fun_multi); zval_dtor(&z_ret); } @@ -670,7 +670,7 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { } /* run cmd */ - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args TSRMLS_CC); zval_dtor(&z_args[0]); zval_dtor(&z_fun); @@ -731,7 +731,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { ZVAL_STRINGL(&z_args[1], key, key_len); /* run SADD */ - call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args TSRMLS_CC); zval_dtor(&z_fun_sadd); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); @@ -745,7 +745,7 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { /* run EXEC */ ZVAL_STRINGL(&z_fun_exec, "EXEC", 4); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL TSRMLS_CC); zval_dtor(&z_fun_exec); /* extract first element of exec array and put into return_value. */ @@ -772,7 +772,7 @@ ra_index_discard(zval *z_redis, zval *return_value TSRMLS_DC) { /* run DISCARD */ ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL TSRMLS_CC); zval_dtor(&z_fun_discard); zval_dtor(&z_ret); @@ -785,7 +785,7 @@ ra_index_unwatch(zval *z_redis, zval *return_value TSRMLS_DC) { /* run UNWATCH */ ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL TSRMLS_CC); zval_dtor(&z_fun_unwatch); zval_dtor(&z_ret); @@ -825,14 +825,14 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TYPE", 4); - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg TSRMLS_CC); zval_dtor(&z_fun); zval_dtor(&z_ret); /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TTL", 3); - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg TSRMLS_CC); zval_dtor(&z_fun); zval_dtor(&z_ret); @@ -864,7 +864,7 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); ZVAL_STRINGL(&z_args[1], key, key_len); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_fun_srem); @@ -886,7 +886,7 @@ ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { /* run DEL on source */ ZVAL_STRINGL(&z_fun_del, "DEL", 3); ZVAL_STRINGL(&z_args[0], key, key_len); - call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args TSRMLS_CC); zval_dtor(&z_fun_del); zval_dtor(&z_args[0]); zval_dtor(&z_ret); @@ -911,7 +911,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6); ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_LONG(&z_args[1], ttl); - call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args TSRMLS_CC); zval_dtor(&z_fun_expire); zval_dtor(&z_args[0]); zval_dtor(&z_ret); @@ -935,7 +935,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS ZVAL_STRINGL(&z_args[1], "0", 1); ZVAL_STRINGL(&z_args[2], "-1", 2); ZVAL_BOOL(&z_args[3], 1); - call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args TSRMLS_CC); zval_dtor(&z_fun_zrange); zval_dtor(&z_args[2]); zval_dtor(&z_args[1]); @@ -973,7 +973,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run ZADD on target */ ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4); - call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args TSRMLS_CC); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); @@ -1000,7 +1000,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl /* run GET on source */ ZVAL_STRINGL(&z_fun_get, "GET", 3); ZVAL_STRINGL(&z_args[0], key, key_len); - call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args TSRMLS_CC); zval_dtor(&z_fun_get); if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ @@ -1016,14 +1016,14 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl ZVAL_LONG(&z_args[1], ttl); ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous call */ - call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_args[2]); } else { ZVAL_STRINGL(&z_fun_set, "SET", 3); ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous return value */ - call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_args[1]); } @@ -1041,7 +1041,7 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run HGETALL on source */ ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); - call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args TSRMLS_CC); zval_dtor(&z_fun_hgetall); if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ @@ -1053,7 +1053,7 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run HMSET on target */ ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); - call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args TSRMLS_CC); zval_dtor(&z_fun_hmset); zval_dtor(&z_ret_dest); @@ -1088,7 +1088,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, ZVAL_STRING(&z_retrieve_args[i], cmd_list[i]); } - call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_fun_retrieve); @@ -1120,7 +1120,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, /* Clean up our input return value */ zval_dtor(&z_ret); - call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_fun_sadd); @@ -1263,7 +1263,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool ZVAL_STRING(&z_argv, "*"); } ZVAL_NULL(&z_ret); - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv TSRMLS_CC); zval_dtor(&z_argv); zval_dtor(&z_fun); From 3ce9d6b1623be5b29b0a211cdbc69e355a34397d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 1 Mar 2019 09:22:11 +0200 Subject: [PATCH 1120/1986] Update documentation Fix minimum Redis server version required for session locking. --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 4f4f835939..7d38fc2c4a 100644 --- a/README.markdown +++ b/README.markdown @@ -68,7 +68,7 @@ session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeou * database (integer): selects a different database. Sessions have a lifetime expressed in seconds and stored in the INI variable "session.gc_maxlifetime". You can change it with [`ini_set()`](http://php.net/ini_set). -The session handler requires a version of Redis with the `SETEX` command (at least 2.0). +The session handler requires a version of Redis supporting `EX` and `NX` options of `SET` command (at least 2.6.12). phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0`. ### Session locking From 99e67ab696306bf0a5d715d4866f74a6f49d1d1b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 10:35:29 +0200 Subject: [PATCH 1121/1986] Travis CI: run tests without igbinary if it can't be installed --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4829561f76..4aa3137280 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,8 +26,7 @@ addons: packages: clang before_install: - phpize - - pecl install igbinary - - ./configure --enable-redis-igbinary --enable-redis-lzf CFLAGS=-Wall + - pecl install igbinary && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf install: make install before_script: - gem install redis From 0c02d5762b285063a7cfd8626a0545f237bc452d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 13:32:44 +0200 Subject: [PATCH 1122/1986] Travis CI: igbinary 2.0.8 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4aa3137280..5815e0f7ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ addons: packages: clang before_install: - phpize - - pecl install igbinary && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf + - pecl install igbinary-2.0.8 && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf install: make install before_script: - gem install redis From 0d2dd169fac48f09fb4fbea3268e06b61d4d0a0c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 18 Feb 2019 14:33:29 +0200 Subject: [PATCH 1123/1986] Use zend_string for storing key hashing algorithm --- redis_array.c | 12 ++++++------ redis_array.h | 2 +- redis_array_impl.c | 22 ++++++++++++---------- redis_array_impl.h | 2 +- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/redis_array.c b/redis_array.c index 37b31484d1..1641950bff 100644 --- a/redis_array.c +++ b/redis_array.c @@ -160,7 +160,7 @@ redis_array_free(RedisArray *ra) zval_dtor(&ra->z_dist); /* Hashing algorithm */ - zval_dtor(&ra->z_algo); + if (ra->algorithm) zend_string_release(ra->algorithm); /* Delete pur commands */ zend_hash_destroy(ra->pure_cmds); @@ -226,13 +226,14 @@ redis_array_get(zval *id TSRMLS_DC) Public constructor */ PHP_METHOD(RedisArray, __construct) { - zval *z0, z_fun, z_dist, z_algo, *zpData, *z_opts = NULL; + zval *z0, z_fun, z_dist, *zpData, *z_opts = NULL; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; HashTable *hPrev = NULL, *hOpts = NULL; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0, read_timeout = 0.0; + zend_string *algorithm = NULL; redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { @@ -241,7 +242,6 @@ PHP_METHOD(RedisArray, __construct) ZVAL_NULL(&z_fun); ZVAL_NULL(&z_dist); - ZVAL_NULL(&z_algo); /* extract options */ if(z_opts) { hOpts = Z_ARRVAL_P(z_opts); @@ -266,7 +266,7 @@ PHP_METHOD(RedisArray, __construct) /* extract function name. */ if ((zpData = zend_hash_str_find(hOpts, "algorithm", sizeof("algorithm") - 1)) != NULL && Z_TYPE_P(zpData) == IS_STRING) { - ZVAL_ZVAL(&z_algo, zpData, 1, 0); + algorithm = zval_get_string(zpData); } /* extract index option. */ @@ -333,13 +333,13 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, &z_algo, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); break; default: WRONG_PARAM_COUNT; } - zval_dtor(&z_algo); + if (algorithm) zend_string_release(algorithm); zval_dtor(&z_dist); zval_dtor(&z_fun); diff --git a/redis_array.h b/redis_array.h index 1ee192fae7..05cf5ad6ba 100644 --- a/redis_array.h +++ b/redis_array.h @@ -58,7 +58,7 @@ typedef struct RedisArray_ { zend_bool pconnect; /* should we use pconnect */ zval z_fun; /* key extractor, callable */ zval z_dist; /* key distributor, callable */ - zval z_algo; /* key hashing algorithm name */ + zend_string *algorithm; /* key hashing algorithm name */ HashTable *pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ double read_timeout; /* socket read timeout */ diff --git a/redis_array_impl.c b/redis_array_impl.c index 918359c5e5..2c5b6d4899 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -161,7 +161,7 @@ ra_find_name(const char *name) { /* laod array from INI settings */ RedisArray *ra_load_array(const char *name TSRMLS_DC) { - zval *z_data, z_fun, z_dist, z_algo; + zval *z_data, z_fun, z_dist; zval z_params_hosts; zval z_params_prev; zval z_params_funs; @@ -177,6 +177,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_consistent; RedisArray *ra = NULL; + zend_string *algorithm= NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; @@ -232,9 +233,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { if ((iptr = INI_STR("redis.arrays.algorithm")) != NULL) { sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_algo TSRMLS_CC); } - ZVAL_NULL(&z_algo); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_algo), name, name_len)) != NULL) { - ZVAL_ZVAL(&z_algo, z_data, 1, 0); + algorithm = zval_get_string(z_data); } /* find index option */ @@ -337,13 +337,15 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, &z_algo, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; } /* cleanup */ + if (algorithm) zend_string_release(algorithm); + zval_dtor(&z_params_hosts); zval_dtor(&z_params_prev); zval_dtor(&z_params_funs); @@ -357,7 +359,6 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); zval_dtor(&z_params_consistent); - zval_dtor(&z_algo); zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -405,7 +406,7 @@ ra_make_continuum(zend_string **hosts, int nb_hosts) } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC) { int i, count; RedisArray *ra; @@ -415,7 +416,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab /* create object */ ra = emalloc(sizeof(RedisArray)); ra->hosts = ecalloc(count, sizeof(*ra->hosts)); - ra->redis = ecalloc(count, sizeof(zval)); + ra->redis = ecalloc(count, sizeof(*ra->redis)); ra->count = 0; ra->z_multi_exec = NULL; ra->index = b_index; @@ -424,6 +425,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab ra->connect_timeout = connect_timeout; ra->read_timeout = read_timeout; ra->continuum = NULL; + ra->algorithm = NULL; if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { @@ -435,7 +437,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, z_algo, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); @@ -443,7 +445,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTab /* Set hash function and distribtor if provided */ ZVAL_ZVAL(&ra->z_fun, z_fun, 1, 0); ZVAL_ZVAL(&ra->z_dist, z_dist, 1, 0); - ZVAL_ZVAL(&ra->z_algo, z_algo, 1, 0); + if (algorithm) ra->algorithm = zend_string_copy(algorithm); /* init continuum */ if (consistent) { @@ -537,7 +539,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D const php_hash_ops *ops; /* hash */ - if (Z_TYPE(ra->z_algo) == IS_STRING && (ops = php_hash_fetch_ops(Z_STRVAL(ra->z_algo), Z_STRLEN(ra->z_algo))) != NULL) { + if (ra->algorithm && (ops = php_hash_fetch_ops(ZSTR_VAL(ra->algorithm), ZSTR_LEN(ra->algorithm))) != NULL) { void *ctx = emalloc(ops->context_size); unsigned char *digest = emalloc(ops->digest_size); diff --git a/redis_array_impl.h b/redis_array_impl.h index 43705ee591..8159d6ed00 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -11,7 +11,7 @@ RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, zval *z_algo, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); From 5053a9e7e47b87ead0ad2818e9186c218fe03a09 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 17 Feb 2019 21:49:25 +0200 Subject: [PATCH 1124/1986] RedisArray auth. Issue #1508 --- redis.c | 1 + redis_array.c | 10 ++++++++-- redis_array_impl.c | 30 ++++++++++++++++++++++-------- redis_array_impl.h | 3 +-- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/redis.c b/redis.c index f0e386d98d..d94b5a6d0a 100644 --- a/redis.c +++ b/redis.c @@ -58,6 +58,7 @@ extern zend_function_entry redis_cluster_functions[]; PHP_INI_BEGIN() /* redis arrays */ PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.arrays.auth", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.autorehash", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.connecttimeout", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL) diff --git a/redis_array.c b/redis_array.c index 1641950bff..0b450a9283 100644 --- a/redis_array.c +++ b/redis_array.c @@ -233,7 +233,7 @@ PHP_METHOD(RedisArray, __construct) long l_retry_interval = 0; zend_bool b_lazy_connect = 0; double d_connect_timeout = 0, read_timeout = 0.0; - zend_string *algorithm = NULL; + zend_string *algorithm = NULL, *auth = NULL; redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { @@ -324,6 +324,11 @@ PHP_METHOD(RedisArray, __construct) if ((zpData = zend_hash_str_find(hOpts, "consistent", sizeof("consistent") - 1)) != NULL) { consistent = zval_is_true(zpData); } + + /* auth */ + if ((zpData = zend_hash_str_find(hOpts, "auth", sizeof("auth") - 1)) != NULL) { + auth = zval_get_string(zpData); + } } /* extract either name of list of hosts from z0 */ @@ -333,13 +338,14 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC); break; default: WRONG_PARAM_COUNT; } if (algorithm) zend_string_release(algorithm); + if (auth) zend_string_release(auth); zval_dtor(&z_dist); zval_dtor(&z_fun); diff --git a/redis_array_impl.c b/redis_array_impl.c index 2c5b6d4899..b3fed26e4b 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -32,8 +32,8 @@ extern zend_class_entry *redis_ce; -RedisArray* -ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) +static RedisArray * +ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) { int i = 0, host_len; char *host, *p; @@ -74,6 +74,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b /* create socket */ redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval); + /* copy password is specified */ + if (auth) redis->sock->auth = zend_string_copy(auth); + if (!b_lazy_connect) { /* connect */ @@ -175,9 +178,10 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_read_timeout; zval z_params_lazy_connect; zval z_params_consistent; + zval z_params_auth; RedisArray *ra = NULL; - zend_string *algorithm= NULL; + zend_string *algorithm = NULL, *auth = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; @@ -335,9 +339,17 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } } + /* find auth option */ + array_init(&z_params_auth); + if ((iptr = INI_STR("redis.arrays.auth")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_auth TSRMLS_CC); + } + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_auth), name, name_len)) != NULL) { + auth = zval_get_string(z_data); + } /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; @@ -345,6 +357,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* cleanup */ if (algorithm) zend_string_release(algorithm); + if (auth) zend_string_release(auth); zval_dtor(&z_params_hosts); zval_dtor(&z_params_prev); @@ -359,6 +372,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); zval_dtor(&z_params_consistent); + zval_dtor(&z_params_auth); zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -406,8 +420,8 @@ ra_make_continuum(zend_string **hosts, int nb_hosts) } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC) { - +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth TSRMLS_DC) +{ int i, count; RedisArray *ra; @@ -427,7 +441,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->continuum = NULL; ra->algorithm = NULL; - if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { + if (ra_load_hosts(ra, hosts, auth, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { zval_dtor(&ra->redis[i]); zend_string_release(ra->hosts[i]); @@ -437,7 +451,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); diff --git a/redis_array_impl.h b/redis_array_impl.h index 8159d6ed00..877b22f8d8 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -9,9 +9,8 @@ #include "redis_array.h" -RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); From 0f38afa2e2ed56ae3cbdfaf0f6a245cdb9a8eff5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 26 Feb 2019 17:44:08 +0200 Subject: [PATCH 1125/1986] Fix RedisArray authentication --- redis_array_impl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_array_impl.c b/redis_array_impl.c index b3fed26e4b..f152989f31 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -80,7 +80,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in if (!b_lazy_connect) { /* connect */ - if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { + if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0 || (auth && redis_sock_auth(redis->sock TSRMLS_CC) < 0)) { zval_dtor(&z_cons); ra->count = ++i; return NULL; From 112c77e3a1967ff7c655fd8fcc283d4b4888a5e6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 27 Feb 2019 15:23:25 +0200 Subject: [PATCH 1126/1986] Issue #1508 Wrap all calls of `call_user_function` into `ra_call_user_function` where AUTH command sended before function call. --- redis_array.c | 29 +++++++++++++++++++++-------- redis_array.h | 2 ++ redis_array_impl.c | 46 +++++++++++++++++++++++----------------------- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/redis_array.c b/redis_array.c index 37b31484d1..c332f6fc04 100644 --- a/redis_array.c +++ b/redis_array.c @@ -222,6 +222,19 @@ redis_array_get(zval *id TSRMLS_DC) return NULL; } +PHP_REDIS_API int +ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[] TSRMLS_DC) +{ + if (object) { + redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object); + if (redis->sock->auth && redis->sock->status != REDIS_SOCK_STATUS_CONNECTED) { + redis_sock_server_open(redis->sock TSRMLS_CC); + redis_sock_auth(redis->sock TSRMLS_CC); + } + } + return call_user_function(function_table, object, function_name, retval_ptr, param_count, params); +} + /* {{{ proto RedisArray RedisArray::__construct() Public constructor */ PHP_METHOD(RedisArray, __construct) @@ -398,7 +411,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* multi/exec */ if(ra->z_multi_exec) { - call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); + ra_call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs TSRMLS_CC); zval_dtor(return_value); zval_dtor(&z_fun); for (i = 0; i < argc; ++i) { @@ -416,7 +429,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* add MULTI + SADD */ ra_index_multi(redis_inst, MULTI TSRMLS_CC); /* call using discarded temp value and extract exec results after. */ - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); + ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC); zval_dtor(return_value); /* add keys to index. */ @@ -425,7 +438,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* call EXEC */ ra_index_exec(redis_inst, return_value, 0 TSRMLS_CC); } else { /* call directly through. */ - call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); + ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC); if (!b_write_cmd) { /* check if we have an error. */ @@ -634,7 +647,7 @@ PHP_METHOD(RedisArray, _continuum) static void multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int argc, zval *argv TSRMLS_DC) { - zval z_arg; + zval z_arg, z_tmp; int i; /* Init our array return */ @@ -643,7 +656,7 @@ multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int a /* Iterate our RedisArray nodes */ for (i = 0; i < ra->count; ++i) { /* Call each node in turn */ - call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_arg, argc, argv); + ra_call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_tmp, argc, argv TSRMLS_CC); /* Add the result for this host */ add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), &z_arg); @@ -953,7 +966,7 @@ PHP_METHOD(RedisArray, mget) /* prepare call */ ZVAL_STRINGL(&z_fun, "MGET", 4); /* call MGET on the node */ - call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); zval_dtor(&z_fun); /* cleanup args array */ @@ -1097,7 +1110,7 @@ PHP_METHOD(RedisArray, mset) ZVAL_STRINGL(&z_fun, "MSET", 4); /* call */ - call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); zval_dtor(&z_fun); zval_dtor(&z_ret); @@ -1229,7 +1242,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { } /* call */ - call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); if(ra->index) { zval_dtor(&z_ret); diff --git a/redis_array.h b/redis_array.h index 1ee192fae7..55538860d7 100644 --- a/redis_array.h +++ b/redis_array.h @@ -69,4 +69,6 @@ typedef struct RedisArray_ { zend_object *create_redis_array_object(zend_class_entry *ce TSRMLS_DC); void free_redis_array_object(zend_object *object); +PHP_REDIS_API int ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[] TSRMLS_DC); + #endif diff --git a/redis_array_impl.c b/redis_array_impl.c index 918359c5e5..c879d094f0 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -470,7 +470,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); - call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); + ra_call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv TSRMLS_CC); if (Z_TYPE(z_ret) == IS_STRING) { out = zval_get_string(&z_ret); @@ -511,7 +511,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); - call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv); + ra_call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv TSRMLS_CC); ret = (Z_TYPE(z_ret) == IS_LONG) ? Z_LVAL(z_ret) : -1; @@ -609,7 +609,7 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { /* run MULTI */ ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); ZVAL_LONG(&z_args[0], multi_value); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args TSRMLS_CC); zval_dtor(&z_fun_multi); zval_dtor(&z_ret); } @@ -639,7 +639,7 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { } /* run cmd */ - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args TSRMLS_CC); zval_dtor(&z_args[0]); zval_dtor(&z_fun); @@ -695,7 +695,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { ZVAL_STRINGL(&z_args[1], key, key_len); /* run SADD */ - call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args TSRMLS_CC); zval_dtor(&z_fun_sadd); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); @@ -709,7 +709,7 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { /* run EXEC */ ZVAL_STRINGL(&z_fun_exec, "EXEC", 4); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL TSRMLS_CC); zval_dtor(&z_fun_exec); /* extract first element of exec array and put into return_value. */ @@ -736,7 +736,7 @@ ra_index_discard(zval *z_redis, zval *return_value TSRMLS_DC) { /* run DISCARD */ ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL TSRMLS_CC); zval_dtor(&z_fun_discard); zval_dtor(&z_ret); @@ -749,7 +749,7 @@ ra_index_unwatch(zval *z_redis, zval *return_value TSRMLS_DC) { /* run UNWATCH */ ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL TSRMLS_CC); zval_dtor(&z_fun_unwatch); zval_dtor(&z_ret); @@ -789,14 +789,14 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TYPE", 4); - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg TSRMLS_CC); zval_dtor(&z_fun); zval_dtor(&z_ret); /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TTL", 3); - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg TSRMLS_CC); zval_dtor(&z_fun); zval_dtor(&z_ret); @@ -828,7 +828,7 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); ZVAL_STRINGL(&z_args[1], key, key_len); - call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_fun_srem); @@ -850,7 +850,7 @@ ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { /* run DEL on source */ ZVAL_STRINGL(&z_fun_del, "DEL", 3); ZVAL_STRINGL(&z_args[0], key, key_len); - call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args TSRMLS_CC); zval_dtor(&z_fun_del); zval_dtor(&z_args[0]); zval_dtor(&z_ret); @@ -875,7 +875,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6); ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_LONG(&z_args[1], ttl); - call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args TSRMLS_CC); zval_dtor(&z_fun_expire); zval_dtor(&z_args[0]); zval_dtor(&z_ret); @@ -899,7 +899,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS ZVAL_STRINGL(&z_args[1], "0", 1); ZVAL_STRINGL(&z_args[2], "-1", 2); ZVAL_BOOL(&z_args[3], 1); - call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args TSRMLS_CC); zval_dtor(&z_fun_zrange); zval_dtor(&z_args[2]); zval_dtor(&z_args[1]); @@ -937,7 +937,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run ZADD on target */ ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4); - call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args TSRMLS_CC); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); @@ -964,7 +964,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl /* run GET on source */ ZVAL_STRINGL(&z_fun_get, "GET", 3); ZVAL_STRINGL(&z_args[0], key, key_len); - call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args TSRMLS_CC); zval_dtor(&z_fun_get); if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ @@ -980,14 +980,14 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl ZVAL_LONG(&z_args[1], ttl); ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous call */ - call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_args[2]); } else { ZVAL_STRINGL(&z_fun_set, "SET", 3); ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous return value */ - call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_args[1]); } @@ -1005,7 +1005,7 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run HGETALL on source */ ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); - call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args TSRMLS_CC); zval_dtor(&z_fun_hgetall); if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ @@ -1017,7 +1017,7 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run HMSET on target */ ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); - call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args TSRMLS_CC); zval_dtor(&z_fun_hmset); zval_dtor(&z_ret_dest); @@ -1052,7 +1052,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, ZVAL_STRING(&z_retrieve_args[i], cmd_list[i]); } - call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_fun_retrieve); @@ -1084,7 +1084,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, /* Clean up our input return value */ zval_dtor(&z_ret); - call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args TSRMLS_CC); /* cleanup */ zval_dtor(&z_fun_sadd); @@ -1209,7 +1209,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool ZVAL_STRING(&z_argv, "*"); } ZVAL_NULL(&z_ret); - call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv TSRMLS_CC); zval_dtor(&z_argv); zval_dtor(&z_fun); From 11c902b844d1bdcd14aea29b58a9faef0b0df958 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 26 Feb 2019 10:05:55 +0200 Subject: [PATCH 1127/1986] Add documentation for RedisArray auth --- arrays.markdown | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arrays.markdown b/arrays.markdown index cd6394d159..7171c2a261 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -75,6 +75,12 @@ This option applies to main and previous ring if specified. $ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("consistent" => true)); +#### Specifying the "auth" parameter +The value is string and used to specify the password for authenticate with the server prior to sending commands +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("auth" => "mysecretpassword"));
+
+ #### Defining arrays in Redis.ini Because php.ini parameters must be pre-defined, Redis Arrays must all share the same .ini settings. @@ -91,6 +97,9 @@ ini_set('redis.arrays.functions', 'users=user_hash'); // use index only for users ini_set('redis.arrays.index', 'users=1,friends=0'); + +// use password for authentication +ini_set('redis.arrays.auth', 'users=mysecretpassword') ## Usage From b2511d632cc2fb4a4c4cda07ae9403e37bf9055b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 1 Mar 2019 09:22:11 +0200 Subject: [PATCH 1128/1986] Update documentation Fix minimum Redis server version required for session locking. --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 111461d3cf..04a0a1e1e4 100644 --- a/README.markdown +++ b/README.markdown @@ -68,7 +68,7 @@ session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeou * database (integer): selects a different database. Sessions have a lifetime expressed in seconds and stored in the INI variable "session.gc_maxlifetime". You can change it with [`ini_set()`](http://php.net/ini_set). -The session handler requires a version of Redis with the `SETEX` command (at least 2.0). +The session handler requires a version of Redis supporting `EX` and `NX` options of `SET` command (at least 2.6.12). phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0`. ### Session locking From b994ee42df8a4acf4b5cc16faeb09a60619e3245 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 16 Feb 2019 19:48:02 +0200 Subject: [PATCH 1129/1986] Persistent connections pool --- common.h | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ library.c | 80 +++++++--- redis.c | 28 ++++ 3 files changed, 550 insertions(+), 21 deletions(-) diff --git a/common.h b/common.h index 3fa078c5de..d4f7a4b8c8 100644 --- a/common.h +++ b/common.h @@ -9,6 +9,469 @@ #include "zend_llist.h" #include #include +#if (PHP_MAJOR_VERSION < 7) +#include +typedef smart_str smart_string; +#define smart_string_0(x) smart_str_0(x) +#define smart_string_appendc(dest, c) smart_str_appendc(dest, c) +#define smart_string_append_long(dest, val) smart_str_append_long(dest, val) +#define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) + +typedef struct { + short gc; + size_t len; + char *val; +} zend_string; + +#define REDIS_MAKE_STD_ZVAL(zv) MAKE_STD_ZVAL(zv) +#define REDIS_FREE_ZVAL(zv) (efree(zv)) + +#define ZSTR_VAL(s) (s)->val +#define ZSTR_LEN(s) (s)->len + +static zend_always_inline zend_string * +zend_string_alloc(size_t len, int persistent) +{ + zend_string *zstr = emalloc(sizeof(*zstr) + len + 1); + + ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); + ZSTR_LEN(zstr) = len; + zstr->gc = 0x01; + return zstr; +} + +static zend_always_inline zend_string * +zend_string_init(const char *str, size_t len, int persistent) +{ + zend_string *zstr = zend_string_alloc(len, persistent); + + memcpy(ZSTR_VAL(zstr), str, len); + ZSTR_VAL(zstr)[len] = '\0'; + return zstr; +} + +static zend_always_inline zend_string * +zend_string_realloc(zend_string *s, size_t len, int persistent) +{ + zend_string *zstr; + + if (!s->gc) { + zstr = zend_string_init(ZSTR_VAL(s), len, 0); + } else if (s->gc & 0x10) { + ZSTR_VAL(s) = erealloc(ZSTR_VAL(s), len + 1); + ZSTR_LEN(s) = len; + zstr = s; + } else { + zstr = erealloc(s, sizeof(*zstr) + len + 1); + ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); + ZSTR_LEN(zstr) = len; + } + return zstr; +} + +#define strpprintf zend_strpprintf + +static zend_string * +zend_strpprintf(size_t max_len, const char *format, ...) +{ + va_list ap; + zend_string *zstr; + + va_start(ap, format); + zstr = ecalloc(1, sizeof(*zstr)); + ZSTR_LEN(zstr) = vspprintf(&ZSTR_VAL(zstr), max_len, format, ap); + zstr->gc = 0x11; + va_end(ap); + return zstr; +} + +#define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0) + +#define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) +#define zend_string_equal_content(s1, s2) (ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2)) +#define zend_string_equals(s1, s2) (s1 == s2 || zend_string_equal_content(s1, s2)) + +#define zend_string_release(s) do { \ + if ((s) && (s)->gc) { \ + if ((s)->gc & 0x10 && ZSTR_VAL(s)) efree(ZSTR_VAL(s)); \ + if ((s)->gc & 0x01) efree((s)); \ + } \ +} while (0) + +#define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) do { \ + HashPosition _hpos; \ + for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ + zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ + zend_hash_move_forward_ex(ht, &_hpos) \ + ) { \ + zend_string _zstr = {0}; \ + char *_str_index; uint _str_length; ulong _num_index; \ + _h = 0; _key = NULL; _val = zend_hash_get_current_data_ex(ht, &_hpos); \ + switch (zend_hash_get_current_key_ex(ht, &_str_index, &_str_length, &_num_index, 0, &_hpos)) { \ + case HASH_KEY_IS_STRING: \ + _zstr.len = _str_length - 1; \ + _zstr.val = _str_index; \ + _key = &_zstr; \ + break; \ + case HASH_KEY_IS_LONG: \ + _h = _num_index; \ + break; \ + default: \ + /* noop */ break; \ + } + +#define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ + HashPosition _hpos; \ + for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ + zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ + zend_hash_move_forward_ex(ht, &_hpos) \ + ) { \ + _val = zend_hash_get_current_data_ex(ht, &_hpos); \ + +#define ZEND_HASH_FOREACH_PTR(ht, _ptr) do { \ + HashPosition _hpos; \ + for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ + zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ + zend_hash_move_forward_ex(ht, &_hpos) \ + ) { \ + _ptr = zend_hash_get_current_data_ptr_ex(ht, &_hpos); \ + +#define ZEND_HASH_FOREACH_END() \ + } \ +} while(0) + +#undef zend_hash_get_current_key +#define zend_hash_get_current_key(ht, str_index, num_index) \ + zend_hash_get_current_key_ex(ht, str_index, NULL, num_index, 0, NULL) + +#define zend_hash_str_exists(ht, str, len) zend_hash_exists(ht, str, len + 1) + +static zend_always_inline zval * +zend_hash_str_find(const HashTable *ht, const char *key, size_t len) +{ + zval **zv; + + if (zend_hash_find(ht, key, len + 1, (void **)&zv) == SUCCESS) { + return *zv; + } + return NULL; +} + +#define zend_hash_find_ptr(ht, s) zend_hash_str_find_ptr(ht, ZSTR_VAL(s), ZSTR_LEN(s)) + +static zend_always_inline void * +zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) +{ + void **ptr; + + if (zend_hash_find(ht, str, len + 1, (void **)&ptr) == SUCCESS) { + return *ptr; + } + return NULL; +} + +#define zend_hash_str_update_ptr(ht, str, len, pData) zend_hash_str_update_mem(ht, str, len, pData, sizeof(void *)) + +static zend_always_inline void * +zend_hash_str_update_mem(HashTable *ht, const char *str, size_t len, void *pData, size_t size) +{ + if (zend_hash_update(ht, str, len + 1, (void *)&pData, size, NULL) == SUCCESS) { + return pData; + } + return NULL; +} + +static zend_always_inline void * +zend_hash_index_update_ptr(HashTable *ht, zend_ulong h, void *pData) +{ + if (zend_hash_index_update(ht, h, (void **)&pData, sizeof(void *), NULL) == SUCCESS) { + return pData; + } + return NULL; +} + +#undef zend_hash_get_current_data +static zend_always_inline zval * +zend_hash_get_current_data(HashTable *ht) +{ + zval **zv; + + if (zend_hash_get_current_data_ex(ht, (void **)&zv, NULL) == SUCCESS) { + return *zv; + } + return NULL; +} + +static zend_always_inline void * +zend_hash_get_current_data_ptr_ex(HashTable *ht, HashPosition *pos) +{ + void **ptr; + + if (zend_hash_get_current_data_ex(ht, (void **)&ptr, pos) == SUCCESS) { + return *ptr; + } + return NULL; +} +#define zend_hash_get_current_data_ptr(ht) zend_hash_get_current_data_ptr_ex(ht, NULL) + +static int (*_zend_hash_index_find)(const HashTable *, ulong, void **) = &zend_hash_index_find; +#define zend_hash_index_find(ht, h) inline_zend_hash_index_find(ht, h) + +static zend_always_inline zval * +inline_zend_hash_index_find(const HashTable *ht, zend_ulong h) +{ + zval **zv; + if (_zend_hash_index_find(ht, h, (void **)&zv) == SUCCESS) { + return *zv; + } + return NULL; +} + +static zend_always_inline void * +zend_hash_index_find_ptr(const HashTable *ht, zend_ulong h) +{ + void **ptr; + + if (_zend_hash_index_find(ht, h, (void **)&ptr) == SUCCESS) { + return *ptr; + } + return NULL; +} + +static int (*_zend_hash_get_current_data_ex)(HashTable *, void **, HashPosition *) = &zend_hash_get_current_data_ex; +#define zend_hash_get_current_data_ex(ht, pos) inline_zend_hash_get_current_data_ex(ht, pos) +static zend_always_inline zval * +inline_zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos) +{ + zval **zv; + if (_zend_hash_get_current_data_ex(ht, (void **)&zv, pos) == SUCCESS) { + return *zv; + } + return NULL; +} + +#undef zend_hash_next_index_insert +#define zend_hash_next_index_insert(ht, pData) \ + _zend_hash_next_index_insert(ht, pData ZEND_FILE_LINE_CC) +static zend_always_inline zval * +_zend_hash_next_index_insert(HashTable *ht, zval *pData ZEND_FILE_LINE_DC) +{ + if (_zend_hash_index_update_or_next_insert(ht, 0, &pData, sizeof(pData), + NULL, HASH_NEXT_INSERT ZEND_FILE_LINE_CC) == SUCCESS + ) { + return pData; + } + return NULL; +} + +#undef zend_get_parameters_array +#define zend_get_parameters_array(ht, param_count, argument_array) \ + inline_zend_get_parameters_array(ht, param_count, argument_array TSRMLS_CC) + +static zend_always_inline int +inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array TSRMLS_DC) +{ + int i, ret = FAILURE; + zval **zv = ecalloc(param_count, sizeof(zval *)); + + if (_zend_get_parameters_array(ht, param_count, zv TSRMLS_CC) == SUCCESS) { + for (i = 0; i < param_count; i++) { + argument_array[i] = *zv[i]; + } + ret = SUCCESS; + } + efree(zv); + return ret; +} + +typedef zend_rsrc_list_entry zend_resource; + +extern int (*_add_next_index_string)(zval *, const char *, int); +#define add_next_index_string(arg, str) _add_next_index_string(arg, str, 1); +extern int (*_add_next_index_stringl)(zval *, const char *, uint, int); +#define add_next_index_stringl(arg, str, length) _add_next_index_stringl(arg, str, length, 1); + +#undef ZVAL_STRING +#define ZVAL_STRING(z, s) do { \ + const char *_s = (s); \ + ZVAL_STRINGL(z, _s, strlen(_s)); \ +} while (0) +#undef RETVAL_STRING +#define RETVAL_STRING(s) ZVAL_STRING(return_value, s) +#undef RETURN_STRING +#define RETURN_STRING(s) { RETVAL_STRING(s); return; } + +#undef ZVAL_STRINGL +#define ZVAL_STRINGL(z, s, l) do { \ + const char *__s = (s); int __l = l; \ + zval *__z = (z); \ + Z_STRLEN_P(__z) = __l; \ + Z_STRVAL_P(__z) = estrndup(__s, __l); \ + Z_TYPE_P(__z) = IS_STRING; \ +} while (0) +#undef RETVAL_STRINGL +#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l) +#undef RETURN_STRINGL +#define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; } + +static int (*_call_user_function)(HashTable *, zval **, zval *, zval *, zend_uint, zval *[] TSRMLS_DC) = &call_user_function; +#define call_user_function(function_table, object, function_name, retval_ptr, param_count, params) \ + inline_call_user_function(function_table, object, function_name, retval_ptr, param_count, params TSRMLS_CC) + +static zend_always_inline int +inline_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, zend_uint param_count, zval params[] TSRMLS_DC) +{ + int i, ret; + zval **_params = NULL; + if (!params) param_count = 0; + if (param_count > 0) { + _params = ecalloc(param_count, sizeof(zval *)); + for (i = 0; i < param_count; ++i) { + zval *zv = ¶ms[i]; + MAKE_STD_ZVAL(_params[i]); + ZVAL_ZVAL(_params[i], zv, 1, 0); + } + } + ret = _call_user_function(function_table, &object, function_name, retval_ptr, param_count, _params TSRMLS_CC); + if (_params) { + for (i = 0; i < param_count; ++i) { + zval_ptr_dtor(&_params[i]); + } + efree(_params); + } + return ret; +} + +#undef add_assoc_bool +#define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key), __b) +extern int (*_add_assoc_bool_ex)(zval *, const char *, uint, int); +#define add_assoc_bool_ex(_arg, _key, _key_len, _b) _add_assoc_bool_ex(_arg, _key, _key_len + 1, _b) + +#undef add_assoc_long +#define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key), __n) +extern int (*_add_assoc_long_ex)(zval *, const char *, uint, long); +#define add_assoc_long_ex(_arg, _key, _key_len, _n) _add_assoc_long_ex(_arg, _key, _key_len + 1, _n) + +#undef add_assoc_double +#define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key), __d) +extern int (*_add_assoc_double_ex)(zval *, const char *, uint, double); +#define add_assoc_double_ex(_arg, _key, _key_len, _d) _add_assoc_double_ex(_arg, _key, _key_len + 1, _d) + +#undef add_assoc_string +#define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str) +extern int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int); +#define add_assoc_string_ex(_arg, _key, _key_len, _str) _add_assoc_string_ex(_arg, _key, _key_len + 1, _str, 1) + +extern int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int); +#define add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length) _add_assoc_stringl_ex(_arg, _key, _key_len + 1, _str, _length, 1) + +#undef add_assoc_zval +#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key), __value) +extern int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *); +#define add_assoc_zval_ex(_arg, _key, _key_len, _value) _add_assoc_zval_ex(_arg, _key, _key_len + 1, _value); + +typedef long zend_long; +static zend_always_inline zend_long +zval_get_long(zval *op) +{ + switch (Z_TYPE_P(op)) { + case IS_BOOL: + case IS_LONG: + return Z_LVAL_P(op); + case IS_DOUBLE: + return zend_dval_to_lval(Z_DVAL_P(op)); + case IS_STRING: + { + double dval; + zend_long lval; + zend_uchar type = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, 0); + if (type == IS_LONG) { + return lval; + } else if (type == IS_DOUBLE) { + return zend_dval_to_lval(dval); + } + } + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + return 0; +} + +static zend_always_inline double +zval_get_double(zval *op) +{ + switch (Z_TYPE_P(op)) { + case IS_BOOL: + case IS_LONG: + return (double)Z_LVAL_P(op); + case IS_DOUBLE: + return Z_DVAL_P(op); + case IS_STRING: + return zend_strtod(Z_STRVAL_P(op), NULL); + EMPTY_SWITCH_DEFAULT_CASE() + } + return 0.0; +} + +static zend_always_inline zend_string * +zval_get_string(zval *op) +{ + zend_string *zstr = ecalloc(1, sizeof(zend_string)); + + zstr->gc = 0; + ZSTR_VAL(zstr) = ""; + ZSTR_LEN(zstr) = 0; + switch (Z_TYPE_P(op)) { + case IS_STRING: + ZSTR_VAL(zstr) = Z_STRVAL_P(op); + ZSTR_LEN(zstr) = Z_STRLEN_P(op); + break; + case IS_BOOL: + if (Z_LVAL_P(op)) { + ZSTR_VAL(zstr) = "1"; + ZSTR_LEN(zstr) = 1; + } + break; + case IS_LONG: { + zstr->gc = 0x10; + ZSTR_LEN(zstr) = spprintf(&ZSTR_VAL(zstr), 0, "%ld", Z_LVAL_P(op)); + break; + } + case IS_DOUBLE: { + zstr->gc = 0x10; + ZSTR_LEN(zstr) = spprintf(&ZSTR_VAL(zstr), 0, "%.16g", Z_DVAL_P(op)); + break; + } + EMPTY_SWITCH_DEFAULT_CASE() + } + zstr->gc |= 0x01; + return zstr; +} + +extern void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC); +#define php_var_serialize(buf, struc, data) _php_var_serialize(buf, &struc, data TSRMLS_CC) +extern int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC); +#define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) +typedef int strlen_t; + +#define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_BOOL && !Z_BVAL_P(z)) + +/* If ZEND_MOD_END isn't defined, use legacy version */ +#ifndef ZEND_MOD_END +#define ZEND_MOD_END { NULL, NULL, NULL } +#endif + +/* PHP_FE_END exists since 5.3.7 */ +#ifndef PHP_FE_END +#define PHP_FE_END { NULL, NULL, NULL } +#endif + +/* References don't need any actions */ +#define ZVAL_DEREF(v) PHPREDIS_NOTUSED(v) + +#define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)zend_objects_get_address(z TSRMLS_CC) + +#else #include #include diff --git a/library.c b/library.c index d3b59d7456..3869f8adb9 100644 --- a/library.c +++ b/library.c @@ -43,6 +43,8 @@ extern zend_class_entry *redis_ce; extern zend_class_entry *redis_exception_ce; +extern int le_redis_pconnect; + /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; @@ -1669,10 +1671,10 @@ redis_sock_create(char *host, int host_len, unsigned short port, PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) { struct timeval tv, read_tv, *tv_ptr = NULL; - char host[1024], *persistent_id = NULL; + zend_string *persistent_id = NULL; + char host[1024]; const char *fmtstr = "%s:%d"; int host_len, usocket = 0, err = 0; - php_netstream_data_t *sock; int tcp_flag = 1; zend_string *estr = NULL; @@ -1680,15 +1682,6 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock_disconnect(redis_sock, 0 TSRMLS_CC); } - tv.tv_sec = (time_t)redis_sock->timeout; - tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); - if(tv.tv_sec != 0 || tv.tv_usec != 0) { - tv_ptr = &tv; - } - - read_tv.tv_sec = (time_t)redis_sock->read_timeout; - read_tv.tv_usec = (int)((redis_sock->read_timeout-read_tv.tv_sec)*1000000); - if (ZSTR_VAL(redis_sock->host)[0] == '/' && redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", ZSTR_VAL(redis_sock->host)); usocket = 1; @@ -1707,21 +1700,46 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } if (redis_sock->persistent) { - if (redis_sock->persistent_id) { - spprintf(&persistent_id, 0, "phpredis:%s:%s", host, - ZSTR_VAL(redis_sock->persistent_id)); + if (INI_INT("redis.pconnect.pooling_enabled")) { + persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (le && zend_llist_count(le->ptr) > 0) { + redis_sock->stream = *(php_stream **)zend_llist_get_last(le->ptr); + zend_llist_remove_tail(le->ptr); + /* Check socket liveness using 0 second timeout */ + if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { + redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; + zend_string_release(persistent_id); + return SUCCESS; + } + php_stream_pclose(redis_sock->stream); + } + zend_string_release(persistent_id); + + gettimeofday(&tv, NULL); + persistent_id = strpprintf(0, "phpredis_%d%d", tv.tv_sec, tv.tv_usec); } else { - spprintf(&persistent_id, 0, "phpredis:%s:%f", host, - redis_sock->timeout); + if (redis_sock->persistent_id) { + persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id)); + } else { + persistent_id = strpprintf(0, "phpredis:%s:%f", host, redis_sock->timeout); + } } } + tv.tv_sec = (time_t)redis_sock->timeout; + tv.tv_usec = (int)((redis_sock->timeout - tv.tv_sec) * 1000000); + if (tv.tv_sec != 0 || tv.tv_usec != 0) { + tv_ptr = &tv; + } + redis_sock->stream = php_stream_xport_create(host, host_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, - persistent_id, tv_ptr, NULL, &estr, &err); + persistent_id ? ZSTR_VAL(persistent_id) : NULL, + tv_ptr, NULL, &estr, &err); if (persistent_id) { - efree(persistent_id); + zend_string_release(persistent_id); } if (!redis_sock->stream) { @@ -1729,12 +1747,12 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock_set_err(redis_sock, ZSTR_VAL(estr), ZSTR_LEN(estr)); zend_string_release(estr); } - return -1; + return FAILURE; } /* Attempt to set TCP_NODELAY/TCP_KEEPALIVE if we're not using a unix socket. */ - sock = (php_netstream_data_t*)redis_sock->stream->abstract; if (!usocket) { + php_netstream_data_t *sock = (php_netstream_data_t*)redis_sock->stream->abstract; err = setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, (char*) &tcp_flag, sizeof(tcp_flag)); PHPREDIS_NOTUSED(err); err = setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char*) &redis_sock->tcp_keepalive, sizeof(redis_sock->tcp_keepalive)); @@ -1743,6 +1761,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) php_stream_auto_cleanup(redis_sock->stream); + read_tv.tv_sec = (time_t)redis_sock->read_timeout; + read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000); + if (read_tv.tv_sec != 0 || read_tv.tv_usec != 0) { php_stream_set_option(redis_sock->stream,PHP_STREAM_OPTION_READ_TIMEOUT, 0, &read_tv); @@ -1752,7 +1773,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; - return 0; + return SUCCESS; } /** @@ -1786,6 +1807,23 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (redis_sock->persistent) { if (force) { php_stream_pclose(redis_sock->stream); + } else if (INI_INT("redis.pconnect.pooling_enabled")) { + zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (!le) { +#if (PHP_MAJOR_VERSION < 7) + le = ecalloc(1, sizeof(*le)); +#else + zend_resource res; + le = &res; +#endif + le->type = le_redis_pconnect; + le->ptr = pecalloc(1, sizeof(zend_llist), 1); + zend_llist_init(le->ptr, sizeof(void *), NULL, 1); + zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + } + zend_llist_prepend_element(le->ptr, &redis_sock->stream); + zend_string_release(persistent_id); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index d94b5a6d0a..a33ff1a1d3 100644 --- a/redis.c +++ b/redis.c @@ -55,6 +55,8 @@ extern int le_cluster_slot_cache; extern zend_function_entry redis_array_functions[]; extern zend_function_entry redis_cluster_functions[]; +int le_redis_pconnect; + PHP_INI_BEGIN() /* redis arrays */ PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL) @@ -81,6 +83,9 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL) + /* redis pconnect */ + PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "0", PHP_INI_ALL, NULL) + /* redis session */ PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL) @@ -711,6 +716,25 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } +static void +redis_pconnect_dtor(void *ptr TSRMLS_DC) +{ + php_stream_pclose(*(php_stream **)ptr); +} + +static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) +{ +#if (PHP_MAJOR_VERSION < 7) + zend_resource *res = rsrc; +#endif + + if (res->ptr) { + zend_llist_apply(res->ptr, redis_pconnect_dtor TSRMLS_CC); + zend_llist_destroy(res->ptr); + pefree(res->ptr, 1); + } +} + /** * PHP_MINIT_FUNCTION */ @@ -781,6 +805,10 @@ PHP_MINIT_FUNCTION(redis) php_session_register_module(&ps_mod_redis_cluster); #endif + /* Register resource destructors */ + le_redis_pconnect = zend_register_list_destructors_ex(NULL, redis_connections_pool_dtor, + "phpredis persistent connections pool", module_number); + return SUCCESS; } From 80dcc9611e99616f0df7040de64604d19dfa4a76 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 21:43:10 +0200 Subject: [PATCH 1130/1986] Fix memory leak on PHP 5 --- library.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/library.c b/library.c index 3869f8adb9..2b308ea566 100644 --- a/library.c +++ b/library.c @@ -1811,15 +1811,11 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { -#if (PHP_MAJOR_VERSION < 7) - le = ecalloc(1, sizeof(*le)); -#else - zend_resource res; - le = &res; -#endif + zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); + zend_llist_init(l, sizeof(void *), NULL, 1); + le = (void *)l + sizeof(*l); le->type = le_redis_pconnect; - le->ptr = pecalloc(1, sizeof(zend_llist), 1); - zend_llist_init(le->ptr, sizeof(void *), NULL, 1); + le->ptr = l; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); } zend_llist_prepend_element(le->ptr, &redis_sock->stream); From f9016f1bfb4e6350efe2f91d724d74fa482136bd Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 20 Feb 2019 09:59:17 +0200 Subject: [PATCH 1131/1986] Update documentation. --- README.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 04a0a1e1e4..7b67225b63 100644 --- a/README.markdown +++ b/README.markdown @@ -207,13 +207,15 @@ $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay ----- _**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. -The connection will not be closed on `close` or end of request until the php process ends. +The connection will not be closed on end of request until the php process ends. So be prepared for too many open FD's errors (specially on redis server side) when using persistent connections on many servers connecting to one redis server. Also more than one persistent connection can be made identified by either host + port + timeout or host + persistent_id or unix socket + timeout. +Starting from version 4.2.1, it became possible to use connection pooling by setting INI variable `redis.pconnect.pooling_enabled` to 1. + This feature is not available in threaded versions. `pconnect` and `popen` then working like their non persistent equivalents. From c6519dda47ed29f7130959d3ed7c74ff11e6ec62 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 09:09:49 +0200 Subject: [PATCH 1132/1986] Issue #1514 `sizeof(void *)` isn't standard so change it to `sizeof(char *)` --- library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 2b308ea566..dfc6a87641 100644 --- a/library.c +++ b/library.c @@ -1812,8 +1812,8 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); - zend_llist_init(l, sizeof(void *), NULL, 1); - le = (void *)l + sizeof(*l); + zend_llist_init(l, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)l + sizeof(*l)); le->type = le_redis_pconnect; le->ptr = l; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); From 675d1277006a3349a45bd22d8ba7abfced460e07 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 09:42:50 +0200 Subject: [PATCH 1133/1986] Refactor connection pooling code. Move logic of searching+creating connection pool resource to `redis_sock_get_connection_pool`. Don't close streams in ZEND_RSRC_DTOR_FUNC because all of them stored in `EG(persistent_list)` and will be destroyed by PHP engine. --- library.c | 42 +++++++++++++++++++++++------------------- redis.c | 7 ------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/library.c b/library.c index dfc6a87641..a390845b2b 100644 --- a/library.c +++ b/library.c @@ -45,6 +45,23 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; +static zend_llist * +redis_sock_get_connection_pool(RedisSock *redis_sock) +{ + zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (!le) { + zend_llist *list = pecalloc(1, sizeof(*list) + sizeof(*le), 1); + zend_llist_init(list, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)list + sizeof(*list)); + le->type = le_redis_pconnect; + le->ptr = list; + zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + } + zend_string_release(persistent_id); + return le->ptr; +} + /* Helper to reselect the proper DB number when we reconnect */ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { char *cmd, *response; @@ -1701,20 +1718,17 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); - zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); - if (le && zend_llist_count(le->ptr) > 0) { - redis_sock->stream = *(php_stream **)zend_llist_get_last(le->ptr); - zend_llist_remove_tail(le->ptr); + zend_llist *list = redis_sock_get_connection_pool(redis_sock); + if (zend_llist_count(list) > 0) { + redis_sock->stream = *(php_stream **)zend_llist_get_last(list); + zend_llist_remove_tail(list); /* Check socket liveness using 0 second timeout */ if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; - zend_string_release(persistent_id); return SUCCESS; } php_stream_pclose(redis_sock->stream); } - zend_string_release(persistent_id); gettimeofday(&tv, NULL); persistent_id = strpprintf(0, "phpredis_%d%d", tv.tv_sec, tv.tv_usec); @@ -1808,18 +1822,8 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); - zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); - if (!le) { - zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); - zend_llist_init(l, sizeof(php_stream *), NULL, 1); - le = (zend_resource *)((char *)l + sizeof(*l)); - le->type = le_redis_pconnect; - le->ptr = l; - zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); - } - zend_llist_prepend_element(le->ptr, &redis_sock->stream); - zend_string_release(persistent_id); + zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist_prepend_element(list, &redis_sock->stream); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index a33ff1a1d3..e005aaacce 100644 --- a/redis.c +++ b/redis.c @@ -716,12 +716,6 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } -static void -redis_pconnect_dtor(void *ptr TSRMLS_DC) -{ - php_stream_pclose(*(php_stream **)ptr); -} - static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) { #if (PHP_MAJOR_VERSION < 7) @@ -729,7 +723,6 @@ static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) #endif if (res->ptr) { - zend_llist_apply(res->ptr, redis_pconnect_dtor TSRMLS_CC); zend_llist_destroy(res->ptr); pefree(res->ptr, 1); } From 8e80a508881669b43aee3b94cf0d8e9d3e5dddf6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 10:02:12 +0200 Subject: [PATCH 1134/1986] Fix compilation error on PHP5 --- library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index a390845b2b..36d34d9fe4 100644 --- a/library.c +++ b/library.c @@ -46,7 +46,7 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; static zend_llist * -redis_sock_get_connection_pool(RedisSock *redis_sock) +redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) { zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); @@ -1718,7 +1718,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); if (zend_llist_count(list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(list); zend_llist_remove_tail(list); @@ -1822,7 +1822,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); zend_llist_prepend_element(list, &redis_sock->stream); } } else { From 15d3b9ee222eb95fb5a40c72bf8141724ac07b72 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 16 Feb 2019 19:48:02 +0200 Subject: [PATCH 1135/1986] Persistent connections pool --- library.c | 18 ++++++++++++++++-- redis.c | 11 +++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/library.c b/library.c index 36d34d9fe4..a6aea8afc9 100644 --- a/library.c +++ b/library.c @@ -1822,8 +1822,22 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); - zend_llist_prepend_element(list, &redis_sock->stream); + zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); + zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); + if (!le) { +#if (PHP_MAJOR_VERSION < 7) + le = ecalloc(1, sizeof(*le)); +#else + zend_resource res; + le = &res; +#endif + le->type = le_redis_pconnect; + le->ptr = pecalloc(1, sizeof(zend_llist), 1); + zend_llist_init(le->ptr, sizeof(void *), NULL, 1); + zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + } + zend_llist_prepend_element(le->ptr, &redis_sock->stream); + zend_string_release(persistent_id); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index e005aaacce..a88753f26a 100644 --- a/redis.c +++ b/redis.c @@ -716,13 +716,16 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } -static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) +static void +redis_pconnect_dtor(void *ptr TSRMLS_DC) { -#if (PHP_MAJOR_VERSION < 7) - zend_resource *res = rsrc; -#endif + php_stream_pclose(*(php_stream **)ptr); +} +static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) +{ if (res->ptr) { + zend_llist_apply(res->ptr, redis_pconnect_dtor TSRMLS_CC); zend_llist_destroy(res->ptr); pefree(res->ptr, 1); } From 95d57a20afde248e7b3376c76c0acbfa110da763 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Feb 2019 21:43:10 +0200 Subject: [PATCH 1136/1986] Fix memory leak on PHP 5 --- library.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/library.c b/library.c index a6aea8afc9..51dbf2c399 100644 --- a/library.c +++ b/library.c @@ -1825,15 +1825,11 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { -#if (PHP_MAJOR_VERSION < 7) - le = ecalloc(1, sizeof(*le)); -#else - zend_resource res; - le = &res; -#endif + zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); + zend_llist_init(l, sizeof(void *), NULL, 1); + le = (void *)l + sizeof(*l); le->type = le_redis_pconnect; - le->ptr = pecalloc(1, sizeof(zend_llist), 1); - zend_llist_init(le->ptr, sizeof(void *), NULL, 1); + le->ptr = l; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); } zend_llist_prepend_element(le->ptr, &redis_sock->stream); From 09f3abb6a32596d5a6b7fec1a80d733682bdfce3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 09:09:49 +0200 Subject: [PATCH 1137/1986] Issue #1514 `sizeof(void *)` isn't standard so change it to `sizeof(char *)` --- library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 51dbf2c399..0ec3f1a130 100644 --- a/library.c +++ b/library.c @@ -1826,8 +1826,8 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); - zend_llist_init(l, sizeof(void *), NULL, 1); - le = (void *)l + sizeof(*l); + zend_llist_init(l, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)l + sizeof(*l)); le->type = le_redis_pconnect; le->ptr = l; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); From c83e5238a7bd5e860e24536ce05140087a6532c8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 09:42:50 +0200 Subject: [PATCH 1138/1986] Refactor connection pooling code. Move logic of searching+creating connection pool resource to `redis_sock_get_connection_pool`. Don't close streams in ZEND_RSRC_DTOR_FUNC because all of them stored in `EG(persistent_list)` and will be destroyed by PHP engine. --- library.c | 18 ++++-------------- redis.c | 7 ------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/library.c b/library.c index 0ec3f1a130..a390845b2b 100644 --- a/library.c +++ b/library.c @@ -46,7 +46,7 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; static zend_llist * -redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) +redis_sock_get_connection_pool(RedisSock *redis_sock) { zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); @@ -1718,7 +1718,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); + zend_llist *list = redis_sock_get_connection_pool(redis_sock); if (zend_llist_count(list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(list); zend_llist_remove_tail(list); @@ -1822,18 +1822,8 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); - zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); - if (!le) { - zend_llist *l = pecalloc(1, sizeof(*l) + sizeof(*le), 1); - zend_llist_init(l, sizeof(php_stream *), NULL, 1); - le = (zend_resource *)((char *)l + sizeof(*l)); - le->type = le_redis_pconnect; - le->ptr = l; - zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); - } - zend_llist_prepend_element(le->ptr, &redis_sock->stream); - zend_string_release(persistent_id); + zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist_prepend_element(list, &redis_sock->stream); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index a88753f26a..eaeaf1ce6c 100644 --- a/redis.c +++ b/redis.c @@ -716,16 +716,9 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } -static void -redis_pconnect_dtor(void *ptr TSRMLS_DC) -{ - php_stream_pclose(*(php_stream **)ptr); -} - static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) { if (res->ptr) { - zend_llist_apply(res->ptr, redis_pconnect_dtor TSRMLS_CC); zend_llist_destroy(res->ptr); pefree(res->ptr, 1); } From c56ffc51b38d27d16760be0d778f50ff5d424b5d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 10:02:12 +0200 Subject: [PATCH 1139/1986] Fix compilation error on PHP5 --- library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index a390845b2b..36d34d9fe4 100644 --- a/library.c +++ b/library.c @@ -46,7 +46,7 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; static zend_llist * -redis_sock_get_connection_pool(RedisSock *redis_sock) +redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) { zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); @@ -1718,7 +1718,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); if (zend_llist_count(list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(list); zend_llist_remove_tail(list); @@ -1822,7 +1822,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (force) { php_stream_pclose(redis_sock->stream); } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock); + zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); zend_llist_prepend_element(list, &redis_sock->stream); } } else { From b826234556be01c2cf9494be9b489bc2443c57f5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Feb 2019 22:25:15 +0200 Subject: [PATCH 1140/1986] Connection limit for pool. --- common.h | 12 ++++++++++++ library.c | 45 +++++++++++++++++++++++++++++++-------------- redis.c | 4 +++- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/common.h b/common.h index d4f7a4b8c8..11a434a44d 100644 --- a/common.h +++ b/common.h @@ -733,10 +733,22 @@ typedef struct { } RedisSock; /* }}} */ +typedef struct { + zend_llist list; + int nb_active; +} ConnectionPool; + +#if (PHP_MAJOR_VERSION < 7) +typedef struct { + zend_object std; + RedisSock *sock; +} redis_object; +#else typedef struct { RedisSock *sock; zend_object std; } redis_object; +#endif /** Argument info for any function expecting 0 args */ ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) diff --git a/library.c b/library.c index 36d34d9fe4..6170320801 100644 --- a/library.c +++ b/library.c @@ -45,18 +45,19 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; -static zend_llist * +static ConnectionPool * redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) { zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { - zend_llist *list = pecalloc(1, sizeof(*list) + sizeof(*le), 1); - zend_llist_init(list, sizeof(php_stream *), NULL, 1); - le = (zend_resource *)((char *)list + sizeof(*list)); + ConnectionPool *p = pecalloc(1, sizeof(*p) + sizeof(*le), 1); + zend_llist_init(&p->list, sizeof(php_stream *), NULL, 1); + le = (zend_resource *)((char *)p + sizeof(*p)); le->type = le_redis_pconnect; - le->ptr = list; + le->ptr = p; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); + p->nb_active = 0; } zend_string_release(persistent_id); return le->ptr; @@ -1691,8 +1692,11 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) zend_string *persistent_id = NULL; char host[1024]; const char *fmtstr = "%s:%d"; - int host_len, usocket = 0, err = 0; - int tcp_flag = 1; + int host_len, usocket = 0, err = 0, tcp_flag = 1; + ConnectionPool *p = NULL; +#if (PHP_MAJOR_VERSION < 7) + char *estr = NULL; +#else zend_string *estr = NULL; if (redis_sock->stream != NULL) { @@ -1718,16 +1722,23 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); - if (zend_llist_count(list) > 0) { - redis_sock->stream = *(php_stream **)zend_llist_get_last(list); - zend_llist_remove_tail(list); + p = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); + if (zend_llist_count(&p->list) > 0) { + redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list); + zend_llist_remove_tail(&p->list); /* Check socket liveness using 0 second timeout */ if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; return SUCCESS; } php_stream_pclose(redis_sock->stream); + p->nb_active--; + } + + int limit = INI_INT("redis.pconnect.connection_limit"); + if (limit > 0 && p->nb_active >= limit) { + redis_sock_set_err(redis_sock, "Connection limit reached", sizeof("Connection limit reached") - 1); + return FAILURE; } gettimeofday(&tv, NULL); @@ -1764,6 +1775,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) return FAILURE; } + if (p) p->nb_active++; + /* Attempt to set TCP_NODELAY/TCP_KEEPALIVE if we're not using a unix socket. */ if (!usocket) { php_netstream_data_t *sock = (php_netstream_data_t*)redis_sock->stream->abstract; @@ -1819,11 +1832,15 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) return FAILURE; } else if (redis_sock->stream) { if (redis_sock->persistent) { + ConnectionPool *p = NULL; + if (INI_INT("redis.pconnect.pooling_enabled")) { + p = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); + } if (force) { php_stream_pclose(redis_sock->stream); - } else if (INI_INT("redis.pconnect.pooling_enabled")) { - zend_llist *list = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); - zend_llist_prepend_element(list, &redis_sock->stream); + if (p) p->nb_active--; + } else if (p) { + zend_llist_prepend_element(&p->list, &redis_sock->stream); } } else { php_stream_close(redis_sock->stream); diff --git a/redis.c b/redis.c index eaeaf1ce6c..4cbc451db7 100644 --- a/redis.c +++ b/redis.c @@ -85,6 +85,7 @@ PHP_INI_BEGIN() /* redis pconnect */ PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL) /* redis session */ PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL) @@ -719,7 +720,8 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) { if (res->ptr) { - zend_llist_destroy(res->ptr); + ConnectionPool *p = res->ptr; + zend_llist_destroy(&p->list); pefree(res->ptr, 1); } } From 7f4a76e9224ab52261c625bd614c428dc4cfcc49 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Feb 2019 23:57:18 +0200 Subject: [PATCH 1141/1986] TravisCI: enable connection pooling --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5815e0f7ed..c9c3c3418a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ before_script: # but --cluster is an unknown option for travis trusty - echo yes | ruby redis-trib.rb create --replicas 3 $(seq -f 127.0.0.1:%g 7000 7011) - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - echo 'redis.pconnect.pooling_enabled = 1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini script: - php tests/TestRedis.php --class Redis - php tests/TestRedis.php --class RedisArray From c1433b0d367bf2c4ee829deb7148ee46cbdfa829 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 24 Feb 2019 21:43:44 +0200 Subject: [PATCH 1142/1986] Fix review comments --- common.h | 16 ---------------- library.c | 1 - 2 files changed, 17 deletions(-) diff --git a/common.h b/common.h index 11a434a44d..8badbb831f 100644 --- a/common.h +++ b/common.h @@ -69,22 +69,6 @@ zend_string_realloc(zend_string *s, size_t len, int persistent) return zstr; } -#define strpprintf zend_strpprintf - -static zend_string * -zend_strpprintf(size_t max_len, const char *format, ...) -{ - va_list ap; - zend_string *zstr; - - va_start(ap, format); - zstr = ecalloc(1, sizeof(*zstr)); - ZSTR_LEN(zstr) = vspprintf(&ZSTR_VAL(zstr), max_len, format, ap); - zstr->gc = 0x11; - va_end(ap); - return zstr; -} - #define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0) #define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) diff --git a/library.c b/library.c index 6170320801..5e22d88656 100644 --- a/library.c +++ b/library.c @@ -57,7 +57,6 @@ redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) le->type = le_redis_pconnect; le->ptr = p; zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); - p->nb_active = 0; } zend_string_release(persistent_id); return le->ptr; From 46a50c128700a608f93a9603550f7bb4feb14496 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Mar 2019 13:53:01 -0700 Subject: [PATCH 1143/1986] Nuke missed PHP5/PHP7 conditionals --- common.h | 447 ------------------------------------------------------ library.c | 9 +- 2 files changed, 3 insertions(+), 453 deletions(-) diff --git a/common.h b/common.h index 8badbb831f..caec1feca5 100644 --- a/common.h +++ b/common.h @@ -9,453 +9,6 @@ #include "zend_llist.h" #include #include -#if (PHP_MAJOR_VERSION < 7) -#include -typedef smart_str smart_string; -#define smart_string_0(x) smart_str_0(x) -#define smart_string_appendc(dest, c) smart_str_appendc(dest, c) -#define smart_string_append_long(dest, val) smart_str_append_long(dest, val) -#define smart_string_appendl(dest, src, len) smart_str_appendl(dest, src, len) - -typedef struct { - short gc; - size_t len; - char *val; -} zend_string; - -#define REDIS_MAKE_STD_ZVAL(zv) MAKE_STD_ZVAL(zv) -#define REDIS_FREE_ZVAL(zv) (efree(zv)) - -#define ZSTR_VAL(s) (s)->val -#define ZSTR_LEN(s) (s)->len - -static zend_always_inline zend_string * -zend_string_alloc(size_t len, int persistent) -{ - zend_string *zstr = emalloc(sizeof(*zstr) + len + 1); - - ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); - ZSTR_LEN(zstr) = len; - zstr->gc = 0x01; - return zstr; -} - -static zend_always_inline zend_string * -zend_string_init(const char *str, size_t len, int persistent) -{ - zend_string *zstr = zend_string_alloc(len, persistent); - - memcpy(ZSTR_VAL(zstr), str, len); - ZSTR_VAL(zstr)[len] = '\0'; - return zstr; -} - -static zend_always_inline zend_string * -zend_string_realloc(zend_string *s, size_t len, int persistent) -{ - zend_string *zstr; - - if (!s->gc) { - zstr = zend_string_init(ZSTR_VAL(s), len, 0); - } else if (s->gc & 0x10) { - ZSTR_VAL(s) = erealloc(ZSTR_VAL(s), len + 1); - ZSTR_LEN(s) = len; - zstr = s; - } else { - zstr = erealloc(s, sizeof(*zstr) + len + 1); - ZSTR_VAL(zstr) = (char *)zstr + sizeof(*zstr); - ZSTR_LEN(zstr) = len; - } - return zstr; -} - -#define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0) - -#define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)) -#define zend_string_equal_content(s1, s2) (ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2)) -#define zend_string_equals(s1, s2) (s1 == s2 || zend_string_equal_content(s1, s2)) - -#define zend_string_release(s) do { \ - if ((s) && (s)->gc) { \ - if ((s)->gc & 0x10 && ZSTR_VAL(s)) efree(ZSTR_VAL(s)); \ - if ((s)->gc & 0x01) efree((s)); \ - } \ -} while (0) - -#define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) do { \ - HashPosition _hpos; \ - for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ - zend_hash_move_forward_ex(ht, &_hpos) \ - ) { \ - zend_string _zstr = {0}; \ - char *_str_index; uint _str_length; ulong _num_index; \ - _h = 0; _key = NULL; _val = zend_hash_get_current_data_ex(ht, &_hpos); \ - switch (zend_hash_get_current_key_ex(ht, &_str_index, &_str_length, &_num_index, 0, &_hpos)) { \ - case HASH_KEY_IS_STRING: \ - _zstr.len = _str_length - 1; \ - _zstr.val = _str_index; \ - _key = &_zstr; \ - break; \ - case HASH_KEY_IS_LONG: \ - _h = _num_index; \ - break; \ - default: \ - /* noop */ break; \ - } - -#define ZEND_HASH_FOREACH_VAL(ht, _val) do { \ - HashPosition _hpos; \ - for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ - zend_hash_move_forward_ex(ht, &_hpos) \ - ) { \ - _val = zend_hash_get_current_data_ex(ht, &_hpos); \ - -#define ZEND_HASH_FOREACH_PTR(ht, _ptr) do { \ - HashPosition _hpos; \ - for (zend_hash_internal_pointer_reset_ex(ht, &_hpos); \ - zend_hash_has_more_elements_ex(ht, &_hpos) == SUCCESS; \ - zend_hash_move_forward_ex(ht, &_hpos) \ - ) { \ - _ptr = zend_hash_get_current_data_ptr_ex(ht, &_hpos); \ - -#define ZEND_HASH_FOREACH_END() \ - } \ -} while(0) - -#undef zend_hash_get_current_key -#define zend_hash_get_current_key(ht, str_index, num_index) \ - zend_hash_get_current_key_ex(ht, str_index, NULL, num_index, 0, NULL) - -#define zend_hash_str_exists(ht, str, len) zend_hash_exists(ht, str, len + 1) - -static zend_always_inline zval * -zend_hash_str_find(const HashTable *ht, const char *key, size_t len) -{ - zval **zv; - - if (zend_hash_find(ht, key, len + 1, (void **)&zv) == SUCCESS) { - return *zv; - } - return NULL; -} - -#define zend_hash_find_ptr(ht, s) zend_hash_str_find_ptr(ht, ZSTR_VAL(s), ZSTR_LEN(s)) - -static zend_always_inline void * -zend_hash_str_find_ptr(const HashTable *ht, const char *str, size_t len) -{ - void **ptr; - - if (zend_hash_find(ht, str, len + 1, (void **)&ptr) == SUCCESS) { - return *ptr; - } - return NULL; -} - -#define zend_hash_str_update_ptr(ht, str, len, pData) zend_hash_str_update_mem(ht, str, len, pData, sizeof(void *)) - -static zend_always_inline void * -zend_hash_str_update_mem(HashTable *ht, const char *str, size_t len, void *pData, size_t size) -{ - if (zend_hash_update(ht, str, len + 1, (void *)&pData, size, NULL) == SUCCESS) { - return pData; - } - return NULL; -} - -static zend_always_inline void * -zend_hash_index_update_ptr(HashTable *ht, zend_ulong h, void *pData) -{ - if (zend_hash_index_update(ht, h, (void **)&pData, sizeof(void *), NULL) == SUCCESS) { - return pData; - } - return NULL; -} - -#undef zend_hash_get_current_data -static zend_always_inline zval * -zend_hash_get_current_data(HashTable *ht) -{ - zval **zv; - - if (zend_hash_get_current_data_ex(ht, (void **)&zv, NULL) == SUCCESS) { - return *zv; - } - return NULL; -} - -static zend_always_inline void * -zend_hash_get_current_data_ptr_ex(HashTable *ht, HashPosition *pos) -{ - void **ptr; - - if (zend_hash_get_current_data_ex(ht, (void **)&ptr, pos) == SUCCESS) { - return *ptr; - } - return NULL; -} -#define zend_hash_get_current_data_ptr(ht) zend_hash_get_current_data_ptr_ex(ht, NULL) - -static int (*_zend_hash_index_find)(const HashTable *, ulong, void **) = &zend_hash_index_find; -#define zend_hash_index_find(ht, h) inline_zend_hash_index_find(ht, h) - -static zend_always_inline zval * -inline_zend_hash_index_find(const HashTable *ht, zend_ulong h) -{ - zval **zv; - if (_zend_hash_index_find(ht, h, (void **)&zv) == SUCCESS) { - return *zv; - } - return NULL; -} - -static zend_always_inline void * -zend_hash_index_find_ptr(const HashTable *ht, zend_ulong h) -{ - void **ptr; - - if (_zend_hash_index_find(ht, h, (void **)&ptr) == SUCCESS) { - return *ptr; - } - return NULL; -} - -static int (*_zend_hash_get_current_data_ex)(HashTable *, void **, HashPosition *) = &zend_hash_get_current_data_ex; -#define zend_hash_get_current_data_ex(ht, pos) inline_zend_hash_get_current_data_ex(ht, pos) -static zend_always_inline zval * -inline_zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos) -{ - zval **zv; - if (_zend_hash_get_current_data_ex(ht, (void **)&zv, pos) == SUCCESS) { - return *zv; - } - return NULL; -} - -#undef zend_hash_next_index_insert -#define zend_hash_next_index_insert(ht, pData) \ - _zend_hash_next_index_insert(ht, pData ZEND_FILE_LINE_CC) -static zend_always_inline zval * -_zend_hash_next_index_insert(HashTable *ht, zval *pData ZEND_FILE_LINE_DC) -{ - if (_zend_hash_index_update_or_next_insert(ht, 0, &pData, sizeof(pData), - NULL, HASH_NEXT_INSERT ZEND_FILE_LINE_CC) == SUCCESS - ) { - return pData; - } - return NULL; -} - -#undef zend_get_parameters_array -#define zend_get_parameters_array(ht, param_count, argument_array) \ - inline_zend_get_parameters_array(ht, param_count, argument_array TSRMLS_CC) - -static zend_always_inline int -inline_zend_get_parameters_array(int ht, int param_count, zval *argument_array TSRMLS_DC) -{ - int i, ret = FAILURE; - zval **zv = ecalloc(param_count, sizeof(zval *)); - - if (_zend_get_parameters_array(ht, param_count, zv TSRMLS_CC) == SUCCESS) { - for (i = 0; i < param_count; i++) { - argument_array[i] = *zv[i]; - } - ret = SUCCESS; - } - efree(zv); - return ret; -} - -typedef zend_rsrc_list_entry zend_resource; - -extern int (*_add_next_index_string)(zval *, const char *, int); -#define add_next_index_string(arg, str) _add_next_index_string(arg, str, 1); -extern int (*_add_next_index_stringl)(zval *, const char *, uint, int); -#define add_next_index_stringl(arg, str, length) _add_next_index_stringl(arg, str, length, 1); - -#undef ZVAL_STRING -#define ZVAL_STRING(z, s) do { \ - const char *_s = (s); \ - ZVAL_STRINGL(z, _s, strlen(_s)); \ -} while (0) -#undef RETVAL_STRING -#define RETVAL_STRING(s) ZVAL_STRING(return_value, s) -#undef RETURN_STRING -#define RETURN_STRING(s) { RETVAL_STRING(s); return; } - -#undef ZVAL_STRINGL -#define ZVAL_STRINGL(z, s, l) do { \ - const char *__s = (s); int __l = l; \ - zval *__z = (z); \ - Z_STRLEN_P(__z) = __l; \ - Z_STRVAL_P(__z) = estrndup(__s, __l); \ - Z_TYPE_P(__z) = IS_STRING; \ -} while (0) -#undef RETVAL_STRINGL -#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l) -#undef RETURN_STRINGL -#define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; } - -static int (*_call_user_function)(HashTable *, zval **, zval *, zval *, zend_uint, zval *[] TSRMLS_DC) = &call_user_function; -#define call_user_function(function_table, object, function_name, retval_ptr, param_count, params) \ - inline_call_user_function(function_table, object, function_name, retval_ptr, param_count, params TSRMLS_CC) - -static zend_always_inline int -inline_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, zend_uint param_count, zval params[] TSRMLS_DC) -{ - int i, ret; - zval **_params = NULL; - if (!params) param_count = 0; - if (param_count > 0) { - _params = ecalloc(param_count, sizeof(zval *)); - for (i = 0; i < param_count; ++i) { - zval *zv = ¶ms[i]; - MAKE_STD_ZVAL(_params[i]); - ZVAL_ZVAL(_params[i], zv, 1, 0); - } - } - ret = _call_user_function(function_table, &object, function_name, retval_ptr, param_count, _params TSRMLS_CC); - if (_params) { - for (i = 0; i < param_count; ++i) { - zval_ptr_dtor(&_params[i]); - } - efree(_params); - } - return ret; -} - -#undef add_assoc_bool -#define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key), __b) -extern int (*_add_assoc_bool_ex)(zval *, const char *, uint, int); -#define add_assoc_bool_ex(_arg, _key, _key_len, _b) _add_assoc_bool_ex(_arg, _key, _key_len + 1, _b) - -#undef add_assoc_long -#define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key), __n) -extern int (*_add_assoc_long_ex)(zval *, const char *, uint, long); -#define add_assoc_long_ex(_arg, _key, _key_len, _n) _add_assoc_long_ex(_arg, _key, _key_len + 1, _n) - -#undef add_assoc_double -#define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key), __d) -extern int (*_add_assoc_double_ex)(zval *, const char *, uint, double); -#define add_assoc_double_ex(_arg, _key, _key_len, _d) _add_assoc_double_ex(_arg, _key, _key_len + 1, _d) - -#undef add_assoc_string -#define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str) -extern int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int); -#define add_assoc_string_ex(_arg, _key, _key_len, _str) _add_assoc_string_ex(_arg, _key, _key_len + 1, _str, 1) - -extern int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int); -#define add_assoc_stringl_ex(_arg, _key, _key_len, _str, _length) _add_assoc_stringl_ex(_arg, _key, _key_len + 1, _str, _length, 1) - -#undef add_assoc_zval -#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key), __value) -extern int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *); -#define add_assoc_zval_ex(_arg, _key, _key_len, _value) _add_assoc_zval_ex(_arg, _key, _key_len + 1, _value); - -typedef long zend_long; -static zend_always_inline zend_long -zval_get_long(zval *op) -{ - switch (Z_TYPE_P(op)) { - case IS_BOOL: - case IS_LONG: - return Z_LVAL_P(op); - case IS_DOUBLE: - return zend_dval_to_lval(Z_DVAL_P(op)); - case IS_STRING: - { - double dval; - zend_long lval; - zend_uchar type = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, 0); - if (type == IS_LONG) { - return lval; - } else if (type == IS_DOUBLE) { - return zend_dval_to_lval(dval); - } - } - break; - EMPTY_SWITCH_DEFAULT_CASE() - } - return 0; -} - -static zend_always_inline double -zval_get_double(zval *op) -{ - switch (Z_TYPE_P(op)) { - case IS_BOOL: - case IS_LONG: - return (double)Z_LVAL_P(op); - case IS_DOUBLE: - return Z_DVAL_P(op); - case IS_STRING: - return zend_strtod(Z_STRVAL_P(op), NULL); - EMPTY_SWITCH_DEFAULT_CASE() - } - return 0.0; -} - -static zend_always_inline zend_string * -zval_get_string(zval *op) -{ - zend_string *zstr = ecalloc(1, sizeof(zend_string)); - - zstr->gc = 0; - ZSTR_VAL(zstr) = ""; - ZSTR_LEN(zstr) = 0; - switch (Z_TYPE_P(op)) { - case IS_STRING: - ZSTR_VAL(zstr) = Z_STRVAL_P(op); - ZSTR_LEN(zstr) = Z_STRLEN_P(op); - break; - case IS_BOOL: - if (Z_LVAL_P(op)) { - ZSTR_VAL(zstr) = "1"; - ZSTR_LEN(zstr) = 1; - } - break; - case IS_LONG: { - zstr->gc = 0x10; - ZSTR_LEN(zstr) = spprintf(&ZSTR_VAL(zstr), 0, "%ld", Z_LVAL_P(op)); - break; - } - case IS_DOUBLE: { - zstr->gc = 0x10; - ZSTR_LEN(zstr) = spprintf(&ZSTR_VAL(zstr), 0, "%.16g", Z_DVAL_P(op)); - break; - } - EMPTY_SWITCH_DEFAULT_CASE() - } - zstr->gc |= 0x01; - return zstr; -} - -extern void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC); -#define php_var_serialize(buf, struc, data) _php_var_serialize(buf, &struc, data TSRMLS_CC) -extern int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC); -#define php_var_unserialize(rval, p, max, var_hash) _php_var_unserialize(&rval, p, max, var_hash TSRMLS_CC) -typedef int strlen_t; - -#define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_BOOL && !Z_BVAL_P(z)) - -/* If ZEND_MOD_END isn't defined, use legacy version */ -#ifndef ZEND_MOD_END -#define ZEND_MOD_END { NULL, NULL, NULL } -#endif - -/* PHP_FE_END exists since 5.3.7 */ -#ifndef PHP_FE_END -#define PHP_FE_END { NULL, NULL, NULL } -#endif - -/* References don't need any actions */ -#define ZVAL_DEREF(v) PHPREDIS_NOTUSED(v) - -#define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)zend_objects_get_address(z TSRMLS_CC) - -#else #include #include diff --git a/library.c b/library.c index 5e22d88656..8f675d33ea 100644 --- a/library.c +++ b/library.c @@ -556,7 +556,7 @@ union resparg { /* A printf like method to construct a Redis RESP command. It has been extended * to take a few different format specifiers that are convienient to phpredis. * - * s - C string followed by length as a + * s - C string followed by length as a * S - Pointer to a zend_string * k - Same as 's' but the value will be prefixed if phpredis is set up do do * that and the working slot will be set if it has been passed. @@ -1693,9 +1693,6 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) const char *fmtstr = "%s:%d"; int host_len, usocket = 0, err = 0, tcp_flag = 1; ConnectionPool *p = NULL; -#if (PHP_MAJOR_VERSION < 7) - char *estr = NULL; -#else zend_string *estr = NULL; if (redis_sock->stream != NULL) { @@ -2224,7 +2221,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, PHP_VAR_UNSERIALIZE_INIT(var_hash); ret = php_var_unserialize(z_ret, (const unsigned char **)&val, - (const unsigned char *)val + val_len, + (const unsigned char *)val + val_len, &var_hash); //if (php_var_unserialize(z_ret, (const unsigned char**)&val, @@ -2445,7 +2442,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s // Construct an array for our sub element, and add it, and recurse array_init(&z_subelem); add_next_index_zval(z_ret, &z_subelem); - redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, + redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, &z_subelem TSRMLS_CC); break; default: From 796931999a81f67693bcad70e53fe7ec99a1bed5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Mar 2019 14:14:13 -0700 Subject: [PATCH 1144/1986] Get rid of warning --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 8f675d33ea..a5254372d6 100644 --- a/library.c +++ b/library.c @@ -1738,7 +1738,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } gettimeofday(&tv, NULL); - persistent_id = strpprintf(0, "phpredis_%d%d", tv.tv_sec, tv.tv_usec); + persistent_id = strpprintf(0, "phpredis_%ld%ld", tv.tv_sec, tv.tv_usec); } else { if (redis_sock->persistent_id) { persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id)); From feb582fe434e18112206a33dfeea538084fbdbf2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Mar 2019 14:17:53 -0700 Subject: [PATCH 1145/1986] This should be even more portable --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index a5254372d6..084bbdf30a 100644 --- a/library.c +++ b/library.c @@ -1738,7 +1738,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } gettimeofday(&tv, NULL); - persistent_id = strpprintf(0, "phpredis_%ld%ld", tv.tv_sec, tv.tv_usec); + persistent_id = strpprintf(0, "phpredis_%ld%ld", (long)tv.tv_sec, (long)tv.tv_usec); } else { if (redis_sock->persistent_id) { persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id)); From 4601887dad7168f44a12b42f881c240ef3329d7a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Mar 2019 14:26:44 -0700 Subject: [PATCH 1146/1986] More cleanup of PHP5 conditionals/commented code --- common.h | 7 ------- library.c | 6 ------ 2 files changed, 13 deletions(-) diff --git a/common.h b/common.h index caec1feca5..23769d5be5 100644 --- a/common.h +++ b/common.h @@ -275,17 +275,10 @@ typedef struct { int nb_active; } ConnectionPool; -#if (PHP_MAJOR_VERSION < 7) -typedef struct { - zend_object std; - RedisSock *sock; -} redis_object; -#else typedef struct { RedisSock *sock; zend_object std; } redis_object; -#endif /** Argument info for any function expecting 0 args */ ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) diff --git a/library.c b/library.c index 084bbdf30a..ef134a27eb 100644 --- a/library.c +++ b/library.c @@ -2224,12 +2224,6 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, (const unsigned char *)val + val_len, &var_hash); - //if (php_var_unserialize(z_ret, (const unsigned char**)&val, - // (const unsigned char*)val + val_len, &var_hash) - //) { - // ret = 1; - //} - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); break; From 6ebb36ce65860d66ed9499bf237ce88e93ff770e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Mar 2019 16:06:53 -0700 Subject: [PATCH 1147/1986] Get rid of ifdefs --- common.h | 7 ------- library.c | 35 ----------------------------------- 2 files changed, 42 deletions(-) diff --git a/common.h b/common.h index caec1feca5..23769d5be5 100644 --- a/common.h +++ b/common.h @@ -275,17 +275,10 @@ typedef struct { int nb_active; } ConnectionPool; -#if (PHP_MAJOR_VERSION < 7) -typedef struct { - zend_object std; - RedisSock *sock; -} redis_object; -#else typedef struct { RedisSock *sock; zend_object std; } redis_object; -#endif /** Argument info for any function expecting 0 args */ ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) diff --git a/library.c b/library.c index 9c993db1a2..e91e156927 100644 --- a/library.c +++ b/library.c @@ -38,41 +38,6 @@ #include /* SO_KEEPALIVE */ #else #include - - # if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 4 - /* This proto is available from 5.5 on only */ - PHP_REDIS_API int usleep(unsigned int useconds); - # endif -#endif - -#if (PHP_MAJOR_VERSION < 7) - int (*_add_next_index_string)(zval *, const char *, int) = &add_next_index_string; - int (*_add_next_index_stringl)(zval *, const char *, uint, int) = &add_next_index_stringl; - int (*_add_assoc_bool_ex)(zval *, const char *, uint, int) = &add_assoc_bool_ex; - int (*_add_assoc_long_ex)(zval *, const char *, uint, long) = &add_assoc_long_ex; - int (*_add_assoc_double_ex)(zval *, const char *, uint, double) = &add_assoc_double_ex; - int (*_add_assoc_string_ex)(zval *, const char *, uint, char *, int) = &add_assoc_string_ex; - int (*_add_assoc_stringl_ex)(zval *, const char *, uint, char *, uint, int) = &add_assoc_stringl_ex; - int (*_add_assoc_zval_ex)(zval *, const char *, uint, zval *) = &add_assoc_zval_ex; - void (*_php_var_serialize)(smart_str *, zval **, php_serialize_data_t * TSRMLS_DC) = &php_var_serialize; - int (*_php_var_unserialize)(zval **, const unsigned char **, const unsigned char *, php_unserialize_data_t * TSRMLS_DC) = &php_var_unserialize; - -#define strpprintf zend_strpprintf - -static zend_string * -zend_strpprintf(size_t max_len, const char *format, ...) -{ - va_list ap; - zend_string *zstr; - - va_start(ap, format); - zstr = ecalloc(1, sizeof(*zstr)); - ZSTR_LEN(zstr) = vspprintf(&ZSTR_VAL(zstr), max_len, format, ap); - zstr->gc = 0x11; - va_end(ap); - return zstr; -} - #endif extern zend_class_entry *redis_ce; From ea081e05c2fc069790b3031d0daa174fb28820c2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Mar 2019 17:01:44 -0700 Subject: [PATCH 1148/1986] Use a more specific name for our 'slot caching enabled' define --- redis_cluster.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 2688c178d0..4b11c987a3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -372,14 +372,14 @@ static zend_string *cluster_hash_seeds(HashTable *ht) { return hash.s; } -#define CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1) +#define SLOT_CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1) static redisCachedCluster *cluster_cache_load(HashTable *ht_seeds TSRMLS_DC) { zend_resource *le; zend_string *h; /* Short circuit if we're not caching slots or if our seeds don't have any * elements, since it doesn't make sense to cache an empty string */ - if (!CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) + if (!SLOT_CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) return NULL; /* Look for cached slot information */ @@ -408,8 +408,8 @@ static int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes TSRMLS_DC) zend_string *hash; /* Short circuit if caching is disabled or there aren't any seeds */ - if (!CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) - return !CACHING_ENABLED() ? SUCCESS : FAILURE; + if (!SLOT_CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) + return !SLOT_CACHING_ENABLED() ? SUCCESS : FAILURE; /* Construct our cache */ hash = cluster_hash_seeds(ht_seeds); From fdbe9d29491a085294ca6dfe944b042dfc448ea2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Mar 2019 17:05:41 -0700 Subject: [PATCH 1149/1986] Remove last remnants of PHP5 --- redis.c | 20 -------------------- redis_cluster.c | 8 -------- 2 files changed, 28 deletions(-) diff --git a/redis.c b/redis.c index 4cbc451db7..64a75d778d 100644 --- a/redis.c +++ b/redis.c @@ -112,11 +112,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1) ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2) @@ -131,11 +127,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1) ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1) @@ -155,20 +147,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_exists, 0, 0, 1) ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1) @@ -227,11 +211,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1) ZEND_ARG_INFO(0, cmd) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, args) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() /** diff --git a/redis_cluster.c b/redis_cluster.c index 4b11c987a3..c234a2062b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -52,11 +52,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1) ZEND_ARG_INFO(0, key) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_keys) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1) @@ -74,11 +70,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_key_or_address_variadic, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_ARG_INFO(0, arg) -#if PHP_VERSION_ID >= 50600 ZEND_ARG_VARIADIC_INFO(0, other_args) -#else - ZEND_ARG_INFO(0, ...) -#endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 1) From a9fe96e2ec46cb820e1b10a3a30372a924e1c227 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Mar 2019 18:43:50 -0700 Subject: [PATCH 1150/1986] Document slot caching --- cluster.markdown | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cluster.markdown b/cluster.markdown index 96cb13705b..e451070456 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -45,6 +45,9 @@ $obj_cluster = new RedisCluster('mycluster'); On construction, the RedisCluster class will iterate over the provided seed nodes until it can attain a connection to the cluster and run CLUSTER SLOTS to map every node in the cluster locally. Once the keyspace is mapped, RedisCluster will only connect to nodes when it needs to (e.g. you're getting a key that we believe is on that node.) +## Slot caching +Each time the a `RedisCluster` class is constructed from scratch, phpredis needs to execute a `CLUSTER SLOTS` command to map the keyspace. Although this isn't an expensive command, it does require a round trip for each newly created object, which is inefficient. Starting from PhpRedis 5.0.0 these slots can be cached by setting `redis.clusters.cache_slots = 1` in `php.ini`. + ## Timeouts Because Redis cluster is intended to provide high availability, timeouts do not work in the same way they do in normal socket communication. It's fully possible to have a timeout or even exception on a given socket (say in the case that a master node has failed), and continue to serve the request if and when a slave can be promoted as the new master. From 603ba48d58409b4bb4f8012b3eceb4596e064945 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 20 Mar 2019 05:32:54 -0700 Subject: [PATCH 1151/1986] Fix documentation for zRem/zDelete Fixes #1527 --- README.markdown | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.markdown b/README.markdown index 7b67225b63..a0aea736a2 100644 --- a/README.markdown +++ b/README.markdown @@ -2835,24 +2835,24 @@ $redis->zRevRank('key', 'two'); /* 0 */ ### zRem, zDelete ----- -_**Description**_: Deletes a specified member from the ordered set. +_**Description**_: Delete one or more members from a sorted set. -##### *Parameters* -*key* -*member* +##### *Prototype* +~~~php +$redis->zRem($key, $member [, $member ...]); +~~~ ##### *Return value* -*LONG* 1 on success, 0 on failure. +*LONG:* The number of members deleted. ##### *Example* ~~~php -$redis->zAdd('key', 0, 'val0'); -$redis->zAdd('key', 2, 'val2'); -$redis->zAdd('key', 10, 'val10'); -$redis->zDelete('key', 'val2'); -$redis->zRange('key', 0, -1); /* ['val0', 'val10'] */ +$redis->zAdd('key', 0, 'val0', 1, 'val1', 2, 'val2'); +$redis->zRem('key', 'val0', 'val1', 'val2'); // Returns: 3 ~~~ +**Note:** `zDelete` is an alias for `zRem` and may be removed in future versions of phpredis. + ### zRemRangeByRank, zDeleteRangeByRank ----- _**Description**_: Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end]. From e8fb49be5f33ad61ac4c8a358b327b88014f3494 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Mar 2019 14:10:58 +0200 Subject: [PATCH 1152/1986] Issue #1523 Add server address to exception message. --- library.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index e91e156927..671055b3a0 100644 --- a/library.c +++ b/library.c @@ -2295,11 +2295,19 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { + char *errmsg = NULL; + + if (redis_sock->port < 0) { + spprintf(&errmsg, 0, "read error on connection to %S", redis_sock->host); + } else { + spprintf(&errmsg, 0, "read error on connection to %S:%d", redis_sock->host, redis_sock->port); + } // Close our socket redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); // Throw a read error exception - REDIS_THROW_EXCEPTION( "read error on connection", 0); + REDIS_THROW_EXCEPTION(errmsg, 0); + efree(errmsg); return -1; } From 6a1685d33bbee73e703f7ad49114cb3b54ebcd49 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Mar 2019 16:28:58 +0200 Subject: [PATCH 1153/1986] TravisCI: latest stable igbinary --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c9c3c3418a..d54beeae20 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ addons: packages: clang before_install: - phpize - - pecl install igbinary-2.0.8 && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf + - pecl install igbinary && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf install: make install before_script: - gem install redis From d5b8f833404ce81c120b14a930427a74c3cb5b26 Mon Sep 17 00:00:00 2001 From: "B. Gortney" Date: Wed, 14 Dec 2016 11:32:00 -0500 Subject: [PATCH 1154/1986] rebase msgpack pull request (#801) on develop branch --- common.h | 1 + config.m4 | 54 +++++++++++++++++++++++++-- library.c | 91 +++++++++++++++++++++++++++++++++++++++++++++ redis.c | 4 ++ redis_commands.c | 3 ++ tests/RedisTest.php | 22 ++++++++++- 6 files changed, 170 insertions(+), 5 deletions(-) mode change 100755 => 100644 config.m4 diff --git a/common.h b/common.h index 23769d5be5..2b55efb946 100644 --- a/common.h +++ b/common.h @@ -85,6 +85,7 @@ typedef enum _PUBSUB_TYPE { #define REDIS_SERIALIZER_NONE 0 #define REDIS_SERIALIZER_PHP 1 #define REDIS_SERIALIZER_IGBINARY 2 +#define REDIS_SERIALIZER_MSGPACK 3 /* compression */ #define REDIS_COMPRESSION_NONE 0 #define REDIS_COMPRESSION_LZF 1 diff --git a/config.m4 b/config.m4 old mode 100755 new mode 100644 index 8a9072f169..e4ef9de3c8 --- a/config.m4 +++ b/config.m4 @@ -3,10 +3,10 @@ dnl config.m4 for extension redis PHP_ARG_ENABLE(redis, whether to enable redis support, dnl Make sure that the comment is aligned: -[ --enable-redis Enable redis support]) +[ --enable-redis Enable redis support]) -PHP_ARG_ENABLE(redis-session, whether to enable sessions, -[ --disable-redis-session Disable session support], yes, no) +PHP_ARG_ENABLE(redis-session, whether to disable sessions, +[ --disable-redis-session Disable session support], yes, no) PHP_ARG_ENABLE(redis-igbinary, whether to enable igbinary serializer support, [ --enable-redis-igbinary Enable igbinary serializer support], no, no) @@ -16,6 +16,10 @@ PHP_ARG_ENABLE(redis-lzf, whether to enable lzf compression, PHP_ARG_WITH(liblzf, use system liblzf, [ --with-liblzf[=DIR] Use system liblzf], no, no) +[ --enable-redis-igbinary Enable igbinary serializer support], no, no) + +PHP_ARG_ENABLE(redis-msgpack, whether to enable msgpack serializer support, +[ --enable-redis-msgpack Enable msgpack serializer support], no, no) if test "$PHP_REDIS" != "no"; then @@ -35,7 +39,7 @@ dnl Check for igbinary elif test -f "$phpincludedir/ext/igbinary/igbinary.h"; then igbinary_inc_path="$phpincludedir" else - for i in php php4 php5 php6; do + for i in php php4 php5 php6 php7; do if test -f "$prefix/include/$i/ext/igbinary/igbinary.h"; then igbinary_inc_path="$prefix/include/$i" fi @@ -102,6 +106,48 @@ dnl Check for igbinary AC_DEFINE_UNQUOTED(GIT_REVISION, ["$(git log -1 --format=%H)"], [ ]) fi +dnl Check for msgpack + if test "$PHP_REDIS_MSGPACK" != "no"; then + AC_MSG_CHECKING([for msgpack includes]) + msgpack_inc_path="" + + if test -f "$abs_srcdir/include/php/ext/msgpack/php_msgpack.h"; then + msgpack_inc_path="$abs_srcdir/include/php" + elif test -f "$abs_srcdir/ext/msgpack/php_msgpack.h"; then + msgpack_inc_path="$abs_srcdir" + elif test -f "$phpincludedir/ext/msgpack/php_msgpack.h"; then + msgpack_inc_path="$phpincludedir" + else + for i in php php4 php5 php6 php7; do + if test -f "$prefix/include/$i/ext/msgpack/php_msgpack.h"; then + msgpack_inc_path="$prefix/include/$i" + fi + done + fi + + if test "$msgpack_inc_path" = ""; then + AC_MSG_ERROR([Cannot find php_msgpack.h]) + else + AC_MSG_RESULT([$msgpack_inc_path]) + fi + fi + + AC_MSG_CHECKING([for redis msgpack support]) + if test "$PHP_REDIS_MSGPACK" != "no"; then + AC_MSG_RESULT([enabled]) + AC_DEFINE(HAVE_REDIS_MSGPACK,1,[Whether redis msgpack serializer is enabled]) + MSGPACK_INCLUDES="-I$msgpack_inc_path" + MSGPACK_EXT_DIR="$msgpack_inc_path/ext" + ifdef([PHP_ADD_EXTENSION_DEP], + [ + PHP_ADD_EXTENSION_DEP(redis, msgpack) + ]) + PHP_ADD_INCLUDE($MSGPACK_EXT_DIR) + else + MSGPACK_INCLUDES="" + AC_MSG_RESULT([disabled]) + fi + dnl # --with-redis -> check with-path dnl SEARCH_PATH="/usr/local /usr" # you might want to change this dnl SEARCH_FOR="/include/redis.h" # you most likely want to change this diff --git a/library.c b/library.c index e91e156927..3bf2fbeac2 100644 --- a/library.c +++ b/library.c @@ -9,6 +9,9 @@ #ifdef HAVE_REDIS_IGBINARY #include "igbinary/igbinary.h" #endif +#ifdef HAVE_REDIS_MSGPACK +#include "msgpack/php_msgpack.h" +#endif #ifdef HAVE_REDIS_LZF #include @@ -1894,6 +1897,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, } return -1; } + numElems = atoi(inbuf+1); zval z_multi_result; array_init(&z_multi_result); /* pre-allocate array for multi's results. */ @@ -1910,6 +1914,67 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, return 0; } +/** + * redis_sock_read_multibulk_reply_vals + * + * This is identical to redis_sock_read_multibulk_reply except that it unserializes vals only, rather than rely on + * the chosen unserializer to silently return 0 after a failed attempt (which msgpack does not do). + * + * Perhaps not the optimal solution, but the easiest way to resolve the problem of failed attempts to + * unserialize a key that hadn't been serialized to begin with in blpop, brpop. + * + */ +PHP_REDIS_API int redis_sock_read_multibulk_reply_vals(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + void *ctx) +{ + char inbuf[1024]; + int numElems, err_len; + + if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { + return -1; + } + + if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { + REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); + zend_throw_exception(redis_exception_ce, "read error on connection", 0 + TSRMLS_CC); + return -1; + } + + if(inbuf[0] != '*') { + IF_MULTI_OR_PIPELINE() { + add_next_index_bool(z_tab, 0); + } else { + if (inbuf[0] == '-') { + err_len = strlen(inbuf+1) - 2; + redis_sock_set_err(redis_sock, inbuf+1, err_len); + } + RETVAL_FALSE; + } + return -1; + } + + numElems = atoi(inbuf+1); + zval zv, *z_multi_result = &zv; +#if (PHP_MAJOR_VERSION < 7) + MAKE_STD_ZVAL(z_multi_result); +#endif + array_init(z_multi_result); /* pre-allocate array for multi's results. */ + + redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_multi_result, numElems, UNSERIALIZE_VALS); + + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_multi_result); + } else { + RETVAL_ZVAL(z_multi_result, 0, 1); + } + /*zval_copy_ctor(return_value); */ + return 0; +} + + /* Like multibulk reply, but don't touch the values, they won't be unserialized * (this is used by HKEYS). */ PHP_REDIS_API int @@ -2195,6 +2260,17 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len PHP_VAR_SERIALIZE_DESTROY(ht); return 1; + + case REDIS_SERIALIZER_MSGPACK: +#ifdef HAVE_REDIS_MSGPACK + php_msgpack_serialize(&sstr, z TSRMLS_CC); + *val = estrndup(sstr.s->val, sstr.s->len); + *val_len = sstr.s->len; + smart_str_free(&sstr); + + return 1; +#endif + break; case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { @@ -2205,6 +2281,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len #endif break; } + return 0; } @@ -2227,6 +2304,19 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, PHP_VAR_UNSERIALIZE_DESTROY(var_hash); break; + case REDIS_SERIALIZER_MSGPACK: +#ifdef HAVE_REDIS_MSGPACK + /* + * Would like to be able to check to see if a string is msgpack'd (like with igbinary, below), + * but I don't believe there's an easy way to do that as there's no consistent header or + * other simple indication of packed-ness in msgpacked binary sequences, as far as I know. + */ + + php_msgpack_unserialize(z_ret, (char *)val, (size_t)val_len TSRMLS_CC); + ret = 1; +#endif + break; + case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY /* @@ -2257,6 +2347,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, #endif break; } + return ret; } diff --git a/redis.c b/redis.c index 64a75d778d..65e6a9d91a 100644 --- a/redis.c +++ b/redis.c @@ -693,6 +693,10 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES TSRMLS_CC); } +#ifdef HAVE_REDIS_MSGPACK + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK TSRMLS_CC); +#endif + zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5 TSRMLS_CC); zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } diff --git a/redis_commands.c b/redis_commands.c index 3c127b822f..30324b6141 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3886,6 +3886,9 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, if (val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP #ifdef HAVE_REDIS_IGBINARY || val_long == REDIS_SERIALIZER_IGBINARY +#endif +#ifdef HAVE_REDIS_MSGPACK + || val_long == REDIS_SERIALIZER_MSGPACK #endif ) { redis_sock->serializer = val_long; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 81d497347f..a865ffd7e1 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4223,6 +4223,17 @@ public function testSerializerIGBinary() { } } + 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, ""); + } + } + private function checkSerializer($mode) { $this->redis->del('key'); @@ -4716,6 +4727,10 @@ public function testSerialize() { $arr_serializers[] = Redis::SERIALIZER_IGBINARY; } + if(defined('Redis::SERIALIZER_MSGPACK')) { + $arr_serializers[] = Redis::SERIALIZER_MSGPACK; + } + foreach($arr_serializers as $mode) { $arr_enc = []; $arr_dec = []; @@ -4735,11 +4750,16 @@ public function testUnserialize() { 1,1.5,'one',['this','is','an','array'] ]; - $serializers = [Redis::SERIALIZER_PHP]; + $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 = []; From 545250f30b88ab1adb644d7ec8fb64bc88f90e32 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 20 Mar 2019 19:32:21 -0700 Subject: [PATCH 1155/1986] Cleanup rebase artifact and move msgpack to serializer section --- config.m4 | 7 +++---- library.c | 61 ------------------------------------------------------- 2 files changed, 3 insertions(+), 65 deletions(-) diff --git a/config.m4 b/config.m4 index e4ef9de3c8..0a410bb983 100644 --- a/config.m4 +++ b/config.m4 @@ -11,15 +11,14 @@ PHP_ARG_ENABLE(redis-session, whether to disable sessions, PHP_ARG_ENABLE(redis-igbinary, whether to enable igbinary serializer support, [ --enable-redis-igbinary Enable igbinary serializer support], no, no) +PHP_ARG_ENABLE(redis-msgpack, whether to enable msgpack serializer support, +[ --enable-redis-msgpack Enable msgpack serializer support], no, no) + PHP_ARG_ENABLE(redis-lzf, whether to enable lzf compression, [ --enable-redis-lzf Enable lzf compression support], no, no) PHP_ARG_WITH(liblzf, use system liblzf, [ --with-liblzf[=DIR] Use system liblzf], no, no) -[ --enable-redis-igbinary Enable igbinary serializer support], no, no) - -PHP_ARG_ENABLE(redis-msgpack, whether to enable msgpack serializer support, -[ --enable-redis-msgpack Enable msgpack serializer support], no, no) if test "$PHP_REDIS" != "no"; then diff --git a/library.c b/library.c index 3bf2fbeac2..ef7f579dac 100644 --- a/library.c +++ b/library.c @@ -1914,67 +1914,6 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, return 0; } -/** - * redis_sock_read_multibulk_reply_vals - * - * This is identical to redis_sock_read_multibulk_reply except that it unserializes vals only, rather than rely on - * the chosen unserializer to silently return 0 after a failed attempt (which msgpack does not do). - * - * Perhaps not the optimal solution, but the easiest way to resolve the problem of failed attempts to - * unserialize a key that hadn't been serialized to begin with in blpop, brpop. - * - */ -PHP_REDIS_API int redis_sock_read_multibulk_reply_vals(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, - void *ctx) -{ - char inbuf[1024]; - int numElems, err_len; - - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { - return -1; - } - - if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { - REDIS_STREAM_CLOSE_MARK_FAILED(redis_sock); - zend_throw_exception(redis_exception_ce, "read error on connection", 0 - TSRMLS_CC); - return -1; - } - - if(inbuf[0] != '*') { - IF_MULTI_OR_PIPELINE() { - add_next_index_bool(z_tab, 0); - } else { - if (inbuf[0] == '-') { - err_len = strlen(inbuf+1) - 2; - redis_sock_set_err(redis_sock, inbuf+1, err_len); - } - RETVAL_FALSE; - } - return -1; - } - - numElems = atoi(inbuf+1); - zval zv, *z_multi_result = &zv; -#if (PHP_MAJOR_VERSION < 7) - MAKE_STD_ZVAL(z_multi_result); -#endif - array_init(z_multi_result); /* pre-allocate array for multi's results. */ - - redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_multi_result, numElems, UNSERIALIZE_VALS); - - IF_MULTI_OR_PIPELINE() { - add_next_index_zval(z_tab, z_multi_result); - } else { - RETVAL_ZVAL(z_multi_result, 0, 1); - } - /*zval_copy_ctor(return_value); */ - return 0; -} - - /* Like multibulk reply, but don't touch the values, they won't be unserialized * (this is used by HKEYS). */ PHP_REDIS_API int From 52bae8abb951605d8dcbd4151a90b845fd05068b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 20 Mar 2019 20:23:54 -0700 Subject: [PATCH 1156/1986] Hook msgpack into available serializers output --- redis.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/redis.c b/redis.c index 65e6a9d91a..6d32dad580 100644 --- a/redis.c +++ b/redis.c @@ -795,6 +795,22 @@ PHP_MSHUTDOWN_FUNCTION(redis) return SUCCESS; } +static const char *get_available_serializers(void) { +#ifdef HAVE_REDIS_IGBINARY + #ifdef HAVE_REDIS_MSGPACK + return "php, igbinary, msgpack"; + #else + return "php, igbinary"; + #endif +#else + #ifdef HAVE_REDIS_MSGPACK + return "php, msgpack"; + #else + return "php"; + #endif +#endif +} + /** * PHP_MINFO_FUNCTION */ @@ -806,11 +822,7 @@ PHP_MINFO_FUNCTION(redis) #ifdef GIT_REVISION php_info_print_table_row(2, "Git revision", "$Id: " GIT_REVISION " $"); #endif -#ifdef HAVE_REDIS_IGBINARY - php_info_print_table_row(2, "Available serializers", "php, igbinary"); -#else - php_info_print_table_row(2, "Available serializers", "php"); -#endif + php_info_print_table_row(2, "Available serializers", get_available_serializers()); #ifdef HAVE_REDIS_LZF php_info_print_table_row(2, "Available compression", "lzf"); #endif From aff75ffdb38821109259f578f33ceb7dd243e0be Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 20 Mar 2019 20:25:10 -0700 Subject: [PATCH 1157/1986] We're only compatible with PHP7 now \o/ This update fixes merge conflicts that would have prevented us from merging the msgpack PR (#1050). --- config.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.m4 b/config.m4 index 0a410bb983..c1f793c8aa 100644 --- a/config.m4 +++ b/config.m4 @@ -38,7 +38,7 @@ dnl Check for igbinary elif test -f "$phpincludedir/ext/igbinary/igbinary.h"; then igbinary_inc_path="$phpincludedir" else - for i in php php4 php5 php6 php7; do + for i in php php7; do if test -f "$prefix/include/$i/ext/igbinary/igbinary.h"; then igbinary_inc_path="$prefix/include/$i" fi @@ -117,7 +117,7 @@ dnl Check for msgpack elif test -f "$phpincludedir/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$phpincludedir" else - for i in php php4 php5 php6 php7; do + for i in php php7; do if test -f "$prefix/include/$i/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$prefix/include/$i" fi From 823978b4e337bff0b2d3dd7b01f1cb643de4fcf7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Mar 2019 16:16:21 +0200 Subject: [PATCH 1158/1986] msgpack 2.0.3 or greater required --- config.m4 | 23 ++++++++++++++--------- library.c | 9 +-------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/config.m4 b/config.m4 index c1f793c8aa..d84ff46c07 100644 --- a/config.m4 +++ b/config.m4 @@ -133,15 +133,20 @@ dnl Check for msgpack AC_MSG_CHECKING([for redis msgpack support]) if test "$PHP_REDIS_MSGPACK" != "no"; then - AC_MSG_RESULT([enabled]) - AC_DEFINE(HAVE_REDIS_MSGPACK,1,[Whether redis msgpack serializer is enabled]) - MSGPACK_INCLUDES="-I$msgpack_inc_path" - MSGPACK_EXT_DIR="$msgpack_inc_path/ext" - ifdef([PHP_ADD_EXTENSION_DEP], - [ - PHP_ADD_EXTENSION_DEP(redis, msgpack) - ]) - PHP_ADD_INCLUDE($MSGPACK_EXT_DIR) + msgpack_version=`grep -o 'PHP_MSGPACK_VERSION "[0-9\.]\+"' $msgpack_inc_path/ext/msgpack/php_msgpack.h | awk '{print $2}' | tr -d '"'` + if expr $msgpack_version "<" "2.0.3" > /dev/null; then + AC_MSG_ERROR([msgpack 2.0.3 or greater required]) + else + AC_MSG_RESULT([enabled]) + AC_DEFINE(HAVE_REDIS_MSGPACK,1,[Whether redis msgpack serializer is enabled]) + MSGPACK_INCLUDES="-I$msgpack_inc_path" + MSGPACK_EXT_DIR="$msgpack_inc_path/ext" + ifdef([PHP_ADD_EXTENSION_DEP], + [ + PHP_ADD_EXTENSION_DEP(redis, msgpack) + ]) + PHP_ADD_INCLUDE($MSGPACK_EXT_DIR) + fi else MSGPACK_INCLUDES="" AC_MSG_RESULT([disabled]) diff --git a/library.c b/library.c index ef7f579dac..ca48100f26 100644 --- a/library.c +++ b/library.c @@ -2245,14 +2245,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, case REDIS_SERIALIZER_MSGPACK: #ifdef HAVE_REDIS_MSGPACK - /* - * Would like to be able to check to see if a string is msgpack'd (like with igbinary, below), - * but I don't believe there's an easy way to do that as there's no consistent header or - * other simple indication of packed-ness in msgpacked binary sequences, as far as I know. - */ - - php_msgpack_unserialize(z_ret, (char *)val, (size_t)val_len TSRMLS_CC); - ret = 1; + ret = !php_msgpack_unserialize(z_ret, (char *)val, (size_t)val_len TSRMLS_CC); #endif break; From 4b6be96e7b8f24e5b61f4ccfef5169e3eb936077 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Mar 2019 16:52:49 +0200 Subject: [PATCH 1159/1986] TravisCI: msgpack --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d54beeae20..5e5e545221 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,10 @@ addons: packages: clang before_install: - phpize - - pecl install igbinary && ./configure --enable-redis-igbinary --enable-redis-lzf || ./configure --enable-redis-lzf + - CFGARGS="--enable-redis-lzf" + - pecl install igbinary && CFGARGS="$CFGARGS --enable-redis-igbinary" + - pecl install msgpack && CFGARGS="$CFGARGS --enable-redis-msgpack" + - ./configure $CFGARGS install: make install before_script: - gem install redis From bfaa89a8ae9b6a82dd8f3156e150ae898ee201be Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 23 Mar 2019 08:44:32 -0700 Subject: [PATCH 1160/1986] Document msgpack serialization (#1050) --- INSTALL.markdown | 3 ++- README.markdown | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/INSTALL.markdown b/INSTALL.markdown index 64ca1ad988..88d1b360f7 100644 --- a/INSTALL.markdown +++ b/INSTALL.markdown @@ -12,11 +12,12 @@ To build this extension for the sources tree: ~~~ phpize -./configure [--enable-redis-igbinary] [--enable-redis-lzf [--with-liblzf[=DIR]]] +./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] make && make install ~~~ If you would like phpredis to serialize your data using the igbinary library, run configure with `--enable-redis-igbinary`. +If you would like to use the msgpack serializer, run configure with `--enable-redis-msgpack` (note: Requires php-msgpack >= 2.0.3) The extension also may compress data before sending it to Redis server, if you run configure with `--enable-redis-lzf`. If you want to use lzf library pre-installed into your system use `--with-liblzf` configuration option to specify the path where to search files. `make install` copies `redis.so` to an appropriate location, but you still need to enable the module in the PHP config file. To do so, either edit your php.ini or add a redis.ini file in `/etc/php5/conf.d` with the following contents: `extension=redis.so`. diff --git a/README.markdown b/README.markdown index a0aea736a2..2ae17e429c 100644 --- a/README.markdown +++ b/README.markdown @@ -313,9 +313,10 @@ _**Description**_: Set client option. ##### *Example* ~~~php -$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // don't serialize data -$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // use built-in serialize/unserialize -$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize +$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // Don't serialize data +$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // Use built-in serialize/unserialize +$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // Use igBinary serialize/unserialize +$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_MSGPACK); // Use msgpack serialize/unserialize $redis->setOption(Redis::OPT_PREFIX, 'myAppName:'); // use custom prefix on all keys @@ -342,7 +343,9 @@ Parameter value. ##### *Example* ~~~php -$redis->getOption(Redis::OPT_SERIALIZER); // return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, or Redis::SERIALIZER_IGBINARY. +// return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, +// Redis::SERIALIZER_IGBINARY, or Redis::SERIALIZER_MSGPACK +$redis->getOption(Redis::OPT_SERIALIZER); ~~~ ### ping From 60d8b679b3d948176f7dfc13c47da3d9deea926c Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Thu, 28 Mar 2019 22:02:27 -0700 Subject: [PATCH 1161/1986] Handle references in MGET (#1535) Fixes #1534 --- redis_array.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/redis_array.c b/redis_array.c index 67e117dd04..b4848d1dc6 100644 --- a/redis_array.c +++ b/redis_array.c @@ -924,6 +924,9 @@ PHP_METHOD(RedisArray, mget) unsigned int key_len; char kbuf[40], *key_lookup; + /* Handle the possibility that we're a reference */ + ZVAL_DEREF(data); + /* phpredis proper can only use string or long keys, so restrict to that here */ if (Z_TYPE_P(data) != IS_STRING && Z_TYPE_P(data) != IS_LONG) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs"); From 29f507a1e143b1998aea6d4600248ee4501782ea Mon Sep 17 00:00:00 2001 From: Shogo Date: Mon, 1 Apr 2019 01:22:23 +0900 Subject: [PATCH 1162/1986] fix typo of cluster description (#1536) Fix grammatical error about the cluster description --- cluster.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cluster.markdown b/cluster.markdown index e451070456..7246cb8071 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -51,12 +51,12 @@ Each time the a `RedisCluster` class is constructed from scratch, phpredis needs ## Timeouts Because Redis cluster is intended to provide high availability, timeouts do not work in the same way they do in normal socket communication. It's fully possible to have a timeout or even exception on a given socket (say in the case that a master node has failed), and continue to serve the request if and when a slave can be promoted as the new master. -The way RedisCluster handles user specified timeout values is that every time a command is sent to the cluster, we record the the time at the start of the request and then again every time we have to re-issue the command to a different node (either because Redis cluster responded with MOVED/ASK or because we failed to communicate with a given node). Once we detect having been in the command loop for longer than our specified timeout, an error is raised. +The way RedisCluster handles user specified timeout values is that every time a command is sent to the cluster, we record the time at the start of the request and then again every time we have to re-issue the command to a different node (either because Redis cluster responded with MOVED/ASK or because we failed to communicate with a given node). Once we detect having been in the command loop for longer than our specified timeout, an error is raised. ## Keyspace map As previously described, RedisCluster makes an initial mapping of every master (and any slaves) on construction, which it uses to determine which nodes to direct a given command. However, one of the core functionalities of Redis cluster is that this keyspace can change while the cluster is running. -Because of this, the RedisCluster class will update it's keyspace mapping whenever it receives a MOVED error when requesting data. In the case that we receive ASK redirection, it follows the Redis specification and requests the key from the ASK node, prefixed with an ASKING command. +Because of this, the RedisCluster class will update its keyspace mapping whenever it receives a MOVED error when requesting data. In the case that we receive ASK redirection, it follows the Redis specification and requests the key from the ASK node, prefixed with an ASKING command. ## Automatic slave failover / distribution By default, RedisCluster will only ever send commands to master nodes, but can be configured differently for readonly commands if requested. @@ -84,7 +84,7 @@ With the exception of commands that are directed to a specific node, each comman 1. We fail to communicate with *any* node that we are aware of, in which case a ```RedisClusterException``` is raised. 2. We have been bounced around longer than the timeout which was set on construction. -3. Redis cluster returns us a ```CLUSTERDOWN``` error, in which case a ```RedisClusterException``` is raised. +3. Redis cluster returns to us a ```CLUSTERDOWN``` error, in which case a ```RedisClusterException``` is raised. 4. We receive a valid response, in which case the data is returned to the caller. ## Transactions From 4c7643ee11670013918d9e002010ddb63b77a4cf Mon Sep 17 00:00:00 2001 From: Joyce Babu Date: Thu, 4 Apr 2019 21:45:47 +0530 Subject: [PATCH 1163/1986] Fix TypeError when using built-in constants in `setOption` In strict_type mode `Redis::setOption` requires the second argument to be of type `string`. This throws a TypeError for some options, even when using built-in constants The type of `value` argument of setOption has been changed to mixed to prevent the error. --- redis_commands.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 30324b6141..95ed8915d7 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3868,21 +3868,21 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, { long val_long; zend_long option; - char *val_str; + zval *val; + zend_string *val_str; struct timeval read_tv; - size_t val_len; int tcp_keepalive = 0; php_netstream_data_t *sock; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &option, - &val_str, &val_len) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &option, + &val) == FAILURE) { RETURN_FALSE; } switch(option) { case REDIS_OPT_SERIALIZER: - val_long = atol(val_str); + val_long = zval_get_long(val); if (val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP #ifdef HAVE_REDIS_IGBINARY || val_long == REDIS_SERIALIZER_IGBINARY @@ -3896,7 +3896,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, } break; case REDIS_OPT_COMPRESSION: - val_long = atol(val_str); + val_long = zval_get_long(val); if (val_long == REDIS_COMPRESSION_NONE #ifdef HAVE_REDIS_LZF || val_long == REDIS_COMPRESSION_LZF @@ -3911,12 +3911,15 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, zend_string_release(redis_sock->prefix); redis_sock->prefix = NULL; } - if (val_str && val_len > 0) { - redis_sock->prefix = zend_string_init(val_str, val_len, 0); + val_str = zval_get_string(val); + if (ZSTR_LEN(val_str) > 0) { + redis_sock->prefix = val_str; + } else { + zend_string_release(val_str); } RETURN_TRUE; case REDIS_OPT_READ_TIMEOUT: - redis_sock->read_timeout = atof(val_str); + redis_sock->read_timeout = zval_get_double(val); if (redis_sock->stream) { read_tv.tv_sec = (time_t)redis_sock->read_timeout; read_tv.tv_usec = (int)((redis_sock->read_timeout - @@ -3932,7 +3935,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, if (ZSTR_VAL(redis_sock->host)[0] == '/' && redis_sock->port < 1) { RETURN_FALSE; } - tcp_keepalive = atol(val_str) > 0 ? 1 : 0; + tcp_keepalive = zval_get_long(val) > 0 ? 1 : 0; if (redis_sock->tcp_keepalive == tcp_keepalive) { RETURN_TRUE; } @@ -3947,14 +3950,14 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, } RETURN_TRUE; case REDIS_OPT_SCAN: - val_long = atol(val_str); + val_long = zval_get_long(val); if (val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) { redis_sock->scan = val_long; RETURN_TRUE; } break; case REDIS_OPT_FAILOVER: - val_long = atol(val_str); + val_long = zval_get_long(val); if (val_long == REDIS_FAILOVER_NONE || val_long == REDIS_FAILOVER_ERROR || val_long == REDIS_FAILOVER_DISTRIBUTE || From 591585c4bc0bd199a8744a9299fffbbe91170f7b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Apr 2019 13:06:11 -0700 Subject: [PATCH 1164/1986] Type correctness for zval_get_long return value --- redis_commands.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 95ed8915d7..b18e632b15 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3866,8 +3866,7 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redisCluster *c) { - long val_long; - zend_long option; + zend_long val_long, option; zval *val; zend_string *val_str; struct timeval read_tv; From 5bf0c84c8fc7411e3d1dbee821c96b5a73561849 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Sat, 6 Apr 2019 15:44:54 +0200 Subject: [PATCH 1165/1986] Fix example how to create a stream with a new group --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 2ae17e429c..c2d44268ac 100644 --- a/README.markdown +++ b/README.markdown @@ -3430,8 +3430,8 @@ _**Description**_: This command is used in order to create, destroy, or manage ##### *Example* ~~~php -$obj_redis->xGroup('CREATE', 'mystream', 'mygroup'); -$obj_redis->xGroup('CREATE', 'mystream', 'mygroup2', true); /* Create stream if non-existent. */ +$obj_redis->xGroup('CREATE', 'mystream', 'mygroup', 0); +$obj_redis->xGroup('CREATE', 'mystream', 'mygroup2', 0, true); /* Create stream if non-existent. */ $obj_redis->xGroup('DESTROY', 'mystream', 'mygroup'); ~~~ From d7450b2f59700e4662624317438c456a0dd36165 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 16 Apr 2019 07:57:40 -0700 Subject: [PATCH 1166/1986] Add support for STREAM to the type command --- common.h | 1 + library.c | 2 + redis.c | 1 + tests/RedisTest.php | 94 +++++++++++++++++++++++++-------------------- 4 files changed, 56 insertions(+), 42 deletions(-) diff --git a/common.h b/common.h index 2b55efb946..8954e93491 100644 --- a/common.h +++ b/common.h @@ -33,6 +33,7 @@ #define REDIS_LIST 3 #define REDIS_ZSET 4 #define REDIS_HASH 5 +#define REDIS_STREAM 6 #ifdef PHP_WIN32 #define PHP_REDIS_API __declspec(dllexport) diff --git a/library.c b/library.c index ca48100f26..49e27a08aa 100644 --- a/library.c +++ b/library.c @@ -807,6 +807,8 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * l = REDIS_ZSET; } else if (strncmp(response, "+hash", 5) == 0){ l = REDIS_HASH; + } else if (strncmp(response, "+stream", 7) == 0) { + l = REDIS_STREAM; } else { l = REDIS_NOT_FOUND; } diff --git a/redis.c b/redis.c index 6d32dad580..fa0c2b6d85 100644 --- a/redis.c +++ b/redis.c @@ -649,6 +649,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STREAM"), REDIS_STREAM TSRMLS_CC); /* Cluster doesn't support pipelining at this time */ if(!is_cluster) { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index a865ffd7e1..9f92c78043 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -696,47 +696,57 @@ public function testUnlink() { public function testType() { - // 0 => none, (key didn't exist) - // 1=> string, - // 2 => set, - // 3 => list, - // 4 => zset, - // 5 => hash - - // 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(1 === $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')); - - //None - $this->assertEquals(Redis::REDIS_NOT_FOUND, $this->redis->type('keyNotExists')); + // 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(1 === $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() { @@ -2447,7 +2457,7 @@ public function testZPop() { $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'); From f63b87f173edd854e7e5ee190901f91560e176ca Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 11 May 2019 12:46:02 +0300 Subject: [PATCH 1167/1986] Use enum for storing redis_sock status --- common.h | 55 ++++++++++++++++++++++++++++--------------------------- library.c | 16 ++++++++-------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/common.h b/common.h index 8954e93491..c0e9182cd0 100644 --- a/common.h +++ b/common.h @@ -20,9 +20,11 @@ #define NULL ((void *) 0) #endif -#define REDIS_SOCK_STATUS_FAILED 0 -#define REDIS_SOCK_STATUS_DISCONNECTED 1 -#define REDIS_SOCK_STATUS_CONNECTED 2 +typedef enum { + REDIS_SOCK_STATUS_FAILED = -1, + REDIS_SOCK_STATUS_DISCONNECTED, + REDIS_SOCK_STATUS_CONNECTED +} redis_sock_status; #define _NL "\r\n" @@ -238,37 +240,36 @@ typedef struct fold_item { /* {{{ struct RedisSock */ typedef struct { - php_stream *stream; - zend_string *host; - short port; - zend_string *auth; - double timeout; - double read_timeout; - long retry_interval; - int failed; - int status; - int persistent; - int watching; - zend_string *persistent_id; + php_stream *stream; + zend_string *host; + short port; + zend_string *auth; + double timeout; + double read_timeout; + long retry_interval; + redis_sock_status status; + int persistent; + int watching; + zend_string *persistent_id; - int serializer; - int compression; - long dbNumber; + int serializer; + int compression; + long dbNumber; - zend_string *prefix; + zend_string *prefix; - short mode; - fold_item *head; - fold_item *current; + short mode; + fold_item *head; + fold_item *current; - zend_string *pipeline_cmd; + zend_string *pipeline_cmd; - zend_string *err; + zend_string *err; - int scan; + int scan; - int readonly; - int tcp_keepalive; + int readonly; + int tcp_keepalive; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index 49e27a08aa..ec469c3afb 100644 --- a/library.c +++ b/library.c @@ -1810,17 +1810,17 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC) { - int res = -1; - - switch (redis_sock->status) { + if (redis_sock) { + switch (redis_sock->status) { + case REDIS_SOCK_STATUS_FAILED: + return FAILURE; case REDIS_SOCK_STATUS_DISCONNECTED: return redis_sock_connect(redis_sock TSRMLS_CC); - case REDIS_SOCK_STATUS_CONNECTED: - res = 0; - break; + default: + return SUCCESS; + } } - - return res; + return FAILURE; } /** From 068ce978e1e0f8c9fd3665da15917acb0d4155ee Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 11 May 2019 13:04:13 +0300 Subject: [PATCH 1168/1986] Fix support for STREAM to the RedisCluster::type command --- cluster_library.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cluster_library.c b/cluster_library.c index cc1ef16ebb..364f7d4b69 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1808,6 +1808,8 @@ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster CLUSTER_RETURN_LONG(c, REDIS_HASH); } else if (strncmp(c->line_reply, "zset", 4) == 0) { CLUSTER_RETURN_LONG(c, REDIS_ZSET); + } else if (strncmp(c->line_reply, "+stream", 7) == 0) { + CLUSTER_RETURN_LONG(c, REDIS_STREAM); } else { CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND); } From 8a45d18c383647e573d5fd2b3617eb402a6bb6a5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 11 May 2019 13:10:20 +0300 Subject: [PATCH 1169/1986] Fix copy-paste typo --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 364f7d4b69..7dd104f506 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1808,7 +1808,7 @@ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster CLUSTER_RETURN_LONG(c, REDIS_HASH); } else if (strncmp(c->line_reply, "zset", 4) == 0) { CLUSTER_RETURN_LONG(c, REDIS_ZSET); - } else if (strncmp(c->line_reply, "+stream", 7) == 0) { + } else if (strncmp(c->line_reply, "stream", 6) == 0) { CLUSTER_RETURN_LONG(c, REDIS_STREAM); } else { CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND); From 98bd2886c391d01607e1c4d1d06faeebbd4ed2c3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 12 May 2019 15:30:47 +0300 Subject: [PATCH 1170/1986] JSON serializer --- common.h | 13 ++++++++----- library.c | 20 +++++++++++++++++--- redis.c | 8 ++++---- redis_commands.c | 4 +++- tests/RedisTest.php | 19 +++++++++++++++++-- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/common.h b/common.h index c0e9182cd0..2208cc48c7 100644 --- a/common.h +++ b/common.h @@ -85,10 +85,13 @@ typedef enum _PUBSUB_TYPE { #define REDIS_FAILOVER_DISTRIBUTE 2 #define REDIS_FAILOVER_DISTRIBUTE_SLAVES 3 /* serializers */ -#define REDIS_SERIALIZER_NONE 0 -#define REDIS_SERIALIZER_PHP 1 -#define REDIS_SERIALIZER_IGBINARY 2 -#define REDIS_SERIALIZER_MSGPACK 3 +typedef enum { + REDIS_SERIALIZER_NONE, + REDIS_SERIALIZER_PHP, + REDIS_SERIALIZER_IGBINARY, + REDIS_SERIALIZER_MSGPACK, + REDIS_SERIALIZER_JSON +} redis_serializer; /* compression */ #define REDIS_COMPRESSION_NONE 0 #define REDIS_COMPRESSION_LZF 1 @@ -252,7 +255,7 @@ typedef struct { int watching; zend_string *persistent_id; - int serializer; + redis_serializer serializer; int compression; long dbNumber; diff --git a/library.c b/library.c index ec469c3afb..87aa5697cb 100644 --- a/library.c +++ b/library.c @@ -25,6 +25,7 @@ #include "php_redis.h" #include "library.h" #include "redis_commands.h" +#include #include #define UNSERIALIZE_NONE 0 @@ -2165,7 +2166,6 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len switch(redis_sock->serializer) { case REDIS_SERIALIZER_NONE: switch(Z_TYPE_P(z)) { - case IS_STRING: *val = Z_STRVAL_P(z); *val_len = Z_STRLEN_P(z); @@ -2205,8 +2205,8 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len case REDIS_SERIALIZER_MSGPACK: #ifdef HAVE_REDIS_MSGPACK php_msgpack_serialize(&sstr, z TSRMLS_CC); - *val = estrndup(sstr.s->val, sstr.s->len); - *val_len = sstr.s->len; + *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); + *val_len = ZSTR_LEN(sstr.s); smart_str_free(&sstr); return 1; @@ -2221,6 +2221,13 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len } #endif break; + case REDIS_SERIALIZER_JSON: + php_json_encode(&sstr, z, PHP_JSON_OBJECT_AS_ARRAY); + *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); + *val_len = ZSTR_LEN(sstr.s); + smart_str_free(&sstr); + return 1; + EMPTY_SWITCH_DEFAULT_CASE() } return 0; @@ -2235,6 +2242,9 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, int ret = 0; switch(redis_sock->serializer) { + case REDIS_SERIALIZER_NONE: + /* Nothing to do */ + break; case REDIS_SERIALIZER_PHP: PHP_VAR_UNSERIALIZE_INIT(var_hash); @@ -2280,6 +2290,10 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, z_ret TSRMLS_CC); #endif break; + case REDIS_SERIALIZER_JSON: + ret = !php_json_decode(z_ret, (char *)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); + break; + EMPTY_SWITCH_DEFAULT_CASE() } return ret; diff --git a/redis.c b/redis.c index fa0c2b6d85..ef54b4a1e5 100644 --- a/redis.c +++ b/redis.c @@ -673,6 +673,10 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) #ifdef HAVE_REDIS_IGBINARY zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); #endif +#ifdef HAVE_REDIS_MSGPACK + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK TSRMLS_CC); +#endif + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_JSON"), REDIS_SERIALIZER_JSON TSRMLS_CC); /* compression */ zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_NONE"), REDIS_COMPRESSION_NONE TSRMLS_CC); @@ -694,10 +698,6 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES TSRMLS_CC); } -#ifdef HAVE_REDIS_MSGPACK - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK TSRMLS_CC); -#endif - zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5 TSRMLS_CC); zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); } diff --git a/redis_commands.c b/redis_commands.c index b18e632b15..829bfb046e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3882,7 +3882,9 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, switch(option) { case REDIS_OPT_SERIALIZER: val_long = zval_get_long(val); - if (val_long == REDIS_SERIALIZER_NONE || val_long == REDIS_SERIALIZER_PHP + if (val_long == REDIS_SERIALIZER_NONE + || val_long == REDIS_SERIALIZER_PHP + || val_long == REDIS_SERIALIZER_JSON #ifdef HAVE_REDIS_IGBINARY || val_long == REDIS_SERIALIZER_IGBINARY #endif diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 9f92c78043..903c381c80 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4244,6 +4244,16 @@ public function testSerializerMsgPack() { } } + 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'); @@ -4452,8 +4462,13 @@ private function checkSerializer($mode) { $this->redis->set('x', [new stdClass, new stdClass]); $x = $this->redis->get('x'); $this->assertTrue(is_array($x)); - $this->assertTrue(is_object($x[0]) && get_class($x[0]) === 'stdClass'); - $this->assertTrue(is_object($x[1]) && get_class($x[1]) === 'stdClass'); + 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_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE); // set ok From 96c571391e69b0a4f9c40bed393e4e8161ead71a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 12 May 2019 12:19:21 -0700 Subject: [PATCH 1171/1986] Make JSON deserialization work in php 7.0.x In PHP 7.0.x the json_decode function returned void so we need to test the error_code global to determine if deserialization failed. --- library.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library.c b/library.c index 87aa5697cb..df675dc996 100644 --- a/library.c +++ b/library.c @@ -2291,7 +2291,13 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, #endif break; case REDIS_SERIALIZER_JSON: +#if PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION < 1 + JSON_G(error_code) = PHP_JSON_ERROR_NONE; + php_json_decode(z_ret, (char*)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); + ret = JSON_G(error_code) == PHP_JSON_ERROR_NONE; +#else ret = !php_json_decode(z_ret, (char *)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); +#endif break; EMPTY_SWITCH_DEFAULT_CASE() } From be3089c6cb8344697556a00fdcebdc0619df58e0 Mon Sep 17 00:00:00 2001 From: Tim Bond Date: Wed, 24 Apr 2019 15:41:24 -0700 Subject: [PATCH 1172/1986] Add syntax highlighting to cluster readme --- cluster.markdown | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/cluster.markdown b/cluster.markdown index 7246cb8071..2c6c1db464 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -8,7 +8,7 @@ Redis introduces cluster support as of version 3.0.0, and to communicate with a To maintain consistency with the RedisArray class, one can create and connect to a cluster either by passing it one or more 'seed' nodes, or by defining these in redis.ini as a 'named' cluster. #### Declaring a cluster with an array of seeds -
+~~~php
 // Create a cluster setting two nodes as seeds
 $obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003'));
 
@@ -21,25 +21,24 @@ $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5,
 
 // Connect with cluster using password.
 $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, "password");
-
-
+~~~ #### Loading a cluster configuration by name In order to load a named array, one must first define the seed nodes in redis.ini. The following lines would define the cluster 'mycluster', and be loaded automatically by phpredis. -
+~~~ini
 # In redis.ini
 redis.clusters.seeds = "mycluster[]=localhost:7000&test[]=localhost:7001"
 redis.clusters.timeout = "mycluster=5"
 redis.clusters.read_timeout = "mycluster=10"
 redis.clusters.auth = "mycluster=password"
-
+~~~ Then, this cluster can be loaded by doing the following -
+~~~php
 $obj_cluster = new RedisCluster('mycluster');
-
+~~~ ## Connection process @@ -61,7 +60,7 @@ Because of this, the RedisCluster class will update its keyspace mapping wheneve ## Automatic slave failover / distribution By default, RedisCluster will only ever send commands to master nodes, but can be configured differently for readonly commands if requested. -
+~~~php
 // The default option, only send commands to master nodes
 $obj_cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_NONE);
 
@@ -77,24 +76,24 @@ $obj_cluster->setOption(
 $obj_cluster->setOption(
     RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE_SLAVES
 );
-
+~~~ ## Main command loop With the exception of commands that are directed to a specific node, each command executed via RedisCluster is processed through a command loop, where we make the request, handle any MOVED or ASK redirection, and repeat if necessary. This continues until one of the following conditions is met: -1. We fail to communicate with *any* node that we are aware of, in which case a ```RedisClusterException``` is raised. +1. We fail to communicate with *any* node that we are aware of, in which case a ~~~RedisClusterException~~~ is raised. 2. We have been bounced around longer than the timeout which was set on construction. -3. Redis cluster returns to us a ```CLUSTERDOWN``` error, in which case a ```RedisClusterException``` is raised. +3. Redis cluster returns to us a ~~~CLUSTERDOWN~~~ error, in which case a ~~~RedisClusterException~~~ is raised. 4. We receive a valid response, in which case the data is returned to the caller. ## Transactions The RedisCluster class fully supports MULTI ... EXEC transactions, including commands such as MGET and MSET which operate on multiple keys. There are considerations that must be taken into account here however. -When you call ```RedisCluster->multi()```, the cluster is put into a MULTI state, but the MULTI command is not delivered to any nodes until a key is requested on that node. In addition, calls to EXEC will always return an array (even in the event that a transaction to a given node failed), as the commands can be going to any number of nodes depending on what is called. +When you call ~~~RedisCluster->multi()~~~, the cluster is put into a MULTI state, but the MULTI command is not delivered to any nodes until a key is requested on that node. In addition, calls to EXEC will always return an array (even in the event that a transaction to a given node failed), as the commands can be going to any number of nodes depending on what is called. Consider the following example: -
+~~~php
 // Cluster is put into MULTI state locally
 $obj_cluster->multi();
 
@@ -109,7 +108,7 @@ $obj_cluster->get("myotherkey");
 // This will always return an array, even in the event of a failed transaction
 // on one of the nodes, in which case that element will be FALSE
 print_r($obj_cluster->exec());
-
+~~~ ## Pipelining The RedisCluster class does not support pipelining as there is no way to detect whether the keys still live where our map indicates that they do and would therefore be inherently unsafe. It would be possible to implement this support as an option if there is demand for such a feature. @@ -122,11 +121,11 @@ For all of these multiple key commands (with the exception of MGET and MSET), th ### MGET and MSET RedisCluster has specialized processing for MGET and MSET which allows you to send any number of keys (hashing to whichever slots) without having to consider where they live. The way this works, is that the RedisCluster class will split the command as it iterates through keys, delivering a subset of commands per each key's slot. -
+~~~php
 // This will be delivered in two commands.  First for all of the {hash1} keys, 
 // and then to grab 'otherkey'
 $obj_cluster->mget(Array("{hash1}key1","{hash1}key2","{hash1}key3","otherkey"));
-
+~~~ This operation can also be done in MULTI mode transparently. @@ -141,7 +140,6 @@ $obj_cluster->echo("mykey","Hello World!"); foreach ($obj_cluster->_masters() as $arr_master) { $obj_cluster->echo($arr_master, "Hello: " . implode(':', $arr_master)); } - ~~~ In the case of all commands which need to be directed at a node, the calling convention is identical to the Redis call, except that they require an additional (first) argument in order to deliver the command. Following is a list of each of these commands: @@ -168,7 +166,7 @@ You can use the cluster functionality of phpredis to store PHP session informati To do this, you must configure your `session.save_handler` and `session.save_path` INI variables to give phpredis enough information to communicate with the cluster. -~~~ +~~~ini session.save_handler = rediscluster session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1&auth=password" ~~~ From 5cb30fb2a65abc9d31ff58af6da0fbb6be4ec153 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 12 May 2019 18:49:33 -0700 Subject: [PATCH 1173/1986] Adds OPT_REPLY_LITERAL for rawCommand and EVAL Adds an option to process the actual strings in simple string replies as opposed to translating them to `true`. This only applies to `rawCommand` and `eval` because as far as I know know vanilla Redis command attaches any information besides `OK` to simple string replies. Addresses #1550 --- cluster_library.c | 6 ++++++ cluster_library.h | 4 +++- common.h | 2 ++ library.c | 9 +++++++++ library.h | 1 + redis.c | 7 ++++--- redis_cluster.c | 8 ++++---- redis_commands.c | 6 ++++++ tests/RedisClusterTest.php | 20 ++++++++++++++++++++ tests/RedisTest.php | 20 ++++++++++++++++++++ 10 files changed, 75 insertions(+), 8 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 7dd104f506..dd1e0bbcb6 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2098,6 +2098,12 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 0, ctx); } +PHP_REDIS_API void cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, c->flags->reply_literal, ctx); +} + PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { diff --git a/cluster_library.h b/cluster_library.h index 4522330864..3b3b4a7d8d 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -431,10 +431,12 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -/* Generic/Variant handler for stuff like EVAL */ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); + PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); diff --git a/common.h b/common.h index 2208cc48c7..6b75a81e89 100644 --- a/common.h +++ b/common.h @@ -78,6 +78,7 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_FAILOVER 5 #define REDIS_OPT_TCP_KEEPALIVE 6 #define REDIS_OPT_COMPRESSION 7 +#define REDIS_OPT_REPLY_LITERAL 8 /* cluster options */ #define REDIS_FAILOVER_NONE 0 @@ -272,6 +273,7 @@ typedef struct { int scan; int readonly; + int reply_literal; int tcp_keepalive; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index 0f3d9f580e..9c383135e0 100644 --- a/library.c +++ b/library.c @@ -1684,6 +1684,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->readonly = 0; redis_sock->tcp_keepalive = 0; + redis_sock->reply_literal = 0; return redis_sock; } @@ -2558,6 +2559,14 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return 0; } +PHP_REDIS_API int +redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_sock->reply_literal, z_tab, ctx); +} + PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) diff --git a/library.h b/library.h index 3028711c1e..16204295f6 100644 --- a/library.h +++ b/library.h @@ -116,6 +116,7 @@ PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_strings, zval *z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); diff --git a/redis.c b/redis.c index ef54b4a1e5..dc315e365a 100644 --- a/redis.c +++ b/redis.c @@ -666,6 +666,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_TCP_KEEPALIVE"), REDIS_OPT_TCP_KEEPALIVE TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_REPLY_LITERAL"), REDIS_OPT_REPLY_LITERAL); /* serializer */ zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE TSRMLS_CC); @@ -3007,12 +3008,12 @@ PHP_METHOD(Redis, pubsub) { /* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */ PHP_METHOD(Redis, eval) { - REDIS_PROCESS_KW_CMD("EVAL", redis_eval_cmd, redis_read_variant_reply); + REDIS_PROCESS_KW_CMD("EVAL", redis_eval_cmd, redis_read_raw_variant_reply); } /* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */ PHP_METHOD(Redis, evalsha) { - REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_variant_reply); + REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_raw_variant_reply); } /* {{{ proto status Redis::script('flush') @@ -3384,7 +3385,7 @@ PHP_METHOD(Redis, rawcommand) { /* Execute our command */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if (IS_ATOMIC(redis_sock)) { - redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL); + redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL); } REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } diff --git a/redis_cluster.c b/redis_cluster.c index c234a2062b..346ee88f4d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1989,13 +1989,13 @@ PHP_METHOD(RedisCluster, punsubscribe) { /* {{{ proto mixed RedisCluster::eval(string script, [array args, int numkeys) */ PHP_METHOD(RedisCluster, eval) { - CLUSTER_PROCESS_KW_CMD("EVAL", redis_eval_cmd, cluster_variant_resp, 0); + CLUSTER_PROCESS_KW_CMD("EVAL", redis_eval_cmd, cluster_variant_raw_resp, 0); } /* }}} */ /* {{{ proto mixed RedisCluster::evalsha(string sha, [array args, int numkeys]) */ PHP_METHOD(RedisCluster, evalsha) { - CLUSTER_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, cluster_variant_resp, 0); + CLUSTER_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, cluster_variant_raw_resp, 0); } /* }}} */ @@ -3161,10 +3161,10 @@ PHP_METHOD(RedisCluster, rawcommand) { /* Process variant response */ if (CLUSTER_IS_ATOMIC(c)) { - cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_raw_resp, ctx); } efree(cmd); diff --git a/redis_commands.c b/redis_commands.c index 829bfb046e..77c7366ed6 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3856,6 +3856,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_LONG(redis_sock->tcp_keepalive); case REDIS_OPT_SCAN: RETURN_LONG(redis_sock->scan); + case REDIS_OPT_REPLY_LITERAL: + RETURN_LONG(redis_sock->reply_literal); case REDIS_OPT_FAILOVER: RETURN_LONG(c->failover); default: @@ -3896,6 +3898,10 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_TRUE; } break; + case REDIS_OPT_REPLY_LITERAL: + val_long = zval_get_long(val); + redis_sock->reply_literal = val_long != 0; + RETURN_TRUE; case REDIS_OPT_COMPRESSION: val_long = zval_get_long(val); if (val_long == REDIS_COMPRESSION_NONE diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 02e78209f3..ead4ab9cd4 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -609,6 +609,26 @@ protected function rawCommandArray($key, $args) { return call_user_func_array([$this->redis, 'rawCommand'], $args); } + /* Test that rawCommand and EVAL can be configured to return simple string values */ + public function testReplyLiteral() { + $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false); + $this->assertTrue($this->redis->rawCommand('foo', 'set', 'foo', 'bar')); + $this->assertTrue($this->redis->eval("return redis.call('set', KEYS[1], 'bar')", ['foo'], 1)); + + $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1); + $this->assertEquals([true, true], $rv); + + $this->redis->setOption(Redis::OPT_REPLY_LITERAL, true); + $this->assertEquals('OK', $this->redis->rawCommand('foo', 'set', 'foo', 'bar')); + $this->assertEquals('OK', $this->redis->eval("return redis.call('set', KEYS[1], 'bar')", ['foo'], 1)); + + $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_LITERAL, false); + } + public function testSession() { @ini_set('session.save_handler', 'rediscluster'); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 903c381c80..903a97bf71 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4825,6 +4825,26 @@ public function testPrefix() { } + public function testReplyLiteral() { + $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false); + $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([true, true], $rv); + + $this->redis->setOption(Redis::OPT_REPLY_LITERAL, true); + $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_LITERAL, false); + } + public function testReconnectSelect() { $key = 'reconnect-select'; $value = 'Has been set!'; From 34d6403dd5579a421bcb9172f94a71b0d0dc87df Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 14 May 2019 21:37:36 +0300 Subject: [PATCH 1174/1986] Issue #1523 --- library.c | 4 ++-- redis.c | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index 9c383135e0..78f8715088 100644 --- a/library.c +++ b/library.c @@ -2344,9 +2344,9 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, char *errmsg = NULL; if (redis_sock->port < 0) { - spprintf(&errmsg, 0, "read error on connection to %S", redis_sock->host); + spprintf(&errmsg, 0, "read error on connection to %s", ZSTR_VAL(redis_sock->host)); } else { - spprintf(&errmsg, 0, "read error on connection to %S:%d", redis_sock->host, redis_sock->port); + spprintf(&errmsg, 0, "read error on connection to %s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); } // Close our socket redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); diff --git a/redis.c b/redis.c index dc315e365a..05a283daea 100644 --- a/redis.c +++ b/redis.c @@ -612,6 +612,16 @@ redis_sock_get(zval *id TSRMLS_DC, int no_throw) } if (redis_sock_server_open(redis_sock TSRMLS_CC) < 0) { + if (!no_throw) { + char *errmsg = NULL; + if (redis_sock->port < 0) { + spprintf(&errmsg, 0, "Redis server %s went away", ZSTR_VAL(redis_sock->host)); + } else { + spprintf(&errmsg, 0, "Redis server %s:%d went away", ZSTR_VAL(redis_sock->host), redis_sock->port); + } + REDIS_THROW_EXCEPTION(errmsg, 0); + efree(errmsg); + } return NULL; } From 4f352059433215072e856b004e1edd69920381d6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 15 May 2019 14:46:29 -0700 Subject: [PATCH 1175/1986] Fix for type conversion warning. It appears that `tv_sec` and `tv_usec` are declared as long on most systems, but I'm not totally sure about Windows. --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 78f8715088..994c2dd1c8 100644 --- a/library.c +++ b/library.c @@ -1745,7 +1745,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) } gettimeofday(&tv, NULL); - persistent_id = strpprintf(0, "phpredis_%d%d", tv.tv_sec, tv.tv_usec); + persistent_id = strpprintf(0, "phpredis_%ld%ld", tv.tv_sec, tv.tv_usec); } else { if (redis_sock->persistent_id) { persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id)); From 60223762ada628c26cb82386bed281e4ec097f4f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 15 May 2019 12:38:11 -0700 Subject: [PATCH 1176/1986] Allow persistent_id to be passed as NULL with strict_types enabled. Right now it is not possible to pass NULL as the persistent_id if strict types are enabled. This change allows the argument to be NULL. Addresses issue #1551 --- redis.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/redis.c b/redis.c index 05a283daea..1a11997c66 100644 --- a/redis.c +++ b/redis.c @@ -906,7 +906,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { zval *object; - char *host = NULL, *persistent_id = ""; + char *host = NULL, *persistent_id = NULL; zend_long port = -1, retry_interval = 0; size_t host_len, persistent_id_len; double timeout = 0.0, read_timeout = 0.0; @@ -919,13 +919,16 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #endif if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|ldsld", &object, redis_ce, &host, + "Os|lds!ld", &object, redis_ce, &host, &host_len, &port, &timeout, &persistent_id, &persistent_id_len, &retry_interval, &read_timeout) == FAILURE) { return FAILURE; - } else if (!persistent) { + } + + /* Disregard persistent_id if we're not opening a persistent connection */ + if (!persistent) { persistent_id = NULL; } From 418428faf3924abceeaa6a39699692d2202fe988 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 24 May 2019 12:20:17 +0300 Subject: [PATCH 1177/1986] Allow to specify server address as schema://host --- library.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/library.c b/library.c index 78f8715088..ffbbc8a543 100644 --- a/library.c +++ b/library.c @@ -1695,19 +1695,23 @@ redis_sock_create(char *host, int host_len, unsigned short port, PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) { struct timeval tv, read_tv, *tv_ptr = NULL; - zend_string *persistent_id = NULL; - char host[1024]; - const char *fmtstr = "%s:%d"; + zend_string *persistent_id = NULL, *estr = NULL; + char host[1024], *pos, *address, *schema = NULL; + const char *fmtstr = "%s://%s:%d"; int host_len, usocket = 0, err = 0, tcp_flag = 1; ConnectionPool *p = NULL; - zend_string *estr = NULL; if (redis_sock->stream != NULL) { redis_sock_disconnect(redis_sock, 0 TSRMLS_CC); } - if (ZSTR_VAL(redis_sock->host)[0] == '/' && redis_sock->port < 1) { - host_len = snprintf(host, sizeof(host), "unix://%s", ZSTR_VAL(redis_sock->host)); + address = ZSTR_VAL(redis_sock->host); + if ((pos = strstr(address, "://")) != NULL) { + schema = estrndup(address, pos - address); + address = pos + sizeof("://") - 1; + } + if (redis_sock->port < 1) { + host_len = snprintf(host, sizeof(host), "unix://%s", address); usocket = 1; } else { if(redis_sock->port == 0) @@ -1716,11 +1720,12 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) #ifdef HAVE_IPV6 /* If we've got IPv6 and find a colon in our address, convert to proper * IPv6 [host]:port format */ - if (strchr(ZSTR_VAL(redis_sock->host), ':') != NULL) { - fmtstr = "[%s]:%d"; + if (strchr(address, ':') != NULL) { + fmtstr = "%s://[%s]:%d"; } #endif - host_len = snprintf(host, sizeof(host), fmtstr, ZSTR_VAL(redis_sock->host), redis_sock->port); + host_len = snprintf(host, sizeof(host), fmtstr, schema ? schema : "tcp", address, redis_sock->port); + if (schema) efree(schema); } if (redis_sock->persistent) { From f8f3ede6f8a688144d589638a732dbbc13e7f6eb Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 24 May 2019 19:44:05 +0300 Subject: [PATCH 1178/1986] Add notice about using schema to documentation --- README.markdown | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index c2d44268ac..a4c494fa00 100644 --- a/README.markdown +++ b/README.markdown @@ -182,7 +182,7 @@ _**Description**_: Connects to a Redis instance. ##### *Parameters* -*host*: string. can be a host, or the path to a unix domain socket +*host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) *reserved*: should be NULL if retry_interval is specified @@ -198,9 +198,12 @@ _**Description**_: Connects to a Redis instance. ~~~php $redis->connect('127.0.0.1', 6379); $redis->connect('127.0.0.1'); // port 6379 by default +$redis->connect('tls://127.0.0.1', 6379); // enable transport level security. +$redis->connect('tls://127.0.0.1'); // enable transport level security, port 6379 by default. $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout. $redis->connect('/tmp/redis.sock'); // unix domain socket. $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts. +$redis->connect('unix://redis.sock'); // relative path to unix domain socket requires version 5.0.0 or higher. ~~~ ### pconnect, popen @@ -221,7 +224,7 @@ persistent equivalents. ##### *Parameters* -*host*: string. can be a host, or the path to a unix domain socket +*host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema *port*: int, optional *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) *persistent_id*: string. identity for the requested persistent connection @@ -237,9 +240,12 @@ persistent equivalents. ~~~php $redis->pconnect('127.0.0.1', 6379); $redis->pconnect('127.0.0.1'); // port 6379 by default - same connection like before. +$redis->pconnect('tls://127.0.0.1', 6379); // enable transport level security. +$redis->pconnect('tls://127.0.0.1'); // enable transport level security, port 6379 by default. $redis->pconnect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout and would be another connection than the two before. $redis->pconnect('127.0.0.1', 6379, 2.5, 'x'); // x is sent as persistent_id and would be another connection than the three before. $redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another connection than the four before. +$redis->pconnect('unix://redis.sock'); // relative path to unix domain socket requires version 5.0.0 or higher. ~~~ ### auth From 90b780396aadf9a8f9d1043a950758f7bd15eb29 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 2 Jun 2019 10:59:22 -0700 Subject: [PATCH 1179/1986] Clarify that SUNION can be variadic or take a single array Addresses #163 --- README.markdown | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.markdown b/README.markdown index a4c494fa00..3dbff72697 100644 --- a/README.markdown +++ b/README.markdown @@ -2499,6 +2499,8 @@ _**Description**_: Performs the union between N sets and returns it. ##### *Return value* *Array of strings*: The union of all these sets. +**Note:** `sUnion` can also take a single array with keys (see example below). + ##### *Example* ~~~php $redis->delete('s0', 's1', 's2'); @@ -2510,7 +2512,12 @@ $redis->sAdd('s1', '1'); $redis->sAdd('s2', '3'); $redis->sAdd('s2', '4'); +/* Get the union with variadic arguments */ var_dump($redis->sUnion('s0', 's1', 's2')); + +/* Pass a single array */ +var_dump($redis->sUnion(['s0', 's1', 's2']); + ~~~ Return value: all elements that are either in s0 or in s1 or in s2. ~~~ From 07b1e7738e462f5bf8a34061156a93c0d00400ce Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 2 Jun 2019 11:39:52 -0700 Subject: [PATCH 1180/1986] Fix SF longitude/latitude. Fixes #1548 --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 3dbff72697..b1693b3699 100644 --- a/README.markdown +++ b/README.markdown @@ -3037,7 +3037,7 @@ $redis->del("myplaces"); /* Since the key will be new, $result will be 2 */ $result = $redis->geoAdd( "myplaces", - 37.773, -122.431, "San Francisco", + -122.431, 37.773, "San Francisco", -157.858, 21.315, "Honolulu" ); ~~~ From 19f3efcfe6e1a32451a543fa186a8f38d250d941 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Sun, 2 Jun 2019 17:44:26 -0700 Subject: [PATCH 1181/1986] Issue.1555 zrange withscores arg (#1565) Allows ZRANGE to be called either with `true` or `['withscores' => true]` so it's consistent with `ZRANGEBYSCORE` but also backward compatible. Fixes #1555 --- redis_cluster.c | 4 +++- redis_commands.c | 42 +++++++++++++++++++++++++++++------------- tests/RedisTest.php | 13 +++++++++++++ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 346ee88f4d..b952cc4b9d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2440,7 +2440,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* We at least need the key or [host,port] argument */ - if (argc<1) { + if (argc < 1) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Command requires at least an argument to direct to a node"); RETURN_FALSE; @@ -3069,6 +3069,8 @@ PHP_METHOD(RedisCluster, xtrim) { CLUSTER_PROCESS_CMD(xtrim, cluster_long_resp, 0); } + + /* {{{ proto string RedisCluster::echo(string key, string msg) * proto string RedisCluster::echo(array host_port, string msg) */ PHP_METHOD(RedisCluster, echo) { diff --git a/redis_commands.c b/redis_commands.c index 77c7366ed6..7466ca0a7e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -491,6 +491,12 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, return cmdstr.len; } +/* ZRANGEBYSCORE/ZREVRANGEBYSCORE */ +#define IS_WITHSCORES_ARG(s, l) \ + (l == sizeof("withscores") - 1 && !strncasecmp(s, "withscores", l)) +#define IS_LIMIT_ARG(s, l) \ + (l == sizeof("limit") - 1 && !strncasecmp(s,"limit", l)) + /* ZRANGE/ZREVRANGE */ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, @@ -499,33 +505,43 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; size_t key_len; zend_long start, end; - zend_bool ws = 0; + zend_string *zkey; + zval *z_ws = NULL, *z_ele; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|b", &key, &key_len, - &start, &end, &ws) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|z", &key, &key_len, + &start, &end, &z_ws) == FAILURE) { return FAILURE; } - if (ws) { + // Clear withscores arg + *withscores = 0; + + /* Accept ['withscores' => true], or the legacy `true` value */ + if (z_ws) { + if (Z_TYPE_P(z_ws) == IS_ARRAY) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_ws), zkey, z_ele) { + ZVAL_DEREF(z_ele); + if (IS_WITHSCORES_ARG(ZSTR_VAL(zkey), ZSTR_LEN(zkey))) { + *withscores = zval_is_true(z_ele); + break; + } + } ZEND_HASH_FOREACH_END(); + } else if (Z_TYPE_P(z_ws) == IS_TRUE) { + *withscores = Z_TYPE_P(z_ws) == IS_TRUE; + } + } + + if (*withscores) { *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdds", key, key_len, start, end, "WITHSCORES", sizeof("WITHSCORES") - 1); } else { *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdd", key, key_len, start, end); } - // Push out WITHSCORES option - *withscores = ws; - return SUCCESS; } -/* ZRANGEBYSCORE/ZREVRANGEBYSCORE */ -#define IS_WITHSCORES_ARG(s, l) \ - (l == sizeof("withscores") - 1 && !strncasecmp(s, "withscores", l)) -#define IS_LIMIT_ARG(s, l) \ - (l == sizeof("limit") - 1 && !strncasecmp(s,"limit", l)) - int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, void **ctx) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 903a97bf71..328a3e1293 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2350,6 +2350,19 @@ public function testZX() { $this->assertTrue(0 === $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, -1, true)); + $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", "lt")) { From 6e4941706835d32bfce6b2843f791ef9720b7b88 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 29 May 2019 09:42:29 -0700 Subject: [PATCH 1182/1986] Allow PING to take an optional argument. Addresses #1563 --- redis.c | 2 +- redis_cluster.c | 66 +++++++++++++++++++++++++++++++++++--- redis_commands.c | 20 ++++++++++++ redis_commands.h | 3 ++ tests/RedisClusterTest.php | 9 +++++- tests/RedisTest.php | 20 +++++++----- 6 files changed, 106 insertions(+), 14 deletions(-) diff --git a/redis.c b/redis.c index 1a11997c66..65002191fe 100644 --- a/redis.c +++ b/redis.c @@ -1093,7 +1093,7 @@ PHP_METHOD(Redis, get) */ PHP_METHOD(Redis, ping) { - REDIS_PROCESS_KW_CMD("PING", redis_empty_cmd, redis_ping_response); + REDIS_PROCESS_KW_CMD("PING", redis_opt_str_cmd, redis_read_variant_reply); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index b952cc4b9d..0342096c1e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -3002,11 +3002,69 @@ PHP_METHOD(RedisCluster, randomkey) { } /* }}} */ -/* {{{ proto bool RedisCluster::ping(string key) - * proto bool RedisCluster::ping(array host_port) */ +/* {{{ proto bool RedisCluster::ping(string key| string msg) + * proto bool RedisCluster::ping(array host_port| string msg) */ PHP_METHOD(RedisCluster, ping) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PING", - TYPE_LINE, cluster_ping_resp); + redisCluster *c = GET_CONTEXT(); + REDIS_REPLY_TYPE rtype; + void *ctx = NULL; + zval *z_node; + char *cmd, *arg = NULL; + int cmdlen; + size_t arglen; + short slot; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s!", &z_node, &arg, + &arglen) == FAILURE) + { + RETURN_FALSE; + } + + /* Treat this as a readonly command */ + c->readonly = CLUSTER_IS_ATOMIC(c); + + /* Grab slot either by key or host/port */ + slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC); + if (slot < 0) { + RETURN_FALSE; + } + + /* Construct our command */ + if (arg != NULL) { + cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "PING", "s", arg, arglen); + } else { + cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "PING", ""); + } + + /* Send it off */ + rtype = CLUSTER_IS_ATOMIC(c) && arg != NULL ? TYPE_BULK : TYPE_LINE; + if (cluster_send_slot(c, slot, cmd, cmdlen, rtype TSRMLS_CC) < 0) { + CLUSTER_THROW_EXCEPTION("Unable to send commnad at the specificed node", 0); + efree(cmd); + RETURN_FALSE; + } + + /* We're done with our command */ + efree(cmd); + + /* Process response */ + if (CLUSTER_IS_ATOMIC(c)) { + if (arg != NULL) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + /* If we're atomic and didn't send an argument then we have already + * processed the reply (which must have been successful. */ + RETURN_TRUE; + } + } else { + if (arg != NULL) { + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx); + } else { + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); + } + + RETURN_ZVAL(getThis(), 1, 0); + } } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 7466ca0a7e..b74092ed6f 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -168,6 +168,26 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args) return cmd; } +/* Command that takes one optional string */ +int redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *arg = NULL; + size_t arglen; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &arg, &arglen) == FAILURE) { + return FAILURE; + } + + if (arg != NULL) { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", arg, arglen); + } else { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, ""); + } + + return SUCCESS; +} + /* Generic command where we just take a string and do nothing to it*/ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 9a9c777d69..71bdb5f7fc 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -33,6 +33,9 @@ smart_string *redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args); int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index ead4ab9cd4..9806ed5432 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -95,9 +95,16 @@ protected function newInstance() { * at a specific node */ public function testPing() { - for ($i = 0; $i < 100; $i++) { + for ($i = 0; $i < 20; $i++) { $this->assertTrue($this->redis->ping("key:$i")); + $this->assertEquals('BEEP', $this->redis->ping("key:$i", 'BEEP')); } + + /* Make sure both variations work in MULTI mode */ + $this->redis->multi(); + $this->redis->ping('{ping-test}'); + $this->redis->ping('{ping-test}','BEEP'); + $this->assertEquals([true, 'BEEP'], $this->redis->exec()); } public function testRandomKey() { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 328a3e1293..8694b4722a 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -89,14 +89,18 @@ public function testMinimumVersion() $this->assertTrue(version_compare($this->version, "2.4.0", "ge")); } - public function testPing() - { - $this->assertEquals('+PONG', $this->redis->ping()); + public function testPing() { + /* Reply literal off */ + $this->assertTrue($this->redis->ping()); + $this->assertTrue($this->redis->ping(NULL)); + $this->assertEquals('BEEP', $this->redis->ping('BEEP')); - $count = 1000; - while($count --) { - $this->assertEquals('+PONG', $this->redis->ping()); - } + /* 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() { @@ -6011,7 +6015,7 @@ public function testMultipleConnect() { for($i = 0; $i < 5; $i++) { $this->redis->connect($host, $port); - $this->assertEquals($this->redis->ping(), "+PONG"); + $this->assertEquals(true, $this->redis->ping()); } } From 0c17bd27a03f499f96fad90fb2fec069c3408156 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 3 Jun 2019 11:46:03 -0700 Subject: [PATCH 1183/1986] Make the XREADGROUP optional COUNT and BLOCK arguments nullable. Change the way we accept COUNT and BLOCK such that a user can pass NULL to mean "no value". This is technically a breaking change, since previously the value `-1` was used for "no value". Fixes #1560 --- redis_commands.c | 19 +++++++++++++------ tests/RedisTest.php | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index b74092ed6f..006d3dc1f6 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3466,15 +3466,22 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *group, *consumer; size_t grouplen, consumerlen; int scount, argc; - zend_long count = -1, block = -1; + zend_long count, block; + zend_bool no_count = 1, no_block = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa|ll", &group, + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa|l!l!", &group, &grouplen, &consumer, &consumerlen, &z_streams, - &count, &block) == FAILURE) + &count, &no_count, &block, &no_block) == FAILURE) { return FAILURE; } + /* Negative COUNT or BLOCK is illegal so abort immediately */ + if ((!no_count && count < 0) || (!no_block && block < 0)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values for COUNT or BLOCK are illegal."); + return FAILURE; + } + /* Redis requires at least one stream */ kt = Z_ARRVAL_P(z_streams); if ((scount = zend_hash_num_elements(kt)) < 1) { @@ -3482,7 +3489,7 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Calculate argc and start constructing commands */ - argc = 4 + (2 * scount) + (2 * (count > -1)) + (2 * (block > -1)); + argc = 4 + (2 * scount) + (2 * !no_count) + (2 * !no_block); REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XREADGROUP"); /* Group and consumer */ @@ -3491,13 +3498,13 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); /* Append COUNT if we have it */ - if (count > -1) { + if (!no_count) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); redis_cmd_append_sstr_long(&cmdstr, count); } /* Append BLOCK argument if we have it */ - if (block > -1) { + if (!no_block) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BLOCK"); redis_cmd_append_sstr_long(&cmdstr, block); } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 8694b4722a..f1f960cf0d 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5687,12 +5687,29 @@ public function testXReadGroup() { } } + /* Test COUNT option with NULL (should be ignored) */ + $this->addStreamsAndGroups($streams, 3, $groups, NULL); + $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, NULL); + 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', $qnew, -1, 100); + $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, 100); $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', $qnew, NULL, NULL); + $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', $qnew, NULL, -1)); } public function testXPending() { From fa02ddd51d45c2ba6a2bb2d330f54950ddefe2b6 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Thu, 6 Jun 2019 17:59:06 +0200 Subject: [PATCH 1184/1986] Add maxlen and approximate parameter to example of xAdd --- README.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index b1693b3699..40e52ddcbc 100644 --- a/README.markdown +++ b/README.markdown @@ -3347,7 +3347,7 @@ $obj_redis->xAck('stream', 'group1', ['1530063064286-0', '1530063064286-1']); ##### *Prototype* ~~~php -$obj_redis->xAdd($str_key, $str_id, $arr_message); +$obj_redis->xAdd($str_key, $str_id, $arr_message[, $i_maxlen, $boo_approximate]); ~~~ _**Description**_: Add a message to a stream @@ -3358,6 +3358,8 @@ _**Description**_: Add a message to a stream ##### *Example* ~~~php $obj_redis->xAdd('mystream', "*", ['field' => 'value']); +$obj_redis->xAdd('mystream', "*", ['field' => 'value'], 1000); // set max length of stream to 1000 +$obj_redis->xAdd('mystream', "*", ['field' => 'value'], 1000, true); // set max length of stream to ~1000 ~~~ ### xClaim From 2a4dbddb5fd9abb260420f5b979cb7507035945e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 7 Jun 2019 10:11:03 +0300 Subject: [PATCH 1185/1986] TravisCI: PHP 7.4 --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5e5e545221..9783c1d3c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,12 +5,15 @@ php: - 7.1 - 7.2 - 7.3 + - 7.4snapshot - nightly env: CC=gcc matrix: allow_failures: - php: 7.3 env: CC=clang + - php: 7.4snapshot + env: CC=clang - php: nightly include: - php: 7.0 @@ -21,6 +24,8 @@ matrix: env: CC=clang - php: 7.3 env: CC=clang + - php: 7.4snapshot + env: CC=clang addons: apt: packages: clang From a81b4f2d645fa975bf725a12607cd4c9df543362 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 7 Jun 2019 11:05:31 +0300 Subject: [PATCH 1186/1986] Swap methods/aliases For unknown reasons some methods named differently to Redis commands (for example setTimeout instead of expire). This change aligns naming and allows easly remove aliases in future if we will decide to deprecate them. --- php_redis.h | 176 ++++++++++++++++++++++++++-------------------------- redis.c | 152 ++++++++++++++++++++++----------------------- 2 files changed, 164 insertions(+), 164 deletions(-) diff --git a/php_redis.h b/php_redis.h index d6240ec10a..0be45c0a2a 100644 --- a/php_redis.h +++ b/php_redis.h @@ -29,120 +29,120 @@ PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); -PHP_METHOD(Redis, connect); -PHP_METHOD(Redis, pconnect); +PHP_METHOD(Redis, append); +PHP_METHOD(Redis, auth); +PHP_METHOD(Redis, bgSave); +PHP_METHOD(Redis, bgrewriteaof); +PHP_METHOD(Redis, bitcount); +PHP_METHOD(Redis, bitop); +PHP_METHOD(Redis, bitpos); +PHP_METHOD(Redis, blPop); +PHP_METHOD(Redis, brPop); +PHP_METHOD(Redis, bzPopMax); +PHP_METHOD(Redis, bzPopMin); PHP_METHOD(Redis, close); -PHP_METHOD(Redis, ping); +PHP_METHOD(Redis, connect); +PHP_METHOD(Redis, dbSize); +PHP_METHOD(Redis, decr); +PHP_METHOD(Redis, decrBy); +PHP_METHOD(Redis, del); PHP_METHOD(Redis, echo); +PHP_METHOD(Redis, exists); +PHP_METHOD(Redis, expire); +PHP_METHOD(Redis, expireAt); +PHP_METHOD(Redis, flushAll); +PHP_METHOD(Redis, flushDB); PHP_METHOD(Redis, get); -PHP_METHOD(Redis, set); -PHP_METHOD(Redis, setex); -PHP_METHOD(Redis, psetex); -PHP_METHOD(Redis, setnx); +PHP_METHOD(Redis, getBit); +PHP_METHOD(Redis, getRange); PHP_METHOD(Redis, getSet); -PHP_METHOD(Redis, randomKey); -PHP_METHOD(Redis, renameKey); -PHP_METHOD(Redis, renameNx); -PHP_METHOD(Redis, getMultiple); -PHP_METHOD(Redis, exists); -PHP_METHOD(Redis, delete); -PHP_METHOD(Redis, unlink); PHP_METHOD(Redis, incr); PHP_METHOD(Redis, incrBy); PHP_METHOD(Redis, incrByFloat); -PHP_METHOD(Redis, decr); -PHP_METHOD(Redis, decrBy); -PHP_METHOD(Redis, type); -PHP_METHOD(Redis, append); -PHP_METHOD(Redis, getRange); -PHP_METHOD(Redis, setRange); -PHP_METHOD(Redis, getBit); -PHP_METHOD(Redis, setBit); -PHP_METHOD(Redis, strlen); -PHP_METHOD(Redis, getKeys); -PHP_METHOD(Redis, sort); -PHP_METHOD(Redis, sortAsc); -PHP_METHOD(Redis, sortAscAlpha); -PHP_METHOD(Redis, sortDesc); -PHP_METHOD(Redis, sortDescAlpha); +PHP_METHOD(Redis, info); +PHP_METHOD(Redis, keys); +PHP_METHOD(Redis, lInsert); +PHP_METHOD(Redis, lLen); +PHP_METHOD(Redis, lPop); PHP_METHOD(Redis, lPush); PHP_METHOD(Redis, lPushx); +PHP_METHOD(Redis, lSet); +PHP_METHOD(Redis, lastSave); +PHP_METHOD(Redis, lindex); +PHP_METHOD(Redis, lrange); +PHP_METHOD(Redis, lrem); +PHP_METHOD(Redis, ltrim); +PHP_METHOD(Redis, mget); +PHP_METHOD(Redis, move); +PHP_METHOD(Redis, object); +PHP_METHOD(Redis, pconnect); +PHP_METHOD(Redis, persist); +PHP_METHOD(Redis, pexpire); +PHP_METHOD(Redis, pexpireAt); +PHP_METHOD(Redis, ping); +PHP_METHOD(Redis, psetex); +PHP_METHOD(Redis, pttl); +PHP_METHOD(Redis, rPop); PHP_METHOD(Redis, rPush); PHP_METHOD(Redis, rPushx); -PHP_METHOD(Redis, lPop); -PHP_METHOD(Redis, rPop); -PHP_METHOD(Redis, blPop); -PHP_METHOD(Redis, brPop); -PHP_METHOD(Redis, lSize); -PHP_METHOD(Redis, lRemove); -PHP_METHOD(Redis, listTrim); -PHP_METHOD(Redis, lGet); -PHP_METHOD(Redis, lGetRange); -PHP_METHOD(Redis, lSet); -PHP_METHOD(Redis, lInsert); +PHP_METHOD(Redis, randomKey); +PHP_METHOD(Redis, rename); +PHP_METHOD(Redis, renameNx); PHP_METHOD(Redis, sAdd); PHP_METHOD(Redis, sAddArray); -PHP_METHOD(Redis, sSize); -PHP_METHOD(Redis, sRemove); +PHP_METHOD(Redis, sDiff); +PHP_METHOD(Redis, sDiffStore); +PHP_METHOD(Redis, sInter); +PHP_METHOD(Redis, sInterStore); +PHP_METHOD(Redis, sMembers); PHP_METHOD(Redis, sMove); PHP_METHOD(Redis, sPop); PHP_METHOD(Redis, sRandMember); -PHP_METHOD(Redis, sContains); -PHP_METHOD(Redis, sMembers); -PHP_METHOD(Redis, sInter); -PHP_METHOD(Redis, sInterStore); PHP_METHOD(Redis, sUnion); PHP_METHOD(Redis, sUnionStore); -PHP_METHOD(Redis, sDiff); -PHP_METHOD(Redis, sDiffStore); -PHP_METHOD(Redis, setTimeout); -PHP_METHOD(Redis, pexpire); PHP_METHOD(Redis, save); -PHP_METHOD(Redis, bgSave); -PHP_METHOD(Redis, lastSave); -PHP_METHOD(Redis, flushDB); -PHP_METHOD(Redis, flushAll); -PHP_METHOD(Redis, dbSize); -PHP_METHOD(Redis, auth); -PHP_METHOD(Redis, ttl); -PHP_METHOD(Redis, pttl); -PHP_METHOD(Redis, persist); -PHP_METHOD(Redis, info); +PHP_METHOD(Redis, scard); PHP_METHOD(Redis, select); +PHP_METHOD(Redis, set); +PHP_METHOD(Redis, setBit); +PHP_METHOD(Redis, setRange); +PHP_METHOD(Redis, setex); +PHP_METHOD(Redis, setnx); +PHP_METHOD(Redis, sismember); +PHP_METHOD(Redis, slaveof); +PHP_METHOD(Redis, sort); +PHP_METHOD(Redis, sortAsc); +PHP_METHOD(Redis, sortAscAlpha); +PHP_METHOD(Redis, sortDesc); +PHP_METHOD(Redis, sortDescAlpha); +PHP_METHOD(Redis, srem); +PHP_METHOD(Redis, strlen); PHP_METHOD(Redis, swapdb); -PHP_METHOD(Redis, move); +PHP_METHOD(Redis, ttl); +PHP_METHOD(Redis, type); +PHP_METHOD(Redis, unlink); PHP_METHOD(Redis, zAdd); -PHP_METHOD(Redis, zDelete); -PHP_METHOD(Redis, zRange); -PHP_METHOD(Redis, zRevRange); -PHP_METHOD(Redis, zRangeByScore); -PHP_METHOD(Redis, zRevRangeByScore); -PHP_METHOD(Redis, zRangeByLex); -PHP_METHOD(Redis, zRevRangeByLex); -PHP_METHOD(Redis, zRemRangeByLex); -PHP_METHOD(Redis, zLexCount); -PHP_METHOD(Redis, zCount); -PHP_METHOD(Redis, zDeleteRangeByScore); -PHP_METHOD(Redis, zDeleteRangeByRank); PHP_METHOD(Redis, zCard); -PHP_METHOD(Redis, zScore); -PHP_METHOD(Redis, zRank); -PHP_METHOD(Redis, zRevRank); +PHP_METHOD(Redis, zCount); PHP_METHOD(Redis, zIncrBy); -PHP_METHOD(Redis, zInter); -PHP_METHOD(Redis, zUnion); +PHP_METHOD(Redis, zLexCount); PHP_METHOD(Redis, zPopMax); PHP_METHOD(Redis, zPopMin); -PHP_METHOD(Redis, bzPopMax); -PHP_METHOD(Redis, bzPopMin); -PHP_METHOD(Redis, expireAt); -PHP_METHOD(Redis, pexpireAt); -PHP_METHOD(Redis, bgrewriteaof); -PHP_METHOD(Redis, slaveof); -PHP_METHOD(Redis, object); -PHP_METHOD(Redis, bitop); -PHP_METHOD(Redis, bitcount); -PHP_METHOD(Redis, bitpos); +PHP_METHOD(Redis, zRange); +PHP_METHOD(Redis, zRangeByLex); +PHP_METHOD(Redis, zRangeByScore); +PHP_METHOD(Redis, zRank); +PHP_METHOD(Redis, zRem); +PHP_METHOD(Redis, zRemRangeByLex); +PHP_METHOD(Redis, zRemRangeByRank); +PHP_METHOD(Redis, zRemRangeByScore); +PHP_METHOD(Redis, zRevRange); +PHP_METHOD(Redis, zRevRangeByLex); +PHP_METHOD(Redis, zRevRangeByScore); +PHP_METHOD(Redis, zRevRank); +PHP_METHOD(Redis, zScore); +PHP_METHOD(Redis, zinterstore); +PHP_METHOD(Redis, zunionstore); PHP_METHOD(Redis, eval); PHP_METHOD(Redis, evalsha); diff --git a/redis.c b/redis.c index 65002191fe..c76a3c96b8 100644 --- a/redis.c +++ b/redis.c @@ -261,7 +261,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, debug, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, decr, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, decrBy, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, delete, arginfo_del, ZEND_ACC_PUBLIC) + PHP_ME(Redis, del, arginfo_del, ZEND_ACC_PUBLIC) PHP_ME(Redis, discard, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, dump, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, echo, arginfo_echo, ZEND_ACC_PUBLIC) @@ -269,6 +269,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, exists, arginfo_exists, ZEND_ACC_PUBLIC) + PHP_ME(Redis, expire, arginfo_expire, ZEND_ACC_PUBLIC) PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC) PHP_ME(Redis, flushAll, arginfo_flush, ZEND_ACC_PUBLIC) PHP_ME(Redis, flushDB, arginfo_flush, ZEND_ACC_PUBLIC) @@ -285,10 +286,8 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, getBit, arginfo_key_offset, ZEND_ACC_PUBLIC) PHP_ME(Redis, getDBNum, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, getHost, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getKeys, arginfo_keys, ZEND_ACC_PUBLIC) PHP_ME(Redis, getLastError, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, getMode, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) PHP_ME(Redis, getOption, arginfo_getoption, ZEND_ACC_PUBLIC) PHP_ME(Redis, getPersistentID, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, getPort, arginfo_void, ZEND_ACC_PUBLIC) @@ -316,17 +315,19 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, incrByFloat, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, info, arginfo_info, ZEND_ACC_PUBLIC) PHP_ME(Redis, isConnected, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lGet, arginfo_lindex, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, keys, arginfo_keys, ZEND_ACC_PUBLIC) PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lLen, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, lPop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, lPush, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, lPushx, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) PHP_ME(Redis, lSet, arginfo_lset, ZEND_ACC_PUBLIC) - PHP_ME(Redis, lSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, lastSave, arginfo_void, ZEND_ACC_PUBLIC) - PHP_ME(Redis, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lindex, arginfo_lindex, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, lrem, arginfo_lrem, ZEND_ACC_PUBLIC) + PHP_ME(Redis, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC) + PHP_ME(Redis, mget, arginfo_mget, ZEND_ACC_PUBLIC) PHP_ME(Redis, migrate, arginfo_migrate, ZEND_ACC_PUBLIC) PHP_ME(Redis, move, arginfo_move, ZEND_ACC_PUBLIC) PHP_ME(Redis, mset, arginfo_pairs, ZEND_ACC_PUBLIC) @@ -353,14 +354,13 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, rPushx, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, randomKey, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC) - PHP_ME(Redis, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_ME(Redis, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC) PHP_ME(Redis, renameNx, arginfo_key_newkey, ZEND_ACC_PUBLIC) PHP_ME(Redis, restore, arginfo_restore, ZEND_ACC_PUBLIC) PHP_ME(Redis, role, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC) PHP_ME(Redis, sAdd, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, sAddArray, arginfo_sadd_array, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sContains, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, sDiff, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sDiffStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sInter, arginfo_nkeys, ZEND_ACC_PUBLIC) @@ -369,21 +369,20 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC) PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sRemove, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sSize, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, sUnion, arginfo_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, sUnionStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC) PHP_ME(Redis, save, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) + PHP_ME(Redis, scard, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, script, arginfo_script, ZEND_ACC_PUBLIC) PHP_ME(Redis, select, arginfo_select, ZEND_ACC_PUBLIC) PHP_ME(Redis, set, arginfo_set, ZEND_ACC_PUBLIC) PHP_ME(Redis, setBit, arginfo_key_offset_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, setOption, arginfo_setoption, ZEND_ACC_PUBLIC) PHP_ME(Redis, setRange, arginfo_key_offset_value, ZEND_ACC_PUBLIC) - PHP_ME(Redis, setTimeout, arginfo_expire, ZEND_ACC_PUBLIC) PHP_ME(Redis, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, setnx, arginfo_key_value, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sismember, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_ME(Redis, slaveof, arginfo_slaveof, ZEND_ACC_PUBLIC) PHP_ME(Redis, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) PHP_ME(Redis, sort, arginfo_sort, ZEND_ACC_PUBLIC) @@ -391,6 +390,7 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC) PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, srem, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC) @@ -419,55 +419,55 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zAdd, arginfo_zadd, ZEND_ACC_PUBLIC) PHP_ME(Redis, zCard, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDeleteRangeByRank, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_ME(Redis, zIncrBy, arginfo_zincrby, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zLexCount, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zPopMax, arginfo_key, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zPopMin, arginfo_key, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRank, arginfo_key_member, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRem, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRemRangeByLex, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRemRangeByRank, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zRevRank, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zPopMax, arginfo_key, ZEND_ACC_PUBLIC) - PHP_ME(Redis, zPopMin, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, del, delete, arginfo_del, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, delete, del, arginfo_del, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, expire, setTimeout, arginfo_expire, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, keys, getKeys, arginfo_keys, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lLen, lSize, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lindex, lGet, arginfo_lindex, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lrange, lGetRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lrem, lRemove, arginfo_lrem, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, ltrim, listTrim, arginfo_ltrim, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, mget, getMultiple, arginfo_mget, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, getKeys, keys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lGet, lindex, arginfo_lindex, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lGetRange, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lRemove, lrem, arginfo_lrem, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, lSize, lLen, arginfo_key, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, listTrim, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, rename, renameKey, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, renameKey, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, sContains, sismember, arginfo_key_value, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, scard, sSize, arginfo_key, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, sRemove, srem, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, sSize, scard, arginfo_key, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sismember, sContains, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, srem, sRemove, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, setTimeout, expire, arginfo_expire, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRem, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemRangeByRank, zDeleteRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemove, zDelete, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemoveRangeByScore, zDeleteRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zDelete, zRem, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zDeleteRangeByRank, zRemRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zDeleteRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zInter, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemove, zRem, arginfo_key_members, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zRemoveRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zReverseRange, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zinterstore, zInter, arginfo_zstore, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zunionstore, zUnion, arginfo_zstore, ZEND_ACC_PUBLIC) + PHP_MALIAS(Redis, zUnion, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -1062,9 +1062,9 @@ PHP_METHOD(Redis, echo) } /* }}} */ -/* {{{ proto string Redis::renameKey(string key_src, string key_dst) +/* {{{ proto string Redis::rename(string key_src, string key_dst) */ -PHP_METHOD(Redis, renameKey) +PHP_METHOD(Redis, rename) { REDIS_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, redis_boolean_response); } @@ -1133,9 +1133,9 @@ PHP_METHOD(Redis, decrBy){ } /* }}} */ -/* {{{ proto array Redis::getMultiple(array keys) +/* {{{ proto array Redis::mget(array keys) */ -PHP_METHOD(Redis, getMultiple) +PHP_METHOD(Redis, mget) { zval *object, *z_args, *z_ele; HashTable *hash; @@ -1191,9 +1191,9 @@ PHP_METHOD(Redis, exists) } /* }}} */ -/* {{{ proto boolean Redis::delete(string key) +/* {{{ proto boolean Redis::del(string key) */ -PHP_METHOD(Redis, delete) +PHP_METHOD(Redis, del) { REDIS_PROCESS_CMD(del, redis_long_response); } @@ -1247,9 +1247,9 @@ PHP_METHOD(Redis, unwatch) } /* }}} */ -/* {{{ proto array Redis::getKeys(string pattern) +/* {{{ proto array Redis::keys(string pattern) */ -PHP_METHOD(Redis, getKeys) +PHP_METHOD(Redis, keys) { REDIS_PROCESS_KW_CMD("KEYS", redis_key_cmd, redis_mbulk_reply_raw); } @@ -1371,37 +1371,37 @@ PHP_METHOD(Redis, brPop) /* }}} */ -/* {{{ proto int Redis::lSize(string key) */ -PHP_METHOD(Redis, lSize) +/* {{{ proto int Redis::lLen(string key) */ +PHP_METHOD(Redis, lLen) { REDIS_PROCESS_KW_CMD("LLEN", redis_key_cmd, redis_long_response); } /* }}} */ -/* {{{ proto boolean Redis::lRemove(string list, string value, int count = 0) */ -PHP_METHOD(Redis, lRemove) +/* {{{ proto boolean Redis::lrem(string list, string value, int count = 0) */ +PHP_METHOD(Redis, lrem) { REDIS_PROCESS_CMD(lrem, redis_long_response); } /* }}} */ -/* {{{ proto boolean Redis::listTrim(string key , int start , int end) */ -PHP_METHOD(Redis, listTrim) +/* {{{ proto boolean Redis::ltrim(string key , int start , int end) */ +PHP_METHOD(Redis, ltrim) { REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, redis_boolean_response); } /* }}} */ -/* {{{ proto string Redis::lGet(string key , int index) */ -PHP_METHOD(Redis, lGet) +/* {{{ proto string Redis::lindex(string key , int index) */ +PHP_METHOD(Redis, lindex) { REDIS_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, redis_string_response); } /* }}} */ -/* {{{ proto array Redis::lGetRange(string key, int start , int end) */ -PHP_METHOD(Redis, lGetRange) +/* {{{ proto array Redis::lrange(string key, int start , int end) */ +PHP_METHOD(Redis, lrange) { REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd, redis_sock_read_multibulk_reply); @@ -1420,15 +1420,15 @@ PHP_METHOD(Redis, sAddArray) { REDIS_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, redis_long_response); } /* }}} */ -/* {{{ proto int Redis::sSize(string key) */ -PHP_METHOD(Redis, sSize) +/* {{{ proto int Redis::scard(string key) */ +PHP_METHOD(Redis, scard) { REDIS_PROCESS_KW_CMD("SCARD", redis_key_cmd, redis_long_response); } /* }}} */ -/* {{{ proto boolean Redis::sRemove(string set, string value) */ -PHP_METHOD(Redis, sRemove) +/* {{{ proto boolean Redis::srem(string set, string value) */ +PHP_METHOD(Redis, srem) { REDIS_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, redis_long_response); } @@ -1491,8 +1491,8 @@ PHP_METHOD(Redis, sRandMember) } /* }}} */ -/* {{{ proto boolean Redis::sContains(string set, string value) */ -PHP_METHOD(Redis, sContains) +/* {{{ proto boolean Redis::sismember(string set, string value) */ +PHP_METHOD(Redis, sismember) { REDIS_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, redis_1_response); } @@ -1697,8 +1697,8 @@ PHP_METHOD(Redis, sortDescAlpha) } /* }}} */ -/* {{{ proto array Redis::setTimeout(string key, int timeout) */ -PHP_METHOD(Redis, setTimeout) { +/* {{{ proto array Redis::expire(string key, int timeout) */ +PHP_METHOD(Redis, expire) { REDIS_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, redis_1_response); } /* }}} */ @@ -2049,23 +2049,23 @@ PHP_METHOD(Redis, zRemRangeByLex) { } /* }}} */ -/* {{{ proto long Redis::zDelete(string key, string member) */ -PHP_METHOD(Redis, zDelete) +/* {{{ proto long Redis::zRem(string key, string member) */ +PHP_METHOD(Redis, zRem) { REDIS_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, redis_long_response); } /* }}} */ -/* {{{ proto long Redis::zDeleteRangeByScore(string k, string s, string e) */ -PHP_METHOD(Redis, zDeleteRangeByScore) +/* {{{ proto long Redis::zRemRangeByScore(string k, string s, string e) */ +PHP_METHOD(Redis, zRemRangeByScore) { REDIS_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd, redis_long_response); } /* }}} */ -/* {{{ proto long Redis::zDeleteRangeByRank(string key, long start, long end) */ -PHP_METHOD(Redis, zDeleteRangeByRank) +/* {{{ proto long Redis::zRemRangeByRank(string key, long start, long end) */ +PHP_METHOD(Redis, zRemRangeByRank) { REDIS_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd, redis_long_response); @@ -2113,13 +2113,13 @@ PHP_METHOD(Redis, zIncrBy) } /* }}} */ -/* zInter */ -PHP_METHOD(Redis, zInter) { +/* zinterstore */ +PHP_METHOD(Redis, zinterstore) { REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response); } -/* zUnion */ -PHP_METHOD(Redis, zUnion) { +/* zunionstore */ +PHP_METHOD(Redis, zunionstore) { REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, redis_long_response); } From 27b1dfa2f6ec664171672e5be7bd9532a70fce72 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 10 Jun 2019 17:34:10 +0300 Subject: [PATCH 1187/1986] Update README --- README.markdown | 171 +++++++++++++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 69 deletions(-) diff --git a/README.markdown b/README.markdown index 40e52ddcbc..a22339f82c 100644 --- a/README.markdown +++ b/README.markdown @@ -789,14 +789,15 @@ $redis->set('key2', 'val2'); $redis->set('key3', 'val3'); $redis->set('key4', 'val4'); -$redis->delete('key1', 'key2'); /* return 2 */ -$redis->delete(['key3', 'key4']); /* return 2 */ +$redis->del('key1', 'key2'); /* return 2 */ +$redis->del(['key3', 'key4']); /* return 2 */ /* If using Redis >= 4.0.0 you can call unlink */ $redis->unlink('key1', 'key2'); $redis->unlink(['key1', 'key2']); ~~~ +**Note:** `delete` is an alias for `del` and will be removed in future versions of phpredis. ### exists ----- @@ -912,6 +913,8 @@ $redis->mGet(['key1', 'key2', 'key3']); /* ['value1', 'value2', 'value3']; $redis->mGet(['key0', 'key1', 'key5']); /* [`FALSE`, 'value1', `FALSE`]; ~~~ +**Note:** `getMultiple` is an alias for `mGet` and will be removed in future versions of phpredis. + ### getSet ----- _**Description**_: Sets a value and returns the previous entry at that key. @@ -983,6 +986,8 @@ $redis->get('y'); // → 42 $redis->get('x'); // → `FALSE` ~~~ +**Note:** `renameKey` is an alias for `rename` and will be removed in future versions of phpredis. + ### renameNx ----- _**Description**_: Same as rename, but will not replace a key if the destination already exists. This is the same behaviour as setNx. @@ -1001,11 +1006,13 @@ _**Description**_: Sets an expiration date (a timeout) on an item. pexpire requi ##### *Example* ~~~php $redis->set('x', '42'); -$redis->setTimeout('x', 3); // x will disappear in 3 seconds. +$redis->expire('x', 3); // x will disappear in 3 seconds. sleep(5); // wait 5 seconds $redis->get('x'); // will return `FALSE`, as 'x' has expired. ~~~ +**Note:** `setTimeout` is an alias for `expire` and will be removed in future versions of phpredis. + ### expireAt, pexpireAt ----- _**Description**_: Sets an expiration date (a timestamp) on an item. pexpireAt requires a timestamp in milliseconds. @@ -1042,6 +1049,8 @@ $allKeys = $redis->keys('*'); // all keys will match this. $keyWithUserPrefix = $redis->keys('user*'); ~~~ +**Note:** `getKeys` is an alias for `keys` and will be removed in future versions of phpredis. + ### scan ----- _**Description**_: Scan the keyspace for keys @@ -1286,7 +1295,7 @@ An array of values, or a number corresponding to the number of elements stored i ##### *Example* ~~~php -$redis->delete('s'); +$redis->del('s'); $redis->sAdd('s', 5); $redis->sAdd('s', 4); $redis->sAdd('s', 2); @@ -1442,7 +1451,7 @@ _**Description**_: Adds a value to the hash stored at key. *LONG* `1` if value didn't exist and was added successfully, `0` if the value was already present and was replaced, `FALSE` if there was an error. ##### *Example* ~~~php -$redis->delete('h') +$redis->del('h') $redis->hSet('h', 'key1', 'hello'); /* 1, 'key1' => 'hello' in the hash at "h" */ $redis->hGet('h', 'key1'); /* returns "hello" */ @@ -1459,7 +1468,7 @@ _**Description**_: Adds a value to the hash stored at key only if this field isn ##### *Example* ~~~php -$redis->delete('h') +$redis->del('h') $redis->hSetNx('h', 'key1', 'hello'); /* TRUE, 'key1' => 'hello' in the hash at "h" */ $redis->hSetNx('h', 'key1', 'world'); /* FALSE, 'key1' => 'hello' in the hash at "h". No change since the field wasn't replaced. */ ~~~ @@ -1487,7 +1496,7 @@ _**Description**_: Returns the length of a hash, in number of items *LONG* the number of items in a hash, `FALSE` if the key doesn't exist or isn't a hash. ##### *Example* ~~~php -$redis->delete('h') +$redis->del('h') $redis->hSet('h', 'key1', 'hello'); $redis->hSet('h', 'key2', 'plop'); $redis->hLen('h'); /* returns 2 */ @@ -1518,7 +1527,7 @@ An array of elements, the keys of the hash. This works like PHP's array_keys(). ##### *Example* ~~~php -$redis->delete('h'); +$redis->del('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); $redis->hSet('h', 'c', 'z'); @@ -1553,7 +1562,7 @@ An array of elements, the values of the hash. This works like PHP's array_values ##### *Example* ~~~php -$redis->delete('h'); +$redis->del('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); $redis->hSet('h', 'c', 'z'); @@ -1588,7 +1597,7 @@ An array of elements, the contents of the hash. ##### *Example* ~~~php -$redis->delete('h'); +$redis->del('h'); $redis->hSet('h', 'a', 'x'); $redis->hSet('h', 'b', 'y'); $redis->hSet('h', 'c', 'z'); @@ -1637,7 +1646,7 @@ _**Description**_: Increments the value of a member from a hash by a given amoun *LONG* the new value ##### *Examples* ~~~php -$redis->delete('h'); +$redis->del('h'); $redis->hIncrBy('h', 'x', 2); /* returns 2: h[x] = 2 now. */ $redis->hIncrBy('h', 'x', 1); /* h[x] ← 2 + 1. Returns 3 */ ~~~ @@ -1653,7 +1662,7 @@ _**Description**_: Increments the value of a hash member by the provided float v *FLOAT* the new value ##### *Examples* ~~~php -$redis->delete('h'); +$redis->del('h'); $redis->hIncrByFloat('h','x', 1.5); /* returns 1.5: h[x] = 1.5 now */ $redis->hIncrByFloat('h', 'x', 1.5); /* returns 3.0: h[x] = 3.0 now */ $redis->hIncrByFloat('h', 'x', -3.0); /* returns 0.0: h[x] = 0.0 now */ @@ -1669,7 +1678,7 @@ _**Description**_: Fills in a whole hash. Non-string values are converted to str *BOOL* ##### *Examples* ~~~php -$redis->delete('user:1'); +$redis->del('user:1'); $redis->hMSet('user:1', ['name' => 'Joe', 'salary' => 2000]); $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. ~~~ @@ -1684,7 +1693,7 @@ _**Description**_: Retrieve the values associated to the specified fields in the *Array* An array of elements, the values of the specified fields in the hash, with the hash keys as array keys. ##### *Examples* ~~~php -$redis->delete('h'); +$redis->del('h'); $redis->hSet('h', 'field1', 'value1'); $redis->hSet('h', 'field2', 'value2'); $redis->hMGet('h', ['field1', 'field2']); /* returns ['field1' => 'value1', 'field2' => 'value2'] */ @@ -1764,7 +1773,7 @@ Or ~~~php /* Non blocking feature */ $redis->lPush('key1', 'A'); -$redis->delete('key2'); +$redis->del('key2'); $redis->blPop('key1', 'key2', 10); /* ['key1', 'A'] */ /* OR */ @@ -1777,7 +1786,7 @@ $redis->brPop(['key1', 'key2'], 10); /* ['key1', 'A'] */ /* Blocking feature */ /* process 1 */ -$redis->delete('key1'); +$redis->del('key1'); $redis->blPop('key1', 10); /* blocking for 10 seconds */ @@ -1822,11 +1831,13 @@ Return `FALSE` in case of a bad index or a key that doesn't point to a list. $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ -$redis->lGet('key1', 0); /* 'A' */ -$redis->lGet('key1', -1); /* 'C' */ -$redis->lGet('key1', 10); /* `FALSE` */ +$redis->lindex('key1', 0); /* 'A' */ +$redis->lindex('key1', -1); /* 'C' */ +$redis->lindex('key1', 10); /* `FALSE` */ ~~~ +**Note:** `lGet` is an alias for `lIndex` and will be removed in future versions of phpredis. + ### lInsert ----- _**Description**_: Insert value in the list before or after the pivot value. @@ -1845,7 +1856,7 @@ The number of the elements in the list, -1 if the pivot didn't exists. ##### *Example* ~~~php -$redis->delete('key1'); +$redis->del('key1'); $redis->lInsert('key1', Redis::AFTER, 'A', 'X'); /* 0 */ $redis->lPush('key1', 'A'); @@ -1893,7 +1904,7 @@ _**Description**_: Adds the string value to the head (left) of the list. Creates ##### *Examples* ~~~php -$redis->delete('key1'); +$redis->del('key1'); $redis->lPush('key1', 'C'); // returns 1 $redis->lPush('key1', 'B'); // returns 2 $redis->lPush('key1', 'A'); // returns 3 @@ -1913,7 +1924,7 @@ _**Description**_: Adds the string value to the head (left) of the list if the l ##### *Examples* ~~~php -$redis->delete('key1'); +$redis->del('key1'); $redis->lPushx('key1', 'A'); // returns 0 $redis->lPush('key1', 'A'); // returns 1 $redis->lPushx('key1', 'B'); // returns 2 @@ -1943,6 +1954,8 @@ $redis->rPush('key1', 'C'); $redis->lRange('key1', 0, -1); /* ['A', 'B', 'C'] */ ~~~ +**Note:** `lGetRange` is an alias for `lRange` and will be removed in future versions of phpredis. + ### lRem, lRemove ----- _**Description**_: Removes the first `count` occurrences of the value element from the list. If count is zero, all the matching elements are removed. If count is negative, elements are removed from tail to head. @@ -1971,6 +1984,8 @@ $redis->lRem('key1', 'A', 2); /* 2 */ $redis->lRange('key1', 0, -1); /* ['C', 'B', 'A'] */ ~~~ +**Note:** `lRemove` is an alias for `lRem` and will be removed in future versions of phpredis. + ### lSet ----- _**Description**_: Set the list at index with the new value. @@ -1988,9 +2003,9 @@ _**Description**_: Set the list at index with the new value. $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ -$redis->lGet('key1', 0); /* 'A' */ +$redis->lindex('key1', 0); /* 'A' */ $redis->lSet('key1', 0, 'X'); -$redis->lGet('key1', 0); /* 'X' */ +$redis->lindex('key1', 0); /* 'X' */ ~~~ ### lTrim, listTrim @@ -2016,6 +2031,8 @@ $redis->lTrim('key1', 0, 1); $redis->lRange('key1', 0, -1); /* ['A', 'B'] */ ~~~ +**Note:** `listTrim` is an alias for `lTrim` and will be removed in future versions of phpredis. + ### rPop ----- _**Description**_: Returns and removes the last element of the list. @@ -2048,7 +2065,7 @@ _**Description**_: Pops a value from the tail of a list, and pushes it to the fr ##### *Example* ~~~php -$redis->delete('x', 'y'); +$redis->del('x', 'y'); $redis->lPush('x', 'abc'); $redis->lPush('x', 'def'); @@ -2091,7 +2108,7 @@ _**Description**_: Adds the string value to the tail (right) of the list. Create ##### *Examples* ~~~php -$redis->delete('key1'); +$redis->del('key1'); $redis->rPush('key1', 'A'); // returns 1 $redis->rPush('key1', 'B'); // returns 2 $redis->rPush('key1', 'C'); // returns 3 @@ -2111,7 +2128,7 @@ _**Description**_: Adds the string value to the tail (right) of the list if the ##### *Examples* ~~~php -$redis->delete('key1'); +$redis->del('key1'); $redis->rPushX('key1', 'A'); // returns 0 $redis->rPush('key1', 'A'); // returns 1 $redis->rPushX('key1', 'B'); // returns 2 @@ -2137,11 +2154,13 @@ If the list didn't exist or is empty, the command returns 0. If the data type id $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ -$redis->lSize('key1');/* 3 */ +$redis->lLen('key1');/* 3 */ $redis->rPop('key1'); -$redis->lSize('key1');/* 2 */ +$redis->lLen('key1');/* 2 */ ~~~ +**Note:** `lSize` is an alias for `lLen` and will be removed in future versions of phpredis. + ## Sets @@ -2193,6 +2212,8 @@ $redis->sCard('key1'); /* 3 */ $redis->sCard('keyX'); /* 0 */ ~~~ +**Note:** `sSize` is an alias for `sCard` and will be removed in future versions of phpredis. + ### sDiff ----- _**Description**_: Performs the difference between N sets and returns it. @@ -2205,7 +2226,7 @@ _**Description**_: Performs the difference between N sets and returns it. ##### *Example* ~~~php -$redis->delete('s0', 's1', 's2'); +$redis->del('s0', 's1', 's2'); $redis->sAdd('s0', '1'); $redis->sAdd('s0', '2'); @@ -2239,7 +2260,7 @@ _**Description**_: Performs the same action as sDiff, but stores the result in t ##### *Example* ~~~php -$redis->delete('s0', 's1', 's2'); +$redis->del('s0', 's1', 's2'); $redis->sAdd('s0', '1'); $redis->sAdd('s0', '2'); @@ -2365,6 +2386,8 @@ $redis->sIsMember('key1', 'member1'); /* TRUE */ $redis->sIsMember('key1', 'memberX'); /* FALSE */ ~~~ +**Note:** `sContains` is an alias for `sIsMember` and will be removed in future versions of phpredis. + ### sMembers, sGetMembers ----- _**Description**_: Returns the contents of a set. @@ -2377,7 +2400,7 @@ An array of elements, the contents of the set. ##### *Example* ~~~php -$redis->delete('s'); +$redis->del('s'); $redis->sAdd('s', 'a'); $redis->sAdd('s', 'b'); $redis->sAdd('s', 'a'); @@ -2489,6 +2512,8 @@ $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'} $redis->sRem('key1', 'member2', 'member3'); /*return 2. 'key1' => {'member1'} */ ~~~ +**Note:** `sRemove` is an alias for `sRem` and will be removed in future versions of phpredis. + ### sUnion ----- _**Description**_: Performs the union between N sets and returns it. @@ -2503,7 +2528,7 @@ _**Description**_: Performs the union between N sets and returns it. ##### *Example* ~~~php -$redis->delete('s0', 's1', 's2'); +$redis->del('s0', 's1', 's2'); $redis->sAdd('s0', '1'); $redis->sAdd('s0', '2'); @@ -2547,7 +2572,7 @@ _**Description**_: Performs the same action as sUnion, but stores the result in ##### *Example* ~~~php -$redis->delete('s0', 's1', 's2'); +$redis->del('s0', 's1', 's2'); $redis->sAdd('s0', '1'); $redis->sAdd('s0', '2'); @@ -2616,17 +2641,17 @@ while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) { * [zCard, zSize](#zcard-zsize) - Get the number of members in a sorted set * [zCount](#zcount) - Count the members in a sorted set with scores within the given values * [zIncrBy](#zincrby) - Increment the score of a member in a sorted set -* [zInter](#zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key +* [zinterstore, zInter](#zinterstore-zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key * [zRange](#zrange) - Return a range of members in a sorted set, by index * [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score * [zRangeByLex](#zrangebylex) - Return a lexicographical range from members that share the same score * [zRank, zRevRank](#zrank-zrevrank) - Determine the index of a member in a sorted set -* [zRem, zDelete](#zrem-zdelete) - Remove one or more members from a sorted set +* [zRem, zDelete, zRemove](#zrem-zdelete-zremove) - Remove one or more members from a sorted set * [zRemRangeByRank, zDeleteRangeByRank](#zremrangebyrank-zdeleterangebyrank) - Remove all members in a sorted set within the given indexes -* [zRemRangeByScore, zDeleteRangeByScore](#zremrangebyscore-zdeleterangebyscore) - Remove all members in a sorted set within the given scores +* [zRemRangeByScore, zDeleteRangeByScore, zRemoveRangeByScore](#zremrangebyscore-zdeleterangebyscore-zremoverangebyscore) - Remove all members in a sorted set within the given scores * [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low * [zScore](#zscore) - Get the score associated with the given member in a sorted set -* [zUnion](#zunion) - Add multiple sorted sets and store the resulting sorted set in a new key +* [zunionstore, zUnion](#zunionstore-zunion) - Add multiple sorted sets and store the resulting sorted set in a new key * [zScan](#zscan) - Scan a sorted set for members ### zAdd @@ -2701,13 +2726,13 @@ _**Description**_: Increments the score of a member from a sorted set by a given ##### *Examples* ~~~php -$redis->delete('key'); +$redis->del('key'); $redis->zIncrBy('key', 2.5, 'member1'); /* key or member1 didn't exist, so member1's score is to 0 before the increment */ /* and now has the value 2.5 */ $redis->zIncrBy('key', 1, 'member1'); /* 3.5 */ ~~~ -### zInter +### zinterstore, zInter ----- _**Description**_: Creates an intersection of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. @@ -2718,21 +2743,21 @@ The forth argument defines the `AGGREGATE` option which specify how the results *keyOutput* *arrayZSetKeys* *arrayWeights* -*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zInter. +*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zinterstore. ##### *Return value* *LONG* The number of values in the new sorted set. ##### *Example* ~~~php -$redis->delete('k1'); -$redis->delete('k2'); -$redis->delete('k3'); +$redis->del('k1'); +$redis->del('k2'); +$redis->del('k3'); -$redis->delete('ko1'); -$redis->delete('ko2'); -$redis->delete('ko3'); -$redis->delete('ko4'); +$redis->del('ko1'); +$redis->del('ko2'); +$redis->del('ko3'); +$redis->del('ko4'); $redis->zAdd('k1', 0, 'val0'); $redis->zAdd('k1', 1, 'val1'); @@ -2741,14 +2766,16 @@ $redis->zAdd('k1', 3, 'val3'); $redis->zAdd('k2', 2, 'val1'); $redis->zAdd('k2', 3, 'val3'); -$redis->zInter('ko1', ['k1', 'k2']); /* 2, 'ko1' => ['val1', 'val3'] */ -$redis->zInter('ko2', ['k1', 'k2'], [1, 1]); /* 2, 'ko2' => ['val1', 'val3'] */ +$redis->zinterstore('ko1', ['k1', 'k2']); /* 2, 'ko1' => ['val1', 'val3'] */ +$redis->zinterstore('ko2', ['k1', 'k2'], [1, 1]); /* 2, 'ko2' => ['val1', 'val3'] */ -/* Weighted zInter */ -$redis->zInter('ko3', ['k1', 'k2'], [1, 5], 'min'); /* 2, 'ko3' => ['val1', 'val3'] */ -$redis->zInter('ko4', ['k1', 'k2'], [1, 5], 'max'); /* 2, 'ko4' => ['val3', 'val1'] */ +/* Weighted zinterstore */ +$redis->zinterstore('ko3', ['k1', 'k2'], [1, 5], 'min'); /* 2, 'ko3' => ['val1', 'val3'] */ +$redis->zinterstore('ko4', ['k1', 'k2'], [1, 5], 'max'); /* 2, 'ko4' => ['val3', 'val1'] */ ~~~ +**Note:** `zInter` is an alias for `zinterstore` and will be removed in future versions of phpredis. + ### zRange ----- _**Description**_: Returns a range of elements from the ordered set stored at the specified key, with values in the range [start, end]. @@ -2840,7 +2867,7 @@ _**Description**_: Returns the rank of a given member in the specified sorted se ##### *Example* ~~~php -$redis->delete('z'); +$redis->del('z'); $redis->zAdd('key', 1, 'one'); $redis->zAdd('key', 2, 'two'); $redis->zRank('key', 'one'); /* 0 */ @@ -2849,7 +2876,7 @@ $redis->zRevRank('key', 'one'); /* 1 */ $redis->zRevRank('key', 'two'); /* 0 */ ~~~ -### zRem, zDelete +### zRem, zDelete, zRemove ----- _**Description**_: Delete one or more members from a sorted set. @@ -2867,7 +2894,7 @@ $redis->zAdd('key', 0, 'val0', 1, 'val1', 2, 'val2'); $redis->zRem('key', 'val0', 'val1', 'val2'); // Returns: 3 ~~~ -**Note:** `zDelete` is an alias for `zRem` and may be removed in future versions of phpredis. +**Note:** `zDelete` and `zRemove` are an alias for `zRem` and will be removed in future versions of phpredis. ### zRemRangeByRank, zDeleteRangeByRank ----- @@ -2890,7 +2917,9 @@ $redis->zRemRangeByRank('key', 0, 1); /* 2 */ $redis->zRange('key', 0, -1, ['withscores' => TRUE]); /* ['three' => 3] */ ~~~ -### zRemRangeByScore, zDeleteRangeByScore +**Note:** `zDeleteRangeByRank` is an alias for `zRemRangeByRank` and will be removed in future versions of phpredis. + +### zRemRangeByScore, zDeleteRangeByScore, zRemoveRangeByScore ----- _**Description**_: Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end]. @@ -2910,6 +2939,8 @@ $redis->zAdd('key', 10, 'val10'); $redis->zRemRangeByScore('key', 0, 3); /* 2 */ ~~~ +**Note:** `zDeleteRangeByScore` and `zRemoveRangeByScore` are an alias for `zRemRangeByScore` and will be removed in future versions of phpredis. + ### zRevRange ----- _**Description**_: Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpreted as zero-based indices: @@ -2953,7 +2984,7 @@ $redis->zAdd('key', 2.5, 'val2'); $redis->zScore('key', 'val2'); /* 2.5 */ ~~~ -### zUnion +### zunionstore, zUnion ----- _**Description**_: Creates an union of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. @@ -2964,19 +2995,19 @@ The forth argument defines the `AGGREGATE` option which specify how the results *keyOutput* *arrayZSetKeys* *arrayWeights* -*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zUnion. +*aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zunionstore. ##### *Return value* *LONG* The number of values in the new sorted set. ##### *Example* ~~~php -$redis->delete('k1'); -$redis->delete('k2'); -$redis->delete('k3'); -$redis->delete('ko1'); -$redis->delete('ko2'); -$redis->delete('ko3'); +$redis->del('k1'); +$redis->del('k2'); +$redis->del('k3'); +$redis->del('ko1'); +$redis->del('ko2'); +$redis->del('ko3'); $redis->zAdd('k1', 0, 'val0'); $redis->zAdd('k1', 1, 'val1'); @@ -2984,13 +3015,15 @@ $redis->zAdd('k1', 1, 'val1'); $redis->zAdd('k2', 2, 'val2'); $redis->zAdd('k2', 3, 'val3'); -$redis->zUnion('ko1', ['k1', 'k2']); /* 4, 'ko1' => ['val0', 'val1', 'val2', 'val3'] */ +$redis->zunionstore('ko1', ['k1', 'k2']); /* 4, 'ko1' => ['val0', 'val1', 'val2', 'val3'] */ -/* Weighted zUnion */ -$redis->zUnion('ko2', ['k1', 'k2'], [1, 1]); /* 4, 'ko2' => ['val0', 'val1', 'val2', 'val3'] */ -$redis->zUnion('ko3', ['k1', 'k2'], [5, 1]); /* 4, 'ko3' => ['val0', 'val2', 'val3', 'val1'] */ +/* Weighted zunionstore */ +$redis->zunionstore('ko2', ['k1', 'k2'], [1, 1]); /* 4, 'ko2' => ['val0', 'val1', 'val2', 'val3'] */ +$redis->zunionstore('ko3', ['k1', 'k2'], [5, 1]); /* 4, 'ko3' => ['val0', 'val2', 'val3', 'val1'] */ ~~~ +**Note:** `zUnion` is an alias for `zunionstore` and will be removed in future versions of phpredis. + ### zScan ----- _**Description**_: Scan a sorted set for members, with optional pattern and count From 4852a5106a10d52dee8b570a9736aa37cf606661 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 12 Jun 2019 12:20:38 +0300 Subject: [PATCH 1188/1986] xInfo response format --- library.c | 88 +++++++++++++++++++++++++++++++++++++++++++++ library.h | 2 ++ redis.c | 2 +- tests/RedisTest.php | 28 +++++++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index 1b766d76d1..f312735ce4 100644 --- a/library.c +++ b/library.c @@ -1451,6 +1451,94 @@ redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } +static int +redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements) +{ + zval zv; + int i, len; + char *key = NULL, *data; + REDIS_REPLY_TYPE type; + long li; + + for (i = 0; i < elements; ++i) { + if (redis_read_reply_type(redis_sock, &type, &li TSRMLS_CC) < 0) { + goto failure; + } + switch (type) { + case TYPE_BULK: + if ((data = redis_sock_read_bulk_reply(redis_sock, li TSRMLS_CC)) == NULL) { + goto failure; + } else if (key) { + add_assoc_stringl_ex(z_ret, key, len, data, li); + efree(data); + efree(key); + key = NULL; + } else { + key = data; + len = li; + } + break; + case TYPE_INT: + if (key) { + add_assoc_long_ex(z_ret, key, len, li); + efree(key); + key = NULL; + } else { + len = spprintf(&key, 0, "%ld", li); + } + break; + case TYPE_MULTIBULK: + array_init(&zv); + if (redis_read_xinfo_response(redis_sock, &zv, li) != SUCCESS) { + zval_dtor(&zv); + goto failure; + } + if (key) { + add_assoc_zval_ex(z_ret, key, len, &zv); + efree(key); + key = NULL; + } else { + add_next_index_zval(z_ret, &zv); + } + break; + default: + goto failure; + } + } + + return SUCCESS; + +failure: + if (key) efree(key); + return FAILURE; +} + +PHP_REDIS_API int +redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + zval z_ret; + int i, elements; + + if (read_mbulk_header(redis_sock, &elements TSRMLS_CC) == SUCCESS) { + array_init(&z_ret); + if (redis_read_xinfo_response(redis_sock, &z_ret, elements TSRMLS_CC) == SUCCESS) { + if (IS_ATOMIC(redis_sock)) { + RETVAL_ZVAL(&z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, &z_ret); + } + return SUCCESS; + } + zval_dtor(&z_ret); + } + if (IS_ATOMIC(redis_sock)) { + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); + } + return FAILURE; +} + /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */ PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { diff --git a/library.h b/library.h index 16204295f6..4f36399115 100644 --- a/library.h +++ b/library.h @@ -78,6 +78,8 @@ PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/redis.c b/redis.c index c76a3c96b8..4ad9761ff7 100644 --- a/redis.c +++ b/redis.c @@ -3663,7 +3663,7 @@ PHP_METHOD(Redis, xgroup) { } PHP_METHOD(Redis, xinfo) { - REDIS_PROCESS_CMD(xinfo, redis_read_variant_reply); + REDIS_PROCESS_CMD(xinfo, redis_xinfo_reply); } PHP_METHOD(Redis, xlen) { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index f1f960cf0d..d5aa89b656 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5854,6 +5854,34 @@ public function testXClaim() { } } + 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])); + } + } + public function testSession_savedToRedis() { $this->setSessionHandler(); From ac9dca0a925de9cd87268c1e4629893cc436e50c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 12 Jun 2019 13:50:50 +0300 Subject: [PATCH 1189/1986] Fix xInfo ro RedisCluster --- cluster_library.c | 18 ++++++++++++++++++ cluster_library.h | 2 ++ library.c | 2 +- library.h | 2 ++ redis_cluster.c | 2 +- 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index dd1e0bbcb6..2e9269635a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2314,6 +2314,24 @@ cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { } +/* XINFO */ +PHP_REDIS_API void +cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +{ + zval z_ret; + + array_init(&z_ret); + if (redis_read_xinfo_response(c->cmd_sock, &z_ret, c->reply_len) != SUCCESS) { + zval_dtor(&z_ret); + CLUSTER_RETURN_FALSE(c); + } + + if (CLUSTER_IS_ATOMIC(c)) { + RETURN_ZVAL(&z_ret, 0, 1); + } + add_next_index_zval(&c->multi_resp, &z_ret); +} + /* MULTI BULK response loop where we might pull the next one */ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb, zval *z_ret) diff --git a/cluster_library.h b/cluster_library.h index 3b3b4a7d8d..df83d96799 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -487,6 +487,8 @@ PHP_REDIS_API void cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, diff --git a/library.c b/library.c index f312735ce4..8d44fb6adf 100644 --- a/library.c +++ b/library.c @@ -1451,7 +1451,7 @@ redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } -static int +PHP_REDIS_API int redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements) { zval zv; diff --git a/library.h b/library.h index 4f36399115..c60e4a69d6 100644 --- a/library.h +++ b/library.h @@ -109,6 +109,8 @@ PHP_REDIS_API int redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_ret TSRMLS_DC); PHP_REDIS_API int redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC); +PHP_REDIS_API int +redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements TSRMLS_DC); /* * Variant Read methods, mostly to implement eval diff --git a/redis_cluster.c b/redis_cluster.c index 0342096c1e..591138f6c3 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -3095,7 +3095,7 @@ PHP_METHOD(RedisCluster, xgroup) { /* {{{ proto variant RedisCluster::xinfo(string op, [string arg1, string arg2]); */ PHP_METHOD(RedisCluster, xinfo) { - CLUSTER_PROCESS_CMD(xinfo, cluster_variant_resp, 0); + CLUSTER_PROCESS_CMD(xinfo, cluster_xinfo_resp, 0); } /* {{{ proto string RedisCluster::xlen(string key) }}} */ From 7ff6e5368fd2715d99e8d41f2e137d67433e2e26 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 12 Jun 2019 15:31:20 -0700 Subject: [PATCH 1190/1986] Remove unused variable --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 8d44fb6adf..a4637b7ef0 100644 --- a/library.c +++ b/library.c @@ -1517,7 +1517,7 @@ PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { zval z_ret; - int i, elements; + int elements; if (read_mbulk_header(redis_sock, &elements TSRMLS_CC) == SUCCESS) { array_init(&z_ret); From 17600dd131a1ad11af081d0345a4627613f83b9d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 4 Jun 2019 21:56:39 -0700 Subject: [PATCH 1191/1986] WIP: Enable pooling for cluster slave nodes. Connections are stashed via redis_sock_disconnect so if RedisCluster doesn't explicitly call that for slaves then pooling is never used for those connections. Addresses #1568 --- cluster_library.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 2e9269635a..564629b528 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1256,11 +1256,19 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type /* Disconnect from each node we're connected to */ PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC) { - redisClusterNode *node; + redisClusterNode *node, *slave; ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) continue; + + /* Disconnect from the master */ redis_sock_disconnect(node->sock, force TSRMLS_CC); + + /* We also want to disconnect any slave connections so they will be pooled + * in the event we are using persistent connections and connection pooling. */ + ZEND_HASH_FOREACH_PTR(node->slaves, slave) { + redis_sock_disconnect(slave->sock, force TSRMLS_CC); + } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); } From 9a699e53087dc730212c86cfc7b44e8357f4fae3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 16 Jun 2019 11:37:05 -0700 Subject: [PATCH 1192/1986] Try to give the EXPIREAT test a bit more leeway to succeed. --- tests/RedisTest.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index d5aa89b656..8ec3246760 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -466,14 +466,20 @@ public function testSetTimeout() { $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() { - $this->redis->del('key'); - $this->redis->set('key', 'value'); - $now = time(); - $this->redis->expireAt('key', $now + 1); - $this->assertEquals('value', $this->redis->get('key')); - sleep(2); - $this->assertEquals(FALSE, $this->redis->get('key')); + $success = false; + + for ($i = 0; !$success && $i < 3; $i++) { + $this->redis->del('key'); + $time = $this->redis->time(); + $this->redis->set('key', 'value'); + $this->redis->expireAt('key', $time[0] + 1); + usleep(1500000); + $success = FALSE === $this->redis->get('key'); + } + + $this->assertTrue($success); } public function testSetEx() { From 19e47b4b322c1befebf82aff4e17d28a1ec26037 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 16 Jun 2019 11:44:49 -0700 Subject: [PATCH 1193/1986] Revert to using PHP's time --- tests/RedisTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 8ec3246760..48ae46bb30 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -472,9 +472,8 @@ public function testExpireAt() { for ($i = 0; !$success && $i < 3; $i++) { $this->redis->del('key'); - $time = $this->redis->time(); $this->redis->set('key', 'value'); - $this->redis->expireAt('key', $time[0] + 1); + $this->redis->expireAt('key', time() + 1); usleep(1500000); $success = FALSE === $this->redis->get('key'); } From 95c8aab9dfc60890d713f48d78f4e43c373843bf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 16 Jun 2019 15:58:00 -0700 Subject: [PATCH 1194/1986] Soft deprecate methods that aren't actually Redis commands. Addresses #1168 --- redis.c | 68 +++++++------- tests/RedisTest.php | 216 +++++++++++++++++++++----------------------- 2 files changed, 137 insertions(+), 147 deletions(-) diff --git a/redis.c b/redis.c index 4ad9761ff7..ea184755d7 100644 --- a/redis.c +++ b/redis.c @@ -386,10 +386,10 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, slaveof, arginfo_slaveof, ZEND_ACC_PUBLIC) PHP_ME(Redis, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC) PHP_ME(Redis, sort, arginfo_sort, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortAsc, arginfo_generic_sort, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC) - PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC) + PHP_ME(Redis, sortAsc, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) PHP_ME(Redis, srem, arginfo_key_members, ZEND_ACC_PUBLIC) PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC) @@ -439,35 +439,37 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, delete, del, arginfo_del, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, getKeys, keys, arginfo_keys, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lGet, lindex, arginfo_lindex, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lGetRange, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lRemove, lrem, arginfo_lrem, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, lSize, lLen, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, listTrim, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, renameKey, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sContains, sismember, arginfo_key_value, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sRemove, srem, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sSize, scard, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, setTimeout, expire, arginfo_expire, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zDelete, zRem, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zDeleteRangeByRank, zRemRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zDeleteRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zInter, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemove, zRem, arginfo_key_members, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zRemoveRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zReverseRange, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC) - PHP_MALIAS(Redis, zUnion, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC) + + /* Mark all of these aliases deprecated. They aren't actual Redis commands. */ + PHP_MALIAS(Redis, delete, del, arginfo_del, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, getKeys, keys, arginfo_keys, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, lGet, lindex, arginfo_lindex, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, lGetRange, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, lRemove, lrem, arginfo_lrem, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, lSize, lLen, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, listTrim, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, renameKey, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, sContains, sismember, arginfo_key_value, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, sRemove, srem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, sSize, scard, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, setTimeout, expire, arginfo_expire, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zDelete, zRem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zDeleteRangeByRank, zRemRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zDeleteRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zInter, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zRemove, zRem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zRemoveRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zReverseRange, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) + PHP_MALIAS(Redis, zUnion, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) PHP_FE_END }; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 48ae46bb30..5f2423e7de 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -639,9 +639,9 @@ public function testExists() $this->assertEquals(count($mkeys), call_user_func_array([$this->redis, 'exists'], $mkeys)); } - public function testGetKeys() + public function testKeys() { - $pattern = 'getKeys-test-'; + $pattern = 'keys-test-'; for($i = 1; $i < 10; $i++) { $this->redis->set($pattern.$i, $i); } @@ -1090,7 +1090,7 @@ public function testSortDesc() { } // LINDEX - public function testlGet() { + public function testLindex() { $this->redis->del('list'); @@ -1098,17 +1098,17 @@ public function testlGet() { $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); - $this->assertEquals('val3', $this->redis->lGet('list', 0)); - $this->assertEquals('val2', $this->redis->lGet('list', 1)); - $this->assertEquals('val', $this->redis->lGet('list', 2)); - $this->assertEquals('val', $this->redis->lGet('list', -1)); - $this->assertEquals('val2', $this->redis->lGet('list', -2)); - $this->assertEquals('val3', $this->redis->lGet('list', -3)); - $this->assertEquals(FALSE, $this->redis->lGet('list', -4)); + $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->lGet('list', 3)); - $this->assertEquals('val4', $this->redis->lGet('list', -1)); + $this->assertEquals('val4', $this->redis->lIndex('list', 3)); + $this->assertEquals('val4', $this->redis->lIndex('list', -1)); } // lRem testing @@ -1120,14 +1120,15 @@ public function testlrem() { $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->lGET('list', 0)); - $this->assertEquals('c', $this->redis->lGET('list', 1)); - $this->assertEquals('c', $this->redis->lGET('list', 2)); - $this->assertEquals('a', $this->redis->lGET('list', 3)); + + // ['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'); @@ -1136,72 +1137,60 @@ public function testlrem() { $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->lGET('list', 0)); - $this->assertEquals('b', $this->redis->lGET('list', 1)); - $this->assertEquals('b', $this->redis->lGET('list', 2)); - $this->assertEquals('a', $this->redis->lGET('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')); + // ['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() - { + public function testsAdd() { $this->redis->del('set'); - $this->assertEquals(1, $this->redis->sAdd('set', 'val')); - $this->assertEquals(0, $this->redis->sAdd('set', 'val')); + $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->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')); + 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(1, $this->redis->sAdd('set', 'val2')); $this->assertEquals(2, $this->redis->scard('set')); } - public function testsrem() - { + 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() - { + public function testsMove() { $this->redis->del('{set}0'); $this->redis->del('{set}1'); @@ -1219,8 +1208,7 @@ public function testsMove() $this->assertEquals(['val'], $this->redis->smembers('{set}1')); } - public function testsPop() - { + public function testsPop() { $this->redis->del('set0'); $this->assertTrue($this->redis->sPop('set0') === FALSE); @@ -1399,15 +1387,15 @@ public function testlSet() { $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); - $this->assertEquals($this->redis->lGet('list', 0), 'val3'); - $this->assertEquals($this->redis->lGet('list', 1), 'val2'); - $this->assertEquals($this->redis->lGet('list', 2), 'val'); + $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->lGet('list', 0), 'val3'); - $this->assertEquals($this->redis->lGet('list', 1), 'valx'); - $this->assertEquals($this->redis->lGet('list', 2), 'val'); + $this->assertEquals($this->redis->lIndex('list', 0), 'val3'); + $this->assertEquals($this->redis->lIndex('list', 1), 'valx'); + $this->assertEquals($this->redis->lIndex('list', 2), 'val'); } @@ -2288,7 +2276,7 @@ public function testZX() { $this->redis->del('{zset}1'); - // zInter + // zInterStore $this->redis->zAdd('{zset}1', 0, 'val0'); $this->redis->zAdd('{zset}1', 1, 'val1'); @@ -2313,7 +2301,7 @@ public function testZX() { $this->assertTrue([] === $this->redis->zRange('keyY', 0, -1)); - // test weighted zInter + // test weighted zInterStore $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); $this->redis->del('{zset}3'); @@ -2928,7 +2916,7 @@ protected function sequence($mode) { ->llen('{list}lkey') ->lrem('{list}lkey', 'lvalue', 3) ->llen('{list}lkey') - ->lget('{list}lkey', 0) + ->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) @@ -3086,7 +3074,7 @@ protected function sequence($mode) { ->llen('{l}key') ->lrem('{l}key', 'lvalue', 3) ->llen('{l}key') - ->lget('{l}key', 0) + ->lIndex('{l}key', 0) ->lrange('{l}key', 0, -1) ->lSet('{l}key', 1, "newValue") // check errors on missing key ->lrange('{l}key', 0, -1) @@ -3095,7 +3083,7 @@ protected function sequence($mode) { $this->assertTrue(is_array($ret)); $i = 0; - $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // delete + $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // del $i++; $this->assertTrue($ret[$i++] === 1); // 1 value $this->assertTrue($ret[$i++] === 2); // 2 values @@ -3351,7 +3339,7 @@ protected function differentType($mode) { ->lPop($key) ->lrange($key, 0, -1) ->lTrim($key, 0, 1) - ->lGet($key, 0) + ->lIndex($key, 0) ->lSet($key, 0, "newValue") ->lrem($key, 'lvalue', 1) ->lPop($key) @@ -3412,9 +3400,9 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // lpush $this->assertTrue($ret[$i++] === FALSE); // llen $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // lrange $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lget + $this->assertTrue($ret[$i++] === FALSE); // lindex $this->assertTrue($ret[$i++] === FALSE); // lset $this->assertTrue($ret[$i++] === FALSE); // lremove $this->assertTrue($ret[$i++] === FALSE); // lpop @@ -3425,8 +3413,8 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // sremove $this->assertTrue($ret[$i++] === FALSE); // spop $this->assertTrue($ret[$i++] === FALSE); // smove - $this->assertTrue($ret[$i++] === FALSE); // ssize - $this->assertTrue($ret[$i++] === FALSE); // scontains + $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 @@ -3434,7 +3422,7 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // srandmember $this->assertTrue($ret[$i++] === FALSE); // zadd - $this->assertTrue($ret[$i++] === FALSE); // zdelete + $this->assertTrue($ret[$i++] === FALSE); // zrem $this->assertTrue($ret[$i++] === FALSE); // zincrby $this->assertTrue($ret[$i++] === FALSE); // zrank $this->assertTrue($ret[$i++] === FALSE); // zrevrank @@ -3444,8 +3432,8 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // zcount $this->assertTrue($ret[$i++] === FALSE); // zcard $this->assertTrue($ret[$i++] === FALSE); // zscore - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore $this->assertTrue($ret[$i++] === FALSE); // hset $this->assertTrue($ret[$i++] === FALSE); // hget @@ -3542,8 +3530,8 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // sremove $this->assertTrue($ret[$i++] === FALSE); // spop $this->assertTrue($ret[$i++] === FALSE); // smove - $this->assertTrue($ret[$i++] === FALSE); // ssize - $this->assertTrue($ret[$i++] === FALSE); // scontains + $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 @@ -3551,7 +3539,7 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // srandmember $this->assertTrue($ret[$i++] === FALSE); // zadd - $this->assertTrue($ret[$i++] === FALSE); // zdelete + $this->assertTrue($ret[$i++] === FALSE); // zrem $this->assertTrue($ret[$i++] === FALSE); // zincrby $this->assertTrue($ret[$i++] === FALSE); // zrank $this->assertTrue($ret[$i++] === FALSE); // zrevrank @@ -3561,8 +3549,8 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // zcount $this->assertTrue($ret[$i++] === FALSE); // zcard $this->assertTrue($ret[$i++] === FALSE); // zscore - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore $this->assertTrue($ret[$i++] === FALSE); // hset $this->assertTrue($ret[$i++] === FALSE); // hget @@ -3603,7 +3591,7 @@ protected function differentType($mode) { ->lPop($key) ->lrange($key, 0, -1) ->lTrim($key, 0, 1) - ->lGet($key, 0) + ->lIndex($key, 0) ->lSet($key, 0, "newValue") ->lrem($key, 'lvalue', 1) ->lPop($key) @@ -3660,9 +3648,9 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // lpush $this->assertTrue($ret[$i++] === FALSE); // llen $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // lrange $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lget + $this->assertTrue($ret[$i++] === FALSE); // lindex $this->assertTrue($ret[$i++] === FALSE); // lset $this->assertTrue($ret[$i++] === FALSE); // lremove $this->assertTrue($ret[$i++] === FALSE); // lpop @@ -3670,7 +3658,7 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // rpoplush $this->assertTrue($ret[$i++] === FALSE); // zadd - $this->assertTrue($ret[$i++] === FALSE); // zdelete + $this->assertTrue($ret[$i++] === FALSE); // zrem $this->assertTrue($ret[$i++] === FALSE); // zincrby $this->assertTrue($ret[$i++] === FALSE); // zrank $this->assertTrue($ret[$i++] === FALSE); // zrevrank @@ -3680,8 +3668,8 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // zcount $this->assertTrue($ret[$i++] === FALSE); // zcard $this->assertTrue($ret[$i++] === FALSE); // zscore - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore $this->assertTrue($ret[$i++] === FALSE); // hset $this->assertTrue($ret[$i++] === FALSE); // hget @@ -3722,7 +3710,7 @@ protected function differentType($mode) { ->lPop($key) ->lrange($key, 0, -1) ->lTrim($key, 0, 1) - ->lGet($key, 0) + ->lIndex($key, 0) ->lSet($key, 0, "newValue") ->lrem($key, 'lvalue', 1) ->lPop($key) @@ -3777,9 +3765,9 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // lpush $this->assertTrue($ret[$i++] === FALSE); // llen $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // lrange $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lget + $this->assertTrue($ret[$i++] === FALSE); // lindex $this->assertTrue($ret[$i++] === FALSE); // lset $this->assertTrue($ret[$i++] === FALSE); // lremove $this->assertTrue($ret[$i++] === FALSE); // lpop @@ -3790,8 +3778,8 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // sremove $this->assertTrue($ret[$i++] === FALSE); // spop $this->assertTrue($ret[$i++] === FALSE); // smove - $this->assertTrue($ret[$i++] === FALSE); // ssize - $this->assertTrue($ret[$i++] === FALSE); // scontains + $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 @@ -3837,7 +3825,7 @@ protected function differentType($mode) { ->lPop($key) ->lrange($key, 0, -1) ->lTrim($key, 0, 1) - ->lGet($key, 0) + ->lIndex($key, 0) ->lSet($key, 0, "newValue") ->lrem($key, 'lvalue', 1) ->lPop($key) @@ -3894,9 +3882,9 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // lpush $this->assertTrue($ret[$i++] === FALSE); // llen $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // lgetrange + $this->assertTrue($ret[$i++] === FALSE); // lrange $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lget + $this->assertTrue($ret[$i++] === FALSE); // lindex $this->assertTrue($ret[$i++] === FALSE); // lset $this->assertTrue($ret[$i++] === FALSE); // lremove $this->assertTrue($ret[$i++] === FALSE); // lpop @@ -3907,8 +3895,8 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // sremove $this->assertTrue($ret[$i++] === FALSE); // spop $this->assertTrue($ret[$i++] === FALSE); // smove - $this->assertTrue($ret[$i++] === FALSE); // ssize - $this->assertTrue($ret[$i++] === FALSE); // scontains + $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 @@ -3916,7 +3904,7 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // srandmember $this->assertTrue($ret[$i++] === FALSE); // zadd - $this->assertTrue($ret[$i++] === FALSE); // zdelete + $this->assertTrue($ret[$i++] === FALSE); // zrem $this->assertTrue($ret[$i++] === FALSE); // zincrby $this->assertTrue($ret[$i++] === FALSE); // zrank $this->assertTrue($ret[$i++] === FALSE); // zrevrank @@ -3926,8 +3914,8 @@ protected function differentType($mode) { $this->assertTrue($ret[$i++] === FALSE); // zcount $this->assertTrue($ret[$i++] === FALSE); // zcard $this->assertTrue($ret[$i++] === FALSE); // zscore - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zdeleterangebyscore + $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank + $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore $this->assertEquals($i, count($ret)); } @@ -3946,7 +3934,7 @@ public function testDifferentTypeString() { $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); $this->assertEquals(FALSE, $this->redis->lPop($key)); @@ -4079,7 +4067,7 @@ public function testDifferentTypeSet() { $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); $this->assertEquals(FALSE, $this->redis->lPop($key)); @@ -4140,7 +4128,7 @@ public function testDifferentTypeSortedSet() { $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); $this->assertEquals(FALSE, $this->redis->lPop($key)); @@ -4199,7 +4187,7 @@ public function testDifferentTypeHash() { $this->assertEquals(FALSE, $this->redis->lPop($key)); $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lGet($key, 0)); + $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); $this->assertEquals(FALSE, $this->redis->lPop($key)); @@ -4295,11 +4283,11 @@ private function checkSerializer($mode) { // lrange $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); - // lGet - $this->assertTrue($a[0] === $this->redis->lGet('key', 0)); - $this->assertTrue($a[1] === $this->redis->lGet('key', 1)); - $this->assertTrue($a[2] === $this->redis->lGet('key', 2)); - $this->assertTrue($a[3] === $this->redis->lGet('key', 3)); + // 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); @@ -4308,7 +4296,7 @@ private function checkSerializer($mode) { // lSet $a[0] = ['k' => 'v']; // update $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); - $this->assertTrue($a[0] === $this->redis->lGet('key', 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); From 8206b14749e2583895023312c2143116c2480a50 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Jun 2019 11:38:36 +0300 Subject: [PATCH 1195/1986] Enable connection pooling by default --- .travis.yml | 1 - redis.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9783c1d3c5..47685adc30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,6 @@ before_script: # but --cluster is an unknown option for travis trusty - echo yes | ruby redis-trib.rb create --replicas 3 $(seq -f 127.0.0.1:%g 7000 7011) - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - echo 'redis.pconnect.pooling_enabled = 1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini script: - php tests/TestRedis.php --class Redis - php tests/TestRedis.php --class RedisArray diff --git a/redis.c b/redis.c index ea184755d7..15a5038e47 100644 --- a/redis.c +++ b/redis.c @@ -84,7 +84,7 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL) /* redis pconnect */ - PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL) /* redis session */ From 90aa067cd48d6406336b3852cbbc5af58395637b Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 21 Jun 2019 16:38:36 +0200 Subject: [PATCH 1196/1986] Update Fedora installation instructions Remove Fedora <= 28 (redis v3) which is now EOL Fedora 31 already have php-pecl-redis5 version 5.0.0RC1 php-pecl-redis4 will be removed from F31 repo and php-pecl-redis5 added to F29/F30 repo after 5.0.0GA --- INSTALL.markdown | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/INSTALL.markdown b/INSTALL.markdown index 88d1b360f7..96d1399454 100644 --- a/INSTALL.markdown +++ b/INSTALL.markdown @@ -36,22 +36,22 @@ Follow the DLL link on the [https://pecl.php.net/package/redis](https://pecl.php ## Fedora -Fedora users can install the package from the official repositor. +Fedora users can install the package from the official repository. -**Fedora ≤ 28, Version 3 ** +### Fedora ≤ 30, Version 4 -Installation of the [php-pecl-redis](https://apps.fedoraproject.org/packages/php-pecl-redis) package: +Installation of the [php-pecl-redis4](https://apps.fedoraproject.org/packages/php-pecl-redis4) package: ~~~ -dnf install php-pecl-redis +dnf install php-pecl-redis4 ~~~ -**Fedora ≥ 27, Version 4 ** +### Fedora ≥ 29, Version 5 -Installation of the [php-pecl-redis4](https://apps.fedoraproject.org/packages/php-pecl-redis4) package: +Installation of the [php-pecl-redis5](https://apps.fedoraproject.org/packages/php-pecl-redis5) package: ~~~ -dnf install php-pecl-redis4 +dnf install php-pecl-redis5 ~~~ ## RHEL / CentOS From 235a27e7c088bbf5e5dbbd8b4a2684b600d6c79a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 20 Jun 2019 16:50:13 +0300 Subject: [PATCH 1197/1986] Issue #1577 Remove checking of version of msgpack. Allow to disable json serializer. Fix tests. --- config.m4 | 132 ++++++++++++++++++++++++++++---------------- library.c | 13 ++++- redis.c | 40 +++++++++++--- tests/RedisTest.php | 49 ++++++++-------- 4 files changed, 151 insertions(+), 83 deletions(-) diff --git a/config.m4 b/config.m4 index d84ff46c07..63a59b2e5d 100644 --- a/config.m4 +++ b/config.m4 @@ -8,6 +8,9 @@ dnl Make sure that the comment is aligned: PHP_ARG_ENABLE(redis-session, whether to disable sessions, [ --disable-redis-session Disable session support], yes, no) +PHP_ARG_ENABLE(redis-json, whether to disable json serializer support, +[ --disable-redis-json Disable json serializer support], yes, no) + PHP_ARG_ENABLE(redis-igbinary, whether to enable igbinary serializer support, [ --enable-redis-igbinary Enable igbinary serializer support], no, no) @@ -26,7 +29,46 @@ if test "$PHP_REDIS" != "no"; then AC_DEFINE(PHP_SESSION,1,[redis sessions]) fi -dnl Check for igbinary + if test "PHP_REDIS_JSON" != "no"; then + AC_MSG_CHECKING([for json includes]) + json_inc_path="" + if test -f "$abs_srcdir/include/php/ext/json/php_json.h"; then + json_inc_path="$abs_srcdir/include/php" + elif test -f "$abs_srcdir/ext/json/php_json.h"; then + json_inc_path="$abs_srcdir" + elif test -f "$phpincludedir/ext/json/php_json.h"; then + json_inc_path="$phpincludedir" + else + for i in php php7; do + if test -f "$prefix/include/$i/ext/json/php_json.h"; then + json_inc_path="$prefix/include/$i" + fi + done + fi + + if test "$json_inc_path" = ""; then + AC_MSG_ERROR([Cannot find php_json.h]) + else + AC_MSG_RESULT([$json_inc_path]) + fi + fi + + AC_MSG_CHECKING([for redis json support]) + if test "$PHP_REDIS_JSON" != "no"; then + AC_MSG_RESULT([enabled]) + AC_DEFINE(HAVE_REDIS_JSON,1,[Whether redis json serializer is enabled]) + JSON_INCLUDES="-I$json_inc_path" + JSON_EXT_DIR="$json_inc_path/ext" + ifdef([PHP_ADD_EXTENSION_DEP], + [ + PHP_ADD_EXTENSION_DEP(redis, json) + ]) + PHP_ADD_INCLUDE($JSON_EXT_DIR) + else + JSON_INCLUDES="" + AC_MSG_RESULT([disabled]) + fi + if test "$PHP_REDIS_IGBINARY" != "no"; then AC_MSG_CHECKING([for igbinary includes]) igbinary_inc_path="" @@ -68,6 +110,47 @@ dnl Check for igbinary AC_MSG_RESULT([disabled]) fi + if test "$PHP_REDIS_MSGPACK" != "no"; then + AC_MSG_CHECKING([for msgpack includes]) + msgpack_inc_path="" + + if test -f "$abs_srcdir/include/php/ext/msgpack/php_msgpack.h"; then + msgpack_inc_path="$abs_srcdir/include/php" + elif test -f "$abs_srcdir/ext/msgpack/php_msgpack.h"; then + msgpack_inc_path="$abs_srcdir" + elif test -f "$phpincludedir/ext/msgpack/php_msgpack.h"; then + msgpack_inc_path="$phpincludedir" + else + for i in php php7; do + if test -f "$prefix/include/$i/ext/msgpack/php_msgpack.h"; then + msgpack_inc_path="$prefix/include/$i" + fi + done + fi + + if test "$msgpack_inc_path" = ""; then + AC_MSG_ERROR([Cannot find php_msgpack.h]) + else + AC_MSG_RESULT([$msgpack_inc_path]) + fi + fi + + AC_MSG_CHECKING([for redis msgpack support]) + if test "$PHP_REDIS_MSGPACK" != "no"; then + AC_MSG_RESULT([enabled]) + AC_DEFINE(HAVE_REDIS_MSGPACK,1,[Whether redis msgpack serializer is enabled]) + MSGPACK_INCLUDES="-I$msgpack_inc_path" + MSGPACK_EXT_DIR="$msgpack_inc_path/ext" + ifdef([PHP_ADD_EXTENSION_DEP], + [ + PHP_ADD_EXTENSION_DEP(redis, msgpack) + ]) + PHP_ADD_INCLUDE($MSGPACK_EXT_DIR) + else + MSGPACK_INCLUDES="" + AC_MSG_RESULT([disabled]) + fi + if test "$PHP_REDIS_LZF" != "no"; then AC_DEFINE(HAVE_REDIS_LZF, 1, [ ]) if test "$PHP_LIBLZF" != "no"; then @@ -105,53 +188,6 @@ dnl Check for igbinary AC_DEFINE_UNQUOTED(GIT_REVISION, ["$(git log -1 --format=%H)"], [ ]) fi -dnl Check for msgpack - if test "$PHP_REDIS_MSGPACK" != "no"; then - AC_MSG_CHECKING([for msgpack includes]) - msgpack_inc_path="" - - if test -f "$abs_srcdir/include/php/ext/msgpack/php_msgpack.h"; then - msgpack_inc_path="$abs_srcdir/include/php" - elif test -f "$abs_srcdir/ext/msgpack/php_msgpack.h"; then - msgpack_inc_path="$abs_srcdir" - elif test -f "$phpincludedir/ext/msgpack/php_msgpack.h"; then - msgpack_inc_path="$phpincludedir" - else - for i in php php7; do - if test -f "$prefix/include/$i/ext/msgpack/php_msgpack.h"; then - msgpack_inc_path="$prefix/include/$i" - fi - done - fi - - if test "$msgpack_inc_path" = ""; then - AC_MSG_ERROR([Cannot find php_msgpack.h]) - else - AC_MSG_RESULT([$msgpack_inc_path]) - fi - fi - - AC_MSG_CHECKING([for redis msgpack support]) - if test "$PHP_REDIS_MSGPACK" != "no"; then - msgpack_version=`grep -o 'PHP_MSGPACK_VERSION "[0-9\.]\+"' $msgpack_inc_path/ext/msgpack/php_msgpack.h | awk '{print $2}' | tr -d '"'` - if expr $msgpack_version "<" "2.0.3" > /dev/null; then - AC_MSG_ERROR([msgpack 2.0.3 or greater required]) - else - AC_MSG_RESULT([enabled]) - AC_DEFINE(HAVE_REDIS_MSGPACK,1,[Whether redis msgpack serializer is enabled]) - MSGPACK_INCLUDES="-I$msgpack_inc_path" - MSGPACK_EXT_DIR="$msgpack_inc_path/ext" - ifdef([PHP_ADD_EXTENSION_DEP], - [ - PHP_ADD_EXTENSION_DEP(redis, msgpack) - ]) - PHP_ADD_INCLUDE($MSGPACK_EXT_DIR) - fi - else - MSGPACK_INCLUDES="" - AC_MSG_RESULT([disabled]) - fi - dnl # --with-redis -> check with-path dnl SEARCH_PATH="/usr/local /usr" # you might want to change this dnl SEARCH_FOR="/include/redis.h" # you most likely want to change this diff --git a/library.c b/library.c index a4637b7ef0..d757fad6f5 100644 --- a/library.c +++ b/library.c @@ -25,7 +25,11 @@ #include "php_redis.h" #include "library.h" #include "redis_commands.h" + +#ifdef HAVE_REDIS_JSON #include +#endif + #include #define UNSERIALIZE_NONE 0 @@ -2316,11 +2320,14 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len #endif break; case REDIS_SERIALIZER_JSON: +#ifdef HAVE_REDIS_JSON php_json_encode(&sstr, z, PHP_JSON_OBJECT_AS_ARRAY); *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); *val_len = ZSTR_LEN(sstr.s); smart_str_free(&sstr); return 1; +#endif + break; EMPTY_SWITCH_DEFAULT_CASE() } @@ -2385,12 +2392,14 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, #endif break; case REDIS_SERIALIZER_JSON: -#if PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION < 1 +#ifdef HAVE_REDIS_JSON + #if (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION < 1) JSON_G(error_code) = PHP_JSON_ERROR_NONE; php_json_decode(z_ret, (char*)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); ret = JSON_G(error_code) == PHP_JSON_ERROR_NONE; -#else + #else ret = !php_json_decode(z_ret, (char *)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); + #endif #endif break; EMPTY_SWITCH_DEFAULT_CASE() diff --git a/redis.c b/redis.c index 15a5038e47..84139e8d27 100644 --- a/redis.c +++ b/redis.c @@ -477,6 +477,12 @@ static const zend_module_dep redis_deps[] = { #ifdef HAVE_REDIS_IGBINARY ZEND_MOD_REQUIRED("igbinary") #endif +#ifdef HAVE_REDIS_MSGPACK + ZEND_MOD_REQUIRED("msgpack") +#endif +#ifdef HAVE_REDIS_JSON + ZEND_MOD_REQUIRED("json") +#endif #ifdef PHP_SESSION ZEND_MOD_REQUIRED("session") #endif @@ -809,18 +815,36 @@ PHP_MSHUTDOWN_FUNCTION(redis) return SUCCESS; } -static const char *get_available_serializers(void) { -#ifdef HAVE_REDIS_IGBINARY - #ifdef HAVE_REDIS_MSGPACK - return "php, igbinary, msgpack"; +static const char * +get_available_serializers(void) +{ +#ifdef HAVE_REDIS_JSON + #ifdef HAVE_REDIS_IGBINARY + #ifdef HAVE_REDIS_MSGPACK + return "php, json, igbinary, msgpack"; + #else + return "php, json, igbinary"; + #endif #else - return "php, igbinary"; + #ifdef HAVE_REDIS_MSGPACK + return "php, json, msgpack"; + #else + return "php, json"; + #endif #endif #else - #ifdef HAVE_REDIS_MSGPACK - return "php, msgpack"; + #ifdef HAVE_REDIS_IGBINARY + #ifdef HAVE_REDIS_MSGPACK + return "php, igbinary, msgpack"; + #else + return "php, igbinary"; + #endif #else - return "php"; + #ifdef HAVE_REDIS_MSGPACK + return "php, msgpack"; + #else + return "php"; + #endif #endif #endif } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 5f2423e7de..6e3614365f 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -981,8 +981,8 @@ public function testSortPrefix() { $this->redis->sadd('some-item', 2); $this->redis->sadd('some-item', 3); - $this->assertEquals(['1','2','3'], $this->redis->sortAsc('some-item')); - $this->assertEquals(['3','2','1'], $this->redis->sortDesc('some-item')); + $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 @@ -992,36 +992,31 @@ public function testSortPrefix() { public function testSortAsc() { $this->setupSort(); - $this->assertTrue(FALSE === $this->redis->sortAsc(NULL)); - // sort by age and get IDs $byAgeAsc = ['3','1','2','4']; - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*')); + $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->sortAsc('person:id', NULL)); // check that NULL works. - $this->assertEquals(['1', '2', '3', '4'], $this->redis->sortAsc('person:id', NULL, NULL)); // for all fields. + $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->sortAsc('person:id', 'person:age_*', 'person:name_*')); + $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($byAgeAsc, 0, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 2)); + $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2]])); $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'asc'])); - $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 1, 2)); + $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2]])); $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'asc'])); - $this->assertEquals(array_slice($byAgeAsc, 0, 3), $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, 3)); // NULL is transformed to 0 if there is something after it. - $this->assertEquals($byAgeAsc, $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', 0, 4)); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 4]])); $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]])); - $this->assertEquals([], $this->redis->sortAsc('person:id', 'person:age_*', 'person:name_*', NULL, NULL)); // NULL, NULL is the same as (0,0). That returns no element. // sort by salary and get ages $agesBySalaryAsc = ['34', '27', '25', '41']; - $this->assertEquals($agesBySalaryAsc, $this->redis->sortAsc('person:id', 'person:salary_*', 'person:age_*')); + $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']); @@ -1037,7 +1032,7 @@ public function testSortAsc() { // SORT list → [ghi, def, abc] if (version_compare($this->version, "2.5.0", "lt")) { - $this->assertEquals(array_reverse($list), $this->redis->sortAsc('list')); + $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: @@ -1046,7 +1041,7 @@ public function testSortAsc() { } // SORT list ALPHA → [abc, def, ghi] - $this->assertEquals($list, $this->redis->sortAscAlpha('list')); + $this->assertEquals($list, $this->redis->sort('list', ['alpha' => TRUE])); $this->assertEquals($list, $this->redis->sort('list', ['sort' => 'asc', 'alpha' => TRUE])); } @@ -1056,18 +1051,18 @@ public function testSortDesc() { // sort by age and get IDs $byAgeDesc = ['4','2','1','3']; - $this->assertEquals($byAgeDesc, $this->redis->sortDesc('person:id', 'person:age_*')); + $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->sortDesc('person:id', 'person:age_*', 'person:name_*')); + $this->assertEquals($byAgeDesc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'desc'])); - $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 0, 2)); - $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sortDesc('person:id', 'person:age_*', 'person:name_*', 1, 2)); + $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'desc'])); + $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'desc'])); // sort by salary and get ages $agesBySalaryDesc = ['41', '25', '27', '34']; - $this->assertEquals($agesBySalaryDesc, $this->redis->sortDesc('person:id', 'person:salary_*', 'person:age_*')); + $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']; @@ -1078,7 +1073,7 @@ public function testSortDesc() { // SORT list → [ghi, abc, def] if (version_compare($this->version, "2.5.0", "lt")) { - $this->assertEquals(array_reverse($list), $this->redis->sortDesc('list')); + $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 @@ -1086,7 +1081,7 @@ public function testSortDesc() { } // SORT list ALPHA → [abc, def, ghi] - $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sortDescAlpha('list')); + $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sort('list', ['sort' => 'desc', 'alpha' => TRUE])); } // LINDEX @@ -6314,7 +6309,7 @@ private function getPhpCommand($script) static $cmd = NULL; if (!$cmd) { - $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: (defined('PHP_BINARY') ? PHP_BINARY : 'php')); // PHP_BINARY is 5.4+ + $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY); if ($test_args = getenv('TEST_PHP_ARGS')) { $cmd .= ' '; @@ -6324,9 +6319,13 @@ private function getPhpCommand($script) $result = shell_exec("$cmd --no-php-ini -m"); $redis = strpos($result, 'redis') !== false; $igbinary = strpos($result, 'igbinary') !== false; + $msgpack = strpos($result, 'msgpack') !== false; - if (!$redis || !$igbinary) { + if (!$redis || !$igbinary || !$msgpack) { $cmd .= ' --no-php-ini'; + if (!$msgpack) { + $cmd .= ' --define extension=msgpack.so'; + } if (!$igbinary) { $cmd .= ' --define extension=igbinary.so'; } From 6973478c3214208fe3fdac5aa088cacd802011fc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 21 Jun 2019 09:27:21 -0700 Subject: [PATCH 1198/1986] Require msgpack > 2.0.3 Although the msgpack version numbers appear to be x.y.z[-dev], I'm testing as if it were w.x.y.z[-dev] in case this ever changes. Right now the $4 is the same as adding 0. --- config.m4 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config.m4 b/config.m4 index 63a59b2e5d..433f25b19b 100644 --- a/config.m4 +++ b/config.m4 @@ -137,6 +137,13 @@ if test "$PHP_REDIS" != "no"; then AC_MSG_CHECKING([for redis msgpack support]) if test "$PHP_REDIS_MSGPACK" != "no"; then + AC_MSG_CHECKING([for php_msgpack version >= 2.0.3]) + MSGPACK_VERSION=`$EGREP "define PHP_MSGPACK_VERSION" $msgpack_inc_path/ext/msgpack/php_msgpack.h | $SED -e 's/[[^0-9\.]]//g'` + AC_MSG_RESULT([$MSGPACK_VERSION]) + if test `echo $MSGPACK_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*1000 + $2*100 + $3*10 + $4}'` -lt 2030; then + AC_MSG_ERROR([php msgpack version >= 2.0.3 required]) + fi + AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_REDIS_MSGPACK,1,[Whether redis msgpack serializer is enabled]) MSGPACK_INCLUDES="-I$msgpack_inc_path" From aa110feb8206aa514889d588c03b71f44ec70b76 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 21 Jun 2019 11:19:19 -0700 Subject: [PATCH 1199/1986] Cleanup version check output --- config.m4 | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/config.m4 b/config.m4 index 433f25b19b..24fa554d97 100644 --- a/config.m4 +++ b/config.m4 @@ -135,16 +135,15 @@ if test "$PHP_REDIS" != "no"; then fi fi - AC_MSG_CHECKING([for redis msgpack support]) if test "$PHP_REDIS_MSGPACK" != "no"; then - AC_MSG_CHECKING([for php_msgpack version >= 2.0.3]) + AC_MSG_CHECKING([for php msgpack version >= 2.0.3]) MSGPACK_VERSION=`$EGREP "define PHP_MSGPACK_VERSION" $msgpack_inc_path/ext/msgpack/php_msgpack.h | $SED -e 's/[[^0-9\.]]//g'` - AC_MSG_RESULT([$MSGPACK_VERSION]) if test `echo $MSGPACK_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*1000 + $2*100 + $3*10 + $4}'` -lt 2030; then - AC_MSG_ERROR([php msgpack version >= 2.0.3 required]) + AC_MSG_ERROR([version $MSGPACK_VERSION is too old]) + else + AC_MSG_RESULT([yes]) fi - AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_REDIS_MSGPACK,1,[Whether redis msgpack serializer is enabled]) MSGPACK_INCLUDES="-I$msgpack_inc_path" MSGPACK_EXT_DIR="$msgpack_inc_path/ext" @@ -155,7 +154,6 @@ if test "$PHP_REDIS" != "no"; then PHP_ADD_INCLUDE($MSGPACK_EXT_DIR) else MSGPACK_INCLUDES="" - AC_MSG_RESULT([disabled]) fi if test "$PHP_REDIS_LZF" != "no"; then From a537df8321d4a42f5af7d4cd61c72d3b0e5a01cf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 21 Jun 2019 11:38:36 -0700 Subject: [PATCH 1200/1986] Bikeshedding error message --- config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 24fa554d97..512fc61570 100644 --- a/config.m4 +++ b/config.m4 @@ -139,7 +139,7 @@ if test "$PHP_REDIS" != "no"; then AC_MSG_CHECKING([for php msgpack version >= 2.0.3]) MSGPACK_VERSION=`$EGREP "define PHP_MSGPACK_VERSION" $msgpack_inc_path/ext/msgpack/php_msgpack.h | $SED -e 's/[[^0-9\.]]//g'` if test `echo $MSGPACK_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*1000 + $2*100 + $3*10 + $4}'` -lt 2030; then - AC_MSG_ERROR([version $MSGPACK_VERSION is too old]) + AC_MSG_ERROR([version >= 2.0.3 required]) else AC_MSG_RESULT([yes]) fi From 55c5586c9c54f09bd91f53bbc27b143f36af8b04 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 27 Jun 2019 00:38:36 +0200 Subject: [PATCH 1201/1986] Remove HAVE_SPL The HAVE_SPL symbol is defined in PHP to indicate the presence of the spl extension. Since PHP 5.3 the spl extension is always availabe and since PHP-7.4 the HAVE_SPL symbol has also been removed. --- redis.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/redis.c b/redis.c index 84139e8d27..3cc13f52d4 100644 --- a/redis.c +++ b/redis.c @@ -772,9 +772,7 @@ PHP_MINIT_FUNCTION(redis) module_number); /* Base Exception class */ -#if HAVE_SPL exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1); -#endif if (exception_ce == NULL) { exception_ce = zend_exception_get_default(TSRMLS_C); } From 97490a30640d1134310c72cf5ce7ca91e9a385bf Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 2 Jul 2019 09:32:10 +0300 Subject: [PATCH 1202/1986] Back to dev --- package.xml | 159 +++++++++++++++++++++++++++++++++++++++------------- php_redis.h | 2 +- 2 files changed, 121 insertions(+), 40 deletions(-) diff --git a/package.xml b/package.xml index c331de776b..f1f72130da 100644 --- a/package.xml +++ b/package.xml @@ -25,12 +25,12 @@ http://pear.php.net/dtd/package-2.0.xsd"> Nicolas Favre-Felix nff n.favrefelix@gmail.com - yes + no - 2018-11-18 + 2019-07-02 - 4.2.0 - 4.2.0 + 5.0.0 + 5.0.0 stable @@ -38,41 +38,42 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP - phpredis 4.2.0 - - The main feature of this release is new Streams API implemented by Michael Grunder. - - Note: There are no changes between 4.2.0RC3 and 4.2.0. - - 4.2.0RC3: - - * Optimize close method [2a1ef961] (fanjiapeng) - * Prevent potential infinite loop for sessions [4e2de158] (Pavlo Yatsukhnenko) - * Fix coverty warnings [6f7ddd27] (Pavlo Yatsukhnenko) - * Fix session memory leaks [071a1d54, 92f14b14] (Pavlo Yatsukhnenko, Michael Grunder) - * Fix XCLAIM on 32-bit installs [18dc2aac] (Michael Grunder) - * Build warning fixes [b5093910, 51027044, 8b0f28cd] (Pavlo Yatsukhnenko, Remi Collet, twosee) - - 4.2.0RC2: - - * Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce] (Pavlo Yatsukhnenko) - * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) - * Treat a -1 response from cluster_check_response as a timeout. [27df9220, 07ef7f4e, d1172426] (Michael Grunder) - * Use a ZSET insted of SET for EVAL tests [2e412373] (Michael Grunder) - * Missing space between command and args [0af2a7fe] (@remicollet) - - 4.2.0RC1: - - * Streams API [2c9e0572] (Michael Grunder) - * Reset the socket after a timeout to make sure no wrong data is received [cd6ebc6d] (@marcdejonge) - * Modify session testing logic [bfd27471] (Michael Grunder) - * Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex [d4a08697] (Michael Grunder) - * Fix printf format warnings [dcde9331] (Pavlo Yatsukhnenko) - * Session module is required [58bd8cc8] (@remicollet) - * Set default values for ini entries [e206ce9c] (Pavlo Yatsukhnenko) - * Display ini entries in output of phpinfo [908ac4b3] (Pavlo Yatsukhnenko) - * Persistant connections can be closed via close method + change reconnection logic [1d997873] (Pavlo Yatsukhnenko) - * Documentation improvements (@mg, @elcheco, @lucascourot, @nolimitdev, Michael Grunder) + This release contains important improvements and breaking changes. + The most interesting are: drop PHP5 support, RedisCluster slots caching, + JSON and msgpack serializers, soft deprecation of non-Redis commands. + + phpredis 5.0.0 + + * Remove HAVE_SPL [55c5586c] (@petk) + * Update Fedora installation instructions [90aa067c] (@remicollet) + + phpredis 5.0.0RC2 + + * Allow compilation without JSON serialization enabled and fixes for deprecated + helper methods. [235a27] (Pavlo Yatsukhnenko) + * Fix php msgpack >= 2.0.3 version requirement. [6973478..a537df8] (Michael Grunder) + + phpredis 5.0.0RC1 + + * Enable connection pooling by default [8206b147] (Pavlo Yatsukhnenko) + * Soft deprecate methods that aren't actually Redis commands [a81b4f2d, 95c8aab9] (Pavlo Yatsukhnenko, Michael Grunder) + * Enable pooling for cluster slave nodes [17600dd1] (Michael Grunder) + * xInfo response format [4852a510, ac9dca0a] (Pavlo Yatsukhnenko) + * Make the XREADGROUP optional COUNT and BLOCK arguments nullable [0c17bd27] (Michael Grunder) + * Allow PING to take an optional argument [6e494170] (Michael Grunder) + * Allow ZRANGE to be called either with `true` or `['withscores' => true]` [19f3efcf] (Michael Grunder) + * Allow to specify server address as schema://host [418428fa] (Pavlo Yatsukhnenko) + * Allow persistent_id to be passed as NULL with strict_types enabled [60223762] (Michael Grunder) + * Add server address to exception message [e8fb49be, 34d6403d] (Pavlo Yatsukhnenko) + * Adds OPT_REPLY_LITERAL for rawCommand and EVAL [5cb30fb2] (Michael Grunder) + * JSON serializer [98bd2886, 96c57139] (Pavlo Yatsukhnenko, Michael Grunder) + * Add support for STREAM to the type command [d7450b2f, 068ce978, 8a45d18c] (Michael Grunder, Pavlo Yatsukhnenko) + * Fix TypeError when using built-in constants in `setOption` [4c7643ee] (@JoyceBabu) + * Handle references in MGET [60d8b679] (Michael Grunder) + * msgpack serializer [d5b8f833, 545250f3, 52bae8ab] (@bgort, Pavlo Yatsukhnenko, Michael Grunder) + * Add Cluster slots caching [9f0d7bc0, ea081e05] (Michael Grunder) + * Drop PHP5 support [f9928642, 46a50c12, 4601887d, 6ebb36ce, fdbe9d29] (Michael Grunder) + * Documentation improvements (@alexander-schranz, @cookieguru, Pavlo Yatsukhnenko, Michael Grunder) @@ -141,6 +142,86 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + stablestable + 5.0.05.0.0 + 2019-07-02 + + This release contains important improvements and breaking changes. + The most interesting are: drop PHP5 support, RedisCluster slots caching, + JSON and msgpack serializers, soft deprecation of non-Redis commands. + + phpredis 5.0.0 + + * Remove HAVE_SPL [55c5586c] (@petk) + * Update Fedora installation instructions [90aa067c] (@remicollet) + + phpredis 5.0.0RC2 + + * Allow compilation without JSON serialization enabled and fixes for deprecated + helper methods. [235a27] (Pavlo Yatsukhnenko) + * Fix php msgpack >= 2.0.3 version requirement. [6973478..a537df8] (Michael Grunder) + + phpredis 5.0.0RC1 + + * Enable connection pooling by default [8206b147] (Pavlo Yatsukhnenko) + * Soft deprecate methods that aren't actually Redis commands [a81b4f2d, 95c8aab9] (Pavlo Yatsukhnenko, Michael Grunder) + * Enable pooling for cluster slave nodes [17600dd1] (Michael Grunder) + * xInfo response format [4852a510, ac9dca0a] (Pavlo Yatsukhnenko) + * Make the XREADGROUP optional COUNT and BLOCK arguments nullable [0c17bd27] (Michael Grunder) + * Allow PING to take an optional argument [6e494170] (Michael Grunder) + * Allow ZRANGE to be called either with `true` or `['withscores' => true]` [19f3efcf] (Michael Grunder) + * Allow to specify server address as schema://host [418428fa] (Pavlo Yatsukhnenko) + * Allow persistent_id to be passed as NULL with strict_types enabled [60223762] (Michael Grunder) + * Add server address to exception message [e8fb49be, 34d6403d] (Pavlo Yatsukhnenko) + * Adds OPT_REPLY_LITERAL for rawCommand and EVAL [5cb30fb2] (Michael Grunder) + * JSON serializer [98bd2886, 96c57139] (Pavlo Yatsukhnenko, Michael Grunder) + * Add support for STREAM to the type command [d7450b2f, 068ce978, 8a45d18c] (Michael Grunder, Pavlo Yatsukhnenko) + * Fix TypeError when using built-in constants in `setOption` [4c7643ee] (@JoyceBabu) + * Handle references in MGET [60d8b679] (Michael Grunder) + * msgpack serializer [d5b8f833, 545250f3, 52bae8ab] (@bgort, Pavlo Yatsukhnenko, Michael Grunder) + * Add RedisCluster slots caching [9f0d7bc0, ea081e05] (Michael Grunder) + * Drop PHP5 support [f9928642, 46a50c12, 4601887d, 6ebb36ce, fdbe9d29] (Michael Grunder) + * Documentation improvements (@alexander-schranz, @cookieguru, Pavlo Yatsukhnenko, Michael Grunder) + + + + + stablestable + 4.3.04.3.0 + 2019-03-13 + + phpredis 4.3.0 + + This is probably the latest release with PHP 5 suport!!! + + * Proper persistent connections pooling implementation [a3703820, c76e00fb, 0433dc03, c75b3b93] (Pavlo Yatsukhnenko) + * RedisArray auth [b5549cff, 339cfa2b, 6b411aa8] (Pavlo Yatsukhnenko) + * Use zend_string for storing key hashing algorithm [8cd165df, 64e6a57f] (Pavlo Yatsukhnenko) + * Add ZPOPMAX and ZPOPMIN support [46f03561, f89e941a, 2ec7d91a] (@mbezhanov, Michael Grunder) + * Implement GEORADIUS_RO and GEORADIUSBYMEMBER_RO [22d81a94] (Michael Grunder) + * Add callback parameter to subscribe/psubscribe arginfo [0653ff31] (Pavlo Yatsukhnenko) + * Don't check the number affected keys in PS_UPDATE_TIMESTAMP_FUNC [b00060ce] (Pavlo Yatsukhnenko) + * Xgroup updates [15995c06] (Michael Grunder) + * RedisCluster auth [c5994f2a] (Pavlo Yatsukhnenko) + * Cancel pipeline mode without executing commands [789256d7] (Pavlo Yatsukhnenko) + * Use zend_string for pipeline_cmd [e98f5116] (Pavlo Yatsukhnenko) + * Different key hashing algorithms from hash extension [850027ff] (Pavlo Yatsukhnenko) + * Breaking the lock acquire loop in case of network problems [61889cd7] (@SkydiveMarius) + * Implement consistent hashing algorithm for RedisArray [bb32e6f3, 71922bf1] (Pavlo Yatsukhnenko) + * Use zend_string for storing RedisArray hosts [602740d3, 3e7e1c83] (Pavlo Yatsukhnenko) + * Update lzf_compress to be compatible with PECL lzf extension [b27fd430] (@jrchamp) + * Fix RedisCluster keys memory leak [3b56b7db] (Michael Grunder) + * Directly use return_value in RedisCluster::keys method [ad10a49e] (Pavlo Yatsukhnenko) + * Fix segfault in Redis Cluster with inconsistent configuration [72749916, 6e455e2e] (Pavlo Yatsukhnenko) + * Masters info leakfix [91bd7426] (Michael Grunder) + * Refactor redis_sock_read_bulk_reply [bc4dbc4b] (Pavlo Yatsukhnenko) + * Remove unused parameter lazy_connect from redis_sock_create [c0793e8b] (Pavlo Yatsukhnenko) + * Remove useless ZEND_ACC_[C|D]TOR. [bc9b5597] (@twosee) + * Documentation improvements (@fanjiapeng, @alexander-schranz, @hmc, Pavlo Yatsukhnenko, Michael Grunder) + + + betabeta 4.2.0RC34.2.0RC3 diff --git a/php_redis.h b/php_redis.h index 0be45c0a2a..07c2e253aa 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "4.2.0" +#define PHP_REDIS_VERSION "5.1.0-dev" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 8f1ed94f4618bfaad20ea69683ec1575540d88f2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 2 Jul 2019 14:39:00 +0300 Subject: [PATCH 1203/1986] Add Changelog All Changes should be added to Changelog. --- Changelog | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Changelog diff --git a/Changelog b/Changelog new file mode 100644 index 0000000000..e69de29bb2 From 484b75ae127839d897a36115c60220caaae18744 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Thu, 4 Jul 2019 19:12:10 -0700 Subject: [PATCH 1204/1986] Issue.1588 populate changelog (#1590) Add a Markdown Changelog --- Changelog | 0 Changelog.md | 554 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 554 insertions(+) delete mode 100644 Changelog create mode 100644 Changelog.md diff --git a/Changelog b/Changelog deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000000..7fae96f0a3 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,554 @@ +# Changelog + +All changes to phpredis will be documented in this file. + +We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and PhpRedis adhears to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [5.0.0] - 2019-07-02 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.0.0), [PECL](https://pecl.php.net/package/redis/5.0.0)) + +This release contains important improvements and breaking changes. The most +interesting are: drop PHP5 support, RedisCluster slots caching, JSON and msgpack +serializers, soft deprecation of non-Redis commands. + +### Breaking Changes + +- [Nullable xReadGroup COUNT and BLOCK arguments](#brk500-xreadgroup) +- [RedisArray exception now includes host information](#brk500-exception-host) +- [zRange now conforms to zRangeByScore to get scores](#brk500-zrange-withscores) +- [ping can now take an argument](#brk500-ping-argument) + +### Added +- Adds OPT_REPLY_LITERAL for rawCommand and EVAL [5cb30fb2](https://www.github.com/phpredis/phpredis/commit/5cb30fb2) + ([Michael Grunder](https://github.com/michael-grunder)) +- JSON serializer [98bd2886](https://www.github.com/phpredis/phpredis/commit/98bd2886), + [96c57139](https://www.github.com/phpredis/phpredis/commit/96c57139), + [235a27](https://www.github.com/phpredis/phpredis/commit/235a27) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) +- msgpack serializer [d5b8f833](https://www.github.com/phpredis/phpredis/commit/d5b8f833), + [545250f3](https://www.github.com/phpredis/phpredis/commit/545250f3), + [52bae8ab](https://www.github.com/phpredis/phpredis/commit/52bae8ab) + ([@bgort](https://github.com/bgort), [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), + [Michael Grunder](https://github.com/michael-grunder)) +- Add support for STREAM to the type command [d7450b2f](https://www.github.com/phpredis/phpredis/commit/d7450b2f), + [068ce978](https://www.github.com/phpredis/phpredis/commit/068ce978), [8a45d18c](https://www.github.com/phpredis/phpredis/commit/8a45d18c) + ([Michael Grunder](https://github.com/michael-grunder), [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add Cluster slots caching [9f0d7bc0](https://www.github.com/phpredis/phpredis/commit/9f0d7bc0), + [ea081e05](https://www.github.com/phpredis/phpredis/commit/ea081e05) ([Michael Grunder](https://github.com/michael-grunder)) + +### Changed + +- Add server address to exception message [e8fb49be](https://www.github.com/phpredis/phpredis/commit/e8fb49be), + [34d6403d](https://www.github.com/phpredis/phpredis/commit/34d6403d) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow to specify server address as `schema://host` [418428fa](https://www.github.com/phpredis/phpredis/commit/418428fa) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)). +- Update Fedora installation instructions [90aa067c](https://www.github.com/phpredis/phpredis/commit/90aa067c) + ([@remicollet](https://github.com/remicollet)) +- Enable connection pooling by default [8206b147](https://www.github.com/phpredis/phpredis/commit/8206b147) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow PING to take an optional argument [6e494170](https://www.github.com/phpredis/phpredis/commit/6e494170) + ([Michael Grunder](https://github.com/michael-grunder)) +- Allow ZRANGE to be called either with `true` or `['withscores' => true]` + [19f3efcf](https://www.github.com/phpredis/phpredis/commit/19f3efcf) ([Michael Grunder](https://github.com/michael-grunder)) +- Documentation improvements ([@alexander-schranz](https://github.com/alexander-schranz), [@cookieguru](https://github.com/cookieguru), + [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) + +### Deprecated + +- Soft deprecate methods that aren't actually Redis commands [a81b4f2d](https://www.github.com/phpredis/phpredis/commit/a81b4f2d), + [95c8aab9](https://www.github.com/phpredis/phpredis/commit/95c8aab9), [235a27](https://www.github.com/phpredis/phpredis/commit/235a27) ([@michael-grunder](https://github.com/michael-grunder), [@yatsukhnenko](https://github.com/weltling)) +- Remove HAVE_SPL define [[55c5586c](https://www.github.com/phpredis/phpredis/commit/55c5586c)] ([@petk](https://github.com/petk)) + +### Removed + +- Drop PHP5 support [[f9928642](https://www.github.com/phpredis/phpredis/commit/f9928642), [46a50c12](https://www.github.com/phpredis/phpredis/commit/46a50c12), [4601887d](https://www.github.com/phpredis/phpredis/commit/4601887d), [6ebb36ce](https://www.github.com/phpredis/phpredis/commit/6ebb36ce), [fdbe9d29](https://www.github.com/phpredis/phpredis/commit/fdbe9d29)] (Michael + Grunder) + +### Fixed + +- Reworked PHP msgpack >= 2.0.3 version requirement. [6973478](https://www.github.com/phpredis/phpredis/commit/6973478)..[a537df8](https://www.github.com/phpredis/phpredis/commit/a537df8) + ([@michael-grunder](https://github.com/michael-grunder)). +- Enable pooling for cluster slave nodes [17600dd1](https://www.github.com/phpredis/phpredis/commit/17600dd1) ([Michael Grunder](https://github.com/michael-grunder)) +- xInfo response format [4852a510](https://www.github.com/phpredis/phpredis/commit/4852a510), [ac9dca0a](https://www.github.com/phpredis/phpredis/commit/ac9dca0a) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Make the XREADGROUP optional COUNT and BLOCK arguments nullable + [0c17bd27](https://www.github.com/phpredis/phpredis/commit/0c17bd27) + ([Michael Grunder](https://github.com/michael-grunder)) +- Allow persistent_id to be passed as NULL with strict_types enabled [60223762](https://www.github.com/phpredis/phpredis/commit/60223762) + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix TypeError when using built-in constants in `setOption` [4c7643ee](https://www.github.com/phpredis/phpredis/commit/4c7643ee) + ([@JoyceBabu](https://github.com/JoyceBabu)) +- Handle references in MGET [60d8b679](https://www.github.com/phpredis/phpredis/commit/60d8b679) ([Michael Grunder](https://github.com/michael-grunder)) + +--- + +## [4.3.0] - 2019-03-13 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/4.3.0), [PECL](https://pecl.php.net/package/redis/4.3.0)) + +This is probably the latest release with PHP 5 suport!!! + +### Added + +- RedisArray auth [b5549cff](https://www.github.com/phpredis/phpredis/commit/b5549cff), [339cfa2b](https://www.github.com/phpredis/phpredis/commit/339cfa2b), + [6b411aa8](https://www.github.com/phpredis/phpredis/commit/6b411aa8) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add ZPOPMAX and ZPOPMIN support [46f03561](https://www.github.com/phpredis/phpredis/commit/46f03561), + [f89e941a](https://www.github.com/phpredis/phpredis/commit/f89e941a), + [2ec7d91a](https://www.github.com/phpredis/phpredis/commit/2ec7d91a) (@mbezhanov, [Michael Grunder](https://github.com/michael-grunder)) +- Implement GEORADIUS_RO and GEORADIUSBYMEMBER_RO [22d81a94](https://www.github.com/phpredis/phpredis/commit/22d81a94) ([Michael Grunder](https://github.com/michael-grunder)) +- RedisCluster auth [c5994f2a](https://www.github.com/phpredis/phpredis/commit/c5994f2a) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Different key hashing algorithms from hash extension [850027ff](https://www.github.com/phpredis/phpredis/commit/850027ff) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Changed + +- Proper persistent connections pooling implementation [a3703820](https://www.github.com/phpredis/phpredis/commit/a3703820), + [c76e00fb](https://www.github.com/phpredis/phpredis/commit/c76e00fb), [0433dc03](https://www.github.com/phpredis/phpredis/commit/0433dc03), + [c75b3b93](https://www.github.com/phpredis/phpredis/commit/c75b3b93) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use zend_string for storing key hashing algorithm [8cd165df](https://www.github.com/phpredis/phpredis/commit/8cd165df), + [64e6a57f](https://www.github.com/phpredis/phpredis/commit/64e6a57f), [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) + +- Add callback parameter to subscribe/psubscribe arginfo [0653ff31](https://www.github.com/phpredis/phpredis/commit/0653ff31), + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Xgroup updates [15995c06](https://www.github.com/phpredis/phpredis/commit/15995c06) ([Michael Grunder](https://github.com/michael-grunder)) +- Use zend_string for pipeline_cmd [e98f5116](https://www.github.com/phpredis/phpredis/commit/e98f5116) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Implement consistent hashing algorithm for RedisArray [bb32e6f3](https://www.github.com/phpredis/phpredis/commit/bb32e6f3), [71922bf1](https://www.github.com/phpredis/phpredis/commit/71922bf1) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use zend_string for storing RedisArray hosts [602740d3](https://www.github.com/phpredis/phpredis/commit/602740d3), + [3e7e1c83](https://www.github.com/phpredis/phpredis/commit/3e7e1c83) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor redis_sock_read_bulk_reply [bc4dbc4b](https://www.github.com/phpredis/phpredis/commit/bc4dbc4b) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Fixed + +- Don't check the number affected keys in PS_UPDATE_TIMESTAMP_FUNC [b00060ce](https://www.github.com/phpredis/phpredis/commit/b00060ce) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Cancel pipeline mode without executing commands [789256d7](https://www.github.com/phpredis/phpredis/commit/789256d7) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Breaking the lock acquire loop in case of network problems [61889cd7](https://www.github.com/phpredis/phpredis/commit/61889cd7) + ([@SkydiveMarius](https://github.com/SkydiveMarius)) +- Update lzf_compress to be compatible with PECL lzf extension [b27fd430](https://www.github.com/phpredis/phpredis/commit/b27fd430) + ([@jrchamp](https://github.com/jrchamp)) +- Fix RedisCluster keys memory leak [3b56b7db](https://www.github.com/phpredis/phpredis/commit/3b56b7db) ([Michael Grunder](https://github.com/michael-grunder)) +- Directly use return_value in RedisCluster::keys method [ad10a49e](https://www.github.com/phpredis/phpredis/commit/ad10a49e) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix segfault in Redis Cluster with inconsistent configuration [72749916](https://www.github.com/phpredis/phpredis/commit/72749916), + [6e455e2e](https://www.github.com/phpredis/phpredis/commit/6e455e2e) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Masters info leakfix [91bd7426](https://www.github.com/phpredis/phpredis/commit/91bd7426) ([Michael Grunder](https://github.com/michael-grunder)) +- Remove unused parameter lazy_connect from redis_sock_create [c0793e8b](https://www.github.com/phpredis/phpredis/commit/c0793e8b) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Remove useless `ZEND_ACC_[C|D]TOR`. [bc9b5597](https://www.github.com/phpredis/phpredis/commit/bc9b5597) (@[twosee](https://github.com/twose)) +- Documentation improvements ([yulonghu](https://github.com/yulonghu), [@alexander-schranz](https://github.com/alexander-schranz), [@hmc](https://github.com/hmczju), + [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) + +--- + +## [4.2.0] - 2018-11-08 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/4.2.0), [PECL](https://pecl.php.net/package/redis/4.2.0)) + +The main feature of this release is new Streams API implemented by +[Michael Grunder](https://github.com/michael-grunder). + +### Added + +- Streams API [2c9e0572](https://www.github.com/phpredis/phpredis/commit/2c9e0572), [0b97ec37](https://www.github.com/phpredis/phpredis/commit/0b97ec37) ([Michael Grunder](https://github.com/michael-grunder)) +- Display ini entries in output of phpinfo [908ac4b3](https://www.github.com/phpredis/phpredis/commit/908ac4b3) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Persistant connections can be closed via close method + change reconnection + logic [1d997873](https://www.github.com/phpredis/phpredis/commit/1d997873) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Changed + +- Optimize close method [2a1ef961](https://www.github.com/phpredis/phpredis/commit/2a1ef961) ([yulonghu](https://github.com/yulonghu)) +- Use a ZSET insted of SET for EVAL tests [2e412373](https://www.github.com/phpredis/phpredis/commit/2e412373) ([Michael Grunder](https://github.com/michael-grunder)) +- Modify session testing logic [bfd27471](https://www.github.com/phpredis/phpredis/commit/bfd27471) ([Michael Grunder](https://github.com/michael-grunder)) +- Documentation improvements ([@michael-grunder](https://github.com/michael-grunder), [@elcheco](https://github.com/elcheco), [@lucascourot](https://github.com/lucascourot), [@nolimitdev](https://github.com/nolimitdev), + [Michael Grunder](https://github.com/michael-grunder)) + +### Fixed + +- Prevent potential infinite loop for sessions [4e2de158](https://www.github.com/phpredis/phpredis/commit/4e2de158) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix coverty warnings [6f7ddd27](https://www.github.com/phpredis/phpredis/commit/6f7ddd27) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix session memory leaks [071a1d54](https://www.github.com/phpredis/phpredis/commit/071a1d54), [92f14b14](https://www.github.com/phpredis/phpredis/commit/92f14b14) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), + [Michael Grunder](https://github.com/michael-grunder)) +- Fix XCLAIM on 32-bit installs [18dc2aac](https://www.github.com/phpredis/phpredis/commit/18dc2aac) ([Michael Grunder](https://github.com/michael-grunder)) +- Build warning fixes [b5093910](https://www.github.com/phpredis/phpredis/commit/b5093910), [51027044](https://www.github.com/phpredis/phpredis/commit/51027044), [8b0f28cd](https://www.github.com/phpredis/phpredis/commit/8b0f28cd) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), + [Remi Collet](https://github.com/remicollet), [twosee](https://github.com/twose)) +- Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce](https://www.github.com/phpredis/phpredis/commit/25b043ce) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Treat a -1 response from cluster_check_response as a timeout. [27df9220](https://www.github.com/phpredis/phpredis/commit/27df9220), + [07ef7f4e](https://www.github.com/phpredis/phpredis/commit/07ef7f4e), [d1172426](https://www.github.com/phpredis/phpredis/commit/d1172426) ([Michael Grunder](https://github.com/michael-grunder)). +- Missing space between command and args [0af2a7fe](https://www.github.com/phpredis/phpredis/commit/0af2a7fe) ([@remicollet](https://github.com/remicollet)) +- Reset the socket after a timeout to make sure no wrong data is received + [cd6ebc6d](https://www.github.com/phpredis/phpredis/commit/cd6ebc6d) ([@marcdejonge](https://github.com/marcdejonge)) +- Allow '-' and '+' arguments and add tests for zLexCount and zRemRangeByLex + [d4a08697](https://www.github.com/phpredis/phpredis/commit/d4a08697) ([Michael Grunder](https://github.com/michael-grunder)) +- Fix printf format warnings [dcde9331](https://www.github.com/phpredis/phpredis/commit/dcde9331) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Session module is required [58bd8cc8](https://www.github.com/phpredis/phpredis/commit/58bd8cc8) ([@remicollet](https://github.com/remicollet)) +- Set default values for ini entries [e206ce9c](https://www.github.com/phpredis/phpredis/commit/e206ce9c) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +--- + +## [4.0.0] - 2018-03-07 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/4.0.0), [PECL](https://pecl.php.net/package/redis/4.0.0)) + +*WARNING:* THIS RELEASE CONTAINS BREAKING API CHANGES! + +### Added + +- Add proper ARGINFO for all methods. ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) +- Let EXISTS take multiple keys [cccc39](https://www.github.com/phpredis/phpredis/commit/cccc39) ([Michael Grunder](https://github.com/michael-grunder)) +- Implement SWAPDB and UNLINK commands [84f1f28b](https://www.github.com/phpredis/phpredis/commit/84f1f28b), [9e65c429](https://www.github.com/phpredis/phpredis/commit/9e65c429) ([Michael Grunder](https://github.com/michael-grunder)) +- Add LZF compression (experimental) [e2c51251](https://www.github.com/phpredis/phpredis/commit/e2c51251), [8cb2d5bd](https://www.github.com/phpredis/phpredis/commit/8cb2d5bd), [8657557](https://www.github.com/phpredis/phpredis/commit/8657557) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow mixing MULTI and PIPELINE modes (experimental) [5874b0](https://www.github.com/phpredis/phpredis/commit/5874b0) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Changed + +- Use zend_string as returning value for ra_extract_key and ra_call_extractor + [9cd05911](https://www.github.com/phpredis/phpredis/commit/9cd05911) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Return real connection error as exception [5b9c0c60](https://www.github.com/phpredis/phpredis/commit/5b9c0c60) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), + [Michael Grunder](https://github.com/michael-grunder)) +- Use zend_string for storing auth and prefix members [4b8336f7](https://www.github.com/phpredis/phpredis/commit/4b8336f7) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add tcp_keepalive option to redis sock [68c58513](https://www.github.com/phpredis/phpredis/commit/68c58513), [5101172a](https://www.github.com/phpredis/phpredis/commit/5101172a), [010336d5](https://www.github.com/phpredis/phpredis/commit/010336d5), + [51e48729](https://www.github.com/phpredis/phpredis/commit/51e48729) ([@git-hulk](https://github.com/git-hulk), [Michael Grunder](https://github.com/michael-grunder)) +- More robust GEORADIUS COUNT validation [f7edee5d](https://www.github.com/phpredis/phpredis/commit/f7edee5d) ([Michael Grunder](https://github.com/michael-grunder)) +- Allow to use empty string as persistant_id [ec4fd1bd](https://www.github.com/phpredis/phpredis/commit/ec4fd1bd) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Documentation improvements ([Michael Grunder](https://github.com/michael-grunder), [@TomA-R](https://github.com/TomA-R)) + +### Fixed + +- Disallow using empty string as session name. [485db46f](https://www.github.com/phpredis/phpredis/commit/485db46f) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- The element of z_seeds may be a reference on php7 [367bc6aa](https://www.github.com/phpredis/phpredis/commit/367bc6aa), [1e63717a](https://www.github.com/phpredis/phpredis/commit/1e63717a) + ([@janic716](https://github.com/janic716)) +- Avoid connection in helper methods [91e9cfe1](https://www.github.com/phpredis/phpredis/commit/91e9cfe1) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Don't use convert_to_string in redis_hmget_cmd [99335d6](https://www.github.com/phpredis/phpredis/commit/99335d6) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- PHP >=7.3.0 uses zend_string to store `php_url` elements [b566fb44](https://www.github.com/phpredis/phpredis/commit/b566fb44) ([@fmk](https://github.com/fmk)) + +--- + +## [3.1.5] - 2017-09-27 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/3.1.5), [PECL](https://pecl.php.net/package/redis/3.1.5)) + +This is interim release which contains only bug fixes. + +### Fixed + +- Fix segfault when extending Redis class in PHP 5 [d23eff](https://www.github.com/phpredis/phpredis/commit/d23eff) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix RedisCluster constructor with PHP 7 strict scalar type [5c21d7](https://www.github.com/phpredis/phpredis/commit/5c21d7) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow to use empty string as persistant_id [344de5](https://www.github.com/phpredis/phpredis/commit/344de5) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix cluster_init_seeds. [db1347](https://www.github.com/phpredis/phpredis/commit/db1347) ([@adlagares](https://github.com/adlagares)) +- Fix z_seeds may be a reference [42581a](https://www.github.com/phpredis/phpredis/commit/42581a) ([@janic716](https://github.com/janic716)) +- PHP >=7.3 uses zend_string for php_url elements [b566fb](https://www.github.com/phpredis/phpredis/commit/b566fb) ([@fmk](https://github.com/fmk)) + +--- + +## [3.1.4] - 2017-09-27 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/3.1.4), [PECL](https://pecl.php.net/package/redis/3.1.4)) + +The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC +blocks in pipeline mode. There are also many bugfixes and minor improvements +to the api, listed below. + +### Added + +- Allow mixing MULTI and PIPELINE modes (experimental)! [5874b0](https://www.github.com/phpredis/phpredis/commit/5874b0) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Added integration for coverty static analysis and fixed several warnings + [faac8b0](https://www.github.com/phpredis/phpredis/commit/faac8b0), [eff7398](https://www.github.com/phpredis/phpredis/commit/eff7398), [4766c25](https://www.github.com/phpredis/phpredis/commit/4766c25), [0438ab4](https://www.github.com/phpredis/phpredis/commit/0438ab4), [1e0b065](https://www.github.com/phpredis/phpredis/commit/1e0b065), [733732a](https://www.github.com/phpredis/phpredis/commit/733732a), [26eeda5](https://www.github.com/phpredis/phpredis/commit/26eeda5), [735025](https://www.github.com/phpredis/phpredis/commit/735025), + [42f1c9](https://www.github.com/phpredis/phpredis/commit/42f1c9), [af71d4](https://www.github.com/phpredis/phpredis/commit/af71d4) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)). +- Added arginfo introspection structures [81a0303](https://www.github.com/phpredis/phpredis/commit/81a0303), [d5609fc](https://www.github.com/phpredis/phpredis/commit/d5609fc), [e5660be](https://www.github.com/phpredis/phpredis/commit/e5660be), [3c60e1f](https://www.github.com/phpredis/phpredis/commit/3c60e1f), + [50dcb15](https://www.github.com/phpredis/phpredis/commit/50dcb15), [6c2c6fa](https://www.github.com/phpredis/phpredis/commit/6c2c6fa), [212e323](https://www.github.com/phpredis/phpredis/commit/212e323), [e23be2c](https://www.github.com/phpredis/phpredis/commit/e23be2c), [682593d](https://www.github.com/phpredis/phpredis/commit/682593d), [f8de702](https://www.github.com/phpredis/phpredis/commit/f8de702), [4ef3acd](https://www.github.com/phpredis/phpredis/commit/4ef3acd), [f116be9](https://www.github.com/phpredis/phpredis/commit/f116be9), + [5c111dd](https://www.github.com/phpredis/phpredis/commit/5c111dd), [9caa029](https://www.github.com/phpredis/phpredis/commit/9caa029), [0d69650](https://www.github.com/phpredis/phpredis/commit/0d69650), [6859828](https://www.github.com/phpredis/phpredis/commit/6859828), [024e593](https://www.github.com/phpredis/phpredis/commit/024e593), [3643ab6](https://www.github.com/phpredis/phpredis/commit/3643ab6), [f576fab](https://www.github.com/phpredis/phpredis/commit/f576fab), [122d41f](https://www.github.com/phpredis/phpredis/commit/122d41f), + [a09d0e6](https://www.github.com/phpredis/phpredis/commit/a09d0e6) ([Tyson Andre](https://github.com/TysonAndre), [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)). +- Added a github issue template [61aba9](https://www.github.com/phpredis/phpredis/commit/61aba9) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Changed + +- Refactor redis_send_discard [ea15ce](https://www.github.com/phpredis/phpredis/commit/ea15ce) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Improve redis array rehash [577a91](https://www.github.com/phpredis/phpredis/commit/577a91) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Change redis array pure_cmds from zval to hashtable [a56ed7](https://www.github.com/phpredis/phpredis/commit/a56ed7) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use zend_string rather than char for various context fields (err, prefix, etc) + [2bf7b2](https://www.github.com/phpredis/phpredis/commit/2bf7b2) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Updated runtime exception handling [8dcaa4](https://www.github.com/phpredis/phpredis/commit/8dcaa4), [7c1407](https://www.github.com/phpredis/phpredis/commit/7c1407) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Fixed + +- Fixed link to redis cluster documentation [3b0b06](https://www.github.com/phpredis/phpredis/commit/3b0b06) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Removed duplicate HGET in redis array hash table, formatting [d0b9c5](https://www.github.com/phpredis/phpredis/commit/d0b9c5) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)). +- Treat NULL bulk as success for session read [659450](https://www.github.com/phpredis/phpredis/commit/659450) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix valgrind warnings [471ce07](https://www.github.com/phpredis/phpredis/commit/471ce07), [1ab89e1](https://www.github.com/phpredis/phpredis/commit/1ab89e1), [b624a8b](https://www.github.com/phpredis/phpredis/commit/b624a8b) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix php5/php7 compatibility layer [1ab89e](https://www.github.com/phpredis/phpredis/commit/1ab89e), [4e3225](https://www.github.com/phpredis/phpredis/commit/4e3225) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix typo in README.markdown [e47e44](https://www.github.com/phpredis/phpredis/commit/e47e44) ([Toby Schrapel](https://github.com/schrapel)) +- Initialize gc member of zend_string [37f569](https://www.github.com/phpredis/phpredis/commit/37f569) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)). +- Don't try to set TCP_NODELAY on a unix socket and don't warn on multiple + calls to pipeline [d11798](https://www.github.com/phpredis/phpredis/commit/d11798), [77aeba](https://www.github.com/phpredis/phpredis/commit/77aeba) ([Michael Grunder](https://github.com/michael-grunder)) +- Various other library fixes [142b51](https://www.github.com/phpredis/phpredis/commit/142b51), [4452f6](https://www.github.com/phpredis/phpredis/commit/4452f6), [e672f4](https://www.github.com/phpredis/phpredis/commit/e672f4), [658ee3](https://www.github.com/phpredis/phpredis/commit/658ee3), [c9df77](https://www.github.com/phpredis/phpredis/commit/c9df77), [4a0a46](https://www.github.com/phpredis/phpredis/commit/4a0a46) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Removed + +- Remove unused PHP_RINIT and PHP_RSHUTDOWN functions [c760bf](https://www.github.com/phpredis/phpredis/commit/c760bf) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +--- + +## [3.1.3] - 2017-07-15 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/3.1.3), [PECL](https://pecl.php.net/package/redis/3.1.3)) + +This release contains two big improvements: + +1. Adding a new printf like command construction function with additionaly + format specifiers specific to phpredis. +2. Implementation of custom objects for Redis and RedisArray wich eliminates + double hash lookup. + +Also many small improvements and bug fixes were made. + +### Added + +- Add hStrLen command [c52077](https://www.github.com/phpredis/phpredis/commit/c52077), [fb88e1](https://www.github.com/phpredis/phpredis/commit/fb88e1) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- A printf like method to construct a Redis RESP command [a4a0ed](https://www.github.com/phpredis/phpredis/commit/a4a0ed), [d75081](https://www.github.com/phpredis/phpredis/commit/d75081), + [bdd287](https://www.github.com/phpredis/phpredis/commit/bdd287), [0eaeae](https://www.github.com/phpredis/phpredis/commit/0eaeae), [b3d00d](https://www.github.com/phpredis/phpredis/commit/b3d00d) ([Michael Grunder](https://github.com/michael-grunder)) +- Use custom objects instead of zend_list for storing Redis/RedisArray [a765f8](https://www.github.com/phpredis/phpredis/commit/a765f8), + [8fa85a](https://www.github.com/phpredis/phpredis/commit/8fa85a) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add configureoption tag to package.xml [750963](https://www.github.com/phpredis/phpredis/commit/750963) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Changed + +- Add optional COUNT argument to sPop [d2e203](https://www.github.com/phpredis/phpredis/commit/d2e203) ([Michael Grunder](https://github.com/michael-grunder)) +- Allow sInterStore to take one arg [26aec4](https://www.github.com/phpredis/phpredis/commit/26aec4), [4cd06b](https://www.github.com/phpredis/phpredis/commit/4cd06b) ([Michael Grunder](https://github.com/michael-grunder)) +- Allow MIGRATE to accept multiple keys [9aa3db](https://www.github.com/phpredis/phpredis/commit/9aa3db) ([Michael Grunder](https://github.com/michael-grunder)) +- Use crc32 table from PHP distro [f81694](https://www.github.com/phpredis/phpredis/commit/f81694) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Throw exception for all non recoverable errors [e37239](https://www.github.com/phpredis/phpredis/commit/e37239) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Increase read buffers size [520e06](https://www.github.com/phpredis/phpredis/commit/520e06) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Better documentation [f0c25a](https://www.github.com/phpredis/phpredis/commit/f0c25a), [c5991f](https://www.github.com/phpredis/phpredis/commit/c5991f), [9ec9ae](https://www.github.com/phpredis/phpredis/commit/9ec9ae) ([Michael Grunder](https://github.com/michael-grunder)) +- Better TravisCI integration [e37c08](https://www.github.com/phpredis/phpredis/commit/e37c08) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Fixed + +- Make sure redisCluster members are all initialized on (re)creation [162d88](https://www.github.com/phpredis/phpredis/commit/162d88) +- ([Michael Grunder](https://github.com/michael-grunder)). +- Fix Null Bulk String response parsing in cluster library [058753](https://www.github.com/phpredis/phpredis/commit/058753) +- ([Alberto Fernández](https://github.com/albertofem)) +- Allow using numeric string in zInter command [ba0070](https://www.github.com/phpredis/phpredis/commit/ba0070) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use ZVAL_DEREF macros for dereference input variables [ad4596](https://www.github.com/phpredis/phpredis/commit/ad4596) +- ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix read_timeout [18149e](https://www.github.com/phpredis/phpredis/commit/18149e), [b56dc4](https://www.github.com/phpredis/phpredis/commit/b56dc4) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix zval_get_string impl for PHP5 [4e56ba](https://www.github.com/phpredis/phpredis/commit/4e56ba) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix Redis/RedisArray segfaults [be5c1f](https://www.github.com/phpredis/phpredis/commit/be5c1f), [635c3a](https://www.github.com/phpredis/phpredis/commit/635c3a), [1f8dde](https://www.github.com/phpredis/phpredis/commit/1f8dde), [43e1e0](https://www.github.com/phpredis/phpredis/commit/43e1e0) +- ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix memory leak and potential segfault [aa6ff7](https://www.github.com/phpredis/phpredis/commit/aa6ff7), [88efaa](https://www.github.com/phpredis/phpredis/commit/88efaa) ([Michael Grunder](https://github.com/michael-grunder)) +- Assume "NULL bulk" reply as success (empty session data) [4a81e1](https://www.github.com/phpredis/phpredis/commit/4a81e1) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactoring ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) + +--- + +## [3.1.2] - 2017-03-16 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/3.1.2), [PECL](https://pecl.php.net/package/redis/3.1.2)) + +### Changes + +- Re allow single array for sInterStore [6ef0c2](https://www.github.com/phpredis/phpredis/commit/6ef0c2), [d01966](https://www.github.com/phpredis/phpredis/commit/d01966) ([Michael Grunder](https://github.com/michael-grunder)) +- Better TravisCI integration [4fd2f6](https://www.github.com/phpredis/phpredis/commit/4fd2f6) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Fixed + +- RedisArray segfault fix [564ce3](https://www.github.com/phpredis/phpredis/commit/564ce3) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Small memory leak fix [645888b](https://www.github.com/phpredis/phpredis/commit/645888b) (Mike Grunder) +- Segfault fix when recreating RedisCluster objects [abf7d4](https://www.github.com/phpredis/phpredis/commit/abf7d4) ([Michael Grunder](https://github.com/michael-grunder)) +- Fix for RedisCluster bulk response parsing [4121c4](https://www.github.com/phpredis/phpredis/commit/4121c4) ([Alberto Fernández](https://github.com/albertofem)) + +--- + +## [3.1.1] - 2017-02-01 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/3.1.1), [PECL](https://pecl.php.net/package/redis/3.1.1)) + +This release contains mostly fixes for issues introduced when merging +the php 5 and 7 codebase into a single branch. + +- Additional test updates for 32 bit systems ([@remicollet](https://github.com/remicollet)) +- ARM rounding issue in tests ([@remicollet](https://github.com/remicollet)) +- Use new zend_list_close instead of zend_list_delete when reconnecting. +- Refactoring of redis_boolean_response_impl and redis_sock_write ([@yatsukhnenko](https://github.com/weltling)) +- Fixed a segfault in igbinary serialization ([@yatsukhnenko](https://github.com/weltling)) +- Restore 2.2.8/3.0.0 functionality to distinguish between an error + and simply empty session data. ([@remicollet](https://github.com/remicollet)) +- Fix double to string conversion function ([@yatsukhnenko](https://github.com/weltling)) +- Use PHP_FE_END definition when available ([@remicollet](https://github.com/remicollet)) +- Fixed various 'static function declared but not used' warnings +- Fixes to various calls which were typecasting pointers to the +- wrong size. ([@remicollet](https://github.com/remicollet)) +- +- Added php session unit test ([@yatsukhnenko](https://github.com/weltling)) +- Added explicit module dependancy for igbinary ([@remicollet](https://github.com/remicollet)) +- Added phpinfo serialization information ([@remicollet](https://github.com/remicollet)) + +--- + +## [3.1.0] - 2016-12-14 ([GitHub](https://github.com/phpredis/phpredis/releases/3.1.0), [PECL](https://pecl.php.net/package/redis/3.1.0)) + +In this version of phpredis codebase was unified to work with all versions of php \o/ +Also many bug fixes and some improvements has been made. + +### Added + +- Support the client to Redis Cluster just having one master ([andyli](https://github.com/andyli029)) [892e5646](https://www.github.com/phpredis/phpredis/commit/892e5646) +- Allow both long and strings that are longs for zrangebyscore offset/limit + ([Michael Grunder](https://github.com/michael-grunder)) [bdcdd2aa](https://www.github.com/phpredis/phpredis/commit/bdcdd2aa) +- Process NX|XX, CH and INCR options in zAdd command [71c9f7c8](https://www.github.com/phpredis/phpredis/commit/71c9f7c8) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Fixed + +- Fix incrby/decrby for large integers ([Michael Grunder](https://github.com/michael-grunder)) [3a12758a](https://www.github.com/phpredis/phpredis/commit/3a12758a) +- Use static declarations for spl_ce_RuntimeException decl [a9857d69](https://www.github.com/phpredis/phpredis/commit/a9857d69) + ([Jeremy Mikola](https://github.com/jmikola)) +- Fixed method call problem causes session handler to display two times + [24f86c49](https://www.github.com/phpredis/phpredis/commit/24f86c49) ([ZiHang Gao](https://github.com/cdoco)). +- PSETEX method returns '+OK' on success, not true [afcd8445](https://www.github.com/phpredis/phpredis/commit/afcd8445) ([sitri@ndxbn](https://github.com/ndxbn)) +- Fix integer overflow for long (>32bit) increments in hIncrBy [58e1d799](https://www.github.com/phpredis/phpredis/commit/58e1d799) + ([@iyesin](https://github.com/iyesin)) +- Move zend_object handler to the end ([Michael Grunder](https://github.com/michael-grunder)) [34107966](https://www.github.com/phpredis/phpredis/commit/34107966) +- Using setOption on redis array causes immediate connection [f1a85b38](https://www.github.com/phpredis/phpredis/commit/f1a85b38) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +--- + +## [3.0.0] - 2016-06-10 ([GitHub](https://github.com/phpredis/phpredis/releases/3.0.0), [PECL](https://pecl.php.net/package/redis/3.0.0)) + +This version of phpredis supports cluster and is intended for php versions +7.0.0 and higher. To compile cluster-enabled phpredis for older versions +of php, please use the 2.2.8 pecl package. + +A huge thanks goes out to Sean DuBois for doing all the work required to get +phpredis working in php 7.0! + +### Added + +- PHP 7 Support [3159bd2](https://www.github.com/phpredis//phpredis/commit/3159bd2), + [567dc2f](https://www.github.com/phpredis//phpredis/commit/567dc2f), [daa4d9f](https://www.github.com/phpredis//phpredis/commit/daa4d9f), + [f2711e3](https://www.github.com/phpredis//phpredis/commit/f2711e3), [9cb9d07](https://www.github.com/phpredis//phpredis/commit/9cb9d07), + [d51c89](https://www.github.com/phpredis//phpredis/commit/d51c89), [9ff8f49](https://www.github.com/phpredis//phpredis/commit/9ff8f49), + [33bb629](https://www.github.com/phpredis//phpredis/commit/33bb629), [cbdf65a](https://www.github.com/phpredis//phpredis/commit/cbdf65a), + [f30b7fd](https://www.github.com/phpredis//phpredis/commit/f30b7fd), [c687a51](https://www.github.com/phpredis//phpredis/commit/c687a51), + [6b3e773](https://www.github.com/phpredis//phpredis/commit/6b3e773), [2bf8241](https://www.github.com/phpredis//phpredis/commit/2bf8241), + [71bd3d](https://www.github.com/phpredis//phpredis/commit/71bd3d), [9221ca4](https://www.github.com/phpredis//phpredis/commit/9221ca4), + [4e00df6](https://www.github.com/phpredis//phpredis/commit/4e00df6), [e2407ca](https://www.github.com/phpredis//phpredis/commit/e2407ca), + [97fcfe6](https://www.github.com/phpredis//phpredis/commit/97fcfe6), [77e6200](https://www.github.com/phpredis//phpredis/commit/77e6200) + [Sean DuBois](https://github.com/Sean-Der) +- Redis Cluster support +- IPv6 support + +### Changed + +- Allow SINTERSTORE to take a single array argument again +- Exception handling improvement [Jan-E](https://github.com/Jan-E) [314a2c3c](https://www.github.com/phpredis//phpredis/commit/314a2c3c) +- Allow '-' and '+' in ZRANGEBYLEX [Patrick Pokatilo](https://github.com/SHyx0rmZ) [8bfa2188](https://www.github.com/phpredis//phpredis/commit/8bfa2188) + +### Fixed + +- config.w32 fix [Jan-E](https://github.com/Jan-E) [495d308](https://www.github.com/phpredis//phpredis/commit/495d308), [c9e0b682](https://www.github.com/phpredis//phpredis/commit/c9e0b682) +- Unit test fix for max int value [Jan-E](https://github.com/Jan-E) [659ea2aa](https://www.github.com/phpredis//phpredis/commit/659ea2aa) +- unsigned long -> zend_ulong fix [Jan-E](https://github.com/Jan-E) [4d66e3d4](https://www.github.com/phpredis//phpredis/commit/4d66e3d4) +- Visual Stuio 14 fixes [Jan-E](https://github.com/Jan-E) [ea98401c](https://www.github.com/phpredis//phpredis/commit/ea98401c) +- Segfault fix when looking up our socket [ephemeralsnow](https://github.com/ephemeralsnow) [0126481a](https://www.github.com/phpredis//phpredis/commit/0126481a) +- Documentation fixes [Ares](https://github.com/ares333) [54b9a0ec](https://www.github.com/phpredis//phpredis/commit/54b9a0ec) +- php7 related memory leak fix [Stuart Carnie](https://github.com/stuartcarnie) [b75bf3b4](https://www.github.com/phpredis//phpredis/commit/b75bf3b4) +- Potential segfault fix in cluster session [Sergei Lomakov](https://github.com/sapfeer0k) [661fb5b1](https://www.github.com/phpredis//phpredis/commit/661fb5b1) +- php7 related serialization leak fix (Adam Harvey) [c40fc1d8](https://www.github.com/phpredis//phpredis/commit/c40fc1d8) + +--- + +## [2.2.8] - 2016-06-02 ([GitHub](https://github.com/phpredis/phpredis/releases/2.2.8), [PECL](https://pecl.php.net/package/redis/2.2.8)) + +The main improvement in this version of phpredis is support for Redis +Cluster. This version of phpredis is intended for versions of php older +than 7. + +### Added + +- Added randomization to our seed nodes to balance which instance is used + to map the keyspace [32eb1c5f](https://www.github.com/phpredis/phpredis/commit/32eb1c5f) (Vitaliy Stepanyuk) +- Added support for IPv6 addresses + +### Fixed + +- PHP liveness checking workaround (Shafreeck Sea) [c18d58b9](https://www.github.com/phpredis/phpredis/commit/c18d58b9) +- Various documentation and code formatting and style fixes ([ares333](https://github.com/ares333), + [sanpili](https://github.com/sanpili), [Bryan Nelson](https://github.com/bplus), [linfangrong](https://github.com/linfangrong), [Romero Malaquias](https://github.com/RomeroMalaquias), [Viktor Szépe](https://github.com/szepeviktor)) +- Fix scan reply processing to use long instead of int to avoid overflow + [mixiaojiong](https://github.com/mixiaojiong)). +- Fix potential segfault in Redis Cluster session storage [cc15aae](https://www.github.com/phpredis/phpredis/commit/cc15aae) + ([Sergei Lomakov](https://github.com/sapfeer0k)). +- Fixed memory leak in discard function [17b1f427](https://www.github.com/phpredis/phpredis/commit/17b1f427) +- Sanity check for igbinary unserialization + [3266b222](https://www.github.com/phpredis/phpredis/commit/3266b222), [528297a](https://www.github.com/phpredis/phpredis/commit/528297a) ([Maurus Cuelenaere](https://github.com/mcuelenaere)). +- Fix segfault occuring from unclosed socket connection for Redis Cluster + [04196aee](https://www.github.com/phpredis/phpredis/commit/04196aee) ([CatKang](https://github.com/CatKang)) +- Case insensitive zRangeByScore options +- Fixed dreaded size_t vs long long compiler warning + +--- + +## [2.2.7] - 2015-03-03 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/2.2.7), [PECL](https://pecl.php.net/package/redis/2.2.7)) + +### Added + +- Implemented PFADD, PFMERGE, and PFCOUNT command handling +- Implemented ZRANGEBYLEX command (holding off on ZREVRANGEBYLEX + as that won't be out until 3.0) +- Implemented getMode() so clients can detect whether we're in + ATOMIC/MULTI/PIPELINE mode. +- Implemented rawCommand() so clients can send arbitrary things to + the redis server +- Implemented DEBUG OBJECT ([@michael-grunder](https://github.com/michael-grunder), [@isage](https://github.com/isage)) +- Added/abide by connect timeout for RedisArray + +### Fixed + +- Select to the last selected DB when phpredis reconnects +- Fix a possible invalid free in \_serialize() +- Added SAVE and BGSAVE to "distributable" commands for RedisArray +- Fixed invalid "argc" calculation in HLL commands ([@welting](https://github.com/weltling)) +- Allow clients to break out of the subscribe loop and return context. +- Fixes a memory leak in SCAN when OPT_SCAN_RETRY id. +- Fix possible segfault when igbinary is enabled ([@remicollet](https://github.com/remicollet)). +- Add a couple of cases where we throw on an error (LOADING/NOAUTH/MASTERDOWN) +- Fix several issues with serialization NARY +- Fix missing TSRMLS_CC and a TSRMLS_DC/TSRMLS_CC typo ([@itcom](https://github.com/itcom)) + +--- + +## [2.2.5] - 2014-03-15 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/2.2.5), [PECL](https://pecl.php.net/package/redis/2.2.5)) + +### Added + +- Support for the BITPOS command +- Connection timeout option for RedisArray ([@MiketoString](https://github.com/MiketoString)) +- A \_serialize method, to complement our existing \_unserialize method +- Support for the PUBSUB command +- Support for SCAN, SSCAN, HSCAN, and ZSCAN +- Support for the WAIT command + +### Fixed + +- Handle the COPY and REPLACE arguments for the MIGRATE command +- Fix syntax error in documentation for the SET command ([@mithunsatheesh](https://github.com/mithunsatheesh)) +- Fix Homebrew documentation instructions ([@mathias](https://github.com/mathiasverraes)) + +--- + +## [2.2.4] - 2013-09-01 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/2.2.4), [PECL](https://pecl.php.net/package/redis/2.2.4)) + +### Added + +- Randomized reconnect delay for RedisArray @mobli +- Lazy connections to RedisArray servers @mobli +- Allow LONG and STRING keys in MGET/MSET +- Extended SET options for Redis >= 2.6.12 +- Persistent connections and UNIX SOCKET support for RedisArray +- Allow aggregates for ZUNION/ZINTER without weights @mheijkoop +- Support for SLOWLOG command + +### Changed +- Reworked MGET algorithm to run in linear time regardless of key count. +- Reworked ZINTERSTORE/ZUNIONSTORE algorithm to run in linear time + +### Fixed + +- C99 Compliance (or rather lack thereof) fix @mobli +- Added ZEND_ACC_CTOR and ZEND_ACC_DTOR [@euskadi31](https://github.com/euskadi31) +- Stop throwing and clearing an exception on connect failure @matmoi +- Fix a false positive unit test failure having to do with TTL returns From 44b276da0a023e3804a310c8b182cdbd197c0b63 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Jul 2019 21:23:41 -0700 Subject: [PATCH 1205/1986] Be more specific about change to read error on connection string --- Changelog.md | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7fae96f0a3..2cb2a53f5e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,35 +19,37 @@ serializers, soft deprecation of non-Redis commands. - [ping can now take an argument](#brk500-ping-argument) ### Added -- Adds OPT_REPLY_LITERAL for rawCommand and EVAL [5cb30fb2](https://www.github.com/phpredis/phpredis/commit/5cb30fb2) +- Adds OPT_REPLY_LITERAL for rawCommand and EVAL [5cb30fb2](https://www.github.com/phpredis/phpredis/commit/5cb30fb2) ([Michael Grunder](https://github.com/michael-grunder)) -- JSON serializer [98bd2886](https://www.github.com/phpredis/phpredis/commit/98bd2886), - [96c57139](https://www.github.com/phpredis/phpredis/commit/96c57139), - [235a27](https://www.github.com/phpredis/phpredis/commit/235a27) +- JSON serializer [98bd2886](https://www.github.com/phpredis/phpredis/commit/98bd2886), + [96c57139](https://www.github.com/phpredis/phpredis/commit/96c57139), + [235a27](https://www.github.com/phpredis/phpredis/commit/235a27) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) -- msgpack serializer [d5b8f833](https://www.github.com/phpredis/phpredis/commit/d5b8f833), - [545250f3](https://www.github.com/phpredis/phpredis/commit/545250f3), - [52bae8ab](https://www.github.com/phpredis/phpredis/commit/52bae8ab) +- msgpack serializer [d5b8f833](https://www.github.com/phpredis/phpredis/commit/d5b8f833), + [545250f3](https://www.github.com/phpredis/phpredis/commit/545250f3), + [52bae8ab](https://www.github.com/phpredis/phpredis/commit/52bae8ab) ([@bgort](https://github.com/bgort), [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) -- Add support for STREAM to the type command [d7450b2f](https://www.github.com/phpredis/phpredis/commit/d7450b2f), +- Add support for STREAM to the type command [d7450b2f](https://www.github.com/phpredis/phpredis/commit/d7450b2f), [068ce978](https://www.github.com/phpredis/phpredis/commit/068ce978), [8a45d18c](https://www.github.com/phpredis/phpredis/commit/8a45d18c) ([Michael Grunder](https://github.com/michael-grunder), [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) -- Add Cluster slots caching [9f0d7bc0](https://www.github.com/phpredis/phpredis/commit/9f0d7bc0), +- Add Cluster slots caching [9f0d7bc0](https://www.github.com/phpredis/phpredis/commit/9f0d7bc0), [ea081e05](https://www.github.com/phpredis/phpredis/commit/ea081e05) ([Michael Grunder](https://github.com/michael-grunder)) ### Changed -- Add server address to exception message [e8fb49be](https://www.github.com/phpredis/phpredis/commit/e8fb49be), +- Add server address to exception message. This changes the exception message from `read error on connection` to + `read error on connection to :` or `read error on connection to ` so code matching the exception string might break. + [e8fb49be](https://www.github.com/phpredis/phpredis/commit/e8fb49be), [34d6403d](https://www.github.com/phpredis/phpredis/commit/34d6403d) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Allow to specify server address as `schema://host` [418428fa](https://www.github.com/phpredis/phpredis/commit/418428fa) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)). -- Update Fedora installation instructions [90aa067c](https://www.github.com/phpredis/phpredis/commit/90aa067c) +- Update Fedora installation instructions [90aa067c](https://www.github.com/phpredis/phpredis/commit/90aa067c) ([@remicollet](https://github.com/remicollet)) -- Enable connection pooling by default [8206b147](https://www.github.com/phpredis/phpredis/commit/8206b147) +- Enable connection pooling by default [8206b147](https://www.github.com/phpredis/phpredis/commit/8206b147) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) -- Allow PING to take an optional argument [6e494170](https://www.github.com/phpredis/phpredis/commit/6e494170) +- Allow PING to take an optional argument [6e494170](https://www.github.com/phpredis/phpredis/commit/6e494170) ([Michael Grunder](https://github.com/michael-grunder)) - Allow ZRANGE to be called either with `true` or `['withscores' => true]` [19f3efcf](https://www.github.com/phpredis/phpredis/commit/19f3efcf) ([Michael Grunder](https://github.com/michael-grunder)) @@ -71,7 +73,7 @@ serializers, soft deprecation of non-Redis commands. ([@michael-grunder](https://github.com/michael-grunder)). - Enable pooling for cluster slave nodes [17600dd1](https://www.github.com/phpredis/phpredis/commit/17600dd1) ([Michael Grunder](https://github.com/michael-grunder)) - xInfo response format [4852a510](https://www.github.com/phpredis/phpredis/commit/4852a510), [ac9dca0a](https://www.github.com/phpredis/phpredis/commit/ac9dca0a) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) -- Make the XREADGROUP optional COUNT and BLOCK arguments nullable +- Make the XREADGROUP optional COUNT and BLOCK arguments nullable [0c17bd27](https://www.github.com/phpredis/phpredis/commit/0c17bd27) ([Michael Grunder](https://github.com/michael-grunder)) - Allow persistent_id to be passed as NULL with strict_types enabled [60223762](https://www.github.com/phpredis/phpredis/commit/60223762) @@ -88,10 +90,10 @@ This is probably the latest release with PHP 5 suport!!! ### Added -- RedisArray auth [b5549cff](https://www.github.com/phpredis/phpredis/commit/b5549cff), [339cfa2b](https://www.github.com/phpredis/phpredis/commit/339cfa2b), +- RedisArray auth [b5549cff](https://www.github.com/phpredis/phpredis/commit/b5549cff), [339cfa2b](https://www.github.com/phpredis/phpredis/commit/339cfa2b), [6b411aa8](https://www.github.com/phpredis/phpredis/commit/6b411aa8) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) -- Add ZPOPMAX and ZPOPMIN support [46f03561](https://www.github.com/phpredis/phpredis/commit/46f03561), - [f89e941a](https://www.github.com/phpredis/phpredis/commit/f89e941a), +- Add ZPOPMAX and ZPOPMIN support [46f03561](https://www.github.com/phpredis/phpredis/commit/46f03561), + [f89e941a](https://www.github.com/phpredis/phpredis/commit/f89e941a), [2ec7d91a](https://www.github.com/phpredis/phpredis/commit/2ec7d91a) (@mbezhanov, [Michael Grunder](https://github.com/michael-grunder)) - Implement GEORADIUS_RO and GEORADIUSBYMEMBER_RO [22d81a94](https://www.github.com/phpredis/phpredis/commit/22d81a94) ([Michael Grunder](https://github.com/michael-grunder)) - RedisCluster auth [c5994f2a](https://www.github.com/phpredis/phpredis/commit/c5994f2a) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) @@ -112,7 +114,7 @@ This is probably the latest release with PHP 5 suport!!! - Use zend_string for pipeline_cmd [e98f5116](https://www.github.com/phpredis/phpredis/commit/e98f5116) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Implement consistent hashing algorithm for RedisArray [bb32e6f3](https://www.github.com/phpredis/phpredis/commit/bb32e6f3), [71922bf1](https://www.github.com/phpredis/phpredis/commit/71922bf1) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) -- Use zend_string for storing RedisArray hosts [602740d3](https://www.github.com/phpredis/phpredis/commit/602740d3), +- Use zend_string for storing RedisArray hosts [602740d3](https://www.github.com/phpredis/phpredis/commit/602740d3), [3e7e1c83](https://www.github.com/phpredis/phpredis/commit/3e7e1c83) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Refactor redis_sock_read_bulk_reply [bc4dbc4b](https://www.github.com/phpredis/phpredis/commit/bc4dbc4b) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) From 905b64191cfbafa7d269c8776372029a002e5cf7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 4 Jul 2019 21:26:15 -0700 Subject: [PATCH 1206/1986] PING modification isn't breaking after all --- Changelog.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 2cb2a53f5e..1e907db19f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,7 +16,6 @@ serializers, soft deprecation of non-Redis commands. - [Nullable xReadGroup COUNT and BLOCK arguments](#brk500-xreadgroup) - [RedisArray exception now includes host information](#brk500-exception-host) - [zRange now conforms to zRangeByScore to get scores](#brk500-zrange-withscores) -- [ping can now take an argument](#brk500-ping-argument) ### Added - Adds OPT_REPLY_LITERAL for rawCommand and EVAL [5cb30fb2](https://www.github.com/phpredis/phpredis/commit/5cb30fb2) From 52764748121bf0c6980b53f1212fa5a25e98fa5b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 5 Jul 2019 19:44:00 +0300 Subject: [PATCH 1207/1986] Issue #1591 (#1592) * Issue #1591 * Add notes to Changelog --- Changelog.md | 9 +++++++++ cluster_library.c | 8 +++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1e907db19f..d42ef25770 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,15 @@ All changes to phpredis will be documented in this file. We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and PhpRedis adhears to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed + +- RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/pull/1592/commits/f52cd237) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +--- + ## [5.0.0] - 2019-07-02 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.0.0), [PECL](https://pecl.php.net/package/redis/5.0.0)) This release contains important improvements and breaking changes. The most diff --git a/cluster_library.c b/cluster_library.c index 564629b528..a9205cf677 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1266,9 +1266,11 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC) { /* We also want to disconnect any slave connections so they will be pooled * in the event we are using persistent connections and connection pooling. */ - ZEND_HASH_FOREACH_PTR(node->slaves, slave) { - redis_sock_disconnect(slave->sock, force TSRMLS_CC); - } ZEND_HASH_FOREACH_END(); + if (node->slaves) { + ZEND_HASH_FOREACH_PTR(node->slaves, slave) { + redis_sock_disconnect(slave->sock, force TSRMLS_CC); + } ZEND_HASH_FOREACH_END(); + } } ZEND_HASH_FOREACH_END(); } From 2abc61da318e2b8287fe0647f84a2b028ca913b0 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 26 Jun 2019 16:26:08 +0200 Subject: [PATCH 1208/1986] Add support for Zstd compression --- .travis.yml | 6 ++- Changelog.md | 6 +++ common.h | 3 ++ config.m4 | 35 +++++++++++++ library.c | 125 ++++++++++++++++++++++++++++++++------------ redis.c | 29 +++++++++- redis_commands.c | 9 ++++ tests/RedisTest.php | 16 +++++- 8 files changed, 192 insertions(+), 37 deletions(-) diff --git a/.travis.yml b/.travis.yml index 47685adc30..828416ea37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,10 +28,12 @@ matrix: env: CC=clang addons: apt: - packages: clang + packages: + - clang + - libzstd1-dev before_install: - phpize - - CFGARGS="--enable-redis-lzf" + - CFGARGS="--enable-redis-lzf --enable-redis-zstd" - pecl install igbinary && CFGARGS="$CFGARGS --enable-redis-igbinary" - pecl install msgpack && CFGARGS="$CFGARGS --enable-redis-msgpack" - ./configure $CFGARGS diff --git a/Changelog.md b/Changelog.md index d42ef25770..d1c9533efb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,12 @@ and PhpRedis adhears to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Added + +- Add optional support for Zstd compression, using `--enable-redis-ztsd`. + This requires libzstd version >= 1.3.0 [PR #1382](https://github.com/phpredis/phpredis/pull/1582) + ([Remi Collet](https://github.com/remicollet)) + ### Fixed - RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/pull/1592/commits/f52cd237) diff --git a/common.h b/common.h index 6b75a81e89..2975c39a98 100644 --- a/common.h +++ b/common.h @@ -79,6 +79,7 @@ typedef enum _PUBSUB_TYPE { #define REDIS_OPT_TCP_KEEPALIVE 6 #define REDIS_OPT_COMPRESSION 7 #define REDIS_OPT_REPLY_LITERAL 8 +#define REDIS_OPT_COMPRESSION_LEVEL 9 /* cluster options */ #define REDIS_FAILOVER_NONE 0 @@ -96,6 +97,7 @@ typedef enum { /* compression */ #define REDIS_COMPRESSION_NONE 0 #define REDIS_COMPRESSION_LZF 1 +#define REDIS_COMPRESSION_ZSTD 2 /* SCAN options */ #define REDIS_SCAN_NORETRY 0 @@ -258,6 +260,7 @@ typedef struct { redis_serializer serializer; int compression; + int compression_level; long dbNumber; zend_string *prefix; diff --git a/config.m4 b/config.m4 index 512fc61570..bc4dd40d31 100644 --- a/config.m4 +++ b/config.m4 @@ -23,6 +23,12 @@ PHP_ARG_ENABLE(redis-lzf, whether to enable lzf compression, PHP_ARG_WITH(liblzf, use system liblzf, [ --with-liblzf[=DIR] Use system liblzf], no, no) +PHP_ARG_ENABLE(redis-zstd, whether to enable Zstd compression, +[ --enable-redis-zstd Enable Zstd compression support], no, no) + +PHP_ARG_WITH(libzstd, use system libsztd, +[ --with-libzstd[=DIR] Use system libzstd], yes, no) + if test "$PHP_REDIS" != "no"; then if test "$PHP_REDIS_SESSION" != "no"; then @@ -188,6 +194,35 @@ if test "$PHP_REDIS" != "no"; then fi fi + if test "$PHP_REDIS_ZSTD" != "no"; then + AC_DEFINE(HAVE_REDIS_ZSTD, 1, [ ]) + if test "$PHP_LIBZSTD" != "no"; then + AC_MSG_CHECKING(for libzstd files in default path) + for i in $PHP_LIBZSTD /usr/local /usr; do + if test -r $i/include/zstd.h; then + AC_MSG_RESULT(found in $i) + LIBZSTD_DIR=$i + break + fi + done + if test -z "$LIBZSTD_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Please reinstall the libzstd distribution]) + fi + PHP_CHECK_LIBRARY(zstd, ZSTD_getFrameContentSize, + [ + PHP_ADD_LIBRARY_WITH_PATH(zstd, $LIBZSTD_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD) + ], [ + AC_MSG_ERROR([could not find usable libzstd, version 1.3.0 required]) + ], [ + -L$LIBZSTD_DIR/$PHP_LIBDIR + ]) + PHP_SUBST(REDIS_SHARED_LIBADD) + else + AC_MSG_ERROR([only system libzstd is supported]) + fi + fi + AC_CHECK_PROG([GIT], [git], [yes], [no]) if test "$GIT" == "yes" && test -d "$srcdir/.git"; then AC_DEFINE_UNQUOTED(GIT_REVISION, ["$(git log -1 --format=%H)"], [ ]) diff --git a/library.c b/library.c index d757fad6f5..ab02c3fcef 100644 --- a/library.c +++ b/library.c @@ -21,6 +21,10 @@ #endif #endif +#ifdef HAVE_REDIS_ZSTD +#include +#endif + #include #include "php_redis.h" #include "library.h" @@ -1764,6 +1768,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, redis_sock->serializer = REDIS_SERIALIZER_NONE; redis_sock->compression = REDIS_COMPRESSION_NONE; + redis_sock->compression_level = 0; /* default */ redis_sock->mode = ATOMIC; redis_sock->head = NULL; redis_sock->current = NULL; @@ -2186,26 +2191,60 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC char *buf; int valfree; size_t len; -#ifdef HAVE_REDIS_LZF - char *data; - uint32_t res; - double size; -#endif valfree = redis_serialize(redis_sock, z, &buf, &len TSRMLS_CC); switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: #ifdef HAVE_REDIS_LZF - /* preserve compatibility with PECL lzf_compress margin (greater of 4% and LZF_MARGIN) */ - size = len + MIN(UINT_MAX - len, MAX(LZF_MARGIN, len / 25)); - data = emalloc(size); - if ((res = lzf_compress(buf, len, data, size)) > 0) { - if (valfree) efree(buf); - *val = data; - *val_len = res; - return 1; + { + char *data; + uint32_t res; + double size; + + /* preserve compatibility with PECL lzf_compress margin (greater of 4% and LZF_MARGIN) */ + size = len + MIN(UINT_MAX - len, MAX(LZF_MARGIN, len / 25)); + data = emalloc(size); + if ((res = lzf_compress(buf, len, data, size)) > 0) { + if (valfree) efree(buf); + *val = data; + *val_len = res; + return 1; + } + efree(data); + } +#endif + break; + case REDIS_COMPRESSION_ZSTD: +#ifdef HAVE_REDIS_ZSTD + { + char *data; + size_t size; + int level; + + if (redis_sock->compression_level < 1) { +#ifdef ZSTD_CLEVEL_DEFAULT + level = ZSTD_CLEVEL_DEFAULT; +#else + level = 3; +#endif + } else if (redis_sock->compression_level > ZSTD_maxCLevel()) { + level = ZSTD_maxCLevel(); + } else { + level = redis_sock->compression_level; + } + + size = ZSTD_compressBound(len); + data = emalloc(size); + size = ZSTD_compress(data, size, buf, len, level); + if (!ZSTD_isError(size)) { + if (valfree) efree(buf); + data = erealloc(data, size); + *val = data; + *val_len = size; + return 1; + } + efree(data); } - efree(data); #endif break; } @@ -2217,29 +2256,51 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC) { -#ifdef HAVE_REDIS_LZF - char *data; - int i; - uint32_t res; -#endif - switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: #ifdef HAVE_REDIS_LZF - errno = E2BIG; - /* start from two-times bigger buffer and - * increase it exponentially if needed */ - for (i = 2; errno == E2BIG; i *= 2) { - data = emalloc(i * val_len); - if ((res = lzf_decompress(val, val_len, data, i * val_len)) == 0) { - /* errno != E2BIG will brake for loop */ + { + char *data; + int i; + uint32_t res; + + errno = E2BIG; + /* start from two-times bigger buffer and + * increase it exponentially if needed */ + for (i = 2; errno == E2BIG; i *= 2) { + data = emalloc(i * val_len); + if ((res = lzf_decompress(val, val_len, data, i * val_len)) == 0) { + /* errno != E2BIG will brake for loop */ + efree(data); + continue; + } else if (redis_unserialize(redis_sock, data, res, z_ret TSRMLS_CC) == 0) { + ZVAL_STRINGL(z_ret, data, res); + } efree(data); - continue; - } else if (redis_unserialize(redis_sock, data, res, z_ret TSRMLS_CC) == 0) { - ZVAL_STRINGL(z_ret, data, res); + return 1; + } + } +#endif + break; + case REDIS_COMPRESSION_ZSTD: +#ifdef HAVE_REDIS_ZSTD + { + char *data; + size_t len; + + len = ZSTD_getFrameContentSize(val, val_len); + if (len >= 0) { + data = emalloc(len); + len = ZSTD_decompress(data, len, val, val_len); + if (ZSTD_isError(len)) { + efree(data); + break; + } else if (redis_unserialize(redis_sock, data, len, z_ret TSRMLS_CC) == 0) { + ZVAL_STRINGL(z_ret, data, len); + } + efree(data); + return 1; } - efree(data); - return 1; } #endif break; diff --git a/redis.c b/redis.c index 3cc13f52d4..3d3a7e0809 100644 --- a/redis.c +++ b/redis.c @@ -38,6 +38,10 @@ #include "library.h" +#ifdef HAVE_REDIS_ZSTD +#include +#endif + #ifdef PHP_SESSION extern ps_module ps_mod_redis; extern ps_module ps_mod_redis_cluster; @@ -685,6 +689,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) zend_declare_class_constant_long(ce, ZEND_STRL("OPT_TCP_KEEPALIVE"), REDIS_OPT_TCP_KEEPALIVE TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION TSRMLS_CC); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_REPLY_LITERAL"), REDIS_OPT_REPLY_LITERAL); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_LEVEL"), REDIS_OPT_COMPRESSION_LEVEL); /* serializer */ zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE TSRMLS_CC); @@ -702,6 +707,16 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) #ifdef HAVE_REDIS_LZF zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZF"), REDIS_COMPRESSION_LZF TSRMLS_CC); #endif +#ifdef HAVE_REDIS_ZSTD + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD"), REDIS_COMPRESSION_ZSTD); + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MIN"), 1); +#ifdef ZSTD_CLEVEL_DEFAULT + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_DEFAULT"), ZSTD_CLEVEL_DEFAULT); +#else + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_DEFAULT"), 3); +#endif + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MAX"), ZSTD_maxCLevel()); +#endif /* scan options*/ zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN TSRMLS_CC); @@ -852,6 +867,8 @@ get_available_serializers(void) */ PHP_MINFO_FUNCTION(redis) { + smart_str names = {0,}; + php_info_print_table_start(); php_info_print_table_header(2, "Redis Support", "enabled"); php_info_print_table_row(2, "Redis Version", PHP_REDIS_VERSION); @@ -860,8 +877,18 @@ PHP_MINFO_FUNCTION(redis) #endif php_info_print_table_row(2, "Available serializers", get_available_serializers()); #ifdef HAVE_REDIS_LZF - php_info_print_table_row(2, "Available compression", "lzf"); + smart_str_appends(&names, "lzf"); +#endif +#ifdef HAVE_REDIS_ZSTD + if (names.s) { + smart_str_appends(&names, ", "); + } + smart_str_appends(&names, "zstd"); #endif + if (names.s) { + php_info_print_table_row(2, "Available compression", names.s->val); + } + smart_str_free(&names); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); diff --git a/redis_commands.c b/redis_commands.c index 006d3dc1f6..e6f0d2c81c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3888,6 +3888,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_LONG(redis_sock->serializer); case REDIS_OPT_COMPRESSION: RETURN_LONG(redis_sock->compression); + case REDIS_OPT_COMPRESSION_LEVEL: + RETURN_LONG(redis_sock->compression_level); case REDIS_OPT_PREFIX: if (redis_sock->prefix) { RETURN_STRINGL(ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix)); @@ -3950,12 +3952,19 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, if (val_long == REDIS_COMPRESSION_NONE #ifdef HAVE_REDIS_LZF || val_long == REDIS_COMPRESSION_LZF +#endif +#ifdef HAVE_REDIS_ZSTD + || val_long == REDIS_COMPRESSION_ZSTD #endif ) { redis_sock->compression = val_long; RETURN_TRUE; } break; + case REDIS_OPT_COMPRESSION_LEVEL: + val_long = zval_get_long(val); + redis_sock->compression_level = val_long; + RETURN_TRUE; case REDIS_OPT_PREFIX: if (redis_sock->prefix) { zend_string_release(redis_sock->prefix); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 6e3614365f..027291a2d8 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -4485,14 +4485,26 @@ public function testCompressionLZF() if (!defined('Redis::COMPRESSION_LZF')) { $this->markTestSkipped(); } - $this->checkCompression(Redis::COMPRESSION_LZF); + $this->checkCompression(Redis::COMPRESSION_LZF, 0); } - private function checkCompression($mode) + public function testCompressionZSTD() + { + if (!defined('Redis::COMPRESSION_ZSTD')) { + $this->markTestSkipped(); + } + $this->checkCompression(Redis::COMPRESSION_ZSTD, 0); + $this->checkCompression(Redis::COMPRESSION_ZSTD, 9); + } + + 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')); From 28388abceeb217202eb48c6edb5fd8671d46068e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 9 Jul 2019 14:11:59 +0300 Subject: [PATCH 1209/1986] Update Changelog --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d1c9533efb..f17a85392a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,7 @@ and PhpRedis adhears to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Add optional support for Zstd compression, using `--enable-redis-ztsd`. - This requires libzstd version >= 1.3.0 [PR #1382](https://github.com/phpredis/phpredis/pull/1582) + This requires libzstd version >= 1.3.0 [2abc61da](https://github.com/phpredis/phpredis/pull/1582/commits/2abc61da) ([Remi Collet](https://github.com/remicollet)) ### Fixed From 943802272a9557c513eb6b59f285e175ec734ad4 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Tue, 9 Jul 2019 15:26:44 +0200 Subject: [PATCH 1210/1986] cleanup TSRMLS_* usage --- cluster_library.c | 204 +++++++++++++++++----------------- cluster_library.h | 40 +++---- common.h | 8 +- library.c | 266 ++++++++++++++++++++++----------------------- library.h | 52 ++++----- php_redis.h | 6 +- redis.c | 266 ++++++++++++++++++++++----------------------- redis_array.c | 186 +++++++++++++++---------------- redis_array.h | 4 +- redis_array_impl.c | 208 +++++++++++++++++------------------ redis_array_impl.h | 26 ++--- redis_cluster.c | 206 +++++++++++++++++------------------ redis_cluster.h | 10 +- redis_commands.c | 248 +++++++++++++++++++++--------------------- redis_commands.h | 2 +- redis_session.c | 152 +++++++++++++------------- 16 files changed, 942 insertions(+), 942 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index a9205cf677..767e95b0ae 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -114,7 +114,7 @@ void cluster_free_reply(clusterReply *reply, int free_data) { static void cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, clusterReply **element, int status_strings, - int *err TSRMLS_DC) + int *err) { int i; size_t sz; @@ -126,7 +126,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, r = element[i] = ecalloc(1, sizeof(clusterReply)); // Bomb out, flag error condition on a communication failure - if (redis_read_reply_type(sock, &r->type, &len TSRMLS_CC) < 0) { + if (redis_read_reply_type(sock, &r->type, &len) < 0) { *err = 1; return; } @@ -137,7 +137,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, switch(r->type) { case TYPE_ERR: case TYPE_LINE: - if (redis_sock_gets(sock,buf,sizeof(buf),&sz TSRMLS_CC) < 0) { + if (redis_sock_gets(sock,buf,sizeof(buf),&sz) < 0) { *err = 1; return; } @@ -149,7 +149,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, break; case TYPE_BULK: if (r->len >= 0) { - r->str = redis_sock_read_bulk_reply(sock,r->len TSRMLS_CC); + r->str = redis_sock_read_bulk_reply(sock,r->len); if (!r->str) { *err = 1; return; @@ -162,7 +162,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, if (r->len > 0) { r->element = ecalloc(r->len,sizeof(clusterReply*)); cluster_multibulk_resp_recursive(sock, r->elements, r->element, - status_strings, err TSRMLS_CC); + status_strings, err); } if (*err) return; } @@ -197,17 +197,17 @@ static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, } /* Read the response from a cluster */ -clusterReply *cluster_read_resp(redisCluster *c, int status_strings TSRMLS_DC) { +clusterReply *cluster_read_resp(redisCluster *c, int status_strings) { return cluster_read_sock_resp(c->cmd_sock, c->reply_type, status_strings ? c->line_reply : NULL, - c->reply_len TSRMLS_CC); + c->reply_len); } /* Read any sort of response from the socket, having already issued the * command and consumed the reply type and meta info (length) */ clusterReply* cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, - char *line_reply, size_t len TSRMLS_DC) + char *line_reply, size_t len) { clusterReply *r; @@ -230,7 +230,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, return r; case TYPE_BULK: r->len = len; - r->str = redis_sock_read_bulk_reply(redis_sock, len TSRMLS_CC); + r->str = redis_sock_read_bulk_reply(redis_sock, len); if (r->len != -1 && !r->str) { cluster_free_reply(r, 1); return NULL; @@ -241,7 +241,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, if (len != (size_t)-1) { r->element = ecalloc(len, sizeof(clusterReply*)*len); cluster_multibulk_resp_recursive(redis_sock, len, r->element, - line_reply != NULL, &err TSRMLS_CC); + line_reply != NULL, &err); } break; default: @@ -261,10 +261,10 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, /* Helper to open connection and send AUTH if necessary */ static zend_always_inline int -cluster_sock_open(RedisSock *redis_sock TSRMLS_DC) +cluster_sock_open(RedisSock *redis_sock) { zend_bool need_auth = (redis_sock->auth && redis_sock->status != REDIS_SOCK_STATUS_CONNECTED); - if (!redis_sock_server_open(redis_sock TSRMLS_CC) && (!need_auth || !redis_sock_auth(redis_sock TSRMLS_CC))) { + if (!redis_sock_server_open(redis_sock) && (!need_auth || !redis_sock_auth(redis_sock ))) { return SUCCESS; } return FAILURE; @@ -277,7 +277,7 @@ cluster_sock_open(RedisSock *redis_sock TSRMLS_DC) /* Send a command to the specific socket and validate reply type */ static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len, - REDIS_REPLY_TYPE type TSRMLS_DC) + REDIS_REPLY_TYPE type) { char buf[1024]; @@ -290,15 +290,15 @@ static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len, return 0; } -static int cluster_send_asking(RedisSock *redis_sock TSRMLS_DC) { +static int cluster_send_asking(RedisSock *redis_sock) { return cluster_send_direct(redis_sock, RESP_ASKING_CMD, - sizeof(RESP_ASKING_CMD)-1, TYPE_LINE TSRMLS_CC); + sizeof(RESP_ASKING_CMD)-1, TYPE_LINE); } /* Send READONLY to a specific RedisSock unless it's already flagged as being * in READONLY mode. If we can send the command, we flag the socket as being * in that mode. */ -static int cluster_send_readonly(RedisSock *redis_sock TSRMLS_DC) { +static int cluster_send_readonly(RedisSock *redis_sock) { int ret; /* We don't have to do anything if we're already in readonly mode */ @@ -306,7 +306,7 @@ static int cluster_send_readonly(RedisSock *redis_sock TSRMLS_DC) { /* Return success if we can send it */ ret = cluster_send_direct(redis_sock, RESP_READONLY_CMD, - sizeof(RESP_READONLY_CMD) - 1, TYPE_LINE TSRMLS_CC); + sizeof(RESP_READONLY_CMD) - 1, TYPE_LINE); /* Flag this socket as READONLY if our command worked */ redis_sock->readonly = !ret; @@ -316,9 +316,9 @@ static int cluster_send_readonly(RedisSock *redis_sock TSRMLS_DC) { } /* Send MULTI to a specific ReidsSock */ -static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { +static int cluster_send_multi(redisCluster *c, short slot) { if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_MULTI_CMD, - sizeof(RESP_MULTI_CMD) - 1, TYPE_LINE TSRMLS_CC) == 0) + sizeof(RESP_MULTI_CMD) - 1, TYPE_LINE) == 0) { c->cmd_sock->mode = MULTI; return 0; @@ -330,12 +330,12 @@ static int cluster_send_multi(redisCluster *c, short slot TSRMLS_DC) { * here because we know we'll only have sent MULTI to the master nodes. We can't * failover inside a transaction, as we don't know if the transaction will only * be readonly commands, or contain write commands as well */ -PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { +PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot) { int retval; /* Send exec */ retval = cluster_send_slot(c, slot, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD)-1, - TYPE_MULTIBULK TSRMLS_CC); + TYPE_MULTIBULK); /* We'll either get a length corresponding to the number of commands sent to * this node, or -1 in the case of EXECABORT or WATCH failure. */ @@ -345,9 +345,9 @@ PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC) { return retval; } -PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC) { +PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot) { if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_DISCARD_CMD, - sizeof(RESP_DISCARD_CMD)-1, TYPE_LINE TSRMLS_CC)) + sizeof(RESP_DISCARD_CMD)-1, TYPE_LINE)) { return 0; } @@ -464,14 +464,14 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, /* Provided a clusterKeyVal, add a value */ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val - TSRMLS_DC) + ) { char *val; size_t val_len; int val_free; // Serialize our value - val_free = redis_pack(c->flags, z_val, &val, &val_len TSRMLS_CC); + val_free = redis_pack(c->flags, z_val, &val, &val_len); // Attach it to the provied keyval entry kv->val = val; @@ -613,7 +613,7 @@ static void fyshuffle(int *array, size_t len) { /* Execute a CLUSTER SLOTS command against the seed socket, and return the * reply or NULL on failure. */ -clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) +clusterReply* cluster_get_slots(RedisSock *redis_sock) { clusterReply *r; REDIS_REPLY_TYPE type; @@ -621,14 +621,14 @@ clusterReply* cluster_get_slots(RedisSock *redis_sock TSRMLS_DC) // Send the command to the socket and consume reply type if (redis_sock_write(redis_sock, RESP_CLUSTER_SLOTS_CMD, - sizeof(RESP_CLUSTER_SLOTS_CMD)-1 TSRMLS_CC) < 0 || - redis_read_reply_type(redis_sock, &type, &len TSRMLS_CC) < 0) + sizeof(RESP_CLUSTER_SLOTS_CMD)-1) < 0 || + redis_read_reply_type(redis_sock, &type, &len) < 0) { return NULL; } // Consume the rest of our response - if ((r = cluster_read_sock_resp(redis_sock, type, NULL, len TSRMLS_CC)) == NULL || + if ((r = cluster_read_sock_resp(redis_sock, type, NULL, len)) == NULL || r->type != TYPE_MULTIBULK || r->elements < 1) { if (r) cluster_free_reply(r, 1); @@ -774,7 +774,7 @@ PHP_REDIS_API void cluster_free_node(redisClusterNode *node) { } /* Get or create a redisClusterNode that corresponds to the asking redirection */ -static redisClusterNode *cluster_get_asking_node(redisCluster *c TSRMLS_DC) { +static redisClusterNode *cluster_get_asking_node(redisCluster *c) { redisClusterNode *pNode; char key[1024]; int key_len; @@ -797,8 +797,8 @@ static redisClusterNode *cluster_get_asking_node(redisCluster *c TSRMLS_DC) { /* Get or create a node at the host:port we were asked to check, and return the * redis_sock for it. */ -static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) { - return cluster_get_asking_node(c TSRMLS_CC)->sock; +static RedisSock *cluster_get_asking_sock(redisCluster *c) { + return cluster_get_asking_node(c)->sock; } /* Our context seeds will be a hash table with RedisSock* pointers */ @@ -865,10 +865,10 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, } PHP_REDIS_API void -cluster_free(redisCluster *c, int free_ctx TSRMLS_DC) +cluster_free(redisCluster *c, int free_ctx) { /* Disconnect from each node we're connected to */ - cluster_disconnect(c, 0 TSRMLS_CC); + cluster_disconnect(c, 0); /* Free any allocated prefix */ if (c->flags->prefix) zend_string_release(c->flags->prefix); @@ -1116,7 +1116,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { } /* Initial mapping of our cluster keyspace */ -PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { +PHP_REDIS_API int cluster_map_keyspace(redisCluster *c) { RedisSock *seed; clusterReply *slots = NULL; int mapped = 0; @@ -1124,12 +1124,12 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { // Iterate over seeds until we can get slots ZEND_HASH_FOREACH_PTR(c->seeds, seed) { // Attempt to connect to this seed node - if (seed == NULL || cluster_sock_open(seed TSRMLS_CC) != 0) { + if (seed == NULL || cluster_sock_open(seed) != 0) { continue; } // Parse out cluster nodes. Flag mapped if we are valid - slots = cluster_get_slots(seed TSRMLS_CC); + slots = cluster_get_slots(seed); if (slots) { mapped = !cluster_map_slots(c, slots); // Bin anything mapped, if we failed somewhere @@ -1137,7 +1137,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) { memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); } } - redis_sock_disconnect(seed, 0 TSRMLS_CC); + redis_sock_disconnect(seed, 0); if (mapped) break; } ZEND_HASH_FOREACH_END(); @@ -1193,7 +1193,7 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) * This function will return -1 on a critical error (e.g. parse/communication * error, 0 if no redirection was encountered, and 1 if the data was moved. */ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type - TSRMLS_DC) + ) { size_t sz; @@ -1201,7 +1201,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type CLUSTER_CLEAR_ERROR(c); CLUSTER_CLEAR_REPLY(c); - if (-1 == redis_check_eof(c->cmd_sock, 1 TSRMLS_CC) || + if (-1 == redis_check_eof(c->cmd_sock, 1) || EOF == (*reply_type = php_stream_getc(c->cmd_sock->stream))) { return -1; @@ -1237,7 +1237,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type // Fetch the first line of our response from Redis. if (redis_sock_gets(c->cmd_sock,c->line_reply,sizeof(c->line_reply), - &sz TSRMLS_CC) < 0) + &sz) < 0) { return -1; } @@ -1255,20 +1255,20 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type } /* Disconnect from each node we're connected to */ -PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC) { +PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force) { redisClusterNode *node, *slave; ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) continue; /* Disconnect from the master */ - redis_sock_disconnect(node->sock, force TSRMLS_CC); + redis_sock_disconnect(node->sock, force); /* We also want to disconnect any slave connections so they will be pooled * in the event we are using persistent connections and connection pooling. */ if (node->slaves) { ZEND_HASH_FOREACH_PTR(node->slaves, slave) { - redis_sock_disconnect(slave->sock, force TSRMLS_CC); + redis_sock_disconnect(slave->sock, force); } ZEND_HASH_FOREACH_END(); } } ZEND_HASH_FOREACH_END(); @@ -1277,7 +1277,7 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC) { /* This method attempts to write our command at random to the master and any * attached slaves, until we either successufly do so, or fail. */ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, - int nomaster TSRMLS_DC) + int nomaster) { int i, count = 1, *nodes; RedisSock *redis_sock; @@ -1308,7 +1308,7 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, /* If we're not on the master, attempt to send the READONLY command to * this slave, and skip it if that fails */ if (nodes[i] == 0 || redis_sock->readonly || - cluster_send_readonly(redis_sock TSRMLS_CC) == 0) + cluster_send_readonly(redis_sock) == 0) { /* Attempt to send the command */ if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz)) { @@ -1350,7 +1350,7 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz, * ASKING redirection, such that the keyspace can be updated. */ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, - int direct TSRMLS_DC) + int direct) { redisClusterNode *seed_node; RedisSock *redis_sock; @@ -1366,8 +1366,8 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, /* If in ASK redirection, get/create the node for that host:port, otherwise * just use the command socket. */ if (c->redir_type == REDIR_ASK) { - redis_sock = cluster_get_asking_sock(c TSRMLS_CC); - if (cluster_send_asking(redis_sock TSRMLS_CC) < 0) { + redis_sock = cluster_get_asking_sock(c); + if (cluster_send_asking(redis_sock) < 0) { return -1; } } @@ -1383,12 +1383,12 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz, } else if (failover == REDIS_FAILOVER_ERROR) { /* Try the master, then fall back to any slaves we may have */ if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz) || - !cluster_dist_write(c, cmd, sz, 1 TSRMLS_CC)) return 0; + !cluster_dist_write(c, cmd, sz, 1)) return 0; } else { /* Include or exclude master node depending on failover option and * attempt to make our write */ nomaster = failover == REDIS_FAILOVER_DISTRIBUTE_SLAVES; - if (!cluster_dist_write(c, cmd, sz, nomaster TSRMLS_CC)) { + if (!cluster_dist_write(c, cmd, sz, nomaster)) { /* We were able to write to a master or slave at random */ return 0; } @@ -1428,7 +1428,7 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host, /* Provided a redisCluster object, the slot where we thought data was and * the slot where data was moved, update our node mapping */ -static void cluster_update_slot(redisCluster *c TSRMLS_DC) { +static void cluster_update_slot(redisCluster *c) { redisClusterNode *node; char key[1024]; size_t klen; @@ -1481,14 +1481,14 @@ static void cluster_update_slot(redisCluster *c TSRMLS_DC) { /* Abort any transaction in process, by sending DISCARD to any nodes that * have active transactions in progress. If we can't send DISCARD, we need * to disconnect as it would leave us in an undefined state. */ -PHP_REDIS_API int cluster_abort_exec(redisCluster *c TSRMLS_DC) { +PHP_REDIS_API int cluster_abort_exec(redisCluster *c) { clusterFoldItem *fi = c->multi_head; /* Loop through our fold items */ while (fi) { if (SLOT_SOCK(c,fi->slot)->mode == MULTI) { - if (cluster_send_discard(c, fi->slot TSRMLS_CC) < 0) { - cluster_disconnect(c, 0 TSRMLS_CC); + if (cluster_send_discard(c, fi->slot) < 0) { + cluster_disconnect(c, 0); return -1; } SLOT_SOCK(c,fi->slot)->mode = ATOMIC; @@ -1527,7 +1527,7 @@ PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, /* Send a command to a specific slot */ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, - int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC) + int cmd_len, REDIS_REPLY_TYPE rtype) { /* Point our cluster to this slot and it's socket */ c->cmd_slot = slot; @@ -1536,19 +1536,19 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, /* Enable multi mode on this slot if we've been directed to but haven't * send it to this node yet */ if (c->flags->mode == MULTI && c->cmd_sock->mode != MULTI) { - if (cluster_send_multi(c, slot TSRMLS_CC) == -1) { + if (cluster_send_multi(c, slot) == -1) { CLUSTER_THROW_EXCEPTION("Unable to enter MULTI mode on requested slot", 0); return -1; } } /* Try the slot */ - if (cluster_sock_write(c, cmd, cmd_len, 1 TSRMLS_CC) == -1) { + if (cluster_sock_write(c, cmd, cmd_len, 1) == -1) { return -1; } /* Check our response */ - if (cluster_check_response(c, &c->reply_type TSRMLS_CC) != 0 || + if (cluster_check_response(c, &c->reply_type) != 0 || (rtype != TYPE_EOF && rtype != c->reply_type)) return -1; /* Success */ @@ -1558,7 +1558,7 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, /* Send a command to given slot in our cluster. If we get a MOVED or ASK error * we attempt to send the command to the node as directed. */ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, - int cmd_len TSRMLS_DC) + int cmd_len) { int resp, timedout = 0; long msstart; @@ -1585,7 +1585,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char /* Send MULTI to the socket if we're in MULTI mode but haven't yet */ if (c->flags->mode == MULTI && CMD_SOCK(c)->mode != MULTI) { /* We have to fail if we can't send MULTI to the node */ - if (cluster_send_multi(c, slot TSRMLS_CC) == -1) { + if (cluster_send_multi(c, slot) == -1) { CLUSTER_THROW_EXCEPTION("Unable to enter MULTI mode on requested slot", 0); return -1; } @@ -1593,14 +1593,14 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char /* Attempt to deliver our command to the node, and that failing, to any * node until we find one that is available. */ - if (cluster_sock_write(c, cmd, cmd_len, 0 TSRMLS_CC) == -1) { + if (cluster_sock_write(c, cmd, cmd_len, 0) == -1) { /* We have to abort, as no nodes are reachable */ CLUSTER_THROW_EXCEPTION("Can't communicate with any node in the cluster", 0); return -1; } /* Check response and short-circuit on success or communication error */ - resp = cluster_check_response(c, &c->reply_type TSRMLS_CC); + resp = cluster_check_response(c, &c->reply_type); if (resp <= 0) { break; } @@ -1615,7 +1615,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char /* Update mapping if the data has MOVED */ if (c->redir_type == REDIR_MOVED) { - cluster_update_slot(c TSRMLS_CC); + cluster_update_slot(c); c->cmd_sock = SLOT_SOCK(c, slot); } } @@ -1630,7 +1630,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char return -1; } else if (timedout || resp == -1) { // Make sure the socket is reconnected, it such that it is in a clean state - redis_sock_disconnect(c->cmd_sock, 1 TSRMLS_CC); + redis_sock_disconnect(c->cmd_sock, 1); if (timedout) { CLUSTER_THROW_EXCEPTION( @@ -1666,7 +1666,7 @@ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, // Make sure we can read the response if (c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC)) == NULL) + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) { if (c->flags->mode != MULTI) { RETURN_FALSE; @@ -1707,18 +1707,18 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // Make sure we can read the response if (c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC)) == NULL) + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) { CLUSTER_RETURN_FALSE(c); } if (CLUSTER_IS_ATOMIC(c)) { - if (!redis_unpack(c->flags, resp, c->reply_len, return_value TSRMLS_CC)) { + if (!redis_unpack(c->flags, resp, c->reply_len, return_value)) { CLUSTER_RETURN_STRING(c, resp, c->reply_len); } } else { zval z_unpacked; - if (redis_unpack(c->flags, resp, c->reply_len, &z_unpacked TSRMLS_CC)) { + if (redis_unpack(c->flags, resp, c->reply_len, &z_unpacked)) { add_next_index_zval(&c->multi_resp, &z_unpacked); } else { add_next_index_stringl(&c->multi_resp, resp, c->reply_len); @@ -1736,7 +1736,7 @@ PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * // Make sure we can read the response if (c->reply_type != TYPE_BULK || - (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC)) == NULL) + (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) { CLUSTER_RETURN_FALSE(c); } @@ -1915,7 +1915,7 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * sctx->cb.param_count = tab_idx; // Execute our callback - if (zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC) != + if (zend_call_function(&(sctx->cb), &(sctx->cb_cache)) != SUCCESS) { break; @@ -2028,7 +2028,7 @@ cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int i; // Make sure we can read it - if ((r = cluster_read_resp(c, status_strings TSRMLS_CC)) == NULL) { + if ((r = cluster_read_resp(c, status_strings)) == NULL) { CLUSTER_RETURN_FALSE(c); } @@ -2140,7 +2140,7 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, c->cmd_sock->serializer = c->flags->serializer; /* Call our specified callback */ - if (cb(c->cmd_sock, &z_result, c->reply_len, ctx TSRMLS_CC) == FAILURE) { + if (cb(c->cmd_sock, &z_result, c->reply_len, ctx) == FAILURE) { zval_dtor(&z_result); CLUSTER_RETURN_FALSE(c); } @@ -2167,14 +2167,14 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * } // Read the BULK size - if (cluster_check_response(c, &c->reply_type TSRMLS_CC),0 || + if (cluster_check_response(c, &c->reply_type),0 || c->reply_type != TYPE_BULK) { return FAILURE; } // Read the iterator - if ((pit = redis_sock_read_bulk_reply(c->cmd_sock,c->reply_len TSRMLS_CC)) == NULL) + if ((pit = redis_sock_read_bulk_reply(c->cmd_sock,c->reply_len)) == NULL) { return FAILURE; } @@ -2184,7 +2184,7 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * efree(pit); // We'll need another MULTIBULK response for the payload - if (cluster_check_response(c, &c->reply_type TSRMLS_CC) < 0) + if (cluster_check_response(c, &c->reply_type) < 0) { return FAILURE; } @@ -2219,7 +2219,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster char *info; // Read our bulk response - if ((info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC)) == NULL) + if ((info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) { CLUSTER_RETURN_FALSE(c); } @@ -2244,7 +2244,7 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC zval z_result; /* Read the bulk response */ - info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len TSRMLS_CC); + info = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len); if (info == NULL) { CLUSTER_RETURN_FALSE(c); } @@ -2270,7 +2270,7 @@ cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; - if (redis_read_stream_messages(c->cmd_sock, c->reply_len, &z_messages TSRMLS_CC) < 0) { + if (redis_read_stream_messages(c->cmd_sock, c->reply_len, &z_messages) < 0) { zval_dtor(&z_messages); CLUSTER_RETURN_FALSE(c); } @@ -2292,7 +2292,7 @@ cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; - if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, &z_streams TSRMLS_CC) < 0) { + if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, &z_streams) < 0) { zval_dtor(&z_streams); CLUSTER_RETURN_FALSE(c); } @@ -2311,7 +2311,7 @@ cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { array_init(&z_msg); - if (redis_read_xclaim_response(c->cmd_sock, c->reply_len, &z_msg TSRMLS_CC) < 0) { + if (redis_read_xclaim_response(c->cmd_sock, c->reply_len, &z_msg) < 0) { zval_dtor(&z_msg); CLUSTER_RETURN_FALSE(c); } @@ -2349,7 +2349,7 @@ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, ZVAL_NULL(z_ret); // Pull our next response if directed if (pull) { - if (cluster_check_response(c, &c->reply_type TSRMLS_CC) < 0) + if (cluster_check_response(c, &c->reply_type) < 0) { return NULL; } @@ -2363,7 +2363,7 @@ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, array_init(z_ret); // Call our callback - if (cb(c->cmd_sock, z_ret, c->reply_len, NULL TSRMLS_CC) == FAILURE) { + if (cb(c->cmd_sock, z_ret, c->reply_len, NULL) == FAILURE) { zval_dtor(z_ret); return NULL; } @@ -2388,7 +2388,7 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, c->cmd_slot = fi->slot; c->cmd_sock = SLOT_SOCK(c, fi->slot); - if (cluster_check_response(c, &c->reply_type TSRMLS_CC) < 0) { + if (cluster_check_response(c, &c->reply_type) < 0) { zval_dtor(multi_resp); RETURN_FALSE; } @@ -2417,7 +2417,7 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || - mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL TSRMLS_CC) == FAILURE; + mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL) == FAILURE; // If we had a failure, pad results with FALSE to indicate failure. Non // existant keys (e.g. for MGET will come back as NULL) @@ -2449,7 +2449,7 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluste // Protect against an invalid response type if (c->reply_type != TYPE_INT) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Invalid response type for MSETNX"); while (real_argc--) { add_next_index_bool(mctx->z_multi, 0); @@ -2483,7 +2483,7 @@ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * // If we get an invalid reply, inform the client if (c->reply_type != TYPE_INT) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Invalid reply type returned for DEL command"); efree(mctx); return; @@ -2513,7 +2513,7 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster // If we get an invalid reply type something very wrong has happened, // and we have to abort. if (c->reply_type != TYPE_LINE) { - php_error_docref(0 TSRMLS_CC, E_ERROR, + php_error_docref(0, E_ERROR, "Invalid reply type returned for MSET command"); zval_dtor(mctx->z_multi); efree(mctx->z_multi); @@ -2584,7 +2584,7 @@ cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* MULTI BULK response where we don't touch the values (e.g. KEYS) */ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC) + long long count, void *ctx) { char *line; int line_len; @@ -2592,7 +2592,7 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, // Iterate over the number we have while (count--) { // Read the line, which should never come back null - line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + line = redis_sock_read(redis_sock, &line_len); if (line == NULL) return FAILURE; // Add to our result array @@ -2606,7 +2606,7 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, /* MULTI BULK response where we unserialize everything */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC) + long long count, void *ctx) { char *line; int line_len; @@ -2614,11 +2614,11 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, /* Iterate over the lines we have to process */ while (count--) { /* Read our line */ - line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + line = redis_sock_read(redis_sock, &line_len); if (line != NULL) { zval z_unpacked; - if (redis_unpack(redis_sock, line, line_len, &z_unpacked TSRMLS_CC)) { + if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) { add_next_index_zval(z_result, &z_unpacked); } else { add_next_index_stringl(z_result, line, line_len); @@ -2635,7 +2635,7 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, /* MULTI BULK response where we turn key1,value1 into key1=>value1 */ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC) + long long count, void *ctx) { char *line, *key = NULL; int line_len, key_len = 0; @@ -2649,7 +2649,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, // Iterate through our elements while (count--) { // Grab our line, bomb out on failure - line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + line = redis_sock_read(redis_sock, &line_len); if (!line) return -1; if (idx++ % 2 == 0) { @@ -2659,7 +2659,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, } else { /* Attempt unpacking */ zval z_unpacked; - if (redis_unpack(redis_sock, line, line_len, &z_unpacked TSRMLS_CC)) { + if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) { add_assoc_zval(z_result, key, &z_unpacked); } else { add_assoc_stringl_ex(z_result, key, key_len, line, line_len); @@ -2675,7 +2675,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, /* MULTI BULK loop processor where we expect key,score key, score */ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC) + long long count, void *ctx) { char *line, *key = NULL; int line_len, key_len = 0; @@ -2688,14 +2688,14 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, // While we have elements while (count--) { - line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + line = redis_sock_read(redis_sock, &line_len); if (line != NULL) { if (idx++ % 2 == 0) { key = line; key_len = line_len; } else { zval zv, *z = &zv; - if (redis_unpack(redis_sock,key,key_len, z TSRMLS_CC)) { + if (redis_unpack(redis_sock,key,key_len, z)) { zend_string *zstr = zval_get_string(z); add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line)); zend_string_release(zstr); @@ -2716,7 +2716,7 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, /* MULTI BULK where we're passed the keys, and we attach vals */ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC) + long long count, void *ctx) { char *line; int line_len,i = 0; @@ -2725,11 +2725,11 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, // Loop while we've got replies while (count--) { zend_string *zstr = zval_get_string(&z_keys[i]); - line = redis_sock_read(redis_sock, &line_len TSRMLS_CC); + line = redis_sock_read(redis_sock, &line_len); if (line != NULL) { zval z_unpacked; - if (redis_unpack(redis_sock, line, line_len, &z_unpacked TSRMLS_CC)) { + if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) { add_assoc_zval_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked); } else { add_assoc_stringl_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), line, line_len); diff --git a/cluster_library.h b/cluster_library.h index df83d96799..fa3bdc1108 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -62,12 +62,12 @@ /* Protected sending of data down the wire to a RedisSock->stream */ #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \ - (sock && !cluster_sock_open(sock TSRMLS_CC) && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \ + (sock && !cluster_sock_open(sock) && sock->stream && !redis_check_eof(sock, 1 ) && \ php_stream_write(sock->stream, buf, len)==len) /* Macro to read our reply type character */ #define CLUSTER_VALIDATE_REPLY_TYPE(sock, type) \ - (redis_check_eof(sock, 1 TSRMLS_CC) == 0 && \ + (redis_check_eof(sock, 1) == 0 && \ (php_stream_getc(sock->stream) == type)) /* Reset our last single line reply buffer and length */ @@ -141,7 +141,7 @@ typedef enum CLUSTER_REDIR_TYPE { } CLUSTER_REDIR_TYPE; /* MULTI BULK response callback typedef */ -typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void* TSRMLS_DC); +typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void*); /* A list of covered slot ranges */ typedef struct redisSlotRange { @@ -340,9 +340,9 @@ typedef struct clusterReply { } clusterReply; /* Direct variant response handler */ -clusterReply *cluster_read_resp(redisCluster *c, int status_strings TSRMLS_DC); +clusterReply *cluster_read_resp(redisCluster *c, int status_strings); clusterReply *cluster_read_sock_resp(RedisSock *redis_sock, - REDIS_REPLY_TYPE type, char *line_reply, size_t reply_len TSRMLS_DC); + REDIS_REPLY_TYPE type, char *line_reply, size_t reply_len); void cluster_free_reply(clusterReply *reply, int free_data); /* Cluster distribution helpers for WATCH */ @@ -351,7 +351,7 @@ void cluster_dist_free(HashTable *ht); int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, size_t key_len, clusterKeyVal **kv); void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val - TSRMLS_DC); + ); /* Aggregation for multi commands like MGET, MSET, and MSETNX */ void cluster_multi_init(clusterMultiCmd *mc, char *kw, int kw_len); @@ -367,25 +367,25 @@ unsigned short cluster_hash_key(const char *key, int len); long long mstime(void); PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd, - int cmd_len TSRMLS_DC); + int cmd_len); -PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force TSRMLS_DC); +PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force); -PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot TSRMLS_DC); -PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot TSRMLS_DC); -PHP_REDIS_API int cluster_abort_exec(redisCluster *c TSRMLS_DC); +PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot); +PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot); +PHP_REDIS_API int cluster_abort_exec(redisCluster *c); PHP_REDIS_API int cluster_reset_multi(redisCluster *c); PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, unsigned short port); PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, - int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC); + int cmd_len, REDIS_REPLY_TYPE rtype); PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, int failover, int persistent); -PHP_REDIS_API void cluster_free(redisCluster *c, int free_ctx TSRMLS_DC); +PHP_REDIS_API void cluster_free(redisCluster *c, int free_ctx); PHP_REDIS_API int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds); -PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC); +PHP_REDIS_API int cluster_map_keyspace(redisCluster *c); PHP_REDIS_API void cluster_free_node(redisClusterNode *node); /* Functions for interacting with cached slots maps */ @@ -396,7 +396,7 @@ PHP_REDIS_API void cluster_init_cache(redisCluster *c, redisCachedCluster *rcc); /* Functions to facilitate cluster slot caching */ PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, - int *len TSRMLS_DC); + int *len); /* * Redis Cluster response handlers. Our response handlers generally take the @@ -492,15 +492,15 @@ PHP_REDIS_API void cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, /* MULTI BULK processing callbacks */ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC); + long long count, void *ctx); int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC); + long long count, void *ctx); int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC); + long long count, void *ctx); int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC); + long long count, void *ctx); int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx TSRMLS_DC); + long long count, void *ctx); #endif diff --git a/common.h b/common.h index 2975c39a98..45e8768fdc 100644 --- a/common.h +++ b/common.h @@ -127,7 +127,7 @@ typedef enum { } while (0) #define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) \ - if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { \ + if(redis_sock_write(redis_sock, cmd, cmd_len) < 0) { \ efree(cmd); \ RETURN_FALSE; \ } @@ -156,7 +156,7 @@ typedef enum { #define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ if (!IS_PIPELINE(redis_sock)) { \ - if (redis_response_enqueued(redis_sock TSRMLS_CC) != SUCCESS) { \ + if (redis_response_enqueued(redis_sock) != SUCCESS) { \ RETURN_FALSE; \ } \ } \ @@ -177,7 +177,7 @@ typedef enum { * function is redis__cmd */ #define REDIS_PROCESS_CMD(cmdname, resp_func) \ RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || \ + if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || \ redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, \ &cmd, &cmd_len, NULL, &ctx)==FAILURE) { \ RETURN_FALSE; \ @@ -193,7 +193,7 @@ typedef enum { * and keyword which is passed to us*/ #define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \ - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || \ + if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || \ cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, \ &cmd_len, NULL, &ctx)==FAILURE) { \ RETURN_FALSE; \ diff --git a/library.c b/library.c index ab02c3fcef..5b1a238e0a 100644 --- a/library.c +++ b/library.c @@ -58,7 +58,7 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; static ConnectionPool * -redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) +redis_sock_get_connection_pool(RedisSock *redis_sock) { zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); @@ -75,21 +75,21 @@ redis_sock_get_connection_pool(RedisSock *redis_sock TSRMLS_DC) } /* Helper to reselect the proper DB number when we reconnect */ -static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { +static int reselect_db(RedisSock *redis_sock) { char *cmd, *response; int cmd_len, response_len; - cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, &cmd, "SELECT", "d", + cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "SELECT", "d", redis_sock->dbNumber); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); return -1; } efree(cmd); - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { return -1; } @@ -104,22 +104,22 @@ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) { /* Helper to resend AUTH in the case of a reconnect */ PHP_REDIS_API int -redis_sock_auth(RedisSock *redis_sock TSRMLS_DC) +redis_sock_auth(RedisSock *redis_sock) { char *cmd, *response; int cmd_len, response_len; - cmd_len = redis_spprintf(redis_sock, NULL TSRMLS_CC, &cmd, "AUTH", "s", + cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "s", ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); return -1; } efree(cmd); - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + response = redis_sock_read(redis_sock, &response_len); if (response == NULL) { return -1; } @@ -148,7 +148,7 @@ static int redis_sock_errcmp(RedisSock *redis_sock, const char *err, size_t errl * 3) LOADING */ static void -redis_error_throw(RedisSock *redis_sock TSRMLS_DC) +redis_error_throw(RedisSock *redis_sock) { /* Short circuit if we have no redis_sock or any error */ if (redis_sock == NULL || redis_sock->err == NULL) @@ -168,7 +168,7 @@ redis_error_throw(RedisSock *redis_sock TSRMLS_DC) } PHP_REDIS_API int -redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) +redis_check_eof(RedisSock *redis_sock, int no_throw) { int count; char *errmsg; @@ -206,26 +206,26 @@ redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) for (count = 0; count < 10; ++count) { /* close existing stream before reconnecting */ if (redis_sock->stream) { - redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); + redis_sock_disconnect(redis_sock, 1); } // Wait for a while before trying to reconnect if (redis_sock->retry_interval) { // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time - long retry_interval = (count ? redis_sock->retry_interval : (php_rand(TSRMLS_C) % redis_sock->retry_interval)); + long retry_interval = (count ? redis_sock->retry_interval : (php_rand() % redis_sock->retry_interval)); usleep(retry_interval); } /* reconnect */ - if (redis_sock_connect(redis_sock TSRMLS_CC) == 0) { + if (redis_sock_connect(redis_sock) == 0) { /* check for EOF again. */ errno = 0; if (php_stream_eof(redis_sock->stream) == 0) { /* If we're using a password, attempt a reauthorization */ - if (redis_sock->auth && redis_sock_auth(redis_sock TSRMLS_CC) != 0) { + if (redis_sock->auth && redis_sock_auth(redis_sock) != 0) { errmsg = "AUTH failed while reconnecting"; break; } /* If we're using a non-zero db, reselect it */ - if (redis_sock->dbNumber && reselect_db(redis_sock TSRMLS_CC) != 0) { + if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) { errmsg = "SELECT failed while reconnecting"; break; } @@ -236,7 +236,7 @@ redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC) } } /* close stream and mark socket as failed */ - redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); + redis_sock_disconnect(redis_sock, 1); redis_sock->status = REDIS_SOCK_STATUS_FAILED; if (!no_throw) { REDIS_THROW_EXCEPTION( errmsg, 0); @@ -254,21 +254,21 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *p_iter; /* Our response should have two multibulk replies */ - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0 + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info)<0 || reply_type != TYPE_MULTIBULK || reply_info != 2) { return -1; } /* The BULK response iterator */ - if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0 + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info)<0 || reply_type != TYPE_BULK) { return -1; } /* Attempt to read the iterator */ - if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info TSRMLS_CC))) { + if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info))) { return -1; } @@ -389,7 +389,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, sctx->cb.param_count = tab_idx; // Execute callback - if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC) + if(zend_call_function(&(sctx->cb), &(sctx->cb_cache)) ==FAILURE) { break; @@ -448,7 +448,7 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, size_t len; ZVAL_NULL(z_tab); - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return NULL; } @@ -459,7 +459,7 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, array_init(z_tab); - redis_mbulk_reply_loop(redis_sock, z_tab, numElems, UNSERIALIZE_ALL TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, z_tab, numElems, UNSERIALIZE_ALL); return z_tab; } @@ -468,13 +468,13 @@ redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, * redis_sock_read_bulk_reply */ PHP_REDIS_API char * -redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) +redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes) { int offset = 0, nbytes; char *reply; size_t got; - if (-1 == bytes || -1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { + if (-1 == bytes || -1 == redis_check_eof(redis_sock, 0)) { return NULL; } @@ -507,13 +507,13 @@ redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC) * redis_sock_read */ PHP_REDIS_API char * -redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) +redis_sock_read(RedisSock *redis_sock, int *buf_len) { char inbuf[4096]; size_t len; *buf_len = 0; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return NULL; } @@ -522,12 +522,12 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC) redis_sock_set_err(redis_sock, inbuf+1, len); /* Filter our ERROR through the few that should actually throw */ - redis_error_throw(redis_sock TSRMLS_CC); + redis_error_throw(redis_sock); return NULL; case '$': *buf_len = atoi(inbuf + 1); - return redis_sock_read_bulk_reply(redis_sock, *buf_len TSRMLS_CC); + return redis_sock_read_bulk_reply(redis_sock, *buf_len); case '*': /* For null multi-bulk replies (like timeouts from brpoplpush): */ @@ -581,7 +581,7 @@ union resparg { * L - Alias to 'l' */ PHP_REDIS_API int -redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *kw, char *fmt, ...) { +redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...) { smart_string cmd = {0}; va_list ap; union resparg arg; @@ -615,7 +615,7 @@ redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *k break; case 'v': arg.zv = va_arg(ap, zval*); - argfree = redis_pack(redis_sock, arg.zv, &dup, &arglen TSRMLS_CC); + argfree = redis_pack(redis_sock, arg.zv, &dup, &arglen); redis_cmd_append_sstr(&cmd, dup, arglen); if (argfree) efree(dup); break; @@ -727,12 +727,12 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value) /* Append a zval to a redis command. The value will be serialized if we are * configured to do that */ -int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC) { +int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock) { char *val; size_t vallen; int valfree, retval; - valfree = redis_pack(redis_sock, z, &val, &vallen TSRMLS_CC); + valfree = redis_pack(redis_sock, z, &val, &vallen); retval = redis_cmd_append_sstr(str, val, vallen); if (valfree) efree(val); @@ -776,7 +776,7 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi int response_len; double ret; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } @@ -798,7 +798,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * int response_len; long l; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } @@ -836,7 +836,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * zval z_ret; /* Read bulk response */ - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { RETURN_FALSE; } @@ -914,7 +914,7 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo zval z_ret; /* Make sure we can read the bulk response from Redis */ - if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { + if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) { RETURN_FALSE; } @@ -1037,7 +1037,7 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int response_len; zend_bool ret = 0; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) != NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) { ret = (*response == '+'); efree(response); } @@ -1068,7 +1068,7 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { @@ -1108,7 +1108,7 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, * key => value] when returning data to the caller. Depending on our decode * flag we'll convert the value data types */ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, - int decode TSRMLS_DC) + int decode) { zval z_ret, z_sub; @@ -1161,13 +1161,13 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, } static int -read_mbulk_header(RedisSock *redis_sock, int *nelem TSRMLS_DC) +read_mbulk_header(RedisSock *redis_sock, int *nelem) { char line[4096]; size_t len; /* Throws exception on failure */ - if (redis_sock_gets(redis_sock, line, sizeof(line)-1, &len TSRMLS_CC) < 0) + if (redis_sock_gets(redis_sock, line, sizeof(line)-1, &len) < 0) return -1; if (line[0] != '*') { @@ -1192,7 +1192,7 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int numElems; size_t len; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return -1; } @@ -1209,10 +1209,10 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, array_init(&z_multi_result); /* pre-allocate array for multi's results. */ /* Grab our key, value, key, value array */ - redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize); /* Zip keys and values */ - array_zip_values_and_scores(redis_sock, &z_multi_result, decode TSRMLS_CC); + array_zip_values_and_scores(redis_sock, &z_multi_result, decode); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_multi_result, 0, 1); @@ -1226,18 +1226,18 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Consume message ID */ PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen, - size_t *linelen, int set_err TSRMLS_DC) + size_t *linelen, int set_err) { REDIS_REPLY_TYPE type; long info; - if (redis_read_reply_type(redis_sock, &type, &info TSRMLS_CC) < 0 || + if (redis_read_reply_type(redis_sock, &type, &info) < 0 || (type != TYPE_LINE && type != TYPE_ERR)) { return -1; } - if (redis_sock_gets(redis_sock, buffer, buflen, linelen TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, buffer, buflen, linelen) < 0) { return -1; } @@ -1254,7 +1254,7 @@ redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen, * multiple stream callers (e.g. XREAD[GROUP], and X[REV]RANGE handlers). */ PHP_REDIS_API int redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret - TSRMLS_DC) + ) { zval z_message; int i, mhdr, fields; @@ -1265,9 +1265,9 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret for (i = 0; i < count; i++) { /* Consume inner multi-bulk header, message ID itself and finaly * the multi-bulk header for field and values */ - if ((read_mbulk_header(redis_sock, &mhdr TSRMLS_CC) < 0 || mhdr != 2) || - ((id = redis_sock_read(redis_sock, &idlen TSRMLS_CC)) == NULL) || - (read_mbulk_header(redis_sock, &fields TSRMLS_CC) < 0 || fields % 2 != 0)) + if ((read_mbulk_header(redis_sock, &mhdr) < 0 || mhdr != 2) || + ((id = redis_sock_read(redis_sock, &idlen)) == NULL) || + (read_mbulk_header(redis_sock, &fields) < 0 || fields % 2 != 0)) { if (id) efree(id); return -1; @@ -1275,8 +1275,8 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret array_init(&z_message); - redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS TSRMLS_CC); - array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS); + array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE); add_assoc_zval_ex(z_ret, id, idlen, &z_message); efree(id); } @@ -1293,8 +1293,8 @@ redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, array_init(&z_messages); - if (read_mbulk_header(redis_sock, &messages TSRMLS_CC) < 0 || - redis_read_stream_messages(redis_sock, messages, &z_messages TSRMLS_CC) < 0) + if (read_mbulk_header(redis_sock, &messages) < 0 || + redis_read_stream_messages(redis_sock, messages, &z_messages) < 0) { zval_dtor(&z_messages); if (IS_ATOMIC(redis_sock)) { @@ -1316,7 +1316,7 @@ redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, PHP_REDIS_API int redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_streams - TSRMLS_DC) + ) { zval z_messages; int i, shdr, messages; @@ -1324,9 +1324,9 @@ redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_strea int idlen; for (i = 0; i < count; i++) { - if ((read_mbulk_header(redis_sock, &shdr TSRMLS_CC) < 0 || shdr != 2) || - (id = redis_sock_read(redis_sock, &idlen TSRMLS_CC)) == NULL || - read_mbulk_header(redis_sock, &messages TSRMLS_CC) < 0) + if ((read_mbulk_header(redis_sock, &shdr) < 0 || shdr != 2) || + (id = redis_sock_read(redis_sock, &idlen)) == NULL || + read_mbulk_header(redis_sock, &messages) < 0) { if (id) efree(id); return -1; @@ -1334,7 +1334,7 @@ redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_strea array_init(&z_messages); - if (redis_read_stream_messages(redis_sock, messages, &z_messages TSRMLS_CC) < 0) + if (redis_read_stream_messages(redis_sock, messages, &z_messages) < 0) goto failure; add_assoc_zval_ex(z_streams, id, idlen, &z_messages); @@ -1355,12 +1355,12 @@ redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval z_rv; int streams; - if (read_mbulk_header(redis_sock, &streams TSRMLS_CC) < 0) + if (read_mbulk_header(redis_sock, &streams) < 0) goto failure; array_init(&z_rv); - if (redis_read_stream_messages_multi(redis_sock, streams, &z_rv TSRMLS_CC) < 0) + if (redis_read_stream_messages_multi(redis_sock, streams, &z_rv) < 0) goto cleanup; if (IS_ATOMIC(redis_sock)) { @@ -1385,7 +1385,7 @@ redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * Redis and RedisCluster. Note that XCLAIM is somewhat unique in that its reply type depends * on whether or not it was called with the JUSTID option */ PHP_REDIS_API int -redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) { +redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv) { zval z_msg; REDIS_REPLY_TYPE type; char *id = NULL; @@ -1394,20 +1394,20 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) for (i = 0; i < count; i++) { /* Consume inner reply type */ - if (redis_read_reply_type(redis_sock, &type, &li TSRMLS_CC) < 0 || + if (redis_read_reply_type(redis_sock, &type, &li) < 0 || (type != TYPE_BULK && type != TYPE_MULTIBULK) || (type == TYPE_BULK && li <= 0)) return -1; /* TYPE_BULK is the JUSTID variant, otherwise it's standard xclaim response */ if (type == TYPE_BULK) { - if ((id = redis_sock_read_bulk_reply(redis_sock, (size_t)li TSRMLS_CC)) == NULL) + if ((id = redis_sock_read_bulk_reply(redis_sock, (size_t)li)) == NULL) return -1; add_next_index_stringl(rv, id, li); efree(id); } else { - if ((li != 2 || (id = redis_sock_read(redis_sock, &idlen TSRMLS_CC)) == NULL) || - (read_mbulk_header(redis_sock, &fields TSRMLS_CC) < 0 || fields % 2 != 0)) + if ((li != 2 || (id = redis_sock_read(redis_sock, &idlen)) == NULL) || + (read_mbulk_header(redis_sock, &fields) < 0 || fields % 2 != 0)) { if (id) efree(id); return -1; @@ -1415,8 +1415,8 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC) array_init(&z_msg); - redis_mbulk_reply_loop(redis_sock, &z_msg, fields, UNSERIALIZE_VALS TSRMLS_CC); - array_zip_values_and_scores(redis_sock, &z_msg, SCORE_DECODE_NONE TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, &z_msg, fields, UNSERIALIZE_VALS); + array_zip_values_and_scores(redis_sock, &z_msg, SCORE_DECODE_NONE); add_assoc_zval_ex(rv, id, idlen, &z_msg); efree(id); } @@ -1433,12 +1433,12 @@ redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int messages; /* All XCLAIM responses start multibulk */ - if (read_mbulk_header(redis_sock, &messages TSRMLS_CC) < 0) + if (read_mbulk_header(redis_sock, &messages) < 0) goto failure; array_init(&z_ret); - if (redis_read_xclaim_response(redis_sock, messages, &z_ret TSRMLS_CC) < 0) { + if (redis_read_xclaim_response(redis_sock, messages, &z_ret) < 0) { zval_dtor(&z_ret); goto failure; } @@ -1469,12 +1469,12 @@ redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements) long li; for (i = 0; i < elements; ++i) { - if (redis_read_reply_type(redis_sock, &type, &li TSRMLS_CC) < 0) { + if (redis_read_reply_type(redis_sock, &type, &li) < 0) { goto failure; } switch (type) { case TYPE_BULK: - if ((data = redis_sock_read_bulk_reply(redis_sock, li TSRMLS_CC)) == NULL) { + if ((data = redis_sock_read_bulk_reply(redis_sock, li)) == NULL) { goto failure; } else if (key) { add_assoc_stringl_ex(z_ret, key, len, data, li); @@ -1527,9 +1527,9 @@ redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_t zval z_ret; int elements; - if (read_mbulk_header(redis_sock, &elements TSRMLS_CC) == SUCCESS) { + if (read_mbulk_header(redis_sock, &elements) == SUCCESS) { array_init(&z_ret); - if (redis_read_xinfo_response(redis_sock, &z_ret, elements TSRMLS_CC) == SUCCESS) { + if (redis_read_xinfo_response(redis_sock, &z_ret, elements) == SUCCESS) { if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { @@ -1585,7 +1585,7 @@ redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ta int response_len; zend_bool ret = 0; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) != NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) { ret = (response[1] == '1'); efree(response); } @@ -1602,7 +1602,7 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { @@ -1612,12 +1612,12 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock return; } if (IS_ATOMIC(redis_sock)) { - if (!redis_unpack(redis_sock, response, response_len, return_value TSRMLS_CC)) { + if (!redis_unpack(redis_sock, response, response_len, return_value)) { RETVAL_STRINGL(response, response_len); } } else { zval z_unpacked; - if (redis_unpack(redis_sock, response, response_len, &z_unpacked TSRMLS_CC)) { + if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) { add_next_index_zval(z_tab, &z_unpacked); } else { add_next_index_stringl(z_tab, response, response_len); @@ -1633,7 +1633,7 @@ void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock char buffer[4096]; size_t len; - if (redis_sock_read_single_line(redis_sock, buffer, sizeof(buffer), &len, 1 TSRMLS_CC) < 0) { + if (redis_sock_read_single_line(redis_sock, buffer, sizeof(buffer), &len, 1) < 0) { if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } else { @@ -1659,7 +1659,7 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { if (IS_ATOMIC(redis_sock)) { @@ -1684,7 +1684,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock int is_numeric, resp_len; /* Add or return false if we can't read from the socket */ - if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC))==NULL) { + if((resp = redis_sock_read(redis_sock, &resp_len))==NULL) { if (IS_ATOMIC(redis_sock)) { RETURN_FALSE; } @@ -1789,7 +1789,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, /** * redis_sock_connect */ -PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) +PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) { struct timeval tv, read_tv, *tv_ptr = NULL; zend_string *persistent_id = NULL, *estr = NULL; @@ -1799,7 +1799,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) ConnectionPool *p = NULL; if (redis_sock->stream != NULL) { - redis_sock_disconnect(redis_sock, 0 TSRMLS_CC); + redis_sock_disconnect(redis_sock, 0); } address = ZSTR_VAL(redis_sock->host); @@ -1827,7 +1827,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { - p = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); + p = redis_sock_get_connection_pool(redis_sock); if (zend_llist_count(&p->list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list); zend_llist_remove_tail(&p->list); @@ -1912,14 +1912,14 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC) * redis_sock_server_open */ PHP_REDIS_API int -redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC) +redis_sock_server_open(RedisSock *redis_sock) { if (redis_sock) { switch (redis_sock->status) { case REDIS_SOCK_STATUS_FAILED: return FAILURE; case REDIS_SOCK_STATUS_DISCONNECTED: - return redis_sock_connect(redis_sock TSRMLS_CC); + return redis_sock_connect(redis_sock); default: return SUCCESS; } @@ -1931,7 +1931,7 @@ redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC) * redis_sock_disconnect */ PHP_REDIS_API int -redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) +redis_sock_disconnect(RedisSock *redis_sock, int force) { if (redis_sock == NULL) { return FAILURE; @@ -1939,7 +1939,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC) if (redis_sock->persistent) { ConnectionPool *p = NULL; if (INI_INT("redis.pconnect.pooling_enabled")) { - p = redis_sock_get_connection_pool(redis_sock TSRMLS_CC); + p = redis_sock_get_connection_pool(redis_sock); } if (force) { php_stream_pclose(redis_sock->stream); @@ -1988,7 +1988,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, int numElems; size_t len; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return -1; } @@ -2008,7 +2008,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, zval z_multi_result; array_init(&z_multi_result); /* pre-allocate array for multi's results. */ - redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_multi_result, 0, 1); @@ -2029,7 +2029,7 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval int numElems; size_t len; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return -1; } @@ -2048,7 +2048,7 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval zval z_multi_result; array_init(&z_multi_result); /* pre-allocate array for multi's results. */ - redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE TSRMLS_CC); + redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE); if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_multi_result, 0, 1); @@ -2061,14 +2061,14 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, - int unserialize TSRMLS_DC) + int unserialize) { zval z_unpacked; char *line; int i, len; for (i = 0; i < count; ++i) { - if ((line = redis_sock_read(redis_sock, &len TSRMLS_CC)) == NULL) { + if ((line = redis_sock_read(redis_sock, &len)) == NULL) { add_next_index_bool(z_tab, 0); continue; } @@ -2082,7 +2082,7 @@ redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, (unserialize == UNSERIALIZE_VALS && i % 2 != 0) ); - if (unwrap && redis_unpack(redis_sock, line, len, &z_unpacked TSRMLS_CC)) { + if (unwrap && redis_unpack(redis_sock, line, len, &z_unpacked)) { add_next_index_zval(z_tab, &z_unpacked); } else { add_next_index_stringl(z_tab, line, len); @@ -2102,7 +2102,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc zval *z_keys = ctx; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return -1; } @@ -2120,10 +2120,10 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc for(i = 0; i < numElems; ++i) { zend_string *zstr = zval_get_string(&z_keys[i]); - response = redis_sock_read(redis_sock, &response_len TSRMLS_CC); + response = redis_sock_read(redis_sock, &response_len); if(response != NULL) { zval z_unpacked; - if (redis_unpack(redis_sock, response, response_len, &z_unpacked TSRMLS_CC)) { + if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) { add_assoc_zval_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked); } else { add_assoc_stringl_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), response, response_len); @@ -2149,9 +2149,9 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc * redis_sock_write */ PHP_REDIS_API int -redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC) +redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz) { - if (redis_check_eof(redis_sock, 0 TSRMLS_CC) == 0 && + if (redis_check_eof(redis_sock, 0) == 0 && php_stream_write(redis_sock->stream, cmd, sz) == sz ) { return sz; @@ -2186,13 +2186,13 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) } PHP_REDIS_API int -redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC) +redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) { char *buf; int valfree; size_t len; - valfree = redis_serialize(redis_sock, z, &buf, &len TSRMLS_CC); + valfree = redis_serialize(redis_sock, z, &buf, &len); switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: #ifdef HAVE_REDIS_LZF @@ -2254,7 +2254,7 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC } PHP_REDIS_API int -redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC) +redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret) { switch (redis_sock->compression) { case REDIS_COMPRESSION_LZF: @@ -2273,7 +2273,7 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TS /* errno != E2BIG will brake for loop */ efree(data); continue; - } else if (redis_unserialize(redis_sock, data, res, z_ret TSRMLS_CC) == 0) { + } else if (redis_unserialize(redis_sock, data, res, z_ret) == 0) { ZVAL_STRINGL(z_ret, data, res); } efree(data); @@ -2295,7 +2295,7 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TS if (ZSTD_isError(len)) { efree(data); break; - } else if (redis_unserialize(redis_sock, data, len, z_ret TSRMLS_CC) == 0) { + } else if (redis_unserialize(redis_sock, data, len, z_ret) == 0) { ZVAL_STRINGL(z_ret, data, len); } efree(data); @@ -2305,12 +2305,12 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TS #endif break; } - return redis_unserialize(redis_sock, val, val_len, z_ret TSRMLS_CC); + return redis_unserialize(redis_sock, val, val_len, z_ret); } PHP_REDIS_API int redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len - TSRMLS_DC) + ) { php_serialize_data_t ht; @@ -2363,7 +2363,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len case REDIS_SERIALIZER_MSGPACK: #ifdef HAVE_REDIS_MSGPACK - php_msgpack_serialize(&sstr, z TSRMLS_CC); + php_msgpack_serialize(&sstr, z); *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); *val_len = ZSTR_LEN(sstr.s); smart_str_free(&sstr); @@ -2373,7 +2373,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len break; case REDIS_SERIALIZER_IGBINARY: #ifdef HAVE_REDIS_IGBINARY - if(igbinary_serialize(&val8, (size_t *)&sz, z TSRMLS_CC) == 0) { + if(igbinary_serialize(&val8, (size_t *)&sz, z) == 0) { *val = (char*)val8; *val_len = sz; return 1; @@ -2397,7 +2397,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len PHP_REDIS_API int redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, - zval *z_ret TSRMLS_DC) + zval *z_ret) { php_unserialize_data_t var_hash; @@ -2419,7 +2419,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, case REDIS_SERIALIZER_MSGPACK: #ifdef HAVE_REDIS_MSGPACK - ret = !php_msgpack_unserialize(z_ret, (char *)val, (size_t)val_len TSRMLS_CC); + ret = !php_msgpack_unserialize(z_ret, (char *)val, (size_t)val_len); #endif break; @@ -2449,7 +2449,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, break; } - ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, z_ret TSRMLS_CC); + ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, z_ret); #endif break; case REDIS_SERIALIZER_JSON: @@ -2494,10 +2494,10 @@ redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len) { PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, - size_t *line_size TSRMLS_DC) + size_t *line_size) { // Handle EOF - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0)) { return -1; } @@ -2512,7 +2512,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, spprintf(&errmsg, 0, "read error on connection to %s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); } // Close our socket - redis_sock_disconnect(redis_sock, 1 TSRMLS_CC); + redis_sock_disconnect(redis_sock, 1); // Throw a read error exception REDIS_THROW_EXCEPTION(errmsg, 0); @@ -2530,10 +2530,10 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, - long *reply_info TSRMLS_DC) + long *reply_info) { // Make sure we haven't lost the connection, even trying to reconnect - if(-1 == redis_check_eof(redis_sock, 0 TSRMLS_CC)) { + if(-1 == redis_check_eof(redis_sock, 0)) { // Failure *reply_type = EOF; return -1; @@ -2571,21 +2571,21 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, */ static int redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, - int as_string, zval *z_ret TSRMLS_DC) + int as_string, zval *z_ret) { // Buffer to read our single line reply char inbuf[4096]; size_t len; /* Attempt to read our single line reply */ - if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len TSRMLS_CC) < 0) { + if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) { return -1; } /* Throw exception on SYNC error otherwise just set error string */ if(reply_type == TYPE_ERR) { redis_sock_set_err(redis_sock, inbuf, len); - redis_error_throw(redis_sock TSRMLS_CC); + redis_error_throw(redis_sock); ZVAL_FALSE(z_ret); } else if (as_string) { ZVAL_STRINGL(z_ret, inbuf, len); @@ -2598,10 +2598,10 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret - TSRMLS_DC) + ) { // Attempt to read the bulk reply - char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size TSRMLS_CC); + char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size); /* Set our reply to FALSE on failure, and the string on success */ if(bulk_resp == NULL) { @@ -2615,7 +2615,7 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_strings, - zval *z_ret TSRMLS_DC) + zval *z_ret) { long reply_info; REDIS_REPLY_TYPE reply_type; @@ -2625,7 +2625,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s while(elements > 0) { // Attempt to read our reply type if(redis_read_reply_type(redis_sock, &reply_type, &reply_info - TSRMLS_CC) < 0) + ) < 0) { zend_throw_exception_ex(redis_exception_ce, 0, "protocol error, couldn't parse MULTI-BULK response\n"); @@ -2637,7 +2637,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s case TYPE_ERR: case TYPE_LINE: redis_read_variant_line(redis_sock, reply_type, status_strings, - &z_subelem TSRMLS_CC); + &z_subelem); add_next_index_zval(z_ret, &z_subelem); break; case TYPE_INT: @@ -2646,7 +2646,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s break; case TYPE_BULK: // Init a zval for our bulk response, read and add it - redis_read_variant_bulk(redis_sock, reply_info, &z_subelem TSRMLS_CC); + redis_read_variant_bulk(redis_sock, reply_info, &z_subelem); add_next_index_zval(z_ret, &z_subelem); break; case TYPE_MULTIBULK: @@ -2654,7 +2654,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s array_init(&z_subelem); add_next_index_zval(z_ret, &z_subelem); redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, - &z_subelem TSRMLS_CC); + &z_subelem); break; default: // Stop the compiler from whinging @@ -2678,7 +2678,7 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval z_ret; // Attempt to read our header - if(redis_read_reply_type(redis_sock,&reply_type,&reply_info TSRMLS_CC) < 0) + if(redis_read_reply_type(redis_sock,&reply_type,&reply_info) < 0) { return -1; } @@ -2687,13 +2687,13 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, switch(reply_type) { case TYPE_ERR: case TYPE_LINE: - redis_read_variant_line(redis_sock, reply_type, status_strings, &z_ret TSRMLS_CC); + redis_read_variant_line(redis_sock, reply_type, status_strings, &z_ret); break; case TYPE_INT: ZVAL_LONG(&z_ret, reply_info); break; case TYPE_BULK: - redis_read_variant_bulk(redis_sock, reply_info, &z_ret TSRMLS_CC); + redis_read_variant_bulk(redis_sock, reply_info, &z_ret); break; case TYPE_MULTIBULK: /* Initialize an array for our multi-bulk response */ @@ -2702,7 +2702,7 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If we've got more than zero elements, parse our multi bulk // response recursively if (reply_info > -1) { - redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, &z_ret TSRMLS_CC); + redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, &z_ret); } break; default: diff --git a/library.h b/library.h index c60e4a69d6..8d844747bd 100644 --- a/library.h +++ b/library.h @@ -3,7 +3,7 @@ /* Non cluster command helper */ #define REDIS_SPPRINTF(ret, kw, fmt, ...) \ - redis_spprintf(redis_sock, NULL TSRMLS_CC, ret, kw, fmt, ##__VA_ARGS__) + redis_spprintf(redis_sock, NULL, ret, kw, fmt, ##__VA_ARGS__) #define REDIS_CMD_APPEND_SSTR_STATIC(sstr, str) \ redis_cmd_append_sstr(sstr, str, sizeof(str)-1); @@ -26,14 +26,14 @@ int redis_cmd_append_sstr_int(smart_string *str, int append); int redis_cmd_append_sstr_long(smart_string *str, long append); int redis_cmd_append_sstr_i64(smart_string *str, int64_t append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); -int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC); +int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock); int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, ulong idx); -PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot TSRMLS_DC, char **ret, char *kw, char *fmt, ...); +PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...); -PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_DC); -PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len TSRMLS_DC); +PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len); +PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len); PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx); typedef void (*SuccessCallback)(RedisSock *redis_sock); @@ -49,17 +49,17 @@ PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); -PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock TSRMLS_DC); -PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC); +PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock); +PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock); +PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock); +PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, - size_t buflen, size_t *linelen, int set_err TSRMLS_DC); -PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes TSRMLS_DC); + size_t buflen, size_t *linelen, int set_err); +PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes); PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); //PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize); -PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize TSRMLS_DC); +PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize); PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); @@ -86,39 +86,39 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC); -PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC); -PHP_REDIS_API RedisSock *redis_sock_get(zval *id TSRMLS_DC, int nothrow); +PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz); +PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw); +PHP_REDIS_API RedisSock *redis_sock_get(zval *id, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); PHP_REDIS_API int -redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC); +redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len); PHP_REDIS_API int redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len); PHP_REDIS_API int -redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); +redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret); -PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len TSRMLS_DC); -PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len); +PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret); PHP_REDIS_API int -redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret TSRMLS_DC); +redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret); PHP_REDIS_API int -redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_ret TSRMLS_DC); +redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_ret); PHP_REDIS_API int -redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv TSRMLS_DC); +redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv); PHP_REDIS_API int -redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements TSRMLS_DC); +redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements); /* * Variant Read methods, mostly to implement eval */ -PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info TSRMLS_DC); -PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret TSRMLS_DC); -PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_strings, zval *z_ret TSRMLS_DC); +PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info); +PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret); +PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_strings, zval *z_ret); PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/php_redis.h b/php_redis.h index 07c2e253aa..ad25b43c24 100644 --- a/php_redis.h +++ b/php_redis.h @@ -273,11 +273,11 @@ PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *unsub_cmd); -PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC); +PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock); -PHP_REDIS_API int get_flag(zval *object TSRMLS_DC); +PHP_REDIS_API int get_flag(zval *object); -PHP_REDIS_API void set_flag(zval *object, int new_flag TSRMLS_DC); +PHP_REDIS_API void set_flag(zval *object, int new_flag); PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, diff --git a/redis.c b/redis.c index 3d3a7e0809..b087ac4427 100644 --- a/redis.c +++ b/redis.c @@ -516,7 +516,7 @@ zend_object_handlers redis_object_handlers; /* Send a static DISCARD in case we're in MULTI mode. */ static int -redis_send_discard(RedisSock *redis_sock TSRMLS_DC) +redis_send_discard(RedisSock *redis_sock) { int result = FAILURE; char *cmd, *resp; @@ -526,8 +526,8 @@ redis_send_discard(RedisSock *redis_sock TSRMLS_DC) cmd_len = REDIS_SPPRINTF(&cmd, "DISCARD", ""); /* send our DISCARD command */ - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0 && - (resp = redis_sock_read(redis_sock,&resp_len TSRMLS_CC)) != NULL) + if (redis_sock_write(redis_sock, cmd, cmd_len) >= 0 && + (resp = redis_sock_read(redis_sock,&resp_len)) != NULL) { /* success if we get OK */ result = (resp_len == 3 && strncmp(resp,"+OK", 3) == 0) ? SUCCESS:FAILURE; @@ -568,21 +568,21 @@ free_redis_object(zend_object *object) { redis_object *redis = (redis_object *)((char *)(object) - XtOffsetOf(redis_object, std)); - zend_object_std_dtor(&redis->std TSRMLS_CC); + zend_object_std_dtor(&redis->std); if (redis->sock) { - redis_sock_disconnect(redis->sock, 0 TSRMLS_CC); + redis_sock_disconnect(redis->sock, 0); redis_free_socket(redis->sock); } } zend_object * -create_redis_object(zend_class_entry *ce TSRMLS_DC) +create_redis_object(zend_class_entry *ce) { redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(ce)); redis->sock = NULL; - zend_object_std_init(&redis->std, ce TSRMLS_CC); + zend_object_std_init(&redis->std, ce); object_properties_init(&redis->std, ce); memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers)); @@ -594,7 +594,7 @@ create_redis_object(zend_class_entry *ce TSRMLS_DC) } static zend_always_inline RedisSock * -redis_sock_get_instance(zval *id TSRMLS_DC, int no_throw) +redis_sock_get_instance(zval *id, int no_throw) { redis_object *redis; @@ -615,15 +615,15 @@ redis_sock_get_instance(zval *id TSRMLS_DC, int no_throw) * redis_sock_get */ PHP_REDIS_API RedisSock * -redis_sock_get(zval *id TSRMLS_DC, int no_throw) +redis_sock_get(zval *id, int no_throw) { RedisSock *redis_sock; - if ((redis_sock = redis_sock_get_instance(id TSRMLS_CC, no_throw)) == NULL) { + if ((redis_sock = redis_sock_get_instance(id, no_throw)) == NULL) { return NULL; } - if (redis_sock_server_open(redis_sock TSRMLS_CC) < 0) { + if (redis_sock_server_open(redis_sock) < 0) { if (!no_throw) { char *errmsg = NULL; if (redis_sock->port < 0) { @@ -650,9 +650,9 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) // If we can't grab our object, or get a socket, or we're not connected, // return NULL - if((zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if((zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) || - (redis_sock = redis_sock_get(object TSRMLS_CC, 1)) == NULL || + (redis_sock = redis_sock_get(object, 1)) == NULL || redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { return NULL; @@ -664,48 +664,48 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) /* Redis and RedisCluster objects share serialization/prefixing settings so * this is a generic function to add class constants to either */ -static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) { - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STRING"), REDIS_STRING TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_SET"), REDIS_SET TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STREAM"), REDIS_STREAM TSRMLS_CC); +static void add_class_constants(zend_class_entry *ce, int is_cluster) { + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STRING"), REDIS_STRING); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_SET"), REDIS_SET); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH); + zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STREAM"), REDIS_STREAM); /* Cluster doesn't support pipelining at this time */ if(!is_cluster) { - zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE); } /* Add common mode constants */ - zend_declare_class_constant_long(ce, ZEND_STRL("ATOMIC"), ATOMIC TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("MULTI"), MULTI TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("ATOMIC"), ATOMIC); + zend_declare_class_constant_long(ce, ZEND_STRL("MULTI"), MULTI); /* options */ - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_TCP_KEEPALIVE"), REDIS_OPT_TCP_KEEPALIVE TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_TCP_KEEPALIVE"), REDIS_OPT_TCP_KEEPALIVE); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_REPLY_LITERAL"), REDIS_OPT_REPLY_LITERAL); zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_LEVEL"), REDIS_OPT_COMPRESSION_LEVEL); /* serializer */ - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE); + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP); #ifdef HAVE_REDIS_IGBINARY - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY); #endif #ifdef HAVE_REDIS_MSGPACK - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK); #endif - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_JSON"), REDIS_SERIALIZER_JSON TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_JSON"), REDIS_SERIALIZER_JSON); /* compression */ - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_NONE"), REDIS_COMPRESSION_NONE TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_NONE"), REDIS_COMPRESSION_NONE); #ifdef HAVE_REDIS_LZF - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZF"), REDIS_COMPRESSION_LZF TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZF"), REDIS_COMPRESSION_LZF); #endif #ifdef HAVE_REDIS_ZSTD zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD"), REDIS_COMPRESSION_ZSTD); @@ -719,21 +719,21 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC) #endif /* scan options*/ - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN); + zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY); + zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY); /* Cluster option to allow for slave failover */ if (is_cluster) { - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SLAVE_FAILOVER"), REDIS_OPT_FAILOVER TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_NONE"), REDIS_FAILOVER_NONE TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_ERROR"), REDIS_FAILOVER_ERROR TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE TSRMLS_CC); - zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES TSRMLS_CC); + zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SLAVE_FAILOVER"), REDIS_OPT_FAILOVER); + zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_NONE"), REDIS_FAILOVER_NONE); + zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_ERROR"), REDIS_FAILOVER_ERROR); + zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE); + zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES); } - zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5 TSRMLS_CC); - zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6 TSRMLS_CC); + zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5); + zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6); } static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) @@ -768,17 +768,17 @@ PHP_MINIT_FUNCTION(redis) /* Redis class */ INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions); - redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC); + redis_ce = zend_register_internal_class(&redis_class_entry); redis_ce->create_object = create_redis_object; /* RedisArray class */ INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions); - redis_array_ce = zend_register_internal_class(&redis_array_class_entry TSRMLS_CC); + redis_array_ce = zend_register_internal_class(&redis_array_class_entry); redis_array_ce->create_object = create_redis_array_object; /* RedisCluster class */ INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_functions); - redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry TSRMLS_CC); + redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry); redis_cluster_ce->create_object = create_cluster_context; /* Register our cluster cache list item */ @@ -789,7 +789,7 @@ PHP_MINIT_FUNCTION(redis) /* Base Exception class */ exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1); if (exception_ce == NULL) { - exception_ce = zend_exception_get_default(TSRMLS_C); + exception_ce = zend_exception_get_default(); } /* RedisException class */ @@ -805,8 +805,8 @@ PHP_MINIT_FUNCTION(redis) &redis_cluster_exception_class_entry, exception_ce); /* Add shared class constants to Redis and RedisCluster objects */ - add_class_constants(redis_ce, 0 TSRMLS_CC); - add_class_constants(redis_cluster_ce, 1 TSRMLS_CC); + add_class_constants(redis_ce, 0); + add_class_constants(redis_cluster_ce, 1); #ifdef PHP_SESSION php_session_register_module(&ps_mod_redis); @@ -898,7 +898,7 @@ PHP_MINFO_FUNCTION(redis) Public constructor */ PHP_METHOD(Redis, __construct) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) { RETURN_FALSE; } } @@ -908,13 +908,13 @@ PHP_METHOD(Redis, __construct) Public Destructor */ PHP_METHOD(Redis,__destruct) { - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { + if(zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) { RETURN_FALSE; } // Grab our socket RedisSock *redis_sock; - if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 1)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis(), 1)) == NULL) { RETURN_FALSE; } @@ -923,7 +923,7 @@ PHP_METHOD(Redis,__destruct) { if (!IS_PIPELINE(redis_sock) && redis_sock->stream) { // Discard any multi commands, and free any callbacks that have been // queued - redis_send_discard(redis_sock TSRMLS_CC); + redis_send_discard(redis_sock); } free_reply_callbacks(redis_sock); } @@ -969,7 +969,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) persistent = 0; #endif - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|lds!ld", &object, redis_ce, &host, &host_len, &port, &timeout, &persistent_id, &persistent_id_len, &retry_interval, @@ -1006,14 +1006,14 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) redis = PHPREDIS_GET_OBJECT(redis_object, object); /* if there is a redis sock already we have to remove it */ if (redis->sock) { - redis_sock_disconnect(redis->sock, 0 TSRMLS_CC); + redis_sock_disconnect(redis->sock, 0); redis_free_socket(redis->sock); } redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent, persistent_id, retry_interval); - if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { + if (redis_sock_server_open(redis->sock) < 0) { if (redis->sock->err) { REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0); } @@ -1053,7 +1053,7 @@ PHP_METHOD(Redis, close) { RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (redis_sock_disconnect(redis_sock, 1 TSRMLS_CC) == SUCCESS) { + if (redis_sock_disconnect(redis_sock, 1) == SUCCESS) { RETURN_TRUE; } RETURN_FALSE; @@ -1195,13 +1195,13 @@ PHP_METHOD(Redis, mget) int arg_count; /* Make sure we have proper arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", &object, redis_ce, &z_args) == FAILURE) { RETURN_FALSE; } /* We'll need the socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -1515,7 +1515,7 @@ PHP_METHOD(Redis, sRandMember) RedisSock *redis_sock; // Grab our socket, validate call - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || + if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &cmd, &cmd_len, NULL, NULL, &have_count) == FAILURE) { @@ -1601,7 +1601,7 @@ PHP_METHOD(Redis, sort) { RedisSock *redis_sock; // Grab socket, handle command construction - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL || + if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &have_store, &cmd, &cmd_len, NULL, NULL) == FAILURE) { @@ -1632,7 +1632,7 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) smart_string cmd = {0}; /* Parse myriad of sort arguments */ - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|s!z!lls", &object, redis_ce, &key, &keylen, &pattern, &patternlen, &zget, &offset, &count, &store, &storelen) @@ -1642,7 +1642,7 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) } /* Ensure we're sorting something, and we can get context */ - if (keylen == 0 || !(redis_sock = redis_sock_get(object TSRMLS_CC, 0))) + if (keylen == 0 || !(redis_sock = redis_sock_get(object, 0))) RETURN_FALSE; /* Start calculating argc depending on input arguments */ @@ -1855,14 +1855,14 @@ PHP_METHOD(Redis, info) { size_t opt_len; int cmd_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|s", &object, redis_ce, &opt, &opt_len) == FAILURE) { RETURN_FALSE; } - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -1893,12 +1893,12 @@ PHP_METHOD(Redis, select) { int cmd_len; zend_long dbNumber; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, redis_ce, &dbNumber) == FAILURE) { RETURN_FALSE; } - if (dbNumber < 0 || (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if (dbNumber < 0 || (redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -1938,14 +1938,14 @@ void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) size_t keylen; zend_ulong idx; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", &object, redis_ce, &z_array) == FAILURE) { RETURN_FALSE; } /* Make sure we can get our socket, and we were not passed an empty array */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL || + if ((redis_sock = redis_sock_get(object, 0)) == NULL || zend_hash_num_elements(Z_ARRVAL_P(z_array)) == 0) { RETURN_FALSE; @@ -1965,7 +1965,7 @@ void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun) } /* Append our value */ - redis_cmd_append_sstr_zval(&cmd, zmem, redis_sock TSRMLS_CC); + redis_cmd_append_sstr_zval(&cmd, zmem, redis_sock); } ZEND_HASH_FOREACH_END(); REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); @@ -2016,7 +2016,7 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, RedisSock *redis_sock; int withscores = 0; - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) { RETURN_FALSE; } @@ -2319,7 +2319,7 @@ PHP_METHOD(Redis, multi) zval *object; zend_long multi_value = MULTI; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|l", &object, redis_ce, &multi_value) == FAILURE) { @@ -2328,14 +2328,14 @@ PHP_METHOD(Redis, multi) /* if the flag is activated, send the command, the reply will be "QUEUED" * or -ERR */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } if (multi_value == PIPELINE) { /* Cannot enter pipeline mode in a MULTI block */ if (IS_MULTI(redis_sock)) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate pipeline in multi mode!"); + php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!"); RETURN_FALSE; } @@ -2356,7 +2356,7 @@ PHP_METHOD(Redis, multi) } else { SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); - if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) { + if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) { RETURN_FALSE; } else if (strncmp(resp, "+OK", 3) != 0) { efree(resp); @@ -2367,7 +2367,7 @@ PHP_METHOD(Redis, multi) } } } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown mode sent to Redis::multi"); + php_error_docref(NULL, E_WARNING, "Unknown mode sent to Redis::multi"); RETURN_FALSE; } @@ -2381,12 +2381,12 @@ PHP_METHOD(Redis, discard) RedisSock *redis_sock; zval *object; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -2397,7 +2397,7 @@ PHP_METHOD(Redis, discard) redis_sock->pipeline_cmd = NULL; } } else if (IS_MULTI(redis_sock)) { - ret = redis_send_discard(redis_sock TSRMLS_CC); + ret = redis_send_discard(redis_sock); } if (ret == SUCCESS) { free_reply_callbacks(redis_sock); @@ -2416,7 +2416,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME int numElems; size_t len; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { return - 1; } @@ -2444,9 +2444,9 @@ PHP_METHOD(Redis, exec) int cmd_len, ret; zval *object; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE || - (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL + (redis_sock = redis_sock_get(object, 0)) == NULL ) { RETURN_FALSE; } @@ -2480,7 +2480,7 @@ PHP_METHOD(Redis, exec) array_init(return_value); } else { if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd), - ZSTR_LEN(redis_sock->pipeline_cmd) TSRMLS_CC) < 0) { + ZSTR_LEN(redis_sock->pipeline_cmd)) < 0) { ZVAL_FALSE(return_value); } else { array_init(return_value); @@ -2496,12 +2496,12 @@ PHP_METHOD(Redis, exec) } PHP_REDIS_API int -redis_response_enqueued(RedisSock *redis_sock TSRMLS_DC) +redis_response_enqueued(RedisSock *redis_sock) { char *resp; int resp_len, ret = FAILURE; - if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) != NULL) { + if ((resp = redis_sock_read(redis_sock, &resp_len)) != NULL) { if (strncmp(resp, "+QUEUED", 7) == 0) { ret = SUCCESS; } @@ -2522,24 +2522,24 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, for (fi = redis_sock->head; fi; /* void */) { if (fi->fun) { fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, - fi->ctx TSRMLS_CC); + fi->ctx); fi = fi->next; continue; } size_t len; char inbuf[255]; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { } else if (strncmp(inbuf, "+OK", 3) != 0) { } while ((fi = fi->next) && fi->fun) { - if (redis_response_enqueued(redis_sock TSRMLS_CC) == SUCCESS) { + if (redis_response_enqueued(redis_sock) == SUCCESS) { } else { } } - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { } zval z_ret; @@ -2548,7 +2548,7 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, int num = atol(inbuf + 1); - if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret TSRMLS_CC) < 0) { + if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) < 0) { } if (fi) fi = fi->next; @@ -2562,16 +2562,16 @@ PHP_METHOD(Redis, pipeline) RedisSock *redis_sock; zval *object; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE || - (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL + (redis_sock = redis_sock_get(object, 0)) == NULL ) { RETURN_FALSE; } /* User cannot enter MULTI mode if already in a pipeline */ if (IS_MULTI(redis_sock)) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate pipeline in multi mode!"); + php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!"); RETURN_FALSE; } @@ -2632,11 +2632,11 @@ PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, int i; zval z_tab, *z_channel; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", &object, redis_ce, &array) == FAILURE) { RETURN_FALSE; } - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -2717,13 +2717,13 @@ PHP_METHOD(Redis, slaveof) zend_long port = 6379; int cmd_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|sl", &object, redis_ce, &host, &host_len, &port) == FAILURE) { RETURN_FALSE; } - if (port < 0 || (redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if (port < 0 || (redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -2749,7 +2749,7 @@ PHP_METHOD(Redis, object) char *cmd; int cmd_len; REDIS_REPLY_TYPE rtype; - if ((redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) { RETURN_FALSE; } @@ -2782,7 +2782,7 @@ PHP_METHOD(Redis, getOption) { RedisSock *redis_sock; - if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } @@ -2795,7 +2795,7 @@ PHP_METHOD(Redis, setOption) { RedisSock *redis_sock; - if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } @@ -2813,7 +2813,7 @@ PHP_METHOD(Redis, config) enum {CFG_GET, CFG_SET} mode; int cmd_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss|s", &object, redis_ce, &op, &op_len, &key, &key_len, &val, &val_len) == FAILURE) { @@ -2829,7 +2829,7 @@ PHP_METHOD(Redis, config) RETURN_FALSE; } - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -2869,7 +2869,7 @@ PHP_METHOD(Redis, slowlog) { enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; // Make sure we can get parameters - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|l", &object, redis_ce, &arg, &arg_len, &option) == FAILURE) { @@ -2889,7 +2889,7 @@ PHP_METHOD(Redis, slowlog) { } /* Make sure we can grab our redis socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -2922,7 +2922,7 @@ PHP_METHOD(Redis, wait) { int cmd_len; /* Make sure arguments are valid */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll", &object, redis_ce, &num_slaves, &timeout) ==FAILURE) { @@ -2935,7 +2935,7 @@ PHP_METHOD(Redis, wait) { } /* Grab our socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -2954,7 +2954,7 @@ PHP_METHOD(Redis, wait) { /* Construct a PUBSUB command */ PHP_REDIS_API int redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type, - zval *arg TSRMLS_DC) + zval *arg) { HashTable *ht_chan; zval *z_ele; @@ -3009,7 +3009,7 @@ PHP_METHOD(Redis, pubsub) { zval *arg = NULL; // Parse arguments - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|z", &object, redis_ce, &keyword, &kw_len, &arg)==FAILURE) { @@ -3039,12 +3039,12 @@ PHP_METHOD(Redis, pubsub) { } /* Grab our socket context object */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } /* Construct our "PUBSUB" command */ - cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg TSRMLS_CC); + cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg); REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); @@ -3092,7 +3092,7 @@ PHP_METHOD(Redis, script) { int argc = ZEND_NUM_ARGS(); /* Attempt to grab our socket */ - if (argc < 1 || (redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL) { + if (argc < 1 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL) { RETURN_FALSE; } @@ -3151,7 +3151,7 @@ PHP_METHOD(Redis, migrate) { PHP_METHOD(Redis, _prefix) { RedisSock *redis_sock; - if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } @@ -3163,7 +3163,7 @@ PHP_METHOD(Redis, _serialize) { RedisSock *redis_sock; // Grab socket - if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } @@ -3175,7 +3175,7 @@ PHP_METHOD(Redis, _unserialize) { RedisSock *redis_sock; // Grab socket - if ((redis_sock = redis_sock_get_instance(getThis() TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { RETURN_FALSE; } @@ -3189,14 +3189,14 @@ PHP_METHOD(Redis, getLastError) { RedisSock *redis_sock; // Grab our object - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } // Grab socket - if ((redis_sock = redis_sock_get_instance(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) { RETURN_FALSE; } @@ -3213,13 +3213,13 @@ PHP_METHOD(Redis, clearLastError) { RedisSock *redis_sock; // Grab our object - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } // Grab socket - if ((redis_sock = redis_sock_get_instance(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) { RETURN_FALSE; } @@ -3240,12 +3240,12 @@ PHP_METHOD(Redis, getMode) { RedisSock *redis_sock; /* Grab our object */ - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, redis_ce) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) { RETURN_FALSE; } /* Grab socket */ - if ((redis_sock = redis_sock_get_instance(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) { RETURN_FALSE; } @@ -3378,7 +3378,7 @@ PHP_METHOD(Redis, client) { int cmd_len; // Parse our method parameters - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|s", &object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE) { @@ -3386,7 +3386,7 @@ PHP_METHOD(Redis, client) { } /* Grab our socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } @@ -3425,18 +3425,18 @@ PHP_METHOD(Redis, rawcommand) { /* Sanity check on arguments */ if (argc < 1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Must pass at least one command keyword"); RETURN_FALSE; } z_args = emalloc(argc * sizeof(zval)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Internal PHP error parsing arguments"); efree(z_args); RETURN_FALSE; - } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len TSRMLS_CC) < 0 || - (redis_sock = redis_sock_get(getThis() TSRMLS_CC, 0)) == NULL + } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len) < 0 || + (redis_sock = redis_sock_get(getThis(), 0)) == NULL ) { if (cmd) efree(cmd); efree(z_args); @@ -3529,7 +3529,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Different prototype depending on if this is a key based scan */ if(type != TYPE_SCAN) { // Requires a key - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Osz/|s!l", &object, redis_ce, &key, &key_len, &z_iter, &pattern, &pattern_len, &count)==FAILURE) @@ -3538,7 +3538,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } } else { // Doesn't require a key - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oz/|s!l", &object, redis_ce, &z_iter, &pattern, &pattern_len, &count) == FAILURE) @@ -3548,13 +3548,13 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } /* Grab our socket */ - if ((redis_sock = redis_sock_get(object TSRMLS_CC, 0)) == NULL) { + if ((redis_sock = redis_sock_get(object, 0)) == NULL) { RETURN_FALSE; } /* Calling this in a pipeline makes no sense */ if (!IS_ATOMIC(redis_sock)) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, + php_error_docref(NULL, E_ERROR, "Can't call SCAN commands in multi or pipeline mode!"); RETURN_FALSE; } diff --git a/redis_array.c b/redis_array.c index b4848d1dc6..556d7f063d 100644 --- a/redis_array.c +++ b/redis_array.c @@ -186,17 +186,17 @@ free_redis_array_object(zend_object *object) if (obj->ra->prev) redis_array_free(obj->ra->prev); redis_array_free(obj->ra); } - zend_object_std_dtor(&obj->std TSRMLS_CC); + zend_object_std_dtor(&obj->std); } zend_object * -create_redis_array_object(zend_class_entry *ce TSRMLS_DC) +create_redis_array_object(zend_class_entry *ce) { redis_array_object *obj = ecalloc(1, sizeof(redis_array_object) + zend_object_properties_size(ce)); obj->ra = NULL; - zend_object_std_init(&obj->std, ce TSRMLS_CC); + zend_object_std_init(&obj->std, ce); object_properties_init(&obj->std, ce); memcpy(&redis_array_object_handlers, zend_get_std_object_handlers(), sizeof(redis_array_object_handlers)); @@ -211,7 +211,7 @@ create_redis_array_object(zend_class_entry *ce TSRMLS_DC) * redis_array_get */ PHP_REDIS_API RedisArray * -redis_array_get(zval *id TSRMLS_DC) +redis_array_get(zval *id) { redis_array_object *obj; @@ -223,13 +223,13 @@ redis_array_get(zval *id TSRMLS_DC) } PHP_REDIS_API int -ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[] TSRMLS_DC) +ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[]) { if (object) { redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object); if (redis->sock->auth && redis->sock->status != REDIS_SOCK_STATUS_CONNECTED) { - redis_sock_server_open(redis->sock TSRMLS_CC); - redis_sock_auth(redis->sock TSRMLS_CC); + redis_sock_server_open(redis->sock); + redis_sock_auth(redis->sock); } } return call_user_function(function_table, object, function_name, retval_ptr, param_count, params); @@ -249,7 +249,7 @@ PHP_METHOD(RedisArray, __construct) zend_string *algorithm = NULL, *auth = NULL; redis_array_object *obj; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|a", &z0, &z_opts) == FAILURE) { RETURN_FALSE; } @@ -347,11 +347,11 @@ PHP_METHOD(RedisArray, __construct) /* extract either name of list of hosts from z0 */ switch(Z_TYPE_P(z0)) { case IS_STRING: - ra = ra_load_array(Z_STRVAL_P(z0) TSRMLS_CC); + ra = ra_load_array(Z_STRVAL_P(z0)); break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth); break; default: @@ -390,16 +390,16 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i } else { /* extract key and hash it. */ if ((zp_tmp = zend_hash_index_find(h_args, 0)) == NULL || Z_TYPE_P(zp_tmp) != IS_STRING) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not find key"); + php_error_docref(NULL, E_ERROR, "Could not find key"); RETURN_FALSE; } key = Z_STRVAL_P(zp_tmp); key_len = Z_STRLEN_P(zp_tmp); /* find node */ - redis_inst = ra_find_node(ra, key, key_len, NULL TSRMLS_CC); + redis_inst = ra_find_node(ra, key, key_len, NULL); if(!redis_inst) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not find any redis servers for this key."); + php_error_docref(NULL, E_ERROR, "Could not find any redis servers for this key."); RETURN_FALSE; } } @@ -417,7 +417,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* multi/exec */ if(ra->z_multi_exec) { - ra_call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); zval_dtor(return_value); zval_dtor(&z_fun); for (i = 0; i < argc; ++i) { @@ -433,18 +433,18 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* CALL! */ if(ra->index && b_write_cmd) { /* add MULTI + SADD */ - ra_index_multi(redis_inst, MULTI TSRMLS_CC); + ra_index_multi(redis_inst, MULTI); /* call using discarded temp value and extract exec results after. */ - ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); zval_dtor(return_value); /* add keys to index. */ - ra_index_key(key, key_len, redis_inst TSRMLS_CC); + ra_index_key(key, key_len, redis_inst); /* call EXEC */ - ra_index_exec(redis_inst, return_value, 0 TSRMLS_CC); + ra_index_exec(redis_inst, return_value, 0); } else { /* call directly through. */ - ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); if (!b_write_cmd) { /* check if we have an error. */ @@ -458,7 +458,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i /* Autorehash if the key was found on the previous node if this is a read command and auto rehashing is on */ if (ra->auto_rehash && z_new_target && !RA_CALL_FAILED(return_value, cmd)) { /* move key from old ring to new ring */ - ra_move_key(key, key_len, redis_inst, z_new_target TSRMLS_CC); + ra_move_key(key, key_len, redis_inst, z_new_target); } } } @@ -480,12 +480,12 @@ PHP_METHOD(RedisArray, __call) char *cmd; size_t cmd_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Osa", &object, redis_array_ce, &cmd, &cmd_len, &z_args) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -498,12 +498,12 @@ PHP_METHOD(RedisArray, _hosts) int i; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -522,16 +522,16 @@ PHP_METHOD(RedisArray, _target) zval *redis_inst; int i; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, redis_array_ce, &key, &key_len) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } - redis_inst = ra_find_node(ra, key, key_len, &i TSRMLS_CC); + redis_inst = ra_find_node(ra, key, key_len, &i); if(redis_inst) { RETURN_STRINGL(ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i])); } else { @@ -547,16 +547,16 @@ PHP_METHOD(RedisArray, _instance) size_t target_len; zval *z_redis; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, redis_array_ce, &target, &target_len) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } - z_redis = ra_find_node_by_name(ra, target, target_len TSRMLS_CC); + z_redis = ra_find_node_by_name(ra, target, target_len); if(z_redis) { RETURN_ZVAL(z_redis, 1, 0); } else { @@ -569,12 +569,12 @@ PHP_METHOD(RedisArray, _function) zval *object, *z_fun; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -587,12 +587,12 @@ PHP_METHOD(RedisArray, _distributor) zval *object, *z_dist; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -607,19 +607,19 @@ PHP_METHOD(RedisArray, _rehash) zend_fcall_info z_cb = {0}; zend_fcall_info_cache z_cb_cache = {0}; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|f", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|f", &object, redis_array_ce, &z_cb, &z_cb_cache) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } if (ZEND_NUM_ARGS() == 0) { - ra_rehash(ra, NULL, NULL TSRMLS_CC); + ra_rehash(ra, NULL, NULL); } else { - ra_rehash(ra, &z_cb, &z_cb_cache TSRMLS_CC); + ra_rehash(ra, &z_cb, &z_cb_cache); } } @@ -629,12 +629,12 @@ PHP_METHOD(RedisArray, _continuum) zval *object, z_ret; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -651,7 +651,7 @@ PHP_METHOD(RedisArray, _continuum) static void -multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int argc, zval *argv TSRMLS_DC) +multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int argc, zval *argv) { zval z_arg, z_tmp; int i; @@ -662,7 +662,7 @@ multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int a /* Iterate our RedisArray nodes */ for (i = 0; i < ra->count; ++i) { /* Call each node in turn */ - ra_call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_tmp, argc, argv TSRMLS_CC); + ra_call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_tmp, argc, argv); /* Add the result for this host */ add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), &z_arg); @@ -675,19 +675,19 @@ multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) zval *object, z_fun; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* prepare call */ ZVAL_STRING(&z_fun, method_name); - multihost_distribute_call(ra, return_value, &z_fun, 0, NULL TSRMLS_CC); + multihost_distribute_call(ra, return_value, &z_fun, 0, NULL); zval_dtor(&z_fun); } @@ -699,12 +699,12 @@ multihost_distribute_flush(INTERNAL_FUNCTION_PARAMETERS, const char *method_name zend_bool async = 0; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|b", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|b", &object, redis_array_ce, &async) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -712,7 +712,7 @@ multihost_distribute_flush(INTERNAL_FUNCTION_PARAMETERS, const char *method_name ZVAL_STRING(&z_fun, method_name); ZVAL_BOOL(&z_args[0], async); - multihost_distribute_call(ra, return_value, &z_fun, 1, z_args TSRMLS_CC); + multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); zval_dtor(&z_fun); } @@ -756,14 +756,14 @@ PHP_METHOD(RedisArray, keys) size_t pattern_len; /* Make sure the prototype is correct */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, redis_array_ce, &pattern, &pattern_len) == FAILURE) { RETURN_FALSE; } /* Make sure we can grab our RedisArray object */ - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -773,7 +773,7 @@ PHP_METHOD(RedisArray, keys) /* We will be passing with one string argument (the pattern) */ ZVAL_STRINGL(z_args, pattern, pattern_len); - multihost_distribute_call(ra, return_value, &z_fun, 1, z_args TSRMLS_CC); + multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); zval_dtor(&z_args[0]); zval_dtor(&z_fun); @@ -785,12 +785,12 @@ PHP_METHOD(RedisArray, getOption) RedisArray *ra; zend_long opt; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, redis_array_ce, &opt) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -800,7 +800,7 @@ PHP_METHOD(RedisArray, getOption) /* copy arg */ ZVAL_LONG(&z_args[0], opt); - multihost_distribute_call(ra, return_value, &z_fun, 1, z_args TSRMLS_CC); + multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); zval_dtor(&z_fun); } @@ -813,12 +813,12 @@ PHP_METHOD(RedisArray, setOption) char *val_str; size_t val_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ols", &object, redis_array_ce, &opt, &val_str, &val_len) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -829,7 +829,7 @@ PHP_METHOD(RedisArray, setOption) ZVAL_LONG(&z_args[0], opt); ZVAL_STRINGL(&z_args[1], val_str, val_len); - multihost_distribute_call(ra, return_value, &z_fun, 2, z_args TSRMLS_CC); + multihost_distribute_call(ra, return_value, &z_fun, 2, z_args); zval_dtor(&z_args[1]); zval_dtor(&z_fun); @@ -841,12 +841,12 @@ PHP_METHOD(RedisArray, select) RedisArray *ra; zend_long opt; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, redis_array_ce, &opt) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } @@ -856,7 +856,7 @@ PHP_METHOD(RedisArray, select) /* copy args */ ZVAL_LONG(&z_args[0], opt); - multihost_distribute_call(ra, return_value, &z_fun, 1, z_args TSRMLS_CC); + multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); zval_dtor(&z_fun); } @@ -865,7 +865,7 @@ PHP_METHOD(RedisArray, select) if (ra && ra->z_multi_exec) { \ int i, num_varargs; \ zval *varargs = NULL, z_arg_array; \ - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O*", \ + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O*", \ &object, redis_array_ce, &varargs, &num_varargs) == FAILURE) { \ RETURN_FALSE;\ } \ @@ -893,14 +893,14 @@ PHP_METHOD(RedisArray, mget) HashTable *h_keys; zval **argv; - if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(getThis())) == NULL) { RETURN_FALSE; } /* Multi/exec support */ HANDLE_MULTI_EXEC(ra, "MGET", sizeof("MGET") - 1); - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", &object, redis_array_ce, &z_keys) == FAILURE) { RETURN_FALSE; } @@ -929,7 +929,7 @@ PHP_METHOD(RedisArray, mget) /* phpredis proper can only use string or long keys, so restrict to that here */ if (Z_TYPE_P(data) != IS_STRING && Z_TYPE_P(data) != IS_LONG) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "MGET: all keys must be strings or longs"); + php_error_docref(NULL, E_ERROR, "MGET: all keys must be strings or longs"); efree(argv); efree(pos); efree(argc_each); @@ -946,7 +946,7 @@ PHP_METHOD(RedisArray, mget) } /* Find our node */ - if (ra_find_node(ra, key_lookup, key_len, &pos[i] TSRMLS_CC) == NULL) { + if (ra_find_node(ra, key_lookup, key_len, &pos[i]) == NULL) { /* TODO: handle */ } @@ -975,7 +975,7 @@ PHP_METHOD(RedisArray, mget) /* prepare call */ ZVAL_STRINGL(&z_fun, "MGET", 4); /* call MGET on the node */ - ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); zval_dtor(&z_fun); /* cleanup args array */ @@ -1035,14 +1035,14 @@ PHP_METHOD(RedisArray, mset) zend_string **keys, *zkey; ulong idx; - if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(getThis())) == NULL) { RETURN_FALSE; } /* Multi/exec support */ HANDLE_MULTI_EXEC(ra, "MSET", sizeof("MSET") - 1); - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", &object, redis_array_ce, &z_keys) == FAILURE) { RETURN_FALSE; @@ -1071,7 +1071,7 @@ PHP_METHOD(RedisArray, mset) key = kbuf; } - if (ra_find_node(ra, key, (int)key_len, &pos[i] TSRMLS_CC) == NULL) { + if (ra_find_node(ra, key, (int)key_len, &pos[i]) == NULL) { // TODO: handle } @@ -1110,7 +1110,7 @@ PHP_METHOD(RedisArray, mset) } if(ra->index) { /* add MULTI */ - ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); + ra_index_multi(&ra->redis[n], MULTI); } zval z_fun; @@ -1119,13 +1119,13 @@ PHP_METHOD(RedisArray, mset) ZVAL_STRINGL(&z_fun, "MSET", 4); /* call */ - ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); zval_dtor(&z_fun); zval_dtor(&z_ret); if(ra->index) { - ra_index_keys(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SADD to add keys to node index */ - ra_index_exec(&ra->redis[n], NULL, 0 TSRMLS_CC); /* run EXEC */ + ra_index_keys(&z_argarray, &ra->redis[n]); /* use SADD to add keys to node index */ + ra_index_exec(&ra->redis[n], NULL, 0); /* run EXEC */ } zval_dtor(&z_argarray); @@ -1156,7 +1156,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { long total = 0; int free_zkeys = 0; - if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(getThis())) == NULL) { RETURN_FALSE; } @@ -1203,7 +1203,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { i = 0; ZEND_HASH_FOREACH_VAL(h_keys, data) { if (Z_TYPE_P(data) != IS_STRING) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "DEL: all keys must be string."); + php_error_docref(NULL, E_ERROR, "DEL: all keys must be string."); if (free_zkeys) zval_dtor(&z_keys); efree(z_args); efree(argv); @@ -1212,7 +1212,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { RETURN_FALSE; } - if (ra_find_node(ra, Z_STRVAL_P(data), Z_STRLEN_P(data), &pos[i] TSRMLS_CC) == NULL) { + if (ra_find_node(ra, Z_STRVAL_P(data), Z_STRLEN_P(data), &pos[i]) == NULL) { // TODO: handle } argc_each[pos[i]]++; /* count number of keys per node */ @@ -1247,16 +1247,16 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { } if(ra->index) { /* add MULTI */ - ra_index_multi(&ra->redis[n], MULTI TSRMLS_CC); + ra_index_multi(&ra->redis[n], MULTI); } /* call */ - ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); if(ra->index) { zval_dtor(&z_ret); - ra_index_del(&z_argarray, &ra->redis[n] TSRMLS_CC); /* use SREM to remove keys from node index */ - ra_index_exec(&ra->redis[n], &z_ret, 0 TSRMLS_CC); /* run EXEC */ + ra_index_del(&z_argarray, &ra->redis[n]); /* use SREM to remove keys from node index */ + ra_index_exec(&ra->redis[n], &z_ret, 0); /* run EXEC */ } total += Z_LVAL(z_ret); /* increment total */ @@ -1297,17 +1297,17 @@ PHP_METHOD(RedisArray, multi) size_t host_len; zend_long multi_value = MULTI; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|l", &object, redis_array_ce, &host, &host_len, &multi_value) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL) { + if ((ra = redis_array_get(object)) == NULL) { RETURN_FALSE; } /* find node */ - z_redis = ra_find_node_by_name(ra, host, host_len TSRMLS_CC); + z_redis = ra_find_node_by_name(ra, host, host_len); if(!z_redis) { RETURN_FALSE; } @@ -1320,7 +1320,7 @@ PHP_METHOD(RedisArray, multi) ra->z_multi_exec = z_redis; /* switch redis instance to multi/exec mode. */ - ra_index_multi(z_redis, multi_value TSRMLS_CC); + ra_index_multi(z_redis, multi_value); /* return this. */ RETURN_ZVAL(object, 1, 0); @@ -1331,17 +1331,17 @@ PHP_METHOD(RedisArray, exec) zval *object; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { + if ((ra = redis_array_get(object)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } /* switch redis instance out of multi/exec mode. */ - ra_index_exec(ra->z_multi_exec, return_value, 1 TSRMLS_CC); + ra_index_exec(ra->z_multi_exec, return_value, 1); /* remove multi object */ ra->z_multi_exec = NULL; @@ -1352,17 +1352,17 @@ PHP_METHOD(RedisArray, discard) zval *object; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { + if ((ra = redis_array_get(object)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } /* switch redis instance out of multi/exec mode. */ - ra_index_discard(ra->z_multi_exec, return_value TSRMLS_CC); + ra_index_discard(ra->z_multi_exec, return_value); /* remove multi object */ ra->z_multi_exec = NULL; @@ -1373,15 +1373,15 @@ PHP_METHOD(RedisArray, unwatch) zval *object; RedisArray *ra; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_array_ce) == FAILURE) { RETURN_FALSE; } - if ((ra = redis_array_get(object TSRMLS_CC)) == NULL || !ra->z_multi_exec) { + if ((ra = redis_array_get(object)) == NULL || !ra->z_multi_exec) { RETURN_FALSE; } /* unwatch keys, stay in multi/exec mode. */ - ra_index_unwatch(ra->z_multi_exec, return_value TSRMLS_CC); + ra_index_unwatch(ra->z_multi_exec, return_value); } diff --git a/redis_array.h b/redis_array.h index 5421841256..0a75197e6e 100644 --- a/redis_array.h +++ b/redis_array.h @@ -66,9 +66,9 @@ typedef struct RedisArray_ { struct RedisArray_ *prev; } RedisArray; -zend_object *create_redis_array_object(zend_class_entry *ce TSRMLS_DC); +zend_object *create_redis_array_object(zend_class_entry *ce); void free_redis_array_object(zend_object *object); -PHP_REDIS_API int ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[] TSRMLS_DC); +PHP_REDIS_API int ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[]); #endif diff --git a/redis_array_impl.c b/redis_array_impl.c index 153368d5c5..54ae50023d 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -33,7 +33,7 @@ extern zend_class_entry *redis_ce; static RedisArray * -ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) +ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_interval, zend_bool b_lazy_connect) { int i = 0, host_len; char *host, *p; @@ -80,7 +80,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in if (!b_lazy_connect) { /* connect */ - if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0 || (auth && redis_sock_auth(redis->sock TSRMLS_CC) < 0)) { + if (redis_sock_server_open(redis->sock) < 0 || (auth && redis_sock_auth(redis->sock ) < 0)) { zval_dtor(&z_cons); ra->count = ++i; return NULL; @@ -162,7 +162,7 @@ ra_find_name(const char *name) { } /* laod array from INI settings */ -RedisArray *ra_load_array(const char *name TSRMLS_DC) { +RedisArray *ra_load_array(const char *name) { zval *z_data, z_fun, z_dist; zval z_params_hosts; @@ -197,7 +197,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find hosts */ array_init(&z_params_hosts); if ((iptr = INI_STR("redis.arrays.hosts")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_hosts TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_hosts); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_hosts), name, name_len)) != NULL) { hHosts = Z_ARRVAL_P(z_data); @@ -206,7 +206,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find previous hosts */ array_init(&z_params_prev); if ((iptr = INI_STR("redis.arrays.previous")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_prev TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_prev); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_prev), name, name_len)) != NULL) { hPrev = Z_ARRVAL_P(z_data); @@ -215,7 +215,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find function */ array_init(&z_params_funs); if ((iptr = INI_STR("redis.arrays.functions")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_funs TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_funs); } ZVAL_NULL(&z_fun); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_funs), name, name_len)) != NULL) { @@ -225,7 +225,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find distributor */ array_init(&z_params_dist); if ((iptr = INI_STR("redis.arrays.distributor")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_dist TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_dist); } ZVAL_NULL(&z_dist); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_dist), name, name_len)) != NULL) { @@ -235,7 +235,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find hash algorithm */ array_init(&z_params_algo); if ((iptr = INI_STR("redis.arrays.algorithm")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_algo TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_algo); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_algo), name, name_len)) != NULL) { algorithm = zval_get_string(z_data); @@ -244,7 +244,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find index option */ array_init(&z_params_index); if ((iptr = INI_STR("redis.arrays.index")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_index TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_index); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_index), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { @@ -255,7 +255,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find autorehash option */ array_init(&z_params_autorehash); if ((iptr = INI_STR("redis.arrays.autorehash")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_autorehash TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_autorehash); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_autorehash), name, name_len)) != NULL) { if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { @@ -266,7 +266,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find retry interval option */ array_init(&z_params_retry_interval); if ((iptr = INI_STR("redis.arrays.retryinterval")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_retry_interval TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_retry_interval); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_retry_interval), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_LONG) { @@ -279,7 +279,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find pconnect option */ array_init(&z_params_pconnect); if ((iptr = INI_STR("redis.arrays.pconnect")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_pconnect TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_pconnect); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_pconnect), name, name_len)) != NULL) { if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { @@ -290,7 +290,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find lazy connect option */ array_init(&z_params_lazy_connect); if ((iptr = INI_STR("redis.arrays.lazyconnect")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_lazy_connect TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_lazy_connect); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_lazy_connect), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { @@ -301,7 +301,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find connect timeout option */ array_init(&z_params_connect_timeout); if ((iptr = INI_STR("redis.arrays.connecttimeout")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_connect_timeout TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_connect_timeout); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_connect_timeout), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_DOUBLE) { @@ -316,7 +316,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find read timeout option */ array_init(&z_params_read_timeout); if ((iptr = INI_STR("redis.arrays.readtimeout")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_read_timeout TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_read_timeout); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_read_timeout), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_DOUBLE) { @@ -331,7 +331,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find consistent option */ array_init(&z_params_consistent); if ((iptr = INI_STR("redis.arrays.consistent")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_consistent TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_consistent); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_consistent), name, name_len)) != NULL) { if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) { @@ -342,14 +342,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { /* find auth option */ array_init(&z_params_auth); if ((iptr = INI_STR("redis.arrays.auth")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_auth TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_auth); } if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_auth), name, name_len)) != NULL) { auth = zval_get_string(z_data); } /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; @@ -420,7 +420,7 @@ ra_make_continuum(zend_string **hosts, int nb_hosts) } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth TSRMLS_DC) +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth) { int i, count; RedisArray *ra; @@ -441,7 +441,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->continuum = NULL; ra->algorithm = NULL; - if (ra_load_hosts(ra, hosts, auth, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { + if (ra_load_hosts(ra, hosts, auth, retry_interval, b_lazy_connect) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { zval_dtor(&ra->redis[i]); zend_string_release(ra->hosts[i]); @@ -451,7 +451,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm, auth TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm, auth) : NULL; /* init array data structures */ ra_init_function_table(ra); @@ -472,21 +472,21 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev /* call userland key extraction function */ zend_string * -ra_call_extractor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) +ra_call_extractor(RedisArray *ra, const char *key, int key_len) { zend_string *out = NULL; zval z_ret, z_argv; /* check that we can call the extractor function */ if (!zend_is_callable_ex(&ra->z_fun, NULL, 0, NULL, NULL, NULL)) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call extractor function"); + php_error_docref(NULL, E_ERROR, "Could not call extractor function"); return NULL; } ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); - ra_call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv TSRMLS_CC); + ra_call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv); if (Z_TYPE(z_ret) == IS_STRING) { out = zval_get_string(&z_ret); @@ -498,12 +498,12 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) } static zend_string * -ra_extract_key(RedisArray *ra, const char *key, int key_len TSRMLS_DC) +ra_extract_key(RedisArray *ra, const char *key, int key_len) { char *start, *end; if (Z_TYPE(ra->z_fun) != IS_NULL) { - return ra_call_extractor(ra, key, key_len TSRMLS_CC); + return ra_call_extractor(ra, key, key_len); } else if ((start = strchr(key, '{')) == NULL || (end = strchr(start + 1, '}')) == NULL) { return zend_string_init(key, key_len, 0); } @@ -513,21 +513,21 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len TSRMLS_DC) /* call userland key distributor function */ int -ra_call_distributor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) +ra_call_distributor(RedisArray *ra, const char *key, int key_len) { int ret; zval z_ret, z_argv; /* check that we can call the extractor function */ if (!zend_is_callable_ex(&ra->z_dist, NULL, 0, NULL, NULL, NULL)) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function"); + php_error_docref(NULL, E_ERROR, "Could not call distributor function"); return -1; } ZVAL_NULL(&z_ret); /* call extraction function */ ZVAL_STRINGL(&z_argv, key, key_len); - ra_call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv TSRMLS_CC); + ra_call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv); ret = (Z_TYPE(z_ret) == IS_LONG) ? Z_LVAL(z_ret) : -1; @@ -537,13 +537,13 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len TSRMLS_DC) } zval * -ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) +ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos) { int pos; zend_string *out; /* extract relevant part of the key */ - if ((out = ra_extract_key(ra, key, key_len TSRMLS_CC)) == NULL) { + if ((out = ra_extract_key(ra, key, key_len)) == NULL) { return NULL; } @@ -591,7 +591,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D pos = (int)((ret ^ 0xffffffff) * ra->count / 0xffffffff); } } else { - pos = ra_call_distributor(ra, key, key_len TSRMLS_CC); + pos = ra_call_distributor(ra, key, key_len); if (pos < 0 || pos >= ra->count) { zend_string_release(out); return NULL; @@ -605,7 +605,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D } zval * -ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC) { +ra_find_node_by_name(RedisArray *ra, const char *host, int host_len) { int i; for(i = 0; i < ra->count; ++i) { @@ -617,7 +617,7 @@ ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC) { } void -ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { +ra_index_multi(zval *z_redis, long multi_value) { zval z_fun_multi, z_ret; zval z_args[1]; @@ -625,13 +625,13 @@ ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC) { /* run MULTI */ ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); ZVAL_LONG(&z_args[0], multi_value); - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); zval_dtor(&z_fun_multi); zval_dtor(&z_ret); } static void -ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { +ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis) { int i, argc; zval z_fun, z_ret, *z_args; @@ -655,7 +655,7 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { } /* run cmd */ - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); zval_dtor(&z_args[0]); zval_dtor(&z_fun); @@ -664,12 +664,12 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis TSRMLS_DC) { } void -ra_index_del(zval *z_keys, zval *z_redis TSRMLS_DC) { - ra_index_change_keys("SREM", z_keys, z_redis TSRMLS_CC); +ra_index_del(zval *z_keys, zval *z_redis) { + ra_index_change_keys("SREM", z_keys, z_redis); } void -ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { +ra_index_keys(zval *z_pairs, zval *z_redis) { zval z_keys, *z_val; zend_string *zkey; @@ -693,14 +693,14 @@ ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC) { } ZEND_HASH_FOREACH_END(); /* add keys to index */ - ra_index_change_keys("SADD", &z_keys, z_redis TSRMLS_CC); + ra_index_change_keys("SADD", &z_keys, z_redis); /* cleanup */ zval_dtor(&z_keys); } void -ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { +ra_index_key(const char *key, int key_len, zval *z_redis) { zval z_fun_sadd, z_ret, z_args[2]; @@ -711,7 +711,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { ZVAL_STRINGL(&z_args[1], key, key_len); /* run SADD */ - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args); zval_dtor(&z_fun_sadd); zval_dtor(&z_args[1]); zval_dtor(&z_args[0]); @@ -719,13 +719,13 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) { } void -ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { +ra_index_exec(zval *z_redis, zval *return_value, int keep_all) { zval z_fun_exec, z_ret, *zp_tmp; /* run EXEC */ ZVAL_STRINGL(&z_fun_exec, "EXEC", 4); - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); zval_dtor(&z_fun_exec); /* extract first element of exec array and put into return_value. */ @@ -742,30 +742,30 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC) { zval_dtor(&z_ret); /* zval *zptr = &z_ret; */ - /* php_var_dump(&zptr, 0 TSRMLS_CC); */ + /* php_var_dump(&zptr, 0); */ } void -ra_index_discard(zval *z_redis, zval *return_value TSRMLS_DC) { +ra_index_discard(zval *z_redis, zval *return_value) { zval z_fun_discard, z_ret; /* run DISCARD */ ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7); - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); zval_dtor(&z_fun_discard); zval_dtor(&z_ret); } void -ra_index_unwatch(zval *z_redis, zval *return_value TSRMLS_DC) { +ra_index_unwatch(zval *z_redis, zval *return_value) { zval z_fun_unwatch, z_ret; /* run UNWATCH */ ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7); - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); zval_dtor(&z_fun_unwatch); zval_dtor(&z_ret); @@ -790,14 +790,14 @@ ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len) { /* run TYPE to find the type */ static zend_bool -ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long *res TSRMLS_DC) { +ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long *res) { int i = 0; zval z_fun, z_ret, z_arg, *z_data; long success = 1; /* Pipelined */ - ra_index_multi(z_from, PIPELINE TSRMLS_CC); + ra_index_multi(z_from, PIPELINE); /* prepare args */ ZVAL_STRINGL(&z_arg, key, key_len); @@ -805,19 +805,19 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TYPE", 4); - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); zval_dtor(&z_fun); zval_dtor(&z_ret); /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TTL", 3); - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); zval_dtor(&z_fun); zval_dtor(&z_ret); /* Get the result from the pipeline. */ - ra_index_exec(z_from, &z_ret, 1 TSRMLS_CC); + ra_index_exec(z_from, &z_ret, 1); if (Z_TYPE(z_ret) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_ret), z_data) { if (z_data == NULL || Z_TYPE_P(z_data) != IS_LONG) { @@ -835,7 +835,7 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long /* delete key from source server index during rehashing */ static void -ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { +ra_remove_from_index(zval *z_redis, const char *key, int key_len) { zval z_fun_srem, z_ret, z_args[2]; @@ -844,7 +844,7 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1); ZVAL_STRINGL(&z_args[1], key, key_len); - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args); /* cleanup */ zval_dtor(&z_fun_srem); @@ -856,32 +856,32 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len TSRMLS_DC) { /* delete key from source server during rehashing */ static zend_bool -ra_del_key(const char *key, int key_len, zval *z_from TSRMLS_DC) { +ra_del_key(const char *key, int key_len, zval *z_from) { zval z_fun_del, z_ret, z_args[1]; /* in a transaction */ - ra_index_multi(z_from, MULTI TSRMLS_CC); + ra_index_multi(z_from, MULTI); /* run DEL on source */ ZVAL_STRINGL(&z_fun_del, "DEL", 3); ZVAL_STRINGL(&z_args[0], key, key_len); - ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); zval_dtor(&z_fun_del); zval_dtor(&z_args[0]); zval_dtor(&z_ret); /* remove key from index */ - ra_remove_from_index(z_from, key, key_len TSRMLS_CC); + ra_remove_from_index(z_from, key, key_len); /* close transaction */ - ra_index_exec(z_from, NULL, 0 TSRMLS_CC); + ra_index_exec(z_from, NULL, 0); return 1; } static zend_bool -ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { +ra_expire_key(const char *key, int key_len, zval *z_to, long ttl) { zval z_fun_expire, z_ret, z_args[2]; @@ -891,7 +891,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6); ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_LONG(&z_args[1], ttl); - ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); zval_dtor(&z_fun_expire); zval_dtor(&z_args[0]); zval_dtor(&z_ret); @@ -901,7 +901,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl TSRMLS_DC) { } static zend_bool -ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { +ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { zval z_fun_zrange, z_fun_zadd, z_ret, z_ret_dest, z_args[4], *z_zadd_args, *z_score_p; int i, count; @@ -915,7 +915,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS ZVAL_STRINGL(&z_args[1], "0", 1); ZVAL_STRINGL(&z_args[2], "-1", 2); ZVAL_BOOL(&z_args[3], 1); - ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); zval_dtor(&z_fun_zrange); zval_dtor(&z_args[2]); zval_dtor(&z_args[1]); @@ -953,10 +953,10 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run ZADD on target */ ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4); - ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args); /* Expire if needed */ - ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); + ra_expire_key(key, key_len, z_to, ttl); /* cleanup */ zval_dtor(&z_fun_zadd); @@ -973,14 +973,14 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS } static zend_bool -ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { +ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { zval z_fun_get, z_fun_set, z_ret, z_args[3]; /* run GET on source */ ZVAL_STRINGL(&z_fun_get, "GET", 3); ZVAL_STRINGL(&z_args[0], key, key_len); - ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args); zval_dtor(&z_fun_get); if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ @@ -996,14 +996,14 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl ZVAL_LONG(&z_args[1], ttl); ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous call */ - ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args); /* cleanup */ zval_dtor(&z_args[2]); } else { ZVAL_STRINGL(&z_fun_set, "SET", 3); ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ zval_dtor(&z_ret); /* free memory from our previous return value */ - ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args); /* cleanup */ zval_dtor(&z_args[1]); } @@ -1015,13 +1015,13 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl } static zend_bool -ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { +ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { zval z_fun_hgetall, z_fun_hmset, z_ret_dest, z_args[2]; /* run HGETALL on source */ ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); - ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); zval_dtor(&z_fun_hgetall); if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ @@ -1033,12 +1033,12 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS /* run HMSET on target */ ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); - ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); zval_dtor(&z_fun_hmset); zval_dtor(&z_ret_dest); /* Expire if needed */ - ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); + ra_expire_key(key, key_len, z_to, ttl); /* cleanup */ zval_dtor(&z_args[1]); @@ -1050,7 +1050,7 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TS static zend_bool ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, int list_count, const char **cmd_list, - int add_count, const char **cmd_add, long ttl TSRMLS_DC) { + int add_count, const char **cmd_add, long ttl) { zval z_fun_retrieve, z_fun_sadd, z_ret, *z_retrieve_args, *z_sadd_args, *z_data_p; int count, i; @@ -1068,7 +1068,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, ZVAL_STRING(&z_retrieve_args[i], cmd_list[i]); } - ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args); /* cleanup */ zval_dtor(&z_fun_retrieve); @@ -1100,7 +1100,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, /* Clean up our input return value */ zval_dtor(&z_ret); - ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args); /* cleanup */ zval_dtor(&z_fun_sadd); @@ -1113,56 +1113,56 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, zval_dtor(&z_ret); /* Expire if needed */ - ra_expire_key(key, key_len, z_to, ttl TSRMLS_CC); + ra_expire_key(key, key_len, z_to, ttl); return 1; } static zend_bool -ra_move_set(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { +ra_move_set(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { const char *cmd_list[] = {"SMEMBERS"}; const char *cmd_add[] = {"SADD"}; - return ra_move_collection(key, key_len, z_from, z_to, 1, cmd_list, 1, cmd_add, ttl TSRMLS_CC); + return ra_move_collection(key, key_len, z_from, z_to, 1, cmd_list, 1, cmd_add, ttl); } static zend_bool -ra_move_list(const char *key, int key_len, zval *z_from, zval *z_to, long ttl TSRMLS_DC) { +ra_move_list(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { const char *cmd_list[] = {"LRANGE", "0", "-1"}; const char *cmd_add[] = {"RPUSH"}; - return ra_move_collection(key, key_len, z_from, z_to, 3, cmd_list, 1, cmd_add, ttl TSRMLS_CC); + return ra_move_collection(key, key_len, z_from, z_to, 3, cmd_list, 1, cmd_add, ttl); } void -ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { +ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to) { long res[2] = {0}, type, ttl; zend_bool success = 0; - if (ra_get_key_type(z_from, key, key_len, z_from, res TSRMLS_CC)) { + if (ra_get_key_type(z_from, key, key_len, z_from, res)) { type = res[0]; ttl = res[1]; /* open transaction on target server */ - ra_index_multi(z_to, MULTI TSRMLS_CC); + ra_index_multi(z_to, MULTI); switch(type) { case REDIS_STRING: - success = ra_move_string(key, key_len, z_from, z_to, ttl TSRMLS_CC); + success = ra_move_string(key, key_len, z_from, z_to, ttl); break; case REDIS_SET: - success = ra_move_set(key, key_len, z_from, z_to, ttl TSRMLS_CC); + success = ra_move_set(key, key_len, z_from, z_to, ttl); break; case REDIS_LIST: - success = ra_move_list(key, key_len, z_from, z_to, ttl TSRMLS_CC); + success = ra_move_list(key, key_len, z_from, z_to, ttl); break; case REDIS_ZSET: - success = ra_move_zset(key, key_len, z_from, z_to, ttl TSRMLS_CC); + success = ra_move_zset(key, key_len, z_from, z_to, ttl); break; case REDIS_HASH: - success = ra_move_hash(key, key_len, z_from, z_to, ttl TSRMLS_CC); + success = ra_move_hash(key, key_len, z_from, z_to, ttl); break; default: @@ -1172,18 +1172,18 @@ ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC) { } if(success) { - ra_del_key(key, key_len, z_from TSRMLS_CC); - ra_index_key(key, key_len, z_to TSRMLS_CC); + ra_del_key(key, key_len, z_from); + ra_index_key(key, key_len, z_to); } /* close transaction */ - ra_index_exec(z_to, NULL, 0 TSRMLS_CC); + ra_index_exec(z_to, NULL, 0); } /* callback with the current progress, with hostname and count */ static void zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, - zend_string *hostname, long count TSRMLS_DC) { + zend_string *hostname, long count) { zval zv, *z_ret = &zv; @@ -1201,7 +1201,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, z_cb->param_count = 2; /* run cb(hostname, count) */ - zend_call_function(z_cb, z_cb_cache TSRMLS_CC); + zend_call_function(z_cb, z_cb_cache); /* cleanup */ zval_dtor(&z_args[0]); @@ -1210,7 +1210,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, static void ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool b_index, - zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { + zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache) { HashTable *h_keys; long count = 0; @@ -1225,7 +1225,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool ZVAL_STRING(&z_argv, "*"); } ZVAL_NULL(&z_ret); - ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv TSRMLS_CC); + ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv); zval_dtor(&z_argv); zval_dtor(&z_fun); @@ -1241,17 +1241,17 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool /* callback */ if(z_cb && z_cb_cache) { - zval_rehash_callback(z_cb, z_cb_cache, hostname, count TSRMLS_CC); + zval_rehash_callback(z_cb, z_cb_cache, hostname, count); } /* for each key, redistribute */ ZEND_HASH_FOREACH_VAL(h_keys, z_ele) { int pos = 0; /* check that we're not moving to the same node. */ - zval *z_target = ra_find_node(ra, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &pos TSRMLS_CC); + zval *z_target = ra_find_node(ra, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &pos); if (z_target && !zend_string_equals(hostname, ra->hosts[pos])) { /* different host */ - ra_move_key(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), z_redis, z_target TSRMLS_CC); + ra_move_key(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), z_redis, z_target); } } ZEND_HASH_FOREACH_END(); @@ -1261,7 +1261,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool } void -ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC) { +ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache) { int i; /* redistribute the data, server by server. */ @@ -1269,7 +1269,7 @@ ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cac return; /* TODO: compare the two rings for equality */ for(i = 0; i < ra->prev->count; ++i) { - ra_rehash_server(ra, &ra->prev->redis[i], ra->prev->hosts[i], ra->index, z_cb, z_cb_cache TSRMLS_CC); + ra_rehash_server(ra, &ra->prev->redis[i], ra->prev->hosts[i], ra->index, z_cb, z_cb_cache); } } diff --git a/redis_array_impl.h b/redis_array_impl.h index 877b22f8d8..b5d2e1ce72 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -9,23 +9,23 @@ #include "redis_array.h" -RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth TSRMLS_DC); -zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); -zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); +RedisArray *ra_load_array(const char *name); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth); +zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len); +zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos); void ra_init_function_table(RedisArray *ra); -void ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to TSRMLS_DC); -void ra_index_multi(zval *z_redis, long multi_value TSRMLS_DC); +void ra_move_key(const char *key, int key_len, zval *z_from, zval *z_to); +void ra_index_multi(zval *z_redis, long multi_value); -void ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC); -void ra_index_keys(zval *z_pairs, zval *z_redis TSRMLS_DC); -void ra_index_del(zval *z_keys, zval *z_redis TSRMLS_DC); -void ra_index_exec(zval *z_redis, zval *return_value, int keep_all TSRMLS_DC); -void ra_index_discard(zval *z_redis, zval *return_value TSRMLS_DC); -void ra_index_unwatch(zval *z_redis, zval *return_value TSRMLS_DC); +void ra_index_key(const char *key, int key_len, zval *z_redis); +void ra_index_keys(zval *z_pairs, zval *z_redis); +void ra_index_del(zval *z_keys, zval *z_redis); +void ra_index_exec(zval *z_redis, zval *return_value, int keep_all); +void ra_index_discard(zval *z_redis, zval *return_value); +void ra_index_unwatch(zval *z_redis, zval *return_value); zend_bool ra_is_write_cmd(RedisArray *ra, const char *cmd, int cmd_len); -void ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache TSRMLS_DC); +void ra_rehash(RedisArray *ra, zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache); #endif diff --git a/redis_cluster.c b/redis_cluster.c index 591138f6c3..719e5203d2 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -302,7 +302,7 @@ static void ht_free_node(zval *data) { } /* Create redisCluster context */ -zend_object * create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { +zend_object * create_cluster_context(zend_class_entry *class_type) { redisCluster *cluster; // Allocate our actual struct @@ -323,7 +323,7 @@ zend_object * create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { zend_hash_init(cluster->nodes, 0, NULL, ht_free_node, 0); // Initialize it - zend_object_std_init(&cluster->std, class_type TSRMLS_CC); + zend_object_std_init(&cluster->std, class_type); object_properties_init(&cluster->std, class_type); memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); @@ -339,8 +339,8 @@ zend_object * create_cluster_context(zend_class_entry *class_type TSRMLS_DC) { void free_cluster_context(zend_object *object) { redisCluster *cluster = (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std)); - cluster_free(cluster, 0 TSRMLS_CC); - zend_object_std_dtor(&cluster->std TSRMLS_CC); + cluster_free(cluster, 0); + zend_object_std_dtor(&cluster->std); } /* Turn a seed array into a zend_string we can use to look up a slot cache */ @@ -365,7 +365,7 @@ static zend_string *cluster_hash_seeds(HashTable *ht) { } #define SLOT_CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1) -static redisCachedCluster *cluster_cache_load(HashTable *ht_seeds TSRMLS_DC) { +static redisCachedCluster *cluster_cache_load(HashTable *ht_seeds) { zend_resource *le; zend_string *h; @@ -382,7 +382,7 @@ static redisCachedCluster *cluster_cache_load(HashTable *ht_seeds TSRMLS_DC) { if (le != NULL) { /* Sanity check on our list type */ if (le->type != le_cluster_slot_cache) { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Invalid slot cache resource"); + php_error_docref(0, E_WARNING, "Invalid slot cache resource"); return NULL; } @@ -395,7 +395,7 @@ static redisCachedCluster *cluster_cache_load(HashTable *ht_seeds TSRMLS_DC) { } /* Cache a cluster's slot information in persistent_list if it's enabled */ -static int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes TSRMLS_DC) { +static int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes) { redisCachedCluster *cc; zend_string *hash; @@ -445,7 +445,7 @@ cluster_validate_args(double timeout, double read_timeout, HashTable *seeds) { /* Attempt to connect to a Redis cluster provided seeds and timeout options */ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout, double read_timeout, int persistent, char *auth, - size_t auth_len TSRMLS_DC) + size_t auth_len) { redisCachedCluster *cc; @@ -464,18 +464,18 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time c->waitms = (long)(timeout * 1000); /* Attempt to load from cache */ - if ((cc = cluster_cache_load(ht_seeds TSRMLS_CC))) { + if ((cc = cluster_cache_load(ht_seeds))) { cluster_init_cache(c, cc); } else if (cluster_init_seeds(c, ht_seeds) == SUCCESS && - cluster_map_keyspace(c TSRMLS_CC) == SUCCESS) + cluster_map_keyspace(c) == SUCCESS) { - cluster_cache_store(ht_seeds, c->nodes TSRMLS_CC); + cluster_cache_store(ht_seeds, c->nodes); } } /* Attempt to load a named cluster configured in php.ini */ -void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { +void redis_cluster_load(redisCluster *c, char *name, int name_len) { zval z_seeds, z_timeout, z_read_timeout, z_persistent, z_auth, *z_value; char *iptr, *auth = NULL; size_t auth_len = 0; @@ -486,7 +486,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Seeds */ array_init(&z_seeds); if ((iptr = INI_STR("redis.clusters.seeds")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_seeds TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_seeds); } if ((z_value = zend_hash_str_find(Z_ARRVAL(z_seeds), name, name_len)) != NULL) { ht_seeds = Z_ARRVAL_P(z_value); @@ -499,7 +499,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Connection timeout */ array_init(&z_timeout); if ((iptr = INI_STR("redis.clusters.timeout")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_timeout TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_timeout); } if ((z_value = zend_hash_str_find(Z_ARRVAL(z_timeout), name, name_len)) != NULL) { if (Z_TYPE_P(z_value) == IS_STRING) { @@ -514,7 +514,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Read timeout */ array_init(&z_read_timeout); if ((iptr = INI_STR("redis.clusters.read_timeout")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_read_timeout TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_read_timeout); } if ((z_value = zend_hash_str_find(Z_ARRVAL(z_read_timeout), name, name_len)) != NULL) { if (Z_TYPE_P(z_value) == IS_STRING) { @@ -529,7 +529,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Persistent connections */ array_init(&z_persistent); if ((iptr = INI_STR("redis.clusters.persistent")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_persistent TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_persistent); } if ((z_value = zend_hash_str_find(Z_ARRVAL(z_persistent), name, name_len)) != NULL) { if (Z_TYPE_P(z_value) == IS_STRING) { @@ -542,7 +542,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { /* Cluster auth */ array_init(&z_auth); if ((iptr = INI_STR("redis.clusters.auth")) != NULL) { - sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_auth TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_auth); } if ((z_value = zend_hash_str_find(Z_ARRVAL(z_auth), name, name_len)) != NULL && Z_TYPE_P(z_value) == IS_STRING && Z_STRLEN_P(z_value) > 0 @@ -552,7 +552,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { } /* Attempt to create/connect to the cluster */ - redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, auth, auth_len TSRMLS_CC); + redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, auth, auth_len); /* Clean up our arrays */ zval_dtor(&z_seeds); @@ -576,7 +576,7 @@ PHP_METHOD(RedisCluster, __construct) { redisCluster *context = GET_CONTEXT(); // Parse arguments - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os!|addbs", &object, redis_cluster_ce, &name, &name_len, &z_seeds, &timeout, &read_timeout, &persistent, &auth, &auth_len) == FAILURE) @@ -593,9 +593,9 @@ PHP_METHOD(RedisCluster, __construct) { * to a named cluster, stored in php.ini, otherwise we'll need manual seeds */ if (ZEND_NUM_ARGS() > 1) { redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout, - persistent, auth, auth_len TSRMLS_CC); + persistent, auth, auth_len); } else { - redis_cluster_load(context, name, name_len TSRMLS_CC); + redis_cluster_load(context, name, name_len); } } @@ -605,7 +605,7 @@ PHP_METHOD(RedisCluster, __construct) { /* {{{ proto bool RedisCluster::close() */ PHP_METHOD(RedisCluster, close) { - cluster_disconnect(GET_CONTEXT(), 1 TSRMLS_CC); + cluster_disconnect(GET_CONTEXT(), 1); RETURN_TRUE; } @@ -638,7 +638,7 @@ distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, ctx->last = last; // Attempt to send the command - if (cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len TSRMLS_CC) < 0 || + if (cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len) < 0 || c->err != NULL) { cluster_multi_free(mc); @@ -676,7 +676,7 @@ typedef struct clusterKeyValHT { /* Helper to pull a key/value pair from a HashTable */ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, - clusterKeyValHT *kv TSRMLS_DC) + clusterKeyValHT *kv) { zval *z_val; zend_ulong idx; @@ -709,7 +709,7 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, } // Serialize our value if required - kv->val_free = redis_pack(c->flags,z_val,&(kv->val),&(kv->val_len) TSRMLS_CC); + kv->val_free = redis_pack(c->flags,z_val,&(kv->val),&(kv->val_len)); // Success return 0; @@ -717,7 +717,7 @@ static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, /* Helper to pull, prefix, and hash a key from a HashTable value */ static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr, - clusterKeyValHT *kv TSRMLS_DC) + clusterKeyValHT *kv) { zval *z_key; @@ -805,7 +805,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Process the first key outside of our loop, so we don't have to check if // it's the first iteration every time, needlessly zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); - if (get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC) < 0) { + if (get_key_ht(c, ht_arr, &ptr, &kv) < 0) { efree(z_args); return -1; } @@ -822,7 +822,7 @@ static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Iterate over keys 2...N slot = kv.slot; while (zend_hash_has_more_elements_ex(ht_arr, &ptr) ==SUCCESS) { - if (get_key_ht(c, ht_arr, &ptr, &kv TSRMLS_CC) < 0) { + if (get_key_ht(c, ht_arr, &ptr, &kv) < 0) { cluster_multi_free(&mc); if (ht_free) { zend_hash_destroy(ht_arr); @@ -907,7 +907,7 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, short slot; // Parse our arguments - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) { return -1; } @@ -925,7 +925,7 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, // Process the first key/value pair outside of our loop zend_hash_internal_pointer_reset_ex(ht_arr, &ptr); - if (get_key_val_ht(c, ht_arr, &ptr, &kv TSRMLS_CC) ==-1) return -1; + if (get_key_val_ht(c, ht_arr, &ptr, &kv) ==-1) return -1; zend_hash_move_forward_ex(ht_arr, &ptr); // Add this to our multi cmd, set slot, free key if we prefixed @@ -938,7 +938,7 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len, slot = kv.slot; while (zend_hash_has_more_elements_ex(ht_arr, &ptr) ==SUCCESS) { // Pull the next key/value pair - if (get_key_val_ht(c, ht_arr, &ptr, &kv TSRMLS_CC) ==-1) { + if (get_key_val_ht(c, ht_arr, &ptr, &kv) ==-1) { return -1; } @@ -1102,14 +1102,14 @@ PHP_METHOD(RedisCluster, keys) { clusterReply *resp; int i, cmd_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pat, &pat_len) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pat, &pat_len) == FAILURE) { RETURN_FALSE; } /* Prefix and then build our command */ - cmd_len = redis_spprintf(c->flags, NULL TSRMLS_CC, &cmd, "KEYS", "k", pat, pat_len); + cmd_len = redis_spprintf(c->flags, NULL, &cmd, "KEYS", "k", pat, pat_len); array_init(return_value); @@ -1120,9 +1120,9 @@ PHP_METHOD(RedisCluster, keys) { ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) continue; if (cluster_send_slot(c, node->slot, cmd, cmd_len, TYPE_MULTIBULK - TSRMLS_CC) < 0) + ) < 0) { - php_error_docref(0 TSRMLS_CC, E_ERROR, "Can't send KEYS to %s:%d", + php_error_docref(0, E_ERROR, "Can't send KEYS to %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); zval_dtor(return_value); efree(cmd); @@ -1130,9 +1130,9 @@ PHP_METHOD(RedisCluster, keys) { } /* Ensure we can get a response */ - resp = cluster_read_resp(c, 0 TSRMLS_CC); + resp = cluster_read_resp(c, 0); if (!resp) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Can't read response from %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); continue; @@ -1210,7 +1210,7 @@ PHP_METHOD(RedisCluster, srandmember) { RETURN_FALSE; } - if (cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC) < 0 || c->err != NULL) { + if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) { efree(cmd); RETURN_FALSE; } @@ -1738,7 +1738,7 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, RETURN_FALSE; } - if (cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC) < 0 || c->err != NULL) { + if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) { efree(cmd); RETURN_FALSE; } @@ -1881,7 +1881,7 @@ PHP_METHOD(RedisCluster, sort) { RETURN_FALSE; } - if (cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC) < 0 || c->err != NULL) { + if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) { efree(cmd); RETURN_FALSE; } @@ -1908,7 +1908,7 @@ PHP_METHOD(RedisCluster, object) { RETURN_FALSE; } - if (cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC) < 0 || c->err != NULL) { + if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) { efree(cmd); RETURN_FALSE; } @@ -1945,7 +1945,7 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // There is not reason to unsubscribe outside of a subscribe loop if (c->subscribed_slot == -1) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "You can't unsubscribe outside of a subscribe loop"); RETURN_FALSE; } @@ -1960,7 +1960,7 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // This has to operate on our subscribe slot if (cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK - TSRMLS_CC) == FAILURE) + ) == FAILURE) { CLUSTER_THROW_EXCEPTION("Failed to UNSUBSCRIBE within our subscribe loop!", 0); RETURN_FALSE; @@ -2110,7 +2110,7 @@ PHP_METHOD(RedisCluster, multi) { redisCluster *c = GET_CONTEXT(); if (c->flags->mode == MULTI) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "RedisCluster is already in MULTI mode, ignoring"); RETURN_FALSE; } @@ -2135,7 +2135,7 @@ PHP_METHOD(RedisCluster, watch) { // Disallow in MULTI mode if (c->flags->mode == MULTI) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "WATCH command not allowed in MULTI mode"); RETURN_FALSE; } @@ -2189,7 +2189,7 @@ PHP_METHOD(RedisCluster, watch) { } // If we get a failure from this, we have to abort - if (cluster_send_command(c,(short)slot,cmd.c,cmd.len TSRMLS_CC) ==-1) { + if (cluster_send_command(c,(short)slot,cmd.c,cmd.len) ==-1) { RETURN_FALSE; } @@ -2218,7 +2218,7 @@ PHP_METHOD(RedisCluster, unwatch) { if (c->master[slot] && SLOT_SOCK(c,slot)->watching) { if (cluster_send_slot(c, slot, RESP_UNWATCH_CMD, sizeof(RESP_UNWATCH_CMD)-1, - TYPE_LINE TSRMLS_CC) ==-1) + TYPE_LINE) ==-1) { CLUSTER_RETURN_BOOL(c, 0); } @@ -2238,7 +2238,7 @@ PHP_METHOD(RedisCluster, exec) { // Verify we are in fact in multi mode if (CLUSTER_IS_ATOMIC(c)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "RedisCluster is not in MULTI mode"); + php_error_docref(NULL, E_WARNING, "RedisCluster is not in MULTI mode"); RETURN_FALSE; } @@ -2246,8 +2246,8 @@ PHP_METHOD(RedisCluster, exec) { fi = c->multi_head; while (fi) { if (SLOT_SOCK(c, fi->slot)->mode == MULTI) { - if ( cluster_send_exec(c, fi->slot TSRMLS_CC) < 0) { - cluster_abort_exec(c TSRMLS_CC); + if ( cluster_send_exec(c, fi->slot) < 0) { + cluster_abort_exec(c); CLUSTER_THROW_EXCEPTION("Error processing EXEC across the cluster", 0); // Free our queue, reset MULTI state @@ -2276,11 +2276,11 @@ PHP_METHOD(RedisCluster, discard) { redisCluster *c = GET_CONTEXT(); if (CLUSTER_IS_ATOMIC(c)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); + php_error_docref(NULL, E_WARNING, "Cluster is not in MULTI mode"); RETURN_FALSE; } - if (cluster_abort_exec(c TSRMLS_CC) < 0) { + if (cluster_abort_exec(c) < 0) { CLUSTER_RESET_MULTI(c); } @@ -2291,7 +2291,7 @@ PHP_METHOD(RedisCluster, discard) { /* Get a slot either by key (string) or host/port array */ static short -cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) +cluster_cmd_get_slot(redisCluster *c, zval *z_arg) { size_t key_len; int key_free; @@ -2326,11 +2326,11 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) /* Inform the caller if they've passed bad data */ if (slot < 0) { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", + php_error_docref(0, E_WARNING, "Unknown node %s:%ld", Z_STRVAL_P(z_host), Z_LVAL_P(z_port)); } } else { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Direted commands musty be passed a key or [host,port] array"); return -1; } @@ -2350,22 +2350,22 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, zval *z_arg; short slot; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_arg) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z_arg) == FAILURE) { RETURN_FALSE; } // One argument means find the node (treated like a key), and two means // send the command to a specific host and port - slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); + slot = cluster_cmd_get_slot(c, z_arg); if (slot < 0) { RETURN_FALSE; } // Construct our command - cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, kw, ""); + cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, ""); // Kick off our command - if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC) < 0) { + if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0); efree(cmd); RETURN_FALSE; @@ -2388,27 +2388,27 @@ cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply zend_bool async = 0; short slot; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &z_arg, &async) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &z_arg, &async) == FAILURE) { RETURN_FALSE; } // One argument means find the node (treated like a key), and two means // send the command to a specific host and port - slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); + slot = cluster_cmd_get_slot(c, z_arg); if (slot < 0) { RETURN_FALSE; } // Construct our command if (async) { - cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1); + cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1); } else { - cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, kw, ""); + cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, ""); } // Kick off our command - if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type TSRMLS_CC) < 0) { + if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0); efree(cmd); RETURN_FALSE; @@ -2434,14 +2434,14 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) /* Commands using this pass-thru don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Command can't be issued in MULTI mode"); RETURN_FALSE; } /* We at least need the key or [host,port] argument */ if (argc < 1) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Command requires at least an argument to direct to a node"); RETURN_FALSE; } @@ -2456,7 +2456,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* First argument needs to be the "where" */ - if ((slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC)) < 0) { + if ((slot = cluster_cmd_get_slot(c, &z_args[0])) < 0) { efree(z_args); RETURN_FALSE; } @@ -2472,7 +2472,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* Send it off */ - if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF TSRMLS_CC) < 0) { + if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0); efree(cmd.c); efree(z_args); @@ -2507,7 +2507,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, } /* Parse arguments */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/|s!l", &key, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/|s!l", &key, &key_len, &z_it, &pat, &pat_len, &count) == FAILURE) { RETURN_FALSE; @@ -2545,7 +2545,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, count); // Send it off - if (cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC) == FAILURE) + if (cluster_send_command(c, slot, cmd, cmd_len) == FAILURE) { CLUSTER_THROW_EXCEPTION("Couldn't send SCAN command", 0); if (key_free) efree(key); @@ -2599,7 +2599,7 @@ PHP_METHOD(RedisCluster, scan) { } /* Parse arguments */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z|s!l", &z_it, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/z|s!l", &z_it, &z_node, &pat, &pat_len, &count) == FAILURE) { RETURN_FALSE; @@ -2628,12 +2628,12 @@ PHP_METHOD(RedisCluster, scan) { cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, count); - if ((slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC)) < 0) { + if ((slot = cluster_cmd_get_slot(c, z_node)) < 0) { RETURN_FALSE; } // Send it to the node in question - if (cluster_send_command(c, slot, cmd, cmd_len TSRMLS_CC) < 0) + if (cluster_send_command(c, slot, cmd, cmd_len) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send SCAN to node", 0); efree(cmd); @@ -2744,7 +2744,7 @@ PHP_METHOD(RedisCluster, info) { zval *z_arg; short slot; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &z_arg, &opt, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s", &z_arg, &opt, &opt_len) == FAILURE) { RETURN_FALSE; @@ -2753,19 +2753,19 @@ PHP_METHOD(RedisCluster, info) { /* Treat INFO as non read-only, as we probably want the master */ c->readonly = 0; - slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); + slot = cluster_cmd_get_slot(c, z_arg); if (slot < 0) { RETURN_FALSE; } if (opt != NULL) { - cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "INFO", "s", opt, opt_len); + cmd_len = redis_spprintf(NULL, NULL, &cmd, "INFO", "s", opt, opt_len); } else { - cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "INFO", ""); + cmd_len = redis_spprintf(NULL, NULL, &cmd, "INFO", ""); } rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; - if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC) < 0) { + if (cluster_send_slot(c, slot, cmd, cmd_len, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send INFO command to specific node", 0); efree(cmd); RETURN_FALSE; @@ -2797,14 +2797,14 @@ PHP_METHOD(RedisCluster, client) { cluster_cb cb; /* Parse args */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|s", &z_node, &opt, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs|s", &z_node, &opt, &opt_len, &arg, &arg_len) == FAILURE) { RETURN_FALSE; } /* Make sure we can properly resolve the slot */ - slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC); + slot = cluster_cmd_get_slot(c, z_node); if (slot < 0) RETURN_FALSE; /* Our return type and reply callback is different for all subcommands */ @@ -2820,25 +2820,25 @@ PHP_METHOD(RedisCluster, client) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; cb = cluster_bulk_resp; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Invalid CLIENT subcommand (LIST, KILL, GETNAME, and SETNAME are valid"); RETURN_FALSE; } /* Construct the command */ if (ZEND_NUM_ARGS() == 3) { - cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "CLIENT", "ss", + cmd_len = redis_spprintf(NULL, NULL, &cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len); } else if (ZEND_NUM_ARGS() == 2) { - cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "CLIENT", "s", + cmd_len = redis_spprintf(NULL, NULL, &cmd, "CLIENT", "s", opt, opt_len); } else { - zend_wrong_param_count(TSRMLS_C); + zend_wrong_param_count(); RETURN_FALSE; } /* Attempt to write our command */ - if (cluster_send_slot(c, slot, cmd, cmd_len, rtype TSRMLS_CC) < 0) { + if (cluster_send_slot(c, slot, cmd, cmd_len, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send CLIENT command to specific node", 0); efree(cmd); RETURN_FALSE; @@ -2891,14 +2891,14 @@ PHP_METHOD(RedisCluster, script) { /* Commands using this pass-thru don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Command can't be issued in MULTI mode"); RETURN_FALSE; } /* We at least need the key or [host,port] argument */ if (argc < 2) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Command requires at least an argument to direct to a node"); RETURN_FALSE; } @@ -2908,7 +2908,7 @@ PHP_METHOD(RedisCluster, script) { /* Grab args */ if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || - (slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC)) < 0 || + (slot = cluster_cmd_get_slot(c, &z_args[0])) < 0 || redis_build_script_cmd(&cmd, argc - 1, &z_args[1]) == NULL ) { efree(z_args); @@ -2916,7 +2916,7 @@ PHP_METHOD(RedisCluster, script) { } /* Send it off */ - if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF TSRMLS_CC) < 0) { + if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0); efree(cmd.c); efree(z_args); @@ -3014,7 +3014,7 @@ PHP_METHOD(RedisCluster, ping) { size_t arglen; short slot; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s!", &z_node, &arg, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!", &z_node, &arg, &arglen) == FAILURE) { RETURN_FALSE; @@ -3024,21 +3024,21 @@ PHP_METHOD(RedisCluster, ping) { c->readonly = CLUSTER_IS_ATOMIC(c); /* Grab slot either by key or host/port */ - slot = cluster_cmd_get_slot(c, z_node TSRMLS_CC); + slot = cluster_cmd_get_slot(c, z_node); if (slot < 0) { RETURN_FALSE; } /* Construct our command */ if (arg != NULL) { - cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "PING", "s", arg, arglen); + cmdlen = redis_spprintf(NULL, NULL, &cmd, "PING", "s", arg, arglen); } else { - cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "PING", ""); + cmdlen = redis_spprintf(NULL, NULL, &cmd, "PING", ""); } /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) && arg != NULL ? TYPE_BULK : TYPE_LINE; - if (cluster_send_slot(c, slot, cmd, cmdlen, rtype TSRMLS_CC) < 0) { + if (cluster_send_slot(c, slot, cmd, cmdlen, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send commnad at the specificed node", 0); efree(cmd); RETURN_FALSE; @@ -3140,7 +3140,7 @@ PHP_METHOD(RedisCluster, echo) { size_t msg_len; short slot; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &z_arg, &msg, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs", &z_arg, &msg, &msg_len) == FAILURE) { RETURN_FALSE; @@ -3150,17 +3150,17 @@ PHP_METHOD(RedisCluster, echo) { c->readonly = CLUSTER_IS_ATOMIC(c); /* Grab slot either by key or host/port */ - slot = cluster_cmd_get_slot(c, z_arg TSRMLS_CC); + slot = cluster_cmd_get_slot(c, z_arg); if (slot < 0) { RETURN_FALSE; } /* Construct our command */ - cmd_len = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "ECHO", "s", msg, msg_len); + cmd_len = redis_spprintf(NULL, NULL, &cmd, "ECHO", "s", msg, msg_len); /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; - if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC) < 0) { + if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send commnad at the specificed node", 0); efree(cmd); RETURN_FALSE; @@ -3190,18 +3190,18 @@ PHP_METHOD(RedisCluster, rawcommand) { /* Sanity check on our arguments */ if (argc < 2) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "You must pass at least node information as well as at least a command."); RETURN_FALSE; } z_args = emalloc(argc * sizeof(zval)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Internal PHP error parsing method parameters."); efree(z_args); RETURN_FALSE; - } else if (redis_build_raw_cmd(&z_args[1], argc-1, &cmd, &cmd_len TSRMLS_CC) || - (slot = cluster_cmd_get_slot(c, &z_args[0] TSRMLS_CC)) < 0) + } else if (redis_build_raw_cmd(&z_args[1], argc-1, &cmd, &cmd_len) || + (slot = cluster_cmd_get_slot(c, &z_args[0])) < 0) { if (cmd) efree(cmd); efree(z_args); @@ -3213,7 +3213,7 @@ PHP_METHOD(RedisCluster, rawcommand) { /* Direct the command */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE; - if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC) < 0) { + if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command to the specified node", 0); efree(cmd); RETURN_FALSE; diff --git a/redis_cluster.h b/redis_cluster.h index ca76ac8a20..414c489d60 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -65,7 +65,7 @@ &cmd_len, &slot, &ctx)==FAILURE) { \ RETURN_FALSE; \ } \ - if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) {\ + if(cluster_send_command(c,slot,cmd,cmd_len)<0 || c->err!=NULL) {\ efree(cmd); \ RETURN_FALSE; \ } \ @@ -85,7 +85,7 @@ &slot,&ctx)==FAILURE) { \ RETURN_FALSE; \ } \ - if(cluster_send_command(c,slot,cmd,cmd_len TSRMLS_CC)<0 || c->err!=NULL) { \ + if(cluster_send_command(c,slot,cmd,cmd_len)<0 || c->err!=NULL) { \ efree(cmd); \ RETURN_FALSE; \ } \ @@ -97,17 +97,17 @@ resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); /* For the creation of RedisCluster specific exceptions */ -PHP_REDIS_API zend_class_entry *rediscluster_get_exception_base(int root TSRMLS_DC); +PHP_REDIS_API zend_class_entry *rediscluster_get_exception_base(int root); /* Create cluster context */ -zend_object *create_cluster_context(zend_class_entry *class_type TSRMLS_DC); +zend_object *create_cluster_context(zend_class_entry *class_type); /* Free cluster context struct */ void free_cluster_context(zend_object *object); /* Inittialize our class with PHP */ -void init_rediscluster(TSRMLS_D); +void init_rediscluster(void); /* RedisCluster method implementation */ PHP_METHOD(RedisCluster, __construct); diff --git a/redis_commands.c b/redis_commands.c index e6f0d2c81c..9598ebe989 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -61,9 +61,9 @@ typedef struct geoOptions { /* Local passthrough macro for command construction. Given that these methods * are generic (so they work whether the caller is Redis or RedisCluster) we - * will always have redis_sock, slot*, and TSRMLS_CC */ + * will always have redis_sock, slot*, and */ #define REDIS_CMD_SPPRINTF(ret, kw, fmt, ...) \ - redis_spprintf(redis_sock, slot TSRMLS_CC, ret, kw, fmt, ##__VA_ARGS__) + redis_spprintf(redis_sock, slot, ret, kw, fmt, ##__VA_ARGS__) /* Generic commands based on method signature and what kind of things we're * processing. Lots of Redis commands take something like key, value, or @@ -81,14 +81,14 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Helper to construct a raw command. Given that the cluster and non cluster * versions are different (RedisCluster needs an additional argument to direct * the command) we take the start of our array and count */ -int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC) +int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len) { smart_string cmdstr = {0}; int i; /* Make sure our first argument is a string */ if (Z_TYPE(z_args[0]) != IS_STRING) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "When sending a 'raw' command, the first argument must be a string!"); return FAILURE; } @@ -109,7 +109,7 @@ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len TSRMLS_ redis_cmd_append_sstr_dbl(&cmdstr,Z_DVAL(z_args[i])); break; default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Raw command arguments must be scalar values!"); efree(cmdstr.c); return FAILURE; @@ -175,7 +175,7 @@ int redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char char *arg = NULL; size_t arglen; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &arg, &arglen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &arg, &arglen) == FAILURE) { return FAILURE; } @@ -196,7 +196,7 @@ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, size_t arg_len; // Parse args - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) ==FAILURE) { return FAILURE; @@ -218,7 +218,7 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long expire; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &key, &key_len, &expire, &z_val) == FAILURE) { return FAILURE; @@ -238,7 +238,7 @@ int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len, val_len; zend_long lval; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &key, &key_len, &lval, &val, &val_len) == FAILURE) { return FAILURE; @@ -258,7 +258,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &key, &key_len, &z_val) == FAILURE) { return FAILURE; @@ -277,7 +277,7 @@ int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *val; size_t key_len, val_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &val, &val_len) == FAILURE) { return FAILURE; @@ -297,7 +297,7 @@ int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *k, *v1, *v2; size_t klen, v1len, v2len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &k, &klen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &k, &klen, &v1, &v1len, &v2, &v2len) == FAILURE) { return FAILURE; @@ -318,7 +318,7 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t k1len, k2len; int k1free, k2free; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &k1, &k1len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &k1, &k1len, &k2, &k2len) == FAILURE) { return FAILURE; @@ -336,7 +336,7 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Check if Redis would give us a CROSSLOT error if (slot1 != slot2) { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Keys don't hash to the same slot"); + php_error_docref(0, E_WARNING, "Keys don't hash to the same slot"); if (k1free) efree(k1); if (k2free) efree(k2); return FAILURE; @@ -366,7 +366,7 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t keylen; zend_long lval; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &key, &keylen, &lval) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &key, &keylen, &lval) ==FAILURE) { return FAILURE; @@ -385,7 +385,7 @@ int redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zend_long v1, v2; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &v1, &v2) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &v1, &v2) == FAILURE) { return FAILURE; @@ -405,7 +405,7 @@ int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len; zend_long val1, val2; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &key, &key_len, &val1, &val2) == FAILURE) { return FAILURE; @@ -424,7 +424,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; size_t key_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) ==FAILURE) { return FAILURE; @@ -440,7 +440,7 @@ int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { zend_bool async = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &async) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &async) == FAILURE) { return FAILURE; } @@ -462,7 +462,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len; double val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &key, &key_len, &val) == FAILURE) { return FAILURE; @@ -528,7 +528,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string *zkey; zval *z_ws = NULL, *z_ele; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|z", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|z", &key, &key_len, &start, &end, &z_ws) == FAILURE) { return FAILURE; @@ -577,7 +577,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, PHPREDIS_NOTUSED(idx); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) ==FAILURE) { @@ -647,7 +647,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; // Parse args - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a!s", &key, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a!s", &key, &key_len, &z_keys, &z_weights, &agg_op, &agg_op_len) == FAILURE) { @@ -668,7 +668,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (z_weights != NULL) { ht_weights = Z_ARRVAL_P(z_weights); if (zend_hash_num_elements(ht_weights) != keys_count) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "WEIGHTS and keys array should be the same size!"); return FAILURE; } @@ -683,7 +683,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, strncasecmp(agg_op, "MIN", sizeof("MIN")) && strncasecmp(agg_op, "MAX", sizeof("MAX"))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Invalid AGGREGATE option provided!"); return FAILURE; } @@ -715,7 +715,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // If we're in Cluster mode, verify the slot is the same if (slot && *slot != cluster_hash_key(key,key_len)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot!"); efree(cmdstr.c); zend_string_release(zstr); @@ -766,7 +766,7 @@ int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // fall through } default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Weights must be numeric or '-inf','inf','+inf'"); efree(cmdstr.c); return FAILURE; @@ -800,7 +800,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int key_free; char *key; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &z_arr, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &z_arr, &(sctx->cb), &(sctx->cb_cache)) == FAILURE) { efree(sctx); @@ -858,7 +858,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; subscribeContext *sctx = emalloc(sizeof(subscribeContext)); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_arr) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) { efree(sctx); return FAILURE; } @@ -903,11 +903,11 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* We need either 3 or 5 arguments for this to be valid */ if (argc != 3 && argc != 5) { - php_error_docref(0 TSRMLS_CC, E_WARNING, "Must pass either 3 or 5 arguments"); + php_error_docref(0, E_WARNING, "Must pass either 3 or 5 arguments"); return FAILURE; } - if (zend_parse_parameters(argc TSRMLS_CC, "sss|ll", &key, &key_len, &min, &min_len, + if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, &max, &max_len, &offset, &count) == FAILURE) { return FAILURE; @@ -920,7 +920,7 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, (max[0] != '(' && max[0] != '[' && (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "min and max arguments must start with '[' or '('"); return FAILURE; } @@ -952,7 +952,7 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len, min_len, max_len; /* Parse args */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &min, &min_len, &max, &max_len) == FAILURE) { return FAILURE; @@ -960,7 +960,7 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Quick sanity check on min/max */ if (!validate_zlex_arg(min, min_len) || !validate_zlex_arg(max, max_len)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Min/Max args can be '-' or '+', or start with '[' or '('"); return FAILURE; } @@ -987,7 +987,7 @@ int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw short prevslot = -1; /* Parse args */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|al", &lua, &lua_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &lua, &lua_len, &z_arr, &num_keys) == FAILURE) { return FAILURE; @@ -1017,7 +1017,7 @@ int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw if (slot) { if (prevslot != -1 && prevslot != *slot) { zend_string_release(zstr); - php_error_docref(0 TSRMLS_CC, E_WARNING, "All keys do not map to the same slot"); + php_error_docref(0, E_WARNING, "All keys do not map to the same slot"); return FAILURE; } prevslot = *slot; @@ -1071,7 +1071,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Add members */ for (i = 1; i < argc; i++ ){ - redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock TSRMLS_CC); + redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock); } // Push out values @@ -1100,7 +1100,7 @@ static int gen_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t val_len, key_len; char *key, *val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE || zend_hash_num_elements(Z_ARRVAL_P(z_arr)) == 0) { @@ -1122,7 +1122,7 @@ static int gen_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, assert(valtype == VAL_TYPE_VALUES || valtype == VAL_TYPE_STRINGS); ZEND_HASH_FOREACH_VAL(ht_arr, z_val) { if (valtype == VAL_TYPE_VALUES) { - val_free = redis_pack(redis_sock, z_val, &val, &val_len TSRMLS_CC); + val_free = redis_pack(redis_sock, z_val, &val, &val_len); redis_cmd_append_sstr(&cmdstr, val, val_len); if (val_free) efree(val); } else { @@ -1173,7 +1173,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string *zstr; if (argc < min_argc) { - zend_wrong_param_count(TSRMLS_C); + zend_wrong_param_count(); return FAILURE; } @@ -1222,7 +1222,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else if (cluster_hash_key(key,key_len)!=kslot) { zend_string_release(zstr); if (key_free) efree(key); - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); return FAILURE; } @@ -1238,7 +1238,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } else { if (has_timeout && Z_TYPE(z_args[argc-1])!=IS_LONG) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, + php_error_docref(NULL, E_ERROR, "Timeout value must be a LONG"); efree(z_args); return FAILURE; @@ -1257,7 +1257,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if ( kslot == -1) { kslot = cluster_hash_key(key, key_len); } else if (cluster_hash_key(key,key_len)!=kslot) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot"); zend_string_release(zstr); if (key_free) efree(key); @@ -1311,7 +1311,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len; // Make sure the function is being called correctly - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &key, &key_len, &z_value, &z_opts) == FAILURE) { return FAILURE; @@ -1400,7 +1400,7 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short slot1, slot2; zend_long timeout; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key1, &key1_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key1, &key1_len, &key2, &key2_len, &timeout) == FAILURE) { return FAILURE; @@ -1415,7 +1415,7 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, slot1 = cluster_hash_key(key1, key1_len); slot2 = cluster_hash_key(key2, key2_len); if (slot1 != slot2) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Keys hash to different slots!"); if (key1_free) efree(key1); if (key2_free) efree(key2); @@ -1456,7 +1456,7 @@ redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, size_t key_len; zend_long val = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &val) == FAILURE) { return FAILURE; @@ -1506,7 +1506,7 @@ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len, mem_len; zend_long byval; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { return FAILURE; @@ -1527,7 +1527,7 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len, mem_len; double byval; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssd", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssd", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { return FAILURE; @@ -1553,7 +1553,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; // Parse arguments - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { return FAILURE; @@ -1640,7 +1640,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_val; // Parse args - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { return FAILURE; @@ -1678,7 +1678,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } // Serialize value (if directed) - val_free = redis_pack(redis_sock, z_val, &val, &val_len TSRMLS_CC); + val_free = redis_pack(redis_sock, z_val, &val, &val_len); // Append the key and value to our command redis_cmd_append_sstr(&cmdstr, mem, mem_len); @@ -1710,7 +1710,7 @@ redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *field; size_t key_len, field_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &field, &field_len) == FAILURE ) { return FAILURE; @@ -1731,7 +1731,7 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len; argc = ZEND_NUM_ARGS(); - if (zend_parse_parameters(argc TSRMLS_CC, "sl|ll", &key, &key_len, &bit, + if (zend_parse_parameters(argc, "sl|ll", &key, &key_len, &bit, &start, &end) == FAILURE) { return FAILURE; @@ -1801,7 +1801,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (slot) { kslot = cluster_hash_key(key, key_len); if (*slot == -1 || kslot != *slot) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!"); zend_string_release(zstr); if (key_free) efree(key); @@ -1833,7 +1833,7 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len; zend_long start = 0, end = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &key, &key_len, &start, &end) == FAILURE) { return FAILURE; @@ -1860,7 +1860,7 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string *zstr; // Parse arguments - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { return FAILURE; @@ -1899,14 +1899,14 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Verify slot if (slot && *slot != cluster_hash_key(mem, mem_len)) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "All keys must hash to the same slot!"); zend_string_release(zstr); if (key_free) efree(key); return FAILURE; } } else { - mem_free = redis_pack(redis_sock, z_ele, &mem, &mem_len TSRMLS_CC); + mem_free = redis_pack(redis_sock, z_ele, &mem, &mem_len); zstr = NULL; if (!mem_free) { @@ -1960,7 +1960,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short kslot=-1; zend_string *zstr; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"z",&z_keys) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(),"z",&z_keys) == FAILURE) { return FAILURE; } @@ -2000,7 +2000,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (key_free) efree(key); efree(cmdstr.c); - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); return FAILURE; } @@ -2042,7 +2042,7 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *pw; size_t pw_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pw, &pw_len) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pw, &pw_len) ==FAILURE) { return FAILURE; @@ -2068,7 +2068,7 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long offset; zend_bool val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slb", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "slb", &key, &key_len, &offset, &val) == FAILURE) { return FAILURE; @@ -2076,7 +2076,7 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Validate our offset if (offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Invalid OFFSET for bitop command (must be between 0-2^32-1)"); return FAILURE; } @@ -2094,7 +2094,7 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len, pos_len; zval *z_val, *z_pivot; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sszz", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszz", &key, &key_len, &pos, &pos_len, &z_pivot, &z_val) == FAILURE) { return FAILURE; @@ -2102,7 +2102,7 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Validate position if (strncasecmp(pos, "after", 5) && strncasecmp(pos, "before", 6)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Position must be either 'BEFORE' or 'AFTER'"); return FAILURE; } @@ -2124,7 +2124,7 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long count = 0; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|l", &key, &key_len, &z_val, &count) == FAILURE) { return FAILURE; @@ -2145,7 +2145,7 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int src_free, dst_free; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &src, &src_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &src, &src_len, &dst, &dst_len, &z_val) == FAILURE) { return FAILURE; @@ -2159,7 +2159,7 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, short slot1 = cluster_hash_key(src, src_len); short slot2 = cluster_hash_key(dst, dst_len); if (slot1 != slot2) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Source and destination keys don't hash to the same slot!"); if (src_free) efree(src); if (dst_free) efree(dst); @@ -2188,7 +2188,7 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len, mem_len; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, &mem, &mem_len, &z_val) == FAILURE) { return FAILURE; @@ -2226,7 +2226,7 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len; zend_long count; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { return FAILURE; @@ -2254,7 +2254,7 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, double incrby; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sdz", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sdz", &key, &key_len, &incrby, &z_val) == FAILURE) { return FAILURE; @@ -2277,7 +2277,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len; int key_free; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &key, &key_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, &z_opts) == FAILURE) { return FAILURE; @@ -2318,7 +2318,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) { // "BY" option is disabled in cluster if (slot) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "SORT BY option is not allowed in Redis Cluster"); zval_dtor(&z_argv); return FAILURE; @@ -2348,7 +2348,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); if (cross_slot) { - php_error_docref(0 TSRMLS_CC, E_WARNING, + php_error_docref(0, E_WARNING, "Error, SORT key and STORE key have different slots!"); zval_dtor(&z_argv); return FAILURE; @@ -2369,7 +2369,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) { // Disabled in cluster if (slot) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "GET option for SORT disabled in Redis Cluster"); zval_dtor(&z_argv); return FAILURE; @@ -2398,7 +2398,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Make sure we were able to add at least one if (added == 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Array of GET values requested, but none are valid"); zval_dtor(&z_argv); return FAILURE; @@ -2428,7 +2428,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if ((Z_TYPE_P(z_off) != IS_STRING && Z_TYPE_P(z_off) != IS_LONG) || (Z_TYPE_P(z_cnt) != IS_STRING && Z_TYPE_P(z_cnt) != IS_LONG) ) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "LIMIT options on SORT command must be longs or strings"); zval_dtor(&z_argv); return FAILURE; @@ -2632,7 +2632,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(&z_args[i])); } // serialize value if requested - val_free = redis_pack(redis_sock, &z_args[i+1], &val, &val_len TSRMLS_CC); + val_free = redis_pack(redis_sock, &z_args[i+1], &val, &val_len); redis_cmd_append_sstr(&cmdstr, val, val_len); // Free value if we serialized @@ -2658,7 +2658,7 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *subcmd; size_t key_len, subcmd_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &subcmd, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &subcmd, &subcmd_len, &key, &key_len) == FAILURE) { return FAILURE; @@ -2675,7 +2675,7 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else if (subcmd_len == 8 && !strncasecmp(subcmd, "encoding", 8)) { *rtype = TYPE_BULK; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Invalid subcommand sent to OBJECT"); efree(*cmd); return FAILURE; @@ -2692,7 +2692,7 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *source, *dest, *unit = NULL; size_t keylen, sourcelen, destlen, unitlen; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|s", &key, &keylen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|s", &key, &keylen, &source, &sourcelen, &dest, &destlen, &unit, &unitlen) == FAILURE) { @@ -2722,7 +2722,7 @@ geoStoreType get_georadius_store_type(zend_string *key) { } /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */ -static int get_georadius_opts(HashTable *ht, geoOptions *opts TSRMLS_DC) { +static int get_georadius_opts(HashTable *ht, geoOptions *opts) { ulong idx; char *optstr; zend_string *zkey; @@ -2738,7 +2738,7 @@ static int get_georadius_opts(HashTable *ht, geoOptions *opts TSRMLS_DC) { if (zkey) { if (ZSTR_LEN(zkey) == 5 && !strcasecmp(ZSTR_VAL(zkey), "count")) { if (Z_TYPE_P(optval) != IS_LONG || Z_LVAL_P(optval) <= 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "COUNT must be an integer > 0!"); if (opts->key) zend_string_release(opts->key); return FAILURE; @@ -2774,7 +2774,7 @@ static int get_georadius_opts(HashTable *ht, geoOptions *opts TSRMLS_DC) { /* STORE and STOREDIST are not compatible with the WITH* options */ if (opts->key != NULL && (opts->withcoord || opts->withdist || opts->withhash)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "STORE[DIST] is not compatible with WITHCOORD, WITHDIST or WITHHASH"); if (opts->key) zend_string_release(opts->key); @@ -2847,7 +2847,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, geoOptions gopts = {0}; smart_string cmdstr = {0}; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sddds|a", &key, &keylen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sddds|a", &key, &keylen, &lng, &lat, &radius, &unit, &unitlen, &opts) == FAILURE) { @@ -2857,7 +2857,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Parse any GEORADIUS options we have */ if (opts != NULL) { /* Attempt to parse our options array */ - if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts TSRMLS_CC) != SUCCESS) + if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts) != SUCCESS) { return FAILURE; } @@ -2891,7 +2891,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Protect the user from CROSSSLOT if we're in cluster */ if (slot && gopts.store != STORE_NONE && *slot != store_slot) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Key and STORE[DIST] key must hash to the same slot"); efree(cmdstr.c); return FAILURE; @@ -2919,7 +2919,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s zval *opts = NULL; smart_string cmdstr = {0}; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssds|a", &key, &keylen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssds|a", &key, &keylen, &mem, &memlen, &radius, &unit, &unitlen, &opts) == FAILURE) { return FAILURE; @@ -2927,7 +2927,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s if (opts != NULL) { /* Attempt to parse our options array */ - if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts TSRMLS_CC) == FAILURE) { + if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts) == FAILURE) { return FAILURE; } } @@ -2959,7 +2959,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s /* Protect the user from CROSSSLOT if we're in cluster */ if (slot && gopts.store != STORE_NONE && *slot != store_slot) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Key and STORE[DIST] key must hash to the same slot"); efree(cmdstr.c); return FAILURE; @@ -2984,7 +2984,7 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_bool copy = 0, replace = 0; zend_string *zstr; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slzll|bb", &host, &hostlen, &port, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "slzll|bb", &host, &hostlen, &port, &z_keys, &destdb, &timeout, ©, &replace) == FAILURE) { return FAILURE; @@ -2992,7 +2992,7 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Protect against being passed an array with zero elements */ if (Z_TYPE_P(z_keys) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(z_keys)) == 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Keys array cannot be empty"); + php_error_docref(NULL, E_WARNING, "Keys array cannot be empty"); return FAILURE; } @@ -3146,7 +3146,7 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t kw_len; /* Parse our args */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sz", &kw, &kw_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sz", &kw, &kw_len, &z_arg) == FAILURE) { return FAILURE; @@ -3220,7 +3220,7 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key, *id; size_t keylen, idlen; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa|lb", &key, &keylen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|lb", &key, &keylen, &id, &idlen, &z_fields, &maxlen, &approx) == FAILURE) { return FAILURE; @@ -3233,7 +3233,7 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } if (maxlen < 0 || (maxlen == 0 && approx != 0)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Warning: Invalid MAXLEN argument or approximate flag"); } @@ -3258,7 +3258,7 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr(&cmdstr, id, idlen); ZEND_HASH_FOREACH_KEY_VAL(ht_fields, idx, arrkey, value) { redis_cmd_append_sstr_arrkey(&cmdstr, arrkey, idx); - redis_cmd_append_sstr_zval(&cmdstr, value, redis_sock TSRMLS_CC); + redis_cmd_append_sstr_zval(&cmdstr, value, redis_sock); } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; @@ -3277,7 +3277,7 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long count = -1; // XPENDING mystream group55 - + 10 consumer-123 - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ssls", &key, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ssls", &key, &keylen, &group, &grouplen, &start, &startlen, &end, &endlen, &count, &consumer, &consumerlen) == FAILURE) @@ -3323,7 +3323,7 @@ int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t keylen, startlen, endlen; zend_long count = -1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|l", &key, &keylen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &keylen, &start, &startlen, &end, &endlen, &count) == FAILURE) { @@ -3349,7 +3349,7 @@ int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * STREAMS stream [stream...] id [id ...] arguments to a command string. */ static int append_stream_args(smart_string *cmdstr, HashTable *ht, RedisSock *redis_sock, - short *slot TSRMLS_DC) + short *slot) { char *kptr, kbuf[40]; int klen, i, pos = 0; @@ -3384,7 +3384,7 @@ append_stream_args(smart_string *cmdstr, HashTable *ht, RedisSock *redis_sock, /* Protect the user against CROSSSLOT to avoid confusion */ if (slot) { if (oldslot != -1 && *slot != oldslot) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!"); efree(id); return FAILURE; @@ -3416,7 +3416,7 @@ int redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int argc, scount; HashTable *kt; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|ll", &z_streams, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|ll", &z_streams, &count, &block) == FAILURE) { return FAILURE; @@ -3445,7 +3445,7 @@ int redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Append final STREAM key [key ...] id [id ...] arguments */ - if (append_stream_args(&cmdstr, kt, redis_sock, slot TSRMLS_CC) < 0) { + if (append_stream_args(&cmdstr, kt, redis_sock, slot) < 0) { efree(cmdstr.c); return FAILURE; } @@ -3469,7 +3469,7 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long count, block; zend_bool no_count = 1, no_block = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa|l!l!", &group, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|l!l!", &group, &grouplen, &consumer, &consumerlen, &z_streams, &count, &no_count, &block, &no_block) == FAILURE) { @@ -3478,7 +3478,7 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Negative COUNT or BLOCK is illegal so abort immediately */ if ((!no_count && count < 0) || (!no_block && block < 0)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values for COUNT or BLOCK are illegal."); + php_error_docref(NULL, E_WARNING, "Negative values for COUNT or BLOCK are illegal."); return FAILURE; } @@ -3510,7 +3510,7 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Finally append stream and id args */ - if (append_stream_args(&cmdstr, kt, redis_sock, slot TSRMLS_CC) < 0) { + if (append_stream_args(&cmdstr, kt, redis_sock, slot) < 0) { efree(cmdstr.c); return FAILURE; } @@ -3532,7 +3532,7 @@ int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_ids; int idcount; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssa", &key, &keylen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa", &key, &keylen, &group, &grouplen, &z_ids) == FAILURE) { return FAILURE; @@ -3601,12 +3601,12 @@ static int zval_get_i64(zval *zv, int64_t *retval) { * 32-bit PHP long so we have to extract it as an int64_t. If the value is * not a valid number or negative, we'll inform the user of the problem and * that the argument is being ignored. */ -static int64_t get_xclaim_i64_arg(const char *key, zval *zv TSRMLS_DC) { +static int64_t get_xclaim_i64_arg(const char *key, zval *zv) { int64_t retval = -1; /* Extract an i64, and if we can't let the user know there is an issue. */ if (zval_get_i64(zv, &retval) == FAILURE || retval < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Invalid XCLAIM option '%s' will be ignored", key); } @@ -3614,7 +3614,7 @@ static int64_t get_xclaim_i64_arg(const char *key, zval *zv TSRMLS_DC) { } /* Helper to extract XCLAIM options */ -static void get_xclaim_options(zval *z_arr, xclaimOptions *opt TSRMLS_DC) { +static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) { HashTable *ht; zend_string *zkey; char *kval; @@ -3643,10 +3643,10 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt TSRMLS_DC) { if (klen == 4) { if (!strncasecmp(kval, "TIME", 4)) { opt->idle.type = "TIME"; - opt->idle.time = get_xclaim_i64_arg("TIME", zv TSRMLS_CC); + opt->idle.time = get_xclaim_i64_arg("TIME", zv); } else if (!strncasecmp(kval, "IDLE", 4)) { opt->idle.type = "IDLE"; - opt->idle.time = get_xclaim_i64_arg("IDLE", zv TSRMLS_CC); + opt->idle.time = get_xclaim_i64_arg("IDLE", zv); } } else if (klen == 10 && !strncasecmp(kval, "RETRYCOUNT", 10)) { opt->retrycount = zval_get_long(zv); @@ -3718,7 +3718,7 @@ int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_ids; xclaimOptions opts; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sssla|a", &key, &keylen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssla|a", &key, &keylen, &group, &grouplen, &consumer, &consumerlen, &min_idle, &z_ids, &z_opts) == FAILURE) { @@ -3732,7 +3732,7 @@ int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Extract options array if we've got them */ - get_xclaim_options(z_opts, &opts TSRMLS_CC); + get_xclaim_options(z_opts, &opts); /* Now we have enough information to calculate argc */ argc = 4 + id_count + xclaim_options_argc(&opts); @@ -3773,7 +3773,7 @@ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_bool mkstream = 0; int argc = ZEND_NUM_ARGS(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sssb", &op, &oplen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sssb", &op, &oplen, &key, &keylen, &arg1, &arg1len, &arg2, &arg2len, &mkstream) == FAILURE) { @@ -3823,7 +3823,7 @@ int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char fmt[4]; int argc = ZEND_NUM_ARGS(); - if (argc > 3 || zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ss", + if (argc > 3 || zend_parse_parameters(ZEND_NUM_ARGS(), "s|ss", &op, &oplen, &key, &keylen, &arg, &arglen) == FAILURE) { @@ -3848,7 +3848,7 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long maxlen; zend_bool approx = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|b", &key, &keylen, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|b", &key, &keylen, &maxlen, &approx) == FAILURE) { return FAILURE; @@ -3876,7 +3876,7 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, { zend_long option; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &option) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &option) == FAILURE) { RETURN_FALSE; @@ -3920,7 +3920,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, int tcp_keepalive = 0; php_netstream_data_t *sock; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &option, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &option, &val) == FAILURE) { RETURN_FALSE; @@ -4035,7 +4035,7 @@ void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { char *key; size_t key_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) ==FAILURE) { RETURN_FALSE; @@ -4057,11 +4057,11 @@ void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, char *val; size_t val_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z_val) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z_val) == FAILURE) { RETURN_FALSE; } - int val_free = redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC); + int val_free = redis_serialize(redis_sock, z_val, &val, &val_len); RETVAL_STRINGL(val, val_len); if (val_free) efree(val); @@ -4074,7 +4074,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, size_t value_len; // Parse our arguments - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) == FAILURE) { RETURN_FALSE; @@ -4086,7 +4086,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_STRINGL(value, value_len); } zval zv, *z_ret = &zv; - if (!redis_unserialize(redis_sock, value, value_len, z_ret TSRMLS_CC)) { + if (!redis_unserialize(redis_sock, value, value_len, z_ret)) { // Badly formed input, throw an execption zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0); RETURN_FALSE; diff --git a/redis_commands.h b/redis_commands.h index 71bdb5f7fc..af17d57ab5 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -22,7 +22,7 @@ typedef struct subscribeContext { } subscribeContext; /* Construct a raw command */ -int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC); +int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len); /* Construct a script command */ smart_string *redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args); diff --git a/redis_session.c b/redis_session.c index ed0e187ab5..96445c0f9b 100644 --- a/redis_session.c +++ b/redis_session.c @@ -89,7 +89,7 @@ typedef struct { PHP_REDIS_API void redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, - int database, zend_string *prefix, zend_string *auth TSRMLS_DC) { + int database, zend_string *prefix, zend_string *auth) { redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); rpm->redis_sock = redis_sock; @@ -106,13 +106,13 @@ redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, } PHP_REDIS_API void -redis_pool_free(redis_pool *pool TSRMLS_DC) { +redis_pool_free(redis_pool *pool) { redis_pool_member *rpm, *next; rpm = pool->head; while (rpm) { next = rpm->next; - redis_sock_disconnect(rpm->redis_sock, 0 TSRMLS_CC); + redis_sock_disconnect(rpm->redis_sock, 0); redis_free_socket(rpm->redis_sock); if (rpm->prefix) zend_string_release(rpm->prefix); if (rpm->auth) zend_string_release(rpm->auth); @@ -131,20 +131,20 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) { /* Send a command to Redis. Returns byte count written to socket (-1 on failure) */ static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, - char **reply, int *replylen TSRMLS_DC) + char **reply, int *replylen) { *reply = NULL; - int len_written = redis_sock_write(redis_sock, cmd, cmdlen TSRMLS_CC); + int len_written = redis_sock_write(redis_sock, cmd, cmdlen); if (len_written >= 0) { - *reply = redis_sock_read(redis_sock, replylen TSRMLS_CC); + *reply = redis_sock_read(redis_sock, replylen); } return len_written; } static void -redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { +redis_pool_member_auth(redis_pool_member *rpm) { RedisSock *redis_sock = rpm->redis_sock; char *response, *cmd; int response_len, cmd_len; @@ -155,8 +155,8 @@ redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { } cmd_len = REDIS_SPPRINTF(&cmd, "AUTH", "S", rpm->auth); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { + if (redis_sock_write(redis_sock, cmd, cmd_len) >= 0) { + if ((response = redis_sock_read(redis_sock, &response_len))) { efree(response); } } @@ -164,14 +164,14 @@ redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) { } static void -redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { +redis_pool_member_select(redis_pool_member *rpm) { RedisSock *redis_sock = rpm->redis_sock; char *response, *cmd; int response_len, cmd_len; cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", rpm->database); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) { - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) { + if (redis_sock_write(redis_sock, cmd, cmd_len) >= 0) { + if ((response = redis_sock_read(redis_sock, &response_len))) { efree(response); } } @@ -179,7 +179,7 @@ redis_pool_member_select(redis_pool_member *rpm TSRMLS_DC) { } PHP_REDIS_API redis_pool_member * -redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { +redis_pool_get_sock(redis_pool *pool, const char *key) { unsigned int pos, i; memcpy(&pos, key, sizeof(pos)); @@ -193,12 +193,12 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { if (rpm->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } - if (redis_sock_server_open(rpm->redis_sock TSRMLS_CC) == 0) { + if (redis_sock_server_open(rpm->redis_sock) == 0) { if (needs_auth) { - redis_pool_member_auth(rpm TSRMLS_CC); + redis_pool_member_auth(rpm); } if (rpm->database >= 0) { /* default is -1 which leaves the choice to redis. */ - redis_pool_member_select(rpm TSRMLS_CC); + redis_pool_member_select(rpm); } return rpm; @@ -213,12 +213,12 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) { /* Helper to set our session lock key */ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len - TSRMLS_DC) + ) { char *reply; int sent_len, reply_len; - sent_len = redis_simple_cmd(redis_sock, cmd, cmd_len, &reply, &reply_len TSRMLS_CC); + sent_len = redis_simple_cmd(redis_sock, cmd, cmd_len, &reply, &reply_len); if (reply) { if (IS_REDIS_OK(reply, reply_len)) { efree(reply); @@ -233,7 +233,7 @@ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len } static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status - TSRMLS_DC) + ) { char *cmd, hostname[HOST_NAME_MAX] = {0}, suffix[] = "_LOCK", pid[32]; int cmd_len, lock_wait_time, retries, i, set_lock_key_result, expiry; @@ -286,7 +286,7 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s /* Attempt to get our lock */ for (i = 0; retries == -1 || i <= retries; i++) { - set_lock_key_result = set_session_lock_key(redis_sock, cmd, cmd_len TSRMLS_CC); + set_lock_key_result = set_session_lock_key(redis_sock, cmd, cmd_len); if (set_lock_key_result == SUCCESS) { lock_status->is_locked = 1; @@ -311,7 +311,7 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s } #define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !strncmp(reply, ZSTR_VAL(secret), len)) -static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status) { char *cmd, *reply = NULL; int replylen, cmdlen; @@ -330,7 +330,7 @@ static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status cmdlen = REDIS_SPPRINTF(&cmd, "GET", "S", lock_status->lock_key); /* Attempt to refresh the lock */ - redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen TSRMLS_CC); + redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); if (reply != NULL) { lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret); efree(reply); @@ -342,19 +342,19 @@ static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status * if we aren't flagged as locked, so if we're not flagged here something * failed */ if (!lock_status->is_locked) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to refresh session lock"); + php_error_docref(NULL, E_WARNING, "Failed to refresh session lock"); } /* Cleanup */ efree(cmd); } -static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status) { if (!INI_INT("redis.session.locking_enabled")) return 1; - refresh_lock_status(redis_sock, lock_status TSRMLS_CC); + refresh_lock_status(redis_sock, lock_status); return lock_status->is_locked; } @@ -363,7 +363,7 @@ static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_ * first attempts to use EVALSHA and then falls back to EVAL if EVALSHA fails. This * will cause Redis to cache the script, so subsequent calls should then succeed * using EVALSHA. */ -static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC) +static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status) { char *cmd, *reply; int i, cmdlen, replylen; @@ -380,7 +380,7 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_ lock_status->lock_key, lock_status->lock_secret); /* Send it off */ - redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen TSRMLS_CC); + redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); /* Release lock and cleanup reply if we got one */ if (reply != NULL) { @@ -394,7 +394,7 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_ /* Something has failed if we are still locked */ if (lock_status->is_locked) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to release session lock"); + php_error_docref(NULL, E_WARNING, "Failed to release session lock"); } } @@ -440,11 +440,11 @@ PS_OPEN_FUNC(redis) if (!url) { char *path = estrndup(save_path+i, j-i); - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Failed to parse session.save_path (error at offset %d, url was '%s')", i, path); efree(path); - redis_pool_free(pool TSRMLS_CC); + redis_pool_free(pool); PS_SET_MOD_DATA(NULL); return FAILURE; } @@ -454,9 +454,9 @@ PS_OPEN_FUNC(redis) array_init(¶ms); #if (PHP_VERSION_ID < 70300) - sapi_module.treat_data(PARSE_STRING, estrdup(url->query), ¶ms TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(url->query), ¶ms); #else - sapi_module.treat_data(PARSE_STRING, estrndup(ZSTR_VAL(url->query), ZSTR_LEN(url->query)), ¶ms TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrndup(ZSTR_VAL(url->query), ZSTR_LEN(url->query)), ¶ms); #endif if ((param = zend_hash_str_find(Z_ARRVAL(params), "weight", sizeof("weight") - 1)) != NULL) { @@ -495,7 +495,7 @@ PS_OPEN_FUNC(redis) if (persistent_id) efree(persistent_id); if (prefix) zend_string_release(prefix); if (auth) zend_string_release(auth); - redis_pool_free(pool TSRMLS_CC); + redis_pool_free(pool); PS_SET_MOD_DATA(NULL); return FAILURE; } @@ -514,7 +514,7 @@ PS_OPEN_FUNC(redis) redis_sock = redis_sock_create(ZSTR_VAL(url->path), ZSTR_LEN(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval); #endif } - redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC); + redis_pool_add(pool, redis_sock, weight, database, prefix, auth); php_url_free(url); } @@ -537,15 +537,15 @@ PS_CLOSE_FUNC(redis) if (pool) { if (pool->lock_status.session_key) { - redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(pool->lock_status.session_key) TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(pool->lock_status.session_key)); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (redis_sock) { - lock_release(redis_sock, &pool->lock_status TSRMLS_CC); + lock_release(redis_sock, &pool->lock_status); } } - redis_pool_free(pool TSRMLS_CC); + redis_pool_free(pool); PS_SET_MOD_DATA(NULL); } @@ -582,27 +582,27 @@ PS_CREATE_SID_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); if (!pool) { - return php_session_create_id(NULL TSRMLS_CC); + return php_session_create_id(NULL); } while (retries-- > 0) { - zend_string* sid = php_session_create_id((void **) &pool TSRMLS_CC); - redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid) TSRMLS_CC); + zend_string* sid = php_session_create_id((void **) &pool); + redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid)); RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if (!rpm || !redis_sock) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, + php_error_docref(NULL, E_NOTICE, "Redis not available while creating session_id"); zend_string_release(sid); - return php_session_create_id(NULL TSRMLS_CC); + return php_session_create_id(NULL); } if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); pool->lock_status.session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid)); - if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) == SUCCESS) { + if (lock_acquire(redis_sock, &pool->lock_status) == SUCCESS) { return sid; } @@ -612,7 +612,7 @@ PS_CREATE_SID_FUNC(redis) sid = NULL; } - php_error_docref(NULL TSRMLS_CC, E_NOTICE, + php_error_docref(NULL, E_NOTICE, "Acquiring session lock failed while creating session_id"); return NULL; @@ -632,7 +632,7 @@ PS_VALIDATE_SID_FUNC(redis) if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { return FAILURE; @@ -642,14 +642,14 @@ PS_VALIDATE_SID_FUNC(redis) zend_string *session = redis_session_key(rpm, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "EXISTS", "S", session); zend_string_release(session); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); return FAILURE; } efree(cmd); /* read response */ - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { return FAILURE; } @@ -676,7 +676,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { return FAILURE; @@ -687,14 +687,14 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, INI_INT("session.gc_maxlifetime")); zend_string_release(session); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); return FAILURE; } efree(cmd); /* read response */ - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { return FAILURE; } @@ -720,7 +720,7 @@ PS_READ_FUNC(redis) if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; if (!rpm || !redis_sock){ return FAILURE; @@ -731,12 +731,12 @@ PS_READ_FUNC(redis) pool->lock_status.session_key = redis_session_key(rpm, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); - if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, + if (lock_acquire(redis_sock, &pool->lock_status) != SUCCESS) { + php_error_docref(NULL, E_NOTICE, "Acquire of session lock was not successful"); } - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); return FAILURE; } @@ -745,7 +745,7 @@ PS_READ_FUNC(redis) /* Read response from Redis. If we get a NULL response from redis_sock_read * this can indicate an error, OR a "NULL bulk" reply (empty session data) * in which case we can reply with success. */ - if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL && resp_len != -1) { + if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL && resp_len != -1) { return FAILURE; } @@ -772,7 +772,7 @@ PS_WRITE_FUNC(redis) if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { return FAILURE; @@ -784,14 +784,14 @@ PS_WRITE_FUNC(redis) cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, INI_INT("session.gc_maxlifetime"), sval, svallen); zend_string_release(session); - if (!write_allowed(redis_sock, &pool->lock_status TSRMLS_CC) || redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (!write_allowed(redis_sock, &pool->lock_status) || redis_sock_write(redis_sock, cmd, cmd_len ) < 0) { efree(cmd); return FAILURE; } efree(cmd); /* read response */ - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { return FAILURE; } @@ -815,7 +815,7 @@ PS_DESTROY_FUNC(redis) size_t skeylen = ZSTR_LEN(key); redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC); + redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { return FAILURE; @@ -823,21 +823,21 @@ PS_DESTROY_FUNC(redis) /* Release lock */ if (redis_sock) { - lock_release(redis_sock, &pool->lock_status TSRMLS_CC); + lock_release(redis_sock, &pool->lock_status); } /* send DEL command */ zend_string *session = redis_session_key(rpm, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "S", session); zend_string_release(session); - if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) { + if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); return FAILURE; } efree(cmd); /* read response */ - if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { return FAILURE; } @@ -925,7 +925,7 @@ PS_OPEN_FUNC(rediscluster) { /* Parse configuration for session handler */ array_init(&z_conf); - sapi_module.treat_data(PARSE_STRING, estrdup(save_path), &z_conf TSRMLS_CC); + sapi_module.treat_data(PARSE_STRING, estrdup(save_path), &z_conf); /* Sanity check that we're able to parse and have a seeds array */ if (Z_TYPE(z_conf) != IS_ARRAY || @@ -949,7 +949,7 @@ PS_OPEN_FUNC(rediscluster) { /* Sanity check on our timeouts */ if (timeout < 0 || read_timeout < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL, E_WARNING, "Can't set negative timeout values in session configuration"); zval_dtor(&z_conf); return FAILURE; @@ -989,14 +989,14 @@ PS_OPEN_FUNC(rediscluster) { if (auth && auth_len > 0) { c->auth = zend_string_init(auth, auth_len, 0); } - if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) { + if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c)) { /* Set up our prefix */ c->flags->prefix = zend_string_init(prefix, prefix_len, 0); PS_SET_MOD_DATA(c); retval = SUCCESS; } else { - cluster_free(c, 1 TSRMLS_CC); + cluster_free(c, 1); retval = FAILURE; } @@ -1018,12 +1018,12 @@ PS_READ_FUNC(rediscluster) { /* Set up our command and slot information */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "GET", "s", skey, skeylen); + cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen); efree(skey); /* Attempt to kick off our command */ c->readonly = 1; - if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC) < 0 || c->err) { + if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { efree(cmd); return FAILURE; } @@ -1032,7 +1032,7 @@ PS_READ_FUNC(rediscluster) { efree(cmd); /* Attempt to read reply */ - reply = cluster_read_resp(c, 0 TSRMLS_CC); + reply = cluster_read_resp(c, 0); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; @@ -1065,14 +1065,14 @@ PS_WRITE_FUNC(rediscluster) { /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "SETEX", "sds", skey, + cmdlen = redis_spprintf(NULL, NULL, &cmd, "SETEX", "sds", skey, skeylen, INI_INT("session.gc_maxlifetime"), ZSTR_VAL(val), ZSTR_LEN(val)); efree(skey); /* Attempt to send command */ c->readonly = 0; - if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC) < 0 || c->err) { + if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { efree(cmd); return FAILURE; } @@ -1081,7 +1081,7 @@ PS_WRITE_FUNC(rediscluster) { efree(cmd); /* Attempt to read reply */ - reply = cluster_read_resp(c, 0 TSRMLS_CC); + reply = cluster_read_resp(c, 0); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; @@ -1105,11 +1105,11 @@ PS_DESTROY_FUNC(rediscluster) { /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL TSRMLS_CC, &cmd, "DEL", "s", skey, skeylen); + cmdlen = redis_spprintf(NULL, NULL, &cmd, "DEL", "s", skey, skeylen); efree(skey); /* Attempt to send command */ - if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC) < 0 || c->err) { + if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { efree(cmd); return FAILURE; } @@ -1118,7 +1118,7 @@ PS_DESTROY_FUNC(rediscluster) { efree(cmd); /* Attempt to read reply */ - reply = cluster_read_resp(c, 0 TSRMLS_CC); + reply = cluster_read_resp(c, 0); if (!reply || c->err) { if (reply) cluster_free_reply(reply, 1); return FAILURE; @@ -1136,7 +1136,7 @@ PS_CLOSE_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); if (c) { - cluster_free(c, 1 TSRMLS_CC); + cluster_free(c, 1); PS_SET_MOD_DATA(NULL); } return SUCCESS; From b9828ca59e94dc8db280ed05729d81ded69fddf6 Mon Sep 17 00:00:00 2001 From: 719media Date: Wed, 10 Jul 2019 08:35:53 -0700 Subject: [PATCH 1211/1986] Update Changelog.md --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index f17a85392a..456f12f812 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,7 +3,7 @@ All changes to phpredis will be documented in this file. We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and PhpRedis adhears to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] From 28e84d0094000994bccec4fb7be62ca3132c0b75 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 13 Jul 2019 11:27:48 -0700 Subject: [PATCH 1212/1986] Add BZPOPMIN and BZPOPMAX documentation --- README.markdown | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.markdown b/README.markdown index a22339f82c..c80a161960 100644 --- a/README.markdown +++ b/README.markdown @@ -2637,6 +2637,7 @@ while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) { ## Sorted sets +* [bzPop](#bzpop) - Block until Redis can pop the highest or lowest scoring member from one or more ZSETs. * [zAdd](#zadd) - Add one or more members to a sorted set or update its score if it already exists * [zCard, zSize](#zcard-zsize) - Get the number of members in a sorted set * [zCount](#zcount) - Count the members in a sorted set with scores within the given values @@ -2654,6 +2655,35 @@ while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) { * [zunionstore, zUnion](#zunionstore-zunion) - Add multiple sorted sets and store the resulting sorted set in a new key * [zScan](#zscan) - Scan a sorted set for members +### bzPop +----- +_**Description**_: Block until Redis can pop the highest or lowest scoring members from one or more ZSETs. There are two commands (`BZPOPMIN` and `BZPOPMAX` for popping the lowest and highest scoring elements respectively.) + +##### *Prototype* +~~~php +$redis->bzPopMin(array $keys, int $timeout): array +$redis->bzPopMax(array $keys, int $timeout): array + +$redis->bzPopMin(string $key1, string $key2, ... int $timeout): array +$redis->bzPopMax(string $key1, string $key2, ... int $timeout): array +~~~ + +##### *Return value* +*ARRAY:* Either an array with the key member and score of the higest or lowest element or an empty array if the timeout was reached without an element to pop. + +##### *Example* +~~~php +/* Wait up to 5 seconds to pop the *lowest* scoring member from sets `zs1` and `zs2`. */ +$redis->bzPopMin(['zs1', 'zs2'], 5); +$redis->bzPopMin('zs1', 'zs2', 5); + +/* Wait up to 5 seconds to pop the *highest* scoring member from sets `zs1` and `zs2` */ +$redis->bzPopMax(['zs1', 'zs2'], 5); +$redis->bzPopMax('zs1', 'zs2', 5); +~~~ + +**Note:** Calling these functions with an array of keys or with a variable nubmer of arguments is functionally identical. + ### zAdd ----- _**Description**_: Add one or more members to a sorted set or update its score if it already exists From 148bf373b7a0d276f6f6fd1ec1e6b9407fba6497 Mon Sep 17 00:00:00 2001 From: confused Date: Mon, 15 Jul 2019 16:51:41 +0800 Subject: [PATCH 1213/1986] fix doc word error --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index c80a161960..a9e5893412 100644 --- a/README.markdown +++ b/README.markdown @@ -2117,7 +2117,7 @@ $redis->rPush('key1', 'C'); // returns 3 ### rPushX ----- -_**Description**_: Adds the string value to the tail (right) of the list if the ist exists. `FALSE` in case of Failure. +_**Description**_: Adds the string value to the tail (right) of the list if the list exists. `FALSE` in case of Failure. ##### *Parameters* *key* From cb5d6b9486486c0eb290f57bf7e85437c54bbbd5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 16 Jul 2019 16:59:58 -0700 Subject: [PATCH 1214/1986] Don't double free the cache key Addresses #1591 --- cluster_library.c | 2 +- redis.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 767e95b0ae..fc49e1275a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -892,8 +892,8 @@ cluster_free(redisCluster *c, int free_ctx) if (c->cache_key) { if (c->redirections) { zend_hash_del(&EG(persistent_list), c->cache_key); + c->cache_key = NULL; } - zend_string_release(c->cache_key); } /* Free structure itself */ diff --git a/redis.c b/redis.c index b087ac4427..504fcdaf4d 100644 --- a/redis.c +++ b/redis.c @@ -559,8 +559,10 @@ free_reply_callbacks(RedisSock *redis_sock) /* Passthru for destroying cluster cache */ static void cluster_cache_dtor(zend_resource *rsrc) { - redisCachedCluster *rcc = (redisCachedCluster*)rsrc->ptr; - cluster_cache_free(rcc); + if (rsrc->ptr) { + redisCachedCluster *rcc = (redisCachedCluster*)rsrc->ptr; + cluster_cache_free(rcc); + } } void From d084f334a5521def54e6be76b58ec6604cfd8bd6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 17 Jul 2019 09:38:55 +0300 Subject: [PATCH 1215/1986] Use zend_object_properties_size while creating cluster object --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index 719e5203d2..859e141491 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -306,7 +306,7 @@ zend_object * create_cluster_context(zend_class_entry *class_type) { redisCluster *cluster; // Allocate our actual struct - cluster = ecalloc(1, sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); + cluster = ecalloc(1, sizeof(redisCluster) + zend_object_properties_size(class_type)); // We're not currently subscribed anywhere cluster->subscribed_slot = -1; From 1534b41f07a5db1e1c089306451a61101609c467 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jul 2019 05:22:55 -0700 Subject: [PATCH 1216/1986] Fix object destructor --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 504fcdaf4d..9889ff90c0 100644 --- a/redis.c +++ b/redis.c @@ -561,7 +561,7 @@ free_reply_callbacks(RedisSock *redis_sock) static void cluster_cache_dtor(zend_resource *rsrc) { if (rsrc->ptr) { redisCachedCluster *rcc = (redisCachedCluster*)rsrc->ptr; - cluster_cache_free(rcc); + cluster_cache_free(rcc->ptr); } } From e5ead9fc7d765560706f5be8344016da25fa144b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jul 2019 05:35:21 -0700 Subject: [PATCH 1217/1986] Revert "Fix object destructor" This reverts commit 1534b41f07a5db1e1c089306451a61101609c467. --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 9889ff90c0..504fcdaf4d 100644 --- a/redis.c +++ b/redis.c @@ -561,7 +561,7 @@ free_reply_callbacks(RedisSock *redis_sock) static void cluster_cache_dtor(zend_resource *rsrc) { if (rsrc->ptr) { redisCachedCluster *rcc = (redisCachedCluster*)rsrc->ptr; - cluster_cache_free(rcc->ptr); + cluster_cache_free(rcc); } } From c19e6521b5feaefdddacbabfccb71625fb2a1c9d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 17 Jul 2019 09:38:55 +0300 Subject: [PATCH 1218/1986] Use zend_object_properties_size while creating cluster object --- redis_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_cluster.c b/redis_cluster.c index 719e5203d2..859e141491 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -306,7 +306,7 @@ zend_object * create_cluster_context(zend_class_entry *class_type) { redisCluster *cluster; // Allocate our actual struct - cluster = ecalloc(1, sizeof(redisCluster) + sizeof(zval) * (class_type->default_properties_count - 1)); + cluster = ecalloc(1, sizeof(redisCluster) + zend_object_properties_size(class_type)); // We're not currently subscribed anywhere cluster->subscribed_slot = -1; From 1f41da64fec3f600c4c1da17e0416ca70d139a06 Mon Sep 17 00:00:00 2001 From: Owen Smith Date: Wed, 17 Jul 2019 20:04:05 +0100 Subject: [PATCH 1219/1986] Fix "No such file or directory" when connecting to ports >= 32768 (#1602) * Fix `connect` for port numbers >=32768: Since 5.0.0, using a high enough port number gives a "No such file or directory" error, because type casts treat high ports as -ve which awkwardly triggers the unix-socket-path behaviour. * clean up guard condition --- common.h | 2 +- redis.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common.h b/common.h index 45e8768fdc..0cb751737d 100644 --- a/common.h +++ b/common.h @@ -248,7 +248,7 @@ typedef struct fold_item { typedef struct { php_stream *stream; zend_string *host; - short port; + unsigned short port; zend_string *auth; double timeout; double read_timeout; diff --git a/redis.c b/redis.c index 504fcdaf4d..6700cdfed2 100644 --- a/redis.c +++ b/redis.c @@ -1005,6 +1005,10 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) port = 6379; } + if (port < 0) { + port = 0; + } + redis = PHPREDIS_GET_OBJECT(redis_object, object); /* if there is a redis sock already we have to remove it */ if (redis->sock) { From dfbc9c8cb76d63aaca5f58f21790abc0aae5787a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jul 2019 12:10:03 -0700 Subject: [PATCH 1220/1986] Update changelog --- Changelog.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 456f12f812..3578b64177 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,8 +15,11 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed -- RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/pull/1592/commits/f52cd237) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix regression for conntecting to ports > 32767 [1f41da64](https://github.com/phpredis/phpredis/pull/1592/commits/1f41da64) + ([Owen Smith](https://github.com/orls) +- RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/pull/1592/commits/f52cd237), + [cb5d6b94](https://github.com/phpredis/phpredis/pull/1592/commits/f52cd237) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), ([Michael Grunder](https://github.com/michael-grunder)) --- From 36a35ed8cfc7a1b8db3d468dd9f58e736a1999d5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jul 2019 19:58:54 -0700 Subject: [PATCH 1221/1986] Remove redundant cast --- redis.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/redis.c b/redis.c index 6700cdfed2..522069094b 100644 --- a/redis.c +++ b/redis.c @@ -560,8 +560,7 @@ free_reply_callbacks(RedisSock *redis_sock) /* Passthru for destroying cluster cache */ static void cluster_cache_dtor(zend_resource *rsrc) { if (rsrc->ptr) { - redisCachedCluster *rcc = (redisCachedCluster*)rsrc->ptr; - cluster_cache_free(rcc); + cluster_cache_free(rsrc->ptr); } } From 7d47331bdbfebf4bb75b01990fb218ae0388e45b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 17 Jul 2019 22:00:27 -0700 Subject: [PATCH 1222/1986] First attempt at adding valgrind to Travis --- .travis.yml | 4 ++++ tests/RedisClusterTest.php | 42 ++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 828416ea37..89241389e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ addons: packages: - clang - libzstd1-dev + - valgrind before_install: - phpize - CFGARGS="--enable-redis-lzf --enable-redis-zstd" @@ -52,3 +53,6 @@ script: - php tests/TestRedis.php --class Redis - php tests/TestRedis.php --class RedisArray - php tests/TestRedis.php --class RedisCluster + - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis + - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray + - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 9806ed5432..eb9e427891 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -12,7 +12,7 @@ class Redis_Cluster_Test extends Redis_Test { private $_arr_redis_types = [ Redis::REDIS_STRING, Redis::REDIS_SET, - Redis::REDIS_LIST, + Redis::REDIS_LIST, Redis::REDIS_ZSET, Redis::REDIS_HASH ]; @@ -33,7 +33,7 @@ class Redis_Cluster_Test extends Redis_Test { */ protected $sessionSaveHandler = 'rediscluster'; - /* Tests we'll skip all together in the context of RedisCluster. The + /* Tests we'll skip all together in the context of RedisCluster. The * RedisCluster class doesn't implement specialized (non-redis) commands * such as sortAsc, or sortDesc and other commands such as SELECT are * simply invalid in Redis Cluster */ @@ -274,7 +274,7 @@ public function testMSetNX() { $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']); $this->assertTrue(is_array($ret)); $this->assertEquals(array_sum($ret),1); - + $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE } @@ -390,7 +390,7 @@ public function testEvalSHA() { $this->assertTrue(1 === $this->redis->eval($scr,[$str_key], 1)); $this->assertTrue(1 === $this->redis->evalsha($sha,[$str_key], 1)); } - + public function testEvalBulkResponse() { $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; @@ -458,7 +458,7 @@ public function testEvalBulkEmptyResponseMulti() { public function testIntrospection() { $arr_masters = $this->redis->_masters(); $this->assertTrue(is_array($arr_masters)); - + foreach ($arr_masters as $arr_info) { $this->assertTrue(is_array($arr_info)); $this->assertTrue(is_string($arr_info[0])); @@ -467,10 +467,10 @@ public function testIntrospection() { } protected function genKeyName($i_key_idx, $i_type) { - switch ($i_type) { - case Redis::REDIS_STRING: + switch ($i_type) { + case Redis::REDIS_STRING: return "string-$i_key_idx"; - case Redis::REDIS_SET: + case Redis::REDIS_SET: return "set-$i_key_idx"; case Redis::REDIS_LIST: return "list-$i_key_idx"; @@ -491,7 +491,7 @@ protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) { switch ($i_type) { case Redis::REDIS_STRING: $value = "$str_key-value"; - $this->redis->set($str_key, $value); + $this->redis->set($str_key, $value); break; case Redis::REDIS_SET: $value = [ @@ -509,7 +509,7 @@ protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) { $str_key . '-mem3' => $str_key . '-val3' ]; $this->redis->hmset($str_key, $value); - break; + break; case Redis::REDIS_LIST: $value = [ $str_key . '-ele1', $str_key . '-ele2', $str_key . '-ele3', @@ -528,7 +528,7 @@ protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) { foreach ($value as $str_mem => $i_score) { $this->redis->zadd($str_key, $i_score, $str_mem); } - break; + break; } /* Update our reference array so we can verify values */ @@ -585,7 +585,7 @@ public function testFailOver() { for ($i = 0; $i < 200; $i++) { foreach ($this->_arr_redis_types as $i_type) { $str_key = $this->setKeyVals($i, $i_type, $arr_value_ref); - $arr_type_ref[$str_key] = $i_type; + $arr_type_ref[$str_key] = $i_type; } } @@ -598,7 +598,7 @@ public function testFailOver() { } break; - } + } } /* Test a 'raw' command */ @@ -649,6 +649,22 @@ public function testSession() $this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id())); } + + /* Test that we are able to use the slot cache without issues */ + public function testSlotCache() { + ini_set('redis.clusters.cache_slots', 1); + + $pong = 0; + for ($i = 0; $i < 10; $i++) { + $obj_rc = new RedisCluster(NULL, self::$_arr_node_map); + $pong += $obj_rc->ping("key:$i"); + } + + $this->assertEquals($pong, $i); + + ini_set('redis.clusters.cache_slots', 0); + } + /** * @inheritdoc */ From b4eb158a00ab30c810d7ef6069547a02cad3359b Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 18 Jul 2019 10:36:52 +0200 Subject: [PATCH 1223/1986] ulong => zend_ulong for 7.4 --- cluster_library.c | 4 ++-- library.c | 2 +- library.h | 2 +- redis_array.c | 2 +- redis_array_impl.c | 2 +- redis_commands.c | 14 +++++++------- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index fc49e1275a..7d1e99a205 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -176,7 +176,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements, /* Return the socket for a slot and slave index */ static RedisSock *cluster_slot_sock(redisCluster *c, unsigned short slot, - ulong slaveidx) + zend_ulong slaveidx) { redisClusterNode *node; @@ -669,7 +669,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, PHP_REDIS_API int cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave) { - ulong index; + zend_ulong index; // Allocate our slaves hash table if we haven't yet if (!master->slaves) { diff --git a/library.c b/library.c index 5b1a238e0a..6bf42a9408 100644 --- a/library.c +++ b/library.c @@ -754,7 +754,7 @@ int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSoc /* Append an array key to a redis smart string command. This function * handles the boilerplate conditionals around string or integer keys */ -int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, ulong idx) +int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx) { char *arg, kbuf[128]; int len; diff --git a/library.h b/library.h index 8d844747bd..11da7a4fc1 100644 --- a/library.h +++ b/library.h @@ -28,7 +28,7 @@ int redis_cmd_append_sstr_i64(smart_string *str, int64_t append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock); int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot); -int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, ulong idx); +int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx); PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...); diff --git a/redis_array.c b/redis_array.c index 556d7f063d..d64f6b0504 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1033,7 +1033,7 @@ PHP_METHOD(RedisArray, mset) char *key, kbuf[40]; int key_len; zend_string **keys, *zkey; - ulong idx; + zend_ulong idx; if ((ra = redis_array_get(getThis())) == NULL) { RETURN_FALSE; diff --git a/redis_array_impl.c b/redis_array_impl.c index 54ae50023d..b78cca2217 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -907,7 +907,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { int i, count; HashTable *h_zset_vals; zend_string *zkey; - ulong idx; + zend_ulong idx; /* run ZRANGE key 0 -1 WITHSCORES on source */ ZVAL_STRINGL(&z_fun_zrange, "ZRANGE", 6); diff --git a/redis_commands.c b/redis_commands.c index 9598ebe989..811cc13b9c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -572,7 +572,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, size_t key_len, start_len, end_len; zval *z_opt=NULL, *z_ele; zend_string *zkey; - ulong idx; + zend_ulong idx; HashTable *ht_opt; PHPREDIS_NOTUSED(idx); @@ -1329,7 +1329,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { HashTable *kt = Z_ARRVAL_P(z_opts); zend_string *zkey; - ulong idx; + zend_ulong idx; zval *v; PHPREDIS_NOTUSED(idx); @@ -1632,7 +1632,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; int key_free, count; size_t key_len; - ulong idx; + zend_ulong idx; zval *z_arr; HashTable *ht_vals; smart_string cmdstr = {0}; @@ -2723,7 +2723,7 @@ geoStoreType get_georadius_store_type(zend_string *key) { /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */ static int get_georadius_opts(HashTable *ht, geoOptions *opts) { - ulong idx; + zend_ulong idx; char *optstr; zend_string *zkey; zval *optval; @@ -3214,7 +3214,7 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_fields, *value; zend_long maxlen = 0; zend_bool approx = 0; - ulong idx; + zend_ulong idx; HashTable *ht_fields; int fcount, argc; char *key, *id; @@ -3356,7 +3356,7 @@ append_stream_args(smart_string *cmdstr, HashTable *ht, RedisSock *redis_sock, zend_string *key, *idstr; short oldslot; zval **id; - ulong idx; + zend_ulong idx; /* Append STREAM qualifier */ REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "STREAMS"); @@ -3619,7 +3619,7 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) { zend_string *zkey; char *kval; size_t klen; - ulong idx; + zend_ulong idx; zval *zv; PHPREDIS_NOTUSED(idx); From d6fc5c7348da642a2e076d59af62191d24e743b9 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 18 Jul 2019 10:41:41 +0200 Subject: [PATCH 1224/1986] use uint32_t for call_user_function --- redis_array.c | 2 +- redis_array.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_array.c b/redis_array.c index d64f6b0504..ef3c6a89a1 100644 --- a/redis_array.c +++ b/redis_array.c @@ -223,7 +223,7 @@ redis_array_get(zval *id) } PHP_REDIS_API int -ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[]) +ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]) { if (object) { redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object); diff --git a/redis_array.h b/redis_array.h index 0a75197e6e..5cebc60d74 100644 --- a/redis_array.h +++ b/redis_array.h @@ -69,6 +69,6 @@ typedef struct RedisArray_ { zend_object *create_redis_array_object(zend_class_entry *ce); void free_redis_array_object(zend_object *object); -PHP_REDIS_API int ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint param_count, zval params[]); +PHP_REDIS_API int ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]); #endif From 978c307456937ad7b376f4cf90b2758d841900fc Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 23 Jul 2019 22:16:56 +0300 Subject: [PATCH 1225/1986] Refactor redis_session Use `redis_sock` to store `auth` and `prefix` for session. Use `redis_sock_auth` insted of `redis_pool_member_auth`. --- redis_session.c | 72 +++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/redis_session.c b/redis_session.c index 96445c0f9b..6378dca7a3 100644 --- a/redis_session.c +++ b/redis_session.c @@ -71,8 +71,6 @@ typedef struct redis_pool_member_ { RedisSock *redis_sock; int weight; int database; - zend_string *prefix; - zend_string *auth; struct redis_pool_member_ *next; } redis_pool_member; @@ -88,17 +86,13 @@ typedef struct { } redis_pool; PHP_REDIS_API void -redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, - int database, zend_string *prefix, zend_string *auth) { - +redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, int database) +{ redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); rpm->redis_sock = redis_sock; rpm->weight = weight; rpm->database = database; - rpm->prefix = prefix; - rpm->auth = auth; - rpm->next = pool->head; pool->head = rpm; @@ -114,8 +108,6 @@ redis_pool_free(redis_pool *pool) { next = rpm->next; redis_sock_disconnect(rpm->redis_sock, 0); redis_free_socket(rpm->redis_sock); - if (rpm->prefix) zend_string_release(rpm->prefix); - if (rpm->auth) zend_string_release(rpm->auth); efree(rpm); rpm = next; } @@ -143,26 +135,6 @@ static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, return len_written; } -static void -redis_pool_member_auth(redis_pool_member *rpm) { - RedisSock *redis_sock = rpm->redis_sock; - char *response, *cmd; - int response_len, cmd_len; - - /* Short circuit if we don't have a password */ - if (!rpm->auth) { - return; - } - - cmd_len = REDIS_SPPRINTF(&cmd, "AUTH", "S", rpm->auth); - if (redis_sock_write(redis_sock, cmd, cmd_len) >= 0) { - if ((response = redis_sock_read(redis_sock, &response_len))) { - efree(response); - } - } - efree(cmd); -} - static void redis_pool_member_select(redis_pool_member *rpm) { RedisSock *redis_sock = rpm->redis_sock; @@ -190,12 +162,12 @@ redis_pool_get_sock(redis_pool *pool, const char *key) { for(i = 0; i < pool->totalWeight;) { if (pos >= i && pos < i + rpm->weight) { int needs_auth = 0; - if (rpm->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { + if (rpm->redis_sock->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) { needs_auth = 1; } if (redis_sock_server_open(rpm->redis_sock) == 0) { if (needs_auth) { - redis_pool_member_auth(rpm); + redis_sock_auth(rpm->redis_sock); } if (rpm->database >= 0) { /* default is -1 which leaves the choice to redis. */ redis_pool_member_select(rpm); @@ -514,7 +486,9 @@ PS_OPEN_FUNC(redis) redis_sock = redis_sock_create(ZSTR_VAL(url->path), ZSTR_LEN(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval); #endif } - redis_pool_add(pool, redis_sock, weight, database, prefix, auth); + redis_pool_add(pool, redis_sock, weight, database); + redis_sock->prefix = prefix; + redis_sock->auth = auth; php_url_free(url); } @@ -554,16 +528,16 @@ PS_CLOSE_FUNC(redis) /* }}} */ static zend_string * -redis_session_key(redis_pool_member *rpm, const char *key, int key_len) +redis_session_key(RedisSock *redis_sock, const char *key, int key_len) { zend_string *session; char default_prefix[] = "PHPREDIS_SESSION:"; char *prefix = default_prefix; size_t prefix_len = sizeof(default_prefix)-1; - if (rpm->prefix) { - prefix = ZSTR_VAL(rpm->prefix); - prefix_len = ZSTR_LEN(rpm->prefix); + if (redis_sock->prefix) { + prefix = ZSTR_VAL(redis_sock->prefix); + prefix_len = ZSTR_LEN(redis_sock->prefix); } /* build session key */ @@ -589,9 +563,9 @@ PS_CREATE_SID_FUNC(redis) zend_string* sid = php_session_create_id((void **) &pool); redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid)); - RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; + RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; - if (!rpm || !redis_sock) { + if (!redis_sock) { php_error_docref(NULL, E_NOTICE, "Redis not available while creating session_id"); @@ -600,7 +574,7 @@ PS_CREATE_SID_FUNC(redis) } if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); - pool->lock_status.session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid)); + pool->lock_status.session_key = redis_session_key(redis_sock, ZSTR_VAL(sid), ZSTR_LEN(sid)); if (lock_acquire(redis_sock, &pool->lock_status) == SUCCESS) { return sid; @@ -639,7 +613,7 @@ PS_VALIDATE_SID_FUNC(redis) } /* send EXISTS command */ - zend_string *session = redis_session_key(rpm, skey, skeylen); + zend_string *session = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "EXISTS", "S", session); zend_string_release(session); if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { @@ -683,7 +657,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) } /* send EXPIRE command */ - zend_string *session = redis_session_key(rpm, skey, skeylen); + zend_string *session = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, INI_INT("session.gc_maxlifetime")); zend_string_release(session); @@ -721,14 +695,14 @@ PS_READ_FUNC(redis) redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey); - RedisSock *redis_sock = rpm?rpm->redis_sock:NULL; - if (!rpm || !redis_sock){ + RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; + if (!redis_sock) { return FAILURE; } /* send GET command */ if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); - pool->lock_status.session_key = redis_session_key(rpm, skey, skeylen); + pool->lock_status.session_key = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); if (lock_acquire(redis_sock, &pool->lock_status) != SUCCESS) { @@ -779,7 +753,7 @@ PS_WRITE_FUNC(redis) } /* send SET command */ - zend_string *session = redis_session_key(rpm, skey, skeylen); + zend_string *session = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, INI_INT("session.gc_maxlifetime"), sval, svallen); zend_string_release(session); @@ -822,12 +796,10 @@ PS_DESTROY_FUNC(redis) } /* Release lock */ - if (redis_sock) { - lock_release(redis_sock, &pool->lock_status); - } + lock_release(redis_sock, &pool->lock_status); /* send DEL command */ - zend_string *session = redis_session_key(rpm, skey, skeylen); + zend_string *session = redis_session_key(redis_sock, skey, skeylen); cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "S", session); zend_string_release(session); if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { From 7d4470a735fe64ea84ec9022e53151b88b2a7cc9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 24 Jul 2019 21:44:37 +0300 Subject: [PATCH 1226/1986] Refactor redis_session Remove `refresh_lock_status` helper function --- redis_session.c | 65 ++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/redis_session.c b/redis_session.c index 6378dca7a3..c18d07f843 100644 --- a/redis_session.c +++ b/redis_session.c @@ -283,50 +283,39 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s } #define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !strncmp(reply, ZSTR_VAL(secret), len)) -static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status) +static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status) { - char *cmd, *reply = NULL; - int replylen, cmdlen; - - /* Return early if we're not locked */ - if (!lock_status->is_locked) - return; - - /* If redis.session.lock_expire is not set => TTL=max_execution_time - Therefore it is guaranteed that the current process is still holding - the lock */ - if (lock_status->is_locked && INI_INT("redis.session.lock_expire") == 0) - return; - - /* Command to get our lock key value and compare secrets */ - cmdlen = REDIS_SPPRINTF(&cmd, "GET", "S", lock_status->lock_key); - - /* Attempt to refresh the lock */ - redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); - if (reply != NULL) { - lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret); - efree(reply); - } else { - lock_status->is_locked = 0; + if (!INI_INT("redis.session.locking_enabled")) { + return 1; } + /* If locked and redis.session.lock_expire is not set => TTL=max_execution_time + Therefore it is guaranteed that the current process is still holding the lock */ - /* Issue a warning if we're not locked. We don't attempt to refresh the lock - * if we aren't flagged as locked, so if we're not flagged here something - * failed */ - if (!lock_status->is_locked) { - php_error_docref(NULL, E_WARNING, "Failed to refresh session lock"); - } + if (lock_status->is_locked && INI_INT("redis.session.lock_expire") != 0) { + char *cmd, *reply = NULL; + int replylen, cmdlen; + /* Command to get our lock key value and compare secrets */ + cmdlen = REDIS_SPPRINTF(&cmd, "GET", "S", lock_status->lock_key); - /* Cleanup */ - efree(cmd); -} + /* Attempt to refresh the lock */ + redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); + /* Cleanup */ + efree(cmd); -static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status) -{ - if (!INI_INT("redis.session.locking_enabled")) - return 1; + if (reply == NULL) { + lock_status->is_locked = 0; + } else { + lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret); + efree(reply); + } - refresh_lock_status(redis_sock, lock_status); + /* Issue a warning if we're not locked. We don't attempt to refresh the lock + * if we aren't flagged as locked, so if we're not flagged here something + * failed */ + if (!lock_status->is_locked) { + php_error_docref(NULL, E_WARNING, "Failed to refresh session lock"); + } + } return lock_status->is_locked; } From 70714383901eb917cce955894a1e52ba6c26c0d0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 24 Jul 2019 22:21:14 +0300 Subject: [PATCH 1227/1986] TravisCI: remove redis-trib dependency --- .travis.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 89241389e8..9668f236ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,14 +40,10 @@ before_install: - ./configure $CFGARGS install: make install before_script: - - gem install redis - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap - for PORT in $(seq 6379 6382); do redis-server --port $PORT --daemonize yes; done - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done - - wget https://raw.githubusercontent.com/antirez/redis/1673a3f32ce22498bcb60d73ee254e61e323dda5/src/redis-trib.rb - # Upstream suggests: redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 - # but --cluster is an unknown option for travis trusty - - echo yes | ruby redis-trib.rb create --replicas 3 $(seq -f 127.0.0.1:%g 7000 7011) + - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini script: - php tests/TestRedis.php --class Redis From 91a8e73441e684dfacb2147d6b0346b7b4b09ec2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 27 Jul 2019 21:03:23 +0300 Subject: [PATCH 1228/1986] Refactor redis_session Use strpprintf instead of zend_string_alloc + memcpy. --- redis_session.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/redis_session.c b/redis_session.c index c18d07f843..0bffc0d60a 100644 --- a/redis_session.c +++ b/redis_session.c @@ -207,7 +207,7 @@ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status ) { - char *cmd, hostname[HOST_NAME_MAX] = {0}, suffix[] = "_LOCK", pid[32]; + char *cmd, hostname[HOST_NAME_MAX] = {0}, suffix[] = "_LOCK"; int cmd_len, lock_wait_time, retries, i, set_lock_key_result, expiry; /* Short circuit if we are already locked or not using session locks */ @@ -240,12 +240,8 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s /* Calculate lock secret */ gethostname(hostname, HOST_NAME_MAX); - size_t hostname_len = strlen(hostname); - size_t pid_len = snprintf(pid, sizeof(pid), "|%ld", (long)getpid()); if (lock_status->lock_secret) zend_string_release(lock_status->lock_secret); - lock_status->lock_secret = zend_string_alloc(hostname_len + pid_len, 0); - memcpy(ZSTR_VAL(lock_status->lock_secret), hostname, hostname_len); - memcpy(ZSTR_VAL(lock_status->lock_secret) + hostname_len, pid, pid_len); + lock_status->lock_secret = strpprintf(0, "%s|%ld", hostname, (long)getpid()); if (expiry > 0) { cmd_len = REDIS_SPPRINTF(&cmd, "SET", "SSssd", lock_status->lock_key, From 4a4e44f735f1807a7bd73531396602c0ed1da009 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 29 Jul 2019 17:08:22 +0300 Subject: [PATCH 1229/1986] Update Changelog.md --- Changelog.md | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3578b64177..effb1b07b4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,13 +13,35 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm This requires libzstd version >= 1.3.0 [2abc61da](https://github.com/phpredis/phpredis/pull/1582/commits/2abc61da) ([Remi Collet](https://github.com/remicollet)) +### Changed + +- Cleanup TSRMLS_* usage + [94380227](https://github.com/phpredis/phpredis/commit/94380227) + ([Remi Collet](https://github.com/remicollet)) +- Replace ulong with zend_ulong + [b4eb158a](https://github.com/phpredis/phpredis/commit/b4eb158a) + ([Remi Collet](https://github.com/remicollet)) +- Replace uint with uint32_t + [d6fc5c73](https://github.com/phpredis/phpredis/commit/d6fc5c73) + ([Remi Collet](https://github.com/remicollet)) + ### Fixed - Fix regression for conntecting to ports > 32767 [1f41da64](https://github.com/phpredis/phpredis/pull/1592/commits/1f41da64) - ([Owen Smith](https://github.com/orls) + ([Owen Smith](https://github.com/orls)) - RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/pull/1592/commits/f52cd237), - [cb5d6b94](https://github.com/phpredis/phpredis/pull/1592/commits/f52cd237) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), ([Michael Grunder](https://github.com/michael-grunder)) + [cb5d6b94](https://github.com/phpredis/phpredis/pull/1592/commits/cb5d6b94) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) + +--- + +## [5.0.1] - 2019-07-12 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.0.1), [PECL](https://pecl.php.net/package/redis/5.0.1)) + +### Fixed + +- RedisCluster segfaults after second connection with cache_slots enabled + [327cf0bd](https://github.com/phpredis/phpredis/commit/327cf0bd) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) --- From e4f3b9ebc0ae2b87a774fe1c51769778b9b0b1e1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 29 Jul 2019 17:44:28 +0300 Subject: [PATCH 1230/1986] Update Changelog.md Fix commit links --- Changelog.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index effb1b07b4..35f1dc4439 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,8 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Add optional support for Zstd compression, using `--enable-redis-ztsd`. - This requires libzstd version >= 1.3.0 [2abc61da](https://github.com/phpredis/phpredis/pull/1582/commits/2abc61da) + This requires libzstd version >= 1.3.0 + [2abc61da](https://github.com/phpredis/phpredis/commit/2abc61da) ([Remi Collet](https://github.com/remicollet)) ### Changed @@ -27,10 +28,12 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed -- Fix regression for conntecting to ports > 32767 [1f41da64](https://github.com/phpredis/phpredis/pull/1592/commits/1f41da64) +- Fix regression for conntecting to ports > 32767 + [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64) ([Owen Smith](https://github.com/orls)) -- RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/pull/1592/commits/f52cd237), - [cb5d6b94](https://github.com/phpredis/phpredis/pull/1592/commits/cb5d6b94) +- RedisCluster segfaults after second connection with cache_slots enabled + [f52cd237](https://github.com/phpredis/phpredis/commit/f52cd237), + [cb5d6b94](https://github.com/phpredis/phpredis/commit/cb5d6b94) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) --- From b565c84f13bd757de50c3da720a6822333d6f471 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 29 Jul 2019 18:32:46 +0300 Subject: [PATCH 1231/1986] Update Changelog.md --- Changelog.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 35f1dc4439..3d25aef948 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,17 +14,9 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm [2abc61da](https://github.com/phpredis/phpredis/commit/2abc61da) ([Remi Collet](https://github.com/remicollet)) -### Changed +--- -- Cleanup TSRMLS_* usage - [94380227](https://github.com/phpredis/phpredis/commit/94380227) - ([Remi Collet](https://github.com/remicollet)) -- Replace ulong with zend_ulong - [b4eb158a](https://github.com/phpredis/phpredis/commit/b4eb158a) - ([Remi Collet](https://github.com/remicollet)) -- Replace uint with uint32_t - [d6fc5c73](https://github.com/phpredis/phpredis/commit/d6fc5c73) - ([Remi Collet](https://github.com/remicollet)) +## [5.0.2] - 2019-07-29 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.0.2), [PECL](https://pecl.php.net/package/redis/5.0.2)) ### Fixed @@ -36,6 +28,18 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm [cb5d6b94](https://github.com/phpredis/phpredis/commit/cb5d6b94) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) +### Changed + +- Cleanup TSRMLS_* usage + [94380227](https://github.com/phpredis/phpredis/commit/94380227) + ([Remi Collet](https://github.com/remicollet)) +- Replace ulong with zend_ulong + [b4eb158a](https://github.com/phpredis/phpredis/commit/b4eb158a) + ([Remi Collet](https://github.com/remicollet)) +- Replace uint with uint32_t + [d6fc5c73](https://github.com/phpredis/phpredis/commit/d6fc5c73) + ([Remi Collet](https://github.com/remicollet)) + --- ## [5.0.1] - 2019-07-12 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.0.1), [PECL](https://pecl.php.net/package/redis/5.0.1)) From cf93649ec14d52115082d97436ec9a66e816fdd0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 30 Jul 2019 21:11:06 -0700 Subject: [PATCH 1232/1986] Fix overallocation in directed cluster MULTIBULK handling. Addresses #1611 --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 7d1e99a205..64166cc656 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -239,7 +239,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type, case TYPE_MULTIBULK: r->elements = len; if (len != (size_t)-1) { - r->element = ecalloc(len, sizeof(clusterReply*)*len); + r->element = ecalloc(len, sizeof(clusterReply*)); cluster_multibulk_resp_recursive(redis_sock, len, r->element, line_reply != NULL, &err); } From d310ed7c8d868c31309803328f9ea54fd1a313b0 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 31 Jul 2019 13:46:38 -0700 Subject: [PATCH 1233/1986] Add RedisCluster overallocation fix to changelog --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3d25aef948..72289133b2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,8 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm This requires libzstd version >= 1.3.0 [2abc61da](https://github.com/phpredis/phpredis/commit/2abc61da) ([Remi Collet](https://github.com/remicollet)) +- Fix overallocation in RedisCluster directed node commands [cf93649](https://github.com/phpredis/phpredis/commit/cf93649) + (([Michael Grunder](https://github.com/michael-grunder)) --- From d9a6366c14eded9141ea79a74173502fd7531981 Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Thu, 8 Aug 2019 17:06:28 +0200 Subject: [PATCH 1234/1986] Fix wording: 'latest' should be 'last' "Latest" means most recent, whereas "last" means that nothing will follow. --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 72289133b2..82a888e72b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -134,7 +134,7 @@ serializers, soft deprecation of non-Redis commands. ## [4.3.0] - 2019-03-13 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/4.3.0), [PECL](https://pecl.php.net/package/redis/4.3.0)) -This is probably the latest release with PHP 5 suport!!! +This is probably the last release with PHP 5 suport!!! ### Added From b162262ea93075f95e6988dc7ed50c3644ef998d Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Thu, 8 Aug 2019 17:08:26 +0200 Subject: [PATCH 1235/1986] Fix wording: 'latest' should be 'last' "Latest" means most recent, whereas "last" means that nothing will follow. --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index f1f72130da..b2668e6fd6 100644 --- a/package.xml +++ b/package.xml @@ -193,7 +193,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> phpredis 4.3.0 - This is probably the latest release with PHP 5 suport!!! + This is probably the last release with PHP 5 suport!!! * Proper persistent connections pooling implementation [a3703820, c76e00fb, 0433dc03, c75b3b93] (Pavlo Yatsukhnenko) * RedisArray auth [b5549cff, 339cfa2b, 6b411aa8] (Pavlo Yatsukhnenko) From 458a3d8c0c02eba34169546d617e8fb42293f34b Mon Sep 17 00:00:00 2001 From: ljack-adista <46602882+ljack-adista@users.noreply.github.com> Date: Wed, 7 Aug 2019 16:40:32 +0200 Subject: [PATCH 1236/1986] Fix cluster.markdown --- cluster.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster.markdown b/cluster.markdown index 2c6c1db464..156570d2c3 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -182,6 +182,6 @@ The save path for cluster based session storage takes the form of a PHP GET requ * _persistent_: Tells phpredis whether persistent connections should be used. * _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing). * _failover (string)_: How phpredis should distribute session reads between master and slave nodes. + * _none_ : phpredis will only communicate with master nodes + * _error_: phpredis will communicate with master nodes unless one failes, in which case an attempt will be made to read session information from a slave. * _auth (string, empty by default)_: The password used to authenticate with the server prior to sending commands. -* * _none_ : phpredis will only communicate with master nodes -* * _error_: phpredis will communicate with master nodes unless one failes, in which case an attempt will be made to read session information from a slave. From fbe0f804bc687ba465ca1c0fd474875e3f6768b5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 9 Aug 2019 15:10:05 +0300 Subject: [PATCH 1237/1986] Issue #1618 This commit fixes regression added in 112c77e3 --- redis_array.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_array.c b/redis_array.c index ef3c6a89a1..6876914425 100644 --- a/redis_array.c +++ b/redis_array.c @@ -653,7 +653,7 @@ PHP_METHOD(RedisArray, _continuum) static void multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int argc, zval *argv) { - zval z_arg, z_tmp; + zval z_tmp; int i; /* Init our array return */ @@ -665,7 +665,7 @@ multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int a ra_call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_tmp, argc, argv); /* Add the result for this host */ - add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), &z_arg); + add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), &z_tmp); } } From 17b139d8417f2d5f8bf992425c7cb04cf7400818 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 9 Aug 2019 15:55:02 +0300 Subject: [PATCH 1238/1986] Revert "Fix "No such file or directory" when connecting to ports >= 32768 (#1602)" This reverts commit 1f41da64fec3f600c4c1da17e0416ca70d139a06. --- common.h | 2 +- redis.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/common.h b/common.h index 0cb751737d..45e8768fdc 100644 --- a/common.h +++ b/common.h @@ -248,7 +248,7 @@ typedef struct fold_item { typedef struct { php_stream *stream; zend_string *host; - unsigned short port; + short port; zend_string *auth; double timeout; double read_timeout; diff --git a/redis.c b/redis.c index 522069094b..4a13a7d431 100644 --- a/redis.c +++ b/redis.c @@ -1004,10 +1004,6 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) port = 6379; } - if (port < 0) { - port = 0; - } - redis = PHPREDIS_GET_OBJECT(redis_object, object); /* if there is a redis sock already we have to remove it */ if (redis->sock) { From 7ef17ce1a3e6d417138b6f51bb124187f9dc4d47 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 9 Aug 2019 16:05:36 +0300 Subject: [PATCH 1239/1986] Issue #1602 Fix 1f41da64 was reverted because it broke unix sockets with relative path and exception messages in redis.c and library.c --- common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.h b/common.h index 45e8768fdc..80381a6c26 100644 --- a/common.h +++ b/common.h @@ -248,7 +248,7 @@ typedef struct fold_item { typedef struct { php_stream *stream; zend_string *host; - short port; + int port; zend_string *auth; double timeout; double read_timeout; From 128e6bdcede1c2ed04d1141d2266506c50f6279a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 9 Aug 2019 16:12:03 +0300 Subject: [PATCH 1240/1986] Update changelog --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index 82a888e72b..fc75937ff2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,9 +22,14 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed +- Fix regression for multihost_distribute_call added in [112c77e3](https://github.com/phpredis/phpredis/commit/112c77e3) + [fbe0f804](https://github.com/phpredis/phpredis/commit/fbe0f804) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) - Fix regression for conntecting to ports > 32767 [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64) ([Owen Smith](https://github.com/orls)) + [7ef17ce1](https://github.com/phpredis/phpredis/commit/7ef17ce1) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) - RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/commit/f52cd237), [cb5d6b94](https://github.com/phpredis/phpredis/commit/cb5d6b94) From 1a751ca28971bae62c6134920858a35b85adbb22 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 9 Aug 2019 16:19:55 +0300 Subject: [PATCH 1241/1986] Update changelog --- Changelog.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index fc75937ff2..644b037855 100644 --- a/Changelog.md +++ b/Changelog.md @@ -24,12 +24,12 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix regression for multihost_distribute_call added in [112c77e3](https://github.com/phpredis/phpredis/commit/112c77e3) [fbe0f804](https://github.com/phpredis/phpredis/commit/fbe0f804) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix regression for conntecting to ports > 32767 - [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64) - ([Owen Smith](https://github.com/orls)) + [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64), + [17b139d8](https://github.com/phpredis/phpredis/commit/17b139d8), [7ef17ce1](https://github.com/phpredis/phpredis/commit/7ef17ce1) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) + ([Owen Smith](https://github.com/orls),[Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/commit/f52cd237), [cb5d6b94](https://github.com/phpredis/phpredis/commit/cb5d6b94) From 6e4a7b257546103677fffc6aa28007fd7e0a94c7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 9 Aug 2019 16:23:46 +0300 Subject: [PATCH 1242/1986] Update changelog --- Changelog.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 644b037855..ed14c84e55 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,20 +16,25 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix overallocation in RedisCluster directed node commands [cf93649](https://github.com/phpredis/phpredis/commit/cf93649) (([Michael Grunder](https://github.com/michael-grunder)) +### Fixed + +- Fix regression for multihost_distribute_call added in [112c77e3](https://github.com/phpredis/phpredis/commit/112c77e3) + [fbe0f804](https://github.com/phpredis/phpredis/commit/fbe0f804) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix regression for conntecting to unix sockets with relative path added in [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64) + [17b139d8](https://github.com/phpredis/phpredis/commit/17b139d8), + [7ef17ce1](https://github.com/phpredis/phpredis/commit/7ef17ce1) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) + --- ## [5.0.2] - 2019-07-29 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.0.2), [PECL](https://pecl.php.net/package/redis/5.0.2)) ### Fixed -- Fix regression for multihost_distribute_call added in [112c77e3](https://github.com/phpredis/phpredis/commit/112c77e3) - [fbe0f804](https://github.com/phpredis/phpredis/commit/fbe0f804) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix regression for conntecting to ports > 32767 [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64), - [17b139d8](https://github.com/phpredis/phpredis/commit/17b139d8), - [7ef17ce1](https://github.com/phpredis/phpredis/commit/7ef17ce1) - ([Owen Smith](https://github.com/orls),[Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + ([Owen Smith](https://github.com/orls)) - RedisCluster segfaults after second connection with cache_slots enabled [f52cd237](https://github.com/phpredis/phpredis/commit/f52cd237), [cb5d6b94](https://github.com/phpredis/phpredis/commit/cb5d6b94) From a98a3fbd2054d6b22c6a69eed2d8b5d76d42096c Mon Sep 17 00:00:00 2001 From: Sandstrom <1013635+tangix@users.noreply.github.com> Date: Sun, 11 Aug 2019 21:40:44 +0200 Subject: [PATCH 1243/1986] Updated README with correct return value for ping command --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index a9e5893412..ed5bf83b6c 100644 --- a/README.markdown +++ b/README.markdown @@ -364,8 +364,9 @@ _**Description**_: Check the current connection status ##### *Return value* -*STRING*: `+PONG` on success. Throws a [RedisException](#class-redisexception) object on connectivity error, as described above. +*BOOL*: `TRUE` in case of success. Throws a [RedisException](#class-redisexception) object on connectivity error, as described above. +Staring from version 5.0.0, the command returns boolean `TRUE` instead of *STRING* `+PONG` as in previous versions. ### echo ----- From b9dece2baf6a5c790f12f60415d5ca107136e23b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 11 Aug 2019 17:06:21 -0700 Subject: [PATCH 1244/1986] Update PING documentation Related to #1619 --- README.markdown | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index ed5bf83b6c..11579a5489 100644 --- a/README.markdown +++ b/README.markdown @@ -356,17 +356,26 @@ $redis->getOption(Redis::OPT_SERIALIZER); ### ping ----- -_**Description**_: Check the current connection status +_**Description**_: Check the current connection status. -##### *Parameters* - -(none) +##### *Prototype* +~~~php +$redis->ping([string $message]); +~~~ ##### *Return value* +*Mixed*: This method returns `TRUE` on success, or the passed string if called with an argument. -*BOOL*: `TRUE` in case of success. Throws a [RedisException](#class-redisexception) object on connectivity error, as described above. +##### *Example* +~~~php +/* When called without an argument, PING returns `TRUE` */ +$redis->ping(); + +/* If passed an argument, that argument is returned. Here 'hello' will be returned */ +$redis->ping('hello'); +~~~ -Staring from version 5.0.0, the command returns boolean `TRUE` instead of *STRING* `+PONG` as in previous versions. +*Note*: Prior to PhpRedis 5.0.0 this command simply returned the string `+PONG`. ### echo ----- From 0d6d3fdde34f48ae60c93968fae710ad70fa8c5a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 10 Aug 2019 20:48:50 -0700 Subject: [PATCH 1245/1986] Also attach slaves when caching cluster slots Addresses #1613 --- cluster_library.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 64166cc656..6fc7328b84 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -905,7 +905,7 @@ PHP_REDIS_API redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes) { redisCachedCluster *cc; redisCachedMaster *cm; - redisClusterNode *node; + redisClusterNode *node, *slave; cc = pecalloc(1, sizeof(*cc), 1); cc->hash = zend_string_dup(hash, 1); @@ -925,6 +925,19 @@ redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes) { /* Copy over slot ranges */ cm->slot = slot_range_list_clone(&node->slots, &cm->slots); + /* Attach any slave nodes we have. */ + if (node->slaves) { + /* Allocate memory for slaves */ + cm->slave = pemalloc(sizeof(*cm->slave) * zend_hash_num_elements(node->slaves), 1); + + /* Copy host/port information for each slave */ + ZEND_HASH_FOREACH_PTR(node->slaves, slave) { + cm->slave[cm->slaves].addr = zend_string_dup(slave->sock->host, 1); + cm->slave[cm->slaves].port = slave->sock->port; + cm->slaves++; + } ZEND_HASH_FOREACH_END(); + } + cc->count++; } ZEND_HASH_FOREACH_END(); From b114fc26f15b7b632300bc2b7e67d846aefa4fe5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 27 Aug 2019 11:06:56 -0700 Subject: [PATCH 1246/1986] Use pecalloc for consistency --- cluster_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster_library.c b/cluster_library.c index 6fc7328b84..0dcad152f4 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -928,7 +928,7 @@ redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes) { /* Attach any slave nodes we have. */ if (node->slaves) { /* Allocate memory for slaves */ - cm->slave = pemalloc(sizeof(*cm->slave) * zend_hash_num_elements(node->slaves), 1); + cm->slave = pecalloc(zend_hash_num_elements(node->slaves), sizeof(*cm->slave), 1); /* Copy host/port information for each slave */ ZEND_HASH_FOREACH_PTR(node->slaves, slave) { From 92b3f5d7824ddf31c06cd3a785e24b466ea7dc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Mon, 16 Sep 2019 13:38:35 +0200 Subject: [PATCH 1247/1986] Explicitly state that ping return type changed --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index ed14c84e55..75820f277d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -107,7 +107,7 @@ serializers, soft deprecation of non-Redis commands. ([@remicollet](https://github.com/remicollet)) - Enable connection pooling by default [8206b147](https://www.github.com/phpredis/phpredis/commit/8206b147) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) -- Allow PING to take an optional argument [6e494170](https://www.github.com/phpredis/phpredis/commit/6e494170) +- Allow PING to take an optional argument. PING now returns `true` instead of "+PONG" [6e494170](https://www.github.com/phpredis/phpredis/commit/6e494170) ([Michael Grunder](https://github.com/michael-grunder)) - Allow ZRANGE to be called either with `true` or `['withscores' => true]` [19f3efcf](https://www.github.com/phpredis/phpredis/commit/19f3efcf) ([Michael Grunder](https://github.com/michael-grunder)) From 3d8b85259c47bc9fac69a6a0a20dc1d5a35c4a3a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 25 Sep 2019 10:26:32 +0300 Subject: [PATCH 1248/1986] Use negative port number to indicate unix socket --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 6bf42a9408..0ef3e658ce 100644 --- a/library.c +++ b/library.c @@ -1807,7 +1807,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) schema = estrndup(address, pos - address); address = pos + sizeof("://") - 1; } - if (redis_sock->port < 1) { + if (redis_sock->port < 0) { host_len = snprintf(host, sizeof(host), "unix://%s", address); usocket = 1; } else { From d7b6e9d4bb47cc1dd4c3db5ff168bc7fc1c056ac Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 25 Sep 2019 13:26:40 +0300 Subject: [PATCH 1249/1986] Revert "Use negative port number to indicate unix socket" This reverts commit 3d8b85259c47bc9fac69a6a0a20dc1d5a35c4a3a. --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 0ef3e658ce..6bf42a9408 100644 --- a/library.c +++ b/library.c @@ -1807,7 +1807,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) schema = estrndup(address, pos - address); address = pos + sizeof("://") - 1; } - if (redis_sock->port < 0) { + if (redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", address); usocket = 1; } else { From fdada7ae2d2ebb4c82e80ee78b3ddf28801257e5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 29 Sep 2019 22:53:18 +0300 Subject: [PATCH 1250/1986] Use zend_register_persistent_resource_ex --- library.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/library.c b/library.c index 6bf42a9408..2b99899aa1 100644 --- a/library.c +++ b/library.c @@ -63,12 +63,17 @@ redis_sock_get_connection_pool(RedisSock *redis_sock) zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); if (!le) { - ConnectionPool *p = pecalloc(1, sizeof(*p) + sizeof(*le), 1); + ConnectionPool *p = pecalloc(1, sizeof(*p), 1); zend_llist_init(&p->list, sizeof(php_stream *), NULL, 1); - le = (zend_resource *)((char *)p + sizeof(*p)); - le->type = le_redis_pconnect; - le->ptr = p; - zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); +#if (PHP_VERSION_ID < 70300) + zend_resource res; + res.type = le_redis_pconnect; + res.ptr = p; + le = &res; + zend_hash_update_mem(&EG(persistent_list), persistent_id, le, sizeof(*le)); +#else + le = zend_register_persistent_resource_ex(persistent_id, p, le_redis_pconnect); +#endif } zend_string_release(persistent_id); return le->ptr; From a080b73f07815adf7668e0e70fa4184be606fd6b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 29 Sep 2019 23:28:05 +0300 Subject: [PATCH 1251/1986] Fix unix-socket detection logic broken in 418428fa --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index 2b99899aa1..2b74aca29e 100644 --- a/library.c +++ b/library.c @@ -1812,7 +1812,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) schema = estrndup(address, pos - address); address = pos + sizeof("://") - 1; } - if (redis_sock->port < 1) { + if (address[0] == '/' && redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", address); usocket = 1; } else { From c0db75b5acb30b5f44881144f941cf87ac280977 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 5 Oct 2019 20:23:16 +0300 Subject: [PATCH 1252/1986] Fix coverity scan warnings --- library.c | 10 +++++++--- redis_array.c | 6 ++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/library.c b/library.c index 2b74aca29e..e10caaf08e 100644 --- a/library.c +++ b/library.c @@ -60,10 +60,14 @@ extern int le_redis_pconnect; static ConnectionPool * redis_sock_get_connection_pool(RedisSock *redis_sock) { + ConnectionPool *p; zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id); - if (!le) { - ConnectionPool *p = pecalloc(1, sizeof(*p), 1); + + if (le) { + p = le->ptr; + } else { + p = pecalloc(1, sizeof(*p), 1); zend_llist_init(&p->list, sizeof(php_stream *), NULL, 1); #if (PHP_VERSION_ID < 70300) zend_resource res; @@ -76,7 +80,7 @@ redis_sock_get_connection_pool(RedisSock *redis_sock) #endif } zend_string_release(persistent_id); - return le->ptr; + return p; } /* Helper to reselect the proper DB number when we reconnect */ diff --git a/redis_array.c b/redis_array.c index 6876914425..3645c02f63 100644 --- a/redis_array.c +++ b/redis_array.c @@ -227,8 +227,10 @@ ra_call_user_function(HashTable *function_table, zval *object, zval *function_na { if (object) { redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object); - if (redis->sock->auth && redis->sock->status != REDIS_SOCK_STATUS_CONNECTED) { - redis_sock_server_open(redis->sock); + if (redis->sock->auth && + redis->sock->status != REDIS_SOCK_STATUS_CONNECTED && + redis_sock_server_open(redis->sock) == SUCCESS + ) { redis_sock_auth(redis->sock); } } From 7f42d628ba7d24c93eba649f6b70a2564e5491e3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 6 Oct 2019 00:20:07 +0300 Subject: [PATCH 1253/1986] Issue #1643 Set error message and fix memory leak. --- library.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/library.c b/library.c index e10caaf08e..4c0ee96eb3 100644 --- a/library.c +++ b/library.c @@ -2112,16 +2112,19 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc zval *z_keys = ctx; if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { - return -1; + goto failure; } - if(inbuf[0] != '*') { + if (*inbuf != TYPE_MULTIBULK) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } - return -1; + if (*inbuf == TYPE_ERR) { + redis_sock_set_err(redis_sock, inbuf + 1, len); + } + goto failure; } numElems = atoi(inbuf+1); zval z_multi_result; @@ -2151,7 +2154,15 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc } else { add_next_index_zval(z_tab, &z_multi_result); } - return 0; + return SUCCESS; +failure: + if (z_keys != NULL) { + for (i = 0; Z_TYPE(z_keys[i]) != IS_NULL; ++i) { + zval_dtor(&z_keys[i]); + } + efree(z_keys); + } + return FAILURE; } /** From 3a622a07ba6a0311fd5f8bb2ba644c23caf5fd98 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 5 Oct 2019 15:57:27 -0700 Subject: [PATCH 1254/1986] Also set error for hGetAll and lower error len by 1 --- library.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index 4c0ee96eb3..b2927b3284 100644 --- a/library.c +++ b/library.c @@ -1211,6 +1211,9 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else { add_next_index_bool(z_tab, 0); } + if (*inbuf == TYPE_ERR) { + redis_sock_set_err(redis_sock, inbuf + 1, len - 1); + } return -1; } numElems = atoi(inbuf+1); @@ -2122,7 +2125,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc add_next_index_bool(z_tab, 0); } if (*inbuf == TYPE_ERR) { - redis_sock_set_err(redis_sock, inbuf + 1, len); + redis_sock_set_err(redis_sock, inbuf + 1, len - 1); } goto failure; } From 7c6c43a6ac81b0f73c55892e675c1875dcd0a2c8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 6 Oct 2019 23:06:20 +0300 Subject: [PATCH 1255/1986] Fix segfault in zend_destroy_rsrc_list --- library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.c b/library.c index b2927b3284..8921c0fb9f 100644 --- a/library.c +++ b/library.c @@ -74,9 +74,9 @@ redis_sock_get_connection_pool(RedisSock *redis_sock) res.type = le_redis_pconnect; res.ptr = p; le = &res; - zend_hash_update_mem(&EG(persistent_list), persistent_id, le, sizeof(*le)); + zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le)); #else - le = zend_register_persistent_resource_ex(persistent_id, p, le_redis_pconnect); + le = zend_register_persistent_resource(ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), p, le_redis_pconnect); #endif } zend_string_release(persistent_id); From 2bb086802e4623a8543080883342ed4cf3c45eee Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 7 Oct 2019 15:00:36 +0300 Subject: [PATCH 1256/1986] Issue #1631 --- redis_session.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index 0bffc0d60a..33ccedeef2 100644 --- a/redis_session.c +++ b/redis_session.c @@ -408,13 +408,23 @@ PS_OPEN_FUNC(redis) /* parse parameters */ if (url->query != NULL) { + char *query; array_init(¶ms); #if (PHP_VERSION_ID < 70300) - sapi_module.treat_data(PARSE_STRING, estrdup(url->query), ¶ms); + if (url->fragment != NULL) { + spprintf(&query, 0, "%s#%s", url->query, url->fragment); + } else { + query = estrdup(url->query); + } #else - sapi_module.treat_data(PARSE_STRING, estrndup(ZSTR_VAL(url->query), ZSTR_LEN(url->query)), ¶ms); + if (url->fragment != NULL) { + spprintf(&query, 0, "%s#%s", ZSTR_VAL(url->query), ZSTR_VAL(url->fragment)); + } else { + query = estrndup(ZSTR_VAL(url->query), ZSTR_LEN(url->query)); + } #endif + sapi_module.treat_data(PARSE_STRING, query, ¶ms); if ((param = zend_hash_str_find(Z_ARRVAL(params), "weight", sizeof("weight") - 1)) != NULL) { weight = zval_get_long(param); From 6dda8d42e87346d6df70a2586138617d176a46ec Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 9 Oct 2019 12:47:04 +0300 Subject: [PATCH 1257/1986] Update changelog --- Changelog.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 75820f277d..ae3f27994b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,18 +13,49 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm This requires libzstd version >= 1.3.0 [2abc61da](https://github.com/phpredis/phpredis/commit/2abc61da) ([Remi Collet](https://github.com/remicollet)) -- Fix overallocation in RedisCluster directed node commands [cf93649](https://github.com/phpredis/phpredis/commit/cf93649) - (([Michael Grunder](https://github.com/michael-grunder)) + +### Changed + +- Refactor redis_session + [91a8e734](https://github.com/phpredis/phpredis/commit/91a8e734), + [978c3074](https://github.com/phpredis/phpredis/commit/978c3074) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix overallocation in RedisCluster directed node commands + [cf93649](https://github.com/phpredis/phpredis/commit/cf93649) + ([Michael Grunder](https://github.com/michael-grunder)) +- Also attach slaves when caching cluster slots + [0d6d3fdd](https://github.com/phpredis/phpredis/commit/0d6d3fdd), + [b114fc26](https://github.com/phpredis/phpredis/commit/b114fc26) + ([Michael Grunder](https://github.com/michael-grunder)) +- Use zend_register_persistent_resource_ex for connection pooling + [fdada7ae](https://github.com/phpredis/phpredis/commit/fdada7ae), + [7c6c43a6](https://github.com/phpredis/phpredis/commit/7c6c43a6) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) ### Fixed - Fix regression for multihost_distribute_call added in [112c77e3](https://github.com/phpredis/phpredis/commit/112c77e3) [fbe0f804](https://github.com/phpredis/phpredis/commit/fbe0f804) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Revert "fix regression for conntecting to ports > 32767" added in [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64) and add another fix + [17b139d8](https://github.com/phpredis/phpredis/commit/17b139d8), + [7ef17ce1](https://github.com/phpredis/phpredis/commit/7ef17ce1) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) - Fix regression for conntecting to unix sockets with relative path added in [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64) [17b139d8](https://github.com/phpredis/phpredis/commit/17b139d8), [7ef17ce1](https://github.com/phpredis/phpredis/commit/7ef17ce1) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) +- Fix unix-socket detection logic broken in [418428fa](https://github.com/phpredis/phpredis/commit/418428fa) + [a080b73f](https://github.com/phpredis/phpredis/commit/a080b73f) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) +- Fix memory leak and bug with getLastError for redis_mbulk_reply_assoc and redis_mbulk_reply_zipped. + [7f42d628](https://github.com/phpredis/phpredis/commit/7f42d628), + [3a622a07](https://github.com/phpredis/phpredis/commit/3a622a07) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix bug with password contain "#" for redis_session + [2bb08680](https://github.com/phpredis/phpredis/commit/2bb08680) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) --- From 62fd5a3b5da82be9968acc8936d749abad280a01 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 9 Oct 2019 12:51:19 +0300 Subject: [PATCH 1258/1986] Update changelog --- Changelog.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index ae3f27994b..995875674a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -40,22 +40,22 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Revert "fix regression for conntecting to ports > 32767" added in [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64) and add another fix [17b139d8](https://github.com/phpredis/phpredis/commit/17b139d8), [7ef17ce1](https://github.com/phpredis/phpredis/commit/7ef17ce1) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix regression for conntecting to unix sockets with relative path added in [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64) [17b139d8](https://github.com/phpredis/phpredis/commit/17b139d8), [7ef17ce1](https://github.com/phpredis/phpredis/commit/7ef17ce1) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix unix-socket detection logic broken in [418428fa](https://github.com/phpredis/phpredis/commit/418428fa) [a080b73f](https://github.com/phpredis/phpredis/commit/a080b73f) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix memory leak and bug with getLastError for redis_mbulk_reply_assoc and redis_mbulk_reply_zipped. [7f42d628](https://github.com/phpredis/phpredis/commit/7f42d628), [3a622a07](https://github.com/phpredis/phpredis/commit/3a622a07) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), ([Michael Grunder](https://github.com/michael-grunder)) - Fix bug with password contain "#" for redis_session [2bb08680](https://github.com/phpredis/phpredis/commit/2bb08680) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) --- From 8bc2240c1528d78237983c6e77214977e31692f7 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 9 Oct 2019 14:48:41 +0200 Subject: [PATCH 1259/1986] missing nul byte --- redis.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 4a13a7d431..345ee16a71 100644 --- a/redis.c +++ b/redis.c @@ -887,7 +887,8 @@ PHP_MINFO_FUNCTION(redis) smart_str_appends(&names, "zstd"); #endif if (names.s) { - php_info_print_table_row(2, "Available compression", names.s->val); + smart_str_0(&names); + php_info_print_table_row(2, "Available compression", ZSTR_VAL(names.s)); } smart_str_free(&names); php_info_print_table_end(); From 8ee4abbc3fbc47201f20a2a48ccee6f6f765952b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 11 Oct 2019 21:13:14 +0300 Subject: [PATCH 1260/1986] Dead code generic_unsubscribe_cmd --- php_redis.h | 9 ------- redis.c | 67 ----------------------------------------------------- 2 files changed, 76 deletions(-) diff --git a/php_redis.h b/php_redis.h index ad25b43c24..f17066ec57 100644 --- a/php_redis.h +++ b/php_redis.h @@ -268,17 +268,8 @@ typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); -PHP_REDIS_API void generic_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, char *sub_cmd); - -PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, - char *unsub_cmd); - PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock); -PHP_REDIS_API int get_flag(zval *object); - -PHP_REDIS_API void set_flag(zval *object, int new_flag); - PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int numElems); diff --git a/redis.c b/redis.c index 345ee16a71..1af8549305 100644 --- a/redis.c +++ b/redis.c @@ -2622,73 +2622,6 @@ PHP_METHOD(Redis, subscribe) { * ); **/ -PHP_REDIS_API void generic_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, - char *unsub_cmd) -{ - zval *object, *array, *data; - HashTable *arr_hash; - RedisSock *redis_sock; - char *cmd = "", *old_cmd = NULL; - int cmd_len, array_count; - - int i; - zval z_tab, *z_channel; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", - &object, redis_ce, &array) == FAILURE) { - RETURN_FALSE; - } - if ((redis_sock = redis_sock_get(object, 0)) == NULL) { - RETURN_FALSE; - } - - arr_hash = Z_ARRVAL_P(array); - array_count = zend_hash_num_elements(arr_hash); - - if (array_count == 0) { - RETURN_FALSE; - } - - ZEND_HASH_FOREACH_VAL(arr_hash, data) { - ZVAL_DEREF(data); - if (Z_TYPE_P(data) == IS_STRING) { - char *old_cmd = NULL; - if(*cmd) { - old_cmd = cmd; - } - spprintf(&cmd, 0, "%s %s", cmd, Z_STRVAL_P(data)); - if(old_cmd) { - efree(old_cmd); - } - } - } ZEND_HASH_FOREACH_END(); - - old_cmd = cmd; - cmd_len = spprintf(&cmd, 0, "%s %s\r\n", unsub_cmd, cmd); - efree(old_cmd); - - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) - efree(cmd); - - array_init(return_value); - for (i = 1; i <= array_count; i++) { - redis_sock_read_multibulk_reply_zval( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_tab); - - if (Z_TYPE(z_tab) == IS_ARRAY) { - if ((z_channel = zend_hash_index_find(Z_ARRVAL(z_tab), 1)) == NULL) { - RETURN_FALSE; - } - add_assoc_bool(return_value, Z_STRVAL_P(z_channel), 1); - } else { - //error - zval_dtor(&z_tab); - RETURN_FALSE; - } - zval_dtor(&z_tab); - } -} - PHP_METHOD(Redis, unsubscribe) { REDIS_PROCESS_KW_CMD("UNSUBSCRIBE", redis_unsubscribe_cmd, From 99ec24b361658a98c12bfb4fd29cc16f9a7ec24c Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 14 Oct 2019 22:24:21 +0200 Subject: [PATCH 1261/1986] Add documentation for zpopmin and zpopmax --- README.markdown | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.markdown b/README.markdown index 11579a5489..38a6515313 100644 --- a/README.markdown +++ b/README.markdown @@ -2653,6 +2653,7 @@ while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) { * [zCount](#zcount) - Count the members in a sorted set with scores within the given values * [zIncrBy](#zincrby) - Increment the score of a member in a sorted set * [zinterstore, zInter](#zinterstore-zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key +* [zPop](#zpop) - Redis can pop the highest or lowest scoring member from one a ZSET. * [zRange](#zrange) - Return a range of members in a sorted set, by index * [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score * [zRangeByLex](#zrangebylex) - Return a lexicographical range from members that share the same score @@ -2816,6 +2817,33 @@ $redis->zinterstore('ko4', ['k1', 'k2'], [1, 5], 'max'); /* 2, 'ko4' => ['val3', **Note:** `zInter` is an alias for `zinterstore` and will be removed in future versions of phpredis. +### zPop +----- +_**Description**_: Can pop the highest or lowest scoring members from one ZSETs. There are two commands (`ZPOPMIN` and `ZPOPMAX` for popping the lowest and highest scoring elements respectively.) + +##### *Prototype* +~~~php +$redis->zPopMin(string $key, int $count): array +$redis->zPopMax(string $key, int $count): array + +$redis->zPopMin(string $key, int $count): array +$redis->zPopMax(string $key, int $count): array +~~~ + +##### *Return value* +*ARRAY:* Either an array with the key member and score of the higest or lowest element or an empty array if there is no element available. + +##### *Example* +~~~php +/* Wait up to 5 seconds to pop the *lowest* scoring member from sets `zs1` and `zs2`. */ +$redis->bzPopMin(['zs1', 'zs2'], 5); +$redis->bzPopMin('zs1', 'zs2', 5); + +/* Wait up to 5 seconds to pop the *highest* scoring member from sets `zs1` and `zs2` */ +$redis->bzPopMax(['zs1', 'zs2'], 5); +$redis->bzPopMax('zs1', 'zs2', 5); +~~~ + ### zRange ----- _**Description**_: Returns a range of elements from the ordered set stored at the specified key, with values in the range [start, end]. From 4ab1f940d8da38f78a9cd92b27cabc5230e9bac2 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 14 Oct 2019 22:26:33 +0200 Subject: [PATCH 1262/1986] Fix example --- README.markdown | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index 38a6515313..bca54da10f 100644 --- a/README.markdown +++ b/README.markdown @@ -2835,13 +2835,11 @@ $redis->zPopMax(string $key, int $count): array ##### *Example* ~~~php -/* Wait up to 5 seconds to pop the *lowest* scoring member from sets `zs1` and `zs2`. */ -$redis->bzPopMin(['zs1', 'zs2'], 5); -$redis->bzPopMin('zs1', 'zs2', 5); +/* Pop the *lowest* scoring member from set `zs1`. */ +$redis->zPopMin('zs1', 5); -/* Wait up to 5 seconds to pop the *highest* scoring member from sets `zs1` and `zs2` */ -$redis->bzPopMax(['zs1', 'zs2'], 5); -$redis->bzPopMax('zs1', 'zs2', 5); +/* Pop the *highest* scoring member from set `zs1`. */ +$redis->zPopMax('zs1', 5); ~~~ ### zRange From 8739fa5fa8c3c00fbcdf759dd42e8f0c7d46c66f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 21 Oct 2019 08:44:19 +0300 Subject: [PATCH 1263/1986] Update changelog --- Changelog.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Changelog.md b/Changelog.md index 995875674a..1e2c560594 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,10 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm This requires libzstd version >= 1.3.0 [2abc61da](https://github.com/phpredis/phpredis/commit/2abc61da) ([Remi Collet](https://github.com/remicollet)) +- Add documentation for zpopmin and zpopmax + [99ec24b3](https://github.com/phpredis/phpredis/commit/99ec24b3), + [4ab1f940](https://github.com/phpredis/phpredis/commit/4ab1f940) + ([alexander-schranz](https://github.com/alexander-schranz)) ### Changed @@ -56,6 +60,15 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix bug with password contain "#" for redis_session [2bb08680](https://github.com/phpredis/phpredis/commit/2bb08680) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Missing null byte in PHP_MINFO_FUNCTION + [8bc2240c](https://github.com/phpredis/phpredis/commit/8bc2240c) + ([Remi Collet](https://github.com/remicollet)) + +### Removed + +- Dead code generic_unsubscribe_cmd + [8ee4abbc](https://github.com/phpredis/phpredis/commit/8ee4abbc) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) --- From 75a6f3fa979a36b05890c0ec7db2304d965f4d39 Mon Sep 17 00:00:00 2001 From: Roberto Luna Rojas Date: Tue, 22 Oct 2019 22:25:42 -0400 Subject: [PATCH 1264/1986] HyperLogLogs - pfAdd --- README.markdown | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.markdown b/README.markdown index bca54da10f..d4d9cbc222 100644 --- a/README.markdown +++ b/README.markdown @@ -33,6 +33,7 @@ If you've found phpredis useful and would like to buy the maintainers a coffee ( * [Lists](#lists) * [Sets](#sets) * [Sorted sets](#sorted-sets) + * [HyperLogLogs](#hyperloglogs) * [Geocoding](#geocoding) * [Streams](#streams) * [Pub/sub](#pubsub) @@ -3114,6 +3115,32 @@ while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { } ~~~ +## HyperLogLogs + +### pfAdd +----- + +##### *Prototype* +~~~php +$redis->pfAdd($key, Array $elements); +~~~ + +_**Description**_: Adds the specified elements to the specified HyperLogLog. + +##### *Return value* +*Integer*: 1 if at least 1 HyperLogLog internal register was altered. 0 otherwise. + +##### *Example* +~~~php +$redis->pfAdd('hll', ['a', 'b', 'c']); +~~~ + +### pfCount +----- + +### pfMerge +----- + ## Geocoding ### geoAdd From 96a0f0c3433f600ce7ea700d7e41a16f346e7c34 Mon Sep 17 00:00:00 2001 From: Roberto Luna Rojas Date: Tue, 22 Oct 2019 22:37:03 -0400 Subject: [PATCH 1265/1986] HyperLogLogs - pfCount --- README.markdown | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.markdown b/README.markdown index d4d9cbc222..f6550b81c4 100644 --- a/README.markdown +++ b/README.markdown @@ -3138,6 +3138,28 @@ $redis->pfAdd('hll', ['a', 'b', 'c']); ### pfCount ----- +##### *Prototype* +~~~php +$redis->pfCount($key); +$redis->pfCount(Array $keys); +~~~ + +_**Description**_: Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s). + +##### *Return value* +*Integer*: The approximated number of unique elements observed via [pfAdd](#pfAdd). + +##### *Example* +~~~php +$redis->pfAdd('hll1', ['a', 'b', 'c']); // (int) 1 +$redis->pfCount('hll1'); // (int) 3 + +$redis->pfAdd('hll2', ['d', 'e', 'a']); // (int) 1 +$redis->pfCount('hll2'); // (int) 3 + +$redis->pfCount(['hll1', 'hll2']); // (int) 5 +~~~ + ### pfMerge ----- From 9686757acc0598635f588132cf95a642e2c535b3 Mon Sep 17 00:00:00 2001 From: Roberto Luna Rojas Date: Tue, 22 Oct 2019 22:51:47 -0400 Subject: [PATCH 1266/1986] HyperLogLogs- pfMerge --- README.markdown | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index f6550b81c4..00a60cc654 100644 --- a/README.markdown +++ b/README.markdown @@ -3120,31 +3120,39 @@ while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { ### pfAdd ----- +_**Description**_: Adds the specified elements to the specified HyperLogLog. + ##### *Prototype* ~~~php $redis->pfAdd($key, Array $elements); ~~~ -_**Description**_: Adds the specified elements to the specified HyperLogLog. +##### *Parameters* +_Key_ +_Array of values_ ##### *Return value* *Integer*: 1 if at least 1 HyperLogLog internal register was altered. 0 otherwise. ##### *Example* ~~~php -$redis->pfAdd('hll', ['a', 'b', 'c']); +$redis->pfAdd('hll', ['a', 'b', 'c']); // (int) 1 +$redis->pfAdd('hll', ['a', 'b']); // (int) 0 ~~~ ### pfCount ----- +_**Description**_: Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s). + ##### *Prototype* ~~~php $redis->pfCount($key); $redis->pfCount(Array $keys); ~~~ -_**Description**_: Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s). +##### *Parameters* +_Key_ or _Array of keys_ ##### *Return value* *Integer*: The approximated number of unique elements observed via [pfAdd](#pfAdd). @@ -3163,6 +3171,30 @@ $redis->pfCount(['hll1', 'hll2']); // (int) 5 ### pfMerge ----- +_**Description**_: Merge N different HyperLogLogs into a single one. + +##### *Prototype* +~~~php +$redis->pfMerge($destkey, Array $sourceKeys); +~~~ + +##### *Parameters* +_Destination Key_ +_Array of Source Keys_ + +##### *Return value* +*BOOL*: `TRUE` on success, `FALSE` on error. + +##### *Example* +~~~php +$redis->pfAdd('hll1', ['a', 'b', 'c']); // (int) 1 +$redis->pfAdd('hll2', ['d', 'e', 'a']); // (int) 1 + +$redis->pfMerge('hll3', ['hll1', 'hll2']); // true + +$redis->pfCount('hll3'); // (int) 5 +~~~ + ## Geocoding ### geoAdd From 53a8bcc7a9f316c9dfdc118e316139defc9316e1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 26 Oct 2019 17:54:27 +0300 Subject: [PATCH 1267/1986] Issue #1657 Allow to specify schema for session handler. --- redis_session.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/redis_session.c b/redis_session.c index 33ccedeef2..6bb94c603f 100644 --- a/redis_session.c +++ b/redis_session.c @@ -469,11 +469,14 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if (url->host) { + zend_string *address; #if (PHP_VERSION_ID < 70300) - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval); + address = strpprintf(0, "%s://%s", url->scheme ? url->scheme : "tcp", url->host); #else - redis_sock = redis_sock_create(ZSTR_VAL(url->host), ZSTR_LEN(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval); + address = strpprintf(0, "%s://%s", url->scheme ? ZSTR_VAL(url->scheme) : "tcp", ZSTR_VAL(url->host)); #endif + redis_sock = redis_sock_create(ZSTR_VAL(address), ZSTR_LEN(address), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval); + zend_string_release(address); } else { /* unix */ #if (PHP_VERSION_ID < 70300) redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval); From 351ccef1d59f3ddb550dd093469c08922c269019 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 31 Oct 2019 21:19:26 +0200 Subject: [PATCH 1268/1986] Update changelog --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 1e2c560594..9f8b177e7b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,9 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm [99ec24b3](https://github.com/phpredis/phpredis/commit/99ec24b3), [4ab1f940](https://github.com/phpredis/phpredis/commit/4ab1f940) ([alexander-schranz](https://github.com/alexander-schranz)) +- Allow to specify scheme for session handler. + [53a8bcc7](https://github.com/phpredis/phpredis/commit/53a8bcc7) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) ### Changed From 6bdcd6dfbab3f5143e97dc9a0a61295bfe182efc Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 31 Oct 2019 22:50:23 +0200 Subject: [PATCH 1269/1986] Back to dev --- Changelog.md | 4 ++++ php_redis.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 9f8b177e7b..0f83a4b0f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,10 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +--- + +## [5.1.0] - 2019-10-31 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.1.0), [PECL](https://pecl.php.net/package/redis/5.1.0)) + ### Added - Add optional support for Zstd compression, using `--enable-redis-ztsd`. diff --git a/php_redis.h b/php_redis.h index f17066ec57..977c537835 100644 --- a/php_redis.h +++ b/php_redis.h @@ -25,7 +25,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "5.1.0-dev" +#define PHP_REDIS_VERSION "5.2.0-dev" PHP_METHOD(Redis, __construct); PHP_METHOD(Redis, __destruct); From 2bae8010507c1f48d456b93c700671b21b3b29a4 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 4 Nov 2019 17:17:01 +0200 Subject: [PATCH 1270/1986] Issue #1664 In PR #1602 we decided to use unsigned short for storing RedisSock->port but in previous release I reverted that change. In this PR I changed signatire of redis_sock_create to prevent unneccecary convertion of types. --- README.markdown | 2 -- library.c | 2 +- library.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index 00a60cc654..e1b467e43c 100644 --- a/README.markdown +++ b/README.markdown @@ -204,7 +204,6 @@ $redis->connect('tls://127.0.0.1'); // enable transport level security, port 637 $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout. $redis->connect('/tmp/redis.sock'); // unix domain socket. $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts. -$redis->connect('unix://redis.sock'); // relative path to unix domain socket requires version 5.0.0 or higher. ~~~ ### pconnect, popen @@ -246,7 +245,6 @@ $redis->pconnect('tls://127.0.0.1'); // enable transport level security, port 63 $redis->pconnect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout and would be another connection than the two before. $redis->pconnect('127.0.0.1', 6379, 2.5, 'x'); // x is sent as persistent_id and would be another connection than the three before. $redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another connection than the four before. -$redis->pconnect('unix://redis.sock'); // relative path to unix domain socket requires version 5.0.0 or higher. ~~~ ### auth diff --git a/library.c b/library.c index 8921c0fb9f..98ec4ad32f 100644 --- a/library.c +++ b/library.c @@ -1753,7 +1753,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * redis_sock_create */ PHP_REDIS_API RedisSock* -redis_sock_create(char *host, int host_len, unsigned short port, +redis_sock_create(char *host, int host_len, int port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval) diff --git a/library.h b/library.h index 11da7a4fc1..05ef917e4f 100644 --- a/library.h +++ b/library.h @@ -48,7 +48,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); +PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, int port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock); From 9f4ededa4139f0af324aab56773f26be5a9d1783 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 4 Nov 2019 13:55:34 -0800 Subject: [PATCH 1271/1986] Tests for unix socket and high ports Adds some tests to protect against regressions when connecting to unix sockets and high ports. --- .travis.yml | 2 ++ tests/RedisTest.php | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9668f236ff..237a50200c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,8 @@ before_install: install: make install before_script: - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap + - redis-server --port 0 --daemonize yes --unixsocket /tmp/redis.sock + - for PORT in $(seq 32767 32769); do redis-server --port $PORT --daemonize yes; done - for PORT in $(seq 6379 6382); do redis-server --port $PORT --daemonize yes; done - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 027291a2d8..40523b8368 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5882,6 +5882,62 @@ public function testXInfo() } } + /* 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]); + } + + $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 = [32767, 32768, 32769]; + $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); + $this->assertTrue($obj_r->ping()); + } catch(Exception $ex) { + $this->assertTrue(false); + } + } + } + public function testSession_savedToRedis() { $this->setSessionHandler(); From 741df6c7b390947c3bfcc90f8ea96757d4044223 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Tue, 5 Nov 2019 09:29:30 -0500 Subject: [PATCH 1272/1986] Add `--enable-redis-zstd` to install docs --- Changelog.md | 2 +- INSTALL.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0f83a4b0f5..fad56db352 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,7 +13,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -- Add optional support for Zstd compression, using `--enable-redis-ztsd`. +- Add optional support for Zstd compression, using `--enable-redis-zstd`. This requires libzstd version >= 1.3.0 [2abc61da](https://github.com/phpredis/phpredis/commit/2abc61da) ([Remi Collet](https://github.com/remicollet)) diff --git a/INSTALL.markdown b/INSTALL.markdown index 96d1399454..981b103e5a 100644 --- a/INSTALL.markdown +++ b/INSTALL.markdown @@ -12,7 +12,7 @@ To build this extension for the sources tree: ~~~ phpize -./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] +./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd] make && make install ~~~ From 6f3f3782746c45989ba6cd83b3136bfa112d320a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Wed, 6 Nov 2019 08:49:44 +0200 Subject: [PATCH 1273/1986] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..198b174666 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [michael-grunder] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 96336663228db6c406b7ff83123e8a1d49b0ef9f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 7 Nov 2019 14:46:43 +0200 Subject: [PATCH 1274/1986] Update changelog --- Changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.md b/Changelog.md index fad56db352..f168acc30a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,13 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Fixed + +- Fix fail to connect to redis through unix socket + [2bae8010](https://github.com/phpredis/phpredis/commit/2bae8010), + [9f4ededa](https://github.com/phpredis/phpredis/commit/9f4ededa) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) + --- ## [5.1.0] - 2019-10-31 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.1.0), [PECL](https://pecl.php.net/package/redis/5.1.0)) From 5c7bc3995d9c3dc080f3f9c8fd7086e07d2a4163 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 11 Nov 2019 10:21:01 +0200 Subject: [PATCH 1275/1986] Update changelog --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index f168acc30a..090f537144 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,10 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +--- + +## [5.1.1] - 2019-11-11 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.1.1), [PECL](https://pecl.php.net/package/redis/5.1.1)) + ### Fixed - Fix fail to connect to redis through unix socket From 23b1a9d84caed3e9bd64b9cc1b7b0b0cf2a7861a Mon Sep 17 00:00:00 2001 From: Michael Booth Date: Mon, 2 Dec 2019 09:00:12 +0000 Subject: [PATCH 1276/1986] Enable slot caching for session cluster --- cluster_library.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++ cluster_library.h | 2 ++ redis_cluster.c | 80 --------------------------------------------- redis_session.c | 14 ++++++-- 4 files changed, 96 insertions(+), 82 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0dcad152f4..47c0c52bf3 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -7,6 +7,7 @@ #include extern zend_class_entry *redis_cluster_exception_ce; +int le_cluster_slot_cache; /* Debugging methods/ static void cluster_dump_nodes(redisCluster *c) { @@ -2767,4 +2768,85 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, return SUCCESS; } +/* Turn a seed array into a zend_string we can use to look up a slot cache */ +zend_string *cluster_hash_seeds(HashTable *ht) { + smart_str hash = {0}; + zend_string *zstr; + zval *z_seed; + + ZEND_HASH_FOREACH_VAL(ht, z_seed) { + zstr = zval_get_string(z_seed); + smart_str_appendc(&hash, '['); + smart_str_appendl(&hash, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + smart_str_appendc(&hash, ']'); + zend_string_release(zstr); + } ZEND_HASH_FOREACH_END(); + + /* Not strictly needed but null terminate anyway */ + smart_str_0(&hash); + + /* smart_str is a zend_string internally */ + return hash.s; +} + + +#define SLOT_CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1) +PHP_REDIS_API redisCachedCluster *cluster_cache_load(HashTable *ht_seeds) { + zend_resource *le; + zend_string *h; + + /* Short circuit if we're not caching slots or if our seeds don't have any + * elements, since it doesn't make sense to cache an empty string */ + if (!SLOT_CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) + return NULL; + + /* Look for cached slot information */ + h = cluster_hash_seeds(ht_seeds); + le = zend_hash_str_find_ptr(&EG(persistent_list), ZSTR_VAL(h), ZSTR_LEN(h)); + zend_string_release(h); + + if (le != NULL) { + /* Sanity check on our list type */ + if (le->type != le_cluster_slot_cache) { + php_error_docref(0, E_WARNING, "Invalid slot cache resource"); + return NULL; + } + + /* Success, return the cached entry */ + return le->ptr; + } + + /* Not found */ + return NULL; +} + +/* Cache a cluster's slot information in persistent_list if it's enabled */ +PHP_REDIS_API int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes) { + redisCachedCluster *cc; + zend_string *hash; + + /* Short circuit if caching is disabled or there aren't any seeds */ + if (!SLOT_CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) + return !SLOT_CACHING_ENABLED() ? SUCCESS : FAILURE; + + /* Construct our cache */ + hash = cluster_hash_seeds(ht_seeds); + cc = cluster_cache_create(hash, nodes); + zend_string_release(hash); + + /* Set up our resource */ +#if PHP_VERSION_ID < 70300 + zend_resource le; + le.type = le_cluster_slot_cache; + le.ptr = cc; + + zend_hash_update_mem(&EG(persistent_list), cc->hash, (void*)&le, sizeof(zend_resource)); +#else + zend_register_persistent_resource_ex(cc->hash, cc, le_cluster_slot_cache); +#endif + + return SUCCESS; +} + + /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/cluster_library.h b/cluster_library.h index fa3bdc1108..2f6d6d8bf2 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -397,6 +397,8 @@ PHP_REDIS_API void cluster_init_cache(redisCluster *c, redisCachedCluster *rcc); PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len); +PHP_REDIS_API int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes); +PHP_REDIS_API redisCachedCluster *cluster_cache_load(HashTable *ht_seeds); /* * Redis Cluster response handlers. Our response handlers generally take the diff --git a/redis_cluster.c b/redis_cluster.c index 859e141491..429f771c06 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -33,7 +33,6 @@ #include zend_class_entry *redis_cluster_ce; -int le_cluster_slot_cache; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; @@ -343,85 +342,6 @@ void free_cluster_context(zend_object *object) { zend_object_std_dtor(&cluster->std); } -/* Turn a seed array into a zend_string we can use to look up a slot cache */ -static zend_string *cluster_hash_seeds(HashTable *ht) { - smart_str hash = {0}; - zend_string *zstr; - zval *z_seed; - - ZEND_HASH_FOREACH_VAL(ht, z_seed) { - zstr = zval_get_string(z_seed); - smart_str_appendc(&hash, '['); - smart_str_appendl(&hash, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - smart_str_appendc(&hash, ']'); - zend_string_release(zstr); - } ZEND_HASH_FOREACH_END(); - - /* Not strictly needed but null terminate anyway */ - smart_str_0(&hash); - - /* smart_str is a zend_string internally */ - return hash.s; -} - -#define SLOT_CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1) -static redisCachedCluster *cluster_cache_load(HashTable *ht_seeds) { - zend_resource *le; - zend_string *h; - - /* Short circuit if we're not caching slots or if our seeds don't have any - * elements, since it doesn't make sense to cache an empty string */ - if (!SLOT_CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) - return NULL; - - /* Look for cached slot information */ - h = cluster_hash_seeds(ht_seeds); - le = zend_hash_str_find_ptr(&EG(persistent_list), ZSTR_VAL(h), ZSTR_LEN(h)); - zend_string_release(h); - - if (le != NULL) { - /* Sanity check on our list type */ - if (le->type != le_cluster_slot_cache) { - php_error_docref(0, E_WARNING, "Invalid slot cache resource"); - return NULL; - } - - /* Success, return the cached entry */ - return le->ptr; - } - - /* Not found */ - return NULL; -} - -/* Cache a cluster's slot information in persistent_list if it's enabled */ -static int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes) { - redisCachedCluster *cc; - zend_string *hash; - - /* Short circuit if caching is disabled or there aren't any seeds */ - if (!SLOT_CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0) - return !SLOT_CACHING_ENABLED() ? SUCCESS : FAILURE; - - /* Construct our cache */ - hash = cluster_hash_seeds(ht_seeds); - cc = cluster_cache_create(hash, nodes); - zend_string_release(hash); - - /* Set up our resource */ -#if PHP_VERSION_ID < 70300 - zend_resource le; - le.type = le_cluster_slot_cache; - le.ptr = cc; - - zend_hash_update_mem(&EG(persistent_list), cc->hash, (void*)&le, sizeof(zend_resource)); -#else - zend_register_persistent_resource_ex(cc->hash, cc, le_cluster_slot_cache); -#endif - - return SUCCESS; -} - /* Validate redis cluster construction arguments */ static int cluster_validate_args(double timeout, double read_timeout, HashTable *seeds) { diff --git a/redis_session.c b/redis_session.c index 6bb94c603f..3f8d039ec6 100644 --- a/redis_session.c +++ b/redis_session.c @@ -959,10 +959,20 @@ PS_OPEN_FUNC(rediscluster) { if (auth && auth_len > 0) { c->auth = zend_string_init(auth, auth_len, 0); } - if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c)) { + + redisCachedCluster *cc; + + /* Attempt to load from cache */ + if ((cc = cluster_cache_load(ht_seeds))) { + cluster_init_cache(c, cc); /* Set up our prefix */ c->flags->prefix = zend_string_init(prefix, prefix_len, 0); - + PS_SET_MOD_DATA(c); + retval = SUCCESS; + } else if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c)) { + /* Set up our prefix */ + c->flags->prefix = zend_string_init(prefix, prefix_len, 0); + cluster_cache_store(ht_seeds, c->nodes); PS_SET_MOD_DATA(c); retval = SUCCESS; } else { From 99ebd0cc4f949de97fde97939539b23fecd214a5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Nov 2019 22:02:37 +0200 Subject: [PATCH 1277/1986] Issue #1668 Add helper function to check liveness of connection after getting it from the pool. Send `AUTH` before `PING` if necessary in pipeline. --- library.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/library.c b/library.c index 98ec4ad32f..90a40fe74d 100644 --- a/library.c +++ b/library.c @@ -118,8 +118,7 @@ redis_sock_auth(RedisSock *redis_sock) char *cmd, *response; int cmd_len, response_len; - cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "s", - ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); + cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "S", redis_sock->auth); if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); @@ -1798,6 +1797,50 @@ redis_sock_create(char *host, int host_len, int port, return redis_sock; } +static int +redis_sock_check_liveness(RedisSock *redis_sock) +{ + char inbuf[4096], uniqid[32], *response; + int uniqid_len, response_len; + smart_string cmd = {0}; + struct timeval tv; + size_t len; + + if (redis_sock->auth) { + redis_cmd_init_sstr(&cmd, 1, "AUTH", sizeof("AUTH") - 1); + redis_cmd_append_sstr(&cmd, ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); + } + gettimeofday(&tv, NULL); + uniqid_len = sprintf(uniqid, "%08lx%05lx", tv.tv_sec, tv.tv_usec); + redis_cmd_init_sstr(&cmd, 1, "PING", sizeof("PING") - 1); + redis_cmd_append_sstr(&cmd, uniqid, uniqid_len); + smart_string_0(&cmd); + + if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) { + smart_string_free(&cmd); + return FAILURE; + } + smart_string_free(&cmd); + + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { + return FAILURE; + } else if (redis_sock->auth) { + if (strncmp(inbuf, "+OK", 3) != 0) { + return FAILURE; + } else if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { + return FAILURE; + } + } + if (*inbuf != TYPE_BULK || + atoi(inbuf + 1) != uniqid_len || + redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || + strncmp(inbuf, uniqid, uniqid_len) != 0 + ) { + return FAILURE; + } + return SUCCESS; +} + /** * redis_sock_connect */ @@ -1843,8 +1886,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) if (zend_llist_count(&p->list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list); zend_llist_remove_tail(&p->list); - /* Check socket liveness using 0 second timeout */ - if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { + + if (redis_sock_check_liveness(redis_sock) == SUCCESS) { redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; return SUCCESS; } From 3243f426180673b851347ea5cdaacb75f017707b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Dec 2019 11:52:59 -0800 Subject: [PATCH 1278/1986] Switch to snprintf and modify challenge string * It should be impossible to cause a buffer overrun with this format string but use the safer version anyway. * Make the phpredis challenge string searchable and add 32 bits of entropy since it's theoretically possible that two machines would generate the same `tv_sec` + `tv_usec` string. --- library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index 90a40fe74d..f7e0c6c145 100644 --- a/library.c +++ b/library.c @@ -1800,8 +1800,8 @@ redis_sock_create(char *host, int host_len, int port, static int redis_sock_check_liveness(RedisSock *redis_sock) { - char inbuf[4096], uniqid[32], *response; - int uniqid_len, response_len; + char inbuf[4096], uniqid[64]; + int uniqid_len; smart_string cmd = {0}; struct timeval tv; size_t len; @@ -1811,7 +1811,7 @@ redis_sock_check_liveness(RedisSock *redis_sock) redis_cmd_append_sstr(&cmd, ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); } gettimeofday(&tv, NULL); - uniqid_len = sprintf(uniqid, "%08lx%05lx", tv.tv_sec, tv.tv_usec); + uniqid_len = snprintf(uniqid, sizeof(uniqid), "phpredis_pool:%08lx%05lx:%08" PRIx32, (long)tv.tv_sec, (long)tv.tv_usec, php_mt_rand()); redis_cmd_init_sstr(&cmd, 1, "PING", sizeof("PING") - 1); redis_cmd_append_sstr(&cmd, uniqid, uniqid_len); smart_string_0(&cmd); From 7b6072e05f49f5165765f81152bf685dc4888065 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Nov 2019 22:02:37 +0200 Subject: [PATCH 1279/1986] Issue #1668 Add helper function to check liveness of connection after getting it from the pool. Send `AUTH` before `PING` if necessary in pipeline. --- library.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/library.c b/library.c index 98ec4ad32f..90a40fe74d 100644 --- a/library.c +++ b/library.c @@ -118,8 +118,7 @@ redis_sock_auth(RedisSock *redis_sock) char *cmd, *response; int cmd_len, response_len; - cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "s", - ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); + cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "S", redis_sock->auth); if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { efree(cmd); @@ -1798,6 +1797,50 @@ redis_sock_create(char *host, int host_len, int port, return redis_sock; } +static int +redis_sock_check_liveness(RedisSock *redis_sock) +{ + char inbuf[4096], uniqid[32], *response; + int uniqid_len, response_len; + smart_string cmd = {0}; + struct timeval tv; + size_t len; + + if (redis_sock->auth) { + redis_cmd_init_sstr(&cmd, 1, "AUTH", sizeof("AUTH") - 1); + redis_cmd_append_sstr(&cmd, ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); + } + gettimeofday(&tv, NULL); + uniqid_len = sprintf(uniqid, "%08lx%05lx", tv.tv_sec, tv.tv_usec); + redis_cmd_init_sstr(&cmd, 1, "PING", sizeof("PING") - 1); + redis_cmd_append_sstr(&cmd, uniqid, uniqid_len); + smart_string_0(&cmd); + + if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) { + smart_string_free(&cmd); + return FAILURE; + } + smart_string_free(&cmd); + + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { + return FAILURE; + } else if (redis_sock->auth) { + if (strncmp(inbuf, "+OK", 3) != 0) { + return FAILURE; + } else if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { + return FAILURE; + } + } + if (*inbuf != TYPE_BULK || + atoi(inbuf + 1) != uniqid_len || + redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || + strncmp(inbuf, uniqid, uniqid_len) != 0 + ) { + return FAILURE; + } + return SUCCESS; +} + /** * redis_sock_connect */ @@ -1843,8 +1886,8 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) if (zend_llist_count(&p->list) > 0) { redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list); zend_llist_remove_tail(&p->list); - /* Check socket liveness using 0 second timeout */ - if (php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) == PHP_STREAM_OPTION_RETURN_OK) { + + if (redis_sock_check_liveness(redis_sock) == SUCCESS) { redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; return SUCCESS; } From 25cdaee62f22059cbfb8998b08bc8f22e4ad78c8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 4 Dec 2019 11:52:59 -0800 Subject: [PATCH 1280/1986] Switch to snprintf and modify challenge string * It should be impossible to cause a buffer overrun with this format string but use the safer version anyway. * Make the phpredis challenge string searchable and add 32 bits of entropy since it's theoretically possible that two machines would generate the same `tv_sec` + `tv_usec` string. --- library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index 90a40fe74d..f7e0c6c145 100644 --- a/library.c +++ b/library.c @@ -1800,8 +1800,8 @@ redis_sock_create(char *host, int host_len, int port, static int redis_sock_check_liveness(RedisSock *redis_sock) { - char inbuf[4096], uniqid[32], *response; - int uniqid_len, response_len; + char inbuf[4096], uniqid[64]; + int uniqid_len; smart_string cmd = {0}; struct timeval tv; size_t len; @@ -1811,7 +1811,7 @@ redis_sock_check_liveness(RedisSock *redis_sock) redis_cmd_append_sstr(&cmd, ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth)); } gettimeofday(&tv, NULL); - uniqid_len = sprintf(uniqid, "%08lx%05lx", tv.tv_sec, tv.tv_usec); + uniqid_len = snprintf(uniqid, sizeof(uniqid), "phpredis_pool:%08lx%05lx:%08" PRIx32, (long)tv.tv_sec, (long)tv.tv_usec, php_mt_rand()); redis_cmd_init_sstr(&cmd, 1, "PING", sizeof("PING") - 1); redis_cmd_append_sstr(&cmd, uniqid, uniqid_len); smart_string_0(&cmd); From 5eb4f45c84eadeee584775481a5bc52070ba81a6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 5 Dec 2019 16:38:17 +0200 Subject: [PATCH 1281/1986] Update changelog --- Changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.md b/Changelog.md index 090f537144..be1d6a0220 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,13 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Changed + +- Use PING to check liveness of connection + [99ebd0cc](https://github.com/phpredis/phpredis/commit/99ebd0cc), + [3243f426](https://github.com/phpredis/phpredis/commit/3243f426) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) + --- ## [5.1.1] - 2019-11-11 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.1.1), [PECL](https://pecl.php.net/package/redis/5.1.1)) From a5f95925ccd79963025e8b2d0673199822e6b3fc Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 7 Dec 2019 21:47:37 +0200 Subject: [PATCH 1282/1986] Change PING to ECHO to be compatible with old versions of Redis. --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index f7e0c6c145..d4eb665541 100644 --- a/library.c +++ b/library.c @@ -1812,7 +1812,7 @@ redis_sock_check_liveness(RedisSock *redis_sock) } gettimeofday(&tv, NULL); uniqid_len = snprintf(uniqid, sizeof(uniqid), "phpredis_pool:%08lx%05lx:%08" PRIx32, (long)tv.tv_sec, (long)tv.tv_usec, php_mt_rand()); - redis_cmd_init_sstr(&cmd, 1, "PING", sizeof("PING") - 1); + redis_cmd_init_sstr(&cmd, 1, "ECHO", sizeof("ECHO") - 1); redis_cmd_append_sstr(&cmd, uniqid, uniqid_len); smart_string_0(&cmd); From 3d83157699947df2038954aca4c56e358da959ce Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 7 Dec 2019 21:54:45 +0200 Subject: [PATCH 1283/1986] Update changelog --- Changelog.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index be1d6a0220..4659a1a58e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,10 +9,14 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed -- Use PING to check liveness of connection +- Use ECHO to check liveness of connection [99ebd0cc](https://github.com/phpredis/phpredis/commit/99ebd0cc), - [3243f426](https://github.com/phpredis/phpredis/commit/3243f426) + [3243f426](https://github.com/phpredis/phpredis/commit/3243f426), + [a5f95925](https://github.com/phpredis/phpredis/commit/a5f95925) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder)) +- Enable slot caching for session cluster + [23b1a9d8](https://github.com/phpredis/phpredis/commit/23b1a9d84) + ([Michael03](https://github.com/Michael03)) --- From db446138c533dfd66df729b92d610f84514f3828 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 8 Dec 2019 11:40:46 +0200 Subject: [PATCH 1284/1986] TravisCI: PHP-7.4 --- .travis.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 237a50200c..4078165fe6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,14 +5,14 @@ php: - 7.1 - 7.2 - 7.3 - - 7.4snapshot + - 7.4 - nightly env: CC=gcc matrix: allow_failures: - php: 7.3 env: CC=clang - - php: 7.4snapshot + - php: 7.4 env: CC=clang - php: nightly include: @@ -24,7 +24,7 @@ matrix: env: CC=clang - php: 7.3 env: CC=clang - - php: 7.4snapshot + - php: 7.4 env: CC=clang addons: apt: @@ -42,8 +42,7 @@ install: make install before_script: - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap - redis-server --port 0 --daemonize yes --unixsocket /tmp/redis.sock - - for PORT in $(seq 32767 32769); do redis-server --port $PORT --daemonize yes; done - - for PORT in $(seq 6379 6382); do redis-server --port $PORT --daemonize yes; done + - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes; done - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini From 2ddc5f2160af0dc8285d16196e341bf818282346 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Sun, 5 Jan 2020 21:10:22 -0500 Subject: [PATCH 1285/1986] Fix proto comments for host_port in RedisCluster These methods accept a single argument, not two arguments. (cluster_cmd_get_slot accepts one argument) --- redis_cluster.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 429f771c06..d3dc1421e9 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2596,7 +2596,7 @@ PHP_METHOD(RedisCluster, hscan) { /* }}} */ /* {{{ proto RedisCluster::save(string key) - * proto RedisCluster::save(string host, long port) */ + * proto RedisCluster::save(array host_port) */ PHP_METHOD(RedisCluster, save) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE", TYPE_LINE, cluster_bool_resp); @@ -2604,7 +2604,7 @@ PHP_METHOD(RedisCluster, save) { /* }}} */ /* {{{ proto RedisCluster::bgsave(string key) - * proto RedisCluster::bgsave(string host, long port) */ + * proto RedisCluster::bgsave(array host_port) */ PHP_METHOD(RedisCluster, bgsave) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE", TYPE_LINE, cluster_bool_resp); @@ -2628,7 +2628,7 @@ PHP_METHOD(RedisCluster, flushall) { /* }}} */ /* {{{ proto RedisCluster::dbsize(string key) - * proto RedisCluster::dbsize(string host, long port) */ + * proto RedisCluster::dbsize(array host_port) */ PHP_METHOD(RedisCluster, dbsize) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DBSIZE", TYPE_INT, cluster_long_resp); @@ -2636,7 +2636,7 @@ PHP_METHOD(RedisCluster, dbsize) { /* }}} */ /* {{{ proto RedisCluster::bgrewriteaof(string key) - * proto RedisCluster::bgrewriteaof(string host, long port) */ + * proto RedisCluster::bgrewriteaof(array host_port) */ PHP_METHOD(RedisCluster, bgrewriteaof) { cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGREWRITEAOF", TYPE_LINE, cluster_bool_resp); From f52bd8a853bc2a211c522332aea7433f0ee033a7 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Mon, 6 Jan 2020 09:05:57 -0500 Subject: [PATCH 1286/1986] Fix typos detected by codespell --- README.markdown | 10 +++++----- arrays.markdown | 4 ++-- cluster.markdown | 4 ++-- cluster_library.c | 14 +++++++------- cluster_library.h | 2 +- library.c | 4 ++-- redis.c | 8 ++++---- redis_array_impl.c | 2 +- redis_cluster.c | 12 ++++++------ redis_commands.c | 8 ++++---- redis_session.c | 2 +- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/README.markdown b/README.markdown index e1b467e43c..2bf59d6a81 100644 --- a/README.markdown +++ b/README.markdown @@ -738,7 +738,7 @@ $redis->set('key','value', 10); // Will set the key, if it doesn't exist, with a ttl of 10 seconds $redis->set('key', 'value', ['nx', 'ex'=>10]); -// Will set a key, if it does exist, with a ttl of 1000 miliseconds +// Will set a key, if it does exist, with a ttl of 1000 milliseconds $redis->set('key', 'value', ['xx', 'px'=>1000]); ~~~ @@ -2679,7 +2679,7 @@ $redis->bzPopMax(string $key1, string $key2, ... int $timeout): array ~~~ ##### *Return value* -*ARRAY:* Either an array with the key member and score of the higest or lowest element or an empty array if the timeout was reached without an element to pop. +*ARRAY:* Either an array with the key member and score of the highest or lowest element or an empty array if the timeout was reached without an element to pop. ##### *Example* ~~~php @@ -2692,7 +2692,7 @@ $redis->bzPopMax(['zs1', 'zs2'], 5); $redis->bzPopMax('zs1', 'zs2', 5); ~~~ -**Note:** Calling these functions with an array of keys or with a variable nubmer of arguments is functionally identical. +**Note:** Calling these functions with an array of keys or with a variable number of arguments is functionally identical. ### zAdd ----- @@ -2830,7 +2830,7 @@ $redis->zPopMax(string $key, int $count): array ~~~ ##### *Return value* -*ARRAY:* Either an array with the key member and score of the higest or lowest element or an empty array if there is no element available. +*ARRAY:* Either an array with the key member and score of the highest or lowest element or an empty array if there is no element available. ##### *Example* ~~~php @@ -3807,7 +3807,7 @@ $obj_redis->xTrim($str_stream, $i_max_len [, $boo_approximate]); _**Description**_: Trim the stream length to a given maximum. If the "approximate" flag is pasesed, Redis will use your size as a hint but only trim trees in whole nodes (this is more efficient). ##### *Return value* -*long*: The number of messages trimed from the stream. +*long*: The number of messages trimmed from the stream. ##### *Example* ~~~php diff --git a/arrays.markdown b/arrays.markdown index 7171c2a261..c084f70f29 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -121,7 +121,7 @@ For instance, the keys “{user:1}:name” and “{user:1}:email” will be stor ## Custom key distribution function In order to control the distribution of keys by hand, you can provide a custom function or closure that returns the server number, which is the index in the array of servers that you created the RedisArray object with. -For instance, instanciate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. +For instance, instantiate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. ### Example
@@ -132,7 +132,7 @@ This declares that we started with 2 shards and moved to 4 then 8 shards. The nu
 
 ## Migrating keys
 
-When a node is added or removed from a ring, RedisArray instances must be instanciated with a “previous” list of nodes. A single call to `$ra->_rehash()` causes all the keys to be redistributed according to the new list of nodes. Passing a callback function to `_rehash()` makes it possible to track the progress of that operation: the function is called with a node name and a number of keys that will be examined, e.g. `_rehash(function ($host, $count){ ... });`.
+When a node is added or removed from a ring, RedisArray instances must be instantiated with a “previous” list of nodes. A single call to `$ra->_rehash()` causes all the keys to be redistributed according to the new list of nodes. Passing a callback function to `_rehash()` makes it possible to track the progress of that operation: the function is called with a node name and a number of keys that will be examined, e.g. `_rehash(function ($host, $count){ ... });`.
 
 It is possible to automate this process, by setting `'autorehash' => TRUE` in the constructor options. This will cause keys to be migrated when they need to be read from the previous array.
 
diff --git a/cluster.markdown b/cluster.markdown
index 156570d2c3..cbb1cdd72a 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -122,7 +122,7 @@ For all of these multiple key commands (with the exception of MGET and MSET), th
 RedisCluster has specialized processing for MGET and MSET which allows you to send any number of keys (hashing to whichever slots) without having to consider where they live.  The way this works, is that the RedisCluster class will split the command as it iterates through keys, delivering a subset of commands per each key's slot.
 
 ~~~php
-// This will be delivered in two commands.  First for all of the {hash1} keys, 
+// This will be delivered in two commands.  First for all of the {hash1} keys,
 // and then to grab 'otherkey'
 $obj_cluster->mget(Array("{hash1}key1","{hash1}key2","{hash1}key3","otherkey"));
 ~~~
@@ -183,5 +183,5 @@ The save path for cluster based session storage takes the form of a PHP GET requ
 * _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing).
 * _failover (string)_:  How phpredis should distribute session reads between master and slave nodes.
   * _none_ : phpredis will only communicate with master nodes
-  * _error_: phpredis will communicate with master nodes unless one failes, in which case an attempt will be made to read session information from a slave. 
+  * _error_: phpredis will communicate with master nodes unless one fails, in which case an attempt will be made to read session information from a slave.
 * _auth (string, empty by default)_:  The password used to authenticate with the server prior to sending commands.
diff --git a/cluster_library.c b/cluster_library.c
index 47c0c52bf3..bce78bc9b2 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -545,7 +545,7 @@ unsigned short cluster_hash_key(const char *key, int len) {
     // Hash the whole key if we don't find a tailing } or if {} is empty
     if (e == len || e == s+1) return crc16(key, len) & REDIS_CLUSTER_MOD;
 
-    // Hash just the bit betweeen { and }
+    // Hash just the bit between { and }
     return crc16((char*)key+s+1,e-s-1) & REDIS_CLUSTER_MOD;
 }
 
@@ -945,7 +945,7 @@ redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes) {
     return cc;
 }
 
-/* Takes our input hash table and returns a straigt C array with elements,
+/* Takes our input hash table and returns a straight C array with elements,
  * which have been randomized.  The return value needs to be freed. */
 static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) {
     zval **z_seeds, *z_ele;
@@ -1256,7 +1256,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type
         return -1;
     }
 
-    // For replies that will give us a numberic length, convert it
+    // For replies that will give us a numeric length, convert it
     if (*reply_type != TYPE_LINE) {
         c->reply_len = strtol(c->line_reply, NULL, 10);
     } else {
@@ -1593,7 +1593,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char
     msstart = mstime();
 
     /* Our main cluster request/reply loop.  This loop runs until we're able to
-     * get a valid reply from a node, hit our "request" timeout, or enounter a
+     * get a valid reply from a node, hit our "request" timeout, or encounter a
      * CLUSTERDOWN state from Redis Cluster. */
     do {
         /* Send MULTI to the socket if we're in MULTI mode but haven't yet */
@@ -2046,7 +2046,7 @@ cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
         CLUSTER_RETURN_FALSE(c);
     }
 
-    // Handle ATOMIC vs. MULTI mode in a seperate switch
+    // Handle ATOMIC vs. MULTI mode in a separate switch
     if (CLUSTER_IS_ATOMIC(c)) {
         switch(r->type) {
             case TYPE_INT:
@@ -2434,7 +2434,7 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS,
         mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL) == FAILURE;
 
     // If we had a failure, pad results with FALSE to indicate failure.  Non
-    // existant keys (e.g. for MGET will come back as NULL)
+    // existent keys (e.g. for MGET will come back as NULL)
     if (fail) {
         while (mctx->count--) {
             add_next_index_bool(mctx->z_multi, 0);
@@ -2655,7 +2655,7 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
     int line_len, key_len = 0;
     long long idx = 0;
 
-    // Our count wil need to be divisible by 2
+    // Our count will need to be divisible by 2
     if (count % 2 != 0) {
         return -1;
     }
diff --git a/cluster_library.h b/cluster_library.h
index 2f6d6d8bf2..af69ad4486 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -363,7 +363,7 @@ void cluster_multi_fini(clusterMultiCmd *mc);
 unsigned short cluster_hash_key_zval(zval *key);
 unsigned short cluster_hash_key(const char *key, int len);
 
-/* Get the current time in miliseconds */
+/* Get the current time in milliseconds */
 long long mstime(void);
 
 PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char *cmd,
diff --git a/library.c b/library.c
index d4eb665541..d83ffc0fdb 100644
--- a/library.c
+++ b/library.c
@@ -574,7 +574,7 @@ union resparg {
 };
 
 /* A printf like method to construct a Redis RESP command.  It has been extended
- * to take a few different format specifiers that are convienient to phpredis.
+ * to take a few different format specifiers that are convenient to phpredis.
  *
  * s - C string followed by length as a
  * S - Pointer to a zend_string
@@ -1274,7 +1274,7 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret
 
     /* Iterate over each message */
     for (i = 0; i < count; i++) {
-        /* Consume inner multi-bulk header, message ID itself and finaly
+        /* Consume inner multi-bulk header, message ID itself and finally
          * the multi-bulk header for field and values */
         if ((read_mbulk_header(redis_sock, &mhdr) < 0 || mhdr != 2) ||
             ((id = redis_sock_read(redis_sock, &idlen)) == NULL) ||
diff --git a/redis.c b/redis.c
index 1af8549305..491cdbfe2d 100644
--- a/redis.c
+++ b/redis.c
@@ -2347,7 +2347,7 @@ PHP_METHOD(Redis, multi)
             REDIS_ENABLE_MODE(redis_sock, PIPELINE);
         }
     } else if (multi_value == MULTI) {
-        /* Don't want to do anything if we're alredy in MULTI mode */
+        /* Don't want to do anything if we're already in MULTI mode */
         if (!IS_MULTI(redis_sock)) {
             cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", "");
             if (IS_PIPELINE(redis_sock)) {
@@ -2513,7 +2513,7 @@ redis_response_enqueued(RedisSock *redis_sock)
 }
 
 /* TODO:  Investigate/fix the odd logic going on in here.  Looks like previous abort
- *        condidtions that are now simply empty if { } { } blocks. */
+ *        conditions that are now simply empty if { } { } blocks. */
 PHP_REDIS_API int
 redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
                                            RedisSock *redis_sock, zval *z_tab,
@@ -3042,7 +3042,7 @@ PHP_METHOD(Redis, script) {
         RETURN_FALSE;
     }
 
-    /* Free our alocated arguments */
+    /* Free our allocated arguments */
     efree(z_args);
 
     // Kick off our request
@@ -3496,7 +3496,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
 
     // The iterator should be passed in as NULL for the first iteration, but we
     // can treat any NON LONG value as NULL for these purposes as we've
-    // seperated the variable anyway.
+    // separated the variable anyway.
     if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter) < 0) {
         /* Convert to long */
         convert_to_long(z_iter);
diff --git a/redis_array_impl.c b/redis_array_impl.c
index b78cca2217..f5cce83dc4 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -161,7 +161,7 @@ ra_find_name(const char *name) {
     return 0;
 }
 
-/* laod array from INI settings */
+/* load array from INI settings */
 RedisArray *ra_load_array(const char *name) {
 
     zval *z_data, z_fun, z_dist;
diff --git a/redis_cluster.c b/redis_cluster.c
index 429f771c06..47fbb02fa0 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -90,7 +90,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2)
     ZEND_ARG_INFO(0, i_count)
 ZEND_END_ARG_INFO()
 
-/* Argument infor for SCAN */
+/* Argument info for SCAN */
 ZEND_BEGIN_ARG_INFO_EX(arginfo_scan_cl, 0, 0, 2)
     ZEND_ARG_INFO(1, i_iterator)
     ZEND_ARG_INFO(0, str_node)
@@ -379,7 +379,7 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time
     c->read_timeout = read_timeout;
     c->persistent = persistent;
 
-    /* Calculate the number of miliseconds we will wait when bouncing around,
+    /* Calculate the number of milliseconds we will wait when bouncing around,
      * (e.g. a node goes down), which is not the same as a standard timeout. */
     c->waitms = (long)(timeout * 1000);
 
@@ -679,7 +679,7 @@ static HashTable *method_args_to_ht(zval *z_args, int argc) {
     return ht_ret;
 }
 
-/* Convienience handler for commands that take multiple keys such as
+/* Convenience handler for commands that take multiple keys such as
  * MGET, DEL, and UNLINK */
 static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
                             zval *z_ret, cluster_cb cb)
@@ -2251,7 +2251,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg)
         }
     } else {
         php_error_docref(0, E_WARNING,
-            "Direted commands musty be passed a key or [host,port] array");
+            "Directed commands must be passed a key or [host,port] array");
         return -1;
     }
 
@@ -2959,7 +2959,7 @@ PHP_METHOD(RedisCluster, ping) {
     /* Send it off */
     rtype = CLUSTER_IS_ATOMIC(c) && arg != NULL ? TYPE_BULK : TYPE_LINE;
     if (cluster_send_slot(c, slot, cmd, cmdlen, rtype) < 0) {
-        CLUSTER_THROW_EXCEPTION("Unable to send commnad at the specificed node", 0);
+        CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0);
         efree(cmd);
         RETURN_FALSE;
     }
@@ -3081,7 +3081,7 @@ PHP_METHOD(RedisCluster, echo) {
     /* Send it off */
     rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE;
     if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) {
-        CLUSTER_THROW_EXCEPTION("Unable to send commnad at the specificed node", 0);
+        CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0);
         efree(cmd);
         RETURN_FALSE;
     }
diff --git a/redis_commands.c b/redis_commands.c
index 811cc13b9c..144f27ce53 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2176,7 +2176,7 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     if (src_free) efree(src);
     if (dst_free) efree(dst);
 
-    // Succcess!
+    // Success!
     return SUCCESS;
 }
 
@@ -3867,8 +3867,8 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
 /*
  * Redis commands that don't deal with the server at all.  The RedisSock*
- * pointer is the only thing retreived differently, so we just take that
- * in additon to the standard INTERNAL_FUNCTION_PARAMETERS for arg parsing,
+ * pointer is the only thing retrieved differently, so we just take that
+ * in addition to the standard INTERNAL_FUNCTION_PARAMETERS for arg parsing,
  * return value handling, and thread safety. */
 
 void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS,
@@ -4087,7 +4087,7 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS,
     }
     zval zv, *z_ret = &zv;
     if (!redis_unserialize(redis_sock, value, value_len, z_ret)) {
-        // Badly formed input, throw an execption
+        // Badly formed input, throw an exception
         zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0);
         RETURN_FALSE;
     }
diff --git a/redis_session.c b/redis_session.c
index 3f8d039ec6..dae35c148d 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -846,7 +846,7 @@ static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_le
     }
 }
 
-/* Simple helper to retreive a boolean (0 or 1) value from a string stored in our
+/* Simple helper to retrieve a boolean (0 or 1) value from a string stored in our
  * session.save_path variable.  This is so the user can use 0, 1, or 'true',
  * 'false' */
 static void session_conf_bool(HashTable *ht_conf, char *key, int keylen,

From 3c48a332d219907600875b51263d0048dcf49977 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 9 Jan 2020 11:03:44 -0800
Subject: [PATCH 1287/1986] Protect session.gc_maxlifetime from integer
 overflow

---
 redis_session.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/redis_session.c b/redis_session.c
index dae35c148d..ab04c5aace 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -121,6 +121,17 @@ redis_pool_free(redis_pool *pool) {
     efree(pool);
 }
 
+/* Retreive session.gc_maxlifetime from php.ini protecting against an integer overflow */
+static int session_gc_maxlifetime() {
+    zend_long value = INI_INT("session.gc_maxlifetime");
+    if (value > INT_MAX) {
+        php_error_docref(NULL, E_NOTICE, "session.gc_maxlifetime overflows INT_MAX, truncating.");
+        return INT_MAX;
+    }
+
+    return value;
+}
+
 /* Send a command to Redis.  Returns byte count written to socket (-1 on failure) */
 static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen,
                               char **reply, int *replylen)
@@ -656,7 +667,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis)
 
     /* send EXPIRE command */
     zend_string *session = redis_session_key(redis_sock, skey, skeylen);
-    cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, INI_INT("session.gc_maxlifetime"));
+    cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, session_gc_maxlifetime());
     zend_string_release(session);
 
     if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
@@ -753,7 +764,7 @@ PS_WRITE_FUNC(redis)
     /* send SET command */
     zend_string *session = redis_session_key(redis_sock, skey, skeylen);
 
-    cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, INI_INT("session.gc_maxlifetime"), sval, svallen);
+    cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, session_gc_maxlifetime(), sval, svallen);
     zend_string_release(session);
 
     if (!write_allowed(redis_sock, &pool->lock_status) || redis_sock_write(redis_sock, cmd, cmd_len ) < 0) {
@@ -1046,7 +1057,7 @@ PS_WRITE_FUNC(rediscluster) {
     /* Set up command and slot info */
     skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot);
     cmdlen = redis_spprintf(NULL, NULL, &cmd, "SETEX", "sds", skey,
-                            skeylen, INI_INT("session.gc_maxlifetime"),
+                            skeylen, session_gc_maxlifetime(),
                             ZSTR_VAL(val), ZSTR_LEN(val));
     efree(skey);
 

From 7a79ad9c27b1152a199fa411870767625bbf53a5 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 9 Jan 2020 11:52:06 -0800
Subject: [PATCH 1288/1986] Also protect against session.gc_maxlifetime <= 0

Addresses #1694
---
 redis_session.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/redis_session.c b/redis_session.c
index ab04c5aace..84b88b8657 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -127,6 +127,9 @@ static int session_gc_maxlifetime() {
     if (value > INT_MAX) {
         php_error_docref(NULL, E_NOTICE, "session.gc_maxlifetime overflows INT_MAX, truncating.");
         return INT_MAX;
+    } else if (value <= 0) {
+        php_error_docref(NULL, E_NOTICE, "session.gc_maxlifetime is <= 0, defaulting to 1440 seconds");
+        return 1440;
     }
 
     return value;

From 0ef488fca7858fc99c0d0cbe70e85281fe33c61f Mon Sep 17 00:00:00 2001
From: Tyson Andre 
Date: Tue, 7 Jan 2020 21:05:50 -0500
Subject: [PATCH 1289/1986] Remove "PHP Version 5" section

package.xml has a minimum version of 7.0
PHP 8.0 will probably be out in around a year.
---
 php_redis.h        | 6 ++----
 redis.c            | 2 --
 redis_array.c      | 2 --
 redis_array_impl.c | 4 +---
 redis_cluster.c    | 2 --
 redis_commands.c   | 2 --
 redis_session.c    | 4 +---
 7 files changed, 4 insertions(+), 18 deletions(-)

diff --git a/php_redis.h b/php_redis.h
index 977c537835..c86ac2b4b0 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -1,6 +1,4 @@
 /*
-  +----------------------------------------------------------------------+
-  | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2009 The PHP Group                                |
   +----------------------------------------------------------------------+
@@ -263,7 +261,7 @@ PHP_MSHUTDOWN_FUNCTION(redis);
 PHP_MINFO_FUNCTION(redis);
 
 /* Redis response handler function callback prototype */
-typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, 
+typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent);
@@ -271,7 +269,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent);
 PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock);
 
 PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(
-    INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, 
+    INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab,
     int numElems);
 
 extern zend_module_entry redis_module_entry;
diff --git a/redis.c b/redis.c
index 491cdbfe2d..9d7e720467 100644
--- a/redis.c
+++ b/redis.c
@@ -1,7 +1,5 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /*
-  +----------------------------------------------------------------------+
-  | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2009 The PHP Group                                |
   +----------------------------------------------------------------------+
diff --git a/redis_array.c b/redis_array.c
index 3645c02f63..ca4486163e 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -1,6 +1,4 @@
 /*
-  +----------------------------------------------------------------------+
-  | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2009 The PHP Group                                |
   +----------------------------------------------------------------------+
diff --git a/redis_array_impl.c b/redis_array_impl.c
index f5cce83dc4..f76bac2a17 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -1,6 +1,4 @@
 /*
-  +----------------------------------------------------------------------+
-  | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2009 The PHP Group                                |
   +----------------------------------------------------------------------+
@@ -1196,7 +1194,7 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache,
 
     z_cb->params = z_args;
     z_cb->retval = z_ret;
-    
+
     z_cb->no_separation = 0;
     z_cb->param_count = 2;
 
diff --git a/redis_cluster.c b/redis_cluster.c
index 8702ed25a7..1029b9df41 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1,6 +1,4 @@
 /*
-  +----------------------------------------------------------------------+
-  | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2009 The PHP Group                                |
   +----------------------------------------------------------------------+
diff --git a/redis_commands.c b/redis_commands.c
index 144f27ce53..97fa0e1338 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1,7 +1,5 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /*
-  +----------------------------------------------------------------------+
-  | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2009 The PHP Group                                |
   +----------------------------------------------------------------------+
diff --git a/redis_session.c b/redis_session.c
index 84b88b8657..ead70bee1b 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -1,7 +1,5 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /*
-  +----------------------------------------------------------------------+
-  | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2009 The PHP Group                                |
   +----------------------------------------------------------------------+
@@ -587,7 +585,7 @@ PS_CREATE_SID_FUNC(redis)
 
         if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key);
         pool->lock_status.session_key = redis_session_key(redis_sock, ZSTR_VAL(sid), ZSTR_LEN(sid));
-        
+
         if (lock_acquire(redis_sock, &pool->lock_status) == SUCCESS) {
             return sid;
         }

From a107c9fc08e05c7961750be9dfaec661ca1cbefb Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 17 Jan 2020 13:29:07 -0800
Subject: [PATCH 1290/1986] Fix a couple of memory leaks in Redis/RedisCluster

Addresses #1701
---
 cluster_library.c | 2 ++
 redis_commands.c  | 7 ++++---
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index bce78bc9b2..24d3110a2c 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -2448,6 +2448,8 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS,
         } else {
             add_next_index_zval(&c->multi_resp, mctx->z_multi);
         }
+
+        efree(mctx->z_multi);
     }
 
     // Clean up this context item
diff --git a/redis_commands.c b/redis_commands.c
index 97fa0e1338..b98d0d2729 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4083,13 +4083,14 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS,
         // Just return the value that was passed to us
         RETURN_STRINGL(value, value_len);
     }
-    zval zv, *z_ret = &zv;
-    if (!redis_unserialize(redis_sock, value, value_len, z_ret)) {
+
+    zval z_ret;
+    if (!redis_unserialize(redis_sock, value, value_len, &z_ret)) {
         // Badly formed input, throw an exception
         zend_throw_exception(ex, "Invalid serialized data, or unserialization error", 0);
         RETURN_FALSE;
     }
-    RETURN_ZVAL(z_ret, 1, 0);
+    RETURN_ZVAL(&z_ret, 0, 0);
 }
 
 /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */

From d07a8df67cb66672c920aaa5131ff2bbab50a840 Mon Sep 17 00:00:00 2001
From: Paul DelRe 
Date: Thu, 2 Jan 2020 08:47:00 -0500
Subject: [PATCH 1291/1986] Update README with missing deprecation notes

---
 README.markdown | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/README.markdown b/README.markdown
index 2bf59d6a81..a9a3f75923 100644
--- a/README.markdown
+++ b/README.markdown
@@ -206,6 +206,8 @@ $redis->connect('/tmp/redis.sock'); // unix domain socket.
 $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts.
 ~~~
 
+**Note:** `open` is an alias for `connect` and will be removed in future versions of phpredis.
+
 ### pconnect, popen
 -----
 _**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`.
@@ -247,6 +249,8 @@ $redis->pconnect('127.0.0.1', 6379, 2.5, 'x'); // x is sent as persistent_id and
 $redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another connection than the four before.
 ~~~
 
+**Note:** `popen` is an alias for `pconnect` and will be removed in future versions of phpredis.
+
 ### auth
 -----
 _**Description**_: Authenticate the connection using a password.
@@ -1171,8 +1175,6 @@ $redis->get('key'); /* 'value1value2' */
 -----
 _**Description**_: Return a substring of a larger string
 
-*Note*: substr also supported but deprecated in redis.
-
 ##### *Parameters*
 *key*  
 *start*  
@@ -1188,6 +1190,8 @@ $redis->getRange('key', 0, 5); /* 'string' */
 $redis->getRange('key', -5, -1); /* 'value' */
 ~~~
 
+**Note**: `substr` is an alias for `getRange` and will be removed in future versions of phpredis.
+
 ### setRange
 -----
 _**Description**_: Changes a substring of a larger string.
@@ -2430,6 +2434,8 @@ array(3) {
 ~~~
 The order is random and corresponds to redis' own internal representation of the set structure.
 
+**Note:** `sGetMembers` is an alias for `sMembers` and will be removed in future versions of phpredis.
+
 ### sMove
 -----
 _**Description**_: Moves the specified member from the set at srcKey to the set at dstKey.
@@ -2729,9 +2735,11 @@ _**Description**_: Returns the cardinality of an ordered set.
 $redis->zAdd('key', 0, 'val0');
 $redis->zAdd('key', 2, 'val2');
 $redis->zAdd('key', 10, 'val10');
-$redis->zSize('key'); /* 3 */
+$redis->zCard('key'); /* 3 */
 ~~~
 
+**Note**: `zSize` is an alias for `zCard` and will be removed in future versions of phpredis.
+
 ### zCount
 -----
 _**Description**_: Returns the *number* of elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits.

From c3d83d44603b9ff46e60cd5add278ca47366124f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 21 Jan 2020 10:07:00 -0800
Subject: [PATCH 1292/1986] Create a specific exception for 'test skipped'

I our test suite we were only checking if an exception was an instance
of `RedisException` and marking the test 'SKIPPED' if not.  This was
masking a failure in the RedisCluster test for testMultiExec by showing
it as skipped when it was actually throwing an exception (not being able
to execute the MULTI across the cluster).
---
 tests/RedisTest.php |  2 +-
 tests/TestSuite.php | 12 ++++++++----
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 40523b8368..d6f3320aac 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2876,7 +2876,7 @@ protected function sequence($mode) {
         $ret = $this->redis->multi($mode)
             ->ttl('key')
             ->mget(['{key}1', '{key}2', '{key}3'])
-            ->mset(['{key}3' => 'value3', 'key4' => 'value4'])
+            ->mset(['{key}3' => 'value3', '{key}4' => 'value4'])
             ->set('key', 'value')
             ->expire('key', 5)
             ->ttl('key')
diff --git a/tests/TestSuite.php b/tests/TestSuite.php
index 78fc87290e..961ce191bc 100644
--- a/tests/TestSuite.php
+++ b/tests/TestSuite.php
@@ -1,5 +1,8 @@
 getMessage()."' ($name)\n";
                     $str_msg = self::make_fail('FAILED');
-                } else {
-                    $str_msg = self::make_warning('SKIPPED');
                 }
             }
 

From ba73fbee747107ce9aaea44abd33dfa36a5f63ce Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 16 Jan 2020 18:21:58 -0800
Subject: [PATCH 1293/1986] Initial commit of ASK redirection fix

See #1693
---
 cluster_library.c | 21 ++++++++++++---------
 cluster_library.h |  2 +-
 2 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 24d3110a2c..c8c103fa42 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -1206,8 +1206,7 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved)
  *
  * This function will return -1 on a critical error (e.g. parse/communication
  * error, 0 if no redirection was encountered, and 1 if the data was moved. */
-static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type
-                                 )
+static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type)
 {
     size_t sz;
 
@@ -1232,15 +1231,17 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type
         }
 
         // Check for MOVED or ASK redirection
-        if ((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) { // Set our redirection information
-            /* We'll want to invalidate slot cache if we're using one */
-            c->redirections++;
+        if ((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) {
+            /* The Redis Cluster specification suggests clients do not update
+             * their slot mapping for an ASK redirection, only for MOVED */
+            if (moved) c->redirections++;
 
+            /* Make sure we can parse the redirection host and port */
             if (cluster_set_redirection(c,inbuf,moved) < 0) {
                 return -1;
             }
 
-            // Data moved
+            /* We've been redirected */
             return 1;
         } else {
             // Capture the error string Redis returned
@@ -1380,8 +1381,7 @@ static int cluster_sock_write(redisCluster *c, const char *cmd, size_t sz,
     /* If in ASK redirection, get/create the node for that host:port, otherwise
      * just use the command socket. */
     if (c->redir_type == REDIR_ASK) {
-        redis_sock = cluster_get_asking_sock(c);
-        if (cluster_send_asking(redis_sock) < 0) {
+        if (cluster_send_asking(c->cmd_sock) < 0) {
             return -1;
         }
     }
@@ -1627,10 +1627,13 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char
                return -1;
            }
 
-           /* Update mapping if the data has MOVED */
            if (c->redir_type == REDIR_MOVED) {
+               /* For MOVED redirection we want to update our cached mapping */
                cluster_update_slot(c);
                c->cmd_sock = SLOT_SOCK(c, slot);
+           } else if (c->redir_type == REDIR_ASK) {
+               /* For ASK redirection we want to redirect but not update slot mapping */
+               c->cmd_sock = cluster_get_asking_sock(c);
            }
         }
 
diff --git a/cluster_library.h b/cluster_library.h
index af69ad4486..784087ebbb 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -26,7 +26,7 @@
 /* MOVED/ASK comparison macros */
 #define IS_MOVED(p) (p[0]=='M' && p[1]=='O' && p[2]=='V' && p[3]=='E' && \
                      p[4]=='D' && p[5]==' ')
-#define IS_ASK(p)   (p[0]=='A' && p[1]=='S' && p[3]=='K' && p[4]==' ')
+#define IS_ASK(p)   (p[0]=='A' && p[1]=='S' && p[2]=='K' && p[3]==' ')
 
 /* MOVED/ASK lengths */
 #define MOVED_LEN (sizeof("MOVED ")-1)

From 8eb39a260c514030f47cf2d3be7a0609dbe18b97 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 9 Oct 2019 23:56:14 +0300
Subject: [PATCH 1294/1986] Issue #1646

Add TYPE param to SCAN command.
Arginfo wasn't updated so this change isn't breaking change :)
---
 redis.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/redis.c b/redis.c
index 9d7e720467..0b7453895f 100644
--- a/redis.c
+++ b/redis.c
@@ -3399,7 +3399,8 @@ PHP_METHOD(Redis, command) {
 /* Helper to format any combination of SCAN arguments */
 PHP_REDIS_API int
 redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
-                     int iter, char *pattern, int pattern_len, int count)
+                     int iter, char *pattern, int pattern_len, int count,
+                     zend_string *match_type)
 {
     smart_string cmdstr = {0};
     char *keyword;
@@ -3407,7 +3408,7 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
 
     /* Count our arguments +1 for key if it's got one, and + 2 for pattern */
     /* or count given that they each carry keywords with them. */
-    argc = 1 + (key_len > 0) + (pattern_len > 0 ? 2 : 0) + (count > 0 ? 2 : 0);
+    argc = 1 + (key_len > 0) + (pattern_len > 0 ? 2 : 0) + (count > 0 ? 2 : 0) + (match_type ? 2 : 0);
 
     /* Turn our type into a keyword */
     switch(type) {
@@ -3443,12 +3444,17 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
         redis_cmd_append_sstr(&cmdstr, pattern, pattern_len);
     }
 
+    if (match_type) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TYPE");
+        redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(match_type), ZSTR_LEN(match_type));
+    }
+
     /* Return our command length */
     *cmd = cmdstr.c;
     return cmdstr.len;
 }
 
-/* {{{ proto redis::scan(&$iterator, [pattern, [count]]) */
+/* {{{ proto redis::scan(&$iterator, [pattern, [count, [type]]]) */
 PHP_REDIS_API void
 generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
     zval *object, *z_iter;
@@ -3457,6 +3463,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
     char *pattern = NULL, *cmd, *key = NULL;
     int cmd_len, num_elements, key_free = 0;
     size_t key_len = 0, pattern_len = 0;
+    zend_string *match_type = NULL;
     zend_long count = 0, iter;
 
     /* Different prototype depending on if this is a key based scan */
@@ -3472,8 +3479,8 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
     } else {
         // Doesn't require a key
         if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                        "Oz/|s!l", &object, redis_ce, &z_iter,
-                                        &pattern, &pattern_len, &count)
+                                        "Oz/|s!lS", &object, redis_ce, &z_iter,
+                                        &pattern, &pattern_len, &count, &match_type)
                                         == FAILURE)
         {
             RETURN_FALSE;
@@ -3530,7 +3537,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
 
         // Format our SCAN command
         cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter,
-                                   pattern, pattern_len, count);
+                                   pattern, pattern_len, count, match_type);
 
         /* Execute our command getting our new iterator value */
         REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);

From 544e641bf27ac62de65544ecd94672fca42ffbed Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 9 Oct 2019 19:33:35 -0700
Subject: [PATCH 1295/1986] Adds a test for #1646

NOTE:  [This
comment](https://github.com/antirez/redis/pull/6116#issuecomment-509331565)
indicates the feature may be backported to Redis 5, so we'll want to
change our unit test if that happens.
---
 tests/RedisTest.php | 54 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 50 insertions(+), 4 deletions(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index d6f3320aac..fb78bc3f2d 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4924,10 +4924,14 @@ public function testIntrospection() {
 
     protected function get_keyspace_count($str_db) {
         $arr_info = $this->redis->info();
-        $arr_info = $arr_info[$str_db];
-        $arr_info = explode(',', $arr_info);
-        $arr_info = explode('=', $arr_info[0]);
-        return $arr_info[1];
+        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() {
@@ -4962,6 +4966,48 @@ public function testScan() {
             $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", "gte") >= 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 ([NULL, 10] 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);
+                }
+            }
+
+            // Make sure only lists come back even without a pattern or count
+            $it = NULL;
+            $arr_scan = $this->redis->scan($it, NULL, NULL, "LIST");
+            foreach ($arr_scan as $str_key) {
+                $this->assertEquals($this->redis->type($str_key), Redis::REDIS_LIST);
+            }
+        }
+
     }
 
     public function testHScan() {

From 53fb36c9d3bcb1211c06c517a5bf6d68fc5c778f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 9 Oct 2019 19:58:04 -0700
Subject: [PATCH 1296/1986] Remove additional TYPE test

---
 tests/RedisTest.php | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index fb78bc3f2d..c9e6827a0a 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4999,13 +4999,6 @@ public function testScan() {
                     $this->assertEquals($arr_vals, $arr_resp);
                 }
             }
-
-            // Make sure only lists come back even without a pattern or count
-            $it = NULL;
-            $arr_scan = $this->redis->scan($it, NULL, NULL, "LIST");
-            foreach ($arr_scan as $str_key) {
-                $this->assertEquals($this->redis->type($str_key), Redis::REDIS_LIST);
-            }
         }
 
     }

From b1724b84828eac58c4d3e29b190bd49dc55c209a Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 9 Oct 2019 20:08:28 -0700
Subject: [PATCH 1297/1986] Use proper version compare

---
 tests/RedisTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c9e6827a0a..bfe89f4487 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4968,7 +4968,7 @@ public function testScan() {
         $this->assertEquals(0, $i);
 
         // SCAN with type is scheduled for release in Redis 6.
-        if (version_compare($this->version, "6.0.0", "gte") >= 0) {
+        if (version_compare($this->version, "6.0.0") >= 0) {
             // Use a unique ID so we can find our type keys
             $id = uniqid();
 

From c94e28f1ebabdfceb722ad78eff75ce4fc57f5a3 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 18 Dec 2019 14:00:45 +0200
Subject: [PATCH 1298/1986] Add RedisSentinel class and tests

---
 .travis.yml                 |   3 +
 config.m4                   |   2 +-
 library.c                   |   2 +
 redis.c                     |  16 +++--
 redis_commands.c            |  24 ++++++++
 redis_commands.h            |   6 ++
 redis_sentinel.c            |  94 ++++++++++++++++++++++++++++++
 redis_sentinel.h            |  18 ++++++
 sentinel_library.c          |  62 ++++++++++++++++++++
 sentinel_library.h          |  13 +++++
 tests/RedisSentinelTest.php | 113 ++++++++++++++++++++++++++++++++++++
 tests/TestRedis.php         |  10 +++-
 12 files changed, 355 insertions(+), 8 deletions(-)
 create mode 100644 redis_sentinel.c
 create mode 100644 redis_sentinel.h
 create mode 100644 sentinel_library.c
 create mode 100644 sentinel_library.h
 create mode 100644 tests/RedisSentinelTest.php

diff --git a/.travis.yml b/.travis.yml
index 4078165fe6..675be1aa94 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -44,12 +44,15 @@ before_script:
   - redis-server --port 0 --daemonize yes --unixsocket /tmp/redis.sock
   - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes; done
   - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
+  - for PORT in $(seq 26379 26380); do wget download.redis.io/redis-stable/sentinel.conf -O $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
   - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3
   - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
 script:
   - php tests/TestRedis.php --class Redis
   - php tests/TestRedis.php --class RedisArray
   - php tests/TestRedis.php --class RedisCluster
+  - php tests/TestRedis.php --class RedisSentinel
   - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis
   - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray
   - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster
+  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel
diff --git a/config.m4 b/config.m4
index bc4dd40d31..58b2bce8de 100644
--- a/config.m4
+++ b/config.m4
@@ -267,5 +267,5 @@ if test "$PHP_REDIS" != "no"; then
   dnl
   dnl PHP_SUBST(REDIS_SHARED_LIBADD)
 
-  PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c $lzf_sources, $ext_shared)
+  PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c $lzf_sources, $ext_shared)
 fi
diff --git a/library.c b/library.c
index d83ffc0fdb..ebb9fbf427 100644
--- a/library.c
+++ b/library.c
@@ -167,6 +167,8 @@ redis_error_throw(RedisSock *redis_sock)
      * Disque) */
     if (!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOSCRIPT") &&
+        !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOQUORUM") &&
+        !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGOODSLAVE") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "WRONGTYPE") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "BUSYGROUP") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGROUP"))
diff --git a/redis.c b/redis.c
index 0b7453895f..339328f176 100644
--- a/redis.c
+++ b/redis.c
@@ -22,16 +22,16 @@
 #include "config.h"
 #endif
 
-#include "common.h"
-#include "ext/standard/info.h"
 #include "php_redis.h"
-#include "redis_commands.h"
 #include "redis_array.h"
 #include "redis_cluster.h"
+#include "redis_commands.h"
+#include "redis_sentinel.h"
 #include 
+#include 
 
 #ifdef PHP_SESSION
-#include "ext/session/php_session.h"
+#include 
 #endif
 
 #include "library.h"
@@ -48,6 +48,7 @@ extern ps_module ps_mod_redis_cluster;
 extern zend_class_entry *redis_array_ce;
 extern zend_class_entry *redis_cluster_ce;
 extern zend_class_entry *redis_cluster_exception_ce;
+extern zend_class_entry *redis_sentinel_ce;
 
 zend_class_entry *redis_ce;
 zend_class_entry *redis_exception_ce;
@@ -56,6 +57,7 @@ extern int le_cluster_slot_cache;
 
 extern zend_function_entry redis_array_functions[];
 extern zend_function_entry redis_cluster_functions[];
+extern zend_function_entry redis_sentinel_functions[];
 
 int le_redis_pconnect;
 
@@ -754,6 +756,7 @@ PHP_MINIT_FUNCTION(redis)
     zend_class_entry redis_class_entry;
     zend_class_entry redis_array_class_entry;
     zend_class_entry redis_cluster_class_entry;
+    zend_class_entry redis_sentinel_class_entry;
     zend_class_entry redis_exception_class_entry;
     zend_class_entry redis_cluster_exception_class_entry;
 
@@ -780,6 +783,11 @@ PHP_MINIT_FUNCTION(redis)
     redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry);
     redis_cluster_ce->create_object = create_cluster_context;
 
+    /* RedisSentinel class */
+    INIT_CLASS_ENTRY(redis_sentinel_class_entry, "RedisSentinel", redis_sentinel_functions);
+    redis_sentinel_ce = zend_register_internal_class(&redis_sentinel_class_entry);
+    redis_sentinel_ce->create_object = create_sentinel_object;
+
     /* Register our cluster cache list item */
     le_cluster_slot_cache = zend_register_list_destructors_ex(NULL, cluster_cache_dtor,
                                                               "Redis cluster slot cache",
diff --git a/redis_commands.c b/redis_commands.c
index b98d0d2729..6945ed4b67 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3863,6 +3863,30 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
+        return FAILURE;
+    }
+    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "s", kw, strlen(kw));
+    return SUCCESS;
+}
+
+int
+redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    zend_string *name;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
+        return FAILURE;
+    }
+    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "sS", kw, strlen(kw), name);
+    return SUCCESS;
+}
+
 /*
  * Redis commands that don't deal with the server at all.  The RedisSock*
  * pointer is the only thing retrieved differently, so we just take that
diff --git a/redis_commands.h b/redis_commands.h
index af17d57ab5..addcce532f 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -289,6 +289,12 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
+
+int redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
+
 /* Commands that don't communicate with Redis at all (such as getOption, 
  * setOption, _prefix, _serialize, etc).  These can be handled in one place
  * with the method of grabbing our RedisSock* object in different ways 
diff --git a/redis_sentinel.c b/redis_sentinel.c
new file mode 100644
index 0000000000..e3590a419e
--- /dev/null
+++ b/redis_sentinel.c
@@ -0,0 +1,94 @@
+#include "php_redis.h"
+#include "redis_commands.h"
+#include "redis_sentinel.h"
+
+zend_class_entry *redis_sentinel_ce;
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
+    ZEND_ARG_INFO(0, host)
+    ZEND_ARG_INFO(0, port)
+ZEND_END_ARG_INFO()
+
+zend_function_entry redis_sentinel_functions[] = {
+     PHP_ME(RedisSentinel, __construct, arginfo_ctor, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, ckquorum, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, failover, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, flushconfig, arginfo_void, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, getMasterAddrByName, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, master, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, masters, arginfo_void, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, ping, arginfo_void, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, reset, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, sentinels, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, slaves, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_FE_END
+};
+
+PHP_METHOD(RedisSentinel, __construct)
+{
+    redis_sentinel_object *obj;
+    zend_long port = -1;
+    zend_string *host;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &host, &port) == FAILURE) {
+        RETURN_FALSE;
+    }
+
+    /* If it's not a unix socket, set to default */
+    if (port < 0 && ZSTR_LEN(host) > 0 && *ZSTR_VAL(host) != '/') {
+        port = 26379;
+    }
+
+    obj = PHPREDIS_GET_OBJECT(redis_sentinel_object, getThis());
+    obj->sock = redis_sock_create(ZSTR_VAL(host), ZSTR_LEN(host), port, 0, 0, 0, NULL, 0);
+}
+
+PHP_METHOD(RedisSentinel, ckquorum)
+{
+    REDIS_PROCESS_KW_CMD("ckquorum", redis_sentinel_str_cmd, redis_boolean_response);
+}
+
+PHP_METHOD(RedisSentinel, failover)
+{
+    REDIS_PROCESS_KW_CMD("failover", redis_sentinel_str_cmd, redis_boolean_response);
+}
+
+PHP_METHOD(RedisSentinel, flushconfig)
+{
+    REDIS_PROCESS_KW_CMD("flushconfig", redis_sentinel_cmd, redis_boolean_response);
+}
+
+PHP_METHOD(RedisSentinel, getMasterAddrByName)
+{
+    REDIS_PROCESS_KW_CMD("get-master-addr-by-name", redis_sentinel_str_cmd, redis_mbulk_reply_raw);
+}
+
+PHP_METHOD(RedisSentinel, master)
+{
+    REDIS_PROCESS_KW_CMD("master", redis_sentinel_str_cmd, redis_mbulk_reply_zipped_raw);
+}
+
+PHP_METHOD(RedisSentinel, masters)
+{
+    REDIS_PROCESS_KW_CMD("masters", redis_sentinel_cmd, sentinel_mbulk_reply_zipped_assoc);
+}
+
+PHP_METHOD(RedisSentinel, ping)
+{
+    REDIS_PROCESS_KW_CMD("PING", redis_empty_cmd, redis_boolean_response);
+}
+
+PHP_METHOD(RedisSentinel, reset)
+{
+    REDIS_PROCESS_KW_CMD("reset", redis_sentinel_str_cmd, redis_boolean_response);
+}
+
+PHP_METHOD(RedisSentinel, sentinels)
+{
+    REDIS_PROCESS_KW_CMD("sentinels", redis_sentinel_str_cmd, sentinel_mbulk_reply_zipped_assoc);
+}
+
+PHP_METHOD(RedisSentinel, slaves)
+{
+    REDIS_PROCESS_KW_CMD("slaves", redis_sentinel_str_cmd, sentinel_mbulk_reply_zipped_assoc);
+}
diff --git a/redis_sentinel.h b/redis_sentinel.h
new file mode 100644
index 0000000000..986a63ba0d
--- /dev/null
+++ b/redis_sentinel.h
@@ -0,0 +1,18 @@
+#ifndef REDIS_SENTINEL_H
+#define REDIS_SENTINEL_H
+
+#include "sentinel_library.h"
+
+PHP_METHOD(RedisSentinel, __construct);
+PHP_METHOD(RedisSentinel, ckquorum);
+PHP_METHOD(RedisSentinel, failover);
+PHP_METHOD(RedisSentinel, flushconfig);
+PHP_METHOD(RedisSentinel, getMasterAddrByName);
+PHP_METHOD(RedisSentinel, master);
+PHP_METHOD(RedisSentinel, masters);
+PHP_METHOD(RedisSentinel, ping);
+PHP_METHOD(RedisSentinel, reset);
+PHP_METHOD(RedisSentinel, sentinels);
+PHP_METHOD(RedisSentinel, slaves);
+
+#endif /* REDIS_SENTINEL_H */
diff --git a/sentinel_library.c b/sentinel_library.c
new file mode 100644
index 0000000000..bff5725c33
--- /dev/null
+++ b/sentinel_library.c
@@ -0,0 +1,62 @@
+#include "sentinel_library.h"
+
+static zend_object_handlers redis_sentinel_object_handlers;
+
+static void
+free_redis_sentinel_object(zend_object *object)
+{
+    redis_sentinel_object *obj = (redis_sentinel_object *)((char *)(object) - XtOffsetOf(redis_sentinel_object, std));
+
+    if (obj->sock) {
+        redis_sock_disconnect(obj->sock, 0);
+        redis_free_socket(obj->sock);
+    }
+    zend_object_std_dtor(&obj->std);
+}
+
+zend_object *
+create_sentinel_object(zend_class_entry *ce)
+{
+    redis_sentinel_object *obj = ecalloc(1, sizeof(*obj) + zend_object_properties_size(ce));
+
+    zend_object_std_init(&obj->std, ce);
+    object_properties_init(&obj->std, ce);
+
+    memcpy(&redis_sentinel_object_handlers, zend_get_std_object_handlers(), sizeof(redis_sentinel_object_handlers));
+    redis_sentinel_object_handlers.offset = XtOffsetOf(redis_sentinel_object, std);
+    redis_sentinel_object_handlers.free_obj = free_redis_sentinel_object;
+    obj->std.handlers = &redis_sentinel_object_handlers;
+
+    return &obj->std;
+}
+
+PHP_REDIS_API void
+sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    char inbuf[4096];
+    int i, nelem;
+    size_t len;
+    zval z_ret;
+
+    /* Throws exception on failure */
+    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
+        RETURN_FALSE;
+    }
+
+    if (*inbuf != TYPE_MULTIBULK) {
+        if (*inbuf == TYPE_ERR) {
+            redis_sock_set_err(redis_sock, inbuf + 1, len - 1);
+        }
+        RETURN_FALSE;
+    }
+    array_init(&z_ret);
+    nelem = atoi(inbuf + 1);
+    for (i = 0; i < nelem; ++i) {
+        /* redis_mbulk_reply_zipped_raw calls redis_mbulk_reply_zipped
+         * which puts result into return_value via RETVAL_ZVAL */
+        array_init(return_value);
+        redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
+        add_next_index_zval(&z_ret, return_value);
+    }
+    RETURN_ZVAL(&z_ret, 0, 1);
+}
diff --git a/sentinel_library.h b/sentinel_library.h
new file mode 100644
index 0000000000..460ccfad1d
--- /dev/null
+++ b/sentinel_library.h
@@ -0,0 +1,13 @@
+#ifndef REDIS_SENTINEL_LIBRARY_H
+#define REDIS_SENTINEL_LIBRARY_H
+
+#include "common.h"
+#include "library.h"
+
+typedef redis_object redis_sentinel_object;
+
+zend_object *create_sentinel_object(zend_class_entry *ce);
+
+PHP_REDIS_API void sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+
+#endif /* REDIS_SENTINEL_LIBRARY_H */
diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php
new file mode 100644
index 0000000000..b88e006477
--- /dev/null
+++ b/tests/RedisSentinelTest.php
@@ -0,0 +1,113 @@
+getHost());
+    }
+
+    public function setUp()
+    {
+        $this->sentinel = $this->newInstance();
+    }
+
+    public function testCkquorum()
+    {
+        $this->assertTrue($this->sentinel->ckquorum(self::NAME));
+    }
+
+    public function testFailover()
+    {
+        $this->assertFalse($this->sentinel->failover(self::NAME));
+    }
+
+    public function testFlushconfig()
+    {
+        $this->assertTrue($this->sentinel->flushconfig());
+    }
+
+    public function testGetMasterAddrByName()
+    {
+        $result = $this->sentinel->getMasterAddrByName(self::NAME);
+        $this->assertTrue(is_array($result));
+        $this->assertEquals(2, count($result));
+    }
+
+    protected function checkFields(array $fields)
+    {
+        foreach ($this->fields as $k) {
+            $this->assertTrue(array_key_exists($k, $fields));
+        }
+    }
+
+    public function testMaster()
+    {
+        $result = $this->sentinel->master(self::NAME);
+        $this->assertTrue(is_array($result));
+        $this->checkFields($result);
+    }
+
+    public function testMasters()
+    {
+        $result = $this->sentinel->masters();
+        $this->assertTrue(is_array($result));
+        foreach ($result as $master) {
+            $this->checkFields($master);
+        }
+    }
+
+    public function testPing()
+    {
+        $this->assertTrue($this->sentinel->ping());
+    }
+
+    public function testReset()
+    {
+        $this->assertFalse($this->sentinel->reset('*'));
+    }
+
+    public function testSentinels()
+    {
+        $result = $this->sentinel->sentinels(self::NAME);
+        $this->assertTrue(is_array($result));
+        foreach ($result as $sentinel) {
+            $this->checkFields($sentinel);
+        }
+    }
+
+    public function testSlaves()
+    {
+        $result = $this->sentinel->slaves(self::NAME);
+        $this->assertTrue(is_array($result));
+        foreach ($result as $slave) {
+            $this->checkFields($slave);
+        }
+    }
+}
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 9da9ab8639..1c79d702e4 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -4,6 +4,7 @@
 require_once(dirname($_SERVER['PHP_SELF'])."/RedisTest.php");
 require_once(dirname($_SERVER['PHP_SELF'])."/RedisArrayTest.php");
 require_once(dirname($_SERVER['PHP_SELF'])."/RedisClusterTest.php");
+require_once(dirname($_SERVER['PHP_SELF'])."/RedisSentinelTest.php");
 
 /* Make sure errors go to stdout and are shown */
 error_reporting(E_ALL);
@@ -13,7 +14,7 @@
 $arr_args = getopt('', ['host:', 'class:', 'test:', 'nocolors']);
 
 /* Grab the test the user is trying to run */
-$arr_valid_classes = ['redis', 'redisarray', 'rediscluster'];
+$arr_valid_classes = ['redis', 'redisarray', 'rediscluster', 'redissentinel'];
 $str_class = isset($arr_args['class']) ? strtolower($arr_args['class']) : 'redis';
 $boo_colorize = !isset($arr_args['nocolors']);
 
@@ -25,7 +26,7 @@
 
 /* Validate the class is known */
 if (!in_array($str_class, $arr_valid_classes)) {
-    echo "Error:  Valid test classes are Redis, RedisArray, and RedisCluster!\n";
+    echo "Error:  Valid test classes are Redis, RedisArray, RedisCluster and RedisSentinel!\n";
     exit(1);
 }
 
@@ -56,8 +57,11 @@
             }
         }
     }
-} else {
+} else if ($str_class == 'rediscluster') {
     echo TestSuite::make_bold("RedisCluster") . "\n";
     exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host));
+} else {
+    echo TestSuite::make_bold("RedisSentinel") . "\n";
+    exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host));
 }
 ?>

From 90cb69f37f628bec74a8dc7ebb41a5e2597b5dd5 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 19 Dec 2019 10:33:59 +0200
Subject: [PATCH 1299/1986] Add more connection parameters

---
 redis_sentinel.c | 50 ++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 44 insertions(+), 6 deletions(-)

diff --git a/redis_sentinel.c b/redis_sentinel.c
index e3590a419e..77ec0fdd5e 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -1,12 +1,18 @@
 #include "php_redis.h"
 #include "redis_commands.h"
 #include "redis_sentinel.h"
+#include 
 
 zend_class_entry *redis_sentinel_ce;
+extern zend_class_entry *redis_exception_ce;
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
     ZEND_ARG_INFO(0, host)
     ZEND_ARG_INFO(0, port)
+    ZEND_ARG_INFO(0, timeout)
+    ZEND_ARG_INFO(0, persistent)
+    ZEND_ARG_INFO(0, retry_interval)
+    ZEND_ARG_INFO(0, read_timeout)
 ZEND_END_ARG_INFO()
 
 zend_function_entry redis_sentinel_functions[] = {
@@ -26,21 +32,53 @@ zend_function_entry redis_sentinel_functions[] = {
 
 PHP_METHOD(RedisSentinel, __construct)
 {
+    int persistent = 0;
+    char *persistent_id = NULL;
+    double timeout = 0.0, read_timeout = 0.0;
+    zend_long port = 26379, retry_interval = 0;
     redis_sentinel_object *obj;
-    zend_long port = -1;
     zend_string *host;
+    zval *zv = NULL;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &host, &port) == FAILURE) {
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ldz!ld",
+                                &host, &port, &timeout, &zv,
+                                &retry_interval, &read_timeout) == FAILURE) {
         RETURN_FALSE;
     }
 
-    /* If it's not a unix socket, set to default */
-    if (port < 0 && ZSTR_LEN(host) > 0 && *ZSTR_VAL(host) != '/') {
-        port = 26379;
+    if (port < 0 || port > UINT16_MAX) {
+        REDIS_THROW_EXCEPTION("Invalid port", 0);
+        RETURN_FALSE;
+    }
+
+    if (timeout < 0L || timeout > INT_MAX) {
+        REDIS_THROW_EXCEPTION("Invalid connect timeout", 0);
+        RETURN_FALSE;
+    }
+
+    if (read_timeout < 0L || read_timeout > INT_MAX) {
+        REDIS_THROW_EXCEPTION("Invalid read timeout", 0);
+        RETURN_FALSE;
+    }
+
+    if (retry_interval < 0L || retry_interval > INT_MAX) {
+        REDIS_THROW_EXCEPTION("Invalid retry interval", 0);
+        RETURN_FALSE;
+    }
+
+    if (zv) {
+        ZVAL_DEREF(zv);
+        if (Z_TYPE_P(zv) == IS_STRING) {
+            persistent_id = Z_STRVAL_P(zv);
+            persistent = 1; /* even empty */
+        } else {
+            persistent = zval_is_true(zv);
+        }
     }
 
     obj = PHPREDIS_GET_OBJECT(redis_sentinel_object, getThis());
-    obj->sock = redis_sock_create(ZSTR_VAL(host), ZSTR_LEN(host), port, 0, 0, 0, NULL, 0);
+    obj->sock = redis_sock_create(ZSTR_VAL(host), ZSTR_LEN(host), port,
+        timeout, read_timeout, persistent, persistent_id, retry_interval);
 }
 
 PHP_METHOD(RedisSentinel, ckquorum)

From 46da22b09b31a9d2ad8a6403f9684c99cf7ee60b Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 25 Dec 2019 21:57:22 +0200
Subject: [PATCH 1300/1986] Fix memory leak and add copyright header

---
 redis_sentinel.c   | 17 +++++++++++++++++
 sentinel_library.c |  1 -
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/redis_sentinel.c b/redis_sentinel.c
index 77ec0fdd5e..f4729d7876 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -1,3 +1,20 @@
+/*
+  +----------------------------------------------------------------------+
+  | Copyright (c) The PHP Group                                          |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author: Pavlo Yatsukhnenko                   |
+  | Maintainer: Michael Grunder               |
+  +----------------------------------------------------------------------+
+*/
+
 #include "php_redis.h"
 #include "redis_commands.h"
 #include "redis_sentinel.h"
diff --git a/sentinel_library.c b/sentinel_library.c
index bff5725c33..bd81e8d877 100644
--- a/sentinel_library.c
+++ b/sentinel_library.c
@@ -54,7 +54,6 @@ sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis
     for (i = 0; i < nelem; ++i) {
         /* redis_mbulk_reply_zipped_raw calls redis_mbulk_reply_zipped
          * which puts result into return_value via RETVAL_ZVAL */
-        array_init(return_value);
         redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
         add_next_index_zval(&z_ret, return_value);
     }

From 5a609fa425bffc72668378e0b07a6aea2e234bcd Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 24 Jan 2020 17:25:05 +0200
Subject: [PATCH 1301/1986] Add documentation

---
 README.markdown   |   8 ++
 sentinel.markdown | 194 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 202 insertions(+)
 create mode 100644 sentinel.markdown

diff --git a/README.markdown b/README.markdown
index a9a3f75923..965243b761 100644
--- a/README.markdown
+++ b/README.markdown
@@ -23,6 +23,7 @@ If you've found phpredis useful and would like to buy the maintainers a coffee (
    * [PHP Session handler](#php-session-handler)
    * [Distributed Redis Array](#distributed-redis-array)
    * [Redis Cluster support](#redis-cluster-support)
+   * [Redis Sentinel support](#redis-sentinel-support)
    * [Running the unit tests](#running-the-unit-tests)
 1. [Classes and methods](#classes-and-methods)
    * [Usage](#usage)
@@ -97,6 +98,10 @@ See [dedicated page](./arrays.markdown#readme).
 
 See [dedicated page](./cluster.markdown#readme).
 
+## Redis Sentinel support
+
+See [dedicated page](./sentinel.markdown#readme).
+
 ## Running the unit tests
 
 phpredis uses a small custom unit test suite for testing functionality of the various classes.  To run tests, simply do the following:
@@ -114,6 +119,9 @@ tests/mkring.sh stop
 tests/make-cluster.sh start
 php tests/TestRedis.php --class RedisCluster
 tests/make-cluster.sh stop
+
+# Run tests for RedisSentinel class
+php tests/TestRedis.php --class RedisSentinel
 ~~~
 
 Note that it is possible to run only tests which match a substring of the test itself by passing the additional argument '--test ' when invoking.
diff --git a/sentinel.markdown b/sentinel.markdown
new file mode 100644
index 0000000000..2bd3655313
--- /dev/null
+++ b/sentinel.markdown
@@ -0,0 +1,194 @@
+Redis Sentinel
+==============
+
+Redis Sentinel provides high availability for Redis. In practical terms this means that using Sentinel you can create a Redis deployment that resists without human intervention certain kinds of failures.
+
+Redis Sentinel also provides other collateral tasks such as monitoring, notifications and acts as a configuration provider for clients.
+
+## Class RedisSentinel
+-----
+
+##### *Parameters*
+
+*host*: String, IP address or hostname  
+*port*: Int (optional, default is 26379)  
+*timeout*: Float, value in seconds (optional, default is 0 meaning unlimited)  
+*persistent*: String, persistent connection id (optional, default is NULL meaning not persistent)  
+*retry_interval*: Int, value in milliseconds (optional, default is 0)  
+*read_timeout*: Float, value in seconds (optional, default is 0 meaning unlimited)  
+
+##### *Example*
+
+~~~php
+$sentinel = new RedisSentinel('127.0.0.1'); // default parameters
+$sentinel = new RedisSentinel('127.0.0.1', 26379, 2.5); // 2.5 sec timeout.
+$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, 'sentinel'); // persistent connection with id 'sentinel'
+$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, ''); // also persistent connection with id ''
+$sentinel = new RedisSentinel('127.0.0.1', 26379, 1, null, 100); // 1 sec timeout, 100ms delay between reconnection attempts.
+~~~
+
+### Usage
+-----
+
+* [ckquorum](#ckquorum) - Check if the current Sentinel configuration is able to reach the quorum needed to failover.
+* [failover](#failover) - Force a failover as if the master was not reachable.
+* [flushconfig](#flushconfig) - Force Sentinel to rewrite its configuration on disk.
+* [getMasterAddrByName](#getMasterAddrByName) - Return the ip and port number of the master with that name.
+* [master](#master) - Return the state and info of the specified master.
+* [masters](#masters) - Return a list of monitored masters and their state.
+* [ping](#ping) - Ping the sentinel.
+* [reset](#reset) - Reset all the masters with matching name.
+* [sentinels](#sentinels) - Return a list of sentinel instances for this master, and their state.
+* [slaves](#slaves) - Return a list of replicas for this master, and their state.
+
+-----
+
+### ckquorum
+-----
+_**Description**_: Check if the current Sentinel configuration is able to reach the quorum needed to failover a master, and the majority needed to authorize the failover. This command should be used in monitoring systems to check if a Sentinel deployment is ok.
+
+##### *Parameters*
+*String*: master name
+
+##### *Return value*
+*Bool*: `TRUE` in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->ckquorum('mymaster');
+~~~
+
+### failover
+-----
+_**Description**_: Force a failover as if the master was not reachable, and without asking for agreement to other Sentinels (however a new version of the configuration will be published so that the other Sentinels will update their configurations).
+
+##### *Parameters*
+*String*: master name
+
+##### *Return value*
+*Bool*: `TRUE` in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->failover('mymaster');
+~~~
+
+### flushconfig
+-----
+_**Description**_: Force Sentinel to rewrite its configuration on disk, including the current Sentinel state. Normally Sentinel rewrites the configuration every time something changes in its state (in the context of the subset of the state which is persisted on disk across restart). However sometimes it is possible that the configuration file is lost because of operation errors, disk failures, package upgrade scripts or configuration managers. In those cases a way to to force Sentinel to rewrite the configuration file is handy. This command works even if the previous configuration file is completely missing.
+
+##### *Parameters*
+(none)
+
+##### *Return value*
+*Bool*: `TRUE` in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->flushconfig();
+~~~
+
+### getMasterAddrByName
+-----
+_**Description**_: Return the ip and port number of the master with that name. If a failover is in progress or terminated successfully for this master it returns the address and port of the promoted replica.
+
+##### *Parameters*
+*String*: master name
+
+##### *Return value*
+*Array*, *Bool*: ['address', 'port'] in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->getMasterAddrByName('mymaster');
+~~~
+
+### master
+-----
+_**Description**_: Return the state and info of the specified master.
+
+##### *Parameters*
+*String*: master name
+
+##### *Return value*
+*Array*, *Bool*: Associative array with info in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->master('mymaster');
+~~~
+
+### masters
+-----
+_**Description**_: Return a list of monitored masters and their state.
+
+##### *Parameters*
+(none)
+
+##### *Return value*
+*Array*, *Bool*: List of arrays with info for each master in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->masters();
+~~~
+
+### ping
+-----
+_**Description**_: Ping the sentinel.
+
+##### *Parameters*
+(none)
+
+##### *Return value*
+*Bool*: `TRUE` in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->ping();
+~~~
+
+### reset
+-----
+_**Description**_: This command will reset all the masters with matching name. The pattern argument is a glob-style pattern. The reset process clears any previous state in a master (including a failover in progress), and removes every replica and sentinel already discovered and associated with the master.
+
+##### *Parameters*
+*String*: pattern
+
+##### *Return value*
+*Bool*: `TRUE` in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->reset('*');
+~~~
+
+### sentinels
+-----
+_**Description**_: Return a list of sentinel instances for this master, and their state.
+
+##### *Parameters*
+*String*: master name
+
+##### *Return value*
+*Array*, *Bool*: List of arrays with info for each sentinels in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->sentinels('mymaster');
+~~~
+
+### slaves
+-----
+_**Description**_: Return a list of replicas for this master, and their state.
+
+##### *Parameters*
+*String*: master name
+
+##### *Return value*
+*Array*, *Bool*: List of arrays with info for each replicas in case of success, `FALSE` in case of failure.
+
+##### *Example*
+~~~php
+$sentinel->slaves('mymaster');
+~~~

From 23f9de30cbbd1cdf905d1bc24434fff300411036 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 6 Feb 2020 16:01:18 -0800
Subject: [PATCH 1302/1986] Update sponsorship information

---
 README.markdown | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/README.markdown b/README.markdown
index 965243b761..d84a9c5222 100644
--- a/README.markdown
+++ b/README.markdown
@@ -8,12 +8,16 @@ This code has been developed and maintained by Owlient from November 2009 to Mar
 
 You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)) or to p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)).
 
-## Donating to the project
-If you've found phpredis useful and would like to buy the maintainers a coffee (or a Tesla, we're not picky), feel free to do so.
+## Supporting the project
+PhpRedis will always be free and open source software, but if you or your company has found it useful please consider supporting the project.  Developing a large, complex, and performant library like PhpRedis takes a great deal of time and effort, and support would be appriciated! :heart:
 
-[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5)
-[![Donate with Bitcoin](https://en.cryptobadges.io/badge/micro/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)](https://en.cryptobadges.io/donate/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)
-[![Donate with Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)
+The best way to support the project is through [GitHub sponsors](https://github.com/sponsors/michael-grunder).  Many of the reward tiers grant access to our [slack channel](https://phpredis.slack.com) where [myself](https://github.com/michael-grunder) and [Pavlo](https://github.com/yatsukhnenko) are regularly online and available.  Additionally this will allow you to provide feedback on which fixes and new features to prioritize.
+
+You can also make a one-time contribution with one of the links below.
+
+[![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5)
+[![Bitcoin](https://en.cryptobadges.io/badge/micro/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)](https://en.cryptobadges.io/donate/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)
+[![Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)
 
 
 # Table of contents

From 383779edc2f9d7606f9a451dbde8249d32a34ad4 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 6 Feb 2020 16:06:17 -0800
Subject: [PATCH 1303/1986] Correct spelling and fix wording

---
 README.markdown | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.markdown b/README.markdown
index d84a9c5222..8e4440b779 100644
--- a/README.markdown
+++ b/README.markdown
@@ -9,9 +9,9 @@ This code has been developed and maintained by Owlient from November 2009 to Mar
 You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)) or to p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)).
 
 ## Supporting the project
-PhpRedis will always be free and open source software, but if you or your company has found it useful please consider supporting the project.  Developing a large, complex, and performant library like PhpRedis takes a great deal of time and effort, and support would be appriciated! :heart:
+PhpRedis will always be free and open source software, but if you or your company has found it useful please consider supporting the project.  Developing a large, complex, and performant library like PhpRedis takes a great deal of time and effort, and support would be appreciated! :heart:
 
-The best way to support the project is through [GitHub sponsors](https://github.com/sponsors/michael-grunder).  Many of the reward tiers grant access to our [slack channel](https://phpredis.slack.com) where [myself](https://github.com/michael-grunder) and [Pavlo](https://github.com/yatsukhnenko) are regularly online and available.  Additionally this will allow you to provide feedback on which fixes and new features to prioritize.
+The best way to support the project is through [GitHub sponsors](https://github.com/sponsors/michael-grunder).  Many of the reward tiers grant access to our [slack channel](https://phpredis.slack.com) where [myself](https://github.com/michael-grunder) and [Pavlo](https://github.com/yatsukhnenko) are regularly available to answer questions.  Additionally this will allow you to provide feedback on which fixes and new features to prioritize.
 
 You can also make a one-time contribution with one of the links below.
 

From 17ddbe76a2918f3d47176e74b14ab28a68a51b54 Mon Sep 17 00:00:00 2001
From: Jan Ehrhardt 
Date: Tue, 11 Feb 2020 02:14:17 +0100
Subject: [PATCH 1304/1986] Update config,w32 with sentinel sources

The sentinel sources were added to config.m4 in https://github.com/phpredis/phpredis/commit/c94e28f1ebabdfceb722ad78eff75ce4fc57f5a3#diff-788d457a20b110cc38e571dec9ddc68c

They should be added to config.w32 as well.
---
 config.w32 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config.w32 b/config.w32
index 11953b1a48..e2b4365752 100644
--- a/config.w32
+++ b/config.w32
@@ -5,7 +5,7 @@ ARG_ENABLE("redis-session", "whether to enable sessions", "yes");
 ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no");
 
 if (PHP_REDIS != "no") {
-	var sources = "redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c";
+	var sources = "redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c";
 	if (PHP_REDIS_SESSION != "no") {
 		ADD_EXTENSION_DEP("redis", "session");
 		ADD_FLAG("CFLAGS_REDIS", ' /D PHP_SESSION=1 ');

From bf27e6e3db9648bcbc260273a99554ddb96d7420 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 2 Mar 2020 11:00:53 -0800
Subject: [PATCH 1305/1986] Merge 5.2.0 into develop

---
 Changelog.md               | 105 +++++++++++++++++++--
 README.markdown            |   5 +
 library.c                  |   8 +-
 package.xml                | 184 ++++++++++++++++++++++++++++++-------
 php_redis.h                |   2 +-
 redis.c                    |   1 +
 redis_sentinel.h           |   2 +
 tests/RedisClusterTest.php |  15 +++
 8 files changed, 274 insertions(+), 48 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 4659a1a58e..053953af48 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,18 +5,103 @@ All changes to phpredis will be documented in this file.
 We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## [Unreleased]
+## [5.2.0] - 2020-03-02 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.2.0), [PECL](https://pecl.php.net/package/redis/5.2.0))
 
-### Changed
+*There were no changes between 5.2.0RC2 and 5.2.0*
+
+## [5.2.0RC2] - 2020-02-21 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.2.0RC2), [PECL](https://pecl.php.net/package/redis/5.2.0RC2))
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack.com](https://audiomack.com)
+- [Till Krüss](https://github.com/tillkruss)
+
+### Fixed
+
+- Include RedisSentinelTest.php in package.xml!
+  [eddbfc8f](https://github.com/phpredis/phpredis/commit/eddbfc8f)
+  ([Michel Grunder](https://github.com/michael-grunder))
+
+- Fix -Wmaybe-uninitialized warning
+  [740b8c87](https://github.com/phpredis/phpredis/commit/740b8c87)
+  ([Remi Collet](https://github.com/remicollet))
+
+- Fix improper destructor when zipping values and scores
+  [371ae7ae](https://github.com/phpredis/phpredis/commit/371ae7ae)
+
+- Use php_rand instead of php_mt_rand for liveness challenge string
+  [9ef2ed89](https://github.com/phpredis/phpredis/commit/9ef2ed89)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+## [5.2.0RC1] - 2020-02-15 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.2.0RC1), [PECL](https://pecl.php.net/package/redis/5.2.0RC1))
+
+### Sponsors :sparkling_heart:
+
+- [Till Krüss](https://github.com/tillkruss)
+
+### Added
 
-- Use ECHO to check liveness of connection
+- Added challenge/response mechanism to ensure persistent connections are not in a bad state
+  [a5f95925](https://github.com/phpredis/phpredis/commit/a5f95925),
+  [25cdaee6](https://github.com/phpredis/phpredis/commit/25cdaee6),
+  [7b6072e0](https://github.com/phpredis/phpredis/commit/7b6072e0),
   [99ebd0cc](https://github.com/phpredis/phpredis/commit/99ebd0cc),
-  [3243f426](https://github.com/phpredis/phpredis/commit/3243f426),
-  [a5f95925](https://github.com/phpredis/phpredis/commit/a5f95925)
+  [3243f426](https://github.com/phpredis/phpredis/commit/3243f426)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder))
-- Enable slot caching for session cluster
-  [23b1a9d8](https://github.com/phpredis/phpredis/commit/23b1a9d84)
-  ([Michael03](https://github.com/Michael03))
+
+- Experimental support for RedisSentinel
+  [90cb69f3](https://github.com/phpredis/phpredis/commit/90cb69f3),
+  [c94e28f1](https://github.com/phpredis/phpredis/commit/c94e28f1),
+  [46da22b0](https://github.com/phpredis/phpredis/commit/46da22b0),
+  [5a609fa4](https://github.com/phpredis/phpredis/commit/5a609fa4),
+  [383779ed](https://github.com/phpredis/phpredis/commit/383779ed)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+### Fixed
+
+- Fixed ASK redirection logic
+  [ba73fbee](https://github.com/phpredis/phpredis/commit/ba73fbee)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+- Create specific 'test skipped' exception
+  [c3d83d44](https://github.com/phpredis/phpredis/commit/c3d83d44)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+- Fixed memory leaks in RedisCluster
+  [a107c9fc](https://github.com/phpredis/phpredis/commit/a107c9fc)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+- Fixes for session lifetime values that underflow or overflow
+  [7a79ad9c](https://github.com/phpredis/phpredis/commit/7a79ad9c),
+  [3c48a332](https://github.com/phpredis/phpredis/commit/3c48a332)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+- Enables slot caching for Redis Cluster
+  [23b1a9d8](https://github.com/phpredis/phpredis/commit/23b1a9d8)
+  ([Michael Booth](https://github.com/Michael03))
+
+- Housekeeping (spelling, doc changes, etc)
+  [23f9de30](https://github.com/phpredis/phpredis/commit/23f9de30),
+  [d07a8df6](https://github.com/phpredis/phpredis/commit/d07a8df6),
+  [2d39b48d](https://github.com/phpredis/phpredis/commit/2d39b48d),
+  [0ef488fc](https://github.com/phpredis/phpredis/commit/0ef488fc),
+  [2c35e435](https://github.com/phpredis/phpredis/commit/2c35e435),
+  [f52bd8a8](https://github.com/phpredis/phpredis/commit/f52bd8a8),
+  [2ddc5f21](https://github.com/phpredis/phpredis/commit/2ddc5f21),
+  [1ff7dfb7](https://github.com/phpredis/phpredis/commit/1ff7dfb7),
+  [db446138](https://github.com/phpredis/phpredis/commit/db446138)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko),
+   [Tyson Andre](https://github.com/TysonAndre), [Michael Grunder](https://github.com/michael-grunder),
+   [Paul DelRe](https://github.com/pdelre), [Tyson Andre](https://github.com/TysonAndre))
+
+### Changed
+
+- Support TYPE argument for SCAN
+  [8eb39a26](https://github.com/phpredis/phpredis/commit/8eb39a26)
+  [b1724b84](https://github.com/phpredis/phpredis/commit/b1724b84)
+  [53fb36c9](https://github.com/phpredis/phpredis/commit/53fb36c9)
+  [544e641b](https://github.com/phpredis/phpredis/commit/544e641b)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
 ---
 
@@ -109,7 +194,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
   [1f41da64](https://github.com/phpredis/phpredis/commit/1f41da64),
   ([Owen Smith](https://github.com/orls))
 - RedisCluster segfaults after second connection with cache_slots enabled
-  [f52cd237](https://github.com/phpredis/phpredis/commit/f52cd237), 
+  [f52cd237](https://github.com/phpredis/phpredis/commit/f52cd237),
   [cb5d6b94](https://github.com/phpredis/phpredis/commit/cb5d6b94)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder))
 
@@ -463,7 +548,7 @@ Also many small improvements and bug fixes were made.
 - Fix zval_get_string impl for PHP5 [4e56ba](https://www.github.com/phpredis/phpredis/commit/4e56ba) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 - Fix Redis/RedisArray segfaults [be5c1f](https://www.github.com/phpredis/phpredis/commit/be5c1f), [635c3a](https://www.github.com/phpredis/phpredis/commit/635c3a), [1f8dde](https://www.github.com/phpredis/phpredis/commit/1f8dde), [43e1e0](https://www.github.com/phpredis/phpredis/commit/43e1e0)
 - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
-- Fix memory leak and potential segfault [aa6ff7](https://www.github.com/phpredis/phpredis/commit/aa6ff7), [88efaa](https://www.github.com/phpredis/phpredis/commit/88efaa) ([Michael Grunder](https://github.com/michael-grunder))
+- Fix memory leak and potential segfault [aa6ff77a](https://www.github.com/phpredis/phpredis/commit/aa6ff77a), [88efaa](https://www.github.com/phpredis/phpredis/commit/88efaa) ([Michael Grunder](https://github.com/michael-grunder))
 - Assume "NULL bulk" reply as success (empty session data) [4a81e1](https://www.github.com/phpredis/phpredis/commit/4a81e1)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 - Refactoring ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), [Michael Grunder](https://github.com/michael-grunder))
diff --git a/README.markdown b/README.markdown
index 8e4440b779..949f1837d9 100644
--- a/README.markdown
+++ b/README.markdown
@@ -19,6 +19,11 @@ You can also make a one-time contribution with one of the links below.
 [![Bitcoin](https://en.cryptobadges.io/badge/micro/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)](https://en.cryptobadges.io/donate/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)
 [![Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)
 
+## Sponsors
+
+
+    Audiomack.com
+
 
 # Table of contents
 -----
diff --git a/library.c b/library.c
index ebb9fbf427..c81e2eb9d2 100644
--- a/library.c
+++ b/library.c
@@ -1166,8 +1166,7 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab,
 
     /* replace */
     zval_dtor(z_tab);
-    ZVAL_ZVAL(z_tab, &z_ret, 1, 0);
-    zval_dtor(&z_ret);
+    ZVAL_ZVAL(z_tab, &z_ret, 0, 0);
 }
 
 static int
@@ -1476,7 +1475,7 @@ PHP_REDIS_API int
 redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements)
 {
     zval zv;
-    int i, len;
+    int i, len = 0;
     char *key = NULL, *data;
     REDIS_REPLY_TYPE type;
     long li;
@@ -1812,8 +1811,9 @@ redis_sock_check_liveness(RedisSock *redis_sock)
         redis_cmd_init_sstr(&cmd, 1, "AUTH", sizeof("AUTH") - 1);
         redis_cmd_append_sstr(&cmd, ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth));
     }
+
     gettimeofday(&tv, NULL);
-    uniqid_len = snprintf(uniqid, sizeof(uniqid), "phpredis_pool:%08lx%05lx:%08" PRIx32, (long)tv.tv_sec, (long)tv.tv_usec, php_mt_rand());
+    uniqid_len = snprintf(uniqid, sizeof(uniqid), "phpredis_pool:%08lx%05lx:%08lx", (long)tv.tv_sec, (long)tv.tv_usec, (long)php_rand());
     redis_cmd_init_sstr(&cmd, 1, "ECHO", sizeof("ECHO") - 1);
     redis_cmd_append_sstr(&cmd, uniqid, uniqid_len);
     smart_string_0(&cmd);
diff --git a/package.xml b/package.xml
index b2668e6fd6..190567f866 100644
--- a/package.xml
+++ b/package.xml
@@ -27,10 +27,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2019-07-02
+ 2020-03-02
  
-  5.0.0
-  5.0.0
+  5.2.0
+  5.2.0
  
  
   stable
@@ -38,42 +38,54 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
  PHP
  
-    This release contains important improvements and breaking changes.
-    The most interesting are: drop PHP5 support, RedisCluster slots caching,
-    JSON and msgpack serializers, soft deprecation of non-Redis commands.
+    phpredis 5.2.0
 
-    phpredis 5.0.0
+    - There were no changes between 5.2.0RC2 and 5.2.0.
 
-    * Remove HAVE_SPL [55c5586c] (@petk)
-    * Update Fedora installation instructions [90aa067c] (@remicollet)
+    phpredis 5.2.0RC2
 
-    phpredis 5.0.0RC2
+    * Include RedisSentinelTest.php in package.xml! [eddbfc8f] (Michael Grunder)
+    * Fix -Wmaybe-uninitialized warning [740b8c87] (Remi Collet)
+    * Fix improper destructor when zipping values and scores [371ae7ae]
+      (Michael Grunder)
+    * Use php_rand instead of php_mt_rand for liveness challenge string
+      [9ef2ed89] (Michael Grunder)
 
-    * Allow compilation without JSON serialization enabled and fixes for deprecated
-      helper methods.  [235a27] (Pavlo Yatsukhnenko)
-    * Fix php msgpack >= 2.0.3 version requirement. [6973478..a537df8] (Michael Grunder)
+    phpredis 5.2.0RC1
 
-    phpredis 5.0.0RC1
+    This release contains initial support for Redis Sentinel as well as many
+    smaller bug fixes and improvements.  It is especially of interest if you
+    use persistent connections, as we've added logic to make sure they are in
+    a good state when retreving them from the pool.
 
-    * Enable connection pooling by default [8206b147] (Pavlo Yatsukhnenko)
-    * Soft deprecate methods that aren't actually Redis commands [a81b4f2d, 95c8aab9] (Pavlo Yatsukhnenko, Michael Grunder)
-    * Enable pooling for cluster slave nodes [17600dd1] (Michael Grunder)
-    * xInfo response format [4852a510, ac9dca0a] (Pavlo Yatsukhnenko)
-    * Make the XREADGROUP optional COUNT and BLOCK arguments nullable [0c17bd27] (Michael Grunder)
-    * Allow PING to take an optional argument [6e494170] (Michael Grunder)
-    * Allow ZRANGE to be called either with `true` or `['withscores' => true]` [19f3efcf] (Michael Grunder)
-    * Allow to specify server address as schema://host [418428fa] (Pavlo Yatsukhnenko)
-    * Allow persistent_id to be passed as NULL with strict_types enabled [60223762] (Michael Grunder)
-    * Add server address to exception message [e8fb49be, 34d6403d] (Pavlo Yatsukhnenko)
-    * Adds OPT_REPLY_LITERAL for rawCommand and EVAL [5cb30fb2] (Michael Grunder)
-    * JSON serializer [98bd2886, 96c57139] (Pavlo Yatsukhnenko, Michael Grunder)
-    * Add support for STREAM to the type command [d7450b2f, 068ce978, 8a45d18c] (Michael Grunder, Pavlo Yatsukhnenko)
-    * Fix TypeError when using built-in constants in `setOption` [4c7643ee] (@JoyceBabu)
-    * Handle references in MGET [60d8b679] (Michael Grunder)
-    * msgpack serializer [d5b8f833, 545250f3, 52bae8ab] (@bgort, Pavlo Yatsukhnenko, Michael Grunder)
-    * Add Cluster slots caching [9f0d7bc0, ea081e05] (Michael Grunder)
-    * Drop PHP5 support [f9928642, 46a50c12, 4601887d, 6ebb36ce, fdbe9d29] (Michael Grunder)
-    * Documentation improvements (@alexander-schranz, @cookieguru, Pavlo Yatsukhnenko, Michael Grunder)
+    IMPORTANT: Sentinel support is considered experimental and the API
+               will likely change based on user feedback.
+
+    * Sponsors
+      ~ Audiomack.com - https://audiomack.com
+      ~ Till Kruss - https://github.com/tillkruss
+
+    ---
+
+    * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4,
+      383779ed] (Pavlo Yatsukhnenko)
+
+    * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d,
+      0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre,
+      Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre)
+
+    * Fix for ASK redirections [ba73fbee] (Michael Grunder)
+    * Create specific 'test skipped' exception [c3d83d44] (Michael Grunder)
+    * Fixed memory leaks in RedisCluster [a107c9fc] (Michael Grunder)
+    * Fixes for session lifetime values that underflow or overflow  [7a79ad9c,
+      3c48a332] (Michael Grunder)
+    * Enables slot caching for Redis Cluster [23b1a9d8] (Michael Booth)
+
+    * Support TYPE argument for SCAN [8eb39a26, b1724b84, 53fb36c9, 544e641b]
+      (Pavlo Yatsukhnenko)
+
+    * Added challenge/response mechanism for persistent connections [a5f95925,
+      25cdaee6, 7b6072e0, 99ebd0cc, 3243f426] (Pavlo Yatsukhnenko, Michael Grunder)
  
  
   
@@ -83,6 +95,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
    
    
    
+   
    
    
    
@@ -103,6 +116,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
    
    
    
+   
+   
+   
+   
    
      
      
@@ -114,6 +131,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
    
      
      
+     
      
      
      
@@ -140,8 +158,108 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
   
   
+  
  
  
+  
+   alphaalpha
+   5.2.0RC25.2.0RC2
+   2020-02-21
+   
+    phpredis 5.2.0RC2
+
+    * Include RedisSentinelTest.php in package.xml! [eddbfc8f] (Michael Grunder)
+    * Fix -Wmaybe-uninitialized warning [740b8c87] (Remi Collet)
+    * Fix improper destructor when zipping values and scores [371ae7ae]
+      (Michael Grunder)
+    * Use php_rand instead of php_mt_rand for liveness challenge string
+      [9ef2ed89] (Michael Grunder)
+
+    phpredis 5.2.0RC1
+
+    This release contains initial support for Redis Sentinel as well as many
+    smaller bug fixes and improvements.  It is especially of interest if you
+    use persistent connections, as we've added logic to make sure they are in
+    a good state when retreving them from the pool.
+
+    IMPORTANT: Sentinel support is considered experimental and the API
+               will likely change based on user feedback.
+
+    * Sponsors
+      ~ Audiomack.com - https://audiomack.com
+      ~ Till Kruss - https://github.com/tillkruss
+
+    ---
+
+    * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4,
+      383779ed] (Pavlo Yatsukhnenko)
+
+    * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d,
+      0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre,
+      Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre)
+
+    * Fix for ASK redirections [ba73fbee] (Michael Grunder)
+    * Create specific 'test skipped' exception [c3d83d44] (Michael Grunder)
+    * Fixed memory leaks in RedisCluster [a107c9fc] (Michael Grunder)
+    * Fixes for session lifetime values that underflow or overflow  [7a79ad9c,
+      3c48a332] (Michael Grunder)
+    * Enables slot caching for Redis Cluster [23b1a9d8] (Michael Booth)
+
+    * Support TYPE argument for SCAN [8eb39a26, b1724b84, 53fb36c9, 544e641b]
+      (Pavlo Yatsukhnenko)
+
+    * Added challenge/response mechanism for persistent connections [a5f95925,
+      25cdaee6, 7b6072e0, 99ebd0cc, 3243f426] (Pavlo Yatsukhnenko, Michael Grunder)
+   
+  
+  
+   stablestable
+   5.1.15.1.0
+   2019-11-11
+   
+      phpredis 5.1.1
+
+      This release contains only bugfix for unix-socket connection.
+
+      * Fix fail to connect to redis through unix socket [2bae8010, 9f4ededa] (Pavlo Yatsukhnenko, Michael Grunder)
+      * Documentation improvements (@fitztrev)
+   
+  
+
+  
+   stablestable
+   5.1.05.1.0
+   2019-10-31
+   
+      This release contains important bugfixes and improvements.
+
+      phpredis 5.1.0
+
+      * Allow to specify scheme for session handler [53a8bcc7] (Pavlo Yatsukhnenko)
+      * Add documentation for hyperloglog [75a6f3fa, 96a0f0c3, 9686757a] (@rlunar)
+
+      phpredis 5.1.0RC2
+
+      * Fix missing null byte in PHP_MINFO_FUNCTION [8bc2240c] (Remi Collet)
+      * Remove dead code generic_unsubscribe_cmd [8ee4abbc] (Pavlo Yatsukhnenko)
+      * Add documentation for zpopmin and zpopmax [99ec24b3, 4ab1f940] (@alexander-schranz)
+
+      phpredis 5.1.0RC1
+
+      * Fix regression for multihost_distribute_call added in 112c77e3 [fbe0f804] (Pavlo Yatsukhnenko)
+      * Fix regression for conntecting to unix sockets with relative path added in 1f41da64 [17b139d8, 7ef17ce1] (Pavlo Yatsukhnenko)
+      * Fix unix-socket detection logic broken in 418428fa [a080b73f] (Pavlo Yatsukhnenko)
+      * Fix memory leak and bug with getLastError for redis_mbulk_reply_assoc and redis_mbulk_reply_zipped. [7f42d628, 3a622a07] (Pavlo Yatsukhnenko), (Michael Grunder)
+      * Fix bug with password contain "#" for redis_session [2bb08680] (Pavlo Yatsukhnenko)
+      * Add optional support for Zstd compression, using --enable-redis-ztsd. This requires libzstd version >= 1.3.0 [2abc61da] (Remi Collet)
+      * Fix overallocation in RedisCluster directed node commands [cf93649] (Michael Grunder)
+      * Also attach slaves when caching cluster slots [0d6d3fdd, b114fc26] (Michael Grunder)
+      * Use zend_register_persistent_resource_ex for connection pooling [fdada7ae, 7c6c43a6] (Pavlo Yatsukhnenko)
+      * Refactor redis_session [91a8e734, 978c3074] (Pavlo Yatsukhnenko)
+      * Documentation improvements (@Steveb-p, @tangix, @ljack-adista, @jdreesen, Michael Grunder)
+   
+  
+
   
    stablestable
    5.0.05.0.0
diff --git a/php_redis.h b/php_redis.h
index c86ac2b4b0..af77ea00fe 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -23,7 +23,7 @@
 #define PHP_REDIS_H
 
 /* phpredis version */
-#define PHP_REDIS_VERSION "5.2.0-dev"
+#define PHP_REDIS_VERSION "5.2.0"
 
 PHP_METHOD(Redis, __construct);
 PHP_METHOD(Redis, __destruct);
diff --git a/redis.c b/redis.c
index 339328f176..1a19c6a5c3 100644
--- a/redis.c
+++ b/redis.c
@@ -879,6 +879,7 @@ PHP_MINFO_FUNCTION(redis)
     php_info_print_table_start();
     php_info_print_table_header(2, "Redis Support", "enabled");
     php_info_print_table_row(2, "Redis Version", PHP_REDIS_VERSION);
+    php_info_print_table_row(2, "Redis Sentinel Version", PHP_REDIS_SENTINEL_VERSION);
 #ifdef GIT_REVISION
     php_info_print_table_row(2, "Git revision", "$Id: " GIT_REVISION " $");
 #endif
diff --git a/redis_sentinel.h b/redis_sentinel.h
index 986a63ba0d..651cc1b822 100644
--- a/redis_sentinel.h
+++ b/redis_sentinel.h
@@ -3,6 +3,8 @@
 
 #include "sentinel_library.h"
 
+#define PHP_REDIS_SENTINEL_VERSION "0.1"
+
 PHP_METHOD(RedisSentinel, __construct);
 PHP_METHOD(RedisSentinel, ckquorum);
 PHP_METHOD(RedisSentinel, failover);
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index eb9e427891..4d7ba73029 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -665,6 +665,21 @@ public function testSlotCache() {
         ini_set('redis.clusters.cache_slots', 0);
     }
 
+    /* Regression test for connection pool liveness checks */
+    public function testConnectionPool() {
+        $prev_value = ini_get('redis.pconnect.pooling_enabled');
+        ini_set('redis.pconnect.pooling_enabled', 1);
+
+        $pong = 0;
+        for ($i = 0; $i < 10; $i++) {
+            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true);
+            $pong += $obj_rc->ping("key:$i");
+        }
+
+        $this->assertEquals($pong, $i);
+        ini_set('redis.pconnect.pooling_enabled', $prev_value);
+    }
+
     /**
      * @inheritdoc
      */

From 0ce7ca2fb1eb2f3c445487957a49b70ad8d4ecb6 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Mon, 2 Mar 2020 11:03:51 -0800
Subject: [PATCH 1306/1986] Issue.1714 json session tests (#1717)

* Add json.so as well if we don't find it.

See #1714

* Remove added newline
---
 tests/RedisTest.php | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index bfe89f4487..080e3e8658 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6423,21 +6423,26 @@ private function getPhpCommand($script)
                 $cmd .= $test_args;
             } else {
                 /* Only append specific extension directives if PHP hasn't been compiled with what we need statically */
-                $result   = shell_exec("$cmd --no-php-ini -m");
-                $redis    = strpos($result, 'redis') !== false;
-                $igbinary = strpos($result, 'igbinary') !== false;
-                $msgpack  = strpos($result, 'msgpack') !== false;
+                $modules   = shell_exec("$cmd --no-php-ini -m");
 
-                if (!$redis || !$igbinary || !$msgpack) {
-                    $cmd .= ' --no-php-ini';
-                    if (!$msgpack) {
-                        $cmd .= ' --define extension=msgpack.so';
-                    }
-                    if (!$igbinary) {
-                        $cmd .= ' --define extension=igbinary.so';
+                /* Determine if we need to specifically add extensions */
+                $arr_extensions = array_filter(
+                    ['redis', 'igbinary', 'msgpack', 'json'],
+                    function ($module) use ($modules) {
+                        return strpos($modules, $module) === false;
                     }
-                    if (!$redis) {
-                        $cmd .= ' --define extension=' . dirname(__DIR__) . '/modules/redis.so';
+                );
+
+                /* 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";
                     }
                 }
             }

From 92f8dde1c996d4e1c3d79226b888119307612c40 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Mon, 2 Mar 2020 11:23:24 -0800
Subject: [PATCH 1307/1986] Issue.1636 del documentation (#1718)

* Update multiple key command documentation

See #1636
---
 cluster.markdown | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/cluster.markdown b/cluster.markdown
index cbb1cdd72a..1f7e70fb64 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -118,13 +118,14 @@ Redis cluster does allow commands that operate on multiple keys, but only if all
 
 For all of these multiple key commands (with the exception of MGET and MSET), the RedisCluster class will verify each key maps to the same hash slot and raise a "CROSSSLOT" warning, returning false if they don't.
 
-### MGET and MSET
-RedisCluster has specialized processing for MGET and MSET which allows you to send any number of keys (hashing to whichever slots) without having to consider where they live.  The way this works, is that the RedisCluster class will split the command as it iterates through keys, delivering a subset of commands per each key's slot.
+### MGET, MSET, DEL, and UNLINK
+RedisCluster has specialized processing for MGET, MSET, DEL, and UNLINK which allows you to send any number of keys (hashing to whichever slots) without having to consider where they live.  The way this works, is that the RedisCluster class will split the command as it iterates through keys, delivering a subset of commands per each key's slot.
+
+*Note:  If you send keys that hash to more than one slot, these commands are no longer atomic.*
 
 ~~~php
-// This will be delivered in two commands.  First for all of the {hash1} keys,
-// and then to grab 'otherkey'
-$obj_cluster->mget(Array("{hash1}key1","{hash1}key2","{hash1}key3","otherkey"));
+// This will send two `MGET` commands.  One for `{hash1}` keys, and one for `otherkey`
+$obj_cluster->mget(["{hash1}key1","{hash1}key2","{hash1}key3","otherkey"]);
 ~~~
 
 This operation can also be done in MULTI mode transparently.

From d5dadaf63552360e3e663cdcffd706b859c3ea83 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 9 Mar 2020 13:36:27 +0200
Subject: [PATCH 1308/1986] Add PHPREDIS_GET_OBJECT and
 PHPREDIS_ZVAL_GET_OBJECT macros

---
 common.h           | 3 ++-
 redis.c            | 6 +++---
 redis_array.c      | 8 ++++----
 redis_array_impl.c | 2 +-
 redis_cluster.c    | 2 +-
 redis_cluster.h    | 3 +--
 redis_sentinel.c   | 2 +-
 sentinel_library.c | 2 +-
 8 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/common.h b/common.h
index 80381a6c26..f9aed17a40 100644
--- a/common.h
+++ b/common.h
@@ -13,7 +13,8 @@
 #include 
 
 #define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_FALSE)
-#define PHPREDIS_GET_OBJECT(class_entry, z) (class_entry *)((char *)Z_OBJ_P(z) - XtOffsetOf(class_entry, std))
+#define PHPREDIS_GET_OBJECT(class_entry, o) (class_entry *)((char *)o - XtOffsetOf(class_entry, std))
+#define PHPREDIS_ZVAL_GET_OBJECT(class_entry, z) PHPREDIS_GET_OBJECT(class_entry, Z_OBJ_P(z))
 
 /* NULL check so Eclipse doesn't go crazy */
 #ifndef NULL
diff --git a/redis.c b/redis.c
index 1a19c6a5c3..ff0bd02986 100644
--- a/redis.c
+++ b/redis.c
@@ -567,7 +567,7 @@ static void cluster_cache_dtor(zend_resource *rsrc) {
 void
 free_redis_object(zend_object *object)
 {
-    redis_object *redis = (redis_object *)((char *)(object) - XtOffsetOf(redis_object, std));
+    redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object);
 
     zend_object_std_dtor(&redis->std);
     if (redis->sock) {
@@ -600,7 +600,7 @@ redis_sock_get_instance(zval *id, int no_throw)
     redis_object *redis;
 
     if (Z_TYPE_P(id) == IS_OBJECT) {
-        redis = PHPREDIS_GET_OBJECT(redis_object, id);
+        redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, id);
         if (redis->sock) {
             return redis->sock;
         }
@@ -1012,7 +1012,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
         port = 6379;
     }
 
-    redis = PHPREDIS_GET_OBJECT(redis_object, object);
+    redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, object);
     /* if there is a redis sock already we have to remove it */
     if (redis->sock) {
         redis_sock_disconnect(redis->sock, 0);
diff --git a/redis_array.c b/redis_array.c
index ca4486163e..f5ada2768e 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -178,7 +178,7 @@ zend_object_handlers redis_array_object_handlers;
 void
 free_redis_array_object(zend_object *object)
 {
-    redis_array_object *obj = (redis_array_object *)((char *)(object) - XtOffsetOf(redis_array_object, std));
+    redis_array_object *obj = PHPREDIS_GET_OBJECT(redis_array_object, object);
 
     if (obj->ra) {
         if (obj->ra->prev) redis_array_free(obj->ra->prev);
@@ -214,7 +214,7 @@ redis_array_get(zval *id)
     redis_array_object *obj;
 
     if (Z_TYPE_P(id) == IS_OBJECT) {
-        obj = PHPREDIS_GET_OBJECT(redis_array_object, id);
+        obj = PHPREDIS_ZVAL_GET_OBJECT(redis_array_object, id);
         return obj->ra;
     }
     return NULL;
@@ -224,7 +224,7 @@ PHP_REDIS_API int
 ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[])
 {
     if (object) {
-        redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object);
+        redis_object *redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, object);
         if (redis->sock->auth &&
             redis->sock->status != REDIS_SOCK_STATUS_CONNECTED &&
             redis_sock_server_open(redis->sock) == SUCCESS
@@ -366,7 +366,7 @@ PHP_METHOD(RedisArray, __construct)
         ra->auto_rehash = b_autorehash;
         ra->connect_timeout = d_connect_timeout;
         if(ra->prev) ra->prev->auto_rehash = b_autorehash;
-        obj = PHPREDIS_GET_OBJECT(redis_array_object, getThis());
+        obj = PHPREDIS_ZVAL_GET_OBJECT(redis_array_object, getThis());
         obj->ra = ra;
     }
 }
diff --git a/redis_array_impl.c b/redis_array_impl.c
index f76bac2a17..646b08a281 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -67,7 +67,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in
         call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL);
         zval_dtor(&z_ret);
 
-        redis = PHPREDIS_GET_OBJECT(redis_object, &ra->redis[i]);
+        redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, &ra->redis[i]);
 
         /* create socket */
         redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval);
diff --git a/redis_cluster.c b/redis_cluster.c
index 1029b9df41..1ee7e4444b 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -334,7 +334,7 @@ zend_object * create_cluster_context(zend_class_entry *class_type) {
 
 /* Free redisCluster context */
 void free_cluster_context(zend_object *object) {
-    redisCluster *cluster = (redisCluster*)((char*)(object) - XtOffsetOf(redisCluster, std));
+    redisCluster *cluster = PHPREDIS_GET_OBJECT(redisCluster, object);
 
     cluster_free(cluster, 0);
     zend_object_std_dtor(&cluster->std);
diff --git a/redis_cluster.h b/redis_cluster.h
index 414c489d60..68a0273cd3 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -10,8 +10,7 @@
 #define REDIS_CLUSTER_MOD   (REDIS_CLUSTER_SLOTS-1)
 
 /* Get attached object context */
-#define GET_CONTEXT() \
-    ((redisCluster *)((char *)Z_OBJ_P(getThis()) - XtOffsetOf(redisCluster, std)))
+#define GET_CONTEXT() PHPREDIS_ZVAL_GET_OBJECT(redisCluster, getThis())
 
 /* Command building/processing is identical for every command */
 #define CLUSTER_BUILD_CMD(name, c, cmd, cmd_len, slot) \
diff --git a/redis_sentinel.c b/redis_sentinel.c
index f4729d7876..9fa5414def 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -93,7 +93,7 @@ PHP_METHOD(RedisSentinel, __construct)
         }
     }
 
-    obj = PHPREDIS_GET_OBJECT(redis_sentinel_object, getThis());
+    obj = PHPREDIS_ZVAL_GET_OBJECT(redis_sentinel_object, getThis());
     obj->sock = redis_sock_create(ZSTR_VAL(host), ZSTR_LEN(host), port,
         timeout, read_timeout, persistent, persistent_id, retry_interval);
 }
diff --git a/sentinel_library.c b/sentinel_library.c
index bd81e8d877..481985467f 100644
--- a/sentinel_library.c
+++ b/sentinel_library.c
@@ -5,7 +5,7 @@ static zend_object_handlers redis_sentinel_object_handlers;
 static void
 free_redis_sentinel_object(zend_object *object)
 {
-    redis_sentinel_object *obj = (redis_sentinel_object *)((char *)(object) - XtOffsetOf(redis_sentinel_object, std));
+    redis_sentinel_object *obj = PHPREDIS_GET_OBJECT(redis_sentinel_object, object);
 
     if (obj->sock) {
         redis_sock_disconnect(obj->sock, 0);

From 190c0d34ad718c2246d36b900475d216737263a5 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 9 Mar 2020 13:46:26 +0200
Subject: [PATCH 1309/1986] Fix redis_cluster GET_CONTEXT usage

---
 redis_cluster.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/redis_cluster.c b/redis_cluster.c
index 1ee7e4444b..ee218ba05c 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1953,36 +1953,37 @@ PHP_METHOD(RedisCluster, clearlasterror) {
 
 /* {{{ proto long RedisCluster::getOption(long option */
 PHP_METHOD(RedisCluster, getoption) {
-    redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-        GET_CONTEXT()->flags, GET_CONTEXT());
+    redisCluster *c = GET_CONTEXT();
+    redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, c);
 }
 /* }}} */
 
 /* {{{ proto bool RedisCluster::setOption(long option, mixed value) */
 PHP_METHOD(RedisCluster, setoption) {
-    redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-        GET_CONTEXT()->flags, GET_CONTEXT());
+    redisCluster *c = GET_CONTEXT();
+    redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, c);
 }
 /* }}} */
 
 /* {{{ proto string RedisCluster::_prefix(string key) */
 PHP_METHOD(RedisCluster, _prefix) {
-    redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-        GET_CONTEXT()->flags);
+    redisCluster *c = GET_CONTEXT();
+    redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
 }
 /* }}} */
 
 /* {{{ proto string RedisCluster::_serialize(mixed val) */
 PHP_METHOD(RedisCluster, _serialize) {
-    redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-        GET_CONTEXT()->flags);
+    redisCluster *c = GET_CONTEXT();
+    redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
 }
 /* }}} */
 
 /* {{{ proto mixed RedisCluster::_unserialize(string val) */
 PHP_METHOD(RedisCluster, _unserialize) {
+    redisCluster *c = GET_CONTEXT();
     redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-        GET_CONTEXT()->flags, redis_cluster_exception_ce);
+        c->flags, redis_cluster_exception_ce);
 }
 /* }}} */
 

From 9ee94ca4f6212d1c6a7e8378c17df97d691068f7 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 9 Mar 2020 14:00:07 +0200
Subject: [PATCH 1310/1986] fix usage php_hash_fetch_ops with PHP 8

---
 redis_array_impl.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index 646b08a281..c3cfd2b623 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -551,7 +551,13 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos)
         const php_hash_ops *ops;
 
         /* hash */
-        if (ra->algorithm && (ops = php_hash_fetch_ops(ZSTR_VAL(ra->algorithm), ZSTR_LEN(ra->algorithm))) != NULL) {
+        if (ra->algorithm && (
+#if (PHP_VERSION_ID < 80000)
+            ops = php_hash_fetch_ops(ZSTR_VAL(ra->algorithm), ZSTR_LEN(ra->algorithm))
+#else
+            ops = php_hash_fetch_ops(ra->algorithm)
+#endif
+        ) != NULL) {
             void *ctx = emalloc(ops->context_size);
             unsigned char *digest = emalloc(ops->digest_size);
 

From 7e4c7b3e6934c7257bcd42e65e054e052495e39e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 9 Mar 2020 14:19:26 +0200
Subject: [PATCH 1311/1986] Change version_compare usage in tests

---
 tests/RedisTest.php | 66 ++++++++++++++++++++++-----------------------
 1 file changed, 33 insertions(+), 33 deletions(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 080e3e8658..1c9bcd3916 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -47,7 +47,7 @@ public function setUp() {
     }
 
     protected function minVersionCheck($version) {
-        return version_compare($this->version, $version, "ge");
+        return version_compare($this->version, $version) >= 0;
     }
 
     protected function mstime() {
@@ -86,7 +86,7 @@ protected function havePipeline() {
     public function testMinimumVersion()
     {
         // Minimum server version required for tests
-        $this->assertTrue(version_compare($this->version, "2.4.0", "ge"));
+        $this->assertTrue(version_compare($this->version, "2.4.0") >= 0);
     }
 
     public function testPing() {
@@ -119,7 +119,7 @@ public function testPipelinePublish() {
     // 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", "lt")) {
+        if (version_compare($this->version, "2.8.0") < 0) {
             $this->markTestSkipped();
             return;
         }
@@ -199,7 +199,7 @@ public function testBitsets() {
     }
 
     public function testBitPos() {
-        if(version_compare($this->version, "2.8.7", "lt")) {
+        if (version_compare($this->version, "2.8.7") < 0) {
             $this->MarkTestSkipped();
             return;
         }
@@ -311,7 +311,7 @@ public function testSet()
     /* 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', 'lt')) {
+        if (version_compare($this->version, '2.6.12') < 0) {
             $this->markTestSkipped();
             return;
         }
@@ -556,7 +556,7 @@ public function testIncr()
     public function testIncrByFloat()
     {
         // incrbyfloat is new in 2.6.0
-        if (version_compare($this->version, "2.5.0", "lt")) {
+        if (version_compare($this->version, "2.5.0") < 0) {
             $this->markTestSkipped();
         }
 
@@ -695,7 +695,7 @@ public function testDelete() {
     }
 
     public function testUnlink() {
-        if (version_compare($this->version, "4.0.0", "lt")) {
+        if (version_compare($this->version, "4.0.0") < 0) {
             $this->markTestSkipped();
             return;
         }
@@ -1031,7 +1031,7 @@ public function testSortAsc() {
         }
 
         // SORT list → [ghi, def, abc]
-        if (version_compare($this->version, "2.5.0", "lt")) {
+        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 {
@@ -1072,7 +1072,7 @@ public function testSortDesc() {
     }
 
     // SORT list → [ghi, abc, def]
-    if (version_compare($this->version, "2.5.0", "lt")) {
+    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:
@@ -1803,7 +1803,7 @@ public function testttl() {
         $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", "gte")) {
+        if(version_compare($this->version, "2.8.0") >= 0) {
             $this->redis->del('x');
             $this->assertEquals($this->redis->ttl('x'), -2);
         }
@@ -1857,7 +1857,7 @@ public function testSlowlog() {
 
     public function testWait() {
         // Closest we can check based on redis commmit history
-        if(version_compare($this->version, '2.9.11', 'lt')) {
+        if(version_compare($this->version, '2.9.11') < 0) {
             $this->markTestSkipped();
             return;
         }
@@ -1906,7 +1906,7 @@ public function testInfo() {
                 "total_commands_processed",
                 "role"
             ];
-            if (version_compare($this->version, "2.5.0", "lt")) {
+            if (version_compare($this->version, "2.5.0") < 0) {
                 array_push($keys,
                     "changes_since_last_save",
                     "bgsave_in_progress",
@@ -1929,7 +1929,7 @@ public function testInfo() {
     public function testInfoCommandStats() {
 
     // INFO COMMANDSTATS is new in 2.6.0
-    if (version_compare($this->version, "2.5.0", "lt")) {
+    if (version_compare($this->version, "2.5.0") < 0) {
         $this->markTestSkipped();
     }
 
@@ -1949,7 +1949,7 @@ public function testSelect() {
     }
 
     public function testSwapDB() {
-        if (version_compare($this->version, "4.0.0", "lt")) {
+        if (version_compare($this->version, "4.0.0") < 0) {
             $this->markTestSkipped();
         }
 
@@ -2063,7 +2063,7 @@ public function testZX() {
         $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0'));
         $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2'));
         $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters
-        if (version_compare($this->version, "3.0.2", "lt")) {
+        if (version_compare($this->version, "3.0.2") < 0) {
             $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1'));
             $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3'));
         } else {
@@ -2357,7 +2357,7 @@ public function testZRangeScoreArg() {
 
     public function testZRangeByLex() {
         /* ZRANGEBYLEX available on versions >= 2.8.9 */
-        if(version_compare($this->version, "2.8.9", "lt")) {
+        if(version_compare($this->version, "2.8.9") < 0) {
             $this->MarkTestSkipped();
             return;
         }
@@ -2376,7 +2376,7 @@ public function testZRangeByLex() {
     }
 
     public function testZLexCount() {
-        if (version_compare($this->version, "2.8.9", "lt")) {
+        if (version_compare($this->version, "2.8.9") < 0) {
             $this->MarkTestSkipped();
             return;
         }
@@ -2406,7 +2406,7 @@ public function testZLexCount() {
     }
 
     public function testZRemRangeByLex() {
-        if (version_compare($this->version, "2.8.9", "lt")) {
+        if (version_compare($this->version, "2.8.9") < 0) {
             $this->MarkTestSkipped();
             return;
         }
@@ -2425,7 +2425,7 @@ public function testZRemRangeByLex() {
     }
 
     public function testBZPop() {
-        if (version_compare($this->version, "5.0.0", "lt")) {
+        if (version_compare($this->version, "5.0.0") < 0) {
             $this->MarkTestSkipped();
             return;
         }
@@ -2447,7 +2447,7 @@ public function testBZPop() {
     }
 
     public function testZPop() {
-        if (version_compare($this->version, "5.0.0", "lt")) {
+        if (version_compare($this->version, "5.0.0") < 0) {
             $this->MarkTestSkipped();
             return;
         }
@@ -2536,7 +2536,7 @@ public function testHashes() {
         $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", "ge")) {
+        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));
@@ -2592,7 +2592,7 @@ public function testHashes() {
         $this->assertTrue('' === $h1['t']);
 
         // hstrlen
-        if (version_compare($this->version, '3.2.0', 'ge')) {
+        if (version_compare($this->version, '3.2.0') >= 0) {
             $this->redis->del('h');
             $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // key doesn't exist
             $this->redis->hSet('h', 'foo', 'bar');
@@ -2623,7 +2623,7 @@ public function testSetRange() {
     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", "lt")) {
+        if (version_compare($this->version, "2.9.0") < 0) {
             $str_small_encoding = "raw";
         } else {
             $str_small_encoding = "embstr";
@@ -4512,7 +4512,7 @@ private function checkCompression($mode, $level)
 
     public function testDumpRestore() {
 
-        if (version_compare($this->version, "2.5.0", "lt")) {
+        if (version_compare($this->version, "2.5.0") < 0) {
             $this->markTestSkipped();
         }
 
@@ -4581,7 +4581,7 @@ private function array_diff_recursive($aArray1, $aArray2) {
 
     public function testScript() {
 
-        if (version_compare($this->version, "2.5.0", "lt")) {
+        if (version_compare($this->version, "2.5.0") < 0) {
             $this->markTestSkipped();
         }
 
@@ -4613,7 +4613,7 @@ public function testScript() {
 
     public function testEval() {
 
-        if (version_compare($this->version, "2.5.0", "lt")) {
+        if (version_compare($this->version, "2.5.0") < 0) {
             $this->markTestSkipped();
         }
 
@@ -4733,7 +4733,7 @@ public function testEval() {
     }
 
     public function testEvalSHA() {
-        if (version_compare($this->version, "2.5.0", "lt")) {
+        if (version_compare($this->version, "2.5.0") < 0) {
             $this->markTestSkipped();
         }
 
@@ -4893,7 +4893,7 @@ public function testReconnectSelect() {
 
     public function testTime() {
 
-        if (version_compare($this->version, "2.5.0", "lt")) {
+        if (version_compare($this->version, "2.5.0") < 0) {
             $this->markTestSkipped();
         }
 
@@ -4935,7 +4935,7 @@ protected function get_keyspace_count($str_db) {
     }
 
     public function testScan() {
-        if(version_compare($this->version, "2.8.0", "lt")) {
+        if(version_compare($this->version, "2.8.0") < 0) {
             $this->markTestSkipped();
             return;
         }
@@ -5004,7 +5004,7 @@ public function testScan() {
     }
 
     public function testHScan() {
-        if(version_compare($this->version, "2.8.0", "lt")) {
+        if (version_compare($this->version, "2.8.0") < 0) {
             $this->markTestSkipped();
             return;
         }
@@ -5044,7 +5044,7 @@ public function testHScan() {
     }
 
     public function testSScan() {
-        if(version_compare($this->version, "2.8.0", "lt")) {
+        if (version_compare($this->version, "2.8.0") < 0) {
             $this->markTestSkipped();
             return;
         }
@@ -5076,7 +5076,7 @@ public function testSScan() {
     }
 
     public function testZScan() {
-        if(version_compare($this->version, "2.8.0", "lt")) {
+        if (version_compare($this->version, "2.8.0") < 0) {
             $this->markTestSkipped();
             return;
         }
@@ -5159,7 +5159,7 @@ protected function createPFKey($str_key, $i_count) {
 
     public function testPFCommands() {
         // Isn't available until 2.8.9
-        if(version_compare($this->version, "2.8.9", "lt")) {
+        if (version_compare($this->version, "2.8.9") < 0) {
             $this->markTestSkipped();
             return;
         }

From 460c8f29239c263e15a093c9bcdb6fb24587ec7d Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 10 Mar 2020 23:16:35 +0200
Subject: [PATCH 1312/1986] Various small changes in cluster_library

---
 cluster_library.c | 56 +++++++++++++++++++----------------------------
 cluster_library.h |  6 +----
 2 files changed, 23 insertions(+), 39 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index c8c103fa42..93332d51f3 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -112,10 +112,9 @@ void cluster_free_reply(clusterReply *reply, int free_data) {
     efree(reply);
 }
 
-static void
+static int
 cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements,
-                                 clusterReply **element, int status_strings,
-                                 int *err)
+                                 clusterReply **element, int status_strings)
 {
     int i;
     size_t sz;
@@ -128,8 +127,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements,
 
         // Bomb out, flag error condition on a communication failure
         if (redis_read_reply_type(sock, &r->type, &len) < 0) {
-            *err = 1;
-            return;
+            return FAILURE;
         }
 
         /* Set our reply len */
@@ -139,8 +137,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements,
             case TYPE_ERR:
             case TYPE_LINE:
                 if (redis_sock_gets(sock,buf,sizeof(buf),&sz) < 0) {
-                    *err = 1;
-                    return;
+                    return FAILURE;
                 }
                 r->len = (long long)sz;
                 if (status_strings) r->str = estrndup(buf, r->len);
@@ -152,8 +149,7 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements,
                 if (r->len >= 0) {
                     r->str = redis_sock_read_bulk_reply(sock,r->len);
                     if (!r->str) {
-                        *err = 1;
-                        return;
+                        return FAILURE;
                     }
                 }
                 break;
@@ -162,17 +158,17 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements,
                     r->elements = r->len;
                     if (r->len > 0) {
                         r->element = ecalloc(r->len,sizeof(clusterReply*));
-                        cluster_multibulk_resp_recursive(sock, r->elements, r->element,
-                            status_strings, err);
+                        if (cluster_multibulk_resp_recursive(sock, r->elements, r->element, status_strings) < 0) {
+                            return FAILURE;
+                        }
                     }
-                    if (*err) return;
                 }
                 break;
             default:
-                *err = 1;
-                return;
+                return FAILURE;
         }
     }
+    return SUCCESS;
 }
 
 /* Return the socket for a slot and slave index */
@@ -215,9 +211,6 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
     r = ecalloc(1, sizeof(clusterReply));
     r->type = type;
 
-    // Error flag in case we go recursive
-    int err = 0;
-
     switch(r->type) {
         case TYPE_INT:
             r->integer = len;
@@ -241,8 +234,10 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
             r->elements = len;
             if (len != (size_t)-1) {
                 r->element = ecalloc(len, sizeof(clusterReply*));
-                cluster_multibulk_resp_recursive(redis_sock, len, r->element,
-                                                 line_reply != NULL, &err);
+                if (cluster_multibulk_resp_recursive(redis_sock, len, r->element, line_reply != NULL) < 0) {
+                    cluster_free_reply(r, 1);
+                    return NULL;
+                }
             }
             break;
         default:
@@ -250,12 +245,6 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
             return NULL;
     }
 
-    // Free/return null on communication error
-    if (err) {
-        cluster_free_reply(r,1);
-        return NULL;
-    }
-
     // Success, return the reply
     return r;
 }
@@ -2644,7 +2633,6 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
             }
             efree(line);
         } else {
-            if (line) efree(line);
             add_next_index_bool(z_result, 0);
         }
     }
@@ -2738,11 +2726,11 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result,
                           long long count, void *ctx)
 {
     char *line;
-    int line_len,i = 0;
+    int line_len, i;
     zval *z_keys = ctx;
 
     // Loop while we've got replies
-    while (count--) {
+    for (i = 0; i < count; ++i) {
         zend_string *zstr = zval_get_string(&z_keys[i]);
         line = redis_sock_read(redis_sock, &line_len);
 
@@ -2761,9 +2749,6 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result,
         // Clean up key context
         zend_string_release(zstr);
         zval_dtor(&z_keys[i]);
-
-        // Move to the next key
-        i++;
     }
 
     // Clean up our keys overall
@@ -2807,7 +2792,7 @@ PHP_REDIS_API redisCachedCluster *cluster_cache_load(HashTable *ht_seeds) {
 
     /* Look for cached slot information */
     h = cluster_hash_seeds(ht_seeds);
-    le = zend_hash_str_find_ptr(&EG(persistent_list), ZSTR_VAL(h), ZSTR_LEN(h));
+    le = zend_hash_find_ptr(&EG(persistent_list), h);
     zend_string_release(h);
 
     if (le != NULL) {
@@ -2831,8 +2816,11 @@ PHP_REDIS_API int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes) {
     zend_string *hash;
 
     /* Short circuit if caching is disabled or there aren't any seeds */
-    if (!SLOT_CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0)
-        return !SLOT_CACHING_ENABLED() ? SUCCESS : FAILURE;
+    if (!SLOT_CACHING_ENABLED()) {
+        return SUCCESS;
+    } else if (zend_hash_num_elements(ht_seeds) == 0) {
+        return FAILURE;
+    }
 
     /* Construct our cache */
     hash = cluster_hash_seeds(ht_seeds);
diff --git a/cluster_library.h b/cluster_library.h
index 784087ebbb..5d148b59f9 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -89,11 +89,7 @@
 /* Helper to either return a bool value or add it to MULTI response */
 #define CLUSTER_RETURN_BOOL(c, b) \
     if(CLUSTER_IS_ATOMIC(c)) { \
-        if(b==1) {\
-            RETURN_TRUE; \
-        } else {\
-            RETURN_FALSE; \
-        } \
+        RETURN_BOOL(b); \
     } else { \
         add_next_index_bool(&c->multi_resp, b); \
     }

From b7f9df758b30187864012d5cd831dbbc5fa053d0 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 9 Mar 2020 19:55:52 +0200
Subject: [PATCH 1313/1986] Issue #1721

Sanity check for redis_sock->stream before closing.
---
 library.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/library.c b/library.c
index c81e2eb9d2..d629ebd897 100644
--- a/library.c
+++ b/library.c
@@ -1892,8 +1892,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
                 if (redis_sock_check_liveness(redis_sock) == SUCCESS) {
                     redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
                     return SUCCESS;
+                } else if (redis_sock->stream) {
+                    php_stream_pclose(redis_sock->stream);
                 }
-                php_stream_pclose(redis_sock->stream);
                 p->nb_active--;
             }
 

From a8e2b021f9eb51ad3ed0cc89064e2f004c56f8ba Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 16 Mar 2020 22:54:46 +0200
Subject: [PATCH 1314/1986] Fix arginfo for Redis::zadd

---
 common.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/common.h b/common.h
index f9aed17a40..bb7b6ea7ec 100644
--- a/common.h
+++ b/common.h
@@ -449,6 +449,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zadd, 0, 0, 3)
     ZEND_ARG_INFO(0, key)
     ZEND_ARG_INFO(0, score)
     ZEND_ARG_INFO(0, value)
+    ZEND_ARG_VARIADIC_INFO(0, extra_args)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_zincrby, 0, 0, 3)

From 6b82d40ed55ee249bfc0c5f37e48c45855b22a58 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 16 Mar 2020 23:08:56 +0200
Subject: [PATCH 1315/1986] Update Changelog.md

---
 Changelog.md | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/Changelog.md b/Changelog.md
index 053953af48..c160633a2e 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,6 +5,41 @@ All changes to phpredis will be documented in this file.
 We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [Unreleased]
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack.com](https://audiomack.com)
+- [Till Krüss](https://github.com/tillkruss)
+
+### Fixed
+
+- Fix arginfo for Redis::zadd
+  [a8e2b021](https://github.com/phpredis/phpredis/commit/a8e2b021)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Fix segfault on closing persistent stream
+  [b7f9df75](https://github.com/phpredis/phpredis/commit/b7f9df75)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+### Changed
+
+- Various small changes in cluster_library
+  [460c8f29](https://github.com/phpredis/phpredis/commit/460c8f29)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- PHP 8 compatibility
+  [9ee94ca4](https://github.com/phpredis/phpredis/commit/9ee94ca4)
+  [7e4c7b3e](https://github.com/phpredis/phpredis/commit/7e4c7b3e)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Refactor PHPREDIS_GET_OBJECT macro
+  [d5dadaf6](https://github.com/phpredis/phpredis/commit/d5dadaf6)
+  [190c0d34](https://github.com/phpredis/phpredis/commit/190c0d34)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+---
+
 ## [5.2.0] - 2020-03-02 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.2.0), [PECL](https://pecl.php.net/package/redis/5.2.0))
 
 *There were no changes between 5.2.0RC2 and 5.2.0*

From a42cf189a776fc43acf47ca519f1d7385cc27f2f Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 19 Mar 2020 23:00:25 +0200
Subject: [PATCH 1316/1986] Remove duplicate definitions

---
 redis_cluster.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/redis_cluster.h b/redis_cluster.h
index 68a0273cd3..b7f4ae0fbe 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -5,10 +5,6 @@
 #include 
 #include 
 
-/* Redis cluster hash slots and N-1 which we'll use to find it */
-#define REDIS_CLUSTER_SLOTS 16384
-#define REDIS_CLUSTER_MOD   (REDIS_CLUSTER_SLOTS-1)
-
 /* Get attached object context */
 #define GET_CONTEXT() PHPREDIS_ZVAL_GET_OBJECT(redisCluster, getThis())
 

From bbcf32a37fa856ba0b50b489ba05bd3d43800fcc Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 21 Mar 2020 17:04:15 +0200
Subject: [PATCH 1317/1986] Remove unused declarations

---
 redis_cluster.h | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/redis_cluster.h b/redis_cluster.h
index b7f4ae0fbe..379d034c15 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -91,19 +91,12 @@
     } \
     resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); 
 
-/* For the creation of RedisCluster specific exceptions */
-PHP_REDIS_API zend_class_entry *rediscluster_get_exception_base(int root);
-
 /* Create cluster context */
 zend_object *create_cluster_context(zend_class_entry *class_type);
 
 /* Free cluster context struct */
 void free_cluster_context(zend_object *object);
 
-
-/* Inittialize our class with PHP */
-void init_rediscluster(void);
-
 /* RedisCluster method implementation */
 PHP_METHOD(RedisCluster, __construct);
 PHP_METHOD(RedisCluster, close);

From 48b3dd6705289a9af41f97a9b11c8ab35cf95593 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 21 Mar 2020 17:07:17 +0200
Subject: [PATCH 1318/1986] Update Changelog.md

---
 Changelog.md | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index c160633a2e..c778da2c01 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -12,16 +12,6 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - [Audiomack.com](https://audiomack.com)
 - [Till Krüss](https://github.com/tillkruss)
 
-### Fixed
-
-- Fix arginfo for Redis::zadd
-  [a8e2b021](https://github.com/phpredis/phpredis/commit/a8e2b021)
-  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
-
-- Fix segfault on closing persistent stream
-  [b7f9df75](https://github.com/phpredis/phpredis/commit/b7f9df75)
-  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
-
 ### Changed
 
 - Various small changes in cluster_library
@@ -40,6 +30,25 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ---
 
+## [5.2.1] - 2020-03-19 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.2.1), [PECL](https://pecl.php.net/package/redis/5.2.1))
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack.com](https://audiomack.com)
+- [Till Krüss](https://github.com/tillkruss)
+
+### Fixed
+
+- Fix arginfo for Redis::zadd
+  [a8e2b021](https://github.com/phpredis/phpredis/commit/a8e2b021)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Fix segfault on closing persistent stream
+  [b7f9df75](https://github.com/phpredis/phpredis/commit/b7f9df75)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+---
+
 ## [5.2.0] - 2020-03-02 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.2.0), [PECL](https://pecl.php.net/package/redis/5.2.0))
 
 *There were no changes between 5.2.0RC2 and 5.2.0*

From 8c45816dbf4746f6557f83332be874bd78b5ce34 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 21 Mar 2020 17:25:29 +0200
Subject: [PATCH 1319/1986] Set redis_sock->stream to NULL to avoid
 use-after-free

---
 library.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library.c b/library.c
index d629ebd897..7bf1e69e26 100644
--- a/library.c
+++ b/library.c
@@ -1894,6 +1894,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
                     return SUCCESS;
                 } else if (redis_sock->stream) {
                     php_stream_pclose(redis_sock->stream);
+                    redis_sock->stream = NULL;
                 }
                 p->nb_active--;
             }

From dd66fceeb232f9e1fb0a26373949e810180dc5fc Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 30 Mar 2020 00:15:21 +0300
Subject: [PATCH 1320/1986] Update session.save_path in
 RedisClusterTest::sessionTest

---
 tests/RedisClusterTest.php | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 4d7ba73029..292b24fe07 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -639,9 +639,7 @@ public function testReplyLiteral() {
     public function testSession()
     {
         @ini_set('session.save_handler', 'rediscluster');
-        @ini_set('session.save_path', implode('&', array_map(function ($seed) {
-            return 'seed[]=' . $seed;
-        }, self::$_arr_node_map)) . '&failover=error');
+        @ini_set('session.save_path', $this->getFullHostPath() . '&failover=error');
         if (!@session_start()) {
             return $this->markTestSkipped();
         }
@@ -685,11 +683,9 @@ public function testConnectionPool() {
      */
     protected function getFullHostPath()
     {
-        $hosts = array_map(function ($host) {
-            return 'seed[]=' . $host . '';
-        }, self::$_arr_node_map);
-
-        return implode('&', $hosts);
+        return implode('&', array_map(function ($host) {
+            return 'seed[]=' . $host;
+        }, self::$_arr_node_map));
     }
 }
 ?>

From e37f38a39eb4bece8f49ebd0652112dc992084a0 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 27 Mar 2020 21:21:55 +0200
Subject: [PATCH 1321/1986] requirepass

---
 .travis.yml                | 10 +++----
 tests/RedisArrayTest.php   | 59 ++++++++++++++++++++++++++++----------
 tests/RedisClusterTest.php |  8 +++---
 tests/RedisTest.php        | 22 ++++++++++++--
 tests/TestSuite.php        |  5 +++-
 5 files changed, 76 insertions(+), 28 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 675be1aa94..9de767c688 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -41,11 +41,11 @@ before_install:
 install: make install
 before_script:
   - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap
-  - redis-server --port 0 --daemonize yes --unixsocket /tmp/redis.sock
-  - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes; done
-  - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
-  - for PORT in $(seq 26379 26380); do wget download.redis.io/redis-stable/sentinel.conf -O $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
-  - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3
+  - redis-server --port 0 --daemonize yes --requirepass phpredis --unixsocket /tmp/redis.sock
+  - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes --requirepass phpredis; done
+  - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --requirepass phpredis --masterauth phpredis; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
+  - for PORT in $(seq 26379 26380); do wget download.redis.io/redis-stable/sentinel.conf -O $PORT.conf; echo sentinel auth-pass mymaster phpredis >> $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
+  - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 -a phpredis
   - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
 script:
   - php tests/TestRedis.php --class Redis
diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php
index b46a06ee92..303cdf45ec 100644
--- a/tests/RedisArrayTest.php
+++ b/tests/RedisArrayTest.php
@@ -55,7 +55,11 @@ public function setUp() {
         }
 
         global $newRing, $oldRing, $useIndex;
-        $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
+        $options = ['previous' => $oldRing, 'index' => $useIndex];
+        if (self::AUTH) {
+            $options['auth'] = self::AUTH;
+        }
+        $this->ra = new RedisArray($newRing, $options);
         $this->min_version = getMinVersion($this->ra);
     }
 
@@ -80,6 +84,9 @@ public function testMSet() {
 
             $r = new Redis;
             $r->pconnect($host, (int)$port);
+            if (self::AUTH) {
+                $this->assertTrue($r->auth(self::AUTH));
+            }
             $this->assertTrue($v === $r->get($k));
         }
     }
@@ -118,9 +125,11 @@ public function testKeyLocality() {
 
         // with common hashing function
         global $newRing, $oldRing, $useIndex;
-        $this->ra = new RedisArray($newRing, array('previous' => $oldRing,
-                'index' => $useIndex,
-                'function' => 'custom_hash'));
+        $options = ['previous' => $oldRing, 'index' => $useIndex, 'function' => 'custom_hash'];
+        if (self::AUTH) {
+            $options['auth'] = self::AUTH;
+        }
+        $this->ra = new RedisArray($newRing, $options);
 
         // basic key locality with custom hash
         $this->addData('fb'.rand());
@@ -139,10 +148,11 @@ public function customDistributor($key)
     public function testKeyDistributor()
     {
         global $newRing, $useIndex;
-        $this->ra = new RedisArray($newRing, array(
-                'index' => $useIndex,
-                'function' => 'custom_hash',
-                'distributor' => array($this, "customDistributor")));
+        $options = ['index' => $useIndex, 'function' => 'custom_hash', 'distributor' => [$this, "customDistributor"]];
+        if (self::AUTH) {
+            $options['auth'] = self::AUTH;
+        }
+        $this->ra = new RedisArray($newRing, $options);
 
         // custom key distribution function.
         $this->addData('fb'.rand());
@@ -207,9 +217,12 @@ public function setUp() {
         }
 
         global $newRing, $oldRing, $useIndex;
-
+        $options = ['previous' => $oldRing, 'index' => $useIndex];
+        if (self::AUTH) {
+            $options['auth'] = self::AUTH;
+        }
         // create array
-        $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
+        $this->ra = new RedisArray($newRing, $options);
         $this->min_version = getMinVersion($this->ra);
     }
 
@@ -221,6 +234,9 @@ public function testFlush() {
 
             $r = new Redis();
             $r->pconnect($host, (int)$port, 0);
+            if (self::AUTH) {
+                $this->assertTrue($r->auth(self::AUTH));
+            }
             $r->flushdb();
         }
     }
@@ -354,9 +370,12 @@ public function setUp() {
         }
 
         global $newRing, $oldRing, $useIndex;
-
+        $options = ['previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE];
+        if (self::AUTH) {
+            $options['auth'] = self::AUTH;
+        }
         // create array
-        $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE));
+        $this->ra = new RedisArray($newRing, $options);
         $this->min_version = getMinVersion($this->ra);
     }
 
@@ -397,6 +416,9 @@ public function testAllKeysHaveBeenMigrated() {
 
             $r = new Redis;
             $r->pconnect($host, $port);
+            if (self::AUTH) {
+                $this->assertTrue($r->auth(self::AUTH));
+            }
 
             $this->assertTrue($v === $r->get($k));  // check that the key has actually been migrated to the new node.
         }
@@ -410,9 +432,12 @@ class Redis_Multi_Exec_Test extends TestSuite {
 
     public function setUp() {
         global $newRing, $oldRing, $useIndex;
-
+        $options = ['previous' => $oldRing, 'index' => $useIndex];
+        if (self::AUTH) {
+            $options['auth'] = self::AUTH;
+        }
         // create array
-        $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
+        $this->ra = new RedisArray($newRing, $options);
         $this->min_version = getMinVersion($this->ra);
     }
 
@@ -552,8 +577,12 @@ class Redis_Distributor_Test extends TestSuite {
 
     public function setUp() {
         global $newRing, $oldRing, $useIndex;
+        $options = ['previous' => $oldRing, 'index' => $useIndex, 'distributor' => [$this, 'distribute']];
+        if (self::AUTH) {
+            $options['auth'] = self::AUTH;
+        }
         // create array
-        $this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'distributor' => array($this, 'distribute')));
+        $this->ra = new RedisArray($newRing, $options);
         $this->min_version = getMinVersion($this->ra);
     }
 
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 292b24fe07..10750a1b5b 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -87,7 +87,7 @@ public function setUp() {
 
     /* Override newInstance as we want a RedisCluster object */
     protected function newInstance() {
-        return new RedisCluster(NULL, self::$_arr_node_map);
+        return new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, self::AUTH);
     }
 
     /* Overrides for RedisTest where the function signature is different.  This
@@ -654,7 +654,7 @@ public function testSlotCache() {
 
         $pong = 0;
         for ($i = 0; $i < 10; $i++) {
-            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map);
+            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, self::AUTH);
             $pong += $obj_rc->ping("key:$i");
         }
 
@@ -670,7 +670,7 @@ public function testConnectionPool() {
 
         $pong = 0;
         for ($i = 0; $i < 10; $i++) {
-            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true);
+            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, self::AUTH);
             $pong += $obj_rc->ping("key:$i");
         }
 
@@ -685,7 +685,7 @@ protected function getFullHostPath()
     {
         return implode('&', array_map(function ($host) {
             return 'seed[]=' . $host;
-        }, self::$_arr_node_map));
+        }, self::$_arr_node_map)) . (self::AUTH ? '&auth=' . self::AUTH : '');
     }
 }
 ?>
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 1c9bcd3916..9ba859ab92 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5,7 +5,6 @@
 class Redis_Test extends TestSuite
 {
     const PORT = 6379;
-    const AUTH = NULL; //replace with a string to use Redis authentication
 
     /* City lat/long */
     protected $cities = [
@@ -54,6 +53,15 @@ protected function mstime() {
         return round(microtime(true)*1000);
     }
 
+    protected function getFullHostPath()
+    {
+        $fullHostPath = parent::getFullHostPath();
+        if (isset($fullHostPath) && self::AUTH) {
+            $fullHostPath .= '?auth=' . self::AUTH;
+        }
+        return $fullHostPath;
+    }
+
     protected function newInstance() {
         $r = new Redis();
 
@@ -5943,7 +5951,9 @@ public function testUnixSocket() {
                 } else {
                     @$obj_r->connect($arr_args[0]);
                 }
-
+                if (self::AUTH) {
+                    $this->assertTrue($obj_r->auth(self::AUTH));
+                }
                 $this->assertTrue($obj_r->ping());
             }
         } catch (Exception $ex) {
@@ -5970,6 +5980,9 @@ public function testHighPorts() {
             $obj_r = new Redis();
             try {
                 @$obj_r->connect('localhost', $port);
+                if (self::AUTH) {
+                    $this->assertTrue($obj_r->auth(self::AUTH));
+                }
                 $this->assertTrue($obj_r->ping());
             } catch(Exception $ex) {
                 $this->assertTrue(false);
@@ -6155,7 +6168,10 @@ public function testMultipleConnect() {
 
         for($i = 0; $i < 5; $i++) {
             $this->redis->connect($host, $port);
-            $this->assertEquals(true, $this->redis->ping());
+            if (self::AUTH) {
+                $this->assertTrue($this->redis->auth(self::AUTH));
+            }
+            $this->assertTrue($this->redis->ping());
         }
     }
 
diff --git a/tests/TestSuite.php b/tests/TestSuite.php
index 961ce191bc..40ac9cb4ed 100644
--- a/tests/TestSuite.php
+++ b/tests/TestSuite.php
@@ -4,7 +4,10 @@
 class TestSkippedException extends Exception {}
 
 // phpunit is such a pain to install, we're going with pure-PHP here.
-class TestSuite {
+class TestSuite
+{
+    const AUTH = 'phpredis'; //replace with a string to use Redis authentication
+
     /* Host the tests will use */
     private $str_host;
 

From 35372a1f64f643cf4ce52c62c61e326e7d6a1e6e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 12 Mar 2020 00:21:21 +0200
Subject: [PATCH 1322/1986] Authenticate in redis_sock_server_open

---
 cluster_library.c  | 13 +------------
 cluster_library.h  |  2 +-
 common.h           |  3 ++-
 library.c          | 22 ++++++++++++++++-----
 redis.c            |  2 +-
 redis_array.c      | 29 +++++++---------------------
 redis_array.h      |  2 --
 redis_array_impl.c | 48 +++++++++++++++++++++++-----------------------
 redis_session.c    |  7 -------
 9 files changed, 53 insertions(+), 75 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 93332d51f3..191947c574 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -249,17 +249,6 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
     return r;
 }
 
-/* Helper to open connection and send AUTH if necessary */
-static zend_always_inline int
-cluster_sock_open(RedisSock *redis_sock)
-{
-    zend_bool need_auth = (redis_sock->auth && redis_sock->status != REDIS_SOCK_STATUS_CONNECTED);
-    if (!redis_sock_server_open(redis_sock) && (!need_auth || !redis_sock_auth(redis_sock ))) {
-        return SUCCESS;
-    }
-    return FAILURE;
-}
-
 /*
  * Helpers to send various 'control type commands to a specific node, e.g.
  * MULTI, ASKING, READONLY, READWRITE, etc
@@ -1127,7 +1116,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c) {
     // Iterate over seeds until we can get slots
     ZEND_HASH_FOREACH_PTR(c->seeds, seed) {
         // Attempt to connect to this seed node
-        if (seed == NULL || cluster_sock_open(seed) != 0) {
+        if (seed == NULL || redis_sock_server_open(seed) != SUCCESS) {
             continue;
         }
 
diff --git a/cluster_library.h b/cluster_library.h
index 5d148b59f9..fbf050fe96 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -62,7 +62,7 @@
 
 /* Protected sending of data down the wire to a RedisSock->stream */
 #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \
-    (sock && !cluster_sock_open(sock) && sock->stream && !redis_check_eof(sock, 1 ) && \
+    (sock && !redis_sock_server_open(sock) && sock->stream && !redis_check_eof(sock, 1 ) && \
      php_stream_write(sock->stream, buf, len)==len)
 
 /* Macro to read our reply type character */
diff --git a/common.h b/common.h
index bb7b6ea7ec..e70a8ca027 100644
--- a/common.h
+++ b/common.h
@@ -24,7 +24,8 @@
 typedef enum {
     REDIS_SOCK_STATUS_FAILED = -1,
     REDIS_SOCK_STATUS_DISCONNECTED,
-    REDIS_SOCK_STATUS_CONNECTED
+    REDIS_SOCK_STATUS_CONNECTED,
+    REDIS_SOCK_STATUS_READY
 } redis_sock_status;
 
 #define _NL "\r\n"
diff --git a/library.c b/library.c
index 7bf1e69e26..541789ee1a 100644
--- a/library.c
+++ b/library.c
@@ -234,6 +234,7 @@ redis_check_eof(RedisSock *redis_sock, int no_throw)
                         errmsg = "AUTH failed while reconnecting";
                         break;
                     }
+                    redis_sock->status = REDIS_SOCK_STATUS_READY;
                     /* If we're using a non-zero db, reselect it */
                     if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) {
                         errmsg = "SELECT failed while reconnecting";
@@ -1890,7 +1891,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
                 zend_llist_remove_tail(&p->list);
 
                 if (redis_sock_check_liveness(redis_sock) == SUCCESS) {
-                    redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
+                    redis_sock->status = REDIS_SOCK_STATUS_READY;
                     return SUCCESS;
                 } else if (redis_sock->stream) {
                     php_stream_pclose(redis_sock->stream);
@@ -1975,12 +1976,23 @@ redis_sock_server_open(RedisSock *redis_sock)
 {
     if (redis_sock) {
         switch (redis_sock->status) {
-        case REDIS_SOCK_STATUS_FAILED:
-            return FAILURE;
         case REDIS_SOCK_STATUS_DISCONNECTED:
-            return redis_sock_connect(redis_sock);
-        default:
+            if (redis_sock_connect(redis_sock) != SUCCESS) {
+                break;
+            } else if (redis_sock->status == REDIS_SOCK_STATUS_READY) {
+                return SUCCESS;
+            }
+            // fall through
+        case REDIS_SOCK_STATUS_CONNECTED:
+            if (redis_sock->auth && redis_sock_auth(redis_sock) != SUCCESS) {
+                break;
+            }
+            redis_sock->status = REDIS_SOCK_STATUS_READY;
+            // fall through
+        case REDIS_SOCK_STATUS_READY:
             return SUCCESS;
+        default:
+            return FAILURE;
         }
     }
     return FAILURE;
diff --git a/redis.c b/redis.c
index ff0bd02986..2e0aaff19e 100644
--- a/redis.c
+++ b/redis.c
@@ -654,7 +654,7 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS)
     if((zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
        &object, redis_ce) == FAILURE) ||
        (redis_sock = redis_sock_get(object, 1)) == NULL ||
-       redis_sock->status != REDIS_SOCK_STATUS_CONNECTED)
+       redis_sock->status < REDIS_SOCK_STATUS_CONNECTED)
     {
         return NULL;
     }
diff --git a/redis_array.c b/redis_array.c
index f5ada2768e..7f8fa129d9 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -220,21 +220,6 @@ redis_array_get(zval *id)
     return NULL;
 }
 
-PHP_REDIS_API int
-ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[])
-{
-    if (object) {
-        redis_object *redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, object);
-        if (redis->sock->auth &&
-            redis->sock->status != REDIS_SOCK_STATUS_CONNECTED &&
-            redis_sock_server_open(redis->sock) == SUCCESS
-        ) {
-            redis_sock_auth(redis->sock);
-        }
-    }
-    return call_user_function(function_table, object, function_name, retval_ptr, param_count, params);
-}
-
 /* {{{ proto RedisArray RedisArray::__construct()
     Public constructor */
 PHP_METHOD(RedisArray, __construct)
@@ -417,7 +402,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i
 
     /* multi/exec */
     if(ra->z_multi_exec) {
-        ra_call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs);
+        call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs);
         zval_dtor(return_value);
         zval_dtor(&z_fun);
         for (i = 0; i < argc; ++i) {
@@ -435,7 +420,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i
         /* add MULTI + SADD */
         ra_index_multi(redis_inst, MULTI);
         /* call using discarded temp value and extract exec results after. */
-        ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs);
+        call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs);
         zval_dtor(return_value);
 
         /* add keys to index. */
@@ -444,7 +429,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i
         /* call EXEC */
         ra_index_exec(redis_inst, return_value, 0);
     } else { /* call directly through. */
-        ra_call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs);
+        call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs);
 
         if (!b_write_cmd) {
             /* check if we have an error. */
@@ -662,7 +647,7 @@ multihost_distribute_call(RedisArray *ra, zval *return_value, zval *z_fun, int a
     /* Iterate our RedisArray nodes */
     for (i = 0; i < ra->count; ++i) {
         /* Call each node in turn */
-        ra_call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_tmp, argc, argv);
+        call_user_function(&redis_array_ce->function_table, &ra->redis[i], z_fun, &z_tmp, argc, argv);
 
         /* Add the result for this host */
         add_assoc_zval_ex(return_value, ZSTR_VAL(ra->hosts[i]), ZSTR_LEN(ra->hosts[i]), &z_tmp);
@@ -975,7 +960,7 @@ PHP_METHOD(RedisArray, mget)
         /* prepare call */
         ZVAL_STRINGL(&z_fun, "MGET", 4);
         /* call MGET on the node */
-        ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
+        call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
         zval_dtor(&z_fun);
 
         /* cleanup args array */
@@ -1119,7 +1104,7 @@ PHP_METHOD(RedisArray, mset)
         ZVAL_STRINGL(&z_fun, "MSET", 4);
 
         /* call */
-        ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
+        call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
         zval_dtor(&z_fun);
         zval_dtor(&z_ret);
 
@@ -1251,7 +1236,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
         }
 
         /* call */
-        ra_call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
+        call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
 
         if(ra->index) {
             zval_dtor(&z_ret);
diff --git a/redis_array.h b/redis_array.h
index 5cebc60d74..34460b10a8 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -69,6 +69,4 @@ typedef struct RedisArray_ {
 zend_object *create_redis_array_object(zend_class_entry *ce);
 void free_redis_array_object(zend_object *object);
 
-PHP_REDIS_API int ra_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]);
-
 #endif
diff --git a/redis_array_impl.c b/redis_array_impl.c
index c3cfd2b623..d218ee6cc6 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -78,7 +78,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in
         if (!b_lazy_connect)
         {
             /* connect */
-            if (redis_sock_server_open(redis->sock) < 0 || (auth && redis_sock_auth(redis->sock ) < 0)) {
+            if (redis_sock_server_open(redis->sock) < 0) {
                 zval_dtor(&z_cons);
                 ra->count = ++i;
                 return NULL;
@@ -484,7 +484,7 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len)
     ZVAL_NULL(&z_ret);
     /* call extraction function */
     ZVAL_STRINGL(&z_argv, key, key_len);
-    ra_call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv);
+    call_user_function(EG(function_table), NULL, &ra->z_fun, &z_ret, 1, &z_argv);
 
     if (Z_TYPE(z_ret) == IS_STRING) {
         out = zval_get_string(&z_ret);
@@ -525,7 +525,7 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len)
     ZVAL_NULL(&z_ret);
     /* call extraction function */
     ZVAL_STRINGL(&z_argv, key, key_len);
-    ra_call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv);
+    call_user_function(EG(function_table), NULL, &ra->z_dist, &z_ret, 1, &z_argv);
 
     ret = (Z_TYPE(z_ret) == IS_LONG) ? Z_LVAL(z_ret) : -1;
 
@@ -629,7 +629,7 @@ ra_index_multi(zval *z_redis, long multi_value) {
     /* run MULTI */
     ZVAL_STRINGL(&z_fun_multi, "MULTI", 5);
     ZVAL_LONG(&z_args[0], multi_value);
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args);
     zval_dtor(&z_fun_multi);
     zval_dtor(&z_ret);
 }
@@ -659,7 +659,7 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis) {
     }
 
     /* run cmd */
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args);
 
     zval_dtor(&z_args[0]);
     zval_dtor(&z_fun);
@@ -715,7 +715,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis) {
     ZVAL_STRINGL(&z_args[1], key, key_len);
 
     /* run SADD */
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args);
     zval_dtor(&z_fun_sadd);
     zval_dtor(&z_args[1]);
     zval_dtor(&z_args[0]);
@@ -729,7 +729,7 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all) {
 
     /* run EXEC */
     ZVAL_STRINGL(&z_fun_exec, "EXEC", 4);
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL);
     zval_dtor(&z_fun_exec);
 
     /* extract first element of exec array and put into return_value. */
@@ -756,7 +756,7 @@ ra_index_discard(zval *z_redis, zval *return_value) {
 
     /* run DISCARD */
     ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7);
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL);
 
     zval_dtor(&z_fun_discard);
     zval_dtor(&z_ret);
@@ -769,7 +769,7 @@ ra_index_unwatch(zval *z_redis, zval *return_value) {
 
     /* run UNWATCH */
     ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7);
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL);
 
     zval_dtor(&z_fun_unwatch);
     zval_dtor(&z_ret);
@@ -809,14 +809,14 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long
     /* run TYPE */
     ZVAL_NULL(&z_ret);
     ZVAL_STRINGL(&z_fun, "TYPE", 4);
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg);
     zval_dtor(&z_fun);
     zval_dtor(&z_ret);
 
     /* run TYPE */
     ZVAL_NULL(&z_ret);
     ZVAL_STRINGL(&z_fun, "TTL", 3);
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg);
     zval_dtor(&z_fun);
     zval_dtor(&z_ret);
 
@@ -848,7 +848,7 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len) {
     ZVAL_STRINGL(&z_args[0], PHPREDIS_INDEX_NAME, sizeof(PHPREDIS_INDEX_NAME) - 1);
     ZVAL_STRINGL(&z_args[1], key, key_len);
 
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args);
 
     /* cleanup */
     zval_dtor(&z_fun_srem);
@@ -870,7 +870,7 @@ ra_del_key(const char *key, int key_len, zval *z_from) {
     /* run DEL on source */
     ZVAL_STRINGL(&z_fun_del, "DEL", 3);
     ZVAL_STRINGL(&z_args[0], key, key_len);
-    ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args);
+    call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args);
     zval_dtor(&z_fun_del);
     zval_dtor(&z_args[0]);
     zval_dtor(&z_ret);
@@ -895,7 +895,7 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl) {
         ZVAL_STRINGL(&z_fun_expire, "EXPIRE", 6);
         ZVAL_STRINGL(&z_args[0], key, key_len);
         ZVAL_LONG(&z_args[1], ttl);
-        ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args);
+        call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args);
         zval_dtor(&z_fun_expire);
         zval_dtor(&z_args[0]);
         zval_dtor(&z_ret);
@@ -919,7 +919,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) {
     ZVAL_STRINGL(&z_args[1], "0", 1);
     ZVAL_STRINGL(&z_args[2], "-1", 2);
     ZVAL_BOOL(&z_args[3], 1);
-    ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args);
+    call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args);
     zval_dtor(&z_fun_zrange);
     zval_dtor(&z_args[2]);
     zval_dtor(&z_args[1]);
@@ -957,7 +957,7 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) {
 
     /* run ZADD on target */
     ZVAL_STRINGL(&z_fun_zadd, "ZADD", 4);
-    ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args);
+    call_user_function(&redis_ce->function_table, z_to, &z_fun_zadd, &z_ret_dest, 1 + 2 * count, z_zadd_args);
 
     /* Expire if needed */
     ra_expire_key(key, key_len, z_to, ttl);
@@ -984,7 +984,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl)
     /* run GET on source */
     ZVAL_STRINGL(&z_fun_get, "GET", 3);
     ZVAL_STRINGL(&z_args[0], key, key_len);
-    ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args);
+    call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args);
     zval_dtor(&z_fun_get);
 
     if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */
@@ -1000,14 +1000,14 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl)
         ZVAL_LONG(&z_args[1], ttl);
         ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */
         zval_dtor(&z_ret); /* free memory from our previous call */
-        ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args);
+        call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args);
         /* cleanup */
         zval_dtor(&z_args[2]);
     } else {
         ZVAL_STRINGL(&z_fun_set, "SET", 3);
         ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */
         zval_dtor(&z_ret); /* free memory from our previous return value */
-        ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args);
+        call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args);
         /* cleanup */
         zval_dtor(&z_args[1]);
     }
@@ -1025,7 +1025,7 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) {
     /* run HGETALL on source */
     ZVAL_STRINGL(&z_args[0], key, key_len);
     ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7);
-    ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args);
+    call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args);
     zval_dtor(&z_fun_hgetall);
 
     if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */
@@ -1037,7 +1037,7 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) {
 
     /* run HMSET on target */
     ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5);
-    ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args);
+    call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args);
     zval_dtor(&z_fun_hmset);
     zval_dtor(&z_ret_dest);
 
@@ -1072,7 +1072,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to,
         ZVAL_STRING(&z_retrieve_args[i], cmd_list[i]);
     }
 
-    ra_call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args);
+    call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args);
 
     /* cleanup */
     zval_dtor(&z_fun_retrieve);
@@ -1104,7 +1104,7 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to,
     /* Clean up our input return value */
     zval_dtor(&z_ret);
 
-    ra_call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args);
+    call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args);
 
     /* cleanup */
     zval_dtor(&z_fun_sadd);
@@ -1229,7 +1229,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool
         ZVAL_STRING(&z_argv, "*");
     }
     ZVAL_NULL(&z_ret);
-    ra_call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv);
+    call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv);
     zval_dtor(&z_argv);
     zval_dtor(&z_fun);
 
diff --git a/redis_session.c b/redis_session.c
index ead70bee1b..8fc1778e51 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -173,14 +173,7 @@ redis_pool_get_sock(redis_pool *pool, const char *key) {
 
     for(i = 0; i < pool->totalWeight;) {
         if (pos >= i && pos < i + rpm->weight) {
-            int needs_auth = 0;
-            if (rpm->redis_sock->auth && rpm->redis_sock->status != REDIS_SOCK_STATUS_CONNECTED) {
-                    needs_auth = 1;
-            }
             if (redis_sock_server_open(rpm->redis_sock) == 0) {
-                if (needs_auth) {
-                    redis_sock_auth(rpm->redis_sock);
-                }
                 if (rpm->database >= 0) { /* default is -1 which leaves the choice to redis. */
                     redis_pool_member_select(rpm);
                 }

From 6808cd6a7a30c006bc9133c3058dfe18551d7734 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Fri, 10 Apr 2020 13:11:24 -0700
Subject: [PATCH 1323/1986] Fix documentation to show lPush and rPush are
 variadic. (#1737)

Addresses #1734
---
 README.markdown | 38 ++++++++++++++++++++++----------------
 1 file changed, 22 insertions(+), 16 deletions(-)

diff --git a/README.markdown b/README.markdown
index 949f1837d9..9edc3dca83 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1923,11 +1923,12 @@ $redis->lPop('key1'); /* key1 => [ 'B', 'C' ] */
 
 ### lPush
 -----
-_**Description**_: Adds the string value to the head (left) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned.
+_**Description**_: Adds one or more values to the head of a LIST.  Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned.
 
-##### *Parameters*
-*key*  
-*value* String, value to push in key
+##### *Prototype*
+~~~php
+$redis->lPush($key, $entry [, $entry, $entry]);
+~~~
 
 ##### *Return value*
 *LONG* The new length of the list in case of success, `FALSE` in case of Failure.
@@ -1935,10 +1936,13 @@ _**Description**_: Adds the string value to the head (left) of the list. Creates
 ##### *Examples*
 ~~~php
 $redis->del('key1');
-$redis->lPush('key1', 'C'); // returns 1
-$redis->lPush('key1', 'B'); // returns 2
-$redis->lPush('key1', 'A'); // returns 3
-/* key1 now points to the following list: [ 'A', 'B', 'C' ] */
+$redis->lPush('key1', 'F'); // returns 1
+$redis->lPush('key1', 'E'); // returns 2
+$redis->lPush('key1', 'D'); // returns 3
+/* key1 now contains: [ 'D', 'E', 'F' ] */
+
+$redis->lPush('key1', 'C', 'B', 'A'); // Returns 6
+/* key1 now contains: [ 'A', 'B', 'C', 'D', 'E', 'F' ]
 ~~~
 
 ### lPushx
@@ -2127,11 +2131,12 @@ array(3) {
 
 ### rPush
 -----
-_**Description**_: Adds the string value to the tail (right) of the list. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned.
+_**Description**_: Adds one or more entries to the tail of a LIST. Redis will create the list if it doesn't exist.
 
-##### *Parameters*
-*key*  
-*value* String, value to push in key
+##### *Prototype*
+~~~php
+$redis->rPush($key, $entry [, $entry, $entry]);
+~~~
 
 ##### *Return value*
 *LONG* The new length of the list in case of success, `FALSE` in case of Failure.
@@ -2139,10 +2144,11 @@ _**Description**_: Adds the string value to the tail (right) of the list. Create
 ##### *Examples*
 ~~~php
 $redis->del('key1');
-$redis->rPush('key1', 'A'); // returns 1
-$redis->rPush('key1', 'B'); // returns 2
-$redis->rPush('key1', 'C'); // returns 3
-/* key1 now points to the following list: [ 'A', 'B', 'C' ] */
+$redis->rPush('key1', 'A');           // returns 1
+$redis->rPush('key1', 'B');           // returns 2
+$redis->rPush('key1', 'C');           // returns 3
+$redis->rPush('key1', 'D', 'E', 'F'); // returns 6
+/* key1 now contains: [ 'A', 'B', 'C', 'D', 'E', 'F' ] */
 ~~~
 
 ### rPushX

From 73212e141403ec47441142fe1c7fd5fad24f6720 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 24 Apr 2020 17:12:26 +0300
Subject: [PATCH 1324/1986] Refactor redis_sock_get_connection_pool

---
 library.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/library.c b/library.c
index 541789ee1a..da625d586d 100644
--- a/library.c
+++ b/library.c
@@ -62,11 +62,9 @@ redis_sock_get_connection_pool(RedisSock *redis_sock)
 {
     ConnectionPool *p;
     zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port);
-    zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), persistent_id);
+    zend_resource *le;
 
-    if (le) {
-        p = le->ptr;
-    } else {
+    if ((le = zend_hash_find_ptr(&EG(persistent_list), persistent_id)) == NULL) {
         p = pecalloc(1, sizeof(*p), 1);
         zend_llist_init(&p->list, sizeof(php_stream *), NULL, 1);
 #if (PHP_VERSION_ID < 70300)
@@ -80,7 +78,7 @@ redis_sock_get_connection_pool(RedisSock *redis_sock)
 #endif
     }
     zend_string_release(persistent_id);
-    return p;
+    return le->ptr;
 }
 
 /* Helper to reselect the proper DB number when we reconnect */

From 201a97599953a9621bb8eb02dc8d5f08d16499a3 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Thu, 30 Apr 2020 21:22:40 -0700
Subject: [PATCH 1325/1986] Make unit test authentication configurable (#1748)

Right now cloning the repo and running unit tests will all fail if the
Redis/RedisCluster instances aren't configured with the password
'phpredis'.

This commit simply makes authentication during the tests optional via a
command-line argument.
---
 .travis.yml                | 16 +++++++-------
 tests/RedisArrayTest.php   | 44 +++++++++++++++++++-------------------
 tests/RedisClusterTest.php | 12 ++++++-----
 tests/RedisTest.php        | 22 +++++++++----------
 tests/TestRedis.php        | 13 ++++++-----
 tests/TestSuite.php        | 13 ++++++-----
 6 files changed, 64 insertions(+), 56 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 9de767c688..15bb8c833e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -48,11 +48,11 @@ before_script:
   - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 -a phpredis
   - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
 script:
-  - php tests/TestRedis.php --class Redis
-  - php tests/TestRedis.php --class RedisArray
-  - php tests/TestRedis.php --class RedisCluster
-  - php tests/TestRedis.php --class RedisSentinel
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel
+  - php tests/TestRedis.php --class Redis --auth phpredis
+  - php tests/TestRedis.php --class RedisArray --auth phpredis
+  - php tests/TestRedis.php --class RedisCluster --auth phpredis
+  - php tests/TestRedis.php --class RedisSentinel --auth phpredis
+  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis --auth phpredis
+  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray --auth phpredis
+  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --auth phpredis
+  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel --auth phpredis
diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php
index 303cdf45ec..9c53c9ce86 100644
--- a/tests/RedisArrayTest.php
+++ b/tests/RedisArrayTest.php
@@ -56,8 +56,8 @@ public function setUp() {
 
         global $newRing, $oldRing, $useIndex;
         $options = ['previous' => $oldRing, 'index' => $useIndex];
-        if (self::AUTH) {
-            $options['auth'] = self::AUTH;
+        if ($this->getAuth()) {
+            $options['auth'] = $this->getAuth();
         }
         $this->ra = new RedisArray($newRing, $options);
         $this->min_version = getMinVersion($this->ra);
@@ -84,8 +84,8 @@ public function testMSet() {
 
             $r = new Redis;
             $r->pconnect($host, (int)$port);
-            if (self::AUTH) {
-                $this->assertTrue($r->auth(self::AUTH));
+            if ($this->getAuth()) {
+                $this->assertTrue($r->auth($this->getAuth()));
             }
             $this->assertTrue($v === $r->get($k));
         }
@@ -126,8 +126,8 @@ public function testKeyLocality() {
         // with common hashing function
         global $newRing, $oldRing, $useIndex;
         $options = ['previous' => $oldRing, 'index' => $useIndex, 'function' => 'custom_hash'];
-        if (self::AUTH) {
-            $options['auth'] = self::AUTH;
+        if ($this->getAuth()) {
+            $options['auth'] = $this->getAuth();
         }
         $this->ra = new RedisArray($newRing, $options);
 
@@ -149,8 +149,8 @@ public function testKeyDistributor()
     {
         global $newRing, $useIndex;
         $options = ['index' => $useIndex, 'function' => 'custom_hash', 'distributor' => [$this, "customDistributor"]];
-        if (self::AUTH) {
-            $options['auth'] = self::AUTH;
+        if ($this->getAuth()) {
+            $options['auth'] = $this->getAuth();
         }
         $this->ra = new RedisArray($newRing, $options);
 
@@ -218,8 +218,8 @@ public function setUp() {
 
         global $newRing, $oldRing, $useIndex;
         $options = ['previous' => $oldRing, 'index' => $useIndex];
-        if (self::AUTH) {
-            $options['auth'] = self::AUTH;
+        if ($this->getAuth()) {
+            $options['auth'] = $this->getAuth();
         }
         // create array
         $this->ra = new RedisArray($newRing, $options);
@@ -234,8 +234,8 @@ public function testFlush() {
 
             $r = new Redis();
             $r->pconnect($host, (int)$port, 0);
-            if (self::AUTH) {
-                $this->assertTrue($r->auth(self::AUTH));
+            if ($this->getAuth()) {
+                $this->assertTrue($r->auth($this->getAuth()));
             }
             $r->flushdb();
         }
@@ -371,8 +371,8 @@ public function setUp() {
 
         global $newRing, $oldRing, $useIndex;
         $options = ['previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE];
-        if (self::AUTH) {
-            $options['auth'] = self::AUTH;
+        if ($this->getAuth()) {
+            $options['auth'] = $this->getAuth();
         }
         // create array
         $this->ra = new RedisArray($newRing, $options);
@@ -416,8 +416,8 @@ public function testAllKeysHaveBeenMigrated() {
 
             $r = new Redis;
             $r->pconnect($host, $port);
-            if (self::AUTH) {
-                $this->assertTrue($r->auth(self::AUTH));
+            if ($this->getAuth()) {
+                $this->assertTrue($r->auth($this->getAuth()));
             }
 
             $this->assertTrue($v === $r->get($k));  // check that the key has actually been migrated to the new node.
@@ -433,8 +433,8 @@ class Redis_Multi_Exec_Test extends TestSuite {
     public function setUp() {
         global $newRing, $oldRing, $useIndex;
         $options = ['previous' => $oldRing, 'index' => $useIndex];
-        if (self::AUTH) {
-            $options['auth'] = self::AUTH;
+        if ($this->getAuth()) {
+            $options['auth'] = $this->getAuth();
         }
         // create array
         $this->ra = new RedisArray($newRing, $options);
@@ -578,8 +578,8 @@ class Redis_Distributor_Test extends TestSuite {
     public function setUp() {
         global $newRing, $oldRing, $useIndex;
         $options = ['previous' => $oldRing, 'index' => $useIndex, 'distributor' => [$this, 'distribute']];
-        if (self::AUTH) {
-            $options['auth'] = self::AUTH;
+        if ($this->getAuth()) {
+            $options['auth'] = $this->getAuth();
         }
         // create array
         $this->ra = new RedisArray($newRing, $options);
@@ -616,7 +616,7 @@ public function testDistribution() {
     }
 }
 
-function run_tests($className, $str_filter, $str_host) {
+function run_tests($className, $str_filter, $str_host, $str_auth) {
         // reset rings
         global $newRing, $oldRing, $serverList;
 
@@ -625,7 +625,7 @@ function run_tests($className, $str_filter, $str_host) {
         $serverList = ["$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"];
 
         // run
-        return TestSuite::run($className, $str_filter);
+        return TestSuite::run($className, $str_filter, $str_host, $str_auth);
 }
 
 ?>
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 10750a1b5b..5a60cfdc82 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -62,7 +62,9 @@ public function testSession_noUnlockOfOtherProcess() { return $this->markTestSki
     public function testSession_lockWaitTime() { return $this->markTestSkipped(); }
 
     /* Load our seeds on construction */
-    public function __construct() {
+    public function __construct($str_host, $str_auth) {
+        parent::__construct($str_host, $str_auth);
+
         $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap';
 
         if (!file_exists($str_nodemap_file)) {
@@ -87,7 +89,7 @@ public function setUp() {
 
     /* Override newInstance as we want a RedisCluster object */
     protected function newInstance() {
-        return new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, self::AUTH);
+        return new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth());
     }
 
     /* Overrides for RedisTest where the function signature is different.  This
@@ -654,7 +656,7 @@ public function testSlotCache() {
 
         $pong = 0;
         for ($i = 0; $i < 10; $i++) {
-            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, self::AUTH);
+            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth());
             $pong += $obj_rc->ping("key:$i");
         }
 
@@ -670,7 +672,7 @@ public function testConnectionPool() {
 
         $pong = 0;
         for ($i = 0; $i < 10; $i++) {
-            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, self::AUTH);
+            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth());
             $pong += $obj_rc->ping("key:$i");
         }
 
@@ -685,7 +687,7 @@ protected function getFullHostPath()
     {
         return implode('&', array_map(function ($host) {
             return 'seed[]=' . $host;
-        }, self::$_arr_node_map)) . (self::AUTH ? '&auth=' . self::AUTH : '');
+        }, self::$_arr_node_map)) . ($this->getAuth() ? '&auth=' . $this->getAuth() : '');
     }
 }
 ?>
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 9ba859ab92..77c3fd853a 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -56,8 +56,8 @@ protected function mstime() {
     protected function getFullHostPath()
     {
         $fullHostPath = parent::getFullHostPath();
-        if (isset($fullHostPath) && self::AUTH) {
-            $fullHostPath .= '?auth=' . self::AUTH;
+        if (isset($fullHostPath) && $this->getAuth()) {
+            $fullHostPath .= '?auth=' . $this->getAuth();
         }
         return $fullHostPath;
     }
@@ -67,8 +67,8 @@ protected function newInstance() {
 
         $r->connect($this->getHost(), self::PORT);
 
-        if(self::AUTH) {
-            $this->assertTrue($r->auth(self::AUTH));
+        if($this->getAuth()) {
+            $this->assertTrue($r->auth($this->getAuth()));
         }
         return $r;
     }
@@ -4923,7 +4923,7 @@ public function testIntrospection() {
         // Simple introspection tests
         $this->assertTrue($this->redis->getHost() === $this->getHost());
         $this->assertTrue($this->redis->getPort() === self::PORT);
-        $this->assertTrue($this->redis->getAuth() === self::AUTH);
+        $this->assertTrue($this->redis->getAuth() === $this->getAuth());
     }
 
     /**
@@ -5951,8 +5951,8 @@ public function testUnixSocket() {
                 } else {
                     @$obj_r->connect($arr_args[0]);
                 }
-                if (self::AUTH) {
-                    $this->assertTrue($obj_r->auth(self::AUTH));
+                if ($this->getAuth()) {
+                    $this->assertTrue($obj_r->auth($this->getAuth()));
                 }
                 $this->assertTrue($obj_r->ping());
             }
@@ -5980,8 +5980,8 @@ public function testHighPorts() {
             $obj_r = new Redis();
             try {
                 @$obj_r->connect('localhost', $port);
-                if (self::AUTH) {
-                    $this->assertTrue($obj_r->auth(self::AUTH));
+                if ($this->getAuth()) {
+                    $this->assertTrue($obj_r->auth($this->getAuth()));
                 }
                 $this->assertTrue($obj_r->ping());
             } catch(Exception $ex) {
@@ -6168,8 +6168,8 @@ public function testMultipleConnect() {
 
         for($i = 0; $i < 5; $i++) {
             $this->redis->connect($host, $port);
-            if (self::AUTH) {
-                $this->assertTrue($this->redis->auth(self::AUTH));
+            if ($this->getAuth()) {
+                $this->assertTrue($this->redis->auth($this->getAuth()));
             }
             $this->assertTrue($this->redis->ping());
         }
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index 1c79d702e4..e6f70ca87d 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -11,7 +11,7 @@
 ini_set( 'display_errors','1');
 
 /* Grab options */
-$arr_args = getopt('', ['host:', 'class:', 'test:', 'nocolors']);
+$arr_args = getopt('', ['host:', 'class:', 'test:', 'nocolors', 'auth:']);
 
 /* Grab the test the user is trying to run */
 $arr_valid_classes = ['redis', 'redisarray', 'rediscluster', 'redissentinel'];
@@ -24,6 +24,9 @@
 /* Grab override test host if it was passed */
 $str_host = isset($arr_args['host']) ? $arr_args['host'] : '127.0.0.1';
 
+/* Grab redis authentication password */
+$str_auth = isset($arr_args['auth']) ? $arr_args['auth'] : NULL;
+
 /* Validate the class is known */
 if (!in_array($str_class, $arr_valid_classes)) {
     echo "Error:  Valid test classes are Redis, RedisArray, RedisCluster and RedisSentinel!\n";
@@ -41,7 +44,7 @@
 echo "Testing class ";
 if ($str_class == 'redis') {
     echo TestSuite::make_bold("Redis") . "\n";
-    exit(TestSuite::run("Redis_Test", $str_filter, $str_host));
+    exit(TestSuite::run("Redis_Test", $str_filter, $str_host, $str_auth));
 } else if ($str_class == 'redisarray') {
     echo TestSuite::make_bold("RedisArray") . "\n";
     global $useIndex;
@@ -52,16 +55,16 @@
         $arr_ra_tests = ['Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'];
         foreach ($arr_ra_tests as $str_test) {
             /* Run until we encounter a failure */
-            if (run_tests($str_test, $str_filter, $str_host) != 0) {
+            if (run_tests($str_test, $str_filter, $str_host, $str_auth) != 0) {
                 exit(1);
             }
         }
     }
 } else if ($str_class == 'rediscluster') {
     echo TestSuite::make_bold("RedisCluster") . "\n";
-    exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host));
+    exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host, $str_auth));
 } else {
     echo TestSuite::make_bold("RedisSentinel") . "\n";
-    exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host));
+    exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host, $str_auth));
 }
 ?>
diff --git a/tests/TestSuite.php b/tests/TestSuite.php
index 40ac9cb4ed..24cfed6088 100644
--- a/tests/TestSuite.php
+++ b/tests/TestSuite.php
@@ -6,11 +6,12 @@ class TestSkippedException extends Exception {}
 // phpunit is such a pain to install, we're going with pure-PHP here.
 class TestSuite
 {
-    const AUTH = 'phpredis'; //replace with a string to use Redis authentication
-
     /* Host the tests will use */
     private $str_host;
 
+    /* Redis authentication we'll use */
+    private $str_auth;
+
     private static $_boo_colorize = false;
 
     private static $BOLD_ON = "\033[1m";
@@ -27,11 +28,13 @@ class TestSuite
     public static $errors = [];
     public static $warnings = [];
 
-    public function __construct($str_host) {
+    public function __construct($str_host, $str_auth) {
         $this->str_host = $str_host;
+        $this->str_auth = $str_auth;
     }
 
     public function getHost() { return $this->str_host; }
+    public function getAuth() { return $this->str_auth; }
 
     /**
      * Returns the fully qualified host path,
@@ -154,7 +157,7 @@ public static function flagColorization($boo_override) {
             posix_isatty(STDOUT);
     }
 
-    public static function run($className, $str_limit = NULL, $str_host = NULL) {
+    public static function run($className, $str_limit = NULL, $str_host = NULL, $str_auth = NULL) {
         /* Lowercase our limit arg if we're passed one */
         $str_limit = $str_limit ? strtolower($str_limit) : $str_limit;
 
@@ -178,7 +181,7 @@ public static function run($className, $str_limit = NULL, $str_host = NULL) {
             echo self::make_bold($str_out_name);
 
             $count = count($className::$errors);
-            $rt = new $className($str_host);
+            $rt = new $className($str_host, $str_auth);
 
             try {
                 $rt->setUp();

From b9b383f49939484dcddf1a5edefdb9d753baa7f8 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Wed, 6 May 2020 18:46:43 +0200
Subject: [PATCH 1326/1986] fix [-Wformat=] warning on 32-bit (#1750)

Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers
---
 cluster_library.c | 2 +-
 redis_array.c     | 4 ++--
 redis_cluster.c   | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 191947c574..b21a687585 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -552,7 +552,7 @@ unsigned short cluster_hash_key_zval(zval *z_key) {
             klen = Z_STRLEN_P(z_key);
             break;
         case IS_LONG:
-            klen = snprintf(buf,sizeof(buf),"%ld",Z_LVAL_P(z_key));
+            klen = snprintf(buf,sizeof(buf),ZEND_LONG_FMT,Z_LVAL_P(z_key));
             kptr = (const char *)buf;
             break;
         case IS_DOUBLE:
diff --git a/redis_array.c b/redis_array.c
index 7f8fa129d9..bdd7120efa 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -926,7 +926,7 @@ PHP_METHOD(RedisArray, mget)
             key_len = Z_STRLEN_P(data);
             key_lookup = Z_STRVAL_P(data);
         } else {
-            key_len = snprintf(kbuf, sizeof(kbuf), "%ld", Z_LVAL_P(data));
+            key_len = snprintf(kbuf, sizeof(kbuf), ZEND_LONG_FMT, Z_LVAL_P(data));
             key_lookup = (char*)kbuf;
         }
 
@@ -1052,7 +1052,7 @@ PHP_METHOD(RedisArray, mset)
             key_len = ZSTR_LEN(zkey);
             key = ZSTR_VAL(zkey);
         } else {
-            key_len = snprintf(kbuf, sizeof(kbuf), "%lu", idx);
+            key_len = snprintf(kbuf, sizeof(kbuf), ZEND_ULONG_FMT, idx);
             key = kbuf;
         }
 
diff --git a/redis_cluster.c b/redis_cluster.c
index ee218ba05c..7f79b5fe80 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -2245,7 +2245,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg)
 
         /* Inform the caller if they've passed bad data */
         if (slot < 0) {
-            php_error_docref(0, E_WARNING, "Unknown node %s:%ld",
+            php_error_docref(0, E_WARNING, "Unknown node %s:" ZEND_LONG_FMT,
                 Z_STRVAL_P(z_host), Z_LVAL_P(z_port));
         }
     } else {

From 98499bbb18759da34456ca7d0deaff062d5c91a1 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 8 May 2020 10:16:33 +0300
Subject: [PATCH 1327/1986] Update changelog

---
 Changelog.md | 38 +++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index c778da2c01..7b3b3d7dd6 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -14,20 +14,52 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ### Changed
 
-- Various small changes in cluster_library
+- Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers
+  [b9b383f4](https://github.com/phpredis/phpredis/commit/b9b383f4)
+  ([Remi Collet](https://github.com/remicollet))
+
+- Make unit test authentication configurable
+  [201a9759](https://github.com/phpredis/phpredis/commit/201a9759)
+  ([Michel Grunder](https://github.com/michael-grunder))
+
+- Various small changes in library and cluster_library
+  [73212e14](https://github.com/phpredis/phpredis/commit/73212e14),
   [460c8f29](https://github.com/phpredis/phpredis/commit/460c8f29)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
 - PHP 8 compatibility
-  [9ee94ca4](https://github.com/phpredis/phpredis/commit/9ee94ca4)
+  [9ee94ca4](https://github.com/phpredis/phpredis/commit/9ee94ca4),
   [7e4c7b3e](https://github.com/phpredis/phpredis/commit/7e4c7b3e)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
 - Refactor PHPREDIS_GET_OBJECT macro
-  [d5dadaf6](https://github.com/phpredis/phpredis/commit/d5dadaf6)
+  [d5dadaf6](https://github.com/phpredis/phpredis/commit/d5dadaf6),
   [190c0d34](https://github.com/phpredis/phpredis/commit/190c0d34)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
+- Fix documentation to show lPush and rPush are variadic
+  [6808cd6a](https://github.com/phpredis/phpredis/commit/6808cd6a)
+  ([Michel Grunder](https://github.com/michael-grunder))
+
+---
+
+## [5.2.2] - 2020-05-05 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.2.2), [PECL](https://pecl.php.net/package/redis/5.2.2))
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack.com](https://audiomack.com)
+- [Till Krüss](https://github.com/tillkruss)
+
+### Changed
+
+- Inexpensive liveness check, and making ECHO optional
+  [56898f81](https://github.com/phpredis/phpredis/commit/56898f81)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Move `AUTH` to `redis_sock_server_open`
+  [80f2529b](https://github.com/phpredis/phpredis/commit/80f2529b)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
 ---
 
 ## [5.2.1] - 2020-03-19 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.2.1), [PECL](https://pecl.php.net/package/redis/5.2.1))

From 215828e3474dfd9ea72fdc6da67aa6bee2d95ddf Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 8 May 2020 11:58:08 +0300
Subject: [PATCH 1328/1986] Fix scan-build warning

---
 library.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/library.c b/library.c
index da625d586d..565e1cb39a 100644
--- a/library.c
+++ b/library.c
@@ -728,10 +728,7 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value)
     len = snprintf(tmp, sizeof(tmp), "%.16g", value);
 
     // Append the string
-    retval = redis_cmd_append_sstr(str, tmp, len);
-
-    /* Return new length */
-    return retval;
+    return redis_cmd_append_sstr(str, tmp, len);
 }
 
 /* Append a zval to a redis command.  The value will be serialized if we are
@@ -2400,7 +2397,7 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len
     uint8_t *val8;
 #endif
 
-    *val = NULL;
+    *val = "";
     *val_len = 0;
     switch(redis_sock->serializer) {
         case REDIS_SERIALIZER_NONE:

From 508fef6f18c8ee7e57ff6fc45697248c0bd72709 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 17 May 2020 23:19:01 +0300
Subject: [PATCH 1329/1986] Remove unused PHP_MSHUTDOWN_FUNCTION

---
 redis.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/redis.c b/redis.c
index 2e0aaff19e..729bcf9be2 100644
--- a/redis.c
+++ b/redis.c
@@ -500,7 +500,7 @@ zend_module_entry redis_module_entry = {
      "redis",
      NULL,
      PHP_MINIT(redis),
-     PHP_MSHUTDOWN(redis),
+     NULL,
      NULL,
      NULL,
      PHP_MINFO(redis),
@@ -827,14 +827,6 @@ PHP_MINIT_FUNCTION(redis)
     return SUCCESS;
 }
 
-/**
- * PHP_MSHUTDOWN_FUNCTION
- */
-PHP_MSHUTDOWN_FUNCTION(redis)
-{
-    return SUCCESS;
-}
-
 static const char *
 get_available_serializers(void)
 {

From e80600e244b8442cb7c86e99b067966cd59bf2ee Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 19 May 2020 04:11:40 +0300
Subject: [PATCH 1330/1986] Issue #548 (#1649)

Adds `Redis::SCAN_PREFIX` and `Redis::SCAN_NOPREFIX` as options to SCAN.

See #548
---
 README.markdown            |  5 ++++
 common.h                   |  4 +++-
 library.c                  |  4 ++--
 redis.c                    | 13 ++++++++--
 redis_cluster.c            | 21 ++++++++++++----
 redis_commands.c           | 13 ++++++----
 tests/RedisClusterTest.php | 49 ++++++++++++++++++++++++++++++++++++++
 tests/RedisTest.php        | 34 ++++++++++++++++++++++++++
 8 files changed, 130 insertions(+), 13 deletions(-)

diff --git a/README.markdown b/README.markdown
index 9edc3dca83..1b81f7784d 100644
--- a/README.markdown
+++ b/README.markdown
@@ -354,6 +354,11 @@ $redis->setOption(Redis::OPT_PREFIX, 'myAppName:');	// use custom prefix on all
 */
 $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
 $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+
+/* Scan can also be configured to automatically prepend the currently set PhpRedis
+   prefix to any MATCH pattern. */
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);
 ~~~
 
 
diff --git a/common.h b/common.h
index e70a8ca027..1926d6417f 100644
--- a/common.h
+++ b/common.h
@@ -103,7 +103,9 @@ typedef enum {
 
 /* SCAN options */
 #define REDIS_SCAN_NORETRY 0
-#define REDIS_SCAN_RETRY 1
+#define REDIS_SCAN_RETRY   1
+#define REDIS_SCAN_PREFIX  2
+#define REDIS_SCAN_NOPREFIX 3
 
 /* GETBIT/SETBIT offset range limits */
 #define BITOP_MIN_OFFSET 0
diff --git a/library.c b/library.c
index 565e1cb39a..a67f79bbe0 100644
--- a/library.c
+++ b/library.c
@@ -722,7 +722,7 @@ int
 redis_cmd_append_sstr_dbl(smart_string *str, double value)
 {
     char tmp[64];
-    int len, retval;
+    int len;
 
     /* Convert to string */
     len = snprintf(tmp, sizeof(tmp), "%.16g", value);
@@ -1785,7 +1785,7 @@ redis_sock_create(char *host, int host_len, int port,
 
     redis_sock->err = NULL;
 
-    redis_sock->scan = REDIS_SCAN_NORETRY;
+    redis_sock->scan = 0;
 
     redis_sock->readonly = 0;
     redis_sock->tcp_keepalive = 0;
diff --git a/redis.c b/redis.c
index 729bcf9be2..6c1a764253 100644
--- a/redis.c
+++ b/redis.c
@@ -723,6 +723,8 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) {
     zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN);
     zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY);
     zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY);
+    zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_PREFIX"), REDIS_SCAN_PREFIX);
+    zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NOPREFIX"), REDIS_SCAN_NOPREFIX);
 
     /* Cluster option to allow for slave failover */
     if (is_cluster) {
@@ -3462,7 +3464,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
     RedisSock *redis_sock;
     HashTable *hash;
     char *pattern = NULL, *cmd, *key = NULL;
-    int cmd_len, num_elements, key_free = 0;
+    int cmd_len, num_elements, key_free = 0, pattern_free = 0;
     size_t key_len = 0, pattern_len = 0;
     zend_string *match_type = NULL;
     zend_long count = 0, iter;
@@ -3520,6 +3522,10 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         key_free = redis_key_prefix(redis_sock, &key, &key_len);
     }
 
+    if (redis_sock->scan & REDIS_SCAN_PREFIX) {
+        pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len);
+    }
+
     /**
      * Redis can return to us empty keys, especially in the case where there
      * are a large number of keys to scan, and we're matching against a
@@ -3552,9 +3558,12 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         /* Get the number of elements */
         hash = Z_ARRVAL_P(return_value);
         num_elements = zend_hash_num_elements(hash);
-    } while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 &&
+    } while (redis_sock->scan & REDIS_SCAN_RETRY && iter != 0 &&
             num_elements == 0);
 
+    /* Free our pattern if it was prefixed */
+    if (pattern_free) efree(pattern);
+
     /* Free our key if it was prefixed */
     if(key_free) efree(key);
 
diff --git a/redis_cluster.c b/redis_cluster.c
index 7f79b5fe80..0fc94fe3a5 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -2411,7 +2411,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,
 {
     redisCluster *c = GET_CONTEXT();
     char *cmd, *pat = NULL, *key = NULL;
-    size_t key_len = 0, pat_len = 0;
+    size_t key_len = 0, pat_len = 0, pat_free = 0;
     int cmd_len, key_free = 0;
     short slot;
     zval *z_it;
@@ -2450,6 +2450,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,
     key_free = redis_key_prefix(c->flags, &key, &key_len);
     slot = cluster_hash_key(key, key_len);
 
+    if (c->flags->scan & REDIS_SCAN_PREFIX) {
+        pat_free = redis_key_prefix(c->flags, &pat, &pat_len);
+    }
+
     // If SCAN_RETRY is set, loop until we get a zero iterator or until
     // we get non-zero elements.  Otherwise we just send the command once.
     do {
@@ -2488,7 +2492,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,
 
         // Free our command
         efree(cmd);
-    } while (c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
+    } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
+
+    // Free our pattern
+    if (pat_free) efree(pat);
 
     // Free our key
     if (key_free) efree(key);
@@ -2505,7 +2512,7 @@ PHP_METHOD(RedisCluster, scan) {
     int cmd_len;
     short slot;
     zval *z_it, *z_node;
-    long it, num_ele;
+    long it, num_ele, pat_free = 0;
     zend_long count = 0;
 
     /* Treat as read-only */
@@ -2534,6 +2541,10 @@ PHP_METHOD(RedisCluster, scan) {
         RETURN_FALSE;
     }
 
+    if (c->flags->scan & REDIS_SCAN_PREFIX) {
+        pat_free = redis_key_prefix(c->flags, &pat, &pat_len);
+    }
+
     /* With SCAN_RETRY on, loop until we get some keys, otherwise just return
      * what Redis does, as it does */
     do {
@@ -2570,7 +2581,9 @@ PHP_METHOD(RedisCluster, scan) {
         efree(cmd);
 
         num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value));
-    } while (c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
+    } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
+
+    if (pat_free) efree(pat);
 
     Z_LVAL_P(z_it) = it;
 }
diff --git a/redis_commands.c b/redis_commands.c
index 6945ed4b67..e7cca1a8e1 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4032,11 +4032,16 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
             RETURN_TRUE;
         case REDIS_OPT_SCAN:
             val_long = zval_get_long(val);
-            if (val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) {
-                redis_sock->scan = val_long;
-                RETURN_TRUE;
+            if (val_long == REDIS_SCAN_NORETRY) {
+                redis_sock->scan &= ~REDIS_SCAN_RETRY;
+            } else if (val_long == REDIS_SCAN_NOPREFIX) {
+                redis_sock->scan &= ~REDIS_SCAN_PREFIX;
+            } else if (val_long == REDIS_SCAN_RETRY || val_long == REDIS_SCAN_PREFIX) {
+                redis_sock->scan |= val_long;
+            } else {
+                break;
             }
-            break;
+            RETURN_TRUE;
         case REDIS_OPT_FAILOVER:
             val_long = zval_get_long(val);
             if (val_long == REDIS_FAILOVER_NONE ||
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 5a60cfdc82..e5c66cc6cd 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -222,6 +222,55 @@ public function testScan() {
         $this->assertEquals($i_scan_count, $i_key_count);
     }
 
+    public function testScanPrefix() {
+        $arr_prefixes = ['prefix-a:', 'prefix-b:'];
+        $str_id = uniqid();
+
+        $arr_keys = [];
+        foreach ($arr_prefixes as $str_prefix) {
+            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
+            $this->redis->set($str_id, "LOLWUT");
+            $arr_keys[$str_prefix] = $str_id;
+        }
+
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);
+
+        foreach ($arr_prefixes as $str_prefix) {
+            $arr_prefix_keys = [];
+            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
+
+            foreach ($this->redis->_masters() as $arr_master) {
+                $it = NULL;
+                while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) {
+                    foreach ($arr_iter as $str_key) {
+                        $arr_prefix_keys[$str_prefix] = $str_key;
+                    }
+                }
+            }
+
+            $this->assertTrue(count($arr_prefix_keys) == 1 && isset($arr_prefix_keys[$str_prefix]));
+        }
+
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);
+
+        $arr_scan_keys = [];
+
+        foreach ($this->redis->_masters() as $arr_master) {
+            $it = NULL;
+            while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) {
+                foreach ($arr_iter as $str_key) {
+                    $arr_scan_keys[] = $str_key;
+                }
+            }
+        }
+
+        /* We should now have both prefixs' keys */
+        foreach ($arr_keys as $str_prefix => $str_id) {
+            $this->assertTrue(in_array("${str_prefix}${str_id}", $arr_scan_keys));
+        }
+    }
+
     // 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() {
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 77c3fd853a..06b9591a09 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5008,7 +5008,41 @@ public function testScan() {
                 }
             }
         }
+    }
+
+    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_SCAN, Redis::SCAN_RETRY);
+        $this->redis->setOption(Redis::OPT_SCAN, Redis::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_SCAN, Redis::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 testHScan() {

From 20a3dc7251cb0bf450ef2a1cfeeeaeaa10355cd2 Mon Sep 17 00:00:00 2001
From: mi-nakano 
Date: Thu, 21 May 2020 22:07:48 +0900
Subject: [PATCH 1331/1986] bugfix: PHP_REDIS_JSON parameter at configure

---
 config.m4 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config.m4 b/config.m4
index 58b2bce8de..2bd148d16a 100644
--- a/config.m4
+++ b/config.m4
@@ -35,7 +35,7 @@ if test "$PHP_REDIS" != "no"; then
     AC_DEFINE(PHP_SESSION,1,[redis sessions])
   fi
 
-  if test "PHP_REDIS_JSON" != "no"; then
+  if test "$PHP_REDIS_JSON" != "no"; then
     AC_MSG_CHECKING([for json includes])
     json_inc_path=""
     if test -f "$abs_srcdir/include/php/ext/json/php_json.h"; then

From f13f9b7c7f5e3a7d286b412541199a408a0a98bd Mon Sep 17 00:00:00 2001
From: victor 
Date: Thu, 14 May 2020 16:02:26 +0300
Subject: [PATCH 1332/1986] During scan build the command using long type for
 iterator instead of int. Shall resolve issues with Redis Enterprise which
 uses big 64 bit numbers for cursor.

Conflicts:
	redis.c
---
 redis.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/redis.c b/redis.c
index 6c1a764253..585915de10 100644
--- a/redis.c
+++ b/redis.c
@@ -3402,7 +3402,7 @@ PHP_METHOD(Redis, command) {
 /* Helper to format any combination of SCAN arguments */
 PHP_REDIS_API int
 redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
-                     int iter, char *pattern, int pattern_len, int count,
+                     long iter, char *pattern, int pattern_len, int count,
                      zend_string *match_type)
 {
     smart_string cmdstr = {0};
@@ -3433,7 +3433,7 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
     /* Start the command */
     redis_cmd_init_sstr(&cmdstr, argc, keyword, strlen(keyword));
     if (key_len) redis_cmd_append_sstr(&cmdstr, key, key_len);
-    redis_cmd_append_sstr_int(&cmdstr, iter);
+    redis_cmd_append_sstr_long(&cmdstr, iter);
 
     /* Append COUNT if we've got it */
     if(count) {
@@ -3543,7 +3543,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         }
 
         // Format our SCAN command
-        cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter,
+        cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (long)iter,
                                    pattern, pattern_len, count, match_type);
 
         /* Execute our command getting our new iterator value */

From f9c7bb5788c39614c23e3bb9ec42ec8d6d5bbaa1 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Sat, 23 May 2020 14:25:41 -0700
Subject: [PATCH 1333/1986] Adds Redis 6.0 KEEPTTL option for SET (#1766)

Added support for KEEPTTL option of SET command, which added in Redis 6

See: #1761

Co-authored-by: victor 
---
 common.h            | 30 +++++++++------
 redis_commands.c    | 91 +++++++++++++++++++++++++--------------------
 tests/RedisTest.php | 14 +++++++
 3 files changed, 83 insertions(+), 52 deletions(-)

diff --git a/common.h b/common.h
index 1926d6417f..857e87cfb2 100644
--- a/common.h
+++ b/common.h
@@ -209,18 +209,26 @@ typedef enum {
         REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \
     }
 
+/* Case insensitive compare against compile-time static string */
+#define REDIS_STRICMP_STATIC(s, len, sstr) \
+    (len == sizeof(sstr) - 1 && !strncasecmp(s, sstr, len))
+
+/* Test if a zval is a string and (case insensitive) matches a static string */
+#define ZVAL_STRICMP_STATIC(zv, sstr) \
+    REDIS_STRICMP_STATIC(Z_STRVAL_P(zv), Z_STRLEN_P(zv), sstr)
+
+/* Case insensitive compare of a zend_string to a static string */
+#define ZSTR_STRICMP_STATIC(zs, sstr) \
+    REDIS_STRICMP_STATIC(ZSTR_VAL(zs), ZSTR_LEN(zs), sstr)
+
 /* Extended SET argument detection */
-#define IS_EX_ARG(a) \
-    ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
-#define IS_PX_ARG(a) \
-    ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
-#define IS_NX_ARG(a) \
-    ((a[0]=='n' || a[0]=='N') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
-#define IS_XX_ARG(a) \
-    ((a[0]=='x' || a[0]=='X') && (a[1]=='x' || a[1]=='X') && a[2]=='\0')
-
-#define IS_EX_PX_ARG(a) (IS_EX_ARG(a) || IS_PX_ARG(a))
-#define IS_NX_XX_ARG(a) (IS_NX_ARG(a) || IS_XX_ARG(a))
+#define ZSTR_IS_EX_ARG(zs) ZSTR_STRICMP_STATIC(zs, "EX")
+#define ZSTR_IS_PX_ARG(zs) ZSTR_STRICMP_STATIC(zs, "PX")
+#define ZSTR_IS_NX_ARG(zs) ZSTR_STRICMP_STATIC(zs, "NX")
+#define ZSTR_IS_XX_ARG(zs) ZSTR_STRICMP_STATIC(zs, "XX")
+
+#define ZVAL_IS_NX_XX_ARG(zv) (ZVAL_STRICMP_STATIC(zv, "NX") || ZVAL_STRICMP_STATIC(zv, "XX"))
+#define ZSTR_IS_EX_PX_ARG(a) (ZSTR_IS_EX_ARG(a) || ZSTR_IS_PX_ARG(a))
 
 /* Given a string and length, validate a zRangeByLex argument.  The semantics
  * here are that the argument must start with '(' or '[' or be just the char
diff --git a/redis_commands.c b/redis_commands.c
index e7cca1a8e1..5eb802b767 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1303,9 +1303,10 @@ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                   char **cmd, int *cmd_len, short *slot, void **ctx)
 {
+    smart_string cmdstr = {0};
     zval *z_value, *z_opts=NULL;
     char *key = NULL, *exp_type = NULL, *set_type = NULL;
-    long expire = -1;
+    long expire = -1, exp_set = 0, keep_ttl = 0;
     size_t key_len;
 
     // Make sure the function is being called correctly
@@ -1336,7 +1337,9 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, v) {
             ZVAL_DEREF(v);
             /* Detect PX or EX argument and validate timeout */
-            if (zkey && IS_EX_PX_ARG(ZSTR_VAL(zkey))) {
+            if (zkey && ZSTR_IS_EX_PX_ARG(zkey)) {
+                exp_set = 1;
+
                 /* Set expire type */
                 exp_type = ZSTR_VAL(zkey);
 
@@ -1346,45 +1349,55 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                 } else if (Z_TYPE_P(v) == IS_STRING) {
                     expire = atol(Z_STRVAL_P(v));
                 }
-
-                /* Expiry can't be set < 1 */
-                if (expire < 1) {
-                    return FAILURE;
-                }
-            } else if (Z_TYPE_P(v) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_P(v))) {
+            } else if (ZVAL_STRICMP_STATIC(v, "KEEPTTL")) {
+                keep_ttl  = 1;
+            } else if (ZVAL_IS_NX_XX_ARG(v)) {
                 set_type = Z_STRVAL_P(v);
             }
         } ZEND_HASH_FOREACH_END();
     } else if (z_opts && Z_TYPE_P(z_opts) == IS_LONG) {
-        /* Grab expiry and fail if it's < 1 */
         expire = Z_LVAL_P(z_opts);
-        if (expire < 1) {
-            return FAILURE;
-        }
+        exp_set = 1;
     }
 
-    /* Now let's construct the command we want */
-    if (exp_type && set_type) {
-        /* SET   NX|XX PX|EX  */
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvsls", key, key_len, z_value,
-                                     exp_type, 2, expire, set_type, 2);
-    } else if (exp_type) {
-        /* SET   PX|EX  */
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvsl", key, key_len, z_value,
-                                     exp_type, 2, expire);
-    } else if (set_type) {
-        /* SET   NX|XX */
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kvs", key, key_len, z_value,
-                                     set_type, 2);
-    } else if (expire > 0) {
-        /* Backward compatible SETEX redirection */
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire,
-                                     z_value);
-    } else {
-        /* SET   */
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SET", "kv", key, key_len, z_value);
+    /* Protect the user from syntax errors but give them some info about what's wrong */
+    if (exp_set && expire < 1) {
+        php_error_docref(NULL, E_WARNING, "EXPIRE can't be < 1");
+        return FAILURE;
+    } else if (exp_type && keep_ttl) {
+        php_error_docref(NULL, E_WARNING, "KEEPTTL can't be combined with EX or PX option");
+        return FAILURE;
     }
 
+    /* Backward compatibility:  If we are passed no options except an EXPIRE ttl, we
+     * actually execute a SETEX command */
+    if (expire > 0 && !exp_type && !set_type && !keep_ttl) {
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, z_value);
+        return SUCCESS;
+    }
+
+    /* Calculate argc based on options set */
+    int argc = 2 + (exp_type ? 2 : 0) + (set_type != NULL) + (keep_ttl != 0);
+
+    /* Initial SET   */
+    redis_cmd_init_sstr(&cmdstr, argc, "SET", 3);
+    redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot);
+    redis_cmd_append_sstr_zval(&cmdstr, z_value, redis_sock);
+
+    if (exp_type) {
+        redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type));
+        redis_cmd_append_sstr_long(&cmdstr, expire);
+    }
+
+    if (set_type)
+        redis_cmd_append_sstr(&cmdstr, set_type, strlen(set_type));
+    if (keep_ttl)
+        redis_cmd_append_sstr(&cmdstr, "KEEPTTL", 7);
+
+    /* Push command and length to the caller */
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
     return SUCCESS;
 }
 
@@ -2569,15 +2582,11 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         zval *z_opt;
         ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[1]), z_opt) {
             if (Z_TYPE_P(z_opt) == IS_STRING) {
-                if (Z_STRLEN_P(z_opt) == 2) {
-                    if (IS_NX_XX_ARG(Z_STRVAL_P(z_opt))) {
-                        exp_type = Z_STRVAL_P(z_opt);
-                    } else if (strncasecmp(Z_STRVAL_P(z_opt), "ch", 2) == 0) {
-                        ch = 1;
-                    }
-                } else if (Z_STRLEN_P(z_opt) == 4 &&
-                    strncasecmp(Z_STRVAL_P(z_opt), "incr", 4) == 0
-                ) {
+                if (ZVAL_IS_NX_XX_ARG(z_opt)) {
+                    exp_type = Z_STRVAL_P(z_opt);
+                } else if (ZVAL_STRICMP_STATIC(z_opt, "CH")) {
+                    ch = 1;
+                } else if (ZVAL_STRICMP_STATIC(z_opt, "INCR")) {
                     if (num > 4) {
                         // Only one score-element pair can be specified in this mode.
                         efree(z_args);
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 06b9591a09..95cbe64b71 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -377,6 +377,20 @@ public function testExtendedSet() {
         $this->redis->set('foo','bar', NULL);
         $this->assertEquals($this->redis->get('foo'), 'bar');
         $this->assertTrue($this->redis->ttl('foo')<0);
+
+        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() {

From 25eb3497724838db561a36084e0041fcc6427bb7 Mon Sep 17 00:00:00 2001
From: neodisco 
Date: Mon, 25 May 2020 10:42:30 +1000
Subject: [PATCH 1334/1986] Update INSTALL.markdown

Tested that php74 exists
---
 INSTALL.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index 981b103e5a..b87341cf70 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -84,7 +84,7 @@ See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecl
 You can install it using MacPorts:
 
 - [Get macports-php](https://www.macports.org/)
-- `sudo port install php56-redis` (or php53-redis, php54-redis, php55-redis, php70-redis, php71-redis, php72-redis)
+- `sudo port install php56-redis` (or php53-redis, php54-redis, php55-redis, php70-redis, php71-redis, php72-redis, php73-redis, php74-redis)
 
 # Building on Windows
 

From 5bf881244dd30b5310fcfcaf5bcd8f9e2675bb01 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Sun, 31 May 2020 22:36:36 -0700
Subject: [PATCH 1335/1986] Use ZEND_LONG_FMT and avoid typecast in hMset
 (#1770)

See #1764
---
 redis_commands.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis_commands.c b/redis_commands.c
index 5eb802b767..942b22721a 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1684,7 +1684,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             mem_len = ZSTR_LEN(zkey);
             mem = ZSTR_VAL(zkey);
         } else {
-            mem_len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx);
+            mem_len = snprintf(kbuf, sizeof(kbuf), ZEND_LONG_FMT, idx);
             mem = (char*)kbuf;
         }
 

From b067129678264fc1c5c0f611ce1b192e05c14669 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 27 May 2020 17:39:33 +0300
Subject: [PATCH 1336/1986] Issue #1600

Ssl context options in Redis::connect
---
 common.h  | 65 ++++++++++++++++++++++++++++---------------------------
 library.c | 20 ++++++++++++++++-
 library.h |  1 +
 redis.c   | 10 ++++++---
 4 files changed, 60 insertions(+), 36 deletions(-)

diff --git a/common.h b/common.h
index 857e87cfb2..dc692524ee 100644
--- a/common.h
+++ b/common.h
@@ -258,38 +258,39 @@ typedef struct fold_item {
 
 /* {{{ struct RedisSock */
 typedef struct {
-    php_stream        *stream;
-    zend_string       *host;
-    int               port;
-    zend_string       *auth;
-    double            timeout;
-    double            read_timeout;
-    long              retry_interval;
-    redis_sock_status status;
-    int               persistent;
-    int               watching;
-    zend_string       *persistent_id;
-
-    redis_serializer  serializer;
-    int               compression;
-    int               compression_level;
-    long              dbNumber;
-
-    zend_string       *prefix;
-
-    short             mode;
-    fold_item         *head;
-    fold_item         *current;
-
-    zend_string       *pipeline_cmd;
-
-    zend_string       *err;
-
-    int               scan;
-
-    int               readonly;
-    int               reply_literal;
-    int               tcp_keepalive;
+    php_stream         *stream;
+    php_stream_context *stream_ctx;
+    zend_string        *host;
+    int                port;
+    zend_string        *auth;
+    double             timeout;
+    double             read_timeout;
+    long               retry_interval;
+    redis_sock_status  status;
+    int                persistent;
+    int                watching;
+    zend_string        *persistent_id;
+
+    redis_serializer   serializer;
+    int                compression;
+    int                compression_level;
+    long               dbNumber;
+
+    zend_string        *prefix;
+
+    short              mode;
+    fold_item          *head;
+    fold_item          *current;
+
+    zend_string        *pipeline_cmd;
+
+    zend_string        *err;
+
+    int                scan;
+
+    int                readonly;
+    int                reply_literal;
+    int                tcp_keepalive;
 } RedisSock;
 /* }}} */
 
diff --git a/library.c b/library.c
index a67f79bbe0..fcbb4fd738 100644
--- a/library.c
+++ b/library.c
@@ -1921,7 +1921,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
     redis_sock->stream = php_stream_xport_create(host, host_len,
         0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
         persistent_id ? ZSTR_VAL(persistent_id) : NULL,
-        tv_ptr, NULL, &estr, &err);
+        tv_ptr, redis_sock->stream_ctx, &estr, &err);
 
     if (persistent_id) {
         zend_string_release(persistent_id);
@@ -2043,6 +2043,24 @@ redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len)
     }
 }
 
+PHP_REDIS_API int
+redis_sock_set_stream_context(RedisSock *redis_sock, zval *options)
+{
+    zend_string *zkey;
+    zval *z_ele;
+
+    if (!redis_sock || Z_TYPE_P(options) != IS_ARRAY) {
+        return FAILURE;
+    } else if (!redis_sock->stream_ctx) {
+        redis_sock->stream_ctx = php_stream_context_alloc();
+    }
+    ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), zkey, z_ele) {
+        php_stream_context_set_option(redis_sock->stream_ctx, "ssl", ZSTR_VAL(zkey), z_ele);
+    } ZEND_HASH_FOREACH_END();
+
+    return SUCCESS;
+}
+
 /**
  * redis_sock_read_multibulk_reply
  */
diff --git a/library.h b/library.h
index 05ef917e4f..a5c660b670 100644
--- a/library.h
+++ b/library.h
@@ -91,6 +91,7 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw);
 PHP_REDIS_API RedisSock *redis_sock_get(zval *id, int nothrow);
 PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock);
 PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len);
+PHP_REDIS_API int redis_sock_set_stream_context(RedisSock *redis_sock, zval *options);
 
 PHP_REDIS_API int
 redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len);
diff --git a/redis.c b/redis.c
index 585915de10..46278abd68 100644
--- a/redis.c
+++ b/redis.c
@@ -959,7 +959,7 @@ PHP_METHOD(Redis, pconnect)
 PHP_REDIS_API int
 redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
 {
-    zval *object;
+    zval *object, *ssl = NULL;
     char *host = NULL, *persistent_id = NULL;
     zend_long port = -1, retry_interval = 0;
     size_t host_len, persistent_id_len;
@@ -973,10 +973,10 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
 #endif
 
     if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                     "Os|lds!ld", &object, redis_ce, &host,
+                                     "Os|lds!lda", &object, redis_ce, &host,
                                      &host_len, &port, &timeout, &persistent_id,
                                      &persistent_id_len, &retry_interval,
-                                     &read_timeout) == FAILURE)
+                                     &read_timeout, &ssl) == FAILURE)
     {
         return FAILURE;
     }
@@ -1016,6 +1016,10 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
     redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent,
         persistent_id, retry_interval);
 
+    if (ssl != NULL) {
+        redis_sock_set_stream_context(redis->sock, ssl);
+    }
+
     if (redis_sock_server_open(redis->sock) < 0) {
         if (redis->sock->err) {
             REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0);

From e41e19a8342212ee9cfe35f622804c9870d05ec2 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 3 Jun 2020 01:40:08 +0300
Subject: [PATCH 1337/1986] Don't call Redis::__constructor while initilizing
 RedisArray (#1777)

---
 redis_array_impl.c | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/redis_array_impl.c b/redis_array_impl.c
index d218ee6cc6..82715d0766 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -36,16 +36,12 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in
     int i = 0, host_len;
     char *host, *p;
     short port;
-    zval *zpData, z_cons, z_ret;
+    zval *zpData;
     redis_object *redis;
 
-    /* function calls on the Redis object */
-    ZVAL_STRINGL(&z_cons, "__construct", 11);
-
     /* init connections */
     ZEND_HASH_FOREACH_VAL(hosts, zpData) {
         if (Z_TYPE_P(zpData) != IS_STRING) {
-            zval_dtor(&z_cons);
             return NULL;
         }
 
@@ -64,9 +60,6 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in
 
         /* create Redis object */
         object_init_ex(&ra->redis[i], redis_ce);
-        call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL);
-        zval_dtor(&z_ret);
-
         redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, &ra->redis[i]);
 
         /* create socket */
@@ -79,7 +72,6 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in
         {
             /* connect */
             if (redis_sock_server_open(redis->sock) < 0) {
-                zval_dtor(&z_cons);
                 ra->count = ++i;
                 return NULL;
             }
@@ -88,8 +80,6 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in
         ra->count = ++i;
     } ZEND_HASH_FOREACH_END();
 
-    zval_dtor(&z_cons);
-
     return ra;
 }
 

From 890ee0e656e545b18179cf247db94a33179ce1ab Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 4 Jun 2020 08:42:34 +0300
Subject: [PATCH 1338/1986] TravisCI: test tls connect

---
 .travis.yml                |  5 ++++-
 tests/RedisClusterTest.php |  1 +
 tests/RedisTest.php        | 11 +++++++++++
 3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index 15bb8c833e..52296b1d49 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,3 @@
-sudo: required
 language: php
 php:
   - 7.0
@@ -32,6 +31,7 @@ addons:
     - clang
     - libzstd1-dev
     - valgrind
+    - stunnel
 before_install:
   - phpize
   - CFGARGS="--enable-redis-lzf --enable-redis-zstd"
@@ -47,6 +47,9 @@ before_script:
   - for PORT in $(seq 26379 26380); do wget download.redis.io/redis-stable/sentinel.conf -O $PORT.conf; echo sentinel auth-pass mymaster phpredis >> $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
   - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 -a phpredis
   - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
+  - openssl req -x509 -newkey rsa:1024 -nodes -keyout stunnel.key -out stunnel.pem -days 1 -subj '/CN=localhost'
+  - echo -e 'key=stunnel.key\ncert=stunnel.pem\npid=/tmp/stunnel.pid\n[redis]\naccept=6378\nconnect=6379' > stunnel.conf
+  - stunnel stunnel.conf
 script:
   - php tests/TestRedis.php --class Redis --auth phpredis
   - php tests/TestRedis.php --class RedisArray --auth phpredis
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index e5c66cc6cd..972c64f6b9 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -46,6 +46,7 @@ public function testMultipleConnect() { return $this->markTestSkipped(); }
     public function testDoublePipeNoOp() { return $this->markTestSkipped(); }
     public function testSwapDB() { return $this->markTestSkipped(); }
     public function testConnectException() { return $this->markTestSkipped(); }
+    public function testTlsConnect() { return $this->markTestSkipped(); }
 
     /* Session locking feature is currently not supported in in context of Redis Cluster.
        The biggest issue for this is the distribution nature of Redis cluster */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 95cbe64b71..bbc007cc84 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6236,6 +6236,17 @@ public function testConnectException() {
         }
     }
 
+    public function testTlsConnect()
+    {
+        foreach (['localhost' => true, '127.0.0.1' => false] as $host => $verify) {
+            $redis = new Redis();
+            $this->assertTrue($redis->connect('tls://' . $host, 6378, 0, null, 0, 0, [
+                'verify_peer_name' => $verify,
+                'verify_peer' => false,
+            ]));
+        }
+    }
+
     public  function testSession_regenerateSessionId_noLock_noDestroy() {
         $this->setSessionHandler();
         $sessionId = $this->generateSessionId();

From 58dab5649fcc2cc63f5a29df83f783e154d7fa22 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 5 Jun 2020 10:27:48 +0300
Subject: [PATCH 1339/1986] Store auth information in cluster->flags->auth

---
 cluster_library.c | 14 ++++++--------
 cluster_library.h |  1 -
 redis_cluster.c   |  2 +-
 redis_session.c   |  2 +-
 4 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index b21a687585..0a54be3bbc 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -637,8 +637,8 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len,
     node->sock = redis_sock_create(host, host_len, port, c->timeout,
         c->read_timeout, c->persistent, NULL, 0);
 
-    if (c->auth) {
-        node->sock->auth = zend_string_copy(c->auth);
+    if (c->flags->auth) {
+        node->sock->auth = zend_string_copy(c->flags->auth);
     }
 
     return node;
@@ -826,7 +826,6 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout,
     c->read_timeout = read_timeout;
     c->failover = failover;
     c->persistent = persistent;
-    c->auth = NULL;
     c->err = NULL;
 
     /* Set up our waitms based on timeout */
@@ -851,6 +850,8 @@ cluster_free(redisCluster *c, int free_ctx)
 
     /* Free any allocated prefix */
     if (c->flags->prefix) zend_string_release(c->flags->prefix);
+    /* Free auth info we've got */
+    if (c->flags->auth) zend_string_release(c->flags->auth);
     efree(c->flags);
 
     /* Call hash table destructors */
@@ -861,9 +862,6 @@ cluster_free(redisCluster *c, int free_ctx)
     efree(c->seeds);
     efree(c->nodes);
 
-    /* Free auth info we've got */
-    if (c->auth) zend_string_release(c->auth);
-
     /* Free any error we've got */
     if (c->err) zend_string_release(c->err);
 
@@ -1089,8 +1087,8 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) {
             cluster->read_timeout, cluster->persistent, NULL, 0);
 
         // Set auth information if specified
-        if (cluster->auth) {
-            redis_sock->auth = zend_string_copy(cluster->auth);
+        if (cluster->flags->auth) {
+            redis_sock->auth = zend_string_copy(cluster->flags->auth);
         }
 
         // Index this seed by host/port
diff --git a/cluster_library.h b/cluster_library.h
index fbf050fe96..b7a365ef9a 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -183,7 +183,6 @@ typedef struct clusterFoldItem clusterFoldItem;
 
 /* RedisCluster implementation structure */
 typedef struct redisCluster {
-    zend_string *auth;
 
     /* Timeout and read timeout (for normal operations) */
     double timeout;
diff --git a/redis_cluster.c b/redis_cluster.c
index 0fc94fe3a5..6b3dfa5e29 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -370,7 +370,7 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time
     cluster_validate_args(timeout, read_timeout, ht_seeds);
 
     if (auth && auth_len > 0) {
-        c->auth = zend_string_init(auth, auth_len, 0);
+        c->flags->auth = zend_string_init(auth, auth_len, 0);
     }
 
     c->timeout = timeout;
diff --git a/redis_session.c b/redis_session.c
index 8fc1778e51..e96c4c8b49 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -962,7 +962,7 @@ PS_OPEN_FUNC(rediscluster) {
 
     c = cluster_create(timeout, read_timeout, failover, persistent);
     if (auth && auth_len > 0) {
-        c->auth = zend_string_init(auth, auth_len, 0);
+        c->flags->auth = zend_string_init(auth, auth_len, 0);
     }
 
     redisCachedCluster *cc;

From a0c53e0b30e0c6af15cc137415e7d65f6d1867f7 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Sun, 7 Jun 2020 13:48:06 -0700
Subject: [PATCH 1340/1986] Issue.1762 xinfo full (#1771)

Add support for `XINFO STREAM FULL [COUNT]`
---
 README.markdown     |  3 ++-
 redis_commands.c    | 38 +++++++++++++++++++++++++++-----------
 tests/RedisTest.php | 25 +++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 12 deletions(-)

diff --git a/README.markdown b/README.markdown
index 1b81f7784d..b2eda95284 100644
--- a/README.markdown
+++ b/README.markdown
@@ -3671,7 +3671,7 @@ $obj_redis->xGroup('DESTROY', 'mystream', 'mygroup');
 ~~~php
 $obj_redis->xInfo('CONSUMERS', $str_stream, $str_group);
 $obj_redis->xInfo('GROUPS', $str_stream);
-$obj_redis->xInfo('STREAM', $str_stream);
+$obj_redis->xInfo('STREAM', $str_stream [, 'FULL' [, $i_count]]);
 $obj_redis->xInfo('HELP');
 ~~~
 
@@ -3683,6 +3683,7 @@ _**Description**_:  Get information about a stream or consumer groups.
 ##### *Example*
 ~~~php
 $obj_redis->xInfo('STREAM', 'mystream');
+$obj_redis->xInfo('STREAM', 'mystream', 'FULL', 10);
 ~~~
 
 ### xLen
diff --git a/redis_commands.c b/redis_commands.c
index 942b22721a..f07fdfcb8b 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3816,33 +3816,49 @@ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return FAILURE;
 }
 
-
-
 /* XINFO CONSUMERS key group
  * XINFO GROUPS key
- * XINFO STREAM key
+ * XINFO STREAM key [FULL [COUNT N]]
  * XINFO HELP */
 int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                      char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    char *op, *key, *arg;
+    char *op, *key, *arg = NULL;
     size_t oplen, keylen, arglen;
-    char fmt[4];
+    zend_long count = -1;
     int argc = ZEND_NUM_ARGS();
+    char fmt[] = "skssl";
 
-    if (argc > 3 || zend_parse_parameters(ZEND_NUM_ARGS(), "s|ss",
+    if (argc > 4 || zend_parse_parameters(ZEND_NUM_ARGS(), "s|ssl",
                                           &op, &oplen, &key, &keylen, &arg,
-                                          &arglen) == FAILURE)
+                                          &arglen, &count) == FAILURE)
     {
         return FAILURE;
     }
 
-    /* Our format is simply "s", "sk" or "sks" depending on argc */
-    memcpy(fmt, "sks", sizeof("sks")-1);
+    /* Handle everything except XINFO STREAM */
+    if (strncasecmp(op, "STREAM", 6) != 0) {
+        fmt[argc] = '\0';
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XINFO", fmt, op, oplen, key, keylen,
+                                      arg, arglen);
+        return SUCCESS;
+    }
+
+    /* 'FULL' is the only legal option to XINFO STREAM */
+    if (argc > 2 && strncasecmp(arg, "FULL", 4) != 0) {
+        php_error_docref(NULL, E_WARNING, "'%s' is not a valid option for XINFO STREAM", arg);
+        return FAILURE;
+    }
+
+    /* If we have a COUNT bump the argument count to account for the 'COUNT' literal */
+    if (argc == 4) argc++;
+
     fmt[argc] = '\0';
 
-    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XINFO", fmt, op, oplen, key, keylen,
-                                  arg, arglen);
+    /* Build our XINFO STREAM variant */
+    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XINFO", fmt, "STREAM", 6, key, keylen,
+                                  "FULL", 4, "COUNT", 5, count);
+
     return SUCCESS;
 }
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index bbc007cc84..f029d80dc0 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5975,6 +5975,31 @@ public function testXInfo()
             $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 ([-1, 0] 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));
+        }
     }
 
     /* If we detect a unix socket make sure we can connect to it in a variety of ways */

From 5ca4141c72e23816f146b49877a6a4b8098b34c6 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Sun, 7 Jun 2020 13:50:22 -0700
Subject: [PATCH 1341/1986] Issue.1765 (#1774)

Various improvements and fixes to cluster slot caching.

* Improves slot caching so any unique set of seeds all hash to the same key

* Fix a couple of memory leaks.

* Fixes a segfault when executing a multiple key command such as `MGET` or `MSET` while the cluster is resharding.
---
 cluster_library.c | 273 +++++++++++++++++++++++++++-------------------
 cluster_library.h |  21 +++-
 redis_cluster.c   |  70 ++++++------
 redis_session.c   |  67 ++++++++----
 4 files changed, 255 insertions(+), 176 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 0a54be3bbc..8d244a431f 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -865,12 +865,14 @@ cluster_free(redisCluster *c, int free_ctx)
     /* Free any error we've got */
     if (c->err) zend_string_release(c->err);
 
-    /* Invalidate our cache if we were redirected during operation */
     if (c->cache_key) {
+        /* Invalidate persistent cache if the cluster has changed */
         if (c->redirections) {
             zend_hash_del(&EG(persistent_list), c->cache_key);
-            c->cache_key = NULL;
         }
+
+        /* Release our hold on the cache key */
+        zend_string_release(c->cache_key);
     }
 
     /* Free structure itself */
@@ -921,34 +923,6 @@ redisCachedCluster *cluster_cache_create(zend_string *hash, HashTable *nodes) {
     return cc;
 }
 
-/* Takes our input hash table and returns a straight C array with elements,
- * which have been randomized.  The return value needs to be freed. */
-static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) {
-    zval **z_seeds, *z_ele;
-    int *map, i, count, index = 0;
-
-    /* How many */
-    count = zend_hash_num_elements(seeds);
-
-    /* Allocate our return value and map */
-    z_seeds = ecalloc(count, sizeof(zval*));
-    map = emalloc(sizeof(int)*count);
-
-    /* Fill in and shuffle our map */
-    for (i = 0; i < count; i++) map[i] = i;
-    fyshuffle(map, count);
-
-    /* Iterate over our source array and use our map to create a random list */
-    ZEND_HASH_FOREACH_VAL(seeds, z_ele) {
-        z_seeds[map[index++]] = z_ele;
-    } ZEND_HASH_FOREACH_END();
-
-    efree(map);
-
-    *len = count;
-    return z_seeds;
-}
-
 static void cluster_free_cached_master(redisCachedMaster *cm) {
     size_t i;
 
@@ -988,7 +962,6 @@ PHP_REDIS_API void cluster_cache_free(redisCachedCluster *rcc) {
         cluster_free_cached_master(&rcc->master[i]);
     }
 
-    /* Free hash key */
     zend_string_release(rcc->hash);
     pefree(rcc->master, 1);
     pefree(rcc, 1);
@@ -1009,17 +982,16 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) {
     for (i = 0; i < cc->count; i++) map[i] = i;
     fyshuffle(map, cc->count);
 
+    /* Duplicate the hash key so we can invalidate when redirected */
+    c->cache_key = zend_string_copy(cc->hash);
+
     /* Iterate over masters */
     for (i = 0; i < cc->count; i++) {
-        /* Attach cache key */
-        c->cache_key = cc->hash;
-
         /* Grab the next master */
         cm = &cc->master[map[i]];
 
         /* Hash our host and port */
-        keylen = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(cm->host.addr),
-                          cm->host.port);
+        keylen = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(cm->host.addr), cm->host.port);
 
         /* Create socket */
         sock = redis_sock_create(ZSTR_VAL(cm->host.addr), ZSTR_LEN(cm->host.addr), cm->host.port,
@@ -1053,56 +1025,45 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) {
     efree(map);
 }
 
-/* Initialize seeds */
-PHP_REDIS_API int
-cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) {
-    RedisSock *redis_sock;
-    char *str, *psep, key[1024];
-    int key_len, count, i;
-    zval **z_seeds, *z_seed;
-
-    /* Get our seeds in a randomized array */
-    z_seeds = cluster_shuffle_seeds(ht_seeds, &count);
-
-    // Iterate our seeds array
-    for (i = 0; i < count; i++) {
-        if ((z_seed = z_seeds[i]) == NULL) continue;
-
-        ZVAL_DEREF(z_seed);
+/* Initialize seeds.  By the time we get here we've already validated our
+ * seeds array and know we have a non-empty array of strings all in
+ * host:port format. */
+PHP_REDIS_API void
+cluster_init_seeds(redisCluster *cluster, zend_string **seeds, uint32_t nseeds) {
+    RedisSock *sock;
+    char *seed, *sep, key[1024];
+    int key_len, i, *map;
 
-        /* Has to be a string */
-        if (Z_TYPE_P(z_seed) != IS_STRING) continue;
+    /* Get a randomized order to hit our seeds */
+    map = ecalloc(nseeds, sizeof(*map));
+    for (i = 0; i < nseeds; i++) map[i] = i;
+    fyshuffle(map, nseeds);
 
-        // Grab a copy of the string
-        str = Z_STRVAL_P(z_seed);
+    for (i = 0; i < nseeds; i++) {
+        seed = ZSTR_VAL(seeds[map[i]]);
 
-        /* Make sure we have a colon for host:port.  Search right to left in the
-         * case of IPv6 */
-        if ((psep = strrchr(str, ':')) == NULL)
-            continue;
+        sep = strrchr(seed, ':');
+        ZEND_ASSERT(sep != NULL);
 
         // Allocate a structure for this seed
-        redis_sock = redis_sock_create(str, psep-str,
-            (unsigned short)atoi(psep+1), cluster->timeout,
+        sock = redis_sock_create(seed, sep - seed,
+            (unsigned short)atoi(sep+1), cluster->timeout,
             cluster->read_timeout, cluster->persistent, NULL, 0);
 
         // Set auth information if specified
         if (cluster->flags->auth) {
-            redis_sock->auth = zend_string_copy(cluster->flags->auth);
+            sock->auth = zend_string_copy(cluster->flags->auth);
         }
 
         // Index this seed by host/port
-        key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(redis_sock->host),
-            redis_sock->port);
+        key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(sock->host),
+            sock->port);
 
         // Add to our seed HashTable
-        zend_hash_str_update_ptr(cluster->seeds, key, key_len, redis_sock);
+        zend_hash_str_update_ptr(cluster->seeds, key, key_len, sock);
     }
 
-    efree(z_seeds);
-
-    // Success if at least one seed seems valid
-    return zend_hash_num_elements(cluster->seeds) > 0 ? SUCCESS : FAILURE;
+    efree(map);
 }
 
 /* Initial mapping of our cluster keyspace */
@@ -1137,7 +1098,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c) {
     // Throw an exception if we couldn't map
     if (!mapped) {
         CLUSTER_THROW_EXCEPTION("Couldn't map cluster keyspace using any provided seed", 0);
-        return -1;
+        return FAILURE;
     }
 
     return SUCCESS;
@@ -1626,11 +1587,9 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char
         redis_sock_disconnect(c->cmd_sock, 1);
 
         if (timedout) {
-            CLUSTER_THROW_EXCEPTION(
-                "Timed out attempting to find data in the correct node!", 0);
+            CLUSTER_THROW_EXCEPTION("Timed out attempting to find data in the correct node!", 0);
         } else {
-            CLUSTER_THROW_EXCEPTION(
-                "Error processing response from Redis node!", 0);
+            CLUSTER_THROW_EXCEPTION("Error processing response from Redis node!", 0);
         }
 
         return -1;
@@ -2460,10 +2419,11 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluste
     // Set return value if it's our last response
     if (mctx->last) {
         if (CLUSTER_IS_ATOMIC(c)) {
-            RETVAL_ZVAL(mctx->z_multi, 0, 1);
+            RETVAL_ZVAL(mctx->z_multi, 0, 0);
         } else {
             add_next_index_zval(&c->multi_resp, mctx->z_multi);
         }
+        efree(mctx->z_multi);
     }
 
     // Free multi context
@@ -2745,42 +2705,145 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result,
     return SUCCESS;
 }
 
-/* Turn a seed array into a zend_string we can use to look up a slot cache */
-zend_string *cluster_hash_seeds(HashTable *ht) {
-    smart_str hash = {0};
-    zend_string *zstr;
+/* Free an array of zend_string seeds */
+void free_seed_array(zend_string **seeds, uint32_t nseeds) {
+    int i;
+
+    if (seeds == NULL)
+        return;
+
+    for (i = 0; i < nseeds; i++) 
+        zend_string_release(seeds[i]);
+
+    efree(seeds);
+}
+
+static zend_string **get_valid_seeds(HashTable *input, uint32_t *nseeds) {
+    HashTable *valid;
+    uint32_t count, idx = 0;
     zval *z_seed;
+    zend_string *zkey, **seeds = NULL;
+
+    /* Short circuit if we don't have any sees */
+    count = zend_hash_num_elements(input);
+    if (count == 0)
+        return NULL;
 
-    ZEND_HASH_FOREACH_VAL(ht, z_seed) {
-        zstr = zval_get_string(z_seed);
+    ALLOC_HASHTABLE(valid);
+    zend_hash_init(valid, count, NULL, NULL, 0);
+
+    ZEND_HASH_FOREACH_VAL(input, z_seed) {
+        ZVAL_DEREF(z_seed);
+
+        if (Z_TYPE_P(z_seed) != IS_STRING) {
+            php_error_docref(NULL, E_WARNING, "Skipping non-string entry in seeds array");
+            continue;
+        } else if (strrchr(Z_STRVAL_P(z_seed), ':') == NULL) {
+            php_error_docref(NULL, E_WARNING,
+                "Seed '%s' not in host:port format, ignoring", Z_STRVAL_P(z_seed));
+            continue;
+        }
+
+        /* Add as a key to avoid duplicates */
+        zend_hash_str_update_ptr(valid, Z_STRVAL_P(z_seed), Z_STRLEN_P(z_seed), NULL);
+    } ZEND_HASH_FOREACH_END();
+
+    /* We need at least one valid seed */
+    count = zend_hash_num_elements(valid);
+    if (count == 0)
+        goto cleanup;
+
+    /* Populate our return array */
+    seeds = ecalloc(count, sizeof(*seeds));
+    ZEND_HASH_FOREACH_STR_KEY(valid, zkey) {
+        seeds[idx++] = zend_string_copy(zkey);
+    } ZEND_HASH_FOREACH_END();
+
+    *nseeds = idx;
+
+cleanup:
+    zend_hash_destroy(valid);
+    FREE_HASHTABLE(valid);
+
+    return seeds;
+}
+
+/* Validate cluster construction arguments and return a sanitized and validated
+ * array of seeds */
+zend_string**
+cluster_validate_args(double timeout, double read_timeout, HashTable *seeds, 
+                      uint32_t *nseeds, char **errstr) 
+{
+    zend_string **retval;
+    
+    if (timeout < 0L || timeout > INT_MAX) {
+        if (errstr) *errstr = "Invalid timeout";
+        return NULL;
+    }
+
+    if (read_timeout < 0L || read_timeout > INT_MAX) {
+        if (errstr) *errstr = "Invalid read timeout";
+        return NULL;
+    }
+
+    retval = get_valid_seeds(seeds, nseeds);
+    if (retval == NULL && errstr)
+        *errstr = "No valid seeds detected";
+
+    return retval;
+}
+
+/* Helper function to compare to host:port seeds */
+static int cluster_cmp_seeds(const void *a, const void *b) {
+    zend_string *za = *(zend_string **)a;
+    zend_string *zb = *(zend_string **)b;
+    return strcmp(ZSTR_VAL(za), ZSTR_VAL(zb));
+}
+
+static void cluster_swap_seeds(void *a, void *b) {
+    zend_string **za, **zb, *tmp;
+
+    za = a;
+    zb = b;
+
+    tmp = *za;
+    *za = *zb;
+    *zb = tmp;
+}
+
+/* Turn an array of cluster seeds into a string we can cache.  If we get here we know
+ * we have at least one entry and that every entry is a string in the form host:port */
+#define SLOT_CACHE_PREFIX "phpredis_slots:"
+zend_string *cluster_hash_seeds(zend_string **seeds, uint32_t count) {
+    smart_str hash = {0};
+    size_t i;
+
+    /* Sort our seeds so any any array with identical seeds hashes to the same key
+     * regardless of what order the user gives them to us in. */
+    zend_sort(seeds, count, sizeof(*seeds), cluster_cmp_seeds, cluster_swap_seeds);
+
+    /* Global phpredis hash prefix */
+    smart_str_appendl(&hash, SLOT_CACHE_PREFIX, sizeof(SLOT_CACHE_PREFIX) - 1);
+
+    /* Construct our actual hash */
+    for (i = 0; i < count; i++) {
         smart_str_appendc(&hash, '[');
-        smart_str_appendl(&hash, ZSTR_VAL(zstr), ZSTR_LEN(zstr));
+        smart_str_append_ex(&hash, seeds[i], 0);
         smart_str_appendc(&hash, ']');
-        zend_string_release(zstr);
-    } ZEND_HASH_FOREACH_END();
+    }
 
-    /* Not strictly needed but null terminate anyway */
+    /* Null terminate */
     smart_str_0(&hash);
 
-    /* smart_str is a zend_string internally */
+    /* Return the internal zend_string */
     return hash.s;
 }
 
-
-#define SLOT_CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1)
-PHP_REDIS_API redisCachedCluster *cluster_cache_load(HashTable *ht_seeds) {
+PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash) {
     zend_resource *le;
-    zend_string *h;
-
-    /* Short circuit if we're not caching slots or if our seeds don't have any
-     * elements, since it doesn't make sense to cache an empty string */
-    if (!SLOT_CACHING_ENABLED() || zend_hash_num_elements(ht_seeds) == 0)
-        return NULL;
 
     /* Look for cached slot information */
-    h = cluster_hash_seeds(ht_seeds);
-    le = zend_hash_find_ptr(&EG(persistent_list), h);
-    zend_string_release(h);
+    le = zend_hash_find_ptr(&EG(persistent_list), hash);
 
     if (le != NULL) {
         /* Sanity check on our list type */
@@ -2798,21 +2861,11 @@ PHP_REDIS_API redisCachedCluster *cluster_cache_load(HashTable *ht_seeds) {
 }
 
 /* Cache a cluster's slot information in persistent_list if it's enabled */
-PHP_REDIS_API int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes) {
+PHP_REDIS_API int cluster_cache_store(zend_string *hash, HashTable *nodes) {
     redisCachedCluster *cc;
-    zend_string *hash;
-
-    /* Short circuit if caching is disabled or there aren't any seeds */
-    if (!SLOT_CACHING_ENABLED()) {
-        return SUCCESS;
-    } else if (zend_hash_num_elements(ht_seeds) == 0) {
-        return FAILURE;
-    }
 
     /* Construct our cache */
-    hash = cluster_hash_seeds(ht_seeds);
     cc = cluster_cache_create(hash, nodes);
-    zend_string_release(hash);
 
     /* Set up our resource */
 #if PHP_VERSION_ID < 70300
diff --git a/cluster_library.h b/cluster_library.h
index b7a365ef9a..062db790f8 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -129,6 +129,8 @@
     mc.kw     = keyword; \
     mc.kw_len = keyword_len; \
 
+#define CLUSTER_CACHING_ENABLED() (INI_INT("redis.clusters.cache_slots") == 1)
+
 /* Cluster redirection enum */
 typedef enum CLUSTER_REDIR_TYPE {
     REDIR_NONE,
@@ -358,6 +360,15 @@ void cluster_multi_fini(clusterMultiCmd *mc);
 unsigned short cluster_hash_key_zval(zval *key);
 unsigned short cluster_hash_key(const char *key, int len);
 
+/* Validate and sanitize cluster construction args */
+zend_string** cluster_validate_args(double timeout, double read_timeout, 
+    HashTable *seeds, uint32_t *nseeds, char **errstr);
+
+void free_seed_array(zend_string **seeds, uint32_t nseeds);
+
+/* Generate a unique hash string from seeds array */
+zend_string *cluster_hash_seeds(zend_string **seeds, uint32_t nseeds);
+
 /* Get the current time in milliseconds */
 long long mstime(void);
 
@@ -379,7 +390,7 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd,
 PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout,
     int failover, int persistent);
 PHP_REDIS_API void cluster_free(redisCluster *c, int free_ctx);
-PHP_REDIS_API int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds);
+PHP_REDIS_API void cluster_init_seeds(redisCluster *c, zend_string **seeds, uint32_t nseeds);
 PHP_REDIS_API int cluster_map_keyspace(redisCluster *c);
 PHP_REDIS_API void cluster_free_node(redisClusterNode *node);
 
@@ -390,10 +401,10 @@ PHP_REDIS_API void cluster_init_cache(redisCluster *c, redisCachedCluster *rcc);
 
 /* Functions to facilitate cluster slot caching */
 
-PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock,
-    int *len);
-PHP_REDIS_API int cluster_cache_store(HashTable *ht_seeds, HashTable *nodes);
-PHP_REDIS_API redisCachedCluster *cluster_cache_load(HashTable *ht_seeds);
+PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len);
+
+PHP_REDIS_API int cluster_cache_store(zend_string *hash, HashTable *nodes);
+PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash);
 
 /*
  * Redis Cluster response handlers.  Our response handlers generally take the
diff --git a/redis_cluster.c b/redis_cluster.c
index 6b3dfa5e29..a2ced22a86 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -340,55 +340,51 @@ void free_cluster_context(zend_object *object) {
     zend_object_std_dtor(&cluster->std);
 }
 
-/* Validate redis cluster construction arguments */
-static int
-cluster_validate_args(double timeout, double read_timeout, HashTable *seeds) {
-    if (timeout < 0L || timeout > INT_MAX) {
-        CLUSTER_THROW_EXCEPTION("Invalid timeout", 0);
-        return FAILURE;
-    }
-    if (read_timeout < 0L || read_timeout > INT_MAX) {
-        CLUSTER_THROW_EXCEPTION("Invalid read timeout", 0);
-        return FAILURE;
-    }
-    /* Make sure there are some seeds */
-    if (zend_hash_num_elements(seeds) == 0) {
-        CLUSTER_THROW_EXCEPTION("Must pass seeds", 0);
-        return FAILURE;
-    }
-
-    return SUCCESS;
-}
-
+/* Take user provided seeds and return unique and valid ones */
 /* Attempt to connect to a Redis cluster provided seeds and timeout options */
 static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout,
                                double read_timeout, int persistent, char *auth,
                                size_t auth_len)
 {
+    zend_string *hash = NULL, **seeds;
     redisCachedCluster *cc;
+    uint32_t nseeds;
+    char *err;
 
-    cluster_validate_args(timeout, read_timeout, ht_seeds);
+    /* Validate our arguments and get a sanitized seed array */
+    seeds = cluster_validate_args(timeout, read_timeout, ht_seeds, &nseeds, &err);
+    if (seeds == NULL) {
+        CLUSTER_THROW_EXCEPTION(err, 0);
+        return;
+    }
 
-    if (auth && auth_len > 0) {
+    if (auth && auth_len) {
         c->flags->auth = zend_string_init(auth, auth_len, 0);
     }
 
     c->timeout = timeout;
     c->read_timeout = read_timeout;
     c->persistent = persistent;
+    c->waitms = timeout * 1000L;
+
+    /* Attempt to load slots from cache if caching is enabled */
+    if (CLUSTER_CACHING_ENABLED()) {
+        /* Exit early if we can load from cache */
+        hash = cluster_hash_seeds(seeds, nseeds);
+        if ((cc = cluster_cache_load(hash))) {
+            cluster_init_cache(c, cc);
+            goto cleanup;
+        }
+    }
 
-    /* Calculate the number of milliseconds we will wait when bouncing around,
-     * (e.g. a node goes down), which is not the same as a standard timeout. */
-    c->waitms = (long)(timeout * 1000);
+    /* Initialize seeds and attempt to map keyspace */
+    cluster_init_seeds(c, seeds, nseeds);
+    if (cluster_map_keyspace(c) == SUCCESS && hash)
+        cluster_cache_store(hash, c->nodes);
 
-    /* Attempt to load from cache */
-    if ((cc = cluster_cache_load(ht_seeds))) {
-        cluster_init_cache(c, cc);
-    } else if (cluster_init_seeds(c, ht_seeds) == SUCCESS &&
-               cluster_map_keyspace(c) == SUCCESS)
-    {
-        cluster_cache_store(ht_seeds, c->nodes);
-    }
+cleanup:
+    if (hash) zend_string_release(hash);
+    free_seed_array(seeds, nseeds);
 }
 
 
@@ -556,11 +552,7 @@ distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot,
     ctx->last    = last;
 
     // Attempt to send the command
-    if (cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len) < 0 ||
-       c->err != NULL)
-    {
-        cluster_multi_free(mc);
-        zval_dtor(z_ret);
+    if (cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len) < 0 || c->err != NULL) {
         efree(ctx);
         return -1;
     }
@@ -865,6 +857,7 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
             if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
                                     slot, &mc, z_ret, i == argc, cb) < 0)
             {
+                cluster_multi_free(&mc);
                 return -1;
             }
         }
@@ -890,6 +883,7 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
         if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc,
                                 z_ret, 1, cb) < 0)
         {
+            cluster_multi_free(&mc);
             return -1;
         }
     }
diff --git a/redis_session.c b/redis_session.c
index e96c4c8b49..e3358148d2 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -894,7 +894,7 @@ PS_OPEN_FUNC(rediscluster) {
     zval z_conf, *z_val;
     HashTable *ht_conf, *ht_seeds;
     double timeout = 0, read_timeout = 0;
-    int retval, persistent = 0, failover = REDIS_FAILOVER_NONE;
+    int persistent = 0, failover = REDIS_FAILOVER_NONE;
     size_t prefix_len, auth_len = 0;
     char *prefix, *auth = NULL;
 
@@ -960,35 +960,56 @@ PS_OPEN_FUNC(rediscluster) {
         auth_len = Z_STRLEN_P(z_val);
     }
 
+    redisCachedCluster *cc;
+    zend_string **seeds, *hash = NULL;
+    uint32_t nseeds;
+
+    /* Extract at least one valid seed or abort */ 
+    seeds = cluster_validate_args(timeout, read_timeout, ht_seeds, &nseeds, NULL);
+    if (seeds == NULL) {
+        php_error_docref(NULL, E_WARNING, "No valid seeds detected");
+        zval_dtor(&z_conf);
+        return FAILURE;
+    }
+
+    #define CLUSTER_SESSION_CLEANUP() \
+        if (hash) zend_string_release(hash); \
+        free_seed_array(seeds, nseeds); \
+        zval_dtor(&z_conf); \
+
     c = cluster_create(timeout, read_timeout, failover, persistent);
-    if (auth && auth_len > 0) {
+    c->flags->prefix = zend_string_init(prefix, prefix_len, 0);
+
+    if (auth && auth_len) 
         c->flags->auth = zend_string_init(auth, auth_len, 0);
+
+    /* First attempt to load from cache */
+    if (CLUSTER_CACHING_ENABLED()) {
+        hash = cluster_hash_seeds(seeds, nseeds);
+        if ((cc = cluster_cache_load(hash))) {
+            cluster_init_cache(c, cc);
+            goto success;
+        }
     }
 
-    redisCachedCluster *cc;
+    /* Initialize seed array, and attempt to map keyspace */
+    cluster_init_seeds(c, seeds, nseeds);
+    if (cluster_map_keyspace(c) != SUCCESS)
+        goto failure;
 
-    /* Attempt to load from cache */
-    if ((cc = cluster_cache_load(ht_seeds))) {
-        cluster_init_cache(c, cc);
-        /* Set up our prefix */
-        c->flags->prefix = zend_string_init(prefix, prefix_len, 0);
-        PS_SET_MOD_DATA(c);
-        retval = SUCCESS;
-    } else if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c)) {
-        /* Set up our prefix */
-        c->flags->prefix = zend_string_init(prefix, prefix_len, 0);
-        cluster_cache_store(ht_seeds, c->nodes);
-        PS_SET_MOD_DATA(c);
-        retval = SUCCESS;
-    } else {
-        cluster_free(c, 1);
-        retval = FAILURE;
-    }
+    /* Now cache our cluster if caching is enabled */
+    if (hash)
+        cluster_cache_store(hash, c->nodes);
 
-    /* Cleanup */
-    zval_dtor(&z_conf);
+success:
+    CLUSTER_SESSION_CLEANUP();
+    PS_SET_MOD_DATA(c);
+    return SUCCESS;
 
-    return retval;
+failure:
+    CLUSTER_SESSION_CLEANUP();
+    cluster_free(c, 1);
+    return FAILURE;
 }
 
 /* {{{ PS_READ_FUNC

From 04def9fbe2194b3b711362de57260a6cd5216e69 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Sun, 7 Jun 2020 14:09:30 -0700
Subject: [PATCH 1342/1986] Rebased LZ4 PR (#1781)

LZ4 compression by @iliaal
---
 common.h            |   1 +
 config.m4           |  31 ++++++++++
 library.c           | 138 +++++++++++++++++++++++++++++++++++++++++++-
 redis.c             |  14 +++++
 redis_commands.c    |   3 +
 tests/RedisTest.php |  22 +++++++
 6 files changed, 208 insertions(+), 1 deletion(-)

diff --git a/common.h b/common.h
index dc692524ee..14aefb53a3 100644
--- a/common.h
+++ b/common.h
@@ -100,6 +100,7 @@ typedef enum {
 #define REDIS_COMPRESSION_NONE 0
 #define REDIS_COMPRESSION_LZF  1
 #define REDIS_COMPRESSION_ZSTD 2
+#define REDIS_COMPRESSION_LZ4  3
 
 /* SCAN options */
 #define REDIS_SCAN_NORETRY 0
diff --git a/config.m4 b/config.m4
index 2bd148d16a..30449300ba 100644
--- a/config.m4
+++ b/config.m4
@@ -29,6 +29,12 @@ PHP_ARG_ENABLE(redis-zstd, whether to enable Zstd compression,
 PHP_ARG_WITH(libzstd, use system libsztd,
 [  --with-libzstd[=DIR]      Use system libzstd], yes, no)
 
+PHP_ARG_ENABLE(redis-lz4, whether to enable lz4 compression,
+[  --enable-redis-lz4      Enable lz4 compression support], no, no)
+
+PHP_ARG_WITH(liblz4, use system liblz4,
+[  --with-liblz4[=DIR]       Use system liblz4], no, no)
+
 if test "$PHP_REDIS" != "no"; then
 
   if test "$PHP_REDIS_SESSION" != "no"; then
@@ -194,6 +200,31 @@ if test "$PHP_REDIS" != "no"; then
     fi
   fi
 
+  if test "$PHP_REDIS_LZ4" != "no"; then
+      AC_DEFINE(HAVE_REDIS_LZ4, 1, [ ])
+      AC_MSG_CHECKING(for liblz4 files in default path)
+      for i in $PHP_LIBLZ4 /usr/local /usr; do
+        if test -r $i/include/lz4.h; then
+          AC_MSG_RESULT(found in $i)
+          LIBLZ4_DIR=$i
+          break
+        fi
+      done
+      if test -z "$LIBLZ4_DIR"; then
+        AC_MSG_RESULT([not found])
+        AC_MSG_ERROR([Please reinstall the liblz4 distribution])
+      fi
+      PHP_CHECK_LIBRARY(lz4, LZ4_compress,
+      [
+        PHP_ADD_LIBRARY_WITH_PATH(zstd, $LIBLZ4_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD)
+      ], [
+        AC_MSG_ERROR([could not find usable liblz4])
+      ], [
+        -L$LIBLZ4_DIR/$PHP_LIBDIR
+      ])
+      PHP_SUBST(REDIS_SHARED_LIBADD)
+  fi
+
   if test "$PHP_REDIS_ZSTD" != "no"; then
     AC_DEFINE(HAVE_REDIS_ZSTD, 1, [ ])
     if test "$PHP_LIBZSTD" != "no"; then
diff --git a/library.c b/library.c
index fcbb4fd738..d9fc53a529 100644
--- a/library.c
+++ b/library.c
@@ -25,6 +25,26 @@
 #include 
 #endif
 
+#ifdef HAVE_REDIS_LZ4
+#include 
+#include 
+
+/* uint8_t crf + int length */
+#define REDIS_LZ4_HDR_SIZE (sizeof(uint8_t) + sizeof(int))
+#if defined(LZ4HC_CLEVEL_MAX)
+/* version >= 1.7.5 */
+#define REDIS_LZ4_MAX_CLEVEL LZ4HC_CLEVEL_MAX
+
+#elif defined (LZ4HC_MAX_CLEVEL)
+/* version >= 1.7.3 */
+#define REDIS_LZ4_MAX_CLEVEL LZ4HC_MAX_CLEVEL
+
+#else
+/* older versions */
+#define REDIS_LZ4_MAX_CLEVEL 12
+#endif
+#endif
+
 #include 
 #include "php_redis.h"
 #include "library.h"
@@ -2280,6 +2300,26 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
     efree(redis_sock);
 }
 
+#ifdef HAVE_REDIS_LZ4
+/* Implementation of CRC8 for our LZ4 checksum value */
+static uint8_t crc8(unsigned char *input, size_t len) {
+    size_t i;
+    uint8_t crc = 0xFF;
+
+    while (len--) {
+        crc ^= *input++;
+        for (i = 0; i < 8; i++) {
+            if (crc & 0x80)
+                crc = (uint8_t)(crc << 1) ^ 0x31;
+            else
+                crc <<= 1;
+        }
+    }
+
+    return crc;
+}
+#endif
+
 PHP_REDIS_API int
 redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
 {
@@ -2288,6 +2328,12 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
     size_t len;
 
     valfree = redis_serialize(redis_sock, z, &buf, &len);
+    if (redis_sock->compression == REDIS_COMPRESSION_NONE) {
+        *val = buf;
+        *val_len = len;
+        return valfree;
+    }
+
     switch (redis_sock->compression) {
         case REDIS_COMPRESSION_LZF:
 #ifdef HAVE_REDIS_LZF
@@ -2340,6 +2386,54 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
                 }
                 efree(data);
             }
+#endif
+            break;
+        case REDIS_COMPRESSION_LZ4:
+#ifdef HAVE_REDIS_LZ4
+            {
+                /* Compressing empty data is pointless */
+                if (len < 1)
+                    break;
+
+                /* Compressing more than INT_MAX bytes would require multiple blocks */
+                if (len > INT_MAX) {
+                    php_error_docref(NULL, E_WARNING,
+                        "LZ4: compressing > %d bytes not supported", INT_MAX);
+                    break;
+                }
+
+                int old_len = len, lz4len, lz4bound;
+                uint8_t crc = crc8((unsigned char*)&old_len, sizeof(old_len));
+                char *lz4buf, *lz4pos;
+
+                lz4bound = LZ4_compressBound(len);
+                lz4buf = emalloc(REDIS_LZ4_HDR_SIZE + lz4bound);
+                lz4pos = lz4buf;
+
+                /* Copy and move past crc8 length checksum */
+                memcpy(lz4pos, &crc, sizeof(crc));
+                lz4pos += sizeof(crc);
+
+                /* Copy and advance past length */
+                memcpy(lz4pos, &old_len, sizeof(old_len));
+                lz4pos += sizeof(old_len);
+
+                if (redis_sock->compression_level <= 0 || redis_sock->compression_level > REDIS_LZ4_MAX_CLEVEL) {
+                    lz4len = LZ4_compress_default(buf, lz4pos, old_len, lz4bound);
+                } else {
+                    lz4len = LZ4_compress_HC(buf, lz4pos, old_len, lz4bound, redis_sock->compression_level);
+                }
+
+                if (lz4len <= 0) {
+                    efree(lz4buf);
+                    break;
+                }
+
+                if (valfree) efree(buf);
+                *val = lz4buf;
+                *val_len = lz4len + REDIS_LZ4_HDR_SIZE;
+                return 1;
+            }
 #endif
             break;
     }
@@ -2359,9 +2453,12 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
                 int i;
                 uint32_t res;
 
-                errno = E2BIG;
+                if (val_len == 0)
+                    break;
+
                 /* start from two-times bigger buffer and
                  * increase it exponentially  if needed */
+                errno = E2BIG;
                 for (i = 2; errno == E2BIG; i *= 2) {
                     data = emalloc(i * val_len);
                     if ((res = lzf_decompress(val, val_len, data, i * val_len)) == 0) {
@@ -2397,6 +2494,45 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
                     return 1;
                 }
             }
+#endif
+            break;
+        case REDIS_COMPRESSION_LZ4:
+#ifdef HAVE_REDIS_LZ4
+            {
+                char *data;
+                int datalen;
+                uint8_t lz4crc;
+
+                /* We must have at least enough bytes for our header, and can't have more than
+                 * INT_MAX + our header size. */
+                if (val_len < REDIS_LZ4_HDR_SIZE || val_len > INT_MAX + REDIS_LZ4_HDR_SIZE)
+                    break;
+
+                /* Operate on copies in case our CRC fails */
+                const char *copy = val;
+                size_t copylen = val_len;
+
+                /* Read in our header bytes */
+                memcpy(&lz4crc, copy, sizeof(uint8_t));
+                copy += sizeof(uint8_t); copylen -= sizeof(uint8_t);
+                memcpy(&datalen, copy, sizeof(int));
+                copy += sizeof(int); copylen -= sizeof(int);
+
+                /* Make sure our CRC matches (TODO:  Maybe issue a docref error?) */
+                if (crc8((unsigned char*)&datalen, sizeof(datalen)) != lz4crc)
+                    break;
+
+                /* Finally attempt decompression */
+                data = emalloc(datalen);
+                if (LZ4_decompress_safe(copy, data, copylen, datalen) > 0) {
+                    if (redis_unserialize(redis_sock, data, datalen, z_ret) == 0) {
+                        ZVAL_STRINGL(z_ret, data, datalen);
+                    }
+                    efree(data);
+                    return 1;
+                }
+                efree(data);
+            }
 #endif
             break;
     }
diff --git a/redis.c b/redis.c
index 46278abd68..71e301adb4 100644
--- a/redis.c
+++ b/redis.c
@@ -40,6 +40,10 @@
 #include 
 #endif
 
+#ifdef HAVE_REDIS_LZ4
+#include 
+#endif
+
 #ifdef PHP_SESSION
 extern ps_module ps_mod_redis;
 extern ps_module ps_mod_redis_cluster;
@@ -719,6 +723,10 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) {
     zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MAX"), ZSTD_maxCLevel());
 #endif
 
+#ifdef HAVE_REDIS_LZ4
+    zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZ4"), REDIS_COMPRESSION_LZ4);
+#endif
+
     /* scan options*/
     zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN);
     zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY);
@@ -886,6 +894,12 @@ PHP_MINFO_FUNCTION(redis)
         smart_str_appends(&names, ", ");
     }
     smart_str_appends(&names, "zstd");
+#endif
+#ifdef HAVE_REDIS_LZ4
+    if (names.s) {
+        smart_str_appends(&names, ", ");
+    }
+    smart_str_appends(&names, "lz4");
 #endif
     if (names.s) {
         smart_str_0(&names);
diff --git a/redis_commands.c b/redis_commands.c
index f07fdfcb8b..97442de387 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4002,6 +4002,9 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
 #endif
 #ifdef HAVE_REDIS_ZSTD
                 || val_long == REDIS_COMPRESSION_ZSTD
+#endif
+#ifdef HAVE_REDIS_LZ4
+                || val_long == REDIS_COMPRESSION_LZ4
 #endif
             ) {
                 redis_sock->compression = val_long;
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index f029d80dc0..3cf753d8a9 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4519,6 +4519,16 @@ public function testCompressionZSTD()
         $this->checkCompression(Redis::COMPRESSION_ZSTD, 9);
     }
 
+
+    public function testCompressionLZ4()
+    {
+        if (!defined('Redis::COMPRESSION_LZ4')) {
+            $this->markTestSkipped();
+        }
+        $this->checkCompression(Redis::COMPRESSION_LZ4, 0);
+        $this->checkCompression(Redis::COMPRESSION_LZ4, 9);
+    }
+
     private function checkCompression($mode, $level)
     {
         $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION, $mode) === TRUE);  // set ok
@@ -4530,6 +4540,18 @@ private function checkCompression($mode, $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'));
+            }
+        }
     }
 
     public function testDumpRestore() {

From a311cc4ec3cecdbaf83ba66985efa82137e37cc0 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Wed, 24 Jun 2020 17:00:01 -0700
Subject: [PATCH 1343/1986] Support for Redis 6 ACLs (#1791)

Add support for Redis 6 ACLs in the `Redis`, `RedisCluster`, and `RedisArray` classes.

On a related note, it adds a mechanism for users to customize how we generate persistent connection IDs such that they can be grouped in different ways depending on the specific use case required (e.g. it would allow connections to be grouped by username, or by user-defined persistent_id, or both).
---
 .travis.yml                |  21 +-
 README.markdown            |  33 +-
 cluster_library.c          |  67 ++--
 cluster_library.h          |   4 +
 common.h                   |   7 +-
 library.c                  | 686 +++++++++++++++++++++++++++++++------
 library.h                  |  63 +++-
 php_redis.h                |  18 +-
 redis.c                    | 138 +++++++-
 redis_array.c              | 133 +++----
 redis_array_impl.c         | 223 +++++-------
 redis_array_impl.h         |  10 +-
 redis_cluster.c            | 204 +++++++----
 redis_cluster.h            |   1 +
 redis_commands.c           |  41 ++-
 redis_commands.h           |   3 +
 redis_session.c            | 238 +++++--------
 tests/RedisArrayTest.php   |   4 +-
 tests/RedisClusterTest.php |  18 +-
 tests/RedisTest.php        | 156 ++++++++-
 tests/TestRedis.php        |  32 +-
 tests/TestSuite.php        |  88 ++++-
 tests/make-cluster.sh      | 105 +++++-
 tests/startSession.php     |   2 +-
 tests/users.acl            |   2 +
 25 files changed, 1641 insertions(+), 656 deletions(-)
 create mode 100644 tests/users.acl

diff --git a/.travis.yml b/.travis.yml
index 52296b1d49..4520a6d8cf 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,22 +40,23 @@ before_install:
   - ./configure $CFGARGS
 install: make install
 before_script:
+  - sudo add-apt-repository ppa:chris-lea/redis-server -y && sudo apt-get update && sudo apt install redis-server
   - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap
-  - redis-server --port 0 --daemonize yes --requirepass phpredis --unixsocket /tmp/redis.sock
-  - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes --requirepass phpredis; done
-  - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --requirepass phpredis --masterauth phpredis; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
+  - redis-server --port 0 --daemonize yes --aclfile tests/users.acl --unixsocket /tmp/redis.sock
+  - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes --aclfile tests/users.acl; done
+  - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
   - for PORT in $(seq 26379 26380); do wget download.redis.io/redis-stable/sentinel.conf -O $PORT.conf; echo sentinel auth-pass mymaster phpredis >> $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
-  - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 -a phpredis
+  - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 --user phpredis -a phpredis
   - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
   - openssl req -x509 -newkey rsa:1024 -nodes -keyout stunnel.key -out stunnel.pem -days 1 -subj '/CN=localhost'
   - echo -e 'key=stunnel.key\ncert=stunnel.pem\npid=/tmp/stunnel.pid\n[redis]\naccept=6378\nconnect=6379' > stunnel.conf
   - stunnel stunnel.conf
 script:
-  - php tests/TestRedis.php --class Redis --auth phpredis
-  - php tests/TestRedis.php --class RedisArray --auth phpredis
-  - php tests/TestRedis.php --class RedisCluster --auth phpredis
+  - php tests/TestRedis.php --class Redis --user phpredis --auth phpredis
+  - php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis
+  - php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis
   - php tests/TestRedis.php --class RedisSentinel --auth phpredis
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis --auth phpredis
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray --auth phpredis
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --auth phpredis
+  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis --user phpredis --auth phpredis
+  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis
+  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis
   - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel --auth phpredis
diff --git a/README.markdown b/README.markdown
index b2eda95284..0cf5b97d5b 100644
--- a/README.markdown
+++ b/README.markdown
@@ -75,7 +75,7 @@ session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeou
 * timeout (float): the connection timeout to a redis host, expressed in seconds. If the host is unreachable in that amount of time, the session storage will be unavailable for the client. The default timeout is very high (86400 seconds).
 * persistent (integer, should be 1 or 0): defines if a persistent connection should be used. **(experimental setting)**
 * prefix (string, defaults to "PHPREDIS_SESSION:"): used as a prefix to the Redis key in which the session is stored. The key is composed of the prefix followed by the session ID.
-* auth (string, empty by default): used to authenticate with the server prior to sending commands.
+* auth (string, or an array with one or two elements): used to authenticate with the server prior to sending commands.
 * database (integer): selects a different database.
 
 Sessions have a lifetime expressed in seconds and stored in the INI variable "session.gc_maxlifetime". You can change it with [`ini_set()`](http://php.net/ini_set).
@@ -270,18 +270,27 @@ $redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another co
 
 ### auth
 -----
-_**Description**_: Authenticate the connection using a password.
+_**Description**_: Authenticate the connection using a password or a username and password.
 *Warning*: The password is sent in plain-text over the network.
 
 ##### *Parameters*
-*STRING*: password
+*MIXED*: password
 
 ##### *Return value*
 *BOOL*: `TRUE` if the connection is authenticated, `FALSE` otherwise.
 
+*Note*: In order to authenticate with a username and password you need Redis >= 6.0.
+
 ##### *Example*
 ~~~php
+/* Authenticate with the password 'foobared' */
 $redis->auth('foobared');
+
+/* Authenticate with the username 'phpredis', and password 'haxx00r' */
+$redis->auth(['phpredis', 'haxx00r']);
+
+/* Authenticate with the password 'foobared' */
+$redis->auth(['foobared']);
 ~~~
 
 ### select
@@ -417,6 +426,7 @@ _**Description**_: Sends a string to Redis, which replies with the same string
 
 ## Server
 
+1. [acl](#acl) - Manage Redis ACLs
 1. [bgRewriteAOF](#bgrewriteaof) - Asynchronously rewrite the append-only file
 1. [bgSave](#bgsave) - Asynchronously save the dataset to disk (in background)
 1. [config](#config) - Get or Set the Redis server configuration parameters
@@ -431,6 +441,23 @@ _**Description**_: Sends a string to Redis, which replies with the same string
 1. [time](#time) - Return the current server time
 1. [slowLog](#slowlog) - Access the Redis slowLog entries
 
+### acl
+-----
+_**Description**_: Execute the Redis ACL command.
+
+##### *Parameters*
+_variable_:  Minumum of one argument for `Redis` and two for `RedisCluster`.
+
+##### *Example*
+~~~php
+$redis->acl('USERS'); /* Get a list of users */
+$redis->acl('LOG');   /* See log of Redis' ACL subsystem */
+~~~
+
+*Note*:  In order to user the `ACL` command you must be communicating with Redis >= 6.0 and be logged into an account that has access to administration commands such as ACL.  Please reference [this tutorial](https://redis.io/topics/acl) for an overview of Redis 6 ACLs and [the redis command reference](https://redis.io/commands) for every ACL subcommand.
+
+*Note*: If you are connecting to Redis server >= 4.0.0 you can remove a key with the `unlink` method in the exact same way you would use `del`.  The Redis [unlink](https://redis.io/commands/unlink) command is non-blocking and will perform the actual deletion asynchronously.
+
 ### bgRewriteAOF
 -----
 _**Description**_: Start the background rewrite of AOF (Append-Only File)
diff --git a/cluster_library.c b/cluster_library.c
index 8d244a431f..98ba9c2c69 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -637,9 +637,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len,
     node->sock = redis_sock_create(host, host_len, port, c->timeout,
         c->read_timeout, c->persistent, NULL, 0);
 
-    if (c->flags->auth) {
-        node->sock->auth = zend_string_copy(c->flags->auth);
-    }
+    redis_sock_set_auth(node->sock, c->flags->user, c->flags->pass);
 
     return node;
 }
@@ -850,8 +848,8 @@ cluster_free(redisCluster *c, int free_ctx)
 
     /* Free any allocated prefix */
     if (c->flags->prefix) zend_string_release(c->flags->prefix);
-    /* Free auth info we've got */
-    if (c->flags->auth) zend_string_release(c->flags->auth);
+
+    redis_sock_free_auth(c->flags);
     efree(c->flags);
 
     /* Call hash table destructors */
@@ -1050,10 +1048,8 @@ cluster_init_seeds(redisCluster *cluster, zend_string **seeds, uint32_t nseeds)
             (unsigned short)atoi(sep+1), cluster->timeout,
             cluster->read_timeout, cluster->persistent, NULL, 0);
 
-        // Set auth information if specified
-        if (cluster->flags->auth) {
-            sock->auth = zend_string_copy(cluster->flags->auth);
-        }
+        /* Credentials */
+        redis_sock_set_auth(sock, cluster->flags->user, cluster->flags->pass);
 
         // Index this seed by host/port
         key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(sock->host),
@@ -2294,11 +2290,40 @@ cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
     add_next_index_zval(&c->multi_resp, &z_ret);
 }
 
+static void
+cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx,
+                        int (*cb)(RedisSock*, zval*, long))
+{
+    zval z_ret;
+
+    array_init(&z_ret);
+    if (cb(c->cmd_sock, &z_ret, c->reply_len) != SUCCESS) {
+        zval_dtor(&z_ret);
+        CLUSTER_RETURN_FALSE(c);
+    }
+
+    if (CLUSTER_IS_ATOMIC(c)) {
+        RETURN_ZVAL(&z_ret, 0, 1);
+    }
+    add_next_index_zval(&c->multi_resp, &z_ret);
+}
+
+PHP_REDIS_API void
+cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
+    cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx, redis_read_acl_getuser_reply);
+}
+
+PHP_REDIS_API void
+cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
+    cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx, redis_read_acl_log_reply);
+}
+
 /* MULTI BULK response loop where we might pull the next one */
 PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
                                      redisCluster *c, int pull, mbulk_cb cb, zval *z_ret)
 {
     ZVAL_NULL(z_ret);
+
     // Pull our next response if directed
     if (pull) {
         if (cluster_check_response(c, &c->reply_type) < 0)
@@ -2712,7 +2737,7 @@ void free_seed_array(zend_string **seeds, uint32_t nseeds) {
     if (seeds == NULL)
         return;
 
-    for (i = 0; i < nseeds; i++) 
+    for (i = 0; i < nseeds; i++)
         zend_string_release(seeds[i]);
 
     efree(seeds);
@@ -2771,11 +2796,11 @@ static zend_string **get_valid_seeds(HashTable *input, uint32_t *nseeds) {
 /* Validate cluster construction arguments and return a sanitized and validated
  * array of seeds */
 zend_string**
-cluster_validate_args(double timeout, double read_timeout, HashTable *seeds, 
-                      uint32_t *nseeds, char **errstr) 
+cluster_validate_args(double timeout, double read_timeout, HashTable *seeds,
+                      uint32_t *nseeds, char **errstr)
 {
     zend_string **retval;
-    
+
     if (timeout < 0L || timeout > INT_MAX) {
         if (errstr) *errstr = "Invalid timeout";
         return NULL;
@@ -2862,21 +2887,9 @@ PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash) {
 
 /* Cache a cluster's slot information in persistent_list if it's enabled */
 PHP_REDIS_API int cluster_cache_store(zend_string *hash, HashTable *nodes) {
-    redisCachedCluster *cc;
-
-    /* Construct our cache */
-    cc = cluster_cache_create(hash, nodes);
-
-    /* Set up our resource */
-#if PHP_VERSION_ID < 70300
-    zend_resource le;
-    le.type = le_cluster_slot_cache;
-    le.ptr = cc;
+    redisCachedCluster *cc = cluster_cache_create(hash, nodes);
 
-    zend_hash_update_mem(&EG(persistent_list), cc->hash, (void*)&le, sizeof(zend_resource));
-#else
-    zend_register_persistent_resource_ex(cc->hash, cc, le_cluster_slot_cache);
-#endif
+    redis_register_persistent_resource(cc->hash, cc, le_cluster_slot_cache);
 
     return SUCCESS;
 }
diff --git a/cluster_library.h b/cluster_library.h
index 062db790f8..de9d171828 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -498,6 +498,10 @@ PHP_REDIS_API void cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS,
 PHP_REDIS_API void cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS,
     redisCluster *c, void *ctx);
 
+/* Custom ACL handlers */
+PHP_REDIS_API void cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx);
+PHP_REDIS_API void cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx);
+
 /* MULTI BULK processing callbacks */
 int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
     long long count, void *ctx);
diff --git a/common.h b/common.h
index 14aefb53a3..dda1436673 100644
--- a/common.h
+++ b/common.h
@@ -210,6 +210,10 @@ typedef enum {
         REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \
     }
 
+/* Case sensitive compare against compile-time static string */
+#define REDIS_STRCMP_STATIC(s, len, sstr) \
+    (len == sizeof(sstr) - 1 && !strncmp(s, sstr, len))
+
 /* Case insensitive compare against compile-time static string */
 #define REDIS_STRICMP_STATIC(s, len, sstr) \
     (len == sizeof(sstr) - 1 && !strncasecmp(s, sstr, len))
@@ -263,7 +267,8 @@ typedef struct {
     php_stream_context *stream_ctx;
     zend_string        *host;
     int                port;
-    zend_string        *auth;
+    zend_string        *user;
+    zend_string        *pass;
     double             timeout;
     double             read_timeout;
     long               retry_interval;
diff --git a/library.c b/library.c
index d9fc53a529..209cd1d0a1 100644
--- a/library.c
+++ b/library.c
@@ -1,3 +1,5 @@
+#include "php_redis.h"
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -55,6 +57,7 @@
 #endif
 
 #include 
+#include 
 
 #define UNSERIALIZE_NONE 0
 #define UNSERIALIZE_KEYS 1
@@ -77,28 +80,44 @@ extern zend_class_entry *redis_exception_ce;
 
 extern int le_redis_pconnect;
 
+static int redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int count);
+
+/* Register a persistent resource in a a way that works for every PHP 7 version. */
+void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id) {
+#if PHP_VERSION_ID < 70300
+    zend_resource res;
+    res.type = le_id;
+    res.ptr = ptr;
+
+    zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(id), ZSTR_LEN(id), &res, sizeof(res));
+#else
+    zend_register_persistent_resource(ZSTR_VAL(id), ZSTR_LEN(id), ptr, le_id);
+#endif
+}
+
 static ConnectionPool *
 redis_sock_get_connection_pool(RedisSock *redis_sock)
 {
-    ConnectionPool *p;
-    zend_string *persistent_id = strpprintf(0, "phpredis_%s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port);
+    ConnectionPool *pool;
     zend_resource *le;
+    zend_string *persistent_id;
 
-    if ((le = zend_hash_find_ptr(&EG(persistent_list), persistent_id)) == NULL) {
-        p = pecalloc(1, sizeof(*p), 1);
-        zend_llist_init(&p->list, sizeof(php_stream *), NULL, 1);
-#if (PHP_VERSION_ID < 70300)
-        zend_resource res;
-        res.type = le_redis_pconnect;
-        res.ptr = p;
-        le = &res;
-        zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), le, sizeof(*le));
-#else
-        le = zend_register_persistent_resource(ZSTR_VAL(persistent_id), ZSTR_LEN(persistent_id), p, le_redis_pconnect);
-#endif
+    /* Generate our unique pool id depending on configuration */
+    persistent_id = redis_pool_spprintf(redis_sock, INI_STR("redis.pconnect.pool_pattern"));
+
+    /* Return early if we can find the pool */
+    if ((le = zend_hash_find_ptr(&EG(persistent_list), persistent_id))) {
+        zend_string_release(persistent_id);
+        return le->ptr;
     }
+
+    /* Create the pool and store it in our persistent list */
+    pool = pecalloc(1, sizeof(*pool), 1);
+    zend_llist_init(&pool->list, sizeof(php_stream *), NULL, 1);
+    redis_register_persistent_resource(persistent_id, pool, le_redis_pconnect);
+
     zend_string_release(persistent_id);
-    return le->ptr;
+    return pool;
 }
 
 /* Helper to reselect the proper DB number when we reconnect */
@@ -129,34 +148,112 @@ static int reselect_db(RedisSock *redis_sock) {
     return 0;
 }
 
-/* Helper to resend AUTH  in the case of a reconnect */
-PHP_REDIS_API int
-redis_sock_auth(RedisSock *redis_sock)
+/* Attempt to read a single +OK response */
+static int redis_sock_read_ok(RedisSock *redis_sock) {
+    char buf[64];
+    size_t len;
+
+    if (redis_sock_read_single_line(redis_sock, buf, sizeof(buf), &len, 0) < 0)
+        return FAILURE;
+
+    return REDIS_STRCMP_STATIC(buf, len, "OK") ? SUCCESS : FAILURE;
+}
+
+/* Append an AUTH command to a smart string if neccessary.  This will either
+ * append the new style AUTH  , old style AUTH , or
+ * append no command at all.  Function returns 1 if we appended a command
+ * and 0 otherwise. */
+static int redis_sock_append_auth(RedisSock *redis_sock, smart_string *str) {
+    /* We need a password at least */
+    if (redis_sock->pass == NULL)
+        return 0;
+
+    REDIS_CMD_INIT_SSTR_STATIC(str, !!redis_sock->user + !!redis_sock->pass, "AUTH");
+
+    if (redis_sock->user)
+        redis_cmd_append_sstr_zstr(str, redis_sock->user);
+
+    redis_cmd_append_sstr_zstr(str, redis_sock->pass);
+
+    /* We appended a command */
+    return 1;
+}
+
+PHP_REDIS_API void
+redis_sock_copy_auth(RedisSock *dst, RedisSock *src) {
+    redis_sock_set_auth(dst, src->user, src->pass);
+}
+
+PHP_REDIS_API void
+redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass)
 {
-    char *cmd, *response;
-    int cmd_len, response_len;
+    /* Release existing user/pass */
+    redis_sock_free_auth(redis_sock);
 
-    cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "S", redis_sock->auth);
+    /* Set new user/pass */
+    redis_sock->user = user ? zend_string_copy(user) : NULL;
+    redis_sock->pass = pass ? zend_string_copy(pass) : NULL;
+}
 
-    if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
-        efree(cmd);
-        return -1;
-    }
 
-    efree(cmd);
+PHP_REDIS_API void
+redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv) {
+    zend_string *user, *pass;
 
-    response = redis_sock_read(redis_sock, &response_len);
-    if (response == NULL) {
-        return -1;
+    if (redis_extract_auth_info(zv, &user, &pass) == FAILURE)
+        return;
+
+    redis_sock_set_auth(redis_sock, user, pass);
+
+    if (user) zend_string_release(user);
+    if (pass) zend_string_release(pass);
+}
+
+PHP_REDIS_API void
+redis_sock_free_auth(RedisSock *redis_sock) {
+    if (redis_sock->user) {
+        zend_string_release(redis_sock->user);
+        redis_sock->user = NULL;
     }
 
-    if (strncmp(response, "+OK", 3)) {
-        efree(response);
-        return -1;
+    if (redis_sock->pass) {
+        zend_string_release(redis_sock->pass);
+        redis_sock->pass = NULL;
     }
+}
 
-    efree(response);
-    return 0;
+PHP_REDIS_API char *
+redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen) {
+    char *cmd;
+
+    /* AUTH requires at least a password */
+    if (redis_sock->pass == NULL)
+        return NULL;
+
+    if (redis_sock->user) {
+        *cmdlen = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "SS", redis_sock->user, redis_sock->pass);
+    } else {
+        *cmdlen = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "S", redis_sock->pass);
+    }
+
+    return cmd;
+}
+
+/* Send Redis AUTH and process response */
+PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) {
+    char *cmd;
+    int cmdlen, rv = FAILURE;
+
+    if ((cmd = redis_sock_auth_cmd(redis_sock, &cmdlen)) == NULL)
+        return SUCCESS;
+
+    if (redis_sock_write(redis_sock, cmd, cmdlen) < 0)
+        goto cleanup;
+
+    rv = redis_sock_read_ok(redis_sock) == SUCCESS ? SUCCESS : FAILURE;
+cleanup:
+    efree(cmd);
+    return rv;
 }
 
 /* Helper function and macro to test a RedisSock error prefix. */
@@ -180,18 +277,24 @@ redis_error_throw(RedisSock *redis_sock)
     if (redis_sock == NULL || redis_sock->err == NULL)
         return;
 
+    /* Redis 6 decided to add 'ERR AUTH' which has a normal 'ERR' prefix
+     * but is actually an authentication error that we will want to throw
+     * an exception for, so just short circuit if this is any other 'ERR'
+     * prefixed error. */
+    if (REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR") &&
+        !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR AUTH")) return;
+
     /* We may want to flip this logic and check for MASTERDOWN, AUTH,
      * and LOADING but that may have side effects (esp for things like
      * Disque) */
-    if (!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR") &&
-        !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOSCRIPT") &&
+    if (!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOSCRIPT") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOQUORUM") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGOODSLAVE") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "WRONGTYPE") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "BUSYGROUP") &&
         !REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGROUP"))
     {
-        REDIS_THROW_EXCEPTION( ZSTR_VAL(redis_sock->err), 0);
+        REDIS_THROW_EXCEPTION(ZSTR_VAL(redis_sock->err), 0);
     }
 }
 
@@ -247,11 +350,11 @@ redis_check_eof(RedisSock *redis_sock, int no_throw)
                 /* check for EOF again. */
                 errno = 0;
                 if (php_stream_eof(redis_sock->stream) == 0) {
-                    /* If we're using a password, attempt a reauthorization */
-                    if (redis_sock->auth && redis_sock_auth(redis_sock) != 0) {
+                    if (redis_sock_auth(redis_sock) != SUCCESS) {
                         errmsg = "AUTH failed while reconnecting";
                         break;
                     }
+
                     redis_sock->status = REDIS_SOCK_STATUS_READY;
                     /* If we're using a non-zero db, reselect it */
                     if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) {
@@ -583,6 +686,19 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len)
     return NULL;
 }
 
+static int redis_sock_read_cmp(RedisSock *redis_sock, const char *cmp, int cmplen) {
+    char *resp;
+    int len, rv = FAILURE;
+
+    if ((resp = redis_sock_read(redis_sock, &len)) == NULL)
+        return FAILURE;
+
+    rv = len == cmplen && !memcmp(resp, cmp, cmplen) ? SUCCESS : FAILURE;
+
+    efree(resp);
+    return rv;
+}
+
 /* A simple union to store the various arg types we might handle in our
  * redis_spprintf command formatting function */
 union resparg {
@@ -594,6 +710,107 @@ union resparg {
     double dval;
 };
 
+static zend_string *redis_hash_auth(zend_string *user, zend_string *pass) {
+    zend_string *algo, *hex;
+    smart_str salted = {0};
+    const php_hash_ops *ops;
+    unsigned char *digest;
+    void *ctx;
+
+    /* No op if there is not username/password */
+    if (user == NULL && pass == NULL)
+        return NULL;
+
+    /* Theoretically inpossible but check anyway */
+    algo = zend_string_init("sha256", sizeof("sha256") - 1, 0);
+    if ((ops = redis_hash_fetch_ops(algo)) == NULL) {
+        zend_string_release(algo);
+        return NULL;
+    }
+
+    /* Hash username + password with our salt global */
+    smart_str_alloc(&salted, 256, 0);
+    if (user) smart_str_append_ex(&salted, user, 0);
+    if (pass) smart_str_append_ex(&salted, pass, 0);
+    smart_str_appendl_ex(&salted, REDIS_G(salt), sizeof(REDIS_G(salt)), 0);
+
+    ctx = emalloc(ops->context_size);
+    ops->hash_init(ctx);
+    ops->hash_update(ctx, (const unsigned char *)ZSTR_VAL(salted.s), ZSTR_LEN(salted.s));
+
+    digest = emalloc(ops->digest_size);
+    ops->hash_final(digest, ctx);
+    efree(ctx);
+
+    hex = zend_string_safe_alloc(ops->digest_size, 2, 0, 0);
+    php_hash_bin2hex(ZSTR_VAL(hex), digest, ops->digest_size);
+    ZSTR_VAL(hex)[2 * ops->digest_size] = 0;
+
+    efree(digest);
+    zend_string_release(algo);
+    smart_str_free(&salted);
+
+    return hex;
+}
+
+static void append_auth_hash(smart_str *dst, zend_string *user, zend_string *pass) {
+    zend_string *s;
+
+    if ((s = redis_hash_auth(user, pass)) != NULL) {
+        smart_str_appendc(dst, ':');
+        smart_str_append_ex(dst, s, 0);
+        zend_string_release(s);
+    }
+}
+
+/* A printf like function to generate our connection pool hash value. */
+PHP_REDIS_API zend_string *
+redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...) {
+    smart_str str = {0};
+
+    smart_str_alloc(&str, 128, 0);
+
+    /* We always include phpredis_: */
+    smart_str_appendl(&str, "phpredis_", sizeof("phpredis_") - 1);
+    smart_str_append_ex(&str, redis_sock->host, 0);
+    smart_str_appendc(&str, ':');
+    smart_str_append_long(&str, (zend_long)redis_sock->port);
+
+    /* Short circuit if we don't have a pattern */
+    if (fmt == NULL)
+        return str.s;
+
+    while (*fmt) {
+        switch (*fmt) {
+            case 'i':
+                if (redis_sock->persistent_id) {
+                    smart_str_appendc(&str, ':');
+                    smart_str_append_ex(&str, redis_sock->persistent_id, 0);
+                }
+                break;
+            case 'u':
+                smart_str_appendc(&str, ':');
+                if (redis_sock->user) {
+                    smart_str_append_ex(&str, redis_sock->user, 0);
+                }
+                break;
+            case 'p':
+                append_auth_hash(&str, NULL, redis_sock->pass);
+                break;
+            case 'a':
+                append_auth_hash(&str, redis_sock->user, redis_sock->pass);
+                break;
+            default:
+                /* Maybe issue a php_error_docref? */
+                break;
+        }
+
+        fmt++;
+    }
+
+    return str.s;
+}
+
 /* A printf like method to construct a Redis RESP command.  It has been extended
  * to take a few different format specifiers that are convenient to phpredis.
  *
@@ -765,6 +982,10 @@ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock
     return retval;
 }
 
+int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr) {
+    return redis_cmd_append_sstr(str, ZSTR_VAL(zstr), ZSTR_LEN(zstr));
+}
+
 /* Append a string key to a redis command.  This function takes care of prefixing the key
  * for the caller and setting the slot argument if it is passed non null */
 int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot) {
@@ -1575,6 +1796,98 @@ redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_t
     return FAILURE;
 }
 
+PHP_REDIS_API int
+redis_read_acl_log_reply(RedisSock *redis_sock, zval *zret, long count) {
+    zval zsub;
+    int i, nsub;
+
+    for (i = 0; i < count; i++) {
+        if (read_mbulk_header(redis_sock, &nsub) < 0 || nsub % 2 != 0)
+            return FAILURE;
+
+        array_init(&zsub);
+        if (redis_mbulk_reply_zipped_raw_variant(redis_sock, &zsub, nsub) == FAILURE)
+            return FAILURE;
+
+        add_next_index_zval(zret, &zsub);
+    }
+
+    return SUCCESS;
+}
+
+PHP_REDIS_API int
+redis_read_acl_getuser_reply(RedisSock *redis_sock, zval *zret, long count) {
+    REDIS_REPLY_TYPE type;
+    zval zv;
+    char *key, *val;
+    long vlen;
+    int klen, i;
+
+    for (i = 0; i < count; i += 2) {
+        if (!(key = redis_sock_read(redis_sock, &klen)) ||
+            redis_read_reply_type(redis_sock, &type, &vlen) < 0 ||
+            (type != TYPE_BULK && type != TYPE_MULTIBULK) ||
+            vlen > INT_MAX)
+        {
+            if (key) efree(key);
+            return FAILURE;
+        }
+
+        if (type == TYPE_BULK) {
+            if (!(val = redis_sock_read_bulk_reply(redis_sock, (int)vlen)))
+                return FAILURE;
+            add_assoc_stringl_ex(zret, key, klen, val, vlen);
+            efree(val);
+        } else {
+            array_init(&zv);
+            redis_mbulk_reply_loop(redis_sock, &zv, (int)vlen, UNSERIALIZE_NONE);
+            add_assoc_zval_ex(zret, key, klen, &zv);
+        }
+
+        efree(key);
+    }
+
+    return SUCCESS;
+}
+
+int redis_acl_custom_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx,
+                           int (*cb)(RedisSock*, zval*, long)) {
+    REDIS_REPLY_TYPE type;
+    int res = FAILURE;
+    zval zret;
+    long len;
+
+    if (redis_read_reply_type(redis_sock, &type, &len) == 0 && type == TYPE_MULTIBULK) {
+        array_init(&zret);
+
+        res = cb(redis_sock, &zret, len);
+        if (res == FAILURE) {
+            zval_dtor(&zret);
+            ZVAL_FALSE(&zret);
+        }
+    } else {
+        ZVAL_FALSE(&zret);
+    }
+
+    if (IS_ATOMIC(redis_sock)) {
+        RETVAL_ZVAL(&zret, 0, 0);
+    } else {
+        add_next_index_zval(z_tab, &zret);
+    }
+
+    return res;
+}
+
+int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+    return redis_acl_custom_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx,
+                                  redis_read_acl_getuser_reply);
+}
+
+int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+    return redis_acl_custom_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx,
+                                  redis_read_acl_log_reply);
+}
+
 /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */
 PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
@@ -1776,15 +2089,11 @@ redis_sock_create(char *host, int host_len, int port,
 {
     RedisSock *redis_sock;
 
-    redis_sock         = ecalloc(1, sizeof(RedisSock));
+    redis_sock = ecalloc(1, sizeof(RedisSock));
     redis_sock->host = zend_string_init(host, host_len, 0);
-    redis_sock->stream = NULL;
     redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED;
-    redis_sock->watching = 0;
-    redis_sock->dbNumber = 0;
     redis_sock->retry_interval = retry_interval * 1000;
     redis_sock->persistent = persistent;
-    redis_sock->persistent_id = NULL;
 
     if (persistent && persistent_id != NULL) {
         redis_sock->persistent_id = zend_string_init(persistent_id, strlen(persistent_id), 0);
@@ -1796,67 +2105,41 @@ redis_sock_create(char *host, int host_len, int port,
 
     redis_sock->serializer = REDIS_SERIALIZER_NONE;
     redis_sock->compression = REDIS_COMPRESSION_NONE;
-    redis_sock->compression_level = 0; /* default */
     redis_sock->mode = ATOMIC;
-    redis_sock->head = NULL;
-    redis_sock->current = NULL;
-
-    redis_sock->pipeline_cmd = NULL;
-
-    redis_sock->err = NULL;
 
-    redis_sock->scan = 0;
+    return redis_sock;
+}
 
-    redis_sock->readonly = 0;
-    redis_sock->tcp_keepalive = 0;
-    redis_sock->reply_literal = 0;
+static int redis_uniqid(char *buf, size_t buflen) {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
 
-    return redis_sock;
+    return snprintf(buf, buflen, "phpredis:%08lx%05lx:%08lx",
+                    (long)tv.tv_sec, (long)tv.tv_usec, (long)php_rand());
 }
 
 static int
 redis_sock_check_liveness(RedisSock *redis_sock)
 {
-    char inbuf[4096], uniqid[64];
-    int uniqid_len;
+    char id[64], ok;
+    int idlen, auth;
     smart_string cmd = {0};
-    struct timeval tv;
-    size_t len;
 
-    if (redis_sock->auth) {
-        redis_cmd_init_sstr(&cmd, 1, "AUTH", sizeof("AUTH") - 1);
-        redis_cmd_append_sstr(&cmd, ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth));
-    }
+    /* AUTH (if we need it) */
+    auth = redis_sock_append_auth(redis_sock, &cmd);
 
-    gettimeofday(&tv, NULL);
-    uniqid_len = snprintf(uniqid, sizeof(uniqid), "phpredis_pool:%08lx%05lx:%08lx", (long)tv.tv_sec, (long)tv.tv_usec, (long)php_rand());
-    redis_cmd_init_sstr(&cmd, 1, "ECHO", sizeof("ECHO") - 1);
-    redis_cmd_append_sstr(&cmd, uniqid, uniqid_len);
-    smart_string_0(&cmd);
+    /* ECHO challenge/response */
+    idlen = redis_uniqid(id, sizeof(id));
+    REDIS_CMD_INIT_SSTR_STATIC(&cmd, 1, "ECHO");
+    redis_cmd_append_sstr(&cmd, id, idlen);
 
-    if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) {
-        smart_string_free(&cmd);
-        return FAILURE;
-    }
-    smart_string_free(&cmd);
+    /* Send command(s) and make sure we can consume reply(ies) */
+    ok = (redis_sock_write(redis_sock, cmd.c, cmd.len) >= 0) &&
+         (!auth || redis_sock_read_ok(redis_sock) == SUCCESS) &&
+         (redis_sock_read_cmp(redis_sock, id, idlen) == SUCCESS);
 
-    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
-        return FAILURE;
-    } else if (redis_sock->auth) {
-        if (strncmp(inbuf, "+OK", 3) != 0) {
-            return FAILURE;
-        } else if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
-            return FAILURE;
-        }
-    }
-    if (*inbuf != TYPE_BULK ||
-        atoi(inbuf + 1) != uniqid_len ||
-        redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
-        strncmp(inbuf, uniqid, uniqid_len) != 0
-    ) {
-        return FAILURE;
-    }
-    return SUCCESS;
+    smart_string_free(&cmd);
+    return ok ? SUCCESS : FAILURE;
 }
 
 /**
@@ -1999,9 +2282,8 @@ redis_sock_server_open(RedisSock *redis_sock)
             }
             // fall through
         case REDIS_SOCK_STATUS_CONNECTED:
-            if (redis_sock->auth && redis_sock_auth(redis_sock) != SUCCESS) {
+            if (redis_sock_auth(redis_sock) != SUCCESS)
                 break;
-            }
             redis_sock->status = REDIS_SOCK_STATUS_READY;
             // fall through
         case REDIS_SOCK_STATUS_READY:
@@ -2069,11 +2351,12 @@ redis_sock_set_stream_context(RedisSock *redis_sock, zval *options)
     zend_string *zkey;
     zval *z_ele;
 
-    if (!redis_sock || Z_TYPE_P(options) != IS_ARRAY) {
+    if (!redis_sock || Z_TYPE_P(options) != IS_ARRAY)
         return FAILURE;
-    } else if (!redis_sock->stream_ctx) {
+
+    if (!redis_sock->stream_ctx)
         redis_sock->stream_ctx = php_stream_context_alloc();
-    }
+
     ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), zkey, z_ele) {
         php_stream_context_set_option(redis_sock->stream_ctx, "ssl", ZSTR_VAL(zkey), z_ele);
     } ZEND_HASH_FOREACH_END();
@@ -2159,7 +2442,7 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
     } else {
         add_next_index_zval(z_tab, &z_multi_result);
     }
-    /*zval_copy_ctor(return_value); */
+
     return 0;
 }
 
@@ -2195,6 +2478,55 @@ redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count,
     }
 }
 
+static int
+redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int count) {
+    REDIS_REPLY_TYPE type;
+    char *key, *val;
+    int keylen, i;
+    zend_long lval;
+    double dval;
+    long vallen;
+
+    for (i = 0; i < count; i+= 2) {
+        /* Keys should always be bulk strings */
+        if ((key = redis_sock_read(redis_sock, &keylen)) == NULL)
+            return FAILURE;
+
+        /* This can vary */
+        if (redis_read_reply_type(redis_sock, &type, &vallen) < 0)
+            return FAILURE;
+
+        if (type == TYPE_BULK) {
+            if (vallen > INT_MAX || (val = redis_sock_read_bulk_reply(redis_sock, (int)vallen)) == NULL) {
+                efree(key);
+                return FAILURE;
+            }
+
+            /* Possibly overkill, but provides really nice types */
+            switch (is_numeric_string(val, vallen, &lval, &dval, 0)) {
+                case IS_LONG:
+                    add_assoc_long_ex(zret, key, keylen, lval);
+                    break;
+                case IS_DOUBLE:
+                    add_assoc_double_ex(zret, key, keylen, dval);
+                    break;
+                default:
+                    add_assoc_stringl_ex(zret, key, keylen, val, vallen);
+            }
+
+            efree(val);
+        } else if (type == TYPE_INT) {
+            add_assoc_long_ex(zret, key, keylen, (zend_long)vallen);
+        } else {
+            add_assoc_null_ex(zret, key, keylen);
+        }
+
+        efree(key);
+    }
+
+    return SUCCESS;
+}
+
 /* Specialized multibulk processing for HMGET where we need to pair requested
  * keys with their returned values */
 PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
@@ -2288,15 +2620,13 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
     if (redis_sock->err) {
         zend_string_release(redis_sock->err);
     }
-    if (redis_sock->auth) {
-        zend_string_release(redis_sock->auth);
-    }
     if (redis_sock->persistent_id) {
         zend_string_release(redis_sock->persistent_id);
     }
     if (redis_sock->host) {
         zend_string_release(redis_sock->host);
     }
+    redis_sock_free_auth(redis_sock);
     efree(redis_sock);
 }
 
@@ -2909,8 +3239,7 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     zval z_ret;
 
     // Attempt to read our header
-    if(redis_read_reply_type(redis_sock,&reply_type,&reply_info) < 0)
-    {
+    if(redis_read_reply_type(redis_sock,&reply_type,&reply_info) < 0) {
         return -1;
     }
 
@@ -2975,4 +3304,157 @@ redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_
     return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, z_tab, ctx);
 }
 
+PHP_REDIS_API
+int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) {
+    zval *zv;
+    HashTable *ht;
+    int num;
+
+    /* The user may wish to send us something like [NULL, 'password'] or
+     * [false, 'password'] so don't convert NULL or FALSE into "". */
+    #define TRY_SET_AUTH_ARG(zv, ppzstr) \
+        do { \
+            if (Z_TYPE_P(zv) != IS_NULL && Z_TYPE_P(zv) != IS_FALSE) { \
+                *(ppzstr) = zval_get_string(zv); \
+            } \
+        } while (0)
+
+    /* Null out user and password */
+    *user = *pass = NULL;
+
+    /* User passed nothing */
+    if (ztest == NULL)
+        return SUCCESS;
+
+    /* Handle a non-array first */
+    if (Z_TYPE_P(ztest) != IS_ARRAY) {
+        *pass = zval_get_string(ztest);
+        return SUCCESS;
+    }
+
+    /* Handle the array case */
+    ht = Z_ARRVAL_P(ztest);
+    num = zend_hash_num_elements(ht);
+
+    /* Something other than one or two entries makes no sense */
+    if (num != 1 && num != 2) {
+        php_error_docref(NULL, E_WARNING, "When passing an array as auth it must have one or two elements!");
+        return FAILURE;
+    }
+
+    if (num == 2) {
+        if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "user")) ||
+            (zv = zend_hash_index_find(ht, 0)))
+        {
+            TRY_SET_AUTH_ARG(zv, user);
+        }
+
+        if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "pass")) ||
+            (zv = zend_hash_index_find(ht, 1)))
+        {
+            TRY_SET_AUTH_ARG(zv, pass);
+        }
+    } else if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "pass")) ||
+               (zv = zend_hash_index_find(ht, 0)))
+    {
+        TRY_SET_AUTH_ARG(zv, pass);
+    }
+
+    /* If we at least have a password, we're good */
+    if (*pass != NULL)
+        return SUCCESS;
+
+    /* Failure, clean everything up so caller doesn't need to care */
+    if (*user) zend_string_release(*user);
+    *user = NULL;
+
+    return FAILURE;
+}
+
+/* Helper methods to extract configuration settings from a hash table */
+
+zval *redis_hash_str_find_type(HashTable *ht, const char *key, int keylen, int type) {
+    zval *zv = zend_hash_str_find(ht, key, keylen);
+    if (zv == NULL || Z_TYPE_P(zv) != type)
+        return NULL;
+
+    return zv;
+}
+
+void redis_conf_double(HashTable *ht, const char *key, int keylen, double *dval) {
+    zval *zv = zend_hash_str_find(ht, key, keylen);
+    if (zv == NULL)
+        return;
+
+    *dval = zval_get_double(zv);
+}
+
+void redis_conf_bool(HashTable *ht, const char *key, int keylen, int *ival) {
+    zend_string *zstr = NULL;
+
+    redis_conf_string(ht, key, keylen, &zstr);
+    if (zstr == NULL)
+        return;
+
+    *ival = zend_string_equals_literal_ci(zstr, "true") ||
+            zend_string_equals_literal_ci(zstr, "yes") ||
+            zend_string_equals_literal_ci(zstr, "1");
+
+    zend_string_release(zstr);
+}
+
+void redis_conf_zend_bool(HashTable *ht, const char *key, int keylen, zend_bool *bval) {
+    zval *zv = zend_hash_str_find(ht, key, keylen);
+    if (zv == NULL)
+        return;
+
+    *bval = zend_is_true(zv);
+}
+
+void redis_conf_long(HashTable *ht, const char *key, int keylen, zend_long *lval) {
+    zval *zv = zend_hash_str_find(ht, key, keylen);
+    if (zv == NULL)
+        return;
+
+    *lval = zval_get_long(zv);
+}
+
+void redis_conf_int(HashTable *ht, const char *key, int keylen, int *ival) {
+    zval *zv = zend_hash_str_find(ht, key, keylen);
+    if (zv == NULL)
+        return;
+
+    *ival = zval_get_long(zv);
+}
+
+void redis_conf_string(HashTable *ht, const char *key, size_t keylen,
+                       zend_string **sval)
+{
+    zval *zv = zend_hash_str_find(ht, key, keylen);
+    if (zv == NULL)
+        return;
+
+    *sval = zval_get_string(zv);
+}
+
+void redis_conf_zval(HashTable *ht, const char *key, size_t keylen, zval *zret,
+                     int copy, int dtor)
+{
+    zval *zv = zend_hash_str_find(ht, key, keylen);
+    if (zv == NULL)
+        return;
+
+    ZVAL_ZVAL(zret, zv, copy, dtor);
+}
+
+void redis_conf_auth(HashTable *ht, const char *key, size_t keylen,
+                     zend_string **user, zend_string **pass)
+{
+    zval *zv = zend_hash_str_find(ht, key, keylen);
+    if (zv == NULL)
+        return;
+
+    redis_extract_auth_info(zv, user, pass);
+}
+
 /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
diff --git a/library.h b/library.h
index a5c660b670..da73b34d5b 100644
--- a/library.h
+++ b/library.h
@@ -20,19 +20,34 @@
 #define CLUSTER_THROW_EXCEPTION(msg, code) \
     zend_throw_exception(redis_cluster_exception_ce, (msg), code)
 
+#define redis_sock_write_sstr(redis_sock, sstr) \
+    redis_sock_write(redis_sock, (sstr)->c, (sstr)->len)
+
+#if PHP_VERSION_ID < 80000
+    #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(ZSTR_VAL((zstr)), ZSTR_LEN((zstr)))
+#else
+    #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(zstr)
+#endif
+
+void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id);
+
+PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass);
+
 int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len);
 int redis_cmd_append_sstr(smart_string *str, char *append, int append_len);
 int redis_cmd_append_sstr_int(smart_string *str, int append);
 int redis_cmd_append_sstr_long(smart_string *str, long append);
 int redis_cmd_append_sstr_i64(smart_string *str, int64_t append);
 int redis_cmd_append_sstr_dbl(smart_string *str, double value);
+int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr);
 int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock);
 int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot);
 int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx);
 
 PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...);
+PHP_REDIS_API zend_string *redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...);
 
-PHP_REDIS_API char * redis_sock_read(RedisSock *redis_sock, int *buf_len);
+PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len);
 PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len);
 PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx);
@@ -52,13 +67,17 @@ PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, int port, d
 PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock);
 PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock);
 PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock);
+PHP_REDIS_API char *redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen);
+PHP_REDIS_API void redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass);
+PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv);
+PHP_REDIS_API void redis_sock_copy_auth(RedisSock *dst, RedisSock *src);
+PHP_REDIS_API void redis_sock_free_auth(RedisSock *redis_sock);
 PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force);
 PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
 PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer,
     size_t buflen, size_t *linelen, int set_err);
 PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes);
 PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx);
-//PHP_REDIS_API void redis_mbulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, int count, int unserialize);
 PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize);
 
 
@@ -113,6 +132,12 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv);
 PHP_REDIS_API int
 redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements);
 
+/* Specialized ACL reply handlers */
+PHP_REDIS_API int redis_read_acl_getuser_reply(RedisSock *redis_sock, zval *zret, long len);
+PHP_REDIS_API int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_read_acl_log_reply(RedisSock *redis_sock, zval *zret, long count);
+PHP_REDIS_API int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+
 /*
 * Variant Read methods, mostly to implement eval
 */
@@ -125,4 +150,38 @@ PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, Red
 PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
 
+/* Helper methods to get configuration values from a HashTable. */
+
+#define REDIS_HASH_STR_FIND_STATIC(ht, sstr) \
+    zend_hash_str_find(ht, sstr, sizeof(sstr) - 1)
+#define REDIS_HASH_STR_FIND_TYPE_STATIC(ht, sstr, type) \
+    redis_hash_str_find_type(ht, sstr, sizeof(sstr) - 1, type)
+
+#define REDIS_CONF_DOUBLE_STATIC(ht, sstr, dval) \
+    redis_conf_double(ht, sstr, sizeof(sstr) - 1, dval)
+#define REDIS_CONF_BOOL_STATIC(ht, sstr, rval) \
+    redis_conf_bool(ht, sstr, sizeof(sstr) - 1, rval)
+#define REDIS_CONF_ZEND_BOOL_STATIC(ht, sstr, bval) \
+    redis_conf_zend_bool(ht, sstr, sizeof(sstr) - 1, bval)
+#define REDIS_CONF_LONG_STATIC(ht, sstr, lval) \
+    redis_conf_long(ht, sstr, sizeof(sstr) - 1, lval)
+#define REDIS_CONF_INT_STATIC(ht, sstr, ival) \
+    redis_conf_int(ht, sstr, sizeof(sstr) - 1, ival)
+#define REDIS_CONF_STRING_STATIC(ht, sstr, sval) \
+    redis_conf_string(ht, sstr, sizeof(sstr) - 1, sval)
+#define REDIS_CONF_ZVAL_STATIC(ht, sstr, zret, copy, dtor) \
+    redis_conf_zval(ht, sstr, sizeof(sstr) - 1, zret, copy, dtor)
+#define REDIS_CONF_AUTH_STATIC(ht, sstr, user, pass) \
+    redis_conf_auth(ht, sstr, sizeof(sstr) - 1, user, pass)
+
+zval *redis_hash_str_find_type(HashTable *ht, const char *key, int keylen, int type);
+void redis_conf_double(HashTable *ht, const char *key, int keylen, double *dval);
+void redis_conf_bool(HashTable *ht, const char *key, int keylen, int *bval);
+void redis_conf_zend_bool(HashTable *ht, const char *key, int keylen, zend_bool *bval);
+void redis_conf_long(HashTable *ht, const char *key, int keylen, zend_long *lval);
+void redis_conf_int(HashTable *ht, const char *key, int keylen, int *ival);
+void redis_conf_string(HashTable *ht, const char *key, size_t keylen, zend_string **sval);
+void redis_conf_zval(HashTable *ht, const char *key, size_t keylen, zval *zret, int copy, int dtor);
+void redis_conf_auth(HashTable *ht, const char *key, size_t keylen, zend_string **user, zend_string **pass);
+
 #endif
diff --git a/php_redis.h b/php_redis.h
index af77ea00fe..a4ff9f4b06 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -27,6 +27,7 @@
 
 PHP_METHOD(Redis, __construct);
 PHP_METHOD(Redis, __destruct);
+PHP_METHOD(Redis, acl);
 PHP_METHOD(Redis, append);
 PHP_METHOD(Redis, auth);
 PHP_METHOD(Redis, bgSave);
@@ -252,6 +253,18 @@ PHP_METHOD(Redis, getPersistentID);
 PHP_METHOD(Redis, getAuth);
 PHP_METHOD(Redis, getMode);
 
+/* For convenience we store the salt as a printable hex string which requires 2
+ * characters per byte + 1 for the NULL terminator */
+#define REDIS_SALT_BYTES 32
+#define REDIS_SALT_SIZE ((2 * REDIS_SALT_BYTES) + 1)
+
+ZEND_BEGIN_MODULE_GLOBALS(redis)
+	char salt[REDIS_SALT_SIZE];
+ZEND_END_MODULE_GLOBALS(redis)
+
+ZEND_EXTERN_MODULE_GLOBALS(redis)
+#define REDIS_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(redis, v)
+
 #ifdef ZTS
 #include "TSRM.h"
 #endif
@@ -261,8 +274,9 @@ PHP_MSHUTDOWN_FUNCTION(redis);
 PHP_MINFO_FUNCTION(redis);
 
 /* Redis response handler function callback prototype */
-typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS,
-    RedisSock *redis_sock, zval *z_tab, void *ctx);
+typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+
+typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*, zval*, void*);
 
 PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent);
 
diff --git a/redis.c b/redis.c
index 71e301adb4..b361aab2a8 100644
--- a/redis.c
+++ b/redis.c
@@ -27,9 +27,11 @@
 #include "redis_cluster.h"
 #include "redis_commands.h"
 #include "redis_sentinel.h"
+#include 
 #include 
 #include 
 
+
 #ifdef PHP_SESSION
 #include 
 #endif
@@ -94,6 +96,7 @@ PHP_INI_BEGIN()
     /* redis pconnect */
     PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL)
+    PHP_INI_ENTRY("redis.pconnect.pool_pattern", "", PHP_INI_ALL, NULL)
 
     /* redis session */
     PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL)
@@ -183,7 +186,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_lrem, 0, 0, 3)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_auth, 0, 0, 1)
-    ZEND_ARG_INFO(0, password)
+    ZEND_ARG_INFO(0, auth)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1)
@@ -200,6 +203,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_slaveof, 0, 0, 0)
     ZEND_ARG_INFO(0, port)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_acl, 0, 0, 1)
+    ZEND_ARG_INFO(0, subcmd)
+    ZEND_ARG_VARIADIC_INFO(0, args)
+ZEND_END_ARG_INFO()
+
 /* }}} */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_migrate, 0, 0, 5)
@@ -247,6 +255,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, acl, arginfo_acl, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, append, arginfo_key_value, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, auth, arginfo_auth, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, bgSave, arginfo_void, ZEND_ACC_PUBLIC)
@@ -497,6 +506,9 @@ static const zend_module_dep redis_deps[] = {
      ZEND_MOD_END
 };
 
+ZEND_DECLARE_MODULE_GLOBALS(redis)
+static PHP_GINIT_FUNCTION(redis);
+
 zend_module_entry redis_module_entry = {
      STANDARD_MODULE_HEADER_EX,
      NULL,
@@ -509,7 +521,11 @@ zend_module_entry redis_module_entry = {
      NULL,
      PHP_MINFO(redis),
      PHP_REDIS_VERSION,
-     STANDARD_MODULE_PROPERTIES
+     PHP_MODULE_GLOBALS(redis),
+     PHP_GINIT(redis),
+     NULL,
+     NULL,
+     STANDARD_MODULE_PROPERTIES_EX
 };
 
 #ifdef COMPILE_DL_REDIS
@@ -756,6 +772,39 @@ static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor)
     }
 }
 
+static void redis_random_hex_bytes(char *dst, size_t dstsize) {
+    char chunk[9], *ptr = dst;
+    ssize_t rem = dstsize, len, clen;
+    size_t bytes;
+
+    /* We need two characters per hex byte */
+    bytes = dstsize / 2;
+    zend_string *s = zend_string_alloc(bytes, 0);
+
+    /* First try to have PHP generate the bytes */
+    if (php_random_bytes_silent(ZSTR_VAL(s), bytes) == SUCCESS) {
+        php_hash_bin2hex(dst, (unsigned char *)ZSTR_VAL(s), bytes);
+        zend_string_release(s);
+        return;
+    }
+
+    /* PHP shouldn't have failed, but generate manually if it did. */
+    while (rem > 0) {
+        clen = snprintf(chunk, sizeof(chunk), "%08x", rand());
+        len = rem >= clen ? clen : rem;
+        memcpy(ptr, chunk, len);
+        ptr += len; rem -= len;
+    }
+
+    zend_string_release(s);
+}
+
+static PHP_GINIT_FUNCTION(redis)
+{
+    redis_random_hex_bytes(redis_globals->salt, sizeof(redis_globals->salt) - 1);
+    redis_globals->salt[sizeof(redis_globals->salt)-1] = '\0';
+}
+
 /**
  * PHP_MINIT_FUNCTION
  */
@@ -973,7 +1022,7 @@ PHP_METHOD(Redis, pconnect)
 PHP_REDIS_API int
 redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
 {
-    zval *object, *ssl = NULL;
+    zval *object, *context = NULL, *ele;
     char *host = NULL, *persistent_id = NULL;
     zend_long port = -1, retry_interval = 0;
     size_t host_len, persistent_id_len;
@@ -990,7 +1039,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
                                      "Os|lds!lda", &object, redis_ce, &host,
                                      &host_len, &port, &timeout, &persistent_id,
                                      &persistent_id_len, &retry_interval,
-                                     &read_timeout, &ssl) == FAILURE)
+                                     &read_timeout, &context) == FAILURE)
     {
         return FAILURE;
     }
@@ -1021,6 +1070,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
     }
 
     redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, object);
+
     /* if there is a redis sock already we have to remove it */
     if (redis->sock) {
         redis_sock_disconnect(redis->sock, 0);
@@ -1030,8 +1080,16 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
     redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent,
         persistent_id, retry_interval);
 
-    if (ssl != NULL) {
-        redis_sock_set_stream_context(redis->sock, ssl);
+    if (context) {
+        /* Stream context (e.g. TLS) */
+        if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "stream"))) {
+            redis_sock_set_stream_context(redis->sock, ele);
+        }
+
+        /* AUTH */
+        if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "auth"))) {
+            redis_sock_set_auth_zval(redis->sock, ele);
+        }
     }
 
     if (redis_sock_server_open(redis->sock) < 0) {
@@ -1335,6 +1393,53 @@ PHP_METHOD(Redis, type)
 }
 /* }}} */
 
+/* {{{ proto mixed Redis::acl(string $op, ...) }}} */
+PHP_METHOD(Redis, acl) {
+    RedisSock *redis_sock;
+    FailableResultCallback cb;
+    zval *zargs;
+    zend_string *op;
+    char *cmd;
+    int cmdlen, argc = ZEND_NUM_ARGS();
+
+    if (argc < 1 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
+        if (argc < 1) {
+            php_error_docref(NULL, E_WARNING, "ACL command requires at least one argument");
+        }
+        RETURN_FALSE;
+    }
+
+    zargs = emalloc(argc * sizeof(*zargs));
+    if (zend_get_parameters_array(ht, argc, zargs) == FAILURE) {
+        efree(zargs);
+        RETURN_FALSE;
+    }
+
+    /* Read the subcommand and set response callback */
+    op = zval_get_string(&zargs[0]);
+    if (zend_string_equals_literal_ci(op, "GETUSER")) {
+        cb = redis_acl_getuser_reply;
+    } else if (zend_string_equals_literal_ci(op, "LOG")) {
+        cb = redis_acl_log_reply;
+    } else {
+        cb = redis_read_variant_reply;
+    }
+
+    /* Make our command and free args */
+    cmd = redis_variadic_str_cmd("ACL", zargs, argc, &cmdlen);
+
+    zend_string_release(op);
+    efree(zargs);
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmdlen);
+    if (IS_ATOMIC(redis_sock)) {
+        if (cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
+            RETURN_FALSE;
+        }
+    }
+    REDIS_PROCESS_RESPONSE(cb);
+}
+
 /* {{{ proto long Redis::append(string key, string val) */
 PHP_METHOD(Redis, append)
 {
@@ -2542,8 +2647,7 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
 
     for (fi = redis_sock->head; fi; /* void */) {
         if (fi->fun) {
-            fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab,
-                fi->ctx);
+            fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi->ctx);
             fi = fi->next;
             continue;
         }
@@ -3309,13 +3413,25 @@ PHP_METHOD(Redis, getPersistentID) {
 /* {{{ proto Redis::getAuth */
 PHP_METHOD(Redis, getAuth) {
     RedisSock *redis_sock;
+    zval zret;
 
-    if ((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) {
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE)
+        RETURN_FALSE;
+
+    redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+    if (redis_sock == NULL)
         RETURN_FALSE;
-    } else if (redis_sock->auth == NULL) {
+
+    if (redis_sock->user && redis_sock->pass) {
+        array_init(&zret);
+        add_next_index_str(&zret, zend_string_copy(redis_sock->user));
+        add_next_index_str(&zret, zend_string_copy(redis_sock->pass));
+        RETURN_ZVAL(&zret, 0, 0);
+    } else if (redis_sock->pass) {
+        RETURN_STR_COPY(redis_sock->pass);
+    } else {
         RETURN_NULL();
     }
-    RETURN_STRINGL(ZSTR_VAL(redis_sock->auth), ZSTR_LEN(redis_sock->auth));
 }
 
 /*
diff --git a/redis_array.c b/redis_array.c
index bdd7120efa..998d6a2324 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -231,122 +231,67 @@ PHP_METHOD(RedisArray, __construct)
     long l_retry_interval = 0;
       zend_bool b_lazy_connect = 0;
     double d_connect_timeout = 0, read_timeout = 0.0;
-    zend_string *algorithm = NULL, *auth = NULL;
+    zend_string *algorithm = NULL, *user = NULL, *pass = NULL;
     redis_array_object *obj;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|a", &z0, &z_opts) == FAILURE) {
         RETURN_FALSE;
     }
 
+    /* Bail if z0 isn't a string or an array.
+     * Note:  WRONG_PARAM_COUNT seems wrong but this is what we have been doing
+     *        for ages so we can't really change it until the next major version.
+     */
+    if (Z_TYPE_P(z0) != IS_ARRAY && Z_TYPE_P(z0) != IS_STRING)
+        WRONG_PARAM_COUNT;
+
+    /* If it's a string we want to load the array from ini information */
+    if (Z_TYPE_P(z0) == IS_STRING) {
+        ra = ra_load_array(Z_STRVAL_P(z0));
+        goto finish;
+    }
+
     ZVAL_NULL(&z_fun);
     ZVAL_NULL(&z_dist);
+
     /* extract options */
     if(z_opts) {
         hOpts = Z_ARRVAL_P(z_opts);
 
         /* extract previous ring. */
-        if ((zpData = zend_hash_str_find(hOpts, "previous", sizeof("previous") - 1)) != NULL && Z_TYPE_P(zpData) == IS_ARRAY
-            && zend_hash_num_elements(Z_ARRVAL_P(zpData)) != 0
-        ) {
-            /* consider previous array as non-existent if empty. */
+        zpData = REDIS_HASH_STR_FIND_STATIC(hOpts, "previous");
+        if (zpData && Z_TYPE_P(zpData) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(zpData)) > 0) {
             hPrev = Z_ARRVAL_P(zpData);
         }
 
-        /* extract function name. */
-        if ((zpData = zend_hash_str_find(hOpts, "function", sizeof("function") - 1)) != NULL) {
-            ZVAL_ZVAL(&z_fun, zpData, 1, 0);
-        }
-
-        /* extract function name. */
-        if ((zpData = zend_hash_str_find(hOpts, "distributor", sizeof("distributor") - 1)) != NULL) {
-            ZVAL_ZVAL(&z_dist, zpData, 1, 0);
-        }
-
-        /* extract function name. */
-        if ((zpData = zend_hash_str_find(hOpts, "algorithm", sizeof("algorithm") - 1)) != NULL && Z_TYPE_P(zpData) == IS_STRING) {
-            algorithm = zval_get_string(zpData);
-        }
 
-        /* extract index option. */
-        if ((zpData = zend_hash_str_find(hOpts, "index", sizeof("index") - 1)) != NULL) {
-            b_index = zval_is_true(zpData);
-        }
-
-        /* extract autorehash option. */
-        if ((zpData = zend_hash_str_find(hOpts, "autorehash", sizeof("autorehash") - 1)) != NULL) {
-            b_autorehash = zval_is_true(zpData);
-        }
-
-        /* pconnect */
-        if ((zpData = zend_hash_str_find(hOpts, "pconnect", sizeof("pconnect") - 1)) != NULL) {
-            b_pconnect = zval_is_true(zpData);
-        }
-
-        /* extract retry_interval option. */
-        if ((zpData = zend_hash_str_find(hOpts, "retry_interval", sizeof("retry_interval") - 1)) != NULL) {
-            if (Z_TYPE_P(zpData) == IS_LONG) {
-                l_retry_interval = Z_LVAL_P(zpData);
-            } else if (Z_TYPE_P(zpData) == IS_STRING) {
-                l_retry_interval = atol(Z_STRVAL_P(zpData));
-            }
-        }
-
-        /* extract lazy connect option. */
-        if ((zpData = zend_hash_str_find(hOpts, "lazy_connect", sizeof("lazy_connect") - 1)) != NULL) {
-            b_lazy_connect = zval_is_true(zpData);
-        }
-
-        /* extract connect_timeout option */
-        if ((zpData = zend_hash_str_find(hOpts, "connect_timeout", sizeof("connect_timeout") - 1)) != NULL) {
-            if (Z_TYPE_P(zpData) == IS_DOUBLE) {
-                d_connect_timeout = Z_DVAL_P(zpData);
-            } else if (Z_TYPE_P(zpData) == IS_LONG) {
-                d_connect_timeout = Z_LVAL_P(zpData);
-            } else if (Z_TYPE_P(zpData) == IS_STRING) {
-                d_connect_timeout = atof(Z_STRVAL_P(zpData));
-            }
-        }
-
-        /* extract read_timeout option */
-        if ((zpData = zend_hash_str_find(hOpts, "read_timeout", sizeof("read_timeout") - 1)) != NULL) {
-            if (Z_TYPE_P(zpData) == IS_DOUBLE) {
-                read_timeout = Z_DVAL_P(zpData);
-            } else if (Z_TYPE_P(zpData) == IS_LONG) {
-                read_timeout = Z_LVAL_P(zpData);
-            } else if (Z_TYPE_P(zpData) == IS_STRING) {
-                read_timeout = atof(Z_STRVAL_P(zpData));
-            }
-        }
-
-        /* consistent */
-        if ((zpData = zend_hash_str_find(hOpts, "consistent", sizeof("consistent") - 1)) != NULL) {
-            consistent = zval_is_true(zpData);
-        }
-
-        /* auth */
-        if ((zpData = zend_hash_str_find(hOpts, "auth", sizeof("auth") - 1)) != NULL) {
-            auth = zval_get_string(zpData);
-        }
+        REDIS_CONF_AUTH_STATIC(hOpts, "auth", &user, &pass);
+        REDIS_CONF_ZVAL_STATIC(hOpts, "function", &z_fun, 1, 0);
+        REDIS_CONF_ZVAL_STATIC(hOpts, "distributor", &z_dist, 1, 0);
+        REDIS_CONF_STRING_STATIC(hOpts, "algorithm", &algorithm);
+        REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "index", &b_index);
+        REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "autorehash", &b_autorehash);
+        REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "pconnect", &b_pconnect);
+        REDIS_CONF_LONG_STATIC(hOpts, "retry_interval", &l_retry_interval);
+        REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "lazy_connect", &b_lazy_connect);
+        REDIS_CONF_ZEND_BOOL_STATIC(hOpts, "consistent", &consistent);
+        REDIS_CONF_DOUBLE_STATIC(hOpts, "connect_timeout", &d_connect_timeout);
+        REDIS_CONF_DOUBLE_STATIC(hOpts, "read_timeout", &read_timeout);
     }
 
-    /* extract either name of list of hosts from z0 */
-    switch(Z_TYPE_P(z0)) {
-        case IS_STRING:
-            ra = ra_load_array(Z_STRVAL_P(z0));
-            break;
+    ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index,
+                       b_pconnect, l_retry_interval, b_lazy_connect,
+                       d_connect_timeout, read_timeout, consistent,
+                       algorithm, user, pass);
 
-        case IS_ARRAY:
-            ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth);
-            break;
-
-        default:
-            WRONG_PARAM_COUNT;
-    }
     if (algorithm) zend_string_release(algorithm);
-    if (auth) zend_string_release(auth);
+    if (user) zend_string_release(user);
+    if (pass) zend_string_release(pass);
     zval_dtor(&z_dist);
     zval_dtor(&z_fun);
 
+finish:
+
     if(ra) {
         ra->auto_rehash = b_autorehash;
         ra->connect_timeout = d_connect_timeout;
@@ -357,7 +302,9 @@ PHP_METHOD(RedisArray, __construct)
 }
 
 static void
-ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, int cmd_len, zval *z_args, zval *z_new_target) {
+ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd,
+                int cmd_len, zval *z_args, zval *z_new_target)
+{
 
     zval z_fun, *redis_inst, *z_callargs, *zp_tmp;
     char *key = NULL; /* set to avoid "unused-but-set-variable" */
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 82715d0766..a427f67361 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -31,7 +31,8 @@
 extern zend_class_entry *redis_ce;
 
 static RedisArray *
-ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_interval, zend_bool b_lazy_connect)
+ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *user,
+              zend_string *pass, long retry_interval, zend_bool b_lazy_connect)
 {
     int i = 0, host_len;
     char *host, *p;
@@ -63,14 +64,13 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *auth, long retry_in
         redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, &ra->redis[i]);
 
         /* create socket */
-        redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval);
+        redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout,
+                                        ra->read_timeout, ra->pconnect, NULL,
+                                        retry_interval);
 
-        /* copy password is specified */
-        if (auth) redis->sock->auth = zend_string_copy(auth);
+        redis_sock_set_auth(redis->sock, user, pass);
 
-        if (!b_lazy_connect)
-        {
-            /* connect */
+        if (!b_lazy_connect) {
             if (redis_sock_server_open(redis->sock) < 0) {
                 ra->count = ++i;
                 return NULL;
@@ -151,25 +151,12 @@ ra_find_name(const char *name) {
 
 /* load array from INI settings */
 RedisArray *ra_load_array(const char *name) {
-
-    zval *z_data, z_fun, z_dist;
+    zval *z_data, z_tmp, z_fun, z_dist;
     zval z_params_hosts;
     zval z_params_prev;
-    zval z_params_funs;
-    zval z_params_dist;
-    zval z_params_algo;
-    zval z_params_index;
-    zval z_params_autorehash;
-    zval z_params_retry_interval;
-    zval z_params_pconnect;
-    zval z_params_connect_timeout;
-    zval z_params_read_timeout;
-    zval z_params_lazy_connect;
-    zval z_params_consistent;
-    zval z_params_auth;
     RedisArray *ra = NULL;
 
-    zend_string *algorithm = NULL, *auth = NULL;
+    zend_string *algorithm = NULL, *user = NULL, *pass = NULL;
     zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0;
     long l_retry_interval = 0;
     zend_bool b_lazy_connect = 0;
@@ -182,185 +169,142 @@ RedisArray *ra_load_array(const char *name) {
     if(!ra_find_name(name))
         return ra;
 
+    ZVAL_NULL(&z_fun);
+    ZVAL_NULL(&z_dist);
+
     /* find hosts */
     array_init(&z_params_hosts);
     if ((iptr = INI_STR("redis.arrays.hosts")) != NULL) {
         sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_hosts);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_hosts), name, name_len)) != NULL) {
-        hHosts = Z_ARRVAL_P(z_data);
+        if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_hosts), name, name_len)) != NULL) {
+            hHosts = Z_ARRVAL_P(z_data);
+        }
     }
 
     /* find previous hosts */
     array_init(&z_params_prev);
     if ((iptr = INI_STR("redis.arrays.previous")) != NULL) {
         sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_prev);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_prev), name, name_len)) != NULL) {
-        hPrev = Z_ARRVAL_P(z_data);
+        if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_prev), name, name_len)) != NULL) {
+            if (Z_TYPE_P(z_data) == IS_ARRAY) {
+                hPrev = Z_ARRVAL_P(z_data);
+            }
+        }
     }
 
     /* find function */
-    array_init(&z_params_funs);
     if ((iptr = INI_STR("redis.arrays.functions")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_funs);
-    }
-    ZVAL_NULL(&z_fun);
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_funs), name, name_len)) != NULL) {
-        ZVAL_ZVAL(&z_fun, z_data, 1, 0);
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_zval(Z_ARRVAL(z_tmp), name, name_len, &z_fun, 1, 0);
+        zval_dtor(&z_tmp);
     }
 
     /* find distributor */
-    array_init(&z_params_dist);
     if ((iptr = INI_STR("redis.arrays.distributor")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_dist);
-    }
-    ZVAL_NULL(&z_dist);
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_dist), name, name_len)) != NULL) {
-        ZVAL_ZVAL(&z_dist, z_data, 1, 0);
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_zval(Z_ARRVAL(z_tmp), name, name_len, &z_dist, 1, 0);
+        zval_dtor(&z_tmp);
     }
 
     /* find hash algorithm */
-    array_init(&z_params_algo);
     if ((iptr = INI_STR("redis.arrays.algorithm")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_algo);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_algo), name, name_len)) != NULL) {
-        algorithm = zval_get_string(z_data);
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_string(Z_ARRVAL(z_tmp), name, name_len, &algorithm);
+        zval_dtor(&z_tmp);
     }
 
     /* find index option */
-    array_init(&z_params_index);
     if ((iptr = INI_STR("redis.arrays.index")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_index);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_index), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) {
-            b_index = 1;
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_index);
+        zval_dtor(&z_tmp);
     }
 
     /* find autorehash option */
-    array_init(&z_params_autorehash);
     if ((iptr = INI_STR("redis.arrays.autorehash")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_autorehash);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_autorehash), name, name_len)) != NULL) {
-        if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) {
-            b_autorehash = 1;
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_autorehash);
+        zval_dtor(&z_tmp);
     }
 
     /* find retry interval option */
-    array_init(&z_params_retry_interval);
     if ((iptr = INI_STR("redis.arrays.retryinterval")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_retry_interval);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_retry_interval), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_data) == IS_LONG) {
-            l_retry_interval = Z_LVAL_P(z_data);
-        } else if (Z_TYPE_P(z_data) == IS_STRING) {
-            l_retry_interval = atol(Z_STRVAL_P(z_data));
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_long(Z_ARRVAL(z_tmp), name, name_len, &l_retry_interval);
+        zval_dtor(&z_tmp);
     }
 
     /* find pconnect option */
-    array_init(&z_params_pconnect);
     if ((iptr = INI_STR("redis.arrays.pconnect")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_pconnect);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_pconnect), name, name_len)) != NULL) {
-        if(Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) {
-            b_pconnect = 1;
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_pconnect);
+        zval_dtor(&z_tmp);
     }
 
     /* find lazy connect option */
-    array_init(&z_params_lazy_connect);
     if ((iptr = INI_STR("redis.arrays.lazyconnect")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_lazy_connect);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_lazy_connect), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) {
-            b_lazy_connect = 1;
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_lazy_connect);
+        zval_dtor(&z_tmp);
     }
 
     /* find connect timeout option */
-    array_init(&z_params_connect_timeout);
     if ((iptr = INI_STR("redis.arrays.connecttimeout")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_connect_timeout);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_connect_timeout), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_data) == IS_DOUBLE) {
-            d_connect_timeout = Z_DVAL_P(z_data);
-        } else if (Z_TYPE_P(z_data) == IS_STRING)  {
-            d_connect_timeout = atof(Z_STRVAL_P(z_data));
-        } else if (Z_TYPE_P(z_data) == IS_LONG) {
-            d_connect_timeout = Z_LVAL_P(z_data);
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &d_connect_timeout);
+        zval_dtor(&z_tmp);
     }
 
     /* find read timeout option */
-    array_init(&z_params_read_timeout);
     if ((iptr = INI_STR("redis.arrays.readtimeout")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_read_timeout);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_read_timeout), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_data) == IS_DOUBLE) {
-            read_timeout = Z_DVAL_P(z_data);
-        } else if (Z_TYPE_P(z_data) == IS_STRING)  {
-            read_timeout = atof(Z_STRVAL_P(z_data));
-        } else if (Z_TYPE_P(z_data) == IS_LONG) {
-            read_timeout = Z_LVAL_P(z_data);
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &read_timeout);
+        zval_dtor(&z_tmp);
     }
 
     /* find consistent option */
-    array_init(&z_params_consistent);
     if ((iptr = INI_STR("redis.arrays.consistent")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_consistent);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_consistent), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0) {
-            consistent = 1;
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        if ((z_data = zend_hash_str_find(Z_ARRVAL(z_tmp), name, name_len)) != NULL) {
+            consistent = Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0;
         }
+        zval_dtor(&z_tmp);
     }
 
     /* find auth option */
-    array_init(&z_params_auth);
     if ((iptr = INI_STR("redis.arrays.auth")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_auth);
-    }
-    if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_auth), name, name_len)) != NULL) {
-        auth = zval_get_string(z_data);
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_auth(Z_ARRVAL(z_tmp), name, name_len, &user, &pass);
+        zval_dtor(&z_tmp);
     }
 
     /* create RedisArray object */
-    ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm, auth);
+    ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval,
+                       b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm,
+                       user, pass);
     if (ra) {
         ra->auto_rehash = b_autorehash;
         if(ra->prev) ra->prev->auto_rehash = b_autorehash;
     }
 
-    /* cleanup */
     if (algorithm) zend_string_release(algorithm);
-    if (auth) zend_string_release(auth);
+    if (user) zend_string_release(user);
+    if (pass) zend_string_release(pass);
 
     zval_dtor(&z_params_hosts);
     zval_dtor(&z_params_prev);
-    zval_dtor(&z_params_funs);
-    zval_dtor(&z_params_dist);
-    zval_dtor(&z_params_algo);
-    zval_dtor(&z_params_index);
-    zval_dtor(&z_params_autorehash);
-    zval_dtor(&z_params_retry_interval);
-    zval_dtor(&z_params_pconnect);
-    zval_dtor(&z_params_connect_timeout);
-    zval_dtor(&z_params_read_timeout);
-    zval_dtor(&z_params_lazy_connect);
-    zval_dtor(&z_params_consistent);
-    zval_dtor(&z_params_auth);
     zval_dtor(&z_dist);
     zval_dtor(&z_fun);
 
@@ -408,7 +352,11 @@ ra_make_continuum(zend_string **hosts, int nb_hosts)
 }
 
 RedisArray *
-ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth)
+ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev,
+              zend_bool b_index, zend_bool b_pconnect, long retry_interval,
+              zend_bool b_lazy_connect, double connect_timeout, double read_timeout,
+              zend_bool consistent, zend_string *algorithm, zend_string *user,
+              zend_string *pass)
 {
     int i, count;
     RedisArray *ra;
@@ -429,7 +377,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
     ra->continuum = NULL;
     ra->algorithm = NULL;
 
-    if (ra_load_hosts(ra, hosts, auth, retry_interval, b_lazy_connect) == NULL || !ra->count) {
+    if (ra_load_hosts(ra, hosts, user, pass, retry_interval, b_lazy_connect) == NULL || !ra->count) {
         for (i = 0; i < ra->count; ++i) {
             zval_dtor(&ra->redis[i]);
             zend_string_release(ra->hosts[i]);
@@ -439,7 +387,8 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
         efree(ra);
         return NULL;
     }
-    ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm, auth) : NULL;
+
+    ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout, consistent, algorithm, user, pass) : NULL;
 
     /* init array data structures */
     ra_init_function_table(ra);
@@ -541,13 +490,7 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos)
         const php_hash_ops *ops;
 
         /* hash */
-        if (ra->algorithm && (
-#if (PHP_VERSION_ID < 80000)
-            ops = php_hash_fetch_ops(ZSTR_VAL(ra->algorithm), ZSTR_LEN(ra->algorithm))
-#else
-            ops = php_hash_fetch_ops(ra->algorithm)
-#endif
-        ) != NULL) {
+        if (ra->algorithm && (ops = redis_hash_fetch_ops(ra->algorithm))) {
             void *ctx = emalloc(ops->context_size);
             unsigned char *digest = emalloc(ops->digest_size);
 
diff --git a/redis_array_impl.h b/redis_array_impl.h
index b5d2e1ce72..0ef5a73f10 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -10,7 +10,15 @@
 #include "redis_array.h"
 
 RedisArray *ra_load_array(const char *name);
-RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout, zend_bool consistent, zend_string *algorithm, zend_string *auth);
+
+RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist,
+                          HashTable *hosts_prev, zend_bool b_index,
+                          zend_bool b_pconnect, long retry_interval,
+                          zend_bool b_lazy_connect, double connect_timeout,
+                          double read_timeout, zend_bool consistent,
+                          zend_string *algorithm, zend_string *auth,
+                          zend_string *pass);
+
 zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len);
 zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos);
 void ra_init_function_table(RedisArray *ra);
diff --git a/redis_cluster.c b/redis_cluster.c
index a2ced22a86..3233b65389 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -96,6 +96,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_scan_cl, 0, 0, 2)
     ZEND_ARG_INFO(0, i_count)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_acl_cl, 0, 0, 2)
+    ZEND_ARG_INFO(0, key_or_address)
+    ZEND_ARG_INFO(0, subcmd)
+    ZEND_ARG_VARIADIC_INFO(0, args)
+ZEND_END_ARG_INFO()
+
 /* Function table */
 zend_function_entry redis_cluster_functions[] = {
     PHP_ME(RedisCluster, __construct, arginfo_ctor, ZEND_ACC_PUBLIC)
@@ -104,6 +110,7 @@ zend_function_entry redis_cluster_functions[] = {
     PHP_ME(RedisCluster, _redir, arginfo_void, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
+    PHP_ME(RedisCluster, acl, arginfo_acl_cl, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, bgrewriteaof, arginfo_key_or_address, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, bgsave, arginfo_key_or_address, ZEND_ACC_PUBLIC)
@@ -343,8 +350,8 @@ void free_cluster_context(zend_object *object) {
 /* Take user provided seeds and return unique and valid ones */
 /* Attempt to connect to a Redis cluster provided seeds and timeout options */
 static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout,
-                               double read_timeout, int persistent, char *auth,
-                               size_t auth_len)
+                               double read_timeout, int persistent, zend_string *user,
+                               zend_string *pass)
 {
     zend_string *hash = NULL, **seeds;
     redisCachedCluster *cc;
@@ -358,9 +365,10 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time
         return;
     }
 
-    if (auth && auth_len) {
-        c->flags->auth = zend_string_init(auth, auth_len, 0);
-    }
+    if (user && ZSTR_LEN(user))
+        c->flags->user = zend_string_copy(user);
+    if (pass && ZSTR_LEN(pass))
+        c->flags->pass = zend_string_copy(pass);
 
     c->timeout = timeout;
     c->read_timeout = read_timeout;
@@ -390,11 +398,11 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time
 
 /* Attempt to load a named cluster configured in php.ini */
 void redis_cluster_load(redisCluster *c, char *name, int name_len) {
-    zval z_seeds, z_timeout, z_read_timeout, z_persistent, z_auth, *z_value;
-    char *iptr, *auth = NULL;
-    size_t auth_len = 0;
+    zval z_seeds, z_tmp, *z_value;
+    zend_string *user = NULL, *pass = NULL;
     double timeout = 0, read_timeout = 0;
     int persistent = 0;
+    char *iptr;
     HashTable *ht_seeds = NULL;
 
     /* Seeds */
@@ -411,69 +419,43 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) {
     }
 
     /* Connection timeout */
-    array_init(&z_timeout);
     if ((iptr = INI_STR("redis.clusters.timeout")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_timeout);
-    }
-    if ((z_value = zend_hash_str_find(Z_ARRVAL(z_timeout), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_value) == IS_STRING) {
-            timeout = atof(Z_STRVAL_P(z_value));
-        } else if (Z_TYPE_P(z_value) == IS_DOUBLE) {
-            timeout = Z_DVAL_P(z_value);
-        } else if (Z_TYPE_P(z_value) == IS_LONG) {
-            timeout = Z_LVAL_P(z_value);
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &timeout);
+        zval_dtor(&z_tmp);
     }
 
     /* Read timeout */
-    array_init(&z_read_timeout);
     if ((iptr = INI_STR("redis.clusters.read_timeout")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_read_timeout);
-    }
-    if ((z_value = zend_hash_str_find(Z_ARRVAL(z_read_timeout), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_value) == IS_STRING) {
-            read_timeout = atof(Z_STRVAL_P(z_value));
-        } else if (Z_TYPE_P(z_value) == IS_DOUBLE) {
-            read_timeout = Z_DVAL_P(z_value);
-        } else if (Z_TYPE_P(z_value) == IS_LONG) {
-            read_timeout = Z_LVAL_P(z_value);
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &read_timeout);
+        zval_dtor(&z_tmp);
     }
 
     /* Persistent connections */
-    array_init(&z_persistent);
     if ((iptr = INI_STR("redis.clusters.persistent")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_persistent);
-    }
-    if ((z_value = zend_hash_str_find(Z_ARRVAL(z_persistent), name, name_len)) != NULL) {
-        if (Z_TYPE_P(z_value) == IS_STRING) {
-            persistent = atoi(Z_STRVAL_P(z_value));
-        } else if (Z_TYPE_P(z_value) == IS_LONG) {
-            persistent = Z_LVAL_P(z_value);
-        }
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_bool(Z_ARRVAL(z_tmp), name, name_len, &persistent);
+        zval_dtor(&z_tmp);
     }
 
-    /* Cluster auth */
-    array_init(&z_auth);
-    if ((iptr = INI_STR("redis.clusters.auth")) != NULL) {
-        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_auth);
-    }
-    if ((z_value = zend_hash_str_find(Z_ARRVAL(z_auth), name, name_len)) != NULL &&
-        Z_TYPE_P(z_value) == IS_STRING && Z_STRLEN_P(z_value) > 0
-    ) {
-        auth = Z_STRVAL_P(z_value);
-        auth_len = Z_STRLEN_P(z_value);
+    if ((iptr = INI_STR("redis.clusters.auth"))) {
+        array_init(&z_tmp);
+        sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
+        redis_conf_auth(Z_ARRVAL(z_tmp), name, name_len, &user, &pass);
+        zval_dtor(&z_tmp);
     }
 
     /* Attempt to create/connect to the cluster */
-    redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, auth, auth_len);
+    redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, user, pass);
 
-    /* Clean up our arrays */
+    /* Clean up */
     zval_dtor(&z_seeds);
-    zval_dtor(&z_timeout);
-    zval_dtor(&z_read_timeout);
-    zval_dtor(&z_persistent);
-    zval_dtor(&z_auth);
+    if (user) zend_string_release(user);
+    if (pass) zend_string_release(pass);
 }
 
 /*
@@ -482,18 +464,19 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) {
 
 /* Create a RedisCluster Object */
 PHP_METHOD(RedisCluster, __construct) {
-    zval *object, *z_seeds = NULL;
-    char *name, *auth = NULL;
-    size_t name_len, auth_len = 0;
+    zval *object, *z_seeds = NULL, *z_auth = NULL;
+    zend_string *user = NULL, *pass = NULL;
     double timeout = 0.0, read_timeout = 0.0;
+    size_t name_len;
     zend_bool persistent = 0;
     redisCluster *context = GET_CONTEXT();
+    char *name;
 
     // Parse arguments
     if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                    "Os!|addbs", &object, redis_cluster_ce, &name,
+                                    "Os!|addbz", &object, redis_cluster_ce, &name,
                                     &name_len, &z_seeds, &timeout, &read_timeout,
-                                    &persistent, &auth, &auth_len) == FAILURE)
+                                    &persistent, &z_auth) == FAILURE)
     {
         RETURN_FALSE;
     }
@@ -503,14 +486,19 @@ PHP_METHOD(RedisCluster, __construct) {
         CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0);
     }
 
-    /* If we've been passed only one argument, the user is attempting to connect
-     * to a named cluster, stored in php.ini, otherwise we'll need manual seeds */
-    if (ZEND_NUM_ARGS() > 1) {
-        redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout,
-            persistent, auth, auth_len);
-    } else {
+    /* If we've got a string try to load from INI */
+    if (ZEND_NUM_ARGS() < 2) {
         redis_cluster_load(context, name, name_len);
+        return;
     }
+
+    /* The normal case, loading from arguments */
+    redis_extract_auth_info(z_auth, &user, &pass);
+    redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout,
+                       persistent, user, pass);
+
+    if (user) zend_string_release(user);
+    if (pass) zend_string_release(pass);
 }
 
 /*
@@ -2498,6 +2486,90 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,
     Z_LVAL_P(z_it) = it;
 }
 
+static int redis_acl_op_readonly(zend_string *op) {
+    /* Only return read-only for operations we know to be */
+    if (ZSTR_STRICMP_STATIC(op, "LIST") ||
+        ZSTR_STRICMP_STATIC(op, "USERS") ||
+        ZSTR_STRICMP_STATIC(op, "GETUSER") ||
+        ZSTR_STRICMP_STATIC(op, "CAT") ||
+        ZSTR_STRICMP_STATIC(op, "GENPASS") ||
+        ZSTR_STRICMP_STATIC(op, "WHOAMI") ||
+        ZSTR_STRICMP_STATIC(op, "LOG")) return 1;
+
+    return 0;
+}
+
+PHP_METHOD(RedisCluster, acl) {
+    redisCluster *c = GET_CONTEXT();
+    smart_string cmdstr = {0};
+    int argc = ZEND_NUM_ARGS(), i, readonly;
+    cluster_cb cb;
+    zend_string *zs;
+    zval *zargs;
+    void *ctx = NULL;
+    short slot;
+
+    /* ACL in cluster needs a slot argument, and then at least the op */
+    if (argc < 2) {
+        WRONG_PARAM_COUNT;
+        RETURN_FALSE;
+    }
+
+    /* Grab all our arguments and determine the command slot */
+    zargs = emalloc(argc * sizeof(*zargs));
+    if (zend_get_parameters_array(ht, argc, zargs) == FAILURE ||
+        (slot = cluster_cmd_get_slot(c, &zargs[0]) < 0))
+    {
+        efree(zargs);
+        RETURN_FALSE;
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc - 1, "ACL");
+
+    /* Read the op, determin if it's readonly, and add it */
+    zs = zval_get_string(&zargs[1]);
+    readonly = redis_acl_op_readonly(zs);
+    redis_cmd_append_sstr_zstr(&cmdstr, zs);
+
+    /* We have specialized handlers for GETUSER and LOG, whereas every
+     * other ACL command can be handled generically */
+    if (zend_string_equals_literal_ci(zs, "GETUSER")) {
+        cb = cluster_acl_getuser_resp;
+    } else if (zend_string_equals_literal_ci(zs, "LOG")) {
+        cb = cluster_acl_log_resp;
+    } else {
+        cb = cluster_variant_resp;
+    }
+
+    zend_string_release(zs);
+
+    /* Process remaining args */
+    for (i = 2; i < argc; i++) {
+        zs = zval_get_string(&zargs[i]);
+        redis_cmd_append_sstr_zstr(&cmdstr, zs);
+        zend_string_release(zs);
+    }
+
+    /* Can we use replicas? */
+    c->readonly = readonly && CLUSTER_IS_ATOMIC(c);
+
+    /* Kick off our command */
+    if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, TYPE_EOF) < 0) {
+        CLUSTER_THROW_EXCEPTION("Unabler to send ACL command", 0);
+        efree(zargs);
+        RETURN_FALSE;
+    }
+
+    if (CLUSTER_IS_ATOMIC(c)) {
+        cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
+    } else {
+        CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx);
+    }
+
+    efree(cmdstr.c);
+    efree(zargs);
+}
+
 /* {{{ proto RedisCluster::scan(string master, long it [, string pat, long cnt]) */
 PHP_METHOD(RedisCluster, scan) {
     redisCluster *c = GET_CONTEXT();
diff --git a/redis_cluster.h b/redis_cluster.h
index 379d034c15..c6959fde10 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -99,6 +99,7 @@ void free_cluster_context(zend_object *object);
 
 /* RedisCluster method implementation */
 PHP_METHOD(RedisCluster, __construct);
+PHP_METHOD(RedisCluster, acl);
 PHP_METHOD(RedisCluster, close);
 PHP_METHOD(RedisCluster, get);
 PHP_METHOD(RedisCluster, set);
diff --git a/redis_commands.c b/redis_commands.c
index 97442de387..3b19194438 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2047,26 +2047,47 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+char *redis_variadic_str_cmd(char *kw, zval *argv, int argc, int *cmd_len) {
+    smart_string cmdstr = {0};
+    zend_string *zstr;
+    int i;
+
+    redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw));
+
+    for (i = 0; i < argc; i++) {
+        zstr = zval_get_string(&argv[i]);
+        redis_cmd_append_sstr_zstr(&cmdstr, zstr);
+        zend_string_release(zstr);
+    }
+
+    *cmd_len = cmdstr.len;
+    return cmdstr.c;
+}
+
 int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                    char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    char *pw;
-    size_t pw_len;
+    zend_string *user = NULL, *pass = NULL;
+    zval *ztest;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pw, &pw_len)
-                             ==FAILURE)
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z!", &ztest) == FAILURE ||
+        redis_extract_auth_info(ztest, &user, &pass) == FAILURE)
     {
         return FAILURE;
     }
 
-    // Construct our AUTH command
-    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "s", pw, pw_len);
+    /* Construct either AUTH   or AUTH  */
+    if (user && pass) {
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "SS", user, pass);
+    } else {
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "S", pass);
+    }
 
-    // Free previously allocated password, and update
-    if (redis_sock->auth) zend_string_release(redis_sock->auth);
-    redis_sock->auth = zend_string_init(pw, pw_len, 0);
+    redis_sock_set_auth(redis_sock, user, pass);
+
+    if (user) zend_string_release(user);
+    if (pass) zend_string_release(pass);
 
-    // Success
     return SUCCESS;
 }
 
diff --git a/redis_commands.h b/redis_commands.h
index addcce532f..54bf7ee8f4 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -23,9 +23,12 @@ typedef struct subscribeContext {
 
 /* Construct a raw command */
 int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len);
+
 /* Construct a script command */
 smart_string *redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args);
 
+char *redis_variadic_str_cmd(char *kw, zval *argv, int argc, int *cmd_len);
+
 /* Redis command generics.  Many commands share common prototypes meaning that
  * we can write one function to handle all of them.  For example, there are
  * many COMMAND key value commands, or COMMAND key commands. */
diff --git a/redis_session.c b/redis_session.c
index e3358148d2..5428d5279c 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -39,6 +39,9 @@
 #include "SAPI.h"
 #include "ext/standard/url.h"
 
+#define REDIS_SESSION_PREFIX "PHPREDIS_SESSION:"
+#define CLUSTER_SESSION_PREFIX "PHPREDIS_CLUSTER_SESSION:"
+
 /* Session lock LUA as well as its SHA1 hash */
 #define LOCK_RELEASE_LUA_STR "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"
 #define LOCK_RELEASE_LUA_LEN (sizeof(LOCK_RELEASE_LUA_STR) - 1)
@@ -49,6 +52,9 @@
 #define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3))
 #define NEGATIVE_LOCK_RESPONSE 1
 
+#define CLUSTER_DEFAULT_PREFIX() \
+    zend_string_init(CLUSTER_SESSION_PREFIX, sizeof(CLUSTER_SESSION_PREFIX) - 1, 0)
+
 ps_module ps_mod_redis = {
     PS_MOD_UPDATE_TIMESTAMP(redis)
 };
@@ -83,6 +89,9 @@ typedef struct {
 
 } redis_pool;
 
+// static char *session_conf_string(HashTable *ht, const char *key, size_t keylen) {
+// }
+
 PHP_REDIS_API void
 redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, int database)
 {
@@ -360,12 +369,18 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_
     }
 }
 
+#if PHP_VERSION_ID < 70300
+#define REDIS_URL_STR(umem) umem
+#else
+#define REDIS_URL_STR(umem) ZSTR_VAL(umem)
+#endif
+
 /* {{{ PS_OPEN_FUNC
  */
 PS_OPEN_FUNC(redis)
 {
     php_url *url;
-    zval params, *param;
+    zval params;
     int i, j, path_len;
 
     redis_pool *pool = ecalloc(1, sizeof(*pool));
@@ -383,11 +398,10 @@ PS_OPEN_FUNC(redis)
         if (i < j) {
             int weight = 1;
             double timeout = 86400.0, read_timeout = 0.0;
-            int persistent = 0;
-            int database = -1;
-            char *persistent_id = NULL;
-            long retry_interval = 0;
-            zend_string *prefix = NULL, *auth = NULL;
+            int persistent = 0, db = -1;
+            zend_long retry_interval = 0;
+            zend_string *persistent_id = NULL, *prefix = NULL;
+            zend_string *user = NULL, *pass = NULL;
 
             /* translate unix: into file: */
             if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) {
@@ -413,51 +427,28 @@ PS_OPEN_FUNC(redis)
 
             /* parse parameters */
             if (url->query != NULL) {
+                HashTable *ht;
                 char *query;
                 array_init(¶ms);
 
-#if (PHP_VERSION_ID < 70300)
-                if (url->fragment != NULL) {
-                    spprintf(&query, 0, "%s#%s", url->query, url->fragment);
-                } else {
-                    query = estrdup(url->query);
-                }
-#else
-                if (url->fragment != NULL) {
-                    spprintf(&query, 0, "%s#%s", ZSTR_VAL(url->query), ZSTR_VAL(url->fragment));
+                if (url->fragment) {
+                    spprintf(&query, 0, "%s#%s", REDIS_URL_STR(url->query), REDIS_URL_STR(url->fragment));
                 } else {
-                    query = estrndup(ZSTR_VAL(url->query), ZSTR_LEN(url->query));
+                    query = estrdup(REDIS_URL_STR(url->query));
                 }
-#endif
-                sapi_module.treat_data(PARSE_STRING, query, ¶ms);
 
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "weight", sizeof("weight") - 1)) != NULL) {
-                    weight = zval_get_long(param);
-                }
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "timeout", sizeof("timeout") - 1)) != NULL) {
-                    timeout = zval_get_double(param);
-                }
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "read_timeout", sizeof("read_timeout") - 1)) != NULL) {
-                    read_timeout = zval_get_double(param);
-                }
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent", sizeof("persistent") - 1)) != NULL) {
-                    persistent = (atol(Z_STRVAL_P(param)) == 1 ? 1 : 0);
-                }
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent_id", sizeof("persistent_id") - 1)) != NULL) {
-                    persistent_id = estrndup(Z_STRVAL_P(param), Z_STRLEN_P(param));
-                }
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "prefix", sizeof("prefix") - 1)) != NULL) {
-                    prefix = zend_string_init(Z_STRVAL_P(param), Z_STRLEN_P(param), 0);
-                }
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "auth", sizeof("auth") - 1)) != NULL) {
-                    auth = zend_string_init(Z_STRVAL_P(param), Z_STRLEN_P(param), 0);
-                }
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "database", sizeof("database") - 1)) != NULL) {
-                    database = zval_get_long(param);
-                }
-                if ((param = zend_hash_str_find(Z_ARRVAL(params), "retry_interval", sizeof("retry_interval") - 1)) != NULL) {
-                    retry_interval = zval_get_long(param);
-                }
+                sapi_module.treat_data(PARSE_STRING, query, ¶ms);
+                ht = Z_ARRVAL(params);
+
+                REDIS_CONF_INT_STATIC(ht, "weight", &weight);
+                REDIS_CONF_BOOL_STATIC(ht, "persistent", &persistent);
+                REDIS_CONF_INT_STATIC(ht, "database", &db);
+                REDIS_CONF_DOUBLE_STATIC(ht, "timeout", &timeout);
+                REDIS_CONF_DOUBLE_STATIC(ht, "read_timeout", &read_timeout);
+                REDIS_CONF_LONG_STATIC(ht, "retry_interval", &retry_interval);
+                REDIS_CONF_STRING_STATIC(ht, "persistent_id", &persistent_id);
+                REDIS_CONF_STRING_STATIC(ht, "prefix", &prefix);
+                REDIS_CONF_AUTH_STATIC(ht, "auth", &user, &pass);
 
                 zval_dtor(¶ms);
             }
@@ -466,33 +457,38 @@ PS_OPEN_FUNC(redis)
                 php_url_free(url);
                 if (persistent_id) efree(persistent_id);
                 if (prefix) zend_string_release(prefix);
-                if (auth) zend_string_release(auth);
+                if (user) zend_string_release(user);
+                if (pass) zend_string_release(pass);
                 redis_pool_free(pool);
                 PS_SET_MOD_DATA(NULL);
                 return FAILURE;
             }
 
             RedisSock *redis_sock;
+            char *addr, *scheme;
+            size_t addrlen;
+            int port;
+
+            scheme = url->scheme ? REDIS_URL_STR(url->scheme) : "tcp";
             if (url->host) {
-                zend_string *address;
-#if (PHP_VERSION_ID < 70300)
-                address = strpprintf(0, "%s://%s", url->scheme ? url->scheme : "tcp", url->host);
-#else
-                address = strpprintf(0, "%s://%s", url->scheme ? ZSTR_VAL(url->scheme) : "tcp", ZSTR_VAL(url->host));
-#endif
-                redis_sock = redis_sock_create(ZSTR_VAL(address), ZSTR_LEN(address), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval);
-                zend_string_release(address);
+                port = url->port;
+                addrlen = spprintf(&addr, 0, "%s://%s", scheme, REDIS_URL_STR(url->host));
             } else { /* unix */
-#if (PHP_VERSION_ID < 70300)
-                redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval);
-#else
-                redis_sock = redis_sock_create(ZSTR_VAL(url->path), ZSTR_LEN(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval);
-#endif
+                port = 0;
+                addr = REDIS_URL_STR(url->path);
+                addrlen = strlen(addr);
             }
-            redis_pool_add(pool, redis_sock, weight, database);
+
+            redis_sock = redis_sock_create(addr, addrlen, port, timeout, read_timeout,
+                                           persistent, ZSTR_VAL(persistent_id), retry_interval);
+
+            redis_pool_add(pool, redis_sock, weight, db);
             redis_sock->prefix = prefix;
-            redis_sock->auth = auth;
+            redis_sock_set_auth(redis_sock, user, pass);
 
+            efree(addr);
+            if (user) zend_string_release(user);
+            if (pass) zend_string_release(pass);
             php_url_free(url);
         }
     }
@@ -534,7 +530,7 @@ static zend_string *
 redis_session_key(RedisSock *redis_sock, const char *key, int key_len)
 {
     zend_string *session;
-    char default_prefix[] = "PHPREDIS_SESSION:";
+    char default_prefix[] = REDIS_SESSION_PREFIX;
     char *prefix = default_prefix;
     size_t prefix_len = sizeof(default_prefix)-1;
 
@@ -838,42 +834,6 @@ PS_GC_FUNC(redis)
  * Redis Cluster session handler functions
  */
 
-/* Helper to extract timeout values */
-static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_len,
-                                 double *val)
-{
-    zval *z_val;
-
-    if ((z_val = zend_hash_str_find(ht_conf, key, key_len - 1)) != NULL &&
-        Z_TYPE_P(z_val) == IS_STRING
-    ) {
-        *val = atof(Z_STRVAL_P(z_val));
-    }
-}
-
-/* Simple helper to retrieve a boolean (0 or 1) value from a string stored in our
- * session.save_path variable.  This is so the user can use 0, 1, or 'true',
- * 'false' */
-static void session_conf_bool(HashTable *ht_conf, char *key, int keylen,
-                              int *retval) {
-    zval *z_val;
-    char *str;
-    int strlen;
-
-    /* See if we have the option, and it's a string */
-    if ((z_val = zend_hash_str_find(ht_conf, key, keylen - 1)) != NULL &&
-        Z_TYPE_P(z_val) == IS_STRING
-    ) {
-        str = Z_STRVAL_P(z_val);
-        strlen = Z_STRLEN_P(z_val);
-
-        /* true/yes/1 are treated as true.  Everything else is false */
-        *retval = (strlen == 4 && !strncasecmp(str, "true", 4)) ||
-                  (strlen == 3 && !strncasecmp(str, "yes", 3)) ||
-                  (strlen == 1 && !strncasecmp(str, "1", 1));
-    }
-}
-
 /* Prefix a session key */
 static char *cluster_session_key(redisCluster *c, const char *key, int keylen,
                                  int *skeylen, short *slot) {
@@ -891,36 +851,31 @@ static char *cluster_session_key(redisCluster *c, const char *key, int keylen,
 
 PS_OPEN_FUNC(rediscluster) {
     redisCluster *c;
-    zval z_conf, *z_val;
+    zval z_conf, *zv;
     HashTable *ht_conf, *ht_seeds;
     double timeout = 0, read_timeout = 0;
     int persistent = 0, failover = REDIS_FAILOVER_NONE;
-    size_t prefix_len, auth_len = 0;
-    char *prefix, *auth = NULL;
+    zend_string *prefix = NULL, *user = NULL, *pass = NULL, *failstr = NULL;
 
     /* Parse configuration for session handler */
     array_init(&z_conf);
     sapi_module.treat_data(PARSE_STRING, estrdup(save_path), &z_conf);
 
-    /* Sanity check that we're able to parse and have a seeds array */
-    if (Z_TYPE(z_conf) != IS_ARRAY ||
-        (z_val = zend_hash_str_find(Z_ARRVAL(z_conf), "seed", sizeof("seed") - 1)) == NULL ||
-        Z_TYPE_P(z_val) != IS_ARRAY)
-    {
+    /* We need seeds */
+    zv = REDIS_HASH_STR_FIND_TYPE_STATIC(Z_ARRVAL(z_conf), "seed", IS_ARRAY);
+    if (zv == NULL) {
         zval_dtor(&z_conf);
         return FAILURE;
     }
 
     /* Grab a copy of our config hash table and keep seeds array */
     ht_conf = Z_ARRVAL(z_conf);
-    ht_seeds = Z_ARRVAL_P(z_val);
+    ht_seeds = Z_ARRVAL_P(zv);
 
-    /* Grab timeouts if they were specified */
-    session_conf_timeout(ht_conf, "timeout", sizeof("timeout"), &timeout);
-    session_conf_timeout(ht_conf, "read_timeout", sizeof("read_timeout"), &read_timeout);
-
-    /* Grab persistent option */
-    session_conf_bool(ht_conf, "persistent", sizeof("persistent"), &persistent);
+    /* Optional configuration settings */
+    REDIS_CONF_DOUBLE_STATIC(ht_conf, "timeout", &timeout);
+    REDIS_CONF_DOUBLE_STATIC(ht_conf, "read_timeout", &read_timeout);
+    REDIS_CONF_BOOL_STATIC(ht_conf, "persistent", &persistent);
 
     /* Sanity check on our timeouts */
     if (timeout < 0 || read_timeout < 0) {
@@ -930,58 +885,49 @@ PS_OPEN_FUNC(rediscluster) {
         return FAILURE;
     }
 
-    /* Look for a specific prefix */
-    if ((z_val = zend_hash_str_find(ht_conf, "prefix", sizeof("prefix") - 1)) != NULL &&
-        Z_TYPE_P(z_val) == IS_STRING && Z_STRLEN_P(z_val) > 0
-    ) {
-        prefix = Z_STRVAL_P(z_val);
-        prefix_len = Z_STRLEN_P(z_val);
-    } else {
-        prefix = "PHPREDIS_CLUSTER_SESSION:";
-        prefix_len = sizeof("PHPREDIS_CLUSTER_SESSION:")-1;
-    }
+    REDIS_CONF_STRING_STATIC(ht_conf, "prefix", &prefix);
+    REDIS_CONF_AUTH_STATIC(ht_conf, "auth", &user, &pass);
+    REDIS_CONF_STRING_STATIC(ht_conf, "failover", &failstr);
 
-    /* Look for a specific failover setting */
-    if ((z_val = zend_hash_str_find(ht_conf, "failover", sizeof("failover") - 1)) != NULL &&
-        Z_TYPE_P(z_val) == IS_STRING && Z_STRLEN_P(z_val) > 0
-    ) {
-        if (!strcasecmp(Z_STRVAL_P(z_val), "error")) {
+    /* Need to massage failover string if we have it */
+    if (failstr) {
+        if (zend_string_equals_literal_ci(failstr, "error")) {
             failover = REDIS_FAILOVER_ERROR;
-        } else if (!strcasecmp(Z_STRVAL_P(z_val), "distribute")) {
+        } else if (zend_string_equals_literal_ci(failstr, "distribute")) {
             failover = REDIS_FAILOVER_DISTRIBUTE;
         }
     }
 
-    /* Look for a specific auth setting */
-    if ((z_val = zend_hash_str_find(ht_conf, "auth", sizeof("auth") - 1)) != NULL &&
-        Z_TYPE_P(z_val) == IS_STRING && Z_STRLEN_P(z_val) > 0
-    ) {
-        auth = Z_STRVAL_P(z_val);
-        auth_len = Z_STRLEN_P(z_val);
-    }
-
     redisCachedCluster *cc;
     zend_string **seeds, *hash = NULL;
     uint32_t nseeds;
 
-    /* Extract at least one valid seed or abort */ 
+    #define CLUSTER_SESSION_CLEANUP() \
+        if (hash) zend_string_release(hash); \
+        if (prefix) zend_string_release(prefix); \
+        if (user) zend_string_release(user); \
+        if (pass) zend_string_release(pass); \
+        free_seed_array(seeds, nseeds); \
+        zval_dtor(&z_conf); \
+
+    /* Extract at least one valid seed or abort */
     seeds = cluster_validate_args(timeout, read_timeout, ht_seeds, &nseeds, NULL);
     if (seeds == NULL) {
         php_error_docref(NULL, E_WARNING, "No valid seeds detected");
+        CLUSTER_SESSION_CLEANUP();
         zval_dtor(&z_conf);
         return FAILURE;
     }
 
-    #define CLUSTER_SESSION_CLEANUP() \
-        if (hash) zend_string_release(hash); \
-        free_seed_array(seeds, nseeds); \
-        zval_dtor(&z_conf); \
-
     c = cluster_create(timeout, read_timeout, failover, persistent);
-    c->flags->prefix = zend_string_init(prefix, prefix_len, 0);
 
-    if (auth && auth_len) 
-        c->flags->auth = zend_string_init(auth, auth_len, 0);
+    if (prefix) {
+        c->flags->prefix = zend_string_copy(prefix);
+    } else {
+        c->flags->prefix = CLUSTER_DEFAULT_PREFIX();
+    }
+
+    redis_sock_set_auth(c->flags, user, pass);
 
     /* First attempt to load from cache */
     if (CLUSTER_CACHING_ENABLED()) {
diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php
index 9c53c9ce86..a852688625 100644
--- a/tests/RedisArrayTest.php
+++ b/tests/RedisArrayTest.php
@@ -616,7 +616,7 @@ public function testDistribution() {
     }
 }
 
-function run_tests($className, $str_filter, $str_host, $str_auth) {
+function run_tests($className, $str_filter, $str_host, $auth) {
         // reset rings
         global $newRing, $oldRing, $serverList;
 
@@ -625,7 +625,7 @@ function run_tests($className, $str_filter, $str_host, $str_auth) {
         $serverList = ["$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"];
 
         // run
-        return TestSuite::run($className, $str_filter, $str_host, $str_auth);
+        return TestSuite::run($className, $str_filter, $str_host, NULL, $auth);
 }
 
 ?>
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 972c64f6b9..0da0904cd5 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -47,6 +47,7 @@ public function testDoublePipeNoOp() { return $this->markTestSkipped(); }
     public function testSwapDB() { return $this->markTestSkipped(); }
     public function testConnectException() { return $this->markTestSkipped(); }
     public function testTlsConnect() { return $this->markTestSkipped(); }
+    public function testInvalidAuthArgs() { return $this->markTestSkipped(); }
 
     /* Session locking feature is currently not supported in in context of Redis Cluster.
        The biggest issue for this is the distribution nature of Redis cluster */
@@ -63,8 +64,8 @@ public function testSession_noUnlockOfOtherProcess() { return $this->markTestSki
     public function testSession_lockWaitTime() { return $this->markTestSkipped(); }
 
     /* Load our seeds on construction */
-    public function __construct($str_host, $str_auth) {
-        parent::__construct($str_host, $str_auth);
+    public function __construct($str_host, $i_port, $str_auth) {
+        parent::__construct($str_host, $i_port, $str_auth);
 
         $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap';
 
@@ -688,6 +689,15 @@ public function testReplyLiteral() {
         $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false);
     }
 
+    /* Redis and RedisCluster use the same handler for the ACL command but verify we can direct
+       the command to a specific node. */
+    public function testAcl() {
+        if ( ! $this->minVersionCheck("6.0"))
+            return $this->markTestSkipped();
+
+        $this->assertInArray('default', $this->redis->acl('foo', 'USERS'));
+    }
+
     public function testSession()
     {
         @ini_set('session.save_handler', 'rediscluster');
@@ -735,9 +745,11 @@ public function testConnectionPool() {
      */
     protected function getFullHostPath()
     {
+        $auth = $this->getAuthFragment();
+
         return implode('&', array_map(function ($host) {
             return 'seed[]=' . $host;
-        }, self::$_arr_node_map)) . ($this->getAuth() ? '&auth=' . $this->getAuth() : '');
+        }, self::$_arr_node_map)) . ($auth ? "&$auth" : '');
     }
 }
 ?>
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 3cf753d8a9..4c92e7f67e 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4,8 +4,6 @@
 
 class Redis_Test extends TestSuite
 {
-    const PORT = 6379;
-
     /* City lat/long */
     protected $cities = [
         'Chico'         => [-121.837478, 39.728494],
@@ -53,11 +51,54 @@ 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 % 2 == 0)
+                return "auth[user]=$user&auth[pass]=$pass";
+            else
+                return "auth[]=$user&auth[]=$pass";
+        } else if ($pass) {
+            if ($_authidx % 3 == 0)
+                return "auth[pass]=$pass";
+            if ($_authidx % 2 == 0)
+                return "auth[]=$pass";
+            else
+                return "auth=$pass";
+        } else {
+            return NULL;
+        }
+    }
+
     protected function getFullHostPath()
     {
         $fullHostPath = parent::getFullHostPath();
-        if (isset($fullHostPath) && $this->getAuth()) {
-            $fullHostPath .= '?auth=' . $this->getAuth();
+        $authFragment = $this->getAuthFragment();
+
+        if (isset($fullHostPath) && $authFragment) {
+            $fullHostPath .= "?$authFragment";
         }
         return $fullHostPath;
     }
@@ -65,7 +106,7 @@ protected function getFullHostPath()
     protected function newInstance() {
         $r = new Redis();
 
-        $r->connect($this->getHost(), self::PORT);
+        $r->connect($this->getHost(), $this->getPort());
 
         if($this->getAuth()) {
             $this->assertTrue($r->auth($this->getAuth()));
@@ -4958,7 +4999,7 @@ public function testReadTimeoutOption() {
     public function testIntrospection() {
         // Simple introspection tests
         $this->assertTrue($this->redis->getHost() === $this->getHost());
-        $this->assertTrue($this->redis->getPort() === self::PORT);
+        $this->assertTrue($this->redis->getPort() === $this->getPort());
         $this->assertTrue($this->redis->getAuth() === $this->getAuth());
     }
 
@@ -5435,7 +5476,6 @@ public function genericGeoRadiusTest($cmd) {
                             $ret2 = $this->redis->$cmd('{gk}', $city, 500, 'mi', $realopts);
                         }
 
-                        if ($ret1 != $ret2) die();
                         $this->assertEquals($ret1, $ret2);
                     }
                 }
@@ -6024,6 +6064,100 @@ public function testXInfo()
         }
     }
 
+    public function testInvalidAuthArgs() {
+        $obj_new = $this->newInstance();
+
+        $arr_args = [
+            [],
+            [NULL, NULL],
+            ['foo', 'bar', 'baz'],
+            ['a','b','c','d'],
+            ['a','b','c'],
+            [['a','b'], 'a'],
+            [['a','b','c']],
+            [[NULL, 'pass']],
+            [[NULL, NULL]],
+        ];
+
+        foreach ($arr_args as $arr_arg) {
+            try {
+                @call_user_func_array([$obj_new, 'auth'], $arr_arg);
+            } catch (Exception $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  */
+        $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  */
+        $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")) {
@@ -6285,11 +6419,15 @@ public function testConnectException() {
 
     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://' . $host, 6378, 0, null, 0, 0, [
-                'verify_peer_name' => $verify,
-                'verify_peer' => false,
+                'stream' => ['verify_peer_name' => $verify, 'verify_peer' => false]
             ]));
         }
     }
diff --git a/tests/TestRedis.php b/tests/TestRedis.php
index e6f70ca87d..843a28f884 100644
--- a/tests/TestRedis.php
+++ b/tests/TestRedis.php
@@ -11,7 +11,7 @@
 ini_set( 'display_errors','1');
 
 /* Grab options */
-$arr_args = getopt('', ['host:', 'class:', 'test:', 'nocolors', 'auth:']);
+$arr_args = getopt('', ['host:', 'port:', 'class:', 'test:', 'nocolors', 'user:', 'auth:']);
 
 /* Grab the test the user is trying to run */
 $arr_valid_classes = ['redis', 'redisarray', 'rediscluster', 'redissentinel'];
@@ -21,12 +21,24 @@
 /* Get our test filter if provided one */
 $str_filter = isset($arr_args['test']) ? $arr_args['test'] : NULL;
 
-/* Grab override test host if it was passed */
+/* Grab override host/port if it was passed */
 $str_host = isset($arr_args['host']) ? $arr_args['host'] : '127.0.0.1';
+$i_port = isset($arr_args['port']) ? intval($arr_args['port']) : 6379;
 
-/* Grab redis authentication password */
+/* Get optional username and auth (password) */
+$str_user = isset($arr_args['user']) ? $arr_args['user'] : NULL;
 $str_auth = isset($arr_args['auth']) ? $arr_args['auth'] : NULL;
 
+/* Massage the actual auth arg */
+$auth = NULL;
+if ($str_user && $str_auth) {
+    $auth = [$str_user, $str_auth];
+} else if ($str_auth) {
+    $auth = $str_auth;
+} else if ($str_user) {
+    echo TestSuite::make_warning("User passed without a password, ignoring!\n");
+}
+
 /* Validate the class is known */
 if (!in_array($str_class, $arr_valid_classes)) {
     echo "Error:  Valid test classes are Redis, RedisArray, RedisCluster and RedisSentinel!\n";
@@ -44,7 +56,7 @@
 echo "Testing class ";
 if ($str_class == 'redis') {
     echo TestSuite::make_bold("Redis") . "\n";
-    exit(TestSuite::run("Redis_Test", $str_filter, $str_host, $str_auth));
+    exit(TestSuite::run("Redis_Test", $str_filter, $str_host, $i_port, $auth));
 } else if ($str_class == 'redisarray') {
     echo TestSuite::make_bold("RedisArray") . "\n";
     global $useIndex;
@@ -52,19 +64,23 @@
         echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n";
 
         /* The various RedisArray subtests we can run */
-        $arr_ra_tests = ['Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'];
+        $arr_ra_tests = [
+            'Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test',
+             'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'
+        ];
+
         foreach ($arr_ra_tests as $str_test) {
             /* Run until we encounter a failure */
-            if (run_tests($str_test, $str_filter, $str_host, $str_auth) != 0) {
+            if (run_tests($str_test, $str_filter, $str_host, $auth) != 0) {
                 exit(1);
             }
         }
     }
 } else if ($str_class == 'rediscluster') {
     echo TestSuite::make_bold("RedisCluster") . "\n";
-    exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host, $str_auth));
+    exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host, $i_port, $auth));
 } else {
     echo TestSuite::make_bold("RedisSentinel") . "\n";
-    exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host, $str_auth));
+    exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host, $i_port, $auth));
 }
 ?>
diff --git a/tests/TestSuite.php b/tests/TestSuite.php
index 24cfed6088..c879b33093 100644
--- a/tests/TestSuite.php
+++ b/tests/TestSuite.php
@@ -6,11 +6,12 @@ class TestSkippedException extends Exception {}
 // phpunit is such a pain to install, we're going with pure-PHP here.
 class TestSuite
 {
-    /* Host the tests will use */
+    /* Host and port the unit tests will use */
     private $str_host;
+    private $i_port = 6379;
 
     /* Redis authentication we'll use */
-    private $str_auth;
+    private $auth;
 
     private static $_boo_colorize = false;
 
@@ -28,13 +29,15 @@ class TestSuite
     public static $errors = [];
     public static $warnings = [];
 
-    public function __construct($str_host, $str_auth) {
+    public function __construct($str_host, $i_port, $auth) {
         $this->str_host = $str_host;
-        $this->str_auth = $str_auth;
+        $this->i_port = $i_port;
+        $this->auth = $auth;
     }
 
     public function getHost() { return $this->str_host; }
-    public function getAuth() { return $this->str_auth; }
+    public function getPort() { return $this->i_port; }
+    public function getAuth() { return $this->auth; }
 
     /**
      * Returns the fully qualified host path,
@@ -45,7 +48,7 @@ public function getAuth() { return $this->str_auth; }
     protected function getFullHostPath()
     {
         return $this->str_host
-            ? 'tcp://' . $this->str_host . ':6379'
+            ? 'tcp://' . $this->str_host . ':' . $this->i_port
             : null;
     }
 
@@ -95,6 +98,75 @@ protected function assertTrue($bool) {
         return false;
     }
 
+    protected function assertInArray($ele, $arr, $cb = NULL) {
+        if ($cb && !is_callable($cb))
+            die("Fatal:  assertInArray callback must be callable!\n");
+
+        if (($in = in_array($ele, $arr)) && (!$cb || $cb($arr[array_search($ele, $arr)])))
+            return true;
+
+
+        $bt = debug_backtrace(false);
+        $ex = $in ? 'validation' : 'missing';
+        self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n",
+            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $ele);
+
+        return false;
+    }
+
+    protected function assertArrayKey($arr, $key, $cb = NULL) {
+        if ($cb && !is_callable($cb))
+            die("Fatal:  assertArrayKey callback must be callable\n");
+
+        if (($exists = isset($arr[$key])) && (!$cb || $cb($arr[$key])))
+            return true;
+
+        $bt = debug_backtrace(false);
+        $ex = $exists ? 'validation' : 'missing';
+        self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n",
+            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $key);
+
+        return false;
+    }
+
+    protected function assertValidate($val, $cb) {
+        if ( ! is_callable($cb))
+            die("Fatal:  Callable assertValidate callback required\n");
+
+        if ($cb($val))
+            return true;
+
+        $bt = debug_backtrace(false);
+        self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n",
+            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
+
+        return false;
+    }
+
+    protected function assertThrowsMatch($arg, $cb, $regex = NULL) {
+        $threw = $match = false;
+
+        if ( ! is_callable($cb))
+            die("Fatal:  Callable assertThrows callback required\n");
+
+        try {
+            $cb($arg);
+        } catch (Exception $ex) {
+            $threw = true;
+            $match = !$regex || preg_match($regex, $ex->getMessage());
+        }
+
+        if ($threw && $match)
+            return true;
+
+        $bt = debug_backtrace(false);
+        $ex = !$threw ? 'no exception' : "no match '$regex'";
+        self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s]\n",
+            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex);
+
+        return false;
+    }
+
     protected function assertLess($a, $b) {
         if($a < $b)
             return;
@@ -157,7 +229,7 @@ public static function flagColorization($boo_override) {
             posix_isatty(STDOUT);
     }
 
-    public static function run($className, $str_limit = NULL, $str_host = NULL, $str_auth = NULL) {
+    public static function run($className, $str_limit = NULL, $str_host = NULL, $i_port = NULL, $auth = NULL) {
         /* Lowercase our limit arg if we're passed one */
         $str_limit = $str_limit ? strtolower($str_limit) : $str_limit;
 
@@ -181,7 +253,7 @@ public static function run($className, $str_limit = NULL, $str_host = NULL, $str
             echo self::make_bold($str_out_name);
 
             $count = count($className::$errors);
-            $rt = new $className($str_host, $str_auth);
+            $rt = new $className($str_host, $i_port, $auth);
 
             try {
                 $rt->setUp();
diff --git a/tests/make-cluster.sh b/tests/make-cluster.sh
index b3a9108668..244cc59a7b 100755
--- a/tests/make-cluster.sh
+++ b/tests/make-cluster.sh
@@ -15,6 +15,7 @@ MAPFILE=$NODEDIR/nodemap
 
 # Host, nodes, replicas, ports, etc.  Change if you want different values
 HOST="127.0.0.1"
+NOASK=0
 NODES=12
 REPLICAS=3
 START_PORT=7000
@@ -36,10 +37,15 @@ verboseRun() {
 
 # Spawn a specific redis instance, cluster enabled 
 spawnNode() {
+    # ACL file if we have one
+    if [ ! -z "$ACLFILE" ]; then
+        ACLARG="--aclfile $ACLFILE"
+    fi
+
     # Attempt to spawn the node
     verboseRun redis-server --cluster-enabled yes --dir $NODEDIR --port $PORT \
         --cluster-config-file node-$PORT.conf --daemonize yes --save \'\' \
-        --bind $HOST --dbfilename node-$PORT.rdb
+        --bind $HOST --dbfilename node-$PORT.rdb $ACLARG
 
     # Abort if we can't spin this instance
     if [ $? -ne 0 ]; then 
@@ -80,6 +86,10 @@ checkNodes() {
 cleanConfigInfo() {
     verboseRun mkdir -p $NODEDIR
     verboseRun rm -f $NODEDIR/*
+
+    if [ -f "$ACLFILE" ]; then
+        cp $ACLFILE $NODEDIR/$ACLFILE
+    fi
 }
 
 # Initialize our cluster with redis-trib.rb
@@ -89,7 +99,18 @@ initCluster() {
         TRIBARGS="$TRIBARGS $HOST:$PORT"
     done
 
-    verboseRun redis-trib.rb create --replicas $REPLICAS $TRIBARGS
+    if [[ ! -z "$USER" ]]; then
+        USERARG="--user $USER"
+    fi
+    if [[ ! -z "$PASS" ]]; then
+        PASSARG="-a $PASS"
+    fi
+
+    if [[ "$1" -eq "1" ]]; then
+        echo yes | redis-cli $USERARG $PASSARG -p $START_PORT --cluster create $TRIBARGS --cluster-replicas $REPLICAS
+    else
+        verboseRun redis-cli $USERARG $PASSARG -p $START_PORT --cluster create $TRIBARGS --cluster-replicas $REPLICAS
+    fi
 
     if [ $? -ne 0 ]; then
         echo "Error:  Couldn't create cluster!"
@@ -109,7 +130,7 @@ startCluster() {
     spawnNodes
 
     # Attempt to initialize the cluster
-    initCluster
+    initCluster $1
 }
 
 # Shut down nodes in our cluster
@@ -119,24 +140,86 @@ stopCluster() {
     done
 }
 
-# Make sure we have redis-server and redis-trib.rb on the path
+# Shut down nodes by killing them
+killCluster() {
+    for PORT in `seq $START_PORT $END_PORT`; do
+        PID=$(ps aux|grep [r]edis-server|grep $PORT|awk '{print $2}')
+        echo -n "Killing $PID: "
+        if kill $PID; then
+            echo "OK"
+        else
+            echo "ERROR"
+        fi
+    done
+}
+
+printUsage() {
+    echo "Usage: make-cluster [OPTIONS] "
+    echo
+    echo "  Options"
+    echo
+    echo "  -u Redis username to use when spawning cluster"
+    echo "  -p Redis password to use when spawning cluster"
+    echo "  -a Redis acl filename to use when spawning cluster"
+    echo "  -y Automatically send 'yes' when starting cluster"
+    echo "  -h This message"
+    echo
+    exit 0
+}
+
+# We need redis-server
 checkExe redis-server
-checkExe redis-trib.rb
 
-# Override the host if we've got $2
-if [[ ! -z "$2" ]]; then
-   HOST=$2
+while getopts "u:p:a:hy" OPT; do
+    case $OPT in
+        h)
+            printUsage
+            ;;
+        a)
+            if [ ! -f "$OPTARG" ]; then
+                echo "Error:  '$OPTARG' is not a filename!"
+                exit -1
+            fi
+            ACLFILE=$OPTARG
+            ;;
+        u)
+            USER=$OPTARG
+            ;;
+        p)
+            PASS=$OPTARG
+            ;;
+        h)
+            HOST=$OPTARG
+            ;;
+        y)
+            NOASK=1
+            ;;
+        *)
+            echo "Unknown option: $OPT"
+            exit 1
+            ;;
+    esac
+done
+
+shift "$((OPTIND - 1))"
+
+if [[ $# -lt 1 ]]; then
+    echo "Error:  Must pass an operation (start or stop)"
+    exit -1
 fi
 
-# Main entry point to start or stop/kill a cluster
 case "$1" in
     start)
-        startCluster
+        startCluster $NOASK
         ;;
     stop)
         stopCluster
         ;;
+    kill)
+        killCluster
+        ;;
     *)
-        echo "Usage $0  [host]"
+        echo "Usage: make-cluster.sh [options] "
+        exit 1
         ;;
 esac
diff --git a/tests/startSession.php b/tests/startSession.php
index 2149da684b..c0ae1884a7 100644
--- a/tests/startSession.php
+++ b/tests/startSession.php
@@ -38,4 +38,4 @@
 }
 session_write_close();
 
-echo $sessionStartSuccessful ? 'SUCCESS' : 'FAILURE';
\ No newline at end of file
+echo $sessionStartSuccessful ? 'SUCCESS' : 'FAILURE';
diff --git a/tests/users.acl b/tests/users.acl
new file mode 100644
index 0000000000..f3518d3388
--- /dev/null
+++ b/tests/users.acl
@@ -0,0 +1,2 @@
+user default on >phpredis ~* +@all
+user phpredis on >phpredis ~* +@all

From 857a2af49739beb6341350f881e2acf07a360499 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 24 Jun 2020 18:13:54 -0700
Subject: [PATCH 1344/1986] Add liveness check and create pconnect "YOLO" mode.

---
 library.c | 13 +++++++++++++
 redis.c   |  1 +
 2 files changed, 14 insertions(+)

diff --git a/library.c b/library.c
index 209cd1d0a1..7bf273a378 100644
--- a/library.c
+++ b/library.c
@@ -2118,6 +2118,12 @@ static int redis_uniqid(char *buf, size_t buflen) {
                     (long)tv.tv_sec, (long)tv.tv_usec, (long)php_rand());
 }
 
+static int redis_stream_liveness_check(php_stream *stream) {
+    return php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS,
+                                 0, NULL) == PHP_STREAM_OPTION_RETURN_OK ?
+                                 SUCCESS : FAILURE;
+}
+
 static int
 redis_sock_check_liveness(RedisSock *redis_sock)
 {
@@ -2125,6 +2131,13 @@ redis_sock_check_liveness(RedisSock *redis_sock)
     int idlen, auth;
     smart_string cmd = {0};
 
+    /* Short circuit if we detect the stream has gone bad or if the user has
+     * configured persistent connection "YOLO mode". */
+    if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS)
+        return FAILURE;
+    else if (!INI_INT("redis.pconnect.echo_check_liveness"))
+        return SUCCESS;
+
     /* AUTH (if we need it) */
     auth = redis_sock_append_auth(redis_sock, &cmd);
 
diff --git a/redis.c b/redis.c
index b361aab2a8..ad6817524b 100644
--- a/redis.c
+++ b/redis.c
@@ -96,6 +96,7 @@ PHP_INI_BEGIN()
     /* redis pconnect */
     PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL)
+    PHP_INI_ENTRY("redis.pconnect.echo_check_liveness", "1", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.pconnect.pool_pattern", "", PHP_INI_ALL, NULL)
 
     /* redis session */

From f7ed4aabe1cb2ca360f86238b8e7336b73d0236f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 24 Jun 2020 21:46:04 -0700
Subject: [PATCH 1345/1986] Prepare for 5.3.0 RC1 release

---
 Changelog.md | 111 ++++++++++++++++++++++++++---
 package.xml  | 194 ++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 246 insertions(+), 59 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 7b3b3d7dd6..73abb0dcff 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,28 +5,117 @@ All changes to phpredis will be documented in this file.
 We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## [Unreleased]
+## [5.3.0RC1]
 
 ### Sponsors :sparkling_heart:
 
 - [Audiomack.com](https://audiomack.com)
-- [Till Krüss](https://github.com/tillkruss)
+- [BlueHost](https://bluehost.com)
+- [Redis Cache Pro for WordPress](https://wprediscache.com/)
+
+### Added
+
+ - Support for Redis 6 ACLs
+   [a311cc4e](https://github.com/phpredis/phpredis/commit/a311cc4ec3cecdbaf83ba66985efa82137e37cc0)
+   ([Michael Grunder](https://github.com/michael-grunder))
+
+- LZ4 Compression
+  [04def9fb](https://github.com/phpredis/phpredis/commit/04def9fbe2194b3b711362de57260a6cd5216e69)
+  ([Ilia Alshanetsky](https://github.com/iliaal),
+   [Michael Grunder](https://github.com/michael-grunder))
+
+- Support for new Redis 6 arguments (XINFO FULL, SET KEEPTTL)
+  [a0c53e0b](https://github.com/phpredis/phpredis/commit/a0c53e0b30e0c6af15cc137415e7d65f6d1867f7),
+  [f9c7bb57](https://github.com/phpredis/phpredis/commit/f9c7bb5788c39614c23e3bb9ec42ec8d6d5bbaa1)
+  ([Viktor Sekindo](https://github.com/victor ),
+   [Michael Grunder](https://github.com/michael-grunder))
+
+- Support for TLS connections
+  [890ee0e6](https://github.com/phpredis/phpredis/commit/890ee0e656e545b18179cf247db94a33179ce1ab),
+  [b0671296](https://github.com/phpredis/phpredis/commit/b067129678264fc1c5c0f611ce1b192e05c14669)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- New option Redis::SCAN_PREFIX, Redis::SCAN_NOPREFIX
+  [e80600e2](https://github.com/phpredis/phpredis/commit/e80600e244b8442cb7c86e99b067966cd59bf2ee)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Configurable unit test authentication arguments
+  [e37f38a3](https://github.com/phpredis/phpredis/commit/e37f38a39eb4bece8f49ebd0652112dc992084a0),
+  [201a9759](https://github.com/phpredis/phpredis/commit/201a97599953a9621bb8eb02dc8d5f08d16499a3)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), ([Michael Grunder](https://github.com/michael-grunder))
+
+### Fixed
+
+- Improved cluster slot caching mechanism to fix a couple of bugs and make it more efficient.
+  [5ca4141c](https://github.com/phpredis/phpredis/commit/5ca4141c72e23816f146b49877a6a4b8098b34c6)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+- Stop calling Redis constructor when creating a RedisArray
+  [e41e19a8](https://github.com/phpredis/phpredis/commit/e41e19a8342212ee9cfe35f622804c9870d05ec2)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Use ZEND_LONG_FMT instead of system `long`
+  [5bf88124](https://github.com/phpredis/phpredis/commit/5bf881244dd30b5310fcfcaf5bcd8f9e2675bb01)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+- Use long for SCAN iteration to fix potential overflow
+  [f13f9b7c](https://github.com/phpredis/phpredis/commit/f13f9b7c7f5e3a7d286b412541199a408a0a98bd)
+  ([Viktor Sekindo](https://github.com/victor ))
+
+- Fix config.m4 to test for the variable $PHP_REDIS_JSON and not the literal PHP_REDIS_JSON
+  [20a3dc72](https://github.com/phpredis/phpredis/commit/20a3dc7251cb0bf450ef2a1cfeeeaeaa10355cd2)
+  ([Mizuki Nakano](https://github.com/mi-nakano))
+
+- Fix compiler warnings
+  [b9b383f4](https://github.com/phpredis/phpredis/commit/b9b383f49939484dcddf1a5edefdb9d753baa7f8),
+  [215828e](https://github.com/phpredis/phpredis/commit/215828e3474dfd9ea72fdc6da67aa6bee2d95ddf)
+  ([Remi Collet](https://github.com/remicollet), [Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Avoid use-after-free of RediSock
+  [8c45816d](https://github.com/phpredis/phpredis/commit/8c45816dbf4746f6557f83332be874bd78b5ce34)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Fixed ZADD arginfo
+  [a8e2b021](https://github.com/phpredis/phpredis/commit/a8e2b021f9eb51ad3ed0cc89064e2f004c56f8ba)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
 ### Changed
 
-- Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers
-  [b9b383f4](https://github.com/phpredis/phpredis/commit/b9b383f4)
-  ([Remi Collet](https://github.com/remicollet))
+- Store AUTH information in flags RedisSock rather than duplicating information.
+  [58dab564](https://github.com/phpredis/phpredis/commit/58dab5649fcc2cc63f5a29df83f783e154d7fa22)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
-- Make unit test authentication configurable
-  [201a9759](https://github.com/phpredis/phpredis/commit/201a9759)
-  ([Michel Grunder](https://github.com/michael-grunder))
+- Refactor redis_sock_get_connection_pool logic.
+  [73212e1](https://github.com/phpredis/phpredis/commit/73212e141403ec47441142fe1c7fd5fad24f6720)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Updated documentation to show LPUSH and RPUSH are variadic and fixed DEL documentation.
+  [92f8dde1](https://github.com/phpredis/phpredis/commit/92f8dde1c996d4e1c3d79226b888119307612c40)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+- Authenticate in redis_server_sock_open
+  [4ef465b5](https://github.com/phpredis/phpredis/commit/4ef465b57325d2d93234fd66af06a7091ce7d1ea)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Dynamically include json.so in unit tests based on configuration
+  [0ce7ca2f](https://github.com/phpredis/phpredis/commit/0ce7ca2fb1eb2f3c445487957a49b70ad8d4ecb6)
+  (([Michael Grunder](https://github.com/michael-grunder))
 
-- Various small changes in library and cluster_library
-  [73212e14](https://github.com/phpredis/phpredis/commit/73212e14),
-  [460c8f29](https://github.com/phpredis/phpredis/commit/460c8f29)
+- Update save_path logic in Redis Cluster session unit tests
+  [dd66fce](https://github.com/phpredis/phpredis/commit/dd66fceeb232f9e1fb0a26373949e810180dc5fc)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
+ - Refactoring various bits of logic
+   [bbcf32a3](https://github.com/phpredis/phpredis/commit/bbcf32a37fa856ba0b50b489ba05bd3d43800fcc),
+   [a42cf189](https://github.com/phpredis/phpredis/commit/a42cf189a776fc43acf47ca519f1d7385cc27f2f),
+   [460c8f29](https://github.com/phpredis/phpredis/commit/460c8f29239c263e15a093c9bcdb6fb24587ec7d),
+   [b7f9df75](https://github.com/phpredis/phpredis/commit/b7f9df758b30187864012d5cd831dbbc5fa053d0),
+   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+- Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers
+  [b9b383f4](https://github.com/phpredis/phpredis/commit/b9b383f4)
+  ([Remi Collet](https://github.com/remicollet))
+
 - PHP 8 compatibility
   [9ee94ca4](https://github.com/phpredis/phpredis/commit/9ee94ca4),
   [7e4c7b3e](https://github.com/phpredis/phpredis/commit/7e4c7b3e)
diff --git a/package.xml b/package.xml
index 190567f866..2665f566a3 100644
--- a/package.xml
+++ b/package.xml
@@ -27,65 +27,75 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2020-03-02
+ 2020-06-24
  
-  5.2.0
-  5.2.0
+  5.3.0RC1
+  5.3.0RC1
  
  
-  stable
-  stable
+  alpha
+  alpha
  
  PHP
  
-    phpredis 5.2.0
-
-    - There were no changes between 5.2.0RC2 and 5.2.0.
-
-    phpredis 5.2.0RC2
+    phpredis 5.3.0RC1
 
-    * Include RedisSentinelTest.php in package.xml! [eddbfc8f] (Michael Grunder)
-    * Fix -Wmaybe-uninitialized warning [740b8c87] (Remi Collet)
-    * Fix improper destructor when zipping values and scores [371ae7ae]
-      (Michael Grunder)
-    * Use php_rand instead of php_mt_rand for liveness challenge string
-      [9ef2ed89] (Michael Grunder)
-
-    phpredis 5.2.0RC1
-
-    This release contains initial support for Redis Sentinel as well as many
-    smaller bug fixes and improvements.  It is especially of interest if you
-    use persistent connections, as we've added logic to make sure they are in
-    a good state when retreving them from the pool.
+    This release adds the first round of support for Redis 6 functionality including,
+    most importantly ACLs.  Other Redis 6 functionality is included as well such as
+    KEEPTTL and XINFO FULL command support.
 
-    IMPORTANT: Sentinel support is considered experimental and the API
-               will likely change based on user feedback.
+    Aside from the Redis 6 functionality this releasae contains many bugfixes and
+    improvements.
 
     * Sponsors
-      ~ Audiomack.com - https://audiomack.com
-      ~ Till Kruss - https://github.com/tillkruss
+     ~ Audiomack.com - https://audiomack.com
+     ~ BlueHost.com - https://bluehost.com
+     ~ Redis Cache Pro for WordPress - https://wprediscache.com/
 
     ---
 
-    * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4,
-      383779ed] (Pavlo Yatsukhnenko)
-
-    * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d,
-      0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre,
-      Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre)
-
-    * Fix for ASK redirections [ba73fbee] (Michael Grunder)
-    * Create specific 'test skipped' exception [c3d83d44] (Michael Grunder)
-    * Fixed memory leaks in RedisCluster [a107c9fc] (Michael Grunder)
-    * Fixes for session lifetime values that underflow or overflow  [7a79ad9c,
-      3c48a332] (Michael Grunder)
-    * Enables slot caching for Redis Cluster [23b1a9d8] (Michael Booth)
-
-    * Support TYPE argument for SCAN [8eb39a26, b1724b84, 53fb36c9, 544e641b]
+    * Support for Redis 6 ACLs [a311cc4e] (Michael Grunder)
+    * LZ4 Compression [04def9fb] (Ilia Alshanetsky)
+    * Support for new Redis 6 arguments (XINFO FULL, SET KEEPTTL) [a0c53e0b,
+      f9c7bb57] (Michael Grunder, Viktor Sekindo)
+    * Support for TLS connections [890ee0e6, b0671296] (Pavlo Yatsukhnenko)
+    * New option Redis::SCAN_PREFIX, Redis::SCAN_NOPREFIX [e80600e2] (Pavlo
+      Yatsukhnenko)
+    * Configurable unit test authentication arguments [e37f38a3, 201a9759]
+      (Pavlo Yatsukhnenko, Michael Grunder)
+    * Improved cluster slot caching mechanism to fix a couple of bugs and make
+      it more efficient. [5ca4141c] (Michael Grunder)
+    * Stop calling Redis constructor when creating a RedisArray [e41e19a8]
       (Pavlo Yatsukhnenko)
-
-    * Added challenge/response mechanism for persistent connections [a5f95925,
-      25cdaee6, 7b6072e0, 99ebd0cc, 3243f426] (Pavlo Yatsukhnenko, Michael Grunder)
+    * Use ZEND_LONG_FMT instead of system `long` [5bf88124] (Michael Grunder)
+    * Use long for SCAN iteration to fix potential overflow [f13f9b7c]
+      (Viktor Sekindo)
+    * Fix config.m4 to test for the variable $PHP_REDIS_JSON and not the
+      literal PHP_REDIS_JSON [20a3dc72] (Mizuki Nakano)
+    * Fix compiler warnings [b9b383f4, 215828e] (Remi Collet),
+      Pavlo Yatsukhnenko)
+    * Avoid use-after-free of RediSock [8c45816d] (Pavlo Yatsukhnenko)
+    * Fixed ZADD arginfo [a8e2b021] (Pavlo Yatsukhnenko)
+    * Store AUTH information in flags RedisSock rather than duplicating
+      information. [58dab564] (Pavlo Yatsukhnenko)
+    * Refactor redis_sock_get_connection_pool logic. [73212e1]
+      (Pavlo Yatsukhnenko)
+    * Updated documentation to show LPUSH and RPUSH are variadic and fixed DEL
+      documentation. [92f8dde1] (Michael Grunder)
+    * Authenticate in redis_server_sock_open [4ef465b5] (Pavlo Yatsukhnenko)
+    * Dynamically include json.so in unit tests based on configuration
+      [0ce7ca2f] (Michael Grunder)
+    * Update save_path logic in Redis Cluster session unit tests [dd66fce]
+      (Pavlo Yatsukhnenko)
+    * Refactoring various bits of logic [bbcf32a3, a42cf189, 460c8f29,
+      b7f9df75] (Pavlo Yatsukhnenko)
+    * Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers
+      [b9b383f4](Remi Collet)
+    * PHP 8 compatibility [9ee94ca4, 7e4c7b3e] (Pavlo Yatsukhnenko)
+    * Refactor PHPREDIS_GET_OBJECT macro [d5dadaf6, 190c0d34]
+      (Pavlo Yatsukhnenko)
+    * Fix documentation showing lPush and rPush are variadic [6808cd6a]
+      (Michael Grunder)
  
  
   
@@ -162,10 +172,98 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
  
   
-   alphaalpha
-   5.2.0RC25.2.0RC2
-   2020-02-21
+   stablestable
+   5.2.25.2.2
+   2020-05-05
+   
+    phpredis 5.2.2
+
+    This is a bugfix release that contains a fix for authentication
+    when using persistent connections, and an option to make the
+    ECHO challenge response logic optional.
+
+    * Inexpensive liveness check, and making ECHO optional [56898f81] (Pavlo Yatsukhnenko)
+    * Move `AUTH` to `redis_sock_server_open` [80f2529b](Pavlo Yatsukhnenko)
+
+    * Sponsors
+      ~ Audiomack.com - https://audiomack.com
+      ~ Till Kruss - https://github.com/tillkruss
+   
+  
+  
+   stablestable
+   5.2.15.2.1
+   2020-03-19
+   
+    phpredis 5.2.1
+
+    This is a bugfix release that fixes `redis->zAdd` arginfo as well as a
+    segfault when closing persistent connections.
+
+    * Fix arginfo for Redis::zadd [a8e2b021] (Pavlo Yatsukhnenko)
+    * Fix segfault on closing persistent stream [b7f9df75] (Pavlo Yatsukhnenko)
+
+    * Sponsors
+      ~ Audiomack.com - https://audiomack.com
+      ~ Till Kruss - https://github.com/tillkruss
+   
+  
+
+
+
+  
+   stablestable
+   5.2.05.2.0
+   2020-03-02
    
+    phpredis 5.2.0
+
+    - There were no changes between 5.2.0RC2 and 5.2.0.
+
+    phpredis 5.2.0RC2
+
+    * Include RedisSentinelTest.php in package.xml! [eddbfc8f] (Michael Grunder)
+    * Fix -Wmaybe-uninitialized warning [740b8c87] (Remi Collet)
+    * Fix improper destructor when zipping values and scores [371ae7ae]
+      (Michael Grunder)
+    * Use php_rand instead of php_mt_rand for liveness challenge string
+      [9ef2ed89] (Michael Grunder)
+
+    phpredis 5.2.0RC1
+
+    This release contains initial support for Redis Sentinel as well as many
+    smaller bug fixes and improvements.  It is especially of interest if you
+    use persistent connections, as we've added logic to make sure they are in
+    a good state when retreving them from the pool.
+
+    IMPORTANT: Sentinel support is considered experimental and the API
+               will likely change based on user feedback.
+
+    * Sponsors
+      ~ Audiomack.com - https://audiomack.com
+      ~ Till Kruss - https://github.com/tillkruss
+
+    ---
+
+    * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4,
+      383779ed] (Pavlo Yatsukhnenko)
+
+    * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d,
+      0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre,
+      Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre)
+
+    * Fix for ASK redirections [ba73fbee] (Michael Grunder)
+    * Create specific 'test skipped' exception [c3d83d44] (Michael Grunder)
+    * Fixed memory leaks in RedisCluster [a107c9fc] (Michael Grunder)
+    * Fixes for session lifetime values that underflow or overflow  [7a79ad9c,
+      3c48a332] (Michael Grunder)
+    * Enables slot caching for Redis Cluster [23b1a9d8] (Michael Booth)
+
+    * Support TYPE argument for SCAN [8eb39a26, b1724b84, 53fb36c9, 544e641b]
+      (Pavlo Yatsukhnenko)
+
+    * Added challenge/response mechanism for persistent connections [a5f95925,
+      25cdaee6, 7b6072e0, 99ebd0cc, 3243f426] (Pavlo Yatsukhnenko, Michael Grunder)
     phpredis 5.2.0RC2
 
     * Include RedisSentinelTest.php in package.xml! [eddbfc8f] (Michael Grunder)

From dd3a11a8f7eba80c413bec5b8f4cdc60d9f54071 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 25 Jun 2020 09:56:09 -0700
Subject: [PATCH 1346/1986] Finalize 5.3.0RC changelog/package.xml

---
 Changelog.md    | 26 ++++++++++++++------------
 README.markdown |  5 +----
 package.xml     | 13 +++++++------
 php_redis.h     |  2 +-
 4 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 73abb0dcff..3ed2d0a947 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -12,12 +12,13 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - [Audiomack.com](https://audiomack.com)
 - [BlueHost](https://bluehost.com)
 - [Redis Cache Pro for WordPress](https://wprediscache.com/)
+- [Avtandil Kikabidze](https://github.com/akalongman)
 
 ### Added
 
- - Support for Redis 6 ACLs
-   [a311cc4e](https://github.com/phpredis/phpredis/commit/a311cc4ec3cecdbaf83ba66985efa82137e37cc0)
-   ([Michael Grunder](https://github.com/michael-grunder))
+- Support for Redis 6 ACLs
+  [a311cc4e](https://github.com/phpredis/phpredis/commit/a311cc4ec3cecdbaf83ba66985efa82137e37cc0)
+  ([Michael Grunder](https://github.com/michael-grunder))
 
 - LZ4 Compression
   [04def9fb](https://github.com/phpredis/phpredis/commit/04def9fbe2194b3b711362de57260a6cd5216e69)
@@ -27,7 +28,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - Support for new Redis 6 arguments (XINFO FULL, SET KEEPTTL)
   [a0c53e0b](https://github.com/phpredis/phpredis/commit/a0c53e0b30e0c6af15cc137415e7d65f6d1867f7),
   [f9c7bb57](https://github.com/phpredis/phpredis/commit/f9c7bb5788c39614c23e3bb9ec42ec8d6d5bbaa1)
-  ([Viktor Sekindo](https://github.com/victor ),
+  ([Victor Kislov](https://github.com/vityank),
    [Michael Grunder](https://github.com/michael-grunder))
 
 - Support for TLS connections
@@ -42,7 +43,8 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - Configurable unit test authentication arguments
   [e37f38a3](https://github.com/phpredis/phpredis/commit/e37f38a39eb4bece8f49ebd0652112dc992084a0),
   [201a9759](https://github.com/phpredis/phpredis/commit/201a97599953a9621bb8eb02dc8d5f08d16499a3)
-  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko), ([Michael Grunder](https://github.com/michael-grunder))
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko),
+   [Michael Grunder](https://github.com/michael-grunder))
 
 ### Fixed
 
@@ -60,7 +62,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 - Use long for SCAN iteration to fix potential overflow
   [f13f9b7c](https://github.com/phpredis/phpredis/commit/f13f9b7c7f5e3a7d286b412541199a408a0a98bd)
-  ([Viktor Sekindo](https://github.com/victor ))
+  ([Victor Kislov](https://github.com/vityank))
 
 - Fix config.m4 to test for the variable $PHP_REDIS_JSON and not the literal PHP_REDIS_JSON
   [20a3dc72](https://github.com/phpredis/phpredis/commit/20a3dc7251cb0bf450ef2a1cfeeeaeaa10355cd2)
@@ -105,12 +107,12 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
   [dd66fce](https://github.com/phpredis/phpredis/commit/dd66fceeb232f9e1fb0a26373949e810180dc5fc)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
- - Refactoring various bits of logic
-   [bbcf32a3](https://github.com/phpredis/phpredis/commit/bbcf32a37fa856ba0b50b489ba05bd3d43800fcc),
-   [a42cf189](https://github.com/phpredis/phpredis/commit/a42cf189a776fc43acf47ca519f1d7385cc27f2f),
-   [460c8f29](https://github.com/phpredis/phpredis/commit/460c8f29239c263e15a093c9bcdb6fb24587ec7d),
-   [b7f9df75](https://github.com/phpredis/phpredis/commit/b7f9df758b30187864012d5cd831dbbc5fa053d0),
-   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Refactoring various bits of logic
+  [bbcf32a3](https://github.com/phpredis/phpredis/commit/bbcf32a37fa856ba0b50b489ba05bd3d43800fcc),
+  [a42cf189](https://github.com/phpredis/phpredis/commit/a42cf189a776fc43acf47ca519f1d7385cc27f2f),
+  [460c8f29](https://github.com/phpredis/phpredis/commit/460c8f29239c263e15a093c9bcdb6fb24587ec7d),
+  [b7f9df75](https://github.com/phpredis/phpredis/commit/b7f9df758b30187864012d5cd831dbbc5fa053d0),
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
 - Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers
   [b9b383f4](https://github.com/phpredis/phpredis/commit/b9b383f4)
diff --git a/README.markdown b/README.markdown
index 0cf5b97d5b..f8dd933c8c 100644
--- a/README.markdown
+++ b/README.markdown
@@ -20,10 +20,7 @@ You can also make a one-time contribution with one of the links below.
 [![Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)
 
 ## Sponsors
-
-
-    Audiomack.com
-
+Audiomack.comBluehost.com
 
 # Table of contents
 -----
diff --git a/package.xml b/package.xml
index 2665f566a3..6cbf4d415f 100644
--- a/package.xml
+++ b/package.xml
@@ -27,7 +27,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2020-06-24
+ 2020-06-25
  
   5.3.0RC1
   5.3.0RC1
@@ -48,16 +48,17 @@ http://pear.php.net/dtd/package-2.0.xsd">
     improvements.
 
     * Sponsors
-     ~ Audiomack.com - https://audiomack.com
-     ~ BlueHost.com - https://bluehost.com
-     ~ Redis Cache Pro for WordPress - https://wprediscache.com/
+      ~ Audiomack.com - https://audiomack.com
+      ~ BlueHost.com - https://bluehost.com
+      ~ Redis Cache Pro for WordPress - https://wprediscache.com/
+      ~ Avtandil Kikabidze - https://github.com/akalongman
 
     ---
 
     * Support for Redis 6 ACLs [a311cc4e] (Michael Grunder)
     * LZ4 Compression [04def9fb] (Ilia Alshanetsky)
     * Support for new Redis 6 arguments (XINFO FULL, SET KEEPTTL) [a0c53e0b,
-      f9c7bb57] (Michael Grunder, Viktor Sekindo)
+      f9c7bb57] (Michael Grunder, Victor Kislov)
     * Support for TLS connections [890ee0e6, b0671296] (Pavlo Yatsukhnenko)
     * New option Redis::SCAN_PREFIX, Redis::SCAN_NOPREFIX [e80600e2] (Pavlo
       Yatsukhnenko)
@@ -69,7 +70,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
       (Pavlo Yatsukhnenko)
     * Use ZEND_LONG_FMT instead of system `long` [5bf88124] (Michael Grunder)
     * Use long for SCAN iteration to fix potential overflow [f13f9b7c]
-      (Viktor Sekindo)
+      (Victor Kislov)
     * Fix config.m4 to test for the variable $PHP_REDIS_JSON and not the
       literal PHP_REDIS_JSON [20a3dc72] (Mizuki Nakano)
     * Fix compiler warnings [b9b383f4, 215828e] (Remi Collet),
diff --git a/php_redis.h b/php_redis.h
index a4ff9f4b06..1666c5979a 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -23,7 +23,7 @@
 #define PHP_REDIS_H
 
 /* phpredis version */
-#define PHP_REDIS_VERSION "5.2.0"
+#define PHP_REDIS_VERSION "5.3.0RC1"
 
 PHP_METHOD(Redis, __construct);
 PHP_METHOD(Redis, __destruct);

From 0838b5bde7ef25d419868c7e705bf6c70d68ea20 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 25 Jun 2020 16:22:45 -0700
Subject: [PATCH 1347/1986] Make sure we NULL terminate our pool ID

---
 library.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library.c b/library.c
index 7bf273a378..9b58c53d2f 100644
--- a/library.c
+++ b/library.c
@@ -808,6 +808,7 @@ redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...) {
         fmt++;
     }
 
+    smart_str_0(&str);
     return str.s;
 }
 

From 19269cb1db073376364d2655fc828daeb8f1006e Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Fri, 26 Jun 2020 07:18:57 +0200
Subject: [PATCH 1348/1986] fix lz4 library name

---
 config.m4 | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/config.m4 b/config.m4
index 30449300ba..21058019e8 100644
--- a/config.m4
+++ b/config.m4
@@ -201,7 +201,8 @@ if test "$PHP_REDIS" != "no"; then
   fi
 
   if test "$PHP_REDIS_LZ4" != "no"; then
-      AC_DEFINE(HAVE_REDIS_LZ4, 1, [ ])
+    AC_DEFINE(HAVE_REDIS_LZ4, 1, [ ])
+    if test "$PHP_LIBZSTD" != "no"; then
       AC_MSG_CHECKING(for liblz4 files in default path)
       for i in $PHP_LIBLZ4 /usr/local /usr; do
         if test -r $i/include/lz4.h; then
@@ -216,13 +217,16 @@ if test "$PHP_REDIS" != "no"; then
       fi
       PHP_CHECK_LIBRARY(lz4, LZ4_compress,
       [
-        PHP_ADD_LIBRARY_WITH_PATH(zstd, $LIBLZ4_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD)
+        PHP_ADD_LIBRARY_WITH_PATH(lz4, $LIBLZ4_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD)
       ], [
         AC_MSG_ERROR([could not find usable liblz4])
       ], [
         -L$LIBLZ4_DIR/$PHP_LIBDIR
       ])
       PHP_SUBST(REDIS_SHARED_LIBADD)
+    else
+      AC_MSG_ERROR([only system libz4 is supported])
+    fi
   fi
 
   if test "$PHP_REDIS_ZSTD" != "no"; then

From e5de8fa1ffa4824944283d78e9475df527567a49 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Fri, 26 Jun 2020 07:21:21 +0200
Subject: [PATCH 1349/1986] copy/paste err

---
 config.m4 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config.m4 b/config.m4
index 21058019e8..4445400276 100644
--- a/config.m4
+++ b/config.m4
@@ -202,7 +202,7 @@ if test "$PHP_REDIS" != "no"; then
 
   if test "$PHP_REDIS_LZ4" != "no"; then
     AC_DEFINE(HAVE_REDIS_LZ4, 1, [ ])
-    if test "$PHP_LIBZSTD" != "no"; then
+    if test "$PHP_LIBLZ4" != "no"; then
       AC_MSG_CHECKING(for liblz4 files in default path)
       for i in $PHP_LIBLZ4 /usr/local /usr; do
         if test -r $i/include/lz4.h; then

From f57fae532d2da85f23319d2172e88b4a5e7ba736 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 4 Jun 2020 16:58:28 +0200
Subject: [PATCH 1350/1986] use pkg-config for libzstd and liblzf

---
 config.m4 | 74 +++++++++++++++++++++++--------------------------------
 1 file changed, 31 insertions(+), 43 deletions(-)

diff --git a/config.m4 b/config.m4
index 4445400276..419996568a 100644
--- a/config.m4
+++ b/config.m4
@@ -168,9 +168,22 @@ if test "$PHP_REDIS" != "no"; then
     MSGPACK_INCLUDES=""
   fi
 
+  AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+
   if test "$PHP_REDIS_LZF" != "no"; then
     AC_DEFINE(HAVE_REDIS_LZF, 1, [ ])
-    if test "$PHP_LIBLZF" != "no"; then
+
+    if test "$PHP_LIBLZF" == "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists liblzf; then
+      AC_MSG_CHECKING(for liblzf using pkg-config)
+
+      LIBLZF_INC=`$PKG_CONFIG liblzf --cflags`
+      LIBLZF_LIB=`$PKG_CONFIG liblzf --libs`
+      LIBLZF_VER=`$PKG_CONFIG liblzf --modversion`
+      AC_MSG_RESULT(found version $LIBLZF_VER)
+      PHP_EVAL_LIBLINE($LIBLZF_LIB, REDIS_SHARED_LIBADD)
+      PHP_EVAL_INCLINE($LIBLZF_INC)
+
+    elif test "$PHP_LIBLZF" != "no"; then
       AC_MSG_CHECKING(for liblzf files in default path)
       for i in $PHP_LIBLZF /usr/local /usr; do
         if test -r $i/include/lzf.h; then
@@ -191,7 +204,6 @@ if test "$PHP_REDIS" != "no"; then
       ], [
         -L$LIBLZF_DIR/$PHP_LIBDIR
       ])
-      PHP_SUBST(REDIS_SHARED_LIBADD)
     else
       PHP_ADD_INCLUDE(liblzf)
       PHP_ADD_INCLUDE($srcdir/liblzf)
@@ -231,7 +243,22 @@ if test "$PHP_REDIS" != "no"; then
 
   if test "$PHP_REDIS_ZSTD" != "no"; then
     AC_DEFINE(HAVE_REDIS_ZSTD, 1, [ ])
-    if test "$PHP_LIBZSTD" != "no"; then
+
+    if test "$PHP_LIBZSTD" == "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libzstd; then
+      AC_MSG_CHECKING(for libzstd using pkg-config)
+
+      LIBZSTD_VER=`$PKG_CONFIG libzstd --modversion`
+      if $PKG_CONFIG libzstd --atleast-version 1.3.0; then
+        LIBZSTD_INC=`$PKG_CONFIG libzstd --cflags`
+        LIBZSTD_LIB=`$PKG_CONFIG libzstd --libs`
+        AC_MSG_RESULT(found version $LIBZSTD_VER)
+        PHP_EVAL_LIBLINE($LIBZSTD_LIB, REDIS_SHARED_LIBADD)
+        PHP_EVAL_INCLINE($LIBZSTD_INC)
+      else
+        AC_MSG_ERROR([found version $LIBZSTD_VER, version 1.3.0 required])
+      fi
+
+    elif test "$PHP_LIBZSTD" != "no"; then
       AC_MSG_CHECKING(for libzstd files in default path)
       for i in $PHP_LIBZSTD /usr/local /usr; do
         if test -r $i/include/zstd.h; then
@@ -252,7 +279,6 @@ if test "$PHP_REDIS" != "no"; then
       ], [
         -L$LIBZSTD_DIR/$PHP_LIBDIR
       ])
-      PHP_SUBST(REDIS_SHARED_LIBADD)
     else
       AC_MSG_ERROR([only system libzstd is supported])
     fi
@@ -263,44 +289,6 @@ if test "$PHP_REDIS" != "no"; then
     AC_DEFINE_UNQUOTED(GIT_REVISION, ["$(git log -1 --format=%H)"], [ ])
   fi
 
-  dnl # --with-redis -> check with-path
-  dnl SEARCH_PATH="/usr/local /usr"     # you might want to change this
-  dnl SEARCH_FOR="/include/redis.h"  # you most likely want to change this
-  dnl if test -r $PHP_REDIS/$SEARCH_FOR; then # path given as parameter
-  dnl   REDIS_DIR=$PHP_REDIS
-  dnl else # search default path list
-  dnl   AC_MSG_CHECKING([for redis files in default path])
-  dnl   for i in $SEARCH_PATH ; do
-  dnl     if test -r $i/$SEARCH_FOR; then
-  dnl       REDIS_DIR=$i
-  dnl       AC_MSG_RESULT(found in $i)
-  dnl     fi
-  dnl   done
-  dnl fi
-  dnl
-  dnl if test -z "$REDIS_DIR"; then
-  dnl   AC_MSG_RESULT([not found])
-  dnl   AC_MSG_ERROR([Please reinstall the redis distribution])
-  dnl fi
-
-  dnl # --with-redis -> add include path
-  dnl PHP_ADD_INCLUDE($REDIS_DIR/include)
-
-  dnl # --with-redis -> check for lib and symbol presence
-  dnl LIBNAME=redis # you may want to change this
-  dnl LIBSYMBOL=redis # you most likely want to change this 
-
-  dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
-  dnl [
-  dnl   PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $REDIS_DIR/lib, REDIS_SHARED_LIBADD)
-  dnl   AC_DEFINE(HAVE_REDISLIB,1,[ ])
-  dnl ],[
-  dnl   AC_MSG_ERROR([wrong redis lib version or lib not found])
-  dnl ],[
-  dnl   -L$REDIS_DIR/lib -lm -ldl
-  dnl ])
-  dnl
-  dnl PHP_SUBST(REDIS_SHARED_LIBADD)
-
+  PHP_SUBST(REDIS_SHARED_LIBADD)
   PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c $lzf_sources, $ext_shared)
 fi

From f74207b092c62d6b4218cdb071ce35d9734c5cff Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 4 Jun 2020 17:07:39 +0200
Subject: [PATCH 1351/1986] fix test syntax

---
 config.m4 | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/config.m4 b/config.m4
index 419996568a..715d5d1dd6 100644
--- a/config.m4
+++ b/config.m4
@@ -173,7 +173,7 @@ if test "$PHP_REDIS" != "no"; then
   if test "$PHP_REDIS_LZF" != "no"; then
     AC_DEFINE(HAVE_REDIS_LZF, 1, [ ])
 
-    if test "$PHP_LIBLZF" == "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists liblzf; then
+    if test "$PHP_LIBLZF" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists liblzf; then
       AC_MSG_CHECKING(for liblzf using pkg-config)
 
       LIBLZF_INC=`$PKG_CONFIG liblzf --cflags`
@@ -244,7 +244,7 @@ if test "$PHP_REDIS" != "no"; then
   if test "$PHP_REDIS_ZSTD" != "no"; then
     AC_DEFINE(HAVE_REDIS_ZSTD, 1, [ ])
 
-    if test "$PHP_LIBZSTD" == "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libzstd; then
+    if test "$PHP_LIBZSTD" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libzstd; then
       AC_MSG_CHECKING(for libzstd using pkg-config)
 
       LIBZSTD_VER=`$PKG_CONFIG libzstd --modversion`
@@ -285,7 +285,7 @@ if test "$PHP_REDIS" != "no"; then
   fi
 
   AC_CHECK_PROG([GIT], [git], [yes], [no])
-  if test "$GIT" == "yes" && test -d "$srcdir/.git"; then
+  if test "$GIT" = "yes" && test -d "$srcdir/.git"; then
     AC_DEFINE_UNQUOTED(GIT_REVISION, ["$(git log -1 --format=%H)"], [ ])
   fi
 

From a88186c293acbb6620398a321ec676a50741d52c Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Fri, 26 Jun 2020 07:37:49 +0200
Subject: [PATCH 1352/1986] use pkg-config for liblz4

---
 config.m4 | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/config.m4 b/config.m4
index 715d5d1dd6..72d3c219c2 100644
--- a/config.m4
+++ b/config.m4
@@ -214,7 +214,18 @@ if test "$PHP_REDIS" != "no"; then
 
   if test "$PHP_REDIS_LZ4" != "no"; then
     AC_DEFINE(HAVE_REDIS_LZ4, 1, [ ])
-    if test "$PHP_LIBLZ4" != "no"; then
+
+    if test "$PHP_LIBLZ4" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists liblz4; then
+      AC_MSG_CHECKING(for liblz4 using pkg-config)
+
+      LIBLZ4_VER=`$PKG_CONFIG liblz4 --modversion`
+      LIBLZ4_INC=`$PKG_CONFIG liblz4 --cflags`
+      LIBLZ4_LIB=`$PKG_CONFIG liblz4 --libs`
+      AC_MSG_RESULT(found version $LIBLZ4_VER)
+      PHP_EVAL_LIBLINE($LIBLZ4_LIB, REDIS_SHARED_LIBADD)
+      PHP_EVAL_INCLINE($LIBLZ4_INC)
+
+    elif test "$PHP_LIBLZ4" != "no"; then
       AC_MSG_CHECKING(for liblz4 files in default path)
       for i in $PHP_LIBLZ4 /usr/local /usr; do
         if test -r $i/include/lz4.h; then

From 262cc70e9e4adac13dc4956aef7374e7520fd5d2 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 26 Jun 2020 08:36:16 -0700
Subject: [PATCH 1353/1986] Update documentation with new feature info

---
 README.markdown | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/README.markdown b/README.markdown
index f8dd933c8c..40377223a0 100644
--- a/README.markdown
+++ b/README.markdown
@@ -218,6 +218,9 @@ $redis->connect('tls://127.0.0.1'); // enable transport level security, port 637
 $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
 $redis->connect('/tmp/redis.sock'); // unix domain socket.
 $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts.
+
+/* With PhpRedis >= 5.3.0 you can specify authentication information on connect */
+$redis->connect('127.0.0.1', 6379, 1, NULL, 0, 0, ['auth' => ['phpredis', 'phpredis']]);
 ~~~
 
 **Note:** `open` is an alias for `connect` and will be removed in future versions of phpredis.
@@ -288,6 +291,10 @@ $redis->auth(['phpredis', 'haxx00r']);
 
 /* Authenticate with the password 'foobared' */
 $redis->auth(['foobared']);
+
+/* You can also use an associative array specifying user and pass */
+$redis->auth(['user' => 'phpredis', 'pass' => 'phpredis]);
+$redis->auth(['pass' => 'phpredis']);
 ~~~
 
 ### select
@@ -4324,10 +4331,10 @@ using a persistent ID, and FALSE if we're not connected
 
 ### getAuth
 -----
-_**Description**_:  Get the password used to authenticate the phpredis connection
+_**Description**_:  Get the password (or username and password if using Redis 6 ACLs) used to authenticate the connection.
 
 ### *Parameters*
 None
 
 ### *Return value*
-*Mixed*  Returns the password used to authenticate a phpredis session or NULL if none was used, and FALSE if we're not connected
+*Mixed*  Returns NULL if no username/password are set, the password string if a password is set, and a `[username, password]` array if authenticated with a username and password.

From 57bb95bf5a01a2adb74e2bf73bb285488e0d1586 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 26 Jun 2020 08:36:30 -0700
Subject: [PATCH 1354/1986] Also NULL terminate if format is NULL

---
 library.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/library.c b/library.c
index 9b58c53d2f..e60f35a5b0 100644
--- a/library.c
+++ b/library.c
@@ -777,8 +777,10 @@ redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...) {
     smart_str_append_long(&str, (zend_long)redis_sock->port);
 
     /* Short circuit if we don't have a pattern */
-    if (fmt == NULL)
+    if (fmt == NULL) {
+        smart_str_0(&str);
         return str.s;
+    }
 
     while (*fmt) {
         switch (*fmt) {

From df398cb07cd10d870c6805d5834703dc39590b0f Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Fri, 26 Jun 2020 07:18:57 +0200
Subject: [PATCH 1355/1986] Fix config.m4 errors and user pkg-config when
 possible

---
 config.m4 | 95 ++++++++++++++++++++++++++++---------------------------
 1 file changed, 49 insertions(+), 46 deletions(-)

diff --git a/config.m4 b/config.m4
index 30449300ba..72d3c219c2 100644
--- a/config.m4
+++ b/config.m4
@@ -168,9 +168,22 @@ if test "$PHP_REDIS" != "no"; then
     MSGPACK_INCLUDES=""
   fi
 
+  AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+
   if test "$PHP_REDIS_LZF" != "no"; then
     AC_DEFINE(HAVE_REDIS_LZF, 1, [ ])
-    if test "$PHP_LIBLZF" != "no"; then
+
+    if test "$PHP_LIBLZF" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists liblzf; then
+      AC_MSG_CHECKING(for liblzf using pkg-config)
+
+      LIBLZF_INC=`$PKG_CONFIG liblzf --cflags`
+      LIBLZF_LIB=`$PKG_CONFIG liblzf --libs`
+      LIBLZF_VER=`$PKG_CONFIG liblzf --modversion`
+      AC_MSG_RESULT(found version $LIBLZF_VER)
+      PHP_EVAL_LIBLINE($LIBLZF_LIB, REDIS_SHARED_LIBADD)
+      PHP_EVAL_INCLINE($LIBLZF_INC)
+
+    elif test "$PHP_LIBLZF" != "no"; then
       AC_MSG_CHECKING(for liblzf files in default path)
       for i in $PHP_LIBLZF /usr/local /usr; do
         if test -r $i/include/lzf.h; then
@@ -191,7 +204,6 @@ if test "$PHP_REDIS" != "no"; then
       ], [
         -L$LIBLZF_DIR/$PHP_LIBDIR
       ])
-      PHP_SUBST(REDIS_SHARED_LIBADD)
     else
       PHP_ADD_INCLUDE(liblzf)
       PHP_ADD_INCLUDE($srcdir/liblzf)
@@ -201,7 +213,19 @@ if test "$PHP_REDIS" != "no"; then
   fi
 
   if test "$PHP_REDIS_LZ4" != "no"; then
-      AC_DEFINE(HAVE_REDIS_LZ4, 1, [ ])
+    AC_DEFINE(HAVE_REDIS_LZ4, 1, [ ])
+
+    if test "$PHP_LIBLZ4" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists liblz4; then
+      AC_MSG_CHECKING(for liblz4 using pkg-config)
+
+      LIBLZ4_VER=`$PKG_CONFIG liblz4 --modversion`
+      LIBLZ4_INC=`$PKG_CONFIG liblz4 --cflags`
+      LIBLZ4_LIB=`$PKG_CONFIG liblz4 --libs`
+      AC_MSG_RESULT(found version $LIBLZ4_VER)
+      PHP_EVAL_LIBLINE($LIBLZ4_LIB, REDIS_SHARED_LIBADD)
+      PHP_EVAL_INCLINE($LIBLZ4_INC)
+
+    elif test "$PHP_LIBLZ4" != "no"; then
       AC_MSG_CHECKING(for liblz4 files in default path)
       for i in $PHP_LIBLZ4 /usr/local /usr; do
         if test -r $i/include/lz4.h; then
@@ -216,18 +240,36 @@ if test "$PHP_REDIS" != "no"; then
       fi
       PHP_CHECK_LIBRARY(lz4, LZ4_compress,
       [
-        PHP_ADD_LIBRARY_WITH_PATH(zstd, $LIBLZ4_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD)
+        PHP_ADD_LIBRARY_WITH_PATH(lz4, $LIBLZ4_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD)
       ], [
         AC_MSG_ERROR([could not find usable liblz4])
       ], [
         -L$LIBLZ4_DIR/$PHP_LIBDIR
       ])
       PHP_SUBST(REDIS_SHARED_LIBADD)
+    else
+      AC_MSG_ERROR([only system libz4 is supported])
+    fi
   fi
 
   if test "$PHP_REDIS_ZSTD" != "no"; then
     AC_DEFINE(HAVE_REDIS_ZSTD, 1, [ ])
-    if test "$PHP_LIBZSTD" != "no"; then
+
+    if test "$PHP_LIBZSTD" = "yes" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libzstd; then
+      AC_MSG_CHECKING(for libzstd using pkg-config)
+
+      LIBZSTD_VER=`$PKG_CONFIG libzstd --modversion`
+      if $PKG_CONFIG libzstd --atleast-version 1.3.0; then
+        LIBZSTD_INC=`$PKG_CONFIG libzstd --cflags`
+        LIBZSTD_LIB=`$PKG_CONFIG libzstd --libs`
+        AC_MSG_RESULT(found version $LIBZSTD_VER)
+        PHP_EVAL_LIBLINE($LIBZSTD_LIB, REDIS_SHARED_LIBADD)
+        PHP_EVAL_INCLINE($LIBZSTD_INC)
+      else
+        AC_MSG_ERROR([found version $LIBZSTD_VER, version 1.3.0 required])
+      fi
+
+    elif test "$PHP_LIBZSTD" != "no"; then
       AC_MSG_CHECKING(for libzstd files in default path)
       for i in $PHP_LIBZSTD /usr/local /usr; do
         if test -r $i/include/zstd.h; then
@@ -248,55 +290,16 @@ if test "$PHP_REDIS" != "no"; then
       ], [
         -L$LIBZSTD_DIR/$PHP_LIBDIR
       ])
-      PHP_SUBST(REDIS_SHARED_LIBADD)
     else
       AC_MSG_ERROR([only system libzstd is supported])
     fi
   fi
 
   AC_CHECK_PROG([GIT], [git], [yes], [no])
-  if test "$GIT" == "yes" && test -d "$srcdir/.git"; then
+  if test "$GIT" = "yes" && test -d "$srcdir/.git"; then
     AC_DEFINE_UNQUOTED(GIT_REVISION, ["$(git log -1 --format=%H)"], [ ])
   fi
 
-  dnl # --with-redis -> check with-path
-  dnl SEARCH_PATH="/usr/local /usr"     # you might want to change this
-  dnl SEARCH_FOR="/include/redis.h"  # you most likely want to change this
-  dnl if test -r $PHP_REDIS/$SEARCH_FOR; then # path given as parameter
-  dnl   REDIS_DIR=$PHP_REDIS
-  dnl else # search default path list
-  dnl   AC_MSG_CHECKING([for redis files in default path])
-  dnl   for i in $SEARCH_PATH ; do
-  dnl     if test -r $i/$SEARCH_FOR; then
-  dnl       REDIS_DIR=$i
-  dnl       AC_MSG_RESULT(found in $i)
-  dnl     fi
-  dnl   done
-  dnl fi
-  dnl
-  dnl if test -z "$REDIS_DIR"; then
-  dnl   AC_MSG_RESULT([not found])
-  dnl   AC_MSG_ERROR([Please reinstall the redis distribution])
-  dnl fi
-
-  dnl # --with-redis -> add include path
-  dnl PHP_ADD_INCLUDE($REDIS_DIR/include)
-
-  dnl # --with-redis -> check for lib and symbol presence
-  dnl LIBNAME=redis # you may want to change this
-  dnl LIBSYMBOL=redis # you most likely want to change this 
-
-  dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
-  dnl [
-  dnl   PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $REDIS_DIR/lib, REDIS_SHARED_LIBADD)
-  dnl   AC_DEFINE(HAVE_REDISLIB,1,[ ])
-  dnl ],[
-  dnl   AC_MSG_ERROR([wrong redis lib version or lib not found])
-  dnl ],[
-  dnl   -L$REDIS_DIR/lib -lm -ldl
-  dnl ])
-  dnl
-  dnl PHP_SUBST(REDIS_SHARED_LIBADD)
-
+  PHP_SUBST(REDIS_SHARED_LIBADD)
   PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c $lzf_sources, $ext_shared)
 fi

From 3ba3f06d51ff126eb51dd697381c0e56b38bbcf3 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 26 Jun 2020 09:41:47 -0700
Subject: [PATCH 1356/1986] Attempt to run LZ4 tests in Travis

---
 .travis.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index 4520a6d8cf..c1eb5f53b3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,11 +30,13 @@ addons:
     packages:
     - clang
     - libzstd1-dev
+    - liblz4-dev
+    - pkg-config
     - valgrind
     - stunnel
 before_install:
   - phpize
-  - CFGARGS="--enable-redis-lzf --enable-redis-zstd"
+  - CFGARGS="--enable-redis-lzf --enable-redis-zstd --enable-redis-lz4 --with-liblz4"
   - pecl install igbinary && CFGARGS="$CFGARGS --enable-redis-igbinary"
   - pecl install msgpack && CFGARGS="$CFGARGS --enable-redis-msgpack"
   - ./configure $CFGARGS

From 5ceba7c6d9ec95509cb47258ca2f8d250a6f888f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 26 Jun 2020 11:45:58 -0700
Subject: [PATCH 1357/1986] Prepare for 5.3.0RC2

---
 Changelog.md | 26 ++++++++++++++++++++++++++
 package.xml  | 40 ++++++++++++++++++++++++++--------------
 php_redis.h  |  2 +-
 3 files changed, 53 insertions(+), 15 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 3ed2d0a947..5c389813c0 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,6 +5,32 @@ All changes to phpredis will be documented in this file.
 We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [5.3.0RC2]
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack](https://audiomack.com)
+- [BlueHost](https://bluehost.com)
+- [Redis Cache Pro for WordPress](https://wprediscache.com/)
+- [Avtandil Kikabidze](https://github.com/akalongman)
+
+### Fixed
+
+- Fix LZ4 configuration and use pkg-config if we have it
+  [df398cb0](https://github.com/phpredis/phpredis/commit/df398cb07cd10d870c6805d5834703dc39590b0f)
+  ([Remi Collet](https://github.com/remicollet))
+
+- Make sure persistent pool ID is NULL terminated
+  [0838b5bd](https://github.com/phpredis/phpredis/commit/0838b5bde7ef25d419868c7e705bf6c70d68ea20),
+  [57bb95bf](https://github.com/phpredis/phpredis/commit/57bb95bf5a01a2adb74e2bf73bb285488e0d1586)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+### Changed
+
+- Run LZ4 tests in Travis
+  [3ba3f06d](https://github.com/phpredis/phpredis/commit/3ba3f06d51ff126eb51dd697381c0e56b38bbcf3)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
 ## [5.3.0RC1]
 
 ### Sponsors :sparkling_heart:
diff --git a/package.xml b/package.xml
index 6cbf4d415f..9bad6c2a69 100644
--- a/package.xml
+++ b/package.xml
@@ -27,32 +27,44 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2020-06-25
+ 2020-06-26
  
-  5.3.0RC1
-  5.3.0RC1
+  5.3.0RC2
+  5.3.0RC2
  
  
-  alpha
-  alpha
+  beta
+  beta
  
  PHP
  
-    phpredis 5.3.0RC1
+    phpredis 5.3.0RC2
+
+    This release contains initial support for Redis 6 ACLs, LZ4 compression,
+    and many more fixes and improvements.
 
-    This release adds the first round of support for Redis 6 functionality including,
-    most importantly ACLs.  Other Redis 6 functionality is included as well such as
-    KEEPTTL and XINFO FULL command support.
+    You can find a detailed list of changes in Changelog.md and package.xml
 
-    Aside from the Redis 6 functionality this releasae contains many bugfixes and
-    improvements.
+    A special thanks to BlueHost for sponsoring ACL support \o/
 
     * Sponsors
-      ~ Audiomack.com - https://audiomack.com
-      ~ BlueHost.com - https://bluehost.com
-      ~ Redis Cache Pro for WordPress - https://wprediscache.com/
+      ~ Audiomack - https://audiomack.com
+      ~ BlueHost - https://bluehost.com
+      ~ Redis Cache Pro for WordPress - https://wprediscache.com
       ~ Avtandil Kikabidze - https://github.com/akalongman
 
+    phpredis 5.3.0RC2
+
+    ---
+
+    * Fix LZ4 configuration and use pkg-config if we have it [df398cb0]
+      (Remi Collet)
+    * Make sure persistent pool ID is NULL terminated [0838b5bd, 57bb95bf]
+      (Michael Grunder)
+    * Run LZ4 tests in Travis [3ba3f06d] (Michael Grunder)
+
+    phpredis 5.3.0RC1
+
     ---
 
     * Support for Redis 6 ACLs [a311cc4e] (Michael Grunder)
diff --git a/php_redis.h b/php_redis.h
index 1666c5979a..cc3a276d2c 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -23,7 +23,7 @@
 #define PHP_REDIS_H
 
 /* phpredis version */
-#define PHP_REDIS_VERSION "5.3.0RC1"
+#define PHP_REDIS_VERSION "5.3.0RC2"
 
 PHP_METHOD(Redis, __construct);
 PHP_METHOD(Redis, __destruct);

From adbc12e526c5a026262a414d67891a6f0ec315c2 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 30 Jun 2020 12:11:12 -0700
Subject: [PATCH 1358/1986] Prepare for 5.3.0 GA

---
 Changelog.md | 13 ++++++++++++-
 package.xml  | 18 ++++++++++++------
 php_redis.h  |  2 +-
 3 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 5c389813c0..1bd9a25da9 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,7 +5,18 @@ All changes to phpredis will be documented in this file.
 We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## [5.3.0RC2]
+## [5.3.0] - 2020-06-30 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.3.0), [PECL](https://pecl.php.net/package/redis/5.3.0))
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack](https://audiomack.com)
+- [BlueHost](https://bluehost.com)
+- [Redis Cache Pro for WordPress](https://wprediscache.com)
+- [Avtandil Kikabidze](https://github.com/akalongman)
+
+*There were no changes between 5.3.0RC2 and 5.3.0*
+
+## [5.3.0RC2] - 2020-06-26 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.3.0RC2), [PECL](https://pecl.php.net/package/redis/5.3.0RC2))
 
 ### Sponsors :sparkling_heart:
 
diff --git a/package.xml b/package.xml
index 9bad6c2a69..67306f40bd 100644
--- a/package.xml
+++ b/package.xml
@@ -27,18 +27,18 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2020-06-26
+ 2020-06-30
  
-  5.3.0RC2
-  5.3.0RC2
+  5.3.0
+  5.3.0
  
  
-  beta
-  beta
+  stable
+  stable
  
  PHP
  
-    phpredis 5.3.0RC2
+    phpredis 5.3.0
 
     This release contains initial support for Redis 6 ACLs, LZ4 compression,
     and many more fixes and improvements.
@@ -53,6 +53,12 @@ http://pear.php.net/dtd/package-2.0.xsd">
       ~ Redis Cache Pro for WordPress - https://wprediscache.com
       ~ Avtandil Kikabidze - https://github.com/akalongman
 
+    phpredis 5.3.0
+
+    - There were no changes between 5.3.0RC2 and 5.3.0.
+
+    ---
+
     phpredis 5.3.0RC2
 
     ---
diff --git a/php_redis.h b/php_redis.h
index cc3a276d2c..50483af5a2 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -23,7 +23,7 @@
 #define PHP_REDIS_H
 
 /* phpredis version */
-#define PHP_REDIS_VERSION "5.3.0RC2"
+#define PHP_REDIS_VERSION "5.3.0"
 
 PHP_METHOD(Redis, __construct);
 PHP_METHOD(Redis, __destruct);

From 3c56289c71516a7c0ac81713ef2786c2b9e52274 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Wed, 1 Jul 2020 11:18:44 +0200
Subject: [PATCH 1359/1986] check for hash extension during the build

---
 config.m4 | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/config.m4 b/config.m4
index 72d3c219c2..655557ed62 100644
--- a/config.m4
+++ b/config.m4
@@ -41,6 +41,28 @@ if test "$PHP_REDIS" != "no"; then
     AC_DEFINE(PHP_SESSION,1,[redis sessions])
   fi
 
+  AC_MSG_CHECKING([for hash includes])
+  hash_inc_path=""
+  if test -f "$abs_srcdir/include/php/ext/hash/php_hash.h"; then
+    hash_inc_path="$abs_srcdir/include/php"
+  elif test -f "$abs_srcdir/ext/hash/php_hash.h"; then
+    hash_inc_path="$abs_srcdir"
+  elif test -f "$phpincludedir/ext/hash/php_hash.h"; then
+    hash_inc_path="$phpincludedir"
+  else
+    for i in php php7; do
+      if test -f "$prefix/include/$i/ext/hash/php_hash.h"; then
+        hash_inc_path="$prefix/include/$i"
+      fi
+    done
+  fi
+
+  if test "$hash_inc_path" = ""; then
+    AC_MSG_ERROR([Cannot find php_hash.h])
+  else
+    AC_MSG_RESULT([$hash_inc_path])
+  fi
+
   if test "$PHP_REDIS_JSON" != "no"; then
     AC_MSG_CHECKING([for json includes])
     json_inc_path=""

From 6ba7cffcfd89d8848dfed4f2a5739f9e0438db86 Mon Sep 17 00:00:00 2001
From: Johannes Weberhofer 
Date: Thu, 2 Jul 2020 09:17:46 +0200
Subject: [PATCH 1360/1986] Added installation section for openSUSE

---
 INSTALL.markdown | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index 981b103e5a..53a24bca6b 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -62,6 +62,14 @@ Installation of the [php-pecl-redis](https://apps.fedoraproject.org/packages/php
 yum install php-pecl-redis
 ~~~
 
+### openSUSE ≥ 15.1
+
+Installation of the [php7-redis](https://software.opensuse.org/package/php7-redis?search_term=php7-redis) package:
+
+~~~
+zypper in php7-redis
+~~~
+
 
 # Installation on OSX
 

From b4779e6a919103bd65fa0e6a0c88e658e05a3e7c Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 2 Jul 2020 09:29:42 +0200
Subject: [PATCH 1361/1986] [skip ci] remove instruction for EOL Fedora
 versions

---
 INSTALL.markdown | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index 981b103e5a..6eb626d6ef 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -38,14 +38,6 @@ Follow the DLL link on the [https://pecl.php.net/package/redis](https://pecl.php
 
 Fedora users can install the package from the official repository.
 
-### Fedora ≤ 30, Version 4
-
-Installation of the [php-pecl-redis4](https://apps.fedoraproject.org/packages/php-pecl-redis4) package:
-
-~~~
-dnf install php-pecl-redis4
-~~~
-
 ### Fedora ≥ 29, Version 5
 
 Installation of the [php-pecl-redis5](https://apps.fedoraproject.org/packages/php-pecl-redis5) package:

From 08f202e775037ccf849d7b933dddb467c9c2ee5f Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 2 Jul 2020 18:47:05 +0200
Subject: [PATCH 1362/1986] fix #1796 missing include (#1800)

---
 redis.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/redis.c b/redis.c
index ad6817524b..585a516159 100644
--- a/redis.c
+++ b/redis.c
@@ -30,6 +30,7 @@
 #include 
 #include 
 #include 
+#include 
 
 
 #ifdef PHP_SESSION

From 83a1b7c5e225abd94cd3459c52bf7d502dfb0979 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 2 Jul 2020 18:52:54 +0200
Subject: [PATCH 1363/1986] fix configure message (#1803)

---
 config.m4 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/config.m4 b/config.m4
index 655557ed62..5ffd49d21c 100644
--- a/config.m4
+++ b/config.m4
@@ -5,10 +5,10 @@ PHP_ARG_ENABLE(redis, whether to enable redis support,
 dnl Make sure that the comment is aligned:
 [  --enable-redis               Enable redis support])
 
-PHP_ARG_ENABLE(redis-session, whether to disable sessions,
+PHP_ARG_ENABLE(redis-session, whether to enable sessions,
 [  --disable-redis-session      Disable session support], yes, no)
 
-PHP_ARG_ENABLE(redis-json, whether to disable json serializer support,
+PHP_ARG_ENABLE(redis-json, whether to enable json serializer support,
 [  --disable-redis-json         Disable json serializer support], yes, no)
 
 PHP_ARG_ENABLE(redis-igbinary, whether to enable igbinary serializer support,

From ff2e160f408efdc97676cffaa02093e65c2ad634 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Thu, 2 Jul 2020 09:56:54 -0700
Subject: [PATCH 1364/1986] Don't attempt to take ZSTR_VAL(NULL) (#1804)

Fixes #1798
---
 redis_session.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/redis_session.c b/redis_session.c
index 5428d5279c..6742cb1023 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -480,7 +480,8 @@ PS_OPEN_FUNC(redis)
             }
 
             redis_sock = redis_sock_create(addr, addrlen, port, timeout, read_timeout,
-                                           persistent, ZSTR_VAL(persistent_id), retry_interval);
+                                           persistent, persistent_id ? ZSTR_VAL(persistent_id) : NULL,
+                                           retry_interval);
 
             redis_pool_add(pool, redis_sock, weight, db);
             redis_sock->prefix = prefix;

From 5d30e975ba334605e947a78d002168a38dc9c3f4 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 6 Jul 2020 09:17:58 -0700
Subject: [PATCH 1365/1986] Prepare for 5.3.1

---
 Changelog.md |  32 ++++++++++
 package.xml  | 166 ++++++++++++++++++++++++++++++---------------------
 php_redis.h  |   2 +-
 3 files changed, 131 insertions(+), 69 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 1bd9a25da9..f78f415b82 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,6 +5,38 @@ All changes to phpredis will be documented in this file.
 We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [5.3.1] - 2020-07-06 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.1), [PECL](https://pecl.php.net/package/redis/5.3.1))
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack](https://audiomack.com)
+- [BlueHost](https://bluehost.com)
+- [Redis Cache Pro for WordPress](https://wprediscache.com)
+- [Avtandil Kikabidze](https://github.com/akalongman)
+
+### Fixed
+
+- Don't dereference a NULL zend_string [ff2e160f] (Michael Grunder)
+  [ff2e160f](https://github.com/phpredis/phpredis/commit/ff2e160f408efdc97676cffaa02093e65c2ad634)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- Fix config.m4 messages and test for and include php_hash.h
+  [83a1b7c5](https://github.com/phpredis/phpredis/commit/83a1b7c5e225abd94cd3459c52bf7d502dfb0979),
+  [3c56289c](https://github.com/phpredis/phpredis/commit/3c56289c71516a7c0ac81713ef2786c2b9e52274),
+  [08f202e7](https://github.com/phpredis/phpredis/commit/08f202e775037ccf849d7b933dddb467c9c2ee5f),
+  ([Remi Collet](https://github.com/remicollet))
+
+### Added
+
+- Add openSUSE installation instructions
+  [13a168f4](https://github.com/phpredis/phpredis/commit/13a168f42d6639a051d6f829d573dd81bcb97f3a)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+### Removed
+
+- Remove EOL Fedora installation instructions
+  [b4779e6a](https://github.com/phpredis/phpredis/commit/b4779e6a919103bd65fa0e6a0c88e658e05a3e7c)
+  ([Remi Collet](https://github.com/remicollet))
+
 ## [5.3.0] - 2020-06-30 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/5.3.0), [PECL](https://pecl.php.net/package/redis/5.3.0))
 
 ### Sponsors :sparkling_heart:
diff --git a/package.xml b/package.xml
index 67306f40bd..8c4237ddd4 100644
--- a/package.xml
+++ b/package.xml
@@ -27,10 +27,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2020-06-30
+ 2020-07-06
  
-  5.3.0
-  5.3.0
+  5.3.1
+  5.3.1
  
  
   stable
@@ -38,14 +38,15 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
  PHP
  
-    phpredis 5.3.0
+    phpredis 5.3.1
 
-    This release contains initial support for Redis 6 ACLs, LZ4 compression,
-    and many more fixes and improvements.
+    This is a small bugfix release that fixes a couple of issues in 5.3.0.
 
-    You can find a detailed list of changes in Changelog.md and package.xml
+    You should upgrade if you're using persistent_id in session.save_path or
+    of if you're having trouble building 5.3.0 because the php_hash_bin2hex
+    symbol is missing.
 
-    A special thanks to BlueHost for sponsoring ACL support \o/
+    You can find a detailed list of changes in Changelog.md and package.xml
 
     * Sponsors
       ~ Audiomack - https://audiomack.com
@@ -53,68 +54,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
       ~ Redis Cache Pro for WordPress - https://wprediscache.com
       ~ Avtandil Kikabidze - https://github.com/akalongman
 
-    phpredis 5.3.0
-
-    - There were no changes between 5.3.0RC2 and 5.3.0.
-
     ---
 
-    phpredis 5.3.0RC2
-
-    ---
-
-    * Fix LZ4 configuration and use pkg-config if we have it [df398cb0]
-      (Remi Collet)
-    * Make sure persistent pool ID is NULL terminated [0838b5bd, 57bb95bf]
-      (Michael Grunder)
-    * Run LZ4 tests in Travis [3ba3f06d] (Michael Grunder)
-
-    phpredis 5.3.0RC1
-
-    ---
-
-    * Support for Redis 6 ACLs [a311cc4e] (Michael Grunder)
-    * LZ4 Compression [04def9fb] (Ilia Alshanetsky)
-    * Support for new Redis 6 arguments (XINFO FULL, SET KEEPTTL) [a0c53e0b,
-      f9c7bb57] (Michael Grunder, Victor Kislov)
-    * Support for TLS connections [890ee0e6, b0671296] (Pavlo Yatsukhnenko)
-    * New option Redis::SCAN_PREFIX, Redis::SCAN_NOPREFIX [e80600e2] (Pavlo
-      Yatsukhnenko)
-    * Configurable unit test authentication arguments [e37f38a3, 201a9759]
-      (Pavlo Yatsukhnenko, Michael Grunder)
-    * Improved cluster slot caching mechanism to fix a couple of bugs and make
-      it more efficient. [5ca4141c] (Michael Grunder)
-    * Stop calling Redis constructor when creating a RedisArray [e41e19a8]
-      (Pavlo Yatsukhnenko)
-    * Use ZEND_LONG_FMT instead of system `long` [5bf88124] (Michael Grunder)
-    * Use long for SCAN iteration to fix potential overflow [f13f9b7c]
-      (Victor Kislov)
-    * Fix config.m4 to test for the variable $PHP_REDIS_JSON and not the
-      literal PHP_REDIS_JSON [20a3dc72] (Mizuki Nakano)
-    * Fix compiler warnings [b9b383f4, 215828e] (Remi Collet),
-      Pavlo Yatsukhnenko)
-    * Avoid use-after-free of RediSock [8c45816d] (Pavlo Yatsukhnenko)
-    * Fixed ZADD arginfo [a8e2b021] (Pavlo Yatsukhnenko)
-    * Store AUTH information in flags RedisSock rather than duplicating
-      information. [58dab564] (Pavlo Yatsukhnenko)
-    * Refactor redis_sock_get_connection_pool logic. [73212e1]
-      (Pavlo Yatsukhnenko)
-    * Updated documentation to show LPUSH and RPUSH are variadic and fixed DEL
-      documentation. [92f8dde1] (Michael Grunder)
-    * Authenticate in redis_server_sock_open [4ef465b5] (Pavlo Yatsukhnenko)
-    * Dynamically include json.so in unit tests based on configuration
-      [0ce7ca2f] (Michael Grunder)
-    * Update save_path logic in Redis Cluster session unit tests [dd66fce]
-      (Pavlo Yatsukhnenko)
-    * Refactoring various bits of logic [bbcf32a3, a42cf189, 460c8f29,
-      b7f9df75] (Pavlo Yatsukhnenko)
-    * Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers
-      [b9b383f4](Remi Collet)
-    * PHP 8 compatibility [9ee94ca4, 7e4c7b3e] (Pavlo Yatsukhnenko)
-    * Refactor PHPREDIS_GET_OBJECT macro [d5dadaf6, 190c0d34]
-      (Pavlo Yatsukhnenko)
-    * Fix documentation showing lPush and rPush are variadic [6808cd6a]
-      (Michael Grunder)
+    * Don't dereference a NULL zend_string [ff2e160f] (Michael Grunder)
+    * Fix config.m4 messages and test for and include php_hash.h [83a1b7c5,
+      3c56289c, 08f202e7] (Remi Collet)
+    * Add openSUSE installation instructions [13a168f4] (Pavlo Yatsukhnenko)
+    * Remove EOL Fedora installation instructions [b4779e6a] (Remi Collet)
  
  
   
@@ -190,6 +136,90 @@ http://pear.php.net/dtd/package-2.0.xsd">
   
  
  
+  
+   stablestable
+   5.3.05.3.0
+   2020-06-30
+   
+    phpredis 5.3.0
+
+    This release contains initial support for Redis 6 ACLs, LZ4 compression,
+    and many more fixes and improvements.
+
+    You can find a detailed list of changes in Changelog.md and package.xml
+
+    A special thanks to BlueHost for sponsoring ACL support \o/
+
+    * Sponsors
+      ~ Audiomack - https://audiomack.com
+      ~ BlueHost - https://bluehost.com
+      ~ Redis Cache Pro for WordPress - https://wprediscache.com
+      ~ Avtandil Kikabidze - https://github.com/akalongman
+
+    phpredis 5.3.0
+
+    - There were no changes between 5.3.0RC2 and 5.3.0.
+
+    ---
+
+    phpredis 5.3.0RC2
+
+    ---
+
+    * Fix LZ4 configuration and use pkg-config if we have it [df398cb0]
+      (Remi Collet)
+    * Make sure persistent pool ID is NULL terminated [0838b5bd, 57bb95bf]
+      (Michael Grunder)
+    * Run LZ4 tests in Travis [3ba3f06d] (Michael Grunder)
+
+    phpredis 5.3.0RC1
+
+    ---
+
+    * Support for Redis 6 ACLs [a311cc4e] (Michael Grunder)
+    * LZ4 Compression [04def9fb] (Ilia Alshanetsky)
+    * Support for new Redis 6 arguments (XINFO FULL, SET KEEPTTL) [a0c53e0b,
+      f9c7bb57] (Michael Grunder, Victor Kislov)
+    * Support for TLS connections [890ee0e6, b0671296] (Pavlo Yatsukhnenko)
+    * New option Redis::SCAN_PREFIX, Redis::SCAN_NOPREFIX [e80600e2] (Pavlo
+      Yatsukhnenko)
+    * Configurable unit test authentication arguments [e37f38a3, 201a9759]
+      (Pavlo Yatsukhnenko, Michael Grunder)
+    * Improved cluster slot caching mechanism to fix a couple of bugs and make
+      it more efficient. [5ca4141c] (Michael Grunder)
+    * Stop calling Redis constructor when creating a RedisArray [e41e19a8]
+      (Pavlo Yatsukhnenko)
+    * Use ZEND_LONG_FMT instead of system `long` [5bf88124] (Michael Grunder)
+    * Use long for SCAN iteration to fix potential overflow [f13f9b7c]
+      (Victor Kislov)
+    * Fix config.m4 to test for the variable $PHP_REDIS_JSON and not the
+      literal PHP_REDIS_JSON [20a3dc72] (Mizuki Nakano)
+    * Fix compiler warnings [b9b383f4, 215828e] (Remi Collet),
+      Pavlo Yatsukhnenko)
+    * Avoid use-after-free of RediSock [8c45816d] (Pavlo Yatsukhnenko)
+    * Fixed ZADD arginfo [a8e2b021] (Pavlo Yatsukhnenko)
+    * Store AUTH information in flags RedisSock rather than duplicating
+      information. [58dab564] (Pavlo Yatsukhnenko)
+    * Refactor redis_sock_get_connection_pool logic. [73212e1]
+      (Pavlo Yatsukhnenko)
+    * Updated documentation to show LPUSH and RPUSH are variadic and fixed DEL
+      documentation. [92f8dde1] (Michael Grunder)
+    * Authenticate in redis_server_sock_open [4ef465b5] (Pavlo Yatsukhnenko)
+    * Dynamically include json.so in unit tests based on configuration
+      [0ce7ca2f] (Michael Grunder)
+    * Update save_path logic in Redis Cluster session unit tests [dd66fce]
+      (Pavlo Yatsukhnenko)
+    * Refactoring various bits of logic [bbcf32a3, a42cf189, 460c8f29,
+      b7f9df75] (Pavlo Yatsukhnenko)
+    * Use the portable `ZEND_LONG_FORMAT` family instead of C format specifiers
+      [b9b383f4](Remi Collet)
+    * PHP 8 compatibility [9ee94ca4, 7e4c7b3e] (Pavlo Yatsukhnenko)
+    * Refactor PHPREDIS_GET_OBJECT macro [d5dadaf6, 190c0d34]
+      (Pavlo Yatsukhnenko)
+    * Fix documentation showing lPush and rPush are variadic [6808cd6a]
+      (Michael Grunder)
+   
+  
   
    stablestable
    5.2.25.2.2
diff --git a/php_redis.h b/php_redis.h
index 50483af5a2..76983fe0df 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -23,7 +23,7 @@
 #define PHP_REDIS_H
 
 /* phpredis version */
-#define PHP_REDIS_VERSION "5.3.0"
+#define PHP_REDIS_VERSION "5.3.1"
 
 PHP_METHOD(Redis, __construct);
 PHP_METHOD(Redis, __destruct);

From e20b1cef86ce4b1cac27c36464c707298dae01d3 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 6 Jul 2020 09:45:45 -0700
Subject: [PATCH 1366/1986] Remove duplicate sha/name

---
 Changelog.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Changelog.md b/Changelog.md
index f78f415b82..33c66a930b 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -16,7 +16,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ### Fixed
 
-- Don't dereference a NULL zend_string [ff2e160f] (Michael Grunder)
+- Don't dereference a NULL zend_string
   [ff2e160f](https://github.com/phpredis/phpredis/commit/ff2e160f408efdc97676cffaa02093e65c2ad634)
   ([Michael Grunder](https://github.com/michael-grunder))
 - Fix config.m4 messages and test for and include php_hash.h

From 7fed60f248e2249e6cac5c5c3090509aa47647fb Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 6 Jul 2020 13:16:30 -0700
Subject: [PATCH 1367/1986] We don't want to efree a zend_string

---
 redis_session.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis_session.c b/redis_session.c
index 6742cb1023..6f19634b15 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -455,7 +455,7 @@ PS_OPEN_FUNC(redis)
 
             if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) {
                 php_url_free(url);
-                if (persistent_id) efree(persistent_id);
+                if (persistent_id) zend_string_release(persistent_id);
                 if (prefix) zend_string_release(prefix);
                 if (user) zend_string_release(user);
                 if (pass) zend_string_release(pass);

From b8996894ff933d80ed849ced34f4f3d32ef59e35 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 6 Jul 2020 13:19:53 -0700
Subject: [PATCH 1368/1986] Prepare for 5.3.1

---
 Changelog.md | 5 +++--
 package.xml  | 5 +++--
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 33c66a930b..f5e8b302d8 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -16,8 +16,9 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ### Fixed
 
-- Don't dereference a NULL zend_string
-  [ff2e160f](https://github.com/phpredis/phpredis/commit/ff2e160f408efdc97676cffaa02093e65c2ad634)
+- Don't dereference a NULL zend_string or try to efree it
+  [ff2e160f](https://github.com/phpredis/phpredis/commit/ff2e160f408efdc97676cffaa02093e65c2ad634),
+  [7fed06f2](https://github.com/phpredis/phpredis/commit/7fed60f248e2249e6cac5c5c3090509aa47647fb)
   ([Michael Grunder](https://github.com/michael-grunder))
 - Fix config.m4 messages and test for and include php_hash.h
   [83a1b7c5](https://github.com/phpredis/phpredis/commit/83a1b7c5e225abd94cd3459c52bf7d502dfb0979),
diff --git a/package.xml b/package.xml
index 8c4237ddd4..b17ca5ac39 100644
--- a/package.xml
+++ b/package.xml
@@ -27,7 +27,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2020-07-06
+ 2020-07-07
  
   5.3.1
   5.3.1
@@ -56,7 +56,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
 
     ---
 
-    * Don't dereference a NULL zend_string [ff2e160f] (Michael Grunder)
+    * Don't dereference a NULL zend_string or efree one [ff2e160f, 7fed06f2]
+      (Michael Grunder)
     * Fix config.m4 messages and test for and include php_hash.h [83a1b7c5,
       3c56289c, 08f202e7] (Remi Collet)
     * Add openSUSE installation instructions [13a168f4] (Pavlo Yatsukhnenko)

From 49428a2f7072dc30a52db4155aed3d382800b1a6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 7 Jul 2020 09:33:55 -0700
Subject: [PATCH 1369/1986] Passing only NULL to auth is a failure for
 redis_get_auth_info

Fixes: #1808
---
 library.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library.c b/library.c
index e60f35a5b0..2f95aea734 100644
--- a/library.c
+++ b/library.c
@@ -3340,7 +3340,7 @@ int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass)
 
     /* User passed nothing */
     if (ztest == NULL)
-        return SUCCESS;
+        return FAILURE;
 
     /* Handle a non-array first */
     if (Z_TYPE_P(ztest) != IS_ARRAY) {

From 066cff6adee03ce05ec5d57083eb7995dfa4344d Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 7 Jul 2020 09:41:39 -0700
Subject: [PATCH 1370/1986] Proper cleanup and conditional address
 deallocation.

See: #1807
---
 redis_session.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/redis_session.c b/redis_session.c
index 6f19634b15..6f2c5c514a 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -467,12 +467,13 @@ PS_OPEN_FUNC(redis)
             RedisSock *redis_sock;
             char *addr, *scheme;
             size_t addrlen;
-            int port;
+            int port, addr_free = 0;
 
             scheme = url->scheme ? REDIS_URL_STR(url->scheme) : "tcp";
             if (url->host) {
                 port = url->port;
                 addrlen = spprintf(&addr, 0, "%s://%s", scheme, REDIS_URL_STR(url->host));
+                addr_free = 1;
             } else { /* unix */
                 port = 0;
                 addr = REDIS_URL_STR(url->path);
@@ -487,7 +488,8 @@ PS_OPEN_FUNC(redis)
             redis_sock->prefix = prefix;
             redis_sock_set_auth(redis_sock, user, pass);
 
-            efree(addr);
+            if (addr_free) efree(addr);
+            if (persistent_id) zend_string_release(persistent_id);
             if (user) zend_string_release(user);
             if (pass) zend_string_release(pass);
             php_url_free(url);

From b465b797c052a1b7a308195438f86a6f1d6ad485 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 7 Jul 2020 09:52:51 -0700
Subject: [PATCH 1371/1986] Prepare for 5.3.1

---
 Changelog.md | 6 ++++++
 package.xml  | 3 +++
 2 files changed, 9 insertions(+)

diff --git a/Changelog.md b/Changelog.md
index f5e8b302d8..4dc80329d4 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -16,6 +16,12 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ### Fixed
 
+- Properly clean up on session start failure
+  [066cff6a](https://github.com/phpredis/phpredis/commit/066cff6adee03ce05ec5d57083eb7995dfa4344d)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- Treat NULL as a failure for redis_extract_auth_info
+  [49428a2f](https://github.com/phpredis/phpredis/commit/49428a2f7072dc30a52db4155aed3d382800b1a6)
+  ([Michael Grunder](https://github.com/michael-grunder))
 - Don't dereference a NULL zend_string or try to efree it
   [ff2e160f](https://github.com/phpredis/phpredis/commit/ff2e160f408efdc97676cffaa02093e65c2ad634),
   [7fed06f2](https://github.com/phpredis/phpredis/commit/7fed60f248e2249e6cac5c5c3090509aa47647fb)
diff --git a/package.xml b/package.xml
index b17ca5ac39..e63d142dcb 100644
--- a/package.xml
+++ b/package.xml
@@ -56,6 +56,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
 
     ---
 
+    * Properly clean up on session start failure [066cff6a] (Michael Grunder)
+    * Treat NULL as a failure for redis_extract_auth_info [49428a2f]
+      (Michael Grunder)
     * Don't dereference a NULL zend_string or efree one [ff2e160f, 7fed06f2]
       (Michael Grunder)
     * Fix config.m4 messages and test for and include php_hash.h [83a1b7c5,

From 14ac969da29dbf7203f8db31988ca26b9b45f583 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 7 Jul 2020 10:43:31 -0700
Subject: [PATCH 1372/1986] Add a test for passing NULL to auth

---
 tests/RedisTest.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 4c92e7f67e..c7a403f3a3 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6068,6 +6068,7 @@ public function testInvalidAuthArgs() {
         $obj_new = $this->newInstance();
 
         $arr_args = [
+            NULL,
             [],
             [NULL, NULL],
             ['foo', 'bar', 'baz'],

From bee44fb6c8549b65d036c3e62f96cebd5fd05e08 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 7 Jul 2020 10:48:38 -0700
Subject: [PATCH 1373/1986] Final update for tagging 5.3.1

---
 Changelog.md | 3 ++-
 package.xml  | 3 +--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 4dc80329d4..3c5721dcfb 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -20,7 +20,8 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
   [066cff6a](https://github.com/phpredis/phpredis/commit/066cff6adee03ce05ec5d57083eb7995dfa4344d)
   ([Michael Grunder](https://github.com/michael-grunder))
 - Treat NULL as a failure for redis_extract_auth_info
-  [49428a2f](https://github.com/phpredis/phpredis/commit/49428a2f7072dc30a52db4155aed3d382800b1a6)
+  [49428a2f](https://github.com/phpredis/phpredis/commit/49428a2f7072dc30a52db4155aed3d382800b1a6),
+  [14ac969d](https://github.com/phpredis/phpredis/commit/14ac969da29dbf7203f8db31988ca26b9b45f583)
   ([Michael Grunder](https://github.com/michael-grunder))
 - Don't dereference a NULL zend_string or try to efree it
   [ff2e160f](https://github.com/phpredis/phpredis/commit/ff2e160f408efdc97676cffaa02093e65c2ad634),
diff --git a/package.xml b/package.xml
index e63d142dcb..466be5fcba 100644
--- a/package.xml
+++ b/package.xml
@@ -55,9 +55,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
       ~ Avtandil Kikabidze - https://github.com/akalongman
 
     ---
-
     * Properly clean up on session start failure [066cff6a] (Michael Grunder)
-    * Treat NULL as a failure for redis_extract_auth_info [49428a2f]
+    * Treat NULL as a failure for redis_extract_auth_info [49428a2f, 14ac969d]
       (Michael Grunder)
     * Don't dereference a NULL zend_string or efree one [ff2e160f, 7fed06f2]
       (Michael Grunder)

From f771ea16b77f39fcca555bec2d952412265197aa Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Jul 2020 23:18:01 +0300
Subject: [PATCH 1374/1986] Issue #1607 (#1806)

---
 cluster_library.c | 35 +++++++++++++++++++++++------------
 cluster_library.h | 11 ++---------
 library.c         | 15 +++++++++------
 redis_cluster.c   | 35 ++++++++++++++++++-----------------
 4 files changed, 52 insertions(+), 44 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 98ba9c2c69..d1ec02e77c 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -634,8 +634,12 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len,
     zend_llist_init(&node->slots, sizeof(redisSlotRange), NULL, 0);
 
     // Attach socket
-    node->sock = redis_sock_create(host, host_len, port, c->timeout,
-        c->read_timeout, c->persistent, NULL, 0);
+    node->sock = redis_sock_create(host, host_len, port,
+                                   c->flags->timeout, c->flags->read_timeout,
+                                   c->flags->persistent, NULL, 0);
+
+    /* Stream context */
+    node->sock->stream_ctx = c->flags->stream_ctx;
 
     redis_sock_set_auth(node->sock, c->flags->user, c->flags->pass);
 
@@ -818,12 +822,12 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout,
 
     /* Initialize flags and settings */
     c->flags = ecalloc(1, sizeof(RedisSock));
+    c->flags->timeout = timeout;
+    c->flags->read_timeout = read_timeout;
+    c->flags->persistent = persistent;
     c->subscribed_slot = -1;
     c->clusterdown = 0;
-    c->timeout = timeout;
-    c->read_timeout = read_timeout;
     c->failover = failover;
-    c->persistent = persistent;
     c->err = NULL;
 
     /* Set up our waitms based on timeout */
@@ -993,9 +997,12 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) {
 
         /* Create socket */
         sock = redis_sock_create(ZSTR_VAL(cm->host.addr), ZSTR_LEN(cm->host.addr), cm->host.port,
-                                 c->timeout, c->read_timeout, c->persistent,
+                                 c->flags->timeout, c->flags->read_timeout, c->flags->persistent,
                                  NULL, 0);
 
+        /* Stream context */
+        sock->stream_ctx = c->flags->stream_ctx;
+
         /* Add to seed nodes */
         zend_hash_str_update_ptr(c->seeds, key, keylen, sock);
 
@@ -1027,7 +1034,8 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) {
  * seeds array and know we have a non-empty array of strings all in
  * host:port format. */
 PHP_REDIS_API void
-cluster_init_seeds(redisCluster *cluster, zend_string **seeds, uint32_t nseeds) {
+cluster_init_seeds(redisCluster *c, zend_string **seeds, uint32_t nseeds)
+{
     RedisSock *sock;
     char *seed, *sep, key[1024];
     int key_len, i, *map;
@@ -1044,19 +1052,22 @@ cluster_init_seeds(redisCluster *cluster, zend_string **seeds, uint32_t nseeds)
         ZEND_ASSERT(sep != NULL);
 
         // Allocate a structure for this seed
-        sock = redis_sock_create(seed, sep - seed,
-            (unsigned short)atoi(sep+1), cluster->timeout,
-            cluster->read_timeout, cluster->persistent, NULL, 0);
+        sock = redis_sock_create(seed, sep - seed, atoi(sep + 1),
+                                 c->flags->timeout, c->flags->read_timeout,
+                                 c->flags->persistent, NULL, 0);
+
+        /* Stream context */
+        sock->stream_ctx = c->flags->stream_ctx;
 
         /* Credentials */
-        redis_sock_set_auth(sock, cluster->flags->user, cluster->flags->pass);
+        redis_sock_set_auth(sock, c->flags->user, c->flags->pass);
 
         // Index this seed by host/port
         key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(sock->host),
             sock->port);
 
         // Add to our seed HashTable
-        zend_hash_str_update_ptr(cluster->seeds, key, key_len, sock);
+        zend_hash_str_update_ptr(c->seeds, key, key_len, sock);
     }
 
     efree(map);
diff --git a/cluster_library.h b/cluster_library.h
index de9d171828..98e9b0ec05 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -186,12 +186,8 @@ typedef struct clusterFoldItem clusterFoldItem;
 /* RedisCluster implementation structure */
 typedef struct redisCluster {
 
-    /* Timeout and read timeout (for normal operations) */
-    double timeout;
-    double read_timeout;
-
-    /* Are we using persistent connections */
-    int persistent;
+    /* One RedisSock struct for serialization and prefix information */
+    RedisSock *flags;
 
     /* How long in milliseconds should we wait when being bounced around */
     long waitms;
@@ -241,9 +237,6 @@ typedef struct redisCluster {
     /* The slot where we're subscribed */
     short subscribed_slot;
 
-    /* One RedisSock struct for serialization and prefix information */
-    RedisSock *flags;
-
     /* The first line of our last reply, not including our reply type byte
      * or the trailing \r\n */
     char line_reply[1024];
diff --git a/library.c b/library.c
index 2f95aea734..5ae25ef8a3 100644
--- a/library.c
+++ b/library.c
@@ -2165,9 +2165,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
 {
     struct timeval tv, read_tv, *tv_ptr = NULL;
     zend_string *persistent_id = NULL, *estr = NULL;
-    char host[1024], *pos, *address, *schema = NULL;
+    char host[1024], *pos, *address, *scheme = NULL;
     const char *fmtstr = "%s://%s:%d";
-    int host_len, usocket = 0, err = 0, tcp_flag = 1;
+    int host_len, usocket = 0, err = 0, tcp_flag = 1, scheme_free = 0;
     ConnectionPool *p = NULL;
 
     if (redis_sock->stream != NULL) {
@@ -2175,9 +2175,12 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
     }
 
     address = ZSTR_VAL(redis_sock->host);
-    if ((pos = strstr(address, "://")) != NULL) {
-        schema = estrndup(address, pos - address);
+    if ((pos = strstr(address, "://")) == NULL) {
+        scheme = redis_sock->stream_ctx ? "ssl" : "tcp";
+    } else {
+        scheme = estrndup(address, pos - address);
         address = pos + sizeof("://") - 1;
+        scheme_free = 1;
     }
     if (address[0] == '/' && redis_sock->port < 1) {
         host_len = snprintf(host, sizeof(host), "unix://%s", address);
@@ -2193,9 +2196,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
             fmtstr = "%s://[%s]:%d";
         }
 #endif
-        host_len = snprintf(host, sizeof(host), fmtstr, schema ? schema : "tcp", address, redis_sock->port);
-        if (schema) efree(schema);
+        host_len = snprintf(host, sizeof(host), fmtstr, scheme, address, redis_sock->port);
     }
+    if (scheme_free) efree(scheme);
 
     if (redis_sock->persistent) {
         if (INI_INT("redis.pconnect.pooling_enabled")) {
diff --git a/redis_cluster.c b/redis_cluster.c
index 3233b65389..5cb453b0dc 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -351,7 +351,7 @@ void free_cluster_context(zend_object *object) {
 /* Attempt to connect to a Redis cluster provided seeds and timeout options */
 static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout,
                                double read_timeout, int persistent, zend_string *user,
-                               zend_string *pass)
+                               zend_string *pass, zval *context)
 {
     zend_string *hash = NULL, **seeds;
     redisCachedCluster *cc;
@@ -369,10 +369,13 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time
         c->flags->user = zend_string_copy(user);
     if (pass && ZSTR_LEN(pass))
         c->flags->pass = zend_string_copy(pass);
+    if (context) {
+        redis_sock_set_stream_context(c->flags, context);
+    }
 
-    c->timeout = timeout;
-    c->read_timeout = read_timeout;
-    c->persistent = persistent;
+    c->flags->timeout = timeout;
+    c->flags->read_timeout = read_timeout;
+    c->flags->persistent = persistent;
     c->waitms = timeout * 1000L;
 
     /* Attempt to load slots from cache if caching is enabled */
@@ -450,7 +453,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) {
     }
 
     /* Attempt to create/connect to the cluster */
-    redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, user, pass);
+    redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, user, pass, NULL);
 
     /* Clean up */
     zval_dtor(&z_seeds);
@@ -464,38 +467,36 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) {
 
 /* Create a RedisCluster Object */
 PHP_METHOD(RedisCluster, __construct) {
-    zval *object, *z_seeds = NULL, *z_auth = NULL;
+    zval *object, *z_seeds = NULL, *z_auth = NULL, *context = NULL;
     zend_string *user = NULL, *pass = NULL;
     double timeout = 0.0, read_timeout = 0.0;
     size_t name_len;
     zend_bool persistent = 0;
-    redisCluster *context = GET_CONTEXT();
+    redisCluster *c = GET_CONTEXT();
     char *name;
 
     // Parse arguments
     if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                    "Os!|addbz", &object, redis_cluster_ce, &name,
+                                    "Os!|addbza", &object, redis_cluster_ce, &name,
                                     &name_len, &z_seeds, &timeout, &read_timeout,
-                                    &persistent, &z_auth) == FAILURE)
+                                    &persistent, &z_auth, &context) == FAILURE)
     {
         RETURN_FALSE;
     }
 
-    // Require a name
-    if (name_len == 0 && ZEND_NUM_ARGS() < 2) {
-        CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0);
-    }
-
     /* If we've got a string try to load from INI */
     if (ZEND_NUM_ARGS() < 2) {
-        redis_cluster_load(context, name, name_len);
+        if (name_len == 0) { // Require a name
+            CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0);
+        }
+        redis_cluster_load(c, name, name_len);
         return;
     }
 
     /* The normal case, loading from arguments */
     redis_extract_auth_info(z_auth, &user, &pass);
-    redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout,
-                       persistent, user, pass);
+    redis_cluster_init(c, Z_ARRVAL_P(z_seeds), timeout, read_timeout,
+                       persistent, user, pass, context);
 
     if (user) zend_string_release(user);
     if (pass) zend_string_release(pass);

From a7662da7924dcbaa74f5f2c6e1dce06b19e64bfc Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 9 Jul 2020 09:04:09 +0300
Subject: [PATCH 1375/1986] Don't use zend_fcall_info.no_separation

---
 cluster_library.c  | 1 -
 library.c          | 1 -
 redis_array_impl.c | 1 -
 redis_commands.c   | 4 ++--
 4 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index d1ec02e77c..3720f72cf1 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -1818,7 +1818,6 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
     zval z_ret, z_args[4];
     sctx->cb.retval = &z_ret;
     sctx->cb.params = z_args;
-    sctx->cb.no_separation = 0;
 
     /* We're in a subscribe loop */
     c->subscribed_slot = c->cmd_slot;
diff --git a/library.c b/library.c
index 5ae25ef8a3..fb7dc9a479 100644
--- a/library.c
+++ b/library.c
@@ -464,7 +464,6 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
     zval z_ret, z_args[4];
     sctx->cb.retval = &z_ret;
     sctx->cb.params = z_args;
-    sctx->cb.no_separation = 0;
 
     /* Multibulk response, {[pattern], type, channel, payload } */
     while(1) {
diff --git a/redis_array_impl.c b/redis_array_impl.c
index a427f67361..8d8ceceede 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -1134,7 +1134,6 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache,
     z_cb->params = z_args;
     z_cb->retval = z_ret;
 
-    z_cb->no_separation = 0;
     z_cb->param_count = 2;
 
     /* run cb(hostname, count) */
diff --git a/redis_commands.c b/redis_commands.c
index 3b19194438..9601b395c0 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -793,7 +793,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     zval *z_arr, *z_chan;
     HashTable *ht_chan;
     smart_string cmdstr = {0};
-    subscribeContext *sctx = emalloc(sizeof(subscribeContext));
+    subscribeContext *sctx = ecalloc(1, sizeof(*sctx));
     size_t key_len;
     int key_free;
     char *key;
@@ -854,7 +854,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     zval *z_arr, *z_chan;
     HashTable *ht_arr;
     smart_string cmdstr = {0};
-    subscribeContext *sctx = emalloc(sizeof(subscribeContext));
+    subscribeContext *sctx = ecalloc(1, sizeof(*sctx));
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) {
         efree(sctx);

From c9ed151dbae1532a98c0c9322c9401c47d1da149 Mon Sep 17 00:00:00 2001
From: Ali Alwash 
Date: Tue, 21 Jul 2020 19:22:27 +0200
Subject: [PATCH 1376/1986] Update zAdd/zRangeByScore documentation (#1815)

* Update zAdd functionality
* Fix readability additional params
* Add example of -inf/+inf for zRangeByScore
---
 README.markdown | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 40377223a0..296cf96c83 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2763,10 +2763,19 @@ $redis->bzPopMax('zs1', 'zs2', 5);
 -----
 _**Description**_: Add one or more members to a sorted set or update its score if it already exists
 
+
+##### *Prototype*
+~~~php
+$redis->zAdd($key, [ $options ,] $score, $value [, $score1, $value1, ...]);
+~~~
+
 ##### *Parameters*
-*key*
+*key*: string
+*options*: array (optional)
 *score*: double  
 *value*: string
+*score1*: double
+*value1*: string
 
 ##### *Return value*
 *Long* 1 if the element is added. 0 otherwise.
@@ -2777,6 +2786,9 @@ $redis->zAdd('key', 1, 'val1');
 $redis->zAdd('key', 0, 'val0');
 $redis->zAdd('key', 5, 'val5');
 $redis->zRange('key', 0, -1); // [val0, val1, val5]
+
+// From Redis 3.0.2 it's possible to add options like XX, NX, CH, INCR
+$redis->zAdd('key', ['CH'], 5, 'val5', 10, 'val10', 15, 'val15');
 ~~~
 
 ### zCard, zSize
@@ -2960,6 +2972,7 @@ $redis->zRangeByScore('key', 0, 3); /* ['val0', 'val2'] */
 $redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]); /* ['val0' => 0, 'val2' => 2] */
 $redis->zRangeByScore('key', 0, 3, ['limit' => [1, 1]]); /* ['val2'] */
 $redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE, 'limit' => [1, 1]]); /* ['val2' => 2] */
+$redis->zRangeByScore('key', '-inf', '+inf', ['withscores' => TRUE]); /* ['val0' => 0, 'val2' => 2, 'val10' => 10] */
 ~~~
 
 ### zRangeByLex

From c5950644e92e61e0c3f38a8ab8a380f707102eb0 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 5 Aug 2020 11:44:49 +0300
Subject: [PATCH 1377/1986] Refactor redis_sock_check_liveness

---
 library.c | 83 ++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 57 insertions(+), 26 deletions(-)

diff --git a/library.c b/library.c
index fb7dc9a479..e782638ec8 100644
--- a/library.c
+++ b/library.c
@@ -685,19 +685,6 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len)
     return NULL;
 }
 
-static int redis_sock_read_cmp(RedisSock *redis_sock, const char *cmp, int cmplen) {
-    char *resp;
-    int len, rv = FAILURE;
-
-    if ((resp = redis_sock_read(redis_sock, &len)) == NULL)
-        return FAILURE;
-
-    rv = len == cmplen && !memcmp(resp, cmp, cmplen) ? SUCCESS : FAILURE;
-
-    efree(resp);
-    return rv;
-}
-
 /* A simple union to store the various arg types we might handle in our
  * redis_spprintf command formatting function */
 union resparg {
@@ -2129,16 +2116,21 @@ static int redis_stream_liveness_check(php_stream *stream) {
 static int
 redis_sock_check_liveness(RedisSock *redis_sock)
 {
-    char id[64], ok;
+    char id[64], inbuf[4096];
     int idlen, auth;
     smart_string cmd = {0};
+    size_t len;
 
     /* Short circuit if we detect the stream has gone bad or if the user has
      * configured persistent connection "YOLO mode". */
-    if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS)
-        return FAILURE;
-    else if (!INI_INT("redis.pconnect.echo_check_liveness"))
+    if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS) {
+        goto failure;
+    }
+
+    redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
+    if (!INI_INT("redis.pconnect.echo_check_liveness")) {
         return SUCCESS;
+    }
 
     /* AUTH (if we need it) */
     auth = redis_sock_append_auth(redis_sock, &cmd);
@@ -2149,12 +2141,55 @@ redis_sock_check_liveness(RedisSock *redis_sock)
     redis_cmd_append_sstr(&cmd, id, idlen);
 
     /* Send command(s) and make sure we can consume reply(ies) */
-    ok = (redis_sock_write(redis_sock, cmd.c, cmd.len) >= 0) &&
-         (!auth || redis_sock_read_ok(redis_sock) == SUCCESS) &&
-         (redis_sock_read_cmp(redis_sock, id, idlen) == SUCCESS);
-
+    if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) {
+        smart_string_free(&cmd);
+        goto failure;
+    }
     smart_string_free(&cmd);
-    return ok ? SUCCESS : FAILURE;
+
+    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
+        goto failure;
+    }
+
+    if (auth) {
+        if (strncmp(inbuf, "+OK", 3) == 0 || strncmp(inbuf, "-ERR Client sent AUTH", 21) == 0) {
+            /* successfully authenticated or authentication isn't required */
+            if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
+                goto failure;
+            }
+        } else if (strncmp(inbuf, "-NOAUTH", 7) == 0) {
+            /* connection is fine but authentication failed, next command must fails too */
+            if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "-NOAUTH", 7) != 0) {
+                goto failure;
+            }
+            return SUCCESS;
+        } else {
+            goto failure;
+        }
+        redis_sock->status = REDIS_SOCK_STATUS_READY;
+    } else {
+        if (strncmp(inbuf, "-NOAUTH", 7) == 0) {
+            /* connection is fine but authentication required */
+            return SUCCESS;
+        }
+    }
+
+    /* check echo response */
+    if (*inbuf != TYPE_BULK || atoi(inbuf + 1) != idlen ||
+        redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
+        strncmp(inbuf, id, idlen) != 0
+    ) {
+        goto failure;
+    }
+
+    return SUCCESS;
+failure:
+    redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED;
+    if (redis_sock->stream) {
+        php_stream_pclose(redis_sock->stream);
+        redis_sock->stream = NULL;
+    }
+    return FAILURE;
 }
 
 /**
@@ -2207,11 +2242,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
                 zend_llist_remove_tail(&p->list);
 
                 if (redis_sock_check_liveness(redis_sock) == SUCCESS) {
-                    redis_sock->status = REDIS_SOCK_STATUS_READY;
                     return SUCCESS;
-                } else if (redis_sock->stream) {
-                    php_stream_pclose(redis_sock->stream);
-                    redis_sock->stream = NULL;
                 }
                 p->nb_active--;
             }

From 0ac1d17f3679fef9cd3e0b2422a83ae9254225fa Mon Sep 17 00:00:00 2001
From: wilsonwr 
Date: Thu, 13 Aug 2020 11:52:20 -0600
Subject: [PATCH 1378/1986] Update cluster.markdown

The "distribute" option for session.save_path is listed like "persistent," as if it is a boolean option. But this isn't the case. The code in redis_session.c#L893-L902 expects "distribute" to be a parameter to the "failover" option. The updated cluster.markdown reflects this.
---
 cluster.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cluster.markdown b/cluster.markdown
index 1f7e70fb64..85a49aa90f 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -181,8 +181,8 @@ The save path for cluster based session storage takes the form of a PHP GET requ
 * _timeout (double)_:  The amount of time phpredis will wait when connecting or writing to the cluster.
 * _read_timeout (double)_: The amount of time phpredis will wait for a result from the cluster.
 * _persistent_: Tells phpredis whether persistent connections should be used.
-* _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing).
 * _failover (string)_:  How phpredis should distribute session reads between master and slave nodes.
   * _none_ : phpredis will only communicate with master nodes
   * _error_: phpredis will communicate with master nodes unless one fails, in which case an attempt will be made to read session information from a slave.
+  * _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing).
 * _auth (string, empty by default)_:  The password used to authenticate with the server prior to sending commands.

From b2cffffc107541643bab7eb81751b497bc264639 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 14 Aug 2020 16:56:32 +0300
Subject: [PATCH 1379/1986] Fix memory leak in rediscluster session handler

---
 redis_session.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis_session.c b/redis_session.c
index 6f2c5c514a..0e72fd4ffd 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -907,6 +907,7 @@ PS_OPEN_FUNC(rediscluster) {
 
     #define CLUSTER_SESSION_CLEANUP() \
         if (hash) zend_string_release(hash); \
+        if (failstr) zend_string_release(failstr); \
         if (prefix) zend_string_release(prefix); \
         if (user) zend_string_release(user); \
         if (pass) zend_string_release(pass); \
@@ -918,7 +919,6 @@ PS_OPEN_FUNC(rediscluster) {
     if (seeds == NULL) {
         php_error_docref(NULL, E_WARNING, "No valid seeds detected");
         CLUSTER_SESSION_CLEANUP();
-        zval_dtor(&z_conf);
         return FAILURE;
     }
 

From 5719c9f7ff8ba4595c0f2d82e9549a604d925ed7 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 28 Aug 2020 16:23:38 +0300
Subject: [PATCH 1380/1986] Issue #1831

---
 library.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/library.c b/library.c
index e782638ec8..2699afd30b 100644
--- a/library.c
+++ b/library.c
@@ -1713,7 +1713,10 @@ redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements)
         switch (type) {
         case TYPE_BULK:
             if ((data = redis_sock_read_bulk_reply(redis_sock, li)) == NULL) {
-                goto failure;
+                if (!key) goto failure;
+                add_assoc_null_ex(z_ret, key, len);
+                efree(key);
+                key = NULL;
             } else if (key) {
                 add_assoc_stringl_ex(z_ret, key, len, data, li);
                 efree(data);

From 566fdeeb19c8112ac83cf4e47be6928626aa7b37 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 28 Aug 2020 10:03:38 -0700
Subject: [PATCH 1381/1986] Add a regression test for XINFO on empty stream

---
 tests/RedisTest.php | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c7a403f3a3..2ec75a3b53 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6064,6 +6064,21 @@ public function testXInfo()
         }
     }
 
+    /* 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();
 

From 364580718891de94aac13dc352aa994d531d4272 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Sun, 30 Aug 2020 13:51:12 -0700
Subject: [PATCH 1382/1986] Relax requirements on set's expire argument (#1830)

Relax requirements on set's expire argument

See: #1783
---
 redis_commands.c    | 49 ++++++++++++++++++++++++++++++++++-----------
 tests/RedisTest.php |  9 +++++++--
 2 files changed, 44 insertions(+), 14 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 9601b395c0..d6bb2bcffc 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1299,6 +1299,34 @@ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  * have specific processing (argument validation, etc) that make them unique
  */
 
+/* Attempt to pull a long expiry from a zval.  We're more restrictave than zval_get_long
+ * because that function will return integers from things like open file descriptors
+ * which should simply fail as a TTL */
+static int redis_try_get_expiry(zval *zv, zend_long *lval) {
+    double dval;
+
+    /* Success on an actual long or double */
+    if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
+        *lval = zval_get_long(zv);
+        return SUCCESS;
+    }
+
+    /* Automatically fail if we're not a string */
+    if (Z_TYPE_P(zv) != IS_STRING)
+        return FAILURE;
+
+    /* Attempt to get a long from the string */
+    switch (is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), lval, &dval, 0)) {
+        case IS_DOUBLE:
+            *lval = dval;
+            /* fallthrough */
+        case IS_LONG:
+            return SUCCESS;
+        default:
+            return FAILURE;
+    }
+}
+
 /* SET */
 int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                   char **cmd, int *cmd_len, short *slot, void **ctx)
@@ -1306,7 +1334,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     smart_string cmdstr = {0};
     zval *z_value, *z_opts=NULL;
     char *key = NULL, *exp_type = NULL, *set_type = NULL;
-    long expire = -1, exp_set = 0, keep_ttl = 0;
+    long exp_set = 0, keep_ttl = 0;
+    zend_long expire = -1;
     size_t key_len;
 
     // Make sure the function is being called correctly
@@ -1316,14 +1345,6 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    /* Our optional argument can either be a long (to support legacy SETEX */
-    /* redirection), or an array with Redis >= 2.6.12 set options */
-    if (z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY
-       && Z_TYPE_P(z_opts) != IS_NULL)
-    {
-        return FAILURE;
-    }
-
     // Check for an options array
     if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
         HashTable *kt = Z_ARRVAL_P(z_opts);
@@ -1355,8 +1376,12 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                 set_type = Z_STRVAL_P(v);
             }
         } ZEND_HASH_FOREACH_END();
-    } else if (z_opts && Z_TYPE_P(z_opts) == IS_LONG) {
-        expire = Z_LVAL_P(z_opts);
+    } else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) {
+        if (redis_try_get_expiry(z_opts, &expire) == FAILURE) {
+            php_error_docref(NULL, E_WARNING, "Expire must be a long, double, or a numeric string");
+            return FAILURE;
+        }
+
         exp_set = 1;
     }
 
@@ -1386,7 +1411,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     if (exp_type) {
         redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type));
-        redis_cmd_append_sstr_long(&cmdstr, expire);
+        redis_cmd_append_sstr_long(&cmdstr, (long)expire);
     }
 
     if (set_type)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 2ec75a3b53..12b0da9a9d 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -371,9 +371,14 @@ public function testExtendedSet() {
         $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()));
+        $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');

From 500916a4d052aa180aa8d27a9e147e64f3ee6303 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 31 Aug 2020 17:12:14 +0300
Subject: [PATCH 1383/1986] Issue #1794

---
 library.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/library.c b/library.c
index 2699afd30b..6aef62740f 100644
--- a/library.c
+++ b/library.c
@@ -1505,17 +1505,21 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret
          * the multi-bulk header for field and values */
         if ((read_mbulk_header(redis_sock, &mhdr) < 0 || mhdr != 2) ||
             ((id = redis_sock_read(redis_sock, &idlen)) == NULL) ||
-            (read_mbulk_header(redis_sock, &fields) < 0 || fields % 2 != 0))
+            (read_mbulk_header(redis_sock, &fields) < 0 ||
+            (fields > 0 && fields % 2 != 0)))
         {
             if (id) efree(id);
             return -1;
         }
 
-        array_init(&z_message);
-
-        redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS);
-        array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE);
-        add_assoc_zval_ex(z_ret, id, idlen, &z_message);
+        if (fields < 0) {
+            add_assoc_null_ex(z_ret, id, idlen);
+        } else {
+            array_init(&z_message);
+            redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS);
+            array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE);
+            add_assoc_zval_ex(z_ret, id, idlen, &z_message);
+        }
         efree(id);
     }
 

From cd18d1eac96398210b799ab3f17959983f6ea059 Mon Sep 17 00:00:00 2001
From: wangqr 
Date: Fri, 4 Sep 2020 22:55:22 +0800
Subject: [PATCH 1384/1986] Fix unbalanced parenthesis in README.markdown

---
 README.markdown | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.markdown b/README.markdown
index 296cf96c83..5ebb891830 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2615,7 +2615,7 @@ $redis->sAdd('s2', '4');
 var_dump($redis->sUnion('s0', 's1', 's2'));
 
 /* Pass a single array */
-var_dump($redis->sUnion(['s0', 's1', 's2']);
+var_dump($redis->sUnion(['s0', 's1', 's2']));
 
 ~~~
 Return value: all elements that are either in s0 or in s1 or in s2.
@@ -3794,7 +3794,7 @@ $obj_redis->xRange('mystream', '-', '+', 2);
 
 ##### *Prototype*
 ~~~php
-$obj_redis->xRead($arr_streams [, $i_count, $i_block);
+$obj_redis->xRead($arr_streams [, $i_count, $i_block]);
 ~~~
 
 _**Description**_:  Read data from one or more streams and only return IDs greater than sent in the command.
@@ -4010,7 +4010,7 @@ $redis->rawCommand("set", "foo", "bar");
 $redis->rawCommand("get", "foo");
 
 /* Returns: 3 */
-$redis->rawCommand("rpush", "mylist", "one", 2, 3.5));
+$redis->rawCommand("rpush", "mylist", "one", 2, 3.5);
 
 /* Returns: ["one", "2", "3.5000000000000000"] */
 $redis->rawCommand("lrange", "mylist", 0, -1);

From a8daaff87a055bb6b4fb8702151915f56e144649 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 8 Sep 2020 17:58:08 +0300
Subject: [PATCH 1385/1986] Issue #1782

Allow to specify stream context for rediscluster session handler.
---
 redis_session.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/redis_session.c b/redis_session.c
index 0e72fd4ffd..4165fff843 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -854,7 +854,7 @@ static char *cluster_session_key(redisCluster *c, const char *key, int keylen,
 
 PS_OPEN_FUNC(rediscluster) {
     redisCluster *c;
-    zval z_conf, *zv;
+    zval z_conf, *zv, *context;
     HashTable *ht_conf, *ht_seeds;
     double timeout = 0, read_timeout = 0;
     int persistent = 0, failover = REDIS_FAILOVER_NONE;
@@ -932,6 +932,10 @@ PS_OPEN_FUNC(rediscluster) {
 
     redis_sock_set_auth(c->flags, user, pass);
 
+    if ((context = REDIS_HASH_STR_FIND_TYPE_STATIC(ht_conf, "stream", IS_ARRAY)) != NULL) {
+        redis_sock_set_stream_context(c->flags, context);
+    }
+
     /* First attempt to load from cache */
     if (CLUSTER_CACHING_ENABLED()) {
         hash = cluster_hash_seeds(seeds, nseeds);

From 4fbe7df79b9b0e03f92e8323aed0bda9513bc20a Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 9 Sep 2020 09:43:15 +0300
Subject: [PATCH 1386/1986] Issue #1782

Update documentation.
---
 cluster.markdown | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/cluster.markdown b/cluster.markdown
index 1f7e70fb64..cecae1c99e 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -169,7 +169,7 @@ To do this, you must configure your `session.save_handler` and `session.save_pat
 
 ~~~ini
 session.save_handler = rediscluster
-session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1&auth=password"
+session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1&auth=password&stream[verify_peer]=0"
 ~~~
 
 ### session.session_handler
@@ -186,3 +186,4 @@ The save path for cluster based session storage takes the form of a PHP GET requ
   * _none_ : phpredis will only communicate with master nodes
   * _error_: phpredis will communicate with master nodes unless one fails, in which case an attempt will be made to read session information from a slave.
 * _auth (string, empty by default)_:  The password used to authenticate with the server prior to sending commands.
+* _stream (array)_: ssl/tls stream context options.

From 398c99d9851b267d9aaaa42c097c5fe54d507a6e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= 
Date: Wed, 9 Sep 2020 17:09:30 +0200
Subject: [PATCH 1387/1986] Fixed Documentation of Redis::zScore (#1844)

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 296cf96c83..79d857387f 100644
--- a/README.markdown
+++ b/README.markdown
@@ -3121,7 +3121,7 @@ _**Description**_: Returns the score of a given member in the specified sorted s
 *member*
 
 ##### *Return value*
-*Double*
+*Double* or *FALSE* when the value is not found
 
 ##### *Example*
 ~~~php

From f1f07d7e8861473161e61e4c091a4743b2170023 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 10 Sep 2020 09:40:30 +0300
Subject: [PATCH 1388/1986] TravisCI: ppa:redislabs/redis

---
 .travis.yml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index c1eb5f53b3..30e261c5f9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,6 +27,9 @@ matrix:
       env: CC=clang
 addons:
   apt:
+    update: true
+    sources:
+    - sourceline: ppa:redislabs/redis
     packages:
     - clang
     - libzstd1-dev
@@ -34,6 +37,7 @@ addons:
     - pkg-config
     - valgrind
     - stunnel
+    - redis
 before_install:
   - phpize
   - CFGARGS="--enable-redis-lzf --enable-redis-zstd --enable-redis-lz4 --with-liblz4"
@@ -42,7 +46,6 @@ before_install:
   - ./configure $CFGARGS
 install: make install
 before_script:
-  - sudo add-apt-repository ppa:chris-lea/redis-server -y && sudo apt-get update && sudo apt install redis-server
   - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap
   - redis-server --port 0 --daemonize yes --aclfile tests/users.acl --unixsocket /tmp/redis.sock
   - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes --aclfile tests/users.acl; done

From d3780b487ef3509687a4a719ac4a9d7bdcde2c5b Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 10 Sep 2020 10:34:52 +0300
Subject: [PATCH 1389/1986] TravisCI: skip dependency if not available

---
 .travis.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 30e261c5f9..124c0727ec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -41,8 +41,8 @@ addons:
 before_install:
   - phpize
   - CFGARGS="--enable-redis-lzf --enable-redis-zstd --enable-redis-lz4 --with-liblz4"
-  - pecl install igbinary && CFGARGS="$CFGARGS --enable-redis-igbinary"
-  - pecl install msgpack && CFGARGS="$CFGARGS --enable-redis-msgpack"
+  - pecl install igbinary && CFGARGS="$CFGARGS --enable-redis-igbinary" || true
+  - pecl install msgpack && CFGARGS="$CFGARGS --enable-redis-msgpack" || true
   - ./configure $CFGARGS
 install: make install
 before_script:

From f4a30cb2bda7414b159bf8b1be69dad52ed6f008 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Fri, 11 Sep 2020 09:37:11 +0200
Subject: [PATCH 1390/1986] fix 1 test for PHP 8, use call_user_func when no
 arg

---
 tests/RedisTest.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 12b0da9a9d..3c8488531e 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6102,9 +6102,15 @@ public function testInvalidAuthArgs() {
 
         foreach ($arr_args as $arr_arg) {
             try {
-                @call_user_func_array([$obj_new, 'auth'], $arr_arg);
+                if (is_array($arr_arg)) {
+                    @call_user_func_array([$obj_new, 'auth'], $arr_arg);
+                } else {
+                    call_user_func([$obj_new, 'auth']);
+                }
             } catch (Exception $ex) {
                 unset($ex); /* Suppress intellisense warning */
+            } catch (ArgumentCountError $ex) {
+                unset($ex); /* Suppress intellisense warning */
             }
         }
     }

From 178487919148a0f8f1ad4cae62847bc4ae82ee8c Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Fri, 11 Sep 2020 10:15:30 +0200
Subject: [PATCH 1391/1986] fix arg indexes

---
 tests/startSession.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/startSession.php b/tests/startSession.php
index c0ae1884a7..f82c17e216 100644
--- a/tests/startSession.php
+++ b/tests/startSession.php
@@ -22,11 +22,11 @@
 ini_set('redis.session.lock_expire', $lock_expire);
 ini_set('session.gc_maxlifetime', $sessionLifetime);
 
-if (isset($argv[8])) {
+if (isset($argv[10])) {
     ini_set('redis.session.locking_enabled', $argv[10]);
 }
 
-if (isset($argv[9])) {
+if (isset($argv[11])) {
     ini_set('redis.session.lock_wait_time', $argv[11]);
 }
 

From 5b3771a0b2dd8bafe884558a5f249850acfbb6dd Mon Sep 17 00:00:00 2001
From: Jan-E 
Date: Tue, 22 Sep 2020 11:56:59 +0200
Subject: [PATCH 1392/1986] PHP 8 compatibility Windows

---
 redis_array.h      | 2 +-
 redis_array_impl.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/redis_array.h b/redis_array.h
index 34460b10a8..805442aac9 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -1,7 +1,7 @@
 #ifndef REDIS_ARRAY_H
 #define REDIS_ARRAY_H
 
-#ifdef PHP_WIN32
+#if (defined(_MSC_VER) && _MSC_VER <= 1920)
 #include "win32/php_stdint.h"
 #else
 #include 
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 0ef5a73f10..a6802dd299 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -1,7 +1,7 @@
 #ifndef REDIS_ARRAY_IMPL_H
 #define REDIS_ARRAY_IMPL_H
 
-#ifdef PHP_WIN32
+#if (defined(_MSC_VER) && _MSC_VER <= 1920)
 #include 
 #else
 #include 

From 950e8de807ba17ecfff62504a6ee7a959a5df714 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Mon, 28 Sep 2020 11:07:46 -0700
Subject: [PATCH 1393/1986] Issue.1847 cluster segfault (#1850)

Fix for #1847 when dealing with NULL multi bulk replies in RedisCluster.

Adds `Redis::OPT_NULL_MULTIBULK_AS_NULL` setting to have PhpRedis
treat NULL multi bulk replies as `NULL` instead of `[]`.

Co-authored-by: Alex Offshore 
---
 cluster_library.c          | 112 ++++++++++++++++++++++---------------
 cluster_library.h          |   4 +-
 common.h                   |   2 +
 library.c                  |  62 ++++++++++++--------
 library.h                  |   2 +-
 redis.c                    |   1 +
 redis_commands.c           |   6 ++
 tests/RedisClusterTest.php |  20 +++++++
 tests/RedisTest.php        |  46 ++++++++++++++-
 9 files changed, 181 insertions(+), 74 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 3720f72cf1..5bcd23385f 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -61,7 +61,7 @@ static void dump_reply(clusterReply *reply, int indent) {
             smart_string_appendl(&buf, "\"", 1);
             break;
         case TYPE_MULTIBULK:
-            if (reply->elements == (size_t)-1) {
+            if (reply->elements < 0) {
                 smart_string_appendl(&buf, "(nil)", sizeof("(nil)")-1);
             } else {
                 for (i = 0; i < reply->elements; i++) {
@@ -91,7 +91,7 @@ static void dump_reply(clusterReply *reply, int indent) {
 /* Recursively free our reply object.  If free_data is non-zero we'll also free
  * the payload data (strings) themselves.  If not, we just free the structs */
 void cluster_free_reply(clusterReply *reply, int free_data) {
-    int i;
+    long long i;
 
     switch(reply->type) {
         case TYPE_ERR:
@@ -101,10 +101,14 @@ void cluster_free_reply(clusterReply *reply, int free_data) {
                 efree(reply->str);
             break;
         case TYPE_MULTIBULK:
-            for (i = 0; i < reply->elements && reply->element[i]; i++) {
-                cluster_free_reply(reply->element[i], free_data);
+            if (reply->element) {
+                if (reply->elements > 0) {
+                    for (i = 0; i < reply->elements && reply->element[i]; i++) {
+                        cluster_free_reply(reply->element[i], free_data);
+                    }
+                }
+                efree(reply->element);
             }
-            if (reply->element) efree(reply->element);
             break;
         default:
             break;
@@ -154,13 +158,11 @@ cluster_multibulk_resp_recursive(RedisSock *sock, size_t elements,
                 }
                 break;
             case TYPE_MULTIBULK:
-                if (r->len >= 0) {
-                    r->elements = r->len;
-                    if (r->len > 0) {
-                        r->element = ecalloc(r->len,sizeof(clusterReply*));
-                        if (cluster_multibulk_resp_recursive(sock, r->elements, r->element, status_strings) < 0) {
-                            return FAILURE;
-                        }
+                r->elements = r->len;
+                if (r->elements > 0) {
+                    r->element = ecalloc(r->len, sizeof(*r->element));
+                    if (cluster_multibulk_resp_recursive(sock, r->elements, r->element, status_strings) < 0) {
+                        return FAILURE;
                     }
                 }
                 break;
@@ -204,7 +206,7 @@ clusterReply *cluster_read_resp(redisCluster *c, int status_strings) {
  * command and consumed the reply type and meta info (length) */
 clusterReply*
 cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
-                       char *line_reply, size_t len)
+                       char *line_reply, long long len)
 {
     clusterReply *r;
 
@@ -232,7 +234,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
             break;
         case TYPE_MULTIBULK:
             r->elements = len;
-            if (len != (size_t)-1) {
+            if (r->elements > 0) {
                 r->element = ecalloc(len, sizeof(clusterReply*));
                 if (cluster_multibulk_resp_recursive(redis_sock, len, r->element, line_reply != NULL) < 0) {
                     cluster_free_reply(r, 1);
@@ -241,7 +243,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
             }
             break;
         default:
-            cluster_free_reply(r,1);
+            cluster_free_reply(r, 1);
             return NULL;
     }
 
@@ -1939,10 +1941,11 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS,
 }
 
 /* Recursive MULTI BULK -> PHP style response handling */
-static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret)
+static void cluster_mbulk_variant_resp(clusterReply *r, int null_mbulk_as_null,
+                                       zval *z_ret)
 {
     zval z_sub_ele;
-    int i;
+    long long i;
 
     switch(r->type) {
         case TYPE_INT:
@@ -1963,11 +1966,15 @@ static void cluster_mbulk_variant_resp(clusterReply *r, zval *z_ret)
             }
             break;
         case TYPE_MULTIBULK:
-            array_init(&z_sub_ele);
-            for (i = 0; i < r->elements; i++) {
-                cluster_mbulk_variant_resp(r->element[i], &z_sub_ele);
+            if (r->elements < 0 && null_mbulk_as_null) {
+                add_next_index_null(z_ret);
+            } else {
+                array_init(&z_sub_ele);
+                for (i = 0; i < r->elements; i++) {
+                    cluster_mbulk_variant_resp(r->element[i], null_mbulk_as_null, &z_sub_ele);
+                }
+                add_next_index_zval(z_ret, &z_sub_ele);
             }
-            add_next_index_zval(z_ret, &z_sub_ele);
             break;
         default:
             add_next_index_bool(z_ret, 0);
@@ -1983,7 +1990,7 @@ cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
 {
     clusterReply *r;
     zval zv, *z_arr = &zv;
-    int i;
+    long long i;
 
     // Make sure we can read it
     if ((r = cluster_read_resp(c, status_strings)) == NULL) {
@@ -2014,12 +2021,15 @@ cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                 }
                 break;
             case TYPE_MULTIBULK:
-                array_init(z_arr);
-
-                for (i = 0; i < r->elements; i++) {
-                    cluster_mbulk_variant_resp(r->element[i], z_arr);
+                if (r->elements < 0 && c->flags->null_mbulk_as_null) {
+                    RETVAL_NULL();
+                } else {
+                    array_init(z_arr);
+                    for (i = 0; i < r->elements; i++) {
+                        cluster_mbulk_variant_resp(r->element[i], c->flags->null_mbulk_as_null, z_arr);
+                    }
+                    RETVAL_ZVAL(z_arr, 0, 0);
                 }
-                RETVAL_ZVAL(z_arr, 0, 0);
                 break;
             default:
                 RETVAL_FALSE;
@@ -2048,7 +2058,11 @@ cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                 }
                 break;
             case TYPE_MULTIBULK:
-                cluster_mbulk_variant_resp(r, &c->multi_resp);
+                if (r->elements < 0 && c->flags->null_mbulk_as_null) {
+                    add_next_index_null(&c->multi_resp);
+                } else {
+                    cluster_mbulk_variant_resp(r, c->flags->null_mbulk_as_null, &c->multi_resp);
+                }
                 break;
             default:
                 add_next_index_bool(&c->multi_resp, 0);
@@ -2069,7 +2083,8 @@ PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisClust
 PHP_REDIS_API void cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                                             void *ctx)
 {
-    cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, c->flags->reply_literal, ctx);
+    cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
+                                 c->flags->reply_literal, ctx);
 }
 
 PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
@@ -2084,23 +2099,25 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
 {
     zval z_result;
 
-    /* Return FALSE if we didn't get a multi-bulk response */
-    if (c->reply_type != TYPE_MULTIBULK) {
+    /* Abort if the reply isn't MULTIBULK or has an invalid length */
+    if (c->reply_type != TYPE_MULTIBULK || c->reply_len < -1) {
         CLUSTER_RETURN_FALSE(c);
     }
 
-    /* Allocate our array */
-    array_init(&z_result);
+    if (c->reply_len == -1 && c->flags->null_mbulk_as_null) {
+        ZVAL_NULL(&z_result);
+    } else {
+        array_init(&z_result);
 
-    /* Consume replies as long as there are more than zero */
-    if (c->reply_len > 0) {
-        /* Push serialization settings from the cluster into our socket */
-        c->cmd_sock->serializer = c->flags->serializer;
+        if (c->reply_len > 0) {
+            /* Push serialization settings from the cluster into our socket */
+            c->cmd_sock->serializer = c->flags->serializer;
 
-        /* Call our specified callback */
-        if (cb(c->cmd_sock, &z_result, c->reply_len, ctx) == FAILURE) {
-            zval_dtor(&z_result);
-            CLUSTER_RETURN_FALSE(c);
+            /* Call our specified callback */
+            if (cb(c->cmd_sock, &z_result, c->reply_len, ctx) == FAILURE) {
+                zval_dtor(&z_result);
+                CLUSTER_RETURN_FALSE(c);
+            }
         }
     }
 
@@ -2245,14 +2262,17 @@ PHP_REDIS_API void
 cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
     zval z_streams;
 
-    array_init(&z_streams);
-
     c->cmd_sock->serializer = c->flags->serializer;
     c->cmd_sock->compression = c->flags->compression;
 
-    if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, &z_streams) < 0) {
-        zval_dtor(&z_streams);
-        CLUSTER_RETURN_FALSE(c);
+    if (c->reply_len == -1 && c->flags->null_mbulk_as_null) {
+        ZVAL_NULL(&z_streams);
+    } else {
+        array_init(&z_streams);
+        if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, &z_streams) < 0) {
+            zval_dtor(&z_streams);
+            CLUSTER_RETURN_FALSE(c);
+        }
     }
 
     if (CLUSTER_IS_ATOMIC(c)) {
diff --git a/cluster_library.h b/cluster_library.h
index 98e9b0ec05..f8f1eec845 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -325,14 +325,14 @@ typedef struct clusterReply {
     size_t integer;                /* Integer reply */
     long long len;                 /* Length of our string */
     char *str;                     /* String reply */
-    size_t elements;               /* Count of array elements */
+    long long elements;            /* Count of array elements */
     struct clusterReply **element; /* Array elements */
 } clusterReply;
 
 /* Direct variant response handler */
 clusterReply *cluster_read_resp(redisCluster *c, int status_strings);
 clusterReply *cluster_read_sock_resp(RedisSock *redis_sock,
-    REDIS_REPLY_TYPE type, char *line_reply, size_t reply_len);
+    REDIS_REPLY_TYPE type, char *line_reply, long long reply_len);
 void cluster_free_reply(clusterReply *reply, int free_data);
 
 /* Cluster distribution helpers for WATCH */
diff --git a/common.h b/common.h
index dda1436673..80b34e6568 100644
--- a/common.h
+++ b/common.h
@@ -82,6 +82,7 @@ typedef enum _PUBSUB_TYPE {
 #define REDIS_OPT_COMPRESSION        7
 #define REDIS_OPT_REPLY_LITERAL      8
 #define REDIS_OPT_COMPRESSION_LEVEL  9
+#define REDIS_OPT_NULL_MBULK_AS_NULL 10
 
 /* cluster options */
 #define REDIS_FAILOVER_NONE              0
@@ -296,6 +297,7 @@ typedef struct {
 
     int                readonly;
     int                reply_literal;
+    int                null_mbulk_as_null;
     int                tcp_keepalive;
 } RedisSock;
 /* }}} */
diff --git a/library.c b/library.c
index 6aef62740f..2648c56531 100644
--- a/library.c
+++ b/library.c
@@ -1600,10 +1600,13 @@ redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     if (read_mbulk_header(redis_sock, &streams) < 0)
         goto failure;
 
-    array_init(&z_rv);
-
-    if (redis_read_stream_messages_multi(redis_sock, streams, &z_rv) < 0)
-        goto cleanup;
+    if (streams == -1 && redis_sock->null_mbulk_as_null) {
+        ZVAL_NULL(&z_rv);
+    } else {
+        array_init(&z_rv);
+        if (redis_read_stream_messages_multi(redis_sock, streams, &z_rv) < 0)
+            goto cleanup;
+    }
 
     if (IS_ATOMIC(redis_sock)) {
         RETVAL_ZVAL(&z_rv, 0, 1);
@@ -2427,6 +2430,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
                                            RedisSock *redis_sock, zval *z_tab,
                                            void *ctx)
 {
+    zval z_multi_result;
     char inbuf[4096];
     int numElems;
     size_t len;
@@ -2448,10 +2452,13 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
     }
 
     numElems = atoi(inbuf+1);
-    zval z_multi_result;
-    array_init(&z_multi_result); /* pre-allocate array for multi's results. */
 
-    redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL);
+    if (numElems == -1 && redis_sock->null_mbulk_as_null) {
+        ZVAL_NULL(&z_multi_result);
+    } else {
+        array_init(&z_multi_result);
+        redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL);
+    }
 
     if (IS_ATOMIC(redis_sock)) {
         RETVAL_ZVAL(&z_multi_result, 0, 1);
@@ -2459,7 +2466,6 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
         add_next_index_zval(z_tab, &z_multi_result);
     }
 
-    /*zval_copy_ctor(return_value); */
     return 0;
 }
 
@@ -3231,7 +3237,7 @@ redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret
 }
 
 PHP_REDIS_API int
-redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_strings,
+redis_read_multibulk_recursive(RedisSock *redis_sock, long long elements, int status_strings,
                                zval *z_ret)
 {
     long reply_info;
@@ -3267,11 +3273,15 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s
                 add_next_index_zval(z_ret, &z_subelem);
                 break;
             case TYPE_MULTIBULK:
-                // Construct an array for our sub element, and add it, and recurse
-                array_init(&z_subelem);
-                add_next_index_zval(z_ret, &z_subelem);
-                redis_read_multibulk_recursive(redis_sock, reply_info, status_strings,
-                                               &z_subelem);
+                if (reply_info < 0 && redis_sock->null_mbulk_as_null) {
+                    add_next_index_null(z_ret);
+                } else {
+                    array_init(&z_subelem);
+                    if (reply_info > 0) {
+                        redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, &z_subelem);
+                    }
+                    add_next_index_zval(z_ret, &z_subelem);
+                }
                 break;
             default:
                 // Stop the compiler from whinging
@@ -3287,7 +3297,8 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_s
 
 static int
 variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                      int status_strings, zval *z_tab, void *ctx)
+                      int status_strings, int null_mbulk_as_null,
+                      zval *z_tab, void *ctx)
 {
     // Reply type, and reply size vars
     REDIS_REPLY_TYPE reply_type;
@@ -3312,13 +3323,15 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             redis_read_variant_bulk(redis_sock, reply_info, &z_ret);
             break;
         case TYPE_MULTIBULK:
-            /* Initialize an array for our multi-bulk response */
-            array_init(&z_ret);
-
-            // If we've got more than zero elements, parse our multi bulk
-            // response recursively
             if (reply_info > -1) {
+                array_init(&z_ret);
                 redis_read_multibulk_recursive(redis_sock, reply_info, status_strings, &z_ret);
+            } else {
+                if (null_mbulk_as_null) {
+                    ZVAL_NULL(&z_ret);
+                } else {
+                    array_init(&z_ret);
+                }
             }
             break;
         default:
@@ -3343,21 +3356,24 @@ redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock
                              zval *z_tab, void *ctx)
 {
     return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-                                 redis_sock->reply_literal, z_tab, ctx);
+                                 redis_sock->reply_literal,
+                                 redis_sock->null_mbulk_as_null,
+                                 z_tab, ctx);
 }
 
 PHP_REDIS_API int
 redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                          zval *z_tab, void *ctx)
 {
-    return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 0, z_tab, ctx);
+    return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 0,
+                                 redis_sock->null_mbulk_as_null, z_tab, ctx);
 }
 
 PHP_REDIS_API int
 redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                                  zval *z_tab, void *ctx)
 {
-    return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, z_tab, ctx);
+    return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, 0, z_tab, ctx);
 }
 
 PHP_REDIS_API
diff --git a/library.h b/library.h
index da73b34d5b..db47545dc9 100644
--- a/library.h
+++ b/library.h
@@ -144,7 +144,7 @@ PHP_REDIS_API int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
 
 PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info);
 PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval *z_ret);
-PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, int status_strings, zval *z_ret);
+PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, long long elements, int status_strings, zval *z_ret);
 PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
diff --git a/redis.c b/redis.c
index 585a516159..821e8bcbba 100644
--- a/redis.c
+++ b/redis.c
@@ -713,6 +713,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) {
     zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION);
     zend_declare_class_constant_long(ce, ZEND_STRL("OPT_REPLY_LITERAL"), REDIS_OPT_REPLY_LITERAL);
     zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_LEVEL"), REDIS_OPT_COMPRESSION_LEVEL);
+    zend_declare_class_constant_long(ce, ZEND_STRL("OPT_NULL_MULTIBULK_AS_NULL"), REDIS_OPT_NULL_MBULK_AS_NULL);
 
     /* serializer */
     zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE);
diff --git a/redis_commands.c b/redis_commands.c
index d6bb2bcffc..07f409c7ee 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3996,6 +3996,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS,
             RETURN_LONG(redis_sock->scan);
         case REDIS_OPT_REPLY_LITERAL:
             RETURN_LONG(redis_sock->reply_literal);
+        case REDIS_OPT_NULL_MBULK_AS_NULL:
+            RETURN_LONG(redis_sock->null_mbulk_as_null);
         case REDIS_OPT_FAILOVER:
             RETURN_LONG(c->failover);
         default:
@@ -4040,6 +4042,10 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
             val_long = zval_get_long(val);
             redis_sock->reply_literal = val_long != 0;
             RETURN_TRUE;
+        case REDIS_OPT_NULL_MBULK_AS_NULL:
+            val_long = zval_get_long(val);
+            redis_sock->null_mbulk_as_null = val_long != 0;
+            RETURN_TRUE;
         case REDIS_OPT_COMPRESSION:
             val_long = zval_get_long(val);
             if (val_long == REDIS_COMPRESSION_NONE
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 0da0904cd5..a1baf90dd2 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -751,5 +751,25 @@ protected function getFullHostPath()
             return 'seed[]=' . $host;
         }, self::$_arr_node_map)) . ($auth ? "&$auth" : '');
     }
+
+    /* Test correct handling of null multibulk replies */
+    public function testNullArray() {
+        $key = "key:arr";
+        $this->redis->del($key);
+
+        foreach ([false => [], true => NULL] as $opt => $test) {
+            $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt);
+
+            $r = $this->redis->rawCommand($key, "BLPOP", $key, .05);
+            $this->assertEquals($test, $r);
+
+            $this->redis->multi();
+            $this->redis->rawCommand($key, "BLPOP", $key, .05);
+            $r = $this->redis->exec();
+            $this->assertEquals([$test], $r);
+        }
+
+        $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
+    }
 }
 ?>
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 3c8488531e..781fb0ea48 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -935,8 +935,15 @@ public function testblockingPop() {
 
         // blocking blpop, brpop
         $this->redis->del('list');
-        $this->assertTrue($this->redis->blPop(['list'], 1) === []);
-        $this->assertTrue($this->redis->brPop(['list'], 1) === []);
+
+        /* 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_NULL, false);
     }
 
     public function testllen()
@@ -4952,6 +4959,41 @@ public function testReplyLiteral() {
         $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false);
     }
 
+    public function testNullArray() {
+        $key = "key:arr";
+        $this->redis->del($key);
+
+        foreach ([false => [], true => NULL] as $opt => $test) {
+            $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt);
+
+            $r = $this->redis->rawCommand("BLPOP", $key, .05);
+            $this->assertEquals($test, $r);
+
+            $this->redis->multi();
+            $this->redis->rawCommand("BLPOP", $key, .05);
+            $r = $this->redis->exec();
+            $this->assertEquals([$test], $r);
+        }
+
+        $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
+    }
+
+    /* 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_NULL, false);
+    }
+
     public function testReconnectSelect() {
         $key = 'reconnect-select';
         $value = 'Has been set!';

From 81c502ae7c0de65d63cd514ee59849c9d1b0b952 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 28 Sep 2020 21:08:45 +0300
Subject: [PATCH 1394/1986] Issue #1839 (#1854)

---
 redis_sentinel.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/redis_sentinel.c b/redis_sentinel.c
index 9fa5414def..cbdf331cf5 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -55,11 +55,12 @@ PHP_METHOD(RedisSentinel, __construct)
     zend_long port = 26379, retry_interval = 0;
     redis_sentinel_object *obj;
     zend_string *host;
-    zval *zv = NULL;
+    zval *auth = NULL, *zv = NULL;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ldz!ld",
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ldz!ldz",
                                 &host, &port, &timeout, &zv,
-                                &retry_interval, &read_timeout) == FAILURE) {
+                                &retry_interval, &read_timeout,
+                                &auth) == FAILURE) {
         RETURN_FALSE;
     }
 
@@ -96,6 +97,9 @@ PHP_METHOD(RedisSentinel, __construct)
     obj = PHPREDIS_ZVAL_GET_OBJECT(redis_sentinel_object, getThis());
     obj->sock = redis_sock_create(ZSTR_VAL(host), ZSTR_LEN(host), port,
         timeout, read_timeout, persistent, persistent_id, retry_interval);
+    if (auth) {
+        redis_sock_set_auth_zval(obj->sock, auth);
+    }
 }
 
 PHP_METHOD(RedisSentinel, ckquorum)

From 44345f0afb5e7820410e4ccb95f9bd10b7b8cb1b Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 7 Oct 2020 11:37:42 +0300
Subject: [PATCH 1395/1986] Update Changelog.md

---
 Changelog.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/Changelog.md b/Changelog.md
index 3c5721dcfb..e6e0dbff63 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,6 +5,70 @@ All changes to phpredis will be documented in this file.
 We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [Unreleased]
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack](https://audiomack.com)
+- [BlueHost](https://bluehost.com)
+- [Redis Cache Pro for WordPress](https://wprediscache.com)
+- [Avtandil Kikabidze](https://github.com/akalongman)
+
+### Fixed
+
+- Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster
+  [950e8de8](https://github.com/phpredis/phpredis/commit/950e8de807ba17ecfff62504a6ee7a959a5df714)
+  ([Michael Grunder](https://github.com/michael-grunder),
+   [Alex Offshore](https://github.com/offshore))
+- Fix xReadGroup() must return message id
+  [500916a4](https://github.com/phpredis/phpredis/commit/500916a4d052aa180aa8d27a9e147e64f3ee6303)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Fix memory leak in rediscluster session handler
+  [b2cffffc](https://github.com/phpredis/phpredis/commit/b2cffffc107541643bab7eb81751b497bc264639)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Fix XInfo() returns false if the stream is empty
+  [5719c9f7](https://github.com/phpredis/phpredis/commit/5719c9f7ff8ba4595c0f2d82e9549a604d925ed7),
+  [566fdeeb](https://github.com/phpredis/phpredis/commit/566fdeeb19c8112ac83cf4e47be6928626aa7b37)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko),
+   [Michael Grunder](https://github.com/michael-grunder))
+
+### Changed
+
+- Relax requirements on set's expire argument
+  [36458071](https://github.com/phpredis/phpredis/commit/364580718891de94aac13dc352aa994d531d4272)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- Refactor redis_sock_check_liveness
+  [c5950644](https://github.com/phpredis/phpredis/commit/c5950644e92e61e0c3f38a8ab8a380f707102eb0)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- PHP8 compatibility
+  [a7662da7](https://github.com/phpredis/phpredis/commit/a7662da7924dcbaa74f5f2c6e1dce06b19e64bfc),
+  [f4a30cb2](https://github.com/phpredis/phpredis/commit/f4a30cb2bda7414b159bf8b1be69dad52ed6f008),
+  [17848791](https://github.com/phpredis/phpredis/commit/178487919148a0f8f1ad4cae62847bc4ae82ee8c)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko),
+   [Remi Collet](https://github.com/remicollet))
+- Update documentation
+  [c9ed151d](https://github.com/phpredis/phpredis/commit/c9ed151dbae1532a98c0c9322c9401c47d1da149),
+  [398c99d9](https://github.com/phpredis/phpredis/commit/398c99d9851b267d9aaaa42c097c5fe54d507a6e)
+  ([Ali Alwash](https://github.com/aalwash),
+   [Gregoire Pineau](https://github.com/lyrixx))
+
+### Added
+
+- Add `Redis::OPT_NULL_MULTIBULK_AS_NULL` setting to treat NULL multi bulk replies as `NULL` instead of `[]`.
+  [950e8de8](https://github.com/phpredis/phpredis/commit/950e8de807ba17ecfff62504a6ee7a959a5df714)
+  ([Michael Grunder](https://github.com/michael-grunder),
+   [Alex Offshore](https://github.com/offshore))
+- Allow to specify stream context for rediscluster session handler
+  [a8daaff8](https://github.com/phpredis/phpredis/commit/a8daaff87a055bb6b4fb8702151915f56e144649),
+  [4fbe7df7](https://github.com/phpredis/phpredis/commit/4fbe7df79b9b0e03f92e8323aed0bda9513bc20a)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Add new parameter to RedisCluster to specify stream ssl/tls context.
+  [f771ea16](https://github.com/phpredis/phpredis/commit/f771ea16b77f39fcca555bec2d952412265197aa)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Add new parameter to RedisSentinel to specify auth information
+  [81c502ae](https://github.com/phpredis/phpredis/commit/81c502ae7c0de65d63cd514ee59849c9d1b0b952)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
 ## [5.3.1] - 2020-07-06 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.1), [PECL](https://pecl.php.net/package/redis/5.3.1))
 
 ### Sponsors :sparkling_heart:

From ed8a9c8c9e8d5fa67cf0c322bf95c984a1e9a844 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Tessier?= 
Date: Mon, 12 Oct 2020 15:45:13 +0200
Subject: [PATCH 1396/1986] Fix typo in RedisCluster documentation

---
 cluster.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cluster.markdown b/cluster.markdown
index cecae1c99e..f972ece5e6 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -45,7 +45,7 @@ $obj_cluster = new RedisCluster('mycluster');
 On construction, the RedisCluster class will iterate over the provided seed nodes until it can attain a connection to the cluster and run CLUSTER SLOTS to map every node in the cluster locally.  Once the keyspace is mapped, RedisCluster will only connect to nodes when it needs to (e.g. you're getting a key that we believe is on that node.)
 
 ## Slot caching
-Each time the a `RedisCluster` class is constructed from scratch, phpredis needs to execute a `CLUSTER SLOTS` command to map the keyspace.  Although this isn't an expensive command, it does require a round trip for each newly created object, which is inefficient.  Starting from PhpRedis 5.0.0 these slots can be cached by setting `redis.clusters.cache_slots = 1` in `php.ini`.
+Each time that a `RedisCluster` class is constructed from scratch, phpredis needs to execute a `CLUSTER SLOTS` command to map the keyspace.  Although this isn't an expensive command, it does require a round trip for each newly created object, which is inefficient.  Starting from PhpRedis 5.0.0 these slots can be cached by setting `redis.clusters.cache_slots = 1` in `php.ini`.
 
 ## Timeouts
 Because Redis cluster is intended to provide high availability, timeouts do not work in the same way they do in normal socket communication.  It's fully possible to have a timeout or even exception on a given socket (say in the case that a master node has failed), and continue to serve the request if and when a slave can be promoted as the new master.

From fd5e1a335dc2f8efe4a5fdb8e3b84a3803aa4d0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Tessier?= 
Date: Mon, 12 Oct 2020 20:58:29 +0200
Subject: [PATCH 1397/1986] Update cluster.markdown

---
 cluster.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cluster.markdown b/cluster.markdown
index f972ece5e6..a5d23b4a34 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -45,7 +45,7 @@ $obj_cluster = new RedisCluster('mycluster');
 On construction, the RedisCluster class will iterate over the provided seed nodes until it can attain a connection to the cluster and run CLUSTER SLOTS to map every node in the cluster locally.  Once the keyspace is mapped, RedisCluster will only connect to nodes when it needs to (e.g. you're getting a key that we believe is on that node.)
 
 ## Slot caching
-Each time that a `RedisCluster` class is constructed from scratch, phpredis needs to execute a `CLUSTER SLOTS` command to map the keyspace.  Although this isn't an expensive command, it does require a round trip for each newly created object, which is inefficient.  Starting from PhpRedis 5.0.0 these slots can be cached by setting `redis.clusters.cache_slots = 1` in `php.ini`.
+Each time the `RedisCluster` class is constructed from scratch, phpredis needs to execute a `CLUSTER SLOTS` command to map the keyspace.  Although this isn't an expensive command, it does require a round trip for each newly created object, which is inefficient.  Starting from PhpRedis 5.0.0 these slots can be cached by setting `redis.clusters.cache_slots = 1` in `php.ini`.
 
 ## Timeouts
 Because Redis cluster is intended to provide high availability, timeouts do not work in the same way they do in normal socket communication.  It's fully possible to have a timeout or even exception on a given socket (say in the case that a master node has failed), and continue to serve the request if and when a slave can be promoted as the new master.

From 514bc37102c08c1ba7222212b125390f34c35803 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Tue, 13 Oct 2020 13:05:20 -0700
Subject: [PATCH 1398/1986] Verify SET options are strings before testing them
 as strings. (#1859)

Addresses #1835
---
 redis_commands.c    | 10 ++++++----
 tests/RedisTest.php |  5 +++++
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 07f409c7ee..aa89b7c228 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1370,10 +1370,12 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                 } else if (Z_TYPE_P(v) == IS_STRING) {
                     expire = atol(Z_STRVAL_P(v));
                 }
-            } else if (ZVAL_STRICMP_STATIC(v, "KEEPTTL")) {
-                keep_ttl  = 1;
-            } else if (ZVAL_IS_NX_XX_ARG(v)) {
-                set_type = Z_STRVAL_P(v);
+            } else if (Z_TYPE_P(v) == IS_STRING) {
+                if (ZVAL_STRICMP_STATIC(v, "KEEPTTL")) {
+                    keep_ttl  = 1;
+                } else if (ZVAL_IS_NX_XX_ARG(v)) {
+                    set_type = Z_STRVAL_P(v);
+                }
             }
         } ZEND_HASH_FOREACH_END();
     } else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) {
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 781fb0ea48..c9b147b87b 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -424,6 +424,11 @@ public function testExtendedSet() {
         $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;
 

From 39513cab7147ca813762e7da28d001f4c292fe00 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 14 Oct 2020 17:43:13 +0300
Subject: [PATCH 1399/1986] Update Changelog.md

---
 Changelog.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Changelog.md b/Changelog.md
index e6e0dbff63..5baee155de 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -13,9 +13,13 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - [BlueHost](https://bluehost.com)
 - [Redis Cache Pro for WordPress](https://wprediscache.com)
 - [Avtandil Kikabidze](https://github.com/akalongman)
+- [Oleg Babushkin](https://github.com/olbabushkin)
 
 ### Fixed
 
+- Verify SET options are strings before testing them as strings
+  [514bc371](https://github.com/phpredis/phpredis/commit/514bc37102c08c1ba7222212b125390f34c35803)
+  ([Michael Grunder](https://github.com/michael-grunder))
 - Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster
   [950e8de8](https://github.com/phpredis/phpredis/commit/950e8de807ba17ecfff62504a6ee7a959a5df714)
   ([Michael Grunder](https://github.com/michael-grunder),

From 32be3006e6d5a9d58636efd53fe02aa22f18c496 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 21 Oct 2020 17:38:05 +0300
Subject: [PATCH 1400/1986] Issue #1865

Use "%.17g" sprintf format for doubles.
---
 library.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library.c b/library.c
index 2648c56531..9008ffe0b7 100644
--- a/library.c
+++ b/library.c
@@ -951,7 +951,7 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value)
     int len;
 
     /* Convert to string */
-    len = snprintf(tmp, sizeof(tmp), "%.16g", value);
+    len = snprintf(tmp, sizeof(tmp), "%.17g", value);
 
     // Append the string
     return redis_cmd_append_sstr(str, tmp, len);

From 72024afed3640230bbd1a017b2a374d12ab88e59 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 21 Oct 2020 22:55:09 +0300
Subject: [PATCH 1401/1986] Issue #1864 (#1867)

Allow `$options` to be passed as `NULL`.
---
 redis_cluster.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis_cluster.c b/redis_cluster.c
index 5cb453b0dc..ab9d55b7d5 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -477,7 +477,7 @@ PHP_METHOD(RedisCluster, __construct) {
 
     // Parse arguments
     if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                    "Os!|addbza", &object, redis_cluster_ce, &name,
+                                    "Os!|addbza!", &object, redis_cluster_ce, &name,
                                     &name_len, &z_seeds, &timeout, &read_timeout,
                                     &persistent, &z_auth, &context) == FAILURE)
     {

From fc195d6de0f10bf95cea63d122bca32a5256885b Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 22 Oct 2020 09:51:38 +0300
Subject: [PATCH 1402/1986] Update Changelog.md

---
 Changelog.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Changelog.md b/Changelog.md
index 5baee155de..70ec00ebc3 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -38,6 +38,9 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ### Changed
 
+- Use "%.17g" sprintf format for doubles as done in Redis server.
+  [32be3006](https://github.com/phpredis/phpredis/commit/32be3006e6d5a9d58636efd53fe02aa22f18c496)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 - Relax requirements on set's expire argument
   [36458071](https://github.com/phpredis/phpredis/commit/364580718891de94aac13dc352aa994d531d4272)
   ([Michael Grunder](https://github.com/michael-grunder))
@@ -67,7 +70,8 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
   [4fbe7df7](https://github.com/phpredis/phpredis/commit/4fbe7df79b9b0e03f92e8323aed0bda9513bc20a)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 - Add new parameter to RedisCluster to specify stream ssl/tls context.
-  [f771ea16](https://github.com/phpredis/phpredis/commit/f771ea16b77f39fcca555bec2d952412265197aa)
+  [f771ea16](https://github.com/phpredis/phpredis/commit/f771ea16b77f39fcca555bec2d952412265197aa),
+  [72024afe](https://github.com/phpredis/phpredis/commit/72024afed3640230bbd1a017b2a374d12ab88e59)
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 - Add new parameter to RedisSentinel to specify auth information
   [81c502ae](https://github.com/phpredis/phpredis/commit/81c502ae7c0de65d63cd514ee59849c9d1b0b952)

From 2716cc05133012585400de070071b67403101bdc Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 22 Oct 2020 10:16:51 +0300
Subject: [PATCH 1403/1986] Update release information

---
 Changelog.md |   2 +
 package.xml  | 138 +++++++++++++++++++++++++++++++++++++++++++--------
 php_redis.h  |   2 +-
 3 files changed, 120 insertions(+), 22 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 70ec00ebc3..34e6f1e02a 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -7,6 +7,8 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ## [Unreleased]
 
+## [5.3.2] - 2020-10-22 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.2), [PECL](https://pecl.php.net/package/redis/5.3.2))
+
 ### Sponsors :sparkling_heart:
 
 - [Audiomack](https://audiomack.com)
diff --git a/package.xml b/package.xml
index 466be5fcba..21339b3928 100644
--- a/package.xml
+++ b/package.xml
@@ -27,10 +27,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2020-07-07
+ 2020-10-22
  
-  5.3.1
-  5.3.1
+  5.3.2
+  5.3.2
  
  
   stable
@@ -38,14 +38,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
  PHP
  
-    phpredis 5.3.1
-
-    This is a small bugfix release that fixes a couple of issues in 5.3.0.
-
-    You should upgrade if you're using persistent_id in session.save_path or
-    of if you're having trouble building 5.3.0 because the php_hash_bin2hex
-    symbol is missing.
-
+    This release containse some bugfixes and small improvements.
     You can find a detailed list of changes in Changelog.md and package.xml
 
     * Sponsors
@@ -53,17 +46,38 @@ http://pear.php.net/dtd/package-2.0.xsd">
       ~ BlueHost - https://bluehost.com
       ~ Redis Cache Pro for WordPress - https://wprediscache.com
       ~ Avtandil Kikabidze - https://github.com/akalongman
+      ~ Oleg Babushkin - https://github.com/olbabushkin
+
+    phpredis 5.3.2
+
+    * Use "%.17g" sprintf format for doubles as done in Redis server. [32be3006] (Pavlo Yatsukhnenko)
+    * Allow to pass NULL as RedisCluster stream context options. [72024afe] (Pavlo Yatsukhnenko)
 
     ---
-    * Properly clean up on session start failure [066cff6a] (Michael Grunder)
-    * Treat NULL as a failure for redis_extract_auth_info [49428a2f, 14ac969d]
-      (Michael Grunder)
-    * Don't dereference a NULL zend_string or efree one [ff2e160f, 7fed06f2]
-      (Michael Grunder)
-    * Fix config.m4 messages and test for and include php_hash.h [83a1b7c5,
-      3c56289c, 08f202e7] (Remi Collet)
-    * Add openSUSE installation instructions [13a168f4] (Pavlo Yatsukhnenko)
-    * Remove EOL Fedora installation instructions [b4779e6a] (Remi Collet)
+
+    phpredis 5.3.2RC2
+
+    ---
+
+    * Verify SET options are strings before testing them as strings [514bc371] (Michael Grunder)
+
+    ---
+
+    phpredis 5.3.2RC1
+
+    ---
+    * Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster [950e8de8] (Michael Grunder, Alex Offshore)
+    * Fix xReadGroup() must return message id [500916a4] (Pavlo Yatsukhnenko)
+    * Fix memory leak in rediscluster session handler [b2cffffc] (Pavlo Yatsukhnenko)
+    * Fix XInfo() returns false if the stream is empty [5719c9f7, 566fdeeb] (Pavlo Yatsukhnenko, Michael Grunder)
+    * Relax requirements on set's expire argument [36458071] (Michael Grunder)
+    * Refactor redis_sock_check_liveness [c5950644] (Pavlo Yatsukhnenko)
+    * PHP8 compatibility [a7662da7, f4a30cb2, 17848791] (Pavlo Yatsukhnenko, Remi Collet)
+    * Update documentation [c9ed151d, 398c99d9] (Ali Alwash, Gregoire Pineau)
+    * Add Redis::OPT_NULL_MULTIBULK_AS_NULL setting to treat NULL multi bulk replies as NULL instead of []. [950e8de8] (Michael Grunder, Alex Offshore)
+    * Allow to specify stream context for rediscluster session handler [a8daaff8, 4fbe7df7] (Pavlo Yatsukhnenko)
+    * Add new parameter to RedisCluster to specify stream ssl/tls context. [f771ea16] (Pavlo Yatsukhnenko)
+    * Add new parameter to RedisSentinel to specify auth information [81c502ae] (Pavlo Yatsukhnenko)
  
  
   
@@ -125,7 +139,6 @@ http://pear.php.net/dtd/package-2.0.xsd">
   
    
     7.0.0
-    7.9.99
    
    
     1.4.0b1
@@ -139,6 +152,87 @@ http://pear.php.net/dtd/package-2.0.xsd">
   
  
  
+  
+   stablestable
+   5.3.25.3.2
+   2020-10-22
+   
+    This release containse some bugfixes and small improvements.
+    You can find a detailed list of changes in Changelog.md and package.xml
+
+    * Sponsors
+      ~ Audiomack - https://audiomack.com
+      ~ BlueHost - https://bluehost.com
+      ~ Redis Cache Pro for WordPress - https://wprediscache.com
+      ~ Avtandil Kikabidze - https://github.com/akalongman
+      ~ Oleg Babushkin - https://github.com/olbabushkin
+
+    phpredis 5.3.2
+
+    * Use "%.17g" sprintf format for doubles as done in Redis server. [32be3006] (Pavlo Yatsukhnenko)
+    * Allow to pass NULL as RedisCluster stream context options. [72024afe] (Pavlo Yatsukhnenko)
+
+    ---
+
+    phpredis 5.3.2RC2
+
+    ---
+
+    * Verify SET options are strings before testing them as strings [514bc371] (Michael Grunder)
+
+    ---
+
+    phpredis 5.3.2RC1
+
+    ---
+    * Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster [950e8de8] (Michael Grunder, Alex Offshore)
+    * Fix xReadGroup() must return message id [500916a4] (Pavlo Yatsukhnenko)
+    * Fix memory leak in rediscluster session handler [b2cffffc] (Pavlo Yatsukhnenko)
+    * Fix XInfo() returns false if the stream is empty [5719c9f7, 566fdeeb] (Pavlo Yatsukhnenko, Michael Grunder)
+    * Relax requirements on set's expire argument [36458071] (Michael Grunder)
+    * Refactor redis_sock_check_liveness [c5950644] (Pavlo Yatsukhnenko)
+    * PHP8 compatibility [a7662da7, f4a30cb2, 17848791] (Pavlo Yatsukhnenko, Remi Collet)
+    * Update documentation [c9ed151d, 398c99d9] (Ali Alwash, Gregoire Pineau)
+    * Add Redis::OPT_NULL_MULTIBULK_AS_NULL setting to treat NULL multi bulk replies as NULL instead of []. [950e8de8] (Michael Grunder, Alex Offshore)
+    * Allow to specify stream context for rediscluster session handler [a8daaff8, 4fbe7df7] (Pavlo Yatsukhnenko)
+    * Add new parameter to RedisCluster to specify stream ssl/tls context. [f771ea16] (Pavlo Yatsukhnenko)
+    * Add new parameter to RedisSentinel to specify auth information [81c502ae] (Pavlo Yatsukhnenko)
+   
+  
+
+  
+   stablestable
+   5.3.15.3.1
+   2020-07-07
+   
+    phpredis 5.3.1
+
+    This is a small bugfix release that fixes a couple of issues in 5.3.0.
+
+    You should upgrade if you're using persistent_id in session.save_path or
+    of if you're having trouble building 5.3.0 because the php_hash_bin2hex
+    symbol is missing.
+
+    You can find a detailed list of changes in Changelog.md and package.xml
+
+    * Sponsors
+      ~ Audiomack - https://audiomack.com
+      ~ BlueHost - https://bluehost.com
+      ~ Redis Cache Pro for WordPress - https://wprediscache.com
+      ~ Avtandil Kikabidze - https://github.com/akalongman
+
+    ---
+    * Properly clean up on session start failure [066cff6a] (Michael Grunder)
+    * Treat NULL as a failure for redis_extract_auth_info [49428a2f, 14ac969d]
+      (Michael Grunder)
+    * Don't dereference a NULL zend_string or efree one [ff2e160f, 7fed06f2]
+      (Michael Grunder)
+    * Fix config.m4 messages and test for and include php_hash.h [83a1b7c5,
+      3c56289c, 08f202e7] (Remi Collet)
+    * Add openSUSE installation instructions [13a168f4] (Pavlo Yatsukhnenko)
+    * Remove EOL Fedora installation instructions [b4779e6a] (Remi Collet)
+   
+  
   
    stablestable
    5.3.05.3.0
@@ -223,6 +317,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
       (Michael Grunder)
    
   
+
   
    stablestable
    5.2.25.2.2
@@ -242,6 +337,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
       ~ Till Kruss - https://github.com/tillkruss
    
   
+
   
    stablestable
    5.2.15.2.1
diff --git a/php_redis.h b/php_redis.h
index 76983fe0df..24ba89a77e 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -23,7 +23,7 @@
 #define PHP_REDIS_H
 
 /* phpredis version */
-#define PHP_REDIS_VERSION "5.3.1"
+#define PHP_REDIS_VERSION "5.3.2"
 
 PHP_METHOD(Redis, __construct);
 PHP_METHOD(Redis, __destruct);

From 09accba4ef57b1de17262886d93bd57d15bd58d7 Mon Sep 17 00:00:00 2001
From: MiRacLe 
Date: Thu, 29 Oct 2020 23:11:32 +0300
Subject: [PATCH 1404/1986] Update README.markdown (#1874)

missing quote
---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 79d857387f..c76b889312 100644
--- a/README.markdown
+++ b/README.markdown
@@ -77,7 +77,7 @@ session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeou
 
 Sessions have a lifetime expressed in seconds and stored in the INI variable "session.gc_maxlifetime". You can change it with [`ini_set()`](http://php.net/ini_set).
 The session handler requires a version of Redis supporting `EX` and `NX` options of `SET` command (at least 2.6.12).
-phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0`.
+phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0"`.
 
 ### Session locking
 

From 2634350ea9b242a3948565b33924f2e90445bfd9 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 27 Oct 2020 18:38:13 +0200
Subject: [PATCH 1405/1986] Use zend_string in ra_find_node_by_name

---
 redis_array.c      | 23 +++++++++--------------
 redis_array_impl.c |  4 ++--
 redis_array_impl.h |  2 +-
 3 files changed, 12 insertions(+), 17 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 998d6a2324..51f8e1cfb5 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -475,12 +475,11 @@ PHP_METHOD(RedisArray, _instance)
 {
     zval *object;
     RedisArray *ra;
-    char *target;
-    size_t target_len;
+    zend_string *host;
     zval *z_redis;
 
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os",
-                &object, redis_array_ce, &target, &target_len) == FAILURE) {
+    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS",
+                &object, redis_array_ce, &host) == FAILURE) {
         RETURN_FALSE;
     }
 
@@ -488,12 +487,10 @@ PHP_METHOD(RedisArray, _instance)
         RETURN_FALSE;
     }
 
-    z_redis = ra_find_node_by_name(ra, target, target_len);
-    if(z_redis) {
-        RETURN_ZVAL(z_redis, 1, 0);
-    } else {
+    if ((z_redis = ra_find_node_by_name(ra, host)) == NULL) {
         RETURN_NULL();
     }
+    RETURN_ZVAL(z_redis, 1, 0);
 }
 
 PHP_METHOD(RedisArray, _function)
@@ -1225,12 +1222,11 @@ PHP_METHOD(RedisArray, multi)
     zval *object;
     RedisArray *ra;
     zval *z_redis;
-    char *host;
-    size_t host_len;
+    zend_string *host;
     zend_long multi_value = MULTI;
 
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|l",
-                &object, redis_array_ce, &host, &host_len, &multi_value) == FAILURE) {
+    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS|l",
+                &object, redis_array_ce, &host, &multi_value) == FAILURE) {
         RETURN_FALSE;
     }
 
@@ -1239,8 +1235,7 @@ PHP_METHOD(RedisArray, multi)
     }
 
     /* find node */
-    z_redis = ra_find_node_by_name(ra, host, host_len);
-    if(!z_redis) {
+    if ((z_redis = ra_find_node_by_name(ra, host)) == NULL) {
         RETURN_FALSE;
     }
 
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 8d8ceceede..e34d43d158 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -542,11 +542,11 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos)
 }
 
 zval *
-ra_find_node_by_name(RedisArray *ra, const char *host, int host_len) {
+ra_find_node_by_name(RedisArray *ra, zend_string *host) {
 
     int i;
     for(i = 0; i < ra->count; ++i) {
-        if (ZSTR_LEN(ra->hosts[i]) == host_len && strcmp(ZSTR_VAL(ra->hosts[i]), host) == 0) {
+        if (zend_string_equals(host, ra->hosts[i])) {
             return &ra->redis[i];
         }
     }
diff --git a/redis_array_impl.h b/redis_array_impl.h
index 0ef5a73f10..f55c96e48e 100644
--- a/redis_array_impl.h
+++ b/redis_array_impl.h
@@ -19,7 +19,7 @@ RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist,
                           zend_string *algorithm, zend_string *auth,
                           zend_string *pass);
 
-zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len);
+zval *ra_find_node_by_name(RedisArray *ra, zend_string *host);
 zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos);
 void ra_init_function_table(RedisArray *ra);
 

From 6ca64a1c75abb766b46702223360931ebff4a42b Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 30 Oct 2020 18:09:32 +0200
Subject: [PATCH 1406/1986] Refactor RedisArray

---
 redis_array.c | 135 ++++++++++++++++++++++----------------------------
 1 file changed, 60 insertions(+), 75 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 51f8e1cfb5..3a1e64adce 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -495,7 +495,7 @@ PHP_METHOD(RedisArray, _instance)
 
 PHP_METHOD(RedisArray, _function)
 {
-    zval *object, *z_fun;
+    zval *object;
     RedisArray *ra;
 
     if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
@@ -507,13 +507,12 @@ PHP_METHOD(RedisArray, _function)
         RETURN_FALSE;
     }
 
-    z_fun = &ra->z_fun;
-    RETURN_ZVAL(z_fun, 1, 0);
+    RETURN_ZVAL(&ra->z_fun, 1, 0);
 }
 
 PHP_METHOD(RedisArray, _distributor)
 {
-    zval *object, *z_dist;
+    zval *object;
     RedisArray *ra;
 
     if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
@@ -525,8 +524,7 @@ PHP_METHOD(RedisArray, _distributor)
         RETURN_FALSE;
     }
 
-    z_dist = &ra->z_dist;
-    RETURN_ZVAL(z_dist, 1, 0);
+    RETURN_ZVAL(&ra->z_dist, 1, 0);
 }
 
 PHP_METHOD(RedisArray, _rehash)
@@ -815,12 +813,10 @@ PHP_METHOD(RedisArray, select)
 /* MGET will distribute the call to several nodes and regroup the values. */
 PHP_METHOD(RedisArray, mget)
 {
-    zval *object, *z_keys, z_argarray, *data, z_ret, *z_cur, z_tmp_array;
-    int i, j, n;
-    RedisArray *ra;
-    int *pos, argc, *argc_each;
+    zval *object, *z_keys, *data, z_ret, *z_cur, z_tmp_array, z_fun, z_arg, **argv;
+    int i, j, n, *pos, argc, *argc_each;
     HashTable *h_keys;
-    zval **argv;
+    RedisArray *ra;
 
     if ((ra = redis_array_get(getThis())) == NULL) {
         RETURN_FALSE;
@@ -840,11 +836,10 @@ PHP_METHOD(RedisArray, mget)
     if ((argc = zend_hash_num_elements(h_keys)) == 0) {
         RETURN_FALSE;
     }
-    argv = emalloc(argc * sizeof(zval*));
-    pos = emalloc(argc * sizeof(int));
+    argv = ecalloc(argc, sizeof(*argv));
+    pos = ecalloc(argc, sizeof(*pos));
 
-    argc_each = emalloc(ra->count * sizeof(int));
-    memset(argc_each, 0, ra->count * sizeof(int));
+    argc_each = ecalloc(ra->count, sizeof(*argc_each));
 
     /* associate each key to a redis node */
     i = 0;
@@ -856,95 +851,86 @@ PHP_METHOD(RedisArray, mget)
         /* Handle the possibility that we're a reference */
         ZVAL_DEREF(data);
 
-        /* phpredis proper can only use string or long keys, so restrict to that here */
-        if (Z_TYPE_P(data) != IS_STRING && Z_TYPE_P(data) != IS_LONG) {
-            php_error_docref(NULL, E_ERROR, "MGET: all keys must be strings or longs");
-            efree(argv);
-            efree(pos);
-            efree(argc_each);
-            RETURN_FALSE;
-        }
-
         /* Convert to a string for hash lookup if it isn't one */
         if (Z_TYPE_P(data) == IS_STRING) {
             key_len = Z_STRLEN_P(data);
             key_lookup = Z_STRVAL_P(data);
-        } else {
+        } else if (Z_TYPE_P(data) == IS_LONG) {
             key_len = snprintf(kbuf, sizeof(kbuf), ZEND_LONG_FMT, Z_LVAL_P(data));
             key_lookup = (char*)kbuf;
+        } else {
+            /* phpredis proper can only use string or long keys, so restrict to that here */
+            php_error_docref(NULL, E_ERROR, "MGET: all keys must be strings or longs");
+            RETVAL_FALSE;
+            goto cleanup;
         }
 
         /* Find our node */
         if (ra_find_node(ra, key_lookup, key_len, &pos[i]) == NULL) {
-            /* TODO: handle */
+            RETVAL_FALSE;
+            goto cleanup;
         }
 
         argc_each[pos[i]]++;    /* count number of keys per node */
         argv[i++] = data;
     } ZEND_HASH_FOREACH_END();
 
+    /* prepare call */
     array_init(&z_tmp_array);
+    ZVAL_STRINGL(&z_fun, "MGET", sizeof("MGET") - 1);
+
     /* calls */
     for(n = 0; n < ra->count; ++n) { /* for each node */
         /* We don't even need to make a call to this node if no keys go there */
         if(!argc_each[n]) continue;
 
         /* copy args for MGET call on node. */
-        array_init(&z_argarray);
+        array_init(&z_arg);
 
         for(i = 0; i < argc; ++i) {
-            if(pos[i] != n) continue;
-
-            zval z_ret;
-            ZVAL_ZVAL(&z_ret, argv[i], 1, 0);
-            add_next_index_zval(&z_argarray, &z_ret);
+            if (pos[i] == n) {
+                add_next_index_zval(&z_arg, argv[i]);
+            }
         }
 
-        zval z_fun;
-        /* prepare call */
-        ZVAL_STRINGL(&z_fun, "MGET", 4);
         /* call MGET on the node */
-        call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
-        zval_dtor(&z_fun);
+        call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_arg);
 
         /* cleanup args array */
-        zval_dtor(&z_argarray);
+        zval_dtor(&z_arg);
 
         /* Error out if we didn't get a proper response */
         if (Z_TYPE(z_ret) != IS_ARRAY) {
             /* cleanup */
             zval_dtor(&z_ret);
             zval_dtor(&z_tmp_array);
-            efree(argv);
-            efree(pos);
-            efree(argc_each);
-
-            /* failure */
-            RETURN_FALSE;
+            RETVAL_FALSE;
+            goto cleanup;
         }
 
         for(i = 0, j = 0; i < argc; ++i) {
             if (pos[i] != n || (z_cur = zend_hash_index_find(Z_ARRVAL(z_ret), j++)) == NULL) continue;
 
-            zval z_ret;
-            ZVAL_ZVAL(&z_ret, z_cur, 1, 0);
-            add_index_zval(&z_tmp_array, i, &z_ret);
+            ZVAL_ZVAL(&z_arg, z_cur, 1, 0);
+            add_index_zval(&z_tmp_array, i, &z_arg);
         }
         zval_dtor(&z_ret);
     }
 
+    zval_dtor(&z_fun);
+
     array_init(return_value);
     /* copy temp array in the right order to return_value */
     for(i = 0; i < argc; ++i) {
         if ((z_cur = zend_hash_index_find(Z_ARRVAL(z_tmp_array), i)) == NULL) continue;
 
-        zval z_ret;
-        ZVAL_ZVAL(&z_ret, z_cur, 1, 0);
-        add_next_index_zval(return_value, &z_ret);
+        ZVAL_ZVAL(&z_arg, z_cur, 1, 0);
+        add_next_index_zval(return_value, &z_arg);
     }
 
     /* cleanup */
     zval_dtor(&z_tmp_array);
+cleanup:
     efree(argv);
     efree(pos);
     efree(argc_each);
@@ -954,13 +940,11 @@ PHP_METHOD(RedisArray, mget)
 /* MSET will distribute the call to several nodes and regroup the values. */
 PHP_METHOD(RedisArray, mset)
 {
-    zval *object, *z_keys, z_argarray, *data, z_ret, **argv;
-    int i = 0, n;
+    zval *object, *z_keys, z_argarray, *data, z_fun, z_ret, **argv;
+    int i = 0, n, *pos, argc, *argc_each, key_len;
     RedisArray *ra;
-    int *pos, argc, *argc_each;
     HashTable *h_keys;
     char *key, kbuf[40];
-    int key_len;
     zend_string **keys, *zkey;
     zend_ulong idx;
 
@@ -982,12 +966,11 @@ PHP_METHOD(RedisArray, mset)
     if ((argc = zend_hash_num_elements(h_keys)) == 0) {
         RETURN_FALSE;
     }
-    argv = emalloc(argc * sizeof(zval*));
-    pos = emalloc(argc * sizeof(int));
-    keys = ecalloc(argc, sizeof(zend_string *));
+    argv = ecalloc(argc, sizeof(*argv));
+    pos = ecalloc(argc, sizeof(*pos));
+    keys = ecalloc(argc, sizeof(*keys));
 
-    argc_each = emalloc(ra->count * sizeof(int));
-    memset(argc_each, 0, ra->count * sizeof(int));
+    argc_each = ecalloc(ra->count, sizeof(*argc_each));
 
     /* associate each key to a redis node */
     ZEND_HASH_FOREACH_KEY_VAL(h_keys, idx, zkey, data) {
@@ -1001,16 +984,26 @@ PHP_METHOD(RedisArray, mset)
         }
 
         if (ra_find_node(ra, key, (int)key_len, &pos[i]) == NULL) {
-            // TODO: handle
+            for (n = 0; n < i; ++n) {
+                zend_string_release(keys[n]);
+            }
+            efree(keys);
+            efree(argv);
+            efree(pos);
+            efree(argc_each);
+            RETURN_FALSE;
         }
 
         argc_each[pos[i]]++;    /* count number of keys per node */
-        keys[i] = zend_string_init(key, key_len, 0);
+        keys[i] = zkey ? zend_string_copy(zkey) : zend_string_init(key, key_len, 0);
         argv[i] = data;
         i++;
     } ZEND_HASH_FOREACH_END();
 
 
+    /* prepare call */
+    ZVAL_STRINGL(&z_fun, "MSET", sizeof("MSET") - 1);
+
     /* calls */
     for (n = 0; n < ra->count; ++n) { /* for each node */
         /* We don't even need to make a call to this node if no keys go there */
@@ -1023,7 +1016,6 @@ PHP_METHOD(RedisArray, mset)
         for(i = 0; i < argc; ++i) {
             if(pos[i] != n) continue;
 
-            zval z_ret;
             if (argv[i] == NULL) {
                 ZVAL_NULL(&z_ret);
             } else {
@@ -1040,26 +1032,19 @@ PHP_METHOD(RedisArray, mset)
 
         if(ra->index) { /* add MULTI */
             ra_index_multi(&ra->redis[n], MULTI);
-        }
-
-        zval z_fun;
-
-        /* prepare call */
-        ZVAL_STRINGL(&z_fun, "MSET", 4);
-
-        /* call */
-        call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
-        zval_dtor(&z_fun);
-        zval_dtor(&z_ret);
-
-        if(ra->index) {
+            call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
             ra_index_keys(&z_argarray, &ra->redis[n]); /* use SADD to add keys to node index */
             ra_index_exec(&ra->redis[n], NULL, 0); /* run EXEC */
+        } else {
+            call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
         }
 
         zval_dtor(&z_argarray);
+        zval_dtor(&z_ret);
     }
 
+    zval_dtor(&z_fun);
+
     /* Free any keys that we needed to allocate memory for, because they weren't strings */
     for(i = 0; i < argc; i++) {
         zend_string_release(keys[i]);

From d67e360e1b055addc0ded90faffc816b91ea3e62 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 30 Oct 2020 18:26:56 +0200
Subject: [PATCH 1407/1986] Refactor ra_generic_del

---
 redis_array.c | 66 ++++++++++++++++++++-------------------------------
 1 file changed, 26 insertions(+), 40 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 3a1e64adce..823d941973 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -1060,15 +1060,14 @@ PHP_METHOD(RedisArray, mset)
 }
 
 /* Generic handler for DEL or UNLINK which behave identically to phpredis */
-static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
-    zval *object, z_keys, z_fun, *data, z_ret, *z_args;
-    int i, n;
-    RedisArray *ra;
-    int *pos, argc = ZEND_NUM_ARGS(), *argc_each;
+static void
+ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len)
+{
+    zval *object, z_keys, z_fun, *data, z_ret, *z_args, **argv;
+    int i, n, *pos, argc = ZEND_NUM_ARGS(), *argc_each, free_zkeys = 0;
     HashTable *h_keys;
-    zval **argv;
+    RedisArray *ra;
     long total = 0;
-    int free_zkeys = 0;
 
     if ((ra = redis_array_get(getThis())) == NULL) {
         RETURN_FALSE;
@@ -1078,7 +1077,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
     HANDLE_MULTI_EXEC(ra, kw, kw_len);
 
     /* get all args in z_args */
-    z_args = emalloc(argc * sizeof(zval));
+    z_args = ecalloc(argc, sizeof(*z_args));
     if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
         efree(z_args);
         RETURN_FALSE;
@@ -1091,11 +1090,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
         /* copy all elements to z_keys */
         array_init(&z_keys);
         for (i = 0; i < argc; ++i) {
-            zval *z_arg = &z_args[i];
-            zval z_ret;
-            ZVAL_ZVAL(&z_ret, z_arg, 1, 0);
-            /* add copy to z_keys */
-            add_next_index_zval(&z_keys, &z_ret);
+            add_next_index_zval(&z_keys, &z_args[i]);
         }
         free_zkeys = 1;
     }
@@ -1107,27 +1102,23 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
         efree(z_args);
         RETURN_FALSE;
     }
-    argv = emalloc(argc * sizeof(zval*));
-    pos = emalloc(argc * sizeof(int));
+    argv = ecalloc(argc, sizeof(*argv));
+    pos = ecalloc(argc, sizeof(*pos));
 
-    argc_each = emalloc(ra->count * sizeof(int));
-    memset(argc_each, 0, ra->count * sizeof(int));
+    argc_each = ecalloc(ra->count, sizeof(*argc_each));
 
     /* associate each key to a redis node */
     i = 0;
     ZEND_HASH_FOREACH_VAL(h_keys, data) {
         if (Z_TYPE_P(data) != IS_STRING) {
             php_error_docref(NULL, E_ERROR, "DEL: all keys must be string.");
-            if (free_zkeys) zval_dtor(&z_keys);
-            efree(z_args);
-            efree(argv);
-            efree(pos);
-            efree(argc_each);
-            RETURN_FALSE;
+            RETVAL_FALSE;
+            goto cleanup;
         }
 
         if (ra_find_node(ra, Z_STRVAL_P(data), Z_STRLEN_P(data), &pos[i]) == NULL) {
-            // TODO: handle
+            RETVAL_FALSE;
+            goto cleanup;
         }
         argc_each[pos[i]]++;    /* count number of keys per node */
         argv[i++] = data;
@@ -1147,12 +1138,10 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
         /* copy args */
         array_init(&z_argarray);
         for(i = 0; i < argc; ++i) {
-            if(pos[i] != n) continue;
-
-            zval z_ret;
-            ZVAL_ZVAL(&z_ret, argv[i], 1, 0);
-            add_next_index_zval(&z_argarray, &z_ret);
-            found++;
+            if (pos[i] == n) {
+                add_next_index_zval(&z_argarray, argv[i]);
+                found++;
+            }
         }
 
         if(!found) {    /* don't run empty DEL or UNLINK commands */
@@ -1162,15 +1151,11 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
 
         if(ra->index) { /* add MULTI */
             ra_index_multi(&ra->redis[n], MULTI);
-        }
-
-        /* call */
-        call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
-
-        if(ra->index) {
-            zval_dtor(&z_ret);
+            call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
             ra_index_del(&z_argarray, &ra->redis[n]); /* use SREM to remove keys from node index */
             ra_index_exec(&ra->redis[n], &z_ret, 0); /* run EXEC */
+        } else {
+            call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray);
         }
         total += Z_LVAL(z_ret);    /* increment total */
 
@@ -1178,8 +1163,11 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
         zval_dtor(&z_ret);
     }
 
-    /* cleanup */
     zval_dtor(&z_fun);
+
+    RETVAL_LONG(total);
+
+cleanup:
     efree(argv);
     efree(pos);
     efree(argc_each);
@@ -1187,9 +1175,7 @@ static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
     if(free_zkeys) {
         zval_dtor(&z_keys);
     }
-
     efree(z_args);
-    RETURN_LONG(total);
 }
 
 /* DEL will distribute the call to several nodes and regroup the values. */

From 43a39afb91f8479d1cfea3b78692d0b7df6f9706 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 30 Oct 2020 21:23:25 +0200
Subject: [PATCH 1408/1986] Duplicate zval before add_next_index_zval

---
 redis_array.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/redis_array.c b/redis_array.c
index 823d941973..de330a0616 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -889,7 +889,8 @@ PHP_METHOD(RedisArray, mget)
 
         for(i = 0; i < argc; ++i) {
             if (pos[i] == n) {
-                add_next_index_zval(&z_arg, argv[i]);
+                ZVAL_ZVAL(&z_ret, argv[i], 1, 0);
+                add_next_index_zval(&z_arg, &z_ret);
             }
         }
 

From 9f2972b54eaa8ea9e8842f4237b112bffccd2d24 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 4 Nov 2020 18:33:29 +0200
Subject: [PATCH 1409/1986] Use zend_string_equals_literal_ci instread of
 macros

---
 redis_commands.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index aa89b7c228..2ace3a9de5 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -509,12 +509,6 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
     return cmdstr.len;
 }
 
-/* ZRANGEBYSCORE/ZREVRANGEBYSCORE */
-#define IS_WITHSCORES_ARG(s, l) \
-    (l == sizeof("withscores") - 1 && !strncasecmp(s, "withscores", l))
-#define IS_LIMIT_ARG(s, l) \
-    (l == sizeof("limit") - 1 && !strncasecmp(s,"limit", l))
-
 /* ZRANGE/ZREVRANGE */
 int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                      char *kw, char **cmd, int *cmd_len, int *withscores,
@@ -540,7 +534,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         if (Z_TYPE_P(z_ws) == IS_ARRAY) {
             ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_ws), zkey, z_ele) {
                 ZVAL_DEREF(z_ele);
-                if (IS_WITHSCORES_ARG(ZSTR_VAL(zkey), ZSTR_LEN(zkey))) {
+                if (zend_string_equals_literal_ci(zkey, "withscores")) {
                     *withscores = zval_is_true(z_ele);
                     break;
                 }
@@ -590,9 +584,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
            if (!zkey) continue;
            ZVAL_DEREF(z_ele);
            /* Check for withscores and limit */
-           if (IS_WITHSCORES_ARG(ZSTR_VAL(zkey), ZSTR_LEN(zkey))) {
+           if (zend_string_equals_literal_ci(zkey, "withscores")) {
                *withscores = zval_is_true(z_ele);
-           } else if (IS_LIMIT_ARG(ZSTR_VAL(zkey), ZSTR_LEN(zkey)) && Z_TYPE_P(z_ele) == IS_ARRAY) {
+           } else if (zend_string_equals_literal_ci(zkey, "limit") && Z_TYPE_P(z_ele) == IS_ARRAY) {
                 HashTable *htlimit = Z_ARRVAL_P(z_ele);
                 zval *zoff, *zcnt;
 

From 3a09f69b09821c65510cd130028403d64ad1e620 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 16 Nov 2020 09:28:34 +0200
Subject: [PATCH 1410/1986] Issue #1873

ZADD scores must be numeric or '-inf', '+inf'
---
 redis_commands.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 2ace3a9de5..d8f1b15dca 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2669,16 +2669,28 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     // Now the rest of our arguments
     while (i < num) {
         // Append score and member
-        if (Z_TYPE(z_args[i]) == IS_STRING && (
-            /* The score values should be the string representation of a double
+        switch (Z_TYPE(z_args[i])) {
+        case IS_LONG:
+        case IS_DOUBLE:
+            redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(&z_args[i]));
+            break;
+        case IS_STRING:
+            /* The score values must be the string representation of a double
              * precision floating point number. +inf and -inf values are valid
              * values as well. */
-            strncasecmp(Z_STRVAL(z_args[i]), "-inf", 4) == 0 ||
-            strncasecmp(Z_STRVAL(z_args[i]), "+inf", 4) == 0
-        )) {
-            redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[i]), Z_STRLEN(z_args[i]));
-        } else {
-            redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(&z_args[i]));
+            if (strncasecmp(Z_STRVAL(z_args[i]), "-inf", 4) == 0 ||
+                strncasecmp(Z_STRVAL(z_args[i]), "+inf", 4) == 0 ||
+                is_numeric_string(Z_STRVAL(z_args[i]), Z_STRLEN(z_args[i]), NULL, NULL, 0) != 0
+            ) {
+                redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[i]), Z_STRLEN(z_args[i]));
+                break;
+            }
+            // fall through
+        default:
+            php_error_docref(NULL, E_WARNING, "Scores must be numeric or '-inf','+inf'");
+            smart_string_free(&cmdstr);
+            efree(z_args);
+            return FAILURE;
         }
         // serialize value if requested
         val_free = redis_pack(redis_sock, &z_args[i+1], &val, &val_len);

From 477682e6bfafe6f2bedc8aa1f905950ebb452a0a Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 8 Dec 2020 09:35:33 +0200
Subject: [PATCH 1411/1986] TravisCI: add PHP 8.0

---
 .travis.yml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index 124c0727ec..b9ebc14d19 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,7 @@ php:
   - 7.2
   - 7.3
   - 7.4
+  - 8.0
   - nightly
 env: CC=gcc
 matrix:
@@ -13,6 +14,8 @@ matrix:
       env: CC=clang
     - php: 7.4
       env: CC=clang
+    - php: 8.0
+      env: CC=clang
     - php: nightly
   include:
     - php: 7.0
@@ -25,6 +28,8 @@ matrix:
       env: CC=clang
     - php: 7.4
       env: CC=clang
+    - php: 8.0
+      env: CC=clang
 addons:
   apt:
     update: true

From de985150cf35b04f185ca00029ef8ae7b32c8562 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 8 Dec 2020 09:14:55 +0200
Subject: [PATCH 1412/1986] Remove aliases for all methods.

---
 redis.c       | 31 -------------------------------
 redis_array.c |  2 --
 2 files changed, 33 deletions(-)

diff --git a/redis.c b/redis.c
index 821e8bcbba..0f3a230ec0 100644
--- a/redis.c
+++ b/redis.c
@@ -458,37 +458,6 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC)
-
-     /* Mark all of these aliases deprecated.  They aren't actual Redis commands. */
-     PHP_MALIAS(Redis, delete, del, arginfo_del, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, getKeys, keys, arginfo_keys, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, lGet, lindex, arginfo_lindex, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, lGetRange, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, lRemove, lrem, arginfo_lrem, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, lSize, lLen, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, listTrim, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, renameKey, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, sContains, sismember, arginfo_key_value, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, sRemove, srem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, sSize, scard, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, setTimeout, expire, arginfo_expire, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zDelete, zRem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zDeleteRangeByRank, zRemRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zDeleteRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zInter, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zRemove, zRem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zRemoveRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zReverseRange, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_MALIAS(Redis, zUnion, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
      PHP_FE_END
 };
 
diff --git a/redis_array.c b/redis_array.c
index de330a0616..aa0fd718de 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -127,8 +127,6 @@ zend_function_entry redis_array_functions[] = {
      PHP_ME(RedisArray, setOption,arginfo_setopt, ZEND_ACC_PUBLIC)
      PHP_ME(RedisArray, unlink, arginfo_void, ZEND_ACC_PUBLIC)
      PHP_ME(RedisArray, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_MALIAS(RedisArray, delete, del, arginfo_del, ZEND_ACC_PUBLIC)
-     PHP_MALIAS(RedisArray, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC)
      PHP_FE_END
 };
 

From 7d67749b81e73b11236e9e64b9e4d46aa5fed238 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 12 Dec 2020 20:54:39 +0200
Subject: [PATCH 1413/1986] Remove odd PHPREDIS_ZVAL_IS_STRICT_FALSE macro

---
 common.h      |  1 -
 redis_array.c | 11 ++++-------
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/common.h b/common.h
index 80b34e6568..b84f60a7ad 100644
--- a/common.h
+++ b/common.h
@@ -12,7 +12,6 @@
 #include 
 #include 
 
-#define PHPREDIS_ZVAL_IS_STRICT_FALSE(z) (Z_TYPE_P(z) == IS_FALSE)
 #define PHPREDIS_GET_OBJECT(class_entry, o) (class_entry *)((char *)o - XtOffsetOf(class_entry, std))
 #define PHPREDIS_ZVAL_GET_OBJECT(class_entry, z) PHPREDIS_GET_OBJECT(class_entry, Z_OBJ_P(z))
 
diff --git a/redis_array.c b/redis_array.c
index aa0fd718de..4c0479a48a 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -19,19 +19,16 @@
 #include "config.h"
 #endif
 
-#include "common.h"
-#include "ext/standard/info.h"
-#include "php_ini.h"
-#include "php_redis.h"
-#include 
-
 #include "library.h"
 #include "redis_array.h"
 #include "redis_array_impl.h"
 
+#include 
+#include 
+
 /* Simple macro to detect failure in a RedisArray call */
 #define RA_CALL_FAILED(rv, cmd) ( \
-    PHPREDIS_ZVAL_IS_STRICT_FALSE(rv) || \
+    (Z_TYPE_P(rv) == IS_FALSE) || \
     (Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \
     (Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE")) \
 )

From ee82299666feaab51b87015ad7914f498a98d761 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 13 Dec 2020 09:57:12 +0200
Subject: [PATCH 1414/1986] Add accidentally removed header

---
 redis_array.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/redis_array.c b/redis_array.c
index 4c0479a48a..ff7d8ba6e6 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -19,6 +19,7 @@
 #include "config.h"
 #endif
 
+#include "common.h"
 #include "library.h"
 #include "redis_array.h"
 #include "redis_array_impl.h"
@@ -333,7 +334,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd,
 
     /* pass call through */
     ZVAL_STRINGL(&z_fun, cmd, cmd_len); /* method name */
-    z_callargs = ecalloc(argc, sizeof(zval));
+    z_callargs = ecalloc(argc, sizeof(*z_callargs));
 
     /* copy args to array */
     i = 0;

From 0fa1046fb6bf83e4fd4dd864e5093cb3620508c2 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 21 Dec 2020 10:04:24 +0200
Subject: [PATCH 1415/1986] Issue #1893

On some locales `snprintf` uses comma as radix character.
Simply replace comma with point to make valid float value.
---
 library.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/library.c b/library.c
index 9008ffe0b7..a0a0880d7d 100644
--- a/library.c
+++ b/library.c
@@ -947,12 +947,15 @@ int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) {
 int
 redis_cmd_append_sstr_dbl(smart_string *str, double value)
 {
-    char tmp[64];
+    char tmp[64], *p;
     int len;
 
     /* Convert to string */
     len = snprintf(tmp, sizeof(tmp), "%.17g", value);
 
+    /* snprintf depends on locale, replace comma with point */
+    if ((p = strchr(tmp, ',')) != NULL) *p = '.';
+
     // Append the string
     return redis_cmd_append_sstr(str, tmp, len);
 }

From 62153473209d5b5107e8b2caa40011b76d99d85f Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 21 Dec 2020 23:12:21 +0200
Subject: [PATCH 1416/1986] [WIP] Issue #1894

Add Redis::sMisMember command.
---
 php_redis.h         |  1 +
 redis.c             |  7 +++++++
 tests/RedisTest.php | 21 +++++++++++++++++++++
 3 files changed, 29 insertions(+)

diff --git a/php_redis.h b/php_redis.h
index 24ba89a77e..88e9178f86 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -94,6 +94,7 @@ PHP_METHOD(Redis, sDiffStore);
 PHP_METHOD(Redis, sInter);
 PHP_METHOD(Redis, sInterStore);
 PHP_METHOD(Redis, sMembers);
+PHP_METHOD(Redis, sMisMember);
 PHP_METHOD(Redis, sMove);
 PHP_METHOD(Redis, sPop);
 PHP_METHOD(Redis, sRandMember);
diff --git a/redis.c b/redis.c
index 0f3a230ec0..474d21885f 100644
--- a/redis.c
+++ b/redis.c
@@ -385,6 +385,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, sInter, arginfo_nkeys, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, sInterStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, sMembers, arginfo_key, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, sMisMember, arginfo_key_members, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC)
@@ -1653,6 +1654,12 @@ PHP_METHOD(Redis, sMembers)
     REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd,
         redis_sock_read_multibulk_reply);
 }
+
+/* {{{ proto array Redis::sMisMember(string key, string member0, ...memberN) */
+PHP_METHOD(Redis, sMisMember)
+{
+    REDIS_PROCESS_KW_CMD("SMISMEMBER", redis_key_varval_cmd, redis_sock_read_multibulk_reply);
+}
 /* }}} */
 
 /* {{{ proto array Redis::sInter(string key0, ... string keyN) */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c9b147b87b..6511b7ee5c 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1455,6 +1455,27 @@ public function testsmembers()
         $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([1, 0, 1], $smembers);
+
+        $misMembers = $this->redis->sMisMember('wrongkey', 'val', 'val2', 'val3');
+        $this->assertEquals([0, 0, 0], $misMembers);
+    }
+
     public function testlSet() {
 
         $this->redis->del('list');

From 69e1a1be39f9db54885cded8fb2ac486da714273 Mon Sep 17 00:00:00 2001
From: Mr Bleu <40758407+JGodin-C2C@users.noreply.github.com>
Date: Fri, 15 Jan 2021 20:16:59 +0100
Subject: [PATCH 1417/1986] Update rpm packer for phpredis (#1904)

---
 rpm/README.md      |  3 +++
 rpm/php-redis.spec | 48 ----------------------------------------------
 rpm/redis.ini      |  1 -
 3 files changed, 3 insertions(+), 49 deletions(-)
 create mode 100644 rpm/README.md
 delete mode 100644 rpm/php-redis.spec
 delete mode 100644 rpm/redis.ini

diff --git a/rpm/README.md b/rpm/README.md
new file mode 100644
index 0000000000..ac51cbe38e
--- /dev/null
+++ b/rpm/README.md
@@ -0,0 +1,3 @@
+You can find and up to date version of this RPM builder here :
+
+https://src.fedoraproject.org/rpms/php-pecl-redis5/tree/master
diff --git a/rpm/php-redis.spec b/rpm/php-redis.spec
deleted file mode 100644
index 5363d1eead..0000000000
--- a/rpm/php-redis.spec
+++ /dev/null
@@ -1,48 +0,0 @@
-%global php_apiver  %((echo 0; php -i 2>/dev/null | sed -n 's/^PHP API => //p') | tail -1)
-%global php_extdir  %(php-config --extension-dir 2>/dev/null || echo "undefined")
-%global php_version %(php-config --version 2>/dev/null || echo 0)
-
-Name:           php-redis
-Version:        2.2.5
-Release:        1%{?dist}
-Summary:        The phpredis extension provides an API for communicating with the Redis key-value store.
-
-Group:          Development/Languages
-License:        PHP
-URL:            https://github.com/nicolasff/phpredis
-Source0:        https://github.com/nicolasff/phpredis/tarball/master
-Source1:	redis.ini
-BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-
-BuildRequires:  php-devel
-Requires:       php(zend-abi) = %{php_zend_api}
-Requires:       php(api) = %{php_apiver}
-
-%description
-The phpredis extension provides an API for communicating with the Redis key-value store.
-
-%prep
-%setup -q -n nicolasff-phpredis-43bc590
-
-%build
-%{_bindir}/phpize
-%configure
-make %{?_smp_mflags}
-
-%install
-rm -rf $RPM_BUILD_ROOT
-make install INSTALL_ROOT=$RPM_BUILD_ROOT
-
-# install configuration
-%{__mkdir} -p $RPM_BUILD_ROOT%{_sysconfdir}/php.d
-%{__cp} %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/php.d/redis.ini
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%files
-%defattr(-,root,root,-)
-%doc CREDITS
-%config(noreplace) %{_sysconfdir}/php.d/redis.ini
-%{php_extdir}/redis.so
-
diff --git a/rpm/redis.ini b/rpm/redis.ini
deleted file mode 100644
index 6aecae4895..0000000000
--- a/rpm/redis.ini
+++ /dev/null
@@ -1 +0,0 @@
-extension=redis.so

From a024a9a2bcbdd92c86656223e06a203f9f0a6022 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 17 Jan 2021 20:42:40 +0200
Subject: [PATCH 1418/1986] Fix Redis::sMisMember.

---
 redis.c             | 2 +-
 tests/RedisTest.php | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/redis.c b/redis.c
index 474d21885f..68fee9bc2a 100644
--- a/redis.c
+++ b/redis.c
@@ -1658,7 +1658,7 @@ PHP_METHOD(Redis, sMembers)
 /* {{{ proto array Redis::sMisMember(string key, string member0, ...memberN) */
 PHP_METHOD(Redis, sMisMember)
 {
-    REDIS_PROCESS_KW_CMD("SMISMEMBER", redis_key_varval_cmd, redis_sock_read_multibulk_reply);
+    REDIS_PROCESS_KW_CMD("SMISMEMBER", redis_key_varval_cmd, redis_read_variant_reply);
 }
 /* }}} */
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 6511b7ee5c..961de889a9 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1470,7 +1470,7 @@ public function testsMisMember()
         $this->redis->sAdd('set', 'val3');
 
         $misMembers = $this->redis->sMisMember('set', 'val', 'notamember', 'val3');
-        $this->assertEquals([1, 0, 1], $smembers);
+        $this->assertEquals([1, 0, 1], $misMembers);
 
         $misMembers = $this->redis->sMisMember('wrongkey', 'val', 'val2', 'val3');
         $this->assertEquals([0, 0, 0], $misMembers);

From a534a2c36bb2fab0b27fbd651ae9f64115a2821e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 17 Jan 2021 16:27:30 +0200
Subject: [PATCH 1419/1986] [WIP] Issue #1894

Add Redis::zMscore command.
---
 library.c           | 44 ++++++++++++++++++++++++++++++++++++++++++++
 library.h           |  1 +
 php_redis.h         |  1 +
 redis.c             |  8 ++++++++
 tests/RedisTest.php | 20 ++++++++++++++++++++
 5 files changed, 74 insertions(+)

diff --git a/library.c b/library.c
index a0a0880d7d..9c4c37bfae 100644
--- a/library.c
+++ b/library.c
@@ -2511,6 +2511,50 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
     return 0;
 }
 
+PHP_REDIS_API int
+redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    char inbuf[4096], *line;
+    int i, numElems, len;
+    size_t buf_len;
+    zval z_multi_result;
+
+    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &buf_len) < 0) {
+        return FAILURE;
+    }
+
+    if (*inbuf != TYPE_MULTIBULK) {
+        if (IS_ATOMIC(redis_sock)) {
+            if (*inbuf == TYPE_ERR) {
+                redis_sock_set_err(redis_sock, inbuf + 1, buf_len);
+            }
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
+        }
+        return FAILURE;
+    }
+    numElems = atoi(inbuf + 1);
+
+    array_init(&z_multi_result);
+    for (i = 0; i < numElems; ++i) {
+        if ((line = redis_sock_read(redis_sock, &len)) == NULL) {
+            add_next_index_bool(&z_multi_result, 0);
+            continue;
+        }
+        add_next_index_double(&z_multi_result, atof(line));
+        efree(line);
+    }
+
+    if (IS_ATOMIC(redis_sock)) {
+        RETVAL_ZVAL(&z_multi_result, 0, 1);
+    } else {
+        add_next_index_zval(z_tab, &z_multi_result);
+    }
+
+    return SUCCESS;
+}
+
 PHP_REDIS_API void
 redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count,
                        int unserialize)
diff --git a/library.h b/library.h
index db47545dc9..428f623013 100644
--- a/library.h
+++ b/library.h
@@ -87,6 +87,7 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, Re
 PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, zend_long *iter);
 
diff --git a/php_redis.h b/php_redis.h
index 88e9178f86..92de1a4532 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -126,6 +126,7 @@ PHP_METHOD(Redis, zCard);
 PHP_METHOD(Redis, zCount);
 PHP_METHOD(Redis, zIncrBy);
 PHP_METHOD(Redis, zLexCount);
+PHP_METHOD(Redis, zMscore);
 PHP_METHOD(Redis, zPopMax);
 PHP_METHOD(Redis, zPopMin);
 PHP_METHOD(Redis, zRange);
diff --git a/redis.c b/redis.c
index 68fee9bc2a..dd3409675a 100644
--- a/redis.c
+++ b/redis.c
@@ -441,6 +441,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zIncrBy, arginfo_zincrby, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zLexCount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, zMscore, arginfo_key_members, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zPopMax, arginfo_key, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zPopMin, arginfo_key, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC)
@@ -2250,6 +2251,13 @@ PHP_METHOD(Redis, zScore)
 }
 /* }}} */
 
+/* {{{ proto array Redis::zMscore(string key, string member0, ...memberN) */
+PHP_METHOD(Redis, zMscore)
+{
+    REDIS_PROCESS_KW_CMD("ZMSCORE", redis_key_varval_cmd, redis_mbulk_reply_double);
+}
+/* }}} */
+
 /* {{{ proto long Redis::zRank(string key, string member) */
 PHP_METHOD(Redis, zRank) {
     REDIS_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, redis_long_response);
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 961de889a9..197ceb2edb 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2506,6 +2506,26 @@ public function testZLexCount() {
         }
     }
 
+    public function testzMscore()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->del('key');
+        foreach (range('a', 'c') as $c) {
+            $this->redis->zAdd('key', 1, $c);
+        }
+
+        $scores = $this->redis->zMscore('key', 'a', 'notamember', 'c');
+        $this->assertEquals([1.0, false, 1.0], $scores);
+
+        $scores = $this->redis->zMscore('wrongkey', 'a', 'b', 'c');
+        $this->assertEquals([false, false, false], $scores);
+    }
+
     public function testZRemRangeByLex() {
         if (version_compare($this->version, "2.8.9") < 0) {
             $this->MarkTestSkipped();

From 76d75a6b411a15b6b5e7a596ec452abe6765826b Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 17 Jan 2021 21:02:09 +0200
Subject: [PATCH 1420/1986] Issue #1907

Add PHP version badge from Travis config.
---
 README.markdown | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.markdown b/README.markdown
index c76b889312..10c8c79e04 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2,6 +2,7 @@
 
 [![Build Status](https://travis-ci.org/phpredis/phpredis.svg?branch=develop)](https://travis-ci.org/phpredis/phpredis)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis)
+[![PHP version from Travis config](https://img.shields.io/travis/php-v/phpredis/phpredis/develop)](https://img.shields.io/travis/php-v/phpredis/phpredis/develop)
 
 The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt).
 This code has been developed and maintained by Owlient from November 2009 to March 2011.

From e9ba9ff12e74c3483f2cb54b7fc9fb7250829a2a Mon Sep 17 00:00:00 2001
From: Emanuele Filannino 
Date: Thu, 21 Jan 2021 23:57:58 +0000
Subject: [PATCH 1421/1986] Typo when declaring a cluster with an array of
 seeds (#1914)

In the given example, the number of nodes is three, not two.
---
 cluster.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cluster.markdown b/cluster.markdown
index cecae1c99e..c16cd2da82 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -9,7 +9,7 @@ To maintain consistency with the RedisArray class, one can create and connect to
 
 #### Declaring a cluster with an array of seeds
 ~~~php
-// Create a cluster setting two nodes as seeds
+// Create a cluster setting three nodes as seeds
 $obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003'));
 
 // Connect and specify timeout and read_timeout

From c61396c4898f618373cbbd7c6adc7faf2746f953 Mon Sep 17 00:00:00 2001
From: defender-11 
Date: Sat, 23 Jan 2021 00:52:55 +0800
Subject: [PATCH 1422/1986] Fixed#1895

---
 library.c          | 4 ++++
 redis_array_impl.c | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/library.c b/library.c
index a0a0880d7d..1ba210b93a 100644
--- a/library.c
+++ b/library.c
@@ -721,7 +721,11 @@ static zend_string *redis_hash_auth(zend_string *user, zend_string *pass) {
     smart_str_appendl_ex(&salted, REDIS_G(salt), sizeof(REDIS_G(salt)), 0);
 
     ctx = emalloc(ops->context_size);
+#if PHP_VERSION_ID >= 80100
+    ops->hash_init(ctx,NULL);
+#else
     ops->hash_init(ctx);
+#endif
     ops->hash_update(ctx, (const unsigned char *)ZSTR_VAL(salted.s), ZSTR_LEN(salted.s));
 
     digest = emalloc(ops->digest_size);
diff --git a/redis_array_impl.c b/redis_array_impl.c
index e34d43d158..37a84ba794 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -494,7 +494,11 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos)
             void *ctx = emalloc(ops->context_size);
             unsigned char *digest = emalloc(ops->digest_size);
 
+#if PHP_VERSION_ID >= 80100
+            ops->hash_init(ctx,NULL);
+#else
             ops->hash_init(ctx);
+#endif
             ops->hash_update(ctx, (const unsigned char *)ZSTR_VAL(out), ZSTR_LEN(out));
             ops->hash_final(digest, ctx);
 

From f3ad8e20a3ad596622e9e44af07a15df297de5aa Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 29 Jan 2021 19:17:51 +0200
Subject: [PATCH 1423/1986] [WIP] Issue #1894

Add Redis::lMove command.
---
 php_redis.h         |  1 +
 redis.c             | 33 ++++++++++++++++++++++++---------
 redis_commands.c    | 35 ++++++++++++++++++++++++++++++++++-
 redis_commands.h    |  3 +++
 tests/RedisTest.php | 24 ++++++++++++++++++++++++
 5 files changed, 86 insertions(+), 10 deletions(-)

diff --git a/php_redis.h b/php_redis.h
index 92de1a4532..ab6533dd68 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -62,6 +62,7 @@ PHP_METHOD(Redis, info);
 PHP_METHOD(Redis, keys);
 PHP_METHOD(Redis, lInsert);
 PHP_METHOD(Redis, lLen);
+PHP_METHOD(Redis, lMove);
 PHP_METHOD(Redis, lPop);
 PHP_METHOD(Redis, lPush);
 PHP_METHOD(Redis, lPushx);
diff --git a/redis.c b/redis.c
index dd3409675a..8beeef2b7d 100644
--- a/redis.c
+++ b/redis.c
@@ -158,6 +158,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1)
     ZEND_ARG_ARRAY_INFO(0, keys, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_lmove, 0, 0, 4)
+    ZEND_ARG_INFO(0, source)
+    ZEND_ARG_INFO(0, destination)
+    ZEND_ARG_INFO(0, wherefrom)
+    ZEND_ARG_INFO(0, whereto)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_exists, 0, 0, 1)
     ZEND_ARG_INFO(0, key)
     ZEND_ARG_VARIADIC_INFO(0, other_keys)
@@ -337,6 +344,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, keys, arginfo_keys, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, lLen, arginfo_key, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, lMove, arginfo_lmove, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, lPop, arginfo_key, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, lPush, arginfo_key_value, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, lPushx, arginfo_key_value, ZEND_ACC_PUBLIC)
@@ -667,11 +675,6 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) {
     zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH);
     zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STREAM"), REDIS_STREAM);
 
-    /* Cluster doesn't support pipelining at this time */
-    if(!is_cluster) {
-        zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE);
-    }
-
     /* Add common mode constants */
     zend_declare_class_constant_long(ce, ZEND_STRL("ATOMIC"), ATOMIC);
     zend_declare_class_constant_long(ce, ZEND_STRL("MULTI"), MULTI);
@@ -724,17 +727,23 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) {
     zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_PREFIX"), REDIS_SCAN_PREFIX);
     zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NOPREFIX"), REDIS_SCAN_NOPREFIX);
 
-    /* Cluster option to allow for slave failover */
+    zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5);
+    zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6);
+
     if (is_cluster) {
+        /* Cluster option to allow for slave failover */
         zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SLAVE_FAILOVER"), REDIS_OPT_FAILOVER);
         zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_NONE"), REDIS_FAILOVER_NONE);
         zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_ERROR"), REDIS_FAILOVER_ERROR);
         zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE);
         zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES);
-    }
+    } else {
+        /* Cluster doesn't support pipelining at this time */
+        zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE);
 
-    zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5);
-    zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6);
+        zend_declare_class_constant_stringl(ce, "LEFT", 4, "left", 4);
+        zend_declare_class_constant_stringl(ce, "RIGHT", 5, "right", 5);
+    }
 }
 
 static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor)
@@ -1529,6 +1538,12 @@ PHP_METHOD(Redis, lLen)
 }
 /* }}} */
 
+/* {{{ proto string Redis::lMove(string source, string destination, string wherefrom, string whereto) */
+PHP_METHOD(Redis, lMove)
+{
+    REDIS_PROCESS_CMD(lmove, redis_string_response);
+}
+
 /* {{{ proto boolean Redis::lrem(string list, string value, int count = 0) */
 PHP_METHOD(Redis, lrem)
 {
diff --git a/redis_commands.c b/redis_commands.c
index d8f1b15dca..85e23e462c 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2139,6 +2139,39 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    char *src, *dst, *from, *to;
+    size_t src_len, dst_len, from_len, to_len;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                &src, &src_len, &dst, &dst_len,
+                                &from, &from_len, &to, &to_len) == FAILURE
+    ) {
+        return FAILURE;
+    }
+
+    // Validate wherefrom/whereto
+    if (strcasecmp(from, "left") != 0 && strcasecmp(from, "right") != 0) {
+        php_error_docref(NULL, E_WARNING,
+            "Wherefrom argument must be either 'LEFT' or 'RIGHT'");
+        return FAILURE;
+    } else if (strcasecmp(to, "left") != 0 && strcasecmp(to, "right") != 0) {
+        php_error_docref(NULL, E_WARNING,
+            "Whereto argument must be either 'LEFT' or 'RIGHT'");
+        return FAILURE;
+    }
+
+    /* Construct command */
+    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "LMOVE", "kkss",
+                                  src, src_len, dst, dst_len,
+                                  from, from_len, to, to_len);
+
+    return SUCCESS;
+}
+
 /* LINSERT */
 int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
@@ -2154,7 +2187,7 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     // Validate position
-    if (strncasecmp(pos, "after", 5) && strncasecmp(pos, "before", 6)) {
+    if (strcasecmp(pos, "after") && strcasecmp(pos, "before")) {
         php_error_docref(NULL, E_WARNING,
             "Position must be either 'BEFORE' or 'AFTER'");
         return FAILURE;
diff --git a/redis_commands.h b/redis_commands.h
index 54bf7ee8f4..a641851eff 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -189,6 +189,9 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 197ceb2edb..045bd4f825 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1186,6 +1186,30 @@ public function testLindex() {
         $this->assertEquals('val4', $this->redis->lIndex('list', -1));
     }
 
+    public function testlMove()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->del('list0', 'list1');
+        $this->redis->lPush('list0', 'a');
+        $this->redis->lPush('list0', 'b');
+        $this->redis->lPush('list0', 'c');
+
+        $return = $this->redis->lMove('list0', 'list1', Redis::LEFT, Redis::RIGHT);
+        $this->assertEquals('c', $return);
+
+        $return = $this->redis->lMove('list0', 'list1', Redis::RIGHT, Redis::LEFT);
+        $this->assertEquals('a', $return);
+
+        $this->assertEquals(['b'], $this->redis->lRange('list0', 0, -1));
+        $this->assertEquals(['a', 'c'], $this->redis->lRange('list1', 0, -1));
+
+    }
+
     // lRem testing
     public function testlrem() {
         $this->redis->del('list');

From b3e5a7e27b652e0672ed03139ff7538be7699af4 Mon Sep 17 00:00:00 2001
From: Poplary 
Date: Fri, 5 Feb 2021 16:49:23 +0800
Subject: [PATCH 1424/1986] Fixed README, add the missing single quote mark.

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 10c8c79e04..1de15c3776 100644
--- a/README.markdown
+++ b/README.markdown
@@ -294,7 +294,7 @@ $redis->auth(['phpredis', 'haxx00r']);
 $redis->auth(['foobared']);
 
 /* You can also use an associative array specifying user and pass */
-$redis->auth(['user' => 'phpredis', 'pass' => 'phpredis]);
+$redis->auth(['user' => 'phpredis', 'pass' => 'phpredis']);
 $redis->auth(['pass' => 'phpredis']);
 ~~~
 

From 5eb58a4c790b5e41869161af2ab60cb103d669c2 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 20 Feb 2021 23:10:29 +0200
Subject: [PATCH 1425/1986] [WIP] Issue #1894

Add RedisSentinel::myid command
---
 redis_sentinel.c            |  6 ++++++
 redis_sentinel.h            |  1 +
 tests/RedisSentinelTest.php | 11 +++++++++++
 3 files changed, 18 insertions(+)

diff --git a/redis_sentinel.c b/redis_sentinel.c
index cbdf331cf5..6e125f5213 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -40,6 +40,7 @@ zend_function_entry redis_sentinel_functions[] = {
      PHP_ME(RedisSentinel, getMasterAddrByName, arginfo_value, ZEND_ACC_PUBLIC)
      PHP_ME(RedisSentinel, master, arginfo_value, ZEND_ACC_PUBLIC)
      PHP_ME(RedisSentinel, masters, arginfo_void, ZEND_ACC_PUBLIC)
+     PHP_ME(RedisSentinel, myid, arginfo_void, ZEND_ACC_PUBLIC)
      PHP_ME(RedisSentinel, ping, arginfo_void, ZEND_ACC_PUBLIC)
      PHP_ME(RedisSentinel, reset, arginfo_value, ZEND_ACC_PUBLIC)
      PHP_ME(RedisSentinel, sentinels, arginfo_value, ZEND_ACC_PUBLIC)
@@ -132,6 +133,11 @@ PHP_METHOD(RedisSentinel, masters)
     REDIS_PROCESS_KW_CMD("masters", redis_sentinel_cmd, sentinel_mbulk_reply_zipped_assoc);
 }
 
+PHP_METHOD(RedisSentinel, myid)
+{
+    REDIS_PROCESS_KW_CMD("myid", redis_sentinel_cmd, redis_string_response);
+}
+
 PHP_METHOD(RedisSentinel, ping)
 {
     REDIS_PROCESS_KW_CMD("PING", redis_empty_cmd, redis_boolean_response);
diff --git a/redis_sentinel.h b/redis_sentinel.h
index 651cc1b822..b09ce0cfe1 100644
--- a/redis_sentinel.h
+++ b/redis_sentinel.h
@@ -12,6 +12,7 @@ PHP_METHOD(RedisSentinel, flushconfig);
 PHP_METHOD(RedisSentinel, getMasterAddrByName);
 PHP_METHOD(RedisSentinel, master);
 PHP_METHOD(RedisSentinel, masters);
+PHP_METHOD(RedisSentinel, myid);
 PHP_METHOD(RedisSentinel, ping);
 PHP_METHOD(RedisSentinel, reset);
 PHP_METHOD(RedisSentinel, sentinels);
diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php
index b88e006477..776afbf3f8 100644
--- a/tests/RedisSentinelTest.php
+++ b/tests/RedisSentinelTest.php
@@ -83,6 +83,17 @@ public function testMasters()
         }
     }
 
+    public function testMyid()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+        $result = $this->sentinel->myid();
+        $this->assertTrue(is_string($result));
+    }
+
     public function testPing()
     {
         $this->assertTrue($this->sentinel->ping());

From 1f2a7ef6b5ea9c032e6075b2c21e0cf57bd11c3f Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 25 Feb 2021 09:18:05 +0200
Subject: [PATCH 1426/1986] TravisCI: sentinel config

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index b9ebc14d19..2a789ec69b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -55,7 +55,7 @@ before_script:
   - redis-server --port 0 --daemonize yes --aclfile tests/users.acl --unixsocket /tmp/redis.sock
   - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes --aclfile tests/users.acl; done
   - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
-  - for PORT in $(seq 26379 26380); do wget download.redis.io/redis-stable/sentinel.conf -O $PORT.conf; echo sentinel auth-pass mymaster phpredis >> $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
+  - for PORT in $(seq 26379 26380); do wget raw.githubusercontent.com/redis/redis/6.0/sentinel.conf -O $PORT.conf; echo sentinel auth-pass mymaster phpredis >> $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
   - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 --user phpredis -a phpredis
   - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
   - openssl req -x509 -newkey rsa:1024 -nodes -keyout stunnel.key -out stunnel.pem -days 1 -subj '/CN=localhost'

From e61ee1da45e2c20d8d10c1802d47cfbea4c66b14 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Thu, 25 Feb 2021 10:03:53 -0800
Subject: [PATCH 1427/1986] Normalize Redis callback prototypes and stop
 typecasting. (#1935)

---
 common.h           |  24 ++++++-----
 library.c          | 104 ++++++++++++++++++++++++++++-----------------
 library.h          |  20 ++++-----
 php_redis.h        |   5 ---
 redis.c            |  23 +++++-----
 sentinel_library.c |  13 ++++--
 sentinel_library.h |   2 +-
 7 files changed, 111 insertions(+), 80 deletions(-)

diff --git a/common.h b/common.h
index b84f60a7ad..fd11bf4ed1 100644
--- a/common.h
+++ b/common.h
@@ -139,7 +139,7 @@ typedef enum {
 
 #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \
     fold_item *fi = malloc(sizeof(fold_item)); \
-    fi->fun = (void *)callback; \
+    fi->fun = callback; \
     fi->ctx = closure_context; \
     fi->next = NULL; \
     if (redis_sock->current) { \
@@ -194,7 +194,7 @@ typedef enum {
         REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \
     }
 
-/* Process a command but with a specific command building function 
+/* Process a command but with a specific command building function
  * and keyword which is passed to us*/
 #define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \
     RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \
@@ -255,12 +255,6 @@ typedef enum {
     #endif
 #endif
 
-typedef struct fold_item {
-    zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, void *, ...);
-    void *ctx;
-    struct fold_item *next;
-} fold_item;
-
 /* {{{ struct RedisSock */
 typedef struct {
     php_stream         *stream;
@@ -285,8 +279,8 @@ typedef struct {
     zend_string        *prefix;
 
     short              mode;
-    fold_item          *head;
-    fold_item          *current;
+    struct fold_item   *head;
+    struct fold_item   *current;
 
     zend_string        *pipeline_cmd;
 
@@ -301,6 +295,16 @@ typedef struct {
 } RedisSock;
 /* }}} */
 
+/* Redis response handler function callback prototype */
+typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*, zval*, void*);
+
+typedef struct fold_item {
+    FailableResultCallback fun;
+    void *ctx;
+    struct fold_item *next;
+} fold_item;
+
 typedef struct {
     zend_llist list;
     int nb_active;
diff --git a/library.c b/library.c
index d7b85d8b84..6ec4a24d80 100644
--- a/library.c
+++ b/library.c
@@ -1013,7 +1013,8 @@ int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulon
     return redis_cmd_append_sstr(cmd, arg, len);
 }
 
-PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API int
+redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
 
     char *response;
     int response_len;
@@ -1021,32 +1022,36 @@ PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redi
 
     if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
         if (IS_ATOMIC(redis_sock)) {
-            RETURN_FALSE;
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
         }
-        add_next_index_bool(z_tab, 0);
-        return;
+        return FAILURE;
     }
 
     ret = atof(response);
     efree(response);
     if (IS_ATOMIC(redis_sock)) {
-        RETURN_DOUBLE(ret);
+        RETVAL_DOUBLE(ret);
     } else {
         add_next_index_double(z_tab, ret);
     }
+
+    return SUCCESS;
 }
 
-PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
     char *response;
     int response_len;
     long l;
 
     if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
         if (IS_ATOMIC(redis_sock)) {
-            RETURN_FALSE;
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
         }
-        add_next_index_bool(z_tab, 0);
-        return;
+        return FAILURE;
     }
 
     if (strncmp(response, "+string", 7) == 0) {
@@ -1067,20 +1072,23 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
 
     efree(response);
     if (IS_ATOMIC(redis_sock)) {
-        RETURN_LONG(l);
+        RETVAL_LONG(l);
     } else {
         add_next_index_long(z_tab, l);
     }
+
+    return SUCCESS;
 }
 
-PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
     char *response;
     int response_len;
     zval z_ret;
 
     /* Read bulk response */
     if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
-        RETURN_FALSE;
+        RETVAL_FALSE;
+        return FAILURE;
     }
 
     /* Parse it into a zval array */
@@ -1095,6 +1103,8 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
     } else {
         add_next_index_zval(z_tab, &z_ret);
     }
+
+    return SUCCESS;
 }
 
 PHP_REDIS_API void
@@ -1151,14 +1161,16 @@ redis_parse_info_response(char *response, zval *z_ret)
  * Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code
  * to handle.
  */
-PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
+PHP_REDIS_API int
+redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
     char *resp;
     int resp_len;
     zval z_ret;
 
     /* Make sure we can read the bulk response from Redis */
     if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
-        RETURN_FALSE;
+        RETVAL_FALSE;
+        return FAILURE;
     }
 
     /* Parse it out */
@@ -1173,6 +1185,8 @@ PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSo
     } else {
         add_next_index_zval(z_tab, &z_ret);
     }
+
+    return SUCCESS;
 }
 
 PHP_REDIS_API void
@@ -1270,7 +1284,7 @@ redis_parse_client_list_response(char *response, zval *z_ret)
     }
 }
 
-PHP_REDIS_API void
+PHP_REDIS_API int
 redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                             zval *z_tab, void *ctx,
                             SuccessCallback success_callback)
@@ -1289,36 +1303,38 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         success_callback(redis_sock);
     }
     if (IS_ATOMIC(redis_sock)) {
-        RETURN_BOOL(ret);
+        RETVAL_BOOL(ret);
     } else {
         add_next_index_bool(z_tab, ret);
     }
+
+    return ret ? SUCCESS : FAILURE;
 }
 
-PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS,
+PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS,
                                    RedisSock *redis_sock, zval *z_tab,
                                    void *ctx)
 {
-    redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        z_tab, ctx, NULL);
+    return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+                                       z_tab, ctx, NULL);
 }
 
-PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS,
-                                RedisSock *redis_sock, zval * z_tab,
-                                void *ctx)
+PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS,
+                                      RedisSock *redis_sock, zval * z_tab,
+                                      void *ctx)
 {
 
     char *response;
     int response_len;
 
-    if ((response = redis_sock_read(redis_sock, &response_len))
-                                    == NULL)
-    {
+    if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
         if (IS_ATOMIC(redis_sock)) {
-            RETURN_FALSE;
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
         }
-        add_next_index_bool(z_tab, 0);
-        return;
+
+        return FAILURE;
     }
 
     if(response[0] == ':') {
@@ -1343,8 +1359,12 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS,
         } else {
             add_next_index_null(z_tab);
         }
+        efree(response);
+        return FAILURE;
     }
+
     efree(response);
+    return SUCCESS;
 }
 
 /* Helper method to convert [key, value, key, value] into [key => value,
@@ -1925,7 +1945,7 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, Re
         z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE);
 }
 
-PHP_REDIS_API void
+PHP_REDIS_API int
 redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
     char *response;
@@ -1938,13 +1958,15 @@ redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ta
     }
 
     if (IS_ATOMIC(redis_sock)) {
-        RETURN_BOOL(ret);
+        RETVAL_BOOL(ret);
     } else {
         add_next_index_bool(z_tab, ret);
     }
+
+    return ret ? SUCCESS : FAILURE;
 }
 
-PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
 
     char *response;
     int response_len;
@@ -1953,10 +1975,11 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
                                     == NULL)
     {
         if (IS_ATOMIC(redis_sock)) {
-            RETURN_FALSE;
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
         }
-        add_next_index_bool(z_tab, 0);
-        return;
+        return FAILURE;
     }
     if (IS_ATOMIC(redis_sock)) {
         if (!redis_unpack(redis_sock, response, response_len, return_value)) {
@@ -1970,7 +1993,9 @@ PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
             add_next_index_stringl(z_tab, response, response_len);
         }
     }
+
     efree(response);
+    return SUCCESS;
 }
 
 PHP_REDIS_API
@@ -1998,7 +2023,7 @@ void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock
 }
 
 /* like string response, but never unserialized. */
-PHP_REDIS_API void
+PHP_REDIS_API int
 redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     zval *z_tab, void *ctx)
 {
@@ -2010,17 +2035,20 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                                     == NULL)
     {
         if (IS_ATOMIC(redis_sock)) {
-            RETURN_FALSE;
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
         }
-        add_next_index_bool(z_tab, 0);
-        return;
+        return FAILURE;
     }
     if (IS_ATOMIC(redis_sock)) {
         RETVAL_STRINGL(response, response_len);
     } else {
         add_next_index_stringl(z_tab, response, response_len);
     }
+
     efree(response);
+    return SUCCESS;
 }
 
 /* Response for DEBUG object which is a formatted single line reply */
diff --git a/library.h b/library.h
index 428f623013..a7938735ab 100644
--- a/library.h
+++ b/library.h
@@ -49,20 +49,20 @@ PHP_REDIS_API zend_string *redis_pool_spprintf(RedisSock *redis_sock, char *fmt,
 
 PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len);
 PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len);
-PHP_REDIS_API void redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx);
+PHP_REDIS_API int redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx);
 typedef void (*SuccessCallback)(RedisSock *redis_sock);
-PHP_REDIS_API void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback);
-PHP_REDIS_API void redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback);
+PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     zval *z_tab, void *ctx);
-PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret);
 PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret);
-PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, int port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval);
 PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock);
 PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock);
@@ -149,7 +149,7 @@ PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, long lon
 PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
+PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 /* Helper methods to get configuration values from a HashTable. */
 
diff --git a/php_redis.h b/php_redis.h
index ab6533dd68..e3ed29c8f7 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -276,11 +276,6 @@ PHP_MINIT_FUNCTION(redis);
 PHP_MSHUTDOWN_FUNCTION(redis);
 PHP_MINFO_FUNCTION(redis);
 
-/* Redis response handler function callback prototype */
-typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-
-typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*, zval*, void*);
-
 PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent);
 
 PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock);
diff --git a/redis.c b/redis.c
index 8beeef2b7d..d65699c1ff 100644
--- a/redis.c
+++ b/redis.c
@@ -1088,10 +1088,10 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
 }
 
 /* {{{ proto long Redis::bitop(string op, string key, ...) */
-PHP_METHOD(Redis, bitop)
-{
+PHP_METHOD(Redis, bitop) {
     REDIS_PROCESS_CMD(bitop, redis_long_response);
 }
+
 /* }}} */
 
 /* {{{ proto long Redis::bitcount(string key, [int start], [int end])
@@ -1227,8 +1227,7 @@ PHP_METHOD(Redis, incrBy){
 /* {{{ proto float Redis::incrByFloat(string key, float value)
  */
 PHP_METHOD(Redis, incrByFloat) {
-    REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd,
-        redis_bulk_double_response);
+    REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, redis_bulk_double_response);
 }
 /* }}} */
 
@@ -1324,10 +1323,10 @@ PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock)
     redis_sock->watching = 1;
 }
 
-PHP_REDIS_API void redis_watch_response(INTERNAL_FUNCTION_PARAMETERS,
+PHP_REDIS_API int redis_watch_response(INTERNAL_FUNCTION_PARAMETERS,
                                  RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
-    redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+    return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
         z_tab, ctx, redis_set_watch);
 }
 
@@ -1344,12 +1343,12 @@ PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock)
     redis_sock->watching = 0;
 }
 
-PHP_REDIS_API void redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS,
+PHP_REDIS_API int redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS,
                                    RedisSock *redis_sock, zval *z_tab,
                                    void *ctx)
 {
-    redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        z_tab, ctx, redis_clear_watch);
+    return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+                                       z_tab, ctx, redis_clear_watch);
 }
 
 /* {{{ proto boolean Redis::unwatch()
@@ -2047,7 +2046,7 @@ PHP_METHOD(Redis, move) {
 /* }}} */
 
 static
-void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun)
+void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, FailableResultCallback fun)
 {
     RedisSock *redis_sock;
     smart_string cmd = {0};
@@ -2093,6 +2092,7 @@ void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, ResultCallback fun)
     if (IS_ATOMIC(redis_sock)) {
         fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
     }
+
     REDIS_PROCESS_RESPONSE(fun);
 }
 
@@ -3475,8 +3475,7 @@ PHP_METHOD(Redis, client) {
     /* We handle CLIENT LIST with a custom response function */
     if(!strncasecmp(opt, "list", 4)) {
         if (IS_ATOMIC(redis_sock)) {
-            redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,
-                NULL);
+            redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, NULL, NULL);
         }
         REDIS_PROCESS_RESPONSE(redis_client_list_reply);
     } else {
diff --git a/sentinel_library.c b/sentinel_library.c
index 481985467f..0fe64cc145 100644
--- a/sentinel_library.c
+++ b/sentinel_library.c
@@ -30,7 +30,7 @@ create_sentinel_object(zend_class_entry *ce)
     return &obj->std;
 }
 
-PHP_REDIS_API void
+PHP_REDIS_API int
 sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
     char inbuf[4096];
@@ -40,14 +40,17 @@ sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis
 
     /* Throws exception on failure */
     if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
-        RETURN_FALSE;
+        RETVAL_FALSE;
+        return FAILURE;
     }
 
     if (*inbuf != TYPE_MULTIBULK) {
         if (*inbuf == TYPE_ERR) {
             redis_sock_set_err(redis_sock, inbuf + 1, len - 1);
         }
-        RETURN_FALSE;
+
+        RETVAL_FALSE;
+        return FAILURE;
     }
     array_init(&z_ret);
     nelem = atoi(inbuf + 1);
@@ -57,5 +60,7 @@ sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis
         redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
         add_next_index_zval(&z_ret, return_value);
     }
-    RETURN_ZVAL(&z_ret, 0, 1);
+
+    RETVAL_ZVAL(&z_ret, 0, 1);
+    return SUCCESS;
 }
diff --git a/sentinel_library.h b/sentinel_library.h
index 460ccfad1d..88d9a564e7 100644
--- a/sentinel_library.h
+++ b/sentinel_library.h
@@ -8,6 +8,6 @@ typedef redis_object redis_sentinel_object;
 
 zend_object *create_sentinel_object(zend_class_entry *ce);
 
-PHP_REDIS_API void sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 #endif /* REDIS_SENTINEL_LIBRARY_H */

From 8400ed1cb23a22f70727cb60e88ca5397ee10d23 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 26 Feb 2021 09:52:10 -0800
Subject: [PATCH 1428/1986] Fix ZSTD decompression on bad data.

ZSTD uses two defined error numbers to inform the caller when the
compressed data is invalid (e.g. wrong magic number) or the size is
unknown.

We should always know the size so abort if ZSTD returns either to us.

Fixes: #1936
---
 library.c           | 3 ++-
 tests/RedisTest.php | 8 ++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/library.c b/library.c
index 6ec4a24d80..f3118cc437 100644
--- a/library.c
+++ b/library.c
@@ -2952,7 +2952,8 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
                 size_t len;
 
                 len = ZSTD_getFrameContentSize(val, val_len);
-                if (len >= 0) {
+
+                if (len != ZSTD_CONTENTSIZE_ERROR && len != ZSTD_CONTENTSIZE_UNKNOWN) {
                     data = emalloc(len);
                     len = ZSTD_decompress(data, len, val, val_len);
                     if (ZSTD_isError(len)) {
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 045bd4f825..f00f38ac84 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4638,6 +4638,14 @@ 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_COMPRESSION, Redis::COMPRESSION_ZSTD);
+        $this->assertEquals('123', $this->redis->get('badzstd'));
+        $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
+
         $this->checkCompression(Redis::COMPRESSION_ZSTD, 0);
         $this->checkCompression(Redis::COMPRESSION_ZSTD, 9);
     }

From 037dbbf93db7db96633b0dd21c995b64af7aa1b9 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 27 Feb 2021 23:12:23 +0200
Subject: [PATCH 1429/1986] [WIP] Issue #1894

Add Redis::copy command
---
 php_redis.h         |  1 +
 redis.c             | 13 ++++++++++++
 redis_commands.c    | 50 +++++++++++++++++++++++++++++++++++++++++++++
 redis_commands.h    |  3 +++
 tests/RedisTest.php | 21 +++++++++++++++++++
 5 files changed, 88 insertions(+)

diff --git a/php_redis.h b/php_redis.h
index ab6533dd68..ce5965f902 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -41,6 +41,7 @@ PHP_METHOD(Redis, bzPopMax);
 PHP_METHOD(Redis, bzPopMin);
 PHP_METHOD(Redis, close);
 PHP_METHOD(Redis, connect);
+PHP_METHOD(Redis, copy);
 PHP_METHOD(Redis, dbSize);
 PHP_METHOD(Redis, decr);
 PHP_METHOD(Redis, decrBy);
diff --git a/redis.c b/redis.c
index 8beeef2b7d..0c4e2c293f 100644
--- a/redis.c
+++ b/redis.c
@@ -134,6 +134,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2)
     ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_copy, 0, 0, 2)
+    ZEND_ARG_INFO(0, source)
+    ZEND_ARG_INFO(0, destination)
+    ZEND_ARG_ARRAY_INFO(0, options, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0)
     ZEND_ARG_INFO(0, async)
 ZEND_END_ARG_INFO()
@@ -283,6 +289,7 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, command, arginfo_command, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, config, arginfo_config, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, connect, arginfo_connect, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, copy, arginfo_copy, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, dbSize, arginfo_void, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, debug, arginfo_key, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, decr, arginfo_key, ZEND_ACC_PUBLIC)
@@ -3535,6 +3542,12 @@ PHP_METHOD(Redis, command) {
 }
 /* }}} */
 
+/* {{{ proto array Redis::copy(string $source, string $destination, array $options = null) */
+PHP_METHOD(Redis, copy) {
+    REDIS_PROCESS_CMD(copy, redis_1_response)
+}
+/* }}} */
+
 /* Helper to format any combination of SCAN arguments */
 PHP_REDIS_API int
 redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
diff --git a/redis_commands.c b/redis_commands.c
index 85e23e462c..027fbda0e6 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3299,6 +3299,56 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+               char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    smart_string cmdstr = {0};
+    char *src, *dst;
+    size_t src_len, dst_len;
+    zend_long db = -1;
+    zend_bool replace = 0;
+    zval *opts = NULL;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a",
+                              &src, &src_len, &dst, &dst_len, &opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) {
+        zend_ulong idx;
+        zend_string *zkey;
+        zval *z_ele;
+        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(opts), idx, zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "db")) {
+                    db = zval_get_long(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "replace")) {
+                    replace = zval_is_true(z_ele);
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (db > -1) + replace, "COPY");
+    redis_cmd_append_sstr(&cmdstr, src, src_len);
+    redis_cmd_append_sstr(&cmdstr, dst, dst_len);
+
+    if (db > -1) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DB");
+        redis_cmd_append_sstr_long(&cmdstr, db);
+    }
+    if (replace) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REPLACE");
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return SUCCESS;
+}
+
 /* XADD */
 int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                    char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index a641851eff..3a3e0491bb 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -259,6 +259,9 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
     long it, char *pat, int pat_len, long count);
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 045bd4f825..9572135080 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6571,6 +6571,27 @@ public function testTlsConnect()
         }
     }
 
+    public function testCopy()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->del('key2');
+        $this->redis->set('key', 'foo');
+        $this->assertTrue($this->redis->copy('key', 'key2'));
+        $this->assertEquals('foo', $this->redis->get('key2'));
+
+        $this->redis->set('key', 'bar');
+        $this->assertFalse($this->redis->copy('key', 'key2'));
+        $this->assertEquals('foo', $this->redis->get('key2'));
+
+        $this->assertTrue($this->redis->copy('key', 'key2', ['replace' => true]));
+        $this->assertEquals('bar', $this->redis->get('key2'));
+    }
+
     public  function testSession_regenerateSessionId_noLock_noDestroy() {
         $this->setSessionHandler();
         $sessionId = $this->generateSessionId();

From dcf4b83be33ecdde345ddfc70e5c0d481e3b7aac Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 6 Mar 2021 12:30:21 +0200
Subject: [PATCH 1430/1986] TravisCI: sentinel tests

---
 .travis.yml                 | 2 +-
 redis_sentinel.c            | 2 +-
 tests/RedisSentinelTest.php | 5 -----
 3 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 2a789ec69b..95562ceb18 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -55,7 +55,7 @@ before_script:
   - redis-server --port 0 --daemonize yes --aclfile tests/users.acl --unixsocket /tmp/redis.sock
   - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes --aclfile tests/users.acl; done
   - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
-  - for PORT in $(seq 26379 26380); do wget raw.githubusercontent.com/redis/redis/6.0/sentinel.conf -O $PORT.conf; echo sentinel auth-pass mymaster phpredis >> $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
+  - for PORT in $(seq 26379 26380); do wget raw.githubusercontent.com/redis/redis/6.0/sentinel.conf -O $PORT.conf; sed -i '/^sentinel/d' $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel sentinel auth-pass mymaster phpredis; done
   - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 --user phpredis -a phpredis
   - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
   - openssl req -x509 -newkey rsa:1024 -nodes -keyout stunnel.key -out stunnel.pem -days 1 -subj '/CN=localhost'
diff --git a/redis_sentinel.c b/redis_sentinel.c
index 6e125f5213..e5148e99ae 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -140,7 +140,7 @@ PHP_METHOD(RedisSentinel, myid)
 
 PHP_METHOD(RedisSentinel, ping)
 {
-    REDIS_PROCESS_KW_CMD("PING", redis_empty_cmd, redis_boolean_response);
+    REDIS_PROCESS_KW_CMD("ping", redis_empty_cmd, redis_boolean_response);
 }
 
 PHP_METHOD(RedisSentinel, reset)
diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php
index 776afbf3f8..4d941dd95a 100644
--- a/tests/RedisSentinelTest.php
+++ b/tests/RedisSentinelTest.php
@@ -85,11 +85,6 @@ public function testMasters()
 
     public function testMyid()
     {
-        // Only available since 6.2.0
-        if (version_compare($this->version, '6.2.0') < 0) {
-            $this->markTestSkipped();
-            return;
-        }
         $result = $this->sentinel->myid();
         $this->assertTrue(is_string($result));
     }

From 963f2a9624218ce80a39327689fb05be1d776fbc Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 6 Mar 2021 12:38:56 +0200
Subject: [PATCH 1431/1986] TravisCI: fix tests

---
 .travis.yml         | 2 +-
 tests/RedisTest.php | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 95562ceb18..664f2f3848 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -55,7 +55,7 @@ before_script:
   - redis-server --port 0 --daemonize yes --aclfile tests/users.acl --unixsocket /tmp/redis.sock
   - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes --aclfile tests/users.acl; done
   - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
-  - for PORT in $(seq 26379 26380); do wget raw.githubusercontent.com/redis/redis/6.0/sentinel.conf -O $PORT.conf; sed -i '/^sentinel/d' $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel sentinel auth-pass mymaster phpredis; done
+  - for PORT in $(seq 26379 26380); do wget raw.githubusercontent.com/redis/redis/6.0/sentinel.conf -O $PORT.conf; sed -i '/^sentinel/d' $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel auth-pass mymaster phpredis; done
   - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 --user phpredis -a phpredis
   - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
   - openssl req -x509 -newkey rsa:1024 -nodes -keyout stunnel.key -out stunnel.pem -days 1 -subj '/CN=localhost'
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c8443e6581..d4b3cfe15d 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6208,7 +6208,6 @@ public function testInvalidAuthArgs() {
         $obj_new = $this->newInstance();
 
         $arr_args = [
-            NULL,
             [],
             [NULL, NULL],
             ['foo', 'bar', 'baz'],
@@ -6224,8 +6223,6 @@ public function testInvalidAuthArgs() {
             try {
                 if (is_array($arr_arg)) {
                     @call_user_func_array([$obj_new, 'auth'], $arr_arg);
-                } else {
-                    call_user_func([$obj_new, 'auth']);
                 }
             } catch (Exception $ex) {
                 unset($ex); /* Suppress intellisense warning */

From 5d50fef961d35ee8563ca7719d064b8dc7148b04 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 6 Mar 2021 20:15:36 +0200
Subject: [PATCH 1432/1986] [WIP] Issue #1894

Add Redis::zdiff and Redis::zdiffstore commands
---
 library.c           | 23 +++++++++++--
 library.h           |  2 ++
 php_redis.h         |  2 ++
 redis.c             | 24 +++++++++++++
 redis_commands.c    | 83 +++++++++++++++++++++++++++++++++++++++++++++
 redis_commands.h    |  6 ++++
 tests/RedisTest.php | 33 ++++++++++++++++++
 7 files changed, 171 insertions(+), 2 deletions(-)

diff --git a/library.c b/library.c
index f3118cc437..79847c7fd1 100644
--- a/library.c
+++ b/library.c
@@ -1284,6 +1284,15 @@ redis_parse_client_list_response(char *response, zval *z_ret)
     }
 }
 
+PHP_REDIS_API int
+redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    if (ctx == NULL) {
+        return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    }
+    return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+}
+
 PHP_REDIS_API int
 redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                             zval *z_tab, void *ctx,
@@ -1455,7 +1464,12 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     size_t len;
 
     if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
-        return -1;
+        if (IS_ATOMIC(redis_sock)) {
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
+        }
+        return FAILURE;
     }
 
     if(inbuf[0] != '*') {
@@ -2514,7 +2528,12 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
     size_t len;
 
     if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
-        return -1;
+        if (IS_ATOMIC(redis_sock)) {
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
+        }
+        return FAILURE;
     }
 
     if(inbuf[0] != '*') {
diff --git a/library.h b/library.h
index a7938735ab..0c8a55df02 100644
--- a/library.h
+++ b/library.h
@@ -151,6 +151,8 @@ PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, Red
 PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
+PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+
 /* Helper methods to get configuration values from a HashTable. */
 
 #define REDIS_HASH_STR_FIND_STATIC(ht, sstr) \
diff --git a/php_redis.h b/php_redis.h
index 9d44e5133c..975b8ab524 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -144,6 +144,8 @@ PHP_METHOD(Redis, zRevRangeByLex);
 PHP_METHOD(Redis, zRevRangeByScore);
 PHP_METHOD(Redis, zRevRank);
 PHP_METHOD(Redis, zScore);
+PHP_METHOD(Redis, zdiff);
+PHP_METHOD(Redis, zdiffstore);
 PHP_METHOD(Redis, zinterstore);
 PHP_METHOD(Redis, zunionstore);
 
diff --git a/redis.c b/redis.c
index 91eba589d1..10b0e159c1 100644
--- a/redis.c
+++ b/redis.c
@@ -115,6 +115,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 1)
     ZEND_ARG_INFO(0, retry_interval)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_zdiff, 0, 0, 1)
+    ZEND_ARG_ARRAY_INFO(0, keys, 0)
+    ZEND_ARG_ARRAY_INFO(0, options, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_zdiffstore, 0, 0, 2)
+    ZEND_ARG_INFO(0, destination)
+    ZEND_ARG_ARRAY_INFO(0, keys, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0)
     ZEND_ARG_INFO(0, option)
 ZEND_END_ARG_INFO()
@@ -472,6 +482,8 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, zRevRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zRevRank, arginfo_key_member, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, zdiff, arginfo_zdiff, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, zdiffstore, arginfo_zdiffstore, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC)
@@ -2299,6 +2311,18 @@ PHP_METHOD(Redis, zIncrBy)
 }
 /* }}} */
 
+/* {{{ proto array Redis::zdiff(array keys, array options) */
+PHP_METHOD(Redis, zdiff) {
+    REDIS_PROCESS_CMD(zdiff, redis_zdiff_response);
+}
+/* }}} */
+
+/* {{{ proto array Redis::zdiffstore(string destination, array keys) */
+PHP_METHOD(Redis, zdiffstore) {
+    REDIS_PROCESS_CMD(zdiffstore, redis_long_response);
+}
+/* }}} */
+
 /* zinterstore */
 PHP_METHOD(Redis, zinterstore) {
     REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response);
diff --git a/redis_commands.c b/redis_commands.c
index 027fbda0e6..e18690a4ec 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -625,6 +625,89 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     return SUCCESS;
 }
+int
+redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    int numkeys;
+    smart_string cmdstr = {0};
+    zval *z_keys, *z_opts = NULL, *z_ele;
+    zend_bool withscores = 0;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a",
+                              &z_keys, &z_opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) {
+        return FAILURE;
+    }
+
+    if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
+        zend_ulong idx;
+        zend_string *zkey;
+        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_opts), idx, zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "withscores")) {
+                    withscores = zval_is_true(z_ele);
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + numkeys + withscores, "ZDIFF");
+    redis_cmd_append_sstr_long(&cmdstr, numkeys);
+
+    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) {
+        ZVAL_DEREF(z_ele);
+        redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock);
+    } ZEND_HASH_FOREACH_END();
+
+    if (withscores) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES");
+        *ctx = redis_sock;
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return SUCCESS;
+}
+
+int
+redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                     char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    char *dst;
+    size_t dst_len;
+    int numkeys;
+    zval *z_keys, *z_ele;
+    smart_string cmdstr = {0};
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa",
+                              &dst, &dst_len, &z_keys) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) {
+        return FAILURE;
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + numkeys, "ZDIFFSTORE");
+    redis_cmd_append_sstr(&cmdstr, dst, dst_len);
+    redis_cmd_append_sstr_long(&cmdstr, numkeys);
+
+    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) {
+        ZVAL_DEREF(z_ele);
+        redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock);
+    } ZEND_HASH_FOREACH_END();
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return SUCCESS;
+}
 
 /* ZUNIONSTORE, ZINTERSTORE */
 int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
diff --git a/redis_commands.h b/redis_commands.h
index 3a3e0491bb..c2f790954a 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -103,6 +103,12 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, 
     void **ctx);
 
+int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
+int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index d4b3cfe15d..fcf5d811b8 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2530,6 +2530,39 @@ public function testZLexCount() {
         }
     }
 
+    public function testzDiff()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->del('key');
+        foreach (range('a', 'c') as $c) {
+            $this->redis->zAdd('key', 1, $c);
+        }
+
+        $this->assertEquals(['a', 'b', 'c'], $this->redis->zDiff(['key']));
+        $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zDiff(['key'], ['withscores' => true]));
+    }
+
+    public function testzDiffStore()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->del('key');
+        foreach (range('a', 'c') as $c) {
+            $this->redis->zAdd('key', 1, $c);
+        }
+        $this->assertEquals(3, $this->redis->zDiffStore('key2', ['key']));
+        $this->assertEquals(['a', 'b', 'c'], $this->redis->zRange('key2', 0, -1));
+    }
+
     public function testzMscore()
     {
         // Only available since 6.2.0

From 687a0b405051adada1ff460a3863d0f85cd6e98a Mon Sep 17 00:00:00 2001
From: Mike Griego 
Date: Sat, 16 Jan 2021 14:50:06 -0600
Subject: [PATCH 1433/1986] Fail if session lock can't be acquired, more sane
 lock wait defaults, and add more logging.

---
 .gitignore      |  1 +
 README.markdown |  6 ++---
 redis.c         |  4 +--
 redis_session.c | 70 +++++++++++++++++++++++++++----------------------
 4 files changed, 45 insertions(+), 36 deletions(-)

diff --git a/.gitignore b/.gitignore
index 965c536206..733e981cad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ run-tests.php
 idea/*
 .cquery
 tags
+.vscode/*
\ No newline at end of file
diff --git a/README.markdown b/README.markdown
index 10c8c79e04..71fc973a1c 100644
--- a/README.markdown
+++ b/README.markdown
@@ -91,10 +91,10 @@ Following INI variables can be used to configure session locking:
 redis.session.locking_enabled = 1
 ; How long should the lock live (in seconds)? Defaults to: value of max_execution_time.
 redis.session.lock_expire = 60
-; How long to wait between attempts to acquire lock, in microseconds (µs)?. Defaults to: 2000
+; How long to wait between attempts to acquire lock, in microseconds (µs)?. Defaults to: 20000
 redis.session.lock_wait_time = 50000
-; Maximum number of times to retry (-1 means infinite). Defaults to: 10
-redis.session.lock_retries = 10
+; Maximum number of times to retry (-1 means infinite). Defaults to: 1000
+redis.session.lock_retries = 2000
 ~~~
 
 ## Distributed Redis Array
diff --git a/redis.c b/redis.c
index 10b0e159c1..7af1095084 100644
--- a/redis.c
+++ b/redis.c
@@ -103,8 +103,8 @@ PHP_INI_BEGIN()
     /* redis session */
     PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL)
-    PHP_INI_ENTRY("redis.session.lock_retries", "10", PHP_INI_ALL, NULL)
-    PHP_INI_ENTRY("redis.session.lock_wait_time", "2000", PHP_INI_ALL, NULL)
+    PHP_INI_ENTRY("redis.session.lock_retries", "1000", PHP_INI_ALL, NULL)
+    PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL)
 PHP_INI_END()
 
 /** {{{ Argument info for commands in redis 1.0 */
diff --git a/redis_session.c b/redis_session.c
index 4165fff843..24ed98972a 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -231,13 +231,13 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s
     /* How long to wait between attempts to acquire lock */
     lock_wait_time = INI_INT("redis.session.lock_wait_time");
     if (lock_wait_time == 0) {
-        lock_wait_time = 2000;
+        lock_wait_time = 20000;
     }
 
     /* Maximum number of times to retry (-1 means infinite) */
     retries = INI_INT("redis.session.lock_retries");
     if (retries == 0) {
-        retries = 10;
+        retries = 1000;
     }
 
     /* How long should the lock live (in seconds) */
@@ -323,7 +323,7 @@ static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_
          * if we aren't flagged as locked, so if we're not flagged here something
          * failed */
         if (!lock_status->is_locked) {
-            php_error_docref(NULL, E_WARNING, "Failed to refresh session lock");
+            php_error_docref(NULL, E_WARNING, "Session lock expired");
         }
     }
 
@@ -454,6 +454,11 @@ PS_OPEN_FUNC(redis)
             }
 
             if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) {
+                char *path = estrndup(save_path+i, j-i);
+                php_error_docref(NULL, E_WARNING,
+                    "Failed to parse session.save_path (error at offset %d, url was '%s')", i, path);
+                efree(path);
+
                 php_url_free(url);
                 if (persistent_id) zend_string_release(persistent_id);
                 if (prefix) zend_string_release(prefix);
@@ -461,6 +466,7 @@ PS_OPEN_FUNC(redis)
                 if (pass) zend_string_release(pass);
                 redis_pool_free(pool);
                 PS_SET_MOD_DATA(NULL);
+
                 return FAILURE;
             }
 
@@ -568,9 +574,7 @@ PS_CREATE_SID_FUNC(redis)
         RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL;
 
         if (!redis_sock) {
-            php_error_docref(NULL, E_NOTICE,
-                "Redis not available while creating session_id");
-
+            php_error_docref(NULL, E_NOTICE, "Redis connection not available");
             zend_string_release(sid);
             return php_session_create_id(NULL);
         }
@@ -588,7 +592,7 @@ PS_CREATE_SID_FUNC(redis)
         sid = NULL;
     }
 
-    php_error_docref(NULL, E_NOTICE,
+    php_error_docref(NULL, E_WARNING,
         "Acquiring session lock failed while creating session_id");
 
     return NULL;
@@ -611,6 +615,7 @@ PS_VALIDATE_SID_FUNC(redis)
     redis_pool_member *rpm = redis_pool_get_sock(pool, skey);
     RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL;
     if (!redis_sock) {
+        php_error_docref(NULL, E_WARNING, "Redis connection not available");
         return FAILURE;
     }
 
@@ -618,16 +623,13 @@ PS_VALIDATE_SID_FUNC(redis)
     zend_string *session = redis_session_key(redis_sock, skey, skeylen);
     cmd_len = REDIS_SPPRINTF(&cmd, "EXISTS", "S", session);
     zend_string_release(session);
-    if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
+    if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) {
+        php_error_docref(NULL, E_WARNING, "Error communicating with Redis server");
         efree(cmd);
         return FAILURE;
     }
-    efree(cmd);
 
-    /* read response */
-    if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
-        return FAILURE;
-    }
+    efree(cmd);
 
     if (response_len == 2 && response[0] == ':' && response[1] == '1') {
         efree(response);
@@ -655,6 +657,7 @@ PS_UPDATE_TIMESTAMP_FUNC(redis)
     redis_pool_member *rpm = redis_pool_get_sock(pool, skey);
     RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL;
     if (!redis_sock) {
+        php_error_docref(NULL, E_WARNING, "Redis connection not available");
         return FAILURE;
     }
 
@@ -663,16 +666,13 @@ PS_UPDATE_TIMESTAMP_FUNC(redis)
     cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, session_gc_maxlifetime());
     zend_string_release(session);
 
-    if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
+    if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) {
+        php_error_docref(NULL, E_WARNING, "Error communicating with Redis server");
         efree(cmd);
         return FAILURE;
     }
-    efree(cmd);
 
-    /* read response */
-    if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
-        return FAILURE;
-    }
+    efree(cmd);
 
     if (response_len == 2 && response[0] == ':') {
         efree(response);
@@ -699,6 +699,7 @@ PS_READ_FUNC(redis)
     redis_pool_member *rpm = redis_pool_get_sock(pool, skey);
     RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL;
     if (!redis_sock) {
+        php_error_docref(NULL, E_WARNING, "Redis connection not available");
         return FAILURE;
     }
 
@@ -708,20 +709,24 @@ PS_READ_FUNC(redis)
     cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key);
 
     if (lock_acquire(redis_sock, &pool->lock_status) != SUCCESS) {
-        php_error_docref(NULL, E_NOTICE,
-            "Acquire of session lock was not successful");
+        php_error_docref(NULL, E_WARNING, "Failed to acquire session lock");
+        efree(cmd);
+        return FAILURE;
     }
 
     if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
+        php_error_docref(NULL, E_WARNING, "Error communicating with Redis server");
         efree(cmd);
         return FAILURE;
     }
+
     efree(cmd);
 
     /* Read response from Redis.  If we get a NULL response from redis_sock_read
      * this can indicate an error, OR a "NULL bulk" reply (empty session data)
      * in which case we can reply with success. */
     if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL && resp_len != -1) {
+        php_error_docref(NULL, E_WARNING, "Error communicating with Redis server");
         return FAILURE;
     }
 
@@ -751,6 +756,7 @@ PS_WRITE_FUNC(redis)
     redis_pool_member *rpm = redis_pool_get_sock(pool, skey);
     RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL;
     if (!redis_sock) {
+        php_error_docref(NULL, E_WARNING, "Redis connection not available");
         return FAILURE;
     }
 
@@ -760,21 +766,25 @@ PS_WRITE_FUNC(redis)
     cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, session_gc_maxlifetime(), sval, svallen);
     zend_string_release(session);
 
-    if (!write_allowed(redis_sock, &pool->lock_status) || redis_sock_write(redis_sock, cmd, cmd_len ) < 0) {
+    if (!write_allowed(redis_sock, &pool->lock_status)) {
+        php_error_docref(NULL, E_WARNING, "Unable to write session: session lock not held");
         efree(cmd);
         return FAILURE;
     }
-    efree(cmd);
 
-    /* read response */
-    if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
+    if (redis_sock_write(redis_sock, cmd, cmd_len ) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) {
+        php_error_docref(NULL, E_WARNING, "Error communicating with Redis server");
+        efree(cmd);
         return FAILURE;
     }
 
+    efree(cmd);
+
     if (IS_REDIS_OK(response, response_len)) {
         efree(response);
         return SUCCESS;
     } else {
+        php_error_docref(NULL, E_WARNING, "Error writing session data to Redis: %s", response);
         efree(response);
         return FAILURE;
     }
@@ -794,6 +804,7 @@ PS_DESTROY_FUNC(redis)
     redis_pool_member *rpm = redis_pool_get_sock(pool, skey);
     RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL;
     if (!redis_sock) {
+        php_error_docref(NULL, E_WARNING, "Redis connection not available");
         return FAILURE;
     }
 
@@ -804,16 +815,13 @@ PS_DESTROY_FUNC(redis)
     zend_string *session = redis_session_key(redis_sock, skey, skeylen);
     cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "S", session);
     zend_string_release(session);
-    if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
+    if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) {
+        php_error_docref(NULL, E_WARNING, "Error communicating with Redis server");
         efree(cmd);
         return FAILURE;
     }
-    efree(cmd);
 
-    /* read response */
-    if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
-        return FAILURE;
-    }
+    efree(cmd);
 
     if (response_len == 2 && response[0] == ':' && (response[1] == '0' || response[1] == '1')) {
         efree(response);

From 92d5e3065d571d3557f5057dc809c949417ce6ee Mon Sep 17 00:00:00 2001
From: Mike Griego 
Date: Sun, 17 Jan 2021 08:08:44 -0600
Subject: [PATCH 1434/1986] Fix failing test.

---
 tests/RedisTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index fcf5d811b8..a2da81550f 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6523,7 +6523,7 @@ public function testSession_defaultLockRetryCount()
         usleep(100000);
 
         $start = microtime(true);
-        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 200000, 0);
+        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 2000, 0);
         $end = microtime(true);
         $elapsedTime = $end - $start;
 

From 306fa25c19d9164a0c3e2dea20ad19e683319a5f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 9 Mar 2021 11:21:24 -0800
Subject: [PATCH 1435/1986] Smaller default retry count

See #1908
---
 README.markdown | 2 +-
 redis.c         | 2 +-
 redis_session.c | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.markdown b/README.markdown
index 71fc973a1c..c30fd2d35c 100644
--- a/README.markdown
+++ b/README.markdown
@@ -93,7 +93,7 @@ redis.session.locking_enabled = 1
 redis.session.lock_expire = 60
 ; How long to wait between attempts to acquire lock, in microseconds (µs)?. Defaults to: 20000
 redis.session.lock_wait_time = 50000
-; Maximum number of times to retry (-1 means infinite). Defaults to: 1000
+; Maximum number of times to retry (-1 means infinite). Defaults to: 100
 redis.session.lock_retries = 2000
 ~~~
 
diff --git a/redis.c b/redis.c
index 7af1095084..481ccb200d 100644
--- a/redis.c
+++ b/redis.c
@@ -103,7 +103,7 @@ PHP_INI_BEGIN()
     /* redis session */
     PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL)
-    PHP_INI_ENTRY("redis.session.lock_retries", "1000", PHP_INI_ALL, NULL)
+    PHP_INI_ENTRY("redis.session.lock_retries", "100", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL)
 PHP_INI_END()
 
diff --git a/redis_session.c b/redis_session.c
index 24ed98972a..19ae712fdf 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -237,7 +237,7 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s
     /* Maximum number of times to retry (-1 means infinite) */
     retries = INI_INT("redis.session.lock_retries");
     if (retries == 0) {
-        retries = 1000;
+        retries = 100;
     }
 
     /* How long should the lock live (in seconds) */

From ba1314f41ad57e63886be1b8bf7468c72f0d8e62 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 9 Mar 2021 13:29:14 -0800
Subject: [PATCH 1436/1986] Fix session retry test

See #1908
---
 tests/RedisTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index a2da81550f..df6a17e750 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6527,7 +6527,7 @@ public function testSession_defaultLockRetryCount()
         $end = microtime(true);
         $elapsedTime = $end - $start;
 
-        $this->assertTrue($elapsedTime > 2 && $elapsedTime < 3);
+        $this->assertTrue($elapsedTime > .2 && $elapsedTime < .3);
         $this->assertFalse($sessionSuccessful);
     }
 

From c8a9859e2d07f69c7409829a1e22e6dedd6d1a83 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 9 Mar 2021 13:29:47 -0800
Subject: [PATCH 1437/1986] Fix typo in config.m4

---
 config.m4 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config.m4 b/config.m4
index 5ffd49d21c..87dca33350 100644
--- a/config.m4
+++ b/config.m4
@@ -270,7 +270,7 @@ if test "$PHP_REDIS" != "no"; then
       ])
       PHP_SUBST(REDIS_SHARED_LIBADD)
     else
-      AC_MSG_ERROR([only system libz4 is supported])
+      AC_MSG_ERROR([only system liblz4 is supported])
     fi
   fi
 

From ccd142fa442817a3a000e375c9c7344e02acd16a Mon Sep 17 00:00:00 2001
From: Adam Olley 
Date: Wed, 10 Mar 2021 14:11:04 +1030
Subject: [PATCH 1438/1986] Pass compression flag when performing HMGET (#1945)

Without this, performing a HMGET call fails to decompress the data before
returning it to php.
---
 cluster_library.c   | 1 +
 tests/RedisTest.php | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/cluster_library.c b/cluster_library.c
index 5bcd23385f..f61a6704a7 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -2112,6 +2112,7 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
         if (c->reply_len > 0) {
             /* Push serialization settings from the cluster into our socket */
             c->cmd_sock->serializer = c->flags->serializer;
+            c->cmd_sock->compression = c->flags->compression;
 
             /* Call our specified callback */
             if (cb(c->cmd_sock, &z_result, c->reply_len, ctx) == FAILURE) {
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index df6a17e750..05c3b4825f 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4716,6 +4716,10 @@ private function checkCompression($mode, $level)
                 $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() {

From 1a0ae97ef56df17748a558e02b1f31065749d7e6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 10 Mar 2021 10:57:42 -0800
Subject: [PATCH 1439/1986] Fix PhpRedis session tests to soften timing issues

Rework the session locking unit tests to be less reliant on arbitrary
sleep calls which can be very troublesome when running in Travis and
especially when running in Travis under valgrind.

Additionally, skip multiple newly added Redis 6.2 commands that aren't
yet implemented in RedisCluster.
---
 tests/RedisClusterTest.php |   7 +++
 tests/RedisTest.php        | 102 +++++++++++++++++++++++++++----------
 2 files changed, 81 insertions(+), 28 deletions(-)

diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index a1baf90dd2..b7746c5ec5 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -49,6 +49,13 @@ public function testConnectException() { return $this->markTestSkipped(); }
     public function testTlsConnect() { return $this->markTestSkipped(); }
     public function testInvalidAuthArgs() { return $this->markTestSkipped(); }
 
+    public function testlMove() { return $this->markTestSkipped(); }
+    public function testsMisMember() { return $this->markTestSkipped(); }
+    public function testzDiff() { return $this->markTestSkipped(); }
+    public function testzDiffStore() { return $this->markTestSkipped(); }
+    public function testzMscore() { return $this->marktestSkipped(); }
+    public function testCopy() { return $this->marktestSkipped(); }
+
     /* Session locking feature is currently not supported in in context of Redis Cluster.
        The biggest issue for this is the distribution nature of Redis cluster */
     public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); }
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 05c3b4825f..b4410cd949 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6415,10 +6415,15 @@ public function testSession_lockKeyCorrect()
     {
         $this->setSessionHandler();
         $sessionId = $this->generateSessionId();
+
         $this->startSessionProcess($sessionId, 5, true);
-        usleep(100000);
 
-        $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
+        $maxwait = (ini_get('redis.session.lock_wait_time') *
+                    ini_get('redis.session.lock_retries') /
+                    1000000.00);
+
+        $exist = $this->waitForSessionLockKey($sessionId, $maxwait);
+        $this->assertTrue($exist);
     }
 
     public function testSession_lockingDisabledByDefault()
@@ -6443,8 +6448,8 @@ public function testSession_lockReleasedOnClose()
         $this->setSessionHandler();
         $sessionId = $this->generateSessionId();
         $this->startSessionProcess($sessionId, 1, true);
-        usleep(1100000);
-
+        $sleep = ini_get('redis.session.lock_wait_time') * ini_get('redis.session.lock_retries');
+        usleep($sleep + 10000);
         $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
     }
 
@@ -6503,20 +6508,23 @@ public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock()
         $this->assertTrue('firstProcess' !== $this->getSessionData($sessionId));
     }
 
-    public function testSession_correctLockRetryCount()
-    {
+    public function testSession_correctLockRetryCount() {
         $this->setSessionHandler();
         $sessionId = $this->generateSessionId();
+
+        /* Start another process and wait until it has the lock */
         $this->startSessionProcess($sessionId, 10, true);
-        usleep(100000);
+        if ( ! $this->waitForSessionLockKey($sessionId, 2)) {
+            $this->assertTrue(false);
+            return;
+        }
 
-        $start = microtime(true);
-        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 1000000, 3);
-        $end = microtime(true);
-        $elapsedTime = $end - $start;
+        $t1 = microtime(true);
+        $ok = $this->startSessionProcess($sessionId, 0, false, 10, true, 100000, 10);
+        if ( ! $this->assertFalse($ok)) return;
+        $t2 = microtime(true);
 
-        $this->assertTrue($elapsedTime > 3 && $elapsedTime < 4);
-        $this->assertFalse($sessionSuccessful);
+        $this->assertTrue($t2 - $t1 >= 1 && $t2 - $t1 <= 3);
     }
 
     public function testSession_defaultLockRetryCount()
@@ -6524,14 +6532,21 @@ public function testSession_defaultLockRetryCount()
         $this->setSessionHandler();
         $sessionId = $this->generateSessionId();
         $this->startSessionProcess($sessionId, 10, true);
-        usleep(100000);
+
+        $keyname = $this->sessionPrefix . $sessionId . '_LOCK';
+        $begin = microtime(true);
+
+        if ( ! $this->waitForSessionLockKey($sessionId, 3)) {
+            $this->assertTrue(false);
+            return;
+        }
 
         $start = microtime(true);
-        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 2000, 0);
+        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 20000, 0);
         $end = microtime(true);
         $elapsedTime = $end - $start;
 
-        $this->assertTrue($elapsedTime > .2 && $elapsedTime < .3);
+        $this->assertTrue($elapsedTime > 2 && $elapsedTime < 3);
         $this->assertFalse($sessionSuccessful);
     }
 
@@ -6539,20 +6554,27 @@ public function testSession_noUnlockOfOtherProcess()
     {
         $this->setSessionHandler();
         $sessionId = $this->generateSessionId();
-        $this->startSessionProcess($sessionId, 3, true, 1); // Process 1
-        usleep(100000);
-        $this->startSessionProcess($sessionId, 5, true);    // Process 2
 
-        $start = microtime(true);
-        // Waiting until TTL of process 1 ended and process 2 locked the session,
-        // because is not guaranteed which waiting process gets the next lock
-        sleep(1);
-        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
-        $end = microtime(true);
-        $elapsedTime = $end - $start;
+        $t1 = microtime(true);
 
-        $this->assertTrue($elapsedTime > 5);
-        $this->assertTrue($sessionSuccessful);
+        /* 1.  Start a background process, and wait until we are certain
+         *     the lock was attained. */
+        $nsec = 3;
+        $this->startSessionProcess($sessionId, $nsec, true, $nsec);
+        if ( ! $this->waitForSessionLockKey($sessionId, 1)) {
+            $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($sessionId, 0, false);
+        $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()
@@ -6832,6 +6854,30 @@ private function startSessionProcess($sessionId, $sleepTime, $background, $maxEx
         }
     }
 
+    /**
+     * @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 $sessionId
      * @param int    $sessionLifetime

From d3b76f71db5bf2470e99ca8ab3b057ec8c113079 Mon Sep 17 00:00:00 2001
From: roy 
Date: Thu, 18 Mar 2021 15:51:49 +0100
Subject: [PATCH 1440/1986] Fix #1952

---
 redis_cluster.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis_cluster.c b/redis_cluster.c
index ab9d55b7d5..e733f4e2c8 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -247,7 +247,7 @@ zend_function_entry redis_cluster_functions[] = {
     PHP_ME(RedisCluster, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, sunion, arginfo_nkeys, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, sunionstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, time, arginfo_void, ZEND_ACC_PUBLIC)
+    PHP_ME(RedisCluster, time, key_or_address, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)

From fe097a47d84b23b33f93768dd43ebe3e4be4ff83 Mon Sep 17 00:00:00 2001
From: Roy 
Date: Thu, 18 Mar 2021 22:10:11 +0100
Subject: [PATCH 1441/1986] fix

---
 redis_cluster.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis_cluster.c b/redis_cluster.c
index e733f4e2c8..f42ae99d8f 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -247,7 +247,7 @@ zend_function_entry redis_cluster_functions[] = {
     PHP_ME(RedisCluster, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, sunion, arginfo_nkeys, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, sunionstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, time, key_or_address, ZEND_ACC_PUBLIC)
+    PHP_ME(RedisCluster, time, arginfo_key_or_address, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)

From d5cf52cb8ad38718894052038d8e32dc53f4d5b5 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 20 Mar 2021 23:21:18 +0200
Subject: [PATCH 1442/1986] [WIP] Issue #1894

Add Redis::zinter and Redis::zunion commands
---
 php_redis.h                |   2 +
 redis.c                    |  24 +++++++-
 redis_cluster.c            |   4 +-
 redis_commands.c           | 121 ++++++++++++++++++++++++++++++++++++-
 redis_commands.h           |   5 +-
 tests/RedisClusterTest.php |   2 +
 tests/RedisTest.php        |  34 +++++++++++
 7 files changed, 186 insertions(+), 6 deletions(-)

diff --git a/php_redis.h b/php_redis.h
index 975b8ab524..6b7b3be4ba 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -146,7 +146,9 @@ PHP_METHOD(Redis, zRevRank);
 PHP_METHOD(Redis, zScore);
 PHP_METHOD(Redis, zdiff);
 PHP_METHOD(Redis, zdiffstore);
+PHP_METHOD(Redis, zinter);
 PHP_METHOD(Redis, zinterstore);
+PHP_METHOD(Redis, zunion);
 PHP_METHOD(Redis, zunionstore);
 
 PHP_METHOD(Redis, eval);
diff --git a/redis.c b/redis.c
index 481ccb200d..5d860401a3 100644
--- a/redis.c
+++ b/redis.c
@@ -120,6 +120,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zdiff, 0, 0, 1)
     ZEND_ARG_ARRAY_INFO(0, options, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_zinterunion, 0, 0, 1)
+    ZEND_ARG_ARRAY_INFO(0, keys, 0)
+    ZEND_ARG_ARRAY_INFO(0, weights, 1)
+    ZEND_ARG_ARRAY_INFO(0, options, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_zdiffstore, 0, 0, 2)
     ZEND_ARG_INFO(0, destination)
     ZEND_ARG_ARRAY_INFO(0, keys, 0)
@@ -484,8 +490,10 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zdiff, arginfo_zdiff, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zdiffstore, arginfo_zdiffstore, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, zinter, arginfo_zinterunion, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, zunion, arginfo_zinterunion, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC)
      PHP_FE_END
 };
@@ -2317,6 +2325,18 @@ PHP_METHOD(Redis, zdiff) {
 }
 /* }}} */
 
+/* {{{ proto array Redis::zinter(array keys, array|null weights, array options) */
+PHP_METHOD(Redis, zinter) {
+    REDIS_PROCESS_KW_CMD("ZINTER", redis_zinterunion_cmd, redis_zdiff_response);
+}
+/* }}} */
+
+/* {{{ proto array Redis::zunion(array keys, array|null weights, array options) */
+PHP_METHOD(Redis, zunion) {
+    REDIS_PROCESS_KW_CMD("ZUNION", redis_zinterunion_cmd, redis_zdiff_response);
+}
+/* }}} */
+
 /* {{{ proto array Redis::zdiffstore(string destination, array keys) */
 PHP_METHOD(Redis, zdiffstore) {
     REDIS_PROCESS_CMD(zdiffstore, redis_long_response);
@@ -2325,12 +2345,12 @@ PHP_METHOD(Redis, zdiffstore) {
 
 /* zinterstore */
 PHP_METHOD(Redis, zinterstore) {
-    REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response);
+    REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinterunionstore_cmd, redis_long_response);
 }
 
 /* zunionstore */
 PHP_METHOD(Redis, zunionstore) {
-    REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, redis_long_response);
+    REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinterunionstore_cmd, redis_long_response);
 }
 
 /* {{{ proto array Redis::zPopMax(string key) */
diff --git a/redis_cluster.c b/redis_cluster.c
index ab9d55b7d5..23bf781a37 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1683,14 +1683,14 @@ PHP_METHOD(RedisCluster, zrangebyscore) {
 /* {{{ proto RedisCluster::zunionstore(string dst, array keys, [array weights,
  *                                     string agg]) */
 PHP_METHOD(RedisCluster, zunionstore) {
-    CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, cluster_long_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinterunionstore_cmd, cluster_long_resp, 0);
 }
 /* }}} */
 
 /* {{{ proto RedisCluster::zinterstore(string dst, array keys, [array weights,
  *                                     string agg]) */
 PHP_METHOD(RedisCluster, zinterstore) {
-    CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, cluster_long_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinterunionstore_cmd, cluster_long_resp, 0);
 }
 /* }}} */
 
diff --git a/redis_commands.c b/redis_commands.c
index e18690a4ec..aa1cf62d0c 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -675,6 +675,124 @@ redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                      char *kw, char **cmd, int *cmd_len, short *slot,
+                      void **ctx)
+{
+    int numkeys;
+    smart_string cmdstr = {0};
+    zval *z_keys, *z_weights = NULL, *z_opts = NULL, *z_ele;
+    zend_string *aggregate = NULL;
+    zend_bool withscores = 0;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a!a",
+                             &z_keys, &z_weights, &z_opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) {
+        return FAILURE;
+    }
+
+    if (z_weights) {
+        if (zend_hash_num_elements(Z_ARRVAL_P(z_weights)) != numkeys) {
+            php_error_docref(NULL, E_WARNING,
+                "WEIGHTS and keys array should be the same size!");
+            return FAILURE;
+        }
+    }
+
+    if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
+        zend_ulong idx;
+        zend_string *zkey;
+        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_opts), idx, zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "aggregate")) {
+                    aggregate = zval_get_string(z_ele);
+                    if (!zend_string_equals_literal_ci(aggregate, "sum") &&
+                        !zend_string_equals_literal_ci(aggregate, "min") &&
+                        !zend_string_equals_literal_ci(aggregate, "max")
+                    ) {
+                        php_error_docref(NULL, E_WARNING,
+                            "Invalid AGGREGATE option provided!");
+                        zend_string_release(aggregate);
+                        return FAILURE;
+                    }
+                } else if (zend_string_equals_literal_ci(zkey, "withscores")) {
+                    withscores = zval_is_true(z_ele);
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    redis_cmd_init_sstr(&cmdstr, 1 + numkeys + (z_weights ? 1 + numkeys : 0) + (aggregate ? 2 : 0) + withscores, kw, strlen(kw));
+    redis_cmd_append_sstr_long(&cmdstr, numkeys);
+
+
+    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) {
+        ZVAL_DEREF(z_ele);
+        redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock);
+    } ZEND_HASH_FOREACH_END();
+
+    if (z_weights) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WEIGHTS");
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_weights), z_ele) {
+            ZVAL_DEREF(z_ele);
+            switch (Z_TYPE_P(z_ele)) {
+                case IS_LONG:
+                    redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele));
+                    break;
+                case IS_DOUBLE:
+                    redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(z_ele));
+                    break;
+                case IS_STRING: {
+                    double dval;
+                    zend_long lval;
+                    zend_uchar type = is_numeric_string(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &lval, &dval, 0);
+                    if (type == IS_LONG) {
+                        redis_cmd_append_sstr_long(&cmdstr, lval);
+                        break;
+                    } else if (type == IS_DOUBLE) {
+                        redis_cmd_append_sstr_dbl(&cmdstr, dval);
+                        break;
+                    } else if (strncasecmp(Z_STRVAL_P(z_ele), "-inf", sizeof("-inf") - 1) == 0 ||
+                               strncasecmp(Z_STRVAL_P(z_ele), "+inf", sizeof("+inf") - 1) == 0 ||
+                               strncasecmp(Z_STRVAL_P(z_ele), "inf", sizeof("inf") - 1) == 0
+                    ) {
+                        redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele));
+                        break;
+                    }
+                    // fall through
+                }
+                default:
+                    php_error_docref(NULL, E_WARNING,
+                        "Weights must be numeric or '-inf','inf','+inf'");
+                    if (aggregate) zend_string_release(aggregate);
+                    efree(cmdstr.c);
+                    return FAILURE;
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    if (aggregate) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AGGREGATE");
+        redis_cmd_append_sstr_zstr(&cmdstr, aggregate);
+        zend_string_release(aggregate);
+    }
+
+    if (withscores) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES");
+        *ctx = redis_sock;
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return SUCCESS;
+}
+
 int
 redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                      char **cmd, int *cmd_len, short *slot, void **ctx)
@@ -710,7 +828,8 @@ redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 }
 
 /* ZUNIONSTORE, ZINTERSTORE */
-int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+int
+redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                      char *kw, char **cmd, int *cmd_len, short *slot,
                      void **ctx)
 {
diff --git a/redis_commands.h b/redis_commands.h
index c2f790954a..be30a310f5 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -106,10 +106,13 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
-int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+int redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
 int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index b7746c5ec5..350f35f8a1 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -52,6 +52,8 @@ public function testInvalidAuthArgs() { return $this->markTestSkipped(); }
     public function testlMove() { return $this->markTestSkipped(); }
     public function testsMisMember() { return $this->markTestSkipped(); }
     public function testzDiff() { return $this->markTestSkipped(); }
+    public function testzInter() { return $this->markTestSkipped(); }
+    public function testzUnion() { return $this->markTestSkipped(); }
     public function testzDiffStore() { return $this->markTestSkipped(); }
     public function testzMscore() { return $this->marktestSkipped(); }
     public function testCopy() { return $this->marktestSkipped(); }
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index b4410cd949..a2463f5983 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2547,6 +2547,40 @@ public function testzDiff()
         $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zDiff(['key'], ['withscores' => true]));
     }
 
+    public function testzInter()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->del('key');
+        foreach (range('a', 'c') as $c) {
+            $this->redis->zAdd('key', 1, $c);
+        }
+
+        $this->assertEquals(['a', 'b', 'c'], $this->redis->zInter(['key']));
+        $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zInter(['key'], null, ['withscores' => true]));
+    }
+
+    public function testzUnion()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->redis->del('key');
+        foreach (range('a', 'c') as $c) {
+            $this->redis->zAdd('key', 1, $c);
+        }
+
+        $this->assertEquals(['a', 'b', 'c'], $this->redis->zUnion(['key']));
+        $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zUnion(['key'], null, ['withscores' => true]));
+    }
+
     public function testzDiffStore()
     {
         // Only available since 6.2.0

From c93eba4a4f9ef00dcb29003c91d550e0a8ecb835 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 25 Mar 2021 08:54:15 +0100
Subject: [PATCH 1443/1986] Fix #1956 bad type usage on 32-bit

---
 library.c          | 2 +-
 redis_array.c      | 4 ++--
 redis_array_impl.c | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/library.c b/library.c
index 79847c7fd1..bb5c1e9a86 100644
--- a/library.c
+++ b/library.c
@@ -2968,7 +2968,7 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
 #ifdef HAVE_REDIS_ZSTD
             {
                 char *data;
-                size_t len;
+                unsigned long long len;
 
                 len = ZSTD_getFrameContentSize(val, val_len);
 
diff --git a/redis_array.c b/redis_array.c
index ff7d8ba6e6..2eb8e3ddbf 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -224,7 +224,7 @@ PHP_METHOD(RedisArray, __construct)
     RedisArray *ra = NULL;
     zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0;
     HashTable *hPrev = NULL, *hOpts = NULL;
-    long l_retry_interval = 0;
+    zend_long l_retry_interval = 0;
       zend_bool b_lazy_connect = 0;
     double d_connect_timeout = 0, read_timeout = 0.0;
     zend_string *algorithm = NULL, *user = NULL, *pass = NULL;
@@ -276,7 +276,7 @@ PHP_METHOD(RedisArray, __construct)
     }
 
     ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index,
-                       b_pconnect, l_retry_interval, b_lazy_connect,
+                       b_pconnect, (long)l_retry_interval, b_lazy_connect,
                        d_connect_timeout, read_timeout, consistent,
                        algorithm, user, pass);
 
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 37a84ba794..1d96542d18 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -158,7 +158,7 @@ RedisArray *ra_load_array(const char *name) {
 
     zend_string *algorithm = NULL, *user = NULL, *pass = NULL;
     zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0, consistent = 0;
-    long l_retry_interval = 0;
+    zend_long l_retry_interval = 0;
     zend_bool b_lazy_connect = 0;
     double d_connect_timeout = 0, read_timeout = 0.0;
     HashTable *hHosts = NULL, *hPrev = NULL;
@@ -291,7 +291,7 @@ RedisArray *ra_load_array(const char *name) {
     }
 
     /* create RedisArray object */
-    ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval,
+    ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, (long)l_retry_interval,
                        b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm,
                        user, pass);
     if (ra) {

From 21cce7844d81610e054e8c3c2839c0dd82c16bff Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 25 Mar 2021 09:16:27 +0100
Subject: [PATCH 1444/1986] cleanup unneeded cast

---
 redis_array.c      | 2 +-
 redis_array_impl.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 2eb8e3ddbf..9d6883c305 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -276,7 +276,7 @@ PHP_METHOD(RedisArray, __construct)
     }
 
     ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index,
-                       b_pconnect, (long)l_retry_interval, b_lazy_connect,
+                       b_pconnect, l_retry_interval, b_lazy_connect,
                        d_connect_timeout, read_timeout, consistent,
                        algorithm, user, pass);
 
diff --git a/redis_array_impl.c b/redis_array_impl.c
index 1d96542d18..8c1bc6eef2 100644
--- a/redis_array_impl.c
+++ b/redis_array_impl.c
@@ -291,7 +291,7 @@ RedisArray *ra_load_array(const char *name) {
     }
 
     /* create RedisArray object */
-    ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, (long)l_retry_interval,
+    ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval,
                        b_lazy_connect, d_connect_timeout, read_timeout, consistent, algorithm,
                        user, pass);
     if (ra) {

From 6da1758af83380990b4afc684652febfc873904e Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 25 Mar 2021 11:06:19 -0700
Subject: [PATCH 1445/1986] Small extra ZSTD validity check

---
 library.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/library.c b/library.c
index bb5c1e9a86..c4e9fd7bcd 100644
--- a/library.c
+++ b/library.c
@@ -2972,14 +2972,17 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
 
                 len = ZSTD_getFrameContentSize(val, val_len);
 
-                if (len != ZSTD_CONTENTSIZE_ERROR && len != ZSTD_CONTENTSIZE_UNKNOWN) {
+                if (len != ZSTD_CONTENTSIZE_ERROR && len != ZSTD_CONTENTSIZE_UNKNOWN && len <= INT_MAX)
+                {
+                    size_t zlen;
+
                     data = emalloc(len);
-                    len = ZSTD_decompress(data, len, val, val_len);
-                    if (ZSTD_isError(len)) {
+                    zlen = ZSTD_decompress(data, len, val, val_len);
+                    if (ZSTD_isError(zlen) || zlen != len) {
                         efree(data);
                         break;
-                    } else if (redis_unserialize(redis_sock, data, len, z_ret) == 0) {
-                        ZVAL_STRINGL(z_ret, data, len);
+                    } else if (redis_unserialize(redis_sock, data, zlen, z_ret) == 0) {
+                        ZVAL_STRINGL(z_ret, data, zlen);
                     }
                     efree(data);
                     return 1;

From d7a6eda7d039791df441b176c091ffc2a33d7720 Mon Sep 17 00:00:00 2001
From: dengliming 
Date: Wed, 7 Apr 2021 11:47:29 +0800
Subject: [PATCH 1446/1986] Update README.markdown

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index c30fd2d35c..a3c909634d 100644
--- a/README.markdown
+++ b/README.markdown
@@ -883,7 +883,7 @@ $redis->exists('key'); /* 1 */
 $redis->exists('NonExistingKey'); /* 0 */
 
 $redis->mset(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz']);
-$redis->exists(['foo', 'bar', 'baz]); /* 3 */
+$redis->exists(['foo', 'bar', 'baz']); /* 3 */
 $redis->exists('foo', 'bar', 'baz'); /* 3 */
 ~~~
 

From b0b9dd78ef7c15af936144c1b17df1a9273d72ab Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 8 Apr 2021 08:53:36 +0300
Subject: [PATCH 1447/1986] [WIP] Issue #1894

Add GET option to SET command
---
 cluster_library.c   | 10 ++++++++++
 cluster_library.h   |  2 ++
 library.c           |  9 +++++++++
 library.h           |  1 +
 redis.c             |  2 +-
 redis_cluster.c     |  2 +-
 redis_commands.c    |  9 ++++++++-
 tests/RedisTest.php |  5 +++++
 8 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index f61a6704a7..7c455ef358 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -1737,6 +1737,16 @@ PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
     CLUSTER_RETURN_BOOL(c, 1);
 }
 
+PHP_REDIS_API void
+cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
+{
+    if (ctx == NULL) {
+        cluster_bool_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
+    } else {
+        cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
+    }
+}
+
 /* 1 or 0 response, for things like SETNX */
 PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                            void *ctx)
diff --git a/cluster_library.h b/cluster_library.h
index f8f1eec845..bc937be5bd 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -413,6 +413,8 @@ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
     void *ctx);
 PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
+PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+    void *ctx);
 PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
 PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
diff --git a/library.c b/library.c
index c4e9fd7bcd..87b6bfa83e 100644
--- a/library.c
+++ b/library.c
@@ -1293,6 +1293,15 @@ redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *
     return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
 }
 
+PHP_REDIS_API int
+redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    if (ctx == NULL) {
+        return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    }
+    return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+}
+
 PHP_REDIS_API int
 redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                             zval *z_tab, void *ctx,
diff --git a/library.h b/library.h
index 0c8a55df02..f78f38c565 100644
--- a/library.h
+++ b/library.h
@@ -152,6 +152,7 @@ PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS,
 PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 /* Helper methods to get configuration values from a HashTable. */
 
diff --git a/redis.c b/redis.c
index 5d860401a3..b8a7221b81 100644
--- a/redis.c
+++ b/redis.c
@@ -1152,7 +1152,7 @@ PHP_METHOD(Redis, close)
 /* {{{ proto boolean Redis::set(string key, mixed val, long timeout,
  *                              [array opt) */
 PHP_METHOD(Redis, set) {
-    REDIS_PROCESS_CMD(set, redis_boolean_response);
+    REDIS_PROCESS_CMD(set, redis_set_response);
 }
 
 /* {{{ proto boolean Redis::setex(string key, long expire, string value)
diff --git a/redis_cluster.c b/redis_cluster.c
index ec72408a58..402c23b7cc 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -520,7 +520,7 @@ PHP_METHOD(RedisCluster, get) {
 
 /* {{{ proto bool RedisCluster::set(string key, string value) */
 PHP_METHOD(RedisCluster, set) {
-    CLUSTER_PROCESS_CMD(set, cluster_bool_resp, 0);
+    CLUSTER_PROCESS_CMD(set, cluster_set_resp, 0);
 }
 /* }}} */
 
diff --git a/redis_commands.c b/redis_commands.c
index aa1cf62d0c..991ea1ffba 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1532,6 +1532,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *key = NULL, *exp_type = NULL, *set_type = NULL;
     long exp_set = 0, keep_ttl = 0;
     zend_long expire = -1;
+    zend_bool get = 0;
     size_t key_len;
 
     // Make sure the function is being called correctly
@@ -1569,6 +1570,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             } else if (Z_TYPE_P(v) == IS_STRING) {
                 if (ZVAL_STRICMP_STATIC(v, "KEEPTTL")) {
                     keep_ttl  = 1;
+                } else if (ZVAL_STRICMP_STATIC((v), "GET")) {
+                    get = 1;
                 } else if (ZVAL_IS_NX_XX_ARG(v)) {
                     set_type = Z_STRVAL_P(v);
                 }
@@ -1600,7 +1603,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     /* Calculate argc based on options set */
-    int argc = 2 + (exp_type ? 2 : 0) + (set_type != NULL) + (keep_ttl != 0);
+    int argc = 2 + (exp_type ? 2 : 0) + (set_type != NULL) + (keep_ttl != 0) + get;
 
     /* Initial SET   */
     redis_cmd_init_sstr(&cmdstr, argc, "SET", 3);
@@ -1616,6 +1619,10 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         redis_cmd_append_sstr(&cmdstr, set_type, strlen(set_type));
     if (keep_ttl)
         redis_cmd_append_sstr(&cmdstr, "KEEPTTL", 7);
+    if (get) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GET");
+        *ctx = redis_sock;
+    }
 
     /* Push command and length to the caller */
     *cmd = cmdstr.c;
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index a2463f5983..31a0cfac99 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -442,6 +442,11 @@ public function testExtendedSet() {
         $this->assertTrue($this->redis->ttl('foo') > -1);
         $this->redis->set('foo', 'bar', ['XX']);
         $this->assertTrue($this->redis->ttl('foo') == -1);
+
+        if (version_compare($this->version, "6.2.0") < 0)
+            return;
+
+        $this->assertTrue($this->redis->set('foo', 'baz', ['GET']) === 'bar');
     }
 
     public function testGetSet() {

From 6aa9c0899cab2210e9340bd710ee81db72d76d40 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 14 Apr 2021 12:55:38 +0300
Subject: [PATCH 1448/1986] Add PHPREDIS_CTX_PTR magic value

---
 README.markdown  | 2 +-
 common.h         | 1 +
 redis_commands.c | 6 +++---
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/README.markdown b/README.markdown
index c30fd2d35c..4840f4cd78 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,6 +1,6 @@
 # PhpRedis
 
-[![Build Status](https://travis-ci.org/phpredis/phpredis.svg?branch=develop)](https://travis-ci.org/phpredis/phpredis)
+[![Build Status](https://travis-ci.com/phpredis/phpredis.svg?branch=develop)](https://travis-ci.com/phpredis/phpredis)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis)
 [![PHP version from Travis config](https://img.shields.io/travis/php-v/phpredis/phpredis/develop)](https://img.shields.io/travis/php-v/phpredis/phpredis/develop)
 
diff --git a/common.h b/common.h
index fd11bf4ed1..e48089692a 100644
--- a/common.h
+++ b/common.h
@@ -4,6 +4,7 @@
 #ifndef REDIS_COMMON_H
 #define REDIS_COMMON_H
 
+#define PHPREDIS_CTX_PTR ((void *)0xDEADC0DE)
 #define PHPREDIS_NOTUSED(v) ((void)v)
 
 #include "zend_llist.h"
diff --git a/redis_commands.c b/redis_commands.c
index 991ea1ffba..06e352cc9e 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -667,7 +667,7 @@ redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     if (withscores) {
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES");
-        *ctx = redis_sock;
+        *ctx = PHPREDIS_CTX_PTR;
     }
 
     *cmd = cmdstr.c;
@@ -785,7 +785,7 @@ redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     if (withscores) {
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES");
-        *ctx = redis_sock;
+        *ctx = PHPREDIS_CTX_PTR;
     }
 
     *cmd = cmdstr.c;
@@ -1621,7 +1621,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         redis_cmd_append_sstr(&cmdstr, "KEEPTTL", 7);
     if (get) {
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GET");
-        *ctx = redis_sock;
+        *ctx = PHPREDIS_CTX_PTR;
     }
 
     /* Push command and length to the caller */

From 28de62202e600725139e6cb2976a0f6d9547439f Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 15 Apr 2021 09:26:10 +0300
Subject: [PATCH 1449/1986] [WIP] Issue #1894

Add IDLE argument to XPENDING command
---
 common.h         |  1 +
 redis_commands.c | 18 +++++++++++-------
 2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/common.h b/common.h
index e48089692a..2ad7c70865 100644
--- a/common.h
+++ b/common.h
@@ -680,6 +680,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_xpending, 0, 0, 2)
     ZEND_ARG_INFO(0, str_end)
     ZEND_ARG_INFO(0, i_count)
     ZEND_ARG_INFO(0, str_consumer)
+    ZEND_ARG_INFO(0, idle)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_xrange, 0, 0, 3)
diff --git a/redis_commands.c b/redis_commands.c
index 06e352cc9e..16bc8e8c7d 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3619,7 +3619,7 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-// XPENDING key group [start end count] [consumer]
+// XPENDING key group [start end count [consumer] [idle]]
 int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                        char **cmd, int *cmd_len, short *slot, void **ctx)
 {
@@ -3627,13 +3627,13 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *key, *group, *start = NULL, *end = NULL, *consumer = NULL;
     size_t keylen, grouplen, startlen, endlen, consumerlen;
     int argc;
-    zend_long count = -1;
+    zend_long count = -1, idle = 0;
 
     // XPENDING mystream group55 - + 10 consumer-123
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ssls", &key,
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|sslsl", &key,
                               &keylen, &group, &grouplen, &start, &startlen,
-                              &end, &endlen, &count, &consumer, &consumerlen)
-                              == FAILURE)
+                              &end, &endlen, &count, &consumer, &consumerlen,
+                              &idle) == FAILURE)
     {
         return FAILURE;
     }
@@ -3643,8 +3643,8 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    /* Calculate argc.  It's either 2, 5, or 6 */
-    argc = 2 + (start != NULL ? 3 + (consumer != NULL) : 0);
+    /* Calculate argc.  It's either 2, 5, 6 or 7 */
+    argc = 2 + (start != NULL ? 3 + (consumer != NULL) + (idle != 0) : 0);
 
     /* Construct command and add required arguments */
     REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XPENDING");
@@ -3653,6 +3653,10 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     /* Add optional argumentst */
     if (start) {
+        if (idle != 0) {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IDLE");
+            redis_cmd_append_sstr_long(&cmdstr, (long)idle);
+        }
         redis_cmd_append_sstr(&cmdstr, start, startlen);
         redis_cmd_append_sstr(&cmdstr, end, endlen);
         redis_cmd_append_sstr_long(&cmdstr, (long)count);

From 3c40582034851df9102fa17979a318deaba8d1fb Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 24 Apr 2021 19:57:29 +0300
Subject: [PATCH 1450/1986] [WIP] Issue #1894

Add CLIENT INFO subcommand.
---
 redis.c             | 6 ++++++
 tests/RedisTest.php | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/redis.c b/redis.c
index b8a7221b81..230859dbc4 100644
--- a/redis.c
+++ b/redis.c
@@ -3489,6 +3489,7 @@ PHP_METHOD(Redis, getAuth) {
 
 /*
  * $redis->client('list');
+ * $redis->client('info');
  * $redis->client('kill', );
  * $redis->client('setname', );
  * $redis->client('getname');
@@ -3529,6 +3530,11 @@ PHP_METHOD(Redis, client) {
             redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, NULL, NULL);
         }
         REDIS_PROCESS_RESPONSE(redis_client_list_reply);
+    } else if (!strncasecmp(opt, "info", 4)) {
+        if (IS_ATOMIC(redis_sock)) {
+            redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+        }
+        REDIS_PROCESS_RESPONSE(redis_string_response);
     } else {
         if (IS_ATOMIC(redis_sock)) {
             redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 31a0cfac99..ed8b53f253 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1971,8 +1971,13 @@ public function testClient() {
         /* CLIENT GETNAME */
         $this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests');
 
+        if (version_compare($this->version, "6.2.0") >= 0) {
+            $this->assertFalse(empty($this->redis->client('info')));
+        }
+
         /* CLIENT KILL -- phpredis will reconnect, so we can do this */
         $this->assertTrue($this->redis->client('kill', $str_addr));
+
     }
 
     public function testSlowlog() {

From 5f8b0a7a69d3ae71929deb83ea0f0c6bf04ed9fa Mon Sep 17 00:00:00 2001
From: Maxime CORNET 
Date: Wed, 12 May 2021 20:36:04 +0200
Subject: [PATCH 1451/1986] Add json serializer in documentation

---
 README.markdown | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 4840f4cd78..34159e7e80 100644
--- a/README.markdown
+++ b/README.markdown
@@ -357,6 +357,7 @@ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);	  // Don't ser
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);	  // Use built-in serialize/unserialize
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // Use igBinary serialize/unserialize
 $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_MSGPACK);  // Use msgpack serialize/unserialize
+$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON);  // Use JSON to serialize/unserialize
 
 $redis->setOption(Redis::OPT_PREFIX, 'myAppName:');	// use custom prefix on all keys
 
@@ -389,7 +390,7 @@ Parameter value.
 ##### *Example*
 ~~~php
 // return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, 
-//        Redis::SERIALIZER_IGBINARY, or Redis::SERIALIZER_MSGPACK
+//        Redis::SERIALIZER_IGBINARY, Redis::SERIALIZER_MSGPACK or Redis::SERIALIZER_JSON
 $redis->getOption(Redis::OPT_SERIALIZER);
 ~~~
 

From 568fc3a824faec7b4910b815272580ee7c93b243 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 14 May 2021 15:24:13 +0300
Subject: [PATCH 1452/1986] [WIP] Issue #1894

Add GT and LT options to ZADD.
---
 redis_commands.c    | 8 ++++++--
 tests/RedisTest.php | 5 +++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 16bc8e8c7d..22d23b8270 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2842,7 +2842,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                    char **cmd, int *cmd_len, short *slot, void **ctx)
 {
     zval *z_args;
-    char *key, *val, *exp_type = NULL;
+    char *key, *val, *exp_type = NULL, *range_type = NULL;
     size_t key_len, val_len;
     int key_free, val_free;
     int num = ZEND_NUM_ARGS(), i = 1, argc;
@@ -2857,7 +2857,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    // Need key, [NX|XX] [CH] [INCR] score, value, [score, value...] */
+    // Need key, [NX|XX] [LT|GT] [CH] [INCR] score, value, [score, value...] */
     if (num % 2 == 0) {
         if (Z_TYPE(z_args[1]) != IS_ARRAY) {
             efree(z_args);
@@ -2868,6 +2868,8 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             if (Z_TYPE_P(z_opt) == IS_STRING) {
                 if (ZVAL_IS_NX_XX_ARG(z_opt)) {
                     exp_type = Z_STRVAL_P(z_opt);
+                } else if (ZVAL_STRICMP_STATIC(z_opt, "LT") || ZVAL_STRICMP_STATIC(z_opt, "GT")) {
+                    range_type = Z_STRVAL_P(z_opt);
                 } else if (ZVAL_STRICMP_STATIC(z_opt, "CH")) {
                     ch = 1;
                 } else if (ZVAL_STRICMP_STATIC(z_opt, "INCR")) {
@@ -2883,6 +2885,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         } ZEND_HASH_FOREACH_END();
         argc  = num - 1;
         if (exp_type) argc++;
+        if (range_type) argc++;
         argc += ch + incr;
         i++;
     } else {
@@ -2905,6 +2908,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     if (key_free) efree(key);
 
     if (exp_type) redis_cmd_append_sstr(&cmdstr, exp_type, 2);
+    if (range_type) redis_cmd_append_sstr(&cmdstr, range_type, 2);
     if (ch) redis_cmd_append_sstr(&cmdstr, "CH", 2);
     if (incr) redis_cmd_append_sstr(&cmdstr, "INCR", 4);
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index ed8b53f253..b6b6934c3b 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2205,6 +2205,11 @@ public function testZX() {
             $this->assertTrue(1 === $this->redis->zAdd('key', [], 1, 'val1')); // empty options
             $this->assertTrue(1 === $this->redis->zAdd('key', ['nx'], 3, 'val3')); // nx option
             $this->assertTrue(0 === $this->redis->zAdd('key', ['xx'], 3, 'val3')); // xx option
+
+            if (version_compare($this->version, "6.2.0") >= 0) {
+                $this->assertTrue(0 === $this->redis->zAdd('key', ['lt'], 4, 'val3')); // lt option
+                $this->assertTrue(0 === $this->redis->zAdd('key', ['gt'], 2, 'val3')); // gt option
+            }
         }
 
         $this->assertTrue(['val0', 'val1', 'val2', 'val3', 'val4', 'val5'] === $this->redis->zRange('key', 0, -1));

From 4cb4cd0ee26fdd1a07f468b976f990dbb9de7ed0 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 18 Apr 2021 15:27:59 -0700
Subject: [PATCH 1453/1986] Separate compression and create utility methods

This commit splits compression and serialization into two distinct parts
and adds some utility functions so the user can compress/uncompress
or pack/unpack data explicily.

See #1939
---
 library.c           | 137 +++++++++++++++++++++++++-------------------
 library.h           |   5 ++
 php_redis.h         |   6 ++
 redis.c             |  49 ++++++++++++++++
 redis_cluster.c     |  25 ++++++++
 redis_cluster.h     |  12 ++--
 redis_commands.c    |  63 ++++++++++++++++++++
 redis_commands.h    |  31 ++++++----
 tests/RedisTest.php |  65 +++++++++++++++++++++
 tests/TestSuite.php |  12 ++++
 10 files changed, 330 insertions(+), 75 deletions(-)

diff --git a/library.c b/library.c
index 87b6bfa83e..d15dad1ede 100644
--- a/library.c
+++ b/library.c
@@ -2820,19 +2820,7 @@ static uint8_t crc8(unsigned char *input, size_t len) {
 #endif
 
 PHP_REDIS_API int
-redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
-{
-    char *buf;
-    int valfree;
-    size_t len;
-
-    valfree = redis_serialize(redis_sock, z, &buf, &len);
-    if (redis_sock->compression == REDIS_COMPRESSION_NONE) {
-        *val = buf;
-        *val_len = len;
-        return valfree;
-    }
-
+redis_compress(RedisSock *redis_sock, char **dst, size_t *dstlen, char *buf, size_t len) {
     switch (redis_sock->compression) {
         case REDIS_COMPRESSION_LZF:
 #ifdef HAVE_REDIS_LZF
@@ -2845,9 +2833,8 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
                 size = len + MIN(UINT_MAX - len, MAX(LZF_MARGIN, len / 25));
                 data = emalloc(size);
                 if ((res = lzf_compress(buf, len, data, size)) > 0) {
-                    if (valfree) efree(buf);
-                    *val = data;
-                    *val_len = res;
+                    *dst = data;
+                    *dstlen = res;
                     return 1;
                 }
                 efree(data);
@@ -2877,10 +2864,8 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
                 data = emalloc(size);
                 size = ZSTD_compress(data, size, buf, len, level);
                 if (!ZSTD_isError(size)) {
-                    if (valfree) efree(buf);
-                    data = erealloc(data, size);
-                    *val = data;
-                    *val_len = size;
+                    *dst = erealloc(data, size);
+                    *dstlen = size;
                     return 1;
                 }
                 efree(data);
@@ -2928,22 +2913,21 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
                     break;
                 }
 
-                if (valfree) efree(buf);
-                *val = lz4buf;
-                *val_len = lz4len + REDIS_LZ4_HDR_SIZE;
+                *dst = lz4buf;
+                *dstlen = lz4len + REDIS_LZ4_HDR_SIZE;
                 return 1;
             }
 #endif
             break;
     }
-    *val = buf;
-    *val_len = len;
-    return valfree;
+
+    *dst = buf;
+    *dstlen = len;
+    return 0;
 }
 
 PHP_REDIS_API int
-redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
-{
+redis_uncompress(RedisSock *redis_sock, char **dst, size_t *dstlen, const char *src, size_t len) {
     switch (redis_sock->compression) {
         case REDIS_COMPRESSION_LZF:
 #ifdef HAVE_REDIS_LZF
@@ -2952,24 +2936,27 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
                 int i;
                 uint32_t res;
 
-                if (val_len == 0)
+                if (len == 0)
                     break;
 
                 /* start from two-times bigger buffer and
                  * increase it exponentially  if needed */
                 errno = E2BIG;
                 for (i = 2; errno == E2BIG; i *= 2) {
-                    data = emalloc(i * val_len);
-                    if ((res = lzf_decompress(val, val_len, data, i * val_len)) == 0) {
+                    data = emalloc(i * len);
+                    if ((res = lzf_decompress(src, len, data, i * len)) == 0) {
                         /* errno != E2BIG will brake for loop */
                         efree(data);
                         continue;
-                    } else if (redis_unserialize(redis_sock, data, res, z_ret) == 0) {
-                        ZVAL_STRINGL(z_ret, data, res);
                     }
-                    efree(data);
+
+                    *dst = data;
+                    *dstlen = res;
                     return 1;
                 }
+
+                efree(data);
+                break;
             }
 #endif
             break;
@@ -2977,25 +2964,21 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
 #ifdef HAVE_REDIS_ZSTD
             {
                 char *data;
-                unsigned long long len;
+                unsigned long long zlen;
 
-                len = ZSTD_getFrameContentSize(val, val_len);
-
-                if (len != ZSTD_CONTENTSIZE_ERROR && len != ZSTD_CONTENTSIZE_UNKNOWN && len <= INT_MAX)
-                {
-                    size_t zlen;
+                zlen = ZSTD_getFrameContentSize(src, len);
+                if (zlen == ZSTD_CONTENTSIZE_ERROR || zlen == ZSTD_CONTENTSIZE_UNKNOWN || zlen > INT_MAX)
+                    break;
 
-                    data = emalloc(len);
-                    zlen = ZSTD_decompress(data, len, val, val_len);
-                    if (ZSTD_isError(zlen) || zlen != len) {
-                        efree(data);
-                        break;
-                    } else if (redis_unserialize(redis_sock, data, zlen, z_ret) == 0) {
-                        ZVAL_STRINGL(z_ret, data, zlen);
-                    }
+                data = emalloc(zlen);
+                *dstlen = ZSTD_decompress(data, zlen, src, len);
+                if (ZSTD_isError(*dstlen) || *dstlen != zlen) {
                     efree(data);
-                    return 1;
+                    break;
                 }
+
+                *dst = data;
+                return 1;
             }
 #endif
             break;
@@ -3008,12 +2991,12 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
 
                 /* We must have at least enough bytes for our header, and can't have more than
                  * INT_MAX + our header size. */
-                if (val_len < REDIS_LZ4_HDR_SIZE || val_len > INT_MAX + REDIS_LZ4_HDR_SIZE)
+                if (len < REDIS_LZ4_HDR_SIZE || len > INT_MAX + REDIS_LZ4_HDR_SIZE)
                     break;
 
                 /* Operate on copies in case our CRC fails */
-                const char *copy = val;
-                size_t copylen = val_len;
+                const char *copy = src;
+                size_t copylen = len;
 
                 /* Read in our header bytes */
                 memcpy(&lz4crc, copy, sizeof(uint8_t));
@@ -3028,23 +3011,59 @@ redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret)
                 /* Finally attempt decompression */
                 data = emalloc(datalen);
                 if (LZ4_decompress_safe(copy, data, copylen, datalen) > 0) {
-                    if (redis_unserialize(redis_sock, data, datalen, z_ret) == 0) {
-                        ZVAL_STRINGL(z_ret, data, datalen);
-                    }
-                    efree(data);
+                    *dst = data;
+                    *dstlen = datalen;
                     return 1;
                 }
+
                 efree(data);
             }
 #endif
             break;
     }
-    return redis_unserialize(redis_sock, val, val_len, z_ret);
+
+    *dst = (char*)src;
+    *dstlen = len;
+    return 0;
+}
+
+PHP_REDIS_API int
+redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) {
+    size_t tmplen;
+    int tmpfree;
+    char *tmp;
+
+    /* First serialize */
+    tmpfree = redis_serialize(redis_sock, z, &tmp, &tmplen);
+
+    /* Now attempt compression */
+    if (redis_compress(redis_sock, val, val_len, tmp, tmplen)) {
+        if (tmpfree) efree(tmp);
+        return 1;
+    }
+
+    return tmpfree;
+}
+
+PHP_REDIS_API int
+redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) {
+    size_t len;
+    char *buf;
+
+    /* Uncompress, then unserialize */
+    if (redis_uncompress(redis_sock, &buf, &len, src, srclen)) {
+        if (!redis_unserialize(redis_sock, buf, len, zdst)) {
+            ZVAL_STRINGL(zdst, buf, len);
+        }
+        efree(buf);
+        return 1;
+    }
+
+    return redis_unserialize(redis_sock, buf, len, zdst);
 }
 
 PHP_REDIS_API int
-redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len
-               )
+redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
 {
     php_serialize_data_t ht;
 
diff --git a/library.h b/library.h
index f78f38c565..2ae5599260 100644
--- a/library.h
+++ b/library.h
@@ -121,6 +121,11 @@ redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len);
 PHP_REDIS_API int
 redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret);
 
+PHP_REDIS_API int
+redis_compress(RedisSock *redis_sock, char **dst, size_t *dstlen, char *buf, size_t len);
+PHP_REDIS_API int
+redis_uncompress(RedisSock *redis_sock, char **dst, size_t *dstlen, const char *src, size_t len);
+
 PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len);
 PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret);
 
diff --git a/php_redis.h b/php_redis.h
index 6b7b3be4ba..a0c5de40fb 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -165,9 +165,15 @@ PHP_METHOD(Redis, role);
 PHP_METHOD(Redis, getLastError);
 PHP_METHOD(Redis, clearLastError);
 PHP_METHOD(Redis, _prefix);
+PHP_METHOD(Redis, _pack);
+PHP_METHOD(Redis, _unpack);
+
 PHP_METHOD(Redis, _serialize);
 PHP_METHOD(Redis, _unserialize);
 
+PHP_METHOD(Redis, _compress);
+PHP_METHOD(Redis, _uncompress);
+
 PHP_METHOD(Redis, mset);
 PHP_METHOD(Redis, msetnx);
 PHP_METHOD(Redis, rpoplpush);
diff --git a/redis.c b/redis.c
index 230859dbc4..bbf5bd89a0 100644
--- a/redis.c
+++ b/redis.c
@@ -286,6 +286,10 @@ static zend_function_entry redis_functions[] = {
      PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, _pack, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, _unpack, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, _compress, arginfo_value, ZEND_ACC_PUBLIC)
+     PHP_ME(Redis, _uncompress, arginfo_value, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, acl, arginfo_acl, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, append, arginfo_key_value, ZEND_ACC_PUBLIC)
      PHP_ME(Redis, auth, arginfo_auth, ZEND_ACC_PUBLIC)
@@ -3294,6 +3298,51 @@ PHP_METHOD(Redis, _unserialize) {
         redis_exception_ce);
 }
 
+PHP_METHOD(Redis, _compress) {
+    RedisSock *redis_sock;
+
+    // Grab socket
+    if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    redis_compress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
+}
+
+PHP_METHOD(Redis, _uncompress) {
+    RedisSock *redis_sock;
+
+    // Grab socket
+    if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    redis_uncompress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+        redis_exception_ce);
+}
+
+PHP_METHOD(Redis, _pack) {
+    RedisSock *redis_sock;
+
+    // Grab socket
+    if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
+}
+
+PHP_METHOD(Redis, _unpack) {
+    RedisSock *redis_sock;
+
+    // Grab socket
+    if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    redis_unpack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
+}
+
 /* {{{ proto Redis::getLastError() */
 PHP_METHOD(Redis, getLastError) {
     zval *object;
diff --git a/redis_cluster.c b/redis_cluster.c
index 402c23b7cc..8c4519941b 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -110,6 +110,10 @@ zend_function_entry redis_cluster_functions[] = {
     PHP_ME(RedisCluster, _redir, arginfo_void, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
+    PHP_ME(RedisCluster, _compress, arginfo_value, ZEND_ACC_PUBLIC)
+    PHP_ME(RedisCluster, _uncompress, arginfo_value, ZEND_ACC_PUBLIC)
+    PHP_ME(RedisCluster, _pack, arginfo_value, ZEND_ACC_PUBLIC)
+    PHP_ME(RedisCluster, _unpack, arginfo_value, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, acl, arginfo_acl_cl, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC)
     PHP_ME(RedisCluster, bgrewriteaof, arginfo_key_or_address, ZEND_ACC_PUBLIC)
@@ -1970,6 +1974,27 @@ PHP_METHOD(RedisCluster, _unserialize) {
 }
 /* }}} */
 
+PHP_METHOD(RedisCluster, _compress) {
+    redisCluster *c = GET_CONTEXT();
+    redis_compress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
+}
+
+PHP_METHOD(RedisCluster, _uncompress) {
+    redisCluster *c = GET_CONTEXT();
+    redis_uncompress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags,
+                             redis_cluster_exception_ce);
+}
+
+PHP_METHOD(RedisCluster, _pack) {
+    redisCluster *c = GET_CONTEXT();
+    redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
+}
+
+PHP_METHOD(RedisCluster, _unpack) {
+    redisCluster *c = GET_CONTEXT();
+    redis_unpack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
+}
+
 /* {{{ proto array RedisCluster::_masters() */
 PHP_METHOD(RedisCluster, _masters) {
     redisCluster *c = GET_CONTEXT();
diff --git a/redis_cluster.h b/redis_cluster.h
index c6959fde10..41f40c1af7 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -13,7 +13,7 @@
     redis_##name##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, \
                        &cmd_len, &slot)
 
-/* Append information required to handle MULTI commands to the tail of our MULTI 
+/* Append information required to handle MULTI commands to the tail of our MULTI
  * linked list. */
 #define CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx) \
     clusterFoldItem *_item; \
@@ -69,8 +69,8 @@
         CLUSTER_ENQUEUE_RESPONSE(c, slot, resp_func, ctx); \
         RETURN_ZVAL(getThis(), 1, 0); \
     } \
-    resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); 
-        
+    resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx);
+
 /* More generic processing, where only the keyword differs */
 #define CLUSTER_PROCESS_KW_CMD(kw, cmdfunc, resp_func, readcmd) \
     redisCluster *c = GET_CONTEXT(); \
@@ -89,7 +89,7 @@
         CLUSTER_ENQUEUE_RESPONSE(c, slot, resp_func, ctx); \
         RETURN_ZVAL(getThis(), 1, 0); \
     } \
-    resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); 
+    resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx);
 
 /* Create cluster context */
 zend_object *create_cluster_context(zend_class_entry *class_type);
@@ -293,6 +293,10 @@ PHP_METHOD(RedisCluster, setoption);
 PHP_METHOD(RedisCluster, _prefix);
 PHP_METHOD(RedisCluster, _serialize);
 PHP_METHOD(RedisCluster, _unserialize);
+PHP_METHOD(RedisCluster, _compress);
+PHP_METHOD(RedisCluster, _uncompress);
+PHP_METHOD(RedisCluster, _pack);
+PHP_METHOD(RedisCluster, _unpack);
 PHP_METHOD(RedisCluster, _masters);
 PHP_METHOD(RedisCluster, _redir);
 
diff --git a/redis_commands.c b/redis_commands.c
index 22d23b8270..4d463ba1e9 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4510,4 +4510,67 @@ void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS,
     RETURN_ZVAL(&z_ret, 0, 0);
 }
 
+void redis_compress_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
+    zend_string *zstr;
+    size_t len;
+    char *buf;
+    int cmp_free;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &zstr) == FAILURE) {
+        RETURN_FALSE;
+    }
+
+    cmp_free = redis_compress(redis_sock, &buf, &len, ZSTR_VAL(zstr), ZSTR_LEN(zstr));
+    RETVAL_STRINGL(buf, len);
+    if (cmp_free) efree(buf);
+}
+
+void redis_uncompress_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                              zend_class_entry *ex)
+{
+    zend_string *zstr;
+    size_t len;
+    char *buf;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &zstr) == FAILURE) {
+        RETURN_FALSE;
+    } else if (ZSTR_LEN(zstr) == 0 || redis_sock->compression == REDIS_COMPRESSION_NONE) {
+        RETURN_STR_COPY(zstr);
+    }
+
+    if (!redis_uncompress(redis_sock, &buf, &len, ZSTR_VAL(zstr), ZSTR_LEN(zstr))) {
+        zend_throw_exception(ex, "Invalid compressed data or uncompression error", 0);
+        RETURN_FALSE;
+    }
+
+    RETVAL_STRINGL(buf, len);
+    efree(buf);
+}
+
+void redis_pack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
+    int valfree;
+    size_t len;
+    char *val;
+    zval *zv;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zv) == FAILURE) {
+        RETURN_FALSE;
+    }
+
+    valfree = redis_pack(redis_sock, zv, &val, &len);
+    RETVAL_STRINGL(val, len);
+    if (valfree) efree(val);
+}
+
+void redis_unpack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
+    zend_string *str;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
+        RETURN_FALSE;
+    }
+
+    if (redis_unpack(redis_sock, ZSTR_VAL(str), ZSTR_LEN(str), return_value) == 0) {
+        RETURN_STR_COPY(str);
+    }
+}
 /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
diff --git a/redis_commands.h b/redis_commands.h
index be30a310f5..6550d5c695 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -52,7 +52,7 @@ int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
 int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);     
+    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
 int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
@@ -96,11 +96,11 @@ typedef int (*zrange_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                          char *,char**,int*,int*,short*,void**);
 
 int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, 
+    char *kw, char **cmd, int *cmd_len, int *withscores, short *slot,
     void **ctx);
 
 int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, 
+    char *kw, char **cmd, int *cmd_len, int *withscores, short *slot,
     void **ctx);
 
 int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
@@ -143,7 +143,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
 /* Commands which need a unique construction mechanism.  This is either because
- * they don't share a signature with any other command, or because there is 
+ * they don't share a signature with any other command, or because there is
  * specific processing we do (e.g. verifying subarguments) that make them
  * unique */
 
@@ -174,7 +174,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
-int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, 
+int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
 int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
@@ -313,22 +313,29 @@ int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
-/* Commands that don't communicate with Redis at all (such as getOption, 
+/* Commands that don't communicate with Redis at all (such as getOption,
  * setOption, _prefix, _serialize, etc).  These can be handled in one place
- * with the method of grabbing our RedisSock* object in different ways 
+ * with the method of grabbing our RedisSock* object in different ways
  * depending if this is a Redis object or a RedisCluster object. */
 
-void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, 
+void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock, redisCluster *c);
-void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, 
+void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock, redisCluster *c);
-void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, 
+void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS,
+    RedisSock *redis_sock);
+void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock);
-void redis_serialize_handler(INTERNAL_FUNCTION_PARAMETERS, 
+void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS,
+    RedisSock *redis_sock, zend_class_entry *ex);
+void redis_compress_handler(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock);
-void redis_unserialize_handler(INTERNAL_FUNCTION_PARAMETERS, 
+void redis_uncompress_handler(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock, zend_class_entry *ex);
 
+void redis_pack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock);
+void redis_unpack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock);
+
 #endif
 
 /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index b6b6934c3b..d2d8ce9dc2 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5089,6 +5089,71 @@ public function testUnserialize() {
         }
     }
 
+    public function testCompressHelpers() {
+        $compressors = self::getAvailableCompression();
+
+        $vals = ['foo', 12345, random_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_COMPRESSION, Redis::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', 12345, random_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_SERIALIZER, Redis::SERIALIZER_NONE);
+                    $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::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, '');
diff --git a/tests/TestSuite.php b/tests/TestSuite.php
index c879b33093..d4f737f994 100644
--- a/tests/TestSuite.php
+++ b/tests/TestSuite.php
@@ -39,6 +39,18 @@ public function getHost() { return $this->str_host; }
     public function getPort() { return $this->i_port; }
     public function getAuth() { return $this->auth; }
 
+    public static function getAvailableCompression() {
+        $result[] = Redis::COMPRESSION_NONE;
+        if (defined('Redis::COMPRESSION_LZF'))
+            $result[] = Redis::COMPRESSION_LZF;
+        if (defined('Redis::COMPRESSION_LZ4'))
+            $result[] = Redis::COMPRESSION_LZ4;
+        if (defined('Redis::COMPRESSION_ZSTD'))
+            $result[] = Redis::COMPRESSION_ZSTD;
+
+        return $result;
+    }
+
     /**
      * Returns the fully qualified host path,
      * which may be used directly for php.ini parameters like session.save_path

From 43b3821b3602b918195ae635b7e9e72d48519714 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 24 Jun 2021 20:48:47 +0300
Subject: [PATCH 1454/1986] GitHub Actions

---
 .github/workflows/ci.yml | 81 ++++++++++++++++++++++++++++++++++++++++
 .travis.yml              | 72 -----------------------------------
 README.markdown          |  2 +-
 3 files changed, 82 insertions(+), 73 deletions(-)
 create mode 100644 .github/workflows/ci.yml
 delete mode 100644 .travis.yml

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000..7fadd586cb
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,81 @@
+on: [push, pull_request]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    continue-on-error: ${{ matrix.experimental }}
+    strategy:
+      fail-fast: false
+      matrix:
+        php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
+        experimental: [false]
+        include:
+          - php: '8.1'
+            experimental: true
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          submodules: true
+      - name: Install PHP ${{ matrix.php }}
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php }}
+          extensions: json, igbinary, msgpack, :redis
+          coverage: none
+          tools: none
+      - name: Install dependencies
+        run: |
+          sudo add-apt-repository ppa:redislabs/redis
+          sudo add-apt-repository ppa:ondrej/php
+          sudo apt-get update
+          sudo apt-get install redis valgrind libzstd-dev liblz4-dev
+      - name: Build phpredis
+        run: |
+          phpize
+          ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4
+          sudo make install
+          sudo mkdir -p /etc/php/${{ matrix.php }}/cli/conf.d
+          echo 'extension = redis.so' | sudo tee -a /etc/php/${{ matrix.php }}/cli/conf.d/90-redis.ini
+      - name: Start redis
+        run: |
+          redis-cli SHUTDOWN NOSAVE
+          for PORT in $(seq 6379 6382) $(seq 32767 32769); do
+            redis-server --port $PORT --daemonize yes --aclfile tests/users.acl
+          done
+          redis-server --port 0 --unixsocket /tmp/redis.sock --daemonize yes --aclfile tests/users.acl
+      - name: Start redis cluster
+        run: |
+          mkdir -p tests/nodes
+          echo -n > tests/nodes/nodemap
+          for PORT in $(seq 7000 7011); do
+            redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl
+            echo 127.0.0.1:$PORT >> tests/nodes/nodemap
+          done
+          echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 --user phpredis -a phpredis
+      - name: Start redis sentinel
+        run: |
+          wget raw.githubusercontent.com/redis/redis/6.2/sentinel.conf
+          for PORT in $(seq 26379 26380); do
+            cp sentinel.conf $PORT.conf
+            sed -i '/^sentinel/d' $PORT.conf
+            redis-server $PORT.conf --port $PORT --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel auth-pass mymaster phpredis
+          done
+      - name: Run tests
+        run: |
+          php tests/TestRedis.php --class Redis --user phpredis --auth phpredis
+          php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis
+          php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis
+          php tests/TestRedis.php --class RedisSentinel --auth phpredis
+        env:
+          TEST_PHP_ARGS: -e
+      - name: Run tests using valgrind
+        continue-on-error: true
+        run: |
+          valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis --user phpredis --auth phpredis
+          valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis
+          valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis
+          valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel --auth phpredis
+        env:
+          TEST_PHP_ARGS: -e
+          USE_ZEND_ALLOC: 0
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 664f2f3848..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,72 +0,0 @@
-language: php
-php:
-  - 7.0
-  - 7.1
-  - 7.2
-  - 7.3
-  - 7.4
-  - 8.0
-  - nightly
-env: CC=gcc
-matrix:
-  allow_failures:
-    - php: 7.3
-      env: CC=clang
-    - php: 7.4
-      env: CC=clang
-    - php: 8.0
-      env: CC=clang
-    - php: nightly
-  include:
-    - php: 7.0
-      env: CC=clang
-    - php: 7.1
-      env: CC=clang
-    - php: 7.2
-      env: CC=clang
-    - php: 7.3
-      env: CC=clang
-    - php: 7.4
-      env: CC=clang
-    - php: 8.0
-      env: CC=clang
-addons:
-  apt:
-    update: true
-    sources:
-    - sourceline: ppa:redislabs/redis
-    packages:
-    - clang
-    - libzstd1-dev
-    - liblz4-dev
-    - pkg-config
-    - valgrind
-    - stunnel
-    - redis
-before_install:
-  - phpize
-  - CFGARGS="--enable-redis-lzf --enable-redis-zstd --enable-redis-lz4 --with-liblz4"
-  - pecl install igbinary && CFGARGS="$CFGARGS --enable-redis-igbinary" || true
-  - pecl install msgpack && CFGARGS="$CFGARGS --enable-redis-msgpack" || true
-  - ./configure $CFGARGS
-install: make install
-before_script:
-  - mkdir -p tests/nodes/ && echo > tests/nodes/nodemap
-  - redis-server --port 0 --daemonize yes --aclfile tests/users.acl --unixsocket /tmp/redis.sock
-  - for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes --aclfile tests/users.acl; done
-  - for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
-  - for PORT in $(seq 26379 26380); do wget raw.githubusercontent.com/redis/redis/6.0/sentinel.conf -O $PORT.conf; sed -i '/^sentinel/d' $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel auth-pass mymaster phpredis; done
-  - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 --user phpredis -a phpredis
-  - echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
-  - openssl req -x509 -newkey rsa:1024 -nodes -keyout stunnel.key -out stunnel.pem -days 1 -subj '/CN=localhost'
-  - echo -e 'key=stunnel.key\ncert=stunnel.pem\npid=/tmp/stunnel.pid\n[redis]\naccept=6378\nconnect=6379' > stunnel.conf
-  - stunnel stunnel.conf
-script:
-  - php tests/TestRedis.php --class Redis --user phpredis --auth phpredis
-  - php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis
-  - php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis
-  - php tests/TestRedis.php --class RedisSentinel --auth phpredis
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis --user phpredis --auth phpredis
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis
-  - USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel --auth phpredis
diff --git a/README.markdown b/README.markdown
index 4840f4cd78..b8251c5374 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,6 +1,6 @@
 # PhpRedis
 
-[![Build Status](https://travis-ci.com/phpredis/phpredis.svg?branch=develop)](https://travis-ci.com/phpredis/phpredis)
+[![Build Status](https://github.com/phpredis/phpredis/actions/workflows/ci.yml/badge.svg)](https://github.com/phpredis/phpredis/actions/workflows/ci.yml)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis)
 [![PHP version from Travis config](https://img.shields.io/travis/php-v/phpredis/phpredis/develop)](https://img.shields.io/travis/php-v/phpredis/phpredis/develop)
 

From 7c0ae874d4b17fbd513624bb1849d736949888c8 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 19 Jul 2021 15:10:03 +0300
Subject: [PATCH 1455/1986] Fix PHP 8.1 tests

---
 .github/workflows/ci.yml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7fadd586cb..a38dd75055 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,8 +35,7 @@ jobs:
           phpize
           ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4
           sudo make install
-          sudo mkdir -p /etc/php/${{ matrix.php }}/cli/conf.d
-          echo 'extension = redis.so' | sudo tee -a /etc/php/${{ matrix.php }}/cli/conf.d/90-redis.ini
+          echo 'extension = redis.so' | sudo tee -a $(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')/90-redis.ini
       - name: Start redis
         run: |
           redis-cli SHUTDOWN NOSAVE

From 18706d92711c376750d91d113e4e397477e8241e Mon Sep 17 00:00:00 2001
From: Nathaniel Braun 
Date: Mon, 28 Jun 2021 14:41:50 +0300
Subject: [PATCH 1456/1986] Add support for exponential backoff on retry

---
 backoff.c           | 90 +++++++++++++++++++++++++++++++++++++++++++++
 backoff.h           | 17 +++++++++
 common.h            | 88 ++++++++++++++++++++++++++------------------
 config.m4           |  2 +-
 config.w32          |  2 +-
 library.c           | 19 +++++-----
 package.xml         |  2 +
 redis.c             | 16 ++++++++
 redis_commands.c    | 37 +++++++++++++++++++
 tests/RedisTest.php | 44 ++++++++++++++++++++++
 10 files changed, 271 insertions(+), 46 deletions(-)
 create mode 100644 backoff.c
 create mode 100644 backoff.h

diff --git a/backoff.c b/backoff.c
new file mode 100644
index 0000000000..a86ab5a4c4
--- /dev/null
+++ b/backoff.c
@@ -0,0 +1,90 @@
+#include "common.h"
+
+#include 
+
+#if PHP_VERSION_ID >= 70100
+#include 
+#else
+static zend_long php_mt_rand_range(zend_long min, zend_long max) {
+    zend_long number = php_rand();
+    RAND_RANGE(number, min, max, PHP_RAND_MAX);
+    return number;
+}
+#endif
+
+#include "backoff.h"
+
+static zend_ulong random_range(zend_ulong min, zend_ulong max) {
+    if (max < min) {
+        return php_mt_rand_range(max, min);
+    }
+
+    return php_mt_rand_range(min, max);
+}
+
+static zend_ulong redis_default_backoff(struct RedisBackoff *self, unsigned int retry_index) {
+    zend_ulong backoff = retry_index ? self->base : random_range(0, self->base);
+    return MIN(self->cap, backoff);
+}
+
+static zend_ulong redis_constant_backoff(struct RedisBackoff *self, unsigned int retry_index) {
+    zend_ulong backoff = self->base;
+    return MIN(self->cap, backoff);
+}
+
+static zend_ulong redis_uniform_backoff(struct RedisBackoff *self, unsigned int retry_index) {
+    zend_ulong backoff = random_range(0, self->base);
+    return MIN(self->cap, backoff);
+}
+
+static zend_ulong redis_exponential_backoff(struct RedisBackoff *self, unsigned int retry_index) {
+    zend_ulong pow = MIN(retry_index, 10);
+    zend_ulong backoff = self->base * (1 << pow);
+    return MIN(self->cap, backoff);
+}
+
+static zend_ulong redis_full_jitter_backoff(struct RedisBackoff *self, unsigned int retry_index) {
+    zend_ulong pow = MIN(retry_index, 10);
+    zend_ulong backoff = self->base * (1 << pow);
+    zend_ulong cap = MIN(self->cap, backoff);
+    return random_range(0, self->cap);
+}
+
+static zend_ulong redis_equal_jitter_backoff(struct RedisBackoff *self, unsigned int retry_index) {
+    zend_ulong pow = MIN(retry_index, 10);
+    zend_ulong backoff = self->base * (1 << pow);
+    zend_ulong temp = MIN(self->cap, backoff);
+    return temp / 2 + random_range(0, temp) / 2;
+}
+
+static zend_ulong redis_decorrelated_jitter_backoff(struct RedisBackoff *self, unsigned int retry_index) {
+    self->previous_backoff = random_range(self->base, self->previous_backoff * 3);
+    return MIN(self->cap, self->previous_backoff);
+}
+
+typedef zend_ulong (*redis_backoff_algorithm)(struct RedisBackoff *self, unsigned int retry_index);
+
+static redis_backoff_algorithm redis_backoff_algorithms[REDIS_BACKOFF_ALGORITHMS] = {
+    redis_default_backoff,
+    redis_decorrelated_jitter_backoff,
+    redis_full_jitter_backoff,
+    redis_equal_jitter_backoff,
+    redis_exponential_backoff,
+    redis_uniform_backoff,
+    redis_constant_backoff,
+};
+
+void redis_initialize_backoff(struct RedisBackoff *self, unsigned long retry_interval) {
+    self->algorithm = 0; // default backoff
+    self->base = retry_interval;
+    self->cap = retry_interval;
+    self->previous_backoff = 0;
+}
+
+void redis_backoff_reset(struct RedisBackoff *self) {
+    self->previous_backoff = 0;
+}
+
+zend_ulong redis_backoff_compute(struct RedisBackoff *self, unsigned int retry_index) {
+    return redis_backoff_algorithms[self->algorithm](self, retry_index);
+}
diff --git a/backoff.h b/backoff.h
new file mode 100644
index 0000000000..bc6828c612
--- /dev/null
+++ b/backoff.h
@@ -0,0 +1,17 @@
+#ifndef REDIS_BACKOFF_H
+#define REDIS_BACKOFF_H
+
+/* {{{ struct RedisBackoff */
+struct RedisBackoff {
+    unsigned int algorithm;        /* index of algorithm function, returns backoff in microseconds*/
+    zend_ulong   base;             /* base backoff in microseconds */
+    zend_ulong   cap;              /* max backoff in microseconds */
+    zend_ulong   previous_backoff; /* previous backoff in microseconds */
+};
+/* }}} */
+
+void redis_initialize_backoff(struct RedisBackoff *self, unsigned long retry_interval);
+void redis_backoff_reset(struct RedisBackoff *self);
+zend_ulong redis_backoff_compute(struct RedisBackoff *self, unsigned int retry_index);
+
+#endif
diff --git a/common.h b/common.h
index 2ad7c70865..2b9ec72308 100644
--- a/common.h
+++ b/common.h
@@ -21,6 +21,8 @@
 #define NULL   ((void *) 0)
 #endif
 
+#include "backoff.h"
+
 typedef enum {
     REDIS_SOCK_STATUS_FAILED = -1,
     REDIS_SOCK_STATUS_DISCONNECTED,
@@ -83,6 +85,10 @@ typedef enum _PUBSUB_TYPE {
 #define REDIS_OPT_REPLY_LITERAL      8
 #define REDIS_OPT_COMPRESSION_LEVEL  9
 #define REDIS_OPT_NULL_MBULK_AS_NULL 10
+#define REDIS_OPT_MAX_RETRIES        11
+#define REDIS_OPT_BACKOFF_ALGORITHM  12
+#define REDIS_OPT_BACKOFF_BASE       13
+#define REDIS_OPT_BACKOFF_CAP        14
 
 /* cluster options */
 #define REDIS_FAILOVER_NONE              0
@@ -109,6 +115,16 @@ typedef enum {
 #define REDIS_SCAN_PREFIX  2
 #define REDIS_SCAN_NOPREFIX 3
 
+/* BACKOFF_ALGORITHM options */
+#define REDIS_BACKOFF_ALGORITHMS                    7
+#define REDIS_BACKOFF_ALGORITHM_DEFAULT             0
+#define REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER 1
+#define REDIS_BACKOFF_ALGORITHM_FULL_JITTER         2
+#define REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER        3
+#define REDIS_BACKOFF_ALGORITHM_EXPONENTIAL         4
+#define REDIS_BACKOFF_ALGORITHM_UNIFORM             5
+#define REDIS_BACKOFF_ALGORITHM_CONSTANT            6
+
 /* GETBIT/SETBIT offset range limits */
 #define BITOP_MIN_OFFSET 0
 #define BITOP_MAX_OFFSET 4294967295U
@@ -258,41 +274,43 @@ typedef enum {
 
 /* {{{ struct RedisSock */
 typedef struct {
-    php_stream         *stream;
-    php_stream_context *stream_ctx;
-    zend_string        *host;
-    int                port;
-    zend_string        *user;
-    zend_string        *pass;
-    double             timeout;
-    double             read_timeout;
-    long               retry_interval;
-    redis_sock_status  status;
-    int                persistent;
-    int                watching;
-    zend_string        *persistent_id;
-
-    redis_serializer   serializer;
-    int                compression;
-    int                compression_level;
-    long               dbNumber;
-
-    zend_string        *prefix;
-
-    short              mode;
-    struct fold_item   *head;
-    struct fold_item   *current;
-
-    zend_string        *pipeline_cmd;
-
-    zend_string        *err;
-
-    int                scan;
-
-    int                readonly;
-    int                reply_literal;
-    int                null_mbulk_as_null;
-    int                tcp_keepalive;
+    php_stream          *stream;
+    php_stream_context  *stream_ctx;
+    zend_string         *host;
+    int                 port;
+    zend_string         *user;
+    zend_string         *pass;
+    double              timeout;
+    double              read_timeout;
+    long                retry_interval;
+    int                 max_retries;
+    struct RedisBackoff backoff;
+    redis_sock_status   status;
+    int                 persistent;
+    int                 watching;
+    zend_string         *persistent_id;
+
+    redis_serializer    serializer;
+    int                 compression;
+    int                 compression_level;
+    long                dbNumber;
+
+    zend_string         *prefix;
+
+    short               mode;
+    struct fold_item    *head;
+    struct fold_item    *current;
+
+    zend_string         *pipeline_cmd;
+
+    zend_string         *err;
+
+    int                 scan;
+
+    int                 readonly;
+    int                 reply_literal;
+    int                 null_mbulk_as_null;
+    int                 tcp_keepalive;
 } RedisSock;
 /* }}} */
 
diff --git a/config.m4 b/config.m4
index 87dca33350..750e58ac64 100644
--- a/config.m4
+++ b/config.m4
@@ -323,5 +323,5 @@ if test "$PHP_REDIS" != "no"; then
   fi
 
   PHP_SUBST(REDIS_SHARED_LIBADD)
-  PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c $lzf_sources, $ext_shared)
+  PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c backoff.c $lzf_sources, $ext_shared)
 fi
diff --git a/config.w32 b/config.w32
index e2b4365752..751bf73dee 100644
--- a/config.w32
+++ b/config.w32
@@ -5,7 +5,7 @@ ARG_ENABLE("redis-session", "whether to enable sessions", "yes");
 ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no");
 
 if (PHP_REDIS != "no") {
-	var sources = "redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c";
+	var sources = "redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c backoff.c";
 	if (PHP_REDIS_SESSION != "no") {
 		ADD_EXTENSION_DEP("redis", "session");
 		ADD_FLAG("CFLAGS_REDIS", ' /D PHP_SESSION=1 ');
diff --git a/library.c b/library.c
index d15dad1ede..25e53a7db3 100644
--- a/library.c
+++ b/library.c
@@ -301,7 +301,7 @@ redis_error_throw(RedisSock *redis_sock)
 PHP_REDIS_API int
 redis_check_eof(RedisSock *redis_sock, int no_throw)
 {
-    int count;
+    unsigned int retry_index;
     char *errmsg;
 
     if (!redis_sock || !redis_sock->stream || redis_sock->status == REDIS_SOCK_STATUS_FAILED) {
@@ -333,18 +333,17 @@ redis_check_eof(RedisSock *redis_sock, int no_throw)
         errmsg = "Connection lost and socket is in MULTI/watching mode";
     } else {
         errmsg = "Connection lost";
-        /* TODO: configurable max retry count */
-        for (count = 0; count < 10; ++count) {
+        redis_backoff_reset(&redis_sock->backoff);
+        for (retry_index = 0; retry_index < redis_sock->max_retries; ++retry_index) {
             /* close existing stream before reconnecting */
             if (redis_sock->stream) {
                 redis_sock_disconnect(redis_sock, 1);
             }
-            // Wait for a while before trying to reconnect
-            if (redis_sock->retry_interval) {
-                // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time
-                long retry_interval = (count ? redis_sock->retry_interval : (php_rand() % redis_sock->retry_interval));
-                usleep(retry_interval);
-            }
+            /* Sleep based on our backoff algorithm */
+            zend_ulong delay = redis_backoff_compute(&redis_sock->backoff, retry_index);
+            if (delay != 0)
+                usleep(delay);
+
             /* reconnect */
             if (redis_sock_connect(redis_sock) == 0) {
                 /* check for EOF again. */
@@ -2150,6 +2149,8 @@ redis_sock_create(char *host, int host_len, int port,
     redis_sock->host = zend_string_init(host, host_len, 0);
     redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED;
     redis_sock->retry_interval = retry_interval * 1000;
+    redis_sock->max_retries = 10;
+    redis_initialize_backoff(&redis_sock->backoff, retry_interval);
     redis_sock->persistent = persistent;
 
     if (persistent && persistent_id != NULL) {
diff --git a/package.xml b/package.xml
index 21339b3928..f4df32dd65 100644
--- a/package.xml
+++ b/package.xml
@@ -88,6 +88,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
    
    
    
+   
+   
    
    
    
diff --git a/redis.c b/redis.c
index bbf5bd89a0..ad6acffd74 100644
--- a/redis.c
+++ b/redis.c
@@ -775,6 +775,22 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) {
         zend_declare_class_constant_stringl(ce, "LEFT", 4, "left", 4);
         zend_declare_class_constant_stringl(ce, "RIGHT", 5, "right", 5);
     }
+
+    /* retry/backoff options*/
+    zend_declare_class_constant_long(ce, ZEND_STRL("OPT_MAX_RETRIES"), REDIS_OPT_MAX_RETRIES);
+
+    zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_ALGORITHM"), REDIS_OPT_BACKOFF_ALGORITHM);
+    zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_DEFAULT"), REDIS_BACKOFF_ALGORITHM_DEFAULT);
+    zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_CONSTANT"), REDIS_BACKOFF_ALGORITHM_CONSTANT);
+    zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_UNIFORM"), REDIS_BACKOFF_ALGORITHM_UNIFORM);
+    zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_EXPONENTIAL"), REDIS_BACKOFF_ALGORITHM_EXPONENTIAL);
+    zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_FULL_JITTER"), REDIS_BACKOFF_ALGORITHM_FULL_JITTER);
+    zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_EQUAL_JITTER"), REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER);
+    zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_DECORRELATED_JITTER"), REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER);
+
+    zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_BASE"), REDIS_OPT_BACKOFF_BASE);
+
+    zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_CAP"), REDIS_OPT_BACKOFF_CAP);
 }
 
 static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor)
diff --git a/redis_commands.c b/redis_commands.c
index 4d463ba1e9..286d5820cc 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4308,6 +4308,14 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS,
             RETURN_LONG(redis_sock->null_mbulk_as_null);
         case REDIS_OPT_FAILOVER:
             RETURN_LONG(c->failover);
+        case REDIS_OPT_MAX_RETRIES:
+            RETURN_LONG(redis_sock->max_retries);
+        case REDIS_OPT_BACKOFF_ALGORITHM:
+            RETURN_LONG(redis_sock->backoff.algorithm);
+        case REDIS_OPT_BACKOFF_BASE:
+            RETURN_LONG(redis_sock->backoff.base / 1000);
+        case REDIS_OPT_BACKOFF_CAP:
+            RETURN_LONG(redis_sock->backoff.cap / 1000);
         default:
             RETURN_FALSE;
     }
@@ -4441,6 +4449,35 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
                 RETURN_TRUE;
             }
             break;
+        case REDIS_OPT_MAX_RETRIES:
+            val_long = zval_get_long(val);
+            if(val_long >= 0) {
+                redis_sock->max_retries = val_long;
+                RETURN_TRUE;
+            }
+            break;
+        case REDIS_OPT_BACKOFF_ALGORITHM:
+            val_long = zval_get_long(val);
+            if(val_long >= 0 &&
+               val_long < REDIS_BACKOFF_ALGORITHMS) {
+                redis_sock->backoff.algorithm = val_long;
+                RETURN_TRUE;
+            }
+            break;
+        case REDIS_OPT_BACKOFF_BASE:
+            val_long = zval_get_long(val);
+            if(val_long >= 0) {
+                redis_sock->backoff.base = val_long * 1000;
+                RETURN_TRUE;
+            }
+            break;
+        case REDIS_OPT_BACKOFF_CAP:
+            val_long = zval_get_long(val);
+            if(val_long >= 0) {
+                redis_sock->backoff.cap = val_long * 1000;
+                RETURN_TRUE;
+            }
+            break;
         EMPTY_SWITCH_DEFAULT_CASE()
     }
     RETURN_FALSE;
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index d2d8ce9dc2..b1cbd2518e 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5398,6 +5398,50 @@ public function testScanPrefix() {
         $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_MAX_RETRIES, 5);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_MAX_RETRIES), 5);
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DEFAULT);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DEFAULT);
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_CONSTANT);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_CONSTANT);
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_UNIFORM);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_UNIFORM);
+
+        $this->redis -> setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EXPONENTIAL);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EXPONENTIAL);
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_FULL_JITTER);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_FULL_JITTER);
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::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_ALGORITHM, 55555));
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 500);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 500);
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 750);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 750);
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 500);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 500);
+
+        $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 750);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 750);
+    }
+
     public function testHScan() {
         if (version_compare($this->version, "2.8.0") < 0) {
             $this->markTestSkipped();

From b5e02c7ede1fb7ff71061906bad9a8ddb73715e5 Mon Sep 17 00:00:00 2001
From: "T. Todua" <7117978+ttodua@users.noreply.github.com>
Date: Tue, 20 Jul 2021 16:18:29 +0400
Subject: [PATCH 1457/1986] Updated approach

Pickle is becoming standard after PHP 7.3, so you should include that too.
---
 INSTALL.markdown | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index 845c90d755..332f634b9a 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -1,9 +1,11 @@
-# Installation from pecl
+# Installation from pecl/pickle
 
-To pull latest stable released version, from [pecl](https://pecl.php.net/package/redis):
+To pull latest stable released version, from [pecl](https://pecl.php.net/package/redis) / [pickle](https://wiki.php.net/rfc/deprecate-pear-include-composer):
 
 ~~~
 pecl install redis
+// or 
+pickle install redis
 ~~~
 
 # Installation from sources

From 08445a8ceb11cdef3c783fb57e6b0b4db4e8dac2 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 20 Jul 2021 10:50:53 -0700
Subject: [PATCH 1458/1986] Minor fix of full jitter backoff

---
 backoff.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/backoff.c b/backoff.c
index a86ab5a4c4..d0961fcfaf 100644
--- a/backoff.c
+++ b/backoff.c
@@ -47,7 +47,7 @@ static zend_ulong redis_full_jitter_backoff(struct RedisBackoff *self, unsigned
     zend_ulong pow = MIN(retry_index, 10);
     zend_ulong backoff = self->base * (1 << pow);
     zend_ulong cap = MIN(self->cap, backoff);
-    return random_range(0, self->cap);
+    return random_range(0, cap);
 }
 
 static zend_ulong redis_equal_jitter_backoff(struct RedisBackoff *self, unsigned int retry_index) {

From d1097f6fcf309ab0aa28242c4dfa26811176fd15 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 20 Jul 2021 11:04:24 -0700
Subject: [PATCH 1459/1986] Clarify pickle is for PHP >= 7.3

See: #1991
---
 INSTALL.markdown | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index 332f634b9a..1c9cad4098 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -4,7 +4,8 @@ To pull latest stable released version, from [pecl](https://pecl.php.net/package
 
 ~~~
 pecl install redis
-// or 
+
+// If using PHP >= 7.3
 pickle install redis
 ~~~
 

From 0c04f65e069319f578a8c160ec7529428c719551 Mon Sep 17 00:00:00 2001
From: Nathaniel Braun 
Date: Wed, 21 Jul 2021 08:16:05 +0000
Subject: [PATCH 1460/1986] Add documentation for backoff algorithms

---
 README.markdown     | 36 ++++++++++++++++++++++++++++++++++++
 tests/RedisTest.php |  6 +++---
 2 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/README.markdown b/README.markdown
index b8251c5374..804d09af48 100644
--- a/README.markdown
+++ b/README.markdown
@@ -35,6 +35,7 @@ You can also make a one-time contribution with one of the links below.
 1. [Classes and methods](#classes-and-methods)
    * [Usage](#usage)
    * [Connection](#connection)
+   * [Retry and backoff](#retry-and-backoff)
    * [Server](#server)
    * [Keys and strings](#keys-and-strings)
    * [Hashes](#hashes)
@@ -428,6 +429,41 @@ _**Description**_: Sends a string to Redis, which replies with the same string
 
 *STRING*: the same message.
 
+## Retry and backoff
+
+1. [Maximum retries](#maximum-retries)
+1. [Backoff algorithms](#backoff-algorithms)
+
+### Maximum retries
+You can set and get the maximum retries upon connection issues using the `OPT_MAX_RETRIES` option. Note that this is the number of _retries_, meaning if you set this option to _n_, there will be a maximum _n+1_ attemps overall. Defaults to 10.
+
+##### *Example*
+
+~~~php
+$redis->setOption(Redis::OPT_MAX_RETRIES, 5);
+$redis->getOption(Redis::OPT_MAX_RETRIES);
+~~~
+
+### Backoff algorithms
+You can set the backoff algorithm using the `Redis::OPT_BACKOFF_ALGORITHM` option and choose among the following algorithms described in this blog post by Marc Brooker from AWS: [Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter):
+
+* Default: `Redis::BACKOFF_ALGORITHM_DEFAULT`
+* Decorrelated jitter: `Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER`
+* Full jitter: `Redis::BACKOFF_ALGORITHM_FULL_JITTER`
+* Equal jitter: `Redis::BACKOFF_ALGORITHM_EQUAL_JITTER`
+* Exponential: `Redis::BACKOFF_ALGORITHM_EXPONENTIAL`
+* Uniform: `Redis::BACKOFF_ALGORITHM_UNIFORM`
+* Constant: `Redis::BACKOFF_ALGORITHM_CONSTANT`
+
+These algorithms depend on the _base_ and _cap_ parameters, both in milliseconds, which you can set using the `Redis::OPT_BACKOFF_BASE` and `Redis::OPT_BACKOFF_CAP` options, respectively.
+
+##### *Example*
+
+~~~php
+$redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER);
+$redis->setOption(Redis::OPT_BACKOFF_BASE, 500); // base for backoff computation: 500ms
+$redis->setOption(Redis::OPT_BACKOFF_CAP, 750); // backoff time capped at 750ms
+~~~
 
 ## Server
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index b1cbd2518e..1a3c468fa4 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5406,9 +5406,6 @@ public function testMaxRetriesOption() {
     }
 
     public function testBackoffOptions() {
-        $this->redis->setOption(Redis::OPT_MAX_RETRIES, 5);
-        $this->assertEquals($this->redis->getOption(Redis::OPT_MAX_RETRIES), 5);
-
         $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DEFAULT);
         $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DEFAULT);
 
@@ -5421,6 +5418,9 @@ public function testBackoffOptions() {
         $this->redis -> setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EXPONENTIAL);
         $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EXPONENTIAL);
 
+        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EQUAL_JITTER);
+        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EQUAL_JITTER);
+
         $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_FULL_JITTER);
         $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_FULL_JITTER);
 

From 4d5d74dcf7b2f3faa6a9b5fe963dce511a4fc15b Mon Sep 17 00:00:00 2001
From: "T. Todua" <7117978+ttodua@users.noreply.github.com>
Date: Tue, 27 Jul 2021 23:37:04 +0400
Subject: [PATCH 1461/1986] Update INSTALL.markdown

As a quick-hint to simplify process for many newcomers.
---
 INSTALL.markdown | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index 1c9cad4098..aeeced1c92 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -14,6 +14,8 @@ pickle install redis
 To build this extension for the sources tree:
 
 ~~~
+git clone https://github.com/phpredis/phpredis.git
+cd phpredis
 phpize
 ./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd]
 make && make install

From f88159a724ed4517905f3a59bfb6c4607cd2bc62 Mon Sep 17 00:00:00 2001
From: "T. Todua" <7117978+ttodua@users.noreply.github.com>
Date: Tue, 27 Jul 2021 23:38:45 +0400
Subject: [PATCH 1462/1986] Update INSTALL.markdown

---
 INSTALL.markdown | 1 +
 1 file changed, 1 insertion(+)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index aeeced1c92..94593eff60 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -19,6 +19,7 @@ cd phpredis
 phpize
 ./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd]
 make && make install
+cd .. && rm -r phpredis
 ~~~
 
 If you would like phpredis to serialize your data using the igbinary library, run configure with `--enable-redis-igbinary`.

From edbf520ca41d44c7b3c87350f986f80a040b4409 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Wed, 25 Aug 2021 14:59:30 -0700
Subject: [PATCH 1463/1986] Fix sAdd documentation (see #2002)

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 804d09af48..441da1c61c 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2291,7 +2291,7 @@ $redis->lLen('key1');/* 2 */
 
 ### sAdd
 -----
-_**Description**_: Adds a value to the set value stored at key. If this value is already in the set, `FALSE` is returned.
+_**Description**_: Adds a value to the set value stored at key.
 ##### *Parameters*
 *key*  
 *value*

From edac508ef5fc0b397f445504c01593b44e001201 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Mon, 14 Sep 2020 16:51:32 +0200
Subject: [PATCH 1464/1986] use stub/arginfo for RedisSentinel

---
 library.h                | 26 +++++++++++++++
 redis.c                  |  9 +++---
 redis_array.c            |  7 ++++-
 redis_sentinel.c         | 46 +++++++++------------------
 redis_sentinel.h         | 13 +-------
 redis_sentinel.stub.php  | 28 +++++++++++++++++
 redis_sentinel_arginfo.h | 68 ++++++++++++++++++++++++++++++++++++++++
 7 files changed, 147 insertions(+), 50 deletions(-)
 create mode 100644 redis_sentinel.stub.php
 create mode 100644 redis_sentinel_arginfo.h

diff --git a/library.h b/library.h
index 2ae5599260..f6d59364c0 100644
--- a/library.h
+++ b/library.h
@@ -23,12 +23,38 @@
 #define redis_sock_write_sstr(redis_sock, sstr) \
     redis_sock_write(redis_sock, (sstr)->c, (sstr)->len)
 
+#if PHP_VERSION_ID < 70200
+/* drop return type hinting in PHP 7.0 and 7.1*/
+#undef  ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX
+#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
+        ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args)
+#endif
+
 #if PHP_VERSION_ID < 80000
     #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(ZSTR_VAL((zstr)), ZSTR_LEN((zstr)))
+
+    /* use RedisException when ValueError not available */
+    #define REDIS_VALUE_EXCEPTION(m) REDIS_THROW_EXCEPTION(m, 0)
+    #define RETURN_THROWS() RETURN_FALSE
+
+    /* default value only managed in 8+ */
+    #define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) \
+            ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)
+
+    /* union type only managed in 8+ */
+    #define ZEND_ARG_TYPE_MASK(pass_by_ref, name, type_mask, default_value) ZEND_ARG_INFO(pass_by_ref, name)
+
+    #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, return_reference, required_num_args, type) \
+            ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args)
+
+    #define IS_MIXED 0
 #else
     #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(zstr)
+
+    #define REDIS_VALUE_EXCEPTION(m) zend_value_error(m)
 #endif
 
+
 void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id);
 
 PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass);
diff --git a/redis.c b/redis.c
index ad6acffd74..054ed92493 100644
--- a/redis.c
+++ b/redis.c
@@ -64,7 +64,6 @@ extern int le_cluster_slot_cache;
 
 extern zend_function_entry redis_array_functions[];
 extern zend_function_entry redis_cluster_functions[];
-extern zend_function_entry redis_sentinel_functions[];
 
 int le_redis_pconnect;
 
@@ -873,7 +872,7 @@ PHP_MINIT_FUNCTION(redis)
     redis_cluster_ce->create_object = create_cluster_context;
 
     /* RedisSentinel class */
-    INIT_CLASS_ENTRY(redis_sentinel_class_entry, "RedisSentinel", redis_sentinel_functions);
+    INIT_CLASS_ENTRY(redis_sentinel_class_entry, "RedisSentinel", redis_sentinel_get_methods());
     redis_sentinel_ce = zend_register_internal_class(&redis_sentinel_class_entry);
     redis_sentinel_ce->create_object = create_sentinel_object;
 
@@ -1080,17 +1079,17 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
     }
 
     if (timeout < 0L || timeout > INT_MAX) {
-        REDIS_THROW_EXCEPTION("Invalid connect timeout", 0);
+        REDIS_VALUE_EXCEPTION("Invalid connect timeout");
         return FAILURE;
     }
 
     if (read_timeout < 0L || read_timeout > INT_MAX) {
-        REDIS_THROW_EXCEPTION("Invalid read timeout", 0);
+        REDIS_VALUE_EXCEPTION("Invalid read timeout");
         return FAILURE;
     }
 
     if (retry_interval < 0L || retry_interval > INT_MAX) {
-        REDIS_THROW_EXCEPTION("Invalid retry interval", 0);
+        REDIS_VALUE_EXCEPTION("Invalid retry interval");
         return FAILURE;
     }
 
diff --git a/redis_array.c b/redis_array.c
index 9d6883c305..7e7f600798 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -38,7 +38,7 @@ extern zend_class_entry *redis_ce;
 zend_class_entry *redis_array_ce;
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
-    ZEND_ARG_INFO(0, name_or_hosts)
+    ZEND_ARG_TYPE_MASK(0, name_or_hosts, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
     ZEND_ARG_ARRAY_INFO(0, options, 0)
 ZEND_END_ARG_INFO()
 
@@ -239,7 +239,12 @@ PHP_METHOD(RedisArray, __construct)
      *        for ages so we can't really change it until the next major version.
      */
     if (Z_TYPE_P(z0) != IS_ARRAY && Z_TYPE_P(z0) != IS_STRING)
+#if PHP_VERSION_ID < 80000
         WRONG_PARAM_COUNT;
+#else
+        zend_argument_type_error(1, "must be of type string|array, %s given", zend_zval_type_name(z0));
+        RETURN_THROWS();
+#endif
 
     /* If it's a string we want to load the array from ini information */
     if (Z_TYPE_P(z0) == IS_STRING) {
diff --git a/redis_sentinel.c b/redis_sentinel.c
index e5148e99ae..b89cdb3d19 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -23,30 +23,12 @@
 zend_class_entry *redis_sentinel_ce;
 extern zend_class_entry *redis_exception_ce;
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
-    ZEND_ARG_INFO(0, host)
-    ZEND_ARG_INFO(0, port)
-    ZEND_ARG_INFO(0, timeout)
-    ZEND_ARG_INFO(0, persistent)
-    ZEND_ARG_INFO(0, retry_interval)
-    ZEND_ARG_INFO(0, read_timeout)
-ZEND_END_ARG_INFO()
-
-zend_function_entry redis_sentinel_functions[] = {
-     PHP_ME(RedisSentinel, __construct, arginfo_ctor, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, ckquorum, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, failover, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, flushconfig, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, getMasterAddrByName, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, master, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, masters, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, myid, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, ping, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, reset, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, sentinels, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisSentinel, slaves, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_FE_END
-};
+#include "redis_sentinel_arginfo.h"
+
+extern const zend_function_entry *redis_sentinel_get_methods(void)
+{
+    return class_RedisSentinel_methods;
+}
 
 PHP_METHOD(RedisSentinel, __construct)
 {
@@ -66,23 +48,23 @@ PHP_METHOD(RedisSentinel, __construct)
     }
 
     if (port < 0 || port > UINT16_MAX) {
-        REDIS_THROW_EXCEPTION("Invalid port", 0);
-        RETURN_FALSE;
+        REDIS_VALUE_EXCEPTION("Invalid port");
+        RETURN_THROWS();
     }
 
     if (timeout < 0L || timeout > INT_MAX) {
-        REDIS_THROW_EXCEPTION("Invalid connect timeout", 0);
-        RETURN_FALSE;
+        REDIS_VALUE_EXCEPTION("Invalid connect timeout");
+        RETURN_THROWS();
     }
 
     if (read_timeout < 0L || read_timeout > INT_MAX) {
-        REDIS_THROW_EXCEPTION("Invalid read timeout", 0);
-        RETURN_FALSE;
+        REDIS_VALUE_EXCEPTION("Invalid read timeout");
+        RETURN_THROWS();
     }
 
     if (retry_interval < 0L || retry_interval > INT_MAX) {
-        REDIS_THROW_EXCEPTION("Invalid retry interval", 0);
-        RETURN_FALSE;
+        REDIS_VALUE_EXCEPTION("Invalid retry interval");
+        RETURN_THROWS();
     }
 
     if (zv) {
diff --git a/redis_sentinel.h b/redis_sentinel.h
index b09ce0cfe1..a24c5c0bcb 100644
--- a/redis_sentinel.h
+++ b/redis_sentinel.h
@@ -5,17 +5,6 @@
 
 #define PHP_REDIS_SENTINEL_VERSION "0.1"
 
-PHP_METHOD(RedisSentinel, __construct);
-PHP_METHOD(RedisSentinel, ckquorum);
-PHP_METHOD(RedisSentinel, failover);
-PHP_METHOD(RedisSentinel, flushconfig);
-PHP_METHOD(RedisSentinel, getMasterAddrByName);
-PHP_METHOD(RedisSentinel, master);
-PHP_METHOD(RedisSentinel, masters);
-PHP_METHOD(RedisSentinel, myid);
-PHP_METHOD(RedisSentinel, ping);
-PHP_METHOD(RedisSentinel, reset);
-PHP_METHOD(RedisSentinel, sentinels);
-PHP_METHOD(RedisSentinel, slaves);
+extern const zend_function_entry *redis_sentinel_get_methods(void);
 
 #endif /* REDIS_SENTINEL_H */
diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php
new file mode 100644
index 0000000000..11309c0bf5
--- /dev/null
+++ b/redis_sentinel.stub.php
@@ -0,0 +1,28 @@
+
Date: Wed, 16 Sep 2020 10:18:56 +0200
Subject: [PATCH 1465/1986] drop return type hinting

---
 redis_sentinel.stub.php  | 30 ++++++++++++++++++++----------
 redis_sentinel_arginfo.h | 20 +++++++++-----------
 2 files changed, 29 insertions(+), 21 deletions(-)

diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php
index 11309c0bf5..34394ee682 100644
--- a/redis_sentinel.stub.php
+++ b/redis_sentinel.stub.php
@@ -6,23 +6,33 @@ class RedisSentinel {
 
     public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = NULL, int $retry_interval = 0, float $read_timeout = 0);
 
-    public function ckquorum(string $master): bool;
+	/** @return bool|RedisSentinel */
+    public function ckquorum(string $master);
 
-    public function failover(string $master): bool;
+	/** @return bool|RedisSentinel */
+    public function failover(string $master);
 
-    public function flushconfig(): bool;
+	/** @return bool|RedisSentinel */
+    public function flushconfig();
 
-    public function getMasterAddrByName(string $master): array|false;
+	/** @return array|bool|RedisSentinel */
+    public function getMasterAddrByName(string $master);
 
-    public function master(string $master): array|false;
+	/** @return array|bool|RedisSentinel */
+    public function master(string $master);
 
-    public function masters(): array|false;
+	/** @return array|bool|RedisSentinel */
+    public function masters(): array;
 
-    public function ping(): bool;
+	/** @return bool|RedisSentinel */
+    public function ping();
 
-    public function reset(string $pattern): bool;
+	/** @return bool|RedisSentinel */
+    public function reset(string $pattern);
 
-    public function sentinels(string $master): array|false;
+	/** @return array|bool|RedisSentinel */
+    public function sentinels(string $master);
 
-    public function slaves(string $master): array|false;
+	/** @return array|bool|RedisSentinel */
+    public function slaves(string $master);
 }
diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h
index b5a9fa90d0..935609e8dd 100644
--- a/redis_sentinel_arginfo.h
+++ b/redis_sentinel_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: cfb8ad8fbaaed2ecae02a1385d26e9645364ba9d */
+ * Stub hash: a054acbf095ee7d0215af7481fe06eb397b0c377 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
@@ -10,33 +10,31 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, master, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisSentinel_failover arginfo_class_RedisSentinel_ckquorum
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisSentinel_flushconfig, 0, 0, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_flushconfig, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisSentinel_getMasterAddrByName, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_INFO(0, master, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisSentinel_getMasterAddrByName arginfo_class_RedisSentinel_ckquorum
 
-#define arginfo_class_RedisSentinel_master arginfo_class_RedisSentinel_getMasterAddrByName
+#define arginfo_class_RedisSentinel_master arginfo_class_RedisSentinel_ckquorum
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisSentinel_masters, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisSentinel_masters, 0, 0, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisSentinel_ping arginfo_class_RedisSentinel_flushconfig
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisSentinel_reset, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_reset, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisSentinel_sentinels arginfo_class_RedisSentinel_getMasterAddrByName
+#define arginfo_class_RedisSentinel_sentinels arginfo_class_RedisSentinel_ckquorum
 
-#define arginfo_class_RedisSentinel_slaves arginfo_class_RedisSentinel_getMasterAddrByName
+#define arginfo_class_RedisSentinel_slaves arginfo_class_RedisSentinel_ckquorum
 
 
 ZEND_METHOD(RedisSentinel, __construct);

From 7e621b99da443938ed97d8bba94e3b06a79ea231 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Wed, 16 Sep 2020 10:20:17 +0200
Subject: [PATCH 1466/1986] use zend_parse_parameters_none

---
 redis.c          | 7 ++++---
 redis_commands.c | 3 ++-
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/redis.c b/redis.c
index 054ed92493..d8b9328732 100644
--- a/redis.c
+++ b/redis.c
@@ -993,7 +993,7 @@ PHP_MINFO_FUNCTION(redis)
     Public constructor */
 PHP_METHOD(Redis, __construct)
 {
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
+    if (zend_parse_parameters_none() == FAILURE) {
         RETURN_FALSE;
     }
 }
@@ -1003,7 +1003,7 @@ PHP_METHOD(Redis, __construct)
     Public Destructor
  */
 PHP_METHOD(Redis,__destruct) {
-    if(zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
+    if (zend_parse_parameters_none() == FAILURE) {
         RETURN_FALSE;
     }
 
@@ -3532,8 +3532,9 @@ PHP_METHOD(Redis, getAuth) {
     RedisSock *redis_sock;
     zval zret;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE)
+    if (zend_parse_parameters_none() == FAILURE) {
         RETURN_FALSE;
+    }
 
     redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU);
     if (redis_sock == NULL)
diff --git a/redis_commands.c b/redis_commands.c
index 286d5820cc..4e0647d471 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4246,7 +4246,8 @@ int
 redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
+    if (zend_parse_parameters_none() == FAILURE) {
+
         return FAILURE;
     }
     *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "s", kw, strlen(kw));

From 9b02ea0fa65422ab64252462b2cba0436389dfdf Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Wed, 16 Sep 2020 11:11:58 +0200
Subject: [PATCH 1467/1986] add some Redis methods

---
 redis.stub.php  | 135 +++++++++++++++++++++++++++++++
 redis_arginfo.h | 209 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 344 insertions(+)
 create mode 100644 redis.stub.php
 create mode 100644 redis_arginfo.h

diff --git a/redis.stub.php b/redis.stub.php
new file mode 100644
index 0000000000..a43c2bfe20
--- /dev/null
+++ b/redis.stub.php
@@ -0,0 +1,135 @@
+
Date: Wed, 16 Sep 2020 11:41:36 +0200
Subject: [PATCH 1468/1986] drop return type hinting (missing)

---
 redis_sentinel.stub.php  | 2 +-
 redis_sentinel_arginfo.h | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php
index 34394ee682..1ce728b9d9 100644
--- a/redis_sentinel.stub.php
+++ b/redis_sentinel.stub.php
@@ -22,7 +22,7 @@ public function getMasterAddrByName(string $master);
     public function master(string $master);
 
 	/** @return array|bool|RedisSentinel */
-    public function masters(): array;
+    public function masters();
 
 	/** @return bool|RedisSentinel */
     public function ping();
diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h
index 935609e8dd..9c5c735596 100644
--- a/redis_sentinel_arginfo.h
+++ b/redis_sentinel_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a054acbf095ee7d0215af7481fe06eb397b0c377 */
+ * Stub hash: 779d2b82a083131e73402389db47d08355a2417e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
@@ -23,8 +23,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisSentinel_master arginfo_class_RedisSentinel_ckquorum
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisSentinel_masters, 0, 0, IS_ARRAY, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisSentinel_masters arginfo_class_RedisSentinel_flushconfig
 
 #define arginfo_class_RedisSentinel_ping arginfo_class_RedisSentinel_flushconfig
 

From fe344e3bbf11dee5db8b3e8fe8a038d40a8b87ee Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Mon, 5 Oct 2020 15:56:31 +0200
Subject: [PATCH 1469/1986] switch to legacy arginfo for PHP < 8

---
 library.h                       |  19 ---
 redis_legacy_arginfo.h          | 202 ++++++++++++++++++++++++++++++++
 redis_sentinel.c                |   4 +
 redis_sentinel_legacy_arginfo.h |  65 ++++++++++
 4 files changed, 271 insertions(+), 19 deletions(-)
 create mode 100644 redis_legacy_arginfo.h
 create mode 100644 redis_sentinel_legacy_arginfo.h

diff --git a/library.h b/library.h
index f6d59364c0..293012c6b5 100644
--- a/library.h
+++ b/library.h
@@ -23,31 +23,12 @@
 #define redis_sock_write_sstr(redis_sock, sstr) \
     redis_sock_write(redis_sock, (sstr)->c, (sstr)->len)
 
-#if PHP_VERSION_ID < 70200
-/* drop return type hinting in PHP 7.0 and 7.1*/
-#undef  ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX
-#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
-        ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args)
-#endif
-
 #if PHP_VERSION_ID < 80000
     #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(ZSTR_VAL((zstr)), ZSTR_LEN((zstr)))
 
     /* use RedisException when ValueError not available */
     #define REDIS_VALUE_EXCEPTION(m) REDIS_THROW_EXCEPTION(m, 0)
     #define RETURN_THROWS() RETURN_FALSE
-
-    /* default value only managed in 8+ */
-    #define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) \
-            ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)
-
-    /* union type only managed in 8+ */
-    #define ZEND_ARG_TYPE_MASK(pass_by_ref, name, type_mask, default_value) ZEND_ARG_INFO(pass_by_ref, name)
-
-    #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, return_reference, required_num_args, type) \
-            ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, required_num_args)
-
-    #define IS_MIXED 0
 #else
     #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(zstr)
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
new file mode 100644
index 0000000000..3e3a8cfced
--- /dev/null
+++ b/redis_legacy_arginfo.h
@@ -0,0 +1,202 @@
+/* This is a generated file, edit the .stub.php file instead.
+ * Stub hash: be75361e8e76c8a25455d7c40a36735b56a9e8a0 */
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis___destruct arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_connect, 0, 0, 1)
+	ZEND_ARG_INFO(0, host)
+	ZEND_ARG_INFO(0, port)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, persistent_id)
+	ZEND_ARG_INFO(0, retry_interval)
+	ZEND_ARG_INFO(0, read_timeout)
+	ZEND_ARG_INFO(0, context)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitop, 0, 0, 3)
+	ZEND_ARG_INFO(0, operation)
+	ZEND_ARG_INFO(0, deskey)
+	ZEND_ARG_INFO(0, srckey)
+	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, bit)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_close arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, opt)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setex, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, expire)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_psetex arginfo_class_Redis_setex
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setnx, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_getset arginfo_class_Redis_setnx
+
+#define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1)
+	ZEND_ARG_INFO(0, str)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rename, 0, 0, 2)
+	ZEND_ARG_INFO(0, key_src)
+	ZEND_ARG_INFO(0, key_dst)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_get, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
+	ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_incr arginfo_class_Redis_get
+
+#define arginfo_class_Redis_incrBy arginfo_class_Redis_setnx
+
+#define arginfo_class_Redis_incrByFloat arginfo_class_Redis_setnx
+
+#define arginfo_class_Redis_decr arginfo_class_Redis_get
+
+#define arginfo_class_Redis_decrBy arginfo_class_Redis_setnx
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
+	ZEND_ARG_INFO(0, keys)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_exists arginfo_class_Redis_get
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_unlink arginfo_class_Redis_del
+
+#define arginfo_class_Redis_watch arginfo_class_Redis_del
+
+#define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
+	ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_keys arginfo_class_Redis_get
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
+	ZEND_ARG_INFO(0, subcmd)
+	ZEND_ARG_VARIADIC_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_delete arginfo_class_Redis_del
+
+#define arginfo_class_Redis_open arginfo_class_Redis_connect
+
+#define arginfo_class_Redis_popen arginfo_class_Redis_connect
+
+
+ZEND_METHOD(Redis, __construct);
+ZEND_METHOD(Redis, __destruct);
+ZEND_METHOD(Redis, connect);
+ZEND_METHOD(Redis, pconnect);
+ZEND_METHOD(Redis, bitop);
+ZEND_METHOD(Redis, bitcount);
+ZEND_METHOD(Redis, bitpos);
+ZEND_METHOD(Redis, close);
+ZEND_METHOD(Redis, set);
+ZEND_METHOD(Redis, setex);
+ZEND_METHOD(Redis, psetex);
+ZEND_METHOD(Redis, setnx);
+ZEND_METHOD(Redis, getset);
+ZEND_METHOD(Redis, randomKey);
+ZEND_METHOD(Redis, echo);
+ZEND_METHOD(Redis, rename);
+ZEND_METHOD(Redis, renameNx);
+ZEND_METHOD(Redis, get);
+ZEND_METHOD(Redis, ping);
+ZEND_METHOD(Redis, incr);
+ZEND_METHOD(Redis, incrBy);
+ZEND_METHOD(Redis, incrByFloat);
+ZEND_METHOD(Redis, decr);
+ZEND_METHOD(Redis, decrBy);
+ZEND_METHOD(Redis, mget);
+ZEND_METHOD(Redis, exists);
+ZEND_METHOD(Redis, del);
+ZEND_METHOD(Redis, unlink);
+ZEND_METHOD(Redis, watch);
+ZEND_METHOD(Redis, unwatch);
+ZEND_METHOD(Redis, keys);
+ZEND_METHOD(Redis, acl);
+
+
+static const zend_function_entry class_Redis_methods[] = {
+	ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, close, arginfo_class_Redis_close, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, randomKey, arginfo_class_Redis_randomKey, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC)
+	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_FE_END
+};
diff --git a/redis_sentinel.c b/redis_sentinel.c
index b89cdb3d19..ca72640e17 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -23,7 +23,11 @@
 zend_class_entry *redis_sentinel_ce;
 extern zend_class_entry *redis_exception_ce;
 
+#if PHP_VERSION_ID < 80000
+#include "redis_sentinel_legacy_arginfo.h"
+#else
 #include "redis_sentinel_arginfo.h"
+#endif
 
 extern const zend_function_entry *redis_sentinel_get_methods(void)
 {
diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h
new file mode 100644
index 0000000000..76d222cec8
--- /dev/null
+++ b/redis_sentinel_legacy_arginfo.h
@@ -0,0 +1,65 @@
+/* This is a generated file, edit the .stub.php file instead.
+ * Stub hash: 779d2b82a083131e73402389db47d08355a2417e */
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
+	ZEND_ARG_INFO(0, host)
+	ZEND_ARG_INFO(0, port)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, persistent)
+	ZEND_ARG_INFO(0, retry_interval)
+	ZEND_ARG_INFO(0, read_timeout)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1)
+	ZEND_ARG_INFO(0, master)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisSentinel_failover arginfo_class_RedisSentinel_ckquorum
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_flushconfig, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisSentinel_getMasterAddrByName arginfo_class_RedisSentinel_ckquorum
+
+#define arginfo_class_RedisSentinel_master arginfo_class_RedisSentinel_ckquorum
+
+#define arginfo_class_RedisSentinel_masters arginfo_class_RedisSentinel_flushconfig
+
+#define arginfo_class_RedisSentinel_ping arginfo_class_RedisSentinel_flushconfig
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_reset, 0, 0, 1)
+	ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisSentinel_sentinels arginfo_class_RedisSentinel_ckquorum
+
+#define arginfo_class_RedisSentinel_slaves arginfo_class_RedisSentinel_ckquorum
+
+
+ZEND_METHOD(RedisSentinel, __construct);
+ZEND_METHOD(RedisSentinel, ckquorum);
+ZEND_METHOD(RedisSentinel, failover);
+ZEND_METHOD(RedisSentinel, flushconfig);
+ZEND_METHOD(RedisSentinel, getMasterAddrByName);
+ZEND_METHOD(RedisSentinel, master);
+ZEND_METHOD(RedisSentinel, masters);
+ZEND_METHOD(RedisSentinel, ping);
+ZEND_METHOD(RedisSentinel, reset);
+ZEND_METHOD(RedisSentinel, sentinels);
+ZEND_METHOD(RedisSentinel, slaves);
+
+
+static const zend_function_entry class_RedisSentinel_methods[] = {
+	ZEND_ME(RedisSentinel, __construct, arginfo_class_RedisSentinel___construct, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, ckquorum, arginfo_class_RedisSentinel_ckquorum, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, failover, arginfo_class_RedisSentinel_failover, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, flushconfig, arginfo_class_RedisSentinel_flushconfig, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, getMasterAddrByName, arginfo_class_RedisSentinel_getMasterAddrByName, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, master, arginfo_class_RedisSentinel_master, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, masters, arginfo_class_RedisSentinel_masters, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, ping, arginfo_class_RedisSentinel_ping, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, reset, arginfo_class_RedisSentinel_reset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, sentinels, arginfo_class_RedisSentinel_sentinels, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, slaves, arginfo_class_RedisSentinel_slaves, ZEND_ACC_PUBLIC)
+	ZEND_FE_END
+};

From 91da008b3829676ab48cacc641a85543bc40820c Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Mon, 5 Oct 2020 16:25:12 +0200
Subject: [PATCH 1470/1986] few more Redis methods

---
 redis.stub.php  | 50 +++++++++++++++++++++++++++++-
 redis_arginfo.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 128 insertions(+), 4 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index a43c2bfe20..b41a43ce54 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -105,7 +105,7 @@ public function unwatch();
     public function keys(string $pattern);
 
 	/** @return int|Redis */
-    public function keys(string $key);
+    public function type(string $key);
 
     /**
      * @param string $args 
@@ -113,6 +113,54 @@ public function keys(string $key);
      */
     public function acl(string $subcmd, ...$args);
 
+	/** @return int|Redis */
+    public function append(string $key, mixed $value);
+
+	/** @return string|Redis */
+    public function getRange(string $key, int $start, int $end);
+
+	/** @return int|Redis */
+    public function setRange(string $key, int $start, string $value);
+
+	/** @return int|Redis */
+    public function getBit(string $key, int $idx);
+
+	/** @return int|Redis */
+    public function setBit(string $key, int $idx, bool $value);
+
+	/** @return int|Redis */
+    public function strlen(string $key);
+
+    /**
+     * @param mixed $elements
+     * @return int|Redis
+     */
+    public function lPush(string $key, ...$elements);
+
+    /**
+     * @param mixed $elements
+     * @return int|Redis
+     */
+    public function rPush(string $key, ...$elements);
+
+    /**
+     * @param mixed $elements
+     * @return int|Redis
+     */
+    public function lInsert(string $key, string $pos, mixed $pivot, mixed $value);
+
+	/** @return int|Redis */
+    public function lPushx(string $key, mixed $value);
+
+	/** @return int|Redis */
+    public function rPushx(string $key, mixed $value);
+
+	/** @return string|Redis */
+    public function lPop(string $key);
+
+	/** @return string|Redis */
+    public function rPop(string $key);
+
     /**
      * @param string $otherkeys 
      * @deprecated
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 21c7a95229..5452b20ac3 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: be75361e8e76c8a25455d7c40a36735b56a9e8a0 */
+ * Stub hash: d24829fc760465f81da2364ab820d0d20215f1da */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -120,13 +120,62 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_keys arginfo_class_Redis_get
+#define arginfo_class_Redis_type arginfo_class_Redis_get
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_INFO(0, args)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_append arginfo_class_Redis_setnx
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_strlen arginfo_class_Redis_get
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_INFO(0, elements)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_rPush arginfo_class_Redis_lPush
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, pos, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, pivot, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_lPushx arginfo_class_Redis_setnx
+
+#define arginfo_class_Redis_rPushx arginfo_class_Redis_setnx
+
+#define arginfo_class_Redis_lPop arginfo_class_Redis_get
+
+#define arginfo_class_Redis_rPop arginfo_class_Redis_get
+
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
 
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
@@ -165,7 +214,21 @@ ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
 ZEND_METHOD(Redis, keys);
+ZEND_METHOD(Redis, type);
 ZEND_METHOD(Redis, acl);
+ZEND_METHOD(Redis, append);
+ZEND_METHOD(Redis, getRange);
+ZEND_METHOD(Redis, setRange);
+ZEND_METHOD(Redis, getBit);
+ZEND_METHOD(Redis, setBit);
+ZEND_METHOD(Redis, strlen);
+ZEND_METHOD(Redis, lPush);
+ZEND_METHOD(Redis, rPush);
+ZEND_METHOD(Redis, lInsert);
+ZEND_METHOD(Redis, lPushx);
+ZEND_METHOD(Redis, rPushx);
+ZEND_METHOD(Redis, lPop);
+ZEND_METHOD(Redis, rPop);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -200,8 +263,21 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)

From b84550eb4001f68f6ea7ad51a641e4ee1bc79031 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Mon, 5 Oct 2020 16:31:31 +0200
Subject: [PATCH 1471/1986] use @generate-legacy-arginfo

---
 redis.stub.php                  |  5 +-
 redis_arginfo.h                 |  2 +-
 redis_legacy_arginfo.h          | 82 +++++++++++++++++++++++++++++++--
 redis_sentinel.stub.php         |  5 +-
 redis_sentinel_arginfo.h        |  2 +-
 redis_sentinel_legacy_arginfo.h |  2 +-
 6 files changed, 90 insertions(+), 8 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index b41a43ce54..5bac5a479c 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1,6 +1,9 @@
 
Date: Fri, 6 Nov 2020 08:33:31 +0100
Subject: [PATCH 1472/1986] add Redis::ping

---
 redis.stub.php         | 3 +++
 redis_arginfo.h        | 7 ++++++-
 redis_legacy_arginfo.h | 7 ++++++-
 3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 5bac5a479c..880e25d401 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -172,6 +172,9 @@ public function rPop(string $key);
      */
     public function delete(array|string $key, ...$otherkeys);
 
+	/** @return mixed|Redis */
+    public function ping(string $message = NULL);
+
     /**
      * @deprecated
      * @alias Redis::connect
diff --git a/redis_arginfo.h b/redis_arginfo.h
index af8403ce14..a2726727b3 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 98b4be46b77ced79e9136821ed9c510fc8d596b9 */
+ * Stub hash: b3a02d01273649649ff8627a90ab9c46b9beddbb */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -178,6 +178,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "NULL")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
 
 #define arginfo_class_Redis_popen arginfo_class_Redis_connect
@@ -279,6 +283,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_FE_END
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index c28c1c46f7..6f257eaabd 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 98b4be46b77ced79e9136821ed9c510fc8d596b9 */
+ * Stub hash: b3a02d01273649649ff8627a90ab9c46b9beddbb */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -171,6 +171,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
+	ZEND_ARG_INFO(0, message)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
 
 #define arginfo_class_Redis_popen arginfo_class_Redis_connect
@@ -272,6 +276,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_FE_END

From c8ae53952349441ae0ecaedf60bd4381d33cba76 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 12 Dec 2020 20:44:45 +0200
Subject: [PATCH 1473/1986] Use stub/arginfo for RedisArray

---
 redis.c                         |   2 +-
 redis_array.c                   | 100 +++------------------
 redis_array.h                   |  31 +------
 redis_array.stub.php            |  64 ++++++++++++++
 redis_array_arginfo.h           | 152 ++++++++++++++++++++++++++++++++
 redis_array_legacy_arginfo.h    | 149 +++++++++++++++++++++++++++++++
 redis_sentinel_arginfo.h        |   2 +-
 redis_sentinel_legacy_arginfo.h |   2 +-
 8 files changed, 379 insertions(+), 123 deletions(-)
 create mode 100644 redis_array.stub.php
 create mode 100644 redis_array_arginfo.h
 create mode 100644 redis_array_legacy_arginfo.h

diff --git a/redis.c b/redis.c
index d8b9328732..52b5045473 100644
--- a/redis.c
+++ b/redis.c
@@ -862,7 +862,7 @@ PHP_MINIT_FUNCTION(redis)
     redis_ce->create_object = create_redis_object;
 
     /* RedisArray class */
-    INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions);
+    INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_get_methods());
     redis_array_ce = zend_register_internal_class(&redis_array_class_entry);
     redis_array_ce->create_object = create_redis_array_object;
 
diff --git a/redis_array.c b/redis_array.c
index 7e7f600798..93a2ad8922 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -37,96 +37,16 @@
 extern zend_class_entry *redis_ce;
 zend_class_entry *redis_array_ce;
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
-    ZEND_ARG_TYPE_MASK(0, name_or_hosts, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
-    ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_call, 0, 0, 2)
-    ZEND_ARG_INFO(0, function_name)
-    ZEND_ARG_INFO(0, arguments)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_target, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_instance, 0, 0, 1)
-    ZEND_ARG_INFO(0, host)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_rehash, 0, 0, 0)
-    ZEND_ARG_INFO(0, callable)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1)
-    ZEND_ARG_INFO(0, index)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1)
-    ZEND_ARG_INFO(0, keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_mset, 0, 0, 1)
-    ZEND_ARG_INFO(0, pairs)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1)
-    ZEND_ARG_INFO(0, keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_getopt, 0, 0, 1)
-    ZEND_ARG_INFO(0, opt)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_setopt, 0, 0, 2)
-    ZEND_ARG_INFO(0, opt)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1)
-    ZEND_ARG_INFO(0, pattern)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_multi, 0, 0, 1)
-    ZEND_ARG_INFO(0, host)
-    ZEND_ARG_INFO(0, mode)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0)
-    ZEND_ARG_INFO(0, async)
-ZEND_END_ARG_INFO()
-
-zend_function_entry redis_array_functions[] = {
-     PHP_ME(RedisArray, __call, arginfo_call, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, __construct, arginfo_ctor, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, _continuum, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, _distributor, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, _function, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, _hosts, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, _instance, arginfo_instance, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, _rehash, arginfo_rehash, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, _target, arginfo_target, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, bgsave, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, del, arginfo_del, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, discard, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, exec, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, flushall, arginfo_flush, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, flushdb, arginfo_flush, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, getOption, arginfo_getopt, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, info, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, keys, arginfo_keys, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, mget, arginfo_mget, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, mset, arginfo_mset, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, multi, arginfo_multi, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, ping, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, save, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, select, arginfo_select, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, setOption,arginfo_setopt, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, unlink, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(RedisArray, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_FE_END
-};
+#if PHP_VERSION_ID < 80000
+#include "redis_array_legacy_arginfo.h"
+#else
+#include "redis_array_arginfo.h"
+#endif
+
+extern const zend_function_entry *redis_array_get_methods(void)
+{
+    return class_RedisArray_methods;
+}
 
 static void
 redis_array_free(RedisArray *ra)
diff --git a/redis_array.h b/redis_array.h
index 805442aac9..1e70be33d9 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -8,36 +8,6 @@
 #endif
 #include "common.h"
 
-PHP_METHOD(RedisArray, __construct);
-PHP_METHOD(RedisArray, __call);
-PHP_METHOD(RedisArray, _hosts);
-PHP_METHOD(RedisArray, _target);
-PHP_METHOD(RedisArray, _instance);
-PHP_METHOD(RedisArray, _function);
-PHP_METHOD(RedisArray, _distributor);
-PHP_METHOD(RedisArray, _rehash);
-PHP_METHOD(RedisArray, _continuum);
-
-PHP_METHOD(RedisArray, select);
-PHP_METHOD(RedisArray, info);
-PHP_METHOD(RedisArray, ping);
-PHP_METHOD(RedisArray, flushdb);
-PHP_METHOD(RedisArray, flushall);
-PHP_METHOD(RedisArray, mget);
-PHP_METHOD(RedisArray, mset);
-PHP_METHOD(RedisArray, del);
-PHP_METHOD(RedisArray, unlink);
-PHP_METHOD(RedisArray, keys);
-PHP_METHOD(RedisArray, getOption);
-PHP_METHOD(RedisArray, setOption);
-PHP_METHOD(RedisArray, save);
-PHP_METHOD(RedisArray, bgsave);
-
-PHP_METHOD(RedisArray, multi);
-PHP_METHOD(RedisArray, exec);
-PHP_METHOD(RedisArray, discard);
-PHP_METHOD(RedisArray, unwatch);
-
 typedef struct {
     uint32_t value;
     int index;
@@ -66,6 +36,7 @@ typedef struct RedisArray_ {
     struct RedisArray_ *prev;
 } RedisArray;
 
+extern const zend_function_entry *redis_array_get_methods(void);
 zend_object *create_redis_array_object(zend_class_entry *ce);
 void free_redis_array_object(zend_object *object);
 
diff --git a/redis_array.stub.php b/redis_array.stub.php
new file mode 100644
index 0000000000..8845d33ce4
--- /dev/null
+++ b/redis_array.stub.php
@@ -0,0 +1,64 @@
+
Date: Sun, 13 Dec 2020 11:45:38 +0200
Subject: [PATCH 1474/1986] [WIP] Use stub/arginfo for RedisCluster

---
 redis.c                        |   6 +-
 redis_cluster.c                | 270 ++-------------------------------
 redis_cluster.h                | 205 +------------------------
 redis_cluster.stub.php         | 211 ++++++++++++++++++++++++++
 redis_cluster_arginfo.h        | 100 ++++++++++++
 redis_cluster_legacy_arginfo.h |  97 ++++++++++++
 6 files changed, 422 insertions(+), 467 deletions(-)
 create mode 100644 redis_cluster.stub.php
 create mode 100644 redis_cluster_arginfo.h
 create mode 100644 redis_cluster_legacy_arginfo.h

diff --git a/redis.c b/redis.c
index 52b5045473..f560b28e0b 100644
--- a/redis.c
+++ b/redis.c
@@ -61,10 +61,6 @@ zend_class_entry *redis_ce;
 zend_class_entry *redis_exception_ce;
 
 extern int le_cluster_slot_cache;
-
-extern zend_function_entry redis_array_functions[];
-extern zend_function_entry redis_cluster_functions[];
-
 int le_redis_pconnect;
 
 PHP_INI_BEGIN()
@@ -867,7 +863,7 @@ PHP_MINIT_FUNCTION(redis)
     redis_array_ce->create_object = create_redis_array_object;
 
     /* RedisCluster class */
-    INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_functions);
+    INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_get_methods());
     redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry);
     redis_cluster_ce->create_object = create_cluster_context;
 
diff --git a/redis_cluster.c b/redis_cluster.c
index 8c4519941b..140f51bc89 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -35,268 +35,20 @@ zend_class_entry *redis_cluster_ce;
 /* Exception handler */
 zend_class_entry *redis_cluster_exception_ce;
 
+#if PHP_VERSION_ID < 80000
+#include "redis_cluster_legacy_arginfo.h"
+#else
+#include "redis_cluster_arginfo.h"
+#endif
+
+extern const zend_function_entry *redis_cluster_get_methods(void)
+{
+    return class_RedisCluster_methods;
+}
+
 /* Handlers for RedisCluster */
 zend_object_handlers RedisCluster_handlers;
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
-    ZEND_ARG_INFO(0, name)
-    ZEND_ARG_ARRAY_INFO(0, seeds, 0)
-    ZEND_ARG_INFO(0, timeout)
-    ZEND_ARG_INFO(0, read_timeout)
-    ZEND_ARG_INFO(0, persistent)
-    ZEND_ARG_INFO(0, auth)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_VARIADIC_INFO(0, other_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1)
-    ZEND_ARG_ARRAY_INFO(0, keys, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1)
-    ZEND_ARG_INFO(0, pattern)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_or_address, 0, 0, 1)
-    ZEND_ARG_INFO(0, key_or_address)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_or_address_variadic, 0, 0, 1)
-    ZEND_ARG_INFO(0, key_or_address)
-    ZEND_ARG_INFO(0, arg)
-    ZEND_ARG_VARIADIC_INFO(0, other_args)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 1)
-    ZEND_ARG_INFO(0, key_or_address)
-    ZEND_ARG_INFO(0, option)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 1)
-    ZEND_ARG_INFO(0, key_or_address)
-    ZEND_ARG_INFO(0, async)
-ZEND_END_ARG_INFO()
-
-/* Argument info for HSCAN, SSCAN, HSCAN */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(1, i_iterator)
-    ZEND_ARG_INFO(0, str_pattern)
-    ZEND_ARG_INFO(0, i_count)
-ZEND_END_ARG_INFO()
-
-/* Argument info for SCAN */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_scan_cl, 0, 0, 2)
-    ZEND_ARG_INFO(1, i_iterator)
-    ZEND_ARG_INFO(0, str_node)
-    ZEND_ARG_INFO(0, str_pattern)
-    ZEND_ARG_INFO(0, i_count)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_acl_cl, 0, 0, 2)
-    ZEND_ARG_INFO(0, key_or_address)
-    ZEND_ARG_INFO(0, subcmd)
-    ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-/* Function table */
-zend_function_entry redis_cluster_functions[] = {
-    PHP_ME(RedisCluster, __construct, arginfo_ctor, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _masters, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _prefix, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _redir, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _compress, arginfo_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _uncompress, arginfo_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _pack, arginfo_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, _unpack, arginfo_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, acl, arginfo_acl_cl, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, bgrewriteaof, arginfo_key_or_address, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, bgsave, arginfo_key_or_address, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, bitcount, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, bitop, arginfo_bitop, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, blpop, arginfo_blrpop, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, brpop, arginfo_blrpop, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, clearlasterror, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, bzpopmax, arginfo_blrpop, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, bzpopmin, arginfo_blrpop, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, client, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, close, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, cluster, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, command, arginfo_command, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, config, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, dbsize, arginfo_key_or_address, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, decr, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, decrby, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, del, arginfo_del, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, discard, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, dump, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, echo, arginfo_echo, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, eval, arginfo_eval, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, exec, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, exists, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, expire, arginfo_expire, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, expireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, flushall, arginfo_flush, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, flushdb, arginfo_flush, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, geodist, arginfo_geodist, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, geohash, arginfo_key_members, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, geopos, arginfo_key_members, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, georadius, arginfo_georadius, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, georadius_ro, arginfo_georadius, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, georadiusbymember_ro, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, get, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, getbit, arginfo_key_offset, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, getlasterror, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, getmode, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, getoption, arginfo_getoption, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, getrange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, getset, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hdel, arginfo_key_members, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hexists, arginfo_key_member, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hget, arginfo_key_member, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hgetall, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hincrby, arginfo_key_member_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hincrbyfloat, arginfo_key_member_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hkeys, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hlen, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hmget, arginfo_hmget, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hmset, arginfo_hmset, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hset, arginfo_key_member_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hsetnx, arginfo_key_member_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hstrlen, arginfo_key_member, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, hvals, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, incr, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, incrby, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, incrbyfloat, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, info, arginfo_info, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, keys, arginfo_keys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lastsave, arginfo_key_or_address, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lget, arginfo_lindex, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lindex, arginfo_lindex, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, linsert, arginfo_linsert, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, llen, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lpop, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lpush, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lpushx, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lrem, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, lset, arginfo_lset, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, mget, arginfo_mget, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, mset, arginfo_pairs, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, multi, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, object, arginfo_object, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, persist, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, pexpireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, pfcount, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, ping, arginfo_key_or_address, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, pttl, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, publish, arginfo_publish, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, pubsub, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, randomkey, arginfo_key_or_address, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, renamenx, arginfo_key_newkey, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, restore, arginfo_restore, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, role, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, rpop, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, rpush, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, rpushx, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sadd, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, saddarray, arginfo_sadd_array, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, save, arginfo_key_or_address, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, scan, arginfo_scan_cl, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, scard, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, script, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sdiff, arginfo_nkeys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sdiffstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, set, arginfo_set, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, setbit, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, setnx, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, setoption, arginfo_setoption, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, setrange, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sinter, arginfo_nkeys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sinterstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sismember, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, slowlog, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, smembers, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, smove, arginfo_smove, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sort, arginfo_sort, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, spop, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, srandmember, arginfo_srand_member, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, srem, arginfo_key_value, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, strlen, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sunion, arginfo_nkeys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, sunionstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, time, arginfo_key_or_address, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, unlink, arginfo_del, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xack, arginfo_xack, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xadd, arginfo_xadd, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xclaim, arginfo_xclaim, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xdel, arginfo_xdel, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xgroup, arginfo_xgroup, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xinfo, arginfo_xinfo, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xlen, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xpending, arginfo_xpending, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xrange, arginfo_xrange, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xread, arginfo_xread, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xreadgroup, arginfo_xreadgroup, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xrevrange, arginfo_xrange, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, xtrim, arginfo_xtrim, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zcard, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zcount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zincrby, arginfo_zincrby, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zlexcount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zpopmax, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zpopmin, arginfo_key, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrange, arginfo_zrange, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrank, arginfo_key_member, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrem, arginfo_key_members, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zremrangebylex, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zremrangebyrank, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zremrangebyscore, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrevrange, arginfo_zrange, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrevrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrevrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zrevrank, arginfo_key_member, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zscore, arginfo_key_member, ZEND_ACC_PUBLIC)
-    PHP_ME(RedisCluster, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC)
-    PHP_FE_END
-};
-
 /* Our context seeds will be a hash table with RedisSock* pointers */
 static void ht_free_seed(zval *data) {
     RedisSock *redis_sock = *(RedisSock**)data;
diff --git a/redis_cluster.h b/redis_cluster.h
index 41f40c1af7..d8e62e7f65 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -91,213 +91,12 @@
     } \
     resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx);
 
+extern const zend_function_entry *redis_cluster_get_methods(void);
+
 /* Create cluster context */
 zend_object *create_cluster_context(zend_class_entry *class_type);
 
 /* Free cluster context struct */
 void free_cluster_context(zend_object *object);
 
-/* RedisCluster method implementation */
-PHP_METHOD(RedisCluster, __construct);
-PHP_METHOD(RedisCluster, acl);
-PHP_METHOD(RedisCluster, close);
-PHP_METHOD(RedisCluster, get);
-PHP_METHOD(RedisCluster, set);
-PHP_METHOD(RedisCluster, mget);
-PHP_METHOD(RedisCluster, mset);
-PHP_METHOD(RedisCluster, msetnx);
-PHP_METHOD(RedisCluster, mset);
-PHP_METHOD(RedisCluster, del);
-PHP_METHOD(RedisCluster, unlink);
-PHP_METHOD(RedisCluster, dump);
-PHP_METHOD(RedisCluster, setex);
-PHP_METHOD(RedisCluster, psetex);
-PHP_METHOD(RedisCluster, setnx);
-PHP_METHOD(RedisCluster, getset);
-PHP_METHOD(RedisCluster, exists);
-PHP_METHOD(RedisCluster, keys);
-PHP_METHOD(RedisCluster, type);
-PHP_METHOD(RedisCluster, persist);
-PHP_METHOD(RedisCluster, lpop);
-PHP_METHOD(RedisCluster, rpop);
-PHP_METHOD(RedisCluster, spop);
-PHP_METHOD(RedisCluster, rpush);
-PHP_METHOD(RedisCluster, lpush);
-PHP_METHOD(RedisCluster, blpop);
-PHP_METHOD(RedisCluster, brpop);
-PHP_METHOD(RedisCluster, rpushx);
-PHP_METHOD(RedisCluster, lpushx);
-PHP_METHOD(RedisCluster, linsert);
-PHP_METHOD(RedisCluster, lindex);
-PHP_METHOD(RedisCluster, lrem);
-PHP_METHOD(RedisCluster, brpoplpush);
-PHP_METHOD(RedisCluster, rpoplpush);
-PHP_METHOD(RedisCluster, llen);
-PHP_METHOD(RedisCluster, scard);
-PHP_METHOD(RedisCluster, smembers);
-PHP_METHOD(RedisCluster, sismember);
-PHP_METHOD(RedisCluster, sadd);
-PHP_METHOD(RedisCluster, saddarray);
-PHP_METHOD(RedisCluster, srem);
-PHP_METHOD(RedisCluster, sunion);
-PHP_METHOD(RedisCluster, sunionstore);
-PHP_METHOD(RedisCluster, sinter);
-PHP_METHOD(RedisCluster, sinterstore);
-PHP_METHOD(RedisCluster, sdiff);
-PHP_METHOD(RedisCluster, sdiffstore);
-PHP_METHOD(RedisCluster, strlen);
-PHP_METHOD(RedisCluster, ttl);
-PHP_METHOD(RedisCluster, pttl);
-PHP_METHOD(RedisCluster, zcard);
-PHP_METHOD(RedisCluster, zscore);
-PHP_METHOD(RedisCluster, zcount);
-PHP_METHOD(RedisCluster, zrem);
-PHP_METHOD(RedisCluster, zremrangebyscore);
-PHP_METHOD(RedisCluster, zrank);
-PHP_METHOD(RedisCluster, zrevrank);
-PHP_METHOD(RedisCluster, zadd);
-PHP_METHOD(RedisCluster, zincrby);
-PHP_METHOD(RedisCluster, hlen);
-PHP_METHOD(RedisCluster, hget);
-PHP_METHOD(RedisCluster, hkeys);
-PHP_METHOD(RedisCluster, hvals);
-PHP_METHOD(RedisCluster, hmget);
-PHP_METHOD(RedisCluster, hmset);
-PHP_METHOD(RedisCluster, hdel);
-PHP_METHOD(RedisCluster, hgetall);
-PHP_METHOD(RedisCluster, hexists);
-PHP_METHOD(RedisCluster, hincrby);
-PHP_METHOD(RedisCluster, hincrbyfloat);
-PHP_METHOD(RedisCluster, hset);
-PHP_METHOD(RedisCluster, hsetnx);
-PHP_METHOD(RedisCluster, hstrlen);
-PHP_METHOD(RedisCluster, incr);
-PHP_METHOD(RedisCluster, decr);
-PHP_METHOD(RedisCluster, incrby);
-PHP_METHOD(RedisCluster, decrby);
-PHP_METHOD(RedisCluster, incrbyfloat);
-PHP_METHOD(RedisCluster, expire);
-PHP_METHOD(RedisCluster, expireat);
-PHP_METHOD(RedisCluster, pexpire);
-PHP_METHOD(RedisCluster, pexpireat);
-PHP_METHOD(RedisCluster, append);
-PHP_METHOD(RedisCluster, getbit);
-PHP_METHOD(RedisCluster, setbit);
-PHP_METHOD(RedisCluster, bitop);
-PHP_METHOD(RedisCluster, bitpos);
-PHP_METHOD(RedisCluster, bitcount);
-PHP_METHOD(RedisCluster, lget);
-PHP_METHOD(RedisCluster, getrange);
-PHP_METHOD(RedisCluster, ltrim);
-PHP_METHOD(RedisCluster, lrange);
-PHP_METHOD(RedisCluster, zremrangebyrank);
-PHP_METHOD(RedisCluster, publish);
-PHP_METHOD(RedisCluster, lset);
-PHP_METHOD(RedisCluster, rename);
-PHP_METHOD(RedisCluster, renamenx);
-PHP_METHOD(RedisCluster, pfcount);
-PHP_METHOD(RedisCluster, pfadd);
-PHP_METHOD(RedisCluster, pfmerge);
-PHP_METHOD(RedisCluster, restore);
-PHP_METHOD(RedisCluster, setrange);
-PHP_METHOD(RedisCluster, smove);
-PHP_METHOD(RedisCluster, srandmember);
-PHP_METHOD(RedisCluster, zpopmin);
-PHP_METHOD(RedisCluster, zpopmax);
-PHP_METHOD(RedisCluster, bzpopmax);
-PHP_METHOD(RedisCluster, bzpopmin);
-PHP_METHOD(RedisCluster, zrange);
-PHP_METHOD(RedisCluster, zrevrange);
-PHP_METHOD(RedisCluster, zrangebyscore);
-PHP_METHOD(RedisCluster, zrevrangebyscore);
-PHP_METHOD(RedisCluster, zrangebylex);
-PHP_METHOD(RedisCluster, zrevrangebylex);
-PHP_METHOD(RedisCluster, zlexcount);
-PHP_METHOD(RedisCluster, zremrangebylex);
-PHP_METHOD(RedisCluster, zunionstore);
-PHP_METHOD(RedisCluster, zinterstore);
-PHP_METHOD(RedisCluster, sort);
-PHP_METHOD(RedisCluster, object);
-PHP_METHOD(RedisCluster, subscribe);
-PHP_METHOD(RedisCluster, psubscribe);
-PHP_METHOD(RedisCluster, unsubscribe);
-PHP_METHOD(RedisCluster, punsubscribe);
-PHP_METHOD(RedisCluster, eval);
-PHP_METHOD(RedisCluster, evalsha);
-PHP_METHOD(RedisCluster, info);
-PHP_METHOD(RedisCluster, cluster);
-PHP_METHOD(RedisCluster, client);
-PHP_METHOD(RedisCluster, config);
-PHP_METHOD(RedisCluster, pubsub);
-PHP_METHOD(RedisCluster, script);
-PHP_METHOD(RedisCluster, slowlog);
-PHP_METHOD(RedisCluster, command);
-PHP_METHOD(RedisCluster, geoadd);
-PHP_METHOD(RedisCluster, geohash);
-PHP_METHOD(RedisCluster, geopos);
-PHP_METHOD(RedisCluster, geodist);
-PHP_METHOD(RedisCluster, georadius);
-PHP_METHOD(RedisCluster, georadius_ro);
-PHP_METHOD(RedisCluster, georadiusbymember);
-PHP_METHOD(RedisCluster, georadiusbymember_ro);
-
-/* SCAN and friends */
-PHP_METHOD(RedisCluster, scan);
-PHP_METHOD(RedisCluster, zscan);
-PHP_METHOD(RedisCluster, hscan);
-PHP_METHOD(RedisCluster, sscan);
-
-/* STREAMS */
-PHP_METHOD(RedisCluster, xack);
-PHP_METHOD(RedisCluster, xadd);
-PHP_METHOD(RedisCluster, xclaim);
-PHP_METHOD(RedisCluster, xdel);
-PHP_METHOD(RedisCluster, xgroup);
-PHP_METHOD(RedisCluster, xinfo);
-PHP_METHOD(RedisCluster, xlen);
-PHP_METHOD(RedisCluster, xpending);
-PHP_METHOD(RedisCluster, xrange);
-PHP_METHOD(RedisCluster, xread);
-PHP_METHOD(RedisCluster, xreadgroup);
-PHP_METHOD(RedisCluster, xrevrange);
-PHP_METHOD(RedisCluster, xtrim);
-
-/* Transactions */
-PHP_METHOD(RedisCluster, multi);
-PHP_METHOD(RedisCluster, exec);
-PHP_METHOD(RedisCluster, discard);
-PHP_METHOD(RedisCluster, watch);
-PHP_METHOD(RedisCluster, unwatch);
-
-/* Commands we direct to a node */
-PHP_METHOD(RedisCluster, save);
-PHP_METHOD(RedisCluster, bgsave);
-PHP_METHOD(RedisCluster, flushdb);
-PHP_METHOD(RedisCluster, flushall);
-PHP_METHOD(RedisCluster, dbsize);
-PHP_METHOD(RedisCluster, bgrewriteaof);
-PHP_METHOD(RedisCluster, lastsave);
-PHP_METHOD(RedisCluster, role);
-PHP_METHOD(RedisCluster, time);
-PHP_METHOD(RedisCluster, randomkey);
-PHP_METHOD(RedisCluster, ping);
-PHP_METHOD(RedisCluster, echo);
-PHP_METHOD(RedisCluster, rawcommand);
-
-/* Introspection */
-PHP_METHOD(RedisCluster, getmode);
-PHP_METHOD(RedisCluster, getlasterror);
-PHP_METHOD(RedisCluster, clearlasterror);
-PHP_METHOD(RedisCluster, getoption);
-PHP_METHOD(RedisCluster, setoption);
-PHP_METHOD(RedisCluster, _prefix);
-PHP_METHOD(RedisCluster, _serialize);
-PHP_METHOD(RedisCluster, _unserialize);
-PHP_METHOD(RedisCluster, _compress);
-PHP_METHOD(RedisCluster, _uncompress);
-PHP_METHOD(RedisCluster, _pack);
-PHP_METHOD(RedisCluster, _unpack);
-PHP_METHOD(RedisCluster, _masters);
-PHP_METHOD(RedisCluster, _redir);
-
 #endif
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
new file mode 100644
index 0000000000..f2c76bc0b2
--- /dev/null
+++ b/redis_cluster.stub.php
@@ -0,0 +1,211 @@
+
Date: Sat, 6 Feb 2021 19:26:19 +0200
Subject: [PATCH 1475/1986] Add stub-based arginfo for Redis Cluster methods
 b-d

---
 redis_cluster.stub.php         |  51 +++++++++++-----
 redis_cluster_arginfo.h        | 105 ++++++++++++++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h |  95 ++++++++++++++++++++++++++++-
 3 files changed, 233 insertions(+), 18 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index f2c76bc0b2..3c33af3d62 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -32,26 +32,45 @@ public function bitcount(string $key, int $start = 0, int $end = -1): bool|int;
     public function bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys): bool|int;
 
     public function bitpos(string $key, int $bit, int $start = NULL, int $end = NULL): bool|int;
+
+    public function blpop(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+
+    public function brpop(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+
+    public function brpoplpush(string $srckey, string $deskey, int $timeout): mixed;
+
+    public function bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+
+    public function bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+
+    public function clearlasterror(): bool;
+
+    public function client(string|array $node, string $subcommand, string|null $arg): array|string|bool;
+
+    public function close(): bool;
+
+    public function cluster(string|array $node, string $command, mixed ...$extra_args): mixed;
+
+    public function command(mixed ...$extra_args): mixed;
+
+    public function config(string|array $node, string $subcommand, mixed ...$extra_args): mixed;
+
+    public function dbsize(string|array $key_or_address): int;
+
+    public function decr(string $key): int;
+
+    public function decrby(string $key, int $value): int;
+
+    public function del(string $key, string ...$other_keys): array;
+
+    public function discard(): bool;
+
+    public function dump(string $key): string;
+
 }
 
 /*
     TODO:
-    public function brpop
-    public function brpoplpush
-    public function clearlasterror
-    public function bzpopmax
-    public function bzpopmin
-    public function client
-    public function close
-    public function cluster
-    public function command
-    public function config
-    public function dbsize
-    public function decr
-    public function decrby
-    public function del
-    public function discard
-    public function dump
     public function echo
     public function eval
     public function evalsha
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 97acaf536a..932694347a 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e75f14ee54edbf3d7460402a4f445aa57b6c1d1d */
+ * Stub hash: b00398a68b9846d7266b8232d11de787fc4bae0c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -66,6 +66,75 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_bitpos, 0, 2,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "NULL")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_blpop, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_brpop arginfo_class_RedisCluster_blpop
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_brpoplpush, 0, 3, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, deskey, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_bzpopmax arginfo_class_RedisCluster_blpop
+
+#define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_blpop
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_clearlasterror, 0, 0, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_client, 0, 3, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, arg, IS_STRING, 1)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_close arginfo_class_RedisCluster_clearlasterror
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_cluster, 0, 2, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_command, 0, 0, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_config, 0, 2, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_dbsize, 0, 1, IS_LONG, 0)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_decr, 0, 1, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_decrby, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_del, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_discard arginfo_class_RedisCluster_clearlasterror
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_dump, 0, 1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -80,6 +149,23 @@ ZEND_METHOD(RedisCluster, bgsave);
 ZEND_METHOD(RedisCluster, bitcount);
 ZEND_METHOD(RedisCluster, bitop);
 ZEND_METHOD(RedisCluster, bitpos);
+ZEND_METHOD(RedisCluster, blpop);
+ZEND_METHOD(RedisCluster, brpop);
+ZEND_METHOD(RedisCluster, brpoplpush);
+ZEND_METHOD(RedisCluster, bzpopmax);
+ZEND_METHOD(RedisCluster, bzpopmin);
+ZEND_METHOD(RedisCluster, clearlasterror);
+ZEND_METHOD(RedisCluster, client);
+ZEND_METHOD(RedisCluster, close);
+ZEND_METHOD(RedisCluster, cluster);
+ZEND_METHOD(RedisCluster, command);
+ZEND_METHOD(RedisCluster, config);
+ZEND_METHOD(RedisCluster, dbsize);
+ZEND_METHOD(RedisCluster, decr);
+ZEND_METHOD(RedisCluster, decrby);
+ZEND_METHOD(RedisCluster, del);
+ZEND_METHOD(RedisCluster, discard);
+ZEND_METHOD(RedisCluster, dump);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -96,5 +182,22 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, bitcount, arginfo_class_RedisCluster_bitcount, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bitop, arginfo_class_RedisCluster_bitop, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bitpos, arginfo_class_RedisCluster_bitpos, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, blpop, arginfo_class_RedisCluster_blpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, brpop, arginfo_class_RedisCluster_brpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, brpoplpush, arginfo_class_RedisCluster_brpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, bzpopmax, arginfo_class_RedisCluster_bzpopmax, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, bzpopmin, arginfo_class_RedisCluster_bzpopmin, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, clearlasterror, arginfo_class_RedisCluster_clearlasterror, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, client, arginfo_class_RedisCluster_client, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, close, arginfo_class_RedisCluster_close, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, cluster, arginfo_class_RedisCluster_cluster, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, command, arginfo_class_RedisCluster_command, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, config, arginfo_class_RedisCluster_config, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index d4f153f206..0efe837081 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e75f14ee54edbf3d7460402a4f445aa57b6c1d1d */
+ * Stub hash: b00398a68b9846d7266b8232d11de787fc4bae0c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -63,6 +63,65 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitpos, 0, 0, 2)
 	ZEND_ARG_INFO(0, end)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_blpop, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timeout_or_key)
+	ZEND_ARG_VARIADIC_INFO(0, extra_args)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_brpop arginfo_class_RedisCluster_blpop
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_brpoplpush, 0, 0, 3)
+	ZEND_ARG_INFO(0, srckey)
+	ZEND_ARG_INFO(0, deskey)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_bzpopmax arginfo_class_RedisCluster_blpop
+
+#define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_blpop
+
+#define arginfo_class_RedisCluster_clearlasterror arginfo_class_RedisCluster__masters
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_client, 0, 0, 3)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, subcommand)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_close arginfo_class_RedisCluster__masters
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_cluster, 0, 0, 2)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, command)
+	ZEND_ARG_VARIADIC_INFO(0, extra_args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_command, 0, 0, 0)
+	ZEND_ARG_VARIADIC_INFO(0, extra_args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_config, 0, 0, 2)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, subcommand)
+	ZEND_ARG_VARIADIC_INFO(0, extra_args)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_dbsize arginfo_class_RedisCluster_bgrewriteaof
+
+#define arginfo_class_RedisCluster_decr arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_decrby arginfo_class_RedisCluster_append
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_del, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_VARIADIC_INFO(0, other_keys)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_discard arginfo_class_RedisCluster__masters
+
+#define arginfo_class_RedisCluster_dump arginfo_class_RedisCluster__prefix
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -77,6 +136,23 @@ ZEND_METHOD(RedisCluster, bgsave);
 ZEND_METHOD(RedisCluster, bitcount);
 ZEND_METHOD(RedisCluster, bitop);
 ZEND_METHOD(RedisCluster, bitpos);
+ZEND_METHOD(RedisCluster, blpop);
+ZEND_METHOD(RedisCluster, brpop);
+ZEND_METHOD(RedisCluster, brpoplpush);
+ZEND_METHOD(RedisCluster, bzpopmax);
+ZEND_METHOD(RedisCluster, bzpopmin);
+ZEND_METHOD(RedisCluster, clearlasterror);
+ZEND_METHOD(RedisCluster, client);
+ZEND_METHOD(RedisCluster, close);
+ZEND_METHOD(RedisCluster, cluster);
+ZEND_METHOD(RedisCluster, command);
+ZEND_METHOD(RedisCluster, config);
+ZEND_METHOD(RedisCluster, dbsize);
+ZEND_METHOD(RedisCluster, decr);
+ZEND_METHOD(RedisCluster, decrby);
+ZEND_METHOD(RedisCluster, del);
+ZEND_METHOD(RedisCluster, discard);
+ZEND_METHOD(RedisCluster, dump);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -93,5 +169,22 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, bitcount, arginfo_class_RedisCluster_bitcount, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bitop, arginfo_class_RedisCluster_bitop, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bitpos, arginfo_class_RedisCluster_bitpos, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, blpop, arginfo_class_RedisCluster_blpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, brpop, arginfo_class_RedisCluster_brpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, brpoplpush, arginfo_class_RedisCluster_brpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, bzpopmax, arginfo_class_RedisCluster_bzpopmax, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, bzpopmin, arginfo_class_RedisCluster_bzpopmin, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, clearlasterror, arginfo_class_RedisCluster_clearlasterror, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, client, arginfo_class_RedisCluster_client, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, close, arginfo_class_RedisCluster_close, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, cluster, arginfo_class_RedisCluster_cluster, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, command, arginfo_class_RedisCluster_command, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, config, arginfo_class_RedisCluster_config, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From e1458a299697d07e4ce4a828016d2920784b5106 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Viktor=20Djupsj=C3=B6backa?=
 
Date: Sat, 13 Feb 2021 22:33:50 +0200
Subject: [PATCH 1476/1986] Add stub-based arginfo for Redis Cluster methods
 e-g

---
 redis_cluster.stub.php         |  72 ++++++++++-----
 redis_cluster_arginfo.h        | 156 ++++++++++++++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h | 152 +++++++++++++++++++++++++++++++-
 3 files changed, 354 insertions(+), 26 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 3c33af3d62..43573983de 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -67,34 +67,58 @@ public function discard(): bool;
 
     public function dump(string $key): string;
 
+    public function echo(string|array $node, string $msg): string;
+
+    public function eval(string $script, array $args = [], int $num_keys = 0): mixed;
+
+    public function evalsha(string $script_sha, array $args = [], int $num_keys = 0): mixed;
+
+    public function exec(): array;
+
+    public function exists(string $key): int;
+
+    public function expire(string $key, int $timeout): bool;
+
+    public function expireat(string $key, int $timestamp): bool;
+
+    public function flushall(string|array $node, bool $async = false): bool;
+
+    public function flushdb(string|array $node, bool $async = false): bool;
+
+    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): int;
+
+    public function geodist(string $key, string $src, string $dest, ?string $unit = null): array;
+
+    public function geohash(string $key, string $member, string ...$other_members): array;
+
+    public function geopos(string $key, string $member, string ...$other_members): array;
+
+    public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): array;
+
+    public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): array;
+
+    public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): array;
+
+    public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): array;
+
+    public function get(string $key): string;
+
+    public function getbit(string $key, int $value): int;
+
+    public function getlasterror(): string|null;
+
+    public function getmode(): int;
+
+    public function getoption(int $option): mixed;
+
+    public function getrange(string $key, int $start, int $end): string;
+
+    public function getset(string $key, mixed $value): string;
+
 }
 
 /*
     TODO:
-    public function echo
-    public function eval
-    public function evalsha
-    public function exec
-    public function exists
-    public function expire
-    public function expireat
-    public function flushall
-    public function flushdb
-    public function geoadd
-    public function geodist
-    public function geohash
-    public function geopos
-    public function georadius
-    public function georadius_ro
-    public function georadiusbymember
-    public function georadiusbymember_ro
-    public function get
-    public function getbit
-    public function getlasterror
-    public function getmode
-    public function getoption
-    public function getrange
-    public function getset
     public function hdel
     public function hexists
     public function hget
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 932694347a..1a22b993ce 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b00398a68b9846d7266b8232d11de787fc4bae0c */
+ * Stub hash: 5b130a06b4290b7ebec9b20d3973f726cc3fe7af */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -135,6 +135,112 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_dump, 0, 1, I
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_echo, 0, 2, IS_STRING, 0)
+	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO(0, msg, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_eval, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, script, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_evalsha, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, script_sha, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_exec arginfo_class_RedisCluster__masters
+
+#define arginfo_class_RedisCluster_exists arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_expire, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_expireat, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_flushall, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, async, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 4, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_geodist, 0, 3, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dest, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_geohash, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_geopos arginfo_class_RedisCluster_geohash
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_georadius, 0, 5, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_georadius_ro arginfo_class_RedisCluster_georadius
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_georadiusbymember, 0, 4, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_georadiusbymember_ro arginfo_class_RedisCluster_georadiusbymember
+
+#define arginfo_class_RedisCluster_get arginfo_class_RedisCluster_dump
+
+#define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_decrby
+
+#define arginfo_class_RedisCluster_getlasterror arginfo_class_RedisCluster__redir
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getmode, 0, 0, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getoption, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getrange, 0, 3, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getset, 0, 2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -166,6 +272,30 @@ ZEND_METHOD(RedisCluster, decrby);
 ZEND_METHOD(RedisCluster, del);
 ZEND_METHOD(RedisCluster, discard);
 ZEND_METHOD(RedisCluster, dump);
+ZEND_METHOD(RedisCluster, echo);
+ZEND_METHOD(RedisCluster, eval);
+ZEND_METHOD(RedisCluster, evalsha);
+ZEND_METHOD(RedisCluster, exec);
+ZEND_METHOD(RedisCluster, exists);
+ZEND_METHOD(RedisCluster, expire);
+ZEND_METHOD(RedisCluster, expireat);
+ZEND_METHOD(RedisCluster, flushall);
+ZEND_METHOD(RedisCluster, flushdb);
+ZEND_METHOD(RedisCluster, geoadd);
+ZEND_METHOD(RedisCluster, geodist);
+ZEND_METHOD(RedisCluster, geohash);
+ZEND_METHOD(RedisCluster, geopos);
+ZEND_METHOD(RedisCluster, georadius);
+ZEND_METHOD(RedisCluster, georadius_ro);
+ZEND_METHOD(RedisCluster, georadiusbymember);
+ZEND_METHOD(RedisCluster, georadiusbymember_ro);
+ZEND_METHOD(RedisCluster, get);
+ZEND_METHOD(RedisCluster, getbit);
+ZEND_METHOD(RedisCluster, getlasterror);
+ZEND_METHOD(RedisCluster, getmode);
+ZEND_METHOD(RedisCluster, getoption);
+ZEND_METHOD(RedisCluster, getrange);
+ZEND_METHOD(RedisCluster, getset);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -199,5 +329,29 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, echo, arginfo_class_RedisCluster_echo, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, eval, arginfo_class_RedisCluster_eval, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, evalsha, arginfo_class_RedisCluster_evalsha, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, exec, arginfo_class_RedisCluster_exec, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, expireat, arginfo_class_RedisCluster_expireat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, flushall, arginfo_class_RedisCluster_flushall, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, flushdb, arginfo_class_RedisCluster_flushdb, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, geoadd, arginfo_class_RedisCluster_geoadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, geodist, arginfo_class_RedisCluster_geodist, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, geohash, arginfo_class_RedisCluster_geohash, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, geopos, arginfo_class_RedisCluster_geopos, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, georadius, arginfo_class_RedisCluster_georadius, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, georadius_ro, arginfo_class_RedisCluster_georadius_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, georadiusbymember, arginfo_class_RedisCluster_georadiusbymember, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, georadiusbymember_ro, arginfo_class_RedisCluster_georadiusbymember_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 0efe837081..fe78be8fd4 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b00398a68b9846d7266b8232d11de787fc4bae0c */
+ * Stub hash: 5b130a06b4290b7ebec9b20d3973f726cc3fe7af */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -122,6 +122,108 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_dump arginfo_class_RedisCluster__prefix
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_echo, 0, 0, 2)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, msg)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_eval, 0, 0, 1)
+	ZEND_ARG_INFO(0, script)
+	ZEND_ARG_INFO(0, args)
+	ZEND_ARG_INFO(0, num_keys)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_evalsha, 0, 0, 1)
+	ZEND_ARG_INFO(0, script_sha)
+	ZEND_ARG_INFO(0, args)
+	ZEND_ARG_INFO(0, num_keys)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_exec arginfo_class_RedisCluster__masters
+
+#define arginfo_class_RedisCluster_exists arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expire, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expireat, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timestamp)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_flushall, 0, 0, 1)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, async)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 0, 4)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, lng)
+	ZEND_ARG_INFO(0, lat)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_VARIADIC_INFO(0, other_triples)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geodist, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dest)
+	ZEND_ARG_INFO(0, unit)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geohash, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_VARIADIC_INFO(0, other_members)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_geopos arginfo_class_RedisCluster_geohash
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_georadius, 0, 0, 5)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, lng)
+	ZEND_ARG_INFO(0, lat)
+	ZEND_ARG_INFO(0, radius)
+	ZEND_ARG_INFO(0, unit)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_georadius_ro arginfo_class_RedisCluster_georadius
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_georadiusbymember, 0, 0, 4)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_INFO(0, radius)
+	ZEND_ARG_INFO(0, unit)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_georadiusbymember_ro arginfo_class_RedisCluster_georadiusbymember
+
+#define arginfo_class_RedisCluster_get arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_append
+
+#define arginfo_class_RedisCluster_getlasterror arginfo_class_RedisCluster__masters
+
+#define arginfo_class_RedisCluster_getmode arginfo_class_RedisCluster__masters
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getoption, 0, 0, 1)
+	ZEND_ARG_INFO(0, option)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getrange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_getset arginfo_class_RedisCluster_append
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -153,6 +255,30 @@ ZEND_METHOD(RedisCluster, decrby);
 ZEND_METHOD(RedisCluster, del);
 ZEND_METHOD(RedisCluster, discard);
 ZEND_METHOD(RedisCluster, dump);
+ZEND_METHOD(RedisCluster, echo);
+ZEND_METHOD(RedisCluster, eval);
+ZEND_METHOD(RedisCluster, evalsha);
+ZEND_METHOD(RedisCluster, exec);
+ZEND_METHOD(RedisCluster, exists);
+ZEND_METHOD(RedisCluster, expire);
+ZEND_METHOD(RedisCluster, expireat);
+ZEND_METHOD(RedisCluster, flushall);
+ZEND_METHOD(RedisCluster, flushdb);
+ZEND_METHOD(RedisCluster, geoadd);
+ZEND_METHOD(RedisCluster, geodist);
+ZEND_METHOD(RedisCluster, geohash);
+ZEND_METHOD(RedisCluster, geopos);
+ZEND_METHOD(RedisCluster, georadius);
+ZEND_METHOD(RedisCluster, georadius_ro);
+ZEND_METHOD(RedisCluster, georadiusbymember);
+ZEND_METHOD(RedisCluster, georadiusbymember_ro);
+ZEND_METHOD(RedisCluster, get);
+ZEND_METHOD(RedisCluster, getbit);
+ZEND_METHOD(RedisCluster, getlasterror);
+ZEND_METHOD(RedisCluster, getmode);
+ZEND_METHOD(RedisCluster, getoption);
+ZEND_METHOD(RedisCluster, getrange);
+ZEND_METHOD(RedisCluster, getset);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -186,5 +312,29 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, echo, arginfo_class_RedisCluster_echo, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, eval, arginfo_class_RedisCluster_eval, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, evalsha, arginfo_class_RedisCluster_evalsha, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, exec, arginfo_class_RedisCluster_exec, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, expireat, arginfo_class_RedisCluster_expireat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, flushall, arginfo_class_RedisCluster_flushall, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, flushdb, arginfo_class_RedisCluster_flushdb, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, geoadd, arginfo_class_RedisCluster_geoadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, geodist, arginfo_class_RedisCluster_geodist, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, geohash, arginfo_class_RedisCluster_geohash, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, geopos, arginfo_class_RedisCluster_geopos, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, georadius, arginfo_class_RedisCluster_georadius, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, georadius_ro, arginfo_class_RedisCluster_georadius_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, georadiusbymember, arginfo_class_RedisCluster_georadiusbymember, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, georadiusbymember_ro, arginfo_class_RedisCluster_georadiusbymember_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From cdfa0fde5dfe8835a97a68f28421a3afa73b076e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Viktor=20Djupsj=C3=B6backa?=
 
Date: Sat, 13 Mar 2021 13:54:57 +0200
Subject: [PATCH 1477/1986] Add stub-based arginfo for Redis Cluster methods
 h-k

---
 redis_cluster.stub.php         |  62 ++++++++++-----
 redis_cluster_arginfo.h        | 136 ++++++++++++++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h | 112 ++++++++++++++++++++++++++-
 3 files changed, 288 insertions(+), 22 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 43573983de..43dfb0a4c0 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -61,6 +61,8 @@ public function decr(string $key): int;
 
     public function decrby(string $key, int $value): int;
 
+    public function decrbyfloat(string $key, float $value): float;
+
     public function del(string $key, string ...$other_keys): array;
 
     public function discard(): bool;
@@ -115,30 +117,50 @@ public function getrange(string $key, int $start, int $end): string;
 
     public function getset(string $key, mixed $value): string;
 
+    public function hdel(string $key, string $member, string ...$other_members): int;
+
+    public function hexists(string $key, string $member): bool;
+
+    public function hget(string $key, string $member): string;
+
+    public function hgetall(string $key): array;
+
+    public function hincrby(string $key, string $member, int $value): int;
+
+    public function hincrbyfloat(string $key, string $member, float $value): float;
+
+    public function hkeys(string $key): array;
+
+    public function hlen(string $key): int;
+
+    public function hmget(string $key, array $members): array;
+
+    public function hmset(string $key, array $key_values): bool;
+
+    public function hscan(string $key, int $iterator, ?string $pattern = null, int $count = 0): array|bool;
+
+    public function hset(string $key, string $member, mixed $value): int;
+
+    public function hsetnx(string $key, string $member, mixed $value): bool;
+
+    public function hstrlen(string $key, string $field): int;
+
+    public function hvals(string $key): array;
+
+    public function incr(string $key): int;
+
+    public function incrby(string $key, int $value): int;
+
+    public function incrbyfloat(string $key, float $value): float;
+
+    public function info(string|array $node, ?string $section = null): array;
+
+    public function keys(string $pattern): array;
+
 }
 
 /*
     TODO:
-    public function hdel
-    public function hexists
-    public function hget
-    public function hgetall
-    public function hincrby
-    public function hincrbyfloat
-    public function hkeys
-    public function hlen
-    public function hmget
-    public function hmset
-    public function hscan
-    public function hset
-    public function hsetnx
-    public function hstrlen
-    public function hvals
-    public function incr
-    public function incrby
-    public function incrbyfloat
-    public function info
-    public function keys
     public function lastsave
     public function lget
     public function lindex
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 1a22b993ce..2927a164c2 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5b130a06b4290b7ebec9b20d3973f726cc3fe7af */
+ * Stub hash: d6ed41291334f05001a55176b2aef85a183f09dc */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -124,6 +124,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_decrby, 0, 2,
 	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_decrbyfloat, 0, 2, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_del, 0, 1, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
@@ -241,6 +246,93 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getset, 0, 2,
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hdel, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hexists, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hget, 0, 2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hgetall, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hincrby, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hincrbyfloat, 0, 3, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster_hgetall
+
+#define arginfo_class_RedisCluster_hlen arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hmget, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, members, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hmset, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2, MAY_BE_ARRAY|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hset, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hsetnx, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hstrlen, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster_hgetall
+
+#define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr
+
+#define arginfo_class_RedisCluster_incrby arginfo_class_RedisCluster_decrby
+
+#define arginfo_class_RedisCluster_incrbyfloat arginfo_class_RedisCluster_decrbyfloat
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_info, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, section, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_keys, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -269,6 +361,7 @@ ZEND_METHOD(RedisCluster, config);
 ZEND_METHOD(RedisCluster, dbsize);
 ZEND_METHOD(RedisCluster, decr);
 ZEND_METHOD(RedisCluster, decrby);
+ZEND_METHOD(RedisCluster, decrbyfloat);
 ZEND_METHOD(RedisCluster, del);
 ZEND_METHOD(RedisCluster, discard);
 ZEND_METHOD(RedisCluster, dump);
@@ -296,6 +389,26 @@ ZEND_METHOD(RedisCluster, getmode);
 ZEND_METHOD(RedisCluster, getoption);
 ZEND_METHOD(RedisCluster, getrange);
 ZEND_METHOD(RedisCluster, getset);
+ZEND_METHOD(RedisCluster, hdel);
+ZEND_METHOD(RedisCluster, hexists);
+ZEND_METHOD(RedisCluster, hget);
+ZEND_METHOD(RedisCluster, hgetall);
+ZEND_METHOD(RedisCluster, hincrby);
+ZEND_METHOD(RedisCluster, hincrbyfloat);
+ZEND_METHOD(RedisCluster, hkeys);
+ZEND_METHOD(RedisCluster, hlen);
+ZEND_METHOD(RedisCluster, hmget);
+ZEND_METHOD(RedisCluster, hmset);
+ZEND_METHOD(RedisCluster, hscan);
+ZEND_METHOD(RedisCluster, hset);
+ZEND_METHOD(RedisCluster, hsetnx);
+ZEND_METHOD(RedisCluster, hstrlen);
+ZEND_METHOD(RedisCluster, hvals);
+ZEND_METHOD(RedisCluster, incr);
+ZEND_METHOD(RedisCluster, incrby);
+ZEND_METHOD(RedisCluster, incrbyfloat);
+ZEND_METHOD(RedisCluster, info);
+ZEND_METHOD(RedisCluster, keys);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -326,6 +439,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC)
@@ -353,5 +467,25 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hgetall, arginfo_class_RedisCluster_hgetall, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hincrby, arginfo_class_RedisCluster_hincrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hincrbyfloat, arginfo_class_RedisCluster_hincrbyfloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hkeys, arginfo_class_RedisCluster_hkeys, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hlen, arginfo_class_RedisCluster_hlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, incrbyfloat, arginfo_class_RedisCluster_incrbyfloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, info, arginfo_class_RedisCluster_info, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, keys, arginfo_class_RedisCluster_keys, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index fe78be8fd4..76b37690ac 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5b130a06b4290b7ebec9b20d3973f726cc3fe7af */
+ * Stub hash: d6ed41291334f05001a55176b2aef85a183f09dc */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -113,6 +113,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_decrby arginfo_class_RedisCluster_append
 
+#define arginfo_class_RedisCluster_decrbyfloat arginfo_class_RedisCluster_append
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_del, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_VARIADIC_INFO(0, other_keys)
@@ -224,6 +226,72 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_getset arginfo_class_RedisCluster_append
 
+#define arginfo_class_RedisCluster_hdel arginfo_class_RedisCluster_geohash
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexists, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_hget arginfo_class_RedisCluster_hexists
+
+#define arginfo_class_RedisCluster_hgetall arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hincrby, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_hincrbyfloat arginfo_class_RedisCluster_hincrby
+
+#define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_hlen arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hmget, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, members)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hmset, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, key_values)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hscan, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, iterator)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_hset arginfo_class_RedisCluster_hincrby
+
+#define arginfo_class_RedisCluster_hsetnx arginfo_class_RedisCluster_hincrby
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hstrlen, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, field)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_incrby arginfo_class_RedisCluster_append
+
+#define arginfo_class_RedisCluster_incrbyfloat arginfo_class_RedisCluster_append
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_info, 0, 0, 1)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, section)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_keys, 0, 0, 1)
+	ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -252,6 +320,7 @@ ZEND_METHOD(RedisCluster, config);
 ZEND_METHOD(RedisCluster, dbsize);
 ZEND_METHOD(RedisCluster, decr);
 ZEND_METHOD(RedisCluster, decrby);
+ZEND_METHOD(RedisCluster, decrbyfloat);
 ZEND_METHOD(RedisCluster, del);
 ZEND_METHOD(RedisCluster, discard);
 ZEND_METHOD(RedisCluster, dump);
@@ -279,6 +348,26 @@ ZEND_METHOD(RedisCluster, getmode);
 ZEND_METHOD(RedisCluster, getoption);
 ZEND_METHOD(RedisCluster, getrange);
 ZEND_METHOD(RedisCluster, getset);
+ZEND_METHOD(RedisCluster, hdel);
+ZEND_METHOD(RedisCluster, hexists);
+ZEND_METHOD(RedisCluster, hget);
+ZEND_METHOD(RedisCluster, hgetall);
+ZEND_METHOD(RedisCluster, hincrby);
+ZEND_METHOD(RedisCluster, hincrbyfloat);
+ZEND_METHOD(RedisCluster, hkeys);
+ZEND_METHOD(RedisCluster, hlen);
+ZEND_METHOD(RedisCluster, hmget);
+ZEND_METHOD(RedisCluster, hmset);
+ZEND_METHOD(RedisCluster, hscan);
+ZEND_METHOD(RedisCluster, hset);
+ZEND_METHOD(RedisCluster, hsetnx);
+ZEND_METHOD(RedisCluster, hstrlen);
+ZEND_METHOD(RedisCluster, hvals);
+ZEND_METHOD(RedisCluster, incr);
+ZEND_METHOD(RedisCluster, incrby);
+ZEND_METHOD(RedisCluster, incrbyfloat);
+ZEND_METHOD(RedisCluster, info);
+ZEND_METHOD(RedisCluster, keys);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -309,6 +398,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC)
@@ -336,5 +426,25 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hgetall, arginfo_class_RedisCluster_hgetall, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hincrby, arginfo_class_RedisCluster_hincrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hincrbyfloat, arginfo_class_RedisCluster_hincrbyfloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hkeys, arginfo_class_RedisCluster_hkeys, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hlen, arginfo_class_RedisCluster_hlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, incrbyfloat, arginfo_class_RedisCluster_incrbyfloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, info, arginfo_class_RedisCluster_info, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, keys, arginfo_class_RedisCluster_keys, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 4d3db95b39ceb444b809458ece8fac3153129584 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Viktor=20Djupsj=C3=B6backa?=
 
Date: Fri, 23 Jul 2021 20:00:32 +0300
Subject: [PATCH 1478/1986] Add stub-based arginfo for Redis Cluster methods
 l-o

---
 redis_cluster.stub.php | 50 ++++++++++++++++++++++++++++--------------
 1 file changed, 33 insertions(+), 17 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 43dfb0a4c0..e02a23f032 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -157,27 +157,43 @@ public function info(string|array $node, ?string $section = null): array;
 
     public function keys(string $pattern): array;
 
+    public function lastsave(string|array $node): int;
+
+    public function lget(string $key, int $index): string|bool;
+
+    public function lindex(string $key, int $index): string|bool;
+
+    public function linsert(string $key, string $pos, mixed $pivot, mixed $value): int;
+
+    public function llen(string $key): int|bool;
+
+    public function lpop(string $key): string|bool;
+
+    public function lpush(string $key, mixed $value, ...mixed $other_values): int|bool;
+
+    public function lpushx(string $key, mixed $value): int|bool;
+
+    public function lrange(string $key, int $start, int $end): array;
+
+    public function lrem(string $key, int $count, string $value): int|bool;
+
+    public function lset(string $key, int $index, string $value): bool;
+
+    public function ltrim(string $key, int $start, int $end): bool;
+
+    public function mget(array $keys): array;
+
+    public function mset(array $key_values): bool;
+
+    public function msetnx(array $key_values): int;
+
+    public function multi(): self|bool;
+
+    public function object(string $subcommand, string $key): string|int|bool;
 }
 
 /*
     TODO:
-    public function lastsave
-    public function lget
-    public function lindex
-    public function linsert
-    public function llen
-    public function lpop
-    public function lpush
-    public function lpushx
-    public function lrange
-    public function lrem
-    public function lset
-    public function ltrim
-    public function mget
-    public function mset
-    public function msetnx
-    public function multi
-    public function object
     public function persist
     public function pexpire
     public function pexpireat

From d527061aaa8cc26de291023cac8050d731420d64 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 3 Aug 2021 20:15:40 +0300
Subject: [PATCH 1479/1986] Add stub-based arginfo for Redis Cluster methods p

---
 redis_cluster.stub.php         |  41 ++++---
 redis_cluster_arginfo.h        | 196 ++++++++++++++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h | 176 ++++++++++++++++++++++++++++-
 3 files changed, 397 insertions(+), 16 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index e02a23f032..3e9d0eb51f 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -169,7 +169,7 @@ public function llen(string $key): int|bool;
 
     public function lpop(string $key): string|bool;
 
-    public function lpush(string $key, mixed $value, ...mixed $other_values): int|bool;
+    public function lpush(string $key, mixed $value, mixed ...$other_values): int|bool;
 
     public function lpushx(string $key, mixed $value): int|bool;
 
@@ -190,23 +190,36 @@ public function msetnx(array $key_values): int;
     public function multi(): self|bool;
 
     public function object(string $subcommand, string $key): string|int|bool;
+
+    public function persist(string $key): bool;
+
+    public function pexpire(string $key, int $timeout): bool;
+
+    public function pexpireat(string $key, int $timestamp): bool;
+
+    public function pfadd(string $key, array $elements): bool;
+
+    public function pfcount(string $key): int;
+
+    public function pfmerge(string $key, array $keys): bool;
+
+    public function ping(string|array $key_or_address, ?string $message): mixed;
+
+    public function psetex(string $key, int $timeout, string $value): bool;
+
+    public function psubscribe(array $patterns, callable $callback): void;
+
+    public function pttl(string $key): int;
+
+    public function publish(string $channel, string $message): bool;
+
+    public function pubsub(string|array $key_or_address, string ...$values): mixed;
+
+    public function punsubscribe(string $pattern, string ...$other_patterns): bool|array;
 }
 
 /*
     TODO:
-    public function persist
-    public function pexpire
-    public function pexpireat
-    public function pfadd
-    public function pfcount
-    public function pfmerge
-    public function ping
-    public function psetex
-    public function psubscribe
-    public function pttl
-    public function publish
-    public function pubsub
-    public function punsubscribe
     public function randomkey
     public function rawcommand
     public function rename
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 2927a164c2..dec11e20b8 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d6ed41291334f05001a55176b2aef85a183f09dc */
+ * Stub hash: 52c97ef3f0f7fb1a3205fdfce3b9929852f540b6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -333,6 +333,140 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_keys, 0, 1, I
 	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_lastsave, 0, 1, IS_LONG, 0)
+	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lget, 0, 2, MAY_BE_STRING|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_lindex arginfo_class_RedisCluster_lget
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_linsert, 0, 4, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, pos, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, pivot, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_llen, 0, 1, MAY_BE_LONG|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpop, 0, 1, MAY_BE_STRING|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, MAY_BE_LONG|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpushx, 0, 2, MAY_BE_LONG|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_lrange, 0, 3, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lrem, 0, 3, MAY_BE_LONG|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_lset, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_ltrim, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_mget, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_mset, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_msetnx, 0, 1, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_multi, 0, 0, self, MAY_BE_BOOL)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_object, 0, 2, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_persist, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_pexpire arginfo_class_RedisCluster_expire
+
+#define arginfo_class_RedisCluster_pexpireat arginfo_class_RedisCluster_expireat
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_pfadd, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, elements, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_pfcount arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_pfmerge, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_ping, 0, 2, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 1)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_psetex, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_psubscribe, 0, 2, IS_VOID, 0)
+	ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_pttl arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_publish, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_pubsub, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, values, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_punsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_patterns, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -409,6 +543,36 @@ ZEND_METHOD(RedisCluster, incrby);
 ZEND_METHOD(RedisCluster, incrbyfloat);
 ZEND_METHOD(RedisCluster, info);
 ZEND_METHOD(RedisCluster, keys);
+ZEND_METHOD(RedisCluster, lastsave);
+ZEND_METHOD(RedisCluster, lget);
+ZEND_METHOD(RedisCluster, lindex);
+ZEND_METHOD(RedisCluster, linsert);
+ZEND_METHOD(RedisCluster, llen);
+ZEND_METHOD(RedisCluster, lpop);
+ZEND_METHOD(RedisCluster, lpush);
+ZEND_METHOD(RedisCluster, lpushx);
+ZEND_METHOD(RedisCluster, lrange);
+ZEND_METHOD(RedisCluster, lrem);
+ZEND_METHOD(RedisCluster, lset);
+ZEND_METHOD(RedisCluster, ltrim);
+ZEND_METHOD(RedisCluster, mget);
+ZEND_METHOD(RedisCluster, mset);
+ZEND_METHOD(RedisCluster, msetnx);
+ZEND_METHOD(RedisCluster, multi);
+ZEND_METHOD(RedisCluster, object);
+ZEND_METHOD(RedisCluster, persist);
+ZEND_METHOD(RedisCluster, pexpire);
+ZEND_METHOD(RedisCluster, pexpireat);
+ZEND_METHOD(RedisCluster, pfadd);
+ZEND_METHOD(RedisCluster, pfcount);
+ZEND_METHOD(RedisCluster, pfmerge);
+ZEND_METHOD(RedisCluster, ping);
+ZEND_METHOD(RedisCluster, psetex);
+ZEND_METHOD(RedisCluster, psubscribe);
+ZEND_METHOD(RedisCluster, pttl);
+ZEND_METHOD(RedisCluster, publish);
+ZEND_METHOD(RedisCluster, pubsub);
+ZEND_METHOD(RedisCluster, punsubscribe);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -487,5 +651,35 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, incrbyfloat, arginfo_class_RedisCluster_incrbyfloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, info, arginfo_class_RedisCluster_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, keys, arginfo_class_RedisCluster_keys, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lastsave, arginfo_class_RedisCluster_lastsave, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lget, arginfo_class_RedisCluster_lget, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lindex, arginfo_class_RedisCluster_lindex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, linsert, arginfo_class_RedisCluster_linsert, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, llen, arginfo_class_RedisCluster_llen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lpop, arginfo_class_RedisCluster_lpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lpush, arginfo_class_RedisCluster_lpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lpushx, arginfo_class_RedisCluster_lpushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lrange, arginfo_class_RedisCluster_lrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lrem, arginfo_class_RedisCluster_lrem, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lset, arginfo_class_RedisCluster_lset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, ltrim, arginfo_class_RedisCluster_ltrim, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, mget, arginfo_class_RedisCluster_mget, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, mset, arginfo_class_RedisCluster_mset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, msetnx, arginfo_class_RedisCluster_msetnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, multi, arginfo_class_RedisCluster_multi, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, object, arginfo_class_RedisCluster_object, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, persist, arginfo_class_RedisCluster_persist, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pexpire, arginfo_class_RedisCluster_pexpire, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pexpireat, arginfo_class_RedisCluster_pexpireat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pfadd, arginfo_class_RedisCluster_pfadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pfcount, arginfo_class_RedisCluster_pfcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pfmerge, arginfo_class_RedisCluster_pfmerge, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, ping, arginfo_class_RedisCluster_ping, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, psetex, arginfo_class_RedisCluster_psetex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, psubscribe, arginfo_class_RedisCluster_psubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pttl, arginfo_class_RedisCluster_pttl, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, publish, arginfo_class_RedisCluster_publish, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pubsub, arginfo_class_RedisCluster_pubsub, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, punsubscribe, arginfo_class_RedisCluster_punsubscribe, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 76b37690ac..4a1bc99a7d 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d6ed41291334f05001a55176b2aef85a183f09dc */
+ * Stub hash: 52c97ef3f0f7fb1a3205fdfce3b9929852f540b6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -292,6 +292,120 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_keys, 0, 0, 1)
 	ZEND_ARG_INFO(0, pattern)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lastsave, 0, 0, 1)
+	ZEND_ARG_INFO(0, node)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lget, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, index)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_lindex arginfo_class_RedisCluster_lget
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_linsert, 0, 0, 4)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, pos)
+	ZEND_ARG_INFO(0, pivot)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_llen arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_lpop arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpush, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_VARIADIC_INFO(0, other_values)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_lpushx arginfo_class_RedisCluster_append
+
+#define arginfo_class_RedisCluster_lrange arginfo_class_RedisCluster_getrange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lrem, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lset, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, index)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_ltrim arginfo_class_RedisCluster_getrange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_mget, 0, 0, 1)
+	ZEND_ARG_INFO(0, keys)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_mset, 0, 0, 1)
+	ZEND_ARG_INFO(0, key_values)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_msetnx arginfo_class_RedisCluster_mset
+
+#define arginfo_class_RedisCluster_multi arginfo_class_RedisCluster__masters
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_object, 0, 0, 2)
+	ZEND_ARG_INFO(0, subcommand)
+	ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_persist arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_pexpire arginfo_class_RedisCluster_expire
+
+#define arginfo_class_RedisCluster_pexpireat arginfo_class_RedisCluster_expireat
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_pfadd, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, elements)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_pfcount arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_pfmerge, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, keys)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_ping, 0, 0, 2)
+	ZEND_ARG_INFO(0, key_or_address)
+	ZEND_ARG_INFO(0, message)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_psetex, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_psubscribe, 0, 0, 2)
+	ZEND_ARG_INFO(0, patterns)
+	ZEND_ARG_INFO(0, callback)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_pttl arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_publish, 0, 0, 2)
+	ZEND_ARG_INFO(0, channel)
+	ZEND_ARG_INFO(0, message)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_pubsub, 0, 0, 1)
+	ZEND_ARG_INFO(0, key_or_address)
+	ZEND_ARG_VARIADIC_INFO(0, values)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_punsubscribe, 0, 0, 1)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_VARIADIC_INFO(0, other_patterns)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -368,6 +482,36 @@ ZEND_METHOD(RedisCluster, incrby);
 ZEND_METHOD(RedisCluster, incrbyfloat);
 ZEND_METHOD(RedisCluster, info);
 ZEND_METHOD(RedisCluster, keys);
+ZEND_METHOD(RedisCluster, lastsave);
+ZEND_METHOD(RedisCluster, lget);
+ZEND_METHOD(RedisCluster, lindex);
+ZEND_METHOD(RedisCluster, linsert);
+ZEND_METHOD(RedisCluster, llen);
+ZEND_METHOD(RedisCluster, lpop);
+ZEND_METHOD(RedisCluster, lpush);
+ZEND_METHOD(RedisCluster, lpushx);
+ZEND_METHOD(RedisCluster, lrange);
+ZEND_METHOD(RedisCluster, lrem);
+ZEND_METHOD(RedisCluster, lset);
+ZEND_METHOD(RedisCluster, ltrim);
+ZEND_METHOD(RedisCluster, mget);
+ZEND_METHOD(RedisCluster, mset);
+ZEND_METHOD(RedisCluster, msetnx);
+ZEND_METHOD(RedisCluster, multi);
+ZEND_METHOD(RedisCluster, object);
+ZEND_METHOD(RedisCluster, persist);
+ZEND_METHOD(RedisCluster, pexpire);
+ZEND_METHOD(RedisCluster, pexpireat);
+ZEND_METHOD(RedisCluster, pfadd);
+ZEND_METHOD(RedisCluster, pfcount);
+ZEND_METHOD(RedisCluster, pfmerge);
+ZEND_METHOD(RedisCluster, ping);
+ZEND_METHOD(RedisCluster, psetex);
+ZEND_METHOD(RedisCluster, psubscribe);
+ZEND_METHOD(RedisCluster, pttl);
+ZEND_METHOD(RedisCluster, publish);
+ZEND_METHOD(RedisCluster, pubsub);
+ZEND_METHOD(RedisCluster, punsubscribe);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -446,5 +590,35 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, incrbyfloat, arginfo_class_RedisCluster_incrbyfloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, info, arginfo_class_RedisCluster_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, keys, arginfo_class_RedisCluster_keys, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lastsave, arginfo_class_RedisCluster_lastsave, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lget, arginfo_class_RedisCluster_lget, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lindex, arginfo_class_RedisCluster_lindex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, linsert, arginfo_class_RedisCluster_linsert, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, llen, arginfo_class_RedisCluster_llen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lpop, arginfo_class_RedisCluster_lpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lpush, arginfo_class_RedisCluster_lpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lpushx, arginfo_class_RedisCluster_lpushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lrange, arginfo_class_RedisCluster_lrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lrem, arginfo_class_RedisCluster_lrem, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lset, arginfo_class_RedisCluster_lset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, ltrim, arginfo_class_RedisCluster_ltrim, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, mget, arginfo_class_RedisCluster_mget, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, mset, arginfo_class_RedisCluster_mset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, msetnx, arginfo_class_RedisCluster_msetnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, multi, arginfo_class_RedisCluster_multi, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, object, arginfo_class_RedisCluster_object, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, persist, arginfo_class_RedisCluster_persist, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pexpire, arginfo_class_RedisCluster_pexpire, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pexpireat, arginfo_class_RedisCluster_pexpireat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pfadd, arginfo_class_RedisCluster_pfadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pfcount, arginfo_class_RedisCluster_pfcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pfmerge, arginfo_class_RedisCluster_pfmerge, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, ping, arginfo_class_RedisCluster_ping, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, psetex, arginfo_class_RedisCluster_psetex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, psubscribe, arginfo_class_RedisCluster_psubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pttl, arginfo_class_RedisCluster_pttl, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, publish, arginfo_class_RedisCluster_publish, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pubsub, arginfo_class_RedisCluster_pubsub, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, punsubscribe, arginfo_class_RedisCluster_punsubscribe, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From ecd6462309247ee554469fe2ccd765be0b7db589 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 4 Aug 2021 15:36:06 +0300
Subject: [PATCH 1480/1986] Add stub-based arginfo for Redis Cluster methods r

---
 redis_cluster.stub.php         | 30 ++++++++++------
 redis_cluster_arginfo.h        | 63 +++++++++++++++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h | 52 +++++++++++++++++++++++++++-
 3 files changed, 133 insertions(+), 12 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 3e9d0eb51f..7ce729abf0 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -216,20 +216,30 @@ public function publish(string $channel, string $message): bool;
     public function pubsub(string|array $key_or_address, string ...$values): mixed;
 
     public function punsubscribe(string $pattern, string ...$other_patterns): bool|array;
+
+    public function randomkey(string|array $key_or_address): bool|string;
+
+    public function rawcommand(string|array $key_or_address, string $command, mixed ...$args): mixed;
+
+    public function rename(string $key, string $newkey): bool;
+
+    public function renamenx(string $key, string $newkey): bool;
+
+    public function restore(string $key, int $timeout, string $value): bool;
+
+    public function role(string|array $key_or_address): mixed;
+
+    public function rpop(string $key): bool|string;
+
+    public function rpoplpush(string $src, string $dst): bool|string;
+
+    public function rpush(string $key, string $value, string ...$other_values): bool|int;
+
+    public function rpushx(string $key, string $value): bool|int;
 }
 
 /*
     TODO:
-    public function randomkey
-    public function rawcommand
-    public function rename
-    public function renamenx
-    public function restore
-    public function role
-    public function rpop
-    public function rpoplpush
-    public function rpush
-    public function rpushx
     public function sadd
     public function saddarray
     public function save
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index dec11e20b8..1d106e2e5f 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 52c97ef3f0f7fb1a3205fdfce3b9929852f540b6 */
+ * Stub hash: 397fd43d7b94f97620da517fdbeaccf2de4b55f3 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -467,6 +467,47 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_punsubscribe,
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_patterns, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_randomkey, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_rawcommand, 0, 2, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_rename, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, newkey, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_renamenx arginfo_class_RedisCluster_rename
+
+#define arginfo_class_RedisCluster_restore arginfo_class_RedisCluster_psetex
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_role, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_rpoplpush, 0, 2, MAY_BE_BOOL|MAY_BE_STRING)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_rpush, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_rpushx, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -573,6 +614,16 @@ ZEND_METHOD(RedisCluster, pttl);
 ZEND_METHOD(RedisCluster, publish);
 ZEND_METHOD(RedisCluster, pubsub);
 ZEND_METHOD(RedisCluster, punsubscribe);
+ZEND_METHOD(RedisCluster, randomkey);
+ZEND_METHOD(RedisCluster, rawcommand);
+ZEND_METHOD(RedisCluster, rename);
+ZEND_METHOD(RedisCluster, renamenx);
+ZEND_METHOD(RedisCluster, restore);
+ZEND_METHOD(RedisCluster, role);
+ZEND_METHOD(RedisCluster, rpop);
+ZEND_METHOD(RedisCluster, rpoplpush);
+ZEND_METHOD(RedisCluster, rpush);
+ZEND_METHOD(RedisCluster, rpushx);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -681,5 +732,15 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, publish, arginfo_class_RedisCluster_publish, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, pubsub, arginfo_class_RedisCluster_pubsub, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, punsubscribe, arginfo_class_RedisCluster_punsubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, randomkey, arginfo_class_RedisCluster_randomkey, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rawcommand, arginfo_class_RedisCluster_rawcommand, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rename, arginfo_class_RedisCluster_rename, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, renamenx, arginfo_class_RedisCluster_renamenx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, restore, arginfo_class_RedisCluster_restore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, role, arginfo_class_RedisCluster_role, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rpop, arginfo_class_RedisCluster_rpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rpoplpush, arginfo_class_RedisCluster_rpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rpush, arginfo_class_RedisCluster_rpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rpushx, arginfo_class_RedisCluster_rpushx, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 4a1bc99a7d..2b1b6083ad 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 52c97ef3f0f7fb1a3205fdfce3b9929852f540b6 */
+ * Stub hash: 397fd43d7b94f97620da517fdbeaccf2de4b55f3 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -406,6 +406,36 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_punsubscribe, 0, 0, 1)
 	ZEND_ARG_VARIADIC_INFO(0, other_patterns)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_randomkey arginfo_class_RedisCluster_bgrewriteaof
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rawcommand, 0, 0, 2)
+	ZEND_ARG_INFO(0, key_or_address)
+	ZEND_ARG_INFO(0, command)
+	ZEND_ARG_VARIADIC_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rename, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, newkey)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_renamenx arginfo_class_RedisCluster_rename
+
+#define arginfo_class_RedisCluster_restore arginfo_class_RedisCluster_psetex
+
+#define arginfo_class_RedisCluster_role arginfo_class_RedisCluster_bgrewriteaof
+
+#define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rpoplpush, 0, 0, 2)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_rpush arginfo_class_RedisCluster_lpush
+
+#define arginfo_class_RedisCluster_rpushx arginfo_class_RedisCluster_append
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -512,6 +542,16 @@ ZEND_METHOD(RedisCluster, pttl);
 ZEND_METHOD(RedisCluster, publish);
 ZEND_METHOD(RedisCluster, pubsub);
 ZEND_METHOD(RedisCluster, punsubscribe);
+ZEND_METHOD(RedisCluster, randomkey);
+ZEND_METHOD(RedisCluster, rawcommand);
+ZEND_METHOD(RedisCluster, rename);
+ZEND_METHOD(RedisCluster, renamenx);
+ZEND_METHOD(RedisCluster, restore);
+ZEND_METHOD(RedisCluster, role);
+ZEND_METHOD(RedisCluster, rpop);
+ZEND_METHOD(RedisCluster, rpoplpush);
+ZEND_METHOD(RedisCluster, rpush);
+ZEND_METHOD(RedisCluster, rpushx);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -620,5 +660,15 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, publish, arginfo_class_RedisCluster_publish, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, pubsub, arginfo_class_RedisCluster_pubsub, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, punsubscribe, arginfo_class_RedisCluster_punsubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, randomkey, arginfo_class_RedisCluster_randomkey, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rawcommand, arginfo_class_RedisCluster_rawcommand, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rename, arginfo_class_RedisCluster_rename, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, renamenx, arginfo_class_RedisCluster_renamenx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, restore, arginfo_class_RedisCluster_restore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, role, arginfo_class_RedisCluster_role, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rpop, arginfo_class_RedisCluster_rpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rpoplpush, arginfo_class_RedisCluster_rpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rpush, arginfo_class_RedisCluster_rpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, rpushx, arginfo_class_RedisCluster_rpushx, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 78bb6b367b9dd235f59d7a08ab5e241e0df014e3 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 5 Aug 2021 21:27:53 +0300
Subject: [PATCH 1481/1986] Add stub-based arginfo for Redis Cluster methods s

---
 redis_cluster.stub.php         |  87 ++++++++++------
 redis_cluster_arginfo.h        | 183 ++++++++++++++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h | 167 +++++++++++++++++++++++++++++-
 3 files changed, 406 insertions(+), 31 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 7ce729abf0..98fda4f4e1 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -236,39 +236,68 @@ public function rpoplpush(string $src, string $dst): bool|string;
     public function rpush(string $key, string $value, string ...$other_values): bool|int;
 
     public function rpushx(string $key, string $value): bool|int;
+
+    public function sadd(string $key, string $value, string ...$other_values): bool|int;
+
+    public function saddarray(string $key, array $values): bool|int;
+
+    public function save(string|array $key_or_address): bool;
+
+    public function scan(int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
+
+    public function scard(string $key): int;
+
+    public function script(string|array $key_or_address, mixed ...$args): mixed;
+
+    public function sdiff(string $key, string ...$other_keys): array;
+
+    public function sdiffstore(string $dst, string $key, string ...$other_keys): int;
+
+    public function set(string $key, string $value): bool;
+
+    public function setbit(string $key, int $offset, bool $onoff): bool;
+
+    public function setex(string $key, string $value, int $timeout): bool;
+
+    public function setnx(string $key, string $value, int $timeout): bool;
+
+    public function setoption(int $option, mixed $value): bool;
+
+    public function setrange(string $key, int $offset, string $value): int;
+
+    public function sinter(string $key, string ...$other_keys): array;
+
+    public function sinterstore(string $dst, string $key, string ...$other_keys): bool;
+
+    public function sismember(string $key): int;
+
+    public function slowlog(string|array $key_or_address, mixed ...$args): mixed;
+
+    public function smembers(string $key): array;
+
+    public function smove(string $src, string $dst, string $member): bool;
+
+    public function sort(string $key, array $options): bool|int|string;
+
+    public function spop(string $key): string|array;
+
+    public function srandmember(string $key, int $count = 0): string|array;
+
+    public function srem(string $key, string $value, string ...$other_values): int;
+
+    public function sscan(string $key, int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
+
+    public function strlen(string $key): int;
+
+    public function subscribe(array $channels, callable $cb): void;
+
+    public function sunion(string $key, string ...$other_keys): bool|array;
+
+    public function sunionstore(string $dst, string $key, string ...$other_keys): int;
 }
 
 /*
     TODO:
-    public function sadd
-    public function saddarray
-    public function save
-    public function scan
-    public function scard
-    public function script
-    public function sdiff
-    public function sdiffstore
-    public function set
-    public function setbit
-    public function setex
-    public function setnx
-    public function setoption
-    public function setrange
-    public function sinter
-    public function sinterstore
-    public function sismember
-    public function slowlog
-    public function smembers
-    public function smove
-    public function sort
-    public function spop
-    public function srandmember
-    public function srem
-    public function sscan
-    public function strlen
-    public function subscribe
-    public function sunion
-    public function sunionstore
     public function time
     public function ttl
     public function type
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 1d106e2e5f..ecba63ae48 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 397fd43d7b94f97620da517fdbeaccf2de4b55f3 */
+ * Stub hash: a8dbdd46b676b84c2d644382b7c4e108a820b12f */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -508,6 +508,129 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_rpushx, 0, 2,
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_sadd arginfo_class_RedisCluster_rpush
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_saddarray, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_save arginfo_class_RedisCluster_bgrewriteaof
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, node, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_scard arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_script, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_sdiff arginfo_class_RedisCluster_del
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_sdiffstore, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_set, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setbit, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, onoff, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setex, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_setnx arginfo_class_RedisCluster_setex
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setoption, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setrange, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_sinter arginfo_class_RedisCluster_del
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_sinterstore, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster_decr
+
+#define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script
+
+#define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster_hgetall
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_smove, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sort, 0, 2, MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_STRING)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_spop, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_srandmember, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_srem, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sscan, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, node, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_strlen arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_subscribe, 0, 2, IS_VOID, 0)
+	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sunion, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_sunionstore arginfo_class_RedisCluster_sdiffstore
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -624,6 +747,35 @@ ZEND_METHOD(RedisCluster, rpop);
 ZEND_METHOD(RedisCluster, rpoplpush);
 ZEND_METHOD(RedisCluster, rpush);
 ZEND_METHOD(RedisCluster, rpushx);
+ZEND_METHOD(RedisCluster, sadd);
+ZEND_METHOD(RedisCluster, saddarray);
+ZEND_METHOD(RedisCluster, save);
+ZEND_METHOD(RedisCluster, scan);
+ZEND_METHOD(RedisCluster, scard);
+ZEND_METHOD(RedisCluster, script);
+ZEND_METHOD(RedisCluster, sdiff);
+ZEND_METHOD(RedisCluster, sdiffstore);
+ZEND_METHOD(RedisCluster, set);
+ZEND_METHOD(RedisCluster, setbit);
+ZEND_METHOD(RedisCluster, setex);
+ZEND_METHOD(RedisCluster, setnx);
+ZEND_METHOD(RedisCluster, setoption);
+ZEND_METHOD(RedisCluster, setrange);
+ZEND_METHOD(RedisCluster, sinter);
+ZEND_METHOD(RedisCluster, sinterstore);
+ZEND_METHOD(RedisCluster, sismember);
+ZEND_METHOD(RedisCluster, slowlog);
+ZEND_METHOD(RedisCluster, smembers);
+ZEND_METHOD(RedisCluster, smove);
+ZEND_METHOD(RedisCluster, sort);
+ZEND_METHOD(RedisCluster, spop);
+ZEND_METHOD(RedisCluster, srandmember);
+ZEND_METHOD(RedisCluster, srem);
+ZEND_METHOD(RedisCluster, sscan);
+ZEND_METHOD(RedisCluster, strlen);
+ZEND_METHOD(RedisCluster, subscribe);
+ZEND_METHOD(RedisCluster, sunion);
+ZEND_METHOD(RedisCluster, sunionstore);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -742,5 +894,34 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, rpoplpush, arginfo_class_RedisCluster_rpoplpush, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, rpush, arginfo_class_RedisCluster_rpush, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, rpushx, arginfo_class_RedisCluster_rpushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sadd, arginfo_class_RedisCluster_sadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, saddarray, arginfo_class_RedisCluster_saddarray, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, save, arginfo_class_RedisCluster_save, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, scan, arginfo_class_RedisCluster_scan, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, scard, arginfo_class_RedisCluster_scard, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, script, arginfo_class_RedisCluster_script, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sdiff, arginfo_class_RedisCluster_sdiff, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sdiffstore, arginfo_class_RedisCluster_sdiffstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, set, arginfo_class_RedisCluster_set, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setbit, arginfo_class_RedisCluster_setbit, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setex, arginfo_class_RedisCluster_setex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setnx, arginfo_class_RedisCluster_setnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setoption, arginfo_class_RedisCluster_setoption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setrange, arginfo_class_RedisCluster_setrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sinter, arginfo_class_RedisCluster_sinter, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sinterstore, arginfo_class_RedisCluster_sinterstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sismember, arginfo_class_RedisCluster_sismember, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, slowlog, arginfo_class_RedisCluster_slowlog, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, smembers, arginfo_class_RedisCluster_smembers, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, smove, arginfo_class_RedisCluster_smove, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sort, arginfo_class_RedisCluster_sort, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, spop, arginfo_class_RedisCluster_spop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, srandmember, arginfo_class_RedisCluster_srandmember, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, srem, arginfo_class_RedisCluster_srem, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sscan, arginfo_class_RedisCluster_sscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, strlen, arginfo_class_RedisCluster_strlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, subscribe, arginfo_class_RedisCluster_subscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sunion, arginfo_class_RedisCluster_sunion, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sunionstore, arginfo_class_RedisCluster_sunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 2b1b6083ad..b7e9ba53de 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 397fd43d7b94f97620da517fdbeaccf2de4b55f3 */
+ * Stub hash: a8dbdd46b676b84c2d644382b7c4e108a820b12f */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -436,6 +436,113 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_rpushx arginfo_class_RedisCluster_append
 
+#define arginfo_class_RedisCluster_sadd arginfo_class_RedisCluster_lpush
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_saddarray, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, values)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_save arginfo_class_RedisCluster_bgrewriteaof
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_scan, 0, 0, 2)
+	ZEND_ARG_INFO(1, iterator)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_scard arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_script, 0, 0, 1)
+	ZEND_ARG_INFO(0, key_or_address)
+	ZEND_ARG_VARIADIC_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_sdiff arginfo_class_RedisCluster_del
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sdiffstore, 0, 0, 2)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_VARIADIC_INFO(0, other_keys)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_set arginfo_class_RedisCluster_append
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setbit, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, offset)
+	ZEND_ARG_INFO(0, onoff)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setex, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_setnx arginfo_class_RedisCluster_setex
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setoption, 0, 0, 2)
+	ZEND_ARG_INFO(0, option)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setrange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, offset)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_sinter arginfo_class_RedisCluster_del
+
+#define arginfo_class_RedisCluster_sinterstore arginfo_class_RedisCluster_sdiffstore
+
+#define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script
+
+#define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_smove, 0, 0, 3)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, member)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sort, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_srandmember, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_srem arginfo_class_RedisCluster_lpush
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sscan, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(1, iterator)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_strlen arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_subscribe, 0, 0, 2)
+	ZEND_ARG_INFO(0, channels)
+	ZEND_ARG_INFO(0, cb)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_sunion arginfo_class_RedisCluster_del
+
+#define arginfo_class_RedisCluster_sunionstore arginfo_class_RedisCluster_sdiffstore
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -552,6 +659,35 @@ ZEND_METHOD(RedisCluster, rpop);
 ZEND_METHOD(RedisCluster, rpoplpush);
 ZEND_METHOD(RedisCluster, rpush);
 ZEND_METHOD(RedisCluster, rpushx);
+ZEND_METHOD(RedisCluster, sadd);
+ZEND_METHOD(RedisCluster, saddarray);
+ZEND_METHOD(RedisCluster, save);
+ZEND_METHOD(RedisCluster, scan);
+ZEND_METHOD(RedisCluster, scard);
+ZEND_METHOD(RedisCluster, script);
+ZEND_METHOD(RedisCluster, sdiff);
+ZEND_METHOD(RedisCluster, sdiffstore);
+ZEND_METHOD(RedisCluster, set);
+ZEND_METHOD(RedisCluster, setbit);
+ZEND_METHOD(RedisCluster, setex);
+ZEND_METHOD(RedisCluster, setnx);
+ZEND_METHOD(RedisCluster, setoption);
+ZEND_METHOD(RedisCluster, setrange);
+ZEND_METHOD(RedisCluster, sinter);
+ZEND_METHOD(RedisCluster, sinterstore);
+ZEND_METHOD(RedisCluster, sismember);
+ZEND_METHOD(RedisCluster, slowlog);
+ZEND_METHOD(RedisCluster, smembers);
+ZEND_METHOD(RedisCluster, smove);
+ZEND_METHOD(RedisCluster, sort);
+ZEND_METHOD(RedisCluster, spop);
+ZEND_METHOD(RedisCluster, srandmember);
+ZEND_METHOD(RedisCluster, srem);
+ZEND_METHOD(RedisCluster, sscan);
+ZEND_METHOD(RedisCluster, strlen);
+ZEND_METHOD(RedisCluster, subscribe);
+ZEND_METHOD(RedisCluster, sunion);
+ZEND_METHOD(RedisCluster, sunionstore);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -670,5 +806,34 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, rpoplpush, arginfo_class_RedisCluster_rpoplpush, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, rpush, arginfo_class_RedisCluster_rpush, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, rpushx, arginfo_class_RedisCluster_rpushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sadd, arginfo_class_RedisCluster_sadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, saddarray, arginfo_class_RedisCluster_saddarray, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, save, arginfo_class_RedisCluster_save, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, scan, arginfo_class_RedisCluster_scan, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, scard, arginfo_class_RedisCluster_scard, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, script, arginfo_class_RedisCluster_script, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sdiff, arginfo_class_RedisCluster_sdiff, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sdiffstore, arginfo_class_RedisCluster_sdiffstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, set, arginfo_class_RedisCluster_set, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setbit, arginfo_class_RedisCluster_setbit, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setex, arginfo_class_RedisCluster_setex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setnx, arginfo_class_RedisCluster_setnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setoption, arginfo_class_RedisCluster_setoption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, setrange, arginfo_class_RedisCluster_setrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sinter, arginfo_class_RedisCluster_sinter, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sinterstore, arginfo_class_RedisCluster_sinterstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sismember, arginfo_class_RedisCluster_sismember, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, slowlog, arginfo_class_RedisCluster_slowlog, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, smembers, arginfo_class_RedisCluster_smembers, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, smove, arginfo_class_RedisCluster_smove, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sort, arginfo_class_RedisCluster_sort, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, spop, arginfo_class_RedisCluster_spop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, srandmember, arginfo_class_RedisCluster_srandmember, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, srem, arginfo_class_RedisCluster_srem, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sscan, arginfo_class_RedisCluster_sscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, strlen, arginfo_class_RedisCluster_strlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, subscribe, arginfo_class_RedisCluster_subscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sunion, arginfo_class_RedisCluster_sunion, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sunionstore, arginfo_class_RedisCluster_sunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 98c026e38599d98392558457fc43c0dbdc2b7253 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 19 Aug 2021 19:45:21 +0200
Subject: [PATCH 1482/1986] Add stub-based arginfo for Redis Cluster methods
 t-x

---
 redis_cluster.stub.php         |  60 +++++++++-----
 redis_cluster_arginfo.h        | 145 ++++++++++++++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h | 140 ++++++++++++++++++++++++++++++-
 3 files changed, 323 insertions(+), 22 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 98fda4f4e1..3008d15c0a 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -294,30 +294,50 @@ public function subscribe(array $channels, callable $cb): void;
     public function sunion(string $key, string ...$other_keys): bool|array;
 
     public function sunionstore(string $dst, string $key, string ...$other_keys): int;
+
+    public function time(string|array $key_or_address): bool|array;
+
+    public function ttl(string $key): int;
+
+    public function type(string $key): int;
+
+    public function unsubscribe(array $channels): bool|array;
+
+    public function unlink(string $key, string ...$other_keys): array;
+
+    public function unwatch(): bool;
+
+    public function watch(string $key, string ...$other_keys): bool;
+
+    public function xack(string $key, string $group, array $ids): int;
+
+    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false): string;
+
+    public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): string|array;
+
+    public function xdel(string $key, array $ids): int;
+
+    public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
+
+    public function xinfo(string $operation, string $arg1 = null, string $arg2 = null): mixed;
+
+    public function xlen(string $key): int;
+
+    public function xpending(string $key, string $group, string $start = null, string $end = null, int $count = -1, string $consumer = null): string;
+
+    public function xrange(string $key, string $start, string $end, int $count = -1): bool|array;
+
+    public function xread(array $streams, int $count = -1, int $block = -1): bool|array;
+
+    public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): bool|array;
+
+    public function xrevrange(string $key, string $start, string $end, int $count = -1): bool|array;
+
+    public function xtrim(string $key, int $maxlen, bool $approx = false): int;
 }
 
 /*
     TODO:
-    public function time
-    public function ttl
-    public function type
-    public function unsubscribe
-    public function unlink
-    public function unwatch
-    public function watch
-    public function xack
-    public function xadd
-    public function xclaim
-    public function xdel
-    public function xgroup
-    public function xinfo
-    public function xlen
-    public function xpending
-    public function xrange
-    public function xread
-    public function xreadgroup
-    public function xrevrange
-    public function xtrim
     public function zadd
     public function zcard
     public function zcount
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index ecba63ae48..bb23062a0f 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a8dbdd46b676b84c2d644382b7c4e108a820b12f */
+ * Stub hash: 5d62f0da16946eeff4782ceb22097e563b9864e2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -631,6 +631,109 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_sunionstore arginfo_class_RedisCluster_sdiffstore
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_time, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_ttl arginfo_class_RedisCluster_decr
+
+#define arginfo_class_RedisCluster_type arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_unsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_unlink arginfo_class_RedisCluster_del
+
+#define arginfo_class_RedisCluster_unwatch arginfo_class_RedisCluster_clearlasterror
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_watch, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xack, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xadd, 0, 3, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, maxlen, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xclaim, 0, 6, MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min_iddle, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xdel, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg3, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xpending, 0, 2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xread, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xreadgroup, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "1")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_xrevrange arginfo_class_RedisCluster_xrange
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xtrim, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, maxlen, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -776,6 +879,26 @@ ZEND_METHOD(RedisCluster, strlen);
 ZEND_METHOD(RedisCluster, subscribe);
 ZEND_METHOD(RedisCluster, sunion);
 ZEND_METHOD(RedisCluster, sunionstore);
+ZEND_METHOD(RedisCluster, time);
+ZEND_METHOD(RedisCluster, ttl);
+ZEND_METHOD(RedisCluster, type);
+ZEND_METHOD(RedisCluster, unsubscribe);
+ZEND_METHOD(RedisCluster, unlink);
+ZEND_METHOD(RedisCluster, unwatch);
+ZEND_METHOD(RedisCluster, watch);
+ZEND_METHOD(RedisCluster, xack);
+ZEND_METHOD(RedisCluster, xadd);
+ZEND_METHOD(RedisCluster, xclaim);
+ZEND_METHOD(RedisCluster, xdel);
+ZEND_METHOD(RedisCluster, xgroup);
+ZEND_METHOD(RedisCluster, xinfo);
+ZEND_METHOD(RedisCluster, xlen);
+ZEND_METHOD(RedisCluster, xpending);
+ZEND_METHOD(RedisCluster, xrange);
+ZEND_METHOD(RedisCluster, xread);
+ZEND_METHOD(RedisCluster, xreadgroup);
+ZEND_METHOD(RedisCluster, xrevrange);
+ZEND_METHOD(RedisCluster, xtrim);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -923,5 +1046,25 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, subscribe, arginfo_class_RedisCluster_subscribe, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sunion, arginfo_class_RedisCluster_sunion, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sunionstore, arginfo_class_RedisCluster_sunionstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, time, arginfo_class_RedisCluster_time, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, ttl, arginfo_class_RedisCluster_ttl, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, type, arginfo_class_RedisCluster_type, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, unsubscribe, arginfo_class_RedisCluster_unsubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, unlink, arginfo_class_RedisCluster_unlink, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, unwatch, arginfo_class_RedisCluster_unwatch, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, watch, arginfo_class_RedisCluster_watch, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xack, arginfo_class_RedisCluster_xack, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xadd, arginfo_class_RedisCluster_xadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xlen, arginfo_class_RedisCluster_xlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xpending, arginfo_class_RedisCluster_xpending, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xrange, arginfo_class_RedisCluster_xrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xread, arginfo_class_RedisCluster_xread, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xreadgroup, arginfo_class_RedisCluster_xreadgroup, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xrevrange, arginfo_class_RedisCluster_xrevrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xtrim, arginfo_class_RedisCluster_xtrim, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index b7e9ba53de..3f29c08c72 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a8dbdd46b676b84c2d644382b7c4e108a820b12f */
+ * Stub hash: 5d62f0da16946eeff4782ceb22097e563b9864e2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -543,6 +543,104 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_sunionstore arginfo_class_RedisCluster_sdiffstore
 
+#define arginfo_class_RedisCluster_time arginfo_class_RedisCluster_bgrewriteaof
+
+#define arginfo_class_RedisCluster_ttl arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_type arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_unsubscribe, 0, 0, 1)
+	ZEND_ARG_INFO(0, channels)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_unlink arginfo_class_RedisCluster_del
+
+#define arginfo_class_RedisCluster_unwatch arginfo_class_RedisCluster__masters
+
+#define arginfo_class_RedisCluster_watch arginfo_class_RedisCluster_del
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xack, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, ids)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xadd, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, id)
+	ZEND_ARG_INFO(0, values)
+	ZEND_ARG_INFO(0, maxlen)
+	ZEND_ARG_INFO(0, approx)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xclaim, 0, 0, 6)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, consumer)
+	ZEND_ARG_INFO(0, min_iddle)
+	ZEND_ARG_INFO(0, ids)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xdel, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, ids)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 0, 1)
+	ZEND_ARG_INFO(0, operation)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, arg1)
+	ZEND_ARG_INFO(0, arg2)
+	ZEND_ARG_INFO(0, arg3)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 0, 1)
+	ZEND_ARG_INFO(0, operation)
+	ZEND_ARG_INFO(0, arg1)
+	ZEND_ARG_INFO(0, arg2)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xpending, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, consumer)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xrange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xread, 0, 0, 1)
+	ZEND_ARG_INFO(0, streams)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, block)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xreadgroup, 0, 0, 3)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, consumer)
+	ZEND_ARG_INFO(0, streams)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, block)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_xrevrange arginfo_class_RedisCluster_xrange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xtrim, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, maxlen)
+	ZEND_ARG_INFO(0, approx)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -688,6 +786,26 @@ ZEND_METHOD(RedisCluster, strlen);
 ZEND_METHOD(RedisCluster, subscribe);
 ZEND_METHOD(RedisCluster, sunion);
 ZEND_METHOD(RedisCluster, sunionstore);
+ZEND_METHOD(RedisCluster, time);
+ZEND_METHOD(RedisCluster, ttl);
+ZEND_METHOD(RedisCluster, type);
+ZEND_METHOD(RedisCluster, unsubscribe);
+ZEND_METHOD(RedisCluster, unlink);
+ZEND_METHOD(RedisCluster, unwatch);
+ZEND_METHOD(RedisCluster, watch);
+ZEND_METHOD(RedisCluster, xack);
+ZEND_METHOD(RedisCluster, xadd);
+ZEND_METHOD(RedisCluster, xclaim);
+ZEND_METHOD(RedisCluster, xdel);
+ZEND_METHOD(RedisCluster, xgroup);
+ZEND_METHOD(RedisCluster, xinfo);
+ZEND_METHOD(RedisCluster, xlen);
+ZEND_METHOD(RedisCluster, xpending);
+ZEND_METHOD(RedisCluster, xrange);
+ZEND_METHOD(RedisCluster, xread);
+ZEND_METHOD(RedisCluster, xreadgroup);
+ZEND_METHOD(RedisCluster, xrevrange);
+ZEND_METHOD(RedisCluster, xtrim);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -835,5 +953,25 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, subscribe, arginfo_class_RedisCluster_subscribe, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sunion, arginfo_class_RedisCluster_sunion, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sunionstore, arginfo_class_RedisCluster_sunionstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, time, arginfo_class_RedisCluster_time, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, ttl, arginfo_class_RedisCluster_ttl, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, type, arginfo_class_RedisCluster_type, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, unsubscribe, arginfo_class_RedisCluster_unsubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, unlink, arginfo_class_RedisCluster_unlink, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, unwatch, arginfo_class_RedisCluster_unwatch, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, watch, arginfo_class_RedisCluster_watch, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xack, arginfo_class_RedisCluster_xack, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xadd, arginfo_class_RedisCluster_xadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xlen, arginfo_class_RedisCluster_xlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xpending, arginfo_class_RedisCluster_xpending, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xrange, arginfo_class_RedisCluster_xrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xread, arginfo_class_RedisCluster_xread, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xreadgroup, arginfo_class_RedisCluster_xreadgroup, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xrevrange, arginfo_class_RedisCluster_xrevrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, xtrim, arginfo_class_RedisCluster_xtrim, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 0b9a1d163ff4896c990399708a987c1d316ef570 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 26 Aug 2021 09:50:23 +0300
Subject: [PATCH 1483/1986] Add stub-based arginfo for Redis Cluster methods z

---
 redis_cluster.stub.php         |  73 ++++++++++-------
 redis_cluster_arginfo.h        | 139 ++++++++++++++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h | 125 ++++++++++++++++++++++++++++-
 3 files changed, 308 insertions(+), 29 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 3008d15c0a..304b37a640 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -334,31 +334,50 @@ public function xreadgroup(string $group, string $consumer, array $streams, int
     public function xrevrange(string $key, string $start, string $end, int $count = -1): bool|array;
 
     public function xtrim(string $key, int $maxlen, bool $approx = false): int;
-}
 
-/*
-    TODO:
-    public function zadd
-    public function zcard
-    public function zcount
-    public function zincrby
-    public function zinterstore
-    public function zlexcount
-    public function zpopmax
-    public function zpopmin
-    public function zrange
-    public function zrangebylex
-    public function zrangebyscore
-    public function zrank
-    public function zrem
-    public function zremrangebylex
-    public function zremrangebyrank
-    public function zremrangebyscore
-    public function zrevrange
-    public function zrevrangebylex
-    public function zrevrangebyscore
-    public function zrevrank
-    public function zscan
-    public function zscore
-    public function zunionstore
-*/
+    public function zadd(string $key, float $score, string $member, mixed ...$extra_args): int;
+
+    public function zcard(string $key): int;
+
+    public function zcount(string $key, string $start, string $end): int;
+
+    public function zincrby(string $key, float $value, string $member): float;
+
+    public function zinterstore(string $key, array $keys, array $weights = null, string $aggregate = null): int;
+
+    public function zlexcount(string $key, string $min, string $max): int;
+
+    public function zpopmax(string $key, int $value = null): bool|array;
+
+    public function zpopmin(string $key, int $value = null): bool|array;
+
+    public function zrange(string $key, int $start, int $end, array $options = null): bool|array;
+
+    public function zrangebylex(string $key, int $start, int $end, array $options = null): bool|array;
+
+    public function zrangebyscore(string $key, int $start, int $end, array $options = null): bool|array;
+
+    public function zrank(string $key, mixed $member): int;
+
+    public function zrem(string $key, string $value, string ...$other_values): int;
+
+    public function zremrangebylex(string $key, string $min, string $max): int;
+
+    public function zremrangebyrank(string $key, string $min, string $max): int;
+
+    public function zremrangebyscore(string $key, string $min, string $max): int;
+
+    public function zrevrange(string $key, string $min, string $max, array $options = null): bool|array;
+
+    public function zrevrangebylex(string $key, string $min, string $max, array $options = null): bool|array;
+
+    public function zrevrangebyscore(string $key, string $min, string $max, array $options = null): bool|array;
+
+    public function zrevrank(string $key, mixed $member): int;
+
+    public function zscan(string $key, int $iterator, ?string $pattern = null, int $count = 0): bool|array;
+
+    public function zscore(string $key): float;
+
+    public function zunionstore(string $key, array $keys, array $weights = null, string $aggregate = null): int;
+}
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index bb23062a0f..9f1258b05c 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5d62f0da16946eeff4782ceb22097e563b9864e2 */
+ * Stub hash: 858c52a2d14a58a7917f4cf6ff4129116ead0d98 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -734,6 +734,97 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xtrim, 0, 2,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zadd, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, score, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zcard arginfo_class_RedisCluster_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zcount, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zincrby, 0, 3, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zinterstore, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zlexcount, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zpopmax, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zpopmin arginfo_class_RedisCluster_zpopmax
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zrangebylex arginfo_class_RedisCluster_zrange
+
+#define arginfo_class_RedisCluster_zrangebyscore arginfo_class_RedisCluster_zrange
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zrank, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zrem arginfo_class_RedisCluster_srem
+
+#define arginfo_class_RedisCluster_zremrangebylex arginfo_class_RedisCluster_zlexcount
+
+#define arginfo_class_RedisCluster_zremrangebyrank arginfo_class_RedisCluster_zlexcount
+
+#define arginfo_class_RedisCluster_zremrangebyscore arginfo_class_RedisCluster_zlexcount
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zrevrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zrevrangebylex arginfo_class_RedisCluster_zrevrange
+
+#define arginfo_class_RedisCluster_zrevrangebyscore arginfo_class_RedisCluster_zrevrange
+
+#define arginfo_class_RedisCluster_zrevrank arginfo_class_RedisCluster_zrank
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zscore, 0, 1, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zunionstore arginfo_class_RedisCluster_zinterstore
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -899,6 +990,29 @@ ZEND_METHOD(RedisCluster, xread);
 ZEND_METHOD(RedisCluster, xreadgroup);
 ZEND_METHOD(RedisCluster, xrevrange);
 ZEND_METHOD(RedisCluster, xtrim);
+ZEND_METHOD(RedisCluster, zadd);
+ZEND_METHOD(RedisCluster, zcard);
+ZEND_METHOD(RedisCluster, zcount);
+ZEND_METHOD(RedisCluster, zincrby);
+ZEND_METHOD(RedisCluster, zinterstore);
+ZEND_METHOD(RedisCluster, zlexcount);
+ZEND_METHOD(RedisCluster, zpopmax);
+ZEND_METHOD(RedisCluster, zpopmin);
+ZEND_METHOD(RedisCluster, zrange);
+ZEND_METHOD(RedisCluster, zrangebylex);
+ZEND_METHOD(RedisCluster, zrangebyscore);
+ZEND_METHOD(RedisCluster, zrank);
+ZEND_METHOD(RedisCluster, zrem);
+ZEND_METHOD(RedisCluster, zremrangebylex);
+ZEND_METHOD(RedisCluster, zremrangebyrank);
+ZEND_METHOD(RedisCluster, zremrangebyscore);
+ZEND_METHOD(RedisCluster, zrevrange);
+ZEND_METHOD(RedisCluster, zrevrangebylex);
+ZEND_METHOD(RedisCluster, zrevrangebyscore);
+ZEND_METHOD(RedisCluster, zrevrank);
+ZEND_METHOD(RedisCluster, zscan);
+ZEND_METHOD(RedisCluster, zscore);
+ZEND_METHOD(RedisCluster, zunionstore);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -1066,5 +1180,28 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, xreadgroup, arginfo_class_RedisCluster_xreadgroup, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, xrevrange, arginfo_class_RedisCluster_xrevrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, xtrim, arginfo_class_RedisCluster_xtrim, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zadd, arginfo_class_RedisCluster_zadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zcard, arginfo_class_RedisCluster_zcard, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zcount, arginfo_class_RedisCluster_zcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zincrby, arginfo_class_RedisCluster_zincrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zinterstore, arginfo_class_RedisCluster_zinterstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zlexcount, arginfo_class_RedisCluster_zlexcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrem, arginfo_class_RedisCluster_zrem, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zremrangebylex, arginfo_class_RedisCluster_zremrangebylex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zremrangebyrank, arginfo_class_RedisCluster_zremrangebyrank, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zremrangebyscore, arginfo_class_RedisCluster_zremrangebyscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrevrange, arginfo_class_RedisCluster_zrevrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrevrangebylex, arginfo_class_RedisCluster_zrevrangebylex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrevrangebyscore, arginfo_class_RedisCluster_zrevrangebyscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrevrank, arginfo_class_RedisCluster_zrevrank, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zscan, arginfo_class_RedisCluster_zscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zscore, arginfo_class_RedisCluster_zscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zunionstore, arginfo_class_RedisCluster_zunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 3f29c08c72..8076f1a327 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5d62f0da16946eeff4782ceb22097e563b9864e2 */
+ * Stub hash: 858c52a2d14a58a7917f4cf6ff4129116ead0d98 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -641,6 +641,83 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xtrim, 0, 0, 2)
 	ZEND_ARG_INFO(0, approx)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zadd, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, score)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_VARIADIC_INFO(0, extra_args)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zcard arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_zcount arginfo_class_RedisCluster_getrange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zincrby, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, member)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zinterstore, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, weights)
+	ZEND_ARG_INFO(0, aggregate)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zlexcount, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, min)
+	ZEND_ARG_INFO(0, max)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zpopmax, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zpopmin arginfo_class_RedisCluster_zpopmax
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zrangebylex arginfo_class_RedisCluster_zrange
+
+#define arginfo_class_RedisCluster_zrangebyscore arginfo_class_RedisCluster_zrange
+
+#define arginfo_class_RedisCluster_zrank arginfo_class_RedisCluster_hexists
+
+#define arginfo_class_RedisCluster_zrem arginfo_class_RedisCluster_lpush
+
+#define arginfo_class_RedisCluster_zremrangebylex arginfo_class_RedisCluster_zlexcount
+
+#define arginfo_class_RedisCluster_zremrangebyrank arginfo_class_RedisCluster_zlexcount
+
+#define arginfo_class_RedisCluster_zremrangebyscore arginfo_class_RedisCluster_zlexcount
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrevrange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, min)
+	ZEND_ARG_INFO(0, max)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_zrevrangebylex arginfo_class_RedisCluster_zrevrange
+
+#define arginfo_class_RedisCluster_zrevrangebyscore arginfo_class_RedisCluster_zrevrange
+
+#define arginfo_class_RedisCluster_zrevrank arginfo_class_RedisCluster_hexists
+
+#define arginfo_class_RedisCluster_zscan arginfo_class_RedisCluster_hscan
+
+#define arginfo_class_RedisCluster_zscore arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_zunionstore arginfo_class_RedisCluster_zinterstore
+
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _masters);
@@ -806,6 +883,29 @@ ZEND_METHOD(RedisCluster, xread);
 ZEND_METHOD(RedisCluster, xreadgroup);
 ZEND_METHOD(RedisCluster, xrevrange);
 ZEND_METHOD(RedisCluster, xtrim);
+ZEND_METHOD(RedisCluster, zadd);
+ZEND_METHOD(RedisCluster, zcard);
+ZEND_METHOD(RedisCluster, zcount);
+ZEND_METHOD(RedisCluster, zincrby);
+ZEND_METHOD(RedisCluster, zinterstore);
+ZEND_METHOD(RedisCluster, zlexcount);
+ZEND_METHOD(RedisCluster, zpopmax);
+ZEND_METHOD(RedisCluster, zpopmin);
+ZEND_METHOD(RedisCluster, zrange);
+ZEND_METHOD(RedisCluster, zrangebylex);
+ZEND_METHOD(RedisCluster, zrangebyscore);
+ZEND_METHOD(RedisCluster, zrank);
+ZEND_METHOD(RedisCluster, zrem);
+ZEND_METHOD(RedisCluster, zremrangebylex);
+ZEND_METHOD(RedisCluster, zremrangebyrank);
+ZEND_METHOD(RedisCluster, zremrangebyscore);
+ZEND_METHOD(RedisCluster, zrevrange);
+ZEND_METHOD(RedisCluster, zrevrangebylex);
+ZEND_METHOD(RedisCluster, zrevrangebyscore);
+ZEND_METHOD(RedisCluster, zrevrank);
+ZEND_METHOD(RedisCluster, zscan);
+ZEND_METHOD(RedisCluster, zscore);
+ZEND_METHOD(RedisCluster, zunionstore);
 
 
 static const zend_function_entry class_RedisCluster_methods[] = {
@@ -973,5 +1073,28 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, xreadgroup, arginfo_class_RedisCluster_xreadgroup, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, xrevrange, arginfo_class_RedisCluster_xrevrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, xtrim, arginfo_class_RedisCluster_xtrim, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zadd, arginfo_class_RedisCluster_zadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zcard, arginfo_class_RedisCluster_zcard, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zcount, arginfo_class_RedisCluster_zcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zincrby, arginfo_class_RedisCluster_zincrby, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zinterstore, arginfo_class_RedisCluster_zinterstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zlexcount, arginfo_class_RedisCluster_zlexcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrem, arginfo_class_RedisCluster_zrem, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zremrangebylex, arginfo_class_RedisCluster_zremrangebylex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zremrangebyrank, arginfo_class_RedisCluster_zremrangebyrank, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zremrangebyscore, arginfo_class_RedisCluster_zremrangebyscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrevrange, arginfo_class_RedisCluster_zrevrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrevrangebylex, arginfo_class_RedisCluster_zrevrangebylex, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrevrangebyscore, arginfo_class_RedisCluster_zrevrangebyscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrevrank, arginfo_class_RedisCluster_zrevrank, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zscan, arginfo_class_RedisCluster_zscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zscore, arginfo_class_RedisCluster_zscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zunionstore, arginfo_class_RedisCluster_zunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 7cc15ca2093ffb72968cc7aec550ef98fa0f94ec Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 2 Sep 2021 14:02:44 +0300
Subject: [PATCH 1484/1986] [WIP] Add stub-based arginfo for Redis

---
 common.h               | 428 +----------------------------------------
 php_redis.h            | 242 -----------------------
 redis.c                | 410 ++-------------------------------------
 redis.stub.php         | 180 ++++++++++++++++-
 redis_arginfo.h        |   7 +-
 redis_legacy_arginfo.h |   7 +-
 6 files changed, 193 insertions(+), 1081 deletions(-)

diff --git a/common.h b/common.h
index 2b9ec72308..40266827ce 100644
--- a/common.h
+++ b/common.h
@@ -334,432 +334,6 @@ typedef struct {
     zend_object std;
 } redis_object;
 
-/** Argument info for any function expecting 0 args */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_value, 0, 0, 1)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_value, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_expire_value, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, expire)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_newkey, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, newkey)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_pairs, 0, 0, 1)
-    ZEND_ARG_ARRAY_INFO(0, pairs, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_nkeys, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_VARIADIC_INFO(0, other_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_dst_nkeys, 0, 0, 2)
-    ZEND_ARG_INFO(0, dst)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_VARIADIC_INFO(0, other_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_min_max, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, min)
-    ZEND_ARG_INFO(0, max)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, member)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_member_value, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, member)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_members, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, member)
-    ZEND_ARG_VARIADIC_INFO(0, other_members)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_timestamp, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, timestamp)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, offset)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_offset_value, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, offset)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_swapdb, 0, 0, 2)
-    ZEND_ARG_INFO(0, srcdb)
-    ZEND_ARG_INFO(0, dstdb)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_key_start_end, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, start)
-    ZEND_ARG_INFO(0, end)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_echo, 0, 0, 1)
-    ZEND_ARG_INFO(0, msg)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_expire, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, timeout)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, value)
-    ZEND_ARG_INFO(0, opts)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_lset, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, index)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_blrpop, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, timeout_or_key)
-    ZEND_ARG_VARIADIC_INFO(0, extra_args)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_linsert, 0, 0, 4)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, position)
-    ZEND_ARG_INFO(0, pivot)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_lindex, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, index)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_brpoplpush, 0, 0, 3)
-    ZEND_ARG_INFO(0, src)
-    ZEND_ARG_INFO(0, dst)
-    ZEND_ARG_INFO(0, timeout)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_rpoplpush, 0, 0, 2)
-    ZEND_ARG_INFO(0, src)
-    ZEND_ARG_INFO(0, dst)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_sadd_array, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_srand_member, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, count)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zadd, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, score)
-    ZEND_ARG_INFO(0, value)
-    ZEND_ARG_VARIADIC_INFO(0, extra_args)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zincrby, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, value)
-    ZEND_ARG_INFO(0, member)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_hmget, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_ARRAY_INFO(0, keys, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_hmset, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_ARRAY_INFO(0, pairs, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_bitop, 0, 0, 3)
-    ZEND_ARG_INFO(0, operation)
-    ZEND_ARG_INFO(0, ret_key)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_VARIADIC_INFO(0, other_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_bitpos, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, bit)
-    ZEND_ARG_INFO(0, start)
-    ZEND_ARG_INFO(0, end)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_ltrim, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, start)
-    ZEND_ARG_INFO(0, stop)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_publish, 0, 0, 2)
-    ZEND_ARG_INFO(0, channel)
-    ZEND_ARG_INFO(0, message)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_pfadd, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_ARRAY_INFO(0, elements, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_pfmerge, 0, 0, 2)
-    ZEND_ARG_INFO(0, dstkey)
-    ZEND_ARG_ARRAY_INFO(0, keys, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_restore, 0, 0, 3)
-    ZEND_ARG_INFO(0, ttl)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_smove, 0, 0, 3)
-    ZEND_ARG_INFO(0, src)
-    ZEND_ARG_INFO(0, dst)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zrange, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, start)
-    ZEND_ARG_INFO(0, end)
-    ZEND_ARG_INFO(0, scores)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zrangebyscore, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, start)
-    ZEND_ARG_INFO(0, end)
-    ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zrangebylex, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, min)
-    ZEND_ARG_INFO(0, max)
-    ZEND_ARG_INFO(0, offset)
-    ZEND_ARG_INFO(0, limit)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zstore, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_ARRAY_INFO(0, keys, 0)
-    ZEND_ARG_ARRAY_INFO(0, weights, 1)
-    ZEND_ARG_INFO(0, aggregate)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_sort, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_object, 0, 0, 2)
-    ZEND_ARG_INFO(0, field)
-    ZEND_ARG_INFO(0, key)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_subscribe, 0, 0, 2)
-    ZEND_ARG_ARRAY_INFO(0, channels, 0)
-    ZEND_ARG_INFO(0, callback)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_psubscribe, 0, 0, 2)
-    ZEND_ARG_ARRAY_INFO(0, patterns, 0)
-    ZEND_ARG_INFO(0, callback)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_unsubscribe, 0, 0, 1)
-    ZEND_ARG_INFO(0, channel)
-    ZEND_ARG_VARIADIC_INFO(0, other_channels)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_punsubscribe, 0, 0, 1)
-    ZEND_ARG_INFO(0, pattern)
-    ZEND_ARG_VARIADIC_INFO(0, other_patterns)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_eval, 0, 0, 1)
-    ZEND_ARG_INFO(0, script)
-    ZEND_ARG_INFO(0, args)
-    ZEND_ARG_INFO(0, num_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_evalsha, 0, 0, 1)
-    ZEND_ARG_INFO(0, script_sha)
-    ZEND_ARG_INFO(0, args)
-    ZEND_ARG_INFO(0, num_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_getoption, 0, 0, 1)
-    ZEND_ARG_INFO(0, option)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_setoption, 0, 0, 2)
-    ZEND_ARG_INFO(0, option)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_watch, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_VARIADIC_INFO(0, other_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_command, 0, 0, 0)
-    ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_rawcommand, 0, 0, 1)
-    ZEND_ARG_INFO(0, cmd)
-    ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_geoadd, 0, 0, 4)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, lng)
-    ZEND_ARG_INFO(0, lat)
-    ZEND_ARG_INFO(0, member)
-    ZEND_ARG_VARIADIC_INFO(0, other_triples)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_geodist, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, src)
-    ZEND_ARG_INFO(0, dst)
-    ZEND_ARG_INFO(0, unit)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_georadius, 0, 0, 5)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, lng)
-    ZEND_ARG_INFO(0, lan)
-    ZEND_ARG_INFO(0, radius)
-    ZEND_ARG_INFO(0, unit)
-    ZEND_ARG_ARRAY_INFO(0, opts, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_georadiusbymember, 0, 0, 4)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, member)
-    ZEND_ARG_INFO(0, radius)
-    ZEND_ARG_INFO(0, unit)
-    ZEND_ARG_ARRAY_INFO(0, opts, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xadd, 0, 0, 3)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(0, str_id)
-    ZEND_ARG_ARRAY_INFO(0, arr_fields, 0)
-    ZEND_ARG_INFO(0, i_maxlen)
-    ZEND_ARG_INFO(0, boo_approximate)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xpending, 0, 0, 2)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(0, str_group)
-    ZEND_ARG_INFO(0, str_start)
-    ZEND_ARG_INFO(0, str_end)
-    ZEND_ARG_INFO(0, i_count)
-    ZEND_ARG_INFO(0, str_consumer)
-    ZEND_ARG_INFO(0, idle)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xrange, 0, 0, 3)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(0, str_start)
-    ZEND_ARG_INFO(0, str_end)
-    ZEND_ARG_INFO(0, i_count)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xread, 0, 0, 1)
-    ZEND_ARG_ARRAY_INFO(0, arr_streams, 0)
-    ZEND_ARG_INFO(0, i_count)
-    ZEND_ARG_INFO(0, i_block)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xreadgroup, 0, 0, 3)
-    ZEND_ARG_INFO(0, str_group)
-    ZEND_ARG_INFO(0, str_consumer)
-    ZEND_ARG_ARRAY_INFO(0, arr_streams, 0)
-    ZEND_ARG_INFO(0, i_count)
-    ZEND_ARG_INFO(0, i_block)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xack, 0, 0, 3)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(0, str_group)
-    ZEND_ARG_ARRAY_INFO(0, arr_ids, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xclaim, 0, 0, 5)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(0, str_group)
-    ZEND_ARG_INFO(0, str_consumer)
-    ZEND_ARG_INFO(0, i_min_idle)
-    ZEND_ARG_ARRAY_INFO(0, arr_ids, 0)
-    ZEND_ARG_ARRAY_INFO(0, arr_opts, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xgroup, 0, 0, 1)
-    ZEND_ARG_INFO(0, str_operation)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(0, str_arg1)
-    ZEND_ARG_INFO(0, str_arg2)
-    ZEND_ARG_INFO(0, str_arg3)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xinfo, 0, 0, 1)
-    ZEND_ARG_INFO(0, str_cmd)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(0, str_group)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xtrim, 0, 0, 2)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(0, i_maxlen)
-    ZEND_ARG_INFO(0, boo_approximate)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_xdel, 0, 0, 2)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_ARRAY_INFO(0, arr_ids, 0)
-ZEND_END_ARG_INFO()
+extern const zend_function_entry *redis_get_methods(void);
 
 #endif
diff --git a/php_redis.h b/php_redis.h
index a0c5de40fb..2c9ec50fab 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -25,248 +25,6 @@
 /* phpredis version */
 #define PHP_REDIS_VERSION "5.3.2"
 
-PHP_METHOD(Redis, __construct);
-PHP_METHOD(Redis, __destruct);
-PHP_METHOD(Redis, acl);
-PHP_METHOD(Redis, append);
-PHP_METHOD(Redis, auth);
-PHP_METHOD(Redis, bgSave);
-PHP_METHOD(Redis, bgrewriteaof);
-PHP_METHOD(Redis, bitcount);
-PHP_METHOD(Redis, bitop);
-PHP_METHOD(Redis, bitpos);
-PHP_METHOD(Redis, blPop);
-PHP_METHOD(Redis, brPop);
-PHP_METHOD(Redis, bzPopMax);
-PHP_METHOD(Redis, bzPopMin);
-PHP_METHOD(Redis, close);
-PHP_METHOD(Redis, connect);
-PHP_METHOD(Redis, copy);
-PHP_METHOD(Redis, dbSize);
-PHP_METHOD(Redis, decr);
-PHP_METHOD(Redis, decrBy);
-PHP_METHOD(Redis, del);
-PHP_METHOD(Redis, echo);
-PHP_METHOD(Redis, exists);
-PHP_METHOD(Redis, expire);
-PHP_METHOD(Redis, expireAt);
-PHP_METHOD(Redis, flushAll);
-PHP_METHOD(Redis, flushDB);
-PHP_METHOD(Redis, get);
-PHP_METHOD(Redis, getBit);
-PHP_METHOD(Redis, getRange);
-PHP_METHOD(Redis, getSet);
-PHP_METHOD(Redis, incr);
-PHP_METHOD(Redis, incrBy);
-PHP_METHOD(Redis, incrByFloat);
-PHP_METHOD(Redis, info);
-PHP_METHOD(Redis, keys);
-PHP_METHOD(Redis, lInsert);
-PHP_METHOD(Redis, lLen);
-PHP_METHOD(Redis, lMove);
-PHP_METHOD(Redis, lPop);
-PHP_METHOD(Redis, lPush);
-PHP_METHOD(Redis, lPushx);
-PHP_METHOD(Redis, lSet);
-PHP_METHOD(Redis, lastSave);
-PHP_METHOD(Redis, lindex);
-PHP_METHOD(Redis, lrange);
-PHP_METHOD(Redis, lrem);
-PHP_METHOD(Redis, ltrim);
-PHP_METHOD(Redis, mget);
-PHP_METHOD(Redis, move);
-PHP_METHOD(Redis, object);
-PHP_METHOD(Redis, pconnect);
-PHP_METHOD(Redis, persist);
-PHP_METHOD(Redis, pexpire);
-PHP_METHOD(Redis, pexpireAt);
-PHP_METHOD(Redis, ping);
-PHP_METHOD(Redis, psetex);
-PHP_METHOD(Redis, pttl);
-PHP_METHOD(Redis, rPop);
-PHP_METHOD(Redis, rPush);
-PHP_METHOD(Redis, rPushx);
-PHP_METHOD(Redis, randomKey);
-PHP_METHOD(Redis, rename);
-PHP_METHOD(Redis, renameNx);
-PHP_METHOD(Redis, sAdd);
-PHP_METHOD(Redis, sAddArray);
-PHP_METHOD(Redis, sDiff);
-PHP_METHOD(Redis, sDiffStore);
-PHP_METHOD(Redis, sInter);
-PHP_METHOD(Redis, sInterStore);
-PHP_METHOD(Redis, sMembers);
-PHP_METHOD(Redis, sMisMember);
-PHP_METHOD(Redis, sMove);
-PHP_METHOD(Redis, sPop);
-PHP_METHOD(Redis, sRandMember);
-PHP_METHOD(Redis, sUnion);
-PHP_METHOD(Redis, sUnionStore);
-PHP_METHOD(Redis, save);
-PHP_METHOD(Redis, scard);
-PHP_METHOD(Redis, select);
-PHP_METHOD(Redis, set);
-PHP_METHOD(Redis, setBit);
-PHP_METHOD(Redis, setRange);
-PHP_METHOD(Redis, setex);
-PHP_METHOD(Redis, setnx);
-PHP_METHOD(Redis, sismember);
-PHP_METHOD(Redis, slaveof);
-PHP_METHOD(Redis, sort);
-PHP_METHOD(Redis, sortAsc);
-PHP_METHOD(Redis, sortAscAlpha);
-PHP_METHOD(Redis, sortDesc);
-PHP_METHOD(Redis, sortDescAlpha);
-PHP_METHOD(Redis, srem);
-PHP_METHOD(Redis, strlen);
-PHP_METHOD(Redis, swapdb);
-PHP_METHOD(Redis, ttl);
-PHP_METHOD(Redis, type);
-PHP_METHOD(Redis, unlink);
-PHP_METHOD(Redis, zAdd);
-PHP_METHOD(Redis, zCard);
-PHP_METHOD(Redis, zCount);
-PHP_METHOD(Redis, zIncrBy);
-PHP_METHOD(Redis, zLexCount);
-PHP_METHOD(Redis, zMscore);
-PHP_METHOD(Redis, zPopMax);
-PHP_METHOD(Redis, zPopMin);
-PHP_METHOD(Redis, zRange);
-PHP_METHOD(Redis, zRangeByLex);
-PHP_METHOD(Redis, zRangeByScore);
-PHP_METHOD(Redis, zRank);
-PHP_METHOD(Redis, zRem);
-PHP_METHOD(Redis, zRemRangeByLex);
-PHP_METHOD(Redis, zRemRangeByRank);
-PHP_METHOD(Redis, zRemRangeByScore);
-PHP_METHOD(Redis, zRevRange);
-PHP_METHOD(Redis, zRevRangeByLex);
-PHP_METHOD(Redis, zRevRangeByScore);
-PHP_METHOD(Redis, zRevRank);
-PHP_METHOD(Redis, zScore);
-PHP_METHOD(Redis, zdiff);
-PHP_METHOD(Redis, zdiffstore);
-PHP_METHOD(Redis, zinter);
-PHP_METHOD(Redis, zinterstore);
-PHP_METHOD(Redis, zunion);
-PHP_METHOD(Redis, zunionstore);
-
-PHP_METHOD(Redis, eval);
-PHP_METHOD(Redis, evalsha);
-PHP_METHOD(Redis, script);
-PHP_METHOD(Redis, debug);
-PHP_METHOD(Redis, dump);
-PHP_METHOD(Redis, restore);
-PHP_METHOD(Redis, migrate);
-
-PHP_METHOD(Redis, time);
-PHP_METHOD(Redis, role);
-
-PHP_METHOD(Redis, getLastError);
-PHP_METHOD(Redis, clearLastError);
-PHP_METHOD(Redis, _prefix);
-PHP_METHOD(Redis, _pack);
-PHP_METHOD(Redis, _unpack);
-
-PHP_METHOD(Redis, _serialize);
-PHP_METHOD(Redis, _unserialize);
-
-PHP_METHOD(Redis, _compress);
-PHP_METHOD(Redis, _uncompress);
-
-PHP_METHOD(Redis, mset);
-PHP_METHOD(Redis, msetnx);
-PHP_METHOD(Redis, rpoplpush);
-PHP_METHOD(Redis, brpoplpush);
-
-PHP_METHOD(Redis, hGet);
-PHP_METHOD(Redis, hSet);
-PHP_METHOD(Redis, hSetNx);
-PHP_METHOD(Redis, hDel);
-PHP_METHOD(Redis, hLen);
-PHP_METHOD(Redis, hKeys);
-PHP_METHOD(Redis, hVals);
-PHP_METHOD(Redis, hGetAll);
-PHP_METHOD(Redis, hExists);
-PHP_METHOD(Redis, hIncrBy);
-PHP_METHOD(Redis, hIncrByFloat);
-PHP_METHOD(Redis, hMset);
-PHP_METHOD(Redis, hMget);
-PHP_METHOD(Redis, hStrLen);
-
-PHP_METHOD(Redis, multi);
-PHP_METHOD(Redis, discard);
-PHP_METHOD(Redis, exec);
-PHP_METHOD(Redis, watch);
-PHP_METHOD(Redis, unwatch);
-
-PHP_METHOD(Redis, pipeline);
-
-PHP_METHOD(Redis, publish);
-PHP_METHOD(Redis, subscribe);
-PHP_METHOD(Redis, psubscribe);
-PHP_METHOD(Redis, unsubscribe);
-PHP_METHOD(Redis, punsubscribe);
-
-PHP_METHOD(Redis, getOption);
-PHP_METHOD(Redis, setOption);
-
-PHP_METHOD(Redis, config);
-PHP_METHOD(Redis, slowlog);
-PHP_METHOD(Redis, wait);
-PHP_METHOD(Redis, pubsub);
-
-/* Geoadd and friends */
-PHP_METHOD(Redis, geoadd);
-PHP_METHOD(Redis, geohash);
-PHP_METHOD(Redis, geopos);
-PHP_METHOD(Redis, geodist);
-PHP_METHOD(Redis, georadius);
-PHP_METHOD(Redis, georadius_ro);
-PHP_METHOD(Redis, georadiusbymember);
-PHP_METHOD(Redis, georadiusbymember_ro);
-
-PHP_METHOD(Redis, client);
-PHP_METHOD(Redis, command);
-PHP_METHOD(Redis, rawcommand);
-
-/* SCAN and friends  */
-PHP_METHOD(Redis, scan);
-PHP_METHOD(Redis, hscan);
-PHP_METHOD(Redis, sscan);
-PHP_METHOD(Redis, zscan);
-
-/* HyperLogLog commands */
-PHP_METHOD(Redis, pfadd);
-PHP_METHOD(Redis, pfcount);
-PHP_METHOD(Redis, pfmerge);
-
-/* STREAMS */
-PHP_METHOD(Redis, xack);
-PHP_METHOD(Redis, xadd);
-PHP_METHOD(Redis, xclaim);
-PHP_METHOD(Redis, xdel);
-PHP_METHOD(Redis, xgroup);
-PHP_METHOD(Redis, xinfo);
-PHP_METHOD(Redis, xlen);
-PHP_METHOD(Redis, xpending);
-PHP_METHOD(Redis, xrange);
-PHP_METHOD(Redis, xread);
-PHP_METHOD(Redis, xreadgroup);
-PHP_METHOD(Redis, xrevrange);
-PHP_METHOD(Redis, xtrim);
-
-/* Reflection */
-PHP_METHOD(Redis, getHost);
-PHP_METHOD(Redis, getPort);
-PHP_METHOD(Redis, getDBNum);
-PHP_METHOD(Redis, getTimeout);
-PHP_METHOD(Redis, getReadTimeout);
-PHP_METHOD(Redis, isConnected);
-PHP_METHOD(Redis, getPersistentID);
-PHP_METHOD(Redis, getAuth);
-PHP_METHOD(Redis, getMode);
-
 /* For convenience we store the salt as a printable hex string which requires 2
  * characters per byte + 1 for the NULL terminator */
 #define REDIS_SALT_BYTES 32
diff --git a/redis.c b/redis.c
index f560b28e0b..957aace836 100644
--- a/redis.c
+++ b/redis.c
@@ -60,6 +60,17 @@ extern zend_class_entry *redis_sentinel_ce;
 zend_class_entry *redis_ce;
 zend_class_entry *redis_exception_ce;
 
+#if PHP_VERSION_ID < 80000
+#include "redis_legacy_arginfo.h"
+#else
+#include "redis_arginfo.h"
+#endif
+
+extern const zend_function_entry *redis_get_methods(void)
+{
+    return class_Redis_methods;
+}
+
 extern int le_cluster_slot_cache;
 int le_redis_pconnect;
 
@@ -102,401 +113,6 @@ PHP_INI_BEGIN()
     PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL)
 PHP_INI_END()
 
-/** {{{ Argument info for commands in redis 1.0 */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 1)
-    ZEND_ARG_INFO(0, host)
-    ZEND_ARG_INFO(0, port)
-    ZEND_ARG_INFO(0, timeout)
-    ZEND_ARG_INFO(0, retry_interval)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zdiff, 0, 0, 1)
-    ZEND_ARG_ARRAY_INFO(0, keys, 0)
-    ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zinterunion, 0, 0, 1)
-    ZEND_ARG_ARRAY_INFO(0, keys, 0)
-    ZEND_ARG_ARRAY_INFO(0, weights, 1)
-    ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_zdiffstore, 0, 0, 2)
-    ZEND_ARG_INFO(0, destination)
-    ZEND_ARG_ARRAY_INFO(0, keys, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0)
-    ZEND_ARG_INFO(0, option)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_multi, 0, 0, 0)
-    ZEND_ARG_INFO(0, mode)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1)
-    ZEND_ARG_INFO(0, cmd)
-    ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2)
-    ZEND_ARG_INFO(0, cmd)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_copy, 0, 0, 2)
-    ZEND_ARG_INFO(0, source)
-    ZEND_ARG_INFO(0, destination)
-    ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0)
-    ZEND_ARG_INFO(0, async)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1)
-    ZEND_ARG_INFO(0, cmd)
-    ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1)
-    ZEND_ARG_INFO(0, arg)
-    ZEND_ARG_INFO(0, option)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 1)
-    ZEND_ARG_INFO(0, host)
-    ZEND_ARG_INFO(0, port)
-    ZEND_ARG_INFO(0, timeout)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1)
-    ZEND_ARG_ARRAY_INFO(0, keys, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_lmove, 0, 0, 4)
-    ZEND_ARG_INFO(0, source)
-    ZEND_ARG_INFO(0, destination)
-    ZEND_ARG_INFO(0, wherefrom)
-    ZEND_ARG_INFO(0, whereto)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_exists, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_VARIADIC_INFO(0, other_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_VARIADIC_INFO(0, other_keys)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1)
-    ZEND_ARG_INFO(0, pattern)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_generic_sort, 0, 0, 1)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, pattern)
-    ZEND_ARG_INFO(0, get)
-    ZEND_ARG_INFO(0, start)
-    ZEND_ARG_INFO(0, end)
-    ZEND_ARG_INFO(0, getList)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_lrem, 0, 0, 3)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, value)
-    ZEND_ARG_INFO(0, count)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_auth, 0, 0, 1)
-    ZEND_ARG_INFO(0, auth)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1)
-    ZEND_ARG_INFO(0, dbindex)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_move, 0, 0, 2)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, dbindex)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_slaveof, 0, 0, 0)
-    ZEND_ARG_INFO(0, host)
-    ZEND_ARG_INFO(0, port)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_acl, 0, 0, 1)
-    ZEND_ARG_INFO(0, subcmd)
-    ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-/* }}} */
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_migrate, 0, 0, 5)
-    ZEND_ARG_INFO(0, host)
-    ZEND_ARG_INFO(0, port)
-    ZEND_ARG_INFO(0, key)
-    ZEND_ARG_INFO(0, db)
-    ZEND_ARG_INFO(0, timeout)
-    ZEND_ARG_INFO(0, copy)
-    ZEND_ARG_INFO(0, replace)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_wait, 0, 0, 2)
-    ZEND_ARG_INFO(0, numslaves)
-    ZEND_ARG_INFO(0, timeout)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1)
-    ZEND_ARG_INFO(0, cmd)
-    ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-/**
- * Argument info for the SCAN proper
- */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 1)
-    ZEND_ARG_INFO(1, i_iterator)
-    ZEND_ARG_INFO(0, str_pattern)
-    ZEND_ARG_INFO(0, i_count)
-ZEND_END_ARG_INFO()
-
-/**
- * Argument info for key scanning
- */
-ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2)
-    ZEND_ARG_INFO(0, str_key)
-    ZEND_ARG_INFO(1, i_iterator)
-    ZEND_ARG_INFO(0, str_pattern)
-    ZEND_ARG_INFO(0, i_count)
-ZEND_END_ARG_INFO()
-
-static zend_function_entry redis_functions[] = {
-     PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, _pack, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, _unpack, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, _compress, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, _uncompress, arginfo_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, acl, arginfo_acl, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, append, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, auth, arginfo_auth, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, bgSave, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, bitcount, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, bitop, arginfo_bitop, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, blPop, arginfo_blrpop, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, brPop, arginfo_blrpop, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, bzPopMax, arginfo_blrpop, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, bzPopMin, arginfo_blrpop, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, clearLastError, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, client, arginfo_client, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, close, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, command, arginfo_command, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, config, arginfo_config, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, connect, arginfo_connect, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, copy, arginfo_copy, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, dbSize, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, debug, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, decr, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, decrBy, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, del, arginfo_del, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, discard, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, dump, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, echo, arginfo_echo, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, eval, arginfo_eval, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, exists, arginfo_exists, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, expire, arginfo_expire, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, flushAll, arginfo_flush, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, flushDB, arginfo_flush, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, geodist, arginfo_geodist, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, geohash, arginfo_key_members, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, geopos, arginfo_key_members, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, georadius, arginfo_georadius, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, georadius_ro, arginfo_georadius, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, georadiusbymember_ro, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, get, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getAuth, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getBit, arginfo_key_offset, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getDBNum, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getHost, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getLastError, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getMode, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getOption, arginfo_getoption, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getPersistentID, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getPort, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getReadTimeout, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getSet, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, getTimeout, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hDel, arginfo_key_members, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hExists, arginfo_key_member, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hGet, arginfo_key_member, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hGetAll, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hIncrBy, arginfo_key_member_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hIncrByFloat, arginfo_key_member_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hKeys, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hLen, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hMget, arginfo_hmget, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hMset, arginfo_hmset, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hSet, arginfo_key_member_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hSetNx, arginfo_key_member_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hStrLen, arginfo_key_member, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hVals, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, incr, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, incrBy, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, incrByFloat, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, info, arginfo_info, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, isConnected, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, keys, arginfo_keys, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lLen, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lMove, arginfo_lmove, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lPop, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lPush, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lPushx, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lSet, arginfo_lset, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lastSave, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lindex, arginfo_lindex, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, lrem, arginfo_lrem, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, mget, arginfo_mget, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, migrate, arginfo_migrate, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, move, arginfo_move, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, mset, arginfo_pairs, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, multi, arginfo_multi, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, object, arginfo_object, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, persist, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pexpireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pfcount, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, ping, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pipeline, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pttl, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, publish, arginfo_publish, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, rPop, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, rPush, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, rPushx, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, randomKey, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, renameNx, arginfo_key_newkey, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, restore, arginfo_restore, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, role, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sAdd, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sAddArray, arginfo_sadd_array, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sDiff, arginfo_nkeys, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sDiffStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sInter, arginfo_nkeys, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sInterStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sMembers, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sMisMember, arginfo_key_members, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sUnion, arginfo_nkeys, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sUnionStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, save, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, scard, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, script, arginfo_script, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, select, arginfo_select, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, set, arginfo_set, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, setBit, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, setOption, arginfo_setoption, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, setRange, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, setnx, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sismember, arginfo_key_value, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, slaveof, arginfo_slaveof, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sort, arginfo_sort, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sortAsc, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
-     PHP_ME(Redis, srem, arginfo_key_members, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, swapdb, arginfo_swapdb, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, unlink, arginfo_nkeys, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, watch, arginfo_watch, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xack, arginfo_xack, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xadd, arginfo_xadd, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xclaim, arginfo_xclaim, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xdel, arginfo_xdel, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xgroup, arginfo_xgroup, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xinfo, arginfo_xinfo, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xlen, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xpending, arginfo_xpending, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xrange, arginfo_xrange, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xread, arginfo_xread, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xreadgroup, arginfo_xreadgroup, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xrevrange, arginfo_xrange, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, xtrim, arginfo_xtrim, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zAdd, arginfo_zadd, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zCard, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zIncrBy, arginfo_zincrby, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zLexCount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zMscore, arginfo_key_members, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zPopMax, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zPopMin, arginfo_key, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRank, arginfo_key_member, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRem, arginfo_key_members, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRemRangeByLex, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRemRangeByRank, arginfo_key_start_end, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRevRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRevRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zRevRank, arginfo_key_member, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zdiff, arginfo_zdiff, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zdiffstore, arginfo_zdiffstore, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zinter, arginfo_zinterunion, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zunion, arginfo_zinterunion, ZEND_ACC_PUBLIC)
-     PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC)
-     PHP_FE_END
-};
-
 static const zend_module_dep redis_deps[] = {
 #ifdef HAVE_REDIS_IGBINARY
      ZEND_MOD_REQUIRED("igbinary")
@@ -853,7 +469,7 @@ PHP_MINIT_FUNCTION(redis)
     REGISTER_INI_ENTRIES();
 
     /* Redis class */
-    INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions);
+    INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_get_methods());
     redis_ce = zend_register_internal_class(&redis_class_entry);
     redis_ce->create_object = create_redis_object;
 
@@ -1195,7 +811,7 @@ PHP_METHOD(Redis, setnx)
 
 /* {{{ proto string Redis::getSet(string key, string value)
  */
-PHP_METHOD(Redis, getSet)
+PHP_METHOD(Redis, getset)
 {
     REDIS_PROCESS_KW_CMD("GETSET", redis_kv_cmd, redis_string_response);
 }
diff --git a/redis.stub.php b/redis.stub.php
index 880e25d401..a9648c51bd 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -172,9 +172,6 @@ public function rPop(string $key);
      */
     public function delete(array|string $key, ...$otherkeys);
 
-	/** @return mixed|Redis */
-    public function ping(string $message = NULL);
-
     /**
      * @deprecated
      * @alias Redis::connect
@@ -187,3 +184,180 @@ public function open(string $host, int $port = 26379, float $timeout = 0, string
      */
     public function popen(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 }
+
+/*
+    TODO:
+    public function _prefix
+    public function _serialize
+    public function _unserialize
+    public function _pack
+    public function _unpack
+    public function _compress
+    public function _uncompress
+    public function auth
+    public function bgSave
+    public function bgrewriteaof
+    public function blPop
+    public function brPop
+    public function brpoplpush
+    public function bzPopMax
+    public function bzPopMin
+    public function clearLastError
+    public function client
+    public function command
+    public function config
+    public function copy
+    public function dbSize
+    public function debug
+    public function discard
+    public function dump
+    public function eval
+    public function evalsha
+    public function exec
+    public function expire
+    public function expireAt
+    public function flushAll
+    public function flushDB
+    public function geoadd
+    public function geodist
+    public function geohash
+    public function geopos
+    public function georadius
+    public function georadius_ro
+    public function georadiusbymember
+    public function georadiusbymember_ro
+    public function getAuth
+    public function getDBNum
+    public function getHost
+    public function getLastError
+    public function getMode
+    public function getOption
+    public function getPersistentID
+    public function getPort
+    public function getReadTimeout
+    public function getSet
+    public function getTimeout
+    public function hDel
+    public function hExists
+    public function hGet
+    public function hGetAll
+    public function hIncrBy
+    public function hIncrByFloat
+    public function hKeys
+    public function hLen
+    public function hMget
+    public function hMset
+    public function hSet
+    public function hSetNx
+    public function hStrLen
+    public function hVals
+    public function hscan
+    public function info
+    public function isConnected
+    public function lLen
+    public function lMove
+    public function lSet
+    public function lastSave
+    public function lindex
+    public function lrange
+    public function lrem
+    public function ltrim
+    public function migrate
+    public function move
+    public function mset
+    public function msetnx
+    public function multi
+    public function object
+    public function persist
+    public function pexpire
+    public function pexpireAt
+    public function pfadd
+    public function pfcount
+    public function pfmerge
+    public function pipeline
+    public function psubscribe
+    public function pttl
+    public function publish
+    public function pubsub
+    public function punsubscribe
+    public function rawcommand
+    public function restore
+    public function role
+    public function rpoplpush
+    public function sAdd
+    public function sAddArray
+    public function sDiff
+    public function sDiffStore
+    public function sInter
+    public function sInterStore
+    public function sMembers
+    public function sMisMember
+    public function sMove
+    public function sPop
+    public function sRandMember
+    public function sUnion
+    public function sUnionStore
+    public function save
+    public function scan
+    public function scard
+    public function script
+    public function select
+    public function setOption
+    public function sismember
+    public function slaveof
+    public function slowlog
+    public function sort
+    public function sortAsc
+    public function sortAscAlpha
+    public function sortDesc
+    public function sortDescAlpha
+    public function srem
+    public function sscan
+    public function subscribe
+    public function swapdb
+    public function time
+    public function ttl
+    public function unsubscribe
+    public function wait
+    public function xack
+    public function xadd
+    public function xclaim
+    public function xdel
+    public function xgroup
+    public function xinfo
+    public function xlen
+    public function xpending
+    public function xrange
+    public function xread
+    public function xreadgroup
+    public function xrevrange
+    public function xtrim
+    public function zAdd
+    public function zCard
+    public function zCount
+    public function zIncrBy
+    public function zLexCount
+    public function zMscore
+    public function zPopMax
+    public function zPopMin
+    public function zRange
+    public function zRangeByLex
+    public function zRangeByScore
+    public function zRank
+    public function zRem
+    public function zRemRangeByLex
+    public function zRemRangeByRank
+    public function zRemRangeByScore
+    public function zRevRange
+    public function zRevRangeByLex
+    public function zRevRangeByScore
+    public function zRevRank
+    public function zScore
+    public function zdiff
+    public function zdiffstore
+    public function zinter
+    public function zinterstore
+    public function zscan
+    public function zunion
+    public function zunionstore
+*/
diff --git a/redis_arginfo.h b/redis_arginfo.h
index a2726727b3..9679a27c14 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b3a02d01273649649ff8627a90ab9c46b9beddbb */
+ * Stub hash: 3997a1b8acf7d0a4a3affc95c10d84906d354e6c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -178,10 +178,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "NULL")
-ZEND_END_ARG_INFO()
-
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
 
 #define arginfo_class_Redis_popen arginfo_class_Redis_connect
@@ -283,7 +279,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
-	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_FE_END
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 6f257eaabd..feaae6bbee 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b3a02d01273649649ff8627a90ab9c46b9beddbb */
+ * Stub hash: 3997a1b8acf7d0a4a3affc95c10d84906d354e6c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -171,10 +171,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
-	ZEND_ARG_INFO(0, message)
-ZEND_END_ARG_INFO()
-
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
 
 #define arginfo_class_Redis_popen arginfo_class_Redis_connect
@@ -276,7 +272,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
-	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_FE_END

From 86e236cb1e3086778eb9ff23f19b67ed973a2f7e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 2 Sep 2021 14:40:20 +0300
Subject: [PATCH 1485/1986] Add stub-based arginfo for Redis _-a

---
 redis.stub.php         | 24 +++++++++++------
 redis_arginfo.h        | 44 +++++++++++++++++++++++++++++++-
 redis_legacy_arginfo.h | 58 ++++++++++++++++++++++++++++++++++--------
 3 files changed, 106 insertions(+), 20 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index a9648c51bd..524ffc408f 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -9,8 +9,24 @@ class Redis {
 
     public function __construct();
 
+    public function _compress(string $value): string;
+
     public function __destruct();
 
+    public function _pack(mixed $value): string;
+
+    public function _prefix(string $key): string;
+
+    public function _serialize(mixed $value): string;
+
+    public function _uncompress(string $value): string;
+
+    public function _unpack(string $value): mixed;
+
+    public function _unserialize(string $value): mixed;
+
+    public function auth(mixed $credentials): bool;
+
     public function connect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
     public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
@@ -187,14 +203,6 @@ public function popen(string $host, int $port = 26379, float $timeout = 0, strin
 
 /*
     TODO:
-    public function _prefix
-    public function _serialize
-    public function _unserialize
-    public function _pack
-    public function _unpack
-    public function _compress
-    public function _uncompress
-    public function auth
     public function bgSave
     public function bgrewriteaof
     public function blPop
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 9679a27c14..a92e769196 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,11 +1,37 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3997a1b8acf7d0a4a3affc95c10d84906d354e6c */
+ * Stub hash: b8e05484964566b2307222ba5813c92a3935d60d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__compress, 0, 1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis___destruct arginfo_class_Redis___construct
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__pack, 0, 1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__prefix, 0, 1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis__serialize arginfo_class_Redis__pack
+
+#define arginfo_class_Redis__uncompress arginfo_class_Redis__compress
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__unpack, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis__unserialize arginfo_class_Redis__unpack
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_auth, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, credentials, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
@@ -184,7 +210,15 @@ ZEND_END_ARG_INFO()
 
 
 ZEND_METHOD(Redis, __construct);
+ZEND_METHOD(Redis, _compress);
 ZEND_METHOD(Redis, __destruct);
+ZEND_METHOD(Redis, _pack);
+ZEND_METHOD(Redis, _prefix);
+ZEND_METHOD(Redis, _serialize);
+ZEND_METHOD(Redis, _uncompress);
+ZEND_METHOD(Redis, _unpack);
+ZEND_METHOD(Redis, _unserialize);
+ZEND_METHOD(Redis, auth);
 ZEND_METHOD(Redis, connect);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, bitop);
@@ -233,7 +267,15 @@ ZEND_METHOD(Redis, rPop);
 
 static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _prefix, arginfo_class_Redis__prefix, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index feaae6bbee..ac2fb9bd7f 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,11 +1,33 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3997a1b8acf7d0a4a3affc95c10d84906d354e6c */
+ * Stub hash: b8e05484964566b2307222ba5813c92a3935d60d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__compress, 0, 0, 1)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis___destruct arginfo_class_Redis___construct
 
+#define arginfo_class_Redis__pack arginfo_class_Redis__compress
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__prefix, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis__serialize arginfo_class_Redis__compress
+
+#define arginfo_class_Redis__uncompress arginfo_class_Redis__compress
+
+#define arginfo_class_Redis__unpack arginfo_class_Redis__compress
+
+#define arginfo_class_Redis__unserialize arginfo_class_Redis__compress
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_auth, 0, 0, 1)
+	ZEND_ARG_INFO(0, credentials)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_connect, 0, 0, 1)
 	ZEND_ARG_INFO(0, host)
 	ZEND_ARG_INFO(0, port)
@@ -74,21 +96,19 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_get, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_get arginfo_class_Redis__prefix
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_INFO(0, key)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_incr arginfo_class_Redis_get
+#define arginfo_class_Redis_incr arginfo_class_Redis__prefix
 
 #define arginfo_class_Redis_incrBy arginfo_class_Redis_setnx
 
 #define arginfo_class_Redis_incrByFloat arginfo_class_Redis_setnx
 
-#define arginfo_class_Redis_decr arginfo_class_Redis_get
+#define arginfo_class_Redis_decr arginfo_class_Redis__prefix
 
 #define arginfo_class_Redis_decrBy arginfo_class_Redis_setnx
 
@@ -96,7 +116,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_INFO(0, keys)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_exists arginfo_class_Redis_get
+#define arginfo_class_Redis_exists arginfo_class_Redis__prefix
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
@@ -113,7 +133,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
 	ZEND_ARG_INFO(0, pattern)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_type arginfo_class_Redis_get
+#define arginfo_class_Redis_type arginfo_class_Redis__prefix
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
 	ZEND_ARG_INFO(0, subcmd)
@@ -145,7 +165,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_strlen arginfo_class_Redis_get
+#define arginfo_class_Redis_strlen arginfo_class_Redis__prefix
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
@@ -165,9 +185,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_rPushx arginfo_class_Redis_setnx
 
-#define arginfo_class_Redis_lPop arginfo_class_Redis_get
+#define arginfo_class_Redis_lPop arginfo_class_Redis__prefix
 
-#define arginfo_class_Redis_rPop arginfo_class_Redis_get
+#define arginfo_class_Redis_rPop arginfo_class_Redis__prefix
 
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
 
@@ -177,7 +197,15 @@ ZEND_END_ARG_INFO()
 
 
 ZEND_METHOD(Redis, __construct);
+ZEND_METHOD(Redis, _compress);
 ZEND_METHOD(Redis, __destruct);
+ZEND_METHOD(Redis, _pack);
+ZEND_METHOD(Redis, _prefix);
+ZEND_METHOD(Redis, _serialize);
+ZEND_METHOD(Redis, _uncompress);
+ZEND_METHOD(Redis, _unpack);
+ZEND_METHOD(Redis, _unserialize);
+ZEND_METHOD(Redis, auth);
 ZEND_METHOD(Redis, connect);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, bitop);
@@ -226,7 +254,15 @@ ZEND_METHOD(Redis, rPop);
 
 static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _prefix, arginfo_class_Redis__prefix, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC)

From 6bbe38319c0a494983f3b43c4dd88977d8287f1e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Sep 2021 10:04:57 +0300
Subject: [PATCH 1486/1986] Add stub-based arginfo for Redis b-d

---
 redis.stub.php         | 100 ++++++++++++---------
 redis_arginfo.h        | 199 ++++++++++++++++++++++++++++++-----------
 redis_legacy_arginfo.h | 192 ++++++++++++++++++++++++++++-----------
 3 files changed, 344 insertions(+), 147 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 524ffc408f..f48fda5cd0 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -25,26 +25,75 @@ public function _unpack(string $value): mixed;
 
     public function _unserialize(string $value): mixed;
 
+    /**
+     * @param string $args
+     * @return mixed|Redis
+     */
+    public function acl(string $subcmd, ...$args);
+
+	/** @return int|Redis */
+    public function append(string $key, mixed $value);
+
     public function auth(mixed $credentials): bool;
 
-    public function connect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
+    public function bgSave(): bool;
 
-    public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
+    public function bgrewriteaof(): bool;
+
+    /** @return int|Redis */
+    public function bitcount(string $key, int $start = 0, int $end = -1);
 
     /**
-     * @param string $otherkeys 
+     * @param string $otherkeys
      * @return int|Redis
      */
     public function bitop(string $operation, string $deskey, string $srckey, ...$otherkeys): int;
 
-    /** @return int|Redis */
-    public function bitcount(string $key, int $start = 0, int $end = -1);
-
     /** @return int|Redis */
     public function bitpos(string $key, int $bit, int $start = 0, int $end = -1);
 
+    public function blPop(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+
+    public function brPop(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+
+    public function brpoplpush(string $src, string $dst, int $timeout): string;
+
+    public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+
+    public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+
+    public function clearLastError(): bool;
+
+    public function client(string $opt, string $arg = null): mixed;
+
     public function close(): bool;
 
+    public function command(string $opt = null, string|array $arg): mixed;
+
+    public function config(string $operation, string $key, mixed $value = null): mixed;
+
+    public function connect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool;
+
+    public function copy(string $src, string $dst, array $options = null): bool;
+
+    public function dbSize(): int;
+
+    public function debug(string $key): string;
+
+    /**
+     * @param string $otherkeys
+     * @deprecated
+     * @alias Redis::del
+     * @return int|Redis
+     */
+    public function delete(array|string $key, ...$otherkeys);
+
+    public function discard(): bool;
+
+    public function dump(string $key): string;
+
+    public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
+
     /** @return bool|Redis */
     public function set(string $key, mixed $value, mixed $opt = NULL);
 
@@ -100,19 +149,19 @@ public function mget(array $keys);
     public function exists(string $key);
 
     /**
-     * @param string $otherkeys 
+     * @param string $otherkeys
      * @return int|Redis
      */
     public function del(array|string $key, ...$otherkeys);
 
     /**
-     * @param string $otherkeys 
+     * @param string $otherkeys
      * @return int|Redis
      */
     public function unlink(array|string $key, ...$otherkeys);
 
     /**
-     * @param string $otherkeys 
+     * @param string $otherkeys
      * @return bool|Redis
      */
     public function watch(array|string $key, ...$otherkeys);
@@ -126,15 +175,6 @@ public function keys(string $pattern);
 	/** @return int|Redis */
     public function type(string $key);
 
-    /**
-     * @param string $args 
-     * @return mixed|Redis
-     */
-    public function acl(string $subcmd, ...$args);
-
-	/** @return int|Redis */
-    public function append(string $key, mixed $value);
-
 	/** @return string|Redis */
     public function getRange(string $key, int $start, int $end);
 
@@ -180,14 +220,6 @@ public function lPop(string $key);
 	/** @return string|Redis */
     public function rPop(string $key);
 
-    /**
-     * @param string $otherkeys 
-     * @deprecated
-     * @alias Redis::del
-     * @return int|Redis
-     */
-    public function delete(array|string $key, ...$otherkeys);
-
     /**
      * @deprecated
      * @alias Redis::connect
@@ -203,22 +235,6 @@ public function popen(string $host, int $port = 26379, float $timeout = 0, strin
 
 /*
     TODO:
-    public function bgSave
-    public function bgrewriteaof
-    public function blPop
-    public function brPop
-    public function brpoplpush
-    public function bzPopMax
-    public function bzPopMin
-    public function clearLastError
-    public function client
-    public function command
-    public function config
-    public function copy
-    public function dbSize
-    public function debug
-    public function discard
-    public function dump
     public function eval
     public function evalsha
     public function exec
diff --git a/redis_arginfo.h b/redis_arginfo.h
index a92e769196..0101b2afd4 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b8e05484964566b2307222ba5813c92a3935d60d */
+ * Stub hash: 29b47dbcf96368be84c77a1881756e903d68042d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -28,21 +28,30 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis__unserialize arginfo_class_Redis__unpack
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_append, 0, 0, 2)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_auth, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, credentials, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "NULL")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL")
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bgSave, 0, 0, _IS_BOOL, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
+#define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis_bgSave
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bitop, 0, 3, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
@@ -51,20 +60,89 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bitop, 0, 3, IS_LONG
 	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, bit, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_blPop, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_brPop arginfo_class_Redis_blPop
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_brpoplpush, 0, 3, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_bzPopMax arginfo_class_Redis_blPop
+
+#define arginfo_class_Redis_bzPopMin arginfo_class_Redis_blPop
+
+#define arginfo_class_Redis_clearLastError arginfo_class_Redis_bgSave
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_client, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, opt, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_close arginfo_class_Redis_bgSave
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 2, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_MASK(0, arg, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 2, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, bit, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_copy, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_close, 0, 0, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_dbSize, 0, 0, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_debug arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_delete, 0, 0, 1)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_discard arginfo_class_Redis_bgSave
+
+#define arginfo_class_Redis_dump arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pconnect, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "NULL")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
@@ -81,12 +159,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_psetex arginfo_class_Redis_setex
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setnx, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_setnx arginfo_class_Redis_append
 
-#define arginfo_class_Redis_getset arginfo_class_Redis_setnx
+#define arginfo_class_Redis_getset arginfo_class_Redis_append
 
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
 
@@ -131,14 +206,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_exists arginfo_class_Redis_get
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
-	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
-	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_del arginfo_class_Redis_delete
 
-#define arginfo_class_Redis_unlink arginfo_class_Redis_del
+#define arginfo_class_Redis_unlink arginfo_class_Redis_delete
 
-#define arginfo_class_Redis_watch arginfo_class_Redis_del
+#define arginfo_class_Redis_watch arginfo_class_Redis_delete
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
 
@@ -148,13 +220,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_type arginfo_class_Redis_get
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_append arginfo_class_Redis_setnx
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
@@ -194,19 +259,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_lPushx arginfo_class_Redis_setnx
+#define arginfo_class_Redis_lPushx arginfo_class_Redis_append
 
-#define arginfo_class_Redis_rPushx arginfo_class_Redis_setnx
+#define arginfo_class_Redis_rPushx arginfo_class_Redis_append
 
 #define arginfo_class_Redis_lPop arginfo_class_Redis_get
 
 #define arginfo_class_Redis_rPop arginfo_class_Redis_get
 
-#define arginfo_class_Redis_delete arginfo_class_Redis_del
-
-#define arginfo_class_Redis_open arginfo_class_Redis_connect
+#define arginfo_class_Redis_open arginfo_class_Redis_pconnect
 
-#define arginfo_class_Redis_popen arginfo_class_Redis_connect
+#define arginfo_class_Redis_popen arginfo_class_Redis_pconnect
 
 
 ZEND_METHOD(Redis, __construct);
@@ -218,13 +281,32 @@ ZEND_METHOD(Redis, _serialize);
 ZEND_METHOD(Redis, _uncompress);
 ZEND_METHOD(Redis, _unpack);
 ZEND_METHOD(Redis, _unserialize);
+ZEND_METHOD(Redis, acl);
+ZEND_METHOD(Redis, append);
 ZEND_METHOD(Redis, auth);
-ZEND_METHOD(Redis, connect);
-ZEND_METHOD(Redis, pconnect);
-ZEND_METHOD(Redis, bitop);
+ZEND_METHOD(Redis, bgSave);
+ZEND_METHOD(Redis, bgrewriteaof);
 ZEND_METHOD(Redis, bitcount);
+ZEND_METHOD(Redis, bitop);
 ZEND_METHOD(Redis, bitpos);
+ZEND_METHOD(Redis, blPop);
+ZEND_METHOD(Redis, brPop);
+ZEND_METHOD(Redis, brpoplpush);
+ZEND_METHOD(Redis, bzPopMax);
+ZEND_METHOD(Redis, bzPopMin);
+ZEND_METHOD(Redis, clearLastError);
+ZEND_METHOD(Redis, client);
 ZEND_METHOD(Redis, close);
+ZEND_METHOD(Redis, command);
+ZEND_METHOD(Redis, config);
+ZEND_METHOD(Redis, connect);
+ZEND_METHOD(Redis, copy);
+ZEND_METHOD(Redis, dbSize);
+ZEND_METHOD(Redis, debug);
+ZEND_METHOD(Redis, del);
+ZEND_METHOD(Redis, discard);
+ZEND_METHOD(Redis, dump);
+ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, psetex);
@@ -243,14 +325,11 @@ ZEND_METHOD(Redis, decr);
 ZEND_METHOD(Redis, decrBy);
 ZEND_METHOD(Redis, mget);
 ZEND_METHOD(Redis, exists);
-ZEND_METHOD(Redis, del);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
 ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, type);
-ZEND_METHOD(Redis, acl);
-ZEND_METHOD(Redis, append);
 ZEND_METHOD(Redis, getRange);
 ZEND_METHOD(Redis, setRange);
 ZEND_METHOD(Redis, getBit);
@@ -275,13 +354,32 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bgSave, arginfo_class_Redis_bgSave, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bgrewriteaof, arginfo_class_Redis_bgrewriteaof, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, blPop, arginfo_class_Redis_blPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, brPop, arginfo_class_Redis_brPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, brpoplpush, arginfo_class_Redis_brpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bzPopMax, arginfo_class_Redis_bzPopMax, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bzPopMin, arginfo_class_Redis_bzPopMin, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, clearLastError, arginfo_class_Redis_clearLastError, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, client, arginfo_class_Redis_client, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, close, arginfo_class_Redis_close, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, command, arginfo_class_Redis_command, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, config, arginfo_class_Redis_config, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, copy, arginfo_class_Redis_copy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, dbSize, arginfo_class_Redis_dbSize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, debug, arginfo_class_Redis_debug, ZEND_ACC_PUBLIC)
+	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC)
@@ -306,8 +404,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
@@ -320,7 +416,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
-	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_FE_END
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index ac2fb9bd7f..1c63618956 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b8e05484964566b2307222ba5813c92a3935d60d */
+ * Stub hash: 29b47dbcf96368be84c77a1881756e903d68042d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -24,21 +24,29 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis__unserialize arginfo_class_Redis__compress
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
+	ZEND_ARG_INFO(0, subcmd)
+	ZEND_ARG_VARIADIC_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_append, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_auth, 0, 0, 1)
 	ZEND_ARG_INFO(0, credentials)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_connect, 0, 0, 1)
-	ZEND_ARG_INFO(0, host)
-	ZEND_ARG_INFO(0, port)
-	ZEND_ARG_INFO(0, timeout)
-	ZEND_ARG_INFO(0, persistent_id)
-	ZEND_ARG_INFO(0, retry_interval)
-	ZEND_ARG_INFO(0, read_timeout)
-	ZEND_ARG_INFO(0, context)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_bgSave arginfo_class_Redis___construct
 
-#define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
+#define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitop, 0, 0, 3)
 	ZEND_ARG_INFO(0, operation)
@@ -47,21 +55,82 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitop, 0, 0, 3)
 	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, bit)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_blPop, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, bit)
-	ZEND_ARG_INFO(0, start)
-	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, timeout_or_key)
+	ZEND_ARG_VARIADIC_INFO(0, extra_args)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_brPop arginfo_class_Redis_blPop
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_brpoplpush, 0, 0, 3)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_bzPopMax arginfo_class_Redis_blPop
+
+#define arginfo_class_Redis_bzPopMin arginfo_class_Redis_blPop
+
+#define arginfo_class_Redis_clearLastError arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_client, 0, 0, 1)
+	ZEND_ARG_INFO(0, opt)
+	ZEND_ARG_INFO(0, arg)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_close arginfo_class_Redis___construct
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_command, 0, 0, 2)
+	ZEND_ARG_INFO(0, opt)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_config, 0, 0, 2)
+	ZEND_ARG_INFO(0, operation)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_connect, 0, 0, 1)
+	ZEND_ARG_INFO(0, host)
+	ZEND_ARG_INFO(0, port)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, persistent_id)
+	ZEND_ARG_INFO(0, retry_interval)
+	ZEND_ARG_INFO(0, read_timeout)
+	ZEND_ARG_INFO(0, context)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_copy, 0, 0, 2)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_dbSize arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_debug arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_delete, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_discard arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_dump arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, value)
@@ -76,12 +145,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_psetex arginfo_class_Redis_setex
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setnx, 0, 0, 2)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_setnx arginfo_class_Redis_append
 
-#define arginfo_class_Redis_getset arginfo_class_Redis_setnx
+#define arginfo_class_Redis_getset arginfo_class_Redis_append
 
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
 
@@ -104,13 +170,13 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_incr arginfo_class_Redis__prefix
 
-#define arginfo_class_Redis_incrBy arginfo_class_Redis_setnx
+#define arginfo_class_Redis_incrBy arginfo_class_Redis_append
 
-#define arginfo_class_Redis_incrByFloat arginfo_class_Redis_setnx
+#define arginfo_class_Redis_incrByFloat arginfo_class_Redis_append
 
 #define arginfo_class_Redis_decr arginfo_class_Redis__prefix
 
-#define arginfo_class_Redis_decrBy arginfo_class_Redis_setnx
+#define arginfo_class_Redis_decrBy arginfo_class_Redis_append
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_INFO(0, keys)
@@ -118,14 +184,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_exists arginfo_class_Redis__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_del arginfo_class_Redis_delete
 
-#define arginfo_class_Redis_unlink arginfo_class_Redis_del
+#define arginfo_class_Redis_unlink arginfo_class_Redis_delete
 
-#define arginfo_class_Redis_watch arginfo_class_Redis_del
+#define arginfo_class_Redis_watch arginfo_class_Redis_delete
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
 
@@ -135,13 +198,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_type arginfo_class_Redis__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
-	ZEND_ARG_INFO(0, subcmd)
-	ZEND_ARG_VARIADIC_INFO(0, args)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_append arginfo_class_Redis_setnx
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, start)
@@ -181,16 +237,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_lPushx arginfo_class_Redis_setnx
+#define arginfo_class_Redis_lPushx arginfo_class_Redis_append
 
-#define arginfo_class_Redis_rPushx arginfo_class_Redis_setnx
+#define arginfo_class_Redis_rPushx arginfo_class_Redis_append
 
 #define arginfo_class_Redis_lPop arginfo_class_Redis__prefix
 
 #define arginfo_class_Redis_rPop arginfo_class_Redis__prefix
 
-#define arginfo_class_Redis_delete arginfo_class_Redis_del
-
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
 
 #define arginfo_class_Redis_popen arginfo_class_Redis_connect
@@ -205,13 +259,32 @@ ZEND_METHOD(Redis, _serialize);
 ZEND_METHOD(Redis, _uncompress);
 ZEND_METHOD(Redis, _unpack);
 ZEND_METHOD(Redis, _unserialize);
+ZEND_METHOD(Redis, acl);
+ZEND_METHOD(Redis, append);
 ZEND_METHOD(Redis, auth);
-ZEND_METHOD(Redis, connect);
-ZEND_METHOD(Redis, pconnect);
-ZEND_METHOD(Redis, bitop);
+ZEND_METHOD(Redis, bgSave);
+ZEND_METHOD(Redis, bgrewriteaof);
 ZEND_METHOD(Redis, bitcount);
+ZEND_METHOD(Redis, bitop);
 ZEND_METHOD(Redis, bitpos);
+ZEND_METHOD(Redis, blPop);
+ZEND_METHOD(Redis, brPop);
+ZEND_METHOD(Redis, brpoplpush);
+ZEND_METHOD(Redis, bzPopMax);
+ZEND_METHOD(Redis, bzPopMin);
+ZEND_METHOD(Redis, clearLastError);
+ZEND_METHOD(Redis, client);
 ZEND_METHOD(Redis, close);
+ZEND_METHOD(Redis, command);
+ZEND_METHOD(Redis, config);
+ZEND_METHOD(Redis, connect);
+ZEND_METHOD(Redis, copy);
+ZEND_METHOD(Redis, dbSize);
+ZEND_METHOD(Redis, debug);
+ZEND_METHOD(Redis, del);
+ZEND_METHOD(Redis, discard);
+ZEND_METHOD(Redis, dump);
+ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, psetex);
@@ -230,14 +303,11 @@ ZEND_METHOD(Redis, decr);
 ZEND_METHOD(Redis, decrBy);
 ZEND_METHOD(Redis, mget);
 ZEND_METHOD(Redis, exists);
-ZEND_METHOD(Redis, del);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
 ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, type);
-ZEND_METHOD(Redis, acl);
-ZEND_METHOD(Redis, append);
 ZEND_METHOD(Redis, getRange);
 ZEND_METHOD(Redis, setRange);
 ZEND_METHOD(Redis, getBit);
@@ -262,13 +332,32 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bgSave, arginfo_class_Redis_bgSave, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bgrewriteaof, arginfo_class_Redis_bgrewriteaof, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, blPop, arginfo_class_Redis_blPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, brPop, arginfo_class_Redis_brPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, brpoplpush, arginfo_class_Redis_brpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bzPopMax, arginfo_class_Redis_bzPopMax, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bzPopMin, arginfo_class_Redis_bzPopMin, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, clearLastError, arginfo_class_Redis_clearLastError, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, client, arginfo_class_Redis_client, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, close, arginfo_class_Redis_close, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, command, arginfo_class_Redis_command, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, config, arginfo_class_Redis_config, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, connect, arginfo_class_Redis_connect, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, copy, arginfo_class_Redis_copy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, dbSize, arginfo_class_Redis_dbSize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, debug, arginfo_class_Redis_debug, ZEND_ACC_PUBLIC)
+	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC)
@@ -293,8 +382,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
@@ -307,7 +394,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
-	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_FE_END

From 0cd8d06d2a752cd351229e86e0dc00f4dacaa470 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Sep 2021 10:21:14 +0300
Subject: [PATCH 1487/1986] Add stub-based arginfo for Redis e-f

---
 redis.stub.php         |  52 +++++++++++--------
 redis_arginfo.h        | 111 ++++++++++++++++++++++++++++++-----------
 redis_legacy_arginfo.h |  86 ++++++++++++++++++++++++-------
 3 files changed, 179 insertions(+), 70 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index f48fda5cd0..78d018ea17 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -80,6 +80,18 @@ public function dbSize(): int;
 
     public function debug(string $key): string;
 
+	/** @return int|Redis */
+    public function decr(string $key);
+
+	/** @return int|Redis */
+    public function decrBy(string $key, int $value);
+
+    /**
+     * @param string $otherkeys
+     * @return int|Redis
+     */
+    public function del(array|string $key, ...$otherkeys);
+
     /**
      * @param string $otherkeys
      * @deprecated
@@ -92,6 +104,23 @@ public function discard(): bool;
 
     public function dump(string $key): string;
 
+    public function eval(string $script, array $keys = null, int $num_keys = 0): mixed;
+
+    public function evalsha(string $sha1, array $keys = null, int $num_keys = 0): mixed;
+
+    public function exec(): array;
+
+	/** @return bool|Redis */
+    public function exists(string $key);
+
+    public function expire(string $key, int $timeout): bool;
+
+    public function expireAt(string $key, int $timestamp): bool;
+
+    public function flushAll(bool $async = false): bool;
+
+    public function flushDB(bool $async = false): bool;
+
     public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
     /** @return bool|Redis */
@@ -136,24 +165,11 @@ public function incrBy(string $key, int $value);
 	/** @return int|Redis */
     public function incrByFloat(string $key, float $value);
 
-	/** @return int|Redis */
-    public function decr(string $key);
-
-	/** @return int|Redis */
-    public function decrBy(string $key, int $value);
+    public function info(string $opt = null): array;
 
 	/** @return array|Redis */
     public function mget(array $keys);
 
-	/** @return bool|Redis */
-    public function exists(string $key);
-
-    /**
-     * @param string $otherkeys
-     * @return int|Redis
-     */
-    public function del(array|string $key, ...$otherkeys);
-
     /**
      * @param string $otherkeys
      * @return int|Redis
@@ -235,13 +251,6 @@ public function popen(string $host, int $port = 26379, float $timeout = 0, strin
 
 /*
     TODO:
-    public function eval
-    public function evalsha
-    public function exec
-    public function expire
-    public function expireAt
-    public function flushAll
-    public function flushDB
     public function geoadd
     public function geodist
     public function geohash
@@ -276,7 +285,6 @@ public function hSetNx
     public function hStrLen
     public function hVals
     public function hscan
-    public function info
     public function isConnected
     public function lLen
     public function lMove
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 0101b2afd4..f9df0deb1a 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 29b47dbcf96368be84c77a1881756e903d68042d */
+ * Stub hash: 19c4026366635e6429dd7a7c17fbe8cdfe6b00e0 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -126,15 +126,59 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_debug arginfo_class_Redis__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_delete, 0, 0, 1)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_decr, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_decrBy, 0, 0, 2)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
 	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_delete arginfo_class_Redis_del
+
 #define arginfo_class_Redis_discard arginfo_class_Redis_bgSave
 
 #define arginfo_class_Redis_dump arginfo_class_Redis__prefix
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_eval, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, script, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, keys, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_evalsha, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, sha1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, keys, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_exec, 0, 0, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_exists arginfo_class_Redis_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_expire, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_expireAt, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, async, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pconnect, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
@@ -176,41 +220,32 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_get, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_get arginfo_class_Redis_decr
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_incr arginfo_class_Redis_get
+#define arginfo_class_Redis_incr arginfo_class_Redis_decr
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrBy, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_incrBy arginfo_class_Redis_decrBy
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrByFloat, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_decr arginfo_class_Redis_get
-
-#define arginfo_class_Redis_decrBy arginfo_class_Redis_incrBy
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_info, 0, 0, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_exists arginfo_class_Redis_get
-
-#define arginfo_class_Redis_del arginfo_class_Redis_delete
+#define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
-#define arginfo_class_Redis_unlink arginfo_class_Redis_delete
-
-#define arginfo_class_Redis_watch arginfo_class_Redis_delete
+#define arginfo_class_Redis_watch arginfo_class_Redis_del
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
 
@@ -218,7 +253,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_type arginfo_class_Redis_get
+#define arginfo_class_Redis_type arginfo_class_Redis_decr
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -243,7 +278,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, value, _IS_BOOL, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_strlen arginfo_class_Redis_get
+#define arginfo_class_Redis_strlen arginfo_class_Redis_decr
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -263,9 +298,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_rPushx arginfo_class_Redis_append
 
-#define arginfo_class_Redis_lPop arginfo_class_Redis_get
+#define arginfo_class_Redis_lPop arginfo_class_Redis_decr
 
-#define arginfo_class_Redis_rPop arginfo_class_Redis_get
+#define arginfo_class_Redis_rPop arginfo_class_Redis_decr
 
 #define arginfo_class_Redis_open arginfo_class_Redis_pconnect
 
@@ -303,9 +338,19 @@ ZEND_METHOD(Redis, connect);
 ZEND_METHOD(Redis, copy);
 ZEND_METHOD(Redis, dbSize);
 ZEND_METHOD(Redis, debug);
+ZEND_METHOD(Redis, decr);
+ZEND_METHOD(Redis, decrBy);
 ZEND_METHOD(Redis, del);
 ZEND_METHOD(Redis, discard);
 ZEND_METHOD(Redis, dump);
+ZEND_METHOD(Redis, eval);
+ZEND_METHOD(Redis, evalsha);
+ZEND_METHOD(Redis, exec);
+ZEND_METHOD(Redis, exists);
+ZEND_METHOD(Redis, expire);
+ZEND_METHOD(Redis, expireAt);
+ZEND_METHOD(Redis, flushAll);
+ZEND_METHOD(Redis, flushDB);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
@@ -321,10 +366,8 @@ ZEND_METHOD(Redis, ping);
 ZEND_METHOD(Redis, incr);
 ZEND_METHOD(Redis, incrBy);
 ZEND_METHOD(Redis, incrByFloat);
-ZEND_METHOD(Redis, decr);
-ZEND_METHOD(Redis, decrBy);
+ZEND_METHOD(Redis, info);
 ZEND_METHOD(Redis, mget);
-ZEND_METHOD(Redis, exists);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
@@ -376,9 +419,20 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, copy, arginfo_class_Redis_copy, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, dbSize, arginfo_class_Redis_dbSize, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, debug, arginfo_class_Redis_debug, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, eval, arginfo_class_Redis_eval, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, evalsha, arginfo_class_Redis_evalsha, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, exec, arginfo_class_Redis_exec, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
@@ -394,11 +448,8 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 1c63618956..7239588c52 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 29b47dbcf96368be84c77a1881756e903d68042d */
+ * Stub hash: 19c4026366635e6429dd7a7c17fbe8cdfe6b00e0 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -120,15 +120,53 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_debug arginfo_class_Redis__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_delete, 0, 0, 1)
+#define arginfo_class_Redis_decr arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_decrBy arginfo_class_Redis_append
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_delete arginfo_class_Redis_del
+
 #define arginfo_class_Redis_discard arginfo_class_Redis___construct
 
 #define arginfo_class_Redis_dump arginfo_class_Redis__prefix
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_eval, 0, 0, 1)
+	ZEND_ARG_INFO(0, script)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, num_keys)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_evalsha, 0, 0, 1)
+	ZEND_ARG_INFO(0, sha1)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, num_keys)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_exec arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_exists arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expire, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expireAt, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timestamp)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, 0)
+	ZEND_ARG_INFO(0, async)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
+
 #define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
@@ -174,21 +212,17 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_incrByFloat arginfo_class_Redis_append
 
-#define arginfo_class_Redis_decr arginfo_class_Redis__prefix
-
-#define arginfo_class_Redis_decrBy arginfo_class_Redis_append
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_info, 0, 0, 0)
+	ZEND_ARG_INFO(0, opt)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_INFO(0, keys)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_exists arginfo_class_Redis__prefix
-
-#define arginfo_class_Redis_del arginfo_class_Redis_delete
-
-#define arginfo_class_Redis_unlink arginfo_class_Redis_delete
+#define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
-#define arginfo_class_Redis_watch arginfo_class_Redis_delete
+#define arginfo_class_Redis_watch arginfo_class_Redis_del
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
 
@@ -281,9 +315,19 @@ ZEND_METHOD(Redis, connect);
 ZEND_METHOD(Redis, copy);
 ZEND_METHOD(Redis, dbSize);
 ZEND_METHOD(Redis, debug);
+ZEND_METHOD(Redis, decr);
+ZEND_METHOD(Redis, decrBy);
 ZEND_METHOD(Redis, del);
 ZEND_METHOD(Redis, discard);
 ZEND_METHOD(Redis, dump);
+ZEND_METHOD(Redis, eval);
+ZEND_METHOD(Redis, evalsha);
+ZEND_METHOD(Redis, exec);
+ZEND_METHOD(Redis, exists);
+ZEND_METHOD(Redis, expire);
+ZEND_METHOD(Redis, expireAt);
+ZEND_METHOD(Redis, flushAll);
+ZEND_METHOD(Redis, flushDB);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
@@ -299,10 +343,8 @@ ZEND_METHOD(Redis, ping);
 ZEND_METHOD(Redis, incr);
 ZEND_METHOD(Redis, incrBy);
 ZEND_METHOD(Redis, incrByFloat);
-ZEND_METHOD(Redis, decr);
-ZEND_METHOD(Redis, decrBy);
+ZEND_METHOD(Redis, info);
 ZEND_METHOD(Redis, mget);
-ZEND_METHOD(Redis, exists);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
@@ -354,9 +396,20 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, copy, arginfo_class_Redis_copy, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, dbSize, arginfo_class_Redis_dbSize, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, debug, arginfo_class_Redis_debug, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, eval, arginfo_class_Redis_eval, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, evalsha, arginfo_class_Redis_evalsha, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, exec, arginfo_class_Redis_exec, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
@@ -372,11 +425,8 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)

From 599e822e17ad3d832235b7d65d5fe3441f62a659 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Sep 2021 10:56:41 +0300
Subject: [PATCH 1488/1986] Add stub-based arginfo for Redis g,i

---
 redis.stub.php         |  79 +++++++++++++---------
 redis_arginfo.h        | 147 ++++++++++++++++++++++++++++++++++++-----
 redis_legacy_arginfo.h | 144 +++++++++++++++++++++++++++++++++++-----
 3 files changed, 308 insertions(+), 62 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 78d018ea17..6ba1ca9524 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -121,6 +121,51 @@ public function flushAll(bool $async = false): bool;
 
     public function flushDB(bool $async = false): bool;
 
+    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): int;
+
+    public function geodist(string $key, string $src, string $dst, ?string $unit = null): array;
+
+    public function geohash(string $key, string $member, string ...$other_members): array;
+
+    public function geopos(string $key, string $member, string ...$other_members): array;
+
+    public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): array;
+
+    public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): array;
+
+    public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): array;
+
+    public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): array;
+
+	/** @return string|Redis */
+    public function get(string $key);
+
+    public function getAuth(): mixed;
+
+	/** @return int|Redis */
+    public function getBit(string $key, int $idx);
+
+    public function getDBNum(): int;
+
+    public function getHost(): string;
+
+    public function getLastError(): ?string;
+
+    public function getMode(): int;
+
+    public function getOption(int $option): mixed;
+
+    public function getPersistentID(): ?string;
+
+    public function getPort(): int;
+
+    public function getReadTimeout(): int;
+
+	/** @return string|Redis */
+    public function getset(string $key, mixed $value);
+
+    public function getTimeout(): int;
+
     public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
     /** @return bool|Redis */
@@ -135,9 +180,6 @@ public function psetex(string $key, int $expire, mixed $value);
 	/** @return bool|array|Redis */
     public function setnx(string $key, mixed $value);
 
-	/** @return string|Redis */
-    public function getset(string $key, mixed $value);
-
 	/** @return string|Redis */
     public function randomKey();
 
@@ -150,9 +192,6 @@ public function rename(string $key_src, string $key_dst);
 	/** @return bool|Redis */
     public function renameNx(string $key_src, string $key_dst);
 
-	/** @return string|Redis */
-    public function get(string $key);
-
 	/** @return string|Redis */
     public function ping(string $key = NULL);
 
@@ -167,6 +206,8 @@ public function incrByFloat(string $key, float $value);
 
     public function info(string $opt = null): array;
 
+    public function isConnected(): bool;
+
 	/** @return array|Redis */
     public function mget(array $keys);
 
@@ -197,9 +238,6 @@ public function getRange(string $key, int $start, int $end);
 	/** @return int|Redis */
     public function setRange(string $key, int $start, string $value);
 
-	/** @return int|Redis */
-    public function getBit(string $key, int $idx);
-
 	/** @return int|Redis */
     public function setBit(string $key, int $idx, bool $value);
 
@@ -233,6 +271,8 @@ public function rPushx(string $key, mixed $value);
 	/** @return string|Redis */
     public function lPop(string $key);
 
+    public function multi(int $value = Redis::MULTI): bool|Redis;
+
 	/** @return string|Redis */
     public function rPop(string $key);
 
@@ -251,25 +291,6 @@ public function popen(string $host, int $port = 26379, float $timeout = 0, strin
 
 /*
     TODO:
-    public function geoadd
-    public function geodist
-    public function geohash
-    public function geopos
-    public function georadius
-    public function georadius_ro
-    public function georadiusbymember
-    public function georadiusbymember_ro
-    public function getAuth
-    public function getDBNum
-    public function getHost
-    public function getLastError
-    public function getMode
-    public function getOption
-    public function getPersistentID
-    public function getPort
-    public function getReadTimeout
-    public function getSet
-    public function getTimeout
     public function hDel
     public function hExists
     public function hGet
@@ -285,7 +306,6 @@ public function hSetNx
     public function hStrLen
     public function hVals
     public function hscan
-    public function isConnected
     public function lLen
     public function lMove
     public function lSet
@@ -298,7 +318,6 @@ public function migrate
     public function move
     public function mset
     public function msetnx
-    public function multi
     public function object
     public function persist
     public function pexpire
diff --git a/redis_arginfo.h b/redis_arginfo.h
index f9df0deb1a..ad38f99b1f 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 19c4026366635e6429dd7a7c17fbe8cdfe6b00e0 */
+ * Stub hash: c1fa15abcac9f96b4a861afb93f61ea73c50a947 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -179,6 +179,84 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geoadd, 0, 4, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geodist, 0, 3, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geohash, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_geopos arginfo_class_Redis_geohash
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_georadius, 0, 5, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_georadius_ro arginfo_class_Redis_georadius
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_georadiusbymember, 0, 4, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_georadiusbymember_ro arginfo_class_Redis_georadiusbymember
+
+#define arginfo_class_Redis_get arginfo_class_Redis_decr
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getAuth, 0, 0, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_getDBNum arginfo_class_Redis_dbSize
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getHost, 0, 0, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getLastError, 0, 0, IS_STRING, 1)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_getMode arginfo_class_Redis_dbSize
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getOption, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_getPersistentID arginfo_class_Redis_getLastError
+
+#define arginfo_class_Redis_getPort arginfo_class_Redis_dbSize
+
+#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis_dbSize
+
+#define arginfo_class_Redis_getset arginfo_class_Redis_append
+
+#define arginfo_class_Redis_getTimeout arginfo_class_Redis_dbSize
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pconnect, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
@@ -205,8 +283,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_setnx arginfo_class_Redis_append
 
-#define arginfo_class_Redis_getset arginfo_class_Redis_append
-
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1)
@@ -220,8 +296,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
-#define arginfo_class_Redis_get arginfo_class_Redis_decr
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "NULL")
 ZEND_END_ARG_INFO()
@@ -239,6 +313,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_info, 0, 0, IS_ARRAY
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_isConnected arginfo_class_Redis_bgSave
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -267,11 +343,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
-ZEND_END_ARG_INFO()
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
@@ -300,6 +371,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_lPop arginfo_class_Redis_decr
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_multi, 0, 0, Redis, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_rPop arginfo_class_Redis_decr
 
 #define arginfo_class_Redis_open arginfo_class_Redis_pconnect
@@ -351,22 +426,42 @@ ZEND_METHOD(Redis, expire);
 ZEND_METHOD(Redis, expireAt);
 ZEND_METHOD(Redis, flushAll);
 ZEND_METHOD(Redis, flushDB);
+ZEND_METHOD(Redis, geoadd);
+ZEND_METHOD(Redis, geodist);
+ZEND_METHOD(Redis, geohash);
+ZEND_METHOD(Redis, geopos);
+ZEND_METHOD(Redis, georadius);
+ZEND_METHOD(Redis, georadius_ro);
+ZEND_METHOD(Redis, georadiusbymember);
+ZEND_METHOD(Redis, georadiusbymember_ro);
+ZEND_METHOD(Redis, get);
+ZEND_METHOD(Redis, getAuth);
+ZEND_METHOD(Redis, getBit);
+ZEND_METHOD(Redis, getDBNum);
+ZEND_METHOD(Redis, getHost);
+ZEND_METHOD(Redis, getLastError);
+ZEND_METHOD(Redis, getMode);
+ZEND_METHOD(Redis, getOption);
+ZEND_METHOD(Redis, getPersistentID);
+ZEND_METHOD(Redis, getPort);
+ZEND_METHOD(Redis, getReadTimeout);
+ZEND_METHOD(Redis, getset);
+ZEND_METHOD(Redis, getTimeout);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, psetex);
 ZEND_METHOD(Redis, setnx);
-ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, randomKey);
 ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
-ZEND_METHOD(Redis, get);
 ZEND_METHOD(Redis, ping);
 ZEND_METHOD(Redis, incr);
 ZEND_METHOD(Redis, incrBy);
 ZEND_METHOD(Redis, incrByFloat);
 ZEND_METHOD(Redis, info);
+ZEND_METHOD(Redis, isConnected);
 ZEND_METHOD(Redis, mget);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
@@ -375,7 +470,6 @@ ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, type);
 ZEND_METHOD(Redis, getRange);
 ZEND_METHOD(Redis, setRange);
-ZEND_METHOD(Redis, getBit);
 ZEND_METHOD(Redis, setBit);
 ZEND_METHOD(Redis, strlen);
 ZEND_METHOD(Redis, lPush);
@@ -384,6 +478,7 @@ ZEND_METHOD(Redis, lInsert);
 ZEND_METHOD(Redis, lPushx);
 ZEND_METHOD(Redis, rPushx);
 ZEND_METHOD(Redis, lPop);
+ZEND_METHOD(Redis, multi);
 ZEND_METHOD(Redis, rPop);
 
 
@@ -433,22 +528,42 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geodist, arginfo_class_Redis_geodist, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geohash, arginfo_class_Redis_geohash, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geopos, arginfo_class_Redis_geopos, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, georadius, arginfo_class_Redis_georadius, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, georadius_ro, arginfo_class_Redis_georadius_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, georadiusbymember, arginfo_class_Redis_georadiusbymember, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, georadiusbymember_ro, arginfo_class_Redis_georadiusbymember_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getDBNum, arginfo_class_Redis_getDBNum, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getHost, arginfo_class_Redis_getHost, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getLastError, arginfo_class_Redis_getLastError, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getMode, arginfo_class_Redis_getMode, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, randomKey, arginfo_class_Redis_randomKey, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
@@ -457,7 +572,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
@@ -466,6 +580,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 7239588c52..b762ffde1c 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 19c4026366635e6429dd7a7c17fbe8cdfe6b00e0 */
+ * Stub hash: c1fa15abcac9f96b4a861afb93f61ea73c50a947 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -167,6 +167,81 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geoadd, 0, 0, 4)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, lng)
+	ZEND_ARG_INFO(0, lat)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_VARIADIC_INFO(0, other_triples)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geodist, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, unit)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geohash, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_VARIADIC_INFO(0, other_members)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_geopos arginfo_class_Redis_geohash
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_georadius, 0, 0, 5)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, lng)
+	ZEND_ARG_INFO(0, lat)
+	ZEND_ARG_INFO(0, radius)
+	ZEND_ARG_INFO(0, unit)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_georadius_ro arginfo_class_Redis_georadius
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_georadiusbymember, 0, 0, 4)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_INFO(0, radius)
+	ZEND_ARG_INFO(0, unit)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_georadiusbymember_ro arginfo_class_Redis_georadiusbymember
+
+#define arginfo_class_Redis_get arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_getAuth arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, idx)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_getDBNum arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_getHost arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_getLastError arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_getMode arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getOption, 0, 0, 1)
+	ZEND_ARG_INFO(0, option)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_getPersistentID arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_getPort arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_getset arginfo_class_Redis_append
+
+#define arginfo_class_Redis_getTimeout arginfo_class_Redis___construct
+
 #define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
@@ -185,8 +260,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_setnx arginfo_class_Redis_append
 
-#define arginfo_class_Redis_getset arginfo_class_Redis_append
-
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1)
@@ -200,8 +273,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
-#define arginfo_class_Redis_get arginfo_class_Redis__prefix
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_INFO(0, key)
 ZEND_END_ARG_INFO()
@@ -216,6 +287,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_info, 0, 0, 0)
 	ZEND_ARG_INFO(0, opt)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_isConnected arginfo_class_Redis___construct
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_INFO(0, keys)
 ZEND_END_ARG_INFO()
@@ -244,11 +317,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, idx)
-ZEND_END_ARG_INFO()
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, idx)
@@ -277,6 +345,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_lPop arginfo_class_Redis__prefix
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_multi, 0, 0, 0)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_rPop arginfo_class_Redis__prefix
 
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
@@ -328,22 +400,42 @@ ZEND_METHOD(Redis, expire);
 ZEND_METHOD(Redis, expireAt);
 ZEND_METHOD(Redis, flushAll);
 ZEND_METHOD(Redis, flushDB);
+ZEND_METHOD(Redis, geoadd);
+ZEND_METHOD(Redis, geodist);
+ZEND_METHOD(Redis, geohash);
+ZEND_METHOD(Redis, geopos);
+ZEND_METHOD(Redis, georadius);
+ZEND_METHOD(Redis, georadius_ro);
+ZEND_METHOD(Redis, georadiusbymember);
+ZEND_METHOD(Redis, georadiusbymember_ro);
+ZEND_METHOD(Redis, get);
+ZEND_METHOD(Redis, getAuth);
+ZEND_METHOD(Redis, getBit);
+ZEND_METHOD(Redis, getDBNum);
+ZEND_METHOD(Redis, getHost);
+ZEND_METHOD(Redis, getLastError);
+ZEND_METHOD(Redis, getMode);
+ZEND_METHOD(Redis, getOption);
+ZEND_METHOD(Redis, getPersistentID);
+ZEND_METHOD(Redis, getPort);
+ZEND_METHOD(Redis, getReadTimeout);
+ZEND_METHOD(Redis, getset);
+ZEND_METHOD(Redis, getTimeout);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, psetex);
 ZEND_METHOD(Redis, setnx);
-ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, randomKey);
 ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
-ZEND_METHOD(Redis, get);
 ZEND_METHOD(Redis, ping);
 ZEND_METHOD(Redis, incr);
 ZEND_METHOD(Redis, incrBy);
 ZEND_METHOD(Redis, incrByFloat);
 ZEND_METHOD(Redis, info);
+ZEND_METHOD(Redis, isConnected);
 ZEND_METHOD(Redis, mget);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
@@ -352,7 +444,6 @@ ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, type);
 ZEND_METHOD(Redis, getRange);
 ZEND_METHOD(Redis, setRange);
-ZEND_METHOD(Redis, getBit);
 ZEND_METHOD(Redis, setBit);
 ZEND_METHOD(Redis, strlen);
 ZEND_METHOD(Redis, lPush);
@@ -361,6 +452,7 @@ ZEND_METHOD(Redis, lInsert);
 ZEND_METHOD(Redis, lPushx);
 ZEND_METHOD(Redis, rPushx);
 ZEND_METHOD(Redis, lPop);
+ZEND_METHOD(Redis, multi);
 ZEND_METHOD(Redis, rPop);
 
 
@@ -410,22 +502,42 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geodist, arginfo_class_Redis_geodist, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geohash, arginfo_class_Redis_geohash, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geopos, arginfo_class_Redis_geopos, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, georadius, arginfo_class_Redis_georadius, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, georadius_ro, arginfo_class_Redis_georadius_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, georadiusbymember, arginfo_class_Redis_georadiusbymember, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, georadiusbymember_ro, arginfo_class_Redis_georadiusbymember_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getDBNum, arginfo_class_Redis_getDBNum, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getHost, arginfo_class_Redis_getHost, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getLastError, arginfo_class_Redis_getLastError, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getMode, arginfo_class_Redis_getMode, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, randomKey, arginfo_class_Redis_randomKey, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
@@ -434,7 +546,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
@@ -443,6 +554,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)

From 365d5f46572750b8542f020c2902ea4955efc031 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Sep 2021 13:32:13 +0300
Subject: [PATCH 1489/1986] Add stub-based arginfo for Redis h

---
 redis.stub.php         |  74 +++++++++++--------
 redis_arginfo.h        | 157 ++++++++++++++++++++++++++++++++++-------
 redis_legacy_arginfo.h | 128 +++++++++++++++++++++++++++------
 3 files changed, 281 insertions(+), 78 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 6ba1ca9524..ee8c18c910 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -166,6 +166,49 @@ public function getset(string $key, mixed $value);
 
     public function getTimeout(): int;
 
+    public function hDel(string $key, string $member, string ...$other_members): int;
+
+    public function hExists(string $key, string $member): bool;
+
+    public function hGet(string $key, string $member): string;
+
+    public function hGetAll(string $key): array;
+
+    public function hIncrBy(string $key, string $member, int $value): int;
+
+    public function hIncrByFloat(string $key, string $member, float $value): float;
+
+    public function hKeys(string $key): array;
+
+    public function hLen(string $key): int;
+
+    public function hMget(string $key, array $keys): array;
+
+    public function hMset(string $key, array $keyvals): bool;
+
+    public function hSet(string $key, string $member, string $value): int;
+
+    public function hSetNx(string $key, string $member, string $value): int;
+
+    public function hStrLen(string $key, string $member): int;
+
+    public function hVals(string $key): array;
+
+    public function hscan(string $key, int $iterator, ?string $pattern = null, int $count = 0): bool|array;
+
+	/** @return int|Redis */
+    public function incr(string $key);
+
+	/** @return int|Redis */
+    public function incrBy(string $key, int $value);
+
+	/** @return int|Redis */
+    public function incrByFloat(string $key, float $value);
+
+    public function info(string $opt = null): array;
+
+    public function isConnected(): bool;
+
     public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
     /** @return bool|Redis */
@@ -195,19 +238,6 @@ public function renameNx(string $key_src, string $key_dst);
 	/** @return string|Redis */
     public function ping(string $key = NULL);
 
-	/** @return int|Redis */
-    public function incr(string $key);
-
-	/** @return int|Redis */
-    public function incrBy(string $key, int $value);
-
-	/** @return int|Redis */
-    public function incrByFloat(string $key, float $value);
-
-    public function info(string $opt = null): array;
-
-    public function isConnected(): bool;
-
 	/** @return array|Redis */
     public function mget(array $keys);
 
@@ -282,6 +312,8 @@ public function rPop(string $key);
      */
     public function open(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
+    public function pipeline(): bool|Redis;
+
     /**
      * @deprecated
      * @alias Redis::pconnect
@@ -291,21 +323,6 @@ public function popen(string $host, int $port = 26379, float $timeout = 0, strin
 
 /*
     TODO:
-    public function hDel
-    public function hExists
-    public function hGet
-    public function hGetAll
-    public function hIncrBy
-    public function hIncrByFloat
-    public function hKeys
-    public function hLen
-    public function hMget
-    public function hMset
-    public function hSet
-    public function hSetNx
-    public function hStrLen
-    public function hVals
-    public function hscan
     public function lLen
     public function lMove
     public function lSet
@@ -325,7 +342,6 @@ public function pexpireAt
     public function pfadd
     public function pfcount
     public function pfmerge
-    public function pipeline
     public function psubscribe
     public function pttl
     public function publish
diff --git a/redis_arginfo.h b/redis_arginfo.h
index ad38f99b1f..b2f65ffdbb 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c1fa15abcac9f96b4a861afb93f61ea73c50a947 */
+ * Stub hash: edc5e7a7829cb72602961fdc621545bbd335444e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -257,6 +257,91 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getTimeout arginfo_class_Redis_dbSize
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hDel, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hExists, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGet, 0, 2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGetAll, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hIncrBy, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hIncrByFloat, 0, 3, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_hKeys arginfo_class_Redis_hGetAll
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hLen, 0, 1, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hMget, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hMset, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keyvals, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hSet, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_hSetNx arginfo_class_Redis_hSet
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hStrLen, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_hVals arginfo_class_Redis_hGetAll
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_incr arginfo_class_Redis_decr
+
+#define arginfo_class_Redis_incrBy arginfo_class_Redis_decrBy
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrByFloat, 0, 0, 2)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_info, 0, 0, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_isConnected arginfo_class_Redis_bgSave
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pconnect, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
@@ -300,21 +385,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_incr arginfo_class_Redis_decr
-
-#define arginfo_class_Redis_incrBy arginfo_class_Redis_decrBy
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrByFloat, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_info, 0, 0, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_isConnected arginfo_class_Redis_bgSave
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -379,6 +449,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_open arginfo_class_Redis_pconnect
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_popen arginfo_class_Redis_pconnect
 
 
@@ -447,6 +520,26 @@ ZEND_METHOD(Redis, getPort);
 ZEND_METHOD(Redis, getReadTimeout);
 ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, getTimeout);
+ZEND_METHOD(Redis, hDel);
+ZEND_METHOD(Redis, hExists);
+ZEND_METHOD(Redis, hGet);
+ZEND_METHOD(Redis, hGetAll);
+ZEND_METHOD(Redis, hIncrBy);
+ZEND_METHOD(Redis, hIncrByFloat);
+ZEND_METHOD(Redis, hKeys);
+ZEND_METHOD(Redis, hLen);
+ZEND_METHOD(Redis, hMget);
+ZEND_METHOD(Redis, hMset);
+ZEND_METHOD(Redis, hSet);
+ZEND_METHOD(Redis, hSetNx);
+ZEND_METHOD(Redis, hStrLen);
+ZEND_METHOD(Redis, hVals);
+ZEND_METHOD(Redis, hscan);
+ZEND_METHOD(Redis, incr);
+ZEND_METHOD(Redis, incrBy);
+ZEND_METHOD(Redis, incrByFloat);
+ZEND_METHOD(Redis, info);
+ZEND_METHOD(Redis, isConnected);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
@@ -457,11 +550,6 @@ ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
 ZEND_METHOD(Redis, ping);
-ZEND_METHOD(Redis, incr);
-ZEND_METHOD(Redis, incrBy);
-ZEND_METHOD(Redis, incrByFloat);
-ZEND_METHOD(Redis, info);
-ZEND_METHOD(Redis, isConnected);
 ZEND_METHOD(Redis, mget);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
@@ -480,6 +568,7 @@ ZEND_METHOD(Redis, rPushx);
 ZEND_METHOD(Redis, lPop);
 ZEND_METHOD(Redis, multi);
 ZEND_METHOD(Redis, rPop);
+ZEND_METHOD(Redis, pipeline);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -549,6 +638,26 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hDel, arginfo_class_Redis_hDel, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hGetAll, arginfo_class_Redis_hGetAll, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hIncrBy, arginfo_class_Redis_hIncrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hIncrByFloat, arginfo_class_Redis_hIncrByFloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hKeys, arginfo_class_Redis_hKeys, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hLen, arginfo_class_Redis_hLen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hMget, arginfo_class_Redis_hMget, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hMset, arginfo_class_Redis_hMset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hSet, arginfo_class_Redis_hSet, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
@@ -559,11 +668,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
@@ -583,6 +687,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_FE_END
 };
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index b762ffde1c..1b137bbe0b 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c1fa15abcac9f96b4a861afb93f61ea73c50a947 */
+ * Stub hash: edc5e7a7829cb72602961fdc621545bbd335444e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -242,6 +242,66 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getTimeout arginfo_class_Redis___construct
 
+#define arginfo_class_Redis_hDel arginfo_class_Redis_geohash
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hExists, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_hGet arginfo_class_Redis_hExists
+
+#define arginfo_class_Redis_hGetAll arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hIncrBy, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_hIncrByFloat arginfo_class_Redis_hIncrBy
+
+#define arginfo_class_Redis_hKeys arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_hLen arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMget, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, keys)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMset, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, keyvals)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_hSet arginfo_class_Redis_hIncrBy
+
+#define arginfo_class_Redis_hSetNx arginfo_class_Redis_hIncrBy
+
+#define arginfo_class_Redis_hStrLen arginfo_class_Redis_hExists
+
+#define arginfo_class_Redis_hVals arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, iterator)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_incr arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_incrBy arginfo_class_Redis_append
+
+#define arginfo_class_Redis_incrByFloat arginfo_class_Redis_append
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_info, 0, 0, 0)
+	ZEND_ARG_INFO(0, opt)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_isConnected arginfo_class_Redis___construct
+
 #define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
@@ -277,18 +337,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_INFO(0, key)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_incr arginfo_class_Redis__prefix
-
-#define arginfo_class_Redis_incrBy arginfo_class_Redis_append
-
-#define arginfo_class_Redis_incrByFloat arginfo_class_Redis_append
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_info, 0, 0, 0)
-	ZEND_ARG_INFO(0, opt)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_isConnected arginfo_class_Redis___construct
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_INFO(0, keys)
 ZEND_END_ARG_INFO()
@@ -353,6 +401,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
 
+#define arginfo_class_Redis_pipeline arginfo_class_Redis___construct
+
 #define arginfo_class_Redis_popen arginfo_class_Redis_connect
 
 
@@ -421,6 +471,26 @@ ZEND_METHOD(Redis, getPort);
 ZEND_METHOD(Redis, getReadTimeout);
 ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, getTimeout);
+ZEND_METHOD(Redis, hDel);
+ZEND_METHOD(Redis, hExists);
+ZEND_METHOD(Redis, hGet);
+ZEND_METHOD(Redis, hGetAll);
+ZEND_METHOD(Redis, hIncrBy);
+ZEND_METHOD(Redis, hIncrByFloat);
+ZEND_METHOD(Redis, hKeys);
+ZEND_METHOD(Redis, hLen);
+ZEND_METHOD(Redis, hMget);
+ZEND_METHOD(Redis, hMset);
+ZEND_METHOD(Redis, hSet);
+ZEND_METHOD(Redis, hSetNx);
+ZEND_METHOD(Redis, hStrLen);
+ZEND_METHOD(Redis, hVals);
+ZEND_METHOD(Redis, hscan);
+ZEND_METHOD(Redis, incr);
+ZEND_METHOD(Redis, incrBy);
+ZEND_METHOD(Redis, incrByFloat);
+ZEND_METHOD(Redis, info);
+ZEND_METHOD(Redis, isConnected);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
@@ -431,11 +501,6 @@ ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
 ZEND_METHOD(Redis, ping);
-ZEND_METHOD(Redis, incr);
-ZEND_METHOD(Redis, incrBy);
-ZEND_METHOD(Redis, incrByFloat);
-ZEND_METHOD(Redis, info);
-ZEND_METHOD(Redis, isConnected);
 ZEND_METHOD(Redis, mget);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
@@ -454,6 +519,7 @@ ZEND_METHOD(Redis, rPushx);
 ZEND_METHOD(Redis, lPop);
 ZEND_METHOD(Redis, multi);
 ZEND_METHOD(Redis, rPop);
+ZEND_METHOD(Redis, pipeline);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -523,6 +589,26 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hDel, arginfo_class_Redis_hDel, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hGetAll, arginfo_class_Redis_hGetAll, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hIncrBy, arginfo_class_Redis_hIncrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hIncrByFloat, arginfo_class_Redis_hIncrByFloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hKeys, arginfo_class_Redis_hKeys, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hLen, arginfo_class_Redis_hLen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hMget, arginfo_class_Redis_hMget, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hMset, arginfo_class_Redis_hMset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hSet, arginfo_class_Redis_hSet, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
@@ -533,11 +619,6 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
@@ -557,6 +638,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_FE_END
 };

From 7009fa02caaad97fc353fb11aab2d6c0c1995661 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Sep 2021 16:00:19 +0300
Subject: [PATCH 1490/1986] Add stub-based arginfo for Redis l-m

---
 redis.stub.php         | 110 +++++++++++++----------
 redis_arginfo.h        | 200 ++++++++++++++++++++++++++++++-----------
 redis_legacy_arginfo.h | 187 +++++++++++++++++++++++++++-----------
 3 files changed, 343 insertions(+), 154 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index ee8c18c910..cef9e559ed 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -159,6 +159,9 @@ public function getPersistentID(): ?string;
 
     public function getPort(): int;
 
+	/** @return string|Redis */
+    public function getRange(string $key, int $start, int $end);
+
     public function getReadTimeout(): int;
 
 	/** @return string|Redis */
@@ -209,6 +212,63 @@ public function info(string $opt = null): array;
 
     public function isConnected(): bool;
 
+    /**
+     * @param mixed $elements
+     * @return int|Redis
+     */
+    public function lInsert(string $key, string $pos, mixed $pivot, mixed $value);
+
+
+    public function lLen(string $key): int;
+
+    public function lMove(string $src, string $dst, string $wherefrom, string $whereto): string;
+
+	/** @return string|Redis */
+    public function lPop(string $key);
+
+    /**
+     * @param mixed $elements
+     * @return int|Redis
+     */
+    public function lPush(string $key, ...$elements);
+
+    /**
+     * @param mixed $elements
+     * @return int|Redis
+     */
+    public function rPush(string $key, ...$elements);
+
+	/** @return int|Redis */
+    public function lPushx(string $key, mixed $value);
+
+	/** @return int|Redis */
+    public function rPushx(string $key, mixed $value);
+
+    public function lSet(string $key, int $index, string $value): bool;
+
+    public function lastSave(): int;
+
+    public function lindex(string $key, int $index): string;
+
+    public function lrange(string $key, int $start , int $end): array;
+
+    public function lrem(string $key, string $value, int $count = 0): bool;
+
+    public function ltrim(string $key, int $start , int $end): bool;
+
+	/** @return array|Redis */
+    public function mget(array $keys);
+
+    public function migrate(string $host, int $port, string $key, string $dst, int $timeout, bool $copy = false, bool $replace = false): bool;
+
+    public function move(string $key, int $index): bool;
+
+    public function mset(array $key_values): bool;
+
+    public function msetnx(array $key_values): int;
+
+    public function multi(int $value = Redis::MULTI): bool|Redis;
+
     public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
     /** @return bool|Redis */
@@ -238,9 +298,6 @@ public function renameNx(string $key_src, string $key_dst);
 	/** @return string|Redis */
     public function ping(string $key = NULL);
 
-	/** @return array|Redis */
-    public function mget(array $keys);
-
     /**
      * @param string $otherkeys
      * @return int|Redis
@@ -262,9 +319,6 @@ public function keys(string $pattern);
 	/** @return int|Redis */
     public function type(string $key);
 
-	/** @return string|Redis */
-    public function getRange(string $key, int $start, int $end);
-
 	/** @return int|Redis */
     public function setRange(string $key, int $start, string $value);
 
@@ -274,35 +328,6 @@ public function setBit(string $key, int $idx, bool $value);
 	/** @return int|Redis */
     public function strlen(string $key);
 
-    /**
-     * @param mixed $elements
-     * @return int|Redis
-     */
-    public function lPush(string $key, ...$elements);
-
-    /**
-     * @param mixed $elements
-     * @return int|Redis
-     */
-    public function rPush(string $key, ...$elements);
-
-    /**
-     * @param mixed $elements
-     * @return int|Redis
-     */
-    public function lInsert(string $key, string $pos, mixed $pivot, mixed $value);
-
-	/** @return int|Redis */
-    public function lPushx(string $key, mixed $value);
-
-	/** @return int|Redis */
-    public function rPushx(string $key, mixed $value);
-
-	/** @return string|Redis */
-    public function lPop(string $key);
-
-    public function multi(int $value = Redis::MULTI): bool|Redis;
-
 	/** @return string|Redis */
     public function rPop(string $key);
 
@@ -319,22 +344,12 @@ public function pipeline(): bool|Redis;
      * @alias Redis::pconnect
      */
     public function popen(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
+
+    public function publish(string $channel, string $message): int;
 }
 
 /*
     TODO:
-    public function lLen
-    public function lMove
-    public function lSet
-    public function lastSave
-    public function lindex
-    public function lrange
-    public function lrem
-    public function ltrim
-    public function migrate
-    public function move
-    public function mset
-    public function msetnx
     public function object
     public function persist
     public function pexpire
@@ -344,7 +359,6 @@ public function pfcount
     public function pfmerge
     public function psubscribe
     public function pttl
-    public function publish
     public function pubsub
     public function punsubscribe
     public function rawcommand
diff --git a/redis_arginfo.h b/redis_arginfo.h
index b2f65ffdbb..ef1a827921 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: edc5e7a7829cb72602961fdc621545bbd335444e */
+ * Stub hash: aac36713ff8410d09d8d45a9388e39603422cf08 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -251,6 +251,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getPort arginfo_class_Redis_dbSize
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_getReadTimeout arginfo_class_Redis_dbSize
 
 #define arginfo_class_Redis_getset arginfo_class_Redis_append
@@ -342,6 +348,97 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_isConnected arginfo_class_Redis_bgSave
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, pos, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, pivot, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_lLen arginfo_class_Redis_hLen
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lMove, 0, 4, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_lPop arginfo_class_Redis_decr
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_INFO(0, elements)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_rPush arginfo_class_Redis_lPush
+
+#define arginfo_class_Redis_lPushx arginfo_class_Redis_append
+
+#define arginfo_class_Redis_rPushx arginfo_class_Redis_append
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lSet, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_lastSave arginfo_class_Redis_dbSize
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lindex, 0, 2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lrange, 0, 3, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lrem, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_ltrim, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_migrate, 0, 5, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, copy, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, replace, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_move, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_mset, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_msetnx, 0, 1, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_multi, 0, 0, Redis, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pconnect, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
@@ -385,10 +482,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
-ZEND_END_ARG_INFO()
-
 #define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
 #define arginfo_class_Redis_watch arginfo_class_Redis_del
@@ -401,12 +494,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_type arginfo_class_Redis_decr
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
-ZEND_END_ARG_INFO()
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
@@ -421,30 +508,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_strlen arginfo_class_Redis_decr
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_INFO(0, elements)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_rPush arginfo_class_Redis_lPush
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, pos, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, pivot, IS_MIXED, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_lPushx arginfo_class_Redis_append
-
-#define arginfo_class_Redis_rPushx arginfo_class_Redis_append
-
-#define arginfo_class_Redis_lPop arginfo_class_Redis_decr
-
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_multi, 0, 0, Redis, MAY_BE_BOOL)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI")
-ZEND_END_ARG_INFO()
-
 #define arginfo_class_Redis_rPop arginfo_class_Redis_decr
 
 #define arginfo_class_Redis_open arginfo_class_Redis_pconnect
@@ -454,6 +517,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_popen arginfo_class_Redis_pconnect
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_publish, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -517,6 +585,7 @@ ZEND_METHOD(Redis, getMode);
 ZEND_METHOD(Redis, getOption);
 ZEND_METHOD(Redis, getPersistentID);
 ZEND_METHOD(Redis, getPort);
+ZEND_METHOD(Redis, getRange);
 ZEND_METHOD(Redis, getReadTimeout);
 ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, getTimeout);
@@ -540,6 +609,26 @@ ZEND_METHOD(Redis, incrBy);
 ZEND_METHOD(Redis, incrByFloat);
 ZEND_METHOD(Redis, info);
 ZEND_METHOD(Redis, isConnected);
+ZEND_METHOD(Redis, lInsert);
+ZEND_METHOD(Redis, lLen);
+ZEND_METHOD(Redis, lMove);
+ZEND_METHOD(Redis, lPop);
+ZEND_METHOD(Redis, lPush);
+ZEND_METHOD(Redis, rPush);
+ZEND_METHOD(Redis, lPushx);
+ZEND_METHOD(Redis, rPushx);
+ZEND_METHOD(Redis, lSet);
+ZEND_METHOD(Redis, lastSave);
+ZEND_METHOD(Redis, lindex);
+ZEND_METHOD(Redis, lrange);
+ZEND_METHOD(Redis, lrem);
+ZEND_METHOD(Redis, ltrim);
+ZEND_METHOD(Redis, mget);
+ZEND_METHOD(Redis, migrate);
+ZEND_METHOD(Redis, move);
+ZEND_METHOD(Redis, mset);
+ZEND_METHOD(Redis, msetnx);
+ZEND_METHOD(Redis, multi);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
@@ -550,25 +639,17 @@ ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
 ZEND_METHOD(Redis, ping);
-ZEND_METHOD(Redis, mget);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
 ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, type);
-ZEND_METHOD(Redis, getRange);
 ZEND_METHOD(Redis, setRange);
 ZEND_METHOD(Redis, setBit);
 ZEND_METHOD(Redis, strlen);
-ZEND_METHOD(Redis, lPush);
-ZEND_METHOD(Redis, rPush);
-ZEND_METHOD(Redis, lInsert);
-ZEND_METHOD(Redis, lPushx);
-ZEND_METHOD(Redis, rPushx);
-ZEND_METHOD(Redis, lPop);
-ZEND_METHOD(Redis, multi);
 ZEND_METHOD(Redis, rPop);
 ZEND_METHOD(Redis, pipeline);
+ZEND_METHOD(Redis, publish);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -635,6 +716,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
@@ -658,6 +740,26 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lSet, arginfo_class_Redis_lSet, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lastSave, arginfo_class_Redis_lastSave, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lindex, arginfo_class_Redis_lindex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lrange, arginfo_class_Redis_lrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lrem, arginfo_class_Redis_lrem, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, ltrim, arginfo_class_Redis_ltrim, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, migrate, arginfo_class_Redis_migrate, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, move, arginfo_class_Redis_move, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, mset, arginfo_class_Redis_mset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, msetnx, arginfo_class_Redis_msetnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
@@ -668,26 +770,18 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, publish, arginfo_class_Redis_publish, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 1b137bbe0b..dff1247c94 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: edc5e7a7829cb72602961fdc621545bbd335444e */
+ * Stub hash: aac36713ff8410d09d8d45a9388e39603422cf08 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -236,6 +236,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getPort arginfo_class_Redis___construct
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_getReadTimeout arginfo_class_Redis___construct
 
 #define arginfo_class_Redis_getset arginfo_class_Redis_append
@@ -302,6 +308,84 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_isConnected arginfo_class_Redis___construct
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, pos)
+	ZEND_ARG_INFO(0, pivot)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_lLen arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lMove, 0, 0, 4)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, wherefrom)
+	ZEND_ARG_INFO(0, whereto)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_lPop arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_VARIADIC_INFO(0, elements)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_rPush arginfo_class_Redis_lPush
+
+#define arginfo_class_Redis_lPushx arginfo_class_Redis_append
+
+#define arginfo_class_Redis_rPushx arginfo_class_Redis_append
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lSet, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, index)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_lastSave arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lindex, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, index)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_lrange arginfo_class_Redis_getRange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lrem, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_ltrim arginfo_class_Redis_getRange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
+	ZEND_ARG_INFO(0, keys)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_migrate, 0, 0, 5)
+	ZEND_ARG_INFO(0, host)
+	ZEND_ARG_INFO(0, port)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, copy)
+	ZEND_ARG_INFO(0, replace)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_move arginfo_class_Redis_lindex
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mset, 0, 0, 1)
+	ZEND_ARG_INFO(0, key_values)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_msetnx arginfo_class_Redis_mset
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_multi, 0, 0, 0)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
@@ -337,10 +421,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_INFO(0, key)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
-	ZEND_ARG_INFO(0, keys)
-ZEND_END_ARG_INFO()
-
 #define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
 #define arginfo_class_Redis_watch arginfo_class_Redis_del
@@ -353,12 +433,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_type arginfo_class_Redis__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, start)
-	ZEND_ARG_INFO(0, end)
-ZEND_END_ARG_INFO()
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, start)
@@ -373,30 +447,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_strlen arginfo_class_Redis__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_VARIADIC_INFO(0, elements)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_rPush arginfo_class_Redis_lPush
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, pos)
-	ZEND_ARG_INFO(0, pivot)
-	ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_lPushx arginfo_class_Redis_append
-
-#define arginfo_class_Redis_rPushx arginfo_class_Redis_append
-
-#define arginfo_class_Redis_lPop arginfo_class_Redis__prefix
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_multi, 0, 0, 0)
-	ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
 #define arginfo_class_Redis_rPop arginfo_class_Redis__prefix
 
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
@@ -405,6 +455,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_popen arginfo_class_Redis_connect
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_publish, 0, 0, 2)
+	ZEND_ARG_INFO(0, channel)
+	ZEND_ARG_INFO(0, message)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -468,6 +523,7 @@ ZEND_METHOD(Redis, getMode);
 ZEND_METHOD(Redis, getOption);
 ZEND_METHOD(Redis, getPersistentID);
 ZEND_METHOD(Redis, getPort);
+ZEND_METHOD(Redis, getRange);
 ZEND_METHOD(Redis, getReadTimeout);
 ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, getTimeout);
@@ -491,6 +547,26 @@ ZEND_METHOD(Redis, incrBy);
 ZEND_METHOD(Redis, incrByFloat);
 ZEND_METHOD(Redis, info);
 ZEND_METHOD(Redis, isConnected);
+ZEND_METHOD(Redis, lInsert);
+ZEND_METHOD(Redis, lLen);
+ZEND_METHOD(Redis, lMove);
+ZEND_METHOD(Redis, lPop);
+ZEND_METHOD(Redis, lPush);
+ZEND_METHOD(Redis, rPush);
+ZEND_METHOD(Redis, lPushx);
+ZEND_METHOD(Redis, rPushx);
+ZEND_METHOD(Redis, lSet);
+ZEND_METHOD(Redis, lastSave);
+ZEND_METHOD(Redis, lindex);
+ZEND_METHOD(Redis, lrange);
+ZEND_METHOD(Redis, lrem);
+ZEND_METHOD(Redis, ltrim);
+ZEND_METHOD(Redis, mget);
+ZEND_METHOD(Redis, migrate);
+ZEND_METHOD(Redis, move);
+ZEND_METHOD(Redis, mset);
+ZEND_METHOD(Redis, msetnx);
+ZEND_METHOD(Redis, multi);
 ZEND_METHOD(Redis, pconnect);
 ZEND_METHOD(Redis, set);
 ZEND_METHOD(Redis, setex);
@@ -501,25 +577,17 @@ ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
 ZEND_METHOD(Redis, ping);
-ZEND_METHOD(Redis, mget);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
 ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, type);
-ZEND_METHOD(Redis, getRange);
 ZEND_METHOD(Redis, setRange);
 ZEND_METHOD(Redis, setBit);
 ZEND_METHOD(Redis, strlen);
-ZEND_METHOD(Redis, lPush);
-ZEND_METHOD(Redis, rPush);
-ZEND_METHOD(Redis, lInsert);
-ZEND_METHOD(Redis, lPushx);
-ZEND_METHOD(Redis, rPushx);
-ZEND_METHOD(Redis, lPop);
-ZEND_METHOD(Redis, multi);
 ZEND_METHOD(Redis, rPop);
 ZEND_METHOD(Redis, pipeline);
+ZEND_METHOD(Redis, publish);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -586,6 +654,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
@@ -609,6 +678,26 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lSet, arginfo_class_Redis_lSet, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lastSave, arginfo_class_Redis_lastSave, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lindex, arginfo_class_Redis_lindex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lrange, arginfo_class_Redis_lrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lrem, arginfo_class_Redis_lrem, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, ltrim, arginfo_class_Redis_ltrim, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, migrate, arginfo_class_Redis_migrate, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, move, arginfo_class_Redis_move, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, mset, arginfo_class_Redis_mset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, msetnx, arginfo_class_Redis_msetnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
@@ -619,26 +708,18 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, mget, arginfo_class_Redis_mget, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, rPushx, arginfo_class_Redis_rPushx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC)
 	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, publish, arginfo_class_Redis_publish, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 2b71ab7639695104638eb6b661ead8d173b34412 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Sep 2021 16:31:27 +0300
Subject: [PATCH 1491/1986] Add stub-based arginfo for Redis o-r

---
 redis.stub.php         | 108 +++++++++++++-----------
 redis_arginfo.h        | 187 ++++++++++++++++++++++++++++++-----------
 redis_legacy_arginfo.h | 175 +++++++++++++++++++++++++++-----------
 3 files changed, 326 insertions(+), 144 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index cef9e559ed..5c1eb3bb68 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -104,6 +104,9 @@ public function discard(): bool;
 
     public function dump(string $key): string;
 
+	/** @return string|Redis */
+    public function echo(string $str);
+
     public function eval(string $script, array $keys = null, int $num_keys = 0): mixed;
 
     public function evalsha(string $sha1, array $keys = null, int $num_keys = 0): mixed;
@@ -212,6 +215,9 @@ public function info(string $opt = null): array;
 
     public function isConnected(): bool;
 
+	/** @return array|Redis */
+    public function keys(string $pattern);
+
     /**
      * @param mixed $elements
      * @return int|Redis
@@ -269,25 +275,58 @@ public function msetnx(array $key_values): int;
 
     public function multi(int $value = Redis::MULTI): bool|Redis;
 
+    public function object(string $key): int|string;
+    /**
+     * @deprecated
+     * @alias Redis::connect
+     */
+    public function open(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
+
     public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
-    /** @return bool|Redis */
-    public function set(string $key, mixed $value, mixed $opt = NULL);
+public function persist(string $key): bool;
 
-    /** @return bool|Redis */
-    public function setex(string $key, int $expire, mixed $value);
+    public function pexpire(string $key, int $timeout): bool;
+
+    public function pexpireAt(string $key, int $timestamp): bool;
+
+    public function pfadd(string $key, array $elements): int;
+
+    public function pfcount(string $key): int;
+
+    public function pfmerge(string $dst, array $keys): bool;
+
+	/** @return string|Redis */
+    public function ping(string $key = NULL);
+
+    public function pipeline(): bool|Redis;
+
+    /**
+     * @deprecated
+     * @alias Redis::pconnect
+     */
+    public function popen(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
     /** @return bool|Redis */
     public function psetex(string $key, int $expire, mixed $value);
 
-	/** @return bool|array|Redis */
-    public function setnx(string $key, mixed $value);
+    public function psubscribe(array $patterns): void;
+
+    public function pttl(string $key): int;
+
+    public function publish(string $channel, string $message): int;
+
+    public function pubsub(string $command, mixed $arg = null): mixed;
+
+    public function punsubscribe(array $patterns): array;
 
 	/** @return string|Redis */
-    public function randomKey();
+    public function rPop(string $key);
 
 	/** @return string|Redis */
-    public function echo(string $str);
+    public function randomKey();
+
+    public function rawcommand(string $command, mixed ...$args): mixed;
 
 	/** @return bool|Redis */
     public function rename(string $key_src, string $key_dst);
@@ -295,8 +334,20 @@ public function rename(string $key_src, string $key_dst);
 	/** @return bool|Redis */
     public function renameNx(string $key_src, string $key_dst);
 
-	/** @return string|Redis */
-    public function ping(string $key = NULL);
+    public function restore(string $key, int $timeout, string $value): bool;
+
+    public function role(): mixed;
+
+    public function rpoplpush(string $src, string $dst): string;
+
+    /** @return bool|Redis */
+    public function set(string $key, mixed $value, mixed $opt = NULL);
+
+    /** @return bool|Redis */
+    public function setex(string $key, int $expire, mixed $value);
+
+	/** @return bool|array|Redis */
+    public function setnx(string $key, mixed $value);
 
     /**
      * @param string $otherkeys
@@ -313,9 +364,6 @@ public function watch(array|string $key, ...$otherkeys);
 	/** @return bool|Redis */
     public function unwatch();
 
-	/** @return array|Redis */
-    public function keys(string $pattern);
-
 	/** @return int|Redis */
     public function type(string $key);
 
@@ -327,44 +375,10 @@ public function setBit(string $key, int $idx, bool $value);
 
 	/** @return int|Redis */
     public function strlen(string $key);
-
-	/** @return string|Redis */
-    public function rPop(string $key);
-
-    /**
-     * @deprecated
-     * @alias Redis::connect
-     */
-    public function open(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
-
-    public function pipeline(): bool|Redis;
-
-    /**
-     * @deprecated
-     * @alias Redis::pconnect
-     */
-    public function popen(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
-
-    public function publish(string $channel, string $message): int;
 }
 
 /*
     TODO:
-    public function object
-    public function persist
-    public function pexpire
-    public function pexpireAt
-    public function pfadd
-    public function pfcount
-    public function pfmerge
-    public function psubscribe
-    public function pttl
-    public function pubsub
-    public function punsubscribe
-    public function rawcommand
-    public function restore
-    public function role
-    public function rpoplpush
     public function sAdd
     public function sAddArray
     public function sDiff
diff --git a/redis_arginfo.h b/redis_arginfo.h
index ef1a827921..3389770bf1 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: aac36713ff8410d09d8d45a9388e39603422cf08 */
+ * Stub hash: 15e5928c22404f33c5790ad9a8c7beed4d485b86 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -146,6 +146,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_dump arginfo_class_Redis__prefix
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_eval, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, script, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, keys, IS_ARRAY, 0, "null")
@@ -348,6 +352,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_isConnected arginfo_class_Redis_bgSave
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, pos, IS_STRING, 0)
@@ -439,7 +447,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_multi, 0, 0, Red
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pconnect, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_object, 0, 1, MAY_BE_LONG|MAY_BE_STRING)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_open, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0")
@@ -449,26 +461,70 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pconnect, 0, 1, _IS_
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
+#define arginfo_class_Redis_pconnect arginfo_class_Redis_open
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_persist, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_MIXED, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setex, 0, 0, 3)
+#define arginfo_class_Redis_pexpire arginfo_class_Redis_expire
+
+#define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfadd, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, elements, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_pfcount arginfo_class_Redis_hLen
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfmerge, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "NULL")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_popen arginfo_class_Redis_open
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psetex, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, expire, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_psetex arginfo_class_Redis_setex
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_psubscribe, 0, 1, IS_VOID, 0)
+	ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_setnx arginfo_class_Redis_append
+#define arginfo_class_Redis_pttl arginfo_class_Redis_hLen
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_publish, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pubsub, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_MIXED, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_punsubscribe, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_rPop arginfo_class_Redis_decr
 
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rawcommand, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rename, 0, 0, 2)
@@ -478,20 +534,35 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "NULL")
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_restore, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_role arginfo_class_Redis_getAuth
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rpoplpush, 0, 2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_MIXED, 0, "NULL")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_setex arginfo_class_Redis_psetex
+
+#define arginfo_class_Redis_setnx arginfo_class_Redis_append
+
 #define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
 #define arginfo_class_Redis_watch arginfo_class_Redis_del
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
 #define arginfo_class_Redis_type arginfo_class_Redis_decr
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
@@ -508,20 +579,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_strlen arginfo_class_Redis_decr
 
-#define arginfo_class_Redis_rPop arginfo_class_Redis_decr
-
-#define arginfo_class_Redis_open arginfo_class_Redis_pconnect
-
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL)
-ZEND_END_ARG_INFO()
-
-#define arginfo_class_Redis_popen arginfo_class_Redis_pconnect
-
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_publish, 0, 2, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -559,6 +616,7 @@ ZEND_METHOD(Redis, decrBy);
 ZEND_METHOD(Redis, del);
 ZEND_METHOD(Redis, discard);
 ZEND_METHOD(Redis, dump);
+ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, eval);
 ZEND_METHOD(Redis, evalsha);
 ZEND_METHOD(Redis, exec);
@@ -609,6 +667,7 @@ ZEND_METHOD(Redis, incrBy);
 ZEND_METHOD(Redis, incrByFloat);
 ZEND_METHOD(Redis, info);
 ZEND_METHOD(Redis, isConnected);
+ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, lInsert);
 ZEND_METHOD(Redis, lLen);
 ZEND_METHOD(Redis, lMove);
@@ -629,27 +688,40 @@ ZEND_METHOD(Redis, move);
 ZEND_METHOD(Redis, mset);
 ZEND_METHOD(Redis, msetnx);
 ZEND_METHOD(Redis, multi);
+ZEND_METHOD(Redis, object);
 ZEND_METHOD(Redis, pconnect);
-ZEND_METHOD(Redis, set);
-ZEND_METHOD(Redis, setex);
+ZEND_METHOD(Redis, persist);
+ZEND_METHOD(Redis, pexpire);
+ZEND_METHOD(Redis, pexpireAt);
+ZEND_METHOD(Redis, pfadd);
+ZEND_METHOD(Redis, pfcount);
+ZEND_METHOD(Redis, pfmerge);
+ZEND_METHOD(Redis, ping);
+ZEND_METHOD(Redis, pipeline);
 ZEND_METHOD(Redis, psetex);
-ZEND_METHOD(Redis, setnx);
+ZEND_METHOD(Redis, psubscribe);
+ZEND_METHOD(Redis, pttl);
+ZEND_METHOD(Redis, publish);
+ZEND_METHOD(Redis, pubsub);
+ZEND_METHOD(Redis, punsubscribe);
+ZEND_METHOD(Redis, rPop);
 ZEND_METHOD(Redis, randomKey);
-ZEND_METHOD(Redis, echo);
+ZEND_METHOD(Redis, rawcommand);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
-ZEND_METHOD(Redis, ping);
+ZEND_METHOD(Redis, restore);
+ZEND_METHOD(Redis, role);
+ZEND_METHOD(Redis, rpoplpush);
+ZEND_METHOD(Redis, set);
+ZEND_METHOD(Redis, setex);
+ZEND_METHOD(Redis, setnx);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
-ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, type);
 ZEND_METHOD(Redis, setRange);
 ZEND_METHOD(Redis, setBit);
 ZEND_METHOD(Redis, strlen);
-ZEND_METHOD(Redis, rPop);
-ZEND_METHOD(Redis, pipeline);
-ZEND_METHOD(Redis, publish);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -690,6 +762,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, eval, arginfo_class_Redis_eval, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, evalsha, arginfo_class_Redis_evalsha, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, exec, arginfo_class_Redis_exec, ZEND_ACC_PUBLIC)
@@ -740,6 +813,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC)
@@ -760,28 +834,41 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, mset, arginfo_class_Redis_mset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, msetnx, arginfo_class_Redis_msetnx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, object, arginfo_class_Redis_object, ZEND_ACC_PUBLIC)
+	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, persist, arginfo_class_Redis_persist, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pexpire, arginfo_class_Redis_pexpire, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pexpireAt, arginfo_class_Redis_pexpireAt, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pfadd, arginfo_class_Redis_pfadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pfcount, arginfo_class_Redis_pfcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pfmerge, arginfo_class_Redis_pfmerge, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC)
+	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, psubscribe, arginfo_class_Redis_psubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pttl, arginfo_class_Redis_pttl, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, publish, arginfo_class_Redis_publish, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pubsub, arginfo_class_Redis_pubsub, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, punsubscribe, arginfo_class_Redis_punsubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, randomKey, arginfo_class_Redis_randomKey, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rawcommand, arginfo_class_Redis_rawcommand, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, restore, arginfo_class_Redis_restore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, role, arginfo_class_Redis_role, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rpoplpush, arginfo_class_Redis_rpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
-	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
-	ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC)
-	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
-	ZEND_ME(Redis, publish, arginfo_class_Redis_publish, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index dff1247c94..f7e4612617 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: aac36713ff8410d09d8d45a9388e39603422cf08 */
+ * Stub hash: 15e5928c22404f33c5790ad9a8c7beed4d485b86 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -135,6 +135,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_dump arginfo_class_Redis__prefix
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1)
+	ZEND_ARG_INFO(0, str)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_eval, 0, 0, 1)
 	ZEND_ARG_INFO(0, script)
 	ZEND_ARG_INFO(0, keys)
@@ -308,6 +312,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_isConnected arginfo_class_Redis___construct
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
+	ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, pos)
@@ -386,28 +394,69 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_multi, 0, 0, 0)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_object arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_open arginfo_class_Redis_connect
+
 #define arginfo_class_Redis_pconnect arginfo_class_Redis_connect
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
+#define arginfo_class_Redis_persist arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_pexpire arginfo_class_Redis_expire
+
+#define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfadd, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, value)
-	ZEND_ARG_INFO(0, opt)
+	ZEND_ARG_INFO(0, elements)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_pfcount arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfmerge, 0, 0, 2)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, keys)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setex, 0, 0, 3)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
+	ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_pipeline arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_popen arginfo_class_Redis_connect
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psetex, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, expire)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_psetex arginfo_class_Redis_setex
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psubscribe, 0, 0, 1)
+	ZEND_ARG_INFO(0, patterns)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_setnx arginfo_class_Redis_append
+#define arginfo_class_Redis_pttl arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_publish, 0, 0, 2)
+	ZEND_ARG_INFO(0, channel)
+	ZEND_ARG_INFO(0, message)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pubsub, 0, 0, 1)
+	ZEND_ARG_INFO(0, command)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_punsubscribe arginfo_class_Redis_psubscribe
+
+#define arginfo_class_Redis_rPop arginfo_class_Redis__prefix
 
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1)
-	ZEND_ARG_INFO(0, str)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rawcommand, 0, 0, 1)
+	ZEND_ARG_INFO(0, command)
+	ZEND_ARG_VARIADIC_INFO(0, args)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rename, 0, 0, 2)
@@ -417,20 +466,35 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_restore, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_role arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rpoplpush, 0, 0, 2)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, opt)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_setex arginfo_class_Redis_psetex
+
+#define arginfo_class_Redis_setnx arginfo_class_Redis_append
+
 #define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
 #define arginfo_class_Redis_watch arginfo_class_Redis_del
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
-	ZEND_ARG_INFO(0, pattern)
-ZEND_END_ARG_INFO()
-
 #define arginfo_class_Redis_type arginfo_class_Redis__prefix
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
@@ -447,19 +511,6 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_strlen arginfo_class_Redis__prefix
 
-#define arginfo_class_Redis_rPop arginfo_class_Redis__prefix
-
-#define arginfo_class_Redis_open arginfo_class_Redis_connect
-
-#define arginfo_class_Redis_pipeline arginfo_class_Redis___construct
-
-#define arginfo_class_Redis_popen arginfo_class_Redis_connect
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_publish, 0, 0, 2)
-	ZEND_ARG_INFO(0, channel)
-	ZEND_ARG_INFO(0, message)
-ZEND_END_ARG_INFO()
-
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -497,6 +548,7 @@ ZEND_METHOD(Redis, decrBy);
 ZEND_METHOD(Redis, del);
 ZEND_METHOD(Redis, discard);
 ZEND_METHOD(Redis, dump);
+ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, eval);
 ZEND_METHOD(Redis, evalsha);
 ZEND_METHOD(Redis, exec);
@@ -547,6 +599,7 @@ ZEND_METHOD(Redis, incrBy);
 ZEND_METHOD(Redis, incrByFloat);
 ZEND_METHOD(Redis, info);
 ZEND_METHOD(Redis, isConnected);
+ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, lInsert);
 ZEND_METHOD(Redis, lLen);
 ZEND_METHOD(Redis, lMove);
@@ -567,27 +620,40 @@ ZEND_METHOD(Redis, move);
 ZEND_METHOD(Redis, mset);
 ZEND_METHOD(Redis, msetnx);
 ZEND_METHOD(Redis, multi);
+ZEND_METHOD(Redis, object);
 ZEND_METHOD(Redis, pconnect);
-ZEND_METHOD(Redis, set);
-ZEND_METHOD(Redis, setex);
+ZEND_METHOD(Redis, persist);
+ZEND_METHOD(Redis, pexpire);
+ZEND_METHOD(Redis, pexpireAt);
+ZEND_METHOD(Redis, pfadd);
+ZEND_METHOD(Redis, pfcount);
+ZEND_METHOD(Redis, pfmerge);
+ZEND_METHOD(Redis, ping);
+ZEND_METHOD(Redis, pipeline);
 ZEND_METHOD(Redis, psetex);
-ZEND_METHOD(Redis, setnx);
+ZEND_METHOD(Redis, psubscribe);
+ZEND_METHOD(Redis, pttl);
+ZEND_METHOD(Redis, publish);
+ZEND_METHOD(Redis, pubsub);
+ZEND_METHOD(Redis, punsubscribe);
+ZEND_METHOD(Redis, rPop);
 ZEND_METHOD(Redis, randomKey);
-ZEND_METHOD(Redis, echo);
+ZEND_METHOD(Redis, rawcommand);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
-ZEND_METHOD(Redis, ping);
+ZEND_METHOD(Redis, restore);
+ZEND_METHOD(Redis, role);
+ZEND_METHOD(Redis, rpoplpush);
+ZEND_METHOD(Redis, set);
+ZEND_METHOD(Redis, setex);
+ZEND_METHOD(Redis, setnx);
 ZEND_METHOD(Redis, unlink);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, unwatch);
-ZEND_METHOD(Redis, keys);
 ZEND_METHOD(Redis, type);
 ZEND_METHOD(Redis, setRange);
 ZEND_METHOD(Redis, setBit);
 ZEND_METHOD(Redis, strlen);
-ZEND_METHOD(Redis, rPop);
-ZEND_METHOD(Redis, pipeline);
-ZEND_METHOD(Redis, publish);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -628,6 +694,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, eval, arginfo_class_Redis_eval, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, evalsha, arginfo_class_Redis_evalsha, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, exec, arginfo_class_Redis_exec, ZEND_ACC_PUBLIC)
@@ -678,6 +745,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, info, arginfo_class_Redis_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, isConnected, arginfo_class_Redis_isConnected, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC)
@@ -698,28 +766,41 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, mset, arginfo_class_Redis_mset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, msetnx, arginfo_class_Redis_msetnx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, object, arginfo_class_Redis_object, ZEND_ACC_PUBLIC)
+	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, persist, arginfo_class_Redis_persist, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pexpire, arginfo_class_Redis_pexpire, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pexpireAt, arginfo_class_Redis_pexpireAt, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pfadd, arginfo_class_Redis_pfadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pfcount, arginfo_class_Redis_pfcount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pfmerge, arginfo_class_Redis_pfmerge, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC)
+	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, psubscribe, arginfo_class_Redis_psubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pttl, arginfo_class_Redis_pttl, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, publish, arginfo_class_Redis_publish, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pubsub, arginfo_class_Redis_pubsub, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, punsubscribe, arginfo_class_Redis_punsubscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, randomKey, arginfo_class_Redis_randomKey, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rawcommand, arginfo_class_Redis_rawcommand, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, restore, arginfo_class_Redis_restore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, role, arginfo_class_Redis_role, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, rpoplpush, arginfo_class_Redis_rpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, keys, arginfo_class_Redis_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, rPop, arginfo_class_Redis_rPop, ZEND_ACC_PUBLIC)
-	ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
-	ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC)
-	ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
-	ZEND_ME(Redis, publish, arginfo_class_Redis_publish, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 69d3c86bf19b597a9af63ad7567d34dc970f3767 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Sep 2021 18:15:19 +0300
Subject: [PATCH 1492/1986] Add stub-based arginfo for Redis s-w

---
 redis.stub.php         | 146 ++++++++++++++++--------
 redis_arginfo.h        | 247 +++++++++++++++++++++++++++++++++++++----
 redis_legacy_arginfo.h | 239 +++++++++++++++++++++++++++++++++++----
 3 files changed, 542 insertions(+), 90 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 5c1eb3bb68..1f15bae136 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -340,80 +340,128 @@ public function role(): mixed;
 
     public function rpoplpush(string $src, string $dst): string;
 
+    public function sAdd(string $key, mixed $value, mixed ...$other_values): int;
+
+    public function sAddArray(string $key, array $values): int;
+
+    public function sDiff(string $key, string ...$other_keys): array;
+
+    public function sDiffStore(string $dst, string $key, string ...$other_keys): int;
+
+    public function sInter(string $key, string ...$other_keys): array;
+
+    public function sInterStore(string $dst, string $key, string ...$other_keys): int;
+
+    public function sMembers(string $key): array;
+
+    public function sMisMember(string $key, string $member, string ...$other_members): array;
+
+    public function sMove(string $src, string $dst, mixed $value): bool;
+
+    public function sPop(string $key, int $count = 0): string|array;
+
+    public function sRandMember(string $key, int $count = 0): string|array;
+
+    public function sUnion(string $key, string ...$other_keys): array;
+
+    public function sUnionStore(string $dst, string $key, string ...$other_keys): int;
+
+    public function save(): bool;
+
+    public function scan(int &$iterator, ?string $pattern = null, int $count = 0): array;
+
+    public function scard(string $key): int;
+
+    public function script(string $command, mixed ...$args): mixed;
+
+    public function select(int $db): bool;
+
     /** @return bool|Redis */
     public function set(string $key, mixed $value, mixed $opt = NULL);
 
+	/** @return int|Redis */
+    public function setBit(string $key, int $idx, bool $value);
+
+	/** @return int|Redis */
+    public function setRange(string $key, int $start, string $value);
+
+
+    public function setOption(string $option, mixed $value): bool;
+
     /** @return bool|Redis */
     public function setex(string $key, int $expire, mixed $value);
 
 	/** @return bool|array|Redis */
     public function setnx(string $key, mixed $value);
 
+    public function sismember(string $key, string $value): bool;
+
+    public function slaveof(string $host = null, int $port = 6379): bool;
+
+    public function slowlog(string $mode, int $option = 0): mixed;
+
+    public function sort(string $key, array $options = null): mixed;
+
     /**
-     * @param string $otherkeys
-     * @return int|Redis
+     * @deprecated
      */
-    public function unlink(array|string $key, ...$otherkeys);
+    public function sortAsc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array;
 
     /**
-     * @param string $otherkeys
-     * @return bool|Redis
+     * @deprecated
      */
-    public function watch(array|string $key, ...$otherkeys);
+    public function sortAscAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array;
 
-	/** @return bool|Redis */
-    public function unwatch();
+    /**
+     * @deprecated
+     */
+    public function sortDesc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array;
 
-	/** @return int|Redis */
-    public function type(string $key);
+    /**
+     * @deprecated
+     */
+    public function sortDescAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array;
 
-	/** @return int|Redis */
-    public function setRange(string $key, int $start, string $value);
+    public function srem(string $key, string $value, string ...$other_values): int;
 
-	/** @return int|Redis */
-    public function setBit(string $key, int $idx, bool $value);
+    public function sscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): array;
 
 	/** @return int|Redis */
     public function strlen(string $key);
+
+    public function subscribe(string $channel, string ...$other_channels): array;
+
+    public function swapdb(string $src, string $dst): bool;
+
+    public function time(): array;
+
+    public function ttl(string $key): int;
+
+	/** @return int|Redis */
+    public function type(string $key);
+
+       /**
+     * @param string $otherkeys
+     * @return int|Redis
+     */
+    public function unlink(array|string $key, ...$otherkeys);
+
+    public function unsubscribe(string $channel, string ...$other_channels): array;
+
+	/** @return bool|Redis */
+    public function unwatch();
+
+    /**
+     * @param string $otherkeys
+     * @return bool|Redis
+     */
+    public function watch(array|string $key, ...$otherkeys);
+
+    public function wait(int $count, int $timeout): int;
 }
 
 /*
     TODO:
-    public function sAdd
-    public function sAddArray
-    public function sDiff
-    public function sDiffStore
-    public function sInter
-    public function sInterStore
-    public function sMembers
-    public function sMisMember
-    public function sMove
-    public function sPop
-    public function sRandMember
-    public function sUnion
-    public function sUnionStore
-    public function save
-    public function scan
-    public function scard
-    public function script
-    public function select
-    public function setOption
-    public function sismember
-    public function slaveof
-    public function slowlog
-    public function sort
-    public function sortAsc
-    public function sortAscAlpha
-    public function sortDesc
-    public function sortDescAlpha
-    public function srem
-    public function sscan
-    public function subscribe
-    public function swapdb
-    public function time
-    public function ttl
-    public function unsubscribe
-    public function wait
     public function xack
     public function xadd
     public function xclaim
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 3389770bf1..1e4458f63e 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 15e5928c22404f33c5790ad9a8c7beed4d485b86 */
+ * Stub hash: ee6f50ca57b834ad01ea917d34b562abb3b02633 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -547,38 +547,175 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rpoplpush, 0, 2, IS_
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sAdd, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sAddArray, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sDiff, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sDiffStore, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_sInter arginfo_class_Redis_sDiff
+
+#define arginfo_class_Redis_sInterStore arginfo_class_Redis_sDiffStore
+
+#define arginfo_class_Redis_sMembers arginfo_class_Redis_hGetAll
+
+#define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sMove, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sPop, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_sRandMember arginfo_class_Redis_sPop
+
+#define arginfo_class_Redis_sUnion arginfo_class_Redis_sDiff
+
+#define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore
+
+#define arginfo_class_Redis_save arginfo_class_Redis_bgSave
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_scan, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_scard arginfo_class_Redis_hLen
+
+#define arginfo_class_Redis_script arginfo_class_Redis_rawcommand
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_select, 0, 1, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, db, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_MIXED, 0, "NULL")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_setOption, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, option, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_setex arginfo_class_Redis_psetex
 
 #define arginfo_class_Redis_setnx arginfo_class_Redis_append
 
-#define arginfo_class_Redis_unlink arginfo_class_Redis_del
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sismember, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_watch arginfo_class_Redis_del
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slowlog, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, mode, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_type arginfo_class_Redis_decr
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sort, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sortAsc, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, get, IS_MIXED, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, store, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_sortAscAlpha arginfo_class_Redis_sortAsc
+
+#define arginfo_class_Redis_sortDesc arginfo_class_Redis_sortAsc
+
+#define arginfo_class_Redis_sortDescAlpha arginfo_class_Redis_sortAsc
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_srem, 0, 2, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sscan, 0, 2, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, value, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_strlen arginfo_class_Redis_decr
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_channels, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_swapdb, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_time arginfo_class_Redis_exec
+
+#define arginfo_class_Redis_ttl arginfo_class_Redis_hLen
+
+#define arginfo_class_Redis_type arginfo_class_Redis_decr
+
+#define arginfo_class_Redis_unlink arginfo_class_Redis_del
+
+#define arginfo_class_Redis_unsubscribe arginfo_class_Redis_subscribe
+
+#define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_watch arginfo_class_Redis_del
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_wait, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -712,16 +849,51 @@ ZEND_METHOD(Redis, renameNx);
 ZEND_METHOD(Redis, restore);
 ZEND_METHOD(Redis, role);
 ZEND_METHOD(Redis, rpoplpush);
+ZEND_METHOD(Redis, sAdd);
+ZEND_METHOD(Redis, sAddArray);
+ZEND_METHOD(Redis, sDiff);
+ZEND_METHOD(Redis, sDiffStore);
+ZEND_METHOD(Redis, sInter);
+ZEND_METHOD(Redis, sInterStore);
+ZEND_METHOD(Redis, sMembers);
+ZEND_METHOD(Redis, sMisMember);
+ZEND_METHOD(Redis, sMove);
+ZEND_METHOD(Redis, sPop);
+ZEND_METHOD(Redis, sRandMember);
+ZEND_METHOD(Redis, sUnion);
+ZEND_METHOD(Redis, sUnionStore);
+ZEND_METHOD(Redis, save);
+ZEND_METHOD(Redis, scan);
+ZEND_METHOD(Redis, scard);
+ZEND_METHOD(Redis, script);
+ZEND_METHOD(Redis, select);
 ZEND_METHOD(Redis, set);
+ZEND_METHOD(Redis, setBit);
+ZEND_METHOD(Redis, setRange);
+ZEND_METHOD(Redis, setOption);
 ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, setnx);
+ZEND_METHOD(Redis, sismember);
+ZEND_METHOD(Redis, slaveof);
+ZEND_METHOD(Redis, slowlog);
+ZEND_METHOD(Redis, sort);
+ZEND_METHOD(Redis, sortAsc);
+ZEND_METHOD(Redis, sortAscAlpha);
+ZEND_METHOD(Redis, sortDesc);
+ZEND_METHOD(Redis, sortDescAlpha);
+ZEND_METHOD(Redis, srem);
+ZEND_METHOD(Redis, sscan);
+ZEND_METHOD(Redis, strlen);
+ZEND_METHOD(Redis, subscribe);
+ZEND_METHOD(Redis, swapdb);
+ZEND_METHOD(Redis, time);
+ZEND_METHOD(Redis, ttl);
+ZEND_METHOD(Redis, type);
 ZEND_METHOD(Redis, unlink);
-ZEND_METHOD(Redis, watch);
+ZEND_METHOD(Redis, unsubscribe);
 ZEND_METHOD(Redis, unwatch);
-ZEND_METHOD(Redis, type);
-ZEND_METHOD(Redis, setRange);
-ZEND_METHOD(Redis, setBit);
-ZEND_METHOD(Redis, strlen);
+ZEND_METHOD(Redis, watch);
+ZEND_METHOD(Redis, wait);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -860,15 +1032,50 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, restore, arginfo_class_Redis_restore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, role, arginfo_class_Redis_role, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rpoplpush, arginfo_class_Redis_rpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sAdd, arginfo_class_Redis_sAdd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sAddArray, arginfo_class_Redis_sAddArray, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sDiff, arginfo_class_Redis_sDiff, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sDiffStore, arginfo_class_Redis_sDiffStore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sInter, arginfo_class_Redis_sInter, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sInterStore, arginfo_class_Redis_sInterStore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sMembers, arginfo_class_Redis_sMembers, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sMisMember, arginfo_class_Redis_sMisMember, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sMove, arginfo_class_Redis_sMove, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sPop, arginfo_class_Redis_sPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sRandMember, arginfo_class_Redis_sRandMember, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sUnion, arginfo_class_Redis_sUnion, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sUnionStore, arginfo_class_Redis_sUnionStore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, save, arginfo_class_Redis_save, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, scan, arginfo_class_Redis_scan, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, scard, arginfo_class_Redis_scard, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, script, arginfo_class_Redis_script, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, select, arginfo_class_Redis_select, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setOption, arginfo_class_Redis_setOption, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sismember, arginfo_class_Redis_sismember, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sortAsc, arginfo_class_Redis_sortAsc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, sortAscAlpha, arginfo_class_Redis_sortAscAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, sortDesc, arginfo_class_Redis_sortDesc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, sortDescAlpha, arginfo_class_Redis_sortDescAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, srem, arginfo_class_Redis_srem, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sscan, arginfo_class_Redis_sscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, subscribe, arginfo_class_Redis_subscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, swapdb, arginfo_class_Redis_swapdb, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, time, arginfo_class_Redis_time, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, ttl, arginfo_class_Redis_ttl, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, unsubscribe, arginfo_class_Redis_unsubscribe, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, wait, arginfo_class_Redis_wait, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index f7e4612617..d869392010 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 15e5928c22404f33c5790ad9a8c7beed4d485b86 */
+ * Stub hash: ee6f50ca57b834ad01ea917d34b562abb3b02633 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -479,28 +479,73 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rpoplpush, 0, 0, 2)
 	ZEND_ARG_INFO(0, dst)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sAdd, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, value)
-	ZEND_ARG_INFO(0, opt)
+	ZEND_ARG_VARIADIC_INFO(0, other_values)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_setex arginfo_class_Redis_psetex
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sAddArray, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, values)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_setnx arginfo_class_Redis_append
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sDiff, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_VARIADIC_INFO(0, other_keys)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_unlink arginfo_class_Redis_del
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sDiffStore, 0, 0, 2)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_VARIADIC_INFO(0, other_keys)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_watch arginfo_class_Redis_del
+#define arginfo_class_Redis_sInter arginfo_class_Redis_sDiff
 
-#define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
+#define arginfo_class_Redis_sInterStore arginfo_class_Redis_sDiffStore
 
-#define arginfo_class_Redis_type arginfo_class_Redis__prefix
+#define arginfo_class_Redis_sMembers arginfo_class_Redis__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
+#define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sMove, 0, 0, 3)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sPop, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_sRandMember arginfo_class_Redis_sPop
+
+#define arginfo_class_Redis_sUnion arginfo_class_Redis_sDiff
+
+#define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore
+
+#define arginfo_class_Redis_save arginfo_class_Redis___construct
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_scan, 0, 0, 1)
+	ZEND_ARG_INFO(1, iterator)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_scard arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_script arginfo_class_Redis_rawcommand
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_select, 0, 0, 1)
+	ZEND_ARG_INFO(0, db)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, opt)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
@@ -509,8 +554,90 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setOption, 0, 0, 2)
+	ZEND_ARG_INFO(0, option)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_setex arginfo_class_Redis_psetex
+
+#define arginfo_class_Redis_setnx arginfo_class_Redis_append
+
+#define arginfo_class_Redis_sismember arginfo_class_Redis_append
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, 0)
+	ZEND_ARG_INFO(0, host)
+	ZEND_ARG_INFO(0, port)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slowlog, 0, 0, 1)
+	ZEND_ARG_INFO(0, mode)
+	ZEND_ARG_INFO(0, option)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sort, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sortAsc, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, get)
+	ZEND_ARG_INFO(0, offset)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, store)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_sortAscAlpha arginfo_class_Redis_sortAsc
+
+#define arginfo_class_Redis_sortDesc arginfo_class_Redis_sortAsc
+
+#define arginfo_class_Redis_sortDescAlpha arginfo_class_Redis_sortAsc
+
+#define arginfo_class_Redis_srem arginfo_class_Redis_sAdd
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sscan, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(1, iterator)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_strlen arginfo_class_Redis__prefix
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_subscribe, 0, 0, 1)
+	ZEND_ARG_INFO(0, channel)
+	ZEND_ARG_VARIADIC_INFO(0, other_channels)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_swapdb arginfo_class_Redis_rpoplpush
+
+#define arginfo_class_Redis_time arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_ttl arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_type arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_unlink arginfo_class_Redis_del
+
+#define arginfo_class_Redis_unsubscribe arginfo_class_Redis_subscribe
+
+#define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
+
+#define arginfo_class_Redis_watch arginfo_class_Redis_del
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_wait, 0, 0, 2)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -644,16 +771,51 @@ ZEND_METHOD(Redis, renameNx);
 ZEND_METHOD(Redis, restore);
 ZEND_METHOD(Redis, role);
 ZEND_METHOD(Redis, rpoplpush);
+ZEND_METHOD(Redis, sAdd);
+ZEND_METHOD(Redis, sAddArray);
+ZEND_METHOD(Redis, sDiff);
+ZEND_METHOD(Redis, sDiffStore);
+ZEND_METHOD(Redis, sInter);
+ZEND_METHOD(Redis, sInterStore);
+ZEND_METHOD(Redis, sMembers);
+ZEND_METHOD(Redis, sMisMember);
+ZEND_METHOD(Redis, sMove);
+ZEND_METHOD(Redis, sPop);
+ZEND_METHOD(Redis, sRandMember);
+ZEND_METHOD(Redis, sUnion);
+ZEND_METHOD(Redis, sUnionStore);
+ZEND_METHOD(Redis, save);
+ZEND_METHOD(Redis, scan);
+ZEND_METHOD(Redis, scard);
+ZEND_METHOD(Redis, script);
+ZEND_METHOD(Redis, select);
 ZEND_METHOD(Redis, set);
+ZEND_METHOD(Redis, setBit);
+ZEND_METHOD(Redis, setRange);
+ZEND_METHOD(Redis, setOption);
 ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, setnx);
+ZEND_METHOD(Redis, sismember);
+ZEND_METHOD(Redis, slaveof);
+ZEND_METHOD(Redis, slowlog);
+ZEND_METHOD(Redis, sort);
+ZEND_METHOD(Redis, sortAsc);
+ZEND_METHOD(Redis, sortAscAlpha);
+ZEND_METHOD(Redis, sortDesc);
+ZEND_METHOD(Redis, sortDescAlpha);
+ZEND_METHOD(Redis, srem);
+ZEND_METHOD(Redis, sscan);
+ZEND_METHOD(Redis, strlen);
+ZEND_METHOD(Redis, subscribe);
+ZEND_METHOD(Redis, swapdb);
+ZEND_METHOD(Redis, time);
+ZEND_METHOD(Redis, ttl);
+ZEND_METHOD(Redis, type);
 ZEND_METHOD(Redis, unlink);
-ZEND_METHOD(Redis, watch);
+ZEND_METHOD(Redis, unsubscribe);
 ZEND_METHOD(Redis, unwatch);
-ZEND_METHOD(Redis, type);
-ZEND_METHOD(Redis, setRange);
-ZEND_METHOD(Redis, setBit);
-ZEND_METHOD(Redis, strlen);
+ZEND_METHOD(Redis, watch);
+ZEND_METHOD(Redis, wait);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -792,15 +954,50 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, restore, arginfo_class_Redis_restore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, role, arginfo_class_Redis_role, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rpoplpush, arginfo_class_Redis_rpoplpush, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sAdd, arginfo_class_Redis_sAdd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sAddArray, arginfo_class_Redis_sAddArray, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sDiff, arginfo_class_Redis_sDiff, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sDiffStore, arginfo_class_Redis_sDiffStore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sInter, arginfo_class_Redis_sInter, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sInterStore, arginfo_class_Redis_sInterStore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sMembers, arginfo_class_Redis_sMembers, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sMisMember, arginfo_class_Redis_sMisMember, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sMove, arginfo_class_Redis_sMove, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sPop, arginfo_class_Redis_sPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sRandMember, arginfo_class_Redis_sRandMember, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sUnion, arginfo_class_Redis_sUnion, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sUnionStore, arginfo_class_Redis_sUnionStore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, save, arginfo_class_Redis_save, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, scan, arginfo_class_Redis_scan, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, scard, arginfo_class_Redis_scard, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, script, arginfo_class_Redis_script, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, select, arginfo_class_Redis_select, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, set, arginfo_class_Redis_set, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, setOption, arginfo_class_Redis_setOption, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sismember, arginfo_class_Redis_sismember, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sortAsc, arginfo_class_Redis_sortAsc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, sortAscAlpha, arginfo_class_Redis_sortAscAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, sortDesc, arginfo_class_Redis_sortDesc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, sortDescAlpha, arginfo_class_Redis_sortDescAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, srem, arginfo_class_Redis_srem, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sscan, arginfo_class_Redis_sscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, subscribe, arginfo_class_Redis_subscribe, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, swapdb, arginfo_class_Redis_swapdb, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, time, arginfo_class_Redis_time, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, ttl, arginfo_class_Redis_ttl, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unlink, arginfo_class_Redis_unlink, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, unsubscribe, arginfo_class_Redis_unsubscribe, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, type, arginfo_class_Redis_type, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, setRange, arginfo_class_Redis_setRange, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, setBit, arginfo_class_Redis_setBit, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, wait, arginfo_class_Redis_wait, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 17712ece917aa60897d80fe705a6409ad254c10a Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Sep 2021 18:52:43 +0300
Subject: [PATCH 1493/1986] Add stub-based arginfo for Redis x

---
 redis.stub.php         |  39 ++++++++++-----
 redis_arginfo.h        | 110 ++++++++++++++++++++++++++++++++++++++++-
 redis_legacy_arginfo.h | 110 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 244 insertions(+), 15 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 1f15bae136..acdb9fab92 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -458,23 +458,36 @@ public function unwatch();
     public function watch(array|string $key, ...$otherkeys);
 
     public function wait(int $count, int $timeout): int;
+
+    public function xack(string $key, string $group, array $ids): int;
+
+    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false): string;
+
+    public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): string|array;
+
+    public function xdel(string $key, array $ids): int;
+
+    public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
+
+    public function xinfo(string $operation, string $arg1 = null, string $arg2 = null): mixed;
+
+    public function xlen(string $key): int;
+
+    public function xpending(string $key, string $group, string $start = null, string $end = null, int $count = -1, string $consumer = null): string;
+
+    public function xrange(string $key, string $start, string $end, int $count = -1): bool|array;
+
+    public function xread(array $streams, int $count = -1, int $block = -1): bool|array;
+
+    public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): bool|array;
+
+    public function xrevrange(string $key, string $start, string $end, int $count = -1): bool|array;
+
+    public function xtrim(string $key, int $maxlen, bool $approx = false): int;
 }
 
 /*
     TODO:
-    public function xack
-    public function xadd
-    public function xclaim
-    public function xdel
-    public function xgroup
-    public function xinfo
-    public function xlen
-    public function xpending
-    public function xrange
-    public function xread
-    public function xreadgroup
-    public function xrevrange
-    public function xtrim
     public function zAdd
     public function zCard
     public function zCount
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 1e4458f63e..ebb76127a5 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ee6f50ca57b834ad01ea917d34b562abb3b02633 */
+ * Stub hash: bd4e41e930b7d0c585a0b800c99d39cc0a9a1325 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -716,6 +716,88 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_wait, 0, 2, IS_LONG,
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xack, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xadd, 0, 3, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, maxlen, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min_iddle, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xdel, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xgroup, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg3, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xinfo, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_xlen arginfo_class_Redis_hLen
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xpending, 0, 2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xread, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xreadgroup, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "1")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_xrevrange arginfo_class_Redis_xrange
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xtrim, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, maxlen, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -894,6 +976,19 @@ ZEND_METHOD(Redis, unsubscribe);
 ZEND_METHOD(Redis, unwatch);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, wait);
+ZEND_METHOD(Redis, xack);
+ZEND_METHOD(Redis, xadd);
+ZEND_METHOD(Redis, xclaim);
+ZEND_METHOD(Redis, xdel);
+ZEND_METHOD(Redis, xgroup);
+ZEND_METHOD(Redis, xinfo);
+ZEND_METHOD(Redis, xlen);
+ZEND_METHOD(Redis, xpending);
+ZEND_METHOD(Redis, xrange);
+ZEND_METHOD(Redis, xread);
+ZEND_METHOD(Redis, xreadgroup);
+ZEND_METHOD(Redis, xrevrange);
+ZEND_METHOD(Redis, xtrim);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -1077,5 +1172,18 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, wait, arginfo_class_Redis_wait, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xack, arginfo_class_Redis_xack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xadd, arginfo_class_Redis_xadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xclaim, arginfo_class_Redis_xclaim, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xdel, arginfo_class_Redis_xdel, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xgroup, arginfo_class_Redis_xgroup, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xinfo, arginfo_class_Redis_xinfo, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xlen, arginfo_class_Redis_xlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xpending, arginfo_class_Redis_xpending, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xrange, arginfo_class_Redis_xrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xread, arginfo_class_Redis_xread, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xreadgroup, arginfo_class_Redis_xreadgroup, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xrevrange, arginfo_class_Redis_xrevrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xtrim, arginfo_class_Redis_xtrim, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index d869392010..a3acfa1062 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ee6f50ca57b834ad01ea917d34b562abb3b02633 */
+ * Stub hash: bd4e41e930b7d0c585a0b800c99d39cc0a9a1325 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -638,6 +638,88 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_wait, 0, 0, 2)
 	ZEND_ARG_INFO(0, timeout)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xack, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, ids)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xadd, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, id)
+	ZEND_ARG_INFO(0, values)
+	ZEND_ARG_INFO(0, maxlen)
+	ZEND_ARG_INFO(0, approx)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xclaim, 0, 0, 6)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, consumer)
+	ZEND_ARG_INFO(0, min_iddle)
+	ZEND_ARG_INFO(0, ids)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xdel, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, ids)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xgroup, 0, 0, 1)
+	ZEND_ARG_INFO(0, operation)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, arg1)
+	ZEND_ARG_INFO(0, arg2)
+	ZEND_ARG_INFO(0, arg3)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xinfo, 0, 0, 1)
+	ZEND_ARG_INFO(0, operation)
+	ZEND_ARG_INFO(0, arg1)
+	ZEND_ARG_INFO(0, arg2)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_xlen arginfo_class_Redis__prefix
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xpending, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, consumer)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xrange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xread, 0, 0, 1)
+	ZEND_ARG_INFO(0, streams)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, block)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xreadgroup, 0, 0, 3)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, consumer)
+	ZEND_ARG_INFO(0, streams)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, block)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_xrevrange arginfo_class_Redis_xrange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xtrim, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, maxlen)
+	ZEND_ARG_INFO(0, approx)
+ZEND_END_ARG_INFO()
+
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -816,6 +898,19 @@ ZEND_METHOD(Redis, unsubscribe);
 ZEND_METHOD(Redis, unwatch);
 ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, wait);
+ZEND_METHOD(Redis, xack);
+ZEND_METHOD(Redis, xadd);
+ZEND_METHOD(Redis, xclaim);
+ZEND_METHOD(Redis, xdel);
+ZEND_METHOD(Redis, xgroup);
+ZEND_METHOD(Redis, xinfo);
+ZEND_METHOD(Redis, xlen);
+ZEND_METHOD(Redis, xpending);
+ZEND_METHOD(Redis, xrange);
+ZEND_METHOD(Redis, xread);
+ZEND_METHOD(Redis, xreadgroup);
+ZEND_METHOD(Redis, xrevrange);
+ZEND_METHOD(Redis, xtrim);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -999,5 +1094,18 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, unwatch, arginfo_class_Redis_unwatch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, watch, arginfo_class_Redis_watch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, wait, arginfo_class_Redis_wait, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xack, arginfo_class_Redis_xack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xadd, arginfo_class_Redis_xadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xclaim, arginfo_class_Redis_xclaim, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xdel, arginfo_class_Redis_xdel, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xgroup, arginfo_class_Redis_xgroup, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xinfo, arginfo_class_Redis_xinfo, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xlen, arginfo_class_Redis_xlen, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xpending, arginfo_class_Redis_xpending, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xrange, arginfo_class_Redis_xrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xread, arginfo_class_Redis_xread, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xreadgroup, arginfo_class_Redis_xreadgroup, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xrevrange, arginfo_class_Redis_xrevrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xtrim, arginfo_class_Redis_xtrim, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 39f1c37dac9fdf07d9fdac200adc5cfeb463fa99 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 10:41:51 +0300
Subject: [PATCH 1494/1986] Add stub-based arginfo for Redis z

---
 redis.stub.php         |  90 +++++++++++++--------
 redis_arginfo.h        | 174 ++++++++++++++++++++++++++++++++++++++++-
 redis_legacy_arginfo.h | 170 ++++++++++++++++++++++++++++++++++++++--
 3 files changed, 391 insertions(+), 43 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index acdb9fab92..31a5e9b948 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -200,7 +200,7 @@ public function hStrLen(string $key, string $member): int;
 
     public function hVals(string $key): array;
 
-    public function hscan(string $key, int $iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function hscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
 	/** @return int|Redis */
     public function incr(string $key);
@@ -484,36 +484,60 @@ public function xreadgroup(string $group, string $consumer, array $streams, int
     public function xrevrange(string $key, string $start, string $end, int $count = -1): bool|array;
 
     public function xtrim(string $key, int $maxlen, bool $approx = false): int;
-}
 
-/*
-    TODO:
-    public function zAdd
-    public function zCard
-    public function zCount
-    public function zIncrBy
-    public function zLexCount
-    public function zMscore
-    public function zPopMax
-    public function zPopMin
-    public function zRange
-    public function zRangeByLex
-    public function zRangeByScore
-    public function zRank
-    public function zRem
-    public function zRemRangeByLex
-    public function zRemRangeByRank
-    public function zRemRangeByScore
-    public function zRevRange
-    public function zRevRangeByLex
-    public function zRevRangeByScore
-    public function zRevRank
-    public function zScore
-    public function zdiff
-    public function zdiffstore
-    public function zinter
-    public function zinterstore
-    public function zscan
-    public function zunion
-    public function zunionstore
-*/
+    public function zAdd(string $key, int $score, string $value): int;
+
+    public function zCard(string $key): int;
+
+    public function zCount(string $key, string $start , string $end): int;
+
+    public function zIncrBy(string $key, float $value, mixed $member): float;
+
+    public function zLexCount(string $key, string $min, string $max): int;
+
+    public function zMscore(string $key, string $member, string ...$other_members): array;
+
+    public function zPopMax(string $key, int $value = null): array;
+
+    public function zPopMin(string $key, int $value = null): array;
+
+    public function zRange(string $key, int $start, int $end, mixed $scores = null): array;
+
+    public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): array;
+
+    public function zRangeByScore(string $key, string $start, string $end, array $options = []): array;
+
+    public function zRank(string $key, string $member): int;
+
+    public function zRem(string $key, string $member, string ...$other_members): int;
+
+    public function zRemRangeByLex(string $key, string $min, string $max): int;
+
+    public function zRemRangeByRank(string $key, int $start, int $end): int;
+
+    public function zRemRangeByScore(string $key, string $start, string $end): int;
+
+    public function zRevRange(string $key, int $start, int $end, mixed $scores = null): array;
+
+    public function zRevRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): array;
+
+    public function zRevRangeByScore(string $key, string $start, string $end, array $options = []): array;
+
+    public function zRevRank(string $key, string $member): int;
+
+    public function zScore(string $key, mixed $member): float;
+
+    public function zdiff(array $keys, array $options = null): array;
+
+    public function zdiffstore(string $dst, array $keys, array $options = null): int;
+
+    public function zinter(array $keys, array $weights = null, array $options = null): array;
+
+    public function zinterstore(string $dst, array $keys, array $weights = null, string $aggregate = null): int;
+
+    public function zscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+
+    public function zunion(array $keys, array $weights = null, array $options = null): array;
+
+    public function zunionstore(string $dst, array $keys, array $weights = null, string $aggregate = null): int;
+}
diff --git a/redis_arginfo.h b/redis_arginfo.h
index ebb76127a5..dfc7862608 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: bd4e41e930b7d0c585a0b800c99d39cc0a9a1325 */
+ * Stub hash: 5b7df02bd08341bc68ca6717d5486aff1393c83f */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -332,7 +332,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
@@ -798,6 +798,120 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xtrim, 0, 2, IS_LONG
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zAdd, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, score, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zCard arginfo_class_Redis_hLen
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zCount, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zIncrBy, 0, 3, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zLexCount, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zMscore arginfo_class_Redis_geohash
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zPopMax, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRange, 0, 3, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scores, IS_MIXED, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 3, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 3, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zRank arginfo_class_Redis_hStrLen
+
+#define arginfo_class_Redis_zRem arginfo_class_Redis_hDel
+
+#define arginfo_class_Redis_zRemRangeByLex arginfo_class_Redis_zLexCount
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRemRangeByRank, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_zCount
+
+#define arginfo_class_Redis_zRevRange arginfo_class_Redis_zRange
+
+#define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex
+
+#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRangeByScore
+
+#define arginfo_class_Redis_zRevRank arginfo_class_Redis_hStrLen
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zScore, 0, 2, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zdiff, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zdiffstore, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zinter, 0, 1, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zinterstore, 0, 2, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 0, "null")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zscan arginfo_class_Redis_hscan
+
+#define arginfo_class_Redis_zunion arginfo_class_Redis_zinter
+
+#define arginfo_class_Redis_zunionstore arginfo_class_Redis_zinterstore
+
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -989,6 +1103,34 @@ ZEND_METHOD(Redis, xread);
 ZEND_METHOD(Redis, xreadgroup);
 ZEND_METHOD(Redis, xrevrange);
 ZEND_METHOD(Redis, xtrim);
+ZEND_METHOD(Redis, zAdd);
+ZEND_METHOD(Redis, zCard);
+ZEND_METHOD(Redis, zCount);
+ZEND_METHOD(Redis, zIncrBy);
+ZEND_METHOD(Redis, zLexCount);
+ZEND_METHOD(Redis, zMscore);
+ZEND_METHOD(Redis, zPopMax);
+ZEND_METHOD(Redis, zPopMin);
+ZEND_METHOD(Redis, zRange);
+ZEND_METHOD(Redis, zRangeByLex);
+ZEND_METHOD(Redis, zRangeByScore);
+ZEND_METHOD(Redis, zRank);
+ZEND_METHOD(Redis, zRem);
+ZEND_METHOD(Redis, zRemRangeByLex);
+ZEND_METHOD(Redis, zRemRangeByRank);
+ZEND_METHOD(Redis, zRemRangeByScore);
+ZEND_METHOD(Redis, zRevRange);
+ZEND_METHOD(Redis, zRevRangeByLex);
+ZEND_METHOD(Redis, zRevRangeByScore);
+ZEND_METHOD(Redis, zRevRank);
+ZEND_METHOD(Redis, zScore);
+ZEND_METHOD(Redis, zdiff);
+ZEND_METHOD(Redis, zdiffstore);
+ZEND_METHOD(Redis, zinter);
+ZEND_METHOD(Redis, zinterstore);
+ZEND_METHOD(Redis, zscan);
+ZEND_METHOD(Redis, zunion);
+ZEND_METHOD(Redis, zunionstore);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -1185,5 +1327,33 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, xreadgroup, arginfo_class_Redis_xreadgroup, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xrevrange, arginfo_class_Redis_xrevrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xtrim, arginfo_class_Redis_xtrim, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zAdd, arginfo_class_Redis_zAdd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zCard, arginfo_class_Redis_zCard, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zCount, arginfo_class_Redis_zCount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zIncrBy, arginfo_class_Redis_zIncrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zLexCount, arginfo_class_Redis_zLexCount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zMscore, arginfo_class_Redis_zMscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zPopMax, arginfo_class_Redis_zPopMax, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zPopMin, arginfo_class_Redis_zPopMin, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRemRangeByLex, arginfo_class_Redis_zRemRangeByLex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRemRangeByRank, arginfo_class_Redis_zRemRangeByRank, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRemRangeByScore, arginfo_class_Redis_zRemRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRevRange, arginfo_class_Redis_zRevRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRevRangeByLex, arginfo_class_Redis_zRevRangeByLex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRevRangeByScore, arginfo_class_Redis_zRevRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRevRank, arginfo_class_Redis_zRevRank, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zScore, arginfo_class_Redis_zScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zdiff, arginfo_class_Redis_zdiff, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zdiffstore, arginfo_class_Redis_zdiffstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zinter, arginfo_class_Redis_zinter, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zinterstore, arginfo_class_Redis_zinterstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zscan, arginfo_class_Redis_zscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zunion, arginfo_class_Redis_zunion, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zunionstore, arginfo_class_Redis_zunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index a3acfa1062..79a507d2a1 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: bd4e41e930b7d0c585a0b800c99d39cc0a9a1325 */
+ * Stub hash: 5b7df02bd08341bc68ca6717d5486aff1393c83f */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -295,7 +295,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, iterator)
+	ZEND_ARG_INFO(1, iterator)
 	ZEND_ARG_INFO(0, pattern)
 	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()
@@ -603,12 +603,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_srem arginfo_class_Redis_sAdd
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sscan, 0, 0, 2)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(1, iterator)
-	ZEND_ARG_INFO(0, pattern)
-	ZEND_ARG_INFO(0, count)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_sscan arginfo_class_Redis_hscan
 
 #define arginfo_class_Redis_strlen arginfo_class_Redis__prefix
 
@@ -720,6 +715,109 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xtrim, 0, 0, 2)
 	ZEND_ARG_INFO(0, approx)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zAdd, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, score)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zCard arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_zCount arginfo_class_Redis_getRange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zIncrBy, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, member)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zLexCount, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, min)
+	ZEND_ARG_INFO(0, max)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zMscore arginfo_class_Redis_geohash
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zPopMax, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, scores)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, min)
+	ZEND_ARG_INFO(0, max)
+	ZEND_ARG_INFO(0, offset)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zRank arginfo_class_Redis_hExists
+
+#define arginfo_class_Redis_zRem arginfo_class_Redis_geohash
+
+#define arginfo_class_Redis_zRemRangeByLex arginfo_class_Redis_zLexCount
+
+#define arginfo_class_Redis_zRemRangeByRank arginfo_class_Redis_getRange
+
+#define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_getRange
+
+#define arginfo_class_Redis_zRevRange arginfo_class_Redis_zRange
+
+#define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex
+
+#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRangeByScore
+
+#define arginfo_class_Redis_zRevRank arginfo_class_Redis_hExists
+
+#define arginfo_class_Redis_zScore arginfo_class_Redis_hExists
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zdiff, 0, 0, 1)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zdiffstore, 0, 0, 2)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zinter, 0, 0, 1)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, weights)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zinterstore, 0, 0, 2)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, weights)
+	ZEND_ARG_INFO(0, aggregate)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_zscan arginfo_class_Redis_hscan
+
+#define arginfo_class_Redis_zunion arginfo_class_Redis_zinter
+
+#define arginfo_class_Redis_zunionstore arginfo_class_Redis_zinterstore
+
 
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, _compress);
@@ -911,6 +1009,34 @@ ZEND_METHOD(Redis, xread);
 ZEND_METHOD(Redis, xreadgroup);
 ZEND_METHOD(Redis, xrevrange);
 ZEND_METHOD(Redis, xtrim);
+ZEND_METHOD(Redis, zAdd);
+ZEND_METHOD(Redis, zCard);
+ZEND_METHOD(Redis, zCount);
+ZEND_METHOD(Redis, zIncrBy);
+ZEND_METHOD(Redis, zLexCount);
+ZEND_METHOD(Redis, zMscore);
+ZEND_METHOD(Redis, zPopMax);
+ZEND_METHOD(Redis, zPopMin);
+ZEND_METHOD(Redis, zRange);
+ZEND_METHOD(Redis, zRangeByLex);
+ZEND_METHOD(Redis, zRangeByScore);
+ZEND_METHOD(Redis, zRank);
+ZEND_METHOD(Redis, zRem);
+ZEND_METHOD(Redis, zRemRangeByLex);
+ZEND_METHOD(Redis, zRemRangeByRank);
+ZEND_METHOD(Redis, zRemRangeByScore);
+ZEND_METHOD(Redis, zRevRange);
+ZEND_METHOD(Redis, zRevRangeByLex);
+ZEND_METHOD(Redis, zRevRangeByScore);
+ZEND_METHOD(Redis, zRevRank);
+ZEND_METHOD(Redis, zScore);
+ZEND_METHOD(Redis, zdiff);
+ZEND_METHOD(Redis, zdiffstore);
+ZEND_METHOD(Redis, zinter);
+ZEND_METHOD(Redis, zinterstore);
+ZEND_METHOD(Redis, zscan);
+ZEND_METHOD(Redis, zunion);
+ZEND_METHOD(Redis, zunionstore);
 
 
 static const zend_function_entry class_Redis_methods[] = {
@@ -1107,5 +1233,33 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, xreadgroup, arginfo_class_Redis_xreadgroup, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xrevrange, arginfo_class_Redis_xrevrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xtrim, arginfo_class_Redis_xtrim, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zAdd, arginfo_class_Redis_zAdd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zCard, arginfo_class_Redis_zCard, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zCount, arginfo_class_Redis_zCount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zIncrBy, arginfo_class_Redis_zIncrBy, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zLexCount, arginfo_class_Redis_zLexCount, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zMscore, arginfo_class_Redis_zMscore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zPopMax, arginfo_class_Redis_zPopMax, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zPopMin, arginfo_class_Redis_zPopMin, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRemRangeByLex, arginfo_class_Redis_zRemRangeByLex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRemRangeByRank, arginfo_class_Redis_zRemRangeByRank, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRemRangeByScore, arginfo_class_Redis_zRemRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRevRange, arginfo_class_Redis_zRevRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRevRangeByLex, arginfo_class_Redis_zRevRangeByLex, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRevRangeByScore, arginfo_class_Redis_zRevRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRevRank, arginfo_class_Redis_zRevRank, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zScore, arginfo_class_Redis_zScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zdiff, arginfo_class_Redis_zdiff, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zdiffstore, arginfo_class_Redis_zdiffstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zinter, arginfo_class_Redis_zinter, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zinterstore, arginfo_class_Redis_zinterstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zscan, arginfo_class_Redis_zscan, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zunion, arginfo_class_Redis_zunion, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zunionstore, arginfo_class_Redis_zunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From dc04a969894b04fbb0ba9cbb73397cb16f1f7736 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 10:48:42 +0300
Subject: [PATCH 1495/1986] Add stub-based arginfo for RedisCluster::_compress

---
 redis_cluster.stub.php         |  4 +++-
 redis_cluster_arginfo.h        | 10 ++++++++--
 redis_cluster_legacy_arginfo.h | 14 +++++++++-----
 3 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 304b37a640..e3c063de3f 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -9,6 +9,8 @@ class RedisCluster {
 
     public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistant = false, mixed $auth = NULL, array $context = NULL);
 
+    public function _compress(string $value): string;
+
     public function _masters(): array;
 
     public function _prefix(string $key): bool|string;
@@ -187,7 +189,7 @@ public function mset(array $key_values): bool;
 
     public function msetnx(array $key_values): int;
 
-    public function multi(): self|bool;
+    public function multi(): RedisCluster|bool;
 
     public function object(string $subcommand, string $key): string|int|bool;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 9f1258b05c..48a7529b5d 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 858c52a2d14a58a7917f4cf6ff4129116ead0d98 */
+ * Stub hash: cabae8161c1b40ddcb0782e887ff699778e20624 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -11,6 +11,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__compress, 0, 1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
@@ -406,7 +410,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_msetnx, 0, 1,
 	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_multi, 0, 0, self, MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_multi, 0, 0, RedisCluster, MAY_BE_BOOL)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_object, 0, 2, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_BOOL)
@@ -827,6 +831,7 @@ ZEND_END_ARG_INFO()
 
 
 ZEND_METHOD(RedisCluster, __construct);
+ZEND_METHOD(RedisCluster, _compress);
 ZEND_METHOD(RedisCluster, _masters);
 ZEND_METHOD(RedisCluster, _prefix);
 ZEND_METHOD(RedisCluster, _redir);
@@ -1017,6 +1022,7 @@ ZEND_METHOD(RedisCluster, zunionstore);
 
 static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _compress, arginfo_class_RedisCluster__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 8076f1a327..8092332624 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 858c52a2d14a58a7917f4cf6ff4129116ead0d98 */
+ * Stub hash: cabae8161c1b40ddcb0782e887ff699778e20624 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -11,6 +11,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, context)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__compress, 0, 0, 1)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
@@ -20,11 +24,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster__redir arginfo_class_RedisCluster__masters
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__serialize, 0, 0, 1)
-	ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster__serialize arginfo_class_RedisCluster__compress
 
-#define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__serialize
+#define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__compress
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_acl, 0, 0, 2)
 	ZEND_ARG_INFO(0, key_or_address)
@@ -720,6 +722,7 @@ ZEND_END_ARG_INFO()
 
 
 ZEND_METHOD(RedisCluster, __construct);
+ZEND_METHOD(RedisCluster, _compress);
 ZEND_METHOD(RedisCluster, _masters);
 ZEND_METHOD(RedisCluster, _prefix);
 ZEND_METHOD(RedisCluster, _redir);
@@ -910,6 +913,7 @@ ZEND_METHOD(RedisCluster, zunionstore);
 
 static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _compress, arginfo_class_RedisCluster__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC)

From ab65e3ca9766ee00cedc26d4d8d67b0c3c1956b7 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 10:59:41 +0300
Subject: [PATCH 1496/1986] Add stub-based arginfo for
 RedisCluster::_uncompress

---
 redis_cluster.stub.php         | 2 ++
 redis_cluster_arginfo.h        | 6 +++++-
 redis_cluster_legacy_arginfo.h | 6 +++++-
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index e3c063de3f..fcaacb85f4 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -19,6 +19,8 @@ public function _redir(): string|null;
 
     public function _serialize(mixed $value): bool|string;
 
+    public function _uncompress(string $value): string;
+
     public function _unserialize(string $value): mixed;
 
     public function acl(string|array $key_or_address, string $subcmd, string ...$args): mixed;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 48a7529b5d..f671dd4cf9 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: cabae8161c1b40ddcb0782e887ff699778e20624 */
+ * Stub hash: 37b910dcb490d2b0a2fcdf4b272f1d96d09311b9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -29,6 +29,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster__serialize, 0
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__unserialize, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -836,6 +838,7 @@ ZEND_METHOD(RedisCluster, _masters);
 ZEND_METHOD(RedisCluster, _prefix);
 ZEND_METHOD(RedisCluster, _redir);
 ZEND_METHOD(RedisCluster, _serialize);
+ZEND_METHOD(RedisCluster, _uncompress);
 ZEND_METHOD(RedisCluster, _unserialize);
 ZEND_METHOD(RedisCluster, acl);
 ZEND_METHOD(RedisCluster, append);
@@ -1027,6 +1030,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 8092332624..84bf63399f 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: cabae8161c1b40ddcb0782e887ff699778e20624 */
+ * Stub hash: 37b910dcb490d2b0a2fcdf4b272f1d96d09311b9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -26,6 +26,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster__serialize arginfo_class_RedisCluster__compress
 
+#define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress
+
 #define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__compress
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_acl, 0, 0, 2)
@@ -727,6 +729,7 @@ ZEND_METHOD(RedisCluster, _masters);
 ZEND_METHOD(RedisCluster, _prefix);
 ZEND_METHOD(RedisCluster, _redir);
 ZEND_METHOD(RedisCluster, _serialize);
+ZEND_METHOD(RedisCluster, _uncompress);
 ZEND_METHOD(RedisCluster, _unserialize);
 ZEND_METHOD(RedisCluster, acl);
 ZEND_METHOD(RedisCluster, append);
@@ -918,6 +921,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC)

From 489107a891898ebbc41345688a8b1478ab6ac845 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 11:13:24 +0300
Subject: [PATCH 1497/1986] Add stub-based arginfo for RedisCluster::_pack and
 RedisCluster::_unpack

---
 redis_cluster.stub.php         |  4 ++++
 redis_cluster_arginfo.h        | 14 ++++++++++++--
 redis_cluster_legacy_arginfo.h | 10 +++++++++-
 3 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index fcaacb85f4..5103bb4e51 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -13,6 +13,8 @@ public function _compress(string $value): string;
 
     public function _masters(): array;
 
+    public function _pack(mixed $value): string;
+
     public function _prefix(string $key): bool|string;
 
     public function _redir(): string|null;
@@ -21,6 +23,8 @@ public function _serialize(mixed $value): bool|string;
 
     public function _uncompress(string $value): string;
 
+    public function _unpack(string $value): mixed;
+
     public function _unserialize(string $value): mixed;
 
     public function acl(string|array $key_or_address, string $subcmd, string ...$args): mixed;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index f671dd4cf9..bf86cccb2b 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 37b910dcb490d2b0a2fcdf4b272f1d96d09311b9 */
+ * Stub hash: 2d9eba2d8dcf0e7bf5433232c8d5d0c00c188829 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -18,6 +18,10 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__pack, 0, 1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster__prefix, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -31,10 +35,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__unserialize, 0, 1, IS_MIXED, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__unpack, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__unpack
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_acl, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0)
@@ -835,10 +841,12 @@ ZEND_END_ARG_INFO()
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _compress);
 ZEND_METHOD(RedisCluster, _masters);
+ZEND_METHOD(RedisCluster, _pack);
 ZEND_METHOD(RedisCluster, _prefix);
 ZEND_METHOD(RedisCluster, _redir);
 ZEND_METHOD(RedisCluster, _serialize);
 ZEND_METHOD(RedisCluster, _uncompress);
+ZEND_METHOD(RedisCluster, _unpack);
 ZEND_METHOD(RedisCluster, _unserialize);
 ZEND_METHOD(RedisCluster, acl);
 ZEND_METHOD(RedisCluster, append);
@@ -1027,10 +1035,12 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _compress, arginfo_class_RedisCluster__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _pack, arginfo_class_RedisCluster__pack, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 84bf63399f..098d87c36d 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 37b910dcb490d2b0a2fcdf4b272f1d96d09311b9 */
+ * Stub hash: 2d9eba2d8dcf0e7bf5433232c8d5d0c00c188829 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -18,6 +18,8 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster__pack arginfo_class_RedisCluster__compress
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__prefix, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 ZEND_END_ARG_INFO()
@@ -28,6 +30,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress
 
+#define arginfo_class_RedisCluster__unpack arginfo_class_RedisCluster__compress
+
 #define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__compress
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_acl, 0, 0, 2)
@@ -726,10 +730,12 @@ ZEND_END_ARG_INFO()
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _compress);
 ZEND_METHOD(RedisCluster, _masters);
+ZEND_METHOD(RedisCluster, _pack);
 ZEND_METHOD(RedisCluster, _prefix);
 ZEND_METHOD(RedisCluster, _redir);
 ZEND_METHOD(RedisCluster, _serialize);
 ZEND_METHOD(RedisCluster, _uncompress);
+ZEND_METHOD(RedisCluster, _unpack);
 ZEND_METHOD(RedisCluster, _unserialize);
 ZEND_METHOD(RedisCluster, acl);
 ZEND_METHOD(RedisCluster, append);
@@ -918,10 +924,12 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _compress, arginfo_class_RedisCluster__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _pack, arginfo_class_RedisCluster__pack, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC)

From abcca1bd14978908b2a7c988d31558a54002d0b5 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 11:42:40 +0300
Subject: [PATCH 1498/1986] Rename $otherkeys to $other_keys

---
 redis.stub.php         | 15 +++++----------
 redis_arginfo.h        |  6 +++---
 redis_legacy_arginfo.h | 15 ++++++---------
 3 files changed, 14 insertions(+), 22 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 31a5e9b948..17077d3e56 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -44,10 +44,9 @@ public function bgrewriteaof(): bool;
     public function bitcount(string $key, int $start = 0, int $end = -1);
 
     /**
-     * @param string $otherkeys
      * @return int|Redis
      */
-    public function bitop(string $operation, string $deskey, string $srckey, ...$otherkeys): int;
+    public function bitop(string $operation, string $deskey, string $srckey, string ...$other_keys): int;
 
     /** @return int|Redis */
     public function bitpos(string $key, int $bit, int $start = 0, int $end = -1);
@@ -87,18 +86,16 @@ public function decr(string $key);
     public function decrBy(string $key, int $value);
 
     /**
-     * @param string $otherkeys
      * @return int|Redis
      */
-    public function del(array|string $key, ...$otherkeys);
+    public function del(array|string $key, string ...$other_keys);
 
     /**
-     * @param string $otherkeys
      * @deprecated
      * @alias Redis::del
      * @return int|Redis
      */
-    public function delete(array|string $key, ...$otherkeys);
+    public function delete(array|string $key, string ...$other_keys);
 
     public function discard(): bool;
 
@@ -441,10 +438,9 @@ public function ttl(string $key): int;
     public function type(string $key);
 
        /**
-     * @param string $otherkeys
      * @return int|Redis
      */
-    public function unlink(array|string $key, ...$otherkeys);
+    public function unlink(array|string $key, string ...$other_keys);
 
     public function unsubscribe(string $channel, string ...$other_channels): array;
 
@@ -452,10 +448,9 @@ public function unsubscribe(string $channel, string ...$other_channels): array;
     public function unwatch();
 
     /**
-     * @param string $otherkeys
      * @return bool|Redis
      */
-    public function watch(array|string $key, ...$otherkeys);
+    public function watch(array|string $key, string ...$other_keys);
 
     public function wait(int $count, int $timeout): int;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index dfc7862608..565b673ceb 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5b7df02bd08341bc68ca6717d5486aff1393c83f */
+ * Stub hash: 144b4c3c5209a1fac7a11881040e657179581a29 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -57,7 +57,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bitop, 0, 3, IS_LONG
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, deskey, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
@@ -137,7 +137,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
-	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 79a507d2a1..d6649125c5 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5b7df02bd08341bc68ca6717d5486aff1393c83f */
+ * Stub hash: 144b4c3c5209a1fac7a11881040e657179581a29 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -52,7 +52,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitop, 0, 0, 3)
 	ZEND_ARG_INFO(0, operation)
 	ZEND_ARG_INFO(0, deskey)
 	ZEND_ARG_INFO(0, srckey)
-	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
+	ZEND_ARG_VARIADIC_INFO(0, other_keys)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
@@ -126,7 +126,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_VARIADIC_INFO(0, otherkeys)
+	ZEND_ARG_VARIADIC_INFO(0, other_keys)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
@@ -490,10 +490,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sAddArray, 0, 0, 2)
 	ZEND_ARG_INFO(0, values)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sDiff, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_VARIADIC_INFO(0, other_keys)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_sDiff arginfo_class_Redis_del
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sDiffStore, 0, 0, 2)
 	ZEND_ARG_INFO(0, dst)
@@ -501,7 +498,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sDiffStore, 0, 0, 2)
 	ZEND_ARG_VARIADIC_INFO(0, other_keys)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_sInter arginfo_class_Redis_sDiff
+#define arginfo_class_Redis_sInter arginfo_class_Redis_del
 
 #define arginfo_class_Redis_sInterStore arginfo_class_Redis_sDiffStore
 
@@ -522,7 +519,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sRandMember arginfo_class_Redis_sPop
 
-#define arginfo_class_Redis_sUnion arginfo_class_Redis_sDiff
+#define arginfo_class_Redis_sUnion arginfo_class_Redis_del
 
 #define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore
 

From 203ffbb66a8201146fa250a09318b92408889388 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 19:24:46 +0300
Subject: [PATCH 1499/1986] Fix RedisCluster [hz]scan iteratior reference

---
 redis_cluster.stub.php         | 4 ++--
 redis_cluster_arginfo.h        | 6 +++---
 redis_cluster_legacy_arginfo.h | 4 ++--
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 5103bb4e51..97c11b40aa 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -145,7 +145,7 @@ public function hmget(string $key, array $members): array;
 
     public function hmset(string $key, array $key_values): bool;
 
-    public function hscan(string $key, int $iterator, ?string $pattern = null, int $count = 0): array|bool;
+    public function hscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): array|bool;
 
     public function hset(string $key, string $member, mixed $value): int;
 
@@ -383,7 +383,7 @@ public function zrevrangebyscore(string $key, string $min, string $max, array $o
 
     public function zrevrank(string $key, mixed $member): int;
 
-    public function zscan(string $key, int $iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function zscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
     public function zscore(string $key): float;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index bf86cccb2b..fffde452bd 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2d9eba2d8dcf0e7bf5433232c8d5d0c00c188829 */
+ * Stub hash: 9ccc5396c168f2b6fccca751b8122b6fabc934b7 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -306,7 +306,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
@@ -826,7 +826,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 098d87c36d..ff3ceb32f7 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2d9eba2d8dcf0e7bf5433232c8d5d0c00c188829 */
+ * Stub hash: 9ccc5396c168f2b6fccca751b8122b6fabc934b7 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -269,7 +269,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hscan, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, iterator)
+	ZEND_ARG_INFO(1, iterator)
 	ZEND_ARG_INFO(0, pattern)
 	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()

From a5d5df4b6856f97f8838cfe3d3dbcb240cbca068 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 20:12:27 +0300
Subject: [PATCH 1500/1986] Add stub-based arginfo for RedisSentinel::myid

---
 redis_sentinel.stub.php         | 2 ++
 redis_sentinel_arginfo.h        | 7 ++++++-
 redis_sentinel_legacy_arginfo.h | 6 +++++-
 3 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php
index 051fc6df90..20f8075502 100644
--- a/redis_sentinel.stub.php
+++ b/redis_sentinel.stub.php
@@ -27,6 +27,8 @@ public function master(string $master);
 	/** @return array|bool|RedisSentinel */
     public function masters();
 
+    public function myid(): string;
+
 	/** @return bool|RedisSentinel */
     public function ping();
 
diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h
index 0230002a5d..980719dc42 100644
--- a/redis_sentinel_arginfo.h
+++ b/redis_sentinel_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 25dd3e55b8118797bf37ac517c2380bb77efdcea */
+ * Stub hash: 9a7a43f9bee2da879c1419d203ddfd12e6052e25 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
@@ -25,6 +25,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisSentinel_masters arginfo_class_RedisSentinel_flushconfig
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisSentinel_myid, 0, 0, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_RedisSentinel_ping arginfo_class_RedisSentinel_flushconfig
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_reset, 0, 0, 1)
@@ -43,6 +46,7 @@ ZEND_METHOD(RedisSentinel, flushconfig);
 ZEND_METHOD(RedisSentinel, getMasterAddrByName);
 ZEND_METHOD(RedisSentinel, master);
 ZEND_METHOD(RedisSentinel, masters);
+ZEND_METHOD(RedisSentinel, myid);
 ZEND_METHOD(RedisSentinel, ping);
 ZEND_METHOD(RedisSentinel, reset);
 ZEND_METHOD(RedisSentinel, sentinels);
@@ -57,6 +61,7 @@ static const zend_function_entry class_RedisSentinel_methods[] = {
 	ZEND_ME(RedisSentinel, getMasterAddrByName, arginfo_class_RedisSentinel_getMasterAddrByName, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, master, arginfo_class_RedisSentinel_master, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, masters, arginfo_class_RedisSentinel_masters, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, myid, arginfo_class_RedisSentinel_myid, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, ping, arginfo_class_RedisSentinel_ping, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, reset, arginfo_class_RedisSentinel_reset, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, sentinels, arginfo_class_RedisSentinel_sentinels, ZEND_ACC_PUBLIC)
diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h
index 0fd87ec9ae..7dd2d8f473 100644
--- a/redis_sentinel_legacy_arginfo.h
+++ b/redis_sentinel_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 25dd3e55b8118797bf37ac517c2380bb77efdcea */
+ * Stub hash: 9a7a43f9bee2da879c1419d203ddfd12e6052e25 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, host)
@@ -25,6 +25,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisSentinel_masters arginfo_class_RedisSentinel_flushconfig
 
+#define arginfo_class_RedisSentinel_myid arginfo_class_RedisSentinel_flushconfig
+
 #define arginfo_class_RedisSentinel_ping arginfo_class_RedisSentinel_flushconfig
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_reset, 0, 0, 1)
@@ -43,6 +45,7 @@ ZEND_METHOD(RedisSentinel, flushconfig);
 ZEND_METHOD(RedisSentinel, getMasterAddrByName);
 ZEND_METHOD(RedisSentinel, master);
 ZEND_METHOD(RedisSentinel, masters);
+ZEND_METHOD(RedisSentinel, myid);
 ZEND_METHOD(RedisSentinel, ping);
 ZEND_METHOD(RedisSentinel, reset);
 ZEND_METHOD(RedisSentinel, sentinels);
@@ -57,6 +60,7 @@ static const zend_function_entry class_RedisSentinel_methods[] = {
 	ZEND_ME(RedisSentinel, getMasterAddrByName, arginfo_class_RedisSentinel_getMasterAddrByName, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, master, arginfo_class_RedisSentinel_master, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, masters, arginfo_class_RedisSentinel_masters, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisSentinel, myid, arginfo_class_RedisSentinel_myid, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, ping, arginfo_class_RedisSentinel_ping, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, reset, arginfo_class_RedisSentinel_reset, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisSentinel, sentinels, arginfo_class_RedisSentinel_sentinels, ZEND_ACC_PUBLIC)

From ebc33a35ee73ef1e80242eeb0c6bcf907df53424 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 20:47:28 +0300
Subject: [PATCH 1501/1986] Fix RedisArray::__construct bug

---
 redis_array.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/redis_array.c b/redis_array.c
index 93a2ad8922..eb24669cb0 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -158,13 +158,14 @@ PHP_METHOD(RedisArray, __construct)
      * Note:  WRONG_PARAM_COUNT seems wrong but this is what we have been doing
      *        for ages so we can't really change it until the next major version.
      */
-    if (Z_TYPE_P(z0) != IS_ARRAY && Z_TYPE_P(z0) != IS_STRING)
+    if (Z_TYPE_P(z0) != IS_ARRAY && Z_TYPE_P(z0) != IS_STRING) {
 #if PHP_VERSION_ID < 80000
         WRONG_PARAM_COUNT;
 #else
         zend_argument_type_error(1, "must be of type string|array, %s given", zend_zval_type_name(z0));
         RETURN_THROWS();
 #endif
+    }
 
     /* If it's a string we want to load the array from ini information */
     if (Z_TYPE_P(z0) == IS_STRING) {

From 5d8923e178d090639844d36b5a8fa7ce1992d6c9 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Sep 2021 21:00:12 +0300
Subject: [PATCH 1502/1986] Fix stub-based arginfo for Redis::object and
 RedisCluster::object

---
 redis.stub.php                 | 3 ++-
 redis_arginfo.h                | 5 +++--
 redis_cluster.stub.php         | 2 +-
 redis_cluster_arginfo.h        | 4 ++--
 redis_cluster_legacy_arginfo.h | 2 +-
 redis_legacy_arginfo.h         | 7 +++++--
 6 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 17077d3e56..49e9f07fb5 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -272,7 +272,8 @@ public function msetnx(array $key_values): int;
 
     public function multi(int $value = Redis::MULTI): bool|Redis;
 
-    public function object(string $key): int|string;
+    public function object(string $subcommand, string $key): int|string;
+
     /**
      * @deprecated
      * @alias Redis::connect
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 565b673ceb..d6b367a1ee 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 144b4c3c5209a1fac7a11881040e657179581a29 */
+ * Stub hash: d32b3d0f25155fa5d5f1c9626bbf3f4bcfdf75ae */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -447,7 +447,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_multi, 0, 0, Red
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_object, 0, 1, MAY_BE_LONG|MAY_BE_STRING)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_object, 0, 2, MAY_BE_LONG|MAY_BE_STRING)
+	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 97c11b40aa..2b1b2a5d5e 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -197,7 +197,7 @@ public function msetnx(array $key_values): int;
 
     public function multi(): RedisCluster|bool;
 
-    public function object(string $subcommand, string $key): string|int|bool;
+    public function object(string $subcommand, string $key): int|string;
 
     public function persist(string $key): bool;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index fffde452bd..74857d63bf 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 9ccc5396c168f2b6fccca751b8122b6fabc934b7 */
+ * Stub hash: aecb51252f52073c36fbe147be0aacddd649db07 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -421,7 +421,7 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_multi, 0, 0, RedisCluster, MAY_BE_BOOL)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_object, 0, 2, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_object, 0, 2, MAY_BE_LONG|MAY_BE_STRING)
 	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index ff3ceb32f7..f27e3f1b76 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 9ccc5396c168f2b6fccca751b8122b6fabc934b7 */
+ * Stub hash: aecb51252f52073c36fbe147be0aacddd649db07 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index d6649125c5..fd56ae90c4 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 144b4c3c5209a1fac7a11881040e657179581a29 */
+ * Stub hash: d32b3d0f25155fa5d5f1c9626bbf3f4bcfdf75ae */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 ZEND_END_ARG_INFO()
@@ -394,7 +394,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_multi, 0, 0, 0)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_object arginfo_class_Redis__prefix
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_object, 0, 0, 2)
+	ZEND_ARG_INFO(0, subcommand)
+	ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_open arginfo_class_Redis_connect
 

From f7c657148f099558c3243aebff8adf8caee41116 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Wed, 8 Sep 2021 10:22:49 +0200
Subject: [PATCH 1503/1986] add new files (stub and arginfo)

---
 package.xml | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/package.xml b/package.xml
index f4df32dd65..dc14dc645d 100644
--- a/package.xml
+++ b/package.xml
@@ -100,18 +100,30 @@ http://pear.php.net/dtd/package-2.0.xsd">
    
    
    
+   
+   
+   
    
    
+   
+   
+   
    
    
    
    
+   
+   
+   
    
    
    
    
    
    
+   
+   
+   
    
    
    

From 61416794adb09d4b13b8ec0209dd3b91ae077321 Mon Sep 17 00:00:00 2001
From: Naphat Deepar 
Date: Thu, 23 Sep 2021 16:28:18 +0700
Subject: [PATCH 1504/1986] Update sentinel.markdown

add document about connect sentinel with authentication
---
 sentinel.markdown | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sentinel.markdown b/sentinel.markdown
index 2bd3655313..ec0adec52b 100644
--- a/sentinel.markdown
+++ b/sentinel.markdown
@@ -16,6 +16,7 @@ Redis Sentinel also provides other collateral tasks such as monitoring, notifica
 *persistent*: String, persistent connection id (optional, default is NULL meaning not persistent)  
 *retry_interval*: Int, value in milliseconds (optional, default is 0)  
 *read_timeout*: Float, value in seconds (optional, default is 0 meaning unlimited)  
+*auth*:String, or an Array with one or two elements, used to authenticate with the redis-sentinel. (optional, default is NULL meaning NOAUTH)
 
 ##### *Example*
 
@@ -25,6 +26,7 @@ $sentinel = new RedisSentinel('127.0.0.1', 26379, 2.5); // 2.5 sec timeout.
 $sentinel = new RedisSentinel('127.0.0.1', 26379, 0, 'sentinel'); // persistent connection with id 'sentinel'
 $sentinel = new RedisSentinel('127.0.0.1', 26379, 0, ''); // also persistent connection with id ''
 $sentinel = new RedisSentinel('127.0.0.1', 26379, 1, null, 100); // 1 sec timeout, 100ms delay between reconnection attempts.
+$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, NULL, 0, 0, "secret"); // connect sentinel with password authentication
 ~~~
 
 ### Usage

From 6c8cd5c283273c0b2dd21651f58e85fe8cdf67e1 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 17 Sep 2021 23:26:07 +0300
Subject: [PATCH 1505/1986] Redis::__construct options

---
 README.markdown        | 26 +++++++++++++++
 redis.c                | 73 ++++++++++++++++++++++++++++++++++++++++--
 redis.stub.php         |  2 +-
 redis_arginfo.h        | 10 +++---
 redis_legacy_arginfo.h | 54 ++++++++++++++++---------------
 tests/RedisTest.php    |  7 ++--
 6 files changed, 135 insertions(+), 37 deletions(-)

diff --git a/README.markdown b/README.markdown
index e503a0cdee..38824ea00a 100644
--- a/README.markdown
+++ b/README.markdown
@@ -158,6 +158,32 @@ _**Description**_: Creates a Redis client
 $redis = new Redis();
 ~~~
 
+Starting from version 6.0.0 it's possible to specify configuration options.
+This allows to connect to the server without explicitly invoking `connect` command.
+
+##### *Example*
+
+~~~php
+$redis = new Redis([
+    'host' => '127.0.0.1',
+    'port' => 6379,
+    'connectTimeout' => 2.5,
+    'auth' => ['phpredis', 'phpredis'],
+    'ssl' => ['verify_peer' => false],
+]);
+~~~
+
+##### *Parameters*
+
+*host*: string. can be a host, or the path to a unix domain socket.
+*port*: int
+*connectTimeout*: float, value in seconds (default is 0 meaning unlimited)
+*retryInterval*: int, value in milliseconds (optional)
+*readTimeout*: float, value in seconds (default is 0 meaning unlimited)
+*persistent*: mixed, if value is string then it used as persistend id, else value casts to boolean
+*auth*: mixed, authentication information
+*ssl*: array, SSL context options
+
 ### Class RedisException
 -----
 phpredis throws a [RedisException](#class-redisexception) object if it can't reach the Redis server. That can happen in case of connectivity issues,
diff --git a/redis.c b/redis.c
index 957aace836..de1b6cbdd2 100644
--- a/redis.c
+++ b/redis.c
@@ -601,12 +601,79 @@ PHP_MINFO_FUNCTION(redis)
     DISPLAY_INI_ENTRIES();
 }
 
-/* {{{ proto Redis Redis::__construct()
+/* {{{ proto Redis Redis::__construct(array $options = null)
     Public constructor */
 PHP_METHOD(Redis, __construct)
 {
-    if (zend_parse_parameters_none() == FAILURE) {
-        RETURN_FALSE;
+    redis_object *redis;
+    zend_string *zkey;
+    zval *val, *opts = NULL;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a", &opts) == FAILURE) {
+        RETURN_THROWS();
+    }
+
+    if (opts != NULL) {
+        redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, getThis());
+        redis->sock = redis_sock_create("127.0.0.1", 0, 6379, 0, 0, 0, NULL, 0);
+
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, val) {
+            ZVAL_DEREF(val);
+            if (zend_string_equals_literal_ci(zkey, "host")) {
+                if (Z_TYPE_P(val) != IS_STRING) {
+                    REDIS_VALUE_EXCEPTION("Invalid host");
+                    RETURN_THROWS();
+                }
+                zend_string_release(redis->sock->host);
+                redis->sock->host = zval_get_string(val);
+            } else if (zend_string_equals_literal_ci(zkey, "port")) {
+                if (Z_TYPE_P(val) != IS_LONG) {
+                    REDIS_VALUE_EXCEPTION("Invalid port");
+                    RETURN_THROWS();
+                }
+                redis->sock->port = zval_get_long(val);
+            } else if (zend_string_equals_literal_ci(zkey, "connectTimeout")) {
+                if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) {
+                    REDIS_VALUE_EXCEPTION("Invalid connect timeout");
+                    RETURN_THROWS();
+                }
+                redis->sock->timeout = zval_get_double(val);
+            } else if (zend_string_equals_literal_ci(zkey, "readTimeout")) {
+                if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) {
+                    REDIS_VALUE_EXCEPTION("Invalid read timeout");
+                    RETURN_THROWS();
+                }
+                redis->sock->read_timeout = zval_get_double(val);
+            } else if (zend_string_equals_literal_ci(zkey, "persistent")) {
+                if (Z_TYPE_P(val) == IS_STRING) {
+                    if (redis->sock->persistent_id) zend_string_release(redis->sock->persistent_id);
+                    redis->sock->persistent_id = zval_get_string(val);
+                    redis->sock->persistent = 1;
+                } else {
+                    redis->sock->persistent = zval_is_true(val);
+                }
+            } else if (zend_string_equals_literal_ci(zkey, "retryInterval")) {
+                if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) {
+                    REDIS_VALUE_EXCEPTION("Invalid retry interval");
+                    RETURN_THROWS();
+                }
+                redis->sock->retry_interval = zval_get_long(val);
+            } else if (zend_string_equals_literal_ci(zkey, "ssl")) {
+                if (Z_TYPE_P(val) != IS_ARRAY) {
+                    REDIS_VALUE_EXCEPTION("Invalid SSL context options");
+                    RETURN_THROWS();
+                }
+                redis_sock_set_stream_context(redis->sock, val);
+            } else if (zend_string_equals_literal_ci(zkey, "auth")) {
+                if (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY) {
+                    REDIS_VALUE_EXCEPTION("Invalid auth credentials");
+                    RETURN_THROWS();
+                }
+                redis_sock_set_auth_zval(redis->sock, val);
+            } else {
+                php_error_docref(NULL, E_WARNING, "Skip unknown option '%s'", ZSTR_VAL(zkey));
+            }
+        } ZEND_HASH_FOREACH_END();
     }
 }
 /* }}} */
diff --git a/redis.stub.php b/redis.stub.php
index 49e9f07fb5..58d68869ce 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -7,7 +7,7 @@
 
 class Redis {
 
-    public function __construct();
+    public function __construct(array $options = null);
 
     public function _compress(string $value): string;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index d6b367a1ee..d3f4656760 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,14 +1,16 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d32b3d0f25155fa5d5f1c9626bbf3f4bcfdf75ae */
+ * Stub hash: fb1a3f1245758210548e5ed11543fe059e6e8770 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__compress, 0, 1, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis___destruct arginfo_class_Redis___construct
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__pack, 0, 1, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
@@ -521,7 +523,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_rPop arginfo_class_Redis_decr
 
-#define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
+#define arginfo_class_Redis_randomKey arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rawcommand, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
@@ -708,7 +710,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_unsubscribe arginfo_class_Redis_subscribe
 
-#define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
+#define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_watch arginfo_class_Redis_del
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index fd56ae90c4..5e3eee73b5 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,14 +1,16 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d32b3d0f25155fa5d5f1c9626bbf3f4bcfdf75ae */
+ * Stub hash: fb1a3f1245758210548e5ed11543fe059e6e8770 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
+	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__compress, 0, 0, 1)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis___destruct arginfo_class_Redis___construct
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis__pack arginfo_class_Redis__compress
 
@@ -38,9 +40,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_auth, 0, 0, 1)
 	ZEND_ARG_INFO(0, credentials)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_bgSave arginfo_class_Redis___construct
+#define arginfo_class_Redis_bgSave arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis___construct
+#define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
@@ -80,14 +82,14 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_bzPopMin arginfo_class_Redis_blPop
 
-#define arginfo_class_Redis_clearLastError arginfo_class_Redis___construct
+#define arginfo_class_Redis_clearLastError arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_client, 0, 0, 1)
 	ZEND_ARG_INFO(0, opt)
 	ZEND_ARG_INFO(0, arg)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_close arginfo_class_Redis___construct
+#define arginfo_class_Redis_close arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_command, 0, 0, 2)
 	ZEND_ARG_INFO(0, opt)
@@ -116,7 +118,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_copy, 0, 0, 2)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_dbSize arginfo_class_Redis___construct
+#define arginfo_class_Redis_dbSize arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_debug arginfo_class_Redis__prefix
 
@@ -131,7 +133,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_delete arginfo_class_Redis_del
 
-#define arginfo_class_Redis_discard arginfo_class_Redis___construct
+#define arginfo_class_Redis_discard arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_dump arginfo_class_Redis__prefix
 
@@ -151,7 +153,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_evalsha, 0, 0, 1)
 	ZEND_ARG_INFO(0, num_keys)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_exec arginfo_class_Redis___construct
+#define arginfo_class_Redis_exec arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_exists arginfo_class_Redis__prefix
 
@@ -217,28 +219,28 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_get arginfo_class_Redis__prefix
 
-#define arginfo_class_Redis_getAuth arginfo_class_Redis___construct
+#define arginfo_class_Redis_getAuth arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, idx)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getDBNum arginfo_class_Redis___construct
+#define arginfo_class_Redis_getDBNum arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_getHost arginfo_class_Redis___construct
+#define arginfo_class_Redis_getHost arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_getLastError arginfo_class_Redis___construct
+#define arginfo_class_Redis_getLastError arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_getMode arginfo_class_Redis___construct
+#define arginfo_class_Redis_getMode arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getOption, 0, 0, 1)
 	ZEND_ARG_INFO(0, option)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getPersistentID arginfo_class_Redis___construct
+#define arginfo_class_Redis_getPersistentID arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_getPort arginfo_class_Redis___construct
+#define arginfo_class_Redis_getPort arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
@@ -246,11 +248,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, end)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis___construct
+#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_getset arginfo_class_Redis_append
 
-#define arginfo_class_Redis_getTimeout arginfo_class_Redis___construct
+#define arginfo_class_Redis_getTimeout arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_hDel arginfo_class_Redis_geohash
 
@@ -310,7 +312,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_info, 0, 0, 0)
 	ZEND_ARG_INFO(0, opt)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_isConnected arginfo_class_Redis___construct
+#define arginfo_class_Redis_isConnected arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
 	ZEND_ARG_INFO(0, pattern)
@@ -351,7 +353,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lSet, 0, 0, 3)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_lastSave arginfo_class_Redis___construct
+#define arginfo_class_Redis_lastSave arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lindex, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
@@ -425,7 +427,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
 	ZEND_ARG_INFO(0, key)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_pipeline arginfo_class_Redis___construct
+#define arginfo_class_Redis_pipeline arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_popen arginfo_class_Redis_connect
 
@@ -455,7 +457,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_rPop arginfo_class_Redis__prefix
 
-#define arginfo_class_Redis_randomKey arginfo_class_Redis___construct
+#define arginfo_class_Redis_randomKey arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rawcommand, 0, 0, 1)
 	ZEND_ARG_INFO(0, command)
@@ -475,7 +477,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_restore, 0, 0, 3)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_role arginfo_class_Redis___construct
+#define arginfo_class_Redis_role arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rpoplpush, 0, 0, 2)
 	ZEND_ARG_INFO(0, src)
@@ -526,7 +528,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore
 
-#define arginfo_class_Redis_save arginfo_class_Redis___construct
+#define arginfo_class_Redis_save arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_scan, 0, 0, 1)
 	ZEND_ARG_INFO(1, iterator)
@@ -614,7 +616,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_swapdb arginfo_class_Redis_rpoplpush
 
-#define arginfo_class_Redis_time arginfo_class_Redis___construct
+#define arginfo_class_Redis_time arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_ttl arginfo_class_Redis__prefix
 
@@ -624,7 +626,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_unsubscribe arginfo_class_Redis_subscribe
 
-#define arginfo_class_Redis_unwatch arginfo_class_Redis___construct
+#define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_watch arginfo_class_Redis_del
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 1a3c468fa4..d5928eb3f2 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -104,9 +104,10 @@ protected function getFullHostPath()
     }
 
     protected function newInstance() {
-        $r = new Redis();
-
-        $r->connect($this->getHost(), $this->getPort());
+        $r = new Redis([
+            'host' => $this->getHost(),
+            'port' => $this->getPort(),
+        ]);
 
         if($this->getAuth()) {
             $this->assertTrue($r->auth($this->getAuth()));

From 0eb1c21a78f00ce29f02dacadc18e1d5aacb84e1 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 25 Sep 2021 16:30:59 +0300
Subject: [PATCH 1506/1986] Update README.markdown

---
 README.markdown | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/README.markdown b/README.markdown
index 38824ea00a..f8526081f0 100644
--- a/README.markdown
+++ b/README.markdown
@@ -175,14 +175,14 @@ $redis = new Redis([
 
 ##### *Parameters*
 
-*host*: string. can be a host, or the path to a unix domain socket.
-*port*: int
-*connectTimeout*: float, value in seconds (default is 0 meaning unlimited)
-*retryInterval*: int, value in milliseconds (optional)
-*readTimeout*: float, value in seconds (default is 0 meaning unlimited)
-*persistent*: mixed, if value is string then it used as persistend id, else value casts to boolean
-*auth*: mixed, authentication information
-*ssl*: array, SSL context options
+*host*: string. can be a host, or the path to a unix domain socket.  
+*port*: int (default is 6379, should be -1 for unix domain socket)  
+*connectTimeout*: float, value in seconds (default is 0 meaning unlimited)  
+*retryInterval*: int, value in milliseconds (optional)  
+*readTimeout*: float, value in seconds (default is 0 meaning unlimited)  
+*persistent*: mixed, if value is string then it used as persistend id, else value casts to boolean  
+*auth*: mixed, authentication information  
+*ssl*: array, SSL context options  
 
 ### Class RedisException
 -----

From cb41edff5ac349cc390c72db62d5e44c7aca4824 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 25 Sep 2021 19:30:08 +0300
Subject: [PATCH 1507/1986] Use ZEND_HASH_FOREACH_STR_KEY_VAL when numeric
 index isn't used

---
 redis_commands.c | 20 ++++----------------
 1 file changed, 4 insertions(+), 16 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 4e0647d471..e47744f6be 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -564,11 +564,8 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     size_t key_len, start_len, end_len;
     zval *z_opt=NULL, *z_ele;
     zend_string *zkey;
-    zend_ulong idx;
     HashTable *ht_opt;
 
-    PHPREDIS_NOTUSED(idx);
-
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len,
                              &start, &start_len, &end, &end_len, &z_opt)
                              ==FAILURE)
@@ -579,7 +576,7 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     // Check for an options array
     if (z_opt && Z_TYPE_P(z_opt) == IS_ARRAY) {
         ht_opt = Z_ARRVAL_P(z_opt);
-        ZEND_HASH_FOREACH_KEY_VAL(ht_opt, idx, zkey, z_ele) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(ht_opt, zkey, z_ele) {
            /* All options require a string key type */
            if (!zkey) continue;
            ZVAL_DEREF(z_ele);
@@ -1546,13 +1543,10 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
         HashTable *kt = Z_ARRVAL_P(z_opts);
         zend_string *zkey;
-        zend_ulong idx;
         zval *v;
 
-        PHPREDIS_NOTUSED(idx);
-
         /* Iterate our option array */
-        ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, v) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(kt, zkey, v) {
             ZVAL_DEREF(v);
             /* Detect PX or EX argument and validate timeout */
             if (zkey && ZSTR_IS_EX_PX_ARG(zkey)) {
@@ -3030,15 +3024,12 @@ geoStoreType get_georadius_store_type(zend_string *key) {
 
 /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */
 static int get_georadius_opts(HashTable *ht, geoOptions *opts) {
-    zend_ulong idx;
     char *optstr;
     zend_string *zkey;
     zval *optval;
 
-    PHPREDIS_NOTUSED(idx);
-
     /* Iterate over our argument array, collating which ones we have */
-    ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, optval) {
+    ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, optval) {
         ZVAL_DEREF(optval);
 
         /* If the key is numeric it's a non value option */
@@ -3980,11 +3971,8 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) {
     zend_string *zkey;
     char *kval;
     size_t klen;
-    zend_ulong idx;
     zval *zv;
 
-    PHPREDIS_NOTUSED(idx);
-
     /* Initialize options array to sane defaults */
     memset(opt, 0, sizeof(*opt));
     opt->retrycount = -1;
@@ -3996,7 +3984,7 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) {
 
     /* Iterate over our options array */
     ht = Z_ARRVAL_P(z_arr);
-    ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, zv) {
+    ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, zv) {
         if (zkey) {
             kval = ZSTR_VAL(zkey);
             klen = ZSTR_LEN(zkey);

From dfbbc8428a0f4e9bc92bb19624d6da1dcf46c3e7 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 14 Sep 2020 19:30:08 -0700
Subject: [PATCH 1508/1986] WIP: Experimental support to detect unconsumed data

This commit is an attempt at detecting unconsumed data on a socket when
we pull it from the connection pool.

Two new INI settings are introduced related to the changes:

redis.pconnect.pool_detect_dirty:

Value Explanation
----- ----------------------------------------------------------------
    0 Don't execute new logic at all.
    1 Abort and close the socket if we find unconsumed bytes in the
      read buffer.
    2 Seek to the end of our read buffer if we find unconsumed bytes
      and then poll the socket FD to detect if we're still readable
      in which case we fail and close the socket.

redis.pconnect.pool_poll_timeout:

The poll timeout to employ when checking if the socket is readable.
This value is in milliseconds and can be zero.
---
 common.h  | 11 ++++++++
 library.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 redis.c   |  2 ++
 3 files changed, 93 insertions(+), 1 deletion(-)

diff --git a/common.h b/common.h
index 40266827ce..100b45eb1b 100644
--- a/common.h
+++ b/common.h
@@ -134,6 +134,17 @@ typedef enum {
 #define MULTI    1
 #define PIPELINE 2
 
+#define PHPREDIS_DEBUG_LOGGING 0
+
+#if PHPREDIS_DEBUG_LOGGING == 1
+#define redisDbgFmt(fmt, ...) \
+    php_printf("%s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define redisDbgStr(str) phpredisDebugFmt("%s", str)
+#else
+#define redisDbgFmt(fmt, ...) ((void)0)
+#define redisDbgStr(str) ((void)0)
+#endif
+
 #define IS_ATOMIC(redis_sock) (redis_sock->mode == ATOMIC)
 #define IS_MULTI(redis_sock) (redis_sock->mode & MULTI)
 #define IS_PIPELINE(redis_sock) (redis_sock->mode & PIPELINE)
diff --git a/library.c b/library.c
index 25e53a7db3..bc29fd6b3f 100644
--- a/library.c
+++ b/library.c
@@ -2182,6 +2182,82 @@ static int redis_stream_liveness_check(php_stream *stream) {
                                  SUCCESS : FAILURE;
 }
 
+/* Try to get the underlying socket FD for use with poll/select.
+ * Returns -1 on failure. */
+static int redis_stream_fd_for_select(php_stream *stream) {
+    php_socket_t fd;
+    int flags;
+
+    flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL;
+    if (php_stream_cast(stream, flags, (void*)&fd, 1) == FAILURE)
+        return -1;
+
+    return fd;
+}
+
+static int redis_detect_dirty_config(void) {
+    int val = INI_INT("redis.pconnect.pool_detect_dirty");
+
+    if (val >= 0 && val <= 2)
+        return val;
+    else if (val > 2)
+        return 2;
+    else
+        return 0;
+}
+
+static int redis_pool_poll_timeout(void) {
+    int val = INI_INT("redis.pconnect.pool_poll_timeout");
+    if (val >= 0)
+        return val;
+
+    return 0;
+}
+
+#define REDIS_POLL_FD_SET(_pfd, _fd, _events) \
+    (_pfd).fd = _fd; (_pfd).events = _events; (_pfd).revents = 0
+
+/* Try to determine if the socket is out of sync (has unconsumed replies) */
+static int redis_stream_detect_dirty(php_stream *stream) {
+    php_socket_t fd;
+    php_pollfd pfd;
+    int rv, action;
+
+    /* Short circuit if this is disabled */
+    if ((action = redis_detect_dirty_config()) == 0)
+        return SUCCESS;
+
+    /* Seek past unconsumed bytes if we detect them */
+    if (stream->readpos < stream->writepos) {
+        redisDbgFmt("%s on unconsumed buffer (%ld < %ld)",
+                    action > 1 ? "Aborting" : "Seeking",
+                    (long)stream->readpos, (long)stream->writepos);
+
+        /* Abort if we are configured to immediately fail */
+        if (action == 1)
+            return FAILURE;
+
+        /* Seek to the end of buffered data */
+        zend_off_t offset = stream->writepos - stream->readpos;
+        if (php_stream_seek(stream, offset, SEEK_CUR) == FAILURE)
+            return FAILURE;
+    }
+
+    /* Get the underlying FD */
+    if ((fd = redis_stream_fd_for_select(stream)) == -1)
+        return FAILURE;
+
+    /* We want to detect a readable socket (it shouln't be) */
+    REDIS_POLL_FD_SET(pfd, fd, PHP_POLLREADABLE);
+    rv = php_poll2(&pfd, 1, redis_pool_poll_timeout());
+
+    /* If we detect the socket is readable, it's dirty which is
+     * a failure.  Otherwise as best we can tell it's good.
+     * TODO:  We could attempt to consume up to N bytes */
+    redisDbgFmt("Detected %s socket", rv > 0 ? "readable" : "unreadable");
+    return rv == 0 ? SUCCESS : FAILURE;
+}
+
 static int
 redis_sock_check_liveness(RedisSock *redis_sock)
 {
@@ -2310,9 +2386,12 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
                 redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list);
                 zend_llist_remove_tail(&p->list);
 
-                if (redis_sock_check_liveness(redis_sock) == SUCCESS) {
+                if (redis_stream_detect_dirty(redis_sock->stream) == SUCCESS &&
+                    redis_sock_check_liveness(redis_sock) == SUCCESS)
+                {
                     return SUCCESS;
                 }
+
                 p->nb_active--;
             }
 
diff --git a/redis.c b/redis.c
index de1b6cbdd2..c96092d4bd 100644
--- a/redis.c
+++ b/redis.c
@@ -104,6 +104,8 @@ PHP_INI_BEGIN()
     PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.pconnect.echo_check_liveness", "1", PHP_INI_ALL, NULL)
+    PHP_INI_ENTRY("redis.pconnect.pool_detect_dirty", "0", PHP_INI_ALL, NULL)
+    PHP_INI_ENTRY("redis.pconnect.pool_poll_timeout", "0", PHP_INI_ALL, NULL)
     PHP_INI_ENTRY("redis.pconnect.pool_pattern", "", PHP_INI_ALL, NULL)
 
     /* redis session */

From 3f651d225dc7d7b1ec3a74bed541e02069cc1f3a Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 28 Sep 2021 09:23:13 -0700
Subject: [PATCH 1509/1986] Remove 'rm -r' from directions

---
 INSTALL.markdown | 1 -
 1 file changed, 1 deletion(-)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index 96f4b2338d..8c8ed16e6a 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -19,7 +19,6 @@ cd phpredis
 phpize
 ./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd]
 make && make install
-cd .. && rm -r phpredis
 ~~~
 
 If you would like phpredis to serialize your data using the igbinary library, run configure with `--enable-redis-igbinary`.

From ae5469e6e8961bdaeefdf2107bb21c788ae1a121 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 28 Sep 2021 09:50:53 -0700
Subject: [PATCH 1510/1986] Review changes

See #2013
---
 library.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/library.c b/library.c
index bc29fd6b3f..ec6a940180 100644
--- a/library.c
+++ b/library.c
@@ -2184,7 +2184,7 @@ static int redis_stream_liveness_check(php_stream *stream) {
 
 /* Try to get the underlying socket FD for use with poll/select.
  * Returns -1 on failure. */
-static int redis_stream_fd_for_select(php_stream *stream) {
+static php_socket_t redis_stream_fd_for_select(php_stream *stream) {
     php_socket_t fd;
     int flags;
 
@@ -2266,6 +2266,11 @@ redis_sock_check_liveness(RedisSock *redis_sock)
     smart_string cmd = {0};
     size_t len;
 
+    /* Short circuit if we detect the stream is "dirty", can't or are
+       configured not to try and fix it */
+    if (redis_stream_detect_dirty(redis_sock->stream) != SUCCESS)
+        goto failure;
+
     /* Short circuit if we detect the stream has gone bad or if the user has
      * configured persistent connection "YOLO mode". */
     if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS) {
@@ -2386,9 +2391,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
                 redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list);
                 zend_llist_remove_tail(&p->list);
 
-                if (redis_stream_detect_dirty(redis_sock->stream) == SUCCESS &&
-                    redis_sock_check_liveness(redis_sock) == SUCCESS)
-                {
+                if (redis_sock_check_liveness(redis_sock) == SUCCESS) {
                     return SUCCESS;
                 }
 

From 661b6a3d58aeb1b6c993e20f00739801332a1700 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 5 Oct 2021 08:22:10 -0700
Subject: [PATCH 1511/1986] Perform cheaper PHP liveness check first.

---
 library.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/library.c b/library.c
index ec6a940180..91d6ffb55e 100644
--- a/library.c
+++ b/library.c
@@ -2266,17 +2266,15 @@ redis_sock_check_liveness(RedisSock *redis_sock)
     smart_string cmd = {0};
     size_t len;
 
+    /* Short circuit if PHP detects the stream isn't live */
+    if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS)
+        goto failure;
+
     /* Short circuit if we detect the stream is "dirty", can't or are
        configured not to try and fix it */
     if (redis_stream_detect_dirty(redis_sock->stream) != SUCCESS)
         goto failure;
 
-    /* Short circuit if we detect the stream has gone bad or if the user has
-     * configured persistent connection "YOLO mode". */
-    if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS) {
-        goto failure;
-    }
-
     redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
     if (!INI_INT("redis.pconnect.echo_check_liveness")) {
         return SUCCESS;

From aac42cd33510030bb62973f8886f4cd98b9b8003 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 7 Oct 2021 18:07:13 +0300
Subject: [PATCH 1512/1986] Fix default port

---
 redis.stub.php         | 8 ++++----
 redis_arginfo.h        | 6 +++---
 redis_legacy_arginfo.h | 2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 58d68869ce..74a73e5188 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -71,7 +71,7 @@ public function command(string $opt = null, string|array $arg): mixed;
 
     public function config(string $operation, string $key, mixed $value = null): mixed;
 
-    public function connect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool;
+    public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool;
 
     public function copy(string $src, string $dst, array $options = null): bool;
 
@@ -278,9 +278,9 @@ public function object(string $subcommand, string $key): int|string;
      * @deprecated
      * @alias Redis::connect
      */
-    public function open(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
+    public function open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
-    public function pconnect(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
+    public function pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
 public function persist(string $key): bool;
 
@@ -303,7 +303,7 @@ public function pipeline(): bool|Redis;
      * @deprecated
      * @alias Redis::pconnect
      */
-    public function popen(string $host, int $port = 26379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
+    public function popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
     /** @return bool|Redis */
     public function psetex(string $key, int $expire, mixed $value);
diff --git a/redis_arginfo.h b/redis_arginfo.h
index d3f4656760..61b8e3f18a 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: fb1a3f1245758210548e5ed11543fe059e6e8770 */
+ * Stub hash: 11ba8a0136290fa3b618512f01a8458b62282f11 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -109,7 +109,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0")
@@ -456,7 +456,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_open, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "NULL")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 5e3eee73b5..b4bb52eb18 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: fb1a3f1245758210548e5ed11543fe059e6e8770 */
+ * Stub hash: 11ba8a0136290fa3b618512f01a8458b62282f11 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From c39f99cfe1553a6e3e06910567848d4c492d6e63 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 7 Oct 2021 18:22:53 +0300
Subject: [PATCH 1513/1986] Links to dedicated pages in README

---
 README.markdown | 18 +++---------------
 1 file changed, 3 insertions(+), 15 deletions(-)

diff --git a/README.markdown b/README.markdown
index 2cec9760cd..a2fd4d04a8 100644
--- a/README.markdown
+++ b/README.markdown
@@ -28,9 +28,9 @@ You can also make a one-time contribution with one of the links below.
 1. [Installing/Configuring](#installingconfiguring)
    * [Installation](#installation)
    * [PHP Session handler](#php-session-handler)
-   * [Distributed Redis Array](#distributed-redis-array)
-   * [Redis Cluster support](#redis-cluster-support)
-   * [Redis Sentinel support](#redis-sentinel-support)
+   * [Distributed Redis Array](./arrays.markdown#readme)
+   * [Redis Cluster support](./cluster.markdown#readme)
+   * [Redis Sentinel support](./sentinel.markdown#readme)
    * [Running the unit tests](#running-the-unit-tests)
 1. [Classes and methods](#classes-and-methods)
    * [Usage](#usage)
@@ -98,18 +98,6 @@ redis.session.lock_wait_time = 50000
 redis.session.lock_retries = 2000
 ~~~
 
-## Distributed Redis Array
-
-See [dedicated page](./arrays.markdown#readme).
-
-## Redis Cluster support
-
-See [dedicated page](./cluster.markdown#readme).
-
-## Redis Sentinel support
-
-See [dedicated page](./sentinel.markdown#readme).
-
 ## Running the unit tests
 
 phpredis uses a small custom unit test suite for testing functionality of the various classes.  To run tests, simply do the following:

From 373f78c731c9182d0da544bb9df5218bce5c5493 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 23 Oct 2021 22:00:12 +0300
Subject: [PATCH 1514/1986] Issue #1733

---
 redis.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/redis.c b/redis.c
index c96092d4bd..b00dd9b023 100644
--- a/redis.c
+++ b/redis.c
@@ -3130,13 +3130,20 @@ PHP_METHOD(Redis, role) {
 
 /* {{{ proto Redis::IsConnected */
 PHP_METHOD(Redis, isConnected) {
+    zval *object;
     RedisSock *redis_sock;
 
-    if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
-        RETURN_TRUE;
-    } else {
+    /* Grab our object */
+    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) {
         RETURN_FALSE;
     }
+
+    /* Grab socket */
+    if ((redis_sock = redis_sock_get_instance(object, 1)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    RETURN_BOOL(redis_sock->status >= REDIS_SOCK_STATUS_CONNECTED);
 }
 
 /* {{{ proto Redis::getHost() */

From 12bf3fddaca9c155ff720221ede7f2bf629fd481 Mon Sep 17 00:00:00 2001
From: Bar Shaul 
Date: Thu, 28 Oct 2021 12:10:55 +0300
Subject: [PATCH 1515/1986] Added support for remapping the cluster's keyspace
 on a failover

---
 cluster_library.c | 20 ++++++++++++++++++--
 cluster_library.h | 10 +++++-----
 2 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 7c455ef358..16ad284d57 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -683,7 +683,7 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) {
     clusterReply *r2, *r3;
     unsigned short port;
     char *host, key[1024];
-
+    zend_hash_clean(c->nodes);
     for (i = 0; i < r->elements; i++) {
         // Inner response
         r2 = r->element[i];
@@ -1396,7 +1396,7 @@ static void cluster_update_slot(redisCluster *c) {
     /* Do we already have the new slot mapped */
     if (c->master[c->redir_slot]) {
         /* No need to do anything if it's the same node */
-        if (!CLUSTER_REDIR_CMP(c)) {
+        if (!CLUSTER_REDIR_CMP(c, SLOT_SOCK(c,c->redir_slot))) {
             return;
         }
 
@@ -1407,6 +1407,22 @@ static void cluster_update_slot(redisCluster *c) {
             /* Just point to this slot */
             c->master[c->redir_slot] = node;
         } else {
+            /* If the redirected node is a replica of the previous slot owner, a failover has taken place.
+            We must then remap the cluster's keyspace in order to update the cluster's topology. */
+            redisClusterNode *prev_master = SLOT(c,c->redir_slot);
+            redisClusterNode *slave;
+            ZEND_HASH_FOREACH_PTR(prev_master->slaves, slave) {
+                if (slave == NULL) {
+                    continue;
+                }
+                if (!CLUSTER_REDIR_CMP(c, slave->sock)) {
+                    // Detected a failover, the redirected node was a replica 
+                    // Remap the cluster's keyspace
+                    cluster_map_keyspace(c);
+                    return;
+                }
+            } ZEND_HASH_FOREACH_END();
+
             /* Create our node */
             node = cluster_node_create(c, c->redir_host, c->redir_host_len,
                 c->redir_port, c->redir_slot, 0);
diff --git a/cluster_library.h b/cluster_library.h
index bc937be5bd..6e450705a8 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -45,11 +45,11 @@
 #define CMD_SOCK(c) (c->cmd_sock)
 #define CMD_STREAM(c) (c->cmd_sock->stream)
 
-/* Compare redirection slot information with what we have */
-#define CLUSTER_REDIR_CMP(c) \
-    (SLOT_SOCK(c,c->redir_slot)->port != c->redir_port || \
-    ZSTR_LEN(SLOT_SOCK(c,c->redir_slot)->host) != c->redir_host_len || \
-    memcmp(ZSTR_VAL(SLOT_SOCK(c,c->redir_slot)->host),c->redir_host,c->redir_host_len))
+/* Compare redirection slot information with the passed node */
+#define CLUSTER_REDIR_CMP(c, sock) \
+    (sock->port != c->redir_port || \
+    ZSTR_LEN(sock->host) != c->redir_host_len || \
+    memcmp(ZSTR_VAL(sock->host),c->redir_host,c->redir_host_len))
 
 /* Clear out our "last error" */
 #define CLUSTER_CLEAR_ERROR(c) do { \

From 53db4b3e2ffdbd60ed4105ada8725fe6c3338c4d Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 9 Nov 2021 13:23:47 -0800
Subject: [PATCH 1516/1986] Whitespace

---
 cluster_library.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cluster_library.c b/cluster_library.c
index 16ad284d57..12da18773f 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -1416,7 +1416,7 @@ static void cluster_update_slot(redisCluster *c) {
                     continue;
                 }
                 if (!CLUSTER_REDIR_CMP(c, slave->sock)) {
-                    // Detected a failover, the redirected node was a replica 
+                    // Detected a failover, the redirected node was a replica
                     // Remap the cluster's keyspace
                     cluster_map_keyspace(c);
                     return;

From 82dd8e56d341c6c875e485204d4c01355782f9d2 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 12 Nov 2021 18:08:16 +0200
Subject: [PATCH 1517/1986] Issue #2030

---
 redis_commands.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/redis_commands.c b/redis_commands.c
index e47744f6be..b821108470 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4428,6 +4428,7 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
             }
             RETURN_TRUE;
         case REDIS_OPT_FAILOVER:
+            if (c == NULL) RETURN_FALSE;
             val_long = zval_get_long(val);
             if (val_long == REDIS_FAILOVER_NONE ||
                 val_long == REDIS_FAILOVER_ERROR ||

From 87c37fd7d05fccb29e36f5db5808566a530c0e95 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 15 Nov 2021 16:07:39 -0800
Subject: [PATCH 1518/1986] Add compile_commands.json to .gitignore

---
 .gitignore | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index 733e981cad..aef8e5cd8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,4 +17,5 @@ run-tests.php
 idea/*
 .cquery
 tags
-.vscode/*
\ No newline at end of file
+.vscode/*
+compile_commands.json

From 3357041b4206cc56f7a67c348c8cf2df682c3c40 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 16 Nov 2021 17:04:35 -0800
Subject: [PATCH 1519/1986] Merge Changlog.md and package.xml changes into
 develop

---
 Changelog.md | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++
 package.xml  | 157 ++++++++++++++++++++++++++++++++++----------
 2 files changed, 302 insertions(+), 34 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 34e6f1e02a..90c59af820 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -7,6 +7,185 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ## [Unreleased]
 
+
+
+## [5.3.5RC1] - 2021-11-16 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.5RC1), [PECL](https:/pecl.php.net/package/redis/5.3.5RC1))
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack](https://audiomack.com)
+- [Open LMS](https://openlms.net/)
+- [BlueHost](https://bluehost.com)
+- [Object Cache Pro for WordPress](https://objectcache.pro/)
+- [Avtandil Kikabidze](https://github.com/akalongman)
+- [Zaher Ghaibeh](https://github.com/zaherg)
+- [BatchLabs](https://batch.com)
+- [Stackhero](https://github.com/stackhero-io)
+- [Florian Levis](https://github.com/Gounlaf)
+- [Luis Zárate](https://github.com/jlzaratec)
+
+### Fixed
+
+- Fixed segfault in redis_setoption_handler
+  [#2030](https://github.com/phpredis/phpredis/issues/2030)
+  [692e4e84](https://github.com/phpredis/phpredis/commit/692e4e84)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Fix masters array in the event of a cluster failover
+  [bce692962](https://github.com/phpredis/phpredis/commit/bce692962)
+  [#2025](https://github.com/phpredis/phpredis/pull/2025)
+  ([Bar Shaul](https://github.com/barshaul))
+- Fix 32bit type error
+  [672dec87f](https://github.com/phpredis/phpredis/commit/672dec87f)
+  ([#1956](https://github.com/phpredis/phpredis/issues/1956))
+  ([Remi Collet](https://github.com/remicollet))
+- Fix radix character in certain locales
+  [#1893](https://github.com/phpredis/phpredis/issues/1893)
+  [89a871e24](https://github.com/phpredis/phpredis/commit/89a871e24)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- ZSTD Validation fix
+  [6a77ef5cd](https://github.com/phpredis/phpredis/commit/6a77ef5cd)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- Remove superfluous typecast
+  [b2871471f](https://github.com/phpredis/phpredis/commit/b2871471f)
+  ([Remi Collet](https://github.com/remicollet))
+- Updated documentation
+  [f84168657](https://github.com/phpredis/phpredis/commit/f84168657),
+  [d017788e7](https://github.com/phpredis/phpredis/commit/d017788e7),
+  [20ac84710](https://github.com/phpredis/phpredis/commit/20ac84710),
+  [0adf05260](https://github.com/phpredis/phpredis/commit/0adf05260),
+  [aee29bf73](https://github.com/phpredis/phpredis/commit/aee29bf73),
+  [09a095e72](https://github.com/phpredis/phpredis/commit/09a095e72),
+  [12ffbf33a](https://github.com/phpredis/phpredis/commit/12ffbf33a),
+  [ff331af98](https://github.com/phpredis/phpredis/commit/ff331af98),
+  [a6bdb8731](https://github.com/phpredis/phpredis/commit/a6bdb8731),
+  [305c15840](https://github.com/phpredis/phpredis/commit/305c15840),
+  [1aa10e93a](https://github.com/phpredis/phpredis/commit/1aa10e93a),
+  [d78b0c79d](https://github.com/phpredis/phpredis/commit/d78b0c79d),
+  [c6d37c27c](https://github.com/phpredis/phpredis/commit/c6d37c27c),
+  [a6303f5b9](https://github.com/phpredis/phpredis/commit/a6303f5b9),
+  [d144bd2c7](https://github.com/phpredis/phpredis/commit/d144bd2c7),
+  [a6fb815ef](https://github.com/phpredis/phpredis/commit/a6fb815ef),
+  [9ef862bc6](https://github.com/phpredis/phpredis/commit/9ef862bc6)
+  ([neodisco](https://github.com/neodisco), [Billy Wilson](https://github.com/wilsonwr),
+  [Clément Tessier](https://github.com/ctessier), [wangqr](https://github.com/wangqr),
+  [T. Todua](https://github.com/ttodua), [Naphat Deepar](https://github.com/feverxai),
+  [dengliming](https://github.com/dengliming), [Poplary](https://github.com/poplary),
+  [Maxime Cornet](https://github.com/xElysioN), [Michael Grunder](https://github.com/michael-grunder),
+  [Emanuele Filannino](https://github.com/tatekan), [MiRacLe](https://github.com/MiRacLe-RPZ),
+  [Michael Grunder](https://github.com/michael-grunder))
+- Travis CI Fixes
+  [a43f4586e](https://github.com/phpredis/phpredis/commit/a43f4586e),
+  [4fde8178f](https://github.com/phpredis/phpredis/commit/4fde8178f),
+  [7bd5415ac](https://github.com/phpredis/phpredis/commit/7bd5415ac),
+  [fdb8c4bb7](https://github.com/phpredis/phpredis/commit/fdb8c4bb7),
+  [d4f407470](https://github.com/phpredis/phpredis/commit/d4f407470)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Minor fixes/cleanup
+  [2e190adc1](https://github.com/phpredis/phpredis/commit/2e190adc1),
+  [99975b592](https://github.com/phpredis/phpredis/commit/99975b592),
+  [9d0879fa5](https://github.com/phpredis/phpredis/commit/9d0879fa5),
+  [22b06457b](https://github.com/phpredis/phpredis/commit/22b06457b),
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Fix RedisArray constructor bug
+  [85dc883ba](https://github.com/phpredis/phpredis/commit/85dc883ba)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+### Changed
+
+- Moved to GitHub Actions
+  [4d2afa786](https://github.com/phpredis/phpredis/commit/4d2afa786),
+  [502d09fd5](https://github.com/phpredis/phpredis/commit/502d09fd5)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Use more appropriate array iteration macro
+  [6008900c2](https://github.com/phpredis/phpredis/commit/6008900c2)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Clean up session tests
+  [ab25ae7f3](https://github.com/phpredis/phpredis/commit/ab25ae7f3)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- RedisArray refactors
+  [1250f0001](https://github.com/phpredis/phpredis/commit/1250f0001),
+  [017b2ea7f](https://github.com/phpredis/phpredis/commit/017b2ea7f),
+  [37ed3f079](https://github.com/phpredis/phpredis/commit/37ed3f079)
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+- Use zend_parse_parameters_none helper
+  [a26b14dbe](https://github.com/phpredis/phpredis/commit/a26b14dbe)
+  ([Remi Collet](https://github.com/remicollet))
+
+### Added
+
+- Support for various exponential backoff strategies
+  [#1986](https://github.com/phpredis/phpredis/commit/#1986),
+  [#1993](https://github.com/phpredis/phpredis/commit/#1993),
+  [732eb8dcb](https://github.com/phpredis/phpredis/commit/732eb8dcb)
+  [05129c3a3](https://github.com/phpredis/phpredis/commit/05129c3a3)
+  [5bba6a7fc](https://github.com/phpredis/phpredis/commit/5bba6a7fc)
+  ([Nathaniel Braun](https://github.com/nbraun-amazon))
+- Added experimental support for detecting a dirty connection by 
+  trying to determine if the underlying stream is readable.
+  [d68579562](https://github.com/phpredis/phpredis/commit/d68579562)
+  [#2013](https://github.com/phpredis/phpredis/issues/2013)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- Created distinct compression utility methods (pack/unpack)
+  [#1939](https://github.com/phpredis/phpredis/issues/1939)
+  [da2790aec](https://github.com/phpredis/phpredis/commit/da2790aec)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- SMISMEMBER Command
+  [#1894](https://github.com/phpredis/phpredis/commit/#1894)
+  [ae2382472](https://github.com/phpredis/phpredis/commit/ae2382472),
+  [ed283e1ab](https://github.com/phpredis/phpredis/commit/ed283e1ab),
+  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
+
+## [5.3.4] - 2021-03-24 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.4), [PECL](https:/pecl.php.net/package/redis/5.3.4))
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack](https://audiomack.com)
+- [Open LMS](https://openlms.net/)
+- [BlueHost](https://bluehost.com)
+- [Object Cache Pro for WordPress](https://objectcache.pro/)
+- [Avtandil Kikabidze](https://github.com/akalongman)
+- [Zaher Ghaibeh](https://github.com/zaherg)
+- [BatchLabs](https://batch.com)
+
+### Fixed
+
+- Fix multi/pipeline segfault on Apple silicon [#1917](https://github.com/phpredis/phpredis/issues/1917)
+  [e0796d48](https://github.com/phpredis/phpredis/commit/e0796d48af18adac2b93982474e7df8de79ec854)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- Pass compression flag on HMGET in RedisCluster [#1945](https://github.com/phpredis/phpredis/issues/1945)
+  [edc724e6](https://github.com/phpredis/phpredis/commit/edc724e6022620414abf4f90256522d03c3160fd)
+  ([Adam Olley](https://github.com/aolley))
+- Abide by ZSTD error return constants [#1936](https://github.com/phpredis/phpredis/issues/1936)
+  [8400ed1c](https://github.com/phpredis/phpredis/pull/1937/commits/8400ed1cb23a22f70727cb60e88ca5397ee10d23)
+  ([Michael Grunder](https://github.com/michael-grunder))
+- Fix timing related CI session tests
+  [9b986bf8](https://github.com/phpredis/phpredis/commit/9b986bf81859f5a5983cd148cb15ee6ce292d288)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
+## [5.3.3] - 2021-02-01 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.3), [PECL](https:/pecl.php.net/package/redis/5.3.3))
+
+### Sponsors :sparkling_heart:
+
+- [Audiomack](https://audiomack.com)
+- [BlueHost](https://bluehost.com)
+- [Redis Cache Pro for WordPress](https://wprediscache.com)
+- [Avtandil Kikabidze](https://github.com/akalongman)
+- [Oleg Babushkin](https://github.com/olbabushkin)
+- [Zaher Ghaibeh](https://github.com/zaherg)
+- [BatchLabs](https://batch.com)
+
+### Fixed
+
+- Fixed Windows includes for PHP 8
+  [270b4db8](https://www.github.com/phpredis//phpredis/commit/270b4db821fcbe9fb881eef83e046f87587c4110)
+  ([Jan-E](https://github.com/Jan-E))
+- Fix hash_ops for PHP 8.0.1
+  [87297cbb](https://www.github.com/phpredis/phpredis/commit/87297cbb4000c88b07e729b9379de321ead74aa2)
+  ([defender-11](https://github.com/defender-11))
+- Disable clone for Redis and RedisCluster objects.  Presently they segfault.
+  [cd05a344](https://www.github.com/phpredis/phpredis/commit/87297cbb4000c88b07e729b9379de321ead74aa2)
+  ([Michael Grunder](https://github.com/michael-grunder))
+
 ## [5.3.2] - 2020-10-22 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.2), [PECL](https://pecl.php.net/package/redis/5.3.2))
 
 ### Sponsors :sparkling_heart:
diff --git a/package.xml b/package.xml
index dc14dc645d..2c37887c6e 100644
--- a/package.xml
+++ b/package.xml
@@ -27,57 +27,92 @@ http://pear.php.net/dtd/package-2.0.xsd">
   n.favrefelix@gmail.com
   no
  
- 2020-10-22
+ 2021-11-16
  
-  5.3.2
-  5.3.2
+  5.3.5RC1
+  5.3.5RC1
  
  
-  stable
-  stable
+  beta
+  beta
  
  PHP
  
-    This release containse some bugfixes and small improvements.
-    You can find a detailed list of changes in Changelog.md and package.xml
+    phpredis 5.3.5RC1
 
-    * Sponsors
-      ~ Audiomack - https://audiomack.com
-      ~ BlueHost - https://bluehost.com
-      ~ Redis Cache Pro for WordPress - https://wprediscache.com
-      ~ Avtandil Kikabidze - https://github.com/akalongman
-      ~ Oleg Babushkin - https://github.com/olbabushkin
+    This release adds support for exponential backoff w/jitter, experimental
+    support for detecting a dirty connection, as well as many other fixes
+    and improvements.
 
-    phpredis 5.3.2
+    You can find a detailed list of changes in Changelog.md and package.xml
+    or by inspecting the git commit logs.
 
-    * Use "%.17g" sprintf format for doubles as done in Redis server. [32be3006] (Pavlo Yatsukhnenko)
-    * Allow to pass NULL as RedisCluster stream context options. [72024afe] (Pavlo Yatsukhnenko)
+    --- Sponsors ---
+
+    Audiomack - https://audiomack.com
+    Open LMS - https://openlms.net
+    BlueHost - https://bluehost.com
+    Object Cache Pro for WordPress - https://objectcache.pro
+    Avtandil Kikabidze - https://github.com/akalongman
+    Zaher Ghaibeh - https://github.com/zaherg
+    BatchLabs - https://batch.com
+    Luis Zarate - https://github.com/jlzaratec
 
     ---
 
-    phpredis 5.3.2RC2
+    * Fixed segfault in redis_setoption_handler [692e4e84] (Pavlo Yatsukhnenko)
+    * Fix masters array in the event of a cluster failover [bce692962] (Bar Shaul)
+    * Fix 32 bit type error [672dec87f] (Remi Collet)
+    * Fix radix character in certain locales [89a871e24] (Pavlo Yatsukhnenko)
+    * ZSTD Validation fix [6a77ef5cd] (Michael Grunder)
+    * Remove superfluous typecast [b2871471f] (Remi Collet)
+
+    * Updated documentation [f84168657, d017788e7, 20ac84710, 0adf05260,
+      aee29bf73, 09a095e72, 12ffbf33a, ff331af98, a6bdb8731, 305c15840,
+      1aa10e93a, d78b0c79d, c6d37c27c, a6303f5b9, d144bd2c7, a6fb815ef, 9ef862bc6]
+     (neodisco, Clement Tessier, T. Todua, dengliming, Maxime Cornet,
+      Emanuele Filannino Michael Grunder)
+
+    * Travis CI Fixes
+      [a43f4586e, 4fde8178f, 7bd5415ac, fdb8c4bb7, d4f407470]
+      (Pavlo Yatsukhnenko)
 
-    ---
+    * Minor fixes/cleanup
+      [2e190adc1, 99975b592, 9d0879fa5, 22b06457b]
+      (Pavlo Yatsukhnenko)
 
-    * Verify SET options are strings before testing them as strings [514bc371] (Michael Grunder)
+    * Fix RedisArray constructor bug
+      [85dc883ba](https://github.com/phpredis/phpredis/commit/85dc883ba)
+      ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
-    ---
+    * Moved to GitHub Actions
+      [4d2afa786, 502d09fd5] (Pavlo Yatsukhnenko)
 
-    phpredis 5.3.2RC1
+    * Use more appropriate array iteration macro
+      [6008900c2] (Pavlo Yatsukhnenko)
+
+    * Clean up session tests
+      [ab25ae7f3] (Michael Grunder)
+
+    * RedisArray refactors [1250f0001, 017b2ea7f, 37ed3f079]
+      (Pavlo Yatsukhnenko)
+
+    * Use zend_parse_parameters_none helper
+      [a26b14dbe] (Remi Collet)
+
+    * Support for various exponential backoff strategies
+      [#1986, #1993, 732eb8dcb, 05129c3a3, 5bba6a7fc],
+      (Nathaniel Braun)
+
+    * Added experimental support for detecting a dirty connection
+      [d68579562] (Michael Grunder)
+
+    * Created distinct compression utility methods (pack/unpack)
+      [#1939, da2790aec] (Michael Grunder)
+
+    * SMISMEMBER Command
+      [#1894, ae2382472, ed283e1ab] (Pavlo Yatsukhnenko)
 
-    ---
-    * Fix cluster segfault when dealing with NULL multi bulk replies in RedisCluster [950e8de8] (Michael Grunder, Alex Offshore)
-    * Fix xReadGroup() must return message id [500916a4] (Pavlo Yatsukhnenko)
-    * Fix memory leak in rediscluster session handler [b2cffffc] (Pavlo Yatsukhnenko)
-    * Fix XInfo() returns false if the stream is empty [5719c9f7, 566fdeeb] (Pavlo Yatsukhnenko, Michael Grunder)
-    * Relax requirements on set's expire argument [36458071] (Michael Grunder)
-    * Refactor redis_sock_check_liveness [c5950644] (Pavlo Yatsukhnenko)
-    * PHP8 compatibility [a7662da7, f4a30cb2, 17848791] (Pavlo Yatsukhnenko, Remi Collet)
-    * Update documentation [c9ed151d, 398c99d9] (Ali Alwash, Gregoire Pineau)
-    * Add Redis::OPT_NULL_MULTIBULK_AS_NULL setting to treat NULL multi bulk replies as NULL instead of []. [950e8de8] (Michael Grunder, Alex Offshore)
-    * Allow to specify stream context for rediscluster session handler [a8daaff8, 4fbe7df7] (Pavlo Yatsukhnenko)
-    * Add new parameter to RedisCluster to specify stream ssl/tls context. [f771ea16] (Pavlo Yatsukhnenko)
-    * Add new parameter to RedisSentinel to specify auth information [81c502ae] (Pavlo Yatsukhnenko)
  
  
   
@@ -166,6 +201,60 @@ http://pear.php.net/dtd/package-2.0.xsd">
   
  
  
+ 
+   stablestable
+   5.3.45.3.4
+   2021-03-24
+   
+    phpredis 5.3.4
+
+    This release fixes a multi/pipeline segfault on apple silicon as well as
+    two small compression related bugs.
+
+    You can find a detailed list of changes in Changelog.md and package.xml
+
+    * Fix multi/pipeline segfault on Apple silicon [e0796d48] (Michael Grunder)
+    * Pass compression flag on HMGET in RedisCluster [edc724e6] (Adam Olley)
+    * Abide by ZSTD error return constants [8400ed1c] (Michael Grunder)
+    * Fix timing related CI session tests [9b986bf8] (Michael Grunder)
+
+    * Sponsors
+      ~ Audiomack - https://audiomack.com
+      ~ Open LMS - https://openlms.net
+      ~ BlueHost - https://bluehost.com
+      ~ Object Cache Pro for WordPress - https://objectcache.pro
+      ~ Avtandil Kikabidze - https://github.com/akalongman
+      ~ Zaher Ghaibeh - https://github.com/zaherg
+      ~ BatchLabs - https://batch.com
+   
+ 
+ 
+   stablestable
+   5.3.35.3.3
+   2021-02-01
+   
+    phpredis 5.3.3
+
+    This release mostly includes just small PHP 8 Windows compatibility fixes
+    such that pecl.php.net can automatically build Windows DLLs.
+
+    You can find a detailed list of changes in Changelog.md and package.xml
+
+    * Fix PHP8 Windows includes [270b4db8] (Jan-E)
+    * Fix hash ops for php 8.0.1 [87297cbb] (defender-11)
+    * Disable cloning Redis and RedisCluster objects [cd05a344]
+      (Michael Grunder)
+
+    * Sponsors
+      ~ Audiomack - https://audiomack.com
+      ~ BlueHost - https://bluehost.com
+      ~ Redis Cache Pro for WordPress - https://wprediscache.com
+      ~ Avtandil Kikabidze - https://github.com/akalongman
+      ~ Zaher Ghaibeh - https://github.com/zaherg
+      ~ BatchLabs - https://batch.com
+   
+  
+
   
    stablestable
    5.3.25.3.2

From a809d764a55d11b91b26323cba6a7f5e6650fbdc Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis 
Date: Wed, 17 Nov 2021 09:13:09 +0100
Subject: [PATCH 1520/1986] Remove experimental for PHP 8.1 on CI

---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a38dd75055..0110ef2dc3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,7 +7,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
+        php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
         experimental: [false]
         include:
           - php: '8.1'

From b8d3427ce903c4fec1a4ea1dddcc099ae447b788 Mon Sep 17 00:00:00 2001
From: Ruud Kamphuis 
Date: Wed, 17 Nov 2021 09:13:16 +0100
Subject: [PATCH 1521/1986] Add experimental support for PHP 8.2

---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0110ef2dc3..17cc26ca6d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,7 +10,7 @@ jobs:
         php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
         experimental: [false]
         include:
-          - php: '8.1'
+          - php: '8.2'
             experimental: true
     steps:
       - name: Checkout

From e9619bb31986b7fb09bb4750c0a939e3fecbb87b Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 17 Nov 2021 23:09:28 +0200
Subject: [PATCH 1522/1986] Use ZEND_HASH_FOREACH_STR_KEY_VAL when numeric
 index isn't used

---
 redis_commands.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index b821108470..ecc59623e0 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -642,9 +642,8 @@ redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
-        zend_ulong idx;
         zend_string *zkey;
-        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_opts), idx, zkey, z_ele) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
             if (zkey != NULL) {
                 ZVAL_DEREF(z_ele);
                 if (zend_string_equals_literal_ci(zkey, "withscores")) {
@@ -702,9 +701,8 @@ redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
-        zend_ulong idx;
         zend_string *zkey;
-        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_opts), idx, zkey, z_ele) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
             if (zkey != NULL) {
                 ZVAL_DEREF(z_ele);
                 if (zend_string_equals_literal_ci(zkey, "aggregate")) {
@@ -3521,10 +3519,9 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) {
-        zend_ulong idx;
         zend_string *zkey;
         zval *z_ele;
-        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(opts), idx, zkey, z_ele) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
             if (zkey != NULL) {
                 ZVAL_DEREF(z_ele);
                 if (zend_string_equals_literal_ci(zkey, "db")) {

From 1135e2e99dc2b73bc08452a27fd6a183b6f87a34 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Wed, 24 Nov 2021 11:34:54 -0800
Subject: [PATCH 1523/1986] Fix link to Audiomack sponsor image. (#2035)

* Fix audiomack sponsor link

* Try center-alignment
---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index a2fd4d04a8..00a0ff7b75 100644
--- a/README.markdown
+++ b/README.markdown
@@ -21,7 +21,7 @@ You can also make a one-time contribution with one of the links below.
 [![Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)
 
 ## Sponsors
-Audiomack.comBluehost.com
+Audiomack.comBluehost.com
 
 # Table of contents
 -----

From cefc5b2f1789a8705085c570218054f9121951a9 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Wed, 24 Nov 2021 18:39:12 -0800
Subject: [PATCH 1524/1986] Add OpenLMS and try to clean up Markdown (#2036)

---
 README.markdown | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 00a0ff7b75..07dbd68510 100644
--- a/README.markdown
+++ b/README.markdown
@@ -21,7 +21,9 @@ You can also make a one-time contribution with one of the links below.
 [![Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)
 
 ## Sponsors
-Audiomack.comBluehost.com
+Audiomack.com
+Bluehost.com
+OpenLMS.net
 
 # Table of contents
 -----

From 893c35e0096ea4d330d0794687207c86b7fe5f50 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 25 Nov 2021 11:11:49 -0800
Subject: [PATCH 1525/1986] Add Object Cache Pro sponsor image

---
 README.markdown | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.markdown b/README.markdown
index 07dbd68510..db3c8fd300 100644
--- a/README.markdown
+++ b/README.markdown
@@ -24,6 +24,7 @@ You can also make a one-time contribution with one of the links below.
 Audiomack.com
 Bluehost.com
 OpenLMS.net
+Object Cache Pro
 
 # Table of contents
 -----

From 4f177d0324173119a28fb4545cb3ebee5db71025 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Till=20Kr=C3=BCss?= 
Date: Tue, 30 Nov 2021 16:02:47 -0800
Subject: [PATCH 1526/1986] update logo (#2037)

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index db3c8fd300..024feefb2d 100644
--- a/README.markdown
+++ b/README.markdown
@@ -23,8 +23,8 @@ You can also make a one-time contribution with one of the links below.
 ## Sponsors
 Audiomack.com
 Bluehost.com
+Object Cache Pro
 OpenLMS.net
-Object Cache Pro
 
 # Table of contents
 -----

From 042fa19e85e1eef9caac76dfbf6de4712d8523d1 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 1 Dec 2021 20:27:21 +0200
Subject: [PATCH 1527/1986] Fix 'PHP Deprecated:  Creation of dynamic property'
 warning

---
 tests/TestSuite.php | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/TestSuite.php b/tests/TestSuite.php
index d4f737f994..abf695745a 100644
--- a/tests/TestSuite.php
+++ b/tests/TestSuite.php
@@ -13,6 +13,9 @@ class TestSuite
     /* Redis authentication we'll use */
     private $auth;
 
+    /* Redis server version */
+    protected $version;
+
     private static $_boo_colorize = false;
 
     private static $BOLD_ON = "\033[1m";

From aada5425ca871be63b8ca92af190088f598a903e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 1 Dec 2021 20:11:17 +0200
Subject: [PATCH 1528/1986] Issue #2038

---
 redis_cluster.stub.php         | 2 +-
 redis_cluster_arginfo.h        | 4 ++--
 redis_cluster_legacy_arginfo.h | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 2b1b2a5d5e..9a1c734ac4 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -269,7 +269,7 @@ public function setex(string $key, string $value, int $timeout): bool;
 
     public function setnx(string $key, string $value, int $timeout): bool;
 
-    public function setoption(int $option, mixed $value): bool;
+    public function setoption(string $option, mixed $value): bool;
 
     public function setrange(string $key, int $offset, string $value): int;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 74857d63bf..08884ed9e5 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: aecb51252f52073c36fbe147be0aacddd649db07 */
+ * Stub hash: 800817f771155db599c082879edec6450b9f35ab */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -571,7 +571,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_setnx arginfo_class_RedisCluster_setex
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setoption, 0, 2, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, option, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index f27e3f1b76..81a6cec101 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: aecb51252f52073c36fbe147be0aacddd649db07 */
+ * Stub hash: 800817f771155db599c082879edec6450b9f35ab */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)

From c9df538c8e035b426517df368a7f8b99e590c379 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Wed, 1 Dec 2021 19:04:46 -0800
Subject: [PATCH 1529/1986] Fix expire check in testttl (#2039)

The previous logic was timing related and also kind of testing Redis'
expiration logic itself.
---
 tests/RedisTest.php | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index d5928eb3f2..0282b54fe1 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1924,10 +1924,8 @@ public function testdbSize() {
     public function testttl() {
         $this->redis->set('x', 'y');
         $this->redis->expire('x', 5);
-        for($i = 5; $i > 0; $i--) {
-            $this->assertEquals($i, $this->redis->ttl('x'));
-            sleep(1);
-        }
+        $ttl = $this->redis->ttl('x');
+        $this->assertTrue($ttl > 0 && $ttl <= 5);
 
         // A key with no TTL
         $this->redis->del('x'); $this->redis->set('x', 'bar');

From 36a1b0c37085fa59c362c94167f935d558bcebcd Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 2 Dec 2021 07:20:42 +0200
Subject: [PATCH 1530/1986] Issue #2038

---
 redis.stub.php                 | 2 +-
 redis_arginfo.h                | 4 ++--
 redis_cluster.stub.php         | 2 +-
 redis_cluster_arginfo.h        | 4 ++--
 redis_cluster_legacy_arginfo.h | 2 +-
 redis_legacy_arginfo.h         | 2 +-
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 74a73e5188..ef12d5d183 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -384,7 +384,7 @@ public function setBit(string $key, int $idx, bool $value);
     public function setRange(string $key, int $start, string $value);
 
 
-    public function setOption(string $option, mixed $value): bool;
+    public function setOption(int $option, mixed $value): bool;
 
     /** @return bool|Redis */
     public function setex(string $key, int $expire, mixed $value);
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 61b8e3f18a..5d0354d011 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 11ba8a0136290fa3b618512f01a8458b62282f11 */
+ * Stub hash: e01609c0f0b0cbd15608feab843ac3b4e7b1caf9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -632,7 +632,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_setOption, 0, 2, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO(0, option, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 9a1c734ac4..2b1b2a5d5e 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -269,7 +269,7 @@ public function setex(string $key, string $value, int $timeout): bool;
 
     public function setnx(string $key, string $value, int $timeout): bool;
 
-    public function setoption(string $option, mixed $value): bool;
+    public function setoption(int $option, mixed $value): bool;
 
     public function setrange(string $key, int $offset, string $value): int;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 08884ed9e5..74857d63bf 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 800817f771155db599c082879edec6450b9f35ab */
+ * Stub hash: aecb51252f52073c36fbe147be0aacddd649db07 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -571,7 +571,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_setnx arginfo_class_RedisCluster_setex
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setoption, 0, 2, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO(0, option, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 81a6cec101..f27e3f1b76 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 800817f771155db599c082879edec6450b9f35ab */
+ * Stub hash: aecb51252f52073c36fbe147be0aacddd649db07 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index b4bb52eb18..c9ccbda30a 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 11ba8a0136290fa3b618512f01a8458b62282f11 */
+ * Stub hash: e01609c0f0b0cbd15608feab843ac3b4e7b1caf9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 04fd44be8b0747149a95f43358211ae5da6e592d Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Thu, 2 Dec 2021 12:14:01 -0800
Subject: [PATCH 1531/1986] Relax TTL test and run a smaller cluster in GitHub
 Actions (#2040)

* Fix expire check in testttl

The previous logic was timing related and also kind of testing Redis'
expiration logic itself.

* Use a smaller cluster in GitHub CI
---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 17cc26ca6d..1330b98288 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -51,7 +51,7 @@ jobs:
             redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl
             echo 127.0.0.1:$PORT >> tests/nodes/nodemap
           done
-          echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3 --user phpredis -a phpredis
+          echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7006) --cluster-replicas 1 --user phpredis -a phpredis
       - name: Start redis sentinel
         run: |
           wget raw.githubusercontent.com/redis/redis/6.2/sentinel.conf

From f08e4f02db85e87bf1c4cbb8f5cb07b2e8e4e70d Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 7 Dec 2021 09:30:54 +0200
Subject: [PATCH 1532/1986] Fix 'PHP Deprecated:  strtolower(): Passing null'
 warning

---
 tests/TestSuite.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/tests/TestSuite.php b/tests/TestSuite.php
index abf695745a..a6043f0528 100644
--- a/tests/TestSuite.php
+++ b/tests/TestSuite.php
@@ -222,7 +222,6 @@ protected function markTestSkipped($msg='') {
     private static function getMaxTestLen($arr_methods, $str_limit) {
         $i_result = 0;
 
-        $str_limit = strtolower($str_limit);
         foreach ($arr_methods as $obj_method) {
             $str_name = strtolower($obj_method->name);
 

From 71fbd34a43e8c84403dfd8d8e853a4e41d28d49c Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 13 Dec 2021 15:47:50 +0200
Subject: [PATCH 1533/1986] Remove unused function redis_sock_copy_auth

---
 library.c | 5 -----
 library.h | 1 -
 2 files changed, 6 deletions(-)

diff --git a/library.c b/library.c
index 91d6ffb55e..3a596c01a5 100644
--- a/library.c
+++ b/library.c
@@ -179,11 +179,6 @@ static int redis_sock_append_auth(RedisSock *redis_sock, smart_string *str) {
     return 1;
 }
 
-PHP_REDIS_API void
-redis_sock_copy_auth(RedisSock *dst, RedisSock *src) {
-    redis_sock_set_auth(dst, src->user, src->pass);
-}
-
 PHP_REDIS_API void
 redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass)
 {
diff --git a/library.h b/library.h
index 293012c6b5..21d523bdc6 100644
--- a/library.h
+++ b/library.h
@@ -77,7 +77,6 @@ PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock);
 PHP_REDIS_API char *redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen);
 PHP_REDIS_API void redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass);
 PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv);
-PHP_REDIS_API void redis_sock_copy_auth(RedisSock *dst, RedisSock *src);
 PHP_REDIS_API void redis_sock_free_auth(RedisSock *redis_sock);
 PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force);
 PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);

From ee550fb990c5779137af745e296222e90b3d52b3 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 25 Sep 2021 19:21:12 +0300
Subject: [PATCH 1534/1986] Issue #2009

---
 library.c                  | 11 +++++++++
 library.h                  |  1 +
 redis.c                    |  8 +++++++
 redis.stub.php             |  2 ++
 redis_arginfo.h            |  9 ++++++-
 redis_commands.c           | 49 ++++++++++++++++++++++++++++++++++++++
 redis_commands.h           |  3 +++
 redis_legacy_arginfo.h     |  6 ++++-
 tests/RedisClusterTest.php |  1 +
 tests/RedisTest.php        | 19 +++++++++++++++
 10 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/library.c b/library.c
index 3a596c01a5..2738cf3a09 100644
--- a/library.c
+++ b/library.c
@@ -1278,6 +1278,17 @@ redis_parse_client_list_response(char *response, zval *z_ret)
     }
 }
 
+PHP_REDIS_API int
+redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    if (ctx == NULL) {
+        return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    }
+    return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+}
+
 PHP_REDIS_API int
 redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
diff --git a/library.h b/library.h
index 21d523bdc6..0b3b713ca1 100644
--- a/library.h
+++ b/library.h
@@ -162,6 +162,7 @@ PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, Red
 PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
+PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
diff --git a/redis.c b/redis.c
index b00dd9b023..991d940e32 100644
--- a/redis.c
+++ b/redis.c
@@ -1892,12 +1892,20 @@ static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw,
     }
 }
 
+/* {{{ proto array Redis::zRandMember(string key, array options) */
+PHP_METHOD(Redis, zRandMember)
+{
+    REDIS_PROCESS_CMD(zrandmember, redis_zrandmember_response);
+}
+/* }}} */
+
 /* {{{ proto array Redis::zRange(string key,int start,int end,bool scores = 0) */
 PHP_METHOD(Redis, zRange)
 {
     generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE",
         redis_zrange_cmd);
 }
+/* }}} */
 
 /* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores = 0) */
 PHP_METHOD(Redis, zRevRange) {
diff --git a/redis.stub.php b/redis.stub.php
index ef12d5d183..1cef27c780 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -503,6 +503,8 @@ public function zRangeByLex(string $key, string $min, string $max, int $offset =
 
     public function zRangeByScore(string $key, string $start, string $end, array $options = []): array;
 
+    public function zRandMember(string $key, array $options = null): string|array;
+
     public function zRank(string $key, string $member): int;
 
     public function zRem(string $key, string $member, string ...$other_members): int;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 5d0354d011..2efa448170 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e01609c0f0b0cbd15608feab843ac3b4e7b1caf9 */
+ * Stub hash: 1c4a88252c8b66263f1b1d72af974ba5ce992d40 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -858,6 +858,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 3,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_zRandMember, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_zRank arginfo_class_Redis_hStrLen
 
 #define arginfo_class_Redis_zRem arginfo_class_Redis_hDel
@@ -1117,6 +1122,7 @@ ZEND_METHOD(Redis, zPopMin);
 ZEND_METHOD(Redis, zRange);
 ZEND_METHOD(Redis, zRangeByLex);
 ZEND_METHOD(Redis, zRangeByScore);
+ZEND_METHOD(Redis, zRandMember);
 ZEND_METHOD(Redis, zRank);
 ZEND_METHOD(Redis, zRem);
 ZEND_METHOD(Redis, zRemRangeByLex);
@@ -1341,6 +1347,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRandMember, arginfo_class_Redis_zRandMember, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRemRangeByLex, arginfo_class_Redis_zRemRangeByLex, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index ecc59623e0..3a274ebe2e 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -622,6 +622,55 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     return SUCCESS;
 }
+
+int
+redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                      char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    char *key;
+    int count = 0;
+    size_t key_len;
+    smart_string cmdstr = {0};
+    zend_bool withscores = 0;
+    zval *z_opts = NULL, *z_ele;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a",
+                              &key, &key_len, &z_opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
+        zend_string *zkey;
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
+            ZVAL_DEREF(z_ele);
+            if (zend_string_equals_literal_ci(zkey, "count")) {
+                count = zval_get_long(z_ele);
+            } else if (zend_string_equals_literal_ci(zkey, "withscores")) {
+                withscores = zval_is_true(z_ele);
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (count != 0) + withscores, "ZRANDMEMBER");
+    redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot);
+
+    if (count != 0) {
+        redis_cmd_append_sstr_long(&cmdstr, count);
+        *ctx = PHPREDIS_CTX_PTR;
+    }
+
+    if (withscores) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES");
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    }
+
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return SUCCESS;
+}
+
 int
 redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                 char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index 6550d5c695..88c66bcff7 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -103,6 +103,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, int *withscores, short *slot,
     void **ctx);
 
+int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index c9ccbda30a..f1022664b2 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e01609c0f0b0cbd15608feab843ac3b4e7b1caf9 */
+ * Stub hash: 1c4a88252c8b66263f1b1d72af974ba5ce992d40 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -770,6 +770,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 0, 3)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_zRandMember arginfo_class_Redis_sort
+
 #define arginfo_class_Redis_zRank arginfo_class_Redis_hExists
 
 #define arginfo_class_Redis_zRem arginfo_class_Redis_geohash
@@ -1022,6 +1024,7 @@ ZEND_METHOD(Redis, zPopMin);
 ZEND_METHOD(Redis, zRange);
 ZEND_METHOD(Redis, zRangeByLex);
 ZEND_METHOD(Redis, zRangeByScore);
+ZEND_METHOD(Redis, zRandMember);
 ZEND_METHOD(Redis, zRank);
 ZEND_METHOD(Redis, zRem);
 ZEND_METHOD(Redis, zRemRangeByLex);
@@ -1246,6 +1249,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zRandMember, arginfo_class_Redis_zRandMember, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRemRangeByLex, arginfo_class_Redis_zRemRangeByLex, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 350f35f8a1..394d13f2f2 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -56,6 +56,7 @@ public function testzInter() { return $this->markTestSkipped(); }
     public function testzUnion() { return $this->markTestSkipped(); }
     public function testzDiffStore() { return $this->markTestSkipped(); }
     public function testzMscore() { return $this->marktestSkipped(); }
+    public function testZRandMember() { return $this->marktestSkipped(); }
     public function testCopy() { return $this->marktestSkipped(); }
 
     /* Session locking feature is currently not supported in in context of Redis Cluster.
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 0282b54fe1..fa26d18e25 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2695,6 +2695,25 @@ public function testZPop() {
         $this->assertTrue(array('a' => 0.0, 'b' => 1.0, 'c' => 2.0) === $this->redis->zPopMin('key', 3));
     }
 
+    public function testZRandMember()
+    {
+        if (version_compare($this->version, "6.2.0") < 0) {
+            $this->MarkTestSkipped();
+            return;
+        }
+        $this->redis->del('key');
+        $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e');
+        $this->assertInArray($this->redis->zRandMember('key'), ['a', 'b', 'c', 'd', 'e']);
+
+        $result = $this->redis->zRandMember('key', ['count' => 3]);
+        $this->assertEquals(3, count($result));
+        $this->assertEquals(array_intersect($result, ['a', 'b', 'c', 'd', 'e']), $result);
+
+        $result = $this->redis->zRandMember('key', ['count' => 2, 'withscores' => true]);
+        $this->assertEquals(2, count($result));
+        $this->assertEquals(array_intersect_key($result, ['a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4]), $result);
+    }
+
     public function testHashes() {
         $this->redis->del('h', 'key');
         $this->assertTrue(0 === $this->redis->hLen('h'));

From eb44ea9aa453192f64a24da0972c9448e015cddb Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 14 Dec 2021 09:19:52 +0200
Subject: [PATCH 1535/1986] Update supported PHP version badge

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 024feefb2d..21f6be9dc9 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2,7 +2,7 @@
 
 [![Build Status](https://github.com/phpredis/phpredis/actions/workflows/ci.yml/badge.svg)](https://github.com/phpredis/phpredis/actions/workflows/ci.yml)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis)
-[![PHP version from Travis config](https://img.shields.io/travis/php-v/phpredis/phpredis/develop)](https://img.shields.io/travis/php-v/phpredis/phpredis/develop)
+[![PHP version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://github.com/phpredis/phpredis)
 
 The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt).
 This code has been developed and maintained by Owlient from November 2009 to March 2011.

From cfc81c90fda6a9ba574fc61f1bfe9951ce5d933e Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Sat, 18 Dec 2021 11:32:31 -0800
Subject: [PATCH 1536/1986] Fix typo/bug in cluster_scan_resp (#2045)

---
 cluster_library.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 12da18773f..6f63d9c706 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -2169,8 +2169,8 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
     }
 
     // Read the BULK size
-    if (cluster_check_response(c, &c->reply_type),0 ||
-       c->reply_type != TYPE_BULK)
+    if (cluster_check_response(c, &c->reply_type) ||
+        c->reply_type != TYPE_BULK)
     {
         return FAILURE;
     }

From 72917827523c1d39bbdc82cac63668c48d7d7917 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 27 Dec 2021 15:38:20 +0200
Subject: [PATCH 1537/1986] Use stack-allocated buffer to store error message

---
 library.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/library.c b/library.c
index 2738cf3a09..119af6f7c2 100644
--- a/library.c
+++ b/library.c
@@ -3344,20 +3344,17 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size,
     if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size)
                            == NULL)
     {
-        char *errmsg = NULL;
-
         if (redis_sock->port < 0) {
-            spprintf(&errmsg, 0, "read error on connection to %s", ZSTR_VAL(redis_sock->host));
+            snprintf(buf, buf_size, "read error on connection to %s", ZSTR_VAL(redis_sock->host));
         } else {
-            spprintf(&errmsg, 0, "read error on connection to %s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port);
+            snprintf(buf, buf_size, "read error on connection to %s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port);
         }
         // Close our socket
         redis_sock_disconnect(redis_sock, 1);
 
         // Throw a read error exception
-        REDIS_THROW_EXCEPTION(errmsg, 0);
-        efree(errmsg);
-        return -1;
+        REDIS_THROW_EXCEPTION(buf, 0);
+        return FAILURE;
     }
 
     /* We don't need \r\n */

From 8751a92c03dd289970d168e1b6efe2836908ea77 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 28 Dec 2021 16:40:58 +0200
Subject: [PATCH 1538/1986] Duplicate zval before add_next_index_zval

---
 redis_array.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index eb24669cb0..01776eb338 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -1013,7 +1013,8 @@ ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len)
         /* copy all elements to z_keys */
         array_init(&z_keys);
         for (i = 0; i < argc; ++i) {
-            add_next_index_zval(&z_keys, &z_args[i]);
+            ZVAL_ZVAL(&z_ret, &z_args[i], 1, 0);
+            add_next_index_zval(&z_keys, &z_ret);
         }
         free_zkeys = 1;
     }
@@ -1062,7 +1063,8 @@ ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len)
         array_init(&z_argarray);
         for(i = 0; i < argc; ++i) {
             if (pos[i] == n) {
-                add_next_index_zval(&z_argarray, argv[i]);
+                ZVAL_ZVAL(&z_ret, argv[i], 1, 0);
+                add_next_index_zval(&z_argarray, &z_ret);
                 found++;
             }
         }

From b344649b6f6d84362411507166daad5312a431f5 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 5 Apr 2021 08:27:45 +0300
Subject: [PATCH 1539/1986] [WIP] Issue #1894

Add geosearch and geosearchstore commands.
---
 library.c                  |  69 +++++++++++
 library.h                  |   1 +
 php_redis.h                |   2 +-
 redis.c                    |   8 ++
 redis.stub.php             |   4 +
 redis_arginfo.h            |  23 +++-
 redis_commands.c           | 242 +++++++++++++++++++++++++++++++++++++
 redis_commands.h           |   6 +
 redis_legacy_arginfo.h     |  23 +++-
 tests/RedisClusterTest.php |   2 +
 tests/RedisTest.php        |  28 +++++
 11 files changed, 405 insertions(+), 3 deletions(-)

diff --git a/library.c b/library.c
index 119af6f7c2..a675551d15 100644
--- a/library.c
+++ b/library.c
@@ -1543,6 +1543,75 @@ redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen,
     return type == TYPE_LINE ? 0 : -1;
 }
 
+static int
+geosearch_cast(zval *zv)
+{
+    if (Z_TYPE_P(zv) == IS_ARRAY) {
+        zend_hash_apply(Z_ARRVAL_P(zv), geosearch_cast);
+    } else if (Z_TYPE_P(zv) == IS_STRING) {
+        convert_to_double(zv);
+    }
+    return SUCCESS;
+}
+
+PHP_REDIS_API int
+redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                         zval *z_tab, void *ctx)
+{
+    int numElems;
+    zval z_ret, z_multi_result, z_sub, z_tmp, *z_ele, *zv;
+    zend_string *zkey;
+
+    if (read_mbulk_header(redis_sock, &numElems) < 0) {
+        return FAILURE;
+    }
+
+    if (numElems < 0 && redis_sock->null_mbulk_as_null) {
+        ZVAL_NULL(&z_ret);
+    } else {
+        array_init(&z_ret);
+        if (ctx == NULL) {
+            redis_mbulk_reply_loop(redis_sock, &z_ret, numElems, UNSERIALIZE_NONE);
+        } else {
+            array_init(&z_multi_result);
+            redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_multi_result);
+
+            ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_multi_result), z_ele) {
+                // The first item in the sub-array is always the name of the returned item
+                zv = zend_hash_index_find(Z_ARRVAL_P(z_ele), 0);
+                zkey = zval_get_string(zv);
+
+                zend_hash_index_del(Z_ARRVAL_P(z_ele), 0);
+
+                // The other information is returned in the following order as successive
+                // elements of the sub-array: distance, geohash, coordinates
+                zend_hash_apply(Z_ARRVAL_P(z_ele), geosearch_cast);
+
+                // Copy values to re-order from zero
+                array_init(&z_sub);
+                ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_ele), zv) {
+                    ZVAL_ZVAL(&z_tmp, zv, 1, 0);
+                    add_next_index_zval(&z_sub, &z_tmp);
+                } ZEND_HASH_FOREACH_END();
+
+                add_assoc_zval_ex(&z_ret, ZSTR_VAL(zkey), ZSTR_LEN(zkey), &z_sub);
+                zend_string_release(zkey);
+            } ZEND_HASH_FOREACH_END();
+
+            // Cleanup
+            zval_dtor(&z_multi_result);
+        }
+    }
+
+    if (IS_ATOMIC(redis_sock)) {
+        RETVAL_ZVAL(&z_ret, 0, 1);
+    } else {
+        add_next_index_zval(z_tab, &z_ret);
+    }
+
+    return SUCCESS;
+}
+
 /* Helper function to consume Redis stream message data.  This is useful for
  * multiple stream callers (e.g. XREAD[GROUP], and X[REV]RANGE handlers). */
 PHP_REDIS_API int
diff --git a/library.h b/library.h
index 0b3b713ca1..4fa32eaeac 100644
--- a/library.h
+++ b/library.h
@@ -165,6 +165,7 @@ PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSoc
 PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 /* Helper methods to get configuration values from a HashTable. */
 
diff --git a/php_redis.h b/php_redis.h
index 2c9ec50fab..97ef3b5515 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -23,7 +23,7 @@
 #define PHP_REDIS_H
 
 /* phpredis version */
-#define PHP_REDIS_VERSION "5.3.2"
+#define PHP_REDIS_VERSION "6.0.0-dev"
 
 /* For convenience we store the salt as a printable hex string which requires 2
  * characters per byte + 1 for the NULL terminator */
diff --git a/redis.c b/redis.c
index 991d940e32..5a5d787b96 100644
--- a/redis.c
+++ b/redis.c
@@ -3598,6 +3598,14 @@ PHP_METHOD(Redis, georadiusbymember_ro) {
     REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, redis_read_variant_reply);
 }
 
+PHP_METHOD(Redis, geosearch) {
+    REDIS_PROCESS_CMD(geosearch, redis_geosearch_response);
+}
+
+PHP_METHOD(Redis, geosearchstore) {
+    REDIS_PROCESS_CMD(geosearchstore, redis_long_response);
+}
+
 /*
  * Streams
  */
diff --git a/redis.stub.php b/redis.stub.php
index 1cef27c780..1ab3b31832 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -137,6 +137,10 @@ public function georadiusbymember(string $key, string $member, float $radius, st
 
     public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): array;
 
+    public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): array;
+
+    public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): array;
+
 	/** @return string|Redis */
     public function get(string $key);
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 2efa448170..09b2fda4e5 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 1c4a88252c8b66263f1b1d72af974ba5ce992d40 */
+ * Stub hash: c2cbe49e22cba6f23e98c1676b7769c55a6fd043 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -229,6 +229,23 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_georadiusbymember_ro arginfo_class_Redis_georadiusbymember
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearch, 0, 4, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL)
+	ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearchstore, 0, 5, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
+	ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL)
+	ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_get arginfo_class_Redis_decr
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getAuth, 0, 0, IS_MIXED, 0)
@@ -974,6 +991,8 @@ ZEND_METHOD(Redis, georadius);
 ZEND_METHOD(Redis, georadius_ro);
 ZEND_METHOD(Redis, georadiusbymember);
 ZEND_METHOD(Redis, georadiusbymember_ro);
+ZEND_METHOD(Redis, geosearch);
+ZEND_METHOD(Redis, geosearchstore);
 ZEND_METHOD(Redis, get);
 ZEND_METHOD(Redis, getAuth);
 ZEND_METHOD(Redis, getBit);
@@ -1197,6 +1216,8 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, georadius_ro, arginfo_class_Redis_georadius_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, georadiusbymember, arginfo_class_Redis_georadiusbymember, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, georadiusbymember_ro, arginfo_class_Redis_georadiusbymember_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 3a274ebe2e..895078dab7 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3316,6 +3316,248 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
     return SUCCESS;
 }
 
+int
+redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                    char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    char *key, *unit;
+    int argc = 2;
+    short store_slot = 0;
+    size_t keylen, unitlen;
+    geoOptions gopts = {0};
+    smart_string cmdstr = {0};
+    zval *position, *shape, *opts = NULL, *z_ele;
+    zend_string *zkey;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a",
+                              &key, &keylen, &position, &shape,
+                              &unit, &unitlen, &opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) {
+        argc += 2;
+    } else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) {
+        argc += 3;
+    } else {
+        php_error_docref(NULL, E_WARNING, "Invalid position");
+        return FAILURE;
+    }
+
+    if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) {
+        argc += 2;
+    } else if (Z_TYPE_P(shape) == IS_ARRAY) {
+        argc += 3;
+    } else {
+        php_error_docref(NULL, E_WARNING, "Invalid shape dimensions");
+        return FAILURE;
+    }
+
+    /* Attempt to parse our options array */
+    if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
+            ZVAL_DEREF(z_ele);
+            if (zkey != NULL) {
+                if (zend_string_equals_literal_ci(zkey, "COUNT")) {
+                    if (Z_TYPE_P(z_ele) != IS_LONG || Z_LVAL_P(z_ele) <= 0) {
+                        php_error_docref(NULL, E_WARNING, "COUNT must be an integer > 0!");
+                        return FAILURE;
+                    }
+                    gopts.count = Z_LVAL_P(z_ele);
+                }
+            } else if (Z_TYPE_P(z_ele) == IS_STRING) {
+                if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHCOORD")) {
+                    gopts.withcoord = 1;
+                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHDIST")) {
+                    gopts.withdist = 1;
+                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHHASH")) {
+                    gopts.withhash = 1;
+                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "ASC")) {
+                    gopts.sort = SORT_ASC;
+                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "DESC")) {
+                    gopts.sort = SORT_DESC;
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    /* Increment argc based on options */
+    argc += gopts.withcoord + gopts.withdist + gopts.withhash
+         + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0);
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCH");
+    redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot);
+
+    if (Z_TYPE_P(position) == IS_ARRAY) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT");
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) {
+            ZVAL_DEREF(z_ele);
+            redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
+        } ZEND_HASH_FOREACH_END();
+    } else {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER");
+        redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position));
+    }
+
+    if (Z_TYPE_P(shape) == IS_ARRAY) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX");
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) {
+            ZVAL_DEREF(z_ele);
+            redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
+        } ZEND_HASH_FOREACH_END();
+    } else {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS");
+        redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape));
+    }
+    redis_cmd_append_sstr(&cmdstr, unit, unitlen);
+
+    /* Append optional arguments */
+    if (gopts.withcoord) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHCOORD");
+    if (gopts.withdist) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHDIST");
+    if (gopts.withhash) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHHASH");
+
+    /* Append sort if it's not GEO_NONE */
+    if (gopts.sort == SORT_ASC) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASC");
+    } else if (gopts.sort == SORT_DESC) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DESC");
+    }
+
+    /* Append our count if we've got one */
+    if (gopts.count) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
+        redis_cmd_append_sstr_long(&cmdstr, gopts.count);
+    }
+
+    if ((argc = gopts.withcoord + gopts.withdist + gopts.withhash) > 0) {
+        *ctx = PHPREDIS_CTX_PTR;
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
+int
+redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                         char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    int argc = 3;
+    char *dest, *src, *unit;
+    short store_slot = 0;
+    size_t destlen, srclen, unitlen;
+    geoOptions gopts = {0};
+    smart_string cmdstr = {0};
+    zval *position, *shape, *opts = NULL, *z_ele;
+    zend_string *zkey;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszzs|a",
+                              &dest, &destlen, &src, &srclen, &position, &shape,
+                              &unit, &unitlen, &opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) {
+        argc += 2;
+    } else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) {
+        argc += 3;
+    } else {
+        php_error_docref(NULL, E_WARNING, "Invalid position");
+        return FAILURE;
+    }
+
+    if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) {
+        argc += 2;
+    } else if (Z_TYPE_P(shape) == IS_ARRAY) {
+        argc += 3;
+    } else {
+        php_error_docref(NULL, E_WARNING, "Invalid shape dimensions");
+        return FAILURE;
+    }
+
+    /* Attempt to parse our options array */
+    if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
+            ZVAL_DEREF(z_ele);
+            if (zkey != NULL) {
+                if (zend_string_equals_literal_ci(zkey, "COUNT")) {
+                    if (Z_TYPE_P(z_ele) != IS_LONG || Z_LVAL_P(z_ele) <= 0) {
+                        php_error_docref(NULL, E_WARNING, "COUNT must be an integer > 0!");
+                        return FAILURE;
+                    }
+                    gopts.count = Z_LVAL_P(z_ele);
+                }
+            } else if (Z_TYPE_P(z_ele) == IS_STRING) {
+                if (!strcasecmp(Z_STRVAL_P(z_ele), "ASC")) {
+                    gopts.sort = SORT_ASC;
+                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "DESC")) {
+                    gopts.sort = SORT_DESC;
+                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "STOREDIST")) {
+                    gopts.store = STORE_DIST;
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+
+    }
+
+    /* Increment argc based on options */
+    argc += gopts.withcoord + gopts.withdist + gopts.withhash
+         + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0)
+         + (gopts.store != STORE_NONE);
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCHSTORE");
+    redis_cmd_append_sstr_key(&cmdstr, dest, destlen, redis_sock, slot);
+    redis_cmd_append_sstr_key(&cmdstr, src, srclen, redis_sock, slot);
+
+    if (Z_TYPE_P(position) == IS_ARRAY) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT");
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) {
+            ZVAL_DEREF(z_ele);
+            redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
+        } ZEND_HASH_FOREACH_END();
+    } else {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER");
+        redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position));
+    }
+
+    if (Z_TYPE_P(shape) == IS_ARRAY) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX");
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) {
+            ZVAL_DEREF(z_ele);
+            redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
+        } ZEND_HASH_FOREACH_END();
+    } else {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS");
+        redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape));
+    }
+    redis_cmd_append_sstr(&cmdstr, unit, unitlen);
+
+    /* Append sort if it's not GEO_NONE */
+    if (gopts.sort == SORT_ASC) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASC");
+    } else if (gopts.sort == SORT_DESC) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DESC");
+    }
+
+    /* Append our count if we've got one */
+    if (gopts.count) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
+        redis_cmd_append_sstr_long(&cmdstr, gopts.count);
+    }
+
+    if (gopts.store == STORE_DIST) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "STOREDIST");
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 /* MIGRATE */
 int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index 88c66bcff7..851e28b224 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -145,6 +145,12 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
+int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 /* Commands which need a unique construction mechanism.  This is either because
  * they don't share a signature with any other command, or because there is
  * specific processing we do (e.g. verifying subarguments) that make them
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index f1022664b2..9021f63ca4 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 1c4a88252c8b66263f1b1d72af974ba5ce992d40 */
+ * Stub hash: c2cbe49e22cba6f23e98c1676b7769c55a6fd043 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -217,6 +217,23 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_georadiusbymember_ro arginfo_class_Redis_georadiusbymember
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geosearch, 0, 0, 4)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, position)
+	ZEND_ARG_INFO(0, shape)
+	ZEND_ARG_INFO(0, unit)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geosearchstore, 0, 0, 5)
+	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, position)
+	ZEND_ARG_INFO(0, shape)
+	ZEND_ARG_INFO(0, unit)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_get arginfo_class_Redis__prefix
 
 #define arginfo_class_Redis_getAuth arginfo_class_Redis___destruct
@@ -876,6 +893,8 @@ ZEND_METHOD(Redis, georadius);
 ZEND_METHOD(Redis, georadius_ro);
 ZEND_METHOD(Redis, georadiusbymember);
 ZEND_METHOD(Redis, georadiusbymember_ro);
+ZEND_METHOD(Redis, geosearch);
+ZEND_METHOD(Redis, geosearchstore);
 ZEND_METHOD(Redis, get);
 ZEND_METHOD(Redis, getAuth);
 ZEND_METHOD(Redis, getBit);
@@ -1099,6 +1118,8 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, georadius_ro, arginfo_class_Redis_georadius_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, georadiusbymember, arginfo_class_Redis_georadiusbymember, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, georadiusbymember_ro, arginfo_class_Redis_georadiusbymember_ro, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 394d13f2f2..f491e92aa0 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -58,6 +58,8 @@ public function testzDiffStore() { return $this->markTestSkipped(); }
     public function testzMscore() { return $this->marktestSkipped(); }
     public function testZRandMember() { return $this->marktestSkipped(); }
     public function testCopy() { return $this->marktestSkipped(); }
+    public function testGeoSearch() { return $this->marktestSkipped(); }
+    public function testGeoSearchStore() { return $this->marktestSkipped(); }
 
     /* Session locking feature is currently not supported in in context of Redis Cluster.
        The biggest issue for this is the distribution nature of Redis cluster */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index fa26d18e25..66b51f7fc5 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5875,6 +5875,34 @@ public function testGeoDist() {
         $this->assertEquals(round($r1, 8), round($r2, 8));
     }
 
+    public function testGeoSearch() {
+        if (!$this->minVersionCheck("6.2.0")) {
+            return $this->markTestSkipped();
+        }
+
+        $this->addCities('gk');
+
+        $this->assertEquals($this->redis->geosearch('gk', 'Chico', 1, 'm'), ['Chico']);
+        $this->assertValidate($this->redis->geosearch('gk', 'Chico', 1, 'm', ['withcoord', 'withdist', 'withhash']), function ($v) {
+            $this->assertArrayKey($v, 'Chico', 'is_array');
+            $this->assertEquals(count($v['Chico']), 3);
+            $this->assertArrayKey($v['Chico'], 0, 'is_float');
+            $this->assertArrayKey($v['Chico'], 1, 'is_int');
+            $this->assertArrayKey($v['Chico'], 2, 'is_array');
+            return true;
+        });
+    }
+
+    public function testGeoSearchStore() {
+        if (!$this->minVersionCheck("6.2.0")) {
+            return $this->markTestSkipped();
+        }
+
+        $this->addCities('gk');
+        $this->assertEquals($this->redis->geosearchstore('{gk}', 'gk', 'Chico', 100, 'km'), 3);
+        $this->assertEquals($this->redis->geosearch('{gk}', 'Chico', 1, 'm'), ['Chico']);
+    }
+
     /* Test a 'raw' command */
     public function testRawCommand() {
         $this->redis->set('mykey','some-value');

From bf7a33e34a2835a10855ee7129fd79d650b83982 Mon Sep 17 00:00:00 2001
From: Shueh Chou Lu 
Date: Wed, 5 Jan 2022 06:41:36 +0800
Subject: [PATCH 1540/1986] Add stream explanation in connect method (#2031)

---
 README.markdown | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 21f6be9dc9..d29b12a352 100644
--- a/README.markdown
+++ b/README.markdown
@@ -222,6 +222,7 @@ _**Description**_: Connects to a Redis instance.
 *reserved*: should be NULL if retry_interval is specified  
 *retry_interval*: int, value in milliseconds (optional)  
 *read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited)
+*others*: array, with PhpRedis >= 5.3.0, it allows setting _auth_ and [_stream_](https://www.php.net/manual/en/context.ssl.php) configuration.
 
 ##### *Return value*
 
@@ -238,7 +239,7 @@ $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
 $redis->connect('/tmp/redis.sock'); // unix domain socket.
 $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts.
 
-/* With PhpRedis >= 5.3.0 you can specify authentication information on connect */
+/* With PhpRedis >= 5.3.0 you can specify authentication and stream information on connect */
 $redis->connect('127.0.0.1', 6379, 1, NULL, 0, 0, ['auth' => ['phpredis', 'phpredis']]);
 ~~~
 

From a54714666160aeecc0479605b9cf0fbafd8db8bf Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 15 Jan 2022 21:27:25 +0200
Subject: [PATCH 1541/1986] Issue #2055

---
 redis_array.c                | 50 ++++++++++++++++++++++++++++++++++++
 redis_array.stub.php         |  5 ++++
 redis_array_arginfo.h        | 19 +++++++++++++-
 redis_array_legacy_arginfo.h | 19 +++++++++++++-
 4 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 01776eb338..4d768b3f45 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -1113,6 +1113,56 @@ PHP_METHOD(RedisArray, unlink) {
     ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1);
 }
 
+static void
+ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, int kw_len)
+{
+    RedisArray *ra;
+    zend_string *key, *pattern = NULL;
+    zval *object, *redis_inst, *z_iter, z_fun, z_args[4];
+    zend_long count = 0;
+
+    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OSz/|S!l",
+            &object, redis_array_ce, &key, &z_iter, &pattern, &count) == FAILURE) {
+        RETURN_FALSE;
+    }
+
+    if ((ra = redis_array_get(object)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    if ((redis_inst = ra_find_node(ra, ZSTR_VAL(key), ZSTR_LEN(key), NULL)) == NULL) {
+        php_error_docref(NULL, E_ERROR, "Could not find any redis servers for this key.");
+        RETURN_FALSE;
+    }
+
+    ZVAL_STR(&z_args[0], key);
+    ZVAL_NEW_REF(&z_args[1], z_iter);
+    if (pattern) ZVAL_STR(&z_args[2], pattern);
+    ZVAL_LONG(&z_args[3], count);
+
+    ZVAL_STRINGL(&z_fun, kw, kw_len);
+    call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, ZEND_NUM_ARGS(), z_args);
+    zval_dtor(&z_fun);
+
+    ZVAL_ZVAL(z_iter, &z_args[1], 0, 1);
+}
+
+PHP_METHOD(RedisArray, hscan)
+{
+    ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HSCAN", sizeof("HSCAN") - 1);
+}
+
+PHP_METHOD(RedisArray, sscan)
+{
+    ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SSCAN", sizeof("SSCAN") - 1);
+}
+
+PHP_METHOD(RedisArray, zscan)
+{
+    ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZSCAN", sizeof("ZSCAN") - 1);
+}
+
+
 PHP_METHOD(RedisArray, multi)
 {
     zval *object;
diff --git a/redis_array.stub.php b/redis_array.stub.php
index 8845d33ce4..1e071a6cbd 100644
--- a/redis_array.stub.php
+++ b/redis_array.stub.php
@@ -39,6 +39,8 @@ public function flushdb(): bool|array;
 
     public function getOption(int $opt): bool|array;
 
+    public function hscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+
     public function info(): bool|array;
 
     public function keys(string $pattern): bool|array;
@@ -57,8 +59,11 @@ public function select(int $index): bool|array;
 
     public function setOption(int $opt, string $value): bool|array;
 
+    public function sscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+
     public function unlink(string|array $key, string ...$otherkeys): bool|int;
 
     public function unwatch(): bool|null;
 
+    public function zscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 }
diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h
index 5a0d034ee6..f262b1775d 100644
--- a/redis_array_arginfo.h
+++ b/redis_array_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e7f3cbb6cba7b52d3cc2d8b2f311dcb37c93ea5b */
+ * Stub hash: 16a0857d62817f14eef16a00e80e587f318b9052 */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0)
@@ -54,6 +54,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_getOption, 0, 1
 	ZEND_ARG_TYPE_INFO(0, opt, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_RedisArray_info arginfo_class_RedisArray__continuum
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_keys, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
@@ -86,10 +93,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_setOption, 0, 2
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisArray_sscan arginfo_class_RedisArray_hscan
+
 #define arginfo_class_RedisArray_unlink arginfo_class_RedisArray_del
 
 #define arginfo_class_RedisArray_unwatch arginfo_class_RedisArray_discard
 
+#define arginfo_class_RedisArray_zscan arginfo_class_RedisArray_hscan
+
 
 ZEND_METHOD(RedisArray, __call);
 ZEND_METHOD(RedisArray, __construct);
@@ -107,6 +118,7 @@ ZEND_METHOD(RedisArray, exec);
 ZEND_METHOD(RedisArray, flushall);
 ZEND_METHOD(RedisArray, flushdb);
 ZEND_METHOD(RedisArray, getOption);
+ZEND_METHOD(RedisArray, hscan);
 ZEND_METHOD(RedisArray, info);
 ZEND_METHOD(RedisArray, keys);
 ZEND_METHOD(RedisArray, mget);
@@ -116,8 +128,10 @@ ZEND_METHOD(RedisArray, ping);
 ZEND_METHOD(RedisArray, save);
 ZEND_METHOD(RedisArray, select);
 ZEND_METHOD(RedisArray, setOption);
+ZEND_METHOD(RedisArray, sscan);
 ZEND_METHOD(RedisArray, unlink);
 ZEND_METHOD(RedisArray, unwatch);
+ZEND_METHOD(RedisArray, zscan);
 
 
 static const zend_function_entry class_RedisArray_methods[] = {
@@ -137,6 +151,7 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, flushall, arginfo_class_RedisArray_flushall, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, flushdb, arginfo_class_RedisArray_flushdb, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, getOption, arginfo_class_RedisArray_getOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, hscan, arginfo_class_RedisArray_hscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, info, arginfo_class_RedisArray_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, keys, arginfo_class_RedisArray_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, mget, arginfo_class_RedisArray_mget, ZEND_ACC_PUBLIC)
@@ -146,7 +161,9 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, save, arginfo_class_RedisArray_save, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, select, arginfo_class_RedisArray_select, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, setOption, arginfo_class_RedisArray_setOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, sscan, arginfo_class_RedisArray_sscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, unlink, arginfo_class_RedisArray_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, unwatch, arginfo_class_RedisArray_unwatch, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, zscan, arginfo_class_RedisArray_zscan, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h
index b655a7b868..f549bd2a95 100644
--- a/redis_array_legacy_arginfo.h
+++ b/redis_array_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e7f3cbb6cba7b52d3cc2d8b2f311dcb37c93ea5b */
+ * Stub hash: 16a0857d62817f14eef16a00e80e587f318b9052 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2)
 	ZEND_ARG_INFO(0, function_name)
@@ -51,6 +51,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_getOption, 0, 0, 1)
 	ZEND_ARG_INFO(0, opt)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_hscan, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(1, iterator)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_RedisArray_info arginfo_class_RedisArray__continuum
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_keys, 0, 0, 1)
@@ -83,10 +90,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_setOption, 0, 0, 2)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisArray_sscan arginfo_class_RedisArray_hscan
+
 #define arginfo_class_RedisArray_unlink arginfo_class_RedisArray_del
 
 #define arginfo_class_RedisArray_unwatch arginfo_class_RedisArray__continuum
 
+#define arginfo_class_RedisArray_zscan arginfo_class_RedisArray_hscan
+
 
 ZEND_METHOD(RedisArray, __call);
 ZEND_METHOD(RedisArray, __construct);
@@ -104,6 +115,7 @@ ZEND_METHOD(RedisArray, exec);
 ZEND_METHOD(RedisArray, flushall);
 ZEND_METHOD(RedisArray, flushdb);
 ZEND_METHOD(RedisArray, getOption);
+ZEND_METHOD(RedisArray, hscan);
 ZEND_METHOD(RedisArray, info);
 ZEND_METHOD(RedisArray, keys);
 ZEND_METHOD(RedisArray, mget);
@@ -113,8 +125,10 @@ ZEND_METHOD(RedisArray, ping);
 ZEND_METHOD(RedisArray, save);
 ZEND_METHOD(RedisArray, select);
 ZEND_METHOD(RedisArray, setOption);
+ZEND_METHOD(RedisArray, sscan);
 ZEND_METHOD(RedisArray, unlink);
 ZEND_METHOD(RedisArray, unwatch);
+ZEND_METHOD(RedisArray, zscan);
 
 
 static const zend_function_entry class_RedisArray_methods[] = {
@@ -134,6 +148,7 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, flushall, arginfo_class_RedisArray_flushall, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, flushdb, arginfo_class_RedisArray_flushdb, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, getOption, arginfo_class_RedisArray_getOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, hscan, arginfo_class_RedisArray_hscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, info, arginfo_class_RedisArray_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, keys, arginfo_class_RedisArray_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, mget, arginfo_class_RedisArray_mget, ZEND_ACC_PUBLIC)
@@ -143,7 +158,9 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, save, arginfo_class_RedisArray_save, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, select, arginfo_class_RedisArray_select, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, setOption, arginfo_class_RedisArray_setOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, sscan, arginfo_class_RedisArray_sscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, unlink, arginfo_class_RedisArray_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, unwatch, arginfo_class_RedisArray_unwatch, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, zscan, arginfo_class_RedisArray_zscan, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 8e58c9d4d6fdd364016165f805274d7c0db429e4 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 16 Jan 2022 16:32:46 +0200
Subject: [PATCH 1542/1986] Remove unused redis_single_line_reply

---
 library.c | 24 ------------------------
 library.h |  2 --
 2 files changed, 26 deletions(-)

diff --git a/library.c b/library.c
index a675551d15..a7a4840671 100644
--- a/library.c
+++ b/library.c
@@ -2095,30 +2095,6 @@ PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
     return SUCCESS;
 }
 
-PHP_REDIS_API
-void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                             zval *z_tab, void *ctx)
-{
-    char buffer[4096];
-    size_t len;
-
-    if (redis_sock_read_single_line(redis_sock, buffer, sizeof(buffer), &len, 1) < 0) {
-        if (IS_ATOMIC(redis_sock)) {
-            RETURN_FALSE;
-        } else {
-            add_next_index_bool(z_tab, 0);
-        }
-        return;
-    }
-
-    //str = estrndup(buffer, len);
-    if (IS_ATOMIC(redis_sock)) {
-        RETVAL_STRINGL(buffer, len);
-    } else {
-        add_next_index_stringl(z_tab, buffer, len);
-    }
-}
-
 /* like string response, but never unserialized. */
 PHP_REDIS_API int
 redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
diff --git a/library.h b/library.h
index 4fa32eaeac..d852ba1ff0 100644
--- a/library.h
+++ b/library.h
@@ -63,8 +63,6 @@ PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, Redi
 PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-PHP_REDIS_API void redis_single_line_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret);

From 92540ad348ba4052dfc6ecf640a67c6459739c35 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 16 Jan 2022 17:04:08 +0200
Subject: [PATCH 1543/1986] Refactor redis_sock_auth

---
 library.c | 58 ++++++++++++-------------------------------------------
 1 file changed, 12 insertions(+), 46 deletions(-)

diff --git a/library.c b/library.c
index a7a4840671..2b07590b83 100644
--- a/library.c
+++ b/library.c
@@ -148,17 +148,6 @@ static int reselect_db(RedisSock *redis_sock) {
     return 0;
 }
 
-/* Attempt to read a single +OK response */
-static int redis_sock_read_ok(RedisSock *redis_sock) {
-    char buf[64];
-    size_t len;
-
-    if (redis_sock_read_single_line(redis_sock, buf, sizeof(buf), &len, 0) < 0)
-        return FAILURE;
-
-    return REDIS_STRCMP_STATIC(buf, len, "OK") ? SUCCESS : FAILURE;
-}
-
 /* Append an AUTH command to a smart string if neccessary.  This will either
  * append the new style AUTH  , old style AUTH , or
  * append no command at all.  Function returns 1 if we appended a command
@@ -236,19 +225,23 @@ redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen) {
 
 /* Send Redis AUTH and process response */
 PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) {
-    char *cmd;
-    int cmdlen, rv = FAILURE;
+    char *cmd, inbuf[4096];
+    int cmdlen;
+    size_t len;
 
     if ((cmd = redis_sock_auth_cmd(redis_sock, &cmdlen)) == NULL)
         return SUCCESS;
 
-    if (redis_sock_write(redis_sock, cmd, cmdlen) < 0)
-        goto cleanup;
-
-    rv = redis_sock_read_ok(redis_sock) == SUCCESS ? SUCCESS : FAILURE;
-cleanup:
+    if (redis_sock_write(redis_sock, cmd, cmdlen) < 0) {
+        efree(cmd);
+        return FAILURE;
+    }
     efree(cmd);
-    return rv;
+
+    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "+OK", 3)) {
+        return FAILURE;
+    }
+    return SUCCESS;
 }
 
 /* Helper function and macro to test a RedisSock error prefix. */
@@ -1516,33 +1509,6 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return 0;
 }
 
-/* Consume message ID */
-PHP_REDIS_API int
-redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen,
-                            size_t *linelen, int set_err)
-{
-    REDIS_REPLY_TYPE type;
-    long info;
-
-    if (redis_read_reply_type(redis_sock, &type, &info) < 0 ||
-        (type != TYPE_LINE && type != TYPE_ERR))
-    {
-        return -1;
-    }
-
-    if (redis_sock_gets(redis_sock, buffer, buflen, linelen) < 0) {
-        return -1;
-    }
-
-    if (set_err && type == TYPE_ERR) {
-        if (IS_ATOMIC(redis_sock)) {
-            redis_sock_set_err(redis_sock, buffer, *linelen);
-        }
-    }
-
-    return type == TYPE_LINE ? 0 : -1;
-}
-
 static int
 geosearch_cast(zval *zv)
 {

From bcfbd5b2c4c5021fd8e83e89ea4eb6dbdbe178d7 Mon Sep 17 00:00:00 2001
From: cheerfuldustin 
Date: Fri, 21 Jan 2022 02:53:55 +0800
Subject: [PATCH 1544/1986] Fix zRank, zRevRank documentation (#2061)

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index d29b12a352..ec7afd5023 100644
--- a/README.markdown
+++ b/README.markdown
@@ -3064,7 +3064,7 @@ _**Description**_: Returns the rank of a given member in the specified sorted se
 *member*
 
 ##### *Return value*
-*Long*, the item's score.
+*Long*, the item's rank.
 
 ##### *Example*
 ~~~php

From a64a0c37853bfcb257ffb5b26d90a9c74b6e0a8e Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Thu, 20 Jan 2022 10:54:57 -0800
Subject: [PATCH 1545/1986] Fix fallthrough warnings and refactor adding a
 score (#2049)

* Suppress implicit fallthrough warnings by using an attribute if we
  have it and a do { } while(0) if we don't.

* Move duplicated logic for appending a ZSET score to one utility
  function.
---
 cluster_library.c |   1 +
 common.h          |  10 ++++
 library.c         |   4 +-
 redis_commands.c  | 129 ++++++++++++++++------------------------------
 4 files changed, 58 insertions(+), 86 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 6f63d9c706..0dee495da5 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -222,6 +222,7 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
                 r->str = estrndup(line_reply, len);
                 r->len = len;
             }
+            REDIS_FALLTHROUGH;
         case TYPE_ERR:
             return r;
         case TYPE_BULK:
diff --git a/common.h b/common.h
index 100b45eb1b..f27748969d 100644
--- a/common.h
+++ b/common.h
@@ -16,6 +16,16 @@
 #define PHPREDIS_GET_OBJECT(class_entry, o) (class_entry *)((char *)o - XtOffsetOf(class_entry, std))
 #define PHPREDIS_ZVAL_GET_OBJECT(class_entry, z) PHPREDIS_GET_OBJECT(class_entry, Z_OBJ_P(z))
 
+/* We'll fallthrough if we want to */
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+#if __has_attribute(__fallthrough__)
+#define REDIS_FALLTHROUGH __attribute__((__fallthrough__))
+#else
+#define REDIS_FALLTHROUGH do { } while (0)
+#endif
+
 /* NULL check so Eclipse doesn't go crazy */
 #ifndef NULL
 #define NULL   ((void *) 0)
diff --git a/library.c b/library.c
index 2b07590b83..386763ec52 100644
--- a/library.c
+++ b/library.c
@@ -652,8 +652,7 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len)
             if(memcmp(inbuf + 1, "-1", 2) == 0) {
                 return NULL;
             }
-            /* fall through */
-
+            REDIS_FALLTHROUGH;
         case '+':
         case ':':
             /* Single Line Reply */
@@ -662,6 +661,7 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len)
                 *buf_len = len;
                 return estrndup(inbuf, *buf_len);
             }
+            REDIS_FALLTHROUGH;
         default:
             zend_throw_exception_ex(redis_exception_ce, 0,
                 "protocol error, got '%c' as reply type byte\n",
diff --git a/redis_commands.c b/redis_commands.c
index 895078dab7..1e9a7ecfb8 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -720,6 +720,42 @@ redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+static int redis_cmd_append_sstr_score(smart_string *dst, zval *score) {
+    zend_uchar type;
+    zend_long lval;
+    size_t cmdlen;
+    double dval;
+
+    /* Get current command length */
+    cmdlen = dst->len;
+
+    if (Z_TYPE_P(score) == IS_LONG) {
+        redis_cmd_append_sstr_long(dst, Z_LVAL_P(score));
+    } else if (Z_TYPE_P(score) == IS_DOUBLE) {
+        redis_cmd_append_sstr_dbl(dst, Z_DVAL_P(score));
+    } else if (Z_TYPE_P(score) == IS_STRING) {
+        type = is_numeric_string(Z_STRVAL_P(score), Z_STRLEN_P(score), &lval, &dval, 0);
+        if (type == IS_LONG) {
+            redis_cmd_append_sstr_long(dst, lval);
+        } else if (type == IS_DOUBLE) {
+            redis_cmd_append_sstr_dbl(dst, dval);
+        } else if (zend_string_equals_literal_ci(Z_STR_P(score), "-inf") ||
+                   zend_string_equals_literal_ci(Z_STR_P(score), "+inf") ||
+                   zend_string_equals_literal_ci(Z_STR_P(score), "inf"))
+        {
+            redis_cmd_append_sstr_zstr(dst, Z_STR_P(score));
+        }
+    }
+
+    /* Success if we appended something */
+    if (dst->len > cmdlen)
+        return SUCCESS;
+
+    /* Nothing appended, failure */
+    php_error_docref(NULL, E_WARNING, "Weights must be numeric or '-inf','inf','+inf'");
+    return FAILURE;
+}
+
 int
 redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char *kw, char **cmd, int *cmd_len, short *slot,
@@ -785,38 +821,10 @@ redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WEIGHTS");
         ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_weights), z_ele) {
             ZVAL_DEREF(z_ele);
-            switch (Z_TYPE_P(z_ele)) {
-                case IS_LONG:
-                    redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele));
-                    break;
-                case IS_DOUBLE:
-                    redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(z_ele));
-                    break;
-                case IS_STRING: {
-                    double dval;
-                    zend_long lval;
-                    zend_uchar type = is_numeric_string(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &lval, &dval, 0);
-                    if (type == IS_LONG) {
-                        redis_cmd_append_sstr_long(&cmdstr, lval);
-                        break;
-                    } else if (type == IS_DOUBLE) {
-                        redis_cmd_append_sstr_dbl(&cmdstr, dval);
-                        break;
-                    } else if (strncasecmp(Z_STRVAL_P(z_ele), "-inf", sizeof("-inf") - 1) == 0 ||
-                               strncasecmp(Z_STRVAL_P(z_ele), "+inf", sizeof("+inf") - 1) == 0 ||
-                               strncasecmp(Z_STRVAL_P(z_ele), "inf", sizeof("inf") - 1) == 0
-                    ) {
-                        redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele));
-                        break;
-                    }
-                    // fall through
-                }
-                default:
-                    php_error_docref(NULL, E_WARNING,
-                        "Weights must be numeric or '-inf','inf','+inf'");
-                    if (aggregate) zend_string_release(aggregate);
-                    efree(cmdstr.c);
-                    return FAILURE;
+            if (redis_cmd_append_sstr_score(&cmdstr, z_ele) == FAILURE) {
+                if (aggregate) zend_string_release(aggregate);
+                efree(cmdstr.c);
+                return FAILURE;
             }
         } ZEND_HASH_FOREACH_END();
     }
@@ -975,39 +983,10 @@ redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
         // Process our weights
         ZEND_HASH_FOREACH_VAL(ht_weights, z_ele) {
-            // Ignore non numeric args unless they're inf/-inf
             ZVAL_DEREF(z_ele);
-            switch (Z_TYPE_P(z_ele)) {
-                case IS_LONG:
-                    redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele));
-                    break;
-                case IS_DOUBLE:
-                    redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(z_ele));
-                    break;
-                case IS_STRING: {
-                    double dval;
-                    zend_long lval;
-                    zend_uchar type = is_numeric_string(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &lval, &dval, 0);
-                    if (type == IS_LONG) {
-                        redis_cmd_append_sstr_long(&cmdstr, lval);
-                        break;
-                    } else if (type == IS_DOUBLE) {
-                        redis_cmd_append_sstr_dbl(&cmdstr, dval);
-                        break;
-                    } else if (strncasecmp(Z_STRVAL_P(z_ele), "-inf", sizeof("-inf") - 1) == 0 ||
-                               strncasecmp(Z_STRVAL_P(z_ele), "+inf", sizeof("+inf") - 1) == 0 ||
-                               strncasecmp(Z_STRVAL_P(z_ele), "inf", sizeof("inf") - 1) == 0
-                    ) {
-                        redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele));
-                        break;
-                    }
-                    // fall through
-                }
-                default:
-                    php_error_docref(NULL, E_WARNING,
-                        "Weights must be numeric or '-inf','inf','+inf'");
-                    efree(cmdstr.c);
-                    return FAILURE;
+            if (redis_cmd_append_sstr_score(&cmdstr, z_ele) == FAILURE) {
+                efree(cmdstr.c);
+                return FAILURE;
             }
         } ZEND_HASH_FOREACH_END();
     }
@@ -1559,7 +1538,7 @@ static int redis_try_get_expiry(zval *zv, zend_long *lval) {
     switch (is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), lval, &dval, 0)) {
         case IS_DOUBLE:
             *lval = dval;
-            /* fallthrough */
+            REDIS_FALLTHROUGH;
         case IS_LONG:
             return SUCCESS;
         default:
@@ -2956,25 +2935,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     // Now the rest of our arguments
     while (i < num) {
         // Append score and member
-        switch (Z_TYPE(z_args[i])) {
-        case IS_LONG:
-        case IS_DOUBLE:
-            redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(&z_args[i]));
-            break;
-        case IS_STRING:
-            /* The score values must be the string representation of a double
-             * precision floating point number. +inf and -inf values are valid
-             * values as well. */
-            if (strncasecmp(Z_STRVAL(z_args[i]), "-inf", 4) == 0 ||
-                strncasecmp(Z_STRVAL(z_args[i]), "+inf", 4) == 0 ||
-                is_numeric_string(Z_STRVAL(z_args[i]), Z_STRLEN(z_args[i]), NULL, NULL, 0) != 0
-            ) {
-                redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[i]), Z_STRLEN(z_args[i]));
-                break;
-            }
-            // fall through
-        default:
-            php_error_docref(NULL, E_WARNING, "Scores must be numeric or '-inf','+inf'");
+        if (redis_cmd_append_sstr_score(&cmdstr, &z_args[i]) == FAILURE) {
             smart_string_free(&cmdstr);
             efree(z_args);
             return FAILURE;

From 08a9d5db914e8177d289870a34fd740e66b099f2 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 15 Jan 2022 21:27:25 +0200
Subject: [PATCH 1546/1986] Issue #2055

---
 redis_array.c                | 50 ++++++++++++++++++++++++++++++++++++
 redis_array.stub.php         |  5 ++++
 redis_array_arginfo.h        | 19 +++++++++++++-
 redis_array_legacy_arginfo.h | 19 +++++++++++++-
 4 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 01776eb338..4d768b3f45 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -1113,6 +1113,56 @@ PHP_METHOD(RedisArray, unlink) {
     ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1);
 }
 
+static void
+ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, int kw_len)
+{
+    RedisArray *ra;
+    zend_string *key, *pattern = NULL;
+    zval *object, *redis_inst, *z_iter, z_fun, z_args[4];
+    zend_long count = 0;
+
+    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OSz/|S!l",
+            &object, redis_array_ce, &key, &z_iter, &pattern, &count) == FAILURE) {
+        RETURN_FALSE;
+    }
+
+    if ((ra = redis_array_get(object)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    if ((redis_inst = ra_find_node(ra, ZSTR_VAL(key), ZSTR_LEN(key), NULL)) == NULL) {
+        php_error_docref(NULL, E_ERROR, "Could not find any redis servers for this key.");
+        RETURN_FALSE;
+    }
+
+    ZVAL_STR(&z_args[0], key);
+    ZVAL_NEW_REF(&z_args[1], z_iter);
+    if (pattern) ZVAL_STR(&z_args[2], pattern);
+    ZVAL_LONG(&z_args[3], count);
+
+    ZVAL_STRINGL(&z_fun, kw, kw_len);
+    call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, ZEND_NUM_ARGS(), z_args);
+    zval_dtor(&z_fun);
+
+    ZVAL_ZVAL(z_iter, &z_args[1], 0, 1);
+}
+
+PHP_METHOD(RedisArray, hscan)
+{
+    ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "HSCAN", sizeof("HSCAN") - 1);
+}
+
+PHP_METHOD(RedisArray, sscan)
+{
+    ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SSCAN", sizeof("SSCAN") - 1);
+}
+
+PHP_METHOD(RedisArray, zscan)
+{
+    ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZSCAN", sizeof("ZSCAN") - 1);
+}
+
+
 PHP_METHOD(RedisArray, multi)
 {
     zval *object;
diff --git a/redis_array.stub.php b/redis_array.stub.php
index 8845d33ce4..1e071a6cbd 100644
--- a/redis_array.stub.php
+++ b/redis_array.stub.php
@@ -39,6 +39,8 @@ public function flushdb(): bool|array;
 
     public function getOption(int $opt): bool|array;
 
+    public function hscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+
     public function info(): bool|array;
 
     public function keys(string $pattern): bool|array;
@@ -57,8 +59,11 @@ public function select(int $index): bool|array;
 
     public function setOption(int $opt, string $value): bool|array;
 
+    public function sscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+
     public function unlink(string|array $key, string ...$otherkeys): bool|int;
 
     public function unwatch(): bool|null;
 
+    public function zscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 }
diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h
index 5a0d034ee6..f262b1775d 100644
--- a/redis_array_arginfo.h
+++ b/redis_array_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e7f3cbb6cba7b52d3cc2d8b2f311dcb37c93ea5b */
+ * Stub hash: 16a0857d62817f14eef16a00e80e587f318b9052 */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0)
@@ -54,6 +54,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_getOption, 0, 1
 	ZEND_ARG_TYPE_INFO(0, opt, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_RedisArray_info arginfo_class_RedisArray__continuum
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_keys, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
@@ -86,10 +93,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_setOption, 0, 2
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisArray_sscan arginfo_class_RedisArray_hscan
+
 #define arginfo_class_RedisArray_unlink arginfo_class_RedisArray_del
 
 #define arginfo_class_RedisArray_unwatch arginfo_class_RedisArray_discard
 
+#define arginfo_class_RedisArray_zscan arginfo_class_RedisArray_hscan
+
 
 ZEND_METHOD(RedisArray, __call);
 ZEND_METHOD(RedisArray, __construct);
@@ -107,6 +118,7 @@ ZEND_METHOD(RedisArray, exec);
 ZEND_METHOD(RedisArray, flushall);
 ZEND_METHOD(RedisArray, flushdb);
 ZEND_METHOD(RedisArray, getOption);
+ZEND_METHOD(RedisArray, hscan);
 ZEND_METHOD(RedisArray, info);
 ZEND_METHOD(RedisArray, keys);
 ZEND_METHOD(RedisArray, mget);
@@ -116,8 +128,10 @@ ZEND_METHOD(RedisArray, ping);
 ZEND_METHOD(RedisArray, save);
 ZEND_METHOD(RedisArray, select);
 ZEND_METHOD(RedisArray, setOption);
+ZEND_METHOD(RedisArray, sscan);
 ZEND_METHOD(RedisArray, unlink);
 ZEND_METHOD(RedisArray, unwatch);
+ZEND_METHOD(RedisArray, zscan);
 
 
 static const zend_function_entry class_RedisArray_methods[] = {
@@ -137,6 +151,7 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, flushall, arginfo_class_RedisArray_flushall, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, flushdb, arginfo_class_RedisArray_flushdb, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, getOption, arginfo_class_RedisArray_getOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, hscan, arginfo_class_RedisArray_hscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, info, arginfo_class_RedisArray_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, keys, arginfo_class_RedisArray_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, mget, arginfo_class_RedisArray_mget, ZEND_ACC_PUBLIC)
@@ -146,7 +161,9 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, save, arginfo_class_RedisArray_save, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, select, arginfo_class_RedisArray_select, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, setOption, arginfo_class_RedisArray_setOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, sscan, arginfo_class_RedisArray_sscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, unlink, arginfo_class_RedisArray_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, unwatch, arginfo_class_RedisArray_unwatch, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, zscan, arginfo_class_RedisArray_zscan, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h
index b655a7b868..f549bd2a95 100644
--- a/redis_array_legacy_arginfo.h
+++ b/redis_array_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e7f3cbb6cba7b52d3cc2d8b2f311dcb37c93ea5b */
+ * Stub hash: 16a0857d62817f14eef16a00e80e587f318b9052 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2)
 	ZEND_ARG_INFO(0, function_name)
@@ -51,6 +51,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_getOption, 0, 0, 1)
 	ZEND_ARG_INFO(0, opt)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_hscan, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(1, iterator)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_RedisArray_info arginfo_class_RedisArray__continuum
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_keys, 0, 0, 1)
@@ -83,10 +90,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_setOption, 0, 0, 2)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisArray_sscan arginfo_class_RedisArray_hscan
+
 #define arginfo_class_RedisArray_unlink arginfo_class_RedisArray_del
 
 #define arginfo_class_RedisArray_unwatch arginfo_class_RedisArray__continuum
 
+#define arginfo_class_RedisArray_zscan arginfo_class_RedisArray_hscan
+
 
 ZEND_METHOD(RedisArray, __call);
 ZEND_METHOD(RedisArray, __construct);
@@ -104,6 +115,7 @@ ZEND_METHOD(RedisArray, exec);
 ZEND_METHOD(RedisArray, flushall);
 ZEND_METHOD(RedisArray, flushdb);
 ZEND_METHOD(RedisArray, getOption);
+ZEND_METHOD(RedisArray, hscan);
 ZEND_METHOD(RedisArray, info);
 ZEND_METHOD(RedisArray, keys);
 ZEND_METHOD(RedisArray, mget);
@@ -113,8 +125,10 @@ ZEND_METHOD(RedisArray, ping);
 ZEND_METHOD(RedisArray, save);
 ZEND_METHOD(RedisArray, select);
 ZEND_METHOD(RedisArray, setOption);
+ZEND_METHOD(RedisArray, sscan);
 ZEND_METHOD(RedisArray, unlink);
 ZEND_METHOD(RedisArray, unwatch);
+ZEND_METHOD(RedisArray, zscan);
 
 
 static const zend_function_entry class_RedisArray_methods[] = {
@@ -134,6 +148,7 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, flushall, arginfo_class_RedisArray_flushall, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, flushdb, arginfo_class_RedisArray_flushdb, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, getOption, arginfo_class_RedisArray_getOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, hscan, arginfo_class_RedisArray_hscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, info, arginfo_class_RedisArray_info, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, keys, arginfo_class_RedisArray_keys, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, mget, arginfo_class_RedisArray_mget, ZEND_ACC_PUBLIC)
@@ -143,7 +158,9 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, save, arginfo_class_RedisArray_save, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, select, arginfo_class_RedisArray_select, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, setOption, arginfo_class_RedisArray_setOption, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, sscan, arginfo_class_RedisArray_sscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, unlink, arginfo_class_RedisArray_unlink, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, unwatch, arginfo_class_RedisArray_unwatch, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, zscan, arginfo_class_RedisArray_zscan, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };

From 56633e1b6b07e5824ba3f4b4c15a90ee3b7f0e1e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 21 Jan 2022 21:21:39 +0200
Subject: [PATCH 1547/1986] Fix tests

---
 .github/workflows/ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1330b98288..625744b6db 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,6 +29,7 @@ jobs:
           sudo add-apt-repository ppa:redislabs/redis
           sudo add-apt-repository ppa:ondrej/php
           sudo apt-get update
+          sudo apt --fix-broken install
           sudo apt-get install redis valgrind libzstd-dev liblz4-dev
       - name: Build phpredis
         run: |

From 0719c1eca0f3ce5650f51dfe878350147d424bbe Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Wed, 26 Jan 2022 09:52:16 -0800
Subject: [PATCH 1548/1986] Fix LZF decompression logic. (#2065)

* Fix LZF decompression logic.

Rework how we decompress LZF data.  Previously it was possible to
encounter a double-free, if the error was not E2BIG.

* .
---
 library.c           | 21 ++++++++-------------
 tests/RedisTest.php |  8 ++++++++
 2 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/library.c b/library.c
index 386763ec52..440306ea8d 100644
--- a/library.c
+++ b/library.c
@@ -3030,27 +3030,22 @@ redis_uncompress(RedisSock *redis_sock, char **dst, size_t *dstlen, const char *
         case REDIS_COMPRESSION_LZF:
 #ifdef HAVE_REDIS_LZF
             {
-                char *data;
-                int i;
+                char *data = NULL;
                 uint32_t res;
+                int i;
 
                 if (len == 0)
                     break;
 
-                /* start from two-times bigger buffer and
-                 * increase it exponentially  if needed */
+                /* Grow our buffer until we succeed or get a non E2BIG error */
                 errno = E2BIG;
                 for (i = 2; errno == E2BIG; i *= 2) {
-                    data = emalloc(i * len);
-                    if ((res = lzf_decompress(src, len, data, i * len)) == 0) {
-                        /* errno != E2BIG will brake for loop */
-                        efree(data);
-                        continue;
+                    data = erealloc(data, len * i);
+                    if ((res = lzf_decompress(src, len, data, len * i)) > 0) {
+                        *dst = data;
+                        *dstlen = res;
+                        return 1;
                     }
-
-                    *dst = data;
-                    *dstlen = res;
-                    return 1;
                 }
 
                 efree(data);
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 66b51f7fc5..6a5f4153ad 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4730,6 +4730,14 @@ 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_COMPRESSION, Redis::COMPRESSION_LZF);
+        $this->assertEquals($payload, $this->redis->get('badlzf'));
+        $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
+
         $this->checkCompression(Redis::COMPRESSION_LZF, 0);
     }
 

From 8689ab1c17a13034d9c8d0b7a011331cae9e5311 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 16 Jan 2022 15:07:30 +0200
Subject: [PATCH 1549/1986] Issue #1393

---
 redis_array.c                | 30 ++++++++++++++++++++++++++++++
 redis_array.stub.php         |  2 ++
 redis_array_arginfo.h        | 11 ++++++++++-
 redis_array_legacy_arginfo.h | 11 ++++++++++-
 4 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/redis_array.c b/redis_array.c
index 4d768b3f45..1c5f09707a 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -1162,6 +1162,36 @@ PHP_METHOD(RedisArray, zscan)
     ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZSCAN", sizeof("ZSCAN") - 1);
 }
 
+PHP_METHOD(RedisArray, scan)
+{
+    RedisArray *ra;
+    zend_string *host, *pattern = NULL;
+    zval *object, *redis_inst, *z_iter, z_fun, z_args[3];
+    zend_long count = 0;
+
+    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oz/S|S!l",
+            &object, redis_array_ce, &z_iter, &host, &pattern, &count) == FAILURE) {
+        RETURN_FALSE;
+    }
+
+    if ((ra = redis_array_get(object)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    if ((redis_inst = ra_find_node_by_name(ra, host)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    ZVAL_NEW_REF(&z_args[0], z_iter);
+    if (pattern) ZVAL_STR(&z_args[1], pattern);
+    ZVAL_LONG(&z_args[2], count);
+
+    ZVAL_STRING(&z_fun, "SCAN");
+    call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, ZEND_NUM_ARGS() - 1, z_args);
+    zval_dtor(&z_fun);
+
+    ZVAL_ZVAL(z_iter, &z_args[0], 0, 1);
+}
 
 PHP_METHOD(RedisArray, multi)
 {
diff --git a/redis_array.stub.php b/redis_array.stub.php
index 1e071a6cbd..206e7095da 100644
--- a/redis_array.stub.php
+++ b/redis_array.stub.php
@@ -55,6 +55,8 @@ public function ping(): bool|array;
 
     public function save(): bool|array;
 
+    public function scan(int &$iterator, string $node, ?string $pattern = null, int $count = 0): bool|array;
+
     public function select(int $index): bool|array;
 
     public function setOption(int $opt, string $value): bool|array;
diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h
index f262b1775d..378b082a55 100644
--- a/redis_array_arginfo.h
+++ b/redis_array_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 16a0857d62817f14eef16a00e80e587f318b9052 */
+ * Stub hash: 52eb7c6a57cea5f116106d24db1c98c7c4469e09 */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0)
@@ -84,6 +84,13 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisArray_save arginfo_class_RedisArray__continuum
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, node, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_select, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
 ZEND_END_ARG_INFO()
@@ -126,6 +133,7 @@ ZEND_METHOD(RedisArray, mset);
 ZEND_METHOD(RedisArray, multi);
 ZEND_METHOD(RedisArray, ping);
 ZEND_METHOD(RedisArray, save);
+ZEND_METHOD(RedisArray, scan);
 ZEND_METHOD(RedisArray, select);
 ZEND_METHOD(RedisArray, setOption);
 ZEND_METHOD(RedisArray, sscan);
@@ -159,6 +167,7 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, multi, arginfo_class_RedisArray_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, ping, arginfo_class_RedisArray_ping, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, save, arginfo_class_RedisArray_save, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, scan, arginfo_class_RedisArray_scan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, select, arginfo_class_RedisArray_select, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, setOption, arginfo_class_RedisArray_setOption, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, sscan, arginfo_class_RedisArray_sscan, ZEND_ACC_PUBLIC)
diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h
index f549bd2a95..7977199960 100644
--- a/redis_array_legacy_arginfo.h
+++ b/redis_array_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 16a0857d62817f14eef16a00e80e587f318b9052 */
+ * Stub hash: 52eb7c6a57cea5f116106d24db1c98c7c4469e09 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2)
 	ZEND_ARG_INFO(0, function_name)
@@ -81,6 +81,13 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisArray_save arginfo_class_RedisArray__continuum
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_scan, 0, 0, 2)
+	ZEND_ARG_INFO(1, iterator)
+	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, pattern)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray_select, 0, 0, 1)
 	ZEND_ARG_INFO(0, index)
 ZEND_END_ARG_INFO()
@@ -123,6 +130,7 @@ ZEND_METHOD(RedisArray, mset);
 ZEND_METHOD(RedisArray, multi);
 ZEND_METHOD(RedisArray, ping);
 ZEND_METHOD(RedisArray, save);
+ZEND_METHOD(RedisArray, scan);
 ZEND_METHOD(RedisArray, select);
 ZEND_METHOD(RedisArray, setOption);
 ZEND_METHOD(RedisArray, sscan);
@@ -156,6 +164,7 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, multi, arginfo_class_RedisArray_multi, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, ping, arginfo_class_RedisArray_ping, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, save, arginfo_class_RedisArray_save, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisArray, scan, arginfo_class_RedisArray_scan, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, select, arginfo_class_RedisArray_select, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, setOption, arginfo_class_RedisArray_setOption, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisArray, sscan, arginfo_class_RedisArray_sscan, ZEND_ACC_PUBLIC)

From 0264de1824b03fb2d0ad515b4d4ec019cd2dae70 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 20 Jan 2022 11:46:08 -0800
Subject: [PATCH 1550/1986] A small test for key scanning in RedisArray

See: #2055
---
 tests/RedisArrayTest.php | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php
index a852688625..5a20d271c7 100644
--- a/tests/RedisArrayTest.php
+++ b/tests/RedisArrayTest.php
@@ -166,6 +166,43 @@ public function testKeyDistributor()
         }
     }
 
+    /* Scan a whole key and return the overall result */
+    protected function execKeyScan($cmd, $key) {
+        $res = [];
+
+        $it = NULL;
+        do {
+            $chunk = $this->ra->$cmd($key, $it);
+            foreach ($chunk as $field => $value) {
+                $res[$field] = $value;
+            }
+        } while ($it !== 0);
+
+        return $res;
+    }
+
+    public function testKeyScanning() {
+        $h_vals = ['foo' => 'bar', 'baz' => 'bop'];
+        $z_vals = ['one' => 1, 'two' => 2, 'three' => 3];
+        $s_vals = ['mem1', 'mem2', 'mem3'];
+
+        $this->ra->del(['scan-hash', 'scan-set', 'scan-zset']);
+
+        $this->ra->hMSet('scan-hash', $h_vals);
+        foreach ($z_vals as $k => $v)
+            $this->ra->zAdd('scan-zset', $v, $k);
+        $this->ra->sAdd('scan-set', ...$s_vals);
+
+        $s_scan = $this->execKeyScan('sScan', 'scan-set');
+        $this->assertTrue(count(array_diff_key(array_flip($s_vals), array_flip($s_scan))) == 0);
+
+        $this->assertEquals($h_vals, $this->execKeyScan('hScan', 'scan-hash'));
+
+        $z_scan = $this->execKeyScan('zScan', 'scan-zset');
+        $this->assertTrue(count($z_scan) == count($z_vals) &&
+                          count(array_diff_key($z_vals, $z_scan)) == 0 &&
+                          array_sum($z_scan) == array_sum($z_vals));
+    }
 }
 
 class Redis_Rehashing_Test extends TestSuite

From 14d121bb69097cd78d60d4712455b0f5b77a93e2 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 1 Feb 2022 21:02:59 +0200
Subject: [PATCH 1551/1986] Allow to pass null as iterator

---
 redis.stub.php                 |  8 ++++----
 redis_arginfo.h                |  8 ++++----
 redis_array.stub.php           |  8 ++++----
 redis_array_arginfo.h          |  6 +++---
 redis_array_legacy_arginfo.h   |  2 +-
 redis_cluster.stub.php         |  8 ++++----
 redis_cluster_arginfo.h        | 10 +++++-----
 redis_cluster_legacy_arginfo.h |  2 +-
 redis_legacy_arginfo.h         |  2 +-
 9 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 1ab3b31832..4fdbeb015a 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -201,7 +201,7 @@ public function hStrLen(string $key, string $member): int;
 
     public function hVals(string $key): array;
 
-    public function hscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
 	/** @return int|Redis */
     public function incr(string $key);
@@ -370,7 +370,7 @@ public function sUnionStore(string $dst, string $key, string ...$other_keys): in
 
     public function save(): bool;
 
-    public function scan(int &$iterator, ?string $pattern = null, int $count = 0): array;
+    public function scan(?int &$iterator, ?string $pattern = null, int $count = 0): array;
 
     public function scard(string $key): int;
 
@@ -426,7 +426,7 @@ public function sortDescAlpha(string $key, ?string $pattern = null, mixed $get =
 
     public function srem(string $key, string $value, string ...$other_values): int;
 
-    public function sscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): array;
+    public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array;
 
 	/** @return int|Redis */
     public function strlen(string $key);
@@ -537,7 +537,7 @@ public function zinter(array $keys, array $weights = null, array $options = null
 
     public function zinterstore(string $dst, array $keys, array $weights = null, string $aggregate = null): int;
 
-    public function zscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
     public function zunion(array $keys, array $weights = null, array $options = null): array;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 09b2fda4e5..097be1e686 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c2cbe49e22cba6f23e98c1676b7769c55a6fd043 */
+ * Stub hash: de74da38c8a832457554c3a0e1e042d47464e36c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -351,7 +351,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
@@ -617,7 +617,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_Redis_save arginfo_class_Redis_bgSave
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_scan, 0, 1, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
@@ -700,7 +700,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sscan, 0, 2, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
diff --git a/redis_array.stub.php b/redis_array.stub.php
index 206e7095da..cab4ffc973 100644
--- a/redis_array.stub.php
+++ b/redis_array.stub.php
@@ -39,7 +39,7 @@ public function flushdb(): bool|array;
 
     public function getOption(int $opt): bool|array;
 
-    public function hscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
     public function info(): bool|array;
 
@@ -55,17 +55,17 @@ public function ping(): bool|array;
 
     public function save(): bool|array;
 
-    public function scan(int &$iterator, string $node, ?string $pattern = null, int $count = 0): bool|array;
+    public function scan(?int &$iterator, string $node, ?string $pattern = null, int $count = 0): bool|array;
 
     public function select(int $index): bool|array;
 
     public function setOption(int $opt, string $value): bool|array;
 
-    public function sscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
     public function unlink(string|array $key, string ...$otherkeys): bool|int;
 
     public function unwatch(): bool|null;
 
-    public function zscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 }
diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h
index 378b082a55..3b426b36f5 100644
--- a/redis_array_arginfo.h
+++ b/redis_array_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 52eb7c6a57cea5f116106d24db1c98c7c4469e09 */
+ * Stub hash: db47879ea03ea74832fe777fcc5d834ea554bb4a */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0)
@@ -56,7 +56,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
@@ -85,7 +85,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisArray_save arginfo_class_RedisArray__continuum
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO(0, node, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h
index 7977199960..9c62aac57e 100644
--- a/redis_array_legacy_arginfo.h
+++ b/redis_array_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 52eb7c6a57cea5f116106d24db1c98c7c4469e09 */
+ * Stub hash: db47879ea03ea74832fe777fcc5d834ea554bb4a */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2)
 	ZEND_ARG_INFO(0, function_name)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 2b1b2a5d5e..c48c419207 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -145,7 +145,7 @@ public function hmget(string $key, array $members): array;
 
     public function hmset(string $key, array $key_values): bool;
 
-    public function hscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): array|bool;
+    public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|bool;
 
     public function hset(string $key, string $member, mixed $value): int;
 
@@ -251,7 +251,7 @@ public function saddarray(string $key, array $values): bool|int;
 
     public function save(string|array $key_or_address): bool;
 
-    public function scan(int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
+    public function scan(?int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
 
     public function scard(string $key): int;
 
@@ -293,7 +293,7 @@ public function srandmember(string $key, int $count = 0): string|array;
 
     public function srem(string $key, string $value, string ...$other_values): int;
 
-    public function sscan(string $key, int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
+    public function sscan(string $key, ?int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
 
     public function strlen(string $key): int;
 
@@ -383,7 +383,7 @@ public function zrevrangebyscore(string $key, string $min, string $max, array $o
 
     public function zrevrank(string $key, mixed $member): int;
 
-    public function zscan(string $key, int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
     public function zscore(string $key): float;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 74857d63bf..eda09a4501 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: aecb51252f52073c36fbe147be0aacddd649db07 */
+ * Stub hash: 7f045c90abd99a53f8fb6557942cd17e00ee8a01 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -306,7 +306,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
@@ -530,7 +530,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_save arginfo_class_RedisCluster_bgrewriteaof
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO(0, node, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
@@ -623,7 +623,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sscan, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO(0, node, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
@@ -826,7 +826,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index f27e3f1b76..38a808967a 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: aecb51252f52073c36fbe147be0aacddd649db07 */
+ * Stub hash: 7f045c90abd99a53f8fb6557942cd17e00ee8a01 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 9021f63ca4..86937f5f00 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c2cbe49e22cba6f23e98c1676b7769c55a6fd043 */
+ * Stub hash: de74da38c8a832457554c3a0e1e042d47464e36c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From f9436e255b3f69c1aa776e7e8e28736efdfb4099 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 21 Mar 2022 19:40:38 +0200
Subject: [PATCH 1552/1986] [WIP] Issue #1894

Add NOMKSTREAM option to XADD command.
---
 redis.stub.php         |  2 +-
 redis_arginfo.h        |  3 ++-
 redis_commands.c       | 13 +++++++++----
 redis_legacy_arginfo.h |  3 ++-
 4 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 4fdbeb015a..f03b42a61c 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -461,7 +461,7 @@ public function wait(int $count, int $timeout): int;
 
     public function xack(string $key, string $group, array $ids): int;
 
-    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false): string;
+    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): string;
 
     public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): string|array;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 097be1e686..84158d9b60 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: de74da38c8a832457554c3a0e1e042d47464e36c */
+ * Stub hash: 49b5dded8c4ca8f50f0427d203e35c89278ad364 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -748,6 +748,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xadd, 0, 3, IS_STRIN
 	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, maxlen, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nomkstream, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, MAY_BE_STRING|MAY_BE_ARRAY)
diff --git a/redis_commands.c b/redis_commands.c
index 1e9a7ecfb8..e60228631a 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3810,15 +3810,16 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     zend_string *arrkey;
     zval *z_fields, *value;
     zend_long maxlen = 0;
-    zend_bool approx = 0;
+    zend_bool approx = 0, nomkstream = 0;
     zend_ulong idx;
     HashTable *ht_fields;
     int fcount, argc;
     char *key, *id;
     size_t keylen, idlen;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|lb", &key, &keylen,
-                              &id, &idlen, &z_fields, &maxlen, &approx) == FAILURE)
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|lbb", &key, &keylen,
+                              &id, &idlen, &z_fields, &maxlen, &approx,
+                              &nomkstream) == FAILURE)
     {
         return FAILURE;
     }
@@ -3838,12 +3839,16 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     /* Calculate argc for XADD.  It's a bit complex because we've got
      * an optional MAXLEN argument which can either take the form MAXLEN N
      * or MAXLEN ~ N */
-    argc = 2 + (fcount*2) + (maxlen > 0 ? (approx ? 3 : 2) : 0);
+    argc = 2 + nomkstream + (fcount * 2) + (maxlen > 0 ? (approx ? 3 : 2) : 0);
 
     /* XADD key ID field string [field string ...] */
     REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XADD");
     redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot);
 
+    if (nomkstream) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NOMKSTREAM");
+    }
+
     /* Now append our MAXLEN bits if we've got them */
     if (maxlen > 0) {
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN");
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 86937f5f00..fabd4ccca7 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: de74da38c8a832457554c3a0e1e042d47464e36c */
+ * Stub hash: 49b5dded8c4ca8f50f0427d203e35c89278ad364 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -664,6 +664,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xadd, 0, 0, 3)
 	ZEND_ARG_INFO(0, values)
 	ZEND_ARG_INFO(0, maxlen)
 	ZEND_ARG_INFO(0, approx)
+	ZEND_ARG_INFO(0, nomkstream)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xclaim, 0, 0, 6)

From 82e08723d046a6cdcac8f29e10aa3425e54783b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Till=20Kr=C3=BCss?= 
Date: Tue, 29 Mar 2022 21:14:34 -0700
Subject: [PATCH 1553/1986] fix restoring keys when using compression

---
 redis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index 5a5d787b96..adbd07d163 100644
--- a/redis.c
+++ b/redis.c
@@ -2949,7 +2949,7 @@ PHP_METHOD(Redis, dump) {
 
 /* {{{ proto Redis::restore(ttl, key, value) */
 PHP_METHOD(Redis, restore) {
-    REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_val_cmd,
+    REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_str_cmd,
         redis_boolean_response);
 }
 /* }}} */

From 5db855617da027d233e700ca588cd3a20e78ca22 Mon Sep 17 00:00:00 2001
From: sy-records <52o@qq52o.cn>
Date: Wed, 30 Mar 2022 12:38:35 +0800
Subject: [PATCH 1554/1986] Fix missing auth in RedisSentinel stub

---
 redis_sentinel.stub.php         | 2 +-
 redis_sentinel_arginfo.h        | 3 ++-
 redis_sentinel_legacy_arginfo.h | 3 ++-
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php
index 20f8075502..58df483892 100644
--- a/redis_sentinel.stub.php
+++ b/redis_sentinel.stub.php
@@ -7,7 +7,7 @@
 
 class RedisSentinel {
 
-    public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = NULL, int $retry_interval = 0, float $read_timeout = 0);
+    public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = NULL, int $retry_interval = 0, float $read_timeout = 0, mixed $auth = NULL);
 
 	/** @return bool|RedisSentinel */
     public function ckquorum(string $master);
diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h
index 980719dc42..2afdd4e13b 100644
--- a/redis_sentinel_arginfo.h
+++ b/redis_sentinel_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 9a7a43f9bee2da879c1419d203ddfd12e6052e25 */
+ * Stub hash: deae7b4a55435fb2ab39f314544064a34c56d218 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
@@ -8,6 +8,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, IS_MIXED, 0, "NULL")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1)
diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h
index 7dd2d8f473..8f35e6c1c5 100644
--- a/redis_sentinel_legacy_arginfo.h
+++ b/redis_sentinel_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 9a7a43f9bee2da879c1419d203ddfd12e6052e25 */
+ * Stub hash: deae7b4a55435fb2ab39f314544064a34c56d218 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, host)
@@ -8,6 +8,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, persistent)
 	ZEND_ARG_INFO(0, retry_interval)
 	ZEND_ARG_INFO(0, read_timeout)
+	ZEND_ARG_INFO(0, auth)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1)

From 01f3342c72c53e76514a652eece17bbc41626b70 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 24 Mar 2022 21:02:53 +0200
Subject: [PATCH 1555/1986] Issue #1894

Add XAUTOCLAIM command
---
 redis.c                |  4 ++++
 redis.stub.php         |  4 +++-
 redis_arginfo.h        | 18 +++++++++++++++---
 redis_commands.c       | 43 ++++++++++++++++++++++++++++++++++++++++++
 redis_commands.h       |  3 +++
 redis_legacy_arginfo.h | 16 ++++++++++++++--
 6 files changed, 82 insertions(+), 6 deletions(-)

diff --git a/redis.c b/redis.c
index 5a5d787b96..22eef5db1b 100644
--- a/redis.c
+++ b/redis.c
@@ -3618,6 +3618,10 @@ PHP_METHOD(Redis, xadd) {
     REDIS_PROCESS_CMD(xadd, redis_read_variant_reply);
 }
 
+PHP_METHOD(Redis, xautoclaim) {
+    REDIS_PROCESS_CMD(xautoclaim, redis_xclaim_reply);
+}
+
 PHP_METHOD(Redis, xclaim) {
     REDIS_PROCESS_CMD(xclaim, redis_xclaim_reply);
 }
diff --git a/redis.stub.php b/redis.stub.php
index f03b42a61c..912eab5785 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -463,7 +463,9 @@ public function xack(string $key, string $group, array $ids): int;
 
     public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): string;
 
-    public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): string|array;
+    public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): bool|array;
+
+    public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): bool|array;
 
     public function xdel(string $key, array $ids): int;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 84158d9b60..515dacd4f4 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 49b5dded8c4ca8f50f0427d203e35c89278ad364 */
+ * Stub hash: 2f9fb51ff39db0f8e399b937728904e3283448ff */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -751,11 +751,21 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xadd, 0, 3, IS_STRIN
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nomkstream, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, MAY_BE_STRING|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xautoclaim, 0, 5, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, min_iddle, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, min_idle, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, justid, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min_idle, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -1120,6 +1130,7 @@ ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, wait);
 ZEND_METHOD(Redis, xack);
 ZEND_METHOD(Redis, xadd);
+ZEND_METHOD(Redis, xautoclaim);
 ZEND_METHOD(Redis, xclaim);
 ZEND_METHOD(Redis, xdel);
 ZEND_METHOD(Redis, xgroup);
@@ -1347,6 +1358,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, wait, arginfo_class_Redis_wait, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xack, arginfo_class_Redis_xack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xadd, arginfo_class_Redis_xadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xautoclaim, arginfo_class_Redis_xautoclaim, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xclaim, arginfo_class_Redis_xclaim, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xdel, arginfo_class_Redis_xdel, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xgroup, arginfo_class_Redis_xgroup, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index e60228631a..f4e3ccdfe9 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4305,6 +4305,49 @@ static void append_xclaim_options(smart_string *cmd, xclaimOptions *opt) {
         REDIS_CMD_APPEND_SSTR_STATIC(cmd, "JUSTID");
 }
 
+
+int
+redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                     char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    smart_string cmdstr = {0};
+    char *key, *group, *consumer, *start;
+    size_t keylen, grouplen, consumerlen, startlen;
+    zend_long min_idle, count = -1;
+    zend_bool justid = 0;
+    int argc;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssls|lb", &key, &keylen,
+                              &group, &grouplen, &consumer, &consumerlen,
+                              &min_idle, &start, &startlen, &count, &justid
+                              ) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    argc = 5 + (count > 0 ? 1 + count : 0) + justid;
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XAUTOCLAIM");
+    redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot);
+    redis_cmd_append_sstr(&cmdstr, group, grouplen);
+    redis_cmd_append_sstr(&cmdstr, consumer, consumerlen);
+    redis_cmd_append_sstr_long(&cmdstr, min_idle);
+    redis_cmd_append_sstr(&cmdstr, start, startlen);
+
+    if (count > 0) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
+        redis_cmd_append_sstr_long(&cmdstr, count);
+    }
+
+    if (justid) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "JUSTID");
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+	return SUCCESS;
+}
+
 /* XCLAIM      
           [IDLE ] [TIME ] [RETRYCOUNT ]
           [FORCE] [JUSTID] */
diff --git a/redis_commands.h b/redis_commands.h
index 851e28b224..eed3d7dccf 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -292,6 +292,9 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index fabd4ccca7..21150b1022 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 49b5dded8c4ca8f50f0427d203e35c89278ad364 */
+ * Stub hash: 2f9fb51ff39db0f8e399b937728904e3283448ff */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -667,11 +667,21 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xadd, 0, 0, 3)
 	ZEND_ARG_INFO(0, nomkstream)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xautoclaim, 0, 0, 5)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, consumer)
+	ZEND_ARG_INFO(0, min_idle)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, justid)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xclaim, 0, 0, 6)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, group)
 	ZEND_ARG_INFO(0, consumer)
-	ZEND_ARG_INFO(0, min_iddle)
+	ZEND_ARG_INFO(0, min_idle)
 	ZEND_ARG_INFO(0, ids)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
@@ -1022,6 +1032,7 @@ ZEND_METHOD(Redis, watch);
 ZEND_METHOD(Redis, wait);
 ZEND_METHOD(Redis, xack);
 ZEND_METHOD(Redis, xadd);
+ZEND_METHOD(Redis, xautoclaim);
 ZEND_METHOD(Redis, xclaim);
 ZEND_METHOD(Redis, xdel);
 ZEND_METHOD(Redis, xgroup);
@@ -1249,6 +1260,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, wait, arginfo_class_Redis_wait, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xack, arginfo_class_Redis_xack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xadd, arginfo_class_Redis_xadd, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, xautoclaim, arginfo_class_Redis_xautoclaim, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xclaim, arginfo_class_Redis_xclaim, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xdel, arginfo_class_Redis_xdel, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, xgroup, arginfo_class_Redis_xgroup, ZEND_ACC_PUBLIC)

From 42cbd88a185ed572cf4d93f1576468244839cc4e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 2 Apr 2022 17:58:52 +0300
Subject: [PATCH 1556/1986] Issue #1974

---
 common.h         | 1 +
 library.c        | 5 ++++-
 redis_sentinel.c | 1 +
 3 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/common.h b/common.h
index f27748969d..5a6f02bf61 100644
--- a/common.h
+++ b/common.h
@@ -332,6 +332,7 @@ typedef struct {
     int                 reply_literal;
     int                 null_mbulk_as_null;
     int                 tcp_keepalive;
+    int                 sentinel;
 } RedisSock;
 /* }}} */
 
diff --git a/library.c b/library.c
index 440306ea8d..0590dce0a6 100644
--- a/library.c
+++ b/library.c
@@ -2340,7 +2340,10 @@ redis_sock_check_liveness(RedisSock *redis_sock)
     }
 
     /* check echo response */
-    if (*inbuf != TYPE_BULK || atoi(inbuf + 1) != idlen ||
+    if ((redis_sock->sentinel && (
+        strncmp(inbuf, "-ERR unknown command", 20) != 0 ||
+        strstr(inbuf, id) == NULL
+    )) || *inbuf != TYPE_BULK || atoi(inbuf + 1) != idlen ||
         redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
         strncmp(inbuf, id, idlen) != 0
     ) {
diff --git a/redis_sentinel.c b/redis_sentinel.c
index ca72640e17..632975cd9d 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -87,6 +87,7 @@ PHP_METHOD(RedisSentinel, __construct)
     if (auth) {
         redis_sock_set_auth_zval(obj->sock, auth);
     }
+    obj->sock->sentinel = 1;
 }
 
 PHP_METHOD(RedisSentinel, ckquorum)

From 5a269ab6d0e21ddc3953792f1776ed70c7d45252 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 3 Apr 2022 19:52:18 +0300
Subject: [PATCH 1557/1986] Don't allow reconnect on read response

---
 cluster_library.c |  2 +-
 cluster_library.h |  4 ++--
 library.c         | 12 ++++++------
 library.h         |  2 +-
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 0dee495da5..8ebc87604d 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -1161,7 +1161,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type)
     CLUSTER_CLEAR_ERROR(c);
     CLUSTER_CLEAR_REPLY(c);
 
-    if (-1 == redis_check_eof(c->cmd_sock, 1) ||
+    if (-1 == redis_check_eof(c->cmd_sock, 1, 1) ||
        EOF == (*reply_type = php_stream_getc(c->cmd_sock->stream)))
     {
         return -1;
diff --git a/cluster_library.h b/cluster_library.h
index 6e450705a8..811c435dcc 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -62,12 +62,12 @@
 
 /* Protected sending of data down the wire to a RedisSock->stream */
 #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \
-    (sock && !redis_sock_server_open(sock) && sock->stream && !redis_check_eof(sock, 1 ) && \
+    (sock && !redis_sock_server_open(sock) && sock->stream && !redis_check_eof(sock, 0, 1) && \
      php_stream_write(sock->stream, buf, len)==len)
 
 /* Macro to read our reply type character */
 #define CLUSTER_VALIDATE_REPLY_TYPE(sock, type) \
-    (redis_check_eof(sock, 1) == 0 && \
+    (redis_check_eof(sock, 1, 1) == 0 && \
      (php_stream_getc(sock->stream) == type))
 
 /* Reset our last single line reply buffer and length */
diff --git a/library.c b/library.c
index 440306ea8d..e7031f06f5 100644
--- a/library.c
+++ b/library.c
@@ -287,7 +287,7 @@ redis_error_throw(RedisSock *redis_sock)
 }
 
 PHP_REDIS_API int
-redis_check_eof(RedisSock *redis_sock, int no_throw)
+redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw)
 {
     unsigned int retry_index;
     char *errmsg;
@@ -322,7 +322,7 @@ redis_check_eof(RedisSock *redis_sock, int no_throw)
     } else {
         errmsg = "Connection lost";
         redis_backoff_reset(&redis_sock->backoff);
-        for (retry_index = 0; retry_index < redis_sock->max_retries; ++retry_index) {
+        for (retry_index = 0; !no_retry && retry_index < redis_sock->max_retries; ++retry_index) {
             /* close existing stream before reconnecting */
             if (redis_sock->stream) {
                 redis_sock_disconnect(redis_sock, 1);
@@ -592,7 +592,7 @@ redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes)
     char *reply;
     size_t got;
 
-    if (-1 == bytes || -1 == redis_check_eof(redis_sock, 0)) {
+    if (-1 == bytes || -1 == redis_check_eof(redis_sock, 1, 0)) {
         return NULL;
     }
 
@@ -2865,7 +2865,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc
 PHP_REDIS_API int
 redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz)
 {
-    if (redis_check_eof(redis_sock, 0) == 0 &&
+    if (redis_check_eof(redis_sock, 0, 0) == 0 &&
         php_stream_write(redis_sock->stream, cmd, sz) == sz
     ) {
         return sz;
@@ -3343,7 +3343,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size,
                 size_t *line_size)
 {
     // Handle EOF
-    if(-1 == redis_check_eof(redis_sock, 0)) {
+    if(-1 == redis_check_eof(redis_sock, 1, 0)) {
         return -1;
     }
 
@@ -3376,7 +3376,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type,
                       long *reply_info)
 {
     // Make sure we haven't lost the connection, even trying to reconnect
-    if(-1 == redis_check_eof(redis_sock, 0)) {
+    if(-1 == redis_check_eof(redis_sock, 1, 0)) {
         // Failure
         *reply_type = EOF;
         return -1;
diff --git a/library.h b/library.h
index d852ba1ff0..e993f8f010 100644
--- a/library.h
+++ b/library.h
@@ -111,7 +111,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz);
-PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, int no_throw);
+PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw);
 PHP_REDIS_API RedisSock *redis_sock_get(zval *id, int nothrow);
 PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock);
 PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len);

From 831d6118c45517d0047a7ca6ca5d805762539f28 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 4 Apr 2022 21:04:00 +0300
Subject: [PATCH 1558/1986] Remove weird macroses

---
 common.h         | 21 ---------------------
 redis_commands.c |  6 +++---
 2 files changed, 3 insertions(+), 24 deletions(-)

diff --git a/common.h b/common.h
index f27748969d..0440d6316a 100644
--- a/common.h
+++ b/common.h
@@ -210,12 +210,6 @@ typedef enum {
     REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) \
 }
 
-/* Clear redirection info */
-#define REDIS_MOVED_CLEAR(redis_sock) \
-    redis_sock->redir_slot = 0; \
-    redis_sock->redir_port = 0; \
-    redis_sock->redir_type = MOVED_NONE; \
-
 /* Process a command assuming our command where our command building
  * function is redis__cmd */
 #define REDIS_PROCESS_CMD(cmdname, resp_func) \
@@ -264,21 +258,6 @@ typedef enum {
 #define ZSTR_STRICMP_STATIC(zs, sstr) \
     REDIS_STRICMP_STATIC(ZSTR_VAL(zs), ZSTR_LEN(zs), sstr)
 
-/* Extended SET argument detection */
-#define ZSTR_IS_EX_ARG(zs) ZSTR_STRICMP_STATIC(zs, "EX")
-#define ZSTR_IS_PX_ARG(zs) ZSTR_STRICMP_STATIC(zs, "PX")
-#define ZSTR_IS_NX_ARG(zs) ZSTR_STRICMP_STATIC(zs, "NX")
-#define ZSTR_IS_XX_ARG(zs) ZSTR_STRICMP_STATIC(zs, "XX")
-
-#define ZVAL_IS_NX_XX_ARG(zv) (ZVAL_STRICMP_STATIC(zv, "NX") || ZVAL_STRICMP_STATIC(zv, "XX"))
-#define ZSTR_IS_EX_PX_ARG(a) (ZSTR_IS_EX_ARG(a) || ZSTR_IS_PX_ARG(a))
-
-/* Given a string and length, validate a zRangeByLex argument.  The semantics
- * here are that the argument must start with '(' or '[' or be just the char
- * '+' or '-' */
-#define IS_LEX_ARG(s,l) \
-    (l>0 && (*s=='(' || *s=='[' || (l==1 && (*s=='+' || *s=='-'))))
-
 #define REDIS_ENABLE_MODE(redis_sock, m) (redis_sock->mode |= m)
 #define REDIS_DISABLE_MODE(redis_sock, m) (redis_sock->mode &= ~m)
 
diff --git a/redis_commands.c b/redis_commands.c
index f4e3ccdfe9..356483d019 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1575,7 +1575,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         ZEND_HASH_FOREACH_STR_KEY_VAL(kt, zkey, v) {
             ZVAL_DEREF(v);
             /* Detect PX or EX argument and validate timeout */
-            if (zkey && ZSTR_IS_EX_PX_ARG(zkey)) {
+            if (zkey && (ZSTR_STRICMP_STATIC(zkey, "EX") || ZSTR_STRICMP_STATIC(zkey, "PX"))) {
                 exp_set = 1;
 
                 /* Set expire type */
@@ -1592,7 +1592,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     keep_ttl  = 1;
                 } else if (ZVAL_STRICMP_STATIC((v), "GET")) {
                     get = 1;
-                } else if (ZVAL_IS_NX_XX_ARG(v)) {
+                } else if (ZVAL_STRICMP_STATIC(v, "NX") || ZVAL_STRICMP_STATIC(v, "XX")) {
                     set_type = Z_STRVAL_P(v);
                 }
             }
@@ -2886,7 +2886,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         zval *z_opt;
         ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[1]), z_opt) {
             if (Z_TYPE_P(z_opt) == IS_STRING) {
-                if (ZVAL_IS_NX_XX_ARG(z_opt)) {
+                if (ZVAL_STRICMP_STATIC(z_opt, "NX") || ZVAL_STRICMP_STATIC(z_opt, "XX")) {
                     exp_type = Z_STRVAL_P(z_opt);
                 } else if (ZVAL_STRICMP_STATIC(z_opt, "LT") || ZVAL_STRICMP_STATIC(z_opt, "GT")) {
                     range_type = Z_STRVAL_P(z_opt);

From 0879770a82c316b00296b6775b23f3a1e145d2e1 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 5 Apr 2022 22:56:25 +0300
Subject: [PATCH 1559/1986] Issue #1746

---
 library.c | 17 ++++++++++++++++-
 library.h |  1 +
 redis.c   | 16 ----------------
 3 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/library.c b/library.c
index 55813d54fa..57efe28c54 100644
--- a/library.c
+++ b/library.c
@@ -2527,8 +2527,9 @@ redis_sock_disconnect(RedisSock *redis_sock, int force)
             if (INI_INT("redis.pconnect.pooling_enabled")) {
                 p = redis_sock_get_connection_pool(redis_sock);
             }
-            if (force) {
+            if (force || !IS_ATOMIC(redis_sock)) {
                 php_stream_pclose(redis_sock->stream);
+                free_reply_callbacks(redis_sock);
                 if (p) p->nb_active--;
             } else if (p) {
                 zend_llist_prepend_element(&p->list, &redis_sock->stream);
@@ -2876,6 +2877,19 @@ redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz)
     return -1;
 }
 
+void
+free_reply_callbacks(RedisSock *redis_sock)
+{
+    fold_item *fi;
+
+    while (redis_sock->head != NULL) {
+        fi = redis_sock->head->next;
+        free(redis_sock->head);
+        redis_sock->head = fi;
+    }
+    redis_sock->current = NULL;
+}
+
 /**
  * redis_free_socket
  */
@@ -2897,6 +2911,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
         zend_string_release(redis_sock->host);
     }
     redis_sock_free_auth(redis_sock);
+    free_reply_callbacks(redis_sock);
     efree(redis_sock);
 }
 
diff --git a/library.h b/library.h
index e993f8f010..ac1582fd36 100644
--- a/library.h
+++ b/library.h
@@ -37,6 +37,7 @@
 
 
 void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id);
+void free_reply_callbacks(RedisSock *redis_sock);
 
 PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass);
 
diff --git a/redis.c b/redis.c
index 5d563407fb..a1233ac1ff 100644
--- a/redis.c
+++ b/redis.c
@@ -188,20 +188,6 @@ redis_send_discard(RedisSock *redis_sock)
     return result;
 }
 
-static void
-free_reply_callbacks(RedisSock *redis_sock)
-{
-    fold_item *fi;
-
-    for (fi = redis_sock->head; fi; ) {
-        fold_item *fi_next = fi->next;
-        free(fi);
-        fi = fi_next;
-    }
-    redis_sock->head = NULL;
-    redis_sock->current = NULL;
-}
-
 /* Passthru for destroying cluster cache */
 static void cluster_cache_dtor(zend_resource *rsrc) {
     if (rsrc->ptr) {
@@ -2228,7 +2214,6 @@ PHP_METHOD(Redis, multi)
 
         /* Enable PIPELINE if we're not already in one */
         if (IS_ATOMIC(redis_sock)) {
-            free_reply_callbacks(redis_sock);
             REDIS_ENABLE_MODE(redis_sock, PIPELINE);
         }
     } else if (multi_value == MULTI) {
@@ -2467,7 +2452,6 @@ PHP_METHOD(Redis, pipeline)
         /* NB : we keep the function fold, to detect the last function.
          * We need the response format of the n - 1 command. So, we can delete
          * when n > 2, the { 1 .. n - 2} commands */
-        free_reply_callbacks(redis_sock);
         REDIS_ENABLE_MODE(redis_sock, PIPELINE);
     }
 

From 750b6cf31abee1623ec494f4dbd159c4a08e4e5c Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 7 Apr 2022 21:45:31 +0300
Subject: [PATCH 1560/1986] Issue #1894

Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH
---
 redis.stub.php         |  4 ++--
 redis_arginfo.h        |  4 ++--
 redis_commands.c       | 32 ++++++++++++++++++++++++--------
 redis_legacy_arginfo.h |  4 ++--
 4 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 912eab5785..52ad2e1f87 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -117,9 +117,9 @@ public function expire(string $key, int $timeout): bool;
 
     public function expireAt(string $key, int $timestamp): bool;
 
-    public function flushAll(bool $async = false): bool;
+    public function flushAll(?bool $sync = null): bool;
 
-    public function flushDB(bool $async = false): bool;
+    public function flushDB(?bool $sync = null): bool;
 
     public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): int;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 515dacd4f4..be0802312f 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2f9fb51ff39db0f8e399b937728904e3283448ff */
+ * Stub hash: 0475243df03f4f3d6e568fa9ae164073dadc930d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -180,7 +180,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_expireAt, 0, 2, _IS_
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, async, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sync, _IS_BOOL, 1, "null")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
diff --git a/redis_commands.c b/redis_commands.c
index 356483d019..7d9fd88a27 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -131,10 +131,24 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args)
         return NULL;
     }
     // Branch based on the directive
-    if (!strcasecmp(Z_STRVAL(z_args[0]), "flush") || !strcasecmp(Z_STRVAL(z_args[0]), "kill")) {
-        // Simple SCRIPT FLUSH, or SCRIPT_KILL command
+    if (!strcasecmp(Z_STRVAL(z_args[0]), "kill")) {
+        // Simple SCRIPT_KILL command
         REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT");
-        redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0]));
+        redis_cmd_append_sstr(cmd, "KILL", sizeof("KILL") - 1);
+    } else if (!strcasecmp(Z_STRVAL(z_args[0]), "flush")) {
+        // Simple SCRIPT FLUSH [ASYNC | SYNC]
+        if (argc > 1 && (
+            Z_TYPE(z_args[1]) != IS_STRING ||
+            strcasecmp(Z_STRVAL(z_args[1]), "sync") ||
+            strcasecmp(Z_STRVAL(z_args[1]), "async")
+        )) {
+            return NULL;
+        }
+        REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT");
+        redis_cmd_append_sstr(cmd, "FLUSH", sizeof("FLUSH") - 1);
+        if (argc > 1) {
+            redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
+        }
     } else if (!strcasecmp(Z_STRVAL(z_args[0]), "load")) {
         // Make sure we have a second argument, and it's not empty.  If it is
         // empty, we can just return an empty array (which is what Redis does)
@@ -436,16 +450,18 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    zend_bool async = 0;
+    zend_bool sync = -1;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &async) == FAILURE) {
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &sync) == FAILURE) {
         return FAILURE;
     }
 
-    if (async) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1);
-    } else {
+    if (sync < 0) {
         *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "");
+    } else if (sync > 0) {
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", "SYNC", sizeof("SYNC") - 1);
+    } else {
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1);
     }
 
     return SUCCESS;
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 21150b1022..77159f50c6 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2f9fb51ff39db0f8e399b937728904e3283448ff */
+ * Stub hash: 0475243df03f4f3d6e568fa9ae164073dadc930d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -168,7 +168,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expireAt, 0, 0, 2)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, 0)
-	ZEND_ARG_INFO(0, async)
+	ZEND_ARG_INFO(0, sync)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll

From 947a2d3897f2be2d51fb4672c351df9835724e78 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 20 Sep 2021 20:50:07 +0300
Subject: [PATCH 1561/1986] Issue #1894

---
 redis.c                    | 44 ++++++++++++++++++++++++++++++++++++++
 redis.stub.php             |  2 ++
 redis_arginfo.h            |  6 +++++-
 redis_legacy_arginfo.h     |  6 +++++-
 tests/RedisClusterTest.php |  1 +
 tests/RedisTest.php        | 13 +++++++++++
 6 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/redis.c b/redis.c
index a1233ac1ff..617906d078 100644
--- a/redis.c
+++ b/redis.c
@@ -904,6 +904,50 @@ PHP_METHOD(Redis, renameNx)
 }
 /* }}} */
 
+/** {{{ proto bool Redis::reset()
+ */
+PHP_METHOD(Redis, reset)
+{
+    char *response;
+    int response_len;
+    RedisSock *redis_sock;
+    smart_string cmd = {0};
+    zend_bool ret = 0;
+
+    if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
+        RETURN_FALSE;
+    }
+
+    if (IS_PIPELINE(redis_sock)) {
+        php_error_docref(NULL, E_ERROR, "Reset ins't allowed in pipeline mode!");
+        RETURN_FALSE;
+    }
+
+    redis_cmd_init_sstr(&cmd, 0, "RESET", 5);
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
+
+    if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) {
+        ret = REDIS_STRCMP_STATIC(response, response_len, "+RESET");
+        efree(response);
+    }
+
+    if (!ret) {
+        if (IS_ATOMIC(redis_sock)) {
+            RETURN_FALSE;
+        }
+        REDIS_THROW_EXCEPTION("Reset failed in multi mode!", 0);
+        RETURN_ZVAL(getThis(), 1, 0);
+    }
+
+    free_reply_callbacks(redis_sock);
+    redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
+    redis_sock->mode = ATOMIC;
+    redis_sock->dbNumber = 0;
+    redis_sock->watching = 0;
+
+    RETURN_TRUE;
+}
 /* }}} */
 
 /* {{{ proto string Redis::get(string key)
diff --git a/redis.stub.php b/redis.stub.php
index 52ad2e1f87..0318381765 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -336,6 +336,8 @@ public function rename(string $key_src, string $key_dst);
 	/** @return bool|Redis */
     public function renameNx(string $key_src, string $key_dst);
 
+    public function reset(): bool;
+
     public function restore(string $key, int $timeout, string $value): bool;
 
     public function role(): mixed;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index be0802312f..786c2547fa 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0475243df03f4f3d6e568fa9ae164073dadc930d */
+ * Stub hash: 4b894d8f0c04d6c25398e5dc399598d0ede4ed05 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -554,6 +554,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
+#define arginfo_class_Redis_reset arginfo_class_Redis_bgSave
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_restore, 0, 3, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
@@ -1080,6 +1082,7 @@ ZEND_METHOD(Redis, randomKey);
 ZEND_METHOD(Redis, rawcommand);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
+ZEND_METHOD(Redis, reset);
 ZEND_METHOD(Redis, restore);
 ZEND_METHOD(Redis, role);
 ZEND_METHOD(Redis, rpoplpush);
@@ -1308,6 +1311,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, rawcommand, arginfo_class_Redis_rawcommand, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, reset, arginfo_class_Redis_reset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, restore, arginfo_class_Redis_restore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, role, arginfo_class_Redis_role, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rpoplpush, arginfo_class_Redis_rpoplpush, ZEND_ACC_PUBLIC)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 77159f50c6..39890355e5 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0475243df03f4f3d6e568fa9ae164073dadc930d */
+ * Stub hash: 4b894d8f0c04d6c25398e5dc399598d0ede4ed05 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -488,6 +488,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
+#define arginfo_class_Redis_reset arginfo_class_Redis___destruct
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_restore, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, timeout)
@@ -982,6 +984,7 @@ ZEND_METHOD(Redis, randomKey);
 ZEND_METHOD(Redis, rawcommand);
 ZEND_METHOD(Redis, rename);
 ZEND_METHOD(Redis, renameNx);
+ZEND_METHOD(Redis, reset);
 ZEND_METHOD(Redis, restore);
 ZEND_METHOD(Redis, role);
 ZEND_METHOD(Redis, rpoplpush);
@@ -1210,6 +1213,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, rawcommand, arginfo_class_Redis_rawcommand, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rename, arginfo_class_Redis_rename, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, renameNx, arginfo_class_Redis_renameNx, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, reset, arginfo_class_Redis_reset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, restore, arginfo_class_Redis_restore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, role, arginfo_class_Redis_role, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rpoplpush, arginfo_class_Redis_rpoplpush, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index f491e92aa0..6ca5153d42 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -47,6 +47,7 @@ public function testDoublePipeNoOp() { return $this->markTestSkipped(); }
     public function testSwapDB() { return $this->markTestSkipped(); }
     public function testConnectException() { return $this->markTestSkipped(); }
     public function testTlsConnect() { return $this->markTestSkipped(); }
+    public function testReset() { return $this->markTestSkipped(); }
     public function testInvalidAuthArgs() { return $this->markTestSkipped(); }
 
     public function testlMove() { return $this->markTestSkipped(); }
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 6a5f4153ad..8c79d6befe 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6847,6 +6847,19 @@ public function testTlsConnect()
         }
     }
 
+    public function testReset()
+    {
+        // Only available since 6.2.0
+        if (version_compare($this->version, '6.2.0') < 0) {
+            $this->markTestSkipped();
+            return;
+        }
+
+        $this->assertTrue($this->redis->multi()->select(2)->set('foo', 'bar')->reset());
+        $this->assertEquals(Redis::ATOMIC, $this->redis->getMode());
+        $this->assertEquals(0, $this->redis->getDBNum());
+    }
+
     public function testCopy()
     {
         // Only available since 6.2.0

From fe397371ef1504b90c484df93c30a596aa201ce1 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 11 Apr 2022 22:59:01 +0300
Subject: [PATCH 1562/1986] Issue #1894

Add Redis::hRandField command
---
 library.c                  | 11 +++++++++
 library.h                  |  1 +
 redis.c                    |  8 ++++++
 redis.stub.php             |  2 ++
 redis_arginfo.h            | 14 +++++++----
 redis_commands.c           | 50 ++++++++++++++++++++++++++++++++++++--
 redis_commands.h           |  3 +++
 redis_legacy_arginfo.h     | 16 +++++++-----
 tests/RedisClusterTest.php |  1 +
 tests/RedisTest.php        | 19 +++++++++++++++
 10 files changed, 112 insertions(+), 13 deletions(-)

diff --git a/library.c b/library.c
index 57efe28c54..98e7c69b50 100644
--- a/library.c
+++ b/library.c
@@ -1300,6 +1300,17 @@ redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_
     return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
 }
 
+PHP_REDIS_API int
+redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    if (ctx == NULL) {
+        return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    }
+    return redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+}
+
 PHP_REDIS_API int
 redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                             zval *z_tab, void *ctx,
diff --git a/library.h b/library.h
index ac1582fd36..8c277b99bd 100644
--- a/library.h
+++ b/library.h
@@ -165,6 +165,7 @@ PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, Redis
 PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 /* Helper methods to get configuration values from a HashTable. */
 
diff --git a/redis.c b/redis.c
index 617906d078..49b16a9187 100644
--- a/redis.c
+++ b/redis.c
@@ -2219,6 +2219,14 @@ PHP_METHOD(Redis, hMset)
 }
 /* }}} */
 
+/* {{{ proto bool Redis::hRandField(string key, [array $options]) */
+PHP_METHOD(Redis, hRandField)
+{
+    REDIS_PROCESS_CMD(hrandfield, redis_hrandfield_response);
+}
+/* }}} */
+
+
 /* {{{ proto long Redis::hstrlen(string key, string field) */
 PHP_METHOD(Redis, hStrLen) {
     REDIS_PROCESS_CMD(hstrlen, redis_long_response);
diff --git a/redis.stub.php b/redis.stub.php
index 0318381765..e5b72dd1c7 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -193,6 +193,8 @@ public function hMget(string $key, array $keys): array;
 
     public function hMset(string $key, array $keyvals): bool;
 
+    public function hRandField(string $key, array $options = null): string|array;
+
     public function hSet(string $key, string $member, string $value): int;
 
     public function hSetNx(string $key, string $member, string $value): int;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 786c2547fa..3525e26cc0 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 4b894d8f0c04d6c25398e5dc399598d0ede4ed05 */
+ * Stub hash: 452d7bafe0b4a9d6d67353613d9cde2d407aa2a2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -334,6 +334,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hMset, 0, 2, _IS_BOO
 	ZEND_ARG_TYPE_INFO(0, keyvals, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hSet, 0, 3, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
@@ -888,10 +893,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 3,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_zRandMember, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField
 
 #define arginfo_class_Redis_zRank arginfo_class_Redis_hStrLen
 
@@ -1030,6 +1032,7 @@ ZEND_METHOD(Redis, hKeys);
 ZEND_METHOD(Redis, hLen);
 ZEND_METHOD(Redis, hMget);
 ZEND_METHOD(Redis, hMset);
+ZEND_METHOD(Redis, hRandField);
 ZEND_METHOD(Redis, hSet);
 ZEND_METHOD(Redis, hSetNx);
 ZEND_METHOD(Redis, hStrLen);
@@ -1257,6 +1260,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, hLen, arginfo_class_Redis_hLen, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hMget, arginfo_class_Redis_hMget, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hMset, arginfo_class_Redis_hMset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hRandField, arginfo_class_Redis_hRandField, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hSet, arginfo_class_Redis_hSet, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 7d9fd88a27..c4809e3284 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2548,6 +2548,54 @@ int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         cmd, cmd_len, slot);
 }
 
+int
+redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                      char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    char *key;
+    int count = 0;
+    size_t key_len;
+    smart_string cmdstr = {0};
+    zend_bool withvalues = 0;
+    zval *z_opts = NULL, *z_ele;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a",
+                              &key, &key_len, &z_opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
+        zend_string *zkey;
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
+            ZVAL_DEREF(z_ele);
+            if (zend_string_equals_literal_ci(zkey, "count")) {
+                count = zval_get_long(z_ele);
+            } else if (zend_string_equals_literal_ci(zkey, "withvalues")) {
+                withvalues = zval_is_true(z_ele);
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (count != 0) + withvalues, "HRANDFIELD");
+    redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot);
+
+    if (count != 0) {
+        redis_cmd_append_sstr_long(&cmdstr, count);
+        *ctx = PHPREDIS_CTX_PTR;
+    }
+
+    if (withvalues) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHVALUES");
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    }
+
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return SUCCESS;
+}
+
 /* SRANDMEMBER */
 int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                           char **cmd, int *cmd_len, short *slot, void **ctx,
@@ -3299,7 +3347,6 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 {
     char *key, *unit;
     int argc = 2;
-    short store_slot = 0;
     size_t keylen, unitlen;
     geoOptions gopts = {0};
     smart_string cmdstr = {0};
@@ -3423,7 +3470,6 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 {
     int argc = 3;
     char *dest, *src, *unit;
-    short store_slot = 0;
     size_t destlen, srclen, unitlen;
     geoOptions gopts = {0};
     smart_string cmdstr = {0};
diff --git a/redis_commands.h b/redis_commands.h
index eed3d7dccf..ab9646e7ac 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -219,6 +219,9 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 39890355e5..1788713cfe 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 4b894d8f0c04d6c25398e5dc399598d0ede4ed05 */
+ * Stub hash: 452d7bafe0b4a9d6d67353613d9cde2d407aa2a2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -304,6 +304,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMset, 0, 0, 2)
 	ZEND_ARG_INFO(0, keyvals)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hRandField, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_hSet arginfo_class_Redis_hIncrBy
 
 #define arginfo_class_Redis_hSetNx arginfo_class_Redis_hIncrBy
@@ -602,10 +607,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slowlog, 0, 0, 1)
 	ZEND_ARG_INFO(0, option)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sort, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, options)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_sort arginfo_class_Redis_hRandField
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sortAsc, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
@@ -800,7 +802,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 0, 3)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRandMember arginfo_class_Redis_sort
+#define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField
 
 #define arginfo_class_Redis_zRank arginfo_class_Redis_hExists
 
@@ -932,6 +934,7 @@ ZEND_METHOD(Redis, hKeys);
 ZEND_METHOD(Redis, hLen);
 ZEND_METHOD(Redis, hMget);
 ZEND_METHOD(Redis, hMset);
+ZEND_METHOD(Redis, hRandField);
 ZEND_METHOD(Redis, hSet);
 ZEND_METHOD(Redis, hSetNx);
 ZEND_METHOD(Redis, hStrLen);
@@ -1159,6 +1162,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, hLen, arginfo_class_Redis_hLen, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hMget, arginfo_class_Redis_hMget, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hMset, arginfo_class_Redis_hMset, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, hRandField, arginfo_class_Redis_hRandField, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hSet, arginfo_class_Redis_hSet, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 6ca5153d42..18e4ac21de 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -61,6 +61,7 @@ public function testZRandMember() { return $this->marktestSkipped(); }
     public function testCopy() { return $this->marktestSkipped(); }
     public function testGeoSearch() { return $this->marktestSkipped(); }
     public function testGeoSearchStore() { return $this->marktestSkipped(); }
+    public function testHRandField() { return $this->marktestSkipped(); }
 
     /* Session locking feature is currently not supported in in context of Redis Cluster.
        The biggest issue for this is the distribution nature of Redis cluster */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 8c79d6befe..d480e7ec2b 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2846,6 +2846,25 @@ public function testHashes() {
 	}
     }
 
+    public function testHRandField()
+    {
+        if (version_compare($this->version, "6.2.0") < 0) {
+            $this->MarkTestSkipped();
+            return;
+        }
+        $this->redis->del('key');
+        $this->redis->hMSet('key', ['a' => 0, 'b' => 1, 'c' => 'foo', 'd' => 'bar', 'e' => null]);
+        $this->assertInArray($this->redis->hRandField('key'), ['a', 'b', 'c', 'd', 'e']);
+
+        $result = $this->redis->hRandField('key', ['count' => 3]);
+        $this->assertEquals(3, count($result));
+        $this->assertEquals(array_intersect($result, ['a', 'b', 'c', 'd', 'e']), $result);
+
+        $result = $this->redis->hRandField('key', ['count' => 2, 'withvalues' => true]);
+        $this->assertEquals(2, count($result));
+        $this->assertEquals(array_intersect_key($result, ['a' => 0, 'b' => 1, 'c' => 'foo', 'd' => 'bar', 'e' => null]), $result);
+    }
+
     public function testSetRange() {
 
         $this->redis->del('key');

From 36457555d4858d6559afccdf7c4a20951af94322 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 13 Apr 2022 16:18:29 +0300
Subject: [PATCH 1563/1986] Fix NULL-pointer dereferences and handle possible
 UB

---
 library.c        | 20 +++++++++++++++----
 redis_commands.c | 52 +++++++++++++++++++++++++-----------------------
 2 files changed, 43 insertions(+), 29 deletions(-)

diff --git a/library.c b/library.c
index 98e7c69b50..4514c4bcc6 100644
--- a/library.c
+++ b/library.c
@@ -1278,8 +1278,11 @@ redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
     } else if (ctx == PHPREDIS_CTX_PTR) {
         return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 1) {
+        return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
     }
-    return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
 }
 
 PHP_REDIS_API int
@@ -1287,8 +1290,11 @@ redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *
 {
     if (ctx == NULL) {
         return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
     }
-    return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
 }
 
 PHP_REDIS_API int
@@ -1296,8 +1302,11 @@ redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_
 {
     if (ctx == NULL) {
         return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
     }
-    return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
 }
 
 PHP_REDIS_API int
@@ -1307,8 +1316,11 @@ redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, z
         return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
     } else if (ctx == PHPREDIS_CTX_PTR) {
         return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 1) {
+        return redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
     }
-    return redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
 }
 
 PHP_REDIS_API int
diff --git a/redis_commands.c b/redis_commands.c
index c4809e3284..83bf5e1e52 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -649,6 +649,7 @@ redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     smart_string cmdstr = {0};
     zend_bool withscores = 0;
     zval *z_opts = NULL, *z_ele;
+    zend_string *zkey;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a",
                               &key, &key_len, &z_opts) == FAILURE)
@@ -656,14 +657,15 @@ redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
-        zend_string *zkey;
+    if (z_opts != NULL) {
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
-            ZVAL_DEREF(z_ele);
-            if (zend_string_equals_literal_ci(zkey, "count")) {
-                count = zval_get_long(z_ele);
-            } else if (zend_string_equals_literal_ci(zkey, "withscores")) {
-                withscores = zval_is_true(z_ele);
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "count")) {
+                    count = zval_get_long(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "withscores")) {
+                    withscores = zval_is_true(z_ele);
+                }
             }
         } ZEND_HASH_FOREACH_END();
     }
@@ -695,6 +697,7 @@ redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     smart_string cmdstr = {0};
     zval *z_keys, *z_opts = NULL, *z_ele;
     zend_bool withscores = 0;
+    zend_string *zkey;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a",
                               &z_keys, &z_opts) == FAILURE)
@@ -706,8 +709,7 @@ redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
-        zend_string *zkey;
+    if (z_opts != NULL) {
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
             if (zkey != NULL) {
                 ZVAL_DEREF(z_ele);
@@ -780,7 +782,7 @@ redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     int numkeys;
     smart_string cmdstr = {0};
     zval *z_keys, *z_weights = NULL, *z_opts = NULL, *z_ele;
-    zend_string *aggregate = NULL;
+    zend_string *aggregate = NULL, *zkey;
     zend_bool withscores = 0;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a!a",
@@ -801,8 +803,7 @@ redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         }
     }
 
-    if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
-        zend_string *zkey;
+    if (z_opts != NULL) {
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
             if (zkey != NULL) {
                 ZVAL_DEREF(z_ele);
@@ -2558,6 +2559,7 @@ redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     smart_string cmdstr = {0};
     zend_bool withvalues = 0;
     zval *z_opts = NULL, *z_ele;
+    zend_string *zkey;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a",
                               &key, &key_len, &z_opts) == FAILURE)
@@ -2565,14 +2567,15 @@ redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
-        zend_string *zkey;
+    if (z_opts != NULL) {
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
-            ZVAL_DEREF(z_ele);
-            if (zend_string_equals_literal_ci(zkey, "count")) {
-                count = zval_get_long(z_ele);
-            } else if (zend_string_equals_literal_ci(zkey, "withvalues")) {
-                withvalues = zval_is_true(z_ele);
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "count")) {
+                    count = zval_get_long(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "withvalues")) {
+                    withvalues = zval_is_true(z_ele);
+                }
             }
         } ZEND_HASH_FOREACH_END();
     }
@@ -3379,7 +3382,7 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     /* Attempt to parse our options array */
-    if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) {
+    if (opts != NULL) {
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
             ZVAL_DEREF(z_ele);
             if (zkey != NULL) {
@@ -3502,7 +3505,7 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     /* Attempt to parse our options array */
-    if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) {
+    if (opts != NULL) {
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
             ZVAL_DEREF(z_ele);
             if (zkey != NULL) {
@@ -3824,7 +3827,8 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     size_t src_len, dst_len;
     zend_long db = -1;
     zend_bool replace = 0;
-    zval *opts = NULL;
+    zval *opts = NULL, *z_ele;
+    zend_string *zkey;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a",
                               &src, &src_len, &dst, &dst_len, &opts) == FAILURE)
@@ -3832,9 +3836,7 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) {
-        zend_string *zkey;
-        zval *z_ele;
+    if (opts != NULL) {
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
             if (zkey != NULL) {
                 ZVAL_DEREF(z_ele);

From ca8b4c930a3b7a4d9cd993fc0b401f2ce5ae78b5 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 13 Apr 2022 17:25:13 +0300
Subject: [PATCH 1564/1986] Use read_mbulk_header helper where possible

---
 library.c | 142 +++++++++++++++---------------------------------------
 1 file changed, 39 insertions(+), 103 deletions(-)

diff --git a/library.c b/library.c
index 4514c4bcc6..6537bc8da7 100644
--- a/library.c
+++ b/library.c
@@ -286,6 +286,29 @@ redis_error_throw(RedisSock *redis_sock)
     }
 }
 
+static int
+read_mbulk_header(RedisSock *redis_sock, int *nelem)
+{
+    char line[4096];
+    size_t len;
+
+    /* Throws exception on failure */
+    if (redis_sock_gets(redis_sock, line, sizeof(line) - 1, &len) < 0) {
+        return FAILURE;
+    }
+
+    if (*line != TYPE_MULTIBULK) {
+        if (*line == TYPE_ERR) {
+            redis_sock_set_err(redis_sock, line + 1, len - 1);
+        }
+        return FAILURE;
+    }
+
+    *nelem = atoi(line + 1);
+
+    return SUCCESS;
+}
+
 PHP_REDIS_API int
 redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw)
 {
@@ -561,22 +584,13 @@ PHP_REDIS_API zval *
 redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS,
                                      RedisSock *redis_sock, zval *z_tab)
 {
-    char inbuf[4096];
     int numElems;
-    size_t len;
 
-    ZVAL_NULL(z_tab);
-    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
-        return NULL;
-    }
-
-    if(inbuf[0] != '*') {
+    if (read_mbulk_header(redis_sock, &numElems) < 0) {
+        ZVAL_NULL(z_tab);
         return NULL;
     }
-    numElems = atoi(inbuf+1);
-
     array_init(z_tab);
-
     redis_mbulk_reply_loop(redis_sock, z_tab, numElems, UNSERIALIZE_ALL);
 
     return z_tab;
@@ -1461,39 +1475,14 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab,
     ZVAL_ZVAL(z_tab, &z_ret, 0, 0);
 }
 
-static int
-read_mbulk_header(RedisSock *redis_sock, int *nelem)
-{
-    char line[4096];
-    size_t len;
-
-    /* Throws exception on failure */
-    if (redis_sock_gets(redis_sock, line, sizeof(line)-1, &len) < 0)
-        return -1;
-
-    if (line[0] != '*') {
-        if (IS_ATOMIC(redis_sock)) {
-            if (line[0] == '-') {
-                redis_sock_set_err(redis_sock, line+1, len-1);
-            }
-        }
-        return -1;
-    }
-
-    *nelem = atoi(line+1);
-
-    return 0;
-}
 
 static int
 redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                          zval *z_tab, int unserialize, int decode)
 {
-    char inbuf[4096];
     int numElems;
-    size_t len;
 
-    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
+    if (read_mbulk_header(redis_sock, &numElems) < 0) {
         if (IS_ATOMIC(redis_sock)) {
             RETVAL_FALSE;
         } else {
@@ -1501,19 +1490,6 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         }
         return FAILURE;
     }
-
-    if(inbuf[0] != '*') {
-        if (IS_ATOMIC(redis_sock)) {
-            RETVAL_FALSE;
-        } else {
-            add_next_index_bool(z_tab, 0);
-        }
-        if (*inbuf == TYPE_ERR) {
-            redis_sock_set_err(redis_sock, inbuf + 1, len - 1);
-        }
-        return -1;
-    }
-    numElems = atoi(inbuf+1);
     zval z_multi_result;
     array_init(&z_multi_result); /* pre-allocate array for multi's results. */
 
@@ -1552,6 +1528,11 @@ redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     zend_string *zkey;
 
     if (read_mbulk_header(redis_sock, &numElems) < 0) {
+        if (IS_ATOMIC(redis_sock)) {
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
+        }
         return FAILURE;
     }
 
@@ -2614,28 +2595,16 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
                                            void *ctx)
 {
     zval z_multi_result;
-    char inbuf[4096];
     int numElems;
-    size_t len;
 
-    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
-        return -1;
-    }
-
-    if(inbuf[0] != '*') {
+    if (read_mbulk_header(redis_sock, &numElems) < 0) {
         if (IS_ATOMIC(redis_sock)) {
-            if (inbuf[0] == '-') {
-                redis_sock_set_err(redis_sock, inbuf+1, len);
-            }
             RETVAL_FALSE;
         } else {
             add_next_index_bool(z_tab, 0);
         }
-        return -1;
+        return FAILURE;
     }
-
-    numElems = atoi(inbuf+1);
-
     if (numElems == -1 && redis_sock->null_mbulk_as_null) {
         ZVAL_NULL(&z_multi_result);
     } else {
@@ -2657,11 +2626,9 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
 PHP_REDIS_API int
 redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
-    char inbuf[4096];
     int numElems;
-    size_t len;
 
-    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
+    if (read_mbulk_header(redis_sock, &numElems) < 0) {
         if (IS_ATOMIC(redis_sock)) {
             RETVAL_FALSE;
         } else {
@@ -2669,19 +2636,6 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
         }
         return FAILURE;
     }
-
-    if(inbuf[0] != '*') {
-        if (IS_ATOMIC(redis_sock)) {
-            if (inbuf[0] == '-') {
-                redis_sock_set_err(redis_sock, inbuf+1, len);
-            }
-            RETVAL_FALSE;
-        } else {
-            add_next_index_bool(z_tab, 0);
-        }
-        return -1;
-    }
-    numElems = atoi(inbuf+1);
     zval z_multi_result;
     array_init(&z_multi_result); /* pre-allocate array for multi's results. */
 
@@ -2693,33 +2647,24 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
         add_next_index_zval(z_tab, &z_multi_result);
     }
 
-    return 0;
+    return SUCCESS;
 }
 
 PHP_REDIS_API int
 redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
-    char inbuf[4096], *line;
+    char *line;
     int i, numElems, len;
-    size_t buf_len;
     zval z_multi_result;
 
-    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &buf_len) < 0) {
-        return FAILURE;
-    }
-
-    if (*inbuf != TYPE_MULTIBULK) {
+    if (read_mbulk_header(redis_sock, &numElems) < 0) {
         if (IS_ATOMIC(redis_sock)) {
-            if (*inbuf == TYPE_ERR) {
-                redis_sock_set_err(redis_sock, inbuf + 1, buf_len);
-            }
             RETVAL_FALSE;
         } else {
             add_next_index_bool(z_tab, 0);
         }
         return FAILURE;
     }
-    numElems = atoi(inbuf + 1);
 
     array_init(&z_multi_result);
     for (i = 0; i < numElems; ++i) {
@@ -2825,29 +2770,20 @@ redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int coun
  * keys with their returned values */
 PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
 {
-    char inbuf[4096], *response;
+    char *response;
     int response_len;
     int i, numElems;
-    size_t len;
 
     zval *z_keys = ctx;
 
-    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
-        goto failure;
-    }
-
-    if (*inbuf != TYPE_MULTIBULK) {
+    if (read_mbulk_header(redis_sock, &numElems) < 0) {
         if (IS_ATOMIC(redis_sock)) {
             RETVAL_FALSE;
         } else {
             add_next_index_bool(z_tab, 0);
         }
-        if (*inbuf == TYPE_ERR) {
-            redis_sock_set_err(redis_sock, inbuf + 1, len - 1);
-        }
         goto failure;
     }
-    numElems = atoi(inbuf+1);
     zval z_multi_result;
     array_init(&z_multi_result); /* pre-allocate array for multi's results. */
 

From bfe05a6936c72eceeea3c392998226f3b87ed280 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 13 Apr 2022 17:39:47 +0300
Subject: [PATCH 1565/1986] CodeQL

---
 .github/workflows/codeql.yml | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 .github/workflows/codeql.yml

diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000000..7b7c2cc310
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,21 @@
+on: [push]
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Initialize CodeQL
+        uses: github/codeql-action/init@v2
+        with:
+          languages: cpp
+          queries: +security-and-quality
+      - name: Pre-build
+        run: |
+          phpize
+      - name: Autobuild
+        uses: github/codeql-action/autobuild@v2
+      - name: Perform CodeQL Analysis
+        uses: github/codeql-action/analyze@v2

From ee210f86e58894e24128b2540690c7838d379f47 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 13 Apr 2022 18:30:38 +0300
Subject: [PATCH 1566/1986] Fix security alerts

---
 redis_commands.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 83bf5e1e52..47f7490405 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -450,7 +450,7 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    zend_bool sync = -1;
+    int sync = -1;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &sync) == FAILURE) {
         return FAILURE;
@@ -549,10 +549,12 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     if (z_ws) {
         if (Z_TYPE_P(z_ws) == IS_ARRAY) {
             ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_ws), zkey, z_ele) {
-                ZVAL_DEREF(z_ele);
-                if (zend_string_equals_literal_ci(zkey, "withscores")) {
-                    *withscores = zval_is_true(z_ele);
-                    break;
+                if (zkey != NULL) {
+                    ZVAL_DEREF(z_ele);
+                    if (zend_string_equals_literal_ci(zkey, "withscores")) {
+                        *withscores = zval_is_true(z_ele);
+                        break;
+                    }
                 }
             } ZEND_HASH_FOREACH_END();
         } else if (Z_TYPE_P(z_ws) == IS_TRUE) {
@@ -4024,16 +4026,13 @@ append_stream_args(smart_string *cmdstr, HashTable *ht, RedisSock *redis_sock,
     char *kptr, kbuf[40];
     int klen, i, pos = 0;
     zend_string *key, *idstr;
-    short oldslot;
+    short oldslot = -1;
     zval **id;
     zend_ulong idx;
 
     /* Append STREAM qualifier */
     REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "STREAMS");
 
-    /* Sentinel slot */
-    if (slot) oldslot = -1;
-
     /* Allocate memory to keep IDs */
     id = emalloc(sizeof(*id) * zend_hash_num_elements(ht));
 

From 55bf0202dd674c70e147fc50e72298719e23391e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 14 Apr 2022 21:13:54 +0300
Subject: [PATCH 1567/1986] Fix segfault

---
 library.c | 4 +++-
 redis.c   | 3 +++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/library.c b/library.c
index 6537bc8da7..dc2f889c79 100644
--- a/library.c
+++ b/library.c
@@ -2581,7 +2581,9 @@ redis_sock_set_stream_context(RedisSock *redis_sock, zval *options)
         redis_sock->stream_ctx = php_stream_context_alloc();
 
     ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), zkey, z_ele) {
-        php_stream_context_set_option(redis_sock->stream_ctx, "ssl", ZSTR_VAL(zkey), z_ele);
+        if (zkey != NULL) {
+            php_stream_context_set_option(redis_sock->stream_ctx, "ssl", ZSTR_VAL(zkey), z_ele);
+        }
     } ZEND_HASH_FOREACH_END();
 
     return SUCCESS;
diff --git a/redis.c b/redis.c
index 49b16a9187..ac7afd8021 100644
--- a/redis.c
+++ b/redis.c
@@ -606,6 +606,9 @@ PHP_METHOD(Redis, __construct)
         redis->sock = redis_sock_create("127.0.0.1", 0, 6379, 0, 0, 0, NULL, 0);
 
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, val) {
+            if (zkey == NULL) {
+                continue;
+            }
             ZVAL_DEREF(val);
             if (zend_string_equals_literal_ci(zkey, "host")) {
                 if (Z_TYPE_P(val) != IS_STRING) {

From 0a160685cd199929eee0fea214bd4f25ab8f3326 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 14 Apr 2022 21:29:35 +0300
Subject: [PATCH 1568/1986] Issue #1894

Add PXAT/EXAT arguments to SET command.
---
 redis_commands.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/redis_commands.c b/redis_commands.c
index 47f7490405..34bf0b0994 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1594,7 +1594,11 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         ZEND_HASH_FOREACH_STR_KEY_VAL(kt, zkey, v) {
             ZVAL_DEREF(v);
             /* Detect PX or EX argument and validate timeout */
-            if (zkey && (ZSTR_STRICMP_STATIC(zkey, "EX") || ZSTR_STRICMP_STATIC(zkey, "PX"))) {
+            if (zkey && (ZSTR_STRICMP_STATIC(zkey, "EX") ||
+                         ZSTR_STRICMP_STATIC(zkey, "PX") ||
+                         ZSTR_STRICMP_STATIC(zkey, "EXAT") ||
+                         ZSTR_STRICMP_STATIC(zkey, "PXAT"))
+            ) {
                 exp_set = 1;
 
                 /* Set expire type */

From 11861d95113a385f6eb6360a14ab35582effbf7e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 17 Apr 2022 17:57:11 +0300
Subject: [PATCH 1569/1986] Issue #1894

Add GETEX, GETDEL commands.
---
 redis.c                | 15 +++++++++++
 redis.stub.php         |  4 +++
 redis_arginfo.h        | 15 ++++++++++-
 redis_commands.c       | 59 ++++++++++++++++++++++++++++++++++++++++++
 redis_commands.h       |  3 +++
 redis_legacy_arginfo.h | 22 +++++++++++-----
 6 files changed, 110 insertions(+), 8 deletions(-)

diff --git a/redis.c b/redis.c
index ac7afd8021..ff385b67cf 100644
--- a/redis.c
+++ b/redis.c
@@ -961,6 +961,21 @@ PHP_METHOD(Redis, get)
 }
 /* }}} */
 
+/* {{{ proto string Redis::getDel(string key)
+ */
+PHP_METHOD(Redis, getDel)
+{
+    REDIS_PROCESS_KW_CMD("GETDEL", redis_key_cmd, redis_string_response);
+}
+/* }}} */
+
+/* {{{ proto string Redis::getEx(string key [, array $options = []])
+ */
+PHP_METHOD(Redis, getEx)
+{
+    REDIS_PROCESS_CMD(getex, redis_string_response);
+}
+/* }}} */
 
 /* {{{ proto string Redis::ping()
  */
diff --git a/redis.stub.php b/redis.stub.php
index e5b72dd1c7..b1c5fc7e5f 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -149,8 +149,12 @@ public function getAuth(): mixed;
 	/** @return int|Redis */
     public function getBit(string $key, int $idx);
 
+    public function getEx(string $key, array $options = []): bool|string;
+
     public function getDBNum(): int;
 
+    public function getDel(string $key): bool|string;
+
     public function getHost(): string;
 
     public function getLastError(): ?string;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 3525e26cc0..4b7c62bfea 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 452d7bafe0b4a9d6d67353613d9cde2d407aa2a2 */
+ * Stub hash: 8ad4d58327d1b0b07b0dbec026b27ed6a594b8b9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -256,8 +256,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getEx, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_getDBNum arginfo_class_Redis_dbSize
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getDel, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getHost, 0, 0, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
@@ -1011,7 +1020,9 @@ ZEND_METHOD(Redis, geosearchstore);
 ZEND_METHOD(Redis, get);
 ZEND_METHOD(Redis, getAuth);
 ZEND_METHOD(Redis, getBit);
+ZEND_METHOD(Redis, getEx);
 ZEND_METHOD(Redis, getDBNum);
+ZEND_METHOD(Redis, getDel);
 ZEND_METHOD(Redis, getHost);
 ZEND_METHOD(Redis, getLastError);
 ZEND_METHOD(Redis, getMode);
@@ -1239,7 +1250,9 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getEx, arginfo_class_Redis_getEx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getDBNum, arginfo_class_Redis_getDBNum, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getDel, arginfo_class_Redis_getDel, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getHost, arginfo_class_Redis_getHost, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getLastError, arginfo_class_Redis_getLastError, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getMode, arginfo_class_Redis_getMode, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 47f7490405..af6754a1df 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1670,6 +1670,65 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    smart_string cmdstr = {0};
+    char *key, *exp_type = NULL;
+    zval *z_opts = NULL, *z_ele;
+    zend_long expire = -1;
+    zend_bool persist = 0;
+    zend_string *zkey;
+    size_t key_len;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a",
+                              &key, &key_len, &z_opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if (z_opts != NULL) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (ZSTR_STRICMP_STATIC(zkey, "EX") ||
+                    ZSTR_STRICMP_STATIC(zkey, "PX") ||
+                    ZSTR_STRICMP_STATIC(zkey, "EXAT") ||
+                    ZSTR_STRICMP_STATIC(zkey, "PXAT")
+                ) {
+                    exp_type = ZSTR_VAL(zkey);
+                    expire = zval_get_long(z_ele);
+                    persist = 0;
+                } else if (ZSTR_STRICMP_STATIC(zkey, "PERSIST")) {
+                    persist = zval_is_true(z_ele);
+                    exp_type = NULL;
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    if (exp_type != NULL && expire < 1) {
+        php_error_docref(NULL, E_WARNING, "EXPIRE can't be < 1");
+        return FAILURE;
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (exp_type ? 2 : persist), "GETEX");
+    redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot);
+
+    if (exp_type != NULL) {
+        redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type));
+        redis_cmd_append_sstr_long(&cmdstr, expire);
+    } else if (persist) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "PERSIST");
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 /* BRPOPLPUSH */
 int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                          char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index ab9646e7ac..94c5dd70ee 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -159,6 +159,9 @@ int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock
 int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 1788713cfe..b275ea646c 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 452d7bafe0b4a9d6d67353613d9cde2d407aa2a2 */
+ * Stub hash: 8ad4d58327d1b0b07b0dbec026b27ed6a594b8b9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -243,8 +243,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
 	ZEND_ARG_INFO(0, idx)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getEx, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_getDBNum arginfo_class_Redis___destruct
 
+#define arginfo_class_Redis_getDel arginfo_class_Redis__prefix
+
 #define arginfo_class_Redis_getHost arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_getLastError arginfo_class_Redis___destruct
@@ -304,10 +311,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMset, 0, 0, 2)
 	ZEND_ARG_INFO(0, keyvals)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hRandField, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, options)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_hRandField arginfo_class_Redis_getEx
 
 #define arginfo_class_Redis_hSet arginfo_class_Redis_hIncrBy
 
@@ -607,7 +611,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slowlog, 0, 0, 1)
 	ZEND_ARG_INFO(0, option)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_sort arginfo_class_Redis_hRandField
+#define arginfo_class_Redis_sort arginfo_class_Redis_getEx
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sortAsc, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
@@ -802,7 +806,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 0, 3)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField
+#define arginfo_class_Redis_zRandMember arginfo_class_Redis_getEx
 
 #define arginfo_class_Redis_zRank arginfo_class_Redis_hExists
 
@@ -913,7 +917,9 @@ ZEND_METHOD(Redis, geosearchstore);
 ZEND_METHOD(Redis, get);
 ZEND_METHOD(Redis, getAuth);
 ZEND_METHOD(Redis, getBit);
+ZEND_METHOD(Redis, getEx);
 ZEND_METHOD(Redis, getDBNum);
+ZEND_METHOD(Redis, getDel);
 ZEND_METHOD(Redis, getHost);
 ZEND_METHOD(Redis, getLastError);
 ZEND_METHOD(Redis, getMode);
@@ -1141,7 +1147,9 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getEx, arginfo_class_Redis_getEx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getDBNum, arginfo_class_Redis_getDBNum, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getDel, arginfo_class_Redis_getDel, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getHost, arginfo_class_Redis_getHost, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getLastError, arginfo_class_Redis_getLastError, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getMode, arginfo_class_Redis_getMode, ZEND_ACC_PUBLIC)

From 415177536729610a0472a5e2d757b4a07707bcd6 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 23 Apr 2022 14:47:29 +0300
Subject: [PATCH 1570/1986] Issue #2098

---
 library.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library.c b/library.c
index dc2f889c79..2e428e08e9 100644
--- a/library.c
+++ b/library.c
@@ -3599,7 +3599,7 @@ int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass)
 
     /* Handle a non-array first */
     if (Z_TYPE_P(ztest) != IS_ARRAY) {
-        *pass = zval_get_string(ztest);
+        TRY_SET_AUTH_ARG(ztest, pass);
         return SUCCESS;
     }
 

From 4b767be7e9c0e225b4c2a6e26ae18b8e217f4ba5 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 23 Apr 2022 15:59:26 +0300
Subject: [PATCH 1571/1986] Issue #1894

Add FAILOVER command.
---
 redis.c                |  7 +++++
 redis.stub.php         |  2 ++
 redis_arginfo.h        | 10 ++++++-
 redis_commands.c       | 64 ++++++++++++++++++++++++++++++++++++++++++
 redis_commands.h       |  3 ++
 redis_legacy_arginfo.h | 10 ++++++-
 6 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/redis.c b/redis.c
index ff385b67cf..2cea0b8704 100644
--- a/redis.c
+++ b/redis.c
@@ -1695,6 +1695,13 @@ PHP_METHOD(Redis, lastSave)
 }
 /* }}} */
 
+/* {{{ proto bool Redis::failover([array to [,bool abort [,int timeout]]] ) */
+PHP_METHOD(Redis, failover)
+{
+    REDIS_PROCESS_CMD(failover, redis_boolean_response);
+}
+/* }}} */
+
 /* {{{ proto bool Redis::flushDB([bool async]) */
 PHP_METHOD(Redis, flushDB)
 {
diff --git a/redis.stub.php b/redis.stub.php
index b1c5fc7e5f..5245f49046 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -117,6 +117,8 @@ public function expire(string $key, int $timeout): bool;
 
     public function expireAt(string $key, int $timestamp): bool;
 
+    public function failover(?array $to = null, bool $abort = false, int $timeout = 0): bool;
+
     public function flushAll(?bool $sync = null): bool;
 
     public function flushDB(?bool $sync = null): bool;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 4b7c62bfea..e615c6ded5 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8ad4d58327d1b0b07b0dbec026b27ed6a594b8b9 */
+ * Stub hash: 858f814d5b91c0829ae6b6a265a740cc037586dd */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -179,6 +179,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_expireAt, 0, 2, _IS_
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_failover, 0, 0, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, to, IS_ARRAY, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, abort, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sync, _IS_BOOL, 1, "null")
 ZEND_END_ARG_INFO()
@@ -1005,6 +1011,7 @@ ZEND_METHOD(Redis, exec);
 ZEND_METHOD(Redis, exists);
 ZEND_METHOD(Redis, expire);
 ZEND_METHOD(Redis, expireAt);
+ZEND_METHOD(Redis, failover);
 ZEND_METHOD(Redis, flushAll);
 ZEND_METHOD(Redis, flushDB);
 ZEND_METHOD(Redis, geoadd);
@@ -1235,6 +1242,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 14d732fbd1..86ec253943 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -447,6 +447,70 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                   char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    int argc;
+    smart_string cmdstr = {0};
+    zend_bool abort = 0, force = 0;
+    zend_long timeout = 0, port = 0;
+    zend_string *zkey, *host = NULL;
+    zval *z_to = NULL, *z_ele;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!bl",
+                              &z_to, &abort, &timeout) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if (z_to != NULL) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_to), zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "host")) {
+                    host = zval_get_string(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "port")) {
+                    port = zval_get_long(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "force")) {
+                    force = zval_is_true(z_ele);
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+        if (!host || !port) {
+            php_error_docref(NULL, E_WARNING, "host and port must be provided!");
+            if (host) zend_string_release(host);
+            return FAILURE;
+        }
+    }
+
+    argc = (host && port ? 3 + force : 0) + abort + (timeout > 0 ? 2 : 0);
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "FAILOVER");
+
+    if (host && port) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TO");
+        redis_cmd_append_sstr_zstr(&cmdstr, host);
+        redis_cmd_append_sstr_int(&cmdstr, port);
+        if (force) {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FORCE");
+        }
+        zend_string_release(host);
+    }
+
+    if (abort) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ABORT");
+    }
+    if (timeout > 0) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TIMEOUT");
+        redis_cmd_append_sstr_long(&cmdstr, timeout);
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
 {
diff --git a/redis_commands.h b/redis_commands.h
index 94c5dd70ee..45f9bd9c0c 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -133,6 +133,9 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index b275ea646c..858f299fe1 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8ad4d58327d1b0b07b0dbec026b27ed6a594b8b9 */
+ * Stub hash: 858f814d5b91c0829ae6b6a265a740cc037586dd */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -167,6 +167,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expireAt, 0, 0, 2)
 	ZEND_ARG_INFO(0, timestamp)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_failover, 0, 0, 0)
+	ZEND_ARG_INFO(0, to)
+	ZEND_ARG_INFO(0, abort)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, 0)
 	ZEND_ARG_INFO(0, sync)
 ZEND_END_ARG_INFO()
@@ -902,6 +908,7 @@ ZEND_METHOD(Redis, exec);
 ZEND_METHOD(Redis, exists);
 ZEND_METHOD(Redis, expire);
 ZEND_METHOD(Redis, expireAt);
+ZEND_METHOD(Redis, failover);
 ZEND_METHOD(Redis, flushAll);
 ZEND_METHOD(Redis, flushDB);
 ZEND_METHOD(Redis, geoadd);
@@ -1132,6 +1139,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC)

From c40f9d6c0262fa6038308753d31e1e30c081653c Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 29 Apr 2022 16:49:29 +0300
Subject: [PATCH 1572/1986] Fix default host length

---
 redis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index 2cea0b8704..ade8d3b565 100644
--- a/redis.c
+++ b/redis.c
@@ -603,7 +603,7 @@ PHP_METHOD(Redis, __construct)
 
     if (opts != NULL) {
         redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, getThis());
-        redis->sock = redis_sock_create("127.0.0.1", 0, 6379, 0, 0, 0, NULL, 0);
+        redis->sock = redis_sock_create("127.0.0.1", sizeof("127.0.0.1") - 1, 6379, 0, 0, 0, NULL, 0);
 
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, val) {
             if (zkey == NULL) {

From b64d93e1c4a65912ca4d107ddd2220fe5f18bf74 Mon Sep 17 00:00:00 2001
From: Karina Kwiatek <6197148+raccube@users.noreply.github.com>
Date: Thu, 5 May 2022 20:00:44 +0200
Subject: [PATCH 1573/1986] Correct misspelling of libzstd in config args

---
 config.m4 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config.m4 b/config.m4
index 750e58ac64..2ba4a8b51d 100644
--- a/config.m4
+++ b/config.m4
@@ -26,7 +26,7 @@ PHP_ARG_WITH(liblzf, use system liblzf,
 PHP_ARG_ENABLE(redis-zstd, whether to enable Zstd compression,
 [  --enable-redis-zstd     Enable Zstd compression support], no, no)
 
-PHP_ARG_WITH(libzstd, use system libsztd,
+PHP_ARG_WITH(libzstd, use system libzstd,
 [  --with-libzstd[=DIR]      Use system libzstd], yes, no)
 
 PHP_ARG_ENABLE(redis-lz4, whether to enable lz4 compression,

From a5c41ceb0fc10f4b63f5681550d6547cb17c8ef3 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 26 May 2022 15:48:28 +0300
Subject: [PATCH 1574/1986] Fix tests on Redis 7.0

---
 .github/workflows/ci.yml    | 12 ++++++------
 tests/RedisSentinelTest.php |  3 +++
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 625744b6db..fcd2de84f8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -41,24 +41,24 @@ jobs:
         run: |
           redis-cli SHUTDOWN NOSAVE
           for PORT in $(seq 6379 6382) $(seq 32767 32769); do
-            redis-server --port $PORT --daemonize yes --aclfile tests/users.acl
+            redis-server --port $PORT --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels
           done
-          redis-server --port 0 --unixsocket /tmp/redis.sock --daemonize yes --aclfile tests/users.acl
+          redis-server --port 0 --unixsocket /tmp/redis.sock --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels
       - name: Start redis cluster
         run: |
           mkdir -p tests/nodes
           echo -n > tests/nodes/nodemap
-          for PORT in $(seq 7000 7011); do
-            redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl
+          for PORT in $(seq 7000 7006); do
+            redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels
             echo 127.0.0.1:$PORT >> tests/nodes/nodemap
           done
           echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7006) --cluster-replicas 1 --user phpredis -a phpredis
       - name: Start redis sentinel
         run: |
-          wget raw.githubusercontent.com/redis/redis/6.2/sentinel.conf
+          wget raw.githubusercontent.com/redis/redis/7.0/sentinel.conf
           for PORT in $(seq 26379 26380); do
             cp sentinel.conf $PORT.conf
-            sed -i '/^sentinel/d' $PORT.conf
+            sed -i '/^sentinel/Id' $PORT.conf
             redis-server $PORT.conf --port $PORT --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel auth-pass mymaster phpredis
           done
       - name: Run tests
diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php
index 4d941dd95a..e79a9762a3 100644
--- a/tests/RedisSentinelTest.php
+++ b/tests/RedisSentinelTest.php
@@ -111,9 +111,12 @@ public function testSentinels()
     public function testSlaves()
     {
         $result = $this->sentinel->slaves(self::NAME);
+        /**
+         * Comment until fix for https://github.com/redis/redis/issues/10722 released
         $this->assertTrue(is_array($result));
         foreach ($result as $slave) {
             $this->checkFields($slave);
         }
+         */
     }
 }

From e6b3fe548421b0f555c6d9d94dea4c26640ba716 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 27 May 2022 15:20:19 +0300
Subject: [PATCH 1575/1986] Backoff settings in constructor

---
 README.markdown |  5 +++++
 library.c       | 38 ++++++++++++++++++++++++++++++++++++++
 library.h       |  1 +
 redis.c         |  8 ++++++--
 4 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/README.markdown b/README.markdown
index ec7afd5023..5f354763cc 100644
--- a/README.markdown
+++ b/README.markdown
@@ -161,6 +161,11 @@ $redis = new Redis([
     'connectTimeout' => 2.5,
     'auth' => ['phpredis', 'phpredis'],
     'ssl' => ['verify_peer' => false],
+    'backoff' => [
+        'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER,
+        'base' => 500,
+        'cap' => 750,
+    ],
 ]);
 ~~~
 
diff --git a/library.c b/library.c
index 2e428e08e9..6c2b24de43 100644
--- a/library.c
+++ b/library.c
@@ -2589,6 +2589,44 @@ redis_sock_set_stream_context(RedisSock *redis_sock, zval *options)
     return SUCCESS;
 }
 
+PHP_REDIS_API int
+redis_sock_set_backoff(RedisSock *redis_sock, zval *options)
+{
+    zend_string *zkey;
+    zend_long val;
+    zval *z_ele;
+
+    if (!redis_sock || Z_TYPE_P(options) != IS_ARRAY) {
+        return FAILURE;
+    }
+
+    ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), zkey, z_ele) {
+        if (zkey != NULL) {
+            ZVAL_DEREF(z_ele);
+            if (zend_string_equals_literal_ci(zkey, "algorithm")) {
+                if ((val = zval_get_long(z_ele)) < 0 || val >= REDIS_BACKOFF_ALGORITHMS) {
+                    return FAILURE;
+                }
+                redis_sock->backoff.algorithm = val;
+            } else if (zend_string_equals_literal_ci(zkey, "base")) {
+                if ((val = zval_get_long(z_ele)) < 0) {
+                    return FAILURE;
+                }
+                redis_sock->backoff.base = val * 1000;
+            } else if (zend_string_equals_literal_ci(zkey, "cap")) {
+                if ((val = zval_get_long(z_ele)) < 0) {
+                    return FAILURE;
+                }
+                redis_sock->backoff.cap = val * 1000;
+            } else {
+                php_error_docref(NULL, E_WARNING, "Skip unknown backoff option '%s'", ZSTR_VAL(zkey));
+            }
+        }
+    } ZEND_HASH_FOREACH_END();
+
+    return SUCCESS;
+}
+
 /**
  * redis_sock_read_multibulk_reply
  */
diff --git a/library.h b/library.h
index 8c277b99bd..cbf26a73e8 100644
--- a/library.h
+++ b/library.h
@@ -117,6 +117,7 @@ PHP_REDIS_API RedisSock *redis_sock_get(zval *id, int nothrow);
 PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock);
 PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len);
 PHP_REDIS_API int redis_sock_set_stream_context(RedisSock *redis_sock, zval *options);
+PHP_REDIS_API int redis_sock_set_backoff(RedisSock *redis_sock, zval *options);
 
 PHP_REDIS_API int
 redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len);
diff --git a/redis.c b/redis.c
index ade8d3b565..d526b99d7f 100644
--- a/redis.c
+++ b/redis.c
@@ -650,17 +650,21 @@ PHP_METHOD(Redis, __construct)
                 }
                 redis->sock->retry_interval = zval_get_long(val);
             } else if (zend_string_equals_literal_ci(zkey, "ssl")) {
-                if (Z_TYPE_P(val) != IS_ARRAY) {
+                if (redis_sock_set_stream_context(redis->sock, val) != SUCCESS) {
                     REDIS_VALUE_EXCEPTION("Invalid SSL context options");
                     RETURN_THROWS();
                 }
-                redis_sock_set_stream_context(redis->sock, val);
             } else if (zend_string_equals_literal_ci(zkey, "auth")) {
                 if (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY) {
                     REDIS_VALUE_EXCEPTION("Invalid auth credentials");
                     RETURN_THROWS();
                 }
                 redis_sock_set_auth_zval(redis->sock, val);
+            } else if (zend_string_equals_literal_ci(zkey, "backoff")) {
+                if (redis_sock_set_backoff(redis->sock, val) != SUCCESS) {
+                    REDIS_VALUE_EXCEPTION("Invalid backoff options");
+                    RETURN_THROWS();
+                }
             } else {
                 php_error_docref(NULL, E_WARNING, "Skip unknown option '%s'", ZSTR_VAL(zkey));
             }

From df97cc353191a83ebd2ecc092990043f007b9600 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 6 Jun 2022 21:55:05 +0300
Subject: [PATCH 1576/1986] Issue #1894

Add the COUNT argument to LPOP and RPOP
---
 cluster_library.c              | 12 ++++++++++++
 cluster_library.h              |  2 ++
 library.c                      | 12 ++++++++++++
 library.h                      |  1 +
 redis.c                        |  8 ++++----
 redis.stub.php                 |  6 ++----
 redis_arginfo.h                |  9 ++++++---
 redis_cluster.c                |  8 ++++----
 redis_cluster.stub.php         |  4 ++--
 redis_cluster_arginfo.h        |  7 ++++---
 redis_cluster_legacy_arginfo.h | 14 +++++++-------
 redis_commands.c               | 29 +++++++++++++++++++++++++++++
 redis_commands.h               |  3 +++
 redis_legacy_arginfo.h         | 16 ++++++++--------
 tests/RedisTest.php            | 18 +++++++++++++-----
 15 files changed, 109 insertions(+), 40 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 8ebc87604d..976cb88963 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -1754,6 +1754,18 @@ PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
     CLUSTER_RETURN_BOOL(c, 1);
 }
 
+PHP_REDIS_API void
+cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
+{
+    if (ctx == NULL) {
+        cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
+    }
+}
+
 PHP_REDIS_API void
 cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
 {
diff --git a/cluster_library.h b/cluster_library.h
index 811c435dcc..995e244fc8 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -413,6 +413,8 @@ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
     void *ctx);
 PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
+PHP_REDIS_API void cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+    void *ctx);
 PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
 PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
diff --git a/library.c b/library.c
index 6c2b24de43..1bf3c3b48a 100644
--- a/library.c
+++ b/library.c
@@ -1337,6 +1337,18 @@ redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, z
     }
 }
 
+PHP_REDIS_API int
+redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    if (ctx == NULL) {
+        return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
+    }
+}
+
 PHP_REDIS_API int
 redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                             zval *z_tab, void *ctx,
diff --git a/library.h b/library.h
index cbf26a73e8..e00e693905 100644
--- a/library.h
+++ b/library.h
@@ -167,6 +167,7 @@ PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
 PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 /* Helper methods to get configuration values from a HashTable. */
 
diff --git a/redis.c b/redis.c
index d526b99d7f..bfa72b88ce 100644
--- a/redis.c
+++ b/redis.c
@@ -1280,17 +1280,17 @@ PHP_METHOD(Redis, rPushx)
 }
 /* }}} */
 
-/* {{{ proto string Redis::lPOP(string key) */
+/* {{{ proto string Redis::lPop(string key, [int count = 0]) */
 PHP_METHOD(Redis, lPop)
 {
-    REDIS_PROCESS_KW_CMD("LPOP", redis_key_cmd, redis_string_response);
+    REDIS_PROCESS_KW_CMD("LPOP", redis_pop_cmd, redis_pop_response);
 }
 /* }}} */
 
-/* {{{ proto string Redis::rPOP(string key) */
+/* {{{ proto string Redis::rPop(string key, [int count = 0]) */
 PHP_METHOD(Redis, rPop)
 {
-    REDIS_PROCESS_KW_CMD("RPOP", redis_key_cmd, redis_string_response);
+    REDIS_PROCESS_KW_CMD("RPOP", redis_pop_cmd, redis_pop_response);
 }
 /* }}} */
 
diff --git a/redis.stub.php b/redis.stub.php
index 5245f49046..f0f8a37a14 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -238,8 +238,7 @@ public function lLen(string $key): int;
 
     public function lMove(string $src, string $dst, string $wherefrom, string $whereto): string;
 
-	/** @return string|Redis */
-    public function lPop(string $key);
+    public function lPop(string $key, int $count = 0): bool|string|array;
 
     /**
      * @param mixed $elements
@@ -330,8 +329,7 @@ public function pubsub(string $command, mixed $arg = null): mixed;
 
     public function punsubscribe(array $patterns): array;
 
-	/** @return string|Redis */
-    public function rPop(string $key);
+    public function rPop(string $key, int $count = 0): bool|string|array;
 
 	/** @return string|Redis */
     public function randomKey();
diff --git a/redis_arginfo.h b/redis_arginfo.h
index e615c6ded5..ee479548e1 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 858f814d5b91c0829ae6b6a265a740cc037586dd */
+ * Stub hash: 9671c30926e8d581a126833360b123c8ae2dd913 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -411,7 +411,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lMove, 0, 4, IS_STRI
 	ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_lPop arginfo_class_Redis_decr
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_lPop, 0, 1, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -558,7 +561,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_punsubscribe, 0, 1,
 	ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_rPop arginfo_class_Redis_decr
+#define arginfo_class_Redis_rPop arginfo_class_Redis_lPop
 
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___destruct
 
diff --git a/redis_cluster.c b/redis_cluster.c
index 140f51bc89..5634560f8b 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -820,15 +820,15 @@ PHP_METHOD(RedisCluster, type) {
 }
 /* }}} */
 
-/* {{{ proto string RedisCluster::pop(string key) */
+/* {{{ proto string RedisCluster::pop(string key, [int count = 0]) */
 PHP_METHOD(RedisCluster, lpop) {
-    CLUSTER_PROCESS_KW_CMD("LPOP", redis_key_cmd, cluster_bulk_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("LPOP", redis_pop_cmd, cluster_pop_resp, 0);
 }
 /* }}} */
 
-/* {{{ proto string RedisCluster::rpop(string key) */
+/* {{{ proto string RedisCluster::rpop(string key, [int count = 0]) */
 PHP_METHOD(RedisCluster, rpop) {
-    CLUSTER_PROCESS_KW_CMD("RPOP", redis_key_cmd, cluster_bulk_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("RPOP", redis_pop_cmd, cluster_pop_resp, 0);
 }
 /* }}} */
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index c48c419207..2fe512fd99 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -175,7 +175,7 @@ public function linsert(string $key, string $pos, mixed $pivot, mixed $value): i
 
     public function llen(string $key): int|bool;
 
-    public function lpop(string $key): string|bool;
+    public function lpop(string $key, int $count = 0): bool|string|array;
 
     public function lpush(string $key, mixed $value, mixed ...$other_values): int|bool;
 
@@ -237,7 +237,7 @@ public function restore(string $key, int $timeout, string $value): bool;
 
     public function role(string|array $key_or_address): mixed;
 
-    public function rpop(string $key): bool|string;
+    public function rpop(string $key, int $count = 0): bool|string|array;
 
     public function rpoplpush(string $src, string $dst): bool|string;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index eda09a4501..95945fc39a 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7f045c90abd99a53f8fb6557942cd17e00ee8a01 */
+ * Stub hash: 8029a0d6df2bbd9cf5d140ff8d9efcc4de2a5bcc */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -367,8 +367,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_llen, 0, 1, M
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpop, 0, 1, MAY_BE_STRING|MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpop, 0, 1, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, MAY_BE_LONG|MAY_BE_BOOL)
@@ -502,7 +503,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_role, 0, 1, I
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster__prefix
+#define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster_lpop
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_rpoplpush, 0, 2, MAY_BE_BOOL|MAY_BE_STRING)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 38a808967a..bc07b11780 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7f045c90abd99a53f8fb6557942cd17e00ee8a01 */
+ * Stub hash: 8029a0d6df2bbd9cf5d140ff8d9efcc4de2a5bcc */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -320,7 +320,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_llen arginfo_class_RedisCluster__prefix
 
-#define arginfo_class_RedisCluster_lpop arginfo_class_RedisCluster__prefix
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpop, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpush, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
@@ -433,7 +436,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_role arginfo_class_RedisCluster_bgrewriteaof
 
-#define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster__prefix
+#define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster_lpop
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rpoplpush, 0, 0, 2)
 	ZEND_ARG_INFO(0, src)
@@ -525,10 +528,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_srandmember, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, count)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster_srandmember arginfo_class_RedisCluster_lpop
 
 #define arginfo_class_RedisCluster_srem arginfo_class_RedisCluster_lpush
 
diff --git a/redis_commands.c b/redis_commands.c
index 86ec253943..3b3fa917e9 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1601,6 +1601,35 @@ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
  * have specific processing (argument validation, etc) that make them unique
  */
 
+int
+redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+              char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    char *key;
+    size_t key_len;
+    smart_string cmdstr = {0};
+    zend_long count = 0;
+
+    // Make sure the function is being called correctly
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l",
+                              &key, &key_len, &count) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    redis_cmd_init_sstr(&cmdstr, 1 + (count > 0), kw, strlen(kw));
+    redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot);
+    if (count > 0) {
+        redis_cmd_append_sstr_long(&cmdstr, (long)count);
+        *ctx = PHPREDIS_CTX_PTR;
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 /* Attempt to pull a long expiry from a zval.  We're more restrictave than zval_get_long
  * because that function will return integers from things like open file descriptors
  * which should simply fail as a TTL */
diff --git a/redis_commands.h b/redis_commands.h
index 45f9bd9c0c..dc88848e6a 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -84,6 +84,9 @@ int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 858f299fe1..85cc8be169 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 858f814d5b91c0829ae6b6a265a740cc037586dd */
+ * Stub hash: 9671c30926e8d581a126833360b123c8ae2dd913 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -366,7 +366,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lMove, 0, 0, 4)
 	ZEND_ARG_INFO(0, whereto)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_lPop arginfo_class_Redis__prefix
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPop, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
@@ -487,7 +490,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_punsubscribe arginfo_class_Redis_psubscribe
 
-#define arginfo_class_Redis_rPop arginfo_class_Redis__prefix
+#define arginfo_class_Redis_rPop arginfo_class_Redis_lPop
 
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___destruct
 
@@ -551,12 +554,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sMove, 0, 0, 3)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sPop, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, count)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_sPop arginfo_class_Redis_lPop
 
-#define arginfo_class_Redis_sRandMember arginfo_class_Redis_sPop
+#define arginfo_class_Redis_sRandMember arginfo_class_Redis_lPop
 
 #define arginfo_class_Redis_sUnion arginfo_class_Redis_del
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index d480e7ec2b..43a0cb1e7d 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -878,11 +878,16 @@ public function testlPop()
         $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'));
+        if (version_compare($this->version, "6.2.0") < 0) {
+            $this->assertEquals('val', $this->redis->lPop('list'));
+            $this->assertEquals('val3', $this->redis->lPop('list'));
+        } else {
+            $this->assertEquals(['val', 'val3'], $this->redis->lPop('list', 2));
+        }
         $this->assertEquals(FALSE, $this->redis->lPop('list'));
 
     // testing binary data
@@ -895,7 +900,6 @@ public function testlPop()
     $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
@@ -913,8 +917,12 @@ public function testrPop()
     // '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'));
+        if (version_compare($this->version, "6.2.0") < 0) {
+            $this->assertEquals('val', $this->redis->rPop('list'));
+            $this->assertEquals('val3', $this->redis->rPop('list'));
+        } else {
+            $this->assertEquals(['val', 'val3'], $this->redis->rPop('list', 2));
+        }
         $this->assertEquals(FALSE, $this->redis->rPop('list'));
 
     // testing binary data

From 68136a297287afff98417f62ef6bfe1449d03c49 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 18 Jun 2022 19:46:28 +0300
Subject: [PATCH 1577/1986] Refactor redis_parse_client_list_response

---
 library.c | 117 +++++++++++++++---------------------------------------
 1 file changed, 33 insertions(+), 84 deletions(-)

diff --git a/library.c b/library.c
index 1bf3c3b48a..7e4e3120c4 100644
--- a/library.c
+++ b/library.c
@@ -1193,95 +1193,44 @@ redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zva
 PHP_REDIS_API void
 redis_parse_client_list_response(char *response, zval *z_ret)
 {
-    char *p, *lpos, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
-    int klen = 0, done = 0, is_numeric;
-    zval z_sub_result;
+    char *p1, *s1 = NULL;
 
-    /* Allocate for response and our user */
-    array_init(z_ret);
-    array_init(&z_sub_result);
-
-    // Pointers for parsing
-    p = response;
-    lpos = response;
-
-    /* While we've got more to parse */
-    while(!done) {
-        /* What character are we on */
-        switch(*p) {
-            /* We're done */
-            case '\0':
-                done = 1;
-                break;
-            /* \n, ' ' mean we can pull a k/v pair */
-            case '\n':
-            case ' ':
-                /* Grab our value */
-                vpos = lpos;
-
-                /* There is some communication error or Redis bug if we don't
-                   have a key and value, but check anyway. */
-                if(kpos && vpos) {
-                    /* Allocate, copy in our key */
-                    key = estrndup(kpos, klen);
-
-                    /* Allocate, copy in our value */
-                    value = estrndup(lpos, p - lpos);
-
-                    /* Treat numbers as numbers, strings as strings */
-                    is_numeric = 1;
-                    for(p2 = value; *p2; ++p2) {
-                        if(*p2 < '0' || *p2 > '9') {
-                            is_numeric = 0;
+    if ((p1 = strtok_r(response, _NL, &s1)) != NULL) {
+        array_init(z_ret);
+        do {
+            char *p2, *s2 = NULL;
+            zval z_sub;
+
+            if ((p2 = strtok_r(p1, " ", &s2)) != NULL) {
+                array_init(&z_sub);
+                do {
+                    char *p;
+                    zend_uchar type;
+                    zend_long lval;
+                    double dval;
+                    if ((p = strchr(p2, '=')) != NULL) {
+                        type = is_numeric_string(p + 1, s2 - p - 1, &lval, &dval, 0);
+                        switch (type) {
+                        case IS_LONG:
+                            add_assoc_long_ex(&z_sub, p2, p - p2, lval);
+                            break;
+                        case IS_DOUBLE:
+                            add_assoc_double_ex(&z_sub, p2, p - p2, dval);
                             break;
+                        default:
+                            add_assoc_stringl_ex(&z_sub, p2, p - p2, p + 1, s2 - p - 1);
                         }
-                    }
-
-                    /* Add as a long or string, depending */
-                    if(is_numeric == 1) {
-                        add_assoc_long(&z_sub_result, key, atol(value));
                     } else {
-                        add_assoc_string(&z_sub_result, key, value);
-                    }
-                    efree(value);
-                    // If we hit a '\n', then we can add this user to our list
-                    if(*p == '\n') {
-                        /* Add our user */
-                        add_next_index_zval(z_ret, &z_sub_result);
-
-                        /* If we have another user, make another one */
-                        if(*(p+1) != '\0') {
-                            array_init(&z_sub_result);
-                        }
+                        add_next_index_string(&z_sub, p2);
                     }
-
-                    // Free our key
-                    efree(key);
-                } else {
-                    // Something is wrong
-                    zval_dtor(z_ret);
-                    ZVAL_BOOL(z_ret, 0);
-                    return;
-                }
-
-                /* Move forward */
-                lpos = p + 1;
-
-                break;
-            /* We can pull the key and null terminate at our sep */
-            case '=':
-                /* Key, key length */
-                kpos = lpos;
-                klen = p - lpos;
-
-                /* Move forward */
-                lpos = p + 1;
-
-                break;
-        }
-
-        /* Increment */
-        p++;
+                } while ((p2 = strtok_r(NULL, " ", &s2)) != NULL);
+            } else {
+                ZVAL_FALSE(&z_sub);
+            }
+            add_next_index_zval(z_ret, &z_sub);
+        } while ((p1 = strtok_r(NULL, _NL, &s1)) != NULL);
+    } else {
+        ZVAL_FALSE(z_ret);
     }
 }
 

From e24b0843b3e5a6f76dd6ba97669e92b7e6ce1dec Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 18 Jun 2022 19:54:31 +0300
Subject: [PATCH 1578/1986] Fix tests

---
 library.c                   | 2 +-
 tests/RedisSentinelTest.php | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/library.c b/library.c
index 7e4e3120c4..121c745440 100644
--- a/library.c
+++ b/library.c
@@ -1218,7 +1218,7 @@ redis_parse_client_list_response(char *response, zval *z_ret)
                             add_assoc_double_ex(&z_sub, p2, p - p2, dval);
                             break;
                         default:
-                            add_assoc_stringl_ex(&z_sub, p2, p - p2, p + 1, s2 - p - 1);
+                            add_assoc_stringl_ex(&z_sub, p2, p - p2, p + 1, s2 - p - 2);
                         }
                     } else {
                         add_next_index_string(&z_sub, p2);
diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php
index e79a9762a3..4d941dd95a 100644
--- a/tests/RedisSentinelTest.php
+++ b/tests/RedisSentinelTest.php
@@ -111,12 +111,9 @@ public function testSentinels()
     public function testSlaves()
     {
         $result = $this->sentinel->slaves(self::NAME);
-        /**
-         * Comment until fix for https://github.com/redis/redis/issues/10722 released
         $this->assertTrue(is_array($result));
         foreach ($result as $slave) {
             $this->checkFields($slave);
         }
-         */
     }
 }

From aaa4c91a7640d2fc0f3fea203dcc64d42a17b2cf Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 22 Jun 2022 21:42:09 +0300
Subject: [PATCH 1579/1986] Replace strtok_r with php_strtok_r

---
 library.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library.c b/library.c
index 121c745440..9c51680c77 100644
--- a/library.c
+++ b/library.c
@@ -1195,13 +1195,13 @@ redis_parse_client_list_response(char *response, zval *z_ret)
 {
     char *p1, *s1 = NULL;
 
-    if ((p1 = strtok_r(response, _NL, &s1)) != NULL) {
+    if ((p1 = php_strtok_r(response, _NL, &s1)) != NULL) {
         array_init(z_ret);
         do {
             char *p2, *s2 = NULL;
             zval z_sub;
 
-            if ((p2 = strtok_r(p1, " ", &s2)) != NULL) {
+            if ((p2 = php_strtok_r(p1, " ", &s2)) != NULL) {
                 array_init(&z_sub);
                 do {
                     char *p;

From 1fb2935b1ec6c1bcc1692cd4bdf71be6315ae977 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 28 Jun 2022 09:56:30 +0300
Subject: [PATCH 1580/1986] Replace strtok_r with php_strtok_r #2

---
 library.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library.c b/library.c
index 9c51680c77..f23c49489a 100644
--- a/library.c
+++ b/library.c
@@ -1223,12 +1223,12 @@ redis_parse_client_list_response(char *response, zval *z_ret)
                     } else {
                         add_next_index_string(&z_sub, p2);
                     }
-                } while ((p2 = strtok_r(NULL, " ", &s2)) != NULL);
+                } while ((p2 = php_strtok_r(NULL, " ", &s2)) != NULL);
             } else {
                 ZVAL_FALSE(&z_sub);
             }
             add_next_index_zval(z_ret, &z_sub);
-        } while ((p1 = strtok_r(NULL, _NL, &s1)) != NULL);
+        } while ((p1 = php_strtok_r(NULL, _NL, &s1)) != NULL);
     } else {
         ZVAL_FALSE(z_ret);
     }

From 3c9e159c7e700a48aff29b83374039c3bdf1e909 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 26 Jun 2022 13:10:56 +0300
Subject: [PATCH 1581/1986] Refactor subscribe/unsubscribe

---
 cluster_library.c      |  10 ++-
 common.h               |   2 +-
 library.c              | 134 +++++++++++++++++++++++++++++------------
 redis.stub.php         |   8 +--
 redis_arginfo.h        |  17 +++---
 redis_commands.c       |   2 +-
 redis_commands.h       |   8 ++-
 redis_legacy_arginfo.h |  19 +++---
 8 files changed, 135 insertions(+), 65 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 976cb88963..2512f0fd60 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -1857,8 +1857,8 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
 
     // Set up our callback pointers
     zval z_ret, z_args[4];
-    sctx->cb.retval = &z_ret;
-    sctx->cb.params = z_args;
+    sctx->cb.fci.retval = &z_ret;
+    sctx->cb.fci.params = z_args;
 
     /* We're in a subscribe loop */
     c->subscribed_slot = c->cmd_slot;
@@ -1911,12 +1911,10 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
         }
 
         // Set arg count
-        sctx->cb.param_count = tab_idx;
+        sctx->cb.fci.param_count = tab_idx;
 
         // Execute our callback
-        if (zend_call_function(&(sctx->cb), &(sctx->cb_cache)) !=
-                              SUCCESS)
-        {
+        if (zend_call_function(&sctx->cb.fci, &sctx->cb.fci_cache) != SUCCESS) {
             break;
         }
 
diff --git a/common.h b/common.h
index 8b1a01274e..0ae19a2d2c 100644
--- a/common.h
+++ b/common.h
@@ -289,7 +289,7 @@ typedef struct {
     int                 persistent;
     int                 watching;
     zend_string         *persistent_id;
-
+    HashTable           *subs;
     redis_serializer    serializer;
     int                 compression;
     int                 compression_level;
diff --git a/library.c b/library.c
index 9c51680c77..1038ae8b68 100644
--- a/library.c
+++ b/library.c
@@ -438,58 +438,87 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 }
 
+static void
+ht_free_subs(zval *data)
+{
+    efree(Z_PTR_P(data));
+}
+
 PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
                                     RedisSock *redis_sock, zval *z_tab,
                                     void *ctx)
 {
+    HashTable *subs;
+    subscribeCallback *cb;
     subscribeContext *sctx = (subscribeContext*)ctx;
     zval *z_tmp, z_resp;
 
+    ALLOC_HASHTABLE(subs);
+    zend_hash_init(subs, 0, NULL, ht_free_subs, 0);
     // Consume response(s) from subscribe, which will vary on argc
     while(sctx->argc--) {
+        ZVAL_NULL(&z_resp);
         if (!redis_sock_read_multibulk_reply_zval(
             INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)
         ) {
-            efree(sctx);
-            return -1;
+            goto error;
         }
 
         // We'll need to find the command response
         if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_resp), 0)) == NULL) {
-            zval_dtor(&z_resp);
-            efree(sctx);
-            return -1;
+            goto error;
         }
 
         // Make sure the command response matches the command we called
         if(strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) !=0) {
-            zval_dtor(&z_resp);
-            efree(sctx);
-            return -1;
+            goto error;
         }
 
+        if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL) {
+            goto error;
+        }
+
+        zend_hash_str_update_mem(subs, Z_STRVAL_P(z_tmp), Z_STRLEN_P(z_tmp),
+                                    &sctx->cb, sizeof(sctx->cb));
+
         zval_dtor(&z_resp);
     }
 
-    zval z_ret, z_args[4];
-    sctx->cb.retval = &z_ret;
-    sctx->cb.params = z_args;
+    efree(sctx);
+
+    if (redis_sock->subs) {
+        zend_string *zkey;
+
+        ZEND_HASH_FOREACH_STR_KEY_PTR(subs, zkey, cb) {
+            zend_hash_update_mem(redis_sock->subs, zkey, cb, sizeof(*cb));
+        } ZEND_HASH_FOREACH_END();
+        zend_hash_destroy(subs);
+        efree(subs);
+
+        RETVAL_TRUE;
+        return SUCCESS;
+    }
 
+    redis_sock->subs = subs;
     /* Multibulk response, {[pattern], type, channel, payload } */
-    while(1) {
-        zval *z_type, *z_chan, *z_pat = NULL, *z_data;
+    while (redis_sock->subs) {
+        zval z_ret, z_args[4], *z_type, *z_chan, *z_pat = NULL, *z_data;
         HashTable *ht_tab;
-        int tab_idx=1, is_pmsg;
+        int tab_idx = 1, is_pmsg = 0;
 
+        ZVAL_NULL(&z_resp);
         if (!redis_sock_read_multibulk_reply_zval(
-            INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)) break;
+            INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)
+        ) {
+            goto failure;
+        }
 
         ht_tab = Z_ARRVAL(z_resp);
 
         if ((z_type = zend_hash_index_find(ht_tab, 0)) == NULL ||
            Z_TYPE_P(z_type) != IS_STRING
         ) {
-            break;
+            goto failure;
         }
 
         // Check for message or pmessage
@@ -498,13 +527,14 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
         {
             is_pmsg = *Z_STRVAL_P(z_type)=='p';
         } else {
-            break;
+            zval_dtor(&z_resp);
+            continue;
         }
 
         // Extract pattern if it's a pmessage
         if(is_pmsg) {
             if ((z_pat = zend_hash_index_find(ht_tab, tab_idx++)) == NULL) {
-                break;
+                goto failure;
             }
         }
 
@@ -512,7 +542,11 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
         if ((z_chan = zend_hash_index_find(ht_tab, tab_idx++)) == NULL ||
             (z_data = zend_hash_index_find(ht_tab, tab_idx++)) == NULL
         ) {
-            break;
+            goto failure;
+        }
+
+        if ((cb = zend_hash_str_find_ptr(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) {
+            goto failure;
         }
 
         // Different args for SUBSCRIBE and PSUBSCRIBE
@@ -527,13 +561,13 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
         }
 
         // Set arg count
-        sctx->cb.param_count = tab_idx;
+        cb->fci.param_count = tab_idx;
+        cb->fci.retval = &z_ret;
+        cb->fci.params = z_args;
 
         // Execute callback
-        if(zend_call_function(&(sctx->cb), &(sctx->cb_cache))
-                              ==FAILURE)
-        {
-            break;
+        if (zend_call_function(&cb->fci, &cb->fci_cache) != SUCCESS) {
+            goto failure;
         }
 
         // If we have a return value free it
@@ -541,11 +575,18 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
         zval_dtor(&z_resp);
     }
 
+    RETVAL_TRUE;
+    return SUCCESS;
+
     // This is an error state, clean up
-    zval_dtor(&z_resp);
+error:
     efree(sctx);
-
-    return -1;
+    zend_hash_destroy(subs);
+    efree(subs);
+failure:
+    zval_dtor(&z_resp);
+    RETVAL_FALSE;
+    return FAILURE;
 }
 
 PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
@@ -553,31 +594,45 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
                                       void *ctx)
 {
     subscribeContext *sctx = (subscribeContext*)ctx;
-    zval *z_chan, zv, *z_ret = &zv, z_resp;
-    int i;
+    zval *z_chan, z_ret, z_resp;
 
-    array_init(z_ret);
+    array_init(&z_ret);
 
-    for (i = 0; i < sctx->argc; i++) {
+    while (sctx->argc--) {
+        ZVAL_NULL(&z_resp);
         if (!redis_sock_read_multibulk_reply_zval(
             INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp) ||
             (z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL
         ) {
-            zval_dtor(z_ret);
-            return -1;
+            efree(sctx);
+            zval_dtor(&z_resp);
+            zval_dtor(&z_ret);
+            RETVAL_FALSE;
+            return FAILURE;
         }
 
-        add_assoc_bool(z_ret, Z_STRVAL_P(z_chan), 1);
+        if (!redis_sock->subs ||
+            !zend_hash_str_exists(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))
+        ) {
+            add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 0);
+        } else {
+            zend_hash_str_del(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan));
+            add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 1);
+        }
 
         zval_dtor(&z_resp);
     }
 
     efree(sctx);
 
-    RETVAL_ZVAL(z_ret, 0, 1);
+    if (redis_sock->subs && !zend_hash_num_elements(redis_sock->subs)) {
+        zend_hash_destroy(redis_sock->subs);
+        efree(redis_sock->subs);
+        redis_sock->subs = NULL;
+    }
 
-    // Success
-    return 0;
+    RETVAL_ZVAL(&z_ret, 0, 1);
+    return SUCCESS;
 }
 
 PHP_REDIS_API zval *
@@ -2870,6 +2925,11 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
     if (redis_sock->host) {
         zend_string_release(redis_sock->host);
     }
+    if (redis_sock->subs) {
+        zend_hash_destroy(redis_sock->subs);
+        efree(redis_sock->subs);
+        redis_sock->subs = NULL;
+    }
     redis_sock_free_auth(redis_sock);
     free_reply_callbacks(redis_sock);
     efree(redis_sock);
diff --git a/redis.stub.php b/redis.stub.php
index f0f8a37a14..93dd273062 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -319,7 +319,7 @@ public function popen(string $host, int $port = 6379, float $timeout = 0, string
     /** @return bool|Redis */
     public function psetex(string $key, int $expire, mixed $value);
 
-    public function psubscribe(array $patterns): void;
+    public function psubscribe(array $patterns, callable $cb): bool;
 
     public function pttl(string $key): int;
 
@@ -327,7 +327,7 @@ public function publish(string $channel, string $message): int;
 
     public function pubsub(string $command, mixed $arg = null): mixed;
 
-    public function punsubscribe(array $patterns): array;
+    public function punsubscribe(array $patterns): bool|array;
 
     public function rPop(string $key, int $count = 0): bool|string|array;
 
@@ -439,7 +439,7 @@ public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int
 	/** @return int|Redis */
     public function strlen(string $key);
 
-    public function subscribe(string $channel, string ...$other_channels): array;
+    public function subscribe(array $channels, callable $cb): bool;
 
     public function swapdb(string $src, string $dst): bool;
 
@@ -455,7 +455,7 @@ public function type(string $key);
      */
     public function unlink(array|string $key, string ...$other_keys);
 
-    public function unsubscribe(string $channel, string ...$other_channels): array;
+    public function unsubscribe(array $channels): bool|array;
 
 	/** @return bool|Redis */
     public function unwatch();
diff --git a/redis_arginfo.h b/redis_arginfo.h
index ee479548e1..ea660631d2 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 9671c30926e8d581a126833360b123c8ae2dd913 */
+ * Stub hash: efcda1ed028d65d0b4848d32133dc0e32f17871f */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -541,8 +541,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psetex, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_psubscribe, 0, 1, IS_VOID, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_psubscribe, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_pttl arginfo_class_Redis_hLen
@@ -557,7 +558,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pubsub, 0, 1, IS_MIX
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_MIXED, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_punsubscribe, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_punsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
@@ -732,9 +733,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_strlen arginfo_class_Redis_decr
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 1, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_channels, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_swapdb, 0, 2, _IS_BOOL, 0)
@@ -750,7 +751,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
-#define arginfo_class_Redis_unsubscribe arginfo_class_Redis_subscribe
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_unsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct
 
diff --git a/redis_commands.c b/redis_commands.c
index 3b3fa917e9..aa7675af16 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1101,7 +1101,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *key;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &z_arr,
-                             &(sctx->cb), &(sctx->cb_cache)) == FAILURE)
+                             &sctx->cb.fci, &sctx->cb.fci_cache) == FAILURE)
     {
         efree(sctx);
         return FAILURE;
diff --git a/redis_commands.h b/redis_commands.h
index dc88848e6a..e766e20d60 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -14,11 +14,15 @@
     if (slot) *slot = cluster_hash_key(key,key_len);
 
 /* Simple container so we can push subscribe context out */
+typedef struct {
+    zend_fcall_info fci;
+    zend_fcall_info_cache fci_cache;
+} subscribeCallback;
+
 typedef struct subscribeContext {
     char *kw;
     int argc;
-    zend_fcall_info cb;
-    zend_fcall_info_cache cb_cache;
+    subscribeCallback cb;
 } subscribeContext;
 
 /* Construct a raw command */
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 85cc8be169..622d61cbdd 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 9671c30926e8d581a126833360b123c8ae2dd913 */
+ * Stub hash: efcda1ed028d65d0b4848d32133dc0e32f17871f */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -472,8 +472,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psetex, 0, 0, 3)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psubscribe, 0, 0, 1)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psubscribe, 0, 0, 2)
 	ZEND_ARG_INFO(0, patterns)
+	ZEND_ARG_INFO(0, cb)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_pttl arginfo_class_Redis__prefix
@@ -488,7 +489,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pubsub, 0, 0, 1)
 	ZEND_ARG_INFO(0, arg)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_punsubscribe arginfo_class_Redis_psubscribe
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_punsubscribe, 0, 0, 1)
+	ZEND_ARG_INFO(0, patterns)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_rPop arginfo_class_Redis_lPop
 
@@ -640,9 +643,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_strlen arginfo_class_Redis__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_subscribe, 0, 0, 1)
-	ZEND_ARG_INFO(0, channel)
-	ZEND_ARG_VARIADIC_INFO(0, other_channels)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_subscribe, 0, 0, 2)
+	ZEND_ARG_INFO(0, channels)
+	ZEND_ARG_INFO(0, cb)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_swapdb arginfo_class_Redis_rpoplpush
@@ -655,7 +658,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
-#define arginfo_class_Redis_unsubscribe arginfo_class_Redis_subscribe
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_unsubscribe, 0, 0, 1)
+	ZEND_ARG_INFO(0, channels)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct
 

From cf2c052c7b5da35abd6935a99cc27aac0f0c3606 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 28 Jun 2022 23:11:37 +0300
Subject: [PATCH 1582/1986] Fix invalid allocation size

---
 library.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/library.c b/library.c
index f23c49489a..f793e1e258 100644
--- a/library.c
+++ b/library.c
@@ -1195,12 +1195,14 @@ redis_parse_client_list_response(char *response, zval *z_ret)
 {
     char *p1, *s1 = NULL;
 
+    ZVAL_FALSE(z_ret);
     if ((p1 = php_strtok_r(response, _NL, &s1)) != NULL) {
         array_init(z_ret);
         do {
             char *p2, *s2 = NULL;
             zval z_sub;
 
+            ZVAL_FALSE(&z_sub);
             if ((p2 = php_strtok_r(p1, " ", &s2)) != NULL) {
                 array_init(&z_sub);
                 do {
@@ -1218,19 +1220,15 @@ redis_parse_client_list_response(char *response, zval *z_ret)
                             add_assoc_double_ex(&z_sub, p2, p - p2, dval);
                             break;
                         default:
-                            add_assoc_stringl_ex(&z_sub, p2, p - p2, p + 1, s2 - p - 2);
+                            add_assoc_string_ex(&z_sub, p2, p - p2, p + 1);
                         }
                     } else {
                         add_next_index_string(&z_sub, p2);
                     }
                 } while ((p2 = php_strtok_r(NULL, " ", &s2)) != NULL);
-            } else {
-                ZVAL_FALSE(&z_sub);
             }
             add_next_index_zval(z_ret, &z_sub);
         } while ((p1 = php_strtok_r(NULL, _NL, &s1)) != NULL);
-    } else {
-        ZVAL_FALSE(z_ret);
     }
 }
 

From de3635dad515279e376d395671f14a5f6e729d61 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 29 Jun 2022 17:31:03 +0300
Subject: [PATCH 1583/1986] Change PHPREDIS_CTX_PTR type

---
 common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common.h b/common.h
index 8b1a01274e..ae16b0dc53 100644
--- a/common.h
+++ b/common.h
@@ -4,7 +4,7 @@
 #ifndef REDIS_COMMON_H
 #define REDIS_COMMON_H
 
-#define PHPREDIS_CTX_PTR ((void *)0xDEADC0DE)
+#define PHPREDIS_CTX_PTR ((char *)0xDEADC0DE)
 #define PHPREDIS_NOTUSED(v) ((void)v)
 
 #include "zend_llist.h"

From 9ff5c33a179909ff75003517c8066b8c28c428e7 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 29 Jun 2022 20:03:40 +0300
Subject: [PATCH 1584/1986] Install redis from official repository

---
 .github/workflows/ci.yml | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fcd2de84f8..5f98241a8c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,10 +26,9 @@ jobs:
           tools: none
       - name: Install dependencies
         run: |
-          sudo add-apt-repository ppa:redislabs/redis
-          sudo add-apt-repository ppa:ondrej/php
+          curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
+          echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
           sudo apt-get update
-          sudo apt --fix-broken install
           sudo apt-get install redis valgrind libzstd-dev liblz4-dev
       - name: Build phpredis
         run: |
@@ -48,11 +47,11 @@ jobs:
         run: |
           mkdir -p tests/nodes
           echo -n > tests/nodes/nodemap
-          for PORT in $(seq 7000 7006); do
+          for PORT in $(seq 7000 7005); do
             redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels
             echo 127.0.0.1:$PORT >> tests/nodes/nodemap
           done
-          echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7006) --cluster-replicas 1 --user phpredis -a phpredis
+          echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7005) --cluster-replicas 1 --user phpredis -a phpredis
       - name: Start redis sentinel
         run: |
           wget raw.githubusercontent.com/redis/redis/7.0/sentinel.conf

From 9d61504e752da668301effaddfb75112f9b8cba4 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 28 Jun 2022 10:13:46 +0300
Subject: [PATCH 1585/1986] GitHub Actions

Test builds on macos and windows
---
 .github/workflows/ci.yml | 74 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 71 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5f98241a8c..51cb7651d5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,7 +1,7 @@
 on: [push, pull_request]
 
 jobs:
-  build:
+  ubuntu:
     runs-on: ubuntu-latest
     continue-on-error: ${{ matrix.experimental }}
     strategy:
@@ -14,7 +14,7 @@ jobs:
             experimental: true
     steps:
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
         with:
           submodules: true
       - name: Install PHP ${{ matrix.php }}
@@ -34,7 +34,7 @@ jobs:
         run: |
           phpize
           ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4
-          sudo make install
+          sudo make -j$(nproc) install
           echo 'extension = redis.so' | sudo tee -a $(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')/90-redis.ini
       - name: Start redis
         run: |
@@ -78,3 +78,71 @@ jobs:
         env:
           TEST_PHP_ARGS: -e
           USE_ZEND_ALLOC: 0
+
+  macos:
+    runs-on: macos-latest
+    continue-on-error: ${{ matrix.experimental }}
+    strategy:
+      fail-fast: false
+      matrix:
+        php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
+        experimental: [false]
+        include:
+          - php: '8.2'
+            experimental: true
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+        with:
+          submodules: true
+      - name: Install PHP ${{ matrix.php }}
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php }}
+          extensions: json, :redis
+          coverage: none
+          tools: none
+      - name: Install dependencies
+        run: |
+          pecl install igbinary
+          pecl install msgpack
+      - name: Build phpredis
+        run: |
+          phpize
+          ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4
+          sudo make install
+          echo 'extension = redis.so' | sudo tee -a $(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')/90-redis.ini
+
+  windows:
+    runs-on: windows-latest
+    continue-on-error: ${{ matrix.experimental }}
+    strategy:
+      fail-fast: false
+      matrix:
+        php: ['7.3', '7.4', '8.0', '8.1']
+        experimental: [false]
+        include:
+          - php: '8.2'
+            experimental: true
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+        with:
+          submodules: true
+      - name: Install PHP ${{ matrix.php }}
+        uses: cmb69/setup-php-sdk@v0.6
+        id: setup-php-sdk
+        with:
+          version: ${{ matrix.php }}
+          arch: x64
+          ts: nts
+      - name: Install dependencies
+        uses: ilammy/msvc-dev-cmd@v1
+        with:
+          arch: x64
+          toolset: ${{steps.setup-php-sdk.outputs.toolset}}
+      - name: Build phpredis
+        run: |
+          phpize
+          ./configure --enable-redis --with-prefix=${{steps.setup-php-sdk.outputs.prefix}}
+          nmake

From 982bd13b9fa7b001598f3583e9c38e969b023cf7 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 1 Jul 2022 21:03:24 +0300
Subject: [PATCH 1586/1986] Refactor redis_parse_info_response

---
 library.c | 69 +++++++++++++++++++++----------------------------------
 1 file changed, 26 insertions(+), 43 deletions(-)

diff --git a/library.c b/library.c
index f793e1e258..73925a37e3 100644
--- a/library.c
+++ b/library.c
@@ -1111,50 +1111,33 @@ PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
 PHP_REDIS_API void
 redis_parse_info_response(char *response, zval *z_ret)
 {
-    char *cur, *pos;
-
-    array_init(z_ret);
+    char *p1, *s1 = NULL;
 
-    cur = response;
-    while(1) {
-        /* skip comments and empty lines */
-        if (*cur == '#' || *cur == '\r') {
-            if ((cur = strstr(cur, _NL)) == NULL) {
-                break;
+    ZVAL_FALSE(z_ret);
+    if ((p1 = php_strtok_r(response, _NL, &s1)) != NULL) {
+        array_init(z_ret);
+        do {
+            if (*p1 == '#') continue;
+            char *p;
+            zend_uchar type;
+            zend_long lval;
+            double dval;
+            if ((p = strchr(p1, ':')) != NULL) {
+                type = is_numeric_string(p + 1, strlen(p + 1), &lval, &dval, 0);
+                switch (type) {
+                case IS_LONG:
+                    add_assoc_long_ex(z_ret, p1, p - p1, lval);
+                    break;
+                case IS_DOUBLE:
+                    add_assoc_double_ex(z_ret, p1, p - p1, dval);
+                    break;
+                default:
+                    add_assoc_string_ex(z_ret, p1, p - p1, p + 1);
+                }
+            } else {
+                add_next_index_string(z_ret, p1);
             }
-            cur += 2;
-            continue;
-        }
-
-        /* key */
-        if ((pos = strchr(cur, ':')) == NULL) {
-            break;
-        }
-        char *key = cur;
-        int key_len = pos - cur;
-        key[key_len] = '\0';
-
-        /* value */
-        cur = pos + 1;
-        if ((pos = strstr(cur, _NL)) == NULL) {
-            break;
-        }
-        char *value = cur;
-        int value_len = pos - cur;
-        value[value_len] = '\0';
-
-        double dval;
-        zend_long lval;
-        zend_uchar type = is_numeric_string(value, value_len, &lval, &dval, 0);
-        if (type == IS_LONG) {
-            add_assoc_long_ex(z_ret, key, key_len, lval);
-        } else if (type == IS_DOUBLE) {
-            add_assoc_double_ex(z_ret, key, key_len, dval);
-        } else {
-            add_assoc_stringl_ex(z_ret, key, key_len, value, value_len);
-        }
-
-        cur = pos + 2; /* \r, \n */
+        } while ((p1 = php_strtok_r(NULL, _NL, &s1)) != NULL);
     }
 }
 
@@ -1211,7 +1194,7 @@ redis_parse_client_list_response(char *response, zval *z_ret)
                     zend_long lval;
                     double dval;
                     if ((p = strchr(p2, '=')) != NULL) {
-                        type = is_numeric_string(p + 1, s2 - p - 1, &lval, &dval, 0);
+                        type = is_numeric_string(p + 1, strlen(p + 1), &lval, &dval, 0);
                         switch (type) {
                         case IS_LONG:
                             add_assoc_long_ex(&z_sub, p2, p - p2, lval);

From c28ad7bbd6c824cf92c671bad8c442d697d499bb Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 3 Jul 2022 18:33:42 +0300
Subject: [PATCH 1587/1986] Issue #2122

Allow IPv6 address within square brackets
---
 library.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library.c b/library.c
index 73925a37e3..cbb8e1b286 100644
--- a/library.c
+++ b/library.c
@@ -2340,7 +2340,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
 #ifdef HAVE_IPV6
         /* If we've got IPv6 and find a colon in our address, convert to proper
          * IPv6 [host]:port format */
-        if (strchr(address, ':') != NULL) {
+        if (strchr(address, ':') != NULL && strchr(address, '[') == NULL) {
             fmtstr = "%s://[%s]:%d";
         }
 #endif

From e858e8e333950a5501b865c280a95dcc6ea16462 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 10 Jul 2022 21:38:16 +0300
Subject: [PATCH 1588/1986] Issue #1768

Allow multiple field-value pairs for hmset command.
---
 redis_commands.c | 98 ++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 78 insertions(+), 20 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 3b3fa917e9..f63c113194 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2670,9 +2670,83 @@ int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-/* Generic command construction for HSET and HSETNX */
-static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                        char *kw, char **cmd, int *cmd_len, short *slot)
+/* HSET */
+int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                   char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    int i, argc;
+    smart_string cmdstr = {0};
+    zend_string *zkey;
+    zval *z_args, *z_ele;
+
+    if ((argc = ZEND_NUM_ARGS()) < 2) {
+        return FAILURE;
+    }
+
+    z_args = ecalloc(argc, sizeof(*z_args));
+    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
+        efree(z_args);
+        return FAILURE;
+    }
+
+    if (argc == 2) {
+        if (Z_TYPE(z_args[1]) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL(z_args[1])) == 0) {
+            efree(z_args);
+            return FAILURE;
+        }
+
+        /* Initialize our command */
+        redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL(z_args[1])), ZEND_STRL("HSET"));
+
+        /* Append key */
+        zkey = zval_get_string(&z_args[0]);
+        redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, slot);
+        zend_string_release(zkey);
+
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey));
+                redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock);
+            }
+        } ZEND_HASH_FOREACH_END();
+    } else {
+        if (argc % 2 == 0) {
+            efree(z_args);
+            return FAILURE;
+        }
+        /* Initialize our command */
+        redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HSET"));
+
+        /* Append key */
+        zkey = zval_get_string(&z_args[0]);
+        redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, slot);
+        zend_string_release(zkey);
+
+        for (i = 1; i < argc; ++i) {
+            if (i % 2) {
+                zkey = zval_get_string(&z_args[i]);
+                redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey));
+                zend_string_release(zkey);
+            } else {
+                redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock);
+            }
+        }
+    }
+
+    // Push out values
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    // Cleanup arg array
+    efree(z_args);
+
+    return SUCCESS;
+}
+
+/* HSETNX */
+int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                     char **cmd, int *cmd_len, short *slot, void **ctx)
 {
     char *key, *mem;
     size_t key_len, mem_len;
@@ -2685,28 +2759,12 @@ static int gen_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     /* Construct command */
-    *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksv", key, key_len, mem, mem_len, z_val);
+    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HSETNX", "ksv", key, key_len, mem, mem_len, z_val);
 
     // Success
     return SUCCESS;
 }
 
-/* HSET */
-int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                   char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_hset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "HSET",
-        cmd, cmd_len, slot);
-}
-
-/* HSETNX */
-int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                     char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_hset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "HSETNX",
-        cmd, cmd_len, slot);
-}
-
 int
 redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)

From 0f1ca0ccf815352d79e716dac49b854491ecc994 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Fri, 15 Jul 2022 16:14:23 +0300
Subject: [PATCH 1589/1986] Unsubscribe from all channels

---
 redis_commands.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 7ed18a3d16..f85a764bc3 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1166,11 +1166,6 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     ht_arr = Z_ARRVAL_P(z_arr);
 
     sctx->argc = zend_hash_num_elements(ht_arr);
-    if (sctx->argc == 0) {
-        efree(sctx);
-        return FAILURE;
-    }
-
     redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw));
 
     ZEND_HASH_FOREACH_VAL(ht_arr, z_chan) {
@@ -1183,6 +1178,10 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         if (key_free) efree(key);
     } ZEND_HASH_FOREACH_END();
 
+    if (!sctx->argc && redis_sock->subs) {
+        sctx->argc = zend_hash_num_elements(redis_sock->subs);
+    }
+
     // Push out vals
     *cmd_len = cmdstr.len;
     *cmd     = cmdstr.c;

From 3675f442e413bd864c12787c3b383b110ed26963 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Mon, 4 Jul 2022 14:27:20 +0200
Subject: [PATCH 1590/1986] mark auth param as sensitive for PHP 8.2 refactor
 MINIT (split in each class sources file) use @generate-class-entries in stub
 files add RedisException and RedisClusterException in stub files

---
 library.h                       |  3 +++
 redis.c                         | 40 +++++----------------------------
 redis.stub.php                  |  4 ++++
 redis_arginfo.h                 | 27 +++++++++++++++++++++-
 redis_array.c                   |  9 ++++++--
 redis_array.h                   |  6 ++---
 redis_array.stub.php            |  1 +
 redis_array_arginfo.h           | 12 +++++++++-
 redis_array_legacy_arginfo.h    | 12 +++++++++-
 redis_cluster.c                 | 17 ++++++++++++--
 redis_cluster.h                 | 13 +++++------
 redis_cluster.stub.php          |  4 ++++
 redis_cluster_arginfo.h         | 27 +++++++++++++++++++++-
 redis_cluster_legacy_arginfo.h  | 27 +++++++++++++++++++++-
 redis_legacy_arginfo.h          | 27 +++++++++++++++++++++-
 redis_sentinel.c                |  9 ++++++--
 redis_sentinel.h                |  3 ++-
 redis_sentinel.stub.php         |  2 ++
 redis_sentinel_arginfo.h        | 12 +++++++++-
 redis_sentinel_legacy_arginfo.h | 12 +++++++++-
 20 files changed, 208 insertions(+), 59 deletions(-)

diff --git a/library.h b/library.h
index e00e693905..1c31adc181 100644
--- a/library.h
+++ b/library.h
@@ -35,6 +35,9 @@
     #define REDIS_VALUE_EXCEPTION(m) zend_value_error(m)
 #endif
 
+#if PHP_VERSION_ID < 80200
+#define zend_mark_function_parameter_as_sensitive(a,b,c)
+#endif
 
 void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id);
 void free_reply_callbacks(RedisSock *redis_sock);
diff --git a/redis.c b/redis.c
index bfa72b88ce..357405ec2e 100644
--- a/redis.c
+++ b/redis.c
@@ -52,17 +52,13 @@ extern ps_module ps_mod_redis;
 extern ps_module ps_mod_redis_cluster;
 #endif
 
-extern zend_class_entry *redis_array_ce;
-extern zend_class_entry *redis_cluster_ce;
-extern zend_class_entry *redis_cluster_exception_ce;
-extern zend_class_entry *redis_sentinel_ce;
-
 zend_class_entry *redis_ce;
 zend_class_entry *redis_exception_ce;
 
 #if PHP_VERSION_ID < 80000
 #include "redis_legacy_arginfo.h"
 #else
+#include "zend_attributes.h"
 #include "redis_arginfo.h"
 #endif
 
@@ -440,14 +436,6 @@ static PHP_GINIT_FUNCTION(redis)
 PHP_MINIT_FUNCTION(redis)
 {
     struct timeval tv;
-
-    zend_class_entry redis_class_entry;
-    zend_class_entry redis_array_class_entry;
-    zend_class_entry redis_cluster_class_entry;
-    zend_class_entry redis_sentinel_class_entry;
-    zend_class_entry redis_exception_class_entry;
-    zend_class_entry redis_cluster_exception_class_entry;
-
     zend_class_entry *exception_ce = NULL;
 
     /* Seed random generator (for RedisCluster failover) */
@@ -457,24 +445,17 @@ PHP_MINIT_FUNCTION(redis)
     REGISTER_INI_ENTRIES();
 
     /* Redis class */
-    INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_get_methods());
-    redis_ce = zend_register_internal_class(&redis_class_entry);
+    redis_ce = register_class_Redis();
     redis_ce->create_object = create_redis_object;
 
     /* RedisArray class */
-    INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_get_methods());
-    redis_array_ce = zend_register_internal_class(&redis_array_class_entry);
-    redis_array_ce->create_object = create_redis_array_object;
+    ZEND_MINIT(redis_array)(INIT_FUNC_ARGS_PASSTHRU);
 
     /* RedisCluster class */
-    INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_get_methods());
-    redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry);
-    redis_cluster_ce->create_object = create_cluster_context;
+    ZEND_MINIT(redis_cluster)(INIT_FUNC_ARGS_PASSTHRU);
 
     /* RedisSentinel class */
-    INIT_CLASS_ENTRY(redis_sentinel_class_entry, "RedisSentinel", redis_sentinel_get_methods());
-    redis_sentinel_ce = zend_register_internal_class(&redis_sentinel_class_entry);
-    redis_sentinel_ce->create_object = create_sentinel_object;
+    ZEND_MINIT(redis_sentinel)(INIT_FUNC_ARGS_PASSTHRU);
 
     /* Register our cluster cache list item */
     le_cluster_slot_cache = zend_register_list_destructors_ex(NULL, cluster_cache_dtor,
@@ -488,16 +469,7 @@ PHP_MINIT_FUNCTION(redis)
     }
 
     /* RedisException class */
-    INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL);
-    redis_exception_ce = zend_register_internal_class_ex(
-        &redis_exception_class_entry,
-        exception_ce);
-
-    /* RedisClusterException class */
-    INIT_CLASS_ENTRY(redis_cluster_exception_class_entry,
-        "RedisClusterException", NULL);
-    redis_cluster_exception_ce = zend_register_internal_class_ex(
-        &redis_cluster_exception_class_entry, exception_ce);
+    redis_exception_ce = register_class_RedisException(exception_ce);
 
     /* Add shared class constants to Redis and RedisCluster objects */
     add_class_constants(redis_ce, 0);
diff --git a/redis.stub.php b/redis.stub.php
index 93dd273062..67db162994 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -3,6 +3,7 @@
 /**
  * @generate-function-entries
  * @generate-legacy-arginfo
+ * @generate-class-entries
  */
 
 class Redis {
@@ -34,6 +35,7 @@ public function acl(string $subcmd, ...$args);
 	/** @return int|Redis */
     public function append(string $key, mixed $value);
 
+    /** @sensitive-param $credentials **/
     public function auth(mixed $credentials): bool;
 
     public function bgSave(): bool;
@@ -553,3 +555,5 @@ public function zunion(array $keys, array $weights = null, array $options = null
 
     public function zunionstore(string $dst, array $keys, array $weights = null, string $aggregate = null): int;
 }
+
+class RedisException extends RuntimeException {}
diff --git a/redis_arginfo.h b/redis_arginfo.h
index ea660631d2..8dc5a4c331 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: efcda1ed028d65d0b4848d32133dc0e32f17871f */
+ * Stub hash: 0e9010a9567392f6f2a8ad7f1f5f09a28a086c45 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -1436,3 +1436,28 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, zunionstore, arginfo_class_Redis_zunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
+
+
+static const zend_function_entry class_RedisException_methods[] = {
+	ZEND_FE_END
+};
+
+static zend_class_entry *register_class_Redis(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+	return class_entry;
+}
+
+static zend_class_entry *register_class_RedisException(zend_class_entry *class_entry_RuntimeException)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisException", class_RedisException_methods);
+	class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException);
+
+	return class_entry;
+}
diff --git a/redis_array.c b/redis_array.c
index 1c5f09707a..53ad4eb2ca 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -40,12 +40,17 @@ zend_class_entry *redis_array_ce;
 #if PHP_VERSION_ID < 80000
 #include "redis_array_legacy_arginfo.h"
 #else
+#include "zend_attributes.h"
 #include "redis_array_arginfo.h"
 #endif
 
-extern const zend_function_entry *redis_array_get_methods(void)
+PHP_MINIT_FUNCTION(redis_array)
 {
-    return class_RedisArray_methods;
+    /* RedisSentinel class */
+    redis_array_ce = register_class_RedisArray();
+    redis_array_ce->create_object = create_redis_array_object;
+
+    return SUCCESS;
 }
 
 static void
diff --git a/redis_array.h b/redis_array.h
index 1e70be33d9..4dea35a68b 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -36,8 +36,8 @@ typedef struct RedisArray_ {
     struct RedisArray_ *prev;
 } RedisArray;
 
-extern const zend_function_entry *redis_array_get_methods(void);
-zend_object *create_redis_array_object(zend_class_entry *ce);
-void free_redis_array_object(zend_object *object);
+extern zend_class_entry *redis_array_ce;
+extern PHP_MINIT_FUNCTION(redis_array);
+extern zend_object *create_redis_array_object(zend_class_entry *ce);
 
 #endif
diff --git a/redis_array.stub.php b/redis_array.stub.php
index cab4ffc973..8fca8ab95a 100644
--- a/redis_array.stub.php
+++ b/redis_array.stub.php
@@ -3,6 +3,7 @@
 /**
  * @generate-function-entries
  * @generate-legacy-arginfo
+ * @generate-class-entries
  */
 
 class RedisArray {
diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h
index 3b426b36f5..bfacd08609 100644
--- a/redis_array_arginfo.h
+++ b/redis_array_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: db47879ea03ea74832fe777fcc5d834ea554bb4a */
+ * Stub hash: fb17c785beccf1dbeedaa48afb4aa7d48fd8b655 */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0)
@@ -176,3 +176,13 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, zscan, arginfo_class_RedisArray_zscan, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
+
+static zend_class_entry *register_class_RedisArray(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisArray", class_RedisArray_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+	return class_entry;
+}
diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h
index 9c62aac57e..1f2174ef0b 100644
--- a/redis_array_legacy_arginfo.h
+++ b/redis_array_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: db47879ea03ea74832fe777fcc5d834ea554bb4a */
+ * Stub hash: fb17c785beccf1dbeedaa48afb4aa7d48fd8b655 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2)
 	ZEND_ARG_INFO(0, function_name)
@@ -173,3 +173,13 @@ static const zend_function_entry class_RedisArray_methods[] = {
 	ZEND_ME(RedisArray, zscan, arginfo_class_RedisArray_zscan, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
+
+static zend_class_entry *register_class_RedisArray(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisArray", class_RedisArray_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+	return class_entry;
+}
diff --git a/redis_cluster.c b/redis_cluster.c
index 5634560f8b..e345189901 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -38,12 +38,25 @@ zend_class_entry *redis_cluster_exception_ce;
 #if PHP_VERSION_ID < 80000
 #include "redis_cluster_legacy_arginfo.h"
 #else
+#include "zend_attributes.h"
 #include "redis_cluster_arginfo.h"
 #endif
 
-extern const zend_function_entry *redis_cluster_get_methods(void)
+PHP_MINIT_FUNCTION(redis_cluster)
 {
-    return class_RedisCluster_methods;
+    zend_class_entry *exception_ce = NULL;
+
+    redis_cluster_ce = register_class_RedisCluster();
+    redis_cluster_ce->create_object = create_cluster_context;
+
+    /* Base Exception class */
+    exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1);
+    if (exception_ce == NULL) {
+        exception_ce = zend_exception_get_default();
+    }
+    redis_cluster_exception_ce = register_class_RedisClusterException(exception_ce);
+
+    return SUCCESS;
 }
 
 /* Handlers for RedisCluster */
diff --git a/redis_cluster.h b/redis_cluster.h
index d8e62e7f65..01781416af 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -91,12 +91,11 @@
     } \
     resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx);
 
-extern const zend_function_entry *redis_cluster_get_methods(void);
-
-/* Create cluster context */
-zend_object *create_cluster_context(zend_class_entry *class_type);
+#endif
 
-/* Free cluster context struct */
-void free_cluster_context(zend_object *object);
+extern zend_class_entry *redis_cluster_ce;
+extern zend_class_entry *redis_cluster_exception_ce;
+extern PHP_MINIT_FUNCTION(redis_cluster);
+extern zend_object * create_cluster_context(zend_class_entry *class_type);
+extern void free_cluster_context(zend_object *object);
 
-#endif
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 2fe512fd99..3c53d6e976 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -3,10 +3,12 @@
 /**
  * @generate-function-entries
  * @generate-legacy-arginfo
+ * @generate-class-entries
  */
 
 class RedisCluster {
 
+    /** @sensitive-param $auth **/
     public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistant = false, mixed $auth = NULL, array $context = NULL);
 
     public function _compress(string $value): string;
@@ -389,3 +391,5 @@ public function zscore(string $key): float;
 
     public function zunionstore(string $key, array $keys, array $weights = null, string $aggregate = null): int;
 }
+
+class RedisClusterException extends RuntimeException {}
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 95945fc39a..24f4ca4798 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8029a0d6df2bbd9cf5d140ff8d9efcc4de2a5bcc */
+ * Stub hash: 72e55bab630cd2a6dd6620d77e97ec0716d667b1 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -1226,3 +1226,28 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, zunionstore, arginfo_class_RedisCluster_zunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
+
+
+static const zend_function_entry class_RedisClusterException_methods[] = {
+	ZEND_FE_END
+};
+
+static zend_class_entry *register_class_RedisCluster(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+	return class_entry;
+}
+
+static zend_class_entry *register_class_RedisClusterException(zend_class_entry *class_entry_RuntimeException)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisClusterException", class_RedisClusterException_methods);
+	class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException);
+
+	return class_entry;
+}
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index bc07b11780..aec77bf90c 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8029a0d6df2bbd9cf5d140ff8d9efcc4de2a5bcc */
+ * Stub hash: 72e55bab630cd2a6dd6620d77e97ec0716d667b1 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -1114,3 +1114,28 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, zunionstore, arginfo_class_RedisCluster_zunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
+
+
+static const zend_function_entry class_RedisClusterException_methods[] = {
+	ZEND_FE_END
+};
+
+static zend_class_entry *register_class_RedisCluster(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+	return class_entry;
+}
+
+static zend_class_entry *register_class_RedisClusterException(zend_class_entry *class_entry_RuntimeException)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisClusterException", class_RedisClusterException_methods);
+	class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException);
+
+	return class_entry;
+}
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 622d61cbdd..83750374f3 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: efcda1ed028d65d0b4848d32133dc0e32f17871f */
+ * Stub hash: 0e9010a9567392f6f2a8ad7f1f5f09a28a086c45 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -1332,3 +1332,28 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, zunionstore, arginfo_class_Redis_zunionstore, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
+
+
+static const zend_function_entry class_RedisException_methods[] = {
+	ZEND_FE_END
+};
+
+static zend_class_entry *register_class_Redis(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+	return class_entry;
+}
+
+static zend_class_entry *register_class_RedisException(zend_class_entry *class_entry_RuntimeException)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisException", class_RedisException_methods);
+	class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException);
+
+	return class_entry;
+}
diff --git a/redis_sentinel.c b/redis_sentinel.c
index 632975cd9d..5aa4442018 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -26,12 +26,17 @@ extern zend_class_entry *redis_exception_ce;
 #if PHP_VERSION_ID < 80000
 #include "redis_sentinel_legacy_arginfo.h"
 #else
+#include "zend_attributes.h"
 #include "redis_sentinel_arginfo.h"
 #endif
 
-extern const zend_function_entry *redis_sentinel_get_methods(void)
+PHP_MINIT_FUNCTION(redis_sentinel)
 {
-    return class_RedisSentinel_methods;
+    /* RedisSentinel class */
+    redis_sentinel_ce = register_class_RedisSentinel();
+    redis_sentinel_ce->create_object = create_sentinel_object;
+
+    return SUCCESS;
 }
 
 PHP_METHOD(RedisSentinel, __construct)
diff --git a/redis_sentinel.h b/redis_sentinel.h
index a24c5c0bcb..0878b62d41 100644
--- a/redis_sentinel.h
+++ b/redis_sentinel.h
@@ -5,6 +5,7 @@
 
 #define PHP_REDIS_SENTINEL_VERSION "0.1"
 
-extern const zend_function_entry *redis_sentinel_get_methods(void);
+extern zend_class_entry *redis_sentinel_ce;
+extern PHP_MINIT_FUNCTION(redis_sentinel);
 
 #endif /* REDIS_SENTINEL_H */
diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php
index 58df483892..313040f9ba 100644
--- a/redis_sentinel.stub.php
+++ b/redis_sentinel.stub.php
@@ -3,10 +3,12 @@
 /**
  * @generate-function-entries
  * @generate-legacy-arginfo
+ * @generate-class-entries
  */
 
 class RedisSentinel {
 
+    /** @sensitive-param $auth **/
     public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = NULL, int $retry_interval = 0, float $read_timeout = 0, mixed $auth = NULL);
 
 	/** @return bool|RedisSentinel */
diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h
index 2afdd4e13b..ac95178309 100644
--- a/redis_sentinel_arginfo.h
+++ b/redis_sentinel_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: deae7b4a55435fb2ab39f314544064a34c56d218 */
+ * Stub hash: 4e8243076e2c4470473a08456ff20be9f230ee74 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
@@ -69,3 +69,13 @@ static const zend_function_entry class_RedisSentinel_methods[] = {
 	ZEND_ME(RedisSentinel, slaves, arginfo_class_RedisSentinel_slaves, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
+
+static zend_class_entry *register_class_RedisSentinel(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisSentinel", class_RedisSentinel_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+	return class_entry;
+}
diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h
index 8f35e6c1c5..e65d841e01 100644
--- a/redis_sentinel_legacy_arginfo.h
+++ b/redis_sentinel_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: deae7b4a55435fb2ab39f314544064a34c56d218 */
+ * Stub hash: 4e8243076e2c4470473a08456ff20be9f230ee74 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, host)
@@ -68,3 +68,13 @@ static const zend_function_entry class_RedisSentinel_methods[] = {
 	ZEND_ME(RedisSentinel, slaves, arginfo_class_RedisSentinel_slaves, ZEND_ACC_PUBLIC)
 	ZEND_FE_END
 };
+
+static zend_class_entry *register_class_RedisSentinel(void)
+{
+	zend_class_entry ce, *class_entry;
+
+	INIT_CLASS_ENTRY(ce, "RedisSentinel", class_RedisSentinel_methods);
+	class_entry = zend_register_internal_class_ex(&ce, NULL);
+
+	return class_entry;
+}

From 3cd5ac1e27796a559fea50f0a0eb716030ac9720 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Mon, 4 Jul 2022 14:44:27 +0200
Subject: [PATCH 1591/1986] use spl_ce_RuntimeException (exists since 5.6)

---
 redis.c         | 10 ++--------
 redis_cluster.c | 10 ++--------
 2 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/redis.c b/redis.c
index 357405ec2e..8c4cada67d 100644
--- a/redis.c
+++ b/redis.c
@@ -28,6 +28,7 @@
 #include "redis_commands.h"
 #include "redis_sentinel.h"
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -436,7 +437,6 @@ static PHP_GINIT_FUNCTION(redis)
 PHP_MINIT_FUNCTION(redis)
 {
     struct timeval tv;
-    zend_class_entry *exception_ce = NULL;
 
     /* Seed random generator (for RedisCluster failover) */
     gettimeofday(&tv, NULL);
@@ -462,14 +462,8 @@ PHP_MINIT_FUNCTION(redis)
                                                               "Redis cluster slot cache",
                                                               module_number);
 
-    /* Base Exception class */
-    exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1);
-    if (exception_ce == NULL) {
-        exception_ce = zend_exception_get_default();
-    }
-
     /* RedisException class */
-    redis_exception_ce = register_class_RedisException(exception_ce);
+    redis_exception_ce = register_class_RedisException(spl_ce_RuntimeException);
 
     /* Add shared class constants to Redis and RedisCluster objects */
     add_class_constants(redis_ce, 0);
diff --git a/redis_cluster.c b/redis_cluster.c
index e345189901..f163158af5 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -25,6 +25,7 @@
 #include "crc16.h"
 #include "redis_cluster.h"
 #include "redis_commands.h"
+#include 
 #include 
 #include "library.h"
 #include 
@@ -44,17 +45,10 @@ zend_class_entry *redis_cluster_exception_ce;
 
 PHP_MINIT_FUNCTION(redis_cluster)
 {
-    zend_class_entry *exception_ce = NULL;
-
     redis_cluster_ce = register_class_RedisCluster();
     redis_cluster_ce->create_object = create_cluster_context;
 
-    /* Base Exception class */
-    exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1);
-    if (exception_ce == NULL) {
-        exception_ce = zend_exception_get_default();
-    }
-    redis_cluster_exception_ce = register_class_RedisClusterException(exception_ce);
+    redis_cluster_exception_ce = register_class_RedisClusterException(spl_ce_RuntimeException);
 
     return SUCCESS;
 }

From a7e5ea643a050c8e90f675cbe62d85796001d89f Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Mon, 11 Jul 2022 09:49:04 +0200
Subject: [PATCH 1592/1986] fix closing condition

---
 redis_cluster.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/redis_cluster.h b/redis_cluster.h
index 01781416af..ebef92184e 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -91,11 +91,10 @@
     } \
     resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx);
 
-#endif
-
 extern zend_class_entry *redis_cluster_ce;
 extern zend_class_entry *redis_cluster_exception_ce;
 extern PHP_MINIT_FUNCTION(redis_cluster);
 extern zend_object * create_cluster_context(zend_class_entry *class_type);
 extern void free_cluster_context(zend_object *object);
 
+#endif

From a38e08daa1c568a696ea8148aa674a691b20b485 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Mon, 18 Jul 2022 12:46:09 +0200
Subject: [PATCH 1593/1986] regenerate arginfo using 8.2.0beta1

---
 library.h                       | 4 ----
 redis.stub.php                  | 3 +--
 redis_arginfo.h                 | 7 ++++++-
 redis_cluster.stub.php          | 3 +--
 redis_cluster_arginfo.h         | 7 ++++++-
 redis_cluster_legacy_arginfo.h  | 2 +-
 redis_legacy_arginfo.h          | 2 +-
 redis_sentinel.stub.php         | 3 +--
 redis_sentinel_arginfo.h        | 7 ++++++-
 redis_sentinel_legacy_arginfo.h | 2 +-
 10 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/library.h b/library.h
index 1c31adc181..d87fc6e7ad 100644
--- a/library.h
+++ b/library.h
@@ -35,10 +35,6 @@
     #define REDIS_VALUE_EXCEPTION(m) zend_value_error(m)
 #endif
 
-#if PHP_VERSION_ID < 80200
-#define zend_mark_function_parameter_as_sensitive(a,b,c)
-#endif
-
 void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id);
 void free_reply_callbacks(RedisSock *redis_sock);
 
diff --git a/redis.stub.php b/redis.stub.php
index 67db162994..0b7e17769e 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -35,8 +35,7 @@ public function acl(string $subcmd, ...$args);
 	/** @return int|Redis */
     public function append(string $key, mixed $value);
 
-    /** @sensitive-param $credentials **/
-    public function auth(mixed $credentials): bool;
+    public function auth(#[\SensitiveParameter] mixed $credentials): bool;
 
     public function bgSave(): bool;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 8dc5a4c331..74cde5dcf2 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0e9010a9567392f6f2a8ad7f1f5f09a28a086c45 */
+ * Stub hash: 954ed131a20d6939f9653dbc384e6244a0862b6e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -1448,6 +1448,11 @@ static zend_class_entry *register_class_Redis(void)
 
 	INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods);
 	class_entry = zend_register_internal_class_ex(&ce, NULL);
+#if (PHP_VERSION_ID >= 80200)
+
+
+	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "auth", sizeof("auth") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
+#endif
 
 	return class_entry;
 }
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 3c53d6e976..80461abcdd 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -8,8 +8,7 @@
 
 class RedisCluster {
 
-    /** @sensitive-param $auth **/
-    public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistant = false, mixed $auth = NULL, array $context = NULL);
+    public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistant = false, #[\SensitiveParameter] mixed $auth = NULL, array $context = NULL);
 
     public function _compress(string $value): string;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 24f4ca4798..189ff76572 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 72e55bab630cd2a6dd6620d77e97ec0716d667b1 */
+ * Stub hash: 7ff59229ef9ab94d3bb918d666610b70a5676030 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -1238,6 +1238,11 @@ static zend_class_entry *register_class_RedisCluster(void)
 
 	INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods);
 	class_entry = zend_register_internal_class_ex(&ce, NULL);
+#if (PHP_VERSION_ID >= 80200)
+
+
+	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 5, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
+#endif
 
 	return class_entry;
 }
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index aec77bf90c..0a5d4daa1f 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 72e55bab630cd2a6dd6620d77e97ec0716d667b1 */
+ * Stub hash: 7ff59229ef9ab94d3bb918d666610b70a5676030 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 83750374f3..05645b02cb 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0e9010a9567392f6f2a8ad7f1f5f09a28a086c45 */
+ * Stub hash: 954ed131a20d6939f9653dbc384e6244a0862b6e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php
index 313040f9ba..c3fc5a9e00 100644
--- a/redis_sentinel.stub.php
+++ b/redis_sentinel.stub.php
@@ -8,8 +8,7 @@
 
 class RedisSentinel {
 
-    /** @sensitive-param $auth **/
-    public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = NULL, int $retry_interval = 0, float $read_timeout = 0, mixed $auth = NULL);
+    public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = NULL, int $retry_interval = 0, float $read_timeout = 0, #[\SensitiveParameter] mixed $auth = NULL);
 
 	/** @return bool|RedisSentinel */
     public function ckquorum(string $master);
diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h
index ac95178309..58a5bc4c40 100644
--- a/redis_sentinel_arginfo.h
+++ b/redis_sentinel_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 4e8243076e2c4470473a08456ff20be9f230ee74 */
+ * Stub hash: 946942bc5a7612650fc0416902778452f6860d13 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
@@ -76,6 +76,11 @@ static zend_class_entry *register_class_RedisSentinel(void)
 
 	INIT_CLASS_ENTRY(ce, "RedisSentinel", class_RedisSentinel_methods);
 	class_entry = zend_register_internal_class_ex(&ce, NULL);
+#if (PHP_VERSION_ID >= 80200)
+
+
+	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 6, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
+#endif
 
 	return class_entry;
 }
diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h
index e65d841e01..30b58dff51 100644
--- a/redis_sentinel_legacy_arginfo.h
+++ b/redis_sentinel_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 4e8243076e2c4470473a08456ff20be9f230ee74 */
+ * Stub hash: 946942bc5a7612650fc0416902778452f6860d13 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, host)

From 584f6b172e8e8334ef0c15e6ea6c9fb81ece29b0 Mon Sep 17 00:00:00 2001
From: Remi Collet 
Date: Thu, 28 Jul 2022 07:28:41 +0200
Subject: [PATCH 1594/1986] empty

---
 library.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library.h b/library.h
index d87fc6e7ad..e00e693905 100644
--- a/library.h
+++ b/library.h
@@ -35,6 +35,7 @@
     #define REDIS_VALUE_EXCEPTION(m) zend_value_error(m)
 #endif
 
+
 void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id);
 void free_reply_callbacks(RedisSock *redis_sock);
 

From ed10f365e758787d75b909cff1b677c3028085f4 Mon Sep 17 00:00:00 2001
From: "patricio.dorantes" 
Date: Mon, 1 Aug 2022 11:16:24 -0500
Subject: [PATCH 1595/1986] fix redis session standalone stream ssl context

---
 redis_session.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/redis_session.c b/redis_session.c
index 19ae712fdf..95af257d1c 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -380,7 +380,7 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_
 PS_OPEN_FUNC(redis)
 {
     php_url *url;
-    zval params;
+    zval params, *context;
     int i, j, path_len;
 
     redis_pool *pool = ecalloc(1, sizeof(*pool));
@@ -451,6 +451,7 @@ PS_OPEN_FUNC(redis)
                 REDIS_CONF_AUTH_STATIC(ht, "auth", &user, &pass);
 
                 zval_dtor(¶ms);
+                context = REDIS_HASH_STR_FIND_TYPE_STATIC(ht, "stream", IS_ARRAY);
             }
 
             if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) {
@@ -490,6 +491,10 @@ PS_OPEN_FUNC(redis)
                                            persistent, persistent_id ? ZSTR_VAL(persistent_id) : NULL,
                                            retry_interval);
 
+            if (context  != NULL) {
+                redis_sock_set_stream_context(redis_sock, context);
+            }
+
             redis_pool_add(pool, redis_sock, weight, db);
             redis_sock->prefix = prefix;
             redis_sock_set_auth(redis_sock, user, pass);

From d1bc672752e4c77dd7a32f8efad5fc5b8efc1291 Mon Sep 17 00:00:00 2001
From: "patricio.dorantes" 
Date: Tue, 2 Aug 2022 17:36:54 -0500
Subject: [PATCH 1596/1986] fix use after free, initialize on NULL

---
 redis_session.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/redis_session.c b/redis_session.c
index 95af257d1c..df1155c130 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -380,7 +380,7 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_
 PS_OPEN_FUNC(redis)
 {
     php_url *url;
-    zval params, *context;
+    zval params, *context = NULL;
     int i, j, path_len;
 
     redis_pool *pool = ecalloc(1, sizeof(*pool));
@@ -450,8 +450,9 @@ PS_OPEN_FUNC(redis)
                 REDIS_CONF_STRING_STATIC(ht, "prefix", &prefix);
                 REDIS_CONF_AUTH_STATIC(ht, "auth", &user, &pass);
 
-                zval_dtor(¶ms);
                 context = REDIS_HASH_STR_FIND_TYPE_STATIC(ht, "stream", IS_ARRAY);
+
+                zval_dtor(¶ms);
             }
 
             if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) {

From 2ff11df528fe42a8f7bf4d70ad806195e1726954 Mon Sep 17 00:00:00 2001
From: "patricio.dorantes" 
Date: Wed, 3 Aug 2022 10:57:01 -0500
Subject: [PATCH 1597/1986] redis_session clean up ssl context.

---
 redis_session.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/redis_session.c b/redis_session.c
index df1155c130..3e3e88164c 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -494,6 +494,7 @@ PS_OPEN_FUNC(redis)
 
             if (context  != NULL) {
                 redis_sock_set_stream_context(redis_sock, context);
+                context = NULL;
             }
 
             redis_pool_add(pool, redis_sock, weight, db);

From 687a5c788064534b82764db3cdba08ee38bad2de Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 17 Jul 2022 15:05:56 +0300
Subject: [PATCH 1598/1986] Issue #1943

Add lPos command.
---
 library.c                  | 50 ++++++++++++++++++++++++++++++
 library.h                  |  1 +
 redis.c                    |  7 +++++
 redis.stub.php             |  6 ++--
 redis_arginfo.h            | 20 ++++++++----
 redis_commands.c           | 63 ++++++++++++++++++++++++++++++++++++++
 redis_commands.h           |  3 ++
 redis_legacy_arginfo.h     | 20 ++++++++----
 tests/RedisClusterTest.php |  1 +
 tests/RedisTest.php        | 13 ++++++++
 10 files changed, 170 insertions(+), 14 deletions(-)

diff --git a/library.c b/library.c
index 498c3b3718..782f81bfac 100644
--- a/library.c
+++ b/library.c
@@ -1334,6 +1334,56 @@ redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_
     }
 }
 
+PHP_REDIS_API int
+redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    char inbuf[4096];
+    int i, numElems;
+    size_t len;
+    zval z_ret;
+
+    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) {
+        goto failure;
+    }
+
+    if (ctx == NULL) {
+        if (*inbuf != TYPE_INT && *inbuf != TYPE_BULK) {
+            goto failure;
+        }
+        ZVAL_LONG(&z_ret, atol(inbuf + 1));
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        if (*inbuf != TYPE_MULTIBULK) {
+            goto failure;
+        }
+        array_init(&z_ret);
+        numElems = atol(inbuf + 1);
+        for (i = 0;  i < numElems; ++i) {
+            if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) {
+                zval_dtor(&z_ret);
+                goto failure;
+            }
+            add_next_index_long(&z_ret, atol(inbuf + 1));
+        }
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
+    }
+
+    if (IS_ATOMIC(redis_sock)) {
+        RETVAL_ZVAL(&z_ret, 0, 1);
+    } else {
+        add_next_index_zval(z_tab, &z_ret);
+    }
+    return SUCCESS;
+
+failure:
+    if (IS_ATOMIC(redis_sock)) {
+        RETVAL_FALSE;
+    } else {
+        add_next_index_bool(z_tab, 0);
+    }
+    return FAILURE;
+}
+
 PHP_REDIS_API int
 redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                             zval *z_tab, void *ctx,
diff --git a/library.h b/library.h
index e00e693905..48164f09b1 100644
--- a/library.h
+++ b/library.h
@@ -168,6 +168,7 @@ PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re
 PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 /* Helper methods to get configuration values from a HashTable. */
 
diff --git a/redis.c b/redis.c
index 8c4cada67d..30e8f40171 100644
--- a/redis.c
+++ b/redis.c
@@ -1253,6 +1253,13 @@ PHP_METHOD(Redis, lPop)
 }
 /* }}} */
 
+/* {{{ proto string Redis::lPos(string key, mixed value, [array options = null]) */
+PHP_METHOD(Redis, lPos)
+{
+    REDIS_PROCESS_CMD(lpos, redis_lpos_response);
+}
+/* }}} */
+
 /* {{{ proto string Redis::rPop(string key, [int count = 0]) */
 PHP_METHOD(Redis, rPop)
 {
diff --git a/redis.stub.php b/redis.stub.php
index 0b7e17769e..16b6164c25 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -10,10 +10,10 @@ class Redis {
 
     public function __construct(array $options = null);
 
-    public function _compress(string $value): string;
-
     public function __destruct();
 
+    public function _compress(string $value): string;
+
     public function _pack(mixed $value): string;
 
     public function _prefix(string $key): string;
@@ -241,6 +241,8 @@ public function lMove(string $src, string $dst, string $wherefrom, string $where
 
     public function lPop(string $key, int $count = 0): bool|string|array;
 
+    public function lPos(string $key, mixed $value, array $options = null): bool|int|array;
+
     /**
      * @param mixed $elements
      * @return int|Redis
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 74cde5dcf2..b5f67ad0a9 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,15 +1,15 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 954ed131a20d6939f9653dbc384e6244a0862b6e */
+ * Stub hash: 7fc0b991dc8404945a0081aef8a422c9c670eab9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__compress, 0, 1, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__compress, 0, 1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__pack, 0, 1, IS_STRING, 0)
@@ -416,6 +416,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_lPop, 0, 1, MAY_BE_B
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_INFO(0, elements)
@@ -975,8 +981,8 @@ ZEND_END_ARG_INFO()
 
 
 ZEND_METHOD(Redis, __construct);
-ZEND_METHOD(Redis, _compress);
 ZEND_METHOD(Redis, __destruct);
+ZEND_METHOD(Redis, _compress);
 ZEND_METHOD(Redis, _pack);
 ZEND_METHOD(Redis, _prefix);
 ZEND_METHOD(Redis, _serialize);
@@ -1072,6 +1078,7 @@ ZEND_METHOD(Redis, lInsert);
 ZEND_METHOD(Redis, lLen);
 ZEND_METHOD(Redis, lMove);
 ZEND_METHOD(Redis, lPop);
+ZEND_METHOD(Redis, lPos);
 ZEND_METHOD(Redis, lPush);
 ZEND_METHOD(Redis, rPush);
 ZEND_METHOD(Redis, lPushx);
@@ -1205,8 +1212,8 @@ ZEND_METHOD(Redis, zunionstore);
 
 static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _prefix, arginfo_class_Redis__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC)
@@ -1303,6 +1310,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPos, arginfo_class_Redis_lPos, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index f85a764bc3..ea1eedc779 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2626,6 +2626,69 @@ int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+               char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    char *key;
+    int argc = 2;
+    size_t key_len;
+    smart_string cmdstr = {0};
+    zend_bool withrank = 0;
+    zend_long rank = 0, count = -1, maxlen = -1;
+    zend_string *zkey;
+    zval *z_val, *z_ele, *z_opts = NULL;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|a",
+                              &key, &key_len, &z_val, &z_opts) == FAILURE)
+    {
+        return FAILURE;
+    }
+
+    if (z_opts != NULL) {
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "count")) {
+                    count = zval_get_long(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "maxlen")) {
+                    maxlen = zval_get_long(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "rank")) {
+                    rank = zval_get_long(z_ele);
+                    withrank = 1;
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    argc += (withrank ? 2 : 0) + (count >= 0 ? 2 : 0) + (maxlen >= 0 ? 2 : 0);
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "LPOS");
+
+    redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot);
+    redis_cmd_append_sstr_zval(&cmdstr, z_val, redis_sock);
+
+    if (withrank) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "RANK");
+        redis_cmd_append_sstr_long(&cmdstr, rank);
+    }
+
+    if (count >= 0) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
+        redis_cmd_append_sstr_long(&cmdstr, count);
+        *ctx = PHPREDIS_CTX_PTR;
+    }
+
+    if (maxlen >= 0) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN");
+        redis_cmd_append_sstr_long(&cmdstr, maxlen);
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     char **cmd, int *cmd_len, short *slot, void **ctx)
 {
diff --git a/redis_commands.h b/redis_commands.h
index e766e20d60..e412d82834 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -229,6 +229,9 @@ int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 05645b02cb..5cced37aac 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,15 +1,15 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 954ed131a20d6939f9653dbc384e6244a0862b6e */
+ * Stub hash: 7fc0b991dc8404945a0081aef8a422c9c670eab9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__compress, 0, 0, 1)
-	ZEND_ARG_INFO(0, value)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__compress, 0, 0, 1)
+	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis__pack arginfo_class_Redis__compress
@@ -371,6 +371,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPop, 0, 0, 1)
 	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPos, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_VARIADIC_INFO(0, elements)
@@ -871,8 +877,8 @@ ZEND_END_ARG_INFO()
 
 
 ZEND_METHOD(Redis, __construct);
-ZEND_METHOD(Redis, _compress);
 ZEND_METHOD(Redis, __destruct);
+ZEND_METHOD(Redis, _compress);
 ZEND_METHOD(Redis, _pack);
 ZEND_METHOD(Redis, _prefix);
 ZEND_METHOD(Redis, _serialize);
@@ -968,6 +974,7 @@ ZEND_METHOD(Redis, lInsert);
 ZEND_METHOD(Redis, lLen);
 ZEND_METHOD(Redis, lMove);
 ZEND_METHOD(Redis, lPop);
+ZEND_METHOD(Redis, lPos);
 ZEND_METHOD(Redis, lPush);
 ZEND_METHOD(Redis, rPush);
 ZEND_METHOD(Redis, lPushx);
@@ -1101,8 +1108,8 @@ ZEND_METHOD(Redis, zunionstore);
 
 static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _prefix, arginfo_class_Redis__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC)
@@ -1199,6 +1206,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lPos, arginfo_class_Redis_lPos, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, rPush, arginfo_class_Redis_rPush, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, lPushx, arginfo_class_Redis_lPushx, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 18e4ac21de..6daa93bb59 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -51,6 +51,7 @@ public function testReset() { return $this->markTestSkipped(); }
     public function testInvalidAuthArgs() { return $this->markTestSkipped(); }
 
     public function testlMove() { return $this->markTestSkipped(); }
+    public function testlPos() { return $this->marktestSkipped(); }
     public function testsMisMember() { return $this->markTestSkipped(); }
     public function testzDiff() { return $this->markTestSkipped(); }
     public function testzInter() { return $this->markTestSkipped(); }
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 43a0cb1e7d..de76e9b459 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1015,6 +1015,19 @@ public function testlPopx() {
         $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val2', 'val0', 'val1']);
     }
 
+    public function testlPos()
+    {
+        $this->redis->del('key');
+        $this->redis->lPush('key', 'val0', 'val1', 'val1');
+        $this->assertEquals(2, $this->redis->lPos('key', 'val0'));
+        $this->assertEquals(0, $this->redis->lPos('key', 'val1'));
+        $this->assertEquals(1, $this->redis->lPos('key', 'val1', ['rank' => 2]));
+        $this->assertEquals([0, 1], $this->redis->lPos('key', 'val1', ['count' => 2]));
+        $this->assertEquals([0], $this->redis->lPos('key', 'val1', ['count' => 2, 'maxlen' => 1]));
+        $this->assertEquals([], $this->redis->lPos('key', 'val2', ['count' => 1]));
+        $this->assertEquals(-1, $this->redis->lPos('key', 'val2'));
+    }
+
     // ltrim, lsize, lpop
     public function testltrim()
     {

From bf6f31e3723a5432186ea22e31fe4c979441028c Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 6 Aug 2022 12:43:18 +0300
Subject: [PATCH 1599/1986] Issue #1894

Add the ANY argument to GEOSEARCH and GEORADIUS
---
 redis_commands.c | 66 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 51 insertions(+), 15 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index ea1eedc779..1de298d800 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -52,6 +52,7 @@ typedef struct geoOptions {
     int withdist;
     int withhash;
     long count;
+    zend_bool any;
     geoSortType sort;
     geoStoreType store;
     zend_string *key;
@@ -3379,7 +3380,7 @@ geoStoreType get_georadius_store_type(zend_string *key) {
 static int get_georadius_opts(HashTable *ht, geoOptions *opts) {
     char *optstr;
     zend_string *zkey;
-    zval *optval;
+    zval *optval, *z_tmp;
 
     /* Iterate over our argument array, collating which ones we have */
     ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, optval) {
@@ -3387,16 +3388,30 @@ static int get_georadius_opts(HashTable *ht, geoOptions *opts) {
 
         /* If the key is numeric it's a non value option */
         if (zkey) {
-            if (ZSTR_LEN(zkey) == 5 && !strcasecmp(ZSTR_VAL(zkey), "count")) {
-                if (Z_TYPE_P(optval) != IS_LONG || Z_LVAL_P(optval) <= 0) {
-                    php_error_docref(NULL, E_WARNING,
-                            "COUNT must be an integer > 0!");
+            if (zend_string_equals_literal_ci(zkey, "COUNT")) {
+                if (Z_TYPE_P(optval) == IS_ARRAY) {
+                    if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 0)) == NULL ||
+                        Z_TYPE_P(z_tmp) != IS_LONG ||
+                        (opts->count = Z_LVAL_P(z_tmp)) <= 0
+                    ) {
+                        php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
+                        if (opts->key) zend_string_release(opts->key);
+                        return FAILURE;
+                    }
+                    if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 1)) != NULL) {
+                        opts->any = zval_is_true(z_tmp);
+                    }
+                } else if (Z_TYPE_P(optval) == IS_LONG) {
+                    if ((opts->count = Z_LVAL_P(optval)) <= 0) {
+                        php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
+                        if (opts->key) zend_string_release(opts->key);
+                        return FAILURE;
+                    }
+                } else {
+                    php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
                     if (opts->key) zend_string_release(opts->key);
                     return FAILURE;
                 }
-
-                /* Set our count */
-                opts->count = Z_LVAL_P(optval);
             } else if (opts->store == STORE_NONE) {
                 opts->store = get_georadius_store_type(zkey);
                 if (opts->store != STORE_NONE) {
@@ -3462,6 +3477,9 @@ void append_georadius_opts(RedisSock *redis_sock, smart_string *str, short *slot
     if (opt->count) {
         REDIS_CMD_APPEND_SSTR_STATIC(str, "COUNT");
         redis_cmd_append_sstr_long(str, opt->count);
+        if (opt->any) {
+            REDIS_CMD_APPEND_SSTR_STATIC(str, "ANY");
+        }
     }
 
     /* Append store options if we've got them */
@@ -3516,7 +3534,7 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     /* Increment argc depending on options */
     argc += gopts.withcoord + gopts.withdist + gopts.withhash +
-            (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0) +
+            (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0) +
             (gopts.store != STORE_NONE ? 2 : 0);
 
     /* Begin construction of our command */
@@ -3585,7 +3603,7 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
 
     /* Increment argc based on options */
     argc += gopts.withcoord + gopts.withdist + gopts.withhash +
-            (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0) +
+            (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0) +
             (gopts.store != STORE_NONE ? 2 : 0);
 
     /* Begin command construction*/
@@ -3631,7 +3649,7 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     size_t keylen, unitlen;
     geoOptions gopts = {0};
     smart_string cmdstr = {0};
-    zval *position, *shape, *opts = NULL, *z_ele;
+    zval *position, *shape, *opts = NULL, *z_ele, *z_tmp;
     zend_string *zkey;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a",
@@ -3665,11 +3683,26 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             ZVAL_DEREF(z_ele);
             if (zkey != NULL) {
                 if (zend_string_equals_literal_ci(zkey, "COUNT")) {
-                    if (Z_TYPE_P(z_ele) != IS_LONG || Z_LVAL_P(z_ele) <= 0) {
-                        php_error_docref(NULL, E_WARNING, "COUNT must be an integer > 0!");
+                    if (Z_TYPE_P(z_ele) == IS_ARRAY) {
+                        if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(z_ele), 0)) == NULL ||
+                            Z_TYPE_P(z_tmp) != IS_LONG ||
+                            (gopts.count = Z_LVAL_P(z_tmp)) <= 0
+                        ) {
+                            php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
+                            return FAILURE;
+                        }
+                        if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(z_ele), 1)) != NULL) {
+                            gopts.any = zval_is_true(z_tmp);
+                        }
+                    } else if (Z_TYPE_P(z_ele) == IS_LONG) {
+                        if ((gopts.count = Z_LVAL_P(z_ele)) <= 0) {
+                            php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
+                            return FAILURE;
+                        }
+                    } else {
+                        php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
                         return FAILURE;
                     }
-                    gopts.count = Z_LVAL_P(z_ele);
                 }
             } else if (Z_TYPE_P(z_ele) == IS_STRING) {
                 if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHCOORD")) {
@@ -3689,7 +3722,7 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     /* Increment argc based on options */
     argc += gopts.withcoord + gopts.withdist + gopts.withhash
-         + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0);
+         + (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0);
 
     REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCH");
     redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot);
@@ -3733,6 +3766,9 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     if (gopts.count) {
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
         redis_cmd_append_sstr_long(&cmdstr, gopts.count);
+        if (gopts.any) {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ANY");
+        }
     }
 
     if ((argc = gopts.withcoord + gopts.withdist + gopts.withhash) > 0) {

From 703d71b530a5e971a50cf1d5818cddcd77e184fa Mon Sep 17 00:00:00 2001
From: Nicolas Grekas 
Date: Tue, 16 Aug 2022 18:06:47 +0200
Subject: [PATCH 1600/1986] Fix typo

---
 Changelog.md                   | 2 +-
 package.xml                    | 6 +++---
 redis_cluster.stub.php         | 2 +-
 redis_cluster_arginfo.h        | 2 +-
 redis_cluster_legacy_arginfo.h | 2 +-
 5 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 90c59af820..2b9cf9bead 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -864,7 +864,7 @@ The main feature of this release is new Streams API implemented by
 
 - Streams API [2c9e0572](https://www.github.com/phpredis/phpredis/commit/2c9e0572), [0b97ec37](https://www.github.com/phpredis/phpredis/commit/0b97ec37) ([Michael Grunder](https://github.com/michael-grunder))
 - Display ini entries in output of phpinfo [908ac4b3](https://www.github.com/phpredis/phpredis/commit/908ac4b3) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
-- Persistant connections can be closed via close method + change reconnection
+- Persistent connections can be closed via close method + change reconnection
   logic [1d997873](https://www.github.com/phpredis/phpredis/commit/1d997873) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
 ### Changed
diff --git a/package.xml b/package.xml
index 2c37887c6e..91163e6df6 100644
--- a/package.xml
+++ b/package.xml
@@ -725,7 +725,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
       * Session module is required [58bd8cc8] (@remicollet)
       * Set default values for ini entries [e206ce9c] (Pavlo Yatsukhnenko)
       * Display ini entries in output of phpinfo [908ac4b3] (Pavlo Yatsukhnenko)
-      * Persistant connections can be closed via close method + change reconnection logic [1d997873] (Pavlo Yatsukhnenko)
+      * Persistent connections can be closed via close method + change reconnection logic [1d997873] (Pavlo Yatsukhnenko)
       * Documentation improvements (@mg, @elcheco, @lucascourot, @nolimitdev, Michael Grunder)
    
    
@@ -815,7 +815,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
     * Add tcp_keepalive option to redis sock [68c58513, 5101172a, 010336d5, 51e48729] (@git-hulk, Michael Grunder)
     * More robust GEORADIUS COUNT validation [f7edee5d] (Michael Grunder)
     * Add LZF compression (experimental) [e2c51251, 8cb2d5bd, 8657557] (Pavlo Yatsukhnenko)
-    * Allow to use empty string as persistant_id [ec4fd1bd] (Pavlo Yatsukhnenko)
+    * Allow to use empty string as persistent_id [ec4fd1bd] (Pavlo Yatsukhnenko)
     * Don't use convert_to_string in redis_hmget_cmd [99335d6] (Pavlo Yatsukhnenko)
     * Allow mixing MULTI and PIPELINE modes (experimental) [5874b0] (Pavlo Yatsukhnenko)
     * PHP >=7.3.0 uses zend_string to store `php_url` elements [b566fb44] (@fmk)
@@ -846,7 +846,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
 
     * Fix segfault when extending Redis class in PHP 5 [d23eff] (Pavlo Yatsukhnenko)
     * Fix RedisCluster constructor with PHP 7 strict scalar type [5c21d7] (Pavlo Yatsukhnenko)
-    * Allow to use empty string as persistant_id [344de5] (Pavlo Yatsukhnenko)
+    * Allow to use empty string as persistent_id [344de5] (Pavlo Yatsukhnenko)
     * Fix cluster_init_seeds. [db1347] (@adlagares)
     * Fix z_seeds may be a reference [42581a] (@janic716)
     * PHP >=7.3 uses zend_string for php_url elements [b566fb] (@fmk)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 80461abcdd..ed0a8293fd 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -8,7 +8,7 @@
 
 class RedisCluster {
 
-    public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistant = false, #[\SensitiveParameter] mixed $auth = NULL, array $context = NULL);
+    public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = NULL, array $context = NULL);
 
     public function _compress(string $value): string;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 189ff76572..ca606b3c53 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -6,7 +6,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seeds, IS_ARRAY, 0, "NULL")
 	ZEND_ARG_TYPE_MASK(0, timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0")
 	ZEND_ARG_TYPE_MASK(0, read_timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistant, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, _IS_BOOL, 0, "false")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "NULL")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL")
 ZEND_END_ARG_INFO()
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 0a5d4daa1f..63ebcd7372 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -6,7 +6,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, seeds)
 	ZEND_ARG_INFO(0, timeout)
 	ZEND_ARG_INFO(0, read_timeout)
-	ZEND_ARG_INFO(0, persistant)
+	ZEND_ARG_INFO(0, persistent)
 	ZEND_ARG_INFO(0, auth)
 	ZEND_ARG_INFO(0, context)
 ZEND_END_ARG_INFO()

From eba1c6d29c167e2019389d2fcfc6a8826213d5fb Mon Sep 17 00:00:00 2001
From: Muhammad Dyas Yaskur 
Date: Fri, 19 Aug 2022 00:52:35 +0700
Subject: [PATCH 1601/1986] Fix Link Typo (#2145)

---
 Changelog.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 2b9cf9bead..70f22987f0 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -9,7 +9,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 
 
-## [5.3.5RC1] - 2021-11-16 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.5RC1), [PECL](https:/pecl.php.net/package/redis/5.3.5RC1))
+## [5.3.5RC1] - 2021-11-16 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.5RC1), [PECL](https://pecl.php.net/package/redis/5.3.5RC1))
 
 ### Sponsors :sparkling_heart:
 
@@ -135,7 +135,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
   [ed283e1ab](https://github.com/phpredis/phpredis/commit/ed283e1ab),
   ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
 
-## [5.3.4] - 2021-03-24 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.4), [PECL](https:/pecl.php.net/package/redis/5.3.4))
+## [5.3.4] - 2021-03-24 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.4), [PECL](https://pecl.php.net/package/redis/5.3.4))
 
 ### Sponsors :sparkling_heart:
 
@@ -162,7 +162,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
   [9b986bf8](https://github.com/phpredis/phpredis/commit/9b986bf81859f5a5983cd148cb15ee6ce292d288)
   ([Michael Grunder](https://github.com/michael-grunder))
 
-## [5.3.3] - 2021-02-01 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.3), [PECL](https:/pecl.php.net/package/redis/5.3.3))
+## [5.3.3] - 2021-02-01 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.3), [PECL](https://pecl.php.net/package/redis/5.3.3))
 
 ### Sponsors :sparkling_heart:
 

From a471c87a3b0fe3f82879b8df0fcfeb869d03e2a2 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 21 Aug 2022 14:59:15 +0300
Subject: [PATCH 1602/1986] Issue #2141

Fix segfault with session+tls
---
 redis_session.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/redis_session.c b/redis_session.c
index 3e3e88164c..b3b3d31feb 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -380,7 +380,7 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_
 PS_OPEN_FUNC(redis)
 {
     php_url *url;
-    zval params, *context = NULL;
+    zval params, context, *zv;
     int i, j, path_len;
 
     redis_pool *pool = ecalloc(1, sizeof(*pool));
@@ -425,6 +425,7 @@ PS_OPEN_FUNC(redis)
                 return FAILURE;
             }
 
+            ZVAL_NULL(&context);
             /* parse parameters */
             if (url->query != NULL) {
                 HashTable *ht;
@@ -450,7 +451,9 @@ PS_OPEN_FUNC(redis)
                 REDIS_CONF_STRING_STATIC(ht, "prefix", &prefix);
                 REDIS_CONF_AUTH_STATIC(ht, "auth", &user, &pass);
 
-                context = REDIS_HASH_STR_FIND_TYPE_STATIC(ht, "stream", IS_ARRAY);
+                if ((zv = REDIS_HASH_STR_FIND_TYPE_STATIC(ht, "stream", IS_ARRAY)) != NULL) {
+                    ZVAL_ZVAL(&context, zv, 1, 0);
+                }
 
                 zval_dtor(¶ms);
             }
@@ -492,9 +495,8 @@ PS_OPEN_FUNC(redis)
                                            persistent, persistent_id ? ZSTR_VAL(persistent_id) : NULL,
                                            retry_interval);
 
-            if (context  != NULL) {
-                redis_sock_set_stream_context(redis_sock, context);
-                context = NULL;
+            if (Z_TYPE(context) == IS_ARRAY) {
+                redis_sock_set_stream_context(redis_sock, &context);
             }
 
             redis_pool_add(pool, redis_sock, weight, db);

From a8d10291a28497e5288b6b7ef9e443ac8290707a Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 4 Sep 2022 21:31:20 +0300
Subject: [PATCH 1603/1986] Redis::client command

---
 library.c              | 151 +++++++++++----
 library.h              |   1 +
 redis.c                |  57 +-----
 redis.stub.php         |   2 +-
 redis_arginfo.h        |   4 +-
 redis_commands.c       | 415 +++++++++++++++++++++++++++++++++++++++++
 redis_commands.h       |   3 +
 redis_legacy_arginfo.h |   4 +-
 tests/RedisTest.php    |  22 ++-
 9 files changed, 565 insertions(+), 94 deletions(-)

diff --git a/library.c b/library.c
index 782f81bfac..2fbc4ae8c0 100644
--- a/library.c
+++ b/library.c
@@ -1196,6 +1196,67 @@ redis_parse_info_response(char *response, zval *z_ret)
     }
 }
 
+static void
+redis_parse_client_info(char *info, zval *z_ret)
+{
+    char *p1, *s1 = NULL;
+
+    ZVAL_FALSE(z_ret);
+    if ((p1 = php_strtok_r(info, " ", &s1)) != NULL) {
+        array_init(z_ret);
+        do {
+            char *p;
+            zend_uchar type;
+            zend_long lval;
+            double dval;
+            if ((p = strchr(p1, '=')) != NULL) {
+                type = is_numeric_string(p + 1, strlen(p + 1), &lval, &dval, 0);
+                switch (type) {
+                case IS_LONG:
+                    add_assoc_long_ex(z_ret, p1, p - p1, lval);
+                    break;
+                case IS_DOUBLE:
+                    add_assoc_double_ex(z_ret, p1, p - p1, dval);
+                    break;
+                default:
+                    add_assoc_string_ex(z_ret, p1, p - p1, p + 1);
+                }
+            } else {
+                add_next_index_string(z_ret, p1);
+            }
+        } while ((p1 = php_strtok_r(NULL, " ", &s1)) != NULL);
+    }
+}
+
+static int
+redis_client_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    char *resp;
+    int resp_len;
+    zval z_ret;
+
+    /* Make sure we can read the bulk response from Redis */
+    if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
+        RETVAL_FALSE;
+        return FAILURE;
+    }
+
+    /* Parse it out */
+    redis_parse_client_info(resp, &z_ret);
+
+    /* Free our response */
+    efree(resp);
+
+    /* Return or append depending if we're atomic */
+    if (IS_ATOMIC(redis_sock)) {
+        RETVAL_ZVAL(&z_ret, 0, 1);
+    } else {
+        add_next_index_zval(z_tab, &z_ret);
+    }
+
+    return SUCCESS;
+}
+
 /*
  * Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code
  * to handle.
@@ -1210,11 +1271,13 @@ redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zva
     if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
         RETVAL_FALSE;
         return FAILURE;
+    } else if (resp_len > 0) {
+        /* Parse it out */
+        redis_parse_client_list_response(resp, &z_ret);
+    } else {
+        array_init(&z_ret);
     }
 
-    /* Parse it out */
-    redis_parse_client_list_response(resp, &z_ret);
-
     /* Free our response */
     efree(resp);
 
@@ -1231,42 +1294,16 @@ redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zva
 PHP_REDIS_API void
 redis_parse_client_list_response(char *response, zval *z_ret)
 {
-    char *p1, *s1 = NULL;
+    char *p, *s = NULL;
 
     ZVAL_FALSE(z_ret);
-    if ((p1 = php_strtok_r(response, _NL, &s1)) != NULL) {
+    if ((p = php_strtok_r(response, _NL, &s)) != NULL) {
         array_init(z_ret);
         do {
-            char *p2, *s2 = NULL;
             zval z_sub;
-
-            ZVAL_FALSE(&z_sub);
-            if ((p2 = php_strtok_r(p1, " ", &s2)) != NULL) {
-                array_init(&z_sub);
-                do {
-                    char *p;
-                    zend_uchar type;
-                    zend_long lval;
-                    double dval;
-                    if ((p = strchr(p2, '=')) != NULL) {
-                        type = is_numeric_string(p + 1, strlen(p + 1), &lval, &dval, 0);
-                        switch (type) {
-                        case IS_LONG:
-                            add_assoc_long_ex(&z_sub, p2, p - p2, lval);
-                            break;
-                        case IS_DOUBLE:
-                            add_assoc_double_ex(&z_sub, p2, p - p2, dval);
-                            break;
-                        default:
-                            add_assoc_string_ex(&z_sub, p2, p - p2, p + 1);
-                        }
-                    } else {
-                        add_next_index_string(&z_sub, p2);
-                    }
-                } while ((p2 = php_strtok_r(NULL, " ", &s2)) != NULL);
-            }
+            redis_parse_client_info(p, &z_sub);
             add_next_index_zval(z_ret, &z_sub);
-        } while ((p1 = php_strtok_r(NULL, _NL, &s1)) != NULL);
+        } while ((p = php_strtok_r(NULL, _NL, &s)) != NULL);
     }
 }
 
@@ -1629,6 +1666,54 @@ redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+static int
+redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    int numElems;
+    zval z_ret;
+
+    if (read_mbulk_header(redis_sock, &numElems) < 0) {
+        if (IS_ATOMIC(redis_sock)) {
+            RETVAL_FALSE;
+        } else {
+            add_next_index_bool(z_tab, 0);
+        }
+        return FAILURE;
+    }
+
+    array_init(&z_ret);
+    redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret);
+    array_zip_values_and_scores(redis_sock, &z_ret, 0);
+
+    if (IS_ATOMIC(redis_sock)) {
+        RETVAL_ZVAL(&z_ret, 0, 1);
+    } else {
+        add_next_index_zval(z_tab, &z_ret);
+    }
+
+    return SUCCESS;
+}
+
+PHP_REDIS_API int
+redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    if (ctx == NULL) {
+        return redis_client_info_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        return redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 1) {
+        return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 2) {
+        return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 3) {
+        return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 4) {
+        return redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
+    }
+}
+
 /* Helper function to consume Redis stream message data.  This is useful for
  * multiple stream callers (e.g. XREAD[GROUP], and X[REV]RANGE handlers). */
 PHP_REDIS_API int
diff --git a/library.h b/library.h
index 48164f09b1..a632a84e19 100644
--- a/library.h
+++ b/library.h
@@ -169,6 +169,7 @@ PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSo
 PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 
 /* Helper methods to get configuration values from a HashTable. */
 
diff --git a/redis.c b/redis.c
index 30e8f40171..8499d33eff 100644
--- a/redis.c
+++ b/redis.c
@@ -3286,62 +3286,11 @@ PHP_METHOD(Redis, getAuth) {
     }
 }
 
-/*
- * $redis->client('list');
- * $redis->client('info');
- * $redis->client('kill', );
- * $redis->client('setname', );
- * $redis->client('getname');
- */
+/* {{{ proto mixed Redis::client(string $command, [ $arg1 ... $argN]) */
 PHP_METHOD(Redis, client) {
-    zval *object;
-    RedisSock *redis_sock;
-    char *cmd, *opt = NULL, *arg = NULL;
-    size_t opt_len, arg_len;
-    int cmd_len;
-
-    // Parse our method parameters
-    if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                    "Os|s", &object, redis_ce, &opt, &opt_len,
-                                    &arg, &arg_len) == FAILURE)
-    {
-        RETURN_FALSE;
-    }
-
-    /* Grab our socket */
-    if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
-        RETURN_FALSE;
-    }
-
-    /* Build our CLIENT command */
-    if (ZEND_NUM_ARGS() == 2) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len);
-    } else {
-        cmd_len = REDIS_SPPRINTF(&cmd, "CLIENT", "s", opt, opt_len);
-    }
-
-    /* Execute our queue command */
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
-
-    /* We handle CLIENT LIST with a custom response function */
-    if(!strncasecmp(opt, "list", 4)) {
-        if (IS_ATOMIC(redis_sock)) {
-            redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, NULL, NULL);
-        }
-        REDIS_PROCESS_RESPONSE(redis_client_list_reply);
-    } else if (!strncasecmp(opt, "info", 4)) {
-        if (IS_ATOMIC(redis_sock)) {
-            redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
-        }
-        REDIS_PROCESS_RESPONSE(redis_string_response);
-    } else {
-        if (IS_ATOMIC(redis_sock)) {
-            redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                redis_sock,NULL,NULL);
-        }
-        REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
-    }
+    REDIS_PROCESS_CMD(client, redis_client_response);
 }
+/* }}} */
 
 /* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */
 PHP_METHOD(Redis, rawcommand) {
diff --git a/redis.stub.php b/redis.stub.php
index 16b6164c25..4a2751b3e8 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -64,7 +64,7 @@ public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed ..
 
     public function clearLastError(): bool;
 
-    public function client(string $opt, string $arg = null): mixed;
+    public function client(string $opt, mixed ...$args): mixed;
 
     public function close(): bool;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index b5f67ad0a9..35ea8d587e 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7fc0b991dc8404945a0081aef8a422c9c670eab9 */
+ * Stub hash: 8d3ef188b058066309394ffaaf00489572d7b629 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -91,7 +91,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_client, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, opt, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 0, "null")
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_close arginfo_class_Redis_bgSave
diff --git a/redis_commands.c b/redis_commands.c
index ea1eedc779..a208119aac 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4028,6 +4028,421 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 1, 0, cmd, cmd_len, slot);
 }
 
+static int
+redis_build_client_list_command(smart_string *cmdstr, int argc, zval *z_args)
+{
+    zend_string *zkey;
+    zval *z_ele, *type = NULL, *id = NULL;
+
+    if (argc > 1) {
+        if (Z_TYPE(z_args[1]) != IS_ARRAY) {
+            return FAILURE;
+        }
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "type")) {
+                    if (Z_TYPE_P(z_ele) != IS_STRING || (
+                        !ZVAL_STRICMP_STATIC(z_ele, "normal") &&
+                        !ZVAL_STRICMP_STATIC(z_ele, "master") &&
+                        !ZVAL_STRICMP_STATIC(z_ele, "replica") &&
+                        !ZVAL_STRICMP_STATIC(z_ele, "pubsub")
+                    )) {
+                        return FAILURE;
+                    }
+                    type = z_ele;
+                } else if (zend_string_equals_literal_ci(zkey, "id")) {
+                    if (Z_TYPE_P(z_ele) != IS_STRING && (
+                        Z_TYPE_P(z_ele) != IS_ARRAY ||
+                        !zend_hash_num_elements(Z_ARRVAL_P(z_ele))
+                    )) {
+                        return FAILURE;
+                    }
+                    id = z_ele;
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+    REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 1 + (type ? 2 : 0) + (
+        id ? (Z_TYPE_P(id) == IS_ARRAY ? 1 + zend_hash_num_elements(Z_ARRVAL_P(id)) : 2) : 0
+    ), "CLIENT");
+    REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "LIST");
+    if (type != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TYPE");
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(type), Z_STRLEN_P(type));
+    }
+    if (id != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ID");
+        if (Z_TYPE_P(id) == IS_ARRAY) {
+            ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(id), z_ele) {
+                if (Z_TYPE_P(z_ele) == IS_STRING) {
+                    redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele));
+                } else {
+                    zkey = zval_get_string(z_ele);
+                    redis_cmd_append_sstr(cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey));
+                    zend_string_release(zkey);
+                }
+            } ZEND_HASH_FOREACH_END();
+        } else {
+            redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(id), Z_STRLEN_P(id));
+        }
+    }
+    return SUCCESS;
+}
+
+static int
+redis_build_client_kill_command(smart_string *cmdstr, int argc, zval *z_args)
+{
+    zend_string *zkey;
+    zval *z_ele, *id = NULL, *type = NULL, *address = NULL, *opts = NULL,
+        *user = NULL, *addr = NULL, *laddr = NULL, *skipme = NULL;
+
+    if (argc > 1) {
+        if (argc > 2) {
+            if (Z_TYPE(z_args[1]) != IS_STRING || Z_TYPE(z_args[2]) != IS_ARRAY) {
+                return FAILURE;
+            }
+            address = &z_args[1];
+            opts = &z_args[2];
+        } else if (Z_TYPE(z_args[1]) == IS_STRING) {
+            address = &z_args[1];
+        } else if (Z_TYPE(z_args[1]) == IS_ARRAY) {
+            opts = &z_args[1];
+        } else {
+            return FAILURE;
+        }
+        if (opts != NULL) {
+            ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
+                if (zkey != NULL) {
+                    ZVAL_DEREF(z_ele);
+                    if (Z_TYPE_P(z_ele) != IS_STRING) {
+                        return FAILURE;
+                    }
+                    if (zend_string_equals_literal_ci(zkey, "id")) {
+                        id = z_ele;
+                    } else if (zend_string_equals_literal_ci(zkey, "type")) {
+                        if (!ZVAL_STRICMP_STATIC(z_ele, "normal") &&
+                            !ZVAL_STRICMP_STATIC(z_ele, "master") &&
+                            !ZVAL_STRICMP_STATIC(z_ele, "slave") &&
+                            !ZVAL_STRICMP_STATIC(z_ele, "replica") &&
+                            !ZVAL_STRICMP_STATIC(z_ele, "pubsub")
+                        ) {
+                            return FAILURE;
+                        }
+                        type = z_ele;
+                    } else if (zend_string_equals_literal_ci(zkey, "user")) {
+                        user = z_ele;
+                    } else if (zend_string_equals_literal_ci(zkey, "addr")) {
+                        addr = z_ele;
+                    } else if (zend_string_equals_literal_ci(zkey, "laddr")) {
+                        laddr = z_ele;
+                    } else if (zend_string_equals_literal_ci(zkey, "skipme")) {
+                        if (!ZVAL_STRICMP_STATIC(z_ele, "yes") &&
+                            !ZVAL_STRICMP_STATIC(z_ele, "no")
+                        ) {
+                            return FAILURE;
+                        }
+                        skipme = z_ele;
+                    }
+                }
+            } ZEND_HASH_FOREACH_END();
+        }
+    }
+    REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 1 + (address != 0) + (id ? 2 : 0)
+        + (type ? 2 : 0) + (user ? 2 : 0) + (addr ? 2 : 0) + (laddr ? 2 : 0)
+        + (skipme ? 2 : 0), "CLIENT");
+    REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "KILL");
+    if (address != NULL) {
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(address), Z_STRLEN_P(address));
+    }
+    if (id != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ID");
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(id), Z_STRLEN_P(id));
+    }
+    if (type != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TYPE");
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(type), Z_STRLEN_P(type));
+    }
+    if (user != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "USER");
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(user), Z_STRLEN_P(user));
+    }
+    if (addr != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ADDR");
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(addr), Z_STRLEN_P(addr));
+    }
+    if (laddr != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "LADDR");
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(laddr), Z_STRLEN_P(laddr));
+    }
+    if (skipme != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "SKIPME");
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(skipme), Z_STRLEN_P(skipme));
+    }
+    return SUCCESS;
+}
+
+static int
+redis_build_client_tracking_command(smart_string *cmdstr, int argc, zval *z_args)
+{
+    zend_string *zkey;
+    zval *z_ele, *redirect = NULL, *prefix = NULL;
+    zend_bool bcast = 0, optin = 0, optout = 0, noloop = 0;
+
+    if (argc < 2) {
+        return FAILURE;
+    }
+    if (argc > 2) {
+        if (Z_TYPE(z_args[2]) != IS_ARRAY) {
+            return FAILURE;
+        }
+        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[2]), zkey, z_ele) {
+            if (zkey != NULL) {
+                ZVAL_DEREF(z_ele);
+                if (zend_string_equals_literal_ci(zkey, "redirect")) {
+                    if (Z_TYPE_P(z_ele) != IS_STRING) {
+                        return FAILURE;
+                    }
+                    redirect = z_ele;
+                } else if (zend_string_equals_literal_ci(zkey, "prefix")) {
+                    if (Z_TYPE_P(z_ele) != IS_STRING && Z_TYPE_P(z_ele) != IS_ARRAY) {
+                        return FAILURE;
+                    }
+                    prefix = z_ele;
+                } else if (zend_string_equals_literal_ci(zkey, "bcast")) {
+                    bcast = zval_is_true(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "optin")) {
+                    optin = zval_is_true(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "optout")) {
+                    optout = zval_is_true(z_ele);
+                } else if (zend_string_equals_literal_ci(zkey, "noloop")) {
+                    noloop = zval_is_true(z_ele);
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+    REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 2 + (redirect ? 2 : 0)
+        + (prefix ? 2 * zend_hash_num_elements(Z_ARRVAL_P(prefix)) : 0)
+        + bcast + optin + optout + noloop, "CLIENT");
+    REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TRACKING");
+    if (Z_TYPE(z_args[1]) == IS_STRING && (
+        ZVAL_STRICMP_STATIC(&z_args[1], "on") ||
+        ZVAL_STRICMP_STATIC(&z_args[1], "off")
+    )) {
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
+    } else if (zval_is_true(&z_args[1])) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ON");
+    } else {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OFF");
+    }
+    if (redirect != NULL) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "REDIRECT");
+        redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(redirect), Z_STRLEN_P(redirect));
+    }
+    if (prefix != NULL) {
+        if (Z_TYPE_P(prefix) == IS_ARRAY) {
+            ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(prefix), z_ele) {
+                REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PREFIX");
+                if (Z_TYPE_P(z_ele) == IS_STRING) {
+                    redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele));
+                } else {
+                    zkey = zval_get_string(z_ele);
+                    redis_cmd_append_sstr(cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey));
+                    zend_string_release(zkey);
+                }
+            } ZEND_HASH_FOREACH_END();
+        } else {
+            REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PREFIX");
+            redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(prefix), Z_STRLEN_P(prefix));
+        }
+    }
+    if (bcast) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "BCAST");
+    }
+    if (optin) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OPTIN");
+    }
+    if (optout) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OPTOUT");
+    }
+    if (noloop) {
+        REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "NOLOOP");
+    }
+    return SUCCESS;
+}
+
+int
+redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                 char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    int argc;
+    smart_string cmdstr = {0};
+    zval *z_args;
+
+    if ((argc = ZEND_NUM_ARGS()) < 1) {
+        return FAILURE;
+    }
+
+    z_args = ecalloc(argc, sizeof(*z_args));
+    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE ||
+        Z_TYPE(z_args[0]) != IS_STRING
+    ) {
+        efree(z_args);
+        return FAILURE;
+    }
+
+    if (ZVAL_STRICMP_STATIC(&z_args[0], "info")) {
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "INFO");
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "list")) {
+        if (redis_build_client_list_command(&cmdstr, argc, z_args) != 0) {
+            efree(z_args);
+            return FAILURE;
+        }
+        *ctx = PHPREDIS_CTX_PTR;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "caching")) {
+        if (argc < 2) {
+            efree(z_args);
+            return FAILURE;
+        }
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "CACHING");
+        if (Z_TYPE(z_args[1]) == IS_STRING && (
+            ZVAL_STRICMP_STATIC(&z_args[1], "yes") ||
+            ZVAL_STRICMP_STATIC(&z_args[1], "no")
+        )) {
+            redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
+        } else if (zval_is_true(&z_args[1])) {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "YES");
+        } else {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO");
+        }
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "getname")) {
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GETNAME");
+        *ctx = PHPREDIS_CTX_PTR + 3;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "getredir") || ZVAL_STRICMP_STATIC(&z_args[0], "id")) {
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT");
+        redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0]));
+        *ctx = PHPREDIS_CTX_PTR + 2;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "kill")) {
+        if (redis_build_client_kill_command(&cmdstr, argc, z_args) != 0) {
+            efree(z_args);
+            return FAILURE;
+        }
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "no-evict")) {
+        if (argc < 2) {
+            efree(z_args);
+            return FAILURE;
+        }
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO-EVICT");
+        if (Z_TYPE(z_args[1]) == IS_STRING && (
+            ZVAL_STRICMP_STATIC(&z_args[1], "on") ||
+            ZVAL_STRICMP_STATIC(&z_args[1], "off")
+        )) {
+            redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
+        } else if (zval_is_true(&z_args[1])) {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ON");
+        } else {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "OFF");
+        }
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "pause")) {
+        if (argc < 2 || Z_TYPE(z_args[1]) != IS_LONG || (
+            argc > 2 && (
+                Z_TYPE(z_args[2]) != IS_STRING || (
+                    !ZVAL_STRICMP_STATIC(&z_args[2], "write") &&
+                    !ZVAL_STRICMP_STATIC(&z_args[2], "all")
+                )
+            )
+        )) {
+            efree(z_args);
+            return FAILURE;
+        }
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 2 ? 3 : 2, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "PAUSE");
+        redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(z_args[1]));
+        if (argc > 2) {
+            redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[2]), Z_STRLEN(z_args[2]));
+        }
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "reply")) {
+        if (argc > 1 && (
+            Z_TYPE(z_args[1]) != IS_STRING || (
+                !ZVAL_STRICMP_STATIC(&z_args[1], "on") &&
+                !ZVAL_STRICMP_STATIC(&z_args[1], "off") &&
+                !ZVAL_STRICMP_STATIC(&z_args[1], "skip")
+            )
+        )) {
+            efree(z_args);
+            return FAILURE;
+        }
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 1 ? 2 : 1, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REPLY");
+        if (argc > 1) {
+            redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
+        }
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "setname")) {
+        if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING) {
+            efree(z_args);
+            return FAILURE;
+        }
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SETNAME");
+        redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "tracking")) {
+        if (redis_build_client_tracking_command(&cmdstr, argc, z_args) != 0) {
+            efree(z_args);
+            return FAILURE;
+        }
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "trackinginfo")) {
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TRACKINGINFO");
+        *ctx = PHPREDIS_CTX_PTR + 4;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "unblock")) {
+        if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING || (
+            argc > 2 && (
+                Z_TYPE(z_args[2]) != IS_STRING || (
+                    !ZVAL_STRICMP_STATIC(&z_args[2], "timeout") &&
+                    !ZVAL_STRICMP_STATIC(&z_args[2], "error")
+                )
+            )
+        )) {
+            efree(z_args);
+            return FAILURE;
+        }
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 2 ? 3 : 2, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "UNBLOCK");
+        redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
+        if (argc > 2) {
+            redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[2]), Z_STRLEN(z_args[2]));
+        }
+        *ctx = PHPREDIS_CTX_PTR + 2;
+    } else if (ZVAL_STRICMP_STATIC(&z_args[0], "unpause")) {
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT");
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "UNPAUSE");
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else {
+        efree(z_args);
+        return FAILURE;
+    }
+
+    // Push out values
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    // Cleanup arg array
+    efree(z_args);
+
+    return SUCCESS;
+}
+
 /* COMMAND */
 int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index e412d82834..662d0500ed 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -293,6 +293,9 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 5cced37aac..11803eb465 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7fc0b991dc8404945a0081aef8a422c9c670eab9 */
+ * Stub hash: 8d3ef188b058066309394ffaaf00489572d7b629 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -86,7 +86,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_client, 0, 0, 1)
 	ZEND_ARG_INFO(0, opt)
-	ZEND_ARG_INFO(0, arg)
+	ZEND_ARG_VARIADIC_INFO(0, args)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_close arginfo_class_Redis___destruct
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index de76e9b459..c2e413ae4e 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1991,8 +1991,26 @@ public function testClient() {
         /* CLIENT GETNAME */
         $this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests');
 
-        if (version_compare($this->version, "6.2.0") >= 0) {
-            $this->assertFalse(empty($this->redis->client('info')));
+        if (version_compare($this->version, '5.0.0') >= 0) {
+            $this->assertLess(0, $this->redis->client('id'));
+            if (version_compare($this->version, '6.0.0') >= 0) {
+                $this->assertEquals($this->redis->client('getredir'), -1);
+                $this->assertTrue($this->redis->client('tracking', 'on', ['optin' => true]));
+                $this->assertEquals($this->redis->client('getredir'), 0);
+                $this->assertTrue($this->redis->client('caching', 'yes'));
+                $this->assertTrue($this->redis->client('tracking', 'off'));
+                if (version_compare($this->version, '6.2.0') >= 0) {
+                    $this->assertFalse(empty($this->redis->client('info')));
+                    $this->assertEquals($this->redis->client('trackinginfo'), [
+                        'flags' => ['off'],
+                        'redirect' => -1,
+                        'prefixes' => [],
+                    ]);
+                    if (version_compare($this->version, '7.0.0') >= 0) {
+                        $this->assertTrue($this->redis->client('no-evict', 'on'));
+                    }
+                }
+            }
         }
 
         /* CLIENT KILL -- phpredis will reconnect, so we can do this */

From 0f502c9ec57d5fcadadb001d9a36fb93c9bff9f9 Mon Sep 17 00:00:00 2001
From: sergkash7 <55360924+sergkash7@users.noreply.github.com>
Date: Thu, 8 Sep 2022 23:20:00 +0300
Subject: [PATCH 1604/1986] Update redis.stub.php (#2120)

---
 redis.stub.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.stub.php b/redis.stub.php
index 4a2751b3e8..eb269ab5cc 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -144,7 +144,7 @@ public function geosearch(string $key, array|string $position, array|int|float $
 
     public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): array;
 
-	/** @return string|Redis */
+	/** @return false|string|Redis */
     public function get(string $key);
 
     public function getAuth(): mixed;

From b3ce0486690a97832ac02504638d57831eae00ef Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Wed, 14 Sep 2022 22:54:22 -0700
Subject: [PATCH 1605/1986] Fix non standards conforming prototypes. (#2150)

These now generate warnings with GCC 13
---
 cluster_library.c | 4 ++--
 cluster_library.h | 4 ++--
 redis_session.c   | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 2512f0fd60..b14b1ac44d 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -359,7 +359,7 @@ static void cluster_dist_free_ht(zval *p) {
 }
 
 /* Spin up a HashTable that will contain distribution lists */
-HashTable *cluster_dist_create() {
+HashTable *cluster_dist_create(void) {
     HashTable *ret;
 
     ALLOC_HASHTABLE(ret);
@@ -375,7 +375,7 @@ void cluster_dist_free(HashTable *ht) {
 }
 
 /* Create a clusterDistList object */
-static clusterDistList *cluster_dl_create() {
+static clusterDistList *cluster_dl_create(void) {
     clusterDistList *dl;
 
     dl        = emalloc(sizeof(clusterDistList));
diff --git a/cluster_library.h b/cluster_library.h
index 995e244fc8..fe2962f06f 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -336,7 +336,7 @@ clusterReply *cluster_read_sock_resp(RedisSock *redis_sock,
 void cluster_free_reply(clusterReply *reply, int free_data);
 
 /* Cluster distribution helpers for WATCH */
-HashTable *cluster_dist_create();
+HashTable *cluster_dist_create(void);
 void cluster_dist_free(HashTable *ht);
 int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key,
     size_t key_len, clusterKeyVal **kv);
@@ -354,7 +354,7 @@ unsigned short cluster_hash_key_zval(zval *key);
 unsigned short cluster_hash_key(const char *key, int len);
 
 /* Validate and sanitize cluster construction args */
-zend_string** cluster_validate_args(double timeout, double read_timeout, 
+zend_string** cluster_validate_args(double timeout, double read_timeout,
     HashTable *seeds, uint32_t *nseeds, char **errstr);
 
 void free_seed_array(zend_string **seeds, uint32_t nseeds);
diff --git a/redis_session.c b/redis_session.c
index b3b3d31feb..192f890cf0 100644
--- a/redis_session.c
+++ b/redis_session.c
@@ -129,7 +129,7 @@ redis_pool_free(redis_pool *pool) {
 }
 
 /* Retreive session.gc_maxlifetime from php.ini protecting against an integer overflow */
-static int session_gc_maxlifetime() {
+static int session_gc_maxlifetime(void) {
     zend_long value = INI_INT("session.gc_maxlifetime");
     if (value > INT_MAX) {
         php_error_docref(NULL, E_NOTICE, "session.gc_maxlifetime overflows INT_MAX, truncating.");

From a3d2f1319daf94505343473fe600b4eea5959560 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Thu, 15 Sep 2022 01:29:31 -0700
Subject: [PATCH 1606/1986] Add 'BIT'/'BYTE' modifier to BITCOUNT + tests
 (#2149)

BITCOUNT can take a third optional argument ('BIT', or 'BYTE').

Additionally add a specific test for BITCOUNT that tests all of the
legal ways it can be called in PhpRedis.
---
 redis.stub.php         |  2 +-
 redis_arginfo.h        |  3 ++-
 redis_commands.c       | 14 ++++++++++----
 redis_legacy_arginfo.h |  3 ++-
 tests/RedisTest.php    | 24 ++++++++++++++++++++++++
 5 files changed, 39 insertions(+), 7 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index eb269ab5cc..ba8febf15e 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -42,7 +42,7 @@ public function bgSave(): bool;
     public function bgrewriteaof(): bool;
 
     /** @return int|Redis */
-    public function bitcount(string $key, int $start = 0, int $end = -1);
+    public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false);
 
     /**
      * @return int|Redis
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 35ea8d587e..fff2314db8 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8d3ef188b058066309394ffaaf00489572d7b629 */
+ * Stub hash: b9da355c27e6fb1b776164d40a521703e31713b5 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -53,6 +53,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bitop, 0, 3, IS_LONG, 0)
diff --git a/redis_commands.c b/redis_commands.c
index a208119aac..b4a788b32d 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2267,15 +2267,21 @@ int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *key;
     size_t key_len;
     zend_long start = 0, end = -1;
+    zend_bool isbit = 0;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &key, &key_len,
-                             &start, &end) == FAILURE)
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|llb", &key, &key_len,
+                             &start, &end, &isbit) == FAILURE)
     {
         return FAILURE;
     }
 
-    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITCOUNT", "kdd", key, key_len,
-                                 (int)start, (int)end);
+    if (isbit) {
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITCOUNT", "kdds", key, key_len,
+                                     (int)start, (int)end, "BIT", 3);
+    } else {
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITCOUNT", "kdd", key, key_len,
+                                     (int)start, (int)end);
+    }
 
     return SUCCESS;
 }
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 11803eb465..4e744ee677 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8d3ef188b058066309394ffaaf00489572d7b629 */
+ * Stub hash: b9da355c27e6fb1b776164d40a521703e31713b5 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -48,6 +48,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, bybit)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitop, 0, 0, 3)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c2e413ae4e..5e234de6d2 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -206,6 +206,30 @@ public function testPubSub() {
         $this->assertFalse($this->redis->pubsub("numsub", "not-an-array"));
     }
 
+    /* These test cases were generated randomly.  We're just trying to test
+       that PhpRedis handles all combination of arguments correctly. */
+    public function testBitcount() {
+        /* key */
+        $this->redis->set('bitcountkey', hex2bin('bd906b854ca76cae'));
+        $this->assertEquals(33, $this->redis->bitcount('bitcountkey'));
+
+        /* key, start */
+        $this->redis->set('bitcountkey', hex2bin('400aac171382a29bebaab554f178'));
+        $this->assertEquals(4, $this->redis->bitcount('bitcountkey', 13));
+
+        /* key, start, end */
+        $this->redis->set('bitcountkey', hex2bin('b1f32405'));
+        $this->assertEquals(2, $this->redis->bitcount('bitcountkey', 3, 3));
+
+        /* key, start, end BYTE */
+        $this->redis->set('bitcountkey', hex2bin('10eb8939e68bfdb640260f0629f3'));
+        $this->assertEquals(1, $this->redis->bitcount('bitcountkey', 8, 8, false));
+
+        /* key, start, end, BIT */
+        $this->redis->set('bitcountkey', hex2bin('cd0e4c80f9e4590d888a10'));
+        $this->assertEquals(5, $this->redis->bitcount('bitcountkey', 0, 9, true));
+    }
+
     public function testBitsets() {
 
         $this->redis->del('key');

From d67b2020e59429cf4dbd89c71a440a92dddfec5c Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 15 Sep 2022 08:52:58 -0700
Subject: [PATCH 1607/1986] Pull COUNT/ANY parsing into a helper function.

See #1894
---
 redis_commands.c | 93 +++++++++++++++++++++++-------------------------
 1 file changed, 45 insertions(+), 48 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 1de298d800..116a63235d 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3376,6 +3376,40 @@ geoStoreType get_georadius_store_type(zend_string *key) {
     return STORE_NONE;
 }
 
+/* Helper function to get COUNT and possible ANY flag which is passable to
+ * both GEORADIUS and GEOSEARCH */
+static int get_georadius_count_options(zval *optval, geoOptions *opts) {
+    zval *z_tmp;
+
+    /* Short circuit on bad options */
+    if (Z_TYPE_P(optval) != IS_ARRAY && Z_TYPE_P(optval) != IS_LONG)
+        goto error;
+
+    if (Z_TYPE_P(optval) == IS_ARRAY) {
+        z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 0);
+        if (z_tmp) {
+            if (Z_TYPE_P(z_tmp) != IS_LONG || Z_LVAL_P(z_tmp) <= 0)
+                goto error;
+            opts->count = Z_LVAL_P(z_tmp);
+        }
+
+        z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 1);
+        if (z_tmp) {
+            opts->any = zval_is_true(z_tmp);
+        }
+    } else {
+        if (Z_LVAL_P(optval) <= 0)
+            goto error;
+        opts->count = Z_LVAL_P(optval);
+    }
+
+    return SUCCESS;
+
+error:
+    php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
+    return FAILURE;
+}
+
 /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */
 static int get_georadius_opts(HashTable *ht, geoOptions *opts) {
     char *optstr;
@@ -3389,26 +3423,7 @@ static int get_georadius_opts(HashTable *ht, geoOptions *opts) {
         /* If the key is numeric it's a non value option */
         if (zkey) {
             if (zend_string_equals_literal_ci(zkey, "COUNT")) {
-                if (Z_TYPE_P(optval) == IS_ARRAY) {
-                    if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 0)) == NULL ||
-                        Z_TYPE_P(z_tmp) != IS_LONG ||
-                        (opts->count = Z_LVAL_P(z_tmp)) <= 0
-                    ) {
-                        php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
-                        if (opts->key) zend_string_release(opts->key);
-                        return FAILURE;
-                    }
-                    if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 1)) != NULL) {
-                        opts->any = zval_is_true(z_tmp);
-                    }
-                } else if (Z_TYPE_P(optval) == IS_LONG) {
-                    if ((opts->count = Z_LVAL_P(optval)) <= 0) {
-                        php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
-                        if (opts->key) zend_string_release(opts->key);
-                        return FAILURE;
-                    }
-                } else {
-                    php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
+                if (get_georadius_count_options(optval, opts) == FAILURE) {
                     if (opts->key) zend_string_release(opts->key);
                     return FAILURE;
                 }
@@ -3650,7 +3665,7 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     geoOptions gopts = {0};
     smart_string cmdstr = {0};
     zval *position, *shape, *opts = NULL, *z_ele, *z_tmp;
-    zend_string *zkey;
+    zend_string *zkey, *zstr;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a",
                               &key, &keylen, &position, &shape,
@@ -3681,39 +3696,21 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     if (opts != NULL) {
         ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
             ZVAL_DEREF(z_ele);
-            if (zkey != NULL) {
-                if (zend_string_equals_literal_ci(zkey, "COUNT")) {
-                    if (Z_TYPE_P(z_ele) == IS_ARRAY) {
-                        if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(z_ele), 0)) == NULL ||
-                            Z_TYPE_P(z_tmp) != IS_LONG ||
-                            (gopts.count = Z_LVAL_P(z_tmp)) <= 0
-                        ) {
-                            php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
-                            return FAILURE;
-                        }
-                        if ((z_tmp = zend_hash_index_find(Z_ARRVAL_P(z_ele), 1)) != NULL) {
-                            gopts.any = zval_is_true(z_tmp);
-                        }
-                    } else if (Z_TYPE_P(z_ele) == IS_LONG) {
-                        if ((gopts.count = Z_LVAL_P(z_ele)) <= 0) {
-                            php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
-                            return FAILURE;
-                        }
-                    } else {
-                        php_error_docref(NULL, E_WARNING, "Invalid COUNT value");
-                        return FAILURE;
-                    }
+            if (zkey != NULL && zend_string_equals_literal_ci(zkey, "COUNT")) {
+                if (get_georadius_count_options(z_ele, &gopts) == FAILURE) {
+                    return FAILURE;
                 }
             } else if (Z_TYPE_P(z_ele) == IS_STRING) {
-                if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHCOORD")) {
+                zstr = Z_STR_P(z_ele);
+                if (zend_string_equals_literal_ci(zstr, "WITHCOORD")) {
                     gopts.withcoord = 1;
-                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHDIST")) {
+                } else if (zend_string_equals_literal_ci(zstr, "WITHDIST")) {
                     gopts.withdist = 1;
-                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHHASH")) {
+                } else if (zend_string_equals_literal_ci(zstr, "WITHHASH")) {
                     gopts.withhash = 1;
-                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "ASC")) {
+                } else if (zend_string_equals_literal_ci(zstr, "ASC")) {
                     gopts.sort = SORT_ASC;
-                } else if (!strcasecmp(Z_STRVAL_P(z_ele), "DESC")) {
+                } else if (zend_string_equals_literal_ci(zstr, "DESC")) {
                     gopts.sort = SORT_DESC;
                 }
             }

From fb6a297ccc4270f8235d8f689660de573e26c4cb Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 15 Sep 2022 09:37:48 -0700
Subject: [PATCH 1608/1986] CodeQL fixes

---
 redis_commands.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 116a63235d..b6da414f35 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3412,9 +3412,9 @@ static int get_georadius_count_options(zval *optval, geoOptions *opts) {
 
 /* Helper function to extract optional arguments for GEORADIUS and GEORADIUSBYMEMBER */
 static int get_georadius_opts(HashTable *ht, geoOptions *opts) {
-    char *optstr;
     zend_string *zkey;
-    zval *optval, *z_tmp;
+    char *optstr;
+    zval *optval;
 
     /* Iterate over our argument array, collating which ones we have */
     ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, optval) {
@@ -3664,7 +3664,7 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     size_t keylen, unitlen;
     geoOptions gopts = {0};
     smart_string cmdstr = {0};
-    zval *position, *shape, *opts = NULL, *z_ele, *z_tmp;
+    zval *position, *shape, *opts = NULL, *z_ele;
     zend_string *zkey, *zstr;
 
     if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a",

From 59053f10d975840bf19ac43e472651c68b9e9aa6 Mon Sep 17 00:00:00 2001
From: Michele Locati 
Date: Sun, 12 Jun 2022 22:29:08 +0200
Subject: [PATCH 1609/1986] Add missing configureoption entries in package.xml

---
 package.xml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/package.xml b/package.xml
index 91163e6df6..350aef5b09 100644
--- a/package.xml
+++ b/package.xml
@@ -199,6 +199,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
   
   
   
+  
+  
  
  
  

From 39a01ac7b5a767185e59edb41a7046021e8af844 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Sun, 18 Sep 2022 01:21:20 -0700
Subject: [PATCH 1610/1986] Return false or NULL on empty lpos response (#2151)

Return false or NULL on empty lpos response

To be consistent with other PhpRedis methods, we should return either
false or NULL when LPOS returns no results, depening on
NULL_MBULK_AS_NULL setting.
---
 library.c              | 10 +++++++++-
 redis.stub.php         |  2 +-
 redis_arginfo.h        |  4 ++--
 redis_legacy_arginfo.h |  2 +-
 tests/RedisTest.php    |  7 ++++++-
 5 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/library.c b/library.c
index 2fbc4ae8c0..ed2b941632 100644
--- a/library.c
+++ b/library.c
@@ -1378,6 +1378,7 @@ redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z
     int i, numElems;
     size_t len;
     zval z_ret;
+    long lval;
 
     if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) {
         goto failure;
@@ -1387,7 +1388,14 @@ redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z
         if (*inbuf != TYPE_INT && *inbuf != TYPE_BULK) {
             goto failure;
         }
-        ZVAL_LONG(&z_ret, atol(inbuf + 1));
+        lval = atol(inbuf + 1);
+        if (lval > -1) {
+            ZVAL_LONG(&z_ret, lval);
+        } else if (redis_sock->null_mbulk_as_null) {
+            ZVAL_NULL(&z_ret);
+        } else {
+            ZVAL_FALSE(&z_ret);
+        }
     } else if (ctx == PHPREDIS_CTX_PTR) {
         if (*inbuf != TYPE_MULTIBULK) {
             goto failure;
diff --git a/redis.stub.php b/redis.stub.php
index ba8febf15e..57e48d01c6 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -241,7 +241,7 @@ public function lMove(string $src, string $dst, string $wherefrom, string $where
 
     public function lPop(string $key, int $count = 0): bool|string|array;
 
-    public function lPos(string $key, mixed $value, array $options = null): bool|int|array;
+    public function lPos(string $key, mixed $value, array $options = null): null|bool|int|array;
 
     /**
      * @param mixed $elements
diff --git a/redis_arginfo.h b/redis_arginfo.h
index fff2314db8..ea5ee288c6 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b9da355c27e6fb1b776164d40a521703e31713b5 */
+ * Stub hash: 2b1fc18e5c464c551df8572363972769b2ec1096 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -417,7 +417,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_lPop, 0, 1, MAY_BE_B
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 4e744ee677..cacf75f371 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b9da355c27e6fb1b776164d40a521703e31713b5 */
+ * Stub hash: 2b1fc18e5c464c551df8572363972769b2ec1096 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 5e234de6d2..49383d476b 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1049,7 +1049,12 @@ public function testlPos()
         $this->assertEquals([0, 1], $this->redis->lPos('key', 'val1', ['count' => 2]));
         $this->assertEquals([0], $this->redis->lPos('key', 'val1', ['count' => 2, 'maxlen' => 1]));
         $this->assertEquals([], $this->redis->lPos('key', 'val2', ['count' => 1]));
-        $this->assertEquals(-1, $this->redis->lPos('key', 'val2'));
+
+        foreach ([[true, NULL], [false, false]] as $optpack) {
+            list ($setting, $expected) = $optpack;
+            $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $setting);
+            $this->assertEquals($expected, $this->redis->lPos('key', 'val2'));
+        }
     }
 
     // ltrim, lsize, lpop

From 130b5d0b8e18ccb8cd37630f9d54f958a556f8dc Mon Sep 17 00:00:00 2001
From: Dawid 'DeyV' Polak 
Date: Tue, 12 Oct 2021 08:27:29 +0200
Subject: [PATCH 1611/1986] Update INSTALL.markdown

On https://pecl.php.net/package/redis is hard to find url to windows dll's - direct url will help https://windows.php.net/downloads/pecl/releases/redis/
---
 INSTALL.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/INSTALL.markdown b/INSTALL.markdown
index 8c8ed16e6a..7e8e025c55 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -37,7 +37,7 @@ Most distributions provides pre-build binary packages of this extension.
 
 ## Windows:
 
-Follow the DLL link on the [https://pecl.php.net/package/redis](https://pecl.php.net/package/redis) page.
+Follow the DLL link on the [https://pecl.php.net/package/redis](https://pecl.php.net/package/redis) page or use [https://windows.php.net/downloads/pecl/releases/redis/](https://windows.php.net/downloads/pecl/releases/redis/)
 
 ## Fedora
 

From f2bfd723573d2099ec411812c5a096da7eea94ff Mon Sep 17 00:00:00 2001
From: Marius Adam 
Date: Sun, 17 Jul 2022 14:48:08 +0300
Subject: [PATCH 1612/1986] Issue #2080: avoid registering the same replicas
 multiple times in case the master handles multiple slot ranges

---
 cluster_library.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index b14b1ac44d..f11c2dcb7c 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -709,25 +709,25 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) {
         if ((pnode = zend_hash_str_find_ptr(c->nodes, key, klen)) == NULL) {
             master = cluster_node_create(c, host, hlen, port, low, 0);
             zend_hash_str_update_ptr(c->nodes, key, klen, master);
-        } else {
-            master = pnode;
-        }
 
-        // Attach slaves
-        for (j = 3; j< r2->elements; j++) {
-            r3 = r2->element[j];
-            if (!VALIDATE_SLOTS_INNER(r3)) {
-                return -1;
-            }
+            // Attach slaves first time we encounter a given master in order to avoid regitering the slaves multiple times
+            for (j = 3; j< r2->elements; j++) {
+                r3 = r2->element[j];
+                if (!VALIDATE_SLOTS_INNER(r3)) {
+                    return -1;
+                }
 
-            // Skip slaves where the host is ""
-            if (r3->element[0]->len == 0) continue;
+                // Skip slaves where the host is ""
+                if (r3->element[0]->len == 0) continue;
 
-            // Attach this node to our slave
-            slave = cluster_node_create(c, r3->element[0]->str,
-                (int)r3->element[0]->len,
-                (unsigned short)r3->element[1]->integer, low, 1);
-            cluster_node_add_slave(master, slave);
+                // Attach this node to our slave
+                slave = cluster_node_create(c, r3->element[0]->str,
+                    (int)r3->element[0]->len,
+                    (unsigned short)r3->element[1]->integer, low, 1);
+                cluster_node_add_slave(master, slave);
+            }
+        } else {
+            master = pnode;
         }
 
         // Attach this node to each slot in the range

From 21c3ef94e848642fb44c0da8812ddb31b30c750d Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 19 Sep 2022 12:15:46 -0700
Subject: [PATCH 1613/1986] Fix typos/bad Markdown in cluster.markdown

---
 cluster.markdown | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/cluster.markdown b/cluster.markdown
index a52787e050..f3306b9915 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -8,7 +8,7 @@ Redis introduces cluster support as of version 3.0.0, and to communicate with a
 To maintain consistency with the RedisArray class, one can create and connect to a cluster either by passing it one or more 'seed' nodes, or by defining these in redis.ini as a 'named' cluster.
 
 #### Declaring a cluster with an array of seeds
-~~~php
+```php
 // Create a cluster setting three nodes as seeds
 $obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003'));
 
@@ -21,24 +21,24 @@ $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5,
 
 // Connect with cluster using password.
 $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, "password");
-~~~
+```
 
 #### Loading a cluster configuration by name
 In order to load a named array, one must first define the seed nodes in redis.ini.  The following lines would define the cluster 'mycluster', and be loaded automatically by phpredis.
 
-~~~ini
+```ini
 # In redis.ini
 redis.clusters.seeds = "mycluster[]=localhost:7000&test[]=localhost:7001"
 redis.clusters.timeout = "mycluster=5"
 redis.clusters.read_timeout = "mycluster=10"
 redis.clusters.auth = "mycluster=password"
-~~~
+```
 
 Then, this cluster can be loaded by doing the following
 
-~~~php
+```php
 $obj_cluster = new RedisCluster('mycluster');
-~~~
+```
 
 ## Connection process
 
@@ -60,7 +60,7 @@ Because of this, the RedisCluster class will update its keyspace mapping wheneve
 ## Automatic slave failover / distribution
 By default, RedisCluster will only ever send commands to master nodes, but can be configured differently for readonly commands if requested.
 
-~~~php
+```php
 // The default option, only send commands to master nodes
 $obj_cluster->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_NONE);
 
@@ -76,24 +76,24 @@ $obj_cluster->setOption(
 $obj_cluster->setOption(
     RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE_SLAVES
 );
-~~~
+```
 
 ## Main command loop
 With the exception of commands that are directed to a specific node, each command executed via RedisCluster is processed through a command loop, where we make the request, handle any MOVED or ASK redirection, and repeat if necessary.  This continues until one of the following conditions is met:
 
-1.  We fail to communicate with *any* node that we are aware of, in which case a ~~~RedisClusterException~~~ is raised.
+1.  We fail to communicate with *any* node that we are aware of, in which case a `RedisClusterException` is raised.
 2.  We have been bounced around longer than the timeout which was set on construction.
-3.  Redis cluster returns to us a ~~~CLUSTERDOWN~~~ error, in which case a ~~~RedisClusterException~~~ is raised.
+3.  Redis cluster returns to us a `CLUSTERDOWN` error, in which case a `RedisClusterException` is raised.
 4.  We receive a valid response, in which case the data is returned to the caller.
 
 ## Transactions
 The RedisCluster class fully supports MULTI ... EXEC transactions, including commands such as MGET and MSET which operate on multiple keys.  There are considerations that must be taken into account here however.
 
-When you call ~~~RedisCluster->multi()~~~, the cluster is put into a MULTI state, but the MULTI command is not delivered to any nodes until a key is requested on that node.  In addition, calls to EXEC will always return an array (even in the event that a transaction to a given node failed), as the commands can be going to any number of nodes depending on what is called.
+When you call `RedisCluster->multi()`, the cluster is put into a MULTI state, but the MULTI command is not delivered to any nodes until a key is requested on that node.  In addition, calls to EXEC will always return an array (even in the event that a transaction to a given node failed), as the commands can be going to any number of nodes depending on what is called.
 
 Consider the following example:
 
-~~~php
+```php
 // Cluster is put into MULTI state locally
 $obj_cluster->multi();
 
@@ -108,7 +108,7 @@ $obj_cluster->get("myotherkey");
 // This will always return an array, even in the event of a failed transaction
 // on one of the nodes, in which case that element will be FALSE
 print_r($obj_cluster->exec());
-~~~
+```
 
 ## Pipelining
 The RedisCluster class does not support pipelining as there is no way to detect whether the keys still live where our map indicates that they do and would therefore be inherently unsafe.  It would be possible to implement this support as an option if there is demand for such a feature.
@@ -123,17 +123,17 @@ RedisCluster has specialized processing for MGET, MSET, DEL, and UNLINK which al
 
 *Note:  If you send keys that hash to more than one slot, these commands are no longer atomic.*
 
-~~~php
+```php
 // This will send two `MGET` commands.  One for `{hash1}` keys, and one for `otherkey`
 $obj_cluster->mget(["{hash1}key1","{hash1}key2","{hash1}key3","otherkey"]);
-~~~
+```
 
 This operation can also be done in MULTI mode transparently.
 
 ## Directed node commands
 There are a variety of commands which have to be directed at a specific node.  In the case of these commands, the caller can either pass a key (which will be hashed and used to direct our command), or an array with host:port.
 
-~~~php
+```php
 // This will be directed at the slot/node which would store "mykey"
 $obj_cluster->echo("mykey","Hello World!");
 
@@ -141,7 +141,7 @@ $obj_cluster->echo("mykey","Hello World!");
 foreach ($obj_cluster->_masters() as $arr_master) {
 	$obj_cluster->echo($arr_master, "Hello: " . implode(':', $arr_master));
 }
-~~~
+```
 
 In the case of all commands which need to be directed at a node, the calling convention is identical to the Redis call, except that they require an additional (first) argument in order to deliver the command.  Following is a list of each of these commands:
 
@@ -167,10 +167,10 @@ You can use the cluster functionality of phpredis to store PHP session informati
 
 To do this, you must configure your `session.save_handler` and `session.save_path` INI variables to give phpredis enough information to communicate with the cluster.
 
-~~~ini
+```ini
 session.save_handler = rediscluster
 session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1&auth=password&stream[verify_peer]=0"
-~~~
+```
 
 ### session.session_handler
 Set this variable to "rediscluster" to inform phpredis that this is a cluster instance.
@@ -179,7 +179,7 @@ Set this variable to "rediscluster" to inform phpredis that this is a cluster in
 The save path for cluster based session storage takes the form of a PHP GET request, and requires that you specify at least one `seed` node.  Other options you can specify are as follows:
 
 * _timeout (double)_:  The amount of time phpredis will wait when connecting or writing to the cluster.
-* _read_timeout (double)_: The amount of time phpredis will wait for a result from the cluster.
+* _read\_timeout (double)_: The amount of time phpredis will wait for a result from the cluster.
 * _persistent_: Tells phpredis whether persistent connections should be used.
 * _failover (string)_:  How phpredis should distribute session reads between master and slave nodes.
   * _none_ : phpredis will only communicate with master nodes

From b7bf22d4c95ebb275702b9c93ae46c45f21faae2 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 22 Sep 2022 19:51:01 -0700
Subject: [PATCH 1614/1986] Add an example with a unix socket and timeout

See: #1663
---
 README.markdown | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.markdown b/README.markdown
index 5f354763cc..43a1a2b285 100644
--- a/README.markdown
+++ b/README.markdown
@@ -243,6 +243,7 @@ $redis->connect('tls://127.0.0.1'); // enable transport level security, port 637
 $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
 $redis->connect('/tmp/redis.sock'); // unix domain socket.
 $redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts.
+$redis->connect('/tmp/redis.sock', 0, 1.5, NULL, 0, 1.5); // Unix socket with 1.5s timeouts (connect and read)
 
 /* With PhpRedis >= 5.3.0 you can specify authentication and stream information on connect */
 $redis->connect('127.0.0.1', 6379, 1, NULL, 0, 0, ['auth' => ['phpredis', 'phpredis']]);

From a98605f216449479cf27599244c25cbf415ed226 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 23 Sep 2022 10:41:55 -0700
Subject: [PATCH 1615/1986] BLPOP with a float timeout

See #2157
---
 redis.stub.php         |  4 ++--
 redis_arginfo.h        | 12 ++++++++----
 redis_commands.c       | 34 +++++++++++++++++++++-------------
 redis_legacy_arginfo.h |  2 +-
 tests/RedisTest.php    | 22 ++++++++++++++--------
 5 files changed, 46 insertions(+), 28 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 57e48d01c6..3a7549019f 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -52,9 +52,9 @@ public function bitop(string $operation, string $deskey, string $srckey, string
     /** @return int|Redis */
     public function bitpos(string $key, int $bit, int $start = 0, int $end = -1);
 
-    public function blPop(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+    public function blPop(string|array $key, string|double|int $timeout_or_key, mixed ...$extra_args): array;
 
-    public function brPop(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+    public function brPop(string|array $key, string|double|int $timeout_or_key, mixed ...$extra_args): array;
 
     public function brpoplpush(string $src, string $dst, int $timeout): string;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index ea5ee288c6..e05c4b9fac 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2b1fc18e5c464c551df8572363972769b2ec1096 */
+ * Stub hash: 28b297e0067c033cea6e2c42fb1f42d4585234ac */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -72,7 +72,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_blPop, 0, 2, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
-	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL)
+	ZEND_ARG_OBJ_TYPE_MASK(0, timeout_or_key, double, MAY_BE_STRING|MAY_BE_LONG, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
@@ -84,9 +84,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_brpoplpush, 0, 3, IS
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_bzPopMax arginfo_class_Redis_blPop
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bzPopMax, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_bzPopMin arginfo_class_Redis_blPop
+#define arginfo_class_Redis_bzPopMin arginfo_class_Redis_bzPopMax
 
 #define arginfo_class_Redis_clearLastError arginfo_class_Redis_bgSave
 
diff --git a/redis_commands.c b/redis_commands.c
index 39c7941d1d..35152b7c56 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1461,14 +1461,13 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                           char *kw, int kw_len, int min_argc, int has_timeout,
                           char **cmd, int *cmd_len, short *slot)
 {
-    zval *z_args, *z_ele;
+    zval *z_args, *z_ele, ztimeout = {0};
     HashTable *ht_arr;
     char *key;
     int key_free, i, tail;
     size_t key_len;
     int single_array = 0, argc = ZEND_NUM_ARGS();
     smart_string cmdstr = {0};
-    long timeout = 0;
     short kslot = -1;
     zend_string *zstr;
 
@@ -1489,8 +1488,9 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         single_array = argc==1 && Z_TYPE(z_args[0]) == IS_ARRAY;
     } else {
         single_array = argc==2 && Z_TYPE(z_args[0]) == IS_ARRAY &&
-            Z_TYPE(z_args[1]) == IS_LONG;
-        timeout = Z_LVAL(z_args[1]);
+            (Z_TYPE(z_args[1]) == IS_LONG || Z_TYPE(z_args[1]) == IS_DOUBLE);
+        if (single_array)
+            ZVAL_COPY_VALUE(&ztimeout, &z_args[1]);
     }
 
     // If we're running a single array, rework args
@@ -1533,17 +1533,22 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             zend_string_release(zstr);
             if (key_free) efree(key);
         } ZEND_HASH_FOREACH_END();
-        if (has_timeout) {
-            redis_cmd_append_sstr_long(&cmdstr, timeout);
+        if (Z_TYPE(ztimeout) == IS_LONG) {
+            redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(ztimeout));
+        } else if (Z_TYPE(ztimeout) == IS_DOUBLE) {
+            redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL(ztimeout));
         }
     } else {
-        if (has_timeout && Z_TYPE(z_args[argc-1])!=IS_LONG) {
-            php_error_docref(NULL, E_ERROR,
-                "Timeout value must be a LONG");
-            efree(z_args);
-            return FAILURE;
+        if (has_timeout) {
+            zend_uchar type = Z_TYPE(z_args[argc - 1]);
+            if (type == IS_LONG || type == IS_DOUBLE) {
+                ZVAL_COPY_VALUE(&ztimeout, &z_args[argc - 1]);
+            } else {
+                php_error_docref(NULL, E_ERROR, "Timeout value must be a long or double");
+                efree(z_args);
+                return FAILURE;
+            }
         }
-
         tail = has_timeout ? argc-1 : argc;
         for(i = 0; i < tail; i++) {
             zstr = zval_get_string(&z_args[i]);
@@ -1571,7 +1576,10 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             zend_string_release(zstr);
             if (key_free) efree(key);
         }
-        if (has_timeout) {
+
+        if (Z_TYPE(ztimeout) == IS_DOUBLE) {
+            redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL(z_args[tail]));
+        } else if (Z_TYPE(ztimeout) == IS_LONG) {
             redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(z_args[tail]));
         }
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index cacf75f371..b26cd9e606 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2b1fc18e5c464c551df8572363972769b2ec1096 */
+ * Stub hash: 28b297e0067c033cea6e2c42fb1f42d4585234ac */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 49383d476b..6ef56490f4 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -963,18 +963,24 @@ public function testrPop()
     }
 
     public function testblockingPop() {
+        /* Test with a double timeout in Redis >= 6.0.0 */
+        if (version_compare($this->version, "6.0.0") >= 0) {
+            $this->redis->del('list');
+            $this->redis->lpush('list', 'val1', 'val2');
+            $this->assertEquals(['list', 'val2'], $this->redis->blpop(['list'], .1));
+            $this->assertEquals(['list', 'val1'], $this->redis->blpop(['list'], .1));
+        }
+
         // 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->lPush('list', 'val1', 'val2');
+        $this->assertEquals(['list', 'val2'], $this->redis->blPop(['list'], 2));
+        $this->assertEquals(['list', 'val1'], $this->redis->blPop(['list'], 2));
 
         $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']);
+        $this->redis->lPush('list', 'val1', 'val2');
+        $this->assertEquals(['list', 'val1'], $this->redis->brPop(['list'], 1));
+        $this->assertEquals(['list', 'val2'], $this->redis->brPop(['list'], 1));
 
         // blocking blpop, brpop
         $this->redis->del('list');

From 98fda1b870171a800a587512bcc59c8bdbd0869a Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 2 Mar 2020 22:13:33 -0800
Subject: [PATCH 1616/1986] Make sure we set an error for key based scans

See #1661
---
 library.c                  | 10 ++++++++--
 tests/RedisClusterTest.php |  1 +
 tests/RedisTest.php        | 11 +++++++++++
 3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/library.c b/library.c
index ed2b941632..647b73b3e6 100644
--- a/library.c
+++ b/library.c
@@ -386,19 +386,25 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw)
     return -1;
 }
 
-
 PHP_REDIS_API int
 redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                            REDIS_SCAN_TYPE type, zend_long *iter)
 {
     REDIS_REPLY_TYPE reply_type;
     long reply_info;
-    char *p_iter;
+    char err[4096], *p_iter;
+    size_t errlen;
 
     /* Our response should have two multibulk replies */
     if(redis_read_reply_type(redis_sock, &reply_type, &reply_info)<0
        || reply_type != TYPE_MULTIBULK || reply_info != 2)
     {
+        if (reply_type == TYPE_ERR) {
+            if (redis_sock_gets(redis_sock, err, sizeof(err), &errlen) == 0) {
+                redis_sock_set_err(redis_sock, err, errlen);
+            }
+        }
+
         return -1;
     }
 
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 6daa93bb59..908e1e58d7 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -49,6 +49,7 @@ public function testConnectException() { return $this->markTestSkipped(); }
     public function testTlsConnect() { return $this->markTestSkipped(); }
     public function testReset() { return $this->markTestSkipped(); }
     public function testInvalidAuthArgs() { return $this->markTestSkipped(); }
+    public function testScanErrors() { return $this->markTestSkipped(); }
 
     public function testlMove() { return $this->markTestSkipped(); }
     public function testlPos() { return $this->marktestSkipped(); }
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 6ef56490f4..9c06b64e5d 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5701,6 +5701,17 @@ public function testZScan() {
         $this->assertEquals(0, $i_p_count);
     }
 
+    /* Make sure we capture errors when scanning */
+    public function testScanErrors() {
+        $this->redis->set('scankey', 'simplekey');
+
+        foreach (['sScan', 'hScan', 'zScan'] as $str_method) {
+            $it = NULL;
+            $this->redis->$str_method('scankey', $it);
+            $this->assertTrue(strpos($this->redis->getLastError(), 'WRONGTYPE') === 0);
+        }
+    }
+
     //
     // HyperLogLog (PF) commands
     //

From dc9af5296bedef126d2fa7d5ce31181c2824ad94 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 30 Sep 2022 02:25:02 -0700
Subject: [PATCH 1617/1986] float not double.

See: #2158
---
 redis.stub.php         | 4 ++--
 redis_arginfo.h        | 4 ++--
 redis_legacy_arginfo.h | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 3a7549019f..ddb551681f 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -52,9 +52,9 @@ public function bitop(string $operation, string $deskey, string $srckey, string
     /** @return int|Redis */
     public function bitpos(string $key, int $bit, int $start = 0, int $end = -1);
 
-    public function blPop(string|array $key, string|double|int $timeout_or_key, mixed ...$extra_args): array;
+    public function blPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): array;
 
-    public function brPop(string|array $key, string|double|int $timeout_or_key, mixed ...$extra_args): array;
+    public function brPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): array;
 
     public function brpoplpush(string $src, string $dst, int $timeout): string;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index e05c4b9fac..e5990f4ee2 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 28b297e0067c033cea6e2c42fb1f42d4585234ac */
+ * Stub hash: 2c5f558917526d1a034a45b63bf7890bd8cb49e5 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -72,7 +72,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_blPop, 0, 2, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
-	ZEND_ARG_OBJ_TYPE_MASK(0, timeout_or_key, double, MAY_BE_STRING|MAY_BE_LONG, NULL)
+	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_LONG, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index b26cd9e606..ebd87a7d87 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 28b297e0067c033cea6e2c42fb1f42d4585234ac */
+ * Stub hash: 2c5f558917526d1a034a45b63bf7890bd8cb49e5 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 8746493296b5cadbfce4b57b86313936627c0216 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 29 Sep 2022 14:21:13 -0700
Subject: [PATCH 1618/1986] Add back a default switch case for setoption
 handler

We can't use `EMPTY_SWITCH_DEFAULT_CASE` here as the underlying macro
will actually panic, as it calls `ZEND_UNREACHABLE` under the hood.
---
 redis_commands.c    | 4 +++-
 tests/RedisTest.php | 5 +++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/redis_commands.c b/redis_commands.c
index 35152b7c56..3db4514663 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5566,7 +5566,9 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
                 RETURN_TRUE;
             }
             break;
-        EMPTY_SWITCH_DEFAULT_CASE()
+        default:
+            php_error_docref(NULL, E_WARNING, "Unknown option '" ZEND_LONG_FMT "'", option);
+            break;
     }
     RETURN_FALSE;
 }
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 9c06b64e5d..c5a87d1546 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6985,6 +6985,11 @@ public function testCopy()
         $this->assertEquals('bar', $this->redis->get('key2'));
     }
 
+    /* Make sure we handle a bad option value gracefully */
+    public function testBadOptionValue() {
+        $this->assertFalse(@$this->redis->setOption(pow(2, 32), false));
+    }
+
     public  function testSession_regenerateSessionId_noLock_noDestroy() {
         $this->setSessionHandler();
         $sessionId = $this->generateSessionId();

From 239678a03bb9efcd30893a99c37ba62d22d22b5d Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 1 Oct 2022 10:12:15 -0700
Subject: [PATCH 1619/1986] Implement CONFIG RESETSTAT

Rework the CONFIG command and add RESETSTAT variant.

Fixes #1673
---
 redis.c                        | 57 ++++++++++++++--------------------
 redis.stub.php                 |  2 +-
 redis_arginfo.h                |  6 ++--
 redis_cluster_arginfo.h        |  2 +-
 redis_cluster_legacy_arginfo.h |  2 +-
 redis_legacy_arginfo.h         |  4 +--
 tests/RedisClusterTest.php     |  1 +
 tests/RedisTest.php            | 25 +++++++++++++++
 8 files changed, 57 insertions(+), 42 deletions(-)

diff --git a/redis.c b/redis.c
index 8499d33eff..017e60ee04 100644
--- a/redis.c
+++ b/redis.c
@@ -2663,54 +2663,43 @@ PHP_METHOD(Redis, setOption)
 /* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */
 PHP_METHOD(Redis, config)
 {
-    zval *object;
+    zend_string *op, *key = NULL, *val = NULL;
+    FailableResultCallback cb;
     RedisSock *redis_sock;
-    char *key = NULL, *val = NULL, *cmd, *op = NULL;
-    size_t key_len, val_len, op_len;
-    enum {CFG_GET, CFG_SET} mode;
+    zval *object;
     int cmd_len;
+    char *cmd;
 
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                     "Oss|s", &object, redis_ce, &op, &op_len,
-                                     &key, &key_len, &val, &val_len) == FAILURE)
+    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS|SS", &object,
+                                     redis_ce, &op, &key, &val) == FAILURE)
     {
         RETURN_FALSE;
     }
 
-    /* op must be GET or SET */
-    if(strncasecmp(op, "GET", 3) == 0) {
-        mode = CFG_GET;
-    } else if(strncasecmp(op, "SET", 3) == 0) {
-        mode = CFG_SET;
-    } else {
-        RETURN_FALSE;
-    }
-
     if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
         RETURN_FALSE;
     }
 
-    if (mode == CFG_GET && val == NULL) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "ss", op, op_len, key, key_len);
-
-        REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
-        if (IS_ATOMIC(redis_sock)) {
-            redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
-        }
-        REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw);
-
-    } else if(mode == CFG_SET && val != NULL) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len);
-
-        REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
-        if (IS_ATOMIC(redis_sock)) {
-            redis_boolean_response(
-                INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
-        }
-        REDIS_PROCESS_RESPONSE(redis_boolean_response);
+    if (zend_string_equals_literal_ci(op, "GET") && key != NULL) {
+        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SS", op, key);
+        cb = redis_mbulk_reply_zipped_raw;
+    } else if (zend_string_equals_literal_ci(op, "RESETSTAT")) {
+        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "s", ZEND_STRL("RESETSTAT"));
+        cb = redis_boolean_response;
+    } else if (zend_string_equals_literal_ci(op, "SET") && key != NULL && val != NULL) {
+        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SSS", op, key, val);
+        cb = redis_boolean_response;
     } else {
         RETURN_FALSE;
     }
+
+    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
+    if (IS_ATOMIC(redis_sock)) {
+        cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
+    }
+    REDIS_PROCESS_RESPONSE(redis_boolean_response);
+
+    return;
 }
 /* }}} */
 
diff --git a/redis.stub.php b/redis.stub.php
index ddb551681f..be56508d36 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -70,7 +70,7 @@ public function close(): bool;
 
     public function command(string $opt = null, string|array $arg): mixed;
 
-    public function config(string $operation, string $key, mixed $value = null): mixed;
+    public function config(string $operation, ?string $key = NULL, mixed $value = null): mixed;
 
     public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index e5990f4ee2..66176b346d 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2c5f558917526d1a034a45b63bf7890bd8cb49e5 */
+ * Stub hash: 122b5ca534302a09a4f072106d097f2831ba6f22 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -106,9 +106,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 2, IS_MI
 	ZEND_ARG_TYPE_MASK(0, arg, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 2, IS_MIXED, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "NULL")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, "null")
 ZEND_END_ARG_INFO()
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index ca606b3c53..842cd4a207 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7ff59229ef9ab94d3bb918d666610b70a5676030 */
+ * Stub hash: 97fe79bf371074b77d22e1e820903fb2103d10c9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 63ebcd7372..3cc0618de2 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7ff59229ef9ab94d3bb918d666610b70a5676030 */
+ * Stub hash: 97fe79bf371074b77d22e1e820903fb2103d10c9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index ebd87a7d87..1d7673e67d 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2c5f558917526d1a034a45b63bf7890bd8cb49e5 */
+ * Stub hash: 122b5ca534302a09a4f072106d097f2831ba6f22 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -97,7 +97,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_command, 0, 0, 2)
 	ZEND_ARG_INFO(0, arg)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_config, 0, 0, 2)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_config, 0, 0, 1)
 	ZEND_ARG_INFO(0, operation)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, value)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 908e1e58d7..132c3744f3 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -64,6 +64,7 @@ public function testCopy() { return $this->marktestSkipped(); }
     public function testGeoSearch() { return $this->marktestSkipped(); }
     public function testGeoSearchStore() { return $this->marktestSkipped(); }
     public function testHRandField() { return $this->marktestSkipped(); }
+    public function testConfig() { return $this->markTestSkipped(); }
 
     /* Session locking feature is currently not supported in in context of Redis Cluster.
        The biggest issue for this is the distribution nature of Redis cluster */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c5a87d1546..6c04c9d0d8 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5342,6 +5342,31 @@ public function testNestedNullArray() {
         $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
     }
 
+    public function testConfig() {
+        /* GET */
+        $cfg = $this->redis->config('GET', 'timeout');
+        $this->assertTrue(is_array($cfg) && isset($cfg['timeout']));
+        $sec = $cfg['timeout'];
+
+        /* SET */
+        foreach ([$sec + 30, $sec] as $val) {
+            $this->assertTrue($this->redis->config('SET', 'timeout', $val));
+            $cfg = $this->redis->config('GET', 'timeout');
+            $this->assertTrue(isset($cfg['timeout']) && $cfg['timeout'] == $val);
+        }
+
+        /* RESETSTAT */
+        $c1 = count($this->redis->info('commandstats'));
+        $this->assertTrue($this->redis->config('resetstat'));
+        $this->assertTrue(count($this->redis->info('commandstats')) < $c1);
+
+        /* Ensure invalid calls are handled by PhpRedis */
+        foreach (['notacommand', 'get', 'set'] as $cmd) {
+            $this->assertFalse($this->redis->config($cmd));
+        }
+        $this->assertFalse($this->redis->config('set', 'foo'));
+    }
+
     public function testReconnectSelect() {
         $key = 'reconnect-select';
         $value = 'Has been set!';

From 643005080839b35c40d78af40eda3e2743ad4d6f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 1 Oct 2022 10:25:54 -0700
Subject: [PATCH 1620/1986] SINTERCARD and ZINTERCARD commands

Implement Redis 7.0.0 commands SINTERCARD and ZINTERCARD.
---
 cluster_library.c              |  4 +++
 cluster_library.h              |  1 +
 library.c                      | 41 ++++++++++++++++++++++++
 library.h                      |  4 +++
 redis.c                        |  8 +++++
 redis.stub.php                 |  4 +++
 redis_arginfo.h                | 13 +++++++-
 redis_cluster.c                | 13 ++++++++
 redis_cluster.stub.php         |  4 +++
 redis_cluster_arginfo.h        | 13 +++++++-
 redis_cluster_legacy_arginfo.h | 13 +++++++-
 redis_commands.c               | 57 ++++++++++++++++++++++++++++++++++
 redis_commands.h               |  4 +++
 redis_legacy_arginfo.h         | 13 +++++++-
 tests/RedisTest.php            | 49 +++++++++++++++++++++++++++++
 15 files changed, 237 insertions(+), 4 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index f11c2dcb7c..6e69c51847 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -530,6 +530,10 @@ unsigned short cluster_hash_key(const char *key, int len) {
     return crc16((char*)key+s+1,e-s-1) & REDIS_CLUSTER_MOD;
 }
 
+unsigned short cluster_hash_key_zstr(zend_string *key) {
+    return cluster_hash_key(ZSTR_VAL(key), ZSTR_LEN(key));
+}
+
 /* Grab the current time in milliseconds */
 long long mstime(void) {
     struct timeval tv;
diff --git a/cluster_library.h b/cluster_library.h
index fe2962f06f..ddb29f2916 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -352,6 +352,7 @@ void cluster_multi_fini(clusterMultiCmd *mc);
 /* Hash a key to it's slot, using the Redis Cluster hash algorithm */
 unsigned short cluster_hash_key_zval(zval *key);
 unsigned short cluster_hash_key(const char *key, int len);
+unsigned short cluster_hash_key_zstr(zend_string *key);
 
 /* Validate and sanitize cluster construction args */
 zend_string** cluster_validate_args(double timeout, double read_timeout,
diff --git a/library.c b/library.c
index 647b73b3e6..67a49be041 100644
--- a/library.c
+++ b/library.c
@@ -3502,6 +3502,47 @@ redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len) {
     return 1;
 }
 
+/* This is very similar to PHP >= 7.4 zend_string_concat2 only we are taking
+ * two zend_string arguments rather than two char*, size_t pairs */
+static zend_string *redis_zstr_concat(zend_string *prefix, zend_string *suffix) {
+    zend_string *res;
+    size_t len;
+
+    ZEND_ASSERT(prefix != NULL && suffix != NULL);
+
+    len = ZSTR_LEN(prefix) + ZSTR_LEN(suffix);
+    res = zend_string_alloc(len, 0);
+
+    memcpy(ZSTR_VAL(res), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
+    memcpy(ZSTR_VAL(res) + ZSTR_LEN(prefix), ZSTR_VAL(suffix), ZSTR_LEN(suffix));
+    ZSTR_VAL(res)[len] = '\0';
+
+    return res;
+}
+
+PHP_REDIS_API zend_string *
+redis_key_prefix_zval(RedisSock *redis_sock, zval *zv) {
+    zend_string *zstr, *dup;
+
+    zstr = zval_get_string(zv);
+    if (redis_sock->prefix == NULL)
+        return zstr;
+
+    dup = redis_zstr_concat(redis_sock->prefix, zstr);
+
+    zend_string_release(zstr);
+
+    return dup;
+}
+
+PHP_REDIS_API zend_string *
+redis_key_prefix_zstr(RedisSock *redis_sock, zend_string *key) {
+    if (redis_sock->prefix == NULL)
+        return zend_string_copy(key);
+
+    return redis_zstr_concat(redis_sock->prefix, key);
+}
+
 /*
  * Processing for variant reply types (think EVAL)
  */
diff --git a/library.h b/library.h
index a632a84e19..9e1cfd7348 100644
--- a/library.h
+++ b/library.h
@@ -123,6 +123,10 @@ PHP_REDIS_API int
 redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len);
 PHP_REDIS_API int
 redis_key_prefix(RedisSock *redis_sock, char **key, size_t *key_len);
+PHP_REDIS_API zend_string*
+redis_key_prefix_zval(RedisSock *redis_sock, zval *zv);
+PHP_REDIS_API zend_string *
+redis_key_prefix_zstr(RedisSock *redis_sock, zend_string *key);
 
 PHP_REDIS_API int
 redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval *z_ret);
diff --git a/redis.c b/redis.c
index 017e60ee04..ca8f96c5fd 100644
--- a/redis.c
+++ b/redis.c
@@ -1435,6 +1435,10 @@ PHP_METHOD(Redis, sInter) {
 }
 /* }}} */
 
+PHP_METHOD(Redis, sintercard) {
+    REDIS_PROCESS_KW_CMD("SINTERCARD", redis_intercard_cmd, redis_long_response);
+}
+
 /* {{{ proto array Redis::sInterStore(string dst, string key0,...string keyN) */
 PHP_METHOD(Redis, sInterStore) {
     REDIS_PROCESS_CMD(sinterstore, redis_long_response);
@@ -2071,6 +2075,10 @@ PHP_METHOD(Redis, zinter) {
 }
 /* }}} */
 
+PHP_METHOD(Redis, zintercard) {
+    REDIS_PROCESS_KW_CMD("ZINTERCARD", redis_intercard_cmd, redis_long_response);
+}
+
 /* {{{ proto array Redis::zunion(array keys, array|null weights, array options) */
 PHP_METHOD(Redis, zunion) {
     REDIS_PROCESS_KW_CMD("ZUNION", redis_zinterunion_cmd, redis_zdiff_response);
diff --git a/redis.stub.php b/redis.stub.php
index be56508d36..7f125ddf5a 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -363,6 +363,8 @@ public function sDiffStore(string $dst, string $key, string ...$other_keys): int
 
     public function sInter(string $key, string ...$other_keys): array;
 
+    public function sintercard(array $keys, int $limit = -1): Redis|int|false;
+
     public function sInterStore(string $dst, string $key, string ...$other_keys): int;
 
     public function sMembers(string $key): array;
@@ -548,6 +550,8 @@ public function zdiffstore(string $dst, array $keys, array $options = null): int
 
     public function zinter(array $keys, array $weights = null, array $options = null): array;
 
+    public function zintercard(array $keys, int $limit = -1): Redis|int|false;
+
     public function zinterstore(string $dst, array $keys, array $weights = null, string $aggregate = null): int;
 
     public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 66176b346d..33526de0ad 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 122b5ca534302a09a4f072106d097f2831ba6f22 */
+ * Stub hash: f43a528bc5874c419161b5eb874537fbe9c00e87 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -628,6 +628,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sInter arginfo_class_Redis_sDiff
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sintercard, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_sInterStore arginfo_class_Redis_sDiffStore
 
 #define arginfo_class_Redis_sMembers arginfo_class_Redis_hGetAll
@@ -971,6 +976,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zinter, 0, 1, IS_ARR
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_zintercard arginfo_class_Redis_sintercard
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zinterstore, 0, 2, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
@@ -1130,6 +1137,7 @@ ZEND_METHOD(Redis, sAddArray);
 ZEND_METHOD(Redis, sDiff);
 ZEND_METHOD(Redis, sDiffStore);
 ZEND_METHOD(Redis, sInter);
+ZEND_METHOD(Redis, sintercard);
 ZEND_METHOD(Redis, sInterStore);
 ZEND_METHOD(Redis, sMembers);
 ZEND_METHOD(Redis, sMisMember);
@@ -1209,6 +1217,7 @@ ZEND_METHOD(Redis, zScore);
 ZEND_METHOD(Redis, zdiff);
 ZEND_METHOD(Redis, zdiffstore);
 ZEND_METHOD(Redis, zinter);
+ZEND_METHOD(Redis, zintercard);
 ZEND_METHOD(Redis, zinterstore);
 ZEND_METHOD(Redis, zscan);
 ZEND_METHOD(Redis, zunion);
@@ -1364,6 +1373,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, sDiff, arginfo_class_Redis_sDiff, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sDiffStore, arginfo_class_Redis_sDiffStore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sInter, arginfo_class_Redis_sInter, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sintercard, arginfo_class_Redis_sintercard, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sInterStore, arginfo_class_Redis_sInterStore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sMembers, arginfo_class_Redis_sMembers, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sMisMember, arginfo_class_Redis_sMisMember, ZEND_ACC_PUBLIC)
@@ -1443,6 +1453,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, zdiff, arginfo_class_Redis_zdiff, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zdiffstore, arginfo_class_Redis_zdiffstore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zinter, arginfo_class_Redis_zinter, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zintercard, arginfo_class_Redis_zintercard, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zinterstore, arginfo_class_Redis_zinterstore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zscan, arginfo_class_Redis_zscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zunion, arginfo_class_Redis_zunion, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index f163158af5..4be54bf8d6 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1021,6 +1021,13 @@ PHP_METHOD(RedisCluster, sunionstore) {
 PHP_METHOD(RedisCluster, sinter) {
     CLUSTER_PROCESS_CMD(sinter, cluster_mbulk_resp, 0);
 }
+
+/* {{{ proto RedisCluster::sintercard(array $keys, int $count = -1) */
+PHP_METHOD(RedisCluster, sintercard) {
+    CLUSTER_PROCESS_KW_CMD("SINTERCARD", redis_intercard_cmd, cluster_long_resp, 0);
+}
+/* }}} */
+
 /* }}} */
 
 /* {{{ ptoto long RedisCluster::sinterstore(string dst, string k1, ... kN) */
@@ -1457,6 +1464,12 @@ PHP_METHOD(RedisCluster, zinterstore) {
 }
 /* }}} */
 
+/* {{{ proto RedisCluster::zintercard(array $keys, int $count = -1) */
+PHP_METHOD(RedisCluster, zintercard) {
+    CLUSTER_PROCESS_KW_CMD("ZINTERCARD", redis_intercard_cmd, cluster_long_resp, 0);
+}
+/* }}} */
+
 /* {{{ proto RedisCluster::zrem(string key, string val1, ... valN) */
 PHP_METHOD(RedisCluster, zrem) {
     CLUSTER_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, cluster_long_resp, 0);
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index ed0a8293fd..924b9c04f4 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -276,6 +276,8 @@ public function setrange(string $key, int $offset, string $value): int;
 
     public function sinter(string $key, string ...$other_keys): array;
 
+    public function sintercard(array $keys, int $limit = -1): Redis|int|false;
+
     public function sinterstore(string $dst, string $key, string ...$other_keys): bool;
 
     public function sismember(string $key): int;
@@ -354,6 +356,8 @@ public function zincrby(string $key, float $value, string $member): float;
 
     public function zinterstore(string $key, array $keys, array $weights = null, string $aggregate = null): int;
 
+    public function zintercard(array $keys, int $limit = -1): Redis|int|false;
+
     public function zlexcount(string $key, string $min, string $max): int;
 
     public function zpopmax(string $key, int $value = null): bool|array;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 842cd4a207..35f2ab11fa 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 97fe79bf371074b77d22e1e820903fb2103d10c9 */
+ * Stub hash: c9bfd7de5c930c9d7190139e207eb6025e33bdb2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -584,6 +584,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_sinter arginfo_class_RedisCluster_del
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sintercard, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_sinterstore, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -775,6 +780,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zinterstore,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 0, "null")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_zintercard arginfo_class_RedisCluster_sintercard
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zlexcount, 0, 3, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
@@ -973,6 +980,7 @@ ZEND_METHOD(RedisCluster, setnx);
 ZEND_METHOD(RedisCluster, setoption);
 ZEND_METHOD(RedisCluster, setrange);
 ZEND_METHOD(RedisCluster, sinter);
+ZEND_METHOD(RedisCluster, sintercard);
 ZEND_METHOD(RedisCluster, sinterstore);
 ZEND_METHOD(RedisCluster, sismember);
 ZEND_METHOD(RedisCluster, slowlog);
@@ -1012,6 +1020,7 @@ ZEND_METHOD(RedisCluster, zcard);
 ZEND_METHOD(RedisCluster, zcount);
 ZEND_METHOD(RedisCluster, zincrby);
 ZEND_METHOD(RedisCluster, zinterstore);
+ZEND_METHOD(RedisCluster, zintercard);
 ZEND_METHOD(RedisCluster, zlexcount);
 ZEND_METHOD(RedisCluster, zpopmax);
 ZEND_METHOD(RedisCluster, zpopmin);
@@ -1167,6 +1176,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, setoption, arginfo_class_RedisCluster_setoption, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, setrange, arginfo_class_RedisCluster_setrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sinter, arginfo_class_RedisCluster_sinter, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sintercard, arginfo_class_RedisCluster_sintercard, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sinterstore, arginfo_class_RedisCluster_sinterstore, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sismember, arginfo_class_RedisCluster_sismember, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, slowlog, arginfo_class_RedisCluster_slowlog, ZEND_ACC_PUBLIC)
@@ -1206,6 +1216,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, zcount, arginfo_class_RedisCluster_zcount, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zincrby, arginfo_class_RedisCluster_zincrby, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zinterstore, arginfo_class_RedisCluster_zinterstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zintercard, arginfo_class_RedisCluster_zintercard, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zlexcount, arginfo_class_RedisCluster_zlexcount, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 3cc0618de2..bff23cd616 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 97fe79bf371074b77d22e1e820903fb2103d10c9 */
+ * Stub hash: c9bfd7de5c930c9d7190139e207eb6025e33bdb2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -507,6 +507,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_sinter arginfo_class_RedisCluster_del
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sintercard, 0, 0, 1)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, limit)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_RedisCluster_sinterstore arginfo_class_RedisCluster_sdiffstore
 
 #define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster__prefix
@@ -673,6 +678,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zinterstore, 0, 0, 2)
 	ZEND_ARG_INFO(0, aggregate)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_zintercard arginfo_class_RedisCluster_sintercard
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zlexcount, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, min)
@@ -861,6 +868,7 @@ ZEND_METHOD(RedisCluster, setnx);
 ZEND_METHOD(RedisCluster, setoption);
 ZEND_METHOD(RedisCluster, setrange);
 ZEND_METHOD(RedisCluster, sinter);
+ZEND_METHOD(RedisCluster, sintercard);
 ZEND_METHOD(RedisCluster, sinterstore);
 ZEND_METHOD(RedisCluster, sismember);
 ZEND_METHOD(RedisCluster, slowlog);
@@ -900,6 +908,7 @@ ZEND_METHOD(RedisCluster, zcard);
 ZEND_METHOD(RedisCluster, zcount);
 ZEND_METHOD(RedisCluster, zincrby);
 ZEND_METHOD(RedisCluster, zinterstore);
+ZEND_METHOD(RedisCluster, zintercard);
 ZEND_METHOD(RedisCluster, zlexcount);
 ZEND_METHOD(RedisCluster, zpopmax);
 ZEND_METHOD(RedisCluster, zpopmin);
@@ -1055,6 +1064,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, setoption, arginfo_class_RedisCluster_setoption, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, setrange, arginfo_class_RedisCluster_setrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sinter, arginfo_class_RedisCluster_sinter, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sintercard, arginfo_class_RedisCluster_sintercard, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sinterstore, arginfo_class_RedisCluster_sinterstore, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sismember, arginfo_class_RedisCluster_sismember, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, slowlog, arginfo_class_RedisCluster_slowlog, ZEND_ACC_PUBLIC)
@@ -1094,6 +1104,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, zcount, arginfo_class_RedisCluster_zcount, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zincrby, arginfo_class_RedisCluster_zincrby, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zinterstore, arginfo_class_RedisCluster_zinterstore, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zintercard, arginfo_class_RedisCluster_zintercard, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zlexcount, arginfo_class_RedisCluster_zlexcount, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 3db4514663..1db729a667 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -841,6 +841,63 @@ static int redis_cmd_append_sstr_score(smart_string *dst, zval *score) {
     return FAILURE;
 }
 
+int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                        char *kw, char **cmd, int *cmd_len, short *slot,
+                        void **ctx)
+{
+    smart_string cmdstr = {0};
+    zend_long limit = -1;
+    HashTable *keys;
+    zend_string *key;
+    zval *zv;
+
+    ZEND_PARSE_PARAMETERS_START(1, 2)
+        Z_PARAM_ARRAY_HT(keys)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_LONG(limit)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    if (zend_hash_num_elements(keys) == 0) {
+        php_error_docref(NULL, E_WARNING, "Must pass at least one key");
+        return FAILURE;
+    } else if (ZEND_NUM_ARGS() == 2 && limit < 0) {
+        php_error_docref(NULL, E_WARNING, "LIMIT cannot be negative");
+        return FAILURE;
+    }
+
+    redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(keys) + (limit > 0 ? 2 : 0), kw, strlen(kw));
+    redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(keys));
+
+    if (slot) *slot = -1;
+
+    ZEND_HASH_FOREACH_VAL(keys, zv) {
+        key = redis_key_prefix_zval(redis_sock, zv);
+
+        if (slot) {
+            if (*slot == -1) {
+                *slot = cluster_hash_key_zstr(key);
+            } else if (*slot != cluster_hash_key_zstr(key)) {
+                php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot");
+                efree(cmdstr.c);
+                zend_string_release(key);
+                return FAILURE;
+            }
+        }
+
+        redis_cmd_append_sstr_zstr(&cmdstr, key);
+        zend_string_release(key);
+    } ZEND_HASH_FOREACH_END();
+
+    if (limit > 0) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT");
+        redis_cmd_append_sstr_long(&cmdstr, limit);
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return SUCCESS;
+}
+
 int
 redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char *kw, char **cmd, int *cmd_len, short *slot,
diff --git a/redis_commands.h b/redis_commands.h
index 662d0500ed..24fbe92c4c 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -125,6 +125,10 @@ int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                        char *kw, char **cmd, int *cmd_len, short *slot,
+                        void **ctx);
+
 int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 1d7673e67d..aaf02d1973 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 122b5ca534302a09a4f072106d097f2831ba6f22 */
+ * Stub hash: f43a528bc5874c419161b5eb874537fbe9c00e87 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -552,6 +552,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sInter arginfo_class_Redis_del
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sintercard, 0, 0, 1)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, limit)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_sInterStore arginfo_class_Redis_sDiffStore
 
 #define arginfo_class_Redis_sMembers arginfo_class_Redis__prefix
@@ -863,6 +868,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zinter, 0, 0, 1)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_zintercard arginfo_class_Redis_sintercard
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zinterstore, 0, 0, 2)
 	ZEND_ARG_INFO(0, dst)
 	ZEND_ARG_INFO(0, keys)
@@ -1022,6 +1029,7 @@ ZEND_METHOD(Redis, sAddArray);
 ZEND_METHOD(Redis, sDiff);
 ZEND_METHOD(Redis, sDiffStore);
 ZEND_METHOD(Redis, sInter);
+ZEND_METHOD(Redis, sintercard);
 ZEND_METHOD(Redis, sInterStore);
 ZEND_METHOD(Redis, sMembers);
 ZEND_METHOD(Redis, sMisMember);
@@ -1101,6 +1109,7 @@ ZEND_METHOD(Redis, zScore);
 ZEND_METHOD(Redis, zdiff);
 ZEND_METHOD(Redis, zdiffstore);
 ZEND_METHOD(Redis, zinter);
+ZEND_METHOD(Redis, zintercard);
 ZEND_METHOD(Redis, zinterstore);
 ZEND_METHOD(Redis, zscan);
 ZEND_METHOD(Redis, zunion);
@@ -1256,6 +1265,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, sDiff, arginfo_class_Redis_sDiff, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sDiffStore, arginfo_class_Redis_sDiffStore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sInter, arginfo_class_Redis_sInter, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sintercard, arginfo_class_Redis_sintercard, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sInterStore, arginfo_class_Redis_sInterStore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sMembers, arginfo_class_Redis_sMembers, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sMisMember, arginfo_class_Redis_sMisMember, ZEND_ACC_PUBLIC)
@@ -1335,6 +1345,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, zdiff, arginfo_class_Redis_zdiff, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zdiffstore, arginfo_class_Redis_zdiffstore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zinter, arginfo_class_Redis_zinter, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zintercard, arginfo_class_Redis_zintercard, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zinterstore, arginfo_class_Redis_zinterstore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zscan, arginfo_class_Redis_zscan, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zunion, arginfo_class_Redis_zunion, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 6c04c9d0d8..551c6346dd 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1948,6 +1948,55 @@ public function testsDiffStore() {
         $this->assertTrue($count === 0);
     }
 
+    public function testInterCard() {
+        if(version_compare($this->version, "7.0.0") < 0) {
+            $this->markTestSkipped();
+        }
+
+        $set_data = [
+            ['aardvark', 'dog', 'fish', 'squirrel', 'tiger'],
+            ['bear', 'coyote', 'fish', 'gorilla', 'dog']
+        ];
+
+        $ssets = $zsets = [];
+
+        foreach ($set_data as $n => $values) {
+            $sset = "s{set}:$n";
+            $zset = "z{set}:$n";
+
+            $this->redis->del([$sset, $zset]);
+
+            $ssets[] = $sset;
+            $zsets[] = $zset;
+
+            foreach ($values as $score => $value) {
+                $this->assertEquals(1, $this->redis->sAdd("s{set}:$n", $value));
+                $this->assertEquals(1, $this->redis->zAdd("z{set}:$n", $score, $value));
+            }
+        }
+
+        $exp = count(array_intersect(...$set_data));
+
+        $act = $this->redis->sintercard($ssets);
+        $this->assertEquals($exp, $act);
+        $act = $this->redis->zintercard($zsets);
+        $this->assertEquals($exp, $act);
+
+        $this->assertEquals(1, $this->redis->sintercard($ssets, 1));
+        $this->assertEquals(2, $this->redis->sintercard($ssets, 2));
+
+        $this->assertEquals(1, $this->redis->zintercard($zsets, 1));
+        $this->assertEquals(2, $this->redis->zintercard($zsets, 2));
+
+        $this->assertFalse(@$this->redis->sintercard($ssets, -1));
+        $this->assertFalse(@$this->redis->zintercard($ssets, -1));
+
+        $this->assertFalse(@$this->redis->sintercard([]));
+        $this->assertFalse(@$this->redis->zintercard([]));
+
+        $this->redis->del(array_merge($ssets, $zsets));
+    }
+
     public function testlrange() {
         $this->redis->del('list');
         $this->redis->lPush('list', 'val');

From c0e839f6ac45d51adbb109b246c9b3565c3b61e1 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 1 Oct 2022 10:26:11 -0700
Subject: [PATCH 1621/1986] LCS command

Implement the Redis 7.0.0 LCS command with tests.
---
 common.h                       |   7 +++
 redis.c                        |   6 ++
 redis.stub.php                 |   2 +
 redis_arginfo.h                |  10 +++-
 redis_cluster.c                |   9 ++-
 redis_cluster.stub.php         |   2 +
 redis_cluster_arginfo.h        |  10 +++-
 redis_cluster_legacy_arginfo.h |  10 +++-
 redis_commands.c               | 103 +++++++++++++++++++++++++++++++++
 redis_commands.h               |   3 +
 redis_legacy_arginfo.h         |  10 +++-
 tests/RedisTest.php            |  21 +++++++
 12 files changed, 187 insertions(+), 6 deletions(-)

diff --git a/common.h b/common.h
index 8af8b541fc..c8d6c9b19f 100644
--- a/common.h
+++ b/common.h
@@ -146,6 +146,13 @@ typedef enum {
 
 #define PHPREDIS_DEBUG_LOGGING 0
 
+#if PHP_VERSION_ID < 80000
+#define Z_PARAM_ARRAY_HT_OR_NULL(dest) \
+        Z_PARAM_ARRAY_HT_EX(dest, 1, 0)
+#define Z_PARAM_STR_OR_NULL(dest) \
+        Z_PARAM_STR_EX(dest, 1, 0)
+#endif
+
 #if PHPREDIS_DEBUG_LOGGING == 1
 #define redisDbgFmt(fmt, ...) \
     php_printf("%s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, __VA_ARGS__)
diff --git a/redis.c b/redis.c
index ca8f96c5fd..1a9af65b0f 100644
--- a/redis.c
+++ b/redis.c
@@ -1182,6 +1182,12 @@ PHP_METHOD(Redis, getRange)
 }
 /* }}} */
 
+/* {{{ proto mixed Redis::lcs(string $key1, string $key2, ?array $options = NULL); */
+PHP_METHOD(Redis, lcs) {
+    REDIS_PROCESS_CMD(lcs, redis_read_variant_reply);
+}
+/* }}} */
+
 /* {{{ proto string Redis::setRange(string key, long start, string value) */
 PHP_METHOD(Redis, setRange)
 {
diff --git a/redis.stub.php b/redis.stub.php
index 7f125ddf5a..c3740b8d48 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -173,6 +173,8 @@ public function getPort(): int;
 	/** @return string|Redis */
     public function getRange(string $key, int $start, int $end);
 
+    public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
+
     public function getReadTimeout(): int;
 
 	/** @return string|Redis */
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 33526de0ad..7c2cdd4385 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: f43a528bc5874c419161b5eb874537fbe9c00e87 */
+ * Stub hash: 1c32099810101448fc32ce331e2b494dabc22cc4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -300,6 +300,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_getReadTimeout arginfo_class_Redis_dbSize
 
 #define arginfo_class_Redis_getset arginfo_class_Redis_append
@@ -1061,6 +1067,7 @@ ZEND_METHOD(Redis, getOption);
 ZEND_METHOD(Redis, getPersistentID);
 ZEND_METHOD(Redis, getPort);
 ZEND_METHOD(Redis, getRange);
+ZEND_METHOD(Redis, lcs);
 ZEND_METHOD(Redis, getReadTimeout);
 ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, getTimeout);
@@ -1295,6 +1302,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index 4be54bf8d6..cee00ae27c 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1315,13 +1315,18 @@ PHP_METHOD(RedisCluster, lget) {
 }
 /* }}} */
 
-/* {{{ proto string RedisCluster::getrange(string key, long start, long end) */
-PHP_METHOD(RedisCluster, getrange) {
+/* {{{ proto string RedisCluster::getrange(string key, long start, long end) */ PHP_METHOD(RedisCluster, getrange) {
     CLUSTER_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd,
         cluster_bulk_resp, 1);
 }
 /* }}} */
 
+/* {{{ prot RedisCluster::lcs(string $key1, string $key2, ?array $options = NULL): mixed; */
+PHP_METHOD(RedisCluster, lcs) {
+    CLUSTER_PROCESS_CMD(lcs, cluster_variant_resp, 1);
+}
+
+/* }}} */
 /* {{{ proto string RedisCluster::ltrim(string key, long start, long end) */
 PHP_METHOD(RedisCluster, ltrim) {
     CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp, 0);
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 924b9c04f4..31ba4ef634 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -124,6 +124,8 @@ public function getoption(int $option): mixed;
 
     public function getrange(string $key, int $start, int $end): string;
 
+    public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
+
     public function getset(string $key, mixed $value): string;
 
     public function hdel(string $key, string $member, string ...$other_members): int;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 35f2ab11fa..bbf10c9200 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c9bfd7de5c930c9d7190139e207eb6025e33bdb2 */
+ * Stub hash: 45d120a35a1c256964c45c71ab91c025ea00e862 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -253,6 +253,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getrange, 0,
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getset, 0, 2, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
@@ -904,6 +910,7 @@ ZEND_METHOD(RedisCluster, getlasterror);
 ZEND_METHOD(RedisCluster, getmode);
 ZEND_METHOD(RedisCluster, getoption);
 ZEND_METHOD(RedisCluster, getrange);
+ZEND_METHOD(RedisCluster, lcs);
 ZEND_METHOD(RedisCluster, getset);
 ZEND_METHOD(RedisCluster, hdel);
 ZEND_METHOD(RedisCluster, hexists);
@@ -1100,6 +1107,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index bff23cd616..5669c8be7f 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c9bfd7de5c930c9d7190139e207eb6025e33bdb2 */
+ * Stub hash: 45d120a35a1c256964c45c71ab91c025ea00e862 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -232,6 +232,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getrange, 0, 0, 3)
 	ZEND_ARG_INFO(0, end)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lcs, 0, 0, 2)
+	ZEND_ARG_INFO(0, key1)
+	ZEND_ARG_INFO(0, key2)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_RedisCluster_getset arginfo_class_RedisCluster_append
 
 #define arginfo_class_RedisCluster_hdel arginfo_class_RedisCluster_geohash
@@ -792,6 +798,7 @@ ZEND_METHOD(RedisCluster, getlasterror);
 ZEND_METHOD(RedisCluster, getmode);
 ZEND_METHOD(RedisCluster, getoption);
 ZEND_METHOD(RedisCluster, getrange);
+ZEND_METHOD(RedisCluster, lcs);
 ZEND_METHOD(RedisCluster, getset);
 ZEND_METHOD(RedisCluster, hdel);
 ZEND_METHOD(RedisCluster, hexists);
@@ -988,6 +995,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 1db729a667..22c19a8d40 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -58,6 +58,13 @@ typedef struct geoOptions {
     zend_string *key;
 } geoOptions;
 
+typedef struct redisLcsOptions {
+    zend_bool len;
+    zend_bool idx;
+    zend_long minmatchlen;
+    zend_bool withmatchlen;
+} redisLcsOptions;
+
 /* Local passthrough macro for command construction.  Given that these methods
  * are generic (so they work whether the caller is Redis or RedisCluster) we
  * will always have redis_sock, slot*, and */
@@ -2222,6 +2229,102 @@ redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) {
+    zend_string *key;
+    zval *zv;
+
+    ZEND_ASSERT(dst != NULL);
+
+    memset(dst, 0, sizeof(*dst));
+
+    if (ht == NULL)
+        return;
+
+    ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) {
+        if (key) {
+            if (zend_string_equals_literal_ci(key, "LEN")) {
+                dst->idx = 0;
+                dst->len = zval_is_true(zv);
+            } else if (zend_string_equals_literal_ci(key, "IDX")) {
+                dst->len = 0;
+                dst->idx = zval_is_true(zv);
+            } else if (zend_string_equals_literal_ci(key, "MINMATCHLEN")) {
+                dst->minmatchlen = zval_get_long(zv);
+            } else if (zend_string_equals_literal_ci(key, "WITHMATCHLEN")) {
+                dst->withmatchlen = zval_is_true(zv);
+            } else {
+                php_error_docref(NULL, E_WARNING, "Unknown LCS option '%s'", ZSTR_VAL(key));
+            }
+        } else if (Z_TYPE_P(zv) == IS_STRING) {
+            if (zend_string_equals_literal_ci(Z_STR_P(zv), "LEN")) {
+                dst->idx = 0;
+                dst->len = 1;
+            } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "IDX")) {
+                dst->idx = 1;
+                dst->len = 0;
+            } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "WITHMATCHLEN")) {
+                dst->withmatchlen = 1;
+            }
+        }
+    } ZEND_HASH_FOREACH_END();
+}
+
+/* LCS */
+int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                  char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    zend_string *key1 = NULL, *key2 = NULL;
+    smart_string cmdstr = {0};
+    HashTable *ht = NULL;
+    redisLcsOptions opt;
+    int argc;
+
+    ZEND_PARSE_PARAMETERS_START(2, 3)
+        Z_PARAM_STR(key1)
+        Z_PARAM_STR(key2)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_ARRAY_HT_OR_NULL(ht)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    key1 = redis_key_prefix_zstr(redis_sock, key1);
+    key2 = redis_key_prefix_zstr(redis_sock, key2);
+
+    if (slot) {
+        *slot = cluster_hash_key_zstr(key1);
+        if (*slot != cluster_hash_key_zstr(key2)) {
+            php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!");
+            zend_string_release(key1);
+            zend_string_release(key2);
+            return FAILURE;
+        }
+    }
+
+    redis_get_lcs_options(&opt, ht);
+
+    argc = 2 + !!opt.idx + !!opt.len + !!opt.withmatchlen + (opt.minmatchlen ? 2 : 0);
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "LCS");
+
+    redis_cmd_append_sstr_zstr(&cmdstr, key1);
+    redis_cmd_append_sstr_zstr(&cmdstr, key2);
+
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.idx, "IDX");
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.len, "LEN");
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withmatchlen, "WITHMATCHLEN");
+
+    if (opt.minmatchlen) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MINMATCHLEN");
+        redis_cmd_append_sstr_long(&cmdstr, opt.minmatchlen);
+    }
+
+    zend_string_release(key1);
+    zend_string_release(key2);
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return SUCCESS;
+}
+
+
 /* BITPOS */
 int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                      char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index 24fbe92c4c..c51896add7 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -129,6 +129,9 @@ int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                         char *kw, char **cmd, int *cmd_len, short *slot,
                         void **ctx);
 
+int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                  char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index aaf02d1973..7954c6f2d2 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: f43a528bc5874c419161b5eb874537fbe9c00e87 */
+ * Stub hash: 1c32099810101448fc32ce331e2b494dabc22cc4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -279,6 +279,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, end)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lcs, 0, 0, 2)
+	ZEND_ARG_INFO(0, key1)
+	ZEND_ARG_INFO(0, key2)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_getReadTimeout arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_getset arginfo_class_Redis_append
@@ -953,6 +959,7 @@ ZEND_METHOD(Redis, getOption);
 ZEND_METHOD(Redis, getPersistentID);
 ZEND_METHOD(Redis, getPort);
 ZEND_METHOD(Redis, getRange);
+ZEND_METHOD(Redis, lcs);
 ZEND_METHOD(Redis, getReadTimeout);
 ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, getTimeout);
@@ -1187,6 +1194,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 551c6346dd..2e4402f343 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -272,6 +272,27 @@ public function testBitsets() {
         $this->assertEquals(1, $this->redis->getBit('key', 0x7fffffff));
     }
 
+    public function testLcs() {
+        $key1 = '{lcs}1'; $key2 = '{lcs}2';
+        $this->assertTrue($this->redis->set($key1, '12244447777777'));
+        $this->assertTrue($this->redis->set($key2, '6666662244441'));
+
+        $this->assertEquals('224444', $this->redis->lcs($key1, $key2));
+
+        $this->assertEquals(
+            ['matches', [[[1, 6], [6, 11]]], 'len', 6],
+            $this->redis->lcs($key1, $key2, ['idx'])
+        );
+        $this->assertEquals(
+            ['matches', [[[1, 6], [6, 11], 6]], 'len', 6],
+            $this->redis->lcs($key1, $key2, ['idx', 'withmatchlen'])
+        );
+
+        $this->assertEquals(6, $this->redis->lcs($key1, $key2, ['len']));
+
+        $this->redis->del([$key1, $key2]);
+    }
+
     public function testBitPos() {
         if (version_compare($this->version, "2.8.7") < 0) {
             $this->MarkTestSkipped();

From f5b2a09b3402dda28dc5b23bd9fdce32284ab550 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 1 Oct 2022 16:41:52 -0700
Subject: [PATCH 1622/1986] EXPIRETIME and PEXPIRETIME

---
 redis.c                        | 12 ++++++++++++
 redis.stub.php                 |  4 ++++
 redis_arginfo.h                | 12 +++++++++++-
 redis_cluster.c                | 10 ++++++++++
 redis_cluster.stub.php         |  4 ++++
 redis_cluster_arginfo.h        | 12 +++++++++++-
 redis_cluster_legacy_arginfo.h | 10 +++++++++-
 redis_legacy_arginfo.h         | 10 +++++++++-
 tests/RedisTest.php            | 15 +++++++++++++++
 9 files changed, 85 insertions(+), 4 deletions(-)

diff --git a/redis.c b/redis.c
index 1a9af65b0f..e7f5506c1d 100644
--- a/redis.c
+++ b/redis.c
@@ -1654,6 +1654,18 @@ PHP_METHOD(Redis, pexpireAt) {
 }
 /* }}} */
 
+/* {{{ proto Redis::expiretime(string $key): int */
+PHP_METHOD(Redis, expiretime) {
+    REDIS_PROCESS_KW_CMD("EXPIRETIME", redis_key_cmd, redis_long_response);
+}
+/* }}} */
+
+/* {{{ proto Redis::expiretime(string $key): int */
+PHP_METHOD(Redis, pexpiretime) {
+    REDIS_PROCESS_KW_CMD("PEXPIRETIME", redis_key_cmd, redis_long_response);
+}
+
+/* }}} */
 /* {{{ proto array Redis::lSet(string key, int index, string value) */
 PHP_METHOD(Redis, lSet) {
     REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd,
diff --git a/redis.stub.php b/redis.stub.php
index c3740b8d48..79e2096de1 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -120,6 +120,10 @@ public function expireAt(string $key, int $timestamp): bool;
 
     public function failover(?array $to = null, bool $abort = false, int $timeout = 0): bool;
 
+    public function expiretime(string $key): Redis|int|false;
+
+    public function pexpiretime(string $key): Redis|int|false;
+
     public function flushAll(?bool $sync = null): bool;
 
     public function flushDB(?bool $sync = null): bool;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 7c2cdd4385..86f6373d9f 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 1c32099810101448fc32ce331e2b494dabc22cc4 */
+ * Stub hash: 177e08fec3c3ef380c1cdbab99235090c656cde4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -190,6 +190,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_failover, 0, 0, _IS_
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expiretime, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_pexpiretime arginfo_class_Redis_expiretime
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sync, _IS_BOOL, 1, "null")
 ZEND_END_ARG_INFO()
@@ -1042,6 +1048,8 @@ ZEND_METHOD(Redis, exists);
 ZEND_METHOD(Redis, expire);
 ZEND_METHOD(Redis, expireAt);
 ZEND_METHOD(Redis, failover);
+ZEND_METHOD(Redis, expiretime);
+ZEND_METHOD(Redis, pexpiretime);
 ZEND_METHOD(Redis, flushAll);
 ZEND_METHOD(Redis, flushDB);
 ZEND_METHOD(Redis, geoadd);
@@ -1277,6 +1285,8 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, expiretime, arginfo_class_Redis_expiretime, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index cee00ae27c..00fa3801e4 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1273,6 +1273,16 @@ PHP_METHOD(RedisCluster, pexpireat) {
 }
 /* }}} */
 
+/* {{{ Redis::expiretime(string $key): int */
+PHP_METHOD(RedisCluster, expiretime) {
+    CLUSTER_PROCESS_KW_CMD("EXPIRETIME", redis_key_cmd, cluster_long_resp, 1);
+}
+
+/* {{{ Redis::pexpiretime(string $key): int */
+PHP_METHOD(RedisCluster, pexpiretime) {
+    CLUSTER_PROCESS_KW_CMD("PEXPIRETIME", redis_key_cmd, cluster_long_resp, 1);
+}
+
 /* {{{ proto long RedisCluster::append(string key, string val) */
 PHP_METHOD(RedisCluster, append) {
     CLUSTER_PROCESS_KW_CMD("APPEND", redis_kv_cmd, cluster_long_resp, 0);
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 31ba4ef634..f750e072df 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -92,6 +92,10 @@ public function expire(string $key, int $timeout): bool;
 
     public function expireat(string $key, int $timestamp): bool;
 
+    public function expiretime(string $key): Redis|int|false;
+
+    public function pexpiretime(string $key): Redis|int|false;
+
     public function flushall(string|array $node, bool $async = false): bool;
 
     public function flushdb(string|array $node, bool $async = false): bool;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index bbf10c9200..dbfb302610 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 45d120a35a1c256964c45c71ab91c025ea00e862 */
+ * Stub hash: d6e8120d2edd3cb4a18baa99c6013ac428049448 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -183,6 +183,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_expireat, 0,
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiretime, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_pexpiretime arginfo_class_RedisCluster_expiretime
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_flushall, 0, 1, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, async, _IS_BOOL, 0, "false")
@@ -894,6 +900,8 @@ ZEND_METHOD(RedisCluster, exec);
 ZEND_METHOD(RedisCluster, exists);
 ZEND_METHOD(RedisCluster, expire);
 ZEND_METHOD(RedisCluster, expireat);
+ZEND_METHOD(RedisCluster, expiretime);
+ZEND_METHOD(RedisCluster, pexpiretime);
 ZEND_METHOD(RedisCluster, flushall);
 ZEND_METHOD(RedisCluster, flushdb);
 ZEND_METHOD(RedisCluster, geoadd);
@@ -1091,6 +1099,8 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expireat, arginfo_class_RedisCluster_expireat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, expiretime, arginfo_class_RedisCluster_expiretime, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pexpiretime, arginfo_class_RedisCluster_pexpiretime, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, flushall, arginfo_class_RedisCluster_flushall, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, flushdb, arginfo_class_RedisCluster_flushdb, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, geoadd, arginfo_class_RedisCluster_geoadd, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 5669c8be7f..a153816915 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 45d120a35a1c256964c45c71ab91c025ea00e862 */
+ * Stub hash: d6e8120d2edd3cb4a18baa99c6013ac428049448 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -163,6 +163,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expireat, 0, 0, 2)
 	ZEND_ARG_INFO(0, timestamp)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_expiretime arginfo_class_RedisCluster__prefix
+
+#define arginfo_class_RedisCluster_pexpiretime arginfo_class_RedisCluster__prefix
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_flushall, 0, 0, 1)
 	ZEND_ARG_INFO(0, node)
 	ZEND_ARG_INFO(0, async)
@@ -782,6 +786,8 @@ ZEND_METHOD(RedisCluster, exec);
 ZEND_METHOD(RedisCluster, exists);
 ZEND_METHOD(RedisCluster, expire);
 ZEND_METHOD(RedisCluster, expireat);
+ZEND_METHOD(RedisCluster, expiretime);
+ZEND_METHOD(RedisCluster, pexpiretime);
 ZEND_METHOD(RedisCluster, flushall);
 ZEND_METHOD(RedisCluster, flushdb);
 ZEND_METHOD(RedisCluster, geoadd);
@@ -979,6 +985,8 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expireat, arginfo_class_RedisCluster_expireat, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, expiretime, arginfo_class_RedisCluster_expiretime, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, pexpiretime, arginfo_class_RedisCluster_pexpiretime, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, flushall, arginfo_class_RedisCluster_flushall, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, flushdb, arginfo_class_RedisCluster_flushdb, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, geoadd, arginfo_class_RedisCluster_geoadd, ZEND_ACC_PUBLIC)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 7954c6f2d2..0a0f7016c4 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 1c32099810101448fc32ce331e2b494dabc22cc4 */
+ * Stub hash: 177e08fec3c3ef380c1cdbab99235090c656cde4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -174,6 +174,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_failover, 0, 0, 0)
 	ZEND_ARG_INFO(0, timeout)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_expiretime arginfo_class_Redis__prefix
+
+#define arginfo_class_Redis_pexpiretime arginfo_class_Redis__prefix
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, 0)
 	ZEND_ARG_INFO(0, sync)
 ZEND_END_ARG_INFO()
@@ -934,6 +938,8 @@ ZEND_METHOD(Redis, exists);
 ZEND_METHOD(Redis, expire);
 ZEND_METHOD(Redis, expireAt);
 ZEND_METHOD(Redis, failover);
+ZEND_METHOD(Redis, expiretime);
+ZEND_METHOD(Redis, pexpiretime);
 ZEND_METHOD(Redis, flushAll);
 ZEND_METHOD(Redis, flushDB);
 ZEND_METHOD(Redis, geoadd);
@@ -1169,6 +1175,8 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, expireAt, arginfo_class_Redis_expireAt, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, expiretime, arginfo_class_Redis_expiretime, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 2e4402f343..4d022a6ed2 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -605,6 +605,21 @@ public function testExpireAt() {
         $this->assertTrue($success);
     }
 
+    public function testExpiretime() {
+        if(version_compare($this->version, "7.0.0") < 0) {
+            $this->markTestSkipped();
+        }
+
+        $now = time();
+
+        $this->assertTrue($this->redis->set('key1', 'value'));
+        $this->assertTrue($this->redis->expireat('key1', $now + 10));
+        $this->assertEquals($now + 10, $this->redis->expiretime('key1'));
+        $this->assertEquals(1000 * ($now + 10), $this->redis->pexpiretime('key1'));
+
+        $this->redis->del('key1');
+    }
+
     public function testSetEx() {
 
         $this->redis->del('key');

From d35e2664bb65525b2ae347471094eb4fb1de18d8 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 3 Oct 2022 12:10:43 -0700
Subject: [PATCH 1623/1986] Relax OBJECT idletime tests to avoid false
 failures.

Instead of testing that `OBJECT IDLETIME` returns exactly zero after
updating a key in our tests, just make sure PhpRedis returns us some
numeric value.

We can't really test for a specific number of elapsed seconds in CI as
the VMs may be randomly preempted at times beyond our control, causing
the tests to "fail" even though there was no actual failure.
---
 tests/RedisTest.php | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 4d022a6ed2..f61e263a7a 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -3060,7 +3060,7 @@ public function testObject() {
         $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->assertTrue(is_numeric($this->redis->object('idletime', 'key')));
 
         $this->redis->del('key');
         $this->redis->lpush('key', 'value');
@@ -3071,20 +3071,20 @@ public function testObject() {
         $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->assertTrue(is_numeric($this->redis->object('idletime', 'key')));
 
         $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->assertTrue(is_numeric($this->redis->object('idletime', 'key')));
 
         $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->assertTrue(is_numeric($this->redis->object('idletime', 'key')));
 
         $this->redis->del('key');
         $this->redis->lpush('key', str_repeat('A', pow(10,6))); // 1M elements, too big for a ziplist.
@@ -3093,7 +3093,7 @@ public function testObject() {
         $this->assertTrue($str_encoding === "linkedlist" || $str_encoding == "quicklist");
 
         $this->assertTrue($this->redis->object('refcount', 'key') === 1);
-        $this->assertTrue($this->redis->object('idletime', 'key') === 0);
+        $this->assertTrue(is_numeric($this->redis->object('idletime', 'key')));
     }
 
     public function testMultiExec() {

From 50151e7a4f7b6f831908edbdafe754b8adc2d099 Mon Sep 17 00:00:00 2001
From: Yurun 
Date: Tue, 19 Oct 2021 10:57:11 +0800
Subject: [PATCH 1624/1986] Fix doc xGroup param value type

---
 README.markdown | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.markdown b/README.markdown
index 43a1a2b285..cb94e9c88b 100644
--- a/README.markdown
+++ b/README.markdown
@@ -3765,8 +3765,8 @@ _**Description**_:  This command is used in order to create, destroy, or manage
 
 ##### *Example*
 ~~~php
-$obj_redis->xGroup('CREATE', 'mystream', 'mygroup', 0);
-$obj_redis->xGroup('CREATE', 'mystream', 'mygroup2', 0, true); /* Create stream if non-existent. */
+$obj_redis->xGroup('CREATE', 'mystream', 'mygroup', '0');
+$obj_redis->xGroup('CREATE', 'mystream', 'mygroup2', '0', true); /* Create stream if non-existent. */
 $obj_redis->xGroup('DESTROY', 'mystream', 'mygroup');
 ~~~
 

From 72b37fa93e52a868ea033379ff4e2b9f616c0fed Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 5 Oct 2022 14:56:45 -0700
Subject: [PATCH 1625/1986] PHP 8.1 deprecations

Co-authored-by: 
---
 README.markdown | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.markdown b/README.markdown
index cb94e9c88b..6ecba61f6d 100644
--- a/README.markdown
+++ b/README.markdown
@@ -224,7 +224,7 @@ _**Description**_: Connects to a Redis instance.
 *host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema 
 *port*: int, optional  
 *timeout*: float, value in seconds (optional, default is 0 meaning unlimited)  
-*reserved*: should be NULL if retry_interval is specified  
+*reserved*: should be '' if retry_interval is specified  
 *retry_interval*: int, value in milliseconds (optional)  
 *read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited)
 *others*: array, with PhpRedis >= 5.3.0, it allows setting _auth_ and [_stream_](https://www.php.net/manual/en/context.ssl.php) configuration.
@@ -242,11 +242,11 @@ $redis->connect('tls://127.0.0.1', 6379); // enable transport level security.
 $redis->connect('tls://127.0.0.1'); // enable transport level security, port 6379 by default.
 $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
 $redis->connect('/tmp/redis.sock'); // unix domain socket.
-$redis->connect('127.0.0.1', 6379, 1, NULL, 100); // 1 sec timeout, 100ms delay between reconnection attempts.
+$redis->connect('127.0.0.1', 6379, 1, '', 100); // 1 sec timeout, 100ms delay between reconnection attempts.
 $redis->connect('/tmp/redis.sock', 0, 1.5, NULL, 0, 1.5); // Unix socket with 1.5s timeouts (connect and read)
 
 /* With PhpRedis >= 5.3.0 you can specify authentication and stream information on connect */
-$redis->connect('127.0.0.1', 6379, 1, NULL, 0, 0, ['auth' => ['phpredis', 'phpredis']]);
+$redis->connect('127.0.0.1', 6379, 1, '', 0, 0, ['auth' => ['phpredis', 'phpredis']]);
 ~~~
 
 **Note:** `open` is an alias for `connect` and will be removed in future versions of phpredis.

From b9950727f5c2fe25262e9e899bc31980d7e77f5c Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 5 Oct 2022 20:40:44 -0700
Subject: [PATCH 1626/1986] Fix documentation for resetStat

There is no `resetStat` command, but there is `CONFIG RESETSTAT`.

Fixes #928
---
 README.markdown | 36 ++++++------------------------------
 1 file changed, 6 insertions(+), 30 deletions(-)

diff --git a/README.markdown b/README.markdown
index 6ecba61f6d..d910bf3471 100644
--- a/README.markdown
+++ b/README.markdown
@@ -501,7 +501,6 @@ $redis->setOption(Redis::OPT_BACKOFF_CAP, 750); // backoff time capped at 750ms
 1. [flushDb](#flushdb) - Remove all keys from the current database
 1. [info](#info) - Get information and statistics about the server
 1. [lastSave](#lastsave) - Get the timestamp of the last disk save
-1. [resetStat](#resetstat) - Reset the stats returned by [info](#info) method.
 1. [save](#save) - Synchronously save the dataset to disk (wait to complete)
 1. [slaveOf](#slaveof) - Make the server a slave of another instance, or promote it to master
 1. [time](#time) - Return the current server time
@@ -558,19 +557,20 @@ $redis->bgSave();
 -----
 _**Description**_: Get or Set the Redis server configuration parameters.
 
-##### *Parameters*
-*operation* (string) either `GET` or `SET`  
-*key* string for `SET`, glob-pattern for `GET`. See http://redis.io/commands/config-get for examples.  
-*value* optional string (only for `SET`)
+##### *Prototype*
+~~~php
+$redis->config($operation, ?string $key = NULL, ?string $value = NULL): mixed;
+~~~
 
 ##### *Return value*
 *Associative array* for `GET`, key -> value  
-*bool* for `SET`
+*bool* for `SET`, and `RESETSTAT`
 
 ##### *Examples*
 ~~~php
 $redis->config("GET", "*max-*-entries*");
 $redis->config("SET", "dir", "/var/run/redis/dumps/");
+$redis->config('RESETSTAT');
 ~~~
 
 ### dbSize
@@ -668,30 +668,6 @@ None.
 $redis->lastSave();
 ~~~
 
-### resetStat
------
-_**Description**_: Reset the stats returned by [info](#info) method.
-
-These are the counters that are reset:
-
-* Keyspace hits
-* Keyspace misses
-* Number of commands processed
-* Number of connections received
-* Number of expired keys
-
-
-##### *Parameters*
-None.
-
-##### *Return value*
-*BOOL*: `TRUE` in case of success, `FALSE` in case of failure.
-
-##### *Example*
-~~~php
-$redis->resetStat();
-~~~
-
 ### save
 -----
 _**Description**_: Synchronously save the dataset to disk (wait to complete)

From ab4ce4ab6f6eadbae8245fef5486674eff0d1661 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 7 Oct 2022 11:27:23 -0700
Subject: [PATCH 1627/1986] Allow negative timeout/read_timeout and fix doc

Co-authored-by: twosee 
---
 README.markdown   | 8 ++++----
 cluster_library.c | 4 ++--
 redis.c           | 4 ++--
 redis_sentinel.c  | 4 ++--
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/README.markdown b/README.markdown
index d910bf3471..5690c089c2 100644
--- a/README.markdown
+++ b/README.markdown
@@ -223,10 +223,10 @@ _**Description**_: Connects to a Redis instance.
 
 *host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema 
 *port*: int, optional  
-*timeout*: float, value in seconds (optional, default is 0 meaning unlimited)  
+*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)
 *reserved*: should be '' if retry_interval is specified  
 *retry_interval*: int, value in milliseconds (optional)  
-*read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited)
+*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)
 *others*: array, with PhpRedis >= 5.3.0, it allows setting _auth_ and [_stream_](https://www.php.net/manual/en/context.ssl.php) configuration.
 
 ##### *Return value*
@@ -271,10 +271,10 @@ persistent equivalents.
 
 *host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema 
 *port*: int, optional  
-*timeout*: float, value in seconds (optional, default is 0 meaning unlimited)  
+*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)
 *persistent_id*: string. identity for the requested persistent connection  
 *retry_interval*: int, value in milliseconds (optional)  
-*read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited)
+*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)
 
 ##### *Return value*
 
diff --git a/cluster_library.c b/cluster_library.c
index 6e69c51847..c76c32697f 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -2873,12 +2873,12 @@ cluster_validate_args(double timeout, double read_timeout, HashTable *seeds,
 {
     zend_string **retval;
 
-    if (timeout < 0L || timeout > INT_MAX) {
+    if (timeout > INT_MAX) {
         if (errstr) *errstr = "Invalid timeout";
         return NULL;
     }
 
-    if (read_timeout < 0L || read_timeout > INT_MAX) {
+    if (read_timeout > INT_MAX) {
         if (errstr) *errstr = "Invalid read timeout";
         return NULL;
     }
diff --git a/redis.c b/redis.c
index e7f5506c1d..0e00ba8c36 100644
--- a/redis.c
+++ b/redis.c
@@ -718,12 +718,12 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
         persistent_id = NULL;
     }
 
-    if (timeout < 0L || timeout > INT_MAX) {
+    if (timeout > INT_MAX) {
         REDIS_VALUE_EXCEPTION("Invalid connect timeout");
         return FAILURE;
     }
 
-    if (read_timeout < 0L || read_timeout > INT_MAX) {
+    if (read_timeout > INT_MAX) {
         REDIS_VALUE_EXCEPTION("Invalid read timeout");
         return FAILURE;
     }
diff --git a/redis_sentinel.c b/redis_sentinel.c
index 5aa4442018..55851f1025 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -61,12 +61,12 @@ PHP_METHOD(RedisSentinel, __construct)
         RETURN_THROWS();
     }
 
-    if (timeout < 0L || timeout > INT_MAX) {
+    if (timeout > INT_MAX) {
         REDIS_VALUE_EXCEPTION("Invalid connect timeout");
         RETURN_THROWS();
     }
 
-    if (read_timeout < 0L || read_timeout > INT_MAX) {
+    if (read_timeout > INT_MAX) {
         REDIS_VALUE_EXCEPTION("Invalid read timeout");
         RETURN_THROWS();
     }

From 19bb553e97ccc4a1cc8f24926d1cd8080a53c529 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 7 Oct 2022 12:20:45 -0700
Subject: [PATCH 1628/1986] Reorder contact info so Nicolas doesn't get emails
 to forward.

---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index 5690c089c2..329abcf2dd 100644
--- a/README.markdown
+++ b/README.markdown
@@ -7,7 +7,7 @@
 The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt).
 This code has been developed and maintained by Owlient from November 2009 to March 2011.
 
-You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)) or to p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)).
+You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)), p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)), or n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)).
 
 ## Supporting the project
 PhpRedis will always be free and open source software, but if you or your company has found it useful please consider supporting the project.  Developing a large, complex, and performant library like PhpRedis takes a great deal of time and effort, and support would be appreciated! :heart:

From bebd398c67eaba7d7f612bc1bf5f07a7470787f2 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 4 Oct 2022 13:24:08 -0700
Subject: [PATCH 1629/1986] WIP:  Update stubs so the tests pass in strict mode

These changes allow the PHP 8 unit tests to pass even when zpp strict
mode is enabled.

I'm not exactly sure which setting causes the issue, but without these
changes I get many `zpp` errors if I run the tests inside of a PHP build
tree.
---
 redis.c                |   2 +-
 redis.stub.php         | 171 ++++++++++++++--------------
 redis_arginfo.h        | 247 ++++++++++++++++++++++++-----------------
 redis_commands.c       |   1 +
 redis_legacy_arginfo.h |  21 ++--
 tests/RedisTest.php    |   3 -
 6 files changed, 249 insertions(+), 196 deletions(-)

diff --git a/redis.c b/redis.c
index 0e00ba8c36..a9d1f2d60d 100644
--- a/redis.c
+++ b/redis.c
@@ -3434,7 +3434,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
     if(type != TYPE_SCAN) {
         // Requires a key
         if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                        "Osz/|s!l", &object, redis_ce, &key,
+                                        "Os!z/|s!l", &object, redis_ce, &key,
                                         &key_len, &z_iter, &pattern,
                                         &pattern_len, &count)==FAILURE)
         {
diff --git a/redis.stub.php b/redis.stub.php
index 79e2096de1..c2576b68b1 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -52,11 +52,11 @@ public function bitop(string $operation, string $deskey, string $srckey, string
     /** @return int|Redis */
     public function bitpos(string $key, int $bit, int $start = 0, int $end = -1);
 
-    public function blPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): array;
+    public function blPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): array|null|false;
 
-    public function brPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): array;
+    public function brPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): array|null|false;
 
-    public function brpoplpush(string $src, string $dst, int $timeout): string;
+    public function brpoplpush(string $src, string $dst, int $timeout): Redis|string|false;
 
     public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
 
@@ -81,7 +81,7 @@ public function dbSize(): int;
     public function debug(string $key): string;
 
 	/** @return int|Redis */
-    public function decr(string $key);
+    public function decr(string $key, int $by = 1);
 
 	/** @return int|Redis */
     public function decrBy(string $key, int $value);
@@ -109,14 +109,14 @@ public function eval(string $script, array $keys = null, int $num_keys = 0): mix
 
     public function evalsha(string $sha1, array $keys = null, int $num_keys = 0): mixed;
 
-    public function exec(): array;
+    public function exec(): Redis|array|false;
 
-	/** @return bool|Redis */
-    public function exists(string $key);
+	/** @return int|Redis|bool */
+    public function exists(mixed $key, mixed ...$other_keys);
 
-    public function expire(string $key, int $timeout): bool;
+    public function expire(string $key, int $timeout): Redis|bool;
 
-    public function expireAt(string $key, int $timestamp): bool;
+    public function expireAt(string $key, int $timestamp): Redis|bool;
 
     public function failover(?array $to = null, bool $abort = false, int $timeout = 0): bool;
 
@@ -130,19 +130,19 @@ public function flushDB(?bool $sync = null): bool;
 
     public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): int;
 
-    public function geodist(string $key, string $src, string $dst, ?string $unit = null): array;
+    public function geodist(string $key, string $src, string $dst, ?string $unit = null): Redis|float|false;
 
     public function geohash(string $key, string $member, string ...$other_members): array;
 
-    public function geopos(string $key, string $member, string ...$other_members): array;
+    public function geopos(string $key, string $member, string ...$other_members): Redis|array|false;
 
-    public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): array;
+    public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): Redis|mixed|false;
 
-    public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): array;
+    public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): Redis|mixed|false;
 
-    public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): array;
+    public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): Redis|mixed|false;
 
-    public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): array;
+    public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): Redis|mixed|false;
 
     public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): array;
 
@@ -186,40 +186,40 @@ public function getset(string $key, mixed $value);
 
     public function getTimeout(): int;
 
-    public function hDel(string $key, string $member, string ...$other_members): int;
+    public function hDel(string $key, string $member, string ...$other_members): Redis|int|false;
 
-    public function hExists(string $key, string $member): bool;
+    public function hExists(string $key, string $member): Redis|bool;
 
-    public function hGet(string $key, string $member): string;
+    public function hGet(string $key, string $member): Redis|mixed|false;
 
-    public function hGetAll(string $key): array;
+    public function hGetAll(string $key): Redis|array|false;
 
-    public function hIncrBy(string $key, string $member, int $value): int;
+    public function hIncrBy(string $key, string $member, int $value): Redis|int|false;
 
-    public function hIncrByFloat(string $key, string $member, float $value): float;
+    public function hIncrByFloat(string $key, string $member, float $value): Redis|float|false;
 
-    public function hKeys(string $key): array;
+    public function hKeys(string $key): Redis|array|false;
 
-    public function hLen(string $key): int;
+    public function hLen(string $key): Redis|int|false;
 
-    public function hMget(string $key, array $keys): array;
+    public function hMget(string $key, array $keys): Redis|array|false;
 
-    public function hMset(string $key, array $keyvals): bool;
+    public function hMset(string $key, array $keyvals): Redis|bool|false;
 
-    public function hRandField(string $key, array $options = null): string|array;
+    public function hRandField(string $key, array $options = null): Redis|string|array;
 
-    public function hSet(string $key, string $member, string $value): int;
+    public function hSet(string $key, string $member, mixed $value): Redis|int|false;
 
-    public function hSetNx(string $key, string $member, string $value): int;
+    public function hSetNx(string $key, string $member, string $value): Redis|bool;
 
     public function hStrLen(string $key, string $member): int;
 
-    public function hVals(string $key): array;
+    public function hVals(string $key): Redis|array|false;
 
     public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
 	/** @return int|Redis */
-    public function incr(string $key);
+    public function incr(string $key, int $by = 1);
 
 	/** @return int|Redis */
     public function incrBy(string $key, int $value);
@@ -227,7 +227,7 @@ public function incrBy(string $key, int $value);
 	/** @return int|Redis */
     public function incrByFloat(string $key, float $value);
 
-    public function info(string $opt = null): array;
+    public function info(string $opt = null): Redis|array|false;
 
     public function isConnected(): bool;
 
@@ -241,7 +241,7 @@ public function keys(string $pattern);
     public function lInsert(string $key, string $pos, mixed $pivot, mixed $value);
 
 
-    public function lLen(string $key): int;
+    public function lLen(string $key): Redis|int|false;
 
     public function lMove(string $src, string $dst, string $wherefrom, string $whereto): string;
 
@@ -267,17 +267,20 @@ public function lPushx(string $key, mixed $value);
 	/** @return int|Redis */
     public function rPushx(string $key, mixed $value);
 
-    public function lSet(string $key, int $index, string $value): bool;
+    public function lSet(string $key, int $index, mixed $value): Redis|bool;
 
     public function lastSave(): int;
 
-    public function lindex(string $key, int $index): string;
+    public function lindex(string $key, int $index): Redis|mixed|false;
 
-    public function lrange(string $key, int $start , int $end): array;
+    public function lrange(string $key, int $start , int $end): Redis|array|false;
 
-    public function lrem(string $key, string $value, int $count = 0): bool;
+    /**
+     * @return int|Redis|false
+     */
+    public function lrem(string $key, mixed $value, int $count = 0);
 
-    public function ltrim(string $key, int $start , int $end): bool;
+    public function ltrim(string $key, int $start , int $end): Redis|bool;
 
 	/** @return array|Redis */
     public function mget(array $keys);
@@ -286,13 +289,13 @@ public function migrate(string $host, int $port, string $key, string $dst, int $
 
     public function move(string $key, int $index): bool;
 
-    public function mset(array $key_values): bool;
+    public function mset(array $key_values): Redis|bool;
 
-    public function msetnx(array $key_values): int;
+    public function msetnx(array $key_values): Redis|bool;
 
     public function multi(int $value = Redis::MULTI): bool|Redis;
 
-    public function object(string $subcommand, string $key): int|string;
+    public function object(string $subcommand, string $key): Redis|int|string|false;
 
     /**
      * @deprecated
@@ -330,9 +333,9 @@ public function psetex(string $key, int $expire, mixed $value);
 
     public function psubscribe(array $patterns, callable $cb): bool;
 
-    public function pttl(string $key): int;
+    public function pttl(string $key): Redis|int|false;
 
-    public function publish(string $channel, string $message): int;
+    public function publish(string $channel, string $message): mixed;
 
     public function pubsub(string $command, mixed $arg = null): mixed;
 
@@ -357,41 +360,41 @@ public function restore(string $key, int $timeout, string $value): bool;
 
     public function role(): mixed;
 
-    public function rpoplpush(string $src, string $dst): string;
+    public function rpoplpush(string $src, string $dst): Redis|string|false;
 
-    public function sAdd(string $key, mixed $value, mixed ...$other_values): int;
+    public function sAdd(string $key, mixed $value, mixed ...$other_values): Redis|int|false;
 
     public function sAddArray(string $key, array $values): int;
 
-    public function sDiff(string $key, string ...$other_keys): array;
+    public function sDiff(string $key, string ...$other_keys): Redis|array|false;
 
-    public function sDiffStore(string $dst, string $key, string ...$other_keys): int;
+    public function sDiffStore(string $dst, string $key, string ...$other_keys): Redis|int|false;
 
-    public function sInter(string $key, string ...$other_keys): array;
+    public function sInter(array|string $key, string ...$other_keys): Redis|array|false;
 
     public function sintercard(array $keys, int $limit = -1): Redis|int|false;
 
-    public function sInterStore(string $dst, string $key, string ...$other_keys): int;
+    public function sInterStore(array|string $key, string ...$other_keys): Redis|int|false;
 
-    public function sMembers(string $key): array;
+    public function sMembers(string $key): Redis|array|false;
 
     public function sMisMember(string $key, string $member, string ...$other_members): array;
 
-    public function sMove(string $src, string $dst, mixed $value): bool;
+    public function sMove(string $src, string $dst, mixed $value): Redis|bool;
 
-    public function sPop(string $key, int $count = 0): string|array;
+    public function sPop(string $key, int $count = 0): Redis|string|array|false;
 
-    public function sRandMember(string $key, int $count = 0): string|array;
+    public function sRandMember(string $key, int $count = 0): Redis|string|array|false;
 
-    public function sUnion(string $key, string ...$other_keys): array;
+    public function sUnion(string $key, string ...$other_keys): Redis|array|false;
 
-    public function sUnionStore(string $dst, string $key, string ...$other_keys): int;
+    public function sUnionStore(string $dst, string $key, string ...$other_keys): Redis|int|false;
 
     public function save(): bool;
 
-    public function scan(?int &$iterator, ?string $pattern = null, int $count = 0): array;
+    public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, string $type = NULL): array|false;
 
-    public function scard(string $key): int;
+    public function scard(string $key): Redis|int|false;
 
     public function script(string $command, mixed ...$args): mixed;
 
@@ -415,7 +418,7 @@ public function setex(string $key, int $expire, mixed $value);
 	/** @return bool|array|Redis */
     public function setnx(string $key, mixed $value);
 
-    public function sismember(string $key, string $value): bool;
+    public function sismember(string $key, mixed $value): Redis|bool;
 
     public function slaveof(string $host = null, int $port = 6379): bool;
 
@@ -443,9 +446,9 @@ public function sortDesc(string $key, ?string $pattern = null, mixed $get = null
      */
     public function sortDescAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array;
 
-    public function srem(string $key, string $value, string ...$other_values): int;
+    public function srem(string $key, mixed $value, mixed ...$other_values): Redis|int|false;
 
-    public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array;
+    public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
 
 	/** @return int|Redis */
     public function strlen(string $key);
@@ -456,7 +459,7 @@ public function swapdb(string $src, string $dst): bool;
 
     public function time(): array;
 
-    public function ttl(string $key): int;
+    public function ttl(string $key): Redis|int|false;
 
 	/** @return int|Redis */
     public function type(string $key);
@@ -476,25 +479,25 @@ public function unwatch();
      */
     public function watch(array|string $key, string ...$other_keys);
 
-    public function wait(int $count, int $timeout): int;
+    public function wait(int $count, int $timeout): int|false;
 
-    public function xack(string $key, string $group, array $ids): int;
+    public function xack(string $key, string $group, array $ids): int|false;
 
-    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): string;
+    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): string|false;
 
     public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): bool|array;
 
     public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): bool|array;
 
-    public function xdel(string $key, array $ids): int;
+    public function xdel(string $key, array $ids): Redis|int|false;
 
     public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
 
-    public function xinfo(string $operation, string $arg1 = null, string $arg2 = null): mixed;
+    public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
 
     public function xlen(string $key): int;
 
-    public function xpending(string $key, string $group, string $start = null, string $end = null, int $count = -1, string $consumer = null): string;
+    public function xpending(string $key, string $group, string $start = null, string $end = null, int $count = -1, string $consumer = null): Redis|array|false;
 
     public function xrange(string $key, string $start, string $end, int $count = -1): bool|array;
 
@@ -506,15 +509,15 @@ public function xrevrange(string $key, string $start, string $end, int $count =
 
     public function xtrim(string $key, int $maxlen, bool $approx = false): int;
 
-    public function zAdd(string $key, int $score, string $value): int;
+    public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|false;
 
-    public function zCard(string $key): int;
+    public function zCard(string $key): Redis|int|false;
 
-    public function zCount(string $key, string $start , string $end): int;
+    public function zCount(string $key, string $start , string $end): Redis|int|false;
 
-    public function zIncrBy(string $key, float $value, mixed $member): float;
+    public function zIncrBy(string $key, float $value, mixed $member): Redis|float|false;
 
-    public function zLexCount(string $key, string $min, string $max): int;
+    public function zLexCount(string $key, string $min, string $max): Redis|int|false;
 
     public function zMscore(string $key, string $member, string ...$other_members): array;
 
@@ -522,49 +525,49 @@ public function zPopMax(string $key, int $value = null): array;
 
     public function zPopMin(string $key, int $value = null): array;
 
-    public function zRange(string $key, int $start, int $end, mixed $scores = null): array;
+    public function zRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false;
 
     public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): array;
 
-    public function zRangeByScore(string $key, string $start, string $end, array $options = []): array;
+    public function zRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false;
 
     public function zRandMember(string $key, array $options = null): string|array;
 
-    public function zRank(string $key, string $member): int;
+    public function zRank(string $key, mixed $member): Redis|int|false;
 
-    public function zRem(string $key, string $member, string ...$other_members): int;
+    public function zRem(mixed $key, mixed $member, mixed ...$other_members): Redis|int|false;
 
     public function zRemRangeByLex(string $key, string $min, string $max): int;
 
-    public function zRemRangeByRank(string $key, int $start, int $end): int;
+    public function zRemRangeByRank(string $key, int $start, int $end): Redis|int|false;
 
-    public function zRemRangeByScore(string $key, string $start, string $end): int;
+    public function zRemRangeByScore(string $key, string $start, string $end): Redis|int|false;
 
-    public function zRevRange(string $key, int $start, int $end, mixed $scores = null): array;
+    public function zRevRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false;
 
     public function zRevRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): array;
 
     public function zRevRangeByScore(string $key, string $start, string $end, array $options = []): array;
 
-    public function zRevRank(string $key, string $member): int;
+    public function zRevRank(string $key, mixed $member): Redis|int|false;
 
-    public function zScore(string $key, mixed $member): float;
+    public function zScore(string $key, mixed $member): Redis|float|false;
 
     public function zdiff(array $keys, array $options = null): array;
 
     public function zdiffstore(string $dst, array $keys, array $options = null): int;
 
-    public function zinter(array $keys, array $weights = null, array $options = null): array;
+    public function zinter(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false;
 
     public function zintercard(array $keys, int $limit = -1): Redis|int|false;
 
-    public function zinterstore(string $dst, array $keys, array $weights = null, string $aggregate = null): int;
+    public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false;
 
     public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
 
-    public function zunion(array $keys, array $weights = null, array $options = null): array;
+    public function zunion(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false;
 
-    public function zunionstore(string $dst, array $keys, array $weights = null, string $aggregate = null): int;
+    public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): Redis|int|false;
 }
 
 class RedisException extends RuntimeException {}
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 86f6373d9f..a8d4aa93bb 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 177e08fec3c3ef380c1cdbab99235090c656cde4 */
+ * Stub hash: d7e7c4d63f53a7eeeb17a5d54ce3ee1173eb18e6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -70,7 +70,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_blPop, 0, 2, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_blPop, 0, 2, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_LONG, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
@@ -78,7 +78,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_brPop arginfo_class_Redis_blPop
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_brpoplpush, 0, 3, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_brpoplpush, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
@@ -135,6 +135,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_decr, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_decrBy, 0, 0, 2)
@@ -169,17 +170,20 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_evalsha, 0, 1, IS_MI
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_exec, 0, 0, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_exec, 0, 0, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_exists arginfo_class_Redis_decr
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_exists, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_expire, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expire, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_expireAt, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expireAt, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
 ZEND_END_ARG_INFO()
@@ -210,7 +214,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geoadd, 0, 4, IS_LON
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geodist, 0, 3, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geodist, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
@@ -223,9 +227,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geohash, 0, 2, IS_AR
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_geopos arginfo_class_Redis_geohash
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geopos, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_georadius, 0, 5, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_georadius, 0, 5, Redis, MAY_BE_ANY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
@@ -236,7 +244,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_georadius_ro arginfo_class_Redis_georadius
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_georadiusbymember, 0, 4, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_georadiusbymember, 0, 4, Redis, MAY_BE_ANY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0)
@@ -263,7 +271,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearchstore, 0, 5
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_get arginfo_class_Redis_decr
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_get, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getAuth, 0, 0, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
@@ -318,33 +328,33 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getTimeout arginfo_class_Redis_dbSize
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hDel, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hDel, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hExists, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hExists, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGet, 0, 2, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hGet, 0, 2, Redis, MAY_BE_ANY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGetAll, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hGetAll, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hIncrBy, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrBy, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hIncrByFloat, 0, 3, IS_DOUBLE, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrByFloat, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
@@ -352,32 +362,34 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_hKeys arginfo_class_Redis_hGetAll
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hLen, 0, 1, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_hLen arginfo_class_Redis_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hMget, 0, 2, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMget, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hMset, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMset, 0, 2, Redis, MAY_BE_BOOL|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keyvals, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hSet, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSet, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_hSetNx arginfo_class_Redis_hSet
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSetNx, 0, 3, Redis, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hStrLen, 0, 2, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -402,7 +414,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrByFloat, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_info, 0, 0, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_info, 0, 0, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
 ZEND_END_ARG_INFO()
 
@@ -419,7 +431,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lInsert, 0, 0, 4)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_lLen arginfo_class_Redis_hLen
+#define arginfo_class_Redis_lLen arginfo_class_Redis_expiretime
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lMove, 0, 4, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
@@ -450,32 +462,32 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_rPushx arginfo_class_Redis_append
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lSet, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lSet, 0, 3, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_lastSave arginfo_class_Redis_dbSize
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lindex, 0, 2, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lindex, 0, 2, Redis, MAY_BE_ANY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lrange, 0, 3, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lrange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lrem, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lrem, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_ltrim, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ltrim, 0, 3, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
@@ -500,19 +512,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_move, 0, 2, _IS_BOOL
 	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_mset, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mset, 0, 1, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_msetnx, 0, 1, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_msetnx arginfo_class_Redis_mset
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_multi, 0, 0, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_object, 0, 2, MAY_BE_LONG|MAY_BE_STRING)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_object, 0, 2, Redis, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -533,16 +543,24 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_persist, 0, 1, _IS_B
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_pexpire arginfo_class_Redis_expire
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpire, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpireAt, 0, 2, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfadd, 0, 2, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, elements, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_pfcount arginfo_class_Redis_hLen
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfcount, 0, 1, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfmerge, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
@@ -569,9 +587,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_psubscribe, 0, 2, _I
 	ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_pttl arginfo_class_Redis_hLen
+#define arginfo_class_Redis_pttl arginfo_class_Redis_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_publish, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_publish, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -611,12 +629,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_role arginfo_class_Redis_getAuth
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rpoplpush, 0, 2, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rpoplpush, 0, 2, Redis, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sAdd, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sAdd, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_MIXED, 0)
@@ -627,37 +645,43 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sAddArray, 0, 2, IS_
 	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sDiff, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sDiff, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sDiffStore, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sDiffStore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_sInter arginfo_class_Redis_sDiff
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sInter, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sintercard, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_sInterStore arginfo_class_Redis_sDiffStore
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sInterStore, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sMembers arginfo_class_Redis_hGetAll
 
 #define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sMove, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sMove, 0, 3, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sPop, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sPop, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
@@ -670,13 +694,14 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_save arginfo_class_Redis_bgSave
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_scan, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_scard arginfo_class_Redis_hLen
+#define arginfo_class_Redis_scard arginfo_class_Redis_expiretime
 
 #define arginfo_class_Redis_script arginfo_class_Redis_rawcommand
 
@@ -711,9 +736,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_setnx arginfo_class_Redis_append
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sismember, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sismember, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, _IS_BOOL, 0)
@@ -746,20 +771,16 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sortDescAlpha arginfo_class_Redis_sortAsc
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_srem, 0, 2, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_srem arginfo_class_Redis_sAdd
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sscan, 0, 2, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sscan, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_strlen arginfo_class_Redis_decr
+#define arginfo_class_Redis_strlen arginfo_class_Redis_get
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
@@ -771,11 +792,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_swapdb, 0, 2, _IS_BO
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_time arginfo_class_Redis_exec
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_time, 0, 0, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_ttl arginfo_class_Redis_hLen
+#define arginfo_class_Redis_ttl arginfo_class_Redis_expiretime
 
-#define arginfo_class_Redis_type arginfo_class_Redis_decr
+#define arginfo_class_Redis_type arginfo_class_Redis_get
 
 #define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
@@ -787,18 +809,18 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_watch arginfo_class_Redis_del
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_wait, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_wait, 0, 2, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xack, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xack, 0, 3, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xadd, 0, 3, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xadd, 0, 3, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
@@ -826,7 +848,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, MAY_BE
 	ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xdel, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xdel, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -841,13 +863,14 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xinfo, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_xlen arginfo_class_Redis_hLen
+#define arginfo_class_Redis_xlen arginfo_class_Redis_pfcount
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xpending, 0, 2, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xpending, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 0, "null")
@@ -885,27 +908,27 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xtrim, 0, 2, IS_LONG
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zAdd, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zAdd, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, score, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_MASK(0, score_or_options, MAY_BE_ARRAY|MAY_BE_DOUBLE, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, more_scores_and_mems, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zCard arginfo_class_Redis_hLen
+#define arginfo_class_Redis_zCard arginfo_class_Redis_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zCount, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zCount, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zIncrBy, 0, 3, IS_DOUBLE, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zIncrBy, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zLexCount, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zLexCount, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
@@ -920,7 +943,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRange, 0, 3, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
@@ -935,22 +958,36 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 3, I
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 3, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByScore, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_zRandMember, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRank arginfo_class_Redis_hStrLen
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRank, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRem arginfo_class_Redis_hDel
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRem, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRemRangeByLex arginfo_class_Redis_zLexCount
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRemRangeByLex, 0, 3, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRemRangeByRank, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRemRangeByRank, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
@@ -962,11 +999,16 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex
 
-#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRangeByScore
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRevRangeByScore, 0, 3, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRevRank arginfo_class_Redis_hStrLen
+#define arginfo_class_Redis_zRevRank arginfo_class_Redis_zRank
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zScore, 0, 2, IS_DOUBLE, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zScore, 0, 2, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
@@ -982,26 +1024,31 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zdiffstore, 0, 2, IS
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zinter, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zinter, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zintercard arginfo_class_Redis_sintercard
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zinterstore, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zinterstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zscan arginfo_class_Redis_hscan
 
 #define arginfo_class_Redis_zunion arginfo_class_Redis_zinter
 
-#define arginfo_class_Redis_zunionstore arginfo_class_Redis_zinterstore
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zunionstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "NULL")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL")
+ZEND_END_ARG_INFO()
 
 
 ZEND_METHOD(Redis, __construct);
diff --git a/redis_commands.c b/redis_commands.c
index 22c19a8d40..d3a0a21659 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1415,6 +1415,7 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     // We at least need a key and one value
     if (argc < 2) {
+        zend_wrong_param_count();
         return FAILURE;
     }
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 0a0f7016c4..ade85d2ee0 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 177e08fec3c3ef380c1cdbab99235090c656cde4 */
+ * Stub hash: d7e7c4d63f53a7eeeb17a5d54ce3ee1173eb18e6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -123,7 +123,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_debug arginfo_class_Redis__prefix
 
-#define arginfo_class_Redis_decr arginfo_class_Redis__prefix
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_decr, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, by)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_decrBy arginfo_class_Redis_append
 
@@ -156,7 +159,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_exec arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_exists arginfo_class_Redis__prefix
+#define arginfo_class_Redis_exists arginfo_class_Redis_del
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expire, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
@@ -345,7 +348,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2)
 	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_incr arginfo_class_Redis__prefix
+#define arginfo_class_Redis_incr arginfo_class_Redis_decr
 
 #define arginfo_class_Redis_incrBy arginfo_class_Redis_append
 
@@ -567,7 +570,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sintercard, 0, 0, 1)
 	ZEND_ARG_INFO(0, limit)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_sInterStore arginfo_class_Redis_sDiffStore
+#define arginfo_class_Redis_sInterStore arginfo_class_Redis_del
 
 #define arginfo_class_Redis_sMembers arginfo_class_Redis__prefix
 
@@ -593,6 +596,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_scan, 0, 0, 1)
 	ZEND_ARG_INFO(1, iterator)
 	ZEND_ARG_INFO(0, pattern)
 	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, type)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_scard arginfo_class_Redis__prefix
@@ -744,6 +748,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xinfo, 0, 0, 1)
 	ZEND_ARG_INFO(0, operation)
 	ZEND_ARG_INFO(0, arg1)
 	ZEND_ARG_INFO(0, arg2)
+	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_xlen arginfo_class_Redis__prefix
@@ -786,10 +791,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xtrim, 0, 0, 2)
 	ZEND_ARG_INFO(0, approx)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zAdd, 0, 0, 3)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zAdd, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, score)
-	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, score_or_options)
+	ZEND_ARG_VARIADIC_INFO(0, more_scores_and_mems)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zCard arginfo_class_Redis__prefix
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index f61e263a7a..00b456730d 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4777,9 +4777,6 @@ private function checkSerializer($mode) {
         $this->assertTrue(0 === $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');

From 6ea978eb72507c3c21805de8bc916b1aa7f0f0dd Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 8 Oct 2022 09:18:04 -0700
Subject: [PATCH 1630/1986] [B]LMPOP and [B]ZMPOP commands

Implement the new Redis 7.0.0 commands to pop multiple elements from one
or more lists/zsets.

Additionally, remove INTERNAL_FUNCTION_PARAMETERS from the
redis_sock_read_multibulk_reply_zval helper function as it wasn't
actually being used.
---
 cluster_library.c              |  17 ++++++
 cluster_library.h              |   3 +
 library.c                      | 106 +++++++++++++++++++++++++++++----
 library.h                      |   9 ++-
 redis.c                        |  27 ++++++++-
 redis.stub.php                 |   8 +++
 redis_arginfo.h                |  27 ++++++++-
 redis_cluster.c                |  23 +++++++
 redis_cluster.stub.php         |   8 +++
 redis_cluster_arginfo.h        |  27 ++++++++-
 redis_cluster_legacy_arginfo.h |  27 ++++++++-
 redis_commands.c               |  88 ++++++++++++++++++++++++++-
 redis_commands.h               |   3 +
 redis_legacy_arginfo.h         |  27 ++++++++-
 tests/RedisTest.php            | 100 ++++++++++++++++++++++++++++++-
 15 files changed, 481 insertions(+), 19 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index c76c32697f..fe574e50d9 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -2362,6 +2362,23 @@ cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
     add_next_index_zval(&c->multi_resp, &z_ret);
 }
 
+/* LMPOP, ZMPOP, BLMPOP, BZMPOP */
+PHP_REDIS_API void
+cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
+{
+    zval z_ret;
+
+    c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null;
+    if (redis_read_mpop_response(c->cmd_sock, &z_ret, c->reply_len, ctx) == FAILURE) {
+        CLUSTER_RETURN_FALSE(c);
+    }
+
+    if (CLUSTER_IS_ATOMIC(c)) {
+        RETURN_ZVAL(&z_ret, 0, 0);
+    }
+    add_next_index_zval(&c->multi_resp, &z_ret);
+}
+
 static void
 cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx,
                         int (*cb)(RedisSock*, zval*, long))
diff --git a/cluster_library.h b/cluster_library.h
index ddb29f2916..1f18c35a0b 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -496,6 +496,9 @@ PHP_REDIS_API void cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS,
 PHP_REDIS_API void cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS,
     redisCluster *c, void *ctx);
 
+PHP_REDIS_API void cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS,
+    redisCluster *c, void *ctx);
+
 /* Custom ACL handlers */
 PHP_REDIS_API void cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx);
 PHP_REDIS_API void cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx);
diff --git a/library.c b/library.c
index 67a49be041..09b31e6c54 100644
--- a/library.c
+++ b/library.c
@@ -68,6 +68,17 @@
 #define SCORE_DECODE_INT  1
 #define SCORE_DECODE_DOUBLE 2
 
+/* PhpRedis often returns either FALSE or NULL depending on whether we have
+ * an option set, so this macro just wraps that often repeated logic */
+#define REDIS_ZVAL_NULL(sock_, zv_) \
+    do { \
+        if ((sock_)->null_mbulk_as_null) { \
+            ZVAL_NULL((zv_)); \
+        } else { \
+            ZVAL_FALSE((zv_)); \
+        } \
+    } while (0)
+
 #ifndef PHP_WIN32
     #include  /* TCP_NODELAY */
     #include   /* SO_KEEPALIVE */
@@ -464,9 +475,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
     // Consume response(s) from subscribe, which will vary on argc
     while(sctx->argc--) {
         ZVAL_NULL(&z_resp);
-        if (!redis_sock_read_multibulk_reply_zval(
-            INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)
-        ) {
+        if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp)) {
             goto error;
         }
 
@@ -513,9 +522,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
         int tab_idx = 1, is_pmsg = 0;
 
         ZVAL_NULL(&z_resp);
-        if (!redis_sock_read_multibulk_reply_zval(
-            INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)
-        ) {
+        if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp)) {
             goto failure;
         }
 
@@ -606,8 +613,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
 
     while (sctx->argc--) {
         ZVAL_NULL(&z_resp);
-        if (!redis_sock_read_multibulk_reply_zval(
-            INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp) ||
+        if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp) ||
             (z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL
         ) {
             efree(sctx);
@@ -642,8 +648,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
 }
 
 PHP_REDIS_API zval *
-redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS,
-                                     RedisSock *redis_sock, zval *z_tab)
+redis_sock_read_multibulk_reply_zval(RedisSock *redis_sock, zval *z_tab)
 {
     int numElems;
 
@@ -1617,6 +1622,87 @@ geosearch_cast(zval *zv)
     return SUCCESS;
 }
 
+PHP_REDIS_API int
+redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements,
+                         void *ctx)
+{
+    int subele, keylen;
+    zval zele = {0};
+    char *key;
+
+    ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR);
+
+    if (elements < 0) {
+        REDIS_ZVAL_NULL(redis_sock, zdst);
+        return SUCCESS;
+    }
+
+    /* Invariant:  We should have two elements */
+    ZEND_ASSERT(elements == 2);
+
+    array_init(zdst);
+
+    /* Key name and number of entries */
+    if ((key = redis_sock_read(redis_sock, &keylen)) == NULL ||
+        read_mbulk_header(redis_sock, &elements) < 0 || elements < 0)
+    {
+        if (key) efree(key);
+        goto fail;
+    }
+
+    add_next_index_stringl(zdst, key, keylen);
+    efree(key);
+
+    array_init_size(&zele, elements);
+
+    if (ctx == PHPREDIS_CTX_PTR) {
+        for (int i = 0; i < elements; i++) {
+            if (read_mbulk_header(redis_sock, &subele) < 0 || subele != 2) {
+                zval_dtor(&zele);
+                goto fail;
+            }
+            redis_mbulk_reply_loop(redis_sock, &zele, subele, UNSERIALIZE_KEYS);
+        }
+
+        array_zip_values_and_scores(redis_sock, &zele, SCORE_DECODE_DOUBLE);
+    } else {
+        redis_mbulk_reply_loop(redis_sock, &zele, elements, UNSERIALIZE_ALL);
+    }
+
+    add_next_index_zval(zdst, &zele);
+
+    return SUCCESS;
+
+fail:
+    zval_dtor(zdst);
+    ZVAL_FALSE(zdst);
+
+    return FAILURE;
+}
+
+PHP_REDIS_API int
+redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                    zval *z_tab, void *ctx)
+{
+    int elements, res = SUCCESS;
+    zval zret = {0};
+
+    if (read_mbulk_header(redis_sock, &elements) == FAILURE ||
+        redis_read_mpop_response(redis_sock, &zret, elements, ctx) == FAILURE)
+    {
+        res = FAILURE;
+        ZVAL_FALSE(&zret);
+    }
+
+    if (IS_ATOMIC(redis_sock)) {
+        RETVAL_ZVAL(&zret, 0, 0);
+    } else {
+        add_next_index_zval(z_tab, &zret);
+    }
+
+    return res;
+}
+
 PHP_REDIS_API int
 redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                          zval *z_tab, void *ctx)
diff --git a/library.h b/library.h
index 9e1cfd7348..ded5d3c88c 100644
--- a/library.h
+++ b/library.h
@@ -78,7 +78,7 @@ PHP_REDIS_API void redis_sock_set_auth(RedisSock *redis_sock, zend_string *user,
 PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv);
 PHP_REDIS_API void redis_sock_free_auth(RedisSock *redis_sock);
 PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force);
-PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
+PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(RedisSock *redis_sock, zval *z_tab);
 PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer,
     size_t buflen, size_t *linelen, int set_err);
 PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes);
@@ -148,6 +148,13 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv);
 PHP_REDIS_API int
 redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements);
 
+PHP_REDIS_API int
+redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                    zval *z_tab, void *ctx);
+
+PHP_REDIS_API int
+redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, void *ctx);
+
 /* Specialized ACL reply handlers */
 PHP_REDIS_API int redis_read_acl_getuser_reply(RedisSock *redis_sock, zval *zret, long len);
 PHP_REDIS_API int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
diff --git a/redis.c b/redis.c
index a9d1f2d60d..4a660474e3 100644
--- a/redis.c
+++ b/redis.c
@@ -368,8 +368,8 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) {
         /* Cluster doesn't support pipelining at this time */
         zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE);
 
-        zend_declare_class_constant_stringl(ce, "LEFT", 4, "left", 4);
-        zend_declare_class_constant_stringl(ce, "RIGHT", 5, "right", 5);
+        zend_declare_class_constant_stringl(ce, ZEND_STRL("LEFT"), ZEND_STRL("left"));
+        zend_declare_class_constant_stringl(ce, ZEND_STRL("RIGHT"), ZEND_STRL("right"));
     }
 
     /* retry/backoff options*/
@@ -2157,6 +2157,29 @@ PHP_METHOD(Redis, bzPopMin) {
 }
 /* }}} */
 
+/* {{{ proto Redis|array|false Redis::lmpop(array $keys, string $from, int $count = 1) */
+PHP_METHOD(Redis, lmpop) {
+    REDIS_PROCESS_KW_CMD("LMPOP", redis_mpop_cmd, redis_mpop_response);
+}
+/* }}} */
+
+/* {{{ proto Redis|array|false Redis::blmpop(double $timeout, array $keys, string $from, int $count = 1) */
+PHP_METHOD(Redis, blmpop) {
+    REDIS_PROCESS_KW_CMD("BLMPOP", redis_mpop_cmd, redis_mpop_response);
+}
+/* }}} */
+
+/* {{{ proto Redis|array|false Redis::zmpop(array $keys, string $from, int $count = 1) */
+PHP_METHOD(Redis, zmpop) {
+    REDIS_PROCESS_KW_CMD("ZMPOP", redis_mpop_cmd, redis_mpop_response);
+}
+
+/* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, string $from, int $count = 1) */
+PHP_METHOD(Redis, bzmpop) {
+    REDIS_PROCESS_KW_CMD("BZMPOP", redis_mpop_cmd, redis_mpop_response);
+}
+
+/* }}} */
 /* hashes */
 
 /* {{{ proto long Redis::hset(string key, string mem, string val) */
diff --git a/redis.stub.php b/redis.stub.php
index c2576b68b1..c0ae6153c6 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -62,6 +62,14 @@ public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ..
 
     public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
 
+    public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
+
+    public function zmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
+
+    public function blmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
+
+    public function lmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
+
     public function clearLastError(): bool;
 
     public function client(string $opt, mixed ...$args): mixed;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index a8d4aa93bb..99c22196ed 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d7e7c4d63f53a7eeeb17a5d54ce3ee1173eb18e6 */
+ * Stub hash: f547b5f24c4d373043c89dab57d450d27f959b08 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -92,6 +92,23 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_bzPopMin arginfo_class_Redis_bzPopMax
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bzmpop, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zmpop, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_blmpop arginfo_class_Redis_bzmpop
+
+#define arginfo_class_Redis_lmpop arginfo_class_Redis_zmpop
+
 #define arginfo_class_Redis_clearLastError arginfo_class_Redis_bgSave
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_client, 0, 1, IS_MIXED, 0)
@@ -1073,6 +1090,10 @@ ZEND_METHOD(Redis, brPop);
 ZEND_METHOD(Redis, brpoplpush);
 ZEND_METHOD(Redis, bzPopMax);
 ZEND_METHOD(Redis, bzPopMin);
+ZEND_METHOD(Redis, bzmpop);
+ZEND_METHOD(Redis, zmpop);
+ZEND_METHOD(Redis, blmpop);
+ZEND_METHOD(Redis, lmpop);
 ZEND_METHOD(Redis, clearLastError);
 ZEND_METHOD(Redis, client);
 ZEND_METHOD(Redis, close);
@@ -1309,6 +1330,10 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, brpoplpush, arginfo_class_Redis_brpoplpush, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bzPopMax, arginfo_class_Redis_bzPopMax, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bzPopMin, arginfo_class_Redis_bzPopMin, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bzmpop, arginfo_class_Redis_bzmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zmpop, arginfo_class_Redis_zmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, blmpop, arginfo_class_Redis_blmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lmpop, arginfo_class_Redis_lmpop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, clearLastError, arginfo_class_Redis_clearLastError, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, client, arginfo_class_Redis_client, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, close, arginfo_class_Redis_close, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index 00fa3801e4..9d466bde19 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1336,7 +1336,30 @@ PHP_METHOD(RedisCluster, lcs) {
     CLUSTER_PROCESS_CMD(lcs, cluster_variant_resp, 1);
 }
 
+/* {{{ proto Redis|array|false Redis::lmpop(array $keys, string $from, int $count = 1) */
+PHP_METHOD(RedisCluster, lmpop) {
+    CLUSTER_PROCESS_KW_CMD("LMPOP", redis_mpop_cmd, cluster_mpop_resp, 0);
+}
+/* }}} */
+
+/* {{{ proto Redis|array|false Redis::blmpop(double $timeout, array $keys, string $from, int $count = 1) */
+PHP_METHOD(RedisCluster, blmpop) {
+    CLUSTER_PROCESS_KW_CMD("BLMPOP", redis_mpop_cmd, cluster_mpop_resp, 0);
+}
 /* }}} */
+
+/* {{{ proto Redis|array|false Redis::zmpop(array $keys, string $from, int $count = 1) */
+PHP_METHOD(RedisCluster, zmpop) {
+    CLUSTER_PROCESS_KW_CMD("ZMPOP", redis_mpop_cmd, cluster_mpop_resp, 0);
+}
+/* }}} */
+
+/* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, sring $from, int $count = 1) */
+PHP_METHOD(RedisCluster, bzmpop) {
+    CLUSTER_PROCESS_KW_CMD("BZMPOP", redis_mpop_cmd, cluster_mpop_resp, 0);
+}
+/* }}} */
+
 /* {{{ proto string RedisCluster::ltrim(string key, long start, long end) */
 PHP_METHOD(RedisCluster, ltrim) {
     CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp, 0);
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index f750e072df..2acff20107 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -52,6 +52,14 @@ public function bzpopmax(string|array $key, string|int $timeout_or_key, mixed ..
 
     public function bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
 
+    public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
+
+    public function zmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
+
+    public function blmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
+
+    public function lmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
+
     public function clearlasterror(): bool;
 
     public function client(string|array $node, string $subcommand, string|null $arg): array|string|bool;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index dbfb302610..4f58d04618 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d6e8120d2edd3cb4a18baa99c6013ac428049448 */
+ * Stub hash: 75e03c96590793af52efbea1d6440d3daa57a5d8 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -96,6 +96,23 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_blpop
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bzmpop, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmpop, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1")
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_blmpop arginfo_class_RedisCluster_bzmpop
+
+#define arginfo_class_RedisCluster_lmpop arginfo_class_RedisCluster_zmpop
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_clearlasterror, 0, 0, _IS_BOOL, 0)
 ZEND_END_ARG_INFO()
 
@@ -880,6 +897,10 @@ ZEND_METHOD(RedisCluster, brpop);
 ZEND_METHOD(RedisCluster, brpoplpush);
 ZEND_METHOD(RedisCluster, bzpopmax);
 ZEND_METHOD(RedisCluster, bzpopmin);
+ZEND_METHOD(RedisCluster, bzmpop);
+ZEND_METHOD(RedisCluster, zmpop);
+ZEND_METHOD(RedisCluster, blmpop);
+ZEND_METHOD(RedisCluster, lmpop);
 ZEND_METHOD(RedisCluster, clearlasterror);
 ZEND_METHOD(RedisCluster, client);
 ZEND_METHOD(RedisCluster, close);
@@ -1079,6 +1100,10 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, brpoplpush, arginfo_class_RedisCluster_brpoplpush, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bzpopmax, arginfo_class_RedisCluster_bzpopmax, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bzpopmin, arginfo_class_RedisCluster_bzpopmin, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, bzmpop, arginfo_class_RedisCluster_bzmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zmpop, arginfo_class_RedisCluster_zmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, blmpop, arginfo_class_RedisCluster_blmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lmpop, arginfo_class_RedisCluster_lmpop, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, clearlasterror, arginfo_class_RedisCluster_clearlasterror, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, client, arginfo_class_RedisCluster_client, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, close, arginfo_class_RedisCluster_close, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index a153816915..18d2e56ceb 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d6e8120d2edd3cb4a18baa99c6013ac428049448 */
+ * Stub hash: 75e03c96590793af52efbea1d6440d3daa57a5d8 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -89,6 +89,23 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_blpop
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bzmpop, 0, 0, 3)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, from)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zmpop, 0, 0, 2)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, from)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_RedisCluster_blmpop arginfo_class_RedisCluster_bzmpop
+
+#define arginfo_class_RedisCluster_lmpop arginfo_class_RedisCluster_zmpop
+
 #define arginfo_class_RedisCluster_clearlasterror arginfo_class_RedisCluster__masters
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_client, 0, 0, 3)
@@ -766,6 +783,10 @@ ZEND_METHOD(RedisCluster, brpop);
 ZEND_METHOD(RedisCluster, brpoplpush);
 ZEND_METHOD(RedisCluster, bzpopmax);
 ZEND_METHOD(RedisCluster, bzpopmin);
+ZEND_METHOD(RedisCluster, bzmpop);
+ZEND_METHOD(RedisCluster, zmpop);
+ZEND_METHOD(RedisCluster, blmpop);
+ZEND_METHOD(RedisCluster, lmpop);
 ZEND_METHOD(RedisCluster, clearlasterror);
 ZEND_METHOD(RedisCluster, client);
 ZEND_METHOD(RedisCluster, close);
@@ -965,6 +986,10 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, brpoplpush, arginfo_class_RedisCluster_brpoplpush, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bzpopmax, arginfo_class_RedisCluster_bzpopmax, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bzpopmin, arginfo_class_RedisCluster_bzpopmin, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, bzmpop, arginfo_class_RedisCluster_bzmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zmpop, arginfo_class_RedisCluster_zmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, blmpop, arginfo_class_RedisCluster_blmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, lmpop, arginfo_class_RedisCluster_lmpop, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, clearlasterror, arginfo_class_RedisCluster_clearlasterror, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, client, arginfo_class_RedisCluster_client, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, close, arginfo_class_RedisCluster_close, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index d3a0a21659..43dbf818ac 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1660,6 +1660,92 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw,
+                   char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    zend_string *from = NULL, *key;
+    int argc, blocking, is_zmpop;
+    smart_string cmdstr = {0};
+    HashTable *keys = NULL;
+    double timeout = 0.0;
+    zend_long count = 1;
+    zval *zv;
+
+    /* Sanity check on our keyword */
+    ZEND_ASSERT(kw != NULL && *kw != '\0' && *(kw+1) != '\0');
+
+    blocking = tolower(*kw) == 'b';
+    is_zmpop = tolower(kw[blocking]) == 'z';
+
+    ZEND_PARSE_PARAMETERS_START(2 + blocking, 3 + blocking) {
+        if (blocking) {
+            Z_PARAM_DOUBLE(timeout)
+        }
+        Z_PARAM_ARRAY_HT(keys)
+        Z_PARAM_STR(from);
+        Z_PARAM_OPTIONAL
+        Z_PARAM_LONG(count);
+    } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    if (zend_hash_num_elements(keys) == 0) {
+        php_error_docref(NULL, E_WARNING, "Must pass at least one key");
+        return FAILURE;
+    } else if (count < 1) {
+        php_error_docref(NULL, E_WARNING, "Count must be > 0");
+        return FAILURE;
+    } else if (!is_zmpop && !(zend_string_equals_literal_ci(from, "LEFT") ||
+                              zend_string_equals_literal_ci(from, "RIGHT")))
+    {
+        php_error_docref(NULL, E_WARNING, "from must be either 'LEFT' or 'RIGHT'");
+        return FAILURE;
+    } else if (is_zmpop && !(zend_string_equals_literal_ci(from, "MIN") ||
+                             zend_string_equals_literal_ci(from, "MAX")))
+    {
+        php_error_docref(NULL, E_WARNING, "from must be either 'MIN' or 'MAX'");
+        return FAILURE;
+    }
+
+    argc = 2 + !!blocking + zend_hash_num_elements(keys) + (count != 1 ? 2 : 0);
+    redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw));
+
+    if (blocking) redis_cmd_append_sstr_dbl(&cmdstr, timeout);
+    redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(keys));
+
+    if (slot) *slot = -1;
+
+    ZEND_HASH_FOREACH_VAL(keys, zv) {
+        key = redis_key_prefix_zval(redis_sock, zv);
+
+        if (slot) {
+            if (*slot == -1) {
+                *slot = cluster_hash_key_zstr(key);
+            } else if (*slot != cluster_hash_key_zstr(key)) {
+                php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot");
+                zend_string_release(key);
+                efree(cmdstr.c);
+                return FAILURE;
+            }
+        }
+
+        redis_cmd_append_sstr_zstr(&cmdstr, key);
+
+        zend_string_release(key);
+    } ZEND_HASH_FOREACH_END();
+
+    redis_cmd_append_sstr_zstr(&cmdstr, from);
+
+    if (count != 1) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
+        redis_cmd_append_sstr_long(&cmdstr, count);
+    }
+
+    *ctx = is_zmpop ? PHPREDIS_CTX_PTR : NULL;
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 /* Generic handling of every blocking pop command (BLPOP, BZPOP[MIN/MAX], etc */
 int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                            char *kw, char **cmd, int *cmd_len, short *slot,
@@ -2230,7 +2316,7 @@ redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) {
+static void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) {
     zend_string *key;
     zval *zv;
 
diff --git a/redis_commands.h b/redis_commands.h
index c51896add7..dbd2e71bf1 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -132,6 +132,9 @@ int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                   char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw,
+                   char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index ade85d2ee0..1609080025 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d7e7c4d63f53a7eeeb17a5d54ce3ee1173eb18e6 */
+ * Stub hash: f547b5f24c4d373043c89dab57d450d27f959b08 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -83,6 +83,23 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_bzPopMin arginfo_class_Redis_blPop
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bzmpop, 0, 0, 3)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, from)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zmpop, 0, 0, 2)
+	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, from)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
+
+#define arginfo_class_Redis_blmpop arginfo_class_Redis_bzmpop
+
+#define arginfo_class_Redis_lmpop arginfo_class_Redis_zmpop
+
 #define arginfo_class_Redis_clearLastError arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_client, 0, 0, 1)
@@ -921,6 +938,10 @@ ZEND_METHOD(Redis, brPop);
 ZEND_METHOD(Redis, brpoplpush);
 ZEND_METHOD(Redis, bzPopMax);
 ZEND_METHOD(Redis, bzPopMin);
+ZEND_METHOD(Redis, bzmpop);
+ZEND_METHOD(Redis, zmpop);
+ZEND_METHOD(Redis, blmpop);
+ZEND_METHOD(Redis, lmpop);
 ZEND_METHOD(Redis, clearLastError);
 ZEND_METHOD(Redis, client);
 ZEND_METHOD(Redis, close);
@@ -1157,6 +1178,10 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, brpoplpush, arginfo_class_Redis_brpoplpush, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bzPopMax, arginfo_class_Redis_bzPopMax, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, bzPopMin, arginfo_class_Redis_bzPopMin, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, bzmpop, arginfo_class_Redis_bzmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zmpop, arginfo_class_Redis_zmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, blmpop, arginfo_class_Redis_blmpop, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, lmpop, arginfo_class_Redis_lmpop, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, clearLastError, arginfo_class_Redis_clearLastError, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, client, arginfo_class_Redis_client, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, close, arginfo_class_Redis_close, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 00b456730d..721ce52599 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -273,6 +273,7 @@ public function testBitsets() {
     }
 
     public function testLcs() {
+
         $key1 = '{lcs}1'; $key2 = '{lcs}2';
         $this->assertTrue($this->redis->set($key1, '12244447777777'));
         $this->assertTrue($this->redis->set($key2, '6666662244441'));
@@ -293,6 +294,104 @@ public function testLcs() {
         $this->redis->del([$key1, $key2]);
     }
 
+    public function testLmpop() {
+        if(version_compare($this->version, "7.0.0") < 0) {
+            $this->markTestSkipped();
+        }
+
+        $key1 = '{l}1';
+        $key2 = '{l}2';
+
+        $this->assertTrue($this->redis->del($key1, $key2) !== false);
+
+        $this->assertEquals(6, $this->redis->rpush($key1, 'A', 'B', 'C', 'D', 'E', 'F'));
+        $this->assertEquals(6, $this->redis->rpush($key2, 'F', 'E', 'D', 'C', 'B', 'A'));
+
+        $this->assertEquals([$key1, ['A']], $this->redis->lmpop([$key1, $key2], 'LEFT'));
+        $this->assertEquals([$key1, ['F']], $this->redis->lmpop([$key1, $key2], 'RIGHT'));
+        $this->assertEquals([$key1, ['B', 'C', 'D']], $this->redis->lmpop([$key1, $key2], 'LEFT',  3));
+
+        $this->assertEquals(2, $this->redis->del($key1, $key2));
+    }
+
+    public function testBLmpop() {
+        if(version_compare($this->version, "7.0.0") < 0) {
+            $this->markTestSkipped();
+        }
+
+        $key1 = '{bl}1';
+        $key2 = '{bl}2';
+
+        $this->assertTrue($this->redis->del($key1, $key2) !== false);
+        $this->assertEquals(2, $this->redis->rpush($key1, 'A', 'B'));
+        $this->assertEquals(2, $this->redis->rpush($key2, 'C', 'D'));
+
+        $this->assertEquals([$key1, ['B', 'A']], $this->redis->blmpop(.2, [$key1, $key2], 'RIGHT', 2));
+        $this->assertEquals([$key2, ['C']], $this->redis->blmpop(.2, [$key1, $key2], 'LEFT'));
+        $this->assertEquals([$key2, ['D']], $this->redis->blmpop(.2, [$key1, $key2], 'LEFT'));
+
+        $st = microtime(true);
+        $this->assertFalse($this->redis->blmpop(.2, [$key1, $key2], 'LEFT'));
+        $et = microtime(true);
+        $this->assertTrue($et - $st >= .2);
+    }
+
+    function testZmpop() {
+        if(version_compare($this->version, "7.0.0") < 0) {
+            $this->markTestSkipped();
+        }
+
+        $key1 = '{z}1';
+        $key2 = '{z}2';
+
+        $this->assertTrue($this->redis->del($key1, $key2) !== false);
+
+        $this->assertEquals(4, $this->redis->zadd($key1, 0, 'zero', 2, 'two', 4, 'four', 6, 'six'));
+        $this->assertEquals(4, $this->redis->zadd($key2, 1, 'one', 3, 'three', 5, 'five', 7, 'seven'));
+
+        $this->assertEquals([$key1, ['zero' => 0.0]], $this->redis->zmpop([$key1, $key2], 'MIN'));
+        $this->assertEquals([$key1, ['six' => 6.0]], $this->redis->zmpop([$key1, $key2], 'MAX'));
+        $this->assertEquals([$key1, ['two' => 2.0, 'four' => 4.0]], $this->redis->zmpop([$key1, $key2], 'MIN', 3));
+
+        $this->assertEquals(
+            [$key2, ['one' => 1.0, 'three' => 3.0, 'five' => 5.0, 'seven' => 7.0]],
+            $this->redis->zmpop([$key1, $key2], 'MIN', 128)
+        );
+
+        $this->assertFalse($this->redis->zmpop([$key1, $key2], 'MIN'));
+
+        $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, true);
+        $this->assertEquals(NULL, $this->redis->zmpop([$key1, $key2], 'MIN'));
+        $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
+    }
+
+    function testBZmpop() {
+        if(version_compare($this->version, "7.0.0") < 0) {
+            $this->markTestSkipped();
+        }
+
+        $key1 = '{z}1';
+        $key2 = '{z}2';
+
+        $this->assertTrue($this->redis->del($key1, $key2) !== false);
+
+        $this->assertEquals(2, $this->redis->zadd($key1, 0, 'zero', 2, 'two'));
+        $this->assertEquals(2, $this->redis->zadd($key2, 1, 'one', 3, 'three'));
+
+        $this->assertEquals(
+            [$key1, ['zero' => 0.0, 'two' => 2.0]],
+            $this->redis->bzmpop(.1, [$key1, $key2], 'MIN', 2)
+        );
+
+        $this->assertEquals([$key2, ['three' => 3.0]], $this->redis->bzmpop(.1, [$key1, $key2], 'MAX'));
+        $this->assertEquals([$key2, ['one' => 1.0]], $this->redis->bzmpop(.1, [$key1, $key2], 'MAX'));
+
+        $st = microtime(true);
+        $this->assertFalse($this->redis->bzmpop(.2, [$key1, $key2], 'MIN'));
+        $et = microtime(true);
+        $this->assertTrue($et - $st >= .2);
+    }
+
     public function testBitPos() {
         if (version_compare($this->version, "2.8.7") < 0) {
             $this->MarkTestSkipped();
@@ -577,7 +676,6 @@ public function testMultipleBin() {
 
         $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() {

From 9a3fe401dc559e2e8fdc769c189801edaa1d3c5b Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 8 Oct 2022 09:24:57 -0700
Subject: [PATCH 1631/1986] Implement new RESTORE options

Add the new RESTORE options REPLACE, ABSTTL, FREQ  and IDLETIME 

Fixes #1410
---
 redis.c                        |   3 +-
 redis.stub.php                 |   2 +-
 redis_arginfo.h                |   3 +-
 redis_cluster.c                |   3 +-
 redis_cluster.stub.php         |   2 +-
 redis_cluster_arginfo.h        |   9 ++-
 redis_cluster_legacy_arginfo.h |   9 ++-
 redis_commands.c               | 101 +++++++++++++++++++++++++++++++++
 redis_commands.h               |   3 +
 redis_legacy_arginfo.h         |   3 +-
 tests/RedisTest.php            |  16 ++++++
 11 files changed, 142 insertions(+), 12 deletions(-)

diff --git a/redis.c b/redis.c
index 4a660474e3..4b4808a876 100644
--- a/redis.c
+++ b/redis.c
@@ -3025,8 +3025,7 @@ PHP_METHOD(Redis, dump) {
 
 /* {{{ proto Redis::restore(ttl, key, value) */
 PHP_METHOD(Redis, restore) {
-    REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_str_cmd,
-        redis_boolean_response);
+    REDIS_PROCESS_CMD(restore, redis_boolean_response);
 }
 /* }}} */
 
diff --git a/redis.stub.php b/redis.stub.php
index c0ae6153c6..e415a5b868 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -364,7 +364,7 @@ public function renameNx(string $key_src, string $key_dst);
 
     public function reset(): bool;
 
-    public function restore(string $key, int $timeout, string $value): bool;
+    public function restore(string $key, int $timeout, string $value, ?array $options = NULL): bool;
 
     public function role(): mixed;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 99c22196ed..8b54c21b68 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: f547b5f24c4d373043c89dab57d450d27f959b08 */
+ * Stub hash: 2c4ee6dc4a5aa66b1700df8859233c349aa00519 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -642,6 +642,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_restore, 0, 3, _IS_B
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_role arginfo_class_Redis_getAuth
diff --git a/redis_cluster.c b/redis_cluster.c
index 9d466bde19..5dead3fa7c 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1418,8 +1418,7 @@ PHP_METHOD(RedisCluster, pfmerge) {
 
 /* {{{ proto boolean RedisCluster::restore(string key, long ttl, string val) */
 PHP_METHOD(RedisCluster, restore) {
-    CLUSTER_PROCESS_KW_CMD("RESTORE", redis_key_long_str_cmd,
-        cluster_bool_resp, 0);
+    CLUSTER_PROCESS_CMD(restore, cluster_bool_resp, 0);
 }
 /* }}} */
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 2acff20107..f3475f0166 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -248,7 +248,7 @@ public function rename(string $key, string $newkey): bool;
 
     public function renamenx(string $key, string $newkey): bool;
 
-    public function restore(string $key, int $timeout, string $value): bool;
+    public function restore(string $key, int $timeout, string $value, ?array $options = NULL): bool;
 
     public function role(string|array $key_or_address): mixed;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 4f58d04618..f5f6cc8696 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 75e03c96590793af52efbea1d6440d3daa57a5d8 */
+ * Stub hash: 956f295e74025def86150d0acdf7a11594c72d47 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -526,7 +526,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_renamenx arginfo_class_RedisCluster_rename
 
-#define arginfo_class_RedisCluster_restore arginfo_class_RedisCluster_psetex
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_restore, 0, 3, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_role, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 18d2e56ceb..a491d49448 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 75e03c96590793af52efbea1d6440d3daa57a5d8 */
+ * Stub hash: 956f295e74025def86150d0acdf7a11594c72d47 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -459,7 +459,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_renamenx arginfo_class_RedisCluster_rename
 
-#define arginfo_class_RedisCluster_restore arginfo_class_RedisCluster_psetex
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_restore, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_role arginfo_class_RedisCluster_bgrewriteaof
 
diff --git a/redis_commands.c b/redis_commands.c
index 43dbf818ac..3d1b674d91 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -65,6 +65,13 @@ typedef struct redisLcsOptions {
     zend_bool withmatchlen;
 } redisLcsOptions;
 
+typedef struct redisRestoreOptions {
+    zend_bool replace;
+    zend_bool absttl;
+    zend_long idletime;
+    zend_long freq;
+} redisRestoreOptions;
+
 /* Local passthrough macro for command construction.  Given that these methods
  * are generic (so they work whether the caller is Redis or RedisCluster) we
  * will always have redis_sock, slot*, and */
@@ -2411,6 +2418,100 @@ int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+void redis_get_restore_options(redisRestoreOptions *dst, HashTable *ht) {
+    zend_string *key;
+    zend_long lval;
+    zval *zv;
+
+    ZEND_ASSERT(dst != NULL);
+
+    memset(dst, 0, sizeof(*dst));
+    dst->idletime = dst->freq = -1;
+
+    if (ht == NULL)
+        return;
+
+    ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) {
+        ZVAL_DEREF(zv);
+
+        if (key) {
+            if (zend_string_equals_literal_ci(key, "IDLETIME")) {
+                lval = zval_get_long(zv);
+                if (lval < 0) {
+                    php_error_docref(NULL, E_WARNING, "IDLETIME must be >= 0");
+                } else {
+                    dst->idletime = lval;
+                    dst->freq = -1;
+                }
+            } else if (zend_string_equals_literal_ci(key, "FREQ")) {
+                lval = zval_get_long(zv);
+                if (lval < 0 || lval > 255) {
+                    php_error_docref(NULL, E_WARNING, "FREQ must be >= 0 and <= 255");
+                } else {
+                    dst->freq = lval;
+                    dst->idletime = -1;
+                }
+            } else {
+                php_error_docref(NULL, E_WARNING, "Unknown RESTORE option '%s'", ZSTR_VAL(key));
+            }
+        } else if (Z_TYPE_P(zv) == IS_STRING) {
+            if (zend_string_equals_literal_ci(Z_STR_P(zv), "REPLACE")) {
+                dst->replace = 1;
+            } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "ABSTTL")) {
+                dst->absttl = 1;
+            } else {
+                php_error_docref(NULL, E_WARNING, "Unknown RESTORE option '%s'", Z_STRVAL_P(zv));
+            }
+        }
+    } ZEND_HASH_FOREACH_END();
+}
+
+/* RESTORE */
+int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                      char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    zend_string *key, *value = NULL;
+    smart_string cmdstr = {0};
+    HashTable *options = NULL;
+    redisRestoreOptions opt;
+    zend_long timeout = 0;
+    int argc;
+
+    ZEND_PARSE_PARAMETERS_START(3, 4) {
+        Z_PARAM_STR(key)
+        Z_PARAM_LONG(timeout)
+        Z_PARAM_STR(value)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_ARRAY_HT_OR_NULL(options)
+    } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    redis_get_restore_options(&opt, options);
+
+    argc = 3 + (opt.idletime>-1?2:0) + (opt.freq>-1?2:0) + !!opt.absttl + !!opt.replace;
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "RESTORE");
+
+    redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot);
+    redis_cmd_append_sstr_long(&cmdstr, timeout);
+    redis_cmd_append_sstr_zstr(&cmdstr, value);
+
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.replace, "REPLACE");
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.absttl, "ABSTTL");
+
+    if (opt.idletime > -1) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IDLETIME");
+        redis_cmd_append_sstr_long(&cmdstr, opt.idletime);
+    }
+
+    if (opt.freq > -1) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FREQ");
+        redis_cmd_append_sstr_long(&cmdstr, opt.freq);
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
 
 /* BITPOS */
 int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
diff --git a/redis_commands.h b/redis_commands.h
index dbd2e71bf1..a93be4a17b 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -135,6 +135,9 @@ int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw,
                    char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                      char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 1609080025..a49724918c 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: f547b5f24c4d373043c89dab57d450d27f959b08 */
+ * Stub hash: 2c4ee6dc4a5aa66b1700df8859233c349aa00519 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -552,6 +552,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_restore, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, timeout)
 	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_role arginfo_class_Redis___destruct
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 721ce52599..57faf1af8a 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5096,6 +5096,22 @@ public function testDumpRestore() {
         $this->assertTrue($this->redis->get('foo') === 'this-is-bar');
         $this->assertTrue($this->redis->get('bar') === 'this-is-foo');
 
+        /* Test that we can REPLACE a key */
+        $this->assertTrue($this->redis->set('foo', 'some-value'));
+        $this->assertTrue($this->redis->restore('foo', 0, $d_bar, ['REPLACE']));
+
+        /* Ensure we can set an absolute TTL */
+        $this->assertTrue($this->redis->restore('foo', time() + 10, $d_bar, ['REPLACE', 'ABSTTL']));
+        $this->assertTrue($this->redis->ttl('foo') <= 10);
+
+        /* Ensure we can set an IDLETIME */
+        $this->assertTrue($this->redis->restore('foo', 0, $d_bar, ['REPLACE', 'IDLETIME' => 200]));
+        $this->assertTrue($this->redis->object('idletime', 'foo') > 100);
+
+        /* We can't neccissarily check this depending on LRU policy, but at least attempt to use
+           the FREQ option */
+        $this->assertTrue($this->redis->restore('foo', 0, $d_bar, ['REPLACE', 'FREQ' => 200]));
+
         $this->redis->del('foo');
         $this->redis->del('bar');
     }

From d2044c9fa49eefecebad48aa169ab7bead1af121 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 5 Oct 2022 18:47:39 -0700
Subject: [PATCH 1632/1986] Move where we generate our salt

Fixes #1987
---
 redis.c | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/redis.c b/redis.c
index 4b4808a876..3477e3a5be 100644
--- a/redis.c
+++ b/redis.c
@@ -129,7 +129,6 @@ static const zend_module_dep redis_deps[] = {
 };
 
 ZEND_DECLARE_MODULE_GLOBALS(redis)
-static PHP_GINIT_FUNCTION(redis);
 
 zend_module_entry redis_module_entry = {
      STANDARD_MODULE_HEADER_EX,
@@ -144,7 +143,7 @@ zend_module_entry redis_module_entry = {
      PHP_MINFO(redis),
      PHP_REDIS_VERSION,
      PHP_MODULE_GLOBALS(redis),
-     PHP_GINIT(redis),
+     NULL,
      NULL,
      NULL,
      STANDARD_MODULE_PROPERTIES_EX
@@ -425,12 +424,6 @@ static void redis_random_hex_bytes(char *dst, size_t dstsize) {
     zend_string_release(s);
 }
 
-static PHP_GINIT_FUNCTION(redis)
-{
-    redis_random_hex_bytes(redis_globals->salt, sizeof(redis_globals->salt) - 1);
-    redis_globals->salt[sizeof(redis_globals->salt)-1] = '\0';
-}
-
 /**
  * PHP_MINIT_FUNCTION
  */
@@ -442,6 +435,10 @@ PHP_MINIT_FUNCTION(redis)
     gettimeofday(&tv, NULL);
     srand(tv.tv_usec * tv.tv_sec);
 
+    /* Generate our random salt */
+    redis_random_hex_bytes(REDIS_G(salt), sizeof(REDIS_G(salt)) - 1);
+    REDIS_G(salt)[sizeof(REDIS_G(salt)) - 1] = '\0';
+
     REGISTER_INI_ENTRIES();
 
     /* Redis class */

From 6b34d17fc480c99f7c7c9c8fae1a55c13b7e94f3 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 8 Oct 2022 11:06:23 -0700
Subject: [PATCH 1633/1986] Add new Redis 6.2.0 XTRIM options

Fixes #1961
---
 library.c                      |  4 +++
 library.h                      |  1 +
 redis.stub.php                 |  2 +-
 redis_arginfo.h                |  6 ++--
 redis_cluster.stub.php         |  2 +-
 redis_cluster_arginfo.h        |  6 ++--
 redis_cluster_legacy_arginfo.h |  4 ++-
 redis_commands.c               | 52 +++++++++++++++++++++++++---------
 redis_legacy_arginfo.h         |  4 ++-
 tests/RedisTest.php            | 21 ++++++++++++++
 10 files changed, 81 insertions(+), 21 deletions(-)

diff --git a/library.c b/library.c
index 09b31e6c54..91f8fca9cb 100644
--- a/library.c
+++ b/library.c
@@ -1062,6 +1062,10 @@ int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSoc
     return retval;
 }
 
+int redis_cmd_append_sstr_key_zstr(smart_string *dst, zend_string *key, RedisSock *redis_sock, short *slot) {
+    return redis_cmd_append_sstr_key(dst, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot);
+}
+
 /* Append an array key to a redis smart string command.  This function
  * handles the boilerplate conditionals around string or integer keys */
 int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx)
diff --git a/library.h b/library.h
index ded5d3c88c..b67d72f876 100644
--- a/library.h
+++ b/library.h
@@ -50,6 +50,7 @@ int redis_cmd_append_sstr_dbl(smart_string *str, double value);
 int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr);
 int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock);
 int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot);
+int redis_cmd_append_sstr_key_zstr(smart_string *str, zend_string *key, RedisSock *redis_sock, short *slot);
 int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx);
 
 PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...);
diff --git a/redis.stub.php b/redis.stub.php
index e415a5b868..89e6f0c980 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -515,7 +515,7 @@ public function xreadgroup(string $group, string $consumer, array $streams, int
 
     public function xrevrange(string $key, string $start, string $end, int $count = -1): bool|array;
 
-    public function xtrim(string $key, int $maxlen, bool $approx = false): int;
+    public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): Redis|int|false;
 
     public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|false;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 8b54c21b68..acd15086f7 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2c4ee6dc4a5aa66b1700df8859233c349aa00519 */
+ * Stub hash: 0ace014dc4f3f94eedd835f1d6895703aea3e607 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -920,10 +920,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_xrevrange arginfo_class_Redis_xrange
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xtrim, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xtrim, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, maxlen, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, minid, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zAdd, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index f3475f0166..d4d9518a31 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -358,7 +358,7 @@ public function xreadgroup(string $group, string $consumer, array $streams, int
 
     public function xrevrange(string $key, string $start, string $end, int $count = -1): bool|array;
 
-    public function xtrim(string $key, int $maxlen, bool $approx = false): int;
+    public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): RedisCluster|int|false;
 
     public function zadd(string $key, float $score, string $member, mixed ...$extra_args): int;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index f5f6cc8696..f33574081a 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 956f295e74025def86150d0acdf7a11594c72d47 */
+ * Stub hash: 39c0741e5bf358e116f5ed2caa35c1ca226fd593 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -780,10 +780,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_xrevrange arginfo_class_RedisCluster_xrange
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xtrim, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xtrim, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, maxlen, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, minid, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zadd, 0, 3, IS_LONG, 0)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index a491d49448..fb31eefe12 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 956f295e74025def86150d0acdf7a11594c72d47 */
+ * Stub hash: 39c0741e5bf358e116f5ed2caa35c1ca226fd593 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -684,6 +684,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xtrim, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, maxlen)
 	ZEND_ARG_INFO(0, approx)
+	ZEND_ARG_INFO(0, minid)
+	ZEND_ARG_INFO(0, limit)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zadd, 0, 0, 3)
diff --git a/redis_commands.c b/redis_commands.c
index 3d1b674d91..867ad15ed9 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5650,29 +5650,55 @@ int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-/* XTRIM MAXLEN [~] count */
+// XTRIM key  [= | ~] threshold [LIMIT count]
 int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    char *key;
-    size_t keylen;
-    zend_long maxlen;
-    zend_bool approx = 0;
+    zend_long threshold = 0, limit = -1;
+    zend_bool approx = 0, minid = 0;
+    smart_string cmdstr = {0};
+    zend_string *key = NULL;
+    int argc;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|b", &key, &keylen,
-                              &maxlen, &approx) == FAILURE)
-    {
-        return FAILURE;
+    ZEND_PARSE_PARAMETERS_START(2, 5)
+        Z_PARAM_STR(key)
+        Z_PARAM_LONG(threshold)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_BOOL(approx)
+        Z_PARAM_BOOL(minid)
+        Z_PARAM_LONG(limit)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    argc = 4 + (approx && limit > -1 ? 2 : 0);
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XTRIM");
+
+    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
+
+    if (minid) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MINID");
+    } else {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN");
     }
 
     if (approx) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XTRIM", "kssl", key, keylen,
-                                      "MAXLEN", 6, "~", 1, maxlen);
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "~");
     } else {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XTRIM", "ksl", key, keylen,
-                                      "MAXLEN", 6, maxlen);
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "=");
     }
 
+    redis_cmd_append_sstr_long(&cmdstr, threshold);
+
+    if (limit > -1 && approx) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT");
+        redis_cmd_append_sstr_long(&cmdstr, limit);
+    } else if (limit > -1) {
+        php_error_docref(NULL, E_WARNING, "Cannot use LIMIT without an approximate match, ignoring");
+    } else if (ZEND_NUM_ARGS() == 5) {
+        php_error_docref(NULL, E_WARNING, "Limit must be >= 0");
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
     return SUCCESS;
 }
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index a49724918c..b68b53676f 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2c4ee6dc4a5aa66b1700df8859233c349aa00519 */
+ * Stub hash: 0ace014dc4f3f94eedd835f1d6895703aea3e607 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -807,6 +807,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xtrim, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, maxlen)
 	ZEND_ARG_INFO(0, approx)
+	ZEND_ARG_INFO(0, minid)
+	ZEND_ARG_INFO(0, limit)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zAdd, 0, 0, 2)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 57faf1af8a..d44b036409 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6625,6 +6625,27 @@ public function testXTrim() {
            can call it with the flag */
         $this->addStreamEntries('stream', 100);
         $this->assertFalse($this->redis->xTrim('stream', 1, true) === false);
+
+        /* We need Redis >= 6.2.0 for MINID and LIMIT options */
+        if (!$this->minVersionCheck("6.2.0"))
+            return;
+
+        $this->assertEquals(1, $this->redis->del('stream'));
+
+        /* Test minid by generating a stream with more than one */
+        for ($i = 1; $i < 3; $i++) {
+            for ($j = 0; $j < 3; $j++) {
+                $this->redis->xadd('stream', "$i-$j", ['foo' => 'bar']);
+            }
+        }
+
+        /* MINID of 2-0 */
+        $this->assertEquals(3, $this->redis->xtrim('stream', 2, false, true));
+        $this->assertEquals(['2-0', '2-1', '2-2'], array_keys($this->redis->xrange('stream', '0', '+')));
+
+        /* TODO:  Figure oiut how to test LIMIT deterministically.  For now just
+                  send a LIMIT and verify we don't get a failure from Redis. */
+        $this->assertTrue(is_int($this->redis->xtrim('stream', 2, true, false, 3)));
     }
 
     /* XCLAIM is one of the most complicated commands, with a great deal of different options

From 114d79d15bd21264127e1a8c73e3d7e541fbaac7 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 8 Oct 2022 11:36:23 -0700
Subject: [PATCH 1634/1986] Implement AUTH/AUTH2 arguments for MIGRATE

---
 common.h               |  2 +
 redis.stub.php         |  4 +-
 redis_arginfo.h        | 11 +++--
 redis_commands.c       | 99 ++++++++++++++++++++++++------------------
 redis_legacy_arginfo.h |  5 ++-
 5 files changed, 71 insertions(+), 50 deletions(-)

diff --git a/common.h b/common.h
index c8d6c9b19f..f1b85b232b 100644
--- a/common.h
+++ b/common.h
@@ -151,6 +151,8 @@ typedef enum {
         Z_PARAM_ARRAY_HT_EX(dest, 1, 0)
 #define Z_PARAM_STR_OR_NULL(dest) \
         Z_PARAM_STR_EX(dest, 1, 0)
+#define Z_PARAM_ZVAL_OR_NULL(dest) \
+	Z_PARAM_ZVAL_EX(dest, 1, 0)
 #endif
 
 #if PHPREDIS_DEBUG_LOGGING == 1
diff --git a/redis.stub.php b/redis.stub.php
index 89e6f0c980..55605e9646 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -293,7 +293,9 @@ public function ltrim(string $key, int $start , int $end): Redis|bool;
 	/** @return array|Redis */
     public function mget(array $keys);
 
-    public function migrate(string $host, int $port, string $key, string $dst, int $timeout, bool $copy = false, bool $replace = false): bool;
+    public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout,
+                            bool $copy = false, bool $replace = false,
+                            #[\SensitiveParameter] ?mixed $credentials = NULL): Redis|bool;
 
     public function move(string $key, int $index): bool;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index acd15086f7..37265f100a 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0ace014dc4f3f94eedd835f1d6895703aea3e607 */
+ * Stub hash: a39dd09e86258566f2eae441d920ef280f8a3e72 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -514,14 +514,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_migrate, 0, 5, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_migrate, 0, 5, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_INFO(0, dstdb, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, copy, _IS_BOOL, 0, "false")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, replace, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_move, 0, 2, _IS_BOOL, 0)
@@ -1569,6 +1570,8 @@ static zend_class_entry *register_class_Redis(void)
 
 
 	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "auth", sizeof("auth") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
+
+	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "migrate", sizeof("migrate") - 1), 7, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
 #endif
 
 	return class_entry;
diff --git a/redis_commands.c b/redis_commands.c
index 867ad15ed9..a3dc4c9ea0 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4257,61 +4257,70 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+/*  MIGRATE host port  destination-db timeout [COPY] [REPLACE]
+            [[AUTH password] | [AUTH2 username password]] [KEYS key [key ...]]
+
+    Starting with Redis version 3.0.0: Added the COPY and REPLACE options.
+    Starting with Redis version 3.0.6: Added the KEYS option.
+    Starting with Redis version 4.0.7: Added the AUTH option.
+    Starting with Redis version 6.0.0: Added the AUTH2 option.
+*/
+
 /* MIGRATE */
 int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    smart_string cmdstr = {0};
-    char *host, *key;
-    int argc, keyfree;
-    zval *z_keys, *z_key;
-    size_t hostlen, keylen;
-    zend_long destdb, port, timeout;
+    zend_string *host = NULL, *key = NULL, *user = NULL, *pass = NULL;
+    zend_long destdb = 0, port = 0, timeout = 0;
+    zval *zkeys = NULL, *zkey, *zauth = NULL;
     zend_bool copy = 0, replace = 0;
-    zend_string *zstr;
+    smart_string cmdstr = {0};
+    int argc;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "slzll|bb", &host, &hostlen, &port,
-                              &z_keys, &destdb, &timeout, ©, &replace) == FAILURE)
-    {
-        return FAILURE;
+    ZEND_PARSE_PARAMETERS_START(5, 8)
+        Z_PARAM_STR(host)
+        Z_PARAM_LONG(port)
+        Z_PARAM_ZVAL(zkeys)
+        Z_PARAM_LONG(destdb)
+        Z_PARAM_LONG(timeout)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_BOOL(copy)
+        Z_PARAM_BOOL(replace)
+        Z_PARAM_ZVAL_OR_NULL(zauth)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    /* Sanity check on our optional AUTH argument */
+    if (zauth && redis_extract_auth_info(zauth, &user, &pass) == FAILURE) {
+        php_error_docref(NULL, E_WARNING, "AUTH must be a string or an array with one or two strings");
+        user = pass = NULL;
     }
 
     /* Protect against being passed an array with zero elements */
-    if (Z_TYPE_P(z_keys) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(z_keys)) == 0) {
+    if (Z_TYPE_P(zkeys) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(zkeys)) == 0) {
         php_error_docref(NULL, E_WARNING, "Keys array cannot be empty");
         return FAILURE;
     }
 
     /* host, port, key|"", dest-db, timeout, [copy, replace] [KEYS key1..keyN] */
-    argc = 5 + copy + replace;
-    if (Z_TYPE_P(z_keys) == IS_ARRAY) {
+    argc = 5 + copy + replace + (user||pass ? 1 : 0) + (user != NULL) + (pass != NULL);
+    if (Z_TYPE_P(zkeys) == IS_ARRAY) {
         /* +1 for the "KEYS" argument itself */
-        argc += 1 + zend_hash_num_elements(Z_ARRVAL_P(z_keys));
+        argc += 1 + zend_hash_num_elements(Z_ARRVAL_P(zkeys));
     }
 
     /* Initialize MIGRATE command with host and port */
     REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "MIGRATE");
-    redis_cmd_append_sstr(&cmdstr, host, hostlen);
+    redis_cmd_append_sstr_zstr(&cmdstr, host);
     redis_cmd_append_sstr_long(&cmdstr, port);
 
     /* If passed a keys array the keys come later, otherwise pass the key to
      * migrate here */
-    if (Z_TYPE_P(z_keys) == IS_ARRAY) {
+    if (Z_TYPE_P(zkeys) == IS_ARRAY) {
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "");
     } else {
-        /* Grab passed value as a string */
-        zstr = zval_get_string(z_keys);
-
-        /* We may need to prefix our string */
-        key = ZSTR_VAL(zstr);
-        keylen = ZSTR_LEN(zstr);
-        keyfree = redis_key_prefix(redis_sock, &key, &keylen);
-
-        /* Add key to migrate */
-        redis_cmd_append_sstr(&cmdstr, key, keylen);
-
-        zend_string_release(zstr);
-        if (keyfree) efree(key);
+        key = redis_key_prefix_zval(redis_sock, zkeys);
+        redis_cmd_append_sstr_zstr(&cmdstr, key);
+        zend_string_release(key);
     }
 
     redis_cmd_append_sstr_long(&cmdstr, destdb);
@@ -4319,25 +4328,29 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, copy, "COPY");
     REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, replace, "REPLACE");
 
+    if (user && pass) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AUTH2");
+        redis_cmd_append_sstr_zstr(&cmdstr, user);
+        redis_cmd_append_sstr_zstr(&cmdstr, pass);
+    } else if (pass) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AUTH");
+        redis_cmd_append_sstr_zstr(&cmdstr, pass);
+    }
+
     /* Append actual keys if we've got a keys array */
-    if (Z_TYPE_P(z_keys) == IS_ARRAY) {
+    if (Z_TYPE_P(zkeys) == IS_ARRAY) {
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "KEYS");
 
-        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_key) {
-            zstr = zval_get_string(z_key);
-
-            key = ZSTR_VAL(zstr);
-            keylen = ZSTR_LEN(zstr);
-            keyfree = redis_key_prefix(redis_sock, &key, &keylen);
-
-            /* Append the key */
-            redis_cmd_append_sstr(&cmdstr, key, keylen);
-
-            zend_string_release(zstr);
-            if (keyfree) efree(key);
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zkeys), zkey) {
+            key = redis_key_prefix_zval(redis_sock, zkey);
+            redis_cmd_append_sstr_zstr(&cmdstr, key);
+            zend_string_release(key);
         } ZEND_HASH_FOREACH_END();
     }
 
+    if (user) zend_string_release(user);
+    if (pass) zend_string_release(pass);
+
     *cmd = cmdstr.c;
     *cmd_len = cmdstr.len;
     return SUCCESS;
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index b68b53676f..b3b6115192 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0ace014dc4f3f94eedd835f1d6895703aea3e607 */
+ * Stub hash: a39dd09e86258566f2eae441d920ef280f8a3e72 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -450,10 +450,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_migrate, 0, 0, 5)
 	ZEND_ARG_INFO(0, host)
 	ZEND_ARG_INFO(0, port)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, dstdb)
 	ZEND_ARG_INFO(0, timeout)
 	ZEND_ARG_INFO(0, copy)
 	ZEND_ARG_INFO(0, replace)
+	ZEND_ARG_INFO(0, credentials)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_move arginfo_class_Redis_lindex

From 46e64277afccff11b7c616eb86170eaa02d8cb14 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 8 Oct 2022 14:10:11 -0700
Subject: [PATCH 1635/1986] Suppress a valgrind false positive

---
 .github/workflows/ci.yml | 8 ++++----
 tests/vg.supp            | 5 +++++
 2 files changed, 9 insertions(+), 4 deletions(-)
 create mode 100644 tests/vg.supp

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 51cb7651d5..895f62953f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -71,10 +71,10 @@ jobs:
       - name: Run tests using valgrind
         continue-on-error: true
         run: |
-          valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis --user phpredis --auth phpredis
-          valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis
-          valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis
-          valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel --auth phpredis
+          valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class Redis --user phpredis --auth phpredis
+          valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis
+          valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis
+          valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel --auth phpredis
         env:
           TEST_PHP_ARGS: -e
           USE_ZEND_ALLOC: 0
diff --git a/tests/vg.supp b/tests/vg.supp
new file mode 100644
index 0000000000..21d68c02c4
--- /dev/null
+++ b/tests/vg.supp
@@ -0,0 +1,5 @@
+{
+   String_Equality_Intentionally_Reads_Uninit_Memory
+   Memcheck:Cond
+   fun:zend_string_equal_val
+}

From 525958ea9fd2f49b3f6683846f0641128d94a00e Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 9 Oct 2022 15:44:29 -0700
Subject: [PATCH 1636/1986] Implement CONFIG REWRITE

Closes #847
---
 redis.c             | 8 ++++----
 tests/RedisTest.php | 9 +++++++++
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/redis.c b/redis.c
index 3477e3a5be..f68227fdfe 100644
--- a/redis.c
+++ b/redis.c
@@ -2729,8 +2729,10 @@ PHP_METHOD(Redis, config)
     if (zend_string_equals_literal_ci(op, "GET") && key != NULL) {
         cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SS", op, key);
         cb = redis_mbulk_reply_zipped_raw;
-    } else if (zend_string_equals_literal_ci(op, "RESETSTAT")) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "s", ZEND_STRL("RESETSTAT"));
+    } else if (zend_string_equals_literal_ci(op, "RESETSTAT") ||
+               zend_string_equals_literal_ci(op, "REWRITE"))
+    {
+        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "s", ZSTR_VAL(op), ZSTR_LEN(op));
         cb = redis_boolean_response;
     } else if (zend_string_equals_literal_ci(op, "SET") && key != NULL && val != NULL) {
         cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SSS", op, key, val);
@@ -2744,8 +2746,6 @@ PHP_METHOD(Redis, config)
         cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
     }
     REDIS_PROCESS_RESPONSE(redis_boolean_response);
-
-    return;
 }
 /* }}} */
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index d44b036409..4023c37b12 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5561,6 +5561,15 @@ public function testConfig() {
             $this->assertFalse($this->redis->config($cmd));
         }
         $this->assertFalse($this->redis->config('set', 'foo'));
+
+        /* REWRITE.  We don't care if it actually works, just that the
+           command be attempted */
+        $res = $this->redis->config('rewrite');
+        $this->assertTrue(is_bool($res));
+        if ($res == false) {
+            $this->assertPatternMatch($this->redis->getLastError(), '/.*config.*/');
+            $this->redis->clearLastError();
+        }
     }
 
     public function testReconnectSelect() {

From 3b0d8b77810a39a524b7c287d7ad646e93c20e60 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 10 Oct 2022 13:20:14 -0700
Subject: [PATCH 1637/1986] Refactor XINFO handler

Fixes #2119
---
 redis_cluster.stub.php         |  2 +-
 redis_cluster_arginfo.h        |  7 +++--
 redis_cluster_legacy_arginfo.h |  3 +-
 redis_commands.c               | 50 ++++++++++++++++------------------
 tests/RedisTest.php            | 19 +++++++++++--
 5 files changed, 46 insertions(+), 35 deletions(-)

diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index d4d9518a31..b0792bd0ff 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -344,7 +344,7 @@ public function xdel(string $key, array $ids): int;
 
     public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
 
-    public function xinfo(string $operation, string $arg1 = null, string $arg2 = null): mixed;
+    public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
 
     public function xlen(string $key): int;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index f33574081a..3c5a1e9f1d 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 39c0741e5bf358e116f5ed2caa35c1ca226fd593 */
+ * Stub hash: 396a72b8937928cf3ed504a2a8063f5090e7196b */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -742,8 +742,9 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster_decr
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index fb31eefe12..dd9e31966e 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 39c0741e5bf358e116f5ed2caa35c1ca226fd593 */
+ * Stub hash: 396a72b8937928cf3ed504a2a8063f5090e7196b */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -644,6 +644,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 0, 1)
 	ZEND_ARG_INFO(0, operation)
 	ZEND_ARG_INFO(0, arg1)
 	ZEND_ARG_INFO(0, arg2)
+	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster__prefix
diff --git a/redis_commands.c b/redis_commands.c
index a3dc4c9ea0..9a2395dca8 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5624,42 +5624,38 @@ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                      char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    char *op, *key, *arg = NULL;
-    size_t oplen, keylen, arglen;
+    zend_string *op = NULL, *key = NULL, *arg = NULL;
+    smart_string cmdstr = {0};
     zend_long count = -1;
-    int argc = ZEND_NUM_ARGS();
-    char fmt[] = "skssl";
-
-    if (argc > 4 || zend_parse_parameters(ZEND_NUM_ARGS(), "s|ssl",
-                                          &op, &oplen, &key, &keylen, &arg,
-                                          &arglen, &count) == FAILURE)
-    {
-        return FAILURE;
-    }
 
-    /* Handle everything except XINFO STREAM */
-    if (strncasecmp(op, "STREAM", 6) != 0) {
-        fmt[argc] = '\0';
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XINFO", fmt, op, oplen, key, keylen,
-                                      arg, arglen);
-        return SUCCESS;
-    }
+    ZEND_PARSE_PARAMETERS_START(1, 4)
+        Z_PARAM_STR(op)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_STR_OR_NULL(key)
+        Z_PARAM_STR_OR_NULL(arg)
+        Z_PARAM_LONG(count)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
 
-    /* 'FULL' is the only legal option to XINFO STREAM */
-    if (argc > 2 && strncasecmp(arg, "FULL", 4) != 0) {
-        php_error_docref(NULL, E_WARNING, "'%s' is not a valid option for XINFO STREAM", arg);
+    if ((arg != NULL && key == NULL) || (count != -1 && (key == NULL || arg == NULL))) {
+        php_error_docref(NULL, E_WARNING, "Cannot pass a non-null optional argument after a NULL one.");
         return FAILURE;
     }
 
-    /* If we have a COUNT bump the argument count to account for the 'COUNT' literal */
-    if (argc == 4) argc++;
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (key != NULL) + (arg != NULL) + (count > -1 ? 2 : 0), "XINFO");
+    redis_cmd_append_sstr_zstr(&cmdstr, op);
 
-    fmt[argc] = '\0';
+    if (key != NULL)
+        redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot);
+    if (arg != NULL)
+        redis_cmd_append_sstr_zstr(&cmdstr, arg);
 
-    /* Build our XINFO STREAM variant */
-    *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XINFO", fmt, "STREAM", 6, key, keylen,
-                                  "FULL", 4, "COUNT", 5, count);
+    if (count > -1) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
+        redis_cmd_append_sstr_long(&cmdstr, count);
+    }
 
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
     return SUCCESS;
 }
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 4023c37b12..044c7884c8 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6742,9 +6742,9 @@ public function testXClaim() {
 
     public function testXInfo()
     {
-        if (!$this->minVersionCheck("5.0")) {
-            return $this->markTestSkipped();
-        }
+        if (!$this->minVersionCheck("5.0"))
+            $this->markTestSkipped();
+
         /* Create some streams and groups */
         $stream = 's';
         $groups = ['g1' => 0, 'g2' => 0];
@@ -6767,6 +6767,12 @@ public function testXInfo()
             $this->assertTrue(is_array($info[$key]));
         }
 
+        /* Ensure that default/NULL arguments are ignored */
+        $info = $this->redis->xInfo('STREAM', $stream, NULL);
+        $this->assertTrue(is_array($info));
+        $info = $this->redis->xInfo('STREAM', $stream, NULL, -1);
+        $this->assertTrue(is_array($info));
+
         /* XINFO STREAM FULL [COUNT N] Requires >= 6.0.0 */
         if (!$this->minVersionCheck("6.0"))
             return;
@@ -6791,6 +6797,13 @@ public function testXInfo()
             $n = isset($info['entries']) ? count($info['entries']) : 0;
             $this->assertEquals($n, $this->redis->xLen($stream));
         }
+
+        /* Make sure we can't erroneously send non-null args after null ones */
+        $this->redis->clearLastError();
+        $this->assertFalse(@$this->redis->xInfo('FOO', NULL, 'fail', 25));
+        $this->assertEquals(NULL, $this->redis->getLastError());
+        $this->assertFalse(@$this->redis->xInfo('FOO', NULL, NULL, -2));
+        $this->assertEquals(NULL, $this->redis->getLastError());
     }
 
     /* Regression test for issue-1831 (XINFO STREAM on an empty stream) */

From 457953f4fe027a856e951c43095f2549a937cdff Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 10 Oct 2022 22:57:10 -0700
Subject: [PATCH 1638/1986] Refactor and fix XPENDING handler

Fixes #2128
---
 redis.stub.php                 |  2 +-
 redis_arginfo.h                |  8 ++++----
 redis_cluster.stub.php         |  2 +-
 redis_cluster_arginfo.h        | 10 +++++-----
 redis_cluster_legacy_arginfo.h |  2 +-
 redis_commands.c               | 35 ++++++++++++++++++----------------
 redis_legacy_arginfo.h         |  2 +-
 tests/RedisTest.php            |  4 ++++
 8 files changed, 36 insertions(+), 29 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 55605e9646..41f9fff49e 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -507,7 +507,7 @@ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = n
 
     public function xlen(string $key): int;
 
-    public function xpending(string $key, string $group, string $start = null, string $end = null, int $count = -1, string $consumer = null): Redis|array|false;
+    public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false;
 
     public function xrange(string $key, string $start, string $end, int $count = -1): bool|array;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 37265f100a..61c40e72b7 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a39dd09e86258566f2eae441d920ef280f8a3e72 */
+ * Stub hash: 73e34ca5d2f49dd1dbcbf901d3dd48019e1ba5dc */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -892,10 +892,10 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xpending, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index b0792bd0ff..2a1faaf887 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -348,7 +348,7 @@ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = n
 
     public function xlen(string $key): int;
 
-    public function xpending(string $key, string $group, string $start = null, string $end = null, int $count = -1, string $consumer = null): string;
+    public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false;
 
     public function xrange(string $key, string $start, string $end, int $count = -1): bool|array;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 3c5a1e9f1d..1052db1ef4 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 396a72b8937928cf3ed504a2a8063f5090e7196b */
+ * Stub hash: 59682d20ee8ebad4f8a5c914432f41dac0860770 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -749,13 +749,13 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster_decr
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xpending, 0, 2, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xpending, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index dd9e31966e..3484a2dbfd 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 396a72b8937928cf3ed504a2a8063f5090e7196b */
+ * Stub hash: 59682d20ee8ebad4f8a5c914432f41dac0860770 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_commands.c b/redis_commands.c
index 9a2395dca8..f74b2799fb 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5037,23 +5037,26 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                        char **cmd, int *cmd_len, short *slot, void **ctx)
 {
+    zend_string *key = NULL, *group = NULL, *start = NULL, *end = NULL,
+                *consumer = NULL;
+    zend_long count = -1, idle = 0;
     smart_string cmdstr = {0};
-    char *key, *group, *start = NULL, *end = NULL, *consumer = NULL;
-    size_t keylen, grouplen, startlen, endlen, consumerlen;
     int argc;
-    zend_long count = -1, idle = 0;
 
-    // XPENDING mystream group55 - + 10 consumer-123
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|sslsl", &key,
-                              &keylen, &group, &grouplen, &start, &startlen,
-                              &end, &endlen, &count, &consumer, &consumerlen,
-                              &idle) == FAILURE)
-    {
-        return FAILURE;
-    }
+    ZEND_PARSE_PARAMETERS_START(2, 7)
+        Z_PARAM_STR(key)
+        Z_PARAM_STR(group)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_STR_OR_NULL(start)
+        Z_PARAM_STR_OR_NULL(end)
+        Z_PARAM_LONG(count)
+        Z_PARAM_STR_OR_NULL(consumer)
+        Z_PARAM_LONG(idle)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
 
     /* If we've been passed a start argument, we also need end and count */
     if (start != NULL && (end == NULL || count < 0)) {
+        php_error_docref(NULL, E_WARNING, "'$start' must be accompanied by '$end' and '$count' arguments");
         return FAILURE;
     }
 
@@ -5062,8 +5065,8 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     /* Construct command and add required arguments */
     REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XPENDING");
-    redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot);
-    redis_cmd_append_sstr(&cmdstr, group, grouplen);
+    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
+    redis_cmd_append_sstr_zstr(&cmdstr, group);
 
     /* Add optional argumentst */
     if (start) {
@@ -5071,12 +5074,12 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IDLE");
             redis_cmd_append_sstr_long(&cmdstr, (long)idle);
         }
-        redis_cmd_append_sstr(&cmdstr, start, startlen);
-        redis_cmd_append_sstr(&cmdstr, end, endlen);
+        redis_cmd_append_sstr_zstr(&cmdstr, start);
+        redis_cmd_append_sstr_zstr(&cmdstr, end);
         redis_cmd_append_sstr_long(&cmdstr, (long)count);
 
         /* Finally add consumer if we have it */
-        if (consumer) redis_cmd_append_sstr(&cmdstr, consumer, consumerlen);
+        if (consumer) redis_cmd_append_sstr_zstr(&cmdstr, consumer);
     }
 
     *cmd = cmdstr.c;
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index b3b6115192..d2a8c22c8b 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a39dd09e86258566f2eae441d920ef280f8a3e72 */
+ * Stub hash: 73e34ca5d2f49dd1dbcbf901d3dd48019e1ba5dc */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 044c7884c8..c55f65a896 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6604,6 +6604,10 @@ public function testXPending() {
                 $this->redis->xAck('s', 'group', [$id]);
             }
         }
+
+        /* Ensure we can have NULL trailing arguments */
+        $this->assertTrue(is_array($this->redis->xpending('s', 'group', '-', '+', 1, null)));
+        $this->assertTrue(is_array($this->redis->xpending('s', 'group', NULL, NULL, -1, NULL)));
     }
 
     public function testXDel() {

From 54a084e51c2ec0a511ab37bdcf866babb50a74c6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 10 Oct 2022 22:58:41 -0700
Subject: [PATCH 1639/1986] Refactor FLUSHDB and update docs.

Fixes #2096
---
 README.markdown            |  8 +++++---
 common.h                   |  2 ++
 redis.stub.php             | 24 ++++++++++++++++++++++--
 redis_arginfo.h            |  4 ++--
 redis_commands.c           | 28 ++++++++++++++++++----------
 redis_legacy_arginfo.h     |  2 +-
 tests/RedisClusterTest.php |  1 +
 tests/RedisTest.php        |  7 +++++++
 8 files changed, 58 insertions(+), 18 deletions(-)

diff --git a/README.markdown b/README.markdown
index 329abcf2dd..2106f96e93 100644
--- a/README.markdown
+++ b/README.markdown
@@ -608,11 +608,13 @@ $redis->flushAll();
 -----
 _**Description**_: Remove all keys from the current database.
 
-##### *Parameters*
-*async* (bool) requires server version 4.0.0 or greater
+##### *Prototype*
+~~~php
+$redis->flushdb(?bool $sync = NULL): Redis|bool;
+~~~
 
 ##### *Return value*
-*BOOL*: Always `TRUE`.
+*BOOL*:  This command returns true on success and false on failure.
 
 ##### *Example*
 ~~~php
diff --git a/common.h b/common.h
index f1b85b232b..7abaecd57a 100644
--- a/common.h
+++ b/common.h
@@ -153,6 +153,8 @@ typedef enum {
         Z_PARAM_STR_EX(dest, 1, 0)
 #define Z_PARAM_ZVAL_OR_NULL(dest) \
 	Z_PARAM_ZVAL_EX(dest, 1, 0)
+#define Z_PARAM_BOOL_OR_NULL(dest, is_null) \
+	Z_PARAM_BOOL_EX(dest, is_null, 1, 0)
 #endif
 
 #if PHPREDIS_DEBUG_LOGGING == 1
diff --git a/redis.stub.php b/redis.stub.php
index 41f9fff49e..bdffb41181 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -132,9 +132,29 @@ public function expiretime(string $key): Redis|int|false;
 
     public function pexpiretime(string $key): Redis|int|false;
 
-    public function flushAll(?bool $sync = null): bool;
+    /**
+     * Deletes every key in all Redis databases
+     *
+     * @param  bool  $sync Whether to perform the task in a blocking or non-blocking way.
+     *               when TRUE, PhpRedis will execute `FLUSHALL SYNC`, and when FALSE we
+     *               will execute `FLUSHALL ASYNC`.  If the argument is omitted, we
+     *               simply execute `FLUSHALL` and whether it is SYNC or ASYNC depends
+     *               on Redis' `lazyfree-lazy-user-flush` config setting.
+     * @return bool
+     */
+    public function flushAll(?bool $sync = null): Redis|bool;
 
-    public function flushDB(?bool $sync = null): bool;
+    /**
+     * Deletes all the keys of the currently selected database.
+     *
+     * @param  bool  $sync Whether to perform the task in a blocking or non-blocking way.
+     *               when TRUE, PhpRedis will execute `FLUSHDB SYNC`, and when FALSE we
+     *               will execute `FLUSHDB ASYNC`.  If the argument is omitted, we
+     *               simply execute `FLUSHDB` and whether it is SYNC or ASYNC depends
+     *               on Redis' `lazyfree-lazy-user-flush` config setting.
+     * @return bool
+     */
+    public function flushDB(?bool $sync = null): Redis|bool;
 
     public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): int;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 61c40e72b7..5f9b501b3b 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 73e34ca5d2f49dd1dbcbf901d3dd48019e1ba5dc */
+ * Stub hash: c9de2943a9517d8007381f36a47ab45ef911ae67 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -217,7 +217,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_pexpiretime arginfo_class_Redis_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_flushAll, 0, 0, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sync, _IS_BOOL, 1, "null")
 ZEND_END_ARG_INFO()
 
diff --git a/redis_commands.c b/redis_commands.c
index f74b2799fb..098b3a9d44 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -529,20 +529,28 @@ redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    int sync = -1;
+    smart_string cmdstr = {0};
+    zend_bool sync = 0;
+    zend_bool is_null = 1;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &sync) == FAILURE) {
-        return FAILURE;
-    }
+    ZEND_PARSE_PARAMETERS_START(0, 1)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_BOOL_OR_NULL(sync, is_null)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
 
-    if (sync < 0) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "");
-    } else if (sync > 0) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", "SYNC", sizeof("SYNC") - 1);
-    } else {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1);
+    redis_cmd_init_sstr(&cmdstr, !is_null, kw, strlen(kw));
+    if (!is_null) {
+        ZEND_ASSERT(sync == 0 || sync == 1);
+        if (sync == 0) {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASYNC");
+        } else {
+            REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SYNC");
+        }
     }
 
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
     return SUCCESS;
 }
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index d2a8c22c8b..e2ff15c95b 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 73e34ca5d2f49dd1dbcbf901d3dd48019e1ba5dc */
+ * Stub hash: c9de2943a9517d8007381f36a47ab45ef911ae67 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 132c3744f3..4030faec2e 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -65,6 +65,7 @@ public function testGeoSearch() { return $this->marktestSkipped(); }
     public function testGeoSearchStore() { return $this->marktestSkipped(); }
     public function testHRandField() { return $this->marktestSkipped(); }
     public function testConfig() { return $this->markTestSkipped(); }
+    public function testFlushDB() { return $this->markTestSkipped(); }
 
     /* Session locking feature is currently not supported in in context of Redis Cluster.
        The biggest issue for this is the distribution nature of Redis cluster */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c55f65a896..2e869e9a7e 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2160,6 +2160,13 @@ public function testdbSize() {
         $this->assertTrue($this->redis->dbSize() === 1);
     }
 
+    public function testFlushDB() {
+        $this->assertTrue($this->redis->flushdb());
+        $this->assertTrue($this->redis->flushdb(NULL));
+        $this->assertTrue($this->redis->flushdb(false));
+        $this->assertTrue($this->redis->flushdb(true));
+    }
+
     public function testttl() {
         $this->redis->set('x', 'y');
         $this->redis->expire('x', 5);

From 9aa5f38707d13f44ad367211e1d56b18202f725c Mon Sep 17 00:00:00 2001
From: Nicolas Grekas 
Date: Tue, 11 Oct 2022 17:33:09 +0200
Subject: [PATCH 1640/1986] Fix redis.stub.php

---
 redis.stub.php | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index bdffb41181..3bd3731c12 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -164,13 +164,13 @@ public function geohash(string $key, string $member, string ...$other_members):
 
     public function geopos(string $key, string $member, string ...$other_members): Redis|array|false;
 
-    public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): Redis|mixed|false;
+    public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed;
 
-    public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): Redis|mixed|false;
+    public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed;
 
-    public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): Redis|mixed|false;
+    public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): mixed;
 
-    public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): Redis|mixed|false;
+    public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): mixed;
 
     public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): array;
 
@@ -218,7 +218,7 @@ public function hDel(string $key, string $member, string ...$other_members): Red
 
     public function hExists(string $key, string $member): Redis|bool;
 
-    public function hGet(string $key, string $member): Redis|mixed|false;
+    public function hGet(string $key, string $member): mixed;
 
     public function hGetAll(string $key): Redis|array|false;
 
@@ -299,7 +299,7 @@ public function lSet(string $key, int $index, mixed $value): Redis|bool;
 
     public function lastSave(): int;
 
-    public function lindex(string $key, int $index): Redis|mixed|false;
+    public function lindex(string $key, int $index): mixed;
 
     public function lrange(string $key, int $start , int $end): Redis|array|false;
 
@@ -315,7 +315,7 @@ public function mget(array $keys);
 
     public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout,
                             bool $copy = false, bool $replace = false,
-                            #[\SensitiveParameter] ?mixed $credentials = NULL): Redis|bool;
+                            #[\SensitiveParameter] mixed $credentials = NULL): Redis|bool;
 
     public function move(string $key, int $index): bool;
 

From 74cf49f554b21c2a9f2b0b482835a4985308a3f6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 11 Oct 2022 11:22:35 -0700
Subject: [PATCH 1641/1986] More redis.stub.php and PHP 8.2 deprecation fixes.

With this commit, I can now run the full `--class Redis` unit test suite
inside of a PHP8.2.x buiild tree and have them pass and not throw any
fatal zpp argument errors.
---
 redis.stub.php             | 190 +++++++++++++++-------------------
 redis_arginfo.h            | 207 +++++++++++++++++++------------------
 redis_legacy_arginfo.h     |   2 +-
 tests/RedisClusterTest.php |   2 +-
 tests/RedisTest.php        |   6 +-
 5 files changed, 197 insertions(+), 210 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 3bd3731c12..660da9ab9a 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -26,41 +26,32 @@ public function _unpack(string $value): mixed;
 
     public function _unserialize(string $value): mixed;
 
-    /**
-     * @param string $args
-     * @return mixed|Redis
-     */
-    public function acl(string $subcmd, ...$args);
+    public function acl(string $subcmd, string ...$args): mixed;
 
-	/** @return int|Redis */
-    public function append(string $key, mixed $value);
+    public function append(string $key, mixed $value): Redis|int|false;
 
-    public function auth(#[\SensitiveParameter] mixed $credentials): bool;
+    public function auth(#[\SensitiveParameter] mixed $credentials): Redis|bool;
 
-    public function bgSave(): bool;
+    public function bgSave(): Redis|bool;
 
-    public function bgrewriteaof(): bool;
+    public function bgrewriteaof(): Redis|bool;
 
-    /** @return int|Redis */
-    public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false);
 
-    /**
-     * @return int|Redis
-     */
-    public function bitop(string $operation, string $deskey, string $srckey, string ...$other_keys): int;
+    public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false;
 
-    /** @return int|Redis */
-    public function bitpos(string $key, int $bit, int $start = 0, int $end = -1);
+    public function bitop(string $operation, string $deskey, string $srckey, string ...$other_keys): Redis|int|false;
 
-    public function blPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): array|null|false;
+    public function bitpos(string $key, int $bit, int $start = 0, int $end = -1): Redis|int|false;
 
-    public function brPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): array|null|false;
+    public function blPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false;
+
+    public function brPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false;
 
     public function brpoplpush(string $src, string $dst, int $timeout): Redis|string|false;
 
-    public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+    public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false;
 
-    public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+    public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false;
 
     public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
 
@@ -70,7 +61,7 @@ public function blmpop(float $timeout, array $keys, string $from, int $count = 1
 
     public function lmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
 
-    public function clearLastError(): bool;
+    public function clearLastError(): Redis|bool;
 
     public function client(string $opt, mixed ...$args): mixed;
 
@@ -82,36 +73,29 @@ public function config(string $operation, ?string $key = NULL, mixed $value = nu
 
     public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool;
 
-    public function copy(string $src, string $dst, array $options = null): bool;
+    public function copy(string $src, string $dst, array $options = null): Redis|bool;
 
-    public function dbSize(): int;
+    public function dbSize(): Redis|int;
 
-    public function debug(string $key): string;
+    public function debug(string $key): Redis|string;
 
-	/** @return int|Redis */
-    public function decr(string $key, int $by = 1);
+    public function decr(string $key, int $by = 1): Redis|int|false;
 
-	/** @return int|Redis */
-    public function decrBy(string $key, int $value);
+    public function decrBy(string $key, int $value): Redis|int|false;
 
-    /**
-     * @return int|Redis
-     */
-    public function del(array|string $key, string ...$other_keys);
+    public function del(array|string $key, string ...$other_keys): Redis|int|false;
 
     /**
      * @deprecated
      * @alias Redis::del
-     * @return int|Redis
      */
-    public function delete(array|string $key, string ...$other_keys);
+    public function delete(array|string $key, string ...$other_keys): Redis|int|false;
 
-    public function discard(): bool;
+    public function discard(): Redis|bool;
 
-    public function dump(string $key): string;
+    public function dump(string $key): Redis|string;
 
-	/** @return string|Redis */
-    public function echo(string $str);
+    public function echo(string $str): Redis|string|false;
 
     public function eval(string $script, array $keys = null, int $num_keys = 0): mixed;
 
@@ -119,14 +103,13 @@ public function evalsha(string $sha1, array $keys = null, int $num_keys = 0): mi
 
     public function exec(): Redis|array|false;
 
-	/** @return int|Redis|bool */
-    public function exists(mixed $key, mixed ...$other_keys);
+    public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool;
 
     public function expire(string $key, int $timeout): Redis|bool;
 
     public function expireAt(string $key, int $timestamp): Redis|bool;
 
-    public function failover(?array $to = null, bool $abort = false, int $timeout = 0): bool;
+    public function failover(?array $to = null, bool $abort = false, int $timeout = 0): Redis|bool;
 
     public function expiretime(string $key): Redis|int|false;
 
@@ -156,11 +139,11 @@ public function flushAll(?bool $sync = null): Redis|bool;
      */
     public function flushDB(?bool $sync = null): Redis|bool;
 
-    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): int;
+    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): Redis|int|false;
 
     public function geodist(string $key, string $src, string $dst, ?string $unit = null): Redis|float|false;
 
-    public function geohash(string $key, string $member, string ...$other_members): array;
+    public function geohash(string $key, string $member, string ...$other_members): Redis|array|false;
 
     public function geopos(string $key, string $member, string ...$other_members): Redis|array|false;
 
@@ -174,21 +157,19 @@ public function georadiusbymember_ro(string $key, string $member, float $radius,
 
     public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): array;
 
-    public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): array;
+    public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): Redis|array|int|false;
 
-	/** @return false|string|Redis */
-    public function get(string $key);
+    public function get(string $key): Redis|mixed|false;
 
     public function getAuth(): mixed;
 
-	/** @return int|Redis */
-    public function getBit(string $key, int $idx);
+    public function getBit(string $key, int $idx): Redis|int|false;
 
-    public function getEx(string $key, array $options = []): bool|string;
+    public function getEx(string $key, array $options = []): Redis|string|bool;
 
     public function getDBNum(): int;
 
-    public function getDel(string $key): bool|string;
+    public function getDel(string $key): Redis|string|bool;
 
     public function getHost(): string;
 
@@ -202,15 +183,13 @@ public function getPersistentID(): ?string;
 
     public function getPort(): int;
 
-	/** @return string|Redis */
-    public function getRange(string $key, int $start, int $end);
+    public function getRange(string $key, int $start, int $end): Redis|string|false;
 
     public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
 
     public function getReadTimeout(): int;
 
-	/** @return string|Redis */
-    public function getset(string $key, mixed $value);
+    public function getset(string $key, mixed $value): Redis|string|false;
 
     public function getTimeout(): int;
 
@@ -240,42 +219,41 @@ public function hSet(string $key, string $member, mixed $value): Redis|int|false
 
     public function hSetNx(string $key, string $member, string $value): Redis|bool;
 
-    public function hStrLen(string $key, string $member): int;
+    public function hStrLen(string $key, string $member): Redis|int|false;
 
     public function hVals(string $key): Redis|array|false;
 
-    public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|bool|array;
 
-	/** @return int|Redis */
+    /** @return Redis|int|false */
     public function incr(string $key, int $by = 1);
 
-	/** @return int|Redis */
+    /** @return Redis|int|false */
     public function incrBy(string $key, int $value);
 
-	/** @return int|Redis */
+    /** @return Redis|int|false */
     public function incrByFloat(string $key, float $value);
 
     public function info(string $opt = null): Redis|array|false;
 
     public function isConnected(): bool;
 
-	/** @return array|Redis */
+    /** @return Redis|array|false */
     public function keys(string $pattern);
 
     /**
      * @param mixed $elements
-     * @return int|Redis
+     * @return Redis|int|false
      */
     public function lInsert(string $key, string $pos, mixed $pivot, mixed $value);
 
-
     public function lLen(string $key): Redis|int|false;
 
-    public function lMove(string $src, string $dst, string $wherefrom, string $whereto): string;
+    public function lMove(string $src, string $dst, string $wherefrom, string $whereto): Redis|string|false;
 
-    public function lPop(string $key, int $count = 0): bool|string|array;
+    public function lPop(string $key, int $count = 0): Redis|bool|string|array;
 
-    public function lPos(string $key, mixed $value, array $options = null): null|bool|int|array;
+    public function lPos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array;
 
     /**
      * @param mixed $elements
@@ -285,14 +263,14 @@ public function lPush(string $key, ...$elements);
 
     /**
      * @param mixed $elements
-     * @return int|Redis
+     * @return Redis|int|false
      */
     public function rPush(string $key, ...$elements);
 
-	/** @return int|Redis */
+    /** @return Redis|int|false*/
     public function lPushx(string $key, mixed $value);
 
-	/** @return int|Redis */
+    /** @return Redis|int|false*/
     public function rPushx(string $key, mixed $value);
 
     public function lSet(string $key, int $index, mixed $value): Redis|bool;
@@ -310,7 +288,7 @@ public function lrem(string $key, mixed $value, int $count = 0);
 
     public function ltrim(string $key, int $start , int $end): Redis|bool;
 
-	/** @return array|Redis */
+    /** @return array|Redis */
     public function mget(array $keys);
 
     public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout,
@@ -347,7 +325,7 @@ public function pfcount(string $key): int;
 
     public function pfmerge(string $dst, array $keys): bool;
 
-	/** @return string|Redis */
+    /** @return string|Redis */
     public function ping(string $key = NULL);
 
     public function pipeline(): bool|Redis;
@@ -369,19 +347,19 @@ public function publish(string $channel, string $message): mixed;
 
     public function pubsub(string $command, mixed $arg = null): mixed;
 
-    public function punsubscribe(array $patterns): bool|array;
+    public function punsubscribe(array $patterns): Redis|array|bool;
 
-    public function rPop(string $key, int $count = 0): bool|string|array;
+    public function rPop(string $key, int $count = 0): Redis|array|string|bool;
 
-	/** @return string|Redis */
+    /** @return string|Redis */
     public function randomKey();
 
     public function rawcommand(string $command, mixed ...$args): mixed;
 
-	/** @return bool|Redis */
+    /** @return bool|Redis */
     public function rename(string $key_src, string $key_dst);
 
-	/** @return bool|Redis */
+    /** @return bool|Redis */
     public function renameNx(string $key_src, string $key_dst);
 
     public function reset(): bool;
@@ -428,15 +406,15 @@ public function scard(string $key): Redis|int|false;
 
     public function script(string $command, mixed ...$args): mixed;
 
-    public function select(int $db): bool;
+    public function select(int $db): Redis|bool;
 
     /** @return bool|Redis */
     public function set(string $key, mixed $value, mixed $opt = NULL);
 
-	/** @return int|Redis */
+    /** @return Redis|int|false*/
     public function setBit(string $key, int $idx, bool $value);
 
-	/** @return int|Redis */
+    /** @return Redis|int|false*/
     public function setRange(string $key, int $start, string $value);
 
 
@@ -445,7 +423,7 @@ public function setOption(int $option, mixed $value): bool;
     /** @return bool|Redis */
     public function setex(string $key, int $expire, mixed $value);
 
-	/** @return bool|array|Redis */
+    /** @return bool|array|Redis */
     public function setnx(string $key, mixed $value);
 
     public function sismember(string $key, mixed $value): Redis|bool;
@@ -480,7 +458,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i
 
     public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
 
-	/** @return int|Redis */
+    /** @return Redis|int|false*/
     public function strlen(string $key);
 
     public function subscribe(array $channels, callable $cb): bool;
@@ -491,17 +469,17 @@ public function time(): array;
 
     public function ttl(string $key): Redis|int|false;
 
-	/** @return int|Redis */
+    /** @return Redis|int|false*/
     public function type(string $key);
 
-       /**
-     * @return int|Redis
+    /**
+     * @return Redis|int|false
      */
     public function unlink(array|string $key, string ...$other_keys);
 
-    public function unsubscribe(array $channels): bool|array;
+    public function unsubscribe(array $channels): Redis|array|bool;
 
-	/** @return bool|Redis */
+    /** @return bool|Redis */
     public function unwatch();
 
     /**
@@ -513,11 +491,11 @@ public function wait(int $count, int $timeout): int|false;
 
     public function xack(string $key, string $group, array $ids): int|false;
 
-    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): string|false;
+    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): Redis|string|false;
 
-    public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): bool|array;
+    public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array;
 
-    public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): bool|array;
+    public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): Redis|bool|array;
 
     public function xdel(string $key, array $ids): Redis|int|false;
 
@@ -525,17 +503,17 @@ public function xgroup(string $operation, string $key = null, string $arg1 = nul
 
     public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
 
-    public function xlen(string $key): int;
+    public function xlen(string $key): Redis|int|false;
 
     public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false;
 
-    public function xrange(string $key, string $start, string $end, int $count = -1): bool|array;
+    public function xrange(string $key, string $start, string $end, int $count = -1): Redis|array|bool;
 
-    public function xread(array $streams, int $count = -1, int $block = -1): bool|array;
+    public function xread(array $streams, int $count = -1, int $block = -1): Redis|array|bool;
 
-    public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): bool|array;
+    public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): Redis|array|bool;
 
-    public function xrevrange(string $key, string $start, string $end, int $count = -1): bool|array;
+    public function xrevrange(string $key, string $start, string $end, int $count = -1): Redis|array|bool;
 
     public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): Redis|int|false;
 
@@ -549,25 +527,25 @@ public function zIncrBy(string $key, float $value, mixed $member): Redis|float|f
 
     public function zLexCount(string $key, string $min, string $max): Redis|int|false;
 
-    public function zMscore(string $key, string $member, string ...$other_members): array;
+    public function zMscore(string $key, string $member, string ...$other_members): Redis|array|false;
 
-    public function zPopMax(string $key, int $value = null): array;
+    public function zPopMax(string $key, int $value = null): Redis|array|false;
 
-    public function zPopMin(string $key, int $value = null): array;
+    public function zPopMin(string $key, int $value = null): Redis|array|false;
 
     public function zRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false;
 
-    public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): array;
+    public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false;
 
     public function zRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false;
 
-    public function zRandMember(string $key, array $options = null): string|array;
+    public function zRandMember(string $key, array $options = null): Redis|string|array;
 
     public function zRank(string $key, mixed $member): Redis|int|false;
 
     public function zRem(mixed $key, mixed $member, mixed ...$other_members): Redis|int|false;
 
-    public function zRemRangeByLex(string $key, string $min, string $max): int;
+    public function zRemRangeByLex(string $key, string $min, string $max): Redis|int|false;
 
     public function zRemRangeByRank(string $key, int $start, int $end): Redis|int|false;
 
@@ -575,17 +553,17 @@ public function zRemRangeByScore(string $key, string $start, string $end): Redis
 
     public function zRevRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false;
 
-    public function zRevRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): array;
+    public function zRevRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false;
 
-    public function zRevRangeByScore(string $key, string $start, string $end, array $options = []): array;
+    public function zRevRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false;
 
     public function zRevRank(string $key, mixed $member): Redis|int|false;
 
     public function zScore(string $key, mixed $member): Redis|float|false;
 
-    public function zdiff(array $keys, array $options = null): array;
+    public function zdiff(array $keys, array $options = null): Redis|array|false;
 
-    public function zdiffstore(string $dst, array $keys, array $options = null): int;
+    public function zdiffstore(string $dst, array $keys, array $options = null): Redis|int|false;
 
     public function zinter(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false;
 
@@ -593,7 +571,7 @@ public function zintercard(array $keys, int $limit = -1): Redis|int|false;
 
     public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false;
 
-    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|bool|array;
 
     public function zunion(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 5f9b501b3b..c015689ba9 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c9de2943a9517d8007381f36a47ab45ef911ae67 */
+ * Stub hash: 6d0479328ae627b9c45104a52014b3649e533015 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -30,47 +30,47 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis__unserialize arginfo_class_Redis__unpack
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_acl, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_INFO(0, args)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_append, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_append, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_auth, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_auth, 0, 1, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, credentials, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bgSave, 0, 0, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bgSave, 0, 0, Redis, MAY_BE_BOOL)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis_bgSave
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitcount, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bitop, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitop, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, deskey, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitpos, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, bit, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_blPop, 0, 2, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_blPop, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_LONG, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
@@ -84,7 +84,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_brpoplpush, 0, 3
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_bzPopMax, 0, 2, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bzPopMax, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
@@ -116,7 +116,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_client, 0, 1, IS_MIX
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_close arginfo_class_Redis_bgSave
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_close, 0, 0, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
@@ -139,28 +140,30 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_B
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_copy, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_copy, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_dbSize, 0, 0, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dbSize, 0, 0, Redis, MAY_BE_LONG)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_debug arginfo_class_Redis__prefix
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_debug, 0, 1, Redis, MAY_BE_STRING)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_decr, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_decr, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_decrBy, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_decrBy, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_del, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -169,9 +172,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_discard arginfo_class_Redis_bgSave
 
-#define arginfo_class_Redis_dump arginfo_class_Redis__prefix
+#define arginfo_class_Redis_dump arginfo_class_Redis_debug
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_echo, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_echo, 0, 1, Redis, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
@@ -190,7 +193,7 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_exec, 0, 0, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_exists, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_exists, 0, 1, Redis, MAY_BE_LONG|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
@@ -205,7 +208,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expireAt, 0, 2,
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_failover, 0, 0, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_failover, 0, 0, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, to, IS_ARRAY, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, abort, _IS_BOOL, 0, "false")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_LONG, 0, "0")
@@ -223,7 +226,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geoadd, 0, 4, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geoadd, 0, 4, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
@@ -238,19 +241,15 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geodist, 0, 3, R
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geohash, 0, 2, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geohash, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geopos, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_geopos arginfo_class_Redis_geohash
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_georadius, 0, 5, Redis, MAY_BE_ANY|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_georadius, 0, 5, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
@@ -261,7 +260,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_georadius_ro arginfo_class_Redis_georadius
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_georadiusbymember, 0, 4, Redis, MAY_BE_ANY|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_georadiusbymember, 0, 4, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0)
@@ -279,7 +278,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearch, 0, 4, IS_
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearchstore, 0, 5, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geosearchstore, 0, 5, Redis, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
@@ -288,26 +287,27 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearchstore, 0, 5
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_get, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_get, 0, 1, Redis, MAY_BE_ANY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getAuth, 0, 0, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getBit, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getEx, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getEx, 0, 1, Redis, MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getDBNum arginfo_class_Redis_dbSize
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getDBNum, 0, 0, IS_LONG, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getDel, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getDel, 0, 1, Redis, MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
@@ -317,7 +317,7 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getLastError, 0, 0, IS_STRING, 1)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getMode arginfo_class_Redis_dbSize
+#define arginfo_class_Redis_getMode arginfo_class_Redis_getDBNum
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getOption, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
@@ -325,9 +325,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getPersistentID arginfo_class_Redis_getLastError
 
-#define arginfo_class_Redis_getPort arginfo_class_Redis_dbSize
+#define arginfo_class_Redis_getPort arginfo_class_Redis_getDBNum
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getRange, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
@@ -339,11 +339,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lcs, 0, 2, Redis
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis_dbSize
+#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis_getDBNum
 
-#define arginfo_class_Redis_getset arginfo_class_Redis_append
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getset, 0, 2, Redis, MAY_BE_STRING|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getTimeout arginfo_class_Redis_dbSize
+#define arginfo_class_Redis_getTimeout arginfo_class_Redis_getDBNum
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hDel, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -356,7 +359,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hExists, 0, 2, R
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hGet, 0, 2, Redis, MAY_BE_ANY|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGet, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -408,23 +411,29 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSetNx, 0, 3, Re
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hStrLen, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hStrLen, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_hVals arginfo_class_Redis_hGetAll
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_incr arginfo_class_Redis_decr
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incr, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_incrBy arginfo_class_Redis_decrBy
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrBy, 0, 0, 2)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrByFloat, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -435,7 +444,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_info, 0, 0, Redi
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_isConnected arginfo_class_Redis_bgSave
+#define arginfo_class_Redis_isConnected arginfo_class_Redis_close
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
@@ -450,19 +459,19 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_lLen arginfo_class_Redis_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lMove, 0, 4, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lMove, 0, 4, Redis, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_lPop, 0, 1, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPop, 0, 1, Redis, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -475,9 +484,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_rPush arginfo_class_Redis_lPush
 
-#define arginfo_class_Redis_lPushx arginfo_class_Redis_append
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPushx, 0, 0, 2)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_rPushx arginfo_class_Redis_append
+#define arginfo_class_Redis_rPushx arginfo_class_Redis_lPushx
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lSet, 0, 3, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -485,9 +497,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lSet, 0, 3, Redi
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_lastSave arginfo_class_Redis_dbSize
+#define arginfo_class_Redis_lastSave arginfo_class_Redis_getDBNum
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lindex, 0, 2, Redis, MAY_BE_ANY|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_lindex, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
 ZEND_END_ARG_INFO()
@@ -522,7 +534,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_migrate, 0, 5, R
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, copy, _IS_BOOL, 0, "false")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, replace, _IS_BOOL, 0, "false")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 1, "NULL")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_move, 0, 2, _IS_BOOL, 0)
@@ -617,11 +629,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pubsub, 0, 1, IS_MIX
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_MIXED, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_punsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_punsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_rPop arginfo_class_Redis_lPop
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rPop, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_randomKey arginfo_class_Redis___destruct
 
@@ -637,7 +652,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
-#define arginfo_class_Redis_reset arginfo_class_Redis_bgSave
+#define arginfo_class_Redis_reset arginfo_class_Redis_close
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_restore, 0, 3, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -685,14 +700,15 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sintercard, 0, 1
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sInterStore, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_sInterStore arginfo_class_Redis_del
 
 #define arginfo_class_Redis_sMembers arginfo_class_Redis_hGetAll
 
-#define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sMisMember, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sMove, 0, 3, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
@@ -711,7 +727,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore
 
-#define arginfo_class_Redis_save arginfo_class_Redis_bgSave
+#define arginfo_class_Redis_save arginfo_class_Redis_close
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
@@ -724,7 +740,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_script arginfo_class_Redis_rawcommand
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_select, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_select, 0, 1, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, db, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
@@ -753,7 +769,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_setex arginfo_class_Redis_psetex
 
-#define arginfo_class_Redis_setnx arginfo_class_Redis_append
+#define arginfo_class_Redis_setnx arginfo_class_Redis_lPushx
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sismember, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -799,7 +815,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sscan, 0, 2, MAY_BE_
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_strlen arginfo_class_Redis_get
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_strlen, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
@@ -816,17 +834,20 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_ttl arginfo_class_Redis_expiretime
 
-#define arginfo_class_Redis_type arginfo_class_Redis_get
+#define arginfo_class_Redis_type arginfo_class_Redis_strlen
 
-#define arginfo_class_Redis_unlink arginfo_class_Redis_del
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_unlink, 0, 0, 1)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_unsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_unsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_watch arginfo_class_Redis_del
+#define arginfo_class_Redis_watch arginfo_class_Redis_unlink
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_wait, 0, 2, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
@@ -839,7 +860,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xack, 0, 3, MAY_BE_L
 	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xadd, 0, 3, MAY_BE_STRING|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xadd, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
@@ -848,7 +869,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xadd, 0, 3, MAY_BE_S
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nomkstream, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xautoclaim, 0, 5, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xautoclaim, 0, 5, Redis, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
@@ -858,7 +879,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xautoclaim, 0, 5, MA
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, justid, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, Redis, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
@@ -887,7 +908,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xinfo, 0, 1, IS_MIXE
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_xlen arginfo_class_Redis_pfcount
+#define arginfo_class_Redis_xlen arginfo_class_Redis_expiretime
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xpending, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -898,20 +919,20 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xpending, 0, 2,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xrange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xread, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xread, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_xreadgroup, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xreadgroup, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0)
@@ -957,7 +978,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zMscore arginfo_class_Redis_geohash
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zPopMax, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zPopMax, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "null")
 ZEND_END_ARG_INFO()
@@ -971,7 +992,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRange, 0, 3, Re
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scores, IS_MIXED, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 3, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByLex, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
@@ -986,10 +1007,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByScore, 0
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_zRandMember, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRank, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -1002,11 +1020,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRem, 0, 2, Redi
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRemRangeByLex, 0, 3, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_zRemRangeByLex arginfo_class_Redis_zLexCount
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRemRangeByRank, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -1020,12 +1034,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zRevRangeByScore, 0, 3, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRangeByScore
 
 #define arginfo_class_Redis_zRevRank arginfo_class_Redis_zRank
 
@@ -1034,12 +1043,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zScore, 0, 2, Re
 	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zdiff, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiff, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_zdiffstore, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiffstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index e2ff15c95b..507c010eb7 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c9de2943a9517d8007381f36a47ab45ef911ae67 */
+ * Stub hash: 6d0479328ae627b9c45104a52014b3649e533015 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 4030faec2e..2ba985092f 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -287,7 +287,7 @@ public function testScanPrefix() {
 
         /* We should now have both prefixs' keys */
         foreach ($arr_keys as $str_prefix => $str_id) {
-            $this->assertTrue(in_array("${str_prefix}${str_id}", $arr_scan_keys));
+            $this->assertTrue(in_array("{$str_prefix}{$str_id}", $arr_scan_keys));
         }
     }
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 2e869e9a7e..e2c8b55942 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5704,7 +5704,7 @@ public function testScan() {
 
             // Make sure we can scan for specific types
             foreach ($arr_keys as $str_type => $arr_vals) {
-                foreach ([NULL, 10] as $i_count) {
+                foreach ([0, 10] as $i_count) {
                     $arr_resp = [];
 
                     $it = NULL;
@@ -5727,7 +5727,7 @@ public function testScanPrefix() {
         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;
+            $arr_all_keys["{$str_prefix}{$keyid}"] = true;
         }
 
         $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
@@ -5737,7 +5737,7 @@ public function testScanPrefix() {
             $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
             $it = NULL;
             $arr_keys = $this->redis->scan($it, "*$keyid*");
-            $this->assertEquals($arr_keys, ["${str_prefix}${keyid}"]);
+            $this->assertEquals($arr_keys, ["{$str_prefix}{$keyid}"]);
         }
 
         /* Unset the prefix option */

From 8b1eafe87abc62eb4705011b2a9192ee1db33696 Mon Sep 17 00:00:00 2001
From: Nicolas Grekas 
Date: Wed, 12 Oct 2022 11:58:47 +0200
Subject: [PATCH 1642/1986] Fix redis.stub.php (bis)

---
 redis.stub.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.stub.php b/redis.stub.php
index 660da9ab9a..81f5fe7930 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -159,7 +159,7 @@ public function geosearch(string $key, array|string $position, array|int|float $
 
     public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): Redis|array|int|false;
 
-    public function get(string $key): Redis|mixed|false;
+    public function get(string $key): mixed;
 
     public function getAuth(): mixed;
 

From e392dd88dd89780633da3dd9ea6fb33dd187ff05 Mon Sep 17 00:00:00 2001
From: Michael Grunder 
Date: Wed, 12 Oct 2022 13:16:43 -0700
Subject: [PATCH 1643/1986] RedisCluster stub fixes (#2183)

RedisCluster stub fixes

I can now run RedisCluster unit tests within a PHP build tree build in
debug mode without any deprecation warnings or arginfo/zpp errors.
---
 redis.stub.php                 |   5 +-
 redis_arginfo.h                |   6 +-
 redis_cluster.c                |  10 +
 redis_cluster.stub.php         | 320 ++++++++++++++--------------
 redis_cluster_arginfo.h        | 372 ++++++++++++++++++---------------
 redis_cluster_legacy_arginfo.h |  97 +++++----
 redis_commands.c               |  18 +-
 redis_legacy_arginfo.h         |   2 +-
 tests/RedisClusterTest.php     |   2 +-
 9 files changed, 453 insertions(+), 379 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 81f5fe7930..8cdede9564 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -408,8 +408,7 @@ public function script(string $command, mixed ...$args): mixed;
 
     public function select(int $db): Redis|bool;
 
-    /** @return bool|Redis */
-    public function set(string $key, mixed $value, mixed $opt = NULL);
+    public function set(string $key, mixed $value, mixed $opt = NULL): Redis|string|bool;
 
     /** @return Redis|int|false*/
     public function setBit(string $key, int $idx, bool $value);
@@ -432,7 +431,7 @@ public function slaveof(string $host = null, int $port = 6379): bool;
 
     public function slowlog(string $mode, int $option = 0): mixed;
 
-    public function sort(string $key, array $options = null): mixed;
+    public function sort(string $key, ?array $options = null): mixed;
 
     /**
      * @deprecated
diff --git a/redis_arginfo.h b/redis_arginfo.h
index c015689ba9..37241bbf0c 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 6d0479328ae627b9c45104a52014b3649e533015 */
+ * Stub hash: 1810caef11b38440e073059e2d9c65f92fa8a9a5 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -744,7 +744,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_select, 0, 1, Re
 	ZEND_ARG_TYPE_INFO(0, db, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_set, 0, 2, Redis, MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_MIXED, 0, "NULL")
@@ -788,7 +788,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sort, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sortAsc, 0, 1, IS_ARRAY, 0)
diff --git a/redis_cluster.c b/redis_cluster.c
index 5dead3fa7c..cdd730cefa 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1844,6 +1844,16 @@ PHP_METHOD(RedisCluster, _redir) {
 /* {{{ proto bool RedisCluster::multi() */
 PHP_METHOD(RedisCluster, multi) {
     redisCluster *c = GET_CONTEXT();
+    zend_long value = MULTI;
+
+    ZEND_PARSE_PARAMETERS_START(0, 1)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_LONG(value)
+    ZEND_PARSE_PARAMETERS_END();
+
+    if (value != MULTI) {
+        php_error_docref(NULL, E_WARNING, "RedisCluster does not support PIPELINING");
+    }
 
     if (c->flags->mode == MULTI) {
         php_error_docref(NULL, E_WARNING,
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 2a1faaf887..517ca4c8e1 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -30,21 +30,20 @@ public function _unserialize(string $value): mixed;
 
     public function acl(string|array $key_or_address, string $subcmd, string ...$args): mixed;
 
-    public function append(string $key, mixed $value): bool|int;
+    public function append(string $key, mixed $value): RedisCluster|bool|int;
 
-    public function bgrewriteaof(string|array $key_or_address): bool;
+    public function bgrewriteaof(string|array $key_or_address): RedisCluster|bool;
 
-    public function bgsave(string|array $key_or_address): bool;
+    public function bgsave(string|array $key_or_address): RedisCluster|bool;
 
-    public function bitcount(string $key, int $start = 0, int $end = -1): bool|int;
+    public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false): RedisCluster|bool|int;
 
-    public function bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys): bool|int;
+    public function bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys): RedisCluster|bool|int;
 
-    public function bitpos(string $key, int $bit, int $start = NULL, int $end = NULL): bool|int;
+    public function bitpos(string $key, int $bit, int $start = NULL, int $end = NULL): RedisCluster|bool|int;
 
-    public function blpop(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
-
-    public function brpop(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
+    public function blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): RedisCluster|array|null|false;
+    public function brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): RedisCluster|array|null|false;
 
     public function brpoplpush(string $srckey, string $deskey, int $timeout): mixed;
 
@@ -52,17 +51,17 @@ public function bzpopmax(string|array $key, string|int $timeout_or_key, mixed ..
 
     public function bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
 
-    public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
+    public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
-    public function zmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
+    public function zmpop(array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
-    public function blmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
+    public function blmpop(float $timeout, array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
-    public function lmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
+    public function lmpop(array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
     public function clearlasterror(): bool;
 
-    public function client(string|array $node, string $subcommand, string|null $arg): array|string|bool;
+    public function client(string|array $node, string $subcommand, ?string $arg = NULL): array|string|bool;
 
     public function close(): bool;
 
@@ -72,61 +71,61 @@ public function command(mixed ...$extra_args): mixed;
 
     public function config(string|array $node, string $subcommand, mixed ...$extra_args): mixed;
 
-    public function dbsize(string|array $key_or_address): int;
+    public function dbsize(string|array $key_or_address): RedisCluster|int;
 
-    public function decr(string $key): int;
+    public function decr(string $key, int $by = 1): RedisCluster|int|false;
 
-    public function decrby(string $key, int $value): int;
+    public function decrby(string $key, int $value): RedisCluster|int|false;
 
     public function decrbyfloat(string $key, float $value): float;
 
-    public function del(string $key, string ...$other_keys): array;
+    public function del(array|string $key, string ...$other_keys): RedisCluster|int|false;
 
     public function discard(): bool;
 
-    public function dump(string $key): string;
+    public function dump(string $key): RedisCluster|string|false;
 
-    public function echo(string|array $node, string $msg): string;
+    public function echo(string|array $node, string $msg): RedisCluster|string|false;
 
     public function eval(string $script, array $args = [], int $num_keys = 0): mixed;
 
     public function evalsha(string $script_sha, array $args = [], int $num_keys = 0): mixed;
 
-    public function exec(): array;
+    public function exec(): array|false;
 
-    public function exists(string $key): int;
+    public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
 
-    public function expire(string $key, int $timeout): bool;
+    public function expire(string $key, int $timeout): RedisCluster|bool;
 
-    public function expireat(string $key, int $timestamp): bool;
+    public function expireat(string $key, int $timestamp): RedisCluster|bool;
 
-    public function expiretime(string $key): Redis|int|false;
+    public function expiretime(string $key): RedisCluster|int|false;
 
-    public function pexpiretime(string $key): Redis|int|false;
+    public function pexpiretime(string $key): RedisCluster|int|false;
 
-    public function flushall(string|array $node, bool $async = false): bool;
+    public function flushall(string|array $node, bool $async = false): RedisCluster|bool;
 
-    public function flushdb(string|array $node, bool $async = false): bool;
+    public function flushdb(string|array $node, bool $async = false): RedisCluster|bool;
 
-    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): int;
+    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): RedisCluster|int;
 
-    public function geodist(string $key, string $src, string $dest, ?string $unit = null): array;
+    public function geodist(string $key, string $src, string $dest, ?string $unit = null): RedisCluster|float|false;
 
-    public function geohash(string $key, string $member, string ...$other_members): array;
+    public function geohash(string $key, string $member, string ...$other_members): RedisCluster|array|false;
 
-    public function geopos(string $key, string $member, string ...$other_members): array;
+    public function geopos(string $key, string $member, string ...$other_members): RedisCluster|array|false;
 
-    public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): array;
+    public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed;
 
-    public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): array;
+    public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed;
 
-    public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): array;
+    public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): mixed;
 
-    public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): array;
+    public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): mixed;
 
-    public function get(string $key): string;
+    public function get(string $key): mixed;
 
-    public function getbit(string $key, int $value): int;
+    public function getbit(string $key, int $value): RedisCluster|int|false;
 
     public function getlasterror(): string|null;
 
@@ -134,279 +133,282 @@ public function getmode(): int;
 
     public function getoption(int $option): mixed;
 
-    public function getrange(string $key, int $start, int $end): string;
+    public function getrange(string $key, int $start, int $end): RedisCluster|string|false;
 
-    public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
+    public function lcs(string $key1, string $key2, ?array $options = NULL): RedisCluster|string|array|int|false;
 
-    public function getset(string $key, mixed $value): string;
+    public function getset(string $key, mixed $value): RedisCluster|string|bool;
 
-    public function hdel(string $key, string $member, string ...$other_members): int;
+    public function hdel(string $key, string $member, string ...$other_members): RedisCluster|int|false;
 
-    public function hexists(string $key, string $member): bool;
+    public function hexists(string $key, string $member): RedisCluster|bool;
 
-    public function hget(string $key, string $member): string;
+    public function hget(string $key, string $member): mixed;
 
-    public function hgetall(string $key): array;
+    public function hgetall(string $key): RedisCluster|array|false;
 
-    public function hincrby(string $key, string $member, int $value): int;
+    public function hincrby(string $key, string $member, int $value): RedisCluster|int|false;
 
-    public function hincrbyfloat(string $key, string $member, float $value): float;
+    public function hincrbyfloat(string $key, string $member, float $value): RedisCluster|float|false;
 
-    public function hkeys(string $key): array;
+    public function hkeys(string $key): RedisCluster|array|false;
 
-    public function hlen(string $key): int;
+    public function hlen(string $key): RedisCluster|int|false;
 
-    public function hmget(string $key, array $members): array;
+    public function hmget(string $key, array $keys): RedisCluster|array|false;
 
-    public function hmset(string $key, array $key_values): bool;
+    public function hmset(string $key, array $key_values): RedisCluster|bool;
 
     public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|bool;
 
-    public function hset(string $key, string $member, mixed $value): int;
+    public function hset(string $key, string $member, mixed $value): RedisCluster|int|false;
 
-    public function hsetnx(string $key, string $member, mixed $value): bool;
+    public function hsetnx(string $key, string $member, mixed $value): RedisCluster|bool;
 
-    public function hstrlen(string $key, string $field): int;
+    public function hstrlen(string $key, string $field): RedisCluster|int|false;
 
-    public function hvals(string $key): array;
+    public function hvals(string $key): RedisCluster|array|false;
 
-    public function incr(string $key): int;
+    public function incr(string $key, int $by = 1): RedisCluster|int|false;
 
-    public function incrby(string $key, int $value): int;
+    public function incrby(string $key, int $value): RedisCluster|int|false;
 
-    public function incrbyfloat(string $key, float $value): float;
+    public function incrbyfloat(string $key, float $value): RedisCluster|float|false;
 
-    public function info(string|array $node, ?string $section = null): array;
+    public function info(string|array $node, ?string $section = null): RedisCluster|array|false;
 
-    public function keys(string $pattern): array;
+    public function keys(string $pattern): RedisCluster|array|false;
 
-    public function lastsave(string|array $node): int;
+    public function lastsave(string|array $node): RedisCluster|int|false;
 
-    public function lget(string $key, int $index): string|bool;
+    public function lget(string $key, int $index): RedisCluster|string|bool;
 
-    public function lindex(string $key, int $index): string|bool;
+    public function lindex(string $key, int $index): mixed;
 
-    public function linsert(string $key, string $pos, mixed $pivot, mixed $value): int;
+    public function linsert(string $key, string $pos, mixed $pivot, mixed $value): RedisCluster|int|false;
 
-    public function llen(string $key): int|bool;
+    public function llen(string $key): RedisCluster|int|bool;
 
-    public function lpop(string $key, int $count = 0): bool|string|array;
+    public function lpop(string $key, int $count = 0): RedisCluster|bool|string|array;
 
-    public function lpush(string $key, mixed $value, mixed ...$other_values): int|bool;
+    public function lpush(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|bool;
 
-    public function lpushx(string $key, mixed $value): int|bool;
+    public function lpushx(string $key, mixed $value): RedisCluster|int|bool;
 
-    public function lrange(string $key, int $start, int $end): array;
+    public function lrange(string $key, int $start, int $end): RedisCluster|array|false;
 
-    public function lrem(string $key, int $count, string $value): int|bool;
+    public function lrem(string $key, mixed $value, int $count = 0): RedisCluster|int|bool;
 
-    public function lset(string $key, int $index, string $value): bool;
+    public function lset(string $key, int $index, mixed $value): RedisCluster|bool;
 
-    public function ltrim(string $key, int $start, int $end): bool;
+    public function ltrim(string $key, int $start, int $end): RedisCluster|bool;
 
-    public function mget(array $keys): array;
+    public function mget(array $keys): RedisCluster|array|false;
 
-    public function mset(array $key_values): bool;
+    public function mset(array $key_values): RedisCluster|bool;
 
-    public function msetnx(array $key_values): int;
+    public function msetnx(array $key_values): RedisCluster|array|false;
 
-    public function multi(): RedisCluster|bool;
+    /* We only support Redis::MULTI in RedisCluster but take the argument
+       so we can test MULTI..EXEC with RedisTest.php and in the event
+       we add pipeline support in the future. */
+    public function multi(int $value = Redis::MULTI): RedisCluster|bool;
 
-    public function object(string $subcommand, string $key): int|string;
+    public function object(string $subcommand, string $key): RedisCluster|int|string|false;
 
-    public function persist(string $key): bool;
+    public function persist(string $key): RedisCluster|bool;
 
-    public function pexpire(string $key, int $timeout): bool;
+    public function pexpire(string $key, int $timeout): RedisCluster|bool;
 
-    public function pexpireat(string $key, int $timestamp): bool;
+    public function pexpireat(string $key, int $timestamp): RedisCluster|bool;
 
-    public function pfadd(string $key, array $elements): bool;
+    public function pfadd(string $key, array $elements): RedisCluster|bool;
 
-    public function pfcount(string $key): int;
+    public function pfcount(string $key): RedisCluster|int|false;
 
-    public function pfmerge(string $key, array $keys): bool;
+    public function pfmerge(string $key, array $keys): RedisCluster|bool;
 
-    public function ping(string|array $key_or_address, ?string $message): mixed;
+    public function ping(string|array $key_or_address, ?string $message = NULL): mixed;
 
-    public function psetex(string $key, int $timeout, string $value): bool;
+    public function psetex(string $key, int $timeout, string $value): RedisCluster|bool;
 
     public function psubscribe(array $patterns, callable $callback): void;
 
-    public function pttl(string $key): int;
+    public function pttl(string $key): RedisCluster|int|false;
 
-    public function publish(string $channel, string $message): bool;
+    public function publish(string $channel, string $message): RedisCluster|bool;
 
     public function pubsub(string|array $key_or_address, string ...$values): mixed;
 
     public function punsubscribe(string $pattern, string ...$other_patterns): bool|array;
 
-    public function randomkey(string|array $key_or_address): bool|string;
+    public function randomkey(string|array $key_or_address): RedisCluster|bool|string;
 
     public function rawcommand(string|array $key_or_address, string $command, mixed ...$args): mixed;
 
-    public function rename(string $key, string $newkey): bool;
+    public function rename(string $key_src, string $key_dst): RedisCluster|bool;
 
-    public function renamenx(string $key, string $newkey): bool;
+    public function renamenx(string $key, string $newkey): RedisCluster|bool;
 
-    public function restore(string $key, int $timeout, string $value, ?array $options = NULL): bool;
+    public function restore(string $key, int $timeout, string $value, ?array $options = NULL): RedisCluster|bool;
 
     public function role(string|array $key_or_address): mixed;
 
-    public function rpop(string $key, int $count = 0): bool|string|array;
+    public function rpop(string $key, int $count = 0): RedisCluster|bool|string|array;
 
-    public function rpoplpush(string $src, string $dst): bool|string;
+    public function rpoplpush(string $src, string $dst): RedisCluster|bool|string;
 
-    public function rpush(string $key, string $value, string ...$other_values): bool|int;
+    public function rpush(string $key, mixed ...$elements): RedisCluster|int|false;
 
-    public function rpushx(string $key, string $value): bool|int;
+    public function rpushx(string $key, string $value): RedisCluster|bool|int;
 
-    public function sadd(string $key, string $value, string ...$other_values): bool|int;
+    public function sadd(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false;
 
-    public function saddarray(string $key, array $values): bool|int;
+    public function saddarray(string $key, array $values): RedisCluster|bool|int;
 
-    public function save(string|array $key_or_address): bool;
+    public function save(string|array $key_or_address): RedisCluster|bool;
 
     public function scan(?int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
 
-    public function scard(string $key): int;
+    public function scard(string $key): RedisCluster|int|false;
 
     public function script(string|array $key_or_address, mixed ...$args): mixed;
 
-    public function sdiff(string $key, string ...$other_keys): array;
+    public function sdiff(string $key, string ...$other_keys): RedisCluster|array|false;
 
-    public function sdiffstore(string $dst, string $key, string ...$other_keys): int;
+    public function sdiffstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false;
 
-    public function set(string $key, string $value): bool;
+    public function set(string $key, mixed $value, mixed $options = NULL): RedisCluster|string|bool;
 
-    public function setbit(string $key, int $offset, bool $onoff): bool;
+    public function setbit(string $key, int $offset, bool $onoff): RedisCluster|int|false;
 
-    public function setex(string $key, string $value, int $timeout): bool;
+    public function setex(string $key, int $expire, mixed $value): RedisCluster|bool;
 
-    public function setnx(string $key, string $value, int $timeout): bool;
+    public function setnx(string $key, mixed $value): RedisCluster|bool;
 
     public function setoption(int $option, mixed $value): bool;
 
-    public function setrange(string $key, int $offset, string $value): int;
+    public function setrange(string $key, int $offset, string $value): RedisCluster|int|false;
 
-    public function sinter(string $key, string ...$other_keys): array;
+    public function sinter(array|string $key, string ...$other_keys): RedisCluster|array|false;
 
-    public function sintercard(array $keys, int $limit = -1): Redis|int|false;
+    public function sintercard(array $keys, int $limit = -1): RedisCluster|int|false;
 
-    public function sinterstore(string $dst, string $key, string ...$other_keys): bool;
+    public function sinterstore(array|string $key, string ...$other_keys): RedisCluster|int|false;
 
-    public function sismember(string $key): int;
+    public function sismember(string $key, mixed $value): RedisCluster|bool;
 
     public function slowlog(string|array $key_or_address, mixed ...$args): mixed;
 
-    public function smembers(string $key): array;
+    public function smembers(string $key): RedisCluster|array|false;
 
-    public function smove(string $src, string $dst, string $member): bool;
+    public function smove(string $src, string $dst, string $member): RedisCluster|bool;
 
-    public function sort(string $key, array $options): bool|int|string;
+    public function sort(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string;
 
-    public function spop(string $key): string|array;
+    public function spop(string $key, int $count = 0): RedisCluster|string|array|false;
 
-    public function srandmember(string $key, int $count = 0): string|array;
+    public function srandmember(string $key, int $count = 0): RedisCluster|string|array|false;
 
-    public function srem(string $key, string $value, string ...$other_values): int;
+    public function srem(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false;
 
-    public function sscan(string $key, ?int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
+    public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
 
-    public function strlen(string $key): int;
+    public function strlen(string $key): RedisCluster|int|false;
 
     public function subscribe(array $channels, callable $cb): void;
 
-    public function sunion(string $key, string ...$other_keys): bool|array;
+    public function sunion(string $key, string ...$other_keys): RedisCluster|bool|array;
 
-    public function sunionstore(string $dst, string $key, string ...$other_keys): int;
+    public function sunionstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false;
 
-    public function time(string|array $key_or_address): bool|array;
+    public function time(string|array $key_or_address): RedisCluster|bool|array;
 
-    public function ttl(string $key): int;
+    public function ttl(string $key): RedisCluster|int|false;
 
-    public function type(string $key): int;
+    public function type(string $key): RedisCluster|int|false;
 
     public function unsubscribe(array $channels): bool|array;
 
-    public function unlink(string $key, string ...$other_keys): array;
+    public function unlink(array|string $key, string ...$other_keys): RedisCluster|int|false;
 
     public function unwatch(): bool;
 
-    public function watch(string $key, string ...$other_keys): bool;
+    public function watch(string $key, string ...$other_keys): RedisCluster|bool;
 
-    public function xack(string $key, string $group, array $ids): int;
+    public function xack(string $key, string $group, array $ids): RedisCluster|int|false;
 
-    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false): string;
+    public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false): RedisCluster|string|false;
 
-    public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): string|array;
+    public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): RedisCluster|string|array|false;
 
-    public function xdel(string $key, array $ids): int;
+    public function xdel(string $key, array $ids): RedisCluster|int|false;
 
     public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
 
     public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
 
-    public function xlen(string $key): int;
+    public function xlen(string $key): RedisCluster|int|false;
 
-    public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false;
+    public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): RedisCluster|array|false;
 
-    public function xrange(string $key, string $start, string $end, int $count = -1): bool|array;
+    public function xrange(string $key, string $start, string $end, int $count = -1): RedisCluster|bool|array;
 
-    public function xread(array $streams, int $count = -1, int $block = -1): bool|array;
+    public function xread(array $streams, int $count = -1, int $block = -1): RedisCluster|bool|array;
 
-    public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): bool|array;
+    public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): RedisCluster|bool|array;
 
-    public function xrevrange(string $key, string $start, string $end, int $count = -1): bool|array;
+    public function xrevrange(string $key, string $start, string $end, int $count = -1): RedisCluster|bool|array;
 
     public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): RedisCluster|int|false;
 
-    public function zadd(string $key, float $score, string $member, mixed ...$extra_args): int;
+    public function zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): RedisCluster|int|false;
 
-    public function zcard(string $key): int;
+    public function zcard(string $key): RedisCluster|int|false;
 
-    public function zcount(string $key, string $start, string $end): int;
+    public function zcount(string $key, string $start, string $end): RedisCluster|int|false;
 
-    public function zincrby(string $key, float $value, string $member): float;
+    public function zincrby(string $key, float $value, string $member): RedisCluster|float|false;
 
-    public function zinterstore(string $key, array $keys, array $weights = null, string $aggregate = null): int;
+    public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): RedisCluster|int|false;
 
-    public function zintercard(array $keys, int $limit = -1): Redis|int|false;
+    public function zintercard(array $keys, int $limit = -1): RedisCluster|int|false;
 
-    public function zlexcount(string $key, string $min, string $max): int;
+    public function zlexcount(string $key, string $min, string $max): RedisCluster|int|false;
 
-    public function zpopmax(string $key, int $value = null): bool|array;
+    public function zpopmax(string $key, int $value = null): RedisCluster|bool|array;
 
-    public function zpopmin(string $key, int $value = null): bool|array;
+    public function zpopmin(string $key, int $value = null): RedisCluster|bool|array;
 
-    public function zrange(string $key, int $start, int $end, array $options = null): bool|array;
+    public function zrange(string $key, int $start, int $end, mixed $options_withscores = null): RedisCluster|array|bool;
 
-    public function zrangebylex(string $key, int $start, int $end, array $options = null): bool|array;
+    public function zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1): RedisCluster|array|false;
 
-    public function zrangebyscore(string $key, int $start, int $end, array $options = null): bool|array;
+    public function zrangebyscore(string $key, string $start, string $end, array $options = []): RedisCluster|array|false;
 
-    public function zrank(string $key, mixed $member): int;
+    public function zrank(string $key, mixed $member): RedisCluster|int|false;
 
-    public function zrem(string $key, string $value, string ...$other_values): int;
+    public function zrem(string $key, string $value, string ...$other_values): RedisCluster|int|false;
 
-    public function zremrangebylex(string $key, string $min, string $max): int;
+    public function zremrangebylex(string $key, string $min, string $max): RedisCluster|int|false;
 
-    public function zremrangebyrank(string $key, string $min, string $max): int;
+    public function zremrangebyrank(string $key, string $min, string $max): RedisCluster|int|false;
 
-    public function zremrangebyscore(string $key, string $min, string $max): int;
+    public function zremrangebyscore(string $key, string $min, string $max): RedisCluster|int|false;
 
-    public function zrevrange(string $key, string $min, string $max, array $options = null): bool|array;
+    public function zrevrange(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array;
 
-    public function zrevrangebylex(string $key, string $min, string $max, array $options = null): bool|array;
+    public function zrevrangebylex(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array;
 
-    public function zrevrangebyscore(string $key, string $min, string $max, array $options = null): bool|array;
+    public function zrevrangebyscore(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array;
 
-    public function zrevrank(string $key, mixed $member): int;
+    public function zrevrank(string $key, mixed $member): RedisCluster|int|false;
 
-    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array;
+    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): RedisCluster|bool|array;
 
-    public function zscore(string $key): float;
+    public function zscore(string $key, mixed $member): RedisCluster|float|false;
 
-    public function zunionstore(string $key, array $keys, array $weights = null, string $aggregate = null): int;
+    public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): RedisCluster|int|false;
 }
 
 class RedisClusterException extends RuntimeException {}
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 1052db1ef4..4b02f3e6d0 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 59682d20ee8ebad4f8a5c914432f41dac0860770 */
+ * Stub hash: 280323a9e3fc028641ad1d8bcba2514dfa90fac9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -47,40 +47,41 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_acl, 0, 2, IS
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_append, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_append, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_bgrewriteaof, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bgrewriteaof, 0, 1, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_bgsave arginfo_class_RedisCluster_bgrewriteaof
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_bitcount, 0, 1, MAY_BE_BOOL|MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitcount, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_bitop, 0, 3, MAY_BE_BOOL|MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitop, 0, 3, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, deskey, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, otherkeys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_bitpos, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitpos, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, bit, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "NULL")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_blpop, 0, 2, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_blpop, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
-	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL)
+	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_LONG, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
@@ -92,18 +93,22 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_brpoplpush, 0
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_bzpopmax arginfo_class_RedisCluster_blpop
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_bzpopmax, 0, 2, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_blpop
+#define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_bzpopmax
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bzmpop, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bzmpop, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmpop, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmpop, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1")
@@ -116,10 +121,10 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_clearlasterror, 0, 0, _IS_BOOL, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_client, 0, 3, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_client, 0, 2, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, arg, IS_STRING, 1)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_close arginfo_class_RedisCluster_clearlasterror
@@ -140,15 +145,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_config, 0, 2,
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_dbsize, 0, 1, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_dbsize, 0, 1, RedisCluster, MAY_BE_LONG)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_decr, 0, 1, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decr, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_decrby, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decrby, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
 ZEND_END_ARG_INFO()
@@ -158,18 +164,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_decrbyfloat,
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_del, 0, 1, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_del, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_discard arginfo_class_RedisCluster_clearlasterror
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_dump, 0, 1, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_dump, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_echo, 0, 2, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_echo, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO(0, msg, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -186,34 +192,38 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_evalsha, 0, 1
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_exec arginfo_class_RedisCluster__masters
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_exec, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_exists arginfo_class_RedisCluster_decr
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_exists, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_expire, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expire, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_expireat, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expireat, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiretime, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiretime, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_pexpiretime arginfo_class_RedisCluster_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_flushall, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_flushall, 0, 1, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, async, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 4, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geoadd, 0, 4, RedisCluster, MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
@@ -221,14 +231,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 4,
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_geodist, 0, 3, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geodist, 0, 3, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dest, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_geohash, 0, 2, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geohash, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
@@ -236,7 +246,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_geopos arginfo_class_RedisCluster_geohash
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_georadius, 0, 5, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_georadius, 0, 5, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
@@ -247,7 +257,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_georadius_ro arginfo_class_RedisCluster_georadius
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_georadiusbymember, 0, 4, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_georadiusbymember, 0, 4, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, radius, IS_DOUBLE, 0)
@@ -257,7 +267,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_georadiusbymember_ro arginfo_class_RedisCluster_georadiusbymember
 
-#define arginfo_class_RedisCluster_get arginfo_class_RedisCluster_dump
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_get, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_decrby
 
@@ -270,50 +282,50 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getoption, 0,
 	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getrange, 0, 3, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getrange, 0, 3, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lcs, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getset, 0, 2, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getset, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hdel, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hdel, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hexists, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hexists, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hget, 0, 2, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hget, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hgetall, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hgetall, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hincrby, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hincrby, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hincrbyfloat, 0, 3, IS_DOUBLE, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hincrbyfloat, 0, 3, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
@@ -321,14 +333,14 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster_hgetall
 
-#define arginfo_class_RedisCluster_hlen arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_hlen arginfo_class_RedisCluster_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hmget, 0, 2, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hmget, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, members, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hmset, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hmset, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -340,19 +352,19 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hset, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hset, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hsetnx, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hsetnx, 0, 3, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hstrlen, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hstrlen, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -363,100 +375,107 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_incrby arginfo_class_RedisCluster_decrby
 
-#define arginfo_class_RedisCluster_incrbyfloat arginfo_class_RedisCluster_decrbyfloat
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_incrbyfloat, 0, 2, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_info, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_info, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, section, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_keys, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_keys, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_lastsave, 0, 1, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lastsave, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lget, 0, 2, MAY_BE_STRING|MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lget, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_lindex arginfo_class_RedisCluster_lget
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_lindex, 0, 2, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_linsert, 0, 4, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_linsert, 0, 4, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, pos, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, pivot, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_llen, 0, 1, MAY_BE_LONG|MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_llen, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpop, 0, 1, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpop, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, MAY_BE_LONG|MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lpushx, 0, 2, MAY_BE_LONG|MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpushx, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_lrange, 0, 3, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lrange, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_lrem, 0, 3, MAY_BE_LONG|MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lrem, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_lset, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lset, 0, 3, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_ltrim, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_ltrim, 0, 3, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_mget, 0, 1, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_mget, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_mset, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_mset, 0, 1, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_msetnx, 0, 1, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_msetnx, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_multi, 0, 0, RedisCluster, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_object, 0, 2, MAY_BE_LONG|MAY_BE_STRING)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_object, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_persist, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_persist, 0, 1, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
@@ -464,24 +483,24 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_pexpireat arginfo_class_RedisCluster_expireat
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_pfadd, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_pfadd, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, elements, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_pfcount arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_pfcount arginfo_class_RedisCluster_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_pfmerge, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_pfmerge, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_ping, 0, 2, IS_MIXED, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_ping, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
-	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 1)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_psetex, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_psetex, 0, 3, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
@@ -492,9 +511,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_psubscribe, 0
 	ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_pttl arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_pttl arginfo_class_RedisCluster_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_publish, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_publish, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -509,7 +528,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_punsubscribe,
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_patterns, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_randomkey, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_randomkey, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_STRING)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 ZEND_END_ARG_INFO()
 
@@ -519,14 +538,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_rawcommand, 0
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_rename, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_rename, 0, 2, RedisCluster, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key_src, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, key_dst, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_renamenx, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, newkey, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_renamenx arginfo_class_RedisCluster_rename
-
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_restore, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_restore, 0, 3, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
@@ -539,25 +561,28 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_rpop arginfo_class_RedisCluster_lpop
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_rpoplpush, 0, 2, MAY_BE_BOOL|MAY_BE_STRING)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_rpoplpush, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_STRING)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_rpush, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_rpush, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, elements, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_rpushx, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_rpushx, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_sadd arginfo_class_RedisCluster_rpush
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sadd, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_saddarray, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_saddarray, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -571,125 +596,124 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_scan, 0, 2, M
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_scard arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_scard arginfo_class_RedisCluster_expiretime
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_script, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_sdiff arginfo_class_RedisCluster_del
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sdiff, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_sdiffstore, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sdiffstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_set, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_set, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setbit, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setbit, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, onoff, _IS_BOOL, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setex, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setex, 0, 3, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, expire, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_setnx arginfo_class_RedisCluster_setex
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setnx, 0, 2, RedisCluster, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setoption, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_setrange, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setrange, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_sinter arginfo_class_RedisCluster_del
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sinter, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sintercard, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sintercard, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_sinterstore, 0, 2, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster_sinterstore arginfo_class_RedisCluster_del
 
-#define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster_setnx
 
 #define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script
 
 #define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster_hgetall
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_smove, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_smove, 0, 3, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sort, 0, 2, MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_STRING)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_spop, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sort, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_STRING)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_srandmember, 0, 1, MAY_BE_STRING|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_spop, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_srem, 0, 2, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster_srandmember arginfo_class_RedisCluster_spop
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sscan, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+#define arginfo_class_RedisCluster_srem arginfo_class_RedisCluster_sadd
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sscan, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
-	ZEND_ARG_TYPE_INFO(0, node, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_strlen arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_strlen arginfo_class_RedisCluster_expiretime
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_subscribe, 0, 2, IS_VOID, 0)
 	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sunion, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sunion, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_sunionstore arginfo_class_RedisCluster_sdiffstore
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_time, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_time, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_ttl arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_ttl arginfo_class_RedisCluster_expiretime
 
-#define arginfo_class_RedisCluster_type arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_type arginfo_class_RedisCluster_expiretime
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_unsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
@@ -699,18 +723,18 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_unwatch arginfo_class_RedisCluster_clearlasterror
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_watch, 0, 1, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_watch, 0, 1, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xack, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xack, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xadd, 0, 3, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xadd, 0, 3, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, id, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0)
@@ -718,7 +742,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xadd, 0, 3, I
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xclaim, 0, 6, MAY_BE_STRING|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xclaim, 0, 6, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
@@ -727,7 +751,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xclaim, 0, 6,
 	ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xdel, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xdel, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -747,9 +771,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 1,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_xlen arginfo_class_RedisCluster_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xpending, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xpending, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_STRING, 1, "null")
@@ -758,20 +782,20 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xpending,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, consumer, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xrange, 0, 3, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xread, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xread, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_xreadgroup, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xreadgroup, 0, 3, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, streams, IS_ARRAY, 0)
@@ -789,66 +813,80 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xtrim, 0,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zadd, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zadd, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, score, IS_DOUBLE, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, score_or_options, MAY_BE_ARRAY|MAY_BE_DOUBLE, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, more_scores_and_mems, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_zcard arginfo_class_RedisCluster_decr
+#define arginfo_class_RedisCluster_zcard arginfo_class_RedisCluster_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zcount, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zcount, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zincrby, 0, 3, IS_DOUBLE, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zincrby, 0, 3, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zinterstore, 0, 2, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zinterstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_zintercard arginfo_class_RedisCluster_sintercard
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zlexcount, 0, 3, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zlexcount, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zpopmax, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zpopmax, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "null")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_zpopmin arginfo_class_RedisCluster_zpopmax
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrange, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options_withscores, IS_MIXED, 0, "null")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_zrangebylex arginfo_class_RedisCluster_zrange
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangebylex, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_zrangebyscore arginfo_class_RedisCluster_zrange
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangebyscore, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zrank, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrank, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_zrem arginfo_class_RedisCluster_srem
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrem, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_values, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_zremrangebylex arginfo_class_RedisCluster_zlexcount
 
@@ -856,7 +894,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_zremrangebyscore arginfo_class_RedisCluster_zlexcount
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zrevrange, 0, 3, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrevrange, 0, 3, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
@@ -869,18 +907,24 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_zrevrank arginfo_class_RedisCluster_zrank
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_zscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zscan, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_zscore, 0, 1, IS_DOUBLE, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zscore, 0, 2, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_zunionstore arginfo_class_RedisCluster_zinterstore
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zunionstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "NULL")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL")
+ZEND_END_ARG_INFO()
 
 
 ZEND_METHOD(RedisCluster, __construct);
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 3484a2dbfd..c1552291e3 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 59682d20ee8ebad4f8a5c914432f41dac0860770 */
+ * Stub hash: 280323a9e3fc028641ad1d8bcba2514dfa90fac9 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -55,6 +55,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitcount, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, bybit)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitop, 0, 0, 3)
@@ -108,7 +109,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_clearlasterror arginfo_class_RedisCluster__masters
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_client, 0, 0, 3)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_client, 0, 0, 2)
 	ZEND_ARG_INFO(0, node)
 	ZEND_ARG_INFO(0, subcommand)
 	ZEND_ARG_INFO(0, arg)
@@ -134,7 +135,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_dbsize arginfo_class_RedisCluster_bgrewriteaof
 
-#define arginfo_class_RedisCluster_decr arginfo_class_RedisCluster__prefix
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_decr, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, by)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_decrby arginfo_class_RedisCluster_append
 
@@ -168,7 +172,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_exec arginfo_class_RedisCluster__masters
 
-#define arginfo_class_RedisCluster_exists arginfo_class_RedisCluster__prefix
+#define arginfo_class_RedisCluster_exists arginfo_class_RedisCluster_del
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expire, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
@@ -286,7 +290,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hmget, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, members)
+	ZEND_ARG_INFO(0, keys)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hmset, 0, 0, 2)
@@ -312,7 +316,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster__prefix
 
-#define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster__prefix
+#define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr
 
 #define arginfo_class_RedisCluster_incrby arginfo_class_RedisCluster_append
 
@@ -362,10 +366,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_lrange arginfo_class_RedisCluster_getrange
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lrem, 0, 0, 3)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lrem, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, count)
 	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lset, 0, 0, 3)
@@ -386,7 +390,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_msetnx arginfo_class_RedisCluster_mset
 
-#define arginfo_class_RedisCluster_multi arginfo_class_RedisCluster__masters
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_multi, 0, 0, 0)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_object, 0, 0, 2)
 	ZEND_ARG_INFO(0, subcommand)
@@ -406,12 +412,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_pfcount arginfo_class_RedisCluster__prefix
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_pfmerge, 0, 0, 2)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, keys)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster_pfmerge arginfo_class_RedisCluster_hmget
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_ping, 0, 0, 2)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_ping, 0, 0, 1)
 	ZEND_ARG_INFO(0, key_or_address)
 	ZEND_ARG_INFO(0, message)
 ZEND_END_ARG_INFO()
@@ -453,12 +456,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rawcommand, 0, 0, 2)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rename, 0, 0, 2)
+	ZEND_ARG_INFO(0, key_src)
+	ZEND_ARG_INFO(0, key_dst)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_renamenx, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, newkey)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_renamenx arginfo_class_RedisCluster_rename
-
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_restore, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, timeout)
@@ -475,7 +481,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rpoplpush, 0, 0, 2)
 	ZEND_ARG_INFO(0, dst)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_rpush arginfo_class_RedisCluster_lpush
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_rpush, 0, 0, 1)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_VARIADIC_INFO(0, elements)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_rpushx arginfo_class_RedisCluster_append
 
@@ -510,7 +519,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sdiffstore, 0, 0, 2)
 	ZEND_ARG_VARIADIC_INFO(0, other_keys)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_set arginfo_class_RedisCluster_append
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_set, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setbit, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
@@ -520,11 +533,11 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setex, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, expire)
 	ZEND_ARG_INFO(0, value)
-	ZEND_ARG_INFO(0, timeout)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_setnx arginfo_class_RedisCluster_setex
+#define arginfo_class_RedisCluster_setnx arginfo_class_RedisCluster_append
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setoption, 0, 0, 2)
 	ZEND_ARG_INFO(0, option)
@@ -544,9 +557,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sintercard, 0, 0, 1)
 	ZEND_ARG_INFO(0, limit)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_sinterstore arginfo_class_RedisCluster_sdiffstore
+#define arginfo_class_RedisCluster_sinterstore arginfo_class_RedisCluster_del
 
-#define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster__prefix
+#define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster_append
 
 #define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script
 
@@ -558,24 +571,18 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_smove, 0, 0, 3)
 	ZEND_ARG_INFO(0, member)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sort, 0, 0, 2)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sort, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster__prefix
+#define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster_lpop
 
 #define arginfo_class_RedisCluster_srandmember arginfo_class_RedisCluster_lpop
 
 #define arginfo_class_RedisCluster_srem arginfo_class_RedisCluster_lpush
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sscan, 0, 0, 3)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(1, iterator)
-	ZEND_ARG_INFO(0, node)
-	ZEND_ARG_INFO(0, pattern)
-	ZEND_ARG_INFO(0, count)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster_sscan arginfo_class_RedisCluster_hscan
 
 #define arginfo_class_RedisCluster_strlen arginfo_class_RedisCluster__prefix
 
@@ -689,11 +696,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xtrim, 0, 0, 2)
 	ZEND_ARG_INFO(0, limit)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zadd, 0, 0, 3)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zadd, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, score)
-	ZEND_ARG_INFO(0, member)
-	ZEND_ARG_VARIADIC_INFO(0, extra_args)
+	ZEND_ARG_INFO(0, score_or_options)
+	ZEND_ARG_VARIADIC_INFO(0, more_scores_and_mems)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_zcard arginfo_class_RedisCluster__prefix
@@ -707,7 +713,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zincrby, 0, 0, 3)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zinterstore, 0, 0, 2)
-	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, dst)
 	ZEND_ARG_INFO(0, keys)
 	ZEND_ARG_INFO(0, weights)
 	ZEND_ARG_INFO(0, aggregate)
@@ -732,12 +738,23 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrange, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
-	ZEND_ARG_INFO(0, options)
+	ZEND_ARG_INFO(0, options_withscores)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_zrangebylex arginfo_class_RedisCluster_zrange
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, min)
+	ZEND_ARG_INFO(0, max)
+	ZEND_ARG_INFO(0, offset)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster_zrangebyscore arginfo_class_RedisCluster_zrange
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebyscore, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_zrank arginfo_class_RedisCluster_hexists
 
@@ -764,7 +781,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_zscan arginfo_class_RedisCluster_hscan
 
-#define arginfo_class_RedisCluster_zscore arginfo_class_RedisCluster__prefix
+#define arginfo_class_RedisCluster_zscore arginfo_class_RedisCluster_hexists
 
 #define arginfo_class_RedisCluster_zunionstore arginfo_class_RedisCluster_zinterstore
 
diff --git a/redis_commands.c b/redis_commands.c
index 098b3a9d44..3c56f892af 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -623,7 +623,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     zend_string *zkey;
     zval *z_ws = NULL, *z_ele;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|z", &key, &key_len,
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|z!", &key, &key_len,
                              &start, &end, &z_ws) == FAILURE)
     {
         return FAILURE;
@@ -1861,10 +1861,10 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         ZEND_HASH_FOREACH_STR_KEY_VAL(kt, zkey, v) {
             ZVAL_DEREF(v);
             /* Detect PX or EX argument and validate timeout */
-            if (zkey && (ZSTR_STRICMP_STATIC(zkey, "EX") ||
-                         ZSTR_STRICMP_STATIC(zkey, "PX") ||
-                         ZSTR_STRICMP_STATIC(zkey, "EXAT") ||
-                         ZSTR_STRICMP_STATIC(zkey, "PXAT"))
+            if (zkey && (zend_string_equals_literal_ci(zkey, "EX") ||
+                         zend_string_equals_literal_ci(zkey, "PX") ||
+                         zend_string_equals_literal_ci(zkey, "EXAT") ||
+                         zend_string_equals_literal_ci(zkey, "PXAT"))
             ) {
                 exp_set = 1;
 
@@ -1878,11 +1878,13 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     expire = atol(Z_STRVAL_P(v));
                 }
             } else if (Z_TYPE_P(v) == IS_STRING) {
-                if (ZVAL_STRICMP_STATIC(v, "KEEPTTL")) {
+                if (zend_string_equals_literal_ci(Z_STR_P(v), "KEEPTTL")) {
                     keep_ttl  = 1;
-                } else if (ZVAL_STRICMP_STATIC((v), "GET")) {
+                } else if (zend_string_equals_literal_ci(Z_STR_P(v), "GET")) {
                     get = 1;
-                } else if (ZVAL_STRICMP_STATIC(v, "NX") || ZVAL_STRICMP_STATIC(v, "XX")) {
+                } else if (zend_string_equals_literal_ci(Z_STR_P(v), "NX") ||
+                           zend_string_equals_literal_ci(Z_STR_P(v), "XX"))
+                {
                     set_type = Z_STRVAL_P(v);
                 }
             }
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 507c010eb7..66a63b32c2 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 6d0479328ae627b9c45104a52014b3649e533015 */
+ * Stub hash: 1810caef11b38440e073059e2d9c65f92fa8a9a5 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 2ba985092f..6df481d538 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -390,7 +390,7 @@ public function testFailedTransactions() {
         // watch and unwatch
         $this->redis->watch('x');
         $r->incr('x'); // other instance
-        $this->redis->unwatch('x'); // cancel transaction watch
+        $this->redis->unwatch(); // cancel transaction watch
 
         // This should succeed as the watch has been cancelled
         $ret = $this->redis->multi()->get('x')->exec();

From 5ac92d25f1af906da72dba9331a6b6b4d54a9911 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 12 Oct 2022 19:57:42 -0700
Subject: [PATCH 1644/1986] Add missing directed node command to docs and
 refactor stubs.

* Add every "directed node" command we could find to the relevant
  section in cluster.markdown.

* Refactor the stubs so every node argument is named the same thing
  ($key_or_address) and has the same data type (string|array).

Fixes #1840
---
 cluster.markdown               | 38 ++++++++++++++++++++--------------
 redis_cluster.stub.php         | 18 ++++++++--------
 redis_cluster_arginfo.h        | 18 ++++++++--------
 redis_cluster_legacy_arginfo.h | 20 ++++++++----------
 4 files changed, 49 insertions(+), 45 deletions(-)

diff --git a/cluster.markdown b/cluster.markdown
index f3306b9915..96d0fa4320 100644
--- a/cluster.markdown
+++ b/cluster.markdown
@@ -145,22 +145,28 @@ foreach ($obj_cluster->_masters() as $arr_master) {
 
 In the case of all commands which need to be directed at a node, the calling convention is identical to the Redis call, except that they require an additional (first) argument in order to deliver the command.  Following is a list of each of these commands:
 
-1.  SAVE
-2.  BGSAVE
-3.  FLUSHDB
-4.  FLUSHALL
-5.  DBSIZE
-6.  BGREWRITEAOF
-7.  LASTSAVE
-8.  INFO
-9.  CLIENT
-10.  CLUSTER
-11.  CONFIG
-12.  PUBSUB
-13.  SLOWLOG
-14.  RANDOMKEY
-15.  PING
-16.  SCAN
+1. ACL
+1. BGREWRITEAOF
+1. BGSAVE
+1. CLIENT
+1. CLUSTER
+1. CONFIG
+1. DBSIZE
+1. ECHO
+1. FLUSHALL
+1. FLUSHDB
+1. INFO
+1. LASTSAVE
+1. PING
+1. PUBSUB
+1. RANDOMKEY
+1. RAWCOMMAND
+1. ROLE
+1. SAVE
+1. SCAN
+1. SCRIPT
+1. SLOWLOG
+1. TIME
 
 ## Session Handler
 You can use the cluster functionality of phpredis to store PHP session information in a Redis cluster as you can with a non cluster-enabled Redis instance.
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 517ca4c8e1..90347c9aed 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -61,15 +61,15 @@ public function lmpop(array $keys, string $from, int $count = 1): RedisCluster|a
 
     public function clearlasterror(): bool;
 
-    public function client(string|array $node, string $subcommand, ?string $arg = NULL): array|string|bool;
+    public function client(string|array $key_or_address, string $subcommand, ?string $arg = NULL): array|string|bool;
 
     public function close(): bool;
 
-    public function cluster(string|array $node, string $command, mixed ...$extra_args): mixed;
+    public function cluster(string|array $key_or_address, string $command, mixed ...$extra_args): mixed;
 
     public function command(mixed ...$extra_args): mixed;
 
-    public function config(string|array $node, string $subcommand, mixed ...$extra_args): mixed;
+    public function config(string|array $key_or_address, string $subcommand, mixed ...$extra_args): mixed;
 
     public function dbsize(string|array $key_or_address): RedisCluster|int;
 
@@ -85,7 +85,7 @@ public function discard(): bool;
 
     public function dump(string $key): RedisCluster|string|false;
 
-    public function echo(string|array $node, string $msg): RedisCluster|string|false;
+    public function echo(string|array $key_or_address, string $msg): RedisCluster|string|false;
 
     public function eval(string $script, array $args = [], int $num_keys = 0): mixed;
 
@@ -103,9 +103,9 @@ public function expiretime(string $key): RedisCluster|int|false;
 
     public function pexpiretime(string $key): RedisCluster|int|false;
 
-    public function flushall(string|array $node, bool $async = false): RedisCluster|bool;
+    public function flushall(string|array $key_or_address, bool $async = false): RedisCluster|bool;
 
-    public function flushdb(string|array $node, bool $async = false): RedisCluster|bool;
+    public function flushdb(string|array $key_or_address, bool $async = false): RedisCluster|bool;
 
     public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): RedisCluster|int;
 
@@ -175,11 +175,11 @@ public function incrby(string $key, int $value): RedisCluster|int|false;
 
     public function incrbyfloat(string $key, float $value): RedisCluster|float|false;
 
-    public function info(string|array $node, ?string $section = null): RedisCluster|array|false;
+    public function info(string|array $key_or_address, ?string $section = null): RedisCluster|array|false;
 
     public function keys(string $pattern): RedisCluster|array|false;
 
-    public function lastsave(string|array $node): RedisCluster|int|false;
+    public function lastsave(string|array $key_or_address): RedisCluster|int|false;
 
     public function lget(string $key, int $index): RedisCluster|string|bool;
 
@@ -268,7 +268,7 @@ public function saddarray(string $key, array $values): RedisCluster|bool|int;
 
     public function save(string|array $key_or_address): RedisCluster|bool;
 
-    public function scan(?int &$iterator, mixed $node, ?string $pattern = null, int $count = 0): bool|array;
+    public function scan(?int &$iterator, string|array $key_or_address, ?string $pattern = null, int $count = 0): bool|array;
 
     public function scard(string $key): RedisCluster|int|false;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 4b02f3e6d0..930ed8e658 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 280323a9e3fc028641ad1d8bcba2514dfa90fac9 */
+ * Stub hash: c6326ac0f4a1dc7b6fe920a7358010f1a570832a */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -122,7 +122,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_clearlasterro
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_client, 0, 2, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL)
-	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
@@ -130,7 +130,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_close arginfo_class_RedisCluster_clearlasterror
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_cluster, 0, 2, IS_MIXED, 0)
-	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
@@ -140,7 +140,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_command, 0, 0
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_config, 0, 2, IS_MIXED, 0)
-	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
@@ -176,7 +176,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_dump, 0,
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_echo, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO(0, msg, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
@@ -217,7 +217,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_pexpiretime arginfo_class_RedisCluster_expiretime
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_flushall, 0, 1, RedisCluster, MAY_BE_BOOL)
-	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, async, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
@@ -381,7 +381,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_incrbyflo
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_info, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, section, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
@@ -390,7 +390,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_keys, 0,
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lastsave, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_MASK(0, node, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lget, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL)
@@ -591,7 +591,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
-	ZEND_ARG_TYPE_INFO(0, node, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index c1552291e3..9fde501cf6 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 280323a9e3fc028641ad1d8bcba2514dfa90fac9 */
+ * Stub hash: c6326ac0f4a1dc7b6fe920a7358010f1a570832a */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -110,7 +110,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_clearlasterror arginfo_class_RedisCluster__masters
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_client, 0, 0, 2)
-	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, key_or_address)
 	ZEND_ARG_INFO(0, subcommand)
 	ZEND_ARG_INFO(0, arg)
 ZEND_END_ARG_INFO()
@@ -118,7 +118,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_close arginfo_class_RedisCluster__masters
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_cluster, 0, 0, 2)
-	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, key_or_address)
 	ZEND_ARG_INFO(0, command)
 	ZEND_ARG_VARIADIC_INFO(0, extra_args)
 ZEND_END_ARG_INFO()
@@ -128,7 +128,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_command, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_config, 0, 0, 2)
-	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, key_or_address)
 	ZEND_ARG_INFO(0, subcommand)
 	ZEND_ARG_VARIADIC_INFO(0, extra_args)
 ZEND_END_ARG_INFO()
@@ -154,7 +154,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_dump arginfo_class_RedisCluster__prefix
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_echo, 0, 0, 2)
-	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, key_or_address)
 	ZEND_ARG_INFO(0, msg)
 ZEND_END_ARG_INFO()
 
@@ -189,7 +189,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_pexpiretime arginfo_class_RedisCluster__prefix
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_flushall, 0, 0, 1)
-	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, key_or_address)
 	ZEND_ARG_INFO(0, async)
 ZEND_END_ARG_INFO()
 
@@ -323,7 +323,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_RedisCluster_incrbyfloat arginfo_class_RedisCluster_append
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_info, 0, 0, 1)
-	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, key_or_address)
 	ZEND_ARG_INFO(0, section)
 ZEND_END_ARG_INFO()
 
@@ -331,9 +331,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_keys, 0, 0, 1)
 	ZEND_ARG_INFO(0, pattern)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lastsave, 0, 0, 1)
-	ZEND_ARG_INFO(0, node)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster_lastsave arginfo_class_RedisCluster_bgrewriteaof
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lget, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
@@ -499,7 +497,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_scan, 0, 0, 2)
 	ZEND_ARG_INFO(1, iterator)
-	ZEND_ARG_INFO(0, node)
+	ZEND_ARG_INFO(0, key_or_address)
 	ZEND_ARG_INFO(0, pattern)
 	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()

From d05d301b5af008a495147d55cb7a7e0e7c26b6fd Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 12 Oct 2022 19:38:38 -0700
Subject: [PATCH 1645/1986] Better unix:// or file:// detection.

Fixes #1836
---
 redis.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/redis.c b/redis.c
index f68227fdfe..3a3370bfac 100644
--- a/redis.c
+++ b/redis.c
@@ -694,6 +694,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
     size_t host_len, persistent_id_len;
     double timeout = 0.0, read_timeout = 0.0;
     redis_object *redis;
+    int af_unix;
 
 #ifdef ZTS
     /* not sure how in threaded mode this works so disabled persistence at
@@ -730,8 +731,13 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
         return FAILURE;
     }
 
+    /* Does the host look like a unix socket */
+    af_unix = (host_len > 0 && host[0] == '/') ||
+              (host_len > 6 && !strncasecmp(host, "unix://", sizeof("unix://") - 1)) ||
+              (host_len > 6 && !strncasecmp(host, "file://", sizeof("file://") - 1));
+
     /* If it's not a unix socket, set to default */
-    if(port == -1 && host_len && host[0] != '/') {
+    if (port == -1 && !af_unix) {
         port = 6379;
     }
 

From 872ae1079f8a3f61153e9aa74ab95ba35b706e5b Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 13 Oct 2022 10:51:28 -0700
Subject: [PATCH 1646/1986] Implement Redis 7.0.0 [P]EXPIRE[AT] options

See #2068
---
 README.markdown                | 34 +++++++++++----------
 redis.c                        |  8 ++---
 redis.stub.php                 | 54 ++++++++++++++++++++++++++++++----
 redis_arginfo.h                |  8 +++--
 redis_cluster.c                |  8 ++---
 redis_cluster.stub.php         |  8 ++---
 redis_cluster_arginfo.h        |  4 ++-
 redis_cluster_legacy_arginfo.h |  4 ++-
 redis_commands.c               | 36 +++++++++++++++++++++++
 redis_commands.h               |  4 +++
 redis_legacy_arginfo.h         |  4 ++-
 tests/RedisTest.php            | 33 +++++++++++++++++++++
 12 files changed, 167 insertions(+), 38 deletions(-)

diff --git a/README.markdown b/README.markdown
index 2106f96e93..68cb9029a9 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1098,17 +1098,18 @@ $redis->get('x'); 	// → `FALSE`
 -----
 _**Description**_: Same as rename, but will not replace a key if the destination already exists. This is the same behaviour as setNx.
 
-### expire, setTimeout, pexpire
+### expire, pexpire
 -----
-_**Description**_: Sets an expiration date (a timeout) on an item. pexpire requires a TTL in milliseconds.
+_**Description**_: Sets an expiration on a key in either seconds or milliseconds.
 
-##### *Parameters*
-*Key*: key. The key that will disappear.
-
-*Integer*: ttl. The key's remaining Time To Live, in seconds.
+##### *Prototype*
+~~~php
+public function expire(string $key, int $seconds, ?string $mode = NULL): Redis|bool;
+public function pexpire(string $key, int $milliseconds, ?string $mode = NULL): Redis|bool;
+~~~
 
 ##### *Return value*
-*BOOL*: `TRUE` in case of success, `FALSE` in case of failure.
+*BOOL*: `TRUE` if an expiration was set, and `FALSE` on failure or if one was not set.  You can distinguish between an error and an expiration not being set by checking `getLastError()`.
 ##### *Example*
 ~~~php
 $redis->set('x', '42');
@@ -1121,22 +1122,23 @@ $redis->get('x'); 		// will return `FALSE`, as 'x' has expired.
 
 ### expireAt, pexpireAt
 -----
-_**Description**_: Sets an expiration date (a timestamp) on an item. pexpireAt requires a timestamp in milliseconds.
-
-##### *Parameters*
-*Key*: key. The key that will disappear.
+_**Description**_: Seta specific timestamp for a key to expire in seconds or milliseconds.
 
-*Integer*: Unix timestamp. The key's date of death, in seconds from Epoch time.
+##### *Prototype*
+~~~php
+public function expireat(string $key, int $unix_timestamp, ?string $mode = NULL): Redis|bool;
+public function pexpireat(string $key, int $unix_timestamp_millis, ?string $mode = NULL): Redis|bool;
+~~~
 
 ##### *Return value*
-*BOOL*: `TRUE` in case of success, `FALSE` in case of failure.
+*BOOL*: `TRUE` if an expiration was set and `FALSE` if one was not set or in the event on an error.  You can detect an actual error by checking `getLastError()`.
+
 ##### *Example*
 ~~~php
 $redis->set('x', '42');
-$now = time(NULL); // current timestamp
-$redis->expireAt('x', $now + 3);	// x will disappear in 3 seconds.
+$redis->expireAt('x', time(NULL) + 3); // x will disappear in 3 seconds.
 sleep(5);				// wait 5 seconds
-$redis->get('x'); 		// will return `FALSE`, as 'x' has expired.
+$redis->get('x'); 	// will return `FALSE`, as 'x' has expired.
 ~~~
 
 ### keys, getKeys
diff --git a/redis.c b/redis.c
index 3a3370bfac..36b0cf186e 100644
--- a/redis.c
+++ b/redis.c
@@ -1635,25 +1635,25 @@ PHP_METHOD(Redis, sortDescAlpha)
 
 /* {{{ proto array Redis::expire(string key, int timeout) */
 PHP_METHOD(Redis, expire) {
-    REDIS_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, redis_1_response);
+    REDIS_PROCESS_KW_CMD("EXPIRE", redis_expire_cmd, redis_1_response);
 }
 /* }}} */
 
 /* {{{ proto bool Redis::pexpire(string key, long ms) */
 PHP_METHOD(Redis, pexpire) {
-    REDIS_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, redis_1_response);
+    REDIS_PROCESS_KW_CMD("PEXPIRE", redis_expire_cmd, redis_1_response);
 }
 /* }}} */
 
 /* {{{ proto array Redis::expireAt(string key, int timestamp) */
 PHP_METHOD(Redis, expireAt) {
-    REDIS_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, redis_1_response);
+    REDIS_PROCESS_KW_CMD("EXPIREAT", redis_expire_cmd, redis_1_response);
 }
 /* }}} */
 
 /* {{{ proto array Redis::pexpireAt(string key, int timestamp) */
 PHP_METHOD(Redis, pexpireAt) {
-    REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, redis_1_response);
+    REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_expire_cmd, redis_1_response);
 }
 /* }}} */
 
diff --git a/redis.stub.php b/redis.stub.php
index 8cdede9564..804b0cb54f 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -105,9 +105,32 @@ public function exec(): Redis|array|false;
 
     public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool;
 
-    public function expire(string $key, int $timeout): Redis|bool;
+    /**
+       Sets an expiration in seconds on the key in question.  If connected to
+       redis-server >= 7.0.0 you may send an additional "mode" argument which
+       modifies how the command will execute.
+
+       @param string  $key  The key to set an expiration on.
+       @param ?string $mode A two character modifier that changes how the
+                            command works.
+                            NX - Set expiry only if key has no expiry
+                            XX - Set expiry only if key has an expiry
+                            LT - Set expiry only when new expiry is < current expiry
+                            GT - Set expiry only when new expiry is > current expiry
+     */
+    public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|bool;
+
+    /**
+      Set a key's expiration to a specific unix timestamp in seconds.  If
+      connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.
 
-    public function expireAt(string $key, int $timestamp): Redis|bool;
+      @see Redis::expire() For a description of the mode argument.
+
+       @param string  $key  The key to set an expiration on.
+       @param ?string $mode A two character modifier that changes how the
+                            command works.
+     */
+    public function expireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool;
 
     public function failover(?array $to = null, bool $abort = false, int $timeout = 0): Redis|bool;
 
@@ -313,11 +336,32 @@ public function open(string $host, int $port = 6379, float $timeout = 0, string
 
     public function pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
 
-public function persist(string $key): bool;
+    public function persist(string $key): bool;
+
+    /**
+       Sets an expiration in milliseconds on a given key.  If connected to
+       Redis >= 7.0.0 you can pass an optional mode argument that modifies
+       how the command will execute.
+
+       @see Redis::expire() for a description of the mode argument.
 
-    public function pexpire(string $key, int $timeout): bool;
+       @param string  $key  The key to set an expiration on.
+       @param ?string $mode A two character modifier that changes how the
+                            command works.
+     */
+    public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool;
 
-    public function pexpireAt(string $key, int $timestamp): bool;
+    /**
+      Set a key's expiration to a specific unix timestamp in milliseconds.  If
+      connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.
+
+      @see Redis::expire() For a description of the mode argument.
+
+       @param string  $key  The key to set an expiration on.
+       @param ?string $mode A two character modifier that changes how the
+                            command works.
+     */
+    public function pexpireAt(string $key, int $timestamp, ?string $mode = NULL): bool;
 
     public function pfadd(string $key, array $elements): int;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 37241bbf0c..3c711f3aba 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 1810caef11b38440e073059e2d9c65f92fa8a9a5 */
+ * Stub hash: b53146f6b329f404b4bfa9e5df9dde9c36b50440 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -201,11 +201,13 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expire, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expireAt, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_failover, 0, 0, Redis, MAY_BE_BOOL)
@@ -287,7 +289,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geosearchstore,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_get, 0, 1, Redis, MAY_BE_ANY|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_get, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
@@ -576,11 +578,13 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpire, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpireAt, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfadd, 0, 2, IS_LONG, 0)
diff --git a/redis_cluster.c b/redis_cluster.c
index cdd730cefa..0a701ed8bb 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1252,24 +1252,24 @@ PHP_METHOD(RedisCluster, decrbyfloat) {
 
 /* {{{ proto bool RedisCluster::expire(string key, long sec) */
 PHP_METHOD(RedisCluster, expire) {
-    CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, cluster_1_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_expire_cmd, cluster_1_resp, 0);
 }
 /* }}} */
 
 /* {{{ proto bool RedisCluster::expireat(string key, long ts) */
 PHP_METHOD(RedisCluster, expireat) {
-    CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, cluster_1_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_expire_cmd, cluster_1_resp, 0);
 }
 
 /* {{{ proto bool RedisCluster::pexpire(string key, long ms) */
 PHP_METHOD(RedisCluster, pexpire) {
-    CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, cluster_1_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_expire_cmd, cluster_1_resp, 0);
 }
 /* }}} */
 
 /* {{{ proto bool RedisCluster::pexpireat(string key, long ts) */
 PHP_METHOD(RedisCluster, pexpireat) {
-    CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, cluster_1_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_expire_cmd, cluster_1_resp, 0);
 }
 /* }}} */
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 90347c9aed..485b5479a4 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -95,9 +95,9 @@ public function exec(): array|false;
 
     public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
 
-    public function expire(string $key, int $timeout): RedisCluster|bool;
+    public function expire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool;
 
-    public function expireat(string $key, int $timestamp): RedisCluster|bool;
+    public function expireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
 
     public function expiretime(string $key): RedisCluster|int|false;
 
@@ -218,9 +218,9 @@ public function object(string $subcommand, string $key): RedisCluster|int|string
 
     public function persist(string $key): RedisCluster|bool;
 
-    public function pexpire(string $key, int $timeout): RedisCluster|bool;
+    public function pexpire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool;
 
-    public function pexpireat(string $key, int $timestamp): RedisCluster|bool;
+    public function pexpireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
 
     public function pfadd(string $key, array $elements): RedisCluster|bool;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 930ed8e658..1775ca200e 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c6326ac0f4a1dc7b6fe920a7358010f1a570832a */
+ * Stub hash: 6b41f3c801e587509bc5c7cab302dc6c1e0f7d56 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -203,11 +203,13 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expire, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expireat, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiretime, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 9fde501cf6..d9c82913f6 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c6326ac0f4a1dc7b6fe920a7358010f1a570832a */
+ * Stub hash: 6b41f3c801e587509bc5c7cab302dc6c1e0f7d56 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -177,11 +177,13 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expire, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, mode)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expireat, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, timestamp)
+	ZEND_ARG_INFO(0, mode)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_expiretime arginfo_class_RedisCluster__prefix
diff --git a/redis_commands.c b/redis_commands.c
index 3c56f892af..3a49e48a70 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5724,6 +5724,42 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+// [P]EXPIRE[AT] [NX | XX | GT | LT]
+int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                     char *kw, char **cmd, int *cmd_len, short *slot,
+                     void **ctx)
+{
+    zend_string *key = NULL, *mode = NULL;
+    smart_string cmdstr = {0};
+    zend_long timeout = 0;
+
+    ZEND_PARSE_PARAMETERS_START(2, 3)
+        Z_PARAM_STR(key)
+        Z_PARAM_LONG(timeout)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_STR(mode)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    if (mode != NULL && !(zend_string_equals_literal_ci(mode, "NX") ||
+                          zend_string_equals_literal_ci(mode, "XX") ||
+                          zend_string_equals_literal_ci(mode, "LT") ||
+                          zend_string_equals_literal_ci(mode, "GT")))
+    {
+        php_error_docref(NULL, E_WARNING, "Unknown expiration modifier '%s'", ZSTR_VAL(mode));
+        return FAILURE;
+    }
+
+    redis_cmd_init_sstr(&cmdstr, 2 + (mode != NULL), kw, strlen(kw));
+    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
+    redis_cmd_append_sstr_long(&cmdstr, timeout);
+    if (mode != NULL) redis_cmd_append_sstr_zstr(&cmdstr, mode);
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 int
 redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index a93be4a17b..ac63bc4113 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -354,6 +354,10 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                     char *kw, char **cmd, int *cmd_len, short *slot,
+                     void **ctx);
+
 int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 66a63b32c2..3fbd3c4f61 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 1810caef11b38440e073059e2d9c65f92fa8a9a5 */
+ * Stub hash: b53146f6b329f404b4bfa9e5df9dde9c36b50440 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -181,11 +181,13 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expire, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, mode)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expireAt, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, timestamp)
+	ZEND_ARG_INFO(0, mode)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_failover, 0, 0, 0)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index e2c8b55942..1e82920c8c 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -703,6 +703,39 @@ public function testExpireAt() {
         $this->assertTrue($success);
     }
 
+    function testExpireOptions() {
+        if (!$this->minVersionCheck('7.0.0'))
+            return;
+
+        $this->redis->set('eopts', 'value');
+
+        /* NX -- Only if expiry isn't set so success, then failure */
+        $this->assertTrue($this->redis->expire('eopts', 1000, 'NX'));
+        $this->assertFalse($this->redis->expire('eopts', 1000, 'NX'));
+
+        /* XX -- Only set if the key has an existing expiry */
+        $this->assertTrue($this->redis->expire('eopts', 1000, 'XX'));
+        $this->assertTrue($this->redis->persist('eopts'));
+        $this->assertFalse($this->redis->expire('eopts', 1000, 'XX'));
+
+        /* GT -- Only set when new expiry > current expiry */
+        $this->assertTrue($this->redis->expire('eopts', 200));
+        $this->assertTrue($this->redis->expire('eopts', 300, 'GT'));
+        $this->assertFalse($this->redis->expire('eopts', 100, 'GT'));
+
+        /* LT -- Only set when expiry < current expiry */
+        $this->assertTrue($this->redis->expire('eopts', 200));
+        $this->assertTrue($this->redis->expire('eopts', 100, 'LT'));
+        $this->assertFalse($this->redis->expire('eopts', 300, 'LT'));
+
+        /* Sending a nonsensical mode fails without sending a command */
+        $this->redis->clearLastError();
+        $this->assertFalse(@$this->redis->expire('eopts', 999, 'nonsense'));
+        $this->assertEquals(NULL, $this->redis->getLastError());
+
+        $this->redis->del('eopts');
+    }
+
     public function testExpiretime() {
         if(version_compare($this->version, "7.0.0") < 0) {
             $this->markTestSkipped();

From 4d8afd387ed2c8685a92f046ac84467a00199db8 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 13 Oct 2022 11:36:48 -0700
Subject: [PATCH 1647/1986] Refactor BITPOS and implement BIT/BYTE option.

Update BITPOS to use the new argument parsing macros as well as
implement Redis 7.0.0's BIT/BYTE modifier.

Additionally I changed `int $bit` to `bool $bit` as its a more
appropriate datatype.

See #2068
---
 redis.stub.php                 | 14 +++++++++-
 redis_arginfo.h                |  5 ++--
 redis_cluster.stub.php         | 14 +++++++++-
 redis_cluster_arginfo.h        | 11 ++++----
 redis_cluster_legacy_arginfo.h |  3 +-
 redis_commands.c               | 51 ++++++++++++++++++----------------
 redis_legacy_arginfo.h         |  3 +-
 tests/RedisTest.php            |  8 ++++++
 8 files changed, 74 insertions(+), 35 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 804b0cb54f..eb3f53132a 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -41,7 +41,19 @@ public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit
 
     public function bitop(string $operation, string $deskey, string $srckey, string ...$other_keys): Redis|int|false;
 
-    public function bitpos(string $key, int $bit, int $start = 0, int $end = -1): Redis|int|false;
+    /**
+      Return the position of the first bit set to 0 or 1 in a string.
+
+      @see https://https://redis.io/commands/bitpos/
+
+      @param string $key   The key to check (must be a string)
+      @param bool   $bit   Whether to look for an unset (0) or set (1) bit.
+      @param int    $start Where in the string to start looking.
+      @param int    $end   Where in the string to stop looking.
+      @param bool   $bybit If true, Redis will treat $start and $end as BIT values and not bytes, so if start
+                           was 0 and end was 2, Redis would only search the first two bits.
+     */
+    public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false;
 
     public function blPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 3c711f3aba..ea38af2eed 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b53146f6b329f404b4bfa9e5df9dde9c36b50440 */
+ * Stub hash: a8ddcde8e8af5201d72bfe8b463afc0c9dafb73d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -65,9 +65,10 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitpos, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, bit, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, bit, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_blPop, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 485b5479a4..05a44ccdee 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -40,7 +40,19 @@ public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit
 
     public function bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys): RedisCluster|bool|int;
 
-    public function bitpos(string $key, int $bit, int $start = NULL, int $end = NULL): RedisCluster|bool|int;
+    /**
+      Return the position of the first bit set to 0 or 1 in a string.
+
+      @see https://https://redis.io/commands/bitpos/
+
+      @param string $key   The key to check (must be a string)
+      @param bool   $bit   Whether to look for an unset (0) or set (1) bit.
+      @param int    $start Where in the string to start looking.
+      @param int    $end   Where in the string to stop looking.
+      @param bool   $bybit If true, Redis will treat $start and $end as BIT values and not bytes, so if start
+                           was 0 and end was 2, Redis would only search the first two bits.
+     */
+    public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): RedisCluster|int|false;
 
     public function blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): RedisCluster|array|null|false;
     public function brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): RedisCluster|array|null|false;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 1775ca200e..7578b03206 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 6b41f3c801e587509bc5c7cab302dc6c1e0f7d56 */
+ * Stub hash: ccb418a312ff22f03e2354de508ff8bd9e4d266e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -72,11 +72,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitop, 0,
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, otherkeys, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitpos, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitpos, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, bit, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "NULL")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "NULL")
+	ZEND_ARG_TYPE_INFO(0, bit, _IS_BOOL, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bybit, _IS_BOOL, 0, "false")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_blpop, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index d9c82913f6..6e392c7243 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 6b41f3c801e587509bc5c7cab302dc6c1e0f7d56 */
+ * Stub hash: ccb418a312ff22f03e2354de508ff8bd9e4d266e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -70,6 +70,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitpos, 0, 0, 2)
 	ZEND_ARG_INFO(0, bit)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, bybit)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_blpop, 0, 0, 2)
diff --git a/redis_commands.c b/redis_commands.c
index 3a49e48a70..e415ae5c1e 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2523,38 +2523,41 @@ int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-/* BITPOS */
+/* BITPOS key bit [start [end [BYTE | BIT]]] */
 int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                      char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    char *key;
-    int argc;
-    zend_long bit, start, end;
-    size_t key_len;
+    zend_long start = 0, end = -1;
+    zend_bool bit = 0, bybit = 0;
+    smart_string cmdstr = {0};
+    zend_string *key = NULL;
 
-    argc = ZEND_NUM_ARGS();
-    if (zend_parse_parameters(argc, "sl|ll", &key, &key_len, &bit,
-                             &start, &end) == FAILURE)
-    {
-        return FAILURE;
-    }
+    ZEND_PARSE_PARAMETERS_START(2, 5)
+        Z_PARAM_STR(key)
+        Z_PARAM_BOOL(bit)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_LONG(start)
+        Z_PARAM_LONG(end)
+        Z_PARAM_BOOL(bybit)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
 
-    // Prevalidate bit
-    if (bit != 0 && bit != 1) {
-        return FAILURE;
-    }
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (ZEND_NUM_ARGS() > 2 ? 2 : 0) + !!bybit, "BITPOS");
 
-    // Construct command based on arg count
-    if (argc == 2) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITPOS", "kd", key, key_len, bit);
-    } else if (argc == 3) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITPOS", "kdd", key, key_len, bit,
-                                     start);
-    } else {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITPOS", "kddd", key, key_len, bit,
-                                     start, end);
+    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
+    redis_cmd_append_sstr_long(&cmdstr, bit);
+
+    /* Start and length if we were passed either */
+    if (ZEND_NUM_ARGS() > 2) {
+        redis_cmd_append_sstr_long(&cmdstr, start);
+        redis_cmd_append_sstr_long(&cmdstr, end);
     }
 
+    /* Finally, BIT or BYTE if we were passed that argument */
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, !!bybit, "BIT");
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
     return SUCCESS;
 }
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 3fbd3c4f61..5c8642d9cc 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b53146f6b329f404b4bfa9e5df9dde9c36b50440 */
+ * Stub hash: a8ddcde8e8af5201d72bfe8b463afc0c9dafb73d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -63,6 +63,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
 	ZEND_ARG_INFO(0, bit)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, bybit)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_blPop, 0, 0, 2)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 1e82920c8c..bcef3e6277 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -409,6 +409,14 @@ public function testBitPos() {
 
         $this->redis->set('bpkey', "\x00\x00\x00");
         $this->assertEquals($this->redis->bitpos('bpkey', 1), -1);
+
+        if (!$this->minVersionCheck("7.0.0"))
+            return;
+
+        $this->redis->set('bpkey', "\xF");
+        $this->assertEquals(4, $this->redis->bitpos('bpkey', 1, 0, -1, true));
+        $this->assertEquals(-1,  $this->redis->bitpos('bpkey', 1, 1, -1));
+        $this->assertEquals(-1,  $this->redis->bitpos('bpkey', 1, 1, -1, false));
     }
 
     public function test1000() {

From 8d80ca5bcadd9ab8d6aab920a9f8acde011ae54e Mon Sep 17 00:00:00 2001
From: Juha 
Date: Fri, 14 Oct 2022 09:30:15 +0200
Subject: [PATCH 1648/1986] Update README.markdown

Small fix to couple parameter formatting in the readme file - making sure that all the parameter definitions start from new line for these two functions (previously the port parameter didn't start from it's own new line).
---
 README.markdown | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/README.markdown b/README.markdown
index 68cb9029a9..3d31c8f6f9 100644
--- a/README.markdown
+++ b/README.markdown
@@ -221,13 +221,13 @@ _**Description**_: Connects to a Redis instance.
 
 ##### *Parameters*
 
-*host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema 
+*host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema  
 *port*: int, optional  
-*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)
+*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)  
 *reserved*: should be '' if retry_interval is specified  
 *retry_interval*: int, value in milliseconds (optional)  
-*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)
-*others*: array, with PhpRedis >= 5.3.0, it allows setting _auth_ and [_stream_](https://www.php.net/manual/en/context.ssl.php) configuration.
+*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)  
+*others*: array, with PhpRedis >= 5.3.0, it allows setting _auth_ and [_stream_](https://www.php.net/manual/en/context.ssl.php) configuration.  
 
 ##### *Return value*
 
@@ -269,12 +269,12 @@ persistent equivalents.
 
 ##### *Parameters*
 
-*host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema 
+*host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema  
 *port*: int, optional  
-*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)
+*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)  
 *persistent_id*: string. identity for the requested persistent connection  
 *retry_interval*: int, value in milliseconds (optional)  
-*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)
+*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout)  
 
 ##### *Return value*
 

From 44d03ca00ac68cc475c5fbc4ac4b005ca7df0985 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 14 Oct 2022 09:14:17 -0700
Subject: [PATCH 1649/1986] INFO with multiple sections

See #2068
---
 library.c                      |  1 +
 redis.c                        | 34 +++++++++++----------------
 redis.stub.php                 | 16 ++++++++++++-
 redis_arginfo.h                |  4 ++--
 redis_cluster.c                | 43 +++++++++++++++++-----------------
 redis_cluster.stub.php         | 18 +++++++++++++-
 redis_cluster_arginfo.h        |  4 ++--
 redis_cluster_legacy_arginfo.h |  4 ++--
 redis_legacy_arginfo.h         |  4 ++--
 tests/RedisTest.php            |  6 +++++
 10 files changed, 82 insertions(+), 52 deletions(-)

diff --git a/library.c b/library.c
index 91f8fca9cb..bbdca77d25 100644
--- a/library.c
+++ b/library.c
@@ -3414,6 +3414,7 @@ redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) {
 }
 
 PHP_REDIS_API int
+
 redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
 {
     php_serialize_data_t ht;
diff --git a/redis.c b/redis.c
index 36b0cf186e..9ce8018b21 100644
--- a/redis.c
+++ b/redis.c
@@ -1752,38 +1752,32 @@ PHP_METHOD(Redis, pttl) {
 
 /* {{{ proto array Redis::info() */
 PHP_METHOD(Redis, info) {
-
-    zval *object;
+    smart_string cmdstr = {0};
     RedisSock *redis_sock;
-    char *cmd, *opt = NULL;
-    size_t opt_len;
-    int cmd_len;
+    zend_string *section;
+    zval *args = NULL;
+    int i, argc = 0;
 
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                     "O|s", &object, redis_ce, &opt, &opt_len)
-                                     == FAILURE)
-    {
-        RETURN_FALSE;
-    }
+    ZEND_PARSE_PARAMETERS_START(0, -1)
+        Z_PARAM_VARIADIC('+', args, argc)
+    ZEND_PARSE_PARAMETERS_END();
 
-    if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
+    if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL)
         RETURN_FALSE;
-    }
 
-    /* Build a standalone INFO command or one with an option */
-    if (opt != NULL) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "INFO", "s", opt, opt_len);
-    } else {
-        cmd_len = REDIS_SPPRINTF(&cmd, "INFO", "");
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "INFO");
+    for (i = 0; i < argc; i++) {
+        section = zval_get_string(&args[i]);
+        redis_cmd_append_sstr_zstr(&cmdstr, section);
+        zend_string_release(section);
     }
 
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
+    REDIS_PROCESS_REQUEST(redis_sock, cmdstr.c, cmdstr.len);
     if (IS_ATOMIC(redis_sock)) {
         redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL,
             NULL);
     }
     REDIS_PROCESS_RESPONSE(redis_info_response);
-
 }
 /* }}} */
 
diff --git a/redis.stub.php b/redis.stub.php
index eb3f53132a..b41ff83101 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -269,7 +269,21 @@ public function incrBy(string $key, int $value);
     /** @return Redis|int|false */
     public function incrByFloat(string $key, float $value);
 
-    public function info(string $opt = null): Redis|array|false;
+    /**
+      Retreive information about the connected redis-server.  If no arguments are passed to
+      this function, redis will return every info field.  Alternatively you may pass a specific
+      section you want returned (e.g. 'server', or 'memory') to receive only information pertaining
+      to that section.
+
+      If connected to Redis server >= 7.0.0 you may pass multiple optional sections.
+
+      @see https://redis.io/commands/info/
+
+      @param string ...$sections Optional section(s) you wish Redis server to return.
+
+      @return Redis|array|false
+     */
+    public function info(string ...$sections): Redis|array|false;
 
     public function isConnected(): bool;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index ea38af2eed..41eb4738ae 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a8ddcde8e8af5201d72bfe8b463afc0c9dafb73d */
+ * Stub hash: bd81918bd558ec53399e7f64647c39f288f3413e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -444,7 +444,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrByFloat, 0, 0, 2)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_info, 0, 0, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, sections, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_isConnected arginfo_class_Redis_close
diff --git a/redis_cluster.c b/redis_cluster.c
index 0a701ed8bb..5a3cb4db30 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -2578,39 +2578,38 @@ PHP_METHOD(RedisCluster, lastsave) {
  *     proto array RedisCluster::info(array host_port, [string $arg]) */
 PHP_METHOD(RedisCluster, info) {
     redisCluster *c = GET_CONTEXT();
+    zval *node = NULL, *args = NULL;
+    smart_string cmdstr = {0};
     REDIS_REPLY_TYPE rtype;
-    char *cmd, *opt = NULL;
-    int cmd_len;
-    size_t opt_len = 0;
+    zend_string *section;
     void *ctx = NULL;
-
-    zval *z_arg;
+    int i, argc;
     short slot;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s", &z_arg, &opt,
-                             &opt_len) == FAILURE)
-    {
+    ZEND_PARSE_PARAMETERS_START(1, -1)
+        Z_PARAM_ZVAL(node)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_VARIADIC('*', args, argc)
+    ZEND_PARSE_PARAMETERS_END();
+
+    if ((slot = cluster_cmd_get_slot(c, node)) < 0)
         RETURN_FALSE;
-    }
 
-    /* Treat INFO as non read-only, as we probably want the master */
-    c->readonly = 0;
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "INFO");
 
-    slot = cluster_cmd_get_slot(c, z_arg);
-    if (slot < 0) {
-        RETURN_FALSE;
-    }
+    /* Direct this command at the master */
+    c->readonly = 0;
 
-    if (opt != NULL) {
-        cmd_len = redis_spprintf(NULL, NULL, &cmd, "INFO", "s", opt, opt_len);
-    } else {
-        cmd_len = redis_spprintf(NULL, NULL, &cmd, "INFO", "");
+    for (i = 0; i < argc; i++) {
+        section = zval_get_string(&args[i]);
+        redis_cmd_append_sstr_zstr(&cmdstr, section);
+        zend_string_release(section);
     }
 
     rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE;
-    if (cluster_send_slot(c, slot, cmd, cmd_len, rtype) < 0) {
+    if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, rtype) < 0) {
         CLUSTER_THROW_EXCEPTION("Unable to send INFO command to specific node", 0);
-        efree(cmd);
+        efree(cmdstr.c);
         RETURN_FALSE;
     }
 
@@ -2620,7 +2619,7 @@ PHP_METHOD(RedisCluster, info) {
         CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_info_resp, ctx);
     }
 
-    efree(cmd);
+    efree(cmdstr.c);
 }
 /* }}} */
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 05a44ccdee..713f1c468b 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -187,7 +187,23 @@ public function incrby(string $key, int $value): RedisCluster|int|false;
 
     public function incrbyfloat(string $key, float $value): RedisCluster|float|false;
 
-    public function info(string|array $key_or_address, ?string $section = null): RedisCluster|array|false;
+    /**
+      Retreive information about the connected redis-server.  If no arguments are passed to
+      this function, redis will return every info field.  Alternatively you may pass a specific
+      section you want returned (e.g. 'server', or 'memory') to receive only information pertaining
+      to that section.
+
+      If connected to Redis server >= 7.0.0 you may pass multiple optional sections.
+
+      @see https://redis.io/commands/info/
+
+      @param string|array $key_or_address Either a key name or array with host and port indicating
+                                          which cluster node we want to send the command to.
+      @param string       ...$sections    Optional section(s) you wish Redis server to return.
+
+      @return Redis|array|false
+     */
+    public function info(string|array $key_or_address, string ...$sections): RedisCluster|array|false;
 
     public function keys(string $pattern): RedisCluster|array|false;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 7578b03206..3dd1f55d46 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ccb418a312ff22f03e2354de508ff8bd9e4d266e */
+ * Stub hash: d3d58cb90bf0884a4153cd01f3d0850b42fcd910 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -385,7 +385,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_info, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, section, IS_STRING, 1, "null")
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, sections, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_keys, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 6e392c7243..6dbd023107 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ccb418a312ff22f03e2354de508ff8bd9e4d266e */
+ * Stub hash: d3d58cb90bf0884a4153cd01f3d0850b42fcd910 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -327,7 +327,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_info, 0, 0, 1)
 	ZEND_ARG_INFO(0, key_or_address)
-	ZEND_ARG_INFO(0, section)
+	ZEND_ARG_VARIADIC_INFO(0, sections)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_keys, 0, 0, 1)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 5c8642d9cc..18db4f2584 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a8ddcde8e8af5201d72bfe8b463afc0c9dafb73d */
+ * Stub hash: bd81918bd558ec53399e7f64647c39f288f3413e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -375,7 +375,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_Redis_incrByFloat arginfo_class_Redis_append
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_info, 0, 0, 0)
-	ZEND_ARG_INFO(0, opt)
+	ZEND_ARG_VARIADIC_INFO(0, sections)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_isConnected arginfo_class_Redis___destruct
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index bcef3e6277..c240bc9f72 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2363,6 +2363,12 @@ public function testInfo() {
                 $this->assertTrue(in_array($k, array_keys($info)));
             }
         }
+
+        if (!$this->minVersionCheck("7.0.0"))
+            return;
+
+        $res = $this->redis->info('server', 'memory');
+        $this->assertTrue(is_array($res) && isset($res['redis_version']) && isset($res['used_memory']));
     }
 
     public function testInfoCommandStats() {

From b5ea5fd77fe9034ed003db901bbf1f89e15c8ed4 Mon Sep 17 00:00:00 2001
From: Nicolas Grekas 
Date: Sun, 16 Oct 2022 23:08:28 +0200
Subject: [PATCH 1650/1986] Fix stub

---
 redis.stub.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redis.stub.php b/redis.stub.php
index b41ff83101..9972d8f8cf 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -246,7 +246,7 @@ public function hLen(string $key): Redis|int|false;
 
     public function hMget(string $key, array $keys): Redis|array|false;
 
-    public function hMset(string $key, array $keyvals): Redis|bool|false;
+    public function hMset(string $key, array $keyvals): Redis|bool;
 
     public function hRandField(string $key, array $options = null): Redis|string|array;
 

From 36ef4bd8d16e9f19e194745ba61239bdf19d3851 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 14 Oct 2022 08:43:00 -0700
Subject: [PATCH 1651/1986] Variadic CONFIG GET/SET

Redis 7.0.0 allows for getting and setting multiple config values as an
atomic operation.  In order to support this while maintaining backward
compatibility, the CONFIG command is reworked to also accept an array of
values or keys and values.

See: #2068
---
 README.markdown        |   8 ++-
 library.c              |   9 +++
 library.h              |   1 +
 redis.c                |  43 ++----------
 redis.stub.php         |  24 ++++++-
 redis_arginfo.h        |   6 +-
 redis_commands.c       | 150 +++++++++++++++++++++++++++++++++++++++++
 redis_commands.h       |   3 +
 redis_legacy_arginfo.h |   4 +-
 tests/RedisTest.php    |  36 +++++++++-
 10 files changed, 234 insertions(+), 50 deletions(-)

diff --git a/README.markdown b/README.markdown
index 3d31c8f6f9..d580c27fa0 100644
--- a/README.markdown
+++ b/README.markdown
@@ -559,17 +559,19 @@ _**Description**_: Get or Set the Redis server configuration parameters.
 
 ##### *Prototype*
 ~~~php
-$redis->config($operation, ?string $key = NULL, ?string $value = NULL): mixed;
+$redis->config(string $operation, string|array|null $key = NULL, ?string $value = NULL): mixed;
 ~~~
 
 ##### *Return value*
-*Associative array* for `GET`, key -> value  
-*bool* for `SET`, and `RESETSTAT`
+*Associative array* for `GET`, key(s) -> value(s)  
+*bool* for `SET`, `RESETSTAT`, and `REWRITE`
 
 ##### *Examples*
 ~~~php
 $redis->config("GET", "*max-*-entries*");
+$redis->config("SET", ['timeout', 'loglevel']);
 $redis->config("SET", "dir", "/var/run/redis/dumps/");
+$redis->config("SET", ['timeout' => 128, 'loglevel' => 'warning']);
 $redis->config('RESETSTAT');
 ~~~
 
diff --git a/library.c b/library.c
index bbdca77d25..3c8f5c4dae 100644
--- a/library.c
+++ b/library.c
@@ -1151,6 +1151,15 @@ PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
     return SUCCESS;
 }
 
+PHP_REDIS_API int
+redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+    FailableResultCallback cb = ctx;
+
+    ZEND_ASSERT(cb == redis_boolean_response || cb == redis_mbulk_reply_zipped_raw);
+
+    return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
+}
+
 PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
     char *response;
     int response_len;
diff --git a/library.h b/library.h
index b67d72f876..036cb45615 100644
--- a/library.h
+++ b/library.h
@@ -67,6 +67,7 @@ PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redis
 PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret);
 PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret);
 PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
diff --git a/redis.c b/redis.c
index 9ce8018b21..788deff35a 100644
--- a/redis.c
+++ b/redis.c
@@ -2707,45 +2707,10 @@ PHP_METHOD(Redis, setOption)
 /* }}} */
 
 /* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */
-PHP_METHOD(Redis, config)
-{
-    zend_string *op, *key = NULL, *val = NULL;
-    FailableResultCallback cb;
-    RedisSock *redis_sock;
-    zval *object;
-    int cmd_len;
-    char *cmd;
-
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS|SS", &object,
-                                     redis_ce, &op, &key, &val) == FAILURE)
-    {
-        RETURN_FALSE;
-    }
-
-    if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
-        RETURN_FALSE;
-    }
-
-    if (zend_string_equals_literal_ci(op, "GET") && key != NULL) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SS", op, key);
-        cb = redis_mbulk_reply_zipped_raw;
-    } else if (zend_string_equals_literal_ci(op, "RESETSTAT") ||
-               zend_string_equals_literal_ci(op, "REWRITE"))
-    {
-        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "s", ZSTR_VAL(op), ZSTR_LEN(op));
-        cb = redis_boolean_response;
-    } else if (zend_string_equals_literal_ci(op, "SET") && key != NULL && val != NULL) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SSS", op, key, val);
-        cb = redis_boolean_response;
-    } else {
-        RETURN_FALSE;
-    }
-
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
-    if (IS_ATOMIC(redis_sock)) {
-        cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
-    }
-    REDIS_PROCESS_RESPONSE(redis_boolean_response);
+/* {{{ proto public function config(string $op, string ...$args) }}} */
+// CONFIG SET/GET
+PHP_METHOD(Redis, config) {
+    REDIS_PROCESS_CMD(config, redis_config_response);
 }
 /* }}} */
 
diff --git a/redis.stub.php b/redis.stub.php
index 9972d8f8cf..e0323c3964 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -81,7 +81,29 @@ public function close(): bool;
 
     public function command(string $opt = null, string|array $arg): mixed;
 
-    public function config(string $operation, ?string $key = NULL, mixed $value = null): mixed;
+    /**
+      Execute the Redis CONFIG command in a variety of ways.  What the command does in particular depends
+      on the `$operation` qualifier.
+
+      Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.
+
+      @param string            $operation      The CONFIG subcommand to execute
+      @param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or
+                                               an array of settings or settings and values.
+                                               Note:  Redis 7.0.0 is required to send an array of settings.
+      @param ?string           $value          The setting value when the operation is SET.
+
+      
+      config('GET', 'timeout');
+      $redis->config('GET', ['timeout', 'databases']);
+
+      $redis->config('SET', 'timeout', 30);
+      $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);
+      ?>
+      
+     */
+    public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed;
 
     public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 41eb4738ae..d59539d434 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: bd81918bd558ec53399e7f64647c39f288f3413e */
+ * Stub hash: a024c59eff58030ac224fc22cc4040b6e926a643 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -127,8 +127,8 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "NULL")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, "null")
+	ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "NULL")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0)
diff --git a/redis_commands.c b/redis_commands.c
index e415ae5c1e..6b23acbe5f 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -32,6 +32,14 @@
 
 #include 
 
+/* Config operations */
+typedef enum redisConfigOp {
+    REDIS_CFG_RESETSTAT,
+    REDIS_CFG_REWRITE,
+    REDIS_CFG_GET,
+    REDIS_CFG_SET,
+} redisConfigOp;
+
 /* Georadius sort type */
 typedef enum geoSortType {
     SORT_NONE,
@@ -728,6 +736,148 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+static int redis_get_config_op(enum redisConfigOp *dst, zend_string *op) {
+    if (zend_string_equals_literal_ci(op, "RESETSTAT"))
+        *dst = REDIS_CFG_RESETSTAT;
+    else if (zend_string_equals_literal_ci(op, "REWRITE"))
+        *dst = REDIS_CFG_REWRITE;
+    else if (zend_string_equals_literal_ci(op, "GET"))
+        *dst = REDIS_CFG_GET;
+    else if (zend_string_equals_literal_ci(op, "SET"))
+        *dst = REDIS_CFG_SET;
+    else
+        return FAILURE;
+
+    return SUCCESS;
+}
+
+static int redis_build_config_get_cmd(smart_string *dst, zval *val) {
+    zend_string *zstr;
+    int ncfg;
+    zval *zv;
+
+    if (val == NULL || (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY)) {
+        php_error_docref(NULL, E_WARNING, "Must pass a string or array of values to CONFIG GET");
+        return FAILURE;
+    } else if (Z_TYPE_P(val) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(val)) == 0) {
+        php_error_docref(NULL, E_WARNING, "Cannot pass an empty array to CONFIG GET");
+        return FAILURE;
+    }
+
+    ncfg = Z_TYPE_P(val) == IS_STRING ? 1 : zend_hash_num_elements(Z_ARRVAL_P(val));
+
+    REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + ncfg, "CONFIG");
+    REDIS_CMD_APPEND_SSTR_STATIC(dst, "GET");
+
+    if (Z_TYPE_P(val) == IS_STRING) {
+        redis_cmd_append_sstr_zstr(dst, Z_STR_P(val));
+    } else {
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), zv) {
+            ZVAL_DEREF(zv);
+
+            zstr = zval_get_string(zv);
+            redis_cmd_append_sstr_zstr(dst, zstr);
+            zend_string_release(zstr);
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    return SUCCESS;
+}
+
+static int redis_build_config_set_cmd(smart_string *dst, zval *key, zend_string *val) {
+    zend_string *zkey, *zstr;
+    zval *zv;
+
+    /* Legacy case:  CONFIG SET   */
+    if (key != NULL && val != NULL) {
+        REDIS_CMD_INIT_SSTR_STATIC(dst, 3, "CONFIG");
+        REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET");
+
+        zstr = zval_get_string(key);
+        redis_cmd_append_sstr_zstr(dst, zstr);
+        zend_string_release(zstr);
+
+        redis_cmd_append_sstr_zstr(dst, val);
+
+        return SUCCESS;
+    }
+
+    /* Now we must have an array with at least one element */
+    if (key == NULL || Z_TYPE_P(key) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(key)) == 0) {
+        php_error_docref(NULL, E_WARNING, "Must either pass two strings to CONFIG SET or a non-empty array of values");
+        return FAILURE;
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + (2 * zend_hash_num_elements(Z_ARRVAL_P(key))), "CONFIG");
+    REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET");
+
+    ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(key), zkey, zv) {
+        if (zkey == NULL)
+            goto fail;
+
+        ZVAL_DEREF(zv);
+
+        redis_cmd_append_sstr_zstr(dst, zkey);
+
+        zstr = zval_get_string(zv);
+        redis_cmd_append_sstr_zstr(dst, zstr);
+        zend_string_release(zstr);
+    } ZEND_HASH_FOREACH_END();
+
+    return SUCCESS;
+
+fail:
+    php_error_docref(NULL, E_WARNING, "Must pass an associate array of config keys and values");
+    efree(dst->c);
+    memset(dst, 0, sizeof(*dst));
+    return FAILURE;
+}
+
+int
+redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                 char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    zend_string *op = NULL, *arg = NULL;
+    smart_string cmdstr = {0};
+    enum redisConfigOp cfg_op;
+    int res = FAILURE;
+    zval *key = NULL;
+
+    ZEND_PARSE_PARAMETERS_START(1, 3)
+        Z_PARAM_STR(op)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_ZVAL_OR_NULL(key)
+        Z_PARAM_STR_OR_NULL(arg)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    if (redis_get_config_op(&cfg_op, op) != SUCCESS) {
+        php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op));
+        return FAILURE;
+    }
+
+    switch (cfg_op) {
+        case REDIS_CFG_RESETSTAT: /* fallthrough */
+        case REDIS_CFG_REWRITE:
+            REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CONFIG");
+            redis_cmd_append_sstr_zstr(&cmdstr, op);
+            *ctx = redis_boolean_response;
+            res  = SUCCESS;
+            break;
+        case REDIS_CFG_GET:
+            res  = redis_build_config_get_cmd(&cmdstr, key);
+            *ctx = redis_mbulk_reply_zipped_raw;
+            break;
+        case REDIS_CFG_SET:
+            res  = redis_build_config_set_cmd(&cmdstr, key, arg);
+            *ctx = redis_boolean_response;
+            break;
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+    return res;
+}
+
 int
 redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index ac63bc4113..ad76835613 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -110,6 +110,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, int *withscores, short *slot,
     void **ctx);
 
+int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 18db4f2584..27aec07433 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: bd81918bd558ec53399e7f64647c39f288f3413e */
+ * Stub hash: a024c59eff58030ac224fc22cc4040b6e926a643 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -117,7 +117,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_config, 0, 0, 1)
 	ZEND_ARG_INFO(0, operation)
-	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, key_or_settings)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c240bc9f72..eab3e2f724 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5612,9 +5612,9 @@ public function testConfig() {
 
         /* Ensure invalid calls are handled by PhpRedis */
         foreach (['notacommand', 'get', 'set'] as $cmd) {
-            $this->assertFalse($this->redis->config($cmd));
+            $this->assertFalse(@$this->redis->config($cmd));
         }
-        $this->assertFalse($this->redis->config('set', 'foo'));
+        $this->assertFalse(@$this->redis->config('set', 'foo'));
 
         /* REWRITE.  We don't care if it actually works, just that the
            command be attempted */
@@ -5624,6 +5624,38 @@ public function testConfig() {
             $this->assertPatternMatch($this->redis->getLastError(), '/.*config.*/');
             $this->redis->clearLastError();
         }
+
+        if (!$this->minVersionCheck("7.0.0"))
+            return;
+
+        /* Test getting multiple values */
+        $settings = $this->redis->config('get', ['timeout', 'databases', 'set-max-intset-entries']);
+        $this->assertTrue(is_array($settings) && isset($settings['timeout']) &&
+                          isset($settings['databases']) && isset($settings['set-max-intset-entries']));
+
+        /* Short circuit if the above assertion would have failed */
+        if ( ! is_array($settings) || ! isset($settings['timeout']) || ! isset($settings['set-max-intset-entries']))
+            return;
+
+        list($timeout, $max_intset) = [$settings['timeout'], $settings['set-max-intset-entries']];
+
+        $updates = [
+            ['timeout' => (string)($timeout + 30), 'set-max-intset-entries' => (string)($max_intset + 128)],
+            ['timeout' => (string)($timeout), 'set-max-intset-entries' => (string)$max_intset],
+        ];
+
+        foreach ($updates as $update) {
+            $this->assertTrue($this->redis->config('set', $update));
+            $vals = $this->redis->config('get', array_keys($update));
+            ksort($vals);
+            ksort($update);
+            $this->assertEquals($vals, $update);
+        }
+
+        /* Make sure PhpRedis catches malformed multiple get/set calls */
+        $this->assertFalse(@$this->redis->config('get', []));
+        $this->assertFalse(@$this->redis->config('set', []));
+        $this->assertFalse(@$this->redis->config('set', [0, 1, 2]));
     }
 
     public function testReconnectSelect() {

From a176f58619a918376e66c385ce9dcf73dcc6287a Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 18 Oct 2022 23:08:36 -0700
Subject: [PATCH 1652/1986] Simplify logic by removing CONFIG enum.

---
 redis_commands.c | 57 ++++++++++++------------------------------------
 1 file changed, 14 insertions(+), 43 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 6b23acbe5f..801e5059b8 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -32,14 +32,6 @@
 
 #include 
 
-/* Config operations */
-typedef enum redisConfigOp {
-    REDIS_CFG_RESETSTAT,
-    REDIS_CFG_REWRITE,
-    REDIS_CFG_GET,
-    REDIS_CFG_SET,
-} redisConfigOp;
-
 /* Georadius sort type */
 typedef enum geoSortType {
     SORT_NONE,
@@ -736,21 +728,6 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-static int redis_get_config_op(enum redisConfigOp *dst, zend_string *op) {
-    if (zend_string_equals_literal_ci(op, "RESETSTAT"))
-        *dst = REDIS_CFG_RESETSTAT;
-    else if (zend_string_equals_literal_ci(op, "REWRITE"))
-        *dst = REDIS_CFG_REWRITE;
-    else if (zend_string_equals_literal_ci(op, "GET"))
-        *dst = REDIS_CFG_GET;
-    else if (zend_string_equals_literal_ci(op, "SET"))
-        *dst = REDIS_CFG_SET;
-    else
-        return FAILURE;
-
-    return SUCCESS;
-}
-
 static int redis_build_config_get_cmd(smart_string *dst, zval *val) {
     zend_string *zstr;
     int ncfg;
@@ -839,7 +816,6 @@ redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 {
     zend_string *op = NULL, *arg = NULL;
     smart_string cmdstr = {0};
-    enum redisConfigOp cfg_op;
     int res = FAILURE;
     zval *key = NULL;
 
@@ -850,29 +826,24 @@ redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         Z_PARAM_STR_OR_NULL(arg)
     ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
 
-    if (redis_get_config_op(&cfg_op, op) != SUCCESS) {
+    if (zend_string_equals_literal_ci(op, "RESETSTAT") ||
+        zend_string_equals_literal_ci(op, "REWRITE"))
+    {
+        REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CONFIG");
+        redis_cmd_append_sstr_zstr(&cmdstr, op);
+        *ctx = redis_boolean_response;
+        res  = SUCCESS;
+    } else if (zend_string_equals_literal_ci(op, "GET")) {
+        res  = redis_build_config_get_cmd(&cmdstr, key);
+        *ctx = redis_mbulk_reply_zipped_raw;
+    } else if (zend_string_equals_literal_ci(op, "SET")) {
+        res  = redis_build_config_set_cmd(&cmdstr, key, arg);
+        *ctx = redis_boolean_response;
+    } else {
         php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op));
         return FAILURE;
     }
 
-    switch (cfg_op) {
-        case REDIS_CFG_RESETSTAT: /* fallthrough */
-        case REDIS_CFG_REWRITE:
-            REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CONFIG");
-            redis_cmd_append_sstr_zstr(&cmdstr, op);
-            *ctx = redis_boolean_response;
-            res  = SUCCESS;
-            break;
-        case REDIS_CFG_GET:
-            res  = redis_build_config_get_cmd(&cmdstr, key);
-            *ctx = redis_mbulk_reply_zipped_raw;
-            break;
-        case REDIS_CFG_SET:
-            res  = redis_build_config_set_cmd(&cmdstr, key, arg);
-            *ctx = redis_boolean_response;
-            break;
-    }
-
     *cmd = cmdstr.c;
     *cmd_len = cmdstr.len;
     return res;

From d87f1428260b9270de6d0b7cef253057197f2987 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 19 Oct 2022 09:16:18 -0700
Subject: [PATCH 1653/1986] Refactor SLOWLOG command

Refactor the slowlog command to use the new argument parsing API and
also change it so we no longer manually handle atomic/non-atomic
logic in the handler itself.
---
 redis.c                | 52 +-----------------------------------------
 redis.stub.php         | 28 ++++++++++++++++++++++-
 redis_arginfo.h        |  8 +++----
 redis_commands.c       | 37 ++++++++++++++++++++++++++++++
 redis_commands.h       |  3 +++
 redis_legacy_arginfo.h |  6 ++---
 tests/RedisTest.php    |  2 +-
 7 files changed, 76 insertions(+), 60 deletions(-)

diff --git a/redis.c b/redis.c
index 788deff35a..2696b58dfc 100644
--- a/redis.c
+++ b/redis.c
@@ -2717,57 +2717,7 @@ PHP_METHOD(Redis, config) {
 
 /* {{{ proto boolean Redis::slowlog(string arg, [int option]) */
 PHP_METHOD(Redis, slowlog) {
-    zval *object;
-    RedisSock *redis_sock;
-    char *arg, *cmd;
-    int cmd_len;
-    size_t arg_len;
-    zend_long option = 0;
-    enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode;
-
-    // Make sure we can get parameters
-    if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                    "Os|l", &object, redis_ce, &arg, &arg_len,
-                                    &option) == FAILURE)
-    {
-        RETURN_FALSE;
-    }
-
-    /* Figure out what kind of slowlog command we're executing */
-    if(!strncasecmp(arg, "GET", 3)) {
-        mode = SLOWLOG_GET;
-    } else if(!strncasecmp(arg, "LEN", 3)) {
-        mode = SLOWLOG_LEN;
-    } else if(!strncasecmp(arg, "RESET", 5)) {
-        mode = SLOWLOG_RESET;
-    } else {
-        /* This command is not valid */
-        RETURN_FALSE;
-    }
-
-    /* Make sure we can grab our redis socket */
-    if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
-        RETURN_FALSE;
-    }
-
-    // Create our command.  For everything except SLOWLOG GET (with an arg) it's
-    // just two parts
-    if (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "SLOWLOG", "sl", arg, arg_len, option);
-    } else {
-        cmd_len = REDIS_SPPRINTF(&cmd, "SLOWLOG", "s", arg, arg_len);
-    }
-
-    /* Kick off our command */
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
-    if (IS_ATOMIC(redis_sock)) {
-        if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                                    redis_sock, NULL, NULL) < 0)
-        {
-            RETURN_FALSE;
-        }
-    }
-    REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+    REDIS_PROCESS_CMD(slowlog, redis_read_variant_reply);
 }
 
 /* {{{ proto Redis::wait(int num_slaves, int ms) }}} */
diff --git a/redis.stub.php b/redis.stub.php
index e0323c3964..dcaf7223e9 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -521,7 +521,33 @@ public function sismember(string $key, mixed $value): Redis|bool;
 
     public function slaveof(string $host = null, int $port = 6379): bool;
 
-    public function slowlog(string $mode, int $option = 0): mixed;
+    /**
+      Interact with Redis' slowlog functionality in variousu ways, depending
+      on the value of 'operations'.
+
+      @param string $operation  The operation you wish to perform.  This can
+                                be one of the following values:
+                                'get'   - Retreive the Redis slowlog as an array.
+                                'len'   - Retreive the length of the slowlog.
+                                'reset' - Remove all slowlog entries.
+      
+      slowllog('get', -1);  // Retreive all slowlog entries.
+      $redis->slowlog('len');       // Retreive slowlog length.
+      $redis->slowlog('reset');     // Reset the slowlog.
+      ?>
+      
+
+      @param int    $length     This optional argument can be passed when operation
+                                is 'get' and will specify how many elements to retreive.
+                                If omitted Redis will send up to a default number of
+                                entries, which is configurable.
+
+                                Note:  With Redis >= 7.0.0 you can send -1 to mean "all".
+
+      @return mixed
+     */
+    public function slowlog(string $operation, int $length = 0): mixed;
 
     public function sort(string $key, ?array $options = null): mixed;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index d59539d434..00ffae8eae 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a024c59eff58030ac224fc22cc4040b6e926a643 */
+ * Stub hash: 3ffe58fd2c74dcb474adf0b983f503d798c975d8 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -392,7 +392,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMget, 0, 2, Red
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMset, 0, 2, Redis, MAY_BE_BOOL|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMset, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keyvals, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -787,8 +787,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, _IS_B
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slowlog, 0, 1, IS_MIXED, 0)
-	ZEND_ARG_TYPE_INFO(0, mode, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 0, "0")
+	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sort, 0, 1, IS_MIXED, 0)
diff --git a/redis_commands.c b/redis_commands.c
index 801e5059b8..a0a55151f4 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2549,6 +2549,43 @@ int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                      char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode;
+    smart_string cmdstr = {0};
+    zend_string *op = NULL;
+    zend_long arg = 0;
+
+    ZEND_PARSE_PARAMETERS_START(1, 2)
+        Z_PARAM_STR(op)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_LONG(arg)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    if (zend_string_equals_literal_ci(op, "GET")) {
+        mode = SLOWLOG_GET;
+    } else if (zend_string_equals_literal_ci(op, "LEN")) {
+        mode = SLOWLOG_LEN;
+    } else if (zend_string_equals_literal_ci(op, "RESET")) {
+        mode = SLOWLOG_RESET;
+    } else {
+        php_error_docref(NULL, E_WARNING, "Unknown SLOWLOG operation '%s'", ZSTR_VAL(op));
+        return FAILURE;
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2), "SLOWLOG");
+    redis_cmd_append_sstr_zstr(&cmdstr, op);
+
+    if (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2)
+        redis_cmd_append_sstr_long(&cmdstr, arg);
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 void redis_get_restore_options(redisRestoreOptions *dst, HashTable *ht) {
     zend_string *key;
     zend_long lval;
diff --git a/redis_commands.h b/redis_commands.h
index ad76835613..50512a3dce 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -132,6 +132,9 @@ int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                         char *kw, char **cmd, int *cmd_len, short *slot,
                         void **ctx);
 
+int redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                      char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                   char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 27aec07433..c71406f032 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a024c59eff58030ac224fc22cc4040b6e926a643 */
+ * Stub hash: 3ffe58fd2c74dcb474adf0b983f503d798c975d8 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -664,8 +664,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slowlog, 0, 0, 1)
-	ZEND_ARG_INFO(0, mode)
-	ZEND_ARG_INFO(0, option)
+	ZEND_ARG_INFO(0, operation)
+	ZEND_ARG_INFO(0, length)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sort arginfo_class_Redis_getEx
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index eab3e2f724..2dc654dcea 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2291,7 +2291,7 @@ public function testSlowlog() {
         $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'));
+        $this->assertFalse(@$this->redis->slowlog('notvalid'));
     }
 
     public function testWait() {

From 5dcf3f802b70ecd588a9262c0d441d2160b0909d Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 20 Oct 2022 14:47:44 -0700
Subject: [PATCH 1654/1986] Newer versions of Redis shouldn't need a long delay

---
 tests/RedisTest.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 2dc654dcea..6c02767d32 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5675,10 +5675,10 @@ public function testReconnectSelect() {
         // 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);
+        // Wait for the connection to time out.  On very old versions
+        // of Redis we need to wait much longer (TODO:  Investigate
+        // which version exactly)
+        sleep($this->minVersionCheck('3.0.0') ? 2 : 11);
 
         // Make sure we're still using the same DB.
         $this->assertEquals($value, $this->redis->get($key));

From f3a408305a3df04b6aaaede5fa7f37ddca1f8efb Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 21 Oct 2022 10:16:56 -0700
Subject: [PATCH 1655/1986] EVAL_RO and EVALSHA_RO

Implement Redis 7.0.0's readonly eval variants

See: #2068
---
 redis.c                        | 13 +++++++--
 redis.stub.php                 | 48 ++++++++++++++++++++++++++++++++--
 redis_arginfo.h                | 18 ++++++++++---
 redis_cluster.c                | 12 +++++++++
 redis_cluster.stub.php         |  4 +++
 redis_cluster_arginfo.h        | 10 ++++++-
 redis_cluster_legacy_arginfo.h | 10 ++++++-
 redis_legacy_arginfo.h         | 18 ++++++++++---
 tests/RedisTest.php            | 10 +++++++
 9 files changed, 131 insertions(+), 12 deletions(-)

diff --git a/redis.c b/redis.c
index 2696b58dfc..016658f3d0 100644
--- a/redis.c
+++ b/redis.c
@@ -2877,16 +2877,25 @@ PHP_METHOD(Redis, pubsub) {
 }
 
 /* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */
-PHP_METHOD(Redis, eval)
-{
+PHP_METHOD(Redis, eval) {
     REDIS_PROCESS_KW_CMD("EVAL", redis_eval_cmd, redis_read_raw_variant_reply);
 }
 
+/* {{{ proto variant Redis::eval_ro(string script, [array keys, long num_keys]) */
+PHP_METHOD(Redis, eval_ro) {
+    REDIS_PROCESS_KW_CMD("EVAL_RO", redis_eval_cmd, redis_read_raw_variant_reply);
+}
+
 /* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */
 PHP_METHOD(Redis, evalsha) {
     REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_raw_variant_reply);
 }
 
+/* {{{ proto variant Redis::evalsha_ro(string sha1, [array keys, long num_keys]) */
+PHP_METHOD(Redis, evalsha_ro) {
+    REDIS_PROCESS_KW_CMD("EVALSHA_RO", redis_eval_cmd, redis_read_raw_variant_reply);
+}
+
 /* {{{ proto status Redis::script('flush')
  * {{{ proto status Redis::script('kill')
  * {{{ proto string Redis::script('load', lua_script)
diff --git a/redis.stub.php b/redis.stub.php
index dcaf7223e9..fd9ea3c2c6 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -131,9 +131,53 @@ public function dump(string $key): Redis|string;
 
     public function echo(string $str): Redis|string|false;
 
-    public function eval(string $script, array $keys = null, int $num_keys = 0): mixed;
+    /**
+     * Execute a LUA script on the redis server.
+     *
+     * @see https://redis.io/commands/eval/
+     *
+     * @param string $script   A string containing the lua script
+     * @param array  $args     An array of arguments to pass to this script
+     * @param int    $num_keys How many of the arguments are keys.  This is needed
+     *                         as redis distinguishes between key name arguments
+     *                         and other data.
+     *
+     * @return mixed LUA scripts may return arbitrary data so this method can return
+     *               strings, arrays, nested arrays, etc.
+     */
+    public function eval(string $script, array $args = [], int $num_keys = 0): mixed;
+
+    /**
+     * This is simply the read-only variant of eval, meaning the unerlying script
+     * may not modify data in redis.
+     *
+     * @see Redis::eval()
+     */
+    public function eval_ro(string $script_sha, array $args = [], int $num_keys = 0): mixed;
 
-    public function evalsha(string $sha1, array $keys = null, int $num_keys = 0): mixed;
+    /**
+     * Execute a LUA script on the server but instead of sending the script, send
+     * the sha1 hash of the script.
+     *
+     * @see https://redis.io/commands/evalsha/
+     * @see Redis::eval();
+     *
+     * @param string $script_sha The sha1() hash of the lua code.  Note that the script
+     *                           must already exist on the server, either having been
+     *                           loaded with `SCRIPT LOAD` or having been executed directly
+     *                           with `EVAL` first.
+     * @param array  $args       Arguments to send to the script.
+     * @param int    $num_keys   The number of arguments that are keys
+     */
+    public function evalsha(string $sha1, array $args = [], int $num_keys = 0): mixed;
+
+    /**
+     * This is simply the read-only variant of evalsha, meaning the unerlying script
+     * may not modify data in redis.
+     *
+     * @see Redis::evalsha()
+     */
+    public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): mixed;
 
     public function exec(): Redis|array|false;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 00ffae8eae..482ad347ef 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3ffe58fd2c74dcb474adf0b983f503d798c975d8 */
+ * Stub hash: a27d28648f2d1a77237305083f36abc5e071f5b1 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -181,16 +181,24 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_eval, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, script, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, keys, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_eval_ro, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, script_sha, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_evalsha, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, sha1, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, keys, IS_ARRAY, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_evalsha_ro arginfo_class_Redis_evalsha
+
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_exec, 0, 0, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 ZEND_END_ARG_INFO()
 
@@ -1128,7 +1136,9 @@ ZEND_METHOD(Redis, discard);
 ZEND_METHOD(Redis, dump);
 ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, eval);
+ZEND_METHOD(Redis, eval_ro);
 ZEND_METHOD(Redis, evalsha);
+ZEND_METHOD(Redis, evalsha_ro);
 ZEND_METHOD(Redis, exec);
 ZEND_METHOD(Redis, exists);
 ZEND_METHOD(Redis, expire);
@@ -1369,7 +1379,9 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, eval, arginfo_class_Redis_eval, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, eval_ro, arginfo_class_Redis_eval_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, evalsha, arginfo_class_Redis_evalsha, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, evalsha_ro, arginfo_class_Redis_evalsha_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, exec, arginfo_class_Redis_exec, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index 5a3cb4db30..03e85d844b 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1707,12 +1707,24 @@ PHP_METHOD(RedisCluster, eval) {
 }
 /* }}} */
 
+/* {{{ proto mixed RedisCluster::eval_ro(string script, [array args, int numkeys) */
+PHP_METHOD(RedisCluster, eval_ro) {
+    CLUSTER_PROCESS_KW_CMD("EVAL_RO", redis_eval_cmd, cluster_variant_raw_resp, 1);
+}
+/* }}} */
+
 /* {{{ proto mixed RedisCluster::evalsha(string sha, [array args, int numkeys]) */
 PHP_METHOD(RedisCluster, evalsha) {
     CLUSTER_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, cluster_variant_raw_resp, 0);
 }
 /* }}} */
 
+/* {{{ proto mixed RedisCluster::evalsha_ro(string sha, [array args, int numkeys]) */
+PHP_METHOD(RedisCluster, evalsha_ro) {
+    CLUSTER_PROCESS_KW_CMD("EVALSHA_RO", redis_eval_cmd, cluster_variant_raw_resp, 1);
+}
+
+/* }}} */
 /* Commands that do not interact with Redis, but just report stuff about
  * various options, etc */
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 713f1c468b..598a8126cf 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -101,8 +101,12 @@ public function echo(string|array $key_or_address, string $msg): RedisCluster|st
 
     public function eval(string $script, array $args = [], int $num_keys = 0): mixed;
 
+    public function eval_ro(string $script, array $args = [], int $num_keys = 0): mixed;
+
     public function evalsha(string $script_sha, array $args = [], int $num_keys = 0): mixed;
 
+    public function evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0): mixed;
+
     public function exec(): array|false;
 
     public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 3dd1f55d46..b6bde9f759 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d3d58cb90bf0884a4153cd01f3d0850b42fcd910 */
+ * Stub hash: 7a2f14794870618a17273a2ed9f60d81449c82af */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -187,12 +187,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_eval, 0, 1, I
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_eval_ro arginfo_class_RedisCluster_eval
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_evalsha, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, script_sha, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, num_keys, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_evalsha_ro arginfo_class_RedisCluster_evalsha
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_exec, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
 ZEND_END_ARG_INFO()
 
@@ -971,7 +975,9 @@ ZEND_METHOD(RedisCluster, discard);
 ZEND_METHOD(RedisCluster, dump);
 ZEND_METHOD(RedisCluster, echo);
 ZEND_METHOD(RedisCluster, eval);
+ZEND_METHOD(RedisCluster, eval_ro);
 ZEND_METHOD(RedisCluster, evalsha);
+ZEND_METHOD(RedisCluster, evalsha_ro);
 ZEND_METHOD(RedisCluster, exec);
 ZEND_METHOD(RedisCluster, exists);
 ZEND_METHOD(RedisCluster, expire);
@@ -1174,7 +1180,9 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, echo, arginfo_class_RedisCluster_echo, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, eval, arginfo_class_RedisCluster_eval, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, eval_ro, arginfo_class_RedisCluster_eval_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, evalsha, arginfo_class_RedisCluster_evalsha, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, evalsha_ro, arginfo_class_RedisCluster_evalsha_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, exec, arginfo_class_RedisCluster_exec, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 6dbd023107..db11ebf59c 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d3d58cb90bf0884a4153cd01f3d0850b42fcd910 */
+ * Stub hash: 7a2f14794870618a17273a2ed9f60d81449c82af */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -165,12 +165,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_eval, 0, 0, 1)
 	ZEND_ARG_INFO(0, num_keys)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_eval_ro arginfo_class_RedisCluster_eval
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_evalsha, 0, 0, 1)
 	ZEND_ARG_INFO(0, script_sha)
 	ZEND_ARG_INFO(0, args)
 	ZEND_ARG_INFO(0, num_keys)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_evalsha_ro arginfo_class_RedisCluster_evalsha
+
 #define arginfo_class_RedisCluster_exec arginfo_class_RedisCluster__masters
 
 #define arginfo_class_RedisCluster_exists arginfo_class_RedisCluster_del
@@ -828,7 +832,9 @@ ZEND_METHOD(RedisCluster, discard);
 ZEND_METHOD(RedisCluster, dump);
 ZEND_METHOD(RedisCluster, echo);
 ZEND_METHOD(RedisCluster, eval);
+ZEND_METHOD(RedisCluster, eval_ro);
 ZEND_METHOD(RedisCluster, evalsha);
+ZEND_METHOD(RedisCluster, evalsha_ro);
 ZEND_METHOD(RedisCluster, exec);
 ZEND_METHOD(RedisCluster, exists);
 ZEND_METHOD(RedisCluster, expire);
@@ -1031,7 +1037,9 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, echo, arginfo_class_RedisCluster_echo, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, eval, arginfo_class_RedisCluster_eval, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, eval_ro, arginfo_class_RedisCluster_eval_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, evalsha, arginfo_class_RedisCluster_evalsha, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, evalsha_ro, arginfo_class_RedisCluster_evalsha_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, exec, arginfo_class_RedisCluster_exec, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index c71406f032..2bdf5e1249 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3ffe58fd2c74dcb474adf0b983f503d798c975d8 */
+ * Stub hash: a27d28648f2d1a77237305083f36abc5e071f5b1 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -165,16 +165,24 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_eval, 0, 0, 1)
 	ZEND_ARG_INFO(0, script)
-	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, args)
+	ZEND_ARG_INFO(0, num_keys)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_eval_ro, 0, 0, 1)
+	ZEND_ARG_INFO(0, script_sha)
+	ZEND_ARG_INFO(0, args)
 	ZEND_ARG_INFO(0, num_keys)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_evalsha, 0, 0, 1)
 	ZEND_ARG_INFO(0, sha1)
-	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, args)
 	ZEND_ARG_INFO(0, num_keys)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_evalsha_ro arginfo_class_Redis_evalsha
+
 #define arginfo_class_Redis_exec arginfo_class_Redis___destruct
 
 #define arginfo_class_Redis_exists arginfo_class_Redis_del
@@ -965,7 +973,9 @@ ZEND_METHOD(Redis, discard);
 ZEND_METHOD(Redis, dump);
 ZEND_METHOD(Redis, echo);
 ZEND_METHOD(Redis, eval);
+ZEND_METHOD(Redis, eval_ro);
 ZEND_METHOD(Redis, evalsha);
+ZEND_METHOD(Redis, evalsha_ro);
 ZEND_METHOD(Redis, exec);
 ZEND_METHOD(Redis, exists);
 ZEND_METHOD(Redis, expire);
@@ -1206,7 +1216,9 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, eval, arginfo_class_Redis_eval, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, eval_ro, arginfo_class_Redis_eval_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, evalsha, arginfo_class_Redis_evalsha, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, evalsha_ro, arginfo_class_Redis_evalsha_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, exec, arginfo_class_Redis_exec, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, exists, arginfo_class_Redis_exists, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, expire, arginfo_class_Redis_expire, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 6c02767d32..52b863304a 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5247,6 +5247,11 @@ public function testEval() {
             $this->markTestSkipped();
         }
 
+        /* The eval_ro method uses the same underlying handlers as eval so we
+           only need to verify we can call it. */
+        if ($this->minVersionCheck('7.0.0'))
+            $this->assertEquals('1.55', $this->redis->eval_ro("return '1.55'"));
+
         // Basic single line response tests
         $this->assertTrue(1 == $this->redis->eval('return 1'));
         $this->assertTrue(1.55 == $this->redis->eval("return '1.55'"));
@@ -5383,6 +5388,11 @@ public function testEvalSHA() {
         $this->assertTrue(false === $this->redis->evalsha($scr));
         $this->assertTrue(1 === $this->redis->eval($scr));
         $this->assertTrue(1 === $this->redis->evalsha($sha));
+
+        /* Our evalsha_ro handler is the same as evalsha so just make sure
+           we can invoke the command */
+        if ($this->minVersionCheck('7.0.0'))
+            $this->assertEquals(1, $this->redis->evalsha_ro($sha));
     }
 
     public function testSerialize() {

From 71bcbcb973413ae933cf30d58254dfb02425c1ed Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 22 Oct 2022 12:18:31 -0700
Subject: [PATCH 1656/1986] Implement ZRANGESTORE and add ZRANGE options

* Add ZRANGESTORE command.

* Add Redis 6.2's `REV`, `BYLEX`, and `BYSCORE` to ZRANGE options.

* Refactor several ZRANGE family commands into a single reply and
  options handler, using PHP's new argument parsing macros.

* Extend our tests to use the new ZRANGE options.

See #1894
---
 cluster_library.c              |  11 ++
 cluster_library.h              |   3 +
 library.c                      |  12 ++
 library.h                      |   1 +
 redis.c                        |  59 ++-----
 redis.stub.php                 |  58 ++++++-
 redis_arginfo.h                |  25 ++-
 redis_cluster.c                |  53 ++----
 redis_cluster.stub.php         |  11 +-
 redis_cluster_arginfo.h        |  14 +-
 redis_cluster_legacy_arginfo.h |  21 ++-
 redis_commands.c               | 288 +++++++++++++++++++++------------
 redis_commands.h               |   7 +-
 redis_legacy_arginfo.h         |  22 ++-
 tests/RedisTest.php            |  33 +++-
 15 files changed, 386 insertions(+), 232 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index fe574e50d9..90a6f95ea1 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -2115,6 +2115,17 @@ cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     cluster_free_reply(r, 1);
 }
 
+PHP_REDIS_API void
+cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
+    cluster_cb cb;
+
+    ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR);
+
+    cb = ctx ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp;
+
+    cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx);
+}
+
 PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                                         void *ctx)
 {
diff --git a/cluster_library.h b/cluster_library.h
index 1f18c35a0b..74ede7a4fd 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -437,6 +437,9 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
 PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
 
+PHP_REDIS_API void cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+    void *ctx);
+
 PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS,
     redisCluster *c, void *ctx);
 
diff --git a/library.c b/library.c
index 3c8f5c4dae..66d6178f0b 100644
--- a/library.c
+++ b/library.c
@@ -1160,6 +1160,18 @@ redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
     return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
 }
 
+PHP_REDIS_API int
+redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
+    FailableResultCallback cb;
+
+    /* Whether or not we have WITHSCORES */
+    ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR);
+
+    cb = ctx ? redis_mbulk_reply_zipped_keys_dbl : redis_sock_read_multibulk_reply;
+
+    return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
+}
+
 PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
     char *response;
     int response_len;
diff --git a/library.h b/library.h
index 036cb45615..268c838bff 100644
--- a/library.h
+++ b/library.h
@@ -68,6 +68,7 @@ PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
 PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret);
 PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret);
 PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
diff --git a/redis.c b/redis.c
index 016658f3d0..adfb682748 100644
--- a/redis.c
+++ b/redis.c
@@ -1906,77 +1906,38 @@ PHP_METHOD(Redis, zAdd) {
 }
 /* }}} */
 
-/* Handle ZRANGE and ZREVRANGE as they're the same except for keyword */
-static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw,
-                               zrange_cb fun)
-{
-    char *cmd;
-    int cmd_len;
-    RedisSock *redis_sock;
-    int withscores = 0;
-
-    if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
-        RETURN_FALSE;
-    }
-
-    if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd,
-           &cmd_len, &withscores, NULL, NULL) == FAILURE)
-    {
-        RETURN_FALSE;
-    }
-
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
-    if(withscores) {
-        if (IS_ATOMIC(redis_sock)) {
-            redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
-        }
-        REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl);
-    } else {
-        if (IS_ATOMIC(redis_sock)) {
-            if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                                               redis_sock, NULL, NULL) < 0)
-            {
-                RETURN_FALSE;
-            }
-        }
-        REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
-    }
-}
-
 /* {{{ proto array Redis::zRandMember(string key, array options) */
-PHP_METHOD(Redis, zRandMember)
-{
+PHP_METHOD(Redis, zRandMember) {
     REDIS_PROCESS_CMD(zrandmember, redis_zrandmember_response);
 }
 /* }}} */
 
 /* {{{ proto array Redis::zRange(string key,int start,int end,bool scores = 0) */
-PHP_METHOD(Redis, zRange)
-{
-    generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE",
-        redis_zrange_cmd);
+PHP_METHOD(Redis, zRange) {
+    REDIS_PROCESS_KW_CMD("ZRANGE", redis_zrange_cmd, redis_zrange_response);
 }
 /* }}} */
 
+PHP_METHOD(Redis, zrangestore) {
+    REDIS_PROCESS_KW_CMD("ZRANGESTORE", redis_zrange_cmd, redis_long_response);
+}
+
 /* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores = 0) */
 PHP_METHOD(Redis, zRevRange) {
-    generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE",
-        redis_zrange_cmd);
+    REDIS_PROCESS_KW_CMD("ZREVRANGE", redis_zrange_cmd, redis_zrange_response);
 }
 /* }}} */
 
 /* {{{ proto array Redis::zRangeByScore(string k,string s,string e,array opt) */
 PHP_METHOD(Redis, zRangeByScore) {
-    generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE",
-        redis_zrangebyscore_cmd);
+    REDIS_PROCESS_KW_CMD("ZRANGEBYSCORE", redis_zrange_cmd, redis_zrange_response);
 }
 /* }}} */
 
 /* {{{ proto array Redis::zRevRangeByScore(string key, string start, string end,
  *                                         array options) */
 PHP_METHOD(Redis, zRevRangeByScore) {
-    generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE",
-        redis_zrangebyscore_cmd);
+    REDIS_PROCESS_KW_CMD("ZREVRANGEBYSCORE", redis_zrange_cmd, redis_zrange_response);
 }
 /* }}} */
 
diff --git a/redis.stub.php b/redis.stub.php
index fd9ea3c2c6..51dd1fe34f 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -694,12 +694,68 @@ public function zPopMax(string $key, int $value = null): Redis|array|false;
 
     public function zPopMin(string $key, int $value = null): Redis|array|false;
 
-    public function zRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false;
+    /**
+     * Retreive a range of elements of a sorted set between a start and end point.
+     * How the command works in particular is greatly affected by the options that
+     * are passed in.
+     *
+     * @see https://https://redis.io/commands/zrange/
+     * @category zset
+     *
+     * @param string          $key     The sorted set in question.
+     * @param mixed           $start   The starting index we want to return.
+     * @param mixed           $end     The final index we want to return.
+     *
+     * @param array|bool|null $options This value may either be an array of options to pass to
+     *                                 the command, or for historical purposes a boolean which
+     *                                 controls just the 'WITHSCORES' option.
+     *
+     * @return Redis|array|false  An array with matching elements or false on failure.
+     *
+     * Detailed description of options array:
+     *
+     * 
+     *  true,     // Return both scores and members.
+     *     'LIMIT'      => [10, 10], // Start at offset 10 and return 10 elements.
+     *     'REV'                     // Return the elements in reverse order
+     *     'BYSCORE',                // Treat `start` and `end` as scores instead
+     *     'BYLEX'                   // Treat `start` and `end` as lexographical values.
+     * ];
+     * ?>
+     * 
+     *
+     * Note:  'BYLEX' and 'BYSCORE' are mutually exclusive.
+     *
+     */
+    public function zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null): Redis|array|false;
 
     public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false;
 
     public function zRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false;
 
+    /**
+     * This command is similar to ZRANGE except that instead of returning the values directly
+     * it will store them in a destination key provided by the user
+     *
+     * @see https://https://redis.io/commands/zrange/
+     * @see Redis::zRange
+     * @category zset
+     *
+     * @param string           $dstkey  The key to store the resulting element(s)
+     * @param string           $srckey  The source key with element(s) to retreive
+     * @param string           $start   The starting index to store
+     * @param string           $end     The ending index to store
+     * @param array|bool|null  $options Our options array that controls how the command will function.
+     *
+     * @return Redis|int|false The number of elements stored in dstkey or false on failure.
+     *
+     * See Redis::zRange for a full description of the possible options.
+     */
+    public function zrangestore(string $dstkey, string $srckey, string $start, string $end,
+                                array|bool|null $options = NULL): Redis|int|false;
+
     public function zRandMember(string $key, array $options = null): Redis|string|array;
 
     public function zRank(string $key, mixed $member): Redis|int|false;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 482ad347ef..38b11ccf48 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a27d28648f2d1a77237305083f36abc5e071f5b1 */
+ * Stub hash: 73bbd79b67c155a90acfa2b5ca1be49effdaf8ba */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -1000,9 +1000,9 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scores, IS_MIXED, 0, "null")
+	ZEND_ARG_TYPE_INFO(0, start, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByLex, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
@@ -1020,6 +1020,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByScore, 0
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zrangestore, 0, 4, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, dstkey, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+	ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "NULL")
+ZEND_END_ARG_INFO()
+
 #define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRank, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
@@ -1043,7 +1051,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_zCount
 
-#define arginfo_class_Redis_zRevRange arginfo_class_Redis_zRange
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scores, IS_MIXED, 0, "null")
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex
 
@@ -1314,6 +1327,7 @@ ZEND_METHOD(Redis, zPopMin);
 ZEND_METHOD(Redis, zRange);
 ZEND_METHOD(Redis, zRangeByLex);
 ZEND_METHOD(Redis, zRangeByScore);
+ZEND_METHOD(Redis, zrangestore);
 ZEND_METHOD(Redis, zRandMember);
 ZEND_METHOD(Redis, zRank);
 ZEND_METHOD(Redis, zRem);
@@ -1559,6 +1573,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zrangestore, arginfo_class_Redis_zrangestore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRandMember, arginfo_class_Redis_zRandMember, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index 03e85d844b..c5078521a0 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1429,61 +1429,31 @@ PHP_METHOD(RedisCluster, setrange) {
 }
 /* }}} */
 
-/* Generic implementation for ZRANGE, ZREVRANGE, ZRANGEBYSCORE, ZREVRANGEBYSCORE */
-static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw,
-                               zrange_cb fun)
-{
-    redisCluster *c = GET_CONTEXT();
-    c->readonly = CLUSTER_IS_ATOMIC(c);
-    cluster_cb cb;
-    char *cmd; int cmd_len; short slot;
-    int withscores = 0;
-
-    if (fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,
-           &withscores, &slot, NULL) == FAILURE)
-    {
-        efree(cmd);
-        RETURN_FALSE;
-    }
-
-    if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) {
-        efree(cmd);
-        RETURN_FALSE;
-    }
-
-    efree(cmd);
-
-    cb = withscores ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp;
-    if (CLUSTER_IS_ATOMIC(c)) {
-        cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
-    } else {
-        void *ctx = NULL;
-        CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx);
-        RETURN_ZVAL(getThis(), 1, 0);
-    }
-}
-
 /* {{{ proto
  *     array RedisCluster::zrange(string k, long s, long e, bool score = 0) */
 PHP_METHOD(RedisCluster, zrange) {
-    generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE",
-        redis_zrange_cmd);
+    CLUSTER_PROCESS_KW_CMD("ZRANGE", redis_zrange_cmd, cluster_zrange_resp, 1);
 }
 /* }}} */
 
+/* {{{ proto
+ *     array RedisCluster::zrange(string $dstkey, string $srckey, long s, long e, array|bool $options = false) */
+PHP_METHOD(RedisCluster, zrangestore) {
+    CLUSTER_PROCESS_KW_CMD("ZRANGESTORE", redis_zrange_cmd, cluster_long_resp, 0);
+}
+
+/* }}} */
 /* {{{ proto
  *     array RedisCluster::zrevrange(string k,long s,long e,bool scores = 0) */
 PHP_METHOD(RedisCluster, zrevrange) {
-    generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE",
-        redis_zrange_cmd);
+    CLUSTER_PROCESS_KW_CMD("ZREVRANGE", redis_zrange_cmd, cluster_zrange_resp, 1);
 }
 /* }}} */
 
 /* {{{ proto array
  *     RedisCluster::zrangebyscore(string k, long s, long e, array opts) */
 PHP_METHOD(RedisCluster, zrangebyscore) {
-    generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE",
-        redis_zrangebyscore_cmd);
+    CLUSTER_PROCESS_KW_CMD("ZRANGEBYSCORE", redis_zrange_cmd, cluster_zrange_resp, 1);
 }
 /* }}} */
 
@@ -1516,8 +1486,7 @@ PHP_METHOD(RedisCluster, zrem) {
 /* {{{ proto array
  *     RedisCluster::zrevrangebyscore(string k, long s, long e, array opts) */
 PHP_METHOD(RedisCluster, zrevrangebyscore) {
-    generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE",
-        redis_zrangebyscore_cmd);
+    CLUSTER_PROCESS_KW_CMD("ZREVRANGEBYSCORE", redis_zrange_cmd, cluster_zrange_resp, 1);
 }
 /* }}} */
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 598a8126cf..165dfd3b9d 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -412,7 +412,16 @@ public function zpopmax(string $key, int $value = null): RedisCluster|bool|array
 
     public function zpopmin(string $key, int $value = null): RedisCluster|bool|array;
 
-    public function zrange(string $key, int $start, int $end, mixed $options_withscores = null): RedisCluster|array|bool;
+    /**
+     * @see Redis::zrange
+     */
+    public function zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null): RedisCluster|array|bool;
+
+    /**
+     * @see Redis::zrangestore
+     */
+    public function zrangestore(string $dstkey, string $srckey, int $start, int $end,
+                                array|bool|null $options = null): RedisCluster|int|false;
 
     public function zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1): RedisCluster|array|false;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index b6bde9f759..0b4619fb76 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7a2f14794870618a17273a2ed9f60d81449c82af */
+ * Stub hash: e761b2a65f9f57254e0201f9643b823e79e2a0a8 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -864,9 +864,17 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrange, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_MIXED, 0)
+	ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangestore, 0, 4, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, dstkey, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options_withscores, IS_MIXED, 0, "null")
+	ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangebylex, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
@@ -1122,6 +1130,7 @@ ZEND_METHOD(RedisCluster, zlexcount);
 ZEND_METHOD(RedisCluster, zpopmax);
 ZEND_METHOD(RedisCluster, zpopmin);
 ZEND_METHOD(RedisCluster, zrange);
+ZEND_METHOD(RedisCluster, zrangestore);
 ZEND_METHOD(RedisCluster, zrangebylex);
 ZEND_METHOD(RedisCluster, zrangebyscore);
 ZEND_METHOD(RedisCluster, zrank);
@@ -1327,6 +1336,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrangestore, arginfo_class_RedisCluster_zrangestore, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index db11ebf59c..cc73a26213 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7a2f14794870618a17273a2ed9f60d81449c82af */
+ * Stub hash: e761b2a65f9f57254e0201f9643b823e79e2a0a8 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -743,7 +743,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrange, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
-	ZEND_ARG_INFO(0, options_withscores)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangestore, 0, 0, 4)
+	ZEND_ARG_INFO(0, dstkey)
+	ZEND_ARG_INFO(0, srckey)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3)
@@ -754,12 +762,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3)
 	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebyscore, 0, 0, 3)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, start)
-	ZEND_ARG_INFO(0, end)
-	ZEND_ARG_INFO(0, options)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster_zrangebyscore arginfo_class_RedisCluster_zrange
 
 #define arginfo_class_RedisCluster_zrank arginfo_class_RedisCluster_hexists
 
@@ -979,6 +982,7 @@ ZEND_METHOD(RedisCluster, zlexcount);
 ZEND_METHOD(RedisCluster, zpopmax);
 ZEND_METHOD(RedisCluster, zpopmin);
 ZEND_METHOD(RedisCluster, zrange);
+ZEND_METHOD(RedisCluster, zrangestore);
 ZEND_METHOD(RedisCluster, zrangebylex);
 ZEND_METHOD(RedisCluster, zrangebyscore);
 ZEND_METHOD(RedisCluster, zrank);
@@ -1184,6 +1188,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, zrangestore, arginfo_class_RedisCluster_zrangestore, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index a0a55151f4..08992c2c52 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -72,6 +72,26 @@ typedef struct redisRestoreOptions {
     zend_long freq;
 } redisRestoreOptions;
 
+#define REDIS_ZRANGE_HAS_DST_KEY      (1 << 0)
+#define REDIS_ZRANGE_HAS_WITHSCORES   (1 << 1)
+#define REDIS_ZRANGE_HAS_BY_LEX_SCORE (1 << 2)
+#define REDIS_ZRANGE_HAS_REV          (1 << 3)
+#define REDIS_ZRANGE_HAS_LIMIT        (1 << 4)
+#define REDIS_ZRANGE_INT_RANGE        (1 << 5)
+
+/* ZRANGE, ZRANGEBYSCORE, ZRANGESTORE options */
+typedef struct redisZrangeOptions {
+    zend_bool withscores;
+    zend_bool byscore;
+    zend_bool bylex;
+    zend_bool rev;
+    struct {
+        zend_bool enabled;
+        zend_long offset;
+        zend_long count;
+    } limit;
+} redisZrangeOptions;
+
 /* Local passthrough macro for command construction.  Given that these methods
  * are generic (so they work whether the caller is Redis or RedisCluster) we
  * will always have redis_sock, slot*, and */
@@ -612,119 +632,184 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
     return cmdstr.len;
 }
 
-/* ZRANGE/ZREVRANGE */
-int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                     char *kw, char **cmd, int *cmd_len, int *withscores,
-                     short *slot, void **ctx)
-{
-    char *key;
-    size_t key_len;
-    zend_long start, end;
-    zend_string *zkey;
-    zval *z_ws = NULL, *z_ele;
+void redis_get_zrange_options(redisZrangeOptions *dst, zval *src, int flags) {
+    zval *zv, *zoff, *zcnt;
+    zend_string *key;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|z!", &key, &key_len,
-                             &start, &end, &z_ws) == FAILURE)
-    {
-        return FAILURE;
+    ZEND_ASSERT(dst != NULL);
+
+    memset(dst, 0, sizeof(*dst));
+
+    if (src == NULL)
+        return;
+
+    if (Z_TYPE_P(src) != IS_ARRAY) {
+        if (Z_TYPE_P(src) == IS_TRUE && (flags & REDIS_ZRANGE_HAS_WITHSCORES))
+            dst->withscores = 1;
+        return;
     }
 
-    // Clear withscores arg
-    *withscores = 0;
+    ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(src), key, zv) {
+        ZVAL_DEREF(zv);
 
-    /* Accept ['withscores' => true], or the legacy `true` value */
-    if (z_ws) {
-        if (Z_TYPE_P(z_ws) == IS_ARRAY) {
-            ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_ws), zkey, z_ele) {
-                if (zkey != NULL) {
-                    ZVAL_DEREF(z_ele);
-                    if (zend_string_equals_literal_ci(zkey, "withscores")) {
-                        *withscores = zval_is_true(z_ele);
-                        break;
-                    }
+        if (key) {
+            if ((flags & REDIS_ZRANGE_HAS_WITHSCORES) && zend_string_equals_literal_ci(key, "WITHSCORES"))
+                dst->withscores = zval_is_true(zv);
+            else if ((flags & REDIS_ZRANGE_HAS_LIMIT) && zend_string_equals_literal_ci(key, "LIMIT") &&
+                     Z_TYPE_P(zv) == IS_ARRAY)
+            {
+                if ((zoff = zend_hash_index_find(Z_ARRVAL_P(zv), 0)) != NULL &&
+                    (zcnt = zend_hash_index_find(Z_ARRVAL_P(zv), 1)) != NULL)
+                {
+                    dst->limit.enabled = 1;
+                    dst->limit.offset = zval_get_long(zoff);
+                    dst->limit.count = zval_get_long(zcnt);
+                } else {
+                    php_error_docref(NULL, E_WARNING, "LIMIT offset and count must be an array with twe elements");
                 }
-            } ZEND_HASH_FOREACH_END();
-        } else if (Z_TYPE_P(z_ws) == IS_TRUE) {
-            *withscores = Z_TYPE_P(z_ws) == IS_TRUE;
+            }
+        } else if (Z_TYPE_P(zv) == IS_STRING) {
+            key = Z_STR_P(zv);
+
+            if ((flags & REDIS_ZRANGE_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYSCORE"))
+                dst->byscore = 1, dst->bylex = 0;
+            else if ((flags & REDIS_ZRANGE_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYLEX"))
+                dst->bylex = 1, dst->byscore = 0;
+            else if ((flags & REDIS_ZRANGE_HAS_REV) && zend_string_equals_literal_ci(key, "REV"))
+                dst->rev = 1;
+            else if ((flags & REDIS_ZRANGE_HAS_WITHSCORES && zend_string_equals_literal_ci(key, "WITHSCORES")))
+                dst->withscores = 1;
         }
-    }
+    } ZEND_HASH_FOREACH_END();
+}
 
-    if (*withscores) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdds", key, key_len, start, end,
-            "WITHSCORES", sizeof("WITHSCORES") - 1);
-    } else {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdd", key, key_len, start, end);
+// + ZRANGE               key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count] [WITHSCORES]
+// + ZRANGESTORE      dst src   min  max [BYSCORE | BYLEX] [REV] [LIMIT offset count]
+// + ZREVRANGE            key start stop                                              [WITHSCORES]
+// + ZRANGEBYSCORE        key   min  max                         [LIMIT offset count] [WITHSCORES]
+// + ZREVRANGEBYSCORE     key   max  min                         [LIMIT offset count] [WITHSCORES]
+// - ZRANGEBYLEX          key   min  max                         [LIMIT offset count]
+// - ZREVRANGEBYLEX       key   max  min                         [LIMIT offset count]
+static int redis_get_zrange_cmd_flags(const char *kw) {
+    size_t len = strlen(kw);
+
+    if (REDIS_STRICMP_STATIC(kw, len, "ZRANGESTORE")) {
+        return REDIS_ZRANGE_HAS_DST_KEY |
+               REDIS_ZRANGE_HAS_WITHSCORES |
+               REDIS_ZRANGE_HAS_BY_LEX_SCORE |
+               REDIS_ZRANGE_HAS_REV |
+               REDIS_ZRANGE_HAS_LIMIT;
+    } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGE")) {
+        return REDIS_ZRANGE_HAS_WITHSCORES |
+               REDIS_ZRANGE_HAS_BY_LEX_SCORE |
+               REDIS_ZRANGE_HAS_REV |
+               REDIS_ZRANGE_HAS_LIMIT;
+    } else if (REDIS_STRICMP_STATIC(kw, len, "ZREVRANGE")) {
+        return REDIS_ZRANGE_HAS_WITHSCORES |
+               REDIS_ZRANGE_INT_RANGE;
+    } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGEBYSCORE") ||
+               REDIS_STRICMP_STATIC(kw, len, "ZREVRANGEBYSCORE"))
+    {
+        return REDIS_ZRANGE_HAS_LIMIT |
+               REDIS_ZRANGE_HAS_WITHSCORES;
+    } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGEBYLEX") ||
+               REDIS_STRICMP_STATIC(kw, len, "ZREVRANGEBYLEX"))
+    {
+        return REDIS_ZRANGE_HAS_LIMIT;
     }
 
-    return SUCCESS;
+    /* Reaching this line means a compile-time error */
+    ZEND_ASSERT(0);
 }
 
-int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                            char *kw, char **cmd, int *cmd_len, int *withscores,
-                            short *slot, void **ctx)
+/* Validate ZLEX* min/max argument strings */
+#define validate_zlex_arg_zstr(zs_) validate_zlex_arg(ZSTR_VAL((zs_)), ZSTR_LEN((zs_)))
+static int validate_zlex_arg(const char *arg, size_t len) {
+    return (len  > 1 && (*arg == '[' || *arg == '(')) ||
+           (len == 1 && (*arg == '+' || *arg == '-'));
+}
+
+int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                     char *kw, char **cmd, int *cmd_len, short *slot,
+                     void **ctx)
 {
-    char *key, *start, *end;
-    int has_limit = 0;
-    long offset, count;
-    size_t key_len, start_len, end_len;
-    zval *z_opt=NULL, *z_ele;
-    zend_string *zkey;
-    HashTable *ht_opt;
+    zend_string *dst = NULL, *src = NULL, *sstart = NULL, *send = NULL;
+    struct redisZrangeOptions opt;
+    zend_long start = 0, end = 0;
+    smart_string cmdstr = {0};
+    zval *zoptions = NULL;
+    int min_argc, flags;
+    short slot2;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len,
-                             &start, &start_len, &end, &end_len, &z_opt)
-                             ==FAILURE)
-    {
-        return FAILURE;
+    flags = redis_get_zrange_cmd_flags(kw);
+
+    min_argc = 3 + (flags & REDIS_ZRANGE_HAS_DST_KEY);
+    ZEND_PARSE_PARAMETERS_START(min_argc, min_argc + 1)
+        if (flags & REDIS_ZRANGE_HAS_DST_KEY) {
+            Z_PARAM_STR(dst)
+        }
+        Z_PARAM_STR(src)
+        if (flags & REDIS_ZRANGE_INT_RANGE) {
+            Z_PARAM_LONG(start)
+            Z_PARAM_LONG(end)
+        } else {
+            Z_PARAM_STR(sstart)
+            Z_PARAM_STR(send)
+        }
+        Z_PARAM_OPTIONAL
+        Z_PARAM_ZVAL_OR_NULL(zoptions)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    redis_get_zrange_options(&opt, zoptions, flags);
+
+    if (opt.bylex) {
+        ZEND_ASSERT(!(flags & REDIS_ZRANGE_INT_RANGE));
+        if (!validate_zlex_arg_zstr(sstart) || !validate_zlex_arg_zstr(send)) {
+            php_error_docref(NULL, E_WARNING, "Legographical args must start with '[' or '(' or be '+' or '-'");
+            return FAILURE;
+        }
     }
 
-    // Check for an options array
-    if (z_opt && Z_TYPE_P(z_opt) == IS_ARRAY) {
-        ht_opt = Z_ARRVAL_P(z_opt);
-        ZEND_HASH_FOREACH_STR_KEY_VAL(ht_opt, zkey, z_ele) {
-           /* All options require a string key type */
-           if (!zkey) continue;
-           ZVAL_DEREF(z_ele);
-           /* Check for withscores and limit */
-           if (zend_string_equals_literal_ci(zkey, "withscores")) {
-               *withscores = zval_is_true(z_ele);
-           } else if (zend_string_equals_literal_ci(zkey, "limit") && Z_TYPE_P(z_ele) == IS_ARRAY) {
-                HashTable *htlimit = Z_ARRVAL_P(z_ele);
-                zval *zoff, *zcnt;
-
-                /* We need two arguments (offset and count) */
-                if ((zoff = zend_hash_index_find(htlimit, 0)) != NULL &&
-                    (zcnt = zend_hash_index_find(htlimit, 1)) != NULL
-                ) {
-                    /* Set our limit if we can get valid longs from both args */
-                    offset = zval_get_long(zoff);
-                    count = zval_get_long(zcnt);
-                    has_limit = 1;
-                }
-           }
-        } ZEND_HASH_FOREACH_END();
+    redis_cmd_init_sstr(&cmdstr, min_argc + !!opt.bylex + !!opt.byscore +
+                                 !!opt.rev + !!opt.withscores +
+                                 (opt.limit.enabled ? 3 : 0), kw, strlen(kw));
+
+    if (flags & REDIS_ZRANGE_HAS_DST_KEY)
+        redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot);
+    redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, &slot2);
+
+    /* Protect the user from crossslot errors */
+    if ((flags & REDIS_ZRANGE_HAS_DST_KEY) && slot && *slot != slot2) {
+        php_error_docref(NULL, E_WARNING, "destination and source keys must map to the same slot");
+        efree(cmdstr.c);
+        return FAILURE;
     }
 
-    // Construct our command
-    if (*withscores) {
-        if (has_limit) {
-            *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssdds", key, key_len,
-                start, start_len, end, end_len, "LIMIT", 5, offset, count,
-                "WITHSCORES", 10);
-        } else {
-            *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksss", key, key_len, start,
-                start_len, end, end_len, "WITHSCORES", 10);
-        }
+    if (flags & REDIS_ZRANGE_INT_RANGE) {
+        redis_cmd_append_sstr_long(&cmdstr, start);
+        redis_cmd_append_sstr_long(&cmdstr, end);
     } else {
-        if (has_limit) {
-            *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssdd", key, key_len, start,
-                start_len, end, end_len, "LIMIT", 5, offset, count);
-        } else {
-            *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, start,
-                start_len, end, end_len);
-        }
+        redis_cmd_append_sstr_zstr(&cmdstr, sstart);
+        redis_cmd_append_sstr_zstr(&cmdstr, send);
+    }
+
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.byscore, "BYSCORE");
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.bylex, "BYLEX");
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.rev, "REV");
+
+    if (opt.limit.enabled) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT");
+        redis_cmd_append_sstr_long(&cmdstr, opt.limit.offset);
+        redis_cmd_append_sstr_long(&cmdstr, opt.limit.count);
     }
 
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withscores, "WITHSCORES");
+
+    if (slot) *slot = slot2;
+    *ctx = opt.withscores ? PHPREDIS_CTX_PTR : NULL;
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
     return SUCCESS;
 }
 
@@ -1414,14 +1499,9 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     /* min and max must start with '(' or '[', or be either '-' or '+' */
-    if (min_len < 1 || max_len < 1 ||
-       (min[0] != '(' && min[0] != '[' &&
-       (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) ||
-       (max[0] != '(' && max[0] != '[' &&
-       (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1)))
-    {
-        php_error_docref(0, E_WARNING,
-            "min and max arguments must start with '[' or '('");
+    if (!validate_zlex_arg(min, min_len) || !validate_zlex_arg(max, max_len)) {
+        php_error_docref(NULL, E_WARNING,
+            "Min/Max args can be '-' or '+', or start with '[' or '('");
         return FAILURE;
     }
 
@@ -1437,12 +1517,6 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-/* Validate ZLEX* min/max argument strings */
-static int validate_zlex_arg(const char *arg, size_t len) {
-    return (len  > 1 && (*arg == '[' || *arg == '(')) ||
-           (len == 1 && (*arg == '+' || *arg == '-'));
-}
-
 /* ZLEXCOUNT/ZREMRANGEBYLEX */
 int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                        char *kw, char **cmd, int *cmd_len, short *slot,
diff --git a/redis_commands.h b/redis_commands.h
index 50512a3dce..4d7a13e03d 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -103,12 +103,7 @@ typedef int (*zrange_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                          char *,char**,int*,int*,short*,void**);
 
 int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char *kw, char **cmd, int *cmd_len, int *withscores, short *slot,
-    void **ctx);
-
-int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char *kw, char **cmd, int *cmd_len, int *withscores, short *slot,
-    void **ctx);
+    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
 int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 2bdf5e1249..b5b39586c9 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: a27d28648f2d1a77237305083f36abc5e071f5b1 */
+ * Stub hash: 73bbd79b67c155a90acfa2b5ca1be49effdaf8ba */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -858,7 +858,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
-	ZEND_ARG_INFO(0, scores)
+	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 0, 3)
@@ -869,8 +869,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 0, 3)
 	ZEND_ARG_INFO(0, count)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 0, 3)
-	ZEND_ARG_INFO(0, key)
+#define arginfo_class_Redis_zRangeByScore arginfo_class_Redis_zRange
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zrangestore, 0, 0, 4)
+	ZEND_ARG_INFO(0, dstkey)
+	ZEND_ARG_INFO(0, srckey)
 	ZEND_ARG_INFO(0, start)
 	ZEND_ARG_INFO(0, end)
 	ZEND_ARG_INFO(0, options)
@@ -888,11 +891,16 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_getRange
 
-#define arginfo_class_Redis_zRevRange arginfo_class_Redis_zRange
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, scores)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex
 
-#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRangeByScore
+#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRange
 
 #define arginfo_class_Redis_zRevRank arginfo_class_Redis_hExists
 
@@ -1151,6 +1159,7 @@ ZEND_METHOD(Redis, zPopMin);
 ZEND_METHOD(Redis, zRange);
 ZEND_METHOD(Redis, zRangeByLex);
 ZEND_METHOD(Redis, zRangeByScore);
+ZEND_METHOD(Redis, zrangestore);
 ZEND_METHOD(Redis, zRandMember);
 ZEND_METHOD(Redis, zRank);
 ZEND_METHOD(Redis, zRem);
@@ -1396,6 +1405,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, zrangestore, arginfo_class_Redis_zrangestore, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRandMember, arginfo_class_Redis_zRandMember, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 52b863304a..149bbb84ed 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2566,6 +2566,9 @@ public function testZX() {
         $this->assertTrue(['val1', 'val2'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]]));
         $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]]));
 
+        if ($this->minVersionCheck('6.2.0'))
+            $this->assertEquals(['val0', 'val1'], $this->redis->zrange('key', 0, 1, ['byscore', 'limit' => [0, 100]]));
+
         // limits as references
         $limit = [0, 100];
         foreach ($limit as &$val) {}
@@ -2576,6 +2579,17 @@ public function testZX() {
         $this->assertTrue(['val2', 'val1'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]]));
         $this->assertTrue(['val1', 'val0'] === $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]]));
 
+        if ($this->minVersionCheck('6.2.0')) {
+            $this->assertEquals(['val1', 'val0'], $this->redis->zrange('key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]]));
+            $this->assertEquals(2, $this->redis->zrangestore('dst{key}', 'key', 1, 0,
+                                ['byscore', 'rev', 'limit' => [0, 100]]));
+            $this->assertEquals(['val0', 'val1'], $this->redis->zRange('dst{key}', 0, -1));
+
+            $this->assertEquals(1, $this->redis->zrangestore('dst{key}', 'key', 1, 0,
+                                ['byscore', 'rev', 'limit' => [0, 1]]));
+            $this->assertEquals(['val1'], $this->redis->zrange('dst{key}', 0, -1));
+        }
+
         $this->assertTrue(4 === $this->redis->zCard('key'));
         $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1'));
         $this->assertFalse($this->redis->zScore('key', 'val'));
@@ -2597,7 +2611,6 @@ public function testZX() {
         $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2));
         $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2'));
 
-
         // zincrby
         $this->redis->del('key');
         $this->assertTrue(1.0 === $this->redis->zIncrBy('key', 1, 'val1'));
@@ -2817,12 +2830,22 @@ public function testZRangeByLex() {
             $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']);
+        $this->assertEquals(['a', 'b', 'c'], $this->redis->zRangeByLex('key', '-', '[c'));
+        $this->assertEquals(['f', 'g'], $this->redis->zRangeByLex('key', '(e', '+'));
+
 
         // with limit offset
-        $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c', 1, 2), ['b', 'c'] );
-        $this->assertEquals($this->redis->zRangeByLex('key', '-', '(c', 1, 2), ['b']);
+        $this->assertEquals(['b', 'c'], $this->redis->zRangeByLex('key', '-', '[c', 1, 2) );
+        $this->assertEquals(['b'], $this->redis->zRangeByLex('key', '-', '(c', 1, 2));
+
+        /* Test getting the same functionality via ZRANGE and options */
+        if ($this->minVersionCheck("6.2.0")) {
+            $this->assertEquals(['a','b','c'], $this->redis->zRange('key', '-', '[c', ['BYLEX']));
+            $this->assertEquals(['b', 'c'], $this->redis->zRange('key', '-', '[c', ['BYLEX', 'LIMIT' => [1, 2]]));
+            $this->assertEquals(['b'], $this->redis->zRange('key', '-', '(c', ['BYLEX', 'LIMIT' => [1, 2]]));
+
+            $this->assertEquals(['b', 'a'], $this->redis->zRange('key', '[c', '-', ['BYLEX', 'REV', 'LIMIT' => [1, 2]]));
+        }
     }
 
     public function testZLexCount() {

From 1343f5008301a256fcbfa0161931e2a39791055f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 22 Oct 2022 12:46:26 -0700
Subject: [PATCH 1657/1986] XGROUP DELCONSUMER and ENTRIESREAD

Refactor XGROUP and implement the new DELCONSUMER (Redis 6.2.0) and
ENTRIESREAD (Redis 7.0.0) options.  Additionally, add a proper phpdoc
block to the stub file.

See #2068
---
 redis.stub.php         | 39 +++++++++++++++++-
 redis_arginfo.h        |  9 +++--
 redis_commands.c       | 90 ++++++++++++++++++++++++++----------------
 redis_legacy_arginfo.h |  9 +++--
 tests/RedisTest.php    | 34 ++++++++++++++++
 5 files changed, 137 insertions(+), 44 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 51dd1fe34f..3c3c78e570 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -660,7 +660,44 @@ public function xclaim(string $key, string $group, string $consumer, int $min_id
 
     public function xdel(string $key, array $ids): Redis|int|false;
 
-    public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
+    /**
+     * XGROUP
+     *
+     * Perform various operation on consumer groups for a particular Redis STREAM.
+     * What the command does is primarily based on which operation is passed.
+     *
+     * @see https://redis.io/commands/xgroup/
+     *
+     * @param string $operation      The subcommand you intend to execute.  Valid options are as follows
+     *                               'HELP'          - Redis will return information about the command
+     *                                                 Requires: none
+     *                               'CREATE'        - Create a consumer group.
+     *                                                 Requires:  Key, group, consumer.
+     *                               'SETID'         - Set the ID of an existing consumer group for the stream.
+     *                                                 Requires:  Key, group, id.
+     *                               'CREATECONSUMER - Create a new consumer group for the stream.  You must
+     *                                                 also pass key, group, and the consumer name you wish to
+     *                                                 create.
+     *                                                 Requires:  Key, group, consumer.
+     *                               'DELCONSUMER'   - Delete a consumer from group attached to the stream.
+     *                                                 Requires:  Key, group, consumer.
+     *                               'DESTROY'       - Delete a consumer group from a stream.
+     *                                                  Requires:  Key, group.
+     * @param string $key            The STREAM we're operating on.
+     * @param string $group          The consumer group wse want to create/modify/delete.
+     * @param string $id_or_consumer The STREAM id (e.g. '$') or consumer group.  See the operation section
+     *                               for information about which to send.
+     * @param bool   $mkstream       This flag may be sent in combination with the 'CREATE' operation, and
+     *                               cause Redis to also create the STREAM if it doesn't currently exist.
+     *
+     * @param bool   $entriesread    Allows you to set Redis's 'entries-read' STREAM value.  This argument is
+     *                               only relevant to the 'CREATE' and 'SETID' operations.
+     *                               Note:  Requires Redis >= 7.0.0.
+     *
+     * @return mixed                 This command return various results depending on the operation performed.
+     */
+    public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null,
+                           bool $mkstream = false, int $entries_read = -2): mixed;
 
     public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 38b11ccf48..7d3509106d 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 73bbd79b67c155a90acfa2b5ca1be49effdaf8ba */
+ * Stub hash: c04531e86379ab5c0de12e8e82868b7c7f024068 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -909,9 +909,10 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xgroup, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg3, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xinfo, 0, 1, IS_MIXED, 0)
diff --git a/redis_commands.c b/redis_commands.c
index 08992c2c52..416972d618 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5817,52 +5817,72 @@ int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 }
 
 /* XGROUP HELP
- * XGROUP CREATE key groupname id [MKSTREAM]
- * XGROUP SETID key group id
- * XGROUP DESTROY key groupname
- * XGROUP DELCONSUMER key groupname consumername */
+ * XGROUP CREATE          key group id       [MKSTREAM] [ENTRIESREAD ]
+ * XGROUP SETID           key group id                  [ENTRIESREAD ]
+ * XGROUP CREATECONSUMER  key group consumer
+ * XGROUP DELCONSUMER     key group consumer
+ * XGROUP DESTROY         key group
+ */
 int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                      char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    char *op, *key = NULL, *arg1 = NULL, *arg2 = NULL;
-    size_t oplen, keylen, arg1len, arg2len;
+    zend_string *op = NULL, *key = NULL, *group = NULL, *id_or_consumer = NULL;
+    int nargs, is_create = 0, is_setid = 0;
+    zend_long entries_read = -2;
+    smart_string cmdstr = {0};
     zend_bool mkstream = 0;
-    int argc = ZEND_NUM_ARGS();
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sssb", &op, &oplen,
-                              &key, &keylen, &arg1, &arg1len, &arg2, &arg2len,
-                              &mkstream) == FAILURE)
+    ZEND_PARSE_PARAMETERS_START(1, 6)
+        Z_PARAM_STR(op)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_STR(key)
+        Z_PARAM_STR(group)
+        Z_PARAM_STR(id_or_consumer)
+        Z_PARAM_BOOL(mkstream)
+        Z_PARAM_LONG(entries_read)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    if (zend_string_equals_literal_ci(op, "HELP")) {
+        nargs = 0;
+    } else if ((is_create = zend_string_equals_literal_ci(op, "CREATE")) ||
+               (is_setid  = zend_string_equals_literal_ci(op, "SETID")) ||
+                            zend_string_equals_literal_ci(op, "CREATECONSUMER") ||
+                            zend_string_equals_literal_ci(op, "DELCONSUMER"))
     {
+        nargs = 3;
+    } else if (zend_string_equals_literal_ci(op, "DESTROY")) {
+        nargs = 2;
+    } else {
+        php_error_docref(NULL, E_WARNING, "Unknown XGROUP operation '%s'", ZSTR_VAL(op));
         return FAILURE;
     }
 
-    if (argc == 1 && oplen == 4 && !strncasecmp(op, "HELP", 4)) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "s", "HELP", 4);
-        return SUCCESS;
-    } else if (argc >= 4 && (oplen == 6 && !strncasecmp(op, "CREATE", 6))) {
-        if (mkstream) {
-            *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "sksss", op, oplen, key, keylen,
-                                          arg1, arg1len, arg2, arg2len, "MKSTREAM",
-                                          sizeof("MKSTREAM") - 1);
-        } else {
-            *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "skss", op, oplen, key, keylen,
-                                          arg1, arg1len, arg2, arg2len);
-        }
-        return SUCCESS;
-    } else if (argc == 4 && ((oplen == 5 && !strncasecmp(op, "SETID", 5)) ||
-                             (oplen == 11 && !strncasecmp(op, "DELCONSUMER", 11))))
-    {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "skss", op, oplen, key, keylen,
-                                      arg1, arg1len, arg2, arg2len);
-        return SUCCESS;
-    } else if (argc == 3 && ((oplen == 7 && !strncasecmp(op, "DESTROY", 7)))) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "sks", op, oplen, key,
-                                      keylen, arg1, arg1len);
-        return SUCCESS;
+    if (ZEND_NUM_ARGS() < nargs) {
+        php_error_docref(NULL, E_WARNING, "Operation '%s' requires %d arguments", ZSTR_VAL(op), nargs);
+        return FAILURE;
     }
 
-    /* Didn't detect any valid XGROUP command pattern */
-    return FAILURE;
+    mkstream &= is_create;
+    if (!(is_create || is_setid))
+        entries_read = -2;
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + nargs + !!mkstream + (entries_read != -2 ? 2 : 0), "XGROUP");
+    redis_cmd_append_sstr_zstr(&cmdstr, op);
+
+    if (nargs-- > 0) redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
+    if (nargs-- > 0) redis_cmd_append_sstr_zstr(&cmdstr, group);
+    if (nargs-- > 0) redis_cmd_append_sstr_zstr(&cmdstr, id_or_consumer);
+
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, !!mkstream, "MKSTREAM");
+    if (entries_read != -2) {
+        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ENTRIESREAD");
+        redis_cmd_append_sstr_long(&cmdstr, entries_read);
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
 }
 
 /* XINFO CONSUMERS key group
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index b5b39586c9..0b6fbd2340 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 73bbd79b67c155a90acfa2b5ca1be49effdaf8ba */
+ * Stub hash: c04531e86379ab5c0de12e8e82868b7c7f024068 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -769,9 +769,10 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xgroup, 0, 0, 1)
 	ZEND_ARG_INFO(0, operation)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, arg1)
-	ZEND_ARG_INFO(0, arg2)
-	ZEND_ARG_INFO(0, arg3)
+	ZEND_ARG_INFO(0, group)
+	ZEND_ARG_INFO(0, id_or_consumer)
+	ZEND_ARG_INFO(0, mkstream)
+	ZEND_ARG_INFO(0, entries_read)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xinfo, 0, 0, 1)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 149bbb84ed..ea036ebc75 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6521,6 +6521,40 @@ public function testXGroup() {
         $this->assertFalse($this->redis->xGroup('SETID', 's', 'mygroup', 'BAD_ID'));
 
         $this->assertEquals($this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer'),0);
+
+        if (!$this->minVersionCheck('6.2.0'))
+            return;
+
+        /* CREATECONSUMER */
+        $this->assertTrue($this->redis->del('s'));
+        $this->assertTrue($this->redis->xgroup('create', 's', 'mygroup', '$', true));
+        for ($i = 0; $i < 3; $i++) {
+            $this->assertTrue($this->redis->xgroup('createconsumer', 's', 'mygroup', "c:$i"));
+            $info = $this->redis->xinfo('consumers', 's', 'mygroup');
+            $this->assertTrue(is_array($info) && count($info) == $i + 1);
+            for ($j = 0; $j <= $i; $j++) {
+                $this->assertTrue(isset($info[$j]) && isset($info[$j]['name']) && $info[$j]['name'] == "c:$j");
+            }
+        }
+
+        /* Make sure we don't erroneously send options that don't belong to the operation */
+        $this->assertTrue($this->redis->xGroup('CREATECONSUMER', 's', 'mygroup', 'fake-consumer', true, 1337));
+
+        /* Make sure we handle the case where the user doesn't send enough arguments */
+        $this->redis->clearLastError();
+        $this->assertFalse(@$this->redis->xGroup('CREATECONSUMER'));
+        $this->assertEquals(NULL, $this->redis->getLastError());
+        $this->assertFalse(@$this->redis->xGroup('create'));
+        $this->assertEquals(NULL, $this->redis->getLastError());
+
+        if (!$this->minVersionCheck('7.0.0'))
+            return;
+
+        /* ENTRIESREAD */
+        $this->assertTrue($this->redis->del('s'));
+        $this->assertTrue($this->redis->xGroup('create', 's', 'mygroup', '$', true, 1337));
+        $info = $this->redis->xinfo('groups', 's');
+        $this->assertTrue(isset($info[0]['entries-read']) && 1337 == (int)$info[0]['entries-read']);
     }
 
     public function testXAck() {

From 8c7c5a3aa2c6b693839a391bffa18ee161cbe73a Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 23 Oct 2022 11:38:29 -0700
Subject: [PATCH 1658/1986] Refactor SORT and add SORT_RO command

See #2068
---
 redis.c                        | 25 ++--------
 redis.stub.php                 | 89 +++++++++++++++++++++++++---------
 redis_arginfo.h                |  6 ++-
 redis_cluster.c                | 26 ++--------
 redis_cluster.stub.php         |  8 +++
 redis_cluster_arginfo.h        |  6 ++-
 redis_cluster_legacy_arginfo.h |  6 ++-
 redis_commands.c               | 16 ++----
 redis_commands.h               |  6 +--
 redis_legacy_arginfo.h         |  6 ++-
 tests/RedisTest.php            | 59 +++++++++++-----------
 11 files changed, 142 insertions(+), 111 deletions(-)

diff --git a/redis.c b/redis.c
index adfb682748..7360cdb036 100644
--- a/redis.c
+++ b/redis.c
@@ -1481,27 +1481,12 @@ PHP_METHOD(Redis, sDiffStore) {
 
 /* {{{ proto array Redis::sort(string key, array options) */
 PHP_METHOD(Redis, sort) {
-    char *cmd;
-    int cmd_len, have_store;
-    RedisSock *redis_sock;
-
-    // Grab socket, handle command construction
-    if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL ||
-       redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &have_store,
-                      &cmd, &cmd_len, NULL, NULL) == FAILURE)
-    {
-        RETURN_FALSE;
-    }
+    REDIS_PROCESS_KW_CMD("SORT", redis_sort_cmd, redis_read_variant_reply);
+}
 
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
-    if (IS_ATOMIC(redis_sock)) {
-        if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                                     redis_sock, NULL, NULL) < 0)
-        {
-            RETURN_FALSE;
-        }
-    }
-    REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
+/* {{{ proto array Redis::sort(string key, array options) */
+PHP_METHOD(Redis, sort_ro) {
+    REDIS_PROCESS_KW_CMD("SORT_RO", redis_sort_cmd, redis_read_variant_reply);
 }
 
 static void
diff --git a/redis.stub.php b/redis.stub.php
index 3c3c78e570..ddf6a26de5 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -566,35 +566,76 @@ public function sismember(string $key, mixed $value): Redis|bool;
     public function slaveof(string $host = null, int $port = 6379): bool;
 
     /**
-      Interact with Redis' slowlog functionality in variousu ways, depending
-      on the value of 'operations'.
-
-      @param string $operation  The operation you wish to perform.  This can
-                                be one of the following values:
-                                'get'   - Retreive the Redis slowlog as an array.
-                                'len'   - Retreive the length of the slowlog.
-                                'reset' - Remove all slowlog entries.
-      
-      slowllog('get', -1);  // Retreive all slowlog entries.
-      $redis->slowlog('len');       // Retreive slowlog length.
-      $redis->slowlog('reset');     // Reset the slowlog.
-      ?>
-      
-
-      @param int    $length     This optional argument can be passed when operation
-                                is 'get' and will specify how many elements to retreive.
-                                If omitted Redis will send up to a default number of
-                                entries, which is configurable.
-
-                                Note:  With Redis >= 7.0.0 you can send -1 to mean "all".
-
-      @return mixed
+     * Interact with Redis' slowlog functionality in variousu ways, depending
+     * on the value of 'operations'.
+     *
+     * @see https://https://redis.io/commands/slowlog/
+     * @category administration
+     *
+     * @param string $operation  The operation you wish to perform.  This can
+     *                           be one of the following values:
+     *                           'get'   - Retreive the Redis slowlog as an array.
+     *                           'len'   - Retreive the length of the slowlog.
+     *                           'reset' - Remove all slowlog entries.
+     * 
+     * slowllog('get', -1);  // Retreive all slowlog entries.
+     * $redis->slowlog('len');       // Retreive slowlog length.
+     * $redis->slowlog('reset');     // Reset the slowlog.
+     * ?>
+     * 
+     *
+     * @param int    $length     This optional argument can be passed when operation
+     *                           is 'get' and will specify how many elements to retreive.
+     *                           If omitted Redis will send up to a default number of
+     *                           entries, which is configurable.
+     *
+     *                           Note:  With Redis >= 7.0.0 you can send -1 to mean "all".
+     *
+     * @return mixed
      */
     public function slowlog(string $operation, int $length = 0): mixed;
 
+    /**
+     * Sort the contents of a Redis key in various ways.
+     *
+     * @see https://https://redis.io/commands/sort/
+     *
+     * @param string $key     The key you wish to sort
+     * @param array  $options Various options controlling how you would like the
+     *                        data sorted.  See blow for a detailed description
+     *                        of this options array.
+     *
+     * @return mixed This command can either return an array with the sorted data
+     *               or the number of elements placed in a destination set when
+     *               using the STORE option.
+     *
+     * 
+     *  'ASC'|| 'DESC' // Sort in descending or descending order.
+     *     'ALPHA' => true || false  // Whether to sort alphanumerically.
+     *     'LIMIT' => [0, 10]        // Return a subset of the data at offset, count
+     *     'BY'    => 'weight_*'     // For each element in the key, read data from the
+     *                                  external key weight_* and sort based on that value.
+     *     'GET'   => 'weight_*'     // For each element in the source key, retreive the
+     *                                  data from key weight_* and return that in the result
+     *                                  rather than the source keys' element.  This can
+     *                                  be used in combination with 'BY'
+     * ];
+     * ?>
+     * 
+     *
+     */
     public function sort(string $key, ?array $options = null): mixed;
 
+    /**
+     * This is simply a read-only variant of the sort command
+     *
+     * @see Redis::sort()
+     */
+    public function sort_ro(string $key, ?array $options = null): mixed;
+
     /**
      * @deprecated
      */
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 7d3509106d..216f515443 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c04531e86379ab5c0de12e8e82868b7c7f024068 */
+ * Stub hash: 0ff60ed233053935cfc7c5a5ecacd6adaf06a458 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -804,6 +804,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sort, 0, 1, IS_MIXED
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_sort_ro arginfo_class_Redis_sort
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sortAsc, 0, 1, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
@@ -1286,6 +1288,7 @@ ZEND_METHOD(Redis, sismember);
 ZEND_METHOD(Redis, slaveof);
 ZEND_METHOD(Redis, slowlog);
 ZEND_METHOD(Redis, sort);
+ZEND_METHOD(Redis, sort_ro);
 ZEND_METHOD(Redis, sortAsc);
 ZEND_METHOD(Redis, sortAscAlpha);
 ZEND_METHOD(Redis, sortDesc);
@@ -1532,6 +1535,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sort_ro, arginfo_class_Redis_sort_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sortAsc, arginfo_class_Redis_sortAsc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, sortAscAlpha, arginfo_class_Redis_sortAscAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, sortDesc, arginfo_class_Redis_sortDesc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
diff --git a/redis_cluster.c b/redis_cluster.c
index c5078521a0..8d38989ed2 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1555,28 +1555,12 @@ PHP_METHOD(RedisCluster, bzpopmin) {
 
 /* {{{ proto RedisCluster::sort(string key, array options) */
 PHP_METHOD(RedisCluster, sort) {
-    redisCluster *c = GET_CONTEXT();
-    char *cmd; int cmd_len, have_store; short slot;
-
-    if (redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &have_store,
-                      &cmd, &cmd_len, &slot, NULL) == FAILURE)
-    {
-        RETURN_FALSE;
-    }
-
-    if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) {
-        efree(cmd);
-        RETURN_FALSE;
-    }
-
-    efree(cmd);
+    CLUSTER_PROCESS_KW_CMD("SORT", redis_sort_cmd, cluster_variant_resp, 0);
+}
 
-    // Response type differs based on presence of STORE argument
-    if (!have_store) {
-        cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
-    } else {
-        cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
-    }
+/* {{{ proto RedisCluster::sort_ro(string key, array options) */
+PHP_METHOD(RedisCluster, sort_ro) {
+    CLUSTER_PROCESS_KW_CMD("SORT_RO", redis_sort_cmd, cluster_variant_resp, 1);
 }
 
 /* {{{ proto RedisCluster::object(string subcmd, string key) */
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 165dfd3b9d..88ca6c0eed 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -336,8 +336,16 @@ public function smembers(string $key): RedisCluster|array|false;
 
     public function smove(string $src, string $dst, string $member): RedisCluster|bool;
 
+    /**
+     * @see Redis::sort()
+     */
     public function sort(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string;
 
+    /**
+     * @see Redis::sort_ro()
+     */
+    public function sort_ro(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string;
+
     public function spop(string $key, int $count = 0): RedisCluster|string|array|false;
 
     public function srandmember(string $key, int $count = 0): RedisCluster|string|array|false;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 0b4619fb76..9357e62af2 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e761b2a65f9f57254e0201f9643b823e79e2a0a8 */
+ * Stub hash: 3d10a4c161f9a4bcf65ac9acfebbb86d11f9cf0d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -684,6 +684,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sort, 0,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_sort
+
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_spop, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
@@ -1092,6 +1094,7 @@ ZEND_METHOD(RedisCluster, slowlog);
 ZEND_METHOD(RedisCluster, smembers);
 ZEND_METHOD(RedisCluster, smove);
 ZEND_METHOD(RedisCluster, sort);
+ZEND_METHOD(RedisCluster, sort_ro);
 ZEND_METHOD(RedisCluster, spop);
 ZEND_METHOD(RedisCluster, srandmember);
 ZEND_METHOD(RedisCluster, srem);
@@ -1298,6 +1301,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, smembers, arginfo_class_RedisCluster_smembers, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, smove, arginfo_class_RedisCluster_smove, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sort, arginfo_class_RedisCluster_sort, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sort_ro, arginfo_class_RedisCluster_sort_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, spop, arginfo_class_RedisCluster_spop, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, srandmember, arginfo_class_RedisCluster_srandmember, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, srem, arginfo_class_RedisCluster_srem, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index cc73a26213..e3f41e2583 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: e761b2a65f9f57254e0201f9643b823e79e2a0a8 */
+ * Stub hash: 3d10a4c161f9a4bcf65ac9acfebbb86d11f9cf0d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -581,6 +581,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sort, 0, 0, 1)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_sort
+
 #define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster_lpop
 
 #define arginfo_class_RedisCluster_srandmember arginfo_class_RedisCluster_lpop
@@ -944,6 +946,7 @@ ZEND_METHOD(RedisCluster, slowlog);
 ZEND_METHOD(RedisCluster, smembers);
 ZEND_METHOD(RedisCluster, smove);
 ZEND_METHOD(RedisCluster, sort);
+ZEND_METHOD(RedisCluster, sort_ro);
 ZEND_METHOD(RedisCluster, spop);
 ZEND_METHOD(RedisCluster, srandmember);
 ZEND_METHOD(RedisCluster, srem);
@@ -1150,6 +1153,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, smembers, arginfo_class_RedisCluster_smembers, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, smove, arginfo_class_RedisCluster_smove, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, sort, arginfo_class_RedisCluster_sort, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, sort_ro, arginfo_class_RedisCluster_sort_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, spop, arginfo_class_RedisCluster_spop, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, srandmember, arginfo_class_RedisCluster_srandmember, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, srem, arginfo_class_RedisCluster_srem, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 416972d618..18dfeb6150 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3535,8 +3535,7 @@ int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
 /* SORT */
 int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                   int *using_store, char **cmd, int *cmd_len, short *slot,
-                   void **ctx)
+                   char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
 {
     zval *z_opts=NULL, *z_ele, z_argv;
     char *key;
@@ -3551,16 +3550,10 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    // Default that we're not using store
-    *using_store = 0;
-
     // If we don't have an options array, the command is quite simple
     if (!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) {
         // Construct command
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SORT", "k", key, key_len);
-
-        /* Not storing */
-        *using_store = 0;
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "k", key, key_len);
 
         return SUCCESS;
     }
@@ -3627,7 +3620,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele));
 
         // We are using STORE
-        *using_store = 1;
+        *ctx = PHPREDIS_CTX_PTR;
     }
 
     // GET option
@@ -3725,8 +3718,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     // Start constructing our command
     HashTable *ht_argv = Z_ARRVAL_P(&z_argv);
-    redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(ht_argv), "SORT",
-        sizeof("SORT")-1);
+    redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(ht_argv), kw, strlen(kw));
 
     // Iterate through our arguments
     ZEND_HASH_FOREACH_VAL(ht_argv, z_ele) {
diff --git a/redis_commands.h b/redis_commands.h
index 4d7a13e03d..2de643477c 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -264,9 +264,6 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
-int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    int *using_store, char **cmd, int *cmd_len, short *slot, void **ctx);
-
 int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
@@ -365,6 +362,9 @@ int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
+
 /* Commands that don't communicate with Redis at all (such as getOption,
  * setOption, _prefix, _serialize, etc).  These can be handled in one place
  * with the method of grabbing our RedisSock* object in different ways
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 0b6fbd2340..4eedda7a73 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: c04531e86379ab5c0de12e8e82868b7c7f024068 */
+ * Stub hash: 0ff60ed233053935cfc7c5a5ecacd6adaf06a458 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -678,6 +678,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sort arginfo_class_Redis_getEx
 
+#define arginfo_class_Redis_sort_ro arginfo_class_Redis_getEx
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sortAsc, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, pattern)
@@ -1118,6 +1120,7 @@ ZEND_METHOD(Redis, sismember);
 ZEND_METHOD(Redis, slaveof);
 ZEND_METHOD(Redis, slowlog);
 ZEND_METHOD(Redis, sort);
+ZEND_METHOD(Redis, sort_ro);
 ZEND_METHOD(Redis, sortAsc);
 ZEND_METHOD(Redis, sortAscAlpha);
 ZEND_METHOD(Redis, sortDesc);
@@ -1364,6 +1367,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, sort_ro, arginfo_class_Redis_sort_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sortAsc, arginfo_class_Redis_sortAsc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, sortAscAlpha, arginfo_class_Redis_sortAscAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
 	ZEND_ME(Redis, sortDesc, arginfo_class_Redis_sortDesc, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index ea036ebc75..2a5085ca1d 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1364,41 +1364,46 @@ public function testSortAsc() {
 
     public function testSortDesc() {
 
-    $this->setupSort();
+        $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 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']));
+        // 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($byAgeDesc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'desc']));
-    $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'desc']));
+        $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'desc']));
+        $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], '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 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 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]));
     }
 
-    // SORT list ALPHA → [abc, def, ghi]
-    $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sort('list', ['sort' => 'desc', 'alpha' => TRUE]));
+    /* This test is just to make sure SORT and SORT_RO are both callable */
+    public function testSortHandler() {
+        $this->redis->del('list');
+
+        $this->redis->rpush('list', 'c', 'b', 'a');
+
+        $methods = ['sort'];
+        if ($this->minVersionCheck('7.0.0')) $methods[] = 'sort_ro';
+
+        foreach ($methods as $method) {
+            $this->assertEquals(['a', 'b', 'c'], $this->redis->$method('list', ['sort' => 'asc', 'alpha' => true]));
+        }
     }
 
     // LINDEX

From d73f3f4b08c09aec5700b2e076ea84433f66d575 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 26 May 2022 18:12:01 +0300
Subject: [PATCH 1659/1986] Issue #2106

---
 common.h                       | 1 +
 library.c                      | 2 +-
 redis.c                        | 9 +++++++++
 redis.stub.php                 | 2 ++
 redis_arginfo.h                | 7 ++++++-
 redis_cluster.c                | 4 ++++
 redis_cluster.stub.php         | 2 ++
 redis_cluster_arginfo.h        | 7 ++++++-
 redis_cluster_legacy_arginfo.h | 6 +++++-
 redis_legacy_arginfo.h         | 6 +++++-
 tests/RedisClusterTest.php     | 1 +
 tests/RedisTest.php            | 7 +++++++
 12 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/common.h b/common.h
index 7abaecd57a..258f7d776e 100644
--- a/common.h
+++ b/common.h
@@ -323,6 +323,7 @@ typedef struct {
     int                 null_mbulk_as_null;
     int                 tcp_keepalive;
     int                 sentinel;
+    size_t              txBytes;
 } RedisSock;
 /* }}} */
 
diff --git a/library.c b/library.c
index 66d6178f0b..b2e7923024 100644
--- a/library.c
+++ b/library.c
@@ -3128,7 +3128,7 @@ redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz)
     if (redis_check_eof(redis_sock, 0, 0) == 0 &&
         php_stream_write(redis_sock->stream, cmd, sz) == sz
     ) {
-        return sz;
+        return redis_sock->txBytes = sz;
     }
     return -1;
 }
diff --git a/redis.c b/redis.c
index 7360cdb036..f84cb90792 100644
--- a/redis.c
+++ b/redis.c
@@ -3131,6 +3131,15 @@ PHP_METHOD(Redis, getDBNum) {
     }
 }
 
+PHP_METHOD(Redis, getTransferredBytes) {
+    RedisSock *redis_sock;
+
+    if ((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) {
+        RETURN_FALSE;
+    }
+    RETURN_LONG(redis_sock->txBytes);
+}
+
 /* {{{ proto Redis::getTimeout */
 PHP_METHOD(Redis, getTimeout) {
     RedisSock *redis_sock;
diff --git a/redis.stub.php b/redis.stub.php
index ddf6a26de5..d50aed2af9 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -294,6 +294,8 @@ public function getset(string $key, mixed $value): Redis|string|false;
 
     public function getTimeout(): int;
 
+    public function getTransferredBytes(): int|false;
+
     public function hDel(string $key, string $member, string ...$other_members): Redis|int|false;
 
     public function hExists(string $key, string $member): Redis|bool;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 216f515443..406d445b13 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0ff60ed233053935cfc7c5a5ecacd6adaf06a458 */
+ * Stub hash: 894a03b9e9db1e8ded0f016a00e7e7150dc26f31 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -359,6 +359,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getTimeout arginfo_class_Redis_getDBNum
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getTransferredBytes, 0, 0, MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hDel, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
@@ -1191,6 +1194,7 @@ ZEND_METHOD(Redis, lcs);
 ZEND_METHOD(Redis, getReadTimeout);
 ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, getTimeout);
+ZEND_METHOD(Redis, getTransferredBytes);
 ZEND_METHOD(Redis, hDel);
 ZEND_METHOD(Redis, hExists);
 ZEND_METHOD(Redis, hGet);
@@ -1436,6 +1440,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getTransferredBytes, arginfo_class_Redis_getTransferredBytes, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hDel, arginfo_class_Redis_hDel, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index 8d38989ed2..22a83e0435 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1710,6 +1710,10 @@ PHP_METHOD(RedisCluster, clearlasterror) {
 
     RETURN_TRUE;
 }
+
+PHP_METHOD(RedisCluster, gettransferredbytes) {
+    CLUSTER_THROW_EXCEPTION("Not implemented", 0);
+}
 /* }}} */
 
 /* {{{ proto long RedisCluster::getOption(long option */
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 88ca6c0eed..daf0fa6f36 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -155,6 +155,8 @@ public function lcs(string $key1, string $key2, ?array $options = NULL): RedisCl
 
     public function getset(string $key, mixed $value): RedisCluster|string|bool;
 
+    public function gettransferredbytes(): int|false;
+
     public function hdel(string $key, string $member, string ...$other_members): RedisCluster|int|false;
 
     public function hexists(string $key, string $member): RedisCluster|bool;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 9357e62af2..457f967b5e 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3d10a4c161f9a4bcf65ac9acfebbb86d11f9cf0d */
+ * Stub hash: b9d2d6314c74dbf1e80662aa867b36c4a3ecfe8d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -306,6 +306,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getset, 0
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_gettransferredbytes, 0, 0, MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hdel, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
@@ -1012,6 +1015,7 @@ ZEND_METHOD(RedisCluster, getoption);
 ZEND_METHOD(RedisCluster, getrange);
 ZEND_METHOD(RedisCluster, lcs);
 ZEND_METHOD(RedisCluster, getset);
+ZEND_METHOD(RedisCluster, gettransferredbytes);
 ZEND_METHOD(RedisCluster, hdel);
 ZEND_METHOD(RedisCluster, hexists);
 ZEND_METHOD(RedisCluster, hget);
@@ -1219,6 +1223,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, gettransferredbytes, arginfo_class_RedisCluster_gettransferredbytes, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index e3f41e2583..2d206255cb 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3d10a4c161f9a4bcf65ac9acfebbb86d11f9cf0d */
+ * Stub hash: b9d2d6314c74dbf1e80662aa867b36c4a3ecfe8d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -272,6 +272,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_getset arginfo_class_RedisCluster_append
 
+#define arginfo_class_RedisCluster_gettransferredbytes arginfo_class_RedisCluster__masters
+
 #define arginfo_class_RedisCluster_hdel arginfo_class_RedisCluster_geohash
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexists, 0, 0, 2)
@@ -864,6 +866,7 @@ ZEND_METHOD(RedisCluster, getoption);
 ZEND_METHOD(RedisCluster, getrange);
 ZEND_METHOD(RedisCluster, lcs);
 ZEND_METHOD(RedisCluster, getset);
+ZEND_METHOD(RedisCluster, gettransferredbytes);
 ZEND_METHOD(RedisCluster, hdel);
 ZEND_METHOD(RedisCluster, hexists);
 ZEND_METHOD(RedisCluster, hget);
@@ -1071,6 +1074,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, gettransferredbytes, arginfo_class_RedisCluster_gettransferredbytes, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 4eedda7a73..0bb816ebbb 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0ff60ed233053935cfc7c5a5ecacd6adaf06a458 */
+ * Stub hash: 894a03b9e9db1e8ded0f016a00e7e7150dc26f31 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -326,6 +326,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getTimeout arginfo_class_Redis___destruct
 
+#define arginfo_class_Redis_getTransferredBytes arginfo_class_Redis___destruct
+
 #define arginfo_class_Redis_hDel arginfo_class_Redis_geohash
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hExists, 0, 0, 2)
@@ -1023,6 +1025,7 @@ ZEND_METHOD(Redis, lcs);
 ZEND_METHOD(Redis, getReadTimeout);
 ZEND_METHOD(Redis, getset);
 ZEND_METHOD(Redis, getTimeout);
+ZEND_METHOD(Redis, getTransferredBytes);
 ZEND_METHOD(Redis, hDel);
 ZEND_METHOD(Redis, hExists);
 ZEND_METHOD(Redis, hGet);
@@ -1268,6 +1271,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, getTransferredBytes, arginfo_class_Redis_getTransferredBytes, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hDel, arginfo_class_Redis_hDel, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 6df481d538..88e84691eb 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -50,6 +50,7 @@ public function testTlsConnect() { return $this->markTestSkipped(); }
     public function testReset() { return $this->markTestSkipped(); }
     public function testInvalidAuthArgs() { return $this->markTestSkipped(); }
     public function testScanErrors() { return $this->markTestSkipped(); }
+    public function testTransferredBytes() { return $this->markTestSkipped(); }
 
     public function testlMove() { return $this->markTestSkipped(); }
     public function testlPos() { return $this->marktestSkipped(); }
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 2a5085ca1d..53226a9c24 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5752,6 +5752,13 @@ public function testIntrospection() {
         $this->assertTrue($this->redis->getAuth() === $this->getAuth());
     }
 
+    public function testTransferredBytes() {
+        $this->assertTrue($this->redis->ping());
+        $this->assertEquals(strlen("*1\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes());
+        $this->assertEquals(['cluster_enabled' => 0], $this->redis->info('cluster'));
+        $this->assertEquals(strlen("*2\r\n$4\r\nINFO\r\n$7\r\ncluster\r\n"), $this->redis->getTransferredBytes());
+    }
+
     /**
      * Scan and variants
      */

From e0a88b7bdfe3adc3319224a76bc69d5efddfa9ee Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Thu, 13 Oct 2022 08:14:12 +0300
Subject: [PATCH 1660/1986] Issue #2106

Expose the transferred number of bytes
---
 cluster_library.c          |  9 ++++++---
 library.c                  |  7 ++++++-
 redis_cluster.c            |  5 ++++-
 tests/RedisClusterTest.php | 10 +++++++++-
 tests/RedisTest.php        |  2 ++
 5 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index 90a6f95ea1..a2b02640a2 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -299,9 +299,8 @@ static int cluster_send_readonly(RedisSock *redis_sock) {
 
 /* Send MULTI to a specific ReidsSock */
 static int cluster_send_multi(redisCluster *c, short slot) {
-    if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_MULTI_CMD,
-        sizeof(RESP_MULTI_CMD) - 1, TYPE_LINE) == 0)
-    {
+    if (cluster_send_direct(SLOT_SOCK(c,slot), ZEND_STRL(RESP_MULTI_CMD), TYPE_LINE) == 0) {
+        c->flags->txBytes += sizeof(RESP_MULTI_CMD) - 1;
         c->cmd_sock->mode = MULTI;
         return 0;
     }
@@ -1513,6 +1512,9 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd,
     /* Point our cluster to this slot and it's socket */
     c->cmd_slot = slot;
     c->cmd_sock = SLOT_SOCK(c, slot);
+    if (c->flags->mode != MULTI) {
+        c->flags->txBytes = 0;
+    }
 
     /* Enable multi mode on this slot if we've been directed to but haven't
      * send it to this node yet */
@@ -1527,6 +1529,7 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd,
     if (cluster_sock_write(c, cmd, cmd_len, 1) == -1) {
         return -1;
     }
+    c->flags->txBytes += cmd_len;
 
     /* Check our response */
     if (cluster_check_response(c, &c->reply_type) != 0 ||
diff --git a/library.c b/library.c
index b2e7923024..0709963430 100644
--- a/library.c
+++ b/library.c
@@ -3128,7 +3128,12 @@ redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz)
     if (redis_check_eof(redis_sock, 0, 0) == 0 &&
         php_stream_write(redis_sock->stream, cmd, sz) == sz
     ) {
-        return redis_sock->txBytes = sz;
+        if (IS_MULTI(redis_sock)) {
+            redis_sock->txBytes += sz;
+        } else {
+            redis_sock->txBytes = sz;
+        }
+        return sz;
     }
     return -1;
 }
diff --git a/redis_cluster.c b/redis_cluster.c
index 22a83e0435..b0afe85913 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1712,7 +1712,8 @@ PHP_METHOD(RedisCluster, clearlasterror) {
 }
 
 PHP_METHOD(RedisCluster, gettransferredbytes) {
-    CLUSTER_THROW_EXCEPTION("Not implemented", 0);
+    redisCluster *c = GET_CONTEXT();
+    RETURN_LONG(c->flags->txBytes);
 }
 /* }}} */
 
@@ -1833,6 +1834,8 @@ PHP_METHOD(RedisCluster, multi) {
     /* Flag that we're in MULTI mode */
     c->flags->mode = MULTI;
 
+    c->flags->txBytes = 0;
+
     /* Return our object so we can chain MULTI calls */
     RETVAL_ZVAL(getThis(), 1, 0);
 }
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index 88e84691eb..b9fff009d1 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -50,7 +50,6 @@ public function testTlsConnect() { return $this->markTestSkipped(); }
     public function testReset() { return $this->markTestSkipped(); }
     public function testInvalidAuthArgs() { return $this->markTestSkipped(); }
     public function testScanErrors() { return $this->markTestSkipped(); }
-    public function testTransferredBytes() { return $this->markTestSkipped(); }
 
     public function testlMove() { return $this->markTestSkipped(); }
     public function testlPos() { return $this->marktestSkipped(); }
@@ -759,6 +758,15 @@ public function testConnectionPool() {
         ini_set('redis.pconnect.pooling_enabled', $prev_value);
     }
 
+    public function testTransferredBytes() {
+        $this->assertTrue($this->redis->ping(''));
+        $this->assertEquals(strlen("*1\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes());
+        $this->assertEquals(['cluster_enabled' => 1], $this->redis->info('', 'cluster'));
+        $this->assertEquals(strlen("*2\r\n$4\r\nINFO\r\n$7\r\ncluster\r\n"), $this->redis->getTransferredBytes());
+        $this->assertEquals([true, true], $this->redis->multi()->ping('')->ping('')->exec());
+        $this->assertEquals(strlen("*1\r\n$5\r\nMULTI\r\n*1\r\n$4\r\nEXEC\r\n") + 2 * strlen("*2\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes());
+    }
+
     /**
      * @inheritdoc
      */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 53226a9c24..57ca4c26b6 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -5757,6 +5757,8 @@ public function testTransferredBytes() {
         $this->assertEquals(strlen("*1\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes());
         $this->assertEquals(['cluster_enabled' => 0], $this->redis->info('cluster'));
         $this->assertEquals(strlen("*2\r\n$4\r\nINFO\r\n$7\r\ncluster\r\n"), $this->redis->getTransferredBytes());
+        $this->assertEquals([true, true], $this->redis->multi()->ping()->ping()->exec());
+        $this->assertEquals(strlen("*1\r\n$5\r\nMULTI\r\n*1\r\n$4\r\nEXEC\r\n") + 2 * strlen("*2\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes());
     }
 
     /**

From 69355faae0ff77bd69fbadba4b43779eac1b703e Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 23 Oct 2022 17:30:57 -0700
Subject: [PATCH 1661/1986] Future proof our igbinary header check

See: #2194
---
 library.c           | 13 ++++++-------
 tests/RedisTest.php | 21 +++++++++++++++++++++
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/library.c b/library.c
index 0709963430..9144a8fd0d 100644
--- a/library.c
+++ b/library.c
@@ -3565,15 +3565,14 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len,
              * | header (4) | type (1) | ... (n) |  NUL (1) |
              * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
              *
-             * With header being either 0x00000001 or 0x00000002
-             * (encoded as big endian).
+             * With header being three zero bytes and one non-zero version
+             * specifier.  At the time of this comment, there is only version
+             * 0x01 and 0x02, but newer versions will use subsequent
+             * values.
              *
-             * Not all versions contain the trailing NULL byte though, so
-             * do not check for that.
+             * Not all versions contain a trailing \x00 so don't check for that.
              */
-            if (val_len < 5
-                    || (memcmp(val, "\x00\x00\x00\x01", 4) != 0
-                    && memcmp(val, "\x00\x00\x00\x02", 4) != 0))
+            if (val_len < 5 || memcmp(val, "\x00\x00\x00", 3) || val[3] < '\x01' || val[3] > '\x04')
             {
                 /* This is most definitely not an igbinary string, so do
                    not try to unserialize this as one. */
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 57ca4c26b6..028116b4c6 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -4838,6 +4838,27 @@ public function testSerializerIGBinary() {
             $this->redis->setOption(Redis::OPT_PREFIX, "test:");
             $this->checkSerializer(Redis::SERIALIZER_IGBINARY);
             $this->redis->setOption(Redis::OPT_PREFIX, "");
+
+            /* Test our igbinary header check logic.  The check allows us to do
+               simple INCR type operations even with the serializer enabled, and
+               should also protect against igbinary-like data from being erroneously
+               deserialized */
+            $this->redis->del('incrkey');
+
+            $this->redis->set('spoof-1', "\x00\x00\x00\x00");
+            $this->redis->set('spoof-2', "\x00\x00\x00\x00bad-version1");
+            $this->redis->set('spoof-3', "\x00\x00\x00\x05bad-version2");
+            $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);
+
+            $this->assertEquals(16, $this->redis->incrby('incrkey', 16));
+            $this->assertEquals('16', $this->redis->get('incrkey'));
+
+            $this->assertEquals("\x00\x00\x00\x00", $this->redis->get('spoof-1'));
+            $this->assertEquals("\x00\x00\x00\x00bad-version1", $this->redis->get('spoof-2'));
+            $this->assertEquals("\x00\x00\x00\x05bad-version2", $this->redis->get('spoof-3'));
+            $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
+
+            $this->redis->del('incrkey', 'spoof-1', 'spoof-2', 'spoof-3');
         }
     }
 

From dc1f2398d014d43c402626931ed3b78c4dd1f96e Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 25 Oct 2022 20:22:02 -0700
Subject: [PATCH 1662/1986] TOUCH command

Implement the TOUCH command and refactor several of our "variadic key"
commands, which were previously all using their own specific handlers.

While refactoring the code, I changed `EXISTS` to require one key (it
had previously been set to require zero keys).

Additonally, it looks like we had a disparity in two commands which
should be idential to PhpRedis:  SINTERSTORE and SUNIONSTORE.
Previously, SINTERSTORE required only one argument but SUNIONSTORE 2.

I simply changed SUNIONSTORE to also only require a single argument,
since that argument could be an array.

```php
$redis->sInterStore(['dst', 'src1', 'src2']);
$redis->sUnionStore(['dst', 'src1', 'src2']);
```
---
 redis.c                        | 41 +++++++++--------
 redis.stub.php                 | 16 ++++++-
 redis_arginfo.h                |  9 +++-
 redis_cluster.c                | 22 +++++----
 redis_cluster.stub.php         |  5 +++
 redis_cluster_arginfo.h        |  6 ++-
 redis_cluster_legacy_arginfo.h |  6 ++-
 redis_commands.c               | 81 +++-------------------------------
 redis_commands.h               | 40 +++--------------
 redis_legacy_arginfo.h         |  9 +++-
 tests/RedisTest.php            | 19 +++++++-
 11 files changed, 111 insertions(+), 143 deletions(-)

diff --git a/redis.c b/redis.c
index f84cb90792..89caae9030 100644
--- a/redis.c
+++ b/redis.c
@@ -1043,19 +1043,24 @@ PHP_METHOD(Redis, mget)
     REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
 }
 
-/* {{{ proto boolean Redis::exists(string key)
+/* {{{ proto boolean Redis::exists(string $key, string ...$more_keys)
  */
-PHP_METHOD(Redis, exists)
-{
-    REDIS_PROCESS_CMD(exists, redis_long_response);
+PHP_METHOD(Redis, exists) {
+    REDIS_PROCESS_KW_CMD("EXISTS", redis_varkey_cmd, redis_long_response);
 }
 /* }}} */
 
+/* {{{ proto boolean Redis::touch(string $key, string ...$more_keys)
+ */
+PHP_METHOD(Redis, touch) {
+    REDIS_PROCESS_KW_CMD("TOUCH", redis_varkey_cmd, redis_long_response);
+}
+
+/* }}} */
 /* {{{ proto boolean Redis::del(string key)
  */
-PHP_METHOD(Redis, del)
-{
-    REDIS_PROCESS_CMD(del, redis_long_response);
+PHP_METHOD(Redis, del) {
+    REDIS_PROCESS_KW_CMD("DEL", redis_varkey_cmd, redis_long_response);
 }
 /* }}} */
 
@@ -1063,7 +1068,7 @@ PHP_METHOD(Redis, del)
  * {{{ proto long Redis::unlink(array $keys) */
 PHP_METHOD(Redis, unlink)
 {
-    REDIS_PROCESS_CMD(unlink, redis_long_response);
+    REDIS_PROCESS_KW_CMD("UNLINK", redis_varkey_cmd, redis_long_response);
 }
 
 PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock)
@@ -1080,9 +1085,8 @@ PHP_REDIS_API int redis_watch_response(INTERNAL_FUNCTION_PARAMETERS,
 
 /* {{{ proto boolean Redis::watch(string key1, string key2...)
  */
-PHP_METHOD(Redis, watch)
-{
-    REDIS_PROCESS_CMD(watch, redis_watch_response);
+PHP_METHOD(Redis, watch) {
+    REDIS_PROCESS_KW_CMD("WATCH", redis_varkey_cmd, redis_watch_response);
 }
 /* }}} */
 
@@ -1440,7 +1444,7 @@ PHP_METHOD(Redis, sMisMember)
 
 /* {{{ proto array Redis::sInter(string key0, ... string keyN) */
 PHP_METHOD(Redis, sInter) {
-    REDIS_PROCESS_CMD(sinter, redis_sock_read_multibulk_reply);
+    REDIS_PROCESS_KW_CMD("SINTER", redis_varkey_cmd, redis_sock_read_multibulk_reply);
 }
 /* }}} */
 
@@ -1450,35 +1454,34 @@ PHP_METHOD(Redis, sintercard) {
 
 /* {{{ proto array Redis::sInterStore(string dst, string key0,...string keyN) */
 PHP_METHOD(Redis, sInterStore) {
-    REDIS_PROCESS_CMD(sinterstore, redis_long_response);
+    REDIS_PROCESS_KW_CMD("SINTERSTORE", redis_varkey_cmd, redis_long_response);
 }
 /* }}} */
 
 /* {{{ proto array Redis::sUnion(string key0, ... string keyN) */
 PHP_METHOD(Redis, sUnion) {
-    REDIS_PROCESS_CMD(sunion, redis_sock_read_multibulk_reply);
+    REDIS_PROCESS_KW_CMD("SUNION", redis_varkey_cmd, redis_sock_read_multibulk_reply);
 }
 /* }}} */
 
-/* {{{ proto array Redis::sUnionStore(string dst, string key0, ... keyN) */
+/* {{{ proto array Redis::sUnionStore(array|string $key, string ...$srckeys) */
 PHP_METHOD(Redis, sUnionStore) {
-    REDIS_PROCESS_CMD(sunionstore, redis_long_response);
+    REDIS_PROCESS_KW_CMD("SUNIONSTORE", redis_varkey_cmd, redis_long_response);
 }
 /* }}} */
 
 /* {{{ proto array Redis::sDiff(string key0, ... string keyN) */
 PHP_METHOD(Redis, sDiff) {
-    REDIS_PROCESS_CMD(sdiff, redis_sock_read_multibulk_reply);
+    REDIS_PROCESS_KW_CMD("SDIFF", redis_varkey_cmd, redis_sock_read_multibulk_reply);
 }
 /* }}} */
 
 /* {{{ proto array Redis::sDiffStore(string dst, string key0, ... keyN) */
 PHP_METHOD(Redis, sDiffStore) {
-    REDIS_PROCESS_CMD(sdiffstore, redis_long_response);
+    REDIS_PROCESS_KW_CMD("SDIFFSTORE", redis_varkey_cmd, redis_long_response);
 }
 /* }}} */
 
-
 /* {{{ proto array Redis::sort(string key, array options) */
 PHP_METHOD(Redis, sort) {
     REDIS_PROCESS_KW_CMD("SORT", redis_sort_cmd, redis_read_variant_reply);
diff --git a/redis.stub.php b/redis.stub.php
index d50aed2af9..6f30e337d1 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -567,11 +567,25 @@ public function sismember(string $key, mixed $value): Redis|bool;
 
     public function slaveof(string $host = null, int $port = 6379): bool;
 
+    /**
+     * Update one or more keys last modified metadata.
+     *
+     * @see https://redis.io/commands/touch/
+     *
+     * @param array|string $key    Either the first key or if passed as the only argument
+     *                             an array of keys.
+     * @param string $more_keys    One or more keys to send to the command.
+     *
+     * @return Redis|int|false     This command returns the number of keys that exist and
+     *                             had their last modified time reset
+     */
+    public function touch(array|string $key_or_array, string ...$more_keys): Redis|int|false;
+
     /**
      * Interact with Redis' slowlog functionality in variousu ways, depending
      * on the value of 'operations'.
      *
-     * @see https://https://redis.io/commands/slowlog/
+     * @see https://redis.io/commands/slowlog/
      * @category administration
      *
      * @param string $operation  The operation you wish to perform.  This can
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 406d445b13..fa162b8f9c 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 894a03b9e9db1e8ded0f016a00e7e7150dc26f31 */
+ * Stub hash: 7c2ec068711e216a4308a2c405c32204edf60d23 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -797,6 +797,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, _IS_B
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379")
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_touch, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_MASK(0, key_or_array, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, more_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slowlog, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, "0")
@@ -1290,6 +1295,7 @@ ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, setnx);
 ZEND_METHOD(Redis, sismember);
 ZEND_METHOD(Redis, slaveof);
+ZEND_METHOD(Redis, touch);
 ZEND_METHOD(Redis, slowlog);
 ZEND_METHOD(Redis, sort);
 ZEND_METHOD(Redis, sort_ro);
@@ -1538,6 +1544,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sismember, arginfo_class_Redis_sismember, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, touch, arginfo_class_Redis_touch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sort_ro, arginfo_class_Redis_sort_ro, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index b0afe85913..df731c8ecb 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -751,12 +751,18 @@ PHP_METHOD(RedisCluster, getset) {
 }
 /* }}} */
 
-/* {{{ proto int RedisCluster::exists(string key) */
+/* {{{ proto int RedisCluster::exists(string $key, string ...$more_keys) */
 PHP_METHOD(RedisCluster, exists) {
-    CLUSTER_PROCESS_CMD(exists, cluster_long_resp, 1);
+    CLUSTER_PROCESS_KW_CMD("EXISTS", redis_varkey_cmd, cluster_long_resp, 1);
 }
 /* }}} */
 
+/* {{{ proto int RedisCluster::exists(string $key, string ...$more_keys) */
+PHP_METHOD(RedisCluster, touch) {
+    CLUSTER_PROCESS_KW_CMD("TOUCH", redis_varkey_cmd, cluster_long_resp, 0);
+}
+
+/* }}} */
 /* {{{ proto array Redis::keys(string pattern) */
 PHP_METHOD(RedisCluster, keys) {
     redisCluster *c = GET_CONTEXT();
@@ -1007,19 +1013,19 @@ PHP_METHOD(RedisCluster, srem) {
 
 /* {{{ proto array RedisCluster::sunion(string key1, ... keyN) */
 PHP_METHOD(RedisCluster, sunion) {
-    CLUSTER_PROCESS_CMD(sunion, cluster_mbulk_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("SUNION", redis_varkey_cmd, cluster_mbulk_resp, 0);
 }
 /* }}} */
 
 /* {{{ proto long RedisCluster::sunionstore(string dst, string k1, ... kN) */
 PHP_METHOD(RedisCluster, sunionstore) {
-    CLUSTER_PROCESS_CMD(sunionstore, cluster_long_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("SUNIONSTORE", redis_varkey_cmd, cluster_long_resp, 0);
 }
 /* }}} */
 
 /* {{{ ptoto array RedisCluster::sinter(string k1, ... kN) */
 PHP_METHOD(RedisCluster, sinter) {
-    CLUSTER_PROCESS_CMD(sinter, cluster_mbulk_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("SINTER", redis_varkey_cmd, cluster_mbulk_resp, 0);
 }
 
 /* {{{ proto RedisCluster::sintercard(array $keys, int $count = -1) */
@@ -1032,19 +1038,19 @@ PHP_METHOD(RedisCluster, sintercard) {
 
 /* {{{ ptoto long RedisCluster::sinterstore(string dst, string k1, ... kN) */
 PHP_METHOD(RedisCluster, sinterstore) {
-    CLUSTER_PROCESS_CMD(sinterstore, cluster_long_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("SINTERSTORE", redis_varkey_cmd, cluster_long_resp, 0);
 }
 /* }}} */
 
 /* {{{ proto array RedisCluster::sdiff(string k1, ... kN) */
 PHP_METHOD(RedisCluster, sdiff) {
-    CLUSTER_PROCESS_CMD(sdiff, cluster_mbulk_resp, 1);
+    CLUSTER_PROCESS_KW_CMD("SDIFF", redis_varkey_cmd, cluster_mbulk_resp, 1);
 }
 /* }}} */
 
 /* {{{ proto long RedisCluster::sdiffstore(string dst, string k1, ... kN) */
 PHP_METHOD(RedisCluster, sdiffstore) {
-    CLUSTER_PROCESS_CMD(sdiffstore, cluster_long_resp, 0);
+    CLUSTER_PROCESS_KW_CMD("SDIFFSTORE", redis_varkey_cmd, cluster_long_resp, 0);
 }
 /* }}} */
 
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index daf0fa6f36..4e167c1281 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -111,6 +111,11 @@ public function exec(): array|false;
 
     public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
 
+    /**
+     * @see Redis::touch()
+     */
+    public function touch(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
+
     public function expire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool;
 
     public function expireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 457f967b5e..d46f12b409 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b9d2d6314c74dbf1e80662aa867b36c4a3ecfe8d */
+ * Stub hash: 895f61badfcc6198d72788d80d517144fc1e8daf */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -205,6 +205,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_exists, 0
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster_touch arginfo_class_RedisCluster_exists
+
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expire, 0, 2, RedisCluster, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
@@ -993,6 +995,7 @@ ZEND_METHOD(RedisCluster, evalsha);
 ZEND_METHOD(RedisCluster, evalsha_ro);
 ZEND_METHOD(RedisCluster, exec);
 ZEND_METHOD(RedisCluster, exists);
+ZEND_METHOD(RedisCluster, touch);
 ZEND_METHOD(RedisCluster, expire);
 ZEND_METHOD(RedisCluster, expireat);
 ZEND_METHOD(RedisCluster, expiretime);
@@ -1201,6 +1204,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, evalsha_ro, arginfo_class_RedisCluster_evalsha_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, exec, arginfo_class_RedisCluster_exec, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, touch, arginfo_class_RedisCluster_touch, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expireat, arginfo_class_RedisCluster_expireat, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expiretime, arginfo_class_RedisCluster_expiretime, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 2d206255cb..c51eec087c 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b9d2d6314c74dbf1e80662aa867b36c4a3ecfe8d */
+ * Stub hash: 895f61badfcc6198d72788d80d517144fc1e8daf */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -179,6 +179,8 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_exists arginfo_class_RedisCluster_del
 
+#define arginfo_class_RedisCluster_touch arginfo_class_RedisCluster_del
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expire, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
 	ZEND_ARG_INFO(0, timeout)
@@ -844,6 +846,7 @@ ZEND_METHOD(RedisCluster, evalsha);
 ZEND_METHOD(RedisCluster, evalsha_ro);
 ZEND_METHOD(RedisCluster, exec);
 ZEND_METHOD(RedisCluster, exists);
+ZEND_METHOD(RedisCluster, touch);
 ZEND_METHOD(RedisCluster, expire);
 ZEND_METHOD(RedisCluster, expireat);
 ZEND_METHOD(RedisCluster, expiretime);
@@ -1052,6 +1055,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, evalsha_ro, arginfo_class_RedisCluster_evalsha_ro, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, exec, arginfo_class_RedisCluster_exec, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, exists, arginfo_class_RedisCluster_exists, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, touch, arginfo_class_RedisCluster_touch, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expire, arginfo_class_RedisCluster_expire, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expireat, arginfo_class_RedisCluster_expireat, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, expiretime, arginfo_class_RedisCluster_expiretime, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 18dfeb6150..d19d6315a7 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -4593,84 +4593,13 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-/* EXISTS */
-int redis_exists_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                     char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "EXISTS", sizeof("EXISTS") - 1, 0, 0, cmd, cmd_len, slot);
-}
-
-/* DEL */
-int redis_del_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                  char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "DEL", sizeof("DEL")-1, 1, 0, cmd, cmd_len, slot);
-}
-
-/* UNLINK */
-int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                     char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "UNLINK", sizeof("UNLINK")-1, 1, 0, cmd, cmd_len, slot);
-}
-
-/* WATCH */
-int redis_watch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                    char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "WATCH", sizeof("WATCH")-1, 1, 0, cmd, cmd_len, slot);
-}
-
-/* SINTER */
-int redis_sinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                     char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "SINTER", sizeof("SINTER")-1, 1, 0, cmd, cmd_len, slot);
-}
-
-/* SINTERSTORE */
-int redis_sinterstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                          char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "SINTERSTORE", sizeof("SINTERSTORE")-1, 1, 0, cmd, cmd_len, slot);
-}
-
-/* SUNION */
-int redis_sunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                     char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "SUNION", sizeof("SUNION")-1, 1, 0, cmd, cmd_len, slot);
-}
-
-/* SUNIONSTORE */
-int redis_sunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                          char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "SUNIONSTORE", sizeof("SUNIONSTORE")-1, 2, 0, cmd, cmd_len, slot);
-}
-
-/* SDIFF */
-int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                    char **cmd, int *cmd_len, short *slot, void **ctx)
-{
-    return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, "SDIFF",
-        sizeof("SDIFF")-1, 1, 0, cmd, cmd_len, slot);
-}
-
-/* SDIFFSTORE */
-int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                         char **cmd, int *cmd_len, short *slot, void **ctx)
+/* A generic passthru function for variadic key commands that take one or more
+ * keys.  This is essentially all of them except ones that STORE data. */
+int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
 {
     return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "SDIFFSTORE", sizeof("SDIFFSTORE")-1, 1, 0, cmd, cmd_len, slot);
+                          kw, strlen(kw), 1, 0, cmd, cmd_len, slot);
 }
 
 static int
diff --git a/redis_commands.h b/redis_commands.h
index 2de643477c..3b34a155cf 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -274,36 +274,6 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     REDIS_REPLY_TYPE *rtype, char **cmd, int *cmd_len, short *slot,
     void **ctx);
 
-int redis_exists_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_del_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_watch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_sinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_sinterstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_sunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_sunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
-int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-    char **cmd, int *cmd_len, short *slot, void **ctx);
-
 int redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
@@ -350,11 +320,15 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
 int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                    char **cmd, int *cmd_len, short *slot, void **ctx);
+    char **cmd, int *cmd_len, short *slot, void **ctx);
 
 int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
-                     char *kw, char **cmd, int *cmd_len, short *slot,
-                     void **ctx);
+    char *kw, char **cmd, int *cmd_len, short *slot,
+    void **ctx);
+
+int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char *kw, char **cmd, int *cmd_len, short *slot,
+    void **ctx);
 
 int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 0bb816ebbb..4cd44ad0ef 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 894a03b9e9db1e8ded0f016a00e7e7150dc26f31 */
+ * Stub hash: 7c2ec068711e216a4308a2c405c32204edf60d23 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -673,6 +673,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, 0)
 	ZEND_ARG_INFO(0, port)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_touch, 0, 0, 1)
+	ZEND_ARG_INFO(0, key_or_array)
+	ZEND_ARG_VARIADIC_INFO(0, more_keys)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slowlog, 0, 0, 1)
 	ZEND_ARG_INFO(0, operation)
 	ZEND_ARG_INFO(0, length)
@@ -1121,6 +1126,7 @@ ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, setnx);
 ZEND_METHOD(Redis, sismember);
 ZEND_METHOD(Redis, slaveof);
+ZEND_METHOD(Redis, touch);
 ZEND_METHOD(Redis, slowlog);
 ZEND_METHOD(Redis, sort);
 ZEND_METHOD(Redis, sort_ro);
@@ -1369,6 +1375,7 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sismember, arginfo_class_Redis_sismember, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, touch, arginfo_class_Redis_touch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sort_ro, arginfo_class_Redis_sort_ro, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 028116b4c6..cd9f631bb4 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -894,8 +894,7 @@ public function testDecr()
     }
 
 
-    public function testExists()
-    {
+    public function testExists() {
         /* Single key */
         $this->redis->del('key');
         $this->assertEquals(0, $this->redis->exists('key'));
@@ -917,6 +916,22 @@ public function testExists()
         $this->assertEquals(count($mkeys), call_user_func_array([$this->redis, 'exists'], $mkeys));
     }
 
+    public function testTouch() {
+        if (!$this->minVersionCheck('3.2.1'))
+            $this->markTestSkipped();
+
+        $this->redis->del('notakey');
+
+        $this->assertTrue($this->redis->mset(['{idle}1' => 'beep', '{idle}2' => 'boop']));
+        usleep(1100000);
+        $this->assertTrue($this->redis->object('idletime', '{idle}1') >= 1);
+        $this->assertTrue($this->redis->object('idletime', '{idle}2') >= 1);
+
+        $this->assertEquals(2, $this->redis->touch('{idle}1', '{idle}2', '{idle}notakey'));
+        $this->assertTrue($this->redis->object('idletime', '{idle}1') == 0);
+        $this->assertTrue($this->redis->object('idletime', '{idle}2') == 0);
+    }
+
     public function testKeys()
     {
         $pattern = 'keys-test-';

From 1d6c52ee395884eae69847242bcd04598553f397 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Wed, 26 Oct 2022 20:42:41 +0300
Subject: [PATCH 1663/1986] Small cosmetic changes in cluster_library

---
 cluster_library.c | 54 +++++++++++++++++------------------------------
 cluster_library.h |  7 +-----
 2 files changed, 20 insertions(+), 41 deletions(-)

diff --git a/cluster_library.c b/cluster_library.c
index a2b02640a2..945c7bc128 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -273,8 +273,7 @@ static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len,
 }
 
 static int cluster_send_asking(RedisSock *redis_sock) {
-    return cluster_send_direct(redis_sock, RESP_ASKING_CMD,
-        sizeof(RESP_ASKING_CMD)-1, TYPE_LINE);
+    return cluster_send_direct(redis_sock, ZEND_STRL(RESP_ASKING_CMD), TYPE_LINE);
 }
 
 /* Send READONLY to a specific RedisSock unless it's already flagged as being
@@ -287,8 +286,7 @@ static int cluster_send_readonly(RedisSock *redis_sock) {
     if (redis_sock->readonly) return 0;
 
     /* Return success if we can send it */
-    ret = cluster_send_direct(redis_sock, RESP_READONLY_CMD,
-        sizeof(RESP_READONLY_CMD) - 1, TYPE_LINE);
+    ret = cluster_send_direct(redis_sock, ZEND_STRL(RESP_READONLY_CMD), TYPE_LINE);
 
     /* Flag this socket as READONLY if our command worked */
     redis_sock->readonly = !ret;
@@ -315,8 +313,7 @@ PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot) {
     int retval;
 
     /* Send exec */
-    retval = cluster_send_slot(c, slot, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD)-1,
-        TYPE_MULTIBULK);
+    retval = cluster_send_slot(c, slot, ZEND_STRL(RESP_EXEC_CMD), TYPE_MULTIBULK);
 
     /* We'll either get a length corresponding to the number of commands sent to
      * this node, or -1 in the case of EXECABORT or WATCH failure. */
@@ -327,8 +324,7 @@ PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot) {
 }
 
 PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot) {
-    if (cluster_send_direct(SLOT_SOCK(c,slot), RESP_DISCARD_CMD,
-        sizeof(RESP_DISCARD_CMD)-1, TYPE_LINE))
+    if (cluster_send_direct(SLOT_SOCK(c,slot), ZEND_STRL(RESP_DISCARD_CMD), TYPE_LINE))
     {
         return 0;
     }
@@ -1124,6 +1120,10 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved)
 {
     char *host, *port;
 
+    /* The Redis Cluster specification suggests clients do not update
+     * their slot mapping for an ASK redirection, only for MOVED */
+    if (moved) c->redirections++;
+
     /* Move past "MOVED" or "ASK */
     msg += moved ? MOVED_LEN : ASK_LEN;
 
@@ -1182,22 +1182,12 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type)
 
         // Check for MOVED or ASK redirection
         if ((moved = IS_MOVED(inbuf)) || IS_ASK(inbuf)) {
-            /* The Redis Cluster specification suggests clients do not update
-             * their slot mapping for an ASK redirection, only for MOVED */
-            if (moved) c->redirections++;
-
             /* Make sure we can parse the redirection host and port */
-            if (cluster_set_redirection(c,inbuf,moved) < 0) {
-                return -1;
-            }
-
-            /* We've been redirected */
-            return 1;
-        } else {
-            // Capture the error string Redis returned
-            cluster_set_err(c, inbuf, strlen(inbuf)-2);
-            return 0;
+            return !cluster_set_redirection(c, inbuf, moved) ? 1 : -1;
         }
+        // Capture the error string Redis returned
+        cluster_set_err(c, inbuf, strlen(inbuf)-2);
+        return 0;
     }
 
     // Fetch the first line of our response from Redis.
@@ -1272,9 +1262,7 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz,
 
         /* If we're not on the master, attempt to send the READONLY command to
          * this slave, and skip it if that fails */
-        if (nodes[i] == 0 || redis_sock->readonly ||
-            cluster_send_readonly(redis_sock) == 0)
-        {
+        if (nodes[i] == 0 || cluster_send_readonly(redis_sock) == 0) {
             /* Attempt to send the command */
             if (CLUSTER_SEND_PAYLOAD(redis_sock, cmd, sz)) {
                 c->cmd_sock = redis_sock;
@@ -1567,7 +1555,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char
      * CLUSTERDOWN state from Redis Cluster. */
     do {
         /* Send MULTI to the socket if we're in MULTI mode but haven't yet */
-        if (c->flags->mode == MULTI && CMD_SOCK(c)->mode != MULTI) {
+        if (c->flags->mode == MULTI && c->cmd_sock->mode != MULTI) {
             /* We have to fail if we can't send MULTI to the node */
             if (cluster_send_multi(c, slot) == -1) {
                 CLUSTER_THROW_EXCEPTION("Unable to enter MULTI mode on requested slot", 0);
@@ -2975,13 +2963,11 @@ PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash) {
 
     if (le != NULL) {
         /* Sanity check on our list type */
-        if (le->type != le_cluster_slot_cache) {
-            php_error_docref(0, E_WARNING, "Invalid slot cache resource");
-            return NULL;
+        if (le->type == le_cluster_slot_cache) {
+            /* Success, return the cached entry */
+            return le->ptr;
         }
-
-        /* Success, return the cached entry */
-        return le->ptr;
+        php_error_docref(0, E_WARNING, "Invalid slot cache resource");
     }
 
     /* Not found */
@@ -2989,12 +2975,10 @@ PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash) {
 }
 
 /* Cache a cluster's slot information in persistent_list if it's enabled */
-PHP_REDIS_API int cluster_cache_store(zend_string *hash, HashTable *nodes) {
+PHP_REDIS_API void cluster_cache_store(zend_string *hash, HashTable *nodes) {
     redisCachedCluster *cc = cluster_cache_create(hash, nodes);
 
     redis_register_persistent_resource(cc->hash, cc, le_cluster_slot_cache);
-
-    return SUCCESS;
 }
 
 
diff --git a/cluster_library.h b/cluster_library.h
index 74ede7a4fd..b5d68858d6 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -41,10 +41,6 @@
 #define SLOT_STREAM(c,s) (SLOT_SOCK(c,s)->stream)
 #define SLOT_SLAVES(c,s) (c->master[s]->slaves)
 
-/* Macros to access socket and stream for the node we're communicating with */
-#define CMD_SOCK(c) (c->cmd_sock)
-#define CMD_STREAM(c) (c->cmd_sock->stream)
-
 /* Compare redirection slot information with the passed node */
 #define CLUSTER_REDIR_CMP(c, sock) \
     (sock->port != c->redir_port || \
@@ -374,7 +370,6 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force);
 PHP_REDIS_API int cluster_send_exec(redisCluster *c, short slot);
 PHP_REDIS_API int cluster_send_discard(redisCluster *c, short slot);
 PHP_REDIS_API int cluster_abort_exec(redisCluster *c);
-PHP_REDIS_API int cluster_reset_multi(redisCluster *c);
 
 PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host,
     unsigned short port);
@@ -397,7 +392,7 @@ PHP_REDIS_API void cluster_init_cache(redisCluster *c, redisCachedCluster *rcc);
 
 PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, int *len);
 
-PHP_REDIS_API int cluster_cache_store(zend_string *hash, HashTable *nodes);
+PHP_REDIS_API void cluster_cache_store(zend_string *hash, HashTable *nodes);
 PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash);
 
 /*

From c4de86672b58fa02c71a664a661a36e70b95c2d4 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 26 Oct 2022 11:54:54 -0700
Subject: [PATCH 1664/1986] Documentation:  Redis constructor

---
 redis.stub.php         | 58 ++++++++++++++++++++++++++++++++++++++++++
 redis_arginfo.h        |  2 +-
 redis_legacy_arginfo.h |  2 +-
 3 files changed, 60 insertions(+), 2 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 6f30e337d1..6b84012731 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -8,6 +8,64 @@
 
 class Redis {
 
+    /**
+     * Create a new Redis instance.  If passed sufficient information in the
+     * options array it is also possible to connect to an instance at the same
+     * time.
+     *
+     * @see Redis::connect()
+     * @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
+     *
+     * Following is an example of an options array with the supported
+     * configuration values. Note that all of these values are optional, and you
+     * can instead connect to Redis via PhpRedis' connect() method.
+     *
+     * 
+     *  'localhost',
+     *     'port'           => 6379,
+     *     'readTimeout'    => 2.5,
+     *     'connectTimeout' => 2.5,
+     *     'persistent'     => true,
+     *
+     *     // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass']
+     *     'auth' => ['phpredis', 'phpredis'],
+     *
+     *     // See PHP stream options for valid SSL configuration settings.
+     *     'ssl' => ['verify_peer' => false],
+     *
+     *     // How quickly to retry a connection after we time out or it  closes.
+     *     // Note that this setting is overridden by 'backoff' strategies.
+     *     'retryInterval'  => 100,
+     *
+     *      // Which backoff algorithm to use.  'decorrelated jitter' is
+     *      // likely the best one for most solutiona, but there are many
+     *      // to choose from:
+     *      //     REDIS_BACKOFF_ALGORITHM_DEFAULT
+     *      //     REDIS_BACKOFF_ALGORITHM_CONSTANT
+     *      //     REDIS_BACKOFF_ALGORITHM_UNIFORM
+     *      //     REDIS_BACKOFF_ALGORITHM_EXPONENTIAL
+     *      //     REDIS_BACKOFF_ALGORITHM_FULL_JITTER
+     *      //     REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER
+     *      //     REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER
+     *      //
+     *      // 'base', and 'cap' are in milliseconds and represent the first
+     *      // delay redis will use when reconnecting, and the maximum delay
+     *      // we will reach while retrying.
+     *     'backoff' => [
+     *         'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER,
+     *         'base'      => 500,
+     *         'cap'       => 750,
+     *     ]
+     * ];
+     * ?>
+     * 
+     *
+     * Note: If you do wish to connect via the constructor, only 'host' is
+     *       strictly required, which will cause PhpRedis to connect to that
+     *       host on Redis' default port (6379).
+     */
     public function __construct(array $options = null);
 
     public function __destruct();
diff --git a/redis_arginfo.h b/redis_arginfo.h
index fa162b8f9c..3686df9e87 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7c2ec068711e216a4308a2c405c32204edf60d23 */
+ * Stub hash: 758f508fa69bda30d2af32ce83d19b5645f16263 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 4cd44ad0ef..f5b0801597 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7c2ec068711e216a4308a2c405c32204edf60d23 */
+ * Stub hash: 758f508fa69bda30d2af32ce83d19b5645f16263 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 6982941b0b25c4897a5910558f8e4ee2119322ca Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 26 Oct 2022 12:41:20 -0700
Subject: [PATCH 1665/1986] Documentation:  Document introspection methods

---
 redis.stub.php                 | 72 ++++++++++++++++++++++++++++++++--
 redis_arginfo.h                | 30 +++++++-------
 redis_cluster.stub.php         | 37 +++++++++++++----
 redis_cluster_arginfo.h        | 46 +++++++++++-----------
 redis_cluster_legacy_arginfo.h | 42 ++++++++++----------
 redis_legacy_arginfo.h         | 22 +++++------
 6 files changed, 167 insertions(+), 82 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 6b84012731..590ef953f5 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -70,19 +70,83 @@ public function __construct(array $options = null);
 
     public function __destruct();
 
+    /**
+     * Compress a value with the currently configured compressor as set with
+     * Redis::setOption().
+     *
+     * @see Redis::setOption()
+     *
+     * @param  string $value The value to be compressed
+     * @return string        The compressed result
+     *
+     */
     public function _compress(string $value): string;
 
-    public function _pack(mixed $value): string;
+    /**
+     * Uncompress the provided argument that has been compressed with the
+     * currently configured compressor as set with Redis::setOption().
+     *
+     * @see Redis::setOption()
+     *
+     * @param  string $value  The compressed value to uncompress.
+     * @return string         The uncompressed result.
+     *
+     */
+    public function _uncompress(string $value): string;
 
+    /**
+     * Prefix the passed argument with the currently set key prefix as set
+     * with Redis::setOption().
+     *
+     * @param string  $key The key/string to prefix
+     * @return string      The prefixed string
+     *
+     */
     public function _prefix(string $key): string;
 
+    /**
+     * Serialize the provided value with the currently set serializer as set
+     * with Redis::setOption().
+     *
+     * @see Redis::setOption()
+     *
+     * @param mixed $value The value to serialize
+     * @return string      The serialized result
+     *
+     */
     public function _serialize(mixed $value): string;
 
-    public function _uncompress(string $value): string;
+    /**
+     * Unserialize the passed argument with the currently set serializer as set
+     * with Redis::setOption().
+     *
+     * @see Redis::setOption()
+     *
+     * @param string $value The value to unserialize
+     * @return mixed        The unserialized result
+     *
+     */
+    public function _unserialize(string $value): mixed;
 
-    public function _unpack(string $value): mixed;
+    /**
+     * Pack the provided value with the configured serializer and compressor
+     * as set with Redis::setOption().
+     *
+     * @param  mixed $value  The value to pack
+     * @return string        The packed result having been serialized and
+     *                       compressed.
+     */
+    public function _pack(mixed $value): string;
 
-    public function _unserialize(string $value): mixed;
+    /**
+     * Unpack the provided value with the configured compressor and serializer
+     * as set with Redis::setOption().
+     *
+     * @param  string $value  The value which has been serialized and compressed.
+     * @return mixed          The uncompressed and deserialized value.
+     *
+     */
+    public function _unpack(string $value): mixed;
 
     public function acl(string $subcmd, string ...$args): mixed;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 3686df9e87..6fd5fef17f 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 758f508fa69bda30d2af32ce83d19b5645f16263 */
+ * Stub hash: 4e21096b9ab449cbf12dd9c8a85a875786a9836a */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -12,23 +12,23 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__compress, 0, 1, IS_
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__pack, 0, 1, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis__uncompress arginfo_class_Redis__compress
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__prefix, 0, 1, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis__serialize arginfo_class_Redis__pack
-
-#define arginfo_class_Redis__uncompress arginfo_class_Redis__compress
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__serialize, 0, 1, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__unpack, 0, 1, IS_MIXED, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis__unserialize, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis__unserialize arginfo_class_Redis__unpack
+#define arginfo_class_Redis__pack arginfo_class_Redis__serialize
+
+#define arginfo_class_Redis__unpack arginfo_class_Redis__unserialize
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_acl, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0)
@@ -1121,12 +1121,12 @@ ZEND_END_ARG_INFO()
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, __destruct);
 ZEND_METHOD(Redis, _compress);
-ZEND_METHOD(Redis, _pack);
+ZEND_METHOD(Redis, _uncompress);
 ZEND_METHOD(Redis, _prefix);
 ZEND_METHOD(Redis, _serialize);
-ZEND_METHOD(Redis, _uncompress);
-ZEND_METHOD(Redis, _unpack);
 ZEND_METHOD(Redis, _unserialize);
+ZEND_METHOD(Redis, _pack);
+ZEND_METHOD(Redis, _unpack);
 ZEND_METHOD(Redis, acl);
 ZEND_METHOD(Redis, append);
 ZEND_METHOD(Redis, auth);
@@ -1367,12 +1367,12 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _prefix, arginfo_class_Redis__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 4e167c1281..47cf53bb27 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -10,23 +10,44 @@ class RedisCluster {
 
     public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = NULL, array $context = NULL);
 
+    /**
+     * @see Redis::_compress()
+     */
     public function _compress(string $value): string;
 
-    public function _masters(): array;
+    /**
+     * @see Redis::_uncompress()
+     */
+    public function _uncompress(string $value): string;
 
-    public function _pack(mixed $value): string;
+    /**
+     * @see Redis::_serialize()
+     */
+    public function _serialize(mixed $value): bool|string;
 
-    public function _prefix(string $key): bool|string;
+    /**
+     * @see Redis::_unserialize()
+     */
+    public function _unserialize(string $value): mixed;
 
-    public function _redir(): string|null;
+    /**
+     * @see Redis::_pack()
+     */
+    public function _pack(mixed $value): string;
 
-    public function _serialize(mixed $value): bool|string;
+    /**
+     * @see Redis::_unpack()
+     */
+    public function _unpack(string $value): mixed;
 
-    public function _uncompress(string $value): string;
+    /**
+     * @see Redis::_prefix()
+     */
+    public function _prefix(string $key): bool|string;
 
-    public function _unpack(string $value): mixed;
+    public function _masters(): array;
 
-    public function _unserialize(string $value): mixed;
+    public function _redir(): string|null;
 
     public function acl(string|array $key_or_address, string $subcmd, string ...$args): mixed;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index d46f12b409..9f257cc552 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 895f61badfcc6198d72788d80d517144fc1e8daf */
+ * Stub hash: aed47186facc916ab9732d986c0fde1b86e2dede */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -15,32 +15,32 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__compress, 0,
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, IS_ARRAY, 0)
+#define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster__serialize, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__unserialize, 0, 1, IS_MIXED, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__pack, 0, 1, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_RedisCluster__unpack arginfo_class_RedisCluster__unserialize
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster__prefix, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__redir, 0, 0, IS_STRING, 1)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster__serialize, 0, 1, MAY_BE_BOOL|MAY_BE_STRING)
-	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress
-
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__unpack, 0, 1, IS_MIXED, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__redir, 0, 0, IS_STRING, 1)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__unpack
-
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_acl, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_INFO(0, subcmd, IS_STRING, 0)
@@ -951,14 +951,14 @@ ZEND_END_ARG_INFO()
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _compress);
-ZEND_METHOD(RedisCluster, _masters);
+ZEND_METHOD(RedisCluster, _uncompress);
+ZEND_METHOD(RedisCluster, _serialize);
+ZEND_METHOD(RedisCluster, _unserialize);
 ZEND_METHOD(RedisCluster, _pack);
+ZEND_METHOD(RedisCluster, _unpack);
 ZEND_METHOD(RedisCluster, _prefix);
+ZEND_METHOD(RedisCluster, _masters);
 ZEND_METHOD(RedisCluster, _redir);
-ZEND_METHOD(RedisCluster, _serialize);
-ZEND_METHOD(RedisCluster, _uncompress);
-ZEND_METHOD(RedisCluster, _unpack);
-ZEND_METHOD(RedisCluster, _unserialize);
 ZEND_METHOD(RedisCluster, acl);
 ZEND_METHOD(RedisCluster, append);
 ZEND_METHOD(RedisCluster, bgrewriteaof);
@@ -1160,14 +1160,14 @@ ZEND_METHOD(RedisCluster, zunionstore);
 static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _compress, arginfo_class_RedisCluster__compress, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _pack, arginfo_class_RedisCluster__pack, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bgrewriteaof, arginfo_class_RedisCluster_bgrewriteaof, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index c51eec087c..3f0c10945f 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 895f61badfcc6198d72788d80d517144fc1e8daf */
+ * Stub hash: aed47186facc916ab9732d986c0fde1b86e2dede */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -15,24 +15,24 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__compress, 0, 0, 1)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress
+
+#define arginfo_class_RedisCluster__serialize arginfo_class_RedisCluster__compress
+
+#define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__compress
 
 #define arginfo_class_RedisCluster__pack arginfo_class_RedisCluster__compress
 
+#define arginfo_class_RedisCluster__unpack arginfo_class_RedisCluster__compress
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__prefix, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster__redir arginfo_class_RedisCluster__masters
-
-#define arginfo_class_RedisCluster__serialize arginfo_class_RedisCluster__compress
-
-#define arginfo_class_RedisCluster__uncompress arginfo_class_RedisCluster__compress
-
-#define arginfo_class_RedisCluster__unpack arginfo_class_RedisCluster__compress
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__masters, 0, 0, 0)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_RedisCluster__unserialize arginfo_class_RedisCluster__compress
+#define arginfo_class_RedisCluster__redir arginfo_class_RedisCluster__masters
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_acl, 0, 0, 2)
 	ZEND_ARG_INFO(0, key_or_address)
@@ -802,14 +802,14 @@ ZEND_END_ARG_INFO()
 
 ZEND_METHOD(RedisCluster, __construct);
 ZEND_METHOD(RedisCluster, _compress);
-ZEND_METHOD(RedisCluster, _masters);
+ZEND_METHOD(RedisCluster, _uncompress);
+ZEND_METHOD(RedisCluster, _serialize);
+ZEND_METHOD(RedisCluster, _unserialize);
 ZEND_METHOD(RedisCluster, _pack);
+ZEND_METHOD(RedisCluster, _unpack);
 ZEND_METHOD(RedisCluster, _prefix);
+ZEND_METHOD(RedisCluster, _masters);
 ZEND_METHOD(RedisCluster, _redir);
-ZEND_METHOD(RedisCluster, _serialize);
-ZEND_METHOD(RedisCluster, _uncompress);
-ZEND_METHOD(RedisCluster, _unpack);
-ZEND_METHOD(RedisCluster, _unserialize);
 ZEND_METHOD(RedisCluster, acl);
 ZEND_METHOD(RedisCluster, append);
 ZEND_METHOD(RedisCluster, bgrewriteaof);
@@ -1011,14 +1011,14 @@ ZEND_METHOD(RedisCluster, zunionstore);
 static const zend_function_entry class_RedisCluster_methods[] = {
 	ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _compress, arginfo_class_RedisCluster__compress, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _pack, arginfo_class_RedisCluster__pack, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC)
+	ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, _redir, arginfo_class_RedisCluster__redir, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _uncompress, arginfo_class_RedisCluster__uncompress, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC)
-	ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC)
 	ZEND_ME(RedisCluster, bgrewriteaof, arginfo_class_RedisCluster_bgrewriteaof, ZEND_ACC_PUBLIC)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index f5b0801597..718c18da45 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 758f508fa69bda30d2af32ce83d19b5645f16263 */
+ * Stub hash: 4e21096b9ab449cbf12dd9c8a85a875786a9836a */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -12,7 +12,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__compress, 0, 0, 1)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis__pack arginfo_class_Redis__compress
+#define arginfo_class_Redis__uncompress arginfo_class_Redis__compress
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis__prefix, 0, 0, 1)
 	ZEND_ARG_INFO(0, key)
@@ -20,11 +20,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis__serialize arginfo_class_Redis__compress
 
-#define arginfo_class_Redis__uncompress arginfo_class_Redis__compress
+#define arginfo_class_Redis__unserialize arginfo_class_Redis__compress
 
-#define arginfo_class_Redis__unpack arginfo_class_Redis__compress
+#define arginfo_class_Redis__pack arginfo_class_Redis__compress
 
-#define arginfo_class_Redis__unserialize arginfo_class_Redis__compress
+#define arginfo_class_Redis__unpack arginfo_class_Redis__compress
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1)
 	ZEND_ARG_INFO(0, subcmd)
@@ -952,12 +952,12 @@ ZEND_END_ARG_INFO()
 ZEND_METHOD(Redis, __construct);
 ZEND_METHOD(Redis, __destruct);
 ZEND_METHOD(Redis, _compress);
-ZEND_METHOD(Redis, _pack);
+ZEND_METHOD(Redis, _uncompress);
 ZEND_METHOD(Redis, _prefix);
 ZEND_METHOD(Redis, _serialize);
-ZEND_METHOD(Redis, _uncompress);
-ZEND_METHOD(Redis, _unpack);
 ZEND_METHOD(Redis, _unserialize);
+ZEND_METHOD(Redis, _pack);
+ZEND_METHOD(Redis, _unpack);
 ZEND_METHOD(Redis, acl);
 ZEND_METHOD(Redis, append);
 ZEND_METHOD(Redis, auth);
@@ -1198,12 +1198,12 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, __destruct, arginfo_class_Redis___destruct, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _compress, arginfo_class_Redis__compress, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _prefix, arginfo_class_Redis__prefix, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, _uncompress, arginfo_class_Redis__uncompress, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC)

From 375d093d9482dfd794b5f6fb230b689f0540bbc9 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 26 Oct 2022 15:07:42 -0700
Subject: [PATCH 1666/1986] Documentation:  BITPOS and BITCOUNT

---
 redis.stub.php         | 42 ++++++++++++++++++++++++++++++------------
 redis_arginfo.h        |  2 +-
 redis_legacy_arginfo.h |  2 +-
 3 files changed, 32 insertions(+), 14 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 590ef953f5..37f3bc8dba 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -158,23 +158,41 @@ public function bgSave(): Redis|bool;
 
     public function bgrewriteaof(): Redis|bool;
 
-
+    /**
+     * Count the number of set bits in a Redis string.
+     *
+     * @see https://https://redis.io/commands/bitcount/
+     *
+     * @param string $key     The key in question (must be a string key)
+     * @param int    $start   The index where Redis should start counting.  If ommitted it
+     *                        defaults to zero, which means the start of the string.
+     * @param int    $end     The index where Redis should stop counting.  If ommitted it
+     *                        defaults to -1, meaning the very end of the string.
+     *
+     * @param bool   $bybit   Whether or not Redis should treat $start and $end as bit
+     *                        positions, rather than bytes.
+     *
+     * @return Redis|int|false The number of bits set in the requested range.
+     *
+     */
     public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false;
 
     public function bitop(string $operation, string $deskey, string $srckey, string ...$other_keys): Redis|int|false;
 
     /**
-      Return the position of the first bit set to 0 or 1 in a string.
-
-      @see https://https://redis.io/commands/bitpos/
-
-      @param string $key   The key to check (must be a string)
-      @param bool   $bit   Whether to look for an unset (0) or set (1) bit.
-      @param int    $start Where in the string to start looking.
-      @param int    $end   Where in the string to stop looking.
-      @param bool   $bybit If true, Redis will treat $start and $end as BIT values and not bytes, so if start
-                           was 0 and end was 2, Redis would only search the first two bits.
-     */
+     * Return the position of the first bit set to 0 or 1 in a string.
+     *
+     * @see https://https://redis.io/commands/bitpos/
+     *
+     * @param string $key   The key to check (must be a string)
+     * @param bool   $bit   Whether to look for an unset (0) or set (1) bit.
+     * @param int    $start Where in the string to start looking.
+     * @param int    $end   Where in the string to stop looking.
+     * @param bool   $bybit If true, Redis will treat $start and $end as BIT values and not bytes, so if start
+     *                      was 0 and end was 2, Redis would only search the first two bits.
+     *
+     * @return Redis|int|false The position of the first set or unset bit.
+     **/
     public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false;
 
     public function blPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 6fd5fef17f..4e98b1f5c4 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 4e21096b9ab449cbf12dd9c8a85a875786a9836a */
+ * Stub hash: 21ca57fa960dd8afd88a830b1628e229e831476d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 718c18da45..adda8981f0 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 4e21096b9ab449cbf12dd9c8a85a875786a9836a */
+ * Stub hash: 21ca57fa960dd8afd88a830b1628e229e831476d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 43da8dd9d8e436264630e1623defebc139bad599 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 26 Oct 2022 17:05:54 -0700
Subject: [PATCH 1667/1986] Refactor BRPOPLPUSH and add B[LR]POP documentation

---
 redis.stub.php                 | 59 +++++++++++++++++++++++++++++-----
 redis_arginfo.h                |  6 ++--
 redis_cluster.stub.php         | 10 ++++++
 redis_cluster_arginfo.h        |  2 +-
 redis_cluster_legacy_arginfo.h |  2 +-
 redis_commands.c               | 56 +++++++++++++-------------------
 redis_legacy_arginfo.h         | 12 ++++---
 tests/RedisTest.php            | 10 ++++++
 8 files changed, 106 insertions(+), 51 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 37f3bc8dba..c9a95ffdee 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -161,7 +161,7 @@ public function bgrewriteaof(): Redis|bool;
     /**
      * Count the number of set bits in a Redis string.
      *
-     * @see https://https://redis.io/commands/bitcount/
+     * @see https://redis.io/commands/bitcount/
      *
      * @param string $key     The key in question (must be a string key)
      * @param int    $start   The index where Redis should start counting.  If ommitted it
@@ -182,7 +182,7 @@ public function bitop(string $operation, string $deskey, string $srckey, string
     /**
      * Return the position of the first bit set to 0 or 1 in a string.
      *
-     * @see https://https://redis.io/commands/bitpos/
+     * @see https://redis.io/commands/bitpos/
      *
      * @param string $key   The key to check (must be a string)
      * @param bool   $bit   Whether to look for an unset (0) or set (1) bit.
@@ -195,11 +195,54 @@ public function bitop(string $operation, string $deskey, string $srckey, string
      **/
     public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false;
 
-    public function blPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false;
+    /**
+     * Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified
+     * timeout.  This method may be called in two distinct ways, of which examples are provided below.
+     *
+     * @see https://redis.io/commands/blpop/
+     *
+     * @param string|array     $key_or_keys    This can either be a string key or an array of one or more
+     *                                         keys.
+     * @param string|float|int $timeout_or_key If the previous argument was a string key, this can either
+     *                                         be an additional key, or the timeout you wish to send to
+     *                                         the command.
+     *
+     * 
+     * 
+     * // One way to call this method is in a variadic way, with the final argument being
+     * // the intended timeout.
+     * $redis->blPop('list1', 'list2', 'list3', 1.5);
+     *
+     * // Alternatively, you can send an array of keys
+     * $relay->blPop(['list1', 'list2', 'list3'], 1.5);
+     * ?>
+     * 
+     */
+    public function blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false;
 
-    public function brPop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false;
+    /**
+     * Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.
+     * The calling convention is identical to Redis::blPop() so see that documentation for more details.
+     *
+     * @see https://redis.io/commands/brpop/
+     * @see Redis::blPop()
+     *
+     */
+    public function brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false;
 
-    public function brpoplpush(string $src, string $dst, int $timeout): Redis|string|false;
+    /**
+     * Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,
+     * optionally blocking up to a specified timeout.
+     *
+     * @see https://redis.io/commands/brpoplpush/
+     *
+     * @param string    $src     The source list
+     * @param string    $dst     The destination list
+     * @param int|float $timeout The number of seconds to wait.  Note that you must be connected
+     *                           to Redis >= 6.0.0 to send a floating point timeout.
+     *
+     */
+    public function brpoplpush(string $src, string $dst, int|float $timeout): Redis|string|false;
 
     public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false;
 
@@ -755,7 +798,7 @@ public function slowlog(string $operation, int $length = 0): mixed;
     /**
      * Sort the contents of a Redis key in various ways.
      *
-     * @see https://https://redis.io/commands/sort/
+     * @see https://redis.io/commands/sort/
      *
      * @param string $key     The key you wish to sort
      * @param array  $options Various options controlling how you would like the
@@ -933,7 +976,7 @@ public function zPopMin(string $key, int $value = null): Redis|array|false;
      * How the command works in particular is greatly affected by the options that
      * are passed in.
      *
-     * @see https://https://redis.io/commands/zrange/
+     * @see https://redis.io/commands/zrange/
      * @category zset
      *
      * @param string          $key     The sorted set in question.
@@ -973,7 +1016,7 @@ public function zRangeByScore(string $key, string $start, string $end, array $op
      * This command is similar to ZRANGE except that instead of returning the values directly
      * it will store them in a destination key provided by the user
      *
-     * @see https://https://redis.io/commands/zrange/
+     * @see https://redis.io/commands/zrange/
      * @see Redis::zRange
      * @category zset
      *
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 4e98b1f5c4..ba5641536b 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 21ca57fa960dd8afd88a830b1628e229e831476d */
+ * Stub hash: b42e3cb1c1b50ae6cac12314b83116657365c7c4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -72,7 +72,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitpos, 0, 2, Re
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_blPop, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
+	ZEND_ARG_TYPE_MASK(0, key_or_keys, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
 	ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_LONG, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, extra_args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
@@ -82,7 +82,7 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_brpoplpush, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_MASK(0, timeout, MAY_BE_LONG|MAY_BE_DOUBLE, NULL)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bzPopMax, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 47cf53bb27..de0ddba880 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -75,9 +75,19 @@ public function bitop(string $operation, string $deskey, string $srckey, string
      */
     public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): RedisCluster|int|false;
 
+    /**
+     * See Redis::blpop()
+     */
     public function blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): RedisCluster|array|null|false;
+
+    /**
+     * See Redis::brpop()
+     */
     public function brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): RedisCluster|array|null|false;
 
+    /**
+     * See Redis::brpoplpush()
+     */
     public function brpoplpush(string $srckey, string $deskey, int $timeout): mixed;
 
     public function bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 9f257cc552..68ae1ffbfe 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: aed47186facc916ab9732d986c0fde1b86e2dede */
+ * Stub hash: 3d725e57f5f42243985bca2e64cf727b2475c644 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 3f0c10945f..671f10a4dd 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: aed47186facc916ab9732d986c0fde1b86e2dede */
+ * Stub hash: 3d725e57f5f42243985bca2e64cf727b2475c644 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_commands.c b/redis_commands.c
index d19d6315a7..56446cab4d 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2201,49 +2201,37 @@ redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                          char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    char *key1, *key2;
-    size_t key1_len, key2_len;
-    int key1_free, key2_free;
-    short slot1, slot2;
-    zend_long timeout;
-
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key1, &key1_len,
-                             &key2, &key2_len, &timeout) == FAILURE)
-    {
-        return FAILURE;
-    }
+    zend_string *src = NULL, *dst = NULL;
+    double timeout = 0;
 
-    // Key prefixing
-    key1_free = redis_key_prefix(redis_sock, &key1, &key1_len);
-    key2_free = redis_key_prefix(redis_sock, &key2, &key2_len);
+    ZEND_PARSE_PARAMETERS_START(3, 3)
+        Z_PARAM_STR(src)
+        Z_PARAM_STR(dst)
+        Z_PARAM_DOUBLE(timeout)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
 
-    // In cluster mode, verify the slots match
-    if (slot) {
-        slot1 = cluster_hash_key(key1, key1_len);
-        slot2 = cluster_hash_key(key2, key2_len);
-        if (slot1 != slot2) {
-            php_error_docref(NULL, E_WARNING,
-               "Keys hash to different slots!");
-            if (key1_free) efree(key1);
-            if (key2_free) efree(key2);
-            return FAILURE;
-        }
+    src = redis_key_prefix_zstr(redis_sock, src);
+    dst = redis_key_prefix_zstr(redis_sock, dst);
 
-        // Both slots are the same
-        *slot = slot1;
+    if (slot && (*slot = cluster_hash_key_zstr(src)) != cluster_hash_key_zstr(dst)) {
+        php_error_docref(NULL, E_WARNING, "Keys must hash to the same slot");
+        zend_string_release(src);
+        zend_string_release(dst);
+        return FAILURE;
     }
 
-    // Consistency with Redis, if timeout < 0 use RPOPLPUSH
+    /* Consistency with Redis.  If timeout < 0 use RPOPLPUSH */
     if (timeout < 0) {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "RPOPLPUSH", "ss", key1, key1_len,
-                                     key2, key2_len);
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "RPOPLPUSH", "SS", src, dst);
+    } else if (fabs(timeout - (long)timeout) < .0001) {
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BRPOPLPUSH", "SSd", src, dst, (long)timeout);
     } else {
-        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BRPOPLPUSH", "ssd", key1, key1_len,
-                                     key2, key2_len, timeout);
+        *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BRPOPLPUSH", "SSf", src, dst, timeout);
     }
 
-    if (key1_free) efree(key1);
-    if (key2_free) efree(key2);
+    zend_string_release(src);
+    zend_string_release(dst);
+
     return SUCCESS;
 }
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index adda8981f0..47a600a7fd 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 21ca57fa960dd8afd88a830b1628e229e831476d */
+ * Stub hash: b42e3cb1c1b50ae6cac12314b83116657365c7c4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -67,7 +67,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitpos, 0, 0, 2)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_blPop, 0, 0, 2)
-	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, key_or_keys)
 	ZEND_ARG_INFO(0, timeout_or_key)
 	ZEND_ARG_VARIADIC_INFO(0, extra_args)
 ZEND_END_ARG_INFO()
@@ -80,9 +80,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_brpoplpush, 0, 0, 3)
 	ZEND_ARG_INFO(0, timeout)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_bzPopMax arginfo_class_Redis_blPop
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bzPopMax, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, timeout_or_key)
+	ZEND_ARG_VARIADIC_INFO(0, extra_args)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_bzPopMin arginfo_class_Redis_blPop
+#define arginfo_class_Redis_bzPopMin arginfo_class_Redis_bzPopMax
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bzmpop, 0, 0, 3)
 	ZEND_ARG_INFO(0, timeout)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index cd9f631bb4..466a83a6fa 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -2498,6 +2498,7 @@ public function testBRpopLpush() {
         $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.
 
@@ -2506,6 +2507,15 @@ public function testBRpopLpush() {
         $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));
+
+        if (!$this->minVersionCheck('6.0.0'))
+            return;
+
+        // Redis >= 6.0.0 allows floating point timeouts
+        $st = microtime(true);
+        $this->assertEquals(FALSE, $this->redis->brpoplpush('{list}x', '{list}y', .1));
+        $et = microtime(true);
+        $this->assertTrue($et - $st < 1.0);
     }
 
     public function testZAddFirstArg() {

From 71344612bcce04aba777bbe9b6947837a08600d5 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 26 Oct 2022 19:50:15 -0700
Subject: [PATCH 1668/1986] Documentation:  Add docblocks for several more
 methods.

[skip ci]
---
 redis.stub.php                 | 239 ++++++++++++++++++++++++++-------
 redis_arginfo.h                |  20 ++-
 redis_cluster.stub.php         |  48 +++----
 redis_cluster_arginfo.h        |   2 +-
 redis_cluster_legacy_arginfo.h |   2 +-
 redis_legacy_arginfo.h         |   2 +-
 6 files changed, 222 insertions(+), 91 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index c9a95ffdee..5cad4c3a0d 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -152,10 +152,44 @@ public function acl(string $subcmd, string ...$args): mixed;
 
     public function append(string $key, mixed $value): Redis|int|false;
 
+    /**
+     * Authenticate a Redis connection after its been established.
+     *
+     * @see https://redis.io/commands/auth
+     *
+     * @param mixed $credentials A string password, or an array with one or two string elements.
+     *
+     * @return Redis|bool Whether the AUTH was successful.
+     *
+     * See below for various examples about how this method may be called.
+     *
+     * 
+     * 
+     * $redis->auth('password');
+     * $redis->auth(['password']);
+     * $redis->auth(['username', 'password']);
+     * ?>
+     * 
+     *
+     */
     public function auth(#[\SensitiveParameter] mixed $credentials): Redis|bool;
 
+    /**
+     * Execute a save of the Redis database in the background.
+     *
+     * @see https://redis.io/commands/bgsave
+     *
+     * @return Redis|bool Whether the command was successful.
+     */
     public function bgSave(): Redis|bool;
 
+    /**
+     * Asynchronously rewrite Redis' append-only file
+     *
+     * @see https://redis.io/commands/bgrewriteaof
+     *
+     * @return Redis|bool Whether the command was successful.
+     */
     public function bgrewriteaof(): Redis|bool;
 
     /**
@@ -164,9 +198,9 @@ public function bgrewriteaof(): Redis|bool;
      * @see https://redis.io/commands/bitcount/
      *
      * @param string $key     The key in question (must be a string key)
-     * @param int    $start   The index where Redis should start counting.  If ommitted it
+     * @param int    $start   The index where Redis should start counting.  If omitted it
      *                        defaults to zero, which means the start of the string.
-     * @param int    $end     The index where Redis should stop counting.  If ommitted it
+     * @param int    $end     The index where Redis should stop counting.  If omitted it
      *                        defaults to -1, meaning the very end of the string.
      *
      * @param bool   $bybit   Whether or not Redis should treat $start and $end as bit
@@ -256,7 +290,13 @@ public function blmpop(float $timeout, array $keys, string $from, int $count = 1
 
     public function lmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
 
-    public function clearLastError(): Redis|bool;
+    /**
+     * Reset any last error on the connection to NULL
+     *
+     * @return bool This should always return true or throw an exception if we're not connected.
+     *
+     */
+    public function clearLastError(): bool;
 
     public function client(string $opt, mixed ...$args): mixed;
 
@@ -319,7 +359,7 @@ public function echo(string $str): Redis|string|false;
      *
      * @see https://redis.io/commands/eval/
      *
-     * @param string $script   A string containing the lua script
+     * @param string $script   A string containing the LUA script
      * @param array  $args     An array of arguments to pass to this script
      * @param int    $num_keys How many of the arguments are keys.  This is needed
      *                         as redis distinguishes between key name arguments
@@ -331,7 +371,7 @@ public function echo(string $str): Redis|string|false;
     public function eval(string $script, array $args = [], int $num_keys = 0): mixed;
 
     /**
-     * This is simply the read-only variant of eval, meaning the unerlying script
+     * This is simply the read-only variant of eval, meaning the underlying script
      * may not modify data in redis.
      *
      * @see Redis::eval()
@@ -340,12 +380,12 @@ public function eval_ro(string $script_sha, array $args = [], int $num_keys = 0)
 
     /**
      * Execute a LUA script on the server but instead of sending the script, send
-     * the sha1 hash of the script.
+     * the SHA1 hash of the script.
      *
      * @see https://redis.io/commands/evalsha/
      * @see Redis::eval();
      *
-     * @param string $script_sha The sha1() hash of the lua code.  Note that the script
+     * @param string $script_sha The SHA1 hash of the lua code.  Note that the script
      *                           must already exist on the server, either having been
      *                           loaded with `SCRIPT LOAD` or having been executed directly
      *                           with `EVAL` first.
@@ -355,7 +395,7 @@ public function eval_ro(string $script_sha, array $args = [], int $num_keys = 0)
     public function evalsha(string $sha1, array $args = [], int $num_keys = 0): mixed;
 
     /**
-     * This is simply the read-only variant of evalsha, meaning the unerlying script
+     * This is simply the read-only variant of evalsha, meaning the underlying script
      * may not modify data in redis.
      *
      * @see Redis::evalsha()
@@ -382,7 +422,7 @@ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool;
     public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|bool;
 
     /**
-      Set a key's expiration to a specific unix timestamp in seconds.  If
+      Set a key's expiration to a specific Unix timestamp in seconds.  If
       connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.
 
       @see Redis::expire() For a description of the mode argument.
@@ -445,6 +485,13 @@ public function geosearchstore(string $dst, string $src, array|string $position,
 
     public function get(string $key): mixed;
 
+    /**
+     * Get the authentication information on the connection, if any.
+     *
+     * @see Redis::auth()
+     *
+     * @return mixed The authentication information used to authenticate the connection.
+     */
     public function getAuth(): mixed;
 
     public function getBit(string $key, int $idx): Redis|int|false;
@@ -455,12 +502,35 @@ public function getDBNum(): int;
 
     public function getDel(string $key): Redis|string|bool;
 
+    /**
+     * Return the host or Unix socket we are connected to.
+     *
+     * @return string The host or Unix socket.
+     */
     public function getHost(): string;
 
+    /**
+     * Get the last error returned to us from Redis, if any.
+     *
+     * @return string The error string or NULL if there is none.
+     */
     public function getLastError(): ?string;
 
+    /**
+     * Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode
+     *
+     * @return int The mode we're in.
+     *
+     */
     public function getMode(): int;
 
+    /**
+     * Retrieve the value of a configuration setting as set by Redis::setOption()
+     *
+     * @see Redis::setOption() for a detailed list of options and their values.
+     *
+     * @return mixed The setting itself or false on failure
+     */
     public function getOption(int $option): mixed;
 
     public function getPersistentID(): ?string;
@@ -521,18 +591,18 @@ public function incrBy(string $key, int $value);
     public function incrByFloat(string $key, float $value);
 
     /**
-      Retreive information about the connected redis-server.  If no arguments are passed to
-      this function, redis will return every info field.  Alternatively you may pass a specific
-      section you want returned (e.g. 'server', or 'memory') to receive only information pertaining
-      to that section.
-
-      If connected to Redis server >= 7.0.0 you may pass multiple optional sections.
-
-      @see https://redis.io/commands/info/
-
-      @param string ...$sections Optional section(s) you wish Redis server to return.
-
-      @return Redis|array|false
+     * Retrieve information about the connected redis-server.  If no arguments are passed to
+     * this function, redis will return every info field.  Alternatively you may pass a specific
+     * section you want returned (e.g. 'server', or 'memory') to receive only information pertaining
+     * to that section.
+     *
+     * If connected to Redis server >= 7.0.0 you may pass multiple optional sections.
+     *
+     * @see https://redis.io/commands/info/
+     *
+     * @param string $sections Optional section(s) you wish Redis server to return.
+     *
+     * @return Redis|array|false
      */
     public function info(string ...$sections): Redis|array|false;
 
@@ -616,29 +686,32 @@ public function pconnect(string $host, int $port = 6379, float $timeout = 0, str
     public function persist(string $key): bool;
 
     /**
-       Sets an expiration in milliseconds on a given key.  If connected to
-       Redis >= 7.0.0 you can pass an optional mode argument that modifies
-       how the command will execute.
-
-       @see Redis::expire() for a description of the mode argument.
-
-       @param string  $key  The key to set an expiration on.
-       @param ?string $mode A two character modifier that changes how the
-                            command works.
+     *  Sets an expiration in milliseconds on a given key.  If connected to Redis >= 7.0.0
+     *  you can pass an optional mode argument that modifies how the command will execute.
+     *
+     *  @see Redis::expire() for a description of the mode argument.
+     *
+     *  @param string  $key  The key to set an expiration on.
+     *  @param string $mode  A two character modifier that changes how the
+     *                       command works.
+     *
+     *  @return Redis|bool   True if an expiry was set on the key, and false otherwise.
      */
     public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool;
 
     /**
-      Set a key's expiration to a specific unix timestamp in milliseconds.  If
-      connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.
-
-      @see Redis::expire() For a description of the mode argument.
-
-       @param string  $key  The key to set an expiration on.
-       @param ?string $mode A two character modifier that changes how the
-                            command works.
+     * Set a key's expiration to a specific Unix timestamp in milliseconds.  If connected to
+     * Redis >= 7.0.0 you can pass an optional 'mode' argument.
+     *
+     * @see Redis::expire() For a description of the mode argument.
+     *
+     *  @param string  $key  The key to set an expiration on.
+     *  @param string  $mode A two character modifier that changes how the
+     *                       command works.
+     *
+     *  @return Redis|bool   True if an expiration was set on the key, false otherwise.
      */
-    public function pexpireAt(string $key, int $timestamp, ?string $mode = NULL): bool;
+    public function pexpireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool;
 
     public function pfadd(string $key, array $elements): int;
 
@@ -738,6 +811,68 @@ public function setBit(string $key, int $idx, bool $value);
     public function setRange(string $key, int $start, string $value);
 
 
+    /**
+     * Set a configurable option on the Redis object.
+     *
+     * Following are a list of options you can set:
+     *
+     *  OPTION                     TYPE     DESCRIPTION
+     *  OPT_MAX_RETRIES            int      The maximum number of times Redis will attempt to reconnect
+     *                                      if it gets disconnected, before throwing an exception.
+     *
+     *  OPT_SCAN                   enum     Redis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY
+     *
+     *  OPT_SERIALIZER             int      One of the installed serializers, which can vary depending
+     *                                      on how PhpRedis was compiled.  All of the supported serializers
+     *                                      are as follows:
+     *
+     *                                      Redis::SERIALIZER_NONE
+     *                                      Redis::SERIALIZER_PHP
+     *                                      Redis::SERIALIZER_IGBINARY
+     *                                      Redis::SERIALIZER_MSGPACK
+     *                                      Redis::SERIALIZER_JSON
+     *
+     *                                      Note:  The PHP and JSON serializers are always available.
+     *
+     *  OPT_PREFIX                  string  A string PhpRedis will use to prefix every key we read or write.
+     *                                      To disable the prefix, you may pass an empty string or NULL.
+     *
+     *  OPT_READ_TIMEOUT            double  How long PhpRedis will block for a response from Redis before
+     *                                      throwing a 'read error on connection' exception.
+     *
+     *  OPT_TCP_KEEPALIVE           bool    Set or disable TCP_KEEPALIVE on the connection.
+     *
+     *  OPT_COMPRESSION             enum    Set an automatic compression algorithm to use when reading/writing
+     *                                      data to Redis.  All of the supported compressors are as follows:
+     *
+     *                                      Redis::COMPRESSION_NONE
+     *                                      Redis::COMPRESSION_LZF
+     *                                      Redis::COMPRESSION_LZ4
+     *                                      Redis::COMPRESSION_ZSTD
+     *
+     *                                      Note:  Some of these may not be available depending on how Redis
+     *                                             was compiled.
+     *
+     *  OPT_REPLY_LITERAL           bool    If set to true, PhpRedis will return the literal string Redis returns
+     *                                      for LINE replies (e.g. '+OK'), rather than `true`.
+     *
+     *  OPT_COMPRESSION_LEVEL       int     Set a specific compression level if Redis is compressing data.
+     *
+     *  OPT_NULL_MULTIBULK_AS_NULL  bool    Causes PhpRedis to return `NULL` rather than `false` for NULL MULTIBULK
+     *                                      RESP replies (i.e. `*-1`).
+     *
+     *  OPT_BACKOFF_ALGORITHM       enum    The exponential backoff strategy to use.
+     *  OPT_BACKOFF_BASE            int     The minimum delay between retries when backing off.
+     *  OPT_BACKOFF_CAP             int     The maximum delay between replies when backing off.
+     *
+     * @see Redis::__construct() for details about backoff strategies.
+     *
+     * @param int    $option The option constant.
+     * @param mixed  $value  The option value.
+     *
+     * @return bool  True if the setting was updated, false if not.
+     *
+     */
     public function setOption(int $option, mixed $value): bool;
 
     /** @return bool|Redis */
@@ -765,20 +900,20 @@ public function slaveof(string $host = null, int $port = 6379): bool;
     public function touch(array|string $key_or_array, string ...$more_keys): Redis|int|false;
 
     /**
-     * Interact with Redis' slowlog functionality in variousu ways, depending
-     * on the value of 'operations'.
+     * Interact with Redis' slowlog functionality in various ways, depending
+     * on the value of 'operation'.
      *
      * @see https://redis.io/commands/slowlog/
      * @category administration
      *
      * @param string $operation  The operation you wish to perform.  This can
      *                           be one of the following values:
-     *                           'get'   - Retreive the Redis slowlog as an array.
-     *                           'len'   - Retreive the length of the slowlog.
-     *                           'reset' - Remove all slowlog entries.
+     *                           'GET'   - Retreive the Redis slowlog as an array.
+     *                           'LEN'   - Retreive the length of the slowlog.
+     *                           'RESET' - Remove all slowlog entries.
      * 
      * slowllog('get', -1);  // Retreive all slowlog entries.
+     * $redis->slowlog('get', -1);  // Retreive all slowlog entries.
      * $redis->slowlog('len');       // Retreive slowlog length.
      * $redis->slowlog('reset');     // Reset the slowlog.
      * ?>
@@ -817,7 +952,7 @@ public function slowlog(string $operation, int $length = 0): mixed;
      *     'LIMIT' => [0, 10]        // Return a subset of the data at offset, count
      *     'BY'    => 'weight_*'     // For each element in the key, read data from the
      *                                  external key weight_* and sort based on that value.
-     *     'GET'   => 'weight_*'     // For each element in the source key, retreive the
+     *     'GET'   => 'weight_*'     // For each element in the source key, retrieve the
      *                                  data from key weight_* and return that in the result
      *                                  rather than the source keys' element.  This can
      *                                  be used in combination with 'BY'
@@ -903,8 +1038,8 @@ public function xdel(string $key, array $ids): Redis|int|false;
     /**
      * XGROUP
      *
-     * Perform various operation on consumer groups for a particular Redis STREAM.
-     * What the command does is primarily based on which operation is passed.
+     * Perform various operation on consumer groups for a particular Redis STREAM.  What the command does
+     * is primarily based on which operation is passed.
      *
      * @see https://redis.io/commands/xgroup/
      *
@@ -924,13 +1059,13 @@ public function xdel(string $key, array $ids): Redis|int|false;
      *                               'DESTROY'       - Delete a consumer group from a stream.
      *                                                  Requires:  Key, group.
      * @param string $key            The STREAM we're operating on.
-     * @param string $group          The consumer group wse want to create/modify/delete.
+     * @param string $group          The consumer group we want to create/modify/delete.
      * @param string $id_or_consumer The STREAM id (e.g. '$') or consumer group.  See the operation section
      *                               for information about which to send.
      * @param bool   $mkstream       This flag may be sent in combination with the 'CREATE' operation, and
      *                               cause Redis to also create the STREAM if it doesn't currently exist.
      *
-     * @param bool   $entriesread    Allows you to set Redis's 'entries-read' STREAM value.  This argument is
+     * @param bool   $entriesread    Allows you to set Redis' 'entries-read' STREAM value.  This argument is
      *                               only relevant to the 'CREATE' and 'SETID' operations.
      *                               Note:  Requires Redis >= 7.0.0.
      *
@@ -998,7 +1133,7 @@ public function zPopMin(string $key, int $value = null): Redis|array|false;
      *     'LIMIT'      => [10, 10], // Start at offset 10 and return 10 elements.
      *     'REV'                     // Return the elements in reverse order
      *     'BYSCORE',                // Treat `start` and `end` as scores instead
-     *     'BYLEX'                   // Treat `start` and `end` as lexographical values.
+     *     'BYLEX'                   // Treat `start` and `end` as lexicographical values.
      * ];
      * ?>
      * 
@@ -1021,7 +1156,7 @@ public function zRangeByScore(string $key, string $start, string $end, array $op
      * @category zset
      *
      * @param string           $dstkey  The key to store the resulting element(s)
-     * @param string           $srckey  The source key with element(s) to retreive
+     * @param string           $srckey  The source key with element(s) to retrieve
      * @param string           $start   The starting index to store
      * @param string           $end     The ending index to store
      * @param array|bool|null  $options Our options array that controls how the command will function.
diff --git a/redis_arginfo.h b/redis_arginfo.h
index ba5641536b..0d2ed4ae67 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b42e3cb1c1b50ae6cac12314b83116657365c7c4 */
+ * Stub hash: 823ee9deddd36d3783eb469ce504984b11ac9a50 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -110,15 +110,15 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_lmpop arginfo_class_Redis_zmpop
 
-#define arginfo_class_Redis_clearLastError arginfo_class_Redis_bgSave
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_clearLastError, 0, 0, _IS_BOOL, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_client, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, opt, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_close, 0, 0, _IS_BOOL, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_close arginfo_class_Redis_clearLastError
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 2, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null")
@@ -458,7 +458,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_info, 0, 0, Redi
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, sections, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_isConnected arginfo_class_Redis_close
+#define arginfo_class_Redis_isConnected arginfo_class_Redis_clearLastError
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_keys, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
@@ -593,11 +593,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpire, 0, 2, _IS_B
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpireAt, 0, 2, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfadd, 0, 2, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -668,7 +664,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
 
-#define arginfo_class_Redis_reset arginfo_class_Redis_close
+#define arginfo_class_Redis_reset arginfo_class_Redis_clearLastError
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_restore, 0, 3, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -743,7 +739,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore
 
-#define arginfo_class_Redis_save arginfo_class_Redis_close
+#define arginfo_class_Redis_save arginfo_class_Redis_clearLastError
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index de0ddba880..eaf0eac838 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -62,16 +62,16 @@ public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit
     public function bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys): RedisCluster|bool|int;
 
     /**
-      Return the position of the first bit set to 0 or 1 in a string.
-
-      @see https://https://redis.io/commands/bitpos/
-
-      @param string $key   The key to check (must be a string)
-      @param bool   $bit   Whether to look for an unset (0) or set (1) bit.
-      @param int    $start Where in the string to start looking.
-      @param int    $end   Where in the string to stop looking.
-      @param bool   $bybit If true, Redis will treat $start and $end as BIT values and not bytes, so if start
-                           was 0 and end was 2, Redis would only search the first two bits.
+     * Return the position of the first bit set to 0 or 1 in a string.
+     *
+     * @see https://https://redis.io/commands/bitpos/
+     *
+     * @param string $key   The key to check (must be a string)
+     * @param bool   $bit   Whether to look for an unset (0) or set (1) bit.
+     * @param int    $start Where in the string to start looking.
+     * @param int    $end   Where in the string to stop looking.
+     * @param bool   $bybit If true, Redis will treat $start and $end as BIT values and not bytes, so if start
+     *                      was 0 and end was 2, Redis would only search the first two bits.
      */
     public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): RedisCluster|int|false;
 
@@ -230,20 +230,20 @@ public function incrby(string $key, int $value): RedisCluster|int|false;
     public function incrbyfloat(string $key, float $value): RedisCluster|float|false;
 
     /**
-      Retreive information about the connected redis-server.  If no arguments are passed to
-      this function, redis will return every info field.  Alternatively you may pass a specific
-      section you want returned (e.g. 'server', or 'memory') to receive only information pertaining
-      to that section.
-
-      If connected to Redis server >= 7.0.0 you may pass multiple optional sections.
-
-      @see https://redis.io/commands/info/
-
-      @param string|array $key_or_address Either a key name or array with host and port indicating
-                                          which cluster node we want to send the command to.
-      @param string       ...$sections    Optional section(s) you wish Redis server to return.
-
-      @return Redis|array|false
+     * Retrieve information about the connected redis-server.  If no arguments are passed to
+     * this function, redis will return every info field.  Alternatively you may pass a specific
+     * section you want returned (e.g. 'server', or 'memory') to receive only information pertaining
+     * to that section.
+     *
+     * If connected to Redis server >= 7.0.0 you may pass multiple optional sections.
+     *
+     * @see https://redis.io/commands/info/
+     *
+     * @param string|array $key_or_address Either a key name or array with host and port indicating
+     *                                     which cluster node we want to send the command to.
+     * @param string       $sections       Optional section(s) you wish Redis server to return.
+     *
+     * @return Redis|array|false
      */
     public function info(string|array $key_or_address, string ...$sections): RedisCluster|array|false;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 68ae1ffbfe..3f78295567 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3d725e57f5f42243985bca2e64cf727b2475c644 */
+ * Stub hash: 5bf2e824a39d4139e1d7b21be429995826802994 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 671f10a4dd..a4d8c052ce 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3d725e57f5f42243985bca2e64cf727b2475c644 */
+ * Stub hash: 5bf2e824a39d4139e1d7b21be429995826802994 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 47a600a7fd..d11645af96 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b42e3cb1c1b50ae6cac12314b83116657365c7c4 */
+ * Stub hash: 823ee9deddd36d3783eb469ce504984b11ac9a50 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From af13f951aacda7e8fc0597827812ad4eb194e318 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Wed, 26 Oct 2022 22:06:02 -0700
Subject: [PATCH 1669/1986] Fix BITOP cross-slot bug

Fixes #2210
---
 redis_commands.c    |  6 +++---
 tests/RedisTest.php | 18 ++++++++++++++++++
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 56446cab4d..31c37aa45c 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2825,12 +2825,12 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         // Verify slot if this is a Cluster request
         if (slot) {
             kslot = cluster_hash_key(key, key_len);
-            if (*slot == -1 || kslot != *slot) {
-                php_error_docref(NULL, E_WARNING,
-                    "Warning, not all keys hash to the same slot!");
+            if (*slot != -1 && kslot != *slot) {
+                php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!");
                 zend_string_release(zstr);
                 if (key_free) efree(key);
                 efree(z_args);
+                efree(cmdstr.c);
                 return FAILURE;
             }
             *slot = kslot;
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 466a83a6fa..336cf23ec1 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -230,6 +230,24 @@ public function testBitcount() {
         $this->assertEquals(5, $this->redis->bitcount('bitcountkey', 0, 9, true));
     }
 
+    public function testBitop() {
+        if (!$this->minVersionCheck('2.6.0'))
+            $this->markTestSkipped();
+
+        $this->redis->set("{key}1", "foobar");
+        $this->redis->set("{key}2", "abcdef");
+
+        // Regression test for GitHub issue #2210
+        $this->assertEquals(6, $this->redis->bitop('AND', '{key}1', '{key}2'));
+
+        // Make sure RedisCluster doesn't even send the command.  We don't care
+        // about what Redis returns
+        @$this->redis->bitop('AND', 'key1', 'key2', 'key3');
+        $this->assertEquals(NULL, $this->redis->getLastError());
+
+        $this->redis->del('{key}1', '{key}2');
+    }
+
     public function testBitsets() {
 
         $this->redis->del('key');

From b9de0b9724090f6ebb81b7f6d1ec1697500a5ec8 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 27 Oct 2022 09:26:59 -0700
Subject: [PATCH 1670/1986] Documentation:  Add docblocks for PF* commands and
 ping.

---
 redis.stub.php                 | 52 ++++++++++++++++++++++++++++++----
 redis_arginfo.h                | 14 ++++-----
 redis_cluster.stub.php         | 23 +++++++++++++++
 redis_cluster_arginfo.h        |  2 +-
 redis_cluster_legacy_arginfo.h |  2 +-
 redis_legacy_arginfo.h         |  6 ++--
 6 files changed, 81 insertions(+), 18 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 5cad4c3a0d..5d7bcc3224 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -700,7 +700,7 @@ public function persist(string $key): bool;
     public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool;
 
     /**
-     * Set a key's expiration to a specific Unix timestamp in milliseconds.  If connected to
+     * Set a key's expiration to a specific Unix Timestamp in milliseconds.  If connected to
      * Redis >= 7.0.0 you can pass an optional 'mode' argument.
      *
      * @see Redis::expire() For a description of the mode argument.
@@ -713,14 +713,54 @@ public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool;
      */
     public function pexpireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool;
 
-    public function pfadd(string $key, array $elements): int;
+    /**
+     * Add one or more elements to a Redis HyperLogLog key
+     *
+     * @see https://redis.io/commands/pfadd
+     *
+     * @param string $key      The key in question.
+     *
+     * @param array  $elements One or more elements to add.
+     *
+     * @return Redis|int Returns 1 if the set was altered, and zero if not.
+     */
+    public function pfadd(string $key, array $elements): Redis|int;
 
-    public function pfcount(string $key): int;
+    /**
+     * Retrieve the cardinality of a Redis HyperLogLog key.
+     *
+     * @see https://redis.io/commands/pfcount
+     *
+     * @param string $key The key name we wish to query.
+     *
+     * @return Redis|int The estimated cardinality of the set.
+     */
+    public function pfcount(string $key): Redis|int;
 
-    public function pfmerge(string $dst, array $keys): bool;
+    /**
+     * Merge one or more source HyperLogLog sets into a destination set.
+     *
+     * @see https://redis.io/commands/pfmerge
+     *
+     * @param string $dst     The destination key.
+     * @param array  $srckeys One or more source keys.
+     *
+     * @return Redis|bool Always returns true.
+     */
+    public function pfmerge(string $dst, array $srckeys): Redis|bool;
 
-    /** @return string|Redis */
-    public function ping(string $key = NULL);
+    /**
+     * PING the redis server with an optional string argument.
+     *
+     * @see https://redis.io/commands/ping
+     *
+     * @param string $message An optional string message that Redis will reply with, if passed.
+     *
+     * @return Redis|string|false If passed no message, this command will simply return `true`.
+     *                            If a message is passed, it will return the message.
+     *
+     */
+    public function ping(string $message = NULL): Redis|string|bool;
 
     public function pipeline(): bool|Redis;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 0d2ed4ae67..30605343ae 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 823ee9deddd36d3783eb469ce504984b11ac9a50 */
+ * Stub hash: 891f2eb4099e73c6d7d47f8d673bfa8800db6994 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -595,22 +595,22 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfadd, 0, 2, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfadd, 0, 2, Redis, MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, elements, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfcount, 0, 1, IS_LONG, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfcount, 0, 1, Redis, MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfmerge, 0, 2, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfmerge, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, srckeys, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "NULL")
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ping, 0, 0, Redis, MAY_BE_STRING|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index eaf0eac838..8a705a1636 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -292,12 +292,35 @@ public function pexpire(string $key, int $timeout, ?string $mode = NULL): RedisC
 
     public function pexpireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
 
+
+    /**
+     * @see Redis::pfadd()
+     */
     public function pfadd(string $key, array $elements): RedisCluster|bool;
 
+    /**
+     * @see Redis::pfcount()
+     */
     public function pfcount(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::pfmerge()
+     */
     public function pfmerge(string $key, array $keys): RedisCluster|bool;
 
+    /**
+     * PING an instance in the redis cluster.
+     *
+     * @see Redis::ping()
+     *
+     * @param string|array $key_or_address Either a key name or a two element array with host and
+     *                                     address, informing RedisCluster which node to ping.
+     *
+     * @param string       $message        An optional message to send.
+     *
+     * @return mixed This method always returns `true` if no message was sent, and the message itself
+     *               if one was.
+     */
     public function ping(string|array $key_or_address, ?string $message = NULL): mixed;
 
     public function psetex(string $key, int $timeout, string $value): RedisCluster|bool;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 3f78295567..7a0f716486 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5bf2e824a39d4139e1d7b21be429995826802994 */
+ * Stub hash: 2b379c65c90f7e5e8958bab1b13b3f607fa33c37 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index a4d8c052ce..f8318b6189 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5bf2e824a39d4139e1d7b21be429995826802994 */
+ * Stub hash: 2b379c65c90f7e5e8958bab1b13b3f607fa33c37 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index d11645af96..89539d1dce 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 823ee9deddd36d3783eb469ce504984b11ac9a50 */
+ * Stub hash: 891f2eb4099e73c6d7d47f8d673bfa8800db6994 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -510,11 +510,11 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfmerge, 0, 0, 2)
 	ZEND_ARG_INFO(0, dst)
-	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, srckeys)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ping, 0, 0, 0)
-	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, message)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_pipeline arginfo_class_Redis___destruct

From 2d8a8a44432f3f302653525777f516325002266f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 27 Oct 2022 15:08:18 -0700
Subject: [PATCH 1671/1986] Documentation:  [B]Z[M]POP* doc blocks

---
 redis.stub.php         | 66 ++++++++++++++++++++++++++++++++++++++++++
 redis_arginfo.h        |  2 +-
 redis_legacy_arginfo.h |  2 +-
 3 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 5d7bcc3224..501cc1e5dc 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -278,12 +278,78 @@ public function brPop(string|array $key_or_keys, string|float|int $timeout_or_ke
      */
     public function brpoplpush(string $src, string $dst, int|float $timeout): Redis|string|false;
 
+    /**
+     * POP the maximum scoring element off of one or more sorted sets, blocking up to a specified
+     * timeout if no elements are available.
+     *
+     * @see https://redis.io/commands/bzpopmax
+     *
+     * @param string|array $key_or_keys    Either a string key or an array of one or more keys.
+     * @param string|int  $timeout_or_key  If the previous argument was an array, this argument
+     *                                     must be a timeout value.  Otherwise it could also be
+     *                                     another key.
+     * @param mixed       $extra_args      Can consist of additional keys, until the last argument
+     *                                     which needs to be a timeout.
+     *
+     * Following are examples of the two main ways to call this method.
+     *
+     * 
+     * // Method 1 - Variadic, with the last argument being our timeout
+     * $redis->bzPopMax('key1', 'key2', 'key3', 1.5);
+     *
+     * // Method 2 - A single array of keys, followed by the timeout
+     * $redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);
+     * 
+     *
+     * NOTE:  We reccomend calling this function with an array and a timeout as the other strategy
+     *        may be deprecated in future versions of PhpRedis
+     * ?>
+     */
     public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false;
 
+    /**
+     * POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout
+     * if no elements are available
+     *
+     * This command is identical in semantics to bzPopMax so please see that method for more information.
+     *
+     * @see https://redis.io/commands/bzpopmin
+     * @see Redis::bzPopMax()
+     *
+     */
     public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false;
 
+    /**
+     * POP one or more elements from one or more sorted sets, blocking up to a specified amount of time
+     * when no elements are available.
+     *
+     * @param float  $timeout How long to block if there are no element available
+     * @param array  $keys    The sorted sets to pop from
+     * @param string $from    The string 'MIN' or 'MAX' (case insensitive) telling Redis whether you wish to
+     *                        pop the lowest or highest scoring members from the set(s).
+     * @param int    $count   Pop up to how many elements.
+     *
+     * @return Redis|array|null|false This function will return an array of popped elements, or false
+     *                                depending on whether any elements could be popped within the
+     *                                specified timeout.
+     *
+     * NOTE:  If Redis::OPT_NULL_MULTIBULK_AS_NULL is set to true via Redis::setOption(), this method will
+     *        instead return NULL when Redis doesn't pop any elements.
+     */
     public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
 
+    /**
+     * POP one or more of the highest or lowest scoring elements from one or more sorted sets.
+     *
+     * @see https://redis.io/commands/zmpop
+     *
+     * @param array  $keys  One or more sorted sets
+     * @param string $from  The string 'MIN' or 'MAX' (case insensitive) telling Redis whether you want to
+     *                      pop the lowest or highest scoring elements.
+     * @param int    $count Pop up to how many elements at once.
+     *
+     * @return Redis|array|null|false An array of popped elements or false if none could be popped.
+     */
     public function zmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
 
     public function blmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 30605343ae..d683a42a85 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 891f2eb4099e73c6d7d47f8d673bfa8800db6994 */
+ * Stub hash: 76d56f0a612ec76a5e5f59c90fe09b223f846de6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 89539d1dce..d53dce665d 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 891f2eb4099e73c6d7d47f8d673bfa8800db6994 */
+ * Stub hash: 76d56f0a612ec76a5e5f59c90fe09b223f846de6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From a2b0c86f67ec55326733fe6173c2d2010ac3dafc Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 27 Oct 2022 21:56:04 -0700
Subject: [PATCH 1672/1986] Documentation:  Add detailed docblocks for list pop
 operations.

---
 redis.stub.php                 | 55 +++++++++++++++++++++++++++++++++-
 redis_arginfo.h                |  6 ++--
 redis_cluster.stub.php         | 12 ++++++++
 redis_cluster_arginfo.h        |  2 +-
 redis_cluster_legacy_arginfo.h |  2 +-
 redis_legacy_arginfo.h         | 11 ++++---
 6 files changed, 78 insertions(+), 10 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 501cc1e5dc..42d58abe9f 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -352,8 +352,37 @@ public function bzmpop(float $timeout, array $keys, string $from, int $count = 1
      */
     public function zmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
 
+    /**
+     * Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when
+     * no elements are available.
+     *
+     * @see https://redis.io/commands/blmpop
+     *
+     * @param float  $timeout The number of seconds Redis will block when no elements are available.
+     * @param array  $keys    One or more Redis LISTs to pop from.
+     * @param string $from    The string 'LEFT' or 'RIGHT' (case insensitive), telling Redis whether
+     *                        to pop elements from the beginning or end of the LISTs.
+     * @param int    $count   Pop up to how many elements at once.
+     *
+     * @return Redis|array|null|false One or more elements popped from the list(s) or false if all LISTs
+     *                                were empty.
+     */
     public function blmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false;
 
+    /**
+     * Pop one or more elements off of one or more Redis LISTs.
+     *
+     * @see https://redis.io/commands/lmpop
+     *
+     * @param array  $keys  An array with one or more Redis LIST key names.
+     * @param string $from  The string 'LEFT' or 'RIGHT' (case insensitive), telling Redis whether to pop\
+     *                      elements from the beginning or end of the LISTs.
+     * @param int    $count The maximum number of elements to pop at once.
+     *
+     * @return Redis|array|null|false One or more elements popped from the LIST(s) or false if all the LISTs
+     *                                were empty.
+     *
+     */
     public function lmpop(array $keys, string $from, int $count = 1): Redis|array|null|false;
 
     /**
@@ -849,6 +878,18 @@ public function pubsub(string $command, mixed $arg = null): mixed;
 
     public function punsubscribe(array $patterns): Redis|array|bool;
 
+    /**
+     * Pop one or more elements from the end of a Redis LIST.
+     *
+     * @see https://redis.io/commands/rpop
+     *
+     * @param string $key   A redis LIST key name.
+     * @param int    $count The maximum number of elements to pop at once.
+     *
+     * NOTE:  The `count` argument requires Redis >= 6.2.0
+     *
+     * @return Redis|array|string|bool One ore more popped elements or false if all were empty.
+     */
     public function rPop(string $key, int $count = 0): Redis|array|string|bool;
 
     /** @return string|Redis */
@@ -868,7 +909,19 @@ public function restore(string $key, int $timeout, string $value, ?array $option
 
     public function role(): mixed;
 
-    public function rpoplpush(string $src, string $dst): Redis|string|false;
+    /**
+     * Atomically pop an element off the end of a Redis LIST and push it to the beginning of
+     * another.
+     *
+     * @see https://redis.io/commands/rpoplpush
+     *
+     * @param string $srckey The source key to pop from.
+     * @param string $dstkey The destination key to push to.
+     *
+     * @return Redis|string|false The popped element or false if the source key was empty.
+     *
+     */
+    public function rpoplpush(string $srckey, string $dstkey): Redis|string|false;
 
     public function sAdd(string $key, mixed $value, mixed ...$other_values): Redis|int|false;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index d683a42a85..7f638a06a7 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 76d56f0a612ec76a5e5f59c90fe09b223f846de6 */
+ * Stub hash: 3c2e612a6892a8ae2ac363336c462e24a1333050 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -676,8 +676,8 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_Redis_role arginfo_class_Redis_getAuth
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rpoplpush, 0, 2, Redis, MAY_BE_STRING|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, dstkey, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sAdd, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 8a705a1636..cd316142c6 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -98,8 +98,14 @@ public function bzmpop(float $timeout, array $keys, string $from, int $count = 1
 
     public function zmpop(array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
+    /**
+     * @see Redis::blmpop()
+     */
     public function blmpop(float $timeout, array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
+    /**
+     * @see Redis::lmpop()
+     */
     public function lmpop(array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
     public function clearlasterror(): bool;
@@ -347,8 +353,14 @@ public function restore(string $key, int $timeout, string $value, ?array $option
 
     public function role(string|array $key_or_address): mixed;
 
+    /**
+     * @see Redis::rpop()
+     */
     public function rpop(string $key, int $count = 0): RedisCluster|bool|string|array;
 
+    /**
+     * @see Redis::rpoplpush()
+     */
     public function rpoplpush(string $src, string $dst): RedisCluster|bool|string;
 
     public function rpush(string $key, mixed ...$elements): RedisCluster|int|false;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 7a0f716486..26ed3de7b5 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2b379c65c90f7e5e8958bab1b13b3f607fa33c37 */
+ * Stub hash: 84ef1f62ed4ba37f79eab0897519bba0946b0f26 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index f8318b6189..3766e12553 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2b379c65c90f7e5e8958bab1b13b3f607fa33c37 */
+ * Stub hash: 84ef1f62ed4ba37f79eab0897519bba0946b0f26 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index d53dce665d..d08b92761a 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 76d56f0a612ec76a5e5f59c90fe09b223f846de6 */
+ * Stub hash: 3c2e612a6892a8ae2ac363336c462e24a1333050 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -576,8 +576,8 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_Redis_role arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rpoplpush, 0, 0, 2)
-	ZEND_ARG_INFO(0, src)
-	ZEND_ARG_INFO(0, dst)
+	ZEND_ARG_INFO(0, srckey)
+	ZEND_ARG_INFO(0, dstkey)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sAdd, 0, 0, 2)
@@ -717,7 +717,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_subscribe, 0, 0, 2)
 	ZEND_ARG_INFO(0, cb)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_swapdb arginfo_class_Redis_rpoplpush
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_swapdb, 0, 0, 2)
+	ZEND_ARG_INFO(0, src)
+	ZEND_ARG_INFO(0, dst)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_time arginfo_class_Redis___destruct
 

From e0b24be1ed1c25e53906c58720677f1f5ce4186c Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Fri, 28 Oct 2022 12:29:33 -0700
Subject: [PATCH 1673/1986] Documentation:  Add a BRPOPLPUSH example block

[skip ci]
---
 redis.stub.php         | 26 ++++++++++++++++++++++++++
 redis_arginfo.h        |  2 +-
 redis_legacy_arginfo.h |  2 +-
 3 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 42d58abe9f..b3b3c8e23e 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -920,6 +920,32 @@ public function role(): mixed;
      *
      * @return Redis|string|false The popped element or false if the source key was empty.
      *
+     * 
+     *  'localhost']);
+     *
+     * $redis->pipeline()
+     *       ->del('list1', 'list2')
+     *       ->rpush('list1', 'list1-1', 'list1-2')
+     *       ->rpush('list2', 'list2-1', 'list2-2')
+     *       ->exec();
+     *
+     * var_dump($redis->rpoplpush('list2', 'list1'));
+     * var_dump($redis->lrange('list1', 0, -1));
+     *
+     * // --- OUTPUT ---
+     * // string(7) "list2-2"
+     * //
+     * // array(3) {
+     * //   [0]=>
+     * //   string(7) "list2-2"
+     * //   [1]=>
+     * //   string(7) "list1-1"
+     * //   [2]=>
+     * //   string(7) "list1-2"
+     * // }
+     * ?>
+     * 
      */
     public function rpoplpush(string $srckey, string $dstkey): Redis|string|false;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 7f638a06a7..f3d0919aef 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3c2e612a6892a8ae2ac363336c462e24a1333050 */
+ * Stub hash: 63dd54d205675ceed36354c9b880e6afc7a0604e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index d08b92761a..7a14f9686f 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3c2e612a6892a8ae2ac363336c462e24a1333050 */
+ * Stub hash: 63dd54d205675ceed36354c9b880e6afc7a0604e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 78de25a394264adaebcebe24edb76224d5d8901c Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sat, 29 Oct 2022 22:59:55 +0300
Subject: [PATCH 1674/1986] Use ZEND_STRL in redis_commands.c

---
 redis_commands.c | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 31c37aa45c..2861a134af 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -169,7 +169,7 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args)
     if (!strcasecmp(Z_STRVAL(z_args[0]), "kill")) {
         // Simple SCRIPT_KILL command
         REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT");
-        redis_cmd_append_sstr(cmd, "KILL", sizeof("KILL") - 1);
+        redis_cmd_append_sstr(cmd, ZEND_STRL("KILL"));
     } else if (!strcasecmp(Z_STRVAL(z_args[0]), "flush")) {
         // Simple SCRIPT FLUSH [ASYNC | SYNC]
         if (argc > 1 && (
@@ -180,7 +180,7 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args)
             return NULL;
         }
         REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT");
-        redis_cmd_append_sstr(cmd, "FLUSH", sizeof("FLUSH") - 1);
+        redis_cmd_append_sstr(cmd, ZEND_STRL("FLUSH"));
         if (argc > 1) {
             redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
         }
@@ -192,7 +192,7 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args)
         }
         // Format our SCRIPT LOAD command
         REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT");
-        redis_cmd_append_sstr(cmd, "LOAD", sizeof("LOAD") - 1);
+        redis_cmd_append_sstr(cmd, ZEND_STRL("LOAD"));
         redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
     } else if (!strcasecmp(Z_STRVAL(z_args[0]), "exists")) {
         // Make sure we have a second argument
@@ -201,7 +201,7 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args)
         }
         /* Construct our SCRIPT EXISTS command */
         REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT");
-        redis_cmd_append_sstr(cmd, "EXISTS", sizeof("EXISTS") - 1);
+        redis_cmd_append_sstr(cmd, ZEND_STRL("EXISTS"));
 
         for (i = 1; i < argc; ++i) {
             zstr = zval_get_string(&z_args[i]);
@@ -617,13 +617,13 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
 
     // Append count if we've got one
     if (count) {
-        redis_cmd_append_sstr(&cmdstr,"COUNT",sizeof("COUNT")-1);
+        redis_cmd_append_sstr(&cmdstr, ZEND_STRL("COUNT"));
         redis_cmd_append_sstr_long(&cmdstr, count);
     }
 
     // Append pattern if we've got one
     if (pat_len) {
-        redis_cmd_append_sstr(&cmdstr,"MATCH",sizeof("MATCH")-1);
+        redis_cmd_append_sstr(&cmdstr, ZEND_STRL("MATCH"));
         redis_cmd_append_sstr(&cmdstr,pat,pat_len);
     }
 
@@ -1348,7 +1348,7 @@ redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     // Weights
     if (ht_weights != NULL) {
-        redis_cmd_append_sstr(&cmdstr, "WEIGHTS", sizeof("WEIGHTS")-1);
+        redis_cmd_append_sstr(&cmdstr, ZEND_STRL("WEIGHTS"));
 
         // Process our weights
         ZEND_HASH_FOREACH_VAL(ht_weights, z_ele) {
@@ -1362,7 +1362,7 @@ redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 
     // AGGREGATE
     if (agg_op_len != 0) {
-        redis_cmd_append_sstr(&cmdstr, "AGGREGATE", sizeof("AGGREGATE")-1);
+        redis_cmd_append_sstr(&cmdstr, ZEND_STRL("AGGREGATE"));
         redis_cmd_append_sstr(&cmdstr, agg_op, agg_op_len);
     }
 
@@ -2391,7 +2391,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     ZVAL_NULL(&z_mems[valid]);
 
     // Start command construction
-    redis_cmd_init_sstr(&cmdstr, valid+1, "HMGET", sizeof("HMGET")-1);
+    redis_cmd_init_sstr(&cmdstr, valid+1, ZEND_STRL("HMGET"));
 
     // Prefix our key
     key_free = redis_key_prefix(redis_sock, &key, &key_len);
@@ -2453,7 +2453,7 @@ int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     ht_vals = Z_ARRVAL_P(z_arr);
 
     // Initialize our HMSET command (key + 2x each array entry), add key
-    redis_cmd_init_sstr(&cmdstr, 1+(count*2), "HMSET", sizeof("HMSET")-1);
+    redis_cmd_init_sstr(&cmdstr, 1+(count*2), ZEND_STRL("HMSET"));
     redis_cmd_append_sstr(&cmdstr, key, key_len);
 
     // Start traversing our key => value array
@@ -2806,7 +2806,7 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     if (slot) *slot = -1;
 
     // Initialize command construction, add our operation argument
-    redis_cmd_init_sstr(&cmdstr, argc, "BITOP", sizeof("BITOP")-1);
+    redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("BITOP"));
     redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0]));
 
     // Now iterate over our keys argument
@@ -2967,7 +2967,7 @@ int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     char **cmd, int *cmd_len, short *slot, void **ctx)
 {
     return redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "PFADD", sizeof("PFADD")-1, 0, cmd, cmd_len, slot);
+        ZEND_STRL("PFADD"), 0, cmd, cmd_len, slot);
 }
 
 /* PFMERGE */
@@ -2975,7 +2975,7 @@ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
 {
     return redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-        "PFMERGE", sizeof("PFMERGE")-1, 1, cmd, cmd_len, slot);
+        ZEND_STRL("PFMERGE"), 1, cmd, cmd_len, slot);
 }
 
 /* PFCOUNT */
@@ -3009,7 +3009,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         }
 
         /* Initialize the command with our number of arguments */
-        redis_cmd_init_sstr(&cmdstr, num_keys, "PFCOUNT", sizeof("PFCOUNT")-1);
+        redis_cmd_init_sstr(&cmdstr, num_keys, ZEND_STRL("PFCOUNT"));
 
         /* Append our key(s) */
         ZEND_HASH_FOREACH_VAL(ht_keys, z_key) {
@@ -3043,7 +3043,7 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         } ZEND_HASH_FOREACH_END();
     } else {
         /* Construct our whole command */
-        redis_cmd_init_sstr(&cmdstr, 1, "PFCOUNT", sizeof("PFCOUNT")-1);
+        redis_cmd_init_sstr(&cmdstr, 1, ZEND_STRL("PFCOUNT"));
 
         /* Turn our key into a string if it's a different type */
         zstr = zval_get_string(z_keys);
@@ -3764,7 +3764,7 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     arg_free = redis_key_prefix(redis_sock, &arg, &arg_len);
 
     // Start command construction
-    redis_cmd_init_sstr(&cmdstr, argc, "HDEL", sizeof("HDEL")-1);
+    redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HDEL"));
     redis_cmd_append_sstr(&cmdstr, arg, arg_len);
 
     // Set our slot, free key if we prefixed it
@@ -3852,7 +3852,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     key_free = redis_key_prefix(redis_sock, &key, &key_len);
 
     // Start command construction
-    redis_cmd_init_sstr(&cmdstr, argc, "ZADD", sizeof("ZADD")-1);
+    redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("ZADD"));
     redis_cmd_append_sstr(&cmdstr, key, key_len);
 
     // Set our slot, free key if we prefixed it
@@ -5054,8 +5054,8 @@ int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         HashTable *ht_arr = Z_ARRVAL_P(z_arg);
         smart_string cmdstr = {0};
 
-        redis_cmd_init_sstr(&cmdstr, 1 + arr_len, "COMMAND", sizeof("COMMAND")-1);
-        redis_cmd_append_sstr(&cmdstr, "GETKEYS", sizeof("GETKEYS")-1);
+        redis_cmd_init_sstr(&cmdstr, 1 + arr_len, ZEND_STRL("COMMAND"));
+        redis_cmd_append_sstr(&cmdstr, ZEND_STRL("GETKEYS"));
 
         ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) {
             zend_string *zstr = zval_get_string(z_ele);
@@ -5264,7 +5264,7 @@ int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     redis_cmd_append_sstr(&cmdstr, end, endlen);
 
     if (count > -1) {
-        redis_cmd_append_sstr(&cmdstr, "COUNT", sizeof("COUNT")-1);
+        redis_cmd_append_sstr(&cmdstr, ZEND_STRL("COUNT"));
         redis_cmd_append_sstr_long(&cmdstr, count);
     }
 

From e609fbe8aadea5d8559a435452545ca4acac41a7 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 29 Oct 2022 15:21:07 -0700
Subject: [PATCH 1675/1986] Documentation:  Add detailed docs for several set
 operations

SADD[ARRAY]
SSINTER[STORE]
SUNION[STORE]
SDIFF[STORE]

[skip ci]
---
 redis.stub.php                 | 210 +++++++++++++++++++++++++++++++++
 redis_arginfo.h                |   2 +-
 redis_cluster.stub.php         |  24 ++++
 redis_cluster_arginfo.h        |   2 +-
 redis_cluster_legacy_arginfo.h |   2 +-
 redis_legacy_arginfo.h         |   2 +-
 6 files changed, 238 insertions(+), 4 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index b3b3c8e23e..51aff493ab 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -949,18 +949,177 @@ public function role(): mixed;
      */
     public function rpoplpush(string $srckey, string $dstkey): Redis|string|false;
 
+    /**
+     * Add one or more values to a Redis SET key.
+     *
+     * @see https://redis.io/commands/sadd
+
+     * @param string $key           The key name
+     * @param mixed  $member        A value to add to the set.
+     * @param mixed  $other_members One or more additional values to add
+     *
+     * @return Redis|int|false The number of values added to the set.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('myset');
+     *
+     * var_dump($redis->sadd('myset', 'foo', 'bar', 'baz'));
+     * var_dump($redis->sadd('myset', 'foo', 'new'));
+     *
+     * // --- OUTPUT ---
+     * // int(3)
+     * // int(1)
+     * ?>
+     * 
+     */
     public function sAdd(string $key, mixed $value, mixed ...$other_values): Redis|int|false;
 
+    /**
+     * Add one ore more values to a Redis SET key.  This is an alternative to Redis::sadd() but
+     * instead of being variadic, takes a single array of values.
+     *
+     * @see https://redis.io/commands/sadd
+     * @see Redis::sadd()
+     *
+     * @param string $key       The set to add values to.
+     * @param array  $values    One or more members to add to the set.
+     * @return Redis|int|false  The number of members added to the set.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('myset');
+     *
+     * var_dump($redis->sAddArray('myset', ['foo', 'bar', 'baz']));
+     * var_dump($redis->sAddArray('myset', ['foo', 'new']));
+     *
+     * // --- OUTPUT ---
+     * // int(3)
+     * // int(1)
+     * ?>
+     * 
+     */
     public function sAddArray(string $key, array $values): int;
 
+    /**
+     * Given one or more Redis SETS, this command returns all of the members from the first
+     * set that are not in any subsequent set.
+     *
+     * @see https://redis.io/commands/sdiff
+     *
+     * @param string $key        The first set
+     * @param string $other_keys One or more additional sets
+     *
+     * @return Redis|array|false Returns the elements from keys 2..N that don't exist in the
+     *                           first sorted set, or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->pipeline()
+     *       ->del('set1', 'set2', 'set3')
+     *       ->sadd('set1', 'apple', 'banana', 'carrot', 'date')
+     *       ->sadd('set2', 'carrot')
+     *       ->sadd('set3', 'apple', 'carrot', 'eggplant')
+     *       ->exec();
+     *
+     * // NOTE:  'banana' and 'date' are in set1 but none of the subsequent sets.
+     * var_dump($redis->sdiff('set1', 'set2', 'set3'));
+     *
+     * // --- OUTPUT ---
+     * array(2) {
+     *   [0]=>
+     *   string(6) "banana"
+     *   [1]=>
+     *   string(4) "date"
+     * }
+     * ?>
+     */
     public function sDiff(string $key, string ...$other_keys): Redis|array|false;
 
+    /**
+     * This method performs the same operation as SDIFF except it stores the resulting diff
+     * values in a specified destination key.
+     *
+     * @see https://redis.io/commands/sdiffstore
+     * @see Redis::sdiff()
+     *
+     * @param string $dst The key where to store the result
+     * @param string $key The first key to perform the DIFF on
+     * @param string $other_keys One or more additional keys.
+     *
+     * @return Redis|int|false The number of values stored in the destination set or false on failure.
+     */
     public function sDiffStore(string $dst, string $key, string ...$other_keys): Redis|int|false;
 
+    /**
+     * Given one or more Redis SET keys, this command will return all of the elements that are
+     * in every one.
+     *
+     * @see https://redis.io/commands/sinter
+     *
+     * @param string $key        The first SET key to intersect.
+     * @param string $other_keys One or more Redis SET keys.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->pipeline()
+     *       ->del('alice_likes', 'bob_likes', 'bill_likes')
+     *       ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato')
+     *       ->sadd('bob_likes', 'asparagus', 'carrot', 'potato')
+     *       ->sadd('bill_likes', 'broccoli', 'potato')
+     *       ->exec();
+     *
+     * // NOTE:  'potato' is the only value in all three sets
+     * var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes'));
+     *
+     * // --- OUTPUT ---
+     * // array(1) {
+     * //   [0]=>
+     * //   string(6) "potato"
+     * // }
+     * ?>
+     * 
+     */
     public function sInter(array|string $key, string ...$other_keys): Redis|array|false;
 
     public function sintercard(array $keys, int $limit = -1): Redis|int|false;
 
+    /**
+     * Perform the intersection of one or more Redis SETs, storing the result in a destination
+     * key, rather than returning them.
+     *
+     * @see https://redis.io/commands/sinterstore
+     * @see Redis::sinter()
+     *
+     * @param array|string $key_or_keys Either a string key, or an array of keys (with at least two
+     *                                  elements, consisting of the destination key name and one
+     *                                  or more source keys names.
+     * @param string       $other_keys  If the first argument was a string, subsequent arguments should
+     *                                  be source key names.
+     *
+     * @return Redis|int|false          The number of values stored in the destination key or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * // OPTION 1:  A single array
+     * $redis->sInterStore(['dst', 'src1', 'src2', 'src3']);
+     *
+     * // OPTION 2:  Variadic
+     * $redis->sInterStore('dst', 'src1', 'src'2', 'src3');
+     * ?>
+     * 
+     */
     public function sInterStore(array|string $key, string ...$other_keys): Redis|int|false;
 
     public function sMembers(string $key): Redis|array|false;
@@ -973,8 +1132,59 @@ public function sPop(string $key, int $count = 0): Redis|string|array|false;
 
     public function sRandMember(string $key, int $count = 0): Redis|string|array|false;
 
+    /**
+     * Returns the union of one or more Redis SET keys.
+     *
+     * @see https://redis.io/commands/sunion
+     *
+     * @param string $key         The first SET to do a union with
+     * @param string $other_keys  One or more subsequent keys
+     *
+     * @return Redis|array|false  The union of the one or more input sets or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->pipeline()
+     *       ->del('set1', 'set2', 'set3')
+     *       ->sadd('set1', 'apple', 'banana', 'carrot')
+     *       ->sadd('set2', 'apple', 'carrot', 'fish')
+     *       ->sadd('set3', 'carrot', 'fig', 'eggplant');
+     *
+     * var_dump($redis->sunion('set1', 'set2', 'set3'));
+     *
+     * // --- OPUTPUT ---
+     * // array(5) {
+     * //   [0]=>
+     * //   string(6) "banana"
+     * //   [1]=>
+     * //   string(5) "apple"
+     * //   [2]=>
+     * //   string(4) "fish"
+     * //   [3]=>
+     * //   string(6) "carrot"
+     * //   [4]=>
+     * //   string(8) "eggplant"
+     * // }
+     * ?>
+     * 
+     */
     public function sUnion(string $key, string ...$other_keys): Redis|array|false;
 
+    /**
+     * Perform a union of one or more Redis SET keys and store the result in a new set
+     *
+     * @see https://redis.io/commands/sunionstore
+     * @see Redis::sunion()
+     *
+     * @param string $dst        The destination key
+     * @param string $key        The first source key
+     * @param string $other_keys One or more additional source keys
+     *
+     * @return Redis|int|false   The number of elements stored in the destination SET or
+     *                           false on failure.
+     */
     public function sUnionStore(string $dst, string $key, string ...$other_keys): Redis|int|false;
 
     public function save(): bool;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index f3d0919aef..39085674b5 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 63dd54d205675ceed36354c9b880e6afc7a0604e */
+ * Stub hash: 8e423eab8d5b732655e7fcaab4d0800aadc38747 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index cd316142c6..35e72cf611 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -367,8 +367,14 @@ public function rpush(string $key, mixed ...$elements): RedisCluster|int|false;
 
     public function rpushx(string $key, string $value): RedisCluster|bool|int;
 
+    /**
+     * @see Redis::sadd()
+     */
     public function sadd(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false;
 
+    /**
+     * @see Redis::saddarray()
+     */
     public function saddarray(string $key, array $values): RedisCluster|bool|int;
 
     public function save(string|array $key_or_address): RedisCluster|bool;
@@ -379,8 +385,14 @@ public function scard(string $key): RedisCluster|int|false;
 
     public function script(string|array $key_or_address, mixed ...$args): mixed;
 
+    /**
+     * @see Redis::sdiff()
+     */
     public function sdiff(string $key, string ...$other_keys): RedisCluster|array|false;
 
+    /**
+     * @see Redis::sdiffstore()
+     */
     public function sdiffstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false;
 
     public function set(string $key, mixed $value, mixed $options = NULL): RedisCluster|string|bool;
@@ -395,10 +407,16 @@ public function setoption(int $option, mixed $value): bool;
 
     public function setrange(string $key, int $offset, string $value): RedisCluster|int|false;
 
+    /**
+     * @see Redis::sinter()
+     */
     public function sinter(array|string $key, string ...$other_keys): RedisCluster|array|false;
 
     public function sintercard(array $keys, int $limit = -1): RedisCluster|int|false;
 
+    /**
+     * @see Redis::sinterstore()
+     */
     public function sinterstore(array|string $key, string ...$other_keys): RedisCluster|int|false;
 
     public function sismember(string $key, mixed $value): RedisCluster|bool;
@@ -431,8 +449,14 @@ public function strlen(string $key): RedisCluster|int|false;
 
     public function subscribe(array $channels, callable $cb): void;
 
+    /**
+     * @see Redis::sunion()
+     */
     public function sunion(string $key, string ...$other_keys): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::sunionstore()
+     */
     public function sunionstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false;
 
     public function time(string|array $key_or_address): RedisCluster|bool|array;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 26ed3de7b5..f5fb6babc6 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 84ef1f62ed4ba37f79eab0897519bba0946b0f26 */
+ * Stub hash: 836411b2d661943a61fd5a2aadac30997a506cde */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 3766e12553..feec42989e 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 84ef1f62ed4ba37f79eab0897519bba0946b0f26 */
+ * Stub hash: 836411b2d661943a61fd5a2aadac30997a506cde */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 7a14f9686f..145984d8dc 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 63dd54d205675ceed36354c9b880e6afc7a0604e */
+ * Stub hash: 8e423eab8d5b732655e7fcaab4d0800aadc38747 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From c4aef9567684e22fe5d72ecb43965140506c3953 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sat, 29 Oct 2022 21:43:33 -0700
Subject: [PATCH 1676/1986] Documentation:  More docblocks and examples

[skip ci]
---
 redis.stub.php                 | 158 +++++++++++++++++++++++++++++++--
 redis_arginfo.h                |   4 +-
 redis_cluster.stub.php         |   3 +
 redis_cluster_arginfo.h        |   2 +-
 redis_cluster_legacy_arginfo.h |   2 +-
 redis_legacy_arginfo.h         |   2 +-
 6 files changed, 158 insertions(+), 13 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 51aff493ab..f8a87a8282 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -40,7 +40,7 @@ class Redis {
      *     'retryInterval'  => 100,
      *
      *      // Which backoff algorithm to use.  'decorrelated jitter' is
-     *      // likely the best one for most solutiona, but there are many
+     *      // likely the best one for most solution, but there are many
      *      // to choose from:
      *      //     REDIS_BACKOFF_ALGORITHM_DEFAULT
      *      //     REDIS_BACKOFF_ALGORITHM_CONSTANT
@@ -143,7 +143,7 @@ public function _pack(mixed $value): string;
      * as set with Redis::setOption().
      *
      * @param  string $value  The value which has been serialized and compressed.
-     * @return mixed          The uncompressed and deserialized value.
+     * @return mixed          The uncompressed and eserialized value.
      *
      */
     public function _unpack(string $value): mixed;
@@ -294,6 +294,7 @@ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis|
      * Following are examples of the two main ways to call this method.
      *
      * 
+     * bzPopMax('key1', 'key2', 'key3', 1.5);
      *
@@ -1187,14 +1188,114 @@ public function sUnion(string $key, string ...$other_keys): Redis|array|false;
      */
     public function sUnionStore(string $dst, string $key, string ...$other_keys): Redis|int|false;
 
-    public function save(): bool;
+    /**
+     * Persist the Redis database to disk.  This command will block the server until the save is
+     * completed.  For a nonblocking alternative, see Redis::bgsave().
+     *
+     * @see https://redis.io/commands/save
+     * @see Redis::bgsave()
+     *
+     * @return Redis|bool Returns true unless an error occurs.
+     */
+    public function save(): Redis|bool;
 
+    /**
+     * Incrementally scan the Redis keyspace, with optional pattern and type matching.
+     *
+     * @see https://redis.io/commands/scan
+     * @see Redis::setOption()
+     *
+     * @param int    $iterator The cursor returned by Redis for every subsequent call to SCAN.  On
+     *                         the initial invocation of the call, it should be initialized by the
+     *                         caller to NULL.  Each time SCAN is invoked, the iterator will be
+     *                         updated to a new number, until finally Redis will set the value to
+     *                         zero, indicating that the scan is complete.
+     *
+     * @param string $pattern  An optional glob-style pattern for matching key names.  If passed as
+     *                         NULL, it is the equivalent of sending '*' (match every key).
+     *
+     * @param int    $count    A hint to redis that tells it how many keys to return in a single
+     *                         call to SCAN.  The larger the number, the longer Redis may block
+     *                         clients while iterating the key space.
+     *
+     * @param string $type     An optional argument to specify which key types to scan (e.g.
+     *                         'STRING', 'LIST', 'SET')
+     *
+     * @return array|false     An array of keys, or false if no keys were returned for this
+     *                         invocation of scan.  Note that it is possible for Redis to return
+     *                         zero keys before having scanned the entire key space, so the caller
+     *                         should instead continue to SCAN until the iterator reference is
+     *                         returned to zero.
+     *
+     * A note about Redis::SCAN_NORETRY and Redis::SCAN_RETRY.
+     *
+     * For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of
+     * keys with a nonzero iterator.  This can happen when matching against a pattern that very few
+     * keys match inside a key space with a great many keys.  The following example demonstrates how
+     * to use Redis::scan() with the option disabled and enabled.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+     *
+     * $it = NULL;
+     *
+     * do {
+     *     $keys = $redis->scan($it, '*zorg*');
+     *     foreach ($keys as $key) {
+     *         echo "KEY: $key\n";
+     *     }
+     * } while ($it != 0);
+     *
+     * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+     *
+     * $it = NULL;
+     *
+     * // When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an
+     * // empty array of keys when the iterator is nonzero.
+     * while ($keys = $redis->scan($it, '*zorg*')) {
+     *     foreach ($keys as $key) {
+     *         echo "KEY: $key\n";
+     *     }
+     * }
+     * ?>
+     * 
+     */
     public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, string $type = NULL): array|false;
 
     public function scard(string $key): Redis|int|false;
 
     public function script(string $command, mixed ...$args): mixed;
 
+    /**
+     * Select a specific Redis database.
+     *
+     * @param int $db The database to select.  Note that by default Redis has 16 databases (0-15).
+     *
+     * @return Redis|bool true on success and false on failure
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->select(1);
+     * $redis->set('this_is_db_1', 'test');
+     *
+     * $redis->select(0);
+     * var_dump($redis->exists('this_is_db_1'));
+     *
+     * $redis->select(1);
+     * var_dump($redis->exists('this_is_db_1'));
+     *
+     * // --- OUTPUT ---
+     * // int(0)
+     * // int(1)
+     * ?>
+     * 
+     */
     public function select(int $db): Redis|bool;
 
     public function set(string $key, mixed $value, mixed $opt = NULL): Redis|string|bool;
@@ -1217,6 +1318,21 @@ public function setRange(string $key, int $start, string $value);
      *
      *  OPT_SCAN                   enum     Redis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY
      *
+     *                                      Redis::SCAN_NORETRY (default)
+     *                                      --------------------------------------------------------
+     *                                      PhpRedis will only call `SCAN` once for every time the
+     *                                      user calls Redis::scan().  This means it is possible for
+     *                                      an empty array of keys to be returned while there are
+     *                                      still more keys to be processed.
+     *
+     *                                      Redis::SCAN_RETRY
+     *                                      --------------------------------------------------------
+     *                                      PhpRedis may make multiple calls to `SCAN` for every
+     *                                      time the user calls Redis::scan(), and will never return
+     *                                      an empty array of keys unless Redis returns the iterator
+     *                                      to zero (meaning the `SCAN` is complete).
+     *
+     *
      *  OPT_SERIALIZER             int      One of the installed serializers, which can vary depending
      *                                      on how PhpRedis was compiled.  All of the supported serializers
      *                                      are as follows:
@@ -1303,8 +1419,8 @@ public function touch(array|string $key_or_array, string ...$more_keys): Redis|i
      *
      * @param string $operation  The operation you wish to perform.  This can
      *                           be one of the following values:
-     *                           'GET'   - Retreive the Redis slowlog as an array.
-     *                           'LEN'   - Retreive the length of the slowlog.
+     *                           'GET'   - Retrieve the Redis slowlog as an array.
+     *                           'LEN'   - Retrieve the length of the slowlog.
      *                           'RESET' - Remove all slowlog entries.
      * 
      * 
      *
      * @param int    $length     This optional argument can be passed when operation
-     *                           is 'get' and will specify how many elements to retreive.
+     *                           is 'get' and will specify how many elements to retrieve.
      *                           If omitted Redis will send up to a default number of
      *                           entries, which is configurable.
      *
@@ -1385,6 +1501,32 @@ public function sortDesc(string $key, ?string $pattern = null, mixed $get = null
      */
     public function sortDescAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array;
 
+    /**
+     * Remove one or more values from a Redis SET key.
+     *
+     * @see https://redis.io/commands/srem
+     *
+     * @param string $key         The Redis SET key in question.
+     * @param mixed  $value       The first value to remove.
+     * @param mixed  $more_values One or more additional values to remove.
+     *
+     * @return Redis|int|false    The number of values removed from the set or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->pipeline()->del('set1')
+     *                   ->sadd('set1', 'foo', 'bar', 'baz')
+     *                   ->exec();
+     *
+     * var_dump($redis->sRem('set1', 'foo', 'bar', 'not-in-the-set'));
+     *
+     * // --- OUTPUT ---
+     * // int(2)
+     * ?>
+     * 
+     */
     public function srem(string $key, mixed $value, mixed ...$other_values): Redis|int|false;
 
     public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
@@ -1502,7 +1644,7 @@ public function zPopMax(string $key, int $value = null): Redis|array|false;
     public function zPopMin(string $key, int $value = null): Redis|array|false;
 
     /**
-     * Retreive a range of elements of a sorted set between a start and end point.
+     * Retrieve a range of elements of a sorted set between a start and end point.
      * How the command works in particular is greatly affected by the options that
      * are passed in.
      *
@@ -1556,7 +1698,7 @@ public function zRangeByScore(string $key, string $start, string $end, array $op
      * @param string           $end     The ending index to store
      * @param array|bool|null  $options Our options array that controls how the command will function.
      *
-     * @return Redis|int|false The number of elements stored in dstkey or false on failure.
+     * @return Redis|int|false The number of elements stored in $dstkey or false on failure.
      *
      * See Redis::zRange for a full description of the possible options.
      */
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 39085674b5..4d0a341f98 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8e423eab8d5b732655e7fcaab4d0800aadc38747 */
+ * Stub hash: ed7bf573247cb4bd4ead254d75ad5b60a97313be */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -739,7 +739,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sUnionStore arginfo_class_Redis_sDiffStore
 
-#define arginfo_class_Redis_save arginfo_class_Redis_clearLastError
+#define arginfo_class_Redis_save arginfo_class_Redis_bgSave
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 35e72cf611..e5fb7e88d7 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -441,6 +441,9 @@ public function spop(string $key, int $count = 0): RedisCluster|string|array|fal
 
     public function srandmember(string $key, int $count = 0): RedisCluster|string|array|false;
 
+    /**
+     * @see Redis::srem
+     */
     public function srem(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false;
 
     public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index f5fb6babc6..b8174f47e4 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 836411b2d661943a61fd5a2aadac30997a506cde */
+ * Stub hash: 37a25fa4537a2d10a2c2a8b09292e77e5cc35d59 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index feec42989e..8a82598ad6 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 836411b2d661943a61fd5a2aadac30997a506cde */
+ * Stub hash: 37a25fa4537a2d10a2c2a8b09292e77e5cc35d59 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 145984d8dc..d6f5641aa5 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8e423eab8d5b732655e7fcaab4d0800aadc38747 */
+ * Stub hash: ed7bf573247cb4bd4ead254d75ad5b60a97313be */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From f2bb2cdb5a8cc770d22b6d516e230d2cf544929b Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 7 Aug 2022 17:08:15 +0300
Subject: [PATCH 1677/1986] Issue #2114

Redis Sentinel TLS support
---
 redis_sentinel.c                | 9 ++++++---
 redis_sentinel.stub.php         | 2 +-
 redis_sentinel_arginfo.h        | 7 ++++---
 redis_sentinel_legacy_arginfo.h | 3 ++-
 4 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/redis_sentinel.c b/redis_sentinel.c
index 55851f1025..2d506bd2ec 100644
--- a/redis_sentinel.c
+++ b/redis_sentinel.c
@@ -47,12 +47,12 @@ PHP_METHOD(RedisSentinel, __construct)
     zend_long port = 26379, retry_interval = 0;
     redis_sentinel_object *obj;
     zend_string *host;
-    zval *auth = NULL, *zv = NULL;
+    zval *auth = NULL, *context = NULL, *zv = NULL;
 
-    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ldz!ldz",
+    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ldz!ldza",
                                 &host, &port, &timeout, &zv,
                                 &retry_interval, &read_timeout,
-                                &auth) == FAILURE) {
+                                &auth, &context) == FAILURE) {
         RETURN_FALSE;
     }
 
@@ -92,6 +92,9 @@ PHP_METHOD(RedisSentinel, __construct)
     if (auth) {
         redis_sock_set_auth_zval(obj->sock, auth);
     }
+    if (context) {
+        redis_sock_set_stream_context(obj->sock, context);
+    }
     obj->sock->sentinel = 1;
 }
 
diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php
index c3fc5a9e00..304cd41456 100644
--- a/redis_sentinel.stub.php
+++ b/redis_sentinel.stub.php
@@ -8,7 +8,7 @@
 
 class RedisSentinel {
 
-    public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = NULL, int $retry_interval = 0, float $read_timeout = 0, #[\SensitiveParameter] mixed $auth = NULL);
+    public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = null, int $retry_interval = 0, float $read_timeout = 0, #[\SensitiveParameter] mixed $auth = null, array $context = null);
 
 	/** @return bool|RedisSentinel */
     public function ckquorum(string $master);
diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h
index 58a5bc4c40..7059560d64 100644
--- a/redis_sentinel_arginfo.h
+++ b/redis_sentinel_arginfo.h
@@ -1,14 +1,15 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 946942bc5a7612650fc0416902778452f6860d13 */
+ * Stub hash: 4055ace9f1cf20bef89bdb5d3219470b4c8915e6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, IS_MIXED, 0, "NULL")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, IS_MIXED, 0, "null")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0")
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "NULL")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1)
diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h
index 30b58dff51..16af15b6bc 100644
--- a/redis_sentinel_legacy_arginfo.h
+++ b/redis_sentinel_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 946942bc5a7612650fc0416902778452f6860d13 */
+ * Stub hash: 4055ace9f1cf20bef89bdb5d3219470b4c8915e6 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, host)
@@ -9,6 +9,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, retry_interval)
 	ZEND_ARG_INFO(0, read_timeout)
 	ZEND_ARG_INFO(0, auth)
+	ZEND_ARG_INFO(0, context)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1)

From 2bb64038837419a2d661376a56e5f40c51144522 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 30 Oct 2022 14:33:58 +0200
Subject: [PATCH 1678/1986] Issue #1894

Add the CH, NX, XX arguments to GEOADD
---
 redis.c                        |  2 +-
 redis.stub.php                 |  2 +-
 redis_arginfo.h                |  5 ++-
 redis_cluster.c                |  4 +-
 redis_cluster.stub.php         |  2 +-
 redis_cluster_arginfo.h        |  5 ++-
 redis_cluster_legacy_arginfo.h |  5 ++-
 redis_commands.c               | 73 ++++++++++++++++++++++++++++++++++
 redis_commands.h               |  3 ++
 redis_legacy_arginfo.h         |  5 ++-
 tests/RedisTest.php            |  6 +--
 11 files changed, 96 insertions(+), 16 deletions(-)

diff --git a/redis.c b/redis.c
index 89caae9030..6d3b6292c7 100644
--- a/redis.c
+++ b/redis.c
@@ -3470,7 +3470,7 @@ PHP_METHOD(Redis, pfmerge) {
  */
 
 PHP_METHOD(Redis, geoadd) {
-    REDIS_PROCESS_KW_CMD("GEOADD", redis_key_varval_cmd, redis_long_response);
+    REDIS_PROCESS_CMD(geoadd, redis_long_response);
 }
 
 PHP_METHOD(Redis, geohash) {
diff --git a/redis.stub.php b/redis.stub.php
index f8a87a8282..fddcec9bd6 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -559,7 +559,7 @@ public function flushAll(?bool $sync = null): Redis|bool;
      */
     public function flushDB(?bool $sync = null): Redis|bool;
 
-    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): Redis|int|false;
+    public function geoadd(string $key, ?array $options = null, float $lng, float $lat, string $member, mixed ...$other_triples): Redis|int|false;
 
     public function geodist(string $key, string $src, string $dst, ?string $unit = null): Redis|float|false;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 4d0a341f98..e685218db6 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ed7bf573247cb4bd4ead254d75ad5b60a97313be */
+ * Stub hash: 0ff87f3b3f10dacbef8455ba09b17fa4107c7999 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -237,8 +237,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geoadd, 0, 4, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geoadd, 0, 5, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
diff --git a/redis_cluster.c b/redis_cluster.c
index df731c8ecb..c3fdef3ccd 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -2759,9 +2759,9 @@ PHP_METHOD(RedisCluster, slowlog) {
 }
 /* }}} */
 
-/* {{{ proto int RedisCluster::geoadd(string key, float long float lat string mem, ...) */
+/* {{{ proto int RedisCluster::geoadd(string key, ?array options, float long float lat string mem, ...) */
 PHP_METHOD(RedisCluster, geoadd) {
-    CLUSTER_PROCESS_KW_CMD("GEOADD", redis_key_varval_cmd, cluster_long_resp, 0);
+    CLUSTER_PROCESS_CMD(geoadd, cluster_long_resp, 0);
 }
 
 /* {{{ proto array RedisCluster::geohash(string key, string mem1, [string mem2...]) */
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index e5fb7e88d7..f6ab90e8cd 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -165,7 +165,7 @@ public function flushall(string|array $key_or_address, bool $async = false): Red
 
     public function flushdb(string|array $key_or_address, bool $async = false): RedisCluster|bool;
 
-    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples): RedisCluster|int;
+    public function geoadd(string $key, ?array $options = null, float $lng, float $lat, string $member, mixed ...$other_triples): RedisCluster|int|false;
 
     public function geodist(string $key, string $src, string $dest, ?string $unit = null): RedisCluster|float|false;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index b8174f47e4..15492cb27b 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 37a25fa4537a2d10a2c2a8b09292e77e5cc35d59 */
+ * Stub hash: ae14a0f53c3ee46b132ef5db70e539c52a1388bd */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -232,8 +232,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geoadd, 0, 4, RedisCluster, MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geoadd, 0, 5, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 8a82598ad6..d1630ef064 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 37a25fa4537a2d10a2c2a8b09292e77e5cc35d59 */
+ * Stub hash: ae14a0f53c3ee46b132ef5db70e539c52a1388bd */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -204,8 +204,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 0, 4)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 0, 5)
 	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, options)
 	ZEND_ARG_INFO(0, lng)
 	ZEND_ARG_INFO(0, lat)
 	ZEND_ARG_INFO(0, member)
diff --git a/redis_commands.c b/redis_commands.c
index 2861a134af..e17fce1ffe 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3927,6 +3927,79 @@ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                 char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    zval *z_args, *z_ele;
+    smart_string cmdstr = {0};
+    zend_bool ch = 0;
+    zend_string *zstr;
+    char *mode = NULL;
+    int argc, i;
+
+    // We at least need a key and one value
+    if ((argc = ZEND_NUM_ARGS()) < 5 || argc % 3 != 2) {
+        zend_wrong_param_count();
+        return FAILURE;
+    }
+
+    // Make sure we at least have a key, and we can get other args
+    z_args = ecalloc(argc, sizeof(*z_args));
+    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
+        efree(z_args);
+        return FAILURE;
+    }
+
+    if (Z_TYPE(z_args[1]) != IS_NULL) {
+        if (Z_TYPE(z_args[1]) != IS_ARRAY) {
+            php_error_docref(NULL, E_WARNING, "Invalid options value");
+            efree(z_args);
+            return FAILURE;
+        }
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[1]), z_ele) {
+            ZVAL_DEREF(z_ele);
+            if (Z_TYPE_P(z_ele) == IS_STRING) {
+                if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "NX") ||
+                    zend_string_equals_literal_ci(Z_STR_P(z_ele), "XX"))
+                {
+                    mode = Z_STRVAL_P(z_ele);
+                } else if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "CH")) {
+                    ch = 1;
+                }
+            }
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    /* Initialize our command */
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc - 1 + (mode != NULL) + ch, "GEOADD");
+
+    /* Append key */
+    zstr = zval_get_string(&z_args[0]);
+    redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot);
+    zend_string_release(zstr);
+
+    /* Append options */
+    if (mode != NULL) {
+        redis_cmd_append_sstr(&cmdstr, mode, strlen(mode));
+    }
+    REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, ch, "CH");
+
+    /* Append members */
+    for (i = 2; i < argc; ++i) {
+        redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock);
+    }
+
+    // Cleanup arg array
+    efree(z_args);
+
+    // Push out values
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
 /* GEODIST */
 int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index 3b34a155cf..6a4ec01a87 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -286,6 +286,9 @@ int redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
     long it, char *pat, int pat_len, long count);
 
+int redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index d6f5641aa5..ef1aeb0e31 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ed7bf573247cb4bd4ead254d75ad5b60a97313be */
+ * Stub hash: 0ff87f3b3f10dacbef8455ba09b17fa4107c7999 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -219,8 +219,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geoadd, 0, 0, 4)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geoadd, 0, 0, 5)
 	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, options)
 	ZEND_ARG_INFO(0, lng)
 	ZEND_ARG_INFO(0, lat)
 	ZEND_ARG_INFO(0, member)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 336cf23ec1..c0ab9c0827 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6230,7 +6230,7 @@ protected function rawCommandArray($key, $args) {
     protected function addCities($key) {
         $this->redis->del($key);
         foreach ($this->cities as $city => $longlat) {
-            $this->redis->geoadd($key, $longlat[0], $longlat[1], $city);
+            $this->redis->geoadd($key, null, $longlat[0], $longlat[1], $city);
         }
     }
 
@@ -6244,11 +6244,11 @@ public function testGeoAdd() {
 
         /* Add them one at a time */
         foreach ($this->cities as $city => $longlat) {
-            $this->assertEquals($this->redis->geoadd('geokey', $longlat[0], $longlat[1], $city), 1);
+            $this->assertEquals($this->redis->geoadd('geokey', null, $longlat[0], $longlat[1], $city), 1);
         }
 
         /* Add them again, all at once */
-        $args = ['geokey'];
+        $args = ['geokey', null];
         foreach ($this->cities as $city => $longlat) {
             $args = array_merge($args, [$longlat[0], $longlat[1], $city]);
         }

From e8f5b517484a16756dcfc83168c65fc0604c4ab0 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Sun, 30 Oct 2022 19:49:05 +0200
Subject: [PATCH 1679/1986] Move `options` to the end list

---
 redis.stub.php                 |  2 +-
 redis_arginfo.h                |  7 +++----
 redis_cluster.c                |  2 +-
 redis_cluster.stub.php         |  2 +-
 redis_cluster_arginfo.h        |  7 +++----
 redis_cluster_legacy_arginfo.h |  7 +++----
 redis_commands.c               | 15 ++++++++-------
 redis_legacy_arginfo.h         |  7 +++----
 tests/RedisTest.php            |  6 +++---
 9 files changed, 26 insertions(+), 29 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index fddcec9bd6..c0ff8e260e 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -559,7 +559,7 @@ public function flushAll(?bool $sync = null): Redis|bool;
      */
     public function flushDB(?bool $sync = null): Redis|bool;
 
-    public function geoadd(string $key, ?array $options = null, float $lng, float $lat, string $member, mixed ...$other_triples): Redis|int|false;
+    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options): Redis|int|false;
 
     public function geodist(string $key, string $src, string $dst, ?string $unit = null): Redis|float|false;
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index e685218db6..4e6e20b997 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0ff87f3b3f10dacbef8455ba09b17fa4107c7999 */
+ * Stub hash: dbcafdb797bd3a4a656d0e1708715bba10ed7f94 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -237,13 +237,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geoadd, 0, 5, Redis, MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geoadd, 0, 4, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples_and_options, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geodist, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE)
diff --git a/redis_cluster.c b/redis_cluster.c
index c3fdef3ccd..e611315d5d 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -2759,7 +2759,7 @@ PHP_METHOD(RedisCluster, slowlog) {
 }
 /* }}} */
 
-/* {{{ proto int RedisCluster::geoadd(string key, ?array options, float long float lat string mem, ...) */
+/* {{{ proto int RedisCluster::geoadd(string key, float long float lat string mem, ...) */
 PHP_METHOD(RedisCluster, geoadd) {
     CLUSTER_PROCESS_CMD(geoadd, cluster_long_resp, 0);
 }
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index f6ab90e8cd..e968185da4 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -165,7 +165,7 @@ public function flushall(string|array $key_or_address, bool $async = false): Red
 
     public function flushdb(string|array $key_or_address, bool $async = false): RedisCluster|bool;
 
-    public function geoadd(string $key, ?array $options = null, float $lng, float $lat, string $member, mixed ...$other_triples): RedisCluster|int|false;
+    public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options): RedisCluster|int|false;
 
     public function geodist(string $key, string $src, string $dest, ?string $unit = null): RedisCluster|float|false;
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 15492cb27b..7fea6bb52a 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ae14a0f53c3ee46b132ef5db70e539c52a1388bd */
+ * Stub hash: cb1fe939ac54b2c0e5de0c354fc4a6118336de61 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -232,13 +232,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geoadd, 0, 5, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geoadd, 0, 4, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
 	ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, lat, IS_DOUBLE, 0)
 	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_triples_and_options, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geodist, 0, 3, RedisCluster, MAY_BE_DOUBLE|MAY_BE_FALSE)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index d1630ef064..5ee913e81e 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ae14a0f53c3ee46b132ef5db70e539c52a1388bd */
+ * Stub hash: cb1fe939ac54b2c0e5de0c354fc4a6118336de61 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
@@ -204,13 +204,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_RedisCluster_flushdb arginfo_class_RedisCluster_flushall
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 0, 5)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geoadd, 0, 0, 4)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, options)
 	ZEND_ARG_INFO(0, lng)
 	ZEND_ARG_INFO(0, lat)
 	ZEND_ARG_INFO(0, member)
-	ZEND_ARG_VARIADIC_INFO(0, other_triples)
+	ZEND_ARG_VARIADIC_INFO(0, other_triples_and_options)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geodist, 0, 0, 3)
diff --git a/redis_commands.c b/redis_commands.c
index e17fce1ffe..7a9812fc26 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3938,8 +3938,8 @@ redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *mode = NULL;
     int argc, i;
 
-    // We at least need a key and one value
-    if ((argc = ZEND_NUM_ARGS()) < 5 || argc % 3 != 2) {
+    // We at least need a key and three values
+    if ((argc = ZEND_NUM_ARGS()) < 4 || (argc % 3 != 1 && argc % 3 != 2)) {
         zend_wrong_param_count();
         return FAILURE;
     }
@@ -3951,13 +3951,14 @@ redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return FAILURE;
     }
 
-    if (Z_TYPE(z_args[1]) != IS_NULL) {
-        if (Z_TYPE(z_args[1]) != IS_ARRAY) {
+    if (argc % 3 == 2) {
+        argc--;
+        if (Z_TYPE(z_args[argc]) != IS_ARRAY) {
             php_error_docref(NULL, E_WARNING, "Invalid options value");
             efree(z_args);
             return FAILURE;
         }
-        ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[1]), z_ele) {
+        ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[argc]), z_ele) {
             ZVAL_DEREF(z_ele);
             if (Z_TYPE_P(z_ele) == IS_STRING) {
                 if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "NX") ||
@@ -3972,7 +3973,7 @@ redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 
     /* Initialize our command */
-    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc - 1 + (mode != NULL) + ch, "GEOADD");
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc + (mode != NULL) + ch, "GEOADD");
 
     /* Append key */
     zstr = zval_get_string(&z_args[0]);
@@ -3986,7 +3987,7 @@ redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, ch, "CH");
 
     /* Append members */
-    for (i = 2; i < argc; ++i) {
+    for (i = 1; i < argc; ++i) {
         redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock);
     }
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index ef1aeb0e31..84388245b1 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0ff87f3b3f10dacbef8455ba09b17fa4107c7999 */
+ * Stub hash: dbcafdb797bd3a4a656d0e1708715bba10ed7f94 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -219,13 +219,12 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geoadd, 0, 0, 5)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geoadd, 0, 0, 4)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, options)
 	ZEND_ARG_INFO(0, lng)
 	ZEND_ARG_INFO(0, lat)
 	ZEND_ARG_INFO(0, member)
-	ZEND_ARG_VARIADIC_INFO(0, other_triples)
+	ZEND_ARG_VARIADIC_INFO(0, other_triples_and_options)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geodist, 0, 0, 3)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index c0ab9c0827..336cf23ec1 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6230,7 +6230,7 @@ protected function rawCommandArray($key, $args) {
     protected function addCities($key) {
         $this->redis->del($key);
         foreach ($this->cities as $city => $longlat) {
-            $this->redis->geoadd($key, null, $longlat[0], $longlat[1], $city);
+            $this->redis->geoadd($key, $longlat[0], $longlat[1], $city);
         }
     }
 
@@ -6244,11 +6244,11 @@ public function testGeoAdd() {
 
         /* Add them one at a time */
         foreach ($this->cities as $city => $longlat) {
-            $this->assertEquals($this->redis->geoadd('geokey', null, $longlat[0], $longlat[1], $city), 1);
+            $this->assertEquals($this->redis->geoadd('geokey', $longlat[0], $longlat[1], $city), 1);
         }
 
         /* Add them again, all at once */
-        $args = ['geokey', null];
+        $args = ['geokey'];
         foreach ($this->cities as $city => $longlat) {
             $args = array_merge($args, [$longlat[0], $longlat[1], $city]);
         }

From df50b2ad2d443c19b90a5000f6e0a976b07382b3 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 30 Oct 2022 15:03:07 -0700
Subject: [PATCH 1680/1986] Documentation:  More complete command docblocks

APPEND
clearLastError
DBSIZE
DECR[BY]
DEL

[skip ci]
---
 redis.stub.php                 | 140 ++++++++++++++++++++++++++++++++-
 redis_arginfo.h                |   4 +-
 redis_cluster.stub.php         |  18 +++++
 redis_cluster_arginfo.h        |   2 +-
 redis_cluster_legacy_arginfo.h |   2 +-
 redis_legacy_arginfo.h         |   2 +-
 6 files changed, 162 insertions(+), 6 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index c0ff8e260e..2593f36761 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -150,6 +150,26 @@ public function _unpack(string $value): mixed;
 
     public function acl(string $subcmd, string ...$args): mixed;
 
+    /**
+     * Append data to a Redis STRING key.
+     *
+     * @param string $key   The key in question
+     * @param mixed $value  The data to append to the key.
+     *
+     * @return Redis|int|false The new string length of the key or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('foo', 'hello);
+     * var_dump($redis->append('foo', 'world'));
+     *
+     * // --- OUTPUT ---
+     * // int(10)
+     * ?>
+     * 
+     */
     public function append(string $key, mixed $value): Redis|int|false;
 
     /**
@@ -391,6 +411,22 @@ public function lmpop(array $keys, string $from, int $count = 1): Redis|array|nu
      *
      * @return bool This should always return true or throw an exception if we're not connected.
      *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('string', 'this_is_a_string');
+     * $redis->smembers('string');
+     *
+     * var_dump($redis->getLastError());
+     * $redis->clearLastError();
+     * var_dump($redis->getLastError());
+     *
+     * // --- OUTPUT ---
+     * // string(65) "WRONGTYPE Operation against a key holding the wrong kind of value"
+     * // NULL
+     * ?>
+     * 
      */
     public function clearLastError(): bool;
 
@@ -428,14 +464,116 @@ public function connect(string $host, int $port = 6379, float $timeout = 0, stri
 
     public function copy(string $src, string $dst, array $options = null): Redis|bool;
 
-    public function dbSize(): Redis|int;
+    /**
+     * Return the number of keys in the currently selected Redis database.
+     *
+     * @see https://redis.io/commands/dbsize
+     *
+     * @return Redis|int The number of keys or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->flushdb();
+     *
+     * $redis->set('foo', 'bar');
+     * var_dump($redis->dbsize());
+     *
+     * $redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']);
+     * var_dump($redis->dbsize());
+     *
+     * // --- OUTPUT
+     * // int(1)
+     * // int(5)
+     * ?>
+     */
+    public function dbSize(): Redis|int|false;
 
     public function debug(string $key): Redis|string;
 
+    /**
+     * Decrement a Redis integer by 1 or a provided value.
+     *
+     * @param string $key The key to decrement
+     * @param int    $by  How much to decrement the key.  Note that if this value is
+     *                    not sent or is set to `1`, PhpRedis will actually invoke
+     *                    the 'DECR' command.  If it is any value other than `1`
+     *                    PhpRedis will actually send the `DECRBY` command.
+     *
+     * @return Redis|int|false The new value of the key or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('counter', 3);
+     *
+     * var_dump($redis->decr('counter'));
+     * var_dump($redis->decr('counter', 2));
+     *
+     * // --- OUTPUT ---
+     * // int(2)
+     * // int(0)
+     * ?>
+     * 
+     */
     public function decr(string $key, int $by = 1): Redis|int|false;
 
+    /**
+     * Decrement a redis integer by a value
+     *
+     * @param string $key   The integer key to decrement.
+     * @param int    $value How much to decrement the key.
+     *
+     * @return Redis|int|false The new value of the key or false on failure.
+     *
+     * 
+     *  'localhost');
+     *
+     * $redis->set('counter', 3);
+     * var_dump($redis->decrby('counter', 1));
+     * var_dump($redis->decrby('counter', 2));
+     *
+     * // --- OUTPUT ---
+     * // int(2)
+     * // int(0)
+     * ?>
+     * 
+     */
     public function decrBy(string $key, int $value): Redis|int|false;
 
+    /**
+     * Delete one or more keys from Redis.
+     *
+     * @see https://redis.io/commands/del
+     *
+     * @param array|string $key_or_keys Either an array with one or more key names or a string with
+     *                                  the name of a key.
+     * @param string       $other_keys  One or more additional keys passed in a variadic fashion.
+     *
+     * This method can be called in two distinct ways.  The first is to pass a single array
+     * of keys to delete, and the second is to pass N arguments, all names of keys.  See
+     * below for an example of both strategies.
+     *
+     * 
+     *  'localhost']);
+     *
+     * for ($i = 0; $i < 5; $i++) {
+     *     $redis->set("key:$i", "val:$i");
+     * }
+     *
+     * var_dump($redis->del('key:0', 'key:1'));
+     * var_dump($redis->del(['key:2', 'key:3', 'key:4']));
+     *
+     * // --- OUTPUT ---
+     * // int(2)
+     * // int(3)
+     * ?>
+     * 
+     */
     public function del(array|string $key, string ...$other_keys): Redis|int|false;
 
     /**
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 4e6e20b997..1d09f27ee4 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: dbcafdb797bd3a4a656d0e1708715bba10ed7f94 */
+ * Stub hash: d9cbe3fdc3c4cd1b079427a54ff2b24ac1c21adc */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -147,7 +147,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_copy, 0, 2, Redi
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dbSize, 0, 0, Redis, MAY_BE_LONG)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dbSize, 0, 0, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_debug, 0, 1, Redis, MAY_BE_STRING)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index e968185da4..65cdf57df7 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -51,6 +51,9 @@ public function _redir(): string|null;
 
     public function acl(string|array $key_or_address, string $subcmd, string ...$args): mixed;
 
+    /**
+     * @see Redis::append()
+     */
     public function append(string $key, mixed $value): RedisCluster|bool|int;
 
     public function bgrewriteaof(string|array $key_or_address): RedisCluster|bool;
@@ -108,6 +111,9 @@ public function blmpop(float $timeout, array $keys, string $from, int $count = 1
      */
     public function lmpop(array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
+    /**
+     * @see Redis::clearlasterror()
+     */
     public function clearlasterror(): bool;
 
     public function client(string|array $key_or_address, string $subcommand, ?string $arg = NULL): array|string|bool;
@@ -120,14 +126,26 @@ public function command(mixed ...$extra_args): mixed;
 
     public function config(string|array $key_or_address, string $subcommand, mixed ...$extra_args): mixed;
 
+    /**
+     * @see Redis::dbsize()
+     */
     public function dbsize(string|array $key_or_address): RedisCluster|int;
 
+    /**
+     * @see Redis::decr()
+     */
     public function decr(string $key, int $by = 1): RedisCluster|int|false;
 
+    /**
+     * @see Redis::decrby()
+     */
     public function decrby(string $key, int $value): RedisCluster|int|false;
 
     public function decrbyfloat(string $key, float $value): float;
 
+    /**
+     * @see Redis::del()
+     */
     public function del(array|string $key, string ...$other_keys): RedisCluster|int|false;
 
     public function discard(): bool;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 7fea6bb52a..16096996ee 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: cb1fe939ac54b2c0e5de0c354fc4a6118336de61 */
+ * Stub hash: fb0623f92a600a7b3cba70d9f3ba82164914f267 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 5ee913e81e..9122cd6fd7 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: cb1fe939ac54b2c0e5de0c354fc4a6118336de61 */
+ * Stub hash: fb0623f92a600a7b3cba70d9f3ba82164914f267 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 84388245b1..c8c5d4bfbd 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: dbcafdb797bd3a4a656d0e1708715bba10ed7f94 */
+ * Stub hash: d9cbe3fdc3c4cd1b079427a54ff2b24ac1c21adc */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From fb3fb6f83f1e2b41a1f3054be1a2128b2d2b43b8 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 30 Oct 2022 15:38:22 -0700
Subject: [PATCH 1681/1986] Copy docblock and fix DB option.

---
 redis.stub.php         | 50 ++++++++++++++++++++++++++++++++++++++++++
 redis_arginfo.h        |  2 +-
 redis_commands.c       |  2 +-
 redis_legacy_arginfo.h |  2 +-
 4 files changed, 53 insertions(+), 3 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 2593f36761..bfad44f7b8 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -462,6 +462,56 @@ public function config(string $operation, array|string|null $key_or_settings = N
 
     public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool;
 
+    /**
+     * Make a copy of a redis key.
+     *
+     * @see https://redis.io/commands/copy
+     *
+     * @param string $src     The key to copy
+     * @param string $dst     The name of the new key created from the source key.
+     * @param array  $options An array with modifiers on how COPY should operate.
+     *
+     * Available Options:
+     *
+     * $options = [
+     *     'REPLACE' => true|false // Whether Redis should replace an existing key.
+     *     'DB' => int             // Copy the key to a specific DB.
+     * ];
+     *
+     * @return Redis|bool True if the copy was completed and false if not.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->pipeline()
+     *       ->select(1)
+     *       ->del('newkey')
+     *       ->select(0)
+     *       ->del('newkey')
+     *       ->mset(['source1' => 'value1', 'exists' => 'old_value'])
+     *       ->exec();
+     *
+     * // Will succeed, as 'newkey' doesn't exist
+     * var_dump($redis->copy('source1', 'newkey'));
+     *
+     * // Will succeed, because 'newkey' doesn't exist in DB 1
+     * var_dump($redis->copy('source1', 'newkey', ['db' => 1]));
+     *
+     * // Will fail, because 'exists' does exist
+     * var_dump($redis->copy('source1', 'exists'));
+     *
+     * // Will succeed, because even though 'exists' is a key, we sent the REPLACE option.
+     * var_dump($redis->copy('source1', 'exists', ['REPLACE' => true]));
+     *
+     * // --- OUTPUT ---
+     * // bool(true)
+     * // bool(true)
+     * // bool(false)
+     * // bool(true)
+     * ?>
+     * 
+     */
     public function copy(string $src, string $dst, array $options = null): Redis|bool;
 
     /**
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 1d09f27ee4..d7d9e5d848 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d9cbe3fdc3c4cd1b079427a54ff2b24ac1c21adc */
+ * Stub hash: 1e5a8c3e8d885354e26185e651b0f7ee0dbf8374 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_commands.c b/redis_commands.c
index 7a9812fc26..b17c7d59f2 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5178,7 +5178,7 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         } ZEND_HASH_FOREACH_END();
     }
 
-    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (db > -1) + replace, "COPY");
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (db > -1 ? 2 : 0) + replace, "COPY");
     redis_cmd_append_sstr(&cmdstr, src, src_len);
     redis_cmd_append_sstr(&cmdstr, dst, dst_len);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index c8c5d4bfbd..0865c1bb67 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d9cbe3fdc3c4cd1b079427a54ff2b24ac1c21adc */
+ * Stub hash: 1e5a8c3e8d885354e26185e651b0f7ee0dbf8374 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From cc2383f07666e6afefd7b58995fb607d9967d650 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Sun, 30 Oct 2022 20:11:15 -0700
Subject: [PATCH 1682/1986] Documentation:  Additional docblock headers

[skip ci]
---
 redis.stub.php                 | 215 +++++++++++++++++++++++++++------
 redis_arginfo.h                |   2 +-
 redis_cluster.stub.php         |  15 +++
 redis_cluster_arginfo.h        |   2 +-
 redis_cluster_legacy_arginfo.h |   2 +-
 redis_legacy_arginfo.h         |   2 +-
 6 files changed, 194 insertions(+), 44 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index bfad44f7b8..7f9d0299da 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -437,27 +437,29 @@ public function close(): bool;
     public function command(string $opt = null, string|array $arg): mixed;
 
     /**
-      Execute the Redis CONFIG command in a variety of ways.  What the command does in particular depends
-      on the `$operation` qualifier.
-
-      Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.
-
-      @param string            $operation      The CONFIG subcommand to execute
-      @param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or
-                                               an array of settings or settings and values.
-                                               Note:  Redis 7.0.0 is required to send an array of settings.
-      @param ?string           $value          The setting value when the operation is SET.
-
-      
-      config('GET', 'timeout');
-      $redis->config('GET', ['timeout', 'databases']);
-
-      $redis->config('SET', 'timeout', 30);
-      $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);
-      ?>
-      
-     */
+     *  Execute the Redis CONFIG command in a variety of ways.  What the command does in particular depends
+     *  on the `$operation` qualifier.
+     *
+     *  Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.
+     *
+     *  @see https://redis.io/commands/config
+     *
+     *  @param string            $operation      The CONFIG subcommand to execute
+     *  @param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or
+     *                                           an array of settings or settings and values.
+     *                                           Note:  Redis 7.0.0 is required to send an array of settings.
+     *  @param string            $value          The setting value when the operation is SET.
+     *
+     *  
+     *  config('GET', 'timeout');
+     *  $redis->config('GET', ['timeout', 'databases']);
+     *
+     *  $redis->config('SET', 'timeout', 30);
+     *  $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);
+     *  ?>
+     *  
+     * */
     public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed;
 
     public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool;
@@ -545,6 +547,9 @@ public function debug(string $key): Redis|string;
     /**
      * Decrement a Redis integer by 1 or a provided value.
      *
+     * @see https://redis.io/commands/decr
+     * @see https://redis.io/commands/decrby
+     *
      * @param string $key The key to decrement
      * @param int    $by  How much to decrement the key.  Note that if this value is
      *                    not sent or is set to `1`, PhpRedis will actually invoke
@@ -573,6 +578,8 @@ public function decr(string $key, int $by = 1): Redis|int|false;
     /**
      * Decrement a redis integer by a value
      *
+     * @see https://redis.io/commands/decrby
+     *
      * @param string $key   The integer key to decrement.
      * @param int    $value How much to decrement the key.
      *
@@ -636,6 +643,27 @@ public function discard(): Redis|bool;
 
     public function dump(string $key): Redis|string;
 
+    /**
+     * Have Redis repeat back an arbitrary string to the client.
+     *
+     * @see https://redis.io/commands/echo
+     *
+     * @param string $str The string to echo
+     *
+     * @return Redis|string|false The string sent to Redis or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * var_dump($redis->echo('Hello, World'));
+     *
+     * // --- OUTPUT ---
+     * // string(12) "Hello, World"
+     *
+     * ?>
+     * 
+     */
     public function echo(string $str): Redis|string|false;
 
     /**
@@ -686,41 +714,148 @@ public function evalsha(string $sha1, array $args = [], int $num_keys = 0): mixe
      */
     public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): mixed;
 
+    /**
+     * Execute either a MULTI or PIPELINE block and return the array of replies.
+     *
+     * @see https://redis.io/commands/exec
+     * @see https://redis.io/commands/multi
+     * @see Redis::pipeline()
+     * @see Redis::multi()
+     *
+     * @return Redis|array|false The array of pipeline'd or multi replies or false on failure.
+     *
+     * 
+     * $redis = new Redis(['host' => 'localhost']);
+     *
+     * $res = $redis->multi()
+     *              ->set('foo', 'bar')
+     *              ->get('foo')
+     *              ->del('list')
+     *              ->rpush('list', 'one', 'two', 'three')
+     *              ->exec();
+     *
+     * var_dump($res);
+     *
+     * // --- OUTPUT ---
+     * // array(4) {
+     * //   [0]=>
+     * //   bool(true)           // set('foo', 'bar')
+     * //   [1]=>
+     * //   string(3) "bar"      // get('foo')
+     * //   [2]=>
+     * //   int(1)               // del('list')
+     * //   [3]=>
+     * //   int(3)               // rpush('list', 'one', 'two', 'three')
+     * // }
+     * ?>
+     * 
+     */
     public function exec(): Redis|array|false;
 
+    /**
+     * Test if one or more keys exist.
+     *
+     * @see https://redis.io/commands/exists
+     *
+     * @param mixed $key         Either an array of keys or a string key
+     * @param mixed $other_keys  If the previous argument was a string, you may send any number of
+     *                           additional keys to test.
+     *
+     * @return Redis|int|bool    The number of keys that do exist and false on failure
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->multi()
+     *       ->mset(['k1' => 'v1', 'k2' => 'v2', 'k3' => 'v3', 'k4' => 'v4'])
+     *       ->exec();
+     *
+     * // Using a single array of keys
+     * var_dump($redis->exists(['k1', 'k2', 'k3']));
+     *
+     * // Calling via variadic arguments
+     * var_dump($redis->exists('k4', 'k5', 'notakey'));
+     *
+     * // --- OUTPUT ---
+     * // int(3)
+     * // int(1)
+     * ?>
+     * 
+     */
     public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool;
 
     /**
-       Sets an expiration in seconds on the key in question.  If connected to
-       redis-server >= 7.0.0 you may send an additional "mode" argument which
-       modifies how the command will execute.
-
-       @param string  $key  The key to set an expiration on.
-       @param ?string $mode A two character modifier that changes how the
-                            command works.
-                            NX - Set expiry only if key has no expiry
-                            XX - Set expiry only if key has an expiry
-                            LT - Set expiry only when new expiry is < current expiry
-                            GT - Set expiry only when new expiry is > current expiry
+     * Sets an expiration in seconds on the key in question.  If connected to
+     * redis-server >= 7.0.0 you may send an additional "mode" argument which
+     * modifies how the command will execute.
+     *
+     * @see https://redis.io/commands/expire
+     *
+     * @param string  $key  The key to set an expiration on.
+     * @param string  $mode A two character modifier that changes how the
+     *                      command works.
+     *                      NX - Set expiry only if key has no expiry
+     *                      XX - Set expiry only if key has an expiry
+     *                      LT - Set expiry only when new expiry is < current expiry
+     *                      GT - Set expiry only when new expiry is > current expiry
      */
     public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|bool;
 
     /**
-      Set a key's expiration to a specific Unix timestamp in seconds.  If
-      connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.
-
-      @see Redis::expire() For a description of the mode argument.
-
-       @param string  $key  The key to set an expiration on.
-       @param ?string $mode A two character modifier that changes how the
-                            command works.
+     * Set a key's expiration to a specific Unix timestamp in seconds.  If
+     * connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.
+     *
+     * @see Redis::expire() For a description of the mode argument.
+     *
+     *  @param string  $key  The key to set an expiration on.
+     *  @param string  $mode A two character modifier that changes how the
+     *                       command works.
      */
     public function expireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool;
 
     public function failover(?array $to = null, bool $abort = false, int $timeout = 0): Redis|bool;
 
+    /**
+     * Get the expiration of a given key as a unix timestamp
+     *
+     * @see https://redis.io/commands/expiretime
+     *
+     * @param string $key      The key to check.
+     *
+     * @return Redis|int|false The timestamp when the key expires, or -1 if the key has no expiry
+     *                         and -2 if the key doesn't exist.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('expiry-key', 'this will last a very long time');
+     *
+     * // Expire this key at 2222/02/22 02:22:22 GMT
+     * $redis->expireAt('expiry-key', 7955144542);
+     *
+     * var_dump($redis->expiretime('expiry-key'));
+     *
+     * // --- OUTPUT ---
+     * // int(7955144542)
+     *
+     * ?>php
+     * 
+     */
     public function expiretime(string $key): Redis|int|false;
 
+    /**
+     * Get the expriation timestamp of a given Redis key but in milliseconds.
+     *
+     * @see https://redis.io/commands/pexpiretime
+     * @see Redis::expiretime()
+     *
+     * @param string $key      The key to check
+     *
+     * @return Redis|int|false The expiration timestamp of this key (in milliseconds) or -1 if the
+     *                         key has no expiration, and -2 if it does not exist.
+     */
     public function pexpiretime(string $key): Redis|int|false;
 
     /**
diff --git a/redis_arginfo.h b/redis_arginfo.h
index d7d9e5d848..55fd6856ce 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 1e5a8c3e8d885354e26185e651b0f7ee0dbf8374 */
+ * Stub hash: d59da775ee203c4ec2f7c5a558e07a561a8a501a */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 65cdf57df7..c7d08fee3a 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -124,6 +124,9 @@ public function cluster(string|array $key_or_address, string $command, mixed ...
 
     public function command(mixed ...$extra_args): mixed;
 
+    /**
+     * @see Redis::config()
+     */
     public function config(string|array $key_or_address, string $subcommand, mixed ...$extra_args): mixed;
 
     /**
@@ -152,6 +155,9 @@ public function discard(): bool;
 
     public function dump(string $key): RedisCluster|string|false;
 
+    /**
+     * @see Redis::echo()
+     */
     public function echo(string|array $key_or_address, string $msg): RedisCluster|string|false;
 
     public function eval(string $script, array $args = [], int $num_keys = 0): mixed;
@@ -162,6 +168,9 @@ public function evalsha(string $script_sha, array $args = [], int $num_keys = 0)
 
     public function evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0): mixed;
 
+    /**
+     * @see Redis::exec()
+     */
     public function exec(): array|false;
 
     public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
@@ -175,8 +184,14 @@ public function expire(string $key, int $timeout, ?string $mode = NULL): RedisCl
 
     public function expireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
 
+    /**
+     * @see Redis::expiretime()
+     */
     public function expiretime(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::pexpiretime()
+     */
     public function pexpiretime(string $key): RedisCluster|int|false;
 
     public function flushall(string|array $key_or_address, bool $async = false): RedisCluster|bool;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 16096996ee..aaec34eb0a 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: fb0623f92a600a7b3cba70d9f3ba82164914f267 */
+ * Stub hash: 0a5a8d4a59c4d7929402293be13553ffcaee7c7e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index 9122cd6fd7..e59b904bee 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: fb0623f92a600a7b3cba70d9f3ba82164914f267 */
+ * Stub hash: 0a5a8d4a59c4d7929402293be13553ffcaee7c7e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 0865c1bb67..6d114e2051 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 1e5a8c3e8d885354e26185e651b0f7ee0dbf8374 */
+ * Stub hash: d59da775ee203c4ec2f7c5a558e07a561a8a501a */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 2a0d1c1e6d10d53c3de510cef4310be912bec4c0 Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Mon, 31 Oct 2022 15:39:56 +0200
Subject: [PATCH 1683/1986] Refactor PubSub command

---
 library.c           |  22 +++++++++
 library.h           |   2 +
 redis.c             | 111 +-------------------------------------------
 redis_commands.c    |  76 +++++++++++++++++++++++++++++-
 redis_commands.h    |   3 ++
 tests/RedisTest.php |   4 +-
 6 files changed, 105 insertions(+), 113 deletions(-)

diff --git a/library.c b/library.c
index 9144a8fd0d..3772b73dd9 100644
--- a/library.c
+++ b/library.c
@@ -455,6 +455,22 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     }
 }
 
+PHP_REDIS_API int
+redis_pubsub_response(INTERNAL_FUNCTION_PARAMETERS,
+                      RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    if (ctx == NULL) {
+        return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        return redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 1) {
+        return redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
+        return FAILURE;
+    }
+}
+
 static void
 ht_free_subs(zval *data)
 {
@@ -1354,6 +1370,7 @@ redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
     } else {
         ZEND_ASSERT(!"memory corruption?");
+        return FAILURE;
     }
 }
 
@@ -1366,6 +1383,7 @@ redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *
         return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
     } else {
         ZEND_ASSERT(!"memory corruption?");
+        return FAILURE;
     }
 }
 
@@ -1378,6 +1396,7 @@ redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_
         return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
     } else {
         ZEND_ASSERT(!"memory corruption?");
+        return FAILURE;
     }
 }
 
@@ -1392,6 +1411,7 @@ redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, z
         return redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
     } else {
         ZEND_ASSERT(!"memory corruption?");
+        return FAILURE;
     }
 }
 
@@ -1404,6 +1424,7 @@ redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_
         return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
     } else {
         ZEND_ASSERT(!"memory corruption?");
+        return FAILURE;
     }
 }
 
@@ -1447,6 +1468,7 @@ redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z
         }
     } else {
         ZEND_ASSERT(!"memory corruption?");
+        return FAILURE;
     }
 
     if (IS_ATOMIC(redis_sock)) {
diff --git a/library.h b/library.h
index 268c838bff..05b34ed4e9 100644
--- a/library.h
+++ b/library.h
@@ -109,6 +109,8 @@ PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS,
 PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock, zval *z_tab, void *ctx);
 
+PHP_REDIS_API int redis_pubsub_response(INTERNAL_FUNCTION_PARAMETERS,
+    RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
     RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
diff --git a/redis.c b/redis.c
index 6d3b6292c7..b305075f09 100644
--- a/redis.c
+++ b/redis.c
@@ -2707,122 +2707,13 @@ PHP_METHOD(Redis, wait) {
     REDIS_PROCESS_RESPONSE(redis_long_response);
 }
 
-/* Construct a PUBSUB command */
-PHP_REDIS_API int
-redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
-                       zval *arg)
-{
-    HashTable *ht_chan;
-    zval *z_ele;
-    smart_string cmd = {0};
-
-    if (type == PUBSUB_CHANNELS) {
-        if (arg) {
-            /* With a pattern */
-            return REDIS_SPPRINTF(ret, "PUBSUB", "sk", "CHANNELS", sizeof("CHANNELS") - 1,
-                                  Z_STRVAL_P(arg), Z_STRLEN_P(arg));
-        } else {
-            /* No pattern */
-            return REDIS_SPPRINTF(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS") - 1);
-        }
-    } else if (type == PUBSUB_NUMSUB) {
-        ht_chan = Z_ARRVAL_P(arg);
-
-        // Add PUBSUB and NUMSUB bits
-        redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
-        redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
-
-        /* Iterate our elements */
-        ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) {
-            zend_string *zstr = zval_get_string(z_ele);
-            redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, NULL);
-            zend_string_release(zstr);
-        } ZEND_HASH_FOREACH_END();
-
-        /* Set return */
-        *ret = cmd.c;
-        return cmd.len;
-    } else if (type == PUBSUB_NUMPAT) {
-        return REDIS_SPPRINTF(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT") - 1);
-    }
-
-    /* Shouldn't ever happen */
-    return -1;
-}
-
 /*
  * {{{ proto Redis::pubsub("channels", pattern);
  *     proto Redis::pubsub("numsub", Array channels);
  *     proto Redis::pubsub("numpat"); }}}
  */
 PHP_METHOD(Redis, pubsub) {
-    zval *object;
-    RedisSock *redis_sock;
-    char *keyword, *cmd;
-    int cmd_len;
-    size_t kw_len;
-    PUBSUB_TYPE type;
-    zval *arg = NULL;
-
-    // Parse arguments
-    if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                    "Os|z", &object, redis_ce, &keyword,
-                                    &kw_len, &arg)==FAILURE)
-    {
-        RETURN_FALSE;
-    }
-
-    /* Validate our sub command keyword, and that we've got proper arguments */
-    if(!strncasecmp(keyword, "channels", sizeof("channels"))) {
-        /* One (optional) string argument */
-        if(arg && Z_TYPE_P(arg) != IS_STRING) {
-            RETURN_FALSE;
-        }
-        type = PUBSUB_CHANNELS;
-    } else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
-        /* One array argument */
-        if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
-           zend_hash_num_elements(Z_ARRVAL_P(arg)) == 0)
-        {
-            RETURN_FALSE;
-        }
-        type = PUBSUB_NUMSUB;
-    } else if(!strncasecmp(keyword, "numpat", sizeof("numpat"))) {
-        type = PUBSUB_NUMPAT;
-    } else {
-        /* Invalid keyword */
-        RETURN_FALSE;
-    }
-
-    /* Grab our socket context object */
-    if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
-        RETURN_FALSE;
-    }
-
-    /* Construct our "PUBSUB" command */
-    cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg);
-
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
-
-    if(type == PUBSUB_NUMSUB) {
-        if (IS_ATOMIC(redis_sock)) {
-            if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                                                 redis_sock, NULL, NULL) < 0)
-            {
-                RETURN_FALSE;
-            }
-        }
-        REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_int);
-    } else {
-        if (IS_ATOMIC(redis_sock)) {
-            if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                                        redis_sock, NULL, NULL) < 0)
-            {
-                RETURN_FALSE;
-            }
-        }
-        REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
-    }
+    REDIS_PROCESS_CMD(pubsub, redis_pubsub_response);
 }
 
 /* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */
diff --git a/redis_commands.c b/redis_commands.c
index b17c7d59f2..dabeae3f79 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1373,7 +1373,81 @@ redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-/* SUBSCRIBE/PSUBSCRIBE */
+int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                     char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    HashTable *channels = NULL;
+    smart_string cmdstr = {0};
+    zend_string *op, *pattern = NULL;
+    zval *arg = NULL, *z_chan;
+
+    ZEND_PARSE_PARAMETERS_START(1, 2)
+        Z_PARAM_STR(op)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_ZVAL(arg)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    if (zend_string_equals_literal_ci(op, "NUMPAT")) {
+        *ctx = NULL;
+    } else if (zend_string_equals_literal_ci(op, "CHANNELS") ||
+        zend_string_equals_literal_ci(op, "SHARDCHANNELS")
+    ) {
+        if (arg != NULL) {
+            if (Z_TYPE_P(arg) != IS_STRING) {
+                php_error_docref(NULL, E_WARNING, "Invalid patern value");
+                return FAILURE;
+            }
+            pattern = zval_get_string(arg);
+        }
+        *ctx = PHPREDIS_CTX_PTR;
+    } else if (zend_string_equals_literal_ci(op, "NUMSUB") ||
+        zend_string_equals_literal_ci(op, "SHARDNUMSUB")
+    ) {
+        if (arg != NULL) {
+            if (Z_TYPE_P(arg) != IS_ARRAY) {
+                php_error_docref(NULL, E_WARNING, "Invalid channels value");
+                return FAILURE;
+            }
+            channels = Z_ARRVAL_P(arg);
+        }
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else {
+        php_error_docref(NULL, E_WARNING, "Unknown PUBSUB operation '%s'", ZSTR_VAL(op));
+        return FAILURE;
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + !!pattern + (channels ? zend_hash_num_elements(channels) : 0), "PUBSUB");
+    redis_cmd_append_sstr_zstr(&cmdstr, op);
+
+    if (pattern != NULL) {
+        redis_cmd_append_sstr_zstr(&cmdstr, pattern);
+        zend_string_release(pattern);
+    } else if (channels != NULL) {
+        ZEND_HASH_FOREACH_VAL(channels, z_chan) {
+            // We want to deal with strings here
+            zend_string *zstr = zval_get_string(z_chan);
+
+            // Grab channel name, prefix if required
+            char *key = ZSTR_VAL(zstr);
+            size_t key_len = ZSTR_LEN(zstr);
+            int key_free = redis_key_prefix(redis_sock, &key, &key_len);
+
+            // Add this channel
+            redis_cmd_append_sstr(&cmdstr, key, key_len);
+
+            zend_string_release(zstr);
+            // Free our key if it was prefixed
+            if (key_free) efree(key);
+        } ZEND_HASH_FOREACH_END();
+    }
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+}
+
+/* SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE */
 int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                         char *kw, char **cmd, int *cmd_len, short *slot,
                         void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index 6a4ec01a87..47176c4868 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -139,6 +139,9 @@ int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw
 int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx);
 
+int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 336cf23ec1..a88d800958 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -202,8 +202,8 @@ public function testPubSub() {
         $this->assertTrue(is_int($result));
 
         // Invalid calls
-        $this->assertFalse($this->redis->pubsub("notacommand"));
-        $this->assertFalse($this->redis->pubsub("numsub", "not-an-array"));
+        $this->assertFalse(@$this->redis->pubsub("notacommand"));
+        $this->assertFalse(@$this->redis->pubsub("numsub", "not-an-array"));
     }
 
     /* These test cases were generated randomly.  We're just trying to test

From 0dd2836f3c6b55a557850ad655e7f3cfc89cb9a1 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 31 Oct 2022 17:30:29 -0700
Subject: [PATCH 1684/1986] Documentation:  Add a docblock for the set command.

---
 redis.stub.php                 | 43 +++++++++++++++++++++++++++++++++-
 redis_arginfo.h                |  4 ++--
 redis_cluster.stub.php         |  3 +++
 redis_cluster_arginfo.h        |  2 +-
 redis_cluster_legacy_arginfo.h |  2 +-
 redis_legacy_arginfo.h         |  8 ++-----
 6 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 7f9d0299da..69fda01c11 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1621,7 +1621,48 @@ public function script(string $command, mixed ...$args): mixed;
      */
     public function select(int $db): Redis|bool;
 
-    public function set(string $key, mixed $value, mixed $opt = NULL): Redis|string|bool;
+    /**
+     * Create or set a Redis STRING key to a value.
+     *
+     * @see https://redis.io/commands/set
+     * @see https://redis.io/commands/setex
+     *
+     * @param string    $key     The key name to set.
+     * @param mixed     $value   The value to set the key to.
+     * @param array|int $options Either an array with options for how to perform the set or an
+     *                           integer with an expiration.  If an expiration is set PhpRedis
+     *                           will actually send the `SETEX` command.
+     *
+     * OPTION                         DESCRIPTION
+     * ------------                   --------------------------------------------------------------
+     * ['EX' => 60]                   expire 60 seconds.
+     * ['PX' => 6000]                 expire in 6000 milliseconds.
+     * ['EXAT' => time() + 10]        expire in 10 seconds.
+     * ['PXAT' => time()*1000 + 1000] expire in 1 second.
+     * ['KEEPTTL' => true]            Redis will not update the key's current TTL.
+     * ['XX']                         Only set the key if it already exists.
+     * ['NX']                         Only set the key if it doesn't exist.
+     * ['GET']                        Instead of returning `+OK` return the previous value of the
+     *                                key or NULL if the key didn't exist.
+     *
+     * @return Redis|string|bool True if the key was set or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('key', 'value');
+     *
+     * // Will actually send `SETEX 60 key value` to Redis.
+     * $redis->set('key', 'expires_in_60_seconds', 60);
+     *
+     * // Only have Redis set the key if it already exists.
+     * $redis->set('key', 'options_set', ['XX']);
+     *
+     * ?>
+     * 
+     */
+    public function set(string $key, mixed $value, mixed $options = NULL): Redis|string|bool;
 
     /** @return Redis|int|false*/
     public function setBit(string $key, int $idx, bool $value);
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 55fd6856ce..9d28c7d372 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d59da775ee203c4ec2f7c5a558e07a561a8a501a */
+ * Stub hash: 8a3b18f9b816cfb6aac50ef147008d7349496e08 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -759,7 +759,7 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_set, 0, 2, Redis, MAY_BE_STRING|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_MIXED, 0, "NULL")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index c7d08fee3a..f4ff298f8d 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -428,6 +428,9 @@ public function sdiff(string $key, string ...$other_keys): RedisCluster|array|fa
      */
     public function sdiffstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false;
 
+    /**
+     * @see https://redis.io/commands/set
+     */
     public function set(string $key, mixed $value, mixed $options = NULL): RedisCluster|string|bool;
 
     public function setbit(string $key, int $offset, bool $onoff): RedisCluster|int|false;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index aaec34eb0a..887364060d 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0a5a8d4a59c4d7929402293be13553ffcaee7c7e */
+ * Stub hash: 65c7830c07ea86720c6089dbd0fa7943df0a2ca8 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index e59b904bee..ee4cfafadf 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0a5a8d4a59c4d7929402293be13553ffcaee7c7e */
+ * Stub hash: 65c7830c07ea86720c6089dbd0fa7943df0a2ca8 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 6d114e2051..5baf9d0351 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d59da775ee203c4ec2f7c5a558e07a561a8a501a */
+ * Stub hash: 8a3b18f9b816cfb6aac50ef147008d7349496e08 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -643,11 +643,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_select, 0, 0, 1)
 	ZEND_ARG_INFO(0, db)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_set, 0, 0, 2)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, value)
-	ZEND_ARG_INFO(0, opt)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_set arginfo_class_Redis_lPos
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)

From 7d5db510a0dcacac9ecf89618d1ff9f2c4ab0911 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 31 Oct 2022 20:43:20 -0700
Subject: [PATCH 1685/1986] Documentation:  SSCAN docblock and example

[skip ci]
---
 redis.stub.php         | 61 ++++++++++++++++++++++++++++++++++++++++++
 redis_arginfo.h        |  2 +-
 redis_legacy_arginfo.h |  2 +-
 3 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 69fda01c11..64faa3bc96 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1893,6 +1893,67 @@ public function sortDescAlpha(string $key, ?string $pattern = null, mixed $get =
      */
     public function srem(string $key, mixed $value, mixed ...$other_values): Redis|int|false;
 
+    /**
+     * Scan the members of a redis SET key.
+     *
+     * @see https://redis.io/commands/sscan
+     * @see https://redis.io/commands/scan
+     * @see Redis::setOption()
+     *
+     * @param string $key       The Redis SET key in question.
+     * @param int    $iterator  A reference to an iterator which should be initialized to NULL that
+     *                          PhpRedis will update with the value returned from Redis after each
+     *                          subsequent call to SSCAN.  Once this cursor is zero you know all
+     *                          members have been traversed.
+     * @param string $pattern   An optional glob style pattern to match against, so Redis only
+     *                          returns the subset of members matching this pattern.
+     * @param int    $count     A hint to Redis as to how many members it should scan in one command
+     *                          before returning members for that iteration.
+     *
+     * 
+     * $redis = new Redis(['host' => 'localhost']);
+     *
+     * $redis->del('myset');
+     * for ($i = 0; $i < 10000; $i++) {
+     *     $redis->sAdd('myset', "member:$i");
+     * }
+     * $redis->sadd('myset', 'foofoo');
+     *
+     * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+     *
+     * $scanned = 0;
+     * $it = NULL;
+     *
+     * // Without Redis::SCAN_RETRY we may receive empty results and
+     * // a nonzero iterator.
+     * do {
+     *     // Scan members containing '5'
+     *     $members = $redis->sscan('myset', $it, '*5*');
+     *     foreach ($members as $member) {
+     *          echo "NORETRY: $member\n";
+     *          $scanned++;
+     *     }
+     * } while ($it != 0);
+     * echo "TOTAL: $scanned\n";
+     *
+     * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+     *
+     * $scanned = 0;
+     * $it = NULL;
+     *
+     * // With Redis::SCAN_RETRY PhpRedis will never return an empty array
+     * // when the cursor is non-zero
+     * while (($members = $redis->sscan('myset', $it, '*5*'))) {
+     *     foreach ($members as $member) {
+     *         echo "RETRY: $member\n";
+     *         $scanned++;
+     *     }
+     * }
+     * echo "TOTAL: $scanned\n";
+     * ?>
+     * 
+     *
+     */
     public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
 
     /** @return Redis|int|false*/
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 9d28c7d372..5833affe64 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8a3b18f9b816cfb6aac50ef147008d7349496e08 */
+ * Stub hash: 5554480fcf6749dcfd69f371ad8dbd07fd8ed4c4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 5baf9d0351..5d4355cbfd 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8a3b18f9b816cfb6aac50ef147008d7349496e08 */
+ * Stub hash: 5554480fcf6749dcfd69f371ad8dbd07fd8ed4c4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From f2cef8be06ed8f308c671df35781e91b16ca3f96 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 31 Oct 2022 20:53:21 -0700
Subject: [PATCH 1686/1986] More docblocks and refactor SLAVEOF command.

Add additional complete docblocks for a few more commands.

Refactor SLAVEOF handler to conform with more modern PhpRedis command
handlers.

Create REPLICAOF and deprecate SLAVEOF as Redis has done since 5.0.0.
---
 redis.c                | 41 +++++--------------
 redis.stub.php         | 90 +++++++++++++++++++++++++++++++++++++++++-
 redis_arginfo.h        | 12 ++++--
 redis_commands.c       | 27 +++++++++++++
 redis_commands.h       |  4 ++
 redis_legacy_arginfo.h |  8 +++-
 6 files changed, 143 insertions(+), 39 deletions(-)

diff --git a/redis.c b/redis.c
index b305075f09..0afb4b4881 100644
--- a/redis.c
+++ b/redis.c
@@ -2559,41 +2559,18 @@ PHP_METHOD(Redis, bgrewriteaof)
 }
 /* }}} */
 
-/* {{{ proto string Redis::slaveof([host, port]) */
-PHP_METHOD(Redis, slaveof)
-{
-    zval *object;
-    RedisSock *redis_sock;
-    char *cmd = "", *host = NULL;
-    size_t host_len;
-    zend_long port = 6379;
-    int cmd_len;
-
-    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
-                                     "O|sl", &object, redis_ce, &host,
-                                     &host_len, &port) == FAILURE)
-    {
-        RETURN_FALSE;
-    }
-    if (port < 0 || (redis_sock = redis_sock_get(object, 0)) == NULL) {
-        RETURN_FALSE;
-    }
-
-    if (host && host_len) {
-        cmd_len = REDIS_SPPRINTF(&cmd, "SLAVEOF", "sd", host, host_len, (int)port);
-    } else {
-        cmd_len = REDIS_SPPRINTF(&cmd, "SLAVEOF", "ss", "NO", 2, "ONE", 3);
-    }
-
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
-    if (IS_ATOMIC(redis_sock)) {
-      redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
-          NULL, NULL);
-    }
-    REDIS_PROCESS_RESPONSE(redis_boolean_response);
+/* {{{ public function slaveof(string $host = NULL, int $port = NULL): Redis|bool }}} */
+PHP_METHOD(Redis, slaveof) {
+    REDIS_PROCESS_KW_CMD("SLAVEOF", redis_replicaof_cmd, redis_boolean_response);
 }
 /* }}} */
 
+/* {{{ public function replicaof(string $host = NULL, int $port = NULL): Redis|bool }}} */
+PHP_METHOD(Redis, replicaof) {
+    REDIS_PROCESS_KW_CMD("REPLICAOF", redis_replicaof_cmd, redis_boolean_response);
+}
+
+/* }}} */
 /* {{{ proto string Redis::object(key) */
 PHP_METHOD(Redis, object)
 {
diff --git a/redis.stub.php b/redis.stub.php
index 64faa3bc96..d2ed4449d7 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1751,14 +1751,102 @@ public function setRange(string $key, int $start, string $value);
     public function setOption(int $option, mixed $value): bool;
 
     /** @return bool|Redis */
+
+    /**
+     * Set a Redis STRING key with a specific expiration in seconds.
+     *
+     * @param string $key     The name of the key to set.
+     * @param int    $expire  The key's expiration in seconds.
+     * @param mixed  $value   The value to set the key.
+     *
+     * @return Redis|bool True on success or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * // Set a key with a 60 second expiration
+     * $redis->set('some_key', 60, 'some_value');
+     *
+     * ?>php
+     * 
+     */
     public function setex(string $key, int $expire, mixed $value);
 
     /** @return bool|array|Redis */
     public function setnx(string $key, mixed $value);
 
+    /**
+     * Check whether a given value is the member of a Redis SET.
+     *
+     * @param string $key   The redis set to check.
+     * @param mixed  $value The value to test.
+     *
+     * @return Redis|bool True if the member exists and false if not.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->multi()
+     *       ->del('myset')
+     *       ->sadd('myset', 'foo', 'bar', 'baz')
+     *       ->exec();
+     *
+     * // Will return true, as 'foo' is in the set
+     * $redis->sismember('myset', 'foo');
+     *
+     * // Will return false, as 'not-in-set' is not in the set
+     * $redis->sismember('myset', 'not-in-set');
+     * ?>
+     * 
+     */
     public function sismember(string $key, mixed $value): Redis|bool;
 
-    public function slaveof(string $host = null, int $port = 6379): bool;
+    /**
+     * Turn a redis instance into a replica of another or promote a replica
+     * to a primary.
+     *
+     * This method and the corresponding command in Redis has been marked deprecated
+     * and users should instead use Redis::replicaof() if connecting to redis-server
+     * >= 5.0.0.
+     *
+     * @deprecated
+     *
+     * @see https://redis.io/commands/slaveof
+     * @see https://redis.io/commands/replicaof
+     * @see Redis::slaveof()
+     */
+    public function slaveof(string $host = NULL, int $port = 6379): Redis|bool;
+
+    /**
+     * Used to turn a Redis instance into a replica of another, or to remove
+     * replica status promoting the instance to a primary.
+     *
+     * @see https://redis.io/commands/replicaof
+     * @see https://redis.io/commands/slaveof
+     * @see Redis::slaveof()
+     *
+     * @param string $host The host of the primary to start replicating.
+     * @param string $port The port of the primary to start replicating.
+     *
+     * @return Redis|bool Success if we were successfully able to start replicating a primary or
+     *                    were able to promote teh replicat to a primary.
+     *
+     * 
+     *  'localhost']);
+     *
+     * // Attempt to become a replica of a Redis instance at 127.0.0.1:9999
+     * $redis->slaveof('127.0.0.1', 9999);
+     *
+     * // When passed no arguments, PhpRedis will deliver the command `SLAVEOF NO ONE`
+     * // attempting to promote the instance to a primary.
+     * $redis->slaveof();
+     * ?>
+     * 
+     */
+    public function replicaof(string $host = NULL, int $port = 6379): Redis|bool;
 
     /**
      * Update one or more keys last modified metadata.
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 5833affe64..eabfc65836 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5554480fcf6749dcfd69f371ad8dbd07fd8ed4c4 */
+ * Stub hash: 8c51e9b0082cb7939c7bc8dc226132eede02f420 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -788,11 +788,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sismember, 0, 2,
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "null")
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_slaveof, 0, 0, Redis, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "NULL")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379")
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_replicaof arginfo_class_Redis_slaveof
+
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_touch, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_MASK(0, key_or_array, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, more_keys, IS_STRING, 0)
@@ -1291,6 +1293,7 @@ ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, setnx);
 ZEND_METHOD(Redis, sismember);
 ZEND_METHOD(Redis, slaveof);
+ZEND_METHOD(Redis, replicaof);
 ZEND_METHOD(Redis, touch);
 ZEND_METHOD(Redis, slowlog);
 ZEND_METHOD(Redis, sort);
@@ -1539,7 +1542,8 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sismember, arginfo_class_Redis_sismember, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, replicaof, arginfo_class_Redis_replicaof, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, touch, arginfo_class_Redis_touch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index dabeae3f79..187a2cdf43 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1126,6 +1126,33 @@ int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                        char *kw, char **cmd, int *cmd_len, short *slot,
+                        void **ctx)
+{
+    zend_string *host = NULL;
+    zend_long port = 6379;
+
+    ZEND_PARSE_PARAMETERS_START(0, 2)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_STR(host)
+        Z_PARAM_LONG(port)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+    if (port < 0 || port > UINT16_MAX) {
+        php_error_docref(NULL, E_WARNING, "Invalid port %ld", (long)port);
+        return FAILURE;
+    }
+
+    if (ZEND_NUM_ARGS() == 2) {
+        *cmd_len = REDIS_SPPRINTF(cmd, kw, "Sd", host, (int)port);
+    } else {
+        *cmd_len = REDIS_SPPRINTF(cmd, kw, "ss", ZEND_STRL("NO"), ZEND_STRL("ONE"));
+    }
+
+    return SUCCESS;
+}
+
 int
 redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char *kw, char **cmd, int *cmd_len, short *slot,
diff --git a/redis_commands.h b/redis_commands.h
index 47176c4868..f56662e719 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -37,6 +37,10 @@ char *redis_variadic_str_cmd(char *kw, zval *argv, int argc, int *cmd_len);
  * we can write one function to handle all of them.  For example, there are
  * many COMMAND key value commands, or COMMAND key commands. */
 
+int redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+                        char *kw, char **cmd, int *cmd_len, short *slot,
+                        void **ctx);
+
 int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 5d4355cbfd..a793ef4496 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 5554480fcf6749dcfd69f371ad8dbd07fd8ed4c4 */
+ * Stub hash: 8c51e9b0082cb7939c7bc8dc226132eede02f420 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -673,6 +673,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slaveof, 0, 0, 0)
 	ZEND_ARG_INFO(0, port)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_replicaof arginfo_class_Redis_slaveof
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_touch, 0, 0, 1)
 	ZEND_ARG_INFO(0, key_or_array)
 	ZEND_ARG_VARIADIC_INFO(0, more_keys)
@@ -1129,6 +1131,7 @@ ZEND_METHOD(Redis, setex);
 ZEND_METHOD(Redis, setnx);
 ZEND_METHOD(Redis, sismember);
 ZEND_METHOD(Redis, slaveof);
+ZEND_METHOD(Redis, replicaof);
 ZEND_METHOD(Redis, touch);
 ZEND_METHOD(Redis, slowlog);
 ZEND_METHOD(Redis, sort);
@@ -1377,7 +1380,8 @@ static const zend_function_entry class_Redis_methods[] = {
 	ZEND_ME(Redis, setex, arginfo_class_Redis_setex, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, setnx, arginfo_class_Redis_setnx, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sismember, arginfo_class_Redis_sismember, ZEND_ACC_PUBLIC)
-	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC)
+	ZEND_ME(Redis, slaveof, arginfo_class_Redis_slaveof, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
+	ZEND_ME(Redis, replicaof, arginfo_class_Redis_replicaof, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, touch, arginfo_class_Redis_touch, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, slowlog, arginfo_class_Redis_slowlog, ZEND_ACC_PUBLIC)
 	ZEND_ME(Redis, sort, arginfo_class_Redis_sort, ZEND_ACC_PUBLIC)

From 504810a5d1599c4e5f8c749934de272a625a687f Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 1 Nov 2022 12:53:23 +0200
Subject: [PATCH 1687/1986] Issue #2068, Refactor ACL command

---
 library.c        |  21 +++++++++
 library.h        |   1 +
 redis.c          |  44 +------------------
 redis_commands.c | 111 +++++++++++++++++++++++++++++++++++++++--------
 redis_commands.h |   5 ++-
 5 files changed, 120 insertions(+), 62 deletions(-)

diff --git a/library.c b/library.c
index 3772b73dd9..1560e6b001 100644
--- a/library.c
+++ b/library.c
@@ -2168,6 +2168,27 @@ redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_t
     return FAILURE;
 }
 
+PHP_REDIS_API int
+redis_acl_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
+{
+    if (ctx == NULL) {
+        return redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR) {
+        return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 1) {
+        return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 2) {
+        return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 3) {
+        return redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else if (ctx == PHPREDIS_CTX_PTR + 4) {
+        return redis_acl_log_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL);
+    } else {
+        ZEND_ASSERT(!"memory corruption?");
+        return FAILURE;
+    }
+}
+
 PHP_REDIS_API int
 redis_read_acl_log_reply(RedisSock *redis_sock, zval *zret, long count) {
     zval zsub;
diff --git a/library.h b/library.h
index 05b34ed4e9..c52b0f2f67 100644
--- a/library.h
+++ b/library.h
@@ -161,6 +161,7 @@ PHP_REDIS_API int
 redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, void *ctx);
 
 /* Specialized ACL reply handlers */
+PHP_REDIS_API int redis_acl_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_read_acl_getuser_reply(RedisSock *redis_sock, zval *zret, long len);
 PHP_REDIS_API int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
 PHP_REDIS_API int redis_read_acl_log_reply(RedisSock *redis_sock, zval *zret, long count);
diff --git a/redis.c b/redis.c
index 0afb4b4881..2aef973d29 100644
--- a/redis.c
+++ b/redis.c
@@ -1129,49 +1129,7 @@ PHP_METHOD(Redis, type)
 
 /* {{{ proto mixed Redis::acl(string $op, ...) }}} */
 PHP_METHOD(Redis, acl) {
-    RedisSock *redis_sock;
-    FailableResultCallback cb;
-    zval *zargs;
-    zend_string *op;
-    char *cmd;
-    int cmdlen, argc = ZEND_NUM_ARGS();
-
-    if (argc < 1 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
-        if (argc < 1) {
-            php_error_docref(NULL, E_WARNING, "ACL command requires at least one argument");
-        }
-        RETURN_FALSE;
-    }
-
-    zargs = emalloc(argc * sizeof(*zargs));
-    if (zend_get_parameters_array(ht, argc, zargs) == FAILURE) {
-        efree(zargs);
-        RETURN_FALSE;
-    }
-
-    /* Read the subcommand and set response callback */
-    op = zval_get_string(&zargs[0]);
-    if (zend_string_equals_literal_ci(op, "GETUSER")) {
-        cb = redis_acl_getuser_reply;
-    } else if (zend_string_equals_literal_ci(op, "LOG")) {
-        cb = redis_acl_log_reply;
-    } else {
-        cb = redis_read_variant_reply;
-    }
-
-    /* Make our command and free args */
-    cmd = redis_variadic_str_cmd("ACL", zargs, argc, &cmdlen);
-
-    zend_string_release(op);
-    efree(zargs);
-
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmdlen);
-    if (IS_ATOMIC(redis_sock)) {
-        if (cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
-            RETURN_FALSE;
-        }
-    }
-    REDIS_PROCESS_RESPONSE(cb);
+    REDIS_PROCESS_CMD(acl, redis_acl_response);
 }
 
 /* {{{ proto long Redis::append(string key, string val) */
diff --git a/redis_commands.c b/redis_commands.c
index 187a2cdf43..5c3dcb90fd 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2100,6 +2100,100 @@ redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
+int
+redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+              char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+    smart_string cmdstr = {0};
+    zend_string *zstr;
+    zval *z_args;
+    int argc, i;
+
+    if ((argc = ZEND_NUM_ARGS()) < 1) {
+        php_error_docref(NULL, E_WARNING, "ACL command requires at least one argument");
+        return FAILURE;
+    }
+
+    z_args = ecalloc(argc, sizeof(*z_args));
+    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
+        goto failure;
+    }
+
+    zstr = zval_get_string(&z_args[0]);
+    if (zend_string_equals_literal_ci(zstr, "CAT") ||
+        zend_string_equals_literal_ci(zstr, "LIST") ||
+        zend_string_equals_literal_ci(zstr, "USERS")
+    ) {
+        *ctx = NULL;
+    } else if (zend_string_equals_literal_ci(zstr, "LOAD") ||
+        zend_string_equals_literal_ci(zstr, "SAVE")
+    ) {
+        *ctx = PHPREDIS_CTX_PTR;
+    } else if (zend_string_equals_literal_ci(zstr, "GENPASS") ||
+        zend_string_equals_literal_ci(zstr, "WHOAMI")
+    ) {
+        *ctx = PHPREDIS_CTX_PTR + 1;
+    } else if (zend_string_equals_literal_ci(zstr, "SETUSER")) {
+        if (argc < 2) {
+            php_error_docref(NULL, E_WARNING, "ACL SETUSER requires at least one argument");
+            zend_string_release(zstr);
+            goto failure;
+        }
+        *ctx = PHPREDIS_CTX_PTR;
+    } else if (zend_string_equals_literal_ci(zstr, "DELUSER")) {
+        if (argc < 2) {
+            php_error_docref(NULL, E_WARNING, "ACL DELUSER requires at least one argument");
+            zend_string_release(zstr);
+            goto failure;
+        }
+        *ctx = PHPREDIS_CTX_PTR + 2;
+    } else if (zend_string_equals_literal_ci(zstr, "GETUSER")) {
+        if (argc < 2) {
+            php_error_docref(NULL, E_WARNING, "ACL GETUSER requires at least one argument");
+            zend_string_release(zstr);
+            goto failure;
+        }
+        *ctx = PHPREDIS_CTX_PTR + 3;
+    } else if (zend_string_equals_literal_ci(zstr, "DRYRUN")) {
+        if (argc < 3) {
+            php_error_docref(NULL, E_WARNING, "ACL DRYRUN requires at least two arguments");
+            zend_string_release(zstr);
+            goto failure;
+        }
+        *ctx = PHPREDIS_CTX_PTR;
+    } else if (zend_string_equals_literal_ci(zstr, "LOG")) {
+        if (argc > 1 && Z_TYPE(z_args[1]) == IS_STRING && ZVAL_STRICMP_STATIC(&z_args[1], "RESET")) {
+            *ctx = PHPREDIS_CTX_PTR;
+        } else {
+            *ctx = PHPREDIS_CTX_PTR + 4;
+        }
+    } else {
+        php_error_docref(NULL, E_WARNING, "Unknown ACL operation '%s'", ZSTR_VAL(zstr));
+        zend_string_release(zstr);
+        goto failure;
+    }
+
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "ACL");
+    redis_cmd_append_sstr_zstr(&cmdstr, zstr);
+    zend_string_release(zstr);
+
+    for (i = 1; i < argc; ++i) {
+        zstr = zval_get_string(&z_args[i]);
+        redis_cmd_append_sstr_zstr(&cmdstr, zstr);
+        zend_string_release(zstr);
+    }
+    efree(z_args);
+
+    *cmd = cmdstr.c;
+    *cmd_len = cmdstr.len;
+
+    return SUCCESS;
+
+failure:
+    efree(z_args);
+    return FAILURE;
+}
+
 /* Attempt to pull a long expiry from a zval.  We're more restrictave than zval_get_long
  * because that function will return integers from things like open file descriptors
  * which should simply fail as a TTL */
@@ -3168,23 +3262,6 @@ int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     return SUCCESS;
 }
 
-char *redis_variadic_str_cmd(char *kw, zval *argv, int argc, int *cmd_len) {
-    smart_string cmdstr = {0};
-    zend_string *zstr;
-    int i;
-
-    redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw));
-
-    for (i = 0; i < argc; i++) {
-        zstr = zval_get_string(&argv[i]);
-        redis_cmd_append_sstr_zstr(&cmdstr, zstr);
-        zend_string_release(zstr);
-    }
-
-    *cmd_len = cmdstr.len;
-    return cmdstr.c;
-}
-
 int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                    char **cmd, int *cmd_len, short *slot, void **ctx)
 {
diff --git a/redis_commands.h b/redis_commands.h
index f56662e719..7faef7d5c3 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -31,8 +31,6 @@ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len);
 /* Construct a script command */
 smart_string *redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args);
 
-char *redis_variadic_str_cmd(char *kw, zval *argv, int argc, int *cmd_len);
-
 /* Redis command generics.  Many commands share common prototypes meaning that
  * we can write one function to handle all of them.  For example, there are
  * many COMMAND key value commands, or COMMAND key commands. */
@@ -187,6 +185,9 @@ int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock
  * specific processing we do (e.g. verifying subarguments) that make them
  * unique */
 
+int redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+    char **cmd, int *cmd_len, short *slot, void **ctx);
+
 int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     char **cmd, int *cmd_len, short *slot, void **ctx);
 

From 376d4d27b01a05c1a0634dfad7b3d2e6429a0a7e Mon Sep 17 00:00:00 2001
From: Pavlo Yatsukhnenko 
Date: Tue, 1 Nov 2022 13:11:54 +0200
Subject: [PATCH 1688/1986] Use fast_zpp API

---
 redis_commands.c | 84 ++++++++++++++++++++----------------------------
 1 file changed, 34 insertions(+), 50 deletions(-)

diff --git a/redis_commands.c b/redis_commands.c
index 5c3dcb90fd..47faaebddf 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2105,93 +2105,77 @@ redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
               char **cmd, int *cmd_len, short *slot, void **ctx)
 {
     smart_string cmdstr = {0};
-    zend_string *zstr;
-    zval *z_args;
-    int argc, i;
+    zend_string *op, *zstr;
+    zval *z_args = NULL;
+    int argc = 0, i;
 
-    if ((argc = ZEND_NUM_ARGS()) < 1) {
-        php_error_docref(NULL, E_WARNING, "ACL command requires at least one argument");
-        return FAILURE;
-    }
-
-    z_args = ecalloc(argc, sizeof(*z_args));
-    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
-        goto failure;
-    }
+    ZEND_PARSE_PARAMETERS_START(1, -1)
+        Z_PARAM_STR(op)
+        Z_PARAM_OPTIONAL
+        Z_PARAM_VARIADIC('*', z_args, argc)
+    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
 
-    zstr = zval_get_string(&z_args[0]);
-    if (zend_string_equals_literal_ci(zstr, "CAT") ||
-        zend_string_equals_literal_ci(zstr, "LIST") ||
-        zend_string_equals_literal_ci(zstr, "USERS")
+    if (zend_string_equals_literal_ci(op, "CAT") ||
+        zend_string_equals_literal_ci(op, "LIST") ||
+        zend_string_equals_literal_ci(op, "USERS")
     ) {
         *ctx = NULL;
-    } else if (zend_string_equals_literal_ci(zstr, "LOAD") ||
-        zend_string_equals_literal_ci(zstr, "SAVE")
+    } else if (zend_string_equals_literal_ci(op, "LOAD") ||
+        zend_string_equals_literal_ci(op, "SAVE")
     ) {
         *ctx = PHPREDIS_CTX_PTR;
-    } else if (zend_string_equals_literal_ci(zstr, "GENPASS") ||
-        zend_string_equals_literal_ci(zstr, "WHOAMI")
+    } else if (zend_string_equals_literal_ci(op, "GENPASS") ||
+        zend_string_equals_literal_ci(op, "WHOAMI")
     ) {
         *ctx = PHPREDIS_CTX_PTR + 1;
-    } else if (zend_string_equals_literal_ci(zstr, "SETUSER")) {
-        if (argc < 2) {
+    } else if (zend_string_equals_literal_ci(op, "SETUSER")) {
+        if (argc < 1) {
             php_error_docref(NULL, E_WARNING, "ACL SETUSER requires at least one argument");
-            zend_string_release(zstr);
-            goto failure;
+            return FAILURE;
         }
         *ctx = PHPREDIS_CTX_PTR;
-    } else if (zend_string_equals_literal_ci(zstr, "DELUSER")) {
-        if (argc < 2) {
+    } else if (zend_string_equals_literal_ci(op, "DELUSER")) {
+        if (argc < 1) {
             php_error_docref(NULL, E_WARNING, "ACL DELUSER requires at least one argument");
-            zend_string_release(zstr);
-            goto failure;
+            return FAILURE;
         }
         *ctx = PHPREDIS_CTX_PTR + 2;
-    } else if (zend_string_equals_literal_ci(zstr, "GETUSER")) {
-        if (argc < 2) {
+    } else if (zend_string_equals_literal_ci(op, "GETUSER")) {
+        if (argc < 1) {
             php_error_docref(NULL, E_WARNING, "ACL GETUSER requires at least one argument");
-            zend_string_release(zstr);
-            goto failure;
+            return FAILURE;
         }
         *ctx = PHPREDIS_CTX_PTR + 3;
-    } else if (zend_string_equals_literal_ci(zstr, "DRYRUN")) {
-        if (argc < 3) {
+    } else if (zend_string_equals_literal_ci(op, "DRYRUN")) {
+        if (argc < 2) {
             php_error_docref(NULL, E_WARNING, "ACL DRYRUN requires at least two arguments");
-            zend_string_release(zstr);
-            goto failure;
+            return FAILURE;
         }
         *ctx = PHPREDIS_CTX_PTR;
-    } else if (zend_string_equals_literal_ci(zstr, "LOG")) {
-        if (argc > 1 && Z_TYPE(z_args[1]) == IS_STRING && ZVAL_STRICMP_STATIC(&z_args[1], "RESET")) {
+    } else if (zend_string_equals_literal_ci(op, "LOG")) {
+        if (argc > 0 && Z_TYPE(z_args[0]) == IS_STRING && ZVAL_STRICMP_STATIC(&z_args[0], "RESET")) {
             *ctx = PHPREDIS_CTX_PTR;
         } else {
             *ctx = PHPREDIS_CTX_PTR + 4;
         }
     } else {
-        php_error_docref(NULL, E_WARNING, "Unknown ACL operation '%s'", ZSTR_VAL(zstr));
-        zend_string_release(zstr);
-        goto failure;
+        php_error_docref(NULL, E_WARNING, "Unknown ACL operation '%s'", ZSTR_VAL(op));
+        return FAILURE;
     }
 
-    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "ACL");
-    redis_cmd_append_sstr_zstr(&cmdstr, zstr);
-    zend_string_release(zstr);
+    REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + argc, "ACL");
+    redis_cmd_append_sstr_zstr(&cmdstr, op);
 
-    for (i = 1; i < argc; ++i) {
+    for (i = 0; i < argc; ++i) {
         zstr = zval_get_string(&z_args[i]);
         redis_cmd_append_sstr_zstr(&cmdstr, zstr);
         zend_string_release(zstr);
     }
-    efree(z_args);
 
     *cmd = cmdstr.c;
     *cmd_len = cmdstr.len;
 
     return SUCCESS;
-
-failure:
-    efree(z_args);
-    return FAILURE;
 }
 
 /* Attempt to pull a long expiry from a zval.  We're more restrictave than zval_get_long

From 993408892696040cc4fba0495c8eb9940aaee64d Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Nov 2022 10:33:14 -0700
Subject: [PATCH 1689/1986] Documentation:  Add several more docblocks.

---
 redis.stub.php         | 129 +++++++++++++++++++++++++++++++++++------
 redis_arginfo.h        |  20 +++----
 redis_legacy_arginfo.h |   2 +-
 3 files changed, 123 insertions(+), 28 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index d2ed4449d7..fd78a7b2ed 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -2049,9 +2049,83 @@ public function strlen(string $key);
 
     public function subscribe(array $channels, callable $cb): bool;
 
-    public function swapdb(string $src, string $dst): bool;
+    /**
+     * Atomically swap two Redis databases so that all of the keys in the source database will
+     * now be in the destination database and vice-versa.
+     *
+     * Note: This command simply swaps Redis' internal pointer to the database and is therefore
+     * very fast, regardless of the size of the underlying databases.
+     *
+     * @see https://redis.io/commands/swapdb
+     * @see Redis::del()
+     *
+     * @param int $src The source database number
+     * @param int $dst The destination database number
+     *
+     * @return Redis|bool Success if the databases could be swapped and false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->multi()->select(0)
+     *                ->set('db0-key1', 'value1')->set('db0-key2', 'value2')
+     *                ->select(1)
+     *                ->set('db1-key1', 'value1')->set('db1-key2', 'value2')
+     *                ->select(0)
+     *                ->exec();
+     *
+     * // Array
+     * // (
+     * //     [0] => db0-key1
+     * //     [1] => db0-key2
+     * // )
+     * print_r($redis->keys('*'));
+     *
+     * // Swap db0 and db1
+     * $redis->swapdb(0, 1);
+     *
+     * // Array
+     * // (
+     * //     [0] => db1-key2
+     * //     [1] => db1-key1
+     * // )
+     * print_r($redis->keys('*'));
+     *
+     * // Swap them back
+     * $redis->swapdb(0, 1);
+     *
+     * // Array
+     * // (
+     * //     [0] => db0-key1
+     * //     [1] => db0-key2
+     * // )
+     * print_r($redis->keys('*'));
+     * ?>
+     * 
+     */
+    public function swapdb(int $src, int $dst): Redis|bool;
 
-    public function time(): array;
+    /**
+     * Retrieve the server time from the connected Redis instance.
+     *
+     * @see https://redis.io/commands/time
+     *
+     * @return A two element array consisting of a Unix Timestamp and the number of microseconds
+     *         elapsed since the second.
+     *
+     * 
+     *  'localhost']);
+     *
+     * // Array
+     * // (
+     * //     [0] => 1667271026
+     * //     [1] => 355678
+     * // )
+     * print_r($redis->time());
+     */
+    public function time(): Redis|array;
 
     public function ttl(string $key): Redis|int|false;
 
@@ -2059,9 +2133,30 @@ public function ttl(string $key): Redis|int|false;
     public function type(string $key);
 
     /**
-     * @return Redis|int|false
+     * Delete one or more keys from the Redis database.  Unlike this operation, the actual
+     * deletion is asynchronous, meaning it is safe to delete large keys without fear of
+     * Redis blocking for a long period of time.
+     *
+     * @param array|string $key_or_keys Either an array with one or more keys or a string with
+     *                                  the first key to delete.
+     * @param string       $other_keys  If the first argument passed to this method was a string
+     *                                  you may pass any number of additional key names.
+     *
+     * @return Redis|int|false The number of keys deleted or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * // OPTION 1:  Called with a single array of keys
+     * $redis->unlink(['key1', 'key2', 'key3']);
+     *
+     * // OPTION 2:  Called with a variadic number of arguments
+     * $redis->unlink('key1', 'key2', 'key3');
+     * ?>
+     * 
      */
-    public function unlink(array|string $key, string ...$other_keys);
+    public function unlink(array|string $key, string ...$other_keys): Redis|int|false;
 
     public function unsubscribe(array $channels): Redis|array|bool;
 
@@ -2094,19 +2189,19 @@ public function xdel(string $key, array $ids): Redis|int|false;
      * @see https://redis.io/commands/xgroup/
      *
      * @param string $operation      The subcommand you intend to execute.  Valid options are as follows
-     *                               'HELP'          - Redis will return information about the command
-     *                                                 Requires: none
-     *                               'CREATE'        - Create a consumer group.
-     *                                                 Requires:  Key, group, consumer.
-     *                               'SETID'         - Set the ID of an existing consumer group for the stream.
-     *                                                 Requires:  Key, group, id.
-     *                               'CREATECONSUMER - Create a new consumer group for the stream.  You must
-     *                                                 also pass key, group, and the consumer name you wish to
-     *                                                 create.
-     *                                                 Requires:  Key, group, consumer.
-     *                               'DELCONSUMER'   - Delete a consumer from group attached to the stream.
-     *                                                 Requires:  Key, group, consumer.
-     *                               'DESTROY'       - Delete a consumer group from a stream.
+     *                               'HELP'           - Redis will return information about the command
+     *                                                  Requires: none
+     *                               'CREATE'         - Create a consumer group.
+     *                                                  Requires:  Key, group, consumer.
+     *                               'SETID'          - Set the ID of an existing consumer group for the stream.
+     *                                                  Requires:  Key, group, id.
+     *                               'CREATECONSUMER' - Create a new consumer group for the stream.  You must
+     *                                                  also pass key, group, and the consumer name you wish to
+     *                                                  create.
+     *                                                  Requires:  Key, group, consumer.
+     *                               'DELCONSUMER'    - Delete a consumer from group attached to the stream.
+     *                                                  Requires:  Key, group, consumer.
+     *                               'DESTROY'        - Delete a consumer group from a stream.
      *                                                  Requires:  Key, group.
      * @param string $key            The STREAM we're operating on.
      * @param string $group          The consumer group we want to create/modify/delete.
diff --git a/redis_arginfo.h b/redis_arginfo.h
index eabfc65836..07a81ef798 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8c51e9b0082cb7939c7bc8dc226132eede02f420 */
+ * Stub hash: 3cd40e39fce29d74a80c4c7627e52a2b2499a1f4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -845,22 +845,19 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 2, _IS
 	ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_swapdb, 0, 2, _IS_BOOL, 0)
-	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_swapdb, 0, 2, Redis, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, src, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, dst, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_time, 0, 0, IS_ARRAY, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_time, 0, 0, Redis, MAY_BE_ARRAY)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_ttl arginfo_class_Redis_expiretime
 
 #define arginfo_class_Redis_type arginfo_class_Redis_strlen
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_unlink, 0, 0, 1)
-	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_unsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
@@ -868,7 +865,10 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_watch arginfo_class_Redis_unlink
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_watch, 0, 0, 1)
+	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_wait, 0, 2, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index a793ef4496..7ec04be92b 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 8c51e9b0082cb7939c7bc8dc226132eede02f420 */
+ * Stub hash: 3cd40e39fce29d74a80c4c7627e52a2b2499a1f4 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 70a55f3ef951ba4250ef991f58609e7ad88a3f4f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Nov 2022 11:04:44 -0700
Subject: [PATCH 1690/1986] Documentation:  More docblocks for eventual

[skip ci]
---
 redis.stub.php         | 122 +++++++++++++++++++++++++++++++++++++++--
 redis_arginfo.h        |   8 +--
 redis_legacy_arginfo.h |   2 +-
 3 files changed, 122 insertions(+), 10 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index fd78a7b2ed..c3e1280e55 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -2044,9 +2044,65 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i
      */
     public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
 
-    /** @return Redis|int|false*/
-    public function strlen(string $key);
+    /**
+     * Retrieve the length of a Redis STRING key.
+     *
+     * @param string $key The key we want the length of.
+     *
+     * @return Redis|int|false The length of the string key if it exists, zero if it does not, and
+     *                         false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('string');
+     *
+     * $redis->set('string', 'foo');
+     *
+     * // strlen('foo') == 3
+     * $redis->strlen('string');
+     *
+     * $redis->append('string', 'bar');
+     *
+     * // strlen('foobar') == 6
+     * $redis->strlen('string');
+     *
+     * ?>
+     * 
+     */
+    public function strlen(string $key): Redis|int|false;
 
+    /**
+     * Subscribe to one or more Redis pubsub channels.
+     *
+     * @param array    $channels One or more channel names.
+     * @param callable $cb       The callback PhpRedis will invoke when we receive a message
+     *                           from one of the subscribed channels.
+     *
+     * @return bool True on success, false on faiilure.  Note that this command will block the
+     *              client in a subscribe loop, waiting for messages to arrive.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
+     *     echo "[$channel]: $message\n";
+     *
+     *     // Unsubscribe from the message channel when we read 'quit'
+     *     if ($message == 'quit') {
+     *         echo "Unsubscribing from '$channel'\n";
+     *         $redis->unsubscribe([$channel]);
+     *     }
+     * });
+     *
+     * // Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be
+     * // broken and this command will execute.
+     * echo "Subscribe loop ended\n";
+     * ?>
+     * 
+     */
     public function subscribe(array $channels, callable $cb): bool;
 
     /**
@@ -2127,16 +2183,67 @@ public function swapdb(int $src, int $dst): Redis|bool;
      */
     public function time(): Redis|array;
 
+    /**
+     * Get the amount of time a Redis key has before it will expire, in seconds.
+     *
+     * @param string $key      The Key we want the TTL for.
+     * @return Redis|int|false (a) The number of seconds until the key expires, or -1 if the key has
+     *                         no expiration, and -2 if the key does not exist.  In the event of an
+     *                         error, this command will return false.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->multi()
+     *       ->setex('expires_in_60s', 60, 'test')
+     *       ->set('doesnt_expire', 'persistent')
+     *       ->del('not_a_key')
+     *       ->exec();
+     *
+     * // Returns <= 60
+     * $redis->ttl('expires_in_60s');
+     *
+     * // Returns -1
+     * $redis->ttl('doesnt_expire');
+     *
+     * // Returns -2 (key doesn't exist)
+     * $redis->ttl('not_a_key');
+     *
+     * ?>
+     * 
+     */
     public function ttl(string $key): Redis|int|false;
 
-    /** @return Redis|int|false*/
-    public function type(string $key);
+    /**
+     * Get the type of a given Redis key.
+     *
+     * @see https://redis.io/commands/type
+     *
+     * @param  string $key     The key to check
+     * @return Redis|int|false The Redis type constant or false on failure.
+     *
+     * The Redis class defines several type constants that correspond with Redis key types.
+     *
+     *     Redis::REDIS_NOT_FOUND
+     *     Redis::REDIS_STRING
+     *     Redis::REDIS_SET
+     *     Redis::REDIS_LIST
+     *     Redis::REDIS_ZSET
+     *     Redis::REDIS_HASH
+     *     Redis::REDIS_STREAM
+     */
+    public function type(string $key): Redis|int|false;
 
     /**
      * Delete one or more keys from the Redis database.  Unlike this operation, the actual
      * deletion is asynchronous, meaning it is safe to delete large keys without fear of
      * Redis blocking for a long period of time.
      *
+     * @see https://redis.io/commands/unlink
+     * @see https://redis.io/commands/del
+     * @see Redis::del()
+     *
      * @param array|string $key_or_keys Either an array with one or more keys or a string with
      *                                  the first key to delete.
      * @param string       $other_keys  If the first argument passed to this method was a string
@@ -2158,6 +2265,13 @@ public function type(string $key);
      */
     public function unlink(array|string $key, string ...$other_keys): Redis|int|false;
 
+    /**
+     * Unsubscribe from one or more subscribed channels.
+     *
+     * @see https://redis.io/commands/unsubscribe
+     * @see Redis::subscribe()
+     *
+     */
     public function unsubscribe(array $channels): Redis|array|bool;
 
     /** @return bool|Redis */
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 07a81ef798..2c6a011146 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3cd40e39fce29d74a80c4c7627e52a2b2499a1f4 */
+ * Stub hash: 7baf9e08800a4280ebbf346f397b3b833d4f03e2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -836,9 +836,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sscan, 0, 2, MAY_BE_
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_strlen, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_strlen arginfo_class_Redis_expiretime
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 2, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
@@ -855,7 +853,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_ttl arginfo_class_Redis_expiretime
 
-#define arginfo_class_Redis_type arginfo_class_Redis_strlen
+#define arginfo_class_Redis_type arginfo_class_Redis_expiretime
 
 #define arginfo_class_Redis_unlink arginfo_class_Redis_del
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 7ec04be92b..743232a9a3 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 3cd40e39fce29d74a80c4c7627e52a2b2499a1f4 */
+ * Stub hash: 7baf9e08800a4280ebbf346f397b3b833d4f03e2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From b04684d442271efafeaf49208ec6fa7b2b7fe1c6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Nov 2022 12:22:36 -0700
Subject: [PATCH 1691/1986] Documentation:  More docblocks with examples

[skip ci]
---
 redis.stub.php         | 190 +++++++++++++++++++++++++++++++++++++++++
 redis_arginfo.h        |   2 +-
 redis_commands.c       |   2 +-
 redis_legacy_arginfo.h |   2 +-
 4 files changed, 193 insertions(+), 3 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index c3e1280e55..904e888e72 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -2404,6 +2404,70 @@ public function zRange(string $key, mixed $start, mixed $end, array|bool|null $o
 
     public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false;
 
+    /**
+     * Retrieve a range of members from a sorted set by their score.
+     *
+     * @see https://redis.io/commands/zrangebyscore
+     *
+     * @param string $key     The sorted set to query.
+     * @param string $start   The minimum score of elements that Redis should return.
+     * @param string $end     The maximum score of elements that Redis should return.
+     * @param array  $options Options that change how Redis will execute the command.
+     *
+     *                        OPTION       TYPE            MEANING
+     *                        'WITHSCORES' bool            Whether to also return scores.
+     *                        'LIMIT'      [offset, count] Limit the reply to a subset of elements.
+     *
+     * @return Redis|array|false The number of matching elements or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     *
+     * for ($i = 0; $i < 50; $i++) {
+     *     $redis->zAdd('zs', $i, "mem:$i");
+     * }
+     *
+     * // Array
+     * // (
+     * //     [0] => mem:0
+     * //     [1] => mem:1
+     * //     [2] => mem:2
+     * //     [3] => mem:3
+     * //     [4] => mem:4
+     * // )
+     * $redis->zRangeByScore('zs', 0, 4);
+     *
+     * // Array
+     * // (
+     * //     [mem:20] => 20
+     * //     [mem:21] => 21
+     * //     [mem:22] => 22
+     * //     [mem:23] => 23
+     * //     [mem:24] => 24
+     * //     [mem:25] => 25
+     * //     [mem:26] => 26
+     * //     [mem:27] => 27
+     * //     [mem:28] => 28
+     * //     [mem:29] => 29
+     * //     [mem:30] => 30
+     * // )
+     * $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]);
+     *
+     * // Array
+     * // (
+     * //     [mem:25] => 25
+     * //     [mem:26] => 26
+     * //     [mem:27] => 27
+     * //     [mem:28] => 28
+     * //     [mem:29] => 29
+     * // )
+     * $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]);
+     * ?>
+     * 
+     */
     public function zRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false;
 
     /**
@@ -2427,12 +2491,138 @@ public function zRangeByScore(string $key, string $start, string $end, array $op
     public function zrangestore(string $dstkey, string $srckey, string $start, string $end,
                                 array|bool|null $options = NULL): Redis|int|false;
 
+    /**
+     * Retrieve one or more random members from a Redis sorted set.
+     *
+     * @see https://redis.io/commands/zrandmember
+     *
+     * @param string $key     The sorted set to pull random members from.
+     * @param array  $options One or more options that determine exactly how the command operates.
+     *
+     *                        OPTION       TYPE     MEANING
+     *                        'COUNT'      int      The number of random members to return.
+     *                        'WITHSCORES' bool     Whether to return scores and members instead of
+     *                                              just members.
+     * 
+     *  'localhost']);
+     *
+     * $redis->multi()->del('zs')->zadd('zs', 1, 'one', 2, 'two', 3, 'three')->exec();
+     *
+     * // Return two random members from our set, with scores
+     * $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]);
+     *
+     * ?>
+     * 
+     */
     public function zRandMember(string $key, array $options = null): Redis|string|array;
 
+    /**
+     * Get the rank of a member of a sorted set, by score.
+     *
+     * @see https://redis.io/commands/zrank
+     *
+     * @param string $key     The sorted set to check.
+     * @param mixed  $memeber The member to test.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->multi()->del('zs')->zadd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three')->exec();
+     *
+     * // Rank 0
+     * $redis->zRank('zs', 'zero');
+     *
+     * // Rank 3
+     * $redis->zRank('zs', 'three');
+     *
+     * ?>
+     * 
+     *
+     */
     public function zRank(string $key, mixed $member): Redis|int|false;
 
+    /**
+     * Remove one or more members from a Redis sorted set.
+     *
+     * @see https://redis.io/commands/zrem
+     *
+     * @param mixed $key           The sorted set in question.
+     * @param mixed $member        The first member to remove.
+     * @param mixed $other_members One or more members to remove passed in a variadic fashion.
+     *
+     * @return Redis|int|false The number of members that were actually removed or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     *
+     * for ($i = 0; $i < 10; $i++) {
+     *     $redis->zAdd('zs', $i, "mem:$i");
+     * }
+     *
+     * // Remove a few elements
+     * $redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9');
+     *
+     * // Array
+     * // (
+     * //     [0] => mem:3
+     * //     [1] => mem:4
+     * //     [2] => mem:5
+     * // )
+     * $redis->zRange('zs', 0, -1);
+     * ?>
+     */
     public function zRem(mixed $key, mixed $member, mixed ...$other_members): Redis|int|false;
 
+    /**
+     * Remove zero or more elements from a Redis sorted set by legographical range.
+     *
+     * @see https://redis.io/commands/zremrangebylex
+     * @see Redis::zrangebylex()
+     *
+     * @param string $key The sorted set to remove elements from.
+     * @param string $min The start of the lexographical range to remove.
+     * @param string $max The end of the lexographical range to remove
+     *
+     * @return Redis|int|false The number of elements removed from the set or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->pipeline()->del('zs')
+     *                ->zAdd('zs', 1, 'apple', 2, 'banana', 3, 'carrot', 4, 'date', 5, 'eggplant')
+     *                ->exec();
+     *
+     *
+     * // Remove a* (inclusive) .. b* (exclusive), meaning 'apple' will be removed, but 'banana' not
+     * $redis->zRemRangeByLex('zs', '[a', '(b');
+     *
+     * // Array
+     * // (
+     * //     [0] => banana
+     * //     [1] => carrot
+     * //     [2] => date
+     * //     [3] => eggplant
+     * // )
+     * print_r($redis->zRange('zs', 0, -1));
+     *
+     * // Remove the elements between 'banana' and 'eggplant'
+     * $redis->zRemRangeByLex('zs', '(banana', '(eggplant');
+     *
+     * // Array
+     * // (
+     * //     [0] => banana
+     * //     [1] => eggplant
+     * // )
+     * print_r($redis->zRange('zs', 0, -1));
+     * ?>
+     * 
+     */
     public function zRemRangeByLex(string $key, string $min, string $max): Redis|int|false;
 
     public function zRemRangeByRank(string $key, int $start, int $end): Redis|int|false;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 2c6a011146..3b2fe5a2e3 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7baf9e08800a4280ebbf346f397b3b833d4f03e2 */
+ * Stub hash: 08c0be22623f22153c7df080cfb93388b73fa259 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_commands.c b/redis_commands.c
index 47faaebddf..332351ebff 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -1065,7 +1065,7 @@ static int redis_cmd_append_sstr_score(smart_string *dst, zval *score) {
         return SUCCESS;
 
     /* Nothing appended, failure */
-    php_error_docref(NULL, E_WARNING, "Weights must be numeric or '-inf','inf','+inf'");
+    php_error_docref(NULL, E_WARNING, "scores must be numeric or '-inf', 'inf', '+inf'");
     return FAILURE;
 }
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 743232a9a3..a5700322e3 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 7baf9e08800a4280ebbf346f397b3b833d4f03e2 */
+ * Stub hash: 08c0be22623f22153c7df080cfb93388b73fa259 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 980ea6b15425628f06a0787e80fb516ef62a0eb9 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Nov 2022 14:13:17 -0700
Subject: [PATCH 1692/1986] Documentation:  More docblocks with examples

[skip ci]
---
 redis.stub.php         | 251 ++++++++++++++++++++++++++++++++++++++++-
 redis_arginfo.h        |  10 +-
 redis_legacy_arginfo.h |   9 +-
 3 files changed, 257 insertions(+), 13 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 904e888e72..eab463c4dd 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -2351,19 +2351,224 @@ public function xtrim(string $key, int $maxlen, bool $approx = false, bool $mini
 
     public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|false;
 
+    /**
+     * Return the number of elements in a sorted set.
+     *
+     * @see https://redis.io/commands/zcard
+     *
+     * @param string $key The sorted set to retreive cardinality from.
+     *
+     * @return Redis|int|false The number of elements in the set or false on failure
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     * $redis->zAdd('zs', 0, 'a', 1, 'b', 2, 'c');
+     *
+     * // count(['a', 'b', 'c']) == 3
+     * $redis->zCard('zs');
+     * ?>
+     * 
+     */
     public function zCard(string $key): Redis|int|false;
 
-    public function zCount(string $key, string $start , string $end): Redis|int|false;
+    /**
+     * Count the number of members in a sorted set with scores inside a provided range.
+     *
+     * @see https://redis.io/commands/zcount
+     *
+     * @param string $key The sorted set to check.
+     * @param string $min The minimum score to include in the count
+     * @param string $max The maximum score to include in the count
+     *
+     * NOTE:  In addition to a floating point score you may pass the special values of '-inf' and
+     *        '+inf' meaning negative and positive infinity, respectively.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('fruit-rankings');
+     * $redis->zadd('fruit-rankings', -99, 'tomato', 50, 'apple', 60, 'pear', 85, 'mango');
+     *
+     * // count(['apple', 'oear', 'mango']) == 3
+     * $redis->zCount('fruit-rankings', '0', '+inf');
+     *
+     * // count(['apple', 'pear']) == 2
+     * $redis->zCount('fruit-rankings', 50, 60);
+     *
+     * // count(['tomato']) == 1
+     * $redis->zCount('fruit-rankings', '-inf', 0);
+     * ?>
+     * 
+     */
+    public function zCount(string $key, string $start, string $end): Redis|int|false;
+
+    /**
+     * Create or increment the score of a member in a Redis sorted set
+     *
+     * @see https://redis.io/commands/zincrby
+     *
+     * @param string $key   The sorted set in question.
+     * @param float  $value How much to increment the score.
+     *
+     * @return Redis|float|false The new score of the member or false on failure.
 
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     * $redis->zAdd('zs', 0, 'apples', 2, 'bananas');
+     *
+     * // 2 + 5.0 == 7
+     * print_r($redis->zIncrBy('zs', 5.0, 'bananas'));
+     *
+     * // new element so 0 + 2.0 == 2
+     * print_r($redis->zIncrBy('zs', 2.0, 'eggplants'));
+     * ?>
+     * 
+     */
     public function zIncrBy(string $key, float $value, mixed $member): Redis|float|false;
 
+    /**
+     * Count the number of elements in a sorted set whos members fall within the provided
+     * lexographical range.
+     *
+     * @see https://redis.io/commands/zlexcount
+     *
+     * @param string $key The sorted set to check.
+     * @param string $min The minimum matching lexographical string
+     * @param string $max The maximum matching lexographical string
+     *
+     * @return Redis|int|false The number of members that fall within the range or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('captains');
+     * $redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
+     *
+     * count(['Archer', 'Janeway', 'Kirk', 'Picard']) == 4
+     * $redis->zLexCount('captains', '[A', '[S');
+     *
+     * count(['Kirk', 'Picard']) == 2
+     * $redis->zRangeByLex('captains', '[A', '[S', 2, 2);
+     * ?>
+     * 
+     *
+     */
     public function zLexCount(string $key, string $min, string $max): Redis|int|false;
 
-    public function zMscore(string $key, string $member, string ...$other_members): Redis|array|false;
+    /**
+     * Retreive the score of one or more members in a sorted set.
+     *
+     * @see https://redis.io/commands/zmscore
+     *
+     * @param string $key           The sorted set
+     * @param mixed  $member        The first member to return the score from
+     * @param mixed  $other_members One or more additional members to return the scores of.
+     *
+     * @return Redis|array|false An array of the scores of the requested elements.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     *
+     * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+     *
+     * // array(2) {
+     * //   [0]=>
+     * //   float(0)
+     * //   [1]=>
+     * //   float(2)
+     * // }
+     * $redis->zMScore('zs', 'zero', 'two');
+     *
+     * // array(2) {
+     * //   [0]=>
+     * //   float(1)
+     * //   [1]=>
+     * //   bool(false)
+     * // }
+     * $redis->zMScore('zs', 'one', 'not-a-member');
+     * ?>
+     * 
+     */
+    public function zMscore(string $key, mixed $member, mixed ...$other_members): Redis|array|false;
 
-    public function zPopMax(string $key, int $value = null): Redis|array|false;
+    /**
+     * Pop one or more of the highest scoring elements from a sorted set.
+     *
+     * @see https://redis.io/commands/zpopmax
+     *
+     * @param string $key   The sorted set to pop elements from.
+     * @param int    $count An optional count of elements to pop.
+     *
+     * @return Redis|array|false All of the popped elements with scores or false on fialure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+     *
+     * // Array
+     * // (
+     * //     [three] => 3
+     * // )
+     * print_r($redis->zPopMax('zs'));
+     *
+     * // Array
+     * // (
+     * //     [two] => 2
+     * //     [one] => 1
+     * // )
+     * print_r($redis->zPopMax('zs', 2));
+     * ?>
+     * 
+     */
+    public function zPopMax(string $key, int $count = null): Redis|array|false;
 
-    public function zPopMin(string $key, int $value = null): Redis|array|false;
+    /**
+     * Pop one or more of the lowest scoring elements from a sorted set.
+     *
+     * @see https://redis.io/commands/zpopmin
+     *
+     * @param string $key   The sorted set to pop elements from.
+     * @param int    $count An optional count of elements to pop.
+     *
+     * @return Redis|array|false The popped elements with their scores or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+     *
+     * // Array
+     * // (
+     * //     [zero] => 0
+     * // )
+     * $redis->zPopMin('zs');
+     *
+     * // Array
+     * // (
+     * //     [one] => 1
+     * //     [two] => 2
+     * // )
+     * $redis->zPopMin('zs', 2);
+     * ?>
+     * 
+     */
+    public function zPopMin(string $key, int $count = null): Redis|array|false;
 
     /**
      * Retrieve a range of elements of a sorted set between a start and end point.
@@ -2402,6 +2607,44 @@ public function zPopMin(string $key, int $value = null): Redis|array|false;
      */
     public function zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null): Redis|array|false;
 
+    /**
+     * Retrieve a range of elements from a sorted set by legographical range.
+     *
+     * @see https://redis.io/commands/zrangebylex
+     *
+     * @param string $key    The sorted set to retreive elements from
+     * @param string $min    The minimum legographical value to return
+     * @param string $max    The maximum legographical value to return
+     * @param int    $offset An optional offset within the matching values to return
+     * @param int    $count  An optional count to limit the replies to (used in conjunction with offset)
+     *
+     * @return Redis|array|false An array of matching elements or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('captains');
+     * $redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
+     *
+     * // Array
+     * // (
+     * //     [0] => Archer
+     * //     [1] => Janeway
+     * //     [2] => Kirk
+     * //     [3] => Picard
+     * // )
+     * $redis->zRangeByLex('captains', '[A', '[S');
+     *
+     * // Array
+     * // (
+     * //     [0] => Kirk
+     * //     [1] => Picard
+     * // )
+     * $redis->zRangeByLex('captains', '[A', '[S', 2, 2);
+     * ?>
+     * 
+     */
     public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false;
 
     /**
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 3b2fe5a2e3..511c5dfe65 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 08c0be22623f22153c7df080cfb93388b73fa259 */
+ * Stub hash: 55e15f9e5c33b941552643c0302becdc3241212e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -996,11 +996,15 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zLexCount, 0, 3,
 	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zMscore arginfo_class_Redis_geohash
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zMscore, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zPopMax, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "null")
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index a5700322e3..8ea1a2b21e 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 08c0be22623f22153c7df080cfb93388b73fa259 */
+ * Stub hash: 55e15f9e5c33b941552643c0302becdc3241212e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -862,12 +862,9 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zMscore arginfo_class_Redis_geohash
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zPopMax, 0, 0, 1)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_zPopMax arginfo_class_Redis_lPop
 
-#define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax
+#define arginfo_class_Redis_zPopMin arginfo_class_Redis_lPop
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)

From bb06ffa38010ae3c3066ae22702bb2d14f61bb13 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Nov 2022 16:29:40 -0700
Subject: [PATCH 1693/1986] Documentation:  Even more docblocks

---
 redis.stub.php         | 188 +++++++++++++++++++++++++++++++++++++++--
 redis_arginfo.h        |  14 +--
 redis_legacy_arginfo.h |   8 +-
 3 files changed, 191 insertions(+), 19 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index eab463c4dd..ed9c415eeb 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1589,6 +1589,28 @@ public function save(): Redis|bool;
      */
     public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, string $type = NULL): array|false;
 
+    /**
+     * Retrieve the number of members in a Redis set.
+     *
+     * @see https://redis.io/commands/scard
+     *
+     * @param string $key The set to get the cardinality of.
+     *
+     * @return Redis|int|false The cardinality of the set or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('set');
+     * $redis->sadd('set', 'one', 'two', 'three', 'four', 'five');
+     *
+     * // Returns 5
+     * $redis->scard('set');
+     * ?>
+     * 
+     */
     public function scard(string $key): Redis|int|false;
 
     public function script(string $command, mixed ...$args): mixed;
@@ -1664,16 +1686,61 @@ public function select(int $db): Redis|bool;
      */
     public function set(string $key, mixed $value, mixed $options = NULL): Redis|string|bool;
 
-    /** @return Redis|int|false*/
-    public function setBit(string $key, int $idx, bool $value);
+    /**
+     * Set a specific bit in a Redis string to zero or one
+     *
+     * @see https://redis.io/commands/setbit
+     *
+     * @param string $key    The Redis STRING key to modify
+     * @param bool   $value  Whether to set the bit to zero or one.
+     *
+     * @return Redis|int|false The original value of the bit or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('foo', 'bar');
+     *
+     * // Flip the 7th bit to 1
+     * $redis->setbit('foo', 7, 1);
+     *
+     * // The bit flip turned 'bar' -> 'car'
+     * $redis->get('foo');
+     * ?>
+     * 
+     */
+    public function setBit(string $key, int $idx, bool $value): Redis|int|false;
 
-    /** @return Redis|int|false*/
-    public function setRange(string $key, int $start, string $value);
+    /**
+     * Update or append to a Redis string at a specific starting index
+     *
+     * @see https://redis.io/commands/setrange
+     *
+     * @param string $key    The key to update
+     * @param int    $index  Where to insert the provided value
+     * @param string $value  The value to copy into the string.
+     *
+     * @return Redis|int|false The new length of the string or false on failure
+     *
+     * 
+     *  'localhost']);
+
+     * $redis->set('message', 'Hello World');
 
+     * // Update 'Hello World' to 'Hello Redis'
+     * $redis->setRange('message', 6, 'Redis');
+     * ?>
+     * 
+     */
+    public function setRange(string $key, int $index, string $value): Redis|int|false;
 
     /**
      * Set a configurable option on the Redis object.
      *
+     * @see Redis::getOption()
+     *
      * Following are a list of options you can set:
      *
      *  OPTION                     TYPE     DESCRIPTION
@@ -1773,8 +1840,33 @@ public function setOption(int $option, mixed $value): bool;
      */
     public function setex(string $key, int $expire, mixed $value);
 
-    /** @return bool|array|Redis */
-    public function setnx(string $key, mixed $value);
+    /**
+     * Set a key to a value, but only if that key does not already exist.
+     *
+     * @see https://redis.io/commands/setnx
+     *
+     * @param string $key   The key name to set.
+     * @param mixed  $value What to set the key to.
+     *
+     * @return Redis|bool Returns true if the key was set and false otherwise.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('new-key');
+     * $redis->set('existing-key', 'already-exists');
+     *
+     * // Key is new, returns 1
+     * $redis->setnx('key1', 'here-is-a-new-key');
+     *
+     * // Key exists, returns 0
+     * $redis->setnx('existing-key', 'new-value');
+     * ?>
+     * 
+     *
+     */
+    public function setnx(string $key, mixed $value): Redis|bool;
 
     /**
      * Check whether a given value is the member of a Redis SET.
@@ -2335,6 +2427,29 @@ public function xgroup(string $operation, string $key = null, string $group = nu
 
     public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
 
+
+    /**
+     * Get the number of messages in a Redis STREAM key.
+     *
+     * @see https://redis.io/commands/xlen
+     *
+     * @param string $key The Stream to check.
+     *
+     * @return Redis|int|false The number of messages or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('stream');
+     * $redis->xadd('stream', '*', ['first' => 'message']);
+     * $redis->xadd('stream', '*', ['second' => 'message']);
+     *
+     * // int(2)
+     * $redis->xLen('stream');
+     * ?>
+     * 
+     */
     public function xlen(string $key): Redis|int|false;
 
     public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false;
@@ -2349,6 +2464,67 @@ public function xrevrange(string $key, string $start, string $end, int $count =
 
     public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): Redis|int|false;
 
+    /**
+     * Add one or more elements and scores to a Redis sorted set.
+     *
+     * @see https://redis.io/commands/zadd
+     *
+     * @param string       $key                  The sorted set in question.
+     * @param array|float  $score_or_options     Either the score for the first element, or an array
+     *                                           containing one or more options for the operation.
+     * @param mixed        $more_scores_and_mems A variadic number of additional scores and members.
+     *
+     * Following is information about the options that may be passed as the scond argument:
+     *
+     * 
+     * $options = [
+     *     'NX',       # Only update elements that already exist
+     *     'NX',       # Only add new elements but don't update existing ones.
+     *
+     *     'LT'        # Only update existing elements if the new score is less than the existing one.
+     *     'GT'        # Only update existing elements if the new score is greater than the existing one.
+     *
+     *     'CH'        # Instead of returning the number of elements added, Redis will return the number
+     *                 # Of elements that were changed in the operation.
+     *
+     *     'INCR'      # Instead of setting each element to the provide score, increment the elemnt by the
+     *                 # provided score, much like ZINCRBY.  When this option is passed, you may only
+     *                 # send a single score and member.
+     * ];
+     *
+     * Note:  'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis will send whichever one is last in
+     *        the options array.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     *
+     * // Add three new elements to our zset
+     * $redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third');
+     *
+     * // Array
+     * // (
+     * //     [first] => 1
+     * //     [second] => 2
+     * //     [third] => 3
+     * // )
+     * $redis->zRange('zs', 0, -1, true);
+     *
+     * // Update only existing elements.  Note that 'new-element' isn't added
+     * $redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element');
+     *
+     * // Array
+     * // (
+     * //     [first] => 1
+     * //     [third] => 3
+     * //     [second] => 8
+     * // )
+     * print_r($redis->zRange('zs', 0, -1, true));
+     * ?>
+     * 
+     */
     public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|false;
 
     /**
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 511c5dfe65..1821db2a0d 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 55e15f9e5c33b941552643c0302becdc3241212e */
+ * Stub hash: 357d950a0dd1960a29c514c47385a0d9a5e422b2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -762,15 +762,15 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_set, 0, 2, Redis
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setBit, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, _IS_BOOL, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setRange, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
@@ -781,13 +781,13 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_setex arginfo_class_Redis_psetex
 
-#define arginfo_class_Redis_setnx arginfo_class_Redis_lPushx
-
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sismember, 0, 2, Redis, MAY_BE_BOOL)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setnx, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
+#define arginfo_class_Redis_sismember arginfo_class_Redis_setnx
+
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_slaveof, 0, 0, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "NULL")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 8ea1a2b21e..9effc832f9 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 55e15f9e5c33b941552643c0302becdc3241212e */
+ * Stub hash: 357d950a0dd1960a29c514c47385a0d9a5e422b2 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -651,11 +651,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setBit, 0, 0, 3)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setRange, 0, 0, 3)
-	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, start)
-	ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_setRange arginfo_class_Redis_lSet
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setOption, 0, 0, 2)
 	ZEND_ARG_INFO(0, option)

From b8679d7af1c7337f0132bfd60924846412b03081 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 1 Nov 2022 19:09:29 -0700
Subject: [PATCH 1694/1986] Documentation:  Several ZSET docblocks

---
 redis.stub.php         | 480 ++++++++++++++++++++++++++++++++++++++++-
 redis_arginfo.h        |  25 ++-
 redis_legacy_arginfo.h |  18 +-
 3 files changed, 506 insertions(+), 17 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index ed9c415eeb..d08af5e5dd 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1968,8 +1968,8 @@ public function touch(array|string $key_or_array, string ...$more_keys): Redis|i
      *                           'RESET' - Remove all slowlog entries.
      * 
      * slowlog('get', -1);  // Retreive all slowlog entries.
-     * $redis->slowlog('len');       // Retreive slowlog length.
+     * $redis->slowlog('get', -1);  // Retrieve all slowlog entries.
+     * $redis->slowlog('len');       // Retrieve slowlog length.
      * $redis->slowlog('reset');     // Reset the slowlog.
      * ?>
      * 
@@ -2640,7 +2640,7 @@ public function zIncrBy(string $key, float $value, mixed $member): Redis|float|f
     public function zLexCount(string $key, string $min, string $max): Redis|int|false;
 
     /**
-     * Retreive the score of one or more members in a sorted set.
+     * Retrieve the score of one or more members in a sorted set.
      *
      * @see https://redis.io/commands/zmscore
      *
@@ -3044,34 +3044,498 @@ public function zRem(mixed $key, mixed $member, mixed ...$other_members): Redis|
      */
     public function zRemRangeByLex(string $key, string $min, string $max): Redis|int|false;
 
+    /**
+     * Remove one or more members of a sorted set by their rank.
+     *
+     * @see https://redis.io/commands/zremrangebyrank
+     *
+     * @param string $key    The sorted set where we wnat to remove members.
+     * @param int    $start  The rank when we want to start removing members
+     * @param int    $end    The rank we want to stop removing membersk.
+     *
+     * @return Redis|int|false The number of members removed from the set or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     * $redis->zAdd('zs', 0, 'zeroth', 1, 'first', 2, 'second', 3, 'third', 4, 'fourth');
+     *
+     * // Remove ranks 0..3
+     * $redis->zRemRangeByRank('zs', 0, 3);
+     *
+     * // Array
+     * // (
+     * //     [0] => fourth
+     * // )
+     * $redis->zRange('zs', 0, -1);
+     * ?>
+     * 
+     */
     public function zRemRangeByRank(string $key, int $start, int $end): Redis|int|false;
 
+    /**
+     * Remove one or more members of a sorted set by their score.
+     *
+     * @see https://redis.io/commands/zremrangebyrank
+     *
+     * @param string $key    The sorted set where we wnat to remove members.
+     * @param int    $start  The lowest score to remove.
+     * @param int    $end    The highest score to remove.
+     *
+     * @return Redis|int|false The number of members removed from the set or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs');
+     * $redis->zAdd('zs', 3, 'three', 5, 'five', 7, 'seven', 7, 'seven-again', 13, 'thirteen', 22, 'twenty-two');
+     *
+     * // Removes every member with scores >= 7 and scores <= 13.
+     * $redis->zRemRangeByScore('zs', 7, 13);
+     *
+     * // Array
+     * // (
+     * //     [0] => three
+     * //     [1] => five
+     * //     [2] => twenty-two
+     * // )
+     * $redis->zRange('zs', 0, -1);
+     * ?>
+     * 
+     */
     public function zRemRangeByScore(string $key, string $start, string $end): Redis|int|false;
 
+    /**
+     * List the members of a Redis sorted set in reverse order
+     *
+     * @param string $key        The sorted set in question.
+     * @param int    $start      The index to start listing elements
+     * @param int    $end        The index to stop listing elements.
+     * @param mixed  $withscores Whether or not Redis should also return each members score.  See
+     *                           the example below demonstrating how it may be used.
+     *
+     * @return Redis|array|false The members (and possibly scores) of the matching elements or false
+     *                           on failure.
+     *
+     * $redis = new Redis(['host' => 'localhost']);
+     *
+     * $redis->del('zs');
+     * $redis->zAdd('zs', 1, 'one', 2, 'two', 5, 'five', 10, 'ten');
+     *
+     * // Array
+     * // (
+     * //     [0] => ten
+     * //     [1] => five
+     * //     [2] => two
+     * //     [3] => one
+     * // )
+     * print_r($redis->zRevRange('zs', 0, -1));
+     *
+     * // Array
+     * // (
+     * //     [0] => two
+     * //     [1] => one
+     * // )
+     * print_r($redis->zRevRange('zs', 2, 3));
+     *
+     * // Additionally, you may pass `true` or `['withscores' => true]` to tell redis to return scores
+     * // as well as members.
+     * $redis->zRevRange('zs', 0, -1, true);
+     * $redis->zRevRange('zs', 0, -1, ['withscores' => true]);
+     * ?>
+     * 
+     */
     public function zRevRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false;
 
-    public function zRevRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false;
-
-    public function zRevRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false;
+    /**
+     * List members of a Redis sorted set within a legographical range, in reverse order.
+     *
+     * @see https://redis.io/commands/zrevrangebylex
+     * @see Redis::zrangebylex()
+     *
+     * @param string $key    The sorted set to list
+     * @param string $min    The maximum legographical element to include in the result.
+     * @param string $min    The minimum lexographical element to include in the result.
+     * @param string $offset An option offset within the matching elements to start at.
+     * @param string $count  An optional count to limit the replies to.
+     *
+     * @return Redis|array|false The matching members or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('captains');
+     * $redis->zAdd('captains', 0, 'Janeway', 0, 'Picard', 0, 'Kirk', 0, 'Archer');
+     *
+     * // Array
+     * // (
+     * //     [0] => Picard
+     * //     [1] => Kirk
+     * //     [2] => Janeway
+     * // )
+     * $redis->zRevRangeByLex('captains', '[Q', '[J');
+     *
+     * // Array
+     * // (
+     * //     [0] => Kirk
+     * //     [1] => Janeway
+     * // )
+     * $redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2);
+     * ?>
+     * 
+     */
+    public function zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1): Redis|array|false;
 
+    /**
+     * List elements from a Redis sorted set by score, highest to lowest
+     *
+     * @param string $key     The sorted set to query.
+     * @param string $max     The highest score to include in the results.
+     * @param string $min     The lowest score to include in the results.
+     * @param array  $options An options array that modifies how the command executes.
+     *
+     *                        
+     *                        $options = [
+     *                            'WITHSCORES' => true|false # Whether or not to return scores
+     *                            'LIMIT' => [offset, count] # Return a subset of the matching members
+     *                        ];
+     *                        
+     *
+     *                        NOTE:  For legacy reason, you may also simply pass `true` for the
+     *                               options argument, to mean `WITHSCORES`.
+     *
+     * @return Redis|array|false The matching members in reverse order of score or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('oldest-people');
+     *
+     * $redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka',
+     *                               119.2658, 'Sarah Knauss',   118.7205, 'Lucile Randon',
+     *                               117.7123, 'Nabi Tajima',    117.6301, 'Marie-Louise Meilleur',
+     *                               117.5178, 'Violet Brown',   117.3753, 'Emma Morano',
+     *                               117.2219, 'Chiyo Miyako',   117.0740, 'Misao Okawa');
+     *
+     * // Array
+     * // (
+     * //     [0] => Kane Tanaka
+     * //     [1] => Sarah Knauss
+     * // )
+     * $redis->zRevRangeByScore('oldest-people', 122, 119);
+     *
+     * //Array
+     * //(
+     * //    [0] => Jeanne Calment
+     * //    [1] => Kane Tanaka
+     * //    [2] => Sarah Knauss
+     * //    [3] => Lucile Randon
+     * //)
+     * $redis->zRevRangeByScore('oldest-people', 'inf', 118);
+     *
+     * // Array
+     * // (
+     * //     [0] => Emma Morano
+     * // )
+     * $redis->zRevRangeByScore('oldest-people', '117.5', '-inf', ['LIMIT' => [0, 1]]);
+     * ?>
+     * 
+     *
+     */
+    public function zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []): Redis|array|false;
+
+    /**
+    * Retrieve a member of a sorted set by reverse rank.
+    *
+    * @see https://redis.io/commands/zrevrank
+    *
+    * @param string $key      The sorted set to query.
+    * @param mixed  $member   The member to look up.
+    *
+    * @return Redis|int|false The reverse rank (the rank if counted high to low) of the member or
+    *                         false on failure.
+    *
+    * 
+    *  'localhost']);
+    *
+    * $redis->del('ds9-characters');
+    *
+    * $redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo');
+    *
+    * // Highest score, reverse rank 0
+    * $redis->zrevrank('ds9-characters', 'Sisko');
+    *
+    * // Second highest score, reverse rank 1
+    * $redis->zrevrank('ds9-characters', 'Garak');
+    * ?>
+    * 
+    */
     public function zRevRank(string $key, mixed $member): Redis|int|false;
 
+    /**
+     * Get the score of a member of a sorted set.
+     *
+     * @see https://redis.io/commands/zscore
+     *
+     * @param string $key    The sorted set to query.
+     * @param mixed  $member The member we wish to query.
+     *
+     * @return The score of the requested element or false if it is not found.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('telescopes');
+     *
+     * $redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET');
+     *
+     * foreach ($redis->zRange('telescopes', 0, -1) as $name) {
+     *     // Get the score for this member
+     *     $aperature = $redis->zScore('telescopes', $name);
+     *
+     *     echo "The '$name' telescope has an effective aperature of: $aperature meters\n";
+     * }
+     * ?>
+     * 
+     */
     public function zScore(string $key, mixed $member): Redis|float|false;
 
+    /**
+     * Given one or more sorted set key names, return every element that is in the first
+     * set but not any of the others.
+     *
+     * @see https://redis.io/commands/zdiff
+     *
+     * @param array $keys    One ore more sorted sets.
+     * @param array $options An array which can contain ['WITHSCORES' => true] if you want Redis to
+     *                       return members and scores.
+     *
+     * @return Redis|array|false An array of members or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('primes', 'evens', 'mod3');
+     *
+     * $redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five');
+     * $redis->zAdd('evens', 2, 'two', 4, 'four');
+     * $redis->zAdd('mod3', 3, 'three', 6, 'six');
+     *
+     * // Array
+     * // (
+     * //     [0] => one
+     * //     [1] => five
+     * // )
+     * print_r($redis->zDiff(['primes', 'evens', 'mod3']));
+     * ?>
+     * 
+     *
+     */
     public function zdiff(array $keys, array $options = null): Redis|array|false;
 
-    public function zdiffstore(string $dst, array $keys, array $options = null): Redis|int|false;
+    /**
+     * Store the difference of one or more sorted sets in a destination sorted set.
+     *
+     * @see https://redis.io/commands/zdiff
+     * @see Redis::zdiff()
+     *
+     * @param string $key  The destination set name.
+     * @param array  $keys One or more source key names
+     *
+     * @return Redis|int|false The number of elements stored in the destination set or false on
+     *                         failure.
+     *
+     * NOTE:  See Redis::zdiff() for a more detailed description of how the diff operation works.
+     *
+     */
+    public function zdiffstore(string $dst, array $keys): Redis|int|false;
+
+    /**
+     * Compute the intersection of one or more sorted sets and return the members
+     *
+     * @param array $keys    One ore more sorted sets.
+     * @param array $weights An optional array of weights to be applied to each set when performing
+     *                       the intersection.
+     * @param array $options Options for how Redis should combine duplicate elements when performing the
+     *                       intersection.  See Redis::zunion() for details.
+     *
+     * @return Redis|array|false All of the members that exist in every set.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('tng', 'ds9');
+     *
+     * $redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard');
+     * $redis->zAdd('DS9', 2.5, 'Worf', 3.0, 'Kira', 4.0, 'Sisko');
+     *
+     * // Array
+     * // (
+     * //     [0] => Worf
+     * // )
+     * $redis->zInter(['TNG', 'DS9']);
+     *
+     * // Array
+     * // (
+     * //     [Worf] => 4.5
+     * // )
+     * $redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true]);
+     *
+     * // Array
+     * // (
+     * //     [Worf] => 2.5
+     * // )
+     * $redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true, 'aggregate' => 'max']);
+     *
+     * ?>
+     * 
+     *
+     */
     public function zinter(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false;
 
     public function zintercard(array $keys, int $limit = -1): Redis|int|false;
 
     public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false;
 
-    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|bool|array;
+    /**
+     * Scan the members of a sorted set incrementally, using a cursor
+     *
+     * @see https://redis.io/commands/zscan
+     * @see https://redis.io/commands/scan
+     * @see Redis::scan()
+     *
+     * @param string $key        The sorted set to scan.
+     * @param int    $iterator   A reference to an iterator that should be initialized to NULL initially, that
+     *                           will be updated after each subsequent call to ZSCAN.  Once the iterator
+     *                           has returned to zero the scan is complete
+     * @param string $pattern    An optional glob-style pattern that limits which members are returned during
+     *                           the scanning process.
+     * @param int    $count      A hint for Redis that tells it how many elements it should test before returning
+     *                           from the call.  The higher the more work Redis may do in any one given call to
+     *                           ZSCAN potentially blocking for longer periods of time.
+     *
+     * @return Redis|array|false An array of elements or false on failure.
+     *
+     * NOTE:  See Redis::scan() for detailed example code on how to call SCAN like commands.
+     *
+     */
+    public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|false;
 
+    /**
+     * Retrieve the union of one or more sorted sets
+     *
+     * @param array $keys     One ore more sorted set key names
+     * @param array $weights  An optional array with floating point weights used when performing the union.
+     *                        Note that if this argument is passed, it must contain the same number of
+     *                        elements as the $keys array.
+     * @param array $options  An array that modifies how this command functions.
+     *
+     *                        
+     *                        $options = [
+     *                            // By default when members exist in more than one set Redis will SUM
+     *                            // total score for each match.  Instead, it can return the AVG, MIN,
+     *                            // or MAX value based on this option.
+     *                            'AGGREGATE' => 'sum' | 'min' | 'max'
+     *
+     *                            // Whether Redis should also return each members aggregated score.
+     *                            'WITHSCORES' => true | false
+     *                        ]
+     *                        
+     *
+     * @return Redis|array|false The union of each sorted set or false on failure
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('store1', 'store2', 'store3');
+     * $redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas');
+     * $redis->zAdd('store2', 3, 'apples', 5, 'coconuts', 2, 'bananas');
+     * $redis->zAdd('store3', 2, 'bananas', 6, 'apples', 4, 'figs');
+     *
+     * // Array
+     * // (
+     * //     [pears] => 3
+     * //     [figs] => 4
+     * //     [coconuts] => 5
+     * //     [apples] => 10
+     * //     [bananas] => 10
+     * // )
+     * $redis->zUnion(['store1', 'store2', 'store3'], NULL, ['withscores' => true]);
+     *
+     * // Array
+     * // (
+     * //     [figs] => 2
+     * //     [apples] => 5
+     * //     [pears] => 6
+     * //     [bananas] => 13
+     * // )
+     * $redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true]);
+     *
+     * // Array
+     * // (
+     * //     [bananas] => 1
+     * //     [apples] => 2
+     * //     [figs] => 2
+     * //     [pears] => 6
+     * // )
+     * $redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true, 'aggregate' => 'MIN']);
+     * ?>
+     * 
+     */
     public function zunion(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false;
 
+    /**
+     * Perform a union on one or more Redis sets and store the result in a destination sorted set.
+     *
+     * @see https://redis.io/commands/zunionstore
+     * @see Redis::zunion()
+     *
+     * @param string $dst       The destination set to store the union.
+     * @param array  $keys      One or more input keys on which to perform our union.
+     * @param array  $weights   An optional weights array used to weight each input set.
+     * @param string $aggregate An optional modifier in how Redis will combine duplicate members.
+     *                          Valid:  'MIN', 'MAX', 'SUM'.
+     *
+     * @return Redis|int|false The number of members stored in the destination set or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs1', 'zs2', 'zs3');
+     *
+     * $redis->zAdd('zs1', 1, 'one', 3, 'three');
+     * $redis->zAdd('zs1', 2, 'two', 4, 'four');
+     * $redis->zadd('zs3', 1, 'one', 7, 'five');
+     *
+     * // count(['one','two','three','four','five']) == 5
+     * $redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']);
+     *
+     * // Array
+     * // (
+     * //     [0] => one
+     * //     [1] => two
+     * //     [2] => three
+     * //     [3] => four
+     * //     [4] => five
+     * // )
+     * $redis->zRange('dst', 0, -1);
+     * ?>
+     * 
+     *
+     */
     public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): Redis|int|false;
 }
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 1821db2a0d..906afe7f8a 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 357d950a0dd1960a29c514c47385a0d9a5e422b2 */
+ * Stub hash: 84c333ece3425d10378996ae8b76ec57ced64025 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -1069,9 +1069,20 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRange, 0, 3,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scores, IS_MIXED, 0, "null")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRangeByLex, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "-1")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRangeByScore
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRangeByScore, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0)
+	ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL, "[]")
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRevRank arginfo_class_Redis_zRank
 
@@ -1088,7 +1099,6 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiffstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zinter, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
@@ -1106,7 +1116,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zinterstore, 0,
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zscan arginfo_class_Redis_hscan
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zunion arginfo_class_Redis_zinter
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 9effc832f9..e19fb8b6a0 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 357d950a0dd1960a29c514c47385a0d9a5e422b2 */
+ * Stub hash: 84c333ece3425d10378996ae8b76ec57ced64025 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -906,9 +906,20 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRange, 0, 0, 3)
 	ZEND_ARG_INFO(0, scores)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRangeByLex, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, max)
+	ZEND_ARG_INFO(0, min)
+	ZEND_ARG_INFO(0, offset)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRange
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRangeByScore, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, max)
+	ZEND_ARG_INFO(0, min)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRevRank arginfo_class_Redis_hExists
 
@@ -922,7 +933,6 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zdiffstore, 0, 0, 2)
 	ZEND_ARG_INFO(0, dst)
 	ZEND_ARG_INFO(0, keys)
-	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zinter, 0, 0, 1)

From 854f3aa4262b43e0ea5a27b2eb394b882db06f0a Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 3 Nov 2022 14:10:18 -0700
Subject: [PATCH 1695/1986] Documentation:  Stream doc blocks and xtrim fix

- XTRIM needs to take the `$threshold` arg as a string since MINID is
  not a number.
---
 redis.stub.php         | 293 ++++++++++++++++++++++++++++++++++++++++-
 redis_arginfo.h        |  11 +-
 redis_commands.c       |   8 +-
 redis_legacy_arginfo.h |  11 +-
 4 files changed, 311 insertions(+), 12 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index d08af5e5dd..4f62d731fb 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -2454,15 +2454,229 @@ public function xlen(string $key): Redis|int|false;
 
     public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false;
 
+    /**
+     * Get a range of entries from a STREAM key.
+     *
+     * @see https://redis.io/commands/xrange
+     *
+     * @param string $key   The stream key name to list.
+     * @param string $start The minimum ID to return.
+     * @param string $end   The maximum ID to return.
+     * @param int    $count An optional maximum number of entries to return.
+     *
+     * @return Redis|array|bool The entries in the stream within the requested range or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('stream');
+     *
+     * for ($i = 0; $i < 2; $i++) {
+     *     for ($j = 1; $j <= 2; $j++) {
+     *         $redis->xAdd('stream', "$i-$j", ['message' => "$i:$j"]);
+     *     }
+     * }
+     *
+     * //Array
+     * //(
+     * //    [0-1] => Array
+     * //        (
+     * //            [message] => 0:1
+     * //        )
+     * //
+     * //    [0-2] => Array
+     * //        (
+     * //            [message] => 0:2
+     * //        )
+     * //
+     * //)
+     * $redis->xRange('stream', '0-1', '0-2');
+     *
+     * // '-' and '+' are special values which mean 'minimum possible',
+     * // and 'maximum possible' id, respectively.
+     * $redis->xRange('stream', '-', '+');
+     * ?>
+     * 
+     */
     public function xrange(string $key, string $start, string $end, int $count = -1): Redis|array|bool;
 
+    /**
+     * Consume one or more unconsumed elements in one or more streams.
+     *
+     * @see https://redis.io/commands/xread
+     *
+     * @param array $streams An associative array with stream name keys and minimum id values.
+     * @param int   $count   An optional limit to how many entries are returnd *per stream*
+     * @param int   $block   An optional maximum number of milliseconds to block the caller if no
+     *                       data is available on any of the provided streams.
+     *
+     * 
+     * $redis = new Redis(['host' => 'localhost']);
+     *
+     * $redis->del('s03', 's03');
+     *
+     * $redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']);
+     * $redis->xAdd('s03', '3-2', ['title' => 'The Search, Part II']);
+     * $redis->xAdd('s03', '3-3', ['title' => 'The House Of Quark']);
+     *
+     * $redis->xAdd('s04', '4-1', ['title' => 'The Way of the Warrior']);
+     * $redis->xAdd('s04', '4-3', ['title' => 'The Visitor']);
+     * $redis->xAdd('s04', '4-4', ['title' => 'Hippocratic Oath']);
+     *
+     * // Array
+     * // (
+     * //     [s03] => Array
+     * //         (
+     * //             [3-3] => Array
+     * //                 (
+     * //                     [title] => The House Of Quark
+     * //                 )
+     * //
+     * //         )
+     * //
+     * //     [s04] => Array
+     * //         (
+     * //             [4-3] => Array
+     * //                 (
+     * //                     [title] => The Visitor
+     * //                 )
+     * //
+     * //             [4-4] => Array
+     * //                 (
+     * //                     [title] => Hippocratic Oath
+     * //                 )
+     * //
+     * //         )
+     * //
+     * // )
+     * print_r($redis->xRead(['s03' => '3-2', 's04' => '4-1']));
+     * 
+     */
     public function xread(array $streams, int $count = -1, int $block = -1): Redis|array|bool;
 
     public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): Redis|array|bool;
 
-    public function xrevrange(string $key, string $start, string $end, int $count = -1): Redis|array|bool;
+    /**
+     * Get a range of entries from a STREAM ke in reverse cronological order.
+     *
+     * @see https://redis.io/commands/xrevrange
+     * @see https://redis.io/commands/xrange
+     *
+     * @param string $key   The stream key to query.
+     * @param string $end   The maximum message ID to include.
+     * @param string $start The minimum message ID to include.
+     * @param int    $count An optional maximum number of messages to include.
+     *
+     * @return Redis|array|bool The entries within the requested range, from newest to oldest.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('stream');
+     *
+     * for ($i = 0; $i < 2; $i++) {
+     *     for ($j = 1; $j <= 2; $j++) {
+     *         $redis->xAdd('stream', "$i-$j", ['message' => "$i:$j"]);
+     *     }
+     * }
+     *
+     * // Array
+     * // (
+     * //     [0-2] => Array
+     * //         (
+     * //             [message] => 0:2
+     * //         )
+     * //
+     * //     [0-1] => Array
+     * //         (
+     * //             [message] => 0:1
+     * //         )
+     * //
+     * // )
+     * $redis->xRevRange('stream', '0-2', '0-1');
+     *
+     * // '-' and '+' are special values which mean 'minimum possible',
+     * // and 'maximum possible' id, respectively.
+     * $redis->xRevRange('stream', '+', '-');
+     * ?>
+     * 
+     *
+     */
+    public function xrevrange(string $key, string $end, string $start, int $count = -1): Redis|array|bool;
 
-    public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): Redis|int|false;
+    /**
+     * Truncate a STREAM key in various ways.
+     *
+     * @see https://redis.io/commands/xtrim
+     *
+     * @param string $key       The STREAM key to trim.
+     * @param string $threshold This can either be a maximum length, or a minimum id.
+     *                          MAXLEN - An integer describing the maximum desired length of the stream after the command.
+     *                          MINID  - An ID that will become the new minimum ID in the stream, as Redis will trim all
+     *                                   messages older than this ID.
+     * @param bool   $approx    Whether redis is allowed to do an approximate trimming of the stream.  This is
+     *                          more efficient for Redis given how streams are stored internally.
+     * @param int    $count     An optional upper bound on how many entries Redis should attempt to trim before
+     *                          returning to the caller.
+     * @param bool   $minid     When set to `true`, users should pass a minimum ID to the `$threshold` argument.
+     * @param int    $limit     An optional upper bound on how many entries to trim during the command.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('stream');
+     * $redis->xAdd('stream', '1-1', ['one' => 'one']);
+     * $redis->xAdd('stream', '1-2', ['one' => 'two']);
+     * $redis->xAdd('stream', '2-1', ['two' => 'one']);
+     * $redis->xAdd('stream', '2-2', ['two' => 'two']);
+     *
+     * // Trim to three elemn
+     * $redis->xTrim('stream', 3);
+     *
+     * // Array
+     * // (
+     * //     [1-2] => Array
+     * //         (
+     * //             [one] => two
+     * //         )
+     * //
+     * //     [2-1] => Array
+     * //         (
+     * //             [two] => one
+     * //         )
+     * //
+     * //     [2-2] => Array
+     * //         (
+     * //             [two] => two
+     * //         )
+     * //
+     * // )
+     * $redis->xRange('stream', '-', '+');
+     *
+     * // Now let's trim everything older than '2-1'
+     * $redis->xTrim('stream', '2-1', false, true);
+     *
+     * // Array
+     * // (
+     * //     [2-1] => Array
+     * //         (
+     * //             [two] => one
+     * //         )
+     * //
+     * //     [2-2] => Array
+     * //         (
+     * //             [two] => two
+     * //         )
+     * //
+     * // )
+     * print_r($redis->xRange('stream', '-', '+'));
+     * ?>
+     * 
+     */
+    public function xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1): Redis|int|false;
 
     /**
      * Add one or more elements and scores to a Redis sorted set.
@@ -3362,6 +3576,8 @@ public function zdiffstore(string $dst, array $keys): Redis|int|false;
     /**
      * Compute the intersection of one or more sorted sets and return the members
      *
+     * @see https://redis.io/commands/zinter
+     *
      * @param array $keys    One ore more sorted sets.
      * @param array $weights An optional array of weights to be applied to each set when performing
      *                       the intersection.
@@ -3404,8 +3620,81 @@ public function zdiffstore(string $dst, array $keys): Redis|int|false;
      */
     public function zinter(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false;
 
+    /**
+     * Similar to ZINTER but instead of returning the intersected values, this command returns the
+     * cardinality of the intersected set.
+     *
+     * @see https://redis.io/commands/zintercard
+     * @see https://redis.io/commands/zinter
+     * @see Redis::zinter()
+     *
+     * @param array $keys   One ore more sorted set key names.
+     * @param int   $limit  An optional upper bound on the returned cardinality.  If set to a value
+     *                      greater than zero, Redis will stop processing the intersection once the
+     *                      resulting cardinality reaches this limit.
+     *
+     * @return Redis|int|false The cardinality of the intersection or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs1', 'zs2');
+     *
+     * $redis->zAdd('zs1', 1, 'one', 2, 'two', 3, 'three', 4, 'four');
+     * $redis->zAdd('zs2', 2, 'two', 4, 'four');
+     *
+     * // count(['two', 'four']) == 2
+     * $redis->zInterCard(['zs1', 'zs2']);
+     * ?>
+     * 
+     */
     public function zintercard(array $keys, int $limit = -1): Redis|int|false;
 
+    /**
+     * Compute the intersection of one ore more sorted sets storing the result in a new sorted set.
+     *
+     * @see https://redis.io/commands/zinterstore
+     * @see https://redis.io/commands/zinter
+     *
+     * @param string $dst       The destination sorted set to store the intersected values.
+     * @param array  $keys      One ore more sorted set key names.
+     * @param array  $weights   An optional array of floats to weight each passed input set.
+     * @param string $aggregate An optional aggregation method to use.
+     *
+     *                          'SUM' - Store sum of all intersected members (this is the default).
+     *                          'MIN' - Store minimum value for each intersected member.
+     *                          'MAX' - Store maximum value for each intersected member.
+     *
+     * @return Redis|int|false  The total number of members writtern to the destination set or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zs', 'zs2', 'zs3');
+     * $redis->zAdd('zs1', 3, 'apples', 2, 'pears');
+     * $redis->zAdd('zs2', 4, 'pears', 3, 'bananas');
+     * $redis->zAdd('zs3', 2, 'figs', 3, 'pears');
+     *
+     * // Returns 1 (only 'pears' is in every set)
+     * $redis->zInterStore('fruit-sum', ['zs1', 'zs2', 'zs3']);
+     *
+     * // Array
+     * // (
+     * //     [pears] => 9
+     * // )
+     * $redis->zRange('fruit-sum', 0, -1, true);
+     *
+     * $redis->zInterStore('fruit-max', ['zs1', 'zs2', 'zs3'], NULL, 'MAX');
+     *
+     * // Array
+     * // (
+     * //     [pears] => 4
+     * // )
+     * print_r($redis->zRange('fruit-max', 0, -1, true));
+     * ?>
+     */
     public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false;
 
     /**
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 906afe7f8a..3016d22f83 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 84c333ece3425d10378996ae8b76ec57ced64025 */
+ * Stub hash: 52904ef54aa9857103e3bb65c089cf09833c507c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -960,11 +960,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xreadgroup, 0, 3
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, block, IS_LONG, 0, "1")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_xrevrange arginfo_class_Redis_xrange
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xrevrange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xtrim, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, maxlen, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, threshold, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, approx, _IS_BOOL, 0, "false")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, minid, _IS_BOOL, 0, "false")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
diff --git a/redis_commands.c b/redis_commands.c
index 332351ebff..e75b1892b8 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -6076,15 +6076,15 @@ int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
 int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     char **cmd, int *cmd_len, short *slot, void **ctx)
 {
-    zend_long threshold = 0, limit = -1;
+    zend_string *key = NULL, *threshold = NULL;
     zend_bool approx = 0, minid = 0;
     smart_string cmdstr = {0};
-    zend_string *key = NULL;
+    zend_long limit = -1;
     int argc;
 
     ZEND_PARSE_PARAMETERS_START(2, 5)
         Z_PARAM_STR(key)
-        Z_PARAM_LONG(threshold)
+        Z_PARAM_STR(threshold)
         Z_PARAM_OPTIONAL
         Z_PARAM_BOOL(approx)
         Z_PARAM_BOOL(minid)
@@ -6108,7 +6108,7 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "=");
     }
 
-    redis_cmd_append_sstr_long(&cmdstr, threshold);
+    redis_cmd_append_sstr_zstr(&cmdstr, threshold);
 
     if (limit > -1 && approx) {
         REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT");
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index e19fb8b6a0..e91caa8d05 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 84c333ece3425d10378996ae8b76ec57ced64025 */
+ * Stub hash: 52904ef54aa9857103e3bb65c089cf09833c507c */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -824,11 +824,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xreadgroup, 0, 0, 3)
 	ZEND_ARG_INFO(0, block)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_xrevrange arginfo_class_Redis_xrange
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xrevrange, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, end)
+	ZEND_ARG_INFO(0, start)
+	ZEND_ARG_INFO(0, count)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xtrim, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, maxlen)
+	ZEND_ARG_INFO(0, threshold)
 	ZEND_ARG_INFO(0, approx)
 	ZEND_ARG_INFO(0, minid)
 	ZEND_ARG_INFO(0, limit)

From a5c479011446a57ae842e6aa788d120ee5ba4da6 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 3 Nov 2022 15:41:46 -0700
Subject: [PATCH 1696/1986] Documentation:  More command docblocks

---
 redis.stub.php         | 214 +++++++++++++++++++++++++++++++++++++++--
 redis_arginfo.h        |  20 ++--
 redis_legacy_arginfo.h |  11 ++-
 3 files changed, 225 insertions(+), 20 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 4f62d731fb..9030ee57b2 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -641,6 +641,40 @@ public function delete(array|string $key, string ...$other_keys): Redis|int|fals
 
     public function discard(): Redis|bool;
 
+    //public function restore(string $key, int $timeout, string $value, ?array $options = NULL): bool;
+    /**
+     * Dump Redis' internal binary representation of a key.
+     *
+     * @see https://redis.io/commands/dump
+     *
+     * @param string $key The key to dump.
+     *
+     * @return Redis|string A binary string representing the key's value.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('zset');
+     *
+     * $redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two');
+     *
+     * // Retrieve the binary representation of the zset
+     * $binary = $redis->dump('zset');
+     *
+     * // Retore it to a different name
+     * $redis->restore('new-zset', 0, $binary);
+     *
+     * // Array
+     * // (
+     * //     [zero] => 0
+     * //     [one] => 1
+     * //     [two] => 2
+     * // )
+     * $redis->zRange('new-zset', 0, -1, true);
+     * ?>
+     * 
+     */
     public function dump(string $key): Redis|string;
 
     /**
@@ -1216,21 +1250,46 @@ public function punsubscribe(array $patterns): Redis|array|bool;
      */
     public function rPop(string $key, int $count = 0): Redis|array|string|bool;
 
-    /** @return string|Redis */
-    public function randomKey();
+    /**
+     * Return a random key from the current database
+     *
+     * @see https://redis.io/commands/randomkey
+     *
+     * @return Redis|string|false A random key name or false if no keys exist
+     *
+     */
+    public function randomKey(): Redis|string|false;
 
     public function rawcommand(string $command, mixed ...$args): mixed;
 
-    /** @return bool|Redis */
-    public function rename(string $key_src, string $key_dst);
+    /**
+     * Rename a key
+     *
+     * @param string $old_name The original name of the key
+     * @param string $new_name The new name for the key
+     *
+     * @return Redis|bool True if the key was renamed or false if not.
+     */
+    public function rename(string $old_name, string $new_name): Redis|bool;
 
     /** @return bool|Redis */
     public function renameNx(string $key_src, string $key_dst);
 
-    public function reset(): bool;
+    /**
+     * Reset the state of the connection.
+     *
+     * @return Redis|bool Should always return true unless there is an error.
+     */
+    public function reset(): Redis|bool;
 
     public function restore(string $key, int $timeout, string $value, ?array $options = NULL): bool;
 
+    /**
+     * Query whether the connected instance is a primary or replica
+     *
+     * @return mixed Will return an array with the role of the connected instance unless there is
+     *               an error.
+     */
     public function role(): mixed;
 
     /**
@@ -1415,6 +1474,32 @@ public function sDiffStore(string $dst, string $key, string ...$other_keys): Red
      */
     public function sInter(array|string $key, string ...$other_keys): Redis|array|false;
 
+    /**
+     * Compute the intersection of one or more sets and return the cardinality of the result.
+     *
+     * @see https://redis.io/commands/sintercard
+     *
+     * @param array $keys  One or more set key names.
+     * @param int   $limit A maximum cardinality to return.  This is useful to put an upper bound
+     *                     on the amount of work Redis will do.
+     *
+     * @return Redis|int|false The
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('set1', 'set2', 'set3');
+     *
+     * $redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot');
+     * $redis->sAdd('set2', 'apple',         'banana');
+     * $redis->sAdd('set3',          'pear', 'banana');
+     *
+     * // int(1)
+     * var_dump($redis->sInterCard(['set1', 'set2', 'set3']));
+     * ?>
+     * 
+     */
     public function sintercard(array $keys, int $limit = -1): Redis|int|false;
 
     /**
@@ -1446,6 +1531,36 @@ public function sintercard(array $keys, int $limit = -1): Redis|int|false;
      */
     public function sInterStore(array|string $key, string ...$other_keys): Redis|int|false;
 
+    /**
+     * Retrieve every member from a set key.
+     *
+     * @see https://redis.io/commands/smembers
+     *
+     * @param string $key The set name.
+     *
+     * @return Redis|array|false Every element in the set or false on failure.
+     *
+     * 
+     * $redis = new Redis(['host' => 'localhost']);
+     *
+     * $redis->del('tng-crew');
+     *
+     * $redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']);
+     *
+     * // Array
+     * // (
+     * //     [0] => Riker
+     * //     [1] => Crusher
+     * //     [2] => Troi
+     * //     [3] => Worf
+     * //     [4] => LaForge
+     * //     [5] => Picard
+     * //     [6] => Broccoli
+     * //     [7] => Data
+     * // )
+     * $redis->sMembers('tng-crew');
+     * 
+     */
     public function sMembers(string $key): Redis|array|false;
 
     public function sMisMember(string $key, string $member, string ...$other_members): array;
@@ -1613,6 +1728,38 @@ public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, s
      */
     public function scard(string $key): Redis|int|false;
 
+    /**
+     * An administrative command used to interact with LUA scripts stored on the server.
+     *
+     * @see https://redis.io/commands/script
+     *
+     * @param string $command The script suboperation to execute.
+     * @param mixed  $args    One ore more additional argument
+     *
+     * @return mixed This command returns various things depending on the specific operation executed.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $lua = sprintf("return %f", microtime(true));
+     *
+     * // array(1) {
+     * //   [0]=>
+     * //   int(0)
+     * // }
+     * var_dump($redis->script('exists', sha1($lua)));
+     *
+     * $redis->script('load', $lua);
+     *
+     * // array(1) {
+     * //   [0]=>
+     * //   int(1)
+     * // }
+     * var_dump($redis->script('exists', sha1($lua)));
+     * ?>
+     * 
+     */
     public function script(string $command, mixed ...$args): mixed;
 
     /**
@@ -2366,15 +2513,35 @@ public function unlink(array|string $key, string ...$other_keys): Redis|int|fals
      */
     public function unsubscribe(array $channels): Redis|array|bool;
 
-    /** @return bool|Redis */
-    public function unwatch();
+    /**
+     * Remove any previously WATCH'ed keys in a transaction.
+     *
+     * @see https://redis.io/commands/unwatch
+     * @see https://redis.io/commands/unwatch
+     * @see Redis::watch()
+     *
+     * @return True on success and false on failure.
+     */
+    public function unwatch(): Redis|bool;
 
     /**
      * @return bool|Redis
      */
     public function watch(array|string $key, string ...$other_keys);
 
-    public function wait(int $count, int $timeout): int|false;
+    /**
+     * Block the client up to the provided timeout until a certain number of replicas have confirmed
+     * recieving them.
+     *
+     * @see https://redis.io/commands/wait
+     *
+     * @param int $numreplicas The number of replicas we want to confirm write operaions
+     * @param int $timeout     How long to wait (zero meaning forever).
+     *
+     * @return Redis|int|false The number of replicas that have confirmed or false on failure.
+     *
+     */
+    public function wait(int $numreplicas, int $timeout): int|false;
 
     public function xack(string $key, string $group, array $ids): int|false;
 
@@ -2425,6 +2592,37 @@ public function xdel(string $key, array $ids): Redis|int|false;
     public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null,
                            bool $mkstream = false, int $entries_read = -2): mixed;
 
+    /**
+     * Retrieve information about a stream key.
+     *
+     * @param string $operation The specific info operation to perform.
+     * @param string $arg1      The first argument (depends on operation)
+     * @param string $arg2      The second argument
+     * @param int    $count     The COUNT argument to `XINFO STREAM`
+     *
+     * @return mixed This command can return different things depending on the operation being called.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('stream');
+     *
+     * $redis->xAdd('stream', "0-1", ['payload' => '0-1']);
+     * $redis->xAdd('stream', "0-2", ['payload' => '0-2']);
+     * $redis->xAdd('stream', "0-3", ['payload' => '0-3']);
+     *
+     * // Retrieve any consmers for a given key
+     * $redis->xInfo('CONSUMERS', 'stream');
+     *
+     * // Retrieve any groups for a given key
+     * $redis->xInfo('GROUPS', 'stream');
+     *
+     * // Retrieve general stream information along with messages
+     * $redis->xInfo('STREAM', 'stream');
+     * ?>
+     * 
+     */
     public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
 
 
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 3016d22f83..47dd058437 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 52904ef54aa9857103e3bb65c089cf09833c507c */
+ * Stub hash: ceb169a872a3df211ded811c1a5ac102832a9158 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -650,21 +650,25 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rPop, 0, 1, Redi
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_randomKey arginfo_class_Redis___destruct
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_randomKey, 0, 0, Redis, MAY_BE_STRING|MAY_BE_FALSE)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rawcommand, 0, 1, IS_MIXED, 0)
 	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
 	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rename, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rename, 0, 2, Redis, MAY_BE_BOOL)
+	ZEND_ARG_TYPE_INFO(0, old_name, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, new_name, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_renameNx, 0, 0, 2)
 	ZEND_ARG_TYPE_INFO(0, key_src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key_dst, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
-
-#define arginfo_class_Redis_reset arginfo_class_Redis_clearLastError
+#define arginfo_class_Redis_reset arginfo_class_Redis_bgSave
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_restore, 0, 3, _IS_BOOL, 0)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
@@ -861,7 +865,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_unsubscribe, 0,
 	ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct
+#define arginfo_class_Redis_unwatch arginfo_class_Redis_bgSave
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_watch, 0, 0, 1)
 	ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
@@ -869,7 +873,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_watch, 0, 0, 1)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_wait, 0, 2, MAY_BE_LONG|MAY_BE_FALSE)
-	ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, numreplicas, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index e91caa8d05..5c9e4c3d06 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 52904ef54aa9857103e3bb65c089cf09833c507c */
+ * Stub hash: ceb169a872a3df211ded811c1a5ac102832a9158 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -558,12 +558,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rawcommand, 0, 0, 1)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_rename, 0, 0, 2)
+	ZEND_ARG_INFO(0, old_name)
+	ZEND_ARG_INFO(0, new_name)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_renameNx, 0, 0, 2)
 	ZEND_ARG_INFO(0, key_src)
 	ZEND_ARG_INFO(0, key_dst)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_renameNx arginfo_class_Redis_rename
-
 #define arginfo_class_Redis_reset arginfo_class_Redis___destruct
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_restore, 0, 0, 3)
@@ -733,7 +736,7 @@ ZEND_END_ARG_INFO()
 #define arginfo_class_Redis_watch arginfo_class_Redis_del
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_wait, 0, 0, 2)
-	ZEND_ARG_INFO(0, count)
+	ZEND_ARG_INFO(0, numreplicas)
 	ZEND_ARG_INFO(0, timeout)
 ZEND_END_ARG_INFO()
 

From 14cd882b28cfa6c94d4c062919a1277896d22a59 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 3 Nov 2022 19:52:48 -0700
Subject: [PATCH 1697/1986] pconnect isn't experimental

See #2236
---
 README.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.markdown b/README.markdown
index d580c27fa0..6f2d7b15d6 100644
--- a/README.markdown
+++ b/README.markdown
@@ -75,7 +75,7 @@ session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeou
 
 * weight (integer): the weight of a host is used in comparison with the others in order to customize the session distribution on several hosts. If host A has twice the weight of host B, it will get twice the amount of sessions. In the example, *host1* stores 20% of all the sessions (1/(1+2+2)) while *host2* and *host3* each store 40% (2/(1+2+2)). The target host is determined once and for all at the start of the session, and doesn't change. The default weight is 1.
 * timeout (float): the connection timeout to a redis host, expressed in seconds. If the host is unreachable in that amount of time, the session storage will be unavailable for the client. The default timeout is very high (86400 seconds).
-* persistent (integer, should be 1 or 0): defines if a persistent connection should be used. **(experimental setting)**
+* persistent (integer, should be 1 or 0): defines if a persistent connection should be used.
 * prefix (string, defaults to "PHPREDIS_SESSION:"): used as a prefix to the Redis key in which the session is stored. The key is composed of the prefix followed by the session ID.
 * auth (string, or an array with one or two elements): used to authenticate with the server prior to sending commands.
 * database (integer): selects a different database.

From cf63e96ec5f6c9363bc5c6955d29c726fc7ec6fe Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Thu, 3 Nov 2022 19:45:37 -0700
Subject: [PATCH 1698/1986] Documentation:  More docblocks

---
 redis.stub.php         | 165 ++++++++++++++++++++++++++++++++++++++++-
 redis_arginfo.h        |   2 +-
 redis_legacy_arginfo.h |   2 +-
 3 files changed, 165 insertions(+), 4 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 9030ee57b2..12f77301a7 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1964,8 +1964,6 @@ public function setRange(string $key, int $index, string $value): Redis|int|fals
      */
     public function setOption(int $option, mixed $value): bool;
 
-    /** @return bool|Redis */
-
     /**
      * Set a Redis STRING key with a specific expiration in seconds.
      *
@@ -2545,12 +2543,104 @@ public function wait(int $numreplicas, int $timeout): int|false;
 
     public function xack(string $key, string $group, array $ids): int|false;
 
+    /**
+     * Append a message to a stream.
+     *
+     * @see https://redis.io/commands/xadd
+     *
+     * @param string $key        The stream name.
+     * @param string $id         The ID for the message we want to add.  This can be the special value '*'
+     *                           which means Redis will generate the ID that appends the message to the
+     *                           end of the stream.  It can also be a value in the form -* which will
+     *                           generate an ID that appends to the end ot entries with the same  value
+     *                           (if any exist).
+     * @param int    $maxlen     If specified Redis will append the new message but trim any number of the
+     *                           oldest messages in the stream until the length is <= $maxlen.
+     * @param bool   $approx     Used in conjunction with `$maxlen`, this flag tells Redis to trim the stream
+     *                           but in a more efficient way, meaning the trimming may not be exactly to
+     *                           `$maxlen` values.
+     * @param bool   $nomkstream If passed as `TRUE`, the stream must exist for Redis to append the message.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('ds9-season-1');
+     *
+     * $redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']);
+     * $redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']);
+     * $redis->xAdd('ds9-season-1', '1-3', ['title' => 'Emissary Part 2']);
+     * $redis->xAdd('ds9-season-1', '1-4', ['title' => 'Past Prologue']);
+     *
+     * // Array
+     * // (
+     * //     [1-1] => Array
+     * //         (
+     * //             [title] => Emissary Part 1
+     * //         )
+     * //
+     * //     [1-2] => Array
+     * //         (
+     * //             [title] => A Man Alone
+     * //         )
+     * //
+     * // )
+     * $redis->xRange('ds9-season-1', '1-1', '1-2');
+     * ?>
+     * ?>
+     * 
+     */
     public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): Redis|string|false;
 
     public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array;
 
     public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): Redis|bool|array;
 
+    /**
+     * Remove one or more specific IDs from a stream.
+     *
+     * @param string $key The stream to modify.
+     * @param array $ids One or more message IDs to remove.
+     *
+     * @return Redis|int|false The number of messages removed or false on failure.
+     *
+     * 
+     * $redis = new Redis(['host' => 'localhost']);
+     *
+     * $redis->del('stream');
+     *
+     * for ($a = 1; $a <= 3; $a++) {
+     *     for ($b = 1; $b <= 2; $b++) {
+     *         $redis->xAdd('stream', "$a-$b", ['id' => "$a-$b"]);
+     *     }
+     * }
+     *
+     * // Remove some elements
+     * $redis->xDel('stream', ['1-1', '2-1', '3-1']);
+     *
+     * // Array
+     * // (
+     * //     [1-2] => Array
+     * //         (
+     * //             [id] => 1-2
+     * //         )
+     * //
+     * //     [2-2] => Array
+     * //         (
+     * //             [id] => 2-2
+     * //         )
+     * //
+     * //     [3-2] => Array
+     * //         (
+     * //             [id] => 3-2
+     * //         )
+     * //
+     * // )
+     * $redis->xRange('stream', '-', '+');
+     * ?>
+     * 
+     */
     public function xdel(string $key, array $ids): Redis|int|false;
 
     /**
@@ -2650,6 +2740,23 @@ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = n
      */
     public function xlen(string $key): Redis|int|false;
 
+    /**
+     * Interact with stream messages that have been consumed by a consumer group but not yet
+     * acknowledged with XACK.
+     *
+     * @see https://redis.io/commands/xpending
+     * @see https://redis.io/commands/xreadgroup
+     *
+     * @param string $key      The stream to inspect.
+     * @param string $group    The user group we want to see pending messages from.
+     * @param string $start    The minimum ID to consider.
+     * @param string $string   The maximum ID to consider.
+     * @param string $count    Optional maximum number of messages to return.
+     * @param string $consumer If provided, limit the returned messages to a specific consumer.
+     *
+     * @return Redis|array|false The pending messages belonging to the stream or false on failure.
+     *
+     */
     public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false;
 
     /**
@@ -2753,6 +2860,60 @@ public function xrange(string $key, string $start, string $end, int $count = -1)
      */
     public function xread(array $streams, int $count = -1, int $block = -1): Redis|array|bool;
 
+    /**
+     * Read one or more messages using a consumer group.
+     *
+     * @param string $group     The consumer group to use.
+     * @param string $consumer  The consumer to use.
+     * @param array  $streams   An array of stream names and message IDs
+     * @param int    $count     Optional maximum number of messages to return
+     * @param int    $block     How long to block if there are no messages available.
+     *
+     * @return Redis|array|bool Zero or more unread messages or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('episodes');
+     *
+     * // Create a consumer group (and stream)
+     * $redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true);
+     *
+     * // Add a couple of messages to the stream
+     * $redis->xAdd('episodes', '1-1', ['title' => 'Emissary: Part 1']);
+     * $redis->xAdd('episodes', '1-2', ['title' => 'A Man Alone']);
+     *
+     * // Now read some messages with our consumer group
+     * $messages = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
+     *
+     * // After having read the two messages, add another
+     * $redis->xAdd('episodes', '1-3', ['title' => 'Emissary: Part 2']);
+     *
+     * // Acknowledge the first two read messages
+     * foreach ($messages as $stream => $stream_messages) {
+     *     $ids = array_keys($stream_messages);
+     *     $redis->xAck('stream', 'ds9', $ids);
+     * }
+     *
+     * // We can now pick up where we left off, and will only get the final message
+     * $msgs = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
+     *
+     * // array(1) {
+     * //   ["episodes"]=>
+     * //   array(1) {
+     * //     ["1-3"]=>
+     * //     array(1) {
+     * //       ["title"]=>
+     * //       string(16) "Emissary: Part 2"
+     * //     }
+     * //   }
+     * // }
+     * var_dump($msgs);
+     * ?>
+     * 
+     */
     public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): Redis|array|bool;
 
     /**
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 47dd058437..a13002a184 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ceb169a872a3df211ded811c1a5ac102832a9158 */
+ * Stub hash: 42952974e3686f29934dfff1ebba07150942a405 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 5c9e4c3d06..196ea90a97 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: ceb169a872a3df211ded811c1a5ac102832a9158 */
+ * Stub hash: 42952974e3686f29934dfff1ebba07150942a405 */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From f05ba8193519249fa856751be5be305d28decec1 Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 7 Nov 2022 10:47:05 -0800
Subject: [PATCH 1699/1986] Documentation:  Add several more docblocs

- Add a bunch more docblocks with examples
- Link every relevant RedisCluster method to the Redis docs.
---
 redis.stub.php                 | 163 +++++++++++-
 redis_arginfo.h                |   8 +-
 redis_cluster.stub.php         | 468 +++++++++++++++++++++++++++++++++
 redis_cluster_arginfo.h        |   2 +-
 redis_cluster_legacy_arginfo.h |   2 +-
 redis_legacy_arginfo.h         |   2 +-
 6 files changed, 635 insertions(+), 10 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 12f77301a7..46ac264a2c 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1563,12 +1563,173 @@ public function sInterStore(array|string $key, string ...$other_keys): Redis|int
      */
     public function sMembers(string $key): Redis|array|false;
 
-    public function sMisMember(string $key, string $member, string ...$other_members): array;
+    /**
+     * Check if one or more values are members of a set.
+     *
+     * @see https://redis.io/commands/smismember
+     * @see https://redis.io/commands/smember
+     * @see Redis::smember()
+     *
+     * @param string $key           The set to query.
+     * @param string $member        The first value to test if exists in the set.
+     * @param string $other_members Any number of additional values to check.
+     *
+     * @return Redis|array|false An array of integers representing whether each passed value
+     *                           was a member of the set.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('ds9-crew');
+     * $redis->sAdd('ds9-crew', ...["Sisko", "Kira", "Dax", "Worf", "Bashir", "O'Brien"]);
+     *
+     * $names = ['Sisko', 'Picard', 'Data', 'Worf'];
+     * $members = $redis->sMIsMember('ds9-crew', ...$names);
+     *
+     * // array(4) {
+     * //   ["Sisko"]=>
+     * //   int(1)
+     * //   ["Picard"]=>
+     * //   int(0)
+     * //   ["Data"]=>
+     * //   int(0)
+     * //   ["Worf"]=>
+     * //   int(1)
+     * // }
+     * var_dump(array_combine($names, $members));
+     * ?>
+     * 
+     */
+    public function sMisMember(string $key, string $member, string ...$other_members): Redis|array|false;
 
+    /**
+     * Pop a member from one set and push it onto another.  This command will create the
+     * destination set if it does not currently exist.
+     *
+     * @see https://redis.io/commands/smove
+     *
+     * @param string $src   The source set.
+     * @param string $dst   The destination set.
+     * @param mixed  $value The member you wish to move.
+     *
+     * @return Redis|bool   True if the member was moved, and false if it wasn't in the set.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('numbers', 'evens');
+     * $redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
+     *
+     * $redis->sMove('numbers', 'evens', 'zero');
+     * $redis->sMove('numbers', 'evens', 'two');
+     * $redis->sMove('numbers', 'evens', 'four');
+     *
+     * // array(2) {
+     * //   [0]=>
+     * //   string(5) "three"
+     * //   [1]=>
+     * //   string(3) "one"
+     * // }
+     * var_dump($redis->sMembers('numbers'));
+     *
+     * // array(3) {
+     * //   [0]=>
+     * //   string(4) "zero"
+     * //   [1]=>
+     * //   string(3) "two"
+     * //   [2]=>
+     * //   string(4) "four"
+     * // }
+     * var_dump($redis->sMembers('evens'));
+     *
+     * ?>
+     * 
+     */
     public function sMove(string $src, string $dst, mixed $value): Redis|bool;
 
+    /**
+     * Remove one or more elements from a set.
+     *
+     * @see https://redis.io/commands/spop
+     *
+     * @param string $key    The set in question.
+     * @param int    $count  An optional number of members to pop.   This defaults to
+     *                       removing one element.
+     *
+     * 
+     *  'localhost']);
+     *
+     *  'localhost']);
+     *
+     * $redis->del('numbers', 'evens');
+     * $redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
+     *
+     * $redis->sMove('numbers', 'evens', 'zero');
+     * $redis->sMove('numbers', 'evens', 'two');
+     * $redis->sMove('numbers', 'evens', 'four');
+     *
+     * // array(2) {
+     * //   [0]=>
+     * //   string(5) "three"
+     * //   [1]=>
+     * //   string(3) "one"
+     * // }
+     * var_dump($redis->sMembers('numbers'));
+     *
+     * // array(3) {
+     * //   [0]=>
+     * //   string(4) "zero"
+     * //   [1]=>
+     * //   string(3) "two"
+     * //   [2]=>
+     * //   string(4) "four"
+     * // }
+     * var_dump($redis->sMembers('evens'));
+     * ?>
+     * 
+     */
     public function sPop(string $key, int $count = 0): Redis|string|array|false;
 
+    /**
+     * Retrieve one or more random members of a set.
+     *
+     * @param string $key   The set to query.
+     * @param int    $count An optional count of members to return.
+     *
+     *                      If this value is positive, Redis will return *up to* the requested
+     *                      number but with unique elements that will never repeat.  This means
+     *                      you may recieve fewer then `$count` replies.
+     *
+     *                      If the number is negative, Redis will return the exact number requested
+     *                      but the result may contain duplicate elements.
+     *
+     * @return Redis|array|string|false One or more random members or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('elder-gods');
+     *
+     * $redis->sAdd('elder-gods', ["Cthulhu", "Azathoth", "Daoloth", "D'endrrah"]);
+     *
+     * // A single random member returned.
+     * $rng1 = $redis->sRandMember('elder-gods');
+     *
+     * // Up to SCARD `elder-gods` random members returned
+     * $rng2 = $redis->sRandMember('elder-gods', 9999);
+     *
+     * // 9999 elements from the set returned with duplicates
+     * $rng3 = $redis->sRandMember('elder-gods', -9999);
+     * ?>
+     * 
+     *
+     */
     public function sRandMember(string $key, int $count = 0): Redis|string|array|false;
 
     /**
diff --git a/redis_arginfo.h b/redis_arginfo.h
index a13002a184..0a54fec232 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 42952974e3686f29934dfff1ebba07150942a405 */
+ * Stub hash: 4c4d58bf2ce686c82287a69fef109267828a2d9b */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -720,11 +720,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_sMembers arginfo_class_Redis_hGetAll
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sMisMember, 0, 2, IS_ARRAY, 0)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sMove, 0, 3, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index f4ff298f8d..a017c635fc 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -49,6 +49,9 @@ public function _masters(): array;
 
     public function _redir(): string|null;
 
+    /**
+     * @see Redis::acl
+     */
     public function acl(string|array $key_or_address, string $subcmd, string ...$args): mixed;
 
     /**
@@ -56,12 +59,24 @@ public function acl(string|array $key_or_address, string $subcmd, string ...$arg
      */
     public function append(string $key, mixed $value): RedisCluster|bool|int;
 
+    /**
+     * @see Redis::bgrewriteaof
+     */
     public function bgrewriteaof(string|array $key_or_address): RedisCluster|bool;
 
+    /**
+     * @see Redis::bgsave
+     */
     public function bgsave(string|array $key_or_address): RedisCluster|bool;
 
+    /**
+     * @see Redis::bitcount
+     */
     public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false): RedisCluster|bool|int;
 
+    /**
+     * @see Redis::bitop
+     */
     public function bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys): RedisCluster|bool|int;
 
     /**
@@ -93,12 +108,24 @@ public function brpop(string|array $key, string|float|int $timeout_or_key, mixed
      */
     public function brpoplpush(string $srckey, string $deskey, int $timeout): mixed;
 
+    /**
+     * @see Redis::bzpopmax
+     */
     public function bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
 
+    /**
+     * @see Redis::bzpopmin
+     */
     public function bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array;
 
+    /**
+     * @see Redis::bzmpop
+     */
     public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
+    /**
+     * @see Redis::zmpop
+     */
     public function zmpop(array $keys, string $from, int $count = 1): RedisCluster|array|null|false;
 
     /**
@@ -116,12 +143,24 @@ public function lmpop(array $keys, string $from, int $count = 1): RedisCluster|a
      */
     public function clearlasterror(): bool;
 
+    /**
+     * @see Redis::client
+     */
     public function client(string|array $key_or_address, string $subcommand, ?string $arg = NULL): array|string|bool;
 
+    /**
+     * @see Redis::close
+     */
     public function close(): bool;
 
+    /**
+     * @see Redis::cluster
+     */
     public function cluster(string|array $key_or_address, string $command, mixed ...$extra_args): mixed;
 
+    /**
+     * @see Redis::command
+     */
     public function command(mixed ...$extra_args): mixed;
 
     /**
@@ -144,6 +183,9 @@ public function decr(string $key, int $by = 1): RedisCluster|int|false;
      */
     public function decrby(string $key, int $value): RedisCluster|int|false;
 
+    /**
+     * @see Redis::decrbyfloat
+     */
     public function decrbyfloat(string $key, float $value): float;
 
     /**
@@ -151,8 +193,14 @@ public function decrbyfloat(string $key, float $value): float;
      */
     public function del(array|string $key, string ...$other_keys): RedisCluster|int|false;
 
+    /**
+     * @see Redis::discard
+     */
     public function discard(): bool;
 
+    /**
+     * @see Redis::dump
+     */
     public function dump(string $key): RedisCluster|string|false;
 
     /**
@@ -160,12 +208,24 @@ public function dump(string $key): RedisCluster|string|false;
      */
     public function echo(string|array $key_or_address, string $msg): RedisCluster|string|false;
 
+    /**
+     * @see Redis::eval
+     */
     public function eval(string $script, array $args = [], int $num_keys = 0): mixed;
 
+    /**
+     * @see Redis::eval_ro
+     */
     public function eval_ro(string $script, array $args = [], int $num_keys = 0): mixed;
 
+    /**
+     * @see Redis::evalsha
+     */
     public function evalsha(string $script_sha, array $args = [], int $num_keys = 0): mixed;
 
+    /**
+     * @see Redis::evalsha_ro
+     */
     public function evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0): mixed;
 
     /**
@@ -173,6 +233,9 @@ public function evalsha_ro(string $script_sha, array $args = [], int $num_keys =
      */
     public function exec(): array|false;
 
+    /**
+     * @see Redis::exists
+     */
     public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
 
     /**
@@ -180,8 +243,14 @@ public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
      */
     public function touch(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
 
+    /**
+     * @see Redis::expire
+     */
     public function expire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool;
 
+    /**
+     * @see Redis::expireat
+     */
     public function expireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
 
     /**
@@ -194,78 +263,189 @@ public function expiretime(string $key): RedisCluster|int|false;
      */
     public function pexpiretime(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::flushall
+     */
     public function flushall(string|array $key_or_address, bool $async = false): RedisCluster|bool;
 
+    /**
+     * @see Redis::flushdb
+     */
     public function flushdb(string|array $key_or_address, bool $async = false): RedisCluster|bool;
 
+    /**
+     * @see Redis::geoadd
+     */
     public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options): RedisCluster|int|false;
 
+    /**
+     * @see Redis::geodist
+     */
     public function geodist(string $key, string $src, string $dest, ?string $unit = null): RedisCluster|float|false;
 
+    /**
+     * @see Redis::geohash
+     */
     public function geohash(string $key, string $member, string ...$other_members): RedisCluster|array|false;
 
+    /**
+     * @see Redis::geopos
+     */
     public function geopos(string $key, string $member, string ...$other_members): RedisCluster|array|false;
 
+    /**
+     * @see Redis::georadius
+     */
     public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed;
 
+    /**
+     * @see Redis::georadius_ro
+     */
     public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed;
 
+    /**
+     * @see Redis::georadiusbymember
+     */
     public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): mixed;
 
+    /**
+     * @see Redis::georadiusbymember_ro
+     */
     public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): mixed;
 
+    /**
+     * @see Redis::get
+     */
     public function get(string $key): mixed;
 
+    /**
+     * @see Redis::getbit
+     */
     public function getbit(string $key, int $value): RedisCluster|int|false;
 
+    /**
+     * @see Redis::getlasterror
+     */
     public function getlasterror(): string|null;
 
+    /**
+     * @see Redis::getmode
+     */
     public function getmode(): int;
 
+    /**
+     * @see Redis::getoption
+     */
     public function getoption(int $option): mixed;
 
+    /**
+     * @see Redis::getrange
+     */
     public function getrange(string $key, int $start, int $end): RedisCluster|string|false;
 
+    /**
+     * @see Redis::lcs
+     */
     public function lcs(string $key1, string $key2, ?array $options = NULL): RedisCluster|string|array|int|false;
 
+    /**
+     * @see Redis::getset
+     */
     public function getset(string $key, mixed $value): RedisCluster|string|bool;
 
+    /**
+     * @see Redis::gettransferredbytes
+     */
     public function gettransferredbytes(): int|false;
 
+    /**
+     * @see Redis::hdel
+     */
     public function hdel(string $key, string $member, string ...$other_members): RedisCluster|int|false;
 
+    /**
+     * @see Redis::hexists
+     */
     public function hexists(string $key, string $member): RedisCluster|bool;
 
+    /**
+     * @see Redis::hget
+     */
     public function hget(string $key, string $member): mixed;
 
+    /**
+     * @see Redis::hgetall
+     */
     public function hgetall(string $key): RedisCluster|array|false;
 
+    /**
+     * @see Redis::hincrby
+     */
     public function hincrby(string $key, string $member, int $value): RedisCluster|int|false;
 
+    /**
+     * @see Redis::hincrbyfloat
+     */
     public function hincrbyfloat(string $key, string $member, float $value): RedisCluster|float|false;
 
+    /**
+     * @see Redis::hkeys
+     */
     public function hkeys(string $key): RedisCluster|array|false;
 
+    /**
+     * @see Redis::hlen
+     */
     public function hlen(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::hmget
+     */
     public function hmget(string $key, array $keys): RedisCluster|array|false;
 
+    /**
+     * @see Redis::hmset
+     */
     public function hmset(string $key, array $key_values): RedisCluster|bool;
 
+    /**
+     * @see Redis::hscan
+     */
     public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|bool;
 
+    /**
+     * @see Redis::hset
+     */
     public function hset(string $key, string $member, mixed $value): RedisCluster|int|false;
 
+    /**
+     * @see Redis::hsetnx
+     */
     public function hsetnx(string $key, string $member, mixed $value): RedisCluster|bool;
 
+    /**
+     * @see Redis::hstrlen
+     */
     public function hstrlen(string $key, string $field): RedisCluster|int|false;
 
+    /**
+     * @see Redis::hvals
+     */
     public function hvals(string $key): RedisCluster|array|false;
 
+    /**
+     * @see Redis::incr
+     */
     public function incr(string $key, int $by = 1): RedisCluster|int|false;
 
+    /**
+     * @see Redis::incrby
+     */
     public function incrby(string $key, int $value): RedisCluster|int|false;
 
+    /**
+     * @see Redis::incrbyfloat
+     */
     public function incrbyfloat(string $key, float $value): RedisCluster|float|false;
 
     /**
@@ -286,36 +466,84 @@ public function incrbyfloat(string $key, float $value): RedisCluster|float|false
      */
     public function info(string|array $key_or_address, string ...$sections): RedisCluster|array|false;
 
+    /**
+     * @see Redis::keys
+     */
     public function keys(string $pattern): RedisCluster|array|false;
 
+    /**
+     * @see Redis::lastsave
+     */
     public function lastsave(string|array $key_or_address): RedisCluster|int|false;
 
+    /**
+     * @see Redis::lget
+     */
     public function lget(string $key, int $index): RedisCluster|string|bool;
 
+    /**
+     * @see Redis::lindex
+     */
     public function lindex(string $key, int $index): mixed;
 
+    /**
+     * @see Redis::linsert
+     */
     public function linsert(string $key, string $pos, mixed $pivot, mixed $value): RedisCluster|int|false;
 
+    /**
+     * @see Redis::llen
+     */
     public function llen(string $key): RedisCluster|int|bool;
 
+    /**
+     * @see Redis::lpop
+     */
     public function lpop(string $key, int $count = 0): RedisCluster|bool|string|array;
 
+    /**
+     * @see Redis::lpush
+     */
     public function lpush(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|bool;
 
+    /**
+     * @see Redis::lpushx
+     */
     public function lpushx(string $key, mixed $value): RedisCluster|int|bool;
 
+    /**
+     * @see Redis::lrange
+     */
     public function lrange(string $key, int $start, int $end): RedisCluster|array|false;
 
+    /**
+     * @see Redis::lrem
+     */
     public function lrem(string $key, mixed $value, int $count = 0): RedisCluster|int|bool;
 
+    /**
+     * @see Redis::lset
+     */
     public function lset(string $key, int $index, mixed $value): RedisCluster|bool;
 
+    /**
+     * @see Redis::ltrim
+     */
     public function ltrim(string $key, int $start, int $end): RedisCluster|bool;
 
+    /**
+     * @see Redis::mget
+     */
     public function mget(array $keys): RedisCluster|array|false;
 
+    /**
+     * @see Redis::mset
+     */
     public function mset(array $key_values): RedisCluster|bool;
 
+    /**
+     * @see Redis::msetnx
+     */
     public function msetnx(array $key_values): RedisCluster|array|false;
 
     /* We only support Redis::MULTI in RedisCluster but take the argument
@@ -323,12 +551,24 @@ public function msetnx(array $key_values): RedisCluster|array|false;
        we add pipeline support in the future. */
     public function multi(int $value = Redis::MULTI): RedisCluster|bool;
 
+    /**
+     * @see Redis::object
+     */
     public function object(string $subcommand, string $key): RedisCluster|int|string|false;
 
+    /**
+     * @see Redis::persist
+     */
     public function persist(string $key): RedisCluster|bool;
 
+    /**
+     * @see Redis::pexpire
+     */
     public function pexpire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool;
 
+    /**
+     * @see Redis::pexpireat
+     */
     public function pexpireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
 
 
@@ -362,28 +602,64 @@ public function pfmerge(string $key, array $keys): RedisCluster|bool;
      */
     public function ping(string|array $key_or_address, ?string $message = NULL): mixed;
 
+    /**
+     * @see Redis::psetex
+     */
     public function psetex(string $key, int $timeout, string $value): RedisCluster|bool;
 
+    /**
+     * @see Redis::psubscribe
+     */
     public function psubscribe(array $patterns, callable $callback): void;
 
+    /**
+     * @see Redis::pttl
+     */
     public function pttl(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::publish
+     */
     public function publish(string $channel, string $message): RedisCluster|bool;
 
+    /**
+     * @see Redis::pubsub
+     */
     public function pubsub(string|array $key_or_address, string ...$values): mixed;
 
+    /**
+     * @see Redis::punsubscribe
+     */
     public function punsubscribe(string $pattern, string ...$other_patterns): bool|array;
 
+    /**
+     * @see Redis::randomkey
+     */
     public function randomkey(string|array $key_or_address): RedisCluster|bool|string;
 
+    /**
+     * @see Redis::rawcommand
+     */
     public function rawcommand(string|array $key_or_address, string $command, mixed ...$args): mixed;
 
+    /**
+     * @see Redis::rename
+     */
     public function rename(string $key_src, string $key_dst): RedisCluster|bool;
 
+    /**
+     * @see Redis::renamenx
+     */
     public function renamenx(string $key, string $newkey): RedisCluster|bool;
 
+    /**
+     * @see Redis::restore
+     */
     public function restore(string $key, int $timeout, string $value, ?array $options = NULL): RedisCluster|bool;
 
+    /**
+     * @see Redis::role
+     */
     public function role(string|array $key_or_address): mixed;
 
     /**
@@ -396,8 +672,14 @@ public function rpop(string $key, int $count = 0): RedisCluster|bool|string|arra
      */
     public function rpoplpush(string $src, string $dst): RedisCluster|bool|string;
 
+    /**
+     * @see Redis::rpush
+     */
     public function rpush(string $key, mixed ...$elements): RedisCluster|int|false;
 
+    /**
+     * @see Redis::rpushx
+     */
     public function rpushx(string $key, string $value): RedisCluster|bool|int;
 
     /**
@@ -410,12 +692,24 @@ public function sadd(string $key, mixed $value, mixed ...$other_values): RedisCl
      */
     public function saddarray(string $key, array $values): RedisCluster|bool|int;
 
+    /**
+     * @see Redis::save
+     */
     public function save(string|array $key_or_address): RedisCluster|bool;
 
+    /**
+     * @see Redis::scan
+     */
     public function scan(?int &$iterator, string|array $key_or_address, ?string $pattern = null, int $count = 0): bool|array;
 
+    /**
+     * @see Redis::scard
+     */
     public function scard(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::script
+     */
     public function script(string|array $key_or_address, mixed ...$args): mixed;
 
     /**
@@ -433,14 +727,29 @@ public function sdiffstore(string $dst, string $key, string ...$other_keys): Red
      */
     public function set(string $key, mixed $value, mixed $options = NULL): RedisCluster|string|bool;
 
+    /**
+     * @see Redis::setbit
+     */
     public function setbit(string $key, int $offset, bool $onoff): RedisCluster|int|false;
 
+    /**
+     * @see Redis::setex
+     */
     public function setex(string $key, int $expire, mixed $value): RedisCluster|bool;
 
+    /**
+     * @see Redis::setnx
+     */
     public function setnx(string $key, mixed $value): RedisCluster|bool;
 
+    /**
+     * @see Redis::setoption
+     */
     public function setoption(int $option, mixed $value): bool;
 
+    /**
+     * @see Redis::setrange
+     */
     public function setrange(string $key, int $offset, string $value): RedisCluster|int|false;
 
     /**
@@ -448,6 +757,9 @@ public function setrange(string $key, int $offset, string $value): RedisCluster|
      */
     public function sinter(array|string $key, string ...$other_keys): RedisCluster|array|false;
 
+    /**
+     * @see Redis::sintercard
+     */
     public function sintercard(array $keys, int $limit = -1): RedisCluster|int|false;
 
     /**
@@ -455,12 +767,24 @@ public function sintercard(array $keys, int $limit = -1): RedisCluster|int|false
      */
     public function sinterstore(array|string $key, string ...$other_keys): RedisCluster|int|false;
 
+    /**
+     * @see Redis::sismember
+     */
     public function sismember(string $key, mixed $value): RedisCluster|bool;
 
+    /**
+     * @see Redis::slowlog
+     */
     public function slowlog(string|array $key_or_address, mixed ...$args): mixed;
 
+    /**
+     * @see Redis::smembers()
+     */
     public function smembers(string $key): RedisCluster|array|false;
 
+    /**
+     * @see Redis::smove()
+     */
     public function smove(string $src, string $dst, string $member): RedisCluster|bool;
 
     /**
@@ -473,8 +797,14 @@ public function sort(string $key, ?array $options = NULL): RedisCluster|array|bo
      */
     public function sort_ro(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string;
 
+    /**
+     * @see Redis::spop
+     */
     public function spop(string $key, int $count = 0): RedisCluster|string|array|false;
 
+    /**
+     * @see Redis::srandmember
+     */
     public function srandmember(string $key, int $count = 0): RedisCluster|string|array|false;
 
     /**
@@ -482,10 +812,19 @@ public function srandmember(string $key, int $count = 0): RedisCluster|string|ar
      */
     public function srem(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false;
 
+    /**
+     * @see Redis::sscan
+     */
     public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
 
+    /**
+     * @see Redis::strlen
+     */
     public function strlen(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::subscribe
+     */
     public function subscribe(array $channels, callable $cb): void;
 
     /**
@@ -498,62 +837,149 @@ public function sunion(string $key, string ...$other_keys): RedisCluster|bool|ar
      */
     public function sunionstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false;
 
+    /**
+     * @see Redis::time
+     */
     public function time(string|array $key_or_address): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::ttl
+     */
     public function ttl(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::type
+     */
     public function type(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::unsubscribe
+     */
     public function unsubscribe(array $channels): bool|array;
 
+    /**
+     * @see Redis::unlink
+     */
     public function unlink(array|string $key, string ...$other_keys): RedisCluster|int|false;
 
+    /**
+     * @see Redis::unwatch
+     */
     public function unwatch(): bool;
 
+    /**
+     * @see Redis::watch
+     */
     public function watch(string $key, string ...$other_keys): RedisCluster|bool;
 
+    /**
+     * @see Redis::xack
+     */
     public function xack(string $key, string $group, array $ids): RedisCluster|int|false;
 
+    /**
+     * @see Redis::xadd
+     */
     public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false): RedisCluster|string|false;
 
+    /**
+     * @see Redis::xclaim
+     */
     public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): RedisCluster|string|array|false;
 
+    /**
+     * @see Redis::xdel
+     */
     public function xdel(string $key, array $ids): RedisCluster|int|false;
 
+    /**
+     * @see Redis::xgroup
+     */
     public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
 
+    /**
+     * @see Redis::xinfo
+     */
     public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
 
+    /**
+     * @see Redis::xlen
+     */
     public function xlen(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::xpending
+     */
     public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): RedisCluster|array|false;
 
+    /**
+     * @see Redis::xrange
+     */
     public function xrange(string $key, string $start, string $end, int $count = -1): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::xread
+     */
     public function xread(array $streams, int $count = -1, int $block = -1): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::xreadgroup
+     */
     public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::xrevrange
+     */
     public function xrevrange(string $key, string $start, string $end, int $count = -1): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::xtrim
+     */
     public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zadd
+     */
     public function zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zcard
+     */
     public function zcard(string $key): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zcount
+     */
     public function zcount(string $key, string $start, string $end): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zincrby
+     */
     public function zincrby(string $key, float $value, string $member): RedisCluster|float|false;
 
+    /**
+     * @see Redis::zinterstore
+     */
     public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zintercard
+     */
     public function zintercard(array $keys, int $limit = -1): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zlexcount
+     */
     public function zlexcount(string $key, string $min, string $max): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zpopmax
+     */
     public function zpopmax(string $key, int $value = null): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::zpopmin
+     */
     public function zpopmin(string $key, int $value = null): RedisCluster|bool|array;
 
     /**
@@ -567,32 +993,74 @@ public function zrange(string $key, mixed $start, mixed $end, array|bool|null $o
     public function zrangestore(string $dstkey, string $srckey, int $start, int $end,
                                 array|bool|null $options = null): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zrangebylex
+     */
     public function zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1): RedisCluster|array|false;
 
+    /**
+     * @see Redis::zrangebyscore
+     */
     public function zrangebyscore(string $key, string $start, string $end, array $options = []): RedisCluster|array|false;
 
+    /**
+     * @see Redis::zrank
+     */
     public function zrank(string $key, mixed $member): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zrem
+     */
     public function zrem(string $key, string $value, string ...$other_values): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zremrangebylex
+     */
     public function zremrangebylex(string $key, string $min, string $max): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zremrangebyrank
+     */
     public function zremrangebyrank(string $key, string $min, string $max): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zremrangebyscore
+     */
     public function zremrangebyscore(string $key, string $min, string $max): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zrevrange
+     */
     public function zrevrange(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::zrevrangebylex
+     */
     public function zrevrangebylex(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::zrevrangebyscore
+     */
     public function zrevrangebyscore(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::zrevrank
+     */
     public function zrevrank(string $key, mixed $member): RedisCluster|int|false;
 
+    /**
+     * @see Redis::zscan
+     */
     public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): RedisCluster|bool|array;
 
+    /**
+     * @see Redis::zscore
+     */
     public function zscore(string $key, mixed $member): RedisCluster|float|false;
 
+    /**
+     * @see Redis::zunionstore
+     */
     public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): RedisCluster|int|false;
 }
 
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 887364060d..80907de4fc 100644
--- a/redis_cluster_arginfo.h
+++ b/redis_cluster_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 65c7830c07ea86720c6089dbd0fa7943df0a2ca8 */
+ * Stub hash: 1783d14c476f95598062edb44dab7284b9b2680d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index ee4cfafadf..77df99ded6 100644
--- a/redis_cluster_legacy_arginfo.h
+++ b/redis_cluster_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 65c7830c07ea86720c6089dbd0fa7943df0a2ca8 */
+ * Stub hash: 1783d14c476f95598062edb44dab7284b9b2680d */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
 	ZEND_ARG_INFO(0, name)
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 196ea90a97..8138c8bb08 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 42952974e3686f29934dfff1ebba07150942a405 */
+ * Stub hash: 4c4d58bf2ce686c82287a69fef109267828a2d9b */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)

From 17db23281a734c33b28c032a3df8a308cd48d92e Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 7 Nov 2022 15:49:36 -0800
Subject: [PATCH 1700/1986] Documentation:  Several more docblocks

---
 redis.stub.php         | 552 +++++++++++++++++++++++++++++++++++++++--
 redis_arginfo.h        |  34 ++-
 redis_legacy_arginfo.h |  25 +-
 3 files changed, 567 insertions(+), 44 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 46ac264a2c..aad71f3d6c 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -639,6 +639,29 @@ public function del(array|string $key, string ...$other_keys): Redis|int|false;
      */
     public function delete(array|string $key, string ...$other_keys): Redis|int|false;
 
+    /**
+     * Discard a transaction currently in progress.
+     *
+     * @return Redis|bool  True if we could discard the transaction.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->multi()->set('foo', 'bar')->get('foo');
+     *
+     * // Redis::MULTI
+     * $redis->getMode();
+     *
+     * // Discard the in-progress transaction
+     * $redis->discard();
+     *
+     * // Redis::ATOMIC
+     * $redis->getMode();
+     *
+     * ?>
+     * 
+     */
     public function discard(): Redis|bool;
 
     //public function restore(string $key, int $timeout, string $value, ?array $options = NULL): bool;
@@ -986,28 +1009,210 @@ public function getMode(): int;
      */
     public function getOption(int $option): mixed;
 
+    /**
+     * Get the persistent connection ID, if there is one.
+     *
+     * @return string The ID or NULL if we don't have one.
+     */
     public function getPersistentID(): ?string;
 
+    /**
+     * Get the port we are connected to.  This number will be zero if we are connected to a unix socket.
+     *
+     * @return int The port.
+     */
     public function getPort(): int;
 
+    /**
+     * Retrieve a substring of a string by index.
+     *
+     * @param string $key   The string to query.
+     * @param int    $start The zero-based starting index.
+     * @param int    $end   The zero-based ending index.
+     *
+     * @return Redis|string|false The substring or false on failure.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $word = 'Supercalifragilisticexpialidocious';
+     * $redis->set('silly-word', $word);
+     *
+     * // string "super"
+     * var_dump($redis->getRange('silly-word', 0, 4));
+     *
+     * // string(7) "docious"
+     * var_dump($redis->getRange('silly-word', -7, -1));
+     * ?>
+     */
     public function getRange(string $key, int $start, int $end): Redis|string|false;
 
+    /**
+     * Get the longest common subsequence between two string keys.
+     *
+     * @param string $key1    The first key to check
+     * @param string $key2    The second key to check
+     * @param array  $options An optional array of modifiers for the comand.
+     *
+     *                        
+     *                        $options = [
+     *                            'MINMATCHLEN'  => int  // Exclude matching substrings that are less than this value
+     *
+     *                            'WITHMATCHLEN' => bool // Whether each match should also include its length.
+     *
+     *                            'LEN'                  // Return the length of the longest subsequence
+     *
+     *                            'IDX'                  // Each returned match will include the indexes where the
+     *                                                   // match occurs in each string.
+     *                        ];
+     *                        
+     *
+     *                        NOTE:  'LEN' cannot be used with 'IDX'.
+     *
+     * @return Redis|string|array|int|false Various reply types depending on options.
+     *
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc');
+     * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc');
+     *
+     * // string(37) "acccgcacggcaagtcgttccagcaactggcgctagc"
+     * var_dump($redis->lcs('seq1', 'seq2'));
+     * ?>
+     */
     public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
 
-    public function getReadTimeout(): int;
+    /**
+     * Get the currently set read timeout on the connection.
+     *
+     * @return float The timeout.
+     */
+    public function getReadTimeout(): float;
 
+    /**
+     * Sets a key and returns any previously set value, if the key already existed.
+     *
+     * @param string $key The key to set.
+     * @param mixed $value The value to set the key to.
+     *
+     * @return Redis|string|false The old value of the key or false if it didn't exist.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('captain');
+     *
+     * // bool(false)
+     * var_dump($redis->getset('captain', 'Pike'));
+     *
+     * // string(4) "Pike"
+     * var_dump($redis->getset('captain', 'Kirk'));
+     * ?>
+     * 
+     */
     public function getset(string $key, mixed $value): Redis|string|false;
 
-    public function getTimeout(): int;
+    /**
+     * Retrieve any set connection timeout
+     *
+     * @return float The currently set timeout or false on failure (e.g. we aren't connected).
+     */
+    public function getTimeout(): float|false;
 
     public function getTransferredBytes(): int|false;
 
-    public function hDel(string $key, string $member, string ...$other_members): Redis|int|false;
+    /**
+     * Remove one or more fields from a hash.
+     *
+     * @see https://redis.io/commands/hdel
+     *
+     * @param string $key          The hash key in question.
+     * @param string $field        The first field to remove
+     * @param string $other_fields One or more additional fields to remove.
+     *
+     * @return Redis|int|false     The number of fields actually removed.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('people');
+     *
+     * $redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
+     *
+     * // int(1)
+     * $redis->hDel('comms', 'Mallory', 'Archibald');
+     * ?>
+     * 
+     */
+    public function hDel(string $key, string $field, string ...$other_fields): Redis|int|false;
 
-    public function hExists(string $key, string $member): Redis|bool;
+    /**
+     * Checks whether a field exists in a hash.
+     *
+     * @see https://redis.io/commands/hexists
+     *
+     * @param string $key   The hash to query.
+     * @param string $field The field to check
+     *
+     * @return Redis|bool   True if it exists, false if not.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('captains');
+     *
+     * $redis->hmset('captains', ['Kirk' => 'Enterprise', 'Picard' => 'Enterprise-D', 'Sisko' => 'Defiant']);
+     *
+     * bool(false)
+     * $redis->hExists('captains', 'Pike');
+     *
+     * bool(true)
+     * $redis->hExists('captains', 'Picard');
+     * ?>
+     * 
+     */
+    public function hExists(string $key, string $field): Redis|bool;
 
     public function hGet(string $key, string $member): mixed;
 
+    /**
+     * Read every field and value from a hash.
+     *
+     * @see https://redis.io/commands/hgetall
+     *
+     * @param string $key The hash to query.
+     *
+     * @return Redis|array|false All fields and values or false if the key didn't exist.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('comms');
+     *
+     * $redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
+     *
+     * // array(3) {
+     * //   ["Alice"]=>
+     * //   string(3) "ecc"
+     * //   ["Bob"]=>
+     * //   string(3) "rsa"
+     * //   ["Mallory"]=>
+     * //   string(7) "haxx00r"
+     * // }
+     * $redis->hGetAll('comms');
+     * ?>
+     * 
+     */
     public function hGetAll(string $key): Redis|array|false;
 
     public function hIncrBy(string $key, string $member, int $value): Redis|int|false;
@@ -1034,14 +1239,87 @@ public function hVals(string $key): Redis|array|false;
 
     public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|bool|array;
 
-    /** @return Redis|int|false */
-    public function incr(string $key, int $by = 1);
+    /**
+     * Increment a key's value, optionally by a specifc amount.
+     *
+     * @see https://redis.io/commands/incr
+     * @see https://redis.io/commands/incrby
+     *
+     * @param string $key The key to increment
+     * @param int    $by  An optional amount to increment by.
+     *
+     * @return Redis|int|false  The new value of the key after incremented.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('counter', 1);
+     *
+     * // int(2);
+     * $redis->incr('counter');
+     *
+     * // int(4);
+     * $redis->incr('counter', 2);
+     * ?>
+     * 
+     */
+    public function incr(string $key, int $by = 1): Redis|int|false;
 
-    /** @return Redis|int|false */
-    public function incrBy(string $key, int $value);
+    /**
+     * Increment a key by a specific integer value
+     *
+     * @see https://redis.io/commands/incrby
+     *
+     * @param string $key   The key to increment.
+     * @param int    $value The amount to increment.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->set('primes', 2);
+     *
+     * // int(3)
+     * $redis->incrby('primes', 1);
+     *
+     * // int(5)
+     * $redis->incrby('primes', 2);
+     *
+     * // int(7)
+     * $redis->incrby('primes', 2);
+     *
+     * // int(11)
+     * $redis->incrby('primes', 4);
+     * ?>
+     * 
+     */
+    public function incrBy(string $key, int $value): Redis|int|false;
 
-    /** @return Redis|int|false */
-    public function incrByFloat(string $key, float $value);
+    /**
+     * Increment a numeric key by a floating point value.
+     *
+     * @param string $key The key to increment
+     * @param floag $value How much to increment (or decrement) the value.
+     *
+     * @return Redis|float|false The new value of the key or false if the key didn't contain a string.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('tau');
+     *
+     * // float(3.1415926)
+     * var_dump($redis->incrByFloat('tau', 3.1415926));
+     *
+     * // float(6.2831852)
+     * var_dump($redis->incrByFloat('tau', 3.1415926));
+     * ?>
+     * 
+     */
+    public function incrByFloat(string $key, float $value): Redis|float|false;
 
     /**
      * Retrieve information about the connected redis-server.  If no arguments are passed to
@@ -1059,6 +1337,11 @@ public function incrByFloat(string $key, float $value);
      */
     public function info(string ...$sections): Redis|array|false;
 
+    /**
+     * Check if we are currently connected to a Redis instance.
+     *
+     * @return bool True if we are, false if not
+     */
     public function isConnected(): bool;
 
     /** @return Redis|array|false */
@@ -1212,9 +1495,52 @@ public function pfmerge(string $dst, array $srckeys): Redis|bool;
      * @return Redis|string|false If passed no message, this command will simply return `true`.
      *                            If a message is passed, it will return the message.
      *
+     * 
+     *  'localhost']);
+     *
+     * // bool(true)
+     * $redis->ping();
+     *
+     * // string(9) "beep boop"
+     * $redis->ping('beep boop');
+     * ?>
+     * 
      */
     public function ping(string $message = NULL): Redis|string|bool;
 
+    /**
+     * Enter into pipeline mode.
+     *
+     * Pipeline mode is the highest performance way to send many commands to Redis
+     * as they are aggregated into one stream of commands and then all sent at once
+     * when the user calls Redis::exec().
+     *
+     * NOTE:  That this is shorthand for Redis::multi(Redis::PIPELINE)
+     *
+     * @return Redis The redis object is returned, to facilitate method chaining.
+     *
+     * 
+     *  'localhost']);
+     *
+     * // array(3) {
+     * //   [0]=>
+     * //   bool(true)
+     * //   [1]=>
+     * //   int(0)
+     * //   [2]=>
+     * //   int(3)
+     * // }
+     * $redis->pipeline()
+     *       ->set('foo', 'bar')
+     *       ->del('mylist')
+     *       ->rpush('mylist', 'a', 'b', 'c')
+     *       ->exec();
+     * ?>
+     * 
+     *
+     */
     public function pipeline(): bool|Redis;
 
     /**
@@ -1226,18 +1552,76 @@ public function popen(string $host, int $port = 6379, float $timeout = 0, string
     /** @return bool|Redis */
     public function psetex(string $key, int $expire, mixed $value);
 
+    /**
+     * Subscribe to one or more glob-style patterns
+     *
+     * @see https://redis.io/commands/psubscribe
+     *
+     * @param array     $patterns One or more patterns to subscribe to.
+     * @param callable  $cb       A callback with the following prototype:
+     *
+     *                            
+     *                            function ($redis, $channel, $message) { }
+     *                            
+     *
+     * @return bool True if we were subscribed.
+     */
     public function psubscribe(array $patterns, callable $cb): bool;
 
+    /**
+     * Get a keys time to live in milliseconds.
+     *
+     * @see https://redis.io/commands/pttl
+     *
+     * @param string $key The key to check.
+     *
+     * @return Redis|int|false The keys TTL or false on failure.
+     *
+     * NOTE:  -1 means a key has no TTL and -2 means the key doesn't exist.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->setex('ttl-key', 60, 'ttl-value');
+     *
+     * // int(60000)
+     * var_dump($redis->pttl('ttl-key'));
+     * ?>
+     * 
+     */
     public function pttl(string $key): Redis|int|false;
 
-    public function publish(string $channel, string $message): mixed;
+    /**
+     * Publish a message to a pubsub channel
+     *
+     * @see https://redis.io/commands/publish
+     *
+     * @param string $channel The channel to publish to.
+     * @param string $message The message itself.
+     *
+     * @return Redis|int The number of subscribed clients to the given channel.
+     */
+    public function publish(string $channel, string $message): Redis|int|false;
 
     public function pubsub(string $command, mixed $arg = null): mixed;
 
+    /**
+     * Unsubscribe from one or more channels by pattern
+     *
+     * @see https://redis.io/commands/punsubscribe
+     * @see https://redis.io/commands/subscribe
+     * @see Redis::subscribe()
+     *
+     * @param array $patterns One or more glob-style patterns of channel names.
+     *
+     * @return Redis|array|bool  The array of subscribed patterns or false on failure.
+     */
     public function punsubscribe(array $patterns): Redis|array|bool;
 
     /**
-     * Pop one or more elements from the end of a Redis LIST.
+     * Pop one or more elements from the end of a list.
      *
      * @see https://redis.io/commands/rpop
      *
@@ -1247,6 +1631,25 @@ public function punsubscribe(array $patterns): Redis|array|bool;
      * NOTE:  The `count` argument requires Redis >= 6.2.0
      *
      * @return Redis|array|string|bool One ore more popped elements or false if all were empty.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('mylist');
+     * $redis->rPush('mylist', 'one', 'two', 'three');
+     *
+     * // string(5) "three"
+     * $redis->rPop('mylist');
+     *
+     * // string(3) "two"
+     * $redis->rPop('mylist');
+     *
+     * // string(3) "one"
+     * $redis->rPop('mylist');
+     * ?>
+     * 
      */
     public function rPop(string $key, int $count = 0): Redis|array|string|bool;
 
@@ -1260,10 +1663,42 @@ public function rPop(string $key, int $count = 0): Redis|array|string|bool;
      */
     public function randomKey(): Redis|string|false;
 
+    /**
+     * Execute any arbitrary Redis command by name.
+     *
+     * @param string $command The command to execute
+     * @param mixed  $args    One or more arguments to pass to the command.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->rawCommand('del', 'mystring', 'mylist');
+     * $redis->rawCommand('set', 'mystring', 'myvalue');
+     * $redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three');
+     *
+     * // string(7) "myvalue"
+     * $redis->rawCommand('get', 'mystring');
+     *
+     * // array(3) {
+     * //   [0]=>
+     * //   string(3) "one"
+     * //   [1]=>
+     * //   string(3) "two"
+     * //   [2]=>
+     * //   string(5) "three"
+     * // }
+     * $redis->rawCommand('lrange', 'mylist', 0, -1);
+     * ?>
+     * 
+     */
     public function rawcommand(string $command, mixed ...$args): mixed;
 
     /**
-     * Rename a key
+     * Unconditionally rename a key from $old_name to $new_name
+     *
+     * @see https://redis.io/commands/rename
      *
      * @param string $old_name The original name of the key
      * @param string $new_name The new name for the key
@@ -1272,8 +1707,34 @@ public function rawcommand(string $command, mixed ...$args): mixed;
      */
     public function rename(string $old_name, string $new_name): Redis|bool;
 
-    /** @return bool|Redis */
-    public function renameNx(string $key_src, string $key_dst);
+    /**
+     * Renames $key_src to $key_dst but only if newkey does not exist.
+     *
+     * @see https://redis.io/commands/renamenx
+     *
+     * @param string $key_src The source key name
+     * @param string $key_dst The destination key name.
+     *
+     * @return Redis|bool True if the key was renamed, false if not.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('src', 'dst', 'existing-dst');
+     *
+     * $redis->set('src', 'src_key');
+     * $redis->set('existing-dst', 'i_exist');
+     *
+     * // bool(true)
+     * $redis->renamenx('src', 'dst');
+     *
+     * // bool(false)
+     * $redis->renamenx('dst', 'existing-dst');
+     * ?>
+     * 
+     */
+    public function renameNx(string $key_src, string $key_dst): Redis|bool;
 
     /**
      * Reset the state of the connection.
@@ -1282,7 +1743,66 @@ public function renameNx(string $key_src, string $key_dst);
      */
     public function reset(): Redis|bool;
 
-    public function restore(string $key, int $timeout, string $value, ?array $options = NULL): bool;
+    /**
+     * Restore a key by the binary payload generated by the DUMP command.
+     *
+     * @see https://redis.io/commands/restore
+     * @see https://redis.io/commands/dump
+     * @see Redis::dump()
+     *
+     * @param string $key     The name of the key you wish to create.
+     * @param int    $ttl     What Redis should set the key's TTL (in milliseconds) to once it is created.
+     *                        Zero means no TTL at all.
+     * @param string $value   The serialized binary value of the string (generated by DUMP).
+     * @param array  $options An array of additional options that modifies how the command operates.
+     *
+     *                        
+     *                        $options = [
+     *                            'ABSTTL'          // If this is present, the `$ttl` provided by the user should
+     *                                              // be an absolute timestamp, in milliseconds()
+     *
+     *                            'REPLACE'         // This flag instructs Redis to store the key even if a key with
+     *                                              // that name already exists.
+     *
+     *                            'IDLETIME' => int // Tells Redis to set the keys internal 'idletime' value to a
+     *                                              // specific number (see the Redis command OBJECT for more info).
+     *                            'FREQ'     => int // Tells Redis to set the keys internal 'FREQ' value to a specific
+     *                                              // number (this relates to Redis' LFU eviction algorithm).
+     *                        ];
+     *                        
+     *
+     * @return Redis|bool     True if the key was stored, false if not.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('captains');
+     *
+     * $redis->sAdd('captains', 'Janeway', 'Picard', 'Sisko', 'Kirk', 'Archer');
+     *
+     * $serialized = $redis->dump('captains');
+     *
+     * $redis->select(1);
+     * $redis->restore('captains-backup', 0, $serialized);
+     *
+     * //array(5) {
+     * //  [0]=>
+     * //  string(6) "Archer"
+     * //  [1]=>
+     * //  string(4) "Kirk"
+     * //  [2]=>
+     * //  string(5) "Sisko"
+     * //  [3]=>
+     * //  string(6) "Picard"
+     * //  [4]=>
+     * //  string(7) "Janeway"
+     * //}
+     * var_dump($redis->sMembers('captains-backup'));
+     * ?>
+     * 
+     */
+    public function restore(string $key, int $ttl, string $value, ?array $options = NULL): Redis|bool;
 
     /**
      * Query whether the connected instance is a primary or replica
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 0a54fec232..df218e356e 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 4c4d58bf2ce686c82287a69fef109267828a2d9b */
+ * Stub hash: 35a49b804f7cb67b7cd0a9a1094125855addaf1e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -350,27 +350,29 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lcs, 0, 2, Redis
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis_getDBNum
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getReadTimeout, 0, 0, IS_DOUBLE, 0)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getset, 0, 2, Redis, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_getTimeout arginfo_class_Redis_getDBNum
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getTimeout, 0, 0, MAY_BE_DOUBLE|MAY_BE_FALSE)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getTransferredBytes, 0, 0, MAY_BE_LONG|MAY_BE_FALSE)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hDel, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
-	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, other_fields, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hExists, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGet, 0, 2, IS_MIXED, 0)
@@ -439,17 +441,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Red
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incr, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1")
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_incr arginfo_class_Redis_decr
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrBy, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
-ZEND_END_ARG_INFO()
+#define arginfo_class_Redis_incrBy arginfo_class_Redis_decrBy
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_incrByFloat, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_incrByFloat, 0, 2, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
 ZEND_END_ARG_INFO()
@@ -631,7 +627,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_pttl arginfo_class_Redis_expiretime
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_publish, 0, 2, IS_MIXED, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_publish, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -663,16 +659,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rename, 0, 2, Re
 	ZEND_ARG_TYPE_INFO(0, new_name, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_renameNx, 0, 0, 2)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_renameNx, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key_src, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, key_dst, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_reset arginfo_class_Redis_bgSave
 
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_restore, 0, 3, _IS_BOOL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_restore, 0, 3, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
 ZEND_END_ARG_INFO()
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 8138c8bb08..4240ca1cc8 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 4c4d58bf2ce686c82287a69fef109267828a2d9b */
+ * Stub hash: 35a49b804f7cb67b7cd0a9a1094125855addaf1e */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -332,14 +332,21 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_getTransferredBytes arginfo_class_Redis___destruct
 
-#define arginfo_class_Redis_hDel arginfo_class_Redis_geohash
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hDel, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, field)
+	ZEND_ARG_VARIADIC_INFO(0, other_fields)
+ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hExists, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_INFO(0, field)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_hGet arginfo_class_Redis_hExists
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hGet, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_hGetAll arginfo_class_Redis__prefix
 
@@ -371,7 +378,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_hSetNx arginfo_class_Redis_hIncrBy
 
-#define arginfo_class_Redis_hStrLen arginfo_class_Redis_hExists
+#define arginfo_class_Redis_hStrLen arginfo_class_Redis_hGet
 
 #define arginfo_class_Redis_hVals arginfo_class_Redis__prefix
 
@@ -571,7 +578,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_restore, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, ttl)
 	ZEND_ARG_INFO(0, value)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
@@ -897,7 +904,7 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_zRandMember arginfo_class_Redis_getEx
 
-#define arginfo_class_Redis_zRank arginfo_class_Redis_hExists
+#define arginfo_class_Redis_zRank arginfo_class_Redis_hGet
 
 #define arginfo_class_Redis_zRem arginfo_class_Redis_geohash
 
@@ -929,9 +936,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRangeByScore, 0, 0, 3)
 	ZEND_ARG_INFO(0, options)
 ZEND_END_ARG_INFO()
 
-#define arginfo_class_Redis_zRevRank arginfo_class_Redis_hExists
+#define arginfo_class_Redis_zRevRank arginfo_class_Redis_hGet
 
-#define arginfo_class_Redis_zScore arginfo_class_Redis_hExists
+#define arginfo_class_Redis_zScore arginfo_class_Redis_hGet
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zdiff, 0, 0, 1)
 	ZEND_ARG_INFO(0, keys)

From 450904f75c2e4d45f29813bb73d01c6a0ff61ddb Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Mon, 7 Nov 2022 17:45:08 -0800
Subject: [PATCH 1701/1986] Documentation:  More docblocks with examples.

---
 redis.stub.php         | 306 ++++++++++++++++++++++++++++++++++++++++-
 redis_arginfo.h        |  16 +--
 redis_legacy_arginfo.h |  16 ++-
 tests/RedisTest.php    |   2 +-
 4 files changed, 318 insertions(+), 22 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index aad71f3d6c..39e94ad0ae 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -1215,29 +1215,321 @@ public function hGet(string $key, string $member): mixed;
      */
     public function hGetAll(string $key): Redis|array|false;
 
-    public function hIncrBy(string $key, string $member, int $value): Redis|int|false;
+    /**
+     * Increment a hash field's value by an integer
+     *
+     * @see https://redis.io/commands/hincrby
+     *
+     * @param string $key   The hash to modify
+     * @param string $field The field to increment
+     * @param int    $value How much to increment the value.
+     *
+     * @return Redis|int|false The new value of the field.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('player');
+     *
+     * $redis->hmset('player', ['name' => 'Bob', 'level' => 1]);
+     *
+     * // int(2)
+     * $redis->hIncrBy('player', 'level', 1);
+     *
+     * // int(5)
+     * $redis->hIncrBy('player', 'level', 3);
+     * ?>
+     * 
+     *
+     */
+    public function hIncrBy(string $key, string $field, int $value): Redis|int|false;
 
-    public function hIncrByFloat(string $key, string $member, float $value): Redis|float|false;
+    /**
+     * Increment a hash field by a floating point value
+     *
+     * @see https://redis.io/commands/hincrbyfloat
+     *
+     * @param string $key The hash with the field to increment.
+     * @param string $field The field to increment.
+     *
+     * @return Redis|float|false The field value after incremented.
+     *
+     * 
+     * $redis = new Redis(['host' => 'localhost']);
+     *
+     * $redis->del('trig-numbers')
+     *
+     * // float(3.1415926)
+     * $pi = $redis->hIncrByFloat('trig-numbers', 'pi', 3.1415926);
+     *
+     * // float(6.2831852)
+     * $redis->hIncrByFloat('trig-numbers', 'tau', 2 * $pi);
+     * ?>
+     * 
+     */
+    public function hIncrByFloat(string $key, string $field, float $value): Redis|float|false;
 
+    /**
+     * Retrieve all of the fields of a hash.
+     *
+     * @see https://redis.io/commands/hkeys
+     *
+     * @param string $key The hash to query.
+     *
+     * @return Redis|array|false The fields in the hash or false if the hash doesn't exist.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('ships');
+     *
+     * $redis->hmset('ships', ['Enterprise' => 'NCC-1701D', 'Defiant' => 'NX-74205', 'Voyager' => 'NCC-74656']);
+     *
+     * // array(3) {
+     * //   [0]=>
+     * //   string(10) "Enterprise"
+     * //   [1]=>
+     * //   string(7) "Defiant"
+     * //   [2]=>
+     * //   string(7) "Voyager"
+     * // }
+     * $redis->hKeys('ships');
+     * ?>
+     * 
+     */
     public function hKeys(string $key): Redis|array|false;
 
+    /**
+     * Get the number of fields in a hash.
+     *
+     * @see https://redis.io/commands/hlen
+     *
+     * @param string $key The hash to check.
+     *
+     * @return Redis|int|false The number of fields or false if the key didn't exist.
+     */
     public function hLen(string $key): Redis|int|false;
 
-    public function hMget(string $key, array $keys): Redis|array|false;
+    /**
+     * Get one or more fields from a hash.
+     *
+     * @see https://redis.io/commands/hmget
+     *
+     * @param string $key    The hash to query.
+     * @param array  $fields One or more fields to query in the hash.
+     *
+     * @return Redis|array|false The fields and values or false if the key didn't exist.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('player:1');
+     *
+     * $redis->hmset('player:1', ['name' => 'Alice', 'age' => '26', 'score' => '1337']);
+     *
+     * // array(2) {
+     * //   ["name"]=>
+     * //   string(5) "Alice"
+     * //   ["score"]=>
+     * //   string(4) "1337"
+     * // }
+     * $redis->hmget('player:1', ['name', 'score']);
+     * ?>
+     * 
+     */
+    public function hMget(string $key, array $fields): Redis|array|false;
 
-    public function hMset(string $key, array $keyvals): Redis|bool;
+    /**
+     * Add or update one or more hash fields and values
+     *
+     * @see https://redis.io/commands/hmset
+     *
+     * @param string $key        The hash to create/update
+     * @param array  $fieldvals  An associative array with fields and their values.
+     *
+     * @return Redis|bool True if the operation was successful
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]);
+     * ?>
+     * 
+     */
+    public function hMset(string $key, array $fieldvals): Redis|bool;
 
+    /**
+     * Get one or more random field from a hash.
+     *
+     * @see https://redis.io/commands/hrandfield
+     *
+     * @param string $key     The hash to query.
+     * @param array  $options An array of options to modify how the command behaves.
+     *
+     *                        
+     *                        $options = [
+     *                            'COUNT'      => int  // An optional number of fields to return.
+     *                            'WITHVALUES' => bool // Also return the field values.
+     *                        ];
+     *                        
+     *
+     * @return Redis|array|string One or more random fields (and possibly values).
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('settings');
+     *
+     * $redis->hmset('settings', ['path' => '/', 'state' => 'active', 'jobs' => 15]);
+     *
+     * $redis->hrandfield('settings');
+     *
+     * $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);
+     * ?>
+     * 
+     */
     public function hRandField(string $key, array $options = null): Redis|string|array;
 
     public function hSet(string $key, string $member, mixed $value): Redis|int|false;
 
-    public function hSetNx(string $key, string $member, string $value): Redis|bool;
+    /**
+     * Set a hash field and value, but only if that field does not exist
+     *
+     * @see https://redis.io/commands/hsetnx
+     *
+     * @param string $key   The hash to update.
+     * @param string $field The value to set.
+     *
+     * @return Redis|bool True if the field was set and false if not.
+     *
+     * 
+     * $redis = new Redis(['host' => 'localhost']);
+     *
+     * $redis->del('player:1');
+     *
+     * $redis->hmset('player:1', ['name' => 'bob', 'score' => 0]);
+     *
+     * // bool(true)
+     * var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));
+     *
+     * // bool(false)
+     * var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));
+     * 
+     */
+    public function hSetNx(string $key, string $field, string $value): Redis|bool;
 
-    public function hStrLen(string $key, string $member): Redis|int|false;
+    /**
+     * Get the string length of a hash field
+     *
+     * @see https://redis.io/commands/hstrlen
+     *
+     * @param string $key   The hash to query.
+     * @param string $field The field to query.
+     *
+     * @return Redis|int|false The string length of the field or false.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('hash');
+     * $redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]);
+     *
+     * // int(50)
+     * $redis->hstrlen('hash', '50bytes');
+     *
+     * 
+     */
+    public function hStrLen(string $key, string $field): Redis|int|false;
 
+    /**
+     * Get all of the values from a hash.
+     *
+     * @see https://redis.io/commands/hvals
+     *
+     * @param string $key The hash to query.
+     *
+     * @return Redis|array|false The values from the hash.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('player');
+     *
+     * $redis->hmset('player', ['name' => 'Alice', 'score' => 1337]);
+     *
+     * // array(2) {
+     * //   ["name"]=>
+     * //   string(5) "Alice"
+     * //   ["score"]=>
+     * //   string(4) "1337"
+     * // }
+     * $redis->hgetall('player');
+     * ?>
+     * 
+     */
     public function hVals(string $key): Redis|array|false;
 
-    public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|bool|array;
+
+    /**
+     * Iterate over the fields and values of a hash in an incremental fashion.
+     *
+     * @see https://redis.io/commands/hscan
+     * @see https://redis.io/commands/scan
+     *
+     * @param string $key       The hash to query.
+     * @param int    $iterator  The scan iterator, which should be initialized to NULL before the first call.
+     *                          This value will be updated after every call to hscan, until it reaches zero
+     *                          meaning the scan is complete.
+     * @param string $pattern   An optional glob-style pattern to filter fields with.
+     * @param int    $count     An optional hint to Redis about how many fields and values to return per HSCAN.
+     *
+     * @return Redis|array|bool An array with a subset of fields and values.
+     *
+     * 
+     *  'localhost']);
+     *
+     * $redis->del('big-hash');
+     *
+     * for ($i = 0; $i < 1000; $i++) {
+     *     $fields["field:$i"] = "value:$i";
+     * }
+     *
+     * $redis->hmset('big-hash', $fields);
+     *
+     * $it = NULL;
+     *
+     * do {
+     *     // Scan the hash but limit it to fields that match '*:1?3'
+     *     $fields = $redis->hscan('big-hash', $it, '*:1?3');
+     *
+     *     foreach ($fields as $field => $value) {
+     *         echo "[$field] => $value\n";
+     *     }
+     * } while ($it != 0);
+     *
+     * // --- OUTPUT ---
+     * // [field:143] => value:143
+     * // [field:133] => value:133
+     * // [field:163] => value:163
+     * // [field:183] => value:183
+     * // [field:153] => value:153
+     * // [field:113] => value:113
+     * // [field:103] => value:103
+     * // [field:193] => value:193
+     * // [field:123] => value:123
+     * // [field:173] => value:173
+     * ?>
+     * 
+     */
+    public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool;
 
     /**
      * Increment a key's value, optionally by a specifc amount.
diff --git a/redis_arginfo.h b/redis_arginfo.h
index df218e356e..d11cd333cb 100644
--- a/redis_arginfo.h
+++ b/redis_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 35a49b804f7cb67b7cd0a9a1094125855addaf1e */
+ * Stub hash: c95a6704d3c51686748694926d6f4b0f55a2f3df */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -386,13 +386,13 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrBy, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrByFloat, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
 ZEND_END_ARG_INFO()
 
@@ -402,12 +402,12 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMget, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMset, 0, 2, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, keyvals, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, fieldvals, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY)
@@ -423,18 +423,18 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSetNx, 0, 3, Redis, MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hStrLen, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_hVals arginfo_class_Redis_hGetAll
 
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
 	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null")
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 4240ca1cc8..cde9a8715d 100644
--- a/redis_legacy_arginfo.h
+++ b/redis_legacy_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 35a49b804f7cb67b7cd0a9a1094125855addaf1e */
+ * Stub hash: c95a6704d3c51686748694926d6f4b0f55a2f3df */
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
 	ZEND_ARG_INFO(0, options)
@@ -352,7 +352,7 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hIncrBy, 0, 0, 3)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_INFO(0, field)
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()
 
@@ -364,21 +364,25 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMget, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, keys)
+	ZEND_ARG_INFO(0, fields)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMset, 0, 0, 2)
 	ZEND_ARG_INFO(0, key)
-	ZEND_ARG_INFO(0, keyvals)
+	ZEND_ARG_INFO(0, fieldvals)
 ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_hRandField arginfo_class_Redis_getEx
 
-#define arginfo_class_Redis_hSet arginfo_class_Redis_hIncrBy
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hSet, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, member)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
 
 #define arginfo_class_Redis_hSetNx arginfo_class_Redis_hIncrBy
 
-#define arginfo_class_Redis_hStrLen arginfo_class_Redis_hGet
+#define arginfo_class_Redis_hStrLen arginfo_class_Redis_hExists
 
 #define arginfo_class_Redis_hVals arginfo_class_Redis__prefix
 
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index a88d800958..606c4e738f 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -931,7 +931,7 @@ public function testExists() {
 
         /* 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));
+        $this->assertEquals(count($mkeys), $this->redis->exists(...$mkeys));
     }
 
     public function testTouch() {

From 6d595b35e932dd37ddd7ceb9299185e0ba10ec4f Mon Sep 17 00:00:00 2001
From: michael-grunder 
Date: Tue, 8 Nov 2022 14:11:08 -0800
Subject: [PATCH 1702/1986] Fix xtrim stub

[no ci]
---
 redis.stub.php | 2 --
 1 file changed, 2 deletions(-)

diff --git a/redis.stub.php b/redis.stub.php
index 39e94ad0ae..2025a1b035 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -3950,8 +3950,6 @@ public function xrevrange(string $key, string $end, string $start, int $count =
      *                                   messages older than this ID.
      * @param bool   $approx    Whether redis is allowed to do an approximate trimming of the stream.  This is
      *                          more efficient for Redis given how streams are stored internally.
-     * @param int    $count     An optional upper bound on how many entries Redis should attempt to trim before
-     *                          returning to the caller.
      * @param bool   $minid     When set to `true`, users should pass a minimum ID to the `$threshold` argument.
      * @param int    $limit     An optional upper bound on how many entries to trim during the command.
      *

From 7930a7887565c77afb5eb1ebfec65ce54cee8f7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Till=20Kr=C3=BCss?= 
Date: Tue, 8 Nov 2022 14:12:38 -0800
Subject: [PATCH 1703/1986] Move issue template (#2247)

* move issue template
* ignore /.github; + cleanup
---
 ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md |  0
 .gitignore                                     | 15 ++++++++-------
 2 files changed, 8 insertions(+), 7 deletions(-)
 rename ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md (100%)

diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
similarity index 100%
rename from ISSUE_TEMPLATE.md
rename to .github/ISSUE_TEMPLATE.md
diff --git a/.gitignore b/.gitignore
index aef8e5cd8a..9254656db6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,21 +1,22 @@
+/.github
+/.idea
+/.vscode
+.cquery
 *.deps
 *.libs
+*.o
+*.lo
 Makefile*
+configure*
 ac*.m4
 config.*
-*.o
 install-sh
 libtool
 ./*.sh
-configure*
-*.lo
 build*
 missing
 autom4te.cache
 mkinstalldirs
-run-tests.php
-idea/*
-.cquery
 tags
-.vscode/*
 compile_commands.json
+run-tests.php

From 114f4d605635cfa8755000ff4659680c713ceb06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Till=20Kr=C3=BCss?= 
Date: Tue, 8 Nov 2022 14:49:42 -0800
Subject: [PATCH 1704/1986] Uniform meta file names (#2248)

* rename changelog to CHANGELOG
* Rename COPYING to LICENSE
* Rename cluster.markdown to cluster.md
* Rename README.markdown to README.md
* Rename INSTALL.markdown to INSTALL.md\
* Rename sentinel.markdown to sentinel.md
* Rename arrays.markdown to array.md
* fix all references
---
 Changelog.md => CHANGELOG.md     |  2 --
 INSTALL.markdown => INSTALL.md   |  2 +-
 COPYING => LICENSE               |  0
 README.markdown => README.md     | 10 +++++-----
 arrays.markdown => array.md      |  0
 cluster.markdown => cluster.md   |  0
 package.xml                      | 24 ++++++++++++------------
 sentinel.markdown => sentinel.md |  0
 8 files changed, 18 insertions(+), 20 deletions(-)
 rename Changelog.md => CHANGELOG.md (99%)
 rename INSTALL.markdown => INSTALL.md (92%)
 rename COPYING => LICENSE (100%)
 rename README.markdown => README.md (99%)
 rename arrays.markdown => array.md (100%)
 rename cluster.markdown => cluster.md (100%)
 rename sentinel.markdown => sentinel.md (100%)

diff --git a/Changelog.md b/CHANGELOG.md
similarity index 99%
rename from Changelog.md
rename to CHANGELOG.md
index 70f22987f0..c345854e68 100644
--- a/Changelog.md
+++ b/CHANGELOG.md
@@ -7,8 +7,6 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ## [Unreleased]
 
-
-
 ## [5.3.5RC1] - 2021-11-16 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.5RC1), [PECL](https://pecl.php.net/package/redis/5.3.5RC1))
 
 ### Sponsors :sparkling_heart:
diff --git a/INSTALL.markdown b/INSTALL.md
similarity index 92%
rename from INSTALL.markdown
rename to INSTALL.md
index 7e8e025c55..bd3fbd985a 100644
--- a/INSTALL.markdown
+++ b/INSTALL.md
@@ -28,7 +28,7 @@ The extension also may compress data before sending it to Redis server, if you r
 
 You can generate a debian package for PHP5, accessible from Apache 2 by running `./mkdeb-apache2.sh` or with `dpkg-buildpackage` or `svn-buildpackage`.
 
-This extension exports a single class, [Redis](./README.markdown#class-redis) (and [RedisException](./README.markdown#class-redisexception) used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion.
+This extension exports a single class, [Redis](./README.md#class-redis) (and [RedisException](./README.md#class-redisexception) used in case of errors). Check out https://github.com/ukko/phpredis-phpdoc for a PHP stub that you can use in your IDE for code completion.
 
 
 # Binary packages
diff --git a/COPYING b/LICENSE
similarity index 100%
rename from COPYING
rename to LICENSE
diff --git a/README.markdown b/README.md
similarity index 99%
rename from README.markdown
rename to README.md
index 6f2d7b15d6..efa85e9b17 100644
--- a/README.markdown
+++ b/README.md
@@ -31,9 +31,9 @@ You can also make a one-time contribution with one of the links below.
 1. [Installing/Configuring](#installingconfiguring)
    * [Installation](#installation)
    * [PHP Session handler](#php-session-handler)
-   * [Distributed Redis Array](./arrays.markdown#readme)
-   * [Redis Cluster support](./cluster.markdown#readme)
-   * [Redis Sentinel support](./sentinel.markdown#readme)
+   * [Distributed Redis Array](./array.md#readme)
+   * [Redis Cluster support](./cluster.md#readme)
+   * [Redis Sentinel support](./sentinel.md#readme)
    * [Running the unit tests](#running-the-unit-tests)
 1. [Classes and methods](#classes-and-methods)
    * [Usage](#usage)
@@ -61,7 +61,7 @@ You can also make a one-time contribution with one of the links below.
 ## Installation
 
 For everything you should need to install PhpRedis on your system,
-see the [INSTALL.markdown](./INSTALL.markdown) page.
+see the [INSTALL.md](./INSTALL.md) page.
 
 ## PHP Session handler
 
@@ -1173,7 +1173,7 @@ _**Description**_:  Scan the keyspace for keys
 ##### *Return value*
 *Array, boolean*:  This function will return an array of keys or FALSE if Redis returned zero keys
 
-*Note*: SCAN is a "directed node" command in [RedisCluster](cluster.markdown#directed-node-commands)
+*Note*: SCAN is a "directed node" command in [RedisCluster](cluster.md#directed-node-commands)
 
 ##### *Example*
 ~~~php
diff --git a/arrays.markdown b/array.md
similarity index 100%
rename from arrays.markdown
rename to array.md
diff --git a/cluster.markdown b/cluster.md
similarity index 100%
rename from cluster.markdown
rename to cluster.md
diff --git a/package.xml b/package.xml
index 350aef5b09..cdc815a526 100644
--- a/package.xml
+++ b/package.xml
@@ -44,7 +44,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
     support for detecting a dirty connection, as well as many other fixes
     and improvements.
 
-    You can find a detailed list of changes in Changelog.md and package.xml
+    You can find a detailed list of changes in CHANGELOG.md and package.xml
     or by inspecting the git commit logs.
 
     --- Sponsors ---
@@ -116,13 +116,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
  
  
   
-   
+   
    
-   
-   
-   
-   
-   
+   
+   
+   
+   
+   
    
    
    
@@ -213,7 +213,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
     This release fixes a multi/pipeline segfault on apple silicon as well as
     two small compression related bugs.
 
-    You can find a detailed list of changes in Changelog.md and package.xml
+    You can find a detailed list of changes in CHANGELOG.md and package.xml
 
     * Fix multi/pipeline segfault on Apple silicon [e0796d48] (Michael Grunder)
     * Pass compression flag on HMGET in RedisCluster [edc724e6] (Adam Olley)
@@ -240,7 +240,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
     This release mostly includes just small PHP 8 Windows compatibility fixes
     such that pecl.php.net can automatically build Windows DLLs.
 
-    You can find a detailed list of changes in Changelog.md and package.xml
+    You can find a detailed list of changes in CHANGELOG.md and package.xml
 
     * Fix PHP8 Windows includes [270b4db8] (Jan-E)
     * Fix hash ops for php 8.0.1 [87297cbb] (defender-11)
@@ -263,7 +263,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
    2020-10-22
    
     This release containse some bugfixes and small improvements.
-    You can find a detailed list of changes in Changelog.md and package.xml
+    You can find a detailed list of changes in CHANGELOG.md and package.xml
 
     * Sponsors
       ~ Audiomack - https://audiomack.com
@@ -318,7 +318,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
     of if you're having trouble building 5.3.0 because the php_hash_bin2hex
     symbol is missing.
 
-    You can find a detailed list of changes in Changelog.md and package.xml
+    You can find a detailed list of changes in CHANGELOG.md and package.xml
 
     * Sponsors
       ~ Audiomack - https://audiomack.com
@@ -348,7 +348,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
     This release contains initial support for Redis 6 ACLs, LZ4 compression,
     and many more fixes and improvements.
 
-    You can find a detailed list of changes in Changelog.md and package.xml
+    You can find a detailed list of changes in CHANGELOG.md and package.xml
 
     A special thanks to BlueHost for sponsoring ACL support \o/
 
diff --git a/sentinel.markdown b/sentinel.md
similarity index 100%
rename from sentinel.markdown
rename to sentinel.md

From 142bddf0b71c758a0e2d8930277b61c015ed8322 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Till=20Kr=C3=BCss?= 
Date: Tue, 8 Nov 2022 14:59:24 -0800
Subject: [PATCH 1705/1986] Add initial docs (#2249)

Initial support for `doctum` generated documentation.
---
 .gitattributes                   |     5 +
 .gitignore                       |     4 +-
 docs/DOCTUM_VERSION              |     1 +
 docs/PROJECT_VERSION             |     1 +
 docs/Redis.html                  | 19133 +++++++++++++++++++++++++++++
 docs/RedisArray.html             |  1739 +++
 docs/RedisCluster.html           | 14846 ++++++++++++++++++++++
 docs/RedisClusterException.html  |   103 +
 docs/RedisException.html         |   103 +
 docs/RedisSentinel.html          |   748 ++
 docs/[Global_Namespace].html     |   134 +
 docs/classes.html                |   120 +
 docs/css/bootstrap-theme.min.css |     7 +
 docs/css/bootstrap.min.css       |     7 +
 docs/css/doctum.css              |   508 +
 docs/doc-index.html              |  1187 ++
 docs/doctum-search.json          |     1 +
 docs/doctum.js                   |   316 +
 docs/fonts/doctum-font.css       |    61 +
 docs/fonts/doctum.eot            |   Bin 0 -> 5100 bytes
 docs/fonts/doctum.svg            |    14 +
 docs/fonts/doctum.ttf            |   Bin 0 -> 4940 bytes
 docs/fonts/doctum.woff           |   Bin 0 -> 2868 bytes
 docs/fonts/doctum.woff2          |   Bin 0 -> 2248 bytes
 docs/index.html                  |   120 +
 docs/interfaces.html             |    90 +
 docs/js/autocomplete.min.js      |     5 +
 docs/js/bootstrap.min.js         |    11 +
 docs/js/jquery-3.5.1.slim.min.js |     2 +
 docs/namespaces.html             |    93 +
 docs/opensearch.xml              |     9 +
 docs/renderer.index              |     1 +
 docs/search.html                 |   297 +
 docs/traits.html                 |    89 +
 doctum-config.php                |    28 +
 doctum.md                        |     8 +
 36 files changed, 39790 insertions(+), 1 deletion(-)
 create mode 100644 .gitattributes
 create mode 100644 docs/DOCTUM_VERSION
 create mode 100644 docs/PROJECT_VERSION
 create mode 100644 docs/Redis.html
 create mode 100644 docs/RedisArray.html
 create mode 100644 docs/RedisCluster.html
 create mode 100644 docs/RedisClusterException.html
 create mode 100644 docs/RedisException.html
 create mode 100644 docs/RedisSentinel.html
 create mode 100644 docs/[Global_Namespace].html
 create mode 100644 docs/classes.html
 create mode 100644 docs/css/bootstrap-theme.min.css
 create mode 100644 docs/css/bootstrap.min.css
 create mode 100644 docs/css/doctum.css
 create mode 100644 docs/doc-index.html
 create mode 100644 docs/doctum-search.json
 create mode 100644 docs/doctum.js
 create mode 100644 docs/fonts/doctum-font.css
 create mode 100644 docs/fonts/doctum.eot
 create mode 100644 docs/fonts/doctum.svg
 create mode 100644 docs/fonts/doctum.ttf
 create mode 100644 docs/fonts/doctum.woff
 create mode 100644 docs/fonts/doctum.woff2
 create mode 100644 docs/index.html
 create mode 100644 docs/interfaces.html
 create mode 100644 docs/js/autocomplete.min.js
 create mode 100644 docs/js/bootstrap.min.js
 create mode 100644 docs/js/jquery-3.5.1.slim.min.js
 create mode 100644 docs/namespaces.html
 create mode 100644 docs/opensearch.xml
 create mode 100644 docs/renderer.index
 create mode 100644 docs/search.html
 create mode 100644 docs/traits.html
 create mode 100644 doctum-config.php
 create mode 100644 doctum.md

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..41783f2349
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+/.github export-ignore
+/docs export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.gitmodules export-ignore
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 9254656db6..f858a89c41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 /.github
 /.idea
 /.vscode
+/docs/.cache
 .cquery
 *.deps
 *.libs
@@ -19,4 +20,5 @@ autom4te.cache
 mkinstalldirs
 tags
 compile_commands.json
-run-tests.php
+doctum.phar
+run-tests.php
\ No newline at end of file
diff --git a/docs/DOCTUM_VERSION b/docs/DOCTUM_VERSION
new file mode 100644
index 0000000000..d41f08f1f3
--- /dev/null
+++ b/docs/DOCTUM_VERSION
@@ -0,0 +1 @@
+5.5.1
\ No newline at end of file
diff --git a/docs/PROJECT_VERSION b/docs/PROJECT_VERSION
new file mode 100644
index 0000000000..ce57f64563
--- /dev/null
+++ b/docs/PROJECT_VERSION
@@ -0,0 +1 @@
+develop
\ No newline at end of file
diff --git a/docs/Redis.html b/docs/Redis.html
new file mode 100644
index 0000000000..cfc5a250e0
--- /dev/null
+++ b/docs/Redis.html
@@ -0,0 +1,19133 @@
+
+
+
+    
+    
+    Redis | PhpRedis API
+
+            
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+
+        
+    
+
+    
+            
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + + +

class + Redis (View source) +

+ + + + + + + + + +

Methods

+ +
+
+
+ +
+
+ __construct(array $options = null) + +

Create a new Redis instance. If passed sufficient information in the +options array it is also possible to connect to an instance at the same +time.

+
+
+
+
+ +
+
+ __destruct() + +

No description

+
+
+
+
+
+ string +
+
+ _compress(string $value) + +

Compress a value with the currently configured compressor as set with +Redis::setOption().

+
+
+
+
+ string +
+
+ _uncompress(string $value) + +

Uncompress the provided argument that has been compressed with the +currently configured compressor as set with Redis::setOption().

+
+
+
+
+ string +
+
+ _prefix(string $key) + +

Prefix the passed argument with the currently set key prefix as set +with Redis::setOption().

+
+
+
+
+ string +
+
+ _serialize(mixed $value) + +

Serialize the provided value with the currently set serializer as set +with Redis::setOption().

+
+
+
+
+ mixed +
+
+ _unserialize(string $value) + +

Unserialize the passed argument with the currently set serializer as set +with Redis::setOption().

+
+
+
+
+ string +
+
+ _pack(mixed $value) + +

Pack the provided value with the configured serializer and compressor +as set with Redis::setOption().

+
+
+
+
+ mixed +
+
+ _unpack(string $value) + +

Unpack the provided value with the configured compressor and serializer +as set with Redis::setOption().

+
+
+
+
+ mixed +
+
+ acl(string $subcmd, string ...$args) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ append(string $key, mixed $value) + +

Append data to a Redis STRING key.

+
+
+
+
+ Redis|bool +
+
+ auth(mixed $credentials) + +

Authenticate a Redis connection after its been established.

+
+
+
+
+ Redis|bool +
+
+ bgSave() + +

Execute a save of the Redis database in the background.

+
+
+
+
+ Redis|bool +
+
+ bgrewriteaof() + +

Asynchronously rewrite Redis' append-only file

+
+
+
+
+ Redis|int|false +
+
+ bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) + +

Count the number of set bits in a Redis string.

+
+
+
+
+ Redis|int|false +
+
+ bitop(string $operation, string $deskey, string $srckey, string ...$other_keys) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) + +

Return the position of the first bit set to 0 or 1 in a string.

+
+
+
+
+ Redis|array|null|false +
+
+ blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) + +

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified +timeout. This method may be called in two distinct ways, of which examples are provided below.

+
+
+
+
+ Redis|array|null|false +
+
+ brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) + +

Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

+
+
+
+
+ Redis|string|false +
+
+ brpoplpush(string $src, string $dst, int|float $timeout) + +

Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list, +optionally blocking up to a specified timeout.

+
+
+
+
+ Redis|array|false +
+
+ bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + +

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified +timeout if no elements are available.

+
+
+
+
+ Redis|array|false +
+
+ bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + +

POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout +if no elements are available

+
+
+
+
+ Redis|array|null|false +
+
+ bzmpop(float $timeout, array $keys, string $from, int $count = 1) + +

POP one or more elements from one or more sorted sets, blocking up to a specified amount of time +when no elements are available.

+
+
+
+
+ Redis|array|null|false +
+
+ zmpop(array $keys, string $from, int $count = 1) + +

POP one or more of the highest or lowest scoring elements from one or more sorted sets.

+
+
+
+
+ Redis|array|null|false +
+
+ blmpop(float $timeout, array $keys, string $from, int $count = 1) + +

Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when +no elements are available.

+
+
+
+
+ Redis|array|null|false +
+
+ lmpop(array $keys, string $from, int $count = 1) + +

Pop one or more elements off of one or more Redis LISTs.

+
+
+
+
+ bool +
+
+ clearLastError() + +

Reset any last error on the connection to NULL

+
+
+
+
+ mixed +
+
+ client(string $opt, mixed ...$args) + +

No description

+
+
+
+
+
+ bool +
+
+ close() + +

No description

+
+
+
+
+
+ mixed +
+
+ command(string $opt = null, string|array $arg) + +

No description

+
+
+
+
+
+ mixed +
+
+ config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) + +

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends +on the $operation qualifier.

+
+
+
+
+ bool +
+
+ connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null) + +

No description

+
+
+
+
+
+ Redis|bool +
+
+ copy(string $src, string $dst, array $options = null) + +

Make a copy of a redis key.

+
+
+
+
+ Redis|int|false +
+
+ dbSize() + +

Return the number of keys in the currently selected Redis database.

+
+
+
+
+ Redis|string +
+
+ debug(string $key) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ decr(string $key, int $by = 1) + +

Decrement a Redis integer by 1 or a provided value.

+
+
+
+
+ Redis|int|false +
+
+ decrBy(string $key, int $value) + +

Decrement a redis integer by a value

+
+
+
+
+ Redis|int|false +
+
+ del(array|string $key, string ...$other_keys) + +

Delete one or more keys from Redis.

+
+
+
+
+ Redis|int|false +
+
+ delete(array|string $key, string ...$other_keys) + deprecated +

No description

+
+
+
+
+
+ Redis|bool +
+
+ discard() + +

Discard a transaction currently in progress.

+
+
+
+
+ Redis|string +
+
+ dump(string $key) + +

Dump Redis' internal binary representation of a key.

+
+
+
+
+ Redis|string|false +
+
+ echo(string $str) + +

Have Redis repeat back an arbitrary string to the client.

+
+
+
+
+ mixed +
+
+ eval(string $script, array $args = [], int $num_keys = 0) + +

Execute a LUA script on the redis server.

+
+
+
+
+ mixed +
+
+ eval_ro(string $script_sha, array $args = [], int $num_keys = 0) + +

This is simply the read-only variant of eval, meaning the underlying script +may not modify data in redis.

+
+
+
+
+ mixed +
+
+ evalsha(string $sha1, array $args = [], int $num_keys = 0) + +

Execute a LUA script on the server but instead of sending the script, send +the SHA1 hash of the script.

+
+
+
+
+ mixed +
+
+ evalsha_ro(string $sha1, array $args = [], int $num_keys = 0) + +

This is simply the read-only variant of evalsha, meaning the underlying script +may not modify data in redis.

+
+
+
+
+ Redis|array|false +
+
+ exec() + +

Execute either a MULTI or PIPELINE block and return the array of replies.

+
+
+
+
+ Redis|int|bool +
+
+ exists(mixed $key, mixed ...$other_keys) + +

Test if one or more keys exist.

+
+
+
+
+ Redis|bool +
+
+ expire(string $key, int $timeout, string|null $mode = NULL) + +

Sets an expiration in seconds on the key in question. If connected to +redis-server >= 7.0.0 you may send an additional "mode" argument which +modifies how the command will execute.

+
+
+
+
+ Redis|bool +
+
+ expireAt(string $key, int $timestamp, string|null $mode = NULL) + +

Set a key's expiration to a specific Unix timestamp in seconds. If +connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

+
+
+
+
+ Redis|bool +
+
+ failover(array|null $to = null, bool $abort = false, int $timeout = 0) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ expiretime(string $key) + +

Get the expiration of a given key as a unix timestamp

+
+
+
+
+ Redis|int|false +
+
+ pexpiretime(string $key) + +

Get the expriation timestamp of a given Redis key but in milliseconds.

+
+
+
+
+ Redis|bool +
+
+ flushAll(bool|null $sync = null) + +

Deletes every key in all Redis databases

+
+
+
+
+ Redis|bool +
+
+ flushDB(bool|null $sync = null) + +

Deletes all the keys of the currently selected database.

+
+
+
+
+ Redis|int|false +
+
+ geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) + +

No description

+
+
+
+
+
+ Redis|float|false +
+
+ geodist(string $key, string $src, string $dst, string|null $unit = null) + +

No description

+
+
+
+
+
+ Redis|array|false +
+
+ geohash(string $key, string $member, string ...$other_members) + +

No description

+
+
+
+
+
+ Redis|array|false +
+
+ geopos(string $key, string $member, string ...$other_members) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ array +
+
+ geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ Redis|array|int|false +
+
+ geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ get(string $key) + +

No description

+
+
+
+
+
+ mixed +
+
+ getAuth() + +

Get the authentication information on the connection, if any.

+
+
+
+
+ Redis|int|false +
+
+ getBit(string $key, int $idx) + +

No description

+
+
+
+
+
+ Redis|string|bool +
+
+ getEx(string $key, array $options = []) + +

No description

+
+
+
+
+
+ int +
+
+ getDBNum() + +

No description

+
+
+
+
+
+ Redis|string|bool +
+
+ getDel(string $key) + +

No description

+
+
+
+
+
+ string +
+
+ getHost() + +

Return the host or Unix socket we are connected to.

+
+
+
+
+ string|null +
+
+ getLastError() + +

Get the last error returned to us from Redis, if any.

+
+
+
+
+ int +
+
+ getMode() + +

Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

+
+
+
+
+ mixed +
+
+ getOption(int $option) + +

Retrieve the value of a configuration setting as set by Redis::setOption()

+
+
+
+
+ string|null +
+
+ getPersistentID() + +

Get the persistent connection ID, if there is one.

+
+
+
+
+ int +
+
+ getPort() + +

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

+
+
+
+
+ Redis|string|false +
+
+ getRange(string $key, int $start, int $end) + +

Retrieve a substring of a string by index.

+
+
+
+
+ Redis|string|array|int|false +
+
+ lcs(string $key1, string $key2, array|null $options = NULL) + +

Get the longest common subsequence between two string keys.

+
+
+
+
+ float +
+
+ getReadTimeout() + +

Get the currently set read timeout on the connection.

+
+
+
+
+ Redis|string|false +
+
+ getset(string $key, mixed $value) + +

Sets a key and returns any previously set value, if the key already existed.

+
+
+
+
+ float|false +
+
+ getTimeout() + +

Retrieve any set connection timeout

+
+
+
+
+ int|false +
+
+ getTransferredBytes() + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ hDel(string $key, string $field, string ...$other_fields) + +

Remove one or more fields from a hash.

+
+
+
+
+ Redis|bool +
+
+ hExists(string $key, string $field) + +

Checks whether a field exists in a hash.

+
+
+
+
+ mixed +
+
+ hGet(string $key, string $member) + +

No description

+
+
+
+
+
+ Redis|array|false +
+
+ hGetAll(string $key) + +

Read every field and value from a hash.

+
+
+
+
+ Redis|int|false +
+
+ hIncrBy(string $key, string $field, int $value) + +

Increment a hash field's value by an integer

+
+
+
+
+ Redis|float|false +
+
+ hIncrByFloat(string $key, string $field, float $value) + +

Increment a hash field by a floating point value

+
+
+
+
+ Redis|array|false +
+
+ hKeys(string $key) + +

Retrieve all of the fields of a hash.

+
+
+
+
+ Redis|int|false +
+
+ hLen(string $key) + +

Get the number of fields in a hash.

+
+
+
+
+ Redis|array|false +
+
+ hMget(string $key, array $fields) + +

Get one or more fields from a hash.

+
+
+
+
+ Redis|bool +
+
+ hMset(string $key, array $fieldvals) + +

Add or update one or more hash fields and values

+
+
+
+
+ Redis|string|array +
+
+ hRandField(string $key, array $options = null) + +

Get one or more random field from a hash.

+
+
+
+
+ Redis|int|false +
+
+ hSet(string $key, string $member, mixed $value) + +

No description

+
+
+
+
+
+ Redis|bool +
+
+ hSetNx(string $key, string $field, string $value) + +

Set a hash field and value, but only if that field does not exist

+
+
+
+
+ Redis|int|false +
+
+ hStrLen(string $key, string $field) + +

Get the string length of a hash field

+
+
+
+
+ Redis|array|false +
+
+ hVals(string $key) + +

Get all of the values from a hash.

+
+
+
+
+ Redis|array|bool +
+
+ hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

Iterate over the fields and values of a hash in an incremental fashion.

+
+
+
+
+ Redis|int|false +
+
+ incr(string $key, int $by = 1) + +

Increment a key's value, optionally by a specifc amount.

+
+
+
+
+ Redis|int|false +
+
+ incrBy(string $key, int $value) + +

Increment a key by a specific integer value

+
+
+
+
+ Redis|float|false +
+
+ incrByFloat(string $key, float $value) + +

Increment a numeric key by a floating point value.

+
+
+
+
+ Redis|array|false +
+
+ info(string ...$sections) + +

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

+
+
+
+
+ bool +
+
+ isConnected() + +

Check if we are currently connected to a Redis instance.

+
+
+
+
+ Redis|array|false +
+
+ keys(string $pattern) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ lInsert(string $key, string $pos, mixed $pivot, mixed $value) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ lLen(string $key) + +

No description

+
+
+
+
+
+ Redis|string|false +
+
+ lMove(string $src, string $dst, string $wherefrom, string $whereto) + +

No description

+
+
+
+
+
+ Redis|bool|string|array +
+
+ lPop(string $key, int $count = 0) + +

No description

+
+
+
+
+
+ Redis|null|bool|int|array +
+
+ lPos(string $key, mixed $value, array $options = null) + +

No description

+
+
+
+
+
+ int|Redis +
+
+ lPush(string $key, mixed ...$elements) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ rPush(string $key, mixed ...$elements) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ lPushx(string $key, mixed $value) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ rPushx(string $key, mixed $value) + +

No description

+
+
+
+
+
+ Redis|bool +
+
+ lSet(string $key, int $index, mixed $value) + +

No description

+
+
+
+
+
+ int +
+
+ lastSave() + +

No description

+
+
+
+
+
+ mixed +
+
+ lindex(string $key, int $index) + +

No description

+
+
+
+
+
+ Redis|array|false +
+
+ lrange(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ int|Redis|false +
+
+ lrem(string $key, mixed $value, int $count = 0) + +

No description

+
+
+
+
+
+ Redis|bool +
+
+ ltrim(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ array|Redis +
+
+ mget(array $keys) + +

No description

+
+
+
+
+
+ Redis|bool +
+
+ migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = NULL) + +

No description

+
+
+
+
+
+ bool +
+
+ move(string $key, int $index) + +

No description

+
+
+
+
+
+ Redis|bool +
+
+ mset(array $key_values) + +

No description

+
+
+
+
+
+ Redis|bool +
+
+ msetnx(array $key_values) + +

No description

+
+
+
+
+
+ bool|Redis +
+
+ multi(int $value = Redis::MULTI) + +

No description

+
+
+
+
+
+ Redis|int|string|false +
+
+ object(string $subcommand, string $key) + +

No description

+
+
+
+
+
+ bool +
+
+ open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + deprecated +

No description

+
+
+
+
+
+ bool +
+
+ pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + +

No description

+
+
+
+
+
+ bool +
+
+ persist(string $key) + +

No description

+
+
+
+
+
+ bool +
+
+ pexpire(string $key, int $timeout, string|null $mode = NULL) + +

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 +you can pass an optional mode argument that modifies how the command will execute.

+
+
+
+
+ Redis|bool +
+
+ pexpireAt(string $key, int $timestamp, string|null $mode = NULL) + +

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to +Redis >= 7.0.0 you can pass an optional 'mode' argument.

+
+
+
+
+ Redis|int +
+
+ pfadd(string $key, array $elements) + +

Add one or more elements to a Redis HyperLogLog key

+
+
+
+
+ Redis|int +
+
+ pfcount(string $key) + +

Retrieve the cardinality of a Redis HyperLogLog key.

+
+
+
+
+ Redis|bool +
+
+ pfmerge(string $dst, array $srckeys) + +

Merge one or more source HyperLogLog sets into a destination set.

+
+
+
+
+ Redis|string|bool +
+
+ ping(string $message = NULL) + +

PING the redis server with an optional string argument.

+
+
+
+
+ bool|Redis +
+
+ pipeline() + +

Enter into pipeline mode.

+
+
+
+
+ bool +
+
+ popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + deprecated +

No description

+
+
+
+
+
+ bool|Redis +
+
+ psetex(string $key, int $expire, mixed $value) + +

No description

+
+
+
+
+
+ bool +
+
+ psubscribe(array $patterns, callable $cb) + +

Subscribe to one or more glob-style patterns

+
+
+
+
+ Redis|int|false +
+
+ pttl(string $key) + +

Get a keys time to live in milliseconds.

+
+
+
+
+ Redis|int|false +
+
+ publish(string $channel, string $message) + +

Publish a message to a pubsub channel

+
+
+
+
+ mixed +
+
+ pubsub(string $command, mixed $arg = null) + +

No description

+
+
+
+
+
+ Redis|array|bool +
+
+ punsubscribe(array $patterns) + +

Unsubscribe from one or more channels by pattern

+
+
+
+
+ Redis|array|string|bool +
+
+ rPop(string $key, int $count = 0) + +

Pop one or more elements from the end of a list.

+
+
+
+
+ Redis|string|false +
+
+ randomKey() + +

Return a random key from the current database

+
+
+
+
+ mixed +
+
+ rawcommand(string $command, mixed ...$args) + +

Execute any arbitrary Redis command by name.

+
+
+
+
+ Redis|bool +
+
+ rename(string $old_name, string $new_name) + +

Unconditionally rename a key from $old_name to $new_name

+
+
+
+
+ Redis|bool +
+
+ renameNx(string $key_src, string $key_dst) + +

Renames $key_src to $key_dst but only if newkey does not exist.

+
+
+
+
+ Redis|bool +
+
+ reset() + +

Reset the state of the connection.

+
+
+
+
+ Redis|bool +
+
+ restore(string $key, int $ttl, string $value, array|null $options = NULL) + +

Restore a key by the binary payload generated by the DUMP command.

+
+
+
+
+ mixed +
+
+ role() + +

Query whether the connected instance is a primary or replica

+
+
+
+
+ Redis|string|false +
+
+ rpoplpush(string $srckey, string $dstkey) + +

Atomically pop an element off the end of a Redis LIST and push it to the beginning of +another.

+
+
+
+
+ Redis|int|false +
+
+ sAdd(string $key, mixed $value, mixed ...$other_values) + +

Add one or more values to a Redis SET key.

+
+
+
+
+ int +
+
+ sAddArray(string $key, array $values) + +

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but +instead of being variadic, takes a single array of values.

+
+
+
+
+ Redis|array|false +
+
+ sDiff(string $key, string ...$other_keys) + +

Given one or more Redis SETS, this command returns all of the members from the first +set that are not in any subsequent set.

+
+
+
+
+ Redis|int|false +
+
+ sDiffStore(string $dst, string $key, string ...$other_keys) + +

This method performs the same operation as SDIFF except it stores the resulting diff +values in a specified destination key.

+
+
+
+
+ Redis|array|false +
+
+ sInter(array|string $key, string ...$other_keys) + +

Given one or more Redis SET keys, this command will return all of the elements that are +in every one.

+
+
+
+
+ Redis|int|false +
+
+ sintercard(array $keys, int $limit = -1) + +

Compute the intersection of one or more sets and return the cardinality of the result.

+
+
+
+
+ Redis|int|false +
+
+ sInterStore(array|string $key, string ...$other_keys) + +

Perform the intersection of one or more Redis SETs, storing the result in a destination +key, rather than returning them.

+
+
+
+
+ Redis|array|false +
+
+ sMembers(string $key) + +

Retrieve every member from a set key.

+
+
+
+
+ Redis|array|false +
+
+ sMisMember(string $key, string $member, string ...$other_members) + +

Check if one or more values are members of a set.

+
+
+
+
+ Redis|bool +
+
+ sMove(string $src, string $dst, mixed $value) + +

Pop a member from one set and push it onto another. This command will create the +destination set if it does not currently exist.

+
+
+
+
+ Redis|string|array|false +
+
+ sPop(string $key, int $count = 0) + +

Remove one or more elements from a set.

+
+
+
+
+ Redis|string|array|false +
+
+ sRandMember(string $key, int $count = 0) + +

Retrieve one or more random members of a set.

+
+
+
+
+ Redis|array|false +
+
+ sUnion(string $key, string ...$other_keys) + +

Returns the union of one or more Redis SET keys.

+
+
+
+
+ Redis|int|false +
+
+ sUnionStore(string $dst, string $key, string ...$other_keys) + +

Perform a union of one or more Redis SET keys and store the result in a new set

+
+
+
+
+ Redis|bool +
+
+ save() + +

Persist the Redis database to disk. This command will block the server until the save is +completed. For a nonblocking alternative, see Redis::bgsave().

+
+
+
+
+ array|false +
+
+ scan(int|null $iterator, string|null $pattern = null, int $count = 0, string $type = NULL) + +

Incrementally scan the Redis keyspace, with optional pattern and type matching.

+
+
+
+
+ Redis|int|false +
+
+ scard(string $key) + +

Retrieve the number of members in a Redis set.

+
+
+
+
+ mixed +
+
+ script(string $command, mixed ...$args) + +

An administrative command used to interact with LUA scripts stored on the server.

+
+
+
+
+ Redis|bool +
+
+ select(int $db) + +

Select a specific Redis database.

+
+
+
+
+ Redis|string|bool +
+
+ set(string $key, mixed $value, mixed $options = NULL) + +

Create or set a Redis STRING key to a value.

+
+
+
+
+ Redis|int|false +
+
+ setBit(string $key, int $idx, bool $value) + +

Set a specific bit in a Redis string to zero or one

+
+
+
+
+ Redis|int|false +
+
+ setRange(string $key, int $index, string $value) + +

Update or append to a Redis string at a specific starting index

+
+
+
+
+ bool +
+
+ setOption(int $option, mixed $value) + +

Set a configurable option on the Redis object.

+
+
+
+
+ Redis|bool +
+
+ setex(string $key, int $expire, mixed $value) + +

Set a Redis STRING key with a specific expiration in seconds.

+
+
+
+
+ Redis|bool +
+
+ setnx(string $key, mixed $value) + +

Set a key to a value, but only if that key does not already exist.

+
+
+
+
+ Redis|bool +
+
+ sismember(string $key, mixed $value) + +

Check whether a given value is the member of a Redis SET.

+
+
+
+
+ Redis|bool +
+
+ slaveof(string $host = NULL, int $port = 6379) + deprecated +

Turn a redis instance into a replica of another or promote a replica +to a primary.

+
+
+
+
+ Redis|bool +
+
+ replicaof(string $host = NULL, int $port = 6379) + +

Used to turn a Redis instance into a replica of another, or to remove +replica status promoting the instance to a primary.

+
+
+
+
+ Redis|int|false +
+
+ touch(array|string $key_or_array, string ...$more_keys) + +

Update one or more keys last modified metadata.

+
+
+
+
+ mixed +
+
+ slowlog(string $operation, int $length = 0) + +

Interact with Redis' slowlog functionality in various ways, depending +on the value of 'operation'.

+
+
+
+
+ mixed +
+
+ sort(string $key, array|null $options = null) + +

Sort the contents of a Redis key in various ways.

+
+
+
+
+ mixed +
+
+ sort_ro(string $key, array|null $options = null) + +

This is simply a read-only variant of the sort command

+
+
+
+
+ array +
+
+ sortAsc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) + deprecated +

No description

+
+
+
+
+
+ array +
+
+ sortAscAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) + deprecated +

No description

+
+
+
+
+
+ array +
+
+ sortDesc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) + deprecated +

No description

+
+
+
+
+
+ array +
+
+ sortDescAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) + deprecated +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ srem(string $key, mixed $value, mixed ...$other_values) + +

Remove one or more values from a Redis SET key.

+
+
+
+
+ array|false +
+
+ sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

Scan the members of a redis SET key.

+
+
+
+
+ Redis|int|false +
+
+ strlen(string $key) + +

Retrieve the length of a Redis STRING key.

+
+
+
+
+ bool +
+
+ subscribe(array $channels, callable $cb) + +

Subscribe to one or more Redis pubsub channels.

+
+
+
+
+ Redis|bool +
+
+ swapdb(int $src, int $dst) + +

Atomically swap two Redis databases so that all of the keys in the source database will +now be in the destination database and vice-versa.

+
+
+
+
+ Redis|array +
+
+ time() + +

Retrieve the server time from the connected Redis instance.

+
+
+
+
+ Redis|int|false +
+
+ ttl(string $key) + +

Get the amount of time a Redis key has before it will expire, in seconds.

+
+
+
+
+ Redis|int|false +
+
+ type(string $key) + +

Get the type of a given Redis key.

+
+
+
+
+ Redis|int|false +
+
+ unlink(array|string $key, string ...$other_keys) + +

Delete one or more keys from the Redis database. Unlike this operation, the actual +deletion is asynchronous, meaning it is safe to delete large keys without fear of +Redis blocking for a long period of time.

+
+
+
+
+ Redis|array|bool +
+
+ unsubscribe(array $channels) + +

Unsubscribe from one or more subscribed channels.

+
+
+
+
+ Redis|bool +
+
+ unwatch() + +

Remove any previously WATCH'ed keys in a transaction.

+
+
+
+
+ bool|Redis +
+
+ watch(array|string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ int|false +
+
+ wait(int $numreplicas, int $timeout) + +

Block the client up to the provided timeout until a certain number of replicas have confirmed +recieving them.

+
+
+
+
+ int|false +
+
+ xack(string $key, string $group, array $ids) + +

No description

+
+
+
+
+
+ Redis|string|false +
+
+ xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) + +

Append a message to a stream.

+
+
+
+
+ Redis|bool|array +
+
+ xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) + +

No description

+
+
+
+
+
+ Redis|bool|array +
+
+ xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) + +

No description

+
+
+
+
+
+ Redis|int|false +
+
+ xdel(string $key, array $ids) + +

Remove one or more specific IDs from a stream.

+
+
+
+
+ mixed +
+
+ xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) + +

XGROUP

+
+
+
+
+ mixed +
+
+ xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) + +

Retrieve information about a stream key.

+
+
+
+
+ Redis|int|false +
+
+ xlen(string $key) + +

Get the number of messages in a Redis STREAM key.

+
+
+
+
+ Redis|array|false +
+
+ xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) + +

Interact with stream messages that have been consumed by a consumer group but not yet +acknowledged with XACK.

+
+
+
+
+ Redis|array|bool +
+
+ xrange(string $key, string $start, string $end, int $count = -1) + +

Get a range of entries from a STREAM key.

+
+
+
+
+ Redis|array|bool +
+
+ xread(array $streams, int $count = -1, int $block = -1) + +

Consume one or more unconsumed elements in one or more streams.

+
+
+
+
+ Redis|array|bool +
+
+ xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) + +

Read one or more messages using a consumer group.

+
+
+
+
+ Redis|array|bool +
+
+ xrevrange(string $key, string $end, string $start, int $count = -1) + +

Get a range of entries from a STREAM ke in reverse cronological order.

+
+
+
+
+ Redis|int|false +
+
+ xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) + +

Truncate a STREAM key in various ways.

+
+
+
+
+ Redis|int|false +
+
+ zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) + +

Add one or more elements and scores to a Redis sorted set.

+
+
+
+
+ Redis|int|false +
+
+ zCard(string $key) + +

Return the number of elements in a sorted set.

+
+
+
+
+ Redis|int|false +
+
+ zCount(string $key, string $start, string $end) + +

Count the number of members in a sorted set with scores inside a provided range.

+
+
+
+
+ Redis|float|false +
+
+ zIncrBy(string $key, float $value, mixed $member) + +

Create or increment the score of a member in a Redis sorted set

+
+
+
+
+ Redis|int|false +
+
+ zLexCount(string $key, string $min, string $max) + +

Count the number of elements in a sorted set whos members fall within the provided +lexographical range.

+
+
+
+
+ Redis|array|false +
+
+ zMscore(string $key, mixed $member, mixed ...$other_members) + +

Retrieve the score of one or more members in a sorted set.

+
+
+
+
+ Redis|array|false +
+
+ zPopMax(string $key, int $count = null) + +

Pop one or more of the highest scoring elements from a sorted set.

+
+
+
+
+ Redis|array|false +
+
+ zPopMin(string $key, int $count = null) + +

Pop one or more of the lowest scoring elements from a sorted set.

+
+
+
+
+ Redis|array|false +
+
+ zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) + +

Retrieve a range of elements of a sorted set between a start and end point.

+
+
+
+
+ Redis|array|false +
+
+ zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) + +

Retrieve a range of elements from a sorted set by legographical range.

+
+
+
+
+ Redis|array|false +
+
+ zRangeByScore(string $key, string $start, string $end, array $options = []) + +

Retrieve a range of members from a sorted set by their score.

+
+
+
+
+ Redis|int|false +
+
+ zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) + +

This command is similar to ZRANGE except that instead of returning the values directly +it will store them in a destination key provided by the user

+
+
+
+
+ Redis|string|array +
+
+ zRandMember(string $key, array $options = null) + +

Retrieve one or more random members from a Redis sorted set.

+
+
+
+
+ Redis|int|false +
+
+ zRank(string $key, mixed $member) + +

Get the rank of a member of a sorted set, by score.

+
+
+
+
+ Redis|int|false +
+
+ zRem(mixed $key, mixed $member, mixed ...$other_members) + +

Remove one or more members from a Redis sorted set.

+
+
+
+
+ Redis|int|false +
+
+ zRemRangeByLex(string $key, string $min, string $max) + +

Remove zero or more elements from a Redis sorted set by legographical range.

+
+
+
+
+ Redis|int|false +
+
+ zRemRangeByRank(string $key, int $start, int $end) + +

Remove one or more members of a sorted set by their rank.

+
+
+
+
+ Redis|int|false +
+
+ zRemRangeByScore(string $key, string $start, string $end) + +

Remove one or more members of a sorted set by their score.

+
+
+
+
+ Redis|array|false +
+
+ zRevRange(string $key, int $start, int $end, mixed $scores = null) + +

List the members of a Redis sorted set in reverse order

+
+
+
+
+ Redis|array|false +
+
+ zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) + +

List members of a Redis sorted set within a legographical range, in reverse order.

+
+
+
+
+ Redis|array|false +
+
+ zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) + +

List elements from a Redis sorted set by score, highest to lowest

+
+
+
+
+ Redis|int|false +
+
+ zRevRank(string $key, mixed $member) + +

Retrieve a member of a sorted set by reverse rank.

+
+
+
+
+ Redis|float|false +
+
+ zScore(string $key, mixed $member) + +

Get the score of a member of a sorted set.

+
+
+
+
+ Redis|array|false +
+
+ zdiff(array $keys, array $options = null) + +

Given one or more sorted set key names, return every element that is in the first +set but not any of the others.

+
+
+
+
+ Redis|int|false +
+
+ zdiffstore(string $dst, array $keys) + +

Store the difference of one or more sorted sets in a destination sorted set.

+
+
+
+
+ Redis|array|false +
+
+ zinter(array $keys, array|null $weights = null, array|null $options = null) + +

Compute the intersection of one or more sorted sets and return the members

+
+
+
+
+ Redis|int|false +
+
+ zintercard(array $keys, int $limit = -1) + +

Similar to ZINTER but instead of returning the intersected values, this command returns the +cardinality of the intersected set.

+
+
+
+
+ Redis|int|false +
+
+ zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) + +

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

+
+
+
+
+ Redis|array|false +
+
+ zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

Scan the members of a sorted set incrementally, using a cursor

+
+
+
+
+ Redis|array|false +
+
+ zunion(array $keys, array|null $weights = null, array|null $options = null) + +

Retrieve the union of one or more sorted sets

+
+
+
+
+ Redis|int|false +
+
+ zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) + +

Perform a union on one or more Redis sets and store the result in a destination sorted set.

+
+
+
+ + +

Details

+ +
+
+

+ + + __construct(array $options = null) + +

+
+ + + +
+

Create a new Redis instance. If passed sufficient information in the +options array it is also possible to connect to an instance at the same +time.

+
+
+

Parameters

+ + + + + + + +
array$options
+ + + + +

See also

+ + + + + + + + + + +
+ +Redis::connect +
+ https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ + Following is an example of an options array with the supported +configuration values. Note that all of these values are optional, and you +can instead connect to Redis via PhpRedis' connect() method. + + + 'localhost', + 'port' => 6379, + 'readTimeout' => 2.5, + 'connectTimeout' => 2.5, + 'persistent' => true, + + // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass'] + 'auth' => ['phpredis', 'phpredis'], + + // See PHP stream options for valid SSL configuration settings. + 'ssl' => ['verify_peer' => false], + + // How quickly to retry a connection after we time out or it closes. + // Note that this setting is overridden by 'backoff' strategies. + 'retryInterval' => 100, + + // Which backoff algorithm to use. 'decorrelated jitter' is + // likely the best one for most solution, but there are many + // to choose from: + // REDIS_BACKOFF_ALGORITHM_DEFAULT + // REDIS_BACKOFF_ALGORITHM_CONSTANT + // REDIS_BACKOFF_ALGORITHM_UNIFORM + // REDIS_BACKOFF_ALGORITHM_EXPONENTIAL + // REDIS_BACKOFF_ALGORITHM_FULL_JITTER + // REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER + // REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER + // + // 'base', and 'cap' are in milliseconds and represent the first + // delay redis will use when reconnecting, and the maximum delay + // we will reach while retrying. + 'backoff' => [ + 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, + 'base' => 500, + 'cap' => 750, + ] +]; +?> + + +Note: If you do wish to connect via the constructor, only 'host' is + strictly required, which will cause PhpRedis to connect to that + host on Redis' default port (6379).
+ + +
+
+ +
+
+

+ + + __destruct() + +

+
+ + + +
+

No description

+ +
+
+ + + + +
+
+ +
+
+

+ + string + _compress(string $value) + +

+
+ + + +
+

Compress a value with the currently configured compressor as set with +Redis::setOption().

+
+
+

Parameters

+ + + + + + + +
string$value

The value to be compressed

+ + +

Return Value

+ + + + + + +
string

The compressed result

+ + + +

See also

+ + + + + + +
+ +Redis::setOption +
+ + +
+
+ +
+
+

+ + string + _uncompress(string $value) + +

+
+ + + +
+

Uncompress the provided argument that has been compressed with the +currently configured compressor as set with Redis::setOption().

+
+
+

Parameters

+ + + + + + + +
string$value

The compressed value to uncompress.

+ + +

Return Value

+ + + + + + +
string

The uncompressed result.

+ + + +

See also

+ + + + + + +
+ +Redis::setOption +
+ + +
+
+ +
+
+

+ + string + _prefix(string $key) + +

+
+ + + +
+

Prefix the passed argument with the currently set key prefix as set +with Redis::setOption().

+
+
+

Parameters

+ + + + + + + +
string$key

The key/string to prefix

+ + +

Return Value

+ + + + + + +
string

The prefixed string

+ + + + +
+
+ +
+
+

+ + string + _serialize(mixed $value) + +

+
+ + + +
+

Serialize the provided value with the currently set serializer as set +with Redis::setOption().

+
+
+

Parameters

+ + + + + + + +
mixed$value

The value to serialize

+ + +

Return Value

+ + + + + + +
string

The serialized result

+ + + +

See also

+ + + + + + +
+ +Redis::setOption +
+ + +
+
+ +
+
+

+ + mixed + _unserialize(string $value) + +

+
+ + + +
+

Unserialize the passed argument with the currently set serializer as set +with Redis::setOption().

+
+
+

Parameters

+ + + + + + + +
string$value

The value to unserialize

+ + +

Return Value

+ + + + + + +
mixed

The unserialized result

+ + + +

See also

+ + + + + + +
+ +Redis::setOption +
+ + +
+
+ +
+
+

+ + string + _pack(mixed $value) + +

+
+ + + +
+

Pack the provided value with the configured serializer and compressor +as set with Redis::setOption().

+
+
+

Parameters

+ + + + + + + +
mixed$value

The value to pack

+ + +

Return Value

+ + + + + + +
string

The packed result having been serialized and +compressed.

+ + + + +
+
+ +
+
+

+ + mixed + _unpack(string $value) + +

+
+ + + +
+

Unpack the provided value with the configured compressor and serializer +as set with Redis::setOption().

+
+
+

Parameters

+ + + + + + + +
string$value

The value which has been serialized and compressed.

+ + +

Return Value

+ + + + + + +
mixed

The uncompressed and eserialized value.

+ + + + +
+
+ +
+
+

+ + mixed + acl(string $subcmd, string ...$args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$subcmd
string...$args
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + Redis|int|false + append(string $key, mixed $value) + +

+
+ + + +
+

Append data to a Redis STRING key.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key in question

mixed$value

The data to append to the key.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The new string length of the key or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('foo', 'hello);
+var_dump($redis->append('foo', 'world'));
+
+// --- OUTPUT ---
+// int(10)
+?>
+ + + + +
+
+ +
+
+

+ + Redis|bool + auth(mixed $credentials) + +

+
+ + + +
+

Authenticate a Redis connection after its been established.

+
+
+

Parameters

+ + + + + + + +
mixed$credentials

A string password, or an array with one or two string elements.

+ + +

Return Value

+ + + + + + +
Redis|bool

Whether the AUTH was successful.

+

See below for various examples about how this method may be called.

+
<?php>
+$redis->auth('password');
+$redis->auth(['password']);
+$redis->auth(['username', 'password']);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/auth +
+ + +
+
+ +
+
+

+ + Redis|bool + bgSave() + +

+
+ + + +
+

Execute a save of the Redis database in the background.

+
+
+ +

Return Value

+ + + + + + +
Redis|bool

Whether the command was successful.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/bgsave +
+ + +
+
+ +
+
+

+ + Redis|bool + bgrewriteaof() + +

+
+ + + +
+

Asynchronously rewrite Redis' append-only file

+
+
+ +

Return Value

+ + + + + + +
Redis|bool

Whether the command was successful.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/bgrewriteaof +
+ + +
+
+ +
+
+

+ + Redis|int|false + bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) + +

+
+ + + +
+

Count the number of set bits in a Redis string.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The key in question (must be a string key)

int$start

The index where Redis should start counting. If omitted it +defaults to zero, which means the start of the string.

int$end

The index where Redis should stop counting. If omitted it +defaults to -1, meaning the very end of the string.

bool$bybit

Whether or not Redis should treat $start and $end as bit +positions, rather than bytes.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of bits set in the requested range.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/bitcount/ +
+ + +
+
+ +
+
+

+ + Redis|int|false + bitop(string $operation, string $deskey, string $srckey, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$operation
string$deskey
string$srckey
string...$other_keys
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|int|false + bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) + +

+
+ + + +
+

Return the position of the first bit set to 0 or 1 in a string.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key

The key to check (must be a string)

bool$bit

Whether to look for an unset (0) or set (1) bit.

int$start

Where in the string to start looking.

int$end

Where in the string to stop looking.

bool$bybit

If true, Redis will treat $start and $end as BIT values and not bytes, so if start +was 0 and end was 2, Redis would only search the first two bits.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The position of the first set or unset bit.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/bitpos/ +
+ + +
+
+ +
+
+

+ + Redis|array|null|false + blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) + +

+
+ + + +
+

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified +timeout. This method may be called in two distinct ways, of which examples are provided below.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_keys

This can either be a string key or an array of one or more +keys.

string|float|int$timeout_or_key

If the previous argument was a string key, this can either +be an additional key, or the timeout you wish to send to +the command.

+
<?php>
+// One way to call this method is in a variadic way, with the final argument being
+// the intended timeout.
+$redis->blPop('list1', 'list2', 'list3', 1.5);
+
+// Alternatively, you can send an array of keys
+$relay->blPop(['list1', 'list2', 'list3'], 1.5);
+?>
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
Redis|array|null|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/blpop/ +
+ + +
+
+ +
+
+

+ + Redis|array|null|false + brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) + +

+
+ + + +
+

Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

The calling convention is identical to Redis::blPop() so see that documentation for more details.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_keys
string|float|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
Redis|array|null|false
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/brpop/ +
+ +Redis::blPop +
+ + +
+
+ +
+
+

+ + Redis|string|false + brpoplpush(string $src, string $dst, int|float $timeout) + +

+
+ + + +
+

Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list, +optionally blocking up to a specified timeout.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$src

The source list

string$dst

The destination list

int|float$timeout

The number of seconds to wait. Note that you must be connected +to Redis >= 6.0.0 to send a floating point timeout.

+ + +

Return Value

+ + + + + + +
Redis|string|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/brpoplpush/ +
+ + +
+
+ +
+
+

+ + Redis|array|false + bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + +

+
+ + + +
+

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified +timeout if no elements are available.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|int$timeout_or_key

If the previous argument was an array, this argument +must be a timeout value. Otherwise it could also be +another key.

mixed...$extra_args

Can consist of additional keys, until the last argument +which needs to be a timeout.

+

Following are examples of the two main ways to call this method.

+

+<?php
+// Method 1 - Variadic, with the last argument being our timeout
+$redis->bzPopMax('key1', 'key2', 'key3', 1.5);
+
+// Method 2 - A single array of keys, followed by the timeout
+$redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);
+<?php>
+
+NOTE:  We reccomend calling this function with an array and a timeout as the other strategy
+       may be deprecated in future versions of PhpRedis
+?>
+ + +

Return Value

+ + + + + + +
Redis|array|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/bzpopmax +
+ + +
+
+ +
+
+

+ + Redis|array|false + bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + +

+
+ + + +
+

POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout +if no elements are available

This command is identical in semantics to bzPopMax so please see that method for more information.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
Redis|array|false
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/bzpopmin +
+ +Redis::bzPopMax +
+ + +
+
+ +
+
+

+ + Redis|array|null|false + bzmpop(float $timeout, array $keys, string $from, int $count = 1) + +

+
+ + + +
+

POP one or more elements from one or more sorted sets, blocking up to a specified amount of time +when no elements are available.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
float$timeout

How long to block if there are no element available

array$keys

The sorted sets to pop from

string$from

The string 'MIN' or 'MAX' (case insensitive) telling Redis whether you wish to +pop the lowest or highest scoring members from the set(s).

int$count

Pop up to how many elements.

+ + +

Return Value

+ + + + + + +
Redis|array|null|false

This function will return an array of popped elements, or false +depending on whether any elements could be popped within the +specified timeout.

+

NOTE: If Redis::OPT_NULL_MULTIBULK_AS_NULL is set to true via Redis::setOption(), this method will +instead return NULL when Redis doesn't pop any elements.

+ + + + +
+
+ +
+
+

+ + Redis|array|null|false + zmpop(array $keys, string $from, int $count = 1) + +

+
+ + + +
+

POP one or more of the highest or lowest scoring elements from one or more sorted sets.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys

One or more sorted sets

string$from

The string 'MIN' or 'MAX' (case insensitive) telling Redis whether you want to +pop the lowest or highest scoring elements.

int$count

Pop up to how many elements at once.

+ + +

Return Value

+ + + + + + +
Redis|array|null|false

An array of popped elements or false if none could be popped.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zmpop +
+ + +
+
+ +
+
+

+ + Redis|array|null|false + blmpop(float $timeout, array $keys, string $from, int $count = 1) + +

+
+ + + +
+

Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when +no elements are available.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
float$timeout

The number of seconds Redis will block when no elements are available.

array$keys

One or more Redis LISTs to pop from.

string$from

The string 'LEFT' or 'RIGHT' (case insensitive), telling Redis whether +to pop elements from the beginning or end of the LISTs.

int$count

Pop up to how many elements at once.

+ + +

Return Value

+ + + + + + +
Redis|array|null|false

One or more elements popped from the list(s) or false if all LISTs +were empty.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/blmpop +
+ + +
+
+ +
+
+

+ + Redis|array|null|false + lmpop(array $keys, string $from, int $count = 1) + +

+
+ + + +
+

Pop one or more elements off of one or more Redis LISTs.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys

An array with one or more Redis LIST key names.

string$from

The string 'LEFT' or 'RIGHT' (case insensitive), telling Redis whether to pop\ +elements from the beginning or end of the LISTs.

int$count

The maximum number of elements to pop at once.

+ + +

Return Value

+ + + + + + +
Redis|array|null|false

One or more elements popped from the LIST(s) or false if all the LISTs +were empty.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/lmpop +
+ + +
+
+ +
+
+

+ + bool + clearLastError() + +

+
+ + + +
+

Reset any last error on the connection to NULL

+
+
+ +

Return Value

+ + + + + + +
bool

This should always return true or throw an exception if we're not connected.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('string', 'this_is_a_string');
+$redis->smembers('string');
+
+var_dump($redis->getLastError());
+$redis->clearLastError();
+var_dump($redis->getLastError());
+
+// --- OUTPUT ---
+// string(65) "WRONGTYPE Operation against a key holding the wrong kind of value"
+// NULL
+?>
+ + + + +
+
+ +
+
+

+ + mixed + client(string $opt, mixed ...$args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$opt
mixed...$args
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + bool + close() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + mixed + command(string $opt = null, string|array $arg) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$opt
string|array$arg
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + mixed + config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) + +

+
+ + + +
+

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends +on the $operation qualifier.

Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$operation
array|string|null$key_or_settings
string|null$value
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/config + @param string $operation The CONFIG subcommand to execute +@param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or + an array of settings or settings and values. + Note: Redis 7.0.0 is required to send an array of settings. +@param string $value The setting value when the operation is SET. + + +config('GET', 'timeout'); +$redis->config('GET', ['timeout', 'databases']); + +$redis->config('SET', 'timeout', 30); +$redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); +?> +
+ + +
+
+ +
+
+

+ + bool + connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
string$persistent_id
int$retry_interval
float$read_timeout
array$context
+ + +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + Redis|bool + copy(string $src, string $dst, array $options = null) + +

+
+ + + +
+

Make a copy of a redis key.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$src

The key to copy

string$dst

The name of the new key created from the source key.

array$options

An array with modifiers on how COPY should operate.

+

Available Options:

+

$options = [ +'REPLACE' => true|false // Whether Redis should replace an existing key. +'DB' => int // Copy the key to a specific DB. +];

+ + +

Return Value

+ + + + + + +
Redis|bool

True if the copy was completed and false if not.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->pipeline()
+      ->select(1)
+      ->del('newkey')
+      ->select(0)
+      ->del('newkey')
+      ->mset(['source1' => 'value1', 'exists' => 'old_value'])
+      ->exec();
+
+// Will succeed, as 'newkey' doesn't exist
+var_dump($redis->copy('source1', 'newkey'));
+
+// Will succeed, because 'newkey' doesn't exist in DB 1
+var_dump($redis->copy('source1', 'newkey', ['db' => 1]));
+
+// Will fail, because 'exists' does exist
+var_dump($redis->copy('source1', 'exists'));
+
+// Will succeed, because even though 'exists' is a key, we sent the REPLACE option.
+var_dump($redis->copy('source1', 'exists', ['REPLACE' => true]));
+
+// --- OUTPUT ---
+// bool(true)
+// bool(true)
+// bool(false)
+// bool(true)
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/copy +
+ + +
+
+ +
+
+

+ + Redis|int|false + dbSize() + +

+
+ + + +
+

Return the number of keys in the currently selected Redis database.

+
+
+ +

Return Value

+ + + + + + +
Redis|int|false

The number of keys or false on failure.

+

+<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->flushdb();
+
+$redis->set('foo', 'bar');
+var_dump($redis->dbsize());
+
+$redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']);
+var_dump($redis->dbsize());
+
+// --- OUTPUT
+// int(1)
+// int(5)
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/dbsize +
+ + +
+
+ +
+
+

+ + Redis|string + debug(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
Redis|string
+ + + + +
+
+ +
+
+

+ + Redis|int|false + decr(string $key, int $by = 1) + +

+
+ + + +
+

Decrement a Redis integer by 1 or a provided value.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to decrement

int$by

How much to decrement the key. Note that if this value is +not sent or is set to 1, PhpRedis will actually invoke +the 'DECR' command. If it is any value other than 1 +PhpRedis will actually send the DECRBY command.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The new value of the key or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('counter', 3);
+
+var_dump($redis->decr('counter'));
+var_dump($redis->decr('counter', 2));
+
+// --- OUTPUT ---
+// int(2)
+// int(0)
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/decr +
+ https://redis.io/commands/decrby +
+ + +
+
+ +
+
+

+ + Redis|int|false + decrBy(string $key, int $value) + +

+
+ + + +
+

Decrement a redis integer by a value

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The integer key to decrement.

int$value

How much to decrement the key.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The new value of the key or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost');
+
+$redis->set('counter', 3);
+var_dump($redis->decrby('counter', 1));
+var_dump($redis->decrby('counter', 2));
+
+// --- OUTPUT ---
+// int(2)
+// int(0)
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/decrby +
+ + +
+
+ +
+
+

+ + Redis|int|false + del(array|string $key, string ...$other_keys) + +

+
+ + + +
+

Delete one or more keys from Redis.

+
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys

One or more additional keys passed in a variadic fashion.

+

This method can be called in two distinct ways. The first is to pass a single array +of keys to delete, and the second is to pass N arguments, all names of keys. See +below for an example of both strategies.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+for ($i = 0; $i < 5; $i++) {
+    $redis->set("key:$i", "val:$i");
+}
+
+var_dump($redis->del('key:0', 'key:1'));
+var_dump($redis->del(['key:2', 'key:3', 'key:4']));
+
+// --- OUTPUT ---
+// int(2)
+// int(3)
+?>
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/del +
+ + +
+
+ +
+
+

+ + Redis|int|false + delete(array|string $key, string ...$other_keys) + deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|bool + discard() + +

+
+ + + +
+

Discard a transaction currently in progress.

+
+
+ +

Return Value

+ + + + + + +
Redis|bool

True if we could discard the transaction.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->multi()->set('foo', 'bar')->get('foo');
+
+// Redis::MULTI
+$redis->getMode();
+
+// Discard the in-progress transaction
+$redis->discard();
+
+// Redis::ATOMIC
+$redis->getMode();
+
+?>
+ + + + +
+
+ +
+
+

+ + Redis|string + dump(string $key) + +

+
+ + + +
+

Dump Redis' internal binary representation of a key.

+
+
+

Parameters

+ + + + + + + +
string$key

The key to dump.

+ + +

Return Value

+ + + + + + +
Redis|string

A binary string representing the key's value.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zset');
+
+$redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two');
+
+// Retrieve the binary representation of the zset
+$binary = $redis->dump('zset');
+
+// Retore it to a different name
+$redis->restore('new-zset', 0, $binary);
+
+// Array
+// (
+//     [zero] => 0
+//     [one] => 1
+//     [two] => 2
+// )
+$redis->zRange('new-zset', 0, -1, true);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/dump +
+ + +
+
+ +
+
+

+ + Redis|string|false + echo(string $str) + +

+
+ + + +
+

Have Redis repeat back an arbitrary string to the client.

+
+
+

Parameters

+ + + + + + + +
string$str

The string to echo

+ + +

Return Value

+ + + + + + +
Redis|string|false

The string sent to Redis or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+var_dump($redis->echo('Hello, World'));
+
+// --- OUTPUT ---
+// string(12) "Hello, World"
+
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/echo +
+ + +
+
+ +
+
+

+ + mixed + eval(string $script, array $args = [], int $num_keys = 0) + +

+
+ + + +
+

Execute a LUA script on the redis server.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script

A string containing the LUA script

array$args

An array of arguments to pass to this script

int$num_keys

How many of the arguments are keys. This is needed +as redis distinguishes between key name arguments +and other data.

+ + +

Return Value

+ + + + + + +
mixed

LUA scripts may return arbitrary data so this method can return +strings, arrays, nested arrays, etc.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/eval/ +
+ + +
+
+ +
+
+

+ + mixed + eval_ro(string $script_sha, array $args = [], int $num_keys = 0) + +

+
+ + + +
+

This is simply the read-only variant of eval, meaning the underlying script +may not modify data in redis.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script_sha
array$args
int$num_keys
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ +Redis::eval +
+ + +
+
+ +
+
+

+ + mixed + evalsha(string $sha1, array $args = [], int $num_keys = 0) + +

+
+ + + +
+

Execute a LUA script on the server but instead of sending the script, send +the SHA1 hash of the script.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$sha1
array$args

Arguments to send to the script.

int$num_keys

The number of arguments that are keys

+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/evalsha/ +
+ +Redis::eval +
+ + +
+
+ +
+
+

+ + mixed + evalsha_ro(string $sha1, array $args = [], int $num_keys = 0) + +

+
+ + + +
+

This is simply the read-only variant of evalsha, meaning the underlying script +may not modify data in redis.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$sha1
array$args
int$num_keys
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ +Redis::evalsha +
+ + +
+
+ +
+
+

+ + Redis|array|false + exec() + +

+
+ + + +
+

Execute either a MULTI or PIPELINE block and return the array of replies.

+
+
+ +

Return Value

+ + + + + + +
Redis|array|false

The array of pipeline'd or multi replies or false on failure.

+
$redis = new Redis(['host' => 'localhost']);
+
+$res = $redis->multi()
+             ->set('foo', 'bar')
+             ->get('foo')
+             ->del('list')
+             ->rpush('list', 'one', 'two', 'three')
+             ->exec();
+
+var_dump($res);
+
+// --- OUTPUT ---
+// array(4) {
+//   [0]=>
+//   bool(true)           // set('foo', 'bar')
+//   [1]=>
+//   string(3) "bar"      // get('foo')
+//   [2]=>
+//   int(1)               // del('list')
+//   [3]=>
+//   int(3)               // rpush('list', 'one', 'two', 'three')
+// }
+?>
+ + + +

See also

+ + + + + + + + + + + + + + + + + + +
+ https://redis.io/commands/exec +
+ https://redis.io/commands/multi +
+ +Redis::pipeline +
+ +Redis::multi +
+ + +
+
+ +
+
+

+ + Redis|int|bool + exists(mixed $key, mixed ...$other_keys) + +

+
+ + + +
+

Test if one or more keys exist.

+
+
+

Parameters

+ + + + + + + + + + + + +
mixed$key

Either an array of keys or a string key

mixed...$other_keys

If the previous argument was a string, you may send any number of +additional keys to test.

+ + +

Return Value

+ + + + + + +
Redis|int|bool

The number of keys that do exist and false on failure

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->multi()
+      ->mset(['k1' => 'v1', 'k2' => 'v2', 'k3' => 'v3', 'k4' => 'v4'])
+      ->exec();
+
+// Using a single array of keys
+var_dump($redis->exists(['k1', 'k2', 'k3']));
+
+// Calling via variadic arguments
+var_dump($redis->exists('k4', 'k5', 'notakey'));
+
+// --- OUTPUT ---
+// int(3)
+// int(1)
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/exists +
+ + +
+
+ +
+
+

+ + Redis|bool + expire(string $key, int $timeout, string|null $mode = NULL) + +

+
+ + + +
+

Sets an expiration in seconds on the key in question. If connected to +redis-server >= 7.0.0 you may send an additional "mode" argument which +modifies how the command will execute.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The key to set an expiration on.

int$timeout
string|null$mode

A two character modifier that changes how the +command works. +NX - Set expiry only if key has no expiry +XX - Set expiry only if key has an expiry +LT - Set expiry only when new expiry is < current expiry +GT - Set expiry only when new expiry is > current expiry

+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/expire +
+ + +
+
+ +
+
+

+ + Redis|bool + expireAt(string $key, int $timestamp, string|null $mode = NULL) + +

+
+ + + +
+

Set a key's expiration to a specific Unix timestamp in seconds. If +connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timestamp
string|null$mode
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + +

See also

+ + + + + + +
+ +Redis::expire + For a description of the mode argument. + +@param string $key The key to set an expiration on. +@param string $mode A two character modifier that changes how the + command works.
+ + +
+
+ +
+
+

+ + Redis|bool + failover(array|null $to = null, bool $abort = false, int $timeout = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array|null$to
bool$abort
int$timeout
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + + +
+
+ +
+
+

+ + Redis|int|false + expiretime(string $key) + +

+
+ + + +
+

Get the expiration of a given key as a unix timestamp

+
+
+

Parameters

+ + + + + + + +
string$key

The key to check.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The timestamp when the key expires, or -1 if the key has no expiry +and -2 if the key doesn't exist.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('expiry-key', 'this will last a very long time');
+
+// Expire this key at 2222/02/22 02:22:22 GMT
+$redis->expireAt('expiry-key', 7955144542);
+
+var_dump($redis->expiretime('expiry-key'));
+
+// --- OUTPUT ---
+// int(7955144542)
+
+?>php
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/expiretime +
+ + +
+
+ +
+
+

+ + Redis|int|false + pexpiretime(string $key) + +

+
+ + + +
+

Get the expriation timestamp of a given Redis key but in milliseconds.

+
+
+

Parameters

+ + + + + + + +
string$key

The key to check

+ + +

Return Value

+ + + + + + +
Redis|int|false

The expiration timestamp of this key (in milliseconds) or -1 if the +key has no expiration, and -2 if it does not exist.

+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/pexpiretime +
+ +Redis::expiretime +
+ + +
+
+ +
+
+

+ + Redis|bool + flushAll(bool|null $sync = null) + +

+
+ + + +
+

Deletes every key in all Redis databases

+
+
+

Parameters

+ + + + + + + +
bool|null$sync

Whether to perform the task in a blocking or non-blocking way. +when TRUE, PhpRedis will execute FLUSHALL SYNC, and when FALSE we +will execute FLUSHALL ASYNC. If the argument is omitted, we +simply execute FLUSHALL and whether it is SYNC or ASYNC depends +on Redis' lazyfree-lazy-user-flush config setting.

+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + + +
+
+ +
+
+

+ + Redis|bool + flushDB(bool|null $sync = null) + +

+
+ + + +
+

Deletes all the keys of the currently selected database.

+
+
+

Parameters

+ + + + + + + +
bool|null$sync

Whether to perform the task in a blocking or non-blocking way. +when TRUE, PhpRedis will execute FLUSHDB SYNC, and when FALSE we +will execute FLUSHDB ASYNC. If the argument is omitted, we +simply execute FLUSHDB and whether it is SYNC or ASYNC depends +on Redis' lazyfree-lazy-user-flush config setting.

+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + + +
+
+ +
+
+

+ + Redis|int|false + geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
string$member
mixed...$other_triples_and_options
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|float|false + geodist(string $key, string $src, string $dst, string|null $unit = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$src
string$dst
string|null$unit
+ + +

Return Value

+ + + + + + +
Redis|float|false
+ + + + +
+
+ +
+
+

+ + Redis|array|false + geohash(string $key, string $member, string ...$other_members) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
string...$other_members
+ + +

Return Value

+ + + + + + +
Redis|array|false
+ + + + +
+
+ +
+
+

+ + Redis|array|false + geopos(string $key, string $member, string ...$other_members) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
string...$other_members
+ + +

Return Value

+ + + + + + +
Redis|array|false
+ + + + +
+
+ +
+
+

+ + mixed + georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
float$radius
string$unit
array$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + mixed + georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
float$radius
string$unit
array$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + mixed + georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$member
float$radius
string$unit
array$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + mixed + georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$member
float$radius
string$unit
array$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + array + geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
array|string$position
array|int|float$shape
string$unit
array$options
+ + +

Return Value

+ + + + + + +
array
+ + + + +
+
+ +
+
+

+ + Redis|array|int|false + geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$dst
string$src
array|string$position
array|int|float$shape
string$unit
array$options
+ + +

Return Value

+ + + + + + +
Redis|array|int|false
+ + + + +
+
+ +
+
+

+ + mixed + get(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + mixed + getAuth() + +

+
+ + + +
+

Get the authentication information on the connection, if any.

+
+
+ +

Return Value

+ + + + + + +
mixed

The authentication information used to authenticate the connection.

+ + + +

See also

+ + + + + + +
+ +Redis::auth +
+ + +
+
+ +
+
+

+ + Redis|int|false + getBit(string $key, int $idx) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$idx
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|string|bool + getEx(string $key, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$options
+ + +

Return Value

+ + + + + + +
Redis|string|bool
+ + + + +
+
+ +
+
+

+ + int + getDBNum() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
int
+ + + + +
+
+ +
+
+

+ + Redis|string|bool + getDel(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
Redis|string|bool
+ + + + +
+
+ +
+
+

+ + string + getHost() + +

+
+ + + +
+

Return the host or Unix socket we are connected to.

+
+
+ +

Return Value

+ + + + + + +
string

The host or Unix socket.

+ + + + +
+
+ +
+
+

+ + string|null + getLastError() + +

+
+ + + +
+

Get the last error returned to us from Redis, if any.

+
+
+ +

Return Value

+ + + + + + +
string|null

The error string or NULL if there is none.

+ + + + +
+
+ +
+
+

+ + int + getMode() + +

+
+ + + +
+

Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

+
+
+ +

Return Value

+ + + + + + +
int

The mode we're in.

+ + + + +
+
+ +
+
+

+ + mixed + getOption(int $option) + +

+
+ + + +
+

Retrieve the value of a configuration setting as set by Redis::setOption()

+
+
+

Parameters

+ + + + + + + +
int$option
+ + +

Return Value

+ + + + + + +
mixed

The setting itself or false on failure

+ + + +

See also

+ + + + + + +
+ +Redis::setOption + for a detailed list of options and their values.
+ + +
+
+ +
+
+

+ + string|null + getPersistentID() + +

+
+ + + +
+

Get the persistent connection ID, if there is one.

+
+
+ +

Return Value

+ + + + + + +
string|null

The ID or NULL if we don't have one.

+ + + + +
+
+ +
+
+

+ + int + getPort() + +

+
+ + + +
+

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

+
+
+ +

Return Value

+ + + + + + +
int

The port.

+ + + + +
+
+ +
+
+

+ + Redis|string|false + getRange(string $key, int $start, int $end) + +

+
+ + + +
+

Retrieve a substring of a string by index.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The string to query.

int$start

The zero-based starting index.

int$end

The zero-based ending index.

+ + +

Return Value

+ + + + + + +
Redis|string|false

The substring or false on failure.

+

+<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$word = 'Supercalifragilisticexpialidocious';
+$redis->set('silly-word', $word);
+
+// string "super"
+var_dump($redis->getRange('silly-word', 0, 4));
+
+// string(7) "docious"
+var_dump($redis->getRange('silly-word', -7, -1));
+?>
+ + + + +
+
+ +
+
+

+ + Redis|string|array|int|false + lcs(string $key1, string $key2, array|null $options = NULL) + +

+
+ + + +
+

Get the longest common subsequence between two string keys.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key1

The first key to check

string$key2

The second key to check

array|null$options

An optional array of modifiers for the comand.

+
$options = [
+    'MINMATCHLEN'  => int  // Exclude matching substrings that are less than this value
+
+    'WITHMATCHLEN' => bool // Whether each match should also include its length.
+
+    'LEN'                  // Return the length of the longest subsequence
+
+    'IDX'                  // Each returned match will include the indexes where the
+                           // match occurs in each string.
+];
+

NOTE: 'LEN' cannot be used with 'IDX'.

+ + +

Return Value

+ + + + + + +
Redis|string|array|int|false

Various reply types depending on options.

+

+<?php
+<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc');
+$redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc');
+
+// string(37) "acccgcacggcaagtcgttccagcaactggcgctagc"
+var_dump($redis->lcs('seq1', 'seq2'));
+?>
+ + + + +
+
+ +
+
+

+ + float + getReadTimeout() + +

+
+ + + +
+

Get the currently set read timeout on the connection.

+
+
+ +

Return Value

+ + + + + + +
float

The timeout.

+ + + + +
+
+ +
+
+

+ + Redis|string|false + getset(string $key, mixed $value) + +

+
+ + + +
+

Sets a key and returns any previously set value, if the key already existed.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to set.

mixed$value

The value to set the key to.

+ + +

Return Value

+ + + + + + +
Redis|string|false

The old value of the key or false if it didn't exist.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('captain');
+
+// bool(false)
+var_dump($redis->getset('captain', 'Pike'));
+
+// string(4) "Pike"
+var_dump($redis->getset('captain', 'Kirk'));
+?>
+ + + + +
+
+ +
+
+

+ + float|false + getTimeout() + +

+
+ + + +
+

Retrieve any set connection timeout

+
+
+ +

Return Value

+ + + + + + +
float|false

The currently set timeout or false on failure (e.g. we aren't connected).

+ + + + +
+
+ +
+
+

+ + int|false + getTransferredBytes() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
int|false
+ + + + +
+
+ +
+
+

+ + Redis|int|false + hDel(string $key, string $field, string ...$other_fields) + +

+
+ + + +
+

Remove one or more fields from a hash.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The hash key in question.

string$field

The first field to remove

string...$other_fields

One or more additional fields to remove.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of fields actually removed.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('people');
+
+$redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
+
+// int(1)
+$redis->hDel('comms', 'Mallory', 'Archibald');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hdel +
+ + +
+
+ +
+
+

+ + Redis|bool + hExists(string $key, string $field) + +

+
+ + + +
+

Checks whether a field exists in a hash.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The hash to query.

string$field

The field to check

+ + +

Return Value

+ + + + + + +
Redis|bool

True if it exists, false if not.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('captains');
+
+$redis->hmset('captains', ['Kirk' => 'Enterprise', 'Picard' => 'Enterprise-D', 'Sisko' => 'Defiant']);
+
+bool(false)
+$redis->hExists('captains', 'Pike');
+
+bool(true)
+$redis->hExists('captains', 'Picard');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hexists +
+ + +
+
+ +
+
+

+ + mixed + hGet(string $key, string $member) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$member
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + Redis|array|false + hGetAll(string $key) + +

+
+ + + +
+

Read every field and value from a hash.

+
+
+

Parameters

+ + + + + + + +
string$key

The hash to query.

+ + +

Return Value

+ + + + + + +
Redis|array|false

All fields and values or false if the key didn't exist.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('comms');
+
+$redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
+
+// array(3) {
+//   ["Alice"]=>
+//   string(3) "ecc"
+//   ["Bob"]=>
+//   string(3) "rsa"
+//   ["Mallory"]=>
+//   string(7) "haxx00r"
+// }
+$redis->hGetAll('comms');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hgetall +
+ + +
+
+ +
+
+

+ + Redis|int|false + hIncrBy(string $key, string $field, int $value) + +

+
+ + + +
+

Increment a hash field's value by an integer

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The hash to modify

string$field

The field to increment

int$value

How much to increment the value.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The new value of the field.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('player');
+
+$redis->hmset('player', ['name' => 'Bob', 'level' => 1]);
+
+// int(2)
+$redis->hIncrBy('player', 'level', 1);
+
+// int(5)
+$redis->hIncrBy('player', 'level', 3);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hincrby +
+ + +
+
+ +
+
+

+ + Redis|float|false + hIncrByFloat(string $key, string $field, float $value) + +

+
+ + + +
+

Increment a hash field by a floating point value

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The hash with the field to increment.

string$field

The field to increment.

float$value
+ + +

Return Value

+ + + + + + +
Redis|float|false

The field value after incremented.

+
$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('trig-numbers')
+
+// float(3.1415926)
+$pi = $redis->hIncrByFloat('trig-numbers', 'pi', 3.1415926);
+
+// float(6.2831852)
+$redis->hIncrByFloat('trig-numbers', 'tau', 2 * $pi);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hincrbyfloat +
+ + +
+
+ +
+
+

+ + Redis|array|false + hKeys(string $key) + +

+
+ + + +
+

Retrieve all of the fields of a hash.

+
+
+

Parameters

+ + + + + + + +
string$key

The hash to query.

+ + +

Return Value

+ + + + + + +
Redis|array|false

The fields in the hash or false if the hash doesn't exist.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('ships');
+
+$redis->hmset('ships', ['Enterprise' => 'NCC-1701D', 'Defiant' => 'NX-74205', 'Voyager' => 'NCC-74656']);
+
+// array(3) {
+//   [0]=>
+//   string(10) "Enterprise"
+//   [1]=>
+//   string(7) "Defiant"
+//   [2]=>
+//   string(7) "Voyager"
+// }
+$redis->hKeys('ships');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hkeys +
+ + +
+
+ +
+
+

+ + Redis|int|false + hLen(string $key) + +

+
+ + + +
+

Get the number of fields in a hash.

+
+
+

Parameters

+ + + + + + + +
string$key

The hash to check.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of fields or false if the key didn't exist.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hlen +
+ + +
+
+ +
+
+

+ + Redis|array|false + hMget(string $key, array $fields) + +

+
+ + + +
+

Get one or more fields from a hash.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The hash to query.

array$fields

One or more fields to query in the hash.

+ + +

Return Value

+ + + + + + +
Redis|array|false

The fields and values or false if the key didn't exist.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('player:1');
+
+$redis->hmset('player:1', ['name' => 'Alice', 'age' => '26', 'score' => '1337']);
+
+// array(2) {
+//   ["name"]=>
+//   string(5) "Alice"
+//   ["score"]=>
+//   string(4) "1337"
+// }
+$redis->hmget('player:1', ['name', 'score']);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hmget +
+ + +
+
+ +
+
+

+ + Redis|bool + hMset(string $key, array $fieldvals) + +

+
+ + + +
+

Add or update one or more hash fields and values

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The hash to create/update

array$fieldvals

An associative array with fields and their values.

+ + +

Return Value

+ + + + + + +
Redis|bool

True if the operation was successful

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hmset +
+ + +
+
+ +
+
+

+ + Redis|string|array + hRandField(string $key, array $options = null) + +

+
+ + + +
+

Get one or more random field from a hash.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The hash to query.

array$options

An array of options to modify how the command behaves.

+
$options = [
+    'COUNT'      => int  // An optional number of fields to return.
+    'WITHVALUES' => bool // Also return the field values.
+];
+ + +

Return Value

+ + + + + + +
Redis|string|array

One or more random fields (and possibly values).

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('settings');
+
+$redis->hmset('settings', ['path' => '/', 'state' => 'active', 'jobs' => 15]);
+
+$redis->hrandfield('settings');
+
+$redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hrandfield +
+ + +
+
+ +
+
+

+ + Redis|int|false + hSet(string $key, string $member, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
mixed$value
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|bool + hSetNx(string $key, string $field, string $value) + +

+
+ + + +
+

Set a hash field and value, but only if that field does not exist

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The hash to update.

string$field

The value to set.

string$value
+ + +

Return Value

+ + + + + + +
Redis|bool

True if the field was set and false if not.

+
$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('player:1');
+
+$redis->hmset('player:1', ['name' => 'bob', 'score' => 0]);
+
+// bool(true)
+var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));
+
+// bool(false)
+var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hsetnx +
+ + +
+
+ +
+
+

+ + Redis|int|false + hStrLen(string $key, string $field) + +

+
+ + + +
+

Get the string length of a hash field

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The hash to query.

string$field

The field to query.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The string length of the field or false.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('hash');
+$redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]);
+
+// int(50)
+$redis->hstrlen('hash', '50bytes');
+
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hstrlen +
+ + +
+
+ +
+
+

+ + Redis|array|false + hVals(string $key) + +

+
+ + + +
+

Get all of the values from a hash.

+
+
+

Parameters

+ + + + + + + +
string$key

The hash to query.

+ + +

Return Value

+ + + + + + +
Redis|array|false

The values from the hash.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('player');
+
+$redis->hmset('player', ['name' => 'Alice', 'score' => 1337]);
+
+// array(2) {
+//   ["name"]=>
+//   string(5) "Alice"
+//   ["score"]=>
+//   string(4) "1337"
+// }
+$redis->hgetall('player');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/hvals +
+ + +
+
+ +
+
+

+ + Redis|array|bool + hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

Iterate over the fields and values of a hash in an incremental fashion.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The hash to query.

int|null$iterator

The scan iterator, which should be initialized to NULL before the first call. +This value will be updated after every call to hscan, until it reaches zero +meaning the scan is complete.

string|null$pattern

An optional glob-style pattern to filter fields with.

int$count

An optional hint to Redis about how many fields and values to return per HSCAN.

+ + +

Return Value

+ + + + + + +
Redis|array|bool

An array with a subset of fields and values.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('big-hash');
+
+for ($i = 0; $i < 1000; $i++) {
+    $fields["field:$i"] = "value:$i";
+}
+
+$redis->hmset('big-hash', $fields);
+
+$it = NULL;
+
+do {
+    // Scan the hash but limit it to fields that match '*:1?3'
+    $fields = $redis->hscan('big-hash', $it, '*:1?3');
+
+    foreach ($fields as $field => $value) {
+        echo "[$field] => $value\n";
+    }
+} while ($it != 0);
+
+// --- OUTPUT ---
+// [field:143] => value:143
+// [field:133] => value:133
+// [field:163] => value:163
+// [field:183] => value:183
+// [field:153] => value:153
+// [field:113] => value:113
+// [field:103] => value:103
+// [field:193] => value:193
+// [field:123] => value:123
+// [field:173] => value:173
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/hscan +
+ https://redis.io/commands/scan +
+ + +
+
+ +
+
+

+ + Redis|int|false + incr(string $key, int $by = 1) + +

+
+ + + +
+

Increment a key's value, optionally by a specifc amount.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to increment

int$by

An optional amount to increment by.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The new value of the key after incremented.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('counter', 1);
+
+// int(2);
+$redis->incr('counter');
+
+// int(4);
+$redis->incr('counter', 2);
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/incr +
+ https://redis.io/commands/incrby +
+ + +
+
+ +
+
+

+ + Redis|int|false + incrBy(string $key, int $value) + +

+
+ + + +
+

Increment a key by a specific integer value

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to increment.

int$value

The amount to increment.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('primes', 2);
+
+// int(3)
+$redis->incrby('primes', 1);
+
+// int(5)
+$redis->incrby('primes', 2);
+
+// int(7)
+$redis->incrby('primes', 2);
+
+// int(11)
+$redis->incrby('primes', 4);
+?>
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/incrby +
+ + +
+
+ +
+
+

+ + Redis|float|false + incrByFloat(string $key, float $value) + +

+
+ + + +
+

Increment a numeric key by a floating point value.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to increment

float$value

How much to increment (or decrement) the value.

+ + +

Return Value

+ + + + + + +
Redis|float|false

The new value of the key or false if the key didn't contain a string.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('tau');
+
+// float(3.1415926)
+var_dump($redis->incrByFloat('tau', 3.1415926));
+
+// float(6.2831852)
+var_dump($redis->incrByFloat('tau', 3.1415926));
+?>
+ + + + +
+
+ +
+
+

+ + Redis|array|false + info(string ...$sections) + +

+
+ + + +
+

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

If connected to Redis server >= 7.0.0 you may pass multiple optional sections.

+
+
+

Parameters

+ + + + + + + +
string...$sections

Optional section(s) you wish Redis server to return.

+ + +

Return Value

+ + + + + + +
Redis|array|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/info/ +
+ + +
+
+ +
+
+

+ + bool + isConnected() + +

+
+ + + +
+

Check if we are currently connected to a Redis instance.

+
+
+ +

Return Value

+ + + + + + +
bool

True if we are, false if not

+ + + + +
+
+ +
+
+

+ + Redis|array|false + keys(string $pattern) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$pattern
+ + +

Return Value

+ + + + + + +
Redis|array|false
+ + + + +
+
+ +
+
+

+ + Redis|int|false + lInsert(string $key, string $pos, mixed $pivot, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$pos
mixed$pivot
mixed$value
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|int|false + lLen(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|string|false + lMove(string $src, string $dst, string $wherefrom, string $whereto) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$src
string$dst
string$wherefrom
string$whereto
+ + +

Return Value

+ + + + + + +
Redis|string|false
+ + + + +
+
+ +
+
+

+ + Redis|bool|string|array + lPop(string $key, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ + + + + + +
Redis|bool|string|array
+ + + + +
+
+ +
+
+

+ + Redis|null|bool|int|array + lPos(string $key, mixed $value, array $options = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
array$options
+ + +

Return Value

+ + + + + + +
Redis|null|bool|int|array
+ + + + +
+
+ +
+
+

+ + int|Redis + lPush(string $key, mixed ...$elements) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed...$elements
+ + +

Return Value

+ + + + + + +
int|Redis
+ + + + +
+
+ +
+
+

+ + Redis|int|false + rPush(string $key, mixed ...$elements) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed...$elements
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|int|false + lPushx(string $key, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|int|false + rPushx(string $key, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + + +
+
+ +
+
+

+ + Redis|bool + lSet(string $key, int $index, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$index
mixed$value
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + + +
+
+ +
+
+

+ + int + lastSave() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
int
+ + + + +
+
+ +
+
+

+ + mixed + lindex(string $key, int $index) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$index
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + Redis|array|false + lrange(string $key, int $start, int $end) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
+ + +

Return Value

+ + + + + + +
Redis|array|false
+ + + + +
+
+ +
+
+

+ + int|Redis|false + lrem(string $key, mixed $value, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
int$count
+ + +

Return Value

+ + + + + + +
int|Redis|false
+ + + + +
+
+ +
+
+

+ + Redis|bool + ltrim(string $key, int $start, int $end) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + + +
+
+ +
+
+

+ + array|Redis + mget(array $keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$keys
+ + +

Return Value

+ + + + + + +
array|Redis
+ + + + +
+
+ +
+
+

+ + Redis|bool + migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
string|array$key
int$dstdb
int$timeout
bool$copy
bool$replace
mixed$credentials
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + + +
+
+ +
+
+

+ + bool + move(string $key, int $index) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$index
+ + +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + Redis|bool + mset(array $key_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$key_values
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + + +
+
+ +
+
+

+ + Redis|bool + msetnx(array $key_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$key_values
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + + +
+
+ +
+
+

+ + bool|Redis + multi(int $value = Redis::MULTI) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
int$value
+ + +

Return Value

+ + + + + + +
bool|Redis
+ + + + +
+
+ +
+
+

+ + Redis|int|string|false + object(string $subcommand, string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$subcommand
string$key
+ + +

Return Value

+ + + + + + +
Redis|int|string|false
+ + + + +
+
+ +
+
+

+ + bool + open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
string$persistent_id
int$retry_interval
float$read_timeout
array$context
+ + +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + bool + pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
string$persistent_id
int$retry_interval
float$read_timeout
array$context
+ + +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + bool + persist(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + bool + pexpire(string $key, int $timeout, string|null $mode = NULL) + +

+
+ + + +
+

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 +you can pass an optional mode argument that modifies how the command will execute.

Redis::expire() for a description of the mode argument.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The key to set an expiration on. +@param string $mode A two character modifier that changes how the +command works.

+

@return Redis|bool True if an expiry was set on the key, and false otherwise.

int$timeout
string|null$mode
+ + +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + Redis|bool + pexpireAt(string $key, int $timestamp, string|null $mode = NULL) + +

+
+ + + +
+

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to +Redis >= 7.0.0 you can pass an optional 'mode' argument.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timestamp
string|null$mode
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + +

See also

+ + + + + + +
+ +Redis::expire + For a description of the mode argument. + +@param string $key The key to set an expiration on. +@param string $mode A two character modifier that changes how the + command works. + +@return Redis|bool True if an expiration was set on the key, false otherwise.
+ + +
+
+ +
+
+

+ + Redis|int + pfadd(string $key, array $elements) + +

+
+ + + +
+

Add one or more elements to a Redis HyperLogLog key

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key in question.

array$elements

One or more elements to add.

+ + +

Return Value

+ + + + + + +
Redis|int

Returns 1 if the set was altered, and zero if not.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/pfadd +
+ + +
+
+ +
+
+

+ + Redis|int + pfcount(string $key) + +

+
+ + + +
+

Retrieve the cardinality of a Redis HyperLogLog key.

+
+
+

Parameters

+ + + + + + + +
string$key

The key name we wish to query.

+ + +

Return Value

+ + + + + + +
Redis|int

The estimated cardinality of the set.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/pfcount +
+ + +
+
+ +
+
+

+ + Redis|bool + pfmerge(string $dst, array $srckeys) + +

+
+ + + +
+

Merge one or more source HyperLogLog sets into a destination set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$dst

The destination key.

array$srckeys

One or more source keys.

+ + +

Return Value

+ + + + + + +
Redis|bool

Always returns true.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/pfmerge +
+ + +
+
+ +
+
+

+ + Redis|string|bool + ping(string $message = NULL) + +

+
+ + + +
+

PING the redis server with an optional string argument.

+
+
+

Parameters

+ + + + + + + +
string$message

An optional string message that Redis will reply with, if passed.

+ + +

Return Value

+ + + + + + +
Redis|string|bool

If passed no message, this command will simply return true. +If a message is passed, it will return the message.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+// bool(true)
+$redis->ping();
+
+// string(9) "beep boop"
+$redis->ping('beep boop');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/ping +
+ + +
+
+ +
+
+

+ + bool|Redis + pipeline() + +

+
+ + + +
+

Enter into pipeline mode.

Pipeline mode is the highest performance way to send many commands to Redis +as they are aggregated into one stream of commands and then all sent at once +when the user calls Redis::exec().

+

NOTE: That this is shorthand for Redis::multi(Redis::PIPELINE)

+
+
+ +

Return Value

+ + + + + + +
bool|Redis

The redis object is returned, to facilitate method chaining.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+// array(3) {
+//   [0]=>
+//   bool(true)
+//   [1]=>
+//   int(0)
+//   [2]=>
+//   int(3)
+// }
+$redis->pipeline()
+      ->set('foo', 'bar')
+      ->del('mylist')
+      ->rpush('mylist', 'a', 'b', 'c')
+      ->exec();
+?>
+ + + + +
+
+ +
+
+

+ + bool + popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
string$persistent_id
int$retry_interval
float$read_timeout
array$context
+ + +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + bool|Redis + psetex(string $key, int $expire, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$expire
mixed$value
+ + +

Return Value

+ + + + + + +
bool|Redis
+ + + + +
+
+ +
+
+

+ + bool + psubscribe(array $patterns, callable $cb) + +

+
+ + + +
+

Subscribe to one or more glob-style patterns

+
+
+

Parameters

+ + + + + + + + + + + + +
array$patterns

One or more patterns to subscribe to.

callable$cb

A callback with the following prototype:

+
function ($redis, $channel, $message) { }
+ + +

Return Value

+ + + + + + +
bool

True if we were subscribed.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/psubscribe +
+ + +
+
+ +
+
+

+ + Redis|int|false + pttl(string $key) + +

+
+ + + +
+

Get a keys time to live in milliseconds.

+
+
+

Parameters

+ + + + + + + +
string$key

The key to check.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The keys TTL or false on failure.

+

NOTE: -1 means a key has no TTL and -2 means the key doesn't exist.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->setex('ttl-key', 60, 'ttl-value');
+
+// int(60000)
+var_dump($redis->pttl('ttl-key'));
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/pttl +
+ + +
+
+ +
+
+

+ + Redis|int|false + publish(string $channel, string $message) + +

+
+ + + +
+

Publish a message to a pubsub channel

+
+
+

Parameters

+ + + + + + + + + + + + +
string$channel

The channel to publish to.

string$message

The message itself.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of subscribed clients to the given channel.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/publish +
+ + +
+
+ +
+
+

+ + mixed + pubsub(string $command, mixed $arg = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$command
mixed$arg
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + Redis|array|bool + punsubscribe(array $patterns) + +

+
+ + + +
+

Unsubscribe from one or more channels by pattern

+
+
+

Parameters

+ + + + + + + +
array$patterns

One or more glob-style patterns of channel names.

+ + +

Return Value

+ + + + + + +
Redis|array|bool

The array of subscribed patterns or false on failure.

+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/punsubscribe +
+ https://redis.io/commands/subscribe +
+ +Redis::subscribe +
+ + +
+
+ +
+
+

+ + Redis|array|string|bool + rPop(string $key, int $count = 0) + +

+
+ + + +
+

Pop one or more elements from the end of a list.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

A redis LIST key name.

int$count

The maximum number of elements to pop at once.

+

NOTE: The count argument requires Redis >= 6.2.0

+ + +

Return Value

+ + + + + + +
Redis|array|string|bool

One ore more popped elements or false if all were empty.

+
<?php
+<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('mylist');
+$redis->rPush('mylist', 'one', 'two', 'three');
+
+// string(5) "three"
+$redis->rPop('mylist');
+
+// string(3) "two"
+$redis->rPop('mylist');
+
+// string(3) "one"
+$redis->rPop('mylist');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/rpop +
+ + +
+
+ +
+
+

+ + Redis|string|false + randomKey() + +

+
+ + + +
+

Return a random key from the current database

+
+
+ +

Return Value

+ + + + + + +
Redis|string|false

A random key name or false if no keys exist

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/randomkey +
+ + +
+
+ +
+
+

+ + mixed + rawcommand(string $command, mixed ...$args) + +

+
+ + + +
+

Execute any arbitrary Redis command by name.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$command

The command to execute

mixed...$args

One or more arguments to pass to the command.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->rawCommand('del', 'mystring', 'mylist');
+$redis->rawCommand('set', 'mystring', 'myvalue');
+$redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three');
+
+// string(7) "myvalue"
+$redis->rawCommand('get', 'mystring');
+
+// array(3) {
+//   [0]=>
+//   string(3) "one"
+//   [1]=>
+//   string(3) "two"
+//   [2]=>
+//   string(5) "three"
+// }
+$redis->rawCommand('lrange', 'mylist', 0, -1);
+?>
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + Redis|bool + rename(string $old_name, string $new_name) + +

+
+ + + +
+

Unconditionally rename a key from $old_name to $new_name

+
+
+

Parameters

+ + + + + + + + + + + + +
string$old_name

The original name of the key

string$new_name

The new name for the key

+ + +

Return Value

+ + + + + + +
Redis|bool

True if the key was renamed or false if not.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/rename +
+ + +
+
+ +
+
+

+ + Redis|bool + renameNx(string $key_src, string $key_dst) + +

+
+ + + +
+

Renames $key_src to $key_dst but only if newkey does not exist.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key_src

The source key name

string$key_dst

The destination key name.

+ + +

Return Value

+ + + + + + +
Redis|bool

True if the key was renamed, false if not.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('src', 'dst', 'existing-dst');
+
+$redis->set('src', 'src_key');
+$redis->set('existing-dst', 'i_exist');
+
+// bool(true)
+$redis->renamenx('src', 'dst');
+
+// bool(false)
+$redis->renamenx('dst', 'existing-dst');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/renamenx +
+ + +
+
+ +
+
+

+ + Redis|bool + reset() + +

+
+ + + +
+

Reset the state of the connection.

+
+
+ +

Return Value

+ + + + + + +
Redis|bool

Should always return true unless there is an error.

+ + + + +
+
+ +
+
+

+ + Redis|bool + restore(string $key, int $ttl, string $value, array|null $options = NULL) + +

+
+ + + +
+

Restore a key by the binary payload generated by the DUMP command.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The name of the key you wish to create.

int$ttl

What Redis should set the key's TTL (in milliseconds) to once it is created. +Zero means no TTL at all.

string$value

The serialized binary value of the string (generated by DUMP).

array|null$options

An array of additional options that modifies how the command operates.

+
$options = [
+    'ABSTTL'          // If this is present, the `$ttl` provided by the user should
+                      // be an absolute timestamp, in milliseconds()
+
+    'REPLACE'         // This flag instructs Redis to store the key even if a key with
+                      // that name already exists.
+
+    'IDLETIME' => int // Tells Redis to set the keys internal 'idletime' value to a
+                      // specific number (see the Redis command OBJECT for more info).
+    'FREQ'     => int // Tells Redis to set the keys internal 'FREQ' value to a specific
+                      // number (this relates to Redis' LFU eviction algorithm).
+];
+ + +

Return Value

+ + + + + + +
Redis|bool

True if the key was stored, false if not.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('captains');
+
+$redis->sAdd('captains', 'Janeway', 'Picard', 'Sisko', 'Kirk', 'Archer');
+
+$serialized = $redis->dump('captains');
+
+$redis->select(1);
+$redis->restore('captains-backup', 0, $serialized);
+
+//array(5) {
+//  [0]=>
+//  string(6) "Archer"
+//  [1]=>
+//  string(4) "Kirk"
+//  [2]=>
+//  string(5) "Sisko"
+//  [3]=>
+//  string(6) "Picard"
+//  [4]=>
+//  string(7) "Janeway"
+//}
+var_dump($redis->sMembers('captains-backup'));
+?>
+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/restore +
+ https://redis.io/commands/dump +
+ +Redis::dump +
+ + +
+
+ +
+
+

+ + mixed + role() + +

+
+ + + +
+

Query whether the connected instance is a primary or replica

+
+
+ +

Return Value

+ + + + + + +
mixed

Will return an array with the role of the connected instance unless there is +an error.

+ + + + +
+
+ +
+
+

+ + Redis|string|false + rpoplpush(string $srckey, string $dstkey) + +

+
+ + + +
+

Atomically pop an element off the end of a Redis LIST and push it to the beginning of +another.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$srckey

The source key to pop from.

string$dstkey

The destination key to push to.

+ + +

Return Value

+ + + + + + +
Redis|string|false

The popped element or false if the source key was empty.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->pipeline()
+      ->del('list1', 'list2')
+      ->rpush('list1', 'list1-1', 'list1-2')
+      ->rpush('list2', 'list2-1', 'list2-2')
+      ->exec();
+
+var_dump($redis->rpoplpush('list2', 'list1'));
+var_dump($redis->lrange('list1', 0, -1));
+
+// --- OUTPUT ---
+// string(7) "list2-2"
+//
+// array(3) {
+//   [0]=>
+//   string(7) "list2-2"
+//   [1]=>
+//   string(7) "list1-1"
+//   [2]=>
+//   string(7) "list1-2"
+// }
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/rpoplpush +
+ + +
+
+ +
+
+

+ + Redis|int|false + sAdd(string $key, mixed $value, mixed ...$other_values) + +

+
+ + + +
+

Add one or more values to a Redis SET key.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The key name

mixed$value
mixed...$other_values
+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of values added to the set.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('myset');
+
+var_dump($redis->sadd('myset', 'foo', 'bar', 'baz'));
+var_dump($redis->sadd('myset', 'foo', 'new'));
+
+// --- OUTPUT ---
+// int(3)
+// int(1)
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/sadd +
+ + +
+
+ +
+
+

+ + int + sAddArray(string $key, array $values) + +

+
+ + + +
+

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but +instead of being variadic, takes a single array of values.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The set to add values to.

array$values

One or more members to add to the set.

+ + +

Return Value

+ + + + + + +
int

The number of members added to the set.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('myset');
+
+var_dump($redis->sAddArray('myset', ['foo', 'bar', 'baz']));
+var_dump($redis->sAddArray('myset', ['foo', 'new']));
+
+// --- OUTPUT ---
+// int(3)
+// int(1)
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/sadd +
+ \Redis::sadd() +
+ + +
+
+ +
+
+

+ + Redis|array|false + sDiff(string $key, string ...$other_keys) + +

+
+ + + +
+

Given one or more Redis SETS, this command returns all of the members from the first +set that are not in any subsequent set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The first set

string...$other_keys

One or more additional sets

+ + +

Return Value

+ + + + + + +
Redis|array|false

Returns the elements from keys 2..N that don't exist in the +first sorted set, or false on failure.

+

+<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->pipeline()
+      ->del('set1', 'set2', 'set3')
+      ->sadd('set1', 'apple', 'banana', 'carrot', 'date')
+      ->sadd('set2', 'carrot')
+      ->sadd('set3', 'apple', 'carrot', 'eggplant')
+      ->exec();
+
+// NOTE:  'banana' and 'date' are in set1 but none of the subsequent sets.
+var_dump($redis->sdiff('set1', 'set2', 'set3'));
+
+// --- OUTPUT ---
+array(2) {
+  [0]=>
+  string(6) "banana"
+  [1]=>
+  string(4) "date"
+}
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/sdiff +
+ + +
+
+ +
+
+

+ + Redis|int|false + sDiffStore(string $dst, string $key, string ...$other_keys) + +

+
+ + + +
+

This method performs the same operation as SDIFF except it stores the resulting diff +values in a specified destination key.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$dst

The key where to store the result

string$key

The first key to perform the DIFF on

string...$other_keys

One or more additional keys.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of values stored in the destination set or false on failure.

+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/sdiffstore +
+ \Redis::sdiff() +
+ + +
+
+ +
+
+

+ + Redis|array|false + sInter(array|string $key, string ...$other_keys) + +

+
+ + + +
+

Given one or more Redis SET keys, this command will return all of the elements that are +in every one.

+
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key

The first SET key to intersect.

string...$other_keys

One or more Redis SET keys.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->pipeline()
+      ->del('alice_likes', 'bob_likes', 'bill_likes')
+      ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato')
+      ->sadd('bob_likes', 'asparagus', 'carrot', 'potato')
+      ->sadd('bill_likes', 'broccoli', 'potato')
+      ->exec();
+
+// NOTE:  'potato' is the only value in all three sets
+var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes'));
+
+// --- OUTPUT ---
+// array(1) {
+//   [0]=>
+//   string(6) "potato"
+// }
+?>
+ + +

Return Value

+ + + + + + +
Redis|array|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/sinter +
+ + +
+
+ +
+
+

+ + Redis|int|false + sintercard(array $keys, int $limit = -1) + +

+
+ + + +
+

Compute the intersection of one or more sets and return the cardinality of the result.

+
+
+

Parameters

+ + + + + + + + + + + + +
array$keys

One or more set key names.

int$limit

A maximum cardinality to return. This is useful to put an upper bound +on the amount of work Redis will do.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('set1', 'set2', 'set3');
+
+$redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot');
+$redis->sAdd('set2', 'apple',         'banana');
+$redis->sAdd('set3',          'pear', 'banana');
+
+// int(1)
+var_dump($redis->sInterCard(['set1', 'set2', 'set3']));
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/sintercard +
+ + +
+
+ +
+
+

+ + Redis|int|false + sInterStore(array|string $key, string ...$other_keys) + +

+
+ + + +
+

Perform the intersection of one or more Redis SETs, storing the result in a destination +key, rather than returning them.

+
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys

If the first argument was a string, subsequent arguments should +be source key names.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of values stored in the destination key or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+// OPTION 1:  A single array
+$redis->sInterStore(['dst', 'src1', 'src2', 'src3']);
+
+// OPTION 2:  Variadic
+$redis->sInterStore('dst', 'src1', 'src'2', 'src3');
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/sinterstore +
+ \Redis::sinter() +
+ + +
+
+ +
+
+

+ + Redis|array|false + sMembers(string $key) + +

+
+ + + +
+

Retrieve every member from a set key.

+
+
+

Parameters

+ + + + + + + +
string$key

The set name.

+ + +

Return Value

+ + + + + + +
Redis|array|false

Every element in the set or false on failure.

+
$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('tng-crew');
+
+$redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']);
+
+// Array
+// (
+//     [0] => Riker
+//     [1] => Crusher
+//     [2] => Troi
+//     [3] => Worf
+//     [4] => LaForge
+//     [5] => Picard
+//     [6] => Broccoli
+//     [7] => Data
+// )
+$redis->sMembers('tng-crew');
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/smembers +
+ + +
+
+ +
+
+

+ + Redis|array|false + sMisMember(string $key, string $member, string ...$other_members) + +

+
+ + + +
+

Check if one or more values are members of a set.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The set to query.

string$member

The first value to test if exists in the set.

string...$other_members

Any number of additional values to check.

+ + +

Return Value

+ + + + + + +
Redis|array|false

An array of integers representing whether each passed value +was a member of the set.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('ds9-crew');
+$redis->sAdd('ds9-crew', ...["Sisko", "Kira", "Dax", "Worf", "Bashir", "O'Brien"]);
+
+$names = ['Sisko', 'Picard', 'Data', 'Worf'];
+$members = $redis->sMIsMember('ds9-crew', ...$names);
+
+// array(4) {
+//   ["Sisko"]=>
+//   int(1)
+//   ["Picard"]=>
+//   int(0)
+//   ["Data"]=>
+//   int(0)
+//   ["Worf"]=>
+//   int(1)
+// }
+var_dump(array_combine($names, $members));
+?>
+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/smismember +
+ https://redis.io/commands/smember +
+ \Redis::smember() +
+ + +
+
+ +
+
+

+ + Redis|bool + sMove(string $src, string $dst, mixed $value) + +

+
+ + + +
+

Pop a member from one set and push it onto another. This command will create the +destination set if it does not currently exist.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$src

The source set.

string$dst

The destination set.

mixed$value

The member you wish to move.

+ + +

Return Value

+ + + + + + +
Redis|bool

True if the member was moved, and false if it wasn't in the set.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('numbers', 'evens');
+$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
+
+$redis->sMove('numbers', 'evens', 'zero');
+$redis->sMove('numbers', 'evens', 'two');
+$redis->sMove('numbers', 'evens', 'four');
+
+// array(2) {
+//   [0]=>
+//   string(5) "three"
+//   [1]=>
+//   string(3) "one"
+// }
+var_dump($redis->sMembers('numbers'));
+
+// array(3) {
+//   [0]=>
+//   string(4) "zero"
+//   [1]=>
+//   string(3) "two"
+//   [2]=>
+//   string(4) "four"
+// }
+var_dump($redis->sMembers('evens'));
+
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/smove +
+ + +
+
+ +
+
+

+ + Redis|string|array|false + sPop(string $key, int $count = 0) + +

+
+ + + +
+

Remove one or more elements from a set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The set in question.

int$count

An optional number of members to pop. This defaults to +removing one element.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('numbers', 'evens');
+$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
+
+$redis->sMove('numbers', 'evens', 'zero');
+$redis->sMove('numbers', 'evens', 'two');
+$redis->sMove('numbers', 'evens', 'four');
+
+// array(2) {
+//   [0]=>
+//   string(5) "three"
+//   [1]=>
+//   string(3) "one"
+// }
+var_dump($redis->sMembers('numbers'));
+
+// array(3) {
+//   [0]=>
+//   string(4) "zero"
+//   [1]=>
+//   string(3) "two"
+//   [2]=>
+//   string(4) "four"
+// }
+var_dump($redis->sMembers('evens'));
+?>
+ + +

Return Value

+ + + + + + +
Redis|string|array|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/spop +
+ + +
+
+ +
+
+

+ + Redis|string|array|false + sRandMember(string $key, int $count = 0) + +

+
+ + + +
+

Retrieve one or more random members of a set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The set to query.

int$count

An optional count of members to return.

+

If this value is positive, Redis will return up to the requested +number but with unique elements that will never repeat. This means +you may recieve fewer then $count replies.

+

If the number is negative, Redis will return the exact number requested +but the result may contain duplicate elements.

+ + +

Return Value

+ + + + + + +
Redis|string|array|false

One or more random members or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('elder-gods');
+
+$redis->sAdd('elder-gods', ["Cthulhu", "Azathoth", "Daoloth", "D'endrrah"]);
+
+// A single random member returned.
+$rng1 = $redis->sRandMember('elder-gods');
+
+// Up to SCARD `elder-gods` random members returned
+$rng2 = $redis->sRandMember('elder-gods', 9999);
+
+// 9999 elements from the set returned with duplicates
+$rng3 = $redis->sRandMember('elder-gods', -9999);
+?>
+ + + + +
+
+ +
+
+

+ + Redis|array|false + sUnion(string $key, string ...$other_keys) + +

+
+ + + +
+

Returns the union of one or more Redis SET keys.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The first SET to do a union with

string...$other_keys

One or more subsequent keys

+ + +

Return Value

+ + + + + + +
Redis|array|false

The union of the one or more input sets or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->pipeline()
+      ->del('set1', 'set2', 'set3')
+      ->sadd('set1', 'apple', 'banana', 'carrot')
+      ->sadd('set2', 'apple', 'carrot', 'fish')
+      ->sadd('set3', 'carrot', 'fig', 'eggplant');
+
+var_dump($redis->sunion('set1', 'set2', 'set3'));
+
+// --- OPUTPUT ---
+// array(5) {
+//   [0]=>
+//   string(6) "banana"
+//   [1]=>
+//   string(5) "apple"
+//   [2]=>
+//   string(4) "fish"
+//   [3]=>
+//   string(6) "carrot"
+//   [4]=>
+//   string(8) "eggplant"
+// }
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/sunion +
+ + +
+
+ +
+
+

+ + Redis|int|false + sUnionStore(string $dst, string $key, string ...$other_keys) + +

+
+ + + +
+

Perform a union of one or more Redis SET keys and store the result in a new set

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$dst

The destination key

string$key

The first source key

string...$other_keys

One or more additional source keys

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of elements stored in the destination SET or +false on failure.

+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/sunionstore +
+ \Redis::sunion() +
+ + +
+
+ +
+
+

+ + Redis|bool + save() + +

+
+ + + +
+

Persist the Redis database to disk. This command will block the server until the save is +completed. For a nonblocking alternative, see Redis::bgsave().

+
+
+ +

Return Value

+ + + + + + +
Redis|bool

Returns true unless an error occurs.

+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/save +
+ \Redis::bgsave() +
+ + +
+
+ +
+
+

+ + array|false + scan(int|null $iterator, string|null $pattern = null, int $count = 0, string $type = NULL) + +

+
+ + + +
+

Incrementally scan the Redis keyspace, with optional pattern and type matching.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
int|null$iterator

The cursor returned by Redis for every subsequent call to SCAN. On +the initial invocation of the call, it should be initialized by the +caller to NULL. Each time SCAN is invoked, the iterator will be +updated to a new number, until finally Redis will set the value to +zero, indicating that the scan is complete.

string|null$pattern

An optional glob-style pattern for matching key names. If passed as +NULL, it is the equivalent of sending '*' (match every key).

int$count

A hint to redis that tells it how many keys to return in a single +call to SCAN. The larger the number, the longer Redis may block +clients while iterating the key space.

string$type

An optional argument to specify which key types to scan (e.g. +'STRING', 'LIST', 'SET')

+ + +

Return Value

+ + + + + + +
array|false

An array of keys, or false if no keys were returned for this +invocation of scan. Note that it is possible for Redis to return +zero keys before having scanned the entire key space, so the caller +should instead continue to SCAN until the iterator reference is +returned to zero.

+

A note about Redis::SCAN_NORETRY and Redis::SCAN_RETRY.

+

For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of +keys with a nonzero iterator. This can happen when matching against a pattern that very few +keys match inside a key space with a great many keys. The following example demonstrates how +to use Redis::scan() with the option disabled and enabled.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+
+$it = NULL;
+
+do {
+    $keys = $redis->scan($it, '*zorg*');
+    foreach ($keys as $key) {
+        echo "KEY: $key\n";
+    }
+} while ($it != 0);
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+
+$it = NULL;
+
+// When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an
+// empty array of keys when the iterator is nonzero.
+while ($keys = $redis->scan($it, '*zorg*')) {
+    foreach ($keys as $key) {
+        echo "KEY: $key\n";
+    }
+}
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/scan +
+ +Redis::setOption +
+ + +
+
+ +
+
+

+ + Redis|int|false + scard(string $key) + +

+
+ + + +
+

Retrieve the number of members in a Redis set.

+
+
+

Parameters

+ + + + + + + +
string$key

The set to get the cardinality of.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The cardinality of the set or false on failure.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('set');
+$redis->sadd('set', 'one', 'two', 'three', 'four', 'five');
+
+// Returns 5
+$redis->scard('set');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/scard +
+ + +
+
+ +
+
+

+ + mixed + script(string $command, mixed ...$args) + +

+
+ + + +
+

An administrative command used to interact with LUA scripts stored on the server.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$command

The script suboperation to execute.

mixed...$args

One ore more additional argument

+ + +

Return Value

+ + + + + + +
mixed

This command returns various things depending on the specific operation executed.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$lua = sprintf("return %f", microtime(true));
+
+// array(1) {
+//   [0]=>
+//   int(0)
+// }
+var_dump($redis->script('exists', sha1($lua)));
+
+$redis->script('load', $lua);
+
+// array(1) {
+//   [0]=>
+//   int(1)
+// }
+var_dump($redis->script('exists', sha1($lua)));
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/script +
+ + +
+
+ +
+
+

+ + Redis|bool + select(int $db) + +

+
+ + + +
+

Select a specific Redis database.

+
+
+

Parameters

+ + + + + + + +
int$db

The database to select. Note that by default Redis has 16 databases (0-15).

+ + +

Return Value

+ + + + + + +
Redis|bool

true on success and false on failure

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->select(1);
+$redis->set('this_is_db_1', 'test');
+
+$redis->select(0);
+var_dump($redis->exists('this_is_db_1'));
+
+$redis->select(1);
+var_dump($redis->exists('this_is_db_1'));
+
+// --- OUTPUT ---
+// int(0)
+// int(1)
+?>
+ + + + +
+
+ +
+
+

+ + Redis|string|bool + set(string $key, mixed $value, mixed $options = NULL) + +

+
+ + + +
+

Create or set a Redis STRING key to a value.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The key name to set.

mixed$value

The value to set the key to.

mixed$options

Either an array with options for how to perform the set or an +integer with an expiration. If an expiration is set PhpRedis +will actually send the SETEX command.

+

OPTION DESCRIPTION

+
+

['EX' => 60] expire 60 seconds. +['PX' => 6000] expire in 6000 milliseconds. +['EXAT' => time() + 10] expire in 10 seconds. +['PXAT' => time()*1000 + 1000] expire in 1 second. +['KEEPTTL' => true] Redis will not update the key's current TTL. +['XX'] Only set the key if it already exists. +['NX'] Only set the key if it doesn't exist. +['GET'] Instead of returning +OK return the previous value of the +key or NULL if the key didn't exist.

+ + +

Return Value

+ + + + + + +
Redis|string|bool

True if the key was set or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('key', 'value');
+
+// Will actually send `SETEX 60 key value` to Redis.
+$redis->set('key', 'expires_in_60_seconds', 60);
+
+// Only have Redis set the key if it already exists.
+$redis->set('key', 'options_set', ['XX']);
+
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/set +
+ https://redis.io/commands/setex +
+ + +
+
+ +
+
+

+ + Redis|int|false + setBit(string $key, int $idx, bool $value) + +

+
+ + + +
+

Set a specific bit in a Redis string to zero or one

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The Redis STRING key to modify

int$idx
bool$value

Whether to set the bit to zero or one.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The original value of the bit or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('foo', 'bar');
+
+// Flip the 7th bit to 1
+$redis->setbit('foo', 7, 1);
+
+// The bit flip turned 'bar' -> 'car'
+$redis->get('foo');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/setbit +
+ + +
+
+ +
+
+

+ + Redis|int|false + setRange(string $key, int $index, string $value) + +

+
+ + + +
+

Update or append to a Redis string at a specific starting index

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The key to update

int$index

Where to insert the provided value

string$value

The value to copy into the string.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The new length of the string or false on failure

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('message', 'Hello World');
+
+// Update 'Hello World' to 'Hello Redis'
+$redis->setRange('message', 6, 'Redis');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/setrange +
+ + +
+
+ +
+
+

+ + bool + setOption(int $option, mixed $value) + +

+
+ + + +
+

Set a configurable option on the Redis object.

+
+
+

Parameters

+ + + + + + + + + + + + +
int$option

The option constant.

mixed$value

The option value.

+ + +

Return Value

+ + + + + + +
bool

True if the setting was updated, false if not.

+ + + +

See also

+ + + + + + + + + + +
+ +Redis::getOption + Following are a list of options you can set: + +OPTION TYPE DESCRIPTION +OPT_MAX_RETRIES int The maximum number of times Redis will attempt to reconnect + if it gets disconnected, before throwing an exception. + +OPT_SCAN enum Redis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY + + Redis::SCAN_NORETRY (default) + -------------------------------------------------------- + PhpRedis will only call `SCAN` once for every time the + user calls Redis::scan(). This means it is possible for + an empty array of keys to be returned while there are + still more keys to be processed. + + Redis::SCAN_RETRY + -------------------------------------------------------- + PhpRedis may make multiple calls to `SCAN` for every + time the user calls Redis::scan(), and will never return + an empty array of keys unless Redis returns the iterator + to zero (meaning the `SCAN` is complete). + + +OPT_SERIALIZER int One of the installed serializers, which can vary depending + on how PhpRedis was compiled. All of the supported serializers + are as follows: + + Redis::SERIALIZER_NONE + Redis::SERIALIZER_PHP + Redis::SERIALIZER_IGBINARY + Redis::SERIALIZER_MSGPACK + Redis::SERIALIZER_JSON + + Note: The PHP and JSON serializers are always available. + +OPT_PREFIX string A string PhpRedis will use to prefix every key we read or write. + To disable the prefix, you may pass an empty string or NULL. + +OPT_READ_TIMEOUT double How long PhpRedis will block for a response from Redis before + throwing a 'read error on connection' exception. + +OPT_TCP_KEEPALIVE bool Set or disable TCP_KEEPALIVE on the connection. + +OPT_COMPRESSION enum Set an automatic compression algorithm to use when reading/writing + data to Redis. All of the supported compressors are as follows: + + Redis::COMPRESSION_NONE + Redis::COMPRESSION_LZF + Redis::COMPRESSION_LZ4 + Redis::COMPRESSION_ZSTD + + Note: Some of these may not be available depending on how Redis + was compiled. + +OPT_REPLY_LITERAL bool If set to true, PhpRedis will return the literal string Redis returns + for LINE replies (e.g. '+OK'), rather than `true`. + +OPT_COMPRESSION_LEVEL int Set a specific compression level if Redis is compressing data. + +OPT_NULL_MULTIBULK_AS_NULL bool Causes PhpRedis to return `NULL` rather than `false` for NULL MULTIBULK + RESP replies (i.e. `*-1`). + +OPT_BACKOFF_ALGORITHM enum The exponential backoff strategy to use. +OPT_BACKOFF_BASE int The minimum delay between retries when backing off. +OPT_BACKOFF_CAP int The maximum delay between replies when backing off.
+ +Redis::__construct + for details about backoff strategies.
+ + +
+
+ +
+
+

+ + Redis|bool + setex(string $key, int $expire, mixed $value) + +

+
+ + + +
+

Set a Redis STRING key with a specific expiration in seconds.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The name of the key to set.

int$expire

The key's expiration in seconds.

mixed$value

The value to set the key.

+ + +

Return Value

+ + + + + + +
Redis|bool

True on success or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+// Set a key with a 60 second expiration
+$redis->set('some_key', 60, 'some_value');
+
+?>php
+ + + + +
+
+ +
+
+

+ + Redis|bool + setnx(string $key, mixed $value) + +

+
+ + + +
+

Set a key to a value, but only if that key does not already exist.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key name to set.

mixed$value

What to set the key to.

+ + +

Return Value

+ + + + + + +
Redis|bool

Returns true if the key was set and false otherwise.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('new-key');
+$redis->set('existing-key', 'already-exists');
+
+// Key is new, returns 1
+$redis->setnx('key1', 'here-is-a-new-key');
+
+// Key exists, returns 0
+$redis->setnx('existing-key', 'new-value');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/setnx +
+ + +
+
+ +
+
+

+ + Redis|bool + sismember(string $key, mixed $value) + +

+
+ + + +
+

Check whether a given value is the member of a Redis SET.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The redis set to check.

mixed$value

The value to test.

+ + +

Return Value

+ + + + + + +
Redis|bool

True if the member exists and false if not.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->multi()
+      ->del('myset')
+      ->sadd('myset', 'foo', 'bar', 'baz')
+      ->exec();
+
+// Will return true, as 'foo' is in the set
+$redis->sismember('myset', 'foo');
+
+// Will return false, as 'not-in-set' is not in the set
+$redis->sismember('myset', 'not-in-set');
+?>
+ + + + +
+
+ +
+
+

+ + Redis|bool + slaveof(string $host = NULL, int $port = 6379) + deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

Turn a redis instance into a replica of another or promote a replica +to a primary.

This method and the corresponding command in Redis has been marked deprecated +and users should instead use Redis::replicaof() if connecting to redis-server

+
+

= 5.0.0.

+

+
+
+

Parameters

+ + + + + + + + + + + + +
string$host
int$port
+ + +

Return Value

+ + + + + + +
Redis|bool
+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/slaveof +
+ https://redis.io/commands/replicaof +
+ +Redis::slaveof +
+ + +
+
+ +
+
+

+ + Redis|bool + replicaof(string $host = NULL, int $port = 6379) + +

+
+ + + +
+

Used to turn a Redis instance into a replica of another, or to remove +replica status promoting the instance to a primary.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$host

The host of the primary to start replicating.

int$port

The port of the primary to start replicating.

+ + +

Return Value

+ + + + + + +
Redis|bool

Success if we were successfully able to start replicating a primary or +were able to promote teh replicat to a primary.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+// Attempt to become a replica of a Redis instance at 127.0.0.1:9999
+$redis->slaveof('127.0.0.1', 9999);
+
+// When passed no arguments, PhpRedis will deliver the command `SLAVEOF NO ONE`
+// attempting to promote the instance to a primary.
+$redis->slaveof();
+?>
+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/replicaof +
+ https://redis.io/commands/slaveof +
+ +Redis::slaveof +
+ + +
+
+ +
+
+

+ + Redis|int|false + touch(array|string $key_or_array, string ...$more_keys) + +

+
+ + + +
+

Update one or more keys last modified metadata.

+
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key_or_array
string...$more_keys

One or more keys to send to the command.

+ + +

Return Value

+ + + + + + +
Redis|int|false

This command returns the number of keys that exist and +had their last modified time reset

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/touch/ +
+ + +
+
+ +
+
+

+ + mixed + slowlog(string $operation, int $length = 0) + +

+
+ + + +
+

Interact with Redis' slowlog functionality in various ways, depending +on the value of 'operation'.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$operation

The operation you wish to perform.  This can +be one of the following values: +'GET' - Retrieve the Redis slowlog as an array. +'LEN' - Retrieve the length of the slowlog. +'RESET' - Remove all slowlog entries.

+
<?php
+$redis->slowlog('get', -1);  // Retrieve all slowlog entries.
+$redis->slowlog('len');       // Retrieve slowlog length.
+$redis->slowlog('reset');     // Reset the slowlog.
+?>
int$length

This optional argument can be passed when operation +is 'get' and will specify how many elements to retrieve. +If omitted Redis will send up to a default number of +entries, which is configurable.

+

Note: With Redis >= 7.0.0 you can send -1 to mean "all".

+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/slowlog/ +
+ + +
+
+ +
+
+

+ + mixed + sort(string $key, array|null $options = null) + +

+
+ + + +
+

Sort the contents of a Redis key in various ways.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key you wish to sort

array|null$options

Various options controlling how you would like the +data sorted. See blow for a detailed description +of this options array.

+ + +

Return Value

+ + + + + + +
mixed

This command can either return an array with the sorted data +or the number of elements placed in a destination set when +using the STORE option.

+
<?php
+$options = [
+    'SORT'  => 'ASC'|| 'DESC' // Sort in descending or descending order.
+    'ALPHA' => true || false  // Whether to sort alphanumerically.
+    'LIMIT' => [0, 10]        // Return a subset of the data at offset, count
+    'BY'    => 'weight_*'     // For each element in the key, read data from the
+                                 external key weight_* and sort based on that value.
+    'GET'   => 'weight_*'     // For each element in the source key, retrieve the
+                                 data from key weight_* and return that in the result
+                                 rather than the source keys' element.  This can
+                                 be used in combination with 'BY'
+];
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/sort/ +
+ + +
+
+ +
+
+

+ + mixed + sort_ro(string $key, array|null $options = null) + +

+
+ + + +
+

This is simply a read-only variant of the sort command

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array|null$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ +Redis::sort +
+ + +
+
+ +
+
+

+ + array + sortAsc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) + deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string|null$pattern
mixed$get
int$offset
int$count
string|null$store
+ + +

Return Value

+ + + + + + +
array
+ + + + +
+
+ +
+
+

+ + array + sortAscAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) + deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string|null$pattern
mixed$get
int$offset
int$count
string|null$store
+ + +

Return Value

+ + + + + + +
array
+ + + + +
+
+ +
+
+

+ + array + sortDesc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) + deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string|null$pattern
mixed$get
int$offset
int$count
string|null$store
+ + +

Return Value

+ + + + + + +
array
+ + + + +
+
+ +
+
+

+ + array + sortDescAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) + deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string|null$pattern
mixed$get
int$offset
int$count
string|null$store
+ + +

Return Value

+ + + + + + +
array
+ + + + +
+
+ +
+
+

+ + Redis|int|false + srem(string $key, mixed $value, mixed ...$other_values) + +

+
+ + + +
+

Remove one or more values from a Redis SET key.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The Redis SET key in question.

mixed$value

The first value to remove.

mixed...$other_values
+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of values removed from the set or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->pipeline()->del('set1')
+                  ->sadd('set1', 'foo', 'bar', 'baz')
+                  ->exec();
+
+var_dump($redis->sRem('set1', 'foo', 'bar', 'not-in-the-set'));
+
+// --- OUTPUT ---
+// int(2)
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/srem +
+ + +
+
+ +
+
+

+ + array|false + sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

Scan the members of a redis SET key.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The Redis SET key in question.

int|null$iterator

A reference to an iterator which should be initialized to NULL that +PhpRedis will update with the value returned from Redis after each +subsequent call to SSCAN. Once this cursor is zero you know all +members have been traversed.

string|null$pattern

An optional glob style pattern to match against, so Redis only +returns the subset of members matching this pattern.

int$count

A hint to Redis as to how many members it should scan in one command +before returning members for that iteration.

+
$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('myset');
+for ($i = 0; $i < 10000; $i++) {
+    $redis->sAdd('myset', "member:$i");
+}
+$redis->sadd('myset', 'foofoo');
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+
+$scanned = 0;
+$it = NULL;
+
+// Without Redis::SCAN_RETRY we may receive empty results and
+// a nonzero iterator.
+do {
+    // Scan members containing '5'
+    $members = $redis->sscan('myset', $it, '*5*');
+    foreach ($members as $member) {
+         echo "NORETRY: $member\n";
+         $scanned++;
+    }
+} while ($it != 0);
+echo "TOTAL: $scanned\n";
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+
+$scanned = 0;
+$it = NULL;
+
+// With Redis::SCAN_RETRY PhpRedis will never return an empty array
+// when the cursor is non-zero
+while (($members = $redis->sscan('myset', $it, '*5*'))) {
+    foreach ($members as $member) {
+        echo "RETRY: $member\n";
+        $scanned++;
+    }
+}
+echo "TOTAL: $scanned\n";
+?>
+ + +

Return Value

+ + + + + + +
array|false
+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/sscan +
+ https://redis.io/commands/scan +
+ +Redis::setOption +
+ + +
+
+ +
+
+

+ + Redis|int|false + strlen(string $key) + +

+
+ + + +
+

Retrieve the length of a Redis STRING key.

+
+
+

Parameters

+ + + + + + + +
string$key

The key we want the length of.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The length of the string key if it exists, zero if it does not, and +false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('string');
+
+$redis->set('string', 'foo');
+
+// strlen('foo') == 3
+$redis->strlen('string');
+
+$redis->append('string', 'bar');
+
+// strlen('foobar') == 6
+$redis->strlen('string');
+
+?>
+ + + + +
+
+ +
+
+

+ + bool + subscribe(array $channels, callable $cb) + +

+
+ + + +
+

Subscribe to one or more Redis pubsub channels.

+
+
+

Parameters

+ + + + + + + + + + + + +
array$channels

One or more channel names.

callable$cb

The callback PhpRedis will invoke when we receive a message +from one of the subscribed channels.

+ + +

Return Value

+ + + + + + +
bool

True on success, false on faiilure. Note that this command will block the +client in a subscribe loop, waiting for messages to arrive.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
+    echo "[$channel]: $message\n";
+
+    // Unsubscribe from the message channel when we read 'quit'
+    if ($message == 'quit') {
+        echo "Unsubscribing from '$channel'\n";
+        $redis->unsubscribe([$channel]);
+    }
+});
+
+// Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be
+// broken and this command will execute.
+echo "Subscribe loop ended\n";
+?>
+ + + + +
+
+ +
+
+

+ + Redis|bool + swapdb(int $src, int $dst) + +

+
+ + + +
+

Atomically swap two Redis databases so that all of the keys in the source database will +now be in the destination database and vice-versa.

Note: This command simply swaps Redis' internal pointer to the database and is therefore +very fast, regardless of the size of the underlying databases.

+
+
+

Parameters

+ + + + + + + + + + + + +
int$src

The source database number

int$dst

The destination database number

+ + +

Return Value

+ + + + + + +
Redis|bool

Success if the databases could be swapped and false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->multi()->select(0)
+               ->set('db0-key1', 'value1')->set('db0-key2', 'value2')
+               ->select(1)
+               ->set('db1-key1', 'value1')->set('db1-key2', 'value2')
+               ->select(0)
+               ->exec();
+
+// Array
+// (
+//     [0] => db0-key1
+//     [1] => db0-key2
+// )
+print_r($redis->keys('*'));
+
+// Swap db0 and db1
+$redis->swapdb(0, 1);
+
+// Array
+// (
+//     [0] => db1-key2
+//     [1] => db1-key1
+// )
+print_r($redis->keys('*'));
+
+// Swap them back
+$redis->swapdb(0, 1);
+
+// Array
+// (
+//     [0] => db0-key1
+//     [1] => db0-key2
+// )
+print_r($redis->keys('*'));
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/swapdb +
+ +Redis::del +
+ + +
+
+ +
+
+

+ + Redis|array + time() + +

+
+ + + +
+

Retrieve the server time from the connected Redis instance.

+
+
+ +

Return Value

+ + + + + + +
Redis|array

two element array consisting of a Unix Timestamp and the number of microseconds +elapsed since the second.

+

+<?php
+$redis = new Redis(['host' => 'localhost']);
+
+// Array
+// (
+//     [0] => 1667271026
+//     [1] => 355678
+// )
+print_r($redis->time());
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/time +
+ + +
+
+ +
+
+

+ + Redis|int|false + ttl(string $key) + +

+
+ + + +
+

Get the amount of time a Redis key has before it will expire, in seconds.

+
+
+

Parameters

+ + + + + + + +
string$key

The Key we want the TTL for.

+ + +

Return Value

+ + + + + + +
Redis|int|false

(a) The number of seconds until the key expires, or -1 if the key has +no expiration, and -2 if the key does not exist. In the event of an +error, this command will return false.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->multi()
+      ->setex('expires_in_60s', 60, 'test')
+      ->set('doesnt_expire', 'persistent')
+      ->del('not_a_key')
+      ->exec();
+
+// Returns <= 60
+$redis->ttl('expires_in_60s');
+
+// Returns -1
+$redis->ttl('doesnt_expire');
+
+// Returns -2 (key doesn't exist)
+$redis->ttl('not_a_key');
+
+?>
+ + + + +
+
+ +
+
+

+ + Redis|int|false + type(string $key) + +

+
+ + + +
+

Get the type of a given Redis key.

+
+
+

Parameters

+ + + + + + + +
string$key

The key to check

+ + +

Return Value

+ + + + + + +
Redis|int|false

The Redis type constant or false on failure.

+

The Redis class defines several type constants that correspond with Redis key types.

+
Redis::REDIS_NOT_FOUND
+Redis::REDIS_STRING
+Redis::REDIS_SET
+Redis::REDIS_LIST
+Redis::REDIS_ZSET
+Redis::REDIS_HASH
+Redis::REDIS_STREAM
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/type +
+ + +
+
+ +
+
+ +
+ + + +
+

Delete one or more keys from the Redis database. Unlike this operation, the actual +deletion is asynchronous, meaning it is safe to delete large keys without fear of +Redis blocking for a long period of time.

+
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys

If the first argument passed to this method was a string +you may pass any number of additional key names.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of keys deleted or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+// OPTION 1:  Called with a single array of keys
+$redis->unlink(['key1', 'key2', 'key3']);
+
+// OPTION 2:  Called with a variadic number of arguments
+$redis->unlink('key1', 'key2', 'key3');
+?>
+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/unlink +
+ https://redis.io/commands/del +
+ +Redis::del +
+ + +
+
+ +
+
+

+ + Redis|array|bool + unsubscribe(array $channels) + +

+
+ + + +
+

Unsubscribe from one or more subscribed channels.

+
+
+

Parameters

+ + + + + + + +
array$channels
+ + +

Return Value

+ + + + + + +
Redis|array|bool
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/unsubscribe +
+ +Redis::subscribe +
+ + +
+
+ +
+
+

+ + Redis|bool + unwatch() + +

+
+ + + +
+

Remove any previously WATCH'ed keys in a transaction.

+
+
+ +

Return Value

+ + + + + + +
Redis|bool

on success and false on failure.

+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/unwatch +
+ https://redis.io/commands/unwatch +
+ +Redis::watch +
+ + +
+
+ +
+
+

+ + bool|Redis + watch(array|string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
bool|Redis
+ + + + +
+
+ +
+
+

+ + int|false + wait(int $numreplicas, int $timeout) + +

+
+ + + +
+

Block the client up to the provided timeout until a certain number of replicas have confirmed +recieving them.

+
+
+

Parameters

+ + + + + + + + + + + + +
int$numreplicas

The number of replicas we want to confirm write operaions

int$timeout

How long to wait (zero meaning forever).

+ + +

Return Value

+ + + + + + +
int|false

The number of replicas that have confirmed or false on failure.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/wait +
+ + +
+
+ +
+
+

+ + int|false + xack(string $key, string $group, array $ids) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$group
array$ids
+ + +

Return Value

+ + + + + + +
int|false
+ + + + +
+
+ +
+
+

+ + Redis|string|false + xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) + +

+
+ + + +
+

Append a message to a stream.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key

The stream name.

string$id

The ID for the message we want to add. This can be the special value '' +which means Redis will generate the ID that appends the message to the +end of the stream. It can also be a value in the form - which will +generate an ID that appends to the end ot entries with the same value +(if any exist).

array$values
int$maxlen

If specified Redis will append the new message but trim any number of the +oldest messages in the stream until the length is <= $maxlen.

bool$approx

Used in conjunction with $maxlen, this flag tells Redis to trim the stream +but in a more efficient way, meaning the trimming may not be exactly to +$maxlen values.

bool$nomkstream

If passed as TRUE, the stream must exist for Redis to append the message.

+
</php
+<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('ds9-season-1');
+
+$redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']);
+$redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']);
+$redis->xAdd('ds9-season-1', '1-3', ['title' => 'Emissary Part 2']);
+$redis->xAdd('ds9-season-1', '1-4', ['title' => 'Past Prologue']);
+
+// Array
+// (
+//     [1-1] => Array
+//         (
+//             [title] => Emissary Part 1
+//         )
+//
+//     [1-2] => Array
+//         (
+//             [title] => A Man Alone
+//         )
+//
+// )
+$redis->xRange('ds9-season-1', '1-1', '1-2');
+?>
+?>
+ + +

Return Value

+ + + + + + +
Redis|string|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/xadd +
+ + +
+
+ +
+
+

+ + Redis|bool|array + xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$group
string$consumer
int$min_idle
string$start
int$count
bool$justid
+ + +

Return Value

+ + + + + + +
Redis|bool|array
+ + + + +
+
+ +
+
+

+ + Redis|bool|array + xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$group
string$consumer
int$min_idle
array$ids
array$options
+ + +

Return Value

+ + + + + + +
Redis|bool|array
+ + + + +
+
+ +
+
+

+ + Redis|int|false + xdel(string $key, array $ids) + +

+
+ + + +
+

Remove one or more specific IDs from a stream.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The stream to modify.

array$ids

One or more message IDs to remove.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of messages removed or false on failure.

+
$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('stream');
+
+for ($a = 1; $a <= 3; $a++) {
+    for ($b = 1; $b <= 2; $b++) {
+        $redis->xAdd('stream', "$a-$b", ['id' => "$a-$b"]);
+    }
+}
+
+// Remove some elements
+$redis->xDel('stream', ['1-1', '2-1', '3-1']);
+
+// Array
+// (
+//     [1-2] => Array
+//         (
+//             [id] => 1-2
+//         )
+//
+//     [2-2] => Array
+//         (
+//             [id] => 2-2
+//         )
+//
+//     [3-2] => Array
+//         (
+//             [id] => 3-2
+//         )
+//
+// )
+$redis->xRange('stream', '-', '+');
+?>
+ + + + +
+
+ +
+
+

+ + mixed + xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) + +

+
+ + + +
+

XGROUP

Perform various operation on consumer groups for a particular Redis STREAM. What the command does +is primarily based on which operation is passed.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$operation

The subcommand you intend to execute. Valid options are as follows +'HELP' - Redis will return information about the command +Requires: none +'CREATE' - Create a consumer group. +Requires: Key, group, consumer. +'SETID' - Set the ID of an existing consumer group for the stream. +Requires: Key, group, id. +'CREATECONSUMER' - Create a new consumer group for the stream. You must +also pass key, group, and the consumer name you wish to +create. +Requires: Key, group, consumer. +'DELCONSUMER' - Delete a consumer from group attached to the stream. +Requires: Key, group, consumer. +'DESTROY' - Delete a consumer group from a stream. +Requires: Key, group.

string$key

The STREAM we're operating on.

string$group

The consumer group we want to create/modify/delete.

string$id_or_consumer

The STREAM id (e.g. '$') or consumer group. See the operation section +for information about which to send.

bool$mkstream

This flag may be sent in combination with the 'CREATE' operation, and +cause Redis to also create the STREAM if it doesn't currently exist.

int$entries_read
+ + +

Return Value

+ + + + + + +
mixed

This command return various results depending on the operation performed.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/xgroup/ +
+ + +
+
+ +
+
+

+ + mixed + xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) + +

+
+ + + +
+

Retrieve information about a stream key.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$operation

The specific info operation to perform.

string|null$arg1

The first argument (depends on operation)

string|null$arg2

The second argument

int$count

The COUNT argument to XINFO STREAM

+ + +

Return Value

+ + + + + + +
mixed

This command can return different things depending on the operation being called.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('stream');
+
+$redis->xAdd('stream', "0-1", ['payload' => '0-1']);
+$redis->xAdd('stream', "0-2", ['payload' => '0-2']);
+$redis->xAdd('stream', "0-3", ['payload' => '0-3']);
+
+// Retrieve any consmers for a given key
+$redis->xInfo('CONSUMERS', 'stream');
+
+// Retrieve any groups for a given key
+$redis->xInfo('GROUPS', 'stream');
+
+// Retrieve general stream information along with messages
+$redis->xInfo('STREAM', 'stream');
+?>
+ + + + +
+
+ +
+
+

+ + Redis|int|false + xlen(string $key) + +

+
+ + + +
+

Get the number of messages in a Redis STREAM key.

+
+
+

Parameters

+ + + + + + + +
string$key

The Stream to check.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of messages or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('stream');
+$redis->xadd('stream', '*', ['first' => 'message']);
+$redis->xadd('stream', '*', ['second' => 'message']);
+
+// int(2)
+$redis->xLen('stream');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/xlen +
+ + +
+
+ +
+
+

+ + Redis|array|false + xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) + +

+
+ + + +
+

Interact with stream messages that have been consumed by a consumer group but not yet +acknowledged with XACK.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key

The stream to inspect.

string$group

The user group we want to see pending messages from.

string|null$start

The minimum ID to consider.

string|null$end
int$count

Optional maximum number of messages to return.

string|null$consumer

If provided, limit the returned messages to a specific consumer.

+ + +

Return Value

+ + + + + + +
Redis|array|false

The pending messages belonging to the stream or false on failure.

+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/xpending +
+ https://redis.io/commands/xreadgroup +
+ + +
+
+ +
+
+

+ + Redis|array|bool + xrange(string $key, string $start, string $end, int $count = -1) + +

+
+ + + +
+

Get a range of entries from a STREAM key.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The stream key name to list.

string$start

The minimum ID to return.

string$end

The maximum ID to return.

int$count

An optional maximum number of entries to return.

+ + +

Return Value

+ + + + + + +
Redis|array|bool

The entries in the stream within the requested range or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('stream');
+
+for ($i = 0; $i < 2; $i++) {
+    for ($j = 1; $j <= 2; $j++) {
+        $redis->xAdd('stream', "$i-$j", ['message' => "$i:$j"]);
+    }
+}
+
+//Array
+//(
+//    [0-1] => Array
+//        (
+//            [message] => 0:1
+//        )
+//
+//    [0-2] => Array
+//        (
+//            [message] => 0:2
+//        )
+//
+//)
+$redis->xRange('stream', '0-1', '0-2');
+
+// '-' and '+' are special values which mean 'minimum possible',
+// and 'maximum possible' id, respectively.
+$redis->xRange('stream', '-', '+');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/xrange +
+ + +
+
+ +
+
+

+ + Redis|array|bool + xread(array $streams, int $count = -1, int $block = -1) + +

+
+ + + +
+

Consume one or more unconsumed elements in one or more streams.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$streams

An associative array with stream name keys and minimum id values.

int$count

An optional limit to how many entries are returnd per stream

int$block

An optional maximum number of milliseconds to block the caller if no +data is available on any of the provided streams.

+
$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('s03', 's03');
+
+$redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']);
+$redis->xAdd('s03', '3-2', ['title' => 'The Search, Part II']);
+$redis->xAdd('s03', '3-3', ['title' => 'The House Of Quark']);
+
+$redis->xAdd('s04', '4-1', ['title' => 'The Way of the Warrior']);
+$redis->xAdd('s04', '4-3', ['title' => 'The Visitor']);
+$redis->xAdd('s04', '4-4', ['title' => 'Hippocratic Oath']);
+
+// Array
+// (
+//     [s03] => Array
+//         (
+//             [3-3] => Array
+//                 (
+//                     [title] => The House Of Quark
+//                 )
+//
+//         )
+//
+//     [s04] => Array
+//         (
+//             [4-3] => Array
+//                 (
+//                     [title] => The Visitor
+//                 )
+//
+//             [4-4] => Array
+//                 (
+//                     [title] => Hippocratic Oath
+//                 )
+//
+//         )
+//
+// )
+print_r($redis->xRead(['s03' => '3-2', 's04' => '4-1']));
+ + +

Return Value

+ + + + + + +
Redis|array|bool
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/xread +
+ + +
+
+ +
+
+

+ + Redis|array|bool + xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) + +

+
+ + + +
+

Read one or more messages using a consumer group.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$group

The consumer group to use.

string$consumer

The consumer to use.

array$streams

An array of stream names and message IDs

int$count

Optional maximum number of messages to return

int$block

How long to block if there are no messages available.

+ + +

Return Value

+ + + + + + +
Redis|array|bool

Zero or more unread messages or false on failure.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('episodes');
+
+// Create a consumer group (and stream)
+$redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true);
+
+// Add a couple of messages to the stream
+$redis->xAdd('episodes', '1-1', ['title' => 'Emissary: Part 1']);
+$redis->xAdd('episodes', '1-2', ['title' => 'A Man Alone']);
+
+// Now read some messages with our consumer group
+$messages = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
+
+// After having read the two messages, add another
+$redis->xAdd('episodes', '1-3', ['title' => 'Emissary: Part 2']);
+
+// Acknowledge the first two read messages
+foreach ($messages as $stream => $stream_messages) {
+    $ids = array_keys($stream_messages);
+    $redis->xAck('stream', 'ds9', $ids);
+}
+
+// We can now pick up where we left off, and will only get the final message
+$msgs = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
+
+// array(1) {
+//   ["episodes"]=>
+//   array(1) {
+//     ["1-3"]=>
+//     array(1) {
+//       ["title"]=>
+//       string(16) "Emissary: Part 2"
+//     }
+//   }
+// }
+var_dump($msgs);
+?>
+ + + + +
+
+ +
+
+

+ + Redis|array|bool + xrevrange(string $key, string $end, string $start, int $count = -1) + +

+
+ + + +
+

Get a range of entries from a STREAM ke in reverse cronological order.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The stream key to query.

string$end

The maximum message ID to include.

string$start

The minimum message ID to include.

int$count

An optional maximum number of messages to include.

+ + +

Return Value

+ + + + + + +
Redis|array|bool

The entries within the requested range, from newest to oldest.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('stream');
+
+for ($i = 0; $i < 2; $i++) {
+    for ($j = 1; $j <= 2; $j++) {
+        $redis->xAdd('stream', "$i-$j", ['message' => "$i:$j"]);
+    }
+}
+
+// Array
+// (
+//     [0-2] => Array
+//         (
+//             [message] => 0:2
+//         )
+//
+//     [0-1] => Array
+//         (
+//             [message] => 0:1
+//         )
+//
+// )
+$redis->xRevRange('stream', '0-2', '0-1');
+
+// '-' and '+' are special values which mean 'minimum possible',
+// and 'maximum possible' id, respectively.
+$redis->xRevRange('stream', '+', '-');
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/xrevrange +
+ https://redis.io/commands/xrange +
+ + +
+
+ +
+
+

+ + Redis|int|false + xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) + +

+
+ + + +
+

Truncate a STREAM key in various ways.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key

The STREAM key to trim.

string$threshold

This can either be a maximum length, or a minimum id. +MAXLEN - An integer describing the maximum desired length of the stream after the command. +MINID - An ID that will become the new minimum ID in the stream, as Redis will trim all +messages older than this ID.

bool$approx

Whether redis is allowed to do an approximate trimming of the stream. This is +more efficient for Redis given how streams are stored internally.

bool$minid

When set to true, users should pass a minimum ID to the $threshold argument.

int$limit

An optional upper bound on how many entries to trim during the command.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('stream');
+$redis->xAdd('stream', '1-1', ['one' => 'one']);
+$redis->xAdd('stream', '1-2', ['one' => 'two']);
+$redis->xAdd('stream', '2-1', ['two' => 'one']);
+$redis->xAdd('stream', '2-2', ['two' => 'two']);
+
+// Trim to three elemn
+$redis->xTrim('stream', 3);
+
+// Array
+// (
+//     [1-2] => Array
+//         (
+//             [one] => two
+//         )
+//
+//     [2-1] => Array
+//         (
+//             [two] => one
+//         )
+//
+//     [2-2] => Array
+//         (
+//             [two] => two
+//         )
+//
+// )
+$redis->xRange('stream', '-', '+');
+
+// Now let's trim everything older than '2-1'
+$redis->xTrim('stream', '2-1', false, true);
+
+// Array
+// (
+//     [2-1] => Array
+//         (
+//             [two] => one
+//         )
+//
+//     [2-2] => Array
+//         (
+//             [two] => two
+//         )
+//
+// )
+print_r($redis->xRange('stream', '-', '+'));
+?>
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/xtrim +
+ + +
+
+ +
+
+

+ + Redis|int|false + zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) + +

+
+ + + +
+

Add one or more elements and scores to a Redis sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The sorted set in question.

array|float$score_or_options

Either the score for the first element, or an array +containing one or more options for the operation.

mixed...$more_scores_and_mems

A variadic number of additional scores and members.

+

Following is information about the options that may be passed as the scond argument:

+
$options = [
+    'NX',       # Only update elements that already exist
+    'NX',       # Only add new elements but don't update existing ones.
+
+    'LT'        # Only update existing elements if the new score is less than the existing one.
+    'GT'        # Only update existing elements if the new score is greater than the existing one.
+
+    'CH'        # Instead of returning the number of elements added, Redis will return the number
+                # Of elements that were changed in the operation.
+
+    'INCR'      # Instead of setting each element to the provide score, increment the elemnt by the
+                # provided score, much like ZINCRBY.  When this option is passed, you may only
+                # send a single score and member.
+];
+
+Note:  'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis will send whichever one is last in
+       the options array.
+
+

<?php +$redis = new Redis(['host' => 'localhost']);

+

$redis->del('zs');

+

// Add three new elements to our zset +$redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third');

+

// Array +// ( +// [first] => 1 +// [second] => 2 +// [third] => 3 +// ) +$redis->zRange('zs', 0, -1, true);

+

// Update only existing elements. Note that 'new-element' isn't added +$redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element');

+

// Array +// ( +// [first] => 1 +// [third] => 3 +// [second] => 8 +// ) +print_r($redis->zRange('zs', 0, -1, true)); +?>

+
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zadd +
+ + +
+
+ +
+
+

+ + Redis|int|false + zCard(string $key) + +

+
+ + + +
+

Return the number of elements in a sorted set.

+
+
+

Parameters

+ + + + + + + +
string$key

The sorted set to retreive cardinality from.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of elements in the set or false on failure

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+$redis->zAdd('zs', 0, 'a', 1, 'b', 2, 'c');
+
+// count(['a', 'b', 'c']) == 3
+$redis->zCard('zs');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zcard +
+ + +
+
+ +
+
+

+ + Redis|int|false + zCount(string $key, string $start, string $end) + +

+
+ + + +
+

Count the number of members in a sorted set with scores inside a provided range.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The sorted set to check.

string$start
string$end
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zcount +
+ + +
+
+ +
+
+

+ + Redis|float|false + zIncrBy(string $key, float $value, mixed $member) + +

+
+ + + +
+

Create or increment the score of a member in a Redis sorted set

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The sorted set in question.

float$value

How much to increment the score.

mixed$member
+ + +

Return Value

+ + + + + + +
Redis|float|false

The new score of the member or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+$redis->zAdd('zs', 0, 'apples', 2, 'bananas');
+
+// 2 + 5.0 == 7
+print_r($redis->zIncrBy('zs', 5.0, 'bananas'));
+
+// new element so 0 + 2.0 == 2
+print_r($redis->zIncrBy('zs', 2.0, 'eggplants'));
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zincrby +
+ + +
+
+ +
+
+

+ + Redis|int|false + zLexCount(string $key, string $min, string $max) + +

+
+ + + +
+

Count the number of elements in a sorted set whos members fall within the provided +lexographical range.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The sorted set to check.

string$min

The minimum matching lexographical string

string$max

The maximum matching lexographical string

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of members that fall within the range or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('captains');
+$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
+
+count(['Archer', 'Janeway', 'Kirk', 'Picard']) == 4
+$redis->zLexCount('captains', '[A', '[S');
+
+count(['Kirk', 'Picard']) == 2
+$redis->zRangeByLex('captains', '[A', '[S', 2, 2);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zlexcount +
+ + +
+
+ +
+
+

+ + Redis|array|false + zMscore(string $key, mixed $member, mixed ...$other_members) + +

+
+ + + +
+

Retrieve the score of one or more members in a sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The sorted set

mixed$member

The first member to return the score from

mixed...$other_members

One or more additional members to return the scores of.

+ + +

Return Value

+ + + + + + +
Redis|array|false

An array of the scores of the requested elements.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+
+$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+
+// array(2) {
+//   [0]=>
+//   float(0)
+//   [1]=>
+//   float(2)
+// }
+$redis->zMScore('zs', 'zero', 'two');
+
+// array(2) {
+//   [0]=>
+//   float(1)
+//   [1]=>
+//   bool(false)
+// }
+$redis->zMScore('zs', 'one', 'not-a-member');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zmscore +
+ + +
+
+ +
+
+

+ + Redis|array|false + zPopMax(string $key, int $count = null) + +

+
+ + + +
+

Pop one or more of the highest scoring elements from a sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The sorted set to pop elements from.

int$count

An optional count of elements to pop.

+ + +

Return Value

+ + + + + + +
Redis|array|false

All of the popped elements with scores or false on fialure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+
+// Array
+// (
+//     [three] => 3
+// )
+print_r($redis->zPopMax('zs'));
+
+// Array
+// (
+//     [two] => 2
+//     [one] => 1
+// )
+print_r($redis->zPopMax('zs', 2));
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zpopmax +
+ + +
+
+ +
+
+

+ + Redis|array|false + zPopMin(string $key, int $count = null) + +

+
+ + + +
+

Pop one or more of the lowest scoring elements from a sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The sorted set to pop elements from.

int$count

An optional count of elements to pop.

+ + +

Return Value

+ + + + + + +
Redis|array|false

The popped elements with their scores or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+
+// Array
+// (
+//     [zero] => 0
+// )
+$redis->zPopMin('zs');
+
+// Array
+// (
+//     [one] => 1
+//     [two] => 2
+// )
+$redis->zPopMin('zs', 2);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zpopmin +
+ + +
+
+ +
+
+

+ + Redis|array|false + zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) + +

+
+ + + +
+

Retrieve a range of elements of a sorted set between a start and end point.

How the command works in particular is greatly affected by the options that +are passed in.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The sorted set in question.

mixed$start

The starting index we want to return.

mixed$end

The final index we want to return.

array|bool|null$options

This value may either be an array of options to pass to +the command, or for historical purposes a boolean which +controls just the 'WITHSCORES' option.

+ + +

Return Value

+ + + + + + +
Redis|array|false

An array with matching elements or false on failure.

+

Detailed description of options array:

+
<?php
+$options = [
+    'WITHSCORES' => true,     // Return both scores and members.
+    'LIMIT'      => [10, 10], // Start at offset 10 and return 10 elements.
+    'REV'                     // Return the elements in reverse order
+    'BYSCORE',                // Treat `start` and `end` as scores instead
+    'BYLEX'                   // Treat `start` and `end` as lexicographical values.
+];
+?>
+

Note: 'BYLEX' and 'BYSCORE' are mutually exclusive.

+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zrange/ +
+ + +
+
+ +
+
+

+ + Redis|array|false + zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) + +

+
+ + + +
+

Retrieve a range of elements from a sorted set by legographical range.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key

The sorted set to retreive elements from

string$min

The minimum legographical value to return

string$max

The maximum legographical value to return

int$offset

An optional offset within the matching values to return

int$count

An optional count to limit the replies to (used in conjunction with offset)

+ + +

Return Value

+ + + + + + +
Redis|array|false

An array of matching elements or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('captains');
+$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
+
+// Array
+// (
+//     [0] => Archer
+//     [1] => Janeway
+//     [2] => Kirk
+//     [3] => Picard
+// )
+$redis->zRangeByLex('captains', '[A', '[S');
+
+// Array
+// (
+//     [0] => Kirk
+//     [1] => Picard
+// )
+$redis->zRangeByLex('captains', '[A', '[S', 2, 2);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zrangebylex +
+ + +
+
+ +
+
+

+ + Redis|array|false + zRangeByScore(string $key, string $start, string $end, array $options = []) + +

+
+ + + +
+

Retrieve a range of members from a sorted set by their score.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The sorted set to query.

string$start

The minimum score of elements that Redis should return.

string$end

The maximum score of elements that Redis should return.

array$options

Options that change how Redis will execute the command.

+

OPTION TYPE MEANING +'WITHSCORES' bool Whether to also return scores. +'LIMIT' [offset, count] Limit the reply to a subset of elements.

+ + +

Return Value

+ + + + + + +
Redis|array|false

The number of matching elements or false on failure.

+
</php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+
+for ($i = 0; $i < 50; $i++) {
+    $redis->zAdd('zs', $i, "mem:$i");
+}
+
+// Array
+// (
+//     [0] => mem:0
+//     [1] => mem:1
+//     [2] => mem:2
+//     [3] => mem:3
+//     [4] => mem:4
+// )
+$redis->zRangeByScore('zs', 0, 4);
+
+// Array
+// (
+//     [mem:20] => 20
+//     [mem:21] => 21
+//     [mem:22] => 22
+//     [mem:23] => 23
+//     [mem:24] => 24
+//     [mem:25] => 25
+//     [mem:26] => 26
+//     [mem:27] => 27
+//     [mem:28] => 28
+//     [mem:29] => 29
+//     [mem:30] => 30
+// )
+$redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]);
+
+// Array
+// (
+//     [mem:25] => 25
+//     [mem:26] => 26
+//     [mem:27] => 27
+//     [mem:28] => 28
+//     [mem:29] => 29
+// )
+$redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zrangebyscore +
+ + +
+
+ +
+
+

+ + Redis|int|false + zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) + +

+
+ + + +
+

This command is similar to ZRANGE except that instead of returning the values directly +it will store them in a destination key provided by the user

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$dstkey

The key to store the resulting element(s)

string$srckey

The source key with element(s) to retrieve

string$start

The starting index to store

string$end

The ending index to store

array|bool|null$options

Our options array that controls how the command will function.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of elements stored in $dstkey or false on failure.

+

See Redis::zRange for a full description of the possible options.

+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/zrange/ +
+ Redis::zRange +
+ + +
+
+ +
+
+

+ + Redis|string|array + zRandMember(string $key, array $options = null) + +

+
+ + + +
+

Retrieve one or more random members from a Redis sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The sorted set to pull random members from.

array$options

One or more options that determine exactly how the command operates.

+
                   OPTION       TYPE     MEANING
+                   'COUNT'      int      The number of random members to return.
+                   'WITHSCORES' bool     Whether to return scores and members instead of
+                                         just members.
+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->multi()->del('zs')->zadd('zs', 1, 'one', 2, 'two', 3, 'three')->exec();
+
+// Return two random members from our set, with scores
+$redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]);
+
+?>
+ + +

Return Value

+ + + + + + +
Redis|string|array
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zrandmember +
+ + +
+
+ +
+
+

+ + Redis|int|false + zRank(string $key, mixed $member) + +

+
+ + + +
+

Get the rank of a member of a sorted set, by score.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The sorted set to check.

mixed$member
+ + +

Return Value

+ + + + + + +
Redis|int|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zrank +
+ + +
+
+ +
+
+

+ + Redis|int|false + zRem(mixed $key, mixed $member, mixed ...$other_members) + +

+
+ + + +
+

Remove one or more members from a Redis sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
mixed$key

The sorted set in question.

mixed$member

The first member to remove.

mixed...$other_members

One or more members to remove passed in a variadic fashion.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of members that were actually removed or false on failure.

+

+<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+
+for ($i = 0; $i < 10; $i++) {
+    $redis->zAdd('zs', $i, "mem:$i");
+}
+
+// Remove a few elements
+$redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9');
+
+// Array
+// (
+//     [0] => mem:3
+//     [1] => mem:4
+//     [2] => mem:5
+// )
+$redis->zRange('zs', 0, -1);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zrem +
+ + +
+
+ +
+
+

+ + Redis|int|false + zRemRangeByLex(string $key, string $min, string $max) + +

+
+ + + +
+

Remove zero or more elements from a Redis sorted set by legographical range.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The sorted set to remove elements from.

string$min

The start of the lexographical range to remove.

string$max

The end of the lexographical range to remove

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of elements removed from the set or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->pipeline()->del('zs')
+               ->zAdd('zs', 1, 'apple', 2, 'banana', 3, 'carrot', 4, 'date', 5, 'eggplant')
+               ->exec();
+
+// Remove a* (inclusive) .. b* (exclusive), meaning 'apple' will be removed, but 'banana' not
+$redis->zRemRangeByLex('zs', '[a', '(b');
+
+// Array
+// (
+//     [0] => banana
+//     [1] => carrot
+//     [2] => date
+//     [3] => eggplant
+// )
+print_r($redis->zRange('zs', 0, -1));
+
+// Remove the elements between 'banana' and 'eggplant'
+$redis->zRemRangeByLex('zs', '(banana', '(eggplant');
+
+// Array
+// (
+//     [0] => banana
+//     [1] => eggplant
+// )
+print_r($redis->zRange('zs', 0, -1));
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/zremrangebylex +
+ \Redis::zrangebylex() +
+ + +
+
+ +
+
+

+ + Redis|int|false + zRemRangeByRank(string $key, int $start, int $end) + +

+
+ + + +
+

Remove one or more members of a sorted set by their rank.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The sorted set where we wnat to remove members.

int$start

The rank when we want to start removing members

int$end

The rank we want to stop removing membersk.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of members removed from the set or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+$redis->zAdd('zs', 0, 'zeroth', 1, 'first', 2, 'second', 3, 'third', 4, 'fourth');
+
+// Remove ranks 0..3
+$redis->zRemRangeByRank('zs', 0, 3);
+
+// Array
+// (
+//     [0] => fourth
+// )
+$redis->zRange('zs', 0, -1);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zremrangebyrank +
+ + +
+
+ +
+
+

+ + Redis|int|false + zRemRangeByScore(string $key, string $start, string $end) + +

+
+ + + +
+

Remove one or more members of a sorted set by their score.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The sorted set where we wnat to remove members.

string$start

The lowest score to remove.

string$end

The highest score to remove.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of members removed from the set or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs');
+$redis->zAdd('zs', 3, 'three', 5, 'five', 7, 'seven', 7, 'seven-again', 13, 'thirteen', 22, 'twenty-two');
+
+// Removes every member with scores >= 7 and scores <= 13.
+$redis->zRemRangeByScore('zs', 7, 13);
+
+// Array
+// (
+//     [0] => three
+//     [1] => five
+//     [2] => twenty-two
+// )
+$redis->zRange('zs', 0, -1);
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zremrangebyrank +
+ + +
+
+ +
+
+

+ + Redis|array|false + zRevRange(string $key, int $start, int $end, mixed $scores = null) + +

+
+ + + +
+

List the members of a Redis sorted set in reverse order

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The sorted set in question.

int$start

The index to start listing elements

int$end

The index to stop listing elements.

mixed$scores
+ + +

Return Value

+ + + + + + +
Redis|array|false

The members (and possibly scores) of the matching elements or false +on failure.

+

$redis = new Redis(['host' => 'localhost']);

+

$redis->del('zs'); +$redis->zAdd('zs', 1, 'one', 2, 'two', 5, 'five', 10, 'ten');

+

// Array +// ( +// [0] => ten +// [1] => five +// [2] => two +// [3] => one +// ) +print_r($redis->zRevRange('zs', 0, -1));

+

// Array +// ( +// [0] => two +// [1] => one +// ) +print_r($redis->zRevRange('zs', 2, 3));

+

// Additionally, you may pass true or ['withscores' => true] to tell redis to return scores +// as well as members. +$redis->zRevRange('zs', 0, -1, true); +$redis->zRevRange('zs', 0, -1, ['withscores' => true]); +?>

+
+ + + + +
+
+ +
+
+

+ + Redis|array|false + zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) + +

+
+ + + +
+

List members of a Redis sorted set within a legographical range, in reverse order.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key

The sorted set to list

string$max
string$min

The maximum legographical element to include in the result.

int$offset

An option offset within the matching elements to start at.

int$count

An optional count to limit the replies to.

+ + +

Return Value

+ + + + + + +
Redis|array|false

The matching members or false on failure.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('captains');
+$redis->zAdd('captains', 0, 'Janeway', 0, 'Picard', 0, 'Kirk', 0, 'Archer');
+
+// Array
+// (
+//     [0] => Picard
+//     [1] => Kirk
+//     [2] => Janeway
+// )
+$redis->zRevRangeByLex('captains', '[Q', '[J');
+
+// Array
+// (
+//     [0] => Kirk
+//     [1] => Janeway
+// )
+$redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2);
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/zrevrangebylex +
+ \Redis::zrangebylex() +
+ + +
+
+ +
+
+

+ + Redis|array|false + zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) + +

+
+ + + +
+

List elements from a Redis sorted set by score, highest to lowest

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The sorted set to query.

string$max

The highest score to include in the results.

string$min

The lowest score to include in the results.

array|bool$options

An options array that modifies how the command executes.

+
$options = [
+    'WITHSCORES' => true|false # Whether or not to return scores
+    'LIMIT' => [offset, count] # Return a subset of the matching members
+];
+

NOTE: For legacy reason, you may also simply pass true for the +options argument, to mean WITHSCORES.

+ + +

Return Value

+ + + + + + +
Redis|array|false

The matching members in reverse order of score or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('oldest-people');
+
+$redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka',
+                              119.2658, 'Sarah Knauss',   118.7205, 'Lucile Randon',
+                              117.7123, 'Nabi Tajima',    117.6301, 'Marie-Louise Meilleur',
+                              117.5178, 'Violet Brown',   117.3753, 'Emma Morano',
+                              117.2219, 'Chiyo Miyako',   117.0740, 'Misao Okawa');
+
+// Array
+// (
+//     [0] => Kane Tanaka
+//     [1] => Sarah Knauss
+// )
+$redis->zRevRangeByScore('oldest-people', 122, 119);
+
+//Array
+//(
+//    [0] => Jeanne Calment
+//    [1] => Kane Tanaka
+//    [2] => Sarah Knauss
+//    [3] => Lucile Randon
+//)
+$redis->zRevRangeByScore('oldest-people', 'inf', 118);
+
+// Array
+// (
+//     [0] => Emma Morano
+// )
+$redis->zRevRangeByScore('oldest-people', '117.5', '-inf', ['LIMIT' => [0, 1]]);
+?>
+ + + + +
+
+ +
+
+

+ + Redis|int|false + zRevRank(string $key, mixed $member) + +

+
+ + + +
+

Retrieve a member of a sorted set by reverse rank.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The sorted set to query.

mixed$member

The member to look up.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The reverse rank (the rank if counted high to low) of the member or +false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('ds9-characters');
+
+$redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo');
+
+// Highest score, reverse rank 0
+$redis->zrevrank('ds9-characters', 'Sisko');
+
+// Second highest score, reverse rank 1
+$redis->zrevrank('ds9-characters', 'Garak');
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zrevrank +
+ + +
+
+ +
+
+

+ + Redis|float|false + zScore(string $key, mixed $member) + +

+
+ + + +
+

Get the score of a member of a sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The sorted set to query.

mixed$member

The member we wish to query.

+ + +

Return Value

+ + + + + + +
Redis|float|false

score of the requested element or false if it is not found.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('telescopes');
+
+$redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET');
+
+foreach ($redis->zRange('telescopes', 0, -1) as $name) {
+    // Get the score for this member
+    $aperature = $redis->zScore('telescopes', $name);
+
+    echo "The '$name' telescope has an effective aperature of: $aperature meters\n";
+}
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zscore +
+ + +
+
+ +
+
+

+ + Redis|array|false + zdiff(array $keys, array $options = null) + +

+
+ + + +
+

Given one or more sorted set key names, return every element that is in the first +set but not any of the others.

+
+
+

Parameters

+ + + + + + + + + + + + +
array$keys

One ore more sorted sets.

array$options

An array which can contain ['WITHSCORES' => true] if you want Redis to +return members and scores.

+ + +

Return Value

+ + + + + + +
Redis|array|false

An array of members or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('primes', 'evens', 'mod3');
+
+$redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five');
+$redis->zAdd('evens', 2, 'two', 4, 'four');
+$redis->zAdd('mod3', 3, 'three', 6, 'six');
+
+// Array
+// (
+//     [0] => one
+//     [1] => five
+// )
+print_r($redis->zDiff(['primes', 'evens', 'mod3']));
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zdiff +
+ + +
+
+ +
+
+

+ + Redis|int|false + zdiffstore(string $dst, array $keys) + +

+
+ + + +
+

Store the difference of one or more sorted sets in a destination sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$dst
array$keys

One or more source key names

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of elements stored in the destination set or false on +failure.

+

NOTE: See Redis::zdiff() for a more detailed description of how the diff operation works.

+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/zdiff +
+ +Redis::zdiff +
+ + +
+
+ +
+
+

+ + Redis|array|false + zinter(array $keys, array|null $weights = null, array|null $options = null) + +

+
+ + + +
+

Compute the intersection of one or more sorted sets and return the members

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys

One ore more sorted sets.

array|null$weights

An optional array of weights to be applied to each set when performing +the intersection.

array|null$options

Options for how Redis should combine duplicate elements when performing the +intersection. See Redis::zunion() for details.

+ + +

Return Value

+ + + + + + +
Redis|array|false

All of the members that exist in every set.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('tng', 'ds9');
+
+$redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard');
+$redis->zAdd('DS9', 2.5, 'Worf', 3.0, 'Kira', 4.0, 'Sisko');
+
+// Array
+// (
+//     [0] => Worf
+// )
+$redis->zInter(['TNG', 'DS9']);
+
+// Array
+// (
+//     [Worf] => 4.5
+// )
+$redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true]);
+
+// Array
+// (
+//     [Worf] => 2.5
+// )
+$redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true, 'aggregate' => 'max']);
+
+?>
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/zinter +
+ + +
+
+ +
+
+

+ + Redis|int|false + zintercard(array $keys, int $limit = -1) + +

+
+ + + +
+

Similar to ZINTER but instead of returning the intersected values, this command returns the +cardinality of the intersected set.

+
+
+

Parameters

+ + + + + + + + + + + + +
array$keys

One ore more sorted set key names.

int$limit

An optional upper bound on the returned cardinality. If set to a value +greater than zero, Redis will stop processing the intersection once the +resulting cardinality reaches this limit.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The cardinality of the intersection or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs1', 'zs2');
+
+$redis->zAdd('zs1', 1, 'one', 2, 'two', 3, 'three', 4, 'four');
+$redis->zAdd('zs2', 2, 'two', 4, 'four');
+
+// count(['two', 'four']) == 2
+$redis->zInterCard(['zs1', 'zs2']);
+?>
+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/zintercard +
+ https://redis.io/commands/zinter +
+ +Redis::zinter +
+ + +
+
+ +
+
+

+ + Redis|int|false + zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) + +

+
+ + + +
+

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$dst

The destination sorted set to store the intersected values.

array$keys

One ore more sorted set key names.

array|null$weights

An optional array of floats to weight each passed input set.

string|null$aggregate

An optional aggregation method to use.

+

'SUM' - Store sum of all intersected members (this is the default). +'MIN' - Store minimum value for each intersected member. +'MAX' - Store maximum value for each intersected member.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The total number of members writtern to the destination set or false on failure.

+

+<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs', 'zs2', 'zs3');
+$redis->zAdd('zs1', 3, 'apples', 2, 'pears');
+$redis->zAdd('zs2', 4, 'pears', 3, 'bananas');
+$redis->zAdd('zs3', 2, 'figs', 3, 'pears');
+
+// Returns 1 (only 'pears' is in every set)
+$redis->zInterStore('fruit-sum', ['zs1', 'zs2', 'zs3']);
+
+// Array
+// (
+//     [pears] => 9
+// )
+$redis->zRange('fruit-sum', 0, -1, true);
+
+$redis->zInterStore('fruit-max', ['zs1', 'zs2', 'zs3'], NULL, 'MAX');
+
+// Array
+// (
+//     [pears] => 4
+// )
+print_r($redis->zRange('fruit-max', 0, -1, true));
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/zinterstore +
+ https://redis.io/commands/zinter +
+ + +
+
+ +
+
+

+ + Redis|array|false + zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

Scan the members of a sorted set incrementally, using a cursor

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key

The sorted set to scan.

int|null$iterator

A reference to an iterator that should be initialized to NULL initially, that +will be updated after each subsequent call to ZSCAN. Once the iterator +has returned to zero the scan is complete

string|null$pattern

An optional glob-style pattern that limits which members are returned during +the scanning process.

int$count

A hint for Redis that tells it how many elements it should test before returning +from the call. The higher the more work Redis may do in any one given call to +ZSCAN potentially blocking for longer periods of time.

+ + +

Return Value

+ + + + + + +
Redis|array|false

An array of elements or false on failure.

+

NOTE: See Redis::scan() for detailed example code on how to call SCAN like commands.

+ + + +

See also

+ + + + + + + + + + + + + + +
+ https://redis.io/commands/zscan +
+ https://redis.io/commands/scan +
+ +Redis::scan +
+ + +
+
+ +
+
+

+ + Redis|array|false + zunion(array $keys, array|null $weights = null, array|null $options = null) + +

+
+ + + +
+

Retrieve the union of one or more sorted sets

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys

One ore more sorted set key names

array|null$weights

An optional array with floating point weights used when performing the union. +Note that if this argument is passed, it must contain the same number of +elements as the $keys array.

array|null$options

An array that modifies how this command functions.

+
$options = [
+    // By default when members exist in more than one set Redis will SUM
+    // total score for each match.  Instead, it can return the AVG, MIN,
+    // or MAX value based on this option.
+    'AGGREGATE' => 'sum' | 'min' | 'max'
+
+    // Whether Redis should also return each members aggregated score.
+    'WITHSCORES' => true | false
+]
+ + +

Return Value

+ + + + + + +
Redis|array|false

The union of each sorted set or false on failure

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('store1', 'store2', 'store3');
+$redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas');
+$redis->zAdd('store2', 3, 'apples', 5, 'coconuts', 2, 'bananas');
+$redis->zAdd('store3', 2, 'bananas', 6, 'apples', 4, 'figs');
+
+// Array
+// (
+//     [pears] => 3
+//     [figs] => 4
+//     [coconuts] => 5
+//     [apples] => 10
+//     [bananas] => 10
+// )
+$redis->zUnion(['store1', 'store2', 'store3'], NULL, ['withscores' => true]);
+
+// Array
+// (
+//     [figs] => 2
+//     [apples] => 5
+//     [pears] => 6
+//     [bananas] => 13
+// )
+$redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true]);
+
+// Array
+// (
+//     [bananas] => 1
+//     [apples] => 2
+//     [figs] => 2
+//     [pears] => 6
+// )
+$redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true, 'aggregate' => 'MIN']);
+?>
+ + + + +
+
+ +
+
+

+ + Redis|int|false + zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) + +

+
+ + + +
+

Perform a union on one or more Redis sets and store the result in a destination sorted set.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$dst

The destination set to store the union.

array$keys

One or more input keys on which to perform our union.

array|null$weights

An optional weights array used to weight each input set.

string|null$aggregate

An optional modifier in how Redis will combine duplicate members. +Valid: 'MIN', 'MAX', 'SUM'.

+ + +

Return Value

+ + + + + + +
Redis|int|false

The number of members stored in the destination set or false on failure.

+
<?php
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zs1', 'zs2', 'zs3');
+
+$redis->zAdd('zs1', 1, 'one', 3, 'three');
+$redis->zAdd('zs1', 2, 'two', 4, 'four');
+$redis->zadd('zs3', 1, 'one', 7, 'five');
+
+// count(['one','two','three','four','five']) == 5
+$redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']);
+
+// Array
+// (
+//     [0] => one
+//     [1] => two
+//     [2] => three
+//     [3] => four
+//     [4] => five
+// )
+$redis->zRange('dst', 0, -1);
+?>
+ + + +

See also

+ + + + + + + + + + +
+ https://redis.io/commands/zunionstore +
+ +Redis::zunion +
+ + +
+
+ +
+
+ + +
+
+ + + diff --git a/docs/RedisArray.html b/docs/RedisArray.html new file mode 100644 index 0000000000..43becf9e81 --- /dev/null +++ b/docs/RedisArray.html @@ -0,0 +1,1739 @@ + + + + + + RedisArray | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + + +

class + RedisArray (View source) +

+ + + + + + + + + +

Methods

+ +
+
+
+ mixed +
+
+ __call(string $function_name, array $arguments) + +

No description

+
+
+
+
+
+ +
+
+ __construct(string|array $name_or_hosts, array $options = NULL) + +

No description

+
+
+
+
+
+ bool|array +
+
+ _continuum() + +

No description

+
+
+
+
+
+ bool|callable +
+
+ _distributor() + +

No description

+
+
+
+
+
+ bool|callable +
+
+ _function() + +

No description

+
+
+
+
+
+ bool|array +
+
+ _hosts() + +

No description

+
+
+
+
+
+ bool|null|Redis +
+
+ _instance(string $host) + +

No description

+
+
+
+
+
+ bool|null +
+
+ _rehash(callable $fn = NULL) + +

No description

+
+
+
+
+
+ bool|string|null +
+
+ _target(string $key) + +

No description

+
+
+
+
+
+ array +
+
+ bgsave() + +

No description

+
+
+
+
+
+ bool|int +
+
+ del(string|array $key, string ...$otherkeys) + +

No description

+
+
+
+
+
+ bool|null +
+
+ discard() + +

No description

+
+
+
+
+
+ bool|null +
+
+ exec() + +

No description

+
+
+
+
+
+ bool|array +
+
+ flushall() + +

No description

+
+
+
+
+
+ bool|array +
+
+ flushdb() + +

No description

+
+
+
+
+
+ bool|array +
+
+ getOption(int $opt) + +

No description

+
+
+
+
+
+ bool|array +
+
+ hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+
+ bool|array +
+
+ info() + +

No description

+
+
+
+
+
+ bool|array +
+
+ keys(string $pattern) + +

No description

+
+
+
+
+
+ bool|array +
+
+ mget(array $keys) + +

No description

+
+
+
+
+
+ bool +
+
+ mset(array $pairs) + +

No description

+
+
+
+
+
+ bool|RedisArray +
+
+ multi(string $host, int $mode = NULL) + +

No description

+
+
+
+
+
+ bool|array +
+
+ ping() + +

No description

+
+
+
+
+
+ bool|array +
+
+ save() + +

No description

+
+
+
+
+
+ bool|array +
+
+ scan(int|null $iterator, string $node, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+
+ bool|array +
+
+ select(int $index) + +

No description

+
+
+
+
+
+ bool|array +
+
+ setOption(int $opt, string $value) + +

No description

+
+
+
+
+
+ bool|array +
+
+ sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+
+ bool|int +
+
+ unlink(string|array $key, string ...$otherkeys) + +

No description

+
+
+
+
+
+ bool|null +
+
+ unwatch() + +

No description

+
+
+
+
+
+ bool|array +
+
+ zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+ + +

Details

+ +
+
+

+ + mixed + __call(string $function_name, array $arguments) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$function_name
array$arguments
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + + __construct(string|array $name_or_hosts, array $options = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$name_or_hosts
array$options
+ + + + + +
+
+ +
+
+

+ + bool|array + _continuum() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|callable + _distributor() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|callable
+ + + + +
+
+ +
+
+

+ + bool|callable + _function() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|callable
+ + + + +
+
+ +
+
+

+ + bool|array + _hosts() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|null|Redis + _instance(string $host) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$host
+ + +

Return Value

+ + + + + + +
bool|null|Redis
+ + + + +
+
+ +
+
+

+ + bool|null + _rehash(callable $fn = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
callable$fn
+ + +

Return Value

+ + + + + + +
bool|null
+ + + + +
+
+ +
+
+

+ + bool|string|null + _target(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
bool|string|null
+ + + + +
+
+ +
+
+

+ + array + bgsave() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
array
+ + + + +
+
+ +
+
+

+ + bool|int + del(string|array $key, string ...$otherkeys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key
string...$otherkeys
+ + +

Return Value

+ + + + + + +
bool|int
+ + + + +
+
+ +
+
+

+ + bool|null + discard() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|null
+ + + + +
+
+ +
+
+

+ + bool|null + exec() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|null
+ + + + +
+
+ +
+
+

+ + bool|array + flushall() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + flushdb() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + getOption(int $opt) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
int$opt
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int|null$iterator
string|null$pattern
int$count
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + info() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + keys(string $pattern) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$pattern
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + mget(array $keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$keys
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool + mset(array $pairs) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$pairs
+ + +

Return Value

+ + + + + + +
bool
+ + + + +
+
+ +
+
+

+ + bool|RedisArray + multi(string $host, int $mode = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$host
int$mode
+ + +

Return Value

+ + + + + + +
bool|RedisArray
+ + + + +
+
+ +
+
+

+ + bool|array + ping() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + save() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + scan(int|null $iterator, string $node, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
int|null$iterator
string$node
string|null$pattern
int$count
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + select(int $index) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
int$index
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + setOption(int $opt, string $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
int$opt
string$value
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+

+ + bool|array + sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int|null$iterator
string|null$pattern
int$count
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+ +
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key
string...$otherkeys
+ + +

Return Value

+ + + + + + +
bool|int
+ + + + +
+
+ +
+
+

+ + bool|null + unwatch() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|null
+ + + + +
+
+ +
+
+

+ + bool|array + zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int|null$iterator
string|null$pattern
int$count
+ + +

Return Value

+ + + + + + +
bool|array
+ + + + +
+
+ +
+
+ + +
+
+ + + diff --git a/docs/RedisCluster.html b/docs/RedisCluster.html new file mode 100644 index 0000000000..7894808d9e --- /dev/null +++ b/docs/RedisCluster.html @@ -0,0 +1,14846 @@ + + + + + + RedisCluster | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + + +

class + RedisCluster (View source) +

+ + + + + + + + + +

Methods

+ +
+
+
+ +
+
+ __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, mixed $auth = NULL, array $context = NULL) + +

No description

+
+
+
+
+
+ string +
+
+ _compress(string $value) + +

No description

+
+
+
+
+
+ string +
+
+ _uncompress(string $value) + +

No description

+
+
+
+
+
+ bool|string +
+
+ _serialize(mixed $value) + +

No description

+
+
+
+
+
+ mixed +
+
+ _unserialize(string $value) + +

No description

+
+
+
+
+
+ string +
+
+ _pack(mixed $value) + +

No description

+
+
+
+
+
+ mixed +
+
+ _unpack(string $value) + +

No description

+
+
+
+
+
+ bool|string +
+
+ _prefix(string $key) + +

No description

+
+
+
+
+
+ array +
+
+ _masters() + +

No description

+
+
+
+
+
+ string|null +
+
+ _redir() + +

No description

+
+
+
+
+
+ mixed +
+
+ acl(string|array $key_or_address, string $subcmd, string ...$args) + +

No description

+
+
+
+
+
+ RedisCluster|bool|int +
+
+ append(string $key, mixed $value) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ bgrewriteaof(string|array $key_or_address) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ bgsave(string|array $key_or_address) + +

No description

+
+
+
+
+
+ RedisCluster|bool|int +
+
+ bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) + +

No description

+
+
+
+
+
+ RedisCluster|bool|int +
+
+ bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) + +

Return the position of the first bit set to 0 or 1 in a string.

+
+
+
+
+ RedisCluster|array|null|false +
+
+ blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) + +

See Redis::blpop()

+
+
+
+
+ RedisCluster|array|null|false +
+
+ brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) + +

See Redis::brpop()

+
+
+
+
+ mixed +
+
+ brpoplpush(string $srckey, string $deskey, int $timeout) + +

See Redis::brpoplpush()

+
+
+
+
+ array +
+
+ bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + +

No description

+
+
+
+
+
+ array +
+
+ bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + +

No description

+
+
+
+
+
+ RedisCluster|array|null|false +
+
+ bzmpop(float $timeout, array $keys, string $from, int $count = 1) + +

No description

+
+
+
+
+
+ RedisCluster|array|null|false +
+
+ zmpop(array $keys, string $from, int $count = 1) + +

No description

+
+
+
+
+
+ RedisCluster|array|null|false +
+
+ blmpop(float $timeout, array $keys, string $from, int $count = 1) + +

No description

+
+
+
+
+
+ RedisCluster|array|null|false +
+
+ lmpop(array $keys, string $from, int $count = 1) + +

No description

+
+
+
+
+
+ bool +
+
+ clearlasterror() + +

No description

+
+
+
+
+
+ array|string|bool +
+
+ client(string|array $key_or_address, string $subcommand, string|null $arg = NULL) + +

No description

+
+
+
+
+
+ bool +
+
+ close() + +

No description

+
+
+
+
+
+ mixed +
+
+ cluster(string|array $key_or_address, string $command, mixed ...$extra_args) + +

No description

+
+
+
+
+
+ mixed +
+
+ command(mixed ...$extra_args) + +

No description

+
+
+
+
+
+ mixed +
+
+ config(string|array $key_or_address, string $subcommand, mixed ...$extra_args) + +

No description

+
+
+
+
+
+ RedisCluster|int +
+
+ dbsize(string|array $key_or_address) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ decr(string $key, int $by = 1) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ decrby(string $key, int $value) + +

No description

+
+
+
+
+
+ float +
+
+ decrbyfloat(string $key, float $value) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ del(array|string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ bool +
+
+ discard() + +

No description

+
+
+
+
+
+ RedisCluster|string|false +
+
+ dump(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|string|false +
+
+ echo(string|array $key_or_address, string $msg) + +

No description

+
+
+
+
+
+ mixed +
+
+ eval(string $script, array $args = [], int $num_keys = 0) + +

No description

+
+
+
+
+
+ mixed +
+
+ eval_ro(string $script, array $args = [], int $num_keys = 0) + +

No description

+
+
+
+
+
+ mixed +
+
+ evalsha(string $script_sha, array $args = [], int $num_keys = 0) + +

No description

+
+
+
+
+
+ mixed +
+
+ evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0) + +

No description

+
+
+
+
+
+ array|false +
+
+ exec() + +

No description

+
+
+
+
+
+ RedisCluster|int|bool +
+
+ exists(mixed $key, mixed ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|int|bool +
+
+ touch(mixed $key, mixed ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ expire(string $key, int $timeout, string|null $mode = NULL) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ expireat(string $key, int $timestamp, string|null $mode = NULL) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ expiretime(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ pexpiretime(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ flushall(string|array $key_or_address, bool $async = false) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ flushdb(string|array $key_or_address, bool $async = false) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) + +

No description

+
+
+
+
+
+ RedisCluster|float|false +
+
+ geodist(string $key, string $src, string $dest, string|null $unit = null) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ geohash(string $key, string $member, string ...$other_members) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ geopos(string $key, string $member, string ...$other_members) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ get(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ getbit(string $key, int $value) + +

No description

+
+
+
+
+
+ string|null +
+
+ getlasterror() + +

No description

+
+
+
+
+
+ int +
+
+ getmode() + +

No description

+
+
+
+
+
+ mixed +
+
+ getoption(int $option) + +

No description

+
+
+
+
+
+ RedisCluster|string|false +
+
+ getrange(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ RedisCluster|string|array|int|false +
+
+ lcs(string $key1, string $key2, array|null $options = NULL) + +

No description

+
+
+
+
+
+ RedisCluster|string|bool +
+
+ getset(string $key, mixed $value) + +

No description

+
+
+
+
+
+ int|false +
+
+ gettransferredbytes() + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ hdel(string $key, string $member, string ...$other_members) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ hexists(string $key, string $member) + +

No description

+
+
+
+
+
+ mixed +
+
+ hget(string $key, string $member) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ hgetall(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ hincrby(string $key, string $member, int $value) + +

No description

+
+
+
+
+
+ RedisCluster|float|false +
+
+ hincrbyfloat(string $key, string $member, float $value) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ hkeys(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ hlen(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ hmget(string $key, array $keys) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ hmset(string $key, array $key_values) + +

No description

+
+
+
+
+
+ array|bool +
+
+ hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ hset(string $key, string $member, mixed $value) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ hsetnx(string $key, string $member, mixed $value) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ hstrlen(string $key, string $field) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ hvals(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ incr(string $key, int $by = 1) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ incrby(string $key, int $value) + +

No description

+
+
+
+
+
+ RedisCluster|float|false +
+
+ incrbyfloat(string $key, float $value) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ info(string|array $key_or_address, string ...$sections) + +

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

+
+
+
+
+ RedisCluster|array|false +
+
+ keys(string $pattern) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ lastsave(string|array $key_or_address) + +

No description

+
+
+
+
+
+ RedisCluster|string|bool +
+
+ lget(string $key, int $index) + +

No description

+
+
+
+
+
+ mixed +
+
+ lindex(string $key, int $index) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ linsert(string $key, string $pos, mixed $pivot, mixed $value) + +

No description

+
+
+
+
+
+ RedisCluster|int|bool +
+
+ llen(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|bool|string|array +
+
+ lpop(string $key, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|int|bool +
+
+ lpush(string $key, mixed $value, mixed ...$other_values) + +

No description

+
+
+
+
+
+ RedisCluster|int|bool +
+
+ lpushx(string $key, mixed $value) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ lrange(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ RedisCluster|int|bool +
+
+ lrem(string $key, mixed $value, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ lset(string $key, int $index, mixed $value) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ ltrim(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ mget(array $keys) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ mset(array $key_values) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ msetnx(array $key_values) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ multi(int $value = Redis::MULTI) + +

No description

+
+
+
+
+
+ RedisCluster|int|string|false +
+
+ object(string $subcommand, string $key) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ persist(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ pexpire(string $key, int $timeout, string|null $mode = NULL) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ pexpireat(string $key, int $timestamp, string|null $mode = NULL) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ pfadd(string $key, array $elements) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ pfcount(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ pfmerge(string $key, array $keys) + +

No description

+
+
+
+
+
+ mixed +
+
+ ping(string|array $key_or_address, string|null $message = NULL) + +

PING an instance in the redis cluster.

+
+
+
+
+ RedisCluster|bool +
+
+ psetex(string $key, int $timeout, string $value) + +

No description

+
+
+
+
+
+ void +
+
+ psubscribe(array $patterns, callable $callback) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ pttl(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ publish(string $channel, string $message) + +

No description

+
+
+
+
+
+ mixed +
+
+ pubsub(string|array $key_or_address, string ...$values) + +

No description

+
+
+
+
+
+ bool|array +
+
+ punsubscribe(string $pattern, string ...$other_patterns) + +

No description

+
+
+
+
+
+ RedisCluster|bool|string +
+
+ randomkey(string|array $key_or_address) + +

No description

+
+
+
+
+
+ mixed +
+
+ rawcommand(string|array $key_or_address, string $command, mixed ...$args) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ rename(string $key_src, string $key_dst) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ renamenx(string $key, string $newkey) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ restore(string $key, int $timeout, string $value, array|null $options = NULL) + +

No description

+
+
+
+
+
+ mixed +
+
+ role(string|array $key_or_address) + +

No description

+
+
+
+
+
+ RedisCluster|bool|string|array +
+
+ rpop(string $key, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|bool|string +
+
+ rpoplpush(string $src, string $dst) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ rpush(string $key, mixed ...$elements) + +

No description

+
+
+
+
+
+ RedisCluster|bool|int +
+
+ rpushx(string $key, string $value) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ sadd(string $key, mixed $value, mixed ...$other_values) + +

No description

+
+
+
+
+
+ RedisCluster|bool|int +
+
+ saddarray(string $key, array $values) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ save(string|array $key_or_address) + +

No description

+
+
+
+
+
+ bool|array +
+
+ scan(int|null $iterator, string|array $key_or_address, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ scard(string $key) + +

No description

+
+
+
+
+
+ mixed +
+
+ script(string|array $key_or_address, mixed ...$args) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ sdiff(string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ sdiffstore(string $dst, string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|string|bool +
+
+ set(string $key, mixed $value, mixed $options = NULL) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ setbit(string $key, int $offset, bool $onoff) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ setex(string $key, int $expire, mixed $value) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ setnx(string $key, mixed $value) + +

No description

+
+
+
+
+
+ bool +
+
+ setoption(int $option, mixed $value) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ setrange(string $key, int $offset, string $value) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ sinter(array|string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ sintercard(array $keys, int $limit = -1) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ sinterstore(array|string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ sismember(string $key, mixed $value) + +

No description

+
+
+
+
+
+ mixed +
+
+ slowlog(string|array $key_or_address, mixed ...$args) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ smembers(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ smove(string $src, string $dst, string $member) + +

No description

+
+
+
+
+
+ RedisCluster|array|bool|int|string +
+
+ sort(string $key, array|null $options = NULL) + +

No description

+
+
+
+
+
+ RedisCluster|array|bool|int|string +
+
+ sort_ro(string $key, array|null $options = NULL) + +

No description

+
+
+
+
+
+ RedisCluster|string|array|false +
+
+ spop(string $key, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|string|array|false +
+
+ srandmember(string $key, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ srem(string $key, mixed $value, mixed ...$other_values) + +

No description

+
+
+
+
+
+ array|false +
+
+ sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ strlen(string $key) + +

No description

+
+
+
+
+
+ void +
+
+ subscribe(array $channels, callable $cb) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ sunion(string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ sunionstore(string $dst, string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ time(string|array $key_or_address) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ ttl(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ type(string $key) + +

No description

+
+
+
+
+
+ bool|array +
+
+ unsubscribe(array $channels) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ unlink(array|string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ bool +
+
+ unwatch() + +

No description

+
+
+
+
+
+ RedisCluster|bool +
+
+ watch(string $key, string ...$other_keys) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ xack(string $key, string $group, array $ids) + +

No description

+
+
+
+
+
+ RedisCluster|string|false +
+
+ xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false) + +

No description

+
+
+
+
+
+ RedisCluster|string|array|false +
+
+ xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ xdel(string $key, array $ids) + +

No description

+
+
+
+
+
+ mixed +
+
+ xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false) + +

No description

+
+
+
+
+
+ mixed +
+
+ xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ xlen(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ xrange(string $key, string $start, string $end, int $count = -1) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ xread(array $streams, int $count = -1, int $block = -1) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ xrevrange(string $key, string $start, string $end, int $count = -1) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zcard(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zcount(string $key, string $start, string $end) + +

No description

+
+
+
+
+
+ RedisCluster|float|false +
+
+ zincrby(string $key, float $value, string $member) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zintercard(array $keys, int $limit = -1) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zlexcount(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zpopmax(string $key, int $value = null) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zpopmin(string $key, int $value = null) + +

No description

+
+
+
+
+
+ RedisCluster|array|bool +
+
+ zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ zrangebyscore(string $key, string $start, string $end, array $options = []) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zrank(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zrem(string $key, string $value, string ...$other_values) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zremrangebylex(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zremrangebyrank(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zremrangebyscore(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zrevrange(string $key, string $min, string $max, array $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zrevrangebylex(string $key, string $min, string $max, array $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zrevrangebyscore(string $key, string $min, string $max, array $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zrevrank(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|float|false +
+
+ zscore(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) + +

No description

+
+
+
+
+ + +

Details

+ +
+
+

+ + + __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, mixed $auth = NULL, array $context = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string|null$name
array$seeds
int|float$timeout
int|float$read_timeout
bool$persistent
mixed$auth
array$context
+ + + + + +
+
+ +
+
+

+ + string + _compress(string $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$value
+ + +

Return Value

+ + + + + + +
string
+ + + +

See also

+ + + + + + +
+ +Redis::_compress +
+ + +
+
+ +
+
+

+ + string + _uncompress(string $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$value
+ + +

Return Value

+ + + + + + +
string
+ + + +

See also

+ + + + + + +
+ +Redis::_uncompress +
+ + +
+
+ +
+
+

+ + bool|string + _serialize(mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
mixed$value
+ + +

Return Value

+ + + + + + +
bool|string
+ + + +

See also

+ + + + + + +
+ +Redis::_serialize +
+ + +
+
+ +
+
+

+ + mixed + _unserialize(string $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$value
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ +Redis::_unserialize +
+ + +
+
+ +
+
+

+ + string + _pack(mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
mixed$value
+ + +

Return Value

+ + + + + + +
string
+ + + +

See also

+ + + + + + +
+ +Redis::_pack +
+ + +
+
+ +
+
+

+ + mixed + _unpack(string $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$value
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ +Redis::_unpack +
+ + +
+
+ +
+
+

+ + bool|string + _prefix(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
bool|string
+ + + +

See also

+ + + + + + +
+ +Redis::_prefix +
+ + +
+
+ +
+
+

+ + array + _masters() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
array
+ + + + +
+
+ +
+
+

+ + string|null + _redir() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
string|null
+ + + + +
+
+ +
+
+

+ + mixed + acl(string|array $key_or_address, string $subcmd, string ...$args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$subcmd
string...$args
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::acl +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|int + append(string $key, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|int
+ + + +

See also

+ + + + + + +
+ +Redis::append +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + bgrewriteaof(string|array $key_or_address) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::bgrewriteaof +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + bgsave(string|array $key_or_address) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::bgsave +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|int + bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
bool$bybit
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|int
+ + + +

See also

+ + + + + + +
+ Redis::bitcount +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|int + bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$operation
string$deskey
string$srckey
string...$otherkeys
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|int
+ + + +

See also

+ + + + + + +
+ Redis::bitop +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) + +

+
+ + + +
+

Return the position of the first bit set to 0 or 1 in a string.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key

The key to check (must be a string)

bool$bit

Whether to look for an unset (0) or set (1) bit.

int$start

Where in the string to start looking.

int$end

Where in the string to stop looking.

bool$bybit

If true, Redis will treat $start and $end as BIT values and not bytes, so if start +was 0 and end was 2, Redis would only search the first two bits.

+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ https://https://redis.io/commands/bitpos/ +
+ + +
+
+ +
+
+

+ + RedisCluster|array|null|false + blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) + +

+
+ + + +
+

See Redis::blpop()

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|float|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
RedisCluster|array|null|false
+ + + + +
+
+ +
+
+

+ + RedisCluster|array|null|false + brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) + +

+
+ + + +
+

See Redis::brpop()

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|float|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
RedisCluster|array|null|false
+ + + + +
+
+ +
+
+

+ + mixed + brpoplpush(string $srckey, string $deskey, int $timeout) + +

+
+ + + +
+

See Redis::brpoplpush()

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$srckey
string$deskey
int$timeout
+ + +

Return Value

+ + + + + + +
mixed
+ + + + +
+
+ +
+
+

+ + array + bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
array
+ + + +

See also

+ + + + + + +
+ Redis::bzpopmax +
+ + +
+
+ +
+
+

+ + array + bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
array
+ + + +

See also

+ + + + + + +
+ Redis::bzpopmin +
+ + +
+
+ +
+
+

+ + RedisCluster|array|null|false + bzmpop(float $timeout, array $keys, string $from, int $count = 1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
float$timeout
array$keys
string$from
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|array|null|false
+ + + +

See also

+ + + + + + +
+ Redis::bzmpop +
+ + +
+
+ +
+
+

+ + RedisCluster|array|null|false + zmpop(array $keys, string $from, int $count = 1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys
string$from
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|array|null|false
+ + + +

See also

+ + + + + + +
+ Redis::zmpop +
+ + +
+
+ +
+
+

+ + RedisCluster|array|null|false + blmpop(float $timeout, array $keys, string $from, int $count = 1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
float$timeout
array$keys
string$from
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|array|null|false
+ + + +

See also

+ + + + + + +
+ +Redis::blmpop +
+ + +
+
+ +
+
+

+ + RedisCluster|array|null|false + lmpop(array $keys, string $from, int $count = 1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys
string$from
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|array|null|false
+ + + +

See also

+ + + + + + +
+ +Redis::lmpop +
+ + +
+
+ +
+
+

+ + bool + clearlasterror() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool
+ + + +

See also

+ + + + + + +
+ \Redis::clearlasterror() +
+ + +
+
+ +
+
+

+ + array|string|bool + client(string|array $key_or_address, string $subcommand, string|null $arg = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$subcommand
string|null$arg
+ + +

Return Value

+ + + + + + +
array|string|bool
+ + + +

See also

+ + + + + + +
+ Redis::client +
+ + +
+
+ +
+
+

+ + bool + close() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool
+ + + +

See also

+ + + + + + +
+ Redis::close +
+ + +
+
+ +
+
+

+ + mixed + cluster(string|array $key_or_address, string $command, mixed ...$extra_args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$command
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::cluster +
+ + +
+
+ +
+
+

+ + mixed + command(mixed ...$extra_args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::command +
+ + +
+
+ +
+
+

+ + mixed + config(string|array $key_or_address, string $subcommand, mixed ...$extra_args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$subcommand
mixed...$extra_args
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ +Redis::config +
+ + +
+
+ +
+
+

+ + RedisCluster|int + dbsize(string|array $key_or_address) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ + + + + + +
RedisCluster|int
+ + + +

See also

+ + + + + + +
+ \Redis::dbsize() +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + decr(string $key, int $by = 1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$by
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ +Redis::decr +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + decrby(string $key, int $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$value
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ \Redis::decrby() +
+ + +
+
+ +
+
+

+ + float + decrbyfloat(string $key, float $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
float$value
+ + +

Return Value

+ + + + + + +
float
+ + + +

See also

+ + + + + + +
+ Redis::decrbyfloat +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + del(array|string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ +Redis::del +
+ + +
+
+ +
+
+

+ + bool + discard() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool
+ + + +

See also

+ + + + + + +
+ Redis::discard +
+ + +
+
+ +
+
+

+ + RedisCluster|string|false + dump(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|string|false
+ + + +

See also

+ + + + + + +
+ Redis::dump +
+ + +
+
+ +
+
+

+ + RedisCluster|string|false + echo(string|array $key_or_address, string $msg) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address
string$msg
+ + +

Return Value

+ + + + + + +
RedisCluster|string|false
+ + + +

See also

+ + + + + + +
+ +Redis::echo +
+ + +
+
+ +
+
+

+ + mixed + eval(string $script, array $args = [], int $num_keys = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script
array$args
int$num_keys
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::eval +
+ + +
+
+ +
+
+

+ + mixed + eval_ro(string $script, array $args = [], int $num_keys = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script
array$args
int$num_keys
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::eval_ro +
+ + +
+
+ +
+
+

+ + mixed + evalsha(string $script_sha, array $args = [], int $num_keys = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script_sha
array$args
int$num_keys
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::evalsha +
+ + +
+
+ +
+
+

+ + mixed + evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script_sha
array$args
int$num_keys
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::evalsha_ro +
+ + +
+
+ +
+
+

+ + array|false + exec() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
array|false
+ + + +

See also

+ + + + + + +
+ +Redis::exec +
+ + +
+
+ +
+
+

+ + RedisCluster|int|bool + exists(mixed $key, mixed ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
mixed$key
mixed...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|int|bool
+ + + +

See also

+ + + + + + +
+ Redis::exists +
+ + +
+
+ +
+
+

+ + RedisCluster|int|bool + touch(mixed $key, mixed ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
mixed$key
mixed...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|int|bool
+ + + +

See also

+ + + + + + +
+ +Redis::touch +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + expire(string $key, int $timeout, string|null $mode = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timeout
string|null$mode
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::expire +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + expireat(string $key, int $timestamp, string|null $mode = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timestamp
string|null$mode
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::expireat +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + expiretime(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ +Redis::expiretime +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + pexpiretime(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ +Redis::pexpiretime +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + flushall(string|array $key_or_address, bool $async = false) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address
bool$async
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::flushall +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + flushdb(string|array $key_or_address, bool $async = false) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address
bool$async
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::flushdb +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
string$member
mixed...$other_triples_and_options
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::geoadd +
+ + +
+
+ +
+
+

+ + RedisCluster|float|false + geodist(string $key, string $src, string $dest, string|null $unit = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$src
string$dest
string|null$unit
+ + +

Return Value

+ + + + + + +
RedisCluster|float|false
+ + + +

See also

+ + + + + + +
+ Redis::geodist +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + geohash(string $key, string $member, string ...$other_members) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
string...$other_members
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::geohash +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + geopos(string $key, string $member, string ...$other_members) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
string...$other_members
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::geopos +
+ + +
+
+ +
+
+

+ + mixed + georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
float$radius
string$unit
array$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::georadius +
+ + +
+
+ +
+
+

+ + mixed + georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
float$radius
string$unit
array$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::georadius_ro +
+ + +
+
+ +
+
+

+ + mixed + georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$member
float$radius
string$unit
array$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::georadiusbymember +
+ + +
+
+ +
+
+

+ + mixed + georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$member
float$radius
string$unit
array$options
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::georadiusbymember_ro +
+ + +
+
+ +
+
+

+ + mixed + get(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::get +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + getbit(string $key, int $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$value
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::getbit +
+ + +
+
+ +
+
+

+ + string|null + getlasterror() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
string|null
+ + + +

See also

+ + + + + + +
+ Redis::getlasterror +
+ + +
+
+ +
+
+

+ + int + getmode() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
int
+ + + +

See also

+ + + + + + +
+ Redis::getmode +
+ + +
+
+ +
+
+

+ + mixed + getoption(int $option) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
int$option
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::getoption +
+ + +
+
+ +
+
+

+ + RedisCluster|string|false + getrange(string $key, int $start, int $end) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
+ + +

Return Value

+ + + + + + +
RedisCluster|string|false
+ + + +

See also

+ + + + + + +
+ Redis::getrange +
+ + +
+
+ +
+
+

+ + RedisCluster|string|array|int|false + lcs(string $key1, string $key2, array|null $options = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key1
string$key2
array|null$options
+ + +

Return Value

+ + + + + + +
RedisCluster|string|array|int|false
+ + + +

See also

+ + + + + + +
+ Redis::lcs +
+ + +
+
+ +
+
+

+ + RedisCluster|string|bool + getset(string $key, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|string|bool
+ + + +

See also

+ + + + + + +
+ Redis::getset +
+ + +
+
+ +
+
+

+ + int|false + gettransferredbytes() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
int|false
+ + + +

See also

+ + + + + + +
+ Redis::gettransferredbytes +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + hdel(string $key, string $member, string ...$other_members) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
string...$other_members
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::hdel +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + hexists(string $key, string $member) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$member
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::hexists +
+ + +
+
+ +
+
+

+ + mixed + hget(string $key, string $member) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$member
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::hget +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + hgetall(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::hgetall +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + hincrby(string $key, string $member, int $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
int$value
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::hincrby +
+ + +
+
+ +
+
+

+ + RedisCluster|float|false + hincrbyfloat(string $key, string $member, float $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
float$value
+ + +

Return Value

+ + + + + + +
RedisCluster|float|false
+ + + +

See also

+ + + + + + +
+ Redis::hincrbyfloat +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + hkeys(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::hkeys +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + hlen(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::hlen +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + hmget(string $key, array $keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$keys
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::hmget +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + hmset(string $key, array $key_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$key_values
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::hmset +
+ + +
+
+ +
+
+

+ + array|bool + hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int|null$iterator
string|null$pattern
int$count
+ + +

Return Value

+ + + + + + +
array|bool
+ + + +

See also

+ + + + + + +
+ Redis::hscan +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + hset(string $key, string $member, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::hset +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + hsetnx(string $key, string $member, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::hsetnx +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + hstrlen(string $key, string $field) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$field
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::hstrlen +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + hvals(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::hvals +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + incr(string $key, int $by = 1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$by
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::incr +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + incrby(string $key, int $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$value
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::incrby +
+ + +
+
+ +
+
+

+ + RedisCluster|float|false + incrbyfloat(string $key, float $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
float$value
+ + +

Return Value

+ + + + + + +
RedisCluster|float|false
+ + + +

See also

+ + + + + + +
+ Redis::incrbyfloat +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + info(string|array $key_or_address, string ...$sections) + +

+
+ + + +
+

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

If connected to Redis server >= 7.0.0 you may pass multiple optional sections.

+
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address

Either a key name or array with host and port indicating +which cluster node we want to send the command to.

string...$sections

Optional section(s) you wish Redis server to return.

+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/info/ +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + keys(string $pattern) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$pattern
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::keys +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + lastsave(string|array $key_or_address) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::lastsave +
+ + +
+
+ +
+
+

+ + RedisCluster|string|bool + lget(string $key, int $index) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$index
+ + +

Return Value

+ + + + + + +
RedisCluster|string|bool
+ + + +

See also

+ + + + + + +
+ Redis::lget +
+ + +
+
+ +
+
+

+ + mixed + lindex(string $key, int $index) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$index
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::lindex +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + linsert(string $key, string $pos, mixed $pivot, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$pos
mixed$pivot
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::linsert +
+ + +
+
+ +
+
+

+ + RedisCluster|int|bool + llen(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|bool
+ + + +

See also

+ + + + + + +
+ Redis::llen +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|string|array + lpop(string $key, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|string|array
+ + + +

See also

+ + + + + + +
+ Redis::lpop +
+ + +
+
+ +
+
+

+ + RedisCluster|int|bool + lpush(string $key, mixed $value, mixed ...$other_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
mixed...$other_values
+ + +

Return Value

+ + + + + + +
RedisCluster|int|bool
+ + + +

See also

+ + + + + + +
+ Redis::lpush +
+ + +
+
+ +
+
+

+ + RedisCluster|int|bool + lpushx(string $key, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|int|bool
+ + + +

See also

+ + + + + + +
+ Redis::lpushx +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + lrange(string $key, int $start, int $end) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::lrange +
+ + +
+
+ +
+
+

+ + RedisCluster|int|bool + lrem(string $key, mixed $value, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|int|bool
+ + + +

See also

+ + + + + + +
+ Redis::lrem +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + lset(string $key, int $index, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$index
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::lset +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + ltrim(string $key, int $start, int $end) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::ltrim +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + mget(array $keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$keys
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::mget +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + mset(array $key_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$key_values
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::mset +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + msetnx(array $key_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$key_values
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::msetnx +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + multi(int $value = Redis::MULTI) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
int$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + + +
+
+ +
+
+

+ + RedisCluster|int|string|false + object(string $subcommand, string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$subcommand
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|string|false
+ + + +

See also

+ + + + + + +
+ Redis::object +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + persist(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::persist +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + pexpire(string $key, int $timeout, string|null $mode = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timeout
string|null$mode
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::pexpire +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + pexpireat(string $key, int $timestamp, string|null $mode = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timestamp
string|null$mode
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::pexpireat +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + pfadd(string $key, array $elements) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$elements
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ +Redis::pfadd +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + pfcount(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ +Redis::pfcount +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + pfmerge(string $key, array $keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$keys
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ +Redis::pfmerge +
+ + +
+
+ +
+
+

+ + mixed + ping(string|array $key_or_address, string|null $message = NULL) + +

+
+ + + +
+

PING an instance in the redis cluster.

+
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address

Either a key name or a two element array with host and +address, informing RedisCluster which node to ping.

string|null$message

An optional message to send.

+ + +

Return Value

+ + + + + + +
mixed

This method always returns true if no message was sent, and the message itself +if one was.

+ + + +

See also

+ + + + + + +
+ +Redis::ping +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + psetex(string $key, int $timeout, string $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timeout
string$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::psetex +
+ + +
+
+ +
+
+

+ + void + psubscribe(array $patterns, callable $callback) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$patterns
callable$callback
+ + +

Return Value

+ + + + + + +
void
+ + + +

See also

+ + + + + + +
+ Redis::psubscribe +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + pttl(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::pttl +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + publish(string $channel, string $message) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$channel
string$message
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::publish +
+ + +
+
+ +
+
+

+ + mixed + pubsub(string|array $key_or_address, string ...$values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address
string...$values
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::pubsub +
+ + +
+
+ +
+
+

+ + bool|array + punsubscribe(string $pattern, string ...$other_patterns) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$pattern
string...$other_patterns
+ + +

Return Value

+ + + + + + +
bool|array
+ + + +

See also

+ + + + + + +
+ Redis::punsubscribe +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|string + randomkey(string|array $key_or_address) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|string
+ + + +

See also

+ + + + + + +
+ Redis::randomkey +
+ + +
+
+ +
+
+

+ + mixed + rawcommand(string|array $key_or_address, string $command, mixed ...$args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$command
mixed...$args
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::rawcommand +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + rename(string $key_src, string $key_dst) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key_src
string$key_dst
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::rename +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + renamenx(string $key, string $newkey) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$newkey
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::renamenx +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + restore(string $key, int $timeout, string $value, array|null $options = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int$timeout
string$value
array|null$options
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::restore +
+ + +
+
+ +
+
+

+ + mixed + role(string|array $key_or_address) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::role +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|string|array + rpop(string $key, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|string|array
+ + + +

See also

+ + + + + + +
+ \Redis::rpop() +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|string + rpoplpush(string $src, string $dst) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$src
string$dst
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|string
+ + + +

See also

+ + + + + + +
+ +Redis::rpoplpush +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + rpush(string $key, mixed ...$elements) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed...$elements
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::rpush +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|int + rpushx(string $key, string $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|int
+ + + +

See also

+ + + + + + +
+ Redis::rpushx +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + sadd(string $key, mixed $value, mixed ...$other_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
mixed...$other_values
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ \Redis::sadd() +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|int + saddarray(string $key, array $values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$values
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|int
+ + + +

See also

+ + + + + + +
+ \Redis::saddarray() +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + save(string|array $key_or_address) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::save +
+ + +
+
+ +
+
+

+ + bool|array + scan(int|null $iterator, string|array $key_or_address, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
int|null$iterator
string|array$key_or_address
string|null$pattern
int$count
+ + +

Return Value

+ + + + + + +
bool|array
+ + + +

See also

+ + + + + + +
+ Redis::scan +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + scard(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::scard +
+ + +
+
+ +
+
+

+ + mixed + script(string|array $key_or_address, mixed ...$args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address
mixed...$args
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::script +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + sdiff(string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ \Redis::sdiff() +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + sdiffstore(string $dst, string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$dst
string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ \Redis::sdiffstore() +
+ + +
+
+ +
+
+

+ + RedisCluster|string|bool + set(string $key, mixed $value, mixed $options = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
mixed$options
+ + +

Return Value

+ + + + + + +
RedisCluster|string|bool
+ + + +

See also

+ + + + + + +
+ https://redis.io/commands/set +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + setbit(string $key, int $offset, bool $onoff) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$offset
bool$onoff
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::setbit +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + setex(string $key, int $expire, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$expire
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::setex +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + setnx(string $key, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::setnx +
+ + +
+
+ +
+
+

+ + bool + setoption(int $option, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
int$option
mixed$value
+ + +

Return Value

+ + + + + + +
bool
+ + + +

See also

+ + + + + + +
+ Redis::setoption +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + setrange(string $key, int $offset, string $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$offset
string$value
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::setrange +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + sinter(array|string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ \Redis::sinter() +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + sintercard(array $keys, int $limit = -1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$keys
int$limit
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::sintercard +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + sinterstore(array|string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ \Redis::sinterstore() +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + sismember(string $key, mixed $value) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::sismember +
+ + +
+
+ +
+
+

+ + mixed + slowlog(string|array $key_or_address, mixed ...$args) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address
mixed...$args
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::slowlog +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + smembers(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ \Redis::smembers() +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + smove(string $src, string $dst, string $member) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$src
string$dst
string$member
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ \Redis::smove() +
+ + +
+
+ +
+
+

+ + RedisCluster|array|bool|int|string + sort(string $key, array|null $options = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array|null$options
+ + +

Return Value

+ + + + + + +
RedisCluster|array|bool|int|string
+ + + +

See also

+ + + + + + +
+ +Redis::sort +
+ + +
+
+ +
+
+

+ + RedisCluster|array|bool|int|string + sort_ro(string $key, array|null $options = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array|null$options
+ + +

Return Value

+ + + + + + +
RedisCluster|array|bool|int|string
+ + + +

See also

+ + + + + + +
+ +Redis::sort_ro +
+ + +
+
+ +
+
+

+ + RedisCluster|string|array|false + spop(string $key, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|string|array|false
+ + + +

See also

+ + + + + + +
+ Redis::spop +
+ + +
+
+ +
+
+

+ + RedisCluster|string|array|false + srandmember(string $key, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|string|array|false
+ + + +

See also

+ + + + + + +
+ Redis::srandmember +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + srem(string $key, mixed $value, mixed ...$other_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
mixed...$other_values
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::srem +
+ + +
+
+ +
+
+

+ + array|false + sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int|null$iterator
string|null$pattern
int$count
+ + +

Return Value

+ + + + + + +
array|false
+ + + +

See also

+ + + + + + +
+ Redis::sscan +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + strlen(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::strlen +
+ + +
+
+ +
+
+

+ + void + subscribe(array $channels, callable $cb) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$channels
callable$cb
+ + +

Return Value

+ + + + + + +
void
+ + + +

See also

+ + + + + + +
+ Redis::subscribe +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + sunion(string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ \Redis::sunion() +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + sunionstore(string $dst, string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$dst
string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ \Redis::sunionstore() +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + time(string|array $key_or_address) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::time +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + ttl(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::ttl +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + type(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::type +
+ + +
+
+ +
+
+

+ + bool|array + unsubscribe(array $channels) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$channels
+ + +

Return Value

+ + + + + + +
bool|array
+ + + +

See also

+ + + + + + +
+ Redis::unsubscribe +
+ + +
+
+ +
+
+ +
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::unlink +
+ + +
+
+ +
+
+

+ + bool + unwatch() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool
+ + + +

See also

+ + + + + + +
+ Redis::unwatch +
+ + +
+
+ +
+
+

+ + RedisCluster|bool + watch(string $key, string ...$other_keys) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string...$other_keys
+ + +

Return Value

+ + + + + + +
RedisCluster|bool
+ + + +

See also

+ + + + + + +
+ Redis::watch +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + xack(string $key, string $group, array $ids) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$group
array$ids
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::xack +
+ + +
+
+ +
+
+

+ + RedisCluster|string|false + xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$id
array$values
int$maxlen
bool$approx
+ + +

Return Value

+ + + + + + +
RedisCluster|string|false
+ + + +

See also

+ + + + + + +
+ Redis::xadd +
+ + +
+
+ +
+
+

+ + RedisCluster|string|array|false + xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$group
string$consumer
int$min_iddle
array$ids
array$options
+ + +

Return Value

+ + + + + + +
RedisCluster|string|array|false
+ + + +

See also

+ + + + + + +
+ Redis::xclaim +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + xdel(string $key, array $ids) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$ids
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::xdel +
+ + +
+
+ +
+
+

+ + mixed + xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$operation
string$key
string$arg1
string$arg2
bool$arg3
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::xgroup +
+ + +
+
+ +
+
+

+ + mixed + xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$operation
string|null$arg1
string|null$arg2
int$count
+ + +

Return Value

+ + + + + + +
mixed
+ + + +

See also

+ + + + + + +
+ Redis::xinfo +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + xlen(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::xlen +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$group
string|null$start
string|null$end
int$count
string|null$consumer
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::xpending +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + xrange(string $key, string $start, string $end, int $count = -1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$start
string$end
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::xrange +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + xread(array $streams, int $count = -1, int $block = -1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$streams
int$count
int$block
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::xread +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$group
string$consumer
array$streams
int$count
int$block
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::xreadgroup +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + xrevrange(string $key, string $start, string $end, int $count = -1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$start
string$end
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::xrevrange +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
int$maxlen
bool$approx
bool$minid
int$limit
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::xtrim +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
array|float$score_or_options
mixed...$more_scores_and_mems
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zadd +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zcard(string $key) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zcard +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zcount(string $key, string $start, string $end) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$start
string$end
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zcount +
+ + +
+
+ +
+
+

+ + RedisCluster|float|false + zincrby(string $key, float $value, string $member) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
float$value
string$member
+ + +

Return Value

+ + + + + + +
RedisCluster|float|false
+ + + +

See also

+ + + + + + +
+ Redis::zincrby +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$dst
array$keys
array|null$weights
string|null$aggregate
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zinterstore +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zintercard(array $keys, int $limit = -1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$keys
int$limit
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zintercard +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zlexcount(string $key, string $min, string $max) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zlexcount +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + zpopmax(string $key, int $value = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::zpopmax +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + zpopmin(string $key, int $value = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$value
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::zpopmin +
+ + +
+
+ +
+
+

+ + RedisCluster|array|bool + zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
mixed$start
mixed$end
array|bool|null$options
+ + +

Return Value

+ + + + + + +
RedisCluster|array|bool
+ + + +

See also

+ + + + + + +
+ Redis::zrange +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$dstkey
string$srckey
int$start
int$end
array|bool|null$options
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zrangestore +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
int$offset
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::zrangebylex +
+ + +
+
+ +
+
+

+ + RedisCluster|array|false + zrangebyscore(string $key, string $start, string $end, array $options = []) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$start
string$end
array$options
+ + +

Return Value

+ + + + + + +
RedisCluster|array|false
+ + + +

See also

+ + + + + + +
+ Redis::zrangebyscore +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zrank(string $key, mixed $member) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$member
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zrank +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zrem(string $key, string $value, string ...$other_values) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$value
string...$other_values
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zrem +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zremrangebylex(string $key, string $min, string $max) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zremrangebylex +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zremrangebyrank(string $key, string $min, string $max) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zremrangebyrank +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zremrangebyscore(string $key, string $min, string $max) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zremrangebyscore +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + zrevrange(string $key, string $min, string $max, array $options = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
array$options
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::zrevrange +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + zrevrangebylex(string $key, string $min, string $max, array $options = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
array$options
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::zrevrangebylex +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + zrevrangebyscore(string $key, string $min, string $max, array $options = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
array$options
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::zrevrangebyscore +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zrevrank(string $key, mixed $member) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$member
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zrevrank +
+ + +
+
+ +
+
+

+ + RedisCluster|bool|array + zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int|null$iterator
string|null$pattern
int$count
+ + +

Return Value

+ + + + + + +
RedisCluster|bool|array
+ + + +

See also

+ + + + + + +
+ Redis::zscan +
+ + +
+
+ +
+
+

+ + RedisCluster|float|false + zscore(string $key, mixed $member) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$member
+ + +

Return Value

+ + + + + + +
RedisCluster|float|false
+ + + +

See also

+ + + + + + +
+ Redis::zscore +
+ + +
+
+ +
+
+

+ + RedisCluster|int|false + zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$dst
array$keys
array|null$weights
string|null$aggregate
+ + +

Return Value

+ + + + + + +
RedisCluster|int|false
+ + + +

See also

+ + + + + + +
+ Redis::zunionstore +
+ + +
+
+ +
+
+ + +
+
+ + + diff --git a/docs/RedisClusterException.html b/docs/RedisClusterException.html new file mode 100644 index 0000000000..c366b7fcaa --- /dev/null +++ b/docs/RedisClusterException.html @@ -0,0 +1,103 @@ + + + + + + RedisClusterException | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + + +

class + RedisClusterException extends RuntimeException (View source) +

+ + + + + + + + + + +
+
+ + + diff --git a/docs/RedisException.html b/docs/RedisException.html new file mode 100644 index 0000000000..4851bcad2c --- /dev/null +++ b/docs/RedisException.html @@ -0,0 +1,103 @@ + + + + + + RedisException | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + + +

class + RedisException extends RuntimeException (View source) +

+ + + + + + + + + + +
+
+ + + diff --git a/docs/RedisSentinel.html b/docs/RedisSentinel.html new file mode 100644 index 0000000000..00674d8b5a --- /dev/null +++ b/docs/RedisSentinel.html @@ -0,0 +1,748 @@ + + + + + + RedisSentinel | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + + +

class + RedisSentinel (View source) +

+ + + + + + + + + +

Methods

+ +
+
+
+ +
+
+ __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = null, int $retry_interval = 0, float $read_timeout = 0, mixed $auth = null, array $context = null) + +

No description

+
+
+
+
+
+ bool|RedisSentinel +
+
+ ckquorum(string $master) + +

No description

+
+
+
+
+
+ bool|RedisSentinel +
+
+ failover(string $master) + +

No description

+
+
+
+
+
+ bool|RedisSentinel +
+
+ flushconfig() + +

No description

+
+
+
+
+
+ array|bool|RedisSentinel +
+
+ getMasterAddrByName(string $master) + +

No description

+
+
+
+
+
+ array|bool|RedisSentinel +
+
+ master(string $master) + +

No description

+
+
+
+
+
+ array|bool|RedisSentinel +
+
+ masters() + +

No description

+
+
+
+
+
+ string +
+
+ myid() + +

No description

+
+
+
+
+
+ bool|RedisSentinel +
+
+ ping() + +

No description

+
+
+
+
+
+ bool|RedisSentinel +
+
+ reset(string $pattern) + +

No description

+
+
+
+
+
+ array|bool|RedisSentinel +
+
+ sentinels(string $master) + +

No description

+
+
+
+
+
+ array|bool|RedisSentinel +
+
+ slaves(string $master) + +

No description

+
+
+
+
+ + +

Details

+ +
+
+

+ + + __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = null, int $retry_interval = 0, float $read_timeout = 0, mixed $auth = null, array $context = null) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
mixed$persistent
int$retry_interval
float$read_timeout
mixed$auth
array$context
+ + + + + +
+
+ +
+
+

+ + bool|RedisSentinel + ckquorum(string $master) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$master
+ + +

Return Value

+ + + + + + +
bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + bool|RedisSentinel + failover(string $master) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$master
+ + +

Return Value

+ + + + + + +
bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + bool|RedisSentinel + flushconfig() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + array|bool|RedisSentinel + getMasterAddrByName(string $master) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$master
+ + +

Return Value

+ + + + + + +
array|bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + array|bool|RedisSentinel + master(string $master) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$master
+ + +

Return Value

+ + + + + + +
array|bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + array|bool|RedisSentinel + masters() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
array|bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + string + myid() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
string
+ + + + +
+
+ +
+
+

+ + bool|RedisSentinel + ping() + +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ + + + + + +
bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + bool|RedisSentinel + reset(string $pattern) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$pattern
+ + +

Return Value

+ + + + + + +
bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + array|bool|RedisSentinel + sentinels(string $master) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$master
+ + +

Return Value

+ + + + + + +
array|bool|RedisSentinel
+ + + + +
+
+ +
+
+

+ + array|bool|RedisSentinel + slaves(string $master) + +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$master
+ + +

Return Value

+ + + + + + +
array|bool|RedisSentinel
+ + + + +
+
+ +
+
+ + +
+
+ + + diff --git a/docs/[Global_Namespace].html b/docs/[Global_Namespace].html new file mode 100644 index 0000000000..9c8ee96579 --- /dev/null +++ b/docs/[Global_Namespace].html @@ -0,0 +1,134 @@ + + + + + + [Global Namespace] | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ +
+
+ + +

Namespaces

+ + +

Classes

+
+
+
+ Redis
+
+
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+ +
+
+
+ + + +
+
+ + + diff --git a/docs/classes.html b/docs/classes.html new file mode 100644 index 0000000000..255cc79b28 --- /dev/null +++ b/docs/classes.html @@ -0,0 +1,120 @@ + + + + + + All Classes | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + +
+
+
+ Redis
+
+
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+ +
+
+
+
+
+ + + diff --git a/docs/css/bootstrap-theme.min.css b/docs/css/bootstrap-theme.min.css new file mode 100644 index 0000000000..59e7de99c5 --- /dev/null +++ b/docs/css/bootstrap-theme.min.css @@ -0,0 +1,7 @@ +/*! + * Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/) + *//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-default.disabled,.btn-primary.disabled,.btn-success.disabled,.btn-info.disabled,.btn-warning.disabled,.btn-danger.disabled,.btn-default[disabled],.btn-primary[disabled],.btn-success[disabled],.btn-info[disabled],.btn-warning[disabled],.btn-danger[disabled],fieldset[disabled] .btn-default,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-info,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-danger{-webkit-box-shadow:none;box-shadow:none}.btn-default .badge,.btn-primary .badge,.btn-success .badge,.btn-info .badge,.btn-warning .badge,.btn-danger .badge{text-shadow:none}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-o-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#e0e0e0));background-image:linear-gradient(to bottom, #fff 0, #e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top, #428bca 0, #2d6ca2 100%);background-image:-o-linear-gradient(top, #428bca 0, #2d6ca2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #428bca), to(#2d6ca2));background-image:linear-gradient(to bottom, #428bca 0, #2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#2d6ca2;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#419641));background-image:linear-gradient(to bottom, #5cb85c 0, #419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:-o-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5bc0de), to(#2aabd2));background-image:linear-gradient(to bottom, #5bc0de 0, #2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#eb9316));background-image:linear-gradient(to bottom, #f0ad4e 0, #eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c12e2a));background-image:linear-gradient(to bottom, #d9534f 0, #c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x;background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:-o-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #428bca), to(#357ebd));background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x;background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-o-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#f8f8f8));background-image:linear-gradient(to bottom, #fff 0, #f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);background-image:-o-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dbdbdb), to(#e2e2e2));background-image:linear-gradient(to bottom, #dbdbdb 0, #e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-o-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #3c3c3c), to(#222));background-image:linear-gradient(to bottom, #3c3c3c 0, #222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #080808 0, #0f0f0f 100%);background-image:-o-linear-gradient(top, #080808 0, #0f0f0f 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #080808), to(#0f0f0f));background-image:linear-gradient(to bottom, #080808 0, #0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:-o-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #428bca), to(#357ebd));background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#c8e5bc));background-image:linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:-o-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9edf7), to(#b9def0));background-image:linear-gradient(to bottom, #d9edf7 0, #b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#f8efc0));background-image:linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-o-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#e7c3c3));background-image:linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #ebebeb), to(#f5f5f5));background-image:linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top, #428bca 0, #3071a9 100%);background-image:-o-linear-gradient(top, #428bca 0, #3071a9 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #428bca), to(#3071a9));background-image:linear-gradient(to bottom, #428bca 0, #3071a9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#449d44));background-image:linear-gradient(to bottom, #5cb85c 0, #449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:-o-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5bc0de), to(#31b0d5));background-image:linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#ec971f));background-image:linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c9302c));background-image:linear-gradient(to bottom, #d9534f 0, #c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top, #428bca 0, #3278b3 100%);background-image:-o-linear-gradient(top, #428bca 0, #3278b3 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #428bca), to(#3278b3));background-image:linear-gradient(to bottom, #428bca 0, #3278b3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);background-repeat:repeat-x;border-color:#3278b3}.list-group-item.active .badge,.list-group-item.active:hover .badge,.list-group-item.active:focus .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:-o-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #428bca), to(#357ebd));background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#d0e9c6));background-image:linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:-o-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9edf7), to(#c4e3f3));background-image:linear-gradient(to bottom, #d9edf7 0, #c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#faf2cc));background-image:linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-o-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#ebcccc));background-image:linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #e8e8e8), to(#f5f5f5));background-image:linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} \ No newline at end of file diff --git a/docs/css/bootstrap.min.css b/docs/css/bootstrap.min.css new file mode 100644 index 0000000000..633f7473d1 --- /dev/null +++ b/docs/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/) + *//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;-webkit-box-shadow:none !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover,a.text-primary:focus{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover,a.bg-primary:focus{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:34px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.33}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;background-image:none;border-color:#adadad}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#3071a9;border-color:#193c5a}.btn-primary:hover{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#3071a9;background-image:none;border-color:#285e8e}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#fff;background-color:#285e8e;border-color:#193c5a}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;background-image:none;border-color:#398439}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#fff;background-color:#398439;border-color:#255625}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;background-image:none;border-color:#269abc}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;background-image:none;border-color:#d58512}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;background-image:none;border-color:#ac2925}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#428bca;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height, visibility;-o-transition-property:height, visibility;transition-property:height, visibility;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#777}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#777}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-link{color:#777}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#777}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#428bca;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.33}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.panel-body:before,.panel-body:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.panel-body:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}} \ No newline at end of file diff --git a/docs/css/doctum.css b/docs/css/doctum.css new file mode 100644 index 0000000000..77796f877d --- /dev/null +++ b/docs/css/doctum.css @@ -0,0 +1,508 @@ +html, +body, +#content { + height: 100%; +} + +/* Site menu */ + +#site-nav.navbar-default { + margin: 0; + border-radius: 0; + border-bottom: 1px solid #ccc; + background-color: #edf3fe; + background-image: none; +} + +#site-nav.navbar-default .navbar-brand, +#site-nav.navbar-default .navbar-nav > li > a { + color: #000; +} + +#site-nav.navbar-default .navbar-nav > li > a:hover { + text-decoration: underline; +} + +#navbar-elements { + float: right; +} + +@media (max-width: 768px) { + #navbar-elements { + float: none !important; + } +} + +/* Namespace breadcrumbs */ + +.namespace-breadcrumbs .breadcrumb { + margin: 0 0 12px; + border-radius: 0 0 4px 4px; + padding-left: 35px; +} + +.namespace-breadcrumbs .breadcrumb > li + li:before { + content: ""; +} +.namespace-breadcrumbs .breadcrumb > .backslash { + color: #ccc; +} + +/* Site columns */ + +#right-column { + margin-left: 20%; +} + +#page-content { + padding: 0 30px; +} + +#left-column { + width: 20%; + position: fixed; + height: 100%; + border-right: 1px solid #ccc; + line-height: 18px; + font-size: 13px; + display: flex; + flex-flow: column; +} + +@media (max-width: 991px) { + #left-column { + display: none; + } + #right-column { + width: 100%; + margin-left: 0; + } +} + +/* API Tree */ + +#api-tree { + background: linear-gradient(to bottom, #fff, #fff 50%, #edf3fe 50%, #edf3fe); + background-size: 100% 56px; + overflow: auto; + height: 100%; + background-attachment: local; +} + +#api-tree ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +#api-tree ul li { + padding: 0; + margin: 0; +} + +/* Prevents the menu from jittering on lad */ +#api-tree .icon-play { + width: 26px; +} + +#api-tree ul li .hd { + padding: 5px; +} + +#api-tree li .hd:nth-child(even) { + background-color: #edf3fe; +} + +#api-tree ul li.opened > .hd span { + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -o-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} + +#api-tree .bd { + display: none; +} + +#api-tree li.opened > .bd { + display: block; +} + +#api-tree li .hd:hover { + background-color: #eee; +} + +#api-tree li.active > .hd { + background-color: #3875d7; +} + +#api-tree li.active > .hd a { + color: #eee; + font-weight: bold; +} + +#api-tree a { + color: #222; +} + +#api-tree div.leaf a { + margin-left: 20px; +} + +#api-tree .hd span { + padding: 0px 8px; + font-size: 15px; + line-height: 85%; +} + +/* Control panel, search form, version drop-down */ + +#control-panel { + background: #e8e8e8; + border-bottom: 1px solid #666; + padding: 4px; +} + +#control-panel form, #control-panel > .search-bar { + margin: 4px 4px 5px 4px; +} + +#control-panel > .search-bar > .progress { + height: 5px; + margin-bottom: 0px; +} + +#control-panel > .search-bar > .progress > .progress-bar { + background: #30a0e0; +} + +/* Source: https://stackoverflow.com/a/38229228/5155484 */ + +.progress-bar.indeterminate { + position: relative; + animation: progress-indeterminate 3s linear infinite; +} + +@keyframes progress-indeterminate { + from { left: -25%; width: 25%; } + to { left: 100%; width: 25%;} +} + +#search-form { + position: relative; +} + +#search-form input { + width: 100%; + padding-left: 28px; +} + +#search-form span.icon-search { + position: absolute; + left: 5px; + top: 8px; + font-size: 20px; + z-index: 2; +} + +/** Typeahead */ + +.auto-complete-results { + width: 100%; + z-index: 1; +} + +.auto-complete-dropdown-menu { + overflow: auto; + max-height: 260px; + margin-top: 9px; + background-color: #fff; + border: 1px solid #ccc; + border-radius: 8px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + padding: 8px; +} + +.auto-complete-result { + padding: 8px; + border-bottom: 1px solid #ccc; + font-size: 1.1em; +} + +.auto-complete-selected, .auto-complete-result:hover { + background-color: #3875d7; + color: #fff; +} + +.auto-complete-selected > mark.auto-complete-highlight, .auto-complete-result:hover > mark.auto-complete-highlight { + color: #fff; +} + +.auto-complete-highlight { + padding: 0px; + font-weight: bold; + background-color: transparent; +} + +/** General typography **/ + +.navbar { + border-bottom: 0; +} + +.page-header { + margin: 0 0 20px; +} + +abbr[title], +abbr[data-original-title], +abbr { + border-bottom: none; + cursor: pointer; +} + +a abbr { + cursor: pointer; +} + +.method-description table, +.description table { + border: solid 1px #ccc; + padding: 1em; + margin: 1em; +} + +.method-description td, +.method-description th, +.description td, +.description th { + padding: 0.75em 1.25em; +} + +.method-description tbody tr:nth-child(even), +.description tbody tr:nth-child(even) { + background: #edf3fe; +} + +.method-description tbody tr:nth-child(odd), +.description tbody tr:nth-child(odd) { + background: #fff; +} + +.method-description thead tr, +.description thead tr { + background: #edf3fe; +} + +/** General Doctum styling **/ + +.underlined > .row { + padding: 8px 0; + border-bottom: 1px solid #ddd; +} + +#footer { + text-align: right; + margin: 30px; + font-size: 11px; +} + +.description { + margin: 10px 0; + padding: 10px; + background-color: #efefef; +} + +.description p { + padding: 0; + margin: 8px 0; +} + +.method-description { + margin: 0 0 24px 0; +} + +.details { + padding-left: 30px; +} + +#method-details .method-item { + margin-bottom: 30px; +} + +.method-item h3, +.method-item h3 code { + background-color: #eee; +} + +.method-item h3 { + padding: 4px; + margin-bottom: 20px; + font-size: 20px; +} + +.location { + font-size: 11px; + float: right; + font-style: italic; +} + +.namespace-list a { + padding: 3px 8px; + margin: 0 5px 5px 0; + border: 1px solid #ddd; + background-color: #f9f9f9; + display: inline-block; + border-radius: 4px; +} + +.no-description { + color: #ccc; + font-size: 90%; +} + +.type { + overflow-wrap: break-word; +} + +/* Namespaces page */ + +.namespaces { + clear: both; +} + +.namespaces .namespace-container { + float: left; + margin: 0 14px 14px 0; + min-width: 30%; +} + +.namespaces h2 { + margin: 0 0 20px 0; +} + +.namespace-container > h2 { + background-color: #edf3fe; + padding: 4px 4px 4px 8px; + font-size: 25px; + margin: 20px 0; +} + +@media (max-width: 991px) { + .namespaces .namespace-container { + margin-right: 0; + width: 100%; + } +} + +/** Code and pre tags **/ + +tt, +code, +pre { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; +} + +code { + padding: 0; + padding-top: 0.2em; + padding-bottom: 0.2em; + margin: 0; + font-size: 85%; + background-color: rgba(0, 0, 0, 0.04); + border-radius: 3px; + color: #333; +} + +pre { + padding: 16px; + overflow: auto; + font-size: 85%; + line-height: 1.45; + background-color: #f7f7f7; + border-radius: 3px; +} + +pre.examples { + padding: 1rem; +} + +#page-content > h2 { + background-color: #edf3fe; + padding: 4px 4px 4px 8px; + font-size: 25px; + margin: 20px 0; +} + + +/** Doc index **/ + +dt { + font-weight: normal; +} + +dd { + margin-left: 30px; + line-height: 1.5em; +} + +#doc-index h2 { + font-weight: bold; + margin: 30px 0; +} + +#doc-index .pagination { + margin: 0; +} + +/* Search page */ + +.search-results { + list-style-type: none; + padding: 0; + margin: 0; +} + +.search-results li { + list-style-type: none; + margin: 0; + padding: 14px 0; + border-bottom: 1px solid #ccc; +} + +.search-results > li > h2 { + background: none; + margin: 0; + padding: 0; + font-size: 18px; +} + +.search-results > li > h2 > a { + float: left; + display: block; + margin: 0 0 4px 0; +} + +.search-results .search-type { + float: right; + margin: 0 0 4px 0; +} + +.search-results .search-from { + margin: 0 0 12px 0; + font-size: 12px; + color: #999; +} + +.search-results .search-from a { + font-style: italic; +} + +.search-results .search-description { + margin: 8px 0 0 30px; +} + +.search-description { + white-space: pre; +} diff --git a/docs/doc-index.html b/docs/doc-index.html new file mode 100644 index 0000000000..e9d88992bc --- /dev/null +++ b/docs/doc-index.html @@ -0,0 +1,1187 @@ + + + + + + Index | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + + + +

A

+
+Redis::acl() — Method in class Redis
+
+Redis::append() — Method in class Redis
+

Append data to a Redis STRING key.

+Redis::auth() — Method in class Redis
+

Authenticate a Redis connection after its been established.

+RedisCluster::acl() — Method in class RedisCluster
+
+RedisCluster::append() — Method in class RedisCluster
+

B

+
+Redis::bgSave() — Method in class Redis
+

Execute a save of the Redis database in the background.

+Redis::bgrewriteaof() — Method in class Redis
+

Asynchronously rewrite Redis' append-only file

+Redis::bitcount() — Method in class Redis
+

Count the number of set bits in a Redis string.

+Redis::bitop() — Method in class Redis
+
+Redis::bitpos() — Method in class Redis
+

Return the position of the first bit set to 0 or 1 in a string.

+Redis::blPop() — Method in class Redis
+

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified +timeout. This method may be called in two distinct ways, of which examples are provided below.

+Redis::brPop() — Method in class Redis
+

Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

+Redis::brpoplpush() — Method in class Redis
+

Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list, +optionally blocking up to a specified timeout.

+Redis::bzPopMax() — Method in class Redis
+

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified +timeout if no elements are available.

+Redis::bzPopMin() — Method in class Redis
+

POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout +if no elements are available

+Redis::bzmpop() — Method in class Redis
+

POP one or more elements from one or more sorted sets, blocking up to a specified amount of time +when no elements are available.

+Redis::blmpop() — Method in class Redis
+

Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when +no elements are available.

+RedisArray::bgsave() — Method in class RedisArray
+
+RedisCluster::bgrewriteaof() — Method in class RedisCluster
+
+RedisCluster::bgsave() — Method in class RedisCluster
+
+RedisCluster::bitcount() — Method in class RedisCluster
+
+RedisCluster::bitop() — Method in class RedisCluster
+
+RedisCluster::bitpos() — Method in class RedisCluster
+

Return the position of the first bit set to 0 or 1 in a string.

+RedisCluster::blpop() — Method in class RedisCluster
+

See Redis::blpop()

+RedisCluster::brpop() — Method in class RedisCluster
+

See Redis::brpop()

+RedisCluster::brpoplpush() — Method in class RedisCluster
+

See Redis::brpoplpush()

+RedisCluster::bzpopmax() — Method in class RedisCluster
+
+RedisCluster::bzpopmin() — Method in class RedisCluster
+
+RedisCluster::bzmpop() — Method in class RedisCluster
+
+RedisCluster::blmpop() — Method in class RedisCluster
+

C

+
+Redis::clearLastError() — Method in class Redis
+

Reset any last error on the connection to NULL

+Redis::client() — Method in class Redis
+
+Redis::close() — Method in class Redis
+
+Redis::command() — Method in class Redis
+
+Redis::config() — Method in class Redis
+

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends +on the $operation qualifier.

+Redis::connect() — Method in class Redis
+
+Redis::copy() — Method in class Redis
+

Make a copy of a redis key.

+RedisCluster::clearlasterror() — Method in class RedisCluster
+
+RedisCluster::client() — Method in class RedisCluster
+
+RedisCluster::close() — Method in class RedisCluster
+
+RedisCluster::cluster() — Method in class RedisCluster
+
+RedisCluster::command() — Method in class RedisCluster
+
+RedisCluster::config() — Method in class RedisCluster
+
+RedisSentinel::ckquorum() — Method in class RedisSentinel
+

D

+
+Redis::dbSize() — Method in class Redis
+

Return the number of keys in the currently selected Redis database.

+Redis::debug() — Method in class Redis
+
+Redis::decr() — Method in class Redis
+

Decrement a Redis integer by 1 or a provided value.

+Redis::decrBy() — Method in class Redis
+

Decrement a redis integer by a value

+Redis::del() — Method in class Redis
+

Delete one or more keys from Redis.

+Redis::delete() — Method in class Redis
+
+Redis::discard() — Method in class Redis
+

Discard a transaction currently in progress.

+Redis::dump() — Method in class Redis
+

Dump Redis' internal binary representation of a key.

+RedisArray::del() — Method in class RedisArray
+
+RedisArray::discard() — Method in class RedisArray
+
+RedisCluster::dbsize() — Method in class RedisCluster
+
+RedisCluster::decr() — Method in class RedisCluster
+
+RedisCluster::decrby() — Method in class RedisCluster
+
+RedisCluster::decrbyfloat() — Method in class RedisCluster
+
+RedisCluster::del() — Method in class RedisCluster
+
+RedisCluster::discard() — Method in class RedisCluster
+
+RedisCluster::dump() — Method in class RedisCluster
+

E

+
+Redis::echo() — Method in class Redis
+

Have Redis repeat back an arbitrary string to the client.

+Redis::eval() — Method in class Redis
+

Execute a LUA script on the redis server.

+Redis::eval_ro() — Method in class Redis
+

This is simply the read-only variant of eval, meaning the underlying script +may not modify data in redis.

+Redis::evalsha() — Method in class Redis
+

Execute a LUA script on the server but instead of sending the script, send +the SHA1 hash of the script.

+Redis::evalsha_ro() — Method in class Redis
+

This is simply the read-only variant of evalsha, meaning the underlying script +may not modify data in redis.

+Redis::exec() — Method in class Redis
+

Execute either a MULTI or PIPELINE block and return the array of replies.

+Redis::exists() — Method in class Redis
+

Test if one or more keys exist.

+Redis::expire() — Method in class Redis
+

Sets an expiration in seconds on the key in question. If connected to +redis-server >= 7.0.0 you may send an additional "mode" argument which +modifies how the command will execute.

+Redis::expireAt() — Method in class Redis
+

Set a key's expiration to a specific Unix timestamp in seconds. If +connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

+Redis::expiretime() — Method in class Redis
+

Get the expiration of a given key as a unix timestamp

+RedisArray::exec() — Method in class RedisArray
+
+RedisCluster::echo() — Method in class RedisCluster
+
+RedisCluster::eval() — Method in class RedisCluster
+
+RedisCluster::eval_ro() — Method in class RedisCluster
+
+RedisCluster::evalsha() — Method in class RedisCluster
+
+RedisCluster::evalsha_ro() — Method in class RedisCluster
+
+RedisCluster::exec() — Method in class RedisCluster
+
+RedisCluster::exists() — Method in class RedisCluster
+
+RedisCluster::expire() — Method in class RedisCluster
+
+RedisCluster::expireat() — Method in class RedisCluster
+
+RedisCluster::expiretime() — Method in class RedisCluster
+

F

+
+Redis::failover() — Method in class Redis
+
+Redis::flushAll() — Method in class Redis
+

Deletes every key in all Redis databases

+Redis::flushDB() — Method in class Redis
+

Deletes all the keys of the currently selected database.

+RedisArray::flushall() — Method in class RedisArray
+
+RedisArray::flushdb() — Method in class RedisArray
+
+RedisCluster::flushall() — Method in class RedisCluster
+
+RedisCluster::flushdb() — Method in class RedisCluster
+
+RedisSentinel::failover() — Method in class RedisSentinel
+
+RedisSentinel::flushconfig() — Method in class RedisSentinel
+

G

+
+Redis::geoadd() — Method in class Redis
+
+Redis::geodist() — Method in class Redis
+
+Redis::geohash() — Method in class Redis
+
+Redis::geopos() — Method in class Redis
+
+Redis::georadius() — Method in class Redis
+
+Redis::georadius_ro() — Method in class Redis
+
+Redis::georadiusbymember() — Method in class Redis
+
+Redis::georadiusbymember_ro() — Method in class Redis
+
+Redis::geosearch() — Method in class Redis
+
+Redis::geosearchstore() — Method in class Redis
+
+Redis::get() — Method in class Redis
+
+Redis::getAuth() — Method in class Redis
+

Get the authentication information on the connection, if any.

+Redis::getBit() — Method in class Redis
+
+Redis::getEx() — Method in class Redis
+
+Redis::getDBNum() — Method in class Redis
+
+Redis::getDel() — Method in class Redis
+
+Redis::getHost() — Method in class Redis
+

Return the host or Unix socket we are connected to.

+Redis::getLastError() — Method in class Redis
+

Get the last error returned to us from Redis, if any.

+Redis::getMode() — Method in class Redis
+

Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

+Redis::getOption() — Method in class Redis
+

Retrieve the value of a configuration setting as set by Redis::setOption()

+Redis::getPersistentID() — Method in class Redis
+

Get the persistent connection ID, if there is one.

+Redis::getPort() — Method in class Redis
+

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

+Redis::getRange() — Method in class Redis
+

Retrieve a substring of a string by index.

+Redis::getReadTimeout() — Method in class Redis
+

Get the currently set read timeout on the connection.

+Redis::getset() — Method in class Redis
+

Sets a key and returns any previously set value, if the key already existed.

+Redis::getTimeout() — Method in class Redis
+

Retrieve any set connection timeout

+Redis::getTransferredBytes() — Method in class Redis
+
+RedisArray::getOption() — Method in class RedisArray
+
+RedisCluster::geoadd() — Method in class RedisCluster
+
+RedisCluster::geodist() — Method in class RedisCluster
+
+RedisCluster::geohash() — Method in class RedisCluster
+
+RedisCluster::geopos() — Method in class RedisCluster
+
+RedisCluster::georadius() — Method in class RedisCluster
+
+RedisCluster::georadius_ro() — Method in class RedisCluster
+
+RedisCluster::georadiusbymember() — Method in class RedisCluster
+
+RedisCluster::georadiusbymember_ro() — Method in class RedisCluster
+
+RedisCluster::get() — Method in class RedisCluster
+
+RedisCluster::getbit() — Method in class RedisCluster
+
+RedisCluster::getlasterror() — Method in class RedisCluster
+
+RedisCluster::getmode() — Method in class RedisCluster
+
+RedisCluster::getoption() — Method in class RedisCluster
+
+RedisCluster::getrange() — Method in class RedisCluster
+
+RedisCluster::getset() — Method in class RedisCluster
+
+RedisCluster::gettransferredbytes() — Method in class RedisCluster
+
+RedisSentinel::getMasterAddrByName() — Method in class RedisSentinel
+

H

+
+Redis::hDel() — Method in class Redis
+

Remove one or more fields from a hash.

+Redis::hExists() — Method in class Redis
+

Checks whether a field exists in a hash.

+Redis::hGet() — Method in class Redis
+
+Redis::hGetAll() — Method in class Redis
+

Read every field and value from a hash.

+Redis::hIncrBy() — Method in class Redis
+

Increment a hash field's value by an integer

+Redis::hIncrByFloat() — Method in class Redis
+

Increment a hash field by a floating point value

+Redis::hKeys() — Method in class Redis
+

Retrieve all of the fields of a hash.

+Redis::hLen() — Method in class Redis
+

Get the number of fields in a hash.

+Redis::hMget() — Method in class Redis
+

Get one or more fields from a hash.

+Redis::hMset() — Method in class Redis
+

Add or update one or more hash fields and values

+Redis::hRandField() — Method in class Redis
+

Get one or more random field from a hash.

+Redis::hSet() — Method in class Redis
+
+Redis::hSetNx() — Method in class Redis
+

Set a hash field and value, but only if that field does not exist

+Redis::hStrLen() — Method in class Redis
+

Get the string length of a hash field

+Redis::hVals() — Method in class Redis
+

Get all of the values from a hash.

+Redis::hscan() — Method in class Redis
+

Iterate over the fields and values of a hash in an incremental fashion.

+RedisArray::hscan() — Method in class RedisArray
+
+RedisCluster::hdel() — Method in class RedisCluster
+
+RedisCluster::hexists() — Method in class RedisCluster
+
+RedisCluster::hget() — Method in class RedisCluster
+
+RedisCluster::hgetall() — Method in class RedisCluster
+
+RedisCluster::hincrby() — Method in class RedisCluster
+
+RedisCluster::hincrbyfloat() — Method in class RedisCluster
+
+RedisCluster::hkeys() — Method in class RedisCluster
+
+RedisCluster::hlen() — Method in class RedisCluster
+
+RedisCluster::hmget() — Method in class RedisCluster
+
+RedisCluster::hmset() — Method in class RedisCluster
+
+RedisCluster::hscan() — Method in class RedisCluster
+
+RedisCluster::hset() — Method in class RedisCluster
+
+RedisCluster::hsetnx() — Method in class RedisCluster
+
+RedisCluster::hstrlen() — Method in class RedisCluster
+
+RedisCluster::hvals() — Method in class RedisCluster
+

I

+
+Redis::incr() — Method in class Redis
+

Increment a key's value, optionally by a specifc amount.

+Redis::incrBy() — Method in class Redis
+

Increment a key by a specific integer value

+Redis::incrByFloat() — Method in class Redis
+

Increment a numeric key by a floating point value.

+Redis::info() — Method in class Redis
+

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

+Redis::isConnected() — Method in class Redis
+

Check if we are currently connected to a Redis instance.

+RedisArray::info() — Method in class RedisArray
+
+RedisCluster::incr() — Method in class RedisCluster
+
+RedisCluster::incrby() — Method in class RedisCluster
+
+RedisCluster::incrbyfloat() — Method in class RedisCluster
+
+RedisCluster::info() — Method in class RedisCluster
+

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

K

+
+Redis::keys() — Method in class Redis
+
+RedisArray::keys() — Method in class RedisArray
+
+RedisCluster::keys() — Method in class RedisCluster
+

L

+
+Redis::lmpop() — Method in class Redis
+

Pop one or more elements off of one or more Redis LISTs.

+Redis::lcs() — Method in class Redis
+

Get the longest common subsequence between two string keys.

+Redis::lInsert() — Method in class Redis
+
+Redis::lLen() — Method in class Redis
+
+Redis::lMove() — Method in class Redis
+
+Redis::lPop() — Method in class Redis
+
+Redis::lPos() — Method in class Redis
+
+Redis::lPush() — Method in class Redis
+
+Redis::lPushx() — Method in class Redis
+
+Redis::lSet() — Method in class Redis
+
+Redis::lastSave() — Method in class Redis
+
+Redis::lindex() — Method in class Redis
+
+Redis::lrange() — Method in class Redis
+
+Redis::lrem() — Method in class Redis
+
+Redis::ltrim() — Method in class Redis
+
+RedisCluster::lmpop() — Method in class RedisCluster
+
+RedisCluster::lcs() — Method in class RedisCluster
+
+RedisCluster::lastsave() — Method in class RedisCluster
+
+RedisCluster::lget() — Method in class RedisCluster
+
+RedisCluster::lindex() — Method in class RedisCluster
+
+RedisCluster::linsert() — Method in class RedisCluster
+
+RedisCluster::llen() — Method in class RedisCluster
+
+RedisCluster::lpop() — Method in class RedisCluster
+
+RedisCluster::lpush() — Method in class RedisCluster
+
+RedisCluster::lpushx() — Method in class RedisCluster
+
+RedisCluster::lrange() — Method in class RedisCluster
+
+RedisCluster::lrem() — Method in class RedisCluster
+
+RedisCluster::lset() — Method in class RedisCluster
+
+RedisCluster::ltrim() — Method in class RedisCluster
+

M

+
+Redis::mget() — Method in class Redis
+
+Redis::migrate() — Method in class Redis
+
+Redis::move() — Method in class Redis
+
+Redis::mset() — Method in class Redis
+
+Redis::msetnx() — Method in class Redis
+
+Redis::multi() — Method in class Redis
+
+RedisArray::mget() — Method in class RedisArray
+
+RedisArray::mset() — Method in class RedisArray
+
+RedisArray::multi() — Method in class RedisArray
+
+RedisCluster::mget() — Method in class RedisCluster
+
+RedisCluster::mset() — Method in class RedisCluster
+
+RedisCluster::msetnx() — Method in class RedisCluster
+
+RedisCluster::multi() — Method in class RedisCluster
+
+RedisSentinel::master() — Method in class RedisSentinel
+
+RedisSentinel::masters() — Method in class RedisSentinel
+
+RedisSentinel::myid() — Method in class RedisSentinel
+

O

+
+Redis::object() — Method in class Redis
+
+Redis::open() — Method in class Redis
+
+RedisCluster::object() — Method in class RedisCluster
+

P

+
+Redis::pexpiretime() — Method in class Redis
+

Get the expriation timestamp of a given Redis key but in milliseconds.

+Redis::pconnect() — Method in class Redis
+
+Redis::persist() — Method in class Redis
+
+Redis::pexpire() — Method in class Redis
+

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 +you can pass an optional mode argument that modifies how the command will execute.

+Redis::pexpireAt() — Method in class Redis
+

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to +Redis >= 7.0.0 you can pass an optional 'mode' argument.

+Redis::pfadd() — Method in class Redis
+

Add one or more elements to a Redis HyperLogLog key

+Redis::pfcount() — Method in class Redis
+

Retrieve the cardinality of a Redis HyperLogLog key.

+Redis::pfmerge() — Method in class Redis
+

Merge one or more source HyperLogLog sets into a destination set.

+Redis::ping() — Method in class Redis
+

PING the redis server with an optional string argument.

+Redis::pipeline() — Method in class Redis
+

Enter into pipeline mode.

+Redis::popen() — Method in class Redis
+
+Redis::psetex() — Method in class Redis
+
+Redis::psubscribe() — Method in class Redis
+

Subscribe to one or more glob-style patterns

+Redis::pttl() — Method in class Redis
+

Get a keys time to live in milliseconds.

+Redis::publish() — Method in class Redis
+

Publish a message to a pubsub channel

+Redis::pubsub() — Method in class Redis
+
+Redis::punsubscribe() — Method in class Redis
+

Unsubscribe from one or more channels by pattern

+RedisArray::ping() — Method in class RedisArray
+
+RedisCluster::pexpiretime() — Method in class RedisCluster
+
+RedisCluster::persist() — Method in class RedisCluster
+
+RedisCluster::pexpire() — Method in class RedisCluster
+
+RedisCluster::pexpireat() — Method in class RedisCluster
+
+RedisCluster::pfadd() — Method in class RedisCluster
+
+RedisCluster::pfcount() — Method in class RedisCluster
+
+RedisCluster::pfmerge() — Method in class RedisCluster
+
+RedisCluster::ping() — Method in class RedisCluster
+

PING an instance in the redis cluster.

+RedisCluster::psetex() — Method in class RedisCluster
+
+RedisCluster::psubscribe() — Method in class RedisCluster
+
+RedisCluster::pttl() — Method in class RedisCluster
+
+RedisCluster::publish() — Method in class RedisCluster
+
+RedisCluster::pubsub() — Method in class RedisCluster
+
+RedisCluster::punsubscribe() — Method in class RedisCluster
+
+RedisSentinel::ping() — Method in class RedisSentinel
+

R

+
Redis
+
+Redis::rPush() — Method in class Redis
+
+Redis::rPushx() — Method in class Redis
+
+Redis::rPop() — Method in class Redis
+

Pop one or more elements from the end of a list.

+Redis::randomKey() — Method in class Redis
+

Return a random key from the current database

+Redis::rawcommand() — Method in class Redis
+

Execute any arbitrary Redis command by name.

+Redis::rename() — Method in class Redis
+

Unconditionally rename a key from $old_name to $new_name

+Redis::renameNx() — Method in class Redis
+

Renames $key_src to $key_dst but only if newkey does not exist.

+Redis::reset() — Method in class Redis
+

Reset the state of the connection.

+Redis::restore() — Method in class Redis
+

Restore a key by the binary payload generated by the DUMP command.

+Redis::role() — Method in class Redis
+

Query whether the connected instance is a primary or replica

+Redis::rpoplpush() — Method in class Redis
+

Atomically pop an element off the end of a Redis LIST and push it to the beginning of +another.

+Redis::replicaof() — Method in class Redis
+

Used to turn a Redis instance into a replica of another, or to remove +replica status promoting the instance to a primary.

RedisArray
+
RedisCluster
+
+RedisCluster::randomkey() — Method in class RedisCluster
+
+RedisCluster::rawcommand() — Method in class RedisCluster
+
+RedisCluster::rename() — Method in class RedisCluster
+
+RedisCluster::renamenx() — Method in class RedisCluster
+
+RedisCluster::restore() — Method in class RedisCluster
+
+RedisCluster::role() — Method in class RedisCluster
+
+RedisCluster::rpop() — Method in class RedisCluster
+
+RedisCluster::rpoplpush() — Method in class RedisCluster
+
+RedisCluster::rpush() — Method in class RedisCluster
+
+RedisCluster::rpushx() — Method in class RedisCluster
+
RedisClusterException
+
RedisException
+
RedisSentinel
+
+RedisSentinel::reset() — Method in class RedisSentinel
+

S

+
+Redis::sAdd() — Method in class Redis
+

Add one or more values to a Redis SET key.

+Redis::sAddArray() — Method in class Redis
+

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but +instead of being variadic, takes a single array of values.

+Redis::sDiff() — Method in class Redis
+

Given one or more Redis SETS, this command returns all of the members from the first +set that are not in any subsequent set.

+Redis::sDiffStore() — Method in class Redis
+

This method performs the same operation as SDIFF except it stores the resulting diff +values in a specified destination key.

+Redis::sInter() — Method in class Redis
+

Given one or more Redis SET keys, this command will return all of the elements that are +in every one.

+Redis::sintercard() — Method in class Redis
+

Compute the intersection of one or more sets and return the cardinality of the result.

+Redis::sInterStore() — Method in class Redis
+

Perform the intersection of one or more Redis SETs, storing the result in a destination +key, rather than returning them.

+Redis::sMembers() — Method in class Redis
+

Retrieve every member from a set key.

+Redis::sMisMember() — Method in class Redis
+

Check if one or more values are members of a set.

+Redis::sMove() — Method in class Redis
+

Pop a member from one set and push it onto another. This command will create the +destination set if it does not currently exist.

+Redis::sPop() — Method in class Redis
+

Remove one or more elements from a set.

+Redis::sRandMember() — Method in class Redis
+

Retrieve one or more random members of a set.

+Redis::sUnion() — Method in class Redis
+

Returns the union of one or more Redis SET keys.

+Redis::sUnionStore() — Method in class Redis
+

Perform a union of one or more Redis SET keys and store the result in a new set

+Redis::save() — Method in class Redis
+

Persist the Redis database to disk. This command will block the server until the save is +completed. For a nonblocking alternative, see Redis::bgsave().

+Redis::scan() — Method in class Redis
+

Incrementally scan the Redis keyspace, with optional pattern and type matching.

+Redis::scard() — Method in class Redis
+

Retrieve the number of members in a Redis set.

+Redis::script() — Method in class Redis
+

An administrative command used to interact with LUA scripts stored on the server.

+Redis::select() — Method in class Redis
+

Select a specific Redis database.

+Redis::set() — Method in class Redis
+

Create or set a Redis STRING key to a value.

+Redis::setBit() — Method in class Redis
+

Set a specific bit in a Redis string to zero or one

+Redis::setRange() — Method in class Redis
+

Update or append to a Redis string at a specific starting index

+Redis::setOption() — Method in class Redis
+

Set a configurable option on the Redis object.

+Redis::setex() — Method in class Redis
+

Set a Redis STRING key with a specific expiration in seconds.

+Redis::setnx() — Method in class Redis
+

Set a key to a value, but only if that key does not already exist.

+Redis::sismember() — Method in class Redis
+

Check whether a given value is the member of a Redis SET.

+Redis::slaveof() — Method in class Redis
+

Turn a redis instance into a replica of another or promote a replica +to a primary.

+Redis::slowlog() — Method in class Redis
+

Interact with Redis' slowlog functionality in various ways, depending +on the value of 'operation'.

+Redis::sort() — Method in class Redis
+

Sort the contents of a Redis key in various ways.

+Redis::sort_ro() — Method in class Redis
+

This is simply a read-only variant of the sort command

+Redis::sortAsc() — Method in class Redis
+
+Redis::sortAscAlpha() — Method in class Redis
+
+Redis::sortDesc() — Method in class Redis
+
+Redis::sortDescAlpha() — Method in class Redis
+
+Redis::srem() — Method in class Redis
+

Remove one or more values from a Redis SET key.

+Redis::sscan() — Method in class Redis
+

Scan the members of a redis SET key.

+Redis::strlen() — Method in class Redis
+

Retrieve the length of a Redis STRING key.

+Redis::subscribe() — Method in class Redis
+

Subscribe to one or more Redis pubsub channels.

+Redis::swapdb() — Method in class Redis
+

Atomically swap two Redis databases so that all of the keys in the source database will +now be in the destination database and vice-versa.

+RedisArray::save() — Method in class RedisArray
+
+RedisArray::scan() — Method in class RedisArray
+
+RedisArray::select() — Method in class RedisArray
+
+RedisArray::setOption() — Method in class RedisArray
+
+RedisArray::sscan() — Method in class RedisArray
+
+RedisCluster::sadd() — Method in class RedisCluster
+
+RedisCluster::saddarray() — Method in class RedisCluster
+
+RedisCluster::save() — Method in class RedisCluster
+
+RedisCluster::scan() — Method in class RedisCluster
+
+RedisCluster::scard() — Method in class RedisCluster
+
+RedisCluster::script() — Method in class RedisCluster
+
+RedisCluster::sdiff() — Method in class RedisCluster
+
+RedisCluster::sdiffstore() — Method in class RedisCluster
+
+RedisCluster::set() — Method in class RedisCluster
+
+RedisCluster::setbit() — Method in class RedisCluster
+
+RedisCluster::setex() — Method in class RedisCluster
+
+RedisCluster::setnx() — Method in class RedisCluster
+
+RedisCluster::setoption() — Method in class RedisCluster
+
+RedisCluster::setrange() — Method in class RedisCluster
+
+RedisCluster::sinter() — Method in class RedisCluster
+
+RedisCluster::sintercard() — Method in class RedisCluster
+
+RedisCluster::sinterstore() — Method in class RedisCluster
+
+RedisCluster::sismember() — Method in class RedisCluster
+
+RedisCluster::slowlog() — Method in class RedisCluster
+
+RedisCluster::smembers() — Method in class RedisCluster
+
+RedisCluster::smove() — Method in class RedisCluster
+
+RedisCluster::sort() — Method in class RedisCluster
+
+RedisCluster::sort_ro() — Method in class RedisCluster
+
+RedisCluster::spop() — Method in class RedisCluster
+
+RedisCluster::srandmember() — Method in class RedisCluster
+
+RedisCluster::srem() — Method in class RedisCluster
+
+RedisCluster::sscan() — Method in class RedisCluster
+
+RedisCluster::strlen() — Method in class RedisCluster
+
+RedisCluster::subscribe() — Method in class RedisCluster
+
+RedisCluster::sunion() — Method in class RedisCluster
+
+RedisCluster::sunionstore() — Method in class RedisCluster
+
+RedisSentinel::sentinels() — Method in class RedisSentinel
+
+RedisSentinel::slaves() — Method in class RedisSentinel
+

T

+
+Redis::touch() — Method in class Redis
+

Update one or more keys last modified metadata.

+Redis::time() — Method in class Redis
+

Retrieve the server time from the connected Redis instance.

+Redis::ttl() — Method in class Redis
+

Get the amount of time a Redis key has before it will expire, in seconds.

+Redis::type() — Method in class Redis
+

Get the type of a given Redis key.

+RedisCluster::touch() — Method in class RedisCluster
+
+RedisCluster::time() — Method in class RedisCluster
+
+RedisCluster::ttl() — Method in class RedisCluster
+
+RedisCluster::type() — Method in class RedisCluster
+

U

+
+Redis::unlink() — Method in class Redis
+

Delete one or more keys from the Redis database. Unlike this operation, the actual +deletion is asynchronous, meaning it is safe to delete large keys without fear of +Redis blocking for a long period of time.

+Redis::unsubscribe() — Method in class Redis
+

Unsubscribe from one or more subscribed channels.

+Redis::unwatch() — Method in class Redis
+

Remove any previously WATCH'ed keys in a transaction.

+RedisArray::unlink() — Method in class RedisArray
+
+RedisArray::unwatch() — Method in class RedisArray
+
+RedisCluster::unsubscribe() — Method in class RedisCluster
+
+RedisCluster::unlink() — Method in class RedisCluster
+
+RedisCluster::unwatch() — Method in class RedisCluster
+

W

+
+Redis::watch() — Method in class Redis
+
+Redis::wait() — Method in class Redis
+

Block the client up to the provided timeout until a certain number of replicas have confirmed +recieving them.

+RedisCluster::watch() — Method in class RedisCluster
+

X

+
+Redis::xack() — Method in class Redis
+
+Redis::xadd() — Method in class Redis
+

Append a message to a stream.

+Redis::xautoclaim() — Method in class Redis
+
+Redis::xclaim() — Method in class Redis
+
+Redis::xdel() — Method in class Redis
+

Remove one or more specific IDs from a stream.

+Redis::xgroup() — Method in class Redis
+
XGROUP
+Redis::xinfo() — Method in class Redis
+

Retrieve information about a stream key.

+Redis::xlen() — Method in class Redis
+

Get the number of messages in a Redis STREAM key.

+Redis::xpending() — Method in class Redis
+

Interact with stream messages that have been consumed by a consumer group but not yet +acknowledged with XACK.

+Redis::xrange() — Method in class Redis
+

Get a range of entries from a STREAM key.

+Redis::xread() — Method in class Redis
+

Consume one or more unconsumed elements in one or more streams.

+Redis::xreadgroup() — Method in class Redis
+

Read one or more messages using a consumer group.

+Redis::xrevrange() — Method in class Redis
+

Get a range of entries from a STREAM ke in reverse cronological order.

+Redis::xtrim() — Method in class Redis
+

Truncate a STREAM key in various ways.

+RedisCluster::xack() — Method in class RedisCluster
+
+RedisCluster::xadd() — Method in class RedisCluster
+
+RedisCluster::xclaim() — Method in class RedisCluster
+
+RedisCluster::xdel() — Method in class RedisCluster
+
+RedisCluster::xgroup() — Method in class RedisCluster
+
+RedisCluster::xinfo() — Method in class RedisCluster
+
+RedisCluster::xlen() — Method in class RedisCluster
+
+RedisCluster::xpending() — Method in class RedisCluster
+
+RedisCluster::xrange() — Method in class RedisCluster
+
+RedisCluster::xread() — Method in class RedisCluster
+
+RedisCluster::xreadgroup() — Method in class RedisCluster
+
+RedisCluster::xrevrange() — Method in class RedisCluster
+
+RedisCluster::xtrim() — Method in class RedisCluster
+

Z

+
+Redis::zmpop() — Method in class Redis
+

POP one or more of the highest or lowest scoring elements from one or more sorted sets.

+Redis::zAdd() — Method in class Redis
+

Add one or more elements and scores to a Redis sorted set.

+Redis::zCard() — Method in class Redis
+

Return the number of elements in a sorted set.

+Redis::zCount() — Method in class Redis
+

Count the number of members in a sorted set with scores inside a provided range.

+Redis::zIncrBy() — Method in class Redis
+

Create or increment the score of a member in a Redis sorted set

+Redis::zLexCount() — Method in class Redis
+

Count the number of elements in a sorted set whos members fall within the provided +lexographical range.

+Redis::zMscore() — Method in class Redis
+

Retrieve the score of one or more members in a sorted set.

+Redis::zPopMax() — Method in class Redis
+

Pop one or more of the highest scoring elements from a sorted set.

+Redis::zPopMin() — Method in class Redis
+

Pop one or more of the lowest scoring elements from a sorted set.

+Redis::zRange() — Method in class Redis
+

Retrieve a range of elements of a sorted set between a start and end point.

+Redis::zRangeByLex() — Method in class Redis
+

Retrieve a range of elements from a sorted set by legographical range.

+Redis::zRangeByScore() — Method in class Redis
+

Retrieve a range of members from a sorted set by their score.

+Redis::zrangestore() — Method in class Redis
+

This command is similar to ZRANGE except that instead of returning the values directly +it will store them in a destination key provided by the user

+Redis::zRandMember() — Method in class Redis
+

Retrieve one or more random members from a Redis sorted set.

+Redis::zRank() — Method in class Redis
+

Get the rank of a member of a sorted set, by score.

+Redis::zRem() — Method in class Redis
+

Remove one or more members from a Redis sorted set.

+Redis::zRemRangeByLex() — Method in class Redis
+

Remove zero or more elements from a Redis sorted set by legographical range.

+Redis::zRemRangeByRank() — Method in class Redis
+

Remove one or more members of a sorted set by their rank.

+Redis::zRemRangeByScore() — Method in class Redis
+

Remove one or more members of a sorted set by their score.

+Redis::zRevRange() — Method in class Redis
+

List the members of a Redis sorted set in reverse order

+Redis::zRevRangeByLex() — Method in class Redis
+

List members of a Redis sorted set within a legographical range, in reverse order.

+Redis::zRevRangeByScore() — Method in class Redis
+

List elements from a Redis sorted set by score, highest to lowest

+Redis::zRevRank() — Method in class Redis
+

Retrieve a member of a sorted set by reverse rank.

+Redis::zScore() — Method in class Redis
+

Get the score of a member of a sorted set.

+Redis::zdiff() — Method in class Redis
+

Given one or more sorted set key names, return every element that is in the first +set but not any of the others.

+Redis::zdiffstore() — Method in class Redis
+

Store the difference of one or more sorted sets in a destination sorted set.

+Redis::zinter() — Method in class Redis
+

Compute the intersection of one or more sorted sets and return the members

+Redis::zintercard() — Method in class Redis
+

Similar to ZINTER but instead of returning the intersected values, this command returns the +cardinality of the intersected set.

+Redis::zinterstore() — Method in class Redis
+

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

+Redis::zscan() — Method in class Redis
+

Scan the members of a sorted set incrementally, using a cursor

+Redis::zunion() — Method in class Redis
+

Retrieve the union of one or more sorted sets

+Redis::zunionstore() — Method in class Redis
+

Perform a union on one or more Redis sets and store the result in a destination sorted set.

+RedisArray::zscan() — Method in class RedisArray
+
+RedisCluster::zmpop() — Method in class RedisCluster
+
+RedisCluster::zadd() — Method in class RedisCluster
+
+RedisCluster::zcard() — Method in class RedisCluster
+
+RedisCluster::zcount() — Method in class RedisCluster
+
+RedisCluster::zincrby() — Method in class RedisCluster
+
+RedisCluster::zinterstore() — Method in class RedisCluster
+
+RedisCluster::zintercard() — Method in class RedisCluster
+
+RedisCluster::zlexcount() — Method in class RedisCluster
+
+RedisCluster::zpopmax() — Method in class RedisCluster
+
+RedisCluster::zpopmin() — Method in class RedisCluster
+
+RedisCluster::zrange() — Method in class RedisCluster
+
+RedisCluster::zrangestore() — Method in class RedisCluster
+
+RedisCluster::zrangebylex() — Method in class RedisCluster
+
+RedisCluster::zrangebyscore() — Method in class RedisCluster
+
+RedisCluster::zrank() — Method in class RedisCluster
+
+RedisCluster::zrem() — Method in class RedisCluster
+
+RedisCluster::zremrangebylex() — Method in class RedisCluster
+
+RedisCluster::zremrangebyrank() — Method in class RedisCluster
+
+RedisCluster::zremrangebyscore() — Method in class RedisCluster
+
+RedisCluster::zrevrange() — Method in class RedisCluster
+
+RedisCluster::zrevrangebylex() — Method in class RedisCluster
+
+RedisCluster::zrevrangebyscore() — Method in class RedisCluster
+
+RedisCluster::zrevrank() — Method in class RedisCluster
+
+RedisCluster::zscan() — Method in class RedisCluster
+
+RedisCluster::zscore() — Method in class RedisCluster
+
+RedisCluster::zunionstore() — Method in class RedisCluster
+

_

+
+Redis::__construct() — Method in class Redis
+

Create a new Redis instance. If passed sufficient information in the +options array it is also possible to connect to an instance at the same +time.

+Redis::__destruct() — Method in class Redis
+
+Redis::_compress() — Method in class Redis
+

Compress a value with the currently configured compressor as set with +Redis::setOption().

+Redis::_uncompress() — Method in class Redis
+

Uncompress the provided argument that has been compressed with the +currently configured compressor as set with Redis::setOption().

+Redis::_prefix() — Method in class Redis
+

Prefix the passed argument with the currently set key prefix as set +with Redis::setOption().

+Redis::_serialize() — Method in class Redis
+

Serialize the provided value with the currently set serializer as set +with Redis::setOption().

+Redis::_unserialize() — Method in class Redis
+

Unserialize the passed argument with the currently set serializer as set +with Redis::setOption().

+Redis::_pack() — Method in class Redis
+

Pack the provided value with the configured serializer and compressor +as set with Redis::setOption().

+Redis::_unpack() — Method in class Redis
+

Unpack the provided value with the configured compressor and serializer +as set with Redis::setOption().

+RedisArray::__call() — Method in class RedisArray
+
+RedisArray::__construct() — Method in class RedisArray
+
+RedisArray::_continuum() — Method in class RedisArray
+
+RedisArray::_distributor() — Method in class RedisArray
+
+RedisArray::_function() — Method in class RedisArray
+
+RedisArray::_hosts() — Method in class RedisArray
+
+RedisArray::_instance() — Method in class RedisArray
+
+RedisArray::_rehash() — Method in class RedisArray
+
+RedisArray::_target() — Method in class RedisArray
+
+RedisCluster::__construct() — Method in class RedisCluster
+
+RedisCluster::_compress() — Method in class RedisCluster
+
+RedisCluster::_uncompress() — Method in class RedisCluster
+
+RedisCluster::_serialize() — Method in class RedisCluster
+
+RedisCluster::_unserialize() — Method in class RedisCluster
+
+RedisCluster::_pack() — Method in class RedisCluster
+
+RedisCluster::_unpack() — Method in class RedisCluster
+
+RedisCluster::_prefix() — Method in class RedisCluster
+
+RedisCluster::_masters() — Method in class RedisCluster
+
+RedisCluster::_redir() — Method in class RedisCluster
+
+RedisSentinel::__construct() — Method in class RedisSentinel
+
+
+ + + diff --git a/docs/doctum-search.json b/docs/doctum-search.json new file mode 100644 index 0000000000..23b29afb7f --- /dev/null +++ b/docs/doctum-search.json @@ -0,0 +1 @@ +{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

"},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":null},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

Compress a value with the currently configured compressor as set with\nRedis::setOption().

"},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

Uncompress the provided argument that has been compressed with the\ncurrently configured compressor as set with Redis::setOption().

"},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

Pack the provided value with the configured serializer and compressor\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

Unpack the provided value with the configured compressor and serializer\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":null},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

Append data to a Redis STRING key.

"},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

Authenticate a Redis connection after its been established.

"},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

Execute a save of the Redis database in the background.

"},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

Asynchronously rewrite Redis' append-only file

"},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

Count the number of set bits in a Redis string.

"},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

"},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

"},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

"},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

"},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

"},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

"},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

POP one or more of the highest or lowest scoring elements from one or more sorted sets.

"},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

"},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

Pop one or more elements off of one or more Redis LISTs.

"},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

Reset any last error on the connection to NULL

"},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":null},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":null},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":null},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends\non the $operation qualifier.

"},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":null},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

Make a copy of a redis key.

"},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

Return the number of keys in the currently selected Redis database.

"},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":null},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

Decrement a Redis integer by 1 or a provided value.

"},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

Decrement a redis integer by a value

"},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

Delete one or more keys from Redis.

"},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

Discard a transaction currently in progress.

"},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

Dump Redis' internal binary representation of a key.

"},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

Have Redis repeat back an arbitrary string to the client.

"},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

Execute a LUA script on the redis server.

"},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

"},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

Execute either a MULTI or PIPELINE block and return the array of replies.

"},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

Test if one or more keys exist.

"},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

"},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

Set a key's expiration to a specific Unix timestamp in seconds. If\nconnected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

Get the expiration of a given key as a unix timestamp

"},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

Get the expriation timestamp of a given Redis key but in milliseconds.

"},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

Deletes every key in all Redis databases

"},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

Deletes all the keys of the currently selected database.

"},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":null},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":null},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":null},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":null},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":null},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":null},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":null},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":null},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":null},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":null},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":null},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

Get the authentication information on the connection, if any.

"},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":null},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":null},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":null},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":null},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

Return the host or Unix socket we are connected to.

"},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

Get the last error returned to us from Redis, if any.

"},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

"},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

Retrieve the value of a configuration setting as set by Redis::setOption()

"},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

Get the persistent connection ID, if there is one.

"},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

"},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

Retrieve a substring of a string by index.

"},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

Get the longest common subsequence between two string keys.

"},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

Get the currently set read timeout on the connection.

"},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

Sets a key and returns any previously set value, if the key already existed.

"},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

Retrieve any set connection timeout

"},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":null},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

Remove one or more fields from a hash.

"},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

Checks whether a field exists in a hash.

"},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

Read every field and value from a hash.

"},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

Increment a hash field's value by an integer

"},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

Increment a hash field by a floating point value

"},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

Retrieve all of the fields of a hash.

"},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

Get the number of fields in a hash.

"},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

Get one or more fields from a hash.

"},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

Add or update one or more hash fields and values

"},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

Get one or more random field from a hash.

"},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":null},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

Set a hash field and value, but only if that field does not exist

"},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

Get the string length of a hash field

"},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

Get all of the values from a hash.

"},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

Iterate over the fields and values of a hash in an incremental fashion.

"},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

Increment a key's value, optionally by a specifc amount.

"},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

Increment a key by a specific integer value

"},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

Increment a numeric key by a floating point value.

"},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

Check if we are currently connected to a Redis instance.

"},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":null},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":null},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":null},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":null},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":""},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":""},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":""},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":""},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":null},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":null},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":null},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":null},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":""},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":null},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":""},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":null},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":null},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":null},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":null},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":null},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":null},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":null},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":null},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

"},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

Add one or more elements to a Redis HyperLogLog key

"},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

Retrieve the cardinality of a Redis HyperLogLog key.

"},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

Merge one or more source HyperLogLog sets into a destination set.

"},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

PING the redis server with an optional string argument.

"},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

Enter into pipeline mode.

"},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":""},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

Subscribe to one or more glob-style patterns

"},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

Get a keys time to live in milliseconds.

"},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

Publish a message to a pubsub channel

"},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":null},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

Unsubscribe from one or more channels by pattern

"},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

Pop one or more elements from the end of a list.

"},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

Return a random key from the current database

"},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

Execute any arbitrary Redis command by name.

"},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

Unconditionally rename a key from $old_name to $new_name

"},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

Renames $key_src to $key_dst but only if newkey does not exist.

"},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

Reset the state of the connection.

"},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

Restore a key by the binary payload generated by the DUMP command.

"},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

Query whether the connected instance is a primary or replica

"},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

"},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

Add one or more values to a Redis SET key.

"},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

"},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

"},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

"},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

"},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

Compute the intersection of one or more sets and return the cardinality of the result.

"},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

"},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

Retrieve every member from a set key.

"},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

Check if one or more values are members of a set.

"},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

"},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

Remove one or more elements from a set.

"},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

Retrieve one or more random members of a set.

"},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

Returns the union of one or more Redis SET keys.

"},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

Perform a union of one or more Redis SET keys and store the result in a new set

"},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

"},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

Incrementally scan the Redis keyspace, with optional pattern and type matching.

"},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

Retrieve the number of members in a Redis set.

"},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

An administrative command used to interact with LUA scripts stored on the server.

"},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

Select a specific Redis database.

"},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

Create or set a Redis STRING key to a value.

"},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

Set a specific bit in a Redis string to zero or one

"},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

Update or append to a Redis string at a specific starting index

"},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

Set a configurable option on the Redis object.

"},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

Set a Redis STRING key with a specific expiration in seconds.

"},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

Set a key to a value, but only if that key does not already exist.

"},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

Check whether a given value is the member of a Redis SET.

"},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

Turn a redis instance into a replica of another or promote a replica\nto a primary.

"},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

"},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

Update one or more keys last modified metadata.

"},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

"},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

Sort the contents of a Redis key in various ways.

"},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

This is simply a read-only variant of the sort command

"},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

Remove one or more values from a Redis SET key.

"},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

Scan the members of a redis SET key.

"},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

Retrieve the length of a Redis STRING key.

"},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

Subscribe to one or more Redis pubsub channels.

"},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

"},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

Retrieve the server time from the connected Redis instance.

"},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

Get the amount of time a Redis key has before it will expire, in seconds.

"},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

Get the type of a given Redis key.

"},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

"},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

Unsubscribe from one or more subscribed channels.

"},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

Remove any previously WATCH'ed keys in a transaction.

"},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":""},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

Block the client up to the provided timeout until a certain number of replicas have confirmed\nrecieving them.

"},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":null},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

Append a message to a stream.

"},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":null},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":null},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

Remove one or more specific IDs from a stream.

"},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

Retrieve information about a stream key.

"},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

Get the number of messages in a Redis STREAM key.

"},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

"},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

Get a range of entries from a STREAM key.

"},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

Consume one or more unconsumed elements in one or more streams.

"},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

Read one or more messages using a consumer group.

"},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

Get a range of entries from a STREAM ke in reverse cronological order.

"},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

Truncate a STREAM key in various ways.

"},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

Add one or more elements and scores to a Redis sorted set.

"},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

Return the number of elements in a sorted set.

"},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

Count the number of members in a sorted set with scores inside a provided range.

"},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

Create or increment the score of a member in a Redis sorted set

"},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

Count the number of elements in a sorted set whos members fall within the provided\nlexographical range.

"},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

Retrieve the score of one or more members in a sorted set.

"},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

Pop one or more of the highest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

Pop one or more of the lowest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

Retrieve a range of elements of a sorted set between a start and end point.

"},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

Retrieve a range of elements from a sorted set by legographical range.

"},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

Retrieve a range of members from a sorted set by their score.

"},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

"},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

Retrieve one or more random members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

Get the rank of a member of a sorted set, by score.

"},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

Remove one or more members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

Remove zero or more elements from a Redis sorted set by legographical range.

"},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

Remove one or more members of a sorted set by their rank.

"},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

Remove one or more members of a sorted set by their score.

"},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

List the members of a Redis sorted set in reverse order

"},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

List members of a Redis sorted set within a legographical range, in reverse order.

"},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

List elements from a Redis sorted set by score, highest to lowest

"},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

Retrieve a member of a sorted set by reverse rank.

"},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

Get the score of a member of a sorted set.

"},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

"},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

Store the difference of one or more sorted sets in a destination sorted set.

"},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

Compute the intersection of one or more sorted sets and return the members

"},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

"},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

"},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

Scan the members of a sorted set incrementally, using a cursor

"},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

Retrieve the union of one or more sorted sets

"},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

Perform a union on one or more Redis sets and store the result in a destination sorted set.

"},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

See Redis::blpop()

"},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

See Redis::brpop()

"},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

See Redis::brpoplpush()

"},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

PING an instance in the redis cluster.

"},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} diff --git a/docs/doctum.js b/docs/doctum.js new file mode 100644 index 0000000000..d487386524 --- /dev/null +++ b/docs/doctum.js @@ -0,0 +1,316 @@ +var Doctum = { + treeJson: {"tree":{"l":0,"n":"","p":"","c":[{"l":1,"n":"[Global Namespace]","p":"[Global_Namespace]","c":[{"l":2,"n":"Redis","p":"Redis"},{"l":2,"n":"RedisArray","p":"RedisArray"},{"l":2,"n":"RedisCluster","p":"RedisCluster"},{"l":2,"n":"RedisClusterException","p":"RedisClusterException"},{"l":2,"n":"RedisException","p":"RedisException"},{"l":2,"n":"RedisSentinel","p":"RedisSentinel"}]}]},"treeOpenLevel":2}, + /** @var boolean */ + treeLoaded: false, + /** @var boolean */ + listenersRegistered: false, + autoCompleteData: null, + /** @var boolean */ + autoCompleteLoading: false, + /** @var boolean */ + autoCompleteLoaded: false, + /** @var string|null */ + rootPath: null, + /** @var string|null */ + autoCompleteDataUrl: null, + /** @var HTMLElement|null */ + doctumSearchAutoComplete: null, + /** @var HTMLElement|null */ + doctumSearchAutoCompleteProgressBarContainer: null, + /** @var HTMLElement|null */ + doctumSearchAutoCompleteProgressBar: null, + /** @var number */ + doctumSearchAutoCompleteProgressBarPercent: 0, + /** @var autoComplete|null */ + autoCompleteJS: null, + querySearchSecurityRegex: /([^0-9a-zA-Z:\\\\_\s])/gi, + buildTreeNode: function (treeNode, htmlNode, treeOpenLevel) { + var ulNode = document.createElement('ul'); + for (var childKey in treeNode.c) { + var child = treeNode.c[childKey]; + var liClass = document.createElement('li'); + var hasChildren = child.hasOwnProperty('c'); + var nodeSpecialName = (hasChildren ? 'namespace:' : 'class:') + child.p.replace(/\//g, '_'); + liClass.setAttribute('data-name', nodeSpecialName); + + // Create the node that will have the text + var divHd = document.createElement('div'); + var levelCss = child.l - 1; + divHd.className = hasChildren ? 'hd' : 'hd leaf'; + divHd.style.paddingLeft = (hasChildren ? (levelCss * 18) : (8 + (levelCss * 18))) + 'px'; + if (hasChildren) { + if (child.l <= treeOpenLevel) { + liClass.className = 'opened'; + } + var spanIcon = document.createElement('span'); + spanIcon.className = 'icon icon-play'; + divHd.appendChild(spanIcon); + } + var aLink = document.createElement('a'); + + // Edit the HTML link to work correctly based on the current depth + aLink.href = Doctum.rootPath + child.p + '.html'; + aLink.innerText = child.n; + divHd.appendChild(aLink); + liClass.appendChild(divHd); + + // It has children + if (hasChildren) { + var divBd = document.createElement('div'); + divBd.className = 'bd'; + Doctum.buildTreeNode(child, divBd, treeOpenLevel); + liClass.appendChild(divBd); + } + ulNode.appendChild(liClass); + } + htmlNode.appendChild(ulNode); + }, + initListeners: function () { + if (Doctum.listenersRegistered) { + // Quick exit, already registered + return; + } + Doctum.listenersRegistered = true; + }, + loadTree: function () { + if (Doctum.treeLoaded) { + // Quick exit, already registered + return; + } + Doctum.rootPath = document.body.getAttribute('data-root-path'); + Doctum.buildTreeNode(Doctum.treeJson.tree, document.getElementById('api-tree'), Doctum.treeJson.treeOpenLevel); + + // Toggle left-nav divs on click + $('#api-tree .hd span').on('click', function () { + $(this).parent().parent().toggleClass('opened'); + }); + + // Expand the parent namespaces of the current page. + var expected = $('body').attr('data-name'); + + if (expected) { + // Open the currently selected node and its parents. + var container = $('#api-tree'); + var node = $('#api-tree li[data-name="' + expected + '"]'); + // Node might not be found when simulating namespaces + if (node.length > 0) { + node.addClass('active').addClass('opened'); + node.parents('li').addClass('opened'); + var scrollPos = node.offset().top - container.offset().top + container.scrollTop(); + // Position the item nearer to the top of the screen. + scrollPos -= 200; + container.scrollTop(scrollPos); + } + } + Doctum.treeLoaded = true; + }, + pagePartiallyLoaded: function (event) { + Doctum.initListeners(); + Doctum.loadTree(); + Doctum.loadAutoComplete(); + }, + pageFullyLoaded: function (event) { + // it may not have received DOMContentLoaded event + Doctum.initListeners(); + Doctum.loadTree(); + Doctum.loadAutoComplete(); + // Fire the event in the search page too + if (typeof DoctumSearch === 'object') { + DoctumSearch.pageFullyLoaded(); + } + }, + loadAutoComplete: function () { + if (Doctum.autoCompleteLoaded) { + // Quick exit, already loaded + return; + } + Doctum.autoCompleteDataUrl = document.body.getAttribute('data-search-index-url'); + Doctum.doctumSearchAutoComplete = document.getElementById('doctum-search-auto-complete'); + Doctum.doctumSearchAutoCompleteProgressBarContainer = document.getElementById('search-progress-bar-container'); + Doctum.doctumSearchAutoCompleteProgressBar = document.getElementById('search-progress-bar'); + if (Doctum.doctumSearchAutoComplete !== null) { + // Wait for it to be loaded + Doctum.doctumSearchAutoComplete.addEventListener('init', function (_) { + Doctum.autoCompleteLoaded = true; + Doctum.doctumSearchAutoComplete.addEventListener('selection', function (event) { + // Go to selection page + window.location = Doctum.rootPath + event.detail.selection.value.p; + }); + Doctum.doctumSearchAutoComplete.addEventListener('navigate', function (event) { + // Set selection in text box + if (typeof event.detail.selection.value === 'object') { + Doctum.doctumSearchAutoComplete.value = event.detail.selection.value.n; + } + }); + Doctum.doctumSearchAutoComplete.addEventListener('results', function (event) { + Doctum.markProgressFinished(); + }); + }); + } + // Check if the lib is loaded + if (typeof autoComplete === 'function') { + Doctum.bootAutoComplete(); + } + }, + markInProgress: function () { + Doctum.doctumSearchAutoCompleteProgressBarContainer.className = 'search-bar'; + Doctum.doctumSearchAutoCompleteProgressBar.className = 'progress-bar indeterminate'; + if (typeof DoctumSearch === 'object' && DoctumSearch.pageFullyLoaded) { + DoctumSearch.doctumSearchPageAutoCompleteProgressBarContainer.className = 'search-bar'; + DoctumSearch.doctumSearchPageAutoCompleteProgressBar.className = 'progress-bar indeterminate'; + } + }, + markProgressFinished: function () { + Doctum.doctumSearchAutoCompleteProgressBarContainer.className = 'search-bar hidden'; + Doctum.doctumSearchAutoCompleteProgressBar.className = 'progress-bar'; + if (typeof DoctumSearch === 'object' && DoctumSearch.pageFullyLoaded) { + DoctumSearch.doctumSearchPageAutoCompleteProgressBarContainer.className = 'search-bar hidden'; + DoctumSearch.doctumSearchPageAutoCompleteProgressBar.className = 'progress-bar'; + } + }, + makeProgess: function () { + Doctum.makeProgressOnProgressBar( + Doctum.doctumSearchAutoCompleteProgressBarPercent, + Doctum.doctumSearchAutoCompleteProgressBar + ); + if (typeof DoctumSearch === 'object' && DoctumSearch.pageFullyLoaded) { + Doctum.makeProgressOnProgressBar( + Doctum.doctumSearchAutoCompleteProgressBarPercent, + DoctumSearch.doctumSearchPageAutoCompleteProgressBar + ); + } + }, + loadAutoCompleteData: function (query) { + return new Promise(function (resolve, reject) { + if (Doctum.autoCompleteData !== null) { + resolve(Doctum.autoCompleteData); + return; + } + Doctum.markInProgress(); + function reqListener() { + Doctum.autoCompleteLoading = false; + Doctum.autoCompleteData = JSON.parse(this.responseText).items; + Doctum.markProgressFinished(); + + setTimeout(function () { + resolve(Doctum.autoCompleteData); + }, 50);// Let the UI render once before sending the results for processing. This gives time to the progress bar to hide + } + function reqError(err) { + Doctum.autoCompleteLoading = false; + Doctum.autoCompleteData = null; + console.error(err); + reject(err); + } + + var oReq = new XMLHttpRequest(); + oReq.onload = reqListener; + oReq.onerror = reqError; + oReq.onprogress = function (pe) { + if (pe.lengthComputable) { + Doctum.doctumSearchAutoCompleteProgressBarPercent = parseInt(pe.loaded / pe.total * 100, 10); + Doctum.makeProgess(); + } + }; + oReq.onloadend = function (_) { + Doctum.markProgressFinished(); + }; + oReq.open('get', Doctum.autoCompleteDataUrl, true); + oReq.send(); + }); + }, + /** + * Make some progress on a progress bar + * + * @param number percentage + * @param HTMLElement progressBar + * @return void + */ + makeProgressOnProgressBar: function(percentage, progressBar) { + progressBar.className = 'progress-bar'; + progressBar.style.width = percentage + '%'; + progressBar.setAttribute( + 'aria-valuenow', percentage + ); + }, + searchEngine: function (query, record) { + if (typeof query !== 'string') { + return ''; + } + // replace all (mode = g) spaces and non breaking spaces (\s) by pipes + // g = global mode to mark also the second word searched + // i = case insensitive + // how this function works: + // First: search if the query has the keywords in sequence + // Second: replace the keywords by a mark and leave all the text in between non marked + + if (record.match(new RegExp('(' + query.replace(/\s/g, ').*(') + ')', 'gi')) === null) { + return '';// Does not match + } + + var replacedRecord = record.replace(new RegExp('(' + query.replace(/\s/g, '|') + ')', 'gi'), function (group) { + return '' + group + ''; + }); + + if (replacedRecord !== record) { + return replacedRecord;// This should not happen but just in case there was no match done + } + + return ''; + }, + /** + * Clean the search query + * + * @param string query + * @return string + */ + cleanSearchQuery: function (query) { + // replace any chars that could lead to injecting code in our regex + // remove start or end spaces + // replace backslashes by an escaped version, use case in search: \myRootFunction + return query.replace(Doctum.querySearchSecurityRegex, '').trim().replace(/\\/g, '\\\\'); + }, + bootAutoComplete: function () { + Doctum.autoCompleteJS = new autoComplete( + { + selector: '#doctum-search-auto-complete', + searchEngine: function (query, record) { + return Doctum.searchEngine(query, record); + }, + submit: true, + data: { + src: function (q) { + Doctum.markInProgress(); + return Doctum.loadAutoCompleteData(q); + }, + keys: ['n'],// Data 'Object' key to be searched + cache: false, // Is not compatible with async fetch of data + }, + query: (input) => { + return Doctum.cleanSearchQuery(input); + }, + trigger: (query) => { + return Doctum.cleanSearchQuery(query).length > 0; + }, + resultsList: { + tag: 'ul', + class: 'auto-complete-dropdown-menu', + destination: '#auto-complete-results', + position: 'afterbegin', + maxResults: 500, + noResults: false, + }, + resultItem: { + tag: 'li', + class: 'auto-complete-result', + highlight: 'auto-complete-highlight', + selected: 'auto-complete-selected' + }, + } + ); + } +}; + + +document.addEventListener('DOMContentLoaded', Doctum.pagePartiallyLoaded, false); +window.addEventListener('load', Doctum.pageFullyLoaded, false); diff --git a/docs/fonts/doctum-font.css b/docs/fonts/doctum-font.css new file mode 100644 index 0000000000..d022b4c349 --- /dev/null +++ b/docs/fonts/doctum-font.css @@ -0,0 +1,61 @@ +@font-face { + font-family: "doctum"; + src: url("./doctum.eot?39101248"); + src: url("./doctum.eot?39101248#iefix") format("embedded-opentype"), + url("./doctum.woff2?39101248") format("woff2"), url("./doctum.woff?39101248") format("woff"), + url("./doctum.ttf?39101248") format("truetype"), url("./doctum.svg?39101248#doctum") format("svg"); + font-weight: normal; + font-style: normal; +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'doctum'; + src: url('./doctum.svg?39101248#doctum') format('svg'); + } +} +*/ + +.icon { + font-family: "doctum"; + font-style: normal; + font-weight: normal; + speak: never; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: 0.2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: 0.2em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} + +.icon-search:before { + content: "\e800"; +} /* '' */ +.icon-play:before { + content: "\e801"; +} /* '' */ diff --git a/docs/fonts/doctum.eot b/docs/fonts/doctum.eot new file mode 100644 index 0000000000000000000000000000000000000000..0daf464c73f4ce5623535d18bdab93196f026819 GIT binary patch literal 5100 zcmd^CTWlN06}_`dQV;6kSP#c$;|#Tu?1)QRk{?kVSyuRvB0n1HVTEhA4Oe_fawU3C1`qS5to9~~nKV2;4ihp}9^b4Yczk>Xxv-Va5W`eZ%)TIULShLF;bcC=X=i%7w z#$s)C*`{V_*&<+LtAIw3uddbai+9M2`Crp86TRiO;?Fc9kODjV58Iw6nNMWt-hH|+ z^tlJu>8d_QKLCMeavzL|T_WOGVqURWvn8QrmWj62(R--h;doeoUp#n#H}M;?>oGhL z?D7Bm!+A%<*~kx~avu%<+{khCTs_+!e5NmIAJJhNq{L*jSqQzeqg~g8Hr?jew73^Z zAap@`+9QOfi3K!5OA!c{0!IV|<&nX_VAo@)1i}xnvR`5 z)*+0(V-ay`LLBPr5Zy<`=!7^G5#p_x*ZT+hp7aObpZLJve{JN#`ExTfw=SGqJURMe zesOekQTuS_-1!S5*ZTb*OuQfTKiN0X|N8fD&CH)%bj-M-e%%ShA5Q=7V>Y`jqHrHk!1?2tI1ls)s#9FP{*xwURJK8lr~_$+l(WW$rww4U{H~z zC}zaBFh?vM4J%{j40f)@&-B4A^s4%CKOaoKL|N;6>GD4lA#znqr|XY?~7rA4DT+AjC%0R@zct zi44Y9-l4E+G9^3dk7e$9XbBX!IhnSYl8!r0Hk(^beu^ccL%~L{Tky@WJNT{cC?W(G zVpwrnCMt%_8{tHX0vt^x18}nLrP2osJMK>5|D5isAHC4Iv9#Tt+Bj#7=$5}#Po#nY zBWMi;!>Zk^YFa{N?RZ#qm{@?6sx5Yb*9BliE!ED_9He$gVbzIY4mcr^h(HEztBzP& zR?<>+AZTHAz`VGSs(P|K({&$xOVP>+-IwWl}y9`=`=QR z&sKfT23Co>41_qQXkrU@tCI~~CRIsGETmMY5tRw$M^d(8;&fD|G5yaD91y75(P$-I zJ?IOmQYg@eFm+>XU7@h*F{^^xUc{N(LuOUy_Hnc7;r6gu^>TZ}topbOnAIk3zhYLK zxqZT{ws3pYB-I}J5{^6xM~-3u2}igM!Vzx!;0U({9O1Sfj&M5wN4On?BitT?BitT` zBiue^%5f*phfJ*hKw8E)7QAUTTJ;nsRmfCBAvJ{KVj@$gky87f7sDPixK{m72J$7W z!i_x;y~-R?qG#BNa%6W@_L@$Z^0edUFgPOd=y63A*w-!AQ}0im8XTW6#;PYo4^}dQ zb;HqpyQGn|_E=brnvp}}VRiD0sK~Gk=%=t1)H@&}a+*&jLO4^YOdHdv1S!;P)I1#5 zlS1@#WB$`9A-%|c{BtO(B^Jt6B8HUX6^wOe$1Ef67|Me&!ICP?hk0@~wXI2A2DY_9 z{fHIi0@sXEiTN<2F@>XrUEF(4aHvJdt>juPoi&smv$Lo%TFedrr!5@Jhx^!Y302pa zvd01jW|+dd&^WVTynQBtBU}kRINgZ77m0+r{g5XZ(MIP1#|gY~jc^IunG0F+G{-<< zcJE*<2F5r-Gv=sOGfuCRQ^qt;#=G^56Ea>vEuyrLipX(XeC%;eiNSV4?i+wK15@K_ zbvNqK;Gb++a~tiCmAzIz{DVtp&0H4Pczs7;~jj$4sxNOa@ijW^WT%^mf* zhT37*yYAsknCf(>ZrL;Fc~hMURggoREEOEbM<&L$L{td#Pdee^1guAwlgq%Vh~SX8 zK0b$Hfdbvg)cShAI2EV&ON=j*eH=HgE91skAh>J4gI3MQCKL) z3GgCw7^IlPz+w)AtIUZ3Tw@M{uQ7+g*O|lM8_YQm@Dg(vq?yCOW)6cTQ$5`XbA|== zEYw*CjRWPJe1iHMguJQ78ti2j9QKNXn7!&Crd~JIGY#rDS#YRp4r1!MgP6Kus + + +Material Design Icons + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/doctum.ttf b/docs/fonts/doctum.ttf new file mode 100644 index 0000000000000000000000000000000000000000..cf3f816250257411746ad7dba3a7cfa46d516b82 GIT binary patch literal 4940 zcmd^CU2Ggz6+U-ncm1>eN#mb1S(@DS*iP!5-Pq1g-PCd1{jux((DhH8odoEN_s34K zz1#J+N~#p~1r#kUDnjC^54-_ycWn@9#Y2#IKtf0@QY#@uK?tNEA@KqdQj5ZOW@jBc zajDc-#@RXFIp>>u&pr2?I};K_)JboUPNnJj%W?Vbf!AR96u*TR=Z}w0$eDi=i8S=Z z?3z=$llc3Oq5lZKZ)9Js$iRmWd_+wr;g^Nd@>=z~Z(Gn8h=QeMXQKrBKIkT7^YZGg zLdQFQ`G`nA2K~<~c_(+U{I?AvUmNt(D=;*T>xyS+^ zL$5$@UUP1i=nui4LB9=Mt~+b_r+#26=&p%`!^$23AA1EXf&vYzeqX#z ze$4-xhMDNix)gt+5uw3KejV05u5=-lqkH%1zR>3$+@P!a9DN@&o~eFeOzaX7=MwXZ zMbDRpm02cat84dBzsvEl{l0kc0C(a$^m@dIL~z9a?-$n{5mzH$$jW^P{?#+b*{j>x z_ux}~QTvDv(jcWKvT3hI#IN0Rxj)>9T1HC=nfu;i|4m2G-b+khmeMe*BBHN3+O-2iF@H0w`Qr%$DOmb^U~SV__+@5V`k}e)Eh^m4mwOY z4QMgQ=LTQ<>ErFOdZ5?GXsudWtlsa(8~&sJh9l}hQ%@#6*09?KTj=n^?pp*)r8 zI?yUfN0y2WX^=cNk`jf@J~$UEiiim$Zl++rfS2B&D}W6wNE?jQF6{}h>0 z(dA6alJde`>X^Nt0`pfh>SRbAv+RPbUd||Oz2xNnCasWEV~oZuOV;WnC#R0V zXY*w{!~`!Lt)qxvMQ?>yEg0}z_MmD zb~@zDSsBBEcXB=hSBMwo%^X#JQ#Ba%VwinyN+dFnMcAq%Vauv5RR@w5Rr|~f z3z?cPm$dp-d)~MiRZo}~W-}M&>&8$R_Ab}H->gw*;&P_e*@^vg;;J*kMT*jm*V_2j zj++v_*a>|govCrS->jh^dAmY6$IOqEe?(QhWA`;fxu4R{c)~>Lse8+j}5-l{utD&#;^2 z*v_o%b{#k6DL2kxa760ycEu^MXIN~f-k-QEV%tO1L5Td6W^Pj>I(u?ZH zKbN9f5|Mm0W=J_+#aO2wx@D|BhVo%du%xoNnI~s6Tbk5mXiFQ^4_R?O;F@t%Vm{1h zOrf=~i@W;-O)VnzlUz&KIYa3QCxM0BI^#<$Jb&4=c*+h&*Dcyt$M!c?as z4bQGY&zb6Uq>38iVyU7XADbB45>pY(Kk2563$T%0E-nLI5kr&IV|*Tm1rF%#N^Pw7 z^Gk7hufq5u)yH|`b7kBZ3x#*=ci8e`jB_#?Nlozj!<=~z(Obv%l9O0+PrW(uDn}o7 z#ncFTG4&btDV(*UyGxCNGi|Dq5HE1nQpl^E!aJ*xrx}yWQN4h)o;Np%o(H%9AQ)UU zH-&4t1mIe}%$zt}FEWQgnmG(+nZsa?ITHZ$%we#=90r$}!(fp)lK@wk!yv;P1{QM| zTxCuI;2Lune1$m-zRDa1Ut`WWfR~uVz-A5uhdB(EO!drmnzO8^=U~pcXdEc-))UNU zp%hFtw#{B<#bvL!h}qX&#MB$6dUl)obyi&Js*9Mq<|3x9o9fIqwaAJ~ExCxPFT04T zWpk^=r!~He;t|!HS9*W?CckQ;gpcfB&EW$?dr03imLKQbem;S)rlf^&trV2sGWk_ZZo4FuUB z=qEr;Ba3E-gUN)GV8{c|8-m)e5H(K?1|i7agI*DormzLnD>#f60|26epojmESOWlr zMv%ZbQ3=rh%7F(cVT2ekuP6#+sy{U$Rv%6XBZJKJ*F8h9%BA`>kr7lH0PFzw3uuEf znf&L7aby%3oVx>D88`WQN2+|o=L^ZCc0(kG{It0%{(?@i);mlXGCG9JuDGOx0t=X!>?ugOxr zS4PD?WanJl(=j-#eUWb=I-hcs;})lD{)ZYl#W&2N+k?xh7=)@fl;*fNJ(mhSzx$hSB3 z9QB%?v~iF3xVELasE0HwboM&DQ4D&}9lY=1jBZ9~-MFs1CiAVeOl;1O)ZA=_D7;&^ zhq2OKA<1k1mUd^9RpeFU^l;$Y<2|tz(Pjs0aS?70XAk|>b%{U4r)lAm{QL;x53Vli zdP<%hiM9!S#rNC}yu8A+gVyUAwe)7;E8~(S^bX$OepmBB7ajY0#h&F`mo07mf$YL} zvZ4>qr0t^gpnQ5K`a~rMx327ke=e|6`S$6}t~@bmNQ@HaoV`|&i1aQ3)!1HLVXbdoTfR%Mho#WcsCcXKEZVpHIdpxTwXycj ztD$kgV+^@=-|m7}1@XYB*uZ?q?4C~{V?6aE^PBSu@y^rM!w{ov^#1moMB#`nWItyl zPbql>Hu`aKRqyus;ls=20{+zid z!Hg&+`Dc%_C|kI_d-DpU9#5tZ+}AL$q%Jy%3@F82J7dWlsqBanpajOcRQTjAeK3Bu z0xf>$Q!+9y;^bGs(myyDLg(2ZP$tD%i!o`FY(^$#7J_78l5Rq6y#xMK)0c%48bxfq zRy z=4I6da%q6YN{U&HoI`#W6KD7$_p%-D%djjuVyF4>*4l-L-!6SN`{X{HnS4-aduZ5G ztA*Ipe)X?q#gvjzovQtnF|?!kFR_+Q-*KLv-ePNt4$0oS-F2anLF!4D4-}`ID$TX! zhH@<)d^p2lUT@zrF|?6$-TGo8vr}w5(Z$EnXL0Jz(8%k$ar49|Q)zuiLzC4_uP^Ev z_xt0seY#67$EidLW&KVbJq4|nZGNR=C|>wR^Q9og&uzcM*yszvbcE7sLhNd>pf+Kl z3Nh>QHU;rTh!D<>)$uPo$I?&zc!a;ITmH?jV*2ps+re3__g^Uz?k(ALM^NCJ#$Rv7 z!#!nhRHF9D_8Pl~--h8jUp0l0xAm48zO>nCW_Xj1 z$~oLX)oXT#zkbcA2~-KsI(TEXT9?PSYNZutXk)|gx-_LMD#n% zXyA*Ttw<`0ZPKiCM+s&~ zMQL&gHTUL+Fg<0#CS9&Z*P&UGwqvXiukOD^X2G`ii8H*!k_G8@>jmzfYqrUnw}BFclv29n`7Il z6^I?@78|5Z6z76w+v`t%l&IqUeb=A!+5M$UxU}#VMuEg+izzplxGLNt;j54Lsq(w= z`HSQSMYF4g!{idCCwd1f=WT@LwYjTg&5c(EjEP8zYAdHLCUe_gUxvQ>>Aw7@C(qL{ zJIe%`@f_X~?EEIQ;*ypVSYZFHt-$mfGor<(Z~Jiy4TU)12rB$qKvQbz_BJ7&O+$3? z@OVf%1USBo(EHyUBI`C=qWmbtn7r+~q-knqr%jz>gcbXCS_Q}#vPfh|7{ zVx@(reIOEDZ(5>X|0ykqqwG)YfmKj`Ur#vBLB3cHY)ql@(IfLu{K6Rr^-U-jk8#kO zlXvsY&y}UG^k^4zg0IFjNewkKn$ob^o8kA2w2iJZ zW$E4@57%CVbtb6~?v^FxDimt|KI>b3Gh#f~@N3O$MD^0vU4jJ^dPtLMkxk0JVh7{ zVY9$c=un2t)GabxOl69j{rv{a)|$L?A4S3Ycp}@piIvO!xO&JbuOIK0m;e~S_z8vC z>Dh(Z$@FT|eYLt~i8obsYOl}*04;`~`_FG+`3*Z#U6-h#LBh?#I*XOyO7Q1GiVEJv z`wcNh!F1HM#sZX?s=gHrli3p@KOj(Qti$N}_#l%l&=wdN`RaDts3fYEeM5t&LCxUU v?URU*w}(PCGHvan90VO4H6=pd5s0&WW9%0Q{`R8UXwUyIkM2 literal 0 HcmV?d00001 diff --git a/docs/fonts/doctum.woff2 b/docs/fonts/doctum.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e7b1a3e818bc3053ec8094895da44e87547a3f92 GIT binary patch literal 2248 zcmV;(2sih4Pew8T0RR9100_tc4*&oF0253A00?ma0RR9100000000000000000000 z0000SR0dW6f_?}f36^jX2nvB@fp!ZB00A}vBm)ctAO(a~2R96X1{+NhqoPi2QUbES zII!D%ya0n>!GR6oigxOwQ54_l#Q#mS+iu?db$(>i_s#6y3CIX%1Q}(P5%Dt=?N3r6 zkh)ZUcb7%cAdWbZEW3Hlmr!cExqP`biXDVP;quKH95@FxtpwML|Nm>Lz36Xz;?2iPr9L+p_tfKjSKyj9e-!Fmj@ zEg*md0005KG#%5E!_ecCND>r%tr~K}!PRvrz%PClz-9gC(~ms{m@+`PV$-913@^Ii zBGK~yj2@;5cgxE{%2?!*M93!ELuWM+2rCtgz7$y^$q6p{|9_Pz1CikWJenYQX2r2) zGq^w+5V#Hw&QtBj=p<7319er9DA1n9S$gvi*5)^;-%p8*GqJyNp{_l_+WyzO0jg6w zao&S&P->$d1eO>epv8*-LucYehYX+kSZ^#HpN{uGCvV<$U7~3>d7Rk^hdJBv%c=<` zv}eS6BiD#HrrOk0cyT^msJqx&FwQJaXyMjKF$-DmLCHBg$Z|H&l+JSw2xu&%wr4`& zZbFfWD$#kLp%{=bZ(e6rDWjOMVpU%U6vwBC+zXZJwEu&%R#UrC$gr2nu50&mvh0QP zAVKClPVPbh1?afmgNFs`Q`bo}fr!|`q1KLy-yKudgvcDk zrVyC5Gf$~EjYN6!&U{|;0ShX(${A}%0V|*=VdiT%Ys{3GvACo~EQDgnaBo{9Bve8~QwU*Xgi|1b5|LC8MHSK1A%=Ry z(l99wYGx(GFH~@g8dhI59NiNs5;sNtF$o~vh|dFwkfs{yiPmn~YHIUimvKG^l1Bfk zv?DQxws$&KojgJ|89XTkdf3L(NW~0&3LXVng_{if`@rWHfbFSm))eo(7O+E&rf@4L z#ebtf8V1*zKzgzX5Koi7unbgoyG0E&1rZU}SKY;_O|4ZraXv#5`*cstyj;)9LVl}Kax;Ft2LWJj$wE!n_s~FO~-d%2r{j}`HMi7b(&A7ltXn6_%cWiNU(r&=3#@pk||oK`|zh zL3t#a!T3-jmce->o+0>9D3Kv~B$=W3;FQYHJd(~Zd?=O4uso8@aC|6|%kX$4FPruO zF~s(FB+M|6aK*iq2&B45zgN6KDEV{W^ za&xil<}S68_=rLBPSH6^5+2l#KPV!bwl*ablRyq=QX9b&s#P;VJd|;Ym()HsMkkAy zz4Te}OE~&8(c9GC)ImN^ermkf@c!q=UwqMi@zYO_KKtwv`0S29|9n2`Sq2my<1hgyH(XK>FsH0?GyI)4*NvA-45hBh*iq# zb}QcwSBIe9{{Khp{Q1`;R^D@#=!t~QY7tCaX*MyJDFih^5O+z$Ol^{gxCSZ(Vp%`1N`&XDP9SW5a^Ns0ub!KeYys1`G4tW;R4{B%#XjPwQA=-TaO`ia7by9 zGEhMq3RZ;1d{>jE68miIdjM4VwRL=(2r!2g>F`2fuHgQQpAH>pQgYhRTq7%V26JdHR z<*ra~*qU)GrVX2;DdS|-pR~{us*PH-VAYsKqt>jO*J9kHb#sQTup2VAqs*WjXR)(g zvea0qvk0SGymi-GGVDhoEc- zQt`&@M~qB*w8)AT)$7yzB4WUyD~9vT0%e~_I^W#&p!0kejxk2h_q6^9GG+PtyNqkXIgJlmSN`xe>Ym literal 0 HcmV?d00001 diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000000..f048b2e6dc --- /dev/null +++ b/docs/index.html @@ -0,0 +1,120 @@ + + + + + + All Classes | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + +
+
+
+ Redis
+
+
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+ +
+
+
+
+
+ + + diff --git a/docs/interfaces.html b/docs/interfaces.html new file mode 100644 index 0000000000..804c07efa3 --- /dev/null +++ b/docs/interfaces.html @@ -0,0 +1,90 @@ + + + + + + Interfaces | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + +
+
+
+
+ + + diff --git a/docs/js/autocomplete.min.js b/docs/js/autocomplete.min.js new file mode 100644 index 0000000000..f7a5838e77 --- /dev/null +++ b/docs/js/autocomplete.min.js @@ -0,0 +1,5 @@ +/*! + * AutoComplete.js v10.2.6 (https://github.com/TarekRaafat/autoComplete.js) + * Licensed under the Apache 2.0 license + */ +var t,e;t=this,e=function(){"use strict";function t(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function e(e){for(var n=1;nt.length)&&(e=t.length);for(var n=0,r=new Array(e);n=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,u=!0,a=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return u=t.done,t},e:function(t){a=!0,s=t},f:function(){try{u||null==n.return||n.return()}finally{if(a)throw s}}}}(n.keys);try{for(l.s();!(c=l.n()).done;)a(c.value)}catch(t){l.e(t)}finally{l.f()}}else a()})),n.filter&&(i=n.filter(i));var s=i.slice(0,e.resultsList.maxResults);e.feedback={query:t,matches:i,results:s},f("results",e)},m="aria-expanded",b="aria-activedescendant",y="aria-selected",v=function(t,n){t.feedback.selection=e({index:n},t.feedback.results[n])},g=function(t){t.isOpen||((t.wrapper||t.input).setAttribute(m,!0),t.list.removeAttribute("hidden"),t.isOpen=!0,f("open",t))},w=function(t){t.isOpen&&((t.wrapper||t.input).setAttribute(m,!1),t.input.setAttribute(b,""),t.list.setAttribute("hidden",""),t.isOpen=!1,f("close",t))},O=function(t,e){var n=e.resultItem,r=e.list.getElementsByTagName(n.tag),o=!!n.selected&&n.selected.split(" ");if(e.isOpen&&r.length){var s,u,a=e.cursor;t>=r.length&&(t=0),t<0&&(t=r.length-1),e.cursor=t,a>-1&&(r[a].removeAttribute(y),o&&(u=r[a].classList).remove.apply(u,i(o))),r[t].setAttribute(y,!0),o&&(s=r[t].classList).add.apply(s,i(o)),e.input.setAttribute(b,r[e.cursor].id),e.list.scrollTop=r[t].offsetTop-e.list.clientHeight+r[t].clientHeight+5,e.feedback.cursor=e.cursor,v(e,t),f("navigate",e)}},A=function(t){O(t.cursor+1,t)},k=function(t){O(t.cursor-1,t)},L=function(t,e,n){(n=n>=0?n:t.cursor)<0||(t.feedback.event=e,v(t,n),f("selection",t),w(t))};function j(t,n){var r=this;return new Promise((function(i,o){var s,u;return s=n||((u=t.input)instanceof HTMLInputElement||u instanceof HTMLTextAreaElement?u.value:u.innerHTML),function(t,e,n){return e?e(t):t.length>=n}(s=t.query?t.query(s):s,t.trigger,t.threshold)?d(t,s).then((function(n){try{return t.feedback instanceof Error?i():(h(s,t),t.resultsList&&function(t){var n=t.resultsList,r=t.list,i=t.resultItem,o=t.feedback,s=o.matches,u=o.results;if(t.cursor=-1,r.innerHTML="",s.length||n.noResults){var c=new DocumentFragment;u.forEach((function(t,n){var r=a(i.tag,e({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:t.match,inside:c},i.class&&{class:i.class}));i.element&&i.element(r,t)})),r.append(c),n.element&&n.element(r,o),g(t)}else w(t)}(t),c.call(r))}catch(t){return o(t)}}),o):(w(t),c.call(r));function c(){return i()}}))}var S=function(t,e){for(var n in t)for(var r in t[n])e(n,r)},T=function(t){var n,r,i,o=t.events,s=(n=function(){return j(t)},r=t.debounce,function(){clearTimeout(i),i=setTimeout((function(){return n()}),r)}),u=t.events=e({input:e({},o&&o.input)},t.resultsList&&{list:o?e({},o.list):{}}),a={input:{input:function(){s()},keydown:function(e){!function(t,e){switch(t.keyCode){case 40:case 38:t.preventDefault(),40===t.keyCode?A(e):k(e);break;case 13:e.submit||t.preventDefault(),e.cursor>=0&&L(e,t);break;case 9:e.resultsList.tabSelect&&e.cursor>=0&&L(e,t);break;case 27:e.input.value="",w(e)}}(e,t)},blur:function(){w(t)}},list:{mousedown:function(t){t.preventDefault()},click:function(e){!function(t,e){var n=e.resultItem.tag.toUpperCase(),r=Array.from(e.list.querySelectorAll(n)),i=t.target.closest(n);i&&i.nodeName===n&&L(e,t,r.indexOf(i))}(e,t)}}};S(a,(function(e,n){(t.resultsList||"input"===n)&&(u[e][n]||(u[e][n]=a[e][n]))})),S(u,(function(e,n){t[e].addEventListener(n,u[e][n])}))};function E(t){var n=this;return new Promise((function(r,i){var o,s,u;if(o=t.placeHolder,u={role:"combobox","aria-owns":(s=t.resultsList).id,"aria-haspopup":!0,"aria-expanded":!1},a(t.input,e(e({"aria-controls":s.id,"aria-autocomplete":"both"},o&&{placeholder:o}),!t.wrapper&&e({},u))),t.wrapper&&(t.wrapper=a("div",e({around:t.input,class:t.name+"_wrapper"},u))),s&&(t.list=a(s.tag,e({dest:[s.destination,s.position],id:s.id,role:"listbox",hidden:"hidden"},s.class&&{class:s.class}))),T(t),t.data.cache)return d(t).then((function(t){try{return c.call(n)}catch(t){return i(t)}}),i);function c(){return f("init",t),r()}return c.call(n)}))}function x(t){var e=t.prototype;e.init=function(){E(this)},e.start=function(t){j(this,t)},e.unInit=function(){if(this.wrapper){var t=this.wrapper.parentNode;t.insertBefore(this.input,this.wrapper),t.removeChild(this.wrapper)}var e;S((e=this).events,(function(t,n){e[t].removeEventListener(n,e.events[t][n])}))},e.open=function(){g(this)},e.close=function(){w(this)},e.goTo=function(t){O(t,this)},e.next=function(){A(this)},e.previous=function(){k(this)},e.select=function(t){L(this,null,t)},e.search=function(t,e,n){return p(t,e,n)}}return function t(e){this.options=e,this.id=t.instances=(t.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(t){var e=t.name,r=t.options,i=t.resultsList,o=t.resultItem;for(var s in r)if("object"===n(r[s]))for(var a in t[s]||(t[s]={}),r[s])t[s][a]=r[s][a];else t[s]=r[s];t.selector=t.selector||"#"+e,i.destination=i.destination||t.selector,i.id=i.id||e+"_list_"+t.id,o.id=o.id||e+"_result",t.input=u(t.selector)}(this),x.call(this,t),E(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).autoComplete=e(); diff --git a/docs/js/bootstrap.min.js b/docs/js/bootstrap.min.js new file mode 100644 index 0000000000..5c8647b128 --- /dev/null +++ b/docs/js/bootstrap.min.js @@ -0,0 +1,11 @@ +/*! + * Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/) + */ + +/*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2021 Twitter, Inc. + * Licensed under the MIT license + */ + +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(t){"use strict";var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||e[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(t){"use strict";function e(e){var a=e.attr("data-target");a||(a=e.attr("href"),a=a&&/#[A-Za-z]/.test(a)&&a.replace(/.*(?=#[^\s]*$)/,""));var n="#"!==a?t(document).find(a):null;return n&&n.length?n:e.parent()}function a(a){a&&3===a.which||(t(i).remove(),t(s).each(function(){var n=t(this),i=e(n),s={relatedTarget:this};i.hasClass("open")&&(a&&"click"==a.type&&/input|textarea/i.test(a.target.tagName)&&t.contains(i[0],a.target)||(i.trigger(a=t.Event("hide.bs.dropdown",s)),a.isDefaultPrevented()||(n.attr("aria-expanded","false"),i.removeClass("open").trigger(t.Event("hidden.bs.dropdown",s)))))}))}function n(e){return this.each(function(){var a=t(this),n=a.data("bs.dropdown");n||a.data("bs.dropdown",n=new o(this)),"string"==typeof e&&n[e].call(a)})}var i=".dropdown-backdrop",s='[data-toggle="dropdown"]',o=function(e){t(e).on("click.bs.dropdown",this.toggle)};o.VERSION="3.4.1",o.prototype.toggle=function(n){var i=t(this);if(!i.is(".disabled, :disabled")){var s=e(i),o=s.hasClass("open");if(a(),!o){"ontouchstart"in document.documentElement&&!s.closest(".navbar-nav").length&&t(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(t(this)).on("click",a);var r={relatedTarget:this};if(s.trigger(n=t.Event("show.bs.dropdown",r)),n.isDefaultPrevented())return;i.trigger("focus").attr("aria-expanded","true"),s.toggleClass("open").trigger(t.Event("shown.bs.dropdown",r))}return!1}},o.prototype.keydown=function(a){if(/(38|40|27|32)/.test(a.which)&&!/input|textarea/i.test(a.target.tagName)){var n=t(this);if(a.preventDefault(),a.stopPropagation(),!n.is(".disabled, :disabled")){var i=e(n),o=i.hasClass("open");if(!o&&27!=a.which||o&&27==a.which)return 27==a.which&&i.find(s).trigger("focus"),n.trigger("click");var r=" li:not(.disabled):visible a",l=i.find(".dropdown-menu"+r);if(l.length){var d=l.index(a.target);38==a.which&&d>0&&d--,40==a.which&&d+~]|"+R+")"+R+"*"),U=new RegExp(R+"|>"),V=new RegExp(W),X=new RegExp("^"+B+"$"),Q={ID:new RegExp("^#("+B+")"),CLASS:new RegExp("^\\.("+B+")"),TAG:new RegExp("^("+B+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),bool:new RegExp("^(?:"+I+")$","i"),needsContext:new RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,G=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+R+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){C()},ae=xe(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{O.apply(t=P.call(d.childNodes),d.childNodes),t[d.childNodes.length].nodeType}catch(e){O={apply:t.length?function(e,t){q.apply(e,P.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&(C(e),e=e||T,E)){if(11!==d&&(u=Z.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return O.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&p.getElementsByClassName&&e.getElementsByClassName)return O.apply(n,e.getElementsByClassName(i)),n}if(p.qsa&&!k[t+" "]&&(!v||!v.test(t))&&(1!==d||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===d&&(U.test(t)||_.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&p.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=A)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+be(l[o]);c=l.join(",")}try{return O.apply(n,f.querySelectorAll(c)),n}catch(e){k(t,!0)}finally{s===A&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[A]=!0,e}function ce(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)x.attrHandle[n[r]]=t}function de(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function pe(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in p=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},C=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:d;return r!=T&&9===r.nodeType&&r.documentElement&&(a=(T=r).documentElement,E=!i(T),d!=T&&(n=T.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),p.scope=ce(function(e){return a.appendChild(e).appendChild(T.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),p.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),p.getElementsByTagName=ce(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),p.getElementsByClassName=J.test(T.getElementsByClassName),p.getById=ce(function(e){return a.appendChild(e).id=A,!T.getElementsByName||!T.getElementsByName(A).length}),p.getById?(x.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=p.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):p.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},x.find.CLASS=p.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(p.qsa=J.test(T.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+R+"*(?:value|"+I+")"),e.querySelectorAll("[id~="+A+"-]").length||v.push("~="),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+R+"*name"+R+"*="+R+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+A+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+R+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(p.matchesSelector=J.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){p.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=J.test(a.compareDocumentPosition),y=t||J.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!p.sortDetached&&t.compareDocumentPosition(e)===n?e==T||e.ownerDocument==d&&y(d,e)?-1:t==T||t.ownerDocument==d&&y(d,t)?1:u?H(u,e)-H(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==T?-1:t==T?1:i?-1:o?1:u?H(u,e)-H(u,t):0;if(i===o)return de(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?de(a[r],s[r]):a[r]==d?-1:s[r]==d?1:0}),T},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(C(e),p.matchesSelector&&E&&!k[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||p.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){k(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&V.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return b(n)?E.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?E.grep(e,function(e){return e===n!==r}):"string"!=typeof n?E.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(E.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||L,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:j.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof E?t[0]:t,E.merge(this,E.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:w,!0)),k.test(r[1])&&E.isPlainObject(t))for(r in t)b(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=w.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):b(e)?void 0!==n.ready?n.ready(e):e(E):E.makeArray(e,this)}).prototype=E.fn,L=E(w);var q=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}E.fn.extend({has:function(e){var t=E(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,pe=/^$|^module$|\/(?:java|ecma)script/i;le=w.createDocumentFragment().appendChild(w.createElement("div")),(ce=w.createElement("input")).setAttribute("type","radio"),ce.setAttribute("checked","checked"),ce.setAttribute("name","t"),le.appendChild(ce),m.checkClone=le.cloneNode(!0).cloneNode(!0).lastChild.checked,le.innerHTML="",m.noCloneChecked=!!le.cloneNode(!0).lastChild.defaultValue,le.innerHTML="",m.option=!!le.lastChild;var he={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ge(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?E.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var ye=/<|&#?\w+;/;function me(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),d=[],p=0,h=e.length;p\s*$/g;function Le(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&E(e).children("tbody")[0]||e}function je(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n
",2===ft.childNodes.length),E.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(m.createHTMLDocument?((r=(t=w.implementation.createHTMLDocument("")).createElement("base")).href=w.location.href,t.head.appendChild(r)):t=w),o=!n&&[],(i=k.exec(e))?[t.createElement(i[1])]:(i=me([e],t,o),o&&o.length&&E(o).remove(),E.merge([],i.childNodes)));var r,i,o},E.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=E.css(e,"position"),c=E(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=E.css(e,"top"),u=E.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),b(t)&&(t=t.call(e,n,E.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},E.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){E.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===E.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===E.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=E(e).offset()).top+=E.css(e,"borderTopWidth",!0),i.left+=E.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-E.css(r,"marginTop",!0),left:t.left-i.left-E.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===E.css(e,"position"))e=e.offsetParent;return e||re})}}),E.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;E.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),E.each(["top","left"],function(e,n){E.cssHooks[n]=Fe(m.pixelPosition,function(e,t){if(t)return t=We(e,n),Ie.test(t)?E(e).position()[n]+"px":t})}),E.each({Height:"height",Width:"width"},function(a,s){E.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){E.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?E.css(e,t,i):E.style(e,t,n,i)},s,n?e:void 0,n)}})}),E.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),E.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){E.fn[n]=function(e,t){return 0 + + + + + Namespaces | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + +
+ +
+ + + + diff --git a/docs/opensearch.xml b/docs/opensearch.xml new file mode 100644 index 0000000000..d51e04039a --- /dev/null +++ b/docs/opensearch.xml @@ -0,0 +1,9 @@ + + + PhpRedis API (develop) + Searches PhpRedis API (develop) + PhpRedis API + + UTF-8 + false + diff --git a/docs/renderer.index b/docs/renderer.index new file mode 100644 index 0000000000..e1826a6316 --- /dev/null +++ b/docs/renderer.index @@ -0,0 +1 @@ +O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"65ebe68ff54586b8953ba135c8c8d5c1208cf563";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:14:"RedisException";s:40:"65ebe68ff54586b8953ba135c8c8d5c1208cf563";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file diff --git a/docs/search.html b/docs/search.html new file mode 100644 index 0000000000..9d7121ad76 --- /dev/null +++ b/docs/search.html @@ -0,0 +1,297 @@ + + + + + + Search | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ + +

This page allows you to search through the API documentation for + specific terms. Enter your search words into the box below and click + "submit". The search will be performed on namespaces, classes, interfaces, + traits, functions, and methods.

+ +
+
+ + +
+ +
+ +

Search Results

+ +
+
+ + + + +
+
+ + + diff --git a/docs/traits.html b/docs/traits.html new file mode 100644 index 0000000000..d62c6ea278 --- /dev/null +++ b/docs/traits.html @@ -0,0 +1,89 @@ + + + + + + Traits | PhpRedis API + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ + +
+ +
+
+
+
+ + + diff --git a/doctum-config.php b/doctum-config.php new file mode 100644 index 0000000000..c1ac155e4d --- /dev/null +++ b/doctum-config.php @@ -0,0 +1,28 @@ +files() + ->name('*.stub.php') + ->in($root); + +$versions = GitVersionCollection::create($root) + ->add('develop', 'develop'); + +return new Doctum($iterator, [ + 'title' => 'PhpRedis API', + 'language' => 'en', + 'source_dir' => $root, + 'build_dir' => "{$root}/docs", + 'cache_dir' => "{$root}/docs/.cache", + 'base_url' => 'https://phpredis.github.io/', + 'versions' => $versions, + 'remote_repository' => new GitHubRemoteRepository('phpredis/phpredis', $root), +]); diff --git a/doctum.md b/doctum.md new file mode 100644 index 0000000000..374e27ccb9 --- /dev/null +++ b/doctum.md @@ -0,0 +1,8 @@ +# API Documentation + +```bash +curl -O https://doctum.long-term.support/releases/latest/doctum.phar +chmod +x doctum.phar + +./doctum.phar update doctum-config.php +``` \ No newline at end of file From 87fa36d6c79c13a1884cc1d6cd5f47e443105d90 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Wed, 9 Nov 2022 13:55:15 -0800 Subject: [PATCH 1706/1986] Documentation: Formatting for setOption (#2250) --- redis.stub.php | 155 +++++++++++++++++-------------------------------- 1 file changed, 54 insertions(+), 101 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 2025a1b035..c0cf67c32b 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -302,18 +302,9 @@ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis| * POP the maximum scoring element off of one or more sorted sets, blocking up to a specified * timeout if no elements are available. * - * @see https://redis.io/commands/bzpopmax - * - * @param string|array $key_or_keys Either a string key or an array of one or more keys. - * @param string|int $timeout_or_key If the previous argument was an array, this argument - * must be a timeout value. Otherwise it could also be - * another key. - * @param mixed $extra_args Can consist of additional keys, until the last argument - * which needs to be a timeout. - * * Following are examples of the two main ways to call this method. * - * + * ```php * bzPopMax('key1', 'key2', 'key3', 1.5); @@ -325,6 +316,18 @@ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis| * NOTE: We reccomend calling this function with an array and a timeout as the other strategy * may be deprecated in future versions of PhpRedis * ?> + * ``` + * + * @see https://redis.io/commands/bzpopmax + * + * @param string|array $key_or_keys Either a string key or an array of one or more keys. + * @param string|int $timeout_or_key If the previous argument was an array, this argument + * must be a timeout value. Otherwise it could also be + * another key. + * @param mixed $extra_args Can consist of additional keys, until the last argument + * which needs to be a timeout. + * + * @return Redis|array|false The popped elements. */ public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false; @@ -2613,31 +2616,6 @@ public function save(): Redis|bool; /** * Incrementally scan the Redis keyspace, with optional pattern and type matching. * - * @see https://redis.io/commands/scan - * @see Redis::setOption() - * - * @param int $iterator The cursor returned by Redis for every subsequent call to SCAN. On - * the initial invocation of the call, it should be initialized by the - * caller to NULL. Each time SCAN is invoked, the iterator will be - * updated to a new number, until finally Redis will set the value to - * zero, indicating that the scan is complete. - * - * @param string $pattern An optional glob-style pattern for matching key names. If passed as - * NULL, it is the equivalent of sending '*' (match every key). - * - * @param int $count A hint to redis that tells it how many keys to return in a single - * call to SCAN. The larger the number, the longer Redis may block - * clients while iterating the key space. - * - * @param string $type An optional argument to specify which key types to scan (e.g. - * 'STRING', 'LIST', 'SET') - * - * @return array|false An array of keys, or false if no keys were returned for this - * invocation of scan. Note that it is possible for Redis to return - * zero keys before having scanned the entire key space, so the caller - * should instead continue to SCAN until the iterator reference is - * returned to zero. - * * A note about Redis::SCAN_NORETRY and Redis::SCAN_RETRY. * * For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of @@ -2674,6 +2652,30 @@ public function save(): Redis|bool; * } * ?> * + * @see https://redis.io/commands/scan + * @see Redis::setOption() + * + * @param int $iterator The cursor returned by Redis for every subsequent call to SCAN. On + * the initial invocation of the call, it should be initialized by the + * caller to NULL. Each time SCAN is invoked, the iterator will be + * updated to a new number, until finally Redis will set the value to + * zero, indicating that the scan is complete. + * + * @param string $pattern An optional glob-style pattern for matching key names. If passed as + * NULL, it is the equivalent of sending '*' (match every key). + * + * @param int $count A hint to redis that tells it how many keys to return in a single + * call to SCAN. The larger the number, the longer Redis may block + * clients while iterating the key space. + * + * @param string $type An optional argument to specify which key types to scan (e.g. + * 'STRING', 'LIST', 'SET') + * + * @return array|false An array of keys, or false if no keys were returned for this + * invocation of scan. Note that it is possible for Redis to return + * zero keys before having scanned the entire key space, so the caller + * should instead continue to SCAN until the iterator reference is + * returned to zero. */ public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, string $type = NULL): array|false; @@ -2859,80 +2861,31 @@ public function setRange(string $key, int $index, string $value): Redis|int|fals /** * Set a configurable option on the Redis object. * - * @see Redis::getOption() - * * Following are a list of options you can set: * - * OPTION TYPE DESCRIPTION - * OPT_MAX_RETRIES int The maximum number of times Redis will attempt to reconnect - * if it gets disconnected, before throwing an exception. - * - * OPT_SCAN enum Redis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY - * - * Redis::SCAN_NORETRY (default) - * -------------------------------------------------------- - * PhpRedis will only call `SCAN` once for every time the - * user calls Redis::scan(). This means it is possible for - * an empty array of keys to be returned while there are - * still more keys to be processed. - * - * Redis::SCAN_RETRY - * -------------------------------------------------------- - * PhpRedis may make multiple calls to `SCAN` for every - * time the user calls Redis::scan(), and will never return - * an empty array of keys unless Redis returns the iterator - * to zero (meaning the `SCAN` is complete). - * - * - * OPT_SERIALIZER int One of the installed serializers, which can vary depending - * on how PhpRedis was compiled. All of the supported serializers - * are as follows: - * - * Redis::SERIALIZER_NONE - * Redis::SERIALIZER_PHP - * Redis::SERIALIZER_IGBINARY - * Redis::SERIALIZER_MSGPACK - * Redis::SERIALIZER_JSON - * - * Note: The PHP and JSON serializers are always available. - * - * OPT_PREFIX string A string PhpRedis will use to prefix every key we read or write. - * To disable the prefix, you may pass an empty string or NULL. - * - * OPT_READ_TIMEOUT double How long PhpRedis will block for a response from Redis before - * throwing a 'read error on connection' exception. - * - * OPT_TCP_KEEPALIVE bool Set or disable TCP_KEEPALIVE on the connection. - * - * OPT_COMPRESSION enum Set an automatic compression algorithm to use when reading/writing - * data to Redis. All of the supported compressors are as follows: - * - * Redis::COMPRESSION_NONE - * Redis::COMPRESSION_LZF - * Redis::COMPRESSION_LZ4 - * Redis::COMPRESSION_ZSTD - * - * Note: Some of these may not be available depending on how Redis - * was compiled. - * - * OPT_REPLY_LITERAL bool If set to true, PhpRedis will return the literal string Redis returns - * for LINE replies (e.g. '+OK'), rather than `true`. - * - * OPT_COMPRESSION_LEVEL int Set a specific compression level if Redis is compressing data. - * - * OPT_NULL_MULTIBULK_AS_NULL bool Causes PhpRedis to return `NULL` rather than `false` for NULL MULTIBULK - * RESP replies (i.e. `*-1`). - * - * OPT_BACKOFF_ALGORITHM enum The exponential backoff strategy to use. - * OPT_BACKOFF_BASE int The minimum delay between retries when backing off. - * OPT_BACKOFF_CAP int The maximum delay between replies when backing off. + * | OPTION | TYPE | DESCRIPTION | + * | --------------- | ---- | ----------- | + * | OPT_MAX_RETRIES | int | The maximum number of times Redis will attempt to reconnect if it gets disconnected, before throwing an exception. | + * | OPT_SCAN | enum | Redis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY. Whether PhpRedis should automatically SCAN again when zero keys but a nonzero iterator are returned. | + * | OPT_SERIALIZER | enum | Set the automatic data serializer.
`Redis::SERIALIZER_NONE`
`Redis::SERIALIZER_PHP`
`Redis::SERIALIZER_IGBINARY`
`Redis::SERIALIZER_MSGPACK`, `Redis::SERIALIZER_JSON`| + * | OPT_PREFIX | string | A string PhpRedis will use to prefix every key we read or write. | + * | OPT_READ_TIMEOUT | float | How long PhpRedis will block for a response from Redis before throwing a 'read error on connection' exception. | + * | OPT_TCP_KEEPALIVE | bool | Set or disable TCP_KEEPALIVE on the connection. | + * | OPT_COMPRESSION | enum | Set the compression algorithm
`Redis::COMPRESSION_NONE`
`Redis::COMPRESSION_LZF`
`Redis::COMPRESSION_LZ4`
`Redis::COMPRESSION_ZSTD` | + * | OPT_REPLY_LITERAL | bool | If set to true, PhpRedis will return the literal string Redis returns for LINE replies (e.g. '+OK'), rather than `true`. | + * | OPT_COMPRESSION_LEVEL | int | Set a specific compression level if Redis is compressing data. | + * | OPT_NULL_MULTIBULK_AS_NULL | bool | Causes PhpRedis to return `NULL` rather than `false` for NULL MULTIBULK replies | + * | OPT_BACKOFF_ALGORITHM | enum | The exponential backoff strategy to use. | + * | OPT_BACKOFF_BASE | int | The minimum delay between retries when backing off. | + * | OPT_BACKOFF_CAP | int | The maximum delay between replies when backing off. | * + * @see Redis::getOption() * @see Redis::__construct() for details about backoff strategies. * * @param int $option The option constant. * @param mixed $value The option value. * - * @return bool True if the setting was updated, false if not. + * @return bool true if the setting was updated, false if not. * */ public function setOption(int $option, mixed $value): bool; From 531177d42d3b34230fe4941b4c310e92e938bdc4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 9 Nov 2022 15:33:56 -0800 Subject: [PATCH 1707/1986] Documentation: Render docs --- docs/Redis.html | 697 +++++++++++++++++++++-------------------- docs/renderer.index | 2 +- redis_arginfo.h | 2 +- redis_legacy_arginfo.h | 2 +- 4 files changed, 356 insertions(+), 347 deletions(-) diff --git a/docs/Redis.html b/docs/Redis.html index cfc5a250e0..0c8113711c 100644 --- a/docs/Redis.html +++ b/docs/Redis.html @@ -3824,7 +3824,7 @@

See also

- + Redis|array|false bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3835,7 +3835,18 @@

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified -timeout if no elements are available.

+timeout if no elements are available.

Following are examples of the two main ways to call this method.

+
<?php
+// Method 1 - Variadic, with the last argument being our timeout
+$redis->bzPopMax('key1', 'key2', 'key3', 1.5);
+
+// Method 2 - A single array of keys, followed by the timeout
+$redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);
+<?php>
+
+NOTE:  We reccomend calling this function with an array and a timeout as the other strategy
+       may be deprecated in future versions of PhpRedis
+?>

Parameters

@@ -3857,20 +3868,7 @@

Parameters

mixed ...$extra_args

Can consist of additional keys, until the last argument -which needs to be a timeout.

-

Following are examples of the two main ways to call this method.

-

-<?php
-// Method 1 - Variadic, with the last argument being our timeout
-$redis->bzPopMax('key1', 'key2', 'key3', 1.5);
-
-// Method 2 - A single array of keys, followed by the timeout
-$redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);
-<?php>
-
-NOTE:  We reccomend calling this function with an array and a timeout as the other strategy
-       may be deprecated in future versions of PhpRedis
-?>
+which needs to be a timeout.

@@ -3880,7 +3878,7 @@

Return Value

- +
Redis|array|false

The popped elements.

@@ -3904,7 +3902,7 @@

See also

- + Redis|array|false bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3975,7 +3973,7 @@

See also

- + Redis|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -4038,7 +4036,7 @@

Return Value

- + Redis|array|null|false zmpop(array $keys, string $from, int $count = 1) @@ -4102,7 +4100,7 @@

See also

- + Redis|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -4173,7 +4171,7 @@

See also

- + Redis|array|null|false lmpop(array $keys, string $from, int $count = 1) @@ -4238,7 +4236,7 @@

See also

- + bool clearLastError() @@ -4284,7 +4282,7 @@

Return Value

- + mixed client(string $opt, mixed ...$args) @@ -4332,7 +4330,7 @@

Return Value

- + bool close() @@ -4365,7 +4363,7 @@

Return Value

- + mixed command(string $opt = null, string|array $arg) @@ -4413,7 +4411,7 @@

Return Value

- + mixed config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) @@ -4491,7 +4489,7 @@

See also

- + bool connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null) @@ -4564,7 +4562,7 @@

Return Value

- + Redis|bool copy(string $src, string $dst, array $options = null) @@ -4661,7 +4659,7 @@

See also

- + Redis|int|false dbSize() @@ -4720,7 +4718,7 @@

See also

- + Redis|string debug(string $key) @@ -4763,7 +4761,7 @@

Return Value

- + Redis|int|false decr(string $key, int $by = 1) @@ -4842,7 +4840,7 @@

See also

- + Redis|int|false decrBy(string $key, int $value) @@ -4911,7 +4909,7 @@

See also

- + Redis|int|false del(array|string $key, string ...$other_keys) @@ -4986,7 +4984,7 @@

See also

- + Redis|int|false delete(array|string $key, string ...$other_keys) deprecated @@ -5041,7 +5039,7 @@

Return Value

- + Redis|bool discard() @@ -5088,7 +5086,7 @@

Return Value

- + Redis|string dump(string $key) @@ -5162,7 +5160,7 @@

See also

- + Redis|string|false echo(string $str) @@ -5224,7 +5222,7 @@

See also

- + mixed eval(string $script, array $args = [], int $num_keys = 0) @@ -5290,7 +5288,7 @@

See also

- + mixed eval_ro(string $script_sha, array $args = [], int $num_keys = 0) @@ -5355,7 +5353,7 @@

See also

- + mixed evalsha(string $sha1, array $args = [], int $num_keys = 0) @@ -5426,7 +5424,7 @@

See also

- + mixed evalsha_ro(string $sha1, array $args = [], int $num_keys = 0) @@ -5491,7 +5489,7 @@

See also

- + Redis|array|false exec() @@ -5577,7 +5575,7 @@

See also

- + Redis|int|bool exists(mixed $key, mixed ...$other_keys) @@ -5653,7 +5651,7 @@

See also

- + Redis|bool expire(string $key, int $timeout, string|null $mode = NULL) @@ -5723,7 +5721,7 @@

See also

- + Redis|bool expireAt(string $key, int $timestamp, string|null $mode = NULL) @@ -5792,7 +5790,7 @@

See also

- + Redis|bool failover(array|null $to = null, bool $abort = false, int $timeout = 0) @@ -5845,7 +5843,7 @@

Return Value

- + Redis|int|false expiretime(string $key) @@ -5913,7 +5911,7 @@

See also

- + Redis|int|false pexpiretime(string $key) @@ -5974,7 +5972,7 @@

See also

- + Redis|bool flushAll(bool|null $sync = null) @@ -6020,7 +6018,7 @@

Return Value

- + Redis|bool flushDB(bool|null $sync = null) @@ -6066,7 +6064,7 @@

Return Value

- + Redis|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) @@ -6129,7 +6127,7 @@

Return Value

- + Redis|float|false geodist(string $key, string $src, string $dst, string|null $unit = null) @@ -6187,7 +6185,7 @@

Return Value

- + Redis|array|false geohash(string $key, string $member, string ...$other_members) @@ -6240,7 +6238,7 @@

Return Value

- + Redis|array|false geopos(string $key, string $member, string ...$other_members) @@ -6293,7 +6291,7 @@

Return Value

- + mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -6361,7 +6359,7 @@

Return Value

- + mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -6429,7 +6427,7 @@

Return Value

- + mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6492,7 +6490,7 @@

Return Value

- + mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6555,7 +6553,7 @@

Return Value

- + array geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) @@ -6618,7 +6616,7 @@

Return Value

- + Redis|array|int|false geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) @@ -6686,7 +6684,7 @@

Return Value

- + mixed get(string $key) @@ -6729,7 +6727,7 @@

Return Value

- + mixed getAuth() @@ -6773,7 +6771,7 @@

See also

- + Redis|int|false getBit(string $key, int $idx) @@ -6821,7 +6819,7 @@

Return Value

- + Redis|string|bool getEx(string $key, array $options = []) @@ -6869,7 +6867,7 @@

Return Value

- + int getDBNum() @@ -6902,7 +6900,7 @@

Return Value

- + Redis|string|bool getDel(string $key) @@ -6945,7 +6943,7 @@

Return Value

- + string getHost() @@ -6977,7 +6975,7 @@

Return Value

- + string|null getLastError() @@ -7009,7 +7007,7 @@

Return Value

- + int getMode() @@ -7041,7 +7039,7 @@

Return Value

- + mixed getOption(int $option) @@ -7095,7 +7093,7 @@

See also

- + string|null getPersistentID() @@ -7127,7 +7125,7 @@

Return Value

- + int getPort() @@ -7159,7 +7157,7 @@

Return Value

- + Redis|string|false getRange(string $key, int $start, int $end) @@ -7224,7 +7222,7 @@

Return Value

- + Redis|string|array|int|false lcs(string $key1, string $key2, array|null $options = NULL) @@ -7299,7 +7297,7 @@

Return Value

- + float getReadTimeout() @@ -7331,7 +7329,7 @@

Return Value

- + Redis|string|false getset(string $key, mixed $value) @@ -7389,7 +7387,7 @@

Return Value

- + float|false getTimeout() @@ -7421,7 +7419,7 @@

Return Value

- + int|false getTransferredBytes() @@ -7454,7 +7452,7 @@

Return Value

- + Redis|int|false hDel(string $key, string $field, string ...$other_fields) @@ -7527,7 +7525,7 @@

See also

- + Redis|bool hExists(string $key, string $field) @@ -7598,7 +7596,7 @@

See also

- + mixed hGet(string $key, string $member) @@ -7646,7 +7644,7 @@

Return Value

- + Redis|array|false hGetAll(string $key) @@ -7716,7 +7714,7 @@

See also

- + Redis|int|false hIncrBy(string $key, string $field, int $value) @@ -7792,7 +7790,7 @@

See also

- + Redis|float|false hIncrByFloat(string $key, string $field, float $value) @@ -7865,7 +7863,7 @@

See also

- + Redis|array|false hKeys(string $key) @@ -7935,7 +7933,7 @@

See also

- + Redis|int|false hLen(string $key) @@ -7988,7 +7986,7 @@

See also

- + Redis|array|false hMget(string $key, array $fields) @@ -8061,7 +8059,7 @@

See also

- + Redis|bool hMset(string $key, array $fieldvals) @@ -8124,7 +8122,7 @@

See also

- + Redis|string|array hRandField(string $key, array $options = null) @@ -8197,7 +8195,7 @@

See also

- + Redis|int|false hSet(string $key, string $member, mixed $value) @@ -8250,7 +8248,7 @@

Return Value

- + Redis|bool hSetNx(string $key, string $field, string $value) @@ -8324,7 +8322,7 @@

See also

- + Redis|int|false hStrLen(string $key, string $field) @@ -8391,7 +8389,7 @@

See also

- + Redis|array|false hVals(string $key) @@ -8459,7 +8457,7 @@

See also

- + Redis|array|bool hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -8569,7 +8567,7 @@

See also

- + Redis|int|false incr(string $key, int $by = 1) @@ -8644,7 +8642,7 @@

See also

- + Redis|int|false incrBy(string $key, int $value) @@ -8720,7 +8718,7 @@

See also

- + Redis|float|false incrByFloat(string $key, float $value) @@ -8778,7 +8776,7 @@

Return Value

- + Redis|array|false info(string ...$sections) @@ -8834,7 +8832,7 @@

See also

- + bool isConnected() @@ -8866,7 +8864,7 @@

Return Value

- + Redis|array|false keys(string $pattern) @@ -8909,7 +8907,7 @@

Return Value

- + Redis|int|false lInsert(string $key, string $pos, mixed $pivot, mixed $value) @@ -8967,7 +8965,7 @@

Return Value

- + Redis|int|false lLen(string $key) @@ -9010,7 +9008,7 @@

Return Value

- + Redis|string|false lMove(string $src, string $dst, string $wherefrom, string $whereto) @@ -9068,7 +9066,7 @@

Return Value

- + Redis|bool|string|array lPop(string $key, int $count = 0) @@ -9116,7 +9114,7 @@

Return Value

- + Redis|null|bool|int|array lPos(string $key, mixed $value, array $options = null) @@ -9169,7 +9167,7 @@

Return Value

- + int|Redis lPush(string $key, mixed ...$elements) @@ -9217,7 +9215,7 @@

Return Value

- + Redis|int|false rPush(string $key, mixed ...$elements) @@ -9265,7 +9263,7 @@

Return Value

- + Redis|int|false lPushx(string $key, mixed $value) @@ -9313,7 +9311,7 @@

Return Value

- + Redis|int|false rPushx(string $key, mixed $value) @@ -9361,7 +9359,7 @@

Return Value

- + Redis|bool lSet(string $key, int $index, mixed $value) @@ -9414,7 +9412,7 @@

Return Value

- + int lastSave() @@ -9447,7 +9445,7 @@

Return Value

- + mixed lindex(string $key, int $index) @@ -9495,7 +9493,7 @@

Return Value

- + Redis|array|false lrange(string $key, int $start, int $end) @@ -9548,7 +9546,7 @@

Return Value

- + int|Redis|false lrem(string $key, mixed $value, int $count = 0) @@ -9601,7 +9599,7 @@

Return Value

- + Redis|bool ltrim(string $key, int $start, int $end) @@ -9654,7 +9652,7 @@

Return Value

- + array|Redis mget(array $keys) @@ -9697,7 +9695,7 @@

Return Value

- + Redis|bool migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = NULL) @@ -9775,7 +9773,7 @@

Return Value

- + bool move(string $key, int $index) @@ -9823,7 +9821,7 @@

Return Value

- + Redis|bool mset(array $key_values) @@ -9866,7 +9864,7 @@

Return Value

- + Redis|bool msetnx(array $key_values) @@ -9909,7 +9907,7 @@

Return Value

- + bool|Redis multi(int $value = Redis::MULTI) @@ -9952,7 +9950,7 @@

Return Value

- + Redis|int|string|false object(string $subcommand, string $key) @@ -10000,7 +9998,7 @@

Return Value

- + bool open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) deprecated @@ -10080,7 +10078,7 @@

Return Value

- + bool pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) @@ -10153,7 +10151,7 @@

Return Value

- + bool persist(string $key) @@ -10196,7 +10194,7 @@

Return Value

- + bool pexpire(string $key, int $timeout, string|null $mode = NULL) @@ -10252,7 +10250,7 @@

Return Value

- + Redis|bool pexpireAt(string $key, int $timestamp, string|null $mode = NULL) @@ -10323,7 +10321,7 @@

See also

- + Redis|int pfadd(string $key, array $elements) @@ -10381,7 +10379,7 @@

See also

- + Redis|int pfcount(string $key) @@ -10434,7 +10432,7 @@

See also

- + Redis|bool pfmerge(string $dst, array $srckeys) @@ -10492,7 +10490,7 @@

See also

- + Redis|string|bool ping(string $message = NULL) @@ -10555,7 +10553,7 @@

See also

- + bool|Redis pipeline() @@ -10607,7 +10605,7 @@

Return Value

- + bool popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) deprecated @@ -10687,7 +10685,7 @@

Return Value

- + bool|Redis psetex(string $key, int $expire, mixed $value) @@ -10740,7 +10738,7 @@

Return Value

- + bool psubscribe(array $patterns, callable $cb) @@ -10799,7 +10797,7 @@

See also

- + Redis|int|false pttl(string $key) @@ -10862,7 +10860,7 @@

See also

- + Redis|int|false publish(string $channel, string $message) @@ -10920,7 +10918,7 @@

See also

- + mixed pubsub(string $command, mixed $arg = null) @@ -10968,7 +10966,7 @@

Return Value

- + Redis|array|bool punsubscribe(array $patterns) @@ -11034,7 +11032,7 @@

See also

- + Redis|array|string|bool rPop(string $key, int $count = 0) @@ -11109,7 +11107,7 @@

See also

- + Redis|string|false randomKey() @@ -11152,7 +11150,7 @@

See also

- + mixed rawcommand(string $command, mixed ...$args) @@ -11220,7 +11218,7 @@

Return Value

- + Redis|bool rename(string $old_name, string $new_name) @@ -11278,7 +11276,7 @@

See also

- + Redis|bool renameNx(string $key_src, string $key_dst) @@ -11350,7 +11348,7 @@

See also

- + Redis|bool reset() @@ -11382,7 +11380,7 @@

Return Value

- + Redis|bool restore(string $key, int $ttl, string $value, array|null $options = NULL) @@ -11502,7 +11500,7 @@

See also

- + mixed role() @@ -11535,7 +11533,7 @@

Return Value

- + Redis|string|false rpoplpush(string $srckey, string $dstkey) @@ -11618,7 +11616,7 @@

See also

- + Redis|int|false sAdd(string $key, mixed $value, mixed ...$other_values) @@ -11693,7 +11691,7 @@

See also

- + int sAddArray(string $key, array $values) @@ -11770,7 +11768,7 @@

See also

- + Redis|array|false sDiff(string $key, string ...$other_keys) @@ -11852,7 +11850,7 @@

See also

- + Redis|int|false sDiffStore(string $dst, string $key, string ...$other_keys) @@ -11922,7 +11920,7 @@

See also

- + Redis|array|false sInter(array|string $key, string ...$other_keys) @@ -12001,7 +11999,7 @@

See also

- + Redis|int|false sintercard(array $keys, int $limit = -1) @@ -12072,7 +12070,7 @@

See also

- + Redis|int|false sInterStore(array|string $key, string ...$other_keys) @@ -12147,7 +12145,7 @@

See also

- + Redis|array|false sMembers(string $key) @@ -12218,7 +12216,7 @@

See also

- + Redis|array|false sMisMember(string $key, string $member, string ...$other_members) @@ -12315,7 +12313,7 @@

See also

- + Redis|bool sMove(string $src, string $dst, mixed $value) @@ -12408,7 +12406,7 @@

See also

- + Redis|string|array|false sPop(string $key, int $count = 0) @@ -12499,7 +12497,7 @@

See also

- + Redis|string|array|false sRandMember(string $key, int $count = 0) @@ -12567,7 +12565,7 @@

Return Value

- + Redis|array|false sUnion(string $key, string ...$other_keys) @@ -12650,7 +12648,7 @@

See also

- + Redis|int|false sUnionStore(string $dst, string $key, string ...$other_keys) @@ -12720,7 +12718,7 @@

See also

- + Redis|bool save() @@ -12770,7 +12768,7 @@

See also

- + array|false scan(int|null $iterator, string|null $pattern = null, int $count = 0, string $type = NULL) @@ -12780,7 +12778,38 @@

-

Incrementally scan the Redis keyspace, with optional pattern and type matching.

+

Incrementally scan the Redis keyspace, with optional pattern and type matching.

A note about Redis::SCAN_NORETRY and Redis::SCAN_RETRY.

+

For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of +keys with a nonzero iterator. This can happen when matching against a pattern that very few +keys match inside a key space with a great many keys. The following example demonstrates how +to use Redis::scan() with the option disabled and enabled.

+
<?php
+
+$redis = new Redis(['host' => 'localhost']);
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+
+$it = NULL;
+
+do {
+    $keys = $redis->scan($it, '*zorg*');
+    foreach ($keys as $key) {
+        echo "KEY: $key\n";
+    }
+} while ($it != 0);
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+
+$it = NULL;
+
+// When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an
+// empty array of keys when the iterator is nonzero.
+while ($keys = $redis->scan($it, '*zorg*')) {
+    foreach ($keys as $key) {
+        echo "KEY: $key\n";
+    }
+}
+?>

Parameters

@@ -12826,39 +12855,7 @@

Return Value

invocation of scan. Note that it is possible for Redis to return zero keys before having scanned the entire key space, so the caller should instead continue to SCAN until the iterator reference is -returned to zero.

-

A note about Redis::SCAN_NORETRY and Redis::SCAN_RETRY.

-

For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of -keys with a nonzero iterator. This can happen when matching against a pattern that very few -keys match inside a key space with a great many keys. The following example demonstrates how -to use Redis::scan() with the option disabled and enabled.

-
<?php
-
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
-
-$it = NULL;
-
-do {
-    $keys = $redis->scan($it, '*zorg*');
-    foreach ($keys as $key) {
-        echo "KEY: $key\n";
-    }
-} while ($it != 0);
-
-$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
-
-$it = NULL;
-
-// When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an
-// empty array of keys when the iterator is nonzero.
-while ($keys = $redis->scan($it, '*zorg*')) {
-    foreach ($keys as $key) {
-        echo "KEY: $key\n";
-    }
-}
-?>
+returned to zero.

@@ -12889,7 +12886,7 @@

See also

- + Redis|int|false scard(string $key) @@ -12952,7 +12949,7 @@

See also

- + mixed script(string $command, mixed ...$args) @@ -13029,7 +13026,7 @@

See also

- + Redis|bool select(int $db) @@ -13087,7 +13084,7 @@

Return Value

- + Redis|string|bool set(string $key, mixed $value, mixed $options = NULL) @@ -13181,7 +13178,7 @@

See also

- + Redis|int|false setBit(string $key, int $idx, bool $value) @@ -13255,7 +13252,7 @@

See also

- + Redis|int|false setRange(string $key, int $index, string $value) @@ -13326,7 +13323,7 @@

See also

- + bool setOption(int $option, mixed $value) @@ -13336,7 +13333,83 @@

-

Set a configurable option on the Redis object.

+

Set a configurable option on the Redis object.

Following are a list of options you can set:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OPTIONTYPEDESCRIPTION
OPT_MAX_RETRIESintThe maximum number of times Redis will attempt to reconnect if it gets disconnected, before throwing an exception.
OPT_SCANenumRedis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY. Whether PhpRedis should automatically SCAN again when zero keys but a nonzero iterator are returned.
OPT_SERIALIZERenumSet the automatic data serializer.
Redis::SERIALIZER_NONE
Redis::SERIALIZER_PHP
Redis::SERIALIZER_IGBINARY
Redis::SERIALIZER_MSGPACK, Redis::SERIALIZER_JSON
OPT_PREFIXstringA string PhpRedis will use to prefix every key we read or write.
OPT_READ_TIMEOUTfloatHow long PhpRedis will block for a response from Redis before throwing a 'read error on connection' exception.
OPT_TCP_KEEPALIVEboolSet or disable TCP_KEEPALIVE on the connection.
OPT_COMPRESSIONenumSet the compression algorithm
Redis::COMPRESSION_NONE
Redis::COMPRESSION_LZF
Redis::COMPRESSION_LZ4
Redis::COMPRESSION_ZSTD
OPT_REPLY_LITERALboolIf set to true, PhpRedis will return the literal string Redis returns for LINE replies (e.g. '+OK'), rather than true.
OPT_COMPRESSION_LEVELintSet a specific compression level if Redis is compressing data.
OPT_NULL_MULTIBULK_AS_NULLboolCauses PhpRedis to return NULL rather than false for NULL MULTIBULK replies
OPT_BACKOFF_ALGORITHMenumThe exponential backoff strategy to use.
OPT_BACKOFF_BASEintThe minimum delay between retries when backing off.
OPT_BACKOFF_CAPintThe maximum delay between replies when backing off.

Parameters

@@ -13360,7 +13433,7 @@

Return Value

- +
bool

True if the setting was updated, false if not.

true if the setting was updated, false if not.

@@ -13374,71 +13447,7 @@

See also

Redis::getOption - Following are a list of options you can set: - -OPTION TYPE DESCRIPTION -OPT_MAX_RETRIES int The maximum number of times Redis will attempt to reconnect - if it gets disconnected, before throwing an exception. - -OPT_SCAN enum Redis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY - - Redis::SCAN_NORETRY (default) - -------------------------------------------------------- - PhpRedis will only call `SCAN` once for every time the - user calls Redis::scan(). This means it is possible for - an empty array of keys to be returned while there are - still more keys to be processed. - - Redis::SCAN_RETRY - -------------------------------------------------------- - PhpRedis may make multiple calls to `SCAN` for every - time the user calls Redis::scan(), and will never return - an empty array of keys unless Redis returns the iterator - to zero (meaning the `SCAN` is complete). - - -OPT_SERIALIZER int One of the installed serializers, which can vary depending - on how PhpRedis was compiled. All of the supported serializers - are as follows: - - Redis::SERIALIZER_NONE - Redis::SERIALIZER_PHP - Redis::SERIALIZER_IGBINARY - Redis::SERIALIZER_MSGPACK - Redis::SERIALIZER_JSON - - Note: The PHP and JSON serializers are always available. - -OPT_PREFIX string A string PhpRedis will use to prefix every key we read or write. - To disable the prefix, you may pass an empty string or NULL. - -OPT_READ_TIMEOUT double How long PhpRedis will block for a response from Redis before - throwing a 'read error on connection' exception. - -OPT_TCP_KEEPALIVE bool Set or disable TCP_KEEPALIVE on the connection. - -OPT_COMPRESSION enum Set an automatic compression algorithm to use when reading/writing - data to Redis. All of the supported compressors are as follows: - - Redis::COMPRESSION_NONE - Redis::COMPRESSION_LZF - Redis::COMPRESSION_LZ4 - Redis::COMPRESSION_ZSTD - - Note: Some of these may not be available depending on how Redis - was compiled. - -OPT_REPLY_LITERAL bool If set to true, PhpRedis will return the literal string Redis returns - for LINE replies (e.g. '+OK'), rather than `true`. - -OPT_COMPRESSION_LEVEL int Set a specific compression level if Redis is compressing data. - -OPT_NULL_MULTIBULK_AS_NULL bool Causes PhpRedis to return `NULL` rather than `false` for NULL MULTIBULK - RESP replies (i.e. `*-1`). - -OPT_BACKOFF_ALGORITHM enum The exponential backoff strategy to use. -OPT_BACKOFF_BASE int The minimum delay between retries when backing off. -OPT_BACKOFF_CAP int The maximum delay between replies when backing off. + @@ -13456,7 +13465,7 @@

See also

- + Redis|bool setex(string $key, int $expire, mixed $value) @@ -13515,7 +13524,7 @@

Return Value

- + Redis|bool setnx(string $key, mixed $value) @@ -13585,7 +13594,7 @@

See also

- + Redis|bool sismember(string $key, mixed $value) @@ -13646,7 +13655,7 @@

Return Value

- + Redis|bool slaveof(string $host = NULL, int $port = 6379) deprecated @@ -13729,7 +13738,7 @@

See also

- + Redis|bool replicaof(string $host = NULL, int $port = 6379) @@ -13812,7 +13821,7 @@

See also

- + Redis|int|false touch(array|string $key_or_array, string ...$more_keys) @@ -13871,7 +13880,7 @@

See also

- + mixed slowlog(string $operation, int $length = 0) @@ -13943,7 +13952,7 @@

See also

- + mixed sort(string $key, array|null $options = null) @@ -14018,7 +14027,7 @@

See also

- + mixed sort_ro(string $key, array|null $options = null) @@ -14077,7 +14086,7 @@

See also

- + array sortAsc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14152,7 +14161,7 @@

Return Value

- + array sortAscAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14227,7 +14236,7 @@

Return Value

- + array sortDesc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14302,7 +14311,7 @@

Return Value

- + array sortDescAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14377,7 +14386,7 @@

Return Value

- + Redis|int|false srem(string $key, mixed $value, mixed ...$other_values) @@ -14452,7 +14461,7 @@

See also

- + array|false sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -14578,7 +14587,7 @@

See also

- + Redis|int|false strlen(string $key) @@ -14637,7 +14646,7 @@

Return Value

- + bool subscribe(array $channels, callable $cb) @@ -14703,7 +14712,7 @@

Return Value

- + Redis|bool swapdb(int $src, int $dst) @@ -14807,7 +14816,7 @@

See also

- + Redis|array time() @@ -14861,7 +14870,7 @@

See also

- + Redis|int|false ttl(string $key) @@ -14924,7 +14933,7 @@

Return Value

- + Redis|int|false type(string $key) @@ -14985,7 +14994,7 @@

See also

See also

- + Redis|array|bool unsubscribe(array $channels) @@ -15128,7 +15137,7 @@

See also

- + Redis|bool unwatch() @@ -15184,7 +15193,7 @@

See also

- + bool|Redis watch(array|string $key, string ...$other_keys) @@ -15232,7 +15241,7 @@

Return Value

- + int|false wait(int $numreplicas, int $timeout) @@ -15291,7 +15300,7 @@

See also

- + int|false xack(string $key, string $group, array $ids) @@ -15344,7 +15353,7 @@

Return Value

- + Redis|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) @@ -15456,7 +15465,7 @@

See also

- + Redis|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) @@ -15529,7 +15538,7 @@

Return Value

- + Redis|bool|array xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) @@ -15597,7 +15606,7 @@

Return Value

- + Redis|int|false xdel(string $key, array $ids) @@ -15677,7 +15686,7 @@

Return Value

- + mixed xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) @@ -15772,7 +15781,7 @@

See also

- + mixed xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) @@ -15847,7 +15856,7 @@

Return Value

- + Redis|int|false xlen(string $key) @@ -15910,7 +15919,7 @@

See also

- + Redis|array|false xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) @@ -15995,7 +16004,7 @@

See also

- + Redis|array|bool xrange(string $key, string $start, string $end, int $count = -1) @@ -16093,7 +16102,7 @@

See also

- + Redis|array|bool xread(array $streams, int $count = -1, int $block = -1) @@ -16196,7 +16205,7 @@

See also

- + Redis|array|bool xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) @@ -16298,7 +16307,7 @@

Return Value

- + Redis|array|bool xrevrange(string $key, string $end, string $start, int $count = -1) @@ -16402,7 +16411,7 @@

See also

- + Redis|int|false xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) @@ -16529,7 +16538,7 @@

See also

- + Redis|int|false zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) @@ -16635,7 +16644,7 @@

See also

- + Redis|int|false zCard(string $key) @@ -16697,7 +16706,7 @@

See also

- + Redis|int|false zCount(string $key, string $start, string $end) @@ -16760,7 +16769,7 @@

See also

- + Redis|float|false zIncrBy(string $key, float $value, mixed $member) @@ -16835,7 +16844,7 @@

See also

- + Redis|int|false zLexCount(string $key, string $min, string $max) @@ -16911,7 +16920,7 @@

See also

- + Redis|array|false zMscore(string $key, mixed $member, mixed ...$other_members) @@ -16997,7 +17006,7 @@

See also

- + Redis|array|false zPopMax(string $key, int $count = null) @@ -17074,7 +17083,7 @@

See also

- + Redis|array|false zPopMin(string $key, int $count = null) @@ -17151,7 +17160,7 @@

See also

- + Redis|array|false zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) @@ -17233,7 +17242,7 @@

See also

- + Redis|array|false zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) @@ -17328,7 +17337,7 @@

See also

- + Redis|array|false zRangeByScore(string $key, string $start, string $end, array $options = []) @@ -17444,7 +17453,7 @@

See also

- + Redis|int|false zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) @@ -17525,7 +17534,7 @@

See also

- + Redis|string|array zRandMember(string $key, array $options = null) @@ -17596,7 +17605,7 @@

See also

- + Redis|int|false zRank(string $key, mixed $member) @@ -17654,7 +17663,7 @@

See also

- + Redis|int|false zRem(mixed $key, mixed $member, mixed ...$other_members) @@ -17738,7 +17747,7 @@

See also

- + Redis|int|false zRemRangeByLex(string $key, string $min, string $max) @@ -17836,7 +17845,7 @@

See also

- + Redis|int|false zRemRangeByRank(string $key, int $start, int $end) @@ -17914,7 +17923,7 @@

See also

- + Redis|int|false zRemRangeByScore(string $key, string $start, string $end) @@ -17994,7 +18003,7 @@

See also

- + Redis|array|false zRevRange(string $key, int $start, int $end, mixed $scores = null) @@ -18075,7 +18084,7 @@

Return Value

- + Redis|array|false zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) @@ -18176,7 +18185,7 @@

See also

- + Redis|array|false zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) @@ -18272,7 +18281,7 @@

Return Value

- + Redis|int|false zRevRank(string $key, mixed $member) @@ -18344,7 +18353,7 @@

See also

- + Redis|float|false zScore(string $key, mixed $member) @@ -18416,7 +18425,7 @@

See also

- + Redis|array|false zdiff(array $keys, array $options = null) @@ -18492,7 +18501,7 @@

See also

- + Redis|int|false zdiffstore(string $dst, array $keys) @@ -18559,7 +18568,7 @@

See also

- + Redis|array|false zinter(array $keys, array|null $weights = null, array|null $options = null) @@ -18652,7 +18661,7 @@

See also

- + Redis|int|false zintercard(array $keys, int $limit = -1) @@ -18737,7 +18746,7 @@

See also

- + Redis|int|false zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) @@ -18840,7 +18849,7 @@

See also

- + Redis|array|false zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -18927,7 +18936,7 @@

See also

- + Redis|array|false zunion(array $keys, array|null $weights = null, array|null $options = null) @@ -19026,7 +19035,7 @@

Return Value

- + Redis|int|false zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) diff --git a/docs/renderer.index b/docs/renderer.index index e1826a6316..97fb749e23 100644 --- a/docs/renderer.index +++ b/docs/renderer.index @@ -1 +1 @@ -O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"65ebe68ff54586b8953ba135c8c8d5c1208cf563";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:14:"RedisException";s:40:"65ebe68ff54586b8953ba135c8c8d5c1208cf563";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file +O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"d303f5f87803a7ca760f478c154e5ca8082f29ee";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:14:"RedisException";s:40:"d303f5f87803a7ca760f478c154e5ca8082f29ee";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file diff --git a/redis_arginfo.h b/redis_arginfo.h index d11cd333cb..bb95ce3660 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c95a6704d3c51686748694926d6f4b0f55a2f3df */ + * Stub hash: d303f5f87803a7ca760f478c154e5ca8082f29ee */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index cde9a8715d..156ed4a90b 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c95a6704d3c51686748694926d6f4b0f55a2f3df */ + * Stub hash: d303f5f87803a7ca760f478c154e5ca8082f29ee */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From 71758b091bb7adfd815681ce19e685d071c45376 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 9 Nov 2022 19:24:47 -0800 Subject: [PATCH 1708/1986] Update stubs. --- redis.stub.php | 271 ++++++++++++++++++----------------------- redis_arginfo.h | 2 +- redis_legacy_arginfo.h | 2 +- 3 files changed, 120 insertions(+), 155 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index c0cf67c32b..15288db2e2 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -13,58 +13,55 @@ class Redis { * options array it is also possible to connect to an instance at the same * time. * - * @see Redis::connect() - * @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ - * - * Following is an example of an options array with the supported - * configuration values. Note that all of these values are optional, and you - * can instead connect to Redis via PhpRedis' connect() method. - * - * - * 'localhost', - * 'port' => 6379, - * 'readTimeout' => 2.5, - * 'connectTimeout' => 2.5, - * 'persistent' => true, - * - * // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass'] - * 'auth' => ['phpredis', 'phpredis'], - * - * // See PHP stream options for valid SSL configuration settings. - * 'ssl' => ['verify_peer' => false], - * - * // How quickly to retry a connection after we time out or it closes. - * // Note that this setting is overridden by 'backoff' strategies. - * 'retryInterval' => 100, - * - * // Which backoff algorithm to use. 'decorrelated jitter' is - * // likely the best one for most solution, but there are many - * // to choose from: - * // REDIS_BACKOFF_ALGORITHM_DEFAULT - * // REDIS_BACKOFF_ALGORITHM_CONSTANT - * // REDIS_BACKOFF_ALGORITHM_UNIFORM - * // REDIS_BACKOFF_ALGORITHM_EXPONENTIAL - * // REDIS_BACKOFF_ALGORITHM_FULL_JITTER - * // REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER - * // REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER - * // - * // 'base', and 'cap' are in milliseconds and represent the first - * // delay redis will use when reconnecting, and the maximum delay - * // we will reach while retrying. - * 'backoff' => [ - * 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, - * 'base' => 500, - * 'cap' => 750, - * ] - * ]; - * ?> - * + * **NOTE**: Below is an example options array with various setting + * + * $options = [ + * 'host' => 'localhost', + * 'port' => 6379, + * 'readTimeout' => 2.5, + * 'connectTimeout' => 2.5, + * 'persistent' => true, + * + * // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass'] + * 'auth' => ['phpredis', 'phpredis'], + * + * // See PHP stream options for valid SSL configuration settings. + * 'ssl' => ['verify_peer' => false], + * + * // How quickly to retry a connection after we time out or it closes. + * // Note that this setting is overridden by 'backoff' strategies. + * 'retryInterval' => 100, + * + * // Which backoff algorithm to use. 'decorrelated jitter' is + * // likely the best one for most solution, but there are many + * // to choose from: + * // REDIS_BACKOFF_ALGORITHM_DEFAULT + * // REDIS_BACKOFF_ALGORITHM_CONSTANT + * // REDIS_BACKOFF_ALGORITHM_UNIFORM + * // REDIS_BACKOFF_ALGORITHM_EXPONENTIAL + * // REDIS_BACKOFF_ALGORITHM_FULL_JITTER + * // REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER + * // REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER + * // 'base', and 'cap' are in milliseconds and represent the first + * // delay redis will use when reconnecting, and the maximum delay + * // we will reach while retrying. + * 'backoff' => [ + * 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, + * 'base' => 500, + * 'cap' => 750, + * ] + * ]; * * Note: If you do wish to connect via the constructor, only 'host' is * strictly required, which will cause PhpRedis to connect to that * host on Redis' default port (6379). + * + * + * @see Redis::connect() + * @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ + * @param array $options + * + * @return Redis */ public function __construct(array $options = null); @@ -153,44 +150,32 @@ public function acl(string $subcmd, string ...$args): mixed; /** * Append data to a Redis STRING key. * - * @param string $key The key in question - * @param mixed $value The data to append to the key. - * - * @return Redis|int|false The new string length of the key or false on failure. - * * - * 'localhost']); - * * $redis->set('foo', 'hello); * var_dump($redis->append('foo', 'world')); - * - * // --- OUTPUT --- - * // int(10) - * ?> * + * + * @param string $key The key in question + * @param mixed $value The data to append to the key. + * + * @return Redis|int|false The new string length of the key or false on failure. + * */ public function append(string $key, mixed $value): Redis|int|false; /** * Authenticate a Redis connection after its been established. * + * $redis->auth('password'); + * $redis->auth(['password']); + * $redis->auth(['username', 'password']); + * * @see https://redis.io/commands/auth * * @param mixed $credentials A string password, or an array with one or two string elements. - * * @return Redis|bool Whether the AUTH was successful. * - * See below for various examples about how this method may be called. - * - * - * - * $redis->auth('password'); - * $redis->auth(['password']); - * $redis->auth(['username', 'password']); - * ?> - * - * */ public function auth(#[\SensitiveParameter] mixed $credentials): Redis|bool; @@ -253,6 +238,14 @@ public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bo * Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified * timeout. This method may be called in two distinct ways, of which examples are provided below. * + * + * // Variadic, with the final argument a timeout. + * $redis->blPop('list1', 'list2', 'list3', 1.5); + * + * // Alternatively, you can send an array of keys + * $relay->blPop(['list1', 'list2', 'list3'], 1.5); + * + * * @see https://redis.io/commands/blpop/ * * @param string|array $key_or_keys This can either be a string key or an array of one or more @@ -261,16 +254,7 @@ public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bo * be an additional key, or the timeout you wish to send to * the command. * - * - * - * // One way to call this method is in a variadic way, with the final argument being - * // the intended timeout. - * $redis->blPop('list1', 'list2', 'list3', 1.5); - * - * // Alternatively, you can send an array of keys - * $relay->blPop(['list1', 'list2', 'list3'], 1.5); - * ?> - * + * @return Redis|array|null|false Can return various things depending on command and data in Redis. */ public function blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false; @@ -304,19 +288,16 @@ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis| * * Following are examples of the two main ways to call this method. * - * ```php - * * // Method 1 - Variadic, with the last argument being our timeout * $redis->bzPopMax('key1', 'key2', 'key3', 1.5); * * // Method 2 - A single array of keys, followed by the timeout * $redis->bzPopMax(['key1', 'key2', 'key3'], 1.5); - * + * * - * NOTE: We reccomend calling this function with an array and a timeout as the other strategy - * may be deprecated in future versions of PhpRedis - * ?> - * ``` + * **NOTE**: We reccomend calling this function with an array and a timeout as the other strategy + * may be deprecated in future versions of PhpRedis * * @see https://redis.io/commands/bzpopmax * @@ -412,10 +393,7 @@ public function lmpop(array $keys, string $from, int $count = 1): Redis|array|nu /** * Reset any last error on the connection to NULL * - * @return bool This should always return true or throw an exception if we're not connected. - * * - * 'localhost']); * * $redis->set('string', 'this_is_a_string'); @@ -424,12 +402,10 @@ public function lmpop(array $keys, string $from, int $count = 1): Redis|array|nu * var_dump($redis->getLastError()); * $redis->clearLastError(); * var_dump($redis->getLastError()); - * - * // --- OUTPUT --- - * // string(65) "WRONGTYPE Operation against a key holding the wrong kind of value" - * // NULL - * ?> * + * + * @see Redis::getLastError() + * @return bool This should always return true or throw an exception if we're not connected. */ public function clearLastError(): bool; @@ -445,7 +421,13 @@ public function command(string $opt = null, string|array $arg): mixed; * * Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET. * - * @see https://redis.io/commands/config + * + * $redis->config('GET', 'timeout'); + * $redis->config('GET', ['timeout', 'databases']); + * + * $redis->config('SET', 'timeout', 30); + * $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); + * * * @param string $operation The CONFIG subcommand to execute * @param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or @@ -453,40 +435,20 @@ public function command(string $opt = null, string|array $arg): mixed; * Note: Redis 7.0.0 is required to send an array of settings. * @param string $value The setting value when the operation is SET. * - * - * config('GET', 'timeout'); - * $redis->config('GET', ['timeout', 'databases']); + * @return mixed Can return various things depending on arguments sent. + * + * @see https://redis.io/commands/config * - * $redis->config('SET', 'timeout', 30); - * $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); - * ?> - * * */ public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed; - public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool; + public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, + int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool; /** - * Make a copy of a redis key. - * - * @see https://redis.io/commands/copy - * - * @param string $src The key to copy - * @param string $dst The name of the new key created from the source key. - * @param array $options An array with modifiers on how COPY should operate. - * - * Available Options: - * - * $options = [ - * 'REPLACE' => true|false // Whether Redis should replace an existing key. - * 'DB' => int // Copy the key to a specific DB. - * ]; - * - * @return Redis|bool True if the copy was completed and false if not. + * Make a copy of a key. * * - * 'localhost']); * * $redis->pipeline() @@ -508,26 +470,34 @@ public function connect(string $host, int $port = 6379, float $timeout = 0, stri * * // Will succeed, because even though 'exists' is a key, we sent the REPLACE option. * var_dump($redis->copy('source1', 'exists', ['REPLACE' => true])); - * - * // --- OUTPUT --- - * // bool(true) - * // bool(true) - * // bool(false) - * // bool(true) - * ?> * + * + * **Available Options** + * + * | OPTION | TYPE | DESCRIPTION | + * | --------------- | ---- | ----------- | + * | OPT_MAX_RETRIES | int | foo | + * + * @param string $src The key to copy + * @param string $dst The name of the new key created from the source key. + * @param array $options An array with modifiers on how COPY should operate. + * + * $options = [ + * 'REPLACE' => true|false // Whether to replace an existing key. + * 'DB' => int // Copy key to specific db. + * ]; + * + * + * @return Redis|bool True if the copy was completed and false if not. + * + * @see https://redis.io/commands/copy */ public function copy(string $src, string $dst, array $options = null): Redis|bool; /** * Return the number of keys in the currently selected Redis database. * - * @see https://redis.io/commands/dbsize - * - * @return Redis|int The number of keys or false on failure. - * * - * 'localhost']); * * $redis->flushdb(); @@ -537,11 +507,11 @@ public function copy(string $src, string $dst, array $options = null): Redis|boo * * $redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']); * var_dump($redis->dbsize()); + * * - * // --- OUTPUT - * // int(1) - * // int(5) - * ?> + * @see https://redis.io/commands/dbsize + * + * @return Redis|int The number of keys or false on failure. */ public function dbSize(): Redis|int|false; @@ -550,8 +520,14 @@ public function debug(string $key): Redis|string; /** * Decrement a Redis integer by 1 or a provided value. * - * @see https://redis.io/commands/decr - * @see https://redis.io/commands/decrby + * + * $redis = new Redis(['host' => 'localhost']); + * + * $redis->set('counter', 3); + * + * var_dump($redis->decr('counter')); + * var_dump($redis->decr('counter', 2)); + * * * @param string $key The key to decrement * @param int $by How much to decrement the key. Note that if this value is @@ -561,20 +537,9 @@ public function debug(string $key): Redis|string; * * @return Redis|int|false The new value of the key or false on failure. * - * - * 'localhost']); - * - * $redis->set('counter', 3); - * - * var_dump($redis->decr('counter')); - * var_dump($redis->decr('counter', 2)); + * @see https://redis.io/commands/decr + * @see https://redis.io/commands/decrby * - * // --- OUTPUT --- - * // int(2) - * // int(0) - * ?> - * */ public function decr(string $key, int $by = 1): Redis|int|false; diff --git a/redis_arginfo.h b/redis_arginfo.h index bb95ce3660..09e3c1fe11 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d303f5f87803a7ca760f478c154e5ca8082f29ee */ + * Stub hash: 4f4f62f9f49eb59c17c3dda8e0c3ae397a6df977 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 156ed4a90b..ab2f328c2e 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d303f5f87803a7ca760f478c154e5ca8082f29ee */ + * Stub hash: 4f4f62f9f49eb59c17c3dda8e0c3ae397a6df977 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From ecf65144005bb79428f3bb38969ea94d19a110eb Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 9 Nov 2022 19:26:10 -0800 Subject: [PATCH 1709/1986] Documentation: Render docs --- docs/Redis.html | 862 +++++++++++++++++++--------------------- docs/doc-index.html | 2 +- docs/doctum-search.json | 2 +- docs/renderer.index | 2 +- 4 files changed, 416 insertions(+), 452 deletions(-) diff --git a/docs/Redis.html b/docs/Redis.html index 0c8113711c..05d15dcd77 100644 --- a/docs/Redis.html +++ b/docs/Redis.html @@ -99,7 +99,7 @@

Methods

- + Redis
__construct(array $options = null) @@ -447,7 +447,7 @@

Methods

copy(string $src, string $dst, array $options = null) -

Make a copy of a redis key.

+

Make a copy of a key.

@@ -2687,8 +2687,8 @@

Details

- - + + Redis __construct(array $options = null)

@@ -2699,7 +2699,46 @@

Create a new Redis instance. If passed sufficient information in the options array it is also possible to connect to an instance at the same -time.

+time.

NOTE: Below is an example options array with various setting

+
$options = [
+    'host'           => 'localhost',
+    'port'           => 6379,
+    'readTimeout'    => 2.5,
+    'connectTimeout' => 2.5,
+    'persistent'     => true,
+
+    // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass']
+    'auth' => ['phpredis', 'phpredis'],
+
+    // See PHP stream options for valid SSL configuration settings.
+    'ssl' => ['verify_peer' => false],
+
+    // How quickly to retry a connection after we time out or it  closes.
+    // Note that this setting is overridden by 'backoff' strategies.
+    'retryInterval'  => 100,
+
+     // Which backoff algorithm to use.  'decorrelated jitter' is
+     // likely the best one for most solution, but there are many
+     // to choose from:
+     //     REDIS_BACKOFF_ALGORITHM_DEFAULT
+     //     REDIS_BACKOFF_ALGORITHM_CONSTANT
+     //     REDIS_BACKOFF_ALGORITHM_UNIFORM
+     //     REDIS_BACKOFF_ALGORITHM_EXPONENTIAL
+     //     REDIS_BACKOFF_ALGORITHM_FULL_JITTER
+     //     REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER
+     //     REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER
+     // 'base', and 'cap' are in milliseconds and represent the first
+     // delay redis will use when reconnecting, and the maximum delay
+     // we will reach while retrying.
+    'backoff' => [
+        'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER,
+        'base'      => 500,
+        'cap'       => 750,
+    ]
+];
+

Note: If you do wish to connect via the constructor, only 'host' is +strictly required, which will cause PhpRedis to connect to that +host on Redis' default port (6379).

Parameters

@@ -2713,6 +2752,15 @@

Parameters

+

Return Value

+ + + + + + +
Redis
+

See also

@@ -2729,55 +2777,7 @@

See also

https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ - Following is an example of an options array with the supported -configuration values. Note that all of these values are optional, and you -can instead connect to Redis via PhpRedis' connect() method. - - - 'localhost', - 'port' => 6379, - 'readTimeout' => 2.5, - 'connectTimeout' => 2.5, - 'persistent' => true, - - // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass'] - 'auth' => ['phpredis', 'phpredis'], - - // See PHP stream options for valid SSL configuration settings. - 'ssl' => ['verify_peer' => false], - - // How quickly to retry a connection after we time out or it closes. - // Note that this setting is overridden by 'backoff' strategies. - 'retryInterval' => 100, - - // Which backoff algorithm to use. 'decorrelated jitter' is - // likely the best one for most solution, but there are many - // to choose from: - // REDIS_BACKOFF_ALGORITHM_DEFAULT - // REDIS_BACKOFF_ALGORITHM_CONSTANT - // REDIS_BACKOFF_ALGORITHM_UNIFORM - // REDIS_BACKOFF_ALGORITHM_EXPONENTIAL - // REDIS_BACKOFF_ALGORITHM_FULL_JITTER - // REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER - // REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER - // - // 'base', and 'cap' are in milliseconds and represent the first - // delay redis will use when reconnecting, and the maximum delay - // we will reach while retrying. - 'backoff' => [ - 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, - 'base' => 500, - 'cap' => 750, - ] -]; -?> - - -Note: If you do wish to connect via the constructor, only 'host' is - strictly required, which will cause PhpRedis to connect to that - host on Redis' default port (6379). + @@ -2788,7 +2788,7 @@

See also

- + __destruct() @@ -2812,7 +2812,7 @@

- + string _compress(string $value) @@ -2867,7 +2867,7 @@

See also

- + string _uncompress(string $value) @@ -2922,7 +2922,7 @@

See also

- + string _prefix(string $key) @@ -2965,7 +2965,7 @@

Return Value

- + string _serialize(mixed $value) @@ -3020,7 +3020,7 @@

See also

- + mixed _unserialize(string $value) @@ -3075,7 +3075,7 @@

See also

- + string _pack(mixed $value) @@ -3119,7 +3119,7 @@

Return Value

- + mixed _unpack(string $value) @@ -3162,7 +3162,7 @@

Return Value

- + mixed acl(string $subcmd, string ...$args) @@ -3210,7 +3210,7 @@

Return Value

- + Redis|int|false append(string $key, mixed $value) @@ -3220,7 +3220,9 @@

-

Append data to a Redis STRING key.

+

Append data to a Redis STRING key.

$redis = new Redis(['host' => 'localhost']);
+$redis->set('foo', 'hello);
+var_dump($redis->append('foo', 'world'));

Parameters

@@ -3244,16 +3246,7 @@

Return Value

- +
Redis|int|false

The new string length of the key or false on failure.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->set('foo', 'hello);
-var_dump($redis->append('foo', 'world'));
-
-// --- OUTPUT ---
-// int(10)
-?>

The new string length of the key or false on failure.

@@ -3266,7 +3259,7 @@

Return Value

- + Redis|bool auth(mixed $credentials) @@ -3276,7 +3269,9 @@

-

Authenticate a Redis connection after its been established.

+

Authenticate a Redis connection after its been established.

$redis->auth('password'); +$redis->auth(['password']); +$redis->auth(['username', 'password']);

Parameters

@@ -3295,13 +3290,7 @@

Return Value

- +
Redis|bool

Whether the AUTH was successful.

-

See below for various examples about how this method may be called.

-
<?php>
-$redis->auth('password');
-$redis->auth(['password']);
-$redis->auth(['username', 'password']);
-?>

Whether the AUTH was successful.

@@ -3325,7 +3314,7 @@

See also

- + Redis|bool bgSave() @@ -3368,7 +3357,7 @@

See also

- + Redis|bool bgrewriteaof() @@ -3411,7 +3400,7 @@

See also

- + Redis|int|false bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) @@ -3482,7 +3471,7 @@

See also

- + Redis|int|false bitop(string $operation, string $deskey, string $srckey, string ...$other_keys) @@ -3540,7 +3529,7 @@

Return Value

- + Redis|int|false bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) @@ -3614,7 +3603,7 @@

See also

- + Redis|array|null|false blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) @@ -3625,7 +3614,11 @@

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified -timeout. This method may be called in two distinct ways, of which examples are provided below.

+timeout. This method may be called in two distinct ways, of which examples are provided below.

// Variadic, with the final argument a timeout.
+$redis->blPop('list1', 'list2', 'list3', 1.5);
+
+// Alternatively, you can send an array of keys
+$relay->blPop(['list1', 'list2', 'list3'], 1.5);

Parameters

@@ -3642,15 +3635,7 @@

Parameters

$timeout_or_key

If the previous argument was a string key, this can either be an additional key, or the timeout you wish to send to -the command.

-
<?php>
-// One way to call this method is in a variadic way, with the final argument being
-// the intended timeout.
-$redis->blPop('list1', 'list2', 'list3', 1.5);
-
-// Alternatively, you can send an array of keys
-$relay->blPop(['list1', 'list2', 'list3'], 1.5);
-?>
+the command.

mixed @@ -3665,7 +3650,7 @@

Return Value

- +
Redis|array|null|false

Can return various things depending on command and data in Redis.

@@ -3689,7 +3674,7 @@

See also

- + Redis|array|null|false brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) @@ -3759,7 +3744,7 @@

See also

- + Redis|string|false brpoplpush(string $src, string $dst, int|float $timeout) @@ -3824,7 +3809,7 @@

See also

- + Redis|array|false bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3836,17 +3821,13 @@

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified timeout if no elements are available.

Following are examples of the two main ways to call this method.

-
<?php
-// Method 1 - Variadic, with the last argument being our timeout
+
// Method 1 - Variadic, with the last argument being our timeout
 $redis->bzPopMax('key1', 'key2', 'key3', 1.5);
 
 // Method 2 - A single array of keys, followed by the timeout
-$redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);
-<?php>
-
-NOTE:  We reccomend calling this function with an array and a timeout as the other strategy
-       may be deprecated in future versions of PhpRedis
-?>

+$redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);
+

NOTE: We reccomend calling this function with an array and a timeout as the other strategy +may be deprecated in future versions of PhpRedis

Parameters

@@ -3902,7 +3883,7 @@

See also

- + Redis|array|false bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3973,7 +3954,7 @@

See also

- + Redis|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -4036,7 +4017,7 @@

Return Value

- + Redis|array|null|false zmpop(array $keys, string $from, int $count = 1) @@ -4100,7 +4081,7 @@

See also

- + Redis|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -4171,7 +4152,7 @@

See also

- + Redis|array|null|false lmpop(array $keys, string $from, int $count = 1) @@ -4236,7 +4217,7 @@

See also

- + bool clearLastError() @@ -4246,7 +4227,14 @@

-

Reset any last error on the connection to NULL

+

Reset any last error on the connection to NULL

$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('string', 'this_is_a_string');
+$redis->smembers('string');
+
+var_dump($redis->getLastError());
+$redis->clearLastError();
+var_dump($redis->getLastError());

@@ -4255,26 +4243,24 @@

Return Value

- +
bool

This should always return true or throw an exception if we're not connected.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->set('string', 'this_is_a_string');
-$redis->smembers('string');
-
-var_dump($redis->getLastError());
-$redis->clearLastError();
-var_dump($redis->getLastError());
-
-// --- OUTPUT ---
-// string(65) "WRONGTYPE Operation against a key holding the wrong kind of value"
-// NULL
-?>

This should always return true or throw an exception if we're not connected.

+

See also

+ + + + + + +
+ +Redis::getLastError +
+

@@ -4282,7 +4268,7 @@

Return Value

- + mixed client(string $opt, mixed ...$args) @@ -4330,7 +4316,7 @@

Return Value

- + bool close() @@ -4363,7 +4349,7 @@

Return Value

- + mixed command(string $opt = null, string|array $arg) @@ -4411,7 +4397,7 @@

Return Value

- + mixed config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) @@ -4422,7 +4408,12 @@

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends -on the $operation qualifier.

Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.

+on the $operation qualifier.

Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.

+
$redis->config('GET', 'timeout');
+$redis->config('GET', ['timeout', 'databases']);
+
+$redis->config('SET', 'timeout', 30);
+$redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);

Parameters

@@ -4431,7 +4422,13 @@

Parameters

string $operation - +

The CONFIG subcommand to execute +@param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or +an array of settings or settings and values. +Note: Redis 7.0.0 is required to send an array of settings. +@param string $value The setting value when the operation is SET.

+

@return mixed Can return various things depending on arguments sent.

+

https://redis.io/commands/config

array|string|null @@ -4457,31 +4454,6 @@

Return Value

-

See also

- - - - - - -
- https://redis.io/commands/config - @param string $operation The CONFIG subcommand to execute -@param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or - an array of settings or settings and values. - Note: Redis 7.0.0 is required to send an array of settings. -@param string $value The setting value when the operation is SET. - - -config('GET', 'timeout'); -$redis->config('GET', ['timeout', 'databases']); - -$redis->config('SET', 'timeout', 30); -$redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); -?> -
-

@@ -4489,7 +4461,7 @@

See also

- + bool connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null) @@ -4562,7 +4534,7 @@

Return Value

- + Redis|bool copy(string $src, string $dst, array $options = null) @@ -4572,7 +4544,44 @@

-

Make a copy of a redis key.

+

Make a copy of a key.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->pipeline()
+      ->select(1)
+      ->del('newkey')
+      ->select(0)
+      ->del('newkey')
+      ->mset(['source1' => 'value1', 'exists' => 'old_value'])
+      ->exec();
+
+// Will succeed, as 'newkey' doesn't exist
+var_dump($redis->copy('source1', 'newkey'));
+
+// Will succeed, because 'newkey' doesn't exist in DB 1
+var_dump($redis->copy('source1', 'newkey', ['db' => 1]));
+
+// Will fail, because 'exists' does exist
+var_dump($redis->copy('source1', 'exists'));
+
+// Will succeed, because even though 'exists' is a key, we sent the REPLACE option.
+var_dump($redis->copy('source1', 'exists', ['REPLACE' => true]));
+

Available Options

+ + + + + + + + + + + + + + + +
OPTIONTYPEDESCRIPTION
OPT_MAX_RETRIESintfoo

Parameters

@@ -4592,11 +4601,10 @@

Parameters

array $options

An array with modifiers on how COPY should operate.

-

Available Options:

-

$options = [ -'REPLACE' => true|false // Whether Redis should replace an existing key. -'DB' => int // Copy the key to a specific DB. -];

+
$options = [
+    'REPLACE' => true|false // Whether to replace an existing key.
+    'DB' => int             // Copy key to specific db.
+];
@@ -4606,36 +4614,7 @@

Return Value

- +
Redis|bool

True if the copy was completed and false if not.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->pipeline()
-      ->select(1)
-      ->del('newkey')
-      ->select(0)
-      ->del('newkey')
-      ->mset(['source1' => 'value1', 'exists' => 'old_value'])
-      ->exec();
-
-// Will succeed, as 'newkey' doesn't exist
-var_dump($redis->copy('source1', 'newkey'));
-
-// Will succeed, because 'newkey' doesn't exist in DB 1
-var_dump($redis->copy('source1', 'newkey', ['db' => 1]));
-
-// Will fail, because 'exists' does exist
-var_dump($redis->copy('source1', 'exists'));
-
-// Will succeed, because even though 'exists' is a key, we sent the REPLACE option.
-var_dump($redis->copy('source1', 'exists', ['REPLACE' => true]));
-
-// --- OUTPUT ---
-// bool(true)
-// bool(true)
-// bool(false)
-// bool(true)
-?>

True if the copy was completed and false if not.

@@ -4659,7 +4638,7 @@

See also

- + Redis|int|false dbSize() @@ -4669,19 +4648,7 @@

-

Return the number of keys in the currently selected Redis database.

-
-
- -

Return Value

- - - - - +
Redis|int|false

The number of keys or false on failure.

-

-<?php
-$redis = new Redis(['host' => 'localhost']);
+                            

Return the number of keys in the currently selected Redis database.

$redis = new Redis(['host' => 'localhost']);
 
 $redis->flushdb();
 
@@ -4689,12 +4656,16 @@ 

Return Value

var_dump($redis->dbsize()); $redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']); -var_dump($redis->dbsize()); +var_dump($redis->dbsize());

+ +
+ +

Return Value

-// --- OUTPUT -// int(1) -// int(5) -?>
+ + +
Redis|int|false

The number of keys or false on failure.

@@ -4718,7 +4689,7 @@

See also

- + Redis|string debug(string $key) @@ -4761,7 +4732,7 @@

Return Value

- + Redis|int|false decr(string $key, int $by = 1) @@ -4771,7 +4742,12 @@

-

Decrement a Redis integer by 1 or a provided value.

+

Decrement a Redis integer by 1 or a provided value.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('counter', 3);
+
+var_dump($redis->decr('counter'));
+var_dump($redis->decr('counter', 2));

Parameters

@@ -4798,19 +4774,7 @@

Return Value

- +
Redis|int|false

The new value of the key or false on failure.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->set('counter', 3);
-
-var_dump($redis->decr('counter'));
-var_dump($redis->decr('counter', 2));
-
-// --- OUTPUT ---
-// int(2)
-// int(0)
-?>

The new value of the key or false on failure.

@@ -4840,7 +4804,7 @@

See also

- + Redis|int|false decrBy(string $key, int $value) @@ -4909,7 +4873,7 @@

See also

- + Redis|int|false del(array|string $key, string ...$other_keys) @@ -4984,7 +4948,7 @@

See also

- + Redis|int|false delete(array|string $key, string ...$other_keys) deprecated @@ -5039,7 +5003,7 @@

Return Value

- + Redis|bool discard() @@ -5086,7 +5050,7 @@

Return Value

- + Redis|string dump(string $key) @@ -5160,7 +5124,7 @@

See also

- + Redis|string|false echo(string $str) @@ -5222,7 +5186,7 @@

See also

- + mixed eval(string $script, array $args = [], int $num_keys = 0) @@ -5288,7 +5252,7 @@

See also

- + mixed eval_ro(string $script_sha, array $args = [], int $num_keys = 0) @@ -5353,7 +5317,7 @@

See also

- + mixed evalsha(string $sha1, array $args = [], int $num_keys = 0) @@ -5424,7 +5388,7 @@

See also

- + mixed evalsha_ro(string $sha1, array $args = [], int $num_keys = 0) @@ -5489,7 +5453,7 @@

See also

- + Redis|array|false exec() @@ -5575,7 +5539,7 @@

See also

- + Redis|int|bool exists(mixed $key, mixed ...$other_keys) @@ -5651,7 +5615,7 @@

See also

- + Redis|bool expire(string $key, int $timeout, string|null $mode = NULL) @@ -5721,7 +5685,7 @@

See also

- + Redis|bool expireAt(string $key, int $timestamp, string|null $mode = NULL) @@ -5790,7 +5754,7 @@

See also

- + Redis|bool failover(array|null $to = null, bool $abort = false, int $timeout = 0) @@ -5843,7 +5807,7 @@

Return Value

- + Redis|int|false expiretime(string $key) @@ -5911,7 +5875,7 @@

See also

- + Redis|int|false pexpiretime(string $key) @@ -5972,7 +5936,7 @@

See also

- + Redis|bool flushAll(bool|null $sync = null) @@ -6018,7 +5982,7 @@

Return Value

- + Redis|bool flushDB(bool|null $sync = null) @@ -6064,7 +6028,7 @@

Return Value

- + Redis|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) @@ -6127,7 +6091,7 @@

Return Value

- + Redis|float|false geodist(string $key, string $src, string $dst, string|null $unit = null) @@ -6185,7 +6149,7 @@

Return Value

- + Redis|array|false geohash(string $key, string $member, string ...$other_members) @@ -6238,7 +6202,7 @@

Return Value

- + Redis|array|false geopos(string $key, string $member, string ...$other_members) @@ -6291,7 +6255,7 @@

Return Value

- + mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -6359,7 +6323,7 @@

Return Value

- + mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -6427,7 +6391,7 @@

Return Value

- + mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6490,7 +6454,7 @@

Return Value

- + mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6553,7 +6517,7 @@

Return Value

- + array geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) @@ -6616,7 +6580,7 @@

Return Value

- + Redis|array|int|false geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) @@ -6684,7 +6648,7 @@

Return Value

- + mixed get(string $key) @@ -6727,7 +6691,7 @@

Return Value

- + mixed getAuth() @@ -6771,7 +6735,7 @@

See also

- + Redis|int|false getBit(string $key, int $idx) @@ -6819,7 +6783,7 @@

Return Value

- + Redis|string|bool getEx(string $key, array $options = []) @@ -6867,7 +6831,7 @@

Return Value

- + int getDBNum() @@ -6900,7 +6864,7 @@

Return Value

- + Redis|string|bool getDel(string $key) @@ -6943,7 +6907,7 @@

Return Value

- + string getHost() @@ -6975,7 +6939,7 @@

Return Value

- + string|null getLastError() @@ -7007,7 +6971,7 @@

Return Value

- + int getMode() @@ -7039,7 +7003,7 @@

Return Value

- + mixed getOption(int $option) @@ -7093,7 +7057,7 @@

See also

- + string|null getPersistentID() @@ -7125,7 +7089,7 @@

Return Value

- + int getPort() @@ -7157,7 +7121,7 @@

Return Value

- + Redis|string|false getRange(string $key, int $start, int $end) @@ -7222,7 +7186,7 @@

Return Value

- + Redis|string|array|int|false lcs(string $key1, string $key2, array|null $options = NULL) @@ -7297,7 +7261,7 @@

Return Value

- + float getReadTimeout() @@ -7329,7 +7293,7 @@

Return Value

- + Redis|string|false getset(string $key, mixed $value) @@ -7387,7 +7351,7 @@

Return Value

- + float|false getTimeout() @@ -7419,7 +7383,7 @@

Return Value

- + int|false getTransferredBytes() @@ -7452,7 +7416,7 @@

Return Value

- + Redis|int|false hDel(string $key, string $field, string ...$other_fields) @@ -7525,7 +7489,7 @@

See also

- + Redis|bool hExists(string $key, string $field) @@ -7596,7 +7560,7 @@

See also

- + mixed hGet(string $key, string $member) @@ -7644,7 +7608,7 @@

Return Value

- + Redis|array|false hGetAll(string $key) @@ -7714,7 +7678,7 @@

See also

- + Redis|int|false hIncrBy(string $key, string $field, int $value) @@ -7790,7 +7754,7 @@

See also

- + Redis|float|false hIncrByFloat(string $key, string $field, float $value) @@ -7863,7 +7827,7 @@

See also

- + Redis|array|false hKeys(string $key) @@ -7933,7 +7897,7 @@

See also

- + Redis|int|false hLen(string $key) @@ -7986,7 +7950,7 @@

See also

- + Redis|array|false hMget(string $key, array $fields) @@ -8059,7 +8023,7 @@

See also

- + Redis|bool hMset(string $key, array $fieldvals) @@ -8122,7 +8086,7 @@

See also

- + Redis|string|array hRandField(string $key, array $options = null) @@ -8195,7 +8159,7 @@

See also

- + Redis|int|false hSet(string $key, string $member, mixed $value) @@ -8248,7 +8212,7 @@

Return Value

- + Redis|bool hSetNx(string $key, string $field, string $value) @@ -8322,7 +8286,7 @@

See also

- + Redis|int|false hStrLen(string $key, string $field) @@ -8389,7 +8353,7 @@

See also

- + Redis|array|false hVals(string $key) @@ -8457,7 +8421,7 @@

See also

- + Redis|array|bool hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -8567,7 +8531,7 @@

See also

- + Redis|int|false incr(string $key, int $by = 1) @@ -8642,7 +8606,7 @@

See also

- + Redis|int|false incrBy(string $key, int $value) @@ -8718,7 +8682,7 @@

See also

- + Redis|float|false incrByFloat(string $key, float $value) @@ -8776,7 +8740,7 @@

Return Value

- + Redis|array|false info(string ...$sections) @@ -8832,7 +8796,7 @@

See also

- + bool isConnected() @@ -8864,7 +8828,7 @@

Return Value

- + Redis|array|false keys(string $pattern) @@ -8907,7 +8871,7 @@

Return Value

- + Redis|int|false lInsert(string $key, string $pos, mixed $pivot, mixed $value) @@ -8965,7 +8929,7 @@

Return Value

- + Redis|int|false lLen(string $key) @@ -9008,7 +8972,7 @@

Return Value

- + Redis|string|false lMove(string $src, string $dst, string $wherefrom, string $whereto) @@ -9066,7 +9030,7 @@

Return Value

- + Redis|bool|string|array lPop(string $key, int $count = 0) @@ -9114,7 +9078,7 @@

Return Value

- + Redis|null|bool|int|array lPos(string $key, mixed $value, array $options = null) @@ -9167,7 +9131,7 @@

Return Value

- + int|Redis lPush(string $key, mixed ...$elements) @@ -9215,7 +9179,7 @@

Return Value

- + Redis|int|false rPush(string $key, mixed ...$elements) @@ -9263,7 +9227,7 @@

Return Value

- + Redis|int|false lPushx(string $key, mixed $value) @@ -9311,7 +9275,7 @@

Return Value

- + Redis|int|false rPushx(string $key, mixed $value) @@ -9359,7 +9323,7 @@

Return Value

- + Redis|bool lSet(string $key, int $index, mixed $value) @@ -9412,7 +9376,7 @@

Return Value

- + int lastSave() @@ -9445,7 +9409,7 @@

Return Value

- + mixed lindex(string $key, int $index) @@ -9493,7 +9457,7 @@

Return Value

- + Redis|array|false lrange(string $key, int $start, int $end) @@ -9546,7 +9510,7 @@

Return Value

- + int|Redis|false lrem(string $key, mixed $value, int $count = 0) @@ -9599,7 +9563,7 @@

Return Value

- + Redis|bool ltrim(string $key, int $start, int $end) @@ -9652,7 +9616,7 @@

Return Value

- + array|Redis mget(array $keys) @@ -9695,7 +9659,7 @@

Return Value

- + Redis|bool migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = NULL) @@ -9773,7 +9737,7 @@

Return Value

- + bool move(string $key, int $index) @@ -9821,7 +9785,7 @@

Return Value

- + Redis|bool mset(array $key_values) @@ -9864,7 +9828,7 @@

Return Value

- + Redis|bool msetnx(array $key_values) @@ -9907,7 +9871,7 @@

Return Value

- + bool|Redis multi(int $value = Redis::MULTI) @@ -9950,7 +9914,7 @@

Return Value

- + Redis|int|string|false object(string $subcommand, string $key) @@ -9998,7 +9962,7 @@

Return Value

- + bool open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) deprecated @@ -10078,7 +10042,7 @@

Return Value

- + bool pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) @@ -10151,7 +10115,7 @@

Return Value

- + bool persist(string $key) @@ -10194,7 +10158,7 @@

Return Value

- + bool pexpire(string $key, int $timeout, string|null $mode = NULL) @@ -10250,7 +10214,7 @@

Return Value

- + Redis|bool pexpireAt(string $key, int $timestamp, string|null $mode = NULL) @@ -10321,7 +10285,7 @@

See also

- + Redis|int pfadd(string $key, array $elements) @@ -10379,7 +10343,7 @@

See also

- + Redis|int pfcount(string $key) @@ -10432,7 +10396,7 @@

See also

- + Redis|bool pfmerge(string $dst, array $srckeys) @@ -10490,7 +10454,7 @@

See also

- + Redis|string|bool ping(string $message = NULL) @@ -10553,7 +10517,7 @@

See also

- + bool|Redis pipeline() @@ -10605,7 +10569,7 @@

Return Value

- + bool popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) deprecated @@ -10685,7 +10649,7 @@

Return Value

- + bool|Redis psetex(string $key, int $expire, mixed $value) @@ -10738,7 +10702,7 @@

Return Value

- + bool psubscribe(array $patterns, callable $cb) @@ -10797,7 +10761,7 @@

See also

- + Redis|int|false pttl(string $key) @@ -10860,7 +10824,7 @@

See also

- + Redis|int|false publish(string $channel, string $message) @@ -10918,7 +10882,7 @@

See also

- + mixed pubsub(string $command, mixed $arg = null) @@ -10966,7 +10930,7 @@

Return Value

- + Redis|array|bool punsubscribe(array $patterns) @@ -11032,7 +10996,7 @@

See also

- + Redis|array|string|bool rPop(string $key, int $count = 0) @@ -11107,7 +11071,7 @@

See also

- + Redis|string|false randomKey() @@ -11150,7 +11114,7 @@

See also

- + mixed rawcommand(string $command, mixed ...$args) @@ -11218,7 +11182,7 @@

Return Value

- + Redis|bool rename(string $old_name, string $new_name) @@ -11276,7 +11240,7 @@

See also

- + Redis|bool renameNx(string $key_src, string $key_dst) @@ -11348,7 +11312,7 @@

See also

- + Redis|bool reset() @@ -11380,7 +11344,7 @@

Return Value

- + Redis|bool restore(string $key, int $ttl, string $value, array|null $options = NULL) @@ -11500,7 +11464,7 @@

See also

- + mixed role() @@ -11533,7 +11497,7 @@

Return Value

- + Redis|string|false rpoplpush(string $srckey, string $dstkey) @@ -11616,7 +11580,7 @@

See also

- + Redis|int|false sAdd(string $key, mixed $value, mixed ...$other_values) @@ -11691,7 +11655,7 @@

See also

- + int sAddArray(string $key, array $values) @@ -11768,7 +11732,7 @@

See also

- + Redis|array|false sDiff(string $key, string ...$other_keys) @@ -11850,7 +11814,7 @@

See also

- + Redis|int|false sDiffStore(string $dst, string $key, string ...$other_keys) @@ -11920,7 +11884,7 @@

See also

- + Redis|array|false sInter(array|string $key, string ...$other_keys) @@ -11999,7 +11963,7 @@

See also

- + Redis|int|false sintercard(array $keys, int $limit = -1) @@ -12070,7 +12034,7 @@

See also

- + Redis|int|false sInterStore(array|string $key, string ...$other_keys) @@ -12145,7 +12109,7 @@

See also

- + Redis|array|false sMembers(string $key) @@ -12216,7 +12180,7 @@

See also

- + Redis|array|false sMisMember(string $key, string $member, string ...$other_members) @@ -12313,7 +12277,7 @@

See also

- + Redis|bool sMove(string $src, string $dst, mixed $value) @@ -12406,7 +12370,7 @@

See also

- + Redis|string|array|false sPop(string $key, int $count = 0) @@ -12497,7 +12461,7 @@

See also

- + Redis|string|array|false sRandMember(string $key, int $count = 0) @@ -12565,7 +12529,7 @@

Return Value

- + Redis|array|false sUnion(string $key, string ...$other_keys) @@ -12648,7 +12612,7 @@

See also

- + Redis|int|false sUnionStore(string $dst, string $key, string ...$other_keys) @@ -12718,7 +12682,7 @@

See also

- + Redis|bool save() @@ -12768,7 +12732,7 @@

See also

- + array|false scan(int|null $iterator, string|null $pattern = null, int $count = 0, string $type = NULL) @@ -12886,7 +12850,7 @@

See also

- + Redis|int|false scard(string $key) @@ -12949,7 +12913,7 @@

See also

- + mixed script(string $command, mixed ...$args) @@ -13026,7 +12990,7 @@

See also

- + Redis|bool select(int $db) @@ -13084,7 +13048,7 @@

Return Value

- + Redis|string|bool set(string $key, mixed $value, mixed $options = NULL) @@ -13178,7 +13142,7 @@

See also

- + Redis|int|false setBit(string $key, int $idx, bool $value) @@ -13252,7 +13216,7 @@

See also

- + Redis|int|false setRange(string $key, int $index, string $value) @@ -13323,7 +13287,7 @@

See also

- + bool setOption(int $option, mixed $value) @@ -13465,7 +13429,7 @@

See also

- + Redis|bool setex(string $key, int $expire, mixed $value) @@ -13524,7 +13488,7 @@

Return Value

- + Redis|bool setnx(string $key, mixed $value) @@ -13594,7 +13558,7 @@

See also

- + Redis|bool sismember(string $key, mixed $value) @@ -13655,7 +13619,7 @@

Return Value

- + Redis|bool slaveof(string $host = NULL, int $port = 6379) deprecated @@ -13738,7 +13702,7 @@

See also

- + Redis|bool replicaof(string $host = NULL, int $port = 6379) @@ -13821,7 +13785,7 @@

See also

- + Redis|int|false touch(array|string $key_or_array, string ...$more_keys) @@ -13880,7 +13844,7 @@

See also

- + mixed slowlog(string $operation, int $length = 0) @@ -13952,7 +13916,7 @@

See also

- + mixed sort(string $key, array|null $options = null) @@ -14027,7 +13991,7 @@

See also

- + mixed sort_ro(string $key, array|null $options = null) @@ -14086,7 +14050,7 @@

See also

- + array sortAsc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14161,7 +14125,7 @@

Return Value

- + array sortAscAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14236,7 +14200,7 @@

Return Value

- + array sortDesc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14311,7 +14275,7 @@

Return Value

- + array sortDescAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14386,7 +14350,7 @@

Return Value

- + Redis|int|false srem(string $key, mixed $value, mixed ...$other_values) @@ -14461,7 +14425,7 @@

See also

- + array|false sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -14587,7 +14551,7 @@

See also

- + Redis|int|false strlen(string $key) @@ -14646,7 +14610,7 @@

Return Value

- + bool subscribe(array $channels, callable $cb) @@ -14712,7 +14676,7 @@

Return Value

- + Redis|bool swapdb(int $src, int $dst) @@ -14816,7 +14780,7 @@

See also

- + Redis|array time() @@ -14870,7 +14834,7 @@

See also

- + Redis|int|false ttl(string $key) @@ -14933,7 +14897,7 @@

Return Value

- + Redis|int|false type(string $key) @@ -14994,7 +14958,7 @@

See also

See also

- + Redis|array|bool unsubscribe(array $channels) @@ -15137,7 +15101,7 @@

See also

- + Redis|bool unwatch() @@ -15193,7 +15157,7 @@

See also

- + bool|Redis watch(array|string $key, string ...$other_keys) @@ -15241,7 +15205,7 @@

Return Value

- + int|false wait(int $numreplicas, int $timeout) @@ -15300,7 +15264,7 @@

See also

- + int|false xack(string $key, string $group, array $ids) @@ -15353,7 +15317,7 @@

Return Value

- + Redis|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) @@ -15465,7 +15429,7 @@

See also

- + Redis|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) @@ -15538,7 +15502,7 @@

Return Value

- + Redis|bool|array xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) @@ -15606,7 +15570,7 @@

Return Value

- + Redis|int|false xdel(string $key, array $ids) @@ -15686,7 +15650,7 @@

Return Value

- + mixed xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) @@ -15781,7 +15745,7 @@

See also

- + mixed xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) @@ -15856,7 +15820,7 @@

Return Value

- + Redis|int|false xlen(string $key) @@ -15919,7 +15883,7 @@

See also

- + Redis|array|false xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) @@ -16004,7 +15968,7 @@

See also

- + Redis|array|bool xrange(string $key, string $start, string $end, int $count = -1) @@ -16102,7 +16066,7 @@

See also

- + Redis|array|bool xread(array $streams, int $count = -1, int $block = -1) @@ -16205,7 +16169,7 @@

See also

- + Redis|array|bool xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) @@ -16307,7 +16271,7 @@

Return Value

- + Redis|array|bool xrevrange(string $key, string $end, string $start, int $count = -1) @@ -16411,7 +16375,7 @@

See also

- + Redis|int|false xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) @@ -16538,7 +16502,7 @@

See also

- + Redis|int|false zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) @@ -16644,7 +16608,7 @@

See also

- + Redis|int|false zCard(string $key) @@ -16706,7 +16670,7 @@

See also

- + Redis|int|false zCount(string $key, string $start, string $end) @@ -16769,7 +16733,7 @@

See also

- + Redis|float|false zIncrBy(string $key, float $value, mixed $member) @@ -16844,7 +16808,7 @@

See also

- + Redis|int|false zLexCount(string $key, string $min, string $max) @@ -16920,7 +16884,7 @@

See also

- + Redis|array|false zMscore(string $key, mixed $member, mixed ...$other_members) @@ -17006,7 +16970,7 @@

See also

- + Redis|array|false zPopMax(string $key, int $count = null) @@ -17083,7 +17047,7 @@

See also

- + Redis|array|false zPopMin(string $key, int $count = null) @@ -17160,7 +17124,7 @@

See also

- + Redis|array|false zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) @@ -17242,7 +17206,7 @@

See also

- + Redis|array|false zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) @@ -17337,7 +17301,7 @@

See also

- + Redis|array|false zRangeByScore(string $key, string $start, string $end, array $options = []) @@ -17453,7 +17417,7 @@

See also

- + Redis|int|false zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) @@ -17534,7 +17498,7 @@

See also

- + Redis|string|array zRandMember(string $key, array $options = null) @@ -17605,7 +17569,7 @@

See also

- + Redis|int|false zRank(string $key, mixed $member) @@ -17663,7 +17627,7 @@

See also

- + Redis|int|false zRem(mixed $key, mixed $member, mixed ...$other_members) @@ -17747,7 +17711,7 @@

See also

- + Redis|int|false zRemRangeByLex(string $key, string $min, string $max) @@ -17845,7 +17809,7 @@

See also

- + Redis|int|false zRemRangeByRank(string $key, int $start, int $end) @@ -17923,7 +17887,7 @@

See also

- + Redis|int|false zRemRangeByScore(string $key, string $start, string $end) @@ -18003,7 +17967,7 @@

See also

- + Redis|array|false zRevRange(string $key, int $start, int $end, mixed $scores = null) @@ -18084,7 +18048,7 @@

Return Value

- + Redis|array|false zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) @@ -18185,7 +18149,7 @@

See also

- + Redis|array|false zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) @@ -18281,7 +18245,7 @@

Return Value

- + Redis|int|false zRevRank(string $key, mixed $member) @@ -18353,7 +18317,7 @@

See also

- + Redis|float|false zScore(string $key, mixed $member) @@ -18425,7 +18389,7 @@

See also

- + Redis|array|false zdiff(array $keys, array $options = null) @@ -18501,7 +18465,7 @@

See also

- + Redis|int|false zdiffstore(string $dst, array $keys) @@ -18568,7 +18532,7 @@

See also

- + Redis|array|false zinter(array $keys, array|null $weights = null, array|null $options = null) @@ -18661,7 +18625,7 @@

See also

- + Redis|int|false zintercard(array $keys, int $limit = -1) @@ -18746,7 +18710,7 @@

See also

- + Redis|int|false zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) @@ -18849,7 +18813,7 @@

See also

- + Redis|array|false zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -18936,7 +18900,7 @@

See also

- + Redis|array|false zunion(array $keys, array|null $weights = null, array|null $options = null) @@ -19035,7 +18999,7 @@

Return Value

- + Redis|int|false zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) diff --git a/docs/doc-index.html b/docs/doc-index.html index e9d88992bc..5f71012fc4 100644 --- a/docs/doc-index.html +++ b/docs/doc-index.html @@ -193,7 +193,7 @@

A

Redis::connect() — Method in class Redis
Redis::copy() — Method in class Redis
-

Make a copy of a redis key.

+

Make a copy of a key.

RedisCluster::clearlasterror() — Method in class RedisCluster
RedisCluster::client() — Method in class RedisCluster
diff --git a/docs/doctum-search.json b/docs/doctum-search.json index 23b29afb7f..f2cb76692a 100644 --- a/docs/doctum-search.json +++ b/docs/doctum-search.json @@ -1 +1 @@ -{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

"},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":null},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

Compress a value with the currently configured compressor as set with\nRedis::setOption().

"},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

Uncompress the provided argument that has been compressed with the\ncurrently configured compressor as set with Redis::setOption().

"},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

Pack the provided value with the configured serializer and compressor\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

Unpack the provided value with the configured compressor and serializer\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":null},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

Append data to a Redis STRING key.

"},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

Authenticate a Redis connection after its been established.

"},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

Execute a save of the Redis database in the background.

"},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

Asynchronously rewrite Redis' append-only file

"},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

Count the number of set bits in a Redis string.

"},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

"},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

"},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

"},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

"},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

"},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

"},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

POP one or more of the highest or lowest scoring elements from one or more sorted sets.

"},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

"},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

Pop one or more elements off of one or more Redis LISTs.

"},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

Reset any last error on the connection to NULL

"},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":null},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":null},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":null},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends\non the $operation qualifier.

"},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":null},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

Make a copy of a redis key.

"},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

Return the number of keys in the currently selected Redis database.

"},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":null},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

Decrement a Redis integer by 1 or a provided value.

"},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

Decrement a redis integer by a value

"},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

Delete one or more keys from Redis.

"},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

Discard a transaction currently in progress.

"},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

Dump Redis' internal binary representation of a key.

"},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

Have Redis repeat back an arbitrary string to the client.

"},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

Execute a LUA script on the redis server.

"},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

"},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

Execute either a MULTI or PIPELINE block and return the array of replies.

"},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

Test if one or more keys exist.

"},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

"},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

Set a key's expiration to a specific Unix timestamp in seconds. If\nconnected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

Get the expiration of a given key as a unix timestamp

"},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

Get the expriation timestamp of a given Redis key but in milliseconds.

"},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

Deletes every key in all Redis databases

"},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

Deletes all the keys of the currently selected database.

"},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":null},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":null},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":null},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":null},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":null},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":null},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":null},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":null},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":null},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":null},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":null},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

Get the authentication information on the connection, if any.

"},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":null},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":null},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":null},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":null},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

Return the host or Unix socket we are connected to.

"},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

Get the last error returned to us from Redis, if any.

"},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

"},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

Retrieve the value of a configuration setting as set by Redis::setOption()

"},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

Get the persistent connection ID, if there is one.

"},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

"},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

Retrieve a substring of a string by index.

"},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

Get the longest common subsequence between two string keys.

"},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

Get the currently set read timeout on the connection.

"},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

Sets a key and returns any previously set value, if the key already existed.

"},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

Retrieve any set connection timeout

"},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":null},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

Remove one or more fields from a hash.

"},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

Checks whether a field exists in a hash.

"},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

Read every field and value from a hash.

"},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

Increment a hash field's value by an integer

"},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

Increment a hash field by a floating point value

"},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

Retrieve all of the fields of a hash.

"},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

Get the number of fields in a hash.

"},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

Get one or more fields from a hash.

"},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

Add or update one or more hash fields and values

"},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

Get one or more random field from a hash.

"},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":null},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

Set a hash field and value, but only if that field does not exist

"},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

Get the string length of a hash field

"},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

Get all of the values from a hash.

"},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

Iterate over the fields and values of a hash in an incremental fashion.

"},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

Increment a key's value, optionally by a specifc amount.

"},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

Increment a key by a specific integer value

"},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

Increment a numeric key by a floating point value.

"},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

Check if we are currently connected to a Redis instance.

"},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":null},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":null},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":null},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":null},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":""},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":""},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":""},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":""},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":null},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":null},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":null},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":null},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":""},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":null},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":""},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":null},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":null},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":null},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":null},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":null},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":null},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":null},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":null},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

"},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

Add one or more elements to a Redis HyperLogLog key

"},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

Retrieve the cardinality of a Redis HyperLogLog key.

"},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

Merge one or more source HyperLogLog sets into a destination set.

"},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

PING the redis server with an optional string argument.

"},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

Enter into pipeline mode.

"},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":""},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

Subscribe to one or more glob-style patterns

"},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

Get a keys time to live in milliseconds.

"},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

Publish a message to a pubsub channel

"},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":null},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

Unsubscribe from one or more channels by pattern

"},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

Pop one or more elements from the end of a list.

"},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

Return a random key from the current database

"},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

Execute any arbitrary Redis command by name.

"},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

Unconditionally rename a key from $old_name to $new_name

"},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

Renames $key_src to $key_dst but only if newkey does not exist.

"},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

Reset the state of the connection.

"},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

Restore a key by the binary payload generated by the DUMP command.

"},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

Query whether the connected instance is a primary or replica

"},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

"},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

Add one or more values to a Redis SET key.

"},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

"},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

"},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

"},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

"},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

Compute the intersection of one or more sets and return the cardinality of the result.

"},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

"},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

Retrieve every member from a set key.

"},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

Check if one or more values are members of a set.

"},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

"},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

Remove one or more elements from a set.

"},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

Retrieve one or more random members of a set.

"},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

Returns the union of one or more Redis SET keys.

"},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

Perform a union of one or more Redis SET keys and store the result in a new set

"},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

"},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

Incrementally scan the Redis keyspace, with optional pattern and type matching.

"},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

Retrieve the number of members in a Redis set.

"},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

An administrative command used to interact with LUA scripts stored on the server.

"},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

Select a specific Redis database.

"},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

Create or set a Redis STRING key to a value.

"},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

Set a specific bit in a Redis string to zero or one

"},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

Update or append to a Redis string at a specific starting index

"},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

Set a configurable option on the Redis object.

"},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

Set a Redis STRING key with a specific expiration in seconds.

"},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

Set a key to a value, but only if that key does not already exist.

"},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

Check whether a given value is the member of a Redis SET.

"},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

Turn a redis instance into a replica of another or promote a replica\nto a primary.

"},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

"},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

Update one or more keys last modified metadata.

"},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

"},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

Sort the contents of a Redis key in various ways.

"},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

This is simply a read-only variant of the sort command

"},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

Remove one or more values from a Redis SET key.

"},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

Scan the members of a redis SET key.

"},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

Retrieve the length of a Redis STRING key.

"},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

Subscribe to one or more Redis pubsub channels.

"},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

"},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

Retrieve the server time from the connected Redis instance.

"},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

Get the amount of time a Redis key has before it will expire, in seconds.

"},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

Get the type of a given Redis key.

"},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

"},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

Unsubscribe from one or more subscribed channels.

"},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

Remove any previously WATCH'ed keys in a transaction.

"},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":""},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

Block the client up to the provided timeout until a certain number of replicas have confirmed\nrecieving them.

"},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":null},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

Append a message to a stream.

"},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":null},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":null},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

Remove one or more specific IDs from a stream.

"},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

Retrieve information about a stream key.

"},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

Get the number of messages in a Redis STREAM key.

"},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

"},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

Get a range of entries from a STREAM key.

"},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

Consume one or more unconsumed elements in one or more streams.

"},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

Read one or more messages using a consumer group.

"},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

Get a range of entries from a STREAM ke in reverse cronological order.

"},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

Truncate a STREAM key in various ways.

"},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

Add one or more elements and scores to a Redis sorted set.

"},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

Return the number of elements in a sorted set.

"},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

Count the number of members in a sorted set with scores inside a provided range.

"},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

Create or increment the score of a member in a Redis sorted set

"},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

Count the number of elements in a sorted set whos members fall within the provided\nlexographical range.

"},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

Retrieve the score of one or more members in a sorted set.

"},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

Pop one or more of the highest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

Pop one or more of the lowest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

Retrieve a range of elements of a sorted set between a start and end point.

"},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

Retrieve a range of elements from a sorted set by legographical range.

"},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

Retrieve a range of members from a sorted set by their score.

"},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

"},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

Retrieve one or more random members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

Get the rank of a member of a sorted set, by score.

"},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

Remove one or more members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

Remove zero or more elements from a Redis sorted set by legographical range.

"},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

Remove one or more members of a sorted set by their rank.

"},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

Remove one or more members of a sorted set by their score.

"},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

List the members of a Redis sorted set in reverse order

"},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

List members of a Redis sorted set within a legographical range, in reverse order.

"},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

List elements from a Redis sorted set by score, highest to lowest

"},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

Retrieve a member of a sorted set by reverse rank.

"},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

Get the score of a member of a sorted set.

"},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

"},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

Store the difference of one or more sorted sets in a destination sorted set.

"},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

Compute the intersection of one or more sorted sets and return the members

"},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

"},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

"},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

Scan the members of a sorted set incrementally, using a cursor

"},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

Retrieve the union of one or more sorted sets

"},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

Perform a union on one or more Redis sets and store the result in a destination sorted set.

"},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

See Redis::blpop()

"},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

See Redis::brpop()

"},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

See Redis::brpoplpush()

"},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

PING an instance in the redis cluster.

"},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} +{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

"},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":null},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

Compress a value with the currently configured compressor as set with\nRedis::setOption().

"},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

Uncompress the provided argument that has been compressed with the\ncurrently configured compressor as set with Redis::setOption().

"},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

Pack the provided value with the configured serializer and compressor\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

Unpack the provided value with the configured compressor and serializer\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":null},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

Append data to a Redis STRING key.

"},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

Authenticate a Redis connection after its been established.

"},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

Execute a save of the Redis database in the background.

"},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

Asynchronously rewrite Redis' append-only file

"},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

Count the number of set bits in a Redis string.

"},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

"},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

"},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

"},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

"},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

"},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

"},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

POP one or more of the highest or lowest scoring elements from one or more sorted sets.

"},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

"},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

Pop one or more elements off of one or more Redis LISTs.

"},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

Reset any last error on the connection to NULL

"},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":null},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":null},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":null},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends\non the $operation qualifier.

"},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":null},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

Make a copy of a key.

"},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

Return the number of keys in the currently selected Redis database.

"},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":null},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

Decrement a Redis integer by 1 or a provided value.

"},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

Decrement a redis integer by a value

"},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

Delete one or more keys from Redis.

"},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

Discard a transaction currently in progress.

"},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

Dump Redis' internal binary representation of a key.

"},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

Have Redis repeat back an arbitrary string to the client.

"},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

Execute a LUA script on the redis server.

"},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

"},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

Execute either a MULTI or PIPELINE block and return the array of replies.

"},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

Test if one or more keys exist.

"},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

"},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

Set a key's expiration to a specific Unix timestamp in seconds. If\nconnected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

Get the expiration of a given key as a unix timestamp

"},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

Get the expriation timestamp of a given Redis key but in milliseconds.

"},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

Deletes every key in all Redis databases

"},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

Deletes all the keys of the currently selected database.

"},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":null},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":null},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":null},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":null},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":null},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":null},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":null},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":null},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":null},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":null},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":null},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

Get the authentication information on the connection, if any.

"},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":null},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":null},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":null},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":null},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

Return the host or Unix socket we are connected to.

"},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

Get the last error returned to us from Redis, if any.

"},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

"},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

Retrieve the value of a configuration setting as set by Redis::setOption()

"},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

Get the persistent connection ID, if there is one.

"},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

"},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

Retrieve a substring of a string by index.

"},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

Get the longest common subsequence between two string keys.

"},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

Get the currently set read timeout on the connection.

"},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

Sets a key and returns any previously set value, if the key already existed.

"},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

Retrieve any set connection timeout

"},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":null},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

Remove one or more fields from a hash.

"},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

Checks whether a field exists in a hash.

"},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

Read every field and value from a hash.

"},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

Increment a hash field's value by an integer

"},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

Increment a hash field by a floating point value

"},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

Retrieve all of the fields of a hash.

"},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

Get the number of fields in a hash.

"},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

Get one or more fields from a hash.

"},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

Add or update one or more hash fields and values

"},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

Get one or more random field from a hash.

"},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":null},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

Set a hash field and value, but only if that field does not exist

"},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

Get the string length of a hash field

"},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

Get all of the values from a hash.

"},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

Iterate over the fields and values of a hash in an incremental fashion.

"},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

Increment a key's value, optionally by a specifc amount.

"},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

Increment a key by a specific integer value

"},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

Increment a numeric key by a floating point value.

"},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

Check if we are currently connected to a Redis instance.

"},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":null},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":null},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":null},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":null},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":""},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":""},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":""},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":""},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":null},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":null},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":null},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":null},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":""},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":null},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":""},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":null},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":null},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":null},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":null},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":null},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":null},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":null},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":null},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

"},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

Add one or more elements to a Redis HyperLogLog key

"},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

Retrieve the cardinality of a Redis HyperLogLog key.

"},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

Merge one or more source HyperLogLog sets into a destination set.

"},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

PING the redis server with an optional string argument.

"},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

Enter into pipeline mode.

"},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":""},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

Subscribe to one or more glob-style patterns

"},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

Get a keys time to live in milliseconds.

"},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

Publish a message to a pubsub channel

"},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":null},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

Unsubscribe from one or more channels by pattern

"},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

Pop one or more elements from the end of a list.

"},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

Return a random key from the current database

"},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

Execute any arbitrary Redis command by name.

"},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

Unconditionally rename a key from $old_name to $new_name

"},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

Renames $key_src to $key_dst but only if newkey does not exist.

"},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

Reset the state of the connection.

"},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

Restore a key by the binary payload generated by the DUMP command.

"},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

Query whether the connected instance is a primary or replica

"},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

"},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

Add one or more values to a Redis SET key.

"},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

"},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

"},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

"},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

"},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

Compute the intersection of one or more sets and return the cardinality of the result.

"},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

"},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

Retrieve every member from a set key.

"},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

Check if one or more values are members of a set.

"},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

"},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

Remove one or more elements from a set.

"},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

Retrieve one or more random members of a set.

"},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

Returns the union of one or more Redis SET keys.

"},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

Perform a union of one or more Redis SET keys and store the result in a new set

"},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

"},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

Incrementally scan the Redis keyspace, with optional pattern and type matching.

"},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

Retrieve the number of members in a Redis set.

"},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

An administrative command used to interact with LUA scripts stored on the server.

"},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

Select a specific Redis database.

"},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

Create or set a Redis STRING key to a value.

"},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

Set a specific bit in a Redis string to zero or one

"},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

Update or append to a Redis string at a specific starting index

"},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

Set a configurable option on the Redis object.

"},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

Set a Redis STRING key with a specific expiration in seconds.

"},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

Set a key to a value, but only if that key does not already exist.

"},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

Check whether a given value is the member of a Redis SET.

"},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

Turn a redis instance into a replica of another or promote a replica\nto a primary.

"},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

"},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

Update one or more keys last modified metadata.

"},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

"},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

Sort the contents of a Redis key in various ways.

"},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

This is simply a read-only variant of the sort command

"},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

Remove one or more values from a Redis SET key.

"},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

Scan the members of a redis SET key.

"},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

Retrieve the length of a Redis STRING key.

"},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

Subscribe to one or more Redis pubsub channels.

"},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

"},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

Retrieve the server time from the connected Redis instance.

"},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

Get the amount of time a Redis key has before it will expire, in seconds.

"},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

Get the type of a given Redis key.

"},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

"},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

Unsubscribe from one or more subscribed channels.

"},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

Remove any previously WATCH'ed keys in a transaction.

"},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":""},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

Block the client up to the provided timeout until a certain number of replicas have confirmed\nrecieving them.

"},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":null},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

Append a message to a stream.

"},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":null},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":null},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

Remove one or more specific IDs from a stream.

"},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

Retrieve information about a stream key.

"},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

Get the number of messages in a Redis STREAM key.

"},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

"},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

Get a range of entries from a STREAM key.

"},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

Consume one or more unconsumed elements in one or more streams.

"},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

Read one or more messages using a consumer group.

"},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

Get a range of entries from a STREAM ke in reverse cronological order.

"},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

Truncate a STREAM key in various ways.

"},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

Add one or more elements and scores to a Redis sorted set.

"},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

Return the number of elements in a sorted set.

"},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

Count the number of members in a sorted set with scores inside a provided range.

"},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

Create or increment the score of a member in a Redis sorted set

"},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

Count the number of elements in a sorted set whos members fall within the provided\nlexographical range.

"},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

Retrieve the score of one or more members in a sorted set.

"},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

Pop one or more of the highest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

Pop one or more of the lowest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

Retrieve a range of elements of a sorted set between a start and end point.

"},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

Retrieve a range of elements from a sorted set by legographical range.

"},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

Retrieve a range of members from a sorted set by their score.

"},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

"},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

Retrieve one or more random members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

Get the rank of a member of a sorted set, by score.

"},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

Remove one or more members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

Remove zero or more elements from a Redis sorted set by legographical range.

"},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

Remove one or more members of a sorted set by their rank.

"},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

Remove one or more members of a sorted set by their score.

"},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

List the members of a Redis sorted set in reverse order

"},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

List members of a Redis sorted set within a legographical range, in reverse order.

"},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

List elements from a Redis sorted set by score, highest to lowest

"},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

Retrieve a member of a sorted set by reverse rank.

"},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

Get the score of a member of a sorted set.

"},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

"},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

Store the difference of one or more sorted sets in a destination sorted set.

"},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

Compute the intersection of one or more sorted sets and return the members

"},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

"},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

"},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

Scan the members of a sorted set incrementally, using a cursor

"},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

Retrieve the union of one or more sorted sets

"},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

Perform a union on one or more Redis sets and store the result in a destination sorted set.

"},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

See Redis::blpop()

"},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

See Redis::brpop()

"},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

See Redis::brpoplpush()

"},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

PING an instance in the redis cluster.

"},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} diff --git a/docs/renderer.index b/docs/renderer.index index 97fb749e23..6361316e97 100644 --- a/docs/renderer.index +++ b/docs/renderer.index @@ -1 +1 @@ -O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"d303f5f87803a7ca760f478c154e5ca8082f29ee";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:14:"RedisException";s:40:"d303f5f87803a7ca760f478c154e5ca8082f29ee";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file +O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"4f4f62f9f49eb59c17c3dda8e0c3ae397a6df977";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:14:"RedisException";s:40:"4f4f62f9f49eb59c17c3dda8e0c3ae397a6df977";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file From 65c965638cf5412ac21d72df36b029789b13268b Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Thu, 10 Nov 2022 12:06:35 -0800 Subject: [PATCH 1710/1986] Add a link to our WIP API docs. (#2252) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index efa85e9b17..a3a1dc323d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ This code has been developed and maintained by Owlient from November 2009 to Mar You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)), p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)), or n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)). +## [API Documentation](https://phpredis.github.io/phpredis) +These are a work in progress, but will eventually replace our **ONE README TO RULE THEM ALL** docs. + ## Supporting the project PhpRedis will always be free and open source software, but if you or your company has found it useful please consider supporting the project. Developing a large, complex, and performant library like PhpRedis takes a great deal of time and effort, and support would be appreciated! :heart: From ef4699c726300a631fc9e1439b1c2aaa2004a3e6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 10 Nov 2022 19:31:38 -0800 Subject: [PATCH 1711/1986] Documentation: Rework more formatting and add example sections. [skip ci] --- docs/Redis.html | 1345 +++++++++++++++++++-------------------- docs/doc-index.html | 8 +- docs/doctum-search.json | 2 +- docs/renderer.index | 2 +- redis.stub.php | 574 ++++++++--------- redis_arginfo.h | 2 +- redis_legacy_arginfo.h | 2 +- 7 files changed, 919 insertions(+), 1016 deletions(-) diff --git a/docs/Redis.html b/docs/Redis.html index 05d15dcd77..575df3d272 100644 --- a/docs/Redis.html +++ b/docs/Redis.html @@ -425,8 +425,7 @@

Methods

config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) -

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends -on the $operation qualifier.

+

Execute the Redis CONFIG command in a variety of ways.

@@ -624,8 +623,7 @@

Methods

expireAt(string $key, int $timestamp, string|null $mode = NULL) -

Set a key's expiration to a specific Unix timestamp in seconds. If -connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

+

Set a key to expire at an exact unix timestamp.

@@ -839,8 +837,7 @@

Methods

getDBNum() -

No description

-
+

Get the database number PhpRedis thinks we're connected to.

@@ -3603,7 +3600,7 @@

See also

- + Redis|array|null|false blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) @@ -3614,11 +3611,7 @@

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified -timeout. This method may be called in two distinct ways, of which examples are provided below.

// Variadic, with the final argument a timeout.
-$redis->blPop('list1', 'list2', 'list3', 1.5);
-
-// Alternatively, you can send an array of keys
-$relay->blPop(['list1', 'list2', 'list3'], 1.5);

+timeout. This method may be called in two distinct ways, of which examples are provided below.

Parameters

@@ -3668,13 +3661,22 @@

See also

+

Examples

+ + + + + +
$redis->blPop('list1', 'list2', 'list3', 1.5);
+$relay->blPop(['list1', 'list2', 'list3'], 1.5);
+

- + Redis|array|null|false brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) @@ -3744,7 +3746,7 @@

See also

- + Redis|string|false brpoplpush(string $src, string $dst, int|float $timeout) @@ -3809,7 +3811,7 @@

See also

- + Redis|array|false bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3821,11 +3823,6 @@

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified timeout if no elements are available.

Following are examples of the two main ways to call this method.

-
// Method 1 - Variadic, with the last argument being our timeout
-$redis->bzPopMax('key1', 'key2', 'key3', 1.5);
-
-// Method 2 - A single array of keys, followed by the timeout
-$redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);

NOTE: We reccomend calling this function with an array and a timeout as the other strategy may be deprecated in future versions of PhpRedis

@@ -3877,13 +3874,22 @@

See also

+

Examples

+ + + + + +
$redis->bzPopMax('key1', 'key2', 'key3', 1.5);
+$redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);
+

- + Redis|array|false bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3954,7 +3960,7 @@

See also

- + Redis|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -4017,7 +4023,7 @@

Return Value

- + Redis|array|null|false zmpop(array $keys, string $from, int $count = 1) @@ -4081,7 +4087,7 @@

See also

- + Redis|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -4152,7 +4158,7 @@

See also

- + Redis|array|null|false lmpop(array $keys, string $from, int $count = 1) @@ -4217,7 +4223,7 @@

See also

- + bool clearLastError() @@ -4227,14 +4233,7 @@

-

Reset any last error on the connection to NULL

$redis = new Redis(['host' => 'localhost']);
-
-$redis->set('string', 'this_is_a_string');
-$redis->smembers('string');
-
-var_dump($redis->getLastError());
-$redis->clearLastError();
-var_dump($redis->getLastError());

+

Reset any last error on the connection to NULL

@@ -4262,13 +4261,26 @@

See also

+

Examples

+ + + + + +
$redis = new Redis(['host' => 'localhost']);
+$redis->set('string', 'this_is_a_string');
+$redis->smembers('string');
+var_dump($redis->getLastError());
+$redis->clearLastError();
+var_dump($redis->getLastError());
+

- + mixed client(string $opt, mixed ...$args) @@ -4316,7 +4328,7 @@

Return Value

- + bool close() @@ -4349,7 +4361,7 @@

Return Value

- + mixed command(string $opt = null, string|array $arg) @@ -4397,7 +4409,7 @@

Return Value

- + mixed config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) @@ -4407,13 +4419,8 @@

-

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends -on the $operation qualifier.

Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.

-
$redis->config('GET', 'timeout');
-$redis->config('GET', ['timeout', 'databases']);
-
-$redis->config('SET', 'timeout', 30);
-$redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);

+

Execute the Redis CONFIG command in a variety of ways.

What the command does in particular depends on the $operation qualifier. +Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.

Parameters

@@ -4422,23 +4429,17 @@

Parameters

string $operation -

The CONFIG subcommand to execute -@param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or -an array of settings or settings and values. -Note: Redis 7.0.0 is required to send an array of settings. -@param string $value The setting value when the operation is SET.

-

@return mixed Can return various things depending on arguments sent.

-

https://redis.io/commands/config

+

The CONFIG operation to execute (e.g. GET, SET, REWRITE).

array|string|null $key_or_settings - +

One or more keys or values.

string|null $value - +

The value if this is a CONFIG SET operation.

@@ -4454,14 +4455,36 @@

Return Value

+

See also

+ + + + + + +
+ https://redis.io/commands/config +
+ +

Examples

+ + + + + +
$redis->config('GET', 'timeout');
+$redis->config('GET', ['timeout', 'databases']);
+$redis->config('SET', 'timeout', 30);
+$redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);
+

- + bool connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null) @@ -4534,7 +4557,7 @@

Return Value

- + Redis|bool copy(string $src, string $dst, array $options = null) @@ -4544,44 +4567,7 @@

-

Make a copy of a key.

$redis = new Redis(['host' => 'localhost']);
-
-$redis->pipeline()
-      ->select(1)
-      ->del('newkey')
-      ->select(0)
-      ->del('newkey')
-      ->mset(['source1' => 'value1', 'exists' => 'old_value'])
-      ->exec();
-
-// Will succeed, as 'newkey' doesn't exist
-var_dump($redis->copy('source1', 'newkey'));
-
-// Will succeed, because 'newkey' doesn't exist in DB 1
-var_dump($redis->copy('source1', 'newkey', ['db' => 1]));
-
-// Will fail, because 'exists' does exist
-var_dump($redis->copy('source1', 'exists'));
-
-// Will succeed, because even though 'exists' is a key, we sent the REPLACE option.
-var_dump($redis->copy('source1', 'exists', ['REPLACE' => true]));
-

Available Options

- - - - - - - - - - - - - - - -
OPTIONTYPEDESCRIPTION
OPT_MAX_RETRIESintfoo

+

Make a copy of a key.

$redis = new Redis(['host' => 'localhost']);

Parameters

@@ -4632,13 +4618,32 @@

See also

+

Examples

+ + + + + +
$redis->pipeline()
+ ->select(1)
+ ->del('newkey')
+ ->select(0)
+ ->del('newkey')
+ ->mset(['source1' => 'value1', 'exists' => 'old_value'])
+ ->exec();
+
+var_dump($redis->copy('source1', 'newkey'));
+var_dump($redis->copy('source1', 'newkey', ['db' => 1]));
+var_dump($redis->copy('source1', 'exists'));
+var_dump($redis->copy('source1', 'exists', ['REPLACE' => true]));
+

- + Redis|int|false dbSize() @@ -4648,15 +4653,7 @@

-

Return the number of keys in the currently selected Redis database.

$redis = new Redis(['host' => 'localhost']);
-
-$redis->flushdb();
-
-$redis->set('foo', 'bar');
-var_dump($redis->dbsize());
-
-$redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']);
-var_dump($redis->dbsize());

+

Return the number of keys in the currently selected Redis database.

@@ -4683,13 +4680,26 @@

See also

+

Examples

+ + + + + +
$redis = new Redis(['host' => 'localhost']);
+$redis->flushdb();
+$redis->set('foo', 'bar');
+var_dump($redis->dbsize());
+$redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']);
+var_dump($redis->dbsize());
+

- + Redis|string debug(string $key) @@ -4732,7 +4742,7 @@

Return Value

- + Redis|int|false decr(string $key, int $by = 1) @@ -4804,7 +4814,7 @@

See also

- + Redis|int|false decrBy(string $key, int $value) @@ -4814,7 +4824,11 @@

-

Decrement a redis integer by a value

+

Decrement a redis integer by a value

$redis = new Redis(['host' => 'localhost');
+
+$redis->set('counter', 3);
+var_dump($redis->decrby('counter', 1));
+var_dump($redis->decrby('counter', 2));

Parameters

@@ -4838,18 +4852,7 @@

Return Value

- +
Redis|int|false

The new value of the key or false on failure.

-
<?php
-$redis = new Redis(['host' => 'localhost');
-
-$redis->set('counter', 3);
-var_dump($redis->decrby('counter', 1));
-var_dump($redis->decrby('counter', 2));
-
-// --- OUTPUT ---
-// int(2)
-// int(0)
-?>

The new value of the key or false on failure.

@@ -4873,7 +4876,7 @@

See also

- + Redis|int|false del(array|string $key, string ...$other_keys) @@ -4883,7 +4886,17 @@

-

Delete one or more keys from Redis.

+

Delete one or more keys from Redis.

This method can be called in two distinct ways. The first is to pass a single array +of keys to delete, and the second is to pass N arguments, all names of keys. See +below for an example of both strategies.

+
$redis = new Redis(['host' => 'localhost']);
+
+for ($i = 0; $i < 5; $i++) {
+    $redis->set("key:$i", "val:$i");
+}
+
+var_dump($redis->del('key:0', 'key:1'));
+var_dump($redis->del(['key:2', 'key:3', 'key:4']));

Parameters

@@ -4897,24 +4910,7 @@

Parameters

string ...$other_keys -

One or more additional keys passed in a variadic fashion.

-

This method can be called in two distinct ways. The first is to pass a single array -of keys to delete, and the second is to pass N arguments, all names of keys. See -below for an example of both strategies.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-for ($i = 0; $i < 5; $i++) {
-    $redis->set("key:$i", "val:$i");
-}
-
-var_dump($redis->del('key:0', 'key:1'));
-var_dump($redis->del(['key:2', 'key:3', 'key:4']));
-
-// --- OUTPUT ---
-// int(2)
-// int(3)
-?>
+

One or more additional keys passed in a variadic fashion.

@@ -4924,7 +4920,7 @@

Return Value

- +
Redis|int|false

The number of keys that were deleted

@@ -4948,7 +4944,7 @@

See also

- + Redis|int|false delete(array|string $key, string ...$other_keys) deprecated @@ -5003,7 +4999,7 @@

Return Value

- + Redis|bool discard() @@ -5013,18 +5009,7 @@

-

Discard a transaction currently in progress.

-
-
- -

Return Value

- - - - - +
Redis|bool

True if we could discard the transaction.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
+                            

Discard a transaction currently in progress.

$redis = new Redis(['host' => 'localhost']);
 
 $redis->multi()->set('foo', 'bar')->get('foo');
 
@@ -5035,9 +5020,16 @@ 

Return Value

$redis->discard(); // Redis::ATOMIC -$redis->getMode(); +$redis->getMode();

+ +
+ +

Return Value

-?>
+ + +
Redis|bool

True if we could discard the transaction.

@@ -5050,7 +5042,7 @@

Return Value

- + Redis|string dump(string $key) @@ -5060,7 +5052,19 @@

-

Dump Redis' internal binary representation of a key.

+

Dump Redis' internal binary representation of a key.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('zset');
+
+$redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two');
+
+// Retrieve the binary representation of the zset
+$binary = $redis->dump('zset');
+
+// Retore it to a different name
+$redis->restore('new-zset', 0, $binary);
+
+$redis->zRange('new-zset', 0, -1, true);

Parameters

@@ -5079,28 +5083,7 @@

Return Value

- +
Redis|string

A binary string representing the key's value.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('zset');
-
-$redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two');
-
-// Retrieve the binary representation of the zset
-$binary = $redis->dump('zset');
-
-// Retore it to a different name
-$redis->restore('new-zset', 0, $binary);
-
-// Array
-// (
-//     [zero] => 0
-//     [one] => 1
-//     [two] => 2
-// )
-$redis->zRange('new-zset', 0, -1, true);
-?>

A binary string representing the key's value.

@@ -5124,7 +5107,7 @@

See also

- + Redis|string|false echo(string $str) @@ -5134,7 +5117,9 @@

-

Have Redis repeat back an arbitrary string to the client.

+

Have Redis repeat back an arbitrary string to the client.

$redis = new Redis(['host' => 'localhost']);
+
+var_dump($redis->echo('Hello, World'));

Parameters

@@ -5153,16 +5138,7 @@

Return Value

- +
Redis|string|false

The string sent to Redis or false on failure.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-var_dump($redis->echo('Hello, World'));
-
-// --- OUTPUT ---
-// string(12) "Hello, World"
-
-?>

The string sent to Redis or false on failure.

@@ -5186,7 +5162,7 @@

See also

- + mixed eval(string $script, array $args = [], int $num_keys = 0) @@ -5252,7 +5228,7 @@

See also

- + mixed eval_ro(string $script_sha, array $args = [], int $num_keys = 0) @@ -5317,7 +5293,7 @@

See also

- + mixed evalsha(string $sha1, array $args = [], int $num_keys = 0) @@ -5357,7 +5333,7 @@

Return Value

- +
mixed

Returns whatever the specific script does.

@@ -5388,7 +5364,7 @@

See also

- + mixed evalsha_ro(string $sha1, array $args = [], int $num_keys = 0) @@ -5453,7 +5429,7 @@

See also

- + Redis|array|false exec() @@ -5463,17 +5439,7 @@

-

Execute either a MULTI or PIPELINE block and return the array of replies.

-
-
- -

Return Value

- - - - - +
Redis|array|false

The array of pipeline'd or multi replies or false on failure.

-
$redis = new Redis(['host' => 'localhost']);
+                            

Execute either a MULTI or PIPELINE block and return the array of replies.

$redis = new Redis(['host' => 'localhost']);
 
 $res = $redis->multi()
              ->set('foo', 'bar')
@@ -5482,20 +5448,16 @@ 

Return Value

->rpush('list', 'one', 'two', 'three') ->exec(); -var_dump($res); +var_dump($res);

+ +
+ +

Return Value

-// --- OUTPUT --- -// array(4) { -// [0]=> -// bool(true) // set('foo', 'bar') -// [1]=> -// string(3) "bar" // get('foo') -// [2]=> -// int(1) // del('list') -// [3]=> -// int(3) // rpush('list', 'one', 'two', 'three') -// } -?>
+ + +
Redis|array|false

The array of pipeline'd or multi replies or false on failure.

@@ -5539,7 +5501,7 @@

See also

- + Redis|int|bool exists(mixed $key, mixed ...$other_keys) @@ -5549,7 +5511,21 @@

-

Test if one or more keys exist.

+

Test if one or more keys exist.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->multi()
+      ->mset(['k1' => 'v1', 'k2' => 'v2', 'k3' => 'v3', 'k4' => 'v4'])
+      ->exec();
+
+// Using a single array of keys
+var_dump($redis->exists(['k1', 'k2', 'k3']));
+
+// Calling via variadic arguments
+var_dump($redis->exists('k4', 'k5', 'notakey'));
+
+// --- OUTPUT ---
+// int(3)
+// int(1)

Parameters

@@ -5574,24 +5550,7 @@

Return Value

- +
Redis|int|bool

The number of keys that do exist and false on failure

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->multi()
-      ->mset(['k1' => 'v1', 'k2' => 'v2', 'k3' => 'v3', 'k4' => 'v4'])
-      ->exec();
-
-// Using a single array of keys
-var_dump($redis->exists(['k1', 'k2', 'k3']));
-
-// Calling via variadic arguments
-var_dump($redis->exists('k4', 'k5', 'notakey'));
-
-// --- OUTPUT ---
-// int(3)
-// int(1)
-?>

The number of keys that do exist and false on failure

@@ -5615,7 +5574,7 @@

See also

- + Redis|bool expire(string $key, int $timeout, string|null $mode = NULL) @@ -5647,11 +5606,11 @@

Parameters

string|null $mode

A two character modifier that changes how the -command works. -NX - Set expiry only if key has no expiry +command works.

+
NX - Set expiry only if key has no expiry
 XX - Set expiry only if key has an expiry
 LT - Set expiry only when new expiry is < current expiry
-GT - Set expiry only when new expiry is > current expiry

+GT - Set expiry only when new expiry is > current expiry
@@ -5661,7 +5620,7 @@

Return Value

- +
Redis|bool

True if an expiration was set and false otherwise.

@@ -5685,7 +5644,7 @@

See also

- + Redis|bool expireAt(string $key, int $timestamp, string|null $mode = NULL) @@ -5695,8 +5654,7 @@

-

Set a key's expiration to a specific Unix timestamp in seconds. If -connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

+

Set a key to expire at an exact unix timestamp.

Parameters

@@ -5705,17 +5663,17 @@

Parameters

string $key - +

The key to set an expiration on.

int $timestamp - +

The unix timestamp to expire at.

string|null $mode - +

An option 'mode' that modifies how the command acts (see Redis::expire).

@@ -5725,7 +5683,7 @@

Return Value

- +
Redis|bool

True if an expiration was set, false if not.

@@ -5735,15 +5693,23 @@

See also

+ + + + + + + + - +
+ https://redis.io/commands/expireat +
+ https://redis.io/commands/expire +
Redis::expire For a description of the mode argument. - -@param string $key The key to set an expiration on. -@param string $mode A two character modifier that changes how the - command works.
@@ -5754,7 +5720,7 @@

See also

- + Redis|bool failover(array|null $to = null, bool $abort = false, int $timeout = 0) @@ -5807,7 +5773,7 @@

Return Value

- + Redis|int|false expiretime(string $key) @@ -5817,7 +5783,14 @@

-

Get the expiration of a given key as a unix timestamp

+

Get the expiration of a given key as a unix timestamp

$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('expiry-key', 'this will last a very long time');
+
+// Expire this key at 2222/02/22 02:22:22 GMT
+$redis->expireAt('expiry-key', 7955144542);
+
+var_dump($redis->expiretime('expiry-key'));

Parameters

@@ -5837,21 +5810,7 @@

Return Value

Redis|int|false

The timestamp when the key expires, or -1 if the key has no expiry -and -2 if the key doesn't exist.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->set('expiry-key', 'this will last a very long time');
-
-// Expire this key at 2222/02/22 02:22:22 GMT
-$redis->expireAt('expiry-key', 7955144542);
-
-var_dump($redis->expiretime('expiry-key'));
-
-// --- OUTPUT ---
-// int(7955144542)
-
-?>php
+and -2 if the key doesn't exist.

@@ -5875,7 +5834,7 @@

See also

- + Redis|int|false pexpiretime(string $key) @@ -5936,7 +5895,7 @@

See also

- + Redis|bool flushAll(bool|null $sync = null) @@ -5955,11 +5914,7 @@

Parameters

bool|null $sync -

Whether to perform the task in a blocking or non-blocking way. -when TRUE, PhpRedis will execute FLUSHALL SYNC, and when FALSE we -will execute FLUSHALL ASYNC. If the argument is omitted, we -simply execute FLUSHALL and whether it is SYNC or ASYNC depends -on Redis' lazyfree-lazy-user-flush config setting.

+

Whether to perform the task in a blocking or non-blocking way.

@@ -5975,6 +5930,17 @@

Return Value

+

See also

+ + + + + + +
+ https://redis.io/commands/flushall +
+

@@ -5982,7 +5948,7 @@

Return Value

- + Redis|bool flushDB(bool|null $sync = null) @@ -6001,11 +5967,7 @@

Parameters

bool|null $sync -

Whether to perform the task in a blocking or non-blocking way. -when TRUE, PhpRedis will execute FLUSHDB SYNC, and when FALSE we -will execute FLUSHDB ASYNC. If the argument is omitted, we -simply execute FLUSHDB and whether it is SYNC or ASYNC depends -on Redis' lazyfree-lazy-user-flush config setting.

+

Whether to perform the task in a blocking or non-blocking way.

@@ -6021,6 +5983,17 @@

Return Value

+

See also

+ + + + + + +
+ https://redis.io/commands/flush +
+

@@ -6028,7 +6001,7 @@

Return Value

- + Redis|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) @@ -6091,7 +6064,7 @@

Return Value

- + Redis|float|false geodist(string $key, string $src, string $dst, string|null $unit = null) @@ -6149,7 +6122,7 @@

Return Value

- + Redis|array|false geohash(string $key, string $member, string ...$other_members) @@ -6202,7 +6175,7 @@

Return Value

- + Redis|array|false geopos(string $key, string $member, string ...$other_members) @@ -6255,7 +6228,7 @@

Return Value

- + mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -6323,7 +6296,7 @@

Return Value

- + mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -6391,7 +6364,7 @@

Return Value

- + mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6454,7 +6427,7 @@

Return Value

- + mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6517,7 +6490,7 @@

Return Value

- + array geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) @@ -6580,7 +6553,7 @@

Return Value

- + Redis|array|int|false geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) @@ -6648,7 +6621,7 @@

Return Value

- + mixed get(string $key) @@ -6691,7 +6664,7 @@

Return Value

- + mixed getAuth() @@ -6735,7 +6708,7 @@

See also

- + Redis|int|false getBit(string $key, int $idx) @@ -6783,7 +6756,7 @@

Return Value

- + Redis|string|bool getEx(string $key, array $options = []) @@ -6831,7 +6804,7 @@

Return Value

- + int getDBNum() @@ -6841,8 +6814,7 @@

-

No description

- +

Get the database number PhpRedis thinks we're connected to.

This value is updated internally in PhpRedis each time Redis::select is called.

@@ -6851,12 +6823,30 @@

Return Value

- +
int

database we're connected to.

+

See also

+ + + + + + + + + + +
+ +Redis::select +
+ https://redis.io/commands/select +
+

@@ -6864,7 +6854,7 @@

Return Value

- + Redis|string|bool getDel(string $key) @@ -6907,7 +6897,7 @@

Return Value

- + string getHost() @@ -6939,7 +6929,7 @@

Return Value

- + string|null getLastError() @@ -6971,7 +6961,7 @@

Return Value

- + int getMode() @@ -7003,7 +6993,7 @@

Return Value

- + mixed getOption(int $option) @@ -7057,7 +7047,7 @@

See also

- + string|null getPersistentID() @@ -7089,7 +7079,7 @@

Return Value

- + int getPort() @@ -7121,7 +7111,7 @@

Return Value

- + Redis|string|false getRange(string $key, int $start, int $end) @@ -7131,7 +7121,16 @@

-

Retrieve a substring of a string by index.

+

Retrieve a substring of a string by index.

$redis = new Redis(['host' => 'localhost']);
+
+$word = 'Supercalifragilisticexpialidocious';
+$redis->set('silly-word', $word);
+
+// string "super"
+var_dump($redis->getRange('silly-word', 0, 4));
+
+// string(7) "docious"
+var_dump($redis->getRange('silly-word', -7, -1));

Parameters

@@ -7160,25 +7159,23 @@

Return Value

- +
Redis|string|false

The substring or false on failure.

-

-<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$word = 'Supercalifragilisticexpialidocious';
-$redis->set('silly-word', $word);
-
-// string "super"
-var_dump($redis->getRange('silly-word', 0, 4));
-
-// string(7) "docious"
-var_dump($redis->getRange('silly-word', -7, -1));
-?>

The substring or false on failure.

+

See also

+ + + + + + +
+ https://redis.io/commands/getrange +
+

@@ -7186,7 +7183,7 @@

Return Value

- + Redis|string|array|int|false lcs(string $key1, string $key2, array|null $options = NULL) @@ -7196,7 +7193,13 @@

-

Get the longest common subsequence between two string keys.

+

Get the longest common subsequence between two string keys.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc');
+$redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc');
+
+// string(37) "acccgcacggcaagtcgttccagcaactggcgctagc"
+var_dump($redis->lcs('seq1', 'seq2'));

Parameters

@@ -7236,32 +7239,31 @@

Return Value

- +
Redis|string|array|int|false

Various reply types depending on options.

-

-<?php
-<?php
-
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc');
-$redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc');
-
-// string(37) "acccgcacggcaagtcgttccagcaactggcgctagc"
-var_dump($redis->lcs('seq1', 'seq2'));
-?>

Various reply types depending on options.

- - + +

See also

+ + + + + + +
+ https://redis.io/commands/lcs +
+ +

- + float getReadTimeout() @@ -7293,7 +7295,7 @@

Return Value

- + Redis|string|false getset(string $key, mixed $value) @@ -7303,7 +7305,15 @@

-

Sets a key and returns any previously set value, if the key already existed.

+

Sets a key and returns any previously set value, if the key already existed.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('captain');
+
+// bool(false)
+var_dump($redis->getset('captain', 'Pike'));
+
+// string(4) "Pike"
+var_dump($redis->getset('captain', 'Kirk'));

Parameters

@@ -7327,23 +7337,23 @@

Return Value

- +
Redis|string|false

The old value of the key or false if it didn't exist.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('captain');
-
-// bool(false)
-var_dump($redis->getset('captain', 'Pike'));
-
-// string(4) "Pike"
-var_dump($redis->getset('captain', 'Kirk'));
-?>

The old value of the key or false if it didn't exist.

+

See also

+ + + + + + +
+ https://redis.io/commands/getset +
+

@@ -7351,7 +7361,7 @@

Return Value

- + float|false getTimeout() @@ -7383,7 +7393,7 @@

Return Value

- + int|false getTransferredBytes() @@ -7416,7 +7426,7 @@

Return Value

- + Redis|int|false hDel(string $key, string $field, string ...$other_fields) @@ -7426,7 +7436,14 @@

-

Remove one or more fields from a hash.

+

Remove one or more fields from a hash.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('people');
+
+$redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
+
+// int(1)
+$redis->hDel('comms', 'Mallory', 'Archibald');

Parameters

@@ -7455,17 +7472,7 @@

Return Value

- +
Redis|int|false

The number of fields actually removed.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('people');
-
-$redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
-
-// int(1)
-$redis->hDel('comms', 'Mallory', 'Archibald');
-?>

The number of fields actually removed.

@@ -7489,7 +7496,7 @@

See also

- + Redis|bool hExists(string $key, string $field) @@ -7499,7 +7506,14 @@

-

Checks whether a field exists in a hash.

+

Checks whether a field exists in a hash.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('captains');
+
+$redis->hmset('captains', ['Kirk' => 'Enterprise', 'Picard' => 'Enterprise-D', 'Sisko' => 'Defiant']);
+
+$redis->hExists('captains', 'Pike');
+$redis->hExists('captains', 'Picard');

Parameters

@@ -7523,20 +7537,7 @@

Return Value

- +
Redis|bool

True if it exists, false if not.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('captains');
-
-$redis->hmset('captains', ['Kirk' => 'Enterprise', 'Picard' => 'Enterprise-D', 'Sisko' => 'Defiant']);
-
-bool(false)
-$redis->hExists('captains', 'Pike');
-
-bool(true)
-$redis->hExists('captains', 'Picard');
-?>

True if it exists, false if not.

@@ -7560,7 +7561,7 @@

See also

- + mixed hGet(string $key, string $member) @@ -7608,7 +7609,7 @@

Return Value

- + Redis|array|false hGetAll(string $key) @@ -7618,7 +7619,21 @@

-

Read every field and value from a hash.

+

Read every field and value from a hash.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('comms');
+
+$redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
+
+// array(3) {
+//   ["Alice"]=>
+//   string(3) "ecc"
+//   ["Bob"]=>
+//   string(3) "rsa"
+//   ["Mallory"]=>
+//   string(7) "haxx00r"
+// }
+$redis->hGetAll('comms');

Parameters

@@ -7637,24 +7652,7 @@

Return Value

- +
Redis|array|false

All fields and values or false if the key didn't exist.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('comms');
-
-$redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
-
-// array(3) {
-//   ["Alice"]=>
-//   string(3) "ecc"
-//   ["Bob"]=>
-//   string(3) "rsa"
-//   ["Mallory"]=>
-//   string(7) "haxx00r"
-// }
-$redis->hGetAll('comms');
-?>

All fields and values or false if the key didn't exist.

@@ -7678,7 +7676,7 @@

See also

- + Redis|int|false hIncrBy(string $key, string $field, int $value) @@ -7688,7 +7686,17 @@

-

Increment a hash field's value by an integer

+

Increment a hash field's value by an integer

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('player');
+
+$redis->hmset('player', ['name' => 'Bob', 'level' => 1]);
+
+// int(2)
+$redis->hIncrBy('player', 'level', 1);
+
+// int(5)
+$redis->hIncrBy('player', 'level', 3);

Parameters

@@ -7717,20 +7725,7 @@

Return Value

- +
Redis|int|false

The new value of the field.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('player');
-
-$redis->hmset('player', ['name' => 'Bob', 'level' => 1]);
-
-// int(2)
-$redis->hIncrBy('player', 'level', 1);
-
-// int(5)
-$redis->hIncrBy('player', 'level', 3);
-?>

The new value of the field.

@@ -7754,7 +7749,7 @@

See also

- + Redis|float|false hIncrByFloat(string $key, string $field, float $value) @@ -7764,7 +7759,15 @@

-

Increment a hash field by a floating point value

+

Increment a hash field by a floating point value

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('trig-numbers')
+
+// float(3.1415926)
+$pi = $redis->hIncrByFloat('trig-numbers', 'pi', 3.1415926);
+
+// float(6.2831852)
+$redis->hIncrByFloat('trig-numbers', 'tau', 2 * $pi);

Parameters

@@ -7793,17 +7796,7 @@

Return Value

- +
Redis|float|false

The field value after incremented.

-
$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('trig-numbers')
-
-// float(3.1415926)
-$pi = $redis->hIncrByFloat('trig-numbers', 'pi', 3.1415926);
-
-// float(6.2831852)
-$redis->hIncrByFloat('trig-numbers', 'tau', 2 * $pi);
-?>

The field value after incremented.

@@ -7827,7 +7820,7 @@

See also

- + Redis|array|false hKeys(string $key) @@ -7837,7 +7830,21 @@

-

Retrieve all of the fields of a hash.

+

Retrieve all of the fields of a hash.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('ships');
+
+$redis->hmset('ships', ['Enterprise' => 'NCC-1701D', 'Defiant' => 'NX-74205', 'Voyager' => 'NCC-74656']);
+
+// array(3) {
+//   [0]=>
+//   string(10) "Enterprise"
+//   [1]=>
+//   string(7) "Defiant"
+//   [2]=>
+//   string(7) "Voyager"
+// }
+$redis->hKeys('ships');

Parameters

@@ -7856,24 +7863,7 @@

Return Value

- +
Redis|array|false

The fields in the hash or false if the hash doesn't exist.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('ships');
-
-$redis->hmset('ships', ['Enterprise' => 'NCC-1701D', 'Defiant' => 'NX-74205', 'Voyager' => 'NCC-74656']);
-
-// array(3) {
-//   [0]=>
-//   string(10) "Enterprise"
-//   [1]=>
-//   string(7) "Defiant"
-//   [2]=>
-//   string(7) "Voyager"
-// }
-$redis->hKeys('ships');
-?>

The fields in the hash or false if the hash doesn't exist.

@@ -7897,7 +7887,7 @@

See also

- + Redis|int|false hLen(string $key) @@ -7950,7 +7940,7 @@

See also

- + Redis|array|false hMget(string $key, array $fields) @@ -7960,7 +7950,19 @@

-

Get one or more fields from a hash.

+

Get one or more fields from a hash.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('player:1');
+
+$redis->hmset('player:1', ['name' => 'Alice', 'age' => '26', 'score' => '1337']);
+
+// array(2) {
+//   ["name"]=>
+//   string(5) "Alice"
+//   ["score"]=>
+//   string(4) "1337"
+// }
+$redis->hmget('player:1', ['name', 'score']);

Parameters

@@ -7984,22 +7986,7 @@

Return Value

- +
Redis|array|false

The fields and values or false if the key didn't exist.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('player:1');
-
-$redis->hmset('player:1', ['name' => 'Alice', 'age' => '26', 'score' => '1337']);
-
-// array(2) {
-//   ["name"]=>
-//   string(5) "Alice"
-//   ["score"]=>
-//   string(4) "1337"
-// }
-$redis->hmget('player:1', ['name', 'score']);
-?>

The fields and values or false if the key didn't exist.

@@ -8023,7 +8010,7 @@

See also

- + Redis|bool hMset(string $key, array $fieldvals) @@ -8033,7 +8020,9 @@

-

Add or update one or more hash fields and values

+

Add or update one or more hash fields and values

$redis = new Redis(['host' => 'localhost']);
+
+$redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]);

Parameters

@@ -8057,12 +8046,7 @@

Return Value

- +
Redis|bool

True if the operation was successful

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]);
-?>

True if the operation was successful

@@ -8086,7 +8070,7 @@

See also

- + Redis|string|array hRandField(string $key, array $options = null) @@ -8096,7 +8080,14 @@

-

Get one or more random field from a hash.

+

Get one or more random field from a hash.

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('settings');
+
+$redis->hmset('settings', ['path' => '/', 'state' => 'active', 'jobs' => 15]);
+
+$redis->hrandfield('settings');
+$redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);

Parameters

@@ -8124,18 +8115,7 @@

Return Value

- +
Redis|string|array

One or more random fields (and possibly values).

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('settings');
-
-$redis->hmset('settings', ['path' => '/', 'state' => 'active', 'jobs' => 15]);
-
-$redis->hrandfield('settings');
-
-$redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);
-?>

One or more random fields (and possibly values).

@@ -8159,7 +8139,7 @@

See also

- + Redis|int|false hSet(string $key, string $member, mixed $value) @@ -8212,7 +8192,7 @@

Return Value

- + Redis|bool hSetNx(string $key, string $field, string $value) @@ -8222,7 +8202,17 @@

-

Set a hash field and value, but only if that field does not exist

+

Set a hash field and value, but only if that field does not exist

$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('player:1');
+
+$redis->hmset('player:1', ['name' => 'bob', 'score' => 0]);
+
+// bool(true)
+var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));
+
+// bool(false)
+var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));

Parameters

@@ -8251,18 +8241,7 @@

Return Value

- +
Redis|bool

True if the field was set and false if not.

-
$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('player:1');
-
-$redis->hmset('player:1', ['name' => 'bob', 'score' => 0]);
-
-// bool(true)
-var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));
-
-// bool(false)
-var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));

True if the field was set and false if not.

@@ -8286,7 +8265,7 @@

See also

- + Redis|int|false hStrLen(string $key, string $field) @@ -8320,16 +8299,7 @@

Return Value

- +
Redis|int|false

The string length of the field or false.

-
<?php
-$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('hash');
-$redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]);
-
-// int(50)
-$redis->hstrlen('hash', '50bytes');
-

The string length of the field or false.

@@ -8347,13 +8317,24 @@

See also

+

Examples

+ + + + + +
$redis = new Redis(['host' => 'localhost']);
+$redis->del('hash');
+$redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]);
+$redis->hstrlen('hash', '50bytes');
+

- + Redis|array|false hVals(string $key) @@ -8421,7 +8402,7 @@

See also

- + Redis|array|bool hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -8531,7 +8512,7 @@

See also

- + Redis|int|false incr(string $key, int $by = 1) @@ -8606,7 +8587,7 @@

See also

- + Redis|int|false incrBy(string $key, int $value) @@ -8682,7 +8663,7 @@

See also

- + Redis|float|false incrByFloat(string $key, float $value) @@ -8740,7 +8721,7 @@

Return Value

- + Redis|array|false info(string ...$sections) @@ -8796,7 +8777,7 @@

See also

- + bool isConnected() @@ -8828,7 +8809,7 @@

Return Value

- + Redis|array|false keys(string $pattern) @@ -8871,7 +8852,7 @@

Return Value

- + Redis|int|false lInsert(string $key, string $pos, mixed $pivot, mixed $value) @@ -8929,7 +8910,7 @@

Return Value

- + Redis|int|false lLen(string $key) @@ -8972,7 +8953,7 @@

Return Value

- + Redis|string|false lMove(string $src, string $dst, string $wherefrom, string $whereto) @@ -9030,7 +9011,7 @@

Return Value

- + Redis|bool|string|array lPop(string $key, int $count = 0) @@ -9078,7 +9059,7 @@

Return Value

- + Redis|null|bool|int|array lPos(string $key, mixed $value, array $options = null) @@ -9131,7 +9112,7 @@

Return Value

- + int|Redis lPush(string $key, mixed ...$elements) @@ -9179,7 +9160,7 @@

Return Value

- + Redis|int|false rPush(string $key, mixed ...$elements) @@ -9227,7 +9208,7 @@

Return Value

- + Redis|int|false lPushx(string $key, mixed $value) @@ -9275,7 +9256,7 @@

Return Value

- + Redis|int|false rPushx(string $key, mixed $value) @@ -9323,7 +9304,7 @@

Return Value

- + Redis|bool lSet(string $key, int $index, mixed $value) @@ -9376,7 +9357,7 @@

Return Value

- + int lastSave() @@ -9409,7 +9390,7 @@

Return Value

- + mixed lindex(string $key, int $index) @@ -9457,7 +9438,7 @@

Return Value

- + Redis|array|false lrange(string $key, int $start, int $end) @@ -9510,7 +9491,7 @@

Return Value

- + int|Redis|false lrem(string $key, mixed $value, int $count = 0) @@ -9563,7 +9544,7 @@

Return Value

- + Redis|bool ltrim(string $key, int $start, int $end) @@ -9616,7 +9597,7 @@

Return Value

- + array|Redis mget(array $keys) @@ -9659,7 +9640,7 @@

Return Value

- + Redis|bool migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = NULL) @@ -9737,7 +9718,7 @@

Return Value

- + bool move(string $key, int $index) @@ -9785,7 +9766,7 @@

Return Value

- + Redis|bool mset(array $key_values) @@ -9828,7 +9809,7 @@

Return Value

- + Redis|bool msetnx(array $key_values) @@ -9871,7 +9852,7 @@

Return Value

- + bool|Redis multi(int $value = Redis::MULTI) @@ -9914,7 +9895,7 @@

Return Value

- + Redis|int|string|false object(string $subcommand, string $key) @@ -9962,7 +9943,7 @@

Return Value

- + bool open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) deprecated @@ -10042,7 +10023,7 @@

Return Value

- + bool pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) @@ -10115,7 +10096,7 @@

Return Value

- + bool persist(string $key) @@ -10158,7 +10139,7 @@

Return Value

- + bool pexpire(string $key, int $timeout, string|null $mode = NULL) @@ -10214,7 +10195,7 @@

Return Value

- + Redis|bool pexpireAt(string $key, int $timestamp, string|null $mode = NULL) @@ -10285,7 +10266,7 @@

See also

- + Redis|int pfadd(string $key, array $elements) @@ -10343,7 +10324,7 @@

See also

- + Redis|int pfcount(string $key) @@ -10396,7 +10377,7 @@

See also

- + Redis|bool pfmerge(string $dst, array $srckeys) @@ -10454,7 +10435,7 @@

See also

- + Redis|string|bool ping(string $message = NULL) @@ -10517,7 +10498,7 @@

See also

- + bool|Redis pipeline() @@ -10569,7 +10550,7 @@

Return Value

- + bool popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) deprecated @@ -10649,7 +10630,7 @@

Return Value

- + bool|Redis psetex(string $key, int $expire, mixed $value) @@ -10702,7 +10683,7 @@

Return Value

- + bool psubscribe(array $patterns, callable $cb) @@ -10761,7 +10742,7 @@

See also

- + Redis|int|false pttl(string $key) @@ -10824,7 +10805,7 @@

See also

- + Redis|int|false publish(string $channel, string $message) @@ -10882,7 +10863,7 @@

See also

- + mixed pubsub(string $command, mixed $arg = null) @@ -10930,7 +10911,7 @@

Return Value

- + Redis|array|bool punsubscribe(array $patterns) @@ -10996,7 +10977,7 @@

See also

- + Redis|array|string|bool rPop(string $key, int $count = 0) @@ -11071,7 +11052,7 @@

See also

- + Redis|string|false randomKey() @@ -11114,7 +11095,7 @@

See also

- + mixed rawcommand(string $command, mixed ...$args) @@ -11182,7 +11163,7 @@

Return Value

- + Redis|bool rename(string $old_name, string $new_name) @@ -11240,7 +11221,7 @@

See also

- + Redis|bool renameNx(string $key_src, string $key_dst) @@ -11312,7 +11293,7 @@

See also

- + Redis|bool reset() @@ -11344,7 +11325,7 @@

Return Value

- + Redis|bool restore(string $key, int $ttl, string $value, array|null $options = NULL) @@ -11464,7 +11445,7 @@

See also

- + mixed role() @@ -11497,7 +11478,7 @@

Return Value

- + Redis|string|false rpoplpush(string $srckey, string $dstkey) @@ -11580,7 +11561,7 @@

See also

- + Redis|int|false sAdd(string $key, mixed $value, mixed ...$other_values) @@ -11655,7 +11636,7 @@

See also

- + int sAddArray(string $key, array $values) @@ -11732,7 +11713,7 @@

See also

- + Redis|array|false sDiff(string $key, string ...$other_keys) @@ -11814,7 +11795,7 @@

See also

- + Redis|int|false sDiffStore(string $dst, string $key, string ...$other_keys) @@ -11884,7 +11865,7 @@

See also

- + Redis|array|false sInter(array|string $key, string ...$other_keys) @@ -11963,7 +11944,7 @@

See also

- + Redis|int|false sintercard(array $keys, int $limit = -1) @@ -12034,7 +12015,7 @@

See also

- + Redis|int|false sInterStore(array|string $key, string ...$other_keys) @@ -12109,7 +12090,7 @@

See also

- + Redis|array|false sMembers(string $key) @@ -12180,7 +12161,7 @@

See also

- + Redis|array|false sMisMember(string $key, string $member, string ...$other_members) @@ -12277,7 +12258,7 @@

See also

- + Redis|bool sMove(string $src, string $dst, mixed $value) @@ -12370,7 +12351,7 @@

See also

- + Redis|string|array|false sPop(string $key, int $count = 0) @@ -12461,7 +12442,7 @@

See also

- + Redis|string|array|false sRandMember(string $key, int $count = 0) @@ -12529,7 +12510,7 @@

Return Value

- + Redis|array|false sUnion(string $key, string ...$other_keys) @@ -12612,7 +12593,7 @@

See also

- + Redis|int|false sUnionStore(string $dst, string $key, string ...$other_keys) @@ -12682,7 +12663,7 @@

See also

- + Redis|bool save() @@ -12732,7 +12713,7 @@

See also

- + array|false scan(int|null $iterator, string|null $pattern = null, int $count = 0, string $type = NULL) @@ -12850,7 +12831,7 @@

See also

- + Redis|int|false scard(string $key) @@ -12913,7 +12894,7 @@

See also

- + mixed script(string $command, mixed ...$args) @@ -12990,7 +12971,7 @@

See also

- + Redis|bool select(int $db) @@ -13048,7 +13029,7 @@

Return Value

- + Redis|string|bool set(string $key, mixed $value, mixed $options = NULL) @@ -13142,7 +13123,7 @@

See also

- + Redis|int|false setBit(string $key, int $idx, bool $value) @@ -13216,7 +13197,7 @@

See also

- + Redis|int|false setRange(string $key, int $index, string $value) @@ -13287,7 +13268,7 @@

See also

- + bool setOption(int $option, mixed $value) @@ -13429,7 +13410,7 @@

See also

- + Redis|bool setex(string $key, int $expire, mixed $value) @@ -13488,7 +13469,7 @@

Return Value

- + Redis|bool setnx(string $key, mixed $value) @@ -13558,7 +13539,7 @@

See also

- + Redis|bool sismember(string $key, mixed $value) @@ -13619,7 +13600,7 @@

Return Value

- + Redis|bool slaveof(string $host = NULL, int $port = 6379) deprecated @@ -13702,7 +13683,7 @@

See also

- + Redis|bool replicaof(string $host = NULL, int $port = 6379) @@ -13785,7 +13766,7 @@

See also

- + Redis|int|false touch(array|string $key_or_array, string ...$more_keys) @@ -13844,7 +13825,7 @@

See also

- + mixed slowlog(string $operation, int $length = 0) @@ -13916,7 +13897,7 @@

See also

- + mixed sort(string $key, array|null $options = null) @@ -13991,7 +13972,7 @@

See also

- + mixed sort_ro(string $key, array|null $options = null) @@ -14050,7 +14031,7 @@

See also

- + array sortAsc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14125,7 +14106,7 @@

Return Value

- + array sortAscAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14200,7 +14181,7 @@

Return Value

- + array sortDesc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14275,7 +14256,7 @@

Return Value

- + array sortDescAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14350,7 +14331,7 @@

Return Value

- + Redis|int|false srem(string $key, mixed $value, mixed ...$other_values) @@ -14425,7 +14406,7 @@

See also

- + array|false sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -14551,7 +14532,7 @@

See also

- + Redis|int|false strlen(string $key) @@ -14610,7 +14591,7 @@

Return Value

- + bool subscribe(array $channels, callable $cb) @@ -14676,7 +14657,7 @@

Return Value

- + Redis|bool swapdb(int $src, int $dst) @@ -14780,7 +14761,7 @@

See also

- + Redis|array time() @@ -14834,7 +14815,7 @@

See also

- + Redis|int|false ttl(string $key) @@ -14897,7 +14878,7 @@

Return Value

- + Redis|int|false type(string $key) @@ -14958,7 +14939,7 @@

See also

See also

- + Redis|array|bool unsubscribe(array $channels) @@ -15101,7 +15082,7 @@

See also

- + Redis|bool unwatch() @@ -15157,7 +15138,7 @@

See also

- + bool|Redis watch(array|string $key, string ...$other_keys) @@ -15205,7 +15186,7 @@

Return Value

- + int|false wait(int $numreplicas, int $timeout) @@ -15264,7 +15245,7 @@

See also

- + int|false xack(string $key, string $group, array $ids) @@ -15317,7 +15298,7 @@

Return Value

- + Redis|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) @@ -15429,7 +15410,7 @@

See also

- + Redis|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) @@ -15502,7 +15483,7 @@

Return Value

- + Redis|bool|array xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) @@ -15570,7 +15551,7 @@

Return Value

- + Redis|int|false xdel(string $key, array $ids) @@ -15650,7 +15631,7 @@

Return Value

- + mixed xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) @@ -15745,7 +15726,7 @@

See also

- + mixed xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) @@ -15820,7 +15801,7 @@

Return Value

- + Redis|int|false xlen(string $key) @@ -15883,7 +15864,7 @@

See also

- + Redis|array|false xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) @@ -15968,7 +15949,7 @@

See also

- + Redis|array|bool xrange(string $key, string $start, string $end, int $count = -1) @@ -16066,7 +16047,7 @@

See also

- + Redis|array|bool xread(array $streams, int $count = -1, int $block = -1) @@ -16169,7 +16150,7 @@

See also

- + Redis|array|bool xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) @@ -16271,7 +16252,7 @@

Return Value

- + Redis|array|bool xrevrange(string $key, string $end, string $start, int $count = -1) @@ -16375,7 +16356,7 @@

See also

- + Redis|int|false xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) @@ -16502,7 +16483,7 @@

See also

- + Redis|int|false zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) @@ -16608,7 +16589,7 @@

See also

- + Redis|int|false zCard(string $key) @@ -16670,7 +16651,7 @@

See also

- + Redis|int|false zCount(string $key, string $start, string $end) @@ -16733,7 +16714,7 @@

See also

- + Redis|float|false zIncrBy(string $key, float $value, mixed $member) @@ -16808,7 +16789,7 @@

See also

- + Redis|int|false zLexCount(string $key, string $min, string $max) @@ -16884,7 +16865,7 @@

See also

- + Redis|array|false zMscore(string $key, mixed $member, mixed ...$other_members) @@ -16970,7 +16951,7 @@

See also

- + Redis|array|false zPopMax(string $key, int $count = null) @@ -17047,7 +17028,7 @@

See also

- + Redis|array|false zPopMin(string $key, int $count = null) @@ -17124,7 +17105,7 @@

See also

- + Redis|array|false zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) @@ -17206,7 +17187,7 @@

See also

- + Redis|array|false zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) @@ -17301,7 +17282,7 @@

See also

- + Redis|array|false zRangeByScore(string $key, string $start, string $end, array $options = []) @@ -17417,7 +17398,7 @@

See also

- + Redis|int|false zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) @@ -17498,7 +17479,7 @@

See also

- + Redis|string|array zRandMember(string $key, array $options = null) @@ -17569,7 +17550,7 @@

See also

- + Redis|int|false zRank(string $key, mixed $member) @@ -17627,7 +17608,7 @@

See also

- + Redis|int|false zRem(mixed $key, mixed $member, mixed ...$other_members) @@ -17711,7 +17692,7 @@

See also

- + Redis|int|false zRemRangeByLex(string $key, string $min, string $max) @@ -17809,7 +17790,7 @@

See also

- + Redis|int|false zRemRangeByRank(string $key, int $start, int $end) @@ -17887,7 +17868,7 @@

See also

- + Redis|int|false zRemRangeByScore(string $key, string $start, string $end) @@ -17967,7 +17948,7 @@

See also

- + Redis|array|false zRevRange(string $key, int $start, int $end, mixed $scores = null) @@ -18048,7 +18029,7 @@

Return Value

- + Redis|array|false zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) @@ -18149,7 +18130,7 @@

See also

- + Redis|array|false zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) @@ -18245,7 +18226,7 @@

Return Value

- + Redis|int|false zRevRank(string $key, mixed $member) @@ -18317,7 +18298,7 @@

See also

- + Redis|float|false zScore(string $key, mixed $member) @@ -18389,7 +18370,7 @@

See also

- + Redis|array|false zdiff(array $keys, array $options = null) @@ -18465,7 +18446,7 @@

See also

- + Redis|int|false zdiffstore(string $dst, array $keys) @@ -18532,7 +18513,7 @@

See also

- + Redis|array|false zinter(array $keys, array|null $weights = null, array|null $options = null) @@ -18625,7 +18606,7 @@

See also

- + Redis|int|false zintercard(array $keys, int $limit = -1) @@ -18710,7 +18691,7 @@

See also

- + Redis|int|false zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) @@ -18813,7 +18794,7 @@

See also

- + Redis|array|false zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -18900,7 +18881,7 @@

See also

- + Redis|array|false zunion(array $keys, array|null $weights = null, array|null $options = null) @@ -18999,7 +18980,7 @@

Return Value

- + Redis|int|false zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) diff --git a/docs/doc-index.html b/docs/doc-index.html index 5f71012fc4..792d88cd06 100644 --- a/docs/doc-index.html +++ b/docs/doc-index.html @@ -188,8 +188,7 @@

A

Redis::command() — Method in class Redis
Redis::config() — Method in class Redis
-

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends -on the $operation qualifier.

+

Execute the Redis CONFIG command in a variety of ways.

Redis::connect() — Method in class Redis
Redis::copy() — Method in class Redis
@@ -266,8 +265,7 @@

A

redis-server >= 7.0.0 you may send an additional "mode" argument which modifies how the command will execute.

Redis::expireAt() — Method in class Redis
-

Set a key's expiration to a specific Unix timestamp in seconds. If -connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

+

Set a key to expire at an exact unix timestamp.

Redis::expiretime() — Method in class Redis

Get the expiration of a given key as a unix timestamp

RedisArray::exec() — Method in class RedisArray
@@ -341,7 +339,7 @@

A

Redis::getEx() — Method in class Redis
Redis::getDBNum() — Method in class Redis
-
+

Get the database number PhpRedis thinks we're connected to.

Redis::getDel() — Method in class Redis
Redis::getHost() — Method in class Redis
diff --git a/docs/doctum-search.json b/docs/doctum-search.json index f2cb76692a..7929cf10cf 100644 --- a/docs/doctum-search.json +++ b/docs/doctum-search.json @@ -1 +1 @@ -{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

"},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":null},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

Compress a value with the currently configured compressor as set with\nRedis::setOption().

"},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

Uncompress the provided argument that has been compressed with the\ncurrently configured compressor as set with Redis::setOption().

"},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

Pack the provided value with the configured serializer and compressor\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

Unpack the provided value with the configured compressor and serializer\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":null},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

Append data to a Redis STRING key.

"},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

Authenticate a Redis connection after its been established.

"},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

Execute a save of the Redis database in the background.

"},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

Asynchronously rewrite Redis' append-only file

"},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

Count the number of set bits in a Redis string.

"},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

"},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

"},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

"},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

"},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

"},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

"},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

POP one or more of the highest or lowest scoring elements from one or more sorted sets.

"},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

"},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

Pop one or more elements off of one or more Redis LISTs.

"},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

Reset any last error on the connection to NULL

"},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":null},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":null},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":null},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends\non the $operation qualifier.

"},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":null},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

Make a copy of a key.

"},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

Return the number of keys in the currently selected Redis database.

"},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":null},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

Decrement a Redis integer by 1 or a provided value.

"},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

Decrement a redis integer by a value

"},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

Delete one or more keys from Redis.

"},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

Discard a transaction currently in progress.

"},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

Dump Redis' internal binary representation of a key.

"},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

Have Redis repeat back an arbitrary string to the client.

"},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

Execute a LUA script on the redis server.

"},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

"},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

Execute either a MULTI or PIPELINE block and return the array of replies.

"},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

Test if one or more keys exist.

"},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

"},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

Set a key's expiration to a specific Unix timestamp in seconds. If\nconnected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

Get the expiration of a given key as a unix timestamp

"},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

Get the expriation timestamp of a given Redis key but in milliseconds.

"},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

Deletes every key in all Redis databases

"},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

Deletes all the keys of the currently selected database.

"},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":null},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":null},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":null},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":null},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":null},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":null},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":null},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":null},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":null},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":null},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":null},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

Get the authentication information on the connection, if any.

"},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":null},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":null},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":null},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":null},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

Return the host or Unix socket we are connected to.

"},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

Get the last error returned to us from Redis, if any.

"},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

"},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

Retrieve the value of a configuration setting as set by Redis::setOption()

"},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

Get the persistent connection ID, if there is one.

"},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

"},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

Retrieve a substring of a string by index.

"},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

Get the longest common subsequence between two string keys.

"},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

Get the currently set read timeout on the connection.

"},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

Sets a key and returns any previously set value, if the key already existed.

"},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

Retrieve any set connection timeout

"},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":null},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

Remove one or more fields from a hash.

"},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

Checks whether a field exists in a hash.

"},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

Read every field and value from a hash.

"},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

Increment a hash field's value by an integer

"},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

Increment a hash field by a floating point value

"},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

Retrieve all of the fields of a hash.

"},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

Get the number of fields in a hash.

"},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

Get one or more fields from a hash.

"},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

Add or update one or more hash fields and values

"},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

Get one or more random field from a hash.

"},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":null},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

Set a hash field and value, but only if that field does not exist

"},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

Get the string length of a hash field

"},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

Get all of the values from a hash.

"},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

Iterate over the fields and values of a hash in an incremental fashion.

"},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

Increment a key's value, optionally by a specifc amount.

"},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

Increment a key by a specific integer value

"},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

Increment a numeric key by a floating point value.

"},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

Check if we are currently connected to a Redis instance.

"},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":null},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":null},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":null},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":null},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":""},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":""},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":""},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":""},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":null},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":null},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":null},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":null},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":""},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":null},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":""},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":null},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":null},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":null},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":null},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":null},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":null},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":null},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":null},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

"},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

Add one or more elements to a Redis HyperLogLog key

"},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

Retrieve the cardinality of a Redis HyperLogLog key.

"},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

Merge one or more source HyperLogLog sets into a destination set.

"},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

PING the redis server with an optional string argument.

"},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

Enter into pipeline mode.

"},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":""},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

Subscribe to one or more glob-style patterns

"},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

Get a keys time to live in milliseconds.

"},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

Publish a message to a pubsub channel

"},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":null},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

Unsubscribe from one or more channels by pattern

"},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

Pop one or more elements from the end of a list.

"},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

Return a random key from the current database

"},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

Execute any arbitrary Redis command by name.

"},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

Unconditionally rename a key from $old_name to $new_name

"},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

Renames $key_src to $key_dst but only if newkey does not exist.

"},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

Reset the state of the connection.

"},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

Restore a key by the binary payload generated by the DUMP command.

"},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

Query whether the connected instance is a primary or replica

"},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

"},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

Add one or more values to a Redis SET key.

"},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

"},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

"},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

"},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

"},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

Compute the intersection of one or more sets and return the cardinality of the result.

"},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

"},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

Retrieve every member from a set key.

"},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

Check if one or more values are members of a set.

"},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

"},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

Remove one or more elements from a set.

"},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

Retrieve one or more random members of a set.

"},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

Returns the union of one or more Redis SET keys.

"},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

Perform a union of one or more Redis SET keys and store the result in a new set

"},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

"},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

Incrementally scan the Redis keyspace, with optional pattern and type matching.

"},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

Retrieve the number of members in a Redis set.

"},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

An administrative command used to interact with LUA scripts stored on the server.

"},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

Select a specific Redis database.

"},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

Create or set a Redis STRING key to a value.

"},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

Set a specific bit in a Redis string to zero or one

"},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

Update or append to a Redis string at a specific starting index

"},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

Set a configurable option on the Redis object.

"},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

Set a Redis STRING key with a specific expiration in seconds.

"},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

Set a key to a value, but only if that key does not already exist.

"},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

Check whether a given value is the member of a Redis SET.

"},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

Turn a redis instance into a replica of another or promote a replica\nto a primary.

"},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

"},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

Update one or more keys last modified metadata.

"},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

"},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

Sort the contents of a Redis key in various ways.

"},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

This is simply a read-only variant of the sort command

"},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

Remove one or more values from a Redis SET key.

"},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

Scan the members of a redis SET key.

"},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

Retrieve the length of a Redis STRING key.

"},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

Subscribe to one or more Redis pubsub channels.

"},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

"},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

Retrieve the server time from the connected Redis instance.

"},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

Get the amount of time a Redis key has before it will expire, in seconds.

"},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

Get the type of a given Redis key.

"},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

"},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

Unsubscribe from one or more subscribed channels.

"},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

Remove any previously WATCH'ed keys in a transaction.

"},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":""},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

Block the client up to the provided timeout until a certain number of replicas have confirmed\nrecieving them.

"},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":null},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

Append a message to a stream.

"},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":null},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":null},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

Remove one or more specific IDs from a stream.

"},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

Retrieve information about a stream key.

"},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

Get the number of messages in a Redis STREAM key.

"},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

"},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

Get a range of entries from a STREAM key.

"},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

Consume one or more unconsumed elements in one or more streams.

"},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

Read one or more messages using a consumer group.

"},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

Get a range of entries from a STREAM ke in reverse cronological order.

"},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

Truncate a STREAM key in various ways.

"},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

Add one or more elements and scores to a Redis sorted set.

"},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

Return the number of elements in a sorted set.

"},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

Count the number of members in a sorted set with scores inside a provided range.

"},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

Create or increment the score of a member in a Redis sorted set

"},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

Count the number of elements in a sorted set whos members fall within the provided\nlexographical range.

"},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

Retrieve the score of one or more members in a sorted set.

"},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

Pop one or more of the highest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

Pop one or more of the lowest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

Retrieve a range of elements of a sorted set between a start and end point.

"},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

Retrieve a range of elements from a sorted set by legographical range.

"},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

Retrieve a range of members from a sorted set by their score.

"},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

"},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

Retrieve one or more random members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

Get the rank of a member of a sorted set, by score.

"},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

Remove one or more members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

Remove zero or more elements from a Redis sorted set by legographical range.

"},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

Remove one or more members of a sorted set by their rank.

"},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

Remove one or more members of a sorted set by their score.

"},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

List the members of a Redis sorted set in reverse order

"},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

List members of a Redis sorted set within a legographical range, in reverse order.

"},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

List elements from a Redis sorted set by score, highest to lowest

"},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

Retrieve a member of a sorted set by reverse rank.

"},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

Get the score of a member of a sorted set.

"},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

"},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

Store the difference of one or more sorted sets in a destination sorted set.

"},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

Compute the intersection of one or more sorted sets and return the members

"},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

"},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

"},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

Scan the members of a sorted set incrementally, using a cursor

"},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

Retrieve the union of one or more sorted sets

"},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

Perform a union on one or more Redis sets and store the result in a destination sorted set.

"},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

See Redis::blpop()

"},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

See Redis::brpop()

"},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

See Redis::brpoplpush()

"},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

PING an instance in the redis cluster.

"},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} +{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

"},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":null},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

Compress a value with the currently configured compressor as set with\nRedis::setOption().

"},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

Uncompress the provided argument that has been compressed with the\ncurrently configured compressor as set with Redis::setOption().

"},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

"},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

Pack the provided value with the configured serializer and compressor\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

Unpack the provided value with the configured compressor and serializer\nas set with Redis::setOption().

"},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":null},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

Append data to a Redis STRING key.

"},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

Authenticate a Redis connection after its been established.

"},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

Execute a save of the Redis database in the background.

"},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

Asynchronously rewrite Redis' append-only file

"},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

Count the number of set bits in a Redis string.

"},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

"},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

"},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

"},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

"},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

"},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

"},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

POP one or more of the highest or lowest scoring elements from one or more sorted sets.

"},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

"},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

Pop one or more elements off of one or more Redis LISTs.

"},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

Reset any last error on the connection to NULL

"},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":null},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":null},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":null},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

Execute the Redis CONFIG command in a variety of ways.

"},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":null},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

Make a copy of a key.

"},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

Return the number of keys in the currently selected Redis database.

"},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":null},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

Decrement a Redis integer by 1 or a provided value.

"},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

Decrement a redis integer by a value

"},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

Delete one or more keys from Redis.

"},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

Discard a transaction currently in progress.

"},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

Dump Redis' internal binary representation of a key.

"},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

Have Redis repeat back an arbitrary string to the client.

"},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

Execute a LUA script on the redis server.

"},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

"},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

"},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

Execute either a MULTI or PIPELINE block and return the array of replies.

"},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

Test if one or more keys exist.

"},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

"},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

Set a key to expire at an exact unix timestamp.

"},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

Get the expiration of a given key as a unix timestamp

"},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

Get the expriation timestamp of a given Redis key but in milliseconds.

"},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

Deletes every key in all Redis databases

"},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

Deletes all the keys of the currently selected database.

"},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":null},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":null},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":null},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":null},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":null},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":null},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":null},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":null},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":null},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":null},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":null},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

Get the authentication information on the connection, if any.

"},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":null},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":null},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":"

Get the database number PhpRedis thinks we're connected to.

"},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":null},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

Return the host or Unix socket we are connected to.

"},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

Get the last error returned to us from Redis, if any.

"},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

"},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

Retrieve the value of a configuration setting as set by Redis::setOption()

"},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

Get the persistent connection ID, if there is one.

"},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

"},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

Retrieve a substring of a string by index.

"},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

Get the longest common subsequence between two string keys.

"},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

Get the currently set read timeout on the connection.

"},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

Sets a key and returns any previously set value, if the key already existed.

"},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

Retrieve any set connection timeout

"},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":null},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

Remove one or more fields from a hash.

"},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

Checks whether a field exists in a hash.

"},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

Read every field and value from a hash.

"},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

Increment a hash field's value by an integer

"},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

Increment a hash field by a floating point value

"},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

Retrieve all of the fields of a hash.

"},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

Get the number of fields in a hash.

"},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

Get one or more fields from a hash.

"},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

Add or update one or more hash fields and values

"},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

Get one or more random field from a hash.

"},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":null},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

Set a hash field and value, but only if that field does not exist

"},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

Get the string length of a hash field

"},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

Get all of the values from a hash.

"},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

Iterate over the fields and values of a hash in an incremental fashion.

"},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

Increment a key's value, optionally by a specifc amount.

"},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

Increment a key by a specific integer value

"},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

Increment a numeric key by a floating point value.

"},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

Check if we are currently connected to a Redis instance.

"},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":null},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":null},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":null},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":null},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":""},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":""},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":""},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":""},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":null},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":null},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":null},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":null},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":""},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":null},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":""},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":null},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":null},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":null},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":null},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":null},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":null},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":null},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":null},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

"},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

"},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

Add one or more elements to a Redis HyperLogLog key

"},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

Retrieve the cardinality of a Redis HyperLogLog key.

"},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

Merge one or more source HyperLogLog sets into a destination set.

"},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

PING the redis server with an optional string argument.

"},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

Enter into pipeline mode.

"},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":""},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

Subscribe to one or more glob-style patterns

"},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

Get a keys time to live in milliseconds.

"},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

Publish a message to a pubsub channel

"},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":null},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

Unsubscribe from one or more channels by pattern

"},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

Pop one or more elements from the end of a list.

"},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

Return a random key from the current database

"},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

Execute any arbitrary Redis command by name.

"},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

Unconditionally rename a key from $old_name to $new_name

"},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

Renames $key_src to $key_dst but only if newkey does not exist.

"},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

Reset the state of the connection.

"},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

Restore a key by the binary payload generated by the DUMP command.

"},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

Query whether the connected instance is a primary or replica

"},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

"},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

Add one or more values to a Redis SET key.

"},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

"},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

"},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

"},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

"},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

Compute the intersection of one or more sets and return the cardinality of the result.

"},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

"},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

Retrieve every member from a set key.

"},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

Check if one or more values are members of a set.

"},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

"},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

Remove one or more elements from a set.

"},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

Retrieve one or more random members of a set.

"},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

Returns the union of one or more Redis SET keys.

"},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

Perform a union of one or more Redis SET keys and store the result in a new set

"},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

"},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

Incrementally scan the Redis keyspace, with optional pattern and type matching.

"},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

Retrieve the number of members in a Redis set.

"},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

An administrative command used to interact with LUA scripts stored on the server.

"},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

Select a specific Redis database.

"},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

Create or set a Redis STRING key to a value.

"},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

Set a specific bit in a Redis string to zero or one

"},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

Update or append to a Redis string at a specific starting index

"},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

Set a configurable option on the Redis object.

"},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

Set a Redis STRING key with a specific expiration in seconds.

"},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

Set a key to a value, but only if that key does not already exist.

"},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

Check whether a given value is the member of a Redis SET.

"},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

Turn a redis instance into a replica of another or promote a replica\nto a primary.

"},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

"},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

Update one or more keys last modified metadata.

"},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

"},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

Sort the contents of a Redis key in various ways.

"},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

This is simply a read-only variant of the sort command

"},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

Remove one or more values from a Redis SET key.

"},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

Scan the members of a redis SET key.

"},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

Retrieve the length of a Redis STRING key.

"},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

Subscribe to one or more Redis pubsub channels.

"},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

"},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

Retrieve the server time from the connected Redis instance.

"},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

Get the amount of time a Redis key has before it will expire, in seconds.

"},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

Get the type of a given Redis key.

"},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

"},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

Unsubscribe from one or more subscribed channels.

"},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

Remove any previously WATCH'ed keys in a transaction.

"},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":""},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

Block the client up to the provided timeout until a certain number of replicas have confirmed\nrecieving them.

"},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":null},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

Append a message to a stream.

"},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":null},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":null},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

Remove one or more specific IDs from a stream.

"},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

Retrieve information about a stream key.

"},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

Get the number of messages in a Redis STREAM key.

"},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

"},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

Get a range of entries from a STREAM key.

"},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

Consume one or more unconsumed elements in one or more streams.

"},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

Read one or more messages using a consumer group.

"},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

Get a range of entries from a STREAM ke in reverse cronological order.

"},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

Truncate a STREAM key in various ways.

"},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

Add one or more elements and scores to a Redis sorted set.

"},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

Return the number of elements in a sorted set.

"},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

Count the number of members in a sorted set with scores inside a provided range.

"},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

Create or increment the score of a member in a Redis sorted set

"},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

Count the number of elements in a sorted set whos members fall within the provided\nlexographical range.

"},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

Retrieve the score of one or more members in a sorted set.

"},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

Pop one or more of the highest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

Pop one or more of the lowest scoring elements from a sorted set.

"},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

Retrieve a range of elements of a sorted set between a start and end point.

"},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

Retrieve a range of elements from a sorted set by legographical range.

"},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

Retrieve a range of members from a sorted set by their score.

"},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

"},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

Retrieve one or more random members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

Get the rank of a member of a sorted set, by score.

"},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

Remove one or more members from a Redis sorted set.

"},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

Remove zero or more elements from a Redis sorted set by legographical range.

"},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

Remove one or more members of a sorted set by their rank.

"},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

Remove one or more members of a sorted set by their score.

"},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

List the members of a Redis sorted set in reverse order

"},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

List members of a Redis sorted set within a legographical range, in reverse order.

"},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

List elements from a Redis sorted set by score, highest to lowest

"},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

Retrieve a member of a sorted set by reverse rank.

"},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

Get the score of a member of a sorted set.

"},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

"},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

Store the difference of one or more sorted sets in a destination sorted set.

"},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

Compute the intersection of one or more sorted sets and return the members

"},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

"},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

"},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

Scan the members of a sorted set incrementally, using a cursor

"},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

Retrieve the union of one or more sorted sets

"},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

Perform a union on one or more Redis sets and store the result in a destination sorted set.

"},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

Return the position of the first bit set to 0 or 1 in a string.

"},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

See Redis::blpop()

"},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

See Redis::brpop()

"},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

See Redis::brpoplpush()

"},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

"},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

PING an instance in the redis cluster.

"},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} diff --git a/docs/renderer.index b/docs/renderer.index index 6361316e97..427fc8536e 100644 --- a/docs/renderer.index +++ b/docs/renderer.index @@ -1 +1 @@ -O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"4f4f62f9f49eb59c17c3dda8e0c3ae397a6df977";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:14:"RedisException";s:40:"4f4f62f9f49eb59c17c3dda8e0c3ae397a6df977";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file +O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"0ca9052a6b2da623f76b015fa3271f4a9d1ffcf9";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:14:"RedisException";s:40:"0ca9052a6b2da623f76b015fa3271f4a9d1ffcf9";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file diff --git a/redis.stub.php b/redis.stub.php index 15288db2e2..a05859d070 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -238,14 +238,6 @@ public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bo * Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified * timeout. This method may be called in two distinct ways, of which examples are provided below. * - * - * // Variadic, with the final argument a timeout. - * $redis->blPop('list1', 'list2', 'list3', 1.5); - * - * // Alternatively, you can send an array of keys - * $relay->blPop(['list1', 'list2', 'list3'], 1.5); - * - * * @see https://redis.io/commands/blpop/ * * @param string|array $key_or_keys This can either be a string key or an array of one or more @@ -255,6 +247,10 @@ public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bo * the command. * * @return Redis|array|null|false Can return various things depending on command and data in Redis. + * + * @example + * $redis->blPop('list1', 'list2', 'list3', 1.5); + * $relay->blPop(['list1', 'list2', 'list3'], 1.5); */ public function blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false; @@ -288,14 +284,6 @@ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis| * * Following are examples of the two main ways to call this method. * - * - * // Method 1 - Variadic, with the last argument being our timeout - * $redis->bzPopMax('key1', 'key2', 'key3', 1.5); - * - * // Method 2 - A single array of keys, followed by the timeout - * $redis->bzPopMax(['key1', 'key2', 'key3'], 1.5); - * - * * **NOTE**: We reccomend calling this function with an array and a timeout as the other strategy * may be deprecated in future versions of PhpRedis * @@ -309,6 +297,10 @@ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis| * which needs to be a timeout. * * @return Redis|array|false The popped elements. + * + * @example + * $redis->bzPopMax('key1', 'key2', 'key3', 1.5); + * $redis->bzPopMax(['key1', 'key2', 'key3'], 1.5); */ public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false; @@ -393,19 +385,16 @@ public function lmpop(array $keys, string $from, int $count = 1): Redis|array|nu /** * Reset any last error on the connection to NULL * - * - * $redis = new Redis(['host' => 'localhost']); + * @see Redis::getLastError() + * @return bool This should always return true or throw an exception if we're not connected. * + * @example + * $redis = new Redis(['host' => 'localhost']); * $redis->set('string', 'this_is_a_string'); * $redis->smembers('string'); - * * var_dump($redis->getLastError()); * $redis->clearLastError(); * var_dump($redis->getLastError()); - * - * - * @see Redis::getLastError() - * @return bool This should always return true or throw an exception if we're not connected. */ public function clearLastError(): bool; @@ -416,30 +405,22 @@ public function close(): bool; public function command(string $opt = null, string|array $arg): mixed; /** - * Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends - * on the `$operation` qualifier. + * Execute the Redis CONFIG command in a variety of ways. * + * What the command does in particular depends on the `$operation` qualifier. * Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET. * - * - * $redis->config('GET', 'timeout'); - * $redis->config('GET', ['timeout', 'databases']); - * - * $redis->config('SET', 'timeout', 30); - * $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); - * - * - * @param string $operation The CONFIG subcommand to execute - * @param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or - * an array of settings or settings and values. - * Note: Redis 7.0.0 is required to send an array of settings. - * @param string $value The setting value when the operation is SET. - * - * @return mixed Can return various things depending on arguments sent. + * @param string $operation The CONFIG operation to execute (e.g. GET, SET, REWRITE). + * @param array|string|null $key_or_settings One or more keys or values. + * @param string $value The value if this is a `CONFIG SET` operation. + * @see https://redis.io/commands/config * - * @see https://redis.io/commands/config - * - * */ + * @example + * $redis->config('GET', 'timeout'); + * $redis->config('GET', ['timeout', 'databases']); + * $redis->config('SET', 'timeout', 30); + * $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); + */ public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed; public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, @@ -448,36 +429,8 @@ public function connect(string $host, int $port = 6379, float $timeout = 0, stri /** * Make a copy of a key. * - * * $redis = new Redis(['host' => 'localhost']); * - * $redis->pipeline() - * ->select(1) - * ->del('newkey') - * ->select(0) - * ->del('newkey') - * ->mset(['source1' => 'value1', 'exists' => 'old_value']) - * ->exec(); - * - * // Will succeed, as 'newkey' doesn't exist - * var_dump($redis->copy('source1', 'newkey')); - * - * // Will succeed, because 'newkey' doesn't exist in DB 1 - * var_dump($redis->copy('source1', 'newkey', ['db' => 1])); - * - * // Will fail, because 'exists' does exist - * var_dump($redis->copy('source1', 'exists')); - * - * // Will succeed, because even though 'exists' is a key, we sent the REPLACE option. - * var_dump($redis->copy('source1', 'exists', ['REPLACE' => true])); - * - * - * **Available Options** - * - * | OPTION | TYPE | DESCRIPTION | - * | --------------- | ---- | ----------- | - * | OPT_MAX_RETRIES | int | foo | - * * @param string $src The key to copy * @param string $dst The name of the new key created from the source key. * @param array $options An array with modifiers on how COPY should operate. @@ -491,27 +444,37 @@ public function connect(string $host, int $port = 6379, float $timeout = 0, stri * @return Redis|bool True if the copy was completed and false if not. * * @see https://redis.io/commands/copy + * + * @example + * $redis->pipeline() + * ->select(1) + * ->del('newkey') + * ->select(0) + * ->del('newkey') + * ->mset(['source1' => 'value1', 'exists' => 'old_value']) + * ->exec(); + * + * var_dump($redis->copy('source1', 'newkey')); + * var_dump($redis->copy('source1', 'newkey', ['db' => 1])); + * var_dump($redis->copy('source1', 'exists')); + * var_dump($redis->copy('source1', 'exists', ['REPLACE' => true])); */ public function copy(string $src, string $dst, array $options = null): Redis|bool; /** * Return the number of keys in the currently selected Redis database. * - * - * $redis = new Redis(['host' => 'localhost']); + * @see https://redis.io/commands/dbsize * - * $redis->flushdb(); + * @return Redis|int The number of keys or false on failure. * + * @example + * $redis = new Redis(['host' => 'localhost']); + * $redis->flushdb(); * $redis->set('foo', 'bar'); * var_dump($redis->dbsize()); - * * $redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']); * var_dump($redis->dbsize()); - * - * - * @see https://redis.io/commands/dbsize - * - * @return Redis|int The number of keys or false on failure. */ public function dbSize(): Redis|int|false; @@ -546,44 +509,32 @@ public function decr(string $key, int $by = 1): Redis|int|false; /** * Decrement a redis integer by a value * - * @see https://redis.io/commands/decrby - * - * @param string $key The integer key to decrement. - * @param int $value How much to decrement the key. - * - * @return Redis|int|false The new value of the key or false on failure. - * * - * 'localhost'); * * $redis->set('counter', 3); * var_dump($redis->decrby('counter', 1)); * var_dump($redis->decrby('counter', 2)); - * - * // --- OUTPUT --- - * // int(2) - * // int(0) - * ?> * + * + * @param string $key The integer key to decrement. + * @param int $value How much to decrement the key. + * + * @return Redis|int|false The new value of the key or false on failure. + * + * @see https://redis.io/commands/decrby + * */ public function decrBy(string $key, int $value): Redis|int|false; /** * Delete one or more keys from Redis. * - * @see https://redis.io/commands/del - * - * @param array|string $key_or_keys Either an array with one or more key names or a string with - * the name of a key. - * @param string $other_keys One or more additional keys passed in a variadic fashion. - * * This method can be called in two distinct ways. The first is to pass a single array * of keys to delete, and the second is to pass N arguments, all names of keys. See * below for an example of both strategies. * * - * 'localhost']); * * for ($i = 0; $i < 5; $i++) { @@ -592,12 +543,16 @@ public function decrBy(string $key, int $value): Redis|int|false; * * var_dump($redis->del('key:0', 'key:1')); * var_dump($redis->del(['key:2', 'key:3', 'key:4'])); - * - * // --- OUTPUT --- - * // int(2) - * // int(3) - * ?> * + * + * @param array|string $key_or_keys Either an array with one or more key names or a string with + * the name of a key. + * @param string $other_keys One or more additional keys passed in a variadic fashion. + * + * @return Redis|int|false The number of keys that were deleted + * + * @see https://redis.io/commands/del + * */ public function del(array|string $key, string ...$other_keys): Redis|int|false; @@ -610,10 +565,7 @@ public function delete(array|string $key, string ...$other_keys): Redis|int|fals /** * Discard a transaction currently in progress. * - * @return Redis|bool True if we could discard the transaction. - * * - * 'localhost']); * * $redis->multi()->set('foo', 'bar')->get('foo'); @@ -626,24 +578,17 @@ public function delete(array|string $key, string ...$other_keys): Redis|int|fals * * // Redis::ATOMIC * $redis->getMode(); - * - * ?> * + * + * @return Redis|bool True if we could discard the transaction. + * */ public function discard(): Redis|bool; - //public function restore(string $key, int $timeout, string $value, ?array $options = NULL): bool; /** * Dump Redis' internal binary representation of a key. * - * @see https://redis.io/commands/dump - * - * @param string $key The key to dump. - * - * @return Redis|string A binary string representing the key's value. - * * - * 'localhost']); * * $redis->del('zset'); @@ -656,38 +601,32 @@ public function discard(): Redis|bool; * // Retore it to a different name * $redis->restore('new-zset', 0, $binary); * - * // Array - * // ( - * // [zero] => 0 - * // [one] => 1 - * // [two] => 2 - * // ) * $redis->zRange('new-zset', 0, -1, true); - * ?> * + * + * @param string $key The key to dump. + * + * @return Redis|string A binary string representing the key's value. + * + * @see https://redis.io/commands/dump + * */ public function dump(string $key): Redis|string; /** * Have Redis repeat back an arbitrary string to the client. * - * @see https://redis.io/commands/echo - * - * @param string $str The string to echo - * - * @return Redis|string|false The string sent to Redis or false on failure. - * * - * 'localhost']); * * var_dump($redis->echo('Hello, World')); + * * - * // --- OUTPUT --- - * // string(12) "Hello, World" + * @param string $str The string to echo * - * ?> - * + * @return Redis|string|false The string sent to Redis or false on failure. + * + * @see https://redis.io/commands/echo */ public function echo(string $str): Redis|string|false; @@ -719,15 +658,18 @@ public function eval_ro(string $script_sha, array $args = [], int $num_keys = 0) * Execute a LUA script on the server but instead of sending the script, send * the SHA1 hash of the script. * - * @see https://redis.io/commands/evalsha/ - * @see Redis::eval(); - * * @param string $script_sha The SHA1 hash of the lua code. Note that the script * must already exist on the server, either having been * loaded with `SCRIPT LOAD` or having been executed directly * with `EVAL` first. * @param array $args Arguments to send to the script. * @param int $num_keys The number of arguments that are keys + * + * @return mixed Returns whatever the specific script does. + * + * @see https://redis.io/commands/evalsha/ + * @see Redis::eval(); + * */ public function evalsha(string $sha1, array $args = [], int $num_keys = 0): mixed; @@ -742,13 +684,6 @@ public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): m /** * Execute either a MULTI or PIPELINE block and return the array of replies. * - * @see https://redis.io/commands/exec - * @see https://redis.io/commands/multi - * @see Redis::pipeline() - * @see Redis::multi() - * - * @return Redis|array|false The array of pipeline'd or multi replies or false on failure. - * * * $redis = new Redis(['host' => 'localhost']); * @@ -760,36 +695,22 @@ public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): m * ->exec(); * * var_dump($res); - * - * // --- OUTPUT --- - * // array(4) { - * // [0]=> - * // bool(true) // set('foo', 'bar') - * // [1]=> - * // string(3) "bar" // get('foo') - * // [2]=> - * // int(1) // del('list') - * // [3]=> - * // int(3) // rpush('list', 'one', 'two', 'three') - * // } - * ?> * + * + * @return Redis|array|false The array of pipeline'd or multi replies or false on failure. + * + * @see https://redis.io/commands/exec + * @see https://redis.io/commands/multi + * @see Redis::pipeline() + * @see Redis::multi() + * */ public function exec(): Redis|array|false; /** * Test if one or more keys exist. * - * @see https://redis.io/commands/exists - * - * @param mixed $key Either an array of keys or a string key - * @param mixed $other_keys If the previous argument was a string, you may send any number of - * additional keys to test. - * - * @return Redis|int|bool The number of keys that do exist and false on failure - * * - * 'localhost']); * * $redis->multi() @@ -805,8 +726,15 @@ public function exec(): Redis|array|false; * // --- OUTPUT --- * // int(3) * // int(1) - * ?> * + * + * @param mixed $key Either an array of keys or a string key + * @param mixed $other_keys If the previous argument was a string, you may send any number of + * additional keys to test. + * + * @return Redis|int|bool The number of keys that do exist and false on failure + * + * @see https://redis.io/commands/exists */ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; @@ -815,27 +743,45 @@ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; * redis-server >= 7.0.0 you may send an additional "mode" argument which * modifies how the command will execute. * - * @see https://redis.io/commands/expire - * * @param string $key The key to set an expiration on. * @param string $mode A two character modifier that changes how the * command works. + * * NX - Set expiry only if key has no expiry * XX - Set expiry only if key has an expiry * LT - Set expiry only when new expiry is < current expiry * GT - Set expiry only when new expiry is > current expiry + * + * + * @return Redis|bool True if an expiration was set and false otherwise. + * @see https://redis.io/commands/expire + * */ public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|bool; - /** - * Set a key's expiration to a specific Unix timestamp in seconds. If - * connected to Redis >= 7.0.0 you can pass an optional 'mode' argument. + /* + * Set a key's expiration to a specific Unix timestamp in seconds. * + * If connected to Redis >= 7.0.0 you can pass an optional 'mode' argument. * @see Redis::expire() For a description of the mode argument. * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the - * command works. + * @param string $key The key to set an expiration on. + * + * @return Redis|bool True if an expiration was set, false if not. + * + */ + + /** + * Set a key to expire at an exact unix timestamp. + * + * @param string $key The key to set an expiration on. + * @param int $timestamp The unix timestamp to expire at. + * @param string $mode An option 'mode' that modifies how the command acts (see {@link Redis::expire}). + * @return Redis|bool True if an expiration was set, false if not. + * + * @see https://redis.io/commands/expireat + * @see https://redis.io/commands/expire + * @see Redis::expire() */ public function expireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool; @@ -844,15 +790,7 @@ public function failover(?array $to = null, bool $abort = false, int $timeout = /** * Get the expiration of a given key as a unix timestamp * - * @see https://redis.io/commands/expiretime - * - * @param string $key The key to check. - * - * @return Redis|int|false The timestamp when the key expires, or -1 if the key has no expiry - * and -2 if the key doesn't exist. - * * - * 'localhost']); * * $redis->set('expiry-key', 'this will last a very long time'); @@ -861,12 +799,15 @@ public function failover(?array $to = null, bool $abort = false, int $timeout = * $redis->expireAt('expiry-key', 7955144542); * * var_dump($redis->expiretime('expiry-key')); + * * - * // --- OUTPUT --- - * // int(7955144542) + * @param string $key The key to check. + * + * @return Redis|int|false The timestamp when the key expires, or -1 if the key has no expiry + * and -2 if the key doesn't exist. + * + * @see https://redis.io/commands/expiretime * - * ?>php - * */ public function expiretime(string $key): Redis|int|false; @@ -887,11 +828,9 @@ public function pexpiretime(string $key): Redis|int|false; * Deletes every key in all Redis databases * * @param bool $sync Whether to perform the task in a blocking or non-blocking way. - * when TRUE, PhpRedis will execute `FLUSHALL SYNC`, and when FALSE we - * will execute `FLUSHALL ASYNC`. If the argument is omitted, we - * simply execute `FLUSHALL` and whether it is SYNC or ASYNC depends - * on Redis' `lazyfree-lazy-user-flush` config setting. * @return bool + * + * @see https://redis.io/commands/flushall */ public function flushAll(?bool $sync = null): Redis|bool; @@ -899,11 +838,9 @@ public function flushAll(?bool $sync = null): Redis|bool; * Deletes all the keys of the currently selected database. * * @param bool $sync Whether to perform the task in a blocking or non-blocking way. - * when TRUE, PhpRedis will execute `FLUSHDB SYNC`, and when FALSE we - * will execute `FLUSHDB ASYNC`. If the argument is omitted, we - * simply execute `FLUSHDB` and whether it is SYNC or ASYNC depends - * on Redis' `lazyfree-lazy-user-flush` config setting. * @return bool + * + * @see https://redis.io/commands/flush */ public function flushDB(?bool $sync = null): Redis|bool; @@ -942,6 +879,16 @@ public function getBit(string $key, int $idx): Redis|int|false; public function getEx(string $key, array $options = []): Redis|string|bool; + /** + * Get the database number PhpRedis thinks we're connected to. + * + * This value is updated internally in PhpRedis each time {@link Redis::select} is called. + * + * @return The database we're connected to. + * + * @see Redis::select() + * @see https://redis.io/commands/select + */ public function getDBNum(): int; public function getDel(string $key): Redis|string|bool; @@ -994,14 +941,7 @@ public function getPort(): int; /** * Retrieve a substring of a string by index. * - * @param string $key The string to query. - * @param int $start The zero-based starting index. - * @param int $end The zero-based ending index. - * - * @return Redis|string|false The substring or false on failure. - * * - * 'localhost']); * * $word = 'Supercalifragilisticexpialidocious'; @@ -1012,13 +952,31 @@ public function getPort(): int; * * // string(7) "docious" * var_dump($redis->getRange('silly-word', -7, -1)); - * ?> + * + * + * @param string $key The string to query. + * @param int $start The zero-based starting index. + * @param int $end The zero-based ending index. + * + * @return Redis|string|false The substring or false on failure. + * + * @see https://redis.io/commands/getrange */ public function getRange(string $key, int $start, int $end): Redis|string|false; /** * Get the longest common subsequence between two string keys. * + * + * $redis = new Redis(['host' => 'localhost']); + * + * $redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc'); + * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc'); + * + * // string(37) "acccgcacggcaagtcgttccagcaactggcgctagc" + * var_dump($redis->lcs('seq1', 'seq2')); + * + * * @param string $key1 The first key to check * @param string $key2 The second key to check * @param array $options An optional array of modifiers for the comand. @@ -1040,19 +998,7 @@ public function getRange(string $key, int $start, int $end): Redis|string|false; * * @return Redis|string|array|int|false Various reply types depending on options. * - * - * - * 'localhost']); - * - * $redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc'); - * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc'); - * - * // string(37) "acccgcacggcaagtcgttccagcaactggcgctagc" - * var_dump($redis->lcs('seq1', 'seq2')); - * ?> + * @see https://redis.io/commands/lcs */ public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false; @@ -1066,13 +1012,7 @@ public function getReadTimeout(): float; /** * Sets a key and returns any previously set value, if the key already existed. * - * @param string $key The key to set. - * @param mixed $value The value to set the key to. - * - * @return Redis|string|false The old value of the key or false if it didn't exist. - * * - * 'localhost']); * * $redis->del('captain'); @@ -1082,8 +1022,14 @@ public function getReadTimeout(): float; * * // string(4) "Pike" * var_dump($redis->getset('captain', 'Kirk')); - * ?> * + * + * @param string $key The key to set. + * @param mixed $value The value to set the key to. + * + * @return Redis|string|false The old value of the key or false if it didn't exist. + * + * @see https://redis.io/commands/getset */ public function getset(string $key, mixed $value): Redis|string|false; @@ -1099,16 +1045,7 @@ public function getTransferredBytes(): int|false; /** * Remove one or more fields from a hash. * - * @see https://redis.io/commands/hdel - * - * @param string $key The hash key in question. - * @param string $field The first field to remove - * @param string $other_fields One or more additional fields to remove. - * - * @return Redis|int|false The number of fields actually removed. - * * - * 'localhost']); * * $redis->del('people'); @@ -1117,36 +1054,39 @@ public function getTransferredBytes(): int|false; * * // int(1) * $redis->hDel('comms', 'Mallory', 'Archibald'); - * ?> * + * + * @param string $key The hash key in question. + * @param string $field The first field to remove + * @param string $other_fields One or more additional fields to remove. + * + * @return Redis|int|false The number of fields actually removed. + * + * @see https://redis.io/commands/hdel */ public function hDel(string $key, string $field, string ...$other_fields): Redis|int|false; /** * Checks whether a field exists in a hash. * - * @see https://redis.io/commands/hexists - * - * @param string $key The hash to query. - * @param string $field The field to check - * - * @return Redis|bool True if it exists, false if not. - * * - * 'localhost']); * * $redis->del('captains'); * * $redis->hmset('captains', ['Kirk' => 'Enterprise', 'Picard' => 'Enterprise-D', 'Sisko' => 'Defiant']); * - * bool(false) * $redis->hExists('captains', 'Pike'); - * - * bool(true) * $redis->hExists('captains', 'Picard'); - * ?> * + * + * @param string $key The hash to query. + * @param string $field The field to check + * + * @return Redis|bool True if it exists, false if not. + * + * @see https://redis.io/commands/hexists + * */ public function hExists(string $key, string $field): Redis|bool; @@ -1155,14 +1095,7 @@ public function hGet(string $key, string $member): mixed; /** * Read every field and value from a hash. * - * @see https://redis.io/commands/hgetall - * - * @param string $key The hash to query. - * - * @return Redis|array|false All fields and values or false if the key didn't exist. - * * - * 'localhost']); * * $redis->del('comms'); @@ -1178,24 +1111,20 @@ public function hGet(string $key, string $member): mixed; * // string(7) "haxx00r" * // } * $redis->hGetAll('comms'); - * ?> * + * + * @param string $key The hash to query. + * @return Redis|array|false All fields and values or false if the key didn't exist. + * + * @see https://redis.io/commands/hgetall + * */ public function hGetAll(string $key): Redis|array|false; /** * Increment a hash field's value by an integer * - * @see https://redis.io/commands/hincrby - * - * @param string $key The hash to modify - * @param string $field The field to increment - * @param int $value How much to increment the value. - * - * @return Redis|int|false The new value of the field. - * * - * 'localhost']); * * $redis->del('player'); @@ -1207,22 +1136,22 @@ public function hGetAll(string $key): Redis|array|false; * * // int(5) * $redis->hIncrBy('player', 'level', 3); - * ?> * * + * @param string $key The hash to modify + * @param string $field The field to increment + * @param int $value How much to increment the value. + * + * @return Redis|int|false The new value of the field. + * + * @see https://redis.io/commands/hincrby + * */ public function hIncrBy(string $key, string $field, int $value): Redis|int|false; /** * Increment a hash field by a floating point value * - * @see https://redis.io/commands/hincrbyfloat - * - * @param string $key The hash with the field to increment. - * @param string $field The field to increment. - * - * @return Redis|float|false The field value after incremented. - * * * $redis = new Redis(['host' => 'localhost']); * @@ -1233,22 +1162,22 @@ public function hIncrBy(string $key, string $field, int $value): Redis|int|false * * // float(6.2831852) * $redis->hIncrByFloat('trig-numbers', 'tau', 2 * $pi); - * ?> * + * + * @param string $key The hash with the field to increment. + * @param string $field The field to increment. + * + * @return Redis|float|false The field value after incremented. + * + * @see https://redis.io/commands/hincrbyfloat + * */ public function hIncrByFloat(string $key, string $field, float $value): Redis|float|false; /** * Retrieve all of the fields of a hash. * - * @see https://redis.io/commands/hkeys - * - * @param string $key The hash to query. - * - * @return Redis|array|false The fields in the hash or false if the hash doesn't exist. - * * - * 'localhost']); * * $redis->del('ships'); @@ -1264,8 +1193,13 @@ public function hIncrByFloat(string $key, string $field, float $value): Redis|fl * // string(7) "Voyager" * // } * $redis->hKeys('ships'); - * ?> * + * + * @param string $key The hash to query. + * + * @return Redis|array|false The fields in the hash or false if the hash doesn't exist. + * + * @see https://redis.io/commands/hkeys */ public function hKeys(string $key): Redis|array|false; @@ -1283,15 +1217,7 @@ public function hLen(string $key): Redis|int|false; /** * Get one or more fields from a hash. * - * @see https://redis.io/commands/hmget - * - * @param string $key The hash to query. - * @param array $fields One or more fields to query in the hash. - * - * @return Redis|array|false The fields and values or false if the key didn't exist. - * * - * 'localhost']); * * $redis->del('player:1'); @@ -1305,35 +1231,50 @@ public function hLen(string $key): Redis|int|false; * // string(4) "1337" * // } * $redis->hmget('player:1', ['name', 'score']); - * ?> * + * + * @param string $key The hash to query. + * @param array $fields One or more fields to query in the hash. + * + * @return Redis|array|false The fields and values or false if the key didn't exist. + * + * @see https://redis.io/commands/hmget + * */ public function hMget(string $key, array $fields): Redis|array|false; /** * Add or update one or more hash fields and values * - * @see https://redis.io/commands/hmset + * + * $redis = new Redis(['host' => 'localhost']); + * + * $redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]); + * * * @param string $key The hash to create/update * @param array $fieldvals An associative array with fields and their values. * * @return Redis|bool True if the operation was successful * - * - * 'localhost']); + * @see https://redis.io/commands/hmset * - * $redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]); - * ?> - * */ public function hMset(string $key, array $fieldvals): Redis|bool; /** * Get one or more random field from a hash. * - * @see https://redis.io/commands/hrandfield + * + * $redis = new Redis(['host' => 'localhost']); + * + * $redis->del('settings'); + * + * $redis->hmset('settings', ['path' => '/', 'state' => 'active', 'jobs' => 15]); + * + * $redis->hrandfield('settings'); + * $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]); + * * * @param string $key The hash to query. * @param array $options An array of options to modify how the command behaves. @@ -1347,19 +1288,8 @@ public function hMset(string $key, array $fieldvals): Redis|bool; * * @return Redis|array|string One or more random fields (and possibly values). * - * - * 'localhost']); - * - * $redis->del('settings'); - * - * $redis->hmset('settings', ['path' => '/', 'state' => 'active', 'jobs' => 15]); - * - * $redis->hrandfield('settings'); + * @see https://redis.io/commands/hrandfield * - * $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]); - * ?> - * */ public function hRandField(string $key, array $options = null): Redis|string|array; @@ -1368,13 +1298,6 @@ public function hSet(string $key, string $member, mixed $value): Redis|int|false /** * Set a hash field and value, but only if that field does not exist * - * @see https://redis.io/commands/hsetnx - * - * @param string $key The hash to update. - * @param string $field The value to set. - * - * @return Redis|bool True if the field was set and false if not. - * * * $redis = new Redis(['host' => 'localhost']); * @@ -1388,30 +1311,31 @@ public function hSet(string $key, string $member, mixed $value): Redis|int|false * // bool(false) * var_dump($redis->hsetnx('player:1', 'lock', 'enabled')); * + * + * @param string $key The hash to update. + * @param string $field The value to set. + * + * @return Redis|bool True if the field was set and false if not. + * + * @see https://redis.io/commands/hsetnx */ public function hSetNx(string $key, string $field, string $value): Redis|bool; /** * Get the string length of a hash field * - * @see https://redis.io/commands/hstrlen - * * @param string $key The hash to query. * @param string $field The field to query. * * @return Redis|int|false The string length of the field or false. * - * - * 'localhost']); - * * $redis->del('hash'); * $redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]); - * - * // int(50) * $redis->hstrlen('hash', '50bytes'); * - * + * @see https://redis.io/commands/hstrlen */ public function hStrLen(string $key, string $field): Redis|int|false; diff --git a/redis_arginfo.h b/redis_arginfo.h index 09e3c1fe11..fa86cdc310 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4f4f62f9f49eb59c17c3dda8e0c3ae397a6df977 */ + * Stub hash: 0ca9052a6b2da623f76b015fa3271f4a9d1ffcf9 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index ab2f328c2e..1594edf73f 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4f4f62f9f49eb59c17c3dda8e0c3ae397a6df977 */ + * Stub hash: 0ca9052a6b2da623f76b015fa3271f4a9d1ffcf9 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From 2d365ee2add200897ed40d2dcfc33b5998a0cd8b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 15 Nov 2022 11:26:29 -0800 Subject: [PATCH 1712/1986] Documentation: Format docs and add remaining GEO* docblocks --- doctum-config.php | 6 +- redis.stub.php | 303 +++++++++++++++++++++++++++++++--------------- 2 files changed, 207 insertions(+), 102 deletions(-) diff --git a/doctum-config.php b/doctum-config.php index c1ac155e4d..4dd445ef4a 100644 --- a/doctum-config.php +++ b/doctum-config.php @@ -13,8 +13,8 @@ ->name('*.stub.php') ->in($root); -$versions = GitVersionCollection::create($root) - ->add('develop', 'develop'); +//$versions = GitVersionCollection::create($root) +// ->add('develop', 'develop'); return new Doctum($iterator, [ 'title' => 'PhpRedis API', @@ -23,6 +23,6 @@ 'build_dir' => "{$root}/docs", 'cache_dir' => "{$root}/docs/.cache", 'base_url' => 'https://phpredis.github.io/', - 'versions' => $versions, +// 'versions' => $versions, 'remote_repository' => new GitHubRemoteRepository('phpredis/phpredis', $root), ]); diff --git a/redis.stub.php b/redis.stub.php index a05859d070..3444966ce3 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -483,15 +483,6 @@ public function debug(string $key): Redis|string; /** * Decrement a Redis integer by 1 or a provided value. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->set('counter', 3); - * - * var_dump($redis->decr('counter')); - * var_dump($redis->decr('counter', 2)); - * - * * @param string $key The key to decrement * @param int $by How much to decrement the key. Note that if this value is * not sent or is set to `1`, PhpRedis will actually invoke @@ -503,20 +494,14 @@ public function debug(string $key): Redis|string; * @see https://redis.io/commands/decr * @see https://redis.io/commands/decrby * + * @example $redis->decr('counter'); + * @example $redis->decr('counter', 2); */ public function decr(string $key, int $by = 1): Redis|int|false; /** * Decrement a redis integer by a value * - * - * $redis = new Redis(['host' => 'localhost'); - * - * $redis->set('counter', 3); - * var_dump($redis->decrby('counter', 1)); - * var_dump($redis->decrby('counter', 2)); - * - * * @param string $key The integer key to decrement. * @param int $value How much to decrement the key. * @@ -524,6 +509,8 @@ public function decr(string $key, int $by = 1): Redis|int|false; * * @see https://redis.io/commands/decrby * + * @example $redis->decrby('counter', 1); + * @example $redis->decrby('counter', 2); */ public function decrBy(string $key, int $value): Redis|int|false; @@ -534,17 +521,6 @@ public function decrBy(string $key, int $value): Redis|int|false; * of keys to delete, and the second is to pass N arguments, all names of keys. See * below for an example of both strategies. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * for ($i = 0; $i < 5; $i++) { - * $redis->set("key:$i", "val:$i"); - * } - * - * var_dump($redis->del('key:0', 'key:1')); - * var_dump($redis->del(['key:2', 'key:3', 'key:4'])); - * - * * @param array|string $key_or_keys Either an array with one or more key names or a string with * the name of a key. * @param string $other_keys One or more additional keys passed in a variadic fashion. @@ -553,6 +529,8 @@ public function decrBy(string $key, int $value): Redis|int|false; * * @see https://redis.io/commands/del * + * @example $redis->del('key:0', 'key:1'); + * @example $redis->del(['key:2', 'key:3', 'key:4']); */ public function del(array|string $key, string ...$other_keys): Redis|int|false; @@ -570,37 +548,21 @@ public function delete(array|string $key, string ...$other_keys): Redis|int|fals * * $redis->multi()->set('foo', 'bar')->get('foo'); * - * // Redis::MULTI - * $redis->getMode(); - * - * // Discard the in-progress transaction - * $redis->discard(); - * - * // Redis::ATOMIC - * $redis->getMode(); * * * @return Redis|bool True if we could discard the transaction. * + * @example + * $redis->getMode(); + * $redis->set('foo', 'bar'); + * $redis->discard(); + * $redis->getMode(); */ public function discard(): Redis|bool; /** * Dump Redis' internal binary representation of a key. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('zset'); - * - * $redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two'); - * - * // Retrieve the binary representation of the zset - * $binary = $redis->dump('zset'); - * - * // Retore it to a different name - * $redis->restore('new-zset', 0, $binary); - * * $redis->zRange('new-zset', 0, -1, true); * * @@ -610,23 +572,23 @@ public function discard(): Redis|bool; * * @see https://redis.io/commands/dump * + * @example + * $redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two'); + * $binary = $redis->dump('zset'); + * $redis->restore('new-zset', 0, $binary); */ public function dump(string $key): Redis|string; /** * Have Redis repeat back an arbitrary string to the client. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * var_dump($redis->echo('Hello, World')); - * - * * @param string $str The string to echo * * @return Redis|string|false The string sent to Redis or false on failure. * * @see https://redis.io/commands/echo + * + * @example $redis->echo('Hello, World'); */ public function echo(string $str): Redis|string|false; @@ -650,7 +612,7 @@ public function eval(string $script, array $args = [], int $num_keys = 0): mixed * This is simply the read-only variant of eval, meaning the underlying script * may not modify data in redis. * - * @see Redis::eval() + * @see Redis::eval_ro() */ public function eval_ro(string $script_sha, array $args = [], int $num_keys = 0): mixed; @@ -684,19 +646,6 @@ public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): m /** * Execute either a MULTI or PIPELINE block and return the array of replies. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $res = $redis->multi() - * ->set('foo', 'bar') - * ->get('foo') - * ->del('list') - * ->rpush('list', 'one', 'two', 'three') - * ->exec(); - * - * var_dump($res); - * - * * @return Redis|array|false The array of pipeline'd or multi replies or false on failure. * * @see https://redis.io/commands/exec @@ -704,30 +653,19 @@ public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): m * @see Redis::pipeline() * @see Redis::multi() * + * @example + * $res = $redis->multi() + * ->set('foo', 'bar') + * ->get('foo') + * ->del('list') + * ->rpush('list', 'one', 'two', 'three') + * ->exec(); */ public function exec(): Redis|array|false; /** * Test if one or more keys exist. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->multi() - * ->mset(['k1' => 'v1', 'k2' => 'v2', 'k3' => 'v3', 'k4' => 'v4']) - * ->exec(); - * - * // Using a single array of keys - * var_dump($redis->exists(['k1', 'k2', 'k3'])); - * - * // Calling via variadic arguments - * var_dump($redis->exists('k4', 'k5', 'notakey')); - * - * // --- OUTPUT --- - * // int(3) - * // int(1) - * - * * @param mixed $key Either an array of keys or a string key * @param mixed $other_keys If the previous argument was a string, you may send any number of * additional keys to test. @@ -735,6 +673,9 @@ public function exec(): Redis|array|false; * @return Redis|int|bool The number of keys that do exist and false on failure * * @see https://redis.io/commands/exists + * + * @example $redis->exists(['k1', 'k2', 'k3']); + * @example $redis->exists('k4', 'k5', 'notakey'); */ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; @@ -790,17 +731,6 @@ public function failover(?array $to = null, bool $abort = false, int $timeout = /** * Get the expiration of a given key as a unix timestamp * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->set('expiry-key', 'this will last a very long time'); - * - * // Expire this key at 2222/02/22 02:22:22 GMT - * $redis->expireAt('expiry-key', 7955144542); - * - * var_dump($redis->expiretime('expiry-key')); - * - * * @param string $key The key to check. * * @return Redis|int|false The timestamp when the key expires, or -1 if the key has no expiry @@ -808,6 +738,9 @@ public function failover(?array $to = null, bool $abort = false, int $timeout = * * @see https://redis.io/commands/expiretime * + * @example + * $redis->setEx('mykey', 60, 'myval'); + * $redis->expiretime('mykey'); */ public function expiretime(string $key): Redis|int|false; @@ -840,30 +773,202 @@ public function flushAll(?bool $sync = null): Redis|bool; * @param bool $sync Whether to perform the task in a blocking or non-blocking way. * @return bool * - * @see https://redis.io/commands/flush + * @see https://redis.io/commands/flushdb */ public function flushDB(?bool $sync = null): Redis|bool; + /** + * Add one or more members to a geospacial sorted set + * + * @param string $key The sorted set to add data to. + * @param float $lng The longitude of the first member + * @param float $lat The lattitude of the first member. + * @param member $other_triples_and_options You can continue to pass longitude, lattitude, and member + * arguments to add as many members as you wish. Optionally, the final argument may be + * a string with options for the command @see Redis documentation for the options. + * + * @return Redis|int|false The number of added elements is returned. If the 'CH' option is specified, + * the return value is the number of members *changed*. + * + * @example $redis->geoAdd('cities', -121.8374, 39.7284, 'Chico', -122.03218, 37.322, 'Cupertino'); + * @example $redis->geoadd('cities', -121.837478, 39.728494, 'Chico', ['XX', 'CH']); + * + * @see https://redis.io/commands/geoadd + */ + public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options): Redis|int|false; + /** + * Get the distance between two members of a geospacially encoded sorted set. + * + * @param string $key The Sorted set to query. + * @param string $src The first member. + * @param string $dst The second member. + * @param string $unit Which unit to use when computing distance, defaulting to meters. + * + * M - meters + * KM - kilometers + * FT - feet + * MI - miles + * + * + * @return Redis|float|false The calculated distance in whichever units were specified or false + * if one or both members did not exist. + * + * @example $redis->geodist('cities', 'Chico', 'Cupertino', 'mi'); + * + * @see https://redis.io/commands/geodist + */ public function geodist(string $key, string $src, string $dst, ?string $unit = null): Redis|float|false; + /** + * Retrieve one or more GeoHash encoded strings for members of the set. + * + * @param string $key The key to query + * @param string $member The first member to request + * @param string $other_members One or more additional members to request. + * + * @return Redis|array|false An array of GeoHash encoded values. + * + * @see https://redis.io/commands/geohash + * @see https://en.wikipedia.org/wiki/Geohash + * + * @example $redis->geohash('cities', 'Chico', 'Cupertino'); + */ public function geohash(string $key, string $member, string ...$other_members): Redis|array|false; + /** + * Return the longitude and lattitude for one or more members of a geospacially encoded sorted set. + * + * @param string $key The set to query. + * @param string $member The first member to query. + * @param string $other_members One or more members to query. + * + * @return An array of longitude and lattitude pairs. + * + * @see https://redis.io/commands/geopos + * + * @example $redis->geopos('cities', 'Seattle', 'New York'); + */ public function geopos(string $key, string $member, string ...$other_members): Redis|array|false; + /** + * Retrieve members of a geospacially sorted set that are within a certain radius of a location. + * + * @param string $key The set to query + * @param float $lng The longitude of the location to query. + * @param float $lat The latitude of the location to query. + * @param float $radius The radius of the area to include. + * @param string $unit The unit of the provided radius (defaults to 'meters). + * See {@link Redis::geodist} for possible units. + * @param array $options An array of options that modifies how the command behaves. + * + * $options = [ + * 'WITHCOORD', // Return members and their coordinates. + * 'WITHDIST', // Return members and their distances from the center. + * 'WITHHASH', // Return members GeoHash string. + * 'ASC' | 'DESC', // The sort order of returned members + * + * // Limit to N returned members. Optionally a two element array may be + * // passed as the `LIMIT` argument, and the `ANY` argument. + * 'COUNT' => [], or [, ] + * + * // Instead of returning members, store them in the specified key. + * 'STORE' => + * + * // Store the distances in the specified key + * 'STOREDIST' => + * ]; + * + * + * @return mixed This command can return various things, depending on the options passed. + * + * @see https://redis.io/commands/georadius + * + * @example $redis->georadius('cities', 47.608013, -122.335167, 1000, 'km'); + */ public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed; + /** + * A readonly variant of `GEORADIUS` that may be executed on replicas. + * + * @see Redis::georadius + */ public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed; + /** + * Similar to `GEORADIUS` except it uses a member as the center of the query. + * + * @param string $key The key to query. + * @param string $member The member to treat as the center of the query. + * @param float $radius The radius from the member to include. + * @param string $unit The unit of the provided radius + * See {@link Redis::geodist} for possible units. + * @param array $options An array with various options to modify the command's behavior. + * See {@link Redis::georadius} for options. + * + * @return mixed This command can return various things depending on options. + * + * @example $redis->georadiusbymember('cities', 'Seattle', 200, 'mi'); + */ public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): mixed; + /** + * This is the read-only variant of `GEORADIUSBYMEMBER` that can be run on replicas. + */ public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): mixed; + /** + * Search a geospacial sorted set for members in various ways. + * + * @param string $key The set to query. + * @param array|string $position Either a two element array with longitude and lattitude, or + * a string representing a member of the set. + * @param array|int|float $shape Either a number representine the radius of a circle to search, or + * a two element array representing the width and height of a box + * to search. + * @param string $unit The unit of our shape. See {@link Redis::geodist} for possible units. + * @param array $options @see {@link Redis::georadius} for options. Note that the `STORE` + * options are not allowed for this command. + */ public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): array; + /** + * Search a geospacial sorted set for members within a given area or range, storing the results into + * a new set. + * + * @param string $dst The destination where results will be stored. + * @param string $src The key to query. + * @param array|string $position Either a two element array with longitude and lattitude, or + * a string representing a member of the set. + * @param array|int|float $shape Either a number representine the radius of a circle to search, or + * a two element array representing the width and height of a box + * to search. + * @param string $unit The unit of our shape. See {@link Redis::geodist} for possible units. + * @param array $options + * + * $options = [ + * 'ASC' | 'DESC', // The sort order of returned members + * 'WITHDIST' // Also store distances. + * + * // Limit to N returned members. Optionally a two element array may be + * // passed as the `LIMIT` argument, and the `ANY` argument. + * 'COUNT' => [], or [, ] + * ]; + * + */ public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): Redis|array|int|false; + /** + * Retrieve a string keys value. + * + * @param string $key The key to query + * @return mixed The keys value or false if it did not exist. + * + * @see https://redis.io/commands/get + * + * @example $redis->get('foo'); + */ public function get(string $key): mixed; /** From 53d142d93a7875f0cd1ebbfc136033adc6ab79e7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 15 Nov 2022 15:07:03 -0800 Subject: [PATCH 1713/1986] Documentation: Add more docblocks and fix up formatting. --- docs/classes.html | 12 +- docs/doc-index.html | 591 ++------------------------------------ docs/doctum-search.json | 2 +- docs/doctum.js | 2 +- docs/index.html | 12 +- docs/interfaces.html | 2 +- docs/namespaces.html | 2 +- docs/opensearch.xml | 4 +- docs/renderer.index | 2 +- docs/search.html | 2 +- docs/traits.html | 4 +- redis.stub.php | 618 +++++++++++++++++++++------------------- 12 files changed, 363 insertions(+), 890 deletions(-) diff --git a/docs/classes.html b/docs/classes.html index 255cc79b28..b7243ccf94 100644 --- a/docs/classes.html +++ b/docs/classes.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -82,11 +82,6 @@

Classes

-
- Redis
-
-
-
@@ -100,11 +95,6 @@

Classes

-
-
diff --git a/docs/doc-index.html b/docs/doc-index.html index 792d88cd06..75d12cbe59 100644 --- a/docs/doc-index.html +++ b/docs/doc-index.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -95,7 +95,7 @@

Index

  • L
  • M
  • N
  • -
  • O
  • +
  • O
  • P
  • Q
  • R
  • @@ -103,55 +103,19 @@

    Index

  • T
  • U
  • V
  • -
  • W
  • +
  • W
  • X
  • Y
  • Z
  • A

    -
    -Redis::acl() — Method in class Redis
    -
    -Redis::append() — Method in class Redis
    -

    Append data to a Redis STRING key.

    -Redis::auth() — Method in class Redis
    -

    Authenticate a Redis connection after its been established.

    +
    RedisCluster::acl() — Method in class RedisCluster
    RedisCluster::append() — Method in class RedisCluster

    B

    -
    -Redis::bgSave() — Method in class Redis
    -

    Execute a save of the Redis database in the background.

    -Redis::bgrewriteaof() — Method in class Redis
    -

    Asynchronously rewrite Redis' append-only file

    -Redis::bitcount() — Method in class Redis
    -

    Count the number of set bits in a Redis string.

    -Redis::bitop() — Method in class Redis
    -
    -Redis::bitpos() — Method in class Redis
    -

    Return the position of the first bit set to 0 or 1 in a string.

    -Redis::blPop() — Method in class Redis
    -

    Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified -timeout. This method may be called in two distinct ways, of which examples are provided below.

    -Redis::brPop() — Method in class Redis
    -

    Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

    -Redis::brpoplpush() — Method in class Redis
    -

    Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list, -optionally blocking up to a specified timeout.

    -Redis::bzPopMax() — Method in class Redis
    -

    POP the maximum scoring element off of one or more sorted sets, blocking up to a specified -timeout if no elements are available.

    -Redis::bzPopMin() — Method in class Redis
    -

    POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout -if no elements are available

    -Redis::bzmpop() — Method in class Redis
    -

    POP one or more elements from one or more sorted sets, blocking up to a specified amount of time -when no elements are available.

    -Redis::blmpop() — Method in class Redis
    -

    Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when -no elements are available.

    +
    RedisArray::bgsave() — Method in class RedisArray
    RedisCluster::bgrewriteaof() — Method in class RedisCluster
    @@ -178,21 +142,7 @@

    A

    RedisCluster::blmpop() — Method in class RedisCluster

    C

    -
    -Redis::clearLastError() — Method in class Redis
    -

    Reset any last error on the connection to NULL

    -Redis::client() — Method in class Redis
    -
    -Redis::close() — Method in class Redis
    -
    -Redis::command() — Method in class Redis
    -
    -Redis::config() — Method in class Redis
    -

    Execute the Redis CONFIG command in a variety of ways.

    -Redis::connect() — Method in class Redis
    -
    -Redis::copy() — Method in class Redis
    -

    Make a copy of a key.

    +
    RedisCluster::clearlasterror() — Method in class RedisCluster
    RedisCluster::client() — Method in class RedisCluster
    @@ -207,23 +157,7 @@

    A

    RedisSentinel::ckquorum() — Method in class RedisSentinel

    D

    -
    -Redis::dbSize() — Method in class Redis
    -

    Return the number of keys in the currently selected Redis database.

    -Redis::debug() — Method in class Redis
    -
    -Redis::decr() — Method in class Redis
    -

    Decrement a Redis integer by 1 or a provided value.

    -Redis::decrBy() — Method in class Redis
    -

    Decrement a redis integer by a value

    -Redis::del() — Method in class Redis
    -

    Delete one or more keys from Redis.

    -Redis::delete() — Method in class Redis
    -
    -Redis::discard() — Method in class Redis
    -

    Discard a transaction currently in progress.

    -Redis::dump() — Method in class Redis
    -

    Dump Redis' internal binary representation of a key.

    +
    RedisArray::del() — Method in class RedisArray
    RedisArray::discard() — Method in class RedisArray
    @@ -242,32 +176,7 @@

    A

    RedisCluster::dump() — Method in class RedisCluster

    E

    -
    -Redis::echo() — Method in class Redis
    -

    Have Redis repeat back an arbitrary string to the client.

    -Redis::eval() — Method in class Redis
    -

    Execute a LUA script on the redis server.

    -Redis::eval_ro() — Method in class Redis
    -

    This is simply the read-only variant of eval, meaning the underlying script -may not modify data in redis.

    -Redis::evalsha() — Method in class Redis
    -

    Execute a LUA script on the server but instead of sending the script, send -the SHA1 hash of the script.

    -Redis::evalsha_ro() — Method in class Redis
    -

    This is simply the read-only variant of evalsha, meaning the underlying script -may not modify data in redis.

    -Redis::exec() — Method in class Redis
    -

    Execute either a MULTI or PIPELINE block and return the array of replies.

    -Redis::exists() — Method in class Redis
    -

    Test if one or more keys exist.

    -Redis::expire() — Method in class Redis
    -

    Sets an expiration in seconds on the key in question. If connected to -redis-server >= 7.0.0 you may send an additional "mode" argument which -modifies how the command will execute.

    -Redis::expireAt() — Method in class Redis
    -

    Set a key to expire at an exact unix timestamp.

    -Redis::expiretime() — Method in class Redis
    -

    Get the expiration of a given key as a unix timestamp

    +
    RedisArray::exec() — Method in class RedisArray
    RedisCluster::echo() — Method in class RedisCluster
    @@ -290,13 +199,7 @@

    A

    RedisCluster::expiretime() — Method in class RedisCluster

    F

    -
    -Redis::failover() — Method in class Redis
    -
    -Redis::flushAll() — Method in class Redis
    -

    Deletes every key in all Redis databases

    -Redis::flushDB() — Method in class Redis
    -

    Deletes all the keys of the currently selected database.

    +
    RedisArray::flushall() — Method in class RedisArray
    RedisArray::flushdb() — Method in class RedisArray
    @@ -309,61 +212,7 @@

    A

    RedisSentinel::flushconfig() — Method in class RedisSentinel

    G

    -
    -Redis::geoadd() — Method in class Redis
    -
    -Redis::geodist() — Method in class Redis
    -
    -Redis::geohash() — Method in class Redis
    -
    -Redis::geopos() — Method in class Redis
    -
    -Redis::georadius() — Method in class Redis
    -
    -Redis::georadius_ro() — Method in class Redis
    -
    -Redis::georadiusbymember() — Method in class Redis
    -
    -Redis::georadiusbymember_ro() — Method in class Redis
    -
    -Redis::geosearch() — Method in class Redis
    -
    -Redis::geosearchstore() — Method in class Redis
    -
    -Redis::get() — Method in class Redis
    -
    -Redis::getAuth() — Method in class Redis
    -

    Get the authentication information on the connection, if any.

    -Redis::getBit() — Method in class Redis
    -
    -Redis::getEx() — Method in class Redis
    -
    -Redis::getDBNum() — Method in class Redis
    -

    Get the database number PhpRedis thinks we're connected to.

    -Redis::getDel() — Method in class Redis
    -
    -Redis::getHost() — Method in class Redis
    -

    Return the host or Unix socket we are connected to.

    -Redis::getLastError() — Method in class Redis
    -

    Get the last error returned to us from Redis, if any.

    -Redis::getMode() — Method in class Redis
    -

    Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

    -Redis::getOption() — Method in class Redis
    -

    Retrieve the value of a configuration setting as set by Redis::setOption()

    -Redis::getPersistentID() — Method in class Redis
    -

    Get the persistent connection ID, if there is one.

    -Redis::getPort() — Method in class Redis
    -

    Get the port we are connected to. This number will be zero if we are connected to a unix socket.

    -Redis::getRange() — Method in class Redis
    -

    Retrieve a substring of a string by index.

    -Redis::getReadTimeout() — Method in class Redis
    -

    Get the currently set read timeout on the connection.

    -Redis::getset() — Method in class Redis
    -

    Sets a key and returns any previously set value, if the key already existed.

    -Redis::getTimeout() — Method in class Redis
    -

    Retrieve any set connection timeout

    -Redis::getTransferredBytes() — Method in class Redis
    -
    +
    RedisArray::getOption() — Method in class RedisArray
    RedisCluster::geoadd() — Method in class RedisCluster
    @@ -400,39 +249,7 @@

    A

    RedisSentinel::getMasterAddrByName() — Method in class RedisSentinel

    H

    -
    -Redis::hDel() — Method in class Redis
    -

    Remove one or more fields from a hash.

    -Redis::hExists() — Method in class Redis
    -

    Checks whether a field exists in a hash.

    -Redis::hGet() — Method in class Redis
    -
    -Redis::hGetAll() — Method in class Redis
    -

    Read every field and value from a hash.

    -Redis::hIncrBy() — Method in class Redis
    -

    Increment a hash field's value by an integer

    -Redis::hIncrByFloat() — Method in class Redis
    -

    Increment a hash field by a floating point value

    -Redis::hKeys() — Method in class Redis
    -

    Retrieve all of the fields of a hash.

    -Redis::hLen() — Method in class Redis
    -

    Get the number of fields in a hash.

    -Redis::hMget() — Method in class Redis
    -

    Get one or more fields from a hash.

    -Redis::hMset() — Method in class Redis
    -

    Add or update one or more hash fields and values

    -Redis::hRandField() — Method in class Redis
    -

    Get one or more random field from a hash.

    -Redis::hSet() — Method in class Redis
    -
    -Redis::hSetNx() — Method in class Redis
    -

    Set a hash field and value, but only if that field does not exist

    -Redis::hStrLen() — Method in class Redis
    -

    Get the string length of a hash field

    -Redis::hVals() — Method in class Redis
    -

    Get all of the values from a hash.

    -Redis::hscan() — Method in class Redis
    -

    Iterate over the fields and values of a hash in an incremental fashion.

    +
    RedisArray::hscan() — Method in class RedisArray
    RedisCluster::hdel() — Method in class RedisCluster
    @@ -465,20 +282,7 @@

    A

    RedisCluster::hvals() — Method in class RedisCluster

    I

    -
    -Redis::incr() — Method in class Redis
    -

    Increment a key's value, optionally by a specifc amount.

    -Redis::incrBy() — Method in class Redis
    -

    Increment a key by a specific integer value

    -Redis::incrByFloat() — Method in class Redis
    -

    Increment a numeric key by a floating point value.

    -Redis::info() — Method in class Redis
    -

    Retrieve information about the connected redis-server. If no arguments are passed to -this function, redis will return every info field. Alternatively you may pass a specific -section you want returned (e.g. 'server', or 'memory') to receive only information pertaining -to that section.

    -Redis::isConnected() — Method in class Redis
    -

    Check if we are currently connected to a Redis instance.

    +
    RedisArray::info() — Method in class RedisArray
    RedisCluster::incr() — Method in class RedisCluster
    @@ -492,44 +296,12 @@

    A

    this function, redis will return every info field. Alternatively you may pass a specific section you want returned (e.g. 'server', or 'memory') to receive only information pertaining to that section.

    K

    -
    -Redis::keys() — Method in class Redis
    -
    +
    RedisArray::keys() — Method in class RedisArray
    RedisCluster::keys() — Method in class RedisCluster

    L

    -
    -Redis::lmpop() — Method in class Redis
    -

    Pop one or more elements off of one or more Redis LISTs.

    -Redis::lcs() — Method in class Redis
    -

    Get the longest common subsequence between two string keys.

    -Redis::lInsert() — Method in class Redis
    -
    -Redis::lLen() — Method in class Redis
    -
    -Redis::lMove() — Method in class Redis
    -
    -Redis::lPop() — Method in class Redis
    -
    -Redis::lPos() — Method in class Redis
    -
    -Redis::lPush() — Method in class Redis
    -
    -Redis::lPushx() — Method in class Redis
    -
    -Redis::lSet() — Method in class Redis
    -
    -Redis::lastSave() — Method in class Redis
    -
    -Redis::lindex() — Method in class Redis
    -
    -Redis::lrange() — Method in class Redis
    -
    -Redis::lrem() — Method in class Redis
    -
    -Redis::ltrim() — Method in class Redis
    -
    +
    RedisCluster::lmpop() — Method in class RedisCluster
    RedisCluster::lcs() — Method in class RedisCluster
    @@ -558,19 +330,7 @@

    A

    RedisCluster::ltrim() — Method in class RedisCluster

    M

    -
    -Redis::mget() — Method in class Redis
    -
    -Redis::migrate() — Method in class Redis
    -
    -Redis::move() — Method in class Redis
    -
    -Redis::mset() — Method in class Redis
    -
    -Redis::msetnx() — Method in class Redis
    -
    -Redis::multi() — Method in class Redis
    -
    +
    RedisArray::mget() — Method in class RedisArray
    RedisArray::mset() — Method in class RedisArray
    @@ -591,50 +351,10 @@

    A

    RedisSentinel::myid() — Method in class RedisSentinel

    O

    -
    -Redis::object() — Method in class Redis
    -
    -Redis::open() — Method in class Redis
    -
    +
    RedisCluster::object() — Method in class RedisCluster

    P

    -
    -Redis::pexpiretime() — Method in class Redis
    -

    Get the expriation timestamp of a given Redis key but in milliseconds.

    -Redis::pconnect() — Method in class Redis
    -
    -Redis::persist() — Method in class Redis
    -
    -Redis::pexpire() — Method in class Redis
    -

    Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 -you can pass an optional mode argument that modifies how the command will execute.

    -Redis::pexpireAt() — Method in class Redis
    -

    Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to -Redis >= 7.0.0 you can pass an optional 'mode' argument.

    -Redis::pfadd() — Method in class Redis
    -

    Add one or more elements to a Redis HyperLogLog key

    -Redis::pfcount() — Method in class Redis
    -

    Retrieve the cardinality of a Redis HyperLogLog key.

    -Redis::pfmerge() — Method in class Redis
    -

    Merge one or more source HyperLogLog sets into a destination set.

    -Redis::ping() — Method in class Redis
    -

    PING the redis server with an optional string argument.

    -Redis::pipeline() — Method in class Redis
    -

    Enter into pipeline mode.

    -Redis::popen() — Method in class Redis
    -
    -Redis::psetex() — Method in class Redis
    -
    -Redis::psubscribe() — Method in class Redis
    -

    Subscribe to one or more glob-style patterns

    -Redis::pttl() — Method in class Redis
    -

    Get a keys time to live in milliseconds.

    -Redis::publish() — Method in class Redis
    -

    Publish a message to a pubsub channel

    -Redis::pubsub() — Method in class Redis
    -
    -Redis::punsubscribe() — Method in class Redis
    -

    Unsubscribe from one or more channels by pattern

    +
    RedisArray::ping() — Method in class RedisArray
    RedisCluster::pexpiretime() — Method in class RedisCluster
    @@ -667,34 +387,7 @@

    A

    RedisSentinel::ping() — Method in class RedisSentinel

    R

    -
    Redis
    -
    -Redis::rPush() — Method in class Redis
    -
    -Redis::rPushx() — Method in class Redis
    -
    -Redis::rPop() — Method in class Redis
    -

    Pop one or more elements from the end of a list.

    -Redis::randomKey() — Method in class Redis
    -

    Return a random key from the current database

    -Redis::rawcommand() — Method in class Redis
    -

    Execute any arbitrary Redis command by name.

    -Redis::rename() — Method in class Redis
    -

    Unconditionally rename a key from $old_name to $new_name

    -Redis::renameNx() — Method in class Redis
    -

    Renames $key_src to $key_dst but only if newkey does not exist.

    -Redis::reset() — Method in class Redis
    -

    Reset the state of the connection.

    -Redis::restore() — Method in class Redis
    -

    Restore a key by the binary payload generated by the DUMP command.

    -Redis::role() — Method in class Redis
    -

    Query whether the connected instance is a primary or replica

    -Redis::rpoplpush() — Method in class Redis
    -

    Atomically pop an element off the end of a Redis LIST and push it to the beginning of -another.

    -Redis::replicaof() — Method in class Redis
    -

    Used to turn a Redis instance into a replica of another, or to remove -replica status promoting the instance to a primary.

    RedisArray
    +
    RedisArray
    RedisCluster
    RedisCluster::randomkey() — Method in class RedisCluster
    @@ -717,100 +410,11 @@

    A

    RedisCluster::rpushx() — Method in class RedisCluster
    RedisClusterException
    -
    RedisException
    RedisSentinel
    RedisSentinel::reset() — Method in class RedisSentinel

    S

    -
    -Redis::sAdd() — Method in class Redis
    -

    Add one or more values to a Redis SET key.

    -Redis::sAddArray() — Method in class Redis
    -

    Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but -instead of being variadic, takes a single array of values.

    -Redis::sDiff() — Method in class Redis
    -

    Given one or more Redis SETS, this command returns all of the members from the first -set that are not in any subsequent set.

    -Redis::sDiffStore() — Method in class Redis
    -

    This method performs the same operation as SDIFF except it stores the resulting diff -values in a specified destination key.

    -Redis::sInter() — Method in class Redis
    -

    Given one or more Redis SET keys, this command will return all of the elements that are -in every one.

    -Redis::sintercard() — Method in class Redis
    -

    Compute the intersection of one or more sets and return the cardinality of the result.

    -Redis::sInterStore() — Method in class Redis
    -

    Perform the intersection of one or more Redis SETs, storing the result in a destination -key, rather than returning them.

    -Redis::sMembers() — Method in class Redis
    -

    Retrieve every member from a set key.

    -Redis::sMisMember() — Method in class Redis
    -

    Check if one or more values are members of a set.

    -Redis::sMove() — Method in class Redis
    -

    Pop a member from one set and push it onto another. This command will create the -destination set if it does not currently exist.

    -Redis::sPop() — Method in class Redis
    -

    Remove one or more elements from a set.

    -Redis::sRandMember() — Method in class Redis
    -

    Retrieve one or more random members of a set.

    -Redis::sUnion() — Method in class Redis
    -

    Returns the union of one or more Redis SET keys.

    -Redis::sUnionStore() — Method in class Redis
    -

    Perform a union of one or more Redis SET keys and store the result in a new set

    -Redis::save() — Method in class Redis
    -

    Persist the Redis database to disk. This command will block the server until the save is -completed. For a nonblocking alternative, see Redis::bgsave().

    -Redis::scan() — Method in class Redis
    -

    Incrementally scan the Redis keyspace, with optional pattern and type matching.

    -Redis::scard() — Method in class Redis
    -

    Retrieve the number of members in a Redis set.

    -Redis::script() — Method in class Redis
    -

    An administrative command used to interact with LUA scripts stored on the server.

    -Redis::select() — Method in class Redis
    -

    Select a specific Redis database.

    -Redis::set() — Method in class Redis
    -

    Create or set a Redis STRING key to a value.

    -Redis::setBit() — Method in class Redis
    -

    Set a specific bit in a Redis string to zero or one

    -Redis::setRange() — Method in class Redis
    -

    Update or append to a Redis string at a specific starting index

    -Redis::setOption() — Method in class Redis
    -

    Set a configurable option on the Redis object.

    -Redis::setex() — Method in class Redis
    -

    Set a Redis STRING key with a specific expiration in seconds.

    -Redis::setnx() — Method in class Redis
    -

    Set a key to a value, but only if that key does not already exist.

    -Redis::sismember() — Method in class Redis
    -

    Check whether a given value is the member of a Redis SET.

    -Redis::slaveof() — Method in class Redis
    -

    Turn a redis instance into a replica of another or promote a replica -to a primary.

    -Redis::slowlog() — Method in class Redis
    -

    Interact with Redis' slowlog functionality in various ways, depending -on the value of 'operation'.

    -Redis::sort() — Method in class Redis
    -

    Sort the contents of a Redis key in various ways.

    -Redis::sort_ro() — Method in class Redis
    -

    This is simply a read-only variant of the sort command

    -Redis::sortAsc() — Method in class Redis
    -
    -Redis::sortAscAlpha() — Method in class Redis
    -
    -Redis::sortDesc() — Method in class Redis
    -
    -Redis::sortDescAlpha() — Method in class Redis
    -
    -Redis::srem() — Method in class Redis
    -

    Remove one or more values from a Redis SET key.

    -Redis::sscan() — Method in class Redis
    -

    Scan the members of a redis SET key.

    -Redis::strlen() — Method in class Redis
    -

    Retrieve the length of a Redis STRING key.

    -Redis::subscribe() — Method in class Redis
    -

    Subscribe to one or more Redis pubsub channels.

    -Redis::swapdb() — Method in class Redis
    -

    Atomically swap two Redis databases so that all of the keys in the source database will -now be in the destination database and vice-versa.

    +
    RedisArray::save() — Method in class RedisArray
    RedisArray::scan() — Method in class RedisArray
    @@ -887,15 +491,7 @@

    A

    RedisSentinel::slaves() — Method in class RedisSentinel

    T

    -
    -Redis::touch() — Method in class Redis
    -

    Update one or more keys last modified metadata.

    -Redis::time() — Method in class Redis
    -

    Retrieve the server time from the connected Redis instance.

    -Redis::ttl() — Method in class Redis
    -

    Get the amount of time a Redis key has before it will expire, in seconds.

    -Redis::type() — Method in class Redis
    -

    Get the type of a given Redis key.

    +
    RedisCluster::touch() — Method in class RedisCluster
    RedisCluster::time() — Method in class RedisCluster
    @@ -904,15 +500,7 @@

    A

    RedisCluster::type() — Method in class RedisCluster

    U

    -
    -Redis::unlink() — Method in class Redis
    -

    Delete one or more keys from the Redis database. Unlike this operation, the actual -deletion is asynchronous, meaning it is safe to delete large keys without fear of -Redis blocking for a long period of time.

    -Redis::unsubscribe() — Method in class Redis
    -

    Unsubscribe from one or more subscribed channels.

    -Redis::unwatch() — Method in class Redis
    -

    Remove any previously WATCH'ed keys in a transaction.

    +
    RedisArray::unlink() — Method in class RedisArray
    RedisArray::unwatch() — Method in class RedisArray
    @@ -923,44 +511,10 @@

    A

    RedisCluster::unwatch() — Method in class RedisCluster

    W

    -
    -Redis::watch() — Method in class Redis
    -
    -Redis::wait() — Method in class Redis
    -

    Block the client up to the provided timeout until a certain number of replicas have confirmed -recieving them.

    +
    RedisCluster::watch() — Method in class RedisCluster

    X

    -
    -Redis::xack() — Method in class Redis
    -
    -Redis::xadd() — Method in class Redis
    -

    Append a message to a stream.

    -Redis::xautoclaim() — Method in class Redis
    -
    -Redis::xclaim() — Method in class Redis
    -
    -Redis::xdel() — Method in class Redis
    -

    Remove one or more specific IDs from a stream.

    -Redis::xgroup() — Method in class Redis
    -
    XGROUP
    -Redis::xinfo() — Method in class Redis
    -

    Retrieve information about a stream key.

    -Redis::xlen() — Method in class Redis
    -

    Get the number of messages in a Redis STREAM key.

    -Redis::xpending() — Method in class Redis
    -

    Interact with stream messages that have been consumed by a consumer group but not yet -acknowledged with XACK.

    -Redis::xrange() — Method in class Redis
    -

    Get a range of entries from a STREAM key.

    -Redis::xread() — Method in class Redis
    -

    Consume one or more unconsumed elements in one or more streams.

    -Redis::xreadgroup() — Method in class Redis
    -

    Read one or more messages using a consumer group.

    -Redis::xrevrange() — Method in class Redis
    -

    Get a range of entries from a STREAM ke in reverse cronological order.

    -Redis::xtrim() — Method in class Redis
    -

    Truncate a STREAM key in various ways.

    +
    RedisCluster::xack() — Method in class RedisCluster
    RedisCluster::xadd() — Method in class RedisCluster
    @@ -987,75 +541,7 @@

    A

    RedisCluster::xtrim() — Method in class RedisCluster

    Z

    -
    -Redis::zmpop() — Method in class Redis
    -

    POP one or more of the highest or lowest scoring elements from one or more sorted sets.

    -Redis::zAdd() — Method in class Redis
    -

    Add one or more elements and scores to a Redis sorted set.

    -Redis::zCard() — Method in class Redis
    -

    Return the number of elements in a sorted set.

    -Redis::zCount() — Method in class Redis
    -

    Count the number of members in a sorted set with scores inside a provided range.

    -Redis::zIncrBy() — Method in class Redis
    -

    Create or increment the score of a member in a Redis sorted set

    -Redis::zLexCount() — Method in class Redis
    -

    Count the number of elements in a sorted set whos members fall within the provided -lexographical range.

    -Redis::zMscore() — Method in class Redis
    -

    Retrieve the score of one or more members in a sorted set.

    -Redis::zPopMax() — Method in class Redis
    -

    Pop one or more of the highest scoring elements from a sorted set.

    -Redis::zPopMin() — Method in class Redis
    -

    Pop one or more of the lowest scoring elements from a sorted set.

    -Redis::zRange() — Method in class Redis
    -

    Retrieve a range of elements of a sorted set between a start and end point.

    -Redis::zRangeByLex() — Method in class Redis
    -

    Retrieve a range of elements from a sorted set by legographical range.

    -Redis::zRangeByScore() — Method in class Redis
    -

    Retrieve a range of members from a sorted set by their score.

    -Redis::zrangestore() — Method in class Redis
    -

    This command is similar to ZRANGE except that instead of returning the values directly -it will store them in a destination key provided by the user

    -Redis::zRandMember() — Method in class Redis
    -

    Retrieve one or more random members from a Redis sorted set.

    -Redis::zRank() — Method in class Redis
    -

    Get the rank of a member of a sorted set, by score.

    -Redis::zRem() — Method in class Redis
    -

    Remove one or more members from a Redis sorted set.

    -Redis::zRemRangeByLex() — Method in class Redis
    -

    Remove zero or more elements from a Redis sorted set by legographical range.

    -Redis::zRemRangeByRank() — Method in class Redis
    -

    Remove one or more members of a sorted set by their rank.

    -Redis::zRemRangeByScore() — Method in class Redis
    -

    Remove one or more members of a sorted set by their score.

    -Redis::zRevRange() — Method in class Redis
    -

    List the members of a Redis sorted set in reverse order

    -Redis::zRevRangeByLex() — Method in class Redis
    -

    List members of a Redis sorted set within a legographical range, in reverse order.

    -Redis::zRevRangeByScore() — Method in class Redis
    -

    List elements from a Redis sorted set by score, highest to lowest

    -Redis::zRevRank() — Method in class Redis
    -

    Retrieve a member of a sorted set by reverse rank.

    -Redis::zScore() — Method in class Redis
    -

    Get the score of a member of a sorted set.

    -Redis::zdiff() — Method in class Redis
    -

    Given one or more sorted set key names, return every element that is in the first -set but not any of the others.

    -Redis::zdiffstore() — Method in class Redis
    -

    Store the difference of one or more sorted sets in a destination sorted set.

    -Redis::zinter() — Method in class Redis
    -

    Compute the intersection of one or more sorted sets and return the members

    -Redis::zintercard() — Method in class Redis
    -

    Similar to ZINTER but instead of returning the intersected values, this command returns the -cardinality of the intersected set.

    -Redis::zinterstore() — Method in class Redis
    -

    Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

    -Redis::zscan() — Method in class Redis
    -

    Scan the members of a sorted set incrementally, using a cursor

    -Redis::zunion() — Method in class Redis
    -

    Retrieve the union of one or more sorted sets

    -Redis::zunionstore() — Method in class Redis
    -

    Perform a union on one or more Redis sets and store the result in a destination sorted set.

    +
    RedisArray::zscan() — Method in class RedisArray
    RedisCluster::zmpop() — Method in class RedisCluster
    @@ -1110,34 +596,7 @@

    A

    RedisCluster::zunionstore() — Method in class RedisCluster

    _

    -
    -Redis::__construct() — Method in class Redis
    -

    Create a new Redis instance. If passed sufficient information in the -options array it is also possible to connect to an instance at the same -time.

    -Redis::__destruct() — Method in class Redis
    -
    -Redis::_compress() — Method in class Redis
    -

    Compress a value with the currently configured compressor as set with -Redis::setOption().

    -Redis::_uncompress() — Method in class Redis
    -

    Uncompress the provided argument that has been compressed with the -currently configured compressor as set with Redis::setOption().

    -Redis::_prefix() — Method in class Redis
    -

    Prefix the passed argument with the currently set key prefix as set -with Redis::setOption().

    -Redis::_serialize() — Method in class Redis
    -

    Serialize the provided value with the currently set serializer as set -with Redis::setOption().

    -Redis::_unserialize() — Method in class Redis
    -

    Unserialize the passed argument with the currently set serializer as set -with Redis::setOption().

    -Redis::_pack() — Method in class Redis
    -

    Pack the provided value with the configured serializer and compressor -as set with Redis::setOption().

    -Redis::_unpack() — Method in class Redis
    -

    Unpack the provided value with the configured compressor and serializer -as set with Redis::setOption().

    +
    RedisArray::__call() — Method in class RedisArray
    RedisArray::__construct() — Method in class RedisArray
    diff --git a/docs/doctum-search.json b/docs/doctum-search.json index 7929cf10cf..09b2277810 100644 --- a/docs/doctum-search.json +++ b/docs/doctum-search.json @@ -1 +1 @@ -{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

    Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

    "},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":null},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

    Compress a value with the currently configured compressor as set with\nRedis::setOption().

    "},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

    Uncompress the provided argument that has been compressed with the\ncurrently configured compressor as set with Redis::setOption().

    "},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

    Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

    Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

    Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

    Pack the provided value with the configured serializer and compressor\nas set with Redis::setOption().

    "},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

    Unpack the provided value with the configured compressor and serializer\nas set with Redis::setOption().

    "},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":null},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

    Append data to a Redis STRING key.

    "},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

    Authenticate a Redis connection after its been established.

    "},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

    Execute a save of the Redis database in the background.

    "},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

    Asynchronously rewrite Redis' append-only file

    "},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

    Count the number of set bits in a Redis string.

    "},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

    Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

    "},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

    Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

    "},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

    Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

    "},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

    POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

    "},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

    POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

    "},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

    POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

    "},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

    POP one or more of the highest or lowest scoring elements from one or more sorted sets.

    "},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

    Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

    "},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

    Pop one or more elements off of one or more Redis LISTs.

    "},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

    Reset any last error on the connection to NULL

    "},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":null},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":null},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":null},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

    Execute the Redis CONFIG command in a variety of ways.

    "},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":null},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

    Make a copy of a key.

    "},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

    Return the number of keys in the currently selected Redis database.

    "},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":null},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

    Decrement a Redis integer by 1 or a provided value.

    "},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

    Decrement a redis integer by a value

    "},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

    Delete one or more keys from Redis.

    "},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

    Discard a transaction currently in progress.

    "},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

    Dump Redis' internal binary representation of a key.

    "},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

    Have Redis repeat back an arbitrary string to the client.

    "},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

    Execute a LUA script on the redis server.

    "},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

    This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

    "},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

    Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

    "},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

    This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

    "},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

    Execute either a MULTI or PIPELINE block and return the array of replies.

    "},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

    Test if one or more keys exist.

    "},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

    Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

    "},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

    Set a key to expire at an exact unix timestamp.

    "},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

    Get the expiration of a given key as a unix timestamp

    "},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

    Get the expriation timestamp of a given Redis key but in milliseconds.

    "},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

    Deletes every key in all Redis databases

    "},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

    Deletes all the keys of the currently selected database.

    "},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":null},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":null},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":null},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":null},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":null},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":null},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":null},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":null},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":null},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":null},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":null},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

    Get the authentication information on the connection, if any.

    "},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":null},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":null},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":"

    Get the database number PhpRedis thinks we're connected to.

    "},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":null},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

    Return the host or Unix socket we are connected to.

    "},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

    Get the last error returned to us from Redis, if any.

    "},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

    Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

    "},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

    Retrieve the value of a configuration setting as set by Redis::setOption()

    "},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

    Get the persistent connection ID, if there is one.

    "},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

    Get the port we are connected to. This number will be zero if we are connected to a unix socket.

    "},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

    Retrieve a substring of a string by index.

    "},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

    Get the longest common subsequence between two string keys.

    "},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

    Get the currently set read timeout on the connection.

    "},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

    Sets a key and returns any previously set value, if the key already existed.

    "},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

    Retrieve any set connection timeout

    "},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":null},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

    Remove one or more fields from a hash.

    "},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

    Checks whether a field exists in a hash.

    "},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

    Read every field and value from a hash.

    "},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

    Increment a hash field's value by an integer

    "},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

    Increment a hash field by a floating point value

    "},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

    Retrieve all of the fields of a hash.

    "},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

    Get the number of fields in a hash.

    "},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

    Get one or more fields from a hash.

    "},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

    Add or update one or more hash fields and values

    "},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

    Get one or more random field from a hash.

    "},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":null},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

    Set a hash field and value, but only if that field does not exist

    "},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

    Get the string length of a hash field

    "},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

    Get all of the values from a hash.

    "},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

    Iterate over the fields and values of a hash in an incremental fashion.

    "},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

    Increment a key's value, optionally by a specifc amount.

    "},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

    Increment a key by a specific integer value

    "},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

    Increment a numeric key by a floating point value.

    "},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

    Check if we are currently connected to a Redis instance.

    "},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":null},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":null},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":null},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":null},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":""},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":""},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":""},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":""},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":null},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":null},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":null},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":null},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":""},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":null},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":""},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":null},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":null},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":null},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":null},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":null},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":null},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":null},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":null},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

    Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

    "},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

    Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

    "},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

    Add one or more elements to a Redis HyperLogLog key

    "},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

    Retrieve the cardinality of a Redis HyperLogLog key.

    "},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

    Merge one or more source HyperLogLog sets into a destination set.

    "},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

    PING the redis server with an optional string argument.

    "},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

    Enter into pipeline mode.

    "},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":""},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

    Subscribe to one or more glob-style patterns

    "},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

    Get a keys time to live in milliseconds.

    "},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

    Publish a message to a pubsub channel

    "},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":null},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

    Unsubscribe from one or more channels by pattern

    "},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

    Pop one or more elements from the end of a list.

    "},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

    Return a random key from the current database

    "},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

    Execute any arbitrary Redis command by name.

    "},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

    Unconditionally rename a key from $old_name to $new_name

    "},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

    Renames $key_src to $key_dst but only if newkey does not exist.

    "},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

    Reset the state of the connection.

    "},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

    Restore a key by the binary payload generated by the DUMP command.

    "},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

    Query whether the connected instance is a primary or replica

    "},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

    Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

    "},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

    Add one or more values to a Redis SET key.

    "},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

    Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

    "},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

    Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

    "},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

    This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

    "},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

    Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

    "},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

    Compute the intersection of one or more sets and return the cardinality of the result.

    "},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

    Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

    "},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

    Retrieve every member from a set key.

    "},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

    Check if one or more values are members of a set.

    "},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

    Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

    "},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

    Remove one or more elements from a set.

    "},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

    Retrieve one or more random members of a set.

    "},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

    Returns the union of one or more Redis SET keys.

    "},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

    Perform a union of one or more Redis SET keys and store the result in a new set

    "},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

    Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

    "},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

    Incrementally scan the Redis keyspace, with optional pattern and type matching.

    "},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

    Retrieve the number of members in a Redis set.

    "},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

    An administrative command used to interact with LUA scripts stored on the server.

    "},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

    Select a specific Redis database.

    "},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

    Create or set a Redis STRING key to a value.

    "},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

    Set a specific bit in a Redis string to zero or one

    "},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

    Update or append to a Redis string at a specific starting index

    "},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

    Set a configurable option on the Redis object.

    "},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

    Set a Redis STRING key with a specific expiration in seconds.

    "},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

    Set a key to a value, but only if that key does not already exist.

    "},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

    Check whether a given value is the member of a Redis SET.

    "},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

    Turn a redis instance into a replica of another or promote a replica\nto a primary.

    "},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

    Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

    "},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

    Update one or more keys last modified metadata.

    "},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

    Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

    "},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

    Sort the contents of a Redis key in various ways.

    "},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

    This is simply a read-only variant of the sort command

    "},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

    Remove one or more values from a Redis SET key.

    "},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

    Scan the members of a redis SET key.

    "},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

    Retrieve the length of a Redis STRING key.

    "},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

    Subscribe to one or more Redis pubsub channels.

    "},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

    Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

    "},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

    Retrieve the server time from the connected Redis instance.

    "},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

    Get the amount of time a Redis key has before it will expire, in seconds.

    "},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

    Get the type of a given Redis key.

    "},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

    Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

    "},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

    Unsubscribe from one or more subscribed channels.

    "},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

    Remove any previously WATCH'ed keys in a transaction.

    "},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":""},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

    Block the client up to the provided timeout until a certain number of replicas have confirmed\nrecieving them.

    "},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":null},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

    Append a message to a stream.

    "},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":null},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":null},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

    Remove one or more specific IDs from a stream.

    "},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

    Retrieve information about a stream key.

    "},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

    Get the number of messages in a Redis STREAM key.

    "},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

    Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

    "},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

    Get a range of entries from a STREAM key.

    "},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

    Consume one or more unconsumed elements in one or more streams.

    "},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

    Read one or more messages using a consumer group.

    "},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

    Get a range of entries from a STREAM ke in reverse cronological order.

    "},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

    Truncate a STREAM key in various ways.

    "},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

    Add one or more elements and scores to a Redis sorted set.

    "},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

    Return the number of elements in a sorted set.

    "},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

    Count the number of members in a sorted set with scores inside a provided range.

    "},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

    Create or increment the score of a member in a Redis sorted set

    "},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

    Count the number of elements in a sorted set whos members fall within the provided\nlexographical range.

    "},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

    Retrieve the score of one or more members in a sorted set.

    "},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

    Pop one or more of the highest scoring elements from a sorted set.

    "},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

    Pop one or more of the lowest scoring elements from a sorted set.

    "},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

    Retrieve a range of elements of a sorted set between a start and end point.

    "},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

    Retrieve a range of elements from a sorted set by legographical range.

    "},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

    Retrieve a range of members from a sorted set by their score.

    "},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

    This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

    "},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

    Retrieve one or more random members from a Redis sorted set.

    "},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

    Get the rank of a member of a sorted set, by score.

    "},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

    Remove one or more members from a Redis sorted set.

    "},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

    Remove zero or more elements from a Redis sorted set by legographical range.

    "},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

    Remove one or more members of a sorted set by their rank.

    "},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

    Remove one or more members of a sorted set by their score.

    "},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

    List the members of a Redis sorted set in reverse order

    "},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

    List members of a Redis sorted set within a legographical range, in reverse order.

    "},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

    List elements from a Redis sorted set by score, highest to lowest

    "},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

    Retrieve a member of a sorted set by reverse rank.

    "},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

    Get the score of a member of a sorted set.

    "},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

    Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

    "},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

    Store the difference of one or more sorted sets in a destination sorted set.

    "},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

    Compute the intersection of one or more sorted sets and return the members

    "},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

    Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

    "},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

    Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

    "},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

    Scan the members of a sorted set incrementally, using a cursor

    "},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

    Retrieve the union of one or more sorted sets

    "},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

    Perform a union on one or more Redis sets and store the result in a destination sorted set.

    "},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

    See Redis::blpop()

    "},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

    See Redis::brpop()

    "},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

    See Redis::brpoplpush()

    "},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

    PING an instance in the redis cluster.

    "},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} +{"items":[{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

    See Redis::blpop()

    "},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

    See Redis::brpop()

    "},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

    See Redis::brpoplpush()

    "},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

    PING an instance in the redis cluster.

    "},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} diff --git a/docs/doctum.js b/docs/doctum.js index d487386524..5d1acead9c 100644 --- a/docs/doctum.js +++ b/docs/doctum.js @@ -1,5 +1,5 @@ var Doctum = { - treeJson: {"tree":{"l":0,"n":"","p":"","c":[{"l":1,"n":"[Global Namespace]","p":"[Global_Namespace]","c":[{"l":2,"n":"Redis","p":"Redis"},{"l":2,"n":"RedisArray","p":"RedisArray"},{"l":2,"n":"RedisCluster","p":"RedisCluster"},{"l":2,"n":"RedisClusterException","p":"RedisClusterException"},{"l":2,"n":"RedisException","p":"RedisException"},{"l":2,"n":"RedisSentinel","p":"RedisSentinel"}]}]},"treeOpenLevel":2}, + treeJson: {"tree":{"l":0,"n":"","p":"","c":[{"l":1,"n":"[Global Namespace]","p":"[Global_Namespace]","c":[{"l":2,"n":"RedisArray","p":"RedisArray"},{"l":2,"n":"RedisCluster","p":"RedisCluster"},{"l":2,"n":"RedisClusterException","p":"RedisClusterException"},{"l":2,"n":"RedisSentinel","p":"RedisSentinel"}]}]},"treeOpenLevel":2}, /** @var boolean */ treeLoaded: false, /** @var boolean */ diff --git a/docs/index.html b/docs/index.html index f048b2e6dc..8b17f7c10f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -82,11 +82,6 @@

    Classes

    -
    - Redis
    -
    -
    -
    @@ -100,11 +95,6 @@

    Classes

    -
    -
    diff --git a/docs/interfaces.html b/docs/interfaces.html index 804c07efa3..5497f56840 100644 --- a/docs/interfaces.html +++ b/docs/interfaces.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> diff --git a/docs/namespaces.html b/docs/namespaces.html index 8d77ec5a19..8863bd8627 100644 --- a/docs/namespaces.html +++ b/docs/namespaces.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> diff --git a/docs/opensearch.xml b/docs/opensearch.xml index d51e04039a..73c0fe9792 100644 --- a/docs/opensearch.xml +++ b/docs/opensearch.xml @@ -1,7 +1,7 @@ - PhpRedis API (develop) - Searches PhpRedis API (develop) + PhpRedis API (main) + Searches PhpRedis API (main) PhpRedis API UTF-8 diff --git a/docs/renderer.index b/docs/renderer.index index 427fc8536e..f3a75c7ba9 100644 --- a/docs/renderer.index +++ b/docs/renderer.index @@ -1 +1 @@ -O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"0ca9052a6b2da623f76b015fa3271f4a9d1ffcf9";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:14:"RedisException";s:40:"0ca9052a6b2da623f76b015fa3271f4a9d1ffcf9";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file +O:21:"Doctum\Renderer\Index":3:{i:0;a:4:{s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:4:"main";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file diff --git a/docs/search.html b/docs/search.html index 9d7121ad76..c6ab501268 100644 --- a/docs/search.html +++ b/docs/search.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> diff --git a/docs/traits.html b/docs/traits.html index d62c6ea278..4a0de58f5a 100644 --- a/docs/traits.html +++ b/docs/traits.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -80,7 +80,7 @@

    Traits

    -
    +
    diff --git a/redis.stub.php b/redis.stub.php index 3444966ce3..ae96e5f7a7 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -974,14 +974,45 @@ public function get(string $key): mixed; /** * Get the authentication information on the connection, if any. * - * @see Redis::auth() - * * @return mixed The authentication information used to authenticate the connection. + * + * @see Redis::auth() */ public function getAuth(): mixed; + /** + * Get the bit at a given index in a string key. + * + * @param string $key The key to query. + * @param int $idx The Nth bit that we want to query. + * + * @example $redis->getbit('bitmap', 1337); + * + * @see https://redis.io/commands/getbit + */ public function getBit(string $key, int $idx): Redis|int|false; + /** + * Get the value of a key and optionally set it's expiration. + * + * @param string $key The key to query + * @param array $options Options to modify how the command works. + * + * $options = [ + * 'EX' => // Expire in N seconds + * 'PX' => // Expire in N milliseconds + * 'EXAT' => // Expire at a unix timestamp (in seconds) + * 'PXAT' => // Expire at a unix timestamp (in milliseconds); + * 'PERSIST' // Remove any configured expiration on the key. + * ]; + * + * + * @return Redis|string|bool The key's value or false if it didn't exist. + * + * @see https://redis.io/comands/getex + * + * @example $redis->getEx('mykey', ['EX' => 60]); + */ public function getEx(string $key, array $options = []): Redis|string|bool; /** @@ -996,6 +1027,16 @@ public function getEx(string $key, array $options = []): Redis|string|bool; */ public function getDBNum(): int; + /** + * Get a key from Redis and delete it in an atomic operation. + * + * @param string $key The key to get/delete. + * @return Redis|string|bool The value of the key or false if it didn't exist. + * + * @see https://redis.io/commands/getdel + * + * @example $redis->getdel('token:123'); + */ public function getDel(string $key): Redis|string|bool; /** @@ -1046,19 +1087,6 @@ public function getPort(): int; /** * Retrieve a substring of a string by index. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $word = 'Supercalifragilisticexpialidocious'; - * $redis->set('silly-word', $word); - * - * // string "super" - * var_dump($redis->getRange('silly-word', 0, 4)); - * - * // string(7) "docious" - * var_dump($redis->getRange('silly-word', -7, -1)); - * - * * @param string $key The string to query. * @param int $start The zero-based starting index. * @param int $end The zero-based ending index. @@ -1066,22 +1094,16 @@ public function getPort(): int; * @return Redis|string|false The substring or false on failure. * * @see https://redis.io/commands/getrange + * + * @example + * $redis->set('silly-word', 'Supercalifragilisticexpialidocious'); + * echo $redis->getRange('silly-word', 0, 4) . "\n"; */ public function getRange(string $key, int $start, int $end): Redis|string|false; /** * Get the longest common subsequence between two string keys. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc'); - * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc'); - * - * // string(37) "acccgcacggcaagtcgttccagcaactggcgctagc" - * var_dump($redis->lcs('seq1', 'seq2')); - * - * * @param string $key1 The first key to check * @param string $key2 The second key to check * @param array $options An optional array of modifiers for the comand. @@ -1104,6 +1126,11 @@ public function getRange(string $key, int $start, int $end): Redis|string|false; * @return Redis|string|array|int|false Various reply types depending on options. * * @see https://redis.io/commands/lcs + * + * @example + * $redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc'); + * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc'); + * echo $redis->lcs('seq1', 'seq2') . "\n"; */ public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false; @@ -1117,24 +1144,16 @@ public function getReadTimeout(): float; /** * Sets a key and returns any previously set value, if the key already existed. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('captain'); - * - * // bool(false) - * var_dump($redis->getset('captain', 'Pike')); - * - * // string(4) "Pike" - * var_dump($redis->getset('captain', 'Kirk')); - * - * * @param string $key The key to set. * @param mixed $value The value to set the key to. * * @return Redis|string|false The old value of the key or false if it didn't exist. * * @see https://redis.io/commands/getset + * + * @example + * $redis->getset('captain', 'Pike'); + * $redis->getset('captain', 'Kirk'); */ public function getset(string $key, mixed $value): Redis|string|false; @@ -1150,17 +1169,6 @@ public function getTransferredBytes(): int|false; /** * Remove one or more fields from a hash. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('people'); - * - * $redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']); - * - * // int(1) - * $redis->hDel('comms', 'Mallory', 'Archibald'); - * - * * @param string $key The hash key in question. * @param string $field The first field to remove * @param string $other_fields One or more additional fields to remove. @@ -1168,23 +1176,14 @@ public function getTransferredBytes(): int|false; * @return Redis|int|false The number of fields actually removed. * * @see https://redis.io/commands/hdel + * + * @example $redis->hDel('communication', 'Alice', 'Bob'); */ public function hDel(string $key, string $field, string ...$other_fields): Redis|int|false; /** * Checks whether a field exists in a hash. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('captains'); - * - * $redis->hmset('captains', ['Kirk' => 'Enterprise', 'Picard' => 'Enterprise-D', 'Sisko' => 'Defiant']); - * - * $redis->hExists('captains', 'Pike'); - * $redis->hExists('captains', 'Picard'); - * - * * @param string $key The hash to query. * @param string $field The field to check * @@ -1192,6 +1191,7 @@ public function hDel(string $key, string $field, string ...$other_fields): Redis * * @see https://redis.io/commands/hexists * + * @example $redis->hExists('communication', 'Alice'); */ public function hExists(string $key, string $field): Redis|bool; @@ -1200,49 +1200,18 @@ public function hGet(string $key, string $member): mixed; /** * Read every field and value from a hash. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('comms'); - * - * $redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']); - * - * // array(3) { - * // ["Alice"]=> - * // string(3) "ecc" - * // ["Bob"]=> - * // string(3) "rsa" - * // ["Mallory"]=> - * // string(7) "haxx00r" - * // } - * $redis->hGetAll('comms'); - * - * * @param string $key The hash to query. * @return Redis|array|false All fields and values or false if the key didn't exist. * * @see https://redis.io/commands/hgetall * + * @example $redis->hgetall('myhash'); */ public function hGetAll(string $key): Redis|array|false; /** * Increment a hash field's value by an integer * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('player'); - * - * $redis->hmset('player', ['name' => 'Bob', 'level' => 1]); - * - * // int(2) - * $redis->hIncrBy('player', 'level', 1); - * - * // int(5) - * $redis->hIncrBy('player', 'level', 3); - * - * * @param string $key The hash to modify * @param string $field The field to increment * @param int $value How much to increment the value. @@ -1251,24 +1220,16 @@ public function hGetAll(string $key): Redis|array|false; * * @see https://redis.io/commands/hincrby * + * @example + * $redis->hMSet('player:1', ['name' => 'Alice', 'score' => 0]); + * $redis->hincrby('player:1', 'score', 10); + * */ public function hIncrBy(string $key, string $field, int $value): Redis|int|false; /** * Increment a hash field by a floating point value * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('trig-numbers') - * - * // float(3.1415926) - * $pi = $redis->hIncrByFloat('trig-numbers', 'pi', 3.1415926); - * - * // float(6.2831852) - * $redis->hIncrByFloat('trig-numbers', 'tau', 2 * $pi); - * - * * @param string $key The hash with the field to increment. * @param string $field The field to increment. * @@ -1276,35 +1237,21 @@ public function hIncrBy(string $key, string $field, int $value): Redis|int|false * * @see https://redis.io/commands/hincrbyfloat * + * @example + * $redis->hincrbyfloat('numbers', 'tau', 2 * 3.1415926); */ public function hIncrByFloat(string $key, string $field, float $value): Redis|float|false; /** * Retrieve all of the fields of a hash. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('ships'); - * - * $redis->hmset('ships', ['Enterprise' => 'NCC-1701D', 'Defiant' => 'NX-74205', 'Voyager' => 'NCC-74656']); - * - * // array(3) { - * // [0]=> - * // string(10) "Enterprise" - * // [1]=> - * // string(7) "Defiant" - * // [2]=> - * // string(7) "Voyager" - * // } - * $redis->hKeys('ships'); - * - * * @param string $key The hash to query. * * @return Redis|array|false The fields in the hash or false if the hash doesn't exist. * * @see https://redis.io/commands/hkeys + * + * @example $redis->hkeys('myhash'); */ public function hKeys(string $key): Redis|array|false; @@ -1316,28 +1263,14 @@ public function hKeys(string $key): Redis|array|false; * @param string $key The hash to check. * * @return Redis|int|false The number of fields or false if the key didn't exist. + * + * @example $redis->hlen('myhash'); */ public function hLen(string $key): Redis|int|false; /** * Get one or more fields from a hash. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('player:1'); - * - * $redis->hmset('player:1', ['name' => 'Alice', 'age' => '26', 'score' => '1337']); - * - * // array(2) { - * // ["name"]=> - * // string(5) "Alice" - * // ["score"]=> - * // string(4) "1337" - * // } - * $redis->hmget('player:1', ['name', 'score']); - * - * * @param string $key The hash to query. * @param array $fields One or more fields to query in the hash. * @@ -1345,6 +1278,7 @@ public function hLen(string $key): Redis|int|false; * * @see https://redis.io/commands/hmget * + * @example $redis->hMGet('player:1', ['name', 'score']); */ public function hMget(string $key, array $fields): Redis|array|false; @@ -1370,17 +1304,6 @@ public function hMset(string $key, array $fieldvals): Redis|bool; /** * Get one or more random field from a hash. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('settings'); - * - * $redis->hmset('settings', ['path' => '/', 'state' => 'active', 'jobs' => 15]); - * - * $redis->hrandfield('settings'); - * $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]); - * - * * @param string $key The hash to query. * @param array $options An array of options to modify how the command behaves. * @@ -1395,6 +1318,8 @@ public function hMset(string $key, array $fieldvals): Redis|bool; * * @see https://redis.io/commands/hrandfield * + * @example $redis->hrandfield('settings'); + * @example $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]); */ public function hRandField(string $key, array $options = null): Redis|string|array; @@ -1403,26 +1328,16 @@ public function hSet(string $key, string $member, mixed $value): Redis|int|false /** * Set a hash field and value, but only if that field does not exist * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('player:1'); - * - * $redis->hmset('player:1', ['name' => 'bob', 'score' => 0]); - * - * // bool(true) - * var_dump($redis->hsetnx('player:1', 'lock', 'enabled')); - * - * // bool(false) - * var_dump($redis->hsetnx('player:1', 'lock', 'enabled')); - * - * * @param string $key The hash to update. * @param string $field The value to set. * * @return Redis|bool True if the field was set and false if not. * * @see https://redis.io/commands/hsetnx + * + * @example + * $redis->hsetnx('player:1', 'lock', 'enabled'); + * $redis->hsetnx('player:1', 'lock', 'enabled'); */ public function hSetNx(string $key, string $field, string $value): Redis|bool; @@ -1447,29 +1362,13 @@ public function hStrLen(string $key, string $field): Redis|int|false; /** * Get all of the values from a hash. * - * @see https://redis.io/commands/hvals - * * @param string $key The hash to query. * * @return Redis|array|false The values from the hash. * - * - * 'localhost']); - * - * $redis->del('player'); - * - * $redis->hmset('player', ['name' => 'Alice', 'score' => 1337]); + * @see https://redis.io/commands/hvals * - * // array(2) { - * // ["name"]=> - * // string(5) "Alice" - * // ["score"]=> - * // string(4) "1337" - * // } - * $redis->hgetall('player'); - * ?> - * + * @example $redis->hvals('player:1'); */ public function hVals(string $key): Redis|array|false; @@ -1489,8 +1388,7 @@ public function hVals(string $key): Redis|array|false; * * @return Redis|array|bool An array with a subset of fields and values. * - * - * 'localhost']); * * $redis->del('big-hash'); @@ -1511,20 +1409,6 @@ public function hVals(string $key): Redis|array|false; * echo "[$field] => $value\n"; * } * } while ($it != 0); - * - * // --- OUTPUT --- - * // [field:143] => value:143 - * // [field:133] => value:133 - * // [field:163] => value:163 - * // [field:183] => value:183 - * // [field:153] => value:153 - * // [field:113] => value:113 - * // [field:103] => value:103 - * // [field:193] => value:193 - * // [field:123] => value:123 - * // [field:173] => value:173 - * ?> - * */ public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool; @@ -1539,19 +1423,8 @@ public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int * * @return Redis|int|false The new value of the key after incremented. * - * - * 'localhost']); - * - * $redis->set('counter', 1); - * - * // int(2); - * $redis->incr('counter'); - * - * // int(4); - * $redis->incr('counter', 2); - * ?> - * + * @example $redis->incr('mycounter'); + * @example $redis->incr('mycounter', 10); */ public function incr(string $key, int $by = 1): Redis|int|false; @@ -1563,26 +1436,12 @@ public function incr(string $key, int $by = 1): Redis|int|false; * @param string $key The key to increment. * @param int $value The amount to increment. * - * - * 'localhost']); - * + * @example * $redis->set('primes', 2); - * - * // int(3) * $redis->incrby('primes', 1); - * - * // int(5) * $redis->incrby('primes', 2); - * - * // int(7) * $redis->incrby('primes', 2); - * - * // int(11) * $redis->incrby('primes', 4); - * ?> - * */ public function incrBy(string $key, int $value): Redis|int|false; @@ -1594,19 +1453,9 @@ public function incrBy(string $key, int $value): Redis|int|false; * * @return Redis|float|false The new value of the key or false if the key didn't contain a string. * - * - * 'localhost']); - * - * $redis->del('tau'); - * - * // float(3.1415926) - * var_dump($redis->incrByFloat('tau', 3.1415926)); - * - * // float(6.2831852) - * var_dump($redis->incrByFloat('tau', 3.1415926)); - * ?> - * + * @example + * $redis->incrbyfloat('tau', 3.1415926); + * $redis->incrbyfloat('tau', 3.1415926); */ public function incrByFloat(string $key, float $value): Redis|float|false; @@ -1642,60 +1491,261 @@ public function keys(string $pattern); */ public function lInsert(string $key, string $pos, mixed $pivot, mixed $value); + /** + * Retrieve the lenght of a list. + * + * @param string $key The list + * + * @return Redis|int|false The number of elements in the list or false on failure. + */ public function lLen(string $key): Redis|int|false; + /** + * Move an element from one list into another. + * + * @param string $src The source list. + * @param string $dst The destination list + * @param string $wherefrom Where in the source list to retrieve the element. This can be either + * `Redis::LEFT`, or `Redis::RIGHT`. + * @param string $whereto Where in the destination list to put the element. This can be either + * `Redis::LEFT`, or `Redis::RIGHT`. + * @return Redis|string|false The element removed from the source list. + * + * @example + * $redis->rPush('numbers', 'one', 'two', 'three'); + * $redis->lMove('numbers', 'odds', Redis::LEFT, Redis::LEFT); + */ public function lMove(string $src, string $dst, string $wherefrom, string $whereto): Redis|string|false; + /** + * Pop one or more elements off a list. + * + * @param string $key The list to pop from. + * @param int $count Optional number of elements to remove. By default one element is popped. + * @return Redis|null|bool|int|array Will return the element(s) popped from the list or false/NULL + * if none was removed. + * + * @see https://redis.io/commands/lpop + * + * @example $redis->lpop('mylist'); + * @example $redis->lpop('mylist', 4); + */ public function lPop(string $key, int $count = 0): Redis|bool|string|array; + /** + * Retrieve the index of an element in a list. + * + * @param string $key The list to query. + * @param mixed $value The value to search for. + * @param array $options Options to configure how the command operates + * + * $options = [ + * // How many matches to return. By default a single match is returned. + * // If count is set to zero, it means unlimited. + * 'COUNT' => + * + * // Specify which match you want returned. `RANK` 1 means "the first match" + * // 2 meaans the second, and so on. If passed as a negative number the + * // RANK is computed right to left, so a `RANK` of -1 means "the last match". + * 'RANK' => + * + * // This argument allows you to limit how many elements Redis will search before + * // returning. This is useful to prevent Redis searching very long lists while + * // blocking the client. + * 'MAXLEN => + * ]; + * + * + * @return Redis|null|bool|int|array Returns one or more of the matching indexes, or null/false if none were found. + */ public function lPos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array; /** - * @param mixed $elements - * @return int|Redis + * Prepend one or more elements to a list. + * + * @param string $key The list to prepend. + * @param mixed $elements One or more elements to prepend. + * + * @return Redis|int The new length of the list after prepending. + * + * @see https://redis.io/commands/lpush + * + * @example $redis->lPush('mylist', 'cat', 'bear', 'aligator'); */ - public function lPush(string $key, ...$elements); + public function lPush(string $key, $mixed ...$elements): Redis|int|false; /** - * @param mixed $elements - * @return Redis|int|false + * Append one or more elements to a list. + * + * @param string $key The list to append to. + * @param mixed $elements one or more elements to append. + * + * @return Redis|int|false The new length of the list + * + * @see https://redis.io/commands/rpush + * + * @example $redis->rPush('mylist', 'xray', 'yankee', 'zebra'); */ - public function rPush(string $key, ...$elements); + public function rPush(string $key, mixed ...$elements): Redis|int|false; - /** @return Redis|int|false*/ - public function lPushx(string $key, mixed $value); + /** + * Prepend an element to a list but only if the list exists + * + * @param string $key The key to prepend to. + * @param mixed $value The value to prepend. + * + * @return Redis|int|false The new length of the list. + * + */ + public function lPushx(string $key, mixed $value): Redis|int|false; - /** @return Redis|int|false*/ - public function rPushx(string $key, mixed $value); + /** + * Append an element to a list but only if the list exists + * + * @param string $key The key to prepend to. + * @param mixed $value The value to prepend. + * + * @return Redis|int|false The new length of the list. + * + */ + public function rPushx(string $key, mixed $value): Redis|int|false; + /** + * Set a list element at an index to a specific value. + * + * @param string $key The list to modify. + * @param int $index The position of the element to change. + * @param mixed $value The new value. + * + * @return Redis|bool True if the list was modified. + * + * @see https://redis.io/commands/lset + */ public function lSet(string $key, int $index, mixed $value): Redis|bool; + /** + * Retrieve the last time Redis' database was persisted to disk. + * + * @return int The unix timestamp of the last save time + * + * @see https://redis.io/commands/lastsave + */ public function lastSave(): int; + /** + * Get the element of a list by its index. + * + * @param string $key The key to query + * @param int $index The index to check. + * @return mixed The index or NULL/false if the element was not found. + */ public function lindex(string $key, int $index): mixed; + /** + * Retrieve elements from a list. + * + * @param string $key The list to query. + * @param int $start The beginning index to retrieve. This number can be negative + * meaning start from the end of the list. + * @param int $end The end index to retrieve. This can also be negative to start + * from the end of the list. + * + * @return Redis|array|false The range of elements between the indexes. + * + * @example $redis->lrange('mylist', 0, -1); // the whole list + * @example $redis->lrange('mylist', -2, -1); // the last two elements in the list. + */ public function lrange(string $key, int $start , int $end): Redis|array|false; /** - * @return int|Redis|false + * Remove one or more matching elements from a list. + * + * @param string $key The list to truncate. + * @param mixed $value The value to remove. + * @param int $count How many elements matching the value to remove. + * + * @return Redis|int|false The number of elements removed. + * + * @see https://redis.io/commands/lrem */ - public function lrem(string $key, mixed $value, int $count = 0); + public function lrem(string $key, mixed $value, int $count = 0): Redis|int|false; + /** + * Trim a list to a subrange of elements. + * + * @param string $key The list to trim + * @param int $start The starting index to keep + * @param int $end The ending index to keep. + * + * @return Redis|bool true if the list was trimmed. + * + * @example $redis->ltrim('mylist', 0, 3); // Keep the first four elements + *. public function ltrim(string $key, int $start , int $end): Redis|bool; - /** @return array|Redis */ - public function mget(array $keys); + /** + * Get one ore more string keys. + * + * @param array string $keys The keys to retrieve + * @return Redis|array an array of keys with their values. + * + * @example $redis->mget(['key1', 'key2']); + */ + public function mget(array $keys): Redis|array; public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, #[\SensitiveParameter] mixed $credentials = NULL): Redis|bool; - public function move(string $key, int $index): bool; + /** + * Move a key to a different database on the same redis instance. + * + * @param string $key The key to move + * @return Redis|bool True if the key was moved + */ + public function move(string $key, int $index): Redis|bool; + /** + * Set one ore more string keys. + * + * @param array $key_values An array with keys and their values. + * @return Redis|bool True if the keys could be set. + * + * @see https://redis.io/commands/mset + * + * @example $redis->mSet(['foo' => 'bar', 'baz' => 'bop']); + */ public function mset(array $key_values): Redis|bool; + /** + * Set one ore more string keys but only if none of the key exist. + * + * @param array $key_values An array of keys with their values. + * + * @return Redis|bool True if the keys were set and false if not. + * + * @see https://redis.io/commands/msetnx + * + * @example $redis->msetnx(['foo' => 'bar', 'baz' => 'bop']); + */ public function msetnx(array $key_values): Redis|bool; + /** + * Begin a transaction. + * + * @param int $value The type of transaction to start. This can either be `Redis::MULTI` or + * `Redis::PIPELINE'. + * + * @return Redis|bool True if the transaction could be started. + * + * @see https://redis.io/commands/multi + * + * @example + * $redis->multi(); + * $redis->set('foo', 'bar'); + * $redis->get('foo'); + * $redis->exec(); + */ public function multi(int $value = Redis::MULTI): bool|Redis; public function object(string $subcommand, string $key): Redis|int|string|false; @@ -1708,7 +1758,14 @@ public function open(string $host, int $port = 6379, float $timeout = 0, string public function pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; - public function persist(string $key): bool; + /** + * Remove the expiration from a key. + * + * @param string $key The key to operate against. + * + * @return Redis|bool True if a timeout was removed and false if it was not or the key didn't exist. + */ + public function persist(string $key): Redis|bool; /** * Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 @@ -1784,17 +1841,8 @@ public function pfmerge(string $dst, array $srckeys): Redis|bool; * @return Redis|string|false If passed no message, this command will simply return `true`. * If a message is passed, it will return the message. * - * - * 'localhost']); - * - * // bool(true) - * $redis->ping(); - * - * // string(9) "beep boop" - * $redis->ping('beep boop'); - * ?> - * + * @example $redis->ping(); + * @example $redis->ping('beep boop'); */ public function ping(string $message = NULL): Redis|string|bool; @@ -1809,26 +1857,12 @@ public function ping(string $message = NULL): Redis|string|bool; * * @return Redis The redis object is returned, to facilitate method chaining. * - * - * 'localhost']); - * - * // array(3) { - * // [0]=> - * // bool(true) - * // [1]=> - * // int(0) - * // [2]=> - * // int(3) - * // } + * @example * $redis->pipeline() * ->set('foo', 'bar') * ->del('mylist') * ->rpush('mylist', 'a', 'b', 'c') * ->exec(); - * ?> - * - * */ public function pipeline(): bool|Redis; @@ -1838,8 +1872,18 @@ public function pipeline(): bool|Redis; */ public function popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; - /** @return bool|Redis */ - public function psetex(string $key, int $expire, mixed $value); + /** + * Set a key with an expiration time in milliseconds + * + * @param string $key The key to set + * @param int $expire The TTL to set, in milliseconds. + * @param mixed $value The value to set the key to. + * + * @return Redis|bool True if the key could be set. + * + * @example $redis->psetex('mykey', 1000, 'myval'); + */ + public function psetex(string $key, int $expire, mixed $value)| Redis|bool; /** * Subscribe to one or more glob-style patterns @@ -1868,17 +1912,7 @@ public function psubscribe(array $patterns, callable $cb): bool; * * NOTE: -1 means a key has no TTL and -2 means the key doesn't exist. * - * - * 'localhost']); - * - * $redis->setex('ttl-key', 60, 'ttl-value'); - * - * // int(60000) - * var_dump($redis->pttl('ttl-key')); - * ?> - * + * @example $redis->pttl('ttl-key'); */ public function pttl(string $key): Redis|int|false; From c14a9e3a019a89c270661129e51fc8472263638c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 15 Nov 2022 17:40:46 -0800 Subject: [PATCH 1714/1986] Documentation: Normalize examples. --- redis.stub.php | 1796 ++++++---------------------------------- redis_arginfo.h | 29 +- redis_legacy_arginfo.h | 2 +- 3 files changed, 286 insertions(+), 1541 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index ae96e5f7a7..f5af3a6d58 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -150,17 +150,16 @@ public function acl(string $subcmd, string ...$args): mixed; /** * Append data to a Redis STRING key. * - * - * $redis = new Redis(['host' => 'localhost']); - * $redis->set('foo', 'hello); - * var_dump($redis->append('foo', 'world')); - * - * * @param string $key The key in question * @param mixed $value The data to append to the key. * * @return Redis|int|false The new string length of the key or false on failure. * + * @see https://redis.io/commands/append + * + * @example + * $redis->set('foo', 'hello); + * $redis->append('foo', 'world'); */ public function append(string $key, mixed $value): Redis|int|false; @@ -543,13 +542,6 @@ public function delete(array|string $key, string ...$other_keys): Redis|int|fals /** * Discard a transaction currently in progress. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->multi()->set('foo', 'bar')->get('foo'); - * - * - * * @return Redis|bool True if we could discard the transaction. * * @example @@ -1285,12 +1277,6 @@ public function hMget(string $key, array $fields): Redis|array|false; /** * Add or update one or more hash fields and values * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]); - * - * * @param string $key The hash to create/update * @param array $fieldvals An associative array with fields and their values. * @@ -1298,6 +1284,7 @@ public function hMget(string $key, array $fields): Redis|array|false; * * @see https://redis.io/commands/hmset * + * @example $redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]); */ public function hMset(string $key, array $fieldvals): Redis|bool; @@ -1572,7 +1559,7 @@ public function lPos(string $key, mixed $value, array $options = null): Redis|nu * * @example $redis->lPush('mylist', 'cat', 'bear', 'aligator'); */ - public function lPush(string $key, $mixed ...$elements): Redis|int|false; + public function lPush(string $key, mixed ...$elements): Redis|int|false; /** * Append one or more elements to a list. @@ -1680,13 +1667,13 @@ public function lrem(string $key, mixed $value, int $count = 0): Redis|int|false * @return Redis|bool true if the list was trimmed. * * @example $redis->ltrim('mylist', 0, 3); // Keep the first four elements - *. + */ public function ltrim(string $key, int $start , int $end): Redis|bool; /** * Get one ore more string keys. * - * @param array string $keys The keys to retrieve + * @param array $keys The keys to retrieve * @return Redis|array an array of keys with their values. * * @example $redis->mget(['key1', 'key2']); @@ -1883,13 +1870,11 @@ public function popen(string $host, int $port = 6379, float $timeout = 0, string * * @example $redis->psetex('mykey', 1000, 'myval'); */ - public function psetex(string $key, int $expire, mixed $value)| Redis|bool; + public function psetex(string $key, int $expire, mixed $value): Redis|bool; /** * Subscribe to one or more glob-style patterns * - * @see https://redis.io/commands/psubscribe - * * @param array $patterns One or more patterns to subscribe to. * @param callable $cb A callback with the following prototype: * @@ -1897,6 +1882,8 @@ public function psetex(string $key, int $expire, mixed $value)| Redis|bool; * function ($redis, $channel, $message) { } *
    * + * @see https://redis.io/commands/psubscribe + * * @return bool True if we were subscribed. */ public function psubscribe(array $patterns, callable $cb): bool; @@ -1904,13 +1891,15 @@ public function psubscribe(array $patterns, callable $cb): bool; /** * Get a keys time to live in milliseconds. * - * @see https://redis.io/commands/pttl - * * @param string $key The key to check. * - * @return Redis|int|false The keys TTL or false on failure. + * @return Redis|int|false The key's TTL or one of two special values if it has none. + * + * -1 - The key has no TTL. + * -2 - The key did not exist. + * * - * NOTE: -1 means a key has no TTL and -2 means the key doesn't exist. + * @see https://redis.io/commands/pttl * * @example $redis->pttl('ttl-key'); */ @@ -1946,33 +1935,16 @@ public function punsubscribe(array $patterns): Redis|array|bool; /** * Pop one or more elements from the end of a list. * - * @see https://redis.io/commands/rpop - * * @param string $key A redis LIST key name. * @param int $count The maximum number of elements to pop at once. - * - * NOTE: The `count` argument requires Redis >= 6.2.0 + * NOTE: The `count` argument requires Redis >= 6.2.0 * * @return Redis|array|string|bool One ore more popped elements or false if all were empty. * - * - * 'localhost']); - * - * $redis->del('mylist'); - * $redis->rPush('mylist', 'one', 'two', 'three'); - * - * // string(5) "three" - * $redis->rPop('mylist'); - * - * // string(3) "two" - * $redis->rPop('mylist'); + * @see https://redis.io/commands/rpop * - * // string(3) "one" - * $redis->rPop('mylist'); - * ?> - * + * @example $redis->rPop('mylist'); + * @example $redis->rPop('mylist', 4); */ public function rPop(string $key, int $count = 0): Redis|array|string|bool; @@ -1992,29 +1964,11 @@ public function randomKey(): Redis|string|false; * @param string $command The command to execute * @param mixed $args One or more arguments to pass to the command. * - * - * 'localhost']); + * @return mixed Can return any number of things depending on command executed. * - * $redis->rawCommand('del', 'mystring', 'mylist'); - * $redis->rawCommand('set', 'mystring', 'myvalue'); - * $redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three'); - * - * // string(7) "myvalue" - * $redis->rawCommand('get', 'mystring'); - * - * // array(3) { - * // [0]=> - * // string(3) "one" - * // [1]=> - * // string(3) "two" - * // [2]=> - * // string(5) "three" - * // } - * $redis->rawCommand('lrange', 'mylist', 0, -1); - * ?> - * + * @example $redis->rawCommand('del', 'mystring', 'mylist'); + * @example $redis->rawCommand('set', 'mystring', 'myvalue'); + * @example $redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three'); */ public function rawcommand(string $command, mixed ...$args): mixed; @@ -2040,22 +1994,12 @@ public function rename(string $old_name, string $new_name): Redis|bool; * * @return Redis|bool True if the key was renamed, false if not. * - * - * 'localhost']); - * - * $redis->del('src', 'dst', 'existing-dst'); - * + * @example * $redis->set('src', 'src_key'); * $redis->set('existing-dst', 'i_exist'); * - * // bool(true) * $redis->renamenx('src', 'dst'); - * - * // bool(false) * $redis->renamenx('dst', 'existing-dst'); - * ?> - * */ public function renameNx(string $key_src, string $key_dst): Redis|bool; @@ -2069,10 +2013,6 @@ public function reset(): Redis|bool; /** * Restore a key by the binary payload generated by the DUMP command. * - * @see https://redis.io/commands/restore - * @see https://redis.io/commands/dump - * @see Redis::dump() - * * @param string $key The name of the key you wish to create. * @param int $ttl What Redis should set the key's TTL (in milliseconds) to once it is created. * Zero means no TTL at all. @@ -2096,34 +2036,15 @@ public function reset(): Redis|bool; * * @return Redis|bool True if the key was stored, false if not. * - * - * 'localhost']); - * - * $redis->del('captains'); + * @see https://redis.io/commands/restore + * @see https://redis.io/commands/dump + * @see Redis::dump() * + * @example * $redis->sAdd('captains', 'Janeway', 'Picard', 'Sisko', 'Kirk', 'Archer'); - * * $serialized = $redis->dump('captains'); * - * $redis->select(1); * $redis->restore('captains-backup', 0, $serialized); - * - * //array(5) { - * // [0]=> - * // string(6) "Archer" - * // [1]=> - * // string(4) "Kirk" - * // [2]=> - * // string(5) "Sisko" - * // [3]=> - * // string(6) "Picard" - * // [4]=> - * // string(7) "Janeway" - * //} - * var_dump($redis->sMembers('captains-backup')); - * ?> - * */ public function restore(string $key, int $ttl, string $value, ?array $options = NULL): Redis|bool; @@ -2139,67 +2060,40 @@ public function role(): mixed; * Atomically pop an element off the end of a Redis LIST and push it to the beginning of * another. * - * @see https://redis.io/commands/rpoplpush - * * @param string $srckey The source key to pop from. * @param string $dstkey The destination key to push to. * * @return Redis|string|false The popped element or false if the source key was empty. * - * - * 'localhost']); + * @see https://redis.io/commands/rpoplpush * + * @example * $redis->pipeline() * ->del('list1', 'list2') * ->rpush('list1', 'list1-1', 'list1-2') * ->rpush('list2', 'list2-1', 'list2-2') * ->exec(); * - * var_dump($redis->rpoplpush('list2', 'list1')); - * var_dump($redis->lrange('list1', 0, -1)); - * - * // --- OUTPUT --- - * // string(7) "list2-2" - * // - * // array(3) { - * // [0]=> - * // string(7) "list2-2" - * // [1]=> - * // string(7) "list1-1" - * // [2]=> - * // string(7) "list1-2" - * // } - * ?> - * + * $redis->rpoplpush('list2', 'list1'); */ public function rpoplpush(string $srckey, string $dstkey): Redis|string|false; /** * Add one or more values to a Redis SET key. * - * @see https://redis.io/commands/sadd - * @param string $key The key name * @param mixed $member A value to add to the set. * @param mixed $other_members One or more additional values to add * * @return Redis|int|false The number of values added to the set. * - * - * 'localhost']); + * @see https://redis.io/commands/sadd * + * @example * $redis->del('myset'); * - * var_dump($redis->sadd('myset', 'foo', 'bar', 'baz')); - * var_dump($redis->sadd('myset', 'foo', 'new')); - * - * // --- OUTPUT --- - * // int(3) - * // int(1) - * ?> - * + * $redis->sadd('myset', 'foo', 'bar', 'baz'); + * $redis->sadd('myset', 'foo', 'new'); */ public function sAdd(string $key, mixed $value, mixed ...$other_values): Redis|int|false; @@ -2214,20 +2108,11 @@ public function sAdd(string $key, mixed $value, mixed ...$other_values): Redis|i * @param array $values One or more members to add to the set. * @return Redis|int|false The number of members added to the set. * - * - * 'localhost']); - * + * @example * $redis->del('myset'); * - * var_dump($redis->sAddArray('myset', ['foo', 'bar', 'baz'])); - * var_dump($redis->sAddArray('myset', ['foo', 'new'])); - * - * // --- OUTPUT --- - * // int(3) - * // int(1) - * ?> - * + * $redis->sAddArray('myset', ['foo', 'bar', 'baz']); + * $redis->sAddArray('myset', ['foo', 'new']); */ public function sAddArray(string $key, array $values): int; @@ -2235,18 +2120,15 @@ public function sAddArray(string $key, array $values): int; * Given one or more Redis SETS, this command returns all of the members from the first * set that are not in any subsequent set. * - * @see https://redis.io/commands/sdiff - * * @param string $key The first set * @param string $other_keys One or more additional sets * * @return Redis|array|false Returns the elements from keys 2..N that don't exist in the * first sorted set, or false on failure. * - * - * 'localhost']); + * @see https://redis.io/commands/sdiff * + * @example * $redis->pipeline() * ->del('set1', 'set2', 'set3') * ->sadd('set1', 'apple', 'banana', 'carrot', 'date') @@ -2254,17 +2136,7 @@ public function sAddArray(string $key, array $values): int; * ->sadd('set3', 'apple', 'carrot', 'eggplant') * ->exec(); * - * // NOTE: 'banana' and 'date' are in set1 but none of the subsequent sets. - * var_dump($redis->sdiff('set1', 'set2', 'set3')); - * - * // --- OUTPUT --- - * array(2) { - * [0]=> - * string(6) "banana" - * [1]=> - * string(4) "date" - * } - * ?> + * $redis->sdiff('set1', 'set2', 'set3'); */ public function sDiff(string $key, string ...$other_keys): Redis|array|false; @@ -2292,11 +2164,7 @@ public function sDiffStore(string $dst, string $key, string ...$other_keys): Red * @param string $key The first SET key to intersect. * @param string $other_keys One or more Redis SET keys. * - * - * 'localhost']); - * + * @example * $redis->pipeline() * ->del('alice_likes', 'bob_likes', 'bill_likes') * ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato') @@ -2304,15 +2172,7 @@ public function sDiffStore(string $dst, string $key, string ...$other_keys): Red * ->sadd('bill_likes', 'broccoli', 'potato') * ->exec(); * - * // NOTE: 'potato' is the only value in all three sets * var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes')); - * - * // --- OUTPUT --- - * // array(1) { - * // [0]=> - * // string(6) "potato" - * // } - * ?> * */ public function sInter(array|string $key, string ...$other_keys): Redis|array|false; @@ -2320,26 +2180,20 @@ public function sInter(array|string $key, string ...$other_keys): Redis|array|fa /** * Compute the intersection of one or more sets and return the cardinality of the result. * - * @see https://redis.io/commands/sintercard - * * @param array $keys One or more set key names. * @param int $limit A maximum cardinality to return. This is useful to put an upper bound * on the amount of work Redis will do. * * @return Redis|int|false The * - * - * 'localhost']); - * - * $redis->del('set1', 'set2', 'set3'); + * @see https://redis.io/commands/sintercard * + * @example * $redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot'); * $redis->sAdd('set2', 'apple', 'banana'); * $redis->sAdd('set3', 'pear', 'banana'); * - * // int(1) - * var_dump($redis->sInterCard(['set1', 'set2', 'set3'])); + * $redis->sInterCard(['set1', 'set2', 'set3']); * ?> * */ @@ -2349,9 +2203,6 @@ public function sintercard(array $keys, int $limit = -1): Redis|int|false; * Perform the intersection of one or more Redis SETs, storing the result in a destination * key, rather than returning them. * - * @see https://redis.io/commands/sinterstore - * @see Redis::sinter() - * * @param array|string $key_or_keys Either a string key, or an array of keys (with at least two * elements, consisting of the destination key name and one * or more source keys names. @@ -2360,15 +2211,11 @@ public function sintercard(array $keys, int $limit = -1): Redis|int|false; * * @return Redis|int|false The number of values stored in the destination key or false on failure. * - * - * 'localhost']); - * - * // OPTION 1: A single array - * $redis->sInterStore(['dst', 'src1', 'src2', 'src3']); + * @see https://redis.io/commands/sinterstore + * @see Redis::sinter() * - * // OPTION 2: Variadic - * $redis->sInterStore('dst', 'src1', 'src'2', 'src3'); + * @example $redis->sInterStore(['dst', 'src1', 'src2', 'src3']); + * @example $redis->sInterStore('dst', 'src1', 'src'2', 'src3'); * ?> * */ @@ -2377,32 +2224,15 @@ public function sInterStore(array|string $key, string ...$other_keys): Redis|int /** * Retrieve every member from a set key. * - * @see https://redis.io/commands/smembers - * * @param string $key The set name. * * @return Redis|array|false Every element in the set or false on failure. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('tng-crew'); + * @see https://redis.io/commands/smembers * + * @example * $redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']); - * - * // Array - * // ( - * // [0] => Riker - * // [1] => Crusher - * // [2] => Troi - * // [3] => Worf - * // [4] => LaForge - * // [5] => Picard - * // [6] => Broccoli - * // [7] => Data - * // ) * $redis->sMembers('tng-crew'); - * */ public function sMembers(string $key): Redis|array|false; @@ -2420,29 +2250,9 @@ public function sMembers(string $key): Redis|array|false; * @return Redis|array|false An array of integers representing whether each passed value * was a member of the set. * - * - * 'localhost']); - * - * $redis->del('ds9-crew'); + * @example * $redis->sAdd('ds9-crew', ...["Sisko", "Kira", "Dax", "Worf", "Bashir", "O'Brien"]); - * - * $names = ['Sisko', 'Picard', 'Data', 'Worf']; - * $members = $redis->sMIsMember('ds9-crew', ...$names); - * - * // array(4) { - * // ["Sisko"]=> - * // int(1) - * // ["Picard"]=> - * // int(0) - * // ["Data"]=> - * // int(0) - * // ["Worf"]=> - * // int(1) - * // } - * var_dump(array_combine($names, $members)); - * ?> - * + * $members = $redis->sMIsMember('ds9-crew', ...['Sisko', 'Picard', 'Data', 'Worf']); */ public function sMisMember(string $key, string $member, string ...$other_members): Redis|array|false; @@ -2458,37 +2268,11 @@ public function sMisMember(string $key, string $member, string ...$other_members * * @return Redis|bool True if the member was moved, and false if it wasn't in the set. * - * - * 'localhost']); - * - * $redis->del('numbers', 'evens'); + * @example * $redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four'); - * * $redis->sMove('numbers', 'evens', 'zero'); * $redis->sMove('numbers', 'evens', 'two'); * $redis->sMove('numbers', 'evens', 'four'); - * - * // array(2) { - * // [0]=> - * // string(5) "three" - * // [1]=> - * // string(3) "one" - * // } - * var_dump($redis->sMembers('numbers')); - * - * // array(3) { - * // [0]=> - * // string(4) "zero" - * // [1]=> - * // string(3) "two" - * // [2]=> - * // string(4) "four" - * // } - * var_dump($redis->sMembers('evens')); - * - * ?> - * */ public function sMove(string $src, string $dst, mixed $value): Redis|bool; @@ -2501,40 +2285,10 @@ public function sMove(string $src, string $dst, mixed $value): Redis|bool; * @param int $count An optional number of members to pop. This defaults to * removing one element. * - * - * 'localhost']); - * - * 'localhost']); - * + * @example * $redis->del('numbers', 'evens'); * $redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four'); - * - * $redis->sMove('numbers', 'evens', 'zero'); - * $redis->sMove('numbers', 'evens', 'two'); - * $redis->sMove('numbers', 'evens', 'four'); - * - * // array(2) { - * // [0]=> - * // string(5) "three" - * // [1]=> - * // string(3) "one" - * // } - * var_dump($redis->sMembers('numbers')); - * - * // array(3) { - * // [0]=> - * // string(4) "zero" - * // [1]=> - * // string(3) "two" - * // [2]=> - * // string(4) "four" - * // } - * var_dump($redis->sMembers('evens')); - * ?> - * + * $redis->sPop('numbers'); */ public function sPop(string $key, int $count = 0): Redis|string|array|false; @@ -2553,25 +2307,11 @@ public function sPop(string $key, int $count = 0): Redis|string|array|false; * * @return Redis|array|string|false One or more random members or false on failure. * - * - * 'localhost']); - * - * $redis->del('elder-gods'); - * - * $redis->sAdd('elder-gods', ["Cthulhu", "Azathoth", "Daoloth", "D'endrrah"]); - * - * // A single random member returned. - * $rng1 = $redis->sRandMember('elder-gods'); - * - * // Up to SCARD `elder-gods` random members returned - * $rng2 = $redis->sRandMember('elder-gods', 9999); - * - * // 9999 elements from the set returned with duplicates - * $rng3 = $redis->sRandMember('elder-gods', -9999); - * ?> - * + * @see https://redis.io/commands/srandmember * + * @example $redis->sRandMember('myset'); + * @example $redis->sRandMember('myset', 10); + * @example $redis->sRandMember('myset', -10); */ public function sRandMember(string $key, int $count = 0): Redis|string|array|false; @@ -2585,33 +2325,7 @@ public function sRandMember(string $key, int $count = 0): Redis|string|array|fal * * @return Redis|array|false The union of the one or more input sets or false on failure. * - * - * 'localhost']); - * - * $redis->pipeline() - * ->del('set1', 'set2', 'set3') - * ->sadd('set1', 'apple', 'banana', 'carrot') - * ->sadd('set2', 'apple', 'carrot', 'fish') - * ->sadd('set3', 'carrot', 'fig', 'eggplant'); - * - * var_dump($redis->sunion('set1', 'set2', 'set3')); - * - * // --- OPUTPUT --- - * // array(5) { - * // [0]=> - * // string(6) "banana" - * // [1]=> - * // string(5) "apple" - * // [2]=> - * // string(4) "fish" - * // [3]=> - * // string(6) "carrot" - * // [4]=> - * // string(8) "eggplant" - * // } - * ?> - * + * @example $redis->sunion('set1', 'set2'); */ public function sUnion(string $key, string ...$other_keys): Redis|array|false; @@ -2651,9 +2365,32 @@ public function save(): Redis|bool; * keys match inside a key space with a great many keys. The following example demonstrates how * to use Redis::scan() with the option disabled and enabled. * - * - * 'localhost']); * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); @@ -2678,55 +2415,19 @@ public function save(): Redis|bool; * echo "KEY: $key\n"; * } * } - * ?> - * - * @see https://redis.io/commands/scan - * @see Redis::setOption() - * - * @param int $iterator The cursor returned by Redis for every subsequent call to SCAN. On - * the initial invocation of the call, it should be initialized by the - * caller to NULL. Each time SCAN is invoked, the iterator will be - * updated to a new number, until finally Redis will set the value to - * zero, indicating that the scan is complete. - * - * @param string $pattern An optional glob-style pattern for matching key names. If passed as - * NULL, it is the equivalent of sending '*' (match every key). - * - * @param int $count A hint to redis that tells it how many keys to return in a single - * call to SCAN. The larger the number, the longer Redis may block - * clients while iterating the key space. - * - * @param string $type An optional argument to specify which key types to scan (e.g. - * 'STRING', 'LIST', 'SET') - * - * @return array|false An array of keys, or false if no keys were returned for this - * invocation of scan. Note that it is possible for Redis to return - * zero keys before having scanned the entire key space, so the caller - * should instead continue to SCAN until the iterator reference is - * returned to zero. */ public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, string $type = NULL): array|false; /** * Retrieve the number of members in a Redis set. * - * @see https://redis.io/commands/scard - * * @param string $key The set to get the cardinality of. * * @return Redis|int|false The cardinality of the set or false on failure. * - * - * 'localhost']); - * - * $redis->del('set'); - * $redis->sadd('set', 'one', 'two', 'three', 'four', 'five'); + * @see https://redis.io/commands/scard * - * // Returns 5 - * $redis->scard('set'); - * ?> + * @example $redis->scard('set'); * */ public function scard(string $key): Redis|int|false; @@ -2741,27 +2442,8 @@ public function scard(string $key): Redis|int|false; * * @return mixed This command returns various things depending on the specific operation executed. * - * - * 'localhost']); - * - * $lua = sprintf("return %f", microtime(true)); - * - * // array(1) { - * // [0]=> - * // int(0) - * // } - * var_dump($redis->script('exists', sha1($lua))); - * - * $redis->script('load', $lua); - * - * // array(1) { - * // [0]=> - * // int(1) - * // } - * var_dump($redis->script('exists', sha1($lua))); - * ?> - * + * @example $redis->script('load', 'return 1'); + * @example $redis->script('exists', sha1('return 1')); */ public function script(string $command, mixed ...$args): mixed; @@ -2772,33 +2454,15 @@ public function script(string $command, mixed ...$args): mixed; * * @return Redis|bool true on success and false on failure * - * - * 'localhost']); - * - * $redis->select(1); - * $redis->set('this_is_db_1', 'test'); - * - * $redis->select(0); - * var_dump($redis->exists('this_is_db_1')); - * - * $redis->select(1); - * var_dump($redis->exists('this_is_db_1')); + * @see https://redis.io/commands/select * - * // --- OUTPUT --- - * // int(0) - * // int(1) - * ?> - * + * @example $redis->select(1); */ public function select(int $db): Redis|bool; /** * Create or set a Redis STRING key to a value. * - * @see https://redis.io/commands/set - * @see https://redis.io/commands/setex - * * @param string $key The key name to set. * @param mixed $value The value to set the key to. * @param array|int $options Either an array with options for how to perform the set or an @@ -2819,20 +2483,11 @@ public function select(int $db): Redis|bool; * * @return Redis|string|bool True if the key was set or false on failure. * - * - * 'localhost']); - * - * $redis->set('key', 'value'); - * - * // Will actually send `SETEX 60 key value` to Redis. - * $redis->set('key', 'expires_in_60_seconds', 60); - * - * // Only have Redis set the key if it already exists. - * $redis->set('key', 'options_set', ['XX']); + * @see https://redis.io/commands/set + * @see https://redis.io/commands/setex * - * ?> - * + * @example $redis->set('key', 'value'); + * @example $redis->set('key', 'expires_in_60_seconds', 60); */ public function set(string $key, mixed $value, mixed $options = NULL): Redis|string|bool; @@ -2846,19 +2501,9 @@ public function set(string $key, mixed $value, mixed $options = NULL): Redis|str * * @return Redis|int|false The original value of the bit or false on failure. * - * - * 'localhost']); - * + * @example * $redis->set('foo', 'bar'); - * - * // Flip the 7th bit to 1 * $redis->setbit('foo', 7, 1); - * - * // The bit flip turned 'bar' -> 'car' - * $redis->get('foo'); - * ?> - * */ public function setBit(string $key, int $idx, bool $value): Redis|int|false; @@ -2873,16 +2518,9 @@ public function setBit(string $key, int $idx, bool $value): Redis|int|false; * * @return Redis|int|false The new length of the string or false on failure * - * - * 'localhost']); - + * @example * $redis->set('message', 'Hello World'); - - * // Update 'Hello World' to 'Hello Redis' * $redis->setRange('message', 6, 'Redis'); - * ?> - * */ public function setRange(string $key, int $index, string $value): Redis|int|false; @@ -2927,15 +2565,7 @@ public function setOption(int $option, mixed $value): bool; * * @return Redis|bool True on success or false on failure. * - * - * 'localhost']); - * - * // Set a key with a 60 second expiration - * $redis->set('some_key', 60, 'some_value'); - * - * ?>php - * + * @example $redis->setex('60s-ttl', 60, 'some-value'); */ public function setex(string $key, int $expire, mixed $value); @@ -2949,21 +2579,8 @@ public function setex(string $key, int $expire, mixed $value); * * @return Redis|bool Returns true if the key was set and false otherwise. * - * - * 'localhost']); - * - * $redis->del('new-key'); - * $redis->set('existing-key', 'already-exists'); - * - * // Key is new, returns 1 - * $redis->setnx('key1', 'here-is-a-new-key'); - * - * // Key exists, returns 0 - * $redis->setnx('existing-key', 'new-value'); - * ?> - * - * + * @example $redis->setnx('existing-key', 'existing-value'); + * @example $redis->setnx('new-key', 'new-value'); */ public function setnx(string $key, mixed $value): Redis|bool; @@ -2975,22 +2592,7 @@ public function setnx(string $key, mixed $value): Redis|bool; * * @return Redis|bool True if the member exists and false if not. * - * - * 'localhost']); - * - * $redis->multi() - * ->del('myset') - * ->sadd('myset', 'foo', 'bar', 'baz') - * ->exec(); - * - * // Will return true, as 'foo' is in the set - * $redis->sismember('myset', 'foo'); - * - * // Will return false, as 'not-in-set' is not in the set - * $redis->sismember('myset', 'not-in-set'); - * ?> - * + * @example $redis->sismember('myset', 'mem1', 'mem2'); */ public function sismember(string $key, mixed $value): Redis|bool; @@ -3006,7 +2608,7 @@ public function sismember(string $key, mixed $value): Redis|bool; * * @see https://redis.io/commands/slaveof * @see https://redis.io/commands/replicaof - * @see Redis::slaveof() + * @see Redis::replicaof() */ public function slaveof(string $host = NULL, int $port = 6379): Redis|bool; @@ -3024,18 +2626,15 @@ public function slaveof(string $host = NULL, int $port = 6379): Redis|bool; * @return Redis|bool Success if we were successfully able to start replicating a primary or * were able to promote teh replicat to a primary. * - * - * 'localhost']); * * // Attempt to become a replica of a Redis instance at 127.0.0.1:9999 - * $redis->slaveof('127.0.0.1', 9999); + * $redis->replicaof('127.0.0.1', 9999); * - * // When passed no arguments, PhpRedis will deliver the command `SLAVEOF NO ONE` + * // When passed no arguments, PhpRedis will deliver the command `REPLICAOF NO ONE` * // attempting to promote the instance to a primary. - * $redis->slaveof(); - * ?> - * + * $redis->replicaof(); */ public function replicaof(string $host = NULL, int $port = 6379): Redis|bool; @@ -3057,7 +2656,6 @@ public function touch(array|string $key_or_array, string ...$more_keys): Redis|i * Interact with Redis' slowlog functionality in various ways, depending * on the value of 'operation'. * - * @see https://redis.io/commands/slowlog/ * @category administration * * @param string $operation The operation you wish to perform.  This can @@ -3065,14 +2663,6 @@ public function touch(array|string $key_or_array, string ...$more_keys): Redis|i * 'GET' - Retrieve the Redis slowlog as an array. * 'LEN' - Retrieve the length of the slowlog. * 'RESET' - Remove all slowlog entries. - * - * slowlog('get', -1); // Retrieve all slowlog entries. - * $redis->slowlog('len'); // Retrieve slowlog length. - * $redis->slowlog('reset'); // Reset the slowlog. - * ?> - * - * * @param int $length This optional argument can be passed when operation * is 'get' and will specify how many elements to retrieve. * If omitted Redis will send up to a default number of @@ -3081,6 +2671,12 @@ public function touch(array|string $key_or_array, string ...$more_keys): Redis|i * Note: With Redis >= 7.0.0 you can send -1 to mean "all". * * @return mixed + * + * @see https://redis.io/commands/slowlog/ + * + * @example $redis->slowlog('get', -1); // Retrieve all slowlog entries. + * @example $redis->slowlog('len'); // Retrieve slowlog length. + * @example $redis->slowlog('reset'); // Reset the slowlog. */ public function slowlog(string $operation, int $length = 0): mixed; @@ -3098,8 +2694,7 @@ public function slowlog(string $operation, int $length = 0): mixed; * or the number of elements placed in a destination set when * using the STORE option. * - * - * 'ASC'|| 'DESC' // Sort in descending or descending order. * 'ALPHA' => true || false // Whether to sort alphanumerically. @@ -3111,9 +2706,6 @@ public function slowlog(string $operation, int $length = 0): mixed; * rather than the source keys' element. This can * be used in combination with 'BY' * ]; - * ?> - * - * */ public function sort(string $key, ?array $options = null): mixed; @@ -3155,20 +2747,7 @@ public function sortDescAlpha(string $key, ?string $pattern = null, mixed $get = * * @return Redis|int|false The number of values removed from the set or false on failure. * - * - * 'localhost']); - * - * $redis->pipeline()->del('set1') - * ->sadd('set1', 'foo', 'bar', 'baz') - * ->exec(); - * - * var_dump($redis->sRem('set1', 'foo', 'bar', 'not-in-the-set')); - * - * // --- OUTPUT --- - * // int(2) - * ?> - * + * @example $redis->sRem('set1', 'mem1', 'mem2', 'not-in-set'); */ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|int|false; @@ -3189,9 +2768,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * @param int $count A hint to Redis as to how many members it should scan in one command * before returning members for that iteration. * - * - * $redis = new Redis(['host' => 'localhost']); - * + * @example * $redis->del('myset'); * for ($i = 0; $i < 10000; $i++) { * $redis->sAdd('myset', "member:$i"); @@ -3228,10 +2805,6 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * $scanned++; * } * } - * echo "TOTAL: $scanned\n"; - * ?> - * - * */ public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false; @@ -3243,24 +2816,9 @@ public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int * @return Redis|int|false The length of the string key if it exists, zero if it does not, and * false on failure. * - * - * 'localhost']); - * - * $redis->del('string'); - * - * $redis->set('string', 'foo'); + * @see https://redis.io/commands/strlen * - * // strlen('foo') == 3 - * $redis->strlen('string'); - * - * $redis->append('string', 'bar'); - * - * // strlen('foobar') == 6 - * $redis->strlen('string'); - * - * ?> - * + * @example $redis->strlen('mykey'); */ public function strlen(string $key): Redis|int|false; @@ -3274,8 +2832,9 @@ public function strlen(string $key): Redis|int|false; * @return bool True on success, false on faiilure. Note that this command will block the * client in a subscribe loop, waiting for messages to arrive. * - * - * 'localhost']); * * $redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { @@ -3291,8 +2850,6 @@ public function strlen(string $key): Redis|int|false; * // Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be * // broken and this command will execute. * echo "Subscribe loop ended\n"; - * ?> - * */ public function subscribe(array $channels, callable $cb): bool; @@ -3303,53 +2860,19 @@ public function subscribe(array $channels, callable $cb): bool; * Note: This command simply swaps Redis' internal pointer to the database and is therefore * very fast, regardless of the size of the underlying databases. * - * @see https://redis.io/commands/swapdb - * @see Redis::del() - * * @param int $src The source database number * @param int $dst The destination database number * * @return Redis|bool Success if the databases could be swapped and false on failure. * - * - * 'localhost']); - * - * $redis->multi()->select(0) - * ->set('db0-key1', 'value1')->set('db0-key2', 'value2') - * ->select(1) - * ->set('db1-key1', 'value1')->set('db1-key2', 'value2') - * ->select(0) - * ->exec(); - * - * // Array - * // ( - * // [0] => db0-key1 - * // [1] => db0-key2 - * // ) - * print_r($redis->keys('*')); - * - * // Swap db0 and db1 - * $redis->swapdb(0, 1); - * - * // Array - * // ( - * // [0] => db1-key2 - * // [1] => db1-key1 - * // ) - * print_r($redis->keys('*')); + * @see https://redis.io/commands/swapdb + * @see Redis::del() * - * // Swap them back + * @example + * $redis->select(0); + * $redis->set('db0-key', 'db0-value'); * $redis->swapdb(0, 1); - * - * // Array - * // ( - * // [0] => db0-key1 - * // [1] => db0-key2 - * // ) - * print_r($redis->keys('*')); - * ?> - * + * $redis->get('db0-key'); */ public function swapdb(int $src, int $dst): Redis|bool; @@ -3361,16 +2884,7 @@ public function swapdb(int $src, int $dst): Redis|bool; * @return A two element array consisting of a Unix Timestamp and the number of microseconds * elapsed since the second. * - * - * 'localhost']); - * - * // Array - * // ( - * // [0] => 1667271026 - * // [1] => 355678 - * // ) - * print_r($redis->time()); + * @example $redis->time(); */ public function time(): Redis|array; @@ -3382,27 +2896,9 @@ public function time(): Redis|array; * no expiration, and -2 if the key does not exist. In the event of an * error, this command will return false. * - * - * 'localhost']); - * - * $redis->multi() - * ->setex('expires_in_60s', 60, 'test') - * ->set('doesnt_expire', 'persistent') - * ->del('not_a_key') - * ->exec(); - * - * // Returns <= 60 - * $redis->ttl('expires_in_60s'); - * - * // Returns -1 - * $redis->ttl('doesnt_expire'); - * - * // Returns -2 (key doesn't exist) - * $redis->ttl('not_a_key'); + * @see https://redis.io/commands/ttl * - * ?> - * + * @example $redis->ttl('mykey'); */ public function ttl(string $key): Redis|int|false; @@ -3431,10 +2927,6 @@ public function type(string $key): Redis|int|false; * deletion is asynchronous, meaning it is safe to delete large keys without fear of * Redis blocking for a long period of time. * - * @see https://redis.io/commands/unlink - * @see https://redis.io/commands/del - * @see Redis::del() - * * @param array|string $key_or_keys Either an array with one or more keys or a string with * the first key to delete. * @param string $other_keys If the first argument passed to this method was a string @@ -3442,17 +2934,12 @@ public function type(string $key): Redis|int|false; * * @return Redis|int|false The number of keys deleted or false on failure. * - * - * 'localhost']); - * - * // OPTION 1: Called with a single array of keys - * $redis->unlink(['key1', 'key2', 'key3']); + * @see https://redis.io/commands/unlink + * @see https://redis.io/commands/del + * @see Redis::del() * - * // OPTION 2: Called with a variadic number of arguments - * $redis->unlink('key1', 'key2', 'key3'); - * ?> - * + * @example $redis->unlink('key1', 'key2', 'key3'); + * @example $redis->unlink(['key1', 'key2', 'key3']); */ public function unlink(array|string $key, string ...$other_keys): Redis|int|false; @@ -3500,8 +2987,6 @@ public function xack(string $key, string $group, array $ids): int|false; /** * Append a message to a stream. * - * @see https://redis.io/commands/xadd - * * @param string $key The stream name. * @param string $id The ID for the message we want to add. This can be the special value '*' * which means Redis will generate the ID that appends the message to the @@ -3515,35 +3000,10 @@ public function xack(string $key, string $group, array $ids): int|false; * `$maxlen` values. * @param bool $nomkstream If passed as `TRUE`, the stream must exist for Redis to append the message. * - * - * 'localhost']); + * @see https://redis.io/commands/xadd * - * $redis->del('ds9-season-1'); - * - * $redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']); - * $redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']); - * $redis->xAdd('ds9-season-1', '1-3', ['title' => 'Emissary Part 2']); - * $redis->xAdd('ds9-season-1', '1-4', ['title' => 'Past Prologue']); - * - * // Array - * // ( - * // [1-1] => Array - * // ( - * // [title] => Emissary Part 1 - * // ) - * // - * // [1-2] => Array - * // ( - * // [title] => A Man Alone - * // ) - * // - * // ) - * $redis->xRange('ds9-season-1', '1-1', '1-2'); - * ?> - * ?> - * + * @example $redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']); + * @example $redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']); */ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): Redis|string|false; @@ -3559,41 +3019,7 @@ public function xclaim(string $key, string $group, string $consumer, int $min_id * * @return Redis|int|false The number of messages removed or false on failure. * - * - * $redis = new Redis(['host' => 'localhost']); - * - * $redis->del('stream'); - * - * for ($a = 1; $a <= 3; $a++) { - * for ($b = 1; $b <= 2; $b++) { - * $redis->xAdd('stream', "$a-$b", ['id' => "$a-$b"]); - * } - * } - * - * // Remove some elements - * $redis->xDel('stream', ['1-1', '2-1', '3-1']); - * - * // Array - * // ( - * // [1-2] => Array - * // ( - * // [id] => 1-2 - * // ) - * // - * // [2-2] => Array - * // ( - * // [id] => 2-2 - * // ) - * // - * // [3-2] => Array - * // ( - * // [id] => 3-2 - * // ) - * // - * // ) - * $redis->xRange('stream', '-', '+'); - * ?> - * + * @example $redis->xDel('stream', ['1-1', '2-1', '3-1']); */ public function xdel(string $key, array $ids): Redis|int|false; @@ -3646,26 +3072,11 @@ public function xgroup(string $operation, string $key = null, string $group = nu * * @return mixed This command can return different things depending on the operation being called. * - * - * 'localhost']); - * - * $redis->del('stream'); - * - * $redis->xAdd('stream', "0-1", ['payload' => '0-1']); - * $redis->xAdd('stream', "0-2", ['payload' => '0-2']); - * $redis->xAdd('stream', "0-3", ['payload' => '0-3']); + * @see https://redis.io/commands/xinfo * - * // Retrieve any consmers for a given key - * $redis->xInfo('CONSUMERS', 'stream'); - * - * // Retrieve any groups for a given key - * $redis->xInfo('GROUPS', 'stream'); - * - * // Retrieve general stream information along with messages - * $redis->xInfo('STREAM', 'stream'); - * ?> - * + * @example $redis->xInfo('CONSUMERS', 'stream'); + * @example $redis->xInfo('GROUPS', 'stream'); + * @example $redis->xInfo('STREAM', 'stream'); */ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed; @@ -3673,24 +3084,13 @@ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = n /** * Get the number of messages in a Redis STREAM key. * - * @see https://redis.io/commands/xlen - * * @param string $key The Stream to check. * * @return Redis|int|false The number of messages or false on failure. * - * - * 'localhost']); - * - * $redis->del('stream'); - * $redis->xadd('stream', '*', ['first' => 'message']); - * $redis->xadd('stream', '*', ['second' => 'message']); + * @see https://redis.io/commands/xlen * - * // int(2) - * $redis->xLen('stream'); - * ?> - * + * @example $redis->xLen('stream'); */ public function xlen(string $key): Redis|int|false; @@ -3716,8 +3116,6 @@ public function xpending(string $key, string $group, ?string $start = null, ?str /** * Get a range of entries from a STREAM key. * - * @see https://redis.io/commands/xrange - * * @param string $key The stream key name to list. * @param string $start The minimum ID to return. * @param string $end The maximum ID to return. @@ -3725,92 +3123,34 @@ public function xpending(string $key, string $group, ?string $start = null, ?str * * @return Redis|array|bool The entries in the stream within the requested range or false on failure. * - * - * 'localhost']); - * - * $redis->del('stream'); - * - * for ($i = 0; $i < 2; $i++) { - * for ($j = 1; $j <= 2; $j++) { - * $redis->xAdd('stream', "$i-$j", ['message' => "$i:$j"]); - * } - * } + * @see https://redis.io/commands/xrange * - * //Array - * //( - * // [0-1] => Array - * // ( - * // [message] => 0:1 - * // ) - * // - * // [0-2] => Array - * // ( - * // [message] => 0:2 - * // ) - * // - * //) - * $redis->xRange('stream', '0-1', '0-2'); - * - * // '-' and '+' are special values which mean 'minimum possible', - * // and 'maximum possible' id, respectively. - * $redis->xRange('stream', '-', '+'); - * ?> - * + * @example $redis->xRange('stream', '0-1', '0-2'); + * @example $redis->xRange('stream', '-', '+'); */ public function xrange(string $key, string $start, string $end, int $count = -1): Redis|array|bool; /** * Consume one or more unconsumed elements in one or more streams. * - * @see https://redis.io/commands/xread - * * @param array $streams An associative array with stream name keys and minimum id values. * @param int $count An optional limit to how many entries are returnd *per stream* * @param int $block An optional maximum number of milliseconds to block the caller if no * data is available on any of the provided streams. * - * - * $redis = new Redis(['host' => 'localhost']); + * @return Redis|array|bool An array of read elements or false if there aren't any. * - * $redis->del('s03', 's03'); + * @see https://redis.io/commands/xread * + * @example * $redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']); * $redis->xAdd('s03', '3-2', ['title' => 'The Search, Part II']); * $redis->xAdd('s03', '3-3', ['title' => 'The House Of Quark']); - * * $redis->xAdd('s04', '4-1', ['title' => 'The Way of the Warrior']); * $redis->xAdd('s04', '4-3', ['title' => 'The Visitor']); * $redis->xAdd('s04', '4-4', ['title' => 'Hippocratic Oath']); * - * // Array - * // ( - * // [s03] => Array - * // ( - * // [3-3] => Array - * // ( - * // [title] => The House Of Quark - * // ) - * // - * // ) - * // - * // [s04] => Array - * // ( - * // [4-3] => Array - * // ( - * // [title] => The Visitor - * // ) - * // - * // [4-4] => Array - * // ( - * // [title] => Hippocratic Oath - * // ) - * // - * // ) - * // - * // ) - * print_r($redis->xRead(['s03' => '3-2', 's04' => '4-1'])); - * + * $redis->xRead(['s03' => '3-2', 's04' => '4-1']); */ public function xread(array $streams, int $count = -1, int $block = -1): Redis|array|bool; @@ -3825,21 +3165,14 @@ public function xread(array $streams, int $count = -1, int $block = -1): Redis|a * * @return Redis|array|bool Zero or more unread messages or false on failure. * - * - * 'localhost']); - * - * $redis->del('episodes'); + * @see https://redis.io/commands/xreadgroup * - * // Create a consumer group (and stream) + * @example * $redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true); * - * // Add a couple of messages to the stream * $redis->xAdd('episodes', '1-1', ['title' => 'Emissary: Part 1']); * $redis->xAdd('episodes', '1-2', ['title' => 'A Man Alone']); * - * // Now read some messages with our consumer group * $messages = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']); * * // After having read the two messages, add another @@ -3853,29 +3186,12 @@ public function xread(array $streams, int $count = -1, int $block = -1): Redis|a * * // We can now pick up where we left off, and will only get the final message * $msgs = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']); - * - * // array(1) { - * // ["episodes"]=> - * // array(1) { - * // ["1-3"]=> - * // array(1) { - * // ["title"]=> - * // string(16) "Emissary: Part 2" - * // } - * // } - * // } - * var_dump($msgs); - * ?> - * */ public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): Redis|array|bool; /** * Get a range of entries from a STREAM ke in reverse cronological order. * - * @see https://redis.io/commands/xrevrange - * @see https://redis.io/commands/xrange - * * @param string $key The stream key to query. * @param string $end The maximum message ID to include. * @param string $start The minimum message ID to include. @@ -3883,47 +3199,17 @@ public function xreadgroup(string $group, string $consumer, array $streams, int * * @return Redis|array|bool The entries within the requested range, from newest to oldest. * - * - * 'localhost']); - * - * $redis->del('stream'); - * - * for ($i = 0; $i < 2; $i++) { - * for ($j = 1; $j <= 2; $j++) { - * $redis->xAdd('stream', "$i-$j", ['message' => "$i:$j"]); - * } - * } - * - * // Array - * // ( - * // [0-2] => Array - * // ( - * // [message] => 0:2 - * // ) - * // - * // [0-1] => Array - * // ( - * // [message] => 0:1 - * // ) - * // - * // ) - * $redis->xRevRange('stream', '0-2', '0-1'); - * - * // '-' and '+' are special values which mean 'minimum possible', - * // and 'maximum possible' id, respectively. - * $redis->xRevRange('stream', '+', '-'); - * ?> - * + * @see https://redis.io/commands/xrevrange + * @see https://redis.io/commands/xrange * + * @example $redis->xRevRange('stream', '0-2', '0-1'); + * @example $redis->xRevRange('stream', '+', '-'); */ public function xrevrange(string $key, string $end, string $start, int $count = -1): Redis|array|bool; /** * Truncate a STREAM key in various ways. * - * @see https://redis.io/commands/xtrim - * * @param string $key The STREAM key to trim. * @param string $threshold This can either be a maximum length, or a minimum id. * MAXLEN - An integer describing the maximum desired length of the stream after the command. @@ -3934,71 +3220,25 @@ public function xrevrange(string $key, string $end, string $start, int $count = * @param bool $minid When set to `true`, users should pass a minimum ID to the `$threshold` argument. * @param int $limit An optional upper bound on how many entries to trim during the command. * - * - * 'localhost']); + * @return Redis|int|false The number of entries deleted from the stream. * - * $redis->del('stream'); - * $redis->xAdd('stream', '1-1', ['one' => 'one']); - * $redis->xAdd('stream', '1-2', ['one' => 'two']); - * $redis->xAdd('stream', '2-1', ['two' => 'one']); - * $redis->xAdd('stream', '2-2', ['two' => 'two']); - * - * // Trim to three elemn - * $redis->xTrim('stream', 3); - * - * // Array - * // ( - * // [1-2] => Array - * // ( - * // [one] => two - * // ) - * // - * // [2-1] => Array - * // ( - * // [two] => one - * // ) - * // - * // [2-2] => Array - * // ( - * // [two] => two - * // ) - * // - * // ) - * $redis->xRange('stream', '-', '+'); - * - * // Now let's trim everything older than '2-1' - * $redis->xTrim('stream', '2-1', false, true); - * - * // Array - * // ( - * // [2-1] => Array - * // ( - * // [two] => one - * // ) - * // - * // [2-2] => Array - * // ( - * // [two] => two - * // ) - * // - * // ) - * print_r($redis->xRange('stream', '-', '+')); - * ?> - * + * @see https://redis.io/commands/xtrim + * + * @example $redis->xTrim('stream', 3); + * @example $redis->xTrim('stream', '2-1', false, true); */ public function xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1): Redis|int|false; /** * Add one or more elements and scores to a Redis sorted set. * - * @see https://redis.io/commands/zadd - * * @param string $key The sorted set in question. * @param array|float $score_or_options Either the score for the first element, or an array * containing one or more options for the operation. * @param mixed $more_scores_and_mems A variadic number of additional scores and members. * + * @return Redis|int|false The return value varies depending on the options passed. + * * Following is information about the options that may be passed as the scond argument: * * @@ -4020,66 +3260,29 @@ public function xtrim(string $key, string $threshold, bool $approx = false, bool * Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis will send whichever one is last in * the options array. * - * - * 'localhost']); + * @see https://redis.io/commands/zadd * - * $redis->del('zs'); - * - * // Add three new elements to our zset - * $redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third'); - * - * // Array - * // ( - * // [first] => 1 - * // [second] => 2 - * // [third] => 3 - * // ) - * $redis->zRange('zs', 0, -1, true); - * - * // Update only existing elements. Note that 'new-element' isn't added - * $redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element'); - * - * // Array - * // ( - * // [first] => 1 - * // [third] => 3 - * // [second] => 8 - * // ) - * print_r($redis->zRange('zs', 0, -1, true)); - * ?> - * + * @example $redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third'); + * @example $redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element'); */ public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|false; /** * Return the number of elements in a sorted set. * - * @see https://redis.io/commands/zcard - * * @param string $key The sorted set to retreive cardinality from. * * @return Redis|int|false The number of elements in the set or false on failure * - * - * 'localhost']); - * - * $redis->del('zs'); - * $redis->zAdd('zs', 0, 'a', 1, 'b', 2, 'c'); + * @see https://redis.io/commands/zcard * - * // count(['a', 'b', 'c']) == 3 - * $redis->zCard('zs'); - * ?> - * + * @example $redis->zCard('zs'); */ public function zCard(string $key): Redis|int|false; /** * Count the number of members in a sorted set with scores inside a provided range. * - * @see https://redis.io/commands/zcount - * * @param string $key The sorted set to check. * @param string $min The minimum score to include in the count * @param string $max The maximum score to include in the count @@ -4087,50 +3290,26 @@ public function zCard(string $key): Redis|int|false; * NOTE: In addition to a floating point score you may pass the special values of '-inf' and * '+inf' meaning negative and positive infinity, respectively. * - * - * 'localhost']); - * - * $redis->del('fruit-rankings'); - * $redis->zadd('fruit-rankings', -99, 'tomato', 50, 'apple', 60, 'pear', 85, 'mango'); - * - * // count(['apple', 'oear', 'mango']) == 3 - * $redis->zCount('fruit-rankings', '0', '+inf'); - * - * // count(['apple', 'pear']) == 2 - * $redis->zCount('fruit-rankings', 50, 60); + * @see https://redis.io/commands/zcount * - * // count(['tomato']) == 1 - * $redis->zCount('fruit-rankings', '-inf', 0); - * ?> - * + * @example $redis->zCount('fruit-rankings', '0', '+inf'); + * @example $redis->zCount('fruit-rankings', 50, 60); + * @example $redis->zCount('fruit-rankings', '-inf', 0); */ public function zCount(string $key, string $start, string $end): Redis|int|false; /** * Create or increment the score of a member in a Redis sorted set * - * @see https://redis.io/commands/zincrby - * * @param string $key The sorted set in question. * @param float $value How much to increment the score. * * @return Redis|float|false The new score of the member or false on failure. - - * - * 'localhost']); - * - * $redis->del('zs'); - * $redis->zAdd('zs', 0, 'apples', 2, 'bananas'); * - * // 2 + 5.0 == 7 - * print_r($redis->zIncrBy('zs', 5.0, 'bananas')); + * @see https://redis.io/commands/zincrby * - * // new element so 0 + 2.0 == 2 - * print_r($redis->zIncrBy('zs', 2.0, 'eggplants')); - * ?> - * + * @example $redis->zIncrBy('zs', 5.0, 'bananas'); + * @example $redis->zIncrBy('zs', 2.0, 'eggplants'); */ public function zIncrBy(string $key, float $value, mixed $member): Redis|float|false; @@ -4138,29 +3317,17 @@ public function zIncrBy(string $key, float $value, mixed $member): Redis|float|f * Count the number of elements in a sorted set whos members fall within the provided * lexographical range. * - * @see https://redis.io/commands/zlexcount - * * @param string $key The sorted set to check. * @param string $min The minimum matching lexographical string * @param string $max The maximum matching lexographical string * * @return Redis|int|false The number of members that fall within the range or false on failure. * - * - * 'localhost']); + * @see https://redis.io/commands/zlexcount * - * $redis->del('captains'); + * @example * $redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer'); - * - * count(['Archer', 'Janeway', 'Kirk', 'Picard']) == 4 * $redis->zLexCount('captains', '[A', '[S'); - * - * count(['Kirk', 'Picard']) == 2 - * $redis->zRangeByLex('captains', '[A', '[S', 2, 2); - * ?> - * - * */ public function zLexCount(string $key, string $min, string $max): Redis|int|false; @@ -4175,99 +3342,47 @@ public function zLexCount(string $key, string $min, string $max): Redis|int|fals * * @return Redis|array|false An array of the scores of the requested elements. * - * - * 'localhost']); - * - * $redis->del('zs'); - * + * @example * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three'); * - * // array(2) { - * // [0]=> - * // float(0) - * // [1]=> - * // float(2) - * // } * $redis->zMScore('zs', 'zero', 'two'); - * - * // array(2) { - * // [0]=> - * // float(1) - * // [1]=> - * // bool(false) - * // } * $redis->zMScore('zs', 'one', 'not-a-member'); - * ?> - * */ public function zMscore(string $key, mixed $member, mixed ...$other_members): Redis|array|false; /** * Pop one or more of the highest scoring elements from a sorted set. * - * @see https://redis.io/commands/zpopmax - * * @param string $key The sorted set to pop elements from. * @param int $count An optional count of elements to pop. * * @return Redis|array|false All of the popped elements with scores or false on fialure. * - * - * 'localhost']); + * @see https://redis.io/commands/zpopmax * - * $redis->del('zs'); + * @example * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three'); * - * // Array - * // ( - * // [three] => 3 - * // ) - * print_r($redis->zPopMax('zs')); - * - * // Array - * // ( - * // [two] => 2 - * // [one] => 1 - * // ) - * print_r($redis->zPopMax('zs', 2)); - * ?> - * + * $redis->zPopMax('zs'); + * $redis->zPopMax('zs', 2);. */ public function zPopMax(string $key, int $count = null): Redis|array|false; /** * Pop one or more of the lowest scoring elements from a sorted set. * - * @see https://redis.io/commands/zpopmin - * * @param string $key The sorted set to pop elements from. * @param int $count An optional count of elements to pop. * * @return Redis|array|false The popped elements with their scores or false on failure. * - * - * 'localhost']); + * @see https://redis.io/commands/zpopmin * - * $redis->del('zs'); + * @example * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three'); * - * // Array - * // ( - * // [zero] => 0 - * // ) * $redis->zPopMin('zs'); - * - * // Array - * // ( - * // [one] => 1 - * // [two] => 2 - * // ) * $redis->zPopMin('zs', 2); - * ?> - * */ public function zPopMin(string $key, int $count = null): Redis|array|false; @@ -4276,9 +3391,6 @@ public function zPopMin(string $key, int $count = null): Redis|array|false; * How the command works in particular is greatly affected by the options that * are passed in. * - * @see https://redis.io/commands/zrange/ - * @category zset - * * @param string $key The sorted set in question. * @param mixed $start The starting index we want to return. * @param mixed $end The final index we want to return. @@ -4286,33 +3398,32 @@ public function zPopMin(string $key, int $count = null): Redis|array|false; * @param array|bool|null $options This value may either be an array of options to pass to * the command, or for historical purposes a boolean which * controls just the 'WITHSCORES' option. + * + * $options = [ + * 'WITHSCORES' => true, // Return both scores and members. + * 'LIMIT' => [10, 10], // Start at offset 10 and return 10 elements. + * 'REV' // Return the elements in reverse order + * 'BYSCORE', // Treat `start` and `end` as scores instead + * 'BYLEX' // Treat `start` and `end` as lexicographical values. + * ]; + * * - * @return Redis|array|false An array with matching elements or false on failure. + * Note: 'BYLEX' and 'BYSCORE' are mutually exclusive. * - * Detailed description of options array: * - * - * true, // Return both scores and members. - * 'LIMIT' => [10, 10], // Start at offset 10 and return 10 elements. - * 'REV' // Return the elements in reverse order - * 'BYSCORE', // Treat `start` and `end` as scores instead - * 'BYLEX' // Treat `start` and `end` as lexicographical values. - * ]; - * ?> - * + * @return Redis|array|false An array with matching elements or false on failure. * - * Note: 'BYLEX' and 'BYSCORE' are mutually exclusive. + * @see https://redis.io/commands/zrange/ + * @category zset * + * @example $redis->zRange('zset', 0, -1); + * @example $redis->zRange('zset', '-inf', 'inf', ['byscore' => true]); */ public function zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null): Redis|array|false; /** * Retrieve a range of elements from a sorted set by legographical range. * - * @see https://redis.io/commands/zrangebylex - * * @param string $key The sorted set to retreive elements from * @param string $min The minimum legographical value to return * @param string $max The maximum legographical value to return @@ -4321,38 +3432,20 @@ public function zRange(string $key, mixed $start, mixed $end, array|bool|null $o * * @return Redis|array|false An array of matching elements or false on failure. * - * - * 'localhost']); + * @see https://redis.io/commands/zrangebylex * - * $redis->del('captains'); + * @example + * $redis = new Redis(['host' => 'localhost']); * $redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer'); * - * // Array - * // ( - * // [0] => Archer - * // [1] => Janeway - * // [2] => Kirk - * // [3] => Picard - * // ) * $redis->zRangeByLex('captains', '[A', '[S'); - * - * // Array - * // ( - * // [0] => Kirk - * // [1] => Picard - * // ) * $redis->zRangeByLex('captains', '[A', '[S', 2, 2); - * ?> - * */ public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false; /** * Retrieve a range of members from a sorted set by their score. * - * @see https://redis.io/commands/zrangebyscore - * * @param string $key The sorted set to query. * @param string $start The minimum score of elements that Redis should return. * @param string $end The maximum score of elements that Redis should return. @@ -4364,53 +3457,10 @@ public function zRangeByLex(string $key, string $min, string $max, int $offset = * * @return Redis|array|false The number of matching elements or false on failure. * - * - * 'localhost']); - * - * $redis->del('zs'); - * - * for ($i = 0; $i < 50; $i++) { - * $redis->zAdd('zs', $i, "mem:$i"); - * } + * @see https://redis.io/commands/zrangebyscore * - * // Array - * // ( - * // [0] => mem:0 - * // [1] => mem:1 - * // [2] => mem:2 - * // [3] => mem:3 - * // [4] => mem:4 - * // ) - * $redis->zRangeByScore('zs', 0, 4); - * - * // Array - * // ( - * // [mem:20] => 20 - * // [mem:21] => 21 - * // [mem:22] => 22 - * // [mem:23] => 23 - * // [mem:24] => 24 - * // [mem:25] => 25 - * // [mem:26] => 26 - * // [mem:27] => 27 - * // [mem:28] => 28 - * // [mem:29] => 29 - * // [mem:30] => 30 - * // ) - * $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]); - * - * // Array - * // ( - * // [mem:25] => 25 - * // [mem:26] => 26 - * // [mem:27] => 27 - * // [mem:28] => 28 - * // [mem:29] => 29 - * // ) - * $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]); - * ?> - * + * @example $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]); + * @example $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]); */ public function zRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false; @@ -4418,10 +3468,6 @@ public function zRangeByScore(string $key, string $start, string $end, array $op * This command is similar to ZRANGE except that instead of returning the values directly * it will store them in a destination key provided by the user * - * @see https://redis.io/commands/zrange/ - * @see Redis::zRange - * @category zset - * * @param string $dstkey The key to store the resulting element(s) * @param string $srckey The source key with element(s) to retrieve * @param string $start The starting index to store @@ -4430,7 +3476,11 @@ public function zRangeByScore(string $key, string $start, string $end, array $op * * @return Redis|int|false The number of elements stored in $dstkey or false on failure. * - * See Redis::zRange for a full description of the possible options. + * @see https://redis.io/commands/zrange/ + * @see Redis::zRange + * @category zset + * + * See {@link Redis::zRange} for a full description of the possible options. */ public function zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL): Redis|int|false; @@ -4438,198 +3488,96 @@ public function zrangestore(string $dstkey, string $srckey, string $start, strin /** * Retrieve one or more random members from a Redis sorted set. * - * @see https://redis.io/commands/zrandmember - * * @param string $key The sorted set to pull random members from. * @param array $options One or more options that determine exactly how the command operates. * * OPTION TYPE MEANING * 'COUNT' int The number of random members to return. * 'WITHSCORES' bool Whether to return scores and members instead of - * just members. - * - * 'localhost']); * - * $redis->multi()->del('zs')->zadd('zs', 1, 'one', 2, 'two', 3, 'three')->exec(); + * @return Redis|string|array One ore more random elements. * - * // Return two random members from our set, with scores - * $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]); + * @see https://redis.io/commands/zrandmember * - * ?> - * + * @example $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]); */ public function zRandMember(string $key, array $options = null): Redis|string|array; /** * Get the rank of a member of a sorted set, by score. * - * @see https://redis.io/commands/zrank - * * @param string $key The sorted set to check. * @param mixed $memeber The member to test. * - * - * 'localhost']); - * - * $redis->multi()->del('zs')->zadd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three')->exec(); - * - * // Rank 0 - * $redis->zRank('zs', 'zero'); - * - * // Rank 3 - * $redis->zRank('zs', 'three'); - * - * ?> - * + * @return Redis|int|false The rank of the requested member. + * @see https://redis.io/commands/zrank * + * @example $redis->zRank('zs', 'zero'); + * @example $redis->zRank('zs', 'three'); */ public function zRank(string $key, mixed $member): Redis|int|false; /** * Remove one or more members from a Redis sorted set. * - * @see https://redis.io/commands/zrem - * * @param mixed $key The sorted set in question. * @param mixed $member The first member to remove. * @param mixed $other_members One or more members to remove passed in a variadic fashion. * * @return Redis|int|false The number of members that were actually removed or false on failure. * - * - * 'localhost']); - * - * $redis->del('zs'); - * - * for ($i = 0; $i < 10; $i++) { - * $redis->zAdd('zs', $i, "mem:$i"); - * } - * - * // Remove a few elements - * $redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9'); + * @see https://redis.io/commands/zrem * - * // Array - * // ( - * // [0] => mem:3 - * // [1] => mem:4 - * // [2] => mem:5 - * // ) - * $redis->zRange('zs', 0, -1); - * ?> + * @example $redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9'); */ public function zRem(mixed $key, mixed $member, mixed ...$other_members): Redis|int|false; /** * Remove zero or more elements from a Redis sorted set by legographical range. * - * @see https://redis.io/commands/zremrangebylex - * @see Redis::zrangebylex() - * * @param string $key The sorted set to remove elements from. * @param string $min The start of the lexographical range to remove. * @param string $max The end of the lexographical range to remove * * @return Redis|int|false The number of elements removed from the set or false on failure. * - * - * 'localhost']); - * - * $redis->pipeline()->del('zs') - * ->zAdd('zs', 1, 'apple', 2, 'banana', 3, 'carrot', 4, 'date', 5, 'eggplant') - * ->exec(); - * - * - * // Remove a* (inclusive) .. b* (exclusive), meaning 'apple' will be removed, but 'banana' not - * $redis->zRemRangeByLex('zs', '[a', '(b'); - * - * // Array - * // ( - * // [0] => banana - * // [1] => carrot - * // [2] => date - * // [3] => eggplant - * // ) - * print_r($redis->zRange('zs', 0, -1)); - * - * // Remove the elements between 'banana' and 'eggplant' - * $redis->zRemRangeByLex('zs', '(banana', '(eggplant'); + * @see https://redis.io/commands/zremrangebylex + * @see Redis::zrangebylex() * - * // Array - * // ( - * // [0] => banana - * // [1] => eggplant - * // ) - * print_r($redis->zRange('zs', 0, -1)); - * ?> - * + * @example $redis->zRemRangeByLex('zs', '[a', '(b'); + * @example $redis->zRemRangeByLex('zs', '(banana', '(eggplant'); */ public function zRemRangeByLex(string $key, string $min, string $max): Redis|int|false; /** * Remove one or more members of a sorted set by their rank. * - * @see https://redis.io/commands/zremrangebyrank - * * @param string $key The sorted set where we wnat to remove members. * @param int $start The rank when we want to start removing members * @param int $end The rank we want to stop removing membersk. * * @return Redis|int|false The number of members removed from the set or false on failure. * - * - * 'localhost']); - * - * $redis->del('zs'); - * $redis->zAdd('zs', 0, 'zeroth', 1, 'first', 2, 'second', 3, 'third', 4, 'fourth'); - * - * // Remove ranks 0..3 - * $redis->zRemRangeByRank('zs', 0, 3); + * @see https://redis.io/commands/zremrangebyrank * - * // Array - * // ( - * // [0] => fourth - * // ) - * $redis->zRange('zs', 0, -1); - * ?> - * + * @example $redis->zRemRangeByRank('zs', 0, 3); */ public function zRemRangeByRank(string $key, int $start, int $end): Redis|int|false; /** * Remove one or more members of a sorted set by their score. * - * @see https://redis.io/commands/zremrangebyrank - * * @param string $key The sorted set where we wnat to remove members. * @param int $start The lowest score to remove. * @param int $end The highest score to remove. * * @return Redis|int|false The number of members removed from the set or false on failure. * - * - * 'localhost']); - * - * $redis->del('zs'); - * $redis->zAdd('zs', 3, 'three', 5, 'five', 7, 'seven', 7, 'seven-again', 13, 'thirteen', 22, 'twenty-two'); - * - * // Removes every member with scores >= 7 and scores <= 13. - * $redis->zRemRangeByScore('zs', 7, 13); + * @see https://redis.io/commands/zremrangebyrank * - * // Array - * // ( - * // [0] => three - * // [1] => five - * // [2] => twenty-two - * // ) - * $redis->zRange('zs', 0, -1); - * ?> - * + * @example + * $redis->zAdd('zs', 2, 'two', 4, 'four', 6, 'six'); + * $redis->zRemRangeByScore('zs', 2, 4); */ public function zRemRangeByScore(string $key, string $start, string $end): Redis|int|false; @@ -4645,42 +3593,18 @@ public function zRemRangeByScore(string $key, string $start, string $end): Redis * @return Redis|array|false The members (and possibly scores) of the matching elements or false * on failure. * - * $redis = new Redis(['host' => 'localhost']); + * @see https://redis.io/commands/zrevrange * - * $redis->del('zs'); - * $redis->zAdd('zs', 1, 'one', 2, 'two', 5, 'five', 10, 'ten'); - * - * // Array - * // ( - * // [0] => ten - * // [1] => five - * // [2] => two - * // [3] => one - * // ) - * print_r($redis->zRevRange('zs', 0, -1)); - * - * // Array - * // ( - * // [0] => two - * // [1] => one - * // ) - * print_r($redis->zRevRange('zs', 2, 3)); - * - * // Additionally, you may pass `true` or `['withscores' => true]` to tell redis to return scores - * // as well as members. - * $redis->zRevRange('zs', 0, -1, true); - * $redis->zRevRange('zs', 0, -1, ['withscores' => true]); - * ?> - * + * @example $redis->zRevRange('zs', 0, -1); + * @example $redis->zRevRange('zs', 2, 3); + * @example $redis->zRevRange('zs', 0, -1, true); + * @example $redis->zRevRange('zs', 0, -1, ['withscores' => true]); */ public function zRevRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false; /** * List members of a Redis sorted set within a legographical range, in reverse order. * - * @see https://redis.io/commands/zrevrangebylex - * @see Redis::zrangebylex() - * * @param string $key The sorted set to list * @param string $min The maximum legographical element to include in the result. * @param string $min The minimum lexographical element to include in the result. @@ -4689,30 +3613,11 @@ public function zRevRange(string $key, int $start, int $end, mixed $scores = nul * * @return Redis|array|false The matching members or false on failure. * - * - * 'localhost']); + * @see https://redis.io/commands/zrevrangebylex + * @see Redis::zrangebylex() * - * $redis->del('captains'); - * $redis->zAdd('captains', 0, 'Janeway', 0, 'Picard', 0, 'Kirk', 0, 'Archer'); - * - * // Array - * // ( - * // [0] => Picard - * // [1] => Kirk - * // [2] => Janeway - * // ) - * $redis->zRevRangeByLex('captains', '[Q', '[J'); - * - * // Array - * // ( - * // [0] => Kirk - * // [1] => Janeway - * // ) - * $redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2); - * ?> - * + * @example $redis->zRevRangeByLex('captains', '[Q', '[J'); + * @example $redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2); */ public function zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1): Redis|array|false; @@ -4736,100 +3641,52 @@ public function zRevRangeByLex(string $key, string $max, string $min, int $offse * * @return Redis|array|false The matching members in reverse order of score or false on failure. * - * - * 'localhost']); - * - * $redis->del('oldest-people'); + * @see https://redis.io/commands/zrevrangebyscore * + * @example * $redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka', * 119.2658, 'Sarah Knauss', 118.7205, 'Lucile Randon', * 117.7123, 'Nabi Tajima', 117.6301, 'Marie-Louise Meilleur', * 117.5178, 'Violet Brown', 117.3753, 'Emma Morano', * 117.2219, 'Chiyo Miyako', 117.0740, 'Misao Okawa'); * - * // Array - * // ( - * // [0] => Kane Tanaka - * // [1] => Sarah Knauss - * // ) * $redis->zRevRangeByScore('oldest-people', 122, 119); - * - * //Array - * //( - * // [0] => Jeanne Calment - * // [1] => Kane Tanaka - * // [2] => Sarah Knauss - * // [3] => Lucile Randon - * //) * $redis->zRevRangeByScore('oldest-people', 'inf', 118); - * - * // Array - * // ( - * // [0] => Emma Morano - * // ) * $redis->zRevRangeByScore('oldest-people', '117.5', '-inf', ['LIMIT' => [0, 1]]); - * ?> - * - * */ public function zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []): Redis|array|false; /** * Retrieve a member of a sorted set by reverse rank. * - * @see https://redis.io/commands/zrevrank - * * @param string $key The sorted set to query. * @param mixed $member The member to look up. * * @return Redis|int|false The reverse rank (the rank if counted high to low) of the member or * false on failure. + * @see https://redis.io/commands/zrevrank * - * - * 'localhost']); - * - * $redis->del('ds9-characters'); - * + * @example * $redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo'); * - * // Highest score, reverse rank 0 * $redis->zrevrank('ds9-characters', 'Sisko'); - * - * // Second highest score, reverse rank 1 * $redis->zrevrank('ds9-characters', 'Garak'); - * ?> - * */ public function zRevRank(string $key, mixed $member): Redis|int|false; /** * Get the score of a member of a sorted set. * - * @see https://redis.io/commands/zscore - * * @param string $key The sorted set to query. * @param mixed $member The member we wish to query. * * @return The score of the requested element or false if it is not found. * - * - * 'localhost']); - * - * $redis->del('telescopes'); + * @see https://redis.io/commands/zscore * + * @example * $redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET'); - * - * foreach ($redis->zRange('telescopes', 0, -1) as $name) { - * // Get the score for this member - * $aperature = $redis->zScore('telescopes', $name); - * - * echo "The '$name' telescope has an effective aperature of: $aperature meters\n"; - * } - * ?> - * + * $redis->zScore('telescopes', 'LBT'); */ public function zScore(string $key, mixed $member): Redis|float|false; @@ -4837,41 +3694,27 @@ public function zScore(string $key, mixed $member): Redis|float|false; * Given one or more sorted set key names, return every element that is in the first * set but not any of the others. * - * @see https://redis.io/commands/zdiff - * * @param array $keys One ore more sorted sets. * @param array $options An array which can contain ['WITHSCORES' => true] if you want Redis to * return members and scores. * * @return Redis|array|false An array of members or false on failure. * - * - * 'localhost']); - * - * $redis->del('primes', 'evens', 'mod3'); + * @see https://redis.io/commands/zdiff * + * @example * $redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five'); * $redis->zAdd('evens', 2, 'two', 4, 'four'); * $redis->zAdd('mod3', 3, 'three', 6, 'six'); * - * // Array - * // ( - * // [0] => one - * // [1] => five - * // ) - * print_r($redis->zDiff(['primes', 'evens', 'mod3'])); - * ?> - * - * + * $redis->zDiff(['primes', 'evens', 'mod3']); */ public function zdiff(array $keys, array $options = null): Redis|array|false; /** * Store the difference of one or more sorted sets in a destination sorted set. * - * @see https://redis.io/commands/zdiff - * @see Redis::zdiff() + * See {@link Redis::zdiff} for a more detailed description of how the diff operation works. * * @param string $key The destination set name. * @param array $keys One or more source key names @@ -4879,16 +3722,14 @@ public function zdiff(array $keys, array $options = null): Redis|array|false; * @return Redis|int|false The number of elements stored in the destination set or false on * failure. * - * NOTE: See Redis::zdiff() for a more detailed description of how the diff operation works. - * + * @see https://redis.io/commands/zdiff + * @see Redis::zdiff() */ public function zdiffstore(string $dst, array $keys): Redis|int|false; /** * Compute the intersection of one or more sorted sets and return the members * - * @see https://redis.io/commands/zinter - * * @param array $keys One ore more sorted sets. * @param array $weights An optional array of weights to be applied to each set when performing * the intersection. @@ -4897,37 +3738,15 @@ public function zdiffstore(string $dst, array $keys): Redis|int|false; * * @return Redis|array|false All of the members that exist in every set. * - * - * 'localhost']); - * - * $redis->del('tng', 'ds9'); + * @see https://redis.io/commands/zinter * + * @example * $redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard'); * $redis->zAdd('DS9', 2.5, 'Worf', 3.0, 'Kira', 4.0, 'Sisko'); * - * // Array - * // ( - * // [0] => Worf - * // ) * $redis->zInter(['TNG', 'DS9']); - * - * // Array - * // ( - * // [Worf] => 4.5 - * // ) * $redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true]); - * - * // Array - * // ( - * // [Worf] => 2.5 - * // ) * $redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true, 'aggregate' => 'max']); - * - * ?> - * - * */ public function zinter(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false; @@ -4946,28 +3765,17 @@ public function zinter(array $keys, ?array $weights = null, ?array $options = nu * * @return Redis|int|false The cardinality of the intersection or false on failure. * - * - * 'localhost']); - * - * $redis->del('zs1', 'zs2'); - * + * @example * $redis->zAdd('zs1', 1, 'one', 2, 'two', 3, 'three', 4, 'four'); * $redis->zAdd('zs2', 2, 'two', 4, 'four'); * - * // count(['two', 'four']) == 2 * $redis->zInterCard(['zs1', 'zs2']); - * ?> - * */ public function zintercard(array $keys, int $limit = -1): Redis|int|false; /** * Compute the intersection of one ore more sorted sets storing the result in a new sorted set. * - * @see https://redis.io/commands/zinterstore - * @see https://redis.io/commands/zinter - * * @param string $dst The destination sorted set to store the intersected values. * @param array $keys One ore more sorted set key names. * @param array $weights An optional array of floats to weight each passed input set. @@ -4979,42 +3787,22 @@ public function zintercard(array $keys, int $limit = -1): Redis|int|false; * * @return Redis|int|false The total number of members writtern to the destination set or false on failure. * - * - * 'localhost']); + * @see https://redis.io/commands/zinterstore + * @see https://redis.io/commands/zinter * - * $redis->del('zs', 'zs2', 'zs3'); + * @example * $redis->zAdd('zs1', 3, 'apples', 2, 'pears'); * $redis->zAdd('zs2', 4, 'pears', 3, 'bananas'); * $redis->zAdd('zs3', 2, 'figs', 3, 'pears'); * - * // Returns 1 (only 'pears' is in every set) * $redis->zInterStore('fruit-sum', ['zs1', 'zs2', 'zs3']); - * - * // Array - * // ( - * // [pears] => 9 - * // ) - * $redis->zRange('fruit-sum', 0, -1, true); - * * $redis->zInterStore('fruit-max', ['zs1', 'zs2', 'zs3'], NULL, 'MAX'); - * - * // Array - * // ( - * // [pears] => 4 - * // ) - * print_r($redis->zRange('fruit-max', 0, -1, true)); - * ?> */ public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false; /** * Scan the members of a sorted set incrementally, using a cursor * - * @see https://redis.io/commands/zscan - * @see https://redis.io/commands/scan - * @see Redis::scan() - * * @param string $key The sorted set to scan. * @param int $iterator A reference to an iterator that should be initialized to NULL initially, that * will be updated after each subsequent call to ZSCAN. Once the iterator @@ -5027,6 +3815,10 @@ public function zinterstore(string $dst, array $keys, ?array $weights = null, ?s * * @return Redis|array|false An array of elements or false on failure. * + * @see https://redis.io/commands/zscan + * @see https://redis.io/commands/scan + * @see Redis::scan() + * * NOTE: See Redis::scan() for detailed example code on how to call SCAN like commands. * */ @@ -5055,53 +3847,21 @@ public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int * * @return Redis|array|false The union of each sorted set or false on failure * - * - * 'localhost']); - * + * @example * $redis->del('store1', 'store2', 'store3'); * $redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas'); * $redis->zAdd('store2', 3, 'apples', 5, 'coconuts', 2, 'bananas'); * $redis->zAdd('store3', 2, 'bananas', 6, 'apples', 4, 'figs'); * - * // Array - * // ( - * // [pears] => 3 - * // [figs] => 4 - * // [coconuts] => 5 - * // [apples] => 10 - * // [bananas] => 10 - * // ) * $redis->zUnion(['store1', 'store2', 'store3'], NULL, ['withscores' => true]); - * - * // Array - * // ( - * // [figs] => 2 - * // [apples] => 5 - * // [pears] => 6 - * // [bananas] => 13 - * // ) * $redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true]); - * - * // Array - * // ( - * // [bananas] => 1 - * // [apples] => 2 - * // [figs] => 2 - * // [pears] => 6 - * // ) * $redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true, 'aggregate' => 'MIN']); - * ?> - * */ public function zunion(array $keys, ?array $weights = null, ?array $options = null): Redis|array|false; /** * Perform a union on one or more Redis sets and store the result in a destination sorted set. * - * @see https://redis.io/commands/zunionstore - * @see Redis::zunion() - * * @param string $dst The destination set to store the union. * @param array $keys One or more input keys on which to perform our union. * @param array $weights An optional weights array used to weight each input set. @@ -5110,31 +3870,15 @@ public function zunion(array $keys, ?array $weights = null, ?array $options = nu * * @return Redis|int|false The number of members stored in the destination set or false on failure. * - * - * 'localhost']); - * - * $redis->del('zs1', 'zs2', 'zs3'); + * @see https://redis.io/commands/zunionstore + * @see Redis::zunion() * + * @example * $redis->zAdd('zs1', 1, 'one', 3, 'three'); * $redis->zAdd('zs1', 2, 'two', 4, 'four'); * $redis->zadd('zs3', 1, 'one', 7, 'five'); * - * // count(['one','two','three','four','five']) == 5 * $redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']); - * - * // Array - * // ( - * // [0] => one - * // [1] => two - * // [2] => three - * // [3] => four - * // [4] => five - * // ) - * $redis->zRange('dst', 0, -1); - * ?> - * - * */ public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): Redis|int|false; } diff --git a/redis_arginfo.h b/redis_arginfo.h index fa86cdc310..d30bc9f30c 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0ca9052a6b2da623f76b015fa3271f4a9d1ffcf9 */ + * Stub hash: 897e7ef01adac7ae817e3f6678569996786cf8ed */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -487,19 +487,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, Redi ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPush, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPush, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_VARIADIC_INFO(0, elements) + ZEND_ARG_VARIADIC_TYPE_INFO(0, elements, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_Redis_rPush arginfo_class_Redis_lPush -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPushx, 0, 0, 2) - ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_lPushx arginfo_class_Redis_append -#define arginfo_class_Redis_rPushx arginfo_class_Redis_lPushx +#define arginfo_class_Redis_rPushx arginfo_class_Redis_append ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lSet, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -520,7 +517,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lrange, 0, 3, Re ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lrem, 0, 0, 2) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lrem, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") @@ -532,7 +529,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ltrim, 0, 3, Red ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mget, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mget, 0, 1, Redis, MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -547,7 +544,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_migrate, 0, 5, R ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "NULL") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_move, 0, 2, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_move, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -579,7 +576,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_pconnect arginfo_class_Redis_open -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_persist, 0, 1, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_persist, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -614,7 +611,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_popen arginfo_class_Redis_open -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psetex, 0, 0, 3) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_psetex, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, expire, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) @@ -775,7 +772,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_setOption, 0, 2, _IS ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_setex arginfo_class_Redis_psetex +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_setex, 0, 0, 3) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, expire, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setnx, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 1594edf73f..300a7ca655 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0ca9052a6b2da623f76b015fa3271f4a9d1ffcf9 */ + * Stub hash: 897e7ef01adac7ae817e3f6678569996786cf8ed */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From 0b7bd83f57b65279e05fc39569079e5fedeecc38 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 15 Nov 2022 21:23:52 -0800 Subject: [PATCH 1715/1986] Add more docblocks and fix XAUTOCLAIM response handler. - Finish adding docblocks with examples for all of the stream commands. - Fix XAUTOCLAIM response handler (the reply has a slightly different structure to XCLAIM. --- cluster_library.c | 4 +- library.c | 87 ++++++++-- library.h | 6 +- redis.stub.php | 304 ++++++++++++++++++++++++++++++++- redis_arginfo.h | 6 +- redis_cluster.c | 4 + redis_cluster.stub.php | 8 +- redis_cluster_arginfo.h | 21 ++- redis_cluster_legacy_arginfo.h | 21 ++- redis_commands.c | 41 ++--- redis_legacy_arginfo.h | 2 +- tests/RedisTest.php | 31 ++++ 12 files changed, 478 insertions(+), 57 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 945c7bc128..0d04593e41 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2333,7 +2333,9 @@ cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { array_init(&z_msg); - if (redis_read_xclaim_response(c->cmd_sock, c->reply_len, &z_msg) < 0) { + ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + + if (redis_read_xclaim_reply(c->cmd_sock, c->reply_len, ctx == PHPREDIS_CTX_PTR, &z_msg) < 0) { zval_dtor(&z_msg); CLUSTER_RETURN_FALSE(c); } diff --git a/library.c b/library.c index 1560e6b001..5130249a3d 100644 --- a/library.c +++ b/library.c @@ -1999,11 +1999,9 @@ redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return -1; } -/* This helper function does that actual XCLAIM response handling, which can be used by both - * Redis and RedisCluster. Note that XCLAIM is somewhat unique in that its reply type depends - * on whether or not it was called with the JUSTID option */ -PHP_REDIS_API int -redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv) { +/* A helper method to read X[AUTO]CLAIM messages into an array. */ +static int +redis_read_xclaim_ids(RedisSock *redis_sock, int count, zval *rv) { zval z_msg; REDIS_REPLY_TYPE type; char *id = NULL; @@ -2011,6 +2009,8 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv) { long li; for (i = 0; i < count; i++) { + id = NULL; + /* Consume inner reply type */ if (redis_read_reply_type(redis_sock, &type, &li) < 0 || (type != TYPE_BULK && type != TYPE_MULTIBULK) || @@ -2043,29 +2043,88 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv) { return 0; } +/* Read an X[AUTO]CLAIM reply having already consumed the reply-type byte. */ +PHP_REDIS_API int +redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zval *rv) { + REDIS_REPLY_TYPE type; + zval z_msgs = {0}; + char *id = NULL; + long id_len = 0; + int messages; + + ZEND_ASSERT(!is_xautoclaim || count == 3); + + ZVAL_UNDEF(rv); + + /* If this is XAUTOCLAIM consume the BULK ID and then the actual number of IDs. + * Otherwise, our 'count' argument is the number of IDs. */ + if (is_xautoclaim) { + if (redis_read_reply_type(redis_sock, &type, &id_len) < 0 || type != TYPE_BULK) + goto failure; + if ((id = redis_sock_read_bulk_reply(redis_sock, id_len)) == NULL) + goto failure; + if (read_mbulk_header(redis_sock, &messages) < 0) + goto failure; + } else { + messages = count; + } + + array_init(&z_msgs); + + if (redis_read_xclaim_ids(redis_sock, messages, &z_msgs) < 0) + goto failure; + + /* If XAUTOCLAIM we now need to consume the final array of message IDs */ + if (is_xautoclaim) { + zval z_deleted = {0}; + + if (redis_sock_read_multibulk_reply_zval(redis_sock, &z_deleted) == NULL) + goto failure; + + array_init(rv); + + // Package up ID, message, and deleted messages in our reply + add_next_index_stringl(rv, id, id_len); + add_next_index_zval(rv, &z_msgs); + add_next_index_zval(rv, &z_deleted); + + efree(id); + } else { + // We just want the messages + ZVAL_COPY_VALUE(rv, &z_msgs); + } + + return 0; + +failure: + zval_dtor(&z_msgs); + zval_dtor(rv); + if (id) efree(id); + + return -1; +} + PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - zval z_ret; - int messages; + zval z_ret = {0}; + int count; - /* All XCLAIM responses start multibulk */ - if (read_mbulk_header(redis_sock, &messages) < 0) - goto failure; + ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); - array_init(&z_ret); + if (read_mbulk_header(redis_sock, &count) < 0) + goto failure; - if (redis_read_xclaim_response(redis_sock, messages, &z_ret) < 0) { - zval_dtor(&z_ret); + if (redis_read_xclaim_reply(redis_sock, count, ctx == PHPREDIS_CTX_PTR, &z_ret) < 0) goto failure; - } if (IS_ATOMIC(redis_sock)) { RETVAL_ZVAL(&z_ret, 0, 1); } else { add_next_index_zval(z_tab, &z_ret); } + return 0; failure: diff --git a/library.h b/library.h index c52b0f2f67..4b88cc198c 100644 --- a/library.h +++ b/library.h @@ -104,8 +104,12 @@ PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); + PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_read_xclaim_reply( + RedisSock *redis_sock, int count, int is_xautoclaim, zval *rv); + PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); @@ -149,8 +153,6 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret); PHP_REDIS_API int redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_ret); PHP_REDIS_API int -redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv); -PHP_REDIS_API int redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements); PHP_REDIS_API int diff --git a/redis.stub.php b/redis.stub.php index f5af3a6d58..5af10c2987 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -2919,6 +2919,25 @@ public function ttl(string $key): Redis|int|false; * Redis::REDIS_ZSET * Redis::REDIS_HASH * Redis::REDIS_STREAM + * + * + * 'localhost']); + * + * // NOTE: Never use 'KEYS' in production! + * $keys = $redis->keys('*'); + * + * $redis->pipeline(); + * foreach ($keys as $key) { + * $redis->type($key); + * } + * + * $ktypes = array_combine($keys, $redis->exec()); + * + * // Print each key with its corresponding type + * print_r($ktypes); + * ?> + * */ public function type(string $key): Redis|int|false; @@ -2949,6 +2968,22 @@ public function unlink(array|string $key, string ...$other_keys): Redis|int|fals * @see https://redis.io/commands/unsubscribe * @see Redis::subscribe() * + * + * 'localhost']); + * + * $redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { + * if ($message == 'quit') { + * echo "$channel => 'quit' detected, unsubscribing!\n"; + * $redis->unsubscribe([$channel]); + * } else { + * echo "$channel => $message\n"; + * } + * }); + * + * echo "We've unsubscribed from both channels, exiting\n"; + * ?> + * */ public function unsubscribe(array $channels): Redis|array|bool; @@ -2964,9 +2999,48 @@ public function unsubscribe(array $channels): Redis|array|bool; public function unwatch(): Redis|bool; /** - * @return bool|Redis + * Watch one or more keys for conditional execution of a transaction. + * + * @see https://redis.io/commands/watch + * @see https://redis.io/commands/unwatch + * + * @param array|string $key_or_keys Either an array with one or more key names, or a string key name + * @param string $other_keys If the first argument was passed as a string, any number of additional + * string key names may be passed variadically. + * + * @return Redis|bool + * + * + * 'localhost']); + * $redis2 = new Redis(['host' => 'localhost']); + * + * // Start watching 'incr-key' + * $redis1->watch('incr-key'); + * + * // Retrieve its value. + * $val = $redis1->get('incr-key'); + * + * // A second client modifies 'incr-key' after we read it. + * $redis2->set('incr-key', 0); + * + * // Because another client changed the value of 'incr-key' after we read it, this + * // is no longer a proper increment operation, but because we are `WATCH`ing the + * // key, this transaction will fail and we can try again. + * // + * // If were to comment out the above `$redis2->set('incr-key', 0)` line the + * // transaction would succeed. + * $redis1->multi(); + * $redis1->set('incr-key', $val + 1); + * $res = $redis1->exec(); + * + * // bool(false) + * var_dump($res); + * + * */ - public function watch(array|string $key, string ...$other_keys); + public function watch(array|string $key, string ...$other_keys): Redis|bool; /** * Block the client up to the provided timeout until a certain number of replicas have confirmed @@ -2982,6 +3056,46 @@ public function watch(array|string $key, string ...$other_keys); */ public function wait(int $numreplicas, int $timeout): int|false; + /** + * Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but + * not yet acknowledged by XACK.) + * + * @see https://redis.io/commands/xack + * @see https://redis.io/commands/xreadgroup + * @see Redis::xack() + * + * + * 'localhost']); + * + * $redis->del('ships'); + * + * $redis->xAdd('ships', '*', ['name' => 'Enterprise']); + * $redis->xAdd('ships', '*', ['name' => 'Defiant']); + * + * $redis->xGroup('CREATE', 'ships', 'Federation', '0-0'); + * + * // Consume a single message with the consumer group 'Federation' + * $ship = $redis->xReadGroup('Federation', 'Picard', ['ships' => '>'], 1); + * + * /* Retrieve the ID of the message we read. + * assert(isset($ship['ships'])); + * $id = key($ship['ships']); + * + * // The message we just read is now pending. + * $res = $redis->xPending('ships', 'Federation')); + * var_dump($res); + * + * // We can tell Redis we were able to process the message by using XACK + * $res = $redis->xAck('ships', 'Federation', [$id]); + * assert($res === 1); + * + * // The message should no longer be pending. + * $res = $redis->xPending('ships', 'Federation'); + * var_dump($res); + * ?> + * + */ public function xack(string $key, string $group, array $ids): int|false; /** @@ -3007,9 +3121,193 @@ public function xack(string $key, string $group, array $ids): int|false; */ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): Redis|string|false; + /** + * This command allows a consumer to claim pending messages that have been idle for a specified period of time. + * Its purpose is to provide a mechanism for picking up messages that may have had a failed consumer. + * + * @see https://redis.io/commands/xautoclaim + * @see https://redis.io/commands/xclaim + * @see https://redis.io/docs/data-types/streams-tutorial/ + * + * @param string $key The stream to check. + * @param string $group The consumer group to query. + * @param string $consumer Which consumer to check. + * @param int $min_idle The minimum time in milliseconds for the message to have been pending. + * @param string $start The minimum message id to check. + * @param int $count An optional limit on how many messages are returned. + * @param bool $justid If the client only wants message IDs and not all of their data. + * + * @return Redis|array|bool An array of pending IDs or false if there are none, or on failure. + * + * + * 'localhost']); + * + * $redis->del('ships'); + * + * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); + * + * $redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); + * + * // Consume the ['name' => 'Defiant'] message + * $msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1); + * + * // The "Jem'Hadar" consumer has the message presently + * $pending = $redis->xPending('ships', 'combatants'); + * + * //array(4) { + * // [0]=> + * // int(1) + * // [1]=> + * // string(10) "1424-74205" + * // [2]=> + * // string(10) "1424-74205" + * // [3]=> + * // array(1) { + * // [0]=> + * // array(2) { + * // [0]=> + * // string(9) "Jem'Hadar" + * // [1]=> + * // string(1) "1" + * // } + * // } + * //} + * var_dump($pending); + * + * // Asssume control of the pending message with a different consumer. + * $res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); + * + * // Now the 'Sisko' consumer owns the message + * $pending = $redis->xPending('ships', 'combatants'); + * + * // array(4) { + * // [0]=> + * // int(1) + * // [1]=> + * // string(10) "1424-74205" + * // [2]=> + * // string(10) "1424-74205" + * // [3]=> + * // array(1) { + * // [0]=> + * // array(2) { + * // [0]=> + * // string(5) "Sisko" + * // [1]=> + * // string(1) "1" + * // } + * // } + * // } + * var_dump($pending); + * ?> + * + */ public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array; - public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): Redis|bool|array; + /** + * This method allows a consumer to take ownership of pending stream entries, by ID. Another + * command that does much the same thing but does not require passing specific IDs is `Redis::xAutoClaim`. + * + * @see https://redis.io/commands/xclaim + * @see https://redis.io/commands/xautoclaim. + * + * @param string $key The stream we wish to claim messages for. + * @param string $group Our consumer group. + * @param string $consumer Our consumer. + * @param int $min_idle_time The minimum idle-time in milliseconds a message must have for ownership to be transferred. + * @param array $options An options array that modifies how the command operates. + * + * + * // Following is an options array describing every option you can pass. Note that + * // 'IDLE', and 'TIME' are mutually exclusive. + * $options = [ + * 'IDLE' => 3 // Set the idle time of the message to a 3. By default the + * // idle time is set to zero. + * 'TIME' => 1000*time() // Same as IDLE except it takes a unix timestamp in milliseconds. + * 'RETRYCOUNT' => 0 // Set the retry counter to zero. By default XCLAIM doesn't modify + * // the counter. + * 'FORCE' // Creates the pending message entry even if IDs are not already + * // in the PEL with another client. + * 'JUSTID' // Return only an array of IDs rather than the messages themselves. + * ]; + * + * + * @return Redis|array|bool An array of claimed messags or false on failure. + * + * + * 'localhost']); + * + * $redis->del('ships'); + * + * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); + * + * $redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); + * + * // Consume the ['name' => 'Defiant'] message + * $msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1); + * + * // The "Jem'Hadar" consumer has the message presently + * $pending = $redis->xPending('ships', 'combatants'); + * + * //array(4) { + * // [0]=> + * // int(1) + * // [1]=> + * // string(10) "1424-74205" + * // [2]=> + * // string(10) "1424-74205" + * // [3]=> + * // array(1) { + * // [0]=> + * // array(2) { + * // [0]=> + * // string(9) "Jem'Hadar" + * // [1]=> + * // string(1) "1" + * // } + * // } + * //} + * var_dump($pending); + * + * assert($pending && isset($pending[1])); + * + * // Claim the message by ID. + * $claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']); + * + * // array(1) { + * // [0]=> + * // string(10) "1424-74205" + * // } + * var_dump($claimed); + * + * // Now the 'Sisko' consumer owns the message + * $pending = $redis->xPending('ships', 'combatants'); + * + * // array(4) { + * // [0]=> + * // int(1) + * // [1]=> + * // string(10) "1424-74205" + * // [2]=> + * // string(10) "1424-74205" + * // [3]=> + * // array(1) { + * // [0]=> + * // array(2) { + * // [0]=> + * // string(5) "Sisko" + * // [1]=> + * // string(1) "1" + * // } + * // } + * // } + * var_dump($pending); + * ?> + * + */ + public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): Redis|array|bool; /** * Remove one or more specific IDs from a stream. diff --git a/redis_arginfo.h b/redis_arginfo.h index d30bc9f30c..5c83918a5a 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 897e7ef01adac7ae817e3f6678569996786cf8ed */ + * Stub hash: 7230a9518fe0e79ae51f6b49d269053535a34199 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -860,7 +860,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_unwatch arginfo_class_Redis_bgSave -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_watch, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_watch, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -895,7 +895,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xautoclaim, 0, 5 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, justid, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, Redis, MAY_BE_BOOL|MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) diff --git a/redis_cluster.c b/redis_cluster.c index e611315d5d..66225e5cf0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2904,6 +2904,10 @@ PHP_METHOD(RedisCluster, xclaim) { CLUSTER_PROCESS_CMD(xclaim, cluster_xclaim_resp, 0); } +PHP_METHOD(RedisCluster, xautoclaim) { + CLUSTER_PROCESS_CMD(xautoclaim, cluster_xclaim_resp, 0); +} + PHP_METHOD(RedisCluster, xdel) { CLUSTER_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, cluster_long_resp, 0); } diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index a017c635fc..f8c5e51d48 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -895,7 +895,13 @@ public function xdel(string $key, array $ids): RedisCluster|int|false; /** * @see Redis::xgroup */ - public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed; + public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, + bool $mkstream = false, int $entries_read = -2): mixed; + + /** + * @see Redis::xautoclaim + */ + public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array; /** * @see Redis::xinfo diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 80907de4fc..b22eae225c 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1783d14c476f95598062edb44dab7284b9b2680d */ + * Stub hash: 2f2132e45b1d60011f8ef9298cb35b7ba2b247d5 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -773,9 +773,20 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg3, _IS_BOOL, 0, "false") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xautoclaim, 0, 5, Redis, MAY_BE_BOOL|MAY_BE_ARRAY) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, min_idle, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, justid, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 1, IS_MIXED, 0) @@ -1122,6 +1133,7 @@ ZEND_METHOD(RedisCluster, xadd); ZEND_METHOD(RedisCluster, xclaim); ZEND_METHOD(RedisCluster, xdel); ZEND_METHOD(RedisCluster, xgroup); +ZEND_METHOD(RedisCluster, xautoclaim); ZEND_METHOD(RedisCluster, xinfo); ZEND_METHOD(RedisCluster, xlen); ZEND_METHOD(RedisCluster, xpending); @@ -1331,6 +1343,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, xautoclaim, arginfo_class_RedisCluster_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xlen, arginfo_class_RedisCluster_xlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xpending, arginfo_class_RedisCluster_xpending, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 77df99ded6..99b257a3af 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1783d14c476f95598062edb44dab7284b9b2680d */ + * Stub hash: 2f2132e45b1d60011f8ef9298cb35b7ba2b247d5 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -653,9 +653,20 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, arg1) - ZEND_ARG_INFO(0, arg2) - ZEND_ARG_INFO(0, arg3) + ZEND_ARG_INFO(0, group) + ZEND_ARG_INFO(0, id_or_consumer) + ZEND_ARG_INFO(0, mkstream) + ZEND_ARG_INFO(0, entries_read) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xautoclaim, 0, 0, 5) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, group) + ZEND_ARG_INFO(0, consumer) + ZEND_ARG_INFO(0, min_idle) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, count) + ZEND_ARG_INFO(0, justid) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 0, 1) @@ -973,6 +984,7 @@ ZEND_METHOD(RedisCluster, xadd); ZEND_METHOD(RedisCluster, xclaim); ZEND_METHOD(RedisCluster, xdel); ZEND_METHOD(RedisCluster, xgroup); +ZEND_METHOD(RedisCluster, xautoclaim); ZEND_METHOD(RedisCluster, xinfo); ZEND_METHOD(RedisCluster, xlen); ZEND_METHOD(RedisCluster, xpending); @@ -1182,6 +1194,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, xautoclaim, arginfo_class_RedisCluster_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xlen, arginfo_class_RedisCluster_xlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xpending, arginfo_class_RedisCluster_xpending, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index e75b1892b8..11c5d59f71 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -5776,10 +5776,8 @@ static int64_t get_xclaim_i64_arg(const char *key, zval *zv) { /* Helper to extract XCLAIM options */ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) { - HashTable *ht; zend_string *zkey; - char *kval; - size_t klen; + HashTable *ht; zval *zv; /* Initialize options array to sane defaults */ @@ -5795,29 +5793,20 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) { ht = Z_ARRVAL_P(z_arr); ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, zv) { if (zkey) { - kval = ZSTR_VAL(zkey); - klen = ZSTR_LEN(zkey); - - if (klen == 4) { - if (!strncasecmp(kval, "TIME", 4)) { - opt->idle.type = "TIME"; - opt->idle.time = get_xclaim_i64_arg("TIME", zv); - } else if (!strncasecmp(kval, "IDLE", 4)) { - opt->idle.type = "IDLE"; - opt->idle.time = get_xclaim_i64_arg("IDLE", zv); - } - } else if (klen == 10 && !strncasecmp(kval, "RETRYCOUNT", 10)) { + if (zend_string_equals_literal_ci(zkey, "TIME")) { + opt->idle.type = "TIME"; + opt->idle.time = get_xclaim_i64_arg("TIME", zv); + } else if (zend_string_equals_literal_ci(zkey, "IDLE")) { + opt->idle.type = "IDLE"; + opt->idle.time = get_xclaim_i64_arg("IDLE", zv); + } else if (zend_string_equals_literal_ci(zkey, "RETRYCOUNT")) { opt->retrycount = zval_get_long(zv); } - } else { - if (Z_TYPE_P(zv) == IS_STRING) { - kval = Z_STRVAL_P(zv); - klen = Z_STRLEN_P(zv); - if (klen == 5 && !strncasecmp(kval, "FORCE", 5)) { - opt->force = 1; - } else if (klen == 6 && !strncasecmp(kval, "JUSTID", 6)) { - opt->justid = 1; - } + } else if (Z_TYPE_P(zv) == IS_STRING) { + if (zend_string_equals_literal_ci(Z_STR_P(zv), "FORCE")) { + opt->force = 1; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "JUSTID")) { + opt->justid = 1; } } } ZEND_HASH_FOREACH_END(); @@ -5898,6 +5887,10 @@ redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "JUSTID"); } + // Set the context to distinguish XCLAIM from XAUTOCLAIM which + // have slightly different reply structures. + *ctx = PHPREDIS_CTX_PTR; + *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 300a7ca655..7b06571fb6 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 897e7ef01adac7ae817e3f6678569996786cf8ed */ + * Stub hash: 7230a9518fe0e79ae51f6b49d269053535a34199 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 606c4e738f..167346d5d9 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -6821,6 +6821,7 @@ public function testXPending() { 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 */ @@ -6975,6 +6976,36 @@ public function testXClaim() { } } + /* Make sure our XAUTOCLAIM handler works */ + public function testXAutoClaim() { + $this->redis->del('ships'); + $this->redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); + + // Test an empty xautoclaim reply + $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); + $this->assertEquals(['0-0', [], []], $res); + + $this->redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); + + // Consume the ['name' => 'Defiant'] message + $this->redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1); + + // The "Jem'Hadar" consumer has the message presently + $pending = $this->redis->xPending('ships', 'combatants'); + $this->assertTrue($pending && isset($pending[3][0][0]) && $pending[3][0][0] == "Jem'Hadar"); + + // Asssume control of the pending message with a different consumer. + $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); + + $this->assertTrue($res && count($res) == 3 && $res[0] == '0-0' && + isset($res[1]['1424-74205']['name']) && + $res[1]['1424-74205']['name'] == 'Defiant'); + + // Now the 'Sisko' consumer should own the message + $pending = $this->redis->xPending('ships', 'combatants'); + $this->assertTrue(isset($pending[3][0][0]) && $pending[3][0][0] == 'Sisko'); + } + public function testXInfo() { if (!$this->minVersionCheck("5.0")) From 72f8eb256517e990a2d1430a568ca5e8924d3f02 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 16 Nov 2022 12:13:50 -0800 Subject: [PATCH 1716/1986] Documentation: Rebuild docs. --- docs/PROJECT_VERSION | 2 +- docs/Redis.html | 5307 ++++++++++++++++--------------- docs/RedisArray.html | 66 +- docs/RedisCluster.html | 526 +-- docs/RedisClusterException.html | 4 +- docs/RedisException.html | 4 +- docs/RedisSentinel.html | 28 +- docs/[Global_Namespace].html | 2 +- docs/classes.html | 10 + docs/doc-index.html | 594 +++- docs/doctum-search.json | 2 +- docs/doctum.js | 2 +- docs/index.html | 10 + docs/renderer.index | 2 +- docs/traits.html | 2 +- 15 files changed, 3755 insertions(+), 2806 deletions(-) diff --git a/docs/PROJECT_VERSION b/docs/PROJECT_VERSION index ce57f64563..88d050b190 100644 --- a/docs/PROJECT_VERSION +++ b/docs/PROJECT_VERSION @@ -1 +1 @@ -develop \ No newline at end of file +main \ No newline at end of file diff --git a/docs/Redis.html b/docs/Redis.html index 575df3d272..24190f0131 100644 --- a/docs/Redis.html +++ b/docs/Redis.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -83,7 +83,7 @@

    Redis

    class - Redis (View source) + Redis (View source)

    @@ -684,8 +684,7 @@

    Methods

    geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) -

    No description

    -
    +

    Add one or more members to a geospacial sorted set

    @@ -695,8 +694,7 @@

    Methods

    geodist(string $key, string $src, string $dst, string|null $unit = null) -

    No description

    -
    +

    Get the distance between two members of a geospacially encoded sorted set.

    @@ -706,8 +704,7 @@

    Methods

    geohash(string $key, string $member, string ...$other_members) -

    No description

    -
    +

    Retrieve one or more GeoHash encoded strings for members of the set.

    @@ -717,8 +714,7 @@

    Methods

    geopos(string $key, string $member, string ...$other_members) -

    No description

    -
    +

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    @@ -728,8 +724,7 @@

    Methods

    georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) -

    No description

    -
    +

    Retrieve members of a geospacially sorted set that are within a certain radius of a location.

    @@ -739,8 +734,7 @@

    Methods

    georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) -

    No description

    -
    +

    A readonly variant of GEORADIUS that may be executed on replicas.

    @@ -750,8 +744,7 @@

    Methods

    georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) -

    No description

    -
    +

    Similar to GEORADIUS except it uses a member as the center of the query.

    @@ -761,8 +754,7 @@

    Methods

    georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) -

    No description

    -
    +

    This is the read-only variant of GEORADIUSBYMEMBER that can be run on replicas.

    @@ -772,8 +764,7 @@

    Methods

    geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) -

    No description

    -
    +

    Search a geospacial sorted set for members in various ways.

    @@ -783,8 +774,8 @@

    Methods

    geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) -

    No description

    -
    +

    Search a geospacial sorted set for members within a given area or range, storing the results into +a new set.

    @@ -794,8 +785,7 @@

    Methods

    get(string $key) -

    No description

    -
    +

    Retrieve a string keys value.

    @@ -815,8 +805,7 @@

    Methods

    getBit(string $key, int $idx) -

    No description

    -
    +

    Get the bit at a given index in a string key.

    @@ -826,8 +815,7 @@

    Methods

    getEx(string $key, array $options = []) -

    No description

    -
    +

    Get the value of a key and optionally set it's expiration.

    @@ -847,8 +835,7 @@

    Methods

    getDel(string $key) -

    No description

    -
    +

    Get a key from Redis and delete it in an atomic operation.

    @@ -1216,8 +1203,7 @@

    Methods

    lLen(string $key) -

    No description

    -
    +

    Retrieve the lenght of a list.

    @@ -1227,8 +1213,7 @@

    Methods

    lMove(string $src, string $dst, string $wherefrom, string $whereto) -

    No description

    -
    +

    Move an element from one list into another.

    @@ -1238,8 +1223,7 @@

    Methods

    lPop(string $key, int $count = 0) -

    No description

    -
    +

    Pop one or more elements off a list.

    @@ -1249,52 +1233,47 @@

    Methods

    lPos(string $key, mixed $value, array $options = null) -

    No description

    -
    +

    Retrieve the index of an element in a list.

    - int|Redis + Redis|int|false
    lPush(string $key, mixed ...$elements) -

    No description

    -
    +

    Prepend one or more elements to a list.

    - Redis|int|false + Redis|int|false
    rPush(string $key, mixed ...$elements) -

    No description

    -
    +

    Append one or more elements to a list.

    - Redis|int|false + Redis|int|false
    lPushx(string $key, mixed $value) -

    No description

    -
    +

    Prepend an element to a list but only if the list exists

    - Redis|int|false + Redis|int|false
    rPushx(string $key, mixed $value) -

    No description

    -
    +

    Append an element to a list but only if the list exists

    @@ -1304,8 +1283,7 @@

    Methods

    lSet(string $key, int $index, mixed $value) -

    No description

    -
    +

    Set a list element at an index to a specific value.

    @@ -1315,8 +1293,7 @@

    Methods

    lastSave() -

    No description

    -
    +

    Retrieve the last time Redis' database was persisted to disk.

    @@ -1326,8 +1303,7 @@

    Methods

    lindex(string $key, int $index) -

    No description

    -
    +

    Get the element of a list by its index.

    @@ -1337,19 +1313,17 @@

    Methods

    lrange(string $key, int $start, int $end) -

    No description

    -
    +

    Retrieve elements from a list.

    - int|Redis|false + Redis|int|false
    lrem(string $key, mixed $value, int $count = 0) -

    No description

    -
    +

    Remove one or more matching elements from a list.

    @@ -1359,19 +1333,17 @@

    Methods

    ltrim(string $key, int $start, int $end) -

    No description

    -
    +

    Trim a list to a subrange of elements.

    - array|Redis + Redis|array
    mget(array $keys) -

    No description

    -
    +

    Get one ore more string keys.

    @@ -1387,13 +1359,12 @@

    Methods

    - bool + Redis|bool
    move(string $key, int $index) -

    No description

    -
    +

    Move a key to a different database on the same redis instance.

    @@ -1403,8 +1374,7 @@

    Methods

    mset(array $key_values) -

    No description

    -
    +

    Set one ore more string keys.

    @@ -1414,8 +1384,7 @@

    Methods

    msetnx(array $key_values) -

    No description

    -
    +

    Set one ore more string keys but only if none of the key exist.

    @@ -1425,8 +1394,7 @@

    Methods

    multi(int $value = Redis::MULTI) -

    No description

    -
    +

    Begin a transaction.

    @@ -1464,13 +1432,12 @@

    Methods

    - bool + Redis|bool
    persist(string $key) -

    No description

    -
    +

    Remove the expiration from a key.

    @@ -1558,13 +1525,12 @@

    Methods

    - bool|Redis + Redis|bool
    psetex(string $key, int $expire, mixed $value) -

    No description

    -
    +

    Set a key with an expiration time in milliseconds

    @@ -2198,13 +2164,12 @@

    Methods

    - bool|Redis + Redis|bool
    watch(array|string $key, string ...$other_keys) -

    No description

    -
    +

    Watch one or more keys for conditional execution of a transaction.

    @@ -2225,8 +2190,8 @@

    Methods

    xack(string $key, string $group, array $ids) -

    No description

    -
    +

    Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but +not yet acknowledged by XACK.)

    @@ -2246,19 +2211,18 @@

    Methods

    xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) -

    No description

    -
    +

    This command allows a consumer to claim pending messages that have been idle for a specified period of time.

    - Redis|bool|array + Redis|array|bool
    xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) -

    No description

    -
    +

    This method allows a consumer to take ownership of pending stream entries, by ID. Another +command that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

    @@ -2684,7 +2648,7 @@

    Details

    - + Redis __construct(array $options = null) @@ -2785,7 +2749,7 @@

    See also

    - + __destruct() @@ -2809,7 +2773,7 @@

    - + string _compress(string $value) @@ -2864,7 +2828,7 @@

    See also

    - + string _uncompress(string $value) @@ -2919,7 +2883,7 @@

    See also

    - + string _prefix(string $key) @@ -2962,7 +2926,7 @@

    Return Value

    - + string _serialize(mixed $value) @@ -3017,7 +2981,7 @@

    See also

    - + mixed _unserialize(string $value) @@ -3072,7 +3036,7 @@

    See also

    - + string _pack(mixed $value) @@ -3116,7 +3080,7 @@

    Return Value

    - + mixed _unpack(string $value) @@ -3159,7 +3123,7 @@

    Return Value

    - + mixed acl(string $subcmd, string ...$args) @@ -3207,7 +3171,7 @@

    Return Value

    - + Redis|int|false append(string $key, mixed $value) @@ -3217,9 +3181,7 @@

    -

    Append data to a Redis STRING key.

    $redis = new Redis(['host' => 'localhost']);
    -$redis->set('foo', 'hello);
    -var_dump($redis->append('foo', 'world'));

    +

    Append data to a Redis STRING key.

    Parameters

    @@ -3249,14 +3211,34 @@

    Return Value

    +

    See also

    + + + + + + +
    + https://redis.io/commands/append +
    + +

    Examples

    + + + + + +
    $redis->set('foo', 'hello);
    +$redis->append('foo', 'world');
    +

    - + Redis|bool auth(mixed $credentials) @@ -3311,7 +3293,7 @@

    See also

    - + Redis|bool bgSave() @@ -3354,7 +3336,7 @@

    See also

    - + Redis|bool bgrewriteaof() @@ -3397,7 +3379,7 @@

    See also

    - + Redis|int|false bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) @@ -3468,7 +3450,7 @@

    See also

    - + Redis|int|false bitop(string $operation, string $deskey, string $srckey, string ...$other_keys) @@ -3526,7 +3508,7 @@

    Return Value

    - + Redis|int|false bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) @@ -3600,7 +3582,7 @@

    See also

    - + Redis|array|null|false blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) @@ -3676,7 +3658,7 @@

    Examples

    - + Redis|array|null|false brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) @@ -3746,7 +3728,7 @@

    See also

    - + Redis|string|false brpoplpush(string $src, string $dst, int|float $timeout) @@ -3811,7 +3793,7 @@

    See also

    - + Redis|array|false bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3889,7 +3871,7 @@

    Examples

    - + Redis|array|false bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3960,7 +3942,7 @@

    See also

    - + Redis|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -4023,7 +4005,7 @@

    Return Value

    - + Redis|array|null|false zmpop(array $keys, string $from, int $count = 1) @@ -4087,7 +4069,7 @@

    See also

    - + Redis|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -4158,7 +4140,7 @@

    See also

    - + Redis|array|null|false lmpop(array $keys, string $from, int $count = 1) @@ -4223,7 +4205,7 @@

    See also

    - + bool clearLastError() @@ -4280,7 +4262,7 @@

    Examples

    - + mixed client(string $opt, mixed ...$args) @@ -4328,7 +4310,7 @@

    Return Value

    - + bool close() @@ -4361,7 +4343,7 @@

    Return Value

    - + mixed command(string $opt = null, string|array $arg) @@ -4409,7 +4391,7 @@

    Return Value

    - + mixed config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) @@ -4484,7 +4466,7 @@

    Examples

    - + bool connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null) @@ -4557,7 +4539,7 @@

    Return Value

    - + Redis|bool copy(string $src, string $dst, array $options = null) @@ -4643,7 +4625,7 @@

    Examples

    - + Redis|int|false dbSize() @@ -4699,7 +4681,7 @@

    Examples

    - + Redis|string debug(string $key) @@ -4742,7 +4724,7 @@

    Return Value

    - + Redis|int|false decr(string $key, int $by = 1) @@ -4752,12 +4734,7 @@

    -

    Decrement a Redis integer by 1 or a provided value.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->set('counter', 3);
    -
    -var_dump($redis->decr('counter'));
    -var_dump($redis->decr('counter', 2));

    +

    Decrement a Redis integer by 1 or a provided value.

    Parameters

    @@ -4808,13 +4785,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->decr('counter');
    $redis->decr('counter', 2);
    +

    - + Redis|int|false decrBy(string $key, int $value) @@ -4824,11 +4812,7 @@

    -

    Decrement a redis integer by a value

    $redis = new Redis(['host' => 'localhost');
    -
    -$redis->set('counter', 3);
    -var_dump($redis->decrby('counter', 1));
    -var_dump($redis->decrby('counter', 2));

    +

    Decrement a redis integer by a value

    Parameters

    @@ -4870,13 +4854,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->decrby('counter', 1);
    $redis->decrby('counter', 2);
    +

    - + Redis|int|false del(array|string $key, string ...$other_keys) @@ -4888,15 +4883,7 @@

    Delete one or more keys from Redis.

    This method can be called in two distinct ways. The first is to pass a single array of keys to delete, and the second is to pass N arguments, all names of keys. See -below for an example of both strategies.

    -
    $redis = new Redis(['host' => 'localhost']);
    -
    -for ($i = 0; $i < 5; $i++) {
    -    $redis->set("key:$i", "val:$i");
    -}
    -
    -var_dump($redis->del('key:0', 'key:1'));
    -var_dump($redis->del(['key:2', 'key:3', 'key:4']));

    +below for an example of both strategies.

    Parameters

    @@ -4938,13 +4925,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->del('key:0', 'key:1');
    $redis->del(['key:2', 'key:3', 'key:4']);
    +

    - + Redis|int|false delete(array|string $key, string ...$other_keys) deprecated @@ -4999,7 +4997,7 @@

    Return Value

    - + Redis|bool discard() @@ -5009,18 +5007,7 @@

    -

    Discard a transaction currently in progress.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->multi()->set('foo', 'bar')->get('foo');
    -
    -// Redis::MULTI
    -$redis->getMode();
    -
    -// Discard the in-progress transaction
    -$redis->discard();
    -
    -// Redis::ATOMIC
    -$redis->getMode();

    +

    Discard a transaction currently in progress.

    @@ -5036,13 +5023,24 @@

    Return Value

    +

    Examples

    + + + + + +
    $redis->getMode();
    +$redis->set('foo', 'bar');
    +$redis->discard();
    +$redis->getMode();
    +

    - + Redis|string dump(string $key) @@ -5052,19 +5050,8 @@

    -

    Dump Redis' internal binary representation of a key.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zset');
    -
    -$redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two');
    -
    -// Retrieve the binary representation of the zset
    -$binary = $redis->dump('zset');
    -
    -// Retore it to a different name
    -$redis->restore('new-zset', 0, $binary);
    -
    -$redis->zRange('new-zset', 0, -1, true);

    +

    Dump Redis' internal binary representation of a key.

    $redis->zRange('new-zset', 0, -1, true);

    +

    Parameters

    @@ -5101,13 +5088,23 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two');
    +$binary = $redis->dump('zset');
    +$redis->restore('new-zset', 0, $binary);
    +

    - + Redis|string|false echo(string $str) @@ -5117,9 +5114,7 @@

    -

    Have Redis repeat back an arbitrary string to the client.

    $redis = new Redis(['host' => 'localhost']);
    -
    -var_dump($redis->echo('Hello, World'));

    +

    Have Redis repeat back an arbitrary string to the client.

    Parameters

    @@ -5156,13 +5151,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->echo('Hello, World');
    +

    - + mixed eval(string $script, array $args = [], int $num_keys = 0) @@ -5228,7 +5231,7 @@

    See also

    - + mixed eval_ro(string $script_sha, array $args = [], int $num_keys = 0) @@ -5279,8 +5282,8 @@

    See also

    @@ -5293,7 +5296,7 @@

    See also

    - + mixed evalsha(string $sha1, array $args = [], int $num_keys = 0) @@ -5364,7 +5367,7 @@

    See also

    - + mixed evalsha_ro(string $sha1, array $args = [], int $num_keys = 0) @@ -5429,7 +5432,7 @@

    See also

    - + Redis|array|false exec() @@ -5439,16 +5442,7 @@

    -

    Execute either a MULTI or PIPELINE block and return the array of replies.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$res = $redis->multi()
    -             ->set('foo', 'bar')
    -             ->get('foo')
    -             ->del('list')
    -             ->rpush('list', 'one', 'two', 'three')
    -             ->exec();
    -
    -var_dump($res);

    +

    Execute either a MULTI or PIPELINE block and return the array of replies.

    @@ -5495,13 +5489,26 @@

    See also

    - -Redis::eval + +Redis::eval_ro
    +

    Examples

    + + + + + +
    $res = $redis->multi()
    +->set('foo', 'bar')
    +->get('foo')
    +->del('list')
    +->rpush('list', 'one', 'two', 'three')
    +->exec();
    +

    - + Redis|int|bool exists(mixed $key, mixed ...$other_keys) @@ -5511,21 +5518,7 @@

    -

    Test if one or more keys exist.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->multi()
    -      ->mset(['k1' => 'v1', 'k2' => 'v2', 'k3' => 'v3', 'k4' => 'v4'])
    -      ->exec();
    -
    -// Using a single array of keys
    -var_dump($redis->exists(['k1', 'k2', 'k3']));
    -
    -// Calling via variadic arguments
    -var_dump($redis->exists('k4', 'k5', 'notakey'));
    -
    -// --- OUTPUT ---
    -// int(3)
    -// int(1)

    +

    Test if one or more keys exist.

    Parameters

    @@ -5568,13 +5561,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->exists(['k1', 'k2', 'k3']);
    $redis->exists('k4', 'k5', 'notakey');
    +

    - + Redis|bool expire(string $key, int $timeout, string|null $mode = NULL) @@ -5644,7 +5648,7 @@

    See also

    - + Redis|bool expireAt(string $key, int $timestamp, string|null $mode = NULL) @@ -5720,7 +5724,7 @@

    See also

    - + Redis|bool failover(array|null $to = null, bool $abort = false, int $timeout = 0) @@ -5773,7 +5777,7 @@

    Return Value

    - + Redis|int|false expiretime(string $key) @@ -5783,14 +5787,7 @@

    -

    Get the expiration of a given key as a unix timestamp

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->set('expiry-key', 'this will last a very long time');
    -
    -// Expire this key at 2222/02/22 02:22:22 GMT
    -$redis->expireAt('expiry-key', 7955144542);
    -
    -var_dump($redis->expiretime('expiry-key'));

    +

    Get the expiration of a given key as a unix timestamp

    Parameters

    @@ -5828,13 +5825,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->setEx('mykey', 60, 'myval');
    +$redis->expiretime('mykey');
    +

    - + Redis|int|false pexpiretime(string $key) @@ -5895,7 +5901,7 @@

    See also

    - + Redis|bool flushAll(bool|null $sync = null) @@ -5948,7 +5954,7 @@

    See also

    - + Redis|bool flushDB(bool|null $sync = null) @@ -5988,7 +5994,7 @@

    See also

    @@ -6001,7 +6007,7 @@

    See also

    - + Redis|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) @@ -6011,8 +6017,7 @@

    -

    No description

    - +

    Add one or more members to a geospacial sorted set

    Parameters

    @@ -6021,17 +6026,17 @@

    Parameters

    - + - + - + @@ -6041,7 +6046,9 @@

    Parameters

    - +
    - https://redis.io/commands/flush + https://redis.io/commands/flushdb
    string $key

    The sorted set to add data to.

    float $lng

    The longitude of the first member

    float $lat

    The lattitude of the first member.

    string
    mixed ...$other_triples_and_options

    You can continue to pass longitude, lattitude, and member +arguments to add as many members as you wish. Optionally, the final argument may be +a string with options for the command Redis documentation for the options.

    @@ -6051,20 +6058,43 @@

    Return Value

    - +
    Redis|int|false

    The number of added elements is returned. If the 'CH' option is specified, +the return value is the number of members changed.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/geoadd +
    + +

    Examples

    + + + + + + + + +
    $redis->geoAdd('cities', -121.8374, 39.7284, 'Chico', -122.03218, 37.322, 'Cupertino');
    $redis->geoadd('cities', -121.837478, 39.728494, 'Chico', ['XX', 'CH']);
    +

    - + Redis|float|false geodist(string $key, string $src, string $dst, string|null $unit = null) @@ -6074,8 +6104,7 @@

    -

    No description

    - +

    Get the distance between two members of a geospacially encoded sorted set.

    Parameters

    @@ -6084,22 +6113,26 @@

    Parameters

    string $key - +

    The Sorted set to query.

    string $src - +

    The first member.

    string $dst - +

    The second member.

    string|null $unit - +

    Which unit to use when computing distance, defaulting to meters.

    +
    M  - meters
    +KM - kilometers
    +FT - feet
    +MI - miles
    @@ -6109,31 +6142,50 @@

    Return Value

    - +
    Redis|float|false

    The calculated distance in whichever units were specified or false +if one or both members did not exist.

    - -
    -

    +

    See also

    - -
    -

    - - Redis|array|false - geohash(string $key, string $member, string ...$other_members) - -

    -
    + + + + + +
    + https://redis.io/commands/geodist +
    + + +

    Examples

    + + + + + +
    $redis->geodist('cities', 'Chico', 'Cupertino', 'mi');
    + +
    +
    + + +
    +

    + + Redis|array|false + geohash(string $key, string $member, string ...$other_members) + +

    +
    -

    No description

    - +

    Retrieve one or more GeoHash encoded strings for members of the set.

    Parameters

    @@ -6142,17 +6194,17 @@

    Parameters

    string $key - +

    The key to query

    string $member - +

    The first member to request

    string ...$other_members - +

    One or more additional members to request.

    @@ -6162,20 +6214,45 @@

    Return Value

    - +
    Redis|array|false

    An array of GeoHash encoded values.

    +

    See also

    + + + + + + + + + + +
    + https://redis.io/commands/geohash +
    + https://en.wikipedia.org/wiki/Geohash +
    + +

    Examples

    + + + + + +
    $redis->geohash('cities', 'Chico', 'Cupertino');
    +

    - + Redis|array|false geopos(string $key, string $member, string ...$other_members) @@ -6185,8 +6262,7 @@

    -

    No description

    - +

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    Parameters

    @@ -6195,17 +6271,17 @@

    Parameters

    string $key - +

    The set to query.

    string $member - +

    The first member to query.

    string ...$other_members - +

    One or more members to query.

    @@ -6215,20 +6291,39 @@

    Return Value

    - +
    Redis|array|false

    array of longitude and lattitude pairs.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/geopos +
    + +

    Examples

    + + + + + +
    $redis->geopos('cities', 'Seattle', 'New York');
    +

    - + mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -6238,8 +6333,7 @@

    -

    No description

    - +

    Retrieve members of a geospacially sorted set that are within a certain radius of a location.

    Parameters

    @@ -6248,32 +6342,49 @@

    Parameters

    string $key - +

    The set to query

    float $lng - +

    The longitude of the location to query.

    float $lat - +

    The latitude of the location to query.

    float $radius - +

    The radius of the area to include.

    string $unit - +

    The unit of the provided radius (defaults to 'meters). +See Redis::geodist for possible units.

    array $options - +

    An array of options that modifies how the command behaves.

    +
    $options = [
    +    'WITHCOORD',     // Return members and their coordinates.
    +    'WITHDIST',      // Return members and their distances from the center.
    +    'WITHHASH',      // Return members GeoHash string.
    +    'ASC' | 'DESC',  // The sort order of returned members
    +
    +    // Limit to N returned members.  Optionally a two element array may be
    +    // passed as the `LIMIT` argument, and the `ANY` argument.
    +    'COUNT' => [<int>], or [<int>, <bool>]
    +
    +    // Instead of returning members, store them in the specified key.
    +    'STORE' => <string>
    +
    +    // Store the distances in the specified key
    +    'STOREDIST' => <string>
    +];
    @@ -6283,20 +6394,39 @@

    Return Value

    - +
    mixed

    This command can return various things, depending on the options passed.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/georadius +
    + +

    Examples

    + + + + + +
    $redis->georadius('cities', 47.608013, -122.335167, 1000, 'km');
    +

    - + mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -6306,8 +6436,7 @@

    -

    No description

    - +

    A readonly variant of GEORADIUS that may be executed on replicas.

    Parameters

    @@ -6357,6 +6486,17 @@

    Return Value

    +

    See also

    + + + + + + +
    + Redis::georadius +
    +

    @@ -6364,7 +6504,7 @@

    Return Value

    - + mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6374,8 +6514,7 @@

    -

    No description

    - +

    Similar to GEORADIUS except it uses a member as the center of the query.

    Parameters

    @@ -6384,27 +6523,29 @@

    Parameters

    string $key - +

    The key to query.

    string $member - +

    The member to treat as the center of the query.

    float $radius - +

    The radius from the member to include.

    string $unit - +

    The unit of the provided radius +See Redis::geodist for possible units.

    array $options - +

    An array with various options to modify the command's behavior. +See Redis::georadius for options.

    @@ -6414,20 +6555,28 @@

    Return Value

    - +
    mixed

    This command can return various things depending on options.

    +

    Examples

    + + + + + +
    $redis->georadiusbymember('cities', 'Seattle', 200, 'mi');
    +

    - + mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6437,8 +6586,7 @@

    -

    No description

    - +

    This is the read-only variant of GEORADIUSBYMEMBER that can be run on replicas.

    Parameters

    @@ -6490,7 +6638,7 @@

    Return Value

    - + array geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) @@ -6500,8 +6648,7 @@

    -

    No description

    - +

    Search a geospacial sorted set for members in various ways.

    Parameters

    @@ -6510,27 +6657,31 @@

    Parameters

    string $key - +

    The set to query.

    array|string $position - +

    Either a two element array with longitude and lattitude, or +a string representing a member of the set.

    array|int|float $shape - +

    Either a number representine the radius of a circle to search, or +a two element array representing the width and height of a box +to search.

    string $unit - +

    The unit of our shape. See Redis::geodist for possible units.

    array $options - +

    Redis::georadius for options. Note that the STORE +options are not allowed for this command.

    @@ -6553,7 +6704,7 @@

    Return Value

    - + Redis|array|int|false geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) @@ -6563,8 +6714,8 @@

    -

    No description

    - +

    Search a geospacial sorted set for members within a given area or range, storing the results into +a new set.

    Parameters

    @@ -6573,32 +6724,42 @@

    Parameters

    string $dst - +

    The destination where results will be stored.

    string $src - +

    The key to query.

    array|string $position - +

    Either a two element array with longitude and lattitude, or +a string representing a member of the set.

    array|int|float $shape - +

    Either a number representine the radius of a circle to search, or +a two element array representing the width and height of a box +to search.

    string $unit - +

    The unit of our shape. See Redis::geodist for possible units.

    array $options - +
    $options = [
    +    'ASC' | 'DESC',  // The sort order of returned members
    +    'WITHDIST'       // Also store distances.
    +
    +    // Limit to N returned members.  Optionally a two element array may be
    +    // passed as the `LIMIT` argument, and the `ANY` argument.
    +    'COUNT' => [<int>], or [<int>, <bool>]
    +];
    @@ -6621,7 +6782,7 @@

    Return Value

    - + mixed get(string $key) @@ -6631,8 +6792,7 @@

    -

    No description

    - +

    Retrieve a string keys value.

    Parameters

    @@ -6641,7 +6801,7 @@

    Parameters

    string $key - +

    The key to query

    @@ -6651,20 +6811,39 @@

    Return Value

    - +
    mixed

    The keys value or false if it did not exist.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/get +
    + +

    Examples

    + + + + + +
    $redis->get('foo');
    +

    - + mixed getAuth() @@ -6708,7 +6887,7 @@

    See also

    - + Redis|int|false getBit(string $key, int $idx) @@ -6718,8 +6897,7 @@

    -

    No description

    - +

    Get the bit at a given index in a string key.

    Parameters

    @@ -6728,12 +6906,12 @@

    Parameters

    string $key - +

    The key to query.

    int $idx - +

    The Nth bit that we want to query.

    @@ -6749,14 +6927,33 @@

    Return Value

    +

    See also

    + + + + + + +
    + https://redis.io/commands/getbit +
    + +

    Examples

    + + + + + +
    $redis->getbit('bitmap', 1337);
    +

    - + Redis|string|bool getEx(string $key, array $options = []) @@ -6766,8 +6963,7 @@

    -

    No description

    - +

    Get the value of a key and optionally set it's expiration.

    Parameters

    @@ -6776,12 +6972,19 @@

    Parameters

    string $key - +

    The key to query

    array $options - +

    Options to modify how the command works.

    +
    $options = [
    +    'EX'     => <seconds>      // Expire in N seconds
    +    'PX'     => <milliseconds> // Expire in N milliseconds
    +    'EXAT'   => <timestamp>    // Expire at a unix timestamp (in seconds)
    +    'PXAT'   => <mstimestamp>  // Expire at a unix timestamp (in milliseconds);
    +    'PERSIST'                  // Remove any configured expiration on the key.
    +];
    @@ -6791,20 +6994,39 @@

    Return Value

    - +
    Redis|string|bool

    The key's value or false if it didn't exist.

    +

    See also

    + + + + + + +
    + https://redis.io/comands/getex +
    + +

    Examples

    + + + + + +
    $redis->getEx('mykey', ['EX' => 60]);
    +

    - + int getDBNum() @@ -6854,7 +7076,7 @@

    See also

    - + Redis|string|bool getDel(string $key) @@ -6864,8 +7086,7 @@

    -

    No description

    - +

    Get a key from Redis and delete it in an atomic operation.

    Parameters

    @@ -6874,7 +7095,7 @@

    Parameters

    string $key - +

    The key to get/delete.

    @@ -6884,20 +7105,39 @@

    Return Value

    - +
    Redis|string|bool

    The value of the key or false if it didn't exist.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/getdel +
    + +

    Examples

    + + + + + +
    $redis->getdel('token:123');
    +

    - + string getHost() @@ -6929,7 +7169,7 @@

    Return Value

    - + string|null getLastError() @@ -6961,7 +7201,7 @@

    Return Value

    - + int getMode() @@ -6993,7 +7233,7 @@

    Return Value

    - + mixed getOption(int $option) @@ -7047,7 +7287,7 @@

    See also

    - + string|null getPersistentID() @@ -7079,7 +7319,7 @@

    Return Value

    - + int getPort() @@ -7111,7 +7351,7 @@

    Return Value

    - + Redis|string|false getRange(string $key, int $start, int $end) @@ -7121,16 +7361,7 @@

    -

    Retrieve a substring of a string by index.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$word = 'Supercalifragilisticexpialidocious';
    -$redis->set('silly-word', $word);
    -
    -// string "super"
    -var_dump($redis->getRange('silly-word', 0, 4));
    -
    -// string(7) "docious"
    -var_dump($redis->getRange('silly-word', -7, -1));

    +

    Retrieve a substring of a string by index.

    Parameters

    @@ -7177,13 +7408,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->set('silly-word', 'Supercalifragilisticexpialidocious');
    +echo $redis->getRange('silly-word', 0, 4) . "\n";
    +

    - + Redis|string|array|int|false lcs(string $key1, string $key2, array|null $options = NULL) @@ -7193,13 +7433,7 @@

    -

    Get the longest common subsequence between two string keys.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc');
    -$redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc');
    -
    -// string(37) "acccgcacggcaagtcgttccagcaactggcgctagc"
    -var_dump($redis->lcs('seq1', 'seq2'));

    +

    Get the longest common subsequence between two string keys.

    Parameters

    @@ -7257,13 +7491,23 @@

    See also

    +

    Examples

    + + + + + +
    $redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc');
    +$redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc');
    +echo $redis->lcs('seq1', 'seq2') . "\n";
    +

    - + float getReadTimeout() @@ -7295,7 +7539,7 @@

    Return Value

    - + Redis|string|false getset(string $key, mixed $value) @@ -7305,15 +7549,7 @@

    -

    Sets a key and returns any previously set value, if the key already existed.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('captain');
    -
    -// bool(false)
    -var_dump($redis->getset('captain', 'Pike'));
    -
    -// string(4) "Pike"
    -var_dump($redis->getset('captain', 'Kirk'));

    +

    Sets a key and returns any previously set value, if the key already existed.

    Parameters

    @@ -7355,13 +7591,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->getset('captain', 'Pike');
    +$redis->getset('captain', 'Kirk');
    +

    - + float|false getTimeout() @@ -7393,7 +7638,7 @@

    Return Value

    - + int|false getTransferredBytes() @@ -7426,7 +7671,7 @@

    Return Value

    - + Redis|int|false hDel(string $key, string $field, string ...$other_fields) @@ -7436,14 +7681,7 @@

    -

    Remove one or more fields from a hash.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('people');
    -
    -$redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
    -
    -// int(1)
    -$redis->hDel('comms', 'Mallory', 'Archibald');

    +

    Remove one or more fields from a hash.

    Parameters

    @@ -7490,13 +7728,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hDel('communication', 'Alice', 'Bob');
    +

    - + Redis|bool hExists(string $key, string $field) @@ -7506,14 +7752,7 @@

    -

    Checks whether a field exists in a hash.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('captains');
    -
    -$redis->hmset('captains', ['Kirk' => 'Enterprise', 'Picard' => 'Enterprise-D', 'Sisko' => 'Defiant']);
    -
    -$redis->hExists('captains', 'Pike');
    -$redis->hExists('captains', 'Picard');

    +

    Checks whether a field exists in a hash.

    Parameters

    @@ -7555,13 +7794,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hExists('communication', 'Alice');
    +

    - + mixed hGet(string $key, string $member) @@ -7609,7 +7856,7 @@

    Return Value

    - + Redis|array|false hGetAll(string $key) @@ -7619,21 +7866,7 @@

    -

    Read every field and value from a hash.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('comms');
    -
    -$redis->hmset('comms', ['Alice' => 'ecc', 'Bob' => 'rsa', 'Mallory' => 'haxx00r']);
    -
    -// array(3) {
    -//   ["Alice"]=>
    -//   string(3) "ecc"
    -//   ["Bob"]=>
    -//   string(3) "rsa"
    -//   ["Mallory"]=>
    -//   string(7) "haxx00r"
    -// }
    -$redis->hGetAll('comms');

    +

    Read every field and value from a hash.

    Parameters

    @@ -7670,13 +7903,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hgetall('myhash');
    +

    - + Redis|int|false hIncrBy(string $key, string $field, int $value) @@ -7686,17 +7927,7 @@

    -

    Increment a hash field's value by an integer

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('player');
    -
    -$redis->hmset('player', ['name' => 'Bob', 'level' => 1]);
    -
    -// int(2)
    -$redis->hIncrBy('player', 'level', 1);
    -
    -// int(5)
    -$redis->hIncrBy('player', 'level', 3);

    +

    Increment a hash field's value by an integer

    Parameters

    @@ -7743,13 +7974,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hMSet('player:1', ['name' => 'Alice', 'score' => 0]);
    +$redis->hincrby('player:1', 'score', 10);
    +

    - + Redis|float|false hIncrByFloat(string $key, string $field, float $value) @@ -7759,15 +7999,7 @@

    -

    Increment a hash field by a floating point value

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('trig-numbers')
    -
    -// float(3.1415926)
    -$pi = $redis->hIncrByFloat('trig-numbers', 'pi', 3.1415926);
    -
    -// float(6.2831852)
    -$redis->hIncrByFloat('trig-numbers', 'tau', 2 * $pi);

    +

    Increment a hash field by a floating point value

    Parameters

    @@ -7814,13 +8046,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hincrbyfloat('numbers', 'tau', 2 * 3.1415926);
    +

    - + Redis|array|false hKeys(string $key) @@ -7830,21 +8070,7 @@

    -

    Retrieve all of the fields of a hash.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('ships');
    -
    -$redis->hmset('ships', ['Enterprise' => 'NCC-1701D', 'Defiant' => 'NX-74205', 'Voyager' => 'NCC-74656']);
    -
    -// array(3) {
    -//   [0]=>
    -//   string(10) "Enterprise"
    -//   [1]=>
    -//   string(7) "Defiant"
    -//   [2]=>
    -//   string(7) "Voyager"
    -// }
    -$redis->hKeys('ships');

    +

    Retrieve all of the fields of a hash.

    Parameters

    @@ -7881,13 +8107,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hkeys('myhash');
    +

    - + Redis|int|false hLen(string $key) @@ -7934,13 +8168,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hlen('myhash');
    +

    - + Redis|array|false hMget(string $key, array $fields) @@ -7950,19 +8192,7 @@

    -

    Get one or more fields from a hash.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('player:1');
    -
    -$redis->hmset('player:1', ['name' => 'Alice', 'age' => '26', 'score' => '1337']);
    -
    -// array(2) {
    -//   ["name"]=>
    -//   string(5) "Alice"
    -//   ["score"]=>
    -//   string(4) "1337"
    -// }
    -$redis->hmget('player:1', ['name', 'score']);

    +

    Get one or more fields from a hash.

    Parameters

    @@ -8004,13 +8234,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hMGet('player:1', ['name', 'score']);
    +

    - + Redis|bool hMset(string $key, array $fieldvals) @@ -8020,9 +8258,7 @@

    -

    Add or update one or more hash fields and values

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]);

    +

    Add or update one or more hash fields and values

    Parameters

    @@ -8064,13 +8300,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]);
    +

    - + Redis|string|array hRandField(string $key, array $options = null) @@ -8080,14 +8324,7 @@

    -

    Get one or more random field from a hash.

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('settings');
    -
    -$redis->hmset('settings', ['path' => '/', 'state' => 'active', 'jobs' => 15]);
    -
    -$redis->hrandfield('settings');
    -$redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);

    +

    Get one or more random field from a hash.

    Parameters

    @@ -8133,13 +8370,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->hrandfield('settings');
    $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);
    +

    - + Redis|int|false hSet(string $key, string $member, mixed $value) @@ -8192,7 +8440,7 @@

    Return Value

    - + Redis|bool hSetNx(string $key, string $field, string $value) @@ -8202,17 +8450,7 @@

    -

    Set a hash field and value, but only if that field does not exist

    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('player:1');
    -
    -$redis->hmset('player:1', ['name' => 'bob', 'score' => 0]);
    -
    -// bool(true)
    -var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));
    -
    -// bool(false)
    -var_dump($redis->hsetnx('player:1', 'lock', 'enabled'));

    +

    Set a hash field and value, but only if that field does not exist

    Parameters

    @@ -8259,13 +8497,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hsetnx('player:1', 'lock', 'enabled');
    +$redis->hsetnx('player:1', 'lock', 'enabled');
    +

    - + Redis|int|false hStrLen(string $key, string $field) @@ -8334,7 +8581,7 @@

    Examples

    - + Redis|array|false hVals(string $key) @@ -8363,22 +8610,7 @@

    Return Value

    - +
    Redis|array|false

    The values from the hash.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('player');
    -
    -$redis->hmset('player', ['name' => 'Alice', 'score' => 1337]);
    -
    -// array(2) {
    -//   ["name"]=>
    -//   string(5) "Alice"
    -//   ["score"]=>
    -//   string(4) "1337"
    -// }
    -$redis->hgetall('player');
    -?>

    The values from the hash.

    @@ -8396,13 +8628,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->hvals('player:1');
    +

    - + Redis|array|bool hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -8448,41 +8688,7 @@

    Return Value

    - +
    Redis|array|bool

    An array with a subset of fields and values.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('big-hash');
    -
    -for ($i = 0; $i < 1000; $i++) {
    -    $fields["field:$i"] = "value:$i";
    -}
    -
    -$redis->hmset('big-hash', $fields);
    -
    -$it = NULL;
    -
    -do {
    -    // Scan the hash but limit it to fields that match '*:1?3'
    -    $fields = $redis->hscan('big-hash', $it, '*:1?3');
    -
    -    foreach ($fields as $field => $value) {
    -        echo "[$field] => $value\n";
    -    }
    -} while ($it != 0);
    -
    -// --- OUTPUT ---
    -// [field:143] => value:143
    -// [field:133] => value:133
    -// [field:163] => value:163
    -// [field:183] => value:183
    -// [field:153] => value:153
    -// [field:113] => value:113
    -// [field:103] => value:103
    -// [field:193] => value:193
    -// [field:123] => value:123
    -// [field:173] => value:173
    -?>

    An array with a subset of fields and values.

    @@ -8506,13 +8712,40 @@

    See also

    +

    Examples

    + + + + + +
    $redis = new Redis(['host' => 'localhost']);
    +
    +$redis->del('big-hash');
    +
    +for ($i = 0; $i < 1000; $i++) {
    + $fields["field:$i"] = "value:$i";
    +}
    +
    +$redis->hmset('big-hash', $fields);
    +
    +$it = NULL;
    +
    +do {
    + // Scan the hash but limit it to fields that match '*:1?3'
    + $fields = $redis->hscan('big-hash', $it, '*:1?3');
    +
    + foreach ($fields as $field => $value) {
    + echo "[$field] => $value\n";
    + }
    +} while ($it != 0);
    +

    - + Redis|int|false incr(string $key, int $by = 1) @@ -8546,18 +8779,7 @@

    Return Value

    - +
    Redis|int|false

    The new value of the key after incremented.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->set('counter', 1);
    -
    -// int(2);
    -$redis->incr('counter');
    -
    -// int(4);
    -$redis->incr('counter', 2);
    -?>

    The new value of the key after incremented.

    @@ -8581,13 +8803,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->incr('mycounter');
    $redis->incr('mycounter', 10);
    +

    - + Redis|int|false incrBy(string $key, int $value) @@ -8611,25 +8844,7 @@

    Parameters

    int $value -

    The amount to increment.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->set('primes', 2);
    -
    -// int(3)
    -$redis->incrby('primes', 1);
    -
    -// int(5)
    -$redis->incrby('primes', 2);
    -
    -// int(7)
    -$redis->incrby('primes', 2);
    -
    -// int(11)
    -$redis->incrby('primes', 4);
    -?>
    +

    The amount to increment.

    @@ -8657,13 +8872,25 @@

    See also

    +

    Examples

    + + + + + +
    $redis->set('primes', 2);
    +$redis->incrby('primes', 1);
    +$redis->incrby('primes', 2);
    +$redis->incrby('primes', 2);
    +$redis->incrby('primes', 4);
    +

    - + Redis|float|false incrByFloat(string $key, float $value) @@ -8697,31 +8924,29 @@

    Return Value

    - +
    Redis|float|false

    The new value of the key or false if the key didn't contain a string.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('tau');
    -
    -// float(3.1415926)
    -var_dump($redis->incrByFloat('tau', 3.1415926));
    -
    -// float(6.2831852)
    -var_dump($redis->incrByFloat('tau', 3.1415926));
    -?>

    The new value of the key or false if the key didn't contain a string.

    +

    Examples

    + + + + + +
    $redis->incrbyfloat('tau', 3.1415926);
    +$redis->incrbyfloat('tau', 3.1415926);
    +

    - + Redis|array|false info(string ...$sections) @@ -8777,7 +9002,7 @@

    See also

    - + bool isConnected() @@ -8809,7 +9034,7 @@

    Return Value

    - + Redis|array|false keys(string $pattern) @@ -8852,7 +9077,7 @@

    Return Value

    - + Redis|int|false lInsert(string $key, string $pos, mixed $pivot, mixed $value) @@ -8910,7 +9135,7 @@

    Return Value

    - + Redis|int|false lLen(string $key) @@ -8920,8 +9145,7 @@

    -

    No description

    - +

    Retrieve the lenght of a list.

    Parameters

    @@ -8930,7 +9154,7 @@

    Parameters

    string $key - +

    The list

    @@ -8940,7 +9164,7 @@

    Return Value

    - +
    Redis|int|false

    The number of elements in the list or false on failure.

    @@ -8953,7 +9177,7 @@

    Return Value

    - + Redis|string|false lMove(string $src, string $dst, string $wherefrom, string $whereto) @@ -8963,8 +9187,7 @@

    -

    No description

    - +

    Move an element from one list into another.

    Parameters

    @@ -8973,22 +9196,24 @@

    Parameters

    string $src - +

    The source list.

    string $dst - +

    The destination list

    string $wherefrom - +

    Where in the source list to retrieve the element. This can be either +Redis::LEFT, or Redis::RIGHT.

    string $whereto - +

    Where in the destination list to put the element. This can be either +Redis::LEFT, or Redis::RIGHT.

    @@ -8998,20 +9223,29 @@

    Return Value

    - +
    Redis|string|false

    The element removed from the source list.

    +

    Examples

    + + + + + +
    $redis->rPush('numbers', 'one', 'two', 'three');
    +$redis->lMove('numbers', 'odds', Redis::LEFT, Redis::LEFT);
    +

    - + Redis|bool|string|array lPop(string $key, int $count = 0) @@ -9021,8 +9255,7 @@

    -

    No description

    - +

    Pop one or more elements off a list.

    Parameters

    @@ -9031,12 +9264,12 @@

    Parameters

    string $key - +

    The list to pop from.

    int $count - +

    Optional number of elements to remove. By default one element is popped.

    @@ -9046,20 +9279,43 @@

    Return Value

    - +
    Redis|bool|string|array

    Will return the element(s) popped from the list or false/NULL +if none was removed.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/lpop +
    + -
    -

    +

    Examples

    - + + + + + + + +
    $redis->lpop('mylist');
    $redis->lpop('mylist', 4);
    + + + + +

    - + Redis|null|bool|int|array lPos(string $key, mixed $value, array $options = null) @@ -9069,8 +9325,7 @@

    -

    No description

    - +

    Retrieve the index of an element in a list.

    Parameters

    @@ -9079,17 +9334,32 @@

    Parameters

    string $key - +

    The list to query.

    mixed $value - +

    The value to search for.

    array $options - +

    Options to configure how the command operates

    +
    $options = [
    +    // How many matches to return.  By default a single match is returned.
    +    // If count is set to zero, it means unlimited.
    +    'COUNT' => <num-matches>
    +
    +    // Specify which match you want returned.  `RANK` 1 means "the first match"
    +    // 2 meaans the second, and so on.  If passed as a negative number the
    +    // RANK is computed right to left, so a `RANK` of -1 means "the last match".
    +    'RANK'  => <rank>
    +
    +    // This argument allows you to limit how many elements Redis will search before
    +    // returning.  This is useful to prevent Redis searching very long lists while
    +    // blocking the client.
    +    'MAXLEN => <max-len>
    +];
    @@ -9099,7 +9369,7 @@

    Return Value

    - +
    Redis|null|bool|int|array

    Returns one or more of the matching indexes, or null/false if none were found.

    @@ -9112,8 +9382,8 @@

    Return Value

    - - int|Redis + + Redis|int|false lPush(string $key, mixed ...$elements)

    @@ -9122,8 +9392,7 @@

    -

    No description

    - +

    Prepend one or more elements to a list.

    Parameters

    @@ -9132,12 +9401,12 @@

    Parameters

    string $key - +

    The list to prepend.

    mixed ...$elements - +

    One or more elements to prepend.

    @@ -9146,22 +9415,41 @@

    Return Value

    - - + +
    int|RedisRedis|int|false

    The new length of the list after prepending.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/lpush +
    + +

    Examples

    + + + + + +
    $redis->lPush('mylist', 'cat', 'bear', 'aligator');
    +

    - - Redis|int|false + + Redis|int|false rPush(string $key, mixed ...$elements)

    @@ -9170,8 +9458,7 @@

    -

    No description

    - +

    Append one or more elements to a list.

    Parameters

    @@ -9180,12 +9467,12 @@

    Parameters

    string $key - +

    The list to append to.

    mixed ...$elements - +

    one or more elements to append.

    @@ -9194,22 +9481,41 @@

    Return Value

    - - + +
    Redis|int|falseRedis|int|false

    The new length of the list

    +

    See also

    + + + + + + +
    + https://redis.io/commands/rpush +
    + +

    Examples

    + + + + + +
    $redis->rPush('mylist', 'xray', 'yankee', 'zebra');
    +

    - - Redis|int|false + + Redis|int|false lPushx(string $key, mixed $value)

    @@ -9218,8 +9524,7 @@

    -

    No description

    - +

    Prepend an element to a list but only if the list exists

    Parameters

    @@ -9228,12 +9533,12 @@

    Parameters

    string $key - +

    The key to prepend to.

    mixed $value - +

    The value to prepend.

    @@ -9242,8 +9547,8 @@

    Return Value

    - - + +
    Redis|int|falseRedis|int|false

    The new length of the list.

    @@ -9256,8 +9561,8 @@

    Return Value

    - - Redis|int|false + + Redis|int|false rPushx(string $key, mixed $value)

    @@ -9266,8 +9571,7 @@

    -

    No description

    - +

    Append an element to a list but only if the list exists

    Parameters

    @@ -9276,12 +9580,12 @@

    Parameters

    string $key - +

    The key to prepend to.

    mixed $value - +

    The value to prepend.

    @@ -9290,8 +9594,8 @@

    Return Value

    - - + +
    Redis|int|falseRedis|int|false

    The new length of the list.

    @@ -9304,7 +9608,7 @@

    Return Value

    - + Redis|bool lSet(string $key, int $index, mixed $value) @@ -9314,8 +9618,7 @@

    -

    No description

    - +

    Set a list element at an index to a specific value.

    Parameters

    @@ -9324,17 +9627,17 @@

    Parameters

    string $key - +

    The list to modify.

    int $index - +

    The position of the element to change.

    mixed $value - +

    The new value.

    @@ -9344,12 +9647,23 @@

    Return Value

    - +
    Redis|bool

    True if the list was modified.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/lset +
    +

    @@ -9357,7 +9671,7 @@

    Return Value

    - + int lastSave() @@ -9367,8 +9681,7 @@

    -

    No description

    - +

    Retrieve the last time Redis' database was persisted to disk.

    @@ -9377,12 +9690,23 @@

    Return Value

    - +
    int

    The unix timestamp of the last save time

    +

    See also

    + + + + + + +
    + https://redis.io/commands/lastsave +
    +

    @@ -9390,7 +9714,7 @@

    Return Value

    - + mixed lindex(string $key, int $index) @@ -9400,8 +9724,7 @@

    -

    No description

    - +

    Get the element of a list by its index.

    Parameters

    @@ -9410,12 +9733,12 @@

    Parameters

    string $key - +

    The key to query

    int $index - +

    The index to check.

    @@ -9425,7 +9748,7 @@

    Return Value

    - +
    mixed

    The index or NULL/false if the element was not found.

    @@ -9438,7 +9761,7 @@

    Return Value

    - + Redis|array|false lrange(string $key, int $start, int $end) @@ -9448,8 +9771,7 @@

    -

    No description

    - +

    Retrieve elements from a list.

    Parameters

    @@ -9458,17 +9780,19 @@

    Parameters

    string $key - +

    The list to query.

    int $start - +

    The beginning index to retrieve. This number can be negative +meaning start from the end of the list.

    int $end - +

    The end index to retrieve. This can also be negative to start +from the end of the list.

    @@ -9478,21 +9802,32 @@

    Return Value

    - +
    Redis|array|false

    The range of elements between the indexes.

    +

    Examples

    + + + + + + + + +
    $redis->lrange('mylist', 0, -1);  // the whole list
    $redis->lrange('mylist', -2, -1); // the last two elements in the list.
    +

    - - int|Redis|false + + Redis|int|false lrem(string $key, mixed $value, int $count = 0)

    @@ -9501,8 +9836,7 @@

    -

    No description

    - +

    Remove one or more matching elements from a list.

    Parameters

    @@ -9511,17 +9845,17 @@

    Parameters

    string $key - +

    The list to truncate.

    mixed $value - +

    The value to remove.

    int $count - +

    How many elements matching the value to remove.

    @@ -9530,13 +9864,24 @@

    Return Value

    - - + +
    int|Redis|falseRedis|int|false

    The number of elements removed.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/lrem +
    +

    @@ -9544,7 +9889,7 @@

    Return Value

    - + Redis|bool ltrim(string $key, int $start, int $end) @@ -9554,8 +9899,7 @@

    -

    No description

    - +

    Trim a list to a subrange of elements.

    Parameters

    @@ -9564,17 +9908,17 @@

    Parameters

    string $key - +

    The list to trim

    int $start - +

    The starting index to keep

    int $end - +

    The ending index to keep.

    @@ -9584,21 +9928,29 @@

    Return Value

    - +
    Redis|bool

    true if the list was trimmed.

    +

    Examples

    + + + + + +
    $redis->ltrim('mylist', 0, 3);  // Keep the first four elements
    +

    - - array|Redis + + Redis|array mget(array $keys)

    @@ -9607,8 +9959,7 @@

    -

    No description

    - +

    Get one ore more string keys.

    Parameters

    @@ -9617,7 +9968,7 @@

    Parameters

    array $keys - +

    The keys to retrieve

    @@ -9626,21 +9977,29 @@

    Return Value

    - - + +
    array|RedisRedis|array

    an array of keys with their values.

    +

    Examples

    + + + + + +
    $redis->mget(['key1', 'key2']);
    +

    - + Redis|bool migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = NULL) @@ -9718,8 +10077,8 @@

    Return Value

    - - bool + + Redis|bool move(string $key, int $index)

    @@ -9728,8 +10087,7 @@

    -

    No description

    - +

    Move a key to a different database on the same redis instance.

    Parameters

    @@ -9738,7 +10096,7 @@

    Parameters

    string $key - +

    The key to move

    int @@ -9752,8 +10110,8 @@

    Return Value

    - - + +
    boolRedis|bool

    True if the key was moved

    @@ -9766,7 +10124,7 @@

    Return Value

    - + Redis|bool mset(array $key_values) @@ -9776,8 +10134,7 @@

    -

    No description

    - +

    Set one ore more string keys.

    Parameters

    @@ -9786,7 +10143,7 @@

    Parameters

    array $key_values - +

    An array with keys and their values.

    @@ -9796,20 +10153,39 @@

    Return Value

    - +
    Redis|bool

    True if the keys could be set.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/mset +
    + +

    Examples

    + + + + + +
    $redis->mSet(['foo' => 'bar', 'baz' => 'bop']);
    +

    - + Redis|bool msetnx(array $key_values) @@ -9819,8 +10195,7 @@

    -

    No description

    - +

    Set one ore more string keys but only if none of the key exist.

    Parameters

    @@ -9829,7 +10204,7 @@

    Parameters

    array $key_values - +

    An array of keys with their values.

    @@ -9839,20 +10214,39 @@

    Return Value

    - +
    Redis|bool

    True if the keys were set and false if not.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/msetnx +
    + +

    Examples

    + + + + + +
    $redis->msetnx(['foo' => 'bar', 'baz' => 'bop']);
    +

    - + bool|Redis multi(int $value = Redis::MULTI) @@ -9862,8 +10256,7 @@

    -

    No description

    - +

    Begin a transaction.

    Parameters

    @@ -9872,7 +10265,8 @@

    Parameters

    int $value - +

    The type of transaction to start. This can either be Redis::MULTI or +`Redis::PIPELINE'.

    @@ -9882,20 +10276,42 @@

    Return Value

    - +
    bool|Redis

    True if the transaction could be started.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/multi +
    + +

    Examples

    + + + + + +
    $redis->multi();
    +$redis->set('foo', 'bar');
    +$redis->get('foo');
    +$redis->exec();
    +

    - + Redis|int|string|false object(string $subcommand, string $key) @@ -9943,7 +10359,7 @@

    Return Value

    - + bool open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) deprecated @@ -10023,7 +10439,7 @@

    Return Value

    - + bool pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) @@ -10096,8 +10512,8 @@

    Return Value

    - - bool + + Redis|bool persist(string $key)

    @@ -10106,8 +10522,7 @@

    -

    No description

    - +

    Remove the expiration from a key.

    Parameters

    @@ -10116,7 +10531,7 @@

    Parameters

    string $key - +

    The key to operate against.

    @@ -10125,8 +10540,8 @@

    Return Value

    - - + +
    boolRedis|bool

    True if a timeout was removed and false if it was not or the key didn't exist.

    @@ -10139,7 +10554,7 @@

    Return Value

    - + bool pexpire(string $key, int $timeout, string|null $mode = NULL) @@ -10195,7 +10610,7 @@

    Return Value

    - + Redis|bool pexpireAt(string $key, int $timestamp, string|null $mode = NULL) @@ -10266,7 +10681,7 @@

    See also

    - + Redis|int pfadd(string $key, array $elements) @@ -10324,7 +10739,7 @@

    See also

    - + Redis|int pfcount(string $key) @@ -10377,7 +10792,7 @@

    See also

    - + Redis|bool pfmerge(string $dst, array $srckeys) @@ -10435,7 +10850,7 @@

    See also

    - + Redis|string|bool ping(string $message = NULL) @@ -10465,16 +10880,7 @@

    Return Value

    Redis|string|bool

    If passed no message, this command will simply return true. -If a message is passed, it will return the message.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -// bool(true)
    -$redis->ping();
    -
    -// string(9) "beep boop"
    -$redis->ping('beep boop');
    -?>
    +If a message is passed, it will return the message.

    @@ -10492,13 +10898,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->ping();
    $redis->ping('beep boop');
    +

    - + bool|Redis pipeline() @@ -10520,37 +10937,32 @@

    Return Value

    - +
    bool|Redis

    The redis object is returned, to facilitate method chaining.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -// array(3) {
    -//   [0]=>
    -//   bool(true)
    -//   [1]=>
    -//   int(0)
    -//   [2]=>
    -//   int(3)
    -// }
    -$redis->pipeline()
    -      ->set('foo', 'bar')
    -      ->del('mylist')
    -      ->rpush('mylist', 'a', 'b', 'c')
    -      ->exec();
    -?>

    The redis object is returned, to facilitate method chaining.

    +

    Examples

    + + + + + +
    $redis->pipeline()
    +->set('foo', 'bar')
    +->del('mylist')
    +->rpush('mylist', 'a', 'b', 'c')
    +->exec();
    +

    - + bool popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) deprecated @@ -10630,8 +11042,8 @@

    Return Value

    - - bool|Redis + + Redis|bool psetex(string $key, int $expire, mixed $value)

    @@ -10640,8 +11052,7 @@

    -

    No description

    - +

    Set a key with an expiration time in milliseconds

    Parameters

    @@ -10650,17 +11061,17 @@

    Parameters

    string $key - +

    The key to set

    int $expire - +

    The TTL to set, in milliseconds.

    mixed $value - +

    The value to set the key to.

    @@ -10669,21 +11080,29 @@

    Return Value

    - - + +
    bool|RedisRedis|bool

    True if the key could be set.

    +

    Examples

    + + + + + +
    $redis->psetex('mykey', 1000, 'myval');
    +

    - + bool psubscribe(array $patterns, callable $cb) @@ -10742,7 +11161,7 @@

    See also

    - + Redis|int|false pttl(string $key) @@ -10771,17 +11190,9 @@

    Return Value

    - +
    Redis|int|false

    The keys TTL or false on failure.

    -

    NOTE: -1 means a key has no TTL and -2 means the key doesn't exist.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->setex('ttl-key', 60, 'ttl-value');
    -
    -// int(60000)
    -var_dump($redis->pttl('ttl-key'));
    -?>

    The key's TTL or one of two special values if it has none.

    +
    -1 - The key has no TTL.
    +-2 - The key did not exist.
    @@ -10799,13 +11210,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->pttl('ttl-key');
    +

    - + Redis|int|false publish(string $channel, string $message) @@ -10863,7 +11282,7 @@

    See also

    - + mixed pubsub(string $command, mixed $arg = null) @@ -10911,7 +11330,7 @@

    Return Value

    - + Redis|array|bool punsubscribe(array $patterns) @@ -10977,7 +11396,7 @@

    See also

    - + Redis|array|string|bool rPop(string $key, int $count = 0) @@ -11001,8 +11420,8 @@

    Parameters

    int $count -

    The maximum number of elements to pop at once.

    -

    NOTE: The count argument requires Redis >= 6.2.0

    +

    The maximum number of elements to pop at once. +NOTE: The count argument requires Redis >= 6.2.0

    @@ -11012,23 +11431,7 @@

    Return Value

    - +
    Redis|array|string|bool

    One ore more popped elements or false if all were empty.

    -
    <?php
    -<?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('mylist');
    -$redis->rPush('mylist', 'one', 'two', 'three');
    -
    -// string(5) "three"
    -$redis->rPop('mylist');
    -
    -// string(3) "two"
    -$redis->rPop('mylist');
    -
    -// string(3) "one"
    -$redis->rPop('mylist');
    -?>

    One ore more popped elements or false if all were empty.

    @@ -11046,13 +11449,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->rPop('mylist');
    $redis->rPop('mylist', 4);
    +

    - + Redis|string|false randomKey() @@ -11095,7 +11509,7 @@

    See also

    - + mixed rawcommand(string $command, mixed ...$args) @@ -11119,28 +11533,7 @@

    Parameters

    mixed ...$args -

    One or more arguments to pass to the command.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->rawCommand('del', 'mystring', 'mylist');
    -$redis->rawCommand('set', 'mystring', 'myvalue');
    -$redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three');
    -
    -// string(7) "myvalue"
    -$redis->rawCommand('get', 'mystring');
    -
    -// array(3) {
    -//   [0]=>
    -//   string(3) "one"
    -//   [1]=>
    -//   string(3) "two"
    -//   [2]=>
    -//   string(5) "three"
    -// }
    -$redis->rawCommand('lrange', 'mylist', 0, -1);
    -?>
    +

    One or more arguments to pass to the command.

    @@ -11150,20 +11543,34 @@

    Return Value

    - +
    mixed

    Can return any number of things depending on command executed.

    +

    Examples

    + + + + + + + + + + + +
    $redis->rawCommand('del', 'mystring', 'mylist');
    $redis->rawCommand('set', 'mystring', 'myvalue');
    $redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three');
    +

    - + Redis|bool rename(string $old_name, string $new_name) @@ -11221,7 +11628,7 @@

    See also

    - + Redis|bool renameNx(string $key_src, string $key_dst) @@ -11255,21 +11662,7 @@

    Return Value

    - +
    Redis|bool

    True if the key was renamed, false if not.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('src', 'dst', 'existing-dst');
    -
    -$redis->set('src', 'src_key');
    -$redis->set('existing-dst', 'i_exist');
    -
    -// bool(true)
    -$redis->renamenx('src', 'dst');
    -
    -// bool(false)
    -$redis->renamenx('dst', 'existing-dst');
    -?>

    True if the key was renamed, false if not.

    @@ -11287,13 +11680,25 @@

    See also

    +

    Examples

    + + + + + +
    $redis->set('src', 'src_key');
    +$redis->set('existing-dst', 'i_exist');
    +
    +$redis->renamenx('src', 'dst');
    +$redis->renamenx('dst', 'existing-dst');
    +

    - + Redis|bool reset() @@ -11325,7 +11730,7 @@

    Return Value

    - + Redis|bool restore(string $key, int $ttl, string $value, array|null $options = NULL) @@ -11382,33 +11787,7 @@

    Return Value

    - +
    Redis|bool

    True if the key was stored, false if not.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('captains');
    -
    -$redis->sAdd('captains', 'Janeway', 'Picard', 'Sisko', 'Kirk', 'Archer');
    -
    -$serialized = $redis->dump('captains');
    -
    -$redis->select(1);
    -$redis->restore('captains-backup', 0, $serialized);
    -
    -//array(5) {
    -//  [0]=>
    -//  string(6) "Archer"
    -//  [1]=>
    -//  string(4) "Kirk"
    -//  [2]=>
    -//  string(5) "Sisko"
    -//  [3]=>
    -//  string(6) "Picard"
    -//  [4]=>
    -//  string(7) "Janeway"
    -//}
    -var_dump($redis->sMembers('captains-backup'));
    -?>

    True if the key was stored, false if not.

    @@ -11439,13 +11818,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->sAdd('captains', 'Janeway', 'Picard', 'Sisko', 'Kirk', 'Archer');
    +$serialized = $redis->dump('captains');
    +
    +$redis->restore('captains-backup', 0, $serialized);
    +

    - + mixed role() @@ -11478,7 +11868,7 @@

    Return Value

    - + Redis|string|false rpoplpush(string $srckey, string $dstkey) @@ -11513,31 +11903,7 @@

    Return Value

    - +
    Redis|string|false

    The popped element or false if the source key was empty.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->pipeline()
    -      ->del('list1', 'list2')
    -      ->rpush('list1', 'list1-1', 'list1-2')
    -      ->rpush('list2', 'list2-1', 'list2-2')
    -      ->exec();
    -
    -var_dump($redis->rpoplpush('list2', 'list1'));
    -var_dump($redis->lrange('list1', 0, -1));
    -
    -// --- OUTPUT ---
    -// string(7) "list2-2"
    -//
    -// array(3) {
    -//   [0]=>
    -//   string(7) "list2-2"
    -//   [1]=>
    -//   string(7) "list1-1"
    -//   [2]=>
    -//   string(7) "list1-2"
    -// }
    -?>

    The popped element or false if the source key was empty.

    @@ -11555,13 +11921,27 @@

    See also

    +

    Examples

    + + + + + +
    $redis->pipeline()
    + ->del('list1', 'list2')
    + ->rpush('list1', 'list1-1', 'list1-2')
    + ->rpush('list2', 'list2-1', 'list2-2')
    + ->exec();
    +
    +$redis->rpoplpush('list2', 'list1');
    +

    - + Redis|int|false sAdd(string $key, mixed $value, mixed ...$other_values) @@ -11600,19 +11980,7 @@

    Return Value

    - +
    Redis|int|false

    The number of values added to the set.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('myset');
    -
    -var_dump($redis->sadd('myset', 'foo', 'bar', 'baz'));
    -var_dump($redis->sadd('myset', 'foo', 'new'));
    -
    -// --- OUTPUT ---
    -// int(3)
    -// int(1)
    -?>

    The number of values added to the set.

    @@ -11630,13 +11998,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->del('myset');
    +
    +$redis->sadd('myset', 'foo', 'bar', 'baz');
    +$redis->sadd('myset', 'foo', 'new');
    +

    - + int sAddArray(string $key, array $values) @@ -11671,19 +12050,7 @@

    Return Value

    - +
    int

    The number of members added to the set.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('myset');
    -
    -var_dump($redis->sAddArray('myset', ['foo', 'bar', 'baz']));
    -var_dump($redis->sAddArray('myset', ['foo', 'new']));
    -
    -// --- OUTPUT ---
    -// int(3)
    -// int(1)
    -?>

    The number of members added to the set.

    @@ -11707,13 +12074,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->del('myset');
    +
    +$redis->sAddArray('myset', ['foo', 'bar', 'baz']);
    +$redis->sAddArray('myset', ['foo', 'new']);
    +

    - + Redis|array|false sDiff(string $key, string ...$other_keys) @@ -11749,29 +12127,7 @@

    Return Value

    Redis|array|false

    Returns the elements from keys 2..N that don't exist in the -first sorted set, or false on failure.

    -
    
    -<?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->pipeline()
    -      ->del('set1', 'set2', 'set3')
    -      ->sadd('set1', 'apple', 'banana', 'carrot', 'date')
    -      ->sadd('set2', 'carrot')
    -      ->sadd('set3', 'apple', 'carrot', 'eggplant')
    -      ->exec();
    -
    -// NOTE:  'banana' and 'date' are in set1 but none of the subsequent sets.
    -var_dump($redis->sdiff('set1', 'set2', 'set3'));
    -
    -// --- OUTPUT ---
    -array(2) {
    -  [0]=>
    -  string(6) "banana"
    -  [1]=>
    -  string(4) "date"
    -}
    -?>
    +first sorted set, or false on failure.

    @@ -11789,13 +12145,28 @@

    See also

    +

    Examples

    + + + + + +
    $redis->pipeline()
    + ->del('set1', 'set2', 'set3')
    + ->sadd('set1', 'apple', 'banana', 'carrot', 'date')
    + ->sadd('set2', 'carrot')
    + ->sadd('set3', 'apple', 'carrot', 'eggplant')
    + ->exec();
    +
    +$redis->sdiff('set1', 'set2', 'set3');
    +

    - + Redis|int|false sDiffStore(string $dst, string $key, string ...$other_keys) @@ -11865,7 +12236,7 @@

    See also

    - + Redis|array|false sInter(array|string $key, string ...$other_keys) @@ -11890,27 +12261,7 @@

    Parameters

    string ...$other_keys -

    One or more Redis SET keys.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->pipeline()
    -      ->del('alice_likes', 'bob_likes', 'bill_likes')
    -      ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato')
    -      ->sadd('bob_likes', 'asparagus', 'carrot', 'potato')
    -      ->sadd('bill_likes', 'broccoli', 'potato')
    -      ->exec();
    -
    -// NOTE:  'potato' is the only value in all three sets
    -var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes'));
    -
    -// --- OUTPUT ---
    -// array(1) {
    -//   [0]=>
    -//   string(6) "potato"
    -// }
    -?>
    +

    One or more Redis SET keys.

    @@ -11938,13 +12289,29 @@

    See also

    +

    Examples

    + + + + + +
    $redis->pipeline()
    + ->del('alice_likes', 'bob_likes', 'bill_likes')
    + ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato')
    + ->sadd('bob_likes', 'asparagus', 'carrot', 'potato')
    + ->sadd('bill_likes', 'broccoli', 'potato')
    + ->exec();
    +
    +var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes'));
    +</code>
    +

    - + Redis|int|false sintercard(array $keys, int $limit = -1) @@ -11979,19 +12346,7 @@

    Return Value

    - +
    Redis|int|false

    The

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('set1', 'set2', 'set3');
    -
    -$redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot');
    -$redis->sAdd('set2', 'apple',         'banana');
    -$redis->sAdd('set3',          'pear', 'banana');
    -
    -// int(1)
    -var_dump($redis->sInterCard(['set1', 'set2', 'set3']));
    -?>
    The
    @@ -12009,13 +12364,27 @@

    See also

    +

    Examples

    + + + + + +
    $redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot');
    +$redis->sAdd('set2', 'apple', 'banana');
    +$redis->sAdd('set3', 'pear', 'banana');
    +
    +$redis->sInterCard(['set1', 'set2', 'set3']);
    +?>
    +</code>
    +

    - + Redis|int|false sInterStore(array|string $key, string ...$other_keys) @@ -12051,16 +12420,7 @@

    Return Value

    - +
    Redis|int|false

    The number of values stored in the destination key or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -// OPTION 1:  A single array
    -$redis->sInterStore(['dst', 'src1', 'src2', 'src3']);
    -
    -// OPTION 2:  Variadic
    -$redis->sInterStore('dst', 'src1', 'src'2', 'src3');
    -?>

    The number of values stored in the destination key or false on failure.

    @@ -12084,13 +12444,26 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->sInterStore(['dst', 'src1', 'src2', 'src3']);
    $redis->sInterStore('dst', 'src1', 'src'2', 'src3');
    +?>
    +</code>
    +

    - + Redis|array|false sMembers(string $key) @@ -12119,25 +12492,7 @@

    Return Value

    - +
    Redis|array|false

    Every element in the set or false on failure.

    -
    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('tng-crew');
    -
    -$redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']);
    -
    -// Array
    -// (
    -//     [0] => Riker
    -//     [1] => Crusher
    -//     [2] => Troi
    -//     [3] => Worf
    -//     [4] => LaForge
    -//     [5] => Picard
    -//     [6] => Broccoli
    -//     [7] => Data
    -// )
    -$redis->sMembers('tng-crew');

    Every element in the set or false on failure.

    @@ -12155,13 +12510,22 @@

    See also

    -
    - +

    Examples

    - + + + + +
    $redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']);
    +$redis->sMembers('tng-crew');
    + + + + +

    - + Redis|array|false sMisMember(string $key, string $member, string ...$other_members) @@ -12201,28 +12565,7 @@

    Return Value

    Redis|array|false

    An array of integers representing whether each passed value -was a member of the set.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('ds9-crew');
    -$redis->sAdd('ds9-crew', ...["Sisko", "Kira", "Dax", "Worf", "Bashir", "O'Brien"]);
    -
    -$names = ['Sisko', 'Picard', 'Data', 'Worf'];
    -$members = $redis->sMIsMember('ds9-crew', ...$names);
    -
    -// array(4) {
    -//   ["Sisko"]=>
    -//   int(1)
    -//   ["Picard"]=>
    -//   int(0)
    -//   ["Data"]=>
    -//   int(0)
    -//   ["Worf"]=>
    -//   int(1)
    -// }
    -var_dump(array_combine($names, $members));
    -?>
    +was a member of the set.

    @@ -12252,13 +12595,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->sAdd('ds9-crew', ...["Sisko", "Kira", "Dax", "Worf", "Bashir", "O'Brien"]);
    +$members = $redis->sMIsMember('ds9-crew', ...['Sisko', 'Picard', 'Data', 'Worf']);
    +

    - + Redis|bool sMove(string $src, string $dst, mixed $value) @@ -12298,36 +12650,7 @@

    Return Value

    - +
    Redis|bool

    True if the member was moved, and false if it wasn't in the set.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('numbers', 'evens');
    -$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
    -
    -$redis->sMove('numbers', 'evens', 'zero');
    -$redis->sMove('numbers', 'evens', 'two');
    -$redis->sMove('numbers', 'evens', 'four');
    -
    -// array(2) {
    -//   [0]=>
    -//   string(5) "three"
    -//   [1]=>
    -//   string(3) "one"
    -// }
    -var_dump($redis->sMembers('numbers'));
    -
    -// array(3) {
    -//   [0]=>
    -//   string(4) "zero"
    -//   [1]=>
    -//   string(3) "two"
    -//   [2]=>
    -//   string(4) "four"
    -// }
    -var_dump($redis->sMembers('evens'));
    -
    -?>

    True if the member was moved, and false if it wasn't in the set.

    @@ -12345,13 +12668,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
    +$redis->sMove('numbers', 'evens', 'zero');
    +$redis->sMove('numbers', 'evens', 'two');
    +$redis->sMove('numbers', 'evens', 'four');
    +

    - + Redis|string|array|false sPop(string $key, int $count = 0) @@ -12376,39 +12710,7 @@

    Parameters

    int $count

    An optional number of members to pop. This defaults to -removing one element.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -<?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('numbers', 'evens');
    -$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
    -
    -$redis->sMove('numbers', 'evens', 'zero');
    -$redis->sMove('numbers', 'evens', 'two');
    -$redis->sMove('numbers', 'evens', 'four');
    -
    -// array(2) {
    -//   [0]=>
    -//   string(5) "three"
    -//   [1]=>
    -//   string(3) "one"
    -// }
    -var_dump($redis->sMembers('numbers'));
    -
    -// array(3) {
    -//   [0]=>
    -//   string(4) "zero"
    -//   [1]=>
    -//   string(3) "two"
    -//   [2]=>
    -//   string(4) "four"
    -// }
    -var_dump($redis->sMembers('evens'));
    -?>
    +removing one element.

    @@ -12436,13 +12738,23 @@

    See also

    +

    Examples

    + + + + + +
    $redis->del('numbers', 'evens');
    +$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
    +$redis->sPop('numbers');
    +

    - + Redis|string|array|false sRandMember(string $key, int $count = 0) @@ -12481,36 +12793,45 @@

    Return Value

    - +
    Redis|string|array|false

    One or more random members or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('elder-gods');
    -
    -$redis->sAdd('elder-gods', ["Cthulhu", "Azathoth", "Daoloth", "D'endrrah"]);
    -
    -// A single random member returned.
    -$rng1 = $redis->sRandMember('elder-gods');
    -
    -// Up to SCARD `elder-gods` random members returned
    -$rng2 = $redis->sRandMember('elder-gods', 9999);
    -
    -// 9999 elements from the set returned with duplicates
    -$rng3 = $redis->sRandMember('elder-gods', -9999);
    -?>

    One or more random members or false on failure.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/srandmember +
    + +

    Examples

    + + + + + + + + + + + +
    $redis->sRandMember('myset');
    $redis->sRandMember('myset', 10);
    $redis->sRandMember('myset', -10);
    +

    - + Redis|array|false sUnion(string $key, string ...$other_keys) @@ -12544,32 +12865,7 @@

    Return Value

    - +
    Redis|array|false

    The union of the one or more input sets or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->pipeline()
    -      ->del('set1', 'set2', 'set3')
    -      ->sadd('set1', 'apple', 'banana', 'carrot')
    -      ->sadd('set2', 'apple', 'carrot', 'fish')
    -      ->sadd('set3', 'carrot', 'fig', 'eggplant');
    -
    -var_dump($redis->sunion('set1', 'set2', 'set3'));
    -
    -// --- OPUTPUT ---
    -// array(5) {
    -//   [0]=>
    -//   string(6) "banana"
    -//   [1]=>
    -//   string(5) "apple"
    -//   [2]=>
    -//   string(4) "fish"
    -//   [3]=>
    -//   string(6) "carrot"
    -//   [4]=>
    -//   string(8) "eggplant"
    -// }
    -?>

    The union of the one or more input sets or false on failure.

    @@ -12587,13 +12883,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->sunion('set1', 'set2');
    +

    - + Redis|int|false sUnionStore(string $dst, string $key, string ...$other_keys) @@ -12663,7 +12967,7 @@

    See also

    - + Redis|bool save() @@ -12713,7 +13017,7 @@

    See also

    - + array|false scan(int|null $iterator, string|null $pattern = null, int $count = 0, string $type = NULL) @@ -12727,34 +13031,7 @@

    For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of keys with a nonzero iterator. This can happen when matching against a pattern that very few keys match inside a key space with a great many keys. The following example demonstrates how -to use Redis::scan() with the option disabled and enabled.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
    -
    -$it = NULL;
    -
    -do {
    -    $keys = $redis->scan($it, '*zorg*');
    -    foreach ($keys as $key) {
    -        echo "KEY: $key\n";
    -    }
    -} while ($it != 0);
    -
    -$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
    -
    -$it = NULL;
    -
    -// When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an
    -// empty array of keys when the iterator is nonzero.
    -while ($keys = $redis->scan($it, '*zorg*')) {
    -    foreach ($keys as $key) {
    -        echo "KEY: $key\n";
    -    }
    -}
    -?>

    +to use Redis::scan() with the option disabled and enabled.

    Parameters

    @@ -12825,13 +13102,44 @@

    See also

    +

    Examples

    + + + + + +
    $redis = new Redis(['host' => 'localhost']);
    +
    +$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
    +
    +$it = NULL;
    +
    +do {
    + $keys = $redis->scan($it, '*zorg*');
    + foreach ($keys as $key) {
    + echo "KEY: $key\n";
    + }
    +} while ($it != 0);
    +
    +$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
    +
    +$it = NULL;
    +
    +// When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an
    +// empty array of keys when the iterator is nonzero.
    +while ($keys = $redis->scan($it, '*zorg*')) {
    + foreach ($keys as $key) {
    + echo "KEY: $key\n";
    + }
    +}
    +

    - + Redis|int|false scard(string $key) @@ -12860,17 +13168,7 @@

    Return Value

    - +
    Redis|int|false

    The cardinality of the set or false on failure.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('set');
    -$redis->sadd('set', 'one', 'two', 'three', 'four', 'five');
    -
    -// Returns 5
    -$redis->scard('set');
    -?>

    The cardinality of the set or false on failure.

    @@ -12888,13 +13186,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->scard('set');
    +</code>
    +

    - + mixed script(string $command, mixed ...$args) @@ -12928,26 +13235,7 @@

    Return Value

    - +
    mixed

    This command returns various things depending on the specific operation executed.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$lua = sprintf("return %f", microtime(true));
    -
    -// array(1) {
    -//   [0]=>
    -//   int(0)
    -// }
    -var_dump($redis->script('exists', sha1($lua)));
    -
    -$redis->script('load', $lua);
    -
    -// array(1) {
    -//   [0]=>
    -//   int(1)
    -// }
    -var_dump($redis->script('exists', sha1($lua)));
    -?>

    This command returns various things depending on the specific operation executed.

    @@ -12965,13 +13253,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->script('load', 'return 1');
    $redis->script('exists', sha1('return 1'));
    +

    - + Redis|bool select(int $db) @@ -13000,36 +13299,39 @@

    Return Value

    - +
    Redis|bool

    true on success and false on failure

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->select(1);
    -$redis->set('this_is_db_1', 'test');
    -
    -$redis->select(0);
    -var_dump($redis->exists('this_is_db_1'));
    -
    -$redis->select(1);
    -var_dump($redis->exists('this_is_db_1'));
    -
    -// --- OUTPUT ---
    -// int(0)
    -// int(1)
    -?>

    true on success and false on failure

    +

    See also

    + + + + + + +
    + https://redis.io/commands/select +
    + +

    Examples

    + + + + + +
    $redis->select(1);
    +

    - + Redis|string|bool set(string $key, mixed $value, mixed $options = NULL) @@ -13081,19 +13383,7 @@

    Return Value

    - +
    Redis|string|bool

    True if the key was set or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->set('key', 'value');
    -
    -// Will actually send `SETEX 60 key value` to Redis.
    -$redis->set('key', 'expires_in_60_seconds', 60);
    -
    -// Only have Redis set the key if it already exists.
    -$redis->set('key', 'options_set', ['XX']);
    -
    -?>

    True if the key was set or false on failure.

    @@ -13117,13 +13407,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->set('key', 'value');
    $redis->set('key', 'expires_in_60_seconds', 60);
    +

    - + Redis|int|false setBit(string $key, int $idx, bool $value) @@ -13162,18 +13463,7 @@

    Return Value

    - +
    Redis|int|false

    The original value of the bit or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->set('foo', 'bar');
    -
    -// Flip the 7th bit to 1
    -$redis->setbit('foo', 7, 1);
    -
    -// The bit flip turned 'bar' -> 'car'
    -$redis->get('foo');
    -?>

    The original value of the bit or false on failure.

    @@ -13191,13 +13481,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->set('foo', 'bar');
    +$redis->setbit('foo', 7, 1);
    +

    - + Redis|int|false setRange(string $key, int $index, string $value) @@ -13236,15 +13535,7 @@

    Return Value

    - +
    Redis|int|false

    The new length of the string or false on failure

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->set('message', 'Hello World');
    -
    -// Update 'Hello World' to 'Hello Redis'
    -$redis->setRange('message', 6, 'Redis');
    -?>

    The new length of the string or false on failure

    @@ -13262,13 +13553,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->set('message', 'Hello World');
    +$redis->setRange('message', 6, 'Redis');
    +

    - + bool setOption(int $option, mixed $value) @@ -13410,7 +13710,7 @@

    See also

    - + Redis|bool setex(string $key, int $expire, mixed $value) @@ -13449,27 +13749,28 @@

    Return Value

    - +
    Redis|bool

    True on success or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -// Set a key with a 60 second expiration
    -$redis->set('some_key', 60, 'some_value');
    -
    -?>php

    True on success or false on failure.

    -
    - +

    Examples

    - -
    + + + + +
    $redis->setex('60s-ttl', 60, 'some-value');
    + +
    + + + +

    - + Redis|bool setnx(string $key, mixed $value) @@ -13503,19 +13804,7 @@

    Return Value

    - +
    Redis|bool

    Returns true if the key was set and false otherwise.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('new-key');
    -$redis->set('existing-key', 'already-exists');
    -
    -// Key is new, returns 1
    -$redis->setnx('key1', 'here-is-a-new-key');
    -
    -// Key exists, returns 0
    -$redis->setnx('existing-key', 'new-value');
    -?>

    Returns true if the key was set and false otherwise.

    @@ -13533,13 +13822,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->setnx('existing-key', 'existing-value');
    $redis->setnx('new-key', 'new-value');
    +

    - + Redis|bool sismember(string $key, mixed $value) @@ -13573,34 +13873,28 @@

    Return Value

    - +
    Redis|bool

    True if the member exists and false if not.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->multi()
    -      ->del('myset')
    -      ->sadd('myset', 'foo', 'bar', 'baz')
    -      ->exec();
    -
    -// Will return true, as 'foo' is in the set
    -$redis->sismember('myset', 'foo');
    -
    -// Will return false, as 'not-in-set' is not in the set
    -$redis->sismember('myset', 'not-in-set');
    -?>

    True if the member exists and false if not.

    +

    Examples

    + + + + + +
    $redis->sismember('myset', 'mem1', 'mem2');
    +

    - + Redis|bool slaveof(string $host = NULL, int $port = 6379) deprecated @@ -13669,8 +13963,8 @@

    See also

    - -Redis::slaveof + +Redis::replicaof @@ -13683,7 +13977,7 @@

    See also

    - + Redis|bool replicaof(string $host = NULL, int $port = 6379) @@ -13719,17 +14013,7 @@

    Return Value

    Redis|bool

    Success if we were successfully able to start replicating a primary or -were able to promote teh replicat to a primary.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -// Attempt to become a replica of a Redis instance at 127.0.0.1:9999
    -$redis->slaveof('127.0.0.1', 9999);
    -
    -// When passed no arguments, PhpRedis will deliver the command `SLAVEOF NO ONE`
    -// attempting to promote the instance to a primary.
    -$redis->slaveof();
    -?>
    +were able to promote teh replicat to a primary.

    @@ -13760,13 +14044,28 @@

    See also

    +

    Examples

    + + + + + +
    $redis = new Redis(['host' => 'localhost']);
    +
    +// Attempt to become a replica of a Redis instance at 127.0.0.1:9999
    +$redis->replicaof('127.0.0.1', 9999);
    +
    +// When passed no arguments, PhpRedis will deliver the command `REPLICAOF NO ONE`
    +// attempting to promote the instance to a primary.
    +$redis->replicaof();
    +

    - + Redis|int|false touch(array|string $key_or_array, string ...$more_keys) @@ -13825,7 +14124,7 @@

    See also

    - + mixed slowlog(string $operation, int $length = 0) @@ -13849,12 +14148,7 @@

    Parameters

    be one of the following values: 'GET' - Retrieve the Redis slowlog as an array. 'LEN' - Retrieve the length of the slowlog. -'RESET' - Remove all slowlog entries.

    -
    <?php
    -$redis->slowlog('get', -1);  // Retrieve all slowlog entries.
    -$redis->slowlog('len');       // Retrieve slowlog length.
    -$redis->slowlog('reset');     // Reset the slowlog.
    -?>
    +'RESET' - Remove all slowlog entries.

    int @@ -13891,13 +14185,27 @@

    See also

    +

    Examples

    + + + + + + + + + + + +
    $redis->slowlog('get', -1);   // Retrieve all slowlog entries.
    $redis->slowlog('len');       // Retrieve slowlog length.
    $redis->slowlog('reset');     // Reset the slowlog.
    +

    - + mixed sort(string $key, array|null $options = null) @@ -13935,20 +14243,7 @@

    Return Value

    mixed

    This command can either return an array with the sorted data or the number of elements placed in a destination set when -using the STORE option.

    -
    <?php
    -$options = [
    -    'SORT'  => 'ASC'|| 'DESC' // Sort in descending or descending order.
    -    'ALPHA' => true || false  // Whether to sort alphanumerically.
    -    'LIMIT' => [0, 10]        // Return a subset of the data at offset, count
    -    'BY'    => 'weight_*'     // For each element in the key, read data from the
    -                                 external key weight_* and sort based on that value.
    -    'GET'   => 'weight_*'     // For each element in the source key, retrieve the
    -                                 data from key weight_* and return that in the result
    -                                 rather than the source keys' element.  This can
    -                                 be used in combination with 'BY'
    -];
    -?>
    +using the STORE option.

    @@ -13966,13 +14261,31 @@

    See also

    +

    Examples

    + + + + + +
    $options = [
    + 'SORT' => 'ASC'|| 'DESC' // Sort in descending or descending order.
    + 'ALPHA' => true || false // Whether to sort alphanumerically.
    + 'LIMIT' => [0, 10] // Return a subset of the data at offset, count
    + 'BY' => 'weight_*' // For each element in the key, read data from the
    + external key weight_* and sort based on that value.
    + 'GET' => 'weight_*' // For each element in the source key, retrieve the
    + data from key weight_* and return that in the result
    + rather than the source keys' element. This can
    + be used in combination with 'BY'
    +];
    +

    - + mixed sort_ro(string $key, array|null $options = null) @@ -14031,7 +14344,7 @@

    See also

    - + array sortAsc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14106,7 +14419,7 @@

    Return Value

    - + array sortAscAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14181,7 +14494,7 @@

    Return Value

    - + array sortDesc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14256,7 +14569,7 @@

    Return Value

    - + array sortDescAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated @@ -14331,7 +14644,7 @@

    Return Value

    - + Redis|int|false srem(string $key, mixed $value, mixed ...$other_values) @@ -14370,19 +14683,7 @@

    Return Value

    - +
    Redis|int|false

    The number of values removed from the set or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->pipeline()->del('set1')
    -                  ->sadd('set1', 'foo', 'bar', 'baz')
    -                  ->exec();
    -
    -var_dump($redis->sRem('set1', 'foo', 'bar', 'not-in-the-set'));
    -
    -// --- OUTPUT ---
    -// int(2)
    -?>

    The number of values removed from the set or false on failure.

    @@ -14400,13 +14701,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->sRem('set1', 'mem1', 'mem2', 'not-in-set');
    +

    - + array|false sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -14445,47 +14754,7 @@

    Parameters

    int $count

    A hint to Redis as to how many members it should scan in one command -before returning members for that iteration.

    -
    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('myset');
    -for ($i = 0; $i < 10000; $i++) {
    -    $redis->sAdd('myset', "member:$i");
    -}
    -$redis->sadd('myset', 'foofoo');
    -
    -$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
    -
    -$scanned = 0;
    -$it = NULL;
    -
    -// Without Redis::SCAN_RETRY we may receive empty results and
    -// a nonzero iterator.
    -do {
    -    // Scan members containing '5'
    -    $members = $redis->sscan('myset', $it, '*5*');
    -    foreach ($members as $member) {
    -         echo "NORETRY: $member\n";
    -         $scanned++;
    -    }
    -} while ($it != 0);
    -echo "TOTAL: $scanned\n";
    -
    -$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
    -
    -$scanned = 0;
    -$it = NULL;
    -
    -// With Redis::SCAN_RETRY PhpRedis will never return an empty array
    -// when the cursor is non-zero
    -while (($members = $redis->sscan('myset', $it, '*5*'))) {
    -    foreach ($members as $member) {
    -        echo "RETRY: $member\n";
    -        $scanned++;
    -    }
    -}
    -echo "TOTAL: $scanned\n";
    -?>
    +before returning members for that iteration.

    @@ -14526,13 +14795,56 @@

    See also

    +

    Examples

    + + + + + +
    $redis->del('myset');
    +for ($i = 0; $i < 10000; $i++) {
    + $redis->sAdd('myset', "member:$i");
    +}
    +$redis->sadd('myset', 'foofoo');
    +
    +$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
    +
    +$scanned = 0;
    +$it = NULL;
    +
    +// Without Redis::SCAN_RETRY we may receive empty results and
    +// a nonzero iterator.
    +do {
    + // Scan members containing '5'
    + $members = $redis->sscan('myset', $it, '*5*');
    + foreach ($members as $member) {
    + echo "NORETRY: $member\n";
    + $scanned++;
    + }
    +} while ($it != 0);
    +echo "TOTAL: $scanned\n";
    +
    +$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
    +
    +$scanned = 0;
    +$it = NULL;
    +
    +// With Redis::SCAN_RETRY PhpRedis will never return an empty array
    +// when the cursor is non-zero
    +while (($members = $redis->sscan('myset', $it, '*5*'))) {
    + foreach ($members as $member) {
    + echo "RETRY: $member\n";
    + $scanned++;
    + }
    +}
    +

    - + Redis|int|false strlen(string $key) @@ -14562,36 +14874,39 @@

    Return Value

    Redis|int|false

    The length of the string key if it exists, zero if it does not, and -false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('string');
    -
    -$redis->set('string', 'foo');
    -
    -// strlen('foo') == 3
    -$redis->strlen('string');
    -
    -$redis->append('string', 'bar');
    -
    -// strlen('foobar') == 6
    -$redis->strlen('string');
    -
    -?>
    +false on failure.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/strlen +
    + +

    Examples

    + + + + + +
    $redis->strlen('mykey');
    +

    - + bool subscribe(array $channels, callable $cb) @@ -14627,37 +14942,53 @@

    Return Value

    bool

    True on success, false on faiilure. Note that this command will block the -client in a subscribe loop, waiting for messages to arrive.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
    -    echo "[$channel]: $message\n";
    -
    -    // Unsubscribe from the message channel when we read 'quit'
    -    if ($message == 'quit') {
    -        echo "Unsubscribing from '$channel'\n";
    -        $redis->unsubscribe([$channel]);
    -    }
    -});
    -
    -// Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be
    -// broken and this command will execute.
    -echo "Subscribe loop ended\n";
    -?>
    +client in a subscribe loop, waiting for messages to arrive.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/subscribe +
    + +

    Examples

    + + + + + +
    $redis = new Redis(['host' => 'localhost']);
    +
    +$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
    + echo "[$channel]: $message\n";
    +
    + // Unsubscribe from the message channel when we read 'quit'
    + if ($message == 'quit') {
    + echo "Unsubscribing from '$channel'\n";
    + $redis->unsubscribe([$channel]);
    + }
    +});
    +
    +// Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be
    +// broken and this command will execute.
    +echo "Subscribe loop ended\n";
    +

    - + Redis|bool swapdb(int $src, int $dst) @@ -14693,44 +15024,7 @@

    Return Value

    - +
    Redis|bool

    Success if the databases could be swapped and false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->multi()->select(0)
    -               ->set('db0-key1', 'value1')->set('db0-key2', 'value2')
    -               ->select(1)
    -               ->set('db1-key1', 'value1')->set('db1-key2', 'value2')
    -               ->select(0)
    -               ->exec();
    -
    -// Array
    -// (
    -//     [0] => db0-key1
    -//     [1] => db0-key2
    -// )
    -print_r($redis->keys('*'));
    -
    -// Swap db0 and db1
    -$redis->swapdb(0, 1);
    -
    -// Array
    -// (
    -//     [0] => db1-key2
    -//     [1] => db1-key1
    -// )
    -print_r($redis->keys('*'));
    -
    -// Swap them back
    -$redis->swapdb(0, 1);
    -
    -// Array
    -// (
    -//     [0] => db0-key1
    -//     [1] => db0-key2
    -// )
    -print_r($redis->keys('*'));
    -?>

    Success if the databases could be swapped and false on failure.

    @@ -14755,13 +15049,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->select(0);
    +$redis->set('db0-key', 'db0-value');
    +$redis->swapdb(0, 1);
    +$redis->get('db0-key');
    +

    - + Redis|array time() @@ -14781,17 +15086,7 @@

    Return Value

    Redis|array

    two element array consisting of a Unix Timestamp and the number of microseconds -elapsed since the second.

    -
    
    -<?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -// Array
    -// (
    -//     [0] => 1667271026
    -//     [1] => 355678
    -// )
    -print_r($redis->time());
    +elapsed since the second.

    @@ -14809,13 +15104,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->time();
    +

    - + Redis|int|false ttl(string $key) @@ -14846,39 +15149,39 @@

    Return Value

    Redis|int|false

    (a) The number of seconds until the key expires, or -1 if the key has no expiration, and -2 if the key does not exist. In the event of an -error, this command will return false.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->multi()
    -      ->setex('expires_in_60s', 60, 'test')
    -      ->set('doesnt_expire', 'persistent')
    -      ->del('not_a_key')
    -      ->exec();
    -
    -// Returns <= 60
    -$redis->ttl('expires_in_60s');
    -
    -// Returns -1
    -$redis->ttl('doesnt_expire');
    -
    -// Returns -2 (key doesn't exist)
    -$redis->ttl('not_a_key');
    -
    -?>
    +error, this command will return false.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/ttl +
    + +

    Examples

    + + + + + +
    $redis->ttl('mykey');
    +

    - + Redis|int|false type(string $key) @@ -14915,7 +15218,23 @@

    Return Value

    Redis::REDIS_LIST Redis::REDIS_ZSET Redis::REDIS_HASH -Redis::REDIS_STREAM
    +Redis::REDIS_STREAM +
    <?php
    +$redis = new Redis(['host' => 'localhost']);
    +
    +// NOTE:  Never use 'KEYS' in production!
    +$keys = $redis->keys('*');
    +
    +$redis->pipeline();
    +foreach ($keys as $key) {
    +    $redis->type($key);
    +}
    +
    +$ktypes = array_combine($keys, $redis->exec());
    +
    +// Print each key with its corresponding type
    +print_r($ktypes);
    +?>
    @@ -14939,7 +15258,7 @@

    See also

    Return Value

    - +
    Redis|int|false

    The number of keys deleted or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -// OPTION 1:  Called with a single array of keys
    -$redis->unlink(['key1', 'key2', 'key3']);
    -
    -// OPTION 2:  Called with a variadic number of arguments
    -$redis->unlink('key1', 'key2', 'key3');
    -?>

    The number of keys deleted or false on failure.

    @@ -15016,13 +15326,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->unlink('key1', 'key2', 'key3');
    $redis->unlink(['key1', 'key2', 'key3']);
    +

    - + Redis|array|bool unsubscribe(array $channels) @@ -15071,7 +15392,22 @@

    See also

    Redis::subscribe - + + 'localhost']); + +$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { + if ($message == 'quit') { + echo "$channel => 'quit' detected, unsubscribing!\n"; + $redis->unsubscribe([$channel]); + } else { + echo "$channel => $message\n"; + } +}); + +echo "We've unsubscribed from both channels, exiting\n"; +?> + @@ -15082,7 +15418,7 @@

    See also

    - + Redis|bool unwatch() @@ -15138,8 +15474,8 @@

    See also

    - - bool|Redis + + Redis|bool watch(array|string $key, string ...$other_keys)

    @@ -15148,8 +15484,7 @@

    -

    No description

    - +

    Watch one or more keys for conditional execution of a transaction.

    Parameters

    @@ -15163,7 +15498,8 @@

    Parameters

    string ...$other_keys - +

    If the first argument was passed as a string, any number of additional +string key names may be passed variadically.

    @@ -15172,13 +15508,55 @@

    Return Value

    - - - -
    bool|Redis
    + Redis|bool +
    <?php
    +
    +$redis1 = new Redis(['host' => 'localhost']);
    +$redis2 = new Redis(['host' => 'localhost']);
    +
    +// Start watching 'incr-key'
    +$redis1->watch('incr-key');
    +
    +// Retrieve its value.
    +$val = $redis1->get('incr-key');
    +
    +// A second client modifies 'incr-key' after we read it.
    +$redis2->set('incr-key', 0);
    +
    +// Because another client changed the value of 'incr-key' after we read it, this
    +// is no longer a proper increment operation, but because we are `WATCH`ing the
    +// key, this transaction will fail and we can try again.
    +//
    +// If were to comment out the above `$redis2->set('incr-key', 0)` line the
    +// transaction would succeed.
    +$redis1->multi();
    +$redis1->set('incr-key', $val + 1);
    +$res = $redis1->exec();
    +
    +// bool(false)
    +var_dump($res);
    + + +

    See also

    + + + + + + + + + + +
    + https://redis.io/commands/watch +
    + https://redis.io/commands/unwatch +
    +

    @@ -15186,7 +15564,7 @@

    Return Value

    - + int|false wait(int $numreplicas, int $timeout) @@ -15245,7 +15623,7 @@

    See also

    - + int|false xack(string $key, string $group, array $ids) @@ -15255,8 +15633,8 @@

    -

    No description

    - +

    Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but +not yet acknowledged by XACK.)

    Parameters

    @@ -15291,6 +15669,60 @@

    Return Value

    +

    See also

    + + + + + + + + + + + + + + +
    + https://redis.io/commands/xack +
    + https://redis.io/commands/xreadgroup +
    + +Redis::xack + + 'localhost']); + +$redis->del('ships'); + +$redis->xAdd('ships', '*', ['name' => 'Enterprise']); +$redis->xAdd('ships', '*', ['name' => 'Defiant']); + +$redis->xGroup('CREATE', 'ships', 'Federation', '0-0'); + +// Consume a single message with the consumer group 'Federation' +$ship = $redis->xReadGroup('Federation', 'Picard', ['ships' => '>'], 1); + +/* Retrieve the ID of the message we read. +assert(isset($ship['ships'])); +$id = key($ship['ships']); + +// The message we just read is now pending. +$res = $redis->xPending('ships', 'Federation')); +var_dump($res); + +// We can tell Redis we were able to process the message by using XACK +$res = $redis->xAck('ships', 'Federation', [$id]); +assert($res === 1); + +// The message should no longer be pending. +$res = $redis->xPending('ships', 'Federation'); +var_dump($res); +?> +
    +

    @@ -15298,7 +15730,7 @@

    Return Value

    - + Redis|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) @@ -15349,34 +15781,7 @@

    Parameters

    bool $nomkstream -

    If passed as TRUE, the stream must exist for Redis to append the message.

    -
    </php
    -<?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('ds9-season-1');
    -
    -$redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']);
    -$redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']);
    -$redis->xAdd('ds9-season-1', '1-3', ['title' => 'Emissary Part 2']);
    -$redis->xAdd('ds9-season-1', '1-4', ['title' => 'Past Prologue']);
    -
    -// Array
    -// (
    -//     [1-1] => Array
    -//         (
    -//             [title] => Emissary Part 1
    -//         )
    -//
    -//     [1-2] => Array
    -//         (
    -//             [title] => A Man Alone
    -//         )
    -//
    -// )
    -$redis->xRange('ds9-season-1', '1-1', '1-2');
    -?>
    -?>
    +

    If passed as TRUE, the stream must exist for Redis to append the message.

    @@ -15404,13 +15809,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']);
    $redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']);
    +

    - + Redis|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) @@ -15420,8 +15836,7 @@

    -

    No description

    - +

    This command allows a consumer to claim pending messages that have been idle for a specified period of time.

    Its purpose is to provide a mechanism for picking up messages that may have had a failed consumer.

    Parameters

    @@ -15430,37 +15845,37 @@

    Parameters

    string $key - +

    The stream to check.

    string $group - +

    The consumer group to query.

    string $consumer - +

    Which consumer to check.

    int $min_idle - +

    The minimum time in milliseconds for the message to have been pending.

    string $start - +

    The minimum message id to check.

    int $count - +

    An optional limit on how many messages are returned.

    bool $justid - +

    If the client only wants message IDs and not all of their data.

    @@ -15470,12 +15885,96 @@

    Return Value

    - +
    Redis|bool|array

    An array of pending IDs or false if there are none, or on failure.

    +
    <?php
    +$redis = new Redis(['host' => 'localhost']);
    +
    +$redis->del('ships');
    +
    +$redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
    +
    +$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
    +
    +// Consume the ['name' => 'Defiant'] message
    +$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
    +
    +// The "Jem'Hadar" consumer has the message presently
    +$pending = $redis->xPending('ships', 'combatants');
    +
    +//array(4) {
    +//  [0]=>
    +//  int(1)
    +//  [1]=>
    +//  string(10) "1424-74205"
    +//  [2]=>
    +//  string(10) "1424-74205"
    +//  [3]=>
    +//  array(1) {
    +//    [0]=>
    +//    array(2) {
    +//      [0]=>
    +//      string(9) "Jem'Hadar"
    +//      [1]=>
    +//      string(1) "1"
    +//    }
    +//  }
    +//}
    +var_dump($pending);
    +
    +// Asssume control of the pending message with a different consumer.
    +$res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');
    +
    +// Now the 'Sisko' consumer owns the message
    +$pending = $redis->xPending('ships', 'combatants');
    +
    +// array(4) {
    +//   [0]=>
    +//   int(1)
    +//   [1]=>
    +//   string(10) "1424-74205"
    +//   [2]=>
    +//   string(10) "1424-74205"
    +//   [3]=>
    +//   array(1) {
    +//     [0]=>
    +//     array(2) {
    +//       [0]=>
    +//       string(5) "Sisko"
    +//       [1]=>
    +//       string(1) "1"
    +//     }
    +//   }
    +// }
    +var_dump($pending);
    +?>
    +

    See also

    + + + + + + + + + + + + + + +
    + https://redis.io/commands/xautoclaim +
    + https://redis.io/commands/xclaim +
    + https://redis.io/docs/data-types/streams-tutorial/ +
    +

    @@ -15483,8 +15982,8 @@

    Return Value

    - - Redis|bool|array + + Redis|array|bool xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options)

    @@ -15493,8 +15992,8 @@

    -

    No description

    - +

    This method allows a consumer to take ownership of pending stream entries, by ID. Another +command that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

    Parameters

    @@ -15503,17 +16002,17 @@

    Parameters

    string $key - +

    The stream we wish to claim messages for.

    string $group - +

    Our consumer group.

    string $consumer - +

    Our consumer.

    int @@ -15528,7 +16027,19 @@

    Parameters

    array $options - +

    An options array that modifies how the command operates.

    +
    // Following is an options array describing every option you can pass.  Note that
    +// 'IDLE', and 'TIME' are mutually exclusive.
    +$options = [
    +    'IDLE'       => 3            // Set the idle time of the message to a 3.  By default the
    +                                 // idle time is set to zero.
    +    'TIME'       => 1000*time()  // Same as IDLE except it takes a unix timestamp in milliseconds.
    +    'RETRYCOUNT' => 0            // Set the retry counter to zero.  By default XCLAIM doesn't modify
    +                                 // the counter.
    +    'FORCE'                      // Creates the pending message entry even if IDs are not already
    +                                 // in the PEL with another client.
    +    'JUSTID'                     // Return only an array of IDs rather than the messages themselves.
    +];
    @@ -15537,13 +16048,99 @@

    Return Value

    - - + +
    Redis|bool|arrayRedis|array|bool

    An array of claimed messags or false on failure.

    +
    <?php
    +$redis = new Redis(['host' => 'localhost']);
    +
    +$redis->del('ships');
    +
    +$redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
    +
    +$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
    +
    +// Consume the ['name' => 'Defiant'] message
    +$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
    +
    +// The "Jem'Hadar" consumer has the message presently
    +$pending = $redis->xPending('ships', 'combatants');
    +
    +//array(4) {
    +//  [0]=>
    +//  int(1)
    +//  [1]=>
    +//  string(10) "1424-74205"
    +//  [2]=>
    +//  string(10) "1424-74205"
    +//  [3]=>
    +//  array(1) {
    +//    [0]=>
    +//    array(2) {
    +//      [0]=>
    +//      string(9) "Jem'Hadar"
    +//      [1]=>
    +//      string(1) "1"
    +//    }
    +//  }
    +//}
    +var_dump($pending);
    +
    +assert($pending && isset($pending[1]));
    +
    +// Claim the message by ID.
    +$claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']);
    +
    +// array(1) {
    +//   [0]=>
    +//   string(10) "1424-74205"
    +// }
    +var_dump($claimed);
    +
    +// Now the 'Sisko' consumer owns the message
    +$pending = $redis->xPending('ships', 'combatants');
    +
    +// array(4) {
    +//   [0]=>
    +//   int(1)
    +//   [1]=>
    +//   string(10) "1424-74205"
    +//   [2]=>
    +//   string(10) "1424-74205"
    +//   [3]=>
    +//   array(1) {
    +//     [0]=>
    +//     array(2) {
    +//       [0]=>
    +//       string(5) "Sisko"
    +//       [1]=>
    +//       string(1) "1"
    +//     }
    +//   }
    +// }
    +var_dump($pending);
    +?>
    +

    See also

    + + + + + + + + + + +
    + https://redis.io/commands/xclaim +
    + https://redis.io/commands/xautoclaim. +
    +

    @@ -15551,7 +16148,7 @@

    Return Value

    - + Redis|int|false xdel(string $key, array $ids) @@ -15585,53 +16182,28 @@

    Return Value

    - +
    Redis|int|false

    The number of messages removed or false on failure.

    -
    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('stream');
    -
    -for ($a = 1; $a <= 3; $a++) {
    -    for ($b = 1; $b <= 2; $b++) {
    -        $redis->xAdd('stream', "$a-$b", ['id' => "$a-$b"]);
    -    }
    -}
    -
    -// Remove some elements
    -$redis->xDel('stream', ['1-1', '2-1', '3-1']);
    -
    -// Array
    -// (
    -//     [1-2] => Array
    -//         (
    -//             [id] => 1-2
    -//         )
    -//
    -//     [2-2] => Array
    -//         (
    -//             [id] => 2-2
    -//         )
    -//
    -//     [3-2] => Array
    -//         (
    -//             [id] => 3-2
    -//         )
    -//
    -// )
    -$redis->xRange('stream', '-', '+');
    -?>

    The number of messages removed or false on failure.

    +

    Examples

    + + + + + +
    $redis->xDel('stream', ['1-1', '2-1', '3-1']);
    +

    - + mixed xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) @@ -15726,7 +16298,7 @@

    See also

    - + mixed xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) @@ -15770,38 +16342,45 @@

    Return Value

    - +
    mixed

    This command can return different things depending on the operation being called.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('stream');
    -
    -$redis->xAdd('stream', "0-1", ['payload' => '0-1']);
    -$redis->xAdd('stream', "0-2", ['payload' => '0-2']);
    -$redis->xAdd('stream', "0-3", ['payload' => '0-3']);
    -
    -// Retrieve any consmers for a given key
    -$redis->xInfo('CONSUMERS', 'stream');
    -
    -// Retrieve any groups for a given key
    -$redis->xInfo('GROUPS', 'stream');
    -
    -// Retrieve general stream information along with messages
    -$redis->xInfo('STREAM', 'stream');
    -?>

    This command can return different things depending on the operation being called.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/xinfo +
    + +

    Examples

    + + + + + + + + + + + +
    $redis->xInfo('CONSUMERS', 'stream');
    $redis->xInfo('GROUPS', 'stream');
    $redis->xInfo('STREAM', 'stream');
    +

    - + Redis|int|false xlen(string $key) @@ -15830,17 +16409,7 @@

    Return Value

    - +
    Redis|int|false

    The number of messages or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('stream');
    -$redis->xadd('stream', '*', ['first' => 'message']);
    -$redis->xadd('stream', '*', ['second' => 'message']);
    -
    -// int(2)
    -$redis->xLen('stream');
    -?>

    The number of messages or false on failure.

    @@ -15858,13 +16427,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->xLen('stream');
    +

    - + Redis|array|false xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) @@ -15949,7 +16526,7 @@

    See also

    - + Redis|array|bool xrange(string $key, string $start, string $end, int $count = -1) @@ -15993,37 +16570,7 @@

    Return Value

    - +
    Redis|array|bool

    The entries in the stream within the requested range or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('stream');
    -
    -for ($i = 0; $i < 2; $i++) {
    -    for ($j = 1; $j <= 2; $j++) {
    -        $redis->xAdd('stream', "$i-$j", ['message' => "$i:$j"]);
    -    }
    -}
    -
    -//Array
    -//(
    -//    [0-1] => Array
    -//        (
    -//            [message] => 0:1
    -//        )
    -//
    -//    [0-2] => Array
    -//        (
    -//            [message] => 0:2
    -//        )
    -//
    -//)
    -$redis->xRange('stream', '0-1', '0-2');
    -
    -// '-' and '+' are special values which mean 'minimum possible',
    -// and 'maximum possible' id, respectively.
    -$redis->xRange('stream', '-', '+');
    -?>

    The entries in the stream within the requested range or false on failure.

    @@ -16041,13 +16588,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->xRange('stream', '0-1', '0-2');
    $redis->xRange('stream', '-', '+');
    +

    - + Redis|array|bool xread(array $streams, int $count = -1, int $block = -1) @@ -16077,46 +16635,7 @@

    Parameters

    int $block

    An optional maximum number of milliseconds to block the caller if no -data is available on any of the provided streams.

    -
    $redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('s03', 's03');
    -
    -$redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']);
    -$redis->xAdd('s03', '3-2', ['title' => 'The Search, Part II']);
    -$redis->xAdd('s03', '3-3', ['title' => 'The House Of Quark']);
    -
    -$redis->xAdd('s04', '4-1', ['title' => 'The Way of the Warrior']);
    -$redis->xAdd('s04', '4-3', ['title' => 'The Visitor']);
    -$redis->xAdd('s04', '4-4', ['title' => 'Hippocratic Oath']);
    -
    -// Array
    -// (
    -//     [s03] => Array
    -//         (
    -//             [3-3] => Array
    -//                 (
    -//                     [title] => The House Of Quark
    -//                 )
    -//
    -//         )
    -//
    -//     [s04] => Array
    -//         (
    -//             [4-3] => Array
    -//                 (
    -//                     [title] => The Visitor
    -//                 )
    -//
    -//             [4-4] => Array
    -//                 (
    -//                     [title] => Hippocratic Oath
    -//                 )
    -//
    -//         )
    -//
    -// )
    -print_r($redis->xRead(['s03' => '3-2', 's04' => '4-1']));
    +data is available on any of the provided streams.

    @@ -16126,7 +16645,7 @@

    Return Value

    - +
    Redis|array|bool

    An array of read elements or false if there aren't any.

    @@ -16144,13 +16663,28 @@

    See also

    +

    Examples

    + + + + + +
    $redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']);
    +$redis->xAdd('s03', '3-2', ['title' => 'The Search, Part II']);
    +$redis->xAdd('s03', '3-3', ['title' => 'The House Of Quark']);
    +$redis->xAdd('s04', '4-1', ['title' => 'The Way of the Warrior']);
    +$redis->xAdd('s04', '4-3', ['title' => 'The Visitor']);
    +$redis->xAdd('s04', '4-4', ['title' => 'Hippocratic Oath']);
    +
    +$redis->xRead(['s03' => '3-2', 's04' => '4-1']);
    +

    - + Redis|array|bool xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) @@ -16196,63 +16730,59 @@

    Parameters

    Return Value

    - - - - +
    Redis|array|bool

    Zero or more unread messages or false on failure.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('episodes');
    -
    -// Create a consumer group (and stream)
    -$redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true);
    -
    -// Add a couple of messages to the stream
    -$redis->xAdd('episodes', '1-1', ['title' => 'Emissary: Part 1']);
    -$redis->xAdd('episodes', '1-2', ['title' => 'A Man Alone']);
    -
    -// Now read some messages with our consumer group
    -$messages = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
    -
    -// After having read the two messages, add another
    -$redis->xAdd('episodes', '1-3', ['title' => 'Emissary: Part 2']);
    -
    -// Acknowledge the first two read messages
    -foreach ($messages as $stream => $stream_messages) {
    -    $ids = array_keys($stream_messages);
    -    $redis->xAck('stream', 'ds9', $ids);
    -}
    -
    -// We can now pick up where we left off, and will only get the final message
    -$msgs = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
    -
    -// array(1) {
    -//   ["episodes"]=>
    -//   array(1) {
    -//     ["1-3"]=>
    -//     array(1) {
    -//       ["title"]=>
    -//       string(16) "Emissary: Part 2"
    -//     }
    -//   }
    -// }
    -var_dump($msgs);
    -?>
    + + +
    Redis|array|bool

    Zero or more unread messages or false on failure.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/xreadgroup +
    + +

    Examples

    + + + + + +
    $redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true);
    +
    +$redis->xAdd('episodes', '1-1', ['title' => 'Emissary: Part 1']);
    +$redis->xAdd('episodes', '1-2', ['title' => 'A Man Alone']);
    +
    +$messages = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
    +
    +// After having read the two messages, add another
    +$redis->xAdd('episodes', '1-3', ['title' => 'Emissary: Part 2']);
    +
    +// Acknowledge the first two read messages
    +foreach ($messages as $stream => $stream_messages) {
    + $ids = array_keys($stream_messages);
    + $redis->xAck('stream', 'ds9', $ids);
    +}
    +
    +// We can now pick up where we left off, and will only get the final message
    +$msgs = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
    +

    - + Redis|array|bool xrevrange(string $key, string $end, string $start, int $count = -1) @@ -16296,37 +16826,7 @@

    Return Value

    - +
    Redis|array|bool

    The entries within the requested range, from newest to oldest.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('stream');
    -
    -for ($i = 0; $i < 2; $i++) {
    -    for ($j = 1; $j <= 2; $j++) {
    -        $redis->xAdd('stream', "$i-$j", ['message' => "$i:$j"]);
    -    }
    -}
    -
    -// Array
    -// (
    -//     [0-2] => Array
    -//         (
    -//             [message] => 0:2
    -//         )
    -//
    -//     [0-1] => Array
    -//         (
    -//             [message] => 0:1
    -//         )
    -//
    -// )
    -$redis->xRevRange('stream', '0-2', '0-1');
    -
    -// '-' and '+' are special values which mean 'minimum possible',
    -// and 'maximum possible' id, respectively.
    -$redis->xRevRange('stream', '+', '-');
    -?>

    The entries within the requested range, from newest to oldest.

    @@ -16350,13 +16850,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->xRevRange('stream', '0-2', '0-1');
    $redis->xRevRange('stream', '+', '-');
    +

    - + Redis|int|false xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) @@ -16399,57 +16910,7 @@

    Parameters

    int $limit -

    An optional upper bound on how many entries to trim during the command.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('stream');
    -$redis->xAdd('stream', '1-1', ['one' => 'one']);
    -$redis->xAdd('stream', '1-2', ['one' => 'two']);
    -$redis->xAdd('stream', '2-1', ['two' => 'one']);
    -$redis->xAdd('stream', '2-2', ['two' => 'two']);
    -
    -// Trim to three elemn
    -$redis->xTrim('stream', 3);
    -
    -// Array
    -// (
    -//     [1-2] => Array
    -//         (
    -//             [one] => two
    -//         )
    -//
    -//     [2-1] => Array
    -//         (
    -//             [two] => one
    -//         )
    -//
    -//     [2-2] => Array
    -//         (
    -//             [two] => two
    -//         )
    -//
    -// )
    -$redis->xRange('stream', '-', '+');
    -
    -// Now let's trim everything older than '2-1'
    -$redis->xTrim('stream', '2-1', false, true);
    -
    -// Array
    -// (
    -//     [2-1] => Array
    -//         (
    -//             [two] => one
    -//         )
    -//
    -//     [2-2] => Array
    -//         (
    -//             [two] => two
    -//         )
    -//
    -// )
    -print_r($redis->xRange('stream', '-', '+'));
    -?>
    +

    An optional upper bound on how many entries to trim during the command.

    @@ -16459,7 +16920,7 @@

    Return Value

    - +
    Redis|int|false

    The number of entries deleted from the stream.

    @@ -16477,13 +16938,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->xTrim('stream', 3);
    $redis->xTrim('stream', '2-1', false, true);
    +

    - + Redis|int|false zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) @@ -16513,9 +16985,20 @@

    Parameters

    mixed ...$more_scores_and_mems -

    A variadic number of additional scores and members.

    +

    A variadic number of additional scores and members.

    + + + + +

    Return Value

    + + + + + - -
    Redis|int|false

    The return value varies depending on the options passed.

    Following is information about the options that may be passed as the scond argument:

    -
    $options = [
    +
    
    +$options = [
         'NX',       # Only update elements that already exist
         'NX',       # Only add new elements but don't update existing ones.
     
    @@ -16531,41 +17014,7 @@ 

    Parameters

    ]; Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis will send whichever one is last in - the options array. -
    -

    <?php -$redis = new Redis(['host' => 'localhost']);

    -

    $redis->del('zs');

    -

    // Add three new elements to our zset -$redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third');

    -

    // Array -// ( -// [first] => 1 -// [second] => 2 -// [third] => 3 -// ) -$redis->zRange('zs', 0, -1, true);

    -

    // Update only existing elements. Note that 'new-element' isn't added -$redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element');

    -

    // Array -// ( -// [first] => 1 -// [third] => 3 -// [second] => 8 -// ) -print_r($redis->zRange('zs', 0, -1, true)); -?>

    -
    - - -

    Return Value

    - - - - - + the options array.
    Redis|int|false
    @@ -16583,13 +17032,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third');
    $redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element');
    +

    - + Redis|int|false zCard(string $key) @@ -16618,16 +17078,7 @@

    Return Value

    - +
    Redis|int|false

    The number of elements in the set or false on failure

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -$redis->zAdd('zs', 0, 'a', 1, 'b', 2, 'c');
    -
    -// count(['a', 'b', 'c']) == 3
    -$redis->zCard('zs');
    -?>

    The number of elements in the set or false on failure

    @@ -16645,13 +17096,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zCard('zs');
    +

    - + Redis|int|false zCount(string $key, string $start, string $end) @@ -16708,13 +17167,27 @@

    See also

    +

    Examples

    + + + + + + + + + + + +
    $redis->zCount('fruit-rankings', '0', '+inf');
    $redis->zCount('fruit-rankings', 50, 60);
    $redis->zCount('fruit-rankings', '-inf', 0);
    +

    - + Redis|float|false zIncrBy(string $key, float $value, mixed $member) @@ -16753,19 +17226,7 @@

    Return Value

    - +
    Redis|float|false

    The new score of the member or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -$redis->zAdd('zs', 0, 'apples', 2, 'bananas');
    -
    -// 2 + 5.0 == 7
    -print_r($redis->zIncrBy('zs', 5.0, 'bananas'));
    -
    -// new element so 0 + 2.0 == 2
    -print_r($redis->zIncrBy('zs', 2.0, 'eggplants'));
    -?>

    The new score of the member or false on failure.

    @@ -16783,13 +17244,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->zIncrBy('zs', 5.0, 'bananas');
    $redis->zIncrBy('zs', 2.0, 'eggplants');
    +

    - + Redis|int|false zLexCount(string $key, string $min, string $max) @@ -16829,19 +17301,7 @@

    Return Value

    - +
    Redis|int|false

    The number of members that fall within the range or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('captains');
    -$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
    -
    -count(['Archer', 'Janeway', 'Kirk', 'Picard']) == 4
    -$redis->zLexCount('captains', '[A', '[S');
    -
    -count(['Kirk', 'Picard']) == 2
    -$redis->zRangeByLex('captains', '[A', '[S', 2, 2);
    -?>

    The number of members that fall within the range or false on failure.

    @@ -16859,13 +17319,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
    +$redis->zLexCount('captains', '[A', '[S');
    +

    - + Redis|array|false zMscore(string $key, mixed $member, mixed ...$other_members) @@ -16904,30 +17373,7 @@

    Return Value

    - +
    Redis|array|false

    An array of the scores of the requested elements.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -
    -$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
    -
    -// array(2) {
    -//   [0]=>
    -//   float(0)
    -//   [1]=>
    -//   float(2)
    -// }
    -$redis->zMScore('zs', 'zero', 'two');
    -
    -// array(2) {
    -//   [0]=>
    -//   float(1)
    -//   [1]=>
    -//   bool(false)
    -// }
    -$redis->zMScore('zs', 'one', 'not-a-member');
    -?>

    An array of the scores of the requested elements.

    @@ -16945,13 +17391,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
    +
    +$redis->zMScore('zs', 'zero', 'two');
    +$redis->zMScore('zs', 'one', 'not-a-member');
    +

    - + Redis|array|false zPopMax(string $key, int $count = null) @@ -16985,26 +17442,7 @@

    Return Value

    - +
    Redis|array|false

    All of the popped elements with scores or false on fialure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
    -
    -// Array
    -// (
    -//     [three] => 3
    -// )
    -print_r($redis->zPopMax('zs'));
    -
    -// Array
    -// (
    -//     [two] => 2
    -//     [one] => 1
    -// )
    -print_r($redis->zPopMax('zs', 2));
    -?>

    All of the popped elements with scores or false on fialure.

    @@ -17022,13 +17460,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
    +
    +$redis->zPopMax('zs');
    +$redis->zPopMax('zs', 2);.
    +

    - + Redis|array|false zPopMin(string $key, int $count = null) @@ -17062,26 +17511,7 @@

    Return Value

    - +
    Redis|array|false

    The popped elements with their scores or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
    -
    -// Array
    -// (
    -//     [zero] => 0
    -// )
    -$redis->zPopMin('zs');
    -
    -// Array
    -// (
    -//     [one] => 1
    -//     [two] => 2
    -// )
    -$redis->zPopMin('zs', 2);
    -?>

    The popped elements with their scores or false on failure.

    @@ -17099,13 +17529,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
    +
    +$redis->zPopMin('zs');
    +$redis->zPopMin('zs', 2);
    +

    - + Redis|array|false zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) @@ -17142,7 +17583,15 @@

    Parameters

    $options

    This value may either be an array of options to pass to the command, or for historical purposes a boolean which -controls just the 'WITHSCORES' option.

    +controls just the 'WITHSCORES' option.

    +
    $options = [
    +    'WITHSCORES' => true,     // Return both scores and members.
    +    'LIMIT'      => [10, 10], // Start at offset 10 and return 10 elements.
    +    'REV'                     // Return the elements in reverse order
    +    'BYSCORE',                // Treat `start` and `end` as scores instead
    +    'BYLEX'                   // Treat `start` and `end` as lexicographical values.
    +];
    +

    Note: 'BYLEX' and 'BYSCORE' are mutually exclusive.

    @@ -17152,18 +17601,7 @@

    Return Value

    - +
    Redis|array|false

    An array with matching elements or false on failure.

    -

    Detailed description of options array:

    -
    <?php
    -$options = [
    -    'WITHSCORES' => true,     // Return both scores and members.
    -    'LIMIT'      => [10, 10], // Start at offset 10 and return 10 elements.
    -    'REV'                     // Return the elements in reverse order
    -    'BYSCORE',                // Treat `start` and `end` as scores instead
    -    'BYLEX'                   // Treat `start` and `end` as lexicographical values.
    -];
    -?>
    -

    Note: 'BYLEX' and 'BYSCORE' are mutually exclusive.

    An array with matching elements or false on failure.

    @@ -17181,13 +17619,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->zRange('zset', 0, -1);
    $redis->zRange('zset', '-inf', 'inf', ['byscore' => true]);
    +

    - + Redis|array|false zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) @@ -17236,29 +17685,7 @@

    Return Value

    - +
    Redis|array|false

    An array of matching elements or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('captains');
    -$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
    -
    -// Array
    -// (
    -//     [0] => Archer
    -//     [1] => Janeway
    -//     [2] => Kirk
    -//     [3] => Picard
    -// )
    -$redis->zRangeByLex('captains', '[A', '[S');
    -
    -// Array
    -// (
    -//     [0] => Kirk
    -//     [1] => Picard
    -// )
    -$redis->zRangeByLex('captains', '[A', '[S', 2, 2);
    -?>

    An array of matching elements or false on failure.

    @@ -17276,13 +17703,25 @@

    See also

    +

    Examples

    + + + + + +
    $redis = new Redis(['host' => 'localhost']);
    +$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
    +
    +$redis->zRangeByLex('captains', '[A', '[S');
    +$redis->zRangeByLex('captains', '[A', '[S', 2, 2);
    +

    - + Redis|array|false zRangeByScore(string $key, string $start, string $end, array $options = []) @@ -17329,52 +17768,7 @@

    Return Value

    - +
    Redis|array|false

    The number of matching elements or false on failure.

    -
    </php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -
    -for ($i = 0; $i < 50; $i++) {
    -    $redis->zAdd('zs', $i, "mem:$i");
    -}
    -
    -// Array
    -// (
    -//     [0] => mem:0
    -//     [1] => mem:1
    -//     [2] => mem:2
    -//     [3] => mem:3
    -//     [4] => mem:4
    -// )
    -$redis->zRangeByScore('zs', 0, 4);
    -
    -// Array
    -// (
    -//     [mem:20] => 20
    -//     [mem:21] => 21
    -//     [mem:22] => 22
    -//     [mem:23] => 23
    -//     [mem:24] => 24
    -//     [mem:25] => 25
    -//     [mem:26] => 26
    -//     [mem:27] => 27
    -//     [mem:28] => 28
    -//     [mem:29] => 29
    -//     [mem:30] => 30
    -// )
    -$redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]);
    -
    -// Array
    -// (
    -//     [mem:25] => 25
    -//     [mem:26] => 26
    -//     [mem:27] => 27
    -//     [mem:28] => 28
    -//     [mem:29] => 29
    -// )
    -$redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]);
    -?>

    The number of matching elements or false on failure.

    @@ -17392,13 +17786,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]);
    $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]);
    +

    - + Redis|int|false zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) @@ -17448,8 +17853,7 @@

    Return Value

    - +
    Redis|int|false

    The number of elements stored in $dstkey or false on failure.

    -

    See Redis::zRange for a full description of the possible options.

    The number of elements stored in $dstkey or false on failure.

    @@ -17479,7 +17883,7 @@

    See also

    - + Redis|string|array zRandMember(string $key, array $options = null) @@ -17500,23 +17904,13 @@

    Parameters

    $key

    The sorted set to pull random members from.

    - - array - $options -

    One or more options that determine exactly how the command operates.

    -
                       OPTION       TYPE     MEANING
    -                   'COUNT'      int      The number of random members to return.
    -                   'WITHSCORES' bool     Whether to return scores and members instead of
    -                                         just members.
    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->multi()->del('zs')->zadd('zs', 1, 'one', 2, 'two', 3, 'three')->exec();
    -
    -// Return two random members from our set, with scores
    -$redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]);
    -
    -?>
    + + array + $options +

    One or more options that determine exactly how the command operates.

    +

    OPTION TYPE MEANING +'COUNT' int The number of random members to return. +'WITHSCORES' bool Whether to return scores and members instead of

    @@ -17526,7 +17920,7 @@

    Return Value

    - +
    Redis|string|array

    One ore more random elements.

    @@ -17544,13 +17938,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]);
    +

    - + Redis|int|false zRank(string $key, mixed $member) @@ -17584,7 +17986,7 @@

    Return Value

    - +
    Redis|int|false

    The rank of the requested member.

    @@ -17602,13 +18004,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->zRank('zs', 'zero');
    $redis->zRank('zs', 'three');
    +

    - + Redis|int|false zRem(mixed $key, mixed $member, mixed ...$other_members) @@ -17647,28 +18060,7 @@

    Return Value

    - +
    Redis|int|false

    The number of members that were actually removed or false on failure.

    -
    
    -<?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -
    -for ($i = 0; $i < 10; $i++) {
    -    $redis->zAdd('zs', $i, "mem:$i");
    -}
    -
    -// Remove a few elements
    -$redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9');
    -
    -// Array
    -// (
    -//     [0] => mem:3
    -//     [1] => mem:4
    -//     [2] => mem:5
    -// )
    -$redis->zRange('zs', 0, -1);
    -?>

    The number of members that were actually removed or false on failure.

    @@ -17686,13 +18078,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9');
    +

    - + Redis|int|false zRemRangeByLex(string $key, string $min, string $max) @@ -17731,36 +18131,7 @@

    Return Value

    - +
    Redis|int|false

    The number of elements removed from the set or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->pipeline()->del('zs')
    -               ->zAdd('zs', 1, 'apple', 2, 'banana', 3, 'carrot', 4, 'date', 5, 'eggplant')
    -               ->exec();
    -
    -// Remove a* (inclusive) .. b* (exclusive), meaning 'apple' will be removed, but 'banana' not
    -$redis->zRemRangeByLex('zs', '[a', '(b');
    -
    -// Array
    -// (
    -//     [0] => banana
    -//     [1] => carrot
    -//     [2] => date
    -//     [3] => eggplant
    -// )
    -print_r($redis->zRange('zs', 0, -1));
    -
    -// Remove the elements between 'banana' and 'eggplant'
    -$redis->zRemRangeByLex('zs', '(banana', '(eggplant');
    -
    -// Array
    -// (
    -//     [0] => banana
    -//     [1] => eggplant
    -// )
    -print_r($redis->zRange('zs', 0, -1));
    -?>

    The number of elements removed from the set or false on failure.

    @@ -17784,13 +18155,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->zRemRangeByLex('zs', '[a', '(b');
    $redis->zRemRangeByLex('zs', '(banana', '(eggplant');
    +

    - + Redis|int|false zRemRangeByRank(string $key, int $start, int $end) @@ -17829,22 +18211,7 @@

    Return Value

    - +
    Redis|int|false

    The number of members removed from the set or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -$redis->zAdd('zs', 0, 'zeroth', 1, 'first', 2, 'second', 3, 'third', 4, 'fourth');
    -
    -// Remove ranks 0..3
    -$redis->zRemRangeByRank('zs', 0, 3);
    -
    -// Array
    -// (
    -//     [0] => fourth
    -// )
    -$redis->zRange('zs', 0, -1);
    -?>

    The number of members removed from the set or false on failure.

    @@ -17862,13 +18229,21 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zRemRangeByRank('zs', 0, 3);
    +

    - + Redis|int|false zRemRangeByScore(string $key, string $start, string $end) @@ -17907,24 +18282,7 @@

    Return Value

    - +
    Redis|int|false

    The number of members removed from the set or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs');
    -$redis->zAdd('zs', 3, 'three', 5, 'five', 7, 'seven', 7, 'seven-again', 13, 'thirteen', 22, 'twenty-two');
    -
    -// Removes every member with scores >= 7 and scores <= 13.
    -$redis->zRemRangeByScore('zs', 7, 13);
    -
    -// Array
    -// (
    -//     [0] => three
    -//     [1] => five
    -//     [2] => twenty-two
    -// )
    -$redis->zRange('zs', 0, -1);
    -?>

    The number of members removed from the set or false on failure.

    @@ -17942,13 +18300,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('zs', 2, 'two', 4, 'four', 6, 'six');
    +$redis->zRemRangeByScore('zs', 2, 4);
    +

    - + Redis|array|false zRevRange(string $key, int $start, int $end, mixed $scores = null) @@ -17993,43 +18360,48 @@

    Return Value

    Redis|array|false

    The members (and possibly scores) of the matching elements or false -on failure.

    -

    $redis = new Redis(['host' => 'localhost']);

    -

    $redis->del('zs'); -$redis->zAdd('zs', 1, 'one', 2, 'two', 5, 'five', 10, 'ten');

    -

    // Array -// ( -// [0] => ten -// [1] => five -// [2] => two -// [3] => one -// ) -print_r($redis->zRevRange('zs', 0, -1));

    -

    // Array -// ( -// [0] => two -// [1] => one -// ) -print_r($redis->zRevRange('zs', 2, 3));

    -

    // Additionally, you may pass true or ['withscores' => true] to tell redis to return scores -// as well as members. -$redis->zRevRange('zs', 0, -1, true); -$redis->zRevRange('zs', 0, -1, ['withscores' => true]); -?>

    -
    +on failure.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/zrevrange +
    + +

    Examples

    + + + + + + + + + + + + + + +
    $redis->zRevRange('zs', 0, -1);
    $redis->zRevRange('zs', 2, 3);
    $redis->zRevRange('zs', 0, -1, true);
    $redis->zRevRange('zs', 0, -1, ['withscores' => true]);
    +

    - + Redis|array|false zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) @@ -18078,29 +18450,7 @@

    Return Value

    - +
    Redis|array|false

    The matching members or false on failure.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('captains');
    -$redis->zAdd('captains', 0, 'Janeway', 0, 'Picard', 0, 'Kirk', 0, 'Archer');
    -
    -// Array
    -// (
    -//     [0] => Picard
    -//     [1] => Kirk
    -//     [2] => Janeway
    -// )
    -$redis->zRevRangeByLex('captains', '[Q', '[J');
    -
    -// Array
    -// (
    -//     [0] => Kirk
    -//     [1] => Janeway
    -// )
    -$redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2);
    -?>

    The matching members or false on failure.

    @@ -18124,13 +18474,24 @@

    See also

    +

    Examples

    + + + + + + + + +
    $redis->zRevRangeByLex('captains', '[Q', '[J');
    $redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2);
    +

    - + Redis|array|false zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) @@ -18180,53 +18541,47 @@

    Return Value

    - +
    Redis|array|false

    The matching members in reverse order of score or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('oldest-people');
    -
    -$redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka',
    -                              119.2658, 'Sarah Knauss',   118.7205, 'Lucile Randon',
    -                              117.7123, 'Nabi Tajima',    117.6301, 'Marie-Louise Meilleur',
    -                              117.5178, 'Violet Brown',   117.3753, 'Emma Morano',
    -                              117.2219, 'Chiyo Miyako',   117.0740, 'Misao Okawa');
    -
    -// Array
    -// (
    -//     [0] => Kane Tanaka
    -//     [1] => Sarah Knauss
    -// )
    -$redis->zRevRangeByScore('oldest-people', 122, 119);
    -
    -//Array
    -//(
    -//    [0] => Jeanne Calment
    -//    [1] => Kane Tanaka
    -//    [2] => Sarah Knauss
    -//    [3] => Lucile Randon
    -//)
    -$redis->zRevRangeByScore('oldest-people', 'inf', 118);
    -
    -// Array
    -// (
    -//     [0] => Emma Morano
    -// )
    -$redis->zRevRangeByScore('oldest-people', '117.5', '-inf', ['LIMIT' => [0, 1]]);
    -?>

    The matching members in reverse order of score or false on failure.

    +

    See also

    + + + + + + +
    + https://redis.io/commands/zrevrangebyscore +
    + +

    Examples

    + + + + + +
    $redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka',
    + 119.2658, 'Sarah Knauss', 118.7205, 'Lucile Randon',
    + 117.7123, 'Nabi Tajima', 117.6301, 'Marie-Louise Meilleur',
    + 117.5178, 'Violet Brown', 117.3753, 'Emma Morano',
    + 117.2219, 'Chiyo Miyako', 117.0740, 'Misao Okawa');
    +
    +$redis->zRevRangeByScore('oldest-people', 122, 119);
    +$redis->zRevRangeByScore('oldest-people', 'inf', 118);
    +$redis->zRevRangeByScore('oldest-people', '117.5', '-inf', ['LIMIT' => [0, 1]]);
    +

    - + Redis|int|false zRevRank(string $key, mixed $member) @@ -18261,20 +18616,7 @@

    Return Value

    Redis|int|false

    The reverse rank (the rank if counted high to low) of the member or -false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('ds9-characters');
    -
    -$redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo');
    -
    -// Highest score, reverse rank 0
    -$redis->zrevrank('ds9-characters', 'Sisko');
    -
    -// Second highest score, reverse rank 1
    -$redis->zrevrank('ds9-characters', 'Garak');
    -?>
    +false on failure.

    @@ -18292,13 +18634,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo');
    +
    +$redis->zrevrank('ds9-characters', 'Sisko');
    +$redis->zrevrank('ds9-characters', 'Garak');
    +

    - + Redis|float|false zScore(string $key, mixed $member) @@ -18332,21 +18685,7 @@

    Return Value

    - +
    Redis|float|false

    score of the requested element or false if it is not found.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('telescopes');
    -
    -$redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET');
    -
    -foreach ($redis->zRange('telescopes', 0, -1) as $name) {
    -    // Get the score for this member
    -    $aperature = $redis->zScore('telescopes', $name);
    -
    -    echo "The '$name' telescope has an effective aperature of: $aperature meters\n";
    -}
    -?>

    score of the requested element or false if it is not found.

    @@ -18364,13 +18703,22 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET');
    +$redis->zScore('telescopes', 'LBT');
    +

    - + Redis|array|false zdiff(array $keys, array $options = null) @@ -18406,23 +18754,7 @@

    Return Value

    - +
    Redis|array|false

    An array of members or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('primes', 'evens', 'mod3');
    -
    -$redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five');
    -$redis->zAdd('evens', 2, 'two', 4, 'four');
    -$redis->zAdd('mod3', 3, 'three', 6, 'six');
    -
    -// Array
    -// (
    -//     [0] => one
    -//     [1] => five
    -// )
    -print_r($redis->zDiff(['primes', 'evens', 'mod3']));
    -?>

    An array of members or false on failure.

    @@ -18440,13 +18772,25 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five');
    +$redis->zAdd('evens', 2, 'two', 4, 'four');
    +$redis->zAdd('mod3', 3, 'three', 6, 'six');
    +
    +$redis->zDiff(['primes', 'evens', 'mod3']);
    +

    - + Redis|int|false zdiffstore(string $dst, array $keys) @@ -18456,7 +18800,7 @@

    -

    Store the difference of one or more sorted sets in a destination sorted set.

    +

    Store the difference of one or more sorted sets in a destination sorted set.

    See Redis::zdiff for a more detailed description of how the diff operation works.

    Parameters

    @@ -18481,8 +18825,7 @@

    Return Value

    Redis|int|false

    The number of elements stored in the destination set or false on -failure.

    -

    NOTE: See Redis::zdiff() for a more detailed description of how the diff operation works.

    +failure.

    @@ -18513,7 +18856,7 @@

    See also

    - + Redis|array|false zinter(array $keys, array|null $weights = null, array|null $options = null) @@ -18554,35 +18897,7 @@

    Return Value

    - +
    Redis|array|false

    All of the members that exist in every set.

    -
    <?php
    -
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('tng', 'ds9');
    -
    -$redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard');
    -$redis->zAdd('DS9', 2.5, 'Worf', 3.0, 'Kira', 4.0, 'Sisko');
    -
    -// Array
    -// (
    -//     [0] => Worf
    -// )
    -$redis->zInter(['TNG', 'DS9']);
    -
    -// Array
    -// (
    -//     [Worf] => 4.5
    -// )
    -$redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true]);
    -
    -// Array
    -// (
    -//     [Worf] => 2.5
    -// )
    -$redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true, 'aggregate' => 'max']);
    -
    -?>

    All of the members that exist in every set.

    @@ -18600,13 +18915,26 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard');
    +$redis->zAdd('DS9', 2.5, 'Worf', 3.0, 'Kira', 4.0, 'Sisko');
    +
    +$redis->zInter(['TNG', 'DS9']);
    +$redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true]);
    +$redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true, 'aggregate' => 'max']);
    +

    - + Redis|int|false zintercard(array $keys, int $limit = -1) @@ -18643,18 +18971,7 @@

    Return Value

    - +
    Redis|int|false

    The cardinality of the intersection or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs1', 'zs2');
    -
    -$redis->zAdd('zs1', 1, 'one', 2, 'two', 3, 'three', 4, 'four');
    -$redis->zAdd('zs2', 2, 'two', 4, 'four');
    -
    -// count(['two', 'four']) == 2
    -$redis->zInterCard(['zs1', 'zs2']);
    -?>

    The cardinality of the intersection or false on failure.

    @@ -18685,13 +19002,24 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('zs1', 1, 'one', 2, 'two', 3, 'three', 4, 'four');
    +$redis->zAdd('zs2', 2, 'two', 4, 'four');
    +
    +$redis->zInterCard(['zs1', 'zs2']);
    +

    - + Redis|int|false zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) @@ -18738,33 +19066,7 @@

    Return Value

    - +
    Redis|int|false

    The total number of members writtern to the destination set or false on failure.

    -
    
    -<?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs', 'zs2', 'zs3');
    -$redis->zAdd('zs1', 3, 'apples', 2, 'pears');
    -$redis->zAdd('zs2', 4, 'pears', 3, 'bananas');
    -$redis->zAdd('zs3', 2, 'figs', 3, 'pears');
    -
    -// Returns 1 (only 'pears' is in every set)
    -$redis->zInterStore('fruit-sum', ['zs1', 'zs2', 'zs3']);
    -
    -// Array
    -// (
    -//     [pears] => 9
    -// )
    -$redis->zRange('fruit-sum', 0, -1, true);
    -
    -$redis->zInterStore('fruit-max', ['zs1', 'zs2', 'zs3'], NULL, 'MAX');
    -
    -// Array
    -// (
    -//     [pears] => 4
    -// )
    -print_r($redis->zRange('fruit-max', 0, -1, true));
    -?>

    The total number of members writtern to the destination set or false on failure.

    @@ -18788,13 +19090,26 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('zs1', 3, 'apples', 2, 'pears');
    +$redis->zAdd('zs2', 4, 'pears', 3, 'bananas');
    +$redis->zAdd('zs3', 2, 'figs', 3, 'pears');
    +
    +$redis->zInterStore('fruit-sum', ['zs1', 'zs2', 'zs3']);
    +$redis->zInterStore('fruit-max', ['zs1', 'zs2', 'zs3'], NULL, 'MAX');
    +

    - + Redis|array|false zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -18843,8 +19158,7 @@

    Return Value

    - +
    Redis|array|false

    An array of elements or false on failure.

    -

    NOTE: See Redis::scan() for detailed example code on how to call SCAN like commands.

    An array of elements or false on failure.

    @@ -18870,7 +19184,7 @@

    See also

    Redis::scan - + NOTE: See Redis::scan() for detailed example code on how to call SCAN like commands. @@ -18881,7 +19195,7 @@

    See also

    - + Redis|array|false zunion(array $keys, array|null $weights = null, array|null $options = null) @@ -18931,56 +19245,35 @@

    Return Value

    - +
    Redis|array|false

    The union of each sorted set or false on failure

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('store1', 'store2', 'store3');
    -$redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas');
    -$redis->zAdd('store2', 3, 'apples', 5, 'coconuts', 2, 'bananas');
    -$redis->zAdd('store3', 2, 'bananas', 6, 'apples', 4, 'figs');
    -
    -// Array
    -// (
    -//     [pears] => 3
    -//     [figs] => 4
    -//     [coconuts] => 5
    -//     [apples] => 10
    -//     [bananas] => 10
    -// )
    -$redis->zUnion(['store1', 'store2', 'store3'], NULL, ['withscores' => true]);
    -
    -// Array
    -// (
    -//     [figs] => 2
    -//     [apples] => 5
    -//     [pears] => 6
    -//     [bananas] => 13
    -// )
    -$redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true]);
    -
    -// Array
    -// (
    -//     [bananas] => 1
    -//     [apples] => 2
    -//     [figs] => 2
    -//     [pears] => 6
    -// )
    -$redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true, 'aggregate' => 'MIN']);
    -?>

    The union of each sorted set or false on failure

    +

    Examples

    + + + + + +
    $redis->del('store1', 'store2', 'store3');
    +$redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas');
    +$redis->zAdd('store2', 3, 'apples', 5, 'coconuts', 2, 'bananas');
    +$redis->zAdd('store3', 2, 'bananas', 6, 'apples', 4, 'figs');
    +
    +$redis->zUnion(['store1', 'store2', 'store3'], NULL, ['withscores' => true]);
    +$redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true]);
    +$redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true, 'aggregate' => 'MIN']);
    +

    - + Redis|int|false zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) @@ -19025,29 +19318,7 @@

    Return Value

    - +
    Redis|int|false

    The number of members stored in the destination set or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('zs1', 'zs2', 'zs3');
    -
    -$redis->zAdd('zs1', 1, 'one', 3, 'three');
    -$redis->zAdd('zs1', 2, 'two', 4, 'four');
    -$redis->zadd('zs3', 1, 'one', 7, 'five');
    -
    -// count(['one','two','three','four','five']) == 5
    -$redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']);
    -
    -// Array
    -// (
    -//     [0] => one
    -//     [1] => two
    -//     [2] => three
    -//     [3] => four
    -//     [4] => five
    -// )
    -$redis->zRange('dst', 0, -1);
    -?>

    The number of members stored in the destination set or false on failure.

    @@ -19072,6 +19343,18 @@

    See also

    +

    Examples

    + + + + + +
    $redis->zAdd('zs1', 1, 'one', 3, 'three');
    +$redis->zAdd('zs1', 2, 'two', 4, 'four');
    +$redis->zadd('zs3', 1, 'one', 7, 'five');
    +
    +$redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']);
    +
    diff --git a/docs/RedisArray.html b/docs/RedisArray.html index 43becf9e81..2a8ffb4de1 100644 --- a/docs/RedisArray.html +++ b/docs/RedisArray.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -83,7 +83,7 @@

    RedisArray

    class - RedisArray (View source) + RedisArray (View source)

    @@ -446,7 +446,7 @@

    Details

    - + mixed __call(string $function_name, array $arguments) @@ -494,7 +494,7 @@

    Return Value

    - + __construct(string|array $name_or_hosts, array $options = NULL) @@ -533,7 +533,7 @@

    Parameters

    - + bool|array _continuum() @@ -566,7 +566,7 @@

    Return Value

    - + bool|callable _distributor() @@ -599,7 +599,7 @@

    Return Value

    - + bool|callable _function() @@ -632,7 +632,7 @@

    Return Value

    - + bool|array _hosts() @@ -665,7 +665,7 @@

    Return Value

    - + bool|null|Redis _instance(string $host) @@ -708,7 +708,7 @@

    Return Value

    - + bool|null _rehash(callable $fn = NULL) @@ -751,7 +751,7 @@

    Return Value

    - + bool|string|null _target(string $key) @@ -794,7 +794,7 @@

    Return Value

    - + array bgsave() @@ -827,7 +827,7 @@

    Return Value

    - + bool|int del(string|array $key, string ...$otherkeys) @@ -875,7 +875,7 @@

    Return Value

    - + bool|null discard() @@ -908,7 +908,7 @@

    Return Value

    - + bool|null exec() @@ -941,7 +941,7 @@

    Return Value

    - + bool|array flushall() @@ -974,7 +974,7 @@

    Return Value

    - + bool|array flushdb() @@ -1007,7 +1007,7 @@

    Return Value

    - + bool|array getOption(int $opt) @@ -1050,7 +1050,7 @@

    Return Value

    - + bool|array hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -1108,7 +1108,7 @@

    Return Value

    - + bool|array info() @@ -1141,7 +1141,7 @@

    Return Value

    - + bool|array keys(string $pattern) @@ -1184,7 +1184,7 @@

    Return Value

    - + bool|array mget(array $keys) @@ -1227,7 +1227,7 @@

    Return Value

    - + bool mset(array $pairs) @@ -1270,7 +1270,7 @@

    Return Value

    - + bool|RedisArray multi(string $host, int $mode = NULL) @@ -1318,7 +1318,7 @@

    Return Value

    - + bool|array ping() @@ -1351,7 +1351,7 @@

    Return Value

    - + bool|array save() @@ -1384,7 +1384,7 @@

    Return Value

    - + bool|array scan(int|null $iterator, string $node, string|null $pattern = null, int $count = 0) @@ -1442,7 +1442,7 @@

    Return Value

    - + bool|array select(int $index) @@ -1485,7 +1485,7 @@

    Return Value

    - + bool|array setOption(int $opt, string $value) @@ -1533,7 +1533,7 @@

    Return Value

    - + bool|array sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -1591,7 +1591,7 @@

    Return Value

    Return Value

    - + bool|null unwatch() @@ -1672,7 +1672,7 @@

    Return Value

    - + bool|array zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) diff --git a/docs/RedisCluster.html b/docs/RedisCluster.html index 7894808d9e..4e9fb9fa9e 100644 --- a/docs/RedisCluster.html +++ b/docs/RedisCluster.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -83,7 +83,7 @@

    RedisCluster

    class - RedisCluster (View source) + RedisCluster (View source)

    @@ -1991,7 +1991,18 @@

    Methods

    mixed
    - xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false) + xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) + +

    No description

    +
    +
    +
    +
    +
    + Redis|bool|array +
    +
    + xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false)

    No description

    @@ -2368,7 +2379,7 @@

    Details

    - + __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, mixed $auth = NULL, array $context = NULL) @@ -2432,7 +2443,7 @@

    Parameters

    - + string _compress(string $value) @@ -2487,7 +2498,7 @@

    See also

    - + string _uncompress(string $value) @@ -2542,7 +2553,7 @@

    See also

    - + bool|string _serialize(mixed $value) @@ -2597,7 +2608,7 @@

    See also

    - + mixed _unserialize(string $value) @@ -2652,7 +2663,7 @@

    See also

    - + string _pack(mixed $value) @@ -2707,7 +2718,7 @@

    See also

    - + mixed _unpack(string $value) @@ -2762,7 +2773,7 @@

    See also

    - + bool|string _prefix(string $key) @@ -2817,7 +2828,7 @@

    See also

    - + array _masters() @@ -2850,7 +2861,7 @@

    Return Value

    - + string|null _redir() @@ -2883,7 +2894,7 @@

    Return Value

    - + mixed acl(string|array $key_or_address, string $subcmd, string ...$args) @@ -2947,7 +2958,7 @@

    See also

    - + RedisCluster|bool|int append(string $key, mixed $value) @@ -3007,7 +3018,7 @@

    See also

    - + RedisCluster|bool bgrewriteaof(string|array $key_or_address) @@ -3061,7 +3072,7 @@

    See also

    - + RedisCluster|bool bgsave(string|array $key_or_address) @@ -3115,7 +3126,7 @@

    See also

    - + RedisCluster|bool|int bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) @@ -3184,7 +3195,7 @@

    See also

    - + RedisCluster|bool|int bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys) @@ -3253,7 +3264,7 @@

    See also

    - + RedisCluster|int|false bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) @@ -3327,7 +3338,7 @@

    See also

    - + RedisCluster|array|null|false blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) @@ -3379,7 +3390,7 @@

    Return Value

    - + RedisCluster|array|null|false brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) @@ -3431,7 +3442,7 @@

    Return Value

    - + mixed brpoplpush(string $srckey, string $deskey, int $timeout) @@ -3483,7 +3494,7 @@

    Return Value

    - + array bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3547,7 +3558,7 @@

    See also

    - + array bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) @@ -3611,7 +3622,7 @@

    See also

    - + RedisCluster|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -3680,7 +3691,7 @@

    See also

    - + RedisCluster|array|null|false zmpop(array $keys, string $from, int $count = 1) @@ -3744,7 +3755,7 @@

    See also

    - + RedisCluster|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1) @@ -3814,7 +3825,7 @@

    See also

    - + RedisCluster|array|null|false lmpop(array $keys, string $from, int $count = 1) @@ -3879,7 +3890,7 @@

    See also

    - + bool clearlasterror() @@ -3923,7 +3934,7 @@

    See also

    - + array|string|bool client(string|array $key_or_address, string $subcommand, string|null $arg = NULL) @@ -3987,7 +3998,7 @@

    See also

    - + bool close() @@ -4031,7 +4042,7 @@

    See also

    - + mixed cluster(string|array $key_or_address, string $command, mixed ...$extra_args) @@ -4095,7 +4106,7 @@

    See also

    - + mixed command(mixed ...$extra_args) @@ -4149,7 +4160,7 @@

    See also

    - + mixed config(string|array $key_or_address, string $subcommand, mixed ...$extra_args) @@ -4214,7 +4225,7 @@

    See also

    - + RedisCluster|int dbsize(string|array $key_or_address) @@ -4268,7 +4279,7 @@

    See also

    - + RedisCluster|int|false decr(string $key, int $by = 1) @@ -4328,7 +4339,7 @@

    See also

    - + RedisCluster|int|false decrby(string $key, int $value) @@ -4387,7 +4398,7 @@

    See also

    - + float decrbyfloat(string $key, float $value) @@ -4446,7 +4457,7 @@

    See also

    - + RedisCluster|int|false del(array|string $key, string ...$other_keys) @@ -4506,7 +4517,7 @@

    See also

    - + bool discard() @@ -4550,7 +4561,7 @@

    See also

    - + RedisCluster|string|false dump(string $key) @@ -4604,7 +4615,7 @@

    See also

    - + RedisCluster|string|false echo(string|array $key_or_address, string $msg) @@ -4664,7 +4675,7 @@

    See also

    - + mixed eval(string $script, array $args = [], int $num_keys = 0) @@ -4728,7 +4739,7 @@

    See also

    - + mixed eval_ro(string $script, array $args = [], int $num_keys = 0) @@ -4792,7 +4803,7 @@

    See also

    - + mixed evalsha(string $script_sha, array $args = [], int $num_keys = 0) @@ -4856,7 +4867,7 @@

    See also

    - + mixed evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0) @@ -4920,7 +4931,7 @@

    See also

    - + array|false exec() @@ -4965,7 +4976,7 @@

    See also

    - + RedisCluster|int|bool exists(mixed $key, mixed ...$other_keys) @@ -5024,7 +5035,7 @@

    See also

    - + RedisCluster|int|bool touch(mixed $key, mixed ...$other_keys) @@ -5084,7 +5095,7 @@

    See also

    - + RedisCluster|bool expire(string $key, int $timeout, string|null $mode = NULL) @@ -5148,7 +5159,7 @@

    See also

    - + RedisCluster|bool expireat(string $key, int $timestamp, string|null $mode = NULL) @@ -5212,7 +5223,7 @@

    See also

    - + RedisCluster|int|false expiretime(string $key) @@ -5267,7 +5278,7 @@

    See also

    - + RedisCluster|int|false pexpiretime(string $key) @@ -5322,7 +5333,7 @@

    See also

    - + RedisCluster|bool flushall(string|array $key_or_address, bool $async = false) @@ -5381,7 +5392,7 @@

    See also

    - + RedisCluster|bool flushdb(string|array $key_or_address, bool $async = false) @@ -5440,7 +5451,7 @@

    See also

    - + RedisCluster|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) @@ -5514,7 +5525,7 @@

    See also

    - + RedisCluster|float|false geodist(string $key, string $src, string $dest, string|null $unit = null) @@ -5583,7 +5594,7 @@

    See also

    - + RedisCluster|array|false geohash(string $key, string $member, string ...$other_members) @@ -5647,7 +5658,7 @@

    See also

    - + RedisCluster|array|false geopos(string $key, string $member, string ...$other_members) @@ -5711,7 +5722,7 @@

    See also

    - + mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -5790,7 +5801,7 @@

    See also

    - + mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) @@ -5869,7 +5880,7 @@

    See also

    - + mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) @@ -5943,7 +5954,7 @@

    See also

    - + mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) @@ -6017,7 +6028,7 @@

    See also

    - + mixed get(string $key) @@ -6071,7 +6082,7 @@

    See also

    - + RedisCluster|int|false getbit(string $key, int $value) @@ -6130,7 +6141,7 @@

    See also

    - + string|null getlasterror() @@ -6174,7 +6185,7 @@

    See also

    - + int getmode() @@ -6218,7 +6229,7 @@

    See also

    - + mixed getoption(int $option) @@ -6272,7 +6283,7 @@

    See also

    - + RedisCluster|string|false getrange(string $key, int $start, int $end) @@ -6336,7 +6347,7 @@

    See also

    - + RedisCluster|string|array|int|false lcs(string $key1, string $key2, array|null $options = NULL) @@ -6400,7 +6411,7 @@

    See also

    - + RedisCluster|string|bool getset(string $key, mixed $value) @@ -6459,7 +6470,7 @@

    See also

    - + int|false gettransferredbytes() @@ -6503,7 +6514,7 @@

    See also

    - + RedisCluster|int|false hdel(string $key, string $member, string ...$other_members) @@ -6567,7 +6578,7 @@

    See also

    - + RedisCluster|bool hexists(string $key, string $member) @@ -6626,7 +6637,7 @@

    See also

    - + mixed hget(string $key, string $member) @@ -6685,7 +6696,7 @@

    See also

    - + RedisCluster|array|false hgetall(string $key) @@ -6739,7 +6750,7 @@

    See also

    - + RedisCluster|int|false hincrby(string $key, string $member, int $value) @@ -6803,7 +6814,7 @@

    See also

    - + RedisCluster|float|false hincrbyfloat(string $key, string $member, float $value) @@ -6867,7 +6878,7 @@

    See also

    - + RedisCluster|array|false hkeys(string $key) @@ -6921,7 +6932,7 @@

    See also

    - + RedisCluster|int|false hlen(string $key) @@ -6975,7 +6986,7 @@

    See also

    - + RedisCluster|array|false hmget(string $key, array $keys) @@ -7034,7 +7045,7 @@

    See also

    - + RedisCluster|bool hmset(string $key, array $key_values) @@ -7093,7 +7104,7 @@

    See also

    - + array|bool hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -7162,7 +7173,7 @@

    See also

    - + RedisCluster|int|false hset(string $key, string $member, mixed $value) @@ -7226,7 +7237,7 @@

    See also

    - + RedisCluster|bool hsetnx(string $key, string $member, mixed $value) @@ -7290,7 +7301,7 @@

    See also

    - + RedisCluster|int|false hstrlen(string $key, string $field) @@ -7349,7 +7360,7 @@

    See also

    - + RedisCluster|array|false hvals(string $key) @@ -7403,7 +7414,7 @@

    See also

    - + RedisCluster|int|false incr(string $key, int $by = 1) @@ -7462,7 +7473,7 @@

    See also

    - + RedisCluster|int|false incrby(string $key, int $value) @@ -7521,7 +7532,7 @@

    See also

    - + RedisCluster|float|false incrbyfloat(string $key, float $value) @@ -7580,7 +7591,7 @@

    See also

    - + RedisCluster|array|false info(string|array $key_or_address, string ...$sections) @@ -7642,7 +7653,7 @@

    See also

    - + RedisCluster|array|false keys(string $pattern) @@ -7696,7 +7707,7 @@

    See also

    - + RedisCluster|int|false lastsave(string|array $key_or_address) @@ -7750,7 +7761,7 @@

    See also

    - + RedisCluster|string|bool lget(string $key, int $index) @@ -7809,7 +7820,7 @@

    See also

    - + mixed lindex(string $key, int $index) @@ -7868,7 +7879,7 @@

    See also

    - + RedisCluster|int|false linsert(string $key, string $pos, mixed $pivot, mixed $value) @@ -7937,7 +7948,7 @@

    See also

    - + RedisCluster|int|bool llen(string $key) @@ -7991,7 +8002,7 @@

    See also

    - + RedisCluster|bool|string|array lpop(string $key, int $count = 0) @@ -8050,7 +8061,7 @@

    See also

    - + RedisCluster|int|bool lpush(string $key, mixed $value, mixed ...$other_values) @@ -8114,7 +8125,7 @@

    See also

    - + RedisCluster|int|bool lpushx(string $key, mixed $value) @@ -8173,7 +8184,7 @@

    See also

    - + RedisCluster|array|false lrange(string $key, int $start, int $end) @@ -8237,7 +8248,7 @@

    See also

    - + RedisCluster|int|bool lrem(string $key, mixed $value, int $count = 0) @@ -8301,7 +8312,7 @@

    See also

    - + RedisCluster|bool lset(string $key, int $index, mixed $value) @@ -8365,7 +8376,7 @@

    See also

    - + RedisCluster|bool ltrim(string $key, int $start, int $end) @@ -8429,7 +8440,7 @@

    See also

    - + RedisCluster|array|false mget(array $keys) @@ -8483,7 +8494,7 @@

    See also

    - + RedisCluster|bool mset(array $key_values) @@ -8537,7 +8548,7 @@

    See also

    - + RedisCluster|array|false msetnx(array $key_values) @@ -8591,7 +8602,7 @@

    See also

    - + RedisCluster|bool multi(int $value = Redis::MULTI) @@ -8634,7 +8645,7 @@

    Return Value

    - + RedisCluster|int|string|false object(string $subcommand, string $key) @@ -8693,7 +8704,7 @@

    See also

    - + RedisCluster|bool persist(string $key) @@ -8747,7 +8758,7 @@

    See also

    - + RedisCluster|bool pexpire(string $key, int $timeout, string|null $mode = NULL) @@ -8811,7 +8822,7 @@

    See also

    - + RedisCluster|bool pexpireat(string $key, int $timestamp, string|null $mode = NULL) @@ -8875,7 +8886,7 @@

    See also

    - + RedisCluster|bool pfadd(string $key, array $elements) @@ -8935,7 +8946,7 @@

    See also

    - + RedisCluster|int|false pfcount(string $key) @@ -8990,7 +9001,7 @@

    See also

    - + RedisCluster|bool pfmerge(string $key, array $keys) @@ -9050,7 +9061,7 @@

    See also

    - + mixed ping(string|array $key_or_address, string|null $message = NULL) @@ -9111,7 +9122,7 @@

    See also

    - + RedisCluster|bool psetex(string $key, int $timeout, string $value) @@ -9175,7 +9186,7 @@

    See also

    - + void psubscribe(array $patterns, callable $callback) @@ -9234,7 +9245,7 @@

    See also

    - + RedisCluster|int|false pttl(string $key) @@ -9288,7 +9299,7 @@

    See also

    - + RedisCluster|bool publish(string $channel, string $message) @@ -9347,7 +9358,7 @@

    See also

    - + mixed pubsub(string|array $key_or_address, string ...$values) @@ -9406,7 +9417,7 @@

    See also

    - + bool|array punsubscribe(string $pattern, string ...$other_patterns) @@ -9465,7 +9476,7 @@

    See also

    - + RedisCluster|bool|string randomkey(string|array $key_or_address) @@ -9519,7 +9530,7 @@

    See also

    - + mixed rawcommand(string|array $key_or_address, string $command, mixed ...$args) @@ -9583,7 +9594,7 @@

    See also

    - + RedisCluster|bool rename(string $key_src, string $key_dst) @@ -9642,7 +9653,7 @@

    See also

    - + RedisCluster|bool renamenx(string $key, string $newkey) @@ -9701,7 +9712,7 @@

    See also

    - + RedisCluster|bool restore(string $key, int $timeout, string $value, array|null $options = NULL) @@ -9770,7 +9781,7 @@

    See also

    - + mixed role(string|array $key_or_address) @@ -9824,7 +9835,7 @@

    See also

    - + RedisCluster|bool|string|array rpop(string $key, int $count = 0) @@ -9883,7 +9894,7 @@

    See also

    - + RedisCluster|bool|string rpoplpush(string $src, string $dst) @@ -9943,7 +9954,7 @@

    See also

    - + RedisCluster|int|false rpush(string $key, mixed ...$elements) @@ -10002,7 +10013,7 @@

    See also

    - + RedisCluster|bool|int rpushx(string $key, string $value) @@ -10061,7 +10072,7 @@

    See also

    - + RedisCluster|int|false sadd(string $key, mixed $value, mixed ...$other_values) @@ -10125,7 +10136,7 @@

    See also

    - + RedisCluster|bool|int saddarray(string $key, array $values) @@ -10184,7 +10195,7 @@

    See also

    - + RedisCluster|bool save(string|array $key_or_address) @@ -10238,7 +10249,7 @@

    See also

    - + bool|array scan(int|null $iterator, string|array $key_or_address, string|null $pattern = null, int $count = 0) @@ -10307,7 +10318,7 @@

    See also

    - + RedisCluster|int|false scard(string $key) @@ -10361,7 +10372,7 @@

    See also

    - + mixed script(string|array $key_or_address, mixed ...$args) @@ -10420,7 +10431,7 @@

    See also

    - + RedisCluster|array|false sdiff(string $key, string ...$other_keys) @@ -10479,7 +10490,7 @@

    See also

    - + RedisCluster|int|false sdiffstore(string $dst, string $key, string ...$other_keys) @@ -10543,7 +10554,7 @@

    See also

    - + RedisCluster|string|bool set(string $key, mixed $value, mixed $options = NULL) @@ -10607,7 +10618,7 @@

    See also

    - + RedisCluster|int|false setbit(string $key, int $offset, bool $onoff) @@ -10671,7 +10682,7 @@

    See also

    - + RedisCluster|bool setex(string $key, int $expire, mixed $value) @@ -10735,7 +10746,7 @@

    See also

    - + RedisCluster|bool setnx(string $key, mixed $value) @@ -10794,7 +10805,7 @@

    See also

    - + bool setoption(int $option, mixed $value) @@ -10853,7 +10864,7 @@

    See also

    - + RedisCluster|int|false setrange(string $key, int $offset, string $value) @@ -10917,7 +10928,7 @@

    See also

    - + RedisCluster|array|false sinter(array|string $key, string ...$other_keys) @@ -10976,7 +10987,7 @@

    See also

    - + RedisCluster|int|false sintercard(array $keys, int $limit = -1) @@ -11035,7 +11046,7 @@

    See also

    - + RedisCluster|int|false sinterstore(array|string $key, string ...$other_keys) @@ -11094,7 +11105,7 @@

    See also

    - + RedisCluster|bool sismember(string $key, mixed $value) @@ -11153,7 +11164,7 @@

    See also

    - + mixed slowlog(string|array $key_or_address, mixed ...$args) @@ -11212,7 +11223,7 @@

    See also

    - + RedisCluster|array|false smembers(string $key) @@ -11266,7 +11277,7 @@

    See also

    - + RedisCluster|bool smove(string $src, string $dst, string $member) @@ -11330,7 +11341,7 @@

    See also

    - + RedisCluster|array|bool|int|string sort(string $key, array|null $options = NULL) @@ -11390,7 +11401,7 @@

    See also

    - + RedisCluster|array|bool|int|string sort_ro(string $key, array|null $options = NULL) @@ -11450,7 +11461,7 @@

    See also

    - + RedisCluster|string|array|false spop(string $key, int $count = 0) @@ -11509,7 +11520,7 @@

    See also

    - + RedisCluster|string|array|false srandmember(string $key, int $count = 0) @@ -11568,7 +11579,7 @@

    See also

    - + RedisCluster|int|false srem(string $key, mixed $value, mixed ...$other_values) @@ -11632,7 +11643,7 @@

    See also

    - + array|false sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -11701,7 +11712,7 @@

    See also

    - + RedisCluster|int|false strlen(string $key) @@ -11755,7 +11766,7 @@

    See also

    - + void subscribe(array $channels, callable $cb) @@ -11814,7 +11825,7 @@

    See also

    - + RedisCluster|bool|array sunion(string $key, string ...$other_keys) @@ -11873,7 +11884,7 @@

    See also

    - + RedisCluster|int|false sunionstore(string $dst, string $key, string ...$other_keys) @@ -11937,7 +11948,7 @@

    See also

    - + RedisCluster|bool|array time(string|array $key_or_address) @@ -11991,7 +12002,7 @@

    See also

    - + RedisCluster|int|false ttl(string $key) @@ -12045,7 +12056,7 @@

    See also

    - + RedisCluster|int|false type(string $key) @@ -12099,7 +12110,7 @@

    See also

    - + bool|array unsubscribe(array $channels) @@ -12153,7 +12164,7 @@

    See also

    See also

    - + bool unwatch() @@ -12256,7 +12267,7 @@

    See also

    - + RedisCluster|bool watch(string $key, string ...$other_keys) @@ -12315,7 +12326,7 @@

    See also

    - + RedisCluster|int|false xack(string $key, string $group, array $ids) @@ -12379,7 +12390,7 @@

    See also

    - + RedisCluster|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false) @@ -12453,7 +12464,7 @@

    See also

    - + RedisCluster|string|array|false xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options) @@ -12532,7 +12543,7 @@

    See also

    - + RedisCluster|int|false xdel(string $key, array $ids) @@ -12591,9 +12602,9 @@

    See also

    - + mixed - xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false) + xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2)

    @@ -12620,17 +12631,22 @@

    Parameters

    string - $arg1 + $group string - $arg2 + $id_or_consumer bool - $arg3 + $mkstream + + + + int + $entries_read @@ -12662,10 +12678,94 @@

    See also

    +
    +
    +

    + + Redis|bool|array + xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) + +

    +
    + + + +
    +

    No description

    + +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    string$key
    string$group
    string$consumer
    int$min_idle
    string$start
    int$count
    bool$justid
    + + +

    Return Value

    + + + + + + +
    Redis|bool|array
    + + + +

    See also

    + + + + + + +
    + Redis::xautoclaim +
    + + +
    +
    +

    - + mixed xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) @@ -12734,7 +12834,7 @@

    See also

    - + RedisCluster|int|false xlen(string $key) @@ -12788,7 +12888,7 @@

    See also

    - + RedisCluster|array|false xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) @@ -12867,7 +12967,7 @@

    See also

    - + RedisCluster|bool|array xrange(string $key, string $start, string $end, int $count = -1) @@ -12936,7 +13036,7 @@

    See also

    - + RedisCluster|bool|array xread(array $streams, int $count = -1, int $block = -1) @@ -13000,7 +13100,7 @@

    See also

    - + RedisCluster|bool|array xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) @@ -13074,7 +13174,7 @@

    See also

    - + RedisCluster|bool|array xrevrange(string $key, string $start, string $end, int $count = -1) @@ -13143,7 +13243,7 @@

    See also

    - + RedisCluster|int|false xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1) @@ -13217,7 +13317,7 @@

    See also

    - + RedisCluster|int|false zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) @@ -13281,7 +13381,7 @@

    See also

    - + RedisCluster|int|false zcard(string $key) @@ -13335,7 +13435,7 @@

    See also

    - + RedisCluster|int|false zcount(string $key, string $start, string $end) @@ -13399,7 +13499,7 @@

    See also

    - + RedisCluster|float|false zincrby(string $key, float $value, string $member) @@ -13463,7 +13563,7 @@

    See also

    - + RedisCluster|int|false zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) @@ -13532,7 +13632,7 @@

    See also

    - + RedisCluster|int|false zintercard(array $keys, int $limit = -1) @@ -13591,7 +13691,7 @@

    See also

    - + RedisCluster|int|false zlexcount(string $key, string $min, string $max) @@ -13655,7 +13755,7 @@

    See also

    - + RedisCluster|bool|array zpopmax(string $key, int $value = null) @@ -13714,7 +13814,7 @@

    See also

    - + RedisCluster|bool|array zpopmin(string $key, int $value = null) @@ -13773,7 +13873,7 @@

    See also

    - + RedisCluster|array|bool zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null) @@ -13842,7 +13942,7 @@

    See also

    - + RedisCluster|int|false zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null) @@ -13916,7 +14016,7 @@

    See also

    - + RedisCluster|array|false zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1) @@ -13990,7 +14090,7 @@

    See also

    - + RedisCluster|array|false zrangebyscore(string $key, string $start, string $end, array $options = []) @@ -14059,7 +14159,7 @@

    See also

    - + RedisCluster|int|false zrank(string $key, mixed $member) @@ -14118,7 +14218,7 @@

    See also

    - + RedisCluster|int|false zrem(string $key, string $value, string ...$other_values) @@ -14182,7 +14282,7 @@

    See also

    - + RedisCluster|int|false zremrangebylex(string $key, string $min, string $max) @@ -14246,7 +14346,7 @@

    See also

    - + RedisCluster|int|false zremrangebyrank(string $key, string $min, string $max) @@ -14310,7 +14410,7 @@

    See also

    - + RedisCluster|int|false zremrangebyscore(string $key, string $min, string $max) @@ -14374,7 +14474,7 @@

    See also

    - + RedisCluster|bool|array zrevrange(string $key, string $min, string $max, array $options = null) @@ -14443,7 +14543,7 @@

    See also

    - + RedisCluster|bool|array zrevrangebylex(string $key, string $min, string $max, array $options = null) @@ -14512,7 +14612,7 @@

    See also

    - + RedisCluster|bool|array zrevrangebyscore(string $key, string $min, string $max, array $options = null) @@ -14581,7 +14681,7 @@

    See also

    - + RedisCluster|int|false zrevrank(string $key, mixed $member) @@ -14640,7 +14740,7 @@

    See also

    - + RedisCluster|bool|array zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -14709,7 +14809,7 @@

    See also

    - + RedisCluster|float|false zscore(string $key, mixed $member) @@ -14768,7 +14868,7 @@

    See also

    - + RedisCluster|int|false zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) diff --git a/docs/RedisClusterException.html b/docs/RedisClusterException.html index c366b7fcaa..aeda6a4c86 100644 --- a/docs/RedisClusterException.html +++ b/docs/RedisClusterException.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -83,7 +83,7 @@

    RedisClusterException

    class - RedisClusterException extends RuntimeException (View source) + RedisClusterException extends RuntimeException (View source)

    diff --git a/docs/RedisException.html b/docs/RedisException.html index 4851bcad2c..66c373b690 100644 --- a/docs/RedisException.html +++ b/docs/RedisException.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -83,7 +83,7 @@

    RedisException

    class - RedisException extends RuntimeException (View source) + RedisException extends RuntimeException (View source)

    diff --git a/docs/RedisSentinel.html b/docs/RedisSentinel.html index 00674d8b5a..ac95dd5c28 100644 --- a/docs/RedisSentinel.html +++ b/docs/RedisSentinel.html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> @@ -83,7 +83,7 @@

    RedisSentinel

    class - RedisSentinel (View source) + RedisSentinel (View source)

    @@ -237,7 +237,7 @@

    Details

    - + __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = null, int $retry_interval = 0, float $read_timeout = 0, mixed $auth = null, array $context = null) @@ -306,7 +306,7 @@

    Parameters

    - + bool|RedisSentinel ckquorum(string $master) @@ -349,7 +349,7 @@

    Return Value

    - + bool|RedisSentinel failover(string $master) @@ -392,7 +392,7 @@

    Return Value

    - + bool|RedisSentinel flushconfig() @@ -425,7 +425,7 @@

    Return Value

    - + array|bool|RedisSentinel getMasterAddrByName(string $master) @@ -468,7 +468,7 @@

    Return Value

    - + array|bool|RedisSentinel master(string $master) @@ -511,7 +511,7 @@

    Return Value

    - + array|bool|RedisSentinel masters() @@ -544,7 +544,7 @@

    Return Value

    - + string myid() @@ -577,7 +577,7 @@

    Return Value

    - + bool|RedisSentinel ping() @@ -610,7 +610,7 @@

    Return Value

    - + bool|RedisSentinel reset(string $pattern) @@ -653,7 +653,7 @@

    Return Value

    - + array|bool|RedisSentinel sentinels(string $master) @@ -696,7 +696,7 @@

    Return Value

    - + array|bool|RedisSentinel slaves(string $master) diff --git a/docs/[Global_Namespace].html b/docs/[Global_Namespace].html index 9c8ee96579..b30c8eb2e2 100644 --- a/docs/[Global_Namespace].html +++ b/docs/[Global_Namespace].html @@ -20,7 +20,7 @@ + title="PhpRedis API (main)" /> diff --git a/docs/classes.html b/docs/classes.html index b7243ccf94..f281726e73 100644 --- a/docs/classes.html +++ b/docs/classes.html @@ -82,6 +82,11 @@

    Classes

    +
    + Redis
    +
    +
    +
    @@ -95,6 +100,11 @@

    Classes

    +
    +
    diff --git a/docs/doc-index.html b/docs/doc-index.html index 75d12cbe59..e475ef25be 100644 --- a/docs/doc-index.html +++ b/docs/doc-index.html @@ -95,7 +95,7 @@

    Index

  • L
  • M
  • N
  • -
  • O
  • +
  • O
  • P
  • Q
  • R
  • @@ -103,19 +103,55 @@

    Index

  • T
  • U
  • V
  • -
  • W
  • +
  • W
  • X
  • Y
  • Z
  • A

    -
    +
    +Redis::acl() — Method in class Redis
    +
    +Redis::append() — Method in class Redis
    +

    Append data to a Redis STRING key.

    +Redis::auth() — Method in class Redis
    +

    Authenticate a Redis connection after its been established.

    RedisCluster::acl() — Method in class RedisCluster
    RedisCluster::append() — Method in class RedisCluster

    B

    -
    +
    +Redis::bgSave() — Method in class Redis
    +

    Execute a save of the Redis database in the background.

    +Redis::bgrewriteaof() — Method in class Redis
    +

    Asynchronously rewrite Redis' append-only file

    +Redis::bitcount() — Method in class Redis
    +

    Count the number of set bits in a Redis string.

    +Redis::bitop() — Method in class Redis
    +
    +Redis::bitpos() — Method in class Redis
    +

    Return the position of the first bit set to 0 or 1 in a string.

    +Redis::blPop() — Method in class Redis
    +

    Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified +timeout. This method may be called in two distinct ways, of which examples are provided below.

    +Redis::brPop() — Method in class Redis
    +

    Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

    +Redis::brpoplpush() — Method in class Redis
    +

    Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list, +optionally blocking up to a specified timeout.

    +Redis::bzPopMax() — Method in class Redis
    +

    POP the maximum scoring element off of one or more sorted sets, blocking up to a specified +timeout if no elements are available.

    +Redis::bzPopMin() — Method in class Redis
    +

    POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout +if no elements are available

    +Redis::bzmpop() — Method in class Redis
    +

    POP one or more elements from one or more sorted sets, blocking up to a specified amount of time +when no elements are available.

    +Redis::blmpop() — Method in class Redis
    +

    Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when +no elements are available.

    RedisArray::bgsave() — Method in class RedisArray
    RedisCluster::bgrewriteaof() — Method in class RedisCluster
    @@ -142,7 +178,21 @@

    A

    RedisCluster::blmpop() — Method in class RedisCluster

    C

    -
    +
    +Redis::clearLastError() — Method in class Redis
    +

    Reset any last error on the connection to NULL

    +Redis::client() — Method in class Redis
    +
    +Redis::close() — Method in class Redis
    +
    +Redis::command() — Method in class Redis
    +
    +Redis::config() — Method in class Redis
    +

    Execute the Redis CONFIG command in a variety of ways.

    +Redis::connect() — Method in class Redis
    +
    +Redis::copy() — Method in class Redis
    +

    Make a copy of a key.

    RedisCluster::clearlasterror() — Method in class RedisCluster
    RedisCluster::client() — Method in class RedisCluster
    @@ -157,7 +207,23 @@

    A

    RedisSentinel::ckquorum() — Method in class RedisSentinel

    D

    -
    +
    +Redis::dbSize() — Method in class Redis
    +

    Return the number of keys in the currently selected Redis database.

    +Redis::debug() — Method in class Redis
    +
    +Redis::decr() — Method in class Redis
    +

    Decrement a Redis integer by 1 or a provided value.

    +Redis::decrBy() — Method in class Redis
    +

    Decrement a redis integer by a value

    +Redis::del() — Method in class Redis
    +

    Delete one or more keys from Redis.

    +Redis::delete() — Method in class Redis
    +
    +Redis::discard() — Method in class Redis
    +

    Discard a transaction currently in progress.

    +Redis::dump() — Method in class Redis
    +

    Dump Redis' internal binary representation of a key.

    RedisArray::del() — Method in class RedisArray
    RedisArray::discard() — Method in class RedisArray
    @@ -176,7 +242,32 @@

    A

    RedisCluster::dump() — Method in class RedisCluster

    E

    -
    +
    +Redis::echo() — Method in class Redis
    +

    Have Redis repeat back an arbitrary string to the client.

    +Redis::eval() — Method in class Redis
    +

    Execute a LUA script on the redis server.

    +Redis::eval_ro() — Method in class Redis
    +

    This is simply the read-only variant of eval, meaning the underlying script +may not modify data in redis.

    +Redis::evalsha() — Method in class Redis
    +

    Execute a LUA script on the server but instead of sending the script, send +the SHA1 hash of the script.

    +Redis::evalsha_ro() — Method in class Redis
    +

    This is simply the read-only variant of evalsha, meaning the underlying script +may not modify data in redis.

    +Redis::exec() — Method in class Redis
    +

    Execute either a MULTI or PIPELINE block and return the array of replies.

    +Redis::exists() — Method in class Redis
    +

    Test if one or more keys exist.

    +Redis::expire() — Method in class Redis
    +

    Sets an expiration in seconds on the key in question. If connected to +redis-server >= 7.0.0 you may send an additional "mode" argument which +modifies how the command will execute.

    +Redis::expireAt() — Method in class Redis
    +

    Set a key to expire at an exact unix timestamp.

    +Redis::expiretime() — Method in class Redis
    +

    Get the expiration of a given key as a unix timestamp

    RedisArray::exec() — Method in class RedisArray
    RedisCluster::echo() — Method in class RedisCluster
    @@ -199,7 +290,13 @@

    A

    RedisCluster::expiretime() — Method in class RedisCluster

    F

    -
    +
    +Redis::failover() — Method in class Redis
    +
    +Redis::flushAll() — Method in class Redis
    +

    Deletes every key in all Redis databases

    +Redis::flushDB() — Method in class Redis
    +

    Deletes all the keys of the currently selected database.

    RedisArray::flushall() — Method in class RedisArray
    RedisArray::flushdb() — Method in class RedisArray
    @@ -212,7 +309,62 @@

    A

    RedisSentinel::flushconfig() — Method in class RedisSentinel

    G

    -
    +
    +Redis::geoadd() — Method in class Redis
    +

    Add one or more members to a geospacial sorted set

    +Redis::geodist() — Method in class Redis
    +

    Get the distance between two members of a geospacially encoded sorted set.

    +Redis::geohash() — Method in class Redis
    +

    Retrieve one or more GeoHash encoded strings for members of the set.

    +Redis::geopos() — Method in class Redis
    +

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    +Redis::georadius() — Method in class Redis
    +

    Retrieve members of a geospacially sorted set that are within a certain radius of a location.

    +Redis::georadius_ro() — Method in class Redis
    +

    A readonly variant of GEORADIUS that may be executed on replicas.

    +Redis::georadiusbymember() — Method in class Redis
    +

    Similar to GEORADIUS except it uses a member as the center of the query.

    +Redis::georadiusbymember_ro() — Method in class Redis
    +

    This is the read-only variant of GEORADIUSBYMEMBER that can be run on replicas.

    +Redis::geosearch() — Method in class Redis
    +

    Search a geospacial sorted set for members in various ways.

    +Redis::geosearchstore() — Method in class Redis
    +

    Search a geospacial sorted set for members within a given area or range, storing the results into +a new set.

    +Redis::get() — Method in class Redis
    +

    Retrieve a string keys value.

    +Redis::getAuth() — Method in class Redis
    +

    Get the authentication information on the connection, if any.

    +Redis::getBit() — Method in class Redis
    +

    Get the bit at a given index in a string key.

    +Redis::getEx() — Method in class Redis
    +

    Get the value of a key and optionally set it's expiration.

    +Redis::getDBNum() — Method in class Redis
    +

    Get the database number PhpRedis thinks we're connected to.

    +Redis::getDel() — Method in class Redis
    +

    Get a key from Redis and delete it in an atomic operation.

    +Redis::getHost() — Method in class Redis
    +

    Return the host or Unix socket we are connected to.

    +Redis::getLastError() — Method in class Redis
    +

    Get the last error returned to us from Redis, if any.

    +Redis::getMode() — Method in class Redis
    +

    Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

    +Redis::getOption() — Method in class Redis
    +

    Retrieve the value of a configuration setting as set by Redis::setOption()

    +Redis::getPersistentID() — Method in class Redis
    +

    Get the persistent connection ID, if there is one.

    +Redis::getPort() — Method in class Redis
    +

    Get the port we are connected to. This number will be zero if we are connected to a unix socket.

    +Redis::getRange() — Method in class Redis
    +

    Retrieve a substring of a string by index.

    +Redis::getReadTimeout() — Method in class Redis
    +

    Get the currently set read timeout on the connection.

    +Redis::getset() — Method in class Redis
    +

    Sets a key and returns any previously set value, if the key already existed.

    +Redis::getTimeout() — Method in class Redis
    +

    Retrieve any set connection timeout

    +Redis::getTransferredBytes() — Method in class Redis
    +
    RedisArray::getOption() — Method in class RedisArray
    RedisCluster::geoadd() — Method in class RedisCluster
    @@ -249,7 +401,39 @@

    A

    RedisSentinel::getMasterAddrByName() — Method in class RedisSentinel

    H

    -
    +
    +Redis::hDel() — Method in class Redis
    +

    Remove one or more fields from a hash.

    +Redis::hExists() — Method in class Redis
    +

    Checks whether a field exists in a hash.

    +Redis::hGet() — Method in class Redis
    +
    +Redis::hGetAll() — Method in class Redis
    +

    Read every field and value from a hash.

    +Redis::hIncrBy() — Method in class Redis
    +

    Increment a hash field's value by an integer

    +Redis::hIncrByFloat() — Method in class Redis
    +

    Increment a hash field by a floating point value

    +Redis::hKeys() — Method in class Redis
    +

    Retrieve all of the fields of a hash.

    +Redis::hLen() — Method in class Redis
    +

    Get the number of fields in a hash.

    +Redis::hMget() — Method in class Redis
    +

    Get one or more fields from a hash.

    +Redis::hMset() — Method in class Redis
    +

    Add or update one or more hash fields and values

    +Redis::hRandField() — Method in class Redis
    +

    Get one or more random field from a hash.

    +Redis::hSet() — Method in class Redis
    +
    +Redis::hSetNx() — Method in class Redis
    +

    Set a hash field and value, but only if that field does not exist

    +Redis::hStrLen() — Method in class Redis
    +

    Get the string length of a hash field

    +Redis::hVals() — Method in class Redis
    +

    Get all of the values from a hash.

    +Redis::hscan() — Method in class Redis
    +

    Iterate over the fields and values of a hash in an incremental fashion.

    RedisArray::hscan() — Method in class RedisArray
    RedisCluster::hdel() — Method in class RedisCluster
    @@ -282,7 +466,20 @@

    A

    RedisCluster::hvals() — Method in class RedisCluster

    I

    -
    +
    +Redis::incr() — Method in class Redis
    +

    Increment a key's value, optionally by a specifc amount.

    +Redis::incrBy() — Method in class Redis
    +

    Increment a key by a specific integer value

    +Redis::incrByFloat() — Method in class Redis
    +

    Increment a numeric key by a floating point value.

    +Redis::info() — Method in class Redis
    +

    Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

    +Redis::isConnected() — Method in class Redis
    +

    Check if we are currently connected to a Redis instance.

    RedisArray::info() — Method in class RedisArray
    RedisCluster::incr() — Method in class RedisCluster
    @@ -296,12 +493,44 @@

    A

    this function, redis will return every info field. Alternatively you may pass a specific section you want returned (e.g. 'server', or 'memory') to receive only information pertaining to that section.

    K

    -
    +
    +Redis::keys() — Method in class Redis
    +
    RedisArray::keys() — Method in class RedisArray
    RedisCluster::keys() — Method in class RedisCluster

    L

    -
    +
    +Redis::lmpop() — Method in class Redis
    +

    Pop one or more elements off of one or more Redis LISTs.

    +Redis::lcs() — Method in class Redis
    +

    Get the longest common subsequence between two string keys.

    +Redis::lInsert() — Method in class Redis
    +
    +Redis::lLen() — Method in class Redis
    +

    Retrieve the lenght of a list.

    +Redis::lMove() — Method in class Redis
    +

    Move an element from one list into another.

    +Redis::lPop() — Method in class Redis
    +

    Pop one or more elements off a list.

    +Redis::lPos() — Method in class Redis
    +

    Retrieve the index of an element in a list.

    +Redis::lPush() — Method in class Redis
    +

    Prepend one or more elements to a list.

    +Redis::lPushx() — Method in class Redis
    +

    Prepend an element to a list but only if the list exists

    +Redis::lSet() — Method in class Redis
    +

    Set a list element at an index to a specific value.

    +Redis::lastSave() — Method in class Redis
    +

    Retrieve the last time Redis' database was persisted to disk.

    +Redis::lindex() — Method in class Redis
    +

    Get the element of a list by its index.

    +Redis::lrange() — Method in class Redis
    +

    Retrieve elements from a list.

    +Redis::lrem() — Method in class Redis
    +

    Remove one or more matching elements from a list.

    +Redis::ltrim() — Method in class Redis
    +

    Trim a list to a subrange of elements.

    RedisCluster::lmpop() — Method in class RedisCluster
    RedisCluster::lcs() — Method in class RedisCluster
    @@ -330,7 +559,19 @@

    A

    RedisCluster::ltrim() — Method in class RedisCluster

    M

    -
    +
    +Redis::mget() — Method in class Redis
    +

    Get one ore more string keys.

    +Redis::migrate() — Method in class Redis
    +
    +Redis::move() — Method in class Redis
    +

    Move a key to a different database on the same redis instance.

    +Redis::mset() — Method in class Redis
    +

    Set one ore more string keys.

    +Redis::msetnx() — Method in class Redis
    +

    Set one ore more string keys but only if none of the key exist.

    +Redis::multi() — Method in class Redis
    +

    Begin a transaction.

    RedisArray::mget() — Method in class RedisArray
    RedisArray::mset() — Method in class RedisArray
    @@ -351,10 +592,50 @@

    A

    RedisSentinel::myid() — Method in class RedisSentinel

    O

    -
    +
    +Redis::object() — Method in class Redis
    +
    +Redis::open() — Method in class Redis
    +
    RedisCluster::object() — Method in class RedisCluster

    P

    -
    +
    +Redis::pexpiretime() — Method in class Redis
    +

    Get the expriation timestamp of a given Redis key but in milliseconds.

    +Redis::pconnect() — Method in class Redis
    +
    +Redis::persist() — Method in class Redis
    +

    Remove the expiration from a key.

    +Redis::pexpire() — Method in class Redis
    +

    Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 +you can pass an optional mode argument that modifies how the command will execute.

    +Redis::pexpireAt() — Method in class Redis
    +

    Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to +Redis >= 7.0.0 you can pass an optional 'mode' argument.

    +Redis::pfadd() — Method in class Redis
    +

    Add one or more elements to a Redis HyperLogLog key

    +Redis::pfcount() — Method in class Redis
    +

    Retrieve the cardinality of a Redis HyperLogLog key.

    +Redis::pfmerge() — Method in class Redis
    +

    Merge one or more source HyperLogLog sets into a destination set.

    +Redis::ping() — Method in class Redis
    +

    PING the redis server with an optional string argument.

    +Redis::pipeline() — Method in class Redis
    +

    Enter into pipeline mode.

    +Redis::popen() — Method in class Redis
    +
    +Redis::psetex() — Method in class Redis
    +

    Set a key with an expiration time in milliseconds

    +Redis::psubscribe() — Method in class Redis
    +

    Subscribe to one or more glob-style patterns

    +Redis::pttl() — Method in class Redis
    +

    Get a keys time to live in milliseconds.

    +Redis::publish() — Method in class Redis
    +

    Publish a message to a pubsub channel

    +Redis::pubsub() — Method in class Redis
    +
    +Redis::punsubscribe() — Method in class Redis
    +

    Unsubscribe from one or more channels by pattern

    RedisArray::ping() — Method in class RedisArray
    RedisCluster::pexpiretime() — Method in class RedisCluster
    @@ -387,7 +668,34 @@

    A

    RedisSentinel::ping() — Method in class RedisSentinel

    R

    -
    RedisArray
    +
    Redis
    +
    +Redis::rPush() — Method in class Redis
    +

    Append one or more elements to a list.

    +Redis::rPushx() — Method in class Redis
    +

    Append an element to a list but only if the list exists

    +Redis::rPop() — Method in class Redis
    +

    Pop one or more elements from the end of a list.

    +Redis::randomKey() — Method in class Redis
    +

    Return a random key from the current database

    +Redis::rawcommand() — Method in class Redis
    +

    Execute any arbitrary Redis command by name.

    +Redis::rename() — Method in class Redis
    +

    Unconditionally rename a key from $old_name to $new_name

    +Redis::renameNx() — Method in class Redis
    +

    Renames $key_src to $key_dst but only if newkey does not exist.

    +Redis::reset() — Method in class Redis
    +

    Reset the state of the connection.

    +Redis::restore() — Method in class Redis
    +

    Restore a key by the binary payload generated by the DUMP command.

    +Redis::role() — Method in class Redis
    +

    Query whether the connected instance is a primary or replica

    +Redis::rpoplpush() — Method in class Redis
    +

    Atomically pop an element off the end of a Redis LIST and push it to the beginning of +another.

    +Redis::replicaof() — Method in class Redis
    +

    Used to turn a Redis instance into a replica of another, or to remove +replica status promoting the instance to a primary.

    RedisArray
    RedisCluster
    RedisCluster::randomkey() — Method in class RedisCluster
    @@ -410,11 +718,100 @@

    A

    RedisCluster::rpushx() — Method in class RedisCluster
    RedisClusterException
    +
    RedisException
    RedisSentinel
    RedisSentinel::reset() — Method in class RedisSentinel

    S

    -
    +
    +Redis::sAdd() — Method in class Redis
    +

    Add one or more values to a Redis SET key.

    +Redis::sAddArray() — Method in class Redis
    +

    Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but +instead of being variadic, takes a single array of values.

    +Redis::sDiff() — Method in class Redis
    +

    Given one or more Redis SETS, this command returns all of the members from the first +set that are not in any subsequent set.

    +Redis::sDiffStore() — Method in class Redis
    +

    This method performs the same operation as SDIFF except it stores the resulting diff +values in a specified destination key.

    +Redis::sInter() — Method in class Redis
    +

    Given one or more Redis SET keys, this command will return all of the elements that are +in every one.

    +Redis::sintercard() — Method in class Redis
    +

    Compute the intersection of one or more sets and return the cardinality of the result.

    +Redis::sInterStore() — Method in class Redis
    +

    Perform the intersection of one or more Redis SETs, storing the result in a destination +key, rather than returning them.

    +Redis::sMembers() — Method in class Redis
    +

    Retrieve every member from a set key.

    +Redis::sMisMember() — Method in class Redis
    +

    Check if one or more values are members of a set.

    +Redis::sMove() — Method in class Redis
    +

    Pop a member from one set and push it onto another. This command will create the +destination set if it does not currently exist.

    +Redis::sPop() — Method in class Redis
    +

    Remove one or more elements from a set.

    +Redis::sRandMember() — Method in class Redis
    +

    Retrieve one or more random members of a set.

    +Redis::sUnion() — Method in class Redis
    +

    Returns the union of one or more Redis SET keys.

    +Redis::sUnionStore() — Method in class Redis
    +

    Perform a union of one or more Redis SET keys and store the result in a new set

    +Redis::save() — Method in class Redis
    +

    Persist the Redis database to disk. This command will block the server until the save is +completed. For a nonblocking alternative, see Redis::bgsave().

    +Redis::scan() — Method in class Redis
    +

    Incrementally scan the Redis keyspace, with optional pattern and type matching.

    +Redis::scard() — Method in class Redis
    +

    Retrieve the number of members in a Redis set.

    +Redis::script() — Method in class Redis
    +

    An administrative command used to interact with LUA scripts stored on the server.

    +Redis::select() — Method in class Redis
    +

    Select a specific Redis database.

    +Redis::set() — Method in class Redis
    +

    Create or set a Redis STRING key to a value.

    +Redis::setBit() — Method in class Redis
    +

    Set a specific bit in a Redis string to zero or one

    +Redis::setRange() — Method in class Redis
    +

    Update or append to a Redis string at a specific starting index

    +Redis::setOption() — Method in class Redis
    +

    Set a configurable option on the Redis object.

    +Redis::setex() — Method in class Redis
    +

    Set a Redis STRING key with a specific expiration in seconds.

    +Redis::setnx() — Method in class Redis
    +

    Set a key to a value, but only if that key does not already exist.

    +Redis::sismember() — Method in class Redis
    +

    Check whether a given value is the member of a Redis SET.

    +Redis::slaveof() — Method in class Redis
    +

    Turn a redis instance into a replica of another or promote a replica +to a primary.

    +Redis::slowlog() — Method in class Redis
    +

    Interact with Redis' slowlog functionality in various ways, depending +on the value of 'operation'.

    +Redis::sort() — Method in class Redis
    +

    Sort the contents of a Redis key in various ways.

    +Redis::sort_ro() — Method in class Redis
    +

    This is simply a read-only variant of the sort command

    +Redis::sortAsc() — Method in class Redis
    +
    +Redis::sortAscAlpha() — Method in class Redis
    +
    +Redis::sortDesc() — Method in class Redis
    +
    +Redis::sortDescAlpha() — Method in class Redis
    +
    +Redis::srem() — Method in class Redis
    +

    Remove one or more values from a Redis SET key.

    +Redis::sscan() — Method in class Redis
    +

    Scan the members of a redis SET key.

    +Redis::strlen() — Method in class Redis
    +

    Retrieve the length of a Redis STRING key.

    +Redis::subscribe() — Method in class Redis
    +

    Subscribe to one or more Redis pubsub channels.

    +Redis::swapdb() — Method in class Redis
    +

    Atomically swap two Redis databases so that all of the keys in the source database will +now be in the destination database and vice-versa.

    RedisArray::save() — Method in class RedisArray
    RedisArray::scan() — Method in class RedisArray
    @@ -491,7 +888,15 @@

    A

    RedisSentinel::slaves() — Method in class RedisSentinel

    T

    -
    +
    +Redis::touch() — Method in class Redis
    +

    Update one or more keys last modified metadata.

    +Redis::time() — Method in class Redis
    +

    Retrieve the server time from the connected Redis instance.

    +Redis::ttl() — Method in class Redis
    +

    Get the amount of time a Redis key has before it will expire, in seconds.

    +Redis::type() — Method in class Redis
    +

    Get the type of a given Redis key.

    RedisCluster::touch() — Method in class RedisCluster
    RedisCluster::time() — Method in class RedisCluster
    @@ -500,7 +905,15 @@

    A

    RedisCluster::type() — Method in class RedisCluster

    U

    -
    +
    +Redis::unlink() — Method in class Redis
    +

    Delete one or more keys from the Redis database. Unlike this operation, the actual +deletion is asynchronous, meaning it is safe to delete large keys without fear of +Redis blocking for a long period of time.

    +Redis::unsubscribe() — Method in class Redis
    +

    Unsubscribe from one or more subscribed channels.

    +Redis::unwatch() — Method in class Redis
    +

    Remove any previously WATCH'ed keys in a transaction.

    RedisArray::unlink() — Method in class RedisArray
    RedisArray::unwatch() — Method in class RedisArray
    @@ -511,10 +924,46 @@

    A

    RedisCluster::unwatch() — Method in class RedisCluster

    W

    -
    +
    +Redis::watch() — Method in class Redis
    +

    Watch one or more keys for conditional execution of a transaction.

    +Redis::wait() — Method in class Redis
    +

    Block the client up to the provided timeout until a certain number of replicas have confirmed +recieving them.

    RedisCluster::watch() — Method in class RedisCluster

    X

    -
    +
    +Redis::xack() — Method in class Redis
    +

    Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but +not yet acknowledged by XACK.)

    +Redis::xadd() — Method in class Redis
    +

    Append a message to a stream.

    +Redis::xautoclaim() — Method in class Redis
    +

    This command allows a consumer to claim pending messages that have been idle for a specified period of time.

    +Redis::xclaim() — Method in class Redis
    +

    This method allows a consumer to take ownership of pending stream entries, by ID. Another +command that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

    +Redis::xdel() — Method in class Redis
    +

    Remove one or more specific IDs from a stream.

    +Redis::xgroup() — Method in class Redis
    +
    XGROUP
    +Redis::xinfo() — Method in class Redis
    +

    Retrieve information about a stream key.

    +Redis::xlen() — Method in class Redis
    +

    Get the number of messages in a Redis STREAM key.

    +Redis::xpending() — Method in class Redis
    +

    Interact with stream messages that have been consumed by a consumer group but not yet +acknowledged with XACK.

    +Redis::xrange() — Method in class Redis
    +

    Get a range of entries from a STREAM key.

    +Redis::xread() — Method in class Redis
    +

    Consume one or more unconsumed elements in one or more streams.

    +Redis::xreadgroup() — Method in class Redis
    +

    Read one or more messages using a consumer group.

    +Redis::xrevrange() — Method in class Redis
    +

    Get a range of entries from a STREAM ke in reverse cronological order.

    +Redis::xtrim() — Method in class Redis
    +

    Truncate a STREAM key in various ways.

    RedisCluster::xack() — Method in class RedisCluster
    RedisCluster::xadd() — Method in class RedisCluster
    @@ -524,6 +973,8 @@

    A

    RedisCluster::xdel() — Method in class RedisCluster
    RedisCluster::xgroup() — Method in class RedisCluster
    +
    +RedisCluster::xautoclaim() — Method in class RedisCluster
    RedisCluster::xinfo() — Method in class RedisCluster
    @@ -541,7 +992,75 @@

    A

    RedisCluster::xtrim() — Method in class RedisCluster

    Z

    -
    +
    +Redis::zmpop() — Method in class Redis
    +

    POP one or more of the highest or lowest scoring elements from one or more sorted sets.

    +Redis::zAdd() — Method in class Redis
    +

    Add one or more elements and scores to a Redis sorted set.

    +Redis::zCard() — Method in class Redis
    +

    Return the number of elements in a sorted set.

    +Redis::zCount() — Method in class Redis
    +

    Count the number of members in a sorted set with scores inside a provided range.

    +Redis::zIncrBy() — Method in class Redis
    +

    Create or increment the score of a member in a Redis sorted set

    +Redis::zLexCount() — Method in class Redis
    +

    Count the number of elements in a sorted set whos members fall within the provided +lexographical range.

    +Redis::zMscore() — Method in class Redis
    +

    Retrieve the score of one or more members in a sorted set.

    +Redis::zPopMax() — Method in class Redis
    +

    Pop one or more of the highest scoring elements from a sorted set.

    +Redis::zPopMin() — Method in class Redis
    +

    Pop one or more of the lowest scoring elements from a sorted set.

    +Redis::zRange() — Method in class Redis
    +

    Retrieve a range of elements of a sorted set between a start and end point.

    +Redis::zRangeByLex() — Method in class Redis
    +

    Retrieve a range of elements from a sorted set by legographical range.

    +Redis::zRangeByScore() — Method in class Redis
    +

    Retrieve a range of members from a sorted set by their score.

    +Redis::zrangestore() — Method in class Redis
    +

    This command is similar to ZRANGE except that instead of returning the values directly +it will store them in a destination key provided by the user

    +Redis::zRandMember() — Method in class Redis
    +

    Retrieve one or more random members from a Redis sorted set.

    +Redis::zRank() — Method in class Redis
    +

    Get the rank of a member of a sorted set, by score.

    +Redis::zRem() — Method in class Redis
    +

    Remove one or more members from a Redis sorted set.

    +Redis::zRemRangeByLex() — Method in class Redis
    +

    Remove zero or more elements from a Redis sorted set by legographical range.

    +Redis::zRemRangeByRank() — Method in class Redis
    +

    Remove one or more members of a sorted set by their rank.

    +Redis::zRemRangeByScore() — Method in class Redis
    +

    Remove one or more members of a sorted set by their score.

    +Redis::zRevRange() — Method in class Redis
    +

    List the members of a Redis sorted set in reverse order

    +Redis::zRevRangeByLex() — Method in class Redis
    +

    List members of a Redis sorted set within a legographical range, in reverse order.

    +Redis::zRevRangeByScore() — Method in class Redis
    +

    List elements from a Redis sorted set by score, highest to lowest

    +Redis::zRevRank() — Method in class Redis
    +

    Retrieve a member of a sorted set by reverse rank.

    +Redis::zScore() — Method in class Redis
    +

    Get the score of a member of a sorted set.

    +Redis::zdiff() — Method in class Redis
    +

    Given one or more sorted set key names, return every element that is in the first +set but not any of the others.

    +Redis::zdiffstore() — Method in class Redis
    +

    Store the difference of one or more sorted sets in a destination sorted set.

    +Redis::zinter() — Method in class Redis
    +

    Compute the intersection of one or more sorted sets and return the members

    +Redis::zintercard() — Method in class Redis
    +

    Similar to ZINTER but instead of returning the intersected values, this command returns the +cardinality of the intersected set.

    +Redis::zinterstore() — Method in class Redis
    +

    Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

    +Redis::zscan() — Method in class Redis
    +

    Scan the members of a sorted set incrementally, using a cursor

    +Redis::zunion() — Method in class Redis
    +

    Retrieve the union of one or more sorted sets

    +Redis::zunionstore() — Method in class Redis
    +

    Perform a union on one or more Redis sets and store the result in a destination sorted set.

    RedisArray::zscan() — Method in class RedisArray
    RedisCluster::zmpop() — Method in class RedisCluster
    @@ -596,7 +1115,34 @@

    A

    RedisCluster::zunionstore() — Method in class RedisCluster

    _

    -
    +
    +Redis::__construct() — Method in class Redis
    +

    Create a new Redis instance. If passed sufficient information in the +options array it is also possible to connect to an instance at the same +time.

    +Redis::__destruct() — Method in class Redis
    +
    +Redis::_compress() — Method in class Redis
    +

    Compress a value with the currently configured compressor as set with +Redis::setOption().

    +Redis::_uncompress() — Method in class Redis
    +

    Uncompress the provided argument that has been compressed with the +currently configured compressor as set with Redis::setOption().

    +Redis::_prefix() — Method in class Redis
    +

    Prefix the passed argument with the currently set key prefix as set +with Redis::setOption().

    +Redis::_serialize() — Method in class Redis
    +

    Serialize the provided value with the currently set serializer as set +with Redis::setOption().

    +Redis::_unserialize() — Method in class Redis
    +

    Unserialize the passed argument with the currently set serializer as set +with Redis::setOption().

    +Redis::_pack() — Method in class Redis
    +

    Pack the provided value with the configured serializer and compressor +as set with Redis::setOption().

    +Redis::_unpack() — Method in class Redis
    +

    Unpack the provided value with the configured compressor and serializer +as set with Redis::setOption().

    RedisArray::__call() — Method in class RedisArray
    RedisArray::__construct() — Method in class RedisArray
    diff --git a/docs/doctum-search.json b/docs/doctum-search.json index 09b2277810..20e66a1b42 100644 --- a/docs/doctum-search.json +++ b/docs/doctum-search.json @@ -1 +1 @@ -{"items":[{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

    See Redis::blpop()

    "},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

    See Redis::brpop()

    "},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

    See Redis::brpoplpush()

    "},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

    PING an instance in the redis cluster.

    "},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} +{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

    Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

    "},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":null},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

    Compress a value with the currently configured compressor as set with\nRedis::setOption().

    "},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

    Uncompress the provided argument that has been compressed with the\ncurrently configured compressor as set with Redis::setOption().

    "},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

    Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

    Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

    Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

    Pack the provided value with the configured serializer and compressor\nas set with Redis::setOption().

    "},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

    Unpack the provided value with the configured compressor and serializer\nas set with Redis::setOption().

    "},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":null},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

    Append data to a Redis STRING key.

    "},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

    Authenticate a Redis connection after its been established.

    "},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

    Execute a save of the Redis database in the background.

    "},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

    Asynchronously rewrite Redis' append-only file

    "},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

    Count the number of set bits in a Redis string.

    "},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

    Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

    "},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

    Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

    "},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

    Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

    "},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

    POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

    "},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

    POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

    "},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

    POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

    "},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

    POP one or more of the highest or lowest scoring elements from one or more sorted sets.

    "},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

    Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

    "},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

    Pop one or more elements off of one or more Redis LISTs.

    "},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

    Reset any last error on the connection to NULL

    "},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":null},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":null},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":null},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

    Execute the Redis CONFIG command in a variety of ways.

    "},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":null},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

    Make a copy of a key.

    "},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

    Return the number of keys in the currently selected Redis database.

    "},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":null},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

    Decrement a Redis integer by 1 or a provided value.

    "},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

    Decrement a redis integer by a value

    "},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

    Delete one or more keys from Redis.

    "},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

    Discard a transaction currently in progress.

    "},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

    Dump Redis' internal binary representation of a key.

    "},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

    Have Redis repeat back an arbitrary string to the client.

    "},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

    Execute a LUA script on the redis server.

    "},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

    This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

    "},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

    Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

    "},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

    This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

    "},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

    Execute either a MULTI or PIPELINE block and return the array of replies.

    "},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

    Test if one or more keys exist.

    "},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

    Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

    "},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

    Set a key to expire at an exact unix timestamp.

    "},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

    Get the expiration of a given key as a unix timestamp

    "},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

    Get the expriation timestamp of a given Redis key but in milliseconds.

    "},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

    Deletes every key in all Redis databases

    "},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

    Deletes all the keys of the currently selected database.

    "},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":"

    Add one or more members to a geospacial sorted set

    "},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":"

    Get the distance between two members of a geospacially encoded sorted set.

    "},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":"

    Retrieve one or more GeoHash encoded strings for members of the set.

    "},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":"

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    "},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":"

    Retrieve members of a geospacially sorted set that are within a certain radius of a location.

    "},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":"

    A readonly variant of GEORADIUS that may be executed on replicas.

    "},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":"

    Similar to GEORADIUS except it uses a member as the center of the query.

    "},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":"

    This is the read-only variant of GEORADIUSBYMEMBER that can be run on replicas.

    "},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":"

    Search a geospacial sorted set for members in various ways.

    "},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":"

    Search a geospacial sorted set for members within a given area or range, storing the results into\na new set.

    "},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":"

    Retrieve a string keys value.

    "},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

    Get the authentication information on the connection, if any.

    "},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":"

    Get the bit at a given index in a string key.

    "},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":"

    Get the value of a key and optionally set it's expiration.

    "},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":"

    Get the database number PhpRedis thinks we're connected to.

    "},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":"

    Get a key from Redis and delete it in an atomic operation.

    "},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

    Return the host or Unix socket we are connected to.

    "},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

    Get the last error returned to us from Redis, if any.

    "},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

    Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

    "},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

    Retrieve the value of a configuration setting as set by Redis::setOption()

    "},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

    Get the persistent connection ID, if there is one.

    "},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

    Get the port we are connected to. This number will be zero if we are connected to a unix socket.

    "},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

    Retrieve a substring of a string by index.

    "},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

    Get the longest common subsequence between two string keys.

    "},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

    Get the currently set read timeout on the connection.

    "},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

    Sets a key and returns any previously set value, if the key already existed.

    "},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

    Retrieve any set connection timeout

    "},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":null},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

    Remove one or more fields from a hash.

    "},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

    Checks whether a field exists in a hash.

    "},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

    Read every field and value from a hash.

    "},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

    Increment a hash field's value by an integer

    "},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

    Increment a hash field by a floating point value

    "},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

    Retrieve all of the fields of a hash.

    "},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

    Get the number of fields in a hash.

    "},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

    Get one or more fields from a hash.

    "},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

    Add or update one or more hash fields and values

    "},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

    Get one or more random field from a hash.

    "},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":null},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

    Set a hash field and value, but only if that field does not exist

    "},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

    Get the string length of a hash field

    "},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

    Get all of the values from a hash.

    "},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

    Iterate over the fields and values of a hash in an incremental fashion.

    "},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

    Increment a key's value, optionally by a specifc amount.

    "},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

    Increment a key by a specific integer value

    "},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

    Increment a numeric key by a floating point value.

    "},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

    Check if we are currently connected to a Redis instance.

    "},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":"

    Retrieve the lenght of a list.

    "},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":"

    Move an element from one list into another.

    "},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":"

    Pop one or more elements off a list.

    "},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":"

    Retrieve the index of an element in a list.

    "},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":"

    Prepend one or more elements to a list.

    "},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":"

    Append one or more elements to a list.

    "},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":"

    Prepend an element to a list but only if the list exists

    "},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":"

    Append an element to a list but only if the list exists

    "},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":"

    Set a list element at an index to a specific value.

    "},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":"

    Retrieve the last time Redis' database was persisted to disk.

    "},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":"

    Get the element of a list by its index.

    "},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":"

    Retrieve elements from a list.

    "},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":"

    Remove one or more matching elements from a list.

    "},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":"

    Trim a list to a subrange of elements.

    "},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":"

    Get one ore more string keys.

    "},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":null},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":"

    Move a key to a different database on the same redis instance.

    "},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":"

    Set one ore more string keys.

    "},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":"

    Set one ore more string keys but only if none of the key exist.

    "},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":"

    Begin a transaction.

    "},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":null},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":null},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":"

    Remove the expiration from a key.

    "},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

    Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

    "},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

    Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

    "},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

    Add one or more elements to a Redis HyperLogLog key

    "},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

    Retrieve the cardinality of a Redis HyperLogLog key.

    "},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

    Merge one or more source HyperLogLog sets into a destination set.

    "},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

    PING the redis server with an optional string argument.

    "},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

    Enter into pipeline mode.

    "},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":"

    Set a key with an expiration time in milliseconds

    "},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

    Subscribe to one or more glob-style patterns

    "},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

    Get a keys time to live in milliseconds.

    "},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

    Publish a message to a pubsub channel

    "},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":null},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

    Unsubscribe from one or more channels by pattern

    "},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

    Pop one or more elements from the end of a list.

    "},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

    Return a random key from the current database

    "},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

    Execute any arbitrary Redis command by name.

    "},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

    Unconditionally rename a key from $old_name to $new_name

    "},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

    Renames $key_src to $key_dst but only if newkey does not exist.

    "},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

    Reset the state of the connection.

    "},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

    Restore a key by the binary payload generated by the DUMP command.

    "},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

    Query whether the connected instance is a primary or replica

    "},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

    Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

    "},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

    Add one or more values to a Redis SET key.

    "},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

    Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

    "},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

    Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

    "},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

    This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

    "},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

    Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

    "},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

    Compute the intersection of one or more sets and return the cardinality of the result.

    "},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

    Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

    "},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

    Retrieve every member from a set key.

    "},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

    Check if one or more values are members of a set.

    "},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

    Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

    "},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

    Remove one or more elements from a set.

    "},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

    Retrieve one or more random members of a set.

    "},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

    Returns the union of one or more Redis SET keys.

    "},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

    Perform a union of one or more Redis SET keys and store the result in a new set

    "},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

    Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

    "},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

    Incrementally scan the Redis keyspace, with optional pattern and type matching.

    "},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

    Retrieve the number of members in a Redis set.

    "},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

    An administrative command used to interact with LUA scripts stored on the server.

    "},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

    Select a specific Redis database.

    "},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

    Create or set a Redis STRING key to a value.

    "},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

    Set a specific bit in a Redis string to zero or one

    "},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

    Update or append to a Redis string at a specific starting index

    "},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

    Set a configurable option on the Redis object.

    "},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

    Set a Redis STRING key with a specific expiration in seconds.

    "},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

    Set a key to a value, but only if that key does not already exist.

    "},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

    Check whether a given value is the member of a Redis SET.

    "},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

    Turn a redis instance into a replica of another or promote a replica\nto a primary.

    "},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

    Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

    "},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

    Update one or more keys last modified metadata.

    "},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

    Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

    "},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

    Sort the contents of a Redis key in various ways.

    "},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

    This is simply a read-only variant of the sort command

    "},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

    Remove one or more values from a Redis SET key.

    "},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

    Scan the members of a redis SET key.

    "},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

    Retrieve the length of a Redis STRING key.

    "},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

    Subscribe to one or more Redis pubsub channels.

    "},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

    Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

    "},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

    Retrieve the server time from the connected Redis instance.

    "},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

    Get the amount of time a Redis key has before it will expire, in seconds.

    "},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

    Get the type of a given Redis key.

    "},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

    Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

    "},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

    Unsubscribe from one or more subscribed channels.

    "},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

    Remove any previously WATCH'ed keys in a transaction.

    "},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":"

    Watch one or more keys for conditional execution of a transaction.

    "},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

    Block the client up to the provided timeout until a certain number of replicas have confirmed\nrecieving them.

    "},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":"

    Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but\nnot yet acknowledged by XACK.)

    "},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

    Append a message to a stream.

    "},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":"

    This command allows a consumer to claim pending messages that have been idle for a specified period of time.

    "},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":"

    This method allows a consumer to take ownership of pending stream entries, by ID. Another\ncommand that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

    "},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

    Remove one or more specific IDs from a stream.

    "},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

    Retrieve information about a stream key.

    "},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

    Get the number of messages in a Redis STREAM key.

    "},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

    Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

    "},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

    Get a range of entries from a STREAM key.

    "},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

    Consume one or more unconsumed elements in one or more streams.

    "},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

    Read one or more messages using a consumer group.

    "},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

    Get a range of entries from a STREAM ke in reverse cronological order.

    "},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

    Truncate a STREAM key in various ways.

    "},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

    Add one or more elements and scores to a Redis sorted set.

    "},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

    Return the number of elements in a sorted set.

    "},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

    Count the number of members in a sorted set with scores inside a provided range.

    "},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

    Create or increment the score of a member in a Redis sorted set

    "},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

    Count the number of elements in a sorted set whos members fall within the provided\nlexographical range.

    "},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

    Retrieve the score of one or more members in a sorted set.

    "},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

    Pop one or more of the highest scoring elements from a sorted set.

    "},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

    Pop one or more of the lowest scoring elements from a sorted set.

    "},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

    Retrieve a range of elements of a sorted set between a start and end point.

    "},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

    Retrieve a range of elements from a sorted set by legographical range.

    "},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

    Retrieve a range of members from a sorted set by their score.

    "},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

    This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

    "},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

    Retrieve one or more random members from a Redis sorted set.

    "},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

    Get the rank of a member of a sorted set, by score.

    "},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

    Remove one or more members from a Redis sorted set.

    "},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

    Remove zero or more elements from a Redis sorted set by legographical range.

    "},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

    Remove one or more members of a sorted set by their rank.

    "},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

    Remove one or more members of a sorted set by their score.

    "},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

    List the members of a Redis sorted set in reverse order

    "},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

    List members of a Redis sorted set within a legographical range, in reverse order.

    "},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

    List elements from a Redis sorted set by score, highest to lowest

    "},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

    Retrieve a member of a sorted set by reverse rank.

    "},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

    Get the score of a member of a sorted set.

    "},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

    Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

    "},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

    Store the difference of one or more sorted sets in a destination sorted set.

    "},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

    Compute the intersection of one or more sorted sets and return the members

    "},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

    Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

    "},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

    Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

    "},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

    Scan the members of a sorted set incrementally, using a cursor

    "},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

    Retrieve the union of one or more sorted sets

    "},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

    Perform a union on one or more Redis sets and store the result in a destination sorted set.

    "},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

    See Redis::blpop()

    "},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

    See Redis::brpop()

    "},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

    See Redis::brpoplpush()

    "},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

    PING an instance in the redis cluster.

    "},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xautoclaim","p":"RedisCluster.html#method_xautoclaim","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} diff --git a/docs/doctum.js b/docs/doctum.js index 5d1acead9c..d487386524 100644 --- a/docs/doctum.js +++ b/docs/doctum.js @@ -1,5 +1,5 @@ var Doctum = { - treeJson: {"tree":{"l":0,"n":"","p":"","c":[{"l":1,"n":"[Global Namespace]","p":"[Global_Namespace]","c":[{"l":2,"n":"RedisArray","p":"RedisArray"},{"l":2,"n":"RedisCluster","p":"RedisCluster"},{"l":2,"n":"RedisClusterException","p":"RedisClusterException"},{"l":2,"n":"RedisSentinel","p":"RedisSentinel"}]}]},"treeOpenLevel":2}, + treeJson: {"tree":{"l":0,"n":"","p":"","c":[{"l":1,"n":"[Global Namespace]","p":"[Global_Namespace]","c":[{"l":2,"n":"Redis","p":"Redis"},{"l":2,"n":"RedisArray","p":"RedisArray"},{"l":2,"n":"RedisCluster","p":"RedisCluster"},{"l":2,"n":"RedisClusterException","p":"RedisClusterException"},{"l":2,"n":"RedisException","p":"RedisException"},{"l":2,"n":"RedisSentinel","p":"RedisSentinel"}]}]},"treeOpenLevel":2}, /** @var boolean */ treeLoaded: false, /** @var boolean */ diff --git a/docs/index.html b/docs/index.html index 8b17f7c10f..e0ee6367d8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -82,6 +82,11 @@

    Classes

    +
    + Redis
    +
    +
    +
    @@ -95,6 +100,11 @@

    Classes

    +
    +
    diff --git a/docs/renderer.index b/docs/renderer.index index f3a75c7ba9..752ee083da 100644 --- a/docs/renderer.index +++ b/docs/renderer.index @@ -1 +1 @@ -O:21:"Doctum\Renderer\Index":3:{i:0;a:4:{s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:21:"RedisClusterException";s:40:"1783d14c476f95598062edb44dab7284b9b2680d";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:4:"main";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file +O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"7230a9518fe0e79ae51f6b49d269053535a34199";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"2f2132e45b1d60011f8ef9298cb35b7ba2b247d5";s:21:"RedisClusterException";s:40:"2f2132e45b1d60011f8ef9298cb35b7ba2b247d5";s:14:"RedisException";s:40:"7230a9518fe0e79ae51f6b49d269053535a34199";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:4:"main";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file diff --git a/docs/traits.html b/docs/traits.html index 4a0de58f5a..4c73c7a8fa 100644 --- a/docs/traits.html +++ b/docs/traits.html @@ -80,7 +80,7 @@

    Traits

    -
    +
    From 0243dd9d2a321fade538da3674c484e69c167bd4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 17 Nov 2022 20:42:07 -0800 Subject: [PATCH 1717/1986] SENTINEL RESET returns a long. --- redis_sentinel.c | 2 +- redis_sentinel.stub.php | 2 +- redis_sentinel_arginfo.h | 2 +- redis_sentinel_legacy_arginfo.h | 2 +- tests/RedisSentinelTest.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/redis_sentinel.c b/redis_sentinel.c index 2d506bd2ec..1a4a05f480 100644 --- a/redis_sentinel.c +++ b/redis_sentinel.c @@ -140,7 +140,7 @@ PHP_METHOD(RedisSentinel, ping) PHP_METHOD(RedisSentinel, reset) { - REDIS_PROCESS_KW_CMD("reset", redis_sentinel_str_cmd, redis_boolean_response); + REDIS_PROCESS_KW_CMD("reset", redis_sentinel_str_cmd, redis_long_response); } PHP_METHOD(RedisSentinel, sentinels) diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php index 304cd41456..6ec54d8549 100644 --- a/redis_sentinel.stub.php +++ b/redis_sentinel.stub.php @@ -33,7 +33,7 @@ public function myid(): string; /** @return bool|RedisSentinel */ public function ping(); - /** @return bool|RedisSentinel */ + /** @return int|RedisSentinel */ public function reset(string $pattern); /** @return array|bool|RedisSentinel */ diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h index 7059560d64..43c57e9b1a 100644 --- a/redis_sentinel_arginfo.h +++ b/redis_sentinel_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4055ace9f1cf20bef89bdb5d3219470b4c8915e6 */ + * Stub hash: 847c735dfbbb643366344acfe6e2c5e8b76d0520 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h index 16af15b6bc..07f34be98f 100644 --- a/redis_sentinel_legacy_arginfo.h +++ b/redis_sentinel_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4055ace9f1cf20bef89bdb5d3219470b4c8915e6 */ + * Stub hash: 847c735dfbbb643366344acfe6e2c5e8b76d0520 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1) ZEND_ARG_INFO(0, host) diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php index 4d941dd95a..2d188b3c2f 100644 --- a/tests/RedisSentinelTest.php +++ b/tests/RedisSentinelTest.php @@ -96,7 +96,7 @@ public function testPing() public function testReset() { - $this->assertFalse($this->sentinel->reset('*')); + $this->assertEquals(1, $this->sentinel->reset('*')); } public function testSentinels() From 872b69313be6b338696372656354a77f81dcac9a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 17 Nov 2022 22:33:46 -0800 Subject: [PATCH 1718/1986] Documentation: Normalize formatting. --- docs/Redis.html | 647 ++++++++++++++++++++------------------------ docs/renderer.index | 2 +- redis.stub.php | 328 ++++++++-------------- 3 files changed, 398 insertions(+), 579 deletions(-) diff --git a/docs/Redis.html b/docs/Redis.html index 24190f0131..a74aa2d48f 100644 --- a/docs/Redis.html +++ b/docs/Redis.html @@ -4570,8 +4570,8 @@

    Parameters

    $options

    An array with modifiers on how COPY should operate.

    $options = [
    -    'REPLACE' => true|false // Whether to replace an existing key.
    -    'DB' => int             // Copy key to specific db.
    +    'REPLACE' => true|false # Whether to replace an existing key.
    +    'DB' => int             # Copy key to specific db.
     ];
    @@ -6370,19 +6370,19 @@

    Parameters

    $options

    An array of options that modifies how the command behaves.

    $options = [
    -    'WITHCOORD',     // Return members and their coordinates.
    -    'WITHDIST',      // Return members and their distances from the center.
    -    'WITHHASH',      // Return members GeoHash string.
    -    'ASC' | 'DESC',  // The sort order of returned members
    +    'WITHCOORD',     # Return members and their coordinates.
    +    'WITHDIST',      # Return members and their distances from the center.
    +    'WITHHASH',      # Return members GeoHash string.
    +    'ASC' | 'DESC',  # The sort order of returned members
     
    -    // Limit to N returned members.  Optionally a two element array may be
    -    // passed as the `LIMIT` argument, and the `ANY` argument.
    +    # Limit to N returned members.  Optionally a two element array may be
    +    # passed as the `LIMIT` argument, and the `ANY` argument.
         'COUNT' => [<int>], or [<int>, <bool>]
     
    -    // Instead of returning members, store them in the specified key.
    +    # Instead of returning members, store them in the specified key.
         'STORE' => <string>
     
    -    // Store the distances in the specified key
    +    # Store the distances in the specified key
         'STOREDIST' => <string>
     ];
    @@ -6753,11 +6753,11 @@

    Parameters

    array $options
    $options = [
    -    'ASC' | 'DESC',  // The sort order of returned members
    -    'WITHDIST'       // Also store distances.
    +    'ASC' | 'DESC',  # The sort order of returned members
    +    'WITHDIST'       # Also store distances.
     
    -    // Limit to N returned members.  Optionally a two element array may be
    -    // passed as the `LIMIT` argument, and the `ANY` argument.
    +    # Limit to N returned members.  Optionally a two element array may be
    +    # passed as the `LIMIT` argument, and the `ANY` argument.
         'COUNT' => [<int>], or [<int>, <bool>]
     ];
    @@ -6979,11 +6979,11 @@

    Parameters

    $options

    Options to modify how the command works.

    $options = [
    -    'EX'     => <seconds>      // Expire in N seconds
    -    'PX'     => <milliseconds> // Expire in N milliseconds
    -    'EXAT'   => <timestamp>    // Expire at a unix timestamp (in seconds)
    -    'PXAT'   => <mstimestamp>  // Expire at a unix timestamp (in milliseconds);
    -    'PERSIST'                  // Remove any configured expiration on the key.
    +    'EX'     => <seconds>      # Expire in N seconds
    +    'PX'     => <milliseconds> # Expire in N milliseconds
    +    'EXAT'   => <timestamp>    # Expire at a unix timestamp (in seconds)
    +    'PXAT'   => <mstimestamp>  # Expire at a unix timestamp (in milliseconds);
    +    'PERSIST'                  # Remove any configured expiration on the key.
     ];
    @@ -7454,14 +7454,14 @@

    Parameters

    $options

    An optional array of modifiers for the comand.

    $options = [
    -    'MINMATCHLEN'  => int  // Exclude matching substrings that are less than this value
    +    'MINMATCHLEN'  => int  # Exclude matching substrings that are less than this value
     
    -    'WITHMATCHLEN' => bool // Whether each match should also include its length.
    +    'WITHMATCHLEN' => bool # Whether each match should also include its length.
     
    -    'LEN'                  // Return the length of the longest subsequence
    +    'LEN'                  # Return the length of the longest subsequence
     
    -    'IDX'                  // Each returned match will include the indexes where the
    -                           // match occurs in each string.
    +    'IDX'                  # Each returned match will include the indexes where the
    +                           # match occurs in each string.
     ];

    NOTE: 'LEN' cannot be used with 'IDX'.

    @@ -8340,8 +8340,8 @@

    Parameters

    $options

    An array of options to modify how the command behaves.

    $options = [
    -    'COUNT'      => int  // An optional number of fields to return.
    -    'WITHVALUES' => bool // Also return the field values.
    +    'COUNT'      => int  # An optional number of fields to return.
    +    'WITHVALUES' => bool # Also return the field values.
     ];
    @@ -9346,18 +9346,18 @@

    Parameters

    $options

    Options to configure how the command operates

    $options = [
    -    // How many matches to return.  By default a single match is returned.
    -    // If count is set to zero, it means unlimited.
    +    # How many matches to return.  By default a single match is returned.
    +    # If count is set to zero, it means unlimited.
         'COUNT' => <num-matches>
     
    -    // Specify which match you want returned.  `RANK` 1 means "the first match"
    -    // 2 meaans the second, and so on.  If passed as a negative number the
    -    // RANK is computed right to left, so a `RANK` of -1 means "the last match".
    +    # Specify which match you want returned.  `RANK` 1 means "the first match"
    +    # 2 means the second, and so on.  If passed as a negative number the
    +    # RANK is computed right to left, so a `RANK` of -1 means "the last match".
         'RANK'  => <rank>
     
    -    // This argument allows you to limit how many elements Redis will search before
    -    // returning.  This is useful to prevent Redis searching very long lists while
    -    // blocking the client.
    +    # This argument allows you to limit how many elements Redis will search before
    +    # returning.  This is useful to prevent Redis searching very long lists while
    +    # blocking the client.
         'MAXLEN => <max-len>
     ];
    @@ -11767,16 +11767,16 @@

    Parameters

    $options

    An array of additional options that modifies how the command operates.

    $options = [
    -    'ABSTTL'          // If this is present, the `$ttl` provided by the user should
    -                      // be an absolute timestamp, in milliseconds()
    +    'ABSTTL'          # If this is present, the `$ttl` provided by the user should
    +                      # be an absolute timestamp, in milliseconds()
     
    -    'REPLACE'         // This flag instructs Redis to store the key even if a key with
    -                      // that name already exists.
    +    'REPLACE'         # This flag instructs Redis to store the key even if a key with
    +                      # that name already exists.
     
    -    'IDLETIME' => int // Tells Redis to set the keys internal 'idletime' value to a
    -                      // specific number (see the Redis command OBJECT for more info).
    -    'FREQ'     => int // Tells Redis to set the keys internal 'FREQ' value to a specific
    -                      // number (this relates to Redis' LFU eviction algorithm).
    +    'IDLETIME' => int # Tells Redis to set the keys internal 'idletime' value to a
    +                      # specific number (see the Redis command OBJECT for more info).
    +    'FREQ'     => int # Tells Redis to set the keys internal 'FREQ' value to a specific
    +                      # number (this relates to Redis' LFU eviction algorithm).
     ];
    @@ -15181,7 +15181,7 @@

    Examples

    - + Redis|int|false type(string $key) @@ -15218,23 +15218,7 @@

    Return Value

    Redis::REDIS_LIST Redis::REDIS_ZSET Redis::REDIS_HASH -Redis::REDIS_STREAM
    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -// NOTE:  Never use 'KEYS' in production!
    -$keys = $redis->keys('*');
    -
    -$redis->pipeline();
    -foreach ($keys as $key) {
    -    $redis->type($key);
    -}
    -
    -$ktypes = array_combine($keys, $redis->exec());
    -
    -// Print each key with its corresponding type
    -print_r($ktypes);
    -?>
    +Redis::REDIS_STREAM
    @@ -15252,13 +15236,23 @@

    See also

    +

    Examples

    + + + + + +
    foreach ($redis->keys('*') as $key) {
    + echo "$key => " . $redis->type($key) . "\n";
    +}
    +

    Examples

    - + Redis|array|bool unsubscribe(array $channels) @@ -15362,7 +15356,7 @@

    Parameters

    array $channels - +

    One or more channels to unsubscribe from.

    @@ -15372,7 +15366,7 @@

    Return Value

    - +
    Redis|array|bool

    The array of unsubscribed channels.

    @@ -15392,33 +15386,35 @@

    See also

    Redis::subscribe - - 'localhost']); + + + -$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { - if ($message == 'quit') { - echo "$channel => 'quit' detected, unsubscribing!\n"; - $redis->unsubscribe([$channel]); - } else { - echo "$channel => $message\n"; - } -}); + +

    Examples

    -echo "We've unsubscribed from both channels, exiting\n"; -?> -
    + + +
    $redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
    + if ($message == 'quit') {
    + echo "$channel => 'quit' detected, unsubscribing!\n";
    + $redis->unsubscribe([$channel]);
    + } else {
    + echo "$channel => $message\n";
    + }
    +});
    +
    +echo "We've unsubscribed from both channels, exiting\n";
    -

    - + Redis|bool unwatch() @@ -15474,7 +15470,7 @@

    See also

    - + Redis|bool watch(array|string $key, string ...$other_keys) @@ -15509,32 +15505,7 @@

    Return Value

    - +
    Redis|bool
    <?php
    -
    -$redis1 = new Redis(['host' => 'localhost']);
    -$redis2 = new Redis(['host' => 'localhost']);
    -
    -// Start watching 'incr-key'
    -$redis1->watch('incr-key');
    -
    -// Retrieve its value.
    -$val = $redis1->get('incr-key');
    -
    -// A second client modifies 'incr-key' after we read it.
    -$redis2->set('incr-key', 0);
    -
    -// Because another client changed the value of 'incr-key' after we read it, this
    -// is no longer a proper increment operation, but because we are `WATCH`ing the
    -// key, this transaction will fail and we can try again.
    -//
    -// If were to comment out the above `$redis2->set('incr-key', 0)` line the
    -// transaction would succeed.
    -$redis1->multi();
    -$redis1->set('incr-key', $val + 1);
    -$res = $redis1->exec();
    -
    -// bool(false)
    -var_dump($res);
    @@ -15558,13 +15529,44 @@

    See also

    +

    Examples

    + + + + + +
    $redis1 = new Redis(['host' => 'localhost']);
    +$redis2 = new Redis(['host' => 'localhost']);
    +
    +// Start watching 'incr-key'
    +$redis1->watch('incr-key');
    +
    +// Retrieve its value.
    +$val = $redis1->get('incr-key');
    +
    +// A second client modifies 'incr-key' after we read it.
    +$redis2->set('incr-key', 0);
    +
    +// Because another client changed the value of 'incr-key' after we read it, this
    +// is no longer a proper increment operation, but because we are `WATCH`ing the
    +// key, this transaction will fail and we can try again.
    +//
    +// If were to comment out the above `$redis2->set('incr-key', 0)` line the
    +// transaction would succeed.
    +$redis1->multi();
    +$redis1->set('incr-key', $val + 1);
    +$res = $redis1->exec();
    +
    +// bool(false)
    +var_dump($res);
    +

    - + int|false wait(int $numreplicas, int $timeout) @@ -15623,7 +15625,7 @@

    See also

    - + int|false xack(string $key, string $group, array $ids) @@ -15643,17 +15645,17 @@

    Parameters

    string $key - +

    The stream to query.

    string $group - +

    The consumer group to use.

    array $ids - +

    An array of stream entry IDs.

    @@ -15663,7 +15665,7 @@

    Return Value

    - +
    int|false

    The number of acknowledged messages

    @@ -15689,48 +15691,48 @@

    See also

    Redis::xack - - 'localhost']); - -$redis->del('ships'); - -$redis->xAdd('ships', '*', ['name' => 'Enterprise']); -$redis->xAdd('ships', '*', ['name' => 'Defiant']); - -$redis->xGroup('CREATE', 'ships', 'Federation', '0-0'); - -// Consume a single message with the consumer group 'Federation' -$ship = $redis->xReadGroup('Federation', 'Picard', ['ships' => '>'], 1); - -/* Retrieve the ID of the message we read. -assert(isset($ship['ships'])); -$id = key($ship['ships']); - -// The message we just read is now pending. -$res = $redis->xPending('ships', 'Federation')); -var_dump($res); + + + -// We can tell Redis we were able to process the message by using XACK -$res = $redis->xAck('ships', 'Federation', [$id]); -assert($res === 1); + +

    Examples

    -// The message should no longer be pending. -$res = $redis->xPending('ships', 'Federation'); -var_dump($res); -?> -
    + + +
    $redis->xAdd('ships', '*', ['name' => 'Enterprise']);
    +$redis->xAdd('ships', '*', ['name' => 'Defiant']);
    +
    +$redis->xGroup('CREATE', 'ships', 'Federation', '0-0');
    +
    +// Consume a single message with the consumer group 'Federation'
    +$ship = $redis->xReadGroup('Federation', 'Picard', ['ships' => '>'], 1);
    +
    +/* Retrieve the ID of the message we read.
    +assert(isset($ship['ships']));
    +$id = key($ship['ships']);
    +
    +// The message we just read is now pending.
    +$res = $redis->xPending('ships', 'Federation'));
    +var_dump($res);
    +
    +// We can tell Redis we were able to process the message by using XACK
    +$res = $redis->xAck('ships', 'Federation', [$id]);
    +assert($res === 1);
    +
    +// The message should no longer be pending.
    +$res = $redis->xPending('ships', 'Federation');
    +var_dump($res);
    -

    - + Redis|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) @@ -15826,7 +15828,7 @@

    Examples

    - + Redis|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) @@ -15885,68 +15887,7 @@

    Return Value

    - +
    Redis|bool|array

    An array of pending IDs or false if there are none, or on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('ships');
    -
    -$redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
    -
    -$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
    -
    -// Consume the ['name' => 'Defiant'] message
    -$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
    -
    -// The "Jem'Hadar" consumer has the message presently
    -$pending = $redis->xPending('ships', 'combatants');
    -
    -//array(4) {
    -//  [0]=>
    -//  int(1)
    -//  [1]=>
    -//  string(10) "1424-74205"
    -//  [2]=>
    -//  string(10) "1424-74205"
    -//  [3]=>
    -//  array(1) {
    -//    [0]=>
    -//    array(2) {
    -//      [0]=>
    -//      string(9) "Jem'Hadar"
    -//      [1]=>
    -//      string(1) "1"
    -//    }
    -//  }
    -//}
    -var_dump($pending);
    -
    -// Asssume control of the pending message with a different consumer.
    -$res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');
    -
    -// Now the 'Sisko' consumer owns the message
    -$pending = $redis->xPending('ships', 'combatants');
    -
    -// array(4) {
    -//   [0]=>
    -//   int(1)
    -//   [1]=>
    -//   string(10) "1424-74205"
    -//   [2]=>
    -//   string(10) "1424-74205"
    -//   [3]=>
    -//   array(1) {
    -//     [0]=>
    -//     array(2) {
    -//       [0]=>
    -//       string(5) "Sisko"
    -//       [1]=>
    -//       string(1) "1"
    -//     }
    -//   }
    -// }
    -var_dump($pending);
    -?>

    An array of pending IDs or false if there are none, or on failure.

    @@ -15976,13 +15917,37 @@

    See also

    +

    Examples

    + + + + + +
    $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
    +
    +$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
    +
    +// Consume the ['name' => 'Defiant'] message
    +$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
    +
    +// The "Jem'Hadar" consumer has the message presently
    +$pending = $redis->xPending('ships', 'combatants');
    +var_dump($pending);
    +
    +// Asssume control of the pending message with a different consumer.
    +$res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');
    +
    +// Now the 'Sisko' consumer owns the message
    +$pending = $redis->xPending('ships', 'combatants');
    +var_dump($pending);
    +

    - + Redis|array|bool xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) @@ -16028,17 +15993,20 @@

    Parameters

    array $options

    An options array that modifies how the command operates.

    -
    // Following is an options array describing every option you can pass.  Note that
    -// 'IDLE', and 'TIME' are mutually exclusive.
    +
    # Following is an options array describing every option you can pass.  Note that
    +# 'IDLE', and 'TIME' are mutually exclusive.
     $options = [
    -    'IDLE'       => 3            // Set the idle time of the message to a 3.  By default the
    -                                 // idle time is set to zero.
    -    'TIME'       => 1000*time()  // Same as IDLE except it takes a unix timestamp in milliseconds.
    -    'RETRYCOUNT' => 0            // Set the retry counter to zero.  By default XCLAIM doesn't modify
    -                                 // the counter.
    -    'FORCE'                      // Creates the pending message entry even if IDs are not already
    -                                 // in the PEL with another client.
    -    'JUSTID'                     // Return only an array of IDs rather than the messages themselves.
    +    'IDLE'       => 3            # Set the idle time of the message to a 3.  By default
    +                                 # the idle time is set to zero.
    +    'TIME'       => 1000*time()  # Same as IDLE except it takes a unix timestamp in
    +                                 # milliseconds.
    +    'RETRYCOUNT' => 0            # Set the retry counter to zero.  By default XCLAIM
    +                                 # doesn't modify the counter.
    +    'FORCE'                      # Creates the pending message entry even if IDs are
    +                                 # not already
    +                                 # in the PEL with another client.
    +    'JUSTID'                     # Return only an array of IDs rather than the messages
    +                                 # themselves.
     ];
    @@ -16049,76 +16017,7 @@

    Return Value

    - +
    Redis|array|bool

    An array of claimed messags or false on failure.

    -
    <?php
    -$redis = new Redis(['host' => 'localhost']);
    -
    -$redis->del('ships');
    -
    -$redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
    -
    -$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
    -
    -// Consume the ['name' => 'Defiant'] message
    -$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
    -
    -// The "Jem'Hadar" consumer has the message presently
    -$pending = $redis->xPending('ships', 'combatants');
    -
    -//array(4) {
    -//  [0]=>
    -//  int(1)
    -//  [1]=>
    -//  string(10) "1424-74205"
    -//  [2]=>
    -//  string(10) "1424-74205"
    -//  [3]=>
    -//  array(1) {
    -//    [0]=>
    -//    array(2) {
    -//      [0]=>
    -//      string(9) "Jem'Hadar"
    -//      [1]=>
    -//      string(1) "1"
    -//    }
    -//  }
    -//}
    -var_dump($pending);
    -
    -assert($pending && isset($pending[1]));
    -
    -// Claim the message by ID.
    -$claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']);
    -
    -// array(1) {
    -//   [0]=>
    -//   string(10) "1424-74205"
    -// }
    -var_dump($claimed);
    -
    -// Now the 'Sisko' consumer owns the message
    -$pending = $redis->xPending('ships', 'combatants');
    -
    -// array(4) {
    -//   [0]=>
    -//   int(1)
    -//   [1]=>
    -//   string(10) "1424-74205"
    -//   [2]=>
    -//   string(10) "1424-74205"
    -//   [3]=>
    -//   array(1) {
    -//     [0]=>
    -//     array(2) {
    -//       [0]=>
    -//       string(5) "Sisko"
    -//       [1]=>
    -//       string(1) "1"
    -//     }
    -//   }
    -// }
    -var_dump($pending);
    -?>

    An array of claimed messags or false on failure.

    @@ -16142,13 +16041,40 @@

    See also

    +

    Examples

    + + + + + +
    $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
    +
    +$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
    +
    +// Consume the ['name' => 'Defiant'] message
    +$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
    +
    +// The "Jem'Hadar" consumer has the message presently
    +$pending = $redis->xPending('ships', 'combatants');
    +var_dump($pending);
    +
    +assert($pending && isset($pending[1]));
    +
    +// Claim the message by ID.
    +$claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']);
    +var_dump($claimed);
    +
    +// Now the 'Sisko' consumer owns the message
    +$pending = $redis->xPending('ships', 'combatants');
    +var_dump($pending);
    +

    - + Redis|int|false xdel(string $key, array $ids) @@ -16203,7 +16129,7 @@

    Examples

    - + mixed xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) @@ -16298,7 +16224,7 @@

    See also

    - + mixed xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) @@ -16380,7 +16306,7 @@

    Examples

    - + Redis|int|false xlen(string $key) @@ -16441,7 +16367,7 @@

    Examples

    - + Redis|array|false xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) @@ -16526,7 +16452,7 @@

    See also

    - + Redis|array|bool xrange(string $key, string $start, string $end, int $count = -1) @@ -16605,7 +16531,7 @@

    Examples

    - + Redis|array|bool xread(array $streams, int $count = -1, int $block = -1) @@ -16684,7 +16610,7 @@

    Examples

    - + Redis|array|bool xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) @@ -16782,7 +16708,7 @@

    Examples

    - + Redis|array|bool xrevrange(string $key, string $end, string $start, int $count = -1) @@ -16867,7 +16793,7 @@

    Examples

    - + Redis|int|false xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) @@ -16955,7 +16881,7 @@

    Examples

    - + Redis|int|false zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) @@ -16979,8 +16905,29 @@

    Parameters

    array|float $score_or_options -

    Either the score for the first element, or an array -containing one or more options for the operation.

    +

    Either the score for the first element, or an array of options.

    +
    
    + $options = [
    +     'NX',       # Only update elements that already exist
    +     'NX',       # Only add new elements but don't update existing ones.
    +
    +     'LT'        # Only update existing elements if the new score is
    +                 # less than the existing one.
    +     'GT'        # Only update existing elements if the new score is
    +                 # greater than the existing one.
    +
    +     'CH'        # Instead of returning the number of elements added,
    +                 # Redis will return the number Of elements that were
    +                 # changed in the operation.
    +
    +     'INCR'      # Instead of setting each element to the provide score,
    +                 # increment the element by the
    +                 # provided score, much like ZINCRBY.  When this option
    +                 # is passed, you may only send a single score and member.
    + ];
    +
    + Note:  'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis
    +        will send whichever one is last in the options array.
    mixed @@ -16996,25 +16943,7 @@

    Return Value

    Redis|int|false

    The return value varies depending on the options passed.

    -

    Following is information about the options that may be passed as the scond argument:

    -
    
    -$options = [
    -    'NX',       # Only update elements that already exist
    -    'NX',       # Only add new elements but don't update existing ones.
    -
    -    'LT'        # Only update existing elements if the new score is less than the existing one.
    -    'GT'        # Only update existing elements if the new score is greater than the existing one.
    -
    -    'CH'        # Instead of returning the number of elements added, Redis will return the number
    -                # Of elements that were changed in the operation.
    -
    -    'INCR'      # Instead of setting each element to the provide score, increment the elemnt by the
    -                # provided score, much like ZINCRBY.  When this option is passed, you may only
    -                # send a single score and member.
    -];
    -
    -Note:  'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis will send whichever one is last in
    -       the options array.
    +

    Following is information about the options that may be passed as the second argument:

    @@ -17049,7 +16978,7 @@

    Examples

    - + Redis|int|false zCard(string $key) @@ -17110,7 +17039,7 @@

    Examples

    - + Redis|int|false zCount(string $key, string $start, string $end) @@ -17187,7 +17116,7 @@

    Examples

    - + Redis|float|false zIncrBy(string $key, float $value, mixed $member) @@ -17261,7 +17190,7 @@

    Examples

    - + Redis|int|false zLexCount(string $key, string $min, string $max) @@ -17334,7 +17263,7 @@

    Examples

    - + Redis|array|false zMscore(string $key, mixed $member, mixed ...$other_members) @@ -17408,7 +17337,7 @@

    Examples

    - + Redis|array|false zPopMax(string $key, int $count = null) @@ -17477,7 +17406,7 @@

    Examples

    - + Redis|array|false zPopMin(string $key, int $count = null) @@ -17546,7 +17475,7 @@

    Examples

    - + Redis|array|false zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) @@ -17585,11 +17514,11 @@

    Parameters

    the command, or for historical purposes a boolean which controls just the 'WITHSCORES' option.

    $options = [
    -    'WITHSCORES' => true,     // Return both scores and members.
    -    'LIMIT'      => [10, 10], // Start at offset 10 and return 10 elements.
    -    'REV'                     // Return the elements in reverse order
    -    'BYSCORE',                // Treat `start` and `end` as scores instead
    -    'BYLEX'                   // Treat `start` and `end` as lexicographical values.
    +    'WITHSCORES' => true,     # Return both scores and members.
    +    'LIMIT'      => [10, 10], # Start at offset 10 and return 10 elements.
    +    'REV'                     # Return the elements in reverse order
    +    'BYSCORE',                # Treat `start` and `end` as scores instead
    +    'BYLEX'                   # Treat `start` and `end` as lexicographical values.
     ];

    Note: 'BYLEX' and 'BYSCORE' are mutually exclusive.

    @@ -17636,7 +17565,7 @@

    Examples

    - + Redis|array|false zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) @@ -17721,7 +17650,7 @@

    Examples

    - + Redis|array|false zRangeByScore(string $key, string $start, string $end, array $options = []) @@ -17803,7 +17732,7 @@

    Examples

    - + Redis|int|false zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) @@ -17883,7 +17812,7 @@

    See also

    - + Redis|string|array zRandMember(string $key, array $options = null) @@ -17952,7 +17881,7 @@

    Examples

    - + Redis|int|false zRank(string $key, mixed $member) @@ -18021,7 +17950,7 @@

    Examples

    - + Redis|int|false zRem(mixed $key, mixed $member, mixed ...$other_members) @@ -18092,7 +18021,7 @@

    Examples

    - + Redis|int|false zRemRangeByLex(string $key, string $min, string $max) @@ -18172,7 +18101,7 @@

    Examples

    - + Redis|int|false zRemRangeByRank(string $key, int $start, int $end) @@ -18243,7 +18172,7 @@

    Examples

    - + Redis|int|false zRemRangeByScore(string $key, string $start, string $end) @@ -18315,7 +18244,7 @@

    Examples

    - + Redis|array|false zRevRange(string $key, int $start, int $end, mixed $scores = null) @@ -18401,7 +18330,7 @@

    Examples

    - + Redis|array|false zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) @@ -18491,7 +18420,7 @@

    Examples

    - + Redis|array|false zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) @@ -18581,7 +18510,7 @@

    Examples

    - + Redis|int|false zRevRank(string $key, mixed $member) @@ -18651,7 +18580,7 @@

    Examples

    - + Redis|float|false zScore(string $key, mixed $member) @@ -18718,7 +18647,7 @@

    Examples

    - + Redis|array|false zdiff(array $keys, array $options = null) @@ -18790,7 +18719,7 @@

    Examples

    - + Redis|int|false zdiffstore(string $dst, array $keys) @@ -18856,7 +18785,7 @@

    See also

    - + Redis|array|false zinter(array $keys, array|null $weights = null, array|null $options = null) @@ -18934,7 +18863,7 @@

    Examples

    - + Redis|int|false zintercard(array $keys, int $limit = -1) @@ -19019,7 +18948,7 @@

    Examples

    - + Redis|int|false zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) @@ -19109,7 +19038,7 @@

    Examples

    - + Redis|array|false zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) @@ -19195,7 +19124,7 @@

    See also

    - + Redis|array|false zunion(array $keys, array|null $weights = null, array|null $options = null) @@ -19228,12 +19157,12 @@

    Parameters

    $options

    An array that modifies how this command functions.

    $options = [
    -    // By default when members exist in more than one set Redis will SUM
    -    // total score for each match.  Instead, it can return the AVG, MIN,
    -    // or MAX value based on this option.
    +    # By default when members exist in more than one set Redis will SUM
    +    # total score for each match.  Instead, it can return the AVG, MIN,
    +    # or MAX value based on this option.
         'AGGREGATE' => 'sum' | 'min' | 'max'
     
    -    // Whether Redis should also return each members aggregated score.
    +    # Whether Redis should also return each members aggregated score.
         'WITHSCORES' => true | false
     ]
    @@ -19273,7 +19202,7 @@

    Examples

    - + Redis|int|false zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) diff --git a/docs/renderer.index b/docs/renderer.index index 752ee083da..616851d61a 100644 --- a/docs/renderer.index +++ b/docs/renderer.index @@ -1 +1 @@ -O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"7230a9518fe0e79ae51f6b49d269053535a34199";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"2f2132e45b1d60011f8ef9298cb35b7ba2b247d5";s:21:"RedisClusterException";s:40:"2f2132e45b1d60011f8ef9298cb35b7ba2b247d5";s:14:"RedisException";s:40:"7230a9518fe0e79ae51f6b49d269053535a34199";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:4:"main";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file +O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"39efb36886d0e29b476aa5ccb2e551c2a37fc7cb";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"2f2132e45b1d60011f8ef9298cb35b7ba2b247d5";s:21:"RedisClusterException";s:40:"2f2132e45b1d60011f8ef9298cb35b7ba2b247d5";s:14:"RedisException";s:40:"39efb36886d0e29b476aa5ccb2e551c2a37fc7cb";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:4:"main";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file diff --git a/redis.stub.php b/redis.stub.php index 5af10c2987..61b7a10a29 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -435,8 +435,8 @@ public function connect(string $host, int $port = 6379, float $timeout = 0, stri * @param array $options An array with modifiers on how COPY should operate. * * $options = [ - * 'REPLACE' => true|false // Whether to replace an existing key. - * 'DB' => int // Copy key to specific db. + * 'REPLACE' => true|false # Whether to replace an existing key. + * 'DB' => int # Copy key to specific db. * ]; * * @@ -856,19 +856,19 @@ public function geopos(string $key, string $member, string ...$other_members): R * @param array $options An array of options that modifies how the command behaves. * * $options = [ - * 'WITHCOORD', // Return members and their coordinates. - * 'WITHDIST', // Return members and their distances from the center. - * 'WITHHASH', // Return members GeoHash string. - * 'ASC' | 'DESC', // The sort order of returned members + * 'WITHCOORD', # Return members and their coordinates. + * 'WITHDIST', # Return members and their distances from the center. + * 'WITHHASH', # Return members GeoHash string. + * 'ASC' | 'DESC', # The sort order of returned members * - * // Limit to N returned members. Optionally a two element array may be - * // passed as the `LIMIT` argument, and the `ANY` argument. + * # Limit to N returned members. Optionally a two element array may be + * # passed as the `LIMIT` argument, and the `ANY` argument. * 'COUNT' => [], or [, ] * - * // Instead of returning members, store them in the specified key. + * # Instead of returning members, store them in the specified key. * 'STORE' => * - * // Store the distances in the specified key + * # Store the distances in the specified key * 'STOREDIST' => * ]; * @@ -940,11 +940,11 @@ public function geosearch(string $key, array|string $position, array|int|float $ * @param array $options * * $options = [ - * 'ASC' | 'DESC', // The sort order of returned members - * 'WITHDIST' // Also store distances. + * 'ASC' | 'DESC', # The sort order of returned members + * 'WITHDIST' # Also store distances. * - * // Limit to N returned members. Optionally a two element array may be - * // passed as the `LIMIT` argument, and the `ANY` argument. + * # Limit to N returned members. Optionally a two element array may be + * # passed as the `LIMIT` argument, and the `ANY` argument. * 'COUNT' => [], or [, ] * ]; * @@ -991,11 +991,11 @@ public function getBit(string $key, int $idx): Redis|int|false; * @param array $options Options to modify how the command works. * * $options = [ - * 'EX' => // Expire in N seconds - * 'PX' => // Expire in N milliseconds - * 'EXAT' => // Expire at a unix timestamp (in seconds) - * 'PXAT' => // Expire at a unix timestamp (in milliseconds); - * 'PERSIST' // Remove any configured expiration on the key. + * 'EX' => # Expire in N seconds + * 'PX' => # Expire in N milliseconds + * 'EXAT' => # Expire at a unix timestamp (in seconds) + * 'PXAT' => # Expire at a unix timestamp (in milliseconds); + * 'PERSIST' # Remove any configured expiration on the key. * ]; * * @@ -1102,14 +1102,14 @@ public function getRange(string $key, int $start, int $end): Redis|string|false; * * * $options = [ - * 'MINMATCHLEN' => int // Exclude matching substrings that are less than this value + * 'MINMATCHLEN' => int # Exclude matching substrings that are less than this value * - * 'WITHMATCHLEN' => bool // Whether each match should also include its length. + * 'WITHMATCHLEN' => bool # Whether each match should also include its length. * - * 'LEN' // Return the length of the longest subsequence + * 'LEN' # Return the length of the longest subsequence * - * 'IDX' // Each returned match will include the indexes where the - * // match occurs in each string. + * 'IDX' # Each returned match will include the indexes where the + * # match occurs in each string. * ]; * * @@ -1296,8 +1296,8 @@ public function hMset(string $key, array $fieldvals): Redis|bool; * * * $options = [ - * 'COUNT' => int // An optional number of fields to return. - * 'WITHVALUES' => bool // Also return the field values. + * 'COUNT' => int # An optional number of fields to return. + * 'WITHVALUES' => bool # Also return the field values. * ]; * * @@ -1527,18 +1527,18 @@ public function lPop(string $key, int $count = 0): Redis|bool|string|array; * @param array $options Options to configure how the command operates * * $options = [ - * // How many matches to return. By default a single match is returned. - * // If count is set to zero, it means unlimited. + * # How many matches to return. By default a single match is returned. + * # If count is set to zero, it means unlimited. * 'COUNT' => * - * // Specify which match you want returned. `RANK` 1 means "the first match" - * // 2 meaans the second, and so on. If passed as a negative number the - * // RANK is computed right to left, so a `RANK` of -1 means "the last match". + * # Specify which match you want returned. `RANK` 1 means "the first match" + * # 2 means the second, and so on. If passed as a negative number the + * # RANK is computed right to left, so a `RANK` of -1 means "the last match". * 'RANK' => * - * // This argument allows you to limit how many elements Redis will search before - * // returning. This is useful to prevent Redis searching very long lists while - * // blocking the client. + * # This argument allows you to limit how many elements Redis will search before + * # returning. This is useful to prevent Redis searching very long lists while + * # blocking the client. * 'MAXLEN => * ]; * @@ -2021,16 +2021,16 @@ public function reset(): Redis|bool; * * * $options = [ - * 'ABSTTL' // If this is present, the `$ttl` provided by the user should - * // be an absolute timestamp, in milliseconds() + * 'ABSTTL' # If this is present, the `$ttl` provided by the user should + * # be an absolute timestamp, in milliseconds() * - * 'REPLACE' // This flag instructs Redis to store the key even if a key with - * // that name already exists. + * 'REPLACE' # This flag instructs Redis to store the key even if a key with + * # that name already exists. * - * 'IDLETIME' => int // Tells Redis to set the keys internal 'idletime' value to a - * // specific number (see the Redis command OBJECT for more info). - * 'FREQ' => int // Tells Redis to set the keys internal 'FREQ' value to a specific - * // number (this relates to Redis' LFU eviction algorithm). + * 'IDLETIME' => int # Tells Redis to set the keys internal 'idletime' value to a + * # specific number (see the Redis command OBJECT for more info). + * 'FREQ' => int # Tells Redis to set the keys internal 'FREQ' value to a specific + * # number (this relates to Redis' LFU eviction algorithm). * ]; * * @@ -2920,24 +2920,10 @@ public function ttl(string $key): Redis|int|false; * Redis::REDIS_HASH * Redis::REDIS_STREAM * - * - * 'localhost']); - * - * // NOTE: Never use 'KEYS' in production! - * $keys = $redis->keys('*'); - * - * $redis->pipeline(); - * foreach ($keys as $key) { - * $redis->type($key); + * @example + * foreach ($redis->keys('*') as $key) { + * echo "$key => " . $redis->type($key) . "\n"; * } - * - * $ktypes = array_combine($keys, $redis->exec()); - * - * // Print each key with its corresponding type - * print_r($ktypes); - * ?> - * */ public function type(string $key): Redis|int|false; @@ -2965,13 +2951,13 @@ public function unlink(array|string $key, string ...$other_keys): Redis|int|fals /** * Unsubscribe from one or more subscribed channels. * + * @param array $channels One or more channels to unsubscribe from. + * @return Redis|array|bool The array of unsubscribed channels. + * * @see https://redis.io/commands/unsubscribe * @see Redis::subscribe() * - * - * 'localhost']); - * + * @example * $redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { * if ($message == 'quit') { * echo "$channel => 'quit' detected, unsubscribing!\n"; @@ -2982,8 +2968,6 @@ public function unlink(array|string $key, string ...$other_keys): Redis|int|fals * }); * * echo "We've unsubscribed from both channels, exiting\n"; - * ?> - * */ public function unsubscribe(array $channels): Redis|array|bool; @@ -3001,18 +2985,16 @@ public function unwatch(): Redis|bool; /** * Watch one or more keys for conditional execution of a transaction. * - * @see https://redis.io/commands/watch - * @see https://redis.io/commands/unwatch - * * @param array|string $key_or_keys Either an array with one or more key names, or a string key name * @param string $other_keys If the first argument was passed as a string, any number of additional * string key names may be passed variadically. - * * @return Redis|bool * - * - * 'localhost']); * $redis2 = new Redis(['host' => 'localhost']); * @@ -3037,8 +3019,6 @@ public function unwatch(): Redis|bool; * * // bool(false) * var_dump($res); - * - * */ public function watch(array|string $key, string ...$other_keys): Redis|bool; @@ -3060,16 +3040,17 @@ public function wait(int $numreplicas, int $timeout): int|false; * Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but * not yet acknowledged by XACK.) * + * @param string $key The stream to query. + * @param string $group The consumer group to use. + * @param array $ids An array of stream entry IDs. + * + * @return int|false The number of acknowledged messages + * * @see https://redis.io/commands/xack * @see https://redis.io/commands/xreadgroup * @see Redis::xack() * - * - * 'localhost']); - * - * $redis->del('ships'); - * + * @example * $redis->xAdd('ships', '*', ['name' => 'Enterprise']); * $redis->xAdd('ships', '*', ['name' => 'Defiant']); * @@ -3093,8 +3074,6 @@ public function wait(int $numreplicas, int $timeout): int|false; * // The message should no longer be pending. * $res = $redis->xPending('ships', 'Federation'); * var_dump($res); - * ?> - * */ public function xack(string $key, string $group, array $ids): int|false; @@ -3139,12 +3118,7 @@ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bo * * @return Redis|array|bool An array of pending IDs or false if there are none, or on failure. * - * - * 'localhost']); - * - * $redis->del('ships'); - * + * @example * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); * * $redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); @@ -3154,25 +3128,6 @@ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bo * * // The "Jem'Hadar" consumer has the message presently * $pending = $redis->xPending('ships', 'combatants'); - * - * //array(4) { - * // [0]=> - * // int(1) - * // [1]=> - * // string(10) "1424-74205" - * // [2]=> - * // string(10) "1424-74205" - * // [3]=> - * // array(1) { - * // [0]=> - * // array(2) { - * // [0]=> - * // string(9) "Jem'Hadar" - * // [1]=> - * // string(1) "1" - * // } - * // } - * //} * var_dump($pending); * * // Asssume control of the pending message with a different consumer. @@ -3180,28 +3135,7 @@ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bo * * // Now the 'Sisko' consumer owns the message * $pending = $redis->xPending('ships', 'combatants'); - * - * // array(4) { - * // [0]=> - * // int(1) - * // [1]=> - * // string(10) "1424-74205" - * // [2]=> - * // string(10) "1424-74205" - * // [3]=> - * // array(1) { - * // [0]=> - * // array(2) { - * // [0]=> - * // string(5) "Sisko" - * // [1]=> - * // string(1) "1" - * // } - * // } - * // } * var_dump($pending); - * ?> - * */ public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array; @@ -3219,28 +3153,26 @@ public function xautoclaim(string $key, string $group, string $consumer, int $mi * @param array $options An options array that modifies how the command operates. * * - * // Following is an options array describing every option you can pass. Note that - * // 'IDLE', and 'TIME' are mutually exclusive. + * # Following is an options array describing every option you can pass. Note that + * # 'IDLE', and 'TIME' are mutually exclusive. * $options = [ - * 'IDLE' => 3 // Set the idle time of the message to a 3. By default the - * // idle time is set to zero. - * 'TIME' => 1000*time() // Same as IDLE except it takes a unix timestamp in milliseconds. - * 'RETRYCOUNT' => 0 // Set the retry counter to zero. By default XCLAIM doesn't modify - * // the counter. - * 'FORCE' // Creates the pending message entry even if IDs are not already - * // in the PEL with another client. - * 'JUSTID' // Return only an array of IDs rather than the messages themselves. + * 'IDLE' => 3 # Set the idle time of the message to a 3. By default + * # the idle time is set to zero. + * 'TIME' => 1000*time() # Same as IDLE except it takes a unix timestamp in + * # milliseconds. + * 'RETRYCOUNT' => 0 # Set the retry counter to zero. By default XCLAIM + * # doesn't modify the counter. + * 'FORCE' # Creates the pending message entry even if IDs are + * # not already + * # in the PEL with another client. + * 'JUSTID' # Return only an array of IDs rather than the messages + * # themselves. * ]; * * * @return Redis|array|bool An array of claimed messags or false on failure. * - * - * 'localhost']); - * - * $redis->del('ships'); - * + * @example * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); * * $redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); @@ -3250,62 +3182,17 @@ public function xautoclaim(string $key, string $group, string $consumer, int $mi * * // The "Jem'Hadar" consumer has the message presently * $pending = $redis->xPending('ships', 'combatants'); - * - * //array(4) { - * // [0]=> - * // int(1) - * // [1]=> - * // string(10) "1424-74205" - * // [2]=> - * // string(10) "1424-74205" - * // [3]=> - * // array(1) { - * // [0]=> - * // array(2) { - * // [0]=> - * // string(9) "Jem'Hadar" - * // [1]=> - * // string(1) "1" - * // } - * // } - * //} * var_dump($pending); * * assert($pending && isset($pending[1])); * * // Claim the message by ID. * $claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']); - * - * // array(1) { - * // [0]=> - * // string(10) "1424-74205" - * // } * var_dump($claimed); * * // Now the 'Sisko' consumer owns the message * $pending = $redis->xPending('ships', 'combatants'); - * - * // array(4) { - * // [0]=> - * // int(1) - * // [1]=> - * // string(10) "1424-74205" - * // [2]=> - * // string(10) "1424-74205" - * // [3]=> - * // array(1) { - * // [0]=> - * // array(2) { - * // [0]=> - * // string(5) "Sisko" - * // [1]=> - * // string(1) "1" - * // } - * // } - * // } * var_dump($pending); - * ?> - * */ public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): Redis|array|bool; @@ -3531,32 +3418,35 @@ public function xtrim(string $key, string $threshold, bool $approx = false, bool * Add one or more elements and scores to a Redis sorted set. * * @param string $key The sorted set in question. - * @param array|float $score_or_options Either the score for the first element, or an array - * containing one or more options for the operation. + * @param array|float $score_or_options Either the score for the first element, or an array of options. + * + * $options = [ + * 'NX', # Only update elements that already exist + * 'NX', # Only add new elements but don't update existing ones. + * + * 'LT' # Only update existing elements if the new score is + * # less than the existing one. + * 'GT' # Only update existing elements if the new score is + * # greater than the existing one. + * + * 'CH' # Instead of returning the number of elements added, + * # Redis will return the number Of elements that were + * # changed in the operation. + * + * 'INCR' # Instead of setting each element to the provide score, + * # increment the element by the + * # provided score, much like ZINCRBY. When this option + * # is passed, you may only send a single score and member. + * ]; + * + * Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis + * will send whichever one is last in the options array. + * * @param mixed $more_scores_and_mems A variadic number of additional scores and members. * * @return Redis|int|false The return value varies depending on the options passed. * - * Following is information about the options that may be passed as the scond argument: - * - * - * $options = [ - * 'NX', # Only update elements that already exist - * 'NX', # Only add new elements but don't update existing ones. - * - * 'LT' # Only update existing elements if the new score is less than the existing one. - * 'GT' # Only update existing elements if the new score is greater than the existing one. - * - * 'CH' # Instead of returning the number of elements added, Redis will return the number - * # Of elements that were changed in the operation. - * - * 'INCR' # Instead of setting each element to the provide score, increment the elemnt by the - * # provided score, much like ZINCRBY. When this option is passed, you may only - * # send a single score and member. - * ]; - * - * Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis will send whichever one is last in - * the options array. + * Following is information about the options that may be passed as the second argument: * * @see https://redis.io/commands/zadd * @@ -3698,11 +3588,11 @@ public function zPopMin(string $key, int $count = null): Redis|array|false; * controls just the 'WITHSCORES' option. * * $options = [ - * 'WITHSCORES' => true, // Return both scores and members. - * 'LIMIT' => [10, 10], // Start at offset 10 and return 10 elements. - * 'REV' // Return the elements in reverse order - * 'BYSCORE', // Treat `start` and `end` as scores instead - * 'BYLEX' // Treat `start` and `end` as lexicographical values. + * 'WITHSCORES' => true, # Return both scores and members. + * 'LIMIT' => [10, 10], # Start at offset 10 and return 10 elements. + * 'REV' # Return the elements in reverse order + * 'BYSCORE', # Treat `start` and `end` as scores instead + * 'BYLEX' # Treat `start` and `end` as lexicographical values. * ]; * * @@ -4133,12 +4023,12 @@ public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int * * * $options = [ - * // By default when members exist in more than one set Redis will SUM - * // total score for each match. Instead, it can return the AVG, MIN, - * // or MAX value based on this option. + * # By default when members exist in more than one set Redis will SUM + * # total score for each match. Instead, it can return the AVG, MIN, + * # or MAX value based on this option. * 'AGGREGATE' => 'sum' | 'min' | 'max' * - * // Whether Redis should also return each members aggregated score. + * # Whether Redis should also return each members aggregated score. * 'WITHSCORES' => true | false * ] * From ff863f3f97504f6ce1f9ea5cac47ec9335d5a304 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 12 Nov 2022 13:06:24 +0200 Subject: [PATCH 1719/1986] Refactor `command` command Issue #2068 --- library.c | 42 ++++++++++++++++++++ library.h | 1 + redis.c | 2 +- redis.stub.php | 2 +- redis_arginfo.h | 6 +-- redis_commands.c | 89 ++++++++++++++++++------------------------ redis_legacy_arginfo.h | 6 +-- tests/RedisTest.php | 20 ++++++++++ 8 files changed, 109 insertions(+), 59 deletions(-) diff --git a/library.c b/library.c index 5130249a3d..130177fad7 100644 --- a/library.c +++ b/library.c @@ -1858,6 +1858,48 @@ redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval return redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); + return FAILURE; + } +} + +static int +redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + int numElems; + zval z_ret; + + if (read_mbulk_header(redis_sock, &numElems) < 0) { + if (IS_ATOMIC(redis_sock)) { + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); + } + return FAILURE; + } + + array_init(&z_ret); + redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret); + if (IS_ATOMIC(redis_sock)) { + RETVAL_ZVAL(&z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, &z_ret); + } + + return SUCCESS; +} + +PHP_REDIS_API int +redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + if (ctx == NULL) { + return redis_command_info_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + } else if (ctx == PHPREDIS_CTX_PTR) { + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + } else if (ctx == PHPREDIS_CTX_PTR + 1) { + return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + } else { + ZEND_ASSERT(!"memory corruption?"); + return FAILURE; } } diff --git a/library.h b/library.h index 4b88cc198c..121773a922 100644 --- a/library.h +++ b/library.h @@ -189,6 +189,7 @@ PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisS PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); /* Helper methods to get configuration values from a HashTable. */ diff --git a/redis.c b/redis.c index 2aef973d29..d90a06897f 100644 --- a/redis.c +++ b/redis.c @@ -3077,7 +3077,7 @@ PHP_METHOD(Redis, rawcommand) { * proto array Redis::command('info', string cmd) * proto array Redis::command('getkeys', array cmd_args) */ PHP_METHOD(Redis, command) { - REDIS_PROCESS_CMD(command, redis_read_variant_reply); + REDIS_PROCESS_CMD(command, redis_command_response); } /* }}} */ diff --git a/redis.stub.php b/redis.stub.php index 61b7a10a29..35df9ecdbd 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -401,7 +401,7 @@ public function client(string $opt, mixed ...$args): mixed; public function close(): bool; - public function command(string $opt = null, string|array $arg): mixed; + public function command(string $opt = null, mixed ...$args): mixed; /** * Execute the Redis CONFIG command in a variety of ways. diff --git a/redis_arginfo.h b/redis_arginfo.h index 5c83918a5a..f0d01cd27e 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7230a9518fe0e79ae51f6b49d269053535a34199 */ + * Stub hash: b18973c6cdb4ae3a706dfd1ad5645339f0fcdd84 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -120,9 +120,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_close arginfo_class_Redis_clearLastError -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 2, IS_MIXED, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 0, IS_MIXED, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null") - ZEND_ARG_TYPE_MASK(0, arg, MAY_BE_STRING|MAY_BE_ARRAY, NULL) + ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0) diff --git a/redis_commands.c b/redis_commands.c index 11c5d59f71..bb3fa04701 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -5245,64 +5245,51 @@ redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *kw=NULL; - zval *z_arg; - size_t kw_len; - - /* Parse our args */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sz", &kw, &kw_len, - &z_arg) == FAILURE) - { - return FAILURE; - } + smart_string cmdstr = {0}; + zend_string *op = NULL, *zstr; + zval *z_args = NULL; + int i, argc = 0; - /* Construct our command */ - if (!kw) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "COMMAND", ""); - } else if (!z_arg) { - /* Sanity check */ - if (strncasecmp(kw, "count", sizeof("count") - 1)) { - return FAILURE; - } - /* COMMAND COUNT */ - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "COMMAND", "s", "COUNT", sizeof("COUNT") - 1); - } else if (Z_TYPE_P(z_arg) == IS_STRING) { - /* Sanity check */ - if (strncasecmp(kw, "info", sizeof("info") - 1)) { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(0, -1) + Z_PARAM_OPTIONAL + Z_PARAM_STR(op) + Z_PARAM_VARIADIC('*', z_args, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - /* COMMAND INFO */ - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "COMMAND", "ss", "INFO", sizeof("INFO") - 1, - Z_STRVAL_P(z_arg), Z_STRLEN_P(z_arg)); + if (op == NULL) { + *ctx = NULL; + argc = 0; + } else if (zend_string_equals_literal_ci(op, "COUNT")) { + *ctx = PHPREDIS_CTX_PTR; + argc = 0; + } else if (zend_string_equals_literal_ci(op, "DOCS") || + zend_string_equals_literal_ci(op, "INFO") + ) { + *ctx = NULL; + } else if (zend_string_equals_literal_ci(op, "GETKEYS") || + zend_string_equals_literal_ci(op, "LIST") + ) { + *ctx = PHPREDIS_CTX_PTR + 1; + } else if (zend_string_equals_literal_ci(op, "GETKEYSANDFLAGS")) { + *ctx = PHPREDIS_CTX_PTR + 2; } else { - int arr_len; - - /* Sanity check on args */ - if (strncasecmp(kw, "getkeys", sizeof("getkeys")-1) || - Z_TYPE_P(z_arg)!=IS_ARRAY || - (arr_len=zend_hash_num_elements(Z_ARRVAL_P(z_arg)))<1) - { - return FAILURE; - } - - zval *z_ele; - HashTable *ht_arr = Z_ARRVAL_P(z_arg); - smart_string cmdstr = {0}; - - redis_cmd_init_sstr(&cmdstr, 1 + arr_len, ZEND_STRL("COMMAND")); - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("GETKEYS")); + php_error_docref(NULL, E_WARNING, "Unknown COMMAND operation '%s'", ZSTR_VAL(op)); + return FAILURE; + } - ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - zend_string *zstr = zval_get_string(z_ele); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_string_release(zstr); - } ZEND_HASH_FOREACH_END(); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, !!op + argc, "COMMAND"); + if (op) redis_cmd_append_sstr_zstr(&cmdstr, op); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + for (i = 0; i < argc; ++i) { + zstr = zval_get_string(&z_args[i]); + redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + zend_string_release(zstr); } + // Push out values + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + /* Any slot will do */ CMD_RAND_SLOT(slot); diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 7b06571fb6..9c24f5680f 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7230a9518fe0e79ae51f6b49d269053535a34199 */ + * Stub hash: b18973c6cdb4ae3a706dfd1ad5645339f0fcdd84 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -114,9 +114,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_close arginfo_class_Redis___destruct -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_command, 0, 0, 2) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_command, 0, 0, 0) ZEND_ARG_INFO(0, opt) - ZEND_ARG_INFO(0, arg) + ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_config, 0, 0, 1) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 167346d5d9..0f44dd004c 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7515,6 +7515,26 @@ public function testCopy() $this->assertEquals('bar', $this->redis->get('key2')); } + public function testCommand() + { + $commands = $this->redis->command(); + $this->assertTrue(is_array($commands)); + $this->assertEquals(count($commands), $this->redis->command('count')); + $infos = $this->redis->command('info'); + $this->assertTrue(is_array($infos)); + $this->assertEquals(count($infos), count($commands)); + + if (version_compare($this->version, '7.0') >= 0) { + $docs = $this->redis->command('docs'); + $this->assertTrue(is_array($docs)); + $this->assertEquals(count($docs), 2 * count($commands)); + + $list = $this->redis->command('list', 'filterby', 'pattern', 'lol*'); + $this->assertTrue(is_array($list)); + $this->assertEquals($list, ['lolwut']); + } + } + /* Make sure we handle a bad option value gracefully */ public function testBadOptionValue() { $this->assertFalse(@$this->redis->setOption(pow(2, 32), false)); From b580505b40ef8b182ca79d49cda38c7d1ea9b5c7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 20 Nov 2022 12:58:44 -0800 Subject: [PATCH 1720/1986] Add a link to my Mastodon profile. [skip ci] --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a3a1dc323d..8bb0396d70 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). This code has been developed and maintained by Owlient from November 2009 to March 2011. -You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to michael.grunder@gmail.com ([@grumi78](https://twitter.com/grumi78)), p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)), or n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)). +You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to michael.grunder@gmail.com ([Twitter](https://twitter.com/grumi78), Mastodon), p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)), or n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)). + ## [API Documentation](https://phpredis.github.io/phpredis) These are a work in progress, but will eventually replace our **ONE README TO RULE THEM ALL** docs. From 2a6dee5d4dc5500e1260bcea0f620b6adb0fe22f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 22 Nov 2022 15:03:51 -0800 Subject: [PATCH 1721/1986] Use PHP's new class constant mechanism. Let gen_stub.php define the constants for us, including deriving their actual values from C defines. As a side-effect we have to drop support for PHP < 7.2 as it does not have interned strings. --- .github/workflows/ci.yml | 4 +- redis.c | 102 -------- redis.stub.php | 415 +++++++++++++++++++++++++++++++++ redis_arginfo.h | 330 +++++++++++++++++++++++++- redis_cluster.stub.php | 39 ++++ redis_cluster_arginfo.h | 32 ++- redis_cluster_legacy_arginfo.h | 32 ++- redis_legacy_arginfo.h | 330 +++++++++++++++++++++++++- 8 files changed, 1176 insertions(+), 108 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 895f62953f..907637d488 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1'] experimental: [false] include: - php: '8.2' @@ -85,7 +85,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1'] experimental: [false] include: - php: '8.2' diff --git a/redis.c b/redis.c index d90a06897f..2509d104db 100644 --- a/redis.c +++ b/redis.c @@ -290,104 +290,6 @@ PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) return redis_sock; } -/* Redis and RedisCluster objects share serialization/prefixing settings so - * this is a generic function to add class constants to either */ -static void add_class_constants(zend_class_entry *ce, int is_cluster) { - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STRING"), REDIS_STRING); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_SET"), REDIS_SET); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH); - zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STREAM"), REDIS_STREAM); - - /* Add common mode constants */ - zend_declare_class_constant_long(ce, ZEND_STRL("ATOMIC"), ATOMIC); - zend_declare_class_constant_long(ce, ZEND_STRL("MULTI"), MULTI); - - /* options */ - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_TCP_KEEPALIVE"), REDIS_OPT_TCP_KEEPALIVE); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_REPLY_LITERAL"), REDIS_OPT_REPLY_LITERAL); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_LEVEL"), REDIS_OPT_COMPRESSION_LEVEL); - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_NULL_MULTIBULK_AS_NULL"), REDIS_OPT_NULL_MBULK_AS_NULL); - - /* serializer */ - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE); - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP); -#ifdef HAVE_REDIS_IGBINARY - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY); -#endif -#ifdef HAVE_REDIS_MSGPACK - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK); -#endif - zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_JSON"), REDIS_SERIALIZER_JSON); - - /* compression */ - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_NONE"), REDIS_COMPRESSION_NONE); -#ifdef HAVE_REDIS_LZF - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZF"), REDIS_COMPRESSION_LZF); -#endif -#ifdef HAVE_REDIS_ZSTD - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD"), REDIS_COMPRESSION_ZSTD); - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MIN"), 1); -#ifdef ZSTD_CLEVEL_DEFAULT - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_DEFAULT"), ZSTD_CLEVEL_DEFAULT); -#else - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_DEFAULT"), 3); -#endif - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MAX"), ZSTD_maxCLevel()); -#endif - -#ifdef HAVE_REDIS_LZ4 - zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZ4"), REDIS_COMPRESSION_LZ4); -#endif - - /* scan options*/ - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN); - zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY); - zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY); - zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_PREFIX"), REDIS_SCAN_PREFIX); - zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NOPREFIX"), REDIS_SCAN_NOPREFIX); - - zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5); - zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6); - - if (is_cluster) { - /* Cluster option to allow for slave failover */ - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SLAVE_FAILOVER"), REDIS_OPT_FAILOVER); - zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_NONE"), REDIS_FAILOVER_NONE); - zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_ERROR"), REDIS_FAILOVER_ERROR); - zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE); - zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES); - } else { - /* Cluster doesn't support pipelining at this time */ - zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE); - - zend_declare_class_constant_stringl(ce, ZEND_STRL("LEFT"), ZEND_STRL("left")); - zend_declare_class_constant_stringl(ce, ZEND_STRL("RIGHT"), ZEND_STRL("right")); - } - - /* retry/backoff options*/ - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_MAX_RETRIES"), REDIS_OPT_MAX_RETRIES); - - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_ALGORITHM"), REDIS_OPT_BACKOFF_ALGORITHM); - zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_DEFAULT"), REDIS_BACKOFF_ALGORITHM_DEFAULT); - zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_CONSTANT"), REDIS_BACKOFF_ALGORITHM_CONSTANT); - zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_UNIFORM"), REDIS_BACKOFF_ALGORITHM_UNIFORM); - zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_EXPONENTIAL"), REDIS_BACKOFF_ALGORITHM_EXPONENTIAL); - zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_FULL_JITTER"), REDIS_BACKOFF_ALGORITHM_FULL_JITTER); - zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_EQUAL_JITTER"), REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER); - zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_DECORRELATED_JITTER"), REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER); - - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_BASE"), REDIS_OPT_BACKOFF_BASE); - - zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_CAP"), REDIS_OPT_BACKOFF_CAP); -} - static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor) { if (res->ptr) { @@ -462,10 +364,6 @@ PHP_MINIT_FUNCTION(redis) /* RedisException class */ redis_exception_ce = register_class_RedisException(spl_ce_RuntimeException); - /* Add shared class constants to Redis and RedisCluster objects */ - add_class_constants(redis_ce, 0); - add_class_constants(redis_cluster_ce, 1); - #ifdef PHP_SESSION php_session_register_module(&ps_mod_redis); php_session_register_module(&ps_mod_redis_cluster); diff --git a/redis.stub.php b/redis.stub.php index 35df9ecdbd..6e5347d1e4 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -7,6 +7,421 @@ */ class Redis { + /** + * + * @var int + * @cvalue REDIS_NOT_FOUND + * + */ + public const REDIS_NOT_FOUND = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_STRING + * + */ + public const REDIS_STRING = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_SET + * + */ + public const REDIS_SET = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_LIST + * + */ + public const REDIS_LIST = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_ZSET + * + */ + public const REDIS_ZSET = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_HASH + * + */ + public const REDIS_HASH = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_STREAM + * + */ + public const REDIS_STREAM = UNKNOWN; + + /** + * + * @var int + * @cvalue ATOMIC + * + */ + public const ATOMIC = UNKNOWN; + + /** + * + * @var int + * @cvalue MULTI + * + */ + public const MULTI = UNKNOWN; + + /** + * + * @var int + * @cvalue PIPELINE + * + */ + public const PIPELINE = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_SERIALIZER + * + */ + public const OPT_SERIALIZER = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_PREFIX + * + */ + public const OPT_PREFIX = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_READ_TIMEOUT + * + */ + public const OPT_READ_TIMEOUT = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_TCP_KEEPALIVE + * + */ + public const OPT_TCP_KEEPALIVE = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_COMPRESSION + * + */ + public const OPT_COMPRESSION = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_REPLY_LITERAL + * + */ + public const OPT_REPLY_LITERAL = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_COMPRESSION_LEVEL + * + */ + public const OPT_COMPRESSION_LEVEL = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_NULL_MBULK_AS_NULL + * + */ + public const OPT_NULL_MULTIBULK_AS_NULL = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_SERIALIZER_NONE + * + */ + public const SERIALIZER_NONE = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_SERIALIZER_PHP + * + */ + public const SERIALIZER_PHP = UNKNOWN; + +#ifdef HAVE_REDIS_IGBINARY + /** + * + * @var int + * @cvalue REDIS_SERIALIZER_IGBINARY + * + */ + public const SERIALIZER_IGBINARY = UNKNOWN; +#endif + +#ifdef HAVE_REDIS_MSGPACK + /** + * + * @var int + * @cvalue REDIS_SERIALIZER_MSGPACK + * + */ + public const SERIALIZER_MSGPACK = UNKNOWN; +#endif + + /** + * + * @var int + * @cvalue REDIS_SERIALIZER_JSON + * + */ + public const SERIALIZER_JSON = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_COMPRESSION_NONE + * + */ + public const COMPRESSION_NONE = UNKNOWN; + +#ifdef HAVE_REDIS_LZF + /** + * + * @var int + * @cvalue REDIS_COMPRESSION_LZF + * + */ + public const COMPRESSION_LZF = UNKNOWN; +#endif + +#ifdef HAVE_REDIS_ZSTD + /** + * + * @var int + * @cvalue REDIS_COMPRESSION_ZSTD + * + */ + public const COMPRESSION_ZSTD = UNKNOWN; + +#ifdef ZSTD_CLEVEL_DEFAULT + /** + * + * @var int + * @cvalue ZSTD_CLEVEL_DEFAULT + * + */ + public const COMPRESSION_ZSTD_DEFAULT = UNKNOWN; +#else + /** + * + * @var int + * + */ + public const COMPRESSION_ZSTD_DEFAULT = 3; +#endif + +#ifdef ZSTD_CLEVEL_MAX + /** + * + * @var int + * @cvalue ZSTD_CLEVEL_MAX + * + */ + public const COMPRESSION_ZSTD_MAX = UNKNOWN; +#endif + + /** + * @var int + * @cvalue ZSTD_maxCLevel() + */ + public const COMPRESSION_ZSTD_MAX = UNKNOWN; + +#endif + +#ifdef HAVE_REDIS_LZ4 + /** + * + * @var int + * @cvalue REDIS_COMPRESSION_LZ4 + * + */ + public const COMPRESSION_LZ4 = UNKNOWN; +#endif + + /** + * + * @var int + * @cvalue REDIS_OPT_SCAN + * + */ + public const OPT_SCAN = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_SCAN_RETRY + * + */ + public const SCAN_RETRY = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_SCAN_NORETRY + * + */ + public const SCAN_NORETRY = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_SCAN_PREFIX + * + */ + public const SCAN_PREFIX = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_SCAN_NOPREFIX + * + */ + public const SCAN_NOPREFIX = UNKNOWN; + + /** + * + * @var string + * + */ + public const BEFORE = "before"; + + /** + * + * @var string + * + */ + public const AFTER = "after"; + + /** + * + * @var string + * + */ + public const LEFT = "left"; + + /** + * + * @var string + * + */ + public const RIGHT = "right"; + + /** + * + * @var int + * @cvalue REDIS_OPT_MAX_RETRIES + * + */ + public const OPT_MAX_RETRIES = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_BACKOFF_ALGORITHM + * + */ + public const OPT_BACKOFF_ALGORITHM = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_BACKOFF_ALGORITHM_DEFAULT + * + */ + public const BACKOFF_ALGORITHM_DEFAULT = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_BACKOFF_ALGORITHM_CONSTANT + * + */ + public const BACKOFF_ALGORITHM_CONSTANT = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_BACKOFF_ALGORITHM_UNIFORM + * + */ + public const BACKOFF_ALGORITHM_UNIFORM = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_BACKOFF_ALGORITHM_EXPONENTIAL + * + */ + public const BACKOFF_ALGORITHM_EXPONENTIAL = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_BACKOFF_ALGORITHM_FULL_JITTER + * + */ + public const BACKOFF_ALGORITHM_FULL_JITTER = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER + * + */ + public const BACKOFF_ALGORITHM_EQUAL_JITTER = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER + * + */ + public const BACKOFF_ALGORITHM_DECORRELATED_JITTER = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_BACKOFF_BASE + * + */ + public const OPT_BACKOFF_BASE = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_OPT_BACKOFF_CAP + * + */ + public const OPT_BACKOFF_CAP = UNKNOWN; /** * Create a new Redis instance. If passed sufficient information in the diff --git a/redis_arginfo.h b/redis_arginfo.h index f0d01cd27e..3a3727c498 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b18973c6cdb4ae3a706dfd1ad5645339f0fcdd84 */ + * Stub hash: 27f05179a82a7b33198a3a707134d9da5597ab1c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -1643,6 +1643,334 @@ static zend_class_entry *register_class_Redis(void) INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); + + zval const_REDIS_NOT_FOUND_value; + ZVAL_LONG(&const_REDIS_NOT_FOUND_value, REDIS_NOT_FOUND); + zend_string *const_REDIS_NOT_FOUND_name = zend_string_init_interned("REDIS_NOT_FOUND", sizeof("REDIS_NOT_FOUND") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_NOT_FOUND_name, &const_REDIS_NOT_FOUND_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_NOT_FOUND_name); + + zval const_REDIS_STRING_value; + ZVAL_LONG(&const_REDIS_STRING_value, REDIS_STRING); + zend_string *const_REDIS_STRING_name = zend_string_init_interned("REDIS_STRING", sizeof("REDIS_STRING") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_STRING_name, &const_REDIS_STRING_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_STRING_name); + + zval const_REDIS_SET_value; + ZVAL_LONG(&const_REDIS_SET_value, REDIS_SET); + zend_string *const_REDIS_SET_name = zend_string_init_interned("REDIS_SET", sizeof("REDIS_SET") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_SET_name, &const_REDIS_SET_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_SET_name); + + zval const_REDIS_LIST_value; + ZVAL_LONG(&const_REDIS_LIST_value, REDIS_LIST); + zend_string *const_REDIS_LIST_name = zend_string_init_interned("REDIS_LIST", sizeof("REDIS_LIST") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_LIST_name, &const_REDIS_LIST_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_LIST_name); + + zval const_REDIS_ZSET_value; + ZVAL_LONG(&const_REDIS_ZSET_value, REDIS_ZSET); + zend_string *const_REDIS_ZSET_name = zend_string_init_interned("REDIS_ZSET", sizeof("REDIS_ZSET") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_ZSET_name, &const_REDIS_ZSET_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_ZSET_name); + + zval const_REDIS_HASH_value; + ZVAL_LONG(&const_REDIS_HASH_value, REDIS_HASH); + zend_string *const_REDIS_HASH_name = zend_string_init_interned("REDIS_HASH", sizeof("REDIS_HASH") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_HASH_name, &const_REDIS_HASH_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_HASH_name); + + zval const_REDIS_STREAM_value; + ZVAL_LONG(&const_REDIS_STREAM_value, REDIS_STREAM); + zend_string *const_REDIS_STREAM_name = zend_string_init_interned("REDIS_STREAM", sizeof("REDIS_STREAM") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_STREAM_name, &const_REDIS_STREAM_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_STREAM_name); + + zval const_ATOMIC_value; + ZVAL_LONG(&const_ATOMIC_value, ATOMIC); + zend_string *const_ATOMIC_name = zend_string_init_interned("ATOMIC", sizeof("ATOMIC") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_ATOMIC_name, &const_ATOMIC_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_ATOMIC_name); + + zval const_MULTI_value; + ZVAL_LONG(&const_MULTI_value, MULTI); + zend_string *const_MULTI_name = zend_string_init_interned("MULTI", sizeof("MULTI") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_MULTI_name, &const_MULTI_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_MULTI_name); + + zval const_PIPELINE_value; + ZVAL_LONG(&const_PIPELINE_value, PIPELINE); + zend_string *const_PIPELINE_name = zend_string_init_interned("PIPELINE", sizeof("PIPELINE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_PIPELINE_name, &const_PIPELINE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_PIPELINE_name); + + zval const_OPT_SERIALIZER_value; + ZVAL_LONG(&const_OPT_SERIALIZER_value, REDIS_OPT_SERIALIZER); + zend_string *const_OPT_SERIALIZER_name = zend_string_init_interned("OPT_SERIALIZER", sizeof("OPT_SERIALIZER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_SERIALIZER_name, &const_OPT_SERIALIZER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_SERIALIZER_name); + + zval const_OPT_PREFIX_value; + ZVAL_LONG(&const_OPT_PREFIX_value, REDIS_OPT_PREFIX); + zend_string *const_OPT_PREFIX_name = zend_string_init_interned("OPT_PREFIX", sizeof("OPT_PREFIX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_PREFIX_name, &const_OPT_PREFIX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_PREFIX_name); + + zval const_OPT_READ_TIMEOUT_value; + ZVAL_LONG(&const_OPT_READ_TIMEOUT_value, REDIS_OPT_READ_TIMEOUT); + zend_string *const_OPT_READ_TIMEOUT_name = zend_string_init_interned("OPT_READ_TIMEOUT", sizeof("OPT_READ_TIMEOUT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_READ_TIMEOUT_name, &const_OPT_READ_TIMEOUT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_READ_TIMEOUT_name); + + zval const_OPT_TCP_KEEPALIVE_value; + ZVAL_LONG(&const_OPT_TCP_KEEPALIVE_value, REDIS_OPT_TCP_KEEPALIVE); + zend_string *const_OPT_TCP_KEEPALIVE_name = zend_string_init_interned("OPT_TCP_KEEPALIVE", sizeof("OPT_TCP_KEEPALIVE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_TCP_KEEPALIVE_name, &const_OPT_TCP_KEEPALIVE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_TCP_KEEPALIVE_name); + + zval const_OPT_COMPRESSION_value; + ZVAL_LONG(&const_OPT_COMPRESSION_value, REDIS_OPT_COMPRESSION); + zend_string *const_OPT_COMPRESSION_name = zend_string_init_interned("OPT_COMPRESSION", sizeof("OPT_COMPRESSION") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_COMPRESSION_name, &const_OPT_COMPRESSION_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_COMPRESSION_name); + + zval const_OPT_REPLY_LITERAL_value; + ZVAL_LONG(&const_OPT_REPLY_LITERAL_value, REDIS_OPT_REPLY_LITERAL); + zend_string *const_OPT_REPLY_LITERAL_name = zend_string_init_interned("OPT_REPLY_LITERAL", sizeof("OPT_REPLY_LITERAL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_REPLY_LITERAL_name, &const_OPT_REPLY_LITERAL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_REPLY_LITERAL_name); + + zval const_OPT_COMPRESSION_LEVEL_value; + ZVAL_LONG(&const_OPT_COMPRESSION_LEVEL_value, REDIS_OPT_COMPRESSION_LEVEL); + zend_string *const_OPT_COMPRESSION_LEVEL_name = zend_string_init_interned("OPT_COMPRESSION_LEVEL", sizeof("OPT_COMPRESSION_LEVEL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_COMPRESSION_LEVEL_name, &const_OPT_COMPRESSION_LEVEL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_COMPRESSION_LEVEL_name); + + zval const_OPT_NULL_MULTIBULK_AS_NULL_value; + ZVAL_LONG(&const_OPT_NULL_MULTIBULK_AS_NULL_value, REDIS_OPT_NULL_MBULK_AS_NULL); + zend_string *const_OPT_NULL_MULTIBULK_AS_NULL_name = zend_string_init_interned("OPT_NULL_MULTIBULK_AS_NULL", sizeof("OPT_NULL_MULTIBULK_AS_NULL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_NULL_MULTIBULK_AS_NULL_name, &const_OPT_NULL_MULTIBULK_AS_NULL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_NULL_MULTIBULK_AS_NULL_name); + + zval const_SERIALIZER_NONE_value; + ZVAL_LONG(&const_SERIALIZER_NONE_value, REDIS_SERIALIZER_NONE); + zend_string *const_SERIALIZER_NONE_name = zend_string_init_interned("SERIALIZER_NONE", sizeof("SERIALIZER_NONE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_NONE_name, &const_SERIALIZER_NONE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_NONE_name); + + zval const_SERIALIZER_PHP_value; + ZVAL_LONG(&const_SERIALIZER_PHP_value, REDIS_SERIALIZER_PHP); + zend_string *const_SERIALIZER_PHP_name = zend_string_init_interned("SERIALIZER_PHP", sizeof("SERIALIZER_PHP") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_PHP_name, &const_SERIALIZER_PHP_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_PHP_name); +#if defined(HAVE_REDIS_IGBINARY) + + zval const_SERIALIZER_IGBINARY_value; + ZVAL_LONG(&const_SERIALIZER_IGBINARY_value, REDIS_SERIALIZER_IGBINARY); + zend_string *const_SERIALIZER_IGBINARY_name = zend_string_init_interned("SERIALIZER_IGBINARY", sizeof("SERIALIZER_IGBINARY") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_IGBINARY_name, &const_SERIALIZER_IGBINARY_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_IGBINARY_name); +#endif +#if defined(HAVE_REDIS_MSGPACK) + + zval const_SERIALIZER_MSGPACK_value; + ZVAL_LONG(&const_SERIALIZER_MSGPACK_value, REDIS_SERIALIZER_MSGPACK); + zend_string *const_SERIALIZER_MSGPACK_name = zend_string_init_interned("SERIALIZER_MSGPACK", sizeof("SERIALIZER_MSGPACK") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_MSGPACK_name, &const_SERIALIZER_MSGPACK_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_MSGPACK_name); +#endif + + zval const_SERIALIZER_JSON_value; + ZVAL_LONG(&const_SERIALIZER_JSON_value, REDIS_SERIALIZER_JSON); + zend_string *const_SERIALIZER_JSON_name = zend_string_init_interned("SERIALIZER_JSON", sizeof("SERIALIZER_JSON") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_JSON_name, &const_SERIALIZER_JSON_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_JSON_name); + + zval const_COMPRESSION_NONE_value; + ZVAL_LONG(&const_COMPRESSION_NONE_value, REDIS_COMPRESSION_NONE); + zend_string *const_COMPRESSION_NONE_name = zend_string_init_interned("COMPRESSION_NONE", sizeof("COMPRESSION_NONE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_NONE_name, &const_COMPRESSION_NONE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_NONE_name); +#if defined(HAVE_REDIS_LZF) + + zval const_COMPRESSION_LZF_value; + ZVAL_LONG(&const_COMPRESSION_LZF_value, REDIS_COMPRESSION_LZF); + zend_string *const_COMPRESSION_LZF_name = zend_string_init_interned("COMPRESSION_LZF", sizeof("COMPRESSION_LZF") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_LZF_name, &const_COMPRESSION_LZF_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_LZF_name); +#endif +#if defined(HAVE_REDIS_ZSTD) + + zval const_COMPRESSION_ZSTD_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_value, REDIS_COMPRESSION_ZSTD); + zend_string *const_COMPRESSION_ZSTD_name = zend_string_init_interned("COMPRESSION_ZSTD", sizeof("COMPRESSION_ZSTD") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_name, &const_COMPRESSION_ZSTD_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_DEFAULT) + + zval const_COMPRESSION_ZSTD_DEFAULT_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_DEFAULT_value, ZSTD_CLEVEL_DEFAULT); + zend_string *const_COMPRESSION_ZSTD_DEFAULT_name = zend_string_init_interned("COMPRESSION_ZSTD_DEFAULT", sizeof("COMPRESSION_ZSTD_DEFAULT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && !(defined(ZSTD_CLEVEL_DEFAULT)) + + zval const_COMPRESSION_ZSTD_DEFAULT_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_DEFAULT_value, 3); + zend_string *const_COMPRESSION_ZSTD_DEFAULT_name = zend_string_init_interned("COMPRESSION_ZSTD_DEFAULT", sizeof("COMPRESSION_ZSTD_DEFAULT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_MAX) + + zval const_COMPRESSION_ZSTD_MAX_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_CLEVEL_MAX); + zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MAX_name); +#endif +#if defined(HAVE_REDIS_ZSTD) + + zval const_COMPRESSION_ZSTD_MAX_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_maxCLevel()); + zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MAX_name); +#endif +#if defined(HAVE_REDIS_LZ4) + + zval const_COMPRESSION_LZ4_value; + ZVAL_LONG(&const_COMPRESSION_LZ4_value, REDIS_COMPRESSION_LZ4); + zend_string *const_COMPRESSION_LZ4_name = zend_string_init_interned("COMPRESSION_LZ4", sizeof("COMPRESSION_LZ4") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_LZ4_name, &const_COMPRESSION_LZ4_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_LZ4_name); +#endif + + zval const_OPT_SCAN_value; + ZVAL_LONG(&const_OPT_SCAN_value, REDIS_OPT_SCAN); + zend_string *const_OPT_SCAN_name = zend_string_init_interned("OPT_SCAN", sizeof("OPT_SCAN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_SCAN_name, &const_OPT_SCAN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_SCAN_name); + + zval const_SCAN_RETRY_value; + ZVAL_LONG(&const_SCAN_RETRY_value, REDIS_SCAN_RETRY); + zend_string *const_SCAN_RETRY_name = zend_string_init_interned("SCAN_RETRY", sizeof("SCAN_RETRY") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SCAN_RETRY_name, &const_SCAN_RETRY_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SCAN_RETRY_name); + + zval const_SCAN_NORETRY_value; + ZVAL_LONG(&const_SCAN_NORETRY_value, REDIS_SCAN_NORETRY); + zend_string *const_SCAN_NORETRY_name = zend_string_init_interned("SCAN_NORETRY", sizeof("SCAN_NORETRY") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SCAN_NORETRY_name, &const_SCAN_NORETRY_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SCAN_NORETRY_name); + + zval const_SCAN_PREFIX_value; + ZVAL_LONG(&const_SCAN_PREFIX_value, REDIS_SCAN_PREFIX); + zend_string *const_SCAN_PREFIX_name = zend_string_init_interned("SCAN_PREFIX", sizeof("SCAN_PREFIX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SCAN_PREFIX_name, &const_SCAN_PREFIX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SCAN_PREFIX_name); + + zval const_SCAN_NOPREFIX_value; + ZVAL_LONG(&const_SCAN_NOPREFIX_value, REDIS_SCAN_NOPREFIX); + zend_string *const_SCAN_NOPREFIX_name = zend_string_init_interned("SCAN_NOPREFIX", sizeof("SCAN_NOPREFIX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SCAN_NOPREFIX_name, &const_SCAN_NOPREFIX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SCAN_NOPREFIX_name); + + zval const_BEFORE_value; + zend_string *const_BEFORE_value_str = zend_string_init("before", strlen("before"), 1); + ZVAL_STR(&const_BEFORE_value, const_BEFORE_value_str); + zend_string *const_BEFORE_name = zend_string_init_interned("BEFORE", sizeof("BEFORE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BEFORE_name, &const_BEFORE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BEFORE_name); + + zval const_AFTER_value; + zend_string *const_AFTER_value_str = zend_string_init("after", strlen("after"), 1); + ZVAL_STR(&const_AFTER_value, const_AFTER_value_str); + zend_string *const_AFTER_name = zend_string_init_interned("AFTER", sizeof("AFTER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_AFTER_name, &const_AFTER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_AFTER_name); + + zval const_LEFT_value; + zend_string *const_LEFT_value_str = zend_string_init("left", strlen("left"), 1); + ZVAL_STR(&const_LEFT_value, const_LEFT_value_str); + zend_string *const_LEFT_name = zend_string_init_interned("LEFT", sizeof("LEFT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_LEFT_name, &const_LEFT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_LEFT_name); + + zval const_RIGHT_value; + zend_string *const_RIGHT_value_str = zend_string_init("right", strlen("right"), 1); + ZVAL_STR(&const_RIGHT_value, const_RIGHT_value_str); + zend_string *const_RIGHT_name = zend_string_init_interned("RIGHT", sizeof("RIGHT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_RIGHT_name, &const_RIGHT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_RIGHT_name); + + zval const_OPT_MAX_RETRIES_value; + ZVAL_LONG(&const_OPT_MAX_RETRIES_value, REDIS_OPT_MAX_RETRIES); + zend_string *const_OPT_MAX_RETRIES_name = zend_string_init_interned("OPT_MAX_RETRIES", sizeof("OPT_MAX_RETRIES") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_MAX_RETRIES_name, &const_OPT_MAX_RETRIES_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_MAX_RETRIES_name); + + zval const_OPT_BACKOFF_ALGORITHM_value; + ZVAL_LONG(&const_OPT_BACKOFF_ALGORITHM_value, REDIS_OPT_BACKOFF_ALGORITHM); + zend_string *const_OPT_BACKOFF_ALGORITHM_name = zend_string_init_interned("OPT_BACKOFF_ALGORITHM", sizeof("OPT_BACKOFF_ALGORITHM") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_ALGORITHM_name, &const_OPT_BACKOFF_ALGORITHM_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_BACKOFF_ALGORITHM_name); + + zval const_BACKOFF_ALGORITHM_DEFAULT_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_DEFAULT_value, REDIS_BACKOFF_ALGORITHM_DEFAULT); + zend_string *const_BACKOFF_ALGORITHM_DEFAULT_name = zend_string_init_interned("BACKOFF_ALGORITHM_DEFAULT", sizeof("BACKOFF_ALGORITHM_DEFAULT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_DEFAULT_name, &const_BACKOFF_ALGORITHM_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_DEFAULT_name); + + zval const_BACKOFF_ALGORITHM_CONSTANT_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_CONSTANT_value, REDIS_BACKOFF_ALGORITHM_CONSTANT); + zend_string *const_BACKOFF_ALGORITHM_CONSTANT_name = zend_string_init_interned("BACKOFF_ALGORITHM_CONSTANT", sizeof("BACKOFF_ALGORITHM_CONSTANT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_CONSTANT_name, &const_BACKOFF_ALGORITHM_CONSTANT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_CONSTANT_name); + + zval const_BACKOFF_ALGORITHM_UNIFORM_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_UNIFORM_value, REDIS_BACKOFF_ALGORITHM_UNIFORM); + zend_string *const_BACKOFF_ALGORITHM_UNIFORM_name = zend_string_init_interned("BACKOFF_ALGORITHM_UNIFORM", sizeof("BACKOFF_ALGORITHM_UNIFORM") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_UNIFORM_name, &const_BACKOFF_ALGORITHM_UNIFORM_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_UNIFORM_name); + + zval const_BACKOFF_ALGORITHM_EXPONENTIAL_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_EXPONENTIAL_value, REDIS_BACKOFF_ALGORITHM_EXPONENTIAL); + zend_string *const_BACKOFF_ALGORITHM_EXPONENTIAL_name = zend_string_init_interned("BACKOFF_ALGORITHM_EXPONENTIAL", sizeof("BACKOFF_ALGORITHM_EXPONENTIAL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_EXPONENTIAL_name, &const_BACKOFF_ALGORITHM_EXPONENTIAL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_EXPONENTIAL_name); + + zval const_BACKOFF_ALGORITHM_FULL_JITTER_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_FULL_JITTER_value, REDIS_BACKOFF_ALGORITHM_FULL_JITTER); + zend_string *const_BACKOFF_ALGORITHM_FULL_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_FULL_JITTER", sizeof("BACKOFF_ALGORITHM_FULL_JITTER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_FULL_JITTER_name, &const_BACKOFF_ALGORITHM_FULL_JITTER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_FULL_JITTER_name); + + zval const_BACKOFF_ALGORITHM_EQUAL_JITTER_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_EQUAL_JITTER_value, REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER); + zend_string *const_BACKOFF_ALGORITHM_EQUAL_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_EQUAL_JITTER", sizeof("BACKOFF_ALGORITHM_EQUAL_JITTER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_EQUAL_JITTER_name, &const_BACKOFF_ALGORITHM_EQUAL_JITTER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_EQUAL_JITTER_name); + + zval const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value, REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER); + zend_string *const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_DECORRELATED_JITTER", sizeof("BACKOFF_ALGORITHM_DECORRELATED_JITTER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name, &const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name); + + zval const_OPT_BACKOFF_BASE_value; + ZVAL_LONG(&const_OPT_BACKOFF_BASE_value, REDIS_OPT_BACKOFF_BASE); + zend_string *const_OPT_BACKOFF_BASE_name = zend_string_init_interned("OPT_BACKOFF_BASE", sizeof("OPT_BACKOFF_BASE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_BASE_name, &const_OPT_BACKOFF_BASE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_BACKOFF_BASE_name); + + zval const_OPT_BACKOFF_CAP_value; + ZVAL_LONG(&const_OPT_BACKOFF_CAP_value, REDIS_OPT_BACKOFF_CAP); + zend_string *const_OPT_BACKOFF_CAP_name = zend_string_init_interned("OPT_BACKOFF_CAP", sizeof("OPT_BACKOFF_CAP") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_CAP_name, &const_OPT_BACKOFF_CAP_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_BACKOFF_CAP_name); #if (PHP_VERSION_ID >= 80200) diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index f8c5e51d48..73107dc13d 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -7,6 +7,45 @@ */ class RedisCluster { + /** + * + * @var int + * @cvalue REDIS_OPT_FAILOVER + * + */ + public const OPT_SLAVE_FAILOVER = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_FAILOVER_NONE + * + */ + public const FAILOVER_NONE = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_FAILOVER_ERROR + * + */ + public const FAILOVER_ERROR = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_FAILOVER_DISTRIBUTE + * + */ + public const FAILOVER_DISTRIBUTE = UNKNOWN; + + /** + * + * @var int + * @cvalue REDIS_FAILOVER_DISTRIBUTE_SLAVES + * + */ + public const FAILOVER_DISTRIBUTE_SLAVES = UNKNOWN; public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = NULL, array $context = NULL); diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index b22eae225c..73410276b1 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2f2132e45b1d60011f8ef9298cb35b7ba2b247d5 */ + * Stub hash: f4e2b11cf48fc70db77a52e79443536ad850d06f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -1391,6 +1391,36 @@ static zend_class_entry *register_class_RedisCluster(void) INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); + + zval const_OPT_SLAVE_FAILOVER_value; + ZVAL_LONG(&const_OPT_SLAVE_FAILOVER_value, REDIS_OPT_FAILOVER); + zend_string *const_OPT_SLAVE_FAILOVER_name = zend_string_init_interned("OPT_SLAVE_FAILOVER", sizeof("OPT_SLAVE_FAILOVER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_SLAVE_FAILOVER_name, &const_OPT_SLAVE_FAILOVER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_SLAVE_FAILOVER_name); + + zval const_FAILOVER_NONE_value; + ZVAL_LONG(&const_FAILOVER_NONE_value, REDIS_FAILOVER_NONE); + zend_string *const_FAILOVER_NONE_name = zend_string_init_interned("FAILOVER_NONE", sizeof("FAILOVER_NONE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_FAILOVER_NONE_name, &const_FAILOVER_NONE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_FAILOVER_NONE_name); + + zval const_FAILOVER_ERROR_value; + ZVAL_LONG(&const_FAILOVER_ERROR_value, REDIS_FAILOVER_ERROR); + zend_string *const_FAILOVER_ERROR_name = zend_string_init_interned("FAILOVER_ERROR", sizeof("FAILOVER_ERROR") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_FAILOVER_ERROR_name, &const_FAILOVER_ERROR_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_FAILOVER_ERROR_name); + + zval const_FAILOVER_DISTRIBUTE_value; + ZVAL_LONG(&const_FAILOVER_DISTRIBUTE_value, REDIS_FAILOVER_DISTRIBUTE); + zend_string *const_FAILOVER_DISTRIBUTE_name = zend_string_init_interned("FAILOVER_DISTRIBUTE", sizeof("FAILOVER_DISTRIBUTE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_name, &const_FAILOVER_DISTRIBUTE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_FAILOVER_DISTRIBUTE_name); + + zval const_FAILOVER_DISTRIBUTE_SLAVES_value; + ZVAL_LONG(&const_FAILOVER_DISTRIBUTE_SLAVES_value, REDIS_FAILOVER_DISTRIBUTE_SLAVES); + zend_string *const_FAILOVER_DISTRIBUTE_SLAVES_name = zend_string_init_interned("FAILOVER_DISTRIBUTE_SLAVES", sizeof("FAILOVER_DISTRIBUTE_SLAVES") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_SLAVES_name, &const_FAILOVER_DISTRIBUTE_SLAVES_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_FAILOVER_DISTRIBUTE_SLAVES_name); #if (PHP_VERSION_ID >= 80200) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 99b257a3af..5b2ea77823 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2f2132e45b1d60011f8ef9298cb35b7ba2b247d5 */ + * Stub hash: f4e2b11cf48fc70db77a52e79443536ad850d06f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -1243,6 +1243,36 @@ static zend_class_entry *register_class_RedisCluster(void) INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); + zval const_OPT_SLAVE_FAILOVER_value; + ZVAL_LONG(&const_OPT_SLAVE_FAILOVER_value, REDIS_OPT_FAILOVER); + zend_string *const_OPT_SLAVE_FAILOVER_name = zend_string_init_interned("OPT_SLAVE_FAILOVER", sizeof("OPT_SLAVE_FAILOVER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_SLAVE_FAILOVER_name, &const_OPT_SLAVE_FAILOVER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_SLAVE_FAILOVER_name); + + zval const_FAILOVER_NONE_value; + ZVAL_LONG(&const_FAILOVER_NONE_value, REDIS_FAILOVER_NONE); + zend_string *const_FAILOVER_NONE_name = zend_string_init_interned("FAILOVER_NONE", sizeof("FAILOVER_NONE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_FAILOVER_NONE_name, &const_FAILOVER_NONE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_FAILOVER_NONE_name); + + zval const_FAILOVER_ERROR_value; + ZVAL_LONG(&const_FAILOVER_ERROR_value, REDIS_FAILOVER_ERROR); + zend_string *const_FAILOVER_ERROR_name = zend_string_init_interned("FAILOVER_ERROR", sizeof("FAILOVER_ERROR") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_FAILOVER_ERROR_name, &const_FAILOVER_ERROR_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_FAILOVER_ERROR_name); + + zval const_FAILOVER_DISTRIBUTE_value; + ZVAL_LONG(&const_FAILOVER_DISTRIBUTE_value, REDIS_FAILOVER_DISTRIBUTE); + zend_string *const_FAILOVER_DISTRIBUTE_name = zend_string_init_interned("FAILOVER_DISTRIBUTE", sizeof("FAILOVER_DISTRIBUTE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_name, &const_FAILOVER_DISTRIBUTE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_FAILOVER_DISTRIBUTE_name); + + zval const_FAILOVER_DISTRIBUTE_SLAVES_value; + ZVAL_LONG(&const_FAILOVER_DISTRIBUTE_SLAVES_value, REDIS_FAILOVER_DISTRIBUTE_SLAVES); + zend_string *const_FAILOVER_DISTRIBUTE_SLAVES_name = zend_string_init_interned("FAILOVER_DISTRIBUTE_SLAVES", sizeof("FAILOVER_DISTRIBUTE_SLAVES") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_SLAVES_name, &const_FAILOVER_DISTRIBUTE_SLAVES_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_FAILOVER_DISTRIBUTE_SLAVES_name); + return class_entry; } diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 9c24f5680f..025f3a1ec4 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b18973c6cdb4ae3a706dfd1ad5645339f0fcdd84 */ + * Stub hash: 27f05179a82a7b33198a3a707134d9da5597ab1c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -1485,6 +1485,334 @@ static zend_class_entry *register_class_Redis(void) INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); + zval const_REDIS_NOT_FOUND_value; + ZVAL_LONG(&const_REDIS_NOT_FOUND_value, REDIS_NOT_FOUND); + zend_string *const_REDIS_NOT_FOUND_name = zend_string_init_interned("REDIS_NOT_FOUND", sizeof("REDIS_NOT_FOUND") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_NOT_FOUND_name, &const_REDIS_NOT_FOUND_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_NOT_FOUND_name); + + zval const_REDIS_STRING_value; + ZVAL_LONG(&const_REDIS_STRING_value, REDIS_STRING); + zend_string *const_REDIS_STRING_name = zend_string_init_interned("REDIS_STRING", sizeof("REDIS_STRING") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_STRING_name, &const_REDIS_STRING_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_STRING_name); + + zval const_REDIS_SET_value; + ZVAL_LONG(&const_REDIS_SET_value, REDIS_SET); + zend_string *const_REDIS_SET_name = zend_string_init_interned("REDIS_SET", sizeof("REDIS_SET") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_SET_name, &const_REDIS_SET_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_SET_name); + + zval const_REDIS_LIST_value; + ZVAL_LONG(&const_REDIS_LIST_value, REDIS_LIST); + zend_string *const_REDIS_LIST_name = zend_string_init_interned("REDIS_LIST", sizeof("REDIS_LIST") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_LIST_name, &const_REDIS_LIST_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_LIST_name); + + zval const_REDIS_ZSET_value; + ZVAL_LONG(&const_REDIS_ZSET_value, REDIS_ZSET); + zend_string *const_REDIS_ZSET_name = zend_string_init_interned("REDIS_ZSET", sizeof("REDIS_ZSET") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_ZSET_name, &const_REDIS_ZSET_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_ZSET_name); + + zval const_REDIS_HASH_value; + ZVAL_LONG(&const_REDIS_HASH_value, REDIS_HASH); + zend_string *const_REDIS_HASH_name = zend_string_init_interned("REDIS_HASH", sizeof("REDIS_HASH") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_HASH_name, &const_REDIS_HASH_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_HASH_name); + + zval const_REDIS_STREAM_value; + ZVAL_LONG(&const_REDIS_STREAM_value, REDIS_STREAM); + zend_string *const_REDIS_STREAM_name = zend_string_init_interned("REDIS_STREAM", sizeof("REDIS_STREAM") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_STREAM_name, &const_REDIS_STREAM_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_STREAM_name); + + zval const_ATOMIC_value; + ZVAL_LONG(&const_ATOMIC_value, ATOMIC); + zend_string *const_ATOMIC_name = zend_string_init_interned("ATOMIC", sizeof("ATOMIC") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_ATOMIC_name, &const_ATOMIC_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_ATOMIC_name); + + zval const_MULTI_value; + ZVAL_LONG(&const_MULTI_value, MULTI); + zend_string *const_MULTI_name = zend_string_init_interned("MULTI", sizeof("MULTI") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_MULTI_name, &const_MULTI_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_MULTI_name); + + zval const_PIPELINE_value; + ZVAL_LONG(&const_PIPELINE_value, PIPELINE); + zend_string *const_PIPELINE_name = zend_string_init_interned("PIPELINE", sizeof("PIPELINE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_PIPELINE_name, &const_PIPELINE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_PIPELINE_name); + + zval const_OPT_SERIALIZER_value; + ZVAL_LONG(&const_OPT_SERIALIZER_value, REDIS_OPT_SERIALIZER); + zend_string *const_OPT_SERIALIZER_name = zend_string_init_interned("OPT_SERIALIZER", sizeof("OPT_SERIALIZER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_SERIALIZER_name, &const_OPT_SERIALIZER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_SERIALIZER_name); + + zval const_OPT_PREFIX_value; + ZVAL_LONG(&const_OPT_PREFIX_value, REDIS_OPT_PREFIX); + zend_string *const_OPT_PREFIX_name = zend_string_init_interned("OPT_PREFIX", sizeof("OPT_PREFIX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_PREFIX_name, &const_OPT_PREFIX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_PREFIX_name); + + zval const_OPT_READ_TIMEOUT_value; + ZVAL_LONG(&const_OPT_READ_TIMEOUT_value, REDIS_OPT_READ_TIMEOUT); + zend_string *const_OPT_READ_TIMEOUT_name = zend_string_init_interned("OPT_READ_TIMEOUT", sizeof("OPT_READ_TIMEOUT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_READ_TIMEOUT_name, &const_OPT_READ_TIMEOUT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_READ_TIMEOUT_name); + + zval const_OPT_TCP_KEEPALIVE_value; + ZVAL_LONG(&const_OPT_TCP_KEEPALIVE_value, REDIS_OPT_TCP_KEEPALIVE); + zend_string *const_OPT_TCP_KEEPALIVE_name = zend_string_init_interned("OPT_TCP_KEEPALIVE", sizeof("OPT_TCP_KEEPALIVE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_TCP_KEEPALIVE_name, &const_OPT_TCP_KEEPALIVE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_TCP_KEEPALIVE_name); + + zval const_OPT_COMPRESSION_value; + ZVAL_LONG(&const_OPT_COMPRESSION_value, REDIS_OPT_COMPRESSION); + zend_string *const_OPT_COMPRESSION_name = zend_string_init_interned("OPT_COMPRESSION", sizeof("OPT_COMPRESSION") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_COMPRESSION_name, &const_OPT_COMPRESSION_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_COMPRESSION_name); + + zval const_OPT_REPLY_LITERAL_value; + ZVAL_LONG(&const_OPT_REPLY_LITERAL_value, REDIS_OPT_REPLY_LITERAL); + zend_string *const_OPT_REPLY_LITERAL_name = zend_string_init_interned("OPT_REPLY_LITERAL", sizeof("OPT_REPLY_LITERAL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_REPLY_LITERAL_name, &const_OPT_REPLY_LITERAL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_REPLY_LITERAL_name); + + zval const_OPT_COMPRESSION_LEVEL_value; + ZVAL_LONG(&const_OPT_COMPRESSION_LEVEL_value, REDIS_OPT_COMPRESSION_LEVEL); + zend_string *const_OPT_COMPRESSION_LEVEL_name = zend_string_init_interned("OPT_COMPRESSION_LEVEL", sizeof("OPT_COMPRESSION_LEVEL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_COMPRESSION_LEVEL_name, &const_OPT_COMPRESSION_LEVEL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_COMPRESSION_LEVEL_name); + + zval const_OPT_NULL_MULTIBULK_AS_NULL_value; + ZVAL_LONG(&const_OPT_NULL_MULTIBULK_AS_NULL_value, REDIS_OPT_NULL_MBULK_AS_NULL); + zend_string *const_OPT_NULL_MULTIBULK_AS_NULL_name = zend_string_init_interned("OPT_NULL_MULTIBULK_AS_NULL", sizeof("OPT_NULL_MULTIBULK_AS_NULL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_NULL_MULTIBULK_AS_NULL_name, &const_OPT_NULL_MULTIBULK_AS_NULL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_NULL_MULTIBULK_AS_NULL_name); + + zval const_SERIALIZER_NONE_value; + ZVAL_LONG(&const_SERIALIZER_NONE_value, REDIS_SERIALIZER_NONE); + zend_string *const_SERIALIZER_NONE_name = zend_string_init_interned("SERIALIZER_NONE", sizeof("SERIALIZER_NONE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_NONE_name, &const_SERIALIZER_NONE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_NONE_name); + + zval const_SERIALIZER_PHP_value; + ZVAL_LONG(&const_SERIALIZER_PHP_value, REDIS_SERIALIZER_PHP); + zend_string *const_SERIALIZER_PHP_name = zend_string_init_interned("SERIALIZER_PHP", sizeof("SERIALIZER_PHP") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_PHP_name, &const_SERIALIZER_PHP_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_PHP_name); +#if defined(HAVE_REDIS_IGBINARY) + + zval const_SERIALIZER_IGBINARY_value; + ZVAL_LONG(&const_SERIALIZER_IGBINARY_value, REDIS_SERIALIZER_IGBINARY); + zend_string *const_SERIALIZER_IGBINARY_name = zend_string_init_interned("SERIALIZER_IGBINARY", sizeof("SERIALIZER_IGBINARY") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_IGBINARY_name, &const_SERIALIZER_IGBINARY_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_IGBINARY_name); +#endif +#if defined(HAVE_REDIS_MSGPACK) + + zval const_SERIALIZER_MSGPACK_value; + ZVAL_LONG(&const_SERIALIZER_MSGPACK_value, REDIS_SERIALIZER_MSGPACK); + zend_string *const_SERIALIZER_MSGPACK_name = zend_string_init_interned("SERIALIZER_MSGPACK", sizeof("SERIALIZER_MSGPACK") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_MSGPACK_name, &const_SERIALIZER_MSGPACK_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_MSGPACK_name); +#endif + + zval const_SERIALIZER_JSON_value; + ZVAL_LONG(&const_SERIALIZER_JSON_value, REDIS_SERIALIZER_JSON); + zend_string *const_SERIALIZER_JSON_name = zend_string_init_interned("SERIALIZER_JSON", sizeof("SERIALIZER_JSON") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SERIALIZER_JSON_name, &const_SERIALIZER_JSON_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SERIALIZER_JSON_name); + + zval const_COMPRESSION_NONE_value; + ZVAL_LONG(&const_COMPRESSION_NONE_value, REDIS_COMPRESSION_NONE); + zend_string *const_COMPRESSION_NONE_name = zend_string_init_interned("COMPRESSION_NONE", sizeof("COMPRESSION_NONE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_NONE_name, &const_COMPRESSION_NONE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_NONE_name); +#if defined(HAVE_REDIS_LZF) + + zval const_COMPRESSION_LZF_value; + ZVAL_LONG(&const_COMPRESSION_LZF_value, REDIS_COMPRESSION_LZF); + zend_string *const_COMPRESSION_LZF_name = zend_string_init_interned("COMPRESSION_LZF", sizeof("COMPRESSION_LZF") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_LZF_name, &const_COMPRESSION_LZF_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_LZF_name); +#endif +#if defined(HAVE_REDIS_ZSTD) + + zval const_COMPRESSION_ZSTD_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_value, REDIS_COMPRESSION_ZSTD); + zend_string *const_COMPRESSION_ZSTD_name = zend_string_init_interned("COMPRESSION_ZSTD", sizeof("COMPRESSION_ZSTD") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_name, &const_COMPRESSION_ZSTD_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_DEFAULT) + + zval const_COMPRESSION_ZSTD_DEFAULT_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_DEFAULT_value, ZSTD_CLEVEL_DEFAULT); + zend_string *const_COMPRESSION_ZSTD_DEFAULT_name = zend_string_init_interned("COMPRESSION_ZSTD_DEFAULT", sizeof("COMPRESSION_ZSTD_DEFAULT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && !(defined(ZSTD_CLEVEL_DEFAULT)) + + zval const_COMPRESSION_ZSTD_DEFAULT_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_DEFAULT_value, 3); + zend_string *const_COMPRESSION_ZSTD_DEFAULT_name = zend_string_init_interned("COMPRESSION_ZSTD_DEFAULT", sizeof("COMPRESSION_ZSTD_DEFAULT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_MAX) + + zval const_COMPRESSION_ZSTD_MAX_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_CLEVEL_MAX); + zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MAX_name); +#endif +#if defined(HAVE_REDIS_ZSTD) + + zval const_COMPRESSION_ZSTD_MAX_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_maxCLevel()); + zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MAX_name); +#endif +#if defined(HAVE_REDIS_LZ4) + + zval const_COMPRESSION_LZ4_value; + ZVAL_LONG(&const_COMPRESSION_LZ4_value, REDIS_COMPRESSION_LZ4); + zend_string *const_COMPRESSION_LZ4_name = zend_string_init_interned("COMPRESSION_LZ4", sizeof("COMPRESSION_LZ4") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_LZ4_name, &const_COMPRESSION_LZ4_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_LZ4_name); +#endif + + zval const_OPT_SCAN_value; + ZVAL_LONG(&const_OPT_SCAN_value, REDIS_OPT_SCAN); + zend_string *const_OPT_SCAN_name = zend_string_init_interned("OPT_SCAN", sizeof("OPT_SCAN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_SCAN_name, &const_OPT_SCAN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_SCAN_name); + + zval const_SCAN_RETRY_value; + ZVAL_LONG(&const_SCAN_RETRY_value, REDIS_SCAN_RETRY); + zend_string *const_SCAN_RETRY_name = zend_string_init_interned("SCAN_RETRY", sizeof("SCAN_RETRY") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SCAN_RETRY_name, &const_SCAN_RETRY_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SCAN_RETRY_name); + + zval const_SCAN_NORETRY_value; + ZVAL_LONG(&const_SCAN_NORETRY_value, REDIS_SCAN_NORETRY); + zend_string *const_SCAN_NORETRY_name = zend_string_init_interned("SCAN_NORETRY", sizeof("SCAN_NORETRY") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SCAN_NORETRY_name, &const_SCAN_NORETRY_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SCAN_NORETRY_name); + + zval const_SCAN_PREFIX_value; + ZVAL_LONG(&const_SCAN_PREFIX_value, REDIS_SCAN_PREFIX); + zend_string *const_SCAN_PREFIX_name = zend_string_init_interned("SCAN_PREFIX", sizeof("SCAN_PREFIX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SCAN_PREFIX_name, &const_SCAN_PREFIX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SCAN_PREFIX_name); + + zval const_SCAN_NOPREFIX_value; + ZVAL_LONG(&const_SCAN_NOPREFIX_value, REDIS_SCAN_NOPREFIX); + zend_string *const_SCAN_NOPREFIX_name = zend_string_init_interned("SCAN_NOPREFIX", sizeof("SCAN_NOPREFIX") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_SCAN_NOPREFIX_name, &const_SCAN_NOPREFIX_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_SCAN_NOPREFIX_name); + + zval const_BEFORE_value; + zend_string *const_BEFORE_value_str = zend_string_init("before", strlen("before"), 1); + ZVAL_STR(&const_BEFORE_value, const_BEFORE_value_str); + zend_string *const_BEFORE_name = zend_string_init_interned("BEFORE", sizeof("BEFORE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BEFORE_name, &const_BEFORE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BEFORE_name); + + zval const_AFTER_value; + zend_string *const_AFTER_value_str = zend_string_init("after", strlen("after"), 1); + ZVAL_STR(&const_AFTER_value, const_AFTER_value_str); + zend_string *const_AFTER_name = zend_string_init_interned("AFTER", sizeof("AFTER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_AFTER_name, &const_AFTER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_AFTER_name); + + zval const_LEFT_value; + zend_string *const_LEFT_value_str = zend_string_init("left", strlen("left"), 1); + ZVAL_STR(&const_LEFT_value, const_LEFT_value_str); + zend_string *const_LEFT_name = zend_string_init_interned("LEFT", sizeof("LEFT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_LEFT_name, &const_LEFT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_LEFT_name); + + zval const_RIGHT_value; + zend_string *const_RIGHT_value_str = zend_string_init("right", strlen("right"), 1); + ZVAL_STR(&const_RIGHT_value, const_RIGHT_value_str); + zend_string *const_RIGHT_name = zend_string_init_interned("RIGHT", sizeof("RIGHT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_RIGHT_name, &const_RIGHT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_RIGHT_name); + + zval const_OPT_MAX_RETRIES_value; + ZVAL_LONG(&const_OPT_MAX_RETRIES_value, REDIS_OPT_MAX_RETRIES); + zend_string *const_OPT_MAX_RETRIES_name = zend_string_init_interned("OPT_MAX_RETRIES", sizeof("OPT_MAX_RETRIES") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_MAX_RETRIES_name, &const_OPT_MAX_RETRIES_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_MAX_RETRIES_name); + + zval const_OPT_BACKOFF_ALGORITHM_value; + ZVAL_LONG(&const_OPT_BACKOFF_ALGORITHM_value, REDIS_OPT_BACKOFF_ALGORITHM); + zend_string *const_OPT_BACKOFF_ALGORITHM_name = zend_string_init_interned("OPT_BACKOFF_ALGORITHM", sizeof("OPT_BACKOFF_ALGORITHM") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_ALGORITHM_name, &const_OPT_BACKOFF_ALGORITHM_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_BACKOFF_ALGORITHM_name); + + zval const_BACKOFF_ALGORITHM_DEFAULT_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_DEFAULT_value, REDIS_BACKOFF_ALGORITHM_DEFAULT); + zend_string *const_BACKOFF_ALGORITHM_DEFAULT_name = zend_string_init_interned("BACKOFF_ALGORITHM_DEFAULT", sizeof("BACKOFF_ALGORITHM_DEFAULT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_DEFAULT_name, &const_BACKOFF_ALGORITHM_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_DEFAULT_name); + + zval const_BACKOFF_ALGORITHM_CONSTANT_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_CONSTANT_value, REDIS_BACKOFF_ALGORITHM_CONSTANT); + zend_string *const_BACKOFF_ALGORITHM_CONSTANT_name = zend_string_init_interned("BACKOFF_ALGORITHM_CONSTANT", sizeof("BACKOFF_ALGORITHM_CONSTANT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_CONSTANT_name, &const_BACKOFF_ALGORITHM_CONSTANT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_CONSTANT_name); + + zval const_BACKOFF_ALGORITHM_UNIFORM_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_UNIFORM_value, REDIS_BACKOFF_ALGORITHM_UNIFORM); + zend_string *const_BACKOFF_ALGORITHM_UNIFORM_name = zend_string_init_interned("BACKOFF_ALGORITHM_UNIFORM", sizeof("BACKOFF_ALGORITHM_UNIFORM") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_UNIFORM_name, &const_BACKOFF_ALGORITHM_UNIFORM_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_UNIFORM_name); + + zval const_BACKOFF_ALGORITHM_EXPONENTIAL_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_EXPONENTIAL_value, REDIS_BACKOFF_ALGORITHM_EXPONENTIAL); + zend_string *const_BACKOFF_ALGORITHM_EXPONENTIAL_name = zend_string_init_interned("BACKOFF_ALGORITHM_EXPONENTIAL", sizeof("BACKOFF_ALGORITHM_EXPONENTIAL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_EXPONENTIAL_name, &const_BACKOFF_ALGORITHM_EXPONENTIAL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_EXPONENTIAL_name); + + zval const_BACKOFF_ALGORITHM_FULL_JITTER_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_FULL_JITTER_value, REDIS_BACKOFF_ALGORITHM_FULL_JITTER); + zend_string *const_BACKOFF_ALGORITHM_FULL_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_FULL_JITTER", sizeof("BACKOFF_ALGORITHM_FULL_JITTER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_FULL_JITTER_name, &const_BACKOFF_ALGORITHM_FULL_JITTER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_FULL_JITTER_name); + + zval const_BACKOFF_ALGORITHM_EQUAL_JITTER_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_EQUAL_JITTER_value, REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER); + zend_string *const_BACKOFF_ALGORITHM_EQUAL_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_EQUAL_JITTER", sizeof("BACKOFF_ALGORITHM_EQUAL_JITTER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_EQUAL_JITTER_name, &const_BACKOFF_ALGORITHM_EQUAL_JITTER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_EQUAL_JITTER_name); + + zval const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value; + ZVAL_LONG(&const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value, REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER); + zend_string *const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name = zend_string_init_interned("BACKOFF_ALGORITHM_DECORRELATED_JITTER", sizeof("BACKOFF_ALGORITHM_DECORRELATED_JITTER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name, &const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_BACKOFF_ALGORITHM_DECORRELATED_JITTER_name); + + zval const_OPT_BACKOFF_BASE_value; + ZVAL_LONG(&const_OPT_BACKOFF_BASE_value, REDIS_OPT_BACKOFF_BASE); + zend_string *const_OPT_BACKOFF_BASE_name = zend_string_init_interned("OPT_BACKOFF_BASE", sizeof("OPT_BACKOFF_BASE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_BASE_name, &const_OPT_BACKOFF_BASE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_BACKOFF_BASE_name); + + zval const_OPT_BACKOFF_CAP_value; + ZVAL_LONG(&const_OPT_BACKOFF_CAP_value, REDIS_OPT_BACKOFF_CAP); + zend_string *const_OPT_BACKOFF_CAP_name = zend_string_init_interned("OPT_BACKOFF_CAP", sizeof("OPT_BACKOFF_CAP") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_CAP_name, &const_OPT_BACKOFF_CAP_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_BACKOFF_CAP_name); + return class_entry; } From 90828019de2e9a70829ff1c9aac18d8ca75b2f0f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 24 Nov 2022 10:09:01 -0800 Subject: [PATCH 1722/1986] Refactor network IO tracking. * Create inline wrappers of the low-level php_stream_* functions that also keep track of the number of bytes written/read. * Change the logic to aggregate network traffic until the user explicitly "resets" it. I think this will be a more common use-case (running many commands and then seeing overall network IO). See #2106 --- cluster_library.c | 10 +++----- cluster_library.h | 5 ++-- common.h | 1 + library.c | 35 +++++++++++++------------- library.h | 46 ++++++++++++++++++++++++++++++++++ redis.c | 20 ++++++++++++--- redis.stub.php | 14 ++++++++++- redis_arginfo.h | 9 +++++-- redis_cluster.c | 37 ++++++++++++++++++++++++++- redis_cluster.stub.php | 7 +++++- redis_cluster_arginfo.h | 8 ++++-- redis_cluster_legacy_arginfo.h | 6 ++++- redis_legacy_arginfo.h | 6 ++++- tests/RedisClusterTest.php | 9 ------- tests/RedisTest.php | 28 ++++++++++++++++----- 15 files changed, 187 insertions(+), 54 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0d04593e41..705435f1bd 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -266,7 +266,7 @@ static int cluster_send_direct(RedisSock *redis_sock, char *cmd, int cmd_len, /* Connect to the socket if we aren't yet and send our command, validate the reply type, and consume the first line */ if (!CLUSTER_SEND_PAYLOAD(redis_sock,cmd,cmd_len) || !CLUSTER_VALIDATE_REPLY_TYPE(redis_sock, type) || - !php_stream_gets(redis_sock->stream, buf, sizeof(buf))) return -1; + !redis_sock_gets_raw(redis_sock, buf, sizeof(buf))) return -1; /* Success! */ return 0; @@ -1165,7 +1165,7 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type) CLUSTER_CLEAR_REPLY(c); if (-1 == redis_check_eof(c->cmd_sock, 1, 1) || - EOF == (*reply_type = php_stream_getc(c->cmd_sock->stream))) + EOF == (*reply_type = redis_sock_getc(c->cmd_sock))) { return -1; } @@ -1173,10 +1173,11 @@ static int cluster_check_response(redisCluster *c, REDIS_REPLY_TYPE *reply_type) // In the event of an ERROR, check if it's a MOVED/ASK error if (*reply_type == TYPE_ERR) { char inbuf[4096]; + size_t nbytes; int moved; // Attempt to read the error - if (!php_stream_gets(c->cmd_sock->stream, inbuf, sizeof(inbuf))) { + if (!redis_sock_get_line(c->cmd_sock, inbuf, sizeof(inbuf), &nbytes)) { return -1; } @@ -1500,9 +1501,6 @@ PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, /* Point our cluster to this slot and it's socket */ c->cmd_slot = slot; c->cmd_sock = SLOT_SOCK(c, slot); - if (c->flags->mode != MULTI) { - c->flags->txBytes = 0; - } /* Enable multi mode on this slot if we've been directed to but haven't * send it to this node yet */ diff --git a/cluster_library.h b/cluster_library.h index b5d68858d6..534dc68c6c 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -59,12 +59,11 @@ /* Protected sending of data down the wire to a RedisSock->stream */ #define CLUSTER_SEND_PAYLOAD(sock, buf, len) \ (sock && !redis_sock_server_open(sock) && sock->stream && !redis_check_eof(sock, 0, 1) && \ - php_stream_write(sock->stream, buf, len)==len) + redis_sock_write_raw(sock, buf, len) == len) /* Macro to read our reply type character */ #define CLUSTER_VALIDATE_REPLY_TYPE(sock, type) \ - (redis_check_eof(sock, 1, 1) == 0 && \ - (php_stream_getc(sock->stream) == type)) + (redis_check_eof(sock, 1, 1) == 0 && redis_sock_getc(sock) == type) /* Reset our last single line reply buffer and length */ #define CLUSTER_CLEAR_REPLY(c) \ diff --git a/common.h b/common.h index 258f7d776e..cddf0b695e 100644 --- a/common.h +++ b/common.h @@ -324,6 +324,7 @@ typedef struct { int tcp_keepalive; int sentinel; size_t txBytes; + size_t rxBytes; } RedisSock; /* }}} */ diff --git a/library.c b/library.c index 130177fad7..e6524ecd99 100644 --- a/library.c +++ b/library.c @@ -686,20 +686,24 @@ redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes) { int offset = 0, nbytes; char *reply; - size_t got; + ssize_t got; if (-1 == bytes || -1 == redis_check_eof(redis_sock, 1, 0)) { return NULL; } + /* + 2 for \r\n */ nbytes = bytes + 2; + /* Allocate memory for string */ reply = emalloc(nbytes); /* Consume bulk string */ while (offset < nbytes) { - got = php_stream_read(redis_sock->stream, reply + offset, nbytes - offset); - if (got == 0 && php_stream_eof(redis_sock->stream)) break; + got = redis_sock_read_raw(redis_sock, reply + offset, nbytes - offset); + if (got < 0 || (got == 0 && php_stream_eof(redis_sock->stream))) + break; + offset += got; } @@ -3270,15 +3274,12 @@ PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz) { if (redis_check_eof(redis_sock, 0, 0) == 0 && - php_stream_write(redis_sock->stream, cmd, sz) == sz - ) { - if (IS_MULTI(redis_sock)) { - redis_sock->txBytes += sz; - } else { - redis_sock->txBytes = sz; - } + php_stream_write(redis_sock->stream, cmd, sz) == sz) + { + redis_sock->txBytes += sz; return sz; } + return -1; } @@ -3808,17 +3809,13 @@ redis_key_prefix_zstr(RedisSock *redis_sock, zend_string *key) { */ PHP_REDIS_API int -redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, - size_t *line_size) -{ +redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_size) { // Handle EOF if(-1 == redis_check_eof(redis_sock, 1, 0)) { return -1; } - if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) - == NULL) - { + if(redis_sock_get_line(redis_sock, buf, buf_size, line_size) == NULL) { if (redis_sock->port < 0) { snprintf(buf, buf_size, "read error on connection to %s", ZSTR_VAL(redis_sock->host)); } else { @@ -3844,6 +3841,8 @@ PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info) { + size_t nread; + // Make sure we haven't lost the connection, even trying to reconnect if(-1 == redis_check_eof(redis_sock, 1, 0)) { // Failure @@ -3852,7 +3851,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, } // Attempt to read the reply-type byte - if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { + if((*reply_type = redis_sock_getc(redis_sock)) == EOF) { REDIS_THROW_EXCEPTION( "socket error on read socket", 0); return -1; } @@ -3866,7 +3865,7 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, char inbuf[255]; /* Read up to our newline */ - if (php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) == NULL) { + if (redis_sock_get_line(redis_sock, inbuf, sizeof(inbuf), &nread) == NULL) { return -1; } diff --git a/library.h b/library.h index 121773a922..ab7dc52608 100644 --- a/library.h +++ b/library.h @@ -225,4 +225,50 @@ void redis_conf_string(HashTable *ht, const char *key, size_t keylen, zend_strin void redis_conf_zval(HashTable *ht, const char *key, size_t keylen, zval *zret, int copy, int dtor); void redis_conf_auth(HashTable *ht, const char *key, size_t keylen, zend_string **user, zend_string **pass); +static inline char *redis_sock_get_line(RedisSock *redis_sock, char *buf, size_t buf_size, size_t *nread) { + char *res; + + res = php_stream_get_line(redis_sock->stream, buf, buf_size, nread); + if (res != NULL) + redis_sock->rxBytes += *nread; + + return res; +} + +static inline char redis_sock_getc(RedisSock *redis_sock) { + char res; + + res = php_stream_getc(redis_sock->stream); + if (res != EOF) + redis_sock->rxBytes++; + + return res; +} + +static inline ssize_t redis_sock_read_raw(RedisSock *redis_sock, char *buf, size_t buf_size) { + ssize_t nread; + + nread = php_stream_read(redis_sock->stream, buf, buf_size); + if (nread > 0) + redis_sock->rxBytes += nread; + + return nread; +} + +static inline ssize_t redis_sock_write_raw(RedisSock *redis_sock, const char *buf, size_t buf_size) { + ssize_t nwritten; + + nwritten = php_stream_write(redis_sock->stream, buf, buf_size); + if (nwritten > 0) + redis_sock->txBytes += nwritten; + + return nwritten; +} + +static inline char *redis_sock_gets_raw(RedisSock *redis_sock, char *buf, size_t buf_size) { + size_t nread; + + return redis_sock_get_line(redis_sock, buf, buf_size, &nread); +} + #endif diff --git a/redis.c b/redis.c index 2509d104db..52547c820c 100644 --- a/redis.c +++ b/redis.c @@ -2861,10 +2861,24 @@ PHP_METHOD(Redis, getDBNum) { PHP_METHOD(Redis, getTransferredBytes) { RedisSock *redis_sock; - if ((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) { - RETURN_FALSE; + if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { + RETURN_THROWS(); } - RETURN_LONG(redis_sock->txBytes); + + array_init_size(return_value, 2); + add_next_index_long(return_value, redis_sock->txBytes); + add_next_index_long(return_value, redis_sock->rxBytes); +} + +PHP_METHOD(Redis, clearTransferredBytes) { + RedisSock *redis_sock; + + if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) { + RETURN_THROWS(); + } + + redis_sock->txBytes = 0; + redis_sock->rxBytes = 0; } /* {{{ proto Redis::getTimeout */ diff --git a/redis.stub.php b/redis.stub.php index 6e5347d1e4..1f164c9fec 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1571,7 +1571,19 @@ public function getset(string $key, mixed $value): Redis|string|false; */ public function getTimeout(): float|false; - public function getTransferredBytes(): int|false; + /** + * Get the number of bytes sent and received on the socket. + * + * @return array An array in the form [$sent_bytes, $received_bytes] + */ + public function getTransferredBytes(): array; + + /** + * Reset the number of bytes sent and received on the socket. + * + * @return void + */ + public function clearTransferredBytes(): void; /** * Remove one or more fields from a hash. diff --git a/redis_arginfo.h b/redis_arginfo.h index 3a3727c498..b4f3c9e722 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 27f05179a82a7b33198a3a707134d9da5597ab1c */ + * Stub hash: 2f941423f250241850fc678282e0656ecfb44018 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -361,7 +361,10 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getTimeout, 0, 0, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_getTransferredBytes, 0, 0, MAY_BE_LONG|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getTransferredBytes, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_clearTransferredBytes, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hDel, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -1217,6 +1220,7 @@ ZEND_METHOD(Redis, getReadTimeout); ZEND_METHOD(Redis, getset); ZEND_METHOD(Redis, getTimeout); ZEND_METHOD(Redis, getTransferredBytes); +ZEND_METHOD(Redis, clearTransferredBytes); ZEND_METHOD(Redis, hDel); ZEND_METHOD(Redis, hExists); ZEND_METHOD(Redis, hGet); @@ -1465,6 +1469,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getTransferredBytes, arginfo_class_Redis_getTransferredBytes, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, clearTransferredBytes, arginfo_class_Redis_clearTransferredBytes, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hDel, arginfo_class_Redis_hDel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index 66225e5cf0..8da6e40755 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1719,10 +1719,44 @@ PHP_METHOD(RedisCluster, clearlasterror) { PHP_METHOD(RedisCluster, gettransferredbytes) { redisCluster *c = GET_CONTEXT(); - RETURN_LONG(c->flags->txBytes); + redisClusterNode *node, *slave; + zend_long rx = 0, tx = 0; + + ZEND_HASH_FOREACH_PTR(c->nodes, node) { + tx += node->sock->txBytes; + rx += node->sock->rxBytes; + + if (node->slaves) { + ZEND_HASH_FOREACH_PTR(node->slaves, slave) { + tx += slave->sock->txBytes; + rx += slave->sock->rxBytes; + } ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); + + array_init_size(return_value, 2); + add_next_index_long(return_value, tx); + add_next_index_long(return_value, rx); } /* }}} */ +PHP_METHOD(RedisCluster, cleartransferredbytes) { + redisCluster *c = GET_CONTEXT(); + redisClusterNode *node, *slave; + + ZEND_HASH_FOREACH_PTR(c->nodes, node) { + node->sock->txBytes = 0; + node->sock->rxBytes = 0; + + if (node->slaves) { + ZEND_HASH_FOREACH_PTR(node->slaves, slave) { + slave->sock->txBytes = 0; + slave->sock->rxBytes = 0; + } ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); +} + /* {{{ proto long RedisCluster::getOption(long option */ PHP_METHOD(RedisCluster, getoption) { redisCluster *c = GET_CONTEXT(); @@ -1841,6 +1875,7 @@ PHP_METHOD(RedisCluster, multi) { c->flags->mode = MULTI; c->flags->txBytes = 0; + c->flags->rxBytes = 0; /* Return our object so we can chain MULTI calls */ RETVAL_ZVAL(getThis(), 1, 0); diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 73107dc13d..4ad80503c4 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -395,7 +395,12 @@ public function getset(string $key, mixed $value): RedisCluster|string|bool; /** * @see Redis::gettransferredbytes */ - public function gettransferredbytes(): int|false; + public function gettransferredbytes(): array|false; + + /** + * @see Redis::cleartransferredbytes + */ + public function cleartransferredbytes(): void; /** * @see Redis::hdel diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 73410276b1..7312efbe42 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f4e2b11cf48fc70db77a52e79443536ad850d06f */ + * Stub hash: 9a900fefb0ccb36638768a9d08909c68fff26c10 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -308,7 +308,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getset, 0 ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_gettransferredbytes, 0, 0, MAY_BE_LONG|MAY_BE_FALSE) +#define arginfo_class_RedisCluster_gettransferredbytes arginfo_class_RedisCluster_exec + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_cleartransferredbytes, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hdel, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -1030,6 +1032,7 @@ ZEND_METHOD(RedisCluster, getrange); ZEND_METHOD(RedisCluster, lcs); ZEND_METHOD(RedisCluster, getset); ZEND_METHOD(RedisCluster, gettransferredbytes); +ZEND_METHOD(RedisCluster, cleartransferredbytes); ZEND_METHOD(RedisCluster, hdel); ZEND_METHOD(RedisCluster, hexists); ZEND_METHOD(RedisCluster, hget); @@ -1240,6 +1243,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, gettransferredbytes, arginfo_class_RedisCluster_gettransferredbytes, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, cleartransferredbytes, arginfo_class_RedisCluster_cleartransferredbytes, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 5b2ea77823..6238e43527 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f4e2b11cf48fc70db77a52e79443536ad850d06f */ + * Stub hash: 9a900fefb0ccb36638768a9d08909c68fff26c10 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -276,6 +276,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_gettransferredbytes arginfo_class_RedisCluster__masters +#define arginfo_class_RedisCluster_cleartransferredbytes arginfo_class_RedisCluster__masters + #define arginfo_class_RedisCluster_hdel arginfo_class_RedisCluster_geohash ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexists, 0, 0, 2) @@ -881,6 +883,7 @@ ZEND_METHOD(RedisCluster, getrange); ZEND_METHOD(RedisCluster, lcs); ZEND_METHOD(RedisCluster, getset); ZEND_METHOD(RedisCluster, gettransferredbytes); +ZEND_METHOD(RedisCluster, cleartransferredbytes); ZEND_METHOD(RedisCluster, hdel); ZEND_METHOD(RedisCluster, hexists); ZEND_METHOD(RedisCluster, hget); @@ -1091,6 +1094,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, gettransferredbytes, arginfo_class_RedisCluster_gettransferredbytes, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, cleartransferredbytes, arginfo_class_RedisCluster_cleartransferredbytes, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC) diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 025f3a1ec4..3bbde0cfd2 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 27f05179a82a7b33198a3a707134d9da5597ab1c */ + * Stub hash: 2f941423f250241850fc678282e0656ecfb44018 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -332,6 +332,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_getTransferredBytes arginfo_class_Redis___destruct +#define arginfo_class_Redis_clearTransferredBytes arginfo_class_Redis___destruct + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hDel, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, field) @@ -1058,6 +1060,7 @@ ZEND_METHOD(Redis, getReadTimeout); ZEND_METHOD(Redis, getset); ZEND_METHOD(Redis, getTimeout); ZEND_METHOD(Redis, getTransferredBytes); +ZEND_METHOD(Redis, clearTransferredBytes); ZEND_METHOD(Redis, hDel); ZEND_METHOD(Redis, hExists); ZEND_METHOD(Redis, hGet); @@ -1306,6 +1309,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getTransferredBytes, arginfo_class_Redis_getTransferredBytes, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, clearTransferredBytes, arginfo_class_Redis_clearTransferredBytes, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hDel, arginfo_class_Redis_hDel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index b9fff009d1..6df481d538 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -758,15 +758,6 @@ public function testConnectionPool() { ini_set('redis.pconnect.pooling_enabled', $prev_value); } - public function testTransferredBytes() { - $this->assertTrue($this->redis->ping('')); - $this->assertEquals(strlen("*1\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes()); - $this->assertEquals(['cluster_enabled' => 1], $this->redis->info('', 'cluster')); - $this->assertEquals(strlen("*2\r\n$4\r\nINFO\r\n$7\r\ncluster\r\n"), $this->redis->getTransferredBytes()); - $this->assertEquals([true, true], $this->redis->multi()->ping('')->ping('')->exec()); - $this->assertEquals(strlen("*1\r\n$5\r\nMULTI\r\n*1\r\n$4\r\nEXEC\r\n") + 2 * strlen("*2\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes()); - } - /** * @inheritdoc */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 0f44dd004c..9139a77765 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5817,12 +5817,28 @@ public function testIntrospection() { } public function testTransferredBytes() { - $this->assertTrue($this->redis->ping()); - $this->assertEquals(strlen("*1\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes()); - $this->assertEquals(['cluster_enabled' => 0], $this->redis->info('cluster')); - $this->assertEquals(strlen("*2\r\n$4\r\nINFO\r\n$7\r\ncluster\r\n"), $this->redis->getTransferredBytes()); - $this->assertEquals([true, true], $this->redis->multi()->ping()->ping()->exec()); - $this->assertEquals(strlen("*1\r\n$5\r\nMULTI\r\n*1\r\n$4\r\nEXEC\r\n") + 2 * strlen("*2\r\n$4\r\nPING\r\n"), $this->redis->getTransferredBytes()); + $this->redis->set('key', 'val'); + + $this->redis->clearTransferredBytes(); + + $get_tx_resp = "*3\r\n$3\r\nGET\r\n$3\r\nkey\r\n"; + $get_rx_resp = "$3\r\nval\r\n"; + + $this->assertEquals('val', $this->redis->get('key')); + list ($tx, $rx) = $this->redis->getTransferredBytes(); + $this->assertEquals(strlen($get_tx_resp), $tx); + $this->assertEquals(strlen($get_rx_resp), $rx); + + $this->redis->clearTransferredBytes(); + + $this->redis->multi()->get('key')->get('key')->exec(); + list($tx, $rx) = $this->redis->getTransferredBytes(); + + $this->assertEquals($tx, strlen("*1\r\n$5\r\nMULTI\r\n*1\r\n$4\r\nEXEC\r\n") + + 2 * strlen($get_tx_resp)); + + $this->assertEquals($rx, strlen("+OK\r\n") + strlen("+QUEUED\r\n+QUEUED\r\n") + + strlen("*2\r\n") + 2 * strlen($get_rx_resp)); } /** From 7a4cee2d66963644d938251eac67b353b1fc2ffd Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 25 Nov 2022 08:22:59 -0800 Subject: [PATCH 1723/1986] Review changes See #2106 --- library.c | 3 +-- redis_cluster.c | 52 ++++++++++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/library.c b/library.c index e6524ecd99..187f898257 100644 --- a/library.c +++ b/library.c @@ -3274,9 +3274,8 @@ PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz) { if (redis_check_eof(redis_sock, 0, 0) == 0 && - php_stream_write(redis_sock->stream, cmd, sz) == sz) + redis_sock_write_raw(redis_sock, cmd, sz) == sz) { - redis_sock->txBytes += sz; return sz; } diff --git a/redis_cluster.c b/redis_cluster.c index 8da6e40755..dac0614995 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1717,21 +1717,41 @@ PHP_METHOD(RedisCluster, clearlasterror) { RETURN_TRUE; } +static void redisSumNodeBytes(redisClusterNode *node, zend_long *tx, zend_long *rx) { + struct redisClusterNode *slave; + + *tx += node->sock->txBytes; + *rx += node->sock->rxBytes; + + if (node->slaves) { + ZEND_HASH_FOREACH_PTR(node->slaves, slave) { + *tx += slave->sock->txBytes; + *rx += slave->sock->rxBytes; + } ZEND_HASH_FOREACH_END(); + } +} + +static void redisClearNodeBytes(redisClusterNode *node) { + struct redisClusterNode *slave; + + node->sock->txBytes = 0; + node->sock->rxBytes = 0; + + if (node->slaves) { + ZEND_HASH_FOREACH_PTR(node->slaves, slave) { + slave->sock->txBytes = 0; + slave->sock->rxBytes = 0; + } ZEND_HASH_FOREACH_END(); + } +} + PHP_METHOD(RedisCluster, gettransferredbytes) { redisCluster *c = GET_CONTEXT(); - redisClusterNode *node, *slave; zend_long rx = 0, tx = 0; + redisClusterNode *node; ZEND_HASH_FOREACH_PTR(c->nodes, node) { - tx += node->sock->txBytes; - rx += node->sock->rxBytes; - - if (node->slaves) { - ZEND_HASH_FOREACH_PTR(node->slaves, slave) { - tx += slave->sock->txBytes; - rx += slave->sock->rxBytes; - } ZEND_HASH_FOREACH_END(); - } + redisSumNodeBytes(node, &tx, &rx); } ZEND_HASH_FOREACH_END(); array_init_size(return_value, 2); @@ -1742,18 +1762,10 @@ PHP_METHOD(RedisCluster, gettransferredbytes) { PHP_METHOD(RedisCluster, cleartransferredbytes) { redisCluster *c = GET_CONTEXT(); - redisClusterNode *node, *slave; + redisClusterNode *node; ZEND_HASH_FOREACH_PTR(c->nodes, node) { - node->sock->txBytes = 0; - node->sock->rxBytes = 0; - - if (node->slaves) { - ZEND_HASH_FOREACH_PTR(node->slaves, slave) { - slave->sock->txBytes = 0; - slave->sock->rxBytes = 0; - } ZEND_HASH_FOREACH_END(); - } + redisClearNodeBytes(node); } ZEND_HASH_FOREACH_END(); } From abfac47be031762eed9478a83a19092fb669df07 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 30 Nov 2022 17:54:30 -0800 Subject: [PATCH 1724/1986] Implement SMISMEMBER for RedisCluster See #1894 --- redis_cluster.c | 6 ++++++ redis_cluster.stub.php | 9 +++++++-- redis_cluster_arginfo.h | 8 ++++++-- redis_cluster_legacy_arginfo.h | 6 +++++- tests/RedisClusterTest.php | 1 - 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index dac0614995..7dbe1d21c5 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -993,6 +993,12 @@ PHP_METHOD(RedisCluster, sismember) { } /* }}} */ +/* {{{ proto array RedisCluster::smismember(string key, string member0, ...memberN) */ +PHP_METHOD(RedisCluster, smismember) { + CLUSTER_PROCESS_KW_CMD("SMISMEMBER", redis_key_varval_cmd, cluster_variant_resp, 1); +} +/* }}} */ + /* {{{ proto long RedisCluster::sadd(string key, string val1 [, ...]) */ PHP_METHOD(RedisCluster, sadd) { CLUSTER_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, cluster_long_resp, 0); diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 4ad80503c4..c8b9354c47 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -506,7 +506,7 @@ public function incrbyfloat(string $key, float $value): RedisCluster|float|false * which cluster node we want to send the command to. * @param string $sections Optional section(s) you wish Redis server to return. * - * @return Redis|array|false + * @return RedisCluster|array|false */ public function info(string|array $key_or_address, string ...$sections): RedisCluster|array|false; @@ -816,6 +816,11 @@ public function sinterstore(array|string $key, string ...$other_keys): RedisClus */ public function sismember(string $key, mixed $value): RedisCluster|bool; + /** + * @see Redis::smismember + */ + public function smismember(string $key, string $member, string ...$other_members): RedisCluster|array|false; + /** * @see Redis::slowlog */ @@ -945,7 +950,7 @@ public function xgroup(string $operation, string $key = null, string $group = nu /** * @see Redis::xautoclaim */ - public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array; + public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): RedisCluster|bool|array; /** * @see Redis::xinfo diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 7312efbe42..b2c3fd8820 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9a900fefb0ccb36638768a9d08909c68fff26c10 */ + * Stub hash: 6b0a73d60de6e892ecaaabfe5f69245ebf225fee */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -676,6 +676,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster_setnx +#define arginfo_class_RedisCluster_smismember arginfo_class_RedisCluster_geohash + #define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script #define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster_hgetall @@ -781,7 +783,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 1, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xautoclaim, 0, 5, Redis, MAY_BE_BOOL|MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xautoclaim, 0, 5, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0) @@ -1111,6 +1113,7 @@ ZEND_METHOD(RedisCluster, sinter); ZEND_METHOD(RedisCluster, sintercard); ZEND_METHOD(RedisCluster, sinterstore); ZEND_METHOD(RedisCluster, sismember); +ZEND_METHOD(RedisCluster, smismember); ZEND_METHOD(RedisCluster, slowlog); ZEND_METHOD(RedisCluster, smembers); ZEND_METHOD(RedisCluster, smove); @@ -1322,6 +1325,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, sintercard, arginfo_class_RedisCluster_sintercard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sinterstore, arginfo_class_RedisCluster_sinterstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sismember, arginfo_class_RedisCluster_sismember, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, smismember, arginfo_class_RedisCluster_smismember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, slowlog, arginfo_class_RedisCluster_slowlog, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smembers, arginfo_class_RedisCluster_smembers, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smove, arginfo_class_RedisCluster_smove, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 6238e43527..890e7e3e34 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9a900fefb0ccb36638768a9d08909c68fff26c10 */ + * Stub hash: 6b0a73d60de6e892ecaaabfe5f69245ebf225fee */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -572,6 +572,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sismember arginfo_class_RedisCluster_append +#define arginfo_class_RedisCluster_smismember arginfo_class_RedisCluster_geohash + #define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script #define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster__prefix @@ -962,6 +964,7 @@ ZEND_METHOD(RedisCluster, sinter); ZEND_METHOD(RedisCluster, sintercard); ZEND_METHOD(RedisCluster, sinterstore); ZEND_METHOD(RedisCluster, sismember); +ZEND_METHOD(RedisCluster, smismember); ZEND_METHOD(RedisCluster, slowlog); ZEND_METHOD(RedisCluster, smembers); ZEND_METHOD(RedisCluster, smove); @@ -1173,6 +1176,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, sintercard, arginfo_class_RedisCluster_sintercard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sinterstore, arginfo_class_RedisCluster_sinterstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, sismember, arginfo_class_RedisCluster_sismember, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, smismember, arginfo_class_RedisCluster_smismember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, slowlog, arginfo_class_RedisCluster_slowlog, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smembers, arginfo_class_RedisCluster_smembers, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, smove, arginfo_class_RedisCluster_smove, ZEND_ACC_PUBLIC) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 6df481d538..f1da085254 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -53,7 +53,6 @@ public function testScanErrors() { return $this->markTestSkipped(); } public function testlMove() { return $this->markTestSkipped(); } public function testlPos() { return $this->marktestSkipped(); } - public function testsMisMember() { return $this->markTestSkipped(); } public function testzDiff() { return $this->markTestSkipped(); } public function testzInter() { return $this->markTestSkipped(); } public function testzUnion() { return $this->markTestSkipped(); } From 7644736e136414b075cf6d7b84d04fd4dea02295 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 26 Nov 2022 20:54:41 +0200 Subject: [PATCH 1725/1986] Issue #2068, ssubscribe/sunsubscribe --- common.h | 7 ++++- library.c | 64 +++++++++++++++++++++++++++++------------- redis.c | 19 +++++++++++-- redis.stub.php | 55 ++++++++++++++++++++++++++++++++++++ redis_arginfo.h | 20 +++++++++---- redis_commands.c | 37 ++++++++++++++++++------ redis_legacy_arginfo.h | 20 +++++++++---- 7 files changed, 180 insertions(+), 42 deletions(-) diff --git a/common.h b/common.h index cddf0b695e..dbdf410f5c 100644 --- a/common.h +++ b/common.h @@ -84,6 +84,11 @@ typedef enum _PUBSUB_TYPE { PUBSUB_NUMPAT } PUBSUB_TYPE; +#define REDIS_SUBSCRIBE_IDX 0 +#define REDIS_PSUBSCRIBE_IDX 1 +#define REDIS_SSUBSCRIBE_IDX 2 +#define REDIS_SUBS_BUCKETS 3 + /* options */ #define REDIS_OPT_SERIALIZER 1 #define REDIS_OPT_PREFIX 2 @@ -300,7 +305,7 @@ typedef struct { int persistent; int watching; zend_string *persistent_id; - HashTable *subs; + HashTable *subs[REDIS_SUBS_BUCKETS]; redis_serializer serializer; int compression; int compression_level; diff --git a/library.c b/library.c index 187f898257..db15dcaea2 100644 --- a/library.c +++ b/library.c @@ -485,6 +485,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, subscribeCallback *cb; subscribeContext *sctx = (subscribeContext*)ctx; zval *z_tmp, z_resp; + int i; ALLOC_HASHTABLE(subs); zend_hash_init(subs, 0, NULL, ht_free_subs, 0); @@ -515,13 +516,21 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, zval_dtor(&z_resp); } + if (strcasecmp(sctx->kw, "ssubscribe") == 0) { + i = REDIS_SSUBSCRIBE_IDX; + } else if (strcasecmp(sctx->kw, "psubscribe") == 0) { + i = REDIS_PSUBSCRIBE_IDX; + } else { + i = REDIS_SUBSCRIBE_IDX; + } + efree(sctx); - if (redis_sock->subs) { + if (redis_sock->subs[i]) { zend_string *zkey; ZEND_HASH_FOREACH_STR_KEY_PTR(subs, zkey, cb) { - zend_hash_update_mem(redis_sock->subs, zkey, cb, sizeof(*cb)); + zend_hash_update_mem(redis_sock->subs[i], zkey, cb, sizeof(*cb)); } ZEND_HASH_FOREACH_END(); zend_hash_destroy(subs); efree(subs); @@ -530,9 +539,9 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, return SUCCESS; } - redis_sock->subs = subs; + redis_sock->subs[i] = subs; /* Multibulk response, {[pattern], type, channel, payload } */ - while (redis_sock->subs) { + while (redis_sock->subs[i]) { zval z_ret, z_args[4], *z_type, *z_chan, *z_pat = NULL, *z_data; HashTable *ht_tab; int tab_idx = 1, is_pmsg = 0; @@ -551,9 +560,10 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, } // Check for message or pmessage - if(!strncmp(Z_STRVAL_P(z_type), "message", 7) || - !strncmp(Z_STRVAL_P(z_type), "pmessage", 8)) - { + if (zend_string_equals_literal_ci(Z_STR_P(z_type), "message") || + zend_string_equals_literal_ci(Z_STR_P(z_type), "pmessage") || + zend_string_equals_literal_ci(Z_STR_P(z_type), "smessage") + ) { is_pmsg = *Z_STRVAL_P(z_type)=='p'; } else { zval_dtor(&z_resp); @@ -574,7 +584,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, goto failure; } - if ((cb = zend_hash_str_find_ptr(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) { + if ((cb = zend_hash_str_find_ptr(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) { goto failure; } @@ -624,6 +634,18 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, { subscribeContext *sctx = (subscribeContext*)ctx; zval *z_chan, z_ret, z_resp; + int i; + + if (strcasecmp(sctx->kw, "sunsubscribe") == 0) { + i = REDIS_SSUBSCRIBE_IDX; + } else if (strcasecmp(sctx->kw, "punsubscribe") == 0) { + i = REDIS_PSUBSCRIBE_IDX; + } else { + i = REDIS_SUBSCRIBE_IDX; + } + if (!sctx->argc && redis_sock->subs[i]) { + sctx->argc = zend_hash_num_elements(redis_sock->subs[i]); + } array_init(&z_ret); @@ -639,12 +661,12 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, return FAILURE; } - if (!redis_sock->subs || - !zend_hash_str_exists(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan)) + if (!redis_sock->subs[i] || + !zend_hash_str_exists(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan)) ) { add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 0); } else { - zend_hash_str_del(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan)); + zend_hash_str_del(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan)); add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 1); } @@ -653,10 +675,10 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, efree(sctx); - if (redis_sock->subs && !zend_hash_num_elements(redis_sock->subs)) { - zend_hash_destroy(redis_sock->subs); - efree(redis_sock->subs); - redis_sock->subs = NULL; + if (redis_sock->subs[i] && !zend_hash_num_elements(redis_sock->subs[i])) { + zend_hash_destroy(redis_sock->subs[i]); + efree(redis_sock->subs[i]); + redis_sock->subs[i] = NULL; } RETVAL_ZVAL(&z_ret, 0, 1); @@ -3300,6 +3322,8 @@ free_reply_callbacks(RedisSock *redis_sock) */ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) { + int i; + if (redis_sock->prefix) { zend_string_release(redis_sock->prefix); } @@ -3315,10 +3339,12 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) if (redis_sock->host) { zend_string_release(redis_sock->host); } - if (redis_sock->subs) { - zend_hash_destroy(redis_sock->subs); - efree(redis_sock->subs); - redis_sock->subs = NULL; + for (i = 0; i < REDIS_SUBS_BUCKETS; ++i) { + if (redis_sock->subs[i]) { + zend_hash_destroy(redis_sock->subs[i]); + efree(redis_sock->subs[i]); + redis_sock->subs[i] = NULL; + } } redis_sock_free_auth(redis_sock); free_reply_callbacks(redis_sock); diff --git a/redis.c b/redis.c index 52547c820c..0f94ed516c 100644 --- a/redis.c +++ b/redis.c @@ -2376,6 +2376,15 @@ PHP_METHOD(Redis, psubscribe) REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, redis_subscribe_response); } +/* }}} */ + +/* {{{ proto void Redis::ssubscribe(Array(shardchannel1, shardchannel2, ... shardchannelN)) */ +PHP_METHOD(Redis, ssubscribe) +{ + REDIS_PROCESS_KW_CMD("SSUBSCRIBE", redis_subscribe_cmd, + redis_subscribe_response); +} +/* }}} */ /* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */ PHP_METHOD(Redis, subscribe) { @@ -2384,8 +2393,8 @@ PHP_METHOD(Redis, subscribe) { } /** - * [p]unsubscribe channel_0 channel_1 ... channel_n - * [p]unsubscribe(array(channel_0, channel_1, ..., channel_n)) + * [ps]unsubscribe channel_0 channel_1 ... channel_n + * [ps]unsubscribe(array(channel_0, channel_1, ..., channel_n)) * response format : * array( * channel_0 => TRUE|FALSE, @@ -2407,6 +2416,12 @@ PHP_METHOD(Redis, punsubscribe) redis_unsubscribe_response); } +PHP_METHOD(Redis, sunsubscribe) +{ + REDIS_PROCESS_KW_CMD("SUNSUBSCRIBE", redis_unsubscribe_cmd, + redis_unsubscribe_response); +} + /* {{{ proto string Redis::bgrewriteaof() */ PHP_METHOD(Redis, bgrewriteaof) { diff --git a/redis.stub.php b/redis.stub.php index 1f164c9fec..707e5f3533 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -3235,6 +3235,37 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i */ public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false; + /** + * Subscribes the client to the specified shard channels. + * + * @param array $channels One or more channel names. + * @param callable $cb The callback PhpRedis will invoke when we receive a message + * from one of the subscribed channels. + * + * @return bool True on success, false on faiilure. Note that this command will block the + * client in a subscribe loop, waiting for messages to arrive. + * + * @see https://redis.io/commands/ssubscribe + * + * @example + * $redis = new Redis(['host' => 'localhost']); + * + * $redis->ssubscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { + * echo "[$channel]: $message\n"; + * + * // Unsubscribe from the message channel when we read 'quit' + * if ($message == 'quit') { + * echo "Unsubscribing from '$channel'\n"; + * $redis->sunsubscribe([$channel]); + * } + * }); + * + * // Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be + * // broken and this command will execute. + * echo "Subscribe loop ended\n"; + */ + public function ssubscribe(array $channels, callable $cb): bool; + /** * Retrieve the length of a Redis STRING key. * @@ -3280,6 +3311,30 @@ public function strlen(string $key): Redis|int|false; */ public function subscribe(array $channels, callable $cb): bool; + /** + * Unsubscribes the client from the given shard channels, + * or from all of them if none is given. + * + * @param array $channels One or more channels to unsubscribe from. + * @return Redis|array|bool The array of unsubscribed channels. + * + * @see https://redis.io/commands/sunsubscribe + * @see Redis::ssubscribe() + * + * @example + * $redis->ssubscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) { + * if ($message == 'quit') { + * echo "$channel => 'quit' detected, unsubscribing!\n"; + * $redis->sunsubscribe([$channel]); + * } else { + * echo "$channel => $message\n"; + * } + * }); + * + * echo "We've unsubscribed from both channels, exiting\n"; + */ + public function sunsubscribe(array $channels): Redis|array|bool; + /** * Atomically swap two Redis databases so that all of the keys in the source database will * now be in the destination database and vice-versa. diff --git a/redis_arginfo.h b/redis_arginfo.h index b4f3c9e722..5e5997afbb 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2f941423f250241850fc678282e0656ecfb44018 */ + * Stub hash: a22731395ec32eed913fde7b2de60758fb1e75da */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -836,11 +836,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sscan, 0, 2, MAY_BE_ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_ssubscribe, 0, 2, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_strlen arginfo_class_Redis_expiretime -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 2, _IS_BOOL, 0) +#define arginfo_class_Redis_subscribe arginfo_class_Redis_ssubscribe + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sunsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_swapdb, 0, 2, Redis, MAY_BE_BOOL) @@ -857,9 +863,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_unlink arginfo_class_Redis_del -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_unsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) - ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_unsubscribe arginfo_class_Redis_sunsubscribe #define arginfo_class_Redis_unwatch arginfo_class_Redis_bgSave @@ -1327,8 +1331,10 @@ ZEND_METHOD(Redis, sortDesc); ZEND_METHOD(Redis, sortDescAlpha); ZEND_METHOD(Redis, srem); ZEND_METHOD(Redis, sscan); +ZEND_METHOD(Redis, ssubscribe); ZEND_METHOD(Redis, strlen); ZEND_METHOD(Redis, subscribe); +ZEND_METHOD(Redis, sunsubscribe); ZEND_METHOD(Redis, swapdb); ZEND_METHOD(Redis, time); ZEND_METHOD(Redis, ttl); @@ -1578,8 +1584,10 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, sortDescAlpha, arginfo_class_Redis_sortDescAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, srem, arginfo_class_Redis_srem, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sscan, arginfo_class_Redis_sscan, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, ssubscribe, arginfo_class_Redis_ssubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, subscribe, arginfo_class_Redis_subscribe, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, sunsubscribe, arginfo_class_Redis_sunsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, swapdb, arginfo_class_Redis_swapdb, ZEND_ACC_PUBLIC) ZEND_ME(Redis, time, arginfo_class_Redis_time, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ttl, arginfo_class_Redis_ttl, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index bb3fa04701..dcd67301ef 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1483,6 +1483,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *ht_chan; smart_string cmdstr = {0}; subscribeContext *sctx = ecalloc(1, sizeof(*sctx)); + unsigned short shardslot = REDIS_CLUSTER_SLOTS; size_t key_len; int key_free; char *key; @@ -1503,6 +1504,16 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } + if (strcasecmp(kw, "ssubscribe") == 0) { + zend_hash_internal_pointer_reset(ht_chan); + if ((z_chan = zend_hash_get_current_data(ht_chan)) == NULL) { + php_error_docref(NULL, E_WARNING, "Internal Zend HashTable error"); + efree(sctx); + return FAILURE; + } + shardslot = cluster_hash_key_zval(z_chan); + } + // Start command construction redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); @@ -1510,12 +1521,21 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { // We want to deal with strings here zend_string *zstr = zval_get_string(z_chan); - + // Grab channel name, prefix if required key = ZSTR_VAL(zstr); key_len = ZSTR_LEN(zstr); key_free = redis_key_prefix(redis_sock, &key, &key_len); + if (shardslot != REDIS_CLUSTER_SLOTS && cluster_hash_key(key, key_len) != shardslot) { + php_error_docref(NULL, E_WARNING, "All shard channels needs to belong to a single slot"); + zend_string_release(zstr); + if (key_free) efree(key); + smart_string_free(&cmdstr); + efree(sctx); + return FAILURE; + } + // Add this channel redis_cmd_append_sstr(&cmdstr, key, key_len); @@ -1529,13 +1549,17 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd = cmdstr.c; *ctx = (void*)sctx; - // Pick a slot at random - CMD_RAND_SLOT(slot); + if (shardslot != REDIS_CLUSTER_SLOTS) { + if (slot) *slot = shardslot; + } else { + // Pick a slot at random + CMD_RAND_SLOT(slot); + } return SUCCESS; } -/* UNSUBSCRIBE/PUNSUBSCRIBE */ +/* UNSUBSCRIBE/PUNSUBSCRIBE/SUNSUBSCRIBE */ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -1552,6 +1576,7 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ht_arr = Z_ARRVAL_P(z_arr); + sctx->kw = kw; sctx->argc = zend_hash_num_elements(ht_arr); redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); @@ -1565,10 +1590,6 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); - if (!sctx->argc && redis_sock->subs) { - sctx->argc = zend_hash_num_elements(redis_sock->subs); - } - // Push out vals *cmd_len = cmdstr.len; *cmd = cmdstr.c; diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 3bbde0cfd2..5824c5af00 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2f941423f250241850fc678282e0656ecfb44018 */ + * Stub hash: a22731395ec32eed913fde7b2de60758fb1e75da */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -720,11 +720,17 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_sscan arginfo_class_Redis_hscan +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_ssubscribe, 0, 0, 2) + ZEND_ARG_INFO(0, channels) + ZEND_ARG_INFO(0, cb) +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_strlen arginfo_class_Redis__prefix -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_subscribe, 0, 0, 2) +#define arginfo_class_Redis_subscribe arginfo_class_Redis_ssubscribe + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sunsubscribe, 0, 0, 1) ZEND_ARG_INFO(0, channels) - ZEND_ARG_INFO(0, cb) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_swapdb, 0, 0, 2) @@ -740,9 +746,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_unlink arginfo_class_Redis_del -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_unsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, channels) -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_unsubscribe arginfo_class_Redis_sunsubscribe #define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct @@ -1167,8 +1171,10 @@ ZEND_METHOD(Redis, sortDesc); ZEND_METHOD(Redis, sortDescAlpha); ZEND_METHOD(Redis, srem); ZEND_METHOD(Redis, sscan); +ZEND_METHOD(Redis, ssubscribe); ZEND_METHOD(Redis, strlen); ZEND_METHOD(Redis, subscribe); +ZEND_METHOD(Redis, sunsubscribe); ZEND_METHOD(Redis, swapdb); ZEND_METHOD(Redis, time); ZEND_METHOD(Redis, ttl); @@ -1418,8 +1424,10 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, sortDescAlpha, arginfo_class_Redis_sortDescAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(Redis, srem, arginfo_class_Redis_srem, ZEND_ACC_PUBLIC) ZEND_ME(Redis, sscan, arginfo_class_Redis_sscan, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, ssubscribe, arginfo_class_Redis_ssubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, subscribe, arginfo_class_Redis_subscribe, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, sunsubscribe, arginfo_class_Redis_sunsubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, swapdb, arginfo_class_Redis_swapdb, ZEND_ACC_PUBLIC) ZEND_ME(Redis, time, arginfo_class_Redis_time, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ttl, arginfo_class_Redis_ttl, ZEND_ACC_PUBLIC) From 121e9d9c2948d8da1165aabd39dff67a4b38857f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 1 Dec 2022 13:22:11 -0800 Subject: [PATCH 1726/1986] Implement BLMOVE and add LMOVE/BLMOVE to cluster. * Refactor `redis_lmove_cmd` to work for both `LMOVE` and `BLMOVE` * Implement `LMOVE` and `BLMOVE` in RedisCluster. See #1894 --- redis.c | 10 ++++-- redis.stub.php | 25 ++++++++++++-- redis_arginfo.h | 12 ++++++- redis_cluster.c | 8 +++++ redis_cluster.stub.php | 15 +++++++++ redis_cluster_arginfo.h | 21 +++++++++++- redis_cluster_legacy_arginfo.h | 21 +++++++++++- redis_commands.c | 59 ++++++++++++++++++++++------------ redis_commands.h | 9 +++--- redis_legacy_arginfo.h | 12 ++++++- tests/RedisClusterTest.php | 1 - tests/RedisTest.php | 41 +++++++++++++++-------- 12 files changed, 184 insertions(+), 50 deletions(-) diff --git a/redis.c b/redis.c index 0f94ed516c..b8d4130f62 100644 --- a/redis.c +++ b/redis.c @@ -1158,10 +1158,14 @@ PHP_METHOD(Redis, lLen) } /* }}} */ +/* {{{ proto string Redis::blMove(string source, string destination, string wherefrom, string whereto, double $timeout) */ +PHP_METHOD(Redis, blmove) { + REDIS_PROCESS_KW_CMD("BLMOVE", redis_lmove_cmd, redis_string_response); +} + /* {{{ proto string Redis::lMove(string source, string destination, string wherefrom, string whereto) */ -PHP_METHOD(Redis, lMove) -{ - REDIS_PROCESS_CMD(lmove, redis_string_response); +PHP_METHOD(Redis, lMove) { + REDIS_PROCESS_KW_CMD("LMOVE", redis_lmove_cmd, redis_string_response); } /* {{{ proto boolean Redis::lrem(string list, string value, int count = 0) */ diff --git a/redis.stub.php b/redis.stub.php index 707e5f3533..ad994f7853 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1920,9 +1920,9 @@ public function lLen(string $key): Redis|int|false; * @param string $src The source list. * @param string $dst The destination list * @param string $wherefrom Where in the source list to retrieve the element. This can be either - * `Redis::LEFT`, or `Redis::RIGHT`. + * - `Redis::LEFT`, or `Redis::RIGHT`. * @param string $whereto Where in the destination list to put the element. This can be either - * `Redis::LEFT`, or `Redis::RIGHT`. + * - `Redis::LEFT`, or `Redis::RIGHT`. * @return Redis|string|false The element removed from the source list. * * @example @@ -1931,6 +1931,27 @@ public function lLen(string $key): Redis|int|false; */ public function lMove(string $src, string $dst, string $wherefrom, string $whereto): Redis|string|false; + /** + * Move an element from one list to another, blocking up to a timeout until an element is available. + * + * @param string $src The source list + * @param string $dst The destination list + * @param string $wherefrom Where in the source list to extract the element. + * - `Redis::LEFT`, or `Redis::RIGHT`. + * @param string $whereto Where in the destination list to put the element. + * - `Redis::LEFT`, or `Redis::RIGHT`. + * @param float $timeout How long to block for an element. + * + * @return Redis|string|false; + * + * @example + * @redis->lPush('numbers', 'one'); + * @redis->blmove('numbers', 'odds', Redis::LEFT, Redis::LEFT 1.0); + * // This call will block, if no additional elements are in 'numbers' + * @redis->blmove('numbers', 'odds', Redis::LEFT, Redis::LEFT, 1.0); + */ + public function blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout): Redis|string|false; + /** * Pop one or more elements off a list. * diff --git a/redis_arginfo.h b/redis_arginfo.h index 5e5997afbb..2adb1739db 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a22731395ec32eed913fde7b2de60758fb1e75da */ + * Stub hash: 399e9506bc58ff0da1abc8f46a02b2499ed1223a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -479,6 +479,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lMove, 0, 4, Red ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_blmove, 0, 5, Redis, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPop, 0, 1, Redis, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") @@ -1250,6 +1258,7 @@ ZEND_METHOD(Redis, keys); ZEND_METHOD(Redis, lInsert); ZEND_METHOD(Redis, lLen); ZEND_METHOD(Redis, lMove); +ZEND_METHOD(Redis, blmove); ZEND_METHOD(Redis, lPop); ZEND_METHOD(Redis, lPos); ZEND_METHOD(Redis, lPush); @@ -1501,6 +1510,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, blmove, arginfo_class_Redis_blmove, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPos, arginfo_class_Redis_lPos, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index 7dbe1d21c5..1c71105578 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -969,6 +969,14 @@ PHP_METHOD(RedisCluster, brpoplpush) { } /* }}} */ +PHP_METHOD(RedisCluster, lmove) { + CLUSTER_PROCESS_KW_CMD("LMOVE", redis_lmove_cmd, cluster_bulk_resp, 0); +} + +PHP_METHOD(RedisCluster, blmove) { + CLUSTER_PROCESS_KW_CMD("BLMOVE", redis_lmove_cmd, cluster_bulk_resp, 0); +} + /* {{{ proto long RedisCluster::llen(string key) */ PHP_METHOD(RedisCluster, llen) { CLUSTER_PROCESS_KW_CMD("LLEN", redis_key_cmd, cluster_long_resp, 1); diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index c8b9354c47..76afb89a8f 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -147,6 +147,21 @@ public function brpop(string|array $key, string|float|int $timeout_or_key, mixed */ public function brpoplpush(string $srckey, string $deskey, int $timeout): mixed; + /** + * Move an element from one list into another. + * + * @see Redis::lmove + */ + public function lmove(string $src, string $dst, string $wherefrom, string $whereto): Redis|string|false; + + /** + * Move an element from one list to another, blocking up to a timeout until an element is available. + * + * @see Redis::blmove + * + */ + public function blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout): Redis|string|false; + /** * @see Redis::bzpopmax */ diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index b2c3fd8820..3d0c608773 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6b0a73d60de6e892ecaaabfe5f69245ebf225fee */ + * Stub hash: 4746475a398a16ba176367a0fbc1c9f7e2c5241d */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -94,6 +94,21 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_brpoplpush, 0 ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lmove, 0, 4, Redis, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_blmove, 0, 5, Redis, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_bzpopmax, 0, 2, IS_ARRAY, 0) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_MASK(0, timeout_or_key, MAY_BE_STRING|MAY_BE_LONG, NULL) @@ -984,6 +999,8 @@ ZEND_METHOD(RedisCluster, bitpos); ZEND_METHOD(RedisCluster, blpop); ZEND_METHOD(RedisCluster, brpop); ZEND_METHOD(RedisCluster, brpoplpush); +ZEND_METHOD(RedisCluster, lmove); +ZEND_METHOD(RedisCluster, blmove); ZEND_METHOD(RedisCluster, bzpopmax); ZEND_METHOD(RedisCluster, bzpopmin); ZEND_METHOD(RedisCluster, bzmpop); @@ -1196,6 +1213,8 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, blpop, arginfo_class_RedisCluster_blpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, brpop, arginfo_class_RedisCluster_brpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, brpoplpush, arginfo_class_RedisCluster_brpoplpush, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, lmove, arginfo_class_RedisCluster_lmove, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, blmove, arginfo_class_RedisCluster_blmove, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzpopmax, arginfo_class_RedisCluster_bzpopmax, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzpopmin, arginfo_class_RedisCluster_bzpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzmpop, arginfo_class_RedisCluster_bzmpop, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 890e7e3e34..95fe2595ba 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6b0a73d60de6e892ecaaabfe5f69245ebf225fee */ + * Stub hash: 4746475a398a16ba176367a0fbc1c9f7e2c5241d */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -87,6 +87,21 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_brpoplpush, 0, 0, 3) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lmove, 0, 0, 4) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, wherefrom) + ZEND_ARG_INFO(0, whereto) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_blmove, 0, 0, 5) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, wherefrom) + ZEND_ARG_INFO(0, whereto) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_bzpopmax arginfo_class_RedisCluster_blpop #define arginfo_class_RedisCluster_bzpopmin arginfo_class_RedisCluster_blpop @@ -835,6 +850,8 @@ ZEND_METHOD(RedisCluster, bitpos); ZEND_METHOD(RedisCluster, blpop); ZEND_METHOD(RedisCluster, brpop); ZEND_METHOD(RedisCluster, brpoplpush); +ZEND_METHOD(RedisCluster, lmove); +ZEND_METHOD(RedisCluster, blmove); ZEND_METHOD(RedisCluster, bzpopmax); ZEND_METHOD(RedisCluster, bzpopmin); ZEND_METHOD(RedisCluster, bzmpop); @@ -1047,6 +1064,8 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, blpop, arginfo_class_RedisCluster_blpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, brpop, arginfo_class_RedisCluster_brpop, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, brpoplpush, arginfo_class_RedisCluster_brpoplpush, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, lmove, arginfo_class_RedisCluster_lmove, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, blmove, arginfo_class_RedisCluster_blmove, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzpopmax, arginfo_class_RedisCluster_bzpopmax, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzpopmin, arginfo_class_RedisCluster_bzpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bzmpop, arginfo_class_RedisCluster_bzmpop, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index dcd67301ef..fe75afcb9e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3321,35 +3321,52 @@ int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -int -redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *src, *dst, *from, *to; - size_t src_len, dst_len, from_len, to_len; + zend_string *src = NULL, *dst = NULL, *from = NULL, *to = NULL; + smart_string cmdstr = {0}; + double timeout = 0.0; + short slot2 = 0; + int blocking; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss", - &src, &src_len, &dst, &dst_len, - &from, &from_len, &to, &to_len) == FAILURE - ) { + blocking = toupper(*kw) == 'B'; + + ZEND_PARSE_PARAMETERS_START(4 + !!blocking, 4 + !!blocking) + Z_PARAM_STR(src) + Z_PARAM_STR(dst) + Z_PARAM_STR(from) + Z_PARAM_STR(to) + if (blocking) { + Z_PARAM_DOUBLE(timeout) + } + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (!zend_string_equals_literal_ci(from, "LEFT") && !zend_string_equals_literal_ci(from, "RIGHT")) { + php_error_docref(NULL, E_WARNING, "Wherefrom argument must be 'LEFT' or 'RIGHT'"); + return FAILURE; + } else if (!zend_string_equals_literal_ci(to, "LEFT") && !zend_string_equals_literal_ci(to, "RIGHT")) { + php_error_docref(NULL, E_WARNING, "Whereto argument must be 'LEFT' or 'RIGHT'"); return FAILURE; } - // Validate wherefrom/whereto - if (strcasecmp(from, "left") != 0 && strcasecmp(from, "right") != 0) { - php_error_docref(NULL, E_WARNING, - "Wherefrom argument must be either 'LEFT' or 'RIGHT'"); - return FAILURE; - } else if (strcasecmp(to, "left") != 0 && strcasecmp(to, "right") != 0) { - php_error_docref(NULL, E_WARNING, - "Whereto argument must be either 'LEFT' or 'RIGHT'"); + redis_cmd_init_sstr(&cmdstr, 4 + !!blocking, kw, strlen(kw)); + redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); + redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); + + /* Protect the user from CROSSLOT errors */ + if (slot && slot2 != *slot) { + php_error_docref(NULL, E_WARNING, "Both keys must hash to the same slot!"); + efree(cmdstr.c); return FAILURE; } - /* Construct command */ - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "LMOVE", "kkss", - src, src_len, dst, dst_len, - from, from_len, to, to_len); + redis_cmd_append_sstr_zstr(&cmdstr, from); + redis_cmd_append_sstr_zstr(&cmdstr, to); + if (blocking) redis_cmd_append_sstr_dbl(&cmdstr, timeout); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; return SUCCESS; } diff --git a/redis_commands.h b/redis_commands.h index 7faef7d5c3..f96f12a9ec 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -242,9 +242,6 @@ int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -333,9 +330,11 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx); + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 5824c5af00..4914f81ad9 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a22731395ec32eed913fde7b2de60758fb1e75da */ + * Stub hash: 399e9506bc58ff0da1abc8f46a02b2499ed1223a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -427,6 +427,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lMove, 0, 0, 4) ZEND_ARG_INFO(0, whereto) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_blmove, 0, 0, 5) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, wherefrom) + ZEND_ARG_INFO(0, whereto) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lPop, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, count) @@ -1090,6 +1098,7 @@ ZEND_METHOD(Redis, keys); ZEND_METHOD(Redis, lInsert); ZEND_METHOD(Redis, lLen); ZEND_METHOD(Redis, lMove); +ZEND_METHOD(Redis, blmove); ZEND_METHOD(Redis, lPop); ZEND_METHOD(Redis, lPos); ZEND_METHOD(Redis, lPush); @@ -1341,6 +1350,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, lInsert, arginfo_class_Redis_lInsert, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lLen, arginfo_class_Redis_lLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lMove, arginfo_class_Redis_lMove, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, blmove, arginfo_class_Redis_blmove, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPop, arginfo_class_Redis_lPop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPos, arginfo_class_Redis_lPos, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lPush, arginfo_class_Redis_lPush, ZEND_ACC_PUBLIC) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index f1da085254..2ec30f662d 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -51,7 +51,6 @@ public function testReset() { return $this->markTestSkipped(); } public function testInvalidAuthArgs() { return $this->markTestSkipped(); } public function testScanErrors() { return $this->markTestSkipped(); } - public function testlMove() { return $this->markTestSkipped(); } public function testlPos() { return $this->marktestSkipped(); } public function testzDiff() { return $this->markTestSkipped(); } public function testzInter() { return $this->markTestSkipped(); } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 9139a77765..05bc751bb3 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1461,28 +1461,41 @@ public function testLindex() { $this->assertEquals('val4', $this->redis->lIndex('list', -1)); } - public function testlMove() - { - // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + public function testlMove() { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } - $this->redis->del('list0', 'list1'); - $this->redis->lPush('list0', 'a'); - $this->redis->lPush('list0', 'b'); - $this->redis->lPush('list0', 'c'); + $this->redis->del('{list}0', '{list}1'); + $this->redis->lPush('{list}0', 'a'); + $this->redis->lPush('{list}0', 'b'); + $this->redis->lPush('{list}0', 'c'); - $return = $this->redis->lMove('list0', 'list1', Redis::LEFT, Redis::RIGHT); + $return = $this->redis->lMove('{list}0', '{list}1', Redis::LEFT, Redis::RIGHT); $this->assertEquals('c', $return); - $return = $this->redis->lMove('list0', 'list1', Redis::RIGHT, Redis::LEFT); + $return = $this->redis->lMove('{list}0', '{list}1', Redis::RIGHT, Redis::LEFT); $this->assertEquals('a', $return); - $this->assertEquals(['b'], $this->redis->lRange('list0', 0, -1)); - $this->assertEquals(['a', 'c'], $this->redis->lRange('list1', 0, -1)); + $this->assertEquals(['b'], $this->redis->lRange('{list}0', 0, -1)); + $this->assertEquals(['a', 'c'], $this->redis->lRange('{list}1', 0, -1)); + + } + + public function testBlmove() { + if (version_compare($this->version, '6.2.0') < 0) + $this->markTestSkipped(); + + $this->redis->del('{list}0', '{list}1'); + $this->redis->rpush('{list}0', 'a'); + + $this->assertEquals('a', $this->redis->blmove('{list}0', '{list}1', Redis::LEFT, Redis::LEFT, 1.0)); + + $st = microtime(true); + $ret = $this->redis->blmove('{list}0', '{list}1', Redis::LEFT, Redis::LEFT, .1); + $et = microtime(true); + $this->assertEquals(false, $ret); + $this->assertTrue($et - $st >= .1); } // lRem testing From 7121aaae5c3ab83097453ff8926620b67f57649e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 1 Dec 2022 13:50:24 -0800 Subject: [PATCH 1727/1986] Implement LPOS for RedisCluster See #1894 --- cluster_library.c | 17 ++++++++ cluster_library.h | 2 + library.c | 75 ++++++++++++++++++---------------- library.h | 3 ++ redis_cluster.c | 4 ++ redis_cluster.stub.php | 5 +++ redis_cluster_arginfo.h | 10 ++++- redis_cluster_legacy_arginfo.h | 16 +++++--- tests/RedisClusterTest.php | 1 - 9 files changed, 90 insertions(+), 43 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 705435f1bd..1e35e674f2 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1759,6 +1759,23 @@ cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) } } +PHP_REDIS_API void +cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +{ + zval zret = {0}; + + c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null; + if (redis_read_lpos_response(&zret, c->cmd_sock, c->reply_type, c->reply_len, ctx) < 0) { + ZVAL_FALSE(&zret); + } + + if (CLUSTER_IS_ATOMIC(c)) { + RETVAL_ZVAL(&zret, 0, 1); + } else { + add_next_index_zval(&c->multi_resp, &zret); + } +} + PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { diff --git a/cluster_library.h b/cluster_library.h index 534dc68c6c..3c41dc8b98 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -410,6 +410,8 @@ PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster void *ctx); PHP_REDIS_API void cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, diff --git a/library.c b/library.c index db15dcaea2..c18473d16b 100644 --- a/library.c +++ b/library.c @@ -1455,62 +1455,67 @@ redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ } PHP_REDIS_API int -redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, + long long elements, void *ctx) { char inbuf[4096]; - int i, numElems; size_t len; - zval z_ret; - long lval; - - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) { - goto failure; - } + int i; if (ctx == NULL) { - if (*inbuf != TYPE_INT && *inbuf != TYPE_BULK) { - goto failure; - } - lval = atol(inbuf + 1); - if (lval > -1) { - ZVAL_LONG(&z_ret, lval); - } else if (redis_sock->null_mbulk_as_null) { - ZVAL_NULL(&z_ret); + if (reply_type != TYPE_INT && reply_type != TYPE_BULK) + return FAILURE; + + if (elements > -1) { + ZVAL_LONG(zdst, elements); } else { - ZVAL_FALSE(&z_ret); + REDIS_ZVAL_NULL(redis_sock, zdst); } } else if (ctx == PHPREDIS_CTX_PTR) { - if (*inbuf != TYPE_MULTIBULK) { - goto failure; - } - array_init(&z_ret); - numElems = atol(inbuf + 1); - for (i = 0; i < numElems; ++i) { + if (reply_type != TYPE_MULTIBULK) + return FAILURE; + + array_init(zdst); + + for (i = 0; i < elements; ++i) { if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) { - zval_dtor(&z_ret); - goto failure; + zval_dtor(zdst); + return FAILURE; } - add_next_index_long(&z_ret, atol(inbuf + 1)); + add_next_index_long(zdst, atol(inbuf + 1)); } } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } return SUCCESS; +} + + +PHP_REDIS_API int +redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + char inbuf[1024] = {0}; + int res = SUCCESS; + zval zdst = {0}; + size_t len; + + /* Attempt to read the LPOS response */ + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0 || + redis_read_lpos_response(&zdst, redis_sock, *inbuf, atoll(inbuf+1), ctx) < 0) + { + ZVAL_FALSE(&zdst); + res = FAILURE; + } -failure: if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; + RETVAL_ZVAL(&zdst, 0, 0); } else { - add_next_index_bool(z_tab, 0); + add_next_index_zval(z_tab, &zdst); } - return FAILURE; + + return res; } PHP_REDIS_API int diff --git a/library.h b/library.h index ab7dc52608..508aa346e7 100644 --- a/library.h +++ b/library.h @@ -187,7 +187,10 @@ PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); + PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx); + PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/redis_cluster.c b/redis_cluster.c index 1c71105578..b1e9b5a0ec 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -839,6 +839,10 @@ PHP_METHOD(RedisCluster, lpop) { } /* }}} */ +PHP_METHOD(RedisCluster, lpos) { + CLUSTER_PROCESS_CMD(lpos, cluster_lpos_resp, 1); +} + /* {{{ proto string RedisCluster::rpop(string key, [int count = 0]) */ PHP_METHOD(RedisCluster, rpop) { CLUSTER_PROCESS_KW_CMD("RPOP", redis_pop_cmd, cluster_pop_resp, 0); diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 76afb89a8f..f48e404066 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -560,6 +560,11 @@ public function llen(string $key): RedisCluster|int|bool; */ public function lpop(string $key, int $count = 0): RedisCluster|bool|string|array; + /** + * @see Redis::lpos + */ + public function lpos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array; + /** * @see Redis::lpush */ diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 3d0c608773..75fceb84a9 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4746475a398a16ba176367a0fbc1c9f7e2c5241d */ + * Stub hash: 048afee969c189861f5ba0b8f8fb8cbeeba9a206 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -448,6 +448,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpop, 0, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) @@ -1078,6 +1084,7 @@ ZEND_METHOD(RedisCluster, lindex); ZEND_METHOD(RedisCluster, linsert); ZEND_METHOD(RedisCluster, llen); ZEND_METHOD(RedisCluster, lpop); +ZEND_METHOD(RedisCluster, lpos); ZEND_METHOD(RedisCluster, lpush); ZEND_METHOD(RedisCluster, lpushx); ZEND_METHOD(RedisCluster, lrange); @@ -1292,6 +1299,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, linsert, arginfo_class_RedisCluster_linsert, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, llen, arginfo_class_RedisCluster_llen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpop, arginfo_class_RedisCluster_lpop, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, lpos, arginfo_class_RedisCluster_lpos, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpush, arginfo_class_RedisCluster_lpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpushx, arginfo_class_RedisCluster_lpushx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lrange, arginfo_class_RedisCluster_lrange, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 95fe2595ba..31a7994a12 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4746475a398a16ba176367a0fbc1c9f7e2c5241d */ + * Stub hash: 048afee969c189861f5ba0b8f8fb8cbeeba9a206 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -382,6 +382,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpop, 0, 0, 1) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpos, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lpush, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) @@ -545,11 +551,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sdiffstore, 0, 0, 2) ZEND_ARG_VARIADIC_INFO(0, other_keys) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_set, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_set arginfo_class_RedisCluster_lpos ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_setbit, 0, 0, 3) ZEND_ARG_INFO(0, key) @@ -929,6 +931,7 @@ ZEND_METHOD(RedisCluster, lindex); ZEND_METHOD(RedisCluster, linsert); ZEND_METHOD(RedisCluster, llen); ZEND_METHOD(RedisCluster, lpop); +ZEND_METHOD(RedisCluster, lpos); ZEND_METHOD(RedisCluster, lpush); ZEND_METHOD(RedisCluster, lpushx); ZEND_METHOD(RedisCluster, lrange); @@ -1143,6 +1146,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, linsert, arginfo_class_RedisCluster_linsert, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, llen, arginfo_class_RedisCluster_llen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpop, arginfo_class_RedisCluster_lpop, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, lpos, arginfo_class_RedisCluster_lpos, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpush, arginfo_class_RedisCluster_lpush, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lpushx, arginfo_class_RedisCluster_lpushx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, lrange, arginfo_class_RedisCluster_lrange, ZEND_ACC_PUBLIC) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 2ec30f662d..e3dfc3e624 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -51,7 +51,6 @@ public function testReset() { return $this->markTestSkipped(); } public function testInvalidAuthArgs() { return $this->markTestSkipped(); } public function testScanErrors() { return $this->markTestSkipped(); } - public function testlPos() { return $this->marktestSkipped(); } public function testzDiff() { return $this->markTestSkipped(); } public function testzInter() { return $this->markTestSkipped(); } public function testzUnion() { return $this->markTestSkipped(); } From fa5d1af9ff9a2adc0c2aa10bc99112729ca684c4 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Thu, 1 Dec 2022 21:54:15 -0800 Subject: [PATCH 1728/1986] Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. (#2277) * Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. See #1894 --- cluster_library.c | 18 ++++++ cluster_library.h | 2 + library.c | 103 ++++++++++++++++++++------------- library.h | 1 + redis_cluster.c | 9 +++ redis_cluster.stub.php | 10 ++++ redis_cluster_arginfo.h | 23 +++++++- redis_cluster_legacy_arginfo.h | 23 +++++++- redis_commands.c | 11 +++- tests/RedisClusterTest.php | 2 - tests/RedisTest.php | 6 +- 11 files changed, 158 insertions(+), 50 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 1e35e674f2..133d6d984d 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1776,6 +1776,24 @@ cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) } } +PHP_REDIS_API void +cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + zval zret = {0}; + + c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null; + if (c->reply_type != TYPE_MULTIBULK || + redis_read_geosearch_response(&zret, c->cmd_sock, c->reply_len, ctx != NULL) < 0) + { + ZVAL_FALSE(&zret); + } + + if (CLUSTER_IS_ATOMIC(c)) { + RETVAL_ZVAL(&zret, 0, 1); + } else { + add_next_index_zval(&c->multi_resp, &zret); + } +} + PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { diff --git a/cluster_library.h b/cluster_library.h index 3c41dc8b98..b075ca63fb 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -414,6 +414,8 @@ PHP_REDIS_API void cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster void *ctx); PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, diff --git a/library.c b/library.c index c18473d16b..79941e2adc 100644 --- a/library.c +++ b/library.c @@ -1781,64 +1781,85 @@ redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return res; } +#if PHP_VERSION_ID < 80200 +static HashTable *zend_array_to_list(HashTable *arr) { + zval zret = {0}, *zv; + + array_init_size(&zret, zend_hash_num_elements(arr)); + + ZEND_HASH_FOREACH_VAL(arr, zv) { + Z_TRY_ADDREF_P(zv); + add_next_index_zval(&zret, zv); + } ZEND_HASH_FOREACH_END(); + + return Z_ARRVAL(zret); +} +#endif + PHP_REDIS_API int -redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +redis_read_geosearch_response(zval *zdst, RedisSock *redis_sock, + long long elements, int with_aux_data) { - int numElems; - zval z_ret, z_multi_result, z_sub, z_tmp, *z_ele, *zv; + zval z_multi_result, z_sub, *z_ele, *zv; zend_string *zkey; - if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } - return FAILURE; + /* Handle the trivial "empty" result first */ + if (elements < 0 && redis_sock->null_mbulk_as_null) { + ZVAL_NULL(zdst); + return SUCCESS; } - if (numElems < 0 && redis_sock->null_mbulk_as_null) { - ZVAL_NULL(&z_ret); + array_init(zdst); + + if (with_aux_data == 0) { + redis_mbulk_reply_loop(redis_sock, zdst, elements, UNSERIALIZE_NONE); } else { - array_init(&z_ret); - if (ctx == NULL) { - redis_mbulk_reply_loop(redis_sock, &z_ret, numElems, UNSERIALIZE_NONE); - } else { - array_init(&z_multi_result); - redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_multi_result); + array_init(&z_multi_result); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_multi_result), z_ele) { - // The first item in the sub-array is always the name of the returned item - zv = zend_hash_index_find(Z_ARRVAL_P(z_ele), 0); - zkey = zval_get_string(zv); + redis_read_multibulk_recursive(redis_sock, elements, 0, &z_multi_result); - zend_hash_index_del(Z_ARRVAL_P(z_ele), 0); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_multi_result), z_ele) { + // The first item in the sub-array is always the name of the returned item + zv = zend_hash_index_find(Z_ARRVAL_P(z_ele), 0); + zkey = zval_get_string(zv); - // The other information is returned in the following order as successive - // elements of the sub-array: distance, geohash, coordinates - zend_hash_apply(Z_ARRVAL_P(z_ele), geosearch_cast); + zend_hash_index_del(Z_ARRVAL_P(z_ele), 0); - // Copy values to re-order from zero - array_init(&z_sub); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_ele), zv) { - ZVAL_ZVAL(&z_tmp, zv, 1, 0); - add_next_index_zval(&z_sub, &z_tmp); - } ZEND_HASH_FOREACH_END(); + // The other information is returned in the following order as successive + // elements of the sub-array: distance, geohash, coordinates + zend_hash_apply(Z_ARRVAL_P(z_ele), geosearch_cast); - add_assoc_zval_ex(&z_ret, ZSTR_VAL(zkey), ZSTR_LEN(zkey), &z_sub); - zend_string_release(zkey); - } ZEND_HASH_FOREACH_END(); + // Reindex elements so they start at zero */ + ZVAL_ARR(&z_sub, zend_array_to_list(Z_ARRVAL_P(z_ele))); - // Cleanup - zval_dtor(&z_multi_result); - } + add_assoc_zval_ex(zdst, ZSTR_VAL(zkey), ZSTR_LEN(zkey), &z_sub); + zend_string_release(zkey); + } ZEND_HASH_FOREACH_END(); + + // Cleanup + zval_dtor(&z_multi_result); + } + + return SUCCESS; +} + +PHP_REDIS_API int +redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval zret = {0}; + int elements; + + if (read_mbulk_header(redis_sock, &elements) < 0 || + redis_read_geosearch_response(&zret, redis_sock, elements, ctx != NULL) < 0) + { + ZVAL_FALSE(&zret); } if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); + RETVAL_ZVAL(&zret, 0, 1); } else { - add_next_index_zval(z_tab, &z_ret); + add_next_index_zval(z_tab, &zret); } return SUCCESS; diff --git a/library.h b/library.h index 508aa346e7..acd046d78a 100644 --- a/library.h +++ b/library.h @@ -184,6 +184,7 @@ PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSoc PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_read_geosearch_response(zval *zdst, RedisSock *redis_sock, long long elements, int with_aux_data); PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/redis_cluster.c b/redis_cluster.c index b1e9b5a0ec..cfd6199039 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2864,6 +2864,15 @@ PHP_METHOD(RedisCluster, georadiusbymember_ro) { CLUSTER_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, cluster_variant_resp, 1); } +PHP_METHOD(RedisCluster, geosearch) { + CLUSTER_PROCESS_CMD(geosearch, cluster_geosearch_resp, 1); +} + +PHP_METHOD(RedisCluster, geosearchstore) { + CLUSTER_PROCESS_CMD(geosearchstore, cluster_long_resp, 0); +} + + /* {{{ proto array RedisCluster::role(string key) * proto array RedisCluster::role(array host_port) */ PHP_METHOD(RedisCluster, role) { diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index f48e404066..9b0a39892e 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -367,6 +367,16 @@ public function georadiusbymember(string $key, string $member, float $radius, st */ public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): mixed; + /** + * @see https://redis.io/commands/geosearch + */ + public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): RedisCluster|array; + + /** + * @see https://redis.io/commands/geosearchstore + */ + public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): RedisCluster|array|int|false; + /** * @see Redis::get */ diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 75fceb84a9..0b5fea33e4 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 048afee969c189861f5ba0b8f8fb8cbeeba9a206 */ + * Stub hash: ed8ae1edcec62f211d6ba70e78b3c0f087b2cbde */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -291,6 +291,23 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_georadiusbymember_ro arginfo_class_RedisCluster_georadiusbymember +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geosearch, 0, 4, RedisCluster, MAY_BE_ARRAY) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL) + ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) + ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_geosearchstore, 0, 5, RedisCluster, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) + ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL) + ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) + ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_get, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -1048,6 +1065,8 @@ ZEND_METHOD(RedisCluster, georadius); ZEND_METHOD(RedisCluster, georadius_ro); ZEND_METHOD(RedisCluster, georadiusbymember); ZEND_METHOD(RedisCluster, georadiusbymember_ro); +ZEND_METHOD(RedisCluster, geosearch); +ZEND_METHOD(RedisCluster, geosearchstore); ZEND_METHOD(RedisCluster, get); ZEND_METHOD(RedisCluster, getbit); ZEND_METHOD(RedisCluster, getlasterror); @@ -1263,6 +1282,8 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, georadius_ro, arginfo_class_RedisCluster_georadius_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadiusbymember, arginfo_class_RedisCluster_georadiusbymember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadiusbymember_ro, arginfo_class_RedisCluster_georadiusbymember_ro, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 31a7994a12..794da9bd3d 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 048afee969c189861f5ba0b8f8fb8cbeeba9a206 */ + * Stub hash: ed8ae1edcec62f211d6ba70e78b3c0f087b2cbde */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -263,6 +263,23 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_georadiusbymember_ro arginfo_class_RedisCluster_georadiusbymember +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geosearch, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, position) + ZEND_ARG_INFO(0, shape) + ZEND_ARG_INFO(0, unit) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_geosearchstore, 0, 0, 5) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, position) + ZEND_ARG_INFO(0, shape) + ZEND_ARG_INFO(0, unit) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_get arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_append @@ -895,6 +912,8 @@ ZEND_METHOD(RedisCluster, georadius); ZEND_METHOD(RedisCluster, georadius_ro); ZEND_METHOD(RedisCluster, georadiusbymember); ZEND_METHOD(RedisCluster, georadiusbymember_ro); +ZEND_METHOD(RedisCluster, geosearch); +ZEND_METHOD(RedisCluster, geosearchstore); ZEND_METHOD(RedisCluster, get); ZEND_METHOD(RedisCluster, getbit); ZEND_METHOD(RedisCluster, getlasterror); @@ -1110,6 +1129,8 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, georadius_ro, arginfo_class_RedisCluster_georadius_ro, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadiusbymember, arginfo_class_RedisCluster_georadiusbymember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, georadiusbymember_ro, arginfo_class_RedisCluster_georadiusbymember_ro, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index fe75afcb9e..c493528b8c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1521,7 +1521,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { // We want to deal with strings here zend_string *zstr = zval_get_string(z_chan); - + // Grab channel name, prefix if required key = ZSTR_VAL(zstr); key_len = ZSTR_LEN(zstr); @@ -4650,6 +4650,7 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; zval *position, *shape, *opts = NULL, *z_ele; zend_string *zkey; + short s2 = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszzs|a", &dest, &destlen, &src, &srclen, &position, &shape, @@ -4708,7 +4709,13 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCHSTORE"); redis_cmd_append_sstr_key(&cmdstr, dest, destlen, redis_sock, slot); - redis_cmd_append_sstr_key(&cmdstr, src, srclen, redis_sock, slot); + redis_cmd_append_sstr_key(&cmdstr, src, srclen, redis_sock, slot ? &s2 : NULL); + + if (slot && *slot != s2) { + php_error_docref(NULL, E_WARNING, "All keys must hash to the same slot"); + efree(cmdstr.c); + return FAILURE; + } if (Z_TYPE_P(position) == IS_ARRAY) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index e3dfc3e624..506182400d 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -58,8 +58,6 @@ public function testzDiffStore() { return $this->markTestSkipped(); } public function testzMscore() { return $this->marktestSkipped(); } public function testZRandMember() { return $this->marktestSkipped(); } public function testCopy() { return $this->marktestSkipped(); } - public function testGeoSearch() { return $this->marktestSkipped(); } - public function testGeoSearchStore() { return $this->marktestSkipped(); } public function testHRandField() { return $this->marktestSkipped(); } public function testConfig() { return $this->markTestSkipped(); } public function testFlushDB() { return $this->markTestSkipped(); } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 05bc751bb3..0affd1364b 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -6466,9 +6466,9 @@ public function testGeoSearchStore() { return $this->markTestSkipped(); } - $this->addCities('gk'); - $this->assertEquals($this->redis->geosearchstore('{gk}', 'gk', 'Chico', 100, 'km'), 3); - $this->assertEquals($this->redis->geosearch('{gk}', 'Chico', 1, 'm'), ['Chico']); + $this->addCities('{gk}src'); + $this->assertEquals($this->redis->geosearchstore('{gk}dst', '{gk}src', 'Chico', 100, 'km'), 3); + $this->assertEquals($this->redis->geosearch('{gk}dst', 'Chico', 1, 'm'), ['Chico']); } /* Test a 'raw' command */ From e222b85ecf6dea9621e60bd9cc8c8d928a341f6f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 1 Dec 2022 22:01:53 -0800 Subject: [PATCH 1729/1986] Implement HRANDFIELD for RedisCluster See #1894 --- cluster_library.c | 14 ++++++++++++++ cluster_library.h | 2 ++ redis_cluster.c | 6 ++++++ redis_cluster.stub.php | 5 +++++ redis_cluster_arginfo.h | 9 ++++++++- redis_cluster_legacy_arginfo.h | 16 ++++++++++------ tests/RedisClusterTest.php | 1 - 7 files changed, 45 insertions(+), 8 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 133d6d984d..67844e1a47 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1747,6 +1747,20 @@ PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster CLUSTER_RETURN_BOOL(c, 1); } +PHP_REDIS_API void +cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +{ + if (ctx == NULL) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else if (ctx == PHPREDIS_CTX_PTR) { + cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else if (ctx == PHPREDIS_CTX_PTR + 1) { + return cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + ZEND_ASSERT(!"memory corruption?"); + } +} + PHP_REDIS_API void cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { diff --git a/cluster_library.h b/cluster_library.h index b075ca63fb..a253180c66 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -412,6 +412,8 @@ PHP_REDIS_API void cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * void *ctx); PHP_REDIS_API void cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, diff --git a/redis_cluster.c b/redis_cluster.c index cfd6199039..0da33b4a00 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1212,6 +1212,12 @@ PHP_METHOD(RedisCluster, hmset) { } /* }}} */ +/* {{{ proto bool RedisCluster::hrandfield(string key, [array $options]) */ +PHP_METHOD(RedisCluster, hrandfield) { + CLUSTER_PROCESS_CMD(hrandfield, cluster_hrandfield_resp, 1); +} +/* }}} */ + /* {{{ proto long RedisCluster::hdel(string key, string mem1, ... memN) */ PHP_METHOD(RedisCluster, hdel) { CLUSTER_PROCESS_CMD(hdel, cluster_long_resp, 0); diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 9b0a39892e..2edda599fb 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -482,6 +482,11 @@ public function hmset(string $key, array $key_values): RedisCluster|bool; */ public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|bool; + /** + * @see https://redis.io/commands/hrandfield + */ + public function hrandfield(string $key, array $options = null): RedisCluster|string|array; + /** * @see Redis::hset */ diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 0b5fea33e4..da96a2ed62 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ed8ae1edcec62f211d6ba70e78b3c0f087b2cbde */ + * Stub hash: 2225d10403eb94c52ad017310e783115b9ea869e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -398,6 +398,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hrandfield, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hset, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) @@ -1088,6 +1093,7 @@ ZEND_METHOD(RedisCluster, hlen); ZEND_METHOD(RedisCluster, hmget); ZEND_METHOD(RedisCluster, hmset); ZEND_METHOD(RedisCluster, hscan); +ZEND_METHOD(RedisCluster, hrandfield); ZEND_METHOD(RedisCluster, hset); ZEND_METHOD(RedisCluster, hsetnx); ZEND_METHOD(RedisCluster, hstrlen); @@ -1305,6 +1311,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hrandfield, arginfo_class_RedisCluster_hrandfield, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 794da9bd3d..ea5a4244a2 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ed8ae1edcec62f211d6ba70e78b3c0f087b2cbde */ + * Stub hash: 2225d10403eb94c52ad017310e783115b9ea869e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -350,6 +350,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hscan, 0, 0, 2) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hrandfield, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_hset arginfo_class_RedisCluster_hincrby #define arginfo_class_RedisCluster_hsetnx arginfo_class_RedisCluster_hincrby @@ -618,12 +623,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_smove, 0, 0, 3) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_sort, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_sort arginfo_class_RedisCluster_hrandfield -#define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_sort +#define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_hrandfield #define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster_lpop @@ -935,6 +937,7 @@ ZEND_METHOD(RedisCluster, hlen); ZEND_METHOD(RedisCluster, hmget); ZEND_METHOD(RedisCluster, hmset); ZEND_METHOD(RedisCluster, hscan); +ZEND_METHOD(RedisCluster, hrandfield); ZEND_METHOD(RedisCluster, hset); ZEND_METHOD(RedisCluster, hsetnx); ZEND_METHOD(RedisCluster, hstrlen); @@ -1152,6 +1155,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hrandfield, arginfo_class_RedisCluster_hrandfield, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 506182400d..ab3096104a 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -58,7 +58,6 @@ public function testzDiffStore() { return $this->markTestSkipped(); } public function testzMscore() { return $this->marktestSkipped(); } public function testZRandMember() { return $this->marktestSkipped(); } public function testCopy() { return $this->marktestSkipped(); } - public function testHRandField() { return $this->marktestSkipped(); } public function testConfig() { return $this->markTestSkipped(); } public function testFlushDB() { return $this->markTestSkipped(); } From 40a2c254e20cef48c2797d82d55a0e8b1c51651e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 1 Dec 2022 22:13:39 -0800 Subject: [PATCH 1730/1986] Implement COPY for RedisCluster * Refactor `redis_copy_cmd` to use the new argument parsing macros. * Add a handler in `RedisCluster`. See #1894 --- redis_cluster.c | 4 +++ redis_cluster.stub.php | 5 +++ redis_cluster_arginfo.h | 10 +++++- redis_cluster_legacy_arginfo.h | 10 +++++- redis_commands.c | 56 +++++++++++++++++++++------------- tests/RedisClusterTest.php | 1 - tests/RedisTest.php | 18 +++++------ 7 files changed, 70 insertions(+), 34 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 0da33b4a00..da64dbc43f 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -3142,5 +3142,9 @@ PHP_METHOD(RedisCluster, command) { CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); } +PHP_METHOD(RedisCluster, copy) { + CLUSTER_PROCESS_CMD(copy, cluster_1_resp, 0) +} + /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 2edda599fb..9d48fa5813 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -227,6 +227,11 @@ public function config(string|array $key_or_address, string $subcommand, mixed . */ public function dbsize(string|array $key_or_address): RedisCluster|int; + /** + * @see https://redis.io/commands/copy + */ + public function copy(string $src, string $dst, array $options = null): RedisCluster|bool; + /** * @see Redis::decr() */ diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index da96a2ed62..b2934a6f97 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2225d10403eb94c52ad017310e783115b9ea869e */ + * Stub hash: e6c2d8efa4150e1cb198470d8106e693661c1e4f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -165,6 +165,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_dbsize, 0 ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_copy, 0, 2, RedisCluster, MAY_BE_BOOL) + ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decr, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1") @@ -1042,6 +1048,7 @@ ZEND_METHOD(RedisCluster, cluster); ZEND_METHOD(RedisCluster, command); ZEND_METHOD(RedisCluster, config); ZEND_METHOD(RedisCluster, dbsize); +ZEND_METHOD(RedisCluster, copy); ZEND_METHOD(RedisCluster, decr); ZEND_METHOD(RedisCluster, decrby); ZEND_METHOD(RedisCluster, decrbyfloat); @@ -1260,6 +1267,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, command, arginfo_class_RedisCluster_command, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, config, arginfo_class_RedisCluster_config, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, copy, arginfo_class_RedisCluster_copy, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index ea5a4244a2..19a252ce0d 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2225d10403eb94c52ad017310e783115b9ea869e */ + * Stub hash: e6c2d8efa4150e1cb198470d8106e693661c1e4f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -151,6 +151,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_dbsize arginfo_class_RedisCluster_bgrewriteaof +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_copy, 0, 0, 2) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_decr, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, by) @@ -886,6 +892,7 @@ ZEND_METHOD(RedisCluster, cluster); ZEND_METHOD(RedisCluster, command); ZEND_METHOD(RedisCluster, config); ZEND_METHOD(RedisCluster, dbsize); +ZEND_METHOD(RedisCluster, copy); ZEND_METHOD(RedisCluster, decr); ZEND_METHOD(RedisCluster, decrby); ZEND_METHOD(RedisCluster, decrbyfloat); @@ -1104,6 +1111,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, command, arginfo_class_RedisCluster_command, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, config, arginfo_class_RedisCluster_config, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, copy, arginfo_class_RedisCluster_copy, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index c493528b8c..58039f0407 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -5345,44 +5345,56 @@ int redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { + zend_string *src = NULL, *dst = NULL; smart_string cmdstr = {0}; - char *src, *dst; - size_t src_len, dst_len; - zend_long db = -1; + HashTable *opts = NULL; zend_bool replace = 0; - zval *opts = NULL, *z_ele; zend_string *zkey; + zend_long db = -1; + short slot2; + zval *zv; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a", - &src, &src_len, &dst, &dst_len, &opts) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(src) + Z_PARAM_STR(dst) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(opts) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (opts != NULL) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { - if (zkey != NULL) { - ZVAL_DEREF(z_ele); - if (zend_string_equals_literal_ci(zkey, "db")) { - db = zval_get_long(z_ele); - } else if (zend_string_equals_literal_ci(zkey, "replace")) { - replace = zval_is_true(z_ele); - } + ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zkey, zv) { + if (zkey == NULL) + continue; + + ZVAL_DEREF(zv); + if (zend_string_equals_literal_ci(zkey, "db")) { + db = zval_get_long(zv); + } else if (zend_string_equals_literal_ci(zkey, "replace")) { + replace = zval_is_true(zv); } } ZEND_HASH_FOREACH_END(); } + if (slot && db != -1) { + php_error_docref(NULL, E_WARNING, "Cant copy to a specific DB in cluster mode"); + return FAILURE; + } + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (db > -1 ? 2 : 0) + replace, "COPY"); - redis_cmd_append_sstr(&cmdstr, src, src_len); - redis_cmd_append_sstr(&cmdstr, dst, dst_len); + redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); + redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); + + if (slot && *slot != slot2) { + php_error_docref(NULL, E_WARNING, "Keys must hash to the same slot!"); + efree(cmdstr.c); + return FAILURE; + } if (db > -1) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DB"); redis_cmd_append_sstr_long(&cmdstr, db); } - if (replace) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REPLACE"); - } + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, replace, "REPLACE"); *cmd = cmdstr.c; *cmd_len = cmdstr.len; diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index ab3096104a..4059340ff6 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -57,7 +57,6 @@ public function testzUnion() { return $this->markTestSkipped(); } public function testzDiffStore() { return $this->markTestSkipped(); } public function testzMscore() { return $this->marktestSkipped(); } public function testZRandMember() { return $this->marktestSkipped(); } - public function testCopy() { return $this->marktestSkipped(); } public function testConfig() { return $this->markTestSkipped(); } public function testFlushDB() { return $this->markTestSkipped(); } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 0affd1364b..5edd93e1c4 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7531,17 +7531,17 @@ public function testCopy() return; } - $this->redis->del('key2'); - $this->redis->set('key', 'foo'); - $this->assertTrue($this->redis->copy('key', 'key2')); - $this->assertEquals('foo', $this->redis->get('key2')); + $this->redis->del('{key}dst'); + $this->redis->set('{key}src', 'foo'); + $this->assertTrue($this->redis->copy('{key}src', '{key}dst')); + $this->assertEquals('foo', $this->redis->get('{key}dst')); - $this->redis->set('key', 'bar'); - $this->assertFalse($this->redis->copy('key', 'key2')); - $this->assertEquals('foo', $this->redis->get('key2')); + $this->redis->set('{key}src', 'bar'); + $this->assertFalse($this->redis->copy('{key}src', '{key}dst')); + $this->assertEquals('foo', $this->redis->get('{key}dst')); - $this->assertTrue($this->redis->copy('key', 'key2', ['replace' => true])); - $this->assertEquals('bar', $this->redis->get('key2')); + $this->assertTrue($this->redis->copy('{key}src', '{key}dst', ['replace' => true])); + $this->assertEquals('bar', $this->redis->get('{key}dst')); } public function testCommand() From 27900f39d208ceec8d1b710a48928641a5a1650a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 2 Dec 2022 08:52:57 -0800 Subject: [PATCH 1731/1986] Implement new ZSET commands for cluster * Implement `ZDIFF`, `ZINTER`, `ZUNION`, `ZMSCORE`, and `ZRANDMEMBER` for `RedisCluster`. * Refactor `ZUNIONSTORE` command and switch to using our centralized zset option parsing handler. See #1894 --- cluster_library.c | 51 +++++ cluster_library.h | 8 + library.c | 11 + library.h | 1 + redis_cluster.c | 26 +++ redis_cluster.stub.php | 30 +++ redis_cluster_arginfo.h | 40 +++- redis_cluster_legacy_arginfo.h | 36 +++- redis_commands.c | 371 +++++++++++++++------------------ tests/RedisClusterTest.php | 7 +- tests/RedisTest.php | 10 +- 11 files changed, 374 insertions(+), 217 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 67844e1a47..53ad689d95 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1808,6 +1808,30 @@ cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) } } +PHP_REDIS_API void +cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + if (ctx == NULL) { + cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else if (ctx == PHPREDIS_CTX_PTR) { + cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + ZEND_ASSERT(!"memory corruption?"); + } +} + +PHP_REDIS_API void +cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + if (ctx == NULL) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else if (ctx == PHPREDIS_CTX_PTR) { + cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else if (ctx == PHPREDIS_CTX_PTR + 1) { + cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + ZEND_ASSERT(!"memory corruption?"); + } +} + PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { @@ -2689,6 +2713,14 @@ cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, mbulk_resp_loop_zipdbl, NULL); } +PHP_REDIS_API void +cluster_mbulk_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx) +{ + cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + mbulk_resp_loop_dbl, ctx); +} + /* Associate multi bulk response (for HMGET really) */ PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, @@ -2702,6 +2734,25 @@ cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, * Various MULTI BULK reply callback functions */ +int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result, + long long count, void *ctx) +{ + char *line; + int line_len; + + while (count--) { + line = redis_sock_read(redis_sock, &line_len); + if (line != NULL) { + add_next_index_double(z_result, atof(line)); + efree(line); + } else { + add_next_index_bool(z_result, 0); + } + } + + return SUCCESS; +} + /* MULTI BULK response where we don't touch the values (e.g. KEYS) */ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, long long count, void *ctx) diff --git a/cluster_library.h b/cluster_library.h index a253180c66..362f5cf2a8 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -414,6 +414,10 @@ PHP_REDIS_API void cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster void *ctx); PHP_REDIS_API void cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); +PHP_REDIS_API void cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, @@ -460,6 +464,8 @@ PHP_REDIS_API void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_mbulk_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, void *ctx); PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, @@ -513,6 +519,8 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); +int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result, + long long count, void *ctx); int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, long long count, void *ctx); int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, diff --git a/library.c b/library.c index 79941e2adc..99c2a9f525 100644 --- a/library.c +++ b/library.c @@ -1108,6 +1108,17 @@ int redis_cmd_append_sstr_key_zstr(smart_string *dst, zend_string *key, RedisSoc return redis_cmd_append_sstr_key(dst, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); } +int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot) { + zend_string *key; + int res; + + key = zval_get_string(zv); + res = redis_cmd_append_sstr_key_zstr(dst, key, redis_sock, slot); + zend_string_release(key); + + return res; +} + /* Append an array key to a redis smart string command. This function * handles the boilerplate conditionals around string or integer keys */ int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx) diff --git a/library.h b/library.h index acd046d78a..d8736c039d 100644 --- a/library.h +++ b/library.h @@ -51,6 +51,7 @@ int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr); int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock); int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_key_zstr(smart_string *str, zend_string *key, RedisSock *redis_sock, short *slot); +int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx); PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...); diff --git a/redis_cluster.c b/redis_cluster.c index da64dbc43f..62d71cb13d 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1108,6 +1108,10 @@ PHP_METHOD(RedisCluster, zscore) { } /* }}} */ +PHP_METHOD(RedisCluster, zmscore) { + CLUSTER_PROCESS_KW_CMD("ZMSCORE", redis_key_varval_cmd, cluster_mbulk_dbl_resp, 1); +} + /* {{{ proto long RedisCluster::zadd(string key,double score,string mem, ...) */ PHP_METHOD(RedisCluster, zadd) { CLUSTER_PROCESS_CMD(zadd, cluster_long_resp, 0); @@ -1494,6 +1498,28 @@ PHP_METHOD(RedisCluster, zunionstore) { } /* }}} */ +PHP_METHOD(RedisCluster, zdiff) { + CLUSTER_PROCESS_CMD(zdiff, cluster_zdiff_resp, 1); +} + +PHP_METHOD(RedisCluster, zdiffstore) { + CLUSTER_PROCESS_CMD(zdiffstore, cluster_long_resp, 0); +} + +PHP_METHOD(RedisCluster, zinter) { + CLUSTER_PROCESS_KW_CMD("ZUNION", redis_zinterunion_cmd, cluster_zdiff_resp, 1); +} + +PHP_METHOD(RedisCluster, zunion) { + CLUSTER_PROCESS_KW_CMD("ZINTER", redis_zinterunion_cmd, cluster_zdiff_resp, 1); +} + +/* {{{ proto array RedisCluster::zrandmember(string key, array options) */ +PHP_METHOD(RedisCluster, zrandmember) { + CLUSTER_PROCESS_CMD(zrandmember, cluster_zrandmember_resp, 1); +} + +/* }}} */ /* {{{ proto RedisCluster::zinterstore(string dst, array keys, [array weights, * string agg]) */ PHP_METHOD(RedisCluster, zinterstore) { diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 9d48fa5813..7a9504a122 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -1088,6 +1088,11 @@ public function zrange(string $key, mixed $start, mixed $end, array|bool|null $o public function zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null): RedisCluster|int|false; + /** + * @see https://redis.io/commands/zRandMember + */ + public function zrandmember(string $key, array $options = null): RedisCluster|string|array; + /** * @see Redis::zrangebylex */ @@ -1153,10 +1158,35 @@ public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int */ public function zscore(string $key, mixed $member): RedisCluster|float|false; + /** + * @see https://redis.io/commands/zMscore + */ + public function zmscore(string $key, mixed $member, mixed ...$other_members): Redis|array|false; + /** * @see Redis::zunionstore */ public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): RedisCluster|int|false; + + /** + * @see https://redis.io/commands/zinter + */ + public function zinter(array $keys, ?array $weights = null, ?array $options = null): RedisCluster|array|false; + + /** + * @see https://redis.io/commands/zdiffstore + */ + public function zdiffstore(string $dst, array $keys): RedisCluster|int|false; + + /** + * @see https://redis.io/commands/zunion + */ + public function zunion(array $keys, ?array $weights = null, ?array $options = null): RedisCluster|array|false; + + /** + * @see https://redis.io/commands/zdiff + */ + public function zdiff(array $keys, array $options = null): RedisCluster|array|false; } class RedisClusterException extends RuntimeException {} diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index b2934a6f97..5c7c112814 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e6c2d8efa4150e1cb198470d8106e693661c1e4f */ + * Stub hash: eabecbc2e536faca2a9fcba3c99ad0aeba9721b4 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -948,6 +948,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangesto ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_zrandmember arginfo_class_RedisCluster_hrandfield + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangebylex, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) @@ -1005,6 +1007,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zscore, 0 ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmscore, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zunionstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) @@ -1012,6 +1020,24 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zunionsto ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zinter, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zdiffstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_zunion arginfo_class_RedisCluster_zinter + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zdiff, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") +ZEND_END_ARG_INFO() + ZEND_METHOD(RedisCluster, __construct); ZEND_METHOD(RedisCluster, _compress); @@ -1215,6 +1241,7 @@ ZEND_METHOD(RedisCluster, zpopmax); ZEND_METHOD(RedisCluster, zpopmin); ZEND_METHOD(RedisCluster, zrange); ZEND_METHOD(RedisCluster, zrangestore); +ZEND_METHOD(RedisCluster, zrandmember); ZEND_METHOD(RedisCluster, zrangebylex); ZEND_METHOD(RedisCluster, zrangebyscore); ZEND_METHOD(RedisCluster, zrank); @@ -1228,7 +1255,12 @@ ZEND_METHOD(RedisCluster, zrevrangebyscore); ZEND_METHOD(RedisCluster, zrevrank); ZEND_METHOD(RedisCluster, zscan); ZEND_METHOD(RedisCluster, zscore); +ZEND_METHOD(RedisCluster, zmscore); ZEND_METHOD(RedisCluster, zunionstore); +ZEND_METHOD(RedisCluster, zinter); +ZEND_METHOD(RedisCluster, zdiffstore); +ZEND_METHOD(RedisCluster, zunion); +ZEND_METHOD(RedisCluster, zdiff); static const zend_function_entry class_RedisCluster_methods[] = { @@ -1434,6 +1466,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangestore, arginfo_class_RedisCluster_zrangestore, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zrandmember, arginfo_class_RedisCluster_zrandmember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC) @@ -1447,7 +1480,12 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, zrevrank, arginfo_class_RedisCluster_zrevrank, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zscan, arginfo_class_RedisCluster_zscan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zscore, arginfo_class_RedisCluster_zscore, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zmscore, arginfo_class_RedisCluster_zmscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zunionstore, arginfo_class_RedisCluster_zunionstore, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zinter, arginfo_class_RedisCluster_zinter, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zdiffstore, arginfo_class_RedisCluster_zdiffstore, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zunion, arginfo_class_RedisCluster_zunion, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zdiff, arginfo_class_RedisCluster_zdiff, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 19a252ce0d..583ad4339f 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e6c2d8efa4150e1cb198470d8106e693661c1e4f */ + * Stub hash: eabecbc2e536faca2a9fcba3c99ad0aeba9721b4 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -817,6 +817,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangestore, 0, 0, 4) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_zrandmember arginfo_class_RedisCluster_hrandfield + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, min) @@ -854,8 +856,28 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zscore arginfo_class_RedisCluster_hexists +#define arginfo_class_RedisCluster_zmscore arginfo_class_RedisCluster_geohash + #define arginfo_class_RedisCluster_zunionstore arginfo_class_RedisCluster_zinterstore +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zinter, 0, 0, 1) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, weights) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zdiffstore, 0, 0, 2) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, keys) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_zunion arginfo_class_RedisCluster_zinter + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zdiff, 0, 0, 1) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + ZEND_METHOD(RedisCluster, __construct); ZEND_METHOD(RedisCluster, _compress); @@ -1059,6 +1081,7 @@ ZEND_METHOD(RedisCluster, zpopmax); ZEND_METHOD(RedisCluster, zpopmin); ZEND_METHOD(RedisCluster, zrange); ZEND_METHOD(RedisCluster, zrangestore); +ZEND_METHOD(RedisCluster, zrandmember); ZEND_METHOD(RedisCluster, zrangebylex); ZEND_METHOD(RedisCluster, zrangebyscore); ZEND_METHOD(RedisCluster, zrank); @@ -1072,7 +1095,12 @@ ZEND_METHOD(RedisCluster, zrevrangebyscore); ZEND_METHOD(RedisCluster, zrevrank); ZEND_METHOD(RedisCluster, zscan); ZEND_METHOD(RedisCluster, zscore); +ZEND_METHOD(RedisCluster, zmscore); ZEND_METHOD(RedisCluster, zunionstore); +ZEND_METHOD(RedisCluster, zinter); +ZEND_METHOD(RedisCluster, zdiffstore); +ZEND_METHOD(RedisCluster, zunion); +ZEND_METHOD(RedisCluster, zdiff); static const zend_function_entry class_RedisCluster_methods[] = { @@ -1278,6 +1306,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangestore, arginfo_class_RedisCluster_zrangestore, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zrandmember, arginfo_class_RedisCluster_zrandmember, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC) @@ -1291,7 +1320,12 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, zrevrank, arginfo_class_RedisCluster_zrevrank, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zscan, arginfo_class_RedisCluster_zscan, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zscore, arginfo_class_RedisCluster_zscore, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zmscore, arginfo_class_RedisCluster_zmscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zunionstore, arginfo_class_RedisCluster_zunionstore, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zinter, arginfo_class_RedisCluster_zinter, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zdiffstore, arginfo_class_RedisCluster_zdiffstore, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zunion, arginfo_class_RedisCluster_zunion, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zdiff, arginfo_class_RedisCluster_zdiff, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/redis_commands.c b/redis_commands.c index 58039f0407..d14b093243 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -72,25 +72,27 @@ typedef struct redisRestoreOptions { zend_long freq; } redisRestoreOptions; -#define REDIS_ZRANGE_HAS_DST_KEY (1 << 0) -#define REDIS_ZRANGE_HAS_WITHSCORES (1 << 1) -#define REDIS_ZRANGE_HAS_BY_LEX_SCORE (1 << 2) -#define REDIS_ZRANGE_HAS_REV (1 << 3) -#define REDIS_ZRANGE_HAS_LIMIT (1 << 4) -#define REDIS_ZRANGE_INT_RANGE (1 << 5) +#define REDIS_ZCMD_HAS_DST_KEY (1 << 0) +#define REDIS_ZCMD_HAS_WITHSCORES (1 << 1) +#define REDIS_ZCMD_HAS_BY_LEX_SCORE (1 << 2) +#define REDIS_ZCMD_HAS_REV (1 << 3) +#define REDIS_ZCMD_HAS_LIMIT (1 << 4) +#define REDIS_ZCMD_INT_RANGE (1 << 5) +#define REDIS_ZCMD_HAS_AGGREGATE (1 << 6) /* ZRANGE, ZRANGEBYSCORE, ZRANGESTORE options */ -typedef struct redisZrangeOptions { +typedef struct redisZcmdOptions { zend_bool withscores; zend_bool byscore; zend_bool bylex; zend_bool rev; + zend_string *aggregate; struct { zend_bool enabled; zend_long offset; zend_long count; } limit; -} redisZrangeOptions; +} redisZcmdOptions; /* Local passthrough macro for command construction. Given that these methods * are generic (so they work whether the caller is Redis or RedisCluster) we @@ -632,7 +634,7 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, return cmdstr.len; } -void redis_get_zrange_options(redisZrangeOptions *dst, zval *src, int flags) { +void redis_get_zcmd_options(redisZcmdOptions *dst, zval *src, int flags) { zval *zv, *zoff, *zcnt; zend_string *key; @@ -644,7 +646,7 @@ void redis_get_zrange_options(redisZrangeOptions *dst, zval *src, int flags) { return; if (Z_TYPE_P(src) != IS_ARRAY) { - if (Z_TYPE_P(src) == IS_TRUE && (flags & REDIS_ZRANGE_HAS_WITHSCORES)) + if (Z_TYPE_P(src) == IS_TRUE && (flags & REDIS_ZCMD_HAS_WITHSCORES)) dst->withscores = 1; return; } @@ -653,9 +655,9 @@ void redis_get_zrange_options(redisZrangeOptions *dst, zval *src, int flags) { ZVAL_DEREF(zv); if (key) { - if ((flags & REDIS_ZRANGE_HAS_WITHSCORES) && zend_string_equals_literal_ci(key, "WITHSCORES")) + if ((flags & REDIS_ZCMD_HAS_WITHSCORES) && zend_string_equals_literal_ci(key, "WITHSCORES")) dst->withscores = zval_is_true(zv); - else if ((flags & REDIS_ZRANGE_HAS_LIMIT) && zend_string_equals_literal_ci(key, "LIMIT") && + else if ((flags & REDIS_ZCMD_HAS_LIMIT) && zend_string_equals_literal_ci(key, "LIMIT") && Z_TYPE_P(zv) == IS_ARRAY) { if ((zoff = zend_hash_index_find(Z_ARRVAL_P(zv), 0)) != NULL && @@ -667,17 +669,28 @@ void redis_get_zrange_options(redisZrangeOptions *dst, zval *src, int flags) { } else { php_error_docref(NULL, E_WARNING, "LIMIT offset and count must be an array with twe elements"); } + } else if ((flags & REDIS_ZCMD_HAS_AGGREGATE && zend_string_equals_literal_ci(key, "AGGREGATE")) && + Z_TYPE_P(zv) == IS_STRING) + { + if (Z_TYPE_P(zv) != IS_STRING || (!zend_string_equals_literal_ci(Z_STR_P(zv), "SUM") && + !zend_string_equals_literal_ci(Z_STR_P(zv), "MIN") && + !zend_string_equals_literal_ci(Z_STR_P(zv), "MAX"))) + { + php_error_docref(NULL, E_WARNING, "Valid AGGREGATE options are 'SUM', 'MIN', or 'MAX'"); + } else { + dst->aggregate = Z_STR_P(zv); + } } } else if (Z_TYPE_P(zv) == IS_STRING) { key = Z_STR_P(zv); - if ((flags & REDIS_ZRANGE_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYSCORE")) + if ((flags & REDIS_ZCMD_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYSCORE")) dst->byscore = 1, dst->bylex = 0; - else if ((flags & REDIS_ZRANGE_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYLEX")) + else if ((flags & REDIS_ZCMD_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYLEX")) dst->bylex = 1, dst->byscore = 0; - else if ((flags & REDIS_ZRANGE_HAS_REV) && zend_string_equals_literal_ci(key, "REV")) + else if ((flags & REDIS_ZCMD_HAS_REV) && zend_string_equals_literal_ci(key, "REV")) dst->rev = 1; - else if ((flags & REDIS_ZRANGE_HAS_WITHSCORES && zend_string_equals_literal_ci(key, "WITHSCORES"))) + else if ((flags & REDIS_ZCMD_HAS_WITHSCORES && zend_string_equals_literal_ci(key, "WITHSCORES"))) dst->withscores = 1; } } ZEND_HASH_FOREACH_END(); @@ -690,32 +703,42 @@ void redis_get_zrange_options(redisZrangeOptions *dst, zval *src, int flags) { // + ZREVRANGEBYSCORE key max min [LIMIT offset count] [WITHSCORES] // - ZRANGEBYLEX key min max [LIMIT offset count] // - ZREVRANGEBYLEX key max min [LIMIT offset count] -static int redis_get_zrange_cmd_flags(const char *kw) { +// - ZDIFF [WITHSCORES] +// - ZUNION [WITHSCORES] [AGGREGATE X] +// - ZINTER [WITHSCORES] [AGGREGATE X] +static int redis_get_zcmd_flags(const char *kw) { size_t len = strlen(kw); if (REDIS_STRICMP_STATIC(kw, len, "ZRANGESTORE")) { - return REDIS_ZRANGE_HAS_DST_KEY | - REDIS_ZRANGE_HAS_WITHSCORES | - REDIS_ZRANGE_HAS_BY_LEX_SCORE | - REDIS_ZRANGE_HAS_REV | - REDIS_ZRANGE_HAS_LIMIT; + return REDIS_ZCMD_HAS_DST_KEY | + REDIS_ZCMD_HAS_WITHSCORES | + REDIS_ZCMD_HAS_BY_LEX_SCORE | + REDIS_ZCMD_HAS_REV | + REDIS_ZCMD_HAS_LIMIT; } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGE")) { - return REDIS_ZRANGE_HAS_WITHSCORES | - REDIS_ZRANGE_HAS_BY_LEX_SCORE | - REDIS_ZRANGE_HAS_REV | - REDIS_ZRANGE_HAS_LIMIT; + return REDIS_ZCMD_HAS_WITHSCORES | + REDIS_ZCMD_HAS_BY_LEX_SCORE | + REDIS_ZCMD_HAS_REV | + REDIS_ZCMD_HAS_LIMIT; } else if (REDIS_STRICMP_STATIC(kw, len, "ZREVRANGE")) { - return REDIS_ZRANGE_HAS_WITHSCORES | - REDIS_ZRANGE_INT_RANGE; + return REDIS_ZCMD_HAS_WITHSCORES | + REDIS_ZCMD_INT_RANGE; } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGEBYSCORE") || REDIS_STRICMP_STATIC(kw, len, "ZREVRANGEBYSCORE")) { - return REDIS_ZRANGE_HAS_LIMIT | - REDIS_ZRANGE_HAS_WITHSCORES; + return REDIS_ZCMD_HAS_LIMIT | + REDIS_ZCMD_HAS_WITHSCORES; } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGEBYLEX") || REDIS_STRICMP_STATIC(kw, len, "ZREVRANGEBYLEX")) { - return REDIS_ZRANGE_HAS_LIMIT; + return REDIS_ZCMD_HAS_LIMIT; + } else if (REDIS_STRICMP_STATIC(kw, len, "ZDIFF")) { + return REDIS_ZCMD_HAS_WITHSCORES; + } else if (REDIS_STRICMP_STATIC(kw, len, "ZINTER") || + REDIS_STRICMP_STATIC(kw, len, "ZUNION")) + { + return REDIS_ZCMD_HAS_WITHSCORES | + REDIS_ZCMD_HAS_AGGREGATE; } /* Reaching this line means a compile-time error */ @@ -734,22 +757,22 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { zend_string *dst = NULL, *src = NULL, *sstart = NULL, *send = NULL; - struct redisZrangeOptions opt; zend_long start = 0, end = 0; smart_string cmdstr = {0}; zval *zoptions = NULL; + redisZcmdOptions opt; int min_argc, flags; short slot2; - flags = redis_get_zrange_cmd_flags(kw); + flags = redis_get_zcmd_flags(kw); - min_argc = 3 + (flags & REDIS_ZRANGE_HAS_DST_KEY); + min_argc = 3 + (flags & REDIS_ZCMD_HAS_DST_KEY); ZEND_PARSE_PARAMETERS_START(min_argc, min_argc + 1) - if (flags & REDIS_ZRANGE_HAS_DST_KEY) { + if (flags & REDIS_ZCMD_HAS_DST_KEY) { Z_PARAM_STR(dst) } Z_PARAM_STR(src) - if (flags & REDIS_ZRANGE_INT_RANGE) { + if (flags & REDIS_ZCMD_INT_RANGE) { Z_PARAM_LONG(start) Z_PARAM_LONG(end) } else { @@ -760,10 +783,10 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ZVAL_OR_NULL(zoptions) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - redis_get_zrange_options(&opt, zoptions, flags); + redis_get_zcmd_options(&opt, zoptions, flags); if (opt.bylex) { - ZEND_ASSERT(!(flags & REDIS_ZRANGE_INT_RANGE)); + ZEND_ASSERT(!(flags & REDIS_ZCMD_INT_RANGE)); if (!validate_zlex_arg_zstr(sstart) || !validate_zlex_arg_zstr(send)) { php_error_docref(NULL, E_WARNING, "Legographical args must start with '[' or '(' or be '+' or '-'"); return FAILURE; @@ -774,18 +797,18 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, !!opt.rev + !!opt.withscores + (opt.limit.enabled ? 3 : 0), kw, strlen(kw)); - if (flags & REDIS_ZRANGE_HAS_DST_KEY) + if (flags & REDIS_ZCMD_HAS_DST_KEY) redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, &slot2); /* Protect the user from crossslot errors */ - if ((flags & REDIS_ZRANGE_HAS_DST_KEY) && slot && *slot != slot2) { + if ((flags & REDIS_ZCMD_HAS_DST_KEY) && slot && *slot != slot2) { php_error_docref(NULL, E_WARNING, "destination and source keys must map to the same slot"); efree(cmdstr.c); return FAILURE; } - if (flags & REDIS_ZRANGE_INT_RANGE) { + if (flags & REDIS_ZCMD_INT_RANGE) { redis_cmd_append_sstr_long(&cmdstr, start); redis_cmd_append_sstr_long(&cmdstr, end); } else { @@ -988,11 +1011,11 @@ int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - int numkeys; + zval *z_keys, *z_opts = NULL, *z_key; + redisZcmdOptions opts = {0}; smart_string cmdstr = {0}; - zval *z_keys, *z_opts = NULL, *z_ele; - zend_bool withscores = 0; - zend_string *zkey; + int numkeys, flags; + short s2 = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a", &z_keys, &z_opts) == FAILURE) @@ -1004,26 +1027,26 @@ redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - if (z_opts != NULL) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { - if (zkey != NULL) { - ZVAL_DEREF(z_ele); - if (zend_string_equals_literal_ci(zkey, "withscores")) { - withscores = zval_is_true(z_ele); - } - } - } ZEND_HASH_FOREACH_END(); - } + flags = redis_get_zcmd_flags("ZDIFF"); + redis_get_zcmd_options(&opts, z_opts, flags); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + numkeys + withscores, "ZDIFF"); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + numkeys + opts.withscores, "ZDIFF"); redis_cmd_append_sstr_long(&cmdstr, numkeys); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_key) { + ZVAL_DEREF(z_key); + redis_cmd_append_sstr_key_zval(&cmdstr, z_key, redis_sock, slot); + + if (slot && s2 && s2 != *slot) { + php_error_docref(NULL, E_WARNING, "Not all keys map to the same slot!"); + efree(cmdstr.c); + return FAILURE; + } + + if (slot) s2 = *slot; } ZEND_HASH_FOREACH_END(); - if (withscores) { + if (opts.withscores) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); *ctx = PHPREDIS_CTX_PTR; } @@ -1158,11 +1181,11 @@ redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - int numkeys; - smart_string cmdstr = {0}; zval *z_keys, *z_weights = NULL, *z_opts = NULL, *z_ele; - zend_string *aggregate = NULL, *zkey; - zend_bool withscores = 0; + redisZcmdOptions opts = {0}; + smart_string cmdstr = {0}; + int numkeys, flags; + short s2 = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a!a", &z_keys, &z_weights, &z_opts) == FAILURE) @@ -1174,43 +1197,28 @@ redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - if (z_weights) { - if (zend_hash_num_elements(Z_ARRVAL_P(z_weights)) != numkeys) { - php_error_docref(NULL, E_WARNING, - "WEIGHTS and keys array should be the same size!"); - return FAILURE; - } + if (z_weights && zend_hash_num_elements(Z_ARRVAL_P(z_weights)) != numkeys) { + php_error_docref(NULL, E_WARNING, "WEIGHTS and keys array should be the same size!"); + return FAILURE; } - if (z_opts != NULL) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { - if (zkey != NULL) { - ZVAL_DEREF(z_ele); - if (zend_string_equals_literal_ci(zkey, "aggregate")) { - aggregate = zval_get_string(z_ele); - if (!zend_string_equals_literal_ci(aggregate, "sum") && - !zend_string_equals_literal_ci(aggregate, "min") && - !zend_string_equals_literal_ci(aggregate, "max") - ) { - php_error_docref(NULL, E_WARNING, - "Invalid AGGREGATE option provided!"); - zend_string_release(aggregate); - return FAILURE; - } - } else if (zend_string_equals_literal_ci(zkey, "withscores")) { - withscores = zval_is_true(z_ele); - } - } - } ZEND_HASH_FOREACH_END(); - } + flags = redis_get_zcmd_flags(kw); + redis_get_zcmd_options(&opts, z_opts, flags); - redis_cmd_init_sstr(&cmdstr, 1 + numkeys + (z_weights ? 1 + numkeys : 0) + (aggregate ? 2 : 0) + withscores, kw, strlen(kw)); + redis_cmd_init_sstr(&cmdstr, 1 + numkeys + (z_weights ? 1 + numkeys : 0) + (opts.aggregate ? 2 : 0) + opts.withscores, kw, strlen(kw)); redis_cmd_append_sstr_long(&cmdstr, numkeys); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) { ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); + redis_cmd_append_sstr_key_zval(&cmdstr, z_ele, redis_sock, slot); + if (slot) { + if (s2 && s2 != *slot) { + php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot"); + efree(cmdstr.c); + return FAILURE; + } + s2 = *slot; + } } ZEND_HASH_FOREACH_END(); if (z_weights) { @@ -1218,20 +1226,18 @@ redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_weights), z_ele) { ZVAL_DEREF(z_ele); if (redis_cmd_append_sstr_score(&cmdstr, z_ele) == FAILURE) { - if (aggregate) zend_string_release(aggregate); efree(cmdstr.c); return FAILURE; } } ZEND_HASH_FOREACH_END(); } - if (aggregate) { + if (opts.aggregate) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AGGREGATE"); - redis_cmd_append_sstr_zstr(&cmdstr, aggregate); - zend_string_release(aggregate); + redis_cmd_append_sstr_zstr(&cmdstr, opts.aggregate); } - if (withscores) { + if (opts.withscores) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); *ctx = PHPREDIS_CTX_PTR; } @@ -1245,29 +1251,34 @@ int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *dst; - size_t dst_len; - int numkeys; - zval *z_keys, *z_ele; smart_string cmdstr = {0}; + zend_string *dst = NULL; + HashTable *keys = NULL; + zend_ulong nkeys; + short s2 = 0; + zval *zkey; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", - &dst, &dst_len, &z_keys) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(dst) + Z_PARAM_ARRAY_HT(keys) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) { + nkeys = zend_hash_num_elements(keys); + if (nkeys == 0) return FAILURE; - } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + numkeys, "ZDIFFSTORE"); - redis_cmd_append_sstr(&cmdstr, dst, dst_len); - redis_cmd_append_sstr_long(&cmdstr, numkeys); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + nkeys, "ZDIFFSTORE"); + redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); + redis_cmd_append_sstr_long(&cmdstr, nkeys); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); + ZEND_HASH_FOREACH_VAL(keys, zkey) { + ZVAL_DEREF(zkey); + redis_cmd_append_sstr_key_zval(&cmdstr, zkey, redis_sock, slot ? &s2 : NULL); + if (slot && *slot != s2) { + php_error_docref(NULL, E_WARNING, "All keys must hash to the same slot"); + efree(cmdstr.c); + return FAILURE; + } } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; @@ -1281,120 +1292,72 @@ redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key, *agg_op=NULL; - int key_free, argc = 2, keys_count; - size_t key_len, agg_op_len = 0; - zval *z_keys, *z_weights=NULL, *z_ele; - HashTable *ht_keys, *ht_weights=NULL; + HashTable *keys = NULL, *weights = NULL; smart_string cmdstr = {0}; + zend_string *dst = NULL; + zend_string *agg = NULL; + zend_ulong nkeys; + zval *zv = NULL; + short s2 = 0; - // Parse args - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a!s", &key, - &key_len, &z_keys, &z_weights, &agg_op, - &agg_op_len) == FAILURE) - { - return FAILURE; - } - - // Grab our keys - ht_keys = Z_ARRVAL_P(z_keys); + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STR(dst) + Z_PARAM_ARRAY_HT(keys) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(weights) + Z_PARAM_STR_OR_NULL(agg) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - // Nothing to do if there aren't any - if ((keys_count = zend_hash_num_elements(ht_keys)) == 0) { + nkeys = zend_hash_num_elements(keys); + if (nkeys == 0) return FAILURE; - } else { - argc += keys_count; - } - // Handle WEIGHTS - if (z_weights != NULL) { - ht_weights = Z_ARRVAL_P(z_weights); - if (zend_hash_num_elements(ht_weights) != keys_count) { - php_error_docref(NULL, E_WARNING, - "WEIGHTS and keys array should be the same size!"); - return FAILURE; - } - - // "WEIGHTS" + key count - argc += keys_count + 1; + if (weights != NULL && zend_hash_num_elements(weights) != nkeys) { + php_error_docref(NULL, E_WARNING, "WEIGHTS and keys array must be the same size!"); + return FAILURE; } // AGGREGATE option - if (agg_op_len != 0) { - if (strncasecmp(agg_op, "SUM", sizeof("SUM")) && - strncasecmp(agg_op, "MIN", sizeof("MIN")) && - strncasecmp(agg_op, "MAX", sizeof("MAX"))) - { - php_error_docref(NULL, E_WARNING, - "Invalid AGGREGATE option provided!"); - return FAILURE; - } - - // "AGGREGATE" + type - argc += 2; + if (agg != NULL && (!zend_string_equals_literal_ci(agg, "SUM") && + !zend_string_equals_literal_ci(agg, "MIN") && + !zend_string_equals_literal_ci(agg, "MAX"))) + { + php_error_docref(NULL, E_WARNING, "AGGREGATE option must be 'SUM', 'MIN', or 'MAX'"); + return FAILURE; } - // Prefix key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Start building our command - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - redis_cmd_append_sstr(&cmdstr, key, key_len); - redis_cmd_append_sstr_int(&cmdstr, keys_count); + redis_cmd_init_sstr(&cmdstr, 2 + nkeys + (weights ? 1 + nkeys : 0) + (agg ? 2 : 0), kw, strlen(kw)); + redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); + redis_cmd_append_sstr_int(&cmdstr, nkeys); - // Set our slot, free the key if we prefixed it - CMD_SET_SLOT(slot,key,key_len); - if (key_free) efree(key); - - // Process input keys - ZEND_HASH_FOREACH_VAL(ht_keys, z_ele) { - zend_string *zstr = zval_get_string(z_ele); - char *key = ZSTR_VAL(zstr); - size_t key_len = ZSTR_LEN(zstr); - - // Prefix key if necissary - int key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // If we're in Cluster mode, verify the slot is the same - if (slot && *slot != cluster_hash_key(key,key_len)) { - php_error_docref(NULL, E_WARNING, - "All keys don't hash to the same slot!"); + ZEND_HASH_FOREACH_VAL(keys, zv) { + ZVAL_DEREF(zv); + redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot ? &s2 : NULL); + if (slot && s2 != *slot) { + php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot!"); efree(cmdstr.c); - zend_string_release(zstr); - if (key_free) efree(key); return FAILURE; } - - // Append this input set - redis_cmd_append_sstr(&cmdstr, key, key_len); - - // Cleanup - zend_string_release(zstr); - if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); - // Weights - if (ht_weights != NULL) { - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("WEIGHTS")); - - // Process our weights - ZEND_HASH_FOREACH_VAL(ht_weights, z_ele) { - ZVAL_DEREF(z_ele); - if (redis_cmd_append_sstr_score(&cmdstr, z_ele) == FAILURE) { + if (weights) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WEIGHTS"); + ZEND_HASH_FOREACH_VAL(weights, zv) { + ZVAL_DEREF(zv); + if (redis_cmd_append_sstr_score(&cmdstr, zv) == FAILURE) { efree(cmdstr.c); return FAILURE; } } ZEND_HASH_FOREACH_END(); } - // AGGREGATE - if (agg_op_len != 0) { + if (agg) { redis_cmd_append_sstr(&cmdstr, ZEND_STRL("AGGREGATE")); - redis_cmd_append_sstr(&cmdstr, agg_op, agg_op_len); + redis_cmd_append_sstr_zstr(&cmdstr, agg); } // Push out values - *cmd = cmdstr.c; + *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 4059340ff6..b45e13b2e4 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -51,12 +51,7 @@ public function testReset() { return $this->markTestSkipped(); } public function testInvalidAuthArgs() { return $this->markTestSkipped(); } public function testScanErrors() { return $this->markTestSkipped(); } - public function testzDiff() { return $this->markTestSkipped(); } - public function testzInter() { return $this->markTestSkipped(); } - public function testzUnion() { return $this->markTestSkipped(); } - public function testzDiffStore() { return $this->markTestSkipped(); } - public function testzMscore() { return $this->marktestSkipped(); } - public function testZRandMember() { return $this->marktestSkipped(); } + /* These 'directed node' commands work differently in RedisCluster */ public function testConfig() { return $this->markTestSkipped(); } public function testFlushDB() { return $this->markTestSkipped(); } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 5edd93e1c4..223c81c927 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2708,7 +2708,7 @@ public function testZX() { // test weighted zUnion $this->redis->del('{zset}Z'); - $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [1, 1])); + $this->assertEquals(4, $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [1, 1])); $this->assertTrue(['val0', 'val1', 'val2', 'val3'] === $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->zRemRangeByScore('{zset}Z', 0, 10); @@ -2998,12 +2998,12 @@ public function testzDiffStore() return; } - $this->redis->del('key'); + $this->redis->del('{zkey}src'); foreach (range('a', 'c') as $c) { - $this->redis->zAdd('key', 1, $c); + $this->redis->zAdd('{zkey}src', 1, $c); } - $this->assertEquals(3, $this->redis->zDiffStore('key2', ['key'])); - $this->assertEquals(['a', 'b', 'c'], $this->redis->zRange('key2', 0, -1)); + $this->assertEquals(3, $this->redis->zDiffStore('{zkey}dst', ['{zkey}src'])); + $this->assertEquals(['a', 'b', 'c'], $this->redis->zRange('{zkey}dst', 0, -1)); } public function testzMscore() From 5bcaaa59c5aa893254f6ba9e3c0eff292ecaf209 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 3 Dec 2022 20:17:28 -0800 Subject: [PATCH 1732/1986] Badge links are broken. [skip ci] --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 8bb0396d70..c9103627ce 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,7 @@ PhpRedis will always be free and open source software, but if you or your compan The best way to support the project is through [GitHub sponsors](https://github.com/sponsors/michael-grunder). Many of the reward tiers grant access to our [slack channel](https://phpredis.slack.com) where [myself](https://github.com/michael-grunder) and [Pavlo](https://github.com/yatsukhnenko) are regularly available to answer questions. Additionally this will allow you to provide feedback on which fixes and new features to prioritize. -You can also make a one-time contribution with one of the links below. - -[![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5) -[![Bitcoin](https://en.cryptobadges.io/badge/micro/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG)](https://en.cryptobadges.io/donate/1FXkYHBo5uoaztxFbajiPfbnkgKCbF3ykG) -[![Ethereum](https://en.cryptobadges.io/badge/micro/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1)](https://en.cryptobadges.io/donate/0x43D54E32357B96f68dFF0a6B46976d014Bd603E1) +You can also make a one-time contribution with [![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5) ## Sponsors Audiomack.com From 79c9d2241f782f67e58a886e3fd75b51e11e921c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 3 Dec 2022 20:06:14 -0800 Subject: [PATCH 1733/1986] Refactor rawCommand and WAIT --- redis.c | 76 ++---------------------------------------------- redis_commands.c | 28 ++++++++++++++++++ redis_commands.h | 6 ++-- 3 files changed, 35 insertions(+), 75 deletions(-) diff --git a/redis.c b/redis.c index b8d4130f62..1cb1f5b345 100644 --- a/redis.c +++ b/redis.c @@ -2523,40 +2523,7 @@ PHP_METHOD(Redis, slowlog) { /* {{{ proto Redis::wait(int num_slaves, int ms) }}} */ PHP_METHOD(Redis, wait) { - zval *object; - RedisSock *redis_sock; - zend_long num_slaves, timeout; - char *cmd; - int cmd_len; - - /* Make sure arguments are valid */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll", - &object, redis_ce, &num_slaves, &timeout) - ==FAILURE) - { - RETURN_FALSE; - } - - /* Don't even send this to Redis if our args are negative */ - if(num_slaves < 0 || timeout < 0) { - RETURN_FALSE; - } - - /* Grab our socket */ - if ((redis_sock = redis_sock_get(object, 0)) == NULL) { - RETURN_FALSE; - } - - // Construct the command - cmd_len = REDIS_SPPRINTF(&cmd, "WAIT", "ll", num_slaves, timeout); - - /* Kick it off */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - if (IS_ATOMIC(redis_sock)) { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, - NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); + REDIS_PROCESS_KW_CMD("WAIT", redis_long_long_cmd, redis_long_response); } /* @@ -2588,46 +2555,9 @@ PHP_METHOD(Redis, evalsha_ro) { REDIS_PROCESS_KW_CMD("EVALSHA_RO", redis_eval_cmd, redis_read_raw_variant_reply); } -/* {{{ proto status Redis::script('flush') - * {{{ proto status Redis::script('kill') - * {{{ proto string Redis::script('load', lua_script) - * {{{ proto int Reids::script('exists', script_sha1 [, script_sha2, ...]) - */ +/* {{{ public function script($args...): mixed }}} */ PHP_METHOD(Redis, script) { - zval *z_args; - RedisSock *redis_sock; - smart_string cmd = {0}; - int argc = ZEND_NUM_ARGS(); - - /* Attempt to grab our socket */ - if (argc < 1 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL) { - RETURN_FALSE; - } - - /* Allocate an array big enough to store our arguments */ - z_args = ecalloc(argc, sizeof(zval)); - - /* Make sure we can grab our arguments, we have a string directive */ - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || - redis_build_script_cmd(&cmd, argc, z_args) == NULL - ) { - efree(z_args); - RETURN_FALSE; - } - - /* Free our allocated arguments */ - efree(z_args); - - // Kick off our request - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - if (IS_ATOMIC(redis_sock)) { - if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) - { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_read_variant_reply); + REDIS_PROCESS_KW_CMD("SCRIPT", redis_vararg_cmd, redis_read_variant_reply); } /* {{{ proto DUMP key */ diff --git a/redis_commands.c b/redis_commands.c index d14b093243..cc7a4d2c2f 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1814,6 +1814,34 @@ int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, VAL_TYPE_STRINGS, cmd, cmd_len, slot, ctx); } +/* Generic function that takes one or more non-serialized arguments */ +int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + smart_string cmdstr = {0}; + zval *argv = NULL; + zend_string *arg; + int argc = 0; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('*', argv, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + redis_cmd_init_sstr(&cmdstr, ZEND_NUM_ARGS(), kw, strlen(kw)); + + for (uint32_t i = 0; i < argc; i++) { + arg = zval_get_string(&argv[i]); + redis_cmd_append_sstr_zstr(&cmdstr, arg); + zend_string_release(arg); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* Generic function that takes a variable number of keys, with an optional * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ diff --git a/redis_commands.h b/redis_commands.h index f96f12a9ec..15d07031ae 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -337,8 +337,10 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx); + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); From b6cf6361dd3b3afcdb2de7f9e8d47585f482d2ec Mon Sep 17 00:00:00 2001 From: zorro-fr24 Date: Fri, 18 Nov 2022 19:05:20 +0100 Subject: [PATCH 1734/1986] Add cluster support for strict sessions and lazy write * Add ini setting redis.session.early_refresh to allow for session TTL updates on session start ( requires redis server version 6.2 or greater ) * Enable cluster session support for strict mode sessions ( via PS_VALIDATE_SID_FUNC ) * Cluster sessions used to write on every session, now we only write if the session has been modified. * Send EXPIRE instead of SETEX if sessioh has not been changed * If early refresh is enabled use GETEX for initial session read * When strict sessions are enabled, check whether the session exists first, validate sid and regenerate if necessary --- cluster.md | 9 +++ redis.c | 1 + redis_session.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++-- redis_session.h | 3 + 4 files changed, 185 insertions(+), 4 deletions(-) diff --git a/cluster.md b/cluster.md index 96d0fa4320..3fd9fb16a6 100644 --- a/cluster.md +++ b/cluster.md @@ -193,3 +193,12 @@ The save path for cluster based session storage takes the form of a PHP GET requ * _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing). * _auth (string, empty by default)_: The password used to authenticate with the server prior to sending commands. * _stream (array)_: ssl/tls stream context options. + +### redis.session.early_refresh +Under normal operation, the client will refresh the session's expiry ttl whenever the session is closed. However, we can save this additional round-trip by updating the ttl when the session is opened instead ( This means that sessions that have not been modified will not send further commands to the server ). + +To enable, set the following INI variable: +```ini +redis.session.early_refresh = 1 +``` +Note: This is disabled by default since it may significantly reduce the session lifetime for long-running scripts. Redis server version 6.2+ required. \ No newline at end of file diff --git a/redis.c b/redis.c index 1cb1f5b345..c625d81774 100644 --- a/redis.c +++ b/redis.c @@ -110,6 +110,7 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_retries", "100", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.early_refresh", "0", PHP_INI_ALL, NULL) PHP_INI_END() static const zend_module_dep redis_deps[] = { diff --git a/redis_session.c b/redis_session.c index 192f890cf0..204f651d12 100644 --- a/redis_session.c +++ b/redis_session.c @@ -60,7 +60,7 @@ ps_module ps_mod_redis = { }; ps_module ps_mod_redis_cluster = { - PS_MOD(rediscluster) + PS_MOD_UPDATE_TIMESTAMP(rediscluster) }; typedef struct { @@ -982,6 +982,166 @@ PS_OPEN_FUNC(rediscluster) { return FAILURE; } +/* {{{ PS_CREATE_SID_FUNC + */ +PS_CREATE_SID_FUNC(rediscluster) +{ + redisCluster *c = PS_GET_MOD_DATA(); + clusterReply *reply; + char *cmd, *skey; + zend_string *sid; + int cmdlen, skeylen; + int retries = 3; + short slot; + + if (!c) { + return php_session_create_id(NULL); + } + + if (INI_INT("session.use_strict_mode") == 0) { + return php_session_create_id((void **) &c); + } + + while (retries-- > 0) { + sid = php_session_create_id((void **) &c); + + /* Create session key if it doesn't already exist */ + skey = cluster_session_key(c, ZSTR_VAL(sid), ZSTR_LEN(sid), &skeylen, &slot); + cmdlen = redis_spprintf(NULL, NULL, &cmd, "SET", "ssssd", skey, + skeylen, "", 0, "NX", 2, "EX", 2, session_gc_maxlifetime()); + + efree(skey); + + /* Attempt to kick off our command */ + c->readonly = 0; + if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { + php_error_docref(NULL, E_NOTICE, "Redis connection not available"); + efree(cmd); + zend_string_release(sid); + return php_session_create_id(NULL);; + } + + efree(cmd); + + /* Attempt to read reply */ + reply = cluster_read_resp(c, 1); + + if (!reply || c->err) { + php_error_docref(NULL, E_NOTICE, "Unable to read redis response"); + } else if (reply->len > 0) { + cluster_free_reply(reply, 1); + break; + } else { + php_error_docref(NULL, E_NOTICE, "Redis sid collision on %s, retrying %d time(s)", sid->val, retries); + } + + if (reply) { + cluster_free_reply(reply, 1); + } + + zend_string_release(sid); + sid = NULL; + } + + return sid; +} +/* }}} */ + +/* {{{ PS_VALIDATE_SID_FUNC + */ +PS_VALIDATE_SID_FUNC(rediscluster) +{ + redisCluster *c = PS_GET_MOD_DATA(); + clusterReply *reply; + char *cmd, *skey; + int cmdlen, skeylen; + int res = FAILURE; + short slot; + + /* Check key is valid and whether it already exists */ + if (php_session_valid_key(ZSTR_VAL(key)) == FAILURE) { + php_error_docref(NULL, E_NOTICE, "Invalid session key: %s", ZSTR_VAL(key)); + return FAILURE; + } + + skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); + cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXISTS", "s", skey, skeylen); + efree(skey); + + /* We send to master, to ensure consistency */ + c->readonly = 0; + if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { + php_error_docref(NULL, E_NOTICE, "Redis connection not available"); + efree(cmd); + return FAILURE; + } + + efree(cmd); + + /* Attempt to read reply */ + reply = cluster_read_resp(c, 0); + + if (!reply || c->err) { + php_error_docref(NULL, E_NOTICE, "Unable to read redis response"); + res = FAILURE; + } else if (reply->integer == 1) { + res = SUCCESS; + } + + /* Clean up */ + if (reply) { + cluster_free_reply(reply, 1); + } + + return res; +} +/* }}} */ + +/* {{{ PS_UPDATE_TIMESTAMP_FUNC + */ +PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { + redisCluster *c = PS_GET_MOD_DATA(); + clusterReply *reply; + char *cmd, *skey; + int cmdlen, skeylen; + short slot; + + /* No need to update the session timestamp if we've already done so */ + if (INI_INT("redis.session.early_refresh")) { + return SUCCESS; + } + + /* Set up command and slot info */ + skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); + cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXPIRE", "sd", skey, + skeylen, session_gc_maxlifetime()); + efree(skey); + + /* Attempt to send EXPIRE command */ + c->readonly = 0; + if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { + php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry"); + efree(cmd); + return FAILURE; + } + + /* Clean up our command */ + efree(cmd); + + /* Attempt to read reply */ + reply = cluster_read_resp(c, 0); + if (!reply || c->err) { + if (reply) cluster_free_reply(reply, 1); + return FAILURE; + } + + /* Clean up */ + cluster_free_reply(reply, 1); + + return SUCCESS; +} +/* }}} */ + /* {{{ PS_READ_FUNC */ PS_READ_FUNC(rediscluster) { @@ -994,11 +1154,19 @@ PS_READ_FUNC(rediscluster) { /* Set up our command and slot information */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen); + /* Update the session ttl if early refresh is enabled */ + if (INI_INT("redis.session.early_refresh")) { + cmdlen = redis_spprintf(NULL, NULL, &cmd, "GETEX", "ssd", skey, + skeylen, "EX", 2, session_gc_maxlifetime()); + c->readonly = 0; + } else { + cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen); + c->readonly = 1; + } + efree(skey); /* Attempt to kick off our command */ - c->readonly = 1; if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { efree(cmd); return FAILURE; @@ -1126,4 +1294,4 @@ PS_GC_FUNC(rediscluster) { #endif -/* vim: set tabstop=4 expandtab: */ +/* vim: set tabstop=4 expandtab: */ \ No newline at end of file diff --git a/redis_session.h b/redis_session.h index 1529c05d4e..d72e620892 100644 --- a/redis_session.h +++ b/redis_session.h @@ -20,6 +20,9 @@ PS_READ_FUNC(rediscluster); PS_WRITE_FUNC(rediscluster); PS_DESTROY_FUNC(rediscluster); PS_GC_FUNC(rediscluster); +PS_CREATE_SID_FUNC(rediscluster); +PS_VALIDATE_SID_FUNC(rediscluster); +PS_UPDATE_TIMESTAMP_FUNC(rediscluster); #endif #endif From 86f15ccaa1c45f1971d0c9938659032463d16228 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 5 Dec 2022 16:13:50 -0800 Subject: [PATCH 1735/1986] Refactor SELECT command. * Use our common command handler logic for SELECT. * Shift updating `redis_sock->dbNumber` from the command itself to the reply handler, so we aren't erroneously pointing to a non-existent database before the command succeeds. --- library.c | 11 +++++++++++ library.h | 1 + redis.c | 27 +-------------------------- redis_commands.c | 18 ++++++++++++++++++ redis_commands.h | 3 +++ 5 files changed, 34 insertions(+), 26 deletions(-) diff --git a/library.c b/library.c index 99c2a9f525..f2b830893a 100644 --- a/library.c +++ b/library.c @@ -1529,6 +1529,17 @@ redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z return res; } +PHP_REDIS_API int +redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, + void *ctx) +{ + if (redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL) < 0) + return FAILURE; + + redis_sock->dbNumber = (long)(uintptr_t)ctx; + return SUCCESS; +} + PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, diff --git a/library.h b/library.h index d8736c039d..93680575c9 100644 --- a/library.h +++ b/library.h @@ -195,6 +195,7 @@ PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, ch PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); /* Helper methods to get configuration values from a HashTable. */ diff --git a/redis.c b/redis.c index c625d81774..4058dd1609 100644 --- a/redis.c +++ b/redis.c @@ -1632,32 +1632,7 @@ PHP_METHOD(Redis, info) { /* {{{ proto bool Redis::select(long dbNumber) */ PHP_METHOD(Redis, select) { - - zval *object; - RedisSock *redis_sock; - - char *cmd; - int cmd_len; - zend_long dbNumber; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", - &object, redis_ce, &dbNumber) == FAILURE) { - RETURN_FALSE; - } - - if (dbNumber < 0 || (redis_sock = redis_sock_get(object, 0)) == NULL) { - RETURN_FALSE; - } - - redis_sock->dbNumber = dbNumber; - cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", dbNumber); - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - if (IS_ATOMIC(redis_sock)) { - redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_boolean_response); + REDIS_PROCESS_CMD(select, redis_select_response); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index cc7a4d2c2f..15170ead0f 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3664,6 +3664,24 @@ redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zend_long db = 0; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(db) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (db < 0 || db > INT_MAX) + return FAILURE; + + *ctx = (void*)(uintptr_t)db; + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SELECT", "d", db); + + return SUCCESS; +} + /* SRANDMEMBER */ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx, diff --git a/redis_commands.h b/redis_commands.h index 15d07031ae..0bf9eac6ab 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -266,6 +266,9 @@ int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx, short *have_count); +int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From f62363c2a32832d15242d077f652066b4105917c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 4 Dec 2022 20:04:29 -0800 Subject: [PATCH 1736/1986] Refactor SRANDMEMBER command. --- cluster_library.c | 11 +++++++++++ cluster_library.h | 2 ++ library.c | 12 ++++++++++++ library.h | 2 ++ redis.c | 31 +------------------------------ redis_cluster.c | 32 +------------------------------- redis_commands.c | 35 +++++++++++++++++------------------ redis_commands.h | 2 +- 8 files changed, 47 insertions(+), 80 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 53ad689d95..f411f89602 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1832,6 +1832,17 @@ cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ct } } +PHP_REDIS_API void +cluster_srandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + if (ctx == NULL) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else if (ctx == PHPREDIS_CTX_PTR) { + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + ZEND_ASSERT(!"memory corruption?"); + } +} + PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { diff --git a/cluster_library.h b/cluster_library.h index 362f5cf2a8..d81a2e4ac1 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -418,6 +418,8 @@ PHP_REDIS_API void cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster void *ctx); PHP_REDIS_API void cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_srandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, diff --git a/library.c b/library.c index f2b830893a..26bf1d6c55 100644 --- a/library.c +++ b/library.c @@ -1225,6 +1225,18 @@ redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); } +PHP_REDIS_API int +redis_srandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { + FailableResultCallback cb; + + /* Whether or not we have a COUNT argument */ + ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + + cb = ctx ? redis_sock_read_multibulk_reply : redis_string_response; + + return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); +} + PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; diff --git a/library.h b/library.h index 93680575c9..0a0c0ad8ee 100644 --- a/library.h +++ b/library.h @@ -190,9 +190,11 @@ PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_srandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx); + PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/redis.c b/redis.c index 4058dd1609..539620412e 100644 --- a/redis.c +++ b/redis.c @@ -1249,36 +1249,7 @@ PHP_METHOD(Redis, sPop) /* {{{ proto string Redis::sRandMember(string key [int count]) */ PHP_METHOD(Redis, sRandMember) { - char *cmd; - int cmd_len; - short have_count; - RedisSock *redis_sock; - - // Grab our socket, validate call - if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || - redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - &cmd, &cmd_len, NULL, NULL, &have_count) == FAILURE) - { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - if(have_count) { - if (IS_ATOMIC(redis_sock)) { - if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) - { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); - } else { - if (IS_ATOMIC(redis_sock)) { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); - } + REDIS_PROCESS_CMD(srandmember, redis_srandmember_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 62d71cb13d..886ba36269 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -869,37 +869,7 @@ PHP_METHOD(RedisCluster, spop) { /* {{{ proto string|array RedisCluster::srandmember(string key, [long count]) */ PHP_METHOD(RedisCluster, srandmember) { - redisCluster *c = GET_CONTEXT(); - cluster_cb cb; - char *cmd; int cmd_len; short slot; - short have_count; - - /* Treat as readonly */ - c->readonly = CLUSTER_IS_ATOMIC(c); - - if (redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, - &cmd, &cmd_len, &slot, NULL, &have_count) - == FAILURE) - { - RETURN_FALSE; - } - - if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) { - efree(cmd); - RETURN_FALSE; - } - - // Clean up command - efree(cmd); - - cb = have_count ? cluster_mbulk_resp : cluster_bulk_resp; - if (CLUSTER_IS_ATOMIC(c)) { - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); - RETURN_ZVAL(getThis(), 1, 0); - } + CLUSTER_PROCESS_CMD(srandmember, cluster_srandmember_resp, 1); } /* {{{ proto string RedisCluster::strlen(string key) */ diff --git a/redis_commands.c b/redis_commands.c index 15170ead0f..aaedfe57d3 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3684,28 +3684,27 @@ int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* SRANDMEMBER */ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx, - short *have_count) + char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key; - size_t key_len; - zend_long count; + uint32_t argc = ZEND_NUM_ARGS(); + smart_string cmdstr = {0}; + zend_long count = 0; + zend_string *key; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, - &count) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(count) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - // Set our have count flag - *have_count = ZEND_NUM_ARGS() == 2; + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, ZEND_NUM_ARGS(), "SRANDMEMBER"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + if (argc == 2) + redis_cmd_append_sstr_long(&cmdstr, count); - // Two args means we have the optional COUNT - if (*have_count) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SRANDMEMBER", "kl", key, key_len, count); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SRANDMEMBER", "k", key, key_len); - } + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + *ctx = argc == 2 ? PHPREDIS_CTX_PTR : NULL; return SUCCESS; } diff --git a/redis_commands.h b/redis_commands.h index 0bf9eac6ab..81e2c26908 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -264,7 +264,7 @@ int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx, short *have_count); + char **cmd, int *cmd_len, short *slot, void **ctx); int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From e90557c832cbeb87338851854f0c81c3bf5ab1df Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 4 Dec 2022 21:00:17 -0800 Subject: [PATCH 1737/1986] Silence CodeQL false positive. --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index aaedfe57d3..be61007c2d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3688,8 +3688,8 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { uint32_t argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; + zend_string *key = NULL; zend_long count = 0; - zend_string *key; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(key) From acb5db76617fafa8317e8805132224b2ca905c58 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 6 Dec 2022 11:46:53 -0800 Subject: [PATCH 1738/1986] Refactor OBJECT command. --- cluster_library.c | 11 +++++++++++ cluster_library.h | 2 ++ library.c | 11 +++++++++++ library.h | 1 + redis.c | 30 +----------------------------- redis_cluster.c | 24 +----------------------- redis_commands.c | 41 +++++++++++++++++++---------------------- redis_commands.h | 3 +-- 8 files changed, 47 insertions(+), 76 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index f411f89602..520a228f0c 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1843,6 +1843,17 @@ cluster_srandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ct } } +PHP_REDIS_API void +cluster_object_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); + + if (ctx == PHPREDIS_CTX_PTR) { + cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } +} + PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { diff --git a/cluster_library.h b/cluster_library.h index d81a2e4ac1..0ed588c654 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -410,6 +410,8 @@ PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster void *ctx); PHP_REDIS_API void cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_object_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHP_REDIS_API void cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, diff --git a/library.c b/library.c index 26bf1d6c55..302cf8c899 100644 --- a/library.c +++ b/library.c @@ -1477,6 +1477,17 @@ redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ } } +PHP_REDIS_API int +redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { + ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); + + if (ctx == PHPREDIS_CTX_PTR) { + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } else { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + } +} + PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx) diff --git a/library.h b/library.h index 0a0c0ad8ee..c5246d0ed0 100644 --- a/library.h +++ b/library.h @@ -192,6 +192,7 @@ PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *re PHP_REDIS_API int redis_srandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx); diff --git a/redis.c b/redis.c index 539620412e..186d327002 100644 --- a/redis.c +++ b/redis.c @@ -2396,35 +2396,7 @@ PHP_METHOD(Redis, replicaof) { /* {{{ proto string Redis::object(key) */ PHP_METHOD(Redis, object) { - RedisSock *redis_sock; - char *cmd; int cmd_len; - REDIS_REPLY_TYPE rtype; - - if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) { - RETURN_FALSE; - } - - if(redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &rtype, - &cmd, &cmd_len, NULL, NULL)==FAILURE) - { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - - if(rtype == TYPE_INT) { - if (IS_ATOMIC(redis_sock)) { - redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_long_response); - } else { - if (IS_ATOMIC(redis_sock)) { - redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_string_response); - } + REDIS_PROCESS_CMD(object, redis_object_response); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index 886ba36269..fa9cb0796c 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1591,29 +1591,7 @@ PHP_METHOD(RedisCluster, sort_ro) { /* {{{ proto RedisCluster::object(string subcmd, string key) */ PHP_METHOD(RedisCluster, object) { - redisCluster *c = GET_CONTEXT(); - char *cmd; int cmd_len; short slot; - REDIS_REPLY_TYPE rtype; - - if (redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &rtype, - &cmd, &cmd_len, &slot, NULL) == FAILURE) - { - RETURN_FALSE; - } - - if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) { - efree(cmd); - RETURN_FALSE; - } - - efree(cmd); - - // Use the correct response type - if (rtype == TYPE_INT) { - cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } + CLUSTER_PROCESS_CMD(object, cluster_object_resp, 1); } /* {{{ proto null RedisCluster::subscribe(array chans, callable cb) */ diff --git a/redis_commands.c b/redis_commands.c index be61007c2d..ecdce3d01b 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -4102,36 +4102,33 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* OBJECT */ int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - REDIS_REPLY_TYPE *rtype, char **cmd, int *cmd_len, - short *slot, void **ctx) + char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key, *subcmd; - size_t key_len, subcmd_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &subcmd, - &subcmd_len, &key, &key_len) == FAILURE) - { - return FAILURE; - } + zend_string *subcmd = NULL, *key = NULL; + smart_string cmdstr = {0}; - // Format our command - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "OBJECT", "sk", subcmd, subcmd_len, key, key_len); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(subcmd) + Z_PARAM_STR(key) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - // Push the reply type to our caller - if (subcmd_len == 8 && (!strncasecmp(subcmd,"refcount",8) || - !strncasecmp(subcmd,"idletime",8))) + if (zend_string_equals_literal_ci(subcmd, "REFCOUNT") || + zend_string_equals_literal_ci(subcmd, "IDLETIME")) { - *rtype = TYPE_INT; - } else if (subcmd_len == 8 && !strncasecmp(subcmd, "encoding", 8)) { - *rtype = TYPE_BULK; + *ctx = PHPREDIS_CTX_PTR; + } else if (zend_string_equals_literal_ci(subcmd, "ENCODING")) { + *ctx = PHPREDIS_CTX_PTR + 1; } else { - php_error_docref(NULL, E_WARNING, - "Invalid subcommand sent to OBJECT"); - efree(*cmd); + php_error_docref(NULL, E_WARNING, "Invalid subcommand sent to OBJECT"); return FAILURE; } - // Success + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "OBJECT"); + redis_cmd_append_sstr_zstr(&cmdstr, subcmd); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; return SUCCESS; } diff --git a/redis_commands.h b/redis_commands.h index 81e2c26908..5e3e6a3f08 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -279,8 +279,7 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - REDIS_REPLY_TYPE *rtype, char **cmd, int *cmd_len, short *slot, - void **ctx); + char **cmd, int *cmd_len, short *slot, void **ctx); int redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From 3efa59cbc1b8d4cb25740600683e718c93e0c7bb Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 3 Dec 2022 21:05:12 -0800 Subject: [PATCH 1739/1986] Refactor gen_varkey_cmd Use the new argument parsing API which lets us avoid an allocation and switch to redis_cmd_append_sstr_key_zval to abstract away key prefixing logic. --- redis_commands.c | 145 ++++++++++++++--------------------------------- 1 file changed, 43 insertions(+), 102 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index ecdce3d01b..a5f013a21a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1846,141 +1846,82 @@ int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, int kw_len, int min_argc, int has_timeout, + char *kw, int kw_len, int min_argc, zend_bool has_timeout, char **cmd, int *cmd_len, short *slot) { - zval *z_args, *z_ele, ztimeout = {0}; - HashTable *ht_arr; - char *key; - int key_free, i, tail; - size_t key_len; - int single_array = 0, argc = ZEND_NUM_ARGS(); + zval *argv = NULL, ztimeout = {0}, *zv; smart_string cmdstr = {0}; short kslot = -1; - zend_string *zstr; + int single_array; + int argc = 0; - if (argc < min_argc) { - zend_wrong_param_count(); - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(min_argc, -1) + Z_PARAM_VARIADIC('*', argv, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - // Allocate args - z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - return FAILURE; - } + single_array = argc == 1 + !!has_timeout && Z_TYPE(argv[0]) == IS_ARRAY; - // Handle our "single array" case - if (has_timeout == 0) { - single_array = argc==1 && Z_TYPE(z_args[0]) == IS_ARRAY; - } else { - single_array = argc==2 && Z_TYPE(z_args[0]) == IS_ARRAY && - (Z_TYPE(z_args[1]) == IS_LONG || Z_TYPE(z_args[1]) == IS_DOUBLE); + if (has_timeout) { if (single_array) - ZVAL_COPY_VALUE(&ztimeout, &z_args[1]); + ZVAL_COPY_VALUE(&ztimeout, &argv[1]); + else + ZVAL_COPY_VALUE(&ztimeout, &argv[argc - 1]); + + if (Z_TYPE(ztimeout) != IS_LONG && Z_TYPE(ztimeout) != IS_DOUBLE) { + php_error_docref(NULL, E_WARNING, "Timeout must be a long or double"); + return FAILURE; + } } // If we're running a single array, rework args if (single_array) { - ht_arr = Z_ARRVAL(z_args[0]); - argc = zend_hash_num_elements(ht_arr); - if (has_timeout) argc++; - efree(z_args); - z_args = NULL; + /* Need at least one argument */ + argc = zend_hash_num_elements(Z_ARRVAL(argv[0])); + if (argc == 0) + return FAILURE; - /* If the array is empty, we can simply abort */ - if (argc == 0) return FAILURE; + if (has_timeout) argc++; } // Begin construction of our command redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); if (single_array) { - ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - zstr = zval_get_string(z_ele); - key = ZSTR_VAL(zstr); - key_len = ZSTR_LEN(zstr); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Protect against CROSSLOT errors + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(argv[0]), zv) { + redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); if (slot) { - if (kslot == -1) { - kslot = cluster_hash_key(key, key_len); - } else if (cluster_hash_key(key,key_len)!=kslot) { - zend_string_release(zstr); - if (key_free) efree(key); - php_error_docref(NULL, E_WARNING, - "Not all keys hash to the same slot!"); - return FAILURE; - } + if (kslot != -1 && *slot != kslot) + goto cross_slot; + kslot = *slot; } - - // Append this key, free it if we prefixed - redis_cmd_append_sstr(&cmdstr, key, key_len); - zend_string_release(zstr); - if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); - if (Z_TYPE(ztimeout) == IS_LONG) { - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(ztimeout)); - } else if (Z_TYPE(ztimeout) == IS_DOUBLE) { - redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL(ztimeout)); - } } else { - if (has_timeout) { - zend_uchar type = Z_TYPE(z_args[argc - 1]); - if (type == IS_LONG || type == IS_DOUBLE) { - ZVAL_COPY_VALUE(&ztimeout, &z_args[argc - 1]); - } else { - php_error_docref(NULL, E_ERROR, "Timeout value must be a long or double"); - efree(z_args); - return FAILURE; - } - } - tail = has_timeout ? argc-1 : argc; - for(i = 0; i < tail; i++) { - zstr = zval_get_string(&z_args[i]); - key = ZSTR_VAL(zstr); - key_len = ZSTR_LEN(zstr); - - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - /* Protect against CROSSSLOT errors if we've got a slot */ + for(uint32_t i = 0; i < argc - !!has_timeout; i++) { + redis_cmd_append_sstr_key_zval(&cmdstr, &argv[i], redis_sock, slot); if (slot) { - if ( kslot == -1) { - kslot = cluster_hash_key(key, key_len); - } else if (cluster_hash_key(key,key_len)!=kslot) { - php_error_docref(NULL, E_WARNING, - "Not all keys hash to the same slot"); - zend_string_release(zstr); - if (key_free) efree(key); - efree(z_args); - return FAILURE; - } + if (kslot != -1 && *slot != kslot) + goto cross_slot; + kslot = *slot; } - - // Append this key - redis_cmd_append_sstr(&cmdstr, key, key_len); - zend_string_release(zstr); - if (key_free) efree(key); - } - - if (Z_TYPE(ztimeout) == IS_DOUBLE) { - redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL(z_args[tail])); - } else if (Z_TYPE(ztimeout) == IS_LONG) { - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(z_args[tail])); } + } - // Cleanup args - efree(z_args); + if (Z_TYPE(ztimeout) == IS_DOUBLE) { + redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL(ztimeout)); + } else if (Z_TYPE(ztimeout) == IS_LONG) { + redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(ztimeout)); } // Push out parameters - if (slot) *slot = kslot; *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; + +cross_slot: + efree(cmdstr.c); + php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); + return FAILURE; } int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, @@ -2075,7 +2016,7 @@ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, - strlen(kw), 2, 2, cmd, cmd_len, slot); + strlen(kw), 2, 1, cmd, cmd_len, slot); } /* From 486f131f4497a92c7365838ddd12b080590a14fa Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 3 Dec 2022 22:43:41 -0800 Subject: [PATCH 1740/1986] Clearer logic for checking if we have a single array. --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index a5f013a21a..741f28dc41 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1859,7 +1859,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - single_array = argc == 1 + !!has_timeout && Z_TYPE(argv[0]) == IS_ARRAY; + single_array = Z_TYPE(argv[0]) == IS_ARRAY && argc == has_timeout ? 2 : 1; if (has_timeout) { if (single_array) From 4264940e52d932f3bd49cf98dc5659d5a6c62205 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 3 Dec 2022 22:58:56 -0800 Subject: [PATCH 1741/1986] Parentheses. --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 741f28dc41..a3580830fe 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1859,7 +1859,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - single_array = Z_TYPE(argv[0]) == IS_ARRAY && argc == has_timeout ? 2 : 1; + single_array = Z_TYPE(argv[0]) == IS_ARRAY && argc == (has_timeout ? 2 : 1); if (has_timeout) { if (single_array) From 121d330c161b0267ad55ff292f1221aa51ab6065 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 6 Dec 2022 10:59:51 -0800 Subject: [PATCH 1742/1986] Remove min_argc indirection all together. Given how `gen_varkey_cmd` is called, we can actually derive the `min_argc` from whether or not it has a timeout. --- redis_commands.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index a3580830fe..401cc5a336 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1846,20 +1846,23 @@ int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, int kw_len, int min_argc, zend_bool has_timeout, + char *kw, int kw_len, zend_bool has_timeout, char **cmd, int *cmd_len, short *slot) { zval *argv = NULL, ztimeout = {0}, *zv; smart_string cmdstr = {0}; + uint32_t min_argc; short kslot = -1; int single_array; int argc = 0; + min_argc = has_timeout ? 2 : 1; + ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - single_array = Z_TYPE(argv[0]) == IS_ARRAY && argc == (has_timeout ? 2 : 1); + single_array = argc == min_argc && Z_TYPE(argv[0]) == IS_ARRAY; if (has_timeout) { if (single_array) @@ -2016,7 +2019,7 @@ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, - strlen(kw), 2, 1, cmd, cmd_len, slot); + strlen(kw), 1, cmd, cmd_len, slot); } /* @@ -4814,7 +4817,7 @@ int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - kw, strlen(kw), 1, 0, cmd, cmd_len, slot); + kw, strlen(kw), 0, cmd, cmd_len, slot); } static int From 8cb6dd17fe43edcd3a0f8f16b7786c8d95c7dc93 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 6 Dec 2022 13:07:22 -0800 Subject: [PATCH 1743/1986] Refactor MGET command. --- redis.c | 48 ++---------------------------------------------- redis_commands.c | 31 +++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ 3 files changed, 36 insertions(+), 46 deletions(-) diff --git a/redis.c b/redis.c index 186d327002..23b8b68352 100644 --- a/redis.c +++ b/redis.c @@ -894,52 +894,8 @@ PHP_METHOD(Redis, decrBy){ /* {{{ proto array Redis::mget(array keys) */ -PHP_METHOD(Redis, mget) -{ - zval *object, *z_args, *z_ele; - HashTable *hash; - RedisSock *redis_sock; - smart_string cmd = {0}; - int arg_count; - - /* Make sure we have proper arguments */ - if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", - &object, redis_ce, &z_args) == FAILURE) { - RETURN_FALSE; - } - - /* We'll need the socket */ - if ((redis_sock = redis_sock_get(object, 0)) == NULL) { - RETURN_FALSE; - } - - /* Grab our array */ - hash = Z_ARRVAL_P(z_args); - - /* We don't need to do anything if there aren't any keys */ - if((arg_count = zend_hash_num_elements(hash)) == 0) { - RETURN_FALSE; - } - - /* Build our command header */ - redis_cmd_init_sstr(&cmd, arg_count, "MGET", 4); - - /* Iterate through and grab our keys */ - ZEND_HASH_FOREACH_VAL(hash, z_ele) { - zend_string *zstr = zval_get_string(z_ele); - redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, NULL); - zend_string_release(zstr); - } ZEND_HASH_FOREACH_END(); - - /* Kick off our command */ - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - if (IS_ATOMIC(redis_sock)) { - if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); +PHP_METHOD(Redis, mget) { + REDIS_PROCESS_CMD(mget, redis_sock_read_multibulk_reply); } /* {{{ proto boolean Redis::exists(string $key, string ...$more_keys) diff --git a/redis_commands.c b/redis_commands.c index 401cc5a336..ba85a741d1 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2273,6 +2273,37 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* MGET */ +int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + HashTable *keys = NULL; + zval *zkey; + + /* RedisCluster has a custom MGET implementation */ + ZEND_ASSERT(slot == NULL); + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(keys) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(keys) == 0) + return FAILURE; + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, zend_hash_num_elements(keys), "MGET"); + + ZEND_HASH_FOREACH_VAL(keys, zkey) { + ZVAL_DEREF(zkey); + redis_cmd_append_sstr_key_zval(&cmdstr, zkey, redis_sock, slot); + } ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + int redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 5e3e6a3f08..bf83b8590a 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -212,6 +212,9 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); From 3574ef0867f048d49fd4da786511f517a8c50fb8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 6 Dec 2022 12:27:26 -0800 Subject: [PATCH 1744/1986] Refactor INFO and SCRIPT commands. Use `gen_varkey_cmd` for INFO and SCRIPT. --- redis.c | 29 ++--------------------------- redis_commands.c | 23 +++++++++++++++++++---- redis_commands.h | 9 ++++++--- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/redis.c b/redis.c index 23b8b68352..1d468d6cbe 100644 --- a/redis.c +++ b/redis.c @@ -1528,32 +1528,7 @@ PHP_METHOD(Redis, pttl) { /* {{{ proto array Redis::info() */ PHP_METHOD(Redis, info) { - smart_string cmdstr = {0}; - RedisSock *redis_sock; - zend_string *section; - zval *args = NULL; - int i, argc = 0; - - ZEND_PARSE_PARAMETERS_START(0, -1) - Z_PARAM_VARIADIC('+', args, argc) - ZEND_PARSE_PARAMETERS_END(); - - if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) - RETURN_FALSE; - - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "INFO"); - for (i = 0; i < argc; i++) { - section = zval_get_string(&args[i]); - redis_cmd_append_sstr_zstr(&cmdstr, section); - zend_string_release(section); - } - - REDIS_PROCESS_REQUEST(redis_sock, cmdstr.c, cmdstr.len); - if (IS_ATOMIC(redis_sock)) { - redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, - NULL); - } - REDIS_PROCESS_RESPONSE(redis_info_response); + REDIS_PROCESS_CMD(info, redis_info_response); } /* }}} */ @@ -2432,7 +2407,7 @@ PHP_METHOD(Redis, evalsha_ro) { /* {{{ public function script($args...): mixed }}} */ PHP_METHOD(Redis, script) { - REDIS_PROCESS_KW_CMD("SCRIPT", redis_vararg_cmd, redis_read_variant_reply); + REDIS_PROCESS_CMD(script, redis_read_variant_reply); } /* {{{ proto DUMP key */ diff --git a/redis_commands.c b/redis_commands.c index ba85a741d1..0303007d11 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1815,16 +1815,17 @@ int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Generic function that takes one or more non-serialized arguments */ -int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +static int +gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + uint32_t min_argc, char *kw, char **cmd, int *cmd_len, + short *slot, void **ctx) { smart_string cmdstr = {0}; zval *argv = NULL; zend_string *arg; int argc = 0; - ZEND_PARSE_PARAMETERS_START(1, -1) + ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); @@ -2013,6 +2014,20 @@ int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw return SUCCESS; } +int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return gen_vararg_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 0, + "INFO", cmd, cmd_len, slot, ctx); +} + +int redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return gen_vararg_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, + "SCRIPT", cmd, cmd_len, slot, ctx); +} + /* Generic handling of every blocking pop command (BLPOP, BZPOP[MIN/MAX], etc */ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, diff --git a/redis_commands.h b/redis_commands.h index bf83b8590a..1171b39b56 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -185,6 +185,12 @@ int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock * specific processing we do (e.g. verifying subarguments) that make them * unique */ +int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -344,9 +350,6 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); From 6d104481098527d1c20ec62e18e82b1bdb055c2b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 10 Dec 2022 11:46:45 -0800 Subject: [PATCH 1745/1986] Refactor MSET and MSETNX commands. Switch to using our normal command handling logic for MSET and MSETNX. --- redis.c | 55 ++---------------------------------------------- redis_commands.c | 39 ++++++++++++++++++++++++++++++++++ redis_commands.h | 6 ++++++ 3 files changed, 47 insertions(+), 53 deletions(-) diff --git a/redis.c b/redis.c index 1d468d6cbe..502cb410c3 100644 --- a/redis.c +++ b/redis.c @@ -1549,67 +1549,16 @@ PHP_METHOD(Redis, move) { } /* }}} */ -static -void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, FailableResultCallback fun) -{ - RedisSock *redis_sock; - smart_string cmd = {0}; - zval *object, *z_array; - HashTable *htargs; - zend_string *zkey; - zval *zmem; - char buf[64]; - size_t keylen; - zend_ulong idx; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa", - &object, redis_ce, &z_array) == FAILURE) - { - RETURN_FALSE; - } - - /* Make sure we can get our socket, and we were not passed an empty array */ - if ((redis_sock = redis_sock_get(object, 0)) == NULL || - zend_hash_num_elements(Z_ARRVAL_P(z_array)) == 0) - { - RETURN_FALSE; - } - - /* Initialize our command */ - htargs = Z_ARRVAL_P(z_array); - redis_cmd_init_sstr(&cmd, zend_hash_num_elements(htargs) * 2, kw, strlen(kw)); - - ZEND_HASH_FOREACH_KEY_VAL(htargs, idx, zkey, zmem) { - /* Handle string or numeric keys */ - if (zkey) { - redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, NULL); - } else { - keylen = snprintf(buf, sizeof(buf), "%ld", (long)idx); - redis_cmd_append_sstr_key(&cmd, buf, keylen, redis_sock, NULL); - } - - /* Append our value */ - redis_cmd_append_sstr_zval(&cmd, zmem, redis_sock); - } ZEND_HASH_FOREACH_END(); - - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); - if (IS_ATOMIC(redis_sock)) { - fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - - REDIS_PROCESS_RESPONSE(fun); -} - /* {{{ proto bool Redis::mset(array (key => value, ...)) */ PHP_METHOD(Redis, mset) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response); + REDIS_PROCESS_KW_CMD("MSET", redis_mset_cmd, redis_boolean_response); } /* }}} */ /* {{{ proto bool Redis::msetnx(array (key => value, ...)) */ PHP_METHOD(Redis, msetnx) { - generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response); + REDIS_PROCESS_KW_CMD("MSETNX", redis_mset_cmd, redis_1_response); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index 0303007d11..df175fc0a1 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1843,6 +1843,45 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + smart_string cmdstr = {0}; + HashTable *kvals = NULL; + zend_string *key; + zend_ulong idx; + char buf[64]; + size_t klen; + zval *zv; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(kvals) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(kvals) == 0) + return FAILURE; + + redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(kvals) * 2, kw, strlen(kw)); + + ZEND_HASH_FOREACH_KEY_VAL(kvals, idx, key, zv) { + if (key) { + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, NULL); + } else { + klen = snprintf(buf, sizeof(buf), ZEND_LONG_FMT, idx); + redis_cmd_append_sstr_key(&cmdstr, buf, klen, redis_sock, NULL); + } + + ZVAL_DEREF(zv); + redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); + } ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* Generic function that takes a variable number of keys, with an optional * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ diff --git a/redis_commands.h b/redis_commands.h index 1171b39b56..69cfc738ae 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -350,6 +350,12 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); From 90eb04709a6ef55b173f51b4c54559f0e3cb91a6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 7 Dec 2022 17:03:57 -0800 Subject: [PATCH 1746/1986] Refactor HMSET command. --- redis_commands.c | 75 ++++++++++++------------------------------------ 1 file changed, 18 insertions(+), 57 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index df175fc0a1..7a04c2382f 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2644,76 +2644,37 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key; - int key_free, count; - size_t key_len; - zend_ulong idx; - zval *z_arr; - HashTable *ht_vals; smart_string cmdstr = {0}; - zend_string *zkey; - zval *z_val; + zend_string *key = NULL; + HashTable *ht = NULL; + uint32_t fields; + zend_ulong idx; + zval *zv; - // Parse args - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, - &z_arr) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(ht) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - // We can abort if we have no fields - if ((count = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { + fields = zend_hash_num_elements(ht); + if (fields == 0) return FAILURE; - } - - // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Grab our array as a HashTable - ht_vals = Z_ARRVAL_P(z_arr); - - // Initialize our HMSET command (key + 2x each array entry), add key - redis_cmd_init_sstr(&cmdstr, 1+(count*2), ZEND_STRL("HMSET")); - redis_cmd_append_sstr(&cmdstr, key, key_len); - // Start traversing our key => value array - ZEND_HASH_FOREACH_KEY_VAL(ht_vals, idx, zkey, z_val) { - char *mem, *val, kbuf[40]; - size_t val_len; - int val_free; - unsigned int mem_len; + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (2 * fields), "HMSET"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - // If the hash key is an integer, convert it to a string - if (zkey) { - mem_len = ZSTR_LEN(zkey); - mem = ZSTR_VAL(zkey); + ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zv) { + if (key) { + redis_cmd_append_sstr_zstr(&cmdstr, key); } else { - mem_len = snprintf(kbuf, sizeof(kbuf), ZEND_LONG_FMT, idx); - mem = (char*)kbuf; + redis_cmd_append_sstr_long(&cmdstr, idx); } - - // Serialize value (if directed) - val_free = redis_pack(redis_sock, z_val, &val, &val_len); - - // Append the key and value to our command - redis_cmd_append_sstr(&cmdstr, mem, mem_len); - redis_cmd_append_sstr(&cmdstr, val, val_len); - - // Free our value if we serialized it - if (val_free) efree(val); + redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); } ZEND_HASH_FOREACH_END(); - // Set slot if directed - CMD_SET_SLOT(slot,key,key_len); - - // Free our key if we prefixed it - if (key_free) efree(key); - - // Push return pointers *cmd_len = cmdstr.len; *cmd = cmdstr.c; - // Success! return SUCCESS; } From 19fd7e0c008653eac2b3ac5f0c569a6c75f9b1e6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 7 Dec 2022 17:33:05 -0800 Subject: [PATCH 1747/1986] Refactor PFCOUNT command. --- redis.stub.php | 4 +- redis_arginfo.h | 6 +-- redis_commands.c | 93 ++++++++++++------------------------------ redis_legacy_arginfo.h | 6 ++- 4 files changed, 34 insertions(+), 75 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index ad994f7853..f27dc58902 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -2248,11 +2248,11 @@ public function pfadd(string $key, array $elements): Redis|int; * * @see https://redis.io/commands/pfcount * - * @param string $key The key name we wish to query. + * @param string $key_or_keys Either one key or an array of keys * * @return Redis|int The estimated cardinality of the set. */ - public function pfcount(string $key): Redis|int; + public function pfcount(array|string $key_or_keys): Redis|int|false; /** * Merge one or more source HyperLogLog sets into a destination set. diff --git a/redis_arginfo.h b/redis_arginfo.h index 2adb1739db..4d119f67e2 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 399e9506bc58ff0da1abc8f46a02b2499ed1223a */ + * Stub hash: 2d2bd3a96ba44622f4157ee2e06695ffb87a4949 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -604,8 +604,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfadd, 0, 2, Red ZEND_ARG_TYPE_INFO(0, elements, IS_ARRAY, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfcount, 0, 1, Redis, MAY_BE_LONG) - ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfcount, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_MASK(0, key_or_keys, MAY_BE_ARRAY|MAY_BE_STRING, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfmerge, 0, 2, Redis, MAY_BE_BOOL) diff --git a/redis_commands.c b/redis_commands.c index 7a04c2382f..4258be3f5c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3163,89 +3163,46 @@ int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_keys, *z_key; - HashTable *ht_keys; smart_string cmdstr = {0}; - int num_keys, key_free; - size_t key_len; - char *key; - short kslot=-1; - zend_string *zstr; + zval *zarg = NULL, *zv; + short slot2 = -1; + uint32_t keys; - if (zend_parse_parameters(ZEND_NUM_ARGS(),"z",&z_keys) == FAILURE) { - return FAILURE; - } - - /* If we were passed an array of keys, iterate through them prefixing if - * required and capturing lengths and if we need to free them. Otherwise - * attempt to treat the argument as a string and just pass one */ - if (Z_TYPE_P(z_keys) == IS_ARRAY) { - /* Grab key hash table and the number of keys */ - ht_keys = Z_ARRVAL_P(z_keys); - num_keys = zend_hash_num_elements(ht_keys); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zarg) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - /* There is no reason to send zero keys */ - if (num_keys == 0) { + if (Z_TYPE_P(zarg) == IS_STRING) { + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "PFCOUNT"); + redis_cmd_append_sstr_key_zstr(&cmdstr, Z_STR_P(zarg), redis_sock, slot); + } else if (Z_TYPE_P(zarg) == IS_ARRAY) { + keys = zend_hash_num_elements(Z_ARRVAL_P(zarg)); + if (keys == 0) return FAILURE; - } - /* Initialize the command with our number of arguments */ - redis_cmd_init_sstr(&cmdstr, num_keys, ZEND_STRL("PFCOUNT")); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, keys, "PFCOUNT"); - /* Append our key(s) */ - ZEND_HASH_FOREACH_VAL(ht_keys, z_key) { - /* Turn our value into a string if it isn't one */ - zstr = zval_get_string(z_key); - key = ZSTR_VAL(zstr); - key_len = ZSTR_LEN(zstr); - - /* Append this key to our command */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - redis_cmd_append_sstr(&cmdstr, key, key_len); - - /* Protect against CROSSLOT errors */ + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zarg), zv) { + redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); if (slot) { - if (kslot == -1) { - kslot = cluster_hash_key(key, key_len); - } else if (cluster_hash_key(key,key_len)!=kslot) { - zend_string_release(zstr); - if (key_free) efree(key); - efree(cmdstr.c); - - php_error_docref(NULL, E_WARNING, - "Not all keys hash to the same slot!"); - return FAILURE; - } + if (slot2 != -1 && slot2 != *slot) + goto cross_slot; + slot2 = *slot; } - - /* Cleanup */ - zend_string_release(zstr); - if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); } else { - /* Construct our whole command */ - redis_cmd_init_sstr(&cmdstr, 1, ZEND_STRL("PFCOUNT")); - - /* Turn our key into a string if it's a different type */ - zstr = zval_get_string(z_keys); - key = ZSTR_VAL(zstr); - key_len = ZSTR_LEN(zstr); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - redis_cmd_append_sstr(&cmdstr, key, key_len); - - /* Hash our key */ - CMD_SET_SLOT(slot, key, key_len); - - /* Cleanup */ - zend_string_release(zstr); - if (key_free) efree(key); + php_error_docref(NULL, E_WARNING, "Argument must be either an array or a string"); + return FAILURE; } - /* Push our command and length to the caller */ *cmd = cmdstr.c; *cmd_len = cmdstr.len; - return SUCCESS; + +cross_slot: + php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); + efree(cmdstr.c); + return FAILURE; } int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 4914f81ad9..be14294054 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 399e9506bc58ff0da1abc8f46a02b2499ed1223a */ + * Stub hash: 2d2bd3a96ba44622f4157ee2e06695ffb87a4949 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -527,7 +527,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfadd, 0, 0, 2) ZEND_ARG_INFO(0, elements) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_pfcount arginfo_class_Redis__prefix +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfcount, 0, 0, 1) + ZEND_ARG_INFO(0, key_or_keys) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_pfmerge, 0, 0, 2) ZEND_ARG_INFO(0, dst) From 204a02c5fa4d2827ec038ffa5bb0f2776b2aad8b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 8 Dec 2022 17:13:15 -0800 Subject: [PATCH 1748/1986] Refactor SMOVE command. --- redis_commands.c | 50 ++++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 4258be3f5c..b1346d0641 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3426,43 +3426,31 @@ redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *src, *dst; - size_t src_len, dst_len; - int src_free, dst_free; - zval *z_val; + zend_string *src = NULL, *dst = NULL; + smart_string cmdstr = {0}; + zval *zv = NULL; + short slot2; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &src, &src_len, - &dst, &dst_len, &z_val) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) { + Z_PARAM_STR(src) + Z_PARAM_STR(dst) + Z_PARAM_ZVAL(zv) + } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - src_free = redis_key_prefix(redis_sock, &src, &src_len); - dst_free = redis_key_prefix(redis_sock, &dst, &dst_len); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "SMOVE"); + redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); + redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); + redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); - // Protect against a CROSSSLOT error - if (slot) { - short slot1 = cluster_hash_key(src, src_len); - short slot2 = cluster_hash_key(dst, dst_len); - if (slot1 != slot2) { - php_error_docref(0, E_WARNING, - "Source and destination keys don't hash to the same slot!"); - if (src_free) efree(src); - if (dst_free) efree(dst); - return FAILURE; - } - *slot = slot1; + if (slot && *slot != slot2) { + php_error_docref(0, E_WARNING, "Source and destination keys don't hash to the same slot!"); + efree(cmdstr.c); + return FAILURE; } - // Construct command - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SMOVE", "ssv", src, src_len, dst, - dst_len, z_val); - - // Cleanup - if (src_free) efree(src); - if (dst_free) efree(dst); + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; - // Success! return SUCCESS; } From aa0938a4e21550a6cab497810e3993af419df2c2 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 10 Dec 2022 11:59:40 -0800 Subject: [PATCH 1749/1986] Rework ZRANGE argument handling. Rework argument parsing for `ZRANGE` so we can pass either a string or an integer so everything will work even when using strict types. Additionally update our docs to use the correct mechanism for adding the `BYSCORE` option. Fixes #2291 --- library.c | 21 ++++++++++++++------- redis.stub.php | 4 ++-- redis_arginfo.h | 6 +++--- redis_commands.c | 25 ++++++++++++++----------- redis_legacy_arginfo.h | 2 +- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/library.c b/library.c index 302cf8c899..428f4d542d 100644 --- a/library.c +++ b/library.c @@ -1073,16 +1073,23 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value) return redis_cmd_append_sstr(str, tmp, len); } -/* Append a zval to a redis command. The value will be serialized if we are - * configured to do that */ +/* Append a zval to a redis command. If redis_sock is passed as non-null we will + * the value may be serialized, if we're configured to do that. */ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock) { - char *val; - size_t vallen; int valfree, retval; + zend_string *zstr; + size_t vallen; + char *val; - valfree = redis_pack(redis_sock, z, &val, &vallen); - retval = redis_cmd_append_sstr(str, val, vallen); - if (valfree) efree(val); + if (redis_sock != NULL) { + valfree = redis_pack(redis_sock, z, &val, &vallen); + retval = redis_cmd_append_sstr(str, val, vallen); + if (valfree) efree(val); + } else { + zstr = zval_get_string(z); + retval = redis_cmd_append_sstr_zstr(str, zstr); + zend_string_release(zstr); + } return retval; } diff --git a/redis.stub.php b/redis.stub.php index f27dc58902..7e1cfa94fb 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -4108,9 +4108,9 @@ public function zPopMin(string $key, int $count = null): Redis|array|false; * @category zset * * @example $redis->zRange('zset', 0, -1); - * @example $redis->zRange('zset', '-inf', 'inf', ['byscore' => true]); + * @example $redis->zRange('zset', '-inf', 'inf', ['byscore']); */ - public function zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null): Redis|array|false; + public function zRange(string $key, string|int $start, string|int $end, array|bool|null $options = null): Redis|array|false; /** * Retrieve a range of elements from a sorted set by legographical range. diff --git a/redis_arginfo.h b/redis_arginfo.h index 4d119f67e2..690fd9d96b 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2d2bd3a96ba44622f4157ee2e06695ffb87a4949 */ + * Stub hash: b0d5c56084a89230807e6ba582d2fab536d2e897 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -1028,8 +1028,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, start, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO(0, end, IS_MIXED, 0) + ZEND_ARG_TYPE_MASK(0, start, MAY_BE_STRING|MAY_BE_LONG, NULL) + ZEND_ARG_TYPE_MASK(0, end, MAY_BE_STRING|MAY_BE_LONG, NULL) ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() diff --git a/redis_commands.c b/redis_commands.c index b1346d0641..2914c8d328 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -746,20 +746,23 @@ static int redis_get_zcmd_flags(const char *kw) { } /* Validate ZLEX* min/max argument strings */ -#define validate_zlex_arg_zstr(zs_) validate_zlex_arg(ZSTR_VAL((zs_)), ZSTR_LEN((zs_))) -static int validate_zlex_arg(const char *arg, size_t len) { - return (len > 1 && (*arg == '[' || *arg == '(')) || - (len == 1 && (*arg == '+' || *arg == '-')); +static int validate_zlex_arg(const char *str, size_t len) { + return (len > 1 && (*str == '[' || *str == '(')) || + (len == 1 && (*str == '+' || *str == '-')); +} + +static int validate_zlex_arg_zval(zval *z) { + return Z_TYPE_P(z) == IS_STRING && validate_zlex_arg(Z_STRVAL_P(z), Z_STRLEN_P(z)); } int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zend_string *dst = NULL, *src = NULL, *sstart = NULL, *send = NULL; + zval *zoptions = NULL, *zstart, *zend; + zend_string *dst = NULL, *src = NULL; zend_long start = 0, end = 0; smart_string cmdstr = {0}; - zval *zoptions = NULL; redisZcmdOptions opt; int min_argc, flags; short slot2; @@ -776,8 +779,8 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_LONG(start) Z_PARAM_LONG(end) } else { - Z_PARAM_STR(sstart) - Z_PARAM_STR(send) + Z_PARAM_ZVAL(zstart) + Z_PARAM_ZVAL(zend) } Z_PARAM_OPTIONAL Z_PARAM_ZVAL_OR_NULL(zoptions) @@ -787,7 +790,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (opt.bylex) { ZEND_ASSERT(!(flags & REDIS_ZCMD_INT_RANGE)); - if (!validate_zlex_arg_zstr(sstart) || !validate_zlex_arg_zstr(send)) { + if (!validate_zlex_arg_zval(zstart) || !validate_zlex_arg_zval(zend)) { php_error_docref(NULL, E_WARNING, "Legographical args must start with '[' or '(' or be '+' or '-'"); return FAILURE; } @@ -812,8 +815,8 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_append_sstr_long(&cmdstr, start); redis_cmd_append_sstr_long(&cmdstr, end); } else { - redis_cmd_append_sstr_zstr(&cmdstr, sstart); - redis_cmd_append_sstr_zstr(&cmdstr, send); + redis_cmd_append_sstr_zval(&cmdstr, zstart, NULL); + redis_cmd_append_sstr_zval(&cmdstr, zend, NULL); } REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.byscore, "BYSCORE"); diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index be14294054..aac5253ff7 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2d2bd3a96ba44622f4157ee2e06695ffb87a4949 */ + * Stub hash: b0d5c56084a89230807e6ba582d2fab536d2e897 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From 15347c7d87029522c6b66c5a594f45ff167ba6d4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 8 Dec 2022 19:58:37 -0800 Subject: [PATCH 1750/1986] Fix CodeQL confusion. --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 2914c8d328..a55667c4b9 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -759,7 +759,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *zoptions = NULL, *zstart, *zend; + zval *zoptions = NULL, *zstart = NULL, *zend = NULL; zend_string *dst = NULL, *src = NULL; zend_long start = 0, end = 0; smart_string cmdstr = {0}; From 5b560ccfd749dc525ab955ae46a31f9a8d9abb5a Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 9 Dec 2022 10:01:17 -0800 Subject: [PATCH 1751/1986] Refactor a couple more command methods. Use the new argument parsing API --- redis_commands.c | 81 +++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index a55667c4b9..5fe7a949da 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -363,46 +363,27 @@ int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *k1, *k2; - size_t k1len, k2len; - int k1free, k2free; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &k1, &k1len, - &k2, &k2len) == FAILURE) - { - return FAILURE; - } + zend_string *key1 = NULL, *key2 = NULL; + smart_string cmdstr = {0}; + short slot2; - // Prefix both keys - k1free = redis_key_prefix(redis_sock, &k1, &k1len); - k2free = redis_key_prefix(redis_sock, &k2, &k2len); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key1) + Z_PARAM_STR(key2) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - // If a slot is requested, we can test that they hash the same - if (slot) { - // Slots where these keys resolve - short slot1 = cluster_hash_key(k1, k1len); - short slot2 = cluster_hash_key(k2, k2len); - - // Check if Redis would give us a CROSSLOT error - if (slot1 != slot2) { - php_error_docref(0, E_WARNING, "Keys don't hash to the same slot"); - if (k1free) efree(k1); - if (k2free) efree(k2); - return FAILURE; - } + redis_cmd_init_sstr(&cmdstr, 2, kw, strlen(kw)); + redis_cmd_append_sstr_key_zstr(&cmdstr, key1, redis_sock, slot); + redis_cmd_append_sstr_key_zstr(&cmdstr, key2, redis_sock, slot ? &slot2 : NULL); - // They're both the same - *slot = slot1; + if (slot && *slot != slot2) { + php_error_docref(0, E_WARNING, "Keys don't hash to the same slot"); + smart_string_free(&cmdstr); + return FAILURE; } - /* Send keys as normal strings because we manually prefixed to check against - * cross slot error. */ - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ss", k1, k1len, k2, k2len); - - /* Clean keys up if we prefixed */ - if (k1free) efree(k1); - if (k2free) efree(k2); - + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; return SUCCESS; } @@ -411,19 +392,16 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key; - size_t keylen; - zend_long lval; + zend_string *key = NULL; + zend_long lval = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &key, &keylen, &lval) - ==FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key) + Z_PARAM_LONG(lval) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kl", key, keylen, lval); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kl", ZSTR_VAL(key), ZSTR_LEN(key), lval); - // Success! return SUCCESS; } @@ -432,15 +410,14 @@ int redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zend_long v1, v2; + zend_long l1 = 0, l2 = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &v1, &v2) - == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(l1) + Z_PARAM_LONG(l2) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ll", v1, v2); + *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ll", l1, l2); return SUCCESS; } From ac057145fc2dc4741c50002a8327e2732719bf21 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Sat, 10 Dec 2022 13:08:50 -0800 Subject: [PATCH 1752/1986] Build windows artifacts as part of CI (#2295) Build windows artifacts as part of CI See: #2291 --- .github/workflows/ci.yml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 907637d488..8209194f03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -119,11 +119,9 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.3', '7.4', '8.0', '8.1'] + php: ['7.3', '7.4', '8.0', '8.1', '8.2'] experimental: [false] - include: - - php: '8.2' - experimental: true + ts: [nts, ts] steps: - name: Checkout uses: actions/checkout@v3 @@ -135,7 +133,7 @@ jobs: with: version: ${{ matrix.php }} arch: x64 - ts: nts + ts: ${{matrix.ts}} - name: Install dependencies uses: ilammy/msvc-dev-cmd@v1 with: @@ -146,3 +144,18 @@ jobs: phpize ./configure --enable-redis --with-prefix=${{steps.setup-php-sdk.outputs.prefix}} nmake + - name: package + run: | + md binaries + copy LICENSE binaries + if (Test-Path "x64\Release") { + Set-Variable -Name "prefix" -Value "x64\Release" + } else { + Set-Variable -Name "prefix" -Value "x64\Release_TS" + } + copy $prefix\php_redis.dll binaries + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: redis-${{matrix.php}}-x64-${{matrix.ts}} + path: binaries From 59de183bd44539b199291b26e22e1b4967ee9eb9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 10 Dec 2022 14:41:57 -0800 Subject: [PATCH 1753/1986] Use Get-ChildItem instead of a hardcoded path for php_relay.dll --- .github/workflows/ci.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8209194f03..56dec03b69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,12 +148,7 @@ jobs: run: | md binaries copy LICENSE binaries - if (Test-Path "x64\Release") { - Set-Variable -Name "prefix" -Value "x64\Release" - } else { - Set-Variable -Name "prefix" -Value "x64\Release_TS" - } - copy $prefix\php_redis.dll binaries + Get-ChildItem -Recurse -Filter "php_redis.dll" | ForEach-Object {Copy-Item -Path $_.FullName -Destination "binaries"} - name: Upload artifacts uses: actions/upload-artifact@v2 with: From 856b3509f5877448c80eb471fe566634d8d874b5 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 10 Dec 2022 15:32:02 -0800 Subject: [PATCH 1754/1986] PHP 8.2 is now released. --- .github/workflows/ci.yml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56dec03b69..003d111dd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,15 +3,11 @@ on: [push, pull_request] jobs: ubuntu: runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental }} + continue-on-error: false strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1'] - experimental: [false] - include: - - php: '8.2' - experimental: true + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout uses: actions/checkout@v3 @@ -81,15 +77,11 @@ jobs: macos: runs-on: macos-latest - continue-on-error: ${{ matrix.experimental }} + continue-on-error: false strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1'] - experimental: [false] - include: - - php: '8.2' - experimental: true + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout uses: actions/checkout@v3 @@ -115,12 +107,11 @@ jobs: windows: runs-on: windows-latest - continue-on-error: ${{ matrix.experimental }} + continue-on-error: false strategy: fail-fast: false matrix: php: ['7.3', '7.4', '8.0', '8.1', '8.2'] - experimental: [false] ts: [nts, ts] steps: - name: Checkout From ae3bc505a2ff36a98f9f836b9cbcb0e3252462f4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 11 Dec 2022 13:24:03 -0800 Subject: [PATCH 1755/1986] Fix shellcheck warnings --- .github/workflows/ci.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 003d111dd3..082207ec90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,13 +30,13 @@ jobs: run: | phpize ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4 - sudo make -j$(nproc) install - echo 'extension = redis.so' | sudo tee -a $(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')/90-redis.ini + sudo make -j"$(nproc)" install + echo 'extension = redis.so' | sudo tee -a "$(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')"/90-redis.ini - name: Start redis run: | redis-cli SHUTDOWN NOSAVE for PORT in $(seq 6379 6382) $(seq 32767 32769); do - redis-server --port $PORT --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels + redis-server --port "$PORT" --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels done redis-server --port 0 --unixsocket /tmp/redis.sock --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels - name: Start redis cluster @@ -44,17 +44,17 @@ jobs: mkdir -p tests/nodes echo -n > tests/nodes/nodemap for PORT in $(seq 7000 7005); do - redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels - echo 127.0.0.1:$PORT >> tests/nodes/nodemap + redis-server --port "$PORT" --cluster-enabled yes --cluster-config-file "$PORT".conf --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels + echo 127.0.0.1:"$PORT" >> tests/nodes/nodemap done echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7005) --cluster-replicas 1 --user phpredis -a phpredis - name: Start redis sentinel run: | wget raw.githubusercontent.com/redis/redis/7.0/sentinel.conf for PORT in $(seq 26379 26380); do - cp sentinel.conf $PORT.conf - sed -i '/^sentinel/Id' $PORT.conf - redis-server $PORT.conf --port $PORT --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel auth-pass mymaster phpredis + cp sentinel.conf "$PORT.conf" + sed -i '/^sentinel/Id' "$PORT.conf" + redis-server "$PORT.conf" --port "$PORT" --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel auth-pass mymaster phpredis done - name: Run tests run: | @@ -103,7 +103,7 @@ jobs: phpize ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4 sudo make install - echo 'extension = redis.so' | sudo tee -a $(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')/90-redis.ini + echo 'extension = redis.so' | sudo tee -a "$(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')/90-redis.ini" windows: runs-on: windows-latest From c8224b93b1ed876934e6a3b46bd8cf1e447de2a4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 11 Dec 2022 13:06:11 -0800 Subject: [PATCH 1756/1986] Refactor a more command handlers. Use PHP's new argument parsing API and also our simplified mechanism for dynamically appending strings or packed values. --- redis_commands.c | 91 +++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 59 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 5fe7a949da..6a72605dfd 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1507,36 +1507,31 @@ int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_arr, *z_chan; - HashTable *ht_arr; smart_string cmdstr = {0}; - subscribeContext *sctx = ecalloc(1, sizeof(*sctx)); + subscribeContext *sctx; + HashTable *channels; + zval *channel; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) { - efree(sctx); - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(channels) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - ht_arr = Z_ARRVAL_P(z_arr); + if (zend_hash_num_elements(channels) == 0) + return FAILURE; + sctx = ecalloc(1, sizeof(*sctx)); sctx->kw = kw; - sctx->argc = zend_hash_num_elements(ht_arr); - redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); + sctx->argc = zend_hash_num_elements(channels); - ZEND_HASH_FOREACH_VAL(ht_arr, z_chan) { - char *key = Z_STRVAL_P(z_chan); - size_t key_len = Z_STRLEN_P(z_chan); - int key_free; + redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); - key_free = redis_key_prefix(redis_sock, &key, &key_len); - redis_cmd_append_sstr(&cmdstr, key, key_len); - if (key_free) efree(key); + ZEND_HASH_FOREACH_VAL(channels, channel) { + redis_cmd_append_sstr_key_zval(&cmdstr, channel, redis_sock, slot); } ZEND_HASH_FOREACH_END(); - // Push out vals *cmd_len = cmdstr.len; - *cmd = cmdstr.c; - *ctx = (void*)sctx; + *cmd = cmdstr.c; + *ctx = sctx; return SUCCESS; } @@ -1726,54 +1721,32 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Commands that take a key and then an array of values */ -#define VAL_TYPE_VALUES 1 -#define VAL_TYPE_STRINGS 2 static int gen_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, int valtype, char **cmd, int *cmd_len, + char *kw, zend_bool pack_values, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_arr, *z_val; - HashTable *ht_arr; smart_string cmdstr = {0}; - zend_string *zstr; - int key_free, val_free, argc = 1; - size_t val_len, key_len; - char *key, *val; + HashTable *values = NULL; + zend_string *key = NULL; + zval *zv; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, - &z_arr) == FAILURE || - zend_hash_num_elements(Z_ARRVAL_P(z_arr)) == 0) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(values) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - /* Start constructing our command */ - ht_arr = Z_ARRVAL_P(z_arr); - argc += zend_hash_num_elements(ht_arr); - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + if (zend_hash_num_elements(values) == 0) + return FAILURE; - /* Prefix if required and append the key name */ - key_free = redis_key_prefix(redis_sock, &key, &key_len); - redis_cmd_append_sstr(&cmdstr, key, key_len); - CMD_SET_SLOT(slot, key, key_len); - if (key_free) efree(key); + redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(values), kw, strlen(kw)); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - /* Iterate our hash table, serializing and appending values */ - assert(valtype == VAL_TYPE_VALUES || valtype == VAL_TYPE_STRINGS); - ZEND_HASH_FOREACH_VAL(ht_arr, z_val) { - if (valtype == VAL_TYPE_VALUES) { - val_free = redis_pack(redis_sock, z_val, &val, &val_len); - redis_cmd_append_sstr(&cmdstr, val, val_len); - if (val_free) efree(val); - } else { - zstr = zval_get_string(z_val); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_string_release(zstr); - } + ZEND_HASH_FOREACH_VAL(values, zv) { + redis_cmd_append_sstr_zval(&cmdstr, zv, pack_values ? redis_sock : NULL); } ZEND_HASH_FOREACH_END(); - *cmd_len = cmdstr.len; *cmd = cmdstr.c; + *cmd_len = cmdstr.len; return SUCCESS; } @@ -1783,7 +1756,7 @@ int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, - VAL_TYPE_VALUES, cmd, cmd_len, slot, ctx); + 1, cmd, cmd_len, slot, ctx); } int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, @@ -1791,7 +1764,7 @@ int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, void **ctx) { return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, - VAL_TYPE_STRINGS, cmd, cmd_len, slot, ctx); + 0, cmd, cmd_len, slot, ctx); } /* Generic function that takes one or more non-serialized arguments */ From 40e1b1bfe80159c41ff8e8a2b004a688fbbc8450 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 11 Dec 2022 19:51:36 -0800 Subject: [PATCH 1757/1986] Migrate more command handlers to the new arg API --- library.c | 11 ++++++ library.h | 1 + redis_commands.c | 97 ++++++++++++++++-------------------------------- 3 files changed, 44 insertions(+), 65 deletions(-) diff --git a/library.c b/library.c index 428f4d542d..9f88c881f5 100644 --- a/library.c +++ b/library.c @@ -1126,6 +1126,17 @@ int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis return res; } +int redis_cmd_append_sstr_key_long(smart_string *dst, zend_long lval, RedisSock *redis_sock, short *slot) { + char buf[64]; + size_t len; + int res; + + len = snprintf(buf, sizeof(buf), ZEND_LONG_FMT, lval); + res = redis_cmd_append_sstr_key(dst, buf, len, redis_sock, slot); + + return res; +} + /* Append an array key to a redis smart string command. This function * handles the boilerplate conditionals around string or integer keys */ int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx) diff --git a/library.h b/library.h index c5246d0ed0..8565ecb493 100644 --- a/library.h +++ b/library.h @@ -52,6 +52,7 @@ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_key_zstr(smart_string *str, zend_string *key, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot); +int redis_cmd_append_sstr_key_long(smart_string *dst, zend_long lval, RedisSock *redis_sock, short *slot); int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx); PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...); diff --git a/redis_commands.c b/redis_commands.c index 6a72605dfd..73e026ee08 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1775,19 +1775,16 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { smart_string cmdstr = {0}; zval *argv = NULL; - zend_string *arg; int argc = 0; ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - redis_cmd_init_sstr(&cmdstr, ZEND_NUM_ARGS(), kw, strlen(kw)); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); for (uint32_t i = 0; i < argc; i++) { - arg = zval_get_string(&argv[i]); - redis_cmd_append_sstr_zstr(&cmdstr, arg); - zend_string_release(arg); + redis_cmd_append_sstr_zval(&cmdstr, &argv[i], NULL); } *cmd = cmdstr.c; @@ -1804,8 +1801,6 @@ int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, HashTable *kvals = NULL; zend_string *key; zend_ulong idx; - char buf[64]; - size_t klen; zval *zv; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -1818,14 +1813,12 @@ int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(kvals) * 2, kw, strlen(kw)); ZEND_HASH_FOREACH_KEY_VAL(kvals, idx, key, zv) { + ZVAL_DEREF(zv); if (key) { redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, NULL); } else { - klen = snprintf(buf, sizeof(buf), ZEND_LONG_FMT, idx); - redis_cmd_append_sstr_key(&cmdstr, buf, klen, redis_sock, NULL); + redis_cmd_append_sstr_key_long(&cmdstr, idx, redis_sock, NULL); } - - ZVAL_DEREF(zv); redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); } ZEND_HASH_FOREACH_END(); @@ -1923,12 +1916,13 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zend_string *from = NULL, *key; int argc, blocking, is_zmpop; smart_string cmdstr = {0}; + zend_string *from = NULL; HashTable *keys = NULL; double timeout = 0.0; zend_long count = 1; + short slot2 = -1; zval *zv; /* Sanity check on our keyword */ @@ -1974,22 +1968,15 @@ int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw if (slot) *slot = -1; ZEND_HASH_FOREACH_VAL(keys, zv) { - key = redis_key_prefix_zval(redis_sock, zv); - + redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); if (slot) { - if (*slot == -1) { - *slot = cluster_hash_key_zstr(key); - } else if (*slot != cluster_hash_key_zstr(key)) { + if (slot2 != -1 && *slot != slot2) { php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot"); - zend_string_release(key); efree(cmdstr.c); return FAILURE; } + slot2 = *slot; } - - redis_cmd_append_sstr_zstr(&cmdstr, key); - - zend_string_release(key); } ZEND_HASH_FOREACH_END(); redis_cmd_append_sstr_zstr(&cmdstr, from); @@ -4224,10 +4211,6 @@ static int get_georadius_opts(HashTable *ht, geoOptions *opts) { void append_georadius_opts(RedisSock *redis_sock, smart_string *str, short *slot, geoOptions *opt) { - char *key; - size_t keylen; - int keyfree; - if (opt->withcoord) REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHCOORD"); if (opt->withdist) @@ -4253,21 +4236,13 @@ void append_georadius_opts(RedisSock *redis_sock, smart_string *str, short *slot /* Append store options if we've got them */ if (opt->store != STORE_NONE && opt->key != NULL) { - /* Grab string bits and prefix if requested */ - key = ZSTR_VAL(opt->key); - keylen = ZSTR_LEN(opt->key); - keyfree = redis_key_prefix(redis_sock, &key, &keylen); - if (opt->store == STORE_COORD) { REDIS_CMD_APPEND_SSTR_STATIC(str, "STORE"); } else { REDIS_CMD_APPEND_SSTR_STATIC(str, "STOREDIST"); } - redis_cmd_append_sstr(str, key, keylen); - - CMD_SET_SLOT(slot, key, keylen); - if (keyfree) free(key); + redis_cmd_append_sstr_key_zstr(str, opt->key, redis_sock, slot); } } @@ -4276,55 +4251,47 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key, *unit; - short store_slot = 0; - size_t keylen, unitlen; - int argc = 5, keyfree; - double lng, lat, radius; - zval *opts = NULL; - geoOptions gopts = {0}; + zend_string *key = NULL, *unit = NULL; + double lng = 0, lat = 0, radius = 0; smart_string cmdstr = {0}; + HashTable *opts = NULL; + geoOptions gopts = {0}; + short store_slot = -1; + uint32_t argc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sddds|a", &key, &keylen, - &lng, &lat, &radius, &unit, &unitlen, &opts) - == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(5, 6) + Z_PARAM_STR(key) + Z_PARAM_DOUBLE(lng) + Z_PARAM_DOUBLE(lat) + Z_PARAM_DOUBLE(radius) + Z_PARAM_STR(unit) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(opts) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); /* Parse any GEORADIUS options we have */ - if (opts != NULL) { - /* Attempt to parse our options array */ - if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts) != SUCCESS) - { - return FAILURE; - } - } + if (opts != NULL && get_georadius_opts(opts, &gopts) != SUCCESS) + return FAILURE; /* Increment argc depending on options */ - argc += gopts.withcoord + gopts.withdist + gopts.withhash + - (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0) + - (gopts.store != STORE_NONE ? 2 : 0); + argc = 5 + gopts.withcoord + gopts.withdist + gopts.withhash + + (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0) + + (gopts.store != STORE_NONE ? 2 : 0); /* Begin construction of our command */ redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - - /* Prefix and set slot */ - keyfree = redis_key_prefix(redis_sock, &key, &keylen); - CMD_SET_SLOT(slot, key, keylen); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); /* Append required arguments */ - redis_cmd_append_sstr(&cmdstr, key, keylen); redis_cmd_append_sstr_dbl(&cmdstr, lng); redis_cmd_append_sstr_dbl(&cmdstr, lat); redis_cmd_append_sstr_dbl(&cmdstr, radius); - redis_cmd_append_sstr(&cmdstr, unit, unitlen); + redis_cmd_append_sstr_zstr(&cmdstr, unit); /* Append optional arguments */ append_georadius_opts(redis_sock, &cmdstr, slot ? &store_slot : NULL, &gopts); /* Free key if it was prefixed */ - if (keyfree) efree(key); if (gopts.key) zend_string_release(gopts.key); /* Protect the user from CROSSSLOT if we're in cluster */ From bb66a547721aeb4da03a61e35353ef47608dc511 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 12 Dec 2022 14:26:23 -0800 Subject: [PATCH 1758/1986] Refactor HMGET command Use new argument parsing API and simplify logic. --- redis_commands.c | 78 ++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 56 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 73e026ee08..befca1b110 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2500,83 +2500,49 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *key; - zval *z_arr, *z_mems, *z_mem; - int i, count, valid = 0, key_free; - size_t key_len; - HashTable *ht_arr; + zval *field = NULL, *zctx = NULL; smart_string cmdstr = {0}; + HashTable *fields = NULL; + zend_string *key = NULL; + zend_ulong valid = 0; - // Parse arguments - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, - &z_arr) == FAILURE) - { - return FAILURE; - } - - // Our HashTable - ht_arr = Z_ARRVAL_P(z_arr); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(fields) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - // We can abort if we have no elements - if ((count = zend_hash_num_elements(ht_arr)) == 0) { + if (zend_hash_num_elements(fields) == 0) return FAILURE; - } - // Allocate memory for mems+1 so we can have a sentinel - z_mems = ecalloc(count + 1, sizeof(zval)); + zctx = ecalloc(1 + zend_hash_num_elements(fields), sizeof(*zctx)); - // Iterate over our member array - ZEND_HASH_FOREACH_VAL(ht_arr, z_mem) { - ZVAL_DEREF(z_mem); - // We can only handle string or long values here - if ((Z_TYPE_P(z_mem) == IS_STRING && Z_STRLEN_P(z_mem) > 0) - || Z_TYPE_P(z_mem) == IS_LONG - ) { - // Copy into our member array - ZVAL_ZVAL(&z_mems[valid], z_mem, 1, 0); + ZEND_HASH_FOREACH_VAL(fields, field) { + ZVAL_DEREF(field); + if (!((Z_TYPE_P(field) == IS_STRING && Z_STRLEN_P(field) > 0) || Z_TYPE_P(field) == IS_LONG)) + continue; - // Increment the member count to actually send - valid++; - } + ZVAL_COPY(&zctx[valid++], field); } ZEND_HASH_FOREACH_END(); - // If nothing was valid, fail if (valid == 0) { - efree(z_mems); + efree(zctx); return FAILURE; } - // Sentinel so we can free this even if it's used and then we discard - // the transaction manually or there is a transaction failure - ZVAL_NULL(&z_mems[valid]); - - // Start command construction - redis_cmd_init_sstr(&cmdstr, valid+1, ZEND_STRL("HMGET")); - - // Prefix our key - key_free = redis_key_prefix(redis_sock, &key, &key_len); + ZVAL_NULL(&zctx[valid]); - redis_cmd_append_sstr(&cmdstr, key, key_len); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + valid, "HMGET"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - // Iterate over members, appending as arguments - for(i = 0; i< valid; i++) { - zend_string *zstr = zval_get_string(&z_mems[i]); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_string_release(zstr); + for (zend_ulong i = 0; i < valid; i++) { + redis_cmd_append_sstr_zval(&cmdstr, &zctx[i], NULL); } - // Set our slot - CMD_SET_SLOT(slot,key,key_len); - - // Free our key if we prefixed it - if (key_free) efree(key); - // Push out command, length, and key context *cmd = cmdstr.c; *cmd_len = cmdstr.len; - *ctx = (void*)z_mems; + *ctx = zctx; - // Success! return SUCCESS; } From 90a0e9cc0ee8cb27e02ecd0fcbc6dfc3c12d617d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 11 Dec 2022 17:51:15 +0200 Subject: [PATCH 1759/1986] Add Redis::function command --- library.c | 88 ++++++++++++++++++++++++++++++++++++ library.h | 2 +- redis.c | 6 +++ redis.stub.php | 20 +++++++++ redis_arginfo.h | 9 +++- redis_commands.c | 92 ++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 ++ redis_legacy_arginfo.h | 9 +++- tests/RedisClusterTest.php | 1 + tests/RedisTest.php | 12 +++++ 10 files changed, 239 insertions(+), 3 deletions(-) diff --git a/library.c b/library.c index 428f4d542d..b74926b1f7 100644 --- a/library.c +++ b/library.c @@ -1708,6 +1708,50 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, ZVAL_ZVAL(z_tab, &z_ret, 0, 0); } +static int +array_zip_values_recursive(zval *z_tab) +{ + zend_string *zkey; + zval z_ret, z_sub, *zv; + + array_init(&z_ret); + for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(z_tab)); + zend_hash_has_more_elements(Z_ARRVAL_P(z_tab)) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(z_tab)) + ) { + if ((zv = zend_hash_get_current_data(Z_ARRVAL_P(z_tab))) == NULL) { + zval_dtor(&z_ret); + return FAILURE; + } + if (Z_TYPE_P(zv) == IS_STRING) { + zkey = zval_get_string(zv); + zend_hash_move_forward(Z_ARRVAL_P(z_tab)); + if ((zv = zend_hash_get_current_data(Z_ARRVAL_P(z_tab))) == NULL) { + zend_string_release(zkey); + zval_dtor(&z_ret); + return FAILURE; + } + if (Z_TYPE_P(zv) == IS_ARRAY && array_zip_values_recursive(zv) != SUCCESS) { + zend_string_release(zkey); + zval_dtor(&z_ret); + return FAILURE; + } + ZVAL_ZVAL(&z_sub, zv, 1, 0); + add_assoc_zval_ex(&z_ret, ZSTR_VAL(zkey), ZSTR_LEN(zkey), &z_sub); + zend_string_release(zkey); + } else { + if (Z_TYPE_P(zv) == IS_ARRAY && array_zip_values_recursive(zv) != SUCCESS) { + zval_dtor(&z_ret); + return FAILURE; + } + ZVAL_ZVAL(&z_sub, zv, 1, 0); + add_next_index_zval(&z_ret, &z_sub); + } + } + zval_dtor(z_tab); + ZVAL_ZVAL(z_tab, &z_ret, 0, 0); + return SUCCESS; +} static int redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, @@ -1966,6 +2010,50 @@ redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval } } +static int +redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + int numElems; + zval z_ret; + + if (read_mbulk_header(redis_sock, &numElems) < 0) { + if (IS_ATOMIC(redis_sock)) { + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); + } + return FAILURE; + } + + array_init(&z_ret); + redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret); + array_zip_values_recursive(&z_ret); + + if (IS_ATOMIC(redis_sock)) { + RETVAL_ZVAL(&z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, &z_ret); + } + + return SUCCESS; +} + + +PHP_REDIS_API int +redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + if (ctx == NULL) { + return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + } else if (ctx == PHPREDIS_CTX_PTR) { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + } else if (ctx == PHPREDIS_CTX_PTR + 1) { + return redis_function_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + } else { + ZEND_ASSERT(!"memory corruption?"); + return FAILURE; + } +} + static int redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { diff --git a/library.h b/library.h index c5246d0ed0..4912389ae8 100644 --- a/library.h +++ b/library.h @@ -195,8 +195,8 @@ PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r PHP_REDIS_API int redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx); - PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/redis.c b/redis.c index 502cb410c3..a89830ba22 100644 --- a/redis.c +++ b/redis.c @@ -1494,6 +1494,12 @@ PHP_METHOD(Redis, flushAll) } /* }}} */ +/* {{{ proto mixed Redis::function(string op, mixed ...args) */ +PHP_METHOD(Redis, function) +{ + REDIS_PROCESS_CMD(function, redis_function_response) +} + /* {{{ proto int Redis::dbSize() */ PHP_METHOD(Redis, dbSize) { diff --git a/redis.stub.php b/redis.stub.php index 7e1cfa94fb..4624c82e07 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1184,6 +1184,26 @@ public function flushAll(?bool $sync = null): Redis|bool; */ public function flushDB(?bool $sync = null): Redis|bool; + /** + * Functions is an API for managing code to be executed on the server. + * + * @param string $operation The subcommand you intend to execute. Valid options are as follows + * 'LOAD' - Create a new library with the given library name and code. + * 'DELETE' - Delete the given library. + * 'LIST' - Return general information on all the libraries + * 'STATS' - Return information about the current function running + * 'KILL' - Kill the current running function + * 'FLUSH' - Delete all the libraries + * 'DUMP' - Return a serialized payload representing the current libraries + * 'RESTORE' - Restore the libraries represented by the given payload + * @param member $args Additional arguments + * + * @return Redis|bool|string|array Depends on subcommand. + * + * @see https://redis.io/commands/function + */ + public function function(string $operation, mixed ...$args): Redis|bool|string|array; + /** * Add one or more members to a geospacial sorted set * diff --git a/redis_arginfo.h b/redis_arginfo.h index 690fd9d96b..618868204d 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b0d5c56084a89230807e6ba582d2fab536d2e897 */ + * Stub hash: 3d369227b8f6d01fffa0ffda01f379f0138fa226 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -237,6 +237,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_function, 0, 1, Redis, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_ARRAY) + ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geoadd, 0, 4, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, lng, IS_DOUBLE, 0) @@ -1204,6 +1209,7 @@ ZEND_METHOD(Redis, expiretime); ZEND_METHOD(Redis, pexpiretime); ZEND_METHOD(Redis, flushAll); ZEND_METHOD(Redis, flushDB); +ZEND_METHOD(Redis, function); ZEND_METHOD(Redis, geoadd); ZEND_METHOD(Redis, geodist); ZEND_METHOD(Redis, geohash); @@ -1456,6 +1462,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, function, arginfo_class_Redis_function, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geodist, arginfo_class_Redis_geodist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geohash, arginfo_class_Redis_geohash, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index 5fe7a949da..60236c8b6a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -937,6 +937,98 @@ redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return res; } +int +redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_string *op = NULL, *arg; + zval *argv = NULL; + int i, argc = 0; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_STR(op) + Z_PARAM_OPTIONAL + Z_PARAM_VARIADIC('*', argv, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + for (i = 0; i < argc; ++i) { + if (Z_TYPE(argv[i]) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "invalid argument"); + return FAILURE; + } + } + + if (zend_string_equals_literal_ci(op, "DELETE")) { + if (argc < 1) { + php_error_docref(NULL, E_WARNING, "argument required"); + return FAILURE; + } + } else if (zend_string_equals_literal_ci(op, "DUMP")) { + *ctx = PHPREDIS_CTX_PTR; + } else if (zend_string_equals_literal_ci(op, "FLUSH")) { + if (argc > 0 && + !zend_string_equals_literal_ci(Z_STR(argv[0]), "SYNC") && + !zend_string_equals_literal_ci(Z_STR(argv[0]), "ASYNC") + ) { + php_error_docref(NULL, E_WARNING, "invalid argument"); + return FAILURE; + } + } else if (zend_string_equals_literal_ci(op, "KILL")) { + // noop + } else if (zend_string_equals_literal_ci(op, "LIST")) { + if (argc > 0) { + if (zend_string_equals_literal_ci(Z_STR(argv[0]), "LIBRARYNAME")) { + if (argc < 2) { + php_error_docref(NULL, E_WARNING, "argument required"); + return FAILURE; + } + } else if (!zend_string_equals_literal_ci(Z_STR(argv[0]), "WITHCODE")) { + php_error_docref(NULL, E_WARNING, "invalid argument"); + return FAILURE; + } + } + *ctx = PHPREDIS_CTX_PTR + 1; + } else if (zend_string_equals_literal_ci(op, "LOAD")) { + if (argc < 1 || ( + zend_string_equals_literal_ci(Z_STR(argv[0]), "REPLACE") && argc < 2 + )) { + php_error_docref(NULL, E_WARNING, "argument required"); + return FAILURE; + } + *ctx = PHPREDIS_CTX_PTR; + } else if (zend_string_equals_literal_ci(op, "RESTORE")) { + if (argc < 1 || ( + argc > 1 && + !zend_string_equals_literal_ci(Z_STR(argv[1]), "FLUSH") && + !zend_string_equals_literal_ci(Z_STR(argv[1]), "APPEND") && + !zend_string_equals_literal_ci(Z_STR(argv[1]), "REPLACE") + )) { + php_error_docref(NULL, E_WARNING, "invalid argument"); + return FAILURE; + } + } else if (zend_string_equals_literal_ci(op, "STATS")) { + *ctx = PHPREDIS_CTX_PTR + 1; + } else { + php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op)); + return FAILURE; + } + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + argc, "FUNCTION"); + redis_cmd_append_sstr_zstr(&cmdstr, op); + + for (i = 0; i < argc; i++) { + arg = zval_get_string(&argv[i]); + redis_cmd_append_sstr_zstr(&cmdstr, arg); + zend_string_release(arg); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 69cfc738ae..8f78800b08 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -110,6 +110,9 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index aac5253ff7..2c90eaa27c 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b0d5c56084a89230807e6ba582d2fab536d2e897 */ + * Stub hash: 3d369227b8f6d01fffa0ffda01f379f0138fa226 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -219,6 +219,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_flushDB arginfo_class_Redis_flushAll +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_function, 0, 0, 1) + ZEND_ARG_INFO(0, operation) + ZEND_ARG_VARIADIC_INFO(0, args) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geoadd, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, lng) @@ -1046,6 +1051,7 @@ ZEND_METHOD(Redis, expiretime); ZEND_METHOD(Redis, pexpiretime); ZEND_METHOD(Redis, flushAll); ZEND_METHOD(Redis, flushDB); +ZEND_METHOD(Redis, function); ZEND_METHOD(Redis, geoadd); ZEND_METHOD(Redis, geodist); ZEND_METHOD(Redis, geohash); @@ -1298,6 +1304,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, function, arginfo_class_Redis_function, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geoadd, arginfo_class_Redis_geoadd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geodist, arginfo_class_Redis_geodist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geohash, arginfo_class_Redis_geohash, ZEND_ACC_PUBLIC) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index b45e13b2e4..67491dde00 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -54,6 +54,7 @@ public function testScanErrors() { return $this->markTestSkipped(); } /* These 'directed node' commands work differently in RedisCluster */ public function testConfig() { return $this->markTestSkipped(); } public function testFlushDB() { return $this->markTestSkipped(); } + public function testFunction() { return $this->markTestSkipped(); } /* Session locking feature is currently not supported in in context of Redis Cluster. The biggest issue for this is the distribution nature of Redis cluster */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 223c81c927..7aa33c150b 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7564,6 +7564,18 @@ public function testCommand() } } + public function testFunction() { + $this->assertTrue($this->redis->function('flush', 'sync')); + $this->assertEquals('mylib', $this->redis->function('load', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)")); + $this->assertEquals('mylib', $this->redis->function('load', 'replace', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)")); + $this->assertEquals($this->redis->function('stats'), ['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]]); + $payload = $this->redis->function('dump'); + $this->assertTrue($this->redis->function('delete', 'mylib')); + $this->assertTrue($this->redis->function('restore', $payload)); + $this->assertEquals($this->redis->function('list'), [['library_name' => 'mylib', 'engine' => 'LUA', 'functions' => [['name' => 'myfunc', 'description' => false,'flags' => []]]]]); + $this->assertTrue($this->redis->function('delete', 'mylib')); + } + /* Make sure we handle a bad option value gracefully */ public function testBadOptionValue() { $this->assertFalse(@$this->redis->setOption(pow(2, 32), false)); From d5678b1276c4c471305181dc830e257a838ebaf9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 22 Dec 2022 16:01:16 +0100 Subject: [PATCH 1760/1986] Clarify laziness of Redis::__construct Is this correct BTW? If yes, this would make it clear. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9103627ce..f630c71356 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ $redis = new Redis(); ~~~ Starting from version 6.0.0 it's possible to specify configuration options. -This allows to connect to the server without explicitly invoking `connect` command. +This allows to connect lazily to the server without explicitly invoking `connect` command. ##### *Example* From 7c46ad2c05381daccd0ed622d051db8691012094 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 24 Dec 2022 13:37:09 +0200 Subject: [PATCH 1761/1986] Issue #2068 Add FCALL/FCALL_RO commands --- redis.c | 10 ++++++++++ redis.stub.php | 28 ++++++++++++++++++++++++++++ redis_arginfo.h | 14 +++++++++++++- redis_commands.c | 39 +++++++++++++++++++++++++++++++++++++++ redis_commands.h | 3 +++ redis_legacy_arginfo.h | 14 +++++++++++++- tests/RedisTest.php | 10 ++++++++-- 7 files changed, 114 insertions(+), 4 deletions(-) diff --git a/redis.c b/redis.c index a89830ba22..121e896d50 100644 --- a/redis.c +++ b/redis.c @@ -2360,6 +2360,16 @@ PHP_METHOD(Redis, evalsha_ro) { REDIS_PROCESS_KW_CMD("EVALSHA_RO", redis_eval_cmd, redis_read_raw_variant_reply); } +/* {{{ proto variant Redis::fcall(string fn [, array keys [, array args]]) */ +PHP_METHOD(Redis, fcall) { + REDIS_PROCESS_KW_CMD("FCALL", redis_fcall_cmd, redis_read_raw_variant_reply); +} + +/* {{{ proto variant Redis::fcall_ro(string fn [, array keys [, array args]]) */ +PHP_METHOD(Redis, fcall_ro) { + REDIS_PROCESS_KW_CMD("FCALL_RO", redis_fcall_cmd, redis_read_raw_variant_reply); +} + /* {{{ public function script($args...): mixed }}} */ PHP_METHOD(Redis, script) { REDIS_PROCESS_CMD(script, redis_read_variant_reply); diff --git a/redis.stub.php b/redis.stub.php index 4624c82e07..f6231e9e8a 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1164,6 +1164,34 @@ public function expiretime(string $key): Redis|int|false; */ public function pexpiretime(string $key): Redis|int|false; + /** + * Invoke a function. + * + * @param string $fn The name of the function + * @param array $keys Optional list of keys + * @param array $args Optional list of args + * + * @return mixed Function may return arbitrary data so this method can return + * strings, arrays, nested arrays, etc. + * + * @see https://redis.io/commands/fcall + */ + public function fcall(string $fn, array $keys = [], array $args = []): mixed; + + /** + * This is a read-only variant of the FCALL command that cannot execute commands that modify data. + * + * @param string $fn The name of the function + * @param array $keys Optional list of keys + * @param array $args Optional list of args + * + * @return mixed Function may return arbitrary data so this method can return + * strings, arrays, nested arrays, etc. + * + * @see https://redis.io/commands/fcall_ro + */ + public function fcall_ro(string $fn, array $keys = [], array $args = []): mixed; + /** * Deletes every key in all Redis databases * diff --git a/redis_arginfo.h b/redis_arginfo.h index 618868204d..1b0f69dd80 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3d369227b8f6d01fffa0ffda01f379f0138fa226 */ + * Stub hash: 600a10da9438d33050be825dff3683399737cc5e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -231,6 +231,14 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_pexpiretime arginfo_class_Redis_expiretime +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_fcall, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, fn, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, keys, IS_ARRAY, 0, "[]") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_fcall_ro arginfo_class_Redis_fcall + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_flushAll, 0, 0, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sync, _IS_BOOL, 1, "null") ZEND_END_ARG_INFO() @@ -1207,6 +1215,8 @@ ZEND_METHOD(Redis, expireAt); ZEND_METHOD(Redis, failover); ZEND_METHOD(Redis, expiretime); ZEND_METHOD(Redis, pexpiretime); +ZEND_METHOD(Redis, fcall); +ZEND_METHOD(Redis, fcall_ro); ZEND_METHOD(Redis, flushAll); ZEND_METHOD(Redis, flushDB); ZEND_METHOD(Redis, function); @@ -1460,6 +1470,8 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expiretime, arginfo_class_Redis_expiretime, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, fcall, arginfo_class_Redis_fcall, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, fcall_ro, arginfo_class_Redis_fcall_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC) ZEND_ME(Redis, function, arginfo_class_Redis_function, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index 3c94f7a450..0c3f747d20 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1029,6 +1029,45 @@ redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int +redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) +{ + HashTable *keys = NULL, *args = NULL; + smart_string cmdstr = {0}; + zend_string *fn = NULL; + zval *zv; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STR(fn) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT(keys) + Z_PARAM_ARRAY_HT(args) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + redis_cmd_init_sstr(&cmdstr, 2 + (keys ? zend_hash_num_elements(keys) : 0) + + (args ? zend_hash_num_elements(args) : 0), kw, strlen(kw)); + redis_cmd_append_sstr_zstr(&cmdstr, fn); + redis_cmd_append_sstr_long(&cmdstr, keys ? zend_hash_num_elements(keys) : 0); + + if (keys != NULL) { + ZEND_HASH_FOREACH_VAL(keys, zv) { + redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); + } ZEND_HASH_FOREACH_END(); + } + + if (args != NULL) { + ZEND_HASH_FOREACH_VAL(args, zv) { + redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); + } ZEND_HASH_FOREACH_END(); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) diff --git a/redis_commands.h b/redis_commands.h index 8f78800b08..3165ba0eab 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -162,6 +162,9 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 2c90eaa27c..c3656b75e0 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3d369227b8f6d01fffa0ffda01f379f0138fa226 */ + * Stub hash: 600a10da9438d33050be825dff3683399737cc5e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -213,6 +213,14 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_pexpiretime arginfo_class_Redis__prefix +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_fcall, 0, 0, 1) + ZEND_ARG_INFO(0, fn) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, args) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_fcall_ro arginfo_class_Redis_fcall + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, 0) ZEND_ARG_INFO(0, sync) ZEND_END_ARG_INFO() @@ -1049,6 +1057,8 @@ ZEND_METHOD(Redis, expireAt); ZEND_METHOD(Redis, failover); ZEND_METHOD(Redis, expiretime); ZEND_METHOD(Redis, pexpiretime); +ZEND_METHOD(Redis, fcall); +ZEND_METHOD(Redis, fcall_ro); ZEND_METHOD(Redis, flushAll); ZEND_METHOD(Redis, flushDB); ZEND_METHOD(Redis, function); @@ -1302,6 +1312,8 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expiretime, arginfo_class_Redis_expiretime, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, fcall, arginfo_class_Redis_fcall, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, fcall_ro, arginfo_class_Redis_fcall_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC) ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC) ZEND_ME(Redis, function, arginfo_class_Redis_function, ZEND_ACC_PUBLIC) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 7aa33c150b..3e9838af9a 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7565,11 +7565,17 @@ public function testCommand() } public function testFunction() { + if (version_compare($this->version, '7.0') < 0) { + $this->markTestSkipped(); + return; + } $this->assertTrue($this->redis->function('flush', 'sync')); $this->assertEquals('mylib', $this->redis->function('load', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)")); - $this->assertEquals('mylib', $this->redis->function('load', 'replace', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)")); - $this->assertEquals($this->redis->function('stats'), ['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]]); + $this->assertEquals('foo', $this->redis->fcall('myfunc', [], ['foo'])); $payload = $this->redis->function('dump'); + $this->assertEquals('mylib', $this->redis->function('load', 'replace', "#!lua name=mylib\nredis.register_function{function_name='myfunc', callback=function(keys, args) return args[1] end, flags={'no-writes'}}")); + $this->assertEquals('foo', $this->redis->fcall_ro('myfunc', [], ['foo'])); + $this->assertEquals($this->redis->function('stats'), ['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]]); $this->assertTrue($this->redis->function('delete', 'mylib')); $this->assertTrue($this->redis->function('restore', $payload)); $this->assertEquals($this->redis->function('list'), [['library_name' => 'mylib', 'engine' => 'LUA', 'functions' => [['name' => 'myfunc', 'description' => false,'flags' => []]]]]); From 77c4f7a36b2c784fa596b5b2f4f205638cfa653e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 26 Dec 2022 14:56:05 +0200 Subject: [PATCH 1762/1986] Refactor CLIENT command --- redis_commands.c | 184 +++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 101 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 3c94f7a450..bc6aff3032 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -4795,11 +4795,11 @@ redis_build_client_list_command(smart_string *cmdstr, int argc, zval *z_args) zend_string *zkey; zval *z_ele, *type = NULL, *id = NULL; - if (argc > 1) { - if (Z_TYPE(z_args[1]) != IS_ARRAY) { + if (argc > 0) { + if (Z_TYPE(z_args[0]) != IS_ARRAY) { return FAILURE; } - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[0]), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "type")) { @@ -4858,17 +4858,17 @@ redis_build_client_kill_command(smart_string *cmdstr, int argc, zval *z_args) zval *z_ele, *id = NULL, *type = NULL, *address = NULL, *opts = NULL, *user = NULL, *addr = NULL, *laddr = NULL, *skipme = NULL; - if (argc > 1) { - if (argc > 2) { - if (Z_TYPE(z_args[1]) != IS_STRING || Z_TYPE(z_args[2]) != IS_ARRAY) { + if (argc > 0) { + if (argc > 1) { + if (Z_TYPE(z_args[0]) != IS_STRING || Z_TYPE(z_args[1]) != IS_ARRAY) { return FAILURE; } - address = &z_args[1]; - opts = &z_args[2]; - } else if (Z_TYPE(z_args[1]) == IS_STRING) { - address = &z_args[1]; - } else if (Z_TYPE(z_args[1]) == IS_ARRAY) { + address = &z_args[0]; opts = &z_args[1]; + } else if (Z_TYPE(z_args[0]) == IS_STRING) { + address = &z_args[0]; + } else if (Z_TYPE(z_args[0]) == IS_ARRAY) { + opts = &z_args[0]; } else { return FAILURE; } @@ -4950,14 +4950,14 @@ redis_build_client_tracking_command(smart_string *cmdstr, int argc, zval *z_args zval *z_ele, *redirect = NULL, *prefix = NULL; zend_bool bcast = 0, optin = 0, optout = 0, noloop = 0; - if (argc < 2) { + if (argc < 1) { return FAILURE; } - if (argc > 2) { - if (Z_TYPE(z_args[2]) != IS_ARRAY) { + if (argc > 1) { + if (Z_TYPE(z_args[1]) != IS_ARRAY) { return FAILURE; } - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[2]), zkey, z_ele) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "redirect")) { @@ -4986,12 +4986,12 @@ redis_build_client_tracking_command(smart_string *cmdstr, int argc, zval *z_args + (prefix ? 2 * zend_hash_num_elements(Z_ARRVAL_P(prefix)) : 0) + bcast + optin + optout + noloop, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TRACKING"); - if (Z_TYPE(z_args[1]) == IS_STRING && ( - ZVAL_STRICMP_STATIC(&z_args[1], "on") || - ZVAL_STRICMP_STATIC(&z_args[1], "off") + if (Z_TYPE(z_args[0]) == IS_STRING && ( + ZVAL_STRICMP_STATIC(&z_args[0], "on") || + ZVAL_STRICMP_STATIC(&z_args[0], "off") )) { - redis_cmd_append_sstr(cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); - } else if (zval_is_true(&z_args[1])) { + redis_cmd_append_sstr(cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + } else if (zval_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ON"); } else { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OFF"); @@ -5036,161 +5036,146 @@ int redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - int argc; smart_string cmdstr = {0}; - zval *z_args; - - if ((argc = ZEND_NUM_ARGS()) < 1) { - return FAILURE; - } + zend_string *op = NULL; + zval *z_args = NULL; + int argc = 0; - z_args = ecalloc(argc, sizeof(*z_args)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || - Z_TYPE(z_args[0]) != IS_STRING - ) { - efree(z_args); - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_STR(op) + Z_PARAM_OPTIONAL + Z_PARAM_VARIADIC('*', z_args, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - if (ZVAL_STRICMP_STATIC(&z_args[0], "info")) { + if (zend_string_equals_literal_ci(op, "INFO")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "INFO"); - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "list")) { + } else if (zend_string_equals_literal_ci(op, "LIST")) { if (redis_build_client_list_command(&cmdstr, argc, z_args) != 0) { - efree(z_args); return FAILURE; } *ctx = PHPREDIS_CTX_PTR; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "caching")) { - if (argc < 2) { - efree(z_args); + } else if (zend_string_equals_literal_ci(op, "CACHING")) { + if (argc < 1) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "CACHING"); - if (Z_TYPE(z_args[1]) == IS_STRING && ( - ZVAL_STRICMP_STATIC(&z_args[1], "yes") || - ZVAL_STRICMP_STATIC(&z_args[1], "no") + if (Z_TYPE(z_args[0]) == IS_STRING && ( + ZVAL_STRICMP_STATIC(&z_args[0], "yes") || + ZVAL_STRICMP_STATIC(&z_args[0], "no") )) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); - } else if (zval_is_true(&z_args[1])) { + redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + } else if (zval_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "YES"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO"); } *ctx = PHPREDIS_CTX_PTR + 1; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "getname")) { + } else if (zend_string_equals_literal_ci(op, "GETNAME")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GETNAME"); *ctx = PHPREDIS_CTX_PTR + 3; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "getredir") || ZVAL_STRICMP_STATIC(&z_args[0], "id")) { + } else if (zend_string_equals_literal_ci(op, "GETREDIR") || zend_string_equals_literal_ci(op, "ID")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(op), ZSTR_LEN(op)); *ctx = PHPREDIS_CTX_PTR + 2; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "kill")) { + } else if (zend_string_equals_literal_ci(op, "KILL")) { if (redis_build_client_kill_command(&cmdstr, argc, z_args) != 0) { - efree(z_args); return FAILURE; } *ctx = PHPREDIS_CTX_PTR + 1; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "no-evict")) { - if (argc < 2) { - efree(z_args); + } else if (zend_string_equals_literal_ci(op, "NO-EVICT")) { + if (argc < 1) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO-EVICT"); - if (Z_TYPE(z_args[1]) == IS_STRING && ( - ZVAL_STRICMP_STATIC(&z_args[1], "on") || - ZVAL_STRICMP_STATIC(&z_args[1], "off") + if (Z_TYPE(z_args[0]) == IS_STRING && ( + ZVAL_STRICMP_STATIC(&z_args[0], "on") || + ZVAL_STRICMP_STATIC(&z_args[0], "off") )) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); - } else if (zval_is_true(&z_args[1])) { + redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + } else if (zval_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ON"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "OFF"); } *ctx = PHPREDIS_CTX_PTR + 1; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "pause")) { - if (argc < 2 || Z_TYPE(z_args[1]) != IS_LONG || ( - argc > 2 && ( - Z_TYPE(z_args[2]) != IS_STRING || ( - !ZVAL_STRICMP_STATIC(&z_args[2], "write") && - !ZVAL_STRICMP_STATIC(&z_args[2], "all") + } else if (zend_string_equals_literal_ci(op, "PAUSE")) { + if (argc < 1 || Z_TYPE(z_args[0]) != IS_LONG || ( + argc > 1 && ( + Z_TYPE(z_args[1]) != IS_STRING || ( + !ZVAL_STRICMP_STATIC(&z_args[1], "write") && + !ZVAL_STRICMP_STATIC(&z_args[1], "all") ) ) )) { - efree(z_args); return FAILURE; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 2 ? 3 : 2, "CLIENT"); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 1 ? 3 : 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "PAUSE"); - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(z_args[1])); - if (argc > 2) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[2]), Z_STRLEN(z_args[2])); + redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(z_args[0])); + if (argc > 1) { + redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } *ctx = PHPREDIS_CTX_PTR + 1; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "reply")) { - if (argc > 1 && ( - Z_TYPE(z_args[1]) != IS_STRING || ( - !ZVAL_STRICMP_STATIC(&z_args[1], "on") && - !ZVAL_STRICMP_STATIC(&z_args[1], "off") && - !ZVAL_STRICMP_STATIC(&z_args[1], "skip") + } else if (zend_string_equals_literal_ci(op, "REPLY")) { + if (argc > 0 && ( + Z_TYPE(z_args[0]) != IS_STRING || ( + !ZVAL_STRICMP_STATIC(&z_args[0], "on") && + !ZVAL_STRICMP_STATIC(&z_args[0], "off") && + !ZVAL_STRICMP_STATIC(&z_args[0], "skip") ) )) { - efree(z_args); return FAILURE; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 1 ? 2 : 1, "CLIENT"); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 0 ? 2 : 1, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REPLY"); - if (argc > 1) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); + if (argc > 0) { + redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } *ctx = PHPREDIS_CTX_PTR + 1; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "setname")) { - if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING) { - efree(z_args); + } else if (zend_string_equals_literal_ci(op, "SETNAME")) { + if (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING) { return FAILURE; } REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SETNAME"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); *ctx = PHPREDIS_CTX_PTR + 1; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "tracking")) { + } else if (zend_string_equals_literal_ci(op, "TRACKING")) { if (redis_build_client_tracking_command(&cmdstr, argc, z_args) != 0) { - efree(z_args); return FAILURE; } *ctx = PHPREDIS_CTX_PTR + 1; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "trackinginfo")) { + } else if (zend_string_equals_literal_ci(op, "TRACKINGINFO")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TRACKINGINFO"); *ctx = PHPREDIS_CTX_PTR + 4; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "unblock")) { - if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING || ( - argc > 2 && ( - Z_TYPE(z_args[2]) != IS_STRING || ( - !ZVAL_STRICMP_STATIC(&z_args[2], "timeout") && - !ZVAL_STRICMP_STATIC(&z_args[2], "error") + } else if (zend_string_equals_literal_ci(op, "UNBLOCK")) { + if (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING || ( + argc > 1 && ( + Z_TYPE(z_args[1]) != IS_STRING || ( + !ZVAL_STRICMP_STATIC(&z_args[1], "timeout") && + !ZVAL_STRICMP_STATIC(&z_args[1], "error") ) ) )) { - efree(z_args); return FAILURE; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 2 ? 3 : 2, "CLIENT"); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 1 ? 3 : 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "UNBLOCK"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); - if (argc > 2) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[2]), Z_STRLEN(z_args[2])); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + if (argc > 1) { + redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } *ctx = PHPREDIS_CTX_PTR + 2; - } else if (ZVAL_STRICMP_STATIC(&z_args[0], "unpause")) { + } else if (zend_string_equals_literal_ci(op, "UNPAUSE")) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "UNPAUSE"); *ctx = PHPREDIS_CTX_PTR + 1; } else { - efree(z_args); return FAILURE; } @@ -5198,9 +5183,6 @@ redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd = cmdstr.c; *cmd_len = cmdstr.len; - // Cleanup arg array - efree(z_args); - return SUCCESS; } From f14a80db9a4bef6804b9d2506a807ff0e5806729 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 4 Feb 2023 12:33:43 +0200 Subject: [PATCH 1763/1986] Refactor redis_long_response --- library.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/library.c b/library.c index 08bd81a954..80ea088e8f 100644 --- a/library.c +++ b/library.c @@ -1624,40 +1624,30 @@ PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL || *response != TYPE_INT) { if (IS_ATOMIC(redis_sock)) { RETVAL_FALSE; } else { add_next_index_bool(z_tab, 0); } - + if (response) efree(response); return FAILURE; } - if(response[0] == ':') { - int64_t ret = phpredis_atoi64(response + 1); + int64_t ret = phpredis_atoi64(response + 1); - if (IS_ATOMIC(redis_sock)) { - if(ret > LONG_MAX) { /* overflow */ - RETVAL_STRINGL(response + 1, response_len - 1); - } else { - RETVAL_LONG((long)ret); - } + if (IS_ATOMIC(redis_sock)) { + if (ret > LONG_MAX) { /* overflow */ + RETVAL_STRINGL(response + 1, response_len - 1); } else { - if(ret > LONG_MAX) { /* overflow */ - add_next_index_stringl(z_tab, response + 1, response_len - 1); - } else { - add_next_index_long(z_tab, (long)ret); - } + RETVAL_LONG((long)ret); } } else { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; + if (ret > LONG_MAX) { /* overflow */ + add_next_index_stringl(z_tab, response + 1, response_len - 1); } else { - add_next_index_null(z_tab); + add_next_index_long(z_tab, (long)ret); } - efree(response); - return FAILURE; } efree(response); @@ -3376,8 +3366,10 @@ redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int coun return FAILURE; /* This can vary */ - if (redis_read_reply_type(redis_sock, &type, &vallen) < 0) + if (redis_read_reply_type(redis_sock, &type, &vallen) < 0) { + efree(key); return FAILURE; + } if (type == TYPE_BULK) { if (vallen > INT_MAX || (val = redis_sock_read_bulk_reply(redis_sock, (int)vallen)) == NULL) { From 6b8d682ea51e5c813e2b32ec6c838358aa8997ad Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 4 Feb 2023 12:47:13 +0200 Subject: [PATCH 1764/1986] Update cluster.md --- cluster.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cluster.md b/cluster.md index 3fd9fb16a6..484bc76a84 100644 --- a/cluster.md +++ b/cluster.md @@ -21,6 +21,10 @@ $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, // Connect with cluster using password. $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, "password"); + +// Connect with cluster using SSL/TLS +// last argument is an array with [SSL context](https://www.php.net/manual/en/context.ssl.php) options +$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, NULL, Array("verify_peer" => false)); ``` #### Loading a cluster configuration by name @@ -201,4 +205,4 @@ To enable, set the following INI variable: ```ini redis.session.early_refresh = 1 ``` -Note: This is disabled by default since it may significantly reduce the session lifetime for long-running scripts. Redis server version 6.2+ required. \ No newline at end of file +Note: This is disabled by default since it may significantly reduce the session lifetime for long-running scripts. Redis server version 6.2+ required. From d9cb594678ea5f1cc0f3dd7aabe2380750e0cbd2 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 11 Feb 2023 22:36:43 +0200 Subject: [PATCH 1765/1986] Fix redis_sock_read_multibulk_multi_reply_loop logic --- php_redis.h | 3 +-- redis.c | 47 +++++++++++++++++++++-------------------------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/php_redis.h b/php_redis.h index 97ef3b5515..c79367b4a6 100644 --- a/php_redis.h +++ b/php_redis.h @@ -50,8 +50,7 @@ PHP_REDIS_API int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); PHP_REDIS_API int redis_response_enqueued(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop( - INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, - int numElems); + INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab); extern zend_module_entry redis_module_entry; diff --git a/redis.c b/redis.c index 121e896d50..0eb91eaccc 100644 --- a/redis.c +++ b/redis.c @@ -2021,8 +2021,8 @@ PHP_METHOD(Redis, discard) RETURN_FALSE; } -/* redis_sock_read_multibulk_multi_reply */ -PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, +PHP_REDIS_API int +redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { @@ -2030,23 +2030,16 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAME int numElems; size_t len; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { - return - 1; - } - - /* number of responses */ - numElems = atoi(inbuf+1); - - if(numElems < 0) { - return -1; + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || + *inbuf != TYPE_MULTIBULK || atoi(inbuf + 1) < 0 + ) { + return FAILURE; } array_init(return_value); - redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, return_value, numElems); - - return 0; + return redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, return_value); } @@ -2098,8 +2091,11 @@ PHP_METHOD(Redis, exec) ZVAL_FALSE(return_value); } else { array_init(return_value); - redis_sock_read_multibulk_multi_reply_loop( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, return_value, 0); + if (redis_sock_read_multibulk_multi_reply_loop( + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, return_value) != SUCCESS) { + zval_dtor(return_value); + RETVAL_FALSE; + } } zend_string_release(redis_sock->pipeline_cmd); redis_sock->pipeline_cmd = NULL; @@ -2124,12 +2120,9 @@ redis_response_enqueued(RedisSock *redis_sock) return ret; } -/* TODO: Investigate/fix the odd logic going on in here. Looks like previous abort - * conditions that are now simply empty if { } { } blocks. */ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, - int numElems) + RedisSock *redis_sock, zval *z_tab) { fold_item *fi; @@ -2142,17 +2135,18 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, size_t len; char inbuf[255]; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { - } else if (strncmp(inbuf, "+OK", 3) != 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "+OK", 3) != 0) { + return FAILURE; } while ((fi = fi->next) && fi->fun) { - if (redis_response_enqueued(redis_sock) == SUCCESS) { - } else { + if (redis_response_enqueued(redis_sock) != SUCCESS) { + return FAILURE; } } if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { + return FAILURE; } zval z_ret; @@ -2162,12 +2156,13 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, int num = atol(inbuf + 1); if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) < 0) { + return FAILURE; } if (fi) fi = fi->next; } redis_sock->current = fi; - return 0; + return SUCCESS; } PHP_METHOD(Redis, pipeline) From ebb2386e52c9ddd8a45e3cd67f12d77be894e1d7 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 12 Feb 2023 22:33:45 +0200 Subject: [PATCH 1766/1986] Synchronize Redis and RedisSentinel constructors --- library.c | 74 +++++++++++++++++++++++++++++ library.h | 1 + redis.c | 82 ++++----------------------------- redis_sentinel.c | 61 ++++-------------------- redis_sentinel.h | 2 +- redis_sentinel.stub.php | 2 +- redis_sentinel_arginfo.h | 20 ++------ redis_sentinel_legacy_arginfo.h | 15 ++---- tests/RedisSentinelTest.php | 2 +- 9 files changed, 105 insertions(+), 154 deletions(-) diff --git a/library.c b/library.c index 80ea088e8f..0473f326c4 100644 --- a/library.c +++ b/library.c @@ -2749,6 +2749,80 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock } } +PHP_REDIS_API int +redis_sock_configure(RedisSock *redis_sock, HashTable *opts) +{ + zend_string *zkey; + zval *val; + + ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zkey, val) { + if (zkey == NULL) { + continue; + } + ZVAL_DEREF(val); + if (zend_string_equals_literal_ci(zkey, "host")) { + if (Z_TYPE_P(val) != IS_STRING) { + REDIS_VALUE_EXCEPTION("Invalid host"); + return FAILURE; + } + if (redis_sock->host) zend_string_release(redis_sock->host); + redis_sock->host = zval_get_string(val); + } else if (zend_string_equals_literal_ci(zkey, "port")) { + if (Z_TYPE_P(val) != IS_LONG) { + REDIS_VALUE_EXCEPTION("Invalid port"); + return FAILURE; + } + redis_sock->port = zval_get_long(val); + } else if (zend_string_equals_literal_ci(zkey, "connectTimeout")) { + if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { + REDIS_VALUE_EXCEPTION("Invalid connect timeout"); + return FAILURE; + } + redis_sock->timeout = zval_get_double(val); + } else if (zend_string_equals_literal_ci(zkey, "readTimeout")) { + if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { + REDIS_VALUE_EXCEPTION("Invalid read timeout"); + return FAILURE; + } + redis_sock->read_timeout = zval_get_double(val); + } else if (zend_string_equals_literal_ci(zkey, "persistent")) { + if (Z_TYPE_P(val) == IS_STRING) { + if (redis_sock->persistent_id) zend_string_release(redis_sock->persistent_id); + redis_sock->persistent_id = zval_get_string(val); + redis_sock->persistent = 1; + } else { + redis_sock->persistent = zval_is_true(val); + } + } else if (zend_string_equals_literal_ci(zkey, "retryInterval")) { + if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { + REDIS_VALUE_EXCEPTION("Invalid retry interval"); + return FAILURE; + } + redis_sock->retry_interval = zval_get_long(val); + } else if (zend_string_equals_literal_ci(zkey, "ssl")) { + if (redis_sock_set_stream_context(redis_sock, val) != SUCCESS) { + REDIS_VALUE_EXCEPTION("Invalid SSL context options"); + return FAILURE; + } + } else if (zend_string_equals_literal_ci(zkey, "auth")) { + if (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY) { + REDIS_VALUE_EXCEPTION("Invalid auth credentials"); + return FAILURE; + } + redis_sock_set_auth_zval(redis_sock, val); + } else if (zend_string_equals_literal_ci(zkey, "backoff")) { + if (redis_sock_set_backoff(redis_sock, val) != SUCCESS) { + REDIS_VALUE_EXCEPTION("Invalid backoff options"); + return FAILURE; + } + } else { + php_error_docref(NULL, E_WARNING, "Skip unknown option '%s'", ZSTR_VAL(zkey)); + } + } ZEND_HASH_FOREACH_END(); + + return SUCCESS; +} + /** * redis_sock_create */ diff --git a/library.h b/library.h index f1601e99a6..d93ce05405 100644 --- a/library.h +++ b/library.h @@ -75,6 +75,7 @@ PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, int port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); +PHP_REDIS_API int redis_sock_configure(RedisSock *redis_sock, HashTable *opts); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock); diff --git a/redis.c b/redis.c index 121e896d50..1a451af0e1 100644 --- a/redis.c +++ b/redis.c @@ -455,82 +455,18 @@ PHP_MINFO_FUNCTION(redis) Public constructor */ PHP_METHOD(Redis, __construct) { + HashTable *opts = NULL; redis_object *redis; - zend_string *zkey; - zval *val, *opts = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a", &opts) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(opts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_THROWS()); - if (opts != NULL) { - redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, getThis()); - redis->sock = redis_sock_create("127.0.0.1", sizeof("127.0.0.1") - 1, 6379, 0, 0, 0, NULL, 0); - - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, val) { - if (zkey == NULL) { - continue; - } - ZVAL_DEREF(val); - if (zend_string_equals_literal_ci(zkey, "host")) { - if (Z_TYPE_P(val) != IS_STRING) { - REDIS_VALUE_EXCEPTION("Invalid host"); - RETURN_THROWS(); - } - zend_string_release(redis->sock->host); - redis->sock->host = zval_get_string(val); - } else if (zend_string_equals_literal_ci(zkey, "port")) { - if (Z_TYPE_P(val) != IS_LONG) { - REDIS_VALUE_EXCEPTION("Invalid port"); - RETURN_THROWS(); - } - redis->sock->port = zval_get_long(val); - } else if (zend_string_equals_literal_ci(zkey, "connectTimeout")) { - if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { - REDIS_VALUE_EXCEPTION("Invalid connect timeout"); - RETURN_THROWS(); - } - redis->sock->timeout = zval_get_double(val); - } else if (zend_string_equals_literal_ci(zkey, "readTimeout")) { - if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { - REDIS_VALUE_EXCEPTION("Invalid read timeout"); - RETURN_THROWS(); - } - redis->sock->read_timeout = zval_get_double(val); - } else if (zend_string_equals_literal_ci(zkey, "persistent")) { - if (Z_TYPE_P(val) == IS_STRING) { - if (redis->sock->persistent_id) zend_string_release(redis->sock->persistent_id); - redis->sock->persistent_id = zval_get_string(val); - redis->sock->persistent = 1; - } else { - redis->sock->persistent = zval_is_true(val); - } - } else if (zend_string_equals_literal_ci(zkey, "retryInterval")) { - if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { - REDIS_VALUE_EXCEPTION("Invalid retry interval"); - RETURN_THROWS(); - } - redis->sock->retry_interval = zval_get_long(val); - } else if (zend_string_equals_literal_ci(zkey, "ssl")) { - if (redis_sock_set_stream_context(redis->sock, val) != SUCCESS) { - REDIS_VALUE_EXCEPTION("Invalid SSL context options"); - RETURN_THROWS(); - } - } else if (zend_string_equals_literal_ci(zkey, "auth")) { - if (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY) { - REDIS_VALUE_EXCEPTION("Invalid auth credentials"); - RETURN_THROWS(); - } - redis_sock_set_auth_zval(redis->sock, val); - } else if (zend_string_equals_literal_ci(zkey, "backoff")) { - if (redis_sock_set_backoff(redis->sock, val) != SUCCESS) { - REDIS_VALUE_EXCEPTION("Invalid backoff options"); - RETURN_THROWS(); - } - } else { - php_error_docref(NULL, E_WARNING, "Skip unknown option '%s'", ZSTR_VAL(zkey)); - } - } ZEND_HASH_FOREACH_END(); + redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, getThis()); + redis->sock = redis_sock_create(ZEND_STRL("127.0.0.1"), 6379, 0, 0, 0, NULL, 0); + if (opts != NULL && redis_sock_configure(redis->sock, opts) != SUCCESS) { + RETURN_THROWS(); } } /* }}} */ diff --git a/redis_sentinel.c b/redis_sentinel.c index 1a4a05f480..fdaa191f45 100644 --- a/redis_sentinel.c +++ b/redis_sentinel.c @@ -41,61 +41,20 @@ PHP_MINIT_FUNCTION(redis_sentinel) PHP_METHOD(RedisSentinel, __construct) { - int persistent = 0; - char *persistent_id = NULL; - double timeout = 0.0, read_timeout = 0.0; - zend_long port = 26379, retry_interval = 0; - redis_sentinel_object *obj; - zend_string *host; - zval *auth = NULL, *context = NULL, *zv = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ldz!ldza", - &host, &port, &timeout, &zv, - &retry_interval, &read_timeout, - &auth, &context) == FAILURE) { - RETURN_FALSE; - } - - if (port < 0 || port > UINT16_MAX) { - REDIS_VALUE_EXCEPTION("Invalid port"); - RETURN_THROWS(); - } - - if (timeout > INT_MAX) { - REDIS_VALUE_EXCEPTION("Invalid connect timeout"); - RETURN_THROWS(); - } + HashTable *opts = NULL; + redis_sentinel_object *sentinel; - if (read_timeout > INT_MAX) { - REDIS_VALUE_EXCEPTION("Invalid read timeout"); - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(opts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_THROWS()); - if (retry_interval < 0L || retry_interval > INT_MAX) { - REDIS_VALUE_EXCEPTION("Invalid retry interval"); + sentinel = PHPREDIS_ZVAL_GET_OBJECT(redis_sentinel_object, getThis()); + sentinel->sock = redis_sock_create(ZEND_STRL("127.0.0.1"), 26379, 0, 0, 0, NULL, 0); + if (opts != NULL && redis_sock_configure(sentinel->sock, opts) != SUCCESS) { RETURN_THROWS(); } - - if (zv) { - ZVAL_DEREF(zv); - if (Z_TYPE_P(zv) == IS_STRING) { - persistent_id = Z_STRVAL_P(zv); - persistent = 1; /* even empty */ - } else { - persistent = zval_is_true(zv); - } - } - - obj = PHPREDIS_ZVAL_GET_OBJECT(redis_sentinel_object, getThis()); - obj->sock = redis_sock_create(ZSTR_VAL(host), ZSTR_LEN(host), port, - timeout, read_timeout, persistent, persistent_id, retry_interval); - if (auth) { - redis_sock_set_auth_zval(obj->sock, auth); - } - if (context) { - redis_sock_set_stream_context(obj->sock, context); - } - obj->sock->sentinel = 1; + sentinel->sock->sentinel = 1; } PHP_METHOD(RedisSentinel, ckquorum) diff --git a/redis_sentinel.h b/redis_sentinel.h index 0878b62d41..19a86ccfd4 100644 --- a/redis_sentinel.h +++ b/redis_sentinel.h @@ -3,7 +3,7 @@ #include "sentinel_library.h" -#define PHP_REDIS_SENTINEL_VERSION "0.1" +#define PHP_REDIS_SENTINEL_VERSION "1.0" extern zend_class_entry *redis_sentinel_ce; extern PHP_MINIT_FUNCTION(redis_sentinel); diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php index 6ec54d8549..486ac438ac 100644 --- a/redis_sentinel.stub.php +++ b/redis_sentinel.stub.php @@ -8,7 +8,7 @@ class RedisSentinel { - public function __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = null, int $retry_interval = 0, float $read_timeout = 0, #[\SensitiveParameter] mixed $auth = null, array $context = null); + public function __construct(array $options = null); /** @return bool|RedisSentinel */ public function ckquorum(string $master); diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h index 43c57e9b1a..e917b7d5ba 100644 --- a/redis_sentinel_arginfo.h +++ b/redis_sentinel_arginfo.h @@ -1,15 +1,8 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 847c735dfbbb643366344acfe6e2c5e8b76d0520 */ - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "26379") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, IS_MIXED, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "null") + * Stub hash: f1f746cc848b1debcdf88eae015732720ba206c8 */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1) @@ -77,11 +70,6 @@ static zend_class_entry *register_class_RedisSentinel(void) INIT_CLASS_ENTRY(ce, "RedisSentinel", class_RedisSentinel_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); -#if (PHP_VERSION_ID >= 80200) - - - zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 6, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); -#endif return class_entry; } diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h index 07f34be98f..5f7a70d26d 100644 --- a/redis_sentinel_legacy_arginfo.h +++ b/redis_sentinel_legacy_arginfo.h @@ -1,15 +1,8 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 847c735dfbbb643366344acfe6e2c5e8b76d0520 */ - -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, persistent) - ZEND_ARG_INFO(0, retry_interval) - ZEND_ARG_INFO(0, read_timeout) - ZEND_ARG_INFO(0, auth) - ZEND_ARG_INFO(0, context) + * Stub hash: f1f746cc848b1debcdf88eae015732720ba206c8 */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 0) + ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1) diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php index 2d188b3c2f..0fdc3a957e 100644 --- a/tests/RedisSentinelTest.php +++ b/tests/RedisSentinelTest.php @@ -30,7 +30,7 @@ class Redis_Sentinel_Test extends TestSuite protected function newInstance() { - return new RedisSentinel($this->getHost()); + return new RedisSentinel(['host' => $this->getHost()]); } public function setUp() From 5a643b62d2177a009ab595ac987c97443eda44d5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 14 Feb 2023 20:38:22 +0200 Subject: [PATCH 1767/1986] Use didicated zval to store result --- redis.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/redis.c b/redis.c index 0eb91eaccc..59ca881046 100644 --- a/redis.c +++ b/redis.c @@ -2023,11 +2023,10 @@ PHP_METHOD(Redis, discard) PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock) + RedisSock *redis_sock, zval *z_tab) { char inbuf[4096]; - int numElems; size_t len; if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || @@ -2036,10 +2035,10 @@ redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, return FAILURE; } - array_init(return_value); + array_init(z_tab); return redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, return_value); + redis_sock, z_tab); } @@ -2049,7 +2048,7 @@ PHP_METHOD(Redis, exec) RedisSock *redis_sock; char *cmd; int cmd_len, ret; - zval *object; + zval *object, z_ret; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE || @@ -2070,31 +2069,32 @@ PHP_METHOD(Redis, exec) SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) efree(cmd); + ZVAL_NULL(&z_ret); ret = redis_sock_read_multibulk_multi_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret); free_reply_callbacks(redis_sock); REDIS_DISABLE_MODE(redis_sock, MULTI); redis_sock->watching = 0; if (ret < 0) { - zval_dtor(return_value); - RETURN_FALSE; + zval_dtor(&z_ret); + ZVAL_FALSE(&z_ret); } } if (IS_PIPELINE(redis_sock)) { if (redis_sock->pipeline_cmd == NULL) { /* Empty array when no command was run. */ - array_init(return_value); + array_init(&z_ret); } else { if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd), ZSTR_LEN(redis_sock->pipeline_cmd)) < 0) { - ZVAL_FALSE(return_value); + ZVAL_FALSE(&z_ret); } else { - array_init(return_value); + array_init(&z_ret); if (redis_sock_read_multibulk_multi_reply_loop( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, return_value) != SUCCESS) { - zval_dtor(return_value); - RETVAL_FALSE; + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret) != SUCCESS) { + zval_dtor(&z_ret); + ZVAL_FALSE(&z_ret); } } zend_string_release(redis_sock->pipeline_cmd); @@ -2103,6 +2103,7 @@ PHP_METHOD(Redis, exec) free_reply_callbacks(redis_sock); REDIS_DISABLE_MODE(redis_sock, PIPELINE); } + RETURN_ZVAL(&z_ret, 1, 0); } PHP_REDIS_API int From 02c91d59cb9c21e465f89ae908e39a72775f9c9d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 23 Feb 2023 14:29:46 -0800 Subject: [PATCH 1768/1986] Fix RPOP to unserialize/decompress data. Fixes #2329 --- cluster_library.c | 2 +- library.c | 2 +- tests/RedisTest.php | 37 ++++++++++++++++++++++--------------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 520a228f0c..0861551ef3 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1767,7 +1767,7 @@ cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) if (ctx == NULL) { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { - cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { ZEND_ASSERT(!"memory corruption?"); } diff --git a/library.c b/library.c index 80ea088e8f..10ec1ad2cb 100644 --- a/library.c +++ b/library.c @@ -1488,7 +1488,7 @@ redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ if (ctx == NULL) { return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 3e9838af9a..45180dfa4c 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1137,18 +1137,13 @@ public function testlPop() // 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->redis->lPush('list', 'val3'); - $this->assertEquals('val2', $this->redis->rPop('list')); + $this->assertEquals('val2', $this->redis->rPop('list')); if (version_compare($this->version, "6.2.0") < 0) { $this->assertEquals('val', $this->redis->rPop('list')); $this->assertEquals('val3', $this->redis->rPop('list')); @@ -1157,17 +1152,29 @@ public function testrPop() } $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->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'))); + } - $this->assertEquals('val3', gzuncompress($this->redis->rPop('list'))); - $this->assertEquals('val2', gzuncompress($this->redis->rPop('list'))); - $this->assertEquals('val1', gzuncompress($this->redis->rPop('list'))); + /* Regression test for GH #2329 */ + public function testrPopSerialization() { + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + $this->redis->del('rpopkey'); + $this->redis->rpush('rpopkey', ['foo'], ['bar']); + $this->assertEquals([['bar'], ['foo']], $this->redis->rpop('rpopkey', 2)); + + $this->redis->rpush('rpopkey', ['foo'], ['bar']); + $this->assertEquals([['foo'], ['bar']], $this->redis->lpop('rpopkey', 2)); + + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); } public function testblockingPop() { From 71ce6dd34cacc84f0b6b906bebe2ed8d36511003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 14 Jan 2023 11:36:36 +0000 Subject: [PATCH 1769/1986] Fix documentation branch main is no more --- docs/PROJECT_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PROJECT_VERSION b/docs/PROJECT_VERSION index 88d050b190..6563189c54 100644 --- a/docs/PROJECT_VERSION +++ b/docs/PROJECT_VERSION @@ -1 +1 @@ -main \ No newline at end of file +develop From ccd419a4c8e23af1ac55aa8a70f0b44387ef5b2c Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 1 Mar 2023 11:15:10 -0800 Subject: [PATCH 1770/1986] Small refactor of some methods * Use our `redis_cmd_append_sstr_key_*` and `redis_cmd_append_sstr_zval` wrappers, which handle key prefixing and serialization transparently. * Rework ZADD so it can handle the bulk double response from the `INCR` options. --- cluster_library.c | 12 ++ cluster_library.h | 2 + library.c | 12 ++ library.h | 1 + redis.c | 2 +- redis.stub.php | 4 +- redis_arginfo.h | 4 +- redis_cluster.c | 2 +- redis_cluster.stub.php | 2 +- redis_cluster_arginfo.h | 4 +- redis_cluster_legacy_arginfo.h | 2 +- redis_commands.c | 235 +++++++++------------------------ redis_legacy_arginfo.h | 2 +- tests/RedisTest.php | 9 ++ 14 files changed, 108 insertions(+), 185 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 0861551ef3..24c8a9142c 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1819,6 +1819,18 @@ cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { } } +PHP_REDIS_API void +cluster_zadd_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + + if (ctx == NULL) { + cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + cluster_dbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } +} + + PHP_REDIS_API void cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { if (ctx == NULL) { diff --git a/cluster_library.h b/cluster_library.h index 0ed588c654..d9c29ff467 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -418,6 +418,8 @@ PHP_REDIS_API void cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCl void *ctx); PHP_REDIS_API void cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_zadd_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); PHP_REDIS_API void cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); PHP_REDIS_API void cluster_srandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, diff --git a/library.c b/library.c index 10ec1ad2cb..e5a6954790 100644 --- a/library.c +++ b/library.c @@ -1426,6 +1426,18 @@ redis_parse_client_list_response(char *response, zval *z_ret) } } +PHP_REDIS_API int +redis_zadd_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + FailableResultCallback cb; + + ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + + cb = ctx ? redis_bulk_double_response : redis_long_response; + + return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); +} + PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { diff --git a/library.h b/library.h index f1601e99a6..11ab18d53f 100644 --- a/library.h +++ b/library.h @@ -183,6 +183,7 @@ PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, Red PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_zadd_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/redis.c b/redis.c index 59ca881046..d723c90f38 100644 --- a/redis.c +++ b/redis.c @@ -1583,7 +1583,7 @@ PHP_METHOD(Redis, brpoplpush) { /* {{{ proto long Redis::zAdd(string key, int score, string value) */ PHP_METHOD(Redis, zAdd) { - REDIS_PROCESS_CMD(zadd, redis_long_response); + REDIS_PROCESS_CMD(zadd, redis_zadd_response); } /* }}} */ diff --git a/redis.stub.php b/redis.stub.php index f6231e9e8a..7739d66cc6 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1214,7 +1214,7 @@ public function flushDB(?bool $sync = null): Redis|bool; /** * Functions is an API for managing code to be executed on the server. - * + * * @param string $operation The subcommand you intend to execute. Valid options are as follows * 'LOAD' - Create a new library with the given library name and code. * 'DELETE' - Delete the given library. @@ -4004,7 +4004,7 @@ public function xtrim(string $key, string $threshold, bool $approx = false, bool * @example $redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third'); * @example $redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element'); */ - public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|false; + public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|float|false; /** * Return the number of elements in a sorted set. diff --git a/redis_arginfo.h b/redis_arginfo.h index 1b0f69dd80..8429b51ff4 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 600a10da9438d33050be825dff3683399737cc5e */ + * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -1000,7 +1000,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xtrim, 0, 2, Red ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zAdd, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zAdd, 0, 2, Redis, MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, score_or_options, MAY_BE_ARRAY|MAY_BE_DOUBLE, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, more_scores_and_mems, IS_MIXED, 0) diff --git a/redis_cluster.c b/redis_cluster.c index fa9cb0796c..ae7e2d2e73 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1084,7 +1084,7 @@ PHP_METHOD(RedisCluster, zmscore) { /* {{{ proto long RedisCluster::zadd(string key,double score,string mem, ...) */ PHP_METHOD(RedisCluster, zadd) { - CLUSTER_PROCESS_CMD(zadd, cluster_long_resp, 0); + CLUSTER_PROCESS_CMD(zadd, cluster_zadd_resp, 0); } /* }}} */ diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 7a9504a122..c2ab9f4809 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -1035,7 +1035,7 @@ public function xtrim(string $key, int $maxlen, bool $approx = false, bool $mini /** * @see Redis::zadd */ - public function zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): RedisCluster|int|false; + public function zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): RedisCluster|int|float|false; /** * @see Redis::zcard diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 5c7c112814..2cd817df79 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: eabecbc2e536faca2a9fcba3c99ad0aeba9721b4 */ + * Stub hash: 32b24ce215ff4f2299dd838fab7cbc36b81b65eb */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -891,7 +891,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xtrim, 0, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zadd, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zadd, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_MASK(0, score_or_options, MAY_BE_ARRAY|MAY_BE_DOUBLE, NULL) ZEND_ARG_VARIADIC_TYPE_INFO(0, more_scores_and_mems, IS_MIXED, 0) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 583ad4339f..5b509cde6a 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: eabecbc2e536faca2a9fcba3c99ad0aeba9721b4 */ + * Stub hash: 32b24ce215ff4f2299dd838fab7cbc36b81b65eb */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) diff --git a/redis_commands.c b/redis_commands.c index 707500396c..94129331c9 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1525,20 +1525,7 @@ int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string_release(pattern); } else if (channels != NULL) { ZEND_HASH_FOREACH_VAL(channels, z_chan) { - // We want to deal with strings here - zend_string *zstr = zval_get_string(z_chan); - - // Grab channel name, prefix if required - char *key = ZSTR_VAL(zstr); - size_t key_len = ZSTR_LEN(zstr); - int key_free = redis_key_prefix(redis_sock, &key, &key_len); - - // Add this channel - redis_cmd_append_sstr(&cmdstr, key, key_len); - - zend_string_release(zstr); - // Free our key if it was prefixed - if (key_free) efree(key); + redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot); } ZEND_HASH_FOREACH_END(); } @@ -1558,9 +1545,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; subscribeContext *sctx = ecalloc(1, sizeof(*sctx)); unsigned short shardslot = REDIS_CLUSTER_SLOTS; - size_t key_len; - int key_free; - char *key; + short s2; if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &z_arr, &sctx->cb.fci, &sctx->cb.fci_cache) == FAILURE) @@ -1593,29 +1578,14 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Iterate over channels ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { - // We want to deal with strings here - zend_string *zstr = zval_get_string(z_chan); - - // Grab channel name, prefix if required - key = ZSTR_VAL(zstr); - key_len = ZSTR_LEN(zstr); - key_free = redis_key_prefix(redis_sock, &key, &key_len); + redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot ? &s2 : NULL); - if (shardslot != REDIS_CLUSTER_SLOTS && cluster_hash_key(key, key_len) != shardslot) { + if (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot) { php_error_docref(NULL, E_WARNING, "All shard channels needs to belong to a single slot"); - zend_string_release(zstr); - if (key_free) efree(key); smart_string_free(&cmdstr); efree(sctx); return FAILURE; } - - // Add this channel - redis_cmd_append_sstr(&cmdstr, key, key_len); - - zend_string_release(zstr); - // Free our key if it was prefixed - if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); // Push values out @@ -3004,12 +2974,9 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { zval *z_args; - char *key; - size_t key_len; - int i, key_free, argc = ZEND_NUM_ARGS(); + int i, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; - short kslot; - zend_string *zstr; + short s2; // Allocate space for args, parse them as an array z_args = emalloc(argc * sizeof(zval)); @@ -3029,33 +2996,19 @@ int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Now iterate over our keys argument for (i = 1; i < argc; i++) { - // Make sure we've got a string - zstr = zval_get_string(&z_args[i]); - - // Grab this key and length - key = ZSTR_VAL(zstr); - key_len = ZSTR_LEN(zstr); - - // Prefix key, append - key_free = redis_key_prefix(redis_sock, &key, &key_len); - redis_cmd_append_sstr(&cmdstr, key, key_len); + // Append the key + redis_cmd_append_sstr_key_zval(&cmdstr, &z_args[i], redis_sock, slot ? &s2 : NULL); // Verify slot if this is a Cluster request if (slot) { - kslot = cluster_hash_key(key, key_len); - if (*slot != -1 && kslot != *slot) { + if (*slot != -1 && s2 != *slot) { php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!"); - zend_string_release(zstr); - if (key_free) efree(key); efree(z_args); efree(cmdstr.c); return FAILURE; } - *slot = kslot; + *slot = s2; } - - zend_string_release(zstr); - if (key_free) efree(key); } // Free our argument array @@ -3100,77 +3053,40 @@ static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, int kw_len, int is_keys, char **cmd, int *cmd_len, short *slot) { - zval *z_arr, *z_ele; - HashTable *ht_arr; smart_string cmdstr = {0}; - char *mem, *key; - int key_free, mem_free, argc=1; - size_t key_len, mem_len; - zend_string *zstr; + zend_string *key = NULL; + HashTable *ht = NULL; + zval *z_ele; + int argc=1; + short s2; // Parse arguments - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, - &z_arr) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(ht) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - // Grab HashTable, count total argc - ht_arr = Z_ARRVAL_P(z_arr); - argc += zend_hash_num_elements(ht_arr); + argc += zend_hash_num_elements(ht); // We need at least two arguments if (argc < 2) { return FAILURE; } - // Prefix key, set initial hash slot - key_free = redis_key_prefix(redis_sock, &key, &key_len); - if (slot) *slot = cluster_hash_key(key, key_len); - - // Start command construction redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); - redis_cmd_append_sstr(&cmdstr, key, key_len); - - // Free key if we prefixed - if (key_free) efree(key); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - // Now iterate over the rest of our keys or values - ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - // Prefix keys, serialize values + // Append our array of keys or serialized values */ + ZEND_HASH_FOREACH_VAL(ht, z_ele) { if (is_keys) { - zstr = zval_get_string(z_ele); - mem = ZSTR_VAL(zstr); - mem_len = ZSTR_LEN(zstr); - - // Key prefix - mem_free = redis_key_prefix(redis_sock, &mem, &mem_len); - - // Verify slot - if (slot && *slot != cluster_hash_key(mem, mem_len)) { - php_error_docref(0, E_WARNING, - "All keys must hash to the same slot!"); - zend_string_release(zstr); - if (key_free) efree(key); + redis_cmd_append_sstr_key_zval(&cmdstr, z_ele, redis_sock, slot ? &s2 : NULL); + if (slot && *slot != s2) { + php_error_docref(0, E_WARNING, "All keys must hash to the same slot!"); return FAILURE; } } else { - mem_free = redis_pack(redis_sock, z_ele, &mem, &mem_len); - - zstr = NULL; - if (!mem_free) { - zstr = zval_get_string(z_ele); - mem = ZSTR_VAL(zstr); - mem_len = ZSTR_LEN(zstr); - } + redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); } - - // Append our key or member - redis_cmd_append_sstr(&cmdstr, mem, mem_len); - - // Clean up any allocated memory - if (zstr) zend_string_release(zstr); - if (mem_free) efree(mem); } ZEND_HASH_FOREACH_END(); // Push output arguments @@ -3974,100 +3890,71 @@ int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_args; - char *key, *val, *exp_type = NULL, *range_type = NULL; - size_t key_len, val_len; - int key_free, val_free; - int num = ZEND_NUM_ARGS(), i = 1, argc; + zend_string *zstr, *key = NULL, *exp_type = NULL, *range_type = NULL; zend_bool ch = 0, incr = 0; smart_string cmdstr = {0}; - zend_string *zstr; + zval *argv = NULL, *z_opt; + int argc = 0, pos = 0; - if (num < 3) return FAILURE; - z_args = ecalloc(num, sizeof(zval)); - if (zend_get_parameters_array(ht, num, z_args) == FAILURE) { - efree(z_args); - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, -1) + Z_PARAM_STR(key) + Z_PARAM_VARIADIC('*', argv, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); // Need key, [NX|XX] [LT|GT] [CH] [INCR] score, value, [score, value...] */ - if (num % 2 == 0) { - if (Z_TYPE(z_args[1]) != IS_ARRAY) { - efree(z_args); + if (argc % 2 != 0) { + if (Z_TYPE(argv[0]) != IS_ARRAY) { return FAILURE; } - zval *z_opt; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[1]), z_opt) { + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(argv[0]), z_opt) { if (Z_TYPE_P(z_opt) == IS_STRING) { - if (ZVAL_STRICMP_STATIC(z_opt, "NX") || ZVAL_STRICMP_STATIC(z_opt, "XX")) { - exp_type = Z_STRVAL_P(z_opt); - } else if (ZVAL_STRICMP_STATIC(z_opt, "LT") || ZVAL_STRICMP_STATIC(z_opt, "GT")) { - range_type = Z_STRVAL_P(z_opt); - } else if (ZVAL_STRICMP_STATIC(z_opt, "CH")) { + zstr = Z_STR_P(z_opt); + if (zend_string_equals_literal_ci(zstr, "NX") || zend_string_equals_literal_ci(zstr, "XX")) { + exp_type = Z_STR_P(z_opt); + } else if (zend_string_equals_literal_ci(zstr, "LT") || zend_string_equals_literal_ci(zstr, "GT")) { + range_type = Z_STR_P(z_opt); + } else if (zend_string_equals_literal_ci(zstr, "CH")) { ch = 1; - } else if (ZVAL_STRICMP_STATIC(z_opt, "INCR")) { - if (num > 4) { + } else if (zend_string_equals_literal_ci(zstr, "INCR")) { + if (argc != 3) { // Only one score-element pair can be specified in this mode. - efree(z_args); return FAILURE; } incr = 1; } - } } ZEND_HASH_FOREACH_END(); - argc = num - 1; - if (exp_type) argc++; - if (range_type) argc++; - argc += ch + incr; - i++; - } else { - argc = num; - } - // Prefix our key - zstr = zval_get_string(&z_args[0]); - key = ZSTR_VAL(zstr); - key_len = ZSTR_LEN(zstr); - key_free = redis_key_prefix(redis_sock, &key, &key_len); + pos++; + } // Start command construction - redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("ZADD")); - redis_cmd_append_sstr(&cmdstr, key, key_len); - - // Set our slot, free key if we prefixed it - CMD_SET_SLOT(slot,key,key_len); - zend_string_release(zstr); - if (key_free) efree(key); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (argc - pos) + !!exp_type + !!range_type + !!ch + !!incr, "ZADD"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - if (exp_type) redis_cmd_append_sstr(&cmdstr, exp_type, 2); - if (range_type) redis_cmd_append_sstr(&cmdstr, range_type, 2); - if (ch) redis_cmd_append_sstr(&cmdstr, "CH", 2); - if (incr) redis_cmd_append_sstr(&cmdstr, "INCR", 4); + if (exp_type) redis_cmd_append_sstr_zstr(&cmdstr, exp_type); + if (range_type) redis_cmd_append_sstr_zstr(&cmdstr, range_type); + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, ch, "CH"); + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, incr, "INCR"); // Now the rest of our arguments - while (i < num) { + while (pos < argc) { // Append score and member - if (redis_cmd_append_sstr_score(&cmdstr, &z_args[i]) == FAILURE) { + if (redis_cmd_append_sstr_score(&cmdstr, &argv[pos]) == FAILURE) { smart_string_free(&cmdstr); - efree(z_args); return FAILURE; } - // serialize value if requested - val_free = redis_pack(redis_sock, &z_args[i+1], &val, &val_len); - redis_cmd_append_sstr(&cmdstr, val, val_len); - // Free value if we serialized - if (val_free) efree(val); - i += 2; + redis_cmd_append_sstr_zval(&cmdstr, &argv[pos+1], redis_sock); + + pos += 2; } // Push output values - *cmd = cmdstr.c; + *cmd = cmdstr.c; *cmd_len = cmdstr.len; - - // Cleanup args - efree(z_args); + *ctx = incr ? PHPREDIS_CTX_PTR : NULL; return SUCCESS; } diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index c3656b75e0..0837a1f01f 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 600a10da9438d33050be825dff3683399737cc5e */ + * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 45180dfa4c..6cceed45dc 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2567,6 +2567,15 @@ public function testZAddFirstArg() { $this->assertTrue(['val0', 'val1'] === $this->redis->zRange($zsetName, 0, -1)); } + public function testZaddIncr() { + $this->redis->del('zset'); + + $this->assertEquals(10.0, $this->redis->zAdd('zset', ['incr'], 10, 'value')); + $this->assertEquals(20.0, $this->redis->zAdd('zset', ['incr'], 10, 'value')); + + $this->assertFalse($this->redis->zAdd('zset', ['incr'], 10, 'value', 20, 'value2')); + } + public function testZX() { $this->redis->del('key'); From fea19b5229343212424c9921a977fce300d4e130 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 23 Mar 2023 10:05:04 +0100 Subject: [PATCH 1771/1986] fix testObject for redis 7.2 --- tests/RedisTest.php | 8 +++++--- tests/TestSuite.php | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 6cceed45dc..a43ee4ea22 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -3318,16 +3318,18 @@ public function testObject() { $this->redis->lpush('key', 'value'); /* Newer versions of redis are going to encode lists as 'quicklists', - * so 'quicklist' or 'ziplist' is valid here */ + * redis >= 7.2 as 'listpack' + * so 'quicklist' or 'ziplist' or 'listpack' are valid here */ $str_encoding = $this->redis->object('encoding', 'key'); - $this->assertTrue($str_encoding === "ziplist" || $str_encoding === 'quicklist'); + $this->assertTrue($str_encoding === "ziplist" || $str_encoding === 'quicklist' || $str_encoding === 'listpack', $str_encoding); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->sadd('key', 'value'); - $this->assertTrue($this->redis->object('encoding', 'key') === "hashtable"); + $str_encoding = $this->redis->object('encoding', 'key'); + $this->assertTrue($str_encoding === "hashtable" || $str_encoding === 'listpack', $str_encoding); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); diff --git a/tests/TestSuite.php b/tests/TestSuite.php index a6043f0528..a6006c4fab 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -102,13 +102,13 @@ protected function assertFalse($bool) { return false; } - protected function assertTrue($bool) { + protected function assertTrue($bool, $msg='') { if($bool) return true; $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + self::$errors []= sprintf("Assertion failed: %s:%d (%s) %s\n", + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); return false; } From dcb95a3f06c469d0aaba62ec292abd6c5a204ed4 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 23 Mar 2023 14:17:17 +0100 Subject: [PATCH 1772/1986] change expected value according to redis version --- tests/RedisTest.php | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index a43ee4ea22..20aabf8bcc 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -3317,11 +3317,16 @@ public function testObject() { $this->redis->del('key'); $this->redis->lpush('key', 'value'); - /* Newer versions of redis are going to encode lists as 'quicklists', - * redis >= 7.2 as 'listpack' - * so 'quicklist' or 'ziplist' or 'listpack' are valid here */ $str_encoding = $this->redis->object('encoding', 'key'); - $this->assertTrue($str_encoding === "ziplist" || $str_encoding === 'quicklist' || $str_encoding === 'listpack', $str_encoding); + if (version_compare($this->version, '7.1.240') >= 0) { + /* Since redis 7.2-rc1 */ + $valid = ['listpack']; + } else { + /* Newer versions of redis are going to encode lists as 'quicklists', + * so 'quicklist' or 'ziplist' or 'listpack' are valid here */ + $valid = ['ziplist', 'quicklist']; + } + $this->assertTrue(in_array($str_encoding, $valid), $str_encoding); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); @@ -3329,7 +3334,13 @@ public function testObject() { $this->redis->del('key'); $this->redis->sadd('key', 'value'); $str_encoding = $this->redis->object('encoding', 'key'); - $this->assertTrue($str_encoding === "hashtable" || $str_encoding === 'listpack', $str_encoding); + if (version_compare($this->version, '7.1.240') >= 0) { + /* Since redis 7.2-rc1 */ + $valid = ['listpack']; + } else { + $valid = ['hashtable']; + } + $this->assertTrue(in_array($str_encoding, $valid), $str_encoding); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); From f6c8b9c6d1c22b7879d0a69c7a4424874ef7cac1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 2 Apr 2023 13:36:50 +0300 Subject: [PATCH 1773/1986] Use redis_sock_connect on connect --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 8862909847..4d7dfa4615 100644 --- a/redis.c +++ b/redis.c @@ -599,7 +599,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) } } - if (redis_sock_server_open(redis->sock) < 0) { + if (redis_sock_connect(redis->sock) != SUCCESS) { if (redis->sock->err) { REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0); } From 82265c4d541640d281d2355ff19f549be33d66ed Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 1 Apr 2023 20:24:00 +0300 Subject: [PATCH 1774/1986] Fix install dependencies on Ubuntu --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 082207ec90..e9ba6cbef7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,7 @@ jobs: curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list sudo apt-get update + sudo apt --fix-broken install sudo apt-get install redis valgrind libzstd-dev liblz4-dev - name: Build phpredis run: | From 6930a81cb4a4e1449d47c01fffc0c184e2bbb92c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 2 Apr 2023 14:14:24 +0300 Subject: [PATCH 1775/1986] Auto-select db in redis_sock_server_open --- common.h | 1 + library.c | 17 ++++++++++++----- redis_session.c | 31 +++++++------------------------ 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/common.h b/common.h index dbdf410f5c..9a46f99207 100644 --- a/common.h +++ b/common.h @@ -37,6 +37,7 @@ typedef enum { REDIS_SOCK_STATUS_FAILED = -1, REDIS_SOCK_STATUS_DISCONNECTED, REDIS_SOCK_STATUS_CONNECTED, + REDIS_SOCK_STATUS_AUTHENTICATED, REDIS_SOCK_STATUS_READY } redis_sock_status; diff --git a/library.c b/library.c index d07ad6d2ba..e53f9a6bba 100644 --- a/library.c +++ b/library.c @@ -375,13 +375,14 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) errmsg = "AUTH failed while reconnecting"; break; } + redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; - redis_sock->status = REDIS_SOCK_STATUS_READY; /* If we're using a non-zero db, reselect it */ if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) { errmsg = "SELECT failed while reconnecting"; break; } + redis_sock->status = REDIS_SOCK_STATUS_READY; /* Success */ return 0; } @@ -3015,7 +3016,7 @@ redis_sock_check_liveness(RedisSock *redis_sock) } else { goto failure; } - redis_sock->status = REDIS_SOCK_STATUS_READY; + redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; } else { if (strncmp(inbuf, "-NOAUTH", 7) == 0) { /* connection is fine but authentication required */ @@ -3179,13 +3180,19 @@ redis_sock_server_open(RedisSock *redis_sock) case REDIS_SOCK_STATUS_DISCONNECTED: if (redis_sock_connect(redis_sock) != SUCCESS) { break; - } else if (redis_sock->status == REDIS_SOCK_STATUS_READY) { - return SUCCESS; } + redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; // fall through case REDIS_SOCK_STATUS_CONNECTED: - if (redis_sock_auth(redis_sock) != SUCCESS) + if (redis_sock_auth(redis_sock) != SUCCESS) { + break; + } + redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; + // fall through + case REDIS_SOCK_STATUS_AUTHENTICATED: + if (redis_sock->dbNumber && reselect_db(redis_sock) != SUCCESS) { break; + } redis_sock->status = REDIS_SOCK_STATUS_READY; // fall through case REDIS_SOCK_STATUS_READY: diff --git a/redis_session.c b/redis_session.c index 204f651d12..2bdace7b8d 100644 --- a/redis_session.c +++ b/redis_session.c @@ -74,7 +74,6 @@ typedef struct redis_pool_member_ { RedisSock *redis_sock; int weight; - int database; struct redis_pool_member_ *next; } redis_pool_member; @@ -93,12 +92,11 @@ typedef struct { // } PHP_REDIS_API void -redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight, int database) +redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight) { redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); rpm->redis_sock = redis_sock; rpm->weight = weight; - rpm->database = database; rpm->next = pool->head; pool->head = rpm; @@ -156,21 +154,6 @@ static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, return len_written; } -static void -redis_pool_member_select(redis_pool_member *rpm) { - RedisSock *redis_sock = rpm->redis_sock; - char *response, *cmd; - int response_len, cmd_len; - - cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", rpm->database); - if (redis_sock_write(redis_sock, cmd, cmd_len) >= 0) { - if ((response = redis_sock_read(redis_sock, &response_len))) { - efree(response); - } - } - efree(cmd); -} - PHP_REDIS_API redis_pool_member * redis_pool_get_sock(redis_pool *pool, const char *key) { @@ -183,10 +166,6 @@ redis_pool_get_sock(redis_pool *pool, const char *key) { for(i = 0; i < pool->totalWeight;) { if (pos >= i && pos < i + rpm->weight) { if (redis_sock_server_open(rpm->redis_sock) == 0) { - if (rpm->database >= 0) { /* default is -1 which leaves the choice to redis. */ - redis_pool_member_select(rpm); - } - return rpm; } } @@ -495,11 +474,15 @@ PS_OPEN_FUNC(redis) persistent, persistent_id ? ZSTR_VAL(persistent_id) : NULL, retry_interval); + if (db >= 0) { /* default is -1 which leaves the choice to redis. */ + redis_sock->dbNumber = db; + } + if (Z_TYPE(context) == IS_ARRAY) { redis_sock_set_stream_context(redis_sock, &context); } - redis_pool_add(pool, redis_sock, weight, db); + redis_pool_add(pool, redis_sock, weight); redis_sock->prefix = prefix; redis_sock_set_auth(redis_sock, user, pass); @@ -1294,4 +1277,4 @@ PS_GC_FUNC(rediscluster) { #endif -/* vim: set tabstop=4 expandtab: */ \ No newline at end of file +/* vim: set tabstop=4 expandtab: */ From 7a055cada8f45e69943b06f1aa5c38d865b81369 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 2 Apr 2023 18:36:12 +0300 Subject: [PATCH 1776/1986] Use on-stack allocated valiables --- cluster_library.h | 3 --- common.h | 5 +++++ library.c | 10 ++++------ redis.c | 34 ++++++++++------------------------ 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/cluster_library.h b/cluster_library.h index d9c29ff467..eb2b1531b8 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -12,9 +12,6 @@ #define REDIS_CLUSTER_MOD (REDIS_CLUSTER_SLOTS-1) /* Complete representation for various commands in RESP */ -#define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" -#define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" -#define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" #define RESP_UNWATCH_CMD "*1\r\n$7\r\nUNWATCH\r\n" #define RESP_CLUSTER_SLOTS_CMD "*2\r\n$7\r\nCLUSTER\r\n$5\r\nSLOTS\r\n" #define RESP_ASKING_CMD "*1\r\n$6\r\nASKING\r\n" diff --git a/common.h b/common.h index 9a46f99207..8e2ec57ab8 100644 --- a/common.h +++ b/common.h @@ -289,6 +289,11 @@ typedef enum { #endif #endif +/* Complete representation for various commands in RESP */ +#define RESP_MULTI_CMD "*1\r\n$5\r\nMULTI\r\n" +#define RESP_EXEC_CMD "*1\r\n$4\r\nEXEC\r\n" +#define RESP_DISCARD_CMD "*1\r\n$7\r\nDISCARD\r\n" + /* {{{ struct RedisSock */ typedef struct { php_stream *stream; diff --git a/library.c b/library.c index e53f9a6bba..eb9df5a1a6 100644 --- a/library.c +++ b/library.c @@ -3052,9 +3052,9 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) { struct timeval tv, read_tv, *tv_ptr = NULL; zend_string *persistent_id = NULL, *estr = NULL; - char host[1024], *pos, *address, *scheme = NULL; + char host[1024], scheme[8], *pos, *address; const char *fmtstr = "%s://%s:%d"; - int host_len, usocket = 0, err = 0, tcp_flag = 1, scheme_free = 0; + int host_len, usocket = 0, err = 0, tcp_flag = 1; ConnectionPool *p = NULL; if (redis_sock->stream != NULL) { @@ -3063,11 +3063,10 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) address = ZSTR_VAL(redis_sock->host); if ((pos = strstr(address, "://")) == NULL) { - scheme = redis_sock->stream_ctx ? "ssl" : "tcp"; + strcpy(scheme, redis_sock->stream_ctx ? "ssl" : "tcp"); } else { - scheme = estrndup(address, pos - address); + snprintf(scheme, sizeof(scheme), "%.*s", (int)(pos - address), address); address = pos + sizeof("://") - 1; - scheme_free = 1; } if (address[0] == '/' && redis_sock->port < 1) { host_len = snprintf(host, sizeof(host), "unix://%s", address); @@ -3085,7 +3084,6 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) #endif host_len = snprintf(host, sizeof(host), fmtstr, scheme, address, redis_sock->port); } - if (scheme_free) efree(scheme); if (redis_sock->persistent) { if (INI_INT("redis.pconnect.pooling_enabled")) { diff --git a/redis.c b/redis.c index 4d7dfa4615..79b609ccc6 100644 --- a/redis.c +++ b/redis.c @@ -160,15 +160,11 @@ zend_object_handlers redis_object_handlers; static int redis_send_discard(RedisSock *redis_sock) { - int result = FAILURE; - char *cmd, *resp; - int resp_len, cmd_len; - - /* format our discard command */ - cmd_len = REDIS_SPPRINTF(&cmd, "DISCARD", ""); + char *resp; + int resp_len, result = FAILURE; /* send our DISCARD command */ - if (redis_sock_write(redis_sock, cmd, cmd_len) >= 0 && + if (redis_sock_write(redis_sock, ZEND_STRL(RESP_DISCARD_CMD)) >= 0 && (resp = redis_sock_read(redis_sock,&resp_len)) != NULL) { /* success if we get OK */ @@ -178,9 +174,6 @@ redis_send_discard(RedisSock *redis_sock) efree(resp); } - /* free our command */ - efree(cmd); - /* return success/failure */ return result; } @@ -1865,8 +1858,8 @@ PHP_METHOD(Redis, multi) { RedisSock *redis_sock; - char *resp, *cmd; - int resp_len, cmd_len; + char *resp; + int resp_len; zval *object; zend_long multi_value = MULTI; @@ -1897,15 +1890,12 @@ PHP_METHOD(Redis, multi) } else if (multi_value == MULTI) { /* Don't want to do anything if we're already in MULTI mode */ if (!IS_MULTI(redis_sock)) { - cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", ""); if (IS_PIPELINE(redis_sock)) { - PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); - efree(cmd); + PIPELINE_ENQUEUE_COMMAND(RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD) - 1); REDIS_SAVE_CALLBACK(NULL, NULL); REDIS_ENABLE_MODE(redis_sock, MULTI); } else { - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) - efree(cmd); + SOCKET_WRITE_COMMAND(redis_sock, RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD) - 1) if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) { RETURN_FALSE; } else if (strncmp(resp, "+OK", 3) != 0) { @@ -1982,8 +1972,7 @@ redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, PHP_METHOD(Redis, exec) { RedisSock *redis_sock; - char *cmd; - int cmd_len, ret; + int ret; zval *object, z_ret; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), @@ -1994,16 +1983,13 @@ PHP_METHOD(Redis, exec) } if (IS_MULTI(redis_sock)) { - cmd_len = REDIS_SPPRINTF(&cmd, "EXEC", ""); if (IS_PIPELINE(redis_sock)) { - PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); - efree(cmd); + PIPELINE_ENQUEUE_COMMAND(RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1); REDIS_SAVE_CALLBACK(NULL, NULL); REDIS_DISABLE_MODE(redis_sock, MULTI); RETURN_ZVAL(getThis(), 1, 0); } - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) - efree(cmd); + SOCKET_WRITE_COMMAND(redis_sock, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1) ZVAL_NULL(&z_ret); ret = redis_sock_read_multibulk_multi_reply( From 717713e1bf4eb88620cd7dd1396a659682e6a706 Mon Sep 17 00:00:00 2001 From: Dmitrii Kotov Date: Sun, 30 Apr 2023 21:20:47 +0300 Subject: [PATCH 1777/1986] Fix Fedora package url --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index bd3fbd985a..fe29344884 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -45,7 +45,7 @@ Fedora users can install the package from the official repository. ### Fedora ≥ 29, Version 5 -Installation of the [php-pecl-redis5](https://apps.fedoraproject.org/packages/php-pecl-redis5) package: +Installation of the [php-pecl-redis5](https://packages.fedoraproject.org/pkgs/php-pecl-redis5/php-pecl-redis5/) package: ~~~ dnf install php-pecl-redis5 From 83d838c38819b9725cd56ededfaffd233b22fbbd Mon Sep 17 00:00:00 2001 From: Michele Locati Date: Fri, 5 May 2023 16:02:17 +0200 Subject: [PATCH 1778/1986] Rename array.md to arrays.md In the `package.xml` file we have `arrays.md` instead of `array.md`: that breaks the compilation with pecl: ERROR: file /path/to/phpredis/arrays.md does not exist --- array.md => arrays.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename array.md => arrays.md (100%) diff --git a/array.md b/arrays.md similarity index 100% rename from array.md rename to arrays.md From 35a7cc094c6c264aa37738b074c4c54c4ca73b87 Mon Sep 17 00:00:00 2001 From: Michele Locati Date: Fri, 5 May 2023 16:34:00 +0200 Subject: [PATCH 1779/1986] Test PECL package creation --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9ba6cbef7..484a3a945a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,3 +146,36 @@ jobs: with: name: redis-${{matrix.php}}-x64-${{matrix.ts}} path: binaries + + pecl: + runs-on: ubuntu-latest + container: php:8.2-cli-alpine + steps: + - name: Install required system packages + run: apk add --update $PHPIZE_DEPS zstd-libs zstd-dev git + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: true + - name: Create temporary directory + id: temp-dir + run: printf "path=%s\n" "$(mktemp -d)" >>"$GITHUB_OUTPUT" + - name: Create package + run: | + cd "${{ steps.temp-dir.outputs.path }}" + pecl package "$GITHUB_WORKSPACE/package.xml" + - name: Compile package + run: printf '' | pecl install ${{ steps.temp-dir.outputs.path }}/redis-*.tgz + - name: Enable extension + run: docker-php-ext-enable redis + - name: Check for PHP startup warnings + run: | + php -d display_errors=stderr -d display_startup_errors=1 -d error_reporting=-1 -r ';' 2>/tmp/php-startup-warnings + if [ -s /tmp/php-startup-warnings ]; then + echo 'The PHP extension was successfully installed, but PHP raised these warnings:' >&2 + cat /tmp/php-startup-warnings >&2 + exit 1 + fi + echo "PHP didn't raise any warnings at startup." + - name: Inspect extension + run: php --ri redis From 8f6bc98fd6fe2a9fda8ed6cd4fb65bbcd9be0369 Mon Sep 17 00:00:00 2001 From: Timo Sand Date: Wed, 24 May 2023 23:00:51 +0300 Subject: [PATCH 1780/1986] Fix typo in link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f630c71356..df18cd0305 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ You can also make a one-time contribution with [![PayPal](https://img.shields.io 1. [Installing/Configuring](#installingconfiguring) * [Installation](#installation) * [PHP Session handler](#php-session-handler) - * [Distributed Redis Array](./array.md#readme) + * [Distributed Redis Array](./arrays.md#readme) * [Redis Cluster support](./cluster.md#readme) * [Redis Sentinel support](./sentinel.md#readme) * [Running the unit tests](#running-the-unit-tests) From a3327d9d2bf8e890e16dc62d3e6b202f135f34ad Mon Sep 17 00:00:00 2001 From: thomaston <411598110@qq.com> Date: Fri, 21 Jul 2023 02:48:53 +0800 Subject: [PATCH 1781/1986] Fix bug: the pipeline mode socket return an unexpected result after reconnecting (#2358) * fix bug: the pipeline mode socket return an unexpected result after reconnecting * fix typos: pipeline is right --------- Co-authored-by: marcofu --- cluster_library.c | 8 ++++---- library.c | 15 +++++++++------ library.h | 2 +- redis.c | 6 +++--- redis_session.c | 6 +++--- sentinel_library.c | 2 +- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 24c8a9142c..1fb4bde5eb 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -1097,7 +1097,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c) { memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS); } } - redis_sock_disconnect(seed, 0); + redis_sock_disconnect(seed, 0, 1); if (mapped) break; } ZEND_HASH_FOREACH_END(); @@ -1218,13 +1218,13 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force) { if (node == NULL) continue; /* Disconnect from the master */ - redis_sock_disconnect(node->sock, force); + redis_sock_disconnect(node->sock, force, 1); /* We also want to disconnect any slave connections so they will be pooled * in the event we are using persistent connections and connection pooling. */ if (node->slaves) { ZEND_HASH_FOREACH_PTR(node->slaves, slave) { - redis_sock_disconnect(slave->sock, force); + redis_sock_disconnect(slave->sock, force, 1); } ZEND_HASH_FOREACH_END(); } } ZEND_HASH_FOREACH_END(); @@ -1603,7 +1603,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char return -1; } else if (timedout || resp == -1) { // Make sure the socket is reconnected, it such that it is in a clean state - redis_sock_disconnect(c->cmd_sock, 1); + redis_sock_disconnect(c->cmd_sock, 1, 1); if (timedout) { CLUSTER_THROW_EXCEPTION("Timed out attempting to find data in the correct node!", 0); diff --git a/library.c b/library.c index eb9df5a1a6..dabb2188be 100644 --- a/library.c +++ b/library.c @@ -359,7 +359,8 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) for (retry_index = 0; !no_retry && retry_index < redis_sock->max_retries; ++retry_index) { /* close existing stream before reconnecting */ if (redis_sock->stream) { - redis_sock_disconnect(redis_sock, 1); + /* reconnect no need to reset mode, it will cause pipeline mode socket exception */ + redis_sock_disconnect(redis_sock, 1, 0); } /* Sleep based on our backoff algorithm */ zend_ulong delay = redis_backoff_compute(&redis_sock->backoff, retry_index); @@ -390,7 +391,7 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) } } /* close stream and mark socket as failed */ - redis_sock_disconnect(redis_sock, 1); + redis_sock_disconnect(redis_sock, 1, 1); redis_sock->status = REDIS_SOCK_STATUS_FAILED; if (!no_throw) { REDIS_THROW_EXCEPTION( errmsg, 0); @@ -3058,7 +3059,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) ConnectionPool *p = NULL; if (redis_sock->stream != NULL) { - redis_sock_disconnect(redis_sock, 0); + redis_sock_disconnect(redis_sock, 0, 1); } address = ZSTR_VAL(redis_sock->host); @@ -3206,7 +3207,7 @@ redis_sock_server_open(RedisSock *redis_sock) * redis_sock_disconnect */ PHP_REDIS_API int -redis_sock_disconnect(RedisSock *redis_sock, int force) +redis_sock_disconnect(RedisSock *redis_sock, int force, int is_reset_mode) { if (redis_sock == NULL) { return FAILURE; @@ -3228,7 +3229,9 @@ redis_sock_disconnect(RedisSock *redis_sock, int force) } redis_sock->stream = NULL; } - redis_sock->mode = ATOMIC; + if (is_reset_mode) { + redis_sock->mode = ATOMIC; + } redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; @@ -4107,7 +4110,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_siz snprintf(buf, buf_size, "read error on connection to %s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); } // Close our socket - redis_sock_disconnect(redis_sock, 1); + redis_sock_disconnect(redis_sock, 1, 1); // Throw a read error exception REDIS_THROW_EXCEPTION(buf, 0); diff --git a/library.h b/library.h index 8b669a4357..f240a10e29 100644 --- a/library.h +++ b/library.h @@ -83,7 +83,7 @@ PHP_REDIS_API char *redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen); PHP_REDIS_API void redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass); PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv); PHP_REDIS_API void redis_sock_free_auth(RedisSock *redis_sock); -PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force); +PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force, int is_reset_mode); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(RedisSock *redis_sock, zval *z_tab); PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen, size_t *linelen, int set_err); diff --git a/redis.c b/redis.c index 79b609ccc6..bd69b588ba 100644 --- a/redis.c +++ b/redis.c @@ -192,7 +192,7 @@ free_redis_object(zend_object *object) zend_object_std_dtor(&redis->std); if (redis->sock) { - redis_sock_disconnect(redis->sock, 0); + redis_sock_disconnect(redis->sock, 0, 1); redis_free_socket(redis->sock); } } @@ -573,7 +573,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) /* if there is a redis sock already we have to remove it */ if (redis->sock) { - redis_sock_disconnect(redis->sock, 0); + redis_sock_disconnect(redis->sock, 0, 1); redis_free_socket(redis->sock); } @@ -632,7 +632,7 @@ PHP_METHOD(Redis, close) { RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (redis_sock_disconnect(redis_sock, 1) == SUCCESS) { + if (redis_sock_disconnect(redis_sock, 1, 1) == SUCCESS) { RETURN_TRUE; } RETURN_FALSE; diff --git a/redis_session.c b/redis_session.c index 2bdace7b8d..d10793dfa3 100644 --- a/redis_session.c +++ b/redis_session.c @@ -111,7 +111,7 @@ redis_pool_free(redis_pool *pool) { rpm = pool->head; while (rpm) { next = rpm->next; - redis_sock_disconnect(rpm->redis_sock, 0); + redis_sock_disconnect(rpm->redis_sock, 0, 1); redis_free_socket(rpm->redis_sock); efree(rpm); rpm = next; @@ -1103,7 +1103,7 @@ PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { /* Attempt to send EXPIRE command */ c->readonly = 0; if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { - php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry"); + php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry"); efree(cmd); return FAILURE; } @@ -1146,7 +1146,7 @@ PS_READ_FUNC(rediscluster) { cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen); c->readonly = 1; } - + efree(skey); /* Attempt to kick off our command */ diff --git a/sentinel_library.c b/sentinel_library.c index 0fe64cc145..bed1aca385 100644 --- a/sentinel_library.c +++ b/sentinel_library.c @@ -8,7 +8,7 @@ free_redis_sentinel_object(zend_object *object) redis_sentinel_object *obj = PHPREDIS_GET_OBJECT(redis_sentinel_object, object); if (obj->sock) { - redis_sock_disconnect(obj->sock, 0); + redis_sock_disconnect(obj->sock, 0, 1); redis_free_socket(obj->sock); } zend_object_std_dtor(&obj->std); From 5e4bdf97c887e6b150785629ec59ece7fa0b58e6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 Jul 2023 17:56:02 +0200 Subject: [PATCH 1782/1986] Fix arginfo for arguments that default to null --- redis.stub.php | 70 ++++++++++++++++++++-------------------- redis_arginfo.h | 58 ++++++++++++++++----------------- redis_array.stub.php | 6 ++-- redis_array_arginfo.h | 6 ++-- redis_cluster.stub.php | 48 +++++++++++++-------------- redis_cluster_arginfo.h | 26 +++++++-------- redis_sentinel.stub.php | 2 +- redis_sentinel_arginfo.h | 2 +- 8 files changed, 109 insertions(+), 109 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 7739d66cc6..05a7ebd047 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -478,7 +478,7 @@ class Redis { * * @return Redis */ - public function __construct(array $options = null); + public function __construct(?array $options = null); public function __destruct(); @@ -816,7 +816,7 @@ public function client(string $opt, mixed ...$args): mixed; public function close(): bool; - public function command(string $opt = null, mixed ...$args): mixed; + public function command(?string $opt = null, mixed ...$args): mixed; /** * Execute the Redis CONFIG command in a variety of ways. @@ -835,10 +835,10 @@ public function command(string $opt = null, mixed ...$args): mixed; * $redis->config('SET', 'timeout', 30); * $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); */ - public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed; + public function config(string $operation, array|string|null $key_or_settings = null, ?string $value = null): mixed; - public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, - int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool; + public function connect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, + int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Make a copy of a key. @@ -873,7 +873,7 @@ public function connect(string $host, int $port = 6379, float $timeout = 0, stri * var_dump($redis->copy('source1', 'exists')); * var_dump($redis->copy('source1', 'exists', ['REPLACE' => true])); */ - public function copy(string $src, string $dst, array $options = null): Redis|bool; + public function copy(string $src, string $dst, ?array $options = null): Redis|bool; /** * Return the number of keys in the currently selected Redis database. @@ -1105,7 +1105,7 @@ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; * @see https://redis.io/commands/expire * */ - public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|bool; + public function expire(string $key, int $timeout, ?string $mode = null): Redis|bool; /* * Set a key's expiration to a specific Unix timestamp in seconds. @@ -1131,7 +1131,7 @@ public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|b * @see https://redis.io/commands/expire * @see Redis::expire() */ - public function expireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool; + public function expireAt(string $key, int $timestamp, ?string $mode = null): Redis|bool; public function failover(?array $to = null, bool $abort = false, int $timeout = 0): Redis|bool; @@ -1587,7 +1587,7 @@ public function getRange(string $key, int $start, int $end): Redis|string|false; * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc'); * echo $redis->lcs('seq1', 'seq2') . "\n"; */ - public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false; + public function lcs(string $key1, string $key2, ?array $options = null): Redis|string|array|int|false; /** * Get the currently set read timeout on the connection. @@ -1783,7 +1783,7 @@ public function hMset(string $key, array $fieldvals): Redis|bool; * @example $redis->hrandfield('settings'); * @example $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]); */ - public function hRandField(string $key, array $options = null): Redis|string|array; + public function hRandField(string $key, ?array $options = null): Redis|string|array; public function hSet(string $key, string $member, mixed $value): Redis|int|false; @@ -1861,7 +1861,7 @@ public function hVals(string $key): Redis|array|false; * * $redis->hmset('big-hash', $fields); * - * $it = NULL; + * $it = null; * * do { * // Scan the hash but limit it to fields that match '*:1?3' @@ -2041,7 +2041,7 @@ public function lPop(string $key, int $count = 0): Redis|bool|string|array; * * @return Redis|null|bool|int|array Returns one or more of the matching indexes, or null/false if none were found. */ - public function lPos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array; + public function lPos(string $key, mixed $value, ?array $options = null): Redis|null|bool|int|array; /** * Prepend one or more elements to a list. @@ -2178,7 +2178,7 @@ public function mget(array $keys): Redis|array; public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, - #[\SensitiveParameter] mixed $credentials = NULL): Redis|bool; + #[\SensitiveParameter] mixed $credentials = null): Redis|bool; /** * Move a key to a different database on the same redis instance. @@ -2237,9 +2237,9 @@ public function object(string $subcommand, string $key): Redis|int|string|false; * @deprecated * @alias Redis::connect */ - public function open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + public function open(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; - public function pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + public function pconnect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Remove the expiration from a key. @@ -2262,7 +2262,7 @@ public function persist(string $key): Redis|bool; * * @return Redis|bool True if an expiry was set on the key, and false otherwise. */ - public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool; + public function pexpire(string $key, int $timeout, ?string $mode = null): bool; /** * Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to @@ -2276,7 +2276,7 @@ public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool; * * @return Redis|bool True if an expiration was set on the key, false otherwise. */ - public function pexpireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool; + public function pexpireAt(string $key, int $timestamp, ?string $mode = null): Redis|bool; /** * Add one or more elements to a Redis HyperLogLog key @@ -2327,7 +2327,7 @@ public function pfmerge(string $dst, array $srckeys): Redis|bool; * @example $redis->ping(); * @example $redis->ping('beep boop'); */ - public function ping(string $message = NULL): Redis|string|bool; + public function ping(?string $message = null): Redis|string|bool; /** * Enter into pipeline mode. @@ -2353,7 +2353,7 @@ public function pipeline(): bool|Redis; * @deprecated * @alias Redis::pconnect */ - public function popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + public function popen(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Set a key with an expiration time in milliseconds @@ -2542,7 +2542,7 @@ public function reset(): Redis|bool; * * $redis->restore('captains-backup', 0, $serialized); */ - public function restore(string $key, int $ttl, string $value, ?array $options = NULL): Redis|bool; + public function restore(string $key, int $ttl, string $value, ?array $options = null): Redis|bool; /** * Query whether the connected instance is a primary or replica @@ -2891,7 +2891,7 @@ public function save(): Redis|bool; * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); * - * $it = NULL; + * $it = null; * * do { * $keys = $redis->scan($it, '*zorg*'); @@ -2902,7 +2902,7 @@ public function save(): Redis|bool; * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); * - * $it = NULL; + * $it = null; * * // When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an * // empty array of keys when the iterator is nonzero. @@ -2912,7 +2912,7 @@ public function save(): Redis|bool; * } * } */ - public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, string $type = NULL): array|false; + public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, ?string $type = null): array|false; /** * Retrieve the number of members in a Redis set. @@ -2985,7 +2985,7 @@ public function select(int $db): Redis|bool; * @example $redis->set('key', 'value'); * @example $redis->set('key', 'expires_in_60_seconds', 60); */ - public function set(string $key, mixed $value, mixed $options = NULL): Redis|string|bool; + public function set(string $key, mixed $value, mixed $options = null): Redis|string|bool; /** * Set a specific bit in a Redis string to zero or one @@ -3106,7 +3106,7 @@ public function sismember(string $key, mixed $value): Redis|bool; * @see https://redis.io/commands/replicaof * @see Redis::replicaof() */ - public function slaveof(string $host = NULL, int $port = 6379): Redis|bool; + public function slaveof(?string $host = null, int $port = 6379): Redis|bool; /** * Used to turn a Redis instance into a replica of another, or to remove @@ -3132,7 +3132,7 @@ public function slaveof(string $host = NULL, int $port = 6379): Redis|bool; * // attempting to promote the instance to a primary. * $redis->replicaof(); */ - public function replicaof(string $host = NULL, int $port = 6379): Redis|bool; + public function replicaof(?string $host = null, int $port = 6379): Redis|bool; /** * Update one or more keys last modified metadata. @@ -3274,7 +3274,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); * * $scanned = 0; - * $it = NULL; + * $it = null; * * // Without Redis::SCAN_RETRY we may receive empty results and * // a nonzero iterator. @@ -3291,7 +3291,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); * * $scanned = 0; - * $it = NULL; + * $it = null; * * // With Redis::SCAN_RETRY PhpRedis will never return an empty array * // when the cursor is non-zero @@ -3795,7 +3795,7 @@ public function xdel(string $key, array $ids): Redis|int|false; * * @return mixed This command return various results depending on the operation performed. */ - public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, + public function xgroup(string $operation, ?string $key = null, ?string $group = null, ?string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2): mixed; /** @@ -4105,7 +4105,7 @@ public function zMscore(string $key, mixed $member, mixed ...$other_members): Re * $redis->zPopMax('zs'); * $redis->zPopMax('zs', 2);. */ - public function zPopMax(string $key, int $count = null): Redis|array|false; + public function zPopMax(string $key, ?int $count = null): Redis|array|false; /** * Pop one or more of the lowest scoring elements from a sorted set. @@ -4123,7 +4123,7 @@ public function zPopMax(string $key, int $count = null): Redis|array|false; * $redis->zPopMin('zs'); * $redis->zPopMin('zs', 2); */ - public function zPopMin(string $key, int $count = null): Redis|array|false; + public function zPopMin(string $key, ?int $count = null): Redis|array|false; /** * Retrieve a range of elements of a sorted set between a start and end point. @@ -4222,7 +4222,7 @@ public function zRangeByScore(string $key, string $start, string $end, array $op * See {@link Redis::zRange} for a full description of the possible options. */ public function zrangestore(string $dstkey, string $srckey, string $start, string $end, - array|bool|null $options = NULL): Redis|int|false; + array|bool|null $options = null): Redis|int|false; /** * Retrieve one or more random members from a Redis sorted set. @@ -4240,7 +4240,7 @@ public function zrangestore(string $dstkey, string $srckey, string $start, strin * * @example $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]); */ - public function zRandMember(string $key, array $options = null): Redis|string|array; + public function zRandMember(string $key, ?array $options = null): Redis|string|array; /** * Get the rank of a member of a sorted set, by score. @@ -4448,7 +4448,7 @@ public function zScore(string $key, mixed $member): Redis|float|false; * * $redis->zDiff(['primes', 'evens', 'mod3']); */ - public function zdiff(array $keys, array $options = null): Redis|array|false; + public function zdiff(array $keys, ?array $options = null): Redis|array|false; /** * Store the difference of one or more sorted sets in a destination sorted set. @@ -4619,7 +4619,7 @@ public function zunion(array $keys, ?array $weights = null, ?array $options = nu * * $redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']); */ - public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): Redis|int|false; + public function zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false; } class RedisException extends RuntimeException {} diff --git a/redis_arginfo.h b/redis_arginfo.h index 8429b51ff4..41f6aab06f 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -2,7 +2,7 @@ * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0) @@ -121,30 +121,30 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_close arginfo_class_Redis_clearLastError ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 0, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 1, "null") ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_copy, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dbSize, 0, 0, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -210,13 +210,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expire, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expireAt, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_failover, 0, 0, Redis, MAY_BE_BOOL) @@ -360,7 +360,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getReadTimeout, 0, 0, IS_DOUBLE, 0) @@ -428,7 +428,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSet, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -508,7 +508,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPush, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -565,7 +565,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_migrate, 0, 5, R ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, copy, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, replace, _IS_BOOL, 0, "false") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_move, 0, 2, Redis, MAY_BE_BOOL) @@ -592,10 +592,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_open, 0, 1, _IS_BOOL ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_pconnect arginfo_class_Redis_open @@ -607,7 +607,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpire, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt @@ -627,7 +627,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfmerge, 0, 2, R ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ping, 0, 0, Redis, MAY_BE_STRING|MAY_BE_BOOL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL) @@ -691,7 +691,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_restore, 0, 3, R ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_role arginfo_class_Redis_getAuth @@ -762,7 +762,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_A ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_scard arginfo_class_Redis_expiretime @@ -776,7 +776,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_set, 0, 2, Redis, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setBit, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -810,7 +810,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_sismember arginfo_class_Redis_setnx ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_slaveof, 0, 0, Redis, MAY_BE_BOOL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_END_ARG_INFO() @@ -939,9 +939,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() @@ -1034,7 +1034,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zPopMax, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax @@ -1066,7 +1066,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zrangestore, 0, ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) - ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "NULL") + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField @@ -1123,7 +1123,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiff, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiffstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -1158,8 +1158,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zunionstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null") ZEND_END_ARG_INFO() diff --git a/redis_array.stub.php b/redis_array.stub.php index 8fca8ab95a..c84d636a9c 100644 --- a/redis_array.stub.php +++ b/redis_array.stub.php @@ -10,7 +10,7 @@ class RedisArray { public function __call(string $function_name, array $arguments): mixed; - public function __construct(string|array $name_or_hosts, array $options = NULL); + public function __construct(string|array $name_or_hosts, ?array $options = null); public function _continuum(): bool|array; @@ -22,7 +22,7 @@ public function _hosts(): bool|array; public function _instance(string $host): bool|null|Redis; - public function _rehash(callable $fn = NULL): bool|null; + public function _rehash(?callable $fn = null): bool|null; public function _target(string $key): bool|string|null; @@ -50,7 +50,7 @@ public function mget(array $keys): bool|array; public function mset(array $pairs): bool; - public function multi(string $host, int $mode = NULL): bool|RedisArray; + public function multi(string $host, ?int $mode = null): bool|RedisArray; public function ping(): bool|array; diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h index bfacd08609..ea4d0721ef 100644 --- a/redis_array_arginfo.h +++ b/redis_array_arginfo.h @@ -8,7 +8,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___construct, 0, 0, 1) ZEND_ARG_TYPE_MASK(0, name_or_hosts, MAY_BE_STRING|MAY_BE_ARRAY, NULL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray__continuum, 0, 0, MAY_BE_BOOL|MAY_BE_ARRAY) @@ -26,7 +26,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisArray__instance, ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray__rehash, 0, 0, _IS_BOOL, 1) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fn, IS_CALLABLE, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fn, IS_CALLABLE, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray__target, 0, 1, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_NULL) @@ -77,7 +77,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisArray_multi, 0, 1, RedisArray, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_ping arginfo_class_RedisArray__continuum diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index c2ab9f4809..f5508afdfe 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -47,7 +47,7 @@ class RedisCluster { */ public const FAILOVER_DISTRIBUTE_SLAVES = UNKNOWN; - public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = NULL, array $context = NULL); + public function __construct(string|null $name, ?array $seeds = null, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = null, ?array $context = null); /** * @see Redis::_compress() @@ -200,7 +200,7 @@ public function clearlasterror(): bool; /** * @see Redis::client */ - public function client(string|array $key_or_address, string $subcommand, ?string $arg = NULL): array|string|bool; + public function client(string|array $key_or_address, string $subcommand, ?string $arg = null): array|string|bool; /** * @see Redis::close @@ -230,7 +230,7 @@ public function dbsize(string|array $key_or_address): RedisCluster|int; /** * @see https://redis.io/commands/copy */ - public function copy(string $src, string $dst, array $options = null): RedisCluster|bool; + public function copy(string $src, string $dst, ?array $options = null): RedisCluster|bool; /** * @see Redis::decr() @@ -305,12 +305,12 @@ public function touch(mixed $key, mixed ...$other_keys): RedisCluster|int|bool; /** * @see Redis::expire */ - public function expire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool; + public function expire(string $key, int $timeout, ?string $mode = null): RedisCluster|bool; /** * @see Redis::expireat */ - public function expireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool; + public function expireat(string $key, int $timestamp, ?string $mode = null): RedisCluster|bool; /** * @see Redis::expiretime() @@ -415,7 +415,7 @@ public function getrange(string $key, int $start, int $end): RedisCluster|string /** * @see Redis::lcs */ - public function lcs(string $key1, string $key2, ?array $options = NULL): RedisCluster|string|array|int|false; + public function lcs(string $key1, string $key2, ?array $options = null): RedisCluster|string|array|int|false; /** * @see Redis::getset @@ -490,7 +490,7 @@ public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int /** * @see https://redis.io/commands/hrandfield */ - public function hrandfield(string $key, array $options = null): RedisCluster|string|array; + public function hrandfield(string $key, ?array $options = null): RedisCluster|string|array; /** * @see Redis::hset @@ -583,7 +583,7 @@ public function lpop(string $key, int $count = 0): RedisCluster|bool|string|arra /** * @see Redis::lpos */ - public function lpos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array; + public function lpos(string $key, mixed $value, ?array $options = null): Redis|null|bool|int|array; /** * @see Redis::lpush @@ -648,12 +648,12 @@ public function persist(string $key): RedisCluster|bool; /** * @see Redis::pexpire */ - public function pexpire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool; + public function pexpire(string $key, int $timeout, ?string $mode = null): RedisCluster|bool; /** * @see Redis::pexpireat */ - public function pexpireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool; + public function pexpireat(string $key, int $timestamp, ?string $mode = null): RedisCluster|bool; /** @@ -684,7 +684,7 @@ public function pfmerge(string $key, array $keys): RedisCluster|bool; * @return mixed This method always returns `true` if no message was sent, and the message itself * if one was. */ - public function ping(string|array $key_or_address, ?string $message = NULL): mixed; + public function ping(string|array $key_or_address, ?string $message = null): mixed; /** * @see Redis::psetex @@ -739,7 +739,7 @@ public function renamenx(string $key, string $newkey): RedisCluster|bool; /** * @see Redis::restore */ - public function restore(string $key, int $timeout, string $value, ?array $options = NULL): RedisCluster|bool; + public function restore(string $key, int $timeout, string $value, ?array $options = null): RedisCluster|bool; /** * @see Redis::role @@ -809,7 +809,7 @@ public function sdiffstore(string $dst, string $key, string ...$other_keys): Red /** * @see https://redis.io/commands/set */ - public function set(string $key, mixed $value, mixed $options = NULL): RedisCluster|string|bool; + public function set(string $key, mixed $value, mixed $options = null): RedisCluster|string|bool; /** * @see Redis::setbit @@ -879,12 +879,12 @@ public function smove(string $src, string $dst, string $member): RedisCluster|bo /** * @see Redis::sort() */ - public function sort(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string; + public function sort(string $key, ?array $options = null): RedisCluster|array|bool|int|string; /** * @see Redis::sort_ro() */ - public function sort_ro(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string; + public function sort_ro(string $key, ?array $options = null): RedisCluster|array|bool|int|string; /** * @see Redis::spop @@ -984,7 +984,7 @@ public function xdel(string $key, array $ids): RedisCluster|int|false; /** * @see Redis::xgroup */ - public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, + public function xgroup(string $operation, ?string $key = null, ?string $group = null, ?string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2): mixed; /** @@ -1070,12 +1070,12 @@ public function zlexcount(string $key, string $min, string $max): RedisCluster|i /** * @see Redis::zpopmax */ - public function zpopmax(string $key, int $value = null): RedisCluster|bool|array; + public function zpopmax(string $key, ?int $value = null): RedisCluster|bool|array; /** * @see Redis::zpopmin */ - public function zpopmin(string $key, int $value = null): RedisCluster|bool|array; + public function zpopmin(string $key, ?int $value = null): RedisCluster|bool|array; /** * @see Redis::zrange @@ -1091,7 +1091,7 @@ public function zrangestore(string $dstkey, string $srckey, int $start, int $end /** * @see https://redis.io/commands/zRandMember */ - public function zrandmember(string $key, array $options = null): RedisCluster|string|array; + public function zrandmember(string $key, ?array $options = null): RedisCluster|string|array; /** * @see Redis::zrangebylex @@ -1131,17 +1131,17 @@ public function zremrangebyscore(string $key, string $min, string $max): RedisCl /** * @see Redis::zrevrange */ - public function zrevrange(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrange(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrangebylex */ - public function zrevrangebylex(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrangebylex(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrangebyscore */ - public function zrevrangebyscore(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrangebyscore(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrank @@ -1166,7 +1166,7 @@ public function zmscore(string $key, mixed $member, mixed ...$other_members): Re /** * @see Redis::zunionstore */ - public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): RedisCluster|int|false; + public function zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): RedisCluster|int|false; /** * @see https://redis.io/commands/zinter @@ -1186,7 +1186,7 @@ public function zunion(array $keys, ?array $weights = null, ?array $options = nu /** * @see https://redis.io/commands/zdiff */ - public function zdiff(array $keys, array $options = null): RedisCluster|array|false; + public function zdiff(array $keys, ?array $options = null): RedisCluster|array|false; } class RedisClusterException extends RuntimeException {} diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 2cd817df79..a853337d34 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -3,12 +3,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seeds, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seeds, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_MASK(0, timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0") ZEND_ARG_TYPE_MASK(0, read_timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, _IS_BOOL, 0, "false") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__compress, 0, 1, IS_STRING, 0) @@ -168,7 +168,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_copy, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decr, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -406,7 +406,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hrandfield, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hset, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -479,7 +479,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) @@ -680,7 +680,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_set, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setbit, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -825,9 +825,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() @@ -928,7 +928,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zpopmax, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zpopmin arginfo_class_RedisCluster_zpopmax @@ -986,7 +986,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrevrange ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zrevrangebylex arginfo_class_RedisCluster_zrevrange @@ -1035,7 +1035,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zdiff, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php index 486ac438ac..32551e1dd5 100644 --- a/redis_sentinel.stub.php +++ b/redis_sentinel.stub.php @@ -8,7 +8,7 @@ class RedisSentinel { - public function __construct(array $options = null); + public function __construct(?array $options = null); /** @return bool|RedisSentinel */ public function ckquorum(string $master); diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h index e917b7d5ba..7ef1ae42be 100644 --- a/redis_sentinel_arginfo.h +++ b/redis_sentinel_arginfo.h @@ -2,7 +2,7 @@ * Stub hash: f1f746cc848b1debcdf88eae015732720ba206c8 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1) From 00118ba317c2301640d1807d6f20bb7c3aa6e58f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 23 Jul 2023 08:46:52 +0300 Subject: [PATCH 1783/1986] 6.0.0RC1 --- CHANGELOG.md | 581 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.xml | 493 +++++++++++++++++++++++++++++++++++++------ php_redis.h | 2 +- 3 files changed, 1014 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c345854e68..1347098003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,587 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [6.0.0RC1] - 2023-08-01 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0RC1), [PECL](https://pecl.php.net/package/redis/6.0.0RC1)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fix restoring keys when using compression + [82e08723](https://github.com/phpredis/phpredis/commit/82e08723) + ([Till Krüss](https://github.com/tillkruss)) +- Fix missing auth in RedisSentinel stub + [5db85561](https://github.com/phpredis/phpredis/commit/5db85561) + ([Lu Fei](https://github.com/sy-records)) +- Fix RedisSentinel pconnect check + [42cbd88a](https://github.com/phpredis/phpredis/commit/42cbd88a) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix NULL-pointer dereferences and handle possible UB + [36457555](https://github.com/phpredis/phpredis/commit/36457555) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix security alerts + [ee210f86](https://github.com/phpredis/phpredis/commit/ee210f86), + [fb6a297c](https://github.com/phpredis/phpredis/commit/fb6a297c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix segfault + [55bf0202](https://github.com/phpredis/phpredis/commit/55bf0202) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix default host length + [c40f9d6c](https://github.com/phpredis/phpredis/commit/c40f9d6c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix redis session standalone stream ssl context + [ed10f365](https://github.com/phpredis/phpredis/commit/ed10f365), + [d1bc6727](https://github.com/phpredis/phpredis/commit/d1bc6727), + [2ff11df5](https://github.com/phpredis/phpredis/commit/2ff11df5) + ([patricio.dorantes](https://github.com/patricio.dorantes)) +- Fix segfault with session+tls + [a471c87a](https://github.com/phpredis/phpredis/commit/a471c87a) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix non standards conforming prototypes. + [b3ce0486](https://github.com/phpredis/phpredis/commit/b3ce0486) + ([Michael Grunder](https://github.com/michael-grunder)) +- Avoid registering the same replicas multiple times + [f2bfd723](https://github.com/phpredis/phpredis/commit/f2bfd723) + ([Marius Adam](https://github.com/mariusadam)) +- Better unix:// or file:// detection. + [d05d301b](https://github.com/phpredis/phpredis/commit/d05d301b) + ([Michael Grunder](https://github.com/michael-grunder)) +- Future proof our igbinary header check + [69355faa](https://github.com/phpredis/phpredis/commit/69355faa) + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix BITOP cross-slot bug + [af13f951](https://github.com/phpredis/phpredis/commit/af13f951) + ([Michael Grunder](https://github.com/michael-grunder)) +- SENTINEL RESET returns a long. + [0243dd9d](https://github.com/phpredis/phpredis/commit/0243dd9d) + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix redis_sock_read_multibulk_multi_reply_loop logic + [d9cb5946](https://github.com/phpredis/phpredis/commit/d9cb5946), + [5a643b62](https://github.com/phpredis/phpredis/commit/5a643b62) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix RPOP to unserialize/decompress data. + [02c91d59](https://github.com/phpredis/phpredis/commit/02c91d59) + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix testObject for redis 7.2 + [fea19b52](https://github.com/phpredis/phpredis/commit/fea19b52), + [dcb95a3f](https://github.com/phpredis/phpredis/commit/dcb95a3f) + ([Remi Collet](https://github.com/remicollet)) +- Fix bug: the pipeline mode socket return an unexpected result after reconnecting + [a3327d9d](https://github.com/phpredis/phpredis/commit/a3327d9d) + ([thomaston](https://github.com/omigafu)) +- Fix stub files + [9aa5f387](https://github.com/phpredis/phpredis/commit/9aa5f387), + [74cf49f5](https://github.com/phpredis/phpredis/commit/74cf49f5), + [8b1eafe8](https://github.com/phpredis/phpredis/commit/8b1eafe8), + [e392dd88](https://github.com/phpredis/phpredis/commit/e392dd88), + [b5ea5fd7](https://github.com/phpredis/phpredis/commit/b5ea5fd7), + [71758b09](https://github.com/phpredis/phpredis/commit/71758b09), + [2a6dee5d](https://github.com/phpredis/phpredis/commit/2a6dee5d) + ([Nicolas Grekas](https://github.com/nicolas-grekas)), + ([Michael Grunder](https://github.com/michael-grunder)) +- Update documentation + [b64d93e1](https://github.com/phpredis/phpredis/commit/b64d93e1), + [703d71b5](https://github.com/phpredis/phpredis/commit/703d71b5), + [eba1c6d2](https://github.com/phpredis/phpredis/commit/eba1c6d2), + [0f502c9e](https://github.com/phpredis/phpredis/commit/0f502c9e), + [130b5d0b](https://github.com/phpredis/phpredis/commit/130b5d0b), + [21c3ef94](https://github.com/phpredis/phpredis/commit/21c3ef94), + [b7bf22d4](https://github.com/phpredis/phpredis/commit/b7bf22d4), + [50151e7a](https://github.com/phpredis/phpredis/commit/50151e7a), + [b9950727](https://github.com/phpredis/phpredis/commit/b9950727), + [ab4ce4ab](https://github.com/phpredis/phpredis/commit/ab4ce4ab), + [8d80ca5b](https://github.com/phpredis/phpredis/commit/8d80ca5b), + [c4de8667](https://github.com/phpredis/phpredis/commit/c4de8667), + [6982941b](https://github.com/phpredis/phpredis/commit/6982941b), + [375d093d](https://github.com/phpredis/phpredis/commit/375d093d), + [43da8dd9](https://github.com/phpredis/phpredis/commit/43da8dd9), + [71344612](https://github.com/phpredis/phpredis/commit/71344612), + [b9de0b97](https://github.com/phpredis/phpredis/commit/b9de0b97), + [2d8a8a44](https://github.com/phpredis/phpredis/commit/2d8a8a44), + [a2b0c86f](https://github.com/phpredis/phpredis/commit/a2b0c86f), + [e0b24be1](https://github.com/phpredis/phpredis/commit/e0b24be1), + [e609fbe8](https://github.com/phpredis/phpredis/commit/e609fbe8), + [c4aef956](https://github.com/phpredis/phpredis/commit/c4aef956), + [df50b2ad](https://github.com/phpredis/phpredis/commit/df50b2ad), + [cc2383f0](https://github.com/phpredis/phpredis/commit/cc2383f0), + [0dd2836f](https://github.com/phpredis/phpredis/commit/0dd2836f), + [7d5db510](https://github.com/phpredis/phpredis/commit/7d5db510), + [99340889](https://github.com/phpredis/phpredis/commit/99340889), + [70a55f3e](https://github.com/phpredis/phpredis/commit/70a55f3e), + [b04684d4](https://github.com/phpredis/phpredis/commit/b04684d4), + [980ea6b1](https://github.com/phpredis/phpredis/commit/980ea6b1), + [bb06ffa3](https://github.com/phpredis/phpredis/commit/bb06ffa3), + [b8679d7a](https://github.com/phpredis/phpredis/commit/b8679d7a), + [854f3aa4](https://github.com/phpredis/phpredis/commit/854f3aa4), + [a5c47901](https://github.com/phpredis/phpredis/commit/a5c47901), + [cf63e96e](https://github.com/phpredis/phpredis/commit/cf63e96e), + [f05ba819](https://github.com/phpredis/phpredis/commit/f05ba819), + [17db2328](https://github.com/phpredis/phpredis/commit/17db2328), + [450904f7](https://github.com/phpredis/phpredis/commit/450904f7), + [114f4d60](https://github.com/phpredis/phpredis/commit/114f4d60), + [142bddf0](https://github.com/phpredis/phpredis/commit/142bddf0), + [87fa36d6](https://github.com/phpredis/phpredis/commit/87fa36d6), + [531177d4](https://github.com/phpredis/phpredis/commit/531177d4), + [ecf65144](https://github.com/phpredis/phpredis/commit/ecf65144), + [53d142d9](https://github.com/phpredis/phpredis/commit/53d142d9), + [c14a9e3a](https://github.com/phpredis/phpredis/commit/c14a9e3a), + [72f8eb25](https://github.com/phpredis/phpredis/commit/72f8eb25), + [872b6931](https://github.com/phpredis/phpredis/commit/872b6931) + ([Karina Kwiatek](https://github.com/raccube)), + ([Nicolas Grekas](https://github.com/nicolas-grekas)), + ([Muhammad Dyas Yaskur](https://github.com/dyaskur)), + ([sergkash7](https://github.com/sergkash7)), + ([Dawid Polak](https://github.com/DeyV)), + ([Michael Grunder](https://github.com/michael-grunder)), + ([Yurun](https://github.com/Yurunsoft)), + ([twosee](https://github.com/twose)), + ([Juha](https://github.com/ejuhjav)), + ([Till Krüss](https://github.com/tillkruss)) + +### Changed + +- Allow to pass null as iterator + [14d121bb](https://github.com/phpredis/phpredis/commit/14d121bb) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add NOMKSTREAM option to XADD command. + [f9436e25](https://github.com/phpredis/phpredis/commit/f9436e25) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Don't allow reconnect on read response + [5a269ab6](https://github.com/phpredis/phpredis/commit/5a269ab6) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Reset multi/pipline transaction on pconnect close + [0879770a](https://github.com/phpredis/phpredis/commit/0879770a) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use read_mbulk_header helper where possible + [ca8b4c93](https://github.com/phpredis/phpredis/commit/ca8b4c93) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow to pass null as auth argument + [41517753](https://github.com/phpredis/phpredis/commit/41517753) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor redis_parse_client_list_response + [68136a29](https://github.com/phpredis/phpredis/commit/68136a29), + [aaa4c91a](https://github.com/phpredis/phpredis/commit/aaa4c91a), + [1fb2935b](https://github.com/phpredis/phpredis/commit/1fb2935b), + [cf2c052c](https://github.com/phpredis/phpredis/commit/cf2c052c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor subscribe/unsubscribe + [3c9e159c](https://github.com/phpredis/phpredis/commit/3c9e159c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Change PHPREDIS_CTX_PTR type + [de3635da](https://github.com/phpredis/phpredis/commit/de3635da) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor redis_parse_info_response + [982bd13b](https://github.com/phpredis/phpredis/commit/982bd13b) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow IPv6 address within square brackets + [c28ad7bb](https://github.com/phpredis/phpredis/commit/c28ad7bb) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow multiple field-value pairs for hmset command. + [e858e8e3](https://github.com/phpredis/phpredis/commit/e858e8e3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor MINIT and use @generate-class-entries in stub files + [3675f442](https://github.com/phpredis/phpredis/commit/3675f442) + ([Remi Collet](https://github.com/remicollet)) +- Use spl_ce_RuntimeException + [3cd5ac1e](https://github.com/phpredis/phpredis/commit/3cd5ac1e), + [a7e5ea64](https://github.com/phpredis/phpredis/commit/a7e5ea64) + ([Remi Collet](https://github.com/remicollet)) +- Regenerate arginfo using 8.2.0 + [a38e08da](https://github.com/phpredis/phpredis/commit/a38e08da) + ([Remi Collet](https://github.com/remicollet)) +- Refactor client command + [a8d10291](https://github.com/phpredis/phpredis/commit/a8d10291) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Pull COUNT/ANY parsing into a helper function + [d67b2020](https://github.com/phpredis/phpredis/commit/d67b2020) + ([Michael Grunder](https://github.com/michael-grunder)) +- Return false or NULL on empty lpos response + [39a01ac7](https://github.com/phpredis/phpredis/commit/39a01ac7) + ([Michael Grunder](https://github.com/michael-grunder)) +- BLPOP with a float timeout + [a98605f2](https://github.com/phpredis/phpredis/commit/a98605f2), + [dc9af529](https://github.com/phpredis/phpredis/commit/dc9af529) + ([Michael Grunder](https://github.com/michael-grunder)) +- Make sure we set an error for key based scans + [98fda1b8](https://github.com/phpredis/phpredis/commit/98fda1b8) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add back a default switch case for setoption handler + [87464932](https://github.com/phpredis/phpredis/commit/87464932) + ([Michael Grunder](https://github.com/michael-grunder)) +- Update stubs so the tests pass in strict mode + [bebd398c](https://github.com/phpredis/phpredis/commit/bebd398c) + ([Michael Grunder](https://github.com/michael-grunder)) +- Move where we generate our salt + [d2044c9f](https://github.com/phpredis/phpredis/commit/d2044c9f) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor XINFO handler + [3b0d8b77](https://github.com/phpredis/phpredis/commit/3b0d8b77) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor and fix XPENDING handler + [457953f4](https://github.com/phpredis/phpredis/commit/457953f4) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor FLUSHDB and update docs. + [54a084e5](https://github.com/phpredis/phpredis/commit/54a084e5) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add missing directed node command to docs and refactor stubs. + [5ac92d25](https://github.com/phpredis/phpredis/commit/5ac92d25) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor BITPOS and implement BIT/BYTE option. + [4d8afd38](https://github.com/phpredis/phpredis/commit/4d8afd38) + ([Michael Grunder](https://github.com/michael-grunder)) +- INFO with multiple sections + [44d03ca0](https://github.com/phpredis/phpredis/commit/44d03ca0) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SLOWLOG command + [d87f1428](https://github.com/phpredis/phpredis/commit/d87f1428) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SORT and add SORT_RO command + [8c7c5a3a](https://github.com/phpredis/phpredis/commit/8c7c5a3a) + ([Michael Grunder](https://github.com/michael-grunder)) +- Use ZEND_STRL in redis_commands.c + [78de25a3](https://github.com/phpredis/phpredis/commit/78de25a3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor PubSub command + [2a0d1c1e](https://github.com/phpredis/phpredis/commit/2a0d1c1e) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor SLAVEOF handler + [f2cef8be](https://github.com/phpredis/phpredis/commit/f2cef8be) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor ACL command + [504810a5](https://github.com/phpredis/phpredis/commit/504810a5) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use fast_zpp API + [376d4d27](https://github.com/phpredis/phpredis/commit/376d4d27) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix XAUTOCLAIM response handler + [0b7bd83f](https://github.com/phpredis/phpredis/commit/0b7bd83f) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor `command` command + [ff863f3f](https://github.com/phpredis/phpredis/commit/ff863f3f) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor rawCommand and WAIT + [79c9d224](https://github.com/phpredis/phpredis/commit/79c9d224) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SELECT command + [86f15cca](https://github.com/phpredis/phpredis/commit/86f15cca) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SRANDMEMBER command. + [f62363c2](https://github.com/phpredis/phpredis/commit/f62363c2) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor OBJECT command. + [acb5db76](https://github.com/phpredis/phpredis/commit/acb5db76) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor gen_varkey_cmd + [3efa59cb](https://github.com/phpredis/phpredis/commit/3efa59cb) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor MGET command. + [8cb6dd17](https://github.com/phpredis/phpredis/commit/8cb6dd17) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor INFO and SCRIPT commands. + [3574ef08](https://github.com/phpredis/phpredis/commit/3574ef08) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor MSET and MSETNX commands. + [6d104481](https://github.com/phpredis/phpredis/commit/6d104481) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor HMSET command. + [90eb0470](https://github.com/phpredis/phpredis/commit/90eb0470) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor PFCOUNT command. + [19fd7e0c](https://github.com/phpredis/phpredis/commit/19fd7e0c) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SMOVE command. + [204a02c5](https://github.com/phpredis/phpredis/commit/204a02c5) + ([Michael Grunder](https://github.com/michael-grunder)) +- Rework ZRANGE argument handling. + [aa0938a4](https://github.com/phpredis/phpredis/commit/aa0938a4) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor a couple more command methods. + [5b560ccf](https://github.com/phpredis/phpredis/commit/5b560ccf), + [c8224b93](https://github.com/phpredis/phpredis/commit/c8224b93), + [40e1b1bf](https://github.com/phpredis/phpredis/commit/40e1b1bf), + [ccd419a4](https://github.com/phpredis/phpredis/commit/ccd419a4) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor HMGET command + [bb66a547](https://github.com/phpredis/phpredis/commit/bb66a547) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor CLIENT command + [77c4f7a3](https://github.com/phpredis/phpredis/commit/77c4f7a3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor redis_long_response + [f14a80db](https://github.com/phpredis/phpredis/commit/f14a80db) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Synchronize Redis and RedisSentinel constructors + [ebb2386e](https://github.com/phpredis/phpredis/commit/ebb2386e) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use redis_sock_connect on connect + [f6c8b9c6](https://github.com/phpredis/phpredis/commit/f6c8b9c6) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Auto-select db in redis_sock_server_open + [6930a81c](https://github.com/phpredis/phpredis/commit/6930a81c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use on-stack allocated valiables + [7a055cad](https://github.com/phpredis/phpredis/commit/7a055cad) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Added + +- Add XAUTOCLAIM command + [01f3342c](https://github.com/phpredis/phpredis/commit/01f3342c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH + [750b6cf3](https://github.com/phpredis/phpredis/commit/750b6cf3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add reset command + [947a2d38](https://github.com/phpredis/phpredis/commit/947a2d38) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add hRandField command + [fe397371](https://github.com/phpredis/phpredis/commit/fe397371) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add PXAT/EXAT arguments to SET command. + [0a160685](https://github.com/phpredis/phpredis/commit/0a160685) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add GETEX, GETDEL commands. + [11861d95](https://github.com/phpredis/phpredis/commit/11861d95) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add FAILOVER command. + [4b767be7](https://github.com/phpredis/phpredis/commit/4b767be7) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Backoff settings in constructor + [e6b3fe54](https://github.com/phpredis/phpredis/commit/e6b3fe54) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add the COUNT argument to LPOP and RPOP + [df97cc35](https://github.com/phpredis/phpredis/commit/df97cc35) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Unsubscribe from all channels + [0f1ca0cc](https://github.com/phpredis/phpredis/commit/0f1ca0cc) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add lPos command. + [687a5c78](https://github.com/phpredis/phpredis/commit/687a5c78) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add the ANY argument to GEOSEARCH and GEORADIUS + [bf6f31e3](https://github.com/phpredis/phpredis/commit/bf6f31e3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add 'BIT'/'BYTE' modifier to BITCOUNT + tests + [a3d2f131](https://github.com/phpredis/phpredis/commit/a3d2f131) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add missing configureoption entries in package.xml + [59053f10](https://github.com/phpredis/phpredis/commit/59053f10) + ([Michele Locati](https://github.com/mlocati)) +- Implement CONFIG RESETSTAT + [239678a0](https://github.com/phpredis/phpredis/commit/239678a0) + ([Michael Grunder](https://github.com/michael-grunder)) +- SINTERCARD and ZINTERCARD commands + [64300508](https://github.com/phpredis/phpredis/commit/64300508) + ([Michael Grunder](https://github.com/michael-grunder)) +- LCS command + [c0e839f6](https://github.com/phpredis/phpredis/commit/c0e839f6) + ([Michael Grunder](https://github.com/michael-grunder)) +- EXPIRETIME and PEXPIRETIME + [f5b2a09b](https://github.com/phpredis/phpredis/commit/f5b2a09b) + ([Michael Grunder](https://github.com/michael-grunder)) +- [B]LMPOP and [B]ZMPOP commands + [6ea978eb](https://github.com/phpredis/phpredis/commit/6ea978eb) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement new RESTORE options + [9a3fe401](https://github.com/phpredis/phpredis/commit/9a3fe401) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add new Redis 6.2.0 XTRIM options + [6b34d17f](https://github.com/phpredis/phpredis/commit/6b34d17f) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement AUTH/AUTH2 arguments for MIGRATE + [114d79d1](https://github.com/phpredis/phpredis/commit/114d79d1) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement CONFIG REWRITE + [525958ea](https://github.com/phpredis/phpredis/commit/525958ea) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement Redis 7.0.0 [P]EXPIRE[AT] options + [872ae107](https://github.com/phpredis/phpredis/commit/872ae107) + ([Michael Grunder](https://github.com/michael-grunder)) +- Variadic CONFIG GET/SET + [36ef4bd8](https://github.com/phpredis/phpredis/commit/36ef4bd8), + [a176f586](https://github.com/phpredis/phpredis/commit/a176f586) + ([Michael Grunder](https://github.com/michael-grunder)) +- EVAL_RO and EVALSHA_RO + [f3a40830](https://github.com/phpredis/phpredis/commit/f3a40830) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement ZRANGESTORE and add ZRANGE options + [71bcbcb9](https://github.com/phpredis/phpredis/commit/71bcbcb9) + ([Michael Grunder](https://github.com/michael-grunder)) +- XGROUP DELCONSUMER and ENTRIESREAD + [1343f500](https://github.com/phpredis/phpredis/commit/1343f500) + ([Michael Grunder](https://github.com/michael-grunder)) +- Expose the transferred number of bytes + [e0a88b7b](https://github.com/phpredis/phpredis/commit/e0a88b7b), + [90828019](https://github.com/phpredis/phpredis/commit/90828019), + [7a4cee2d](https://github.com/phpredis/phpredis/commit/7a4cee2d) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Michael Grunder](https://github.com/michael-grunder)) +- TOUCH command + [dc1f2398](https://github.com/phpredis/phpredis/commit/dc1f2398) + ([Michael Grunder](https://github.com/michael-grunder)) +- Redis Sentinel TLS support + [f2bb2cdb](https://github.com/phpredis/phpredis/commit/f2bb2cdb) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add the CH, NX, XX arguments to GEOADD + [2bb64038](https://github.com/phpredis/phpredis/commit/2bb64038), + [e8f5b517](https://github.com/phpredis/phpredis/commit/e8f5b517) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Implement SMISMEMBER for RedisCluster + [abfac47b](https://github.com/phpredis/phpredis/commit/abfac47b) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement ssubscribe/sunsubscribe + [7644736e](https://github.com/phpredis/phpredis/commit/7644736e) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Implement BLMOVE and add LMOVE/BLMOVE to cluster. + [121e9d9c](https://github.com/phpredis/phpredis/commit/121e9d9c) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement LPOS for RedisCluster + [7121aaae](https://github.com/phpredis/phpredis/commit/7121aaae) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. + [fa5d1af9](https://github.com/phpredis/phpredis/commit/fa5d1af9) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement HRANDFIELD for RedisCluster + [e222b85e](https://github.com/phpredis/phpredis/commit/e222b85e) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement COPY for RedisCluster + [40a2c254](https://github.com/phpredis/phpredis/commit/40a2c254) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement new ZSET commands for cluster + [27900f39](https://github.com/phpredis/phpredis/commit/27900f39) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add cluster support for strict sessions and lazy write + [b6cf6361](https://github.com/phpredis/phpredis/commit/b6cf6361) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add function command + [90a0e9cc](https://github.com/phpredis/phpredis/commit/90a0e9cc) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add FCALL/FCALL_RO commands + [7c46ad2c](https://github.com/phpredis/phpredis/commit/7c46ad2c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Removed + +- Remove unused macroses + [831d6118](https://github.com/phpredis/phpredis/commit/831d6118) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +## [5.3.7] - 2021-02-15 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.7), [PECL](https://pecl.php.net/package/redis/5.3.7)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +*There were no changes between 5.3.7 and 5.3.7RC2* + +## [5.3.7RC2] - 2021-02-12 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.7RC2), [PECL](https://pecl.php.net/package/redis/5.3.7RC2)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +*There were no changes between 5.3.7RC2 and 5.3.7RC1* + +## [5.3.7RC1] - 2021-02-02 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.7RC1), [PECL](https://pecl.php.net/package/redis/5.3.7RC1)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fix RedisArray::[hsz]scan and tests + [08a9d5db](https://github.com/phpredis/phpredis/commit/08a9d5db), + [0264de18](https://github.com/phpredis/phpredis/commit/0264de18), + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix RedisArray::scan + [8689ab1c](https://github.com/phpredis/phpredis/commit/8689ab1c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix LZF decompression logic + [0719c1ec](https://github.com/phpredis/phpredis/commit/0719c1ec) + ([Michael Grunder](https://github.com/michael-grunder)) + +## [5.3.6] - 2021-01-17 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.6), [PECL](https://pecl.php.net/package/redis/5.3.6)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fix a segfault in RedisArray::del + [d2f2a7d9](https://github.com/phpredis/phpredis/commit/d2f2a7d9) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +## [5.3.5] - 2021-12-18 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.5), [PECL](https://pecl.php.net/package/redis/5.3.5)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fixed typo in cluster_scan_resp + [44affad2](https://github.com/phpredis/phpredis/commit/44affad2) + ## [5.3.5RC1] - 2021-11-16 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.5RC1), [PECL](https://pecl.php.net/package/redis/5.3.5RC1)) ### Sponsors :sparkling_heart: diff --git a/package.xml b/package.xml index cdc815a526..7df8827481 100644 --- a/package.xml +++ b/package.xml @@ -27,22 +27,21 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2021-11-16 + 2023-08-01 - 5.3.5RC1 - 5.3.5RC1 + 6.0.0RC1 + 6.0.0 - beta - beta + alpha + alpha PHP - phpredis 5.3.5RC1 + phpredis 6.0.0RC1 - This release adds support for exponential backoff w/jitter, experimental - support for detecting a dirty connection, as well as many other fixes - and improvements. + This release adds new commands introduced in Redis 6.2 and 7.0 as well + as many fixes and improvements. You can find a detailed list of changes in CHANGELOG.md and package.xml or by inspecting the git commit logs. @@ -60,58 +59,129 @@ http://pear.php.net/dtd/package-2.0.xsd"> --- - * Fixed segfault in redis_setoption_handler [692e4e84] (Pavlo Yatsukhnenko) - * Fix masters array in the event of a cluster failover [bce692962] (Bar Shaul) - * Fix 32 bit type error [672dec87f] (Remi Collet) - * Fix radix character in certain locales [89a871e24] (Pavlo Yatsukhnenko) - * ZSTD Validation fix [6a77ef5cd] (Michael Grunder) - * Remove superfluous typecast [b2871471f] (Remi Collet) - - * Updated documentation [f84168657, d017788e7, 20ac84710, 0adf05260, - aee29bf73, 09a095e72, 12ffbf33a, ff331af98, a6bdb8731, 305c15840, - 1aa10e93a, d78b0c79d, c6d37c27c, a6303f5b9, d144bd2c7, a6fb815ef, 9ef862bc6] - (neodisco, Clement Tessier, T. Todua, dengliming, Maxime Cornet, - Emanuele Filannino Michael Grunder) - - * Travis CI Fixes - [a43f4586e, 4fde8178f, 7bd5415ac, fdb8c4bb7, d4f407470] - (Pavlo Yatsukhnenko) - - * Minor fixes/cleanup - [2e190adc1, 99975b592, 9d0879fa5, 22b06457b] - (Pavlo Yatsukhnenko) - - * Fix RedisArray constructor bug - [85dc883ba](https://github.com/phpredis/phpredis/commit/85dc883ba) - ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - - * Moved to GitHub Actions - [4d2afa786, 502d09fd5] (Pavlo Yatsukhnenko) - - * Use more appropriate array iteration macro - [6008900c2] (Pavlo Yatsukhnenko) - - * Clean up session tests - [ab25ae7f3] (Michael Grunder) - - * RedisArray refactors [1250f0001, 017b2ea7f, 37ed3f079] - (Pavlo Yatsukhnenko) - - * Use zend_parse_parameters_none helper - [a26b14dbe] (Remi Collet) - - * Support for various exponential backoff strategies - [#1986, #1993, 732eb8dcb, 05129c3a3, 5bba6a7fc], - (Nathaniel Braun) - - * Added experimental support for detecting a dirty connection - [d68579562] (Michael Grunder) - - * Created distinct compression utility methods (pack/unpack) - [#1939, da2790aec] (Michael Grunder) - - * SMISMEMBER Command - [#1894, ae2382472, ed283e1ab] (Pavlo Yatsukhnenko) + * Fix restoring keys when using compression [82e08723] (Till Krüss) + * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) + * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) + * Fix NULL-pointer dereferences and handle possible UB [36457555] (Pavlo Yatsukhnenko) + * Fix security alerts [ee210f86, fb6a297c] (Pavlo Yatsukhnenko), (Michael Grunder) + * Fix segfault [55bf0202] (Pavlo Yatsukhnenko) + * Fix default host length [c40f9d6c] (Pavlo Yatsukhnenko) + * Fix redis session standalone stream ssl context [ed10f365, d1bc6727, 2ff11df5] (patricio.dorantes) + * Fix segfault with session+tls [a471c87a] (Pavlo Yatsukhnenko) + * Fix non standards conforming prototypes. [b3ce0486] (Michael Grunder) + * Avoid registering the same replicas multiple times [f2bfd723] (Marius Adam) + * Better unix:// or file:// detection. [d05d301b] (Michael Grunder) + * Future proof our igbinary header check [69355faa] (Michael Grunder) + * Fix BITOP cross-slot bug [af13f951] (Michael Grunder) + * SENTINEL RESET returns a long. [0243dd9d] (Michael Grunder) + * Fix redis_sock_read_multibulk_multi_reply_loop logic [d9cb5946, 5a643b62] (Pavlo Yatsukhnenko) + * Fix RPOP to unserialize/decompress data. [02c91d59] (Michael Grunder) + * Fix testObject for redis 7.2 [fea19b52, dcb95a3f] (Remi Collet) + * Fix bug: the pipeline mode socket return an unexpected result after reconnecting [a3327d9d] (thomaston) + * Fix stub files [9aa5f387, 74cf49f5, 8b1eafe8, e392dd88, b5ea5fd7, 71758b09, 2a6dee5d] (Nicolas Grekas), (Michael Grunder) + * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Krüss) + * Allow to pass null as iterator [14d121bb] (Pavlo Yatsukhnenko) + * Add NOMKSTREAM option to XADD command. [f9436e25] (Pavlo Yatsukhnenko) + * Don't allow reconnect on read response [5a269ab6] (Pavlo Yatsukhnenko) + * Reset multi/pipline transaction on pconnect close [0879770a] (Pavlo Yatsukhnenko) + * Use read_mbulk_header helper where possible [ca8b4c93] (Pavlo Yatsukhnenko) + * Allow to pass null as auth argument [41517753] (Pavlo Yatsukhnenko) + * Refactor redis_parse_client_list_response [68136a29, aaa4c91a, 1fb2935b, cf2c052c] (Pavlo Yatsukhnenko) + * Refactor subscribe/unsubscribe [3c9e159c] (Pavlo Yatsukhnenko) + * Change PHPREDIS_CTX_PTR type [de3635da] (Pavlo Yatsukhnenko) + * Refactor redis_parse_info_response [982bd13b] (Pavlo Yatsukhnenko) + * Allow IPv6 address within square brackets [c28ad7bb] (Pavlo Yatsukhnenko) + * Allow multiple field-value pairs for hmset command. [e858e8e3] (Pavlo Yatsukhnenko) + * Refactor MINIT and use @generate-class-entries in stub files [3675f442] (Remi Collet) + * Use spl_ce_RuntimeException [3cd5ac1e, a7e5ea64] (Remi Collet) + * Regenerate arginfo using 8.2.0 [a38e08da] (Remi Collet) + * Refactor client command [a8d10291] (Pavlo Yatsukhnenko) + * Pull COUNT/ANY parsing into a helper function [d67b2020] (Michael Grunder) + * Return false or NULL on empty lpos response [39a01ac7] (Michael Grunder) + * BLPOP with a float timeout [a98605f2, dc9af529] (Michael Grunder) + * Make sure we set an error for key based scans [98fda1b8] (Michael Grunder) + * Add back a default switch case for setoption handler [87464932] (Michael Grunder) + * Update stubs so the tests pass in strict mode [bebd398c] (Michael Grunder) + * Move where we generate our salt [d2044c9f] (Michael Grunder) + * Refactor XINFO handler [3b0d8b77] (Michael Grunder) + * Refactor and fix XPENDING handler [457953f4] (Michael Grunder) + * Refactor FLUSHDB and update docs. [54a084e5] (Michael Grunder) + * Add missing directed node command to docs and refactor stubs. [5ac92d25] (Michael Grunder) + * Refactor BITPOS and implement BIT/BYTE option. [4d8afd38] (Michael Grunder) + * INFO with multiple sections [44d03ca0] (Michael Grunder) + * Refactor SLOWLOG command [d87f1428] (Michael Grunder) + * Refactor SORT and add SORT_RO command [8c7c5a3a] (Michael Grunder) + * Use ZEND_STRL in redis_commands.c [78de25a3] (Pavlo Yatsukhnenko) + * Refactor PubSub command [2a0d1c1e] (Pavlo Yatsukhnenko) + * Refactor SLAVEOF handler [f2cef8be] (Michael Grunder) + * Refactor ACL command [504810a5] (Pavlo Yatsukhnenko) + * Use fast_zpp API [376d4d27] (Pavlo Yatsukhnenko) + * Fix XAUTOCLAIM response handler [0b7bd83f] (Michael Grunder) + * Refactor command command [ff863f3f] (Pavlo Yatsukhnenko) + * Refactor rawCommand and WAIT [79c9d224] (Michael Grunder) + * Refactor SELECT command [86f15cca] (Michael Grunder) + * Refactor SRANDMEMBER command. [f62363c2] (Michael Grunder) + * Refactor OBJECT command. [acb5db76] (Michael Grunder) + * Refactor gen_varkey_cmd [3efa59cb] (Michael Grunder) + * Refactor MGET command. [8cb6dd17] (Michael Grunder) + * Refactor INFO and SCRIPT commands. [3574ef08] (Michael Grunder) + * Refactor MSET and MSETNX commands. [6d104481] (Michael Grunder) + * Refactor HMSET command. [90eb0470] (Michael Grunder) + * Refactor PFCOUNT command. [19fd7e0c] (Michael Grunder) + * Refactor SMOVE command. [204a02c5] (Michael Grunder) + * Rework ZRANGE argument handling. [aa0938a4] (Michael Grunder) + * Refactor a couple more command methods. [5b560ccf, c8224b93, 40e1b1bf, ccd419a4] (Michael Grunder) + * Refactor HMGET command [bb66a547] (Michael Grunder) + * Refactor CLIENT command [77c4f7a3] (Pavlo Yatsukhnenko) + * Refactor redis_long_response [f14a80db] (Pavlo Yatsukhnenko) + * Synchronize Redis and RedisSentinel constructors [ebb2386e] (Pavlo Yatsukhnenko) + * Use redis_sock_connect on connect [f6c8b9c6] (Pavlo Yatsukhnenko) + * Auto-select db in redis_sock_server_open [6930a81c] (Pavlo Yatsukhnenko) + * Use on-stack allocated valiables [7a055cad] (Pavlo Yatsukhnenko) + * Add XAUTOCLAIM command [01f3342c] (Pavlo Yatsukhnenko) + * Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH [750b6cf3] (Pavlo Yatsukhnenko) + * Add reset command [947a2d38] (Pavlo Yatsukhnenko) + * Add hRandField command [fe397371] (Pavlo Yatsukhnenko) + * Add PXAT/EXAT arguments to SET command. [0a160685] (Pavlo Yatsukhnenko) + * Add GETEX, GETDEL commands. [11861d95] (Pavlo Yatsukhnenko) + * Add FAILOVER command. [4b767be7] (Pavlo Yatsukhnenko) + * Backoff settings in constructor [e6b3fe54] (Pavlo Yatsukhnenko) + * Add the COUNT argument to LPOP and RPOP [df97cc35] (Pavlo Yatsukhnenko) + * Unsubscribe from all channels [0f1ca0cc] (Pavlo Yatsukhnenko) + * Add lPos command. [687a5c78] (Pavlo Yatsukhnenko) + * Add the ANY argument to GEOSEARCH and GEORADIUS [bf6f31e3] (Pavlo Yatsukhnenko) + * Add 'BIT'/'BYTE' modifier to BITCOUNT + tests [a3d2f131] (Michael Grunder) + * Add missing configureoption entries in package.xml [59053f10] (Michele Locati) + * Implement CONFIG RESETSTAT [239678a0] (Michael Grunder) + * SINTERCARD and ZINTERCARD commands [64300508] (Michael Grunder) + * LCS command [c0e839f6] (Michael Grunder) + * EXPIRETIME and PEXPIRETIME [f5b2a09b] (Michael Grunder) + * [B]LMPOP and [B]ZMPOP commands [6ea978eb] (Michael Grunder) + * Implement new RESTORE options [9a3fe401] (Michael Grunder) + * Add new Redis 6.2.0 XTRIM options [6b34d17f] (Michael Grunder) + * Implement AUTH/AUTH2 arguments for MIGRATE [114d79d1] (Michael Grunder) + * Implement CONFIG REWRITE [525958ea] (Michael Grunder) + * Implement Redis 7.0.0 [P]EXPIRE[AT] [options 872ae107] (Michael Grunder) + * Variadic CONFIG GET/SET [36ef4bd8, a176f586] (Michael Grunder) + * EVAL_RO and EVALSHA_RO [f3a40830] (Michael Grunder) + * Implement ZRANGESTORE and add ZRANGE options [71bcbcb9] (Michael Grunder) + * XGROUP DELCONSUMER and ENTRIESREAD [1343f500] (Michael Grunder) + * Expose the transferred number of bytes [e0a88b7b, 90828019, 7a4cee2d] (Pavlo Yatsukhnenko), (Michael Grunder) + * TOUCH command [dc1f2398] (Michael Grunder) + * Redis Sentinel TLS support [f2bb2cdb] (Pavlo Yatsukhnenko) + * Add the CH, NX, XX arguments to GEOADD [2bb64038, e8f5b517] (Pavlo Yatsukhnenko) + * Implement SMISMEMBER for RedisCluster [abfac47b] (Michael Grunder) + * Implement ssubscribe/sunsubscribe [7644736e] (Pavlo Yatsukhnenko) + * Implement BLMOVE and add LMOVE/BLMOVE to cluster. [121e9d9c] (Michael Grunder) + * Implement LPOS for RedisCluster [7121aaae] (Michael Grunder) + * Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. [fa5d1af9] (Michael Grunder) + * Implement HRANDFIELD for RedisCluster [e222b85e] (Michael Grunder) + * Implement COPY for RedisCluster [40a2c254] (Michael Grunder) + * Implement new ZSET commands for cluster [27900f39] (Michael Grunder) + * Add cluster support for strict sessions and lazy write [b6cf6361] (Michael Grunder) + * Add function command [90a0e9cc] (Pavlo Yatsukhnenko) + * Add FCALL/FCALL_RO commands [7c46ad2c] (Pavlo Yatsukhnenko) + * Remove unused macroses [831d6118] (Pavlo Yatsukhnenko) @@ -203,6 +273,307 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + alphaalpha + 6.0.0RC16.0.0 + 2023-08-01 + + phpredis 6.0.0RC1 + + This release adds new commands introduced in Redis 6.2 and 7.0 as well + as many fixes and improvements. + + You can find a detailed list of changes in CHANGELOG.md and package.xml + or by inspecting the git commit logs. + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Luis Zarate - https://github.com/jlzaratec + + --- + + * Fix restoring keys when using compression [82e08723] (Till Krüss) + * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) + * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) + * Fix NULL-pointer dereferences and handle possible UB [36457555] (Pavlo Yatsukhnenko) + * Fix security alerts [ee210f86, fb6a297c] (Pavlo Yatsukhnenko), (Michael Grunder) + * Fix segfault [55bf0202] (Pavlo Yatsukhnenko) + * Fix default host length [c40f9d6c] (Pavlo Yatsukhnenko) + * Fix redis session standalone stream ssl context [ed10f365, d1bc6727, 2ff11df5] (patricio.dorantes) + * Fix segfault with session+tls [a471c87a] (Pavlo Yatsukhnenko) + * Fix non standards conforming prototypes. [b3ce0486] (Michael Grunder) + * Avoid registering the same replicas multiple times [f2bfd723] (Marius Adam) + * Better unix:// or file:// detection. [d05d301b] (Michael Grunder) + * Future proof our igbinary header check [69355faa] (Michael Grunder) + * Fix BITOP cross-slot bug [af13f951] (Michael Grunder) + * SENTINEL RESET returns a long. [0243dd9d] (Michael Grunder) + * Fix redis_sock_read_multibulk_multi_reply_loop logic [d9cb5946, 5a643b62] (Pavlo Yatsukhnenko) + * Fix RPOP to unserialize/decompress data. [02c91d59] (Michael Grunder) + * Fix testObject for redis 7.2 [fea19b52, dcb95a3f] (Remi Collet) + * Fix bug: the pipeline mode socket return an unexpected result after reconnecting [a3327d9d] (thomaston) + * Fix stub files [9aa5f387, 74cf49f5, 8b1eafe8, e392dd88, b5ea5fd7, 71758b09, 2a6dee5d] (Nicolas Grekas), (Michael Grunder) + * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Krüss) + * Allow to pass null as iterator [14d121bb] (Pavlo Yatsukhnenko) + * Add NOMKSTREAM option to XADD command. [f9436e25] (Pavlo Yatsukhnenko) + * Don't allow reconnect on read response [5a269ab6] (Pavlo Yatsukhnenko) + * Reset multi/pipline transaction on pconnect close [0879770a] (Pavlo Yatsukhnenko) + * Use read_mbulk_header helper where possible [ca8b4c93] (Pavlo Yatsukhnenko) + * Allow to pass null as auth argument [41517753] (Pavlo Yatsukhnenko) + * Refactor redis_parse_client_list_response [68136a29, aaa4c91a, 1fb2935b, cf2c052c] (Pavlo Yatsukhnenko) + * Refactor subscribe/unsubscribe [3c9e159c] (Pavlo Yatsukhnenko) + * Change PHPREDIS_CTX_PTR type [de3635da] (Pavlo Yatsukhnenko) + * Refactor redis_parse_info_response [982bd13b] (Pavlo Yatsukhnenko) + * Allow IPv6 address within square brackets [c28ad7bb] (Pavlo Yatsukhnenko) + * Allow multiple field-value pairs for hmset command. [e858e8e3] (Pavlo Yatsukhnenko) + * Refactor MINIT and use @generate-class-entries in stub files [3675f442] (Remi Collet) + * Use spl_ce_RuntimeException [3cd5ac1e, a7e5ea64] (Remi Collet) + * Regenerate arginfo using 8.2.0 [a38e08da] (Remi Collet) + * Refactor client command [a8d10291] (Pavlo Yatsukhnenko) + * Pull COUNT/ANY parsing into a helper function [d67b2020] (Michael Grunder) + * Return false or NULL on empty lpos response [39a01ac7] (Michael Grunder) + * BLPOP with a float timeout [a98605f2, dc9af529] (Michael Grunder) + * Make sure we set an error for key based scans [98fda1b8] (Michael Grunder) + * Add back a default switch case for setoption handler [87464932] (Michael Grunder) + * Update stubs so the tests pass in strict mode [bebd398c] (Michael Grunder) + * Move where we generate our salt [d2044c9f] (Michael Grunder) + * Refactor XINFO handler [3b0d8b77] (Michael Grunder) + * Refactor and fix XPENDING handler [457953f4] (Michael Grunder) + * Refactor FLUSHDB and update docs. [54a084e5] (Michael Grunder) + * Add missing directed node command to docs and refactor stubs. [5ac92d25] (Michael Grunder) + * Refactor BITPOS and implement BIT/BYTE option. [4d8afd38] (Michael Grunder) + * INFO with multiple sections [44d03ca0] (Michael Grunder) + * Refactor SLOWLOG command [d87f1428] (Michael Grunder) + * Refactor SORT and add SORT_RO command [8c7c5a3a] (Michael Grunder) + * Use ZEND_STRL in redis_commands.c [78de25a3] (Pavlo Yatsukhnenko) + * Refactor PubSub command [2a0d1c1e] (Pavlo Yatsukhnenko) + * Refactor SLAVEOF handler [f2cef8be] (Michael Grunder) + * Refactor ACL command [504810a5] (Pavlo Yatsukhnenko) + * Use fast_zpp API [376d4d27] (Pavlo Yatsukhnenko) + * Fix XAUTOCLAIM response handler [0b7bd83f] (Michael Grunder) + * Refactor command command [ff863f3f] (Pavlo Yatsukhnenko) + * Refactor rawCommand and WAIT [79c9d224] (Michael Grunder) + * Refactor SELECT command [86f15cca] (Michael Grunder) + * Refactor SRANDMEMBER command. [f62363c2] (Michael Grunder) + * Refactor OBJECT command. [acb5db76] (Michael Grunder) + * Refactor gen_varkey_cmd [3efa59cb] (Michael Grunder) + * Refactor MGET command. [8cb6dd17] (Michael Grunder) + * Refactor INFO and SCRIPT commands. [3574ef08] (Michael Grunder) + * Refactor MSET and MSETNX commands. [6d104481] (Michael Grunder) + * Refactor HMSET command. [90eb0470] (Michael Grunder) + * Refactor PFCOUNT command. [19fd7e0c] (Michael Grunder) + * Refactor SMOVE command. [204a02c5] (Michael Grunder) + * Rework ZRANGE argument handling. [aa0938a4] (Michael Grunder) + * Refactor a couple more command methods. [5b560ccf, c8224b93, 40e1b1bf, ccd419a4] (Michael Grunder) + * Refactor HMGET command [bb66a547] (Michael Grunder) + * Refactor CLIENT command [77c4f7a3] (Pavlo Yatsukhnenko) + * Refactor redis_long_response [f14a80db] (Pavlo Yatsukhnenko) + * Synchronize Redis and RedisSentinel constructors [ebb2386e] (Pavlo Yatsukhnenko) + * Use redis_sock_connect on connect [f6c8b9c6] (Pavlo Yatsukhnenko) + * Auto-select db in redis_sock_server_open [6930a81c] (Pavlo Yatsukhnenko) + * Use on-stack allocated valiables [7a055cad] (Pavlo Yatsukhnenko) + * Add XAUTOCLAIM command [01f3342c] (Pavlo Yatsukhnenko) + * Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH [750b6cf3] (Pavlo Yatsukhnenko) + * Add reset command [947a2d38] (Pavlo Yatsukhnenko) + * Add hRandField command [fe397371] (Pavlo Yatsukhnenko) + * Add PXAT/EXAT arguments to SET command. [0a160685] (Pavlo Yatsukhnenko) + * Add GETEX, GETDEL commands. [11861d95] (Pavlo Yatsukhnenko) + * Add FAILOVER command. [4b767be7] (Pavlo Yatsukhnenko) + * Backoff settings in constructor [e6b3fe54] (Pavlo Yatsukhnenko) + * Add the COUNT argument to LPOP and RPOP [df97cc35] (Pavlo Yatsukhnenko) + * Unsubscribe from all channels [0f1ca0cc] (Pavlo Yatsukhnenko) + * Add lPos command. [687a5c78] (Pavlo Yatsukhnenko) + * Add the ANY argument to GEOSEARCH and GEORADIUS [bf6f31e3] (Pavlo Yatsukhnenko) + * Add 'BIT'/'BYTE' modifier to BITCOUNT + tests [a3d2f131] (Michael Grunder) + * Add missing configureoption entries in package.xml [59053f10] (Michele Locati) + * Implement CONFIG RESETSTAT [239678a0] (Michael Grunder) + * SINTERCARD and ZINTERCARD commands [64300508] (Michael Grunder) + * LCS command [c0e839f6] (Michael Grunder) + * EXPIRETIME and PEXPIRETIME [f5b2a09b] (Michael Grunder) + * [B]LMPOP and [B]ZMPOP commands [6ea978eb] (Michael Grunder) + * Implement new RESTORE options [9a3fe401] (Michael Grunder) + * Add new Redis 6.2.0 XTRIM options [6b34d17f] (Michael Grunder) + * Implement AUTH/AUTH2 arguments for MIGRATE [114d79d1] (Michael Grunder) + * Implement CONFIG REWRITE [525958ea] (Michael Grunder) + * Implement Redis 7.0.0 [P]EXPIRE[AT] [options 872ae107] (Michael Grunder) + * Variadic CONFIG GET/SET [36ef4bd8, a176f586] (Michael Grunder) + * EVAL_RO and EVALSHA_RO [f3a40830] (Michael Grunder) + * Implement ZRANGESTORE and add ZRANGE options [71bcbcb9] (Michael Grunder) + * XGROUP DELCONSUMER and ENTRIESREAD [1343f500] (Michael Grunder) + * Expose the transferred number of bytes [e0a88b7b, 90828019, 7a4cee2d] (Pavlo Yatsukhnenko), (Michael Grunder) + * TOUCH command [dc1f2398] (Michael Grunder) + * Redis Sentinel TLS support [f2bb2cdb] (Pavlo Yatsukhnenko) + * Add the CH, NX, XX arguments to GEOADD [2bb64038, e8f5b517] (Pavlo Yatsukhnenko) + * Implement SMISMEMBER for RedisCluster [abfac47b] (Michael Grunder) + * Implement ssubscribe/sunsubscribe [7644736e] (Pavlo Yatsukhnenko) + * Implement BLMOVE and add LMOVE/BLMOVE to cluster. [121e9d9c] (Michael Grunder) + * Implement LPOS for RedisCluster [7121aaae] (Michael Grunder) + * Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. [fa5d1af9] (Michael Grunder) + * Implement HRANDFIELD for RedisCluster [e222b85e] (Michael Grunder) + * Implement COPY for RedisCluster [40a2c254] (Michael Grunder) + * Implement new ZSET commands for cluster [27900f39] (Michael Grunder) + * Add cluster support for strict sessions and lazy write [b6cf6361] (Michael Grunder) + * Add function command [90a0e9cc] (Pavlo Yatsukhnenko) + * Add FCALL/FCALL_RO commands [7c46ad2c] (Pavlo Yatsukhnenko) + * Remove unused macroses [831d6118] (Pavlo Yatsukhnenko) + + + + + stablestable + 5.3.75.3.7 + 2022-02-15 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Luis Zarate - https://github.com/jlzaratec + + phpredis 5.3.7 + + - There were no changes between 5.3.7 and 5.3.7RC2. + + --- + + phpredis 5.3.7RC2 + + - There were no changes between 5.3.7RC2 and 5.3.7RC1. + + --- + + phpredis 5.3.7RC1 + + - Fix RedisArray::[hsz]scan and tests [08a9d5db, 0264de18] (Pavlo Yatsukhnenko, Michael Grunder) + - Fix RedisArray::scan [8689ab1c] (Pavlo Yatsukhnenko) + - Fix LZF decompression logic [0719c1ec] (Michael Grunder) + + + + + stablestable + 5.3.65.3.6 + 2022-01-17 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Luis Zarate - https://github.com/jlzaratec + + --- + + phpredis 5.3.6 + + - Fix a segfault in RedisArray::del [d2f2a7d9] (Pavlo Yatsukhnenko) + + + + + stablestable + 5.3.55.3.5 + 2021-12-18 + + phpredis 5.3.5 + + This release adds support for exponential backoff w/jitter, experimental + support for detecting a dirty connection, as well as many other fixes + and improvements. + + You can find a detailed list of changes in Changelog.md and package.xml + or by inspecting the git commit logs. + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Luis Zarate - https://github.com/jlzaratec + + --- + + phpredis 5.3.5 + + * Fix typo in cluster_scan_resp [44affad2] (Michael Grunder) + + --- + + phpredis 5.3.5RC1 + + * Fixed segfault in redis_setoption_handler [692e4e84] (Pavlo Yatsukhnenko) + * Fix masters array in the event of a cluster failover [bce692962] (Bar Shaul) + * Fix 32 bit type error [672dec87f] (Remi Collet) + * Fix radix character in certain locales [89a871e24] (Pavlo Yatsukhnenko) + * ZSTD Validation fix [6a77ef5cd] (Michael Grunder) + * Remove superfluous typecast [b2871471f] (Remi Collet) + + * Updated documentation [f84168657, d017788e7, 20ac84710, 0adf05260, + aee29bf73, 09a095e72, 12ffbf33a, ff331af98, a6bdb8731, 305c15840, + 1aa10e93a, d78b0c79d, c6d37c27c, a6303f5b9, d144bd2c7, a6fb815ef, 9ef862bc6] + (neodisco, Clement Tessier, T. Todua, dengliming, Maxime Cornet, + Emanuele Filannino Michael Grunder) + + * Travis CI Fixes + [a43f4586e, 4fde8178f, 7bd5415ac, fdb8c4bb7, d4f407470] + (Pavlo Yatsukhnenko) + + * Minor fixes/cleanup + [2e190adc1, 99975b592, 9d0879fa5, 22b06457b] + (Pavlo Yatsukhnenko) + + * Fix RedisArray constructor bug + [85dc883ba](https://github.com/phpredis/phpredis/commit/85dc883ba) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + + * Moved to GitHub Actions + [4d2afa786, 502d09fd5] (Pavlo Yatsukhnenko) + + * Use more appropriate array iteration macro + [6008900c2] (Pavlo Yatsukhnenko) + + * Clean up session tests + [ab25ae7f3] (Michael Grunder) + + * RedisArray refactors [1250f0001, 017b2ea7f, 37ed3f079] + (Pavlo Yatsukhnenko) + + * Use zend_parse_parameters_none helper + [a26b14dbe] (Remi Collet) + + * Support for various exponential backoff strategies + [#1986, #1993, 732eb8dcb, 05129c3a3, 5bba6a7fc], + (Nathaniel Braun) + + * Added experimental support for detecting a dirty connection + [d68579562] (Michael Grunder) + + * Created distinct compression utility methods (pack/unpack) + [#1939, da2790aec] (Michael Grunder) + + * SMISMEMBER Command + [#1894, ae2382472, ed283e1ab] (Pavlo Yatsukhnenko) + + stablestable 5.3.45.3.4 diff --git a/php_redis.h b/php_redis.h index c79367b4a6..20ea76ee64 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.0-dev" +#define PHP_REDIS_VERSION "6.0.0RC1" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ From f0888cc685cc7e45a554017697172f66c734183e Mon Sep 17 00:00:00 2001 From: Michele Locati Date: Wed, 2 Aug 2023 11:32:54 +0200 Subject: [PATCH 1784/1986] Use system liblz4 by default when installing with pecl --- package.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/package.xml b/package.xml index 7df8827481..96c89efd1e 100644 --- a/package.xml +++ b/package.xml @@ -271,6 +271,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + From 31cfb646e1d1e8ade04b84cf237793f602b24133 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 2 Aug 2023 15:42:47 +0200 Subject: [PATCH 1785/1986] raise minimal supported version to 7.2 --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index cdc815a526..7a952290f8 100644 --- a/package.xml +++ b/package.xml @@ -187,7 +187,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> - 7.0.0 + 7.2.0 1.4.0b1 From 715012b2c19bb3e2acd941f8a2cacec7dada13d2 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 2 Aug 2023 16:07:21 +0200 Subject: [PATCH 1786/1986] fix C99 usages --- library.c | 3 ++- redis_commands.c | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/library.c b/library.c index dabb2188be..6405eee1f0 100644 --- a/library.c +++ b/library.c @@ -1845,7 +1845,8 @@ redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, array_init_size(&zele, elements); if (ctx == PHPREDIS_CTX_PTR) { - for (int i = 0; i < elements; i++) { + int i; + for (i = 0; i < elements; i++) { if (read_mbulk_header(redis_sock, &subele) < 0 || subele != 2) { zval_dtor(&zele); goto fail; diff --git a/redis_commands.c b/redis_commands.c index 94129331c9..53d1d0da42 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1877,6 +1877,7 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; zval *argv = NULL; int argc = 0; + uint32_t i; ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) @@ -1884,7 +1885,7 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - for (uint32_t i = 0; i < argc; i++) { + for (i = 0; i < argc; i++) { redis_cmd_append_sstr_zval(&cmdstr, &argv[i], NULL); } @@ -1986,7 +1987,8 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } ZEND_HASH_FOREACH_END(); } else { - for(uint32_t i = 0; i < argc - !!has_timeout; i++) { + uint32_t i; + for(i = 0; i < argc - !!has_timeout; i++) { redis_cmd_append_sstr_key_zval(&cmdstr, &argv[i], redis_sock, slot); if (slot) { if (kslot != -1 && *slot != kslot) @@ -2605,7 +2607,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; HashTable *fields = NULL; zend_string *key = NULL; - zend_ulong valid = 0; + zend_ulong valid = 0, i; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) @@ -2635,7 +2637,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + valid, "HMGET"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - for (zend_ulong i = 0; i < valid; i++) { + for (i = 0; i < valid; i++) { redis_cmd_append_sstr_zval(&cmdstr, &zctx[i], NULL); } From cc40af30db2e5c3be804bceae65c38d5e0a50ddb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 Jul 2023 17:56:02 +0200 Subject: [PATCH 1787/1986] Fix arginfo for arguments that default to null --- redis.stub.php | 70 ++++++++++++++++++++-------------------- redis_arginfo.h | 58 ++++++++++++++++----------------- redis_array.stub.php | 6 ++-- redis_array_arginfo.h | 6 ++-- redis_cluster.stub.php | 48 +++++++++++++-------------- redis_cluster_arginfo.h | 26 +++++++-------- redis_sentinel.stub.php | 2 +- redis_sentinel_arginfo.h | 2 +- 8 files changed, 109 insertions(+), 109 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 7739d66cc6..05a7ebd047 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -478,7 +478,7 @@ class Redis { * * @return Redis */ - public function __construct(array $options = null); + public function __construct(?array $options = null); public function __destruct(); @@ -816,7 +816,7 @@ public function client(string $opt, mixed ...$args): mixed; public function close(): bool; - public function command(string $opt = null, mixed ...$args): mixed; + public function command(?string $opt = null, mixed ...$args): mixed; /** * Execute the Redis CONFIG command in a variety of ways. @@ -835,10 +835,10 @@ public function command(string $opt = null, mixed ...$args): mixed; * $redis->config('SET', 'timeout', 30); * $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); */ - public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed; + public function config(string $operation, array|string|null $key_or_settings = null, ?string $value = null): mixed; - public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, - int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool; + public function connect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, + int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Make a copy of a key. @@ -873,7 +873,7 @@ public function connect(string $host, int $port = 6379, float $timeout = 0, stri * var_dump($redis->copy('source1', 'exists')); * var_dump($redis->copy('source1', 'exists', ['REPLACE' => true])); */ - public function copy(string $src, string $dst, array $options = null): Redis|bool; + public function copy(string $src, string $dst, ?array $options = null): Redis|bool; /** * Return the number of keys in the currently selected Redis database. @@ -1105,7 +1105,7 @@ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; * @see https://redis.io/commands/expire * */ - public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|bool; + public function expire(string $key, int $timeout, ?string $mode = null): Redis|bool; /* * Set a key's expiration to a specific Unix timestamp in seconds. @@ -1131,7 +1131,7 @@ public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|b * @see https://redis.io/commands/expire * @see Redis::expire() */ - public function expireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool; + public function expireAt(string $key, int $timestamp, ?string $mode = null): Redis|bool; public function failover(?array $to = null, bool $abort = false, int $timeout = 0): Redis|bool; @@ -1587,7 +1587,7 @@ public function getRange(string $key, int $start, int $end): Redis|string|false; * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc'); * echo $redis->lcs('seq1', 'seq2') . "\n"; */ - public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false; + public function lcs(string $key1, string $key2, ?array $options = null): Redis|string|array|int|false; /** * Get the currently set read timeout on the connection. @@ -1783,7 +1783,7 @@ public function hMset(string $key, array $fieldvals): Redis|bool; * @example $redis->hrandfield('settings'); * @example $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]); */ - public function hRandField(string $key, array $options = null): Redis|string|array; + public function hRandField(string $key, ?array $options = null): Redis|string|array; public function hSet(string $key, string $member, mixed $value): Redis|int|false; @@ -1861,7 +1861,7 @@ public function hVals(string $key): Redis|array|false; * * $redis->hmset('big-hash', $fields); * - * $it = NULL; + * $it = null; * * do { * // Scan the hash but limit it to fields that match '*:1?3' @@ -2041,7 +2041,7 @@ public function lPop(string $key, int $count = 0): Redis|bool|string|array; * * @return Redis|null|bool|int|array Returns one or more of the matching indexes, or null/false if none were found. */ - public function lPos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array; + public function lPos(string $key, mixed $value, ?array $options = null): Redis|null|bool|int|array; /** * Prepend one or more elements to a list. @@ -2178,7 +2178,7 @@ public function mget(array $keys): Redis|array; public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, - #[\SensitiveParameter] mixed $credentials = NULL): Redis|bool; + #[\SensitiveParameter] mixed $credentials = null): Redis|bool; /** * Move a key to a different database on the same redis instance. @@ -2237,9 +2237,9 @@ public function object(string $subcommand, string $key): Redis|int|string|false; * @deprecated * @alias Redis::connect */ - public function open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + public function open(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; - public function pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + public function pconnect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Remove the expiration from a key. @@ -2262,7 +2262,7 @@ public function persist(string $key): Redis|bool; * * @return Redis|bool True if an expiry was set on the key, and false otherwise. */ - public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool; + public function pexpire(string $key, int $timeout, ?string $mode = null): bool; /** * Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to @@ -2276,7 +2276,7 @@ public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool; * * @return Redis|bool True if an expiration was set on the key, false otherwise. */ - public function pexpireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool; + public function pexpireAt(string $key, int $timestamp, ?string $mode = null): Redis|bool; /** * Add one or more elements to a Redis HyperLogLog key @@ -2327,7 +2327,7 @@ public function pfmerge(string $dst, array $srckeys): Redis|bool; * @example $redis->ping(); * @example $redis->ping('beep boop'); */ - public function ping(string $message = NULL): Redis|string|bool; + public function ping(?string $message = null): Redis|string|bool; /** * Enter into pipeline mode. @@ -2353,7 +2353,7 @@ public function pipeline(): bool|Redis; * @deprecated * @alias Redis::pconnect */ - public function popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + public function popen(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Set a key with an expiration time in milliseconds @@ -2542,7 +2542,7 @@ public function reset(): Redis|bool; * * $redis->restore('captains-backup', 0, $serialized); */ - public function restore(string $key, int $ttl, string $value, ?array $options = NULL): Redis|bool; + public function restore(string $key, int $ttl, string $value, ?array $options = null): Redis|bool; /** * Query whether the connected instance is a primary or replica @@ -2891,7 +2891,7 @@ public function save(): Redis|bool; * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); * - * $it = NULL; + * $it = null; * * do { * $keys = $redis->scan($it, '*zorg*'); @@ -2902,7 +2902,7 @@ public function save(): Redis|bool; * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); * - * $it = NULL; + * $it = null; * * // When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an * // empty array of keys when the iterator is nonzero. @@ -2912,7 +2912,7 @@ public function save(): Redis|bool; * } * } */ - public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, string $type = NULL): array|false; + public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, ?string $type = null): array|false; /** * Retrieve the number of members in a Redis set. @@ -2985,7 +2985,7 @@ public function select(int $db): Redis|bool; * @example $redis->set('key', 'value'); * @example $redis->set('key', 'expires_in_60_seconds', 60); */ - public function set(string $key, mixed $value, mixed $options = NULL): Redis|string|bool; + public function set(string $key, mixed $value, mixed $options = null): Redis|string|bool; /** * Set a specific bit in a Redis string to zero or one @@ -3106,7 +3106,7 @@ public function sismember(string $key, mixed $value): Redis|bool; * @see https://redis.io/commands/replicaof * @see Redis::replicaof() */ - public function slaveof(string $host = NULL, int $port = 6379): Redis|bool; + public function slaveof(?string $host = null, int $port = 6379): Redis|bool; /** * Used to turn a Redis instance into a replica of another, or to remove @@ -3132,7 +3132,7 @@ public function slaveof(string $host = NULL, int $port = 6379): Redis|bool; * // attempting to promote the instance to a primary. * $redis->replicaof(); */ - public function replicaof(string $host = NULL, int $port = 6379): Redis|bool; + public function replicaof(?string $host = null, int $port = 6379): Redis|bool; /** * Update one or more keys last modified metadata. @@ -3274,7 +3274,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); * * $scanned = 0; - * $it = NULL; + * $it = null; * * // Without Redis::SCAN_RETRY we may receive empty results and * // a nonzero iterator. @@ -3291,7 +3291,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); * * $scanned = 0; - * $it = NULL; + * $it = null; * * // With Redis::SCAN_RETRY PhpRedis will never return an empty array * // when the cursor is non-zero @@ -3795,7 +3795,7 @@ public function xdel(string $key, array $ids): Redis|int|false; * * @return mixed This command return various results depending on the operation performed. */ - public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, + public function xgroup(string $operation, ?string $key = null, ?string $group = null, ?string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2): mixed; /** @@ -4105,7 +4105,7 @@ public function zMscore(string $key, mixed $member, mixed ...$other_members): Re * $redis->zPopMax('zs'); * $redis->zPopMax('zs', 2);. */ - public function zPopMax(string $key, int $count = null): Redis|array|false; + public function zPopMax(string $key, ?int $count = null): Redis|array|false; /** * Pop one or more of the lowest scoring elements from a sorted set. @@ -4123,7 +4123,7 @@ public function zPopMax(string $key, int $count = null): Redis|array|false; * $redis->zPopMin('zs'); * $redis->zPopMin('zs', 2); */ - public function zPopMin(string $key, int $count = null): Redis|array|false; + public function zPopMin(string $key, ?int $count = null): Redis|array|false; /** * Retrieve a range of elements of a sorted set between a start and end point. @@ -4222,7 +4222,7 @@ public function zRangeByScore(string $key, string $start, string $end, array $op * See {@link Redis::zRange} for a full description of the possible options. */ public function zrangestore(string $dstkey, string $srckey, string $start, string $end, - array|bool|null $options = NULL): Redis|int|false; + array|bool|null $options = null): Redis|int|false; /** * Retrieve one or more random members from a Redis sorted set. @@ -4240,7 +4240,7 @@ public function zrangestore(string $dstkey, string $srckey, string $start, strin * * @example $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]); */ - public function zRandMember(string $key, array $options = null): Redis|string|array; + public function zRandMember(string $key, ?array $options = null): Redis|string|array; /** * Get the rank of a member of a sorted set, by score. @@ -4448,7 +4448,7 @@ public function zScore(string $key, mixed $member): Redis|float|false; * * $redis->zDiff(['primes', 'evens', 'mod3']); */ - public function zdiff(array $keys, array $options = null): Redis|array|false; + public function zdiff(array $keys, ?array $options = null): Redis|array|false; /** * Store the difference of one or more sorted sets in a destination sorted set. @@ -4619,7 +4619,7 @@ public function zunion(array $keys, ?array $weights = null, ?array $options = nu * * $redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']); */ - public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): Redis|int|false; + public function zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false; } class RedisException extends RuntimeException {} diff --git a/redis_arginfo.h b/redis_arginfo.h index 8429b51ff4..41f6aab06f 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -2,7 +2,7 @@ * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0) @@ -121,30 +121,30 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_close arginfo_class_Redis_clearLastError ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 0, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 1, "null") ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_copy, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dbSize, 0, 0, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -210,13 +210,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expire, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expireAt, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_failover, 0, 0, Redis, MAY_BE_BOOL) @@ -360,7 +360,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getReadTimeout, 0, 0, IS_DOUBLE, 0) @@ -428,7 +428,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSet, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -508,7 +508,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPush, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -565,7 +565,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_migrate, 0, 5, R ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, copy, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, replace, _IS_BOOL, 0, "false") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_move, 0, 2, Redis, MAY_BE_BOOL) @@ -592,10 +592,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_open, 0, 1, _IS_BOOL ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_pconnect arginfo_class_Redis_open @@ -607,7 +607,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpire, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt @@ -627,7 +627,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfmerge, 0, 2, R ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ping, 0, 0, Redis, MAY_BE_STRING|MAY_BE_BOOL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL) @@ -691,7 +691,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_restore, 0, 3, R ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_role arginfo_class_Redis_getAuth @@ -762,7 +762,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_A ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_scard arginfo_class_Redis_expiretime @@ -776,7 +776,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_set, 0, 2, Redis, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setBit, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -810,7 +810,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_sismember arginfo_class_Redis_setnx ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_slaveof, 0, 0, Redis, MAY_BE_BOOL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_END_ARG_INFO() @@ -939,9 +939,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() @@ -1034,7 +1034,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zPopMax, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax @@ -1066,7 +1066,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zrangestore, 0, ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) - ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "NULL") + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField @@ -1123,7 +1123,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiff, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiffstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -1158,8 +1158,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zunionstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null") ZEND_END_ARG_INFO() diff --git a/redis_array.stub.php b/redis_array.stub.php index 8fca8ab95a..c84d636a9c 100644 --- a/redis_array.stub.php +++ b/redis_array.stub.php @@ -10,7 +10,7 @@ class RedisArray { public function __call(string $function_name, array $arguments): mixed; - public function __construct(string|array $name_or_hosts, array $options = NULL); + public function __construct(string|array $name_or_hosts, ?array $options = null); public function _continuum(): bool|array; @@ -22,7 +22,7 @@ public function _hosts(): bool|array; public function _instance(string $host): bool|null|Redis; - public function _rehash(callable $fn = NULL): bool|null; + public function _rehash(?callable $fn = null): bool|null; public function _target(string $key): bool|string|null; @@ -50,7 +50,7 @@ public function mget(array $keys): bool|array; public function mset(array $pairs): bool; - public function multi(string $host, int $mode = NULL): bool|RedisArray; + public function multi(string $host, ?int $mode = null): bool|RedisArray; public function ping(): bool|array; diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h index bfacd08609..ea4d0721ef 100644 --- a/redis_array_arginfo.h +++ b/redis_array_arginfo.h @@ -8,7 +8,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___construct, 0, 0, 1) ZEND_ARG_TYPE_MASK(0, name_or_hosts, MAY_BE_STRING|MAY_BE_ARRAY, NULL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray__continuum, 0, 0, MAY_BE_BOOL|MAY_BE_ARRAY) @@ -26,7 +26,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisArray__instance, ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray__rehash, 0, 0, _IS_BOOL, 1) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fn, IS_CALLABLE, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fn, IS_CALLABLE, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray__target, 0, 1, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_NULL) @@ -77,7 +77,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisArray_multi, 0, 1, RedisArray, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_ping arginfo_class_RedisArray__continuum diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index c2ab9f4809..f5508afdfe 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -47,7 +47,7 @@ class RedisCluster { */ public const FAILOVER_DISTRIBUTE_SLAVES = UNKNOWN; - public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = NULL, array $context = NULL); + public function __construct(string|null $name, ?array $seeds = null, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = null, ?array $context = null); /** * @see Redis::_compress() @@ -200,7 +200,7 @@ public function clearlasterror(): bool; /** * @see Redis::client */ - public function client(string|array $key_or_address, string $subcommand, ?string $arg = NULL): array|string|bool; + public function client(string|array $key_or_address, string $subcommand, ?string $arg = null): array|string|bool; /** * @see Redis::close @@ -230,7 +230,7 @@ public function dbsize(string|array $key_or_address): RedisCluster|int; /** * @see https://redis.io/commands/copy */ - public function copy(string $src, string $dst, array $options = null): RedisCluster|bool; + public function copy(string $src, string $dst, ?array $options = null): RedisCluster|bool; /** * @see Redis::decr() @@ -305,12 +305,12 @@ public function touch(mixed $key, mixed ...$other_keys): RedisCluster|int|bool; /** * @see Redis::expire */ - public function expire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool; + public function expire(string $key, int $timeout, ?string $mode = null): RedisCluster|bool; /** * @see Redis::expireat */ - public function expireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool; + public function expireat(string $key, int $timestamp, ?string $mode = null): RedisCluster|bool; /** * @see Redis::expiretime() @@ -415,7 +415,7 @@ public function getrange(string $key, int $start, int $end): RedisCluster|string /** * @see Redis::lcs */ - public function lcs(string $key1, string $key2, ?array $options = NULL): RedisCluster|string|array|int|false; + public function lcs(string $key1, string $key2, ?array $options = null): RedisCluster|string|array|int|false; /** * @see Redis::getset @@ -490,7 +490,7 @@ public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int /** * @see https://redis.io/commands/hrandfield */ - public function hrandfield(string $key, array $options = null): RedisCluster|string|array; + public function hrandfield(string $key, ?array $options = null): RedisCluster|string|array; /** * @see Redis::hset @@ -583,7 +583,7 @@ public function lpop(string $key, int $count = 0): RedisCluster|bool|string|arra /** * @see Redis::lpos */ - public function lpos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array; + public function lpos(string $key, mixed $value, ?array $options = null): Redis|null|bool|int|array; /** * @see Redis::lpush @@ -648,12 +648,12 @@ public function persist(string $key): RedisCluster|bool; /** * @see Redis::pexpire */ - public function pexpire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool; + public function pexpire(string $key, int $timeout, ?string $mode = null): RedisCluster|bool; /** * @see Redis::pexpireat */ - public function pexpireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool; + public function pexpireat(string $key, int $timestamp, ?string $mode = null): RedisCluster|bool; /** @@ -684,7 +684,7 @@ public function pfmerge(string $key, array $keys): RedisCluster|bool; * @return mixed This method always returns `true` if no message was sent, and the message itself * if one was. */ - public function ping(string|array $key_or_address, ?string $message = NULL): mixed; + public function ping(string|array $key_or_address, ?string $message = null): mixed; /** * @see Redis::psetex @@ -739,7 +739,7 @@ public function renamenx(string $key, string $newkey): RedisCluster|bool; /** * @see Redis::restore */ - public function restore(string $key, int $timeout, string $value, ?array $options = NULL): RedisCluster|bool; + public function restore(string $key, int $timeout, string $value, ?array $options = null): RedisCluster|bool; /** * @see Redis::role @@ -809,7 +809,7 @@ public function sdiffstore(string $dst, string $key, string ...$other_keys): Red /** * @see https://redis.io/commands/set */ - public function set(string $key, mixed $value, mixed $options = NULL): RedisCluster|string|bool; + public function set(string $key, mixed $value, mixed $options = null): RedisCluster|string|bool; /** * @see Redis::setbit @@ -879,12 +879,12 @@ public function smove(string $src, string $dst, string $member): RedisCluster|bo /** * @see Redis::sort() */ - public function sort(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string; + public function sort(string $key, ?array $options = null): RedisCluster|array|bool|int|string; /** * @see Redis::sort_ro() */ - public function sort_ro(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string; + public function sort_ro(string $key, ?array $options = null): RedisCluster|array|bool|int|string; /** * @see Redis::spop @@ -984,7 +984,7 @@ public function xdel(string $key, array $ids): RedisCluster|int|false; /** * @see Redis::xgroup */ - public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, + public function xgroup(string $operation, ?string $key = null, ?string $group = null, ?string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2): mixed; /** @@ -1070,12 +1070,12 @@ public function zlexcount(string $key, string $min, string $max): RedisCluster|i /** * @see Redis::zpopmax */ - public function zpopmax(string $key, int $value = null): RedisCluster|bool|array; + public function zpopmax(string $key, ?int $value = null): RedisCluster|bool|array; /** * @see Redis::zpopmin */ - public function zpopmin(string $key, int $value = null): RedisCluster|bool|array; + public function zpopmin(string $key, ?int $value = null): RedisCluster|bool|array; /** * @see Redis::zrange @@ -1091,7 +1091,7 @@ public function zrangestore(string $dstkey, string $srckey, int $start, int $end /** * @see https://redis.io/commands/zRandMember */ - public function zrandmember(string $key, array $options = null): RedisCluster|string|array; + public function zrandmember(string $key, ?array $options = null): RedisCluster|string|array; /** * @see Redis::zrangebylex @@ -1131,17 +1131,17 @@ public function zremrangebyscore(string $key, string $min, string $max): RedisCl /** * @see Redis::zrevrange */ - public function zrevrange(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrange(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrangebylex */ - public function zrevrangebylex(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrangebylex(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrangebyscore */ - public function zrevrangebyscore(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrangebyscore(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** * @see Redis::zrevrank @@ -1166,7 +1166,7 @@ public function zmscore(string $key, mixed $member, mixed ...$other_members): Re /** * @see Redis::zunionstore */ - public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): RedisCluster|int|false; + public function zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): RedisCluster|int|false; /** * @see https://redis.io/commands/zinter @@ -1186,7 +1186,7 @@ public function zunion(array $keys, ?array $weights = null, ?array $options = nu /** * @see https://redis.io/commands/zdiff */ - public function zdiff(array $keys, array $options = null): RedisCluster|array|false; + public function zdiff(array $keys, ?array $options = null): RedisCluster|array|false; } class RedisClusterException extends RuntimeException {} diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 2cd817df79..a853337d34 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -3,12 +3,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seeds, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seeds, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_MASK(0, timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0") ZEND_ARG_TYPE_MASK(0, read_timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, _IS_BOOL, 0, "false") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__compress, 0, 1, IS_STRING, 0) @@ -168,7 +168,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_copy, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decr, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -406,7 +406,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hrandfield, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hset, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -479,7 +479,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) @@ -680,7 +680,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_set, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setbit, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -825,9 +825,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() @@ -928,7 +928,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zpopmax, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zpopmin arginfo_class_RedisCluster_zpopmax @@ -986,7 +986,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrevrange ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zrevrangebylex arginfo_class_RedisCluster_zrevrange @@ -1035,7 +1035,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zdiff, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php index 486ac438ac..32551e1dd5 100644 --- a/redis_sentinel.stub.php +++ b/redis_sentinel.stub.php @@ -8,7 +8,7 @@ class RedisSentinel { - public function __construct(array $options = null); + public function __construct(?array $options = null); /** @return bool|RedisSentinel */ public function ckquorum(string $master); diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h index e917b7d5ba..7ef1ae42be 100644 --- a/redis_sentinel_arginfo.h +++ b/redis_sentinel_arginfo.h @@ -2,7 +2,7 @@ * Stub hash: f1f746cc848b1debcdf88eae015732720ba206c8 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1) From 40a46b53edf0163239c6d937cb4d9c0908cbd779 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 2 Aug 2023 16:07:21 +0200 Subject: [PATCH 1788/1986] fix C99 usages --- library.c | 3 ++- redis_commands.c | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/library.c b/library.c index dabb2188be..6405eee1f0 100644 --- a/library.c +++ b/library.c @@ -1845,7 +1845,8 @@ redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, array_init_size(&zele, elements); if (ctx == PHPREDIS_CTX_PTR) { - for (int i = 0; i < elements; i++) { + int i; + for (i = 0; i < elements; i++) { if (read_mbulk_header(redis_sock, &subele) < 0 || subele != 2) { zval_dtor(&zele); goto fail; diff --git a/redis_commands.c b/redis_commands.c index 94129331c9..53d1d0da42 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1877,6 +1877,7 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; zval *argv = NULL; int argc = 0; + uint32_t i; ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) @@ -1884,7 +1885,7 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - for (uint32_t i = 0; i < argc; i++) { + for (i = 0; i < argc; i++) { redis_cmd_append_sstr_zval(&cmdstr, &argv[i], NULL); } @@ -1986,7 +1987,8 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } ZEND_HASH_FOREACH_END(); } else { - for(uint32_t i = 0; i < argc - !!has_timeout; i++) { + uint32_t i; + for(i = 0; i < argc - !!has_timeout; i++) { redis_cmd_append_sstr_key_zval(&cmdstr, &argv[i], redis_sock, slot); if (slot) { if (kslot != -1 && *slot != kslot) @@ -2605,7 +2607,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; HashTable *fields = NULL; zend_string *key = NULL; - zend_ulong valid = 0; + zend_ulong valid = 0, i; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) @@ -2635,7 +2637,7 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + valid, "HMGET"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - for (zend_ulong i = 0; i < valid; i++) { + for (i = 0; i < valid; i++) { redis_cmd_append_sstr_zval(&cmdstr, &zctx[i], NULL); } From c94c774a2f2e30c956ddb82bcbf983f2dd684265 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 2 Aug 2023 15:42:47 +0200 Subject: [PATCH 1789/1986] raise minimal supported version to 7.2 --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index 96c89efd1e..10a25379e1 100644 --- a/package.xml +++ b/package.xml @@ -257,7 +257,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> - 7.0.0 + 7.2.0 1.4.0b1 From 3847181afc66c9b90dcdffa638e339a530f74b75 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 20 Aug 2023 11:09:53 +0300 Subject: [PATCH 1790/1986] Update CHANGELOG.md --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1347098003..afa4239a2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,33 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [6.0.0RC2] - 2023-08-20 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0RC2), [PECL](https://pecl.php.net/package/redis/6.0.0RC2)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fix arginfo for arguments that default to null + [8d99b7d1](https://github.com/phpredis/phpredis/commit/8d99b7d1) + ([Nicolas Grekas](https://github.com/nicolas-grekas)) +- Fix C99 usages + [54d9ca45](https://github.com/phpredis/phpredis/commit/54d9ca45) + ([Remi Collet](https://github.com/remicollet)) +- Raise minimal supported version to 7.2 + [e10b9a85](https://github.com/phpredis/phpredis/commit/e10b9a85) + ([Remi Collet](https://github.com/remicollet)) + ## [6.0.0RC1] - 2023-08-01 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0RC1), [PECL](https://pecl.php.net/package/redis/6.0.0RC1)) ### Sponsors :sparkling_heart: From f9c1e2231cb7fbaf9c894425b4d96d10895702f5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 20 Aug 2023 11:20:24 +0300 Subject: [PATCH 1791/1986] 6.0.0RC2 --- package.xml | 52 +++++++++++++++++++++++++++++----------------------- php_redis.h | 2 +- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/package.xml b/package.xml index 10a25379e1..1b3c90fd28 100644 --- a/package.xml +++ b/package.xml @@ -27,25 +27,17 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2023-08-01 + 2023-08-20 - 6.0.0RC1 + 6.0.0RC2 6.0.0 - alpha - alpha + beta + beta PHP - phpredis 6.0.0RC1 - - This release adds new commands introduced in Redis 6.2 and 7.0 as well - as many fixes and improvements. - - You can find a detailed list of changes in CHANGELOG.md and package.xml - or by inspecting the git commit logs. - --- Sponsors --- Audiomack - https://audiomack.com @@ -57,8 +49,22 @@ http://pear.php.net/dtd/package-2.0.xsd"> BatchLabs - https://batch.com Luis Zarate - https://github.com/jlzaratec + phpredis 6.0.0RC2 + + * Fix arginfo for arguments that default to null [8d99b7d1] (Nicolas Grekas) + * Fix C99 usages [54d9ca45] (Remi Collet) + * Raise minimal supported version to 7.2 [e10b9a85] (Remi Collet) + --- + phpredis 6.0.0RC1 + + This release adds new commands introduced in Redis 6.2 and 7.0 as well + as many fixes and improvements. + + You can find a detailed list of changes in CHANGELOG.md and package.xml + or by inspecting the git commit logs. + * Fix restoring keys when using compression [82e08723] (Till Krüss) * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) @@ -275,18 +281,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> - alphaalpha - 6.0.0RC16.0.0 - 2023-08-01 + betabeta + 6.0.0RC26.0.0 + 2023-08-20 - phpredis 6.0.0RC1 - - This release adds new commands introduced in Redis 6.2 and 7.0 as well - as many fixes and improvements. - - You can find a detailed list of changes in CHANGELOG.md and package.xml - or by inspecting the git commit logs. - --- Sponsors --- Audiomack - https://audiomack.com @@ -298,8 +296,16 @@ http://pear.php.net/dtd/package-2.0.xsd"> BatchLabs - https://batch.com Luis Zarate - https://github.com/jlzaratec + phpredis 6.0.0RC2 + + * Fix arginfo for arguments that default to null [8d99b7d1] (Nicolas Grekas) + * Fix C99 usages [54d9ca45] (Remi Collet) + * Raise minimal supported version to 7.2 [e10b9a85] (Remi Collet) + --- + phpredis 6.0.0RC1 + * Fix restoring keys when using compression [82e08723] (Till Krüss) * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) diff --git a/php_redis.h b/php_redis.h index 20ea76ee64..8f3efdacf4 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.0RC1" +#define PHP_REDIS_VERSION "6.0.0RC2" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ From 3674d6635153a2b0f37a62f07ebcad683d916fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Till=20Kr=C3=BCss?= Date: Wed, 6 Sep 2023 09:30:39 -0700 Subject: [PATCH 1792/1986] Add missing option to example --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index bd3fbd985a..0f02eb5c19 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -17,7 +17,7 @@ To build this extension for the sources tree: git clone https://github.com/phpredis/phpredis.git cd phpredis phpize -./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd] +./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd] [--enable-redis-lz4] make && make install ~~~ From e193c873dea288a81ed0537749a5e8930013fa0b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 9 Sep 2023 14:23:42 +0300 Subject: [PATCH 1793/1986] 6.0.0 --- CHANGELOG.md | 17 +++++++++++++++++ package.xml | 26 +++++++++++++++++++------- php_redis.h | 2 +- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afa4239a2c..3601ef083c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [6.0.0] - 2023-09-09 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0), [PECL](https://pecl.php.net/package/redis/6.0.0)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +*There were no changes between 6.0.0 and 6.0.0RC2* + ## [6.0.0RC2] - 2023-08-20 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0RC2), [PECL](https://pecl.php.net/package/redis/6.0.0RC2)) ### Sponsors :sparkling_heart: diff --git a/package.xml b/package.xml index 1b3c90fd28..bb0f7ac2fd 100644 --- a/package.xml +++ b/package.xml @@ -27,14 +27,14 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2023-08-20 + 2023-09-09 - 6.0.0RC2 + 6.0.0 6.0.0 - beta - beta + stable + stable PHP @@ -49,6 +49,12 @@ http://pear.php.net/dtd/package-2.0.xsd"> BatchLabs - https://batch.com Luis Zarate - https://github.com/jlzaratec + phpredis 6.0.0 + + - There were no changes between 6.0.0 and 6.0.0RC2. + + --- + phpredis 6.0.0RC2 * Fix arginfo for arguments that default to null [8d99b7d1] (Nicolas Grekas) @@ -281,9 +287,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> - betabeta - 6.0.0RC26.0.0 - 2023-08-20 + stablestable + 6.0.06.0.0 + 2023-09-09 --- Sponsors --- @@ -296,6 +302,12 @@ http://pear.php.net/dtd/package-2.0.xsd"> BatchLabs - https://batch.com Luis Zarate - https://github.com/jlzaratec + phpredis 6.0.0 + + - There were no changes between 6.0.0 and 6.0.0RC2. + + --- + phpredis 6.0.0RC2 * Fix arginfo for arguments that default to null [8d99b7d1] (Nicolas Grekas) diff --git a/php_redis.h b/php_redis.h index 8f3efdacf4..ce4d2f30aa 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.0RC2" +#define PHP_REDIS_VERSION "6.0.0" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ From 78449acc4c53209cb8a9387fb37ea1e60231da79 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 9 Sep 2023 14:33:16 +0300 Subject: [PATCH 1794/1986] Back to dev --- php_redis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php_redis.h b/php_redis.h index ce4d2f30aa..5944799ac4 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.0" +#define PHP_REDIS_VERSION "6.0.1-dev" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ From 5bd3138731c4239eba2b2a556a0f81636f1714d0 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 11 Sep 2023 16:24:25 +0300 Subject: [PATCH 1795/1986] Replace UTF-8 characters in package.xml --- package.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.xml b/package.xml index bb0f7ac2fd..4bc74aeed8 100644 --- a/package.xml +++ b/package.xml @@ -71,7 +71,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> You can find a detailed list of changes in CHANGELOG.md and package.xml or by inspecting the git commit logs. - * Fix restoring keys when using compression [82e08723] (Till Krüss) + * Fix restoring keys when using compression [82e08723] (Till Kruss) * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) * Fix NULL-pointer dereferences and handle possible UB [36457555] (Pavlo Yatsukhnenko) @@ -91,7 +91,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fix testObject for redis 7.2 [fea19b52, dcb95a3f] (Remi Collet) * Fix bug: the pipeline mode socket return an unexpected result after reconnecting [a3327d9d] (thomaston) * Fix stub files [9aa5f387, 74cf49f5, 8b1eafe8, e392dd88, b5ea5fd7, 71758b09, 2a6dee5d] (Nicolas Grekas), (Michael Grunder) - * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Krüss) + * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Kruss) * Allow to pass null as iterator [14d121bb] (Pavlo Yatsukhnenko) * Add NOMKSTREAM option to XADD command. [f9436e25] (Pavlo Yatsukhnenko) * Don't allow reconnect on read response [5a269ab6] (Pavlo Yatsukhnenko) @@ -318,7 +318,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> phpredis 6.0.0RC1 - * Fix restoring keys when using compression [82e08723] (Till Krüss) + * Fix restoring keys when using compression [82e08723] (Till Kruss) * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) * Fix NULL-pointer dereferences and handle possible UB [36457555] (Pavlo Yatsukhnenko) @@ -338,7 +338,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fix testObject for redis 7.2 [fea19b52, dcb95a3f] (Remi Collet) * Fix bug: the pipeline mode socket return an unexpected result after reconnecting [a3327d9d] (thomaston) * Fix stub files [9aa5f387, 74cf49f5, 8b1eafe8, e392dd88, b5ea5fd7, 71758b09, 2a6dee5d] (Nicolas Grekas), (Michael Grunder) - * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Krüss) + * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Kruss) * Allow to pass null as iterator [14d121bb] (Pavlo Yatsukhnenko) * Add NOMKSTREAM option to XADD command. [f9436e25] (Pavlo Yatsukhnenko) * Don't allow reconnect on read response [5a269ab6] (Pavlo Yatsukhnenko) @@ -1297,7 +1297,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * A printf like method to construct a Redis RESP command [a4a0ed, d75081, bdd287, 0eaeae, b3d00d] (Michael Grunder) * Use custom objects instead of zend_list for storing Redis/RedisArray [a765f8, 8fa85a] (Pavlo Yatsukhnenko) * Make sure redisCluster members are all initialized on (re)creation [162d88] (Michael Grunder) - * Fix Null Bulk String response parsing in cluster library [058753] (Alberto Fernández) + * Fix Null Bulk String response parsing in cluster library [058753] (Alberto Fernandez) * Add hStrLen command [c52077, fb88e1] (Pavlo Yatsukhnenko) * Add optional COUNT argument to sPop [d2e203] (Michael Grunder) * Allow sInterStore to take one arg [26aec4, 4cd06b] (Michael Grunder) @@ -1329,7 +1329,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * RedisArray segfault fix [564ce3] (Pavlo Yatsukhnenko) * Small memory leak fix [645888b] (Mike Grunder) * Segfault fix when recreating RedisCluster objects [abf7d4] (Michael Grunder) - * Fix for RedisCluster bulk response parsing [4121c4] (Alberto Fernández) + * Fix for RedisCluster bulk response parsing [4121c4] (Alberto Fernandez) * Re allow single array for sInterStore [6ef0c2, d01966] (Michael Grunder) * Better TravisCI integration [4fd2f6] (Pavlo Yatsukhnenko) @@ -1428,7 +1428,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * PHP liveness checking workaround (Shafreeck Sea) [c18d58b9] * Various documentation and code formatting and style fixes (ares333, - sanpili, Bryan Nelson, linfangrong, Romero Malaquias, Viktor Szépe) + sanpili, Bryan Nelson, linfangrong, Romero Malaquias, Viktor Szepe) * Fix scan reply processing to use long instead of int to avoid overflow (mixiaojiong). * Fix potential segfault in Redis Cluster session storage (Sergei Lomakov) From 849bedb6c3ecf0082b31ff750092407085272db3 Mon Sep 17 00:00:00 2001 From: Joost Date: Wed, 13 Sep 2023 11:38:55 +0200 Subject: [PATCH 1796/1986] Update sentinel documentation to reflect changes to constructor in 6.0 release --- sentinel.md | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/sentinel.md b/sentinel.md index ec0adec52b..c43a3ff4f3 100644 --- a/sentinel.md +++ b/sentinel.md @@ -21,12 +21,42 @@ Redis Sentinel also provides other collateral tasks such as monitoring, notifica ##### *Example* ~~~php -$sentinel = new RedisSentinel('127.0.0.1'); // default parameters -$sentinel = new RedisSentinel('127.0.0.1', 26379, 2.5); // 2.5 sec timeout. -$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, 'sentinel'); // persistent connection with id 'sentinel' -$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, ''); // also persistent connection with id '' -$sentinel = new RedisSentinel('127.0.0.1', 26379, 1, null, 100); // 1 sec timeout, 100ms delay between reconnection attempts. -$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, NULL, 0, 0, "secret"); // connect sentinel with password authentication +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', +]); // default parameters +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, +]); // 2.5 sec timeout. +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, + 'persistent' => 'sentinel', +]); // persistent connection with id 'sentinel' +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, + 'persistent' => '', +]); // also persistent connection with id '' +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 1, + 'persistent' => null, + 'retryInterval' => 100, +]); // 1 sec timeout, 100ms delay between reconnection attempts. +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 0, + 'persistent' => null, + 'retryInterval' => 0, + 'readTimeout' => 0, + 'auth' => 'secret', +]); // connect sentinel with password authentication ~~~ ### Usage From 1ad95b631811b10312b5682c561ec8fa901d2736 Mon Sep 17 00:00:00 2001 From: Joost Date: Wed, 13 Sep 2023 18:28:27 +0200 Subject: [PATCH 1797/1986] Add back old examples with note --- sentinel.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sentinel.md b/sentinel.md index c43a3ff4f3..eac3d9e0cf 100644 --- a/sentinel.md +++ b/sentinel.md @@ -18,7 +18,7 @@ Redis Sentinel also provides other collateral tasks such as monitoring, notifica *read_timeout*: Float, value in seconds (optional, default is 0 meaning unlimited) *auth*:String, or an Array with one or two elements, used to authenticate with the redis-sentinel. (optional, default is NULL meaning NOAUTH) -##### *Example* +##### *Examples for version 6.0 or later* ~~~php $sentinel = new RedisSentinel([ @@ -59,6 +59,17 @@ $sentinel = new RedisSentinel([ ]); // connect sentinel with password authentication ~~~ +##### *Examples for versions older than 6.0* + +~~~php +$sentinel = new RedisSentinel('127.0.0.1'); // default parameters +$sentinel = new RedisSentinel('127.0.0.1', 26379, 2.5); // 2.5 sec timeout. +$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, 'sentinel'); // persistent connection with id 'sentinel' +$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, ''); // also persistent connection with id '' +$sentinel = new RedisSentinel('127.0.0.1', 26379, 1, null, 100); // 1 sec timeout, 100ms delay between reconnection attempts. +$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, NULL, 0, 0, "secret"); // connect sentinel with password authentication +~~~ + ### Usage ----- From 264c0c7ea46b328aed9f156eb8642679cd72562e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 17 Sep 2023 10:17:05 +0300 Subject: [PATCH 1798/1986] Fix unknown expiration modifier warning when null argument passed --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 53d1d0da42..637f084a83 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -6002,7 +6002,7 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_STR(key) Z_PARAM_LONG(timeout) Z_PARAM_OPTIONAL - Z_PARAM_STR(mode) + Z_PARAM_STR_OR_NULL(mode) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (mode != NULL && !(zend_string_equals_literal_ci(mode, "NX") || From 95bd184be9ad4ea38b8b190a3b7ee3a67ca893d8 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 17 Sep 2023 10:25:55 +0300 Subject: [PATCH 1799/1986] Update redis.stub.php --- redis.stub.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 05a7ebd047..5ba06f3e6a 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1091,8 +1091,9 @@ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; * redis-server >= 7.0.0 you may send an additional "mode" argument which * modifies how the command will execute. * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the + * @param string $key The key to set an expiration on. + * @param int $timeout The number of seconds after which key will be automatically deleted. + * @param string|null $mode A two character modifier that changes how the * command works. * * NX - Set expiry only if key has no expiry @@ -1122,9 +1123,9 @@ public function expire(string $key, int $timeout, ?string $mode = null): Redis|b /** * Set a key to expire at an exact unix timestamp. * - * @param string $key The key to set an expiration on. - * @param int $timestamp The unix timestamp to expire at. - * @param string $mode An option 'mode' that modifies how the command acts (see {@link Redis::expire}). + * @param string $key The key to set an expiration on. + * @param int $timestamp The unix timestamp to expire at. + * @param string|null $mode An option 'mode' that modifies how the command acts (see {@link Redis::expire}). * @return Redis|bool True if an expiration was set, false if not. * * @see https://redis.io/commands/expireat @@ -2256,8 +2257,9 @@ public function persist(string $key): Redis|bool; * * @see Redis::expire() for a description of the mode argument. * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the + * @param string $key The key to set an expiration on. + * @param int $timeout The number of milliseconds after which key will be automatically deleted. + * @param string|null $mode A two character modifier that changes how the * command works. * * @return Redis|bool True if an expiry was set on the key, and false otherwise. @@ -2270,8 +2272,9 @@ public function pexpire(string $key, int $timeout, ?string $mode = null): bool; * * @see Redis::expire() For a description of the mode argument. * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the + * @param string $key The key to set an expiration on. + * @param int $timestamp The unix timestamp to expire at. + * @param string|null $mode A two character modifier that changes how the * command works. * * @return Redis|bool True if an expiration was set on the key, false otherwise. From 5f6ce414c3246007ffa1d4cd2da14d5e2c78e7a4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 6 Dec 2022 11:14:20 -0800 Subject: [PATCH 1800/1986] Update tests to allow users to use a custom class. Add a mechanism that allows users to specify an arbitrary class name, in combination with a search path so that PhpRedis unit tests can be run against a different client. Additionally, this commit allows multiple classes to be invoked in one test execution either by passing multiple `--class` arguments, or a class argument with a comma separated value. --- tests/RedisArrayTest.php | 5 +- tests/RedisClusterTest.php | 6 +- tests/RedisTest.php | 322 +++++++++++++++++++++---------------- tests/TestRedis.php | 102 +++++++----- tests/TestSuite.php | 46 ++++-- 5 files changed, 290 insertions(+), 191 deletions(-) diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 5a20d271c7..696ba927bf 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -59,6 +59,7 @@ public function setUp() { if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } + $this->ra = new RedisArray($newRing, $options); $this->min_version = getMinVersion($this->ra); } @@ -507,7 +508,7 @@ public function testMultiExec() { // change both in a transaction. $host = $this->ra->_target('{employee:joe}'); // transactions are per-node, so we need a reference to it. - $tr = $this->ra->multi($host) + $this->ra->multi($host) ->set('1_{employee:joe}_group', $newGroup) ->set('1_{employee:joe}_salary', $newSalary) ->exec(); @@ -603,7 +604,6 @@ public function testDiscard() { // Get after discard, unchanged: $this->assertTrue($this->ra->get($key) === 'test1'); } - } // Test custom distribution function @@ -660,7 +660,6 @@ function run_tests($className, $str_filter, $str_host, $auth) { $newRing = ["$str_host:6379", "$str_host:6380", "$str_host:6381"]; $oldRing = []; $serverList = ["$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"]; - // run return TestSuite::run($className, $str_filter, $str_host, NULL, $auth); } diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 67491dde00..cf8e412725 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -133,9 +133,9 @@ public function testRandomKey() { } public function testEcho() { - $this->assertEquals($this->redis->echo('k1', 'hello'), 'hello'); - $this->assertEquals($this->redis->echo('k2', 'world'), 'world'); - $this->assertEquals($this->redis->echo('k3', " 0123 "), " 0123 "); + $this->assertEquals($this->redis->echo('echo1', 'hello'), 'hello'); + $this->assertEquals($this->redis->echo('echo2', 'world'), 'world'); + $this->assertEquals($this->redis->echo('echo3', " 0123 "), " 0123 "); } public function testSortPrefix() { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 20aabf8bcc..8bf515811a 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -33,14 +33,48 @@ class Redis_Test extends TestSuite */ protected $sessionSaveHandler = 'redis'; + protected function getNilValue() { + return FALSE; + } + + protected function getSerializers() { + $result = [Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP]; + + if (defined('Redis::SERIALIZER_IGBINARY')) + $result[] = Redis::SERIALIZER_IGBINARY; + if (defined('Redis::SERIALIZER_JSON')) + $result[] = Redis::SERIALIZER_JSON; + if (defined('Redis::SERIALIZER_MSGPACK')) + $result[] = Redis::SERIALIZER_MSGPACK; + + return $result; + } + + protected function getCompressors() { + $result[] = Redis::COMPRESSION_NONE; + if (defined('Redis::COMPRESSION_LZF')) + $result[] = Redis::COMPRESSION_LZF; + if (defined('Redis::COMPRESSION_LZ4')) + $result[] = Redis::COMPRESSION_LZ4; + if (defined('Redis::COMPRESSION_ZSTD')) + $result[] = Redis::COMPRESSION_ZSTD; + + return $result; + } + + /* Overridable left/right constants */ + protected function getLeftConstant() { + return Redis::LEFT; + } + + protected function getRightConstant() { + return Redis::RIGHT; + } + 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) { @@ -127,12 +161,16 @@ public function reset() $this->tearDown(); } - /* Helper function to determine if the clsas has pipeline support */ + /* Helper function to determine if the clsas has pipeline support */ protected function havePipeline() { $str_constant = get_class($this->redis) . '::PIPELINE'; return defined($str_constant); } + protected function haveMulti() { + return defined(get_class($this->redis) . '::MULTI'); + } + public function testMinimumVersion() { // Minimum server version required for tests @@ -146,16 +184,18 @@ public function testPing() { $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()); + if ($this->haveMulti()) { + $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(); + return; } $ret = $this->redis->pipeline() @@ -670,19 +710,21 @@ public function testRenameNx() { } public function testMultiple() { - $this->redis->del('k1'); - $this->redis->del('k2'); - $this->redis->del('k3'); + $kvals = [ + 'mget1' => 'v1', + 'mget2' => 'v2', + 'mget3' => 'v3' + ]; + + $this->redis->mset($kvals); - $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->assertEquals([$kvals['mget1']], $this->redis->mget(['mget1'])); + + $this->assertEquals(['v1', 'v2', false], $this->redis->mget(['mget1', 'mget2', 'NoKey'])); + $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['mget1', 'mget2', 'mget3'])); + $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['mget1', 'mget2', 'mget3'])); $this->redis->set('k5', '$1111111111'); $this->assertEquals([0 => '$1111111111'], $this->redis->mget(['k5'])); @@ -691,17 +733,17 @@ public function testMultiple() { } public function testMultipleBin() { + $kvals = [ + 'binkey-1' => random_bytes(16), + 'binkey-2' => random_bytes(16), + 'binkey-3' => random_bytes(16), + ]; - $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')); + foreach ($kvals as $k => $v) { + $this->redis->set($k, $v); + } - $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'])); + $this->assertEquals(array_values($kvals), $this->redis->mget(array_keys($kvals))); } public function testSetTimeout() { @@ -1477,10 +1519,10 @@ public function testlMove() { $this->redis->lPush('{list}0', 'b'); $this->redis->lPush('{list}0', 'c'); - $return = $this->redis->lMove('{list}0', '{list}1', Redis::LEFT, Redis::RIGHT); + $return = $this->redis->lMove('{list}0', '{list}1', $this->getLeftConstant(), $this->getRightConstant()); $this->assertEquals('c', $return); - $return = $this->redis->lMove('{list}0', '{list}1', Redis::RIGHT, Redis::LEFT); + $return = $this->redis->lMove('{list}0', '{list}1', $this->getRightConstant(), $this->getLeftConstant()); $this->assertEquals('a', $return); $this->assertEquals(['b'], $this->redis->lRange('{list}0', 0, -1)); @@ -1495,10 +1537,10 @@ public function testBlmove() { $this->redis->del('{list}0', '{list}1'); $this->redis->rpush('{list}0', 'a'); - $this->assertEquals('a', $this->redis->blmove('{list}0', '{list}1', Redis::LEFT, Redis::LEFT, 1.0)); + $this->assertEquals('a', $this->redis->blmove('{list}0', '{list}1', $this->getLeftConstant(), $this->getLeftConstant(), 1.0)); $st = microtime(true); - $ret = $this->redis->blmove('{list}0', '{list}1', Redis::LEFT, Redis::LEFT, .1); + $ret = $this->redis->blmove('{list}0', '{list}1', $this->getLeftConstant(), $this->getLeftConstant(), .1); $et = microtime(true); $this->assertEquals(false, $ret); @@ -2381,7 +2423,11 @@ public function testWait() { } public function testInfo() { - foreach ([false, true] as $boo_multi) { + $sequence = [false]; + if ($this->haveMulti()) + $sequence[] = true; + + foreach ($sequence as $boo_multi) { if ($boo_multi) { $this->redis->multi(); $this->redis->info(); @@ -2447,7 +2493,7 @@ public function testInfoCommandStats() { } public function testSelect() { - $this->assertFalse($this->redis->select(-1)); + $this->assertFalse(@$this->redis->select(-1)); $this->assertTrue($this->redis->select(0)); } @@ -2495,12 +2541,12 @@ public function testMset() { 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(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(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 @@ -3187,7 +3233,7 @@ public function testHashes() { $this->assertTrue(2 === $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->assertEquals("".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)); @@ -3213,11 +3259,12 @@ public function testHashes() { $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->assertEquals(['x' => '123', 'y' => '456'], $this->redis->hMget('h', ['x', 'y'])); + $this->assertEquals(['z' => 'abc'], $this->redis->hMget('h', ['z'])); + $this->assertEquals(['x' => '123', 't' => FALSE, 'y' => '456'], $this->redis->hMget('h', ['x', 't', 'y'])); + $this->assertEquals(['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])); + $this->assertEquals([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); @@ -3236,7 +3283,7 @@ public function testHashes() { // references $keys = [123, 'y']; foreach ($keys as &$key) {} - $this->assertTrue([123 => 'x', 'y' => '456'] === $this->redis->hMget('h', $keys)); + $this->assertEquals($this->redis->hMget('h', $keys), [123 => 'x', 'y' => '456']); // check non-string types. $this->redis->del('h1'); @@ -3317,30 +3364,20 @@ public function testObject() { $this->redis->del('key'); $this->redis->lpush('key', 'value'); - $str_encoding = $this->redis->object('encoding', 'key'); - if (version_compare($this->version, '7.1.240') >= 0) { - /* Since redis 7.2-rc1 */ - $valid = ['listpack']; - } else { - /* Newer versions of redis are going to encode lists as 'quicklists', - * so 'quicklist' or 'ziplist' or 'listpack' are valid here */ - $valid = ['ziplist', 'quicklist']; - } - $this->assertTrue(in_array($str_encoding, $valid), $str_encoding); + /* Redis has improved the encoding here throughout the various versions. The value + can either be 'ziplist', 'quicklist', or 'listpack' */ + $encoding = $this->redis->object('encoding', 'key'); + $this->assertTrue(in_array($encoding, ['ziplist', 'quicklist', 'listpack'])); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->sadd('key', 'value'); - $str_encoding = $this->redis->object('encoding', 'key'); - if (version_compare($this->version, '7.1.240') >= 0) { - /* Since redis 7.2-rc1 */ - $valid = ['listpack']; - } else { - $valid = ['hashtable']; - } - $this->assertTrue(in_array($str_encoding, $valid), $str_encoding); + + /* Redis 7.2.0 switched to 'listpack' for small sets */ + $encoding = $this->redis->object('encoding', 'key'); + $this->assertTrue($encoding == 'hashtable' || $encoding == 'listpack'); $this->assertTrue($this->redis->object('refcount', 'key') === 1); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); @@ -3419,6 +3456,7 @@ public function testPipeline() { public function testPipelineMultiExec() { +return; if (!$this->havePipeline()) { $this->markTestSkipped(); } @@ -3575,12 +3613,12 @@ protected function sequence($mode) { $i = 0; $ttl = $ret[$i++]; $this->assertTrue($ttl === -1 || $ttl === -2); - $this->assertTrue($ret[$i++] === ['val1', 'valX', FALSE]); // mget + $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++] == TRUE); // expire $this->assertTrue($ret[$i++] === 5); // ttl - $this->assertTrue($ret[$i++] === TRUE); // expireAt + $this->assertTrue($ret[$i++] == TRUE); // expireAt $this->assertTrue(count($ret) == $i); $ret = $this->redis->multi($mode) @@ -3630,7 +3668,6 @@ protected function sequence($mode) { $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') @@ -3737,9 +3774,9 @@ protected function sequence($mode) { $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++] == 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($ret[$i++] == TRUE); // expireAt returns TRUE for an existing key $this->assertTrue(count($ret) === $i); // lists @@ -3795,10 +3832,8 @@ protected function sequence($mode) { ->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') @@ -3827,13 +3862,12 @@ protected function sequence($mode) { $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. @@ -4971,7 +5005,7 @@ public function testSerializerJSON() private function checkSerializer($mode) { $this->redis->del('key'); - $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // default + $this->assertEquals(Redis::SERIALIZER_NONE, $this->redis->getOption(Redis::OPT_SERIALIZER)); // default $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, $mode) === TRUE); // set ok $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === $mode); // get ok @@ -4988,10 +5022,10 @@ private function checkSerializer($mode) { $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)); + $this->assertEquals($a[0], $this->redis->lIndex('key', 0)); + $this->assertEquals($a[1], $this->redis->lIndex('key', 1)); + $this->assertEquals($a[2], $this->redis->lIndex('key', 2)); + $this->assertEquals($a[3], $this->redis->lIndex('key', 3)); // lrem $this->assertTrue($this->redis->lrem('key', $a[3]) === 1); @@ -4999,8 +5033,8 @@ private function checkSerializer($mode) { // 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)); + $this->assertEquals(TRUE, $this->redis->lSet('key', 0, $a[0])); + $this->assertEquals($a[0], $this->redis->lIndex('key', 0)); // lInsert $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], [1,2,3]) === 4); @@ -5107,36 +5141,36 @@ private function checkSerializer($mode) { $this->assertTrue($this->redis->get($k) === $v); } - $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']]; + $a = ['f0' => 1, 'f1' => 42, 'f2' => NULL, 'f3' => FALSE, 'f4' => ['a' => 'b']]; // hSet - $this->redis->del('key'); + $this->redis->del('hash'); foreach($a as $k => $v) { - $this->assertTrue(1 === $this->redis->hSet('key', $k, $v)); + $this->assertTrue(1 === $this->redis->hSet('hash', $k, $v)); } // hGet foreach($a as $k => $v) { - $this->assertTrue($v === $this->redis->hGet('key', $k)); + $this->assertTrue($v === $this->redis->hGet('hash', $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')); + $this->assertTrue($a === $this->redis->hGetAll('hash')); + $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f0')); + $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f1')); + $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f2')); + $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f3')); + $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f4')); // hMSet - $this->redis->del('key'); - $this->redis->hMSet('key', $a); + $this->redis->del('hash'); + $this->redis->hMSet('hash', $a); foreach($a as $k => $v) { - $this->assertTrue($v === $this->redis->hGet('key', $k)); + $this->assertTrue($v === $this->redis->hGet('hash', $k)); } // hMget - $hmget = $this->redis->hMget('key', array_keys($a)); + $hmget = $this->redis->hMget('hash', array_keys($a)); foreach($hmget as $k => $v) { $this->assertTrue($v === $a[$k]); } @@ -5155,10 +5189,12 @@ private function checkSerializer($mode) { } // multi-exec - $this->sequence(Redis::MULTI); + if ($this->haveMulti()) { + $this->sequence(Redis::MULTI); + } - // keys - $this->assertTrue(is_array($this->redis->keys('*'))); + // TODO: Re enable this before merging into develop + // $this->assertTrue(is_array($this->redis->keys('*'))); // issue #62, hgetall $this->redis->del('hash1'); @@ -5186,6 +5222,11 @@ private function checkSerializer($mode) { $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok } + // check that zRem doesn't crash with a missing parameter (GitHub issue #102): +// public function testGHIssue_102() { +// $this->assertTrue(FALSE === @$this->redis->zRem('key')); +// } + public function testCompressionLZF() { if (!defined('Redis::COMPRESSION_LZF')) { @@ -5231,11 +5272,25 @@ public function testCompressionLZ4() 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 + $set_cmp = $this->redis->setOption(Redis::OPT_COMPRESSION, $mode); + $this->assertTrue($set_cmp); + if ($set_cmp !== true) + return; + + $get_cmp = $this->redis->getOption(Redis::OPT_COMPRESSION); + $this->assertEquals($get_cmp, $mode); + if ($get_cmp !== $mode) + return; + + $set_lvl = $this->redis->setOption(Redis::OPT_COMPRESSION_LEVEL, $level); + $this->assertTrue($set_lvl); + if ($set_lvl !== true) + return; - $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION_LEVEL, $level) === TRUE); - $this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION_LEVEL) === $level); + $get_lvl = $this->redis->getOption(Redis::OPT_COMPRESSION_LEVEL); + $this->assertEquals($get_lvl, $level); + if ($get_lvl !== $level) + return; $val = 'xxxxxxxxxx'; $this->redis->set('key', $val); @@ -5538,16 +5593,7 @@ public function testSerialize() { $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) { + foreach($this->getSerializers() as $mode) { $arr_enc = []; $arr_dec = []; @@ -5603,7 +5649,7 @@ public function testUnserialize() { } public function testCompressHelpers() { - $compressors = self::getAvailableCompression(); + $compressors = $this->getCompressors(); $vals = ['foo', 12345, random_bytes(128), '']; @@ -5635,8 +5681,8 @@ public function testPackHelpers() { $this->redis->getOption(Redis::OPT_COMPRESSION) ]; - foreach ($this->serializers as $ser) { - $compressors = self::getAvailableCompression(); + foreach ($this->getSerializers() as $ser) { + $compressors = $this->getCompressors(); foreach ($compressors as $cmp) { $this->redis->setOption(Redis::OPT_SERIALIZER, $ser); $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp); @@ -6502,8 +6548,10 @@ public function testGeoSearchStore() { /* Test a 'raw' command */ public function testRawCommand() { - $this->redis->set('mykey','some-value'); - $str_result = $this->redis->rawCommand('get', 'mykey'); + $key = uniqid(); + + $this->redis->set($key,'some-value'); + $str_result = $this->redis->rawCommand('get', $key); $this->assertEquals($str_result, 'some-value'); $this->redis->del('mylist'); @@ -6608,7 +6656,7 @@ public function testXRange() { return $this->markTestSkipped(); foreach ([false, true] as $reverse) { - foreach ($this->serializers as $serializer) { + foreach ($this->getSerializers() as $serializer) { foreach ([NULL, 'prefix:'] as $prefix) { $this->redis->setOption(Redis::OPT_PREFIX, $prefix); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); @@ -6768,7 +6816,7 @@ public function testXRead() { if (!$this->minVersionCheck("5.0")) return $this->markTestSkipped(); - foreach ($this->serializers as $serializer) { + foreach ($this->getSerializers() as $serializer) { $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $this->doXReadTest(); } @@ -6801,7 +6849,7 @@ public function testXReadGroup() { /* Create some streams and groups */ $streams = ['{s}-1', '{s}-2']; - $groups = ['g1' => 0, 'g2' => 0]; + $groups = ['group1' => 0, 'group2' => 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 @@ -6814,7 +6862,7 @@ public function testXReadGroup() { $ids = $this->addStreamsAndGroups($streams, 1, $groups); /* Test that we get get the IDs we should */ - foreach (['g1', 'g2'] as $group) { + foreach (['group1', 'group2'] as $group) { foreach ($ids as $stream => $messages) { while ($ids[$stream]) { /* Read more messages */ @@ -6834,7 +6882,7 @@ public function testXReadGroup() { /* Test COUNT option */ for ($c = 1; $c <= 3; $c++) { $this->addStreamsAndGroups($streams, 3, $groups); - $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, $c); + $resp = $this->redis->xReadGroup('group1', 'consumer', $query1, $c); foreach ($resp as $stream => $smsg) { $this->assertEquals(count($smsg), $c); @@ -6843,27 +6891,27 @@ public function testXReadGroup() { /* Test COUNT option with NULL (should be ignored) */ $this->addStreamsAndGroups($streams, 3, $groups, NULL); - $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, NULL); + $resp = $this->redis->xReadGroup('group1', 'consumer', $query1, NULL); foreach ($resp as $stream => $smsg) { $this->assertEquals(count($smsg), 3); } /* Finally test BLOCK with a sloppy timing test */ - $t1 = $this->mstime(); + $tm1 = $this->mstime(); $qnew = ['{s}-1' => '>', '{s}-2' => '>']; - $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, 100); - $t2 = $this->mstime(); - $this->assertTrue($t2 - $t1 >= 100); + $this->redis->xReadGroup('group1', 'c1', $qnew, 0, 100); + $tm2 = $this->mstime(); + $this->assertTrue($tm2 - $tm1 >= 100); /* Make sure passing NULL to block doesn't block */ - $t1 = $this->mstime(); - $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, NULL); - $t2 = $this->mstime(); - $this->assertTrue($t2 - $t1 < 100); + $tm1 = $this->mstime(); + $this->redis->xReadGroup('group1', 'c1', $qnew, NULL, NULL); + $tm2 = $this->mstime(); + $this->assertTrue($tm2 - $tm1 < 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', $qnew, NULL, -1)); + $this->assertFalse(@$this->redis->xReadGroup('group1', 'c1', $qnew, -1)); + $this->assertFalse(@$this->redis->xReadGroup('group1', 'c1', $qnew, NULL, -1)); } public function testXPending() { @@ -7423,12 +7471,12 @@ public function testSession_correctLockRetryCount() { return; } - $t1 = microtime(true); + $tm1 = microtime(true); $ok = $this->startSessionProcess($sessionId, 0, false, 10, true, 100000, 10); if ( ! $this->assertFalse($ok)) return; - $t2 = microtime(true); + $tm2 = microtime(true); - $this->assertTrue($t2 - $t1 >= 1 && $t2 - $t1 <= 3); + $this->assertTrue($tm2 - $tm1 >= 1 && $tm2 - $tm1 <= 3); } public function testSession_defaultLockRetryCount() @@ -7459,7 +7507,7 @@ public function testSession_noUnlockOfOtherProcess() $this->setSessionHandler(); $sessionId = $this->generateSessionId(); - $t1 = microtime(true); + $tm1 = microtime(true); /* 1. Start a background process, and wait until we are certain * the lock was attained. */ @@ -7472,13 +7520,13 @@ public function testSession_noUnlockOfOtherProcess() /* 2. Attempt to lock the same session. This should force us to * wait until the first lock is released. */ - $t2 = microtime(true); + $tm2 = microtime(true); $ok = $this->startSessionProcess($sessionId, 0, false); - $t3 = microtime(true); + $tm3 = 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)); + $this->assertTrue($tm3 - $tm2 >= $nsec - ($tm2 - $tm1)); } public function testSession_lockWaitTime() diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 843a28f884..1ff1114efe 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -6,6 +6,39 @@ require_once(dirname($_SERVER['PHP_SELF'])."/RedisClusterTest.php"); require_once(dirname($_SERVER['PHP_SELF'])."/RedisSentinelTest.php"); +function getClassArray($classes) { + $result = []; + + if ( ! is_array($classes)) + $classes = [$classes]; + + foreach ($classes as $class) { + $result = array_merge($result, explode(',', $class)); + } + + return array_unique( + array_map(function ($v) { return strtolower($v); }, + $result + ) + ); +} + +function getTestClass($class) { + $arr_valid_classes = [ + 'redis' => 'Redis_Test', + 'redisarray' => 'Redis_Array_Test', + 'rediscluster' => 'Redis_Cluster_Test', + 'redissentinel' => 'Redis_Sentinel_Test' + ]; + + /* Return early if the class is one of our built-in ones */ + if (isset($arr_valid_classes[$class])) + return $arr_valid_classes[$class]; + + /* Try to load it */ + return TestSuite::loadTestClass($class); +} + /* Make sure errors go to stdout and are shown */ error_reporting(E_ALL); ini_set( 'display_errors','1'); @@ -13,9 +46,9 @@ /* Grab options */ $arr_args = getopt('', ['host:', 'port:', 'class:', 'test:', 'nocolors', 'user:', 'auth:']); -/* Grab the test the user is trying to run */ -$arr_valid_classes = ['redis', 'redisarray', 'rediscluster', 'redissentinel']; -$str_class = isset($arr_args['class']) ? strtolower($arr_args['class']) : 'redis'; +/* The test class(es) we want to run */ +$arr_classes = getClassArray($arr_args['class'] ?? 'redis'); + $boo_colorize = !isset($arr_args['nocolors']); /* Get our test filter if provided one */ @@ -39,12 +72,6 @@ echo TestSuite::make_warning("User passed without a password, ignoring!\n"); } -/* Validate the class is known */ -if (!in_array($str_class, $arr_valid_classes)) { - echo "Error: Valid test classes are Redis, RedisArray, RedisCluster and RedisSentinel!\n"; - exit(1); -} - /* Toggle colorization in our TestSuite class */ TestSuite::flagColorization($boo_colorize); @@ -52,35 +79,38 @@ echo "Note: these tests might take up to a minute. Don't worry :-)\n"; echo "Using PHP version " . PHP_VERSION . " (" . (PHP_INT_SIZE*8) . " bits)\n"; -/* Depending on the classes being tested, run our tests on it */ -echo "Testing class "; -if ($str_class == 'redis') { - echo TestSuite::make_bold("Redis") . "\n"; - exit(TestSuite::run("Redis_Test", $str_filter, $str_host, $i_port, $auth)); -} else if ($str_class == 'redisarray') { - echo TestSuite::make_bold("RedisArray") . "\n"; - global $useIndex; - foreach(array(true, false) as $useIndex) { - echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; - - /* The various RedisArray subtests we can run */ - $arr_ra_tests = [ - 'Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', - 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test' - ]; - - foreach ($arr_ra_tests as $str_test) { - /* Run until we encounter a failure */ - if (run_tests($str_test, $str_filter, $str_host, $auth) != 0) { - exit(1); +foreach ($arr_classes as $str_class) { + $str_class = getTestClass($str_class); + + /* Depending on the classes being tested, run our tests on it */ + echo "Testing class "; + if ($str_class == 'Redis_Array_Test') { + echo TestSuite::make_bold("RedisArray") . "\n"; + + foreach(array(true, false) as $useIndex) { + echo "\n". ($useIndex ? "WITH" : "WITHOUT") . " per-node index:\n"; + + /* The various RedisArray subtests we can run */ + $arr_ra_tests = [ + 'Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', + 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test' + ]; + + foreach ($arr_ra_tests as $str_test) { + /* Run until we encounter a failure */ + if (run_tests($str_test, $str_filter, $str_host, $auth) != 0) { + exit(1); + } } } + } else { + echo TestSuite::make_bold($str_class) . "\n"; + if (TestSuite::run("$str_class", $str_filter, $str_host, $i_port, $auth)) + exit(1); } -} else if ($str_class == 'rediscluster') { - echo TestSuite::make_bold("RedisCluster") . "\n"; - exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host, $i_port, $auth)); -} else { - echo TestSuite::make_bold("RedisSentinel") . "\n"; - exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host, $i_port, $auth)); } + +/* Success */ +exit(0); + ?> diff --git a/tests/TestSuite.php b/tests/TestSuite.php index a6006c4fab..529a08cb38 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -42,18 +42,6 @@ public function getHost() { return $this->str_host; } public function getPort() { return $this->i_port; } public function getAuth() { return $this->auth; } - public static function getAvailableCompression() { - $result[] = Redis::COMPRESSION_NONE; - if (defined('Redis::COMPRESSION_LZF')) - $result[] = Redis::COMPRESSION_LZF; - if (defined('Redis::COMPRESSION_LZ4')) - $result[] = Redis::COMPRESSION_LZ4; - if (defined('Redis::COMPRESSION_ZSTD')) - $result[] = Redis::COMPRESSION_ZSTD; - - return $result; - } - /** * Returns the fully qualified host path, * which may be used directly for php.ini parameters like session.save_path @@ -237,6 +225,40 @@ private static function getMaxTestLen($arr_methods, $str_limit) { return $i_result; } + private static function findFile($path, $file) { + $files = glob($path . '/*', GLOB_NOSORT); + + foreach ($files as $test) { + $test = basename($test); + if (strcasecmp($file, $test) == 0) + return $path . '/' . $test; + } + + return NULL; + } + + /* Small helper method that tries to load a custom test case class */ + public static function loadTestClass($class) { + $filename = "${class}.php"; + + if (($sp = getenv('PHPREDIS_TEST_SEARCH_PATH'))) { + $fullname = self::findFile($sp, $filename); + } else { + $fullname = self::findFile(__DIR__, $filename); + } + + if ( ! $fullname) + die("Fatal: Couldn't find $filename\n"); + + require_once($fullname); + + if ( ! class_exists($class)) + die("Fatal: Loaded '$filename' but didn't find class '$class'\n"); + + /* Loaded the file and found the class, return it */ + return $class; + } + /* Flag colorization */ public static function flagColorization($boo_override) { self::$_boo_colorize = $boo_override && function_exists('posix_isatty') && From 7825efbcede82c5e0c3ee0a08c824588aecf3d4d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 19 Sep 2023 20:58:44 -0700 Subject: [PATCH 1801/1986] Ensure we're talking to redis-server in our high ports test. Instead of just checking whether or not something is listening on the high ports, send a quick PING to make sure. We're not just using another Redis instance because the point of the test is to protect against a regression when connecting to high port numbers. --- tests/RedisTest.php | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 8bf515811a..9f62d5f3b8 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7323,22 +7323,31 @@ public function testUnixSocket() { } } + protected function detectRedis($host, $port) { + $sock = @fsockopen($host, $port, $errno, $errstr, .1); + if (! $sock) + return false; + + stream_set_timeout($sock, 0, 100000); + + $ping_cmd = "*1\r\n$4\r\nPING\r\n"; + if (fwrite($sock, $ping_cmd) != strlen($ping_cmd)) + return false; + + return fread($sock, strlen("+PONG\r\n")) == "+PONG\r\n"; + } + /* Test high ports if we detect Redis running there */ public function testHighPorts() { - $arr_ports = [32767, 32768, 32769]; - $arr_test_ports = []; - - foreach ($arr_ports as $port) { - if (is_resource(@fsockopen('localhost', $port))) { - $arr_test_ports[] = $port; - } - } + $ports = array_filter(array_map(function ($port) { + return $this->detectRedis('localhost', $port) ? $port : 0; + }, [32768, 32769, 32770])); - if ( ! $arr_test_ports) { + if ( ! $ports) { return $this->markTestSkipped(); } - foreach ($arr_test_ports as $port) { + foreach ($ports as $port) { $obj_r = new Redis(); try { @$obj_r->connect('localhost', $port); From eda399583ddf4261fd3ddc319070a3b01cbf9933 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 21 Sep 2023 14:50:23 +0300 Subject: [PATCH 1802/1986] Cluster nodes from ENV --- tests/RedisClusterTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index cf8e412725..86149be5c2 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -7,8 +7,6 @@ * where we're validating specific cluster mechanisms */ class Redis_Cluster_Test extends Redis_Test { - private static $_arr_node_map = []; - private $_arr_redis_types = [ Redis::REDIS_STRING, Redis::REDIS_SET, @@ -23,6 +21,7 @@ class Redis_Cluster_Test extends Redis_Test { RedisCluster::FAILOVER_DISTRIBUTE ]; + protected static $_arr_node_map = []; /** * @var string */ @@ -74,15 +73,16 @@ public function testSession_lockWaitTime() { return $this->markTestSkipped(); } public function __construct($str_host, $i_port, $str_auth) { parent::__construct($str_host, $i_port, $str_auth); - $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; - - if (!file_exists($str_nodemap_file)) { - fprintf(STDERR, "Error: Can't find nodemap file for seeds!\n"); - exit(1); - } - + self::$_arr_node_map = preg_split('/\s+/', getenv('REDIS_CLUSTER_NODES')); /* Store our node map */ if (!self::$_arr_node_map) { + $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; + + if (!file_exists($str_nodemap_file)) { + fprintf(STDERR, "Error: Can't find nodemap file for seeds!\n"); + exit(1); + } + self::$_arr_node_map = array_filter( explode("\n", file_get_contents($str_nodemap_file) )); From 0672703ba8049ca323e5a932ccb5d6a4f4419661 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 22 Sep 2023 14:57:48 +0300 Subject: [PATCH 1803/1986] Fix cluster nodes from ENV --- tests/RedisClusterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 86149be5c2..cfc3e9c23e 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -73,7 +73,7 @@ public function testSession_lockWaitTime() { return $this->markTestSkipped(); } public function __construct($str_host, $i_port, $str_auth) { parent::__construct($str_host, $i_port, $str_auth); - self::$_arr_node_map = preg_split('/\s+/', getenv('REDIS_CLUSTER_NODES')); + self::$_arr_node_map = array_filter(explode(' ', getenv('REDIS_CLUSTER_NODES'))); /* Store our node map */ if (!self::$_arr_node_map) { $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; From d5fbd7922a9ded50fc3408d0e91317d794174ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Till=20Kr=C3=BCss?= Date: Wed, 6 Sep 2023 09:30:39 -0700 Subject: [PATCH 1804/1986] Add missing option to example --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index bd3fbd985a..0f02eb5c19 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -17,7 +17,7 @@ To build this extension for the sources tree: git clone https://github.com/phpredis/phpredis.git cd phpredis phpize -./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd] +./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd] [--enable-redis-lz4] make && make install ~~~ From 3cdb6cd1c9c398cdb1242b91e850b699f1fd51d6 Mon Sep 17 00:00:00 2001 From: Joost Date: Wed, 13 Sep 2023 11:38:55 +0200 Subject: [PATCH 1805/1986] Update sentinel documentation to reflect changes to constructor in 6.0 release --- sentinel.md | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/sentinel.md b/sentinel.md index ec0adec52b..c43a3ff4f3 100644 --- a/sentinel.md +++ b/sentinel.md @@ -21,12 +21,42 @@ Redis Sentinel also provides other collateral tasks such as monitoring, notifica ##### *Example* ~~~php -$sentinel = new RedisSentinel('127.0.0.1'); // default parameters -$sentinel = new RedisSentinel('127.0.0.1', 26379, 2.5); // 2.5 sec timeout. -$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, 'sentinel'); // persistent connection with id 'sentinel' -$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, ''); // also persistent connection with id '' -$sentinel = new RedisSentinel('127.0.0.1', 26379, 1, null, 100); // 1 sec timeout, 100ms delay between reconnection attempts. -$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, NULL, 0, 0, "secret"); // connect sentinel with password authentication +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', +]); // default parameters +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, +]); // 2.5 sec timeout. +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, + 'persistent' => 'sentinel', +]); // persistent connection with id 'sentinel' +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, + 'persistent' => '', +]); // also persistent connection with id '' +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 1, + 'persistent' => null, + 'retryInterval' => 100, +]); // 1 sec timeout, 100ms delay between reconnection attempts. +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 0, + 'persistent' => null, + 'retryInterval' => 0, + 'readTimeout' => 0, + 'auth' => 'secret', +]); // connect sentinel with password authentication ~~~ ### Usage From 08a5ee071a222c02d81710f86b616b52d2250ac0 Mon Sep 17 00:00:00 2001 From: Joost Date: Wed, 13 Sep 2023 18:28:27 +0200 Subject: [PATCH 1806/1986] Add back old examples with note --- sentinel.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sentinel.md b/sentinel.md index c43a3ff4f3..eac3d9e0cf 100644 --- a/sentinel.md +++ b/sentinel.md @@ -18,7 +18,7 @@ Redis Sentinel also provides other collateral tasks such as monitoring, notifica *read_timeout*: Float, value in seconds (optional, default is 0 meaning unlimited) *auth*:String, or an Array with one or two elements, used to authenticate with the redis-sentinel. (optional, default is NULL meaning NOAUTH) -##### *Example* +##### *Examples for version 6.0 or later* ~~~php $sentinel = new RedisSentinel([ @@ -59,6 +59,17 @@ $sentinel = new RedisSentinel([ ]); // connect sentinel with password authentication ~~~ +##### *Examples for versions older than 6.0* + +~~~php +$sentinel = new RedisSentinel('127.0.0.1'); // default parameters +$sentinel = new RedisSentinel('127.0.0.1', 26379, 2.5); // 2.5 sec timeout. +$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, 'sentinel'); // persistent connection with id 'sentinel' +$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, ''); // also persistent connection with id '' +$sentinel = new RedisSentinel('127.0.0.1', 26379, 1, null, 100); // 1 sec timeout, 100ms delay between reconnection attempts. +$sentinel = new RedisSentinel('127.0.0.1', 26379, 0, NULL, 0, 0, "secret"); // connect sentinel with password authentication +~~~ + ### Usage ----- From d7a06e80a5e112b914f80f2bdf239a3b80bcb91f Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 17 Sep 2023 10:17:05 +0300 Subject: [PATCH 1807/1986] Fix unknown expiration modifier warning when null argument passed --- redis_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_commands.c b/redis_commands.c index 53d1d0da42..637f084a83 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -6002,7 +6002,7 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_STR(key) Z_PARAM_LONG(timeout) Z_PARAM_OPTIONAL - Z_PARAM_STR(mode) + Z_PARAM_STR_OR_NULL(mode) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (mode != NULL && !(zend_string_equals_literal_ci(mode, "NX") || From 2395ca1bbf23b480a12cd0bb00a9d7bd8959998b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 17 Sep 2023 10:25:55 +0300 Subject: [PATCH 1808/1986] Update redis.stub.php --- redis.stub.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 05a7ebd047..5ba06f3e6a 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1091,8 +1091,9 @@ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; * redis-server >= 7.0.0 you may send an additional "mode" argument which * modifies how the command will execute. * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the + * @param string $key The key to set an expiration on. + * @param int $timeout The number of seconds after which key will be automatically deleted. + * @param string|null $mode A two character modifier that changes how the * command works. * * NX - Set expiry only if key has no expiry @@ -1122,9 +1123,9 @@ public function expire(string $key, int $timeout, ?string $mode = null): Redis|b /** * Set a key to expire at an exact unix timestamp. * - * @param string $key The key to set an expiration on. - * @param int $timestamp The unix timestamp to expire at. - * @param string $mode An option 'mode' that modifies how the command acts (see {@link Redis::expire}). + * @param string $key The key to set an expiration on. + * @param int $timestamp The unix timestamp to expire at. + * @param string|null $mode An option 'mode' that modifies how the command acts (see {@link Redis::expire}). * @return Redis|bool True if an expiration was set, false if not. * * @see https://redis.io/commands/expireat @@ -2256,8 +2257,9 @@ public function persist(string $key): Redis|bool; * * @see Redis::expire() for a description of the mode argument. * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the + * @param string $key The key to set an expiration on. + * @param int $timeout The number of milliseconds after which key will be automatically deleted. + * @param string|null $mode A two character modifier that changes how the * command works. * * @return Redis|bool True if an expiry was set on the key, and false otherwise. @@ -2270,8 +2272,9 @@ public function pexpire(string $key, int $timeout, ?string $mode = null): bool; * * @see Redis::expire() For a description of the mode argument. * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the + * @param string $key The key to set an expiration on. + * @param int $timestamp The unix timestamp to expire at. + * @param string|null $mode A two character modifier that changes how the * command works. * * @return Redis|bool True if an expiration was set on the key, false otherwise. From 91c931bf70be9ea8cd083d035fe9d93154a0c19a Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 13:25:50 +0300 Subject: [PATCH 1809/1986] Update CHANGELOG.md --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3601ef083c..b52b414317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,35 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + + +### Fixed +- Fix unknown expiration modifier + [264c0c7e](https://github.com/phpredis/phpredis/commit/264c0c7e), + [95bd184b](https://github.com/phpredis/phpredis/commit/95bd184b) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Added +- Add missing option to exampleA + [3674d663](https://github.com/phpredis/phpredis/commit/3674d663) + ([Till Krüss](https://github.com/tillkruss)) +- Update sentinel documentation + [849bedb6](https://github.com/phpredis/phpredis/commit/849bedb6), + [1ad95b63](https://github.com/phpredis/phpredis/commit/1ad95b63) + ([Joost OrangeJuiced](https://github.com/OrangeJuiced)) + ## [6.0.0] - 2023-09-09 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0), [PECL](https://pecl.php.net/package/redis/6.0.0)) ### Sponsors :sparkling_heart: From e5d358a86ad052a3a77e138c2133d3d75132074d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 13:25:50 +0300 Subject: [PATCH 1810/1986] Update CHANGELOG.md --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3601ef083c..b52b414317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,35 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + + +### Fixed +- Fix unknown expiration modifier + [264c0c7e](https://github.com/phpredis/phpredis/commit/264c0c7e), + [95bd184b](https://github.com/phpredis/phpredis/commit/95bd184b) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Added +- Add missing option to exampleA + [3674d663](https://github.com/phpredis/phpredis/commit/3674d663) + ([Till Krüss](https://github.com/tillkruss)) +- Update sentinel documentation + [849bedb6](https://github.com/phpredis/phpredis/commit/849bedb6), + [1ad95b63](https://github.com/phpredis/phpredis/commit/1ad95b63) + ([Joost OrangeJuiced](https://github.com/OrangeJuiced)) + ## [6.0.0] - 2023-09-09 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0), [PECL](https://pecl.php.net/package/redis/6.0.0)) ### Sponsors :sparkling_heart: From 362e1141a3645089189be9a6aa5e87d4caf41cec Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 14:10:23 +0300 Subject: [PATCH 1811/1986] Fix memory leak and segfault in Redis::exec --- redis.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/redis.c b/redis.c index bd69b588ba..1e001f2097 100644 --- a/redis.c +++ b/redis.c @@ -1982,6 +1982,8 @@ PHP_METHOD(Redis, exec) RETURN_FALSE; } + ZVAL_FALSE(&z_ret); + if (IS_MULTI(redis_sock)) { if (IS_PIPELINE(redis_sock)) { PIPELINE_ENQUEUE_COMMAND(RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1); @@ -1991,7 +1993,6 @@ PHP_METHOD(Redis, exec) } SOCKET_WRITE_COMMAND(redis_sock, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1) - ZVAL_NULL(&z_ret); ret = redis_sock_read_multibulk_multi_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret); free_reply_callbacks(redis_sock); @@ -2025,7 +2026,7 @@ PHP_METHOD(Redis, exec) free_reply_callbacks(redis_sock); REDIS_DISABLE_MODE(redis_sock, PIPELINE); } - RETURN_ZVAL(&z_ret, 1, 0); + RETURN_ZVAL(&z_ret, 0, 1); } PHP_REDIS_API int From c48d150c13914279df0d0d9d31a3b1160540a6d9 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 14:11:37 +0300 Subject: [PATCH 1812/1986] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b52b414317..b3c7f11650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed +- Fix memory leak and segfault in Redis::exec + [362e1141](https://github.com/phpredis/phpredis/commit/362e1141) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Markus Podar](https://github.com/mfn)) - Fix unknown expiration modifier [264c0c7e](https://github.com/phpredis/phpredis/commit/264c0c7e), [95bd184b](https://github.com/phpredis/phpredis/commit/95bd184b) From 0a1ae0e60aba6bdc55c9bdba3469338377899aaa Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 14:10:23 +0300 Subject: [PATCH 1813/1986] Fix memory leak and segfault in Redis::exec --- redis.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/redis.c b/redis.c index bd69b588ba..1e001f2097 100644 --- a/redis.c +++ b/redis.c @@ -1982,6 +1982,8 @@ PHP_METHOD(Redis, exec) RETURN_FALSE; } + ZVAL_FALSE(&z_ret); + if (IS_MULTI(redis_sock)) { if (IS_PIPELINE(redis_sock)) { PIPELINE_ENQUEUE_COMMAND(RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1); @@ -1991,7 +1993,6 @@ PHP_METHOD(Redis, exec) } SOCKET_WRITE_COMMAND(redis_sock, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1) - ZVAL_NULL(&z_ret); ret = redis_sock_read_multibulk_multi_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret); free_reply_callbacks(redis_sock); @@ -2025,7 +2026,7 @@ PHP_METHOD(Redis, exec) free_reply_callbacks(redis_sock); REDIS_DISABLE_MODE(redis_sock, PIPELINE); } - RETURN_ZVAL(&z_ret, 1, 0); + RETURN_ZVAL(&z_ret, 0, 1); } PHP_REDIS_API int From 9467059ddc06a30e672075a05a68982ea0894815 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 14:11:37 +0300 Subject: [PATCH 1814/1986] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b52b414317..b3c7f11650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed +- Fix memory leak and segfault in Redis::exec + [362e1141](https://github.com/phpredis/phpredis/commit/362e1141) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Markus Podar](https://github.com/mfn)) - Fix unknown expiration modifier [264c0c7e](https://github.com/phpredis/phpredis/commit/264c0c7e), [95bd184b](https://github.com/phpredis/phpredis/commit/95bd184b) From dbb4f24e12c9825981263545560f557d2321bd9b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 13:41:28 +0300 Subject: [PATCH 1815/1986] 6.0.1 --- CHANGELOG.md | 2 + package.xml | 185 +++++++++++++-------------------------------------- php_redis.h | 2 +- 3 files changed, 48 insertions(+), 141 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3c7f11650..6494bbd504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [6.0.1] - 2023-09-23 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.1), [PECL](https://pecl.php.net/package/redis/6.0.1)) + ### Sponsors :sparkling_heart: - [Audiomack](https://audiomack.com) diff --git a/package.xml b/package.xml index 4bc74aeed8..66a738a146 100644 --- a/package.xml +++ b/package.xml @@ -27,9 +27,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2023-09-09 + 2023-09-23 - 6.0.0 + 6.0.1 6.0.0 @@ -47,153 +47,24 @@ http://pear.php.net/dtd/package-2.0.xsd"> Avtandil Kikabidze - https://github.com/akalongman Zaher Ghaibeh - https://github.com/zaherg BatchLabs - https://batch.com + Stackhero - https://github.com/stackhero-io + Florian Levis - https://github.com/Gounlaf Luis Zarate - https://github.com/jlzaratec - phpredis 6.0.0 - - - There were no changes between 6.0.0 and 6.0.0RC2. - - --- - - phpredis 6.0.0RC2 - - * Fix arginfo for arguments that default to null [8d99b7d1] (Nicolas Grekas) - * Fix C99 usages [54d9ca45] (Remi Collet) - * Raise minimal supported version to 7.2 [e10b9a85] (Remi Collet) - --- - phpredis 6.0.0RC1 + phpredis 6.0.1 - This release adds new commands introduced in Redis 6.2 and 7.0 as well - as many fixes and improvements. + This release contains fix for unknown expiration modifier issue + as well as memory leak and segfault in exec function + and small documentation improvements. You can find a detailed list of changes in CHANGELOG.md and package.xml or by inspecting the git commit logs. - * Fix restoring keys when using compression [82e08723] (Till Kruss) - * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) - * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) - * Fix NULL-pointer dereferences and handle possible UB [36457555] (Pavlo Yatsukhnenko) - * Fix security alerts [ee210f86, fb6a297c] (Pavlo Yatsukhnenko), (Michael Grunder) - * Fix segfault [55bf0202] (Pavlo Yatsukhnenko) - * Fix default host length [c40f9d6c] (Pavlo Yatsukhnenko) - * Fix redis session standalone stream ssl context [ed10f365, d1bc6727, 2ff11df5] (patricio.dorantes) - * Fix segfault with session+tls [a471c87a] (Pavlo Yatsukhnenko) - * Fix non standards conforming prototypes. [b3ce0486] (Michael Grunder) - * Avoid registering the same replicas multiple times [f2bfd723] (Marius Adam) - * Better unix:// or file:// detection. [d05d301b] (Michael Grunder) - * Future proof our igbinary header check [69355faa] (Michael Grunder) - * Fix BITOP cross-slot bug [af13f951] (Michael Grunder) - * SENTINEL RESET returns a long. [0243dd9d] (Michael Grunder) - * Fix redis_sock_read_multibulk_multi_reply_loop logic [d9cb5946, 5a643b62] (Pavlo Yatsukhnenko) - * Fix RPOP to unserialize/decompress data. [02c91d59] (Michael Grunder) - * Fix testObject for redis 7.2 [fea19b52, dcb95a3f] (Remi Collet) - * Fix bug: the pipeline mode socket return an unexpected result after reconnecting [a3327d9d] (thomaston) - * Fix stub files [9aa5f387, 74cf49f5, 8b1eafe8, e392dd88, b5ea5fd7, 71758b09, 2a6dee5d] (Nicolas Grekas), (Michael Grunder) - * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Kruss) - * Allow to pass null as iterator [14d121bb] (Pavlo Yatsukhnenko) - * Add NOMKSTREAM option to XADD command. [f9436e25] (Pavlo Yatsukhnenko) - * Don't allow reconnect on read response [5a269ab6] (Pavlo Yatsukhnenko) - * Reset multi/pipline transaction on pconnect close [0879770a] (Pavlo Yatsukhnenko) - * Use read_mbulk_header helper where possible [ca8b4c93] (Pavlo Yatsukhnenko) - * Allow to pass null as auth argument [41517753] (Pavlo Yatsukhnenko) - * Refactor redis_parse_client_list_response [68136a29, aaa4c91a, 1fb2935b, cf2c052c] (Pavlo Yatsukhnenko) - * Refactor subscribe/unsubscribe [3c9e159c] (Pavlo Yatsukhnenko) - * Change PHPREDIS_CTX_PTR type [de3635da] (Pavlo Yatsukhnenko) - * Refactor redis_parse_info_response [982bd13b] (Pavlo Yatsukhnenko) - * Allow IPv6 address within square brackets [c28ad7bb] (Pavlo Yatsukhnenko) - * Allow multiple field-value pairs for hmset command. [e858e8e3] (Pavlo Yatsukhnenko) - * Refactor MINIT and use @generate-class-entries in stub files [3675f442] (Remi Collet) - * Use spl_ce_RuntimeException [3cd5ac1e, a7e5ea64] (Remi Collet) - * Regenerate arginfo using 8.2.0 [a38e08da] (Remi Collet) - * Refactor client command [a8d10291] (Pavlo Yatsukhnenko) - * Pull COUNT/ANY parsing into a helper function [d67b2020] (Michael Grunder) - * Return false or NULL on empty lpos response [39a01ac7] (Michael Grunder) - * BLPOP with a float timeout [a98605f2, dc9af529] (Michael Grunder) - * Make sure we set an error for key based scans [98fda1b8] (Michael Grunder) - * Add back a default switch case for setoption handler [87464932] (Michael Grunder) - * Update stubs so the tests pass in strict mode [bebd398c] (Michael Grunder) - * Move where we generate our salt [d2044c9f] (Michael Grunder) - * Refactor XINFO handler [3b0d8b77] (Michael Grunder) - * Refactor and fix XPENDING handler [457953f4] (Michael Grunder) - * Refactor FLUSHDB and update docs. [54a084e5] (Michael Grunder) - * Add missing directed node command to docs and refactor stubs. [5ac92d25] (Michael Grunder) - * Refactor BITPOS and implement BIT/BYTE option. [4d8afd38] (Michael Grunder) - * INFO with multiple sections [44d03ca0] (Michael Grunder) - * Refactor SLOWLOG command [d87f1428] (Michael Grunder) - * Refactor SORT and add SORT_RO command [8c7c5a3a] (Michael Grunder) - * Use ZEND_STRL in redis_commands.c [78de25a3] (Pavlo Yatsukhnenko) - * Refactor PubSub command [2a0d1c1e] (Pavlo Yatsukhnenko) - * Refactor SLAVEOF handler [f2cef8be] (Michael Grunder) - * Refactor ACL command [504810a5] (Pavlo Yatsukhnenko) - * Use fast_zpp API [376d4d27] (Pavlo Yatsukhnenko) - * Fix XAUTOCLAIM response handler [0b7bd83f] (Michael Grunder) - * Refactor command command [ff863f3f] (Pavlo Yatsukhnenko) - * Refactor rawCommand and WAIT [79c9d224] (Michael Grunder) - * Refactor SELECT command [86f15cca] (Michael Grunder) - * Refactor SRANDMEMBER command. [f62363c2] (Michael Grunder) - * Refactor OBJECT command. [acb5db76] (Michael Grunder) - * Refactor gen_varkey_cmd [3efa59cb] (Michael Grunder) - * Refactor MGET command. [8cb6dd17] (Michael Grunder) - * Refactor INFO and SCRIPT commands. [3574ef08] (Michael Grunder) - * Refactor MSET and MSETNX commands. [6d104481] (Michael Grunder) - * Refactor HMSET command. [90eb0470] (Michael Grunder) - * Refactor PFCOUNT command. [19fd7e0c] (Michael Grunder) - * Refactor SMOVE command. [204a02c5] (Michael Grunder) - * Rework ZRANGE argument handling. [aa0938a4] (Michael Grunder) - * Refactor a couple more command methods. [5b560ccf, c8224b93, 40e1b1bf, ccd419a4] (Michael Grunder) - * Refactor HMGET command [bb66a547] (Michael Grunder) - * Refactor CLIENT command [77c4f7a3] (Pavlo Yatsukhnenko) - * Refactor redis_long_response [f14a80db] (Pavlo Yatsukhnenko) - * Synchronize Redis and RedisSentinel constructors [ebb2386e] (Pavlo Yatsukhnenko) - * Use redis_sock_connect on connect [f6c8b9c6] (Pavlo Yatsukhnenko) - * Auto-select db in redis_sock_server_open [6930a81c] (Pavlo Yatsukhnenko) - * Use on-stack allocated valiables [7a055cad] (Pavlo Yatsukhnenko) - * Add XAUTOCLAIM command [01f3342c] (Pavlo Yatsukhnenko) - * Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH [750b6cf3] (Pavlo Yatsukhnenko) - * Add reset command [947a2d38] (Pavlo Yatsukhnenko) - * Add hRandField command [fe397371] (Pavlo Yatsukhnenko) - * Add PXAT/EXAT arguments to SET command. [0a160685] (Pavlo Yatsukhnenko) - * Add GETEX, GETDEL commands. [11861d95] (Pavlo Yatsukhnenko) - * Add FAILOVER command. [4b767be7] (Pavlo Yatsukhnenko) - * Backoff settings in constructor [e6b3fe54] (Pavlo Yatsukhnenko) - * Add the COUNT argument to LPOP and RPOP [df97cc35] (Pavlo Yatsukhnenko) - * Unsubscribe from all channels [0f1ca0cc] (Pavlo Yatsukhnenko) - * Add lPos command. [687a5c78] (Pavlo Yatsukhnenko) - * Add the ANY argument to GEOSEARCH and GEORADIUS [bf6f31e3] (Pavlo Yatsukhnenko) - * Add 'BIT'/'BYTE' modifier to BITCOUNT + tests [a3d2f131] (Michael Grunder) - * Add missing configureoption entries in package.xml [59053f10] (Michele Locati) - * Implement CONFIG RESETSTAT [239678a0] (Michael Grunder) - * SINTERCARD and ZINTERCARD commands [64300508] (Michael Grunder) - * LCS command [c0e839f6] (Michael Grunder) - * EXPIRETIME and PEXPIRETIME [f5b2a09b] (Michael Grunder) - * [B]LMPOP and [B]ZMPOP commands [6ea978eb] (Michael Grunder) - * Implement new RESTORE options [9a3fe401] (Michael Grunder) - * Add new Redis 6.2.0 XTRIM options [6b34d17f] (Michael Grunder) - * Implement AUTH/AUTH2 arguments for MIGRATE [114d79d1] (Michael Grunder) - * Implement CONFIG REWRITE [525958ea] (Michael Grunder) - * Implement Redis 7.0.0 [P]EXPIRE[AT] [options 872ae107] (Michael Grunder) - * Variadic CONFIG GET/SET [36ef4bd8, a176f586] (Michael Grunder) - * EVAL_RO and EVALSHA_RO [f3a40830] (Michael Grunder) - * Implement ZRANGESTORE and add ZRANGE options [71bcbcb9] (Michael Grunder) - * XGROUP DELCONSUMER and ENTRIESREAD [1343f500] (Michael Grunder) - * Expose the transferred number of bytes [e0a88b7b, 90828019, 7a4cee2d] (Pavlo Yatsukhnenko), (Michael Grunder) - * TOUCH command [dc1f2398] (Michael Grunder) - * Redis Sentinel TLS support [f2bb2cdb] (Pavlo Yatsukhnenko) - * Add the CH, NX, XX arguments to GEOADD [2bb64038, e8f5b517] (Pavlo Yatsukhnenko) - * Implement SMISMEMBER for RedisCluster [abfac47b] (Michael Grunder) - * Implement ssubscribe/sunsubscribe [7644736e] (Pavlo Yatsukhnenko) - * Implement BLMOVE and add LMOVE/BLMOVE to cluster. [121e9d9c] (Michael Grunder) - * Implement LPOS for RedisCluster [7121aaae] (Michael Grunder) - * Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. [fa5d1af9] (Michael Grunder) - * Implement HRANDFIELD for RedisCluster [e222b85e] (Michael Grunder) - * Implement COPY for RedisCluster [40a2c254] (Michael Grunder) - * Implement new ZSET commands for cluster [27900f39] (Michael Grunder) - * Add cluster support for strict sessions and lazy write [b6cf6361] (Michael Grunder) - * Add function command [90a0e9cc] (Pavlo Yatsukhnenko) - * Add FCALL/FCALL_RO commands [7c46ad2c] (Pavlo Yatsukhnenko) - * Remove unused macroses [831d6118] (Pavlo Yatsukhnenko) + * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko), (Markus Podar) + * Fix unknown expiration modifier [264c0c7e, 95bd184b] (Pavlo Yatsukhnenko) + * Update documentation [3674d663, 849bedb6, 1ad95b63] (Till Kruss), (Joost OrangeJuiced) @@ -286,6 +157,40 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + stablestable + 6.0.16.0.1 + 2023-09-23 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net/ + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro/ + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Stackhero - https://github.com/stackhero-io + Florian Levis - https://github.com/Gounlaf + Luis Zarate - https://github.com/jlzaratec + + --- + + phpredis 6.0.1 + + This release contains fix for unknown expiration modifier issue + as well as memory leak and segfault in exec function + and small documentation improvements. + + You can find a detailed list of changes in CHANGELOG.md and package.xml + or by inspecting the git commit logs. + + * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko), (Markus Podar) + * Fix unknown expiration modifier [264c0c7e, 95bd184b] (Pavlo Yatsukhnenko) + * Update documentation [3674d663, 849bedb6, 1ad95b63] (Till Kruss), (Joost OrangeJuiced) + + stablestable 6.0.06.0.0 diff --git a/php_redis.h b/php_redis.h index 5944799ac4..3f03883d88 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.1-dev" +#define PHP_REDIS_VERSION "6.0.1" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ From 156e53e7209a4f5b72899a55ef947614b73d7580 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 14:37:51 +0300 Subject: [PATCH 1816/1986] Back to dev --- CHANGELOG.md | 12 +++++++++++- php_redis.h | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6494bbd504..d38a4e6db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + ## [6.0.1] - 2023-09-23 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.1), [PECL](https://pecl.php.net/package/redis/6.0.1)) ### Sponsors :sparkling_heart: @@ -22,7 +33,6 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - [Florian Levis](https://github.com/Gounlaf) - [Luis Zárate](https://github.com/jlzaratec) - ### Fixed - Fix memory leak and segfault in Redis::exec [362e1141](https://github.com/phpredis/phpredis/commit/362e1141) diff --git a/php_redis.h b/php_redis.h index 3f03883d88..c9dd839cfa 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.1" +#define PHP_REDIS_VERSION "6.0.2-dev" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ From f4c2ac26478740e492055b7934b45712d1c2280d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sat, 23 Sep 2023 14:42:14 +0300 Subject: [PATCH 1817/1986] Use actions/checkout@v4 --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/codeql.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 484a3a945a..8b113fb41b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Install PHP ${{ matrix.php }} @@ -85,7 +85,7 @@ jobs: php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Install PHP ${{ matrix.php }} @@ -116,7 +116,7 @@ jobs: ts: [nts, ts] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Install PHP ${{ matrix.php }} @@ -154,7 +154,7 @@ jobs: - name: Install required system packages run: apk add --update $PHPIZE_DEPS zstd-libs zstd-dev git - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Create temporary directory diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7b7c2cc310..228ef44957 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: From 2f276dcd375221b4a11fd67d044bad9ab32e1eab Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 25 Sep 2023 14:03:48 -0700 Subject: [PATCH 1818/1986] Find our callback by pattern with PSUBSCRIBE * Use the pattern Redis provides us not the channel, if this is a wildcard based `PSUBSCRIBE` payload. * Don't test whether our slots match in `SSUBSCRIBE` when not in cluster mode. Fixes #2395 --- library.c | 27 ++++++++++++++++----------- redis_commands.c | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/library.c b/library.c index 6405eee1f0..5749e96254 100644 --- a/library.c +++ b/library.c @@ -545,8 +545,9 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, /* Multibulk response, {[pattern], type, channel, payload } */ while (redis_sock->subs[i]) { zval z_ret, z_args[4], *z_type, *z_chan, *z_pat = NULL, *z_data; - HashTable *ht_tab; int tab_idx = 1, is_pmsg = 0; + HashTable *ht_tab; + zend_string *zs; ZVAL_NULL(&z_resp); if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp)) { @@ -573,22 +574,26 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, } // Extract pattern if it's a pmessage - if(is_pmsg) { - if ((z_pat = zend_hash_index_find(ht_tab, tab_idx++)) == NULL) { + if (is_pmsg) { + z_pat = zend_hash_index_find(ht_tab, tab_idx++); + if (z_pat == NULL || Z_TYPE_P(z_pat) != IS_STRING) goto failure; - } } - // Extract channel and data - if ((z_chan = zend_hash_index_find(ht_tab, tab_idx++)) == NULL || - (z_data = zend_hash_index_find(ht_tab, tab_idx++)) == NULL - ) { + /* Extract channel */ + z_chan = zend_hash_index_find(ht_tab, tab_idx++); + if (z_chan == NULL || Z_TYPE_P(z_chan) != IS_STRING) goto failure; - } - if ((cb = zend_hash_str_find_ptr(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) { + /* Finally, extract data */ + z_data = zend_hash_index_find(ht_tab, tab_idx++); + if (z_data == NULL) + goto failure; + + /* Find our callback, either by channel or pattern string */ + zs = z_pat != NULL ? Z_STR_P(z_pat) : Z_STR_P(z_chan); + if ((cb = zend_hash_find_ptr(redis_sock->subs[i], zs)) == NULL) goto failure; - } // Different args for SUBSCRIBE and PSUBSCRIBE z_args[0] = *getThis(); diff --git a/redis_commands.c b/redis_commands.c index 637f084a83..dd075801a3 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1580,7 +1580,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot ? &s2 : NULL); - if (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot) { + if (slot && (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot)) { php_error_docref(NULL, E_WARNING, "All shard channels needs to belong to a single slot"); smart_string_free(&cmdstr); efree(sctx); From 954fbab896fc4601ac9b6e1dee5cc6f14030a05e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 2 Oct 2023 15:16:34 +0300 Subject: [PATCH 1819/1986] Use newInstance in RedisClusterTest --- tests/RedisClusterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index cfc3e9c23e..e83ce0ee3b 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -723,7 +723,7 @@ public function testSlotCache() { $pong = 0; for ($i = 0; $i < 10; $i++) { - $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth()); + $obj_rc = $this->newInstance(); $pong += $obj_rc->ping("key:$i"); } @@ -739,7 +739,7 @@ public function testConnectionPool() { $pong = 0; for ($i = 0; $i < 10; $i++) { - $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth()); + $obj_rc = $this->newInstance(); $pong += $obj_rc->ping("key:$i"); } From a7f51f70ccbbd24758fb29c66ded0bbc5ea1f495 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Oct 2023 09:27:12 -0700 Subject: [PATCH 1820/1986] Fix flaky test and OBJECT in a pipeline. * We weren't properly passing `z_tab` through to the underlying OBJECT handler, which was causing PhpRedis to crash if you tried to execute the OBJECT command in a pipeline. * Rework the `testTouch` unit test to try and avoid erroneous failures due to CI instance CPU scheduling. --- library.c | 4 ++-- tests/RedisTest.php | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/library.c b/library.c index 5749e96254..c5f9820752 100644 --- a/library.c +++ b/library.c @@ -1519,9 +1519,9 @@ redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); if (ctx == PHPREDIS_CTX_PTR) { - return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 9f62d5f3b8..11ff00984c 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -983,13 +983,18 @@ public function testTouch() { $this->redis->del('notakey'); $this->assertTrue($this->redis->mset(['{idle}1' => 'beep', '{idle}2' => 'boop'])); - usleep(1100000); - $this->assertTrue($this->redis->object('idletime', '{idle}1') >= 1); - $this->assertTrue($this->redis->object('idletime', '{idle}2') >= 1); + usleep(2100000); + $this->assertTrue($this->redis->object('idletime', '{idle}1') >= 2); + $this->assertTrue($this->redis->object('idletime', '{idle}2') >= 2); $this->assertEquals(2, $this->redis->touch('{idle}1', '{idle}2', '{idle}notakey')); - $this->assertTrue($this->redis->object('idletime', '{idle}1') == 0); - $this->assertTrue($this->redis->object('idletime', '{idle}2') == 0); + $idle1 = $this->redis->object('idletime', '{idle}1'); + $idle2 = $this->redis->object('idletime', '{idle}2'); + + /* We're not testing if idle is 0 because CPU scheduling on GitHub CI + * potatoes can cause that to erroneously fail. */ + $this->assertTrue($idle1 < 2); + $this->assertTrue($idle2 < 2); } public function testKeys() From b835aaa3f995cd14880acc4f14bcd63691dfa0c1 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 13 Oct 2023 17:50:37 +0300 Subject: [PATCH 1821/1986] Fix deprecation error when passing null to match_type parameter --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 1e001f2097..1d36d76319 100644 --- a/redis.c +++ b/redis.c @@ -2765,7 +2765,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } else { // Doesn't require a key if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Oz/|s!lS", &object, redis_ce, &z_iter, + "Oz/|s!lS!", &object, redis_ce, &z_iter, &pattern, &pattern_len, &count, &match_type) == FAILURE) { From 7ed047870c5a0206bd081cd7727251c75239965b Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 22 Oct 2023 19:02:13 +0300 Subject: [PATCH 1822/1986] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d38a4e6db6..7d004fd071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,17 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - [Florian Levis](https://github.com/Gounlaf) - [Luis Zárate](https://github.com/jlzaratec) +### Fixed +- Fix deprecation error when passing null to match_type parameter. + [b835aaa3](https://github.com/phpredis/phpredis/commit/b835aaa3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix flaky test and OBJECT in a pipeline. + [a7f51f70](https://github.com/phpredis/phpredis/commit/a7f51f70) + ([Michael Grunder](https://github.com/michael-grunder)) +- Find our callback by pattern with PSUBSCRIBE + [2f276dcd](https://github.com/phpredis/phpredis/commit/2f276dcd) + ([Michael Grunder](https://github.com/michael-grunder)) + ## [6.0.1] - 2023-09-23 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.1), [PECL](https://pecl.php.net/package/redis/6.0.1)) ### Sponsors :sparkling_heart: From f404ecb8335767b32a214ba611629c8ec2f1d27f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 25 Sep 2023 14:03:48 -0700 Subject: [PATCH 1823/1986] Find our callback by pattern with PSUBSCRIBE * Use the pattern Redis provides us not the channel, if this is a wildcard based `PSUBSCRIBE` payload. * Don't test whether our slots match in `SSUBSCRIBE` when not in cluster mode. Fixes #2395 --- library.c | 27 ++++++++++++++++----------- redis_commands.c | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/library.c b/library.c index 6405eee1f0..5749e96254 100644 --- a/library.c +++ b/library.c @@ -545,8 +545,9 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, /* Multibulk response, {[pattern], type, channel, payload } */ while (redis_sock->subs[i]) { zval z_ret, z_args[4], *z_type, *z_chan, *z_pat = NULL, *z_data; - HashTable *ht_tab; int tab_idx = 1, is_pmsg = 0; + HashTable *ht_tab; + zend_string *zs; ZVAL_NULL(&z_resp); if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp)) { @@ -573,22 +574,26 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, } // Extract pattern if it's a pmessage - if(is_pmsg) { - if ((z_pat = zend_hash_index_find(ht_tab, tab_idx++)) == NULL) { + if (is_pmsg) { + z_pat = zend_hash_index_find(ht_tab, tab_idx++); + if (z_pat == NULL || Z_TYPE_P(z_pat) != IS_STRING) goto failure; - } } - // Extract channel and data - if ((z_chan = zend_hash_index_find(ht_tab, tab_idx++)) == NULL || - (z_data = zend_hash_index_find(ht_tab, tab_idx++)) == NULL - ) { + /* Extract channel */ + z_chan = zend_hash_index_find(ht_tab, tab_idx++); + if (z_chan == NULL || Z_TYPE_P(z_chan) != IS_STRING) goto failure; - } - if ((cb = zend_hash_str_find_ptr(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) { + /* Finally, extract data */ + z_data = zend_hash_index_find(ht_tab, tab_idx++); + if (z_data == NULL) + goto failure; + + /* Find our callback, either by channel or pattern string */ + zs = z_pat != NULL ? Z_STR_P(z_pat) : Z_STR_P(z_chan); + if ((cb = zend_hash_find_ptr(redis_sock->subs[i], zs)) == NULL) goto failure; - } // Different args for SUBSCRIBE and PSUBSCRIBE z_args[0] = *getThis(); diff --git a/redis_commands.c b/redis_commands.c index 637f084a83..dd075801a3 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1580,7 +1580,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot ? &s2 : NULL); - if (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot) { + if (slot && (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot)) { php_error_docref(NULL, E_WARNING, "All shard channels needs to belong to a single slot"); smart_string_free(&cmdstr); efree(sctx); From 3ec80ff09f8f8dc65c0f3347c79da32333a771f1 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 10 Oct 2023 09:27:12 -0700 Subject: [PATCH 1824/1986] Fix flaky test and OBJECT in a pipeline. * We weren't properly passing `z_tab` through to the underlying OBJECT handler, which was causing PhpRedis to crash if you tried to execute the OBJECT command in a pipeline. * Rework the `testTouch` unit test to try and avoid erroneous failures due to CI instance CPU scheduling. --- library.c | 4 ++-- tests/RedisTest.php | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/library.c b/library.c index 5749e96254..c5f9820752 100644 --- a/library.c +++ b/library.c @@ -1519,9 +1519,9 @@ redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); if (ctx == PHPREDIS_CTX_PTR) { - return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 20aabf8bcc..7f4ea91632 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -941,13 +941,18 @@ public function testTouch() { $this->redis->del('notakey'); $this->assertTrue($this->redis->mset(['{idle}1' => 'beep', '{idle}2' => 'boop'])); - usleep(1100000); - $this->assertTrue($this->redis->object('idletime', '{idle}1') >= 1); - $this->assertTrue($this->redis->object('idletime', '{idle}2') >= 1); + usleep(2100000); + $this->assertTrue($this->redis->object('idletime', '{idle}1') >= 2); + $this->assertTrue($this->redis->object('idletime', '{idle}2') >= 2); $this->assertEquals(2, $this->redis->touch('{idle}1', '{idle}2', '{idle}notakey')); - $this->assertTrue($this->redis->object('idletime', '{idle}1') == 0); - $this->assertTrue($this->redis->object('idletime', '{idle}2') == 0); + $idle1 = $this->redis->object('idletime', '{idle}1'); + $idle2 = $this->redis->object('idletime', '{idle}2'); + + /* We're not testing if idle is 0 because CPU scheduling on GitHub CI + * potatoes can cause that to erroneously fail. */ + $this->assertTrue($idle1 < 2); + $this->assertTrue($idle2 < 2); } public function testKeys() From abb3708c3dd9ac9bdc61ee72129335e552c979a5 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 13 Oct 2023 17:50:37 +0300 Subject: [PATCH 1825/1986] Fix deprecation error when passing null to match_type parameter --- redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.c b/redis.c index 1e001f2097..1d36d76319 100644 --- a/redis.c +++ b/redis.c @@ -2765,7 +2765,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } else { // Doesn't require a key if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Oz/|s!lS", &object, redis_ce, &z_iter, + "Oz/|s!lS!", &object, redis_ce, &z_iter, &pattern, &pattern_len, &count, &match_type) == FAILURE) { From c9e92365e08faf27be9334a0d9485c1e5e78264e Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 22 Oct 2023 19:02:13 +0300 Subject: [PATCH 1826/1986] Update CHANGELOG.md --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6494bbd504..aa1793b853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,30 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed +- Fix deprecation error when passing null to match_type parameter. + [b835aaa3](https://github.com/phpredis/phpredis/commit/b835aaa3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix flaky test and OBJECT in a pipeline. + [a7f51f70](https://github.com/phpredis/phpredis/commit/a7f51f70) + ([Michael Grunder](https://github.com/michael-grunder)) +- Find our callback by pattern with PSUBSCRIBE + [2f276dcd](https://github.com/phpredis/phpredis/commit/2f276dcd) + ([Michael Grunder](https://github.com/michael-grunder)) + ## [6.0.1] - 2023-09-23 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.1), [PECL](https://pecl.php.net/package/redis/6.0.1)) ### Sponsors :sparkling_heart: From 62cf943fecc5182c6329b332df43cf28012cef55 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 22 Oct 2023 19:16:41 +0300 Subject: [PATCH 1827/1986] 6.0.2 --- CHANGELOG.md | 2 ++ package.xml | 51 +++++++++++++++++++++++++++++++++++++++------------ php_redis.h | 2 +- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa1793b853..ddeba96937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [6.0.2] - 2023-10-22 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.2), [PECL](https://pecl.php.net/package/redis/6.0.2)) + ### Sponsors :sparkling_heart: - [Audiomack](https://audiomack.com) diff --git a/package.xml b/package.xml index 66a738a146..332dab572d 100644 --- a/package.xml +++ b/package.xml @@ -27,9 +27,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> n.favrefelix@gmail.com no - 2023-09-23 + 2023-10-22 - 6.0.1 + 6.0.2 6.0.0 @@ -53,19 +53,15 @@ http://pear.php.net/dtd/package-2.0.xsd"> --- - phpredis 6.0.1 - - This release contains fix for unknown expiration modifier issue - as well as memory leak and segfault in exec function - and small documentation improvements. + phpredis 6.0.2 + This release contains fixes for OBJECT, PSUBSCRIBE and SCAN commands. You can find a detailed list of changes in CHANGELOG.md and package.xml or by inspecting the git commit logs. - * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko), (Markus Podar) - * Fix unknown expiration modifier [264c0c7e, 95bd184b] (Pavlo Yatsukhnenko) - * Update documentation [3674d663, 849bedb6, 1ad95b63] (Till Kruss), (Joost OrangeJuiced) - + * Fix deprecation error when passing null to match_type parameter.[b835aaa3] (Pavlo Yatsukhnenko) + * Fix flaky test and OBJECT in a pipeline. [a7f51f70] (Michael Grunder) + * Find our callback by pattern with PSUBSCRIBE [2f276dcd] (Michael Grunder) @@ -159,7 +155,38 @@ http://pear.php.net/dtd/package-2.0.xsd"> stablestable - 6.0.16.0.1 + 6.0.26.0.0 + 2023-10-22 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Stackhero - https://github.com/stackhero-io + Florian Levis - https://github.com/Gounlaf + Luis Zarate - https://github.com/jlzaratec + + --- + + phpredis 6.0.2 + + This release contains fixes for OBJECT, PSUBSCRIBE and SCAN commands. + You can find a detailed list of changes in CHANGELOG.md and package.xml + or by inspecting the git commit logs. + + * Fix deprecation error when passing null to match_type parameter.[b835aaa3] (Pavlo Yatsukhnenko) + * Fix flaky test and OBJECT in a pipeline. [a7f51f70] (Michael Grunder) + * Find our callback by pattern with PSUBSCRIBE [2f276dcd] (Michael Grunder) + + + + stablestable + 6.0.16.0.0 2023-09-23 --- Sponsors --- diff --git a/php_redis.h b/php_redis.h index 3f03883d88..d9f4dda509 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.1" +#define PHP_REDIS_VERSION "6.0.2" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ From a0c8fcc589bfcf8207ba3d8c7d065109f31cc1c6 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Sun, 22 Oct 2023 21:12:44 +0300 Subject: [PATCH 1828/1986] Back to dev --- CHANGELOG.md | 13 +++++++++++++ php_redis.h | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a98a7e95b..1062938442 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + ## [6.0.2] - 2023-10-22 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.2), [PECL](https://pecl.php.net/package/redis/6.0.2)) ### Sponsors :sparkling_heart: diff --git a/php_redis.h b/php_redis.h index d9f4dda509..3375e418dc 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.2" +#define PHP_REDIS_VERSION "6.0.3-dev" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ From 9b5cad317bf50ba875350b6c68445ef34b5c0129 Mon Sep 17 00:00:00 2001 From: Git'Fellow <12234510+solracsf@users.noreply.github.com> Date: Wed, 25 Oct 2023 10:31:44 +0200 Subject: [PATCH 1829/1986] Fix anchor link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df18cd0305..3648ddf1e5 100644 --- a/README.md +++ b/README.md @@ -780,7 +780,7 @@ $redis->slowLog('len'); * [del, delete, unlink](#del-delete-unlink) - Delete a key * [dump](#dump) - Return a serialized version of the value stored at the specified key. * [exists](#exists) - Determine if a key exists -* [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds +* [expire, setTimeout, pexpire](#expire-pexpire) - Set a key's time to live in seconds * [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp * [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern * [scan](#scan) - Scan for keys in the keyspace (Redis >= 2.8.0) From 12966a7413e47cd1ef2ceed48846fc64179f58ad Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 27 Oct 2023 11:23:15 -0700 Subject: [PATCH 1830/1986] Update generated stubs --- redis_arginfo.h | 23 +++++------------------ redis_array_arginfo.h | 2 +- redis_array_legacy_arginfo.h | 2 +- redis_cluster_arginfo.h | 23 +++++++++-------------- redis_cluster_legacy_arginfo.h | 2 +- redis_legacy_arginfo.h | 2 +- redis_sentinel_arginfo.h | 2 +- redis_sentinel_legacy_arginfo.h | 2 +- 8 files changed, 20 insertions(+), 38 deletions(-) diff --git a/redis_arginfo.h b/redis_arginfo.h index 41f6aab06f..b1df8dce1c 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ + * Stub hash: aa85ec112e321335fe4577c0f939a32a69d4998e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -588,17 +588,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_object, 0, 2, Re ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_open, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 1, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_open arginfo_class_Redis_connect -#define arginfo_class_Redis_pconnect arginfo_class_Redis_open +#define arginfo_class_Redis_pconnect arginfo_class_Redis_connect ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_persist, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -633,7 +625,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_popen arginfo_class_Redis_open +#define arginfo_class_Redis_popen arginfo_class_Redis_connect ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_psetex, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -1155,12 +1147,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_zunion arginfo_class_Redis_zinter -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zunionstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "null") -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_zunionstore arginfo_class_Redis_zinterstore ZEND_METHOD(Redis, __construct); diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h index ea4d0721ef..51d700bd6a 100644 --- a/redis_array_arginfo.h +++ b/redis_array_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fb17c785beccf1dbeedaa48afb4aa7d48fd8b655 */ + * Stub hash: fa84ce2b68b10564dd8abaffecefe4dd5d65b591 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0) diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h index 1f2174ef0b..99b10cf1d3 100644 --- a/redis_array_legacy_arginfo.h +++ b/redis_array_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fb17c785beccf1dbeedaa48afb4aa7d48fd8b655 */ + * Stub hash: fa84ce2b68b10564dd8abaffecefe4dd5d65b591 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2) ZEND_ARG_INFO(0, function_name) diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index a853337d34..09bd412f71 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 32b24ce215ff4f2299dd838fab7cbc36b81b65eb */ + * Stub hash: 1f8038ea72ccc7fd8384d0eba4209702b20d77bd */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -140,7 +140,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_client, 0, 2, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_close arginfo_class_RedisCluster_clearlasterror @@ -231,13 +231,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expire, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expireat, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiretime, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -338,7 +338,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lcs, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getset, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL) @@ -560,7 +560,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_ping, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_psetex, 0, 3, RedisCluster, MAY_BE_BOOL) @@ -615,7 +615,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_restore, ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_role, 0, 1, IS_MIXED, 0) @@ -739,7 +739,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sort, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_sort @@ -1013,12 +1013,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmscore, ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zunionstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL") -ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_zunionstore arginfo_class_RedisCluster_zinterstore ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zinter, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 5b509cde6a..5f06be20a7 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 32b24ce215ff4f2299dd838fab7cbc36b81b65eb */ + * Stub hash: 1f8038ea72ccc7fd8384d0eba4209702b20d77bd */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 0837a1f01f..5645bb6025 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ + * Stub hash: aa85ec112e321335fe4577c0f939a32a69d4998e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h index 7ef1ae42be..98762fce6a 100644 --- a/redis_sentinel_arginfo.h +++ b/redis_sentinel_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f1f746cc848b1debcdf88eae015732720ba206c8 */ + * Stub hash: ca40579af888c5bb0661cd0201d840297474479a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h index 5f7a70d26d..c7582d6e01 100644 --- a/redis_sentinel_legacy_arginfo.h +++ b/redis_sentinel_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f1f746cc848b1debcdf88eae015732720ba206c8 */ + * Stub hash: ca40579af888c5bb0661cd0201d840297474479a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From df074dbe9eab9a634ba1b2478e610596175e82ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C5=82adzimir=20Tsykun?= Date: Sun, 1 Oct 2023 14:17:32 +0200 Subject: [PATCH 1831/1986] the VALUE argument type for hSetNx must be the same as for hSet --- redis.stub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.stub.php b/redis.stub.php index 5ba06f3e6a..b9c3fe51b6 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1802,7 +1802,7 @@ public function hSet(string $key, string $member, mixed $value): Redis|int|false * $redis->hsetnx('player:1', 'lock', 'enabled'); * $redis->hsetnx('player:1', 'lock', 'enabled'); */ - public function hSetNx(string $key, string $field, string $value): Redis|bool; + public function hSetNx(string $key, string $field, mixed $value): Redis|bool; /** * Get the string length of a hash field From ff305349dba87ab857a8f28acbc3b22af5a271cc Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 30 Oct 2023 09:43:40 -0700 Subject: [PATCH 1832/1986] Update generated stubs See #2398 --- redis_arginfo.h | 4 ++-- redis_legacy_arginfo.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/redis_arginfo.h b/redis_arginfo.h index b1df8dce1c..2cb506aa28 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: aa85ec112e321335fe4577c0f939a32a69d4998e */ + * Stub hash: f98761a9bf8bfd22f34609b4d7c0c26f69248668 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -440,7 +440,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSetNx, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hStrLen, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 5645bb6025..ee664afde0 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: aa85ec112e321335fe4577c0f939a32a69d4998e */ + * Stub hash: f98761a9bf8bfd22f34609b4d7c0c26f69248668 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From 78d15140fadde60784d75e1cdc63feff6b8fc27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Kel=C4=8D=C3=A1k?= Date: Sat, 2 Dec 2023 12:36:44 +0100 Subject: [PATCH 1833/1986] Add PHP 8.3 to CI Also Windows setup-php-sdk action was moved to the official repo where is maintained again. --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b113fb41b..c212906e18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] steps: - name: Checkout uses: actions/checkout@v4 @@ -82,7 +82,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] steps: - name: Checkout uses: actions/checkout@v4 @@ -112,7 +112,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] ts: [nts, ts] steps: - name: Checkout @@ -120,7 +120,7 @@ jobs: with: submodules: true - name: Install PHP ${{ matrix.php }} - uses: cmb69/setup-php-sdk@v0.6 + uses: php/setup-php-sdk@v0.8 id: setup-php-sdk with: version: ${{ matrix.php }} @@ -149,7 +149,7 @@ jobs: pecl: runs-on: ubuntu-latest - container: php:8.2-cli-alpine + container: php:8.3-cli-alpine steps: - name: Install required system packages run: apk add --update $PHPIZE_DEPS zstd-libs zstd-dev git From e051a5db3e53933daeb845b6df4bccf252a3ae9d Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Tue, 19 Dec 2023 15:55:02 +0200 Subject: [PATCH 1834/1986] PHP 8.3 is now released. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b113fb41b..f75642c131 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] steps: - name: Checkout uses: actions/checkout@v4 @@ -82,7 +82,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] steps: - name: Checkout uses: actions/checkout@v4 @@ -149,7 +149,7 @@ jobs: pecl: runs-on: ubuntu-latest - container: php:8.2-cli-alpine + container: php:8.3-cli-alpine steps: - name: Install required system packages run: apk add --update $PHPIZE_DEPS zstd-libs zstd-dev git From 9f8f80ca9dbee6206058d033d5c27b0cec3ae6f3 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Fri, 8 Dec 2023 17:01:03 +0200 Subject: [PATCH 1835/1986] sessionSaveHandler --- tests/RedisTest.php | 2 +- tests/startSession.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 11ff00984c..46568ef3eb 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7817,7 +7817,7 @@ private function setSessionHandler() { $host = $this->getHost() ?: 'localhost'; - @ini_set('session.save_handler', 'redis'); + @ini_set('session.save_handler', $this->sessionSaveHandler); @ini_set('session.save_path', 'tcp://' . $host . ':6379'); } diff --git a/tests/startSession.php b/tests/startSession.php index f82c17e216..34af31ae9e 100644 --- a/tests/startSession.php +++ b/tests/startSession.php @@ -18,16 +18,16 @@ ini_set('session.save_handler', $saveHandler); ini_set('session.save_path', $redisHost); ini_set('max_execution_time', $maxExecutionTime); -ini_set('redis.session.lock_retries', $lock_retries); -ini_set('redis.session.lock_expire', $lock_expire); +ini_set("{$saveHandler}.session.lock_retries", $lock_retries); +ini_set("{$saveHandler}.session.lock_expire", $lock_expire); ini_set('session.gc_maxlifetime', $sessionLifetime); if (isset($argv[10])) { - ini_set('redis.session.locking_enabled', $argv[10]); + ini_set("{$saveHandler}.session.locking_enabled", $argv[10]); } if (isset($argv[11])) { - ini_set('redis.session.lock_wait_time', $argv[11]); + ini_set("{$saveHandler}.session.lock_wait_time", $argv[11]); } session_id($sessionId); From 6dc0a0be8de9145660c27b26d42c71b52ff52945 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 8 Jan 2024 10:52:12 -0800 Subject: [PATCH 1836/1986] Fix segfault when passing just false to auth. Fixes #2430 --- library.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/library.c b/library.c index c5f9820752..a276a73dd2 100644 --- a/library.c +++ b/library.c @@ -4358,21 +4358,23 @@ redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_ return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, 0, z_tab, ctx); } +/* The user may wish to send us something like [NULL, 'password'] or + * [false, 'password'] so don't convert NULL or FALSE into "". */ +static int redisTrySetAuthArg(zend_string **dst, zval *zsrc) { + if (Z_TYPE_P(zsrc) == IS_NULL || Z_TYPE_P(zsrc) == IS_FALSE) + return FAILURE; + + *dst = zval_get_string(zsrc); + + return SUCCESS; +} + PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) { zval *zv; HashTable *ht; int num; - /* The user may wish to send us something like [NULL, 'password'] or - * [false, 'password'] so don't convert NULL or FALSE into "". */ - #define TRY_SET_AUTH_ARG(zv, ppzstr) \ - do { \ - if (Z_TYPE_P(zv) != IS_NULL && Z_TYPE_P(zv) != IS_FALSE) { \ - *(ppzstr) = zval_get_string(zv); \ - } \ - } while (0) - /* Null out user and password */ *user = *pass = NULL; @@ -4382,8 +4384,7 @@ int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) /* Handle a non-array first */ if (Z_TYPE_P(ztest) != IS_ARRAY) { - TRY_SET_AUTH_ARG(ztest, pass); - return SUCCESS; + return redisTrySetAuthArg(pass, ztest); } /* Handle the array case */ @@ -4400,18 +4401,18 @@ int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "user")) || (zv = zend_hash_index_find(ht, 0))) { - TRY_SET_AUTH_ARG(zv, user); + redisTrySetAuthArg(user, zv); } if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "pass")) || (zv = zend_hash_index_find(ht, 1))) { - TRY_SET_AUTH_ARG(zv, pass); + redisTrySetAuthArg(pass, zv); } } else if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "pass")) || (zv = zend_hash_index_find(ht, 0))) { - TRY_SET_AUTH_ARG(zv, pass); + redisTrySetAuthArg(pass, zv); } /* If we at least have a password, we're good */ From 3fdd52b42d4768b97fdfdf314aea635d9ff5bbdd Mon Sep 17 00:00:00 2001 From: woodong Date: Tue, 16 Jan 2024 15:47:22 +0800 Subject: [PATCH 1837/1986] Fix the time unit of retry_interval --- library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.c b/library.c index a276a73dd2..77eecaa14d 100644 --- a/library.c +++ b/library.c @@ -2859,7 +2859,7 @@ redis_sock_create(char *host, int host_len, int port, redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->retry_interval = retry_interval * 1000; redis_sock->max_retries = 10; - redis_initialize_backoff(&redis_sock->backoff, retry_interval); + redis_initialize_backoff(&redis_sock->backoff, redis_sock->retry_interval); redis_sock->persistent = persistent; if (persistent && persistent_id != NULL) { From 14f93339c0220fd559ec043019d0146dd1d84ee2 Mon Sep 17 00:00:00 2001 From: Alexandre Choura Date: Tue, 16 Jan 2024 11:53:58 +0100 Subject: [PATCH 1838/1986] fix: RedisCluster::publish returns a cluster_long_resp --- redis_cluster.stub.php | 2 +- redis_cluster_arginfo.h | 4 ++-- redis_cluster_legacy_arginfo.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index f5508afdfe..600638e5f8 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -704,7 +704,7 @@ public function pttl(string $key): RedisCluster|int|false; /** * @see Redis::publish */ - public function publish(string $channel, string $message): RedisCluster|bool; + public function publish(string $channel, string $message): RedisCluster|bool|int; /** * @see Redis::pubsub diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 09bd412f71..eafffcb57d 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1f8038ea72ccc7fd8384d0eba4209702b20d77bd */ + * Stub hash: d832720b86414896922f919bcd559fe82426c7a6 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -576,7 +576,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_pttl arginfo_class_RedisCluster_expiretime -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_publish, 0, 2, RedisCluster, MAY_BE_BOOL) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_publish, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 5f06be20a7..3f554b18dc 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1f8038ea72ccc7fd8384d0eba4209702b20d77bd */ + * Stub hash: d832720b86414896922f919bcd559fe82426c7a6 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) From a4a283ab50a2a5ceac63cc8cc3225f2f6ba1d8e4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 30 Oct 2023 09:43:40 -0700 Subject: [PATCH 1839/1986] Change exec return method type hint --- redis_array.stub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis_array.stub.php b/redis_array.stub.php index c84d636a9c..b863338b24 100644 --- a/redis_array.stub.php +++ b/redis_array.stub.php @@ -32,7 +32,7 @@ public function del(string|array $key, string ...$otherkeys): bool|int; public function discard(): bool|null; - public function exec(): bool|null; + public function exec(): bool|null|array; public function flushall(): bool|array; From 5d293245cdd55cc16b8735a788f06d4cb98bf65c Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 23 Nov 2023 17:05:47 +0200 Subject: [PATCH 1840/1986] Fix Redis::mget signature --- redis.stub.php | 4 ++-- redis_arginfo.h | 4 ++-- redis_legacy_arginfo.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index b9c3fe51b6..46c170b838 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -2171,11 +2171,11 @@ public function ltrim(string $key, int $start , int $end): Redis|bool; * Get one ore more string keys. * * @param array $keys The keys to retrieve - * @return Redis|array an array of keys with their values. + * @return Redis|array|false an array of keys with their values. * * @example $redis->mget(['key1', 'key2']); */ - public function mget(array $keys): Redis|array; + public function mget(array $keys): Redis|array|false; public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, diff --git a/redis_arginfo.h b/redis_arginfo.h index 2cb506aa28..ad136438e5 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f98761a9bf8bfd22f34609b4d7c0c26f69248668 */ + * Stub hash: 6afb67851068637b92e885e8a16ca6818061ed6e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -553,7 +553,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ltrim, 0, 3, Red ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mget, 0, 1, Redis, MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mget, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index ee664afde0..16eb198309 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f98761a9bf8bfd22f34609b4d7c0c26f69248668 */ + * Stub hash: 6afb67851068637b92e885e8a16ca6818061ed6e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From 8f8ff72a79639890d3208945a380f4973fb2620b Mon Sep 17 00:00:00 2001 From: Takayasu Oyama Date: Thu, 25 Jan 2024 05:46:50 +0900 Subject: [PATCH 1841/1986] Update zCount argument type in redis.stub.php (#2439) * Update zCount argument type in redis.stub.php zCount's min/max can also be an integer. * fix arginfo --- redis.stub.php | 6 +++--- redis_arginfo.h | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 46c170b838..0c03a43df9 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -4026,8 +4026,8 @@ public function zCard(string $key): Redis|int|false; * Count the number of members in a sorted set with scores inside a provided range. * * @param string $key The sorted set to check. - * @param string $min The minimum score to include in the count - * @param string $max The maximum score to include in the count + * @param int|string $min The minimum score to include in the count + * @param int|string $max The maximum score to include in the count * * NOTE: In addition to a floating point score you may pass the special values of '-inf' and * '+inf' meaning negative and positive infinity, respectively. @@ -4038,7 +4038,7 @@ public function zCard(string $key): Redis|int|false; * @example $redis->zCount('fruit-rankings', 50, 60); * @example $redis->zCount('fruit-rankings', '-inf', 0); */ - public function zCount(string $key, string $start, string $end): Redis|int|false; + public function zCount(string $key, int|string $start, int|string $end): Redis|int|false; /** * Create or increment the score of a member in a Redis sorted set diff --git a/redis_arginfo.h b/redis_arginfo.h index ad136438e5..928158e009 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6afb67851068637b92e885e8a16ca6818061ed6e */ + * Stub hash: 3b2ecc525884fc1ae2a71b8e053fa245b108c4bb */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -1002,8 +1002,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zCount, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) + ZEND_ARG_TYPE_MASK(0, start, MAY_BE_LONG|MAY_BE_STRING, NULL) + ZEND_ARG_TYPE_MASK(0, end, MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zIncrBy, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE) @@ -1082,7 +1082,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRemRangeByRank, ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_zCount +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRemRangeByScore, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) From 142c1f4a9327d27f2607b5b968a8679f652faa57 Mon Sep 17 00:00:00 2001 From: SplotyCode <31861387+SplotyCode@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:11:29 +0100 Subject: [PATCH 1842/1986] Fix retry_internal documentation --- arrays.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrays.md b/arrays.md index c084f70f29..3ca9b7d2b9 100644 --- a/arrays.md +++ b/arrays.md @@ -40,7 +40,7 @@ $ra = new RedisArray(array("host1", "host2", "host3"), array("previous" => array #### Specifying the "retry_interval" parameter The retry_interval is used to specify a delay in milliseconds between reconnection attempts in case the client loses connection with a server
    -$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_timeout" => 100));
    +$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_interval" => 100));
     
    #### Specifying the "lazy_connect" parameter From ed7c9f6f63b6dc6805caba2aab860152a320b03b Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 13 Feb 2024 15:40:15 -0800 Subject: [PATCH 1843/1986] Implement WAITAOF command. --- redis.c | 4 +++ redis.stub.php | 7 +++++ redis_arginfo.h | 10 +++++++- redis_array_arginfo.h | 5 ++-- redis_array_legacy_arginfo.h | 2 +- redis_cluster.c | 47 ++++++++++++++++++++++++++++++++++ redis_cluster.stub.php | 3 +++ redis_cluster_arginfo.h | 11 +++++++- redis_cluster_legacy_arginfo.h | 11 +++++++- redis_commands.c | 30 +++++++++++++++++++++- redis_commands.h | 3 +++ redis_legacy_arginfo.h | 10 +++++++- tests/RedisClusterTest.php | 4 +++ tests/RedisTest.php | 11 ++++++++ 14 files changed, 150 insertions(+), 8 deletions(-) diff --git a/redis.c b/redis.c index 1d36d76319..d7319f2ff0 100644 --- a/redis.c +++ b/redis.c @@ -2178,6 +2178,10 @@ PHP_METHOD(Redis, sunsubscribe) redis_unsubscribe_response); } +PHP_METHOD(Redis, waitaof) { + REDIS_PROCESS_CMD(waitaof, redis_read_variant_reply); +} + /* {{{ proto string Redis::bgrewriteaof() */ PHP_METHOD(Redis, bgrewriteaof) { diff --git a/redis.stub.php b/redis.stub.php index 0c03a43df9..0a4332e453 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -611,6 +611,13 @@ public function bgSave(): Redis|bool; */ public function bgrewriteaof(): Redis|bool; + /** + * @see https://redis.io/commands/waitaof + * + * @return Redis|array + */ + public function waitaof(int $numlocal, int $numreplicas, int $timeout): Redis|array|false; + /** * Count the number of set bits in a Redis string. * diff --git a/redis_arginfo.h b/redis_arginfo.h index 928158e009..c6cc803767 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3b2ecc525884fc1ae2a71b8e053fa245b108c4bb */ + * Stub hash: de2f6e77cadba00b1f8312a8244db9df00a74a85 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -49,6 +49,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis_bgSave +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_waitaof, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, numlocal, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, numreplicas, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitcount, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0") @@ -1168,6 +1174,7 @@ ZEND_METHOD(Redis, append); ZEND_METHOD(Redis, auth); ZEND_METHOD(Redis, bgSave); ZEND_METHOD(Redis, bgrewriteaof); +ZEND_METHOD(Redis, waitaof); ZEND_METHOD(Redis, bitcount); ZEND_METHOD(Redis, bitop); ZEND_METHOD(Redis, bitpos); @@ -1422,6 +1429,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgSave, arginfo_class_Redis_bgSave, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgrewriteaof, arginfo_class_Redis_bgrewriteaof, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, waitaof, arginfo_class_Redis_waitaof, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC) diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h index 51d700bd6a..06064ecda4 100644 --- a/redis_array_arginfo.h +++ b/redis_array_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fa84ce2b68b10564dd8abaffecefe4dd5d65b591 */ + * Stub hash: 59943eeb14b3ed78f88117e6923d64a95911b5ff */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0) @@ -44,7 +44,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray_discard, 0, 0, _IS_BOOL, 1) ZEND_END_ARG_INFO() -#define arginfo_class_RedisArray_exec arginfo_class_RedisArray_discard +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_exec, 0, 0, MAY_BE_BOOL|MAY_BE_NULL|MAY_BE_ARRAY) +ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_flushall arginfo_class_RedisArray__continuum diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h index 99b10cf1d3..cde854f50b 100644 --- a/redis_array_legacy_arginfo.h +++ b/redis_array_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fa84ce2b68b10564dd8abaffecefe4dd5d65b591 */ + * Stub hash: 59943eeb14b3ed78f88117e6923d64a95911b5ff */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2) ZEND_ARG_INFO(0, function_name) diff --git a/redis_cluster.c b/redis_cluster.c index ae7e2d2e73..a492c2c595 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2876,6 +2876,53 @@ PHP_METHOD(RedisCluster, randomkey) { } /* }}} */ +PHP_METHOD(RedisCluster, waitaof) { + zend_long numlocal, numreplicas, timeout; + redisCluster *c = GET_CONTEXT(); + smart_string cmdstr = {0}; + void *ctx = NULL; + short slot; + zval *node; + + ZEND_PARSE_PARAMETERS_START(4, 4) + Z_PARAM_ZVAL(node) + Z_PARAM_LONG(numlocal) + Z_PARAM_LONG(numreplicas) + Z_PARAM_LONG(timeout) + ZEND_PARSE_PARAMETERS_END(); + + if (numlocal < 0 || numreplicas < 0 || timeout < 0) { + php_error_docref(NULL, E_WARNING, "No arguments can be negative"); + RETURN_FALSE; + } + + slot = cluster_cmd_get_slot(c, node); + if (slot < 0) { + RETURN_FALSE; + } + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "WAITAOF"); + redis_cmd_append_sstr_long(&cmdstr, numlocal); + redis_cmd_append_sstr_long(&cmdstr, numreplicas); + redis_cmd_append_sstr_long(&cmdstr, timeout); + + c->readonly = 0; + + if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, TYPE_MULTIBULK) < 0) { + CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0); + smart_string_free(&cmdstr); + RETURN_FALSE; + } + + if (CLUSTER_IS_ATOMIC(c)) { + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); + } + + smart_string_free(&cmdstr); +} + /* {{{ proto bool RedisCluster::ping(string key| string msg) * proto bool RedisCluster::ping(array host_port| string msg) */ PHP_METHOD(RedisCluster, ping) { diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 600638e5f8..408f876046 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -103,6 +103,9 @@ public function append(string $key, mixed $value): RedisCluster|bool|int; */ public function bgrewriteaof(string|array $key_or_address): RedisCluster|bool; + public function waitaof(string|array $key_or_address, int $numlocal, + int $numreplicas, int $timeout): RedisCluster|array|false; + /** * @see Redis::bgsave */ diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index eafffcb57d..839595c527 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d832720b86414896922f919bcd559fe82426c7a6 */ + * Stub hash: 35b71fe87bbd8df3a7495e14be957b18c3241a19 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -56,6 +56,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bgrewrite ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_waitaof, 0, 4, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) + ZEND_ARG_TYPE_INFO(0, numlocal, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, numreplicas, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_bgsave arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitcount, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) @@ -1047,6 +1054,7 @@ ZEND_METHOD(RedisCluster, _redir); ZEND_METHOD(RedisCluster, acl); ZEND_METHOD(RedisCluster, append); ZEND_METHOD(RedisCluster, bgrewriteaof); +ZEND_METHOD(RedisCluster, waitaof); ZEND_METHOD(RedisCluster, bgsave); ZEND_METHOD(RedisCluster, bitcount); ZEND_METHOD(RedisCluster, bitop); @@ -1272,6 +1280,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgrewriteaof, arginfo_class_RedisCluster_bgrewriteaof, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, waitaof, arginfo_class_RedisCluster_waitaof, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgsave, arginfo_class_RedisCluster_bgsave, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitcount, arginfo_class_RedisCluster_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitop, arginfo_class_RedisCluster_bitop, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 3f554b18dc..1cc6b7cda5 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d832720b86414896922f919bcd559fe82426c7a6 */ + * Stub hash: 35b71fe87bbd8df3a7495e14be957b18c3241a19 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -49,6 +49,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bgrewriteaof, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_waitaof, 0, 0, 4) + ZEND_ARG_INFO(0, key_or_address) + ZEND_ARG_INFO(0, numlocal) + ZEND_ARG_INFO(0, numreplicas) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_bgsave arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitcount, 0, 0, 1) @@ -892,6 +899,7 @@ ZEND_METHOD(RedisCluster, _redir); ZEND_METHOD(RedisCluster, acl); ZEND_METHOD(RedisCluster, append); ZEND_METHOD(RedisCluster, bgrewriteaof); +ZEND_METHOD(RedisCluster, waitaof); ZEND_METHOD(RedisCluster, bgsave); ZEND_METHOD(RedisCluster, bitcount); ZEND_METHOD(RedisCluster, bitop); @@ -1117,6 +1125,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgrewriteaof, arginfo_class_RedisCluster_bgrewriteaof, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, waitaof, arginfo_class_RedisCluster_waitaof, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgsave, arginfo_class_RedisCluster_bgsave, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitcount, arginfo_class_RedisCluster_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitop, arginfo_class_RedisCluster_bitop, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index dd075801a3..6fbd684a80 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1045,7 +1045,7 @@ redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ARRAY_HT(args) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - redis_cmd_init_sstr(&cmdstr, 2 + (keys ? zend_hash_num_elements(keys) : 0) + + redis_cmd_init_sstr(&cmdstr, 2 + (keys ? zend_hash_num_elements(keys) : 0) + (args ? zend_hash_num_elements(args) : 0), kw, strlen(kw)); redis_cmd_append_sstr_zstr(&cmdstr, fn); redis_cmd_append_sstr_long(&cmdstr, keys ? zend_hash_num_elements(keys) : 0); @@ -2231,6 +2231,34 @@ redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int redis_waitaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zend_long numlocal, numreplicas, timeout; + smart_string cmdstr = {0}; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(numlocal) + Z_PARAM_LONG(numreplicas) + Z_PARAM_LONG(timeout) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (numlocal < 0 || numreplicas < 0 || timeout < 0) { + php_error_docref(NULL, E_WARNING, "No arguments can be negative"); + return FAILURE; + } + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "WAITAOF"); + redis_cmd_append_sstr_long(&cmdstr, numlocal); + redis_cmd_append_sstr_long(&cmdstr, numreplicas); + redis_cmd_append_sstr_long(&cmdstr, timeout); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* Attempt to pull a long expiry from a zval. We're more restrictave than zval_get_long * because that function will return integers from things like open file descriptors * which should simply fail as a TTL */ diff --git a/redis_commands.h b/redis_commands.h index 3165ba0eab..dfaa8fd0d2 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -191,6 +191,9 @@ int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock * specific processing we do (e.g. verifying subarguments) that make them * unique */ +int redis_waitaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 16eb198309..c5506b83e1 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6afb67851068637b92e885e8a16ca6818061ed6e */ + * Stub hash: de2f6e77cadba00b1f8312a8244db9df00a74a85 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -44,6 +44,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis___destruct +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_waitaof, 0, 0, 3) + ZEND_ARG_INFO(0, numlocal) + ZEND_ARG_INFO(0, numreplicas) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) @@ -1019,6 +1025,7 @@ ZEND_METHOD(Redis, append); ZEND_METHOD(Redis, auth); ZEND_METHOD(Redis, bgSave); ZEND_METHOD(Redis, bgrewriteaof); +ZEND_METHOD(Redis, waitaof); ZEND_METHOD(Redis, bitcount); ZEND_METHOD(Redis, bitop); ZEND_METHOD(Redis, bitpos); @@ -1273,6 +1280,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgSave, arginfo_class_Redis_bgSave, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgrewriteaof, arginfo_class_Redis_bgrewriteaof, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, waitaof, arginfo_class_Redis_waitaof, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index e83ce0ee3b..e482345d24 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -778,5 +778,9 @@ public function testNullArray() { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } + + protected function execWaitAOF() { + return $this->redis->waitaof(uniqid(), 0, 0, 0); + } } ?> diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 46568ef3eb..fb0db3e588 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7673,6 +7673,17 @@ public function testFunction() { $this->assertTrue($this->redis->function('delete', 'mylib')); } + protected function execWaitAOF() { + return $this->redis->waitaof(0, 0, 0); + } + + public function testWaitAOF() { + $res = $this->execWaitAOF(); + $this->assertTrue(is_array($res) && count($res) == 2 && + isset($res[0]) && is_int($res[0]) && + isset($res[1]) && is_int($res[1])); + } + /* Make sure we handle a bad option value gracefully */ public function testBadOptionValue() { $this->assertFalse(@$this->redis->setOption(pow(2, 32), false)); From 9b90c03bd0bb671cb43e6f9d38a717c4cd03fba3 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 20 Feb 2024 11:28:06 -0800 Subject: [PATCH 1844/1986] Update WAITAOF test to use different assertion + add debug info * Add what value failed to pass our callback assertion so we can see what we actually got from the server. * WAITAOF requires Redis >= 7.2.0 so don't run it if the server is older than that. --- tests/RedisTest.php | 12 +++++++++--- tests/TestSuite.php | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index fb0db3e588..fca58c8c07 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7678,10 +7678,16 @@ protected function execWaitAOF() { } public function testWaitAOF() { + if (!$this->minVersionCheck("7.2.0")) + $this->markTestSkipped(); + $res = $this->execWaitAOF(); - $this->assertTrue(is_array($res) && count($res) == 2 && - isset($res[0]) && is_int($res[0]) && - isset($res[1]) && is_int($res[1])); + $this->assertValidate($res, function ($v) { + if ( ! is_array($v) || count($v) != 2) + return false; + return isset($v[0]) && is_int($v[0]) && + isset($v[1]) && is_int($v[1]); + }); } /* Make sure we handle a bad option value gracefully */ diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 529a08cb38..1c4663bb0e 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -140,8 +140,8 @@ protected function assertValidate($val, $cb) { return true; $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n--- VALUE ---\n%s\n", + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], print_r($val, true)); return false; } From 37c5f8d451d1fb17e82f7547de59905daf13d9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 17 Feb 2024 11:09:29 +0000 Subject: [PATCH 1845/1986] Fix typos --- CHANGELOG.md | 18 +++++------ README.md | 4 +-- cluster_library.c | 8 ++--- cluster_library.h | 2 +- docs/Redis.html | 64 +++++++++++++++++++------------------- docs/doc-index.html | 14 ++++----- docs/doctum.js | 4 +-- library.c | 4 +-- package.xml | 26 ++++++++-------- redis.stub.php | 52 +++++++++++++++---------------- redis_cluster.c | 4 +-- redis_commands.c | 2 +- redis_session.c | 2 +- tests/RedisArrayTest.php | 2 +- tests/RedisClusterTest.php | 2 +- tests/RedisTest.php | 12 +++---- 16 files changed, 110 insertions(+), 110 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1062938442..8feb1cea0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -597,7 +597,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Removed -- Remove unused macroses +- Remove unused macros [831d6118](https://github.com/phpredis/phpredis/commit/831d6118) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) @@ -1495,7 +1495,7 @@ serializers, soft deprecation of non-Redis commands. ## [4.3.0] - 2019-03-13 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/4.3.0), [PECL](https://pecl.php.net/package/redis/4.3.0)) -This is probably the last release with PHP 5 suport!!! +This is probably the last release with PHP 5 support!!! ### Added @@ -1566,7 +1566,7 @@ The main feature of this release is new Streams API implemented by ### Changed - Optimize close method [2a1ef961](https://www.github.com/phpredis/phpredis/commit/2a1ef961) ([yulonghu](https://github.com/yulonghu)) -- Use a ZSET insted of SET for EVAL tests [2e412373](https://www.github.com/phpredis/phpredis/commit/2e412373) ([Michael Grunder](https://github.com/michael-grunder)) +- Use a ZSET instead of SET for EVAL tests [2e412373](https://www.github.com/phpredis/phpredis/commit/2e412373) ([Michael Grunder](https://github.com/michael-grunder)) - Modify session testing logic [bfd27471](https://www.github.com/phpredis/phpredis/commit/bfd27471) ([Michael Grunder](https://github.com/michael-grunder)) - Documentation improvements ([@michael-grunder](https://github.com/michael-grunder), [@elcheco](https://github.com/elcheco), [@lucascourot](https://github.com/lucascourot), [@nolimitdev](https://github.com/nolimitdev), [Michael Grunder](https://github.com/michael-grunder)) @@ -1618,7 +1618,7 @@ The main feature of this release is new Streams API implemented by - Add tcp_keepalive option to redis sock [68c58513](https://www.github.com/phpredis/phpredis/commit/68c58513), [5101172a](https://www.github.com/phpredis/phpredis/commit/5101172a), [010336d5](https://www.github.com/phpredis/phpredis/commit/010336d5), [51e48729](https://www.github.com/phpredis/phpredis/commit/51e48729) ([@git-hulk](https://github.com/git-hulk), [Michael Grunder](https://github.com/michael-grunder)) - More robust GEORADIUS COUNT validation [f7edee5d](https://www.github.com/phpredis/phpredis/commit/f7edee5d) ([Michael Grunder](https://github.com/michael-grunder)) -- Allow to use empty string as persistant_id [ec4fd1bd](https://www.github.com/phpredis/phpredis/commit/ec4fd1bd) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow to use empty string as persistent_id [ec4fd1bd](https://www.github.com/phpredis/phpredis/commit/ec4fd1bd) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Documentation improvements ([Michael Grunder](https://github.com/michael-grunder), [@TomA-R](https://github.com/TomA-R)) ### Fixed @@ -1641,7 +1641,7 @@ This is interim release which contains only bug fixes. - Fix segfault when extending Redis class in PHP 5 [d23eff](https://www.github.com/phpredis/phpredis/commit/d23eff) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix RedisCluster constructor with PHP 7 strict scalar type [5c21d7](https://www.github.com/phpredis/phpredis/commit/5c21d7) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) -- Allow to use empty string as persistant_id [344de5](https://www.github.com/phpredis/phpredis/commit/344de5) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow to use empty string as persistent_id [344de5](https://www.github.com/phpredis/phpredis/commit/344de5) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix cluster_init_seeds. [db1347](https://www.github.com/phpredis/phpredis/commit/db1347) ([@adlagares](https://github.com/adlagares)) - Fix z_seeds may be a reference [42581a](https://www.github.com/phpredis/phpredis/commit/42581a) ([@janic716](https://github.com/janic716)) - PHP >=7.3 uses zend_string for php_url elements [b566fb](https://www.github.com/phpredis/phpredis/commit/b566fb) ([@fmk](https://github.com/fmk)) @@ -1701,9 +1701,9 @@ to the api, listed below. This release contains two big improvements: -1. Adding a new printf like command construction function with additionaly +1. Adding a new printf like command construction function with additionally format specifiers specific to phpredis. -2. Implementation of custom objects for Redis and RedisArray wich eliminates +2. Implementation of custom objects for Redis and RedisArray which eliminates double hash lookup. Also many small improvements and bug fixes were made. @@ -1783,7 +1783,7 @@ the php 5 and 7 codebase into a single branch. - wrong size. ([@remicollet](https://github.com/remicollet)) - - Added php session unit test ([@yatsukhnenko](https://github.com/weltling)) -- Added explicit module dependancy for igbinary ([@remicollet](https://github.com/remicollet)) +- Added explicit module dependency for igbinary ([@remicollet](https://github.com/remicollet)) - Added phpinfo serialization information ([@remicollet](https://github.com/remicollet)) --- @@ -1886,7 +1886,7 @@ than 7. - Fixed memory leak in discard function [17b1f427](https://www.github.com/phpredis/phpredis/commit/17b1f427) - Sanity check for igbinary unserialization [3266b222](https://www.github.com/phpredis/phpredis/commit/3266b222), [528297a](https://www.github.com/phpredis/phpredis/commit/528297a) ([Maurus Cuelenaere](https://github.com/mcuelenaere)). -- Fix segfault occuring from unclosed socket connection for Redis Cluster +- Fix segfault occurring from unclosed socket connection for Redis Cluster [04196aee](https://www.github.com/phpredis/phpredis/commit/04196aee) ([CatKang](https://github.com/CatKang)) - Case insensitive zRangeByScore options - Fixed dreaded size_t vs long long compiler warning diff --git a/README.md b/README.md index 3648ddf1e5..757337193f 100644 --- a/README.md +++ b/README.md @@ -460,7 +460,7 @@ _**Description**_: Sends a string to Redis, which replies with the same string 1. [Backoff algorithms](#backoff-algorithms) ### Maximum retries -You can set and get the maximum retries upon connection issues using the `OPT_MAX_RETRIES` option. Note that this is the number of _retries_, meaning if you set this option to _n_, there will be a maximum _n+1_ attemps overall. Defaults to 10. +You can set and get the maximum retries upon connection issues using the `OPT_MAX_RETRIES` option. Note that this is the number of _retries_, meaning if you set this option to _n_, there will be a maximum _n+1_ attempts overall. Defaults to 10. ##### *Example* @@ -511,7 +511,7 @@ $redis->setOption(Redis::OPT_BACKOFF_CAP, 750); // backoff time capped at 750ms _**Description**_: Execute the Redis ACL command. ##### *Parameters* -_variable_: Minumum of one argument for `Redis` and two for `RedisCluster`. +_variable_: Minimum of one argument for `Redis` and two for `RedisCluster`. ##### *Example* ~~~php diff --git a/cluster_library.c b/cluster_library.c index 1fb4bde5eb..ea19e6427a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -450,7 +450,7 @@ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val // Serialize our value val_free = redis_pack(c->flags, z_val, &val, &val_len); - // Attach it to the provied keyval entry + // Attach it to the provided keyval entry kv->val = val; kv->val_len = val_len; kv->val_free = val_free; @@ -468,7 +468,7 @@ void cluster_multi_add(clusterMultiCmd *mc, char *data, int data_len) { redis_cmd_append_sstr(&(mc->args), data, data_len); } -/* Finalize a clusterMutliCmd by constructing the whole thing */ +/* Finalize a clusterMultiCmd by constructing the whole thing */ void cluster_multi_fini(clusterMultiCmd *mc) { mc->cmd.len = 0; redis_cmd_init_sstr(&(mc->cmd), mc->argc, mc->kw, mc->kw_len); @@ -709,7 +709,7 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) { master = cluster_node_create(c, host, hlen, port, low, 0); zend_hash_str_update_ptr(c->nodes, key, klen, master); - // Attach slaves first time we encounter a given master in order to avoid regitering the slaves multiple times + // Attach slaves first time we encounter a given master in order to avoid registering the slaves multiple times for (j = 3; j< r2->elements; j++) { r3 = r2->element[j]; if (!VALIDATE_SLOTS_INNER(r3)) { @@ -1151,7 +1151,7 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved) * redirection, parsing out slot host and port so the caller can take * appropriate action. * - * In the case of a non MOVED/ASK error, we wlll set our cluster error + * In the case of a non MOVED/ASK error, we will set our cluster error * condition so GetLastError can be queried by the client. * * This function will return -1 on a critical error (e.g. parse/communication diff --git a/cluster_library.h b/cluster_library.h index eb2b1531b8..59e490e24e 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -116,7 +116,7 @@ mc->args.len = 0; \ mc->argc = 0; \ -/* Initialzie a clusterMultiCmd with a keyword and length */ +/* Initialize a clusterMultiCmd with a keyword and length */ #define CLUSTER_MULTI_INIT(mc, keyword, keyword_len) \ mc.kw = keyword; \ mc.kw_len = keyword_len; \ diff --git a/docs/Redis.html b/docs/Redis.html index a74aa2d48f..dd7ee25ffc 100644 --- a/docs/Redis.html +++ b/docs/Redis.html @@ -654,7 +654,7 @@

    Methods

    pexpiretime(string $key) -

    Get the expriation timestamp of a given Redis key but in milliseconds.

    +

    Get the expiration timestamp of a given Redis key but in milliseconds.

    @@ -714,7 +714,7 @@

    Methods

    geopos(string $key, string $member, string ...$other_members) -

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    +

    Return the longitude and latitude for one or more members of a geospacially encoded sorted set.

    @@ -1128,7 +1128,7 @@

    Methods

    incr(string $key, int $by = 1) -

    Increment a key's value, optionally by a specifc amount.

    +

    Increment a key's value, optionally by a specific amount.

    @@ -1203,7 +1203,7 @@

    Methods

    lLen(string $key) -

    Retrieve the lenght of a list.

    +

    Retrieve the length of a list.

    @@ -2180,7 +2180,7 @@

    Methods

    wait(int $numreplicas, int $timeout)

    Block the client up to the provided timeout until a certain number of replicas have confirmed -recieving them.

    +receiving them.

    @@ -2313,7 +2313,7 @@

    Methods

    xrevrange(string $key, string $end, string $start, int $count = -1) -

    Get a range of entries from a STREAM ke in reverse cronological order.

    +

    Get a range of entries from a STREAM key in reverse chronological order.

    @@ -2373,7 +2373,7 @@

    Methods

    zLexCount(string $key, string $min, string $max) -

    Count the number of elements in a sorted set whos members fall within the provided +

    Count the number of elements in a sorted set whose members fall within the provided lexographical range.

    @@ -3805,7 +3805,7 @@

    POP the maximum scoring element off of one or more sorted sets, blocking up to a specified timeout if no elements are available.

    Following are examples of the two main ways to call this method.

    -

    NOTE: We reccomend calling this function with an array and a timeout as the other strategy +

    NOTE: We recommend calling this function with an array and a timeout as the other strategy may be deprecated in future versions of PhpRedis

    @@ -5850,7 +5850,7 @@

    -

    Get the expriation timestamp of a given Redis key but in milliseconds.

    +

    Get the expiration timestamp of a given Redis key but in milliseconds.

    Parameters

    @@ -6036,7 +6036,7 @@

    Parameters

    float $lat -

    The lattitude of the first member.

    +

    The latitude of the first member.

    string @@ -6046,7 +6046,7 @@

    Parameters

    mixed ...$other_triples_and_options -

    You can continue to pass longitude, lattitude, and member +

    You can continue to pass longitude, latitude, and member arguments to add as many members as you wish. Optionally, the final argument may be a string with options for the command Redis documentation for the options.

    @@ -6262,7 +6262,7 @@

    -

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    +

    Return the longitude and latitude for one or more members of a geospacially encoded sorted set.

    Parameters

    @@ -6291,7 +6291,7 @@

    Return Value

    - +
    Redis|array|false

    array of longitude and lattitude pairs.

    array of longitude and latitude pairs.

    @@ -6662,7 +6662,7 @@

    Parameters

    array|string $position -

    Either a two element array with longitude and lattitude, or +

    Either a two element array with longitude and latitude, or a string representing a member of the set.

    @@ -6734,7 +6734,7 @@

    Parameters

    array|string $position -

    Either a two element array with longitude and lattitude, or +

    Either a two element array with longitude and latitude, or a string representing a member of the set.

    @@ -7452,7 +7452,7 @@

    Parameters

    array|null $options -

    An optional array of modifiers for the comand.

    +

    An optional array of modifiers for the command.

    $options = [
         'MINMATCHLEN'  => int  # Exclude matching substrings that are less than this value
     
    @@ -8755,7 +8755,7 @@ 

    -

    Increment a key's value, optionally by a specifc amount.

    +

    Increment a key's value, optionally by a specific amount.

    Parameters

    @@ -9145,7 +9145,7 @@

    -

    Retrieve the lenght of a list.

    +

    Retrieve the length of a list.

    Parameters

    @@ -12781,7 +12781,7 @@

    Parameters

    An optional count of members to return.

    If this value is positive, Redis will return up to the requested number but with unique elements that will never repeat. This means -you may recieve fewer then $count replies.

    +you may receive fewer then $count replies.

    If the number is negative, Redis will return the exact number requested but the result may contain duplicate elements.

    @@ -14013,7 +14013,7 @@

    Return Value

    Redis|bool

    Success if we were successfully able to start replicating a primary or -were able to promote teh replicat to a primary.

    +were able to promote the replicat to a primary.

    @@ -15577,7 +15577,7 @@

    Block the client up to the provided timeout until a certain number of replicas have confirmed -recieving them.

    +receiving them.

    Parameters

    @@ -15586,7 +15586,7 @@

    Parameters

    int $numreplicas -

    The number of replicas we want to confirm write operaions

    +

    The number of replicas we want to confirm write operations

    int @@ -15759,7 +15759,7 @@

    Parameters

    The ID for the message we want to add. This can be the special value '' which means Redis will generate the ID that appends the message to the end of the stream. It can also be a value in the form - which will -generate an ID that appends to the end ot entries with the same value +generate an ID that appends to the end of entries with the same value (if any exist).

    @@ -15932,7 +15932,7 @@

    Examples

    $pending = $redis->xPending('ships', 'combatants');
    var_dump($pending);

    -// Asssume control of the pending message with a different consumer.
    +// Assume control of the pending message with a different consumer.
    $res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');

    // Now the 'Sisko' consumer owns the message
    @@ -16017,7 +16017,7 @@

    Return Value

    - +
    Redis|array|bool

    An array of claimed messags or false on failure.

    An array of claimed messages or false on failure.

    @@ -16555,7 +16555,7 @@

    Parameters

    int $count -

    An optional limit to how many entries are returnd per stream

    +

    An optional limit to how many entries are returned per stream

    int @@ -16718,7 +16718,7 @@

    -

    Get a range of entries from a STREAM ke in reverse cronological order.

    +

    Get a range of entries from a STREAM key in reverse chronological order.

    Parameters

    @@ -16997,7 +16997,7 @@

    Parameters

    string $key -

    The sorted set to retreive cardinality from.

    +

    The sorted set to retrieve cardinality from.

    @@ -17200,7 +17200,7 @@

    -

    Count the number of elements in a sorted set whos members fall within the provided +

    Count the number of elements in a sorted set whose members fall within the provided lexographical range.

    @@ -17584,7 +17584,7 @@

    Parameters

    string $key -

    The sorted set to retreive elements from

    +

    The sorted set to retrieve elements from

    string @@ -18120,7 +18120,7 @@

    Parameters

    string $key -

    The sorted set where we wnat to remove members.

    +

    The sorted set where we want to remove members.

    int @@ -18191,7 +18191,7 @@

    Parameters

    string $key -

    The sorted set where we wnat to remove members.

    +

    The sorted set where we want to remove members.

    string diff --git a/docs/doc-index.html b/docs/doc-index.html index e475ef25be..8dad86c345 100644 --- a/docs/doc-index.html +++ b/docs/doc-index.html @@ -317,7 +317,7 @@

    A

    Redis::geohash() — Method in class Redis

    Retrieve one or more GeoHash encoded strings for members of the set.

    Redis::geopos() — Method in class Redis
    -

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    +

    Return the longitude and latitude for one or more members of a geospacially encoded sorted set.

    Redis::georadius() — Method in class Redis

    Retrieve members of a geospacially sorted set that are within a certain radius of a location.

    Redis::georadius_ro() — Method in class Redis
    @@ -468,7 +468,7 @@

    A

    I

    Redis::incr() — Method in class Redis
    -

    Increment a key's value, optionally by a specifc amount.

    +

    Increment a key's value, optionally by a specific amount.

    Redis::incrBy() — Method in class Redis

    Increment a key by a specific integer value

    Redis::incrByFloat() — Method in class Redis
    @@ -508,7 +508,7 @@

    A

    Redis::lInsert() — Method in class Redis
    Redis::lLen() — Method in class Redis
    -

    Retrieve the lenght of a list.

    +

    Retrieve the length of a list.

    Redis::lMove() — Method in class Redis

    Move an element from one list into another.

    Redis::lPop() — Method in class Redis
    @@ -601,7 +601,7 @@

    A

    P

    Redis::pexpiretime() — Method in class Redis
    -

    Get the expriation timestamp of a given Redis key but in milliseconds.

    +

    Get the expiration timestamp of a given Redis key but in milliseconds.

    Redis::pconnect() — Method in class Redis
    Redis::persist() — Method in class Redis
    @@ -929,7 +929,7 @@

    A

    Watch one or more keys for conditional execution of a transaction.

    Redis::wait() — Method in class Redis

    Block the client up to the provided timeout until a certain number of replicas have confirmed -recieving them.

    +receiving them.

    RedisCluster::watch() — Method in class RedisCluster

    X

    @@ -961,7 +961,7 @@

    A

    Redis::xreadgroup
    () — Method in class Redis

    Read one or more messages using a consumer group.

    Redis::xrevrange() — Method in class Redis
    -

    Get a range of entries from a STREAM ke in reverse cronological order.

    +

    Get a range of entries from a STREAM key in reverse chronological order.

    Redis::xtrim() — Method in class Redis

    Truncate a STREAM key in various ways.

    RedisCluster::xack() — Method in class RedisCluster
    @@ -1004,7 +1004,7 @@

    A

    Redis::zIncrBy() — Method in class Redis

    Create or increment the score of a member in a Redis sorted set

    Redis::zLexCount() — Method in class Redis
    -

    Count the number of elements in a sorted set whos members fall within the provided +

    Count the number of elements in a sorted set whose members fall within the provided lexographical range.

    Redis::zMscore() — Method in class Redis

    Retrieve the score of one or more members in a sorted set.

    diff --git a/docs/doctum.js b/docs/doctum.js index d487386524..dc773facd1 100644 --- a/docs/doctum.js +++ b/docs/doctum.js @@ -168,7 +168,7 @@ var Doctum = { DoctumSearch.doctumSearchPageAutoCompleteProgressBar.className = 'progress-bar'; } }, - makeProgess: function () { + makeProgress: function () { Doctum.makeProgressOnProgressBar( Doctum.doctumSearchAutoCompleteProgressBarPercent, Doctum.doctumSearchAutoCompleteProgressBar @@ -209,7 +209,7 @@ var Doctum = { oReq.onprogress = function (pe) { if (pe.lengthComputable) { Doctum.doctumSearchAutoCompleteProgressBarPercent = parseInt(pe.loaded / pe.total * 100, 10); - Doctum.makeProgess(); + Doctum.makeProgress(); } }; oReq.onloadend = function (_) { diff --git a/library.c b/library.c index 77eecaa14d..8ca6e1c740 100644 --- a/library.c +++ b/library.c @@ -159,7 +159,7 @@ static int reselect_db(RedisSock *redis_sock) { return 0; } -/* Append an AUTH command to a smart string if neccessary. This will either +/* Append an AUTH command to a smart string if necessary. This will either * append the new style AUTH , old style AUTH , or * append no command at all. Function returns 1 if we appended a command * and 0 otherwise. */ @@ -823,7 +823,7 @@ static zend_string *redis_hash_auth(zend_string *user, zend_string *pass) { if (user == NULL && pass == NULL) return NULL; - /* Theoretically inpossible but check anyway */ + /* Theoretically impossible but check anyway */ algo = zend_string_init("sha256", sizeof("sha256") - 1, 0); if ((ops = redis_hash_fetch_ops(algo)) == NULL) { zend_string_release(algo); diff --git a/package.xml b/package.xml index 332dab572d..e800ba8e5a 100644 --- a/package.xml +++ b/package.xml @@ -372,7 +372,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Add cluster support for strict sessions and lazy write [b6cf6361] (Michael Grunder) * Add function command [90a0e9cc] (Pavlo Yatsukhnenko) * Add FCALL/FCALL_RO commands [7c46ad2c] (Pavlo Yatsukhnenko) - * Remove unused macroses [831d6118] (Pavlo Yatsukhnenko) + * Remove unused macros [831d6118] (Pavlo Yatsukhnenko) @@ -809,7 +809,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> This release contains initial support for Redis Sentinel as well as many smaller bug fixes and improvements. It is especially of interest if you use persistent connections, as we've added logic to make sure they are in - a good state when retreving them from the pool. + a good state when retrieving them from the pool. IMPORTANT: Sentinel support is considered experimental and the API will likely change based on user feedback. @@ -823,7 +823,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4, 383779ed] (Pavlo Yatsukhnenko) - * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, + * Housekeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, 0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre, Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre) @@ -853,7 +853,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> This release contains initial support for Redis Sentinel as well as many smaller bug fixes and improvements. It is especially of interest if you use persistent connections, as we've added logic to make sure they are in - a good state when retreving them from the pool. + a good state when retrieving them from the pool. IMPORTANT: Sentinel support is considered experimental and the API will likely change based on user feedback. @@ -867,7 +867,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4, 383779ed] (Pavlo Yatsukhnenko) - * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, + * Housekeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, 0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre, Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre) @@ -984,7 +984,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> phpredis 4.3.0 - This is probably the last release with PHP 5 suport!!! + This is probably the last release with PHP 5 support!!! * Proper persistent connections pooling implementation [a3703820, c76e00fb, 0433dc03, c75b3b93] (Pavlo Yatsukhnenko) * RedisArray auth [b5549cff, 339cfa2b, 6b411aa8] (Pavlo Yatsukhnenko) @@ -1036,7 +1036,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce] (Pavlo Yatsukhnenko) * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) * Treat a -1 response from cluster_check_response as a timeout. [27df9220, 07ef7f4e, d1172426] (Michael Grunder) - * Use a ZSET insted of SET for EVAL tests [2e412373] (Michael Grunder) + * Use a ZSET instead of SET for EVAL tests [2e412373] (Michael Grunder) * Missing space between command and args [0af2a7fe] (@remicollet) 4.2.0RC1: @@ -1154,7 +1154,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> phpredis 3.1.6 - This release conains only fix of RedisArray distributor hashing function + This release contains only fix of RedisArray distributor hashing function which was broken in 3.1.4. Huge thanks to @rexchen123 @@ -1222,8 +1222,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> phpredis 3.1.3 This release contains two big improvements: - 1. Adding a new printf like command construction function with additionaly format specifiers specific to phpredis. - 2. Implementation of custom objects for Redis and RedisArray wich eliminates double hash lookup. + 1. Adding a new printf like command construction function with additionally format specifiers specific to phpredis. + 2. Implementation of custom objects for Redis and RedisArray which eliminates double hash lookup. Also many small improvements and bug fixes were made. * A printf like method to construct a Redis RESP command [a4a0ed, d75081, bdd287, 0eaeae, b3d00d] (Michael Grunder) @@ -1295,7 +1295,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> wrong size. (@remicollet) * Added php session unit test (@yatsukhnenko) - * Added explicit module dependancy for igbinary (@remicollet) + * Added explicit module dependency for igbinary (@remicollet) * Added phpinfo serialization information (@remicollet) @@ -1342,7 +1342,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> In addition there have been many bug fixes and improvements to non cluster related commands, which are listed below. - I've attempted to include everyone who contribued to the project in each fix + I've attempted to include everyone who contributed to the project in each fix description and have included names or github user ids. Thanks to everyone for submitting bug reports and pull requests. A special @@ -1368,7 +1368,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fixed memory leak in discard function [17b1f427] * Sanity check for igbinary unserialization (Maurus Cuelenaere) [3266b222, 5528297a] - * Fix segfault occuring from unclosed socket connection for Redis Cluster + * Fix segfault occurring from unclosed socket connection for Redis Cluster (CatKang) [04196aee] * Case insensitive zRangeByScore options * Fixed dreaded size_t vs long long compiler warning diff --git a/redis.stub.php b/redis.stub.php index 0a4332e453..e9064fe0b0 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -705,7 +705,7 @@ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis| * * Following are examples of the two main ways to call this method. * - * **NOTE**: We reccomend calling this function with an array and a timeout as the other strategy + * **NOTE**: We recommend calling this function with an array and a timeout as the other strategy * may be deprecated in future versions of PhpRedis * * @see https://redis.io/commands/bzpopmax @@ -1160,7 +1160,7 @@ public function failover(?array $to = null, bool $abort = false, int $timeout = public function expiretime(string $key): Redis|int|false; /** - * Get the expriation timestamp of a given Redis key but in milliseconds. + * Get the expiration timestamp of a given Redis key but in milliseconds. * * @see https://redis.io/commands/pexpiretime * @see Redis::expiretime() @@ -1245,8 +1245,8 @@ public function function(string $operation, mixed ...$args): Redis|bool|string|a * * @param string $key The sorted set to add data to. * @param float $lng The longitude of the first member - * @param float $lat The lattitude of the first member. - * @param member $other_triples_and_options You can continue to pass longitude, lattitude, and member + * @param float $lat The latitude of the first member. + * @param member $other_triples_and_options You can continue to pass longitude, latitude, and member * arguments to add as many members as you wish. Optionally, the final argument may be * a string with options for the command @see Redis documentation for the options. * @@ -1301,13 +1301,13 @@ public function geodist(string $key, string $src, string $dst, ?string $unit = n public function geohash(string $key, string $member, string ...$other_members): Redis|array|false; /** - * Return the longitude and lattitude for one or more members of a geospacially encoded sorted set. + * Return the longitude and latitude for one or more members of a geospacially encoded sorted set. * * @param string $key The set to query. * @param string $member The first member to query. * @param string $other_members One or more members to query. * - * @return An array of longitude and lattitude pairs. + * @return An array of longitude and latitude pairs. * * @see https://redis.io/commands/geopos * @@ -1385,7 +1385,7 @@ public function georadiusbymember_ro(string $key, string $member, float $radius, * Search a geospacial sorted set for members in various ways. * * @param string $key The set to query. - * @param array|string $position Either a two element array with longitude and lattitude, or + * @param array|string $position Either a two element array with longitude and latitude, or * a string representing a member of the set. * @param array|int|float $shape Either a number representine the radius of a circle to search, or * a two element array representing the width and height of a box @@ -1402,7 +1402,7 @@ public function geosearch(string $key, array|string $position, array|int|float $ * * @param string $dst The destination where results will be stored. * @param string $src The key to query. - * @param array|string $position Either a two element array with longitude and lattitude, or + * @param array|string $position Either a two element array with longitude and latitude, or * a string representing a member of the set. * @param array|int|float $shape Either a number representine the radius of a circle to search, or * a two element array representing the width and height of a box @@ -1569,7 +1569,7 @@ public function getRange(string $key, int $start, int $end): Redis|string|false; * * @param string $key1 The first key to check * @param string $key2 The second key to check - * @param array $options An optional array of modifiers for the comand. + * @param array $options An optional array of modifiers for the command. * * * $options = [ @@ -1883,7 +1883,7 @@ public function hVals(string $key): Redis|array|false; public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool; /** - * Increment a key's value, optionally by a specifc amount. + * Increment a key's value, optionally by a specific amount. * * @see https://redis.io/commands/incr * @see https://redis.io/commands/incrby @@ -1962,7 +1962,7 @@ public function keys(string $pattern); public function lInsert(string $key, string $pos, mixed $pivot, mixed $value); /** - * Retrieve the lenght of a list. + * Retrieve the length of a list. * * @param string $key The list * @@ -2806,7 +2806,7 @@ public function sPop(string $key, int $count = 0): Redis|string|array|false; * * If this value is positive, Redis will return *up to* the requested * number but with unique elements that will never repeat. This means - * you may recieve fewer then `$count` replies. + * you may receive fewer then `$count` replies. * * If the number is negative, Redis will return the exact number requested * but the result may contain duplicate elements. @@ -3130,7 +3130,7 @@ public function slaveof(?string $host = null, int $port = 6379): Redis|bool; * @param string $port The port of the primary to start replicating. * * @return Redis|bool Success if we were successfully able to start replicating a primary or - * were able to promote teh replicat to a primary. + * were able to promote the replicat to a primary. * * @example * $redis = new Redis(['host' => 'localhost']); @@ -3585,11 +3585,11 @@ public function watch(array|string $key, string ...$other_keys): Redis|bool; /** * Block the client up to the provided timeout until a certain number of replicas have confirmed - * recieving them. + * receiving them. * * @see https://redis.io/commands/wait * - * @param int $numreplicas The number of replicas we want to confirm write operaions + * @param int $numreplicas The number of replicas we want to confirm write operations * @param int $timeout How long to wait (zero meaning forever). * * @return Redis|int|false The number of replicas that have confirmed or false on failure. @@ -3645,7 +3645,7 @@ public function xack(string $key, string $group, array $ids): int|false; * @param string $id The ID for the message we want to add. This can be the special value '*' * which means Redis will generate the ID that appends the message to the * end of the stream. It can also be a value in the form -* which will - * generate an ID that appends to the end ot entries with the same value + * generate an ID that appends to the end of entries with the same value * (if any exist). * @param int $maxlen If specified Redis will append the new message but trim any number of the * oldest messages in the stream until the length is <= $maxlen. @@ -3691,7 +3691,7 @@ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bo * $pending = $redis->xPending('ships', 'combatants'); * var_dump($pending); * - * // Asssume control of the pending message with a different consumer. + * // Assume control of the pending message with a different consumer. * $res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); * * // Now the 'Sisko' consumer owns the message @@ -3731,7 +3731,7 @@ public function xautoclaim(string $key, string $group, string $consumer, int $mi * ]; * * - * @return Redis|array|bool An array of claimed messags or false on failure. + * @return Redis|array|bool An array of claimed messages or false on failure. * * @example * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); @@ -3880,7 +3880,7 @@ public function xrange(string $key, string $start, string $end, int $count = -1) * Consume one or more unconsumed elements in one or more streams. * * @param array $streams An associative array with stream name keys and minimum id values. - * @param int $count An optional limit to how many entries are returnd *per stream* + * @param int $count An optional limit to how many entries are returned *per stream* * @param int $block An optional maximum number of milliseconds to block the caller if no * data is available on any of the provided streams. * @@ -3936,7 +3936,7 @@ public function xread(array $streams, int $count = -1, int $block = -1): Redis|a public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): Redis|array|bool; /** - * Get a range of entries from a STREAM ke in reverse cronological order. + * Get a range of entries from a STREAM key in reverse chronological order. * * @param string $key The stream key to query. * @param string $end The maximum message ID to include. @@ -4019,7 +4019,7 @@ public function zAdd(string $key, array|float $score_or_options, mixed ...$more_ /** * Return the number of elements in a sorted set. * - * @param string $key The sorted set to retreive cardinality from. + * @param string $key The sorted set to retrieve cardinality from. * * @return Redis|int|false The number of elements in the set or false on failure * @@ -4063,7 +4063,7 @@ public function zCount(string $key, int|string $start, int|string $end): Redis|i public function zIncrBy(string $key, float $value, mixed $member): Redis|float|false; /** - * Count the number of elements in a sorted set whos members fall within the provided + * Count the number of elements in a sorted set whose members fall within the provided * lexographical range. * * @param string $key The sorted set to check. @@ -4173,7 +4173,7 @@ public function zRange(string $key, string|int $start, string|int $end, array|bo /** * Retrieve a range of elements from a sorted set by legographical range. * - * @param string $key The sorted set to retreive elements from + * @param string $key The sorted set to retrieve elements from * @param string $min The minimum legographical value to return * @param string $max The maximum legographical value to return * @param int $offset An optional offset within the matching values to return @@ -4256,7 +4256,7 @@ public function zRandMember(string $key, ?array $options = null): Redis|string|a * Get the rank of a member of a sorted set, by score. * * @param string $key The sorted set to check. - * @param mixed $memeber The member to test. + * @param mixed $member The member to test. * * @return Redis|int|false The rank of the requested member. * @see https://redis.io/commands/zrank @@ -4301,7 +4301,7 @@ public function zRemRangeByLex(string $key, string $min, string $max): Redis|int /** * Remove one or more members of a sorted set by their rank. * - * @param string $key The sorted set where we wnat to remove members. + * @param string $key The sorted set where we want to remove members. * @param int $start The rank when we want to start removing members * @param int $end The rank we want to stop removing membersk. * @@ -4316,7 +4316,7 @@ public function zRemRangeByRank(string $key, int $start, int $end): Redis|int|fa /** * Remove one or more members of a sorted set by their score. * - * @param string $key The sorted set where we wnat to remove members. + * @param string $key The sorted set where we want to remove members. * @param int $start The lowest score to remove. * @param int $end The highest score to remove. * diff --git a/redis_cluster.c b/redis_cluster.c index a492c2c595..0a528e0d6a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1358,7 +1358,7 @@ PHP_METHOD(RedisCluster, zmpop) { } /* }}} */ -/* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, sring $from, int $count = 1) */ +/* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, string $from, int $count = 1) */ PHP_METHOD(RedisCluster, bzmpop) { CLUSTER_PROCESS_KW_CMD("BZMPOP", redis_mpop_cmd, cluster_mpop_resp, 0); } @@ -2394,7 +2394,7 @@ PHP_METHOD(RedisCluster, acl) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc - 1, "ACL"); - /* Read the op, determin if it's readonly, and add it */ + /* Read the op, determine if it's readonly, and add it */ zs = zval_get_string(&zargs[1]); readonly = redis_acl_op_readonly(zs); redis_cmd_append_sstr_zstr(&cmdstr, zs); diff --git a/redis_commands.c b/redis_commands.c index 6fbd684a80..906eb23d6c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -1495,7 +1495,7 @@ int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) { if (arg != NULL) { if (Z_TYPE_P(arg) != IS_STRING) { - php_error_docref(NULL, E_WARNING, "Invalid patern value"); + php_error_docref(NULL, E_WARNING, "Invalid pattern value"); return FAILURE; } pattern = zval_get_string(arg); diff --git a/redis_session.c b/redis_session.c index d10793dfa3..9f8c453f54 100644 --- a/redis_session.c +++ b/redis_session.c @@ -126,7 +126,7 @@ redis_pool_free(redis_pool *pool) { efree(pool); } -/* Retreive session.gc_maxlifetime from php.ini protecting against an integer overflow */ +/* Retrieve session.gc_maxlifetime from php.ini protecting against an integer overflow */ static int session_gc_maxlifetime(void) { zend_long value = INI_INT("session.gc_maxlifetime"); if (value > INT_MAX) { diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 696ba927bf..94624bac4a 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -558,7 +558,7 @@ public function testMultiExecDel() { $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary')); } - public function testMutliExecUnlink() { + public function testMultiExecUnlink() { if (version_compare($this->min_version, "4.0.0", "lt")) { $this->markTestSkipped(); } diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index e482345d24..9c9eebf9ff 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -436,7 +436,7 @@ public function testEvalSHA() { // Flush any loaded scripts $this->redis->script($str_key, 'flush'); - // Non existant script (but proper sha1), and a random (not) sha1 string + // Non existent script (but proper sha1), and a random (not) sha1 string $this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$str_key], 1)); $this->assertFalse($this->redis->evalsha('some-random-data'),[$str_key], 1); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index fca58c8c07..9d1701524e 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2400,7 +2400,7 @@ public function testSlowlog() { } public function testWait() { - // Closest we can check based on redis commmit history + // Closest we can check based on redis commit history if(version_compare($this->version, '2.9.11') < 0) { $this->markTestSkipped(); return; @@ -2793,7 +2793,7 @@ public function testZX() { $this->assertTrue($this->redis->zScore('{zset}U', 'duplicate')===1.0); $this->redis->del('{zset}U'); - //now test zUnion *without* weights but with aggregrate function + //now test zUnion *without* weights but with aggregate 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'); @@ -3481,7 +3481,7 @@ public function testPipelineMultiExec() $this->assertEquals(5, count($ret)); // should be 5 atomic operations } - /* Github issue #1211 (ignore redundant calls to pipeline or multi) */ + /* 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++) { @@ -5569,7 +5569,7 @@ public function testEvalSHA() { // Flush any loaded scripts $this->redis->script('flush'); - // Non existant script (but proper sha1), and a random (not) sha1 string + // Non existent 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')); @@ -6813,7 +6813,7 @@ protected function doXReadTest() { ['{stream}-1' => [$new_id => ['final' => 'row']]] ); - /* Emtpy query should fail */ + /* Empty query should fail */ $this->assertFalse($this->redis->xRead([])); } @@ -7105,7 +7105,7 @@ public function testXAutoClaim() { $pending = $this->redis->xPending('ships', 'combatants'); $this->assertTrue($pending && isset($pending[3][0][0]) && $pending[3][0][0] == "Jem'Hadar"); - // Asssume control of the pending message with a different consumer. + // Assume control of the pending message with a different consumer. $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); $this->assertTrue($res && count($res) == 3 && $res[0] == '0-0' && From 732e466a6a593c8ead1cecfddba0ca0fc1e49d35 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 21 Feb 2024 10:11:35 -0800 Subject: [PATCH 1846/1986] Improve warning when we encounter an invalid EXPIRY in SET We actually had two different bits of logic to handle EXPIRY values in the `SET` command. One for the legacy `SET` -> `SETEX` mapping and another for the newer `SET foo bar EX `. Additionally the error message could be confusing. Passing 3.1415 for an `EX` expiry would fail as we didn't allow floats. This commit consolidates expiry parsing to our existing helper function as well as improves the `php_error_docref` warning in the event that the user passes invalid data. The warning will now tell the user the type they tried to pass as an EXPIRY to make it easier to track down what's going wrong. Fixes #2448 --- redis_commands.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/redis_commands.c b/redis_commands.c index 906eb23d6c..df726386e5 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2291,14 +2291,19 @@ static int redis_try_get_expiry(zval *zv, zend_long *lval) { int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - smart_string cmdstr = {0}; - zval *z_value, *z_opts=NULL; char *key = NULL, *exp_type = NULL, *set_type = NULL; - long exp_set = 0, keep_ttl = 0; + zval *z_value, *z_opts=NULL; + smart_string cmdstr = {0}; zend_long expire = -1; zend_bool get = 0; + long keep_ttl = 0; size_t key_len; + #define setExpiryWarning(zv) \ + php_error_docref(NULL, E_WARNING, "%s passed as EXPIRY is invalid " \ + "(must be an int, float, or numeric string >= 1)", \ + zend_zval_type_name((zv))) + // Make sure the function is being called correctly if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &key, &key_len, &z_value, &z_opts) == FAILURE) @@ -2306,6 +2311,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } + // Check for an options array if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { HashTable *kt = Z_ARRVAL_P(z_opts); @@ -2321,17 +2327,12 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string_equals_literal_ci(zkey, "EXAT") || zend_string_equals_literal_ci(zkey, "PXAT")) ) { - exp_set = 1; + if (redis_try_get_expiry(v, &expire) == FAILURE || expire < 1) { + setExpiryWarning(v); + return FAILURE; + } - /* Set expire type */ exp_type = ZSTR_VAL(zkey); - - /* Try to extract timeout */ - if (Z_TYPE_P(v) == IS_LONG) { - expire = Z_LVAL_P(v); - } else if (Z_TYPE_P(v) == IS_STRING) { - expire = atol(Z_STRVAL_P(v)); - } } else if (Z_TYPE_P(v) == IS_STRING) { if (zend_string_equals_literal_ci(Z_STR_P(v), "KEEPTTL")) { keep_ttl = 1; @@ -2345,19 +2346,14 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } ZEND_HASH_FOREACH_END(); } else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) { - if (redis_try_get_expiry(z_opts, &expire) == FAILURE) { - php_error_docref(NULL, E_WARNING, "Expire must be a long, double, or a numeric string"); + if (redis_try_get_expiry(z_opts, &expire) == FAILURE || expire < 1) { + setExpiryWarning(z_opts); return FAILURE; } - - exp_set = 1; } /* Protect the user from syntax errors but give them some info about what's wrong */ - if (exp_set && expire < 1) { - php_error_docref(NULL, E_WARNING, "EXPIRE can't be < 1"); - return FAILURE; - } else if (exp_type && keep_ttl) { + if (exp_type && keep_ttl) { php_error_docref(NULL, E_WARNING, "KEEPTTL can't be combined with EX or PX option"); return FAILURE; } @@ -2396,6 +2392,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd_len = cmdstr.len; return SUCCESS; + + #undef setExpiryWarning } /* MGET */ From 77ab62bccb9995c6523e27edc14de10936e375a4 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Sat, 17 Feb 2024 11:53:42 +0100 Subject: [PATCH 1847/1986] Tighter return types for Redis::(keys|hKeys|hVals|hGetAll)() --- redis.stub.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index e9064fe0b0..3393fcbc5b 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1676,7 +1676,7 @@ public function hGet(string $key, string $member): mixed; * Read every field and value from a hash. * * @param string $key The hash to query. - * @return Redis|array|false All fields and values or false if the key didn't exist. + * @return Redis|array|false All fields and values or false if the key didn't exist. * * @see https://redis.io/commands/hgetall * @@ -1722,7 +1722,7 @@ public function hIncrByFloat(string $key, string $field, float $value): Redis|fl * * @param string $key The hash to query. * - * @return Redis|array|false The fields in the hash or false if the hash doesn't exist. + * @return Redis|list|false The fields in the hash or false if the hash doesn't exist. * * @see https://redis.io/commands/hkeys * @@ -1834,7 +1834,7 @@ public function hStrLen(string $key, string $field): Redis|int|false; * * @param string $key The hash to query. * - * @return Redis|array|false The values from the hash. + * @return Redis|list|false The values from the hash. * * @see https://redis.io/commands/hvals * @@ -1952,7 +1952,7 @@ public function info(string ...$sections): Redis|array|false; */ public function isConnected(): bool; - /** @return Redis|array|false */ + /** @return Redis|list|false */ public function keys(string $pattern); /** From ece3f7bebcf648da244df454395733b28434b32f Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Mon, 4 Mar 2024 21:03:01 -0800 Subject: [PATCH 1848/1986] Fix config.m4 when using custom dep paths (#2453) * We need both PHP_ADD_LIBRARY_WITH_PATH and PHP_ADD_INCLUDE Fixes #2452 * Add an initial test block for ./configure correctness. --- .github/workflows/ci.yml | 66 ++++++++++++++++++++++++++++++++++++++++ config.m4 | 4 ++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c212906e18..91dd88058e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,72 @@ on: [push, pull_request] jobs: + configured-deps: + runs-on: ubuntu-latest + continue-on-error: false + strategy: + fail-fast: true + matrix: + php: ['8.3'] + steps: + - name: Checkout PhpRedis + uses: actions/checkout@v4 + + - name: Install liblzf + run: | + git clone --depth=1 https://github.com/nemequ/liblzf.git + cd liblzf + autoreconf -vi + CFLAGS=-fPIC ./configure --prefix="$GITHUB_WORKSPACE/liblzf" + make install + + - name: Install liblz4 + run: | + git clone -b v1.9.4 --depth=1 https://github.com/lz4/lz4 + cd lz4/lib + PREFIX="$GITHUB_WORKSPACE/liblz4" make install + + - name: Install libzstd + run: | + git clone -b v1.5.5 --depth=1 https://github.com/facebook/zstd + cd zstd + PREFIX="$GITHUB_WORKSPACE/libzstd" make install + + - name: Install PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: json, igbinary, msgpack, :redis + coverage: none + tools: none + + - name: Configure and build PhpRedis with distinct dep paths + run: | + phpize + ./configure \ + --enable-redis-lz4 \ + --with-liblz4="$GITHUB_WORKSPACE/liblz4" \ + --enable-redis-lzf \ + --with-liblzf="$GITHUB_WORKSPACE/liblzf" \ + --enable-redis-zstd \ + --with-libzstd="$GITHUB_WORKSPACE/libzstd" + sudo make -j"$(nproc)" + + - name: Make sure we're linking against specific liblz4 + run: | + grep "INCLUDES.*$GITHUB_WORKSPACE/liblz4" Makefile + grep "REDIS_SHARED_LIBADD.*-L$GITHUB_WORKSPACE/liblz4" Makefile + + - name: Make sure we're linking against specific liblzf + run: | + grep "INCLUDES.*$GITHUB_WORKSPACE/liblzf" Makefile + grep "REDIS_SHARED_LIBADD.*-L$GITHUB_WORKSPACE/liblzf" Makefile + + - name: Make sure we're linking against specific libzstd + run: | + grep "INCLUDES.*$GITHUB_WORKSPACE/libzstd" Makefile + grep "REDIS_SHARED_LIBADD.*-L$GITHUB_WORKSPACE/libzstd" Makefile + ubuntu: runs-on: ubuntu-latest continue-on-error: false diff --git a/config.m4 b/config.m4 index 2ba4a8b51d..c84ce1e99f 100644 --- a/config.m4 +++ b/config.m4 @@ -221,6 +221,7 @@ if test "$PHP_REDIS" != "no"; then PHP_CHECK_LIBRARY(lzf, lzf_compress, [ PHP_ADD_LIBRARY_WITH_PATH(lzf, $LIBLZF_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD) + PHP_ADD_INCLUDE($LIBLZF_DIR/include) ], [ AC_MSG_ERROR([could not find usable liblzf]) ], [ @@ -263,12 +264,12 @@ if test "$PHP_REDIS" != "no"; then PHP_CHECK_LIBRARY(lz4, LZ4_compress, [ PHP_ADD_LIBRARY_WITH_PATH(lz4, $LIBLZ4_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD) + PHP_ADD_INCLUDE($LIBLZ4_DIR/include) ], [ AC_MSG_ERROR([could not find usable liblz4]) ], [ -L$LIBLZ4_DIR/$PHP_LIBDIR ]) - PHP_SUBST(REDIS_SHARED_LIBADD) else AC_MSG_ERROR([only system liblz4 is supported]) fi @@ -307,6 +308,7 @@ if test "$PHP_REDIS" != "no"; then PHP_CHECK_LIBRARY(zstd, ZSTD_getFrameContentSize, [ PHP_ADD_LIBRARY_WITH_PATH(zstd, $LIBZSTD_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD) + PHP_ADD_INCLUDE($LIBZSTD_DIR/include) ], [ AC_MSG_ERROR([could not find usable libzstd, version 1.3.0 required]) ], [ From 4d233977a5d51e03859165ebfe7098a2354d7cdf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 13 Mar 2024 13:07:13 -0700 Subject: [PATCH 1849/1986] Update stubs --- redis_arginfo.h | 2 +- redis_legacy_arginfo.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redis_arginfo.h b/redis_arginfo.h index c6cc803767..908d60cac3 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: de2f6e77cadba00b1f8312a8244db9df00a74a85 */ + * Stub hash: b3743c9174347f5d783043abfdc5b626159b2364 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index c5506b83e1..b03775296c 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: de2f6e77cadba00b1f8312a8244db9df00a74a85 */ + * Stub hash: b3743c9174347f5d783043abfdc5b626159b2364 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From fa1a283ac96779c5d7fa19b8d57e55f3312eabc8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 13 Mar 2024 13:46:47 -0700 Subject: [PATCH 1850/1986] Fix some typos --- library.c | 2 +- redis.c | 2 +- redis.stub.php | 32 ++++++++++++++++---------------- redis_arginfo.h | 2 +- redis_cluster.c | 6 +++--- redis_commands.c | 2 +- redis_legacy_arginfo.h | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/library.c b/library.c index 8ca6e1c740..d3ad3719e6 100644 --- a/library.c +++ b/library.c @@ -2956,7 +2956,7 @@ static int redis_stream_detect_dirty(php_stream *stream) { if ((fd = redis_stream_fd_for_select(stream)) == -1) return FAILURE; - /* We want to detect a readable socket (it shouln't be) */ + /* We want to detect a readable socket (it shouldn't be) */ REDIS_POLL_FD_SET(pfd, fd, PHP_POLLREADABLE); rv = php_poll2(&pfd, 1, redis_pool_poll_timeout()); diff --git a/redis.c b/redis.c index d7319f2ff0..dfbeebe92a 100644 --- a/redis.c +++ b/redis.c @@ -723,7 +723,7 @@ PHP_METHOD(Redis, reset) } if (IS_PIPELINE(redis_sock)) { - php_error_docref(NULL, E_ERROR, "Reset ins't allowed in pipeline mode!"); + php_error_docref(NULL, E_ERROR, "Reset isn't allowed in pipeline mode!"); RETURN_FALSE; } diff --git a/redis.stub.php b/redis.stub.php index 3393fcbc5b..3b69e0d581 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1472,7 +1472,7 @@ public function getBit(string $key, int $idx): Redis|int|false; * * @return Redis|string|bool The key's value or false if it didn't exist. * - * @see https://redis.io/comands/getex + * @see https://redis.io/commands/getex * * @example $redis->getEx('mykey', ['EX' => 60]); */ @@ -2175,7 +2175,7 @@ public function lrem(string $key, mixed $value, int $count = 0): Redis|int|false public function ltrim(string $key, int $start , int $end): Redis|bool; /** - * Get one ore more string keys. + * Get one or more string keys. * * @param array $keys The keys to retrieve * @return Redis|array|false an array of keys with their values. @@ -2197,7 +2197,7 @@ public function migrate(string $host, int $port, string|array $key, int $dstdb, public function move(string $key, int $index): Redis|bool; /** - * Set one ore more string keys. + * Set one or more string keys. * * @param array $key_values An array with keys and their values. * @return Redis|bool True if the keys could be set. @@ -2209,7 +2209,7 @@ public function move(string $key, int $index): Redis|bool; public function mset(array $key_values): Redis|bool; /** - * Set one ore more string keys but only if none of the key exist. + * Set one or more string keys but only if none of the key exist. * * @param array $key_values An array of keys with their values. * @@ -2445,7 +2445,7 @@ public function punsubscribe(array $patterns): Redis|array|bool; * @param int $count The maximum number of elements to pop at once. * NOTE: The `count` argument requires Redis >= 6.2.0 * - * @return Redis|array|string|bool One ore more popped elements or false if all were empty. + * @return Redis|array|string|bool One or more popped elements or false if all were empty. * * @see https://redis.io/commands/rpop * @@ -2604,7 +2604,7 @@ public function rpoplpush(string $srckey, string $dstkey): Redis|string|false; public function sAdd(string $key, mixed $value, mixed ...$other_values): Redis|int|false; /** - * Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but + * Add one or more values to a Redis SET key. This is an alternative to Redis::sadd() but * instead of being variadic, takes a single array of values. * * @see https://redis.io/commands/sadd @@ -2944,7 +2944,7 @@ public function scard(string $key): Redis|int|false; * @see https://redis.io/commands/script * * @param string $command The script suboperation to execute. - * @param mixed $args One ore more additional argument + * @param mixed $args One or more additional argument * * @return mixed This command returns various things depending on the specific operation executed. * @@ -3598,7 +3598,7 @@ public function watch(array|string $key, string ...$other_keys): Redis|bool; public function wait(int $numreplicas, int $timeout): int|false; /** - * Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but + * Acknowledge one or more messages that are pending (have been consumed using XREADGROUP but * not yet acknowledged by XACK.) * * @param string $key The stream to query. @@ -4105,7 +4105,7 @@ public function zMscore(string $key, mixed $member, mixed ...$other_members): Re * @param string $key The sorted set to pop elements from. * @param int $count An optional count of elements to pop. * - * @return Redis|array|false All of the popped elements with scores or false on fialure. + * @return Redis|array|false All of the popped elements with scores or false on failure * * @see https://redis.io/commands/zpopmax * @@ -4244,7 +4244,7 @@ public function zrangestore(string $dstkey, string $srckey, string $start, strin * 'COUNT' int The number of random members to return. * 'WITHSCORES' bool Whether to return scores and members instead of * - * @return Redis|string|array One ore more random elements. + * @return Redis|string|array One or more random elements. * * @see https://redis.io/commands/zrandmember * @@ -4443,7 +4443,7 @@ public function zScore(string $key, mixed $member): Redis|float|false; * Given one or more sorted set key names, return every element that is in the first * set but not any of the others. * - * @param array $keys One ore more sorted sets. + * @param array $keys One or more sorted sets. * @param array $options An array which can contain ['WITHSCORES' => true] if you want Redis to * return members and scores. * @@ -4479,7 +4479,7 @@ public function zdiffstore(string $dst, array $keys): Redis|int|false; /** * Compute the intersection of one or more sorted sets and return the members * - * @param array $keys One ore more sorted sets. + * @param array $keys One or more sorted sets. * @param array $weights An optional array of weights to be applied to each set when performing * the intersection. * @param array $options Options for how Redis should combine duplicate elements when performing the @@ -4507,7 +4507,7 @@ public function zinter(array $keys, ?array $weights = null, ?array $options = nu * @see https://redis.io/commands/zinter * @see Redis::zinter() * - * @param array $keys One ore more sorted set key names. + * @param array $keys One or more sorted set key names. * @param int $limit An optional upper bound on the returned cardinality. If set to a value * greater than zero, Redis will stop processing the intersection once the * resulting cardinality reaches this limit. @@ -4523,10 +4523,10 @@ public function zinter(array $keys, ?array $weights = null, ?array $options = nu public function zintercard(array $keys, int $limit = -1): Redis|int|false; /** - * Compute the intersection of one ore more sorted sets storing the result in a new sorted set. + * Compute the intersection of one or more sorted sets storing the result in a new sorted set. * * @param string $dst The destination sorted set to store the intersected values. - * @param array $keys One ore more sorted set key names. + * @param array $keys One or more sorted set key names. * @param array $weights An optional array of floats to weight each passed input set. * @param string $aggregate An optional aggregation method to use. * @@ -4576,7 +4576,7 @@ public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int /** * Retrieve the union of one or more sorted sets * - * @param array $keys One ore more sorted set key names + * @param array $keys One or more sorted set key names * @param array $weights An optional array with floating point weights used when performing the union. * Note that if this argument is passed, it must contain the same number of * elements as the $keys array. diff --git a/redis_arginfo.h b/redis_arginfo.h index 908d60cac3..563dffe05b 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b3743c9174347f5d783043abfdc5b626159b2364 */ + * Stub hash: 685a816f4e46c30d8a9ae787948c69d8a20213b1 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") diff --git a/redis_cluster.c b/redis_cluster.c index 0a528e0d6a..a3ee540431 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1042,7 +1042,7 @@ PHP_METHOD(RedisCluster, sdiffstore) { } /* }}} */ -/* {{{ proto bool RedisCluster::smove(sting src, string dst, string mem) */ +/* {{{ proto bool RedisCluster::smove(string src, string dst, string mem) */ PHP_METHOD(RedisCluster, smove) { CLUSTER_PROCESS_CMD(smove, cluster_1_resp, 0); } @@ -2201,7 +2201,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) short slot; int i, argc = ZEND_NUM_ARGS(); - /* Commands using this pass-thru don't need to be enabled in MULTI mode */ + /* Commands using this pass-through don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { php_error_docref(0, E_WARNING, "Command can't be issued in MULTI mode"); @@ -2754,7 +2754,7 @@ PHP_METHOD(RedisCluster, script) { short slot; int argc = ZEND_NUM_ARGS(); - /* Commands using this pass-thru don't need to be enabled in MULTI mode */ + /* Commands using this pass-through don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { php_error_docref(0, E_WARNING, "Command can't be issued in MULTI mode"); diff --git a/redis_commands.c b/redis_commands.c index df726386e5..5a8f46384e 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -5230,7 +5230,7 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } if (slot && db != -1) { - php_error_docref(NULL, E_WARNING, "Cant copy to a specific DB in cluster mode"); + php_error_docref(NULL, E_WARNING, "Can't copy to a specific DB in cluster mode"); return FAILURE; } diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index b03775296c..59149622b5 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b3743c9174347f5d783043abfdc5b626159b2364 */ + * Stub hash: 685a816f4e46c30d8a9ae787948c69d8a20213b1 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From e52f0afaed12bf94768ca5718f8b594c62461b19 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 14 Mar 2024 21:50:28 -0700 Subject: [PATCH 1851/1986] Update SCAN to handle very large cursor values. Technically Redis may return any unsigned 64 bit integer as a scan cursor. This presents a problem for PHP in that PHP's integers are signed. Because of that if a scan cursor is > 2^63 it will overflow and fail to work properly. This commit updates our SCAN family of commands to deliver cursors in their string form. ```php public function scan(null|int|string $iterator, ...); ``` On initial entry into our SCAN family we convert either a NULL or empty string cursor to zero, and send the initial scan command. As Redis replies with cursors we either represent them as a long (if they are <= ZEND_ULONG_MAX) and as a string if greater. This should mean the fix is minimally breaking as the following code will still work: ```php $it = NULL; do { print_r($redis->scan($it)); } while ($it !== 0); ``` The `$it !== 0` still works because the zero cursor will be represented as an integer. Only absurdly large (> 2^63) values are represented as a string. Fixes #2454 --- library.c | 4 +- library.h | 2 +- redis.c | 74 ++++++++++++++++++++++++++---------- redis.stub.php | 8 ++-- redis_arginfo.h | 10 ++--- redis_array.c | 8 ++-- redis_array.stub.php | 8 ++-- redis_array_arginfo.h | 6 +-- redis_array_legacy_arginfo.h | 2 +- redis_legacy_arginfo.h | 2 +- 10 files changed, 79 insertions(+), 45 deletions(-) diff --git a/library.c b/library.c index d3ad3719e6..eafb80bb1b 100644 --- a/library.c +++ b/library.c @@ -401,7 +401,7 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - REDIS_SCAN_TYPE type, zend_long *iter) + REDIS_SCAN_TYPE type, uint64_t *cursor) { REDIS_REPLY_TYPE reply_type; long reply_info; @@ -434,7 +434,7 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Push the iterator out to the caller */ - *iter = atol(p_iter); + *cursor = strtoull(p_iter, NULL, 10); efree(p_iter); /* Read our actual keys/members/etc differently depending on what kind of diff --git a/library.h b/library.h index f240a10e29..8125c5c049 100644 --- a/library.h +++ b/library.h @@ -100,7 +100,7 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, zend_long *iter); +PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, uint64_t *cursor); PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, diff --git a/redis.c b/redis.c index dfbeebe92a..8a1dd062cd 100644 --- a/redis.c +++ b/redis.c @@ -2744,24 +2744,60 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, return cmdstr.len; } +/* Update a zval with the current 64 bit scan cursor. This presents a problem + * because we can only represent up to 63 bits in a PHP integer. So depending + * on the cursor value, we may need to represent it as a string. */ +static void updateScanCursorZVal(zval *zv, uint64_t cursor) { + char tmp[21]; + size_t len; + + ZEND_ASSERT(zv != NULL && (Z_TYPE_P(zv) == IS_LONG || + Z_TYPE_P(zv) == IS_STRING)); + + if (Z_TYPE_P(zv) == IS_STRING) + zend_string_release(Z_STR_P(zv)); + + if (cursor > ZEND_LONG_MAX) { + len = snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)cursor); + ZVAL_STRINGL(zv, tmp, len); + } else { + ZVAL_LONG(zv, cursor); + } +} + +static uint64_t getScanCursorZVal(zval *zv, zend_bool *was_zero) { + ZEND_ASSERT(zv != NULL && (Z_TYPE_P(zv) == IS_LONG || + Z_TYPE_P(zv) == IS_STRING));; + + if (Z_TYPE_P(zv) == IS_STRING) { + *was_zero = Z_STRLEN_P(zv) == 1 && Z_STRVAL_P(zv)[0] == '0'; + return strtoull(Z_STRVAL_P(zv), NULL, 10); + } else { + *was_zero = Z_LVAL_P(zv) == 0; + return Z_LVAL_P(zv); + } +} + /* {{{ proto redis::scan(&$iterator, [pattern, [count, [type]]]) */ PHP_REDIS_API void generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { - zval *object, *z_iter; + zval *object, *z_cursor; RedisSock *redis_sock; HashTable *hash; char *pattern = NULL, *cmd, *key = NULL; int cmd_len, num_elements, key_free = 0, pattern_free = 0; size_t key_len = 0, pattern_len = 0; zend_string *match_type = NULL; - zend_long count = 0, iter; + zend_long count = 0; + zend_bool completed; + uint64_t cursor; /* Different prototype depending on if this is a key based scan */ if(type != TYPE_SCAN) { // Requires a key if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os!z/|s!l", &object, redis_ce, &key, - &key_len, &z_iter, &pattern, + &key_len, &z_cursor, &pattern, &pattern_len, &count)==FAILURE) { RETURN_FALSE; @@ -2769,7 +2805,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } else { // Doesn't require a key if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Oz/|s!lS!", &object, redis_ce, &z_iter, + "Oz/|s!lS!", &object, redis_ce, &z_cursor, &pattern, &pattern_len, &count, &match_type) == FAILURE) { @@ -2789,19 +2825,17 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { RETURN_FALSE; } - // The iterator should be passed in as NULL for the first iteration, but we - // can treat any NON LONG value as NULL for these purposes as we've - // separated the variable anyway. - if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter) < 0) { - /* Convert to long */ - convert_to_long(z_iter); - iter = 0; - } else if(Z_LVAL_P(z_iter) != 0) { - /* Update our iterator value for the next passthru */ - iter = Z_LVAL_P(z_iter); + /* If our cursor is NULL (it can only be null|int|string), convert it to a + * long and initialize it to zero for oure initial SCAN. Otherwise et the + * uint64_t value from the zval which can either be in the form of a long or + * a string (if the cursor is too large to fit in a zend_long). */ + if (Z_TYPE_P(z_cursor) == IS_NULL) { + convert_to_long(z_cursor); + cursor = 0; } else { - /* We're done, back to iterator zero */ - RETURN_FALSE; + cursor = getScanCursorZVal(z_cursor, &completed); + if (completed) + RETURN_FALSE; } /* Prefix our key if we've got one and we have a prefix set */ @@ -2830,13 +2864,13 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } // Format our SCAN command - cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (long)iter, + cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (long)cursor, pattern, pattern_len, count, match_type); /* Execute our command getting our new iterator value */ REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock,type,&iter) < 0) + redis_sock,type, &cursor) < 0) { if(key_free) efree(key); RETURN_FALSE; @@ -2845,7 +2879,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Get the number of elements */ hash = Z_ARRVAL_P(return_value); num_elements = zend_hash_num_elements(hash); - } while (redis_sock->scan & REDIS_SCAN_RETRY && iter != 0 && + } while (redis_sock->scan & REDIS_SCAN_RETRY && cursor != 0 && num_elements == 0); /* Free our pattern if it was prefixed */ @@ -2855,7 +2889,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { if(key_free) efree(key); /* Update our iterator reference */ - Z_LVAL_P(z_iter) = iter; + updateScanCursorZVal(z_cursor, cursor); } PHP_METHOD(Redis, scan) { diff --git a/redis.stub.php b/redis.stub.php index 3b69e0d581..6818b6d39d 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1880,7 +1880,7 @@ public function hVals(string $key): Redis|array|false; * } * } while ($it != 0); */ - public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool; + public function hscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool; /** * Increment a key's value, optionally by a specific amount. @@ -2922,7 +2922,7 @@ public function save(): Redis|bool; * } * } */ - public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, ?string $type = null): array|false; + public function scan(null|int|string &$iterator, ?string $pattern = null, int $count = 0, ?string $type = null): array|false; /** * Retrieve the number of members in a Redis set. @@ -3312,7 +3312,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * } * } */ - public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false; + public function sscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): array|false; /** * Subscribes the client to the specified shard channels. @@ -4571,7 +4571,7 @@ public function zinterstore(string $dst, array $keys, ?array $weights = null, ?s * NOTE: See Redis::scan() for detailed example code on how to call SCAN like commands. * */ - public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|false; + public function zscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): Redis|array|false; /** * Retrieve the union of one or more sorted sets diff --git a/redis_arginfo.h b/redis_arginfo.h index 563dffe05b..623d6b1d29 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 685a816f4e46c30d8a9ae787948c69d8a20213b1 */ + * Stub hash: d6839707b66ecf4460374deea10a528bf0c5ea41 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -458,7 +458,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -757,7 +757,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_save arginfo_class_Redis_bgSave ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 1, "null") @@ -850,7 +850,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sscan, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -1150,7 +1150,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() diff --git a/redis_array.c b/redis_array.c index 53ad4eb2ca..bc64047509 100644 --- a/redis_array.c +++ b/redis_array.c @@ -1123,11 +1123,11 @@ ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, int kw_len) { RedisArray *ra; zend_string *key, *pattern = NULL; - zval *object, *redis_inst, *z_iter, z_fun, z_args[4]; + zval *object, *redis_inst, *z_cursor, z_fun, z_args[4]; zend_long count = 0; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OSz/|S!l", - &object, redis_array_ce, &key, &z_iter, &pattern, &count) == FAILURE) { + &object, redis_array_ce, &key, &z_cursor, &pattern, &count) == FAILURE) { RETURN_FALSE; } @@ -1141,7 +1141,7 @@ ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, int kw_len) } ZVAL_STR(&z_args[0], key); - ZVAL_NEW_REF(&z_args[1], z_iter); + ZVAL_NEW_REF(&z_args[1], z_cursor); if (pattern) ZVAL_STR(&z_args[2], pattern); ZVAL_LONG(&z_args[3], count); @@ -1149,7 +1149,7 @@ ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, int kw_len) call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, ZEND_NUM_ARGS(), z_args); zval_dtor(&z_fun); - ZVAL_ZVAL(z_iter, &z_args[1], 0, 1); + ZVAL_ZVAL(z_cursor, &z_args[1], 0, 1); } PHP_METHOD(RedisArray, hscan) diff --git a/redis_array.stub.php b/redis_array.stub.php index b863338b24..9312b58008 100644 --- a/redis_array.stub.php +++ b/redis_array.stub.php @@ -40,7 +40,7 @@ public function flushdb(): bool|array; public function getOption(int $opt): bool|array; - public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array; + public function hscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): bool|array; public function info(): bool|array; @@ -56,17 +56,17 @@ public function ping(): bool|array; public function save(): bool|array; - public function scan(?int &$iterator, string $node, ?string $pattern = null, int $count = 0): bool|array; + public function scan(null|int|string &$iterator, string $node, ?string $pattern = null, int $count = 0): bool|array; public function select(int $index): bool|array; public function setOption(int $opt, string $value): bool|array; - public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array; + public function sscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): bool|array; public function unlink(string|array $key, string ...$otherkeys): bool|int; public function unwatch(): bool|null; - public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array; + public function zscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): bool|array; } diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h index 06064ecda4..ab1ac840e3 100644 --- a/redis_array_arginfo.h +++ b/redis_array_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 59943eeb14b3ed78f88117e6923d64a95911b5ff */ + * Stub hash: ddb92422452cb767a7d6694aa8ac60d883db6672 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0) @@ -57,7 +57,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -86,7 +86,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_save arginfo_class_RedisArray__continuum ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO(0, node, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h index cde854f50b..7fe38aa7dc 100644 --- a/redis_array_legacy_arginfo.h +++ b/redis_array_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 59943eeb14b3ed78f88117e6923d64a95911b5ff */ + * Stub hash: ddb92422452cb767a7d6694aa8ac60d883db6672 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2) ZEND_ARG_INFO(0, function_name) diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 59149622b5..87522686b0 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 685a816f4e46c30d8a9ae787948c69d8a20213b1 */ + * Stub hash: d6839707b66ecf4460374deea10a528bf0c5ea41 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From 2612d444e51397b5990f4bcf1b44e0c0cadde7c4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 17 Mar 2024 22:46:32 -0700 Subject: [PATCH 1852/1986] Update RedisCluster scan logic for large SCAN cursors. We also need to update the `RedisCluster` logic to handle very large curosr values, in addition to handling them for the `Redis` and `RedisArray` classes. See #2454, #2458 --- cluster_library.c | 10 +++---- cluster_library.h | 2 +- library.c | 41 +++++++++++++++++++++++++++ library.h | 4 ++- redis.c | 52 ++++------------------------------ redis_cluster.c | 51 ++++++++++++++------------------- redis_cluster.stub.php | 8 +++--- redis_cluster_arginfo.h | 10 +++---- redis_cluster_legacy_arginfo.h | 2 +- 9 files changed, 87 insertions(+), 93 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index ea19e6427a..9258fca9ce 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2279,8 +2279,9 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* HSCAN, SSCAN, ZSCAN */ -PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - REDIS_SCAN_TYPE type, long *it) +PHP_REDIS_API int +cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + REDIS_SCAN_TYPE type, uint64_t *cursor) { char *pit; @@ -2304,12 +2305,11 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * } // Push the new iterator value to our caller - *it = atol(pit); + *cursor = strtoull(pit, NULL, 10); efree(pit); // We'll need another MULTIBULK response for the payload - if (cluster_check_response(c, &c->reply_type) < 0) - { + if (cluster_check_response(c, &c->reply_type) < 0) { return FAILURE; } diff --git a/cluster_library.h b/cluster_library.h index 59e490e24e..c2fd850221 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -488,7 +488,7 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, /* Response handler for ZSCAN, SSCAN, and HSCAN */ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, REDIS_SCAN_TYPE type, long *it); + redisCluster *c, REDIS_SCAN_TYPE type, uint64_t *cursor); /* INFO response handler */ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, diff --git a/library.c b/library.c index eafb80bb1b..3d65c8529d 100644 --- a/library.c +++ b/library.c @@ -4512,4 +4512,45 @@ void redis_conf_auth(HashTable *ht, const char *key, size_t keylen, redis_extract_auth_info(zv, user, pass); } +/* Update a zval with the current 64 bit scan cursor. This presents a problem + * because we can only represent up to 63 bits in a PHP integer. So depending + * on the cursor value, we may need to represent it as a string. */ +void redisSetScanCursor(zval *zv, uint64_t cursor) { + char tmp[21]; + size_t len; + + ZEND_ASSERT(zv != NULL && (Z_TYPE_P(zv) == IS_LONG || + Z_TYPE_P(zv) == IS_STRING)); + + if (Z_TYPE_P(zv) == IS_STRING) + zend_string_release(Z_STR_P(zv)); + + if (cursor > ZEND_LONG_MAX) { + len = snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)cursor); + ZVAL_STRINGL(zv, tmp, len); + } else { + ZVAL_LONG(zv, cursor); + } +} + +/* Get a Redis SCAN cursor value out of a zval. These are always taken as a + * reference argument that that must be `null`, `int`, or `string`. */ +uint64_t redisGetScanCursor(zval *zv, zend_bool *was_zero) { + ZEND_ASSERT(zv != NULL && (Z_TYPE_P(zv) == IS_LONG || + Z_TYPE_P(zv) == IS_STRING || + Z_TYPE_P(zv) == IS_NULL)); + + if (Z_TYPE_P(zv) == IS_NULL) { + convert_to_long(zv); + *was_zero = 0; + return 0; + } else if (Z_TYPE_P(zv) == IS_STRING) { + *was_zero = Z_STRLEN_P(zv) == 1 && Z_STRVAL_P(zv)[0] == '0'; + return strtoull(Z_STRVAL_P(zv), NULL, 10); + } else { + *was_zero = Z_LVAL_P(zv) == 0; + return Z_LVAL_P(zv); + } +} + /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/library.h b/library.h index 8125c5c049..f758c33bc2 100644 --- a/library.h +++ b/library.h @@ -100,8 +100,10 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, uint64_t *cursor); +void redisSetScanCursor(zval *zv, uint64_t cursor); +uint64_t redisGetScanCursor(zval *zv, zend_bool *was_zero); +PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, uint64_t *cursor); PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); diff --git a/redis.c b/redis.c index 8a1dd062cd..ec6f65d2ed 100644 --- a/redis.c +++ b/redis.c @@ -2744,40 +2744,6 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, return cmdstr.len; } -/* Update a zval with the current 64 bit scan cursor. This presents a problem - * because we can only represent up to 63 bits in a PHP integer. So depending - * on the cursor value, we may need to represent it as a string. */ -static void updateScanCursorZVal(zval *zv, uint64_t cursor) { - char tmp[21]; - size_t len; - - ZEND_ASSERT(zv != NULL && (Z_TYPE_P(zv) == IS_LONG || - Z_TYPE_P(zv) == IS_STRING)); - - if (Z_TYPE_P(zv) == IS_STRING) - zend_string_release(Z_STR_P(zv)); - - if (cursor > ZEND_LONG_MAX) { - len = snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)cursor); - ZVAL_STRINGL(zv, tmp, len); - } else { - ZVAL_LONG(zv, cursor); - } -} - -static uint64_t getScanCursorZVal(zval *zv, zend_bool *was_zero) { - ZEND_ASSERT(zv != NULL && (Z_TYPE_P(zv) == IS_LONG || - Z_TYPE_P(zv) == IS_STRING));; - - if (Z_TYPE_P(zv) == IS_STRING) { - *was_zero = Z_STRLEN_P(zv) == 1 && Z_STRVAL_P(zv)[0] == '0'; - return strtoull(Z_STRVAL_P(zv), NULL, 10); - } else { - *was_zero = Z_LVAL_P(zv) == 0; - return Z_LVAL_P(zv); - } -} - /* {{{ proto redis::scan(&$iterator, [pattern, [count, [type]]]) */ PHP_REDIS_API void generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { @@ -2825,18 +2791,10 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { RETURN_FALSE; } - /* If our cursor is NULL (it can only be null|int|string), convert it to a - * long and initialize it to zero for oure initial SCAN. Otherwise et the - * uint64_t value from the zval which can either be in the form of a long or - * a string (if the cursor is too large to fit in a zend_long). */ - if (Z_TYPE_P(z_cursor) == IS_NULL) { - convert_to_long(z_cursor); - cursor = 0; - } else { - cursor = getScanCursorZVal(z_cursor, &completed); - if (completed) - RETURN_FALSE; - } + /* Get our SCAN cursor short circuiting if we're done */ + cursor = redisGetScanCursor(z_cursor, &completed); + if (completed) + RETURN_FALSE; /* Prefix our key if we've got one and we have a prefix set */ if(key_len) { @@ -2889,7 +2847,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { if(key_free) efree(key); /* Update our iterator reference */ - updateScanCursorZVal(z_cursor, cursor); + redisSetScanCursor(z_cursor, cursor); } PHP_METHOD(Redis, scan) { diff --git a/redis_cluster.c b/redis_cluster.c index a3ee540431..599449ad9e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2266,8 +2266,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, short slot; zval *z_it; HashTable *hash; - long it, num_ele; + long num_ele; zend_long count = 0; + zend_bool complted; + uint64_t cursor; // Can't be in MULTI mode if (!CLUSTER_IS_ATOMIC(c)) { @@ -2285,16 +2287,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, /* Treat as readonly */ c->readonly = 1; - // Convert iterator to long if it isn't, update our long iterator if it's - // set and >0, and finish if it's back to zero - if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) { - convert_to_long(z_it); - it = 0; - } else if (Z_LVAL_P(z_it) != 0) { - it = Z_LVAL_P(z_it); - } else { + /* Get our scan cursor and return early if we're done */ + cursor = redisGetScanCursor(z_it, &complted); + if (complted) RETURN_FALSE; - } // Apply any key prefix we have, get the slot key_free = redis_key_prefix(c->flags, &key, &key_len); @@ -2314,7 +2310,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, } // Create command - cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, it, pat, pat_len, + cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, cursor, pat, pat_len, count); // Send it off @@ -2328,7 +2324,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, // Read response if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, - &it) == FAILURE) + &cursor) == FAILURE) { CLUSTER_THROW_EXCEPTION("Couldn't read SCAN response", 0); if (key_free) efree(key); @@ -2342,7 +2338,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, // Free our command efree(cmd); - } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0); + } while (c->flags->scan & REDIS_SCAN_RETRY && cursor != 0 && num_ele == 0); // Free our pattern if (pat_free) efree(pat); @@ -2351,7 +2347,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, if (key_free) efree(key); // Update iterator reference - Z_LVAL_P(z_it) = it; + redisSetScanCursor(z_it, cursor); } static int redis_acl_op_readonly(zend_string *op) { @@ -2445,9 +2441,11 @@ PHP_METHOD(RedisCluster, scan) { size_t pat_len = 0; int cmd_len; short slot; - zval *z_it, *z_node; - long it, num_ele, pat_free = 0; + zval *zcursor, *z_node; + long num_ele, pat_free = 0; zend_long count = 0; + zend_bool completed; + uint64_t cursor; /* Treat as read-only */ c->readonly = CLUSTER_IS_ATOMIC(c); @@ -2459,21 +2457,16 @@ PHP_METHOD(RedisCluster, scan) { } /* Parse arguments */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/z|s!l", &z_it, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/z|s!l", &zcursor, &z_node, &pat, &pat_len, &count) == FAILURE) { RETURN_FALSE; } - /* Convert or update iterator */ - if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) { - convert_to_long(z_it); - it = 0; - } else if (Z_LVAL_P(z_it) != 0) { - it = Z_LVAL_P(z_it); - } else { + /* Get the scan cursor and return early if we're done */ + cursor = redisGetScanCursor(zcursor, &completed); + if (completed) RETURN_FALSE; - } if (c->flags->scan & REDIS_SCAN_PREFIX) { pat_free = redis_key_prefix(c->flags, &pat, &pat_len); @@ -2489,7 +2482,7 @@ PHP_METHOD(RedisCluster, scan) { } /* Construct our command */ - cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, + cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, cursor, pat, pat_len, count); if ((slot = cluster_cmd_get_slot(c, z_node)) < 0) { @@ -2505,7 +2498,7 @@ PHP_METHOD(RedisCluster, scan) { } if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, TYPE_SCAN, - &it) == FAILURE || Z_TYPE_P(return_value)!=IS_ARRAY) + &cursor) == FAILURE || Z_TYPE_P(return_value) != IS_ARRAY) { CLUSTER_THROW_EXCEPTION("Couldn't process SCAN response from node", 0); efree(cmd); @@ -2515,11 +2508,11 @@ PHP_METHOD(RedisCluster, scan) { efree(cmd); num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value)); - } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0); + } while (c->flags->scan & REDIS_SCAN_RETRY && cursor != 0 && num_ele == 0); if (pat_free) efree(pat); - Z_LVAL_P(z_it) = it; + redisSetScanCursor(zcursor, cursor); } /* }}} */ diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 408f876046..0210b4d074 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -488,7 +488,7 @@ public function hmset(string $key, array $key_values): RedisCluster|bool; /** * @see Redis::hscan */ - public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|bool; + public function hscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): array|bool; /** * @see https://redis.io/commands/hrandfield @@ -787,7 +787,7 @@ public function save(string|array $key_or_address): RedisCluster|bool; /** * @see Redis::scan */ - public function scan(?int &$iterator, string|array $key_or_address, ?string $pattern = null, int $count = 0): bool|array; + public function scan(null|int|string &$iterator, string|array $key_or_address, ?string $pattern = null, int $count = 0): bool|array; /** * @see Redis::scard @@ -907,7 +907,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): RedisCl /** * @see Redis::sscan */ - public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false; + public function sscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): array|false; /** * @see Redis::strlen @@ -1154,7 +1154,7 @@ public function zrevrank(string $key, mixed $member): RedisCluster|int|false; /** * @see Redis::zscan */ - public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): RedisCluster|bool|array; + public function zscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): RedisCluster|bool|array; /** * @see Redis::zscore diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 839595c527..5a66276a0d 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 35b71fe87bbd8df3a7495e14be957b18c3241a19 */ + * Stub hash: c19108e54b637b6c76a529c1285104a0c38da220 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -406,7 +406,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -660,7 +660,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_save arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") @@ -762,7 +762,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sscan, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -1004,7 +1004,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zscan, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 1cc6b7cda5..137dc7c5b9 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 35b71fe87bbd8df3a7495e14be957b18c3241a19 */ + * Stub hash: c19108e54b637b6c76a529c1285104a0c38da220 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) From a51215ce2b22bcd1f506780c35b6833471e0b8cb Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 18 Mar 2024 14:42:35 -0700 Subject: [PATCH 1853/1986] Update random includes. PHP 8.4 has some breaking changes with respect to where PHP's random methods and helpers are. This commit fixes those issues while staying backward compatible. Fixes #2463 --- backoff.c | 12 ++++++------ library.c | 7 ++++++- redis.c | 6 +++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/backoff.c b/backoff.c index d0961fcfaf..1be04a8fe8 100644 --- a/backoff.c +++ b/backoff.c @@ -1,14 +1,14 @@ #include "common.h" +#if PHP_VERSION_ID < 80400 #include - -#if PHP_VERSION_ID >= 70100 -#include #else +#include +#endif + +#if PHP_VERSION_ID < 70100 static zend_long php_mt_rand_range(zend_long min, zend_long max) { - zend_long number = php_rand(); - RAND_RANGE(number, min, max, PHP_RAND_MAX); - return number; + return min + php_rand() % (max - min + 1) } #endif diff --git a/library.c b/library.c index 3d65c8529d..f81556a9ed 100644 --- a/library.c +++ b/library.c @@ -56,9 +56,14 @@ #include #endif -#include #include +#if PHP_VERSION_ID < 80400 +#include +#else +#include +#endif + #define UNSERIALIZE_NONE 0 #define UNSERIALIZE_KEYS 1 #define UNSERIALIZE_VALS 2 diff --git a/redis.c b/redis.c index ec6f65d2ed..2330bf7edf 100644 --- a/redis.c +++ b/redis.c @@ -27,12 +27,16 @@ #include "redis_cluster.h" #include "redis_commands.h" #include "redis_sentinel.h" -#include #include #include #include #include +#if PHP_VERSION_ID < 80400 +#include +#else +#include +#endif #ifdef PHP_SESSION #include From 3dbc2bd814f1c09c549349cc596c0143fb80e73e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 17 Mar 2024 16:38:42 -0700 Subject: [PATCH 1854/1986] Don't use deprecated string interpolation syntax. --- tests/TestSuite.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 1c4663bb0e..b0161e8a1e 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -239,7 +239,7 @@ private static function findFile($path, $file) { /* Small helper method that tries to load a custom test case class */ public static function loadTestClass($class) { - $filename = "${class}.php"; + $filename = "{$class}.php"; if (($sp = getenv('PHPREDIS_TEST_SEARCH_PATH'))) { $fullname = self::findFile($sp, $filename); From 8a39caebe89a81e1dbd0cc8f66ca49ed0eb7bd06 Mon Sep 17 00:00:00 2001 From: Martin Vancl Date: Thu, 30 Mar 2023 12:11:17 +0200 Subject: [PATCH 1855/1986] add: session.save_path examples --- README.md | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 757337193f..999a2e5c9f 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,7 @@ see the [INSTALL.md](./INSTALL.md) page. ## PHP Session handler -phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions: -~~~ -session.save_handler = redis -session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2&read_timeout=2.5" -~~~ +phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions. `session.save_path` can have a simple `host:port` format too, but you need to provide the `tcp://` scheme if you want to use the parameters. The following parameters are available: @@ -84,6 +80,26 @@ Sessions have a lifetime expressed in seconds and stored in the INI variable "se The session handler requires a version of Redis supporting `EX` and `NX` options of `SET` command (at least 2.6.12). phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0"`. +### Examples + +Multiple Redis servers: +~~~ +session.save_handler = redis +session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2&read_timeout=2.5" +~~~ + +Login to Redis using username and password: +~~~ +session.save_handler = redis +session.save_path = "tcp://127.0.0.1:6379?auth[]=user&auth[]=password" +~~~ + +Login to Redis using username, password, and set prefix: +~~~ +session.save_handler = redis +session.save_path = "tcp://127.0.0.1:6379?auth[]=user&auth[]=password&prefix=user_PHPREDIS_SESSION:" +~~~ + ### Session locking **Support**: Locking feature is currently only supported for Redis setup with single master instance (e.g. classic master/slave Sentinel environment). From a9e53fd16e0eeb411125e36fa1e22f931f4c7916 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Thu, 21 Mar 2024 15:09:42 +0200 Subject: [PATCH 1856/1986] Fix segfault and remove redundant macros Replace `SOCKET_WRITE_COMMAND` with `redis_sock_write` because it can't be used with pre-defined commands (it frees memory in case of failed writing operation). After replacement `SOCKET_WRITE_COMMAND` becomes redundant so remove it. --- common.h | 11 +++-------- redis.c | 9 ++++++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/common.h b/common.h index 8e2ec57ab8..c4e2ecb521 100644 --- a/common.h +++ b/common.h @@ -186,12 +186,6 @@ typedef enum { } \ } while (0) -#define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) \ - if(redis_sock_write(redis_sock, cmd, cmd_len) < 0) { \ - efree(cmd); \ - RETURN_FALSE; \ -} - #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \ fold_item *fi = malloc(sizeof(fold_item)); \ fi->fun = callback; \ @@ -209,8 +203,9 @@ typedef enum { #define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \ if (IS_PIPELINE(redis_sock)) { \ PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \ - } else { \ - SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \ + } else if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { \ + efree(cmd); \ + RETURN_FALSE; \ } \ efree(cmd); diff --git a/redis.c b/redis.c index 2330bf7edf..b34667dc1a 100644 --- a/redis.c +++ b/redis.c @@ -1899,7 +1899,9 @@ PHP_METHOD(Redis, multi) REDIS_SAVE_CALLBACK(NULL, NULL); REDIS_ENABLE_MODE(redis_sock, MULTI); } else { - SOCKET_WRITE_COMMAND(redis_sock, RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD) - 1) + if (redis_sock_write(redis_sock, ZEND_STRL(RESP_MULTI_CMD)) < 0) { + RETURN_FALSE; + } if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) { RETURN_FALSE; } else if (strncmp(resp, "+OK", 3) != 0) { @@ -1995,8 +1997,9 @@ PHP_METHOD(Redis, exec) REDIS_DISABLE_MODE(redis_sock, MULTI); RETURN_ZVAL(getThis(), 1, 0); } - SOCKET_WRITE_COMMAND(redis_sock, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1) - + if (redis_sock_write(redis_sock, ZEND_STRL(RESP_EXEC_CMD)) < 0) { + RETURN_FALSE; + } ret = redis_sock_read_multibulk_multi_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret); free_reply_callbacks(redis_sock); From c7a73abbd572ac297f3c62a06cce53f8174d9c8a Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 21 Mar 2024 11:43:10 +0300 Subject: [PATCH 1857/1986] Remove mention of pickle --- INSTALL.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index c2ab82fbab..cabb6e9064 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,12 +1,9 @@ -# Installation from pecl/pickle +# Installation from pecl -To pull latest stable released version, from [pecl](https://pecl.php.net/package/redis) / [pickle](https://wiki.php.net/rfc/deprecate-pear-include-composer): +To pull latest stable released version, from [pecl](https://pecl.php.net/package/redis) ~~~ pecl install redis - -// If using PHP >= 7.3 -pickle install redis ~~~ # Installation from sources From d9c48b788da407423515fb9b6d08c7a297e5ab3e Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 21 Mar 2024 14:34:33 -0700 Subject: [PATCH 1858/1986] KeyDB: Get our tests passing against KeyDB. This commit fixes our unit tests so they also pass against the KeyDB server. We didn't ned to change all that much. Most of it was just adding a version/keydb check. The only change to PhpRedis itself was to relax the reply requirements for XAUTOCLAIM. Redis 7.0.0 added a third "these elements were recently removed" reply which KeyDB does not have. Fixes #2466 --- library.c | 13 ++++++++----- tests/RedisClusterTest.php | 1 + tests/RedisTest.php | 34 ++++++++++++++++++++++++---------- tests/TestSuite.php | 1 + tests/make-cluster.sh | 7 ++++--- 5 files changed, 38 insertions(+), 18 deletions(-) diff --git a/library.c b/library.c index f81556a9ed..ce0cbda263 100644 --- a/library.c +++ b/library.c @@ -2310,9 +2310,9 @@ redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zva zval z_msgs = {0}; char *id = NULL; long id_len = 0; - int messages; + int messages = 0; - ZEND_ASSERT(!is_xautoclaim || count == 3); + ZEND_ASSERT(!is_xautoclaim || (count == 2 || count == 3)); ZVAL_UNDEF(rv); @@ -2338,15 +2338,18 @@ redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zva if (is_xautoclaim) { zval z_deleted = {0}; - if (redis_sock_read_multibulk_reply_zval(redis_sock, &z_deleted) == NULL) + if (count == 3 && redis_sock_read_multibulk_reply_zval(redis_sock, &z_deleted) == NULL) goto failure; array_init(rv); - // Package up ID, message, and deleted messages in our reply + // Package up ID and message add_next_index_stringl(rv, id, id_len); add_next_index_zval(rv, &z_msgs); - add_next_index_zval(rv, &z_deleted); + + // Add deleted messages if they exist + if (count == 3) + add_next_index_zval(rv, &z_deleted); efree(id); } else { diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 9c9eebf9ff..2d6caa596f 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -94,6 +94,7 @@ public function setUp() { $this->redis = $this->newInstance(); $info = $this->redis->info(uniqid()); $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); + $this->is_keydb = $this->redis->info('keydb') !== false; } /* Override newInstance as we want a RedisCluster object */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 9d1701524e..2ead39686a 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -75,6 +75,8 @@ public function setUp() { $this->redis = $this->newInstance(); $info = $this->redis->info(); $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); + + $this->is_keydb = $this->redis->info('keydb') !== false; } protected function minVersionCheck($version) { @@ -265,9 +267,11 @@ public function testBitcount() { $this->redis->set('bitcountkey', hex2bin('10eb8939e68bfdb640260f0629f3')); $this->assertEquals(1, $this->redis->bitcount('bitcountkey', 8, 8, false)); - /* key, start, end, BIT */ - $this->redis->set('bitcountkey', hex2bin('cd0e4c80f9e4590d888a10')); - $this->assertEquals(5, $this->redis->bitcount('bitcountkey', 0, 9, true)); + if ( ! $this->is_keydb) { + /* key, start, end, BIT */ + $this->redis->set('bitcountkey', hex2bin('cd0e4c80f9e4590d888a10')); + $this->assertEquals(5, $this->redis->bitcount('bitcountkey', 0, 9, true)); + } } public function testBitop() { @@ -331,6 +335,8 @@ public function testBitsets() { } public function testLcs() { + if ( ! $this->minVersionCheck('7.0.0') || $this->is_keydb) + $this->markTestSkipped(); $key1 = '{lcs}1'; $key2 = '{lcs}2'; $this->assertTrue($this->redis->set($key1, '12244447777777')); @@ -7094,7 +7100,12 @@ public function testXAutoClaim() { // Test an empty xautoclaim reply $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); - $this->assertEquals(['0-0', [], []], $res); + $this->assertTrue(is_array($res) && (count($res) == 2 || count($res) == 3)); + if (count($res) == 2) { + $this->assertEquals(['0-0', []], $res); + } else { + $this->assertEquals(['0-0', [], []], $res); + } $this->redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); @@ -7108,9 +7119,9 @@ public function testXAutoClaim() { // Assume control of the pending message with a different consumer. $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); - $this->assertTrue($res && count($res) == 3 && $res[0] == '0-0' && - isset($res[1]['1424-74205']['name']) && - $res[1]['1424-74205']['name'] == 'Defiant'); + $this->assertTrue($res && (count($res) == 2 || count($res) == 3)); + $this->assertTrue(isset($res[1]['1424-74205']['name']) && + $res[1]['1424-74205']['name'] == 'Defiant'); // Now the 'Sisko' consumer should own the message $pending = $this->redis->xPending('ships', 'combatants'); @@ -7640,9 +7651,12 @@ public function testCommand() $commands = $this->redis->command(); $this->assertTrue(is_array($commands)); $this->assertEquals(count($commands), $this->redis->command('count')); - $infos = $this->redis->command('info'); - $this->assertTrue(is_array($infos)); - $this->assertEquals(count($infos), count($commands)); + + if (!$this->is_keydb) { + $infos = $this->redis->command('info'); + $this->assertTrue(is_array($infos)); + $this->assertEquals(count($infos), count($commands)); + } if (version_compare($this->version, '7.0') >= 0) { $docs = $this->redis->command('docs'); diff --git a/tests/TestSuite.php b/tests/TestSuite.php index b0161e8a1e..aa467019f2 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -15,6 +15,7 @@ class TestSuite /* Redis server version */ protected $version; + protected $is_keydb; private static $_boo_colorize = false; diff --git a/tests/make-cluster.sh b/tests/make-cluster.sh index 244cc59a7b..e7ce5c7a2a 100755 --- a/tests/make-cluster.sh +++ b/tests/make-cluster.sh @@ -13,6 +13,8 @@ BASEDIR=`pwd` NODEDIR=$BASEDIR/nodes MAPFILE=$NODEDIR/nodemap +REDIS_BINARY=${REDIS_BINARY:-redis-server} + # Host, nodes, replicas, ports, etc. Change if you want different values HOST="127.0.0.1" NOASK=0 @@ -43,7 +45,7 @@ spawnNode() { fi # Attempt to spawn the node - verboseRun redis-server --cluster-enabled yes --dir $NODEDIR --port $PORT \ + verboseRun "$REDIS_BINARY" --cluster-enabled yes --dir $NODEDIR --port $PORT \ --cluster-config-file node-$PORT.conf --daemonize yes --save \'\' \ --bind $HOST --dbfilename node-$PORT.rdb $ACLARG @@ -167,8 +169,7 @@ printUsage() { exit 0 } -# We need redis-server -checkExe redis-server +checkExe "$REDIS_BINARY" while getopts "u:p:a:hy" OPT; do case $OPT in From 54d62c7240a2e3747e523250fcdc78ab1518c0d6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Fri, 22 Mar 2024 09:46:56 -0700 Subject: [PATCH 1859/1986] Add KeyDB to CI See: #2466 --- .github/workflows/ci.yml | 70 +++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91dd88058e..6b26475a30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,6 +74,8 @@ jobs: fail-fast: false matrix: php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] + server: ['redis-server', 'keydb-server'] + steps: - name: Checkout uses: actions/checkout@v4 @@ -86,42 +88,88 @@ jobs: extensions: json, igbinary, msgpack, :redis coverage: none tools: none - - name: Install dependencies + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install valgrind libzstd-dev liblz4-dev + - name: Install Redis + env: + REDIS_PPA_URI: "packages.redis.io/deb" + REDIS_PPA_KEY: "packages.redis.io/gpg" run: | - curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg - echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list + echo "deb https://$REDIS_PPA_URI $(lsb_release -cs) main" | \ + sudo tee /etc/apt/sources.list.d/redis.list + curl -fsSL "https://$REDIS_PPA_KEY" | \ + sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/redis.gpg sudo apt-get update - sudo apt --fix-broken install - sudo apt-get install redis valgrind libzstd-dev liblz4-dev + sudo apt-get install redis + + - name: Install KeyDB + env: + KEYDB_PPA_URI: "download.keydb.dev/open-source-dist" + KEYDB_PPA_KEY: "download.keydb.dev/open-source-dist/keyring.gpg" + run: | + echo "deb https://$KEYDB_PPA_URI $(lsb_release -sc) main" | \ + sudo tee /etc/apt/sources.list.d/keydb.list + sudo wget -O /etc/apt/trusted.gpg.d/keydb.gpg "https://$KEYDB_PPA_KEY" + sudo apt-get update + sudo apt-get install keydb - name: Build phpredis run: | phpize - ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4 + ./configure \ + --enable-redis-lzf \ + --enable-redis-zstd \ + --enable-redis-igbinary \ + --enable-redis-msgpack \ + --enable-redis-lz4 \ + --with-liblz4 sudo make -j"$(nproc)" install + echo 'extension = redis.so' | sudo tee -a "$(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')"/90-redis.ini - name: Start redis run: | redis-cli SHUTDOWN NOSAVE for PORT in $(seq 6379 6382) $(seq 32767 32769); do - redis-server --port "$PORT" --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels + ${{ matrix.server }} \ + --port "$PORT" \ + --daemonize yes \ + --aclfile tests/users.acl \ + --acl-pubsub-default allchannels done - redis-server --port 0 --unixsocket /tmp/redis.sock --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels + ${{ matrix.server }} \ + --port 0 \ + --unixsocket /tmp/redis.sock \ + --daemonize yes \ + --aclfile tests/users.acl \ + --acl-pubsub-default allchannels - name: Start redis cluster run: | mkdir -p tests/nodes echo -n > tests/nodes/nodemap for PORT in $(seq 7000 7005); do - redis-server --port "$PORT" --cluster-enabled yes --cluster-config-file "$PORT".conf --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels + ${{ matrix.server }} \ + --port "$PORT" \ + --cluster-enabled yes \ + --cluster-config-file "$PORT".conf \ + --daemonize yes \ + --aclfile tests/users.acl \ + --acl-pubsub-default allchannels echo 127.0.0.1:"$PORT" >> tests/nodes/nodemap done - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7005) --cluster-replicas 1 --user phpredis -a phpredis + echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7005) \ + --cluster-replicas 1 --user phpredis -a phpredis - name: Start redis sentinel run: | wget raw.githubusercontent.com/redis/redis/7.0/sentinel.conf for PORT in $(seq 26379 26380); do cp sentinel.conf "$PORT.conf" sed -i '/^sentinel/Id' "$PORT.conf" - redis-server "$PORT.conf" --port "$PORT" --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel auth-pass mymaster phpredis + ${{ matrix.server }} "$PORT.conf" \ + --port "$PORT" \ + --daemonize yes \ + --sentinel monitor mymaster 127.0.0.1 6379 1 \ + --sentinel auth-pass mymaster phpredis done - name: Run tests run: | From 37fa3592ce588cd7654dbe55ec5d298cb1f4309b Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Mon, 25 Mar 2024 08:35:09 +1100 Subject: [PATCH 1860/1986] Mention KeyDB support in README.md Mention support for KeyDB in README.md. Remove credit for Owlient from the first paragraph. Owlient was acquired by Ubisoft in 2011, so presumably no longer benefit from such prominent credit. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 999a2e5c9f..97eaaa44ec 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis) [![PHP version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://github.com/phpredis/phpredis) -The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). -This code has been developed and maintained by Owlient from November 2009 to March 2011. +The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It also supports [KeyDB](https://docs.keydb.dev/), an open source alternative to Redis. + +It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to michael.grunder@gmail.com ([Twitter](https://twitter.com/grumi78), Mastodon), p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)), or n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)). From 50e5405c03c3fc8413b04d847c274ee9cf007135 Mon Sep 17 00:00:00 2001 From: Pavlo Yatsukhnenko Date: Mon, 25 Mar 2024 19:28:29 +0200 Subject: [PATCH 1861/1986] Fix Arginfo / zpp mismatch for DUMP command --- redis.stub.php | 2 +- redis_arginfo.h | 6 ++++-- redis_legacy_arginfo.h | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 6818b6d39d..47c8548683 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -991,7 +991,7 @@ public function discard(): Redis|bool; * $binary = $redis->dump('zset'); * $redis->restore('new-zset', 0, $binary); */ - public function dump(string $key): Redis|string; + public function dump(string $key): Redis|string|false; /** * Have Redis repeat back an arbitrary string to the client. diff --git a/redis_arginfo.h b/redis_arginfo.h index 623d6b1d29..e26efd4383 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d6839707b66ecf4460374deea10a528bf0c5ea41 */ + * Stub hash: 21f3434814d9fa077a9a81c8ba114c3faf079e85 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -179,7 +179,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_discard arginfo_class_Redis_bgSave -#define arginfo_class_Redis_dump arginfo_class_Redis_debug +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dump, 0, 1, Redis, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_echo, 0, 1, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 87522686b0..26423fefa6 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d6839707b66ecf4460374deea10a528bf0c5ea41 */ + * Stub hash: 21f3434814d9fa077a9a81c8ba114c3faf079e85 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) From eb7f31e7affdaebd5eb0c829f36d1007ec4794fd Mon Sep 17 00:00:00 2001 From: Jozsef Koszo Date: Sun, 17 Mar 2024 07:44:49 +0100 Subject: [PATCH 1862/1986] Fix random connection timeouts with Redis Cluster When a node timeout occurs, then phpredis will try to connect to another node, whose answer probably will be MOVED redirect. After this we need more time to accomplish the redirection, otherwise we get "Timed out attempting to find data in the correct node" error message. Fixes #795 #888 #1142 #1385 #1633 #1707 #1811 #2407 --- cluster_library.c | 2 +- redis_cluster.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster_library.c b/cluster_library.c index 9258fca9ce..3bd4fc784b 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -833,7 +833,7 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, c->err = NULL; /* Set up our waitms based on timeout */ - c->waitms = (long)(1000 * timeout); + c->waitms = (long)(1000 * (timeout + read_timeout)); /* Allocate our seeds hash table */ ALLOC_HASHTABLE(c->seeds); diff --git a/redis_cluster.c b/redis_cluster.c index 599449ad9e..77d01065d7 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -139,7 +139,7 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time c->flags->timeout = timeout; c->flags->read_timeout = read_timeout; c->flags->persistent = persistent; - c->waitms = timeout * 1000L; + c->waitms = (long)(1000 * (timeout + read_timeout)); /* Attempt to load slots from cache if caching is enabled */ if (CLUSTER_CACHING_ENABLED()) { From b698901818773b00e02b585706bb3e97187f5f7a Mon Sep 17 00:00:00 2001 From: Bitactive Date: Thu, 28 Mar 2024 13:39:35 +0100 Subject: [PATCH 1863/1986] Support for early_refresh in Redis sessions to match cluster behavior Previously, the redis.session.early_refresh feature was implemented for Redis Cluster, utilizing GETEX for the initial session read to minimize the number of commands sent to the Redis server. However, this enhancement was not applied to non-cluster sessions. This update addresses this discrepancy, ensuring consistent behavior between Redis and Redis Cluster. --- redis_session.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/redis_session.c b/redis_session.c index 9f8c453f54..f3ed93cbe6 100644 --- a/redis_session.c +++ b/redis_session.c @@ -645,6 +645,11 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) if (!skeylen) return FAILURE; + /* No need to update the session timestamp if we've already done so */ + if (INI_INT("redis.session.early_refresh")) { + return SUCCESS; + } + redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, skey); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; @@ -698,7 +703,14 @@ PS_READ_FUNC(redis) /* send GET command */ if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); pool->lock_status.session_key = redis_session_key(redis_sock, skey, skeylen); - cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); + + /* Update the session ttl if early refresh is enabled */ + if (INI_INT("redis.session.early_refresh")) { + cmd_len = REDIS_SPPRINTF(&cmd, "GETEX", "Ssd", pool->lock_status.session_key, + "EX", 2, session_gc_maxlifetime()); + } else { + cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); + } if (lock_acquire(redis_sock, &pool->lock_status) != SUCCESS) { php_error_docref(NULL, E_WARNING, "Failed to acquire session lock"); From a819a44b8319397872e39441e43718ea9df987af Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 6 Apr 2024 16:17:45 -0700 Subject: [PATCH 1864/1986] Test against valkey Add Valkey to our server matrix in addition to making the jobs a bit more efficient by only installing the specific server we're testing on each run. For now we allow tests to fail against Valkey as they don't yet have an official release. Once there is an official release we'll remove the `continue-on-error` setting for Valkey. --- .github/workflows/ci.yml | 52 ++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b26475a30..035108c7b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,7 +74,7 @@ jobs: fail-fast: false matrix: php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] - server: ['redis-server', 'keydb-server'] + server: ['redis', 'keydb', 'valkey'] steps: - name: Checkout @@ -91,8 +91,10 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install valgrind libzstd-dev liblz4-dev + sudo apt-get install valgrind libzstd-dev liblz4-dev libssl-dev + - name: Install Redis + if: matrix.server == 'redis' env: REDIS_PPA_URI: "packages.redis.io/deb" REDIS_PPA_KEY: "packages.redis.io/gpg" @@ -105,6 +107,7 @@ jobs: sudo apt-get install redis - name: Install KeyDB + if: matrix.server == 'keydb' env: KEYDB_PPA_URI: "download.keydb.dev/open-source-dist" KEYDB_PPA_KEY: "download.keydb.dev/open-source-dist/keyring.gpg" @@ -114,6 +117,13 @@ jobs: sudo wget -O /etc/apt/trusted.gpg.d/keydb.gpg "https://$KEYDB_PPA_KEY" sudo apt-get update sudo apt-get install keydb + + - name: Install ValKey + if: matrix.server == 'valkey' + run: | + git clone https://github.com/valkey-io/valkey.git + cd valkey && BUILD_TLS=yes sudo make install + - name: Build phpredis run: | phpize @@ -127,28 +137,32 @@ jobs: sudo make -j"$(nproc)" install echo 'extension = redis.so' | sudo tee -a "$(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')"/90-redis.ini - - name: Start redis + + - name: Attempt to shutdown default server + run: ${{ matrix.server }}-cli SHUTDOWN NOSAVE || true + + - name: Start ${{ matrix.server }}-server run: | - redis-cli SHUTDOWN NOSAVE for PORT in $(seq 6379 6382) $(seq 32767 32769); do - ${{ matrix.server }} \ + ${{ matrix.server }}-server \ --port "$PORT" \ --daemonize yes \ --aclfile tests/users.acl \ --acl-pubsub-default allchannels done - ${{ matrix.server }} \ + ${{ matrix.server }}-server \ --port 0 \ --unixsocket /tmp/redis.sock \ --daemonize yes \ --aclfile tests/users.acl \ --acl-pubsub-default allchannels - - name: Start redis cluster + + - name: Start ${{ matrix.server }} cluster run: | mkdir -p tests/nodes echo -n > tests/nodes/nodemap for PORT in $(seq 7000 7005); do - ${{ matrix.server }} \ + ${{ matrix.server }}-server \ --port "$PORT" \ --cluster-enabled yes \ --cluster-config-file "$PORT".conf \ @@ -157,21 +171,25 @@ jobs: --acl-pubsub-default allchannels echo 127.0.0.1:"$PORT" >> tests/nodes/nodemap done - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7005) \ + echo yes | ${{ matrix.server }}-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7005) \ --cluster-replicas 1 --user phpredis -a phpredis - - name: Start redis sentinel + + - name: Start ${{ matrix.server }} sentinel + continue-on-error: ${{ matrix.server == 'valkey' }} run: | wget raw.githubusercontent.com/redis/redis/7.0/sentinel.conf for PORT in $(seq 26379 26380); do cp sentinel.conf "$PORT.conf" sed -i '/^sentinel/Id' "$PORT.conf" - ${{ matrix.server }} "$PORT.conf" \ + ${{ matrix.server }}-server "$PORT.conf" \ --port "$PORT" \ --daemonize yes \ --sentinel monitor mymaster 127.0.0.1 6379 1 \ --sentinel auth-pass mymaster phpredis done + - name: Run tests + continue-on-error: ${{ matrix.server == 'valkey' }} run: | php tests/TestRedis.php --class Redis --user phpredis --auth phpredis php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis @@ -182,10 +200,14 @@ jobs: - name: Run tests using valgrind continue-on-error: true run: | - valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class Redis --user phpredis --auth phpredis - valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis - valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis - valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel --auth phpredis + valgrind --suppressions=tests/vg.supp --error-exitcode=1 \ + php tests/TestRedis.php --class Redis --user phpredis --auth phpredis + valgrind --suppressions=tests/vg.supp --error-exitcode=1 \ + php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis + valgrind --suppressions=tests/vg.supp --error-exitcode=1 \ + php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis + valgrind --suppressions=tests/vg.supp --error-exitcode=1 \ + php tests/TestRedis.php --class RedisSentinel --auth phpredis env: TEST_PHP_ARGS: -e USE_ZEND_ALLOC: 0 From 59965971bb442df51ab42c935be6f99ad5b62e50 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 7 Apr 2024 14:39:41 -0700 Subject: [PATCH 1865/1986] Add a CI step that waits for server instances before running tests Every so often our tests will fail because we attempt to interact with one of the daemonized server instances before it has enough time to actually start. Usually this happens when we try to use the cli tool to execute `--cluster-create`, but it can occur elsewhere as well. This commit adds a step that waits for every instance that we started to actually be up before trying to create the cluster and run subsequent unit tests. Additionally it switches from `$(seq a b)` to the `{a..b}` brace expansion. --- .github/workflows/ci.yml | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 035108c7b6..2aa70af318 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,7 +143,7 @@ jobs: - name: Start ${{ matrix.server }}-server run: | - for PORT in $(seq 6379 6382) $(seq 32767 32769); do + for PORT in {6379..6382} {32767..32769}; do ${{ matrix.server }}-server \ --port "$PORT" \ --daemonize yes \ @@ -161,7 +161,7 @@ jobs: run: | mkdir -p tests/nodes echo -n > tests/nodes/nodemap - for PORT in $(seq 7000 7005); do + for PORT in {7000..7005}; do ${{ matrix.server }}-server \ --port "$PORT" \ --cluster-enabled yes \ @@ -171,14 +171,12 @@ jobs: --acl-pubsub-default allchannels echo 127.0.0.1:"$PORT" >> tests/nodes/nodemap done - echo yes | ${{ matrix.server }}-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7005) \ - --cluster-replicas 1 --user phpredis -a phpredis - name: Start ${{ matrix.server }} sentinel continue-on-error: ${{ matrix.server == 'valkey' }} run: | wget raw.githubusercontent.com/redis/redis/7.0/sentinel.conf - for PORT in $(seq 26379 26380); do + for PORT in {26379..26380}; do cp sentinel.conf "$PORT.conf" sed -i '/^sentinel/Id' "$PORT.conf" ${{ matrix.server }}-server "$PORT.conf" \ @@ -188,6 +186,24 @@ jobs: --sentinel auth-pass mymaster phpredis done + - name: Wait for ${{ matrix.server }} instances + run: | + for PORT in {6379..6382} {7000..7005} {32767..32768} {26379..26380}; do + until echo PING | ${{ matrix.server }}-cli -p "$PORT" 2>&1 | grep -qE 'PONG|NOAUTH'; do + echo "Still waiting for ${{ matrix.server }} on port $PORT" + sleep .05 + done + done + until echo PING | ${{ matrix.server }}-cli -s /tmp/redis.sock 2>&1 | grep -qE 'PONG|NOAUTH'; do + echo "Still waiting for ${{ matrix.server }} at /tmp/redis.sock" + sleep .05 + done + + - name: Initialize ${{ matrix.server }} cluster + run: | + echo yes | ${{ matrix.server }}-cli --cluster create 127.0.0.1:{7000..7005} \ + --cluster-replicas 1 --user phpredis -a phpredis + - name: Run tests continue-on-error: ${{ matrix.server == 'valkey' }} run: | From 5f1eecfba62609c45b5d5b9c8aab9a651baefd1f Mon Sep 17 00:00:00 2001 From: PlavorSeol Date: Tue, 9 Apr 2024 02:58:50 +0900 Subject: [PATCH 1866/1986] Mention Valkey support --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97eaaa44ec..db17dcab98 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis) [![PHP version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://github.com/phpredis/phpredis) -The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It also supports [KeyDB](https://docs.keydb.dev/), an open source alternative to Redis. +The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It also supports [KeyDB](https://docs.keydb.dev/) and [Valkey](https://valkey.io/), which are open source alternatives to Redis. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). From da4ab0a72c664ff2477c1d4864fb307da94d4231 Mon Sep 17 00:00:00 2001 From: bitactive Date: Mon, 8 Apr 2024 23:40:15 +0200 Subject: [PATCH 1867/1986] Add compression support for PHP Sessions (#2473) * Add compression support for PHP Sessions Previously, compression was available for standard data but not for session handling. This update enables the compression of PHP sessions, allowing for more efficient Redis memory usage. * Move session compress/uncompress logic to helper functions * Change session_compress_data to always set the out arguments and adjust PS_READ_FUNC --- README.md | 10 +++++ cluster.md | 10 +++++ redis.c | 2 + redis_session.c | 117 ++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 126 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index db17dcab98..15ff8e4e99 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,16 @@ redis.session.lock_wait_time = 50000 redis.session.lock_retries = 2000 ~~~ +### Session compression + +Following INI variables can be used to configure session compression: +~~~ +; Should session compression be enabled? Possible values are zstd, lzf, lz4, none. Defaults to: none +redis.session.compression = zstd +; What compression level should be used? Compression level depends on used library. For most deployments range 1-9 should be fine. Defaults to: 3 +redis.session.compression_level = 3 +~~~ + ## Running the unit tests phpredis uses a small custom unit test suite for testing functionality of the various classes. To run tests, simply do the following: diff --git a/cluster.md b/cluster.md index 484bc76a84..5be120f26b 100644 --- a/cluster.md +++ b/cluster.md @@ -206,3 +206,13 @@ To enable, set the following INI variable: redis.session.early_refresh = 1 ``` Note: This is disabled by default since it may significantly reduce the session lifetime for long-running scripts. Redis server version 6.2+ required. + +### Session compression + +Following INI variables can be used to configure session compression: +~~~ +; Should session compression be enabled? Possible values are zstd, lzf, lz4, none. Defaults to: none +redis.session.compression = zstd +; What compression level should be used? Compression level depends on used library. For most deployments range 1-9 should be fine. Defaults to: 3 +redis.session.compression_level = 3 +~~~ \ No newline at end of file diff --git a/redis.c b/redis.c index b34667dc1a..b7eaff7ff6 100644 --- a/redis.c +++ b/redis.c @@ -115,6 +115,8 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("redis.session.lock_retries", "100", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.early_refresh", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.compression", "none", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.compression_level", "3", PHP_INI_ALL, NULL) PHP_INI_END() static const zend_module_dep redis_deps[] = { diff --git a/redis_session.c b/redis_session.c index f3ed93cbe6..3a8fd965e4 100644 --- a/redis_session.c +++ b/redis_session.c @@ -140,6 +140,66 @@ static int session_gc_maxlifetime(void) { return value; } +/* Retrieve redis.session.compression from php.ini */ +static int session_compression_type(void) { +#ifdef HAVE_REDIS_LZF + if(strncasecmp(INI_STR("redis.session.compression"), "lzf", sizeof("lzf") - 1) == 0) { + return REDIS_COMPRESSION_LZF; + } +#endif +#ifdef HAVE_REDIS_ZSTD + if(strncasecmp(INI_STR("redis.session.compression"), "zstd", sizeof("zstd") - 1) == 0) { + return REDIS_COMPRESSION_ZSTD; + } +#endif +#ifdef HAVE_REDIS_LZ4 + if(strncasecmp(INI_STR("redis.session.compression"), "lz4", sizeof("lz4") - 1) == 0) { + return REDIS_COMPRESSION_LZ4; + } +#endif + if(strncasecmp(INI_STR("redis.session.compression"), "none", sizeof("none") - 1) == 0) { + return REDIS_COMPRESSION_NONE; + } + + // E_NOTICE when outside of valid values + php_error_docref(NULL, E_NOTICE, "redis.session.compression is outside of valid values, disabling"); + + return REDIS_COMPRESSION_NONE; +} + +/* Helper to compress session data */ +static int +session_compress_data(RedisSock *redis_sock, char *data, size_t len, + char **compressed_data, size_t *compressed_len) +{ + if (redis_sock->compression) { + if(redis_compress(redis_sock, compressed_data, compressed_len, data, len)) { + return 1; + } + } + + *compressed_data = data; + *compressed_len = len; + + return 0; +} + +/* Helper to uncompress session data */ +static int +session_uncompress_data(RedisSock *redis_sock, char *data, size_t len, + char **decompressed_data, size_t *decompressed_len) { + if (redis_sock->compression) { + if(redis_uncompress(redis_sock, decompressed_data, decompressed_len, data, len)) { + return 1; + } + } + + *decompressed_data = data; + *decompressed_len = len; + + return 0; +} + /* Send a command to Redis. Returns byte count written to socket (-1 on failure) */ static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, char **reply, int *replylen) @@ -478,6 +538,9 @@ PS_OPEN_FUNC(redis) redis_sock->dbNumber = db; } + redis_sock->compression = session_compression_type(); + redis_sock->compression_level = INI_INT("redis.session.compression_level"); + if (Z_TYPE(context) == IS_ARRAY) { redis_sock_set_stream_context(redis_sock, &context); } @@ -685,10 +748,10 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) */ PS_READ_FUNC(redis) { - char *resp, *cmd; - int resp_len, cmd_len; + char *resp, *cmd, *compressed_buf; + int resp_len, cmd_len, compressed_free; const char *skey = ZSTR_VAL(key); - size_t skeylen = ZSTR_LEN(key); + size_t skeylen = ZSTR_LEN(key), compressed_len; if (!skeylen) return FAILURE; @@ -737,8 +800,13 @@ PS_READ_FUNC(redis) if (resp_len < 0) { *val = ZSTR_EMPTY_ALLOC(); } else { - *val = zend_string_init(resp, resp_len, 0); + compressed_free = session_uncompress_data(redis_sock, resp, resp_len, &compressed_buf, &compressed_len); + *val = zend_string_init(compressed_buf, compressed_len, 0); + if (compressed_free) { + efree(compressed_buf); // Free the buffer allocated by redis_uncompress + } } + efree(resp); return SUCCESS; @@ -749,10 +817,10 @@ PS_READ_FUNC(redis) */ PS_WRITE_FUNC(redis) { - char *cmd, *response; - int cmd_len, response_len; + char *cmd, *response, *compressed_buf = NULL; + int cmd_len, response_len, compressed_free = 0; const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); - size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); + size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val), compressed_len = 0; if (!skeylen) return FAILURE; @@ -767,8 +835,15 @@ PS_WRITE_FUNC(redis) /* send SET command */ zend_string *session = redis_session_key(redis_sock, skey, skeylen); + compressed_free = session_compress_data(redis_sock, ZSTR_VAL(val), ZSTR_LEN(val), &compressed_buf, &compressed_len); + sval = compressed_buf; + svallen = compressed_len; + cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, session_gc_maxlifetime(), sval, svallen); zend_string_release(session); + if (compressed_free) { + efree(compressed_buf); + } if (!write_allowed(redis_sock, &pool->lock_status)) { php_error_docref(NULL, E_WARNING, "Unable to write session: session lock not held"); @@ -942,6 +1017,9 @@ PS_OPEN_FUNC(rediscluster) { c->flags->prefix = CLUSTER_DEFAULT_PREFIX(); } + c->flags->compression = session_compression_type(); + c->flags->compression_level = INI_INT("redis.session.compression_level"); + redis_sock_set_auth(c->flags, user, pass); if ((context = REDIS_HASH_STR_FIND_TYPE_STATIC(ht_conf, "stream", IS_ARRAY)) != NULL) { @@ -1142,8 +1220,9 @@ PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { PS_READ_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey; - int cmdlen, skeylen, free_flag; + char *cmd, *skey, *compressed_buf; + int cmdlen, skeylen, free_flag, compressed_free; + size_t compressed_len; short slot; /* Set up our command and slot information */ @@ -1181,7 +1260,11 @@ PS_READ_FUNC(rediscluster) { if (reply->str == NULL) { *val = ZSTR_EMPTY_ALLOC(); } else { - *val = zend_string_init(reply->str, reply->len, 0); + compressed_free = session_uncompress_data(c->flags, reply->str, reply->len, &compressed_buf, &compressed_len); + *val = zend_string_init(compressed_buf, compressed_len, 0); + if (compressed_free) { + efree(compressed_buf); // Free the buffer allocated by redis_uncompress + } } free_flag = 1; @@ -1198,16 +1281,24 @@ PS_READ_FUNC(rediscluster) { PS_WRITE_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey; - int cmdlen, skeylen; + char *cmd, *skey, *compressed_buf = NULL, *sval = ZSTR_VAL(val); + int cmdlen, skeylen, compressed_free = 0, svallen = ZSTR_LEN(val); short slot; + size_t compressed_len = 0; + + compressed_free = session_compress_data(c->flags, sval, svallen, &compressed_buf, &compressed_len); + sval = compressed_buf; + svallen = compressed_len; /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL, &cmd, "SETEX", "sds", skey, skeylen, session_gc_maxlifetime(), - ZSTR_VAL(val), ZSTR_LEN(val)); + sval, svallen); efree(skey); + if (compressed_free) { + efree(compressed_buf); + } /* Attempt to send command */ c->readonly = 0; From dc39bd55a050723b88ffa42fe97e0c90666ed164 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Mon, 8 Apr 2024 14:53:27 -0700 Subject: [PATCH 1868/1986] Remove 7.2 and 7.3 from CI. (#2478) < 8.1 is EOL but no one upgrades when they should so it's probably prudent to make sure we still compile against 7.4. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2aa70af318..f98f5aac29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3'] server: ['redis', 'keydb', 'valkey'] steps: @@ -234,7 +234,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3'] steps: - name: Checkout uses: actions/checkout@v4 From 2b555c89ef93ac7064247861848125eee16516b8 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 8 Apr 2024 14:31:15 -0700 Subject: [PATCH 1869/1986] Minor session compression cleanup. * We can compress without the need for sepearate buffers. * Allow both "" and "none" to mean "none" in terms of redis.session.compression. --- redis_session.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/redis_session.c b/redis_session.c index 3a8fd965e4..1434286c50 100644 --- a/redis_session.c +++ b/redis_session.c @@ -142,22 +142,23 @@ static int session_gc_maxlifetime(void) { /* Retrieve redis.session.compression from php.ini */ static int session_compression_type(void) { + const char *compression = INI_STR("redis.session.compression"); #ifdef HAVE_REDIS_LZF - if(strncasecmp(INI_STR("redis.session.compression"), "lzf", sizeof("lzf") - 1) == 0) { + if(strncasecmp(compression, "lzf", sizeof("lzf") - 1) == 0) { return REDIS_COMPRESSION_LZF; } #endif #ifdef HAVE_REDIS_ZSTD - if(strncasecmp(INI_STR("redis.session.compression"), "zstd", sizeof("zstd") - 1) == 0) { + if(strncasecmp(compression, "zstd", sizeof("zstd") - 1) == 0) { return REDIS_COMPRESSION_ZSTD; } #endif #ifdef HAVE_REDIS_LZ4 - if(strncasecmp(INI_STR("redis.session.compression"), "lz4", sizeof("lz4") - 1) == 0) { + if(strncasecmp(compression, "lz4", sizeof("lz4") - 1) == 0) { return REDIS_COMPRESSION_LZ4; } #endif - if(strncasecmp(INI_STR("redis.session.compression"), "none", sizeof("none") - 1) == 0) { + if(*compression == '\0' || strncasecmp(compression, "none", sizeof("none") - 1) == 0) { return REDIS_COMPRESSION_NONE; } @@ -185,7 +186,7 @@ session_compress_data(RedisSock *redis_sock, char *data, size_t len, } /* Helper to uncompress session data */ -static int +static int session_uncompress_data(RedisSock *redis_sock, char *data, size_t len, char **decompressed_data, size_t *decompressed_len) { if (redis_sock->compression) { @@ -817,10 +818,11 @@ PS_READ_FUNC(redis) */ PS_WRITE_FUNC(redis) { - char *cmd, *response, *compressed_buf = NULL; - int cmd_len, response_len, compressed_free = 0; - const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); - size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val), compressed_len = 0; + char *cmd, *response; + int cmd_len, response_len, compressed_free; + const char *skey = ZSTR_VAL(key); + size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); + char *sval; if (!skeylen) return FAILURE; @@ -835,14 +837,13 @@ PS_WRITE_FUNC(redis) /* send SET command */ zend_string *session = redis_session_key(redis_sock, skey, skeylen); - compressed_free = session_compress_data(redis_sock, ZSTR_VAL(val), ZSTR_LEN(val), &compressed_buf, &compressed_len); - sval = compressed_buf; - svallen = compressed_len; - + compressed_free = session_compress_data(redis_sock, ZSTR_VAL(val), ZSTR_LEN(val), + &sval, &svallen); + cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, session_gc_maxlifetime(), sval, svallen); zend_string_release(session); if (compressed_free) { - efree(compressed_buf); + efree(sval); } if (!write_allowed(redis_sock, &pool->lock_status)) { @@ -1281,14 +1282,13 @@ PS_READ_FUNC(rediscluster) { PS_WRITE_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey, *compressed_buf = NULL, *sval = ZSTR_VAL(val); - int cmdlen, skeylen, compressed_free = 0, svallen = ZSTR_LEN(val); + char *cmd, *skey, *sval; + int cmdlen, skeylen, compressed_free; + size_t svallen; short slot; - size_t compressed_len = 0; - compressed_free = session_compress_data(c->flags, sval, svallen, &compressed_buf, &compressed_len); - sval = compressed_buf; - svallen = compressed_len; + compressed_free = session_compress_data(c->flags, ZSTR_VAL(val), ZSTR_LEN(val), + &sval, &svallen); /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); @@ -1297,7 +1297,7 @@ PS_WRITE_FUNC(rediscluster) { sval, svallen); efree(skey); if (compressed_free) { - efree(compressed_buf); + efree(sval); } /* Attempt to send command */ From 9f3ca98c00bd819e0b609d3b8bf6bc3d3751ec9d Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 8 Apr 2024 19:37:12 -0700 Subject: [PATCH 1870/1986] Add a test for session compression. See #2473 #2480 --- tests/RedisTest.php | 71 +++++++++++++++++++++++++++++------------- tests/startSession.php | 15 +++++---- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 2ead39686a..24a8f83c5e 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -51,13 +51,13 @@ protected function getSerializers() { } protected function getCompressors() { - $result[] = Redis::COMPRESSION_NONE; + $result['none'] = Redis::COMPRESSION_NONE; if (defined('Redis::COMPRESSION_LZF')) - $result[] = Redis::COMPRESSION_LZF; + $result['lzf'] = Redis::COMPRESSION_LZF; if (defined('Redis::COMPRESSION_LZ4')) - $result[] = Redis::COMPRESSION_LZ4; + $result['lz4'] = Redis::COMPRESSION_LZ4; if (defined('Redis::COMPRESSION_ZSTD')) - $result[] = Redis::COMPRESSION_ZSTD; + $result['zstd'] = Redis::COMPRESSION_ZSTD; return $result; } @@ -7377,6 +7377,26 @@ public function testHighPorts() { } } + public function testSession_compression() { + $this->setSessionHandler(); + + foreach ($this->getCompressors() as $name => $val) { + + $id = $this->generateSessionId(); + $res = $this->startSessionProcess($id, 0, false, 300, true, null, + -1, 0, "testing_compression_$name", 1440, + $name); + + $this->assertTrue($res); + + $key = $this->sessionPrefix . $id; + + $this->redis->setOption(Redis::OPT_COMPRESSION, $val); + $this->assertTrue($this->redis->get($key) !== false); + $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); + } + } + public function testSession_savedToRedis() { $this->setSessionHandler(); @@ -7878,33 +7898,40 @@ private function generateSessionId() * @param int $lock_retries * @param int $lock_expires * @param string $sessionData - * * @param int $sessionLifetime + * @param string $sessionCompression * * @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) + private function startSessionProcess($sessionId, $sleepTime, $background, + $maxExecutionTime = 300, + $locking_enabled = true, + $lock_wait_time = null, + $lock_retries = -1, + $lock_expires = 0, + $sessionData = '', + $sessionLifetime = 1440, + $sessionCompression = 'none') { - if (substr(php_uname(), 0, 7) == "Windows"){ + if (strpos(php_uname(), 'Windows') !== false) $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); + $commandParameters = [ + $this->getFullHostPath(), $this->sessionSaveHandler, $sessionId, + $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, + $sessionData, $sessionLifetime, $locking_enabled ? 1 : 0, + $lock_wait_time ?? 0, $sessionCompression + ]; - $command = self::getPhpCommand('startSession.php') . implode(' ', $commandParameters); - $command .= $background ? ' 2>/dev/null > /dev/null &' : ' 2>&1'; - exec($command, $output); - return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')) ? true : false; - } + $commandParameters = array_map('escapeshellarg', $commandParameters); + $commandParameters[] = $background ? '>/dev/null 2>&1 &' : '2>&1'; + + $command = self::getPhpCommand('startSession.php') . implode(' ', $commandParameters); + + exec($command, $output); + + return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')); } /** diff --git a/tests/startSession.php b/tests/startSession.php index 34af31ae9e..6975a772b8 100644 --- a/tests/startSession.php +++ b/tests/startSession.php @@ -10,6 +10,9 @@ $lock_expire = $argv[7]; $sessionData = $argv[8]; $sessionLifetime = $argv[9]; +$lockingEnabled = $argv[10]; +$lockWaitTime = $argv[11]; +$sessionCompression = $argv[12]; if (empty($redisHost)) { $redisHost = 'tcp://localhost:6379'; @@ -21,14 +24,9 @@ ini_set("{$saveHandler}.session.lock_retries", $lock_retries); ini_set("{$saveHandler}.session.lock_expire", $lock_expire); ini_set('session.gc_maxlifetime', $sessionLifetime); - -if (isset($argv[10])) { - ini_set("{$saveHandler}.session.locking_enabled", $argv[10]); -} - -if (isset($argv[11])) { - ini_set("{$saveHandler}.session.lock_wait_time", $argv[11]); -} +ini_set("{$saveHandler}.session.locking_enabled", $lockingEnabled); +ini_set("{$saveHandler}.session.lock_wait_time", $lockWaitTime); +ini_set('redis.session.compression', $sessionCompression); session_id($sessionId); $sessionStartSuccessful = session_start(); @@ -36,6 +34,7 @@ if (!empty($sessionData)) { $_SESSION['redis_test'] = $sessionData; } + session_write_close(); echo $sessionStartSuccessful ? 'SUCCESS' : 'FAILURE'; From 0e92616591bc9f4453a307171c9e7cfbafe4f9a7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sat, 6 Apr 2024 15:19:35 -0700 Subject: [PATCH 1871/1986] Fix memory leak if we fail in ps_open_redis. --- redis_session.c | 1 + 1 file changed, 1 insertion(+) diff --git a/redis_session.c b/redis_session.c index 1434286c50..84d2e25259 100644 --- a/redis_session.c +++ b/redis_session.c @@ -563,6 +563,7 @@ PS_OPEN_FUNC(redis) return SUCCESS; } + redis_pool_free(pool); return FAILURE; } /* }}} */ From 3d7be35816cdb0282698da7b785a960185af7025 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Sun, 7 Apr 2024 13:52:24 -0700 Subject: [PATCH 1872/1986] Consolidate failure path --- redis_session.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/redis_session.c b/redis_session.c index 84d2e25259..96f39be7d5 100644 --- a/redis_session.c +++ b/redis_session.c @@ -108,6 +108,10 @@ PHP_REDIS_API void redis_pool_free(redis_pool *pool) { redis_pool_member *rpm, *next; + + if (pool == NULL) + return; + rpm = pool->head; while (rpm) { next = rpm->next; @@ -460,9 +464,7 @@ PS_OPEN_FUNC(redis) "Failed to parse session.save_path (error at offset %d, url was '%s')", i, path); efree(path); - redis_pool_free(pool); - PS_SET_MOD_DATA(NULL); - return FAILURE; + goto fail; } ZVAL_NULL(&context); @@ -509,10 +511,8 @@ PS_OPEN_FUNC(redis) if (prefix) zend_string_release(prefix); if (user) zend_string_release(user); if (pass) zend_string_release(pass); - redis_pool_free(pool); - PS_SET_MOD_DATA(NULL); - return FAILURE; + goto fail; } RedisSock *redis_sock; @@ -563,7 +563,9 @@ PS_OPEN_FUNC(redis) return SUCCESS; } +fail: redis_pool_free(pool); + PS_SET_MOD_DATA(NULL); return FAILURE; } /* }}} */ From f350dc342cb3520b2ee663664a22c25f29bc8aaf Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 16 Apr 2024 11:17:37 -0700 Subject: [PATCH 1873/1986] Test aginst the first stable version of valkey. Now that valkey has an official release we can test against that and remove the "continue-on-error" flag in CI. --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f98f5aac29..26f77fe61a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,7 +121,7 @@ jobs: - name: Install ValKey if: matrix.server == 'valkey' run: | - git clone https://github.com/valkey-io/valkey.git + git clone --depth 1 --branch 7.2.5 https://github.com/valkey-io/valkey.git cd valkey && BUILD_TLS=yes sudo make install - name: Build phpredis @@ -173,7 +173,6 @@ jobs: done - name: Start ${{ matrix.server }} sentinel - continue-on-error: ${{ matrix.server == 'valkey' }} run: | wget raw.githubusercontent.com/redis/redis/7.0/sentinel.conf for PORT in {26379..26380}; do @@ -205,7 +204,6 @@ jobs: --cluster-replicas 1 --user phpredis -a phpredis - name: Run tests - continue-on-error: ${{ matrix.server == 'valkey' }} run: | php tests/TestRedis.php --class Redis --user phpredis --auth phpredis php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis From c0d6f04298e3eb431734e9530e9122af87aa1ca7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 9 May 2024 14:05:32 -0700 Subject: [PATCH 1874/1986] Minor improvements to some session tests. --- tests/RedisTest.php | 22 +++++++++++++++------- tests/TestSuite.php | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 24a8f83c5e..75e1efb001 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7462,7 +7462,7 @@ public function testSession_lock_ttlMaxExecutionTime() $end = microtime(true); $elapsedTime = $end - $start; - $this->assertTrue($elapsedTime < 3); + $this->assertLess($elapsedTime, 3); $this->assertTrue($sessionSuccessful); } @@ -7543,7 +7543,7 @@ public function testSession_defaultLockRetryCount() $end = microtime(true); $elapsedTime = $end - $start; - $this->assertTrue($elapsedTime > 2 && $elapsedTime < 3); + $this->assertBetween($elapsedTime, 2, 3); $this->assertFalse($sessionSuccessful); } @@ -7931,7 +7931,14 @@ private function startSessionProcess($sessionId, $sleepTime, $background, exec($command, $output); - return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')); + if ($background) + return true; + + $result = $output[0] == 'SUCCESS'; + + // var_dump(['command' => $command, 'output' => $output, 'result' => $result]); + + return $result; } /** @@ -8006,11 +8013,12 @@ private function getPhpCommand($script) if (!$cmd) { $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY); - if ($test_args = getenv('TEST_PHP_ARGS')) { - $cmd .= ' '; - $cmd .= $test_args; + $test_args = getenv('TEST_PHP_ARGS'); + if ($test_args !== false) { + $cmd .= ' ' . $test_args; } else { - /* Only append specific extension directives if PHP hasn't been compiled with what we need statically */ + /* 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 */ diff --git a/tests/TestSuite.php b/tests/TestSuite.php index aa467019f2..70d94bda88 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -181,6 +181,21 @@ protected function assertLess($a, $b) { $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); } + protected function assertBetween($value, $min, $max, bool $exclusive = false) { + if ($exclusive) { + if ($value > $min && $value < $max) + return; + } else { + if ($value >= $min && $value <= $max) + return; + } + + $bt = debug_backtrace(false); + self::$errors []= sprintf("Assertion failed (%s not between %s and %s): %s:%d (%s)\n", + print_r($value, true), print_r($min, true), print_r($max, true), + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + } + protected function assertEquals($a, $b) { if($a === $b) return; From 0f94d9c1c6b3b03a6735cb45dd168b5b43d6a4d4 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 9 May 2024 21:30:17 -0700 Subject: [PATCH 1875/1986] Relax timing test slightly --- tests/RedisTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 75e1efb001..94a399af5e 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -7462,7 +7462,7 @@ public function testSession_lock_ttlMaxExecutionTime() $end = microtime(true); $elapsedTime = $end - $start; - $this->assertLess($elapsedTime, 3); + $this->assertLess($elapsedTime, 4); $this->assertTrue($sessionSuccessful); } From f865d5b95dca9004ac82eaa5bef6ef53a21b15b0 Mon Sep 17 00:00:00 2001 From: divinity76 Date: Wed, 8 May 2024 14:01:03 +0200 Subject: [PATCH 1876/1986] fix missing tags --- redis.stub.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 47c8548683..6cc65726df 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -977,6 +977,7 @@ public function discard(): Redis|bool; /** * Dump Redis' internal binary representation of a key. * + * * $redis->zRange('new-zset', 0, -1, true); * * @@ -2671,6 +2672,7 @@ public function sDiffStore(string $dst, string $key, string ...$other_keys): Red * @param string $other_keys One or more Redis SET keys. * * @example + * * $redis->pipeline() * ->del('alice_likes', 'bob_likes', 'bill_likes') * ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato') @@ -2695,12 +2697,12 @@ public function sInter(array|string $key, string ...$other_keys): Redis|array|fa * @see https://redis.io/commands/sintercard * * @example + * * $redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot'); * $redis->sAdd('set2', 'apple', 'banana'); * $redis->sAdd('set3', 'pear', 'banana'); * * $redis->sInterCard(['set1', 'set2', 'set3']); - * ?> * */ public function sintercard(array $keys, int $limit = -1): Redis|int|false; @@ -2719,10 +2721,9 @@ public function sintercard(array $keys, int $limit = -1): Redis|int|false; * * @see https://redis.io/commands/sinterstore * @see Redis::sinter() - * + * * @example $redis->sInterStore(['dst', 'src1', 'src2', 'src3']); * @example $redis->sInterStore('dst', 'src1', 'src'2', 'src3'); - * ?> * */ public function sInterStore(array|string $key, string ...$other_keys): Redis|int|false; @@ -2934,7 +2935,6 @@ public function scan(null|int|string &$iterator, ?string $pattern = null, int $c * @see https://redis.io/commands/scard * * @example $redis->scard('set'); - * */ public function scard(string $key): Redis|int|false; From 34b5bd81ef27fd91cd01a7b39825605471dcb770 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Mon, 13 May 2024 21:05:04 -0700 Subject: [PATCH 1877/1986] Rework how we declare ZSTD min/max constants. Fixes #2487 --- redis.stub.php | 14 ++++++++++---- redis_arginfo.h | 22 +++++++++++++++------- redis_legacy_arginfo.h | 22 +++++++++++++++------- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/redis.stub.php b/redis.stub.php index 6cc65726df..79f8132593 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -239,14 +239,21 @@ class Redis { public const COMPRESSION_ZSTD_DEFAULT = 3; #endif -#ifdef ZSTD_CLEVEL_MAX +#if ZSTD_VERSION_NUMBER >= 10400 /** * * @var int - * @cvalue ZSTD_CLEVEL_MAX + * @cvalue ZSTD_minCLevel() * */ - public const COMPRESSION_ZSTD_MAX = UNKNOWN; + public const COMPRESSION_ZSTD_MIN = UNKNOWN; +#else + /** + * + * @var int + * + */ + public const COMPRESSION_ZSTD_MIN = 1; #endif /** @@ -254,7 +261,6 @@ class Redis { * @cvalue ZSTD_maxCLevel() */ public const COMPRESSION_ZSTD_MAX = UNKNOWN; - #endif #ifdef HAVE_REDIS_LZ4 diff --git a/redis_arginfo.h b/redis_arginfo.h index e26efd4383..c4ebf5c261 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 21f3434814d9fa077a9a81c8ba114c3faf079e85 */ + * Stub hash: 04fe88bbcc4d3dc3be06385e8931dfb080442f23 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -1866,13 +1866,21 @@ static zend_class_entry *register_class_Redis(void) zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); #endif -#if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_MAX) +#if defined(HAVE_REDIS_ZSTD) && ZSTD_VERSION_NUMBER >= 10400 - zval const_COMPRESSION_ZSTD_MAX_value; - ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_CLEVEL_MAX); - zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release(const_COMPRESSION_ZSTD_MAX_name); + zval const_COMPRESSION_ZSTD_MIN_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MIN_value, ZSTD_minCLevel()); + zend_string *const_COMPRESSION_ZSTD_MIN_name = zend_string_init_interned("COMPRESSION_ZSTD_MIN", sizeof("COMPRESSION_ZSTD_MIN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MIN_name, &const_COMPRESSION_ZSTD_MIN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MIN_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && !(ZSTD_VERSION_NUMBER >= 10400) + + zval const_COMPRESSION_ZSTD_MIN_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MIN_value, 1); + zend_string *const_COMPRESSION_ZSTD_MIN_name = zend_string_init_interned("COMPRESSION_ZSTD_MIN", sizeof("COMPRESSION_ZSTD_MIN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MIN_name, &const_COMPRESSION_ZSTD_MIN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MIN_name); #endif #if defined(HAVE_REDIS_ZSTD) diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 26423fefa6..e29ce73322 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 21f3434814d9fa077a9a81c8ba114c3faf079e85 */ + * Stub hash: 04fe88bbcc4d3dc3be06385e8931dfb080442f23 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -1715,13 +1715,21 @@ static zend_class_entry *register_class_Redis(void) zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); #endif -#if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_MAX) +#if defined(HAVE_REDIS_ZSTD) && ZSTD_VERSION_NUMBER >= 10400 - zval const_COMPRESSION_ZSTD_MAX_value; - ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_CLEVEL_MAX); - zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release(const_COMPRESSION_ZSTD_MAX_name); + zval const_COMPRESSION_ZSTD_MIN_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MIN_value, ZSTD_minCLevel()); + zend_string *const_COMPRESSION_ZSTD_MIN_name = zend_string_init_interned("COMPRESSION_ZSTD_MIN", sizeof("COMPRESSION_ZSTD_MIN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MIN_name, &const_COMPRESSION_ZSTD_MIN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MIN_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && !(ZSTD_VERSION_NUMBER >= 10400) + + zval const_COMPRESSION_ZSTD_MIN_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MIN_value, 1); + zend_string *const_COMPRESSION_ZSTD_MIN_name = zend_string_init_interned("COMPRESSION_ZSTD_MIN", sizeof("COMPRESSION_ZSTD_MIN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MIN_name, &const_COMPRESSION_ZSTD_MIN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MIN_name); #endif #if defined(HAVE_REDIS_ZSTD) From d68c30f87d0467f147dbcf8adb2a38357fafc4f9 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Thu, 16 May 2024 10:22:23 -0700 Subject: [PATCH 1878/1986] Remove Windows PHP 7.x jobs --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26f77fe61a..f2ef9a226e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -262,7 +262,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] + php: ['8.0', '8.1', '8.2', '8.3'] ts: [nts, ts] steps: - name: Checkout From b88e72b1e6bbc34d8f95475590f4bb441d04f834 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Thu, 23 May 2024 09:43:36 -0700 Subject: [PATCH 1879/1986] Refactor session tests (#2492) * Refactor session tests * Update these external scripts to take formal arguments with `getopt` to make it more straightforward what each of the currently positional arguments are actually for. * Create small helper classes for invoking these external scripts. Instead of `startSessionProcess` that takes a dozen argument all but three of which have defaults, we can use a construct like this: ```php $runner = $this->sessionRunner() ->maxExecutionTime(300) ->lockingEnabled(true) ->lockWaitTime(-1) ->lockExpires(0) ->data($data) ->compression($name); // Invokes startSession.php with above args. $result = $runner->execFg(); // Invokes regenerateSessionId.php with above args $new_id = $runner->regenerateId(); // Invokes getSessionData.php for this session ID. $data = $runner->getData(); ``` * Add a bit of logic to TestSuite to dump more information about the source of an assertion to make it easier to track down problems when we assert outside of a top level public `test_*` method. * Create a few new assertions like `assertKeyExists` and `assertKeyMissing` which will generate much nicer assertions as opposed to ```php $this->assertTrue($this->redis->exists($some_key)); ``` * If our externally spawned session scripts fail output the exact call that was made along with all arguments as well as the output that we received to make it easier to narrow down. * snake_case -> camelCase --- tests/RedisClusterTest.php | 30 +- tests/RedisTest.php | 678 ++++++++++++---------------------- tests/SessionHelpers.php | 353 ++++++++++++++++++ tests/TestSuite.php | 223 +++++++---- tests/getSessionData.php | 33 +- tests/regenerateSessionId.php | 51 +-- tests/startSession.php | 74 ++-- 7 files changed, 858 insertions(+), 584 deletions(-) create mode 100644 tests/SessionHelpers.php diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 2d6caa596f..2c4420e135 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -22,15 +22,6 @@ class Redis_Cluster_Test extends Redis_Test { ]; protected static $_arr_node_map = []; - /** - * @var string - */ - protected $sessionPrefix = 'PHPREDIS_CLUSTER_SESSION:'; - - /** - * @var string - */ - protected $sessionSaveHandler = 'rediscluster'; /* Tests we'll skip all together in the context of RedisCluster. The * RedisCluster class doesn't implement specialized (non-redis) commands @@ -709,12 +700,14 @@ public function testAcl() { public function testSession() { @ini_set('session.save_handler', 'rediscluster'); - @ini_set('session.save_path', $this->getFullHostPath() . '&failover=error'); + @ini_set('session.save_path', $this->sessionSavePath() . '&failover=error'); + if (!@session_start()) { return $this->markTestSkipped(); } session_write_close(); - $this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id())); + + $this->assertKeyExists($this->sessionPrefix() . session_id()); } @@ -748,16 +741,21 @@ public function testConnectionPool() { ini_set('redis.pconnect.pooling_enabled', $prev_value); } + protected function sessionPrefix(): string { + return 'PHPREDIS_CLUSTER_SESSION:'; + } + + protected function sessionSaveHandler(): string { + return 'rediscluster'; + } + /** * @inheritdoc */ - protected function getFullHostPath() - { - $auth = $this->getAuthFragment(); - + protected function sessionSavePath(): string { return implode('&', array_map(function ($host) { return 'seed[]=' . $host; - }, self::$_arr_node_map)) . ($auth ? "&$auth" : ''); + }, self::$_arr_node_map)) . '&' . $this->getAuthFragment(); } /* Test correct handling of null multibulk replies */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 94a399af5e..0cf6d9fc8e 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1,6 +1,7 @@ getHost(), $this->getPort(), + $this->getAuthFragment()); + } + + protected function getAuthFragment() { $this->getAuthParts($user, $pass); if ($user && $pass) { - if ($_authidx % 2 == 0) - return "auth[user]=$user&auth[pass]=$pass"; - else - return "auth[]=$user&auth[]=$pass"; + return sprintf("auth[user]=%s&auth[pass]=%s", $user, $pass); } else if ($pass) { - if ($_authidx % 3 == 0) - return "auth[pass]=$pass"; - if ($_authidx % 2 == 0) - return "auth[]=$pass"; - else - return "auth=$pass"; + return sprintf("auth[pass]=%s", $pass); } else { - return NULL; - } - } - - protected function getFullHostPath() - { - $fullHostPath = parent::getFullHostPath(); - $authFragment = $this->getAuthFragment(); - - if (isset($fullHostPath) && $authFragment) { - $fullHostPath .= "?$authFragment"; + return ''; } - return $fullHostPath; } protected function newInstance() { @@ -7377,218 +7359,272 @@ public function testHighPorts() { } } - public function testSession_compression() { - $this->setSessionHandler(); + protected function sessionRunner() { + $this->getAuthParts($user, $pass); - foreach ($this->getCompressors() as $name => $val) { + return (new SessionHelpers\Runner()) + ->prefix($this->sessionPrefix()) + ->handler($this->sessionSaveHandler()) + ->savePath($this->sessionSavePath()); + } - $id = $this->generateSessionId(); - $res = $this->startSessionProcess($id, 0, false, 300, true, null, - -1, 0, "testing_compression_$name", 1440, - $name); + public function testSession_compression() { + foreach ($this->getCompressors() as $name => $val) { + $data = "testing_compression_$name"; - $this->assertTrue($res); + $runner = $this->sessionRunner() + ->maxExecutionTime(300) + ->lockingEnabled(true) + ->lockWaitTime(-1) + ->lockExpires(0) + ->data($data) + ->compression($name); - $key = $this->sessionPrefix . $id; + $this->assertEquals('SUCCESS', $runner->execFg()); $this->redis->setOption(Redis::OPT_COMPRESSION, $val); - $this->assertTrue($this->redis->get($key) !== false); + $this->assertPatternMatch($this->redis->get($runner->getSessionKey()), "/.*$data.*/"); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); } } public function testSession_savedToRedis() { - $this->setSessionHandler(); + $runner = $this->sessionRunner(); - $sessionId = $this->generateSessionId(); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); + $this->assertEquals('SUCCESS', $runner->execFg()); + $this->assertKeyExists($this->redis, $runner->getSessionKey()); + } - $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId)); - $this->assertTrue($sessionSuccessful); + protected function sessionWaitUsec() { + return ini_get('redis.session.lock_wait_time') * + ini_get('redis.session.lock_retries'); } - public function testSession_lockKeyCorrect() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); + protected function sessionWaitSec() { + return $this->sessionWaitUsec() / 1000000.0; + } - $this->startSessionProcess($sessionId, 5, true); + public function testSession_lockKeyCorrect() { + $runner = $this->sessionRunner()->sleep(5); - $maxwait = (ini_get('redis.session.lock_wait_time') * - ini_get('redis.session.lock_retries') / - 1000000.00); + $this->assertTrue($runner->execBg()); - $exist = $this->waitForSessionLockKey($sessionId, $maxwait); - $this->assertTrue($exist); + if ( ! $runner->waitForLockKey($this->redis, $this->sessionWaitSec())) { + $this->externalCmdFailure($runner->getCmd(), $runner->output(), + "Failed waiting for session lock key '{$runner->getSessionLockKey()}'", + $runner->getExitCode()); + } } public function testSession_lockingDisabledByDefault() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 5, true, 300, false); - usleep(100000); - - $start = microtime(true); - $sessionSuccessful = $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, false); - $end = microtime(true); - $elapsedTime = $end - $start; + $runner = $this->sessionRunner() + ->lockingEnabled(false) + ->sleep(5); - $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); - $this->assertTrue($elapsedTime < 1); - $this->assertTrue($sessionSuccessful); + $this->assertEquals('SUCCESS', $runner->execFg()); + $this->assertKeyMissing($this->redis, $runner->getSessionLockKey()); } public function testSession_lockReleasedOnClose() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 1, true); - $sleep = ini_get('redis.session.lock_wait_time') * ini_get('redis.session.lock_retries'); - usleep($sleep + 10000); - $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); + $runner = $this->sessionRunner() + ->sleep(1) + ->lockingEnabled(true); + + $this->assertTrue($runner->execBg()); + usleep($this->sessionWaitUsec() + 100000); + $this->assertKeyMissing($this->redis, $runner->getSessionLockKey()); } public function testSession_lock_ttlMaxExecutionTime() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 10, true, 2); + $runner1 = $this->sessionRunner() + ->sleep(10) + ->maxExecutionTime(2); + + $this->assertTrue($runner1->execBg()); usleep(100000); - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); - $end = microtime(true); - $elapsedTime = $end - $start; + $runner2 = $this->sessionRunner() + ->id($runner1->getId()) + ->sleep(0); - $this->assertLess($elapsedTime, 4); - $this->assertTrue($sessionSuccessful); + $st = microtime(true); + $this->assertEquals('SUCCESS', $runner2->execFg()); + $el = microtime(true) - $st; + $this->assertLess($el, 4); } public function testSession_lock_ttlLockExpire() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 10, true, 300, true, null, -1, 2); + + $runner1 = $this->sessionRunner() + ->sleep(10) + ->maxExecutionTime(300) + ->lockingEnabled(true) + ->lockExpires(2); + + $this->assertTrue($runner1->execBg()); usleep(100000); - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); - $end = microtime(true); - $elapsedTime = $end - $start; + $runner2 = $this->sessionRunner() + ->id($runner1->getId()) + ->sleep(0); - $this->assertTrue($elapsedTime < 3); - $this->assertTrue($sessionSuccessful); + $st = microtime(true); + $this->assertEquals('SUCCESS', $runner2->execFg()); + $this->assertLess(microtime(true) - $st, 3); } - public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 2, true, 300, true, null, -1, 1, 'firstProcess'); + public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { + $id = 'test-id'; + + $runner = $this->sessionRunner() + ->sleep(2) + ->lockingEnabled(true) + ->lockExpires(1) + ->data('firstProcess'); + + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0) + ->lockingEnabled(true) + ->lockExpires(10) + ->data('secondProcess'); + + $this->assertTrue($runner->execBg()); usleep(1500000); // 1.5 sec - $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 10, 'secondProcess'); - sleep(1); + $this->assertEquals('SUCCESS', $runner2->execFg()); - $this->assertTrue($writeSuccessful); - $this->assertEquals('secondProcess', $this->getSessionData($sessionId)); + $this->assertEquals($runner->getData(), 'secondProcess'); } public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $writeSuccessful = $this->startSessionProcess($sessionId, 2, false, 300, true, null, -1, 1, 'firstProcess'); + $runner = $this->sessionRunner() + ->sleep(2) + ->lockingEnabled(true) + ->lockExpires(1) + ->data('firstProcess'); - $this->assertFalse($writeSuccessful); - $this->assertTrue('firstProcess' !== $this->getSessionData($sessionId)); + $this->assertNotEquals('SUCCESS', $runner->execFg()); + $this->assertNotEquals('firstProcess', $runner->getData()); } public function testSession_correctLockRetryCount() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); + $runner = $this->sessionRunner() + ->sleep(10); - /* Start another process and wait until it has the lock */ - $this->startSessionProcess($sessionId, 10, true); - if ( ! $this->waitForSessionLockKey($sessionId, 2)) { - $this->assertTrue(false); - return; + $this->assertTrue($runner->execBg()); + if ( ! $runner->waitForLockKey($this->redis, 2)) { + $this->externalCmdFailure($runner->getCmd(), $runner->output(), + "Failed waiting for session lock key", + $runner->getExitCode()); } - $tm1 = microtime(true); - $ok = $this->startSessionProcess($sessionId, 0, false, 10, true, 100000, 10); - if ( ! $this->assertFalse($ok)) return; - $tm2 = microtime(true); + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0) + ->maxExecutionTime(10) + ->lockingEnabled(true) + ->lockWaitTime(100000) + ->lockRetries(10); + + $st = microtime(true); + $ex = $runner2->execFg(); + if (stripos($ex, 'SUCCESS') !== false) { + $this->externalCmdFailure($runner2->getCmd(), $ex, + "Expected failure but lock was acquired!", + $runner2->getExitCode()); + } + $et = microtime(true); - $this->assertTrue($tm2 - $tm1 >= 1 && $tm2 - $tm1 <= 3); + $this->assertBetween($et - $st, 1, 3); } - public function testSession_defaultLockRetryCount() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 10, true); + public function testSession_defaultLockRetryCount() { + $runner = $this->sessionRunner() + ->sleep(10); - $keyname = $this->sessionPrefix . $sessionId . '_LOCK'; - $begin = microtime(true); + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0) + ->lockingEnabled(true) + ->maxExecutionTime(10) + ->lockWaitTime(20000) + ->lockRetries(0); - if ( ! $this->waitForSessionLockKey($sessionId, 3)) { - $this->assertTrue(false); - return; - } + $this->assertTrue($runner->execBg()); - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 20000, 0); - $end = microtime(true); - $elapsedTime = $end - $start; + if ( ! $runner->waitForLockKey($this->redis, 3)) { + $this->externalCmdFailure($runner->getCmd(), $runner->output(), + "Failed waiting for session lock key", + $runner->getExitCode()); + } - $this->assertBetween($elapsedTime, 2, 3); - $this->assertFalse($sessionSuccessful); + $st = microtime(true); + $this->assertNotEquals('SUCCESS', $runner2->execFg()); + $et = microtime(true); + $this->assertBetween($et - $st, 2, 3); } public function testSession_noUnlockOfOtherProcess() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); + $st = microtime(true); + + $sleep = 3; + + $runner = $this->sessionRunner() + ->sleep($sleep) + ->maxExecutionTime(3); $tm1 = microtime(true); /* 1. Start a background process, and wait until we are certain * the lock was attained. */ - $nsec = 3; - $this->startSessionProcess($sessionId, $nsec, true, $nsec); - if ( ! $this->waitForSessionLockKey($sessionId, 1)) { - $this->assertFalse(true); + $this->assertTrue($runner->execBg()); + if ( ! $runner->waitForLockKey($this->redis, 1)) { + $this->assert("Failed waiting for session lock key"); return; } /* 2. Attempt to lock the same session. This should force us to * wait until the first lock is released. */ + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0); + $tm2 = microtime(true); - $ok = $this->startSessionProcess($sessionId, 0, false); + $this->assertEquals('SUCCESS', $runner2->execFg()); $tm3 = microtime(true); - /* 3. Verify that we did in fact have to wait for this lock */ - $this->assertTrue($ok); - $this->assertTrue($tm3 - $tm2 >= $nsec - ($tm2 - $tm1)); + /* 3. Verify we had to wait for this lock */ + $this->assertTrue($tm3 - $tm2 >= $sleep - ($tm2 - $tm1)); } - public function testSession_lockWaitTime() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 1, true, 300); + public function testSession_lockWaitTime() { + + $runner = $this->sessionRunner() + ->sleep(1) + ->maxExecutionTime(300); + + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0) + ->maxExecutionTime(300) + ->lockingEnabled(true) + ->lockWaitTime(3000000); + + $this->assertTrue($runner->execBg()); usleep(100000); - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, 3000000); - $end = microtime(true); - $elapsedTime = $end - $start; + $st = microtime(true); + $this->assertEquals('SUCCESS', $runner2->execFg()); + $et = microtime(true); - $this->assertTrue($elapsedTime > 2.5); - $this->assertTrue($elapsedTime < 3.5); - $this->assertTrue($sessionSuccessful); + $this->assertBetween($et - $st, 2.5, 3.5); } public function testMultipleConnect() { @@ -7729,322 +7765,82 @@ public function testBadOptionValue() { $this->assertFalse(@$this->redis->setOption(pow(2, 32), false)); } - public function testSession_regenerateSessionId_noLock_noDestroy() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId); + protected function regenerateIdHelper(bool $lock, bool $destroy, bool $proxy) { + $data = uniqid('regenerate-id:'); + $runner = $this->sessionRunner() + ->sleep(0) + ->maxExecutionTime(300) + ->lockingEnabled(true) + ->lockRetries(1) + ->data($data); - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); - } + $this->assertEquals('SUCCESS', $runner->execFg()); - public function testSession_regenerateSessionId_noLock_withDestroy() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + $new_id = $runner->regenerateId($lock, $destroy, $proxy); - $newSessionId = $this->regenerateSessionId($sessionId, false, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->assertNotEquals($runner->getId(), $new_id); + $this->assertEquals($runner->getData(), $runner->getData()); } - public function testSession_regenerateSessionId_withLock_noDestroy() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + public function testSession_regenerateSessionId_noLock_noDestroy() { + $this->regenerateIdHelper(false, false, false); + } - $newSessionId = $this->regenerateSessionId($sessionId, true); + public function testSession_regenerateSessionId_noLock_withDestroy() { + $this->regenerateIdHelper(false, true, false); + } - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + public function testSession_regenerateSessionId_withLock_noDestroy() { + $this->regenerateIdHelper(true, false, false); } public function testSession_regenerateSessionId_withLock_withDestroy() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, true, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(true, true, false); } 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($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, false, false, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(false, false, true); } 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($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, false, true, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(false, true, true); } 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($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, true, false, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(true, false, true); } 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($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, true, true, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(true, true, true); } public function testSession_ttl_equalsToSessionLifetime() { - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); - $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); - - $this->assertEquals(600, $ttl); + $runner = $this->sessionRunner()->lifetime(600); + $this->assertEquals('SUCCESS', $runner->execFg()); + $this->assertEquals(600, $this->redis->ttl($runner->getSessionKey())); } public function testSession_ttl_resetOnWrite() { - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); - $this->redis->expire($this->sessionPrefix . $sessionId, 9999); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); - $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); - - $this->assertEquals(600, $ttl); - } - - public function testSession_ttl_resetOnRead() - { - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); - $this->redis->expire($this->sessionPrefix . $sessionId, 9999); - $this->getSessionData($sessionId, 600); - $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); - - $this->assertEquals(600, $ttl); - } - - private function setSessionHandler() - { - $host = $this->getHost() ?: 'localhost'; - - @ini_set('session.save_handler', $this->sessionSaveHandler); - @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 - * @param string $sessionCompression - * - * @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, - $sessionCompression = 'none') - { - if (strpos(php_uname(), 'Windows') !== false) - $this->markTestSkipped(); - - $commandParameters = [ - $this->getFullHostPath(), $this->sessionSaveHandler, $sessionId, - $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, - $sessionData, $sessionLifetime, $locking_enabled ? 1 : 0, - $lock_wait_time ?? 0, $sessionCompression - ]; - - $commandParameters = array_map('escapeshellarg', $commandParameters); - $commandParameters[] = $background ? '>/dev/null 2>&1 &' : '2>&1'; - - $command = self::getPhpCommand('startSession.php') . implode(' ', $commandParameters); - - exec($command, $output); - - if ($background) - return true; - - $result = $output[0] == 'SUCCESS'; - - // var_dump(['command' => $command, 'output' => $output, 'result' => $result]); - - return $result; - } - - /** - * @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 $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); + $runner1 = $this->sessionRunner()->lifetime(600); + $this->assertEquals('SUCCESS', $runner1->execFg()); - return $output[0]; - } + $runner2 = $this->sessionRunner()->id($runner1->getId())->lifetime(1800); + $this->assertEquals('SUCCESS', $runner2->execFg()); - /** - * @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]; + $this->assertEquals(1800, $this->redis->ttl($runner2->getSessionKey())); } - /** - * 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 function getPhpCommand($script) - { - static $cmd = NULL; + public function testSession_ttl_resetOnRead() { + $data = uniqid(__FUNCTION__); - if (!$cmd) { - $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY); - - $test_args = getenv('TEST_PHP_ARGS'); - if ($test_args !== false) { - $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 = array_filter( - ['redis', 'igbinary', 'msgpack', 'json'], - function ($module) use ($modules) { - return strpos($modules, $module) === false; - } - ); - - /* 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"; - } - } - } - } + $runner = $this->sessionRunner()->lifetime(600)->data($data); + $this->assertEquals('SUCCESS', $runner->execFg()); + $this->redis->expire($runner->getSessionKey(), 9999); - return $cmd . ' ' . __DIR__ . '/' . $script . ' '; + $this->assertEquals($data, $runner->getData()); + $this->assertEquals(600, $this->redis->ttl($runner->getSessionKey())); } } ?> diff --git a/tests/SessionHelpers.php b/tests/SessionHelpers.php new file mode 100644 index 0000000000..90ae73beb6 --- /dev/null +++ b/tests/SessionHelpers.php @@ -0,0 +1,353 @@ + null, + 'save-path' => null, + 'id' => null, + 'sleep' => 0, + 'max-execution-time' => 300, + 'locking-enabled' => true, + 'lock-wait-time' => null, + 'lock-retries' => -1, + 'lock-expires' => 0, + 'data' => '', + 'lifetime' => 1440, + 'compression' => 'none', + ]; + + private $prefix = NULL; + private $output_file; + private $exit_code = -1; + private $cmd = NULL; + private $pid; + private $output; + + public function __construct() { + $this->args['id'] = $this->createId(); + } + + public function getExitCode(): int { + return $this->exit_code; + } + + public function getCmd(): ?string { + return $this->cmd; + } + + public function getId(): ?string { + return $this->args['id']; + } + + public function prefix(string $prefix): self { + $this->prefix = $prefix; + return $this; + } + + public function getSessionKey(): string { + return $this->prefix . $this->getId(); + } + + public function getSessionLockKey(): string { + return $this->getSessionKey() . '_LOCK'; + } + + protected function set($setting, $v): self { + $this->args[$setting] = $v; + return $this; + } + + public function handler(string $handler): self { + return $this->set('handler', $handler); + } + + public function savePath(string $path): self { + return $this->set('save-path', $path); + } + + public function id(string $id): self { + return $this->set('id', $id); + } + + public function sleep(int $sleep): self { + return $this->set('sleep', $sleep); + } + + public function maxExecutionTime(int $time): self { + return $this->set('max-execution-time', $time); + } + + public function lockingEnabled(bool $enabled): self { + return $this->set('locking-enabled', $enabled); + } + + public function lockWaitTime(int $time): self { + return $this->set('lock-wait-time', $time); + } + + public function lockRetries(int $retries): self { + return $this->set('lock-retries', $retries); + } + + public function lockExpires(int $expires): self { + return $this->set('lock-expires', $expires); + } + + public function data(string $data): self { + return $this->set('data', $data); + } + + public function lifetime(int $lifetime): self { + return $this->set('lifetime', $lifetime); + } + + public function compression(string $compression): self { + return $this->set('compression', $compression); + } + + protected function validateArgs(array $required) { + foreach ($required as $req) { + if ( ! isset($this->args[$req]) || $this->args[$req] === null) + throw new \Exception("Command requires '$req' arg"); + } + } + + private function createId(): string { + if (function_exists('session_create_id')) + return session_create_id(); + + return uniqid(); + } + + private function getTmpFileName() { + return '/tmp/sessiontmp.txt'; + return tempnam(sys_get_temp_dir(), 'session'); + } + + /* + * @param $client Redis client + * @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 + */ + public function waitForLockKey($redis, $max_wait_sec) { + $now = microtime(true); + + do { + if ($redis->exists($this->getSessionLockKey())) + return true; + usleep(10000); + } while (microtime(true) <= $now + $max_wait_sec); + + return false; + } + + private function appendCmdArgs(array $args): string { + $append = []; + + foreach ($args as $arg => $val) { + if ( ! $val) + continue; + + if (is_string($val)) + $val = escapeshellarg($val); + + $append[] = "--$arg"; + $append[] = $val; + } + + return implode(' ', $append); + } + + private function buildPhpCmd(string $script, array $args): string { + return PhpSpawner::cmd($script) . ' ' . $this->appendCmdArgs($args); + } + + private function startSessionCmd(): string { + return $this->buildPhpCmd(self::start_script, $this->args); + } + + public function output(?int $timeout = NULL): ?string { + if ($this->output) { + var_dump("early return"); + return $this->output; + } + + if ( ! $this->output_file || ! $this->pid) { + throw new \Exception("Process was not started in the background"); + } + + $st = microtime(true); + + do { + if (pcntl_waitpid($this->pid, $exit_code, WNOHANG) == 0) + break; + usleep(100000); + } while ((microtime(true) - $st) < $timeout); + + if ( ! file_exists($this->output_file)) + return ""; + + $this->output = file_get_contents($this->output_file); + $this->output_file = NULL; + $this->exit_code = $exit_code; + $this->pid = NULL; + + return $this->output; + } + + public function execBg(): bool { + if ($this->cmd) + throw new \Exception("Command already executed!"); + + $output_file = $this->getTmpFileName(); + + $this->cmd = $this->startSessionCmd(); + $this->cmd .= " >$output_file 2>&1 & echo $!"; + + $pid = exec($this->cmd, $output, $exit_code); + $this->exit_code = $exit_code; + + if ($this->exit_code || !is_numeric($pid)) + return false; + + $this->pid = (int)$pid; + $this->output_file = $output_file; + + return true; + } + + public function execFg() { + if ($this->cmd) + throw new \Exception("Command already executed!"); + + $this->cmd = $this->startSessionCmd() . ' 2>&1'; + + exec($this->cmd, $output, $exit_code); + $this->exit_code = $exit_code; + $this->output = implode("\n", array_filter($output)); + + return $this->output; + } + + private function regenerateIdCmd($locking, $destroy, $proxy): string { + $this->validateArgs(['handler', 'id', 'save-path']); + + $args = [ + 'handler' => $this->args['handler'], + 'save-path' => $this->args['save-path'], + 'id' => $this->args['id'], + 'locking-enabled' => !!$locking, + 'destroy' => !!$destroy, + 'proxy' => !!$proxy, + ]; + + return $this->buildPhpCmd(self::regenerate_id_script, $args); + } + + public function regenerateId($locking = false, $destroy = false, $proxy = false) { + if ( ! $this->cmd) + throw new \Exception("Cannot regenerate id before starting session!"); + + $cmd = $this->regenerateIdCmd($locking, $destroy, $proxy); + + exec($cmd, $output, $exit_code); + + if ($exit_code != 0) + return false; + + return $output[0]; + } + + private function getDataCmd(?int $lifetime): string { + $this->validateArgs(['handler', 'save-path', 'id']); + + $args = [ + 'handler' => $this->args['handler'], + 'save-path' => $this->args['save-path'], + 'id' => $this->args['id'], + 'lifetime' => is_int($lifetime) ? $lifetime : $this->args['lifetime'], + ]; + + return $this->buildPhpCmd(self::get_data_script, $args); + } + + public function getData(?int $lifetime = NULL): string { + $cmd = $this->getDataCmd($lifetime); + + exec($cmd, $output, $exit_code); + if ($exit_code != 0) { + return implode("\n", $output); + } + + return $output[0]; + } +} diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 70d94bda88..8c5b857aa7 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -43,19 +43,6 @@ public function getHost() { return $this->str_host; } public function getPort() { return $this->i_port; } public function getAuth() { return $this->auth; } - /** - * Returns the fully qualified host path, - * which may be used directly for php.ini parameters like session.save_path - * - * @return null|string - */ - protected function getFullHostPath() - { - return $this->str_host - ? 'tcp://' . $this->str_host . ':' . $this->i_port - : null; - } - public static function make_bold($str_msg) { return self::$_boo_colorize ? self::$BOLD_ON . $str_msg . self::$BOLD_OFF @@ -80,13 +67,84 @@ public static function make_warning($str_msg) { : $str_msg; } + protected function printArg($v) { + if (is_null($v)) + return '(null)'; + else if ($v === false || $v === true) + return $v ? '(true)' : '(false)'; + else if (is_string($v)) + return "'$v'"; + else + return print_r($v, true); + } + + protected function findTestFunction($bt) { + $i = 0; + while (isset($bt[$i])) { + if (substr($bt[$i]['function'], 0, 4) == 'test') + return $bt[$i]['function']; + $i++; + } + return NULL; + } + + protected function assertionTrace(?string $fmt = NULL, ...$args) { + $prefix = 'Assertion failed:'; + + $lines = []; + + $bt = debug_backtrace(); + + $msg = $fmt ? vsprintf($fmt, $args) : NULL; + + $fn = $this->findTestFunction($bt); + $lines []= sprintf("%s %s - %s", $prefix, self::make_bold($fn), + $msg ? $msg : '(no message)'); + + array_shift($bt); + + for ($i = 0; $i < count($bt); $i++) { + $file = $bt[$i]['file']; + $line = $bt[$i]['line']; + $fn = $bt[$i+1]['function'] ?? $bt[$i]['function']; + + $lines []= sprintf("%s %s:%d (%s)%s", + str_repeat(' ', strlen($prefix)), $file, $line, + $fn, $msg ? " $msg" : ''); + + if (substr($fn, 0, 4) == 'test') + break; + } + + return implode("\n", $lines) . "\n"; + } + + protected function assert($fmt, ...$args) { + self::$errors []= $this->assertionTrace($fmt, ...$args); + } + protected function assertFalse($bool) { - if(!$bool) + if( ! $bool) return true; + self::$errors []= $this->assertionTrace(); - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + return false; + } + + protected function assertKeyExists($redis, $key) { + if ($redis->exists($key)) + return true; + + self::$errors []= $this->assertionTrace("Key '%s' does not exist.", $key); + + return false; + } + + protected function assertKeyMissing($redis, $key) { + if ( ! $redis->exists($key)) + return true; + + self::$errors []= $this->assertionTrace("Key '%s' exists but shouldn't.", $key); return false; } @@ -95,40 +153,42 @@ protected function assertTrue($bool, $msg='') { if($bool) return true; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s) %s\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); + self::$errors []= $this->assertionTrace($msg); return false; } - protected function assertInArray($ele, $arr, $cb = NULL) { - if ($cb && !is_callable($cb)) - die("Fatal: assertInArray callback must be callable!\n"); + protected function assertInArray($ele, $arr, ?callable $cb = NULL) { + $cb ??= function ($v) { return true; }; - if (($in = in_array($ele, $arr)) && (!$cb || $cb($arr[array_search($ele, $arr)]))) - return true; + $key = array_search($ele, $arr); + if ($key !== false && ($valid = $cb($ele))) + return true; - $bt = debug_backtrace(false); - $ex = $in ? 'validation' : 'missing'; - self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $ele); + self::$errors []= $this->assertionTrace("%s %s %s", $this->printArg($ele), + $key === false ? 'missing from' : 'is invalid in', + $this->printArg($arr)); return false; } - protected function assertArrayKey($arr, $key, $cb = NULL) { - if ($cb && !is_callable($cb)) - die("Fatal: assertArrayKey callback must be callable\n"); + protected function assertArrayKey($arr, $key, callable $cb = NULL) { + $cb ??= function ($v) { return true; }; - if (($exists = isset($arr[$key])) && (!$cb || $cb($arr[$key]))) + if (($exists = isset($arr[$key])) && $cb($arr[$key])) return true; - $bt = debug_backtrace(false); - $ex = $exists ? 'validation' : 'missing'; - self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $key); + + if ($exists) { + $msg = sprintf("%s is invalid in %s", $this->printArg($arr[$key]), + $this->printArg($arr)); + } else { + $msg = sprintf("%s is not a key in %s", $this->printArg($key), + $this->printArg($arr)); + } + + self::$errors []= $this->assertionTrace($msg); return false; } @@ -140,9 +200,7 @@ protected function assertValidate($val, $cb) { if ($cb($val)) return true; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n--- VALUE ---\n%s\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], print_r($val, true)); + self::$errors []= $this->assertionTrace("%s is invalid.", $this->printArg($val)); return false; } @@ -163,62 +221,101 @@ protected function assertThrowsMatch($arg, $cb, $regex = NULL) { if ($threw && $match) return true; - $bt = debug_backtrace(false); +// $bt = debug_backtrace(false); $ex = !$threw ? 'no exception' : "no match '$regex'"; - self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s]\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex); + self::$errors []= $this->assertionTrace("[$ex]"); +// return false; } protected function assertLess($a, $b) { if($a < $b) - return; + return true; + + self::$errors []= $this->assertionTrace("%s >= %s", $a, $b); + return false; + } + + protected function assertMore($a, $b) { + if($a > $b) + return true; + + self::$errors [] = $this->assertionTrace("%s <= %s", $a, $b); + + return false; + } + + protected function externalCmdFailure($cmd, $output, $msg = NULL, $exit_code = NULL) { $bt = debug_backtrace(false); - self::$errors[] = sprintf("Assertion failed (%s >= %s): %s: %d (%s\n", - print_r($a, true), print_r($b, true), - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + + $lines[] = sprintf("Assertion failed: %s:%d (%s)", + $bt[0]['file'], $bt[0]['line'], + self::make_bold($bt[0]['function'])); + + + if ($msg) + $lines[] = sprintf(" Message: %s", $msg); + if ($exit_code !== NULL) + $lines[] = sprintf(" Exit code: %d", $exit_code); + $lines[] = sprintf( " Command: %s", $cmd); + if ($output) + $lines[] = sprintf(" Output: %s", $output); + + self::$errors[] = implode("\n", $lines) . "\n"; } protected function assertBetween($value, $min, $max, bool $exclusive = false) { if ($exclusive) { if ($value > $min && $value < $max) - return; + return true; } else { if ($value >= $min && $value <= $max) - return; + return true; } - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed (%s not between %s and %s): %s:%d (%s)\n", - print_r($value, true), print_r($min, true), print_r($max, true), - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + self::$errors []= $this->assertionTrace(sprintf("'%s' not between '%s' and '%s'", + $value, $min, $max)); + + return false; } protected function assertEquals($a, $b) { if($a === $b) - return; + return true; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed (%s !== %s): %s:%d (%s)\n", - print_r($a, true), print_r($b, true), - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + self::$errors[] = $this->assertionTrace("%s !== %s", $this->printArg($a), + $this->printArg($b)); + + return false; + } + + public function assertNotEquals($a, $b) { + if($a !== $b) + return true; + + self::$errors []= $this->assertionTrace("%s === %s", $this->printArg($a), + $this->printArg($b)); + + return false; } protected function assertPatternMatch($str_test, $str_regex) { if (preg_match($str_regex, $str_test)) - return; + return true; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed ('%s' doesnt match '%s'): %s:%d (%s)\n", - $str_test, $str_regex, $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + self::$errors []= $this->assertionTrace("'%s' doesnt match '%s'", + $str_test, $str_regex); + + return false; } protected function markTestSkipped($msg='') { $bt = debug_backtrace(false); self::$warnings []= sprintf("Skipped test: %s:%d (%s) %s\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); + $bt[0]["file"], $bt[0]["line"], + $bt[1]["function"], $msg); throw new TestSkippedException($msg); } diff --git a/tests/getSessionData.php b/tests/getSessionData.php index d49256c218..c97da57ead 100644 --- a/tests/getSessionData.php +++ b/tests/getSessionData.php @@ -1,22 +1,33 @@ Date: Fri, 24 May 2024 12:07:10 -0700 Subject: [PATCH 1880/1986] Update unit test assertions. Our tests have a ton of instances where we do something like: ```php $this->assert(TRUE === $this->redis->command1()); $this->assert($this->redis->command2() === 42); ``` Which should be written like this: ```php $this->assertTrue($this->command1()); $this->assertEquals(42, $this->command2()); ``` Additionally it changes some assertions to use more relevant assertions like `assertInArray` rather than `assertTrue(in_array())`. * Add `assertEqualsCanonicalizing` assertion similar to what PHPUnit has. * Add `assertStringContains` helper assertion. --- tests/RedisClusterTest.php | 63 +- tests/RedisTest.php | 2982 ++++++++++++++++++------------------ tests/TestSuite.php | 78 +- 3 files changed, 1598 insertions(+), 1525 deletions(-) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 2c4420e135..df9c53c27b 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -27,38 +27,39 @@ class Redis_Cluster_Test extends Redis_Test { * RedisCluster class doesn't implement specialized (non-redis) commands * such as sortAsc, or sortDesc and other commands such as SELECT are * simply invalid in Redis Cluster */ - public function testSortAsc() { return $this->markTestSkipped(); } - public function testSortDesc() { return $this->markTestSkipped(); } - public function testWait() { return $this->markTestSkipped(); } - public function testSelect() { return $this->markTestSkipped(); } - public function testReconnectSelect() { return $this->markTestSkipped(); } - public function testMultipleConnect() { return $this->markTestSkipped(); } - public function testDoublePipeNoOp() { return $this->markTestSkipped(); } - public function testSwapDB() { return $this->markTestSkipped(); } - public function testConnectException() { return $this->markTestSkipped(); } - public function testTlsConnect() { return $this->markTestSkipped(); } - public function testReset() { return $this->markTestSkipped(); } - public function testInvalidAuthArgs() { return $this->markTestSkipped(); } - public function testScanErrors() { return $this->markTestSkipped(); } + public function testPipelinePublish() { $this->markTestSkipped(); } + public function testSortAsc() { $this->markTestSkipped(); } + public function testSortDesc() { $this->markTestSkipped(); } + public function testWait() { $this->markTestSkipped(); } + public function testSelect() { $this->markTestSkipped(); } + public function testReconnectSelect() { $this->markTestSkipped(); } + public function testMultipleConnect() { $this->markTestSkipped(); } + public function testDoublePipeNoOp() { $this->markTestSkipped(); } + public function testSwapDB() { $this->markTestSkipped(); } + public function testConnectException() { $this->markTestSkipped(); } + public function testTlsConnect() { $this->markTestSkipped(); } + public function testReset() { $this->markTestSkipped(); } + public function testInvalidAuthArgs() { $this->markTestSkipped(); } + public function testScanErrors() { $this->markTestSkipped(); } /* These 'directed node' commands work differently in RedisCluster */ - public function testConfig() { return $this->markTestSkipped(); } - public function testFlushDB() { return $this->markTestSkipped(); } - public function testFunction() { return $this->markTestSkipped(); } + public function testConfig() { $this->markTestSkipped(); } + public function testFlushDB() { $this->markTestSkipped(); } + public function testFunction() { $this->markTestSkipped(); } /* Session locking feature is currently not supported in in context of Redis Cluster. The biggest issue for this is the distribution nature of Redis cluster */ - public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); } - public function testSession_lockingDisabledByDefault() { return $this->markTestSkipped(); } - public function testSession_lockReleasedOnClose() { return $this->markTestSkipped(); } - public function testSession_ttlMaxExecutionTime() { return $this->markTestSkipped(); } - public function testSession_ttlLockExpire() { return $this->markTestSkipped(); } - public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { return $this->markTestSkipped(); } - public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { return $this->markTestSkipped(); } - public function testSession_correctLockRetryCount() { return $this->markTestSkipped(); } - public function testSession_defaultLockRetryCount() { return $this->markTestSkipped(); } - public function testSession_noUnlockOfOtherProcess() { return $this->markTestSkipped(); } - public function testSession_lockWaitTime() { return $this->markTestSkipped(); } + public function testSession_lockKeyCorrect() { $this->markTestSkipped(); } + public function testSession_lockingDisabledByDefault() { $this->markTestSkipped(); } + public function testSession_lockReleasedOnClose() { $this->markTestSkipped(); } + public function testSession_ttlMaxExecutionTime() { $this->markTestSkipped(); } + public function testSession_ttlLockExpire() { $this->markTestSkipped(); } + public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { $this->markTestSkipped(); } + public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { $this->markTestSkipped(); } + public function testSession_correctLockRetryCount() { $this->markTestSkipped(); } + public function testSession_defaultLockRetryCount() { $this->markTestSkipped(); } + public function testSession_noUnlockOfOtherProcess() { $this->markTestSkipped(); } + public function testSession_lockWaitTime() { $this->markTestSkipped(); } /* Load our seeds on construction */ public function __construct($str_host, $i_port, $str_auth) { @@ -692,7 +693,7 @@ public function testReplyLiteral() { the command to a specific node. */ public function testAcl() { if ( ! $this->minVersionCheck("6.0")) - return $this->markTestSkipped(); + $this->markTestSkipped(); $this->assertInArray('default', $this->redis->acl('foo', 'USERS')); } @@ -702,9 +703,9 @@ public function testSession() @ini_set('session.save_handler', 'rediscluster'); @ini_set('session.save_path', $this->sessionSavePath() . '&failover=error'); - if (!@session_start()) { - return $this->markTestSkipped(); - } + if (!@session_start()) + $this->markTestSkipped(); + session_write_close(); $this->assertKeyExists($this->sessionPrefix() . session_id()); diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 0cf6d9fc8e..cb1ecd4b9e 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1,7 +1,7 @@ -redis = $this->newInstance(); $info = $this->redis->info(); $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); - $this->is_keydb = $this->redis->info('keydb') !== false; } @@ -105,7 +104,7 @@ protected function sessionSaveHandler(): string { } protected function sessionSavePath(): string { - return sprintf("tcp://%s:%d?%s", $this->getHost(), $this->getPort(), + return sprintf('tcp://%s:%d?%s', $this->getHost(), $this->getPort(), $this->getAuthFragment()); } @@ -113,9 +112,9 @@ protected function getAuthFragment() { $this->getAuthParts($user, $pass); if ($user && $pass) { - return sprintf("auth[user]=%s&auth[pass]=%s", $user, $pass); + return sprintf('auth[user]=%s&auth[pass]=%s', $user, $pass); } else if ($pass) { - return sprintf("auth[pass]=%s", $pass); + return sprintf('auth[pass]=%s', $pass); } else { return ''; } @@ -147,8 +146,7 @@ public function reset() /* Helper function to determine if the clsas has pipeline support */ protected function havePipeline() { - $str_constant = get_class($this->redis) . '::PIPELINE'; - return defined($str_constant); + return defined(get_class($this->redis) . '::PIPELINE'); } protected function haveMulti() { @@ -158,7 +156,7 @@ protected function haveMulti() { public function testMinimumVersion() { // Minimum server version required for tests - $this->assertTrue(version_compare($this->version, "2.4.0") >= 0); + $this->assertTrue(version_compare($this->version, '2.4.0') >= 0); } public function testPing() { @@ -169,19 +167,17 @@ public function testPing() { /* Make sure we're good in MULTI mode */ if ($this->haveMulti()) { - $this->redis->multi(); - $this->redis->ping(); - $this->redis->ping('BEEP'); - $this->assertEquals([true, 'BEEP'], $this->redis->exec()); + $this->assertEquals( + [true, 'BEEP'], + $this->redis->multi() + ->ping() + ->ping('BEEP') + ->exec() + ); } } public function testPipelinePublish() { - if (!$this->havePipeline()) { - $this->markTestSkipped(); - return; - } - $ret = $this->redis->pipeline() ->publish('chan', 'msg') ->exec(); @@ -193,41 +189,40 @@ public function testPipelinePublish() { // 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) { + 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)); + $result = $this->redis->pubsub('channels', '*'); + $this->assertIsArray($result); + $result = $this->redis->pubsub('channels'); + $this->assertIsArray($result); // PUBSUB NUMSUB $c1 = uniqid() . '-' . rand(1,100); $c2 = uniqid() . '-' . rand(1,100); - $result = $this->redis->pubsub("numsub", [$c1, $c2]); + $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); + $this->assertIsArray($result); + $this->assertEquals(2, count($result)); // 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); + $this->assertArrayKey($result, $channel, function($v) { return $v === 0; }); } // PUBSUB NUMPAT - $result = $this->redis->pubsub("numpat"); - $this->assertTrue(is_int($result)); + $result = $this->redis->pubsub('numpat'); + $this->assertIsInt($result); // Invalid calls - $this->assertFalse(@$this->redis->pubsub("notacommand")); - $this->assertFalse(@$this->redis->pubsub("numsub", "not-an-array")); + $this->assertFalse(@$this->redis->pubsub('notacommand')); + $this->assertFalse(@$this->redis->pubsub('numsub', 'not-an-array')); } /* These test cases were generated randomly. We're just trying to test @@ -260,8 +255,8 @@ public function testBitop() { if (!$this->minVersionCheck('2.6.0')) $this->markTestSkipped(); - $this->redis->set("{key}1", "foobar"); - $this->redis->set("{key}2", "abcdef"); + $this->redis->set('{key}1', 'foobar'); + $this->redis->set('{key}2', 'abcdef'); // Regression test for GitHub issue #2210 $this->assertEquals(6, $this->redis->bitop('AND', '{key}1', '{key}2')); @@ -278,7 +273,7 @@ public function testBitsets() { $this->redis->del('key'); $this->assertEquals(0, $this->redis->getBit('key', 0)); - $this->assertEquals(FALSE, $this->redis->getBit('key', -1)); + $this->assertFalse($this->redis->getBit('key', -1)); $this->assertEquals(0, $this->redis->getBit('key', 100000)); $this->redis->set('key', "\xff"); @@ -341,14 +336,14 @@ public function testLcs() { } public function testLmpop() { - if(version_compare($this->version, "7.0.0") < 0) { + if(version_compare($this->version, '7.0.0') < 0) { $this->markTestSkipped(); } $key1 = '{l}1'; $key2 = '{l}2'; - $this->assertTrue($this->redis->del($key1, $key2) !== false); + $this->redis->del($key1, $key2); $this->assertEquals(6, $this->redis->rpush($key1, 'A', 'B', 'C', 'D', 'E', 'F')); $this->assertEquals(6, $this->redis->rpush($key2, 'F', 'E', 'D', 'C', 'B', 'A')); @@ -361,14 +356,15 @@ public function testLmpop() { } public function testBLmpop() { - if(version_compare($this->version, "7.0.0") < 0) { + if(version_compare($this->version, '7.0.0') < 0) { $this->markTestSkipped(); } $key1 = '{bl}1'; $key2 = '{bl}2'; - $this->assertTrue($this->redis->del($key1, $key2) !== false); + $this->redis->del($key1, $key2); + $this->assertEquals(2, $this->redis->rpush($key1, 'A', 'B')); $this->assertEquals(2, $this->redis->rpush($key2, 'C', 'D')); @@ -383,14 +379,14 @@ public function testBLmpop() { } function testZmpop() { - if(version_compare($this->version, "7.0.0") < 0) { + if(version_compare($this->version, '7.0.0') < 0) { $this->markTestSkipped(); } $key1 = '{z}1'; $key2 = '{z}2'; - $this->assertTrue($this->redis->del($key1, $key2) !== false); + $this->redis->del($key1, $key2); $this->assertEquals(4, $this->redis->zadd($key1, 0, 'zero', 2, 'two', 4, 'four', 6, 'six')); $this->assertEquals(4, $this->redis->zadd($key2, 1, 'one', 3, 'three', 5, 'five', 7, 'seven')); @@ -412,14 +408,14 @@ function testZmpop() { } function testBZmpop() { - if(version_compare($this->version, "7.0.0") < 0) { + if(version_compare($this->version, '7.0.0') < 0) { $this->markTestSkipped(); } $key1 = '{z}1'; $key2 = '{z}2'; - $this->assertTrue($this->redis->del($key1, $key2) !== false); + $this->redis->del($key1, $key2); $this->assertEquals(2, $this->redis->zadd($key1, 0, 'zero', 2, 'two')); $this->assertEquals(2, $this->redis->zadd($key2, 1, 'one', 3, 'three')); @@ -439,7 +435,7 @@ function testBZmpop() { } public function testBitPos() { - if (version_compare($this->version, "2.8.7") < 0) { + if (version_compare($this->version, '2.8.7') < 0) { $this->MarkTestSkipped(); return; } @@ -447,16 +443,16 @@ public function testBitPos() { $this->redis->del('bpkey'); $this->redis->set('bpkey', "\xff\xf0\x00"); - $this->assertEquals($this->redis->bitpos('bpkey', 0), 12); + $this->assertEquals(12, $this->redis->bitpos('bpkey', 0)); $this->redis->set('bpkey', "\x00\xff\xf0"); - $this->assertEquals($this->redis->bitpos('bpkey', 1, 0), 8); - $this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 8); + $this->assertEquals(8, $this->redis->bitpos('bpkey', 1, 0)); + $this->assertEquals(8, $this->redis->bitpos('bpkey', 1, 1)); $this->redis->set('bpkey', "\x00\x00\x00"); - $this->assertEquals($this->redis->bitpos('bpkey', 1), -1); + $this->assertEquals(-1, $this->redis->bitpos('bpkey', 1)); - if (!$this->minVersionCheck("7.0.0")) + if (!$this->minVersionCheck('7.0.0')) return; $this->redis->set('bpkey', "\xF"); @@ -477,29 +473,29 @@ public function test1000() { } public function testEcho() { - $this->assertEquals($this->redis->echo("hello"), "hello"); - $this->assertEquals($this->redis->echo(""), ""); - $this->assertEquals($this->redis->echo(" 0123 "), " 0123 "); + $this->assertEquals('hello', $this->redis->echo('hello')); + $this->assertEquals('', $this->redis->echo('')); + $this->assertEquals(' 0123 ', $this->redis->echo(' 0123 ')); } public function testErr() { $this->redis->set('x', '-ERR'); - $this->assertEquals($this->redis->get('x'), '-ERR'); + $this->assertEquals('-ERR', $this->redis->get('x')); } public function testSet() { - $this->assertEquals(TRUE, $this->redis->set('key', 'nil')); + $this->assertTrue($this->redis->set('key', 'nil')); $this->assertEquals('nil', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', 'val')); + $this->assertTrue($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->assertFalse($this->redis->get('keyNotExist')); $this->redis->set('key2', 'val'); $this->assertEquals('val', $this->redis->get('key2')); @@ -524,35 +520,35 @@ public function testSet() $this->redis->set('key', $value2); $this->assertEquals($value2, $this->redis->get('key')); $this->redis->del('key'); - $this->assertEquals(False, $this->redis->get('key')); + $this->assertFalse($this->redis->get('key')); $data = gzcompress('42'); - $this->assertEquals(True, $this->redis->set('key', $data)); + $this->assertTrue($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->assertTrue($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->assertTrue($this->redis->set('key', 0)); $this->assertEquals('0', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', 1)); + $this->assertTrue($this->redis->set('key', 1)); $this->assertEquals('1', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', 0.1)); + $this->assertTrue($this->redis->set('key', 0.1)); $this->assertEquals('0.1', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', '0.1')); + $this->assertTrue($this->redis->set('key', '0.1')); $this->assertEquals('0.1', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', TRUE)); + $this->assertTrue($this->redis->set('key', TRUE)); $this->assertEquals('1', $this->redis->get('key')); - $this->assertEquals(True, $this->redis->set('key', '')); + $this->assertTrue($this->redis->set('key', '')); $this->assertEquals('', $this->redis->get('key')); - $this->assertEquals(True, $this->redis->set('key', NULL)); + $this->assertTrue($this->redis->set('key', NULL)); $this->assertEquals('', $this->redis->get('key')); - $this->assertEquals(True, $this->redis->set('key', gzcompress('42'))); + $this->assertTrue($this->redis->set('key', gzcompress('42'))); $this->assertEquals('42', gzuncompress($this->redis->get('key'))); } @@ -567,13 +563,13 @@ public function testExtendedSet() { /* 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); + $this->assertEquals('bar', $this->redis->get('foo')); + $this->assertEquals(20, $this->redis->ttl('foo')); /* 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'); + $this->assertEquals(20, $this->redis->ttl('foo')); + $this->assertEquals('bar-20.5', $this->redis->get('foo')); /* Invalid third arguments */ $this->assertFalse(@$this->redis->set('foo','bar','baz')); @@ -582,18 +578,18 @@ public function testExtendedSet() { /* Set if not exist */ $this->redis->del('foo'); $this->assertTrue($this->redis->set('foo','bar', ['nx'])); - $this->assertEquals($this->redis->get('foo'), 'bar'); + $this->assertEquals('bar', $this->redis->get('foo')); $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->assertEquals('bar', $this->redis->get('foo')); $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); + $this->assertEquals(100, $this->redis->ttl('foo')); /* Set with a PTTL */ $this->assertTrue($this->redis->set('foo','bar',['px'=>100000])); @@ -601,26 +597,26 @@ public function testExtendedSet() { /* 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'); + $this->assertEquals(105, $this->redis->ttl('foo')); + $this->assertEquals('bar', $this->redis->get('foo')); /* 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->assertEquals(110, $this->redis->ttl('foo')); + $this->assertEquals('bar', $this->redis->get('foo')); $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'); + $this->assertEquals(200, $this->redis->ttl('foo')); + $this->assertEquals('barbaz', $this->redis->get('foo')); /* 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->assertEquals('bar', $this->redis->get('foo')); $this->assertTrue($this->redis->ttl('foo')<0); /* Make sure we ignore bad/non-string options (regression test for #1835) */ @@ -628,7 +624,7 @@ public function testExtendedSet() { $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) + if (version_compare($this->version, '6.0.0') < 0) return; /* KEEPTTL works by itself */ @@ -642,23 +638,23 @@ public function testExtendedSet() { $this->redis->set('foo', 'bar', ['XX']); $this->assertTrue($this->redis->ttl('foo') == -1); - if (version_compare($this->version, "6.2.0") < 0) + if (version_compare($this->version, '6.2.0') < 0) return; - $this->assertTrue($this->redis->set('foo', 'baz', ['GET']) === 'bar'); + $this->assertEquals('bar', $this->redis->set('foo', 'baz', ['GET'])); } 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'); + $this->assertFalse($this->redis->getSet('key', '42')); + $this->assertEquals('42', $this->redis->getSet('key', '123')); + $this->assertEquals('123', $this->redis->getSet('key', '123')); } public function testRandomKey() { for($i = 0; $i < 1000; $i++) { $k = $this->redis->randomKey(); - $this->assertEquals($this->redis->exists($k), 1); + $this->assertKeyExists($this->redis, $k); } } @@ -667,7 +663,7 @@ public function testRename() { $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->assertFalse($this->redis->get('{key}0')); $this->assertEquals('val0', $this->redis->get('{key}1')); } @@ -676,9 +672,9 @@ public function testRenameNx() { $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'); + $this->assertFalse($this->redis->renameNx('{key}0', '{key}1')); + $this->assertEquals('val0', $this->redis->get('{key}0')); + $this->assertEquals('val1', $this->redis->get('{key}1')); // lists $this->redis->del('{key}0'); @@ -687,14 +683,14 @@ public function testRenameNx() { $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->assertFalse($this->redis->renameNx('{key}0', '{key}1')); + $this->assertEquals(['val1', 'val0'], $this->redis->lRange('{key}0', 0, -1)); + $this->assertEquals(['val1-1', 'val1-0'], $this->redis->lRange('{key}1', 0, -1)); $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']); + $this->assertTrue($this->redis->renameNx('{key}0', '{key}2')); + $this->assertEquals([], $this->redis->lRange('{key}0', 0, -1)); + $this->assertEquals(['val1', 'val0'], $this->redis->lRange('{key}2', 0, -1)); } public function testMultiple() { @@ -741,7 +737,7 @@ public function testSetTimeout() { $this->redis->expire('key', 1); $this->assertEquals('value', $this->redis->get('key')); sleep(2); - $this->assertEquals(False, $this->redis->get('key')); + $this->assertFalse($this->redis->get('key')); } /* This test is prone to failure in the Travis container, so attempt to mitigate this by running more than once */ @@ -793,7 +789,7 @@ function testExpireOptions() { } public function testExpiretime() { - if(version_compare($this->version, "7.0.0") < 0) { + if(version_compare($this->version, '7.0.0') < 0) { $this->markTestSkipped(); } @@ -810,27 +806,27 @@ public function testExpiretime() { 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'); + $this->assertTrue($this->redis->setex('key', 7, 'val')); + $this->assertEquals(7, $this->redis->ttl('key')); + $this->assertEquals('val', $this->redis->get('key')); } public function testPSetEx() { $this->redis->del('key'); - $this->assertTrue($this->redis->psetex('key', 7 * 1000, 'val') === TRUE); - $this->assertTrue($this->redis->ttl('key') ===7); - $this->assertTrue($this->redis->get('key') === 'val'); + $this->assertTrue($this->redis->psetex('key', 7 * 1000, 'val')); + $this->assertEquals(7, $this->redis->ttl('key')); + $this->assertEquals('val', $this->redis->get('key')); } public function testSetNX() { $this->redis->set('key', 42); - $this->assertTrue($this->redis->setnx('key', 'err') === FALSE); - $this->assertTrue($this->redis->get('key') === '42'); + $this->assertFalse($this->redis->setnx('key', 'err')); + $this->assertEquals('42', $this->redis->get('key')); $this->redis->del('key'); - $this->assertTrue($this->redis->setnx('key', '42') === TRUE); - $this->assertTrue($this->redis->get('key') === '42'); + $this->assertTrue($this->redis->setnx('key', '42')); + $this->assertEquals('42', $this->redis->get('key')); } public function testExpireAtWithLong() { @@ -839,8 +835,8 @@ public function testExpireAtWithLong() { } $longExpiryTimeExceedingInt = 3153600000; $this->redis->del('key'); - $this->assertTrue($this->redis->setex('key', $longExpiryTimeExceedingInt, 'val') === TRUE); - $this->assertTrue($this->redis->ttl('key') === $longExpiryTimeExceedingInt); + $this->assertTrue($this->redis->setex('key', $longExpiryTimeExceedingInt, 'val')); + $this->assertEquals($longExpiryTimeExceedingInt, $this->redis->ttl('key')); } public function testIncr() @@ -870,10 +866,10 @@ public function testIncr() $this->redis->set('key', 'abc'); $this->redis->incr('key'); - $this->assertTrue("abc" === $this->redis->get('key')); + $this->assertEquals('abc', $this->redis->get('key')); $this->redis->incr('key'); - $this->assertTrue("abc" === $this->redis->get('key')); + $this->assertEquals('abc', $this->redis->get('key')); $this->redis->set('key', 0); $this->assertEquals(PHP_INT_MAX, $this->redis->incrby('key', PHP_INT_MAX)); @@ -882,7 +878,7 @@ public function testIncr() public function testIncrByFloat() { // incrbyfloat is new in 2.6.0 - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) { $this->markTestSkipped(); } @@ -902,20 +898,19 @@ public function testIncrByFloat() $this->redis->set('key', 'abc'); $this->redis->incrbyfloat('key', 1.5); - $this->assertTrue("abc" === $this->redis->get('key')); + $this->assertEquals('abc', $this->redis->get('key')); $this->redis->incrbyfloat('key', -1.5); - $this->assertTrue("abc" === $this->redis->get('key')); + $this->assertEquals('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.8, floatval($this->redis->get('key'))); // convert to float to avoid rounding issue on arm + $this->assertEquals(1.8, floatval($this->redis->get('key'))); $this->redis->setOption(Redis::OPT_PREFIX, ''); - $this->assertEquals(1, $this->redis->exists('someprefix:key')); + $this->assertKeyExists($this->redis, 'someprefix:key'); $this->redis->del('someprefix:key'); - } public function testDecr() @@ -1009,27 +1004,27 @@ protected function genericDelUnlink($cmd) { $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)); + $this->assertFalse($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')); + $this->assertFalse($this->redis->get('x')); + $this->assertFalse($this->redis->get('y')); + $this->assertFalse($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')); + $this->assertFalse($this->redis->get('x')); + $this->assertFalse($this->redis->get('y')); + $this->assertFalse($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->assertFalse($this->redis->get('y')); $this->redis->set('x', 0); $this->redis->set('y', 1); @@ -1037,16 +1032,16 @@ protected function genericDelUnlink($cmd) { } public function testDelete() { - $this->genericDelUnlink("DEL"); + $this->genericDelUnlink('DEL'); } public function testUnlink() { - if (version_compare($this->version, "4.0.0") < 0) { + if (version_compare($this->version, '4.0.0') < 0) { $this->markTestSkipped(); return; } - $this->genericDelUnlink("UNLINK"); + $this->genericDelUnlink('UNLINK'); } public function testType() @@ -1076,8 +1071,8 @@ public function testType() // sadd with numeric key $this->redis->del(123); - $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); - $this->assertTrue(['val0'] === $this->redis->sMembers(123)); + $this->assertEquals(1, $this->redis->sAdd(123, 'val0')); + $this->assertEquals(['val0'], $this->redis->sMembers(123)); // zset $this->redis->del('keyZSet'); @@ -1092,7 +1087,7 @@ public function testType() $this->assertEquals(Redis::REDIS_HASH, $this->redis->type('keyHash')); // stream - if ($this->minVersionCheck("5.0")) { + if ($this->minVersionCheck('5.0')) { $this->redis->del('stream'); $this->redis->xAdd('stream', '*', ['foo' => 'bar']); $this->assertEquals(Redis::REDIS_STREAM, $this->redis->type('stream')); @@ -1107,28 +1102,28 @@ public function testType() public function testStr() { $this->redis->set('key', 'val1'); - $this->assertTrue($this->redis->append('key', 'val2') === 8); - $this->assertTrue($this->redis->get('key') === 'val1val2'); + $this->assertEquals(8, $this->redis->append('key', 'val2')); + $this->assertEquals('val1val2', $this->redis->get('key')); $this->redis->del('keyNotExist'); - $this->assertTrue($this->redis->append('keyNotExist', 'value') === 5); - $this->assertTrue($this->redis->get('keyNotExist') === 'value'); + $this->assertEquals(5, $this->redis->append('keyNotExist', 'value')); + $this->assertEquals('value', $this->redis->get('keyNotExist')); $this->redis->set('key', 'This is a string') ; - $this->assertTrue($this->redis->getRange('key', 0, 3) === 'This'); - $this->assertTrue($this->redis->getRange('key', -6, -1) === 'string'); - $this->assertTrue($this->redis->getRange('key', -6, 100000) === 'string'); - $this->assertTrue($this->redis->get('key') === 'This is a string'); + $this->assertEquals('This', $this->redis->getRange('key', 0, 3)); + $this->assertEquals('string', $this->redis->getRange('key', -6, -1)); + $this->assertEquals('string', $this->redis->getRange('key', -6, 100000)); + $this->assertEquals('This is a string', $this->redis->get('key')); $this->redis->set('key', 'This is a string') ; - $this->assertTrue($this->redis->strlen('key') === 16); + $this->assertEquals(16, $this->redis->strlen('key')); $this->redis->set('key', 10) ; - $this->assertTrue($this->redis->strlen('key') === 2); + $this->assertEquals(2, $this->redis->strlen('key')); $this->redis->set('key', '') ; - $this->assertTrue($this->redis->strlen('key') === 0); + $this->assertEquals(0, $this->redis->strlen('key')); $this->redis->set('key', '000') ; - $this->assertTrue($this->redis->strlen('key') === 3); + $this->assertEquals(3, $this->redis->strlen('key')); } // PUSH, POP : LPUSH, LPOP @@ -1149,13 +1144,13 @@ public function testlPop() // 'list' = [ 'val2', 'val', 'val3'] $this->assertEquals('val2', $this->redis->lPop('list')); - if (version_compare($this->version, "6.2.0") < 0) { + if (version_compare($this->version, '6.2.0') < 0) { $this->assertEquals('val', $this->redis->lPop('list')); $this->assertEquals('val3', $this->redis->lPop('list')); } else { $this->assertEquals(['val', 'val3'], $this->redis->lPop('list', 2)); } - $this->assertEquals(FALSE, $this->redis->lPop('list')); + $this->assertFalse($this->redis->lPop('list')); // testing binary data @@ -1179,13 +1174,13 @@ public function testrPop() $this->redis->lPush('list', 'val3'); $this->assertEquals('val2', $this->redis->rPop('list')); - if (version_compare($this->version, "6.2.0") < 0) { + if (version_compare($this->version, '6.2.0') < 0) { $this->assertEquals('val', $this->redis->rPop('list')); $this->assertEquals('val3', $this->redis->rPop('list')); } else { $this->assertEquals(['val', 'val3'], $this->redis->rPop('list', 2)); } - $this->assertEquals(FALSE, $this->redis->rPop('list')); + $this->assertFalse($this->redis->rPop('list')); $this->redis->del('list'); @@ -1214,7 +1209,7 @@ public function testrPopSerialization() { public function testblockingPop() { /* Test with a double timeout in Redis >= 6.0.0 */ - if (version_compare($this->version, "6.0.0") >= 0) { + if (version_compare($this->version, '6.0.0') >= 0) { $this->redis->del('list'); $this->redis->lpush('list', 'val1', 'val2'); $this->assertEquals(['list', 'val2'], $this->redis->blpop(['list'], .1)); @@ -1261,38 +1256,38 @@ public function testllen() $this->assertEquals('val', $this->redis->lPop('list')); $this->assertEquals(0, $this->redis->llen('list')); - $this->assertEquals(FALSE, $this->redis->lPop('list')); + $this->assertFalse($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 + $this->assertFalse($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->assertEquals(0, $this->redis->lPushx('keyNotExists', 'value')); + $this->assertEquals(0, $this->redis->rPushx('keyNotExists', 'value')); $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']); + $this->assertEquals(2, $this->redis->lPushx('key', 'val1')); + $this->assertEquals(3, $this->redis->rPushx('key', 'val2')); + $this->assertEquals(['val1', 'val0', 'val2'], $this->redis->lrange('key', 0, -1)); //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->assertEquals(0, $this->redis->lInsert('keyNotExists', Redis::AFTER, 'val1', 'val2')); + $this->assertEquals(-1, $this->redis->lInsert('key', Redis::BEFORE, 'valX', 'val2')); - $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']); + $this->assertEquals(2, $this->redis->lInsert('key', Redis::AFTER, 'val0', 'val1')); + $this->assertEquals(3, $this->redis->lInsert('key', Redis::BEFORE, 'val0', 'val2')); + $this->assertEquals(['val2', 'val0', 'val1'], $this->redis->lrange('key', 0, -1)); } public function testlPos() @@ -1324,19 +1319,19 @@ public function testltrim() $this->redis->lPush('list', 'val3'); $this->redis->lPush('list', 'val4'); - $this->assertEquals(TRUE, $this->redis->ltrim('list', 0, 2)); + $this->assertTrue($this->redis->ltrim('list', 0, 2)); $this->assertEquals(3, $this->redis->llen('list')); $this->redis->ltrim('list', 0, 0); $this->assertEquals(1, $this->redis->llen('list')); $this->assertEquals('val4', $this->redis->lPop('list')); - $this->assertEquals(TRUE, $this->redis->ltrim('list', 10, 10000)); - $this->assertEquals(TRUE, $this->redis->ltrim('list', 10000, 10)); + $this->assertTrue($this->redis->ltrim('list', 10, 10000)); + $this->assertTrue($this->redis->ltrim('list', 10000, 10)); // test invalid type $this->redis->set('list', 'not a list...'); - $this->assertEquals(FALSE, $this->redis->ltrim('list', 0, 2)); + $this->assertFalse($this->redis->ltrim('list', 0, 2)); } @@ -1403,8 +1398,8 @@ public function testSortAsc() { $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2]])); $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'asc'])); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 4]])); - $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]])); + $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']; @@ -1423,7 +1418,7 @@ public function testSortAsc() { } // SORT list → [ghi, def, abc] - if (version_compare($this->version, "2.5.0") < 0) { + 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 { @@ -1496,7 +1491,7 @@ public function testLindex() { $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->assertFalse($this->redis->lIndex('list', -4)); $this->redis->rPush('list', 'val4'); $this->assertEquals('val4', $this->redis->lIndex('list', 3)); @@ -1536,7 +1531,7 @@ public function testBlmove() { $ret = $this->redis->blmove('{list}0', '{list}1', $this->getLeftConstant(), $this->getLeftConstant(), .1); $et = microtime(true); - $this->assertEquals(false, $ret); + $this->assertFalse($ret); $this->assertTrue($et - $st >= .1); } @@ -1581,10 +1576,10 @@ public function testlrem() { $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->assertFalse($this->redis->get('list')); $this->redis->set('list', 'actually not a list'); - $this->assertEquals(FALSE, $this->redis->lrem('list', 'x')); + $this->assertFalse($this->redis->lrem('list', 'x')); } public function testsAdd() { @@ -1639,24 +1634,24 @@ public function testsMove() { public function testsPop() { $this->redis->del('set0'); - $this->assertTrue($this->redis->sPop('set0') === FALSE); + $this->assertFalse($this->redis->sPop('set0')); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); $v0 = $this->redis->sPop('set0'); - $this->assertTrue(1 === $this->redis->scard('set0')); - $this->assertTrue($v0 === 'val' || $v0 === 'val2'); + $this->assertEquals(1, $this->redis->scard('set0')); + $this->assertInArray($v0, ['val' ,'val2']); $v1 = $this->redis->sPop('set0'); - $this->assertTrue(0 === $this->redis->scard('set0')); - $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); + $this->assertEquals(0, $this->redis->scard('set0')); + $this->assertEqualsCanonicalizing(['val', 'val2'], [$v0, $v1]); - $this->assertTrue($this->redis->sPop('set0') === FALSE); + $this->assertFalse($this->redis->sPop('set0')); } public function testsPopWithCount() { - if (!$this->minVersionCheck("3.2")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('3.2')) { + $this->markTestSkipped(); } $set = 'set0'; @@ -1676,14 +1671,14 @@ public function testsPopWithCount() { 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)); + $this->assertInArray($prefix.$i, $ret); } } } public function testsRandMember() { $this->redis->del('set0'); - $this->assertTrue($this->redis->sRandMember('set0') === FALSE); + $this->assertFalse($this->redis->sRandMember('set0')); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); @@ -1691,8 +1686,8 @@ public function testsRandMember() { $got = []; while(true) { $v = $this->redis->sRandMember('set0'); - $this->assertTrue(2 === $this->redis->scard('set0')); // no change. - $this->assertTrue($v === 'val' || $v === 'val2'); + $this->assertEquals(2, $this->redis->scard('set0')); // no change. + $this->assertInArray($v, ['val', 'val2']); $got[$v] = $v; if(count($got) == 2) { @@ -1713,11 +1708,11 @@ public function testsRandMember() { } $member = $this->redis->srandmember('set0'); - $this->assertTrue(in_array($member, $mems)); + $this->assertInArray($member, $mems); $rmembers = $this->redis->srandmember('set0', $i); foreach($rmembers as $reply_mem) { - $this->assertTrue(in_array($reply_mem, $mems)); + $this->assertInArray($reply_mem, $mems); } $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); @@ -1732,8 +1727,8 @@ public function testSRandMemberWithCount() { $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)); + $this->assertEquals([], $ret_pos); + $this->assertEquals([], $ret_neg); // Add a few items to the set for($i=0;$i<100;$i++) { @@ -1790,23 +1785,15 @@ public function testsismember() $this->assertFalse($this->redis->sismember('set', 'val2')); } - public function testsmembers() - { + 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); + $data = ['val', 'val2', 'val3']; + foreach ($data as $member) { + $this->redis->sAdd('set', $member); + } - $sMembers = $this->redis->sMembers('set'); - sort($sMembers); - $this->assertEquals($array, $sMembers); // test alias + $this->assertEqualsCanonicalizing($data, $this->redis->smembers('set')); } public function testsMisMember() @@ -1837,15 +1824,15 @@ public function testlSet() { $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('val3', $this->redis->lIndex('list', 0)); + $this->assertEquals('val2', $this->redis->lIndex('list', 1)); + $this->assertEquals('val', $this->redis->lIndex('list', 2)); - $this->assertEquals(TRUE, $this->redis->lSet('list', 1, 'valx')); + $this->assertTrue($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'); + $this->assertEquals('val3', $this->redis->lIndex('list', 0)); + $this->assertEquals('valx', $this->redis->lIndex('list', 1)); + $this->assertEquals('val', $this->redis->lIndex('list', 2)); } @@ -1878,40 +1865,40 @@ public function testsInter() { $xy = $this->redis->sInter('{set}odd', '{set}prime'); // odd prime numbers foreach($xy as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($x, $y))); + $this->assertInArray($i, array_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($i, array_intersect($x, $y))); + $this->assertInArray($i, array_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($i, array_intersect($y, $z))); + $this->assertInArray($i, array_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($i, array_intersect($y, $z))); + $this->assertInArray($i, array_intersect($y, $z)); } $zt = $this->redis->sInter('{set}square', '{set}seq'); // prime squares - $this->assertTrue($zt === []); + $this->assertEquals([], $zt); $zt = $this->redis->sInter(['{set}square', '{set}seq']); // prime squares, as array - $this->assertTrue($zt === []); + $this->assertEquals([], $zt); $xyz = $this->redis->sInter('{set}odd', '{set}prime', '{set}square');// odd prime squares - $this->assertTrue($xyz === ['1']); + $this->assertEquals(['1'], $xyz); $xyz = $this->redis->sInter(['{set}odd', '{set}prime', '{set}square']);// odd prime squares, with an array as a parameter - $this->assertTrue($xyz === ['1']); + $this->assertEquals(['1'], $xyz); $nil = $this->redis->sInter([]); - $this->assertTrue($nil === FALSE); + $this->assertFalse($nil); } public function testsInterStore() { @@ -1941,7 +1928,7 @@ public function testsInterStore() { } /* Regression test for passing a single array */ - $this->assertEquals($this->redis->sInterStore(['{set}k', '{set}x', '{set}y']), count(array_intersect($x,$y))); + $this->assertEquals(count(array_intersect($x,$y)), $this->redis->sInterStore(['{set}k', '{set}x', '{set}y'])); $count = $this->redis->sInterStore('{set}k', '{set}x', '{set}y'); // odd prime numbers $this->assertEquals($count, $this->redis->scard('{set}k')); @@ -1961,15 +1948,15 @@ public function testsInterStore() { $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->assertEquals(0, $xyz); $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->assertEquals(0, $xyz); $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); + $this->assertEquals(0, $xyz); } public function testsUnion() { @@ -2001,25 +1988,25 @@ public function testsUnion() { $xy = $this->redis->sUnion('{set}x', '{set}y'); // x U y foreach($xy as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($x, $y))); + $this->assertInArray($i, array_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($i, array_merge($y, $z))); + $this->assertInArray($i, array_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($i, array_merge($z, $t))); + $this->assertInArray($i, array_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($i, array_merge($x, $y, $z))); + $this->assertInArray($i, array_merge($x, $y, $z)); } } @@ -2083,15 +2070,15 @@ public function testsUnionStore() { $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->assertEquals($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->assertEquals($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); + $this->assertEquals(0, $count); } public function testsDiff() { @@ -2123,25 +2110,25 @@ public function testsDiff() { $xy = $this->redis->sDiff('{set}x', '{set}y'); // x U y foreach($xy as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($x, $y))); + $this->assertInArray($i, array_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($i, array_diff($y, $z))); + $this->assertInArray($i, array_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($i, array_diff($z, $t))); + $this->assertInArray($i, array_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($i, array_diff($x, $y, $z))); + $this->assertInArray($i, array_diff($x, $y, $z)); } } @@ -2205,19 +2192,19 @@ public function testsDiffStore() { $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->assertEquals(0, $count); $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->assertEquals(0, $count); $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); + $this->assertEquals(0, $count); } public function testInterCard() { - if(version_compare($this->version, "7.0.0") < 0) { + if(version_compare($this->version, '7.0.0') < 0) { $this->markTestSkipped(); } @@ -2275,23 +2262,23 @@ public function testlrange() { // pos : -3 -2 -1 // list: [val3, val2, val] - $this->assertEquals($this->redis->lrange('list', 0, 0), ['val3']); - $this->assertEquals($this->redis->lrange('list', 0, 1), ['val3', 'val2']); - $this->assertEquals($this->redis->lrange('list', 0, 2), ['val3', 'val2', 'val']); - $this->assertEquals($this->redis->lrange('list', 0, 3), ['val3', 'val2', 'val']); + $this->assertEquals(['val3'], $this->redis->lrange('list', 0, 0)); + $this->assertEquals(['val3', 'val2'], $this->redis->lrange('list', 0, 1)); + $this->assertEquals(['val3', 'val2', 'val'], $this->redis->lrange('list', 0, 2)); + $this->assertEquals(['val3', 'val2', 'val'], $this->redis->lrange('list', 0, 3)); - $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->assertEquals(['val3', 'val2', 'val'], $this->redis->lrange('list', 0, -1)); + $this->assertEquals(['val3', 'val2'], $this->redis->lrange('list', 0, -2)); + $this->assertEquals(['val2', 'val'], $this->redis->lrange('list', -2, -1)); $this->redis->del('list'); - $this->assertEquals($this->redis->lrange('list', 0, -1), []); + $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); + $this->assertEquals(1, $this->redis->dbSize()); } public function testFlushDB() { @@ -2309,23 +2296,23 @@ public function testttl() { // A key with no TTL $this->redis->del('x'); $this->redis->set('x', 'bar'); - $this->assertEquals($this->redis->ttl('x'), -1); + $this->assertEquals(-1, $this->redis->ttl('x')); // A key that doesn't exist (> 2.8 will return -2) - if(version_compare($this->version, "2.8.0") >= 0) { + if(version_compare($this->version, '2.8.0') >= 0) { $this->redis->del('x'); - $this->assertEquals($this->redis->ttl('x'), -2); + $this->assertEquals(-2, $this->redis->ttl('x')); } } 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(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. - $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout + $this->assertTrue($this->redis->persist('x')); // true if there is a timeout + $this->assertEquals(-1, $this->redis->ttl('x')); // -1: timeout has been removed. + $this->assertFalse($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. + $this->assertFalse($this->redis->persist('x')); // false if the key doesn’t exist. } public function testClient() { @@ -2334,7 +2321,7 @@ public function testClient() { /* CLIENT LIST */ $arr_clients = $this->redis->client('list'); - $this->assertTrue(is_array($arr_clients)); + $this->assertIsArray($arr_clients); // Figure out which ip:port is us! $str_addr = NULL; @@ -2353,9 +2340,9 @@ public function testClient() { if (version_compare($this->version, '5.0.0') >= 0) { $this->assertLess(0, $this->redis->client('id')); if (version_compare($this->version, '6.0.0') >= 0) { - $this->assertEquals($this->redis->client('getredir'), -1); + $this->assertEquals(-1, $this->redis->client('getredir')); $this->assertTrue($this->redis->client('tracking', 'on', ['optin' => true])); - $this->assertEquals($this->redis->client('getredir'), 0); + $this->assertEquals(0, $this->redis->client('getredir')); $this->assertTrue($this->redis->client('caching', 'yes')); $this->assertTrue($this->redis->client('tracking', 'off')); if (version_compare($this->version, '6.2.0') >= 0) { @@ -2380,8 +2367,8 @@ public function testClient() { 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->assertIsArray($this->redis->slowlog('get')); + $this->assertIsArray($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')); @@ -2403,7 +2390,7 @@ public function testWait() { $this->redis->set('wait-bar', 'revo9000'); // Make sure we get the right replication count - $this->assertEquals($this->redis->wait($i_slaves, 100), $i_slaves); + $this->assertEquals($i_slaves, $this->redis->wait($i_slaves, 100)); // Pass more slaves than are connected $this->redis->set('wait-foo','over9000'); @@ -2431,37 +2418,37 @@ public function testInfo() { } $keys = [ - "redis_version", - "arch_bits", - "uptime_in_seconds", - "uptime_in_days", - "connected_clients", - "connected_slaves", - "used_memory", - "total_connections_received", - "total_commands_processed", - "role" + '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) { + if (version_compare($this->version, '2.5.0') < 0) { array_push($keys, - "changes_since_last_save", - "bgsave_in_progress", - "last_save_time" + '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" + 'rdb_changes_since_last_save', + 'rdb_bgsave_in_progress', + 'rdb_last_save_time' ); } foreach($keys as $k) { - $this->assertTrue(in_array($k, array_keys($info))); + $this->assertInArray($k, array_keys($info)); } } - if (!$this->minVersionCheck("7.0.0")) + if (!$this->minVersionCheck('7.0.0')) return; $res = $this->redis->info('server', 'memory'); @@ -2471,19 +2458,18 @@ public function testInfo() { public function testInfoCommandStats() { // INFO COMMANDSTATS is new in 2.6.0 - if (version_compare($this->version, "2.5.0") < 0) { - $this->markTestSkipped(); - } + if (version_compare($this->version, '2.5.0') < 0) { + $this->markTestSkipped(); + } - $info = $this->redis->info("COMMANDSTATS"); + $info = $this->redis->info('COMMANDSTATS'); + if ( ! $this->assertIsArray($info)) + return; - $this->assertTrue(is_array($info)); - if (is_array($info)) { foreach($info as $k => $value) { - $this->assertTrue(strpos($k, 'cmdstat_') !== false); + $this->assertStringContains('cmdstat_', $k); } } - } public function testSelect() { $this->assertFalse(@$this->redis->select(-1)); @@ -2491,7 +2477,7 @@ public function testSelect() { } public function testSwapDB() { - if (version_compare($this->version, "4.0.0") < 0) { + if (version_compare($this->version, '4.0.0') < 0) { $this->markTestSkipped(); } @@ -2500,47 +2486,46 @@ public function testSwapDB() { } 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->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->assertFalse($this->redis->mset([])); // set ø → FALSE + $this->assertEquals(['a', 'b', 'c'], $this->redis->mget(['x', 'y', 'z'])); // 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(['a', 'b', 'c'], $this->redis->mget(['x', 'y', 'z'])); // check x y z - /* - * Integer keys - */ + $this->assertFalse($this->redis->mset([])); // set ø → FALSE - // No prefix - $set_array = [-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => '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)); + /* + * Integer keys + */ - // 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, ''); + // No prefix + $set_array = [-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three']; + $this->redis->del(array_keys($set_array)); + $this->assertTrue($this->redis->mset($set_array)); + $this->assertEquals(array_values($set_array), $this->redis->mget(array_keys($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(array_values($set_array), $this->redis->mget(array_keys($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->assertEquals(TRUE, $this->redis->msetnx(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z + $this->assertTrue($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->assertEquals(['a', 'b', 'c'], $this->redis->mget(['x', 'y', 'z'])); // check x y z $this->redis->del('x'); // delete just x - $this->assertEquals(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(['x' => 'A', 'y' => 'B', 'z' => 'C'])); // set x y z + $this->assertEquals([FALSE, 'b', 'c'], $this->redis->mget(['x', 'y', 'z'])); // check x y z $this->assertFalse($this->redis->msetnx([])); // set ø → FALSE } @@ -2554,15 +2539,15 @@ public function testRpopLpush() { $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. + $this->assertEquals('abc', $this->redis->rpoplpush('{list}x', '{list}y')); // we RPOP x, yielding abc. + $this->assertEquals(['def'], $this->redis->lrange('{list}x', 0, -1)); // only def remains in x. + $this->assertEquals(['abc', '456', '123'], $this->redis->lrange('{list}y', 0, -1)); // 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)); + $this->assertFalse($this->redis->rpoplpush('{list}x', '{list}y')); + $this->assertEquals([], $this->redis->lrange('{list}x', 0, -1)); + $this->assertEquals([], $this->redis->lrange('{list}y', 0, -1)); } public function testBRpopLpush() { @@ -2574,25 +2559,25 @@ public function testBRpopLpush() { $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('abc', $this->redis->brpoplpush('{list}x', '{list}y', 1)); // 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. + $this->assertEquals(['def'], $this->redis->lrange('{list}x', 0, -1)); // only def remains in x. + $this->assertEquals(['abc', '456', '123'], $this->redis->lrange('{list}y', 0, -1)); // 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)); + $this->assertFalse($this->redis->brpoplpush('{list}x', '{list}y', 1)); + $this->assertEquals([], $this->redis->lrange('{list}x', 0, -1)); + $this->assertEquals([], $this->redis->lrange('{list}y', 0, -1)); if (!$this->minVersionCheck('6.0.0')) return; // Redis >= 6.0.0 allows floating point timeouts $st = microtime(true); - $this->assertEquals(FALSE, $this->redis->brpoplpush('{list}x', '{list}y', .1)); + $this->assertFalse($this->redis->brpoplpush('{list}x', '{list}y', .1)); $et = microtime(true); - $this->assertTrue($et - $st < 1.0); + $this->assertLess($et - $st, 1.0); } public function testZAddFirstArg() { @@ -2600,10 +2585,10 @@ public function testZAddFirstArg() { $this->redis->del('key'); $zsetName = 100; // not a string! - $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1')); + $this->assertEquals(1, $this->redis->zAdd($zsetName, 0, 'val0')); + $this->assertEquals(1, $this->redis->zAdd($zsetName, 1, 'val1')); - $this->assertTrue(['val0', 'val1'] === $this->redis->zRange($zsetName, 0, -1)); + $this->assertEquals(['val0', 'val1'], $this->redis->zRange($zsetName, 0, -1)); } public function testZaddIncr() { @@ -2618,69 +2603,72 @@ public function testZaddIncr() { public function testZX() { $this->redis->del('key'); - $this->assertTrue([] === $this->redis->zRange('key', 0, -1)); - $this->assertTrue([] === $this->redis->zRange('key', 0, -1, true)); + $this->assertEquals([], $this->redis->zRange('key', 0, -1)); + $this->assertEquals([], $this->redis->zRange('key', 0, -1, true)); - $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); - $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters - if (version_compare($this->version, "3.0.2") < 0) { - $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertEquals(1, $this->redis->zAdd('key', 0, 'val0')); + $this->assertEquals(1, $this->redis->zAdd('key', 2, 'val2')); + $this->assertEquals(2, $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters + if (version_compare($this->version, '3.0.2') < 0) { + $this->assertEquals(1, $this->redis->zAdd('key', 1, 'val1')); + $this->assertEquals(1, $this->redis->zAdd('key', 3, 'val3')); } else { - $this->assertTrue(1 === $this->redis->zAdd('key', [], 1, 'val1')); // empty options - $this->assertTrue(1 === $this->redis->zAdd('key', ['nx'], 3, 'val3')); // nx option - $this->assertTrue(0 === $this->redis->zAdd('key', ['xx'], 3, 'val3')); // xx option + $this->assertEquals(1, $this->redis->zAdd('key', [], 1, 'val1')); // empty options + $this->assertEquals(1, $this->redis->zAdd('key', ['nx'], 3, 'val3')); // nx option + $this->assertEquals(0, $this->redis->zAdd('key', ['xx'], 3, 'val3')); // xx option - if (version_compare($this->version, "6.2.0") >= 0) { - $this->assertTrue(0 === $this->redis->zAdd('key', ['lt'], 4, 'val3')); // lt option - $this->assertTrue(0 === $this->redis->zAdd('key', ['gt'], 2, 'val3')); // gt option + if (version_compare($this->version, '6.2.0') >= 0) { + $this->assertEquals(0, $this->redis->zAdd('key', ['lt'], 4, 'val3')); // lt option + $this->assertEquals(0, $this->redis->zAdd('key', ['gt'], 2, 'val3')); // gt option } } - $this->assertTrue(['val0', 'val1', 'val2', 'val3', 'val4', 'val5'] === $this->redis->zRange('key', 0, -1)); + $this->assertEquals(['val0', 'val1', 'val2', 'val3', 'val4', 'val5'], $this->redis->zRange('key', 0, -1)); // withscores $ret = $this->redis->zRange('key', 0, -1, true); - $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->assertEquals(6, count($ret)); + $this->assertEquals(0.0, $ret['val0']); + $this->assertEquals(1.0, $ret['val1']); + $this->assertEquals(2.0, $ret['val2']); + $this->assertEquals(3.0, $ret['val3']); + $this->assertEquals(4.0, $ret['val4']); + $this->assertEquals(5.0, $ret['val5']); - $this->assertTrue(0 === $this->redis->zRem('key', 'valX')); - $this->assertTrue(1 === $this->redis->zRem('key', 'val3')); - $this->assertTrue(1 === $this->redis->zRem('key', 'val4')); - $this->assertTrue(1 === $this->redis->zRem('key', 'val5')); + $this->assertEquals(0, $this->redis->zRem('key', 'valX')); + $this->assertEquals(1, $this->redis->zRem('key', 'val3')); + $this->assertEquals(1, $this->redis->zRem('key', 'val4')); + $this->assertEquals(1, $this->redis->zRem('key', 'val5')); - $this->assertTrue(['val0', 'val1', 'val2'] === $this->redis->zRange('key', 0, -1)); + $this->assertEquals(['val0', 'val1', 'val2'], $this->redis->zRange('key', 0, -1)); // zGetReverseRange - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); + $this->assertEquals(1, $this->redis->zAdd('key', 3, 'val3')); + $this->assertEquals(1, $this->redis->zAdd('key', 3, 'aal3')); $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); - $this->assertTrue(['val0', 'val1', 'val2', 'aal3', 'val3'] === $zero_to_three || ['val0', 'val1', 'val2', 'val3', 'aal3'] === $zero_to_three); + $this->assertEquals(['val0', 'val1', 'val2', 'aal3', 'val3'], $zero_to_three); $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); - $this->assertTrue(array_reverse(['val0', 'val1', 'val2', 'aal3', 'val3']) === $three_to_zero || array_reverse(['val0', 'val1', 'val2', 'val3', 'aal3']) === $three_to_zero); + $this->assertEquals(array_reverse(['val0', 'val1', 'val2', 'aal3', 'val3']), $three_to_zero); - $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); + $this->assertEquals(5, $this->redis->zCount('key', 0, 3)); // withscores $this->redis->zRem('key', 'aal3'); $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]); - $this->assertTrue(['val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3] == $zero_to_three); - $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); + $this->assertEquals(['val0' => 0.0, 'val1' => 1.0, 'val2' => 2.0, 'val3' => 3.0], $zero_to_three); + $this->assertEquals(4, $this->redis->zCount('key', 0, 3)); // limit - $this->assertTrue(['val0'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 1]])); - $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 2]])); - $this->assertTrue(['val1', 'val2'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]])); - $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]])); + $this->assertEquals(['val0'], $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 1]])); + $this->assertEquals(['val0', 'val1'], + $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 2]])); + $this->assertEquals(['val1', 'val2'], + $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]])); + $this->assertEquals(['val0', 'val1'], + $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]])); if ($this->minVersionCheck('6.2.0')) $this->assertEquals(['val0', 'val1'], $this->redis->zrange('key', 0, 1, ['byscore', 'limit' => [0, 100]])); @@ -2688,15 +2676,24 @@ public function testZX() { // limits as references $limit = [0, 100]; foreach ($limit as &$val) {} - $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => $limit])); + $this->assertEquals(['val0', 'val1'], $this->redis->zRangeByScore('key', 0, 1, ['limit' => $limit])); - $this->assertTrue(['val3'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 1]])); - $this->assertTrue(['val3', 'val2'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 2]])); - $this->assertTrue(['val2', 'val1'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]])); - $this->assertTrue(['val1', 'val0'] === $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]])); + $this->assertEquals( + ['val3'], $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 1]]) + ); + $this->assertEquals( + ['val3', 'val2'], $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 2]]) + ); + $this->assertEquals( + ['val2', 'val1'], $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]]) + ); + $this->assertEquals( + ['val1', 'val0'], $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]]) + ); if ($this->minVersionCheck('6.2.0')) { - $this->assertEquals(['val1', 'val0'], $this->redis->zrange('key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); + $this->assertEquals(['val1', 'val0'], + $this->redis->zrange('key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); $this->assertEquals(2, $this->redis->zrangestore('dst{key}', 'key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); $this->assertEquals(['val0', 'val1'], $this->redis->zRange('dst{key}', 0, -1)); @@ -2706,8 +2703,8 @@ public function testZX() { $this->assertEquals(['val1'], $this->redis->zrange('dst{key}', 0, -1)); } - $this->assertTrue(4 === $this->redis->zCard('key')); - $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); + $this->assertEquals(4, $this->redis->zCard('key')); + $this->assertEquals(1.0, $this->redis->zScore('key', 'val1')); $this->assertFalse($this->redis->zScore('key', 'val')); $this->assertFalse($this->redis->zScore(3, 2)); @@ -2717,22 +2714,31 @@ public function testZX() { $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', 1, 2, ['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->assertEquals( + ['foo' => 1.0, 'bar' => 2.0, 'biz' => 3.0, 'foz' => 4.0], + $this->redis->zRangeByScore('zset', '-inf', '+inf', ['withscores' => TRUE]) + ); + $this->assertEquals( + ['foo' => 1.0, 'bar' => 2.0], + $this->redis->zRangeByScore('zset', 1, 2, ['withscores' => TRUE]) + ); + $this->assertEquals( + ['bar' => 2.0], + $this->redis->zRangeByScore('zset', '(1', 2, ['withscores' => TRUE]) + ); + $this->assertEquals([], $this->redis->zRangeByScore('zset', '(1', '(2', ['withscores' => TRUE])); - $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); - $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); - $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); - $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2')); + $this->assertEquals(4, $this->redis->zCount('zset', '-inf', '+inf')); + $this->assertEquals(2, $this->redis->zCount('zset', 1, 2)); + $this->assertEquals(1, $this->redis->zCount('zset', '(1', 2)); + $this->assertEquals(0, $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')); + $this->assertEquals(1.0, $this->redis->zIncrBy('key', 1, 'val1')); + $this->assertEquals(1.0, $this->redis->zScore('key', 'val1')); + $this->assertEquals(2.5, $this->redis->zIncrBy('key', 1.5, 'val1')); + $this->assertEquals(2.5, $this->redis->zScore('key', 'val1')); // zUnionStore $this->redis->del('{zset}1'); @@ -2749,26 +2755,26 @@ public function testZX() { $this->redis->zAdd('{zset}3', 4, 'val4'); $this->redis->zAdd('{zset}3', 5, 'val5'); - $this->assertTrue(4 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}3'])); - $this->assertTrue(['val0', 'val1', 'val4', 'val5'] === $this->redis->zRange('{zset}U', 0, -1)); + $this->assertEquals(4, $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}3'])); + $this->assertEquals(['val0', 'val1', 'val4', 'val5'], $this->redis->zRange('{zset}U', 0, -1)); // Union on non existing keys $this->redis->del('{zset}U'); - $this->assertTrue(0 === $this->redis->zUnionStore('{zset}U', ['{zset}X', '{zset}Y'])); - $this->assertTrue([] === $this->redis->zRange('{zset}U', 0, -1)); + $this->assertEquals(0, $this->redis->zUnionStore('{zset}U', ['{zset}X', '{zset}Y'])); + $this->assertEquals([],$this->redis->zRange('{zset}U', 0, -1)); // !Exist U Exist → copy of existing zset. $this->redis->del('{zset}U', 'X'); - $this->assertTrue(2 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}X'])); + $this->assertEquals(2, $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}X'])); // test weighted zUnion $this->redis->del('{zset}Z'); $this->assertEquals(4, $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [1, 1])); - $this->assertTrue(['val0', 'val1', 'val2', 'val3'] === $this->redis->zRange('{zset}Z', 0, -1)); + $this->assertEquals(['val0', 'val1', 'val2', 'val3'], $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->zRemRangeByScore('{zset}Z', 0, 10); - $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [5, 1])); - $this->assertTrue(['val0', 'val2', 'val3', 'val1'] === $this->redis->zRange('{zset}Z', 0, -1)); + $this->assertEquals(4, $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [5, 1])); + $this->assertEquals(['val0', 'val2', 'val3', 'val1'], $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); @@ -2778,12 +2784,12 @@ public function testZX() { $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->assertEquals(1.0, $this->redis->zScore('{zset}U', 'duplicate')); $this->redis->del('{zset}U'); //now test zUnion *without* weights but with aggregate function $this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], null, 'MIN'); - $this->assertTrue($this->redis->zScore('{zset}U', 'duplicate')===1.0); + $this->assertEquals(1.0, $this->redis->zScore('{zset}U', 'duplicate')); $this->redis->del('{zset}U', '{zset}1', '{zset}2'); // test integer and float weights (GitHub issue #109). @@ -2795,7 +2801,7 @@ public function testZX() { $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'], [2, 3.0]) === 3); + $this->assertEquals(3, $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [2, 3.0])); $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); @@ -2806,20 +2812,20 @@ public function testZX() { $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); + $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, 'inf']) ); + $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '-inf'])); + $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '+inf'])); // 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); + $this->assertEquals(5, $r); $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->assertEquals(2, count($r)); + $this->assertArrayKey($r, 'one'); + $this->assertArrayKey($r, 'two'); } $this->redis->del('{zset}1','{zset}2','{zset}3'); @@ -2829,10 +2835,10 @@ public function testZX() { $this->redis->zadd('{zset}1', 4000.1, 'three'); $ret = $this->redis->zRange('{zset}1', 0, -1, TRUE); - $this->assertTrue(count($ret) === 3); + $this->assertEquals(3, count($ret)); $retValues = array_keys($ret); - $this->assertTrue(['one', 'two', 'three'] === $retValues); + $this->assertEquals(['one', 'two', 'three'], $retValues); // + 0 converts from string to float OR integer $this->assertTrue(is_float($ret['one'] + 0)); @@ -2845,7 +2851,7 @@ public function testZX() { $this->redis->zAdd('{zset}1', 1, 'one'); $this->redis->zAdd('{zset}1', 2, 'two'); $this->redis->zAdd('{zset}1', 3, 'three'); - $this->assertTrue(2 === $this->redis->zremrangebyrank('{zset}1', 0, 1)); + $this->assertEquals(2, $this->redis->zremrangebyrank('{zset}1', 0, 1)); $this->assertTrue(['three' => 3] == $this->redis->zRange('{zset}1', 0, -1, TRUE)); $this->redis->del('{zset}1'); @@ -2863,16 +2869,16 @@ public function testZX() { $this->redis->zAdd('{zset}3', 5, 'val5'); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'])); - $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'])); + $this->assertEquals(['val1', 'val3'], $this->redis->zRange('{zset}I', 0, -1)); // Union on non existing keys - $this->assertTrue(0 === $this->redis->zInterStore('{zset}X', ['{zset}X', '{zset}Y'])); - $this->assertTrue([] === $this->redis->zRange('{zset}X', 0, -1)); + $this->assertEquals(0, $this->redis->zInterStore('{zset}X', ['{zset}X', '{zset}Y'])); + $this->assertEquals([], $this->redis->zRange('{zset}X', 0, -1)); // !Exist U Exist - $this->assertTrue(0 === $this->redis->zInterStore('{zset}Y', ['{zset}1', '{zset}X'])); - $this->assertTrue([] === $this->redis->zRange('keyY', 0, -1)); + $this->assertEquals(0, $this->redis->zInterStore('{zset}Y', ['{zset}1', '{zset}X'])); + $this->assertEquals([], $this->redis->zRange('keyY', 0, -1)); // test weighted zInterStore @@ -2892,19 +2898,19 @@ public function testZX() { $this->redis->zAdd('{zset}3', 3, 'val3'); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'], [1, 1])); - $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'], [1, 1])); + $this->assertEquals(['val1', 'val3'], $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'min')); - $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'min')); + $this->assertEquals(['val1', 'val3'], $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'max')); - $this->assertTrue(['val3', 'val1'] === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'max')); + $this->assertEquals(['val3', 'val1'], $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], null, 'max')); - $this->assertTrue($this->redis->zScore('{zset}I', 'val1') === floatval(7)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], null, 'max')); + $this->assertEquals(floatval(7), $this->redis->zScore('{zset}I', 'val1')); // zrank, zrevrank $this->redis->del('z'); @@ -2912,13 +2918,13 @@ public function testZX() { $this->redis->zadd('z', 2, 'two'); $this->redis->zadd('z', 5, 'five'); - $this->assertTrue(0 === $this->redis->zRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRank('z', 'two')); - $this->assertTrue(2 === $this->redis->zRank('z', 'five')); + $this->assertEquals(0, $this->redis->zRank('z', 'one')); + $this->assertEquals(1, $this->redis->zRank('z', 'two')); + $this->assertEquals(2, $this->redis->zRank('z', 'five')); - $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); - $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); + $this->assertEquals(2, $this->redis->zRevRank('z', 'one')); + $this->assertEquals(1, $this->redis->zRevRank('z', 'two')); + $this->assertEquals(0, $this->redis->zRevRank('z', 'five')); } public function testZRangeScoreArg() { @@ -2936,7 +2942,7 @@ public function testZRangeScoreArg() { public function testZRangeByLex() { /* ZRANGEBYLEX available on versions >= 2.8.9 */ - if(version_compare($this->version, "2.8.9") < 0) { + if(version_compare($this->version, '2.8.9') < 0) { $this->MarkTestSkipped(); return; } @@ -2955,7 +2961,7 @@ public function testZRangeByLex() { $this->assertEquals(['b'], $this->redis->zRangeByLex('key', '-', '(c', 1, 2)); /* Test getting the same functionality via ZRANGE and options */ - if ($this->minVersionCheck("6.2.0")) { + if ($this->minVersionCheck('6.2.0')) { $this->assertEquals(['a','b','c'], $this->redis->zRange('key', '-', '[c', ['BYLEX'])); $this->assertEquals(['b', 'c'], $this->redis->zRange('key', '-', '[c', ['BYLEX', 'LIMIT' => [1, 2]])); $this->assertEquals(['b'], $this->redis->zRange('key', '-', '(c', ['BYLEX', 'LIMIT' => [1, 2]])); @@ -2965,7 +2971,7 @@ public function testZRangeByLex() { } public function testZLexCount() { - if (version_compare($this->version, "2.8.9") < 0) { + if (version_compare($this->version, '2.8.9') < 0) { $this->MarkTestSkipped(); return; } @@ -2977,8 +2983,8 @@ public function testZLexCount() { } /* Special -/+ values */ - $this->assertEquals($this->redis->zLexCount('key', '-', '-'), 0); - $this->assertEquals($this->redis->zLexCount('key', '-', '+'), count($entries)); + $this->assertEquals(0, $this->redis->zLexCount('key', '-', '-')); + $this->assertEquals(count($entries), $this->redis->zLexCount('key', '-', '+')); /* Verify invalid arguments return FALSE */ $this->assertFalse(@$this->redis->zLexCount('key', '[a', 'bad')); @@ -2988,9 +2994,9 @@ public function testZLexCount() { $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); + $this->assertEquals($i + 1, $this->redis->zLexCount('key', "[$start", "[$end")); + $this->assertEquals($i, $this->redis->zLexCount('key', "[$start", "($end")); + $this->assertEquals($i - 1, $this->redis->zLexCount('key', "($start", "($end")); } } @@ -3082,26 +3088,26 @@ public function testzMscore() } public function testZRemRangeByLex() { - if (version_compare($this->version, "2.8.9") < 0) { + 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->assertEquals(3, $this->redis->zRemRangeByLex('key', '-', '+')); $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c'); - $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 3); + $this->assertEquals(3, $this->redis->zRemRangeByLex('key', '[a', '[c')); $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); + $this->assertEquals(0, $this->redis->zRemRangeByLex('key', '[a', '(a')); + $this->assertEquals(1, $this->redis->zRemRangeByLex('key', '(a', '(c')); + $this->assertEquals(2, $this->redis->zRemRangeByLex('key', '[a', '[c')); } public function testBZPop() { - if (version_compare($this->version, "5.0.0") < 0) { + if (version_compare($this->version, '5.0.0') < 0) { $this->MarkTestSkipped(); return; } @@ -3110,9 +3116,9 @@ public function testBZPop() { $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)); + $this->assertEquals(['{zs}1', 'a', '0'], $this->redis->bzPopMin('{zs}1', '{zs}2', 0)); + $this->assertEquals(['{zs}1', 'c', '2'], $this->redis->bzPopMax(['{zs}1', '{zs}2'], 0)); + $this->assertEquals(['{zs}2', 'A', '3'], $this->redis->bzPopMin('{zs}2', '{zs}1', 0)); /* Verify timeout is being sent */ $this->redis->del('{zs}1', '{zs}2'); @@ -3123,7 +3129,7 @@ public function testBZPop() { } public function testZPop() { - if (version_compare($this->version, "5.0.0") < 0) { + if (version_compare($this->version, '5.0.0') < 0) { $this->MarkTestSkipped(); return; } @@ -3131,23 +3137,23 @@ public function testZPop() { // 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')); + $this->assertEquals(['e' => 4.0], $this->redis->zPopMax('key')); + $this->assertEquals(['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)); + $this->assertEquals(['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)); + $this->assertEquals(['a' => 0.0, 'b' => 1.0, 'c' => 2.0], $this->redis->zPopMin('key', 3)); } public function testZRandMember() { - if (version_compare($this->version, "6.2.0") < 0) { + if (version_compare($this->version, '6.2.0') < 0) { $this->MarkTestSkipped(); return; } @@ -3166,140 +3172,143 @@ public function testZRandMember() public function testHashes() { $this->redis->del('h', 'key'); - $this->assertTrue(0 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); - $this->assertTrue(1 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); - $this->assertTrue(2 === $this->redis->hLen('h')); + $this->assertEquals(0, $this->redis->hLen('h')); + $this->assertEquals(1, $this->redis->hSet('h', 'a', 'a-value')); + $this->assertEquals(1, $this->redis->hLen('h')); + $this->assertEquals(1, $this->redis->hSet('h', 'b', 'b-value')); + $this->assertEquals(2, $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->assertEquals('a-value', $this->redis->hGet('h', 'a')); // simple get + $this->assertEquals('b-value', $this->redis->hGet('h', 'b')); // simple get - $this->assertTrue(0 === $this->redis->hSet('h', 'a', 'another-value')); // replacement - $this->assertTrue('another-value' === $this->redis->hGet('h', 'a')); // get the new value + $this->assertEquals(0, $this->redis->hSet('h', 'a', 'another-value')); // replacement + $this->assertEquals('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 + $this->assertEquals('b-value', $this->redis->hGet('h', 'b')); // simple get + $this->assertFalse($this->redis->hGet('h', 'c')); // unknown hash member + $this->assertFalse($this->redis->hGet('key', 'c')); // unknownkey // hDel - $this->assertTrue(1 === $this->redis->hDel('h', 'a')); // 1 on success - $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure + $this->assertEquals(1, $this->redis->hDel('h', 'a')); // 1 on success + $this->assertEquals(0, $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(2 === $this->redis->hDel('h', 'x', 'y')); // variadic + $this->assertEquals(2, $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')); + $this->assertTrue($this->redis->hSetNx('h', 'x', 'a')); + $this->assertTrue($this->redis->hSetNx('h', 'y', 'b')); + $this->assertFalse($this->redis->hSetNx('h', 'x', '?')); + $this->assertFalse($this->redis->hSetNx('h', 'y', '?')); + $this->assertEquals('a', $this->redis->hGet('h', 'x')); + $this->assertEquals('b', $this->redis->hGet('h', 'y')); // keys $keys = $this->redis->hKeys('h'); - $this->assertTrue($keys === ['x', 'y'] || $keys === ['y', 'x']); + $this->assertEqualsCanonicalizing(['x', 'y'], $keys); // values $values = $this->redis->hVals('h'); - $this->assertTrue($values === ['a', 'b'] || $values === ['b', 'a']); + $this->assertEqualsCanonicalizing(['a', 'b'], $values); // keys + values $all = $this->redis->hGetAll('h'); - $this->assertTrue($all === ['x' => 'a', 'y' => 'b'] || $all === ['y' => 'b', 'x' => 'a']); + $this->assertEqualsCanonicalizing(['x' => 'a', 'y' => 'b'], $all, true); // 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->assertTrue($this->redis->hExists('h', 'x')); + $this->assertTrue($this->redis->hExists('h', 'y')); + $this->assertFalse($this->redis->hExists('h', 'w')); $this->redis->del('h'); - $this->assertTrue(FALSE === $this->redis->hExists('h', 'x')); + $this->assertFalse($this->redis->hExists('h', 'x')); // hIncrBy $this->redis->del('h'); - $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); - $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); - $this->assertTrue(2 === $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->assertEquals("".PHP_INT_MAX, $this->redis->hGet('h', 'x')); + $this->assertEquals(2, $this->redis->hIncrBy('h', 'x', 2)); + $this->assertEquals(3, $this->redis->hIncrBy('h', 'x', 1)); + $this->assertEquals(2, $this->redis->hIncrBy('h', 'x', -1)); + $this->assertEquals('2', $this->redis->hGet('h', 'x')); + $this->assertEquals(PHP_INT_MAX, $this->redis->hIncrBy('h', 'x', PHP_INT_MAX-2)); + $this->assertEquals(''.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)); + $this->assertFalse($this->redis->hIncrBy('h', 'y', 1)); - if (version_compare($this->version, "2.5.0") >= 0) { + 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->assertEquals(1.5, $this->redis->hIncrByFloat('h','x', 1.5)); + $this->assertEquals(3.0, $this->redis->hincrByFloat('h','x', 1.5)); + $this->assertEquals(1.5, $this->redis->hincrByFloat('h','x', -1.5)); + $this->assertEquals(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)); + $this->assertFalse($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')); + $this->assertTrue($this->redis->hMset('h', ['x' => 123, 'y' => 456, 'z' => 'abc'])); + $this->assertEquals('123', $this->redis->hGet('h', 'x')); + $this->assertEquals('456', $this->redis->hGet('h', 'y')); + $this->assertEquals('abc', $this->redis->hGet('h', 'z')); + $this->assertFalse($this->redis->hGet('h', 't')); // hmget $this->assertEquals(['x' => '123', 'y' => '456'], $this->redis->hMget('h', ['x', 'y'])); $this->assertEquals(['z' => 'abc'], $this->redis->hMget('h', ['z'])); $this->assertEquals(['x' => '123', 't' => FALSE, 'y' => '456'], $this->redis->hMget('h', ['x', 't', 'y'])); $this->assertEquals(['x' => '123', 't' => FALSE, 'y' => '456'], $this->redis->hMget('h', ['x', 't', 'y'])); - $this->assertFalse([123 => 'x'] === $this->redis->hMget('h', [123])); + $this->assertNotEquals([123 => 'x'], $this->redis->hMget('h', [123])); $this->assertEquals([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); + $this->assertFalse($this->redis->hmget('h', [false,NULL,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])); + $this->assertEquals( + ['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'])); + $this->assertTrue($this->redis->hMset('h', [123 => 'x', 'y' => 456])); + $this->assertEquals('x', $this->redis->hGet('h', 123)); + $this->assertEquals('x', $this->redis->hGet('h', '123')); + $this->assertEquals('456', $this->redis->hGet('h', 'y')); + $this->assertEquals([123 => 'x', 'y' => '456'], $this->redis->hMget('h', ['123', 'y'])); // references $keys = [123, 'y']; foreach ($keys as &$key) {} - $this->assertEquals($this->redis->hMget('h', $keys), [123 => 'x', 'y' => '456']); + $this->assertEquals([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])); + $this->assertTrue($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']); + $this->assertEquals('0', $h1['x']); + $this->assertEquals('Array', $h1['y']); + $this->assertEquals('Object', $h1['z']); + $this->assertEquals('', $h1['t']); // hstrlen if (version_compare($this->version, '3.2.0') >= 0) { $this->redis->del('h'); - $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // key doesn't exist + $this->assertEquals(0, $this->redis->hStrLen('h', 'x')); // key doesn't exist $this->redis->hSet('h', 'foo', 'bar'); - $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // field is not present in the hash - $this->assertTrue(3 === $this->redis->hStrLen('h', 'foo')); + $this->assertEquals(0, $this->redis->hStrLen('h', 'x')); // field is not present in the hash + $this->assertEquals(3, $this->redis->hStrLen('h', 'foo')); } } public function testHRandField() { - if (version_compare($this->version, "6.2.0") < 0) { + if (version_compare($this->version, '6.2.0') < 0) { $this->MarkTestSkipped(); return; } @@ -3321,37 +3330,37 @@ 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->assertEquals('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->assertEquals('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')); + // $this->assertEquals(11, $this->redis->setRange('key', -6, 'redis')); // works with negative offsets too! (disabled because not all versions support this) + // $this->assertEquals('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')); + $this->assertEquals("\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"; + * 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"; + $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->assertFalse($this->redis->object('encoding', 'key')); + $this->assertFalse($this->redis->object('refcount', 'key')); + $this->assertFalse($this->redis->object('idletime', 'key')); $this->redis->set('key', 'value'); - $this->assertTrue($this->redis->object('encoding', 'key') === $str_small_encoding); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + $this->assertEquals($str_small_encoding, $this->redis->object('encoding', 'key')); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); @@ -3360,9 +3369,9 @@ public function testObject() { /* Redis has improved the encoding here throughout the various versions. The value can either be 'ziplist', 'quicklist', or 'listpack' */ $encoding = $this->redis->object('encoding', 'key'); - $this->assertTrue(in_array($encoding, ['ziplist', 'quicklist', 'listpack'])); + $this->assertInArray($encoding, ['ziplist', 'quicklist', 'listpack']); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); @@ -3370,24 +3379,24 @@ public function testObject() { /* Redis 7.2.0 switched to 'listpack' for small sets */ $encoding = $this->redis->object('encoding', 'key'); - $this->assertTrue($encoding == 'hashtable' || $encoding == 'listpack'); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + $this->assertInArray($encoding, ['hashtable', 'listpack']); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $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->assertEquals('intset', $this->redis->object('encoding', 'key')); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $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"); + $encoding = $this->redis->object('encoding', 'key'); + $this->assertInArray($encoding, ['linkedlist', 'quicklist']); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); } @@ -3396,18 +3405,18 @@ public function testMultiExec() { $this->differentType(Redis::MULTI); // with prefix as well - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->sequence(Redis::MULTI); $this->differentType(Redis::MULTI); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); $this->redis->set('x', '42'); - $this->assertTrue(TRUE === $this->redis->watch('x')); + $this->assertTrue($this->redis->watch('x')); $ret = $this->redis->multi()->get('x')->exec(); // successful transaction - $this->assertTrue($ret === ['42']); + $this->assertEquals(['42'], $ret); } public function testFailedTransactions() { @@ -3420,7 +3429,7 @@ public function testFailedTransactions() { $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. + $this->assertFalse($ret); // failed because another client changed our watched key between WATCH and EXEC. // watch and unwatch $this->redis->watch('x'); @@ -3429,7 +3438,8 @@ public function testFailedTransactions() { $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === ['44']); // succeeded since we've cancel the WATCH command. + // succeeded since we've cancel the WATCH command. + $this->assertEquals(['44'], $ret); } public function testPipeline() { @@ -3441,21 +3451,19 @@ public function testPipeline() { $this->differentType(Redis::PIPELINE); // with prefix as well - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->sequence(Redis::PIPELINE); $this->differentType(Redis::PIPELINE); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } - public function testPipelineMultiExec() - { -return; + public function testPipelineMultiExec() { if (!$this->havePipeline()) { $this->markTestSkipped(); } $ret = $this->redis->pipeline()->multi()->exec()->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertEquals(1, count($ret)); // empty transaction $ret = $this->redis->pipeline() @@ -3465,7 +3473,7 @@ public function testPipelineMultiExec() ->multi()->get('x')->del('x')->exec() ->ping() ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertEquals(5, count($ret)); // should be 5 atomic operations } @@ -3518,11 +3526,11 @@ protected function sequence($mode) { ->get('x') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $i = 0; - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] === Redis::REDIS_STRING); - $this->assertTrue($ret[$i] === '42' || $ret[$i] === 42); + $this->assertTrue($ret[$i++]); + $this->assertEquals(Redis::REDIS_STRING, $ret[$i++]); + $this->assertEqualsWeak('42', $ret[$i]); $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer @@ -3548,26 +3556,26 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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->assertEqualsWeak(TRUE, $ret[$i++]); + $this->assertEqualsWeak('value1', $ret[$i++]); + $this->assertEqualsWeak('value1', $ret[$i++]); + $this->assertEqualsWeak('value2', $ret[$i++]); + $this->assertEqualsWeak(TRUE, $ret[$i++]); + $this->assertEqualsWeak(5, $ret[$i++]); + $this->assertEqualsWeak(5, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEqualsWeak(TRUE, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEqualsWeak(FALSE, $ret[$i++]); + $this->assertEqualsWeak(TRUE, $ret[$i++]); + $this->assertEqualsWeak(TRUE, $ret[$i++]); + $this->assertEqualsWeak(9, $ret[$i++]); + $this->assertEqualsWeak(TRUE, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEquals($i, count($ret)); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); @@ -3581,14 +3589,14 @@ protected function sequence($mode) { ->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); + $this->assertIsArray($ret); + $this->assertEqualsWeak(true, $ret[0]); + $this->assertEqualsWeak(true, $ret[1]); + $this->assertEqualsWeak(true, $ret[2]); + $this->assertEqualsWeak(false, $ret[3]); + $this->assertEqualsWeak(true, $ret[4]); + $this->assertEqualsWeak(true, $ret[5]); + $this->assertEqualsWeak(false, $ret[6]); // ttl, mget, mset, msetnx, expire, expireAt $this->redis->del('key'); @@ -3602,17 +3610,17 @@ protected function sequence($mode) { ->expireAt('key', '0000') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $i = 0; $ttl = $ret[$i++]; - $this->assertTrue($ttl === -1 || $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); + $this->assertBetween($ttl, -2, -1); + $this->assertEquals(['val1', 'valX', false], $ret[$i++]); // mget + $this->assertTrue($ret[$i++]); // mset + $this->assertTrue($ret[$i++]); // set + $this->assertTrue($ret[$i++]); // expire + $this->assertEquals(5, $ret[$i++]); // ttl + $this->assertTrue($ret[$i++]); // expireAt + $this->assertEquals($i, count($ret)); $ret = $this->redis->multi($mode) ->set('{list}lkey', 'x') @@ -3632,34 +3640,34 @@ protected function sequence($mode) { ->llen('{list}lkey') ->lIndex('{list}lkey', 0) ->lrange('{list}lkey', 0, -1) - ->lSet('{list}lkey', 1, "newValue") // check errors on key not exists + ->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)); + $this->assertIsArray($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); + $this->assertTrue($ret[$i++]); // SET + $this->assertTrue($ret[$i++]); // SET + $this->assertEquals(2, $ret[$i++]); // deleting 2 keys + $this->assertEquals(1, $ret[$i++]); // rpush, now 1 element + $this->assertEquals(2, $ret[$i++]); // lpush, now 2 elements + $this->assertEquals(3, $ret[$i++]); // lpush, now 3 elements + $this->assertEquals(4, $ret[$i++]); // lpush, now 4 elements + $this->assertEquals(5, $ret[$i++]); // lpush, now 5 elements + $this->assertEquals(6, $ret[$i++]); // lpush, now 6 elements + $this->assertEquals('lvalue', $ret[$i++]); // rpoplpush returns the element: 'lvalue' + $this->assertEquals(['lvalue'], $ret[$i++]); // lDest contains only that one element. + $this->assertEquals('lvalue', $ret[$i++]); // removing a second element from lkey, now 4 elements left ↓ + $this->assertEquals(4, $ret[$i++]); // 4 elements left, after 2 pops. + $this->assertEquals(3, $ret[$i++]); // removing 3 elements, now 1 left. + $this->assertEquals(1, $ret[$i++]); // 1 element left + $this->assertEquals('lvalue', $ret[$i++]); // this is the current head. + $this->assertEquals(['lvalue'], $ret[$i++]); // this is the current list. + $this->assertFalse($ret[$i++]); // updating a non-existent element fails. + $this->assertTrue(['lvalue'], $ret[$i++]); // this is the current list. + $this->assertEquals(1, $ret[$i++]); // 1 element left + $this->assertEquals($i, count($ret)); $ret = $this->redis->multi($mode) ->del('{list}lkey', '{list}lDest') @@ -3670,16 +3678,16 @@ protected function sequence($mode) { ->lrange('{list}lDest', 0, -1) ->lpop('{list}lkey') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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); + $this->assertEquals(1, $ret[$i++]); // 1 element in the list + $this->assertEquals(2, $ret[$i++]); // 2 elements in the list + $this->assertEquals(3, $ret[$i++]); // 3 elements in the list + $this->assertEquals('lvalue', $ret[$i++]); // rpoplpush returns the element: 'lvalue' + $this->assertEquals(['lvalue'], $ret[$i++]); // rpoplpush returns the element: 'lvalue' + $this->assertEquals('lvalue', $ret[$i++]); // pop returns the front element: 'lvalue' + $this->assertEquals($i, count($ret)); $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); @@ -3707,25 +3715,25 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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->assertEqualsWeak(true, $ret[$i++]); + $this->assertEquals('value1', $ret[$i++]); + $this->assertEquals('value1', $ret[$i++]); + $this->assertEquals('value2', $ret[$i++]); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEqualsWeak(5, $ret[$i++]); + $this->assertEqualsWeak(5, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertTrue($ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertFalse($ret[$i++]); + $this->assertTrue($ret[$i++]); + $this->assertTrue($ret[$i++]); + $this->assertEqualsWeak(9, $ret[$i++]); + $this->assertTrue($ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); $this->assertTrue($ret[$i++]); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); @@ -3740,15 +3748,15 @@ protected function sequence($mode) { ->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); + $this->assertIsArray($ret); + $this->assertTrue($ret[0]); + $this->assertTrue($ret[1]); + $this->assertTrue($ret[2]); + $this->assertTrue($ret[3]); + $this->assertFalse($ret[4]); + $this->assertTrue($ret[5]); + $this->assertTrue($ret[6]); + $this->assertFalse($ret[7]); // ttl, mget, mset, msetnx, expire, expireAt $ret = $this->redis->multi($mode) @@ -3761,16 +3769,16 @@ protected function sequence($mode) { ->expireAt('key', '0000') ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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); + $this->assertTrue($ret[$i++]); // mset always returns TRUE + $this->assertTrue($ret[$i++]); // set always returns TRUE + $this->assertTrue($ret[$i++]); // expire always returns TRUE + $this->assertEquals(5, $ret[$i++]); // TTL was just set. + $this->assertTrue($ret[$i++]); // expireAt returns TRUE for an existing key + $this->assertEquals($i, count($ret)); // lists $ret = $this->redis->multi($mode) @@ -3789,33 +3797,33 @@ protected function sequence($mode) { ->llen('{l}key') ->lIndex('{l}key', 0) ->lrange('{l}key', 0, -1) - ->lSet('{l}key', 1, "newValue") // check errors on missing key + ->lSet('{l}key', 1, 'newValue') // check errors on missing key ->lrange('{l}key', 0, -1) ->llen('{l}key') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $i = 0; $this->assertTrue($ret[$i] >= 0 && $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); + $this->assertEquals(1, $ret[$i++]); // 1 value + $this->assertEquals(2, $ret[$i++]); // 2 values + $this->assertEquals(3, $ret[$i++]); // 3 values + $this->assertEquals(4, $ret[$i++]); // 4 values + $this->assertEquals(5, $ret[$i++]); // 5 values + $this->assertEquals(6, $ret[$i++]); // 6 values + $this->assertEquals('lvalue', $ret[$i++]); + $this->assertEquals(['lvalue'], $ret[$i++]); // 1 value only in lDest + $this->assertEquals('lvalue', $ret[$i++]); // now 4 values left + $this->assertEquals(4, $ret[$i++]); + $this->assertEquals(3, $ret[$i++]); // removing 3 elements. + $this->assertEquals(1, $ret[$i++]); // length is now 1 + $this->assertEquals('lvalue', $ret[$i++]); // this is the head + $this->assertEquals(['lvalue'], $ret[$i++]); // 1 value only in lkey + $this->assertFalse($ret[$i++]); // can't set list[1] if we only have a single value in it. + $this->assertEquals(['lvalue'], $ret[$i++]); // the previous error didn't touch anything. + $this->assertEquals(1, $ret[$i++]); // the previous error didn't change the length + $this->assertEquals($i, count($ret)); // sets @@ -3849,52 +3857,52 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 1 element. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 2 elements. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 3 elements. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 4 elements. + $this->assertEquals(1, $ret[$i++]); // skey2 now has 1 element. + $this->assertEquals(1, $ret[$i++]); // skey2 now has 2 elements. + $this->assertEquals(4, $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); // we did remove that value. + $this->assertEquals(3, $ret[$i++]); // now 3 values only. + + $this->assertTrue($ret[$i++]); // the move did succeed. + $this->assertEquals(3, $ret[$i++]); // sKey2 now has 3 values. + $this->assertTrue($ret[$i++]); // sKey2 does contain sValue4. foreach(['sValue1', 'sValue3'] as $k) { // sKey1 contains sValue1 and sValue3. - $this->assertTrue(in_array($k, $ret[$i])); + $this->assertInArray($k, $ret[$i]); } - $this->assertTrue(count($ret[$i++]) === 2); + $this->assertEquals(2, count($ret[$i++])); foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // sKey2 contains sValue1, sValue2, and sValue4. - $this->assertTrue(in_array($k, $ret[$i])); + $this->assertInArray($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 + $this->assertEquals(3, count($ret[$i++])); + $this->assertEquals(['sValue1'], $ret[$i++]); // intersection + $this->assertEquals(1, $ret[$i++]); // intersection + store → 1 value in the destination set. + $this->assertEquals(['sValue1'], $ret[$i++]); // 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->assertInArray($k, $ret[$i]); } - $this->assertTrue(count($ret[$i++]) === 3); // union size + $this->assertEquals(3, count($ret[$i++])); // union size - $this->assertTrue($ret[$i++] === 3); // unionstore size + $this->assertEquals(3, $ret[$i++]); // unionstore size foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4. - $this->assertTrue(in_array($k, $ret[$i])); + $this->assertInArray($k, $ret[$i]); } - $this->assertTrue(count($ret[$i++]) === 3); // skeyUnion size + $this->assertEquals(3, count($ret[$i++])); // 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->assertEquals(['sValue3'], $ret[$i++]); // diff skey1, skey2 : only sValue3 is not shared. + $this->assertEquals(1, $ret[$i++]); // sdiffstore size == 1 + $this->assertEquals(['sValue3'], $ret[$i++]); // 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->assertInArray($ret[$i++], ['sValue1', 'sValue2', 'sValue4']); // we removed an element from sKey2 + $this->assertEquals(2, $ret[$i++]); // sKey2 now has 2 elements only. - $this->assertTrue(count($ret) === $i); + $this->assertEquals($i, count($ret)); // sorted sets $ret = $this->redis->multi($mode) @@ -3931,39 +3939,39 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $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); + $this->assertEquals(1, $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); + $this->assertEquals(['zValue1', 'zValue2', 'zValue5'], $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); + $this->assertEquals(['zValue1', 'zValue5'], $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); // adding zValue11 + $this->assertEquals(1, $ret[$i++]); // adding zValue12 + $this->assertEquals(1, $ret[$i++]); // adding zValue13 + $this->assertEquals(1, $ret[$i++]); // adding zValue14 + $this->assertEquals(1, $ret[$i++]); // adding zValue15 + $this->assertEquals(3, $ret[$i++]); // deleted zValue11, zValue12, zValue13 + $this->assertEquals(['zValue1', 'zValue5', 'zValue14', 'zValue15'], $ret[$i++]); + $this->assertEquals(['zValue15', 'zValue14', 'zValue5', 'zValue1'], $ret[$i++]); + $this->assertEquals(['zValue1', 'zValue5'], $ret[$i++]); + $this->assertEquals(4, $ret[$i++]); // 4 elements + $this->assertEquals(15.0, $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); // added value + $this->assertEquals(1, $ret[$i++]); // added value + $this->assertEquals(1, $ret[$i++]); // zinter only has 1 value + $this->assertEquals(['zValue1', 'zValue5', 'zValue14', 'zValue15'], $ret[$i++]); // {z}key1 contents + $this->assertEquals(['zValue2', 'zValue5'], $ret[$i++]); // {z}key2 contents + $this->assertEquals(['zValue5'], $ret[$i++]); // {z}inter contents + $this->assertEquals(5, $ret[$i++]); // {z}Union has 5 values (1,2,5,14,15) + $this->assertEquals(['zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15'], $ret[$i++]); // {z}Union contents + $this->assertEquals(1, $ret[$i++]); // added value to {z}key5, with score 5 + $this->assertEquals(8.0, $ret[$i++]); // incremented score by 3 → it is now 8. + $this->assertEquals(8.0, $ret[$i++]); // current score is 8. + $this->assertFalse($ret[$i++]); // score for unknown element. + + $this->assertEquals($i, count($ret)); // hash $ret = $this->redis->multi($mode) @@ -3986,24 +3994,24 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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); + $this->assertEquals(1, $ret[$i++]); // added 1 element + $this->assertEquals(1, $ret[$i++]); // added 1 element + $this->assertEquals(1, $ret[$i++]); // added 1 element + $this->assertEquals(['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3'], $ret[$i++]); // hmget, 3 elements + $this->assertEquals('value1', $ret[$i++]); // hget + $this->assertEquals(3, $ret[$i++]); // hlen + $this->assertEquals(1, $ret[$i++]); // hdel succeeded + $this->assertEquals(0, $ret[$i++]); // hdel failed + $this->assertFalse($ret[$i++]); // hexists didn't find the deleted key + $this->assertTrue(['key3', 'key1'], $ret[$i], ['key1', 'key3'] || $ret[$i]); $i++; // hkeys + $this->assertTrue(['value3', 'value1'], $ret[$i], ['value1', 'value3'] || $ret[$i]); $i++; // hvals + $this->assertTrue(['key3' => 'value3', 'key1' => 'value1'], $ret[$i], ['key1' => 'value1', 'key3' => 'value3'] || $ret[$i]); $i++; // hgetall + $this->assertEquals(1, $ret[$i++]); // added 1 element + $this->assertEquals(1, $ret[$i++]); // added the element, so 1. + $this->assertEquals('non-string', $ret[$i++]); // hset succeeded + $this->assertEquals($i, count($ret)); $ret = $this->redis->multi($mode) // default to MULTI, not PIPELINE. ->del('test') @@ -4011,11 +4019,11 @@ protected function sequence($mode) { ->get('test') ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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); + $this->assertTrue($ret[$i++]); // added 1 element + $this->assertEquals('xyz', $ret[$i++]); + $this->assertEquals($i, count($ret)); // GitHub issue 78 $this->redis->del('test'); @@ -4023,13 +4031,13 @@ protected function sequence($mode) { $this->redis->zadd('test', $i, (string)$i); $result = $this->redis->multi($mode) - ->zscore('test', "1") - ->zscore('test', "6") - ->zscore('test', "8") - ->zscore('test', "2") + ->zscore('test', '1') + ->zscore('test', '6') + ->zscore('test', '8') + ->zscore('test', '2') ->exec(); - $this->assertTrue($result === [1.0, FALSE, FALSE, 2.0]); + $this->assertEquals([1.0, FALSE, FALSE, 2.0], $result); } protected function differentType($mode) { @@ -4050,7 +4058,7 @@ protected function differentType($mode) { ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) - ->lSet($key, 0, "newValue") + ->lSet($key, 0, 'newValue') ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) @@ -4102,60 +4110,60 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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->assertTrue($ret[$i++]); // set + + $this->assertFalse($ret[$i++]); // rpush + $this->assertFalse($ret[$i++]); // lpush + $this->assertFalse($ret[$i++]); // llen + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // lrange + $this->assertFalse($ret[$i++]); // ltrim + $this->assertFalse($ret[$i++]); // lindex + $this->assertFalse($ret[$i++]); // lset + $this->assertFalse($ret[$i++]); // lremove + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // rpop + $this->assertFalse($ret[$i++]); // rpoplush + + $this->assertFalse($ret[$i++]); // sadd + $this->assertFalse($ret[$i++]); // sremove + $this->assertFalse($ret[$i++]); // spop + $this->assertFalse($ret[$i++]); // smove + $this->assertFalse($ret[$i++]); // scard + $this->assertFalse($ret[$i++]); // sismember + $this->assertFalse($ret[$i++]); // sinter + $this->assertFalse($ret[$i++]); // sunion + $this->assertFalse($ret[$i++]); // sdiff + $this->assertFalse($ret[$i++]); // smembers + $this->assertFalse($ret[$i++]); // srandmember + + $this->assertFalse($ret[$i++]); // zadd + $this->assertFalse($ret[$i++]); // zrem + $this->assertFalse($ret[$i++]); // zincrby + $this->assertFalse($ret[$i++]); // zrank + $this->assertFalse($ret[$i++]); // zrevrank + $this->assertFalse($ret[$i++]); // zrange + $this->assertFalse($ret[$i++]); // zreverserange + $this->assertFalse($ret[$i++]); // zrangebyscore + $this->assertFalse($ret[$i++]); // zcount + $this->assertFalse($ret[$i++]); // zcard + $this->assertFalse($ret[$i++]); // zscore + $this->assertFalse($ret[$i++]); // zremrangebyrank + $this->assertFalse($ret[$i++]); // zremrangebyscore + + $this->assertFalse($ret[$i++]); // hset + $this->assertFalse($ret[$i++]); // hget + $this->assertFalse($ret[$i++]); // hmget + $this->assertFalse($ret[$i++]); // hmset + $this->assertFalse($ret[$i++]); // hincrby + $this->assertFalse($ret[$i++]); // hexists + $this->assertFalse($ret[$i++]); // hdel + $this->assertFalse($ret[$i++]); // hlen + $this->assertFalse($ret[$i++]); // hkeys + $this->assertFalse($ret[$i++]); // hvals + $this->assertFalse($ret[$i++]); // hgetall $this->assertEquals($i, count($ret)); @@ -4221,58 +4229,57 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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]) === 1 && $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(1, $ret[$i++]); // lpush + + $this->assertFalse($ret[$i++]); // get + $this->assertFalse($ret[$i++]); // getset + $this->assertFalse($ret[$i++]); // append + $this->assertFalse($ret[$i++]); // getRange + $this->assertEquals([false], $ret[$i++]); // mget + $this->assertFalse($ret[$i++]); // incr + $this->assertFalse($ret[$i++]); // incrBy + $this->assertFalse($ret[$i++]); // decr + $this->assertFalse($ret[$i++]); // decrBy + + $this->assertFalse($ret[$i++]); // sadd + $this->assertFalse($ret[$i++]); // sremove + $this->assertFalse($ret[$i++]); // spop + $this->assertFalse($ret[$i++]); // smove + $this->assertFalse($ret[$i++]); // scard + $this->assertFalse($ret[$i++]); // sismember + $this->assertFalse($ret[$i++]); // sinter + $this->assertFalse($ret[$i++]); // sunion + $this->assertFalse($ret[$i++]); // sdiff + $this->assertFalse($ret[$i++]); // smembers + $this->assertFalse($ret[$i++]); // srandmember + + $this->assertFalse($ret[$i++]); // zadd + $this->assertFalse($ret[$i++]); // zrem + $this->assertFalse($ret[$i++]); // zincrby + $this->assertFalse($ret[$i++]); // zrank + $this->assertFalse($ret[$i++]); // zrevrank + $this->assertFalse($ret[$i++]); // zrange + $this->assertFalse($ret[$i++]); // zreverserange + $this->assertFalse($ret[$i++]); // zrangebyscore + $this->assertFalse($ret[$i++]); // zcount + $this->assertFalse($ret[$i++]); // zcard + $this->assertFalse($ret[$i++]); // zscore + $this->assertFalse($ret[$i++]); // zremrangebyrank + $this->assertFalse($ret[$i++]); // zremrangebyscore + + $this->assertFalse($ret[$i++]); // hset + $this->assertFalse($ret[$i++]); // hget + $this->assertFalse($ret[$i++]); // hmget + $this->assertFalse($ret[$i++]); // hmset + $this->assertFalse($ret[$i++]); // hincrby + $this->assertFalse($ret[$i++]); // hexists + $this->assertFalse($ret[$i++]); // hdel + $this->assertFalse($ret[$i++]); // hlen + $this->assertFalse($ret[$i++]); // hkeys + $this->assertFalse($ret[$i++]); // hvals + $this->assertFalse($ret[$i++]); // hgetall $this->assertEquals($i, count($ret)); @@ -4302,7 +4309,7 @@ protected function differentType($mode) { ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) - ->lSet($key, 0, "newValue") + ->lSet($key, 0, 'newValue') ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) @@ -4339,59 +4346,58 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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]) === 1 && $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(1, $ret[$i++]); // zadd + + $this->assertFalse($ret[$i++]); // get + $this->assertFalse($ret[$i++]); // getset + $this->assertFalse($ret[$i++]); // append + $this->assertFalse($ret[$i++]); // getRange + $this->assertEquals([false], $ret[$i++]); // mget + $this->assertFalse($ret[$i++]); // incr + $this->assertFalse($ret[$i++]); // incrBy + $this->assertFalse($ret[$i++]); // decr + $this->assertFalse($ret[$i++]); // decrBy + + $this->assertFalse($ret[$i++]); // rpush + $this->assertFalse($ret[$i++]); // lpush + $this->assertFalse($ret[$i++]); // llen + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // lrange + $this->assertFalse($ret[$i++]); // ltrim + $this->assertFalse($ret[$i++]); // lindex + $this->assertFalse($ret[$i++]); // lset + $this->assertFalse($ret[$i++]); // lremove + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // rpop + $this->assertFalse($ret[$i++]); // rpoplush + + $this->assertFalse($ret[$i++]); // zadd + $this->assertFalse($ret[$i++]); // zrem + $this->assertFalse($ret[$i++]); // zincrby + $this->assertFalse($ret[$i++]); // zrank + $this->assertFalse($ret[$i++]); // zrevrank + $this->assertFalse($ret[$i++]); // zrange + $this->assertFalse($ret[$i++]); // zreverserange + $this->assertFalse($ret[$i++]); // zrangebyscore + $this->assertFalse($ret[$i++]); // zcount + $this->assertFalse($ret[$i++]); // zcard + $this->assertFalse($ret[$i++]); // zscore + $this->assertFalse($ret[$i++]); // zremrangebyrank + $this->assertFalse($ret[$i++]); // zremrangebyscore + + $this->assertFalse($ret[$i++]); // hset + $this->assertFalse($ret[$i++]); // hget + $this->assertFalse($ret[$i++]); // hmget + $this->assertFalse($ret[$i++]); // hmset + $this->assertFalse($ret[$i++]); // hincrby + $this->assertFalse($ret[$i++]); // hexists + $this->assertFalse($ret[$i++]); // hdel + $this->assertFalse($ret[$i++]); // hlen + $this->assertFalse($ret[$i++]); // hkeys + $this->assertFalse($ret[$i++]); // hvals + $this->assertFalse($ret[$i++]); // hgetall $this->assertEquals($i, count($ret)); @@ -4421,7 +4427,7 @@ protected function differentType($mode) { ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) - ->lSet($key, 0, "newValue") + ->lSet($key, 0, 'newValue') ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) @@ -4456,57 +4462,56 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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]) === 1 && $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(1, $ret[$i++]); // zadd + + $this->assertFalse($ret[$i++]); // get + $this->assertFalse($ret[$i++]); // getset + $this->assertFalse($ret[$i++]); // append + $this->assertFalse($ret[$i++]); // getRange + $this->assertEquals([false], $ret[$i++]); // mget + $this->assertFalse($ret[$i++]); // incr + $this->assertFalse($ret[$i++]); // incrBy + $this->assertFalse($ret[$i++]); // decr + $this->assertFalse($ret[$i++]); // decrBy + + $this->assertFalse($ret[$i++]); // rpush + $this->assertFalse($ret[$i++]); // lpush + $this->assertFalse($ret[$i++]); // llen + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // lrange + $this->assertFalse($ret[$i++]); // ltrim + $this->assertFalse($ret[$i++]); // lindex + $this->assertFalse($ret[$i++]); // lset + $this->assertFalse($ret[$i++]); // lremove + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // rpop + $this->assertFalse($ret[$i++]); // rpoplush + + $this->assertFalse($ret[$i++]); // sadd + $this->assertFalse($ret[$i++]); // sremove + $this->assertFalse($ret[$i++]); // spop + $this->assertFalse($ret[$i++]); // smove + $this->assertFalse($ret[$i++]); // scard + $this->assertFalse($ret[$i++]); // sismember + $this->assertFalse($ret[$i++]); // sinter + $this->assertFalse($ret[$i++]); // sunion + $this->assertFalse($ret[$i++]); // sdiff + $this->assertFalse($ret[$i++]); // smembers + $this->assertFalse($ret[$i++]); // srandmember + + $this->assertFalse($ret[$i++]); // hset + $this->assertFalse($ret[$i++]); // hget + $this->assertFalse($ret[$i++]); // hmget + $this->assertFalse($ret[$i++]); // hmset + $this->assertFalse($ret[$i++]); // hincrby + $this->assertFalse($ret[$i++]); // hexists + $this->assertFalse($ret[$i++]); // hdel + $this->assertFalse($ret[$i++]); // hlen + $this->assertFalse($ret[$i++]); // hkeys + $this->assertFalse($ret[$i++]); // hvals + $this->assertFalse($ret[$i++]); // hgetall $this->assertEquals($i, count($ret)); @@ -4536,7 +4541,7 @@ protected function differentType($mode) { ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) - ->lSet($key, 0, "newValue") + ->lSet($key, 0, 'newValue') ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) @@ -4573,59 +4578,58 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($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]) === 1 && $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(1, $ret[$i++]); // hset + + $this->assertFalse($ret[$i++]); // get + $this->assertFalse($ret[$i++]); // getset + $this->assertFalse($ret[$i++]); // append + $this->assertFalse($ret[$i++]); // getRange + $this->assertEquals([false], $ret[$i++]); // mget + $this->assertFalse($ret[$i++]); // incr + $this->assertFalse($ret[$i++]); // incrBy + $this->assertFalse($ret[$i++]); // decr + $this->assertFalse($ret[$i++]); // decrBy + + $this->assertFalse($ret[$i++]); // rpush + $this->assertFalse($ret[$i++]); // lpush + $this->assertFalse($ret[$i++]); // llen + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // lrange + $this->assertFalse($ret[$i++]); // ltrim + $this->assertFalse($ret[$i++]); // lindex + $this->assertFalse($ret[$i++]); // lset + $this->assertFalse($ret[$i++]); // lremove + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // rpop + $this->assertFalse($ret[$i++]); // rpoplush + + $this->assertFalse($ret[$i++]); // sadd + $this->assertFalse($ret[$i++]); // sremove + $this->assertFalse($ret[$i++]); // spop + $this->assertFalse($ret[$i++]); // smove + $this->assertFalse($ret[$i++]); // scard + $this->assertFalse($ret[$i++]); // sismember + $this->assertFalse($ret[$i++]); // sinter + $this->assertFalse($ret[$i++]); // sunion + $this->assertFalse($ret[$i++]); // sdiff + $this->assertFalse($ret[$i++]); // smembers + $this->assertFalse($ret[$i++]); // srandmember + + $this->assertFalse($ret[$i++]); // zadd + $this->assertFalse($ret[$i++]); // zrem + $this->assertFalse($ret[$i++]); // zincrby + $this->assertFalse($ret[$i++]); // zrank + $this->assertFalse($ret[$i++]); // zrevrank + $this->assertFalse($ret[$i++]); // zrange + $this->assertFalse($ret[$i++]); // zreverserange + $this->assertFalse($ret[$i++]); // zrangebyscore + $this->assertFalse($ret[$i++]); // zcount + $this->assertFalse($ret[$i++]); // zcard + $this->assertFalse($ret[$i++]); // zscore + $this->assertFalse($ret[$i++]); // zremrangebyrank + $this->assertFalse($ret[$i++]); // zremrangebyscore $this->assertEquals($i, count($ret)); } @@ -4635,62 +4639,62 @@ public function testDifferentTypeString() { $dkey = '{hash}' . __FUNCTION__; $this->redis->del($key); - $this->assertEquals(TRUE, $this->redis->set($key, 'value')); + $this->assertTrue($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($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "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')); + $this->assertFalse($this->redis->rPush($key, 'lvalue')); + $this->assertFalse($this->redis->lPush($key, 'lvalue')); + $this->assertFalse($this->redis->lLen($key)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->lrange($key, 0, -1)); + $this->assertFalse($this->redis->lTrim($key, 0, 1)); + $this->assertFalse($this->redis->lIndex($key, 0)); + $this->assertFalse($this->redis->lSet($key, 0, 'newValue')); + $this->assertFalse($this->redis->lrem($key, 'lvalue', 1)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->rPop($key)); + $this->assertFalse($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)); + $this->assertFalse($this->redis->sAdd($key, 'sValue1')); + $this->assertFalse($this->redis->srem($key, 'sValue1')); + $this->assertFalse($this->redis->sPop($key)); + $this->assertFalse($this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); + $this->assertFalse($this->redis->scard($key)); + $this->assertFalse($this->redis->sismember($key, 'sValue1')); + $this->assertFalse($this->redis->sInter($key, $dkey. 'skey2')); + $this->assertFalse($this->redis->sUnion($key, $dkey . 'skey4')); + $this->assertFalse($this->redis->sDiff($key, $dkey . 'skey7')); + $this->assertFalse($this->redis->sMembers($key)); + $this->assertFalse($this->redis->sRandMember($key)); // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zAdd($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRem($key, 'zValue1')); + $this->assertFalse($this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRevRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRange($key, 0, -1)); + $this->assertFalse($this->redis->zRevRange($key, 0, -1)); + $this->assertFalse($this->redis->zRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zCount($key, 0, -1)); + $this->assertFalse($this->redis->zCard($key)); + $this->assertFalse($this->redis->zScore($key, 'zValue1')); + $this->assertFalse($this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertFalse($this->redis->zRemRangeByScore($key, 1, 2)); // 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)); + $this->assertFalse($this->redis->hSet($key, 'key1', 'value1')); + $this->assertFalse($this->redis->hGet($key, 'key1')); + $this->assertFalse($this->redis->hMGet($key, ['key1'])); + $this->assertFalse($this->redis->hMSet($key, ['key1' => 'value1'])); + $this->assertFalse($this->redis->hIncrBy($key, 'key2', 1)); + $this->assertFalse($this->redis->hExists($key, 'key2')); + $this->assertFalse($this->redis->hDel($key, 'key2')); + $this->assertFalse($this->redis->hLen($key)); + $this->assertFalse($this->redis->hKeys($key)); + $this->assertFalse($this->redis->hVals($key)); + $this->assertFalse($this->redis->hGetAll($key)); } public function testDifferentTypeList() { @@ -4701,56 +4705,56 @@ public function testDifferentTypeList() { $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($key, 0, 8)); + $this->assertFalse($this->redis->get($key)); + $this->assertFalse($this->redis->getset($key, 'value2')); + $this->assertFalse($this->redis->append($key, 'append')); + $this->assertFalse($this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + $this->assertFalse($this->redis->incr($key)); + $this->assertFalse($this->redis->incrBy($key, 1)); + $this->assertFalse($this->redis->decr($key)); + $this->assertFalse($this->redis->decrBy($key, 1)); // 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)); + $this->assertFalse($this->redis->sAdd($key, 'sValue1')); + $this->assertFalse($this->redis->srem($key, 'sValue1')); + $this->assertFalse($this->redis->sPop($key)); + $this->assertFalse($this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); + $this->assertFalse($this->redis->scard($key)); + $this->assertFalse($this->redis->sismember($key, 'sValue1')); + $this->assertFalse($this->redis->sInter($key, $dkey . 'skey2')); + $this->assertFalse($this->redis->sUnion($key, $dkey . 'skey4')); + $this->assertFalse($this->redis->sDiff($key, $dkey . 'skey7')); + $this->assertFalse($this->redis->sMembers($key)); + $this->assertFalse($this->redis->sRandMember($key)); // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zAdd($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRem($key, 'zValue1')); + $this->assertFalse($this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRevRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRange($key, 0, -1)); + $this->assertFalse($this->redis->zRevRange($key, 0, -1)); + $this->assertFalse($this->redis->zRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zCount($key, 0, -1)); + $this->assertFalse($this->redis->zCard($key)); + $this->assertFalse($this->redis->zScore($key, 'zValue1')); + $this->assertFalse($this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertFalse($this->redis->zRemRangeByScore($key, 1, 2)); // 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)); + $this->assertFalse($this->redis->hSet($key, 'key1', 'value1')); + $this->assertFalse($this->redis->hGet($key, 'key1')); + $this->assertFalse($this->redis->hMGet($key, ['key1'])); + $this->assertFalse($this->redis->hMSet($key, ['key1' => 'value1'])); + $this->assertFalse($this->redis->hIncrBy($key, 'key2', 1)); + $this->assertFalse($this->redis->hExists($key, 'key2')); + $this->assertFalse($this->redis->hDel($key, 'key2')); + $this->assertFalse($this->redis->hLen($key)); + $this->assertFalse($this->redis->hKeys($key)); + $this->assertFalse($this->redis->hVals($key)); + $this->assertFalse($this->redis->hGetAll($key)); } public function testDifferentTypeSet() { @@ -4760,57 +4764,57 @@ public function testDifferentTypeSet() { $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($key, 0, 8)); + $this->assertFalse($this->redis->get($key)); + $this->assertFalse($this->redis->getset($key, 'value2')); + $this->assertFalse($this->redis->append($key, 'append')); + $this->assertFalse($this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + $this->assertFalse($this->redis->incr($key)); + $this->assertFalse($this->redis->incrBy($key, 1)); + $this->assertFalse($this->redis->decr($key)); + $this->assertFalse($this->redis->decrBy($key, 1)); // 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($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "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')); + $this->assertFalse($this->redis->rPush($key, 'lvalue')); + $this->assertFalse($this->redis->lPush($key, 'lvalue')); + $this->assertFalse($this->redis->lLen($key)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->lrange($key, 0, -1)); + $this->assertFalse($this->redis->lTrim($key, 0, 1)); + $this->assertFalse($this->redis->lIndex($key, 0)); + $this->assertFalse($this->redis->lSet($key, 0, 'newValue')); + $this->assertFalse($this->redis->lrem($key, 'lvalue', 1)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->rPop($key)); + $this->assertFalse($this->redis->rPoplPush($key, $dkey . 'lkey1')); // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zAdd($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRem($key, 'zValue1')); + $this->assertFalse($this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRevRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRange($key, 0, -1)); + $this->assertFalse($this->redis->zRevRange($key, 0, -1)); + $this->assertFalse($this->redis->zRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zCount($key, 0, -1)); + $this->assertFalse($this->redis->zCard($key)); + $this->assertFalse($this->redis->zScore($key, 'zValue1')); + $this->assertFalse($this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertFalse($this->redis->zRemRangeByScore($key, 1, 2)); // 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)); + $this->assertFalse($this->redis->hSet($key, 'key1', 'value1')); + $this->assertFalse($this->redis->hGet($key, 'key1')); + $this->assertFalse($this->redis->hMGet($key, ['key1'])); + $this->assertFalse($this->redis->hMSet($key, ['key1' => 'value1'])); + $this->assertFalse($this->redis->hIncrBy($key, 'key2', 1)); + $this->assertFalse($this->redis->hExists($key, 'key2')); + $this->assertFalse($this->redis->hDel($key, 'key2')); + $this->assertFalse($this->redis->hLen($key)); + $this->assertFalse($this->redis->hKeys($key)); + $this->assertFalse($this->redis->hVals($key)); + $this->assertFalse($this->redis->hGetAll($key)); } public function testDifferentTypeSortedSet() { @@ -4821,55 +4825,55 @@ public function testDifferentTypeSortedSet() { $this->assertEquals(1, $this->redis->zAdd($key, 0, '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($key, 0, 8)); + $this->assertFalse($this->redis->get($key)); + $this->assertFalse($this->redis->getset($key, 'value2')); + $this->assertFalse($this->redis->append($key, 'append')); + $this->assertFalse($this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + $this->assertFalse($this->redis->incr($key)); + $this->assertFalse($this->redis->incrBy($key, 1)); + $this->assertFalse($this->redis->decr($key)); + $this->assertFalse($this->redis->decrBy($key, 1)); // 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($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "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')); + $this->assertFalse($this->redis->rPush($key, 'lvalue')); + $this->assertFalse($this->redis->lPush($key, 'lvalue')); + $this->assertFalse($this->redis->lLen($key)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->lrange($key, 0, -1)); + $this->assertFalse($this->redis->lTrim($key, 0, 1)); + $this->assertFalse($this->redis->lIndex($key, 0)); + $this->assertFalse($this->redis->lSet($key, 0, 'newValue')); + $this->assertFalse($this->redis->lrem($key, 'lvalue', 1)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->rPop($key)); + $this->assertFalse($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)); + $this->assertFalse($this->redis->sAdd($key, 'sValue1')); + $this->assertFalse($this->redis->srem($key, 'sValue1')); + $this->assertFalse($this->redis->sPop($key)); + $this->assertFalse($this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); + $this->assertFalse($this->redis->scard($key)); + $this->assertFalse($this->redis->sismember($key, 'sValue1')); + $this->assertFalse($this->redis->sInter($key, $dkey . 'skey2')); + $this->assertFalse($this->redis->sUnion($key, $dkey . 'skey4')); + $this->assertFalse($this->redis->sDiff($key, $dkey . 'skey7')); + $this->assertFalse($this->redis->sMembers($key)); + $this->assertFalse($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)); + $this->assertFalse($this->redis->hSet($key, 'key1', 'value1')); + $this->assertFalse($this->redis->hGet($key, 'key1')); + $this->assertFalse($this->redis->hMGet($key, ['key1'])); + $this->assertFalse($this->redis->hMSet($key, ['key1' => 'value1'])); + $this->assertFalse($this->redis->hIncrBy($key, 'key2', 1)); + $this->assertFalse($this->redis->hExists($key, 'key2')); + $this->assertFalse($this->redis->hDel($key, 'key2')); + $this->assertFalse($this->redis->hLen($key)); + $this->assertFalse($this->redis->hKeys($key)); + $this->assertFalse($this->redis->hVals($key)); + $this->assertFalse($this->redis->hGetAll($key)); } public function testDifferentTypeHash() { @@ -4880,66 +4884,66 @@ public function testDifferentTypeHash() { $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($key, 0, 8)); + $this->assertFalse($this->redis->get($key)); + $this->assertFalse($this->redis->getset($key, 'value2')); + $this->assertFalse($this->redis->append($key, 'append')); + $this->assertFalse($this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + $this->assertFalse($this->redis->incr($key)); + $this->assertFalse($this->redis->incrBy($key, 1)); + $this->assertFalse($this->redis->decr($key)); + $this->assertFalse($this->redis->decrBy($key, 1)); // 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($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "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')); + $this->assertFalse($this->redis->rPush($key, 'lvalue')); + $this->assertFalse($this->redis->lPush($key, 'lvalue')); + $this->assertFalse($this->redis->lLen($key)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->lrange($key, 0, -1)); + $this->assertFalse($this->redis->lTrim($key, 0, 1)); + $this->assertFalse($this->redis->lIndex($key, 0)); + $this->assertFalse($this->redis->lSet($key, 0, 'newValue')); + $this->assertFalse($this->redis->lrem($key, 'lvalue', 1)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->rPop($key)); + $this->assertFalse($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)); + $this->assertFalse($this->redis->sAdd($key, 'sValue1')); + $this->assertFalse($this->redis->srem($key, 'sValue1')); + $this->assertFalse($this->redis->sPop($key)); + $this->assertFalse($this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); + $this->assertFalse($this->redis->scard($key)); + $this->assertFalse($this->redis->sismember($key, 'sValue1')); + $this->assertFalse($this->redis->sInter($key, $dkey . 'skey2')); + $this->assertFalse($this->redis->sUnion($key, $dkey . 'skey4')); + $this->assertFalse($this->redis->sDiff($key, $dkey . 'skey7')); + $this->assertFalse($this->redis->sMembers($key)); + $this->assertFalse($this->redis->sRandMember($key)); // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zAdd($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRem($key, 'zValue1')); + $this->assertFalse($this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRevRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRange($key, 0, -1)); + $this->assertFalse($this->redis->zRevRange($key, 0, -1)); + $this->assertFalse($this->redis->zRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zCount($key, 0, -1)); + $this->assertFalse($this->redis->zCard($key)); + $this->assertFalse($this->redis->zScore($key, 'zValue1')); + $this->assertFalse($this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertFalse($this->redis->zRemRangeByScore($key, 1, 2)); } public function testSerializerPHP() { $this->checkSerializer(Redis::SERIALIZER_PHP); // with prefix - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->checkSerializer(Redis::SERIALIZER_PHP); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } public function testSerializerIGBinary() { @@ -5000,8 +5004,8 @@ private function checkSerializer($mode) { $this->redis->del('key'); $this->assertEquals(Redis::SERIALIZER_NONE, $this->redis->getOption(Redis::OPT_SERIALIZER)); // default - $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, $mode) === TRUE); // set ok - $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === $mode); // get ok + $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, $mode)); // set ok + $this->assertEquals($mode, $this->redis->getOption(Redis::OPT_SERIALIZER)); // get ok // lPush, rPush $a = ['hello world', 42, TRUE, ['' => 1729]]; @@ -5012,7 +5016,7 @@ private function checkSerializer($mode) { $this->redis->rPush('key', $a[3]); // lrange - $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); + $this->assertEquals($this->redis->lrange('key', 0, -1), $a); // lIndex $this->assertEquals($a[0], $this->redis->lIndex('key', 0)); @@ -5021,58 +5025,58 @@ private function checkSerializer($mode) { $this->assertEquals($a[3], $this->redis->lIndex('key', 3)); // lrem - $this->assertTrue($this->redis->lrem('key', $a[3]) === 1); - $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1)); + $this->assertEquals(1, $this->redis->lrem('key', $a[3])); + $this->assertEquals(array_slice($a, 0, 3), $this->redis->lrange('key', 0, -1)); // lSet $a[0] = ['k' => 'v']; // update - $this->assertEquals(TRUE, $this->redis->lSet('key', 0, $a[0])); + $this->assertTrue($this->redis->lSet('key', 0, $a[0])); $this->assertEquals($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); + $this->assertEquals(4, $this->redis->lInsert('key', Redis::BEFORE, $a[0], [1,2,3])); + $this->assertEquals(5, $this->redis->lInsert('key', Redis::AFTER, $a[0], [4,5,6])); $a = [[1,2,3], $a[0], [4,5,6], $a[1], $a[2]]; - $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); + $this->assertEquals($this->redis->lrange('key', 0, -1), $a); // sAdd $this->redis->del('{set}key'); $s = [1,'a', [1,2,3], ['k' => 'v']]; - $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[0])); - $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[1])); - $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[2])); - $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[3])); + $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[0])); + $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[1])); + $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[2])); + $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[3])); // variadic sAdd $this->redis->del('k'); - $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); - $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); + $this->assertEquals(3, $this->redis->sAdd('k', 'a', 'b', 'c')); + $this->assertEquals(1, $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); // srem - $this->assertTrue(1 === $this->redis->srem('{set}key', $s[3])); - $this->assertTrue(0 === $this->redis->srem('{set}key', $s[3])); + $this->assertEquals(1, $this->redis->srem('{set}key', $s[3])); + $this->assertEquals(0, $this->redis->srem('{set}key', $s[3])); // variadic $this->redis->del('k'); $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); - $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); - $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); - $this->assertEquals(0, $this->redis->exists('k')); + $this->assertEquals(2, $this->redis->sRem('k', 'a', 'd')); + $this->assertEquals(2, $this->redis->sRem('k', 'b', 'c', 'e')); + $this->assertKeyMissing($this->redis, '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])); + $this->assertTrue($this->redis->sismember('{set}key', $s[0])); + $this->assertTrue($this->redis->sismember('{set}key', $s[1])); + $this->assertTrue($this->redis->sismember('{set}key', $s[2])); + $this->assertFalse($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])); + $this->assertFalse($this->redis->sismember('{set}key', $s[0])); + $this->assertTrue($this->redis->sismember('{set}tmp', $s[0])); unset($s[0]); // sorted sets @@ -5080,14 +5084,14 @@ private function checkSerializer($mode) { $this->redis->del('key'); // zAdd - $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0])); - $this->assertTrue(1 === $this->redis->zAdd('key', 1, $z[1])); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2])); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3])); + $this->assertEquals(1, $this->redis->zAdd('key', 0, $z[0])); + $this->assertEquals(1, $this->redis->zAdd('key', 1, $z[1])); + $this->assertEquals(1, $this->redis->zAdd('key', 2, $z[2])); + $this->assertEquals(1, $this->redis->zAdd('key', 3, $z[3])); // zRem - $this->assertTrue(1 === $this->redis->zRem('key', $z[3])); - $this->assertTrue(0 === $this->redis->zRem('key', $z[3])); + $this->assertEquals(1, $this->redis->zRem('key', $z[3])); + $this->assertEquals(0, $this->redis->zRem('key', $z[3])); unset($z[3]); // variadic @@ -5095,43 +5099,43 @@ private function checkSerializer($mode) { $this->redis->zAdd('k', 0, 'a'); $this->redis->zAdd('k', 1, 'b'); $this->redis->zAdd('k', 2, 'c'); - $this->assertTrue(2 === $this->redis->zRem('k', 'a', 'c')); - $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); - $this->assertTrue($this->redis->zRange('k', 0, -1, true) == ['b' => 1.0]); + $this->assertEquals(2, $this->redis->zRem('k', 'a', 'c')); + $this->assertEquals(1.0, $this->redis->zScore('k', 'b')); + $this->assertEquals(['b' => 1.0], $this->redis->zRange('k', 0, -1, true)); // zRange - $this->assertTrue($z === $this->redis->zRange('key', 0, -1)); + $this->assertEquals($this->redis->zRange('key', 0, -1), $z); // 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])); + $this->assertEquals(0.0, $this->redis->zScore('key', $z[0])); + $this->assertEquals(1.0, $this->redis->zScore('key', $z[1])); + $this->assertEquals(2.0, $this->redis->zScore('key', $z[2])); // zRank - $this->assertTrue(0 === $this->redis->zRank('key', $z[0])); - $this->assertTrue(1 === $this->redis->zRank('key', $z[1])); - $this->assertTrue(2 === $this->redis->zRank('key', $z[2])); + $this->assertEquals(0, $this->redis->zRank('key', $z[0])); + $this->assertEquals(1, $this->redis->zRank('key', $z[1])); + $this->assertEquals(2, $this->redis->zRank('key', $z[2])); // zRevRank - $this->assertTrue(2 === $this->redis->zRevRank('key', $z[0])); - $this->assertTrue(1 === $this->redis->zRevRank('key', $z[1])); - $this->assertTrue(0 === $this->redis->zRevRank('key', $z[2])); + $this->assertEquals(2, $this->redis->zRevRank('key', $z[0])); + $this->assertEquals(1, $this->redis->zRevRank('key', $z[1])); + $this->assertEquals(0, $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->assertEquals(3.0, $this->redis->zIncrBy('key', 1.0, $z[2])); + $this->assertEquals(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->assertEquals(5.0, $this->redis->zIncrBy('key', 2.0, $z[2])); + $this->assertEquals(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])); + $this->assertEquals(2.0, $this->redis->zIncrBy('key', -3.0, $z[2])); + $this->assertEquals(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)); + $this->assertTrue($this->redis->mset($a)); foreach($a as $k => $v) { - $this->assertTrue($this->redis->get($k) === $v); + $this->assertEquals($v, $this->redis->get($k)); } $a = ['f0' => 1, 'f1' => 42, 'f2' => NULL, 'f3' => FALSE, 'f4' => ['a' => 'b']]; @@ -5139,33 +5143,33 @@ private function checkSerializer($mode) { // hSet $this->redis->del('hash'); foreach($a as $k => $v) { - $this->assertTrue(1 === $this->redis->hSet('hash', $k, $v)); + $this->assertEquals(1, $this->redis->hSet('hash', $k, $v)); } // hGet foreach($a as $k => $v) { - $this->assertTrue($v === $this->redis->hGet('hash', $k)); + $this->assertEquals($this->redis->hGet('hash', $k), $v); } // hGetAll - $this->assertTrue($a === $this->redis->hGetAll('hash')); - $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f0')); - $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f1')); - $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f2')); - $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f3')); - $this->assertTrue(TRUE === $this->redis->hExists('hash', 'f4')); + $this->assertEquals($this->redis->hGetAll('hash'), $a); + $this->assertTrue($this->redis->hExists('hash', 'f0')); + $this->assertTrue($this->redis->hExists('hash', 'f1')); + $this->assertTrue($this->redis->hExists('hash', 'f2')); + $this->assertTrue($this->redis->hExists('hash', 'f3')); + $this->assertTrue($this->redis->hExists('hash', 'f4')); // hMSet $this->redis->del('hash'); $this->redis->hMSet('hash', $a); foreach($a as $k => $v) { - $this->assertTrue($v === $this->redis->hGet('hash', $k)); + $this->assertEquals($this->redis->hGet('hash', $k), $v); } // hMget $hmget = $this->redis->hMget('hash', array_keys($a)); foreach($hmget as $k => $v) { - $this->assertTrue($v === $a[$k]); + $this->assertEquals($a[$k], $v); } // getMultiple @@ -5174,7 +5178,7 @@ private function checkSerializer($mode) { $this->redis->set('c', 42); $this->redis->set('d', ['x' => 'y']); - $this->assertTrue([NULL, FALSE, 42, ['x' => 'y']] === $this->redis->mGet(['a', 'b', 'c', 'd'])); + $this->assertEquals([NULL, FALSE, 42, ['x' => 'y']], $this->redis->mGet(['a', 'b', 'c', 'd'])); // pipeline if ($this->havePipeline()) { @@ -5195,29 +5199,29 @@ private function checkSerializer($mode) { $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'); + $this->assertEquals('test 1', $data['data']); + $this->assertEquals('test 2', $data['session_id']); // issue #145, serializer with objects. $this->redis->set('x', [new stdClass, new stdClass]); $x = $this->redis->get('x'); - $this->assertTrue(is_array($x)); + $this->assertIsArray($x); if ($mode === Redis::SERIALIZER_JSON) { - $this->assertTrue(is_array($x[0])); - $this->assertTrue(is_array($x[1])); + $this->assertIsArray($x[0]); + $this->assertIsArray($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_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE); // set ok - $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok + $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE)); // set ok + $this->assertEquals(Redis::SERIALIZER_NONE, $this->redis->getOption(Redis::OPT_SERIALIZER)); // get ok } // check that zRem doesn't crash with a missing parameter (GitHub issue #102): // public function testGHIssue_102() { -// $this->assertTrue(FALSE === @$this->redis->zRem('key')); +// $this->assertFalse(@$this->redis->zRem('key')); // } public function testCompressionLZF() @@ -5308,7 +5312,7 @@ private function checkCompression($mode, $level) public function testDumpRestore() { - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) { $this->markTestSkipped(); } @@ -5329,8 +5333,8 @@ public function testDumpRestore() { $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->assertEquals('this-is-bar', $this->redis->get('foo')); + $this->assertEquals('this-is-foo', $this->redis->get('bar')); /* Test that we can REPLACE a key */ $this->assertTrue($this->redis->set('foo', 'some-value')); @@ -5354,7 +5358,7 @@ public function testDumpRestore() { public function testGetLastError() { // We shouldn't have any errors now - $this->assertTrue($this->redis->getLastError() === NULL); + $this->assertEquals(NULL, $this->redis->getLastError()); // test getLastError with a regular command $this->redis->set('x', 'a'); @@ -5364,7 +5368,7 @@ public function testGetLastError() { // clear error $this->redis->clearLastError(); - $this->assertTrue($this->redis->getLastError() === NULL); + $this->assertEquals(NULL, $this->redis->getLastError()); } // Helper function to compare nested results -- from the php.net array_diff page, I believe @@ -5393,7 +5397,7 @@ private function array_diff_recursive($aArray1, $aArray2) { public function testScript() { - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) { $this->markTestSkipped(); } @@ -5425,7 +5429,7 @@ public function testScript() { public function testEval() { - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) { $this->markTestSkipped(); } @@ -5460,7 +5464,7 @@ public function testEval() { // 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']); + $this->assertEquals(['a','b','c'], $list); // Use a script to return our zset $zset = $this->redis->eval("return redis.call('zrange', KEYS[1], 0, -1)", ['{eval-key}-zset'], 1); @@ -5470,7 +5474,7 @@ public function testEval() { $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)); + $this->assertEquals([], $empty_resp); // Now test a nested reply $nested_script = " @@ -5531,7 +5535,7 @@ public function testEval() { $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_args, 3); - $this->assertTrue($args_result === $args_args); + $this->assertEquals($args_args, $args_result); // turn on key prefixing $this->redis->setOption(Redis::OPT_PREFIX, 'prefix:'); @@ -5550,7 +5554,7 @@ public function testEval() { } public function testEvalSHA() { - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) { $this->markTestSkipped(); } @@ -5567,9 +5571,9 @@ public function testEvalSHA() { $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(1 === $this->redis->eval($scr)); - $this->assertTrue(1 === $this->redis->evalsha($sha)); + $this->assertFalse($this->redis->evalsha($scr)); + $this->assertEquals(1, $this->redis->eval($scr)); + $this->assertEquals(1, $this->redis->evalsha($sha)); /* Our evalsha_ro handler is the same as evalsha so just make sure we can invoke the command */ @@ -5581,10 +5585,10 @@ public function testSerialize() { $vals = [1, 1.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'); + $this->assertEquals('test', $this->redis->_serialize('test')); + $this->assertEquals('1', $this->redis->_serialize(1)); + $this->assertEquals('Array', $this->redis->_serialize([])); + $this->assertEquals('Object', $this->redis->_serialize(new stdClass)); foreach($this->getSerializers() as $mode) { $arr_enc = []; @@ -5605,7 +5609,7 @@ public function testUnserialize() { 1,1.5,'one',['this','is','an','array'] ]; - $serializers = Array(Redis::SERIALIZER_PHP); + $serializers = [Redis::SERIALIZER_PHP]; if(defined('Redis::SERIALIZER_IGBINARY')) { $serializers[] = Redis::SERIALIZER_IGBINARY; @@ -5622,7 +5626,7 @@ public function testUnserialize() { foreach($vals as $key => $val) { $this->redis->setOption(Redis::OPT_SERIALIZER, $mode); - $key = "key" . ++$key; + $key = 'key' . ++$key; $this->redis->del($key); $this->redis->set($key, $val); @@ -5747,11 +5751,11 @@ public function testNullArray() { foreach ([false => [], true => NULL] as $opt => $test) { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt); - $r = $this->redis->rawCommand("BLPOP", $key, .05); + $r = $this->redis->rawCommand('BLPOP', $key, .05); $this->assertEquals($test, $r); $this->redis->multi(); - $this->redis->rawCommand("BLPOP", $key, .05); + $this->redis->rawCommand('BLPOP', $key, .05); $r = $this->redis->exec(); $this->assertEquals([$test], $r); } @@ -5778,14 +5782,16 @@ public function testNestedNullArray() { public function testConfig() { /* GET */ $cfg = $this->redis->config('GET', 'timeout'); - $this->assertTrue(is_array($cfg) && isset($cfg['timeout'])); + $this->assertArrayKey($cfg, 'timeout'); $sec = $cfg['timeout']; /* SET */ foreach ([$sec + 30, $sec] as $val) { $this->assertTrue($this->redis->config('SET', 'timeout', $val)); $cfg = $this->redis->config('GET', 'timeout'); - $this->assertTrue(isset($cfg['timeout']) && $cfg['timeout'] == $val); + $this->assertArrayKey($cfg, 'timeout', function ($v) use ($val) { + return $v == $val; + }); } /* RESETSTAT */ @@ -5808,7 +5814,7 @@ public function testConfig() { $this->redis->clearLastError(); } - if (!$this->minVersionCheck("7.0.0")) + if (!$this->minVersionCheck('7.0.0')) return; /* Test getting multiple values */ @@ -5830,9 +5836,7 @@ public function testConfig() { foreach ($updates as $update) { $this->assertTrue($this->redis->config('set', $update)); $vals = $this->redis->config('get', array_keys($update)); - ksort($vals); - ksort($update); - $this->assertEquals($vals, $update); + $this->assertEqualsCanonicalizing($vals, $update, true); } /* Make sure PhpRedis catches malformed multiple get/set calls */ @@ -5872,7 +5876,7 @@ public function testReconnectSelect() { public function testTime() { - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) { $this->markTestSkipped(); } @@ -5886,15 +5890,15 @@ public function testReadTimeoutOption() { $this->assertTrue(defined('Redis::OPT_READ_TIMEOUT')); - $this->redis->setOption(Redis::OPT_READ_TIMEOUT, "12.3"); + $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()); + $this->assertEquals($this->getHost(), $this->redis->getHost()); + $this->assertEquals($this->getPort(), $this->redis->getPort()); + $this->assertEquals($this->getAuth(), $this->redis->getAuth()); } public function testTransferredBytes() { @@ -5939,7 +5943,7 @@ protected function get_keyspace_count($str_db) { } public function testScan() { - if(version_compare($this->version, "2.8.0") < 0) { + if(version_compare($this->version, '2.8.0') < 0) { $this->markTestSkipped(); return; } @@ -5972,7 +5976,7 @@ public function testScan() { $this->assertEquals(0, $i); // SCAN with type is scheduled for release in Redis 6. - if (version_compare($this->version, "6.0.0") >= 0) { + if (version_compare($this->version, '6.0.0') >= 0) { // Use a unique ID so we can find our type keys $id = uniqid(); @@ -5985,8 +5989,8 @@ public function testScan() { $this->redis->del($str_list); $this->redis->rpush($str_list, ['foo']); - $arr_keys["STRING"][] = $str_simple; - $arr_keys["LIST"][] = $str_list; + $arr_keys['STRING'][] = $str_simple; + $arr_keys['LIST'][] = $str_list; } // Make sure we can scan for specific types @@ -5999,8 +6003,7 @@ public function testScan() { $arr_resp = array_merge($arr_resp, $arr_scan); } - sort($arr_vals); sort($arr_resp); - $this->assertEquals($arr_vals, $arr_resp); + $this->assertEqualsCanonicalizing($arr_vals, $arr_resp); } } } @@ -6013,7 +6016,7 @@ public function testScanPrefix() { $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"); + $this->redis->set("$keyid", 'LOLWUT'); $arr_all_keys["{$str_prefix}{$keyid}"] = true; } @@ -6050,43 +6053,43 @@ public function testMaxRetriesOption() { public function testBackoffOptions() { $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DEFAULT); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DEFAULT); + $this->assertEquals(Redis::BACKOFF_ALGORITHM_DEFAULT, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM)); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_CONSTANT); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_CONSTANT); + $this->assertEquals(Redis::BACKOFF_ALGORITHM_CONSTANT, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM)); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_UNIFORM); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_UNIFORM); + $this->assertEquals(Redis::BACKOFF_ALGORITHM_UNIFORM, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM)); $this->redis -> setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EXPONENTIAL); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EXPONENTIAL); + $this->assertEquals(Redis::BACKOFF_ALGORITHM_EXPONENTIAL, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM)); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EQUAL_JITTER); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EQUAL_JITTER); + $this->assertEquals(Redis::BACKOFF_ALGORITHM_EQUAL_JITTER, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM)); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_FULL_JITTER); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_FULL_JITTER); + $this->assertEquals(Redis::BACKOFF_ALGORITHM_FULL_JITTER, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM)); $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER); + $this->assertEquals(Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM)); $this->assertFalse($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, 55555)); $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 500); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 500); + $this->assertEquals(500, $this->redis->getOption(Redis::OPT_BACKOFF_BASE)); $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 750); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 750); + $this->assertEquals(750, $this->redis->getOption(Redis::OPT_BACKOFF_BASE)); $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 500); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 500); + $this->assertEquals(500, $this->redis->getOption(Redis::OPT_BACKOFF_CAP)); $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 750); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 750); + $this->assertEquals(750, $this->redis->getOption(Redis::OPT_BACKOFF_CAP)); } public function testHScan() { - if (version_compare($this->version, "2.8.0") < 0) { + if (version_compare($this->version, '2.8.0') < 0) { $this->markTestSkipped(); return; } @@ -6126,7 +6129,7 @@ public function testHScan() { } public function testSScan() { - if (version_compare($this->version, "2.8.0") < 0) { + if (version_compare($this->version, '2.8.0') < 0) { $this->markTestSkipped(); return; } @@ -6158,7 +6161,7 @@ public function testSScan() { } public function testZScan() { - if (version_compare($this->version, "2.8.0") < 0) { + if (version_compare($this->version, '2.8.0') < 0) { $this->markTestSkipped(); return; } @@ -6193,7 +6196,7 @@ public function testZScan() { $this->assertEquals(0, $i); $this->assertEquals((float)0, $i_tot_score); - // Just scan "pmem" members + // Just scan 'pmem' members $it = NULL; $i_p_score_old = $i_p_score; $i_p_count_old = $i_p_count; @@ -6252,7 +6255,7 @@ protected function createPFKey($str_key, $i_count) { public function testPFCommands() { // Isn't available until 2.8.9 - if (version_compare($this->version, "2.8.9") < 0) { + if (version_compare($this->version, '2.8.9') < 0) { $this->markTestSkipped(); return; } @@ -6289,13 +6292,13 @@ public function testPFCommands() { // Grab estimated cardinality $i_card = $this->redis->pfcount($str_key); - $this->assertTrue(is_int($i_card)); + $this->assertIsInt($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); + $this->assertEquals($i_card, $this->redis->pfcount($str_key)); } // Clean up merge key @@ -6333,15 +6336,15 @@ protected function addCities($key) { /* GEOADD */ public function testGeoAdd() { - if (!$this->minVersionCheck("3.2")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('3.2')) { + $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); + $this->assertEquals(1, $this->redis->geoadd('geokey', $longlat[0], $longlat[1], $city)); } /* Add them again, all at once */ @@ -6356,8 +6359,8 @@ public function testGeoAdd() { /* GEORADIUS */ public function genericGeoRadiusTest($cmd) { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('3.2.0')) { + $this->markTestSkipped(); } /* Chico */ @@ -6369,28 +6372,28 @@ public function genericGeoRadiusTest($cmd) { /* 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, $lat, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array($cmd, '{gk}', $lng, $lat, 500, 'mi'); + $this->assertEquals(['Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi')); + $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi')); + $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 50, 'km')); + $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 150000, 'ft')); + $args = [$cmd, '{gk}', $lng, $lat, 500, 'mi']; /* Test a bad COUNT argument */ - foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi', Array('count' => $count))); + foreach ([-1, 0, 'notanumber'] as $count) { + $this->assertFalse(@$this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi', ['count' => $count])); } } else { - $this->assertEquals($this->redis->$cmd('{gk}', $city, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $city, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $city, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $city, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $city, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array($cmd, '{gk}', $city, 500, 'mi'); + $this->assertEquals(['Chico'], $this->redis->$cmd('{gk}', $city, 10, 'mi')); + $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $city, 30, 'mi')); + $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $city, 50, 'km')); + $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $city, 50000, 'm')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 150000, 'ft')); + $args = [$cmd, '{gk}', $city, 500, 'mi']; /* Test a bad COUNT argument */ - foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->$cmd('{gk}', $city, 10, 'mi', Array('count' => $count))); + foreach ([-1, 0, 'notanumber'] as $count) { + $this->assertFalse(@$this->redis->$cmd('{gk}', $city, 10, 'mi', ['count' => $count])); } } @@ -6458,8 +6461,8 @@ public function genericGeoRadiusTest($cmd) { } public function testGeoRadius() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('3.2.0')) { + $this->markTestSkipped(); } $this->genericGeoRadiusTest('georadius'); @@ -6467,8 +6470,8 @@ public function testGeoRadius() { } public function testGeoRadiusByMember() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('3.2.0')) { + $this->markTestSkipped(); } $this->genericGeoRadiusTest('georadiusbymember'); @@ -6476,28 +6479,28 @@ public function testGeoRadiusByMember() { } public function testGeoPos() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('3.2.0')) { + $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'])); + $this->assertEquals($this->rawCommandArray('gk', ['geopos', 'gk', 'Chico', 'Sacramento']), $this->redis->geopos('gk', 'Chico', 'Sacramento')); + $this->assertEquals($this->rawCommandArray('gk', ['geopos', 'gk', 'Cupertino']), $this->redis->geopos('gk', 'Cupertino')); } public function testGeoHash() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('3.2.0')) { + $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'])); + $this->assertEquals($this->rawCommandArray('gk', ['geohash', 'gk', 'Chico', 'Sacramento']), $this->redis->geohash('gk', 'Chico', 'Sacramento')); + $this->assertEquals($this->rawCommandArray('gk', ['geohash', 'gk', 'Chico']), $this->redis->geohash('gk', 'Chico')); } public function testGeoDist() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('3.2.0')) { + $this->markTestSkipped(); } $this->addCities('gk'); @@ -6512,13 +6515,13 @@ public function testGeoDist() { } public function testGeoSearch() { - if (!$this->minVersionCheck("6.2.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('6.2.0')) { + $this->markTestSkipped(); } $this->addCities('gk'); - $this->assertEquals($this->redis->geosearch('gk', 'Chico', 1, 'm'), ['Chico']); + $this->assertEquals(['Chico'], $this->redis->geosearch('gk', 'Chico', 1, 'm')); $this->assertValidate($this->redis->geosearch('gk', 'Chico', 1, 'm', ['withcoord', 'withdist', 'withhash']), function ($v) { $this->assertArrayKey($v, 'Chico', 'is_array'); $this->assertEquals(count($v['Chico']), 3); @@ -6530,13 +6533,13 @@ public function testGeoSearch() { } public function testGeoSearchStore() { - if (!$this->minVersionCheck("6.2.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('6.2.0')) { + $this->markTestSkipped(); } $this->addCities('{gk}src'); - $this->assertEquals($this->redis->geosearchstore('{gk}dst', '{gk}src', 'Chico', 100, 'km'), 3); - $this->assertEquals($this->redis->geosearch('{gk}dst', 'Chico', 1, 'm'), ['Chico']); + $this->assertEquals(3, $this->redis->geosearchstore('{gk}dst', '{gk}src', 'Chico', 100, 'km')); + $this->assertEquals(['Chico'], $this->redis->geosearch('{gk}dst', 'Chico', 1, 'm')); } /* Test a 'raw' command */ @@ -6549,7 +6552,7 @@ public function testRawCommand() { $this->redis->del('mylist'); $this->redis->rpush('mylist', 'A', 'B', 'C', 'D'); - $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']); + $this->assertEquals(['A','B','C','D'], $this->redis->lrange('mylist', 0, -1)); } /* STREAMS */ @@ -6580,13 +6583,13 @@ protected function addStreamsAndGroups($arr_streams, $count, $arr_groups) { } public function testXAdd() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $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); + $id = $this->redis->xAdd('stream', '*', ['k1' => 'v1', 'k2' => 'v2']); + $this->assertEquals($i+1, $this->redis->xLen('stream')); /* Redis should return - */ $bits = explode('-', $id); @@ -6599,7 +6602,7 @@ public function testXAdd() { for ($i = 0; $i < 100; $i++) { $this->redis->xAdd('stream', '*', ['k' => 'v'], 10); } - $this->assertEquals($this->redis->xLen('stream'), 10); + $this->assertEquals(10, $this->redis->xLen('stream')); /* Not the greatest test but I'm unsure if approximate trimming is * totally deterministic, so just make sure we are able to add with @@ -6645,8 +6648,8 @@ protected function doXRangeTest($reverse) { } public function testXRange() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); foreach ([false, true] as $reverse) { foreach ($this->getSerializers() as $serializer) { @@ -6660,19 +6663,19 @@ public function testXRange() { } protected function testXLen() { - if (!$this->minVersionCheck("5.0")) + 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); + $this->assertEquals($i+1, $this->redis->xLen('{stream}')); } } public function testXGroup() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); /* CREATE MKSTREAM */ $str_key = 's:' . uniqid(); @@ -6680,7 +6683,7 @@ public function testXGroup() { $this->assertTrue($this->redis->xGroup('CREATE', $str_key, 'g1', 0, true)); /* XGROUP DESTROY */ - $this->assertEquals($this->redis->xGroup('DESTROY', $str_key, 'g1'), 1); + $this->assertEquals(1, $this->redis->xGroup('DESTROY', $str_key, 'g1')); /* Populate some entries in stream 's' */ $this->addStreamEntries('s', 2); @@ -6691,7 +6694,7 @@ public function testXGroup() { /* BUSYGROUP */ $this->redis->xGroup('CREATE', 's', 'mygroup', '$'); - $this->assertTrue(strpos($this->redis->getLastError(), 'BUSYGROUP') === 0); + $this->assertEquals(0, strpos($this->redis->getLastError(), 'BUSYGROUP')); /* SETID */ $this->assertTrue($this->redis->xGroup('SETID', 's', 'mygroup', '$')); @@ -6735,8 +6738,8 @@ public function testXGroup() { } public function testXAck() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); for ($n = 1; $n <= 3; $n++) { $this->addStreamsAndGroups(['{s}'], 3, ['g1' => 0]); @@ -6748,7 +6751,7 @@ public function testXAck() { /* Now ACK $n messages */ $ids = array_slice($ids, 0, $n); - $this->assertEquals($this->redis->xAck('{s}', 'g1', $ids), $n); + $this->assertEquals($n, $this->redis->xAck('{s}', 'g1', $ids)); } /* Verify sending no IDs is a failure */ @@ -6756,8 +6759,8 @@ public function testXAck() { } protected function doXReadTest() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); $row = ['f1' => 'v1', 'f2' => 'v2']; $msgdata = [ @@ -6806,8 +6809,8 @@ protected function doXReadTest() { } public function testXRead() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); foreach ($this->getSerializers() as $serializer) { $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); @@ -6837,8 +6840,8 @@ protected function compareStreamIds($redis, $control) { } public function testXReadGroup() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); /* Create some streams and groups */ $streams = ['{s}-1', '{s}-2']; @@ -6908,8 +6911,8 @@ public function testXReadGroup() { } public function testXPending() { - if (!$this->minVersionCheck("5.0")) { - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) { + $this->markTestSkipped(); } $rows = 5; @@ -6942,13 +6945,13 @@ public function testXPending() { } public function testXDel() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); for ($n = 5; $n > 0; $n--) { $ids = $this->addStreamEntries('s', 5); $todel = array_slice($ids, 0, $n); - $this->assertEquals($this->redis->xDel('s', $todel), count($todel)); + $this->assertEquals(count($todel), $this->redis->xDel('s', $todel)); } /* Empty array should fail */ @@ -6956,8 +6959,8 @@ public function testXDel() { } public function testXTrim() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); for ($maxlen = 0; $maxlen <= 50; $maxlen += 10) { $this->addStreamEntries('stream', 100); @@ -6968,10 +6971,10 @@ public function testXTrim() { /* 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', 1, true) === false); + $this->assertFalse($this->redis->xTrim('stream', 1, true)); /* We need Redis >= 6.2.0 for MINID and LIMIT options */ - if (!$this->minVersionCheck("6.2.0")) + if (!$this->minVersionCheck('6.2.0')) return; $this->assertEquals(1, $this->redis->del('stream')); @@ -6995,8 +6998,8 @@ public function testXTrim() { /* 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(); + if (!$this->minVersionCheck('5.0')) + $this->markTestSkipped(); foreach ([0, 100] as $min_idle_time) { foreach ([false, true] as $justid) { @@ -7112,7 +7115,7 @@ public function testXAutoClaim() { public function testXInfo() { - if (!$this->minVersionCheck("5.0")) + if (!$this->minVersionCheck('5.0')) $this->markTestSkipped(); /* Create some streams and groups */ @@ -7121,7 +7124,7 @@ public function testXInfo() $this->addStreamsAndGroups([$stream], 1, $groups); $info = $this->redis->xInfo('GROUPS', $stream); - $this->assertTrue(is_array($info)); + $this->assertIsArray($info); $this->assertEquals(count($info), count($groups)); foreach ($info as $group) { $this->assertTrue(array_key_exists('name', $group)); @@ -7129,7 +7132,7 @@ public function testXInfo() } $info = $this->redis->xInfo('STREAM', $stream); - $this->assertTrue(is_array($info)); + $this->assertIsArray($info); $this->assertTrue(array_key_exists('groups', $info)); $this->assertEquals($info['groups'], count($groups)); foreach (['first-entry', 'last-entry'] as $key) { @@ -7139,12 +7142,12 @@ public function testXInfo() /* Ensure that default/NULL arguments are ignored */ $info = $this->redis->xInfo('STREAM', $stream, NULL); - $this->assertTrue(is_array($info)); + $this->assertIsArray($info); $info = $this->redis->xInfo('STREAM', $stream, NULL, -1); - $this->assertTrue(is_array($info)); + $this->assertIsArray($info); /* XINFO STREAM FULL [COUNT N] Requires >= 6.0.0 */ - if (!$this->minVersionCheck("6.0")) + if (!$this->minVersionCheck('6.0')) return; /* Add some items to the stream so we can test COUNT */ @@ -7153,7 +7156,7 @@ public function testXInfo() } $info = $this->redis->xInfo('STREAM', $stream, 'full'); - $this->assertTrue(isset($info['groups'])); + $this->assertArrayKey($info, 'length', 'is_numeric'); for ($count = 1; $count < 5; $count++) { $info = $this->redis->xInfo('STREAM', $stream, 'full', $count); @@ -7185,7 +7188,7 @@ public function testXInfoEmptyStream() { $arr_info = $this->redis->xInfo('STREAM', 's'); - $this->assertTrue(is_array($arr_info)); + $this->assertIsArray($arr_info); $this->assertEquals(0, $arr_info['length']); $this->assertEquals(NULL, $arr_info['first-entry']); $this->assertEquals(NULL, $arr_info['last-entry']); @@ -7220,11 +7223,10 @@ public function testInvalidAuthArgs() { } public function testAcl() { - if ( ! $this->minVersionCheck("6.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('6.0')) + $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')); @@ -7244,7 +7246,7 @@ 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); + $this->assert("Expected an array from ACL LOG, got: " . var_export($arr_log, true)); return; } @@ -7292,7 +7294,7 @@ function($o) { $o->auth(['1337haxx00r', 'lolwut']); }, '/^WRONGPASS.*$/'); /* 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(); + $this->markTestSkipped(); } $arr_sock_tests = [ @@ -7317,7 +7319,7 @@ public function testUnixSocket() { $this->assertTrue($obj_r->ping()); } } catch (Exception $ex) { - $this->assertTrue(false); + $this->assert("Exception: {$ex}"); } } @@ -7342,7 +7344,7 @@ public function testHighPorts() { }, [32768, 32769, 32770])); if ( ! $ports) { - return $this->markTestSkipped(); + $this->markTestSkipped(); } foreach ($ports as $port) { @@ -7354,7 +7356,7 @@ public function testHighPorts() { } $this->assertTrue($obj_r->ping()); } catch(Exception $ex) { - $this->assertTrue(false); + $this->assert("Exception: $ex"); } } } @@ -7643,7 +7645,7 @@ public function testMultipleConnect() { public function testConnectException() { $host = 'github.com'; if (gethostbyname($host) === $host) { - return $this->markTestSkipped('online test'); + $this->markTestSkipped('online test'); } $redis = new Redis(); try { @@ -7656,7 +7658,7 @@ public function testConnectException() { public function testTlsConnect() { if (($fp = @fsockopen($this->getHost(), 6378)) == NULL) - return $this->markTestSkipped(); + $this->markTestSkipped(); fclose($fp); @@ -7705,22 +7707,22 @@ public function testCopy() public function testCommand() { $commands = $this->redis->command(); - $this->assertTrue(is_array($commands)); + $this->assertIsArray($commands); $this->assertEquals(count($commands), $this->redis->command('count')); if (!$this->is_keydb) { $infos = $this->redis->command('info'); - $this->assertTrue(is_array($infos)); + $this->assertIsArray($infos); $this->assertEquals(count($infos), count($commands)); } if (version_compare($this->version, '7.0') >= 0) { $docs = $this->redis->command('docs'); - $this->assertTrue(is_array($docs)); + $this->assertIsArray($docs); $this->assertEquals(count($docs), 2 * count($commands)); $list = $this->redis->command('list', 'filterby', 'pattern', 'lol*'); - $this->assertTrue(is_array($list)); + $this->assertIsArray($list); $this->assertEquals($list, ['lolwut']); } } @@ -7736,10 +7738,10 @@ public function testFunction() { $payload = $this->redis->function('dump'); $this->assertEquals('mylib', $this->redis->function('load', 'replace', "#!lua name=mylib\nredis.register_function{function_name='myfunc', callback=function(keys, args) return args[1] end, flags={'no-writes'}}")); $this->assertEquals('foo', $this->redis->fcall_ro('myfunc', [], ['foo'])); - $this->assertEquals($this->redis->function('stats'), ['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]]); + $this->assertEquals(['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]], $this->redis->function('stats')); $this->assertTrue($this->redis->function('delete', 'mylib')); $this->assertTrue($this->redis->function('restore', $payload)); - $this->assertEquals($this->redis->function('list'), [['library_name' => 'mylib', 'engine' => 'LUA', 'functions' => [['name' => 'myfunc', 'description' => false,'flags' => []]]]]); + $this->assertEquals([['library_name' => 'mylib', 'engine' => 'LUA', 'functions' => [['name' => 'myfunc', 'description' => false,'flags' => []]]]], $this->redis->function('list')); $this->assertTrue($this->redis->function('delete', 'mylib')); } @@ -7748,7 +7750,7 @@ protected function execWaitAOF() { } public function testWaitAOF() { - if (!$this->minVersionCheck("7.2.0")) + if (!$this->minVersionCheck('7.2.0')) $this->markTestSkipped(); $res = $this->execWaitAOF(); diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 8c5b857aa7..55499570c8 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -173,6 +173,24 @@ protected function assertInArray($ele, $arr, ?callable $cb = NULL) { return false; } + protected function assertIsInt($v) { + if (is_int($v)) + return true; + + self::$errors []= $this->assertionTrace("%s is not an integer", $this->printArg($v)); + + return false; + } + + protected function assertIsArray($v) { + if (is_array($v)) + return true; + + self::$errors []= $this->assertionTrace("%s is not an array", $this->printArg($v)); + + return false; + } + protected function assertArrayKey($arr, $key, callable $cb = NULL) { $cb ??= function ($v) { return true; }; @@ -267,6 +285,9 @@ protected function externalCmdFailure($cmd, $output, $msg = NULL, $exit_code = N } protected function assertBetween($value, $min, $max, bool $exclusive = false) { + if ($min > $max) + [$max, $min] = [$min, $max]; + if ($exclusive) { if ($value > $min && $value < $max) return true; @@ -281,12 +302,49 @@ protected function assertBetween($value, $min, $max, bool $exclusive = false) { return false; } - protected function assertEquals($a, $b) { - if($a === $b) + /* Replica of PHPUnit's assertion. Basically are two arrys the same without + ' respect to order. */ + protected function assertEqualsCanonicalizing($expected, $actual, $keep_keys = false) { + if ($expected InstanceOf Traversable) + $expected = iterator_to_array($expected); + + if ($actual InstanceOf Traversable) + $actual = iterator_to_array($actual); + + if ($keep_keys) { + asort($expected); + asort($actual); + } else { + sort($expected); + sort($actual); + } + + if ($expected === $actual) return true; - self::$errors[] = $this->assertionTrace("%s !== %s", $this->printArg($a), - $this->printArg($b)); + self::$errors []= $this->assertionTrace("%s !== %s", + $this->printArg($actual), + $this->printArg($expected)); + + return false; + } + + protected function assertEqualsWeak($expected, $actual) { + if ($expected == $actual) + return true; + + self::$errors []= $this->assertionTrace("%s != %s", $this->printArg($actual), + $this->printArg($expected)); + + return false; + } + + protected function assertEquals($expected, $actual) { + if($expected === $actual) + return true; + + self::$errors[] = $this->assertionTrace("%s !== %s", $this->printArg($actual), + $this->printArg($expected)); return false; } @@ -301,6 +359,18 @@ public function assertNotEquals($a, $b) { return false; } + protected function assertStringContains(string $needle, $haystack) { + if ( ! is_string($haystack)) { + self::$errors []= $this->assertionTrace("'%s' is not a string", $this->printArg($haystack)); + return false; + } + + if (strstr($haystack, $needle) !== false) + return true; + + self::$errors []= $this->assertionTrace("'%s' not found in '%s'", $needle, $haystack); + } + protected function assertPatternMatch($str_test, $str_regex) { if (preg_match($str_regex, $str_test)) return true; From 3c125b09f4e33a05973b73463c0216d812e6af1f Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 28 May 2024 12:17:39 -0700 Subject: [PATCH 1881/1986] More unit test cleanup. * Tighten up `assertTrue` and `assertFalse` such that they test that passed arguments `===` `true` and `===` `false` respectively, instead of testing for truth-like or false-like. * Start modernizing our unit tests to use explicit types for arguments, return types, member variables, etc. * Multiple assertion fixes that were exposed when making `assertTrue` and `assertFalse` more explicit. * Some formatting cleanup to style for incorrect indentation, etc, that had crept in over many years. * Add some more assertion helpers like `assertNull`, `assertGT`, `assertGTE`, `assertLT`, and `assertLTE`. --- tests/RedisArrayTest.php | 84 ++-- tests/RedisClusterTest.php | 52 +-- tests/RedisTest.php | 768 ++++++++++++++-------------------- tests/TestSuite.php | 243 ++++++----- tests/regenerateSessionId.php | 2 +- 5 files changed, 535 insertions(+), 614 deletions(-) diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 94624bac4a..67c35b892b 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -49,7 +49,7 @@ class Redis_Array_Test extends TestSuite public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); + $this->strings = []; for($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } @@ -66,11 +66,11 @@ public function setUp() { public function testMSet() { // run mset - $this->assertTrue(TRUE === $this->ra->mset($this->strings)); + $this->assertTrue($this->ra->mset($this->strings)); // check each key individually using the array foreach($this->strings as $k => $v) { - $this->assertTrue($v === $this->ra->get($k)); + $this->assertEquals($v, $this->ra->get($k)); } // check each key individually using a new connection @@ -88,16 +88,16 @@ public function testMSet() { if ($this->getAuth()) { $this->assertTrue($r->auth($this->getAuth())); } - $this->assertTrue($v === $r->get($k)); + $this->assertEquals($v, $r->get($k)); } } public function testMGet() { - $this->assertTrue(array_values($this->strings) === $this->ra->mget(array_keys($this->strings))); + $this->assertEquals(array_values($this->strings), $this->ra->mget(array_keys($this->strings))); } private function addData($commonString) { - $this->data = array(); + $this->data = []; for($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) { $k = rand().'_'.$commonString.'_'.rand(); $this->data[$k] = rand(); @@ -111,9 +111,9 @@ private function checkCommonLocality() { foreach($this->data as $k => $v) { $node = $this->ra->_target($k); if($lastNode) { - $this->assertTrue($node === $lastNode); + $this->assertEquals($node, $lastNode); } - $this->assertTrue($this->ra->get($k) == $v); + $this->assertEqualsWeak($v, $this->ra->get($k)); $lastNode = $node; } } @@ -163,7 +163,7 @@ public function testKeyDistributor() foreach($this->data as $k => $v) { $node = $this->ra->_target($k); $pos = $this->customDistributor($k); - $this->assertTrue($node === $newRing[$pos]); + $this->assertEquals($node, $newRing[$pos]); } } @@ -225,7 +225,7 @@ public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); + $this->strings = []; for($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } @@ -245,13 +245,13 @@ public function setUp() { // initialize hashes for($i = 0; $i < $n; $i++) { // each hash has 5 keys - $this->hashes['hash-'.$i] = array('A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4); + $this->hashes['hash-'.$i] = ['A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4]; } // initialize sorted sets for($i = 0; $i < $n; $i++) { // each sorted sets has 5 elements - $this->zsets['zset-'.$i] = array($i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E'); + $this->zsets['zset-'.$i] = [$i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E']; } global $newRing, $oldRing, $useIndex; @@ -289,12 +289,12 @@ private function distributeKeys() { // sets foreach($this->sets as $k => $v) { - call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v)); + call_user_func_array([$this->ra, 'sadd'], array_merge([$k], $v)); } // lists foreach($this->lists as $k => $v) { - call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v)); + call_user_func_array([$this->ra, 'rpush'], array_merge([$k], $v)); } // hashes @@ -304,7 +304,7 @@ private function distributeKeys() { // sorted sets foreach($this->zsets as $k => $v) { - call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v)); + call_user_func_array([$this->ra, 'zadd'], array_merge([$k], $v)); } } @@ -320,7 +320,7 @@ private function readAllvalues() { // strings foreach($this->strings as $k => $v) { - $this->assertTrue($this->ra->get($k) === $v); + $this->assertEquals($v, $this->ra->get($k)); } // sets @@ -351,7 +351,7 @@ private function readAllvalues() { $ret = $this->ra->zrange($k, 0, -1, TRUE); // get values with scores // create assoc array from local dataset - $tmp = array(); + $tmp = []; for($i = 0; $i < count($v); $i += 2) { $tmp[$v[$i+1]] = $v[$i]; } @@ -402,7 +402,7 @@ class Redis_Auto_Rehashing_Test extends TestSuite { public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); + $this->strings = []; for($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } @@ -426,7 +426,7 @@ public function testDistribute() { private function readAllvalues() { foreach($this->strings as $k => $v) { - $this->assertTrue($this->ra->get($k) === $v); + $this->assertEquals($v, $this->ra->get($k)); } } @@ -458,7 +458,7 @@ public function testAllKeysHaveBeenMigrated() { $this->assertTrue($r->auth($this->getAuth())); } - $this->assertTrue($v === $r->get($k)); // check that the key has actually been migrated to the new node. + $this->assertEquals($v, $r->get($k)); // check that the key has actually been migrated to the new node. } } } @@ -491,10 +491,10 @@ public function testInit() { public function testKeyDistribution() { // check that all of joe's keys are on the same instance $lastNode = NULL; - foreach(array('name', 'group', 'salary') as $field) { + foreach(['name', 'group', 'salary'] as $field) { $node = $this->ra->_target('1_{employee:joe}_'.$field); if($lastNode) { - $this->assertTrue($node === $lastNode); + $this->assertEquals($node, $lastNode); } $lastNode = $node; } @@ -514,8 +514,8 @@ public function testMultiExec() { ->exec(); // check that the group and salary have been changed - $this->assertTrue($this->ra->get('1_{employee:joe}_group') === $newGroup); - $this->assertTrue($this->ra->get('1_{employee:joe}_salary') == $newSalary); + $this->assertEquals($newGroup, $this->ra->get('1_{employee:joe}_group')); + $this->assertEqualsWeak($newSalary, $this->ra->get('1_{employee:joe}_salary')); } @@ -527,10 +527,10 @@ public function testMultiExecMSet() { // test MSET, making Joe a top-level executive $out = $this->ra->multi($this->ra->_target('{employee:joe}')) - ->mset(array('1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary)) + ->mset(['1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary]) ->exec(); - $this->assertTrue($out[0] === TRUE); + $this->assertTrue($out[0]); } public function testMultiExecMGet() { @@ -539,7 +539,7 @@ public function testMultiExecMGet() { // test MGET $out = $this->ra->multi($this->ra->_target('{employee:joe}')) - ->mget(array('1_{employee:joe}_group', '1_{employee:joe}_salary')) + ->mget(['1_{employee:joe}_group', '1_{employee:joe}_salary']) ->exec(); $this->assertTrue($out[0][0] == $newGroup); @@ -553,7 +553,7 @@ public function testMultiExecDel() { ->del('1_{employee:joe}_group', '1_{employee:joe}_salary') ->exec(); - $this->assertTrue($out[0] === 2); + $this->assertEquals(2, $out[0]); $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_group')); $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary')); } @@ -570,7 +570,7 @@ public function testMultiExecUnlink() { ->del('{unlink}:key1', '{unlink}:key2') ->exec(); - $this->assertTrue($out[0] === 2); + $this->assertEquals(2, $out[0]); } public function testDiscard() { @@ -578,31 +578,31 @@ public function testDiscard() { $key = 'test_err'; $this->assertTrue($this->ra->set($key, 'test')); - $this->assertTrue('test' === $this->ra->get($key)); + $this->assertEquals('test', $this->ra->get($key)); $this->ra->watch($key); // After watch, same - $this->assertTrue('test' === $this->ra->get($key)); + $this->assertEquals('test', $this->ra->get($key)); // change in a multi/exec block. $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test1')->exec(); - $this->assertTrue($ret === array(true)); + $this->assertEquals([true], $ret); // Get after exec, 'test1': - $this->assertTrue($this->ra->get($key) === 'test1'); + $this->assertEquals('test1', $this->ra->get($key)); $this->ra->watch($key); // After second watch, still test1. - $this->assertTrue($this->ra->get($key) === 'test1'); + $this->assertEquals('test1', $this->ra->get($key)); $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test2')->discard(); // Ret after discard: NULL"; - $this->assertTrue($ret === NULL); + $this->assertNull($ret); // Get after discard, unchanged: - $this->assertTrue($this->ra->get($key) === 'test1'); + $this->assertEquals('test1', $this->ra->get($key)); } } @@ -629,9 +629,9 @@ public function testInit() { } public function distribute($key) { - $matches = array(); + $matches = []; if (preg_match('/{([^}]+)}.*/', $key, $matches) == 1) { - $countries = array('uk' => 0, 'us' => 1); + $countries = ['uk' => 0, 'us' => 1]; if (array_key_exists($matches[1], $countries)) { return $countries[$matches[1]]; } @@ -646,10 +646,10 @@ public function testDistribution() { $defaultServer = $this->ra->_target('unknown'); $nodes = $this->ra->_hosts(); - $this->assertTrue($ukServer === $nodes[0]); - $this->assertTrue($usServer === $nodes[1]); - $this->assertTrue($deServer === $nodes[2]); - $this->assertTrue($defaultServer === $nodes[2]); + $this->assertEquals($ukServer, $nodes[0]); + $this->assertEquals($usServer,$nodes[1]); + $this->assertEquals($deServer,$nodes[2]); + $this->assertEquals($defaultServer, $nodes[2]); } } diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index df9c53c27b..f4f0392ff5 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -121,14 +121,14 @@ public function testRandomKey() { for ($i = 0; $i < 1000; $i++) { $k = $this->redis->randomKey("key:$i"); - $this->assertTrue($this->redis->exists($k)); + $this->assertEquals(1, $this->redis->exists($k)); } } public function testEcho() { - $this->assertEquals($this->redis->echo('echo1', 'hello'), 'hello'); - $this->assertEquals($this->redis->echo('echo2', 'world'), 'world'); - $this->assertEquals($this->redis->echo('echo3', " 0123 "), " 0123 "); + $this->assertEquals('hello', $this->redis->echo('echo1', 'hello')); + $this->assertEquals('world', $this->redis->echo('echo2', 'world')); + $this->assertEquals(' 0123 ', $this->redis->echo('echo3', " 0123 ")); } public function testSortPrefix() { @@ -138,7 +138,7 @@ public function testSortPrefix() { $this->redis->sadd('some-item', 2); $this->redis->sadd('some-item', 3); - $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item')); + $this->assertEquals(['1','2','3'], $this->redis->sort('some-item')); // Kill our set/prefix $this->redis->del('some-item'); @@ -291,7 +291,7 @@ public function testPubSub() { // Should get an array back, with two elements $this->assertTrue(is_array($result)); - $this->assertEquals(count($result), 4); + $this->assertEquals(4, count($result)); $arr_zipped = []; for ($i = 0; $i <= count($result) / 2; $i+=2) { @@ -302,7 +302,7 @@ public function testPubSub() { // 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); + $this->assertEquals(0, $result[$channel]); } // PUBSUB NUMPAT @@ -326,9 +326,9 @@ public function testMSetNX() { $this->redis->del('x'); $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']); $this->assertTrue(is_array($ret)); - $this->assertEquals(array_sum($ret),1); + $this->assertEquals(1, array_sum($ret)); - $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE + $this->assertFalse($this->redis->msetnx([])); // set ø → FALSE } /* Slowlog needs to take a key or [ip, port], to direct it to a node */ @@ -368,7 +368,7 @@ public function testFailedTransactions() { // This transaction should fail because the other client changed 'x' $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === [false]); + $this->assertEquals([false], $ret); // watch and unwatch $this->redis->watch('x'); $r->incr('x'); // other instance @@ -376,7 +376,7 @@ public function testFailedTransactions() { // This should succeed as the watch has been cancelled $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === array('44')); + $this->assertEquals(['44'], $ret); } public function testDiscard() @@ -439,9 +439,9 @@ public function testEvalSHA() { $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,[$str_key], 1)); - $this->assertTrue(1 === $this->redis->eval($scr,[$str_key], 1)); - $this->assertTrue(1 === $this->redis->evalsha($sha,[$str_key], 1)); + $this->assertFalse($this->redis->evalsha($scr,[$str_key], 1)); + $this->assertEquals(1, $this->redis->eval($scr,[$str_key], 1)); + $this->assertEquals(1, $this->redis->evalsha($sha,[$str_key], 1)); } public function testEvalBulkResponse() { @@ -455,8 +455,8 @@ public function testEvalBulkResponse() { $result = $this->redis->eval($scr,[$str_key1, $str_key2], 2); - $this->assertTrue($str_key1 === $result[0]); - $this->assertTrue($str_key2 === $result[1]); + $this->assertEquals($str_key1, $result[0]); + $this->assertEquals($str_key2, $result[1]); } public function testEvalBulkResponseMulti() { @@ -473,8 +473,8 @@ public function testEvalBulkResponseMulti() { $result = $this->redis->exec(); - $this->assertTrue($str_key1 === $result[0][0]); - $this->assertTrue($str_key2 === $result[0][1]); + $this->assertEquals($str_key1, $result[0][0]); + $this->assertEquals($str_key2, $result[0][1]); } public function testEvalBulkEmptyResponse() { @@ -488,7 +488,7 @@ public function testEvalBulkEmptyResponse() { $result = $this->redis->eval($scr, [$str_key1, $str_key2], 2); - $this->assertTrue(null === $result); + $this->assertNull($result); } public function testEvalBulkEmptyResponseMulti() { @@ -504,7 +504,7 @@ public function testEvalBulkEmptyResponseMulti() { $this->redis->eval($scr, [$str_key1, $str_key2], 2); $result = $this->redis->exec(); - $this->assertTrue(null === $result[0]); + $this->assertNull($result[0]); } /* Cluster specific introspection stuff */ @@ -513,9 +513,9 @@ public function testIntrospection() { $this->assertTrue(is_array($arr_masters)); foreach ($arr_masters as $arr_info) { - $this->assertTrue(is_array($arr_info)); - $this->assertTrue(is_string($arr_info[0])); - $this->assertTrue(is_long($arr_info[1])); + $this->assertIsArray($arr_info); + $this->assertIsString($arr_info[0]); + $this->assertIsInt($arr_info[1]); } } @@ -598,7 +598,7 @@ protected function checkZSetEquality($a, $b) { array_sum($a) != array_sum($b); if ($boo_diff) { - $this->assertEquals($a,$b); + $this->assertEquals($a, $b); return; } } @@ -657,11 +657,11 @@ public function testFailOver() { /* Test a 'raw' command */ public function testRawCommand() { $this->redis->rawCommand('mykey', 'set', 'mykey', 'my-value'); - $this->assertEquals($this->redis->get('mykey'), 'my-value'); + $this->assertEquals('my-value', $this->redis->get('mykey')); $this->redis->del('mylist'); $this->redis->rpush('mylist', 'A','B','C','D'); - $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']); + $this->assertEquals(['A','B','C','D'], $this->redis->lrange('mylist', 0, -1)); } protected function rawCommandArray($key, $args) { diff --git a/tests/RedisTest.php b/tests/RedisTest.php index cb1ecd4b9e..28e0a09c9b 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -3,8 +3,12 @@ require_once(dirname($_SERVER['PHP_SELF']).'/TestSuite.php'); require_once(dirname($_SERVER['PHP_SELF']).'/SessionHelpers.php'); -class Redis_Test extends TestSuite -{ +class Redis_Test extends TestSuite { + /** + * @var Redis + */ + public $redis; + /* City lat/long */ protected $cities = [ 'Chico' => [-121.837478, 39.728494], @@ -19,11 +23,6 @@ class Redis_Test extends TestSuite Redis::SERIALIZER_PHP, ]; - /** - * @var Redis - */ - public $redis; - protected function getNilValue() { return FALSE; } @@ -138,8 +137,7 @@ public function tearDown() { } } - public function reset() - { + public function reset() { $this->setUp(); $this->tearDown(); } @@ -153,8 +151,7 @@ protected function haveMulti() { return defined(get_class($this->redis) . '::MULTI'); } - public function testMinimumVersion() - { + public function testMinimumVersion() { // Minimum server version required for tests $this->assertTrue(version_compare($this->version, '2.4.0') >= 0); } @@ -182,17 +179,16 @@ public function testPipelinePublish() { ->publish('chan', 'msg') ->exec(); - $this->assertTrue(is_array($ret) && count($ret) === 1 && $ret[0] >= 0); + $this->assertIsArray($ret, 1); + $this->assertGT(-1, $ret[0] ?? -1); } // 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) { + if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } // PUBSUB CHANNELS ... $result = $this->redis->pubsub('channels', '*'); @@ -213,7 +209,7 @@ public function testPubSub() { // Make sure the elements are correct, and have zero counts foreach([$c1,$c2] as $channel) { - $this->assertArrayKey($result, $channel, function($v) { return $v === 0; }); + $this->assertArrayKeyEquals($result, $channel, 0); } // PUBSUB NUMPAT @@ -336,9 +332,8 @@ public function testLcs() { } public function testLmpop() { - if(version_compare($this->version, '7.0.0') < 0) { + if(version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $key1 = '{l}1'; $key2 = '{l}2'; @@ -356,9 +351,8 @@ public function testLmpop() { } public function testBLmpop() { - if(version_compare($this->version, '7.0.0') < 0) { + if(version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $key1 = '{bl}1'; $key2 = '{bl}2'; @@ -375,13 +369,14 @@ public function testBLmpop() { $st = microtime(true); $this->assertFalse($this->redis->blmpop(.2, [$key1, $key2], 'LEFT')); $et = microtime(true); - $this->assertTrue($et - $st >= .2); + + // Very loose tolerance because CI is run on a potato + $this->assertBetween($et - $st, .05, .75); } function testZmpop() { - if(version_compare($this->version, '7.0.0') < 0) { + if(version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $key1 = '{z}1'; $key2 = '{z}2'; @@ -408,9 +403,8 @@ function testZmpop() { } function testBZmpop() { - if(version_compare($this->version, '7.0.0') < 0) { + if(version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $key1 = '{z}1'; $key2 = '{z}2'; @@ -431,7 +425,8 @@ function testBZmpop() { $st = microtime(true); $this->assertFalse($this->redis->bzmpop(.2, [$key1, $key2], 'MIN')); $et = microtime(true); - $this->assertTrue($et - $st >= .2); + + $this->assertBetween($et - $st, .05, .75); } public function testBitPos() { @@ -485,8 +480,7 @@ public function testErr() { } - public function testSet() - { + public function testSet() { $this->assertTrue($this->redis->set('key', 'nil')); $this->assertEquals('nil', $this->redis->get('key')); @@ -500,7 +494,7 @@ public function testSet() $this->redis->set('key2', 'val'); $this->assertEquals('val', $this->redis->get('key2')); - $value = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + $value = str_repeat('A', 128); $this->redis->set('key2', $value); $this->assertEquals($value, $this->redis->get('key2')); @@ -555,10 +549,8 @@ public function testSet() /* 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) { + if (version_compare($this->version, '2.6.12') < 0) $this->markTestSkipped(); - return; - } /* Legacy SETEX redirection */ $this->redis->del('foo'); @@ -588,28 +580,28 @@ public function testExtendedSet() { $this->assertFalse($this->redis->set('foo','bar', ['xx'])); /* Set with a TTL */ - $this->assertTrue($this->redis->set('foo','bar', ['ex'=>100])); + $this->assertTrue($this->redis->set('foo','bar', ['ex' => 100])); $this->assertEquals(100, $this->redis->ttl('foo')); /* Set with a PTTL */ - $this->assertTrue($this->redis->set('foo','bar',['px'=>100000])); - $this->assertTrue(100000 - $this->redis->pttl('foo') < 1000); + $this->assertTrue($this->redis->set('foo','bar', ['px' => 100000])); + $this->assertBetween($this->redis->pttl('foo'), 99000, 100001); /* Set if exists, with a TTL */ - $this->assertTrue($this->redis->set('foo','bar',['xx','ex'=>105])); + $this->assertTrue($this->redis->set('foo','bar', ['xx','ex' => 105])); $this->assertEquals(105, $this->redis->ttl('foo')); $this->assertEquals('bar', $this->redis->get('foo')); /* Set if not exists, with a TTL */ $this->redis->del('foo'); - $this->assertTrue($this->redis->set('foo','bar', ['nx', 'ex'=>110])); + $this->assertTrue($this->redis->set('foo','bar', ['nx', 'ex' => 110])); $this->assertEquals(110, $this->redis->ttl('foo')); $this->assertEquals('bar', $this->redis->get('foo')); - $this->assertFalse($this->redis->set('foo','bar', ['nx', 'ex'=>110])); + $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->assertTrue($this->redis->set('foo','barbaz', ['not-valid', 'nx', 'invalid', 'ex' => 200])); $this->assertEquals(200, $this->redis->ttl('foo')); $this->assertEquals('barbaz', $this->redis->get('foo')); @@ -630,13 +622,13 @@ public function testExtendedSet() { /* KEEPTTL works by itself */ $this->redis->set('foo', 'bar', ['EX' => 100]); $this->redis->set('foo', 'bar', ['KEEPTTL']); - $this->assertTrue($this->redis->ttl('foo') > -1); + $this->assertBetween($this->redis->ttl('foo'), 90, 100); /* Works with other options */ $this->redis->set('foo', 'bar', ['XX', 'KEEPTTL']); - $this->assertTrue($this->redis->ttl('foo') > -1); + $this->assertBetween($this->redis->ttl('foo'), 90, 100); $this->redis->set('foo', 'bar', ['XX']); - $this->assertTrue($this->redis->ttl('foo') == -1); + $this->assertEquals(-1, $this->redis->ttl('foo')); if (version_compare($this->version, '6.2.0') < 0) return; @@ -727,7 +719,8 @@ public function testMultipleBin() { $this->redis->set($k, $v); } - $this->assertEquals(array_values($kvals), $this->redis->mget(array_keys($kvals))); + $this->assertEquals(array_values($kvals), + $this->redis->mget(array_keys($kvals))); } public function testSetTimeout() { @@ -740,7 +733,8 @@ public function testSetTimeout() { $this->assertFalse($this->redis->get('key')); } - /* This test is prone to failure in the Travis container, so attempt to mitigate this by running more than once */ + /* 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; @@ -789,9 +783,8 @@ function testExpireOptions() { } public function testExpiretime() { - if(version_compare($this->version, '7.0.0') < 0) { + if(version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $now = time(); @@ -830,17 +823,16 @@ public function testSetNX() { } public function testExpireAtWithLong() { - if (PHP_INT_SIZE != 8) { + if (PHP_INT_SIZE != 8) $this->markTestSkipped('64 bits only'); - } - $longExpiryTimeExceedingInt = 3153600000; + + $large_expiry = 3153600000; $this->redis->del('key'); - $this->assertTrue($this->redis->setex('key', $longExpiryTimeExceedingInt, 'val')); - $this->assertEquals($longExpiryTimeExceedingInt, $this->redis->ttl('key')); + $this->assertTrue($this->redis->setex('key', $large_expiry, 'val')); + $this->assertEquals($large_expiry, $this->redis->ttl('key')); } - public function testIncr() - { + public function testIncr() { $this->redis->set('key', 0); $this->redis->incr('key'); @@ -875,12 +867,10 @@ public function testIncr() $this->assertEquals(PHP_INT_MAX, $this->redis->incrby('key', PHP_INT_MAX)); } - public function testIncrByFloat() - { + public function testIncrByFloat() { // incrbyfloat is new in 2.6.0 - if (version_compare($this->version, '2.5.0') < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } $this->redis->del('key'); @@ -913,8 +903,7 @@ public function testIncrByFloat() $this->redis->del('someprefix:key'); } - public function testDecr() - { + public function testDecr() { $this->redis->set('key', 5); $this->redis->decr('key'); @@ -966,9 +955,9 @@ public function testTouch() { $this->redis->del('notakey'); $this->assertTrue($this->redis->mset(['{idle}1' => 'beep', '{idle}2' => 'boop'])); - usleep(2100000); - $this->assertTrue($this->redis->object('idletime', '{idle}1') >= 2); - $this->assertTrue($this->redis->object('idletime', '{idle}2') >= 2); + usleep(1100000); + $this->assertGT(0, $this->redis->object('idletime', '{idle}1')); + $this->assertGT(0, $this->redis->object('idletime', '{idle}2')); $this->assertEquals(2, $this->redis->touch('{idle}1', '{idle}2', '{idle}notakey')); $idle1 = $this->redis->object('idletime', '{idle}1'); @@ -976,12 +965,11 @@ public function testTouch() { /* We're not testing if idle is 0 because CPU scheduling on GitHub CI * potatoes can cause that to erroneously fail. */ - $this->assertTrue($idle1 < 2); - $this->assertTrue($idle2 < 2); + $this->assertLT(2, $idle1); + $this->assertLT(2, $idle2); } - public function testKeys() - { + public function testKeys() { $pattern = 'keys-test-'; for($i = 1; $i < 10; $i++) { $this->redis->set($pattern.$i, $i); @@ -1036,24 +1024,13 @@ public function testDelete() { } public function testUnlink() { - if (version_compare($this->version, '4.0.0') < 0) { + 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 - + public function testType() { // string $this->redis->set('key', 'val'); $this->assertEquals(Redis::REDIS_STRING, $this->redis->type('key')); @@ -1100,7 +1077,6 @@ public function testType() } public function testStr() { - $this->redis->set('key', 'val1'); $this->assertEquals(8, $this->redis->append('key', 'val2')); $this->assertEquals('val1val2', $this->redis->get('key')); @@ -1126,47 +1102,34 @@ public function testStr() { $this->assertEquals(3, $this->redis->strlen('key')); } - // PUSH, POP : LPUSH, LPOP - public function testlPop() - { - - // rpush => tail - // lpush => head - - + public function testlPop() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); - $this->redis->rPush('list', 'val3'); - - - // 'list' = [ 'val2', 'val', 'val3'] + $this->redis->rPush('list', 'val3'); - $this->assertEquals('val2', $this->redis->lPop('list')); + $this->assertEquals('val2', $this->redis->lPop('list')); if (version_compare($this->version, '6.2.0') < 0) { $this->assertEquals('val', $this->redis->lPop('list')); $this->assertEquals('val3', $this->redis->lPop('list')); } else { $this->assertEquals(['val', 'val3'], $this->redis->lPop('list', 2)); } - $this->assertFalse($this->redis->lPop('list')); - // testing binary data + $this->assertFalse($this->redis->lPop('list')); - $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->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'))); + $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() - { + public function testrPop() { $this->redis->del('list'); $this->redis->rPush('list', 'val'); @@ -1180,8 +1143,8 @@ public function testrPop() } else { $this->assertEquals(['val', 'val3'], $this->redis->rPop('list', 2)); } - $this->assertFalse($this->redis->rPop('list')); + $this->assertFalse($this->redis->rPop('list')); $this->redis->del('list'); $this->assertEquals(1, $this->redis->rPush('list', gzcompress('val1'))); @@ -1240,8 +1203,7 @@ public function testblockingPop() { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } - public function testllen() - { + public function testllen() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); @@ -1266,9 +1228,7 @@ public function testllen() $this->assertFalse($this->redis->llen('list'));// not a list returns FALSE } - //lInsert, lPopx, rPopx public function testlPopx() { - //test lPushx/rPushx $this->redis->del('keyNotExists'); $this->assertEquals(0, $this->redis->lPushx('keyNotExists', 'value')); $this->assertEquals(0, $this->redis->rPushx('keyNotExists', 'value')); @@ -1290,8 +1250,7 @@ public function testlPopx() { $this->assertEquals(['val2', 'val0', 'val1'], $this->redis->lrange('key', 0, -1)); } - public function testlPos() - { + public function testlPos() { $this->redis->del('key'); $this->redis->lPush('key', 'val0', 'val1', 'val1'); $this->assertEquals(2, $this->redis->lPos('key', 'val0')); @@ -1309,9 +1268,7 @@ public function testlPos() } // ltrim, lsize, lpop - public function testltrim() - { - + public function testltrim() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); @@ -1319,20 +1276,19 @@ public function testltrim() $this->redis->lPush('list', 'val3'); $this->redis->lPush('list', 'val4'); - $this->assertTrue($this->redis->ltrim('list', 0, 2)); - $this->assertEquals(3, $this->redis->llen('list')); + $this->assertTrue($this->redis->ltrim('list', 0, 2)); + $this->assertEquals(3, $this->redis->llen('list')); $this->redis->ltrim('list', 0, 0); $this->assertEquals(1, $this->redis->llen('list')); - $this->assertEquals('val4', $this->redis->lPop('list')); + $this->assertEquals('val4', $this->redis->lPop('list')); - $this->assertTrue($this->redis->ltrim('list', 10, 10000)); - $this->assertTrue($this->redis->ltrim('list', 10000, 10)); - - // test invalid type - $this->redis->set('list', 'not a list...'); - $this->assertFalse($this->redis->ltrim('list', 0, 2)); + $this->assertTrue($this->redis->ltrim('list', 10, 10000)); + $this->assertTrue($this->redis->ltrim('list', 10000, 10)); + // test invalid type + $this->redis->set('list', 'not a list...'); + $this->assertFalse($this->redis->ltrim('list', 0, 2)); } public function setupSort() { @@ -1433,7 +1389,6 @@ public function testSortAsc() { } public function testSortDesc() { - $this->setupSort(); // sort by age and get IDs @@ -1476,9 +1431,7 @@ public function testSortHandler() { } } - // LINDEX public function testLindex() { - $this->redis->del('list'); $this->redis->lPush('list', 'val'); @@ -1502,19 +1455,23 @@ public function testlMove() { if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - $this->redis->del('{list}0', '{list}1'); - $this->redis->lPush('{list}0', 'a'); - $this->redis->lPush('{list}0', 'b'); - $this->redis->lPush('{list}0', 'c'); + [$k1, $k2] = ['{l}0', '{l}1']; + $left = $this->getLeftConstant(); + $right = $this->getRightConstant(); - $return = $this->redis->lMove('{list}0', '{list}1', $this->getLeftConstant(), $this->getRightConstant()); + $this->redis->del($k1, $k2); + $this->redis->lPush($k1, 'a'); + $this->redis->lPush($k1, 'b'); + $this->redis->lPush($k1, 'c'); + + $return = $this->redis->lMove($k1, $k2, $left, $right); $this->assertEquals('c', $return); - $return = $this->redis->lMove('{list}0', '{list}1', $this->getRightConstant(), $this->getLeftConstant()); + $return = $this->redis->lMove($k1, $k2, $right, $left); $this->assertEquals('a', $return); - $this->assertEquals(['b'], $this->redis->lRange('{list}0', 0, -1)); - $this->assertEquals(['a', 'c'], $this->redis->lRange('{list}1', 0, -1)); + $this->assertEquals(['b'], $this->redis->lRange($k1, 0, -1)); + $this->assertEquals(['a', 'c'], $this->redis->lRange($k2, 0, -1)); } @@ -1522,17 +1479,21 @@ public function testBlmove() { if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - $this->redis->del('{list}0', '{list}1'); - $this->redis->rpush('{list}0', 'a'); + [$k1, $k2] = ['{l}0', '{l}1']; + $left = $this->getLeftConstant(); + + $this->redis->del($k1, $k2); + $this->redis->rpush($k1, 'a'); - $this->assertEquals('a', $this->redis->blmove('{list}0', '{list}1', $this->getLeftConstant(), $this->getLeftConstant(), 1.0)); + + $this->assertEquals('a', $this->redis->blmove($k1, $k2, $left, $left, 1.)); $st = microtime(true); - $ret = $this->redis->blmove('{list}0', '{list}1', $this->getLeftConstant(), $this->getLeftConstant(), .1); + $ret = $this->redis->blmove($k1, $k2, $left, $left, .1); $et = microtime(true); $this->assertFalse($ret); - $this->assertTrue($et - $st >= .1); + $this->assertGT(.09, $et - $st); } // lRem testing @@ -1650,9 +1611,8 @@ public function testsPop() { } public function testsPopWithCount() { - if (!$this->minVersionCheck('3.2')) { + if (!$this->minVersionCheck('3.2')) $this->markTestSkipped(); - } $set = 'set0'; $prefix = 'member'; @@ -1739,19 +1699,19 @@ public function testSRandMemberWithCount() { $ret_slice = $this->redis->srandmember('set0', 20); // Should be an array with 20 items - $this->assertTrue(is_array($ret_slice) && count($ret_slice) == 20); + $this->assertIsArray($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); + $this->assertIsArray($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); + $this->assertIsArray($ret_slice, 200); // // Test in a pipeline @@ -1766,17 +1726,16 @@ public function testSRandMemberWithCount() { $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); + $this->assertIsArray($ret[0], 20); + $this->assertIsArray($ret[1], $i); + $this->assertIsArray($ret[2], 200); // Kill the set $this->redis->del('set0'); } } - public function testsismember() - { + public function testsismember() { $this->redis->del('set'); $this->redis->sAdd('set', 'val'); @@ -1796,13 +1755,9 @@ public function testsmembers() { $this->assertEqualsCanonicalizing($data, $this->redis->smembers('set')); } - public function testsMisMember() - { - // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + public function testsMisMember() { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('set'); @@ -1818,22 +1773,20 @@ public function testsMisMember() } public function testlSet() { - $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->redis->lPush('list', 'val3'); - $this->assertTrue($this->redis->lSet('list', 1, 'valx')); + $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('val3', $this->redis->lIndex('list', 0)); - $this->assertEquals('valx', $this->redis->lIndex('list', 1)); - $this->assertEquals('val', $this->redis->lIndex('list', 2)); + $this->assertTrue($this->redis->lSet('list', 1, 'valx')); + $this->assertEquals('val3', $this->redis->lIndex('list', 0)); + $this->assertEquals('valx', $this->redis->lIndex('list', 1)); + $this->assertEquals('val', $this->redis->lIndex('list', 2)); } public function testsInter() { @@ -2204,9 +2157,8 @@ public function testsDiffStore() { } public function testInterCard() { - if(version_compare($this->version, '7.0.0') < 0) { + if(version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $set_data = [ ['aardvark', 'dog', 'fish', 'squirrel', 'tiger'], @@ -2292,7 +2244,7 @@ public function testttl() { $this->redis->set('x', 'y'); $this->redis->expire('x', 5); $ttl = $this->redis->ttl('x'); - $this->assertTrue($ttl > 0 && $ttl <= 5); + $this->assertBetween($ttl, 1, 5); // A key with no TTL $this->redis->del('x'); $this->redis->set('x', 'bar'); @@ -2320,25 +2272,25 @@ public function testClient() { $this->assertTrue($this->redis->client('setname', 'phpredis_unit_tests')); /* CLIENT LIST */ - $arr_clients = $this->redis->client('list'); - $this->assertIsArray($arr_clients); + $clients = $this->redis->client('list'); + $this->assertIsArray($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']; + $address = NULL; + foreach($clients as $cleint) { + if ($cleint['name'] == 'phpredis_unit_tests') { + $address = $cleint['addr']; } } // We should have found our connection - $this->assertFalse(empty($str_addr)); + $this->assertIsString($address); /* CLIENT GETNAME */ - $this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests'); + $this->assertEquals('phpredis_unit_tests', $this->redis->client('getname')); if (version_compare($this->version, '5.0.0') >= 0) { - $this->assertLess(0, $this->redis->client('id')); + $this->assertGT(0, $this->redis->client('id')); if (version_compare($this->version, '6.0.0') >= 0) { $this->assertEquals(-1, $this->redis->client('getredir')); $this->assertTrue($this->redis->client('tracking', 'on', ['optin' => true])); @@ -2347,11 +2299,12 @@ public function testClient() { $this->assertTrue($this->redis->client('tracking', 'off')); if (version_compare($this->version, '6.2.0') >= 0) { $this->assertFalse(empty($this->redis->client('info'))); - $this->assertEquals($this->redis->client('trackinginfo'), [ + $this->assertEquals([ 'flags' => ['off'], 'redirect' => -1, 'prefixes' => [], - ]); + ], $this->redis->client('trackinginfo')); + if (version_compare($this->version, '7.0.0') >= 0) { $this->assertTrue($this->redis->client('no-evict', 'on')); } @@ -2360,7 +2313,7 @@ public function testClient() { } /* CLIENT KILL -- phpredis will reconnect, so we can do this */ - $this->assertTrue($this->redis->client('kill', $str_addr)); + $this->assertTrue($this->redis->client('kill', $address)); } @@ -2369,17 +2322,15 @@ public function testSlowlog() { // the command returns proper types when called in various ways $this->assertIsArray($this->redis->slowlog('get')); $this->assertIsArray($this->redis->slowlog('get', 10)); - $this->assertTrue(is_int($this->redis->slowlog('len'))); + $this->assertIsInt($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 commit history - if(version_compare($this->version, '2.9.11') < 0) { + 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(); @@ -2395,11 +2346,11 @@ public function testWait() { // 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+1, 100) < $i_slaves+1); + $this->assertLT($i_slaves + 1, $this->redis->wait($i_slaves+1, 100)); // 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(-1, 20)); + $this->assertEquals(0, $this->redis->wait(-1, 20)); } public function testInfo() { @@ -2456,11 +2407,9 @@ public function testInfo() { } public function testInfoCommandStats() { - - // INFO COMMANDSTATS is new in 2.6.0 - if (version_compare($this->version, '2.5.0') < 0) { + // INFO COMMANDSTATS is new in 2.6.0 + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } $info = $this->redis->info('COMMANDSTATS'); if ( ! $this->assertIsArray($info)) @@ -2477,9 +2426,8 @@ public function testSelect() { } public function testSwapDB() { - if (version_compare($this->version, '4.0.0') < 0) { + if (version_compare($this->version, '4.0.0') < 0) $this->markTestSkipped(); - } $this->assertTrue($this->redis->swapdb(0, 1)); $this->assertTrue($this->redis->swapdb(0, 1)); @@ -2577,11 +2525,10 @@ public function testBRpopLpush() { $st = microtime(true); $this->assertFalse($this->redis->brpoplpush('{list}x', '{list}y', .1)); $et = microtime(true); - $this->assertLess($et - $st, 1.0); + $this->assertLT(1.0, $et - $st); } public function testZAddFirstArg() { - $this->redis->del('key'); $zsetName = 100; // not a string! @@ -2841,9 +2788,9 @@ public function testZX() { $this->assertEquals(['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->assertArrayKeyEquals($ret, 'one', 2000.1); + $this->assertArrayKeyEquals($ret, 'two', 3000.1); + $this->assertArrayKeyEquals($ret, 'three', 4000.1); $this->redis->del('{zset}1'); @@ -2852,7 +2799,7 @@ public function testZX() { $this->redis->zAdd('{zset}1', 2, 'two'); $this->redis->zAdd('{zset}1', 3, 'three'); $this->assertEquals(2, $this->redis->zremrangebyrank('{zset}1', 0, 1)); - $this->assertTrue(['three' => 3] == $this->redis->zRange('{zset}1', 0, -1, TRUE)); + $this->assertEquals(['three' => 3.], $this->redis->zRange('{zset}1', 0, -1, TRUE)); $this->redis->del('{zset}1'); @@ -3000,13 +2947,10 @@ public function testZLexCount() { } } - public function testzDiff() - { + public function testzDiff() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('key'); foreach (range('a', 'c') as $c) { @@ -3017,13 +2961,10 @@ public function testzDiff() $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zDiff(['key'], ['withscores' => true])); } - public function testzInter() - { + public function testzInter() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('key'); foreach (range('a', 'c') as $c) { @@ -3034,13 +2975,10 @@ public function testzInter() $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zInter(['key'], null, ['withscores' => true])); } - public function testzUnion() - { + public function testzUnion() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('key'); foreach (range('a', 'c') as $c) { @@ -3051,13 +2989,10 @@ public function testzUnion() $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zUnion(['key'], null, ['withscores' => true])); } - public function testzDiffStore() - { + public function testzDiffStore() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('{zkey}src'); foreach (range('a', 'c') as $c) { @@ -3067,13 +3002,10 @@ public function testzDiffStore() $this->assertEquals(['a', 'b', 'c'], $this->redis->zRange('{zkey}dst', 0, -1)); } - public function testzMscore() - { + public function testzMscore() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('key'); foreach (range('a', 'c') as $c) { @@ -3125,7 +3057,7 @@ public function testBZPop() { $st = microtime(true) * 1000; $this->redis->bzPopMin('{zs}1', '{zs}2', 1); $et = microtime(true) * 1000; - $this->assertTrue($et - $st > 100); + $this->assertGT(100, $et - $st); } public function testZPop() { @@ -3151,8 +3083,7 @@ public function testZPop() { $this->assertEquals(['a' => 0.0, 'b' => 1.0, 'c' => 2.0], $this->redis->zPopMin('key', 3)); } - public function testZRandMember() - { + public function testZRandMember() { if (version_compare($this->version, '6.2.0') < 0) { $this->MarkTestSkipped(); return; @@ -3306,12 +3237,10 @@ public function testHashes() { } } - public function testHRandField() - { - if (version_compare($this->version, '6.2.0') < 0) { + public function testHRandField() { + if (version_compare($this->version, '6.2.0') < 0) $this->MarkTestSkipped(); - return; - } + $this->redis->del('key'); $this->redis->hMSet('key', ['a' => 0, 'b' => 1, 'c' => 'foo', 'd' => 'bar', 'e' => null]); $this->assertInArray($this->redis->hRandField('key'), ['a', 'b', 'c', 'd', 'e']); @@ -3335,8 +3264,6 @@ public function testSetRange() { $this->assertEquals('hello youis', $this->redis->get('key')); $this->redis->set('key', 'hello world'); - // $this->assertEquals(11, $this->redis->setRange('key', -6, 'redis')); // works with negative offsets too! (disabled because not all versions support this) - // $this->assertEquals('hello redis', $this->redis->get('key')); // fill with zeros if needed $this->redis->del('key'); @@ -3443,9 +3370,8 @@ public function testFailedTransactions() { } public function testPipeline() { - if (!$this->havePipeline()) { + if (!$this->havePipeline()) $this->markTestSkipped(); - } $this->sequence(Redis::PIPELINE); $this->differentType(Redis::PIPELINE); @@ -3458,9 +3384,8 @@ public function testPipeline() { } public function testPipelineMultiExec() { - if (!$this->havePipeline()) { + if (!$this->havePipeline()) $this->markTestSkipped(); - } $ret = $this->redis->pipeline()->multi()->exec()->exec(); $this->assertIsArray($ret); @@ -3502,8 +3427,7 @@ public function testDoublePipeNoOp() { $this->assertEquals([true, 'over9000'], $data); } - public function testDiscard() - { + public function testDiscard() { foreach ([Redis::PIPELINE, Redis::MULTI] as $mode) { /* start transaction */ $this->redis->multi($mode); @@ -3665,7 +3589,7 @@ protected function sequence($mode) { $this->assertEquals('lvalue', $ret[$i++]); // this is the current head. $this->assertEquals(['lvalue'], $ret[$i++]); // this is the current list. $this->assertFalse($ret[$i++]); // updating a non-existent element fails. - $this->assertTrue(['lvalue'], $ret[$i++]); // this is the current list. + $this->assertEquals(['lvalue'], $ret[$i++]); // this is the current list. $this->assertEquals(1, $ret[$i++]); // 1 element left $this->assertEquals($i, count($ret)); @@ -3679,8 +3603,10 @@ protected function sequence($mode) { ->lpop('{list}lkey') ->exec(); $this->assertIsArray($ret); + $i = 0; - $this->assertTrue($ret[$i++] <= 2); // deleted 0, 1, or 2 items + + $this->assertLTE(2, $ret[$i++]); // deleting 2 keys $this->assertEquals(1, $ret[$i++]); // 1 element in the list $this->assertEquals(2, $ret[$i++]); // 2 elements in the list $this->assertEquals(3, $ret[$i++]); // 3 elements in the list @@ -3716,7 +3642,7 @@ protected function sequence($mode) { $i = 0; $this->assertIsArray($ret); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] <= 1); $i++; + $this->assertLTE(1, $ret[$i++]); $this->assertEqualsWeak(true, $ret[$i++]); $this->assertEquals('value1', $ret[$i++]); $this->assertEquals('value1', $ret[$i++]); @@ -3730,10 +3656,10 @@ protected function sequence($mode) { $this->assertEqualsWeak(4, $ret[$i++]); $this->assertFalse($ret[$i++]); $this->assertTrue($ret[$i++]); - $this->assertTrue($ret[$i++]); - $this->assertEqualsWeak(9, $ret[$i++]); - $this->assertTrue($ret[$i++]); - $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEquals(9, $ret[$i++]); // incrby('{key}2', 5) + $this->assertEqualsWeak(9, $ret[$i++]); // get('{key}2') + $this->assertEquals(4, $ret[$i++]); // decrby('{key}2', 5) + $this->assertEqualsWeak(4, $ret[$i++]); // get('{key}2') $this->assertTrue($ret[$i++]); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); @@ -3749,14 +3675,14 @@ protected function sequence($mode) { ->exec(); $this->assertIsArray($ret); - $this->assertTrue($ret[0]); - $this->assertTrue($ret[1]); - $this->assertTrue($ret[2]); - $this->assertTrue($ret[3]); - $this->assertFalse($ret[4]); - $this->assertTrue($ret[5]); - $this->assertTrue($ret[6]); - $this->assertFalse($ret[7]); + $this->assertEquals(1, $ret[0]); // del('{key}1') + $this->assertEquals(1, $ret[1]); // del('{key}2') + $this->assertEquals(1, $ret[2]); // del('{key}3') + $this->assertTrue($ret[3]); // set('{key}1', 'val1') + $this->assertFalse($ret[4]); // setnx('{key}1', 'valX') + $this->assertTrue($ret[5]); // setnx('{key}2', 'valX') + $this->assertEquals(1, $ret[6]); // exists('{key}1') + $this->assertEquals(0, $ret[7]); // exists('{key}3') // ttl, mget, mset, msetnx, expire, expireAt $ret = $this->redis->multi($mode) @@ -3771,8 +3697,8 @@ protected function sequence($mode) { $i = 0; $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i++])); - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 3); // mget - $i++; + $this->assertIsArray($ret[$i++], 3); +// $i++; $this->assertTrue($ret[$i++]); // mset always returns TRUE $this->assertTrue($ret[$i++]); // set always returns TRUE $this->assertTrue($ret[$i++]); // expire always returns TRUE @@ -3804,8 +3730,7 @@ protected function sequence($mode) { $this->assertIsArray($ret); $i = 0; - $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // del - $i++; + $this->assertBetween($ret[$i++], 0, 2); // del $this->assertEquals(1, $ret[$i++]); // 1 value $this->assertEquals(2, $ret[$i++]); // 2 values $this->assertEquals(3, $ret[$i++]); // 3 values @@ -3858,16 +3783,16 @@ protected function sequence($mode) { $i = 0; $this->assertIsArray($ret); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleted at most 5 values. - $this->assertEquals(1, $ret[$i++]); // skey1 now has 1 element. - $this->assertEquals(1, $ret[$i++]); // skey1 now has 2 elements. - $this->assertEquals(1, $ret[$i++]); // skey1 now has 3 elements. - $this->assertEquals(1, $ret[$i++]); // skey1 now has 4 elements. - $this->assertEquals(1, $ret[$i++]); // skey2 now has 1 element. - $this->assertEquals(1, $ret[$i++]); // skey2 now has 2 elements. + $this->assertBetween($ret[$i++], 0, 5); // we deleted at most 5 values. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 1 element. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 2 elements. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 3 elements. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 4 elements. + $this->assertEquals(1, $ret[$i++]); // skey2 now has 1 element. + $this->assertEquals(1, $ret[$i++]); // skey2 now has 2 elements. $this->assertEquals(4, $ret[$i++]); - $this->assertEquals(1, $ret[$i++]); // we did remove that value. - $this->assertEquals(3, $ret[$i++]); // now 3 values only. + $this->assertEquals(1, $ret[$i++]); // we did remove that value. + $this->assertEquals(3, $ret[$i++]); // now 3 values only. $this->assertTrue($ret[$i++]); // the move did succeed. $this->assertEquals(3, $ret[$i++]); // sKey2 now has 3 values. @@ -3940,7 +3865,7 @@ protected function sequence($mode) { $i = 0; $this->assertIsArray($ret); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleting at most 5 keys + $this->assertBetween($ret[$i++], 0, 5); // we deleted at most 5 values. $this->assertEquals(1, $ret[$i++]); $this->assertEquals(1, $ret[$i++]); $this->assertEquals(1, $ret[$i++]); @@ -3995,7 +3920,7 @@ protected function sequence($mode) { $i = 0; $this->assertIsArray($ret); - $this->assertTrue($ret[$i++] <= 1); // delete + $this->assertLT(2, $ret[$i++]); // delete $this->assertEquals(1, $ret[$i++]); // added 1 element $this->assertEquals(1, $ret[$i++]); // added 1 element $this->assertEquals(1, $ret[$i++]); // added 1 element @@ -4005,9 +3930,9 @@ protected function sequence($mode) { $this->assertEquals(1, $ret[$i++]); // hdel succeeded $this->assertEquals(0, $ret[$i++]); // hdel failed $this->assertFalse($ret[$i++]); // hexists didn't find the deleted key - $this->assertTrue(['key3', 'key1'], $ret[$i], ['key1', 'key3'] || $ret[$i]); $i++; // hkeys - $this->assertTrue(['value3', 'value1'], $ret[$i], ['value1', 'value3'] || $ret[$i]); $i++; // hvals - $this->assertTrue(['key3' => 'value3', 'key1' => 'value1'], $ret[$i], ['key1' => 'value1', 'key3' => 'value3'] || $ret[$i]); $i++; // hgetall + $this->assertEqualsCanonicalizing(['key1', 'key3'], $ret[$i++]); // hkeys + $this->assertEqualsCanonicalizing(['value1', 'value3'], $ret[$i++]); // hvals + $this->assertEqualsCanonicalizing(['key1' => 'value1', 'key3' => 'value3'], $ret[$i++]); // hgetall $this->assertEquals(1, $ret[$i++]); // added 1 element $this->assertEquals(1, $ret[$i++]); // added the element, so 1. $this->assertEquals('non-string', $ret[$i++]); // hset succeeded @@ -4020,7 +3945,7 @@ protected function sequence($mode) { ->exec(); $i = 0; $this->assertIsArray($ret); - $this->assertTrue($ret[$i++] <= 1); // delete + $this->assertLTE(1, $ret[$i++]); // delete $this->assertTrue($ret[$i++]); // added 1 element $this->assertEquals('xyz', $ret[$i++]); $this->assertEquals($i, count($ret)); @@ -4951,9 +4876,9 @@ public function testSerializerIGBinary() { $this->checkSerializer(Redis::SERIALIZER_IGBINARY); // with prefix - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->checkSerializer(Redis::SERIALIZER_IGBINARY); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); /* Test our igbinary header check logic. The check allows us to do simple INCR type operations even with the serializer enabled, and @@ -4983,20 +4908,19 @@ public function testSerializerMsgPack() { $this->checkSerializer(Redis::SERIALIZER_MSGPACK); // with prefix - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->checkSerializer(Redis::SERIALIZER_MSGPACK); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } } - public function testSerializerJSON() - { + public function testSerializerJSON() { $this->checkSerializer(Redis::SERIALIZER_JSON); // with prefix - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->checkSerializer(Redis::SERIALIZER_JSON); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } private function checkSerializer($mode) { @@ -5016,7 +4940,7 @@ private function checkSerializer($mode) { $this->redis->rPush('key', $a[3]); // lrange - $this->assertEquals($this->redis->lrange('key', 0, -1), $a); + $this->assertEquals($a, $this->redis->lrange('key', 0, -1)); // lIndex $this->assertEquals($a[0], $this->redis->lIndex('key', 0)); @@ -5038,7 +4962,7 @@ private function checkSerializer($mode) { $this->assertEquals(5, $this->redis->lInsert('key', Redis::AFTER, $a[0], [4,5,6])); $a = [[1,2,3], $a[0], [4,5,6], $a[1], $a[2]]; - $this->assertEquals($this->redis->lrange('key', 0, -1), $a); + $this->assertEquals($a, $this->redis->lrange('key', 0, -1)); // sAdd $this->redis->del('{set}key'); @@ -5104,7 +5028,7 @@ private function checkSerializer($mode) { $this->assertEquals(['b' => 1.0], $this->redis->zRange('k', 0, -1, true)); // zRange - $this->assertEquals($this->redis->zRange('key', 0, -1), $z); + $this->assertEquals($z, $this->redis->zRange('key', 0, -1)); // zScore $this->assertEquals(0.0, $this->redis->zScore('key', $z[0])); @@ -5148,11 +5072,11 @@ private function checkSerializer($mode) { // hGet foreach($a as $k => $v) { - $this->assertEquals($this->redis->hGet('hash', $k), $v); + $this->assertEquals($v, $this->redis->hGet('hash', $k)); } // hGetAll - $this->assertEquals($this->redis->hGetAll('hash'), $a); + $this->assertEquals($a, $this->redis->hGetAll('hash')); $this->assertTrue($this->redis->hExists('hash', 'f0')); $this->assertTrue($this->redis->hExists('hash', 'f1')); $this->assertTrue($this->redis->hExists('hash', 'f2')); @@ -5163,7 +5087,7 @@ private function checkSerializer($mode) { $this->redis->del('hash'); $this->redis->hMSet('hash', $a); foreach($a as $k => $v) { - $this->assertEquals($this->redis->hGet('hash', $k), $v); + $this->assertEquals($v, $this->redis->hGet('hash', $k)); } // hMget @@ -5190,8 +5114,7 @@ private function checkSerializer($mode) { $this->sequence(Redis::MULTI); } - // TODO: Re enable this before merging into develop - // $this->assertTrue(is_array($this->redis->keys('*'))); + $this->assertIsArray($this->redis->keys('*')); // issue #62, hgetall $this->redis->del('hash1'); @@ -5224,11 +5147,9 @@ private function checkSerializer($mode) { // $this->assertFalse(@$this->redis->zRem('key')); // } - public function testCompressionLZF() - { - if (!defined('Redis::COMPRESSION_LZF')) { + public function testCompressionLZF() { + if (!defined('Redis::COMPRESSION_LZF')) $this->markTestSkipped(); - } /* Don't crash on improperly compressed LZF data */ $payload = 'not-actually-lzf-compressed'; @@ -5240,11 +5161,9 @@ public function testCompressionLZF() $this->checkCompression(Redis::COMPRESSION_LZF, 0); } - public function testCompressionZSTD() - { - if (!defined('Redis::COMPRESSION_ZSTD')) { + 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'); @@ -5258,17 +5177,15 @@ public function testCompressionZSTD() } - public function testCompressionLZ4() - { - if (!defined('Redis::COMPRESSION_LZ4')) { + public function testCompressionLZ4() { + if (!defined('Redis::COMPRESSION_LZ4')) $this->markTestSkipped(); - } + $this->checkCompression(Redis::COMPRESSION_LZ4, 0); $this->checkCompression(Redis::COMPRESSION_LZ4, 9); } - private function checkCompression($mode, $level) - { + private function checkCompression($mode, $level) { $set_cmp = $this->redis->setOption(Redis::OPT_COMPRESSION, $mode); $this->assertTrue($set_cmp); if ($set_cmp !== true) @@ -5312,9 +5229,8 @@ private function checkCompression($mode, $level) public function testDumpRestore() { - if (version_compare($this->version, '2.5.0') < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } $this->redis->del('foo'); $this->redis->del('bar'); @@ -5342,11 +5258,11 @@ public function testDumpRestore() { /* Ensure we can set an absolute TTL */ $this->assertTrue($this->redis->restore('foo', time() + 10, $d_bar, ['REPLACE', 'ABSTTL'])); - $this->assertTrue($this->redis->ttl('foo') <= 10); + $this->assertLTE(10, $this->redis->ttl('foo')); /* Ensure we can set an IDLETIME */ $this->assertTrue($this->redis->restore('foo', 0, $d_bar, ['REPLACE', 'IDLETIME' => 200])); - $this->assertTrue($this->redis->object('idletime', 'foo') > 100); + $this->assertGT(100, $this->redis->object('idletime', 'foo')); /* We can't neccissarily check this depending on LRU policy, but at least attempt to use the FREQ option */ @@ -5364,7 +5280,7 @@ public function testGetLastError() { $this->redis->set('x', 'a'); $this->assertFalse($this->redis->incr('x')); $incrError = $this->redis->getLastError(); - $this->assertTrue(strlen($incrError) > 0); + $this->assertGT(0, strlen($incrError)); // clear error $this->redis->clearLastError(); @@ -5396,10 +5312,8 @@ private function array_diff_recursive($aArray1, $aArray2) { } public function testScript() { - - if (version_compare($this->version, '2.5.0') < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } // Flush any scripts we have $this->assertTrue($this->redis->script('flush')); @@ -5414,7 +5328,7 @@ public function testScript() { // None should exist $result = $this->redis->script('exists', $s1_sha, $s2_sha, $s3_sha); - $this->assertTrue(is_array($result) && count($result) == 3); + $this->assertIsArray($result, 3); $this->assertTrue(is_array($result) && count(array_filter($result)) == 0); // Load them up @@ -5428,10 +5342,9 @@ public function testScript() { } public function testEval() { - - if (version_compare($this->version, '2.5.0') < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } + /* The eval_ro method uses the same underlying handlers as eval so we only need to verify we can call it. */ @@ -5439,9 +5352,9 @@ public function testEval() { $this->assertEquals('1.55', $this->redis->eval_ro("return '1.55'")); // Basic single line response tests - $this->assertTrue(1 == $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'")); + $this->assertEquals(1, $this->redis->eval('return 1')); + $this->assertEqualsWeak(1.55, $this->redis->eval("return '1.55'")); + $this->assertEquals('hello, world', $this->redis->eval("return 'hello, world'")); /* * Keys to be incorporated into lua results @@ -5532,7 +5445,7 @@ public function testEval() { * KEYS/ARGV */ - $args_script = "return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}"; + $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_args, 3); $this->assertEquals($args_args, $args_result); @@ -5554,9 +5467,8 @@ public function testEval() { } public function testEvalSHA() { - if (version_compare($this->version, '2.5.0') < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } // Flush any loaded scripts $this->redis->script('flush'); @@ -5745,7 +5657,7 @@ public function testReplyLiteral() { } public function testNullArray() { - $key = "key:arr"; + $key = 'key:arr'; $this->redis->del($key); foreach ([false => [], true => NULL] as $opt => $test) { @@ -5797,7 +5709,7 @@ public function testConfig() { /* RESETSTAT */ $c1 = count($this->redis->info('commandstats')); $this->assertTrue($this->redis->config('resetstat')); - $this->assertTrue(count($this->redis->info('commandstats')) < $c1); + $this->assertLT($c1, count($this->redis->info('commandstats'))); /* Ensure invalid calls are handled by PhpRedis */ foreach (['notacommand', 'get', 'set'] as $cmd) { @@ -5810,7 +5722,7 @@ public function testConfig() { $res = $this->redis->config('rewrite'); $this->assertTrue(is_bool($res)); if ($res == false) { - $this->assertPatternMatch($this->redis->getLastError(), '/.*config.*/'); + $this->assertPatternMatch('/.*config.*/', $this->redis->getLastError()); $this->redis->clearLastError(); } @@ -5876,9 +5788,8 @@ public function testReconnectSelect() { public function testTime() { - if (version_compare($this->version, '2.5.0') < 0) { + 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) == 2 && @@ -5943,10 +5854,8 @@ protected function get_keyspace_count($str_db) { } public function testScan() { - if(version_compare($this->version, '2.8.0') < 0) { + if(version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } // Key count $i_key_count = $this->get_keyspace_count('db0'); @@ -6089,10 +5998,8 @@ public function testBackoffOptions() { } public function testHScan() { - if (version_compare($this->version, '2.8.0') < 0) { + if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } // Never get empty sets $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); @@ -6129,10 +6036,8 @@ public function testHScan() { } public function testSScan() { - if (version_compare($this->version, '2.8.0') < 0) { + if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); @@ -6161,10 +6066,8 @@ public function testSScan() { } public function testZScan() { - if (version_compare($this->version, '2.8.0') < 0) { + if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); @@ -6200,7 +6103,7 @@ public function testZScan() { $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*")) { + 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; @@ -6215,7 +6118,7 @@ public function testZScan() { $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) { + 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; @@ -6223,7 +6126,7 @@ public function testZScan() { } } // We should still get all the keys, just with several empty results - $this->assertTrue($i_skips > 0); + $this->assertGT(0, $i_skips); $this->assertEquals((float)0, $i_p_score); $this->assertEquals(0, $i_p_count); } @@ -6235,7 +6138,7 @@ public function testScanErrors() { foreach (['sScan', 'hScan', 'zScan'] as $str_method) { $it = NULL; $this->redis->$str_method('scankey', $it); - $this->assertTrue(strpos($this->redis->getLastError(), 'WRONGTYPE') === 0); + $this->assertEquals(0, strpos($this->redis->getLastError(), 'WRONGTYPE')); } } @@ -6255,10 +6158,8 @@ protected function createPFKey($str_key, $i_count) { public function testPFCommands() { // Isn't available until 2.8.9 - if (version_compare($this->version, '2.8.9') < 0) { + if (version_compare($this->version, '2.8.9') < 0) $this->markTestSkipped(); - return; - } $str_uniq = uniqid(); $arr_mems = []; @@ -6288,14 +6189,14 @@ public function testPFCommands() { $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)); + $this->assertGT(0, $this->redis->pfadd($str_key, $arr_mems)); // Grab estimated cardinality $i_card = $this->redis->pfcount($str_key); $this->assertIsInt($i_card); // Count should be close - $this->assertLess(abs($i_card-count($arr_mems)), count($arr_mems) * .1); + $this->assertBetween($i_card, count($arr_mems) * .9, count($arr_mems) * 1.1); // The PFCOUNT on this key should be the same as the above returned response $this->assertEquals($i_card, $this->redis->pfcount($str_key)); @@ -6311,7 +6212,8 @@ public function testPFCommands() { $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); + $this->assertBetween($i_redis_card, count($arr_mems) * .9, + count($arr_mems) * 1.1); // Clean up merge key $this->redis->del('pf-merge-{key}'); @@ -6336,9 +6238,8 @@ protected function addCities($key) { /* GEOADD */ public function testGeoAdd() { - if (!$this->minVersionCheck('3.2')) { + if (!$this->minVersionCheck('3.2')) $this->markTestSkipped(); - } $this->redis->del('geokey'); @@ -6359,9 +6260,8 @@ public function testGeoAdd() { /* GEORADIUS */ public function genericGeoRadiusTest($cmd) { - if (!$this->minVersionCheck('3.2.0')) { + if (!$this->minVersionCheck('3.2.0')) $this->markTestSkipped(); - } /* Chico */ $city = 'Chico'; @@ -6418,7 +6318,6 @@ public function genericGeoRadiusTest($cmd) { $base_subopts = $subopts; foreach ($realstoreopts as $store_type) { - for ($c = 0; $c < 3; $c++) { $subargs = $base_subargs; $subopts = $base_subopts; @@ -6461,27 +6360,24 @@ public function genericGeoRadiusTest($cmd) { } public function testGeoRadius() { - if (!$this->minVersionCheck('3.2.0')) { + if (!$this->minVersionCheck('3.2.0')) $this->markTestSkipped(); - } $this->genericGeoRadiusTest('georadius'); $this->genericGeoRadiusTest('georadius_ro'); } public function testGeoRadiusByMember() { - if (!$this->minVersionCheck('3.2.0')) { + if (!$this->minVersionCheck('3.2.0')) $this->markTestSkipped(); - } $this->genericGeoRadiusTest('georadiusbymember'); $this->genericGeoRadiusTest('georadiusbymember_ro'); } public function testGeoPos() { - if (!$this->minVersionCheck('3.2.0')) { + if (!$this->minVersionCheck('3.2.0')) $this->markTestSkipped(); - } $this->addCities('gk'); $this->assertEquals($this->rawCommandArray('gk', ['geopos', 'gk', 'Chico', 'Sacramento']), $this->redis->geopos('gk', 'Chico', 'Sacramento')); @@ -6489,9 +6385,8 @@ public function testGeoPos() { } public function testGeoHash() { - if (!$this->minVersionCheck('3.2.0')) { + if (!$this->minVersionCheck('3.2.0')) $this->markTestSkipped(); - } $this->addCities('gk'); $this->assertEquals($this->rawCommandArray('gk', ['geohash', 'gk', 'Chico', 'Sacramento']), $this->redis->geohash('gk', 'Chico', 'Sacramento')); @@ -6499,9 +6394,8 @@ public function testGeoHash() { } public function testGeoDist() { - if (!$this->minVersionCheck('3.2.0')) { + if (!$this->minVersionCheck('3.2.0')) $this->markTestSkipped(); - } $this->addCities('gk'); @@ -6515,9 +6409,8 @@ public function testGeoDist() { } public function testGeoSearch() { - if (!$this->minVersionCheck('6.2.0')) { + if (!$this->minVersionCheck('6.2.0')) $this->markTestSkipped(); - } $this->addCities('gk'); @@ -6533,9 +6426,8 @@ public function testGeoSearch() { } public function testGeoSearchStore() { - if (!$this->minVersionCheck('6.2.0')) { + if (!$this->minVersionCheck('6.2.0')) $this->markTestSkipped(); - } $this->addCities('{gk}src'); $this->assertEquals(3, $this->redis->geosearchstore('{gk}dst', '{gk}src', 'Chico', 100, 'km')); @@ -6700,25 +6592,26 @@ public function testXGroup() { $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); + $this->assertEquals(0, $this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer')); if (!$this->minVersionCheck('6.2.0')) return; /* CREATECONSUMER */ - $this->assertTrue($this->redis->del('s')); + $this->assertEquals(1, $this->redis->del('s')); $this->assertTrue($this->redis->xgroup('create', 's', 'mygroup', '$', true)); for ($i = 0; $i < 3; $i++) { - $this->assertTrue($this->redis->xgroup('createconsumer', 's', 'mygroup', "c:$i")); + $this->assertEquals(1, $this->redis->xgroup('createconsumer', 's', 'mygroup', "c:$i")); $info = $this->redis->xinfo('consumers', 's', 'mygroup'); - $this->assertTrue(is_array($info) && count($info) == $i + 1); + $this->assertIsArray($info, $i + 1); for ($j = 0; $j <= $i; $j++) { $this->assertTrue(isset($info[$j]) && isset($info[$j]['name']) && $info[$j]['name'] == "c:$j"); } } /* Make sure we don't erroneously send options that don't belong to the operation */ - $this->assertTrue($this->redis->xGroup('CREATECONSUMER', 's', 'mygroup', 'fake-consumer', true, 1337)); + $this->assertEquals(1, + $this->redis->xGroup('CREATECONSUMER', 's', 'mygroup', 'fake-consumer', true, 1337)); /* Make sure we handle the case where the user doesn't send enough arguments */ $this->redis->clearLastError(); @@ -6731,7 +6624,7 @@ public function testXGroup() { return; /* ENTRIESREAD */ - $this->assertTrue($this->redis->del('s')); + $this->assertEquals(1, $this->redis->del('s')); $this->assertTrue($this->redis->xGroup('create', 's', 'mygroup', '$', true, 1337)); $info = $this->redis->xinfo('groups', 's'); $this->assertTrue(isset($info[0]['entries-read']) && 1337 == (int)$info[0]['entries-read']); @@ -6821,7 +6714,7 @@ public function testXRead() { $m1 = round(microtime(true)*1000); $this->redis->xRead(['somestream' => '$'], -1, 100); $m2 = round(microtime(true)*1000); - $this->assertTrue($m2 - $m1 >= 100); + $this->assertGT(99, $m2 - $m1); } protected function compareStreamIds($redis, $control) { @@ -6896,14 +6789,12 @@ public function testXReadGroup() { $tm1 = $this->mstime(); $qnew = ['{s}-1' => '>', '{s}-2' => '>']; $this->redis->xReadGroup('group1', 'c1', $qnew, 0, 100); - $tm2 = $this->mstime(); - $this->assertTrue($tm2 - $tm1 >= 100); + $this->assertGTE(100, $this->mstime() - $tm1); /* Make sure passing NULL to block doesn't block */ $tm1 = $this->mstime(); $this->redis->xReadGroup('group1', 'c1', $qnew, NULL, NULL); - $tm2 = $this->mstime(); - $this->assertTrue($tm2 - $tm1 < 100); + $this->assertLT(100, $this->mstime() - $tm1); /* Make sure passing bad values to BLOCK or COUNT immediately fails */ $this->assertFalse(@$this->redis->xReadGroup('group1', 'c1', $qnew, -1)); @@ -6911,9 +6802,8 @@ public function testXReadGroup() { } public function testXPending() { - if (!$this->minVersionCheck('5.0')) { + if (!$this->minVersionCheck('5.0')) $this->markTestSkipped(); - } $rows = 5; $this->addStreamsAndGroups(['s'], $rows, ['group' => 0]); @@ -6924,12 +6814,12 @@ public function testXPending() { for ($n = count($ids); $n >= 0; $n--) { $xp = $this->redis->xPending('s', 'group'); - $this->assertEquals($xp[0], count($ids)); + $this->assertEquals(count($ids), $xp[0]); /* Verify we're seeing the IDs themselves */ for ($idx = 1; $idx <= 2; $idx++) { if ($xp[$idx]) { - $this->assertPatternMatch($xp[$idx], "/^[0-9].*-[0-9].*/"); + $this->assertPatternMatch('/^[0-9].*-[0-9].*/', $xp[$idx]); } } @@ -6965,13 +6855,13 @@ public function testXTrim() { for ($maxlen = 0; $maxlen <= 50; $maxlen += 10) { $this->addStreamEntries('stream', 100); $trimmed = $this->redis->xTrim('stream', $maxlen); - $this->assertEquals($trimmed, 100 - $maxlen); + $this->assertEquals(100 - $maxlen, $trimmed); } /* 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', 1, true)); + $this->assertEquals(0, $this->redis->xTrim('stream', 1, true)); /* We need Redis >= 6.2.0 for MINID and LIMIT options */ if (!$this->minVersionCheck('6.2.0')) @@ -6992,7 +6882,7 @@ public function testXTrim() { /* TODO: Figure oiut how to test LIMIT deterministically. For now just send a LIMIT and verify we don't get a failure from Redis. */ - $this->assertTrue(is_int($this->redis->xtrim('stream', 2, true, false, 3))); + $this->assertIsInt(@$this->redis->xtrim('stream', 2, false, false, 3)); } /* XCLAIM is one of the most complicated commands, with a great deal of different options @@ -7058,19 +6948,19 @@ public function testXClaim() { if ($tvalue !== NULL) { if ($ttype == 'IDLE') { /* If testing IDLE the value must be >= what we set */ - $this->assertTrue($pending[0][2] >= $tvalue); + $this->assertGTE($tvalue, $pending[0][2]); } 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); + $this->assertLT(20000, $pending[0][2]); } } } } 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, []); + $this->assertEquals([], $cids); } } } @@ -7113,8 +7003,7 @@ public function testXAutoClaim() { $this->assertTrue(isset($pending[3][0][0]) && $pending[3][0][0] == 'Sisko'); } - public function testXInfo() - { + public function testXInfo() { if (!$this->minVersionCheck('5.0')) $this->markTestSkipped(); @@ -7134,7 +7023,7 @@ public function testXInfo() $info = $this->redis->xInfo('STREAM', $stream); $this->assertIsArray($info); $this->assertTrue(array_key_exists('groups', $info)); - $this->assertEquals($info['groups'], count($groups)); + $this->assertEquals(count($groups), $info['groups']); foreach (['first-entry', 'last-entry'] as $key) { $this->assertTrue(array_key_exists($key, $info)); $this->assertTrue(is_array($info[$key])); @@ -7236,7 +7125,7 @@ public function testAcl() { $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->assertEquals(1, $this->redis->acl('DELUSER', 'admin')); $this->assertTrue(!in_array('admin', $this->redis->acl('USERS'))); /* Try to log in with a bad username/password */ @@ -7246,7 +7135,7 @@ 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->assert("Expected an array from ACL LOG, got: " . var_export($arr_log, true)); + $this->assert('Expected an array from ACL LOG, got: ' . var_export($arr_log, true)); return; } @@ -7293,15 +7182,15 @@ function($o) { $o->auth(['1337haxx00r', 'lolwut']); }, '/^WRONGPASS.*$/'); /* 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")) { + if ( ! file_exists('/tmp/redis.sock')) { $this->markTestSkipped(); } $arr_sock_tests = [ - ["/tmp/redis.sock"], - ["/tmp/redis.sock", null], - ["/tmp/redis.sock", 0], - ["/tmp/redis.sock", -1], + ['/tmp/redis.sock'], + ['/tmp/redis.sock', null], + ['/tmp/redis.sock', 0], + ['/tmp/redis.sock', -1], ]; try { @@ -7343,9 +7232,8 @@ public function testHighPorts() { return $this->detectRedis('localhost', $port) ? $port : 0; }, [32768, 32769, 32770])); - if ( ! $ports) { + if ( ! $ports) $this->markTestSkipped(); - } foreach ($ports as $port) { $obj_r = new Redis(); @@ -7385,13 +7273,12 @@ public function testSession_compression() { $this->assertEquals('SUCCESS', $runner->execFg()); $this->redis->setOption(Redis::OPT_COMPRESSION, $val); - $this->assertPatternMatch($this->redis->get($runner->getSessionKey()), "/.*$data.*/"); + $this->assertPatternMatch("/.*$data.*/", $this->redis->get($runner->getSessionKey())); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); } } - public function testSession_savedToRedis() - { + public function testSession_savedToRedis() { $runner = $this->sessionRunner(); $this->assertEquals('SUCCESS', $runner->execFg()); @@ -7419,8 +7306,7 @@ public function testSession_lockKeyCorrect() { } } - public function testSession_lockingDisabledByDefault() - { + public function testSession_lockingDisabledByDefault() { $runner = $this->sessionRunner() ->lockingEnabled(false) ->sleep(5); @@ -7429,8 +7315,7 @@ public function testSession_lockingDisabledByDefault() $this->assertKeyMissing($this->redis, $runner->getSessionLockKey()); } - public function testSession_lockReleasedOnClose() - { + public function testSession_lockReleasedOnClose() { $runner = $this->sessionRunner() ->sleep(1) ->lockingEnabled(true); @@ -7440,8 +7325,7 @@ public function testSession_lockReleasedOnClose() $this->assertKeyMissing($this->redis, $runner->getSessionLockKey()); } - public function testSession_lock_ttlMaxExecutionTime() - { + public function testSession_lock_ttlMaxExecutionTime() { $runner1 = $this->sessionRunner() ->sleep(10) ->maxExecutionTime(2); @@ -7456,11 +7340,10 @@ public function testSession_lock_ttlMaxExecutionTime() $st = microtime(true); $this->assertEquals('SUCCESS', $runner2->execFg()); $el = microtime(true) - $st; - $this->assertLess($el, 4); + $this->assertLT(4, $el); } - public function testSession_lock_ttlLockExpire() - { + public function testSession_lock_ttlLockExpire() { $runner1 = $this->sessionRunner() ->sleep(10) @@ -7477,7 +7360,7 @@ public function testSession_lock_ttlLockExpire() $st = microtime(true); $this->assertEquals('SUCCESS', $runner2->execFg()); - $this->assertLess(microtime(true) - $st, 3); + $this->assertLT(3, microtime(true) - $st); } public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { @@ -7500,11 +7383,10 @@ public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { usleep(1500000); // 1.5 sec $this->assertEquals('SUCCESS', $runner2->execFg()); - $this->assertEquals($runner->getData(), 'secondProcess'); + $this->assertEquals('secondProcess', $runner->getData()); } - public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() - { + public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { $runner = $this->sessionRunner() ->sleep(2) ->lockingEnabled(true) @@ -7522,7 +7404,7 @@ public function testSession_correctLockRetryCount() { $this->assertTrue($runner->execBg()); if ( ! $runner->waitForLockKey($this->redis, 2)) { $this->externalCmdFailure($runner->getCmd(), $runner->output(), - "Failed waiting for session lock key", + 'Failed waiting for session lock key', $runner->getExitCode()); } @@ -7538,7 +7420,7 @@ public function testSession_correctLockRetryCount() { $ex = $runner2->execFg(); if (stripos($ex, 'SUCCESS') !== false) { $this->externalCmdFailure($runner2->getCmd(), $ex, - "Expected failure but lock was acquired!", + 'Expected failure but lock was acquired!', $runner2->getExitCode()); } $et = microtime(true); @@ -7562,7 +7444,7 @@ public function testSession_defaultLockRetryCount() { if ( ! $runner->waitForLockKey($this->redis, 3)) { $this->externalCmdFailure($runner->getCmd(), $runner->output(), - "Failed waiting for session lock key", + 'Failed waiting for session lock key', $runner->getExitCode()); } @@ -7572,8 +7454,7 @@ public function testSession_defaultLockRetryCount() { $this->assertBetween($et - $st, 2, 3); } - public function testSession_noUnlockOfOtherProcess() - { + public function testSession_noUnlockOfOtherProcess() { $st = microtime(true); $sleep = 3; @@ -7588,7 +7469,7 @@ public function testSession_noUnlockOfOtherProcess() * the lock was attained. */ $this->assertTrue($runner->execBg()); if ( ! $runner->waitForLockKey($this->redis, 1)) { - $this->assert("Failed waiting for session lock key"); + $this->assert('Failed waiting for session lock key'); return; } @@ -7603,7 +7484,7 @@ public function testSession_noUnlockOfOtherProcess() $tm3 = microtime(true); /* 3. Verify we had to wait for this lock */ - $this->assertTrue($tm3 - $tm2 >= $sleep - ($tm2 - $tm1)); + $this->assertGTE($sleep - ($tm2 - $tm1), $tm3 - $tm2); } public function testSession_lockWaitTime() { @@ -7644,19 +7525,18 @@ public function testMultipleConnect() { public function testConnectException() { $host = 'github.com'; - if (gethostbyname($host) === $host) { + if (gethostbyname($host) === $host) $this->markTestSkipped('online test'); - } + $redis = new Redis(); try { $redis->connect($host, 6379, 0.01); } catch (Exception $e) { - $this->assertTrue(strpos($e, "timed out") !== false); + $this->assertStringContains('timed out', $e->getMessage()); } } - public function testTlsConnect() - { + public function testTlsConnect() { if (($fp = @fsockopen($this->getHost(), 6378)) == NULL) $this->markTestSkipped(); @@ -7670,26 +7550,20 @@ public function testTlsConnect() } } - public function testReset() - { + public function testReset() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->assertTrue($this->redis->multi()->select(2)->set('foo', 'bar')->reset()); $this->assertEquals(Redis::ATOMIC, $this->redis->getMode()); $this->assertEquals(0, $this->redis->getDBNum()); } - public function testCopy() - { + public function testCopy() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('{key}dst'); $this->redis->set('{key}src', 'foo'); @@ -7704,8 +7578,7 @@ public function testCopy() $this->assertEquals('bar', $this->redis->get('{key}dst')); } - public function testCommand() - { + public function testCommand() { $commands = $this->redis->command(); $this->assertIsArray($commands); $this->assertEquals(count($commands), $this->redis->command('count')); @@ -7723,15 +7596,14 @@ public function testCommand() $list = $this->redis->command('list', 'filterby', 'pattern', 'lol*'); $this->assertIsArray($list); - $this->assertEquals($list, ['lolwut']); + $this->assertEquals(['lolwut'], $list); } } public function testFunction() { - if (version_compare($this->version, '7.0') < 0) { + if (version_compare($this->version, '7.0') < 0) $this->markTestSkipped(); - return; - } + $this->assertTrue($this->redis->function('flush', 'sync')); $this->assertEquals('mylib', $this->redis->function('load', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)")); $this->assertEquals('foo', $this->redis->fcall('myfunc', [], ['foo'])); @@ -7816,15 +7688,13 @@ public function testSession_regenerateSessionId_withLock_withDestroy_withProxy( $this->regenerateIdHelper(true, true, true); } - public function testSession_ttl_equalsToSessionLifetime() - { + public function testSession_ttl_equalsToSessionLifetime() { $runner = $this->sessionRunner()->lifetime(600); $this->assertEquals('SUCCESS', $runner->execFg()); $this->assertEquals(600, $this->redis->ttl($runner->getSessionKey())); } - public function testSession_ttl_resetOnWrite() - { + public function testSession_ttl_resetOnWrite() { $runner1 = $this->sessionRunner()->lifetime(600); $this->assertEquals('SUCCESS', $runner1->execFg()); diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 55499570c8..653c6da08c 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -7,17 +7,17 @@ class TestSkippedException extends Exception {} class TestSuite { /* Host and port the unit tests will use */ - private $str_host; - private $i_port = 6379; + private string $host; + private ?int $port = 6379; /* Redis authentication we'll use */ private $auth; /* Redis server version */ protected $version; - protected $is_keydb; + protected bool $is_keydb; - private static $_boo_colorize = false; + private static bool $colorize = false; private static $BOLD_ON = "\033[1m"; private static $BOLD_OFF = "\033[0m"; @@ -30,41 +30,33 @@ class TestSuite private static $YELLOW = "\033[0;33m"; private static $RED = "\033[0;31m"; - public static $errors = []; - public static $warnings = []; + public static array $errors = []; + public static array $warnings = []; - public function __construct($str_host, $i_port, $auth) { - $this->str_host = $str_host; - $this->i_port = $i_port; + public function __construct(string $host, ?int $port, $auth) { + $this->host = $host; + $this->port = $port; $this->auth = $auth; } - public function getHost() { return $this->str_host; } - public function getPort() { return $this->i_port; } + public function getHost() { return $this->host; } + public function getPort() { return $this->port; } public function getAuth() { return $this->auth; } - public static function make_bold($str_msg) { - return self::$_boo_colorize - ? self::$BOLD_ON . $str_msg . self::$BOLD_OFF - : $str_msg; + public static function make_bold(string $msg) { + return self::$colorize ? self::$BOLD_ON . $msg . self::$BOLD_OFF : $msg; } - public static function make_success($str_msg) { - return self::$_boo_colorize - ? self::$GREEN . $str_msg . self::$BOLD_OFF - : $str_msg; + public static function make_success(string $msg) { + return self::$colorize ? self::$GREEN . $msg . self::$BOLD_OFF : $msg; } - public static function make_fail($str_msg) { - return self::$_boo_colorize - ? self::$RED . $str_msg . self::$BOLD_OFF - : $str_msg; + public static function make_fail(string $msg) { + return self::$colorize ? self::$RED . $msg . self::$BOLD_OFF : $msg; } - public static function make_warning($str_msg) { - return self::$_boo_colorize - ? self::$YELLOW . $str_msg . self::$BOLD_OFF - : $str_msg; + public static function make_warning(string $msg) { + return self::$colorize ? self::$YELLOW . $msg . self::$BOLD_OFF : $msg; } protected function printArg($v) { @@ -123,42 +115,55 @@ protected function assert($fmt, ...$args) { self::$errors []= $this->assertionTrace($fmt, ...$args); } - protected function assertFalse($bool) { - if( ! $bool) + protected function assertKeyExists($redis, $key): bool { + if ($redis->exists($key)) return true; - self::$errors []= $this->assertionTrace(); + + self::$errors []= $this->assertionTrace("Key '%s' does not exist.", $key); return false; } - protected function assertKeyExists($redis, $key) { - if ($redis->exists($key)) + protected function assertKeyMissing($redis, $key): bool { + if ( ! $redis->exists($key)) return true; - self::$errors []= $this->assertionTrace("Key '%s' does not exist.", $key); + self::$errors []= $this->assertionTrace("Key '%s' exists but shouldn't.", $key); return false; } - protected function assertKeyMissing($redis, $key) { - if ( ! $redis->exists($key)) + protected function assertTrue($value): bool { + if ($value === true) return true; - self::$errors []= $this->assertionTrace("Key '%s' exists but shouldn't.", $key); + self::$errors []= $this->assertionTrace("%s !== %s", $this->printArg($value), + $this->printArg(true)); return false; } - protected function assertTrue($bool, $msg='') { - if($bool) + protected function assertFalse($value): bool { + if ($value === false) return true; - self::$errors []= $this->assertionTrace($msg); + self::$errors []= $this->assertionTrace("%s !== %s", $this->printArg($value), + $this->printArg(false)); + + return false; + } + + protected function assertNull($value): bool { + if ($value === NULL) + return true; + + self::$errors []= $this->assertionTrace("%s !== %s", $this->printArg($value), + $this->printArg(NULL)); return false; } - protected function assertInArray($ele, $arr, ?callable $cb = NULL) { + protected function assertInArray($ele, $arr, ?callable $cb = NULL): bool { $cb ??= function ($v) { return true; }; $key = array_search($ele, $arr); @@ -173,25 +178,39 @@ protected function assertInArray($ele, $arr, ?callable $cb = NULL) { return false; } - protected function assertIsInt($v) { - if (is_int($v)) + protected function assertIsString($v): bool { + if (is_string($v)) return true; - self::$errors []= $this->assertionTrace("%s is not an integer", $this->printArg($v)); + self::$errors []= $this->assertionTrace("%s is not a string", $this->printArg($v)); return false; } - protected function assertIsArray($v) { - if (is_array($v)) + protected function assertIsInt($v): bool { + if (is_int($v)) return true; - self::$errors []= $this->assertionTrace("%s is not an array", $this->printArg($v)); + self::$errors []= $this->assertionTrace("%s is not an integer", $this->printArg($v)); return false; } - protected function assertArrayKey($arr, $key, callable $cb = NULL) { + protected function assertIsArray($v, ?int $size = null): bool { + if ( ! is_array($v)) { + self::$errors []= $this->assertionTrace("%s is not an array", $this->printArg($v)); + return false; + } + + if ( ! is_null($size) && count($v) != $size) { + self::$errors []= $this->assertionTrace("Array size %d != %d", count($v), $size); + return false; + } + + return true; + } + + protected function assertArrayKey($arr, $key, callable $cb = NULL): bool { $cb ??= function ($v) { return true; }; if (($exists = isset($arr[$key])) && $cb($arr[$key])) @@ -211,10 +230,24 @@ protected function assertArrayKey($arr, $key, callable $cb = NULL) { return false; } - protected function assertValidate($val, $cb) { - if ( ! is_callable($cb)) - die("Fatal: Callable assertValidate callback required\n"); + protected function assertArrayKeyEquals($arr, $key, $value): bool { + if ( ! isset($arr[$key])) { + self::$errors []= $this->assertionTrace( + "Key '%s' not found in %s", $key, $this->printArg($arr)); + return false; + } + + if ($arr[$key] !== $value) { + self::$errors []= $this->assertionTrace( + "Value '%s' != '%s' for key '%s' in %s", + $arr[$key], $value, $key, $this->printArg($arr)); + return false; + } + return true; + } + + protected function assertValidate($val, callable $cb): bool { if ($cb($val)) return true; @@ -223,12 +256,9 @@ protected function assertValidate($val, $cb) { return false; } - protected function assertThrowsMatch($arg, $cb, $regex = NULL) { + protected function assertThrowsMatch($arg, callable $cb, $regex = NULL): bool { $threw = $match = false; - if ( ! is_callable($cb)) - die("Fatal: Callable assertThrows callback required\n"); - try { $cb($arg); } catch (Exception $ex) { @@ -239,28 +269,45 @@ protected function assertThrowsMatch($arg, $cb, $regex = NULL) { if ($threw && $match) return true; -// $bt = debug_backtrace(false); $ex = !$threw ? 'no exception' : "no match '$regex'"; self::$errors []= $this->assertionTrace("[$ex]"); -// + + return false; + } + + protected function assertLTE($maximum, $value): bool { + if ($value <= $maximum) + return true; + + self::$errors []= $this->assertionTrace("%s > %s", $value, $maximum); + return false; } - protected function assertLess($a, $b) { - if($a < $b) + protected function assertLT($minimum, $value): bool { + if ($value < $minimum) return true; - self::$errors []= $this->assertionTrace("%s >= %s", $a, $b); + self::$errors []= $this->assertionTrace("%s >= %s", $value, $minimum); return false; } - protected function assertMore($a, $b) { - if($a > $b) + protected function assertGT($maximum, $value): bool { + if ($value > $maximum) return true; - self::$errors [] = $this->assertionTrace("%s <= %s", $a, $b); + self::$errors [] = $this->assertionTrace("%s <= %s", $maximum, $value); + + return false; + } + + protected function assertGTE($minimum, $value): bool { + if ($value >= $minimum) + return true; + + self::$errors [] = $this->assertionTrace("%s < %s", $minimum, $value); return false; } @@ -284,7 +331,7 @@ protected function externalCmdFailure($cmd, $output, $msg = NULL, $exit_code = N self::$errors[] = implode("\n", $lines) . "\n"; } - protected function assertBetween($value, $min, $max, bool $exclusive = false) { + protected function assertBetween($value, $min, $max, bool $exclusive = false): bool { if ($min > $max) [$max, $min] = [$min, $max]; @@ -304,7 +351,7 @@ protected function assertBetween($value, $min, $max, bool $exclusive = false) { /* Replica of PHPUnit's assertion. Basically are two arrys the same without ' respect to order. */ - protected function assertEqualsCanonicalizing($expected, $actual, $keep_keys = false) { + protected function assertEqualsCanonicalizing($expected, $actual, $keep_keys = false): bool { if ($expected InstanceOf Traversable) $expected = iterator_to_array($expected); @@ -329,7 +376,7 @@ protected function assertEqualsCanonicalizing($expected, $actual, $keep_keys = f return false; } - protected function assertEqualsWeak($expected, $actual) { + protected function assertEqualsWeak($expected, $actual): bool { if ($expected == $actual) return true; @@ -339,8 +386,8 @@ protected function assertEqualsWeak($expected, $actual) { return false; } - protected function assertEquals($expected, $actual) { - if($expected === $actual) + protected function assertEquals($expected, $actual): bool { + if ($expected === $actual) return true; self::$errors[] = $this->assertionTrace("%s !== %s", $this->printArg($actual), @@ -349,17 +396,17 @@ protected function assertEquals($expected, $actual) { return false; } - public function assertNotEquals($a, $b) { - if($a !== $b) + public function assertNotEquals($wrong_value, $test_value): bool { + if ($wrong_value !== $test_value) return true; - self::$errors []= $this->assertionTrace("%s === %s", $this->printArg($a), - $this->printArg($b)); + self::$errors []= $this->assertionTrace("%s === %s", $this->printArg($wrong_value), + $this->printArg($test_value)); return false; } - protected function assertStringContains(string $needle, $haystack) { + protected function assertStringContains(string $needle, $haystack): bool { if ( ! is_string($haystack)) { self::$errors []= $this->assertionTrace("'%s' is not a string", $this->printArg($haystack)); return false; @@ -371,18 +418,19 @@ protected function assertStringContains(string $needle, $haystack) { self::$errors []= $this->assertionTrace("'%s' not found in '%s'", $needle, $haystack); } - protected function assertPatternMatch($str_test, $str_regex) { - if (preg_match($str_regex, $str_test)) + protected function assertPatternMatch(string $pattern, string $value): bool { + if (preg_match($pattern, $value)) return true; - self::$errors []= $this->assertionTrace("'%s' doesnt match '%s'", - $str_test, $str_regex); + self::$errors []= $this->assertionTrace("'%s' doesnt match '%s'", $value, + $pattern); return false; } - protected function markTestSkipped($msg='') { + protected function markTestSkipped(string $msg = '') { $bt = debug_backtrace(false); + self::$warnings []= sprintf("Skipped test: %s:%d (%s) %s\n", $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); @@ -390,22 +438,23 @@ protected function markTestSkipped($msg='') { throw new TestSkippedException($msg); } - private static function getMaxTestLen($arr_methods, $str_limit) { - $i_result = 0; + private static function getMaxTestLen(array $methods, ?string $limit): int { + $result = 0; - foreach ($arr_methods as $obj_method) { - $str_name = strtolower($obj_method->name); + foreach ($methods as $obj_method) { + $name = strtolower($obj_method->name); - if (substr($str_name, 0, 4) != 'test') + if (substr($name, 0, 4) != 'test') continue; - if ($str_limit && !strstr($str_name, $str_limit)) + if ($limit && !strstr($name, $limit)) continue; - if (strlen($str_name) > $i_result) { - $i_result = strlen($str_name); + if (strlen($name) > $result) { + $result = strlen($name); } } - return $i_result; + + return $result; } private static function findFile($path, $file) { @@ -443,28 +492,31 @@ public static function loadTestClass($class) { } /* Flag colorization */ - public static function flagColorization($boo_override) { - self::$_boo_colorize = $boo_override && function_exists('posix_isatty') && + public static function flagColorization(bool $override) { + self::$colorize = $override && function_exists('posix_isatty') && posix_isatty(STDOUT); } - public static function run($className, $str_limit = NULL, $str_host = NULL, $i_port = NULL, $auth = NULL) { + public static function run($className, ?string $limit = NULL, + ?string $host = NULL, ?int $port = NULL, + $auth = NULL) + { /* Lowercase our limit arg if we're passed one */ - $str_limit = $str_limit ? strtolower($str_limit) : $str_limit; + $limit ??= strtolower($limit); $rc = new ReflectionClass($className); $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC); - $i_max_len = self::getMaxTestLen($methods, $str_limit); + $i_max_len = self::getMaxTestLen($methods, $limit); foreach($methods as $m) { $name = $m->name; - if(substr($name, 0, 4) !== 'test') + if (substr($name, 0, 4) !== 'test') continue; /* If we're trying to limit to a specific test and can't match the * substring, skip */ - if ($str_limit && strstr(strtolower($name), $str_limit)===FALSE) { + if ($limit && stristr($name, $limit) === false) { continue; } @@ -472,7 +524,7 @@ public static function run($className, $str_limit = NULL, $str_host = NULL, $i_p echo self::make_bold($str_out_name); $count = count($className::$errors); - $rt = new $className($str_host, $i_port, $auth); + $rt = new $className($host, $port, $auth); try { $rt->setUp(); @@ -483,7 +535,6 @@ public static function run($className, $str_limit = NULL, $str_host = NULL, $i_p } else { $str_msg = self::make_fail('FAILED'); } - //echo ($count === count($className::$errors)) ? "." : "F"; } catch (Exception $e) { /* We may have simply skipped the test */ if ($e instanceof TestSkippedException) { @@ -499,7 +550,7 @@ public static function run($className, $str_limit = NULL, $str_host = NULL, $i_p echo "\n"; echo implode('', $className::$warnings) . "\n"; - if(empty($className::$errors)) { + if (empty($className::$errors)) { echo "All tests passed. \o/\n"; return 0; } diff --git a/tests/regenerateSessionId.php b/tests/regenerateSessionId.php index fbffc1b35f..03a45dad63 100644 --- a/tests/regenerateSessionId.php +++ b/tests/regenerateSessionId.php @@ -80,7 +80,7 @@ public function write($session_id, $session_data) if (!session_start()) { $result = "FAILED: session_start()"; -} else if (!session_regenerateId($destroy_previous)) { +} else if (!session_regenerate_id($destroy_previous)) { $result = "FAILED: session_regenerateId()"; } else { $result = session_id(); From e18f6c6d9eccd075810e5998634de101cdd26fc7 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 28 May 2024 20:20:11 -0700 Subject: [PATCH 1882/1986] Minor refactor --- README.md | 4 ++-- cluster.md | 12 ++++++------ redis.c | 12 ++++++------ tests/RedisTest.php | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 15ff8e4e99..80a42b9131 100644 --- a/README.md +++ b/README.md @@ -4013,7 +4013,7 @@ _**Description**_: Subscribe to channels. Warning: this function will probably c ##### *Parameters* *channels*: an array of channels to subscribe to -*callback*: either a string or an Array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. +*callback*: either a string or [$instance, 'method_name']. The callback function receives 3 parameters: the redis instance, the channel name, and the message. *return value*: Mixed. Any non-null return value in the callback will be returned to the caller. ##### *Example* ~~~php @@ -4112,7 +4112,7 @@ $ret = $redis->multi() ->exec(); /* -$ret == Array(0 => TRUE, 1 => 'val1', 2 => TRUE, 3 => 'val2'); +$ret == [0 => TRUE, 1 => 'val1', 2 => TRUE, 3 => 'val2']; */ ~~~ diff --git a/cluster.md b/cluster.md index 5be120f26b..662024f4a7 100644 --- a/cluster.md +++ b/cluster.md @@ -10,21 +10,21 @@ To maintain consistency with the RedisArray class, one can create and connect to #### Declaring a cluster with an array of seeds ```php // Create a cluster setting three nodes as seeds -$obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003')); +$obj_cluster = new RedisCluster(NULL, ['host:7000', 'host:7001', 'host:7003']); // Connect and specify timeout and read_timeout -$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5); +$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5); // Connect with read/write timeout as well as specify that phpredis should use // persistent connections to each node. -$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true); +$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true); // Connect with cluster using password. -$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, "password"); +$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, "password"); // Connect with cluster using SSL/TLS // last argument is an array with [SSL context](https://www.php.net/manual/en/context.ssl.php) options -$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, NULL, Array("verify_peer" => false)); +$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, NULL, Array("verify_peer" => false)); ``` #### Loading a cluster configuration by name @@ -215,4 +215,4 @@ Following INI variables can be used to configure session compression: redis.session.compression = zstd ; What compression level should be used? Compression level depends on used library. For most deployments range 1-9 should be fine. Defaults to: 3 redis.session.compression_level = 3 -~~~ \ No newline at end of file +~~~ diff --git a/redis.c b/redis.c index b7eaff7ff6..45231b67d2 100644 --- a/redis.c +++ b/redis.c @@ -1719,13 +1719,13 @@ PHP_METHOD(Redis, zPopMin) } /* }}} */ -/* {{{ proto Redis::bzPopMax(Array(keys) [, timeout]): Array */ +/* {{{ proto Redis::bzPopMax(Array[keys] [, timeout]): Array */ PHP_METHOD(Redis, bzPopMax) { REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ -/* {{{ proto Redis::bzPopMin(Array(keys) [, timeout]): Array */ +/* {{{ proto Redis::bzPopMin([keys] [, timeout]): Array */ PHP_METHOD(Redis, bzPopMin) { REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } @@ -2135,7 +2135,7 @@ PHP_METHOD(Redis, publish) } /* }}} */ -/* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN)) */ +/* {{{ proto void Redis::psubscribe([pattern1, pattern2, ... patternN]) */ PHP_METHOD(Redis, psubscribe) { REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, @@ -2143,7 +2143,7 @@ PHP_METHOD(Redis, psubscribe) } /* }}} */ -/* {{{ proto void Redis::ssubscribe(Array(shardchannel1, shardchannel2, ... shardchannelN)) */ +/* {{{ proto void Redis::ssubscribe([shardchannel1, shardchannel2, ... shardchannelN]) */ PHP_METHOD(Redis, ssubscribe) { REDIS_PROCESS_KW_CMD("SSUBSCRIBE", redis_subscribe_cmd, @@ -2151,7 +2151,7 @@ PHP_METHOD(Redis, ssubscribe) } /* }}} */ -/* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */ +/* {{{ proto void Redis::subscribe([channel1, channel2, ... channelN]) */ PHP_METHOD(Redis, subscribe) { REDIS_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd, redis_subscribe_response); @@ -2159,7 +2159,7 @@ PHP_METHOD(Redis, subscribe) { /** * [ps]unsubscribe channel_0 channel_1 ... channel_n - * [ps]unsubscribe(array(channel_0, channel_1, ..., channel_n)) + * [ps]unsubscribe([channel_0, channel_1, ..., channel_n]) * response format : * array( * channel_0 => TRUE|FALSE, diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 28e0a09c9b..3023e7dfec 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -703,9 +703,9 @@ public function testMultiple() { $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['mget1', 'mget2', 'mget3'])); $this->redis->set('k5', '$1111111111'); - $this->assertEquals([0 => '$1111111111'], $this->redis->mget(['k5'])); + $this->assertEquals(['$1111111111'], $this->redis->mget(['k5'])); - $this->assertEquals([0 => 'test'], $this->redis->mget([1])); // non-string + $this->assertEquals(['test'], $this->redis->mget([1])); // non-string } public function testMultipleBin() { From 0d89e92889c49e0e693ee16747ace2bc506208b6 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Tue, 28 May 2024 20:39:58 -0700 Subject: [PATCH 1883/1986] Spelling fixes --- redis_cluster.c | 6 +++--- tests/RedisTest.php | 6 +++--- tests/TestSuite.php | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/redis_cluster.c b/redis_cluster.c index 77d01065d7..b60f8045f0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -2268,7 +2268,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, HashTable *hash; long num_ele; zend_long count = 0; - zend_bool complted; + zend_bool completed; uint64_t cursor; // Can't be in MULTI mode @@ -2288,8 +2288,8 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, c->readonly = 1; /* Get our scan cursor and return early if we're done */ - cursor = redisGetScanCursor(z_it, &complted); - if (complted) + cursor = redisGetScanCursor(z_it, &completed); + if (completed) RETURN_FALSE; // Apply any key prefix we have, get the slot diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 3023e7dfec..151dda9ff1 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2277,9 +2277,9 @@ public function testClient() { // Figure out which ip:port is us! $address = NULL; - foreach($clients as $cleint) { - if ($cleint['name'] == 'phpredis_unit_tests') { - $address = $cleint['addr']; + foreach($clients as $client) { + if ($client['name'] == 'phpredis_unit_tests') { + $address = $client['addr']; } } diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 653c6da08c..a50483e42c 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -349,7 +349,7 @@ protected function assertBetween($value, $min, $max, bool $exclusive = false): b return false; } - /* Replica of PHPUnit's assertion. Basically are two arrys the same without + /* Replica of PHPUnit's assertion. Basically are two arrays the same without ' respect to order. */ protected function assertEqualsCanonicalizing($expected, $actual, $keep_keys = false): bool { if ($expected InstanceOf Traversable) From 78b70ca8f49a5918e7ce626c19076036b7d20c75 Mon Sep 17 00:00:00 2001 From: michael-grunder Date: Wed, 29 May 2024 22:12:15 -0700 Subject: [PATCH 1884/1986] More test refactoring. * Switch remaining old-style PHP 5.4 `Array(...)` declarations to `[...]` * Update variable names getting rid hungarian notation prefixes (e.g. `str_`, `i_`, etc). * Allow cluster seeds to be passed on the command-line instead of soley relying on either a node environment variable or our tests/nodes/nodemap file. This should make it easier to run ad-hoc cluster tests by specifying just a single seed. * Add some diagnostics for when we can't find a suitable cluster to run our tests against indicating exactly where we looked for the env var and node file. * Refactor RedisArray tests to use our newer TestSuite assertions. * Allow `RedisArray` ports to be specified on the command-line as well. * Various formatting fixes. * More robust KeyDB detection. --- arrays.md | 2 +- cluster.md | 2 +- tests/RedisArrayTest.php | 199 ++++---- tests/RedisClusterTest.php | 470 ++++++++++--------- tests/RedisTest.php | 933 ++++++++++++++++++------------------- tests/TestRedis.php | 73 +-- tests/TestSuite.php | 54 ++- 7 files changed, 904 insertions(+), 829 deletions(-) diff --git a/arrays.md b/arrays.md index 3ca9b7d2b9..b5ba3738cf 100644 --- a/arrays.md +++ b/arrays.md @@ -121,7 +121,7 @@ For instance, the keys “{user:1}:name” and “{user:1}:email” will be stor ## Custom key distribution function In order to control the distribution of keys by hand, you can provide a custom function or closure that returns the server number, which is the index in the array of servers that you created the RedisArray object with. -For instance, instantiate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. +For instance, instantiate a RedisArray object with `new RedisArray(["us-host", "uk-host", "de-host"], ["distributor" => "dist"]);` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. ### Example
    diff --git a/cluster.md b/cluster.md
    index 662024f4a7..fa1237d062 100644
    --- a/cluster.md
    +++ b/cluster.md
    @@ -24,7 +24,7 @@ $obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true
     
     // Connect with cluster using SSL/TLS
     // last argument is an array with [SSL context](https://www.php.net/manual/en/context.ssl.php) options
    -$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, NULL, Array("verify_peer" => false));
    +$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, NULL, ["verify_peer" => false]);
     ```
     
     #### Loading a cluster configuration by name
    diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php
    index 67c35b892b..f28f4dab45 100644
    --- a/tests/RedisArrayTest.php
    +++ b/tests/RedisArrayTest.php
    @@ -2,6 +2,7 @@
     require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");
     
     define('REDIS_ARRAY_DATA_SIZE', 1000);
    +define('REDIS_RA_DEFAULT_PORTS', [6379, 6380, 6381, 6382]);
     
     function custom_hash($str) {
         // str has the following format: $APPID_fb$FACEBOOKID_$key.
    @@ -18,19 +19,19 @@ function parseHostPort($str, &$host, &$port) {
         $port = substr($str, $pos+1);
     }
     
    -function getRedisVersion($obj_r) {
    -    $arr_info = $obj_r->info();
    +function getRedisVersion(object $client) {
    +    $arr_info = $client->info();
         if (!$arr_info || !isset($arr_info['redis_version'])) {
    -        return "0.0.0";
    +        return '0.0.0';
         }
         return $arr_info['redis_version'];
     }
     
     /* Determine the lowest redis version attached to this RedisArray object */
    -function getMinVersion($obj_ra) {
    -    $min_version = "0.0.0";
    -    foreach ($obj_ra->_hosts() as $host) {
    -        $version = getRedisVersion($obj_ra->_instance($host));
    +function getMinVersion(object $ra) {
    +    $min_version = '0.0.0';
    +    foreach ($ra->_hosts() as $host) {
    +        $version = getRedisVersion($ra->_instance($host));
             if (version_compare($version, $min_version) > 0) {
                 $min_version = $version;
             }
    @@ -43,7 +44,7 @@ class Redis_Array_Test extends TestSuite
     {
         private $min_version;
         private $strings;
    -    public $ra = NULL;
    +    public  $ra = NULL;
         private $data = NULL;
     
         public function setUp() {
    @@ -54,13 +55,13 @@ public function setUp() {
                 $this->strings['key-'.$i] = 'val-'.$i;
             }
     
    -        global $newRing, $oldRing, $useIndex;
    -        $options = ['previous' => $oldRing, 'index' => $useIndex];
    +        global $new_ring, $old_ring, $use_index;
    +        $options = ['previous' => $old_ring, 'index' => $use_index];
             if ($this->getAuth()) {
                 $options['auth'] = $this->getAuth();
             }
     
    -        $this->ra = new RedisArray($newRing, $options);
    +        $this->ra = new RedisArray($new_ring, $options);
             $this->min_version = getMinVersion($this->ra);
         }
     
    @@ -125,12 +126,16 @@ public function testKeyLocality() {
             $this->checkCommonLocality();
     
             // with common hashing function
    -        global $newRing, $oldRing, $useIndex;
    -        $options = ['previous' => $oldRing, 'index' => $useIndex, 'function' => 'custom_hash'];
    +        global $new_ring, $old_ring, $use_index;
    +        $options = [
    +            'previous' => $old_ring,
    +            'index' => $use_index,
    +            'function' => 'custom_hash'
    +        ];
             if ($this->getAuth()) {
                 $options['auth'] = $this->getAuth();
             }
    -        $this->ra = new RedisArray($newRing, $options);
    +        $this->ra = new RedisArray($new_ring, $options);
     
             // basic key locality with custom hash
             $this->addData('fb'.rand());
    @@ -140,20 +145,27 @@ public function testKeyLocality() {
         public function customDistributor($key)
         {
             $a = unpack("N*", md5($key, true));
    -        global $newRing;
    -        $pos = abs($a[1]) % count($newRing);
    +        global $new_ring;
    +        $pos = abs($a[1]) % count($new_ring);
     
             return $pos;
         }
     
         public function testKeyDistributor()
         {
    -        global $newRing, $useIndex;
    -        $options = ['index' => $useIndex, 'function' => 'custom_hash', 'distributor' => [$this, "customDistributor"]];
    +        global $new_ring, $useIndex;
    +
    +        $options = [
    +            'index'       => $useIndex,
    +            'function'    => 'custom_hash',
    +            'distributor' => [$this, "customDistributor"]
    +        ];
    +
             if ($this->getAuth()) {
                 $options['auth'] = $this->getAuth();
             }
    -        $this->ra = new RedisArray($newRing, $options);
    +
    +        $this->ra = new RedisArray($new_ring, $options);
     
             // custom key distribution function.
             $this->addData('fb'.rand());
    @@ -163,7 +175,7 @@ public function testKeyDistributor()
             foreach($this->data as $k => $v) {
                 $node = $this->ra->_target($k);
                 $pos = $this->customDistributor($k);
    -            $this->assertEquals($node, $newRing[$pos]);
    +            $this->assertEquals($node, $new_ring[$pos]);
             }
         }
     
    @@ -254,20 +266,23 @@ public function setUp() {
                 $this->zsets['zset-'.$i] = [$i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E'];
             }
     
    -        global $newRing, $oldRing, $useIndex;
    -        $options = ['previous' => $oldRing, 'index' => $useIndex];
    +        global $new_ring, $old_ring, $useIndex;
    +        $options = [
    +            'previous' => $old_ring,
    +            'index' => $useIndex
    +        ];
             if ($this->getAuth()) {
                 $options['auth'] = $this->getAuth();
             }
             // create array
    -        $this->ra = new RedisArray($newRing, $options);
    +        $this->ra = new RedisArray($new_ring, $options);
             $this->min_version = getMinVersion($this->ra);
         }
     
         public function testFlush() {
             // flush all servers first.
    -        global $serverList;
    -        foreach($serverList as $s) {
    +        global $server_list;
    +        foreach($server_list as $s) {
                 parseHostPort($s, $host, $port);
     
                 $r = new Redis();
    @@ -275,7 +290,7 @@ public function testFlush() {
                 if ($this->getAuth()) {
                     $this->assertTrue($r->auth($this->getAuth()));
                 }
    -            $r->flushdb();
    +            $this->assertTrue($r->flushdb());
             }
         }
     
    @@ -327,28 +342,24 @@ private function readAllvalues() {
             foreach($this->sets as $k => $v) {
                 $ret = $this->ra->smembers($k); // get values
     
    -            // sort sets
    -            sort($v);
    -            sort($ret);
    -
    -            $this->assertTrue($ret == $v);
    +            $this->assertEqualsWeak($v, $ret);
             }
     
             // lists
             foreach($this->lists as $k => $v) {
                 $ret = $this->ra->lrange($k, 0, -1);
    -            $this->assertTrue($ret == $v);
    +            $this->assertEqualsWeak($v, $ret);
             }
     
             // hashes
             foreach($this->hashes as $k => $v) {
                 $ret = $this->ra->hgetall($k); // get values
    -            $this->assertTrue($ret == $v);
    +            $this->assertEqualsWeak($v, $ret);
             }
     
             // sorted sets
             foreach($this->zsets as $k => $v) {
    -            $ret = $this->ra->zrange($k, 0, -1, TRUE); // get values with scores
    +            $ret = $this->ra->zrange($k, 0, -1, TRUE);
     
                 // create assoc array from local dataset
                 $tmp = [];
    @@ -357,16 +368,15 @@ private function readAllvalues() {
                 }
     
                 // compare to RA value
    -            $this->assertTrue($ret == $tmp);
    +            $this->assertEqualsWeak($tmp, $ret);
             }
         }
     
         // add a new node.
         public function testCreateSecondRing() {
    -
    -        global $newRing, $oldRing, $serverList;
    -        $oldRing = $newRing; // back up the original.
    -        $newRing = $serverList; // add a new node to the main ring.
    +        global $new_ring, $old_ring, $server_list;
    +        $old_ring = $new_ring; // back up the original.
    +        $new_ring = $server_list; // add a new node to the main ring.
         }
     
         public function testReadUsingFallbackMechanism() {
    @@ -382,7 +392,7 @@ public function testRehashWithCallback() {
             $this->ra->_rehash(function ($host, $count) use (&$total) {
                 $total += $count;
             });
    -        $this->assertTrue($total > 0);
    +        $this->assertGT(0, $total);
         }
     
         public function testReadRedistributedKeys() {
    @@ -407,13 +417,17 @@ public function setUp() {
                 $this->strings['key-'.$i] = 'val-'.$i;
             }
     
    -        global $newRing, $oldRing, $useIndex;
    -        $options = ['previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE];
    +        global $new_ring, $old_ring, $useIndex;
    +        $options = [
    +            'previous' => $old_ring,
    +            'index' => $useIndex,
    +            'autorehash' => TRUE
    +        ];
             if ($this->getAuth()) {
                 $options['auth'] = $this->getAuth();
             }
             // create array
    -        $this->ra = new RedisArray($newRing, $options);
    +        $this->ra = new RedisArray($new_ring, $options);
             $this->min_version = getMinVersion($this->ra);
         }
     
    @@ -437,9 +451,9 @@ public function testReadAll() {
     
         // add a new node.
         public function testCreateSecondRing() {
    -        global $newRing, $oldRing, $serverList;
    -        $oldRing = $newRing; // back up the original.
    -        $newRing = $serverList; // add a new node to the main ring.
    +        global $new_ring, $old_ring, $server_list;
    +        $old_ring = $new_ring; // back up the original.
    +        $new_ring = $server_list; // add a new node to the main ring.
         }
     
         // Read and migrate keys on fallback, causing the whole ring to be rehashed.
    @@ -458,24 +472,29 @@ public function testAllKeysHaveBeenMigrated() {
                     $this->assertTrue($r->auth($this->getAuth()));
                 }
     
    -            $this->assertEquals($v, $r->get($k));  // check that the key has actually been migrated to the new node.
    +            // check that the key has actually been migrated to the new node.
    +            $this->assertEquals($v, $r->get($k));
             }
         }
     }
     
     // Test node-specific multi/exec
     class Redis_Multi_Exec_Test extends TestSuite {
    -    public $ra = NULL;
         private $min_version;
     
    +    public $ra = NULL;
    +
    +    private static $new_group  = NULL;
    +    private static $new_salary = NULL;
    +
         public function setUp() {
    -        global $newRing, $oldRing, $useIndex;
    -        $options = ['previous' => $oldRing, 'index' => $useIndex];
    +        global $new_ring, $old_ring, $useIndex;
    +        $options = ['previous' => $old_ring, 'index' => $useIndex];
             if ($this->getAuth()) {
                 $options['auth'] = $this->getAuth();
             }
             // create array
    -        $this->ra = new RedisArray($newRing, $options);
    +        $this->ra = new RedisArray($new_ring, $options);
             $this->min_version = getMinVersion($this->ra);
         }
     
    @@ -501,49 +520,47 @@ public function testKeyDistribution() {
         }
     
         public function testMultiExec() {
    -
             // Joe gets a promotion
    -        $newGroup = $this->ra->get('{groups}:executives');
    -        $newSalary = 4000;
    +        self::$new_group  = $this->ra->get('{groups}:executives');
    +        self::$new_salary = 4000;
     
             // change both in a transaction.
    -        $host = $this->ra->_target('{employee:joe}');   // transactions are per-node, so we need a reference to it.
    +        // transactions are per-node, so we need a reference to it.
    +        $host = $this->ra->_target('{employee:joe}');
             $this->ra->multi($host)
    -            ->set('1_{employee:joe}_group', $newGroup)
    -            ->set('1_{employee:joe}_salary', $newSalary)
    +            ->set('1_{employee:joe}_group', self::$new_group)
    +            ->set('1_{employee:joe}_salary', self::$new_salary)
                 ->exec();
     
             // check that the group and salary have been changed
    -        $this->assertEquals($newGroup, $this->ra->get('1_{employee:joe}_group'));
    -        $this->assertEqualsWeak($newSalary, $this->ra->get('1_{employee:joe}_salary'));
    +        $this->assertEquals(self::$new_group, $this->ra->get('1_{employee:joe}_group'));
    +        $this->assertEqualsWeak(self::$new_salary, $this->ra->get('1_{employee:joe}_salary'));
     
         }
     
         public function testMultiExecMSet() {
    -
    -        global $newGroup, $newSalary;
    -        $newGroup = 1;
    -        $newSalary = 10000;
    +        self::$new_group = 1;
    +        self::$new_salary = 10000;
     
             // test MSET, making Joe a top-level executive
             $out = $this->ra->multi($this->ra->_target('{employee:joe}'))
    -                ->mset(['1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary])
    +                ->mset([
    +                    '1_{employee:joe}_group' => self::$new_group,
    +                    '1_{employee:joe}_salary' => self::$new_salary
    +                ])
                     ->exec();
     
             $this->assertTrue($out[0]);
         }
     
         public function testMultiExecMGet() {
    -
    -        global $newGroup, $newSalary;
    -
             // test MGET
             $out = $this->ra->multi($this->ra->_target('{employee:joe}'))
                     ->mget(['1_{employee:joe}_group', '1_{employee:joe}_salary'])
                     ->exec();
     
    -        $this->assertTrue($out[0][0] == $newGroup);
    -        $this->assertTrue($out[0][1] == $newSalary);
    +        $this->assertEqualsWeak(self::$new_group, $out[0][0]);
    +        $this->assertEqualsWeak(self::$new_salary, $out[0][1]);
         }
     
         public function testMultiExecDel() {
    @@ -613,13 +630,17 @@ class Redis_Distributor_Test extends TestSuite {
         private $min_version;
     
         public function setUp() {
    -        global $newRing, $oldRing, $useIndex;
    -        $options = ['previous' => $oldRing, 'index' => $useIndex, 'distributor' => [$this, 'distribute']];
    +        global $new_ring, $old_ring, $useIndex;
    +        $options = [
    +            'previous' => $old_ring,
    +            'index' => $useIndex,
    +            'distributor' => [$this, 'distribute']
    +        ];
             if ($this->getAuth()) {
                 $options['auth'] = $this->getAuth();
             }
             // create array
    -        $this->ra = new RedisArray($newRing, $options);
    +        $this->ra = new RedisArray($new_ring, $options);
             $this->min_version = getMinVersion($this->ra);
         }
     
    @@ -640,28 +661,30 @@ public function distribute($key) {
         }
     
         public function testDistribution() {
    -        $ukServer = $this->ra->_target('{uk}test');
    -        $usServer = $this->ra->_target('{us}test');
    -        $deServer = $this->ra->_target('{de}test');
    -        $defaultServer = $this->ra->_target('unknown');
    +        $UK_server = $this->ra->_target('{uk}test');
    +        $US_server = $this->ra->_target('{us}test');
    +        $DE_server = $this->ra->_target('{de}test');
    +        $XX_server = $this->ra->_target('{xx}test');
     
             $nodes = $this->ra->_hosts();
    -        $this->assertEquals($ukServer, $nodes[0]);
    -        $this->assertEquals($usServer,$nodes[1]);
    -        $this->assertEquals($deServer,$nodes[2]);
    -        $this->assertEquals($defaultServer, $nodes[2]);
    +
    +        $this->assertEquals($UK_server, $nodes[0]);
    +        $this->assertEquals($US_server, $nodes[1]);
    +        $this->assertEquals($DE_server, $nodes[2]);
    +        $this->assertEquals($XX_server, $nodes[2]);
         }
     }
     
    -function run_tests($className, $str_filter, $str_host, $auth) {
    -        // reset rings
    -        global $newRing, $oldRing, $serverList;
    +function run_ra_tests($test_class, $filter, $host, array $full_ring,
    +                      array $sub_ring, $auth)
    +{
    +    global $new_ring, $old_ring, $server_list;
    +
    +    $server_list = $full_ring;
    +    $new_ring    = $sub_ring;
    +    $old_ring    = [];
     
    -        $newRing = ["$str_host:6379", "$str_host:6380", "$str_host:6381"];
    -        $oldRing = [];
    -        $serverList = ["$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"];
    -        // run
    -        return TestSuite::run($className, $str_filter, $str_host, NULL, $auth);
    +    return TestSuite::run($test_class, $filter, $host, NULL, $auth);
     }
     
     ?>
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index f4f0392ff5..b0170f69cc 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -7,7 +7,7 @@
      * where we're validating specific cluster mechanisms
      */
     class Redis_Cluster_Test extends Redis_Test {
    -    private $_arr_redis_types = [
    +    private $redis_types = [
             Redis::REDIS_STRING,
             Redis::REDIS_SET,
             Redis::REDIS_LIST,
    @@ -15,13 +15,17 @@ class Redis_Cluster_Test extends Redis_Test {
             Redis::REDIS_HASH
         ];
     
    -    private $_arr_failover_types = [
    +    private $failover_types = [
             RedisCluster::FAILOVER_NONE,
             RedisCluster::FAILOVER_ERROR,
             RedisCluster::FAILOVER_DISTRIBUTE
         ];
     
    -    protected static $_arr_node_map = [];
    +    protected static array $seeds = [];
    +
    +    private static array  $seed_messages = [];
    +    private static string $seed_source = '';
    +
     
         /* Tests we'll skip all together in the context of RedisCluster.  The
          * RedisCluster class doesn't implement specialized (non-redis) commands
    @@ -61,37 +65,85 @@ public function testSession_defaultLockRetryCount() { $this->markTestSkipped();
         public function testSession_noUnlockOfOtherProcess() { $this->markTestSkipped(); }
         public function testSession_lockWaitTime() { $this->markTestSkipped(); }
     
    -    /* Load our seeds on construction */
    -    public function __construct($str_host, $i_port, $str_auth) {
    -        parent::__construct($str_host, $i_port, $str_auth);
    +    private function loadSeedsFromHostPort($host, $port) {
    +        try {
    +            $rc = new RedisCluster(NULL, ["$host:$port"], 1, 1, true, $this->getAuth());
    +            self::$seed_source = "Host: $host, Port: $port";
    +            return array_map(function($master) {
    +                return sprintf('%s:%s', $master[0], $master[1]);
    +            }, $rc->_masters());
    +        } catch (Exception $ex) {
    +            /* fallthrough */
    +        }
     
    -        self::$_arr_node_map = array_filter(explode(' ', getenv('REDIS_CLUSTER_NODES')));
    -        /* Store our node map */
    -        if (!self::$_arr_node_map) {
    -            $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap';
    +        self::$seed_messages[] = "--host=$host, --port=$port";
     
    -            if (!file_exists($str_nodemap_file)) {
    -                fprintf(STDERR, "Error:  Can't find nodemap file for seeds!\n");
    -                exit(1);
    -            }
    +        return false;
    +    }
    +
    +    private function loadSeedsFromEnv() {
    +        $seeds = getenv('REDIS_CLUSTER_NODES');
    +        if ( ! $seeds) {
    +            self::$seed_messages[] = "environment variable REDIS_CLUSTER_NODES ($seeds)";
    +            return false;
    +        }
    +
    +        self::$seed_source = 'Environment variable REDIS_CLUSTER_NODES';
    +        return array_filter(explode(' ', $seeds));
    +    }
     
    -            self::$_arr_node_map = array_filter(
    -                explode("\n", file_get_contents($str_nodemap_file)
    -            ));
    +    private function loadSeedsFromNodeMap() {
    +        $nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap';
    +        if ( ! file_exists($nodemap_file)) {
    +            self::$seed_messages[] = "nodemap file '$nodemap_file'";
    +            return false;
             }
    +
    +        self::$seed_source = "Nodemap file '$nodemap_file'";
    +        return array_filter(explode("\n", file_get_contents($nodemap_file)));
    +    }
    +
    +    private function loadSeeds($host, $port) {
    +        if (($seeds = $this->loadSeedsFromNodeMap()))
    +            return $seeds;
    +        if (($seeds = $this->loadSeedsFromEnv()))
    +            return $seeds;
    +        if (($seeds = $this->loadSeedsFromHostPort($host, $port)))
    +            return $seeds;
    +
    +        fprintf(STDERR, "Error:  Unable to load seeds for RedisCluster tests\n");
    +        foreach (self::$seed_messages as $msg) {
    +            fprintf(STDERR, "   Tried: %s\n", $msg);
    +        }
    +
    +        exit(1);
    +    }
    +
    +    /* Load our seeds on construction */
    +    public function __construct($host, $port, $auth) {
    +        parent::__construct($host, $port, $auth);
    +
    +        self::$seeds = $this->loadSeeds($host, $port);
         }
     
         /* Override setUp to get info from a specific node */
         public function setUp() {
    -        $this->redis = $this->newInstance();
    -        $info = $this->redis->info(uniqid());
    -        $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0');
    -        $this->is_keydb = $this->redis->info('keydb') !== false;
    +        $this->redis    = $this->newInstance();
    +        $info           = $this->redis->info(uniqid());
    +        $this->version  = $info['redis_version'] ?? '0.0.0';
    +        $this->is_keydb = $this->detectKeyDB($info);
         }
     
         /* Override newInstance as we want a RedisCluster object */
         protected function newInstance() {
    -        return new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth());
    +        try {
    +            return new RedisCluster(NULL, self::$seeds, 30, 30, true, $this->getAuth());
    +        } catch (Exception $ex) {
    +            fprintf(STDERR, "Fatal error: %s\n", $ex->getMessage());
    +            fprintf(STDERR, "Seeds: %s\n", implode(' ', self::$seeds));
    +            fprintf(STDERR, "Seed source: %s\n", self::$seed_source);
    +            exit(1);
    +        }
         }
     
         /* Overrides for RedisTest where the function signature is different.  This
    @@ -107,7 +159,7 @@ public function testPing() {
             /* Make sure both variations work in MULTI mode */
             $this->redis->multi();
             $this->redis->ping('{ping-test}');
    -        $this->redis->ping('{ping-test}','BEEP');
    +        $this->redis->ping('{ping-test}', 'BEEP');
             $this->assertEquals([true, 'BEEP'], $this->redis->exec());
         }
     
    @@ -138,7 +190,7 @@ public function testSortPrefix() {
             $this->redis->sadd('some-item', 2);
             $this->redis->sadd('some-item', 3);
     
    -        $this->assertEquals(['1','2','3'], $this->redis->sort('some-item'));
    +        $this->assertEquals(['1', '2', '3'], $this->redis->sort('some-item'));
     
             // Kill our set/prefix
             $this->redis->del('some-item');
    @@ -147,15 +199,15 @@ public function testSortPrefix() {
     
         public function testDBSize() {
             for ($i = 0; $i < 10; $i++) {
    -            $str_key = "key:$i";
    -            $this->assertTrue($this->redis->flushdb($str_key));
    -            $this->redis->set($str_key, "val:$i");
    -            $this->assertEquals(1, $this->redis->dbsize($str_key));
    +            $key = "key:$i";
    +            $this->assertTrue($this->redis->flushdb($key));
    +            $this->redis->set($key, "val:$i");
    +            $this->assertEquals(1, $this->redis->dbsize($key));
             }
         }
     
         public function testInfo() {
    -        $arr_check_keys = [
    +        $fields = [
                 "redis_version", "arch_bits", "uptime_in_seconds", "uptime_in_days",
                 "connected_clients", "connected_slaves", "used_memory",
                 "total_connections_received", "total_commands_processed",
    @@ -163,113 +215,113 @@ public function testInfo() {
             ];
     
             for ($i = 0; $i < 3; $i++) {
    -            $arr_info = $this->redis->info("k:$i");
    -            foreach ($arr_check_keys as $str_check_key) {
    -                $this->assertTrue(isset($arr_info[$str_check_key]));
    +            $info = $this->redis->info($i);
    +            foreach ($fields as $field) {
    +                $this->assertArrayKey($info, $field);
                 }
             }
         }
     
         public function testClient() {
    -        $str_key = 'key-' . rand(1,100);
    +        $key = 'key-' . rand(1, 100);
     
    -        $this->assertTrue($this->redis->client($str_key, 'setname', 'cluster_tests'));
    +        $this->assertTrue($this->redis->client($key, 'setname', 'cluster_tests'));
     
    -        $arr_clients = $this->redis->client($str_key, 'list');
    -        $this->assertTrue(is_array($arr_clients));
    +        $clients = $this->redis->client($key, 'list');
    +        $this->assertIsArray($clients);
     
             /* Find us in the list */
    -        $str_addr = NULL;
    -        foreach ($arr_clients as $arr_client) {
    -            if ($arr_client['name'] == 'cluster_tests') {
    -                $str_addr = $arr_client['addr'];
    +        $addr = NULL;
    +        foreach ($clients as $client) {
    +            if ($client['name'] == 'cluster_tests') {
    +                $addr = $client['addr'];
                     break;
                 }
             }
     
             /* We should be in there */
    -        $this->assertFalse(empty($str_addr));
    +        $this->assertIsString($addr);
     
             /* Kill our own client! */
    -        $this->assertTrue($this->redis->client($str_key, 'kill', $str_addr));
    +        $this->assertTrue($this->redis->client($key, 'kill', $addr));
         }
     
         public function testTime() {
    -        $time_arr = $this->redis->time("k:" . rand(1,100));
    -        $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 &&
    -                          strval(intval($time_arr[0])) === strval($time_arr[0]) &&
    -                          strval(intval($time_arr[1])) === strval($time_arr[1]));
    +        [$sec, $usec] = $this->redis->time(uniqid());
    +        $this->assertEquals(strval(intval($sec)), strval($sec));
    +        $this->assertEquals(strval(intval($usec)), strval($usec));
         }
     
         public function testScan() {
    -        $i_key_count = 0;
    -        $i_scan_count = 0;
    +        $key_count = 0;
    +        $scan_count = 0;
     
             /* Have scan retry for us */
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
     
             /* Iterate over our masters, scanning each one */
    -        foreach ($this->redis->_masters() as $arr_master) {
    +        foreach ($this->redis->_masters() as $master) {
                 /* Grab the number of keys we have */
    -            $i_key_count += $this->redis->dbsize($arr_master);
    +            $key_count += $this->redis->dbsize($master);
     
                 /* Scan the keys here */
                 $it = NULL;
    -            while ($arr_keys = $this->redis->scan($it, $arr_master)) {
    -                $i_scan_count += count($arr_keys);
    +            while ($keys = $this->redis->scan($it, $master)) {
    +                $scan_count += count($keys);
                 }
             }
     
             /* Our total key count should match */
    -        $this->assertEquals($i_scan_count, $i_key_count);
    +        $this->assertEquals($scan_count, $key_count);
         }
     
         public function testScanPrefix() {
    -        $arr_prefixes = ['prefix-a:', 'prefix-b:'];
    -        $str_id = uniqid();
    +        $prefixes = ['prefix-a:', 'prefix-b:'];
    +        $id = uniqid();
     
             $arr_keys = [];
    -        foreach ($arr_prefixes as $str_prefix) {
    -            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
    -            $this->redis->set($str_id, "LOLWUT");
    -            $arr_keys[$str_prefix] = $str_id;
    +        foreach ($prefixes as $prefix) {
    +            $this->redis->setOption(Redis::OPT_PREFIX, $prefix);
    +            $this->redis->set($id, "LOLWUT");
    +            $arr_keys[$prefix] = $id;
             }
     
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);
     
    -        foreach ($arr_prefixes as $str_prefix) {
    -            $arr_prefix_keys = [];
    -            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
    +        foreach ($prefixes as $prefix) {
    +            $prefix_keys = [];
    +            $this->redis->setOption(Redis::OPT_PREFIX, $prefix);
     
    -            foreach ($this->redis->_masters() as $arr_master) {
    +            foreach ($this->redis->_masters() as $master) {
                     $it = NULL;
    -                while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) {
    -                    foreach ($arr_iter as $str_key) {
    -                        $arr_prefix_keys[$str_prefix] = $str_key;
    +                while ($keys = $this->redis->scan($it, $master, "*$id*")) {
    +                    foreach ($keys as $key) {
    +                        $prefix_keys[$prefix] = $key;
                         }
                     }
                 }
     
    -            $this->assertTrue(count($arr_prefix_keys) == 1 && isset($arr_prefix_keys[$str_prefix]));
    +            $this->assertIsArray($prefix_keys, 1);
    +            $this->assertArrayKey($prefix_keys, $prefix);
             }
     
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);
     
    -        $arr_scan_keys = [];
    +        $scan_keys = [];
     
    -        foreach ($this->redis->_masters() as $arr_master) {
    +        foreach ($this->redis->_masters() as $master) {
                 $it = NULL;
    -            while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) {
    -                foreach ($arr_iter as $str_key) {
    -                    $arr_scan_keys[] = $str_key;
    +            while ($keys = $this->redis->scan($it, $master, "*$id*")) {
    +                foreach ($keys as $key) {
    +                    $scan_keys[] = $key;
                     }
                 }
             }
     
             /* We should now have both prefixs' keys */
    -        foreach ($arr_keys as $str_prefix => $str_id) {
    -            $this->assertTrue(in_array("{$str_prefix}{$str_id}", $arr_scan_keys));
    +        foreach ($arr_keys as $prefix => $id) {
    +            $this->assertInArray("{$prefix}{$id}", $scan_keys);
             }
         }
     
    @@ -278,19 +330,19 @@ public function testScanPrefix() {
         public function testPubSub() {
             // PUBSUB CHANNELS ...
             $result = $this->redis->pubsub("somekey", "channels", "*");
    -        $this->assertTrue(is_array($result));
    +        $this->assertIsArray($result);
             $result = $this->redis->pubsub("somekey", "channels");
    -        $this->assertTrue(is_array($result));
    +        $this->assertIsArray($result);
     
             // PUBSUB NUMSUB
     
    -        $c1 = '{pubsub}-' . rand(1,100);
    -        $c2 = '{pubsub}-' . rand(1,100);
    +        $c1 = '{pubsub}-' . rand(1, 100);
    +        $c2 = '{pubsub}-' . rand(1, 100);
     
             $result = $this->redis->pubsub("{pubsub}", "numsub", $c1, $c2);
     
             // Should get an array back, with two elements
    -        $this->assertTrue(is_array($result));
    +        $this->assertIsArray($result);
             $this->assertEquals(4, count($result));
     
             $arr_zipped = [];
    @@ -301,13 +353,13 @@ public function testPubSub() {
     
             // Make sure the elements are correct, and have zero counts
             foreach([$c1,$c2] as $channel) {
    -            $this->assertTrue(isset($result[$channel]));
    +            $this->assertArrayKey($result, $channel);
                 $this->assertEquals(0, $result[$channel]);
             }
     
             // PUBSUB NUMPAT
             $result = $this->redis->pubsub("somekey", "numpat");
    -        $this->assertTrue(is_int($result));
    +        $this->assertIsInt($result);
     
             // Invalid call
             $this->assertFalse($this->redis->pubsub("somekey", "notacommand"));
    @@ -317,15 +369,15 @@ public function testPubSub() {
          * be set, but rather will only fail per-node when that is the case */
         public function testMSetNX() {
             /* All of these keys should get set */
    -        $this->redis->del('x','y','z');
    -        $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']);
    -        $this->assertTrue(is_array($ret));
    +        $this->redis->del('x', 'y', 'z');
    +        $ret = $this->redis->msetnx(['x'=>'a', 'y'=>'b', 'z'=>'c']);
    +        $this->assertIsArray($ret);
             $this->assertEquals(array_sum($ret),count($ret));
     
             /* Delete one key */
             $this->redis->del('x');
    -        $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']);
    -        $this->assertTrue(is_array($ret));
    +        $ret = $this->redis->msetnx(['x'=>'a', 'y'=>'b', 'z'=>'c']);
    +        $this->assertIsArray($ret);
             $this->assertEquals(1, array_sum($ret));
     
             $this->assertFalse($this->redis->msetnx([])); // set ø → FALSE
    @@ -333,24 +385,23 @@ public function testMSetNX() {
     
         /* Slowlog needs to take a key or [ip, port], to direct it to a node */
         public function testSlowlog() {
    -        $str_key = uniqid() . '-' . rand(1, 1000);
    +        $key = uniqid() . '-' . rand(1, 1000);
     
    -        $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get')));
    -        $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get', 10)));
    -        $this->assertTrue(is_int($this->redis->slowlog($str_key, 'len')));
    -        $this->assertTrue($this->redis->slowlog($str_key, 'reset'));
    -        $this->assertFalse($this->redis->slowlog($str_key, 'notvalid'));
    +        $this->assertIsArray($this->redis->slowlog($key, 'get'));
    +        $this->assertIsArray($this->redis->slowlog($key, 'get', 10));
    +        $this->assertIsInt($this->redis->slowlog($key, 'len'));
    +        $this->assertTrue($this->redis->slowlog($key, 'reset'));
    +        $this->assertFalse($this->redis->slowlog($key, 'notvalid'));
         }
     
         /* INFO COMMANDSTATS requires a key or ip:port for node direction */
         public function testInfoCommandStats() {
    -        $str_key = uniqid() . '-' . rand(1,1000);
    -        $arr_info = $this->redis->info($str_key, "COMMANDSTATS");
    +        $info = $this->redis->info(uniqid(), "COMMANDSTATS");
     
    -        $this->assertTrue(is_array($arr_info));
    -        if (is_array($arr_info)) {
    -            foreach($arr_info as $k => $str_value) {
    -                $this->assertTrue(strpos($k, 'cmdstat_') !== false);
    +        $this->assertIsArray($info);
    +        if (is_array($info)) {
    +            foreach($info as $k => $value) {
    +                $this->assertStringContains('cmdstat_', $k);
                 }
             }
         }
    @@ -379,13 +430,10 @@ public function testFailedTransactions() {
             $this->assertEquals(['44'], $ret);
         }
     
    -    public function testDiscard()
    -    {
    -        /* start transaction */
    +    public function testDiscard() {
             $this->redis->multi();
    -
    -        /* Set and get in our transaction */
    -        $this->redis->set('pipecount','over9000')->get('pipecount');
    +        $this->redis->set('pipecount', 'over9000');
    +        $this->redis->get('pipecount');
     
             $this->assertTrue($this->redis->discard());
         }
    @@ -393,10 +441,10 @@ public function testDiscard()
         /* RedisCluster::script() is a 'raw' command, which requires a key such that
          * we can direct it to a given node */
         public function testScript() {
    -        $str_key = uniqid() . '-' . rand(1,1000);
    +        $key = uniqid() . '-' . rand(1, 1000);
     
             // Flush any scripts we have
    -        $this->assertTrue($this->redis->script($str_key, 'flush'));
    +        $this->assertTrue($this->redis->script($key, 'flush'));
     
             // Silly scripts to test against
             $s1_src = 'return 1';
    @@ -407,31 +455,31 @@ public function testScript() {
             $s3_sha = sha1($s3_src);
     
             // None should exist
    -        $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha);
    -        $this->assertTrue(is_array($result) && count($result) == 3);
    +        $result = $this->redis->script($key, 'exists', $s1_sha, $s2_sha, $s3_sha);
    +        $this->assertIsArray($result, 3);
             $this->assertTrue(is_array($result) && count(array_filter($result)) == 0);
     
             // Load them up
    -        $this->assertTrue($this->redis->script($str_key, 'load', $s1_src) == $s1_sha);
    -        $this->assertTrue($this->redis->script($str_key, 'load', $s2_src) == $s2_sha);
    -        $this->assertTrue($this->redis->script($str_key, 'load', $s3_src) == $s3_sha);
    +        $this->assertEquals($s1_sha, $this->redis->script($key, 'load', $s1_src));
    +        $this->assertEquals($s2_sha, $this->redis->script($key, 'load', $s2_src));
    +        $this->assertEquals($s3_sha, $this->redis->script($key, 'load', $s3_src));
     
             // They should all exist
    -        $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha);
    +        $result = $this->redis->script($key, 'exists', $s1_sha, $s2_sha, $s3_sha);
             $this->assertTrue(is_array($result) && count(array_filter($result)) == 3);
         }
     
         /* RedisCluster::EVALSHA needs a 'key' to let us know which node we want to
          * direct the command at */
         public function testEvalSHA() {
    -        $str_key = uniqid() . '-' . rand(1,1000);
    +        $key = uniqid() . '-' . rand(1, 1000);
     
             // Flush any loaded scripts
    -        $this->redis->script($str_key, 'flush');
    +        $this->redis->script($key, 'flush');
     
             // Non existent script (but proper sha1), and a random (not) sha1 string
    -        $this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$str_key], 1));
    -        $this->assertFalse($this->redis->evalsha('some-random-data'),[$str_key], 1);
    +        $this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$key], 1));
    +        $this->assertFalse($this->redis->evalsha('some-random-data'),[$key], 1);
     
             // Load a script
             $cb  = uniqid(); // To ensure the script is new
    @@ -439,69 +487,69 @@ public function testEvalSHA() {
             $sha = sha1($scr);
     
             // Run it when it doesn't exist, run it with eval, and then run it with sha1
    -        $this->assertFalse($this->redis->evalsha($scr,[$str_key], 1));
    -        $this->assertEquals(1, $this->redis->eval($scr,[$str_key], 1));
    -        $this->assertEquals(1, $this->redis->evalsha($sha,[$str_key], 1));
    +        $this->assertFalse($this->redis->evalsha($scr,[$key], 1));
    +        $this->assertEquals(1, $this->redis->eval($scr,[$key], 1));
    +        $this->assertEquals(1, $this->redis->evalsha($sha,[$key], 1));
         }
     
         public function testEvalBulkResponse() {
    -        $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
    -        $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';
    +        $key1 = uniqid() . '-' . rand(1, 1000) . '{hash}';
    +        $key2 = uniqid() . '-' . rand(1, 1000) . '{hash}';
     
    -        $this->redis->script($str_key1, 'flush');
    -        $this->redis->script($str_key2, 'flush');
    +        $this->redis->script($key1, 'flush');
    +        $this->redis->script($key2, 'flush');
     
             $scr = "return {KEYS[1],KEYS[2]}";
     
    -        $result = $this->redis->eval($scr,[$str_key1, $str_key2], 2);
    +        $result = $this->redis->eval($scr,[$key1, $key2], 2);
     
    -        $this->assertEquals($str_key1, $result[0]);
    -        $this->assertEquals($str_key2, $result[1]);
    +        $this->assertEquals($key1, $result[0]);
    +        $this->assertEquals($key2, $result[1]);
         }
     
         public function testEvalBulkResponseMulti() {
    -        $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
    -        $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';
    +        $key1 = uniqid() . '-' . rand(1, 1000) . '{hash}';
    +        $key2 = uniqid() . '-' . rand(1, 1000) . '{hash}';
     
    -        $this->redis->script($str_key1, 'flush');
    -        $this->redis->script($str_key2, 'flush');
    +        $this->redis->script($key1, 'flush');
    +        $this->redis->script($key2, 'flush');
     
             $scr = "return {KEYS[1],KEYS[2]}";
     
             $this->redis->multi();
    -        $this->redis->eval($scr, [$str_key1, $str_key2], 2);
    +        $this->redis->eval($scr, [$key1, $key2], 2);
     
             $result = $this->redis->exec();
     
    -        $this->assertEquals($str_key1, $result[0][0]);
    -        $this->assertEquals($str_key2, $result[0][1]);
    +        $this->assertEquals($key1, $result[0][0]);
    +        $this->assertEquals($key2, $result[0][1]);
         }
     
         public function testEvalBulkEmptyResponse() {
    -        $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
    -        $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';
    +        $key1 = uniqid() . '-' . rand(1, 1000) . '{hash}';
    +        $key2 = uniqid() . '-' . rand(1, 1000) . '{hash}';
     
    -        $this->redis->script($str_key1, 'flush');
    -        $this->redis->script($str_key2, 'flush');
    +        $this->redis->script($key1, 'flush');
    +        $this->redis->script($key2, 'flush');
     
             $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end";
     
    -        $result = $this->redis->eval($scr, [$str_key1, $str_key2], 2);
    +        $result = $this->redis->eval($scr, [$key1, $key2], 2);
     
             $this->assertNull($result);
         }
     
         public function testEvalBulkEmptyResponseMulti() {
    -        $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
    -        $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';
    +        $key1 = uniqid() . '-' . rand(1, 1000) . '{hash}';
    +        $key2 = uniqid() . '-' . rand(1, 1000) . '{hash}';
     
    -        $this->redis->script($str_key1, 'flush');
    -        $this->redis->script($str_key2, 'flush');
    +        $this->redis->script($key1, 'flush');
    +        $this->redis->script($key2, 'flush');
     
             $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end";
     
             $this->redis->multi();
    -        $this->redis->eval($scr, [$str_key1, $str_key2], 2);
    +        $this->redis->eval($scr, [$key1, $key2], 2);
             $result = $this->redis->exec();
     
             $this->assertNull($result[0]);
    @@ -509,84 +557,90 @@ public function testEvalBulkEmptyResponseMulti() {
     
         /* Cluster specific introspection stuff */
         public function testIntrospection() {
    -        $arr_masters = $this->redis->_masters();
    -        $this->assertTrue(is_array($arr_masters));
    +        $primaries = $this->redis->_masters();
    +        $this->assertIsArray($primaries);
     
    -        foreach ($arr_masters as $arr_info) {
    -            $this->assertIsArray($arr_info);
    -            $this->assertIsString($arr_info[0]);
    -            $this->assertIsInt($arr_info[1]);
    +        foreach ($primaries as [$host, $port]) {
    +            $this->assertIsString($host);
    +            $this->assertIsInt($port);
             }
         }
     
    -    protected function genKeyName($i_key_idx, $i_type) {
    -        switch ($i_type) {
    +    protected function keyTypeToString($key_type) {
    +        switch ($key_type) {
                 case Redis::REDIS_STRING:
    -                return "string-$i_key_idx";
    +                return "string";
                 case Redis::REDIS_SET:
    -                return "set-$i_key_idx";
    +                return "set";
                 case Redis::REDIS_LIST:
    -                return "list-$i_key_idx";
    +                return "list";
                 case Redis::REDIS_ZSET:
    -                return "zset-$i_key_idx";
    +                return "zset";
                 case Redis::REDIS_HASH:
    -                return "hash-$i_key_idx";
    +                return "hash";
    +            case Redis::REDIS_STREAM:
    +                return "stream";
                 default:
    -                return "unknown-$i_key_idx";
    +                return "unknown($key_type)";
             }
    +
         }
     
    -    protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) {
    -        $str_key = $this->genKeyName($i_key_idx, $i_type);
    +    protected function genKeyName($key_index, $key_type) {
    +        return sprintf('%s-%s', $this->keyTypeToString($key_type), $key_index);
    +    }
    +
    +    protected function setKeyVals($key_index, $key_type, &$arr_ref) {
    +        $key = $this->genKeyName($key_index, $key_type);
     
    -        $this->redis->del($str_key);
    +        $this->redis->del($key);
     
    -        switch ($i_type) {
    +        switch ($key_type) {
                 case Redis::REDIS_STRING:
    -                $value = "$str_key-value";
    -                $this->redis->set($str_key, $value);
    +                $value = "$key-value";
    +                $this->redis->set($key, $value);
                     break;
                 case Redis::REDIS_SET:
                     $value = [
    -                    $str_key . '-mem1', $str_key . '-mem2', $str_key . '-mem3',
    -                    $str_key . '-mem4', $str_key . '-mem5', $str_key . '-mem6'
    +                    "$key-mem1", "$key-mem2", "$key-mem3",
    +                    "$key-mem4", "$key-mem5", "$key-mem6"
                     ];
    -                $arr_args = $value;
    -                array_unshift($arr_args, $str_key);
    -                call_user_func_array([$this->redis, 'sadd'], $arr_args);
    +                $args = $value;
    +                array_unshift($args, $key);
    +                call_user_func_array([$this->redis, 'sadd'], $args);
                     break;
                 case Redis::REDIS_HASH:
                     $value = [
    -                    $str_key . '-mem1' => $str_key . '-val1',
    -                    $str_key . '-mem2' => $str_key . '-val2',
    -                    $str_key . '-mem3' => $str_key . '-val3'
    +                    "$key-mem1" => "$key-val1",
    +                    "$key-mem2" => "$key-val2",
    +                    "$key-mem3" => "$key-val3"
                     ];
    -                $this->redis->hmset($str_key, $value);
    +                $this->redis->hmset($key, $value);
                     break;
                 case Redis::REDIS_LIST:
                     $value = [
    -                    $str_key . '-ele1', $str_key . '-ele2', $str_key . '-ele3',
    -                    $str_key . '-ele4', $str_key . '-ele5', $str_key . '-ele6'
    +                    "$key-ele1", "$key-ele2", "$key-ele3",
    +                    "$key-ele4", "$key-ele5", "$key-ele6"
                     ];
    -                $arr_args = $value;
    -                array_unshift($arr_args, $str_key);
    -                call_user_func_array([$this->redis, 'rpush'], $arr_args);
    +                $args = $value;
    +                array_unshift($args, $key);
    +                call_user_func_array([$this->redis, 'rpush'], $args);
                     break;
                 case Redis::REDIS_ZSET:
    -                $i_score = 1;
    +                $score = 1;
                     $value = [
    -                    $str_key . '-mem1' => 1, $str_key . '-mem2' => 2,
    -                    $str_key . '-mem3' => 3, $str_key . '-mem3' => 3
    +                    "$key-mem1" => 1, "$key-mem2" => 2,
    +                    "$key-mem3" => 3, "$key-mem3" => 3
                     ];
    -                foreach ($value as $str_mem => $i_score) {
    -                    $this->redis->zadd($str_key, $i_score, $str_mem);
    +                foreach ($value as $mem => $score) {
    +                    $this->redis->zadd($key, $score, $mem);
                     }
                     break;
             }
     
             /* Update our reference array so we can verify values */
    -        $arr_ref[$str_key] = $value;
    -        return $str_key;
    +        $arr_ref[$key] = $value;
    +        return $key;
         }
     
         /* Verify that our ZSET values are identical */
    @@ -603,51 +657,51 @@ protected function checkZSetEquality($a, $b) {
             }
         }
     
    -    protected function checkKeyValue($str_key, $i_type, $value) {
    -        switch ($i_type) {
    +    protected function checkKeyValue($key, $key_type, $value) {
    +        switch ($key_type) {
                 case Redis::REDIS_STRING:
    -                $this->assertEquals($value, $this->redis->get($str_key));
    +                $this->assertEquals($value, $this->redis->get($key));
                     break;
                 case Redis::REDIS_SET:
    -                $arr_r_values = $this->redis->sMembers($str_key);
    +                $arr_r_values = $this->redis->sMembers($key);
                     $arr_l_values = $value;
                     sort($arr_r_values);
                     sort($arr_l_values);
                     $this->assertEquals($arr_r_values, $arr_l_values);
                     break;
                 case Redis::REDIS_LIST:
    -                $this->assertEquals($value, $this->redis->lrange($str_key,0,-1));
    +                $this->assertEquals($value, $this->redis->lrange($key, 0, -1));
                     break;
                 case Redis::REDIS_HASH:
    -                $this->assertEquals($value, $this->redis->hgetall($str_key));
    +                $this->assertEquals($value, $this->redis->hgetall($key));
                     break;
                 case Redis::REDIS_ZSET:
    -                $this->checkZSetEquality($value, $this->redis->zrange($str_key,0,-1,true));
    +                $this->checkZSetEquality($value, $this->redis->zrange($key, 0, -1, true));
                     break;
                 default:
    -                throw new Exception("Unknown type " . $i_type);
    +                throw new Exception("Unknown type " . $key_type);
             }
         }
     
         /* Test automatic load distributor */
         public function testFailOver() {
    -        $arr_value_ref = [];
    -        $arr_type_ref  = [];
    +        $value_ref = [];
    +        $type_ref  = [];
     
             /* Set a bunch of keys of various redis types*/
             for ($i = 0; $i < 200; $i++) {
    -            foreach ($this->_arr_redis_types as $i_type) {
    -                $str_key = $this->setKeyVals($i, $i_type, $arr_value_ref);
    -                $arr_type_ref[$str_key] = $i_type;
    +            foreach ($this->redis_types as $type) {
    +                $key = $this->setKeyVals($i, $type, $value_ref);
    +                $type_ref[$key] = $type;
                 }
             }
     
             /* Iterate over failover options */
    -        foreach ($this->_arr_failover_types as $i_opt) {
    -            $this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $i_opt);
    +        foreach ($this->failover_types as $failover_type) {
    +            $this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $failover_type);
     
    -            foreach ($arr_value_ref as $str_key => $value) {
    -                $this->checkKeyValue($str_key, $arr_type_ref[$str_key], $value);
    +            foreach ($value_ref as $key => $value) {
    +                $this->checkKeyValue($key, $type_ref[$key], $value);
                 }
     
                 break;
    @@ -660,8 +714,8 @@ public function testRawCommand() {
             $this->assertEquals('my-value', $this->redis->get('mykey'));
     
             $this->redis->del('mylist');
    -        $this->redis->rpush('mylist', 'A','B','C','D');
    -        $this->assertEquals(['A','B','C','D'], $this->redis->lrange('mylist', 0, -1));
    +        $this->redis->rpush('mylist', 'A', 'B', 'C', 'D');
    +        $this->assertEquals(['A', 'B', 'C', 'D'], $this->redis->lrange('mylist', 0, -1));
         }
     
         protected function rawCommandArray($key, $args) {
    @@ -718,8 +772,8 @@ public function testSlotCache() {
     
             $pong = 0;
             for ($i = 0; $i < 10; $i++) {
    -            $obj_rc = $this->newInstance();
    -            $pong += $obj_rc->ping("key:$i");
    +            $new_client = $this->newInstance();
    +            $pong += $new_client->ping("key:$i");
             }
     
             $this->assertEquals($pong, $i);
    @@ -734,8 +788,8 @@ public function testConnectionPool() {
     
             $pong = 0;
             for ($i = 0; $i < 10; $i++) {
    -            $obj_rc = $this->newInstance();
    -            $pong += $obj_rc->ping("key:$i");
    +            $new_client = $this->newInstance();
    +            $pong += $new_client->ping("key:$i");
             }
     
             $this->assertEquals($pong, $i);
    @@ -756,7 +810,7 @@ protected function sessionSaveHandler(): string {
         protected function sessionSavePath(): string {
             return implode('&', array_map(function ($host) {
                 return 'seed[]=' . $host;
    -        }, self::$_arr_node_map)) . '&' . $this->getAuthFragment();
    +        }, self::$seeds)) . '&' . $this->getAuthFragment();
         }
     
         /* Test correct handling of null multibulk replies */
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 151dda9ff1..dc04f31f3e 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -61,11 +61,17 @@ protected function getRightConstant() {
             return Redis::RIGHT;
         }
     
    +    protected function detectKeyDB(array $info) {
    +        return strpos($info['executable'] ?? '', 'keydb') !== false ||
    +               isset($info['keydb']) ||
    +               isset($info['mvcc_depth']);
    +    }
    +
         public function setUp() {
             $this->redis = $this->newInstance();
             $info = $this->redis->info();
             $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0');
    -        $this->is_keydb = $this->redis->info('keydb') !== false;
    +        $this->is_keydb = $this->detectKeyDB($info);
         }
     
         protected function minVersionCheck($version) {
    @@ -125,14 +131,14 @@ protected function newInstance() {
                 'port' => $this->getPort(),
             ]);
     
    -        if($this->getAuth()) {
    +        if ($this->getAuth()) {
                 $this->assertTrue($r->auth($this->getAuth()));
             }
             return $r;
         }
     
         public function tearDown() {
    -        if($this->redis) {
    +        if ($this->redis) {
                 $this->redis->close();
             }
         }
    @@ -152,7 +158,6 @@ protected function haveMulti() {
         }
     
         public function testMinimumVersion() {
    -        // Minimum server version required for tests
             $this->assertTrue(version_compare($this->version, '2.4.0') >= 0);
         }
     
    @@ -198,8 +203,8 @@ public function testPubSub() {
     
             // PUBSUB NUMSUB
     
    -        $c1 = uniqid() . '-' . rand(1,100);
    -        $c2 = uniqid() . '-' . rand(1,100);
    +        $c1 = uniqid() . '-' . rand(1, 100);
    +        $c2 = uniqid() . '-' . rand(1, 100);
     
             $result = $this->redis->pubsub('numsub', [$c1, $c2]);
     
    @@ -208,7 +213,7 @@ public function testPubSub() {
             $this->assertEquals(2, count($result));
     
             // Make sure the elements are correct, and have zero counts
    -        foreach([$c1,$c2] as $channel) {
    +        foreach ([$c1,$c2] as $channel) {
                 $this->assertArrayKeyEquals($result, $channel, 0);
             }
     
    @@ -260,7 +265,7 @@ public function testBitop() {
             // Make sure RedisCluster doesn't even send the command.  We don't care
             // about what Redis returns
             @$this->redis->bitop('AND', 'key1', 'key2', 'key3');
    -        $this->assertEquals(NULL, $this->redis->getLastError());
    +        $this->assertNull($this->redis->getLastError());
     
             $this->redis->del('{key}1', '{key}2');
         }
    @@ -273,7 +278,7 @@ public function testBitsets() {
             $this->assertEquals(0, $this->redis->getBit('key', 100000));
     
             $this->redis->set('key', "\xff");
    -        for($i = 0; $i < 8; $i++) {
    +        for ($i = 0; $i < 8; $i++) {
                 $this->assertEquals(1, $this->redis->getBit('key', $i));
             }
             $this->assertEquals(0, $this->redis->getBit('key', 8));
    @@ -332,7 +337,7 @@ public function testLcs() {
         }
     
         public function testLmpop() {
    -        if(version_compare($this->version, '7.0.0') < 0)
    +        if (version_compare($this->version, '7.0.0') < 0)
                 $this->markTestSkipped();
     
             $key1 = '{l}1';
    @@ -351,7 +356,7 @@ public function testLmpop() {
         }
     
         public function testBLmpop() {
    -        if(version_compare($this->version, '7.0.0') < 0)
    +        if (version_compare($this->version, '7.0.0') < 0)
                 $this->markTestSkipped();
     
             $key1 = '{bl}1';
    @@ -375,7 +380,7 @@ public function testBLmpop() {
         }
     
         function testZmpop() {
    -        if(version_compare($this->version, '7.0.0') < 0)
    +        if (version_compare($this->version, '7.0.0') < 0)
                 $this->markTestSkipped();
     
             $key1 = '{z}1';
    @@ -398,12 +403,12 @@ function testZmpop() {
             $this->assertFalse($this->redis->zmpop([$key1, $key2], 'MIN'));
     
             $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, true);
    -        $this->assertEquals(NULL, $this->redis->zmpop([$key1, $key2], 'MIN'));
    +        $this->assertNull($this->redis->zmpop([$key1, $key2], 'MIN'));
             $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
         }
     
         function testBZmpop() {
    -        if(version_compare($this->version, '7.0.0') < 0)
    +        if (version_compare($this->version, '7.0.0') < 0)
                 $this->markTestSkipped();
     
             $key1 = '{z}1';
    @@ -456,15 +461,12 @@ public function testBitPos() {
             $this->assertEquals(-1,  $this->redis->bitpos('bpkey', 1, 1, -1, false));
         }
     
    -    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 testSetLargeKeys() {
    +        foreach ([1000, 100000, 1000000] as $size) {
    +            $value = str_repeat('A', $size);
    +            $this->assertTrue($this->redis->set('x', $value));
    +            $this->assertEquals($value, $this->redis->get('x'));
    +        }
         }
     
         public function testEcho() {
    @@ -474,10 +476,8 @@ public function testEcho() {
         }
     
         public function testErr() {
    -
    -     $this->redis->set('x', '-ERR');
    -     $this->assertEquals('-ERR', $this->redis->get('x'));
    -
    +        $this->redis->set('x', '-ERR');
    +        $this->assertEquals('-ERR', $this->redis->get('x'));
         }
     
         public function testSet() {
    @@ -494,23 +494,17 @@ public function testSet() {
             $this->redis->set('key2', 'val');
             $this->assertEquals('val', $this->redis->get('key2'));
     
    -        $value = str_repeat('A', 128);
    +        $value1 = bin2hex(random_bytes(rand(64, 128)));
    +        $value2 = random_bytes(rand(65536, 65536 * 2));;
     
    -        $this->redis->set('key2', $value);
    -        $this->assertEquals($value, $this->redis->get('key2'));
    -        $this->assertEquals($value, $this->redis->get('key2'));
    +        $this->redis->set('key2', $value1);
    +        $this->assertEquals($value1, $this->redis->get('key2'));
    +        $this->assertEquals($value1, $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');
    @@ -554,7 +548,7 @@ public function testExtendedSet() {
     
             /* Legacy SETEX redirection */
             $this->redis->del('foo');
    -        $this->assertTrue($this->redis->set('foo','bar', 20));
    +        $this->assertTrue($this->redis->set('foo', 'bar', 20));
             $this->assertEquals('bar', $this->redis->get('foo'));
             $this->assertEquals(20, $this->redis->ttl('foo'));
     
    @@ -564,52 +558,52 @@ public function testExtendedSet() {
             $this->assertEquals('bar-20.5', $this->redis->get('foo'));
     
             /* Invalid third arguments */
    -        $this->assertFalse(@$this->redis->set('foo','bar','baz'));
    -        $this->assertFalse(@$this->redis->set('foo','bar',new StdClass()));
    +        $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->assertTrue($this->redis->set('foo', 'bar', ['nx']));
             $this->assertEquals('bar', $this->redis->get('foo'));
    -        $this->assertFalse($this->redis->set('foo','bar', ['nx']));
    +        $this->assertFalse($this->redis->set('foo', 'bar', ['nx']));
     
             /* Set if exists */
    -        $this->assertTrue($this->redis->set('foo','bar', ['xx']));
    +        $this->assertTrue($this->redis->set('foo', 'bar', ['xx']));
             $this->assertEquals('bar', $this->redis->get('foo'));
             $this->redis->del('foo');
    -        $this->assertFalse($this->redis->set('foo','bar', ['xx']));
    +        $this->assertFalse($this->redis->set('foo', 'bar', ['xx']));
     
             /* Set with a TTL */
    -        $this->assertTrue($this->redis->set('foo','bar', ['ex' => 100]));
    +        $this->assertTrue($this->redis->set('foo', 'bar', ['ex' => 100]));
             $this->assertEquals(100, $this->redis->ttl('foo'));
     
             /* Set with a PTTL */
    -        $this->assertTrue($this->redis->set('foo','bar', ['px' => 100000]));
    +        $this->assertTrue($this->redis->set('foo', 'bar', ['px' => 100000]));
             $this->assertBetween($this->redis->pttl('foo'), 99000, 100001);
     
             /* Set if exists, with a TTL */
    -        $this->assertTrue($this->redis->set('foo','bar', ['xx','ex' => 105]));
    +        $this->assertTrue($this->redis->set('foo', 'bar', ['xx', 'ex' => 105]));
             $this->assertEquals(105, $this->redis->ttl('foo'));
             $this->assertEquals('bar', $this->redis->get('foo'));
     
             /* Set if not exists, with a TTL */
             $this->redis->del('foo');
    -        $this->assertTrue($this->redis->set('foo','bar', ['nx', 'ex' => 110]));
    +        $this->assertTrue($this->redis->set('foo', 'bar', ['nx', 'ex' => 110]));
             $this->assertEquals(110, $this->redis->ttl('foo'));
             $this->assertEquals('bar', $this->redis->get('foo'));
    -        $this->assertFalse($this->redis->set('foo','bar', ['nx', 'ex' => 110]));
    +        $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->assertTrue($this->redis->set('foo', 'barbaz', ['not-valid', 'nx', 'invalid', 'ex' => 200]));
             $this->assertEquals(200, $this->redis->ttl('foo'));
             $this->assertEquals('barbaz', $this->redis->get('foo'));
     
             /* Pass NULL as the optional arguments which should be ignored */
             $this->redis->del('foo');
    -        $this->redis->set('foo','bar', NULL);
    +        $this->redis->set('foo', 'bar', NULL);
             $this->assertEquals('bar', $this->redis->get('foo'));
    -        $this->assertTrue($this->redis->ttl('foo')<0);
    +        $this->assertLT(0, $this->redis->ttl('foo'));
     
             /* Make sure we ignore bad/non-string options (regression test for #1835) */
             $this->assertTrue($this->redis->set('foo', 'bar', [NULL, 'EX' => 60]));
    @@ -644,7 +638,7 @@ public function testGetSet() {
         }
     
         public function testRandomKey() {
    -        for($i = 0; $i < 1000; $i++) {
    +        for ($i = 0; $i < 1000; $i++) {
                 $k = $this->redis->randomKey();
                 $this->assertKeyExists($this->redis, $k);
             }
    @@ -777,13 +771,13 @@ function testExpireOptions() {
             /* Sending a nonsensical mode fails without sending a command */
             $this->redis->clearLastError();
             $this->assertFalse(@$this->redis->expire('eopts', 999, 'nonsense'));
    -        $this->assertEquals(NULL, $this->redis->getLastError());
    +        $this->assertNull($this->redis->getLastError());
     
             $this->redis->del('eopts');
         }
     
         public function testExpiretime() {
    -        if(version_compare($this->version, '7.0.0') < 0)
    +        if (version_compare($this->version, '7.0.0') < 0)
                 $this->markTestSkipped();
     
             $now = time();
    @@ -897,7 +891,7 @@ public function testIncrByFloat() {
             $this->redis->setOption(Redis::OPT_PREFIX, 'someprefix:');
             $this->redis->del('key');
             $this->redis->incrbyfloat('key',1.8);
    -        $this->assertEquals(1.8, floatval($this->redis->get('key')));
    +        $this->assertEqualsWeak(1.8, $this->redis->get('key'));
             $this->redis->setOption(Redis::OPT_PREFIX, '');
             $this->assertKeyExists($this->redis, 'someprefix:key');
             $this->redis->del('someprefix:key');
    @@ -971,7 +965,7 @@ public function testTouch() {
     
         public function testKeys() {
             $pattern = 'keys-test-';
    -        for($i = 1; $i < 10; $i++) {
    +        for ($i = 1; $i < 10; $i++) {
                 $this->redis->set($pattern.$i, $i);
             }
             $this->redis->del($pattern.'3');
    @@ -1203,7 +1197,7 @@ public function testblockingPop() {
             $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
         }
     
    -    public function testllen() {
    +    public function testLLen() {
             $this->redis->del('list');
     
             $this->redis->lPush('list', 'val');
    @@ -1311,7 +1305,7 @@ public function setupSort() {
     
             // set-up
             $this->redis->del('person:id');
    -        foreach([1,2,3,4] as $id) {
    +        foreach ([1, 2, 3, 4] as $id) {
                 $this->redis->lPush('person:id', $id);
             }
         }
    @@ -1344,7 +1338,7 @@ public function testSortAsc() {
             $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['sort' => 'asc']));
     
             // sort by age and get names
    -        $byAgeAsc = ['Carol','Alice','Bob','Dave'];
    +        $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']));
     
    @@ -1369,7 +1363,7 @@ public function testSortAsc() {
             // list → [ghi, def, abc]
             $list = ['abc', 'def', 'ghi'];
             $this->redis->del('list');
    -        foreach($list as $i) {
    +        foreach ($list as $i) {
                 $this->redis->lPush('list', $i);
             }
     
    @@ -1409,7 +1403,7 @@ public function testSortDesc() {
             // sort non-alpha doesn't change all-string lists
             $list = ['def', 'abc', 'ghi'];
             $this->redis->del('list');
    -        foreach($list as $i) {
    +        foreach ($list as $i) {
                 $this->redis->lPush('list', $i);
             }
     
    @@ -1497,7 +1491,7 @@ public function testBlmove() {
         }
     
         // lRem testing
    -    public function testlrem() {
    +    public function testLRem() {
             $this->redis->del('list');
             $this->redis->lPush('list', 'a');
             $this->redis->lPush('list', 'b');
    @@ -1543,7 +1537,7 @@ public function testlrem() {
             $this->assertFalse($this->redis->lrem('list', 'x'));
         }
     
    -    public function testsAdd() {
    +    public function testSAdd() {
             $this->redis->del('set');
     
             $this->assertEquals(1, $this->redis->sAdd('set', 'val'));
    @@ -1557,7 +1551,7 @@ public function testsAdd() {
             $this->assertTrue($this->redis->sismember('set', 'val2'));
         }
     
    -    public function testscard() {
    +    public function testSCard() {
             $this->redis->del('set');
             $this->assertEquals(1, $this->redis->sAdd('set', 'val'));
             $this->assertEquals(1, $this->redis->scard('set'));
    @@ -1565,7 +1559,7 @@ public function testscard() {
             $this->assertEquals(2, $this->redis->scard('set'));
         }
     
    -    public function testsrem() {
    +    public function testSRem() {
             $this->redis->del('set');
             $this->redis->sAdd('set', 'val');
             $this->redis->sAdd('set', 'val2');
    @@ -1628,7 +1622,7 @@ public function testsPopWithCount() {
             $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)) {
    +        if ($this->assertIsArray($ret, $count)) {
                 /* Probably overkill but validate the actual returned members */
                 for ($i = 0; $i < $count; $i++) {
                     $this->assertInArray($prefix.$i, $ret);
    @@ -1644,13 +1638,13 @@ public function testsRandMember() {
             $this->redis->sAdd('set0', 'val2');
     
             $got = [];
    -        while(true) {
    +        while (true) {
                 $v = $this->redis->sRandMember('set0');
                 $this->assertEquals(2, $this->redis->scard('set0')); // no change.
                 $this->assertInArray($v, ['val', 'val2']);
     
                 $got[$v] = $v;
    -            if(count($got) == 2) {
    +            if (count($got) == 2) {
                     break;
                 }
             }
    @@ -1661,7 +1655,7 @@ public function testsRandMember() {
     
             $this->redis->del('set0');
             $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
    -        for($i=0;$i<5;$i++) {
    +        for ($i = 0; $i < 5; $i++) {
                 $member = "member:$i";
                 $this->redis->sAdd('set0', $member);
                 $mems[] = $member;
    @@ -1671,7 +1665,7 @@ public function testsRandMember() {
             $this->assertInArray($member, $mems);
     
             $rmembers = $this->redis->srandmember('set0', $i);
    -        foreach($rmembers as $reply_mem) {
    +        foreach ($rmembers as $reply_mem) {
                 $this->assertInArray($reply_mem, $mems);
             }
     
    @@ -1691,7 +1685,7 @@ public function testSRandMemberWithCount() {
             $this->assertEquals([], $ret_neg);
     
             // Add a few items to the set
    -        for($i=0;$i<100;$i++) {
    +        for ($i = 0; $i< 100; $i++) {
                 $this->redis->sadd('set0', "member$i");
             }
     
    @@ -1735,7 +1729,7 @@ public function testSRandMemberWithCount() {
             }
         }
     
    -    public function testsismember() {
    +    public function testSIsMember() {
             $this->redis->del('set');
     
             $this->redis->sAdd('set', 'val');
    @@ -1744,7 +1738,7 @@ public function testsismember() {
             $this->assertFalse($this->redis->sismember('set', 'val2'));
         }
     
    -    public function testsmembers() {
    +    public function testSMembers() {
             $this->redis->del('set');
     
             $data = ['val', 'val2', 'val3'];
    @@ -1795,46 +1789,46 @@ public function testsInter() {
             $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) {
    +        $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) {
    +        $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) {
    +        $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) {
    +        $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) {
    +        foreach ($xy as $i) {
                 $i = (int)$i;
                 $this->assertInArray($i, array_intersect($x, $y));
             }
     
             $xy = $this->redis->sInter(['{set}odd', '{set}prime']);    // odd prime numbers, as array.
    -        foreach($xy as $i) {
    +        foreach ($xy as $i) {
                 $i = (int)$i;
                 $this->assertInArray($i, array_intersect($x, $y));
             }
     
             $yz = $this->redis->sInter('{set}prime', '{set}square');   // set of prime squares
    -        foreach($yz as $i) {
    +        foreach ($yz as $i) {
                 $i = (int)$i;
                 $this->assertInArray($i, array_intersect($y, $z));
             }
     
             $yz = $this->redis->sInter(['{set}prime', '{set}square']);    // set of odd squares, as array
    -        foreach($yz as $i) {
    +        foreach ($yz as $i) {
             $i = (int)$i;
                 $this->assertInArray($i, array_intersect($y, $z));
             }
    @@ -1855,43 +1849,43 @@ public function testsInter() {
         }
     
         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
    +        $this->redis->del('{set}x', '{set}y', '{set}z', '{set}t');
     
    -        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
    -        foreach($x as $i) {
    +        $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) {
    +        $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) {
    +        $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) {
    +        $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(count(array_intersect($x,$y)), $this->redis->sInterStore(['{set}k', '{set}x', '{set}y']));
    +        $this->assertEquals(
    +            count(array_intersect($x,$y)),
    +            $this->redis->sInterStore(['{set}k', '{set}x', '{set}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) {
    +        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) {
    +        foreach (array_intersect($y, $z) as $i) {
                 $this->assertTrue($this->redis->sismember('{set}k', $i));
             }
     
    @@ -1918,81 +1912,76 @@ public function testsUnion() {
             $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) {
    +        $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) {
    +        $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) {
    +        $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) {
    +        $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;
    +        foreach ($xy as $i) {
                 $this->assertInArray($i, array_merge($x, $y));
             }
     
             $yz = $this->redis->sUnion('{set}y', '{set}z');   // y U Z
    -        foreach($yz as $i) {
    +        foreach ($yz as $i) {
             $i = (int)$i;
                 $this->assertInArray($i, array_merge($y, $z));
             }
     
             $zt = $this->redis->sUnion('{set}z', '{set}t');   // z U t
    -        foreach($zt as $i) {
    +        foreach ($zt as $i) {
             $i = (int)$i;
                 $this->assertInArray($i, array_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;
    +        foreach ($xyz as $i) {
                 $this->assertInArray($i, array_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
    +        $this->redis->del('{set}x', '{set}y', '{set}z', '{set}t');
     
    -        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
    -        foreach($x as $i) {
    +        $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) {
    +        $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) {
    +        $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) {
    +        $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($count, count($xy));
    -        foreach($xy as $i) {
    +        foreach ($xy as $i) {
             $i = (int)$i;
                 $this->assertTrue($this->redis->sismember('{set}k', $i));
             }
    @@ -2000,24 +1989,21 @@ public function testsUnionStore() {
             $count = $this->redis->sUnionStore('{set}k', '{set}y', '{set}z');  // y U z
             $yz = array_unique(array_merge($y, $z));
             $this->assertEquals($count, count($yz));
    -        foreach($yz as $i) {
    -        $i = (int)$i;
    +        foreach ($yz as $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($count, count($zt));
    -        foreach($zt as $i) {
    -        $i = (int)$i;
    +        foreach ($zt as $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($count, count($xyz));
    -        foreach($xyz as $i) {
    -        $i = (int)$i;
    +        foreach ($xyz as $i) {
                 $this->assertTrue($this->redis->sismember('{set}k', $i));
             }
     
    @@ -2040,106 +2026,99 @@ public function testsDiff() {
             $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) {
    +        $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) {
    +        $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) {
    +        $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) {
    +        $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) {
    +        foreach ($xy as $i) {
             $i = (int)$i;
                 $this->assertInArray($i, array_diff($x, $y));
             }
     
             $yz = $this->redis->sDiff('{set}y', '{set}z');    // y U Z
    -        foreach($yz as $i) {
    +        foreach ($yz as $i) {
             $i = (int)$i;
                 $this->assertInArray($i, array_diff($y, $z));
             }
     
             $zt = $this->redis->sDiff('{set}z', '{set}t');    // z U t
    -        foreach($zt as $i) {
    +        foreach ($zt as $i) {
             $i = (int)$i;
                 $this->assertInArray($i, array_diff($z, $t));
             }
     
             $xyz = $this->redis->sDiff('{set}x', '{set}y', '{set}z'); // x U y U z
    -        foreach($xyz as $i) {
    +        foreach ($xyz as $i) {
             $i = (int)$i;
                 $this->assertInArray($i, array_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
    +        $this->redis->del('{set}x', '{set}y', '{set}z', '{set}t');
     
    -        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
    -        foreach($x as $i) {
    +        $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) {
    +        $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) {
    +        $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) {
    +        $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($count, count($xy));
    -        foreach($xy as $i) {
    -            $i = (int)$i;
    +        foreach ($xy as $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($count, count($yz));
    -        foreach($yz as $i) {
    -        $i = (int)$i;
    +        foreach ($yz as $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($count, count($zt));
    -        foreach($zt as $i) {
    -        $i = (int)$i;
    +        foreach ($zt as $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($count, count($xyz));
    -        foreach($xyz as $i) {
    -        $i = (int)$i;
    +        foreach ($xyz as $i) {
                 $this->assertTrue($this->redis->sismember('{set}k', $i));
             }
     
    @@ -2157,7 +2136,7 @@ public function testsDiffStore() {
         }
     
         public function testInterCard() {
    -        if(version_compare($this->version, '7.0.0') < 0)
    +        if (version_compare($this->version, '7.0.0') < 0)
                 $this->markTestSkipped();
     
             $set_data = [
    @@ -2204,16 +2183,12 @@ public function testInterCard() {
             $this->redis->del(array_merge($ssets, $zsets));
         }
     
    -    public function testlrange() {
    +    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(['val3'], $this->redis->lrange('list', 0, 0));
             $this->assertEquals(['val3', 'val2'], $this->redis->lrange('list', 0, 1));
             $this->assertEquals(['val3', 'val2', 'val'], $this->redis->lrange('list', 0, 2));
    @@ -2240,7 +2215,7 @@ public function testFlushDB() {
             $this->assertTrue($this->redis->flushdb(true));
         }
     
    -    public function testttl() {
    +    public function testTTL() {
             $this->redis->set('x', 'y');
             $this->redis->expire('x', 5);
             $ttl = $this->redis->ttl('x');
    @@ -2251,7 +2226,7 @@ public function testttl() {
             $this->assertEquals(-1, $this->redis->ttl('x'));
     
             // A key that doesn't exist (> 2.8 will return -2)
    -        if(version_compare($this->version, '2.8.0') >= 0) {
    +        if (version_compare($this->version, '2.8.0') >= 0) {
                 $this->redis->del('x');
                 $this->assertEquals(-2, $this->redis->ttl('x'));
             }
    @@ -2277,7 +2252,7 @@ public function testClient() {
     
             // Figure out which ip:port is us!
             $address = NULL;
    -        foreach($clients as $client) {
    +        foreach ($clients as $client) {
                 if ($client['name'] == 'phpredis_unit_tests') {
                     $address = $client['addr'];
                 }
    @@ -2329,24 +2304,24 @@ public function testSlowlog() {
     
         public function testWait() {
             // Closest we can check based on redis commit history
    -        if(version_compare($this->version, '2.9.11') < 0)
    +        if (version_compare($this->version, '2.9.11') < 0)
                 $this->markTestSkipped();
     
             // We could have slaves here, so determine that
    -        $arr_slaves = $this->redis->info();
    -        $i_slaves   = $arr_slaves['connected_slaves'];
    +        $info     = $this->redis->info();
    +        $replicas = $info['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($i_slaves, $this->redis->wait($i_slaves, 100));
    +        $this->assertEquals($replicas, $this->redis->wait($replicas, 100));
     
             // Pass more slaves than are connected
             $this->redis->set('wait-foo','over9000');
             $this->redis->set('wait-bar','revo9000');
    -        $this->assertLT($i_slaves + 1, $this->redis->wait($i_slaves+1, 100));
    +        $this->assertLT($replicas + 1, $this->redis->wait($replicas + 1, 100));
     
             // Make sure when we pass with bad arguments we just get back false
             $this->assertFalse($this->redis->wait(-1, -1));
    @@ -2394,7 +2369,7 @@ public function testInfo() {
                     );
                 }
     
    -            foreach($keys as $k) {
    +            foreach ($keys as $k) {
                     $this->assertInArray($k, array_keys($info));
                 }
             }
    @@ -2415,7 +2390,7 @@ public function testInfoCommandStats() {
             if ( ! $this->assertIsArray($info))
                 return;
     
    -        foreach($info as $k => $value) {
    +        foreach ($info as $k => $value) {
                 $this->assertStringContains('cmdstat_', $k);
             }
         }
    @@ -2730,7 +2705,7 @@ public function testZX() {
             //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->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], [1, 1], 'MIN');
             $this->assertEquals(1.0, $this->redis->zScore('{zset}U', 'duplicate'));
             $this->redis->del('{zset}U');
     
    @@ -2764,10 +2739,10 @@ public function testZX() {
             $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '+inf']));
     
             // Now, confirm that they're being sent, and that it works
    -        $arr_weights = ['inf','-inf','+inf'];
    +        $weights = ['inf','-inf','+inf'];
     
    -        foreach($arr_weights as $str_weight) {
    -            $r = $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1,$str_weight]);
    +        foreach ($weights as $weight) {
    +            $r = $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1,$weight]);
                 $this->assertEquals(5, $r);
                 $r = $this->redis->zrangebyscore('{zset}3', '(-inf', '(inf',['withscores'=>true]);
                 $this->assertEquals(2, count($r));
    @@ -2857,7 +2832,7 @@ public function testZX() {
     
             $this->redis->del('{zset}I');
             $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], null, 'max'));
    -        $this->assertEquals(floatval(7), $this->redis->zScore('{zset}I', 'val1'));
    +        $this->assertEquals(7., $this->redis->zScore('{zset}I', 'val1'));
     
             // zrank, zrevrank
             $this->redis->del('z');
    @@ -2877,25 +2852,25 @@ public function testZX() {
         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);
    +        $mems = ['one' => 1.0, 'two' => 2.0, 'three' => 3.0];
    +        foreach ($mems as $mem => $score) {
    +            $this->redis->zAdd('{z}', $score, $mem);
             }
     
             /* Verify we can pass true and ['withscores' => true] */
    -        $this->assertEquals($arr_mems, $this->redis->zRange('{z}', 0, -1, true));
    -        $this->assertEquals($arr_mems, $this->redis->zRange('{z}', 0, -1, ['withscores' => true]));
    +        $this->assertEquals($mems, $this->redis->zRange('{z}', 0, -1, true));
    +        $this->assertEquals($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) {
    +        if (version_compare($this->version, '2.8.9') < 0) {
                 $this->MarkTestSkipped();
                 return;
             }
     
             $this->redis->del('key');
    -        foreach(range('a', 'g') as $c) {
    +        foreach (range('a', 'g') as $c) {
                 $this->redis->zAdd('key', 0, $c);
             }
     
    @@ -2909,7 +2884,7 @@ public function testZRangeByLex() {
     
             /* Test getting the same functionality via ZRANGE and options */
             if ($this->minVersionCheck('6.2.0')) {
    -            $this->assertEquals(['a','b','c'], $this->redis->zRange('key', '-', '[c', ['BYLEX']));
    +            $this->assertEquals(['a', 'b', 'c'], $this->redis->zRange('key', '-', '[c', ['BYLEX']));
                 $this->assertEquals(['b', 'c'], $this->redis->zRange('key', '-', '[c', ['BYLEX', 'LIMIT' => [1, 2]]));
                 $this->assertEquals(['b'], $this->redis->zRange('key', '-', '(c', ['BYLEX', 'LIMIT' => [1, 2]]));
     
    @@ -3171,12 +3146,12 @@ public function testHashes() {
             if (version_compare($this->version, '2.5.0') >= 0) {
                 // hIncrByFloat
                 $this->redis->del('h');
    -            $this->assertEquals(1.5, $this->redis->hIncrByFloat('h','x', 1.5));
    -            $this->assertEquals(3.0, $this->redis->hincrByFloat('h','x', 1.5));
    -            $this->assertEquals(1.5, $this->redis->hincrByFloat('h','x', -1.5));
    -            $this->assertEquals(1000000000001.5, $this->redis->hincrByFloat('h','x', 1000000000000));
    +            $this->assertEquals(1.5, $this->redis->hIncrByFloat('h', 'x', 1.5));
    +            $this->assertEquals(3.0, $this->redis->hincrByFloat('h', 'x', 1.5));
    +            $this->assertEquals(1.5, $this->redis->hincrByFloat('h', 'x', -1.5));
    +            $this->assertEquals(1000000000001.5, $this->redis->hincrByFloat('h', 'x', 1000000000000));
     
    -            $this->redis->hset('h','y','not-a-number');
    +            $this->redis->hset('h', 'y','not-a-number');
                 $this->assertFalse($this->redis->hIncrByFloat('h', 'y', 1.5));
             }
     
    @@ -3275,9 +3250,9 @@ 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';
    +            $small_encoding = 'raw';
             } else {
    -            $str_small_encoding = 'embstr';
    +            $small_encoding = 'embstr';
             }
     
             $this->redis->del('key');
    @@ -3286,7 +3261,7 @@ public function testObject() {
             $this->assertFalse($this->redis->object('idletime', 'key'));
     
             $this->redis->set('key', 'value');
    -        $this->assertEquals($str_small_encoding, $this->redis->object('encoding', 'key'));
    +        $this->assertEquals($small_encoding, $this->redis->object('encoding', 'key'));
             $this->assertEquals(1, $this->redis->object('refcount', 'key'));
             $this->assertTrue(is_numeric($this->redis->object('idletime', 'key')));
     
    @@ -3318,7 +3293,7 @@ public function testObject() {
             $this->assertTrue(is_numeric($this->redis->object('idletime', 'key')));
     
             $this->redis->del('key');
    -        $this->redis->lpush('key', str_repeat('A', pow(10,6))); // 1M elements, too big for a ziplist.
    +        $this->redis->lpush('key', str_repeat('A', pow(10, 6))); // 1M elements, too big for a ziplist.
     
             $encoding = $this->redis->object('encoding', 'key');
             $this->assertInArray($encoding, ['linkedlist', 'quicklist']);
    @@ -3797,11 +3772,11 @@ protected function sequence($mode) {
             $this->assertTrue($ret[$i++]); // the move did succeed.
             $this->assertEquals(3, $ret[$i++]); // sKey2 now has 3 values.
             $this->assertTrue($ret[$i++]); // sKey2 does contain sValue4.
    -        foreach(['sValue1', 'sValue3'] as $k) { // sKey1 contains sValue1 and sValue3.
    +        foreach (['sValue1', 'sValue3'] as $k) { // sKey1 contains sValue1 and sValue3.
                 $this->assertInArray($k, $ret[$i]);
             }
             $this->assertEquals(2, count($ret[$i++]));
    -        foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // sKey2 contains sValue1, sValue2, and sValue4.
    +        foreach (['sValue1', 'sValue2', 'sValue4'] as $k) { // sKey2 contains sValue1, sValue2, and sValue4.
                 $this->assertInArray($k, $ret[$i]);
             }
             $this->assertEquals(3, count($ret[$i++]));
    @@ -3809,13 +3784,13 @@ protected function sequence($mode) {
             $this->assertEquals(1, $ret[$i++]); // intersection + store → 1 value in the destination set.
             $this->assertEquals(['sValue1'], $ret[$i++]); // sinterstore destination contents
     
    -        foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeydest U sKey2) contains sValue1, sValue2, and sValue4.
    +        foreach (['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeydest U sKey2) contains sValue1, sValue2, and sValue4.
                 $this->assertInArray($k, $ret[$i]);
             }
             $this->assertEquals(3, count($ret[$i++])); // union size
     
             $this->assertEquals(3, $ret[$i++]); // unionstore size
    -        foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4.
    +        foreach (['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4.
                 $this->assertInArray($k, $ret[$i]);
             }
             $this->assertEquals(3, count($ret[$i++])); // skeyUnion size
    @@ -3889,7 +3864,7 @@ protected function sequence($mode) {
             $this->assertEquals(['zValue1', 'zValue5', 'zValue14', 'zValue15'], $ret[$i++]); // {z}key1 contents
             $this->assertEquals(['zValue2', 'zValue5'], $ret[$i++]); // {z}key2 contents
             $this->assertEquals(['zValue5'], $ret[$i++]); // {z}inter contents
    -        $this->assertEquals(5, $ret[$i++]); // {z}Union has 5 values (1,2,5,14,15)
    +        $this->assertEquals(5, $ret[$i++]); // {z}Union has 5 values (1, 2, 5, 14, 15)
             $this->assertEquals(['zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15'], $ret[$i++]); // {z}Union contents
             $this->assertEquals(1, $ret[$i++]); // added value to {z}key5, with score 5
             $this->assertEquals(8.0, $ret[$i++]); // incremented score by 3 → it is now 8.
    @@ -3952,7 +3927,7 @@ protected function sequence($mode) {
     
             // GitHub issue 78
             $this->redis->del('test');
    -        for($i = 1; $i <= 5; $i++)
    +        for ($i = 1; $i <= 5; $i++)
                 $this->redis->zadd('test', $i, (string)$i);
     
             $result = $this->redis->multi($mode)
    @@ -4872,7 +4847,7 @@ public function testSerializerPHP() {
         }
     
         public function testSerializerIGBinary() {
    -        if(defined('Redis::SERIALIZER_IGBINARY')) {
    +        if (defined('Redis::SERIALIZER_IGBINARY')) {
                 $this->checkSerializer(Redis::SERIALIZER_IGBINARY);
     
                 // with prefix
    @@ -4904,7 +4879,7 @@ public function testSerializerIGBinary() {
         }
     
         public function testSerializerMsgPack() {
    -        if(defined('Redis::SERIALIZER_MSGPACK')) {
    +        if (defined('Redis::SERIALIZER_MSGPACK')) {
                 $this->checkSerializer(Redis::SERIALIZER_MSGPACK);
     
                 // with prefix
    @@ -4958,15 +4933,15 @@ private function checkSerializer($mode) {
             $this->assertEquals($a[0], $this->redis->lIndex('key', 0));
     
             // lInsert
    -        $this->assertEquals(4, $this->redis->lInsert('key', Redis::BEFORE, $a[0], [1,2,3]));
    -        $this->assertEquals(5, $this->redis->lInsert('key', Redis::AFTER, $a[0], [4,5,6]));
    +        $this->assertEquals(4, $this->redis->lInsert('key', Redis::BEFORE, $a[0], [1, 2, 3]));
    +        $this->assertEquals(5, $this->redis->lInsert('key', Redis::AFTER, $a[0], [4, 5, 6]));
     
    -        $a = [[1,2,3], $a[0], [4,5,6], $a[1], $a[2]];
    +        $a = [[1, 2, 3], $a[0], [4, 5, 6], $a[1], $a[2]];
             $this->assertEquals($a, $this->redis->lrange('key', 0, -1));
     
             // sAdd
             $this->redis->del('{set}key');
    -        $s = [1,'a', [1,2,3], ['k' => 'v']];
    +        $s = [1,'a', [1, 2, 3], ['k' => 'v']];
     
             $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[0]));
             $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[1]));
    @@ -5058,7 +5033,7 @@ private function checkSerializer($mode) {
             // mset
             $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']];
             $this->assertTrue($this->redis->mset($a));
    -        foreach($a as $k => $v) {
    +        foreach ($a as $k => $v) {
                 $this->assertEquals($v, $this->redis->get($k));
             }
     
    @@ -5066,12 +5041,12 @@ private function checkSerializer($mode) {
     
             // hSet
             $this->redis->del('hash');
    -        foreach($a as $k => $v) {
    +        foreach ($a as $k => $v) {
                 $this->assertEquals(1, $this->redis->hSet('hash', $k, $v));
             }
     
             // hGet
    -        foreach($a as $k => $v) {
    +        foreach ($a as $k => $v) {
                 $this->assertEquals($v, $this->redis->hGet('hash', $k));
             }
     
    @@ -5086,13 +5061,13 @@ private function checkSerializer($mode) {
             // hMSet
             $this->redis->del('hash');
             $this->redis->hMSet('hash', $a);
    -        foreach($a as $k => $v) {
    +        foreach ($a as $k => $v) {
                 $this->assertEquals($v, $this->redis->hGet('hash', $k));
             }
     
             // hMget
             $hmget = $this->redis->hMget('hash', array_keys($a));
    -        foreach($hmget as $k => $v) {
    +        foreach ($hmget as $k => $v) {
                 $this->assertEquals($a[$k], $v);
             }
     
    @@ -5133,8 +5108,8 @@ private function checkSerializer($mode) {
                 $this->assertIsArray($x[0]);
                 $this->assertIsArray($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');
    +            $this->assertIsObject($x[0], 'stdClass');
    +            $this->assertIsObject($x[1], 'stdClass');
             }
     
             // revert
    @@ -5274,7 +5249,7 @@ public function testDumpRestore() {
     
         public function testGetLastError() {
             // We shouldn't have any errors now
    -        $this->assertEquals(NULL, $this->redis->getLastError());
    +        $this->assertNull($this->redis->getLastError());
     
             // test getLastError with a regular command
             $this->redis->set('x', 'a');
    @@ -5284,7 +5259,7 @@ public function testGetLastError() {
     
             // clear error
             $this->redis->clearLastError();
    -        $this->assertEquals(NULL, $this->redis->getLastError());
    +        $this->assertNull($this->redis->getLastError());
         }
     
         // Helper function to compare nested results -- from the php.net array_diff page, I believe
    @@ -5332,9 +5307,9 @@ public function testScript() {
             $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);
    +        $this->assertEquals($s1_sha, $this->redis->script('load', $s1_src));
    +        $this->assertEquals($s2_sha, $this->redis->script('load', $s2_src));
    +        $this->assertEquals($s3_sha, $this->redis->script('load', $s3_src));
     
             // They should all exist
             $result = $this->redis->script('exists', $s1_sha, $s2_sha, $s3_sha);
    @@ -5377,11 +5352,11 @@ public function testEval() {
     
             // 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->assertEquals(['a','b','c'], $list);
    +        $this->assertEquals(['a', 'b', 'c'], $list);
     
             // 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']);
    +        $this->assertEquals(['d', 'e', 'f'], $zset);
     
             // Test an empty MULTI BULK response
             $this->redis->del('{eval-key}-nolist');
    @@ -5410,15 +5385,18 @@ public function testEval() {
                     'hello again!',
                     [],
                     [
    -                    ['d','e','f'],
    -                    ['a','b','c']
    +                    ['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);
    +        $this->assertTrue(
    +            is_array($eval_result) &&
    +            count($this->array_diff_recursive($eval_result, $expected)) == 0
    +        );
     
             /*
              * Nested reply wihin a multi/pipeline block
    @@ -5426,18 +5404,21 @@ public function testEval() {
     
             $num_scripts = 10;
     
    -        $arr_modes = [Redis::MULTI];
    -        if ($this->havePipeline()) $arr_modes[] = Redis::PIPELINE;
    +        $modes = [Redis::MULTI];
    +        if ($this->havePipeline()) $modes[] = Redis::PIPELINE;
     
    -        foreach($arr_modes as $mode) {
    +        foreach ($modes as $mode) {
                 $this->redis->multi($mode);
    -            for($i=0;$i<$num_scripts;$i++) {
    +            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);
    +            foreach ($replies as $reply) {
    +                $this->assertTrue(
    +                    is_array($reply) &&
    +                    count($this->array_diff_recursive($reply, $expected)) == 0
    +                );
                 }
             }
     
    @@ -5455,13 +5436,11 @@ public function testEval() {
             $args_result = $this->redis->eval($args_script, $args_args, 3);
     
             // Make sure our first three are prefixed
    -        for($i=0;$iassertTrue($args_result[$i] == 'prefix:' . $args_args[$i]);
    +        for ($i = 0; $i< count($args_result); $i++) {
    +            if ($i < 3) {
    +                $this->assertEquals('prefix:' . $args_args[$i], $args_result[$i]);
                 } else {
    -                // Should not be prefixed
    -                $this->assertTrue($args_result[$i] == $args_args[$i]);
    +                $this->assertEquals($args_args[$i], $args_result[$i]);
                 }
             }
         }
    @@ -5494,7 +5473,7 @@ public function testEvalSHA() {
         }
     
         public function testSerialize() {
    -        $vals = [1, 1.5, 'one', ['here','is','an','array']];
    +        $vals = [1, 1.5, 'one', ['here', 'is', 'an', 'array']];
     
             // Test with no serialization at all
             $this->assertEquals('test', $this->redis->_serialize('test'));
    @@ -5502,40 +5481,40 @@ public function testSerialize() {
             $this->assertEquals('Array', $this->redis->_serialize([]));
             $this->assertEquals('Object', $this->redis->_serialize(new stdClass));
     
    -        foreach($this->getSerializers() as $mode) {
    -            $arr_enc = [];
    -            $arr_dec = [];
    +        foreach ($this->getSerializers() as $mode) {
    +            $enc = [];
    +            $dec = [];
     
    -            foreach($vals as $k => $v) {
    +            foreach ($vals as $k => $v) {
                     $enc = $this->redis->_serialize($v);
                     $dec = $this->redis->_unserialize($enc);
     
                     // They should be the same
    -                $this->assertTrue($enc == $dec);
    +                $this->assertEquals($enc, $dec);
                 }
             }
         }
     
         public function testUnserialize() {
             $vals = [
    -            1,1.5,'one',['this','is','an','array']
    +            1, 1.5,'one',['this', 'is', 'an', 'array']
             ];
     
             $serializers = [Redis::SERIALIZER_PHP];
     
    -        if(defined('Redis::SERIALIZER_IGBINARY')) {
    +        if (defined('Redis::SERIALIZER_IGBINARY')) {
                 $serializers[] = Redis::SERIALIZER_IGBINARY;
             }
     
    -        if(defined('Redis::SERIALIZER_MSGPACK')) {
    +        if (defined('Redis::SERIALIZER_MSGPACK')) {
                 $serializers[] = Redis::SERIALIZER_MSGPACK;
             }
     
    -        foreach($serializers as $mode) {
    +        foreach ($serializers as $mode) {
                 $vals_enc = [];
     
                 // Pass them through redis so they're serialized
    -            foreach($vals as $key => $val) {
    +            foreach ($vals as $key => $val) {
                     $this->redis->setOption(Redis::OPT_SERIALIZER, $mode);
     
                     $key = 'key' . ++$key;
    @@ -5548,10 +5527,10 @@ public function testUnserialize() {
                 }
     
                 // Run through our array comparing values
    -            for($i=0;$iredis->setOption(Redis::OPT_SERIALIZER, $mode);
    -                $this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i]));
    +                $this->assertEquals($vals[$i], $this->redis->_unserialize($vals_enc[$i]));
                     $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
                 }
             }
    @@ -5625,11 +5604,11 @@ public function testPackHelpers() {
         public function testPrefix() {
             // no prefix
             $this->redis->setOption(Redis::OPT_PREFIX, '');
    -        $this->assertTrue('key' == $this->redis->_prefix('key'));
    +        $this->assertEquals('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'));
    +        $this->assertEquals('some-prefix:key', $this->redis->_prefix('key'));
     
             // Clear prefix
             $this->redis->setOption(Redis::OPT_PREFIX, '');
    @@ -5720,7 +5699,7 @@ public function testConfig() {
             /* REWRITE.  We don't care if it actually works, just that the
                command be attempted */
             $res = $this->redis->config('rewrite');
    -        $this->assertTrue(is_bool($res));
    +        $this->assertIsBool($res);
             if ($res == false) {
                 $this->assertPatternMatch('/.*config.*/', $this->redis->getLastError());
                 $this->redis->clearLastError();
    @@ -5841,46 +5820,46 @@ public function testTransferredBytes() {
          * 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];
    +    protected function get_keyspace_count($db) {
    +        $info = $this->redis->info();
    +        if (isset($info[$db])) {
    +            $info = $info[$db];
    +            $info = explode(',', $info);
    +            $info = explode('=', $info[0]);
    +            return $info[1];
             } else {
                 return 0;
             }
         }
     
         public function testScan() {
    -        if(version_compare($this->version, '2.8.0') < 0)
    +        if (version_compare($this->version, '2.8.0') < 0)
                 $this->markTestSkipped();
     
             // Key count
    -        $i_key_count = $this->get_keyspace_count('db0');
    +        $key_count = $this->get_keyspace_count('db0');
     
             // Have scan retry
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
     
             // Scan them all
             $it = NULL;
    -        while($arr_keys = $this->redis->scan($it)) {
    -            $i_key_count -= count($arr_keys);
    +        while ($keys = $this->redis->scan($it)) {
    +            $key_count -= count($keys);
             }
             // Should have iterated all keys
    -        $this->assertEquals(0, $i_key_count);
    +        $this->assertEquals(0, $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");
    +        $uniq = uniqid() . '-' . uniqid();
    +        for ($i = 0; $i < 10; $i++) {
    +            $this->redis->set($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);
    +        while ($keys = $this->redis->scan($it, "*$uniq*")) {
    +            $i -= count($keys);
             }
             $this->assertEquals(0, $i);
     
    @@ -5891,28 +5870,28 @@ public function testScan() {
     
                 // Create some simple keys and lists
                 for ($i = 0; $i < 3; $i++) {
    -                $str_simple = "simple:{$id}:$i";
    -                $str_list = "list:{$id}:$i";
    +                $simple = "simple:{$id}:$i";
    +                $list = "list:{$id}:$i";
     
    -                $this->redis->set($str_simple, $i);
    -                $this->redis->del($str_list);
    -                $this->redis->rpush($str_list, ['foo']);
    +                $this->redis->set($simple, $i);
    +                $this->redis->del($list);
    +                $this->redis->rpush($list, ['foo']);
     
    -                $arr_keys['STRING'][] = $str_simple;
    -                $arr_keys['LIST'][] = $str_list;
    +                $keys['STRING'][] = $simple;
    +                $keys['LIST'][] = $list;
                 }
     
                 // Make sure we can scan for specific types
    -            foreach ($arr_keys as $str_type => $arr_vals) {
    -                foreach ([0, 10] as $i_count) {
    -                    $arr_resp = [];
    +            foreach ($keys as $type => $vals) {
    +                foreach ([0, 10] as $count) {
    +                    $resp = [];
     
                         $it = NULL;
    -                    while ($arr_scan = $this->redis->scan($it, "*$id*", $i_count, $str_type)) {
    -                        $arr_resp = array_merge($arr_resp, $arr_scan);
    +                    while ($scan = $this->redis->scan($it, "*$id*", $count, $type)) {
    +                        $resp = array_merge($resp, $scan);
                         }
     
    -                    $this->assertEqualsCanonicalizing($arr_vals, $arr_resp);
    +                    $this->assertEqualsCanonicalizing($vals, $resp);
                     }
                 }
             }
    @@ -5922,35 +5901,35 @@ 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);
    +        $prefixes = ['prefix-a:', 'prefix-b:'];
    +        foreach ($prefixes as $prefix) {
    +            $this->redis->setOption(Redis::OPT_PREFIX, $prefix);
                 $this->redis->set("$keyid", 'LOLWUT');
    -            $arr_all_keys["{$str_prefix}{$keyid}"] = true;
    +            $all_keys["{$prefix}{$keyid}"] = true;
             }
     
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);
     
    -        foreach ($arr_prefixes as $str_prefix) {
    -            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
    +        foreach ($prefixes as $prefix) {
    +            $this->redis->setOption(Redis::OPT_PREFIX, $prefix);
                 $it = NULL;
    -            $arr_keys = $this->redis->scan($it, "*$keyid*");
    -            $this->assertEquals($arr_keys, ["{$str_prefix}{$keyid}"]);
    +            $keys = $this->redis->scan($it, "*$keyid*");
    +            $this->assertEquals($keys, ["{$prefix}{$keyid}"]);
             }
     
             /* Unset the prefix option */
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);
     
             $it = NULL;
    -        while ($arr_keys = $this->redis->scan($it, "*$keyid*")) {
    -            foreach ($arr_keys as $str_key) {
    -                unset($arr_all_keys[$str_key]);
    +        while ($keys = $this->redis->scan($it, "*$keyid*")) {
    +            foreach ($keys as $key) {
    +                unset($all_keys[$key]);
                 }
             }
     
             /* Should have touched every key */
    -        $this->assertTrue(count($arr_all_keys) == 0);
    +        $this->assertEquals(0, count($all_keys));
         }
     
         public function testMaxRetriesOption() {
    @@ -5961,40 +5940,30 @@ public function testMaxRetriesOption() {
         }
     
         public function testBackoffOptions() {
    -        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DEFAULT);
    -        $this->assertEquals(Redis::BACKOFF_ALGORITHM_DEFAULT, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM));
    -
    -        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_CONSTANT);
    -        $this->assertEquals(Redis::BACKOFF_ALGORITHM_CONSTANT, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM));
    -
    -        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_UNIFORM);
    -        $this->assertEquals(Redis::BACKOFF_ALGORITHM_UNIFORM, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM));
    -
    -        $this->redis -> setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EXPONENTIAL);
    -        $this->assertEquals(Redis::BACKOFF_ALGORITHM_EXPONENTIAL, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM));
    -
    -        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EQUAL_JITTER);
    -        $this->assertEquals(Redis::BACKOFF_ALGORITHM_EQUAL_JITTER, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM));
    -
    -        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_FULL_JITTER);
    -        $this->assertEquals(Redis::BACKOFF_ALGORITHM_FULL_JITTER, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM));
    +        $algorithms = [
    +            Redis::BACKOFF_ALGORITHM_DEFAULT,
    +            Redis::BACKOFF_ALGORITHM_CONSTANT,
    +            Redis::BACKOFF_ALGORITHM_UNIFORM,
    +            Redis::BACKOFF_ALGORITHM_EXPONENTIAL,
    +            Redis::BACKOFF_ALGORITHM_EQUAL_JITTER,
    +            Redis::BACKOFF_ALGORITHM_FULL_JITTER,
    +            Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER
    +        ];
     
    -        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER);
    -        $this->assertEquals(Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM));
    +        foreach ($algorithms as $algorithm) {
    +            $this->assertTrue($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, $algorithm));
    +            $this->assertEquals($algorithm, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM));
    +        }
     
    +        // Invalid algorithm
             $this->assertFalse($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, 55555));
     
    -        $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 500);
    -        $this->assertEquals(500, $this->redis->getOption(Redis::OPT_BACKOFF_BASE));
    -
    -        $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 750);
    -        $this->assertEquals(750, $this->redis->getOption(Redis::OPT_BACKOFF_BASE));
    -
    -        $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 500);
    -        $this->assertEquals(500, $this->redis->getOption(Redis::OPT_BACKOFF_CAP));
    -
    -        $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 750);
    -        $this->assertEquals(750, $this->redis->getOption(Redis::OPT_BACKOFF_CAP));
    +        foreach ([Redis::OPT_BACKOFF_BASE, Redis::OPT_BACKOFF_CAP] as $option) {
    +            foreach ([500, 750] as $value) {
    +                $this->redis->setOption($option, $value);
    +                $this->assertEquals($value, $this->redis->getOption($option));
    +            }
    +        }
         }
     
         public function testHScan() {
    @@ -6005,34 +5974,34 @@ public function testHScan() {
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
     
             $this->redis->del('hash');
    -        $i_foo_mems = 0;
    +        $foo_mems = 0;
     
    -        for($i=0;$i<100;$i++) {
    -            if($i>3) {
    +        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++;
    +                $foo_mems++;
                 }
             }
     
             // Scan all of them
             $it = NULL;
    -        while($arr_keys = $this->redis->hscan('hash', $it)) {
    -            $i -= count($arr_keys);
    +        while ($keys = $this->redis->hscan('hash', $it)) {
    +            $i -= count($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);
    +        while ($keys = $this->redis->hscan('hash', $it, '*foomember*')) {
    +            $foo_mems -= count($keys);
    +            foreach ($keys as $mem => $val) {
    +                $this->assertStringContains('member', $mem);
    +                $this->assertStringContains('value', $val);
                 }
             }
    -        $this->assertEquals(0, $i_foo_mems);
    +        $this->assertEquals(0, $foo_mems);
         }
     
         public function testSScan() {
    @@ -6042,27 +6011,27 @@ public function testSScan() {
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
     
             $this->redis->del('set');
    -        for($i=0;$i<100;$i++) {
    +        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);
    +        while ($keys = $this->redis->sscan('set', $it)) {
    +            $i -= count($keys);
    +            foreach ($keys as $mem) {
    +                $this->assertStringContains('member', $mem);
                 }
             }
             $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);
    +        $w_zero = 0;
    +        while ($keys = $this->redis->sscan('set', $it, '*0*')) {
    +            $w_zero += count($keys);
             }
    -        $this->assertEquals(10, $i_w_zero);
    +        $this->assertEquals(10, $w_zero);
         }
     
         public function testZScan() {
    @@ -6072,72 +6041,70 @@ public function testZScan() {
             $this->redis->setOption(Redis::OPT_SCAN, Redis::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) {
    +
    +        [$t_score, $p_score, $p_count] = [0, 0, 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;
    +                $p_score += $i;
    +                $p_count++;
                 } else {
                     $this->redis->zadd('zset', $i, "mem:$i");
                 }
     
    -            $i_tot_score += $i;
    +            $t_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;
    +        while ($keys = $this->redis->zscan('zset', $it)) {
    +            foreach ($keys as $mem => $f_score) {
    +                $t_score -= $f_score;
                     $i--;
                 }
             }
     
             $this->assertEquals(0, $i);
    -        $this->assertEquals((float)0, $i_tot_score);
    +        $this->assertEquals(0., $t_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;
    +        $p_score_old = $p_score;
    +        $p_count_old = $p_count;
    +        while ($keys = $this->redis->zscan('zset', $it, '*pmem*')) {
    +            foreach ($keys as $mem => $f_score) {
    +                $p_score -= $f_score;
    +                $p_count -= 1;
                 }
             }
    -        $this->assertEquals((float)0, $i_p_score);
    -        $this->assertEquals(0, $i_p_count);
    +        $this->assertEquals(0., $p_score);
    +        $this->assertEquals(0, $p_count);
     
             // Turn off retrying and we should get some empty results
             $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
    -        $i_skips = 0;
    -        $i_p_score = $i_p_score_old;
    -        $i_p_count = $i_p_count_old;
    +        [$skips, $p_score, $p_count] = [0, $p_score_old, $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;
    +        while (($keys = $this->redis->zscan('zset', $it, '*pmem*')) !== FALSE) {
    +            if (count($keys) == 0) $skips++;
    +            foreach ($keys as $mem => $f_score) {
    +                $p_score -= $f_score;
    +                $p_count -= 1;
                 }
             }
             // We should still get all the keys, just with several empty results
    -        $this->assertGT(0, $i_skips);
    -        $this->assertEquals((float)0, $i_p_score);
    -        $this->assertEquals(0, $i_p_count);
    +        $this->assertGT(0, $skips);
    +        $this->assertEquals(0., $p_score);
    +        $this->assertEquals(0, $p_count);
         }
     
         /* Make sure we capture errors when scanning */
         public function testScanErrors() {
             $this->redis->set('scankey', 'simplekey');
     
    -        foreach (['sScan', 'hScan', 'zScan'] as $str_method) {
    +        foreach (['sScan', 'hScan', 'zScan'] as $method) {
                 $it = NULL;
    -            $this->redis->$str_method('scankey', $it);
    +            $this->redis->$method('scankey', $it);
                 $this->assertEquals(0, strpos($this->redis->getLastError(), 'WRONGTYPE'));
             }
         }
    @@ -6146,74 +6113,73 @@ public function testScanErrors() {
         // HyperLogLog (PF) commands
         //
     
    -    protected function createPFKey($str_key, $i_count) {
    -        $arr_mems = [];
    -        for($i=0;$i<$i_count;$i++) {
    -            $arr_mems[] = uniqid() . '-' . $i;
    +    protected function createPFKey($key, $count) {
    +        $mems = [];
    +        for ($i = 0; $i< $count; $i++) {
    +            $mems[] = uniqid('pfmem:');
             }
     
             // Estimation by Redis
    -        $this->redis->pfadd($str_key, $i_count);
    +        $this->redis->pfAdd($key, $count);
         }
     
         public function testPFCommands() {
    -        // Isn't available until 2.8.9
             if (version_compare($this->version, '2.8.9') < 0)
                 $this->markTestSkipped();
     
    -        $str_uniq = uniqid();
    -        $arr_mems = [];
    +        $uniq = uniqid();
    +        $mems = [];
     
    -        for($i=0;$i<1000;$i++) {
    -            if($i%2 == 0) {
    -                $arr_mems[] = $str_uniq . '-' . $i;
    +        for ($i = 0; $i< 1000; $i++) {
    +            if ($i % 2 == 0) {
    +                $mems[] = "$uniq-$i";
                 } else {
    -                $arr_mems[] = $i;
    +                $mems[] = $i;
                 }
             }
     
             // How many keys to create
    -        $i_keys = 10;
    +        $key_count = 10;
     
             // Iterate prefixing/serialization options
    -        foreach([Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP] as $str_ser) {
    -            foreach(['', 'hl-key-prefix:'] as $str_prefix) {
    -                $arr_keys = [];
    +        foreach ([Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP] as $ser) {
    +            foreach (['', 'hl-key-prefix:'] as $prefix) {
    +                $keys = [];
     
                     // Now add for each key
    -                for($i=0;$i<$i_keys;$i++) {
    -                    $str_key    = "{key}:$i";
    -                    $arr_keys[] = $str_key;
    +                for ($i = 0; $i < $key_count; $i++) {
    +                    $key    = "{key}:$i";
    +                    $keys[] = $key;
     
                         // Clean up this key
    -                    $this->redis->del($str_key);
    +                    $this->redis->del($key);
     
                         // Add to our cardinality set, and confirm we got a valid response
    -                    $this->assertGT(0, $this->redis->pfadd($str_key, $arr_mems));
    +                    $this->assertGT(0, $this->redis->pfadd($key, $mems));
     
                         // Grab estimated cardinality
    -                    $i_card = $this->redis->pfcount($str_key);
    -                    $this->assertIsInt($i_card);
    +                    $card = $this->redis->pfcount($key);
    +                    $this->assertIsInt($card);
     
                         // Count should be close
    -                    $this->assertBetween($i_card, count($arr_mems) * .9, count($arr_mems) * 1.1);
    +                    $this->assertBetween($card, count($mems) * .9, count($mems) * 1.1);
     
                         // The PFCOUNT on this key should be the same as the above returned response
    -                    $this->assertEquals($i_card, $this->redis->pfcount($str_key));
    +                    $this->assertEquals($card, $this->redis->pfcount($key));
                     }
     
                     // Clean up merge key
                     $this->redis->del('pf-merge-{key}');
     
                     // Merge the counters
    -                $this->assertTrue($this->redis->pfmerge('pf-merge-{key}', $arr_keys));
    +                $this->assertTrue($this->redis->pfmerge('pf-merge-{key}', $keys));
     
                     // Validate our merged count
    -                $i_redis_card = $this->redis->pfcount('pf-merge-{key}');
    +                $redis_card = $this->redis->pfcount('pf-merge-{key}');
     
                     // Merged cardinality should still be roughly 1000
    -                $this->assertBetween($i_redis_card, count($arr_mems) * .9,
    -                                     count($arr_mems) * 1.1);
    +                $this->assertBetween($redis_card, count($mems) * .9,
    +                                     count($mems) * 1.1);
     
                     // Clean up merge key
                     $this->redis->del('pf-merge-{key}');
    @@ -6273,9 +6239,9 @@ public function genericGeoRadiusTest($cmd) {
             /* Pre tested with redis-cli.  We're just verifying proper delivery of distance and unit */
             if ($cmd == 'georadius' || $cmd == 'georadius_ro') {
                 $this->assertEquals(['Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi'));
    -            $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi'));
    -            $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 50, 'km'));
    -            $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm'));
    +            $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi'));
    +            $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 50, 'km'));
    +            $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm'));
                 $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 150000, 'ft'));
                 $args = [$cmd, '{gk}', $lng, $lat, 500, 'mi'];
     
    @@ -6285,9 +6251,9 @@ public function genericGeoRadiusTest($cmd) {
                 }
             } else {
                 $this->assertEquals(['Chico'], $this->redis->$cmd('{gk}', $city, 10, 'mi'));
    -            $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $city, 30, 'mi'));
    -            $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $city, 50, 'km'));
    -            $this->assertEquals(['Gridley','Chico'], $this->redis->$cmd('{gk}', $city, 50000, 'm'));
    +            $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 30, 'mi'));
    +            $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 50, 'km'));
    +            $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 50000, 'm'));
                 $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 150000, 'ft'));
                 $args = [$cmd, '{gk}', $city, 500, 'mi'];
     
    @@ -6439,12 +6405,12 @@ public function testRawCommand() {
             $key = uniqid();
     
             $this->redis->set($key,'some-value');
    -        $str_result = $this->redis->rawCommand('get', $key);
    -        $this->assertEquals($str_result, 'some-value');
    +        $result = $this->redis->rawCommand('get', $key);
    +        $this->assertEquals($result, 'some-value');
     
             $this->redis->del('mylist');
             $this->redis->rpush('mylist', 'A', 'B', 'C', 'D');
    -        $this->assertEquals(['A','B','C','D'], $this->redis->lrange('mylist', 0, -1));
    +        $this->assertEquals(['A', 'B', 'C', 'D'], $this->redis->lrange('mylist', 0, -1));
         }
     
         /* STREAMS */
    @@ -6461,13 +6427,13 @@ protected function addStreamEntries($key, $count) {
             return $ids;
         }
     
    -    protected function addStreamsAndGroups($arr_streams, $count, $arr_groups) {
    +    protected function addStreamsAndGroups($streams, $count, $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);
    +        foreach ($streams as $stream) {
    +            $ids[$stream] = $this->addStreamEntries($stream, $count);
    +            foreach ($groups as $group => $id) {
    +                $this->redis->xGroup('CREATE', $stream, $group, $id);
                 }
             }
     
    @@ -6570,12 +6536,12 @@ public function testXGroup() {
                 $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', 0, true));
    +        $key = 's:' . uniqid();
    +        $this->assertFalse($this->redis->xGroup('CREATE', $key, 'g0', 0));
    +        $this->assertTrue($this->redis->xGroup('CREATE', $key, 'g1', 0, true));
     
             /* XGROUP DESTROY */
    -        $this->assertEquals(1, $this->redis->xGroup('DESTROY', $str_key, 'g1'));
    +        $this->assertEquals(1, $this->redis->xGroup('DESTROY', $key, 'g1'));
     
             /* Populate some entries in stream 's' */
             $this->addStreamEntries('s', 2);
    @@ -6616,9 +6582,9 @@ public function testXGroup() {
             /* Make sure we handle the case where the user doesn't send enough arguments */
             $this->redis->clearLastError();
             $this->assertFalse(@$this->redis->xGroup('CREATECONSUMER'));
    -        $this->assertEquals(NULL, $this->redis->getLastError());
    +        $this->assertNull($this->redis->getLastError());
             $this->assertFalse(@$this->redis->xGroup('create'));
    -        $this->assertEquals(NULL, $this->redis->getLastError());
    +        $this->assertNull($this->redis->getLastError());
     
             if (!$this->minVersionCheck('7.0.0'))
                 return;
    @@ -7016,17 +6982,18 @@ public function testXInfo() {
             $this->assertIsArray($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));
    +            $this->assertArrayKey($group, 'name');
    +            $this->assertArrayKey($groups, $group['name']);
             }
     
             $info = $this->redis->xInfo('STREAM', $stream);
             $this->assertIsArray($info);
    -        $this->assertTrue(array_key_exists('groups', $info));
    -        $this->assertEquals(count($groups), $info['groups']);
    +        $this->assertArrayKey($info, 'groups', function ($v) use ($groups) {
    +            return count($groups) == $v;
    +        });
    +
             foreach (['first-entry', 'last-entry'] as $key) {
    -            $this->assertTrue(array_key_exists($key, $info));
    -            $this->assertTrue(is_array($info[$key]));
    +            $this->assertArrayKey($info, $key, 'is_array');
             }
     
             /* Ensure that default/NULL arguments are ignored */
    @@ -7063,9 +7030,9 @@ public function testXInfo() {
             /* Make sure we can't erroneously send non-null args after null ones */
             $this->redis->clearLastError();
             $this->assertFalse(@$this->redis->xInfo('FOO', NULL, 'fail', 25));
    -        $this->assertEquals(NULL, $this->redis->getLastError());
    +        $this->assertNull($this->redis->getLastError());
             $this->assertFalse(@$this->redis->xInfo('FOO', NULL, NULL, -2));
    -        $this->assertEquals(NULL, $this->redis->getLastError());
    +        $this->assertNull($this->redis->getLastError());
         }
     
         /* Regression test for issue-1831 (XINFO STREAM on an empty stream) */
    @@ -7075,33 +7042,33 @@ public function testXInfoEmptyStream() {
             $this->redis->xAdd('s', '*', ['foo' => 'bar']);
             $this->redis->xTrim('s', 0);
     
    -        $arr_info = $this->redis->xInfo('STREAM', 's');
    +        $info = $this->redis->xInfo('STREAM', 's');
     
    -        $this->assertIsArray($arr_info);
    -        $this->assertEquals(0, $arr_info['length']);
    -        $this->assertEquals(NULL, $arr_info['first-entry']);
    -        $this->assertEquals(NULL, $arr_info['last-entry']);
    +        $this->assertIsArray($info);
    +        $this->assertEquals(0, $info['length']);
    +        $this->assertNull($info['first-entry']);
    +        $this->assertNull($info['last-entry']);
         }
     
         public function testInvalidAuthArgs() {
    -        $obj_new = $this->newInstance();
    +        $client = $this->newInstance();
     
    -        $arr_args = [
    +        $args = [
                 [],
                 [NULL, NULL],
                 ['foo', 'bar', 'baz'],
    -            ['a','b','c','d'],
    -            ['a','b','c'],
    -            [['a','b'], 'a'],
    -            [['a','b','c']],
    +            ['a', 'b', 'c', 'd'],
    +            ['a', 'b', 'c'],
    +            [['a', 'b'], 'a'],
    +            [['a', 'b', 'c']],
                 [[NULL, 'pass']],
                 [[NULL, NULL]],
             ];
     
    -        foreach ($arr_args as $arr_arg) {
    +        foreach ($args as $arg) {
                 try {
    -                if (is_array($arr_arg)) {
    -                    @call_user_func_array([$obj_new, 'auth'], $arr_arg);
    +                if (is_array($arg)) {
    +                    @call_user_func_array([$client, 'auth'], $arg);
                     }
                 } catch (Exception $ex) {
                     unset($ex); /* Suppress intellisense warning */
    @@ -7121,28 +7088,28 @@ public function testAcl() {
             $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']);
    +        $admin = $this->redis->acl('GETUSER', 'admin');
    +        $this->assertInArray(hash('sha256', 'admin'), $admin['passwords']);
     
             /* Now nuke our 'admin' user and make sure it went away */
             $this->assertEquals(1, $this->redis->acl('DELUSER', 'admin'));
    -        $this->assertTrue(!in_array('admin', $this->redis->acl('USERS')));
    +        $this->assertFalse(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->assert('Expected an array from ACL LOG, got: ' . var_export($arr_log, true));
    +        $log = $this->redis->acl('log');
    +        if (! $log || !is_array($log)) {
    +            $this->assert('Expected an array from ACL LOG, got: ' . var_export($log, true));
                 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');
    +        $entry = array_shift($log);
    +        $this->assertArrayKey($entry, 'age-seconds', 'is_numeric');
    +        $this->assertArrayKey($entry, 'count', 'is_int');
     
             /* ACL CAT */
             $cats = $this->redis->acl('CAT');
    @@ -7186,7 +7153,7 @@ public function testUnixSocket() {
                 $this->markTestSkipped();
             }
     
    -        $arr_sock_tests = [
    +        $sock_tests = [
                 ['/tmp/redis.sock'],
                 ['/tmp/redis.sock', null],
                 ['/tmp/redis.sock', 0],
    @@ -7194,18 +7161,18 @@ public function testUnixSocket() {
             ];
     
             try {
    -            foreach ($arr_sock_tests as $arr_args) {
    -                $obj_r = new Redis();
    +            foreach ($sock_tests as $args) {
    +                $redis = new Redis();
     
    -                if (count($arr_args) == 2) {
    -                    @$obj_r->connect($arr_args[0], $arr_args[1]);
    +                if (count($args) == 2) {
    +                    @$redis->connect($args[0], $args[1]);
                     } else {
    -                    @$obj_r->connect($arr_args[0]);
    +                    @$redis->connect($args[0]);
                     }
                     if ($this->getAuth()) {
    -                    $this->assertTrue($obj_r->auth($this->getAuth()));
    +                    $this->assertTrue($redis->auth($this->getAuth()));
                     }
    -                $this->assertTrue($obj_r->ping());
    +                $this->assertTrue($redis->ping());
                 }
             } catch (Exception $ex) {
                 $this->assert("Exception: {$ex}");
    @@ -7236,13 +7203,13 @@ public function testHighPorts() {
                 $this->markTestSkipped();
     
             foreach ($ports as $port) {
    -            $obj_r = new Redis();
    +            $redis = new Redis();
                 try {
    -                @$obj_r->connect('localhost', $port);
    +                @$redis->connect('localhost', $port);
                     if ($this->getAuth()) {
    -                    $this->assertTrue($obj_r->auth($this->getAuth()));
    +                    $this->assertTrue($redis->auth($this->getAuth()));
                     }
    -                $this->assertTrue($obj_r->ping());
    +                $this->assertTrue($redis->ping());
                 } catch(Exception $ex) {
                     $this->assert("Exception: $ex");
                 }
    @@ -7514,7 +7481,7 @@ public function testMultipleConnect() {
             $host = $this->redis->GetHost();
             $port = $this->redis->GetPort();
     
    -        for($i = 0; $i < 5; $i++) {
    +        for ($i = 0; $i < 5; $i++) {
                 $this->redis->connect($host, $port);
                 if ($this->getAuth()) {
                     $this->assertTrue($this->redis->auth($this->getAuth()));
    @@ -7551,7 +7518,6 @@ public function testTlsConnect() {
         }
     
         public function testReset() {
    -        // Only available since 6.2.0
             if (version_compare($this->version, '6.2.0') < 0)
                 $this->markTestSkipped();
     
    @@ -7561,7 +7527,6 @@ public function testReset() {
         }
     
         public function testCopy() {
    -        // Only available since 6.2.0
             if (version_compare($this->version, '6.2.0') < 0)
                 $this->markTestSkipped();
     
    @@ -7583,7 +7548,7 @@ public function testCommand() {
             $this->assertIsArray($commands);
             $this->assertEquals(count($commands), $this->redis->command('count'));
     
    -        if (!$this->is_keydb) {
    +        if ( ! $this->is_keydb) {
                 $infos = $this->redis->command('info');
                 $this->assertIsArray($infos);
                 $this->assertEquals(count($infos), count($commands));
    diff --git a/tests/TestRedis.php b/tests/TestRedis.php
    index 1ff1114efe..3efca43ffd 100644
    --- a/tests/TestRedis.php
    +++ b/tests/TestRedis.php
    @@ -24,7 +24,7 @@ function getClassArray($classes) {
     }
     
     function getTestClass($class) {
    -    $arr_valid_classes = [
    +    $valid_classes = [
             'redis'         => 'Redis_Test',
             'redisarray'    => 'Redis_Array_Test',
             'rediscluster'  => 'Redis_Cluster_Test',
    @@ -32,80 +32,91 @@ function getTestClass($class) {
         ];
     
         /* Return early if the class is one of our built-in ones */
    -    if (isset($arr_valid_classes[$class]))
    -        return $arr_valid_classes[$class];
    +    if (isset($valid_classes[$class]))
    +        return $valid_classes[$class];
     
         /* Try to load it */
         return TestSuite::loadTestClass($class);
     }
     
    +function raHosts($host, $ports) {
    +    if ( ! is_array($ports))
    +        $ports = [6379, 6380, 6381, 6382];
    +
    +    return array_map(function ($port) use ($host) {
    +        return sprintf("%s:%d", $host, $port);
    +    }, $ports);
    +}
    +
     /* Make sure errors go to stdout and are shown */
     error_reporting(E_ALL);
     ini_set( 'display_errors','1');
     
     /* Grab options */
    -$arr_args = getopt('', ['host:', 'port:', 'class:', 'test:', 'nocolors', 'user:', 'auth:']);
    +$opt = getopt('', ['host:', 'port:', 'class:', 'test:', 'nocolors', 'user:', 'auth:']);
     
     /* The test class(es) we want to run */
    -$arr_classes = getClassArray($arr_args['class'] ?? 'redis');
    +$classes = getClassArray($opt['class'] ?? 'redis');
     
    -$boo_colorize = !isset($arr_args['nocolors']);
    +$colorize = !isset($opt['nocolors']);
     
     /* Get our test filter if provided one */
    -$str_filter = isset($arr_args['test']) ? $arr_args['test'] : NULL;
    +$filter = $opt['test'] ?? NULL;
     
     /* Grab override host/port if it was passed */
    -$str_host = isset($arr_args['host']) ? $arr_args['host'] : '127.0.0.1';
    -$i_port = isset($arr_args['port']) ? intval($arr_args['port']) : 6379;
    +$host = $opt['host'] ?? '127.0.0.1';
    +$port = $opt['port'] ?? 6379;
     
     /* Get optional username and auth (password) */
    -$str_user = isset($arr_args['user']) ? $arr_args['user'] : NULL;
    -$str_auth = isset($arr_args['auth']) ? $arr_args['auth'] : NULL;
    -
    -/* Massage the actual auth arg */
    -$auth = NULL;
    -if ($str_user && $str_auth) {
    -    $auth = [$str_user, $str_auth];
    -} else if ($str_auth) {
    -    $auth = $str_auth;
    -} else if ($str_user) {
    -    echo TestSuite::make_warning("User passed without a password, ignoring!\n");
    +$user = $opt['user'] ?? NULL;
    +$auth = $opt['auth'] ?? NULL;
    +
    +if ($user && $auth) {
    +    $auth = [$user, $auth];
    +} else if ($user && ! $auth) {
    +    echo TestSuite::make_warning("User passed without a password!\n");
     }
     
     /* Toggle colorization in our TestSuite class */
    -TestSuite::flagColorization($boo_colorize);
    +TestSuite::flagColorization($colorize);
     
     /* Let the user know this can take a bit of time */
     echo "Note: these tests might take up to a minute. Don't worry :-)\n";
    -echo "Using PHP version " . PHP_VERSION . " (" . (PHP_INT_SIZE*8) . " bits)\n";
    +echo "Using PHP version " . PHP_VERSION . " (" . (PHP_INT_SIZE * 8) . " bits)\n";
     
    -foreach ($arr_classes as $str_class) {
    -    $str_class = getTestClass($str_class);
    +foreach ($classes as $class) {
    +    $class = getTestClass($class);
     
         /* Depending on the classes being tested, run our tests on it */
         echo "Testing class ";
    -    if ($str_class == 'Redis_Array_Test') {
    +    if ($class == 'Redis_Array_Test') {
             echo TestSuite::make_bold("RedisArray") . "\n";
     
    -        foreach(array(true, false) as $useIndex) {
    +        $full_ring = raHosts($host, $port);
    +        $sub_ring  = array_slice($full_ring, 0, -1);
    +
    +        echo TestSuite::make_bold("Full Ring: ") . implode(' ', $full_ring) . "\n";
    +        echo TestSuite::make_bold(" New Ring: ") . implode(' ',  $sub_ring) . "\n";
    +
    +        foreach([true, false] as $useIndex) {
                 echo "\n". ($useIndex ? "WITH" : "WITHOUT") . " per-node index:\n";
     
                 /* The various RedisArray subtests we can run */
    -            $arr_ra_tests = [
    +            $test_classes = [
                     'Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test',
                     'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'
                 ];
     
    -            foreach ($arr_ra_tests as $str_test) {
    +            foreach ($test_classes as $test_class) {
                     /* Run until we encounter a failure */
    -                if (run_tests($str_test, $str_filter, $str_host, $auth) != 0) {
    +                if (run_ra_tests($test_class, $filter, $host, $full_ring, $sub_ring, $auth) != 0) {
                         exit(1);
                     }
                 }
             }
         } else {
    -        echo TestSuite::make_bold($str_class) . "\n";
    -        if (TestSuite::run("$str_class", $str_filter, $str_host, $i_port, $auth))
    +        echo TestSuite::make_bold($class) . "\n";
    +        if (TestSuite::run("$class", $filter, $host, $port, $auth))
                 exit(1);
         }
     }
    diff --git a/tests/TestSuite.php b/tests/TestSuite.php
    index a50483e42c..605d042d7c 100644
    --- a/tests/TestSuite.php
    +++ b/tests/TestSuite.php
    @@ -187,6 +187,15 @@ protected function assertIsString($v): bool {
             return false;
         }
     
    +    protected function assertIsBool($v): bool {
    +        if (is_bool($v))
    +            return true;
    +
    +        self::$errors []= $this->assertionTrace("%s is not a boolean", $this->printArg($v));
    +
    +        return false;
    +    }
    +
         protected function assertIsInt($v): bool {
             if (is_int($v))
                 return true;
    @@ -196,6 +205,19 @@ protected function assertIsInt($v): bool {
             return false;
         }
     
    +    protected function assertIsObject($v, ?string $type = NULL): bool {
    +        if ( ! is_object($v)) {
    +            self::$errors []= $this->assertionTrace("%s is not an object", $this->printArg($v));
    +            return false;
    +        } else if ( $type !== NULL && !($v InstanceOf $type)) {
    +            self::$errors []= $this->assertionTrace("%s is not an instance of %s",
    +                                                    $this->printArg($v), $type);
    +            return false;
    +        }
    +
    +        return true;
    +    }
    +
         protected function assertIsArray($v, ?int $size = null): bool {
             if ( ! is_array($v)) {
                 self::$errors []= $this->assertionTrace("%s is not an array", $this->printArg($v));
    @@ -497,17 +519,17 @@ public static function flagColorization(bool $override) {
                 posix_isatty(STDOUT);
         }
     
    -    public static function run($className, ?string $limit = NULL,
    +    public static function run($class_name, ?string $limit = NULL,
                                    ?string $host = NULL, ?int $port = NULL,
                                    $auth = NULL)
         {
             /* Lowercase our limit arg if we're passed one */
             $limit ??= strtolower($limit);
     
    -        $rc = new ReflectionClass($className);
    +        $rc = new ReflectionClass($class_name);
             $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC);
     
    -        $i_max_len = self::getMaxTestLen($methods, $limit);
    +        $max_test_len = self::getMaxTestLen($methods, $limit);
     
             foreach($methods as $m) {
                 $name = $m->name;
    @@ -520,42 +542,42 @@ public static function run($className, ?string $limit = NULL,
                     continue;
                 }
     
    -            $str_out_name = str_pad($name, $i_max_len + 1);
    +            $str_out_name = str_pad($name, $max_test_len + 1);
                 echo self::make_bold($str_out_name);
     
    -            $count = count($className::$errors);
    -            $rt = new $className($host, $port, $auth);
    +            $count = count($class_name::$errors);
    +            $rt = new $class_name($host, $port, $auth);
     
                 try {
                     $rt->setUp();
                     $rt->$name();
     
    -                if ($count === count($className::$errors)) {
    -                    $str_msg = self::make_success('PASSED');
    +                if ($count === count($class_name::$errors)) {
    +                    $result = self::make_success('PASSED');
                     } else {
    -                    $str_msg = self::make_fail('FAILED');
    +                    $result = self::make_fail('FAILED');
                     }
                 } catch (Exception $e) {
                     /* We may have simply skipped the test */
                     if ($e instanceof TestSkippedException) {
    -                    $str_msg = self::make_warning('SKIPPED');
    +                    $result = self::make_warning('SKIPPED');
                     } else {
    -                    $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n";
    -                    $str_msg = self::make_fail('FAILED');
    +                    $class_name::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n";
    +                    $result = self::make_fail('FAILED');
                     }
                 }
     
    -            echo "[" . $str_msg . "]\n";
    +            echo "[" . $result . "]\n";
             }
             echo "\n";
    -        echo implode('', $className::$warnings) . "\n";
    +        echo implode('', $class_name::$warnings) . "\n";
     
    -        if (empty($className::$errors)) {
    +        if (empty($class_name::$errors)) {
                 echo "All tests passed. \o/\n";
                 return 0;
             }
     
    -        echo implode('', $className::$errors);
    +        echo implode('', $class_name::$errors);
             return 1;
         }
     }
    
    From c6cd665bdea5bd446ae1213b07a8c7bd232fe75e Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 30 May 2024 11:46:36 -0700
    Subject: [PATCH 1885/1986] Code formatting
    
    ---
     tests/RedisArrayTest.php      |  71 ++++++------
     tests/RedisClusterTest.php    |  11 +-
     tests/RedisTest.php           | 200 +++++++++++++++++-----------------
     tests/TestSuite.php           |   4 +-
     tests/getSessionData.php      |   2 +-
     tests/regenerateSessionId.php |   4 +-
     6 files changed, 144 insertions(+), 148 deletions(-)
    
    diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php
    index f28f4dab45..0586f09289 100644
    --- a/tests/RedisArrayTest.php
    +++ b/tests/RedisArrayTest.php
    @@ -7,7 +7,7 @@
     function custom_hash($str) {
         // str has the following format: $APPID_fb$FACEBOOKID_$key.
         $pos = strpos($str, '_fb');
    -    if(preg_match("#\w+_fb(?\d+)_\w+#", $str, $out)) {
    +    if (preg_match("#\w+_fb(?\d+)_\w+#", $str, $out)) {
                 return $out['facebook_id'];
         }
         return $str;
    @@ -21,7 +21,7 @@ function parseHostPort($str, &$host, &$port) {
     
     function getRedisVersion(object $client) {
         $arr_info = $client->info();
    -    if (!$arr_info || !isset($arr_info['redis_version'])) {
    +    if ( ! $arr_info || !isset($arr_info['redis_version'])) {
             return '0.0.0';
         }
         return $arr_info['redis_version'];
    @@ -51,7 +51,7 @@ public function setUp() {
             // initialize strings.
             $n = REDIS_ARRAY_DATA_SIZE;
             $this->strings = [];
    -        for($i = 0; $i < $n; $i++) {
    +        for ($i = 0; $i < $n; $i++) {
                 $this->strings['key-'.$i] = 'val-'.$i;
             }
     
    @@ -70,12 +70,12 @@ public function testMSet() {
             $this->assertTrue($this->ra->mset($this->strings));
     
             // check each key individually using the array
    -        foreach($this->strings as $k => $v) {
    +        foreach ($this->strings as $k => $v) {
                 $this->assertEquals($v, $this->ra->get($k));
             }
     
             // check each key individually using a new connection
    -        foreach($this->strings as $k => $v) {
    +        foreach ($this->strings as $k => $v) {
                 parseHostPort($this->ra->_target($k), $host, $port);
     
                 $target = $this->ra->_target($k);
    @@ -99,7 +99,7 @@ public function testMGet() {
     
         private function addData($commonString) {
             $this->data = [];
    -        for($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) {
    +        for ($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) {
                 $k = rand().'_'.$commonString.'_'.rand();
                 $this->data[$k] = rand();
             }
    @@ -109,9 +109,9 @@ private function addData($commonString) {
         private function checkCommonLocality() {
             // check that they're all on the same node.
             $lastNode = NULL;
    -        foreach($this->data as $k => $v) {
    +        foreach ($this->data as $k => $v) {
                     $node = $this->ra->_target($k);
    -                if($lastNode) {
    +                if ($lastNode) {
                         $this->assertEquals($node, $lastNode);
                     }
                     $this->assertEqualsWeak($v, $this->ra->get($k));
    @@ -172,7 +172,7 @@ public function testKeyDistributor()
     
             // check that they're all on the expected node.
             $lastNode = NULL;
    -        foreach($this->data as $k => $v) {
    +        foreach ($this->data as $k => $v) {
                 $node = $this->ra->_target($k);
                 $pos = $this->customDistributor($k);
                 $this->assertEquals($node, $new_ring[$pos]);
    @@ -238,30 +238,30 @@ public function setUp() {
             // initialize strings.
             $n = REDIS_ARRAY_DATA_SIZE;
             $this->strings = [];
    -        for($i = 0; $i < $n; $i++) {
    +        for ($i = 0; $i < $n; $i++) {
                 $this->strings['key-'.$i] = 'val-'.$i;
             }
     
             // initialize sets
    -        for($i = 0; $i < $n; $i++) {
    +        for ($i = 0; $i < $n; $i++) {
                 // each set has 20 elements
                 $this->sets['set-'.$i] = range($i, $i+20);
             }
     
             // initialize lists
    -        for($i = 0; $i < $n; $i++) {
    +        for ($i = 0; $i < $n; $i++) {
                 // each list has 20 elements
                 $this->lists['list-'.$i] = range($i, $i+20);
             }
     
             // initialize hashes
    -        for($i = 0; $i < $n; $i++) {
    +        for ($i = 0; $i < $n; $i++) {
                 // each hash has 5 keys
                 $this->hashes['hash-'.$i] = ['A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4];
             }
     
             // initialize sorted sets
    -        for($i = 0; $i < $n; $i++) {
    +        for ($i = 0; $i < $n; $i++) {
                 // each sorted sets has 5 elements
                 $this->zsets['zset-'.$i] = [$i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E'];
             }
    @@ -282,7 +282,7 @@ public function setUp() {
         public function testFlush() {
             // flush all servers first.
             global $server_list;
    -        foreach($server_list as $s) {
    +        foreach ($server_list as $s) {
                 parseHostPort($s, $host, $port);
     
                 $r = new Redis();
    @@ -298,27 +298,27 @@ public function testFlush() {
         private function distributeKeys() {
     
             // strings
    -        foreach($this->strings as $k => $v) {
    +        foreach ($this->strings as $k => $v) {
                 $this->ra->set($k, $v);
             }
     
             // sets
    -        foreach($this->sets as $k => $v) {
    +        foreach ($this->sets as $k => $v) {
                 call_user_func_array([$this->ra, 'sadd'], array_merge([$k], $v));
             }
     
             // lists
    -        foreach($this->lists as $k => $v) {
    +        foreach ($this->lists as $k => $v) {
                 call_user_func_array([$this->ra, 'rpush'], array_merge([$k], $v));
             }
     
             // hashes
    -        foreach($this->hashes as $k => $v) {
    +        foreach ($this->hashes as $k => $v) {
                 $this->ra->hmset($k, $v);
             }
     
             // sorted sets
    -        foreach($this->zsets as $k => $v) {
    +        foreach ($this->zsets as $k => $v) {
                 call_user_func_array([$this->ra, 'zadd'], array_merge([$k], $v));
             }
         }
    @@ -334,36 +334,36 @@ public function testSimpleRead() {
         private function readAllvalues() {
     
             // strings
    -        foreach($this->strings as $k => $v) {
    +        foreach ($this->strings as $k => $v) {
                 $this->assertEquals($v, $this->ra->get($k));
             }
     
             // sets
    -        foreach($this->sets as $k => $v) {
    +        foreach ($this->sets as $k => $v) {
                 $ret = $this->ra->smembers($k); // get values
     
                 $this->assertEqualsWeak($v, $ret);
             }
     
             // lists
    -        foreach($this->lists as $k => $v) {
    +        foreach ($this->lists as $k => $v) {
                 $ret = $this->ra->lrange($k, 0, -1);
                 $this->assertEqualsWeak($v, $ret);
             }
     
             // hashes
    -        foreach($this->hashes as $k => $v) {
    +        foreach ($this->hashes as $k => $v) {
                 $ret = $this->ra->hgetall($k); // get values
                 $this->assertEqualsWeak($v, $ret);
             }
     
             // sorted sets
    -        foreach($this->zsets as $k => $v) {
    -            $ret = $this->ra->zrange($k, 0, -1, TRUE);
    +        foreach ($this->zsets as $k => $v) {
    +            $ret = $this->ra->zrange($k, 0, -1, true);
     
                 // create assoc array from local dataset
                 $tmp = [];
    -            for($i = 0; $i < count($v); $i += 2) {
    +            for ($i = 0; $i < count($v); $i += 2) {
                     $tmp[$v[$i+1]] = $v[$i];
                 }
     
    @@ -413,7 +413,7 @@ public function setUp() {
             // initialize strings.
             $n = REDIS_ARRAY_DATA_SIZE;
             $this->strings = [];
    -        for($i = 0; $i < $n; $i++) {
    +        for ($i = 0; $i < $n; $i++) {
                 $this->strings['key-'.$i] = 'val-'.$i;
             }
     
    @@ -421,7 +421,7 @@ public function setUp() {
             $options = [
                 'previous' => $old_ring,
                 'index' => $useIndex,
    -            'autorehash' => TRUE
    +            'autorehash' => true
             ];
             if ($this->getAuth()) {
                 $options['auth'] = $this->getAuth();
    @@ -433,13 +433,13 @@ public function setUp() {
     
         public function testDistribute() {
             // strings
    -        foreach($this->strings as $k => $v) {
    +        foreach ($this->strings as $k => $v) {
                 $this->ra->set($k, $v);
             }
         }
     
         private function readAllvalues() {
    -        foreach($this->strings as $k => $v) {
    +        foreach ($this->strings as $k => $v) {
                 $this->assertEquals($v, $this->ra->get($k));
             }
         }
    @@ -463,7 +463,7 @@ public function testReadAndMigrateAll() {
     
         // Read and migrate keys on fallback, causing the whole ring to be rehashed.
         public function testAllKeysHaveBeenMigrated() {
    -        foreach($this->strings as $k => $v) {
    +        foreach ($this->strings as $k => $v) {
                 parseHostPort($this->ra->_target($k), $host, $port);
     
                 $r = new Redis;
    @@ -510,9 +510,9 @@ public function testInit() {
         public function testKeyDistribution() {
             // check that all of joe's keys are on the same instance
             $lastNode = NULL;
    -        foreach(['name', 'group', 'salary'] as $field) {
    +        foreach (['name', 'group', 'salary'] as $field) {
                 $node = $this->ra->_target('1_{employee:joe}_'.$field);
    -            if($lastNode) {
    +            if ($lastNode) {
                     $this->assertEquals($node, $lastNode);
                 }
                 $lastNode = $node;
    @@ -554,7 +554,6 @@ public function testMultiExecMSet() {
         }
     
         public function testMultiExecMGet() {
    -        // test MGET
             $out = $this->ra->multi($this->ra->_target('{employee:joe}'))
                     ->mget(['1_{employee:joe}_group', '1_{employee:joe}_salary'])
                     ->exec();
    @@ -564,8 +563,6 @@ public function testMultiExecMGet() {
         }
     
         public function testMultiExecDel() {
    -
    -        // test DEL
             $out = $this->ra->multi($this->ra->_target('{employee:joe}'))
                 ->del('1_{employee:joe}_group', '1_{employee:joe}_salary')
                 ->exec();
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index b0170f69cc..3e081e9ecf 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -345,11 +345,11 @@ public function testPubSub() {
             $this->assertIsArray($result);
             $this->assertEquals(4, count($result));
     
    -        $arr_zipped = [];
    -        for ($i = 0; $i <= count($result) / 2; $i+=2) {
    -            $arr_zipped[$result[$i]] = $result[$i+1];
    +        $zipped = [];
    +        for ($i = 0; $i <= count($result) / 2; $i += 2) {
    +            $zipped[$result[$i]] = $result[$i+1];
             }
    -        $result = $arr_zipped;
    +        $result = $zipped;
     
             // Make sure the elements are correct, and have zero counts
             foreach([$c1,$c2] as $channel) {
    @@ -640,6 +640,7 @@ protected function setKeyVals($key_index, $key_type, &$arr_ref) {
     
             /* Update our reference array so we can verify values */
             $arr_ref[$key] = $value;
    +
             return $key;
         }
     
    @@ -757,7 +758,7 @@ public function testSession()
             @ini_set('session.save_handler', 'rediscluster');
             @ini_set('session.save_path', $this->sessionSavePath() . '&failover=error');
     
    -        if (!@session_start())
    +        if ( ! @session_start())
                 $this->markTestSkipped();
     
             session_write_close();
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index dc04f31f3e..0d371d20f3 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -253,7 +253,7 @@ public function testBitcount() {
         }
     
         public function testBitop() {
    -        if (!$this->minVersionCheck('2.6.0'))
    +        if ( ! $this->minVersionCheck('2.6.0'))
                 $this->markTestSkipped();
     
             $this->redis->set('{key}1', 'foobar');
    @@ -452,7 +452,7 @@ public function testBitPos() {
             $this->redis->set('bpkey', "\x00\x00\x00");
             $this->assertEquals(-1, $this->redis->bitpos('bpkey', 1));
     
    -        if (!$this->minVersionCheck('7.0.0'))
    +        if ( ! $this->minVersionCheck('7.0.0'))
                 return;
     
             $this->redis->set('bpkey', "\xF");
    @@ -528,7 +528,7 @@ public function testSet() {
             $this->assertEquals('0.1', $this->redis->get('key'));
             $this->assertTrue($this->redis->set('key', '0.1'));
             $this->assertEquals('0.1', $this->redis->get('key'));
    -        $this->assertTrue($this->redis->set('key', TRUE));
    +        $this->assertTrue($this->redis->set('key', true));
             $this->assertEquals('1', $this->redis->get('key'));
     
             $this->assertTrue($this->redis->set('key', ''));
    @@ -744,7 +744,7 @@ public function testExpireAt() {
         }
     
         function testExpireOptions() {
    -        if (!$this->minVersionCheck('7.0.0'))
    +        if ( ! $this->minVersionCheck('7.0.0'))
                 return;
     
             $this->redis->set('eopts', 'value');
    @@ -943,7 +943,7 @@ public function testExists() {
         }
     
         public function testTouch() {
    -        if (!$this->minVersionCheck('3.2.1'))
    +        if ( ! $this->minVersionCheck('3.2.1'))
                 $this->markTestSkipped();
     
             $this->redis->del('notakey');
    @@ -1318,9 +1318,9 @@ public function testSortPrefix() {
             $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'));
    +        $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');
    @@ -1330,7 +1330,7 @@ public function testSortPrefix() {
         public function testSortAsc() {
             $this->setupSort();
             // sort by age and get IDs
    -        $byAgeAsc = ['3','1','2','4'];
    +        $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.
    @@ -1378,15 +1378,15 @@ public function testSortAsc() {
             }
     
             // 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]));
    +        $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'];
    +        $byAgeDesc = ['4', '2', '1', '3'];
             $this->assertEquals($byAgeDesc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'sort' => 'desc']));
     
             // sort by age and get names
    @@ -1408,7 +1408,7 @@ public function testSortDesc() {
             }
     
             // SORT list ALPHA → [abc, def, ghi]
    -        $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sort('list', ['sort' => 'desc', 'alpha' => TRUE]));
    +        $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sort('list', ['sort' => 'desc', 'alpha' => true]));
         }
     
         /* This test is just to make sure SORT and SORT_RO are both callable */
    @@ -1596,7 +1596,7 @@ public function testsPop() {
     
             $v0 = $this->redis->sPop('set0');
             $this->assertEquals(1, $this->redis->scard('set0'));
    -        $this->assertInArray($v0, ['val' ,'val2']);
    +        $this->assertInArray($v0, ['val', 'val2']);
             $v1 = $this->redis->sPop('set0');
             $this->assertEquals(0, $this->redis->scard('set0'));
             $this->assertEqualsCanonicalizing(['val', 'val2'], [$v0, $v1]);
    @@ -1605,7 +1605,7 @@ public function testsPop() {
         }
     
         public function testsPopWithCount() {
    -        if (!$this->minVersionCheck('3.2'))
    +        if ( ! $this->minVersionCheck('3.2'))
                 $this->markTestSkipped();
     
             $set = 'set0';
    @@ -1685,7 +1685,7 @@ public function testSRandMemberWithCount() {
             $this->assertEquals([], $ret_neg);
     
             // Add a few items to the set
    -        for ($i = 0; $i< 100; $i++) {
    +        for ($i = 0; $i < 100; $i++) {
                 $this->redis->sadd('set0', "member$i");
             }
     
    @@ -2319,8 +2319,8 @@ public function testWait() {
             $this->assertEquals($replicas, $this->redis->wait($replicas, 100));
     
             // Pass more slaves than are connected
    -        $this->redis->set('wait-foo','over9000');
    -        $this->redis->set('wait-bar','revo9000');
    +        $this->redis->set('wait-foo', 'over9000');
    +        $this->redis->set('wait-bar', 'revo9000');
             $this->assertLT($replicas + 1, $this->redis->wait($replicas + 1, 100));
     
             // Make sure when we pass with bad arguments we just get back false
    @@ -2374,7 +2374,7 @@ public function testInfo() {
                 }
             }
     
    -        if (!$this->minVersionCheck('7.0.0'))
    +        if ( ! $this->minVersionCheck('7.0.0'))
                 return;
     
             $res = $this->redis->info('server', 'memory');
    @@ -2493,7 +2493,7 @@ public function testBRpopLpush() {
             $this->assertEquals([], $this->redis->lrange('{list}x', 0, -1));
             $this->assertEquals([], $this->redis->lrange('{list}y', 0, -1));
     
    -        if (!$this->minVersionCheck('6.0.0'))
    +        if ( ! $this->minVersionCheck('6.0.0'))
                 return;
     
             // Redis >= 6.0.0 allows floating point timeouts
    @@ -2579,7 +2579,7 @@ public function testZX() {
     
             // withscores
             $this->redis->zRem('key', 'aal3');
    -        $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]);
    +        $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, ['withscores' => true]);
             $this->assertEquals(['val0' => 0.0, 'val1' => 1.0, 'val2' => 2.0, 'val3' => 3.0], $zero_to_three);
             $this->assertEquals(4, $this->redis->zCount('key', 0, 3));
     
    @@ -2638,17 +2638,17 @@ public function testZX() {
             $this->redis->zAdd('zset', 4, 'foz');
             $this->assertEquals(
                 ['foo' => 1.0, 'bar' => 2.0, 'biz' => 3.0, 'foz' => 4.0],
    -            $this->redis->zRangeByScore('zset', '-inf', '+inf', ['withscores' => TRUE])
    +            $this->redis->zRangeByScore('zset', '-inf', '+inf', ['withscores' => true])
             );
             $this->assertEquals(
                 ['foo' => 1.0, 'bar' => 2.0],
    -            $this->redis->zRangeByScore('zset', 1, 2, ['withscores' => TRUE])
    +            $this->redis->zRangeByScore('zset', 1, 2, ['withscores' => true])
             );
             $this->assertEquals(
                 ['bar' => 2.0],
    -            $this->redis->zRangeByScore('zset', '(1', 2, ['withscores' => TRUE])
    +            $this->redis->zRangeByScore('zset', '(1', 2, ['withscores' => true])
             );
    -        $this->assertEquals([], $this->redis->zRangeByScore('zset', '(1', '(2', ['withscores' => TRUE]));
    +        $this->assertEquals([], $this->redis->zRangeByScore('zset', '(1', '(2', ['withscores' => true]));
     
             $this->assertEquals(4, $this->redis->zCount('zset', '-inf', '+inf'));
             $this->assertEquals(2, $this->redis->zCount('zset', 1, 2));
    @@ -2705,12 +2705,12 @@ public function testZX() {
             //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->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}2'], [1, 1], 'MIN');
             $this->assertEquals(1.0, $this->redis->zScore('{zset}U', 'duplicate'));
             $this->redis->del('{zset}U');
     
             //now test zUnion *without* weights but with aggregate function
    -        $this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], null, 'MIN');
    +        $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}2'], null, 'MIN');
             $this->assertEquals(1.0, $this->redis->zScore('{zset}U', 'duplicate'));
             $this->redis->del('{zset}U', '{zset}1', '{zset}2');
     
    @@ -2734,15 +2734,15 @@ public function testZX() {
             $this->redis->zadd('{zset}2', 3, 'three', 4, 'four', 5, 'five');
     
             // Make sure phpredis handles these weights
    -        $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, 'inf']) );
    -        $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '-inf']));
    -        $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '+inf']));
    +        $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [1, 'inf']) );
    +        $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [1, '-inf']));
    +        $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [1, '+inf']));
     
             // Now, confirm that they're being sent, and that it works
    -        $weights = ['inf','-inf','+inf'];
    +        $weights = ['inf', '-inf', '+inf'];
     
             foreach ($weights as $weight) {
    -            $r = $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1,$weight]);
    +            $r = $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [1, $weight]);
                 $this->assertEquals(5, $r);
                 $r = $this->redis->zrangebyscore('{zset}3', '(-inf', '(inf',['withscores'=>true]);
                 $this->assertEquals(2, count($r));
    @@ -2750,13 +2750,13 @@ public function testZX() {
                 $this->assertArrayKey($r, 'two');
             }
     
    -        $this->redis->del('{zset}1','{zset}2','{zset}3');
    +        $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, -1, TRUE);
    +        $ret = $this->redis->zRange('{zset}1', 0, -1, true);
             $this->assertEquals(3, count($ret));
             $retValues = array_keys($ret);
     
    @@ -2774,7 +2774,7 @@ public function testZX() {
             $this->redis->zAdd('{zset}1', 2, 'two');
             $this->redis->zAdd('{zset}1', 3, 'three');
             $this->assertEquals(2, $this->redis->zremrangebyrank('{zset}1', 0, 1));
    -        $this->assertEquals(['three' => 3.], $this->redis->zRange('{zset}1', 0, -1, TRUE));
    +        $this->assertEquals(['three' => 3.], $this->redis->zRange('{zset}1', 0, -1, true));
     
             $this->redis->del('{zset}1');
     
    @@ -3151,7 +3151,7 @@ public function testHashes() {
                 $this->assertEquals(1.5, $this->redis->hincrByFloat('h', 'x', -1.5));
                 $this->assertEquals(1000000000001.5, $this->redis->hincrByFloat('h', 'x', 1000000000000));
     
    -            $this->redis->hset('h', 'y','not-a-number');
    +            $this->redis->hset('h', 'y', 'not-a-number');
                 $this->assertFalse($this->redis->hIncrByFloat('h', 'y', 1.5));
             }
     
    @@ -3345,7 +3345,7 @@ public function testFailedTransactions() {
         }
     
         public function testPipeline() {
    -        if (!$this->havePipeline())
    +        if ( ! $this->havePipeline())
                 $this->markTestSkipped();
     
             $this->sequence(Redis::PIPELINE);
    @@ -3359,7 +3359,7 @@ public function testPipeline() {
         }
     
         public function testPipelineMultiExec() {
    -        if (!$this->havePipeline())
    +        if ( ! $this->havePipeline())
                 $this->markTestSkipped();
     
             $ret = $this->redis->pipeline()->multi()->exec()->exec();
    @@ -3385,7 +3385,7 @@ public function testDoublePipeNoOp() {
             }
     
             /* Set and get in our pipeline */
    -        $this->redis->set('pipecount','over9000')->get('pipecount');
    +        $this->redis->set('pipecount', 'over9000')->get('pipecount');
     
             $data = $this->redis->exec();
             $this->assertEquals([true,'over9000'], $data);
    @@ -3408,7 +3408,7 @@ public function testDiscard() {
                 $this->redis->multi($mode);
     
                 /* Set and get in our transaction */
    -            $this->redis->set('pipecount','over9000')->get('pipecount');
    +            $this->redis->set('pipecount', 'over9000')->get('pipecount');
     
                 /* first call closes transaction and clears commands queue */
                 $this->assertTrue($this->redis->discard());
    @@ -3457,22 +3457,22 @@ protected function sequence($mode) {
             $i = 0;
             $this->assertIsArray($ret);
             $this->assertTrue(is_long($ret[$i++]));
    -        $this->assertEqualsWeak(TRUE, $ret[$i++]);
    +        $this->assertEqualsWeak(true, $ret[$i++]);
             $this->assertEqualsWeak('value1', $ret[$i++]);
             $this->assertEqualsWeak('value1', $ret[$i++]);
             $this->assertEqualsWeak('value2', $ret[$i++]);
    -        $this->assertEqualsWeak(TRUE, $ret[$i++]);
    +        $this->assertEqualsWeak(true, $ret[$i++]);
             $this->assertEqualsWeak(5, $ret[$i++]);
             $this->assertEqualsWeak(5, $ret[$i++]);
             $this->assertEqualsWeak(4, $ret[$i++]);
             $this->assertEqualsWeak(4, $ret[$i++]);
    -        $this->assertEqualsWeak(TRUE, $ret[$i++]);
    +        $this->assertEqualsWeak(true, $ret[$i++]);
             $this->assertEqualsWeak(4, $ret[$i++]);
             $this->assertEqualsWeak(FALSE, $ret[$i++]);
    -        $this->assertEqualsWeak(TRUE, $ret[$i++]);
    -        $this->assertEqualsWeak(TRUE, $ret[$i++]);
    +        $this->assertEqualsWeak(true, $ret[$i++]);
    +        $this->assertEqualsWeak(true, $ret[$i++]);
             $this->assertEqualsWeak(9, $ret[$i++]);
    -        $this->assertEqualsWeak(TRUE, $ret[$i++]);
    +        $this->assertEqualsWeak(true, $ret[$i++]);
             $this->assertEqualsWeak(4, $ret[$i++]);
             $this->assertEquals($i, count($ret));
     
    @@ -3674,11 +3674,11 @@ protected function sequence($mode) {
             $this->assertTrue(is_long($ret[$i++]));
             $this->assertIsArray($ret[$i++], 3);
     //        $i++;
    -        $this->assertTrue($ret[$i++]); // mset always returns TRUE
    -        $this->assertTrue($ret[$i++]); // set always returns TRUE
    -        $this->assertTrue($ret[$i++]); // expire always returns TRUE
    +        $this->assertTrue($ret[$i++]); // mset always returns true
    +        $this->assertTrue($ret[$i++]); // set always returns true
    +        $this->assertTrue($ret[$i++]); // expire always returns true
             $this->assertEquals(5, $ret[$i++]); // TTL was just set.
    -        $this->assertTrue($ret[$i++]); // expireAt returns TRUE for an existing key
    +        $this->assertTrue($ret[$i++]); // expireAt returns true for an existing key
             $this->assertEquals($i, count($ret));
     
             // lists
    @@ -4907,7 +4907,7 @@ private function checkSerializer($mode) {
             $this->assertEquals($mode, $this->redis->getOption(Redis::OPT_SERIALIZER));    // get ok
     
             // lPush, rPush
    -        $a = ['hello world', 42, TRUE, ['' => 1729]];
    +        $a = ['hello world', 42, true, ['' => 1729]];
             $this->redis->del('key');
             $this->redis->lPush('key', $a[0]);
             $this->redis->rPush('key', $a[1]);
    @@ -5093,8 +5093,8 @@ private function checkSerializer($mode) {
     
             // issue #62, hgetall
             $this->redis->del('hash1');
    -        $this->redis->hSet('hash1','data', 'test 1');
    -        $this->redis->hSet('hash1','session_id', 'test 2');
    +        $this->redis->hSet('hash1', 'data', 'test 1');
    +        $this->redis->hSet('hash1', 'session_id', 'test 2');
     
             $data = $this->redis->hGetAll('hash1');
             $this->assertEquals('test 1', $data['data']);
    @@ -5123,7 +5123,7 @@ private function checkSerializer($mode) {
     //    }
     
         public function testCompressionLZF() {
    -        if (!defined('Redis::COMPRESSION_LZF'))
    +        if ( ! defined('Redis::COMPRESSION_LZF'))
                 $this->markTestSkipped();
     
             /* Don't crash on improperly compressed LZF data */
    @@ -5137,7 +5137,7 @@ public function testCompressionLZF() {
         }
     
         public function testCompressionZSTD() {
    -        if (!defined('Redis::COMPRESSION_ZSTD'))
    +        if ( ! defined('Redis::COMPRESSION_ZSTD'))
                 $this->markTestSkipped();
     
             /* Issue 1936 regression.  Make sure we don't overflow on bad data */
    @@ -5153,7 +5153,7 @@ public function testCompressionZSTD() {
     
     
         public function testCompressionLZ4() {
    -        if (!defined('Redis::COMPRESSION_LZ4'))
    +        if ( ! defined('Redis::COMPRESSION_LZ4'))
                 $this->markTestSkipped();
     
             $this->checkCompression(Redis::COMPRESSION_LZ4, 0);
    @@ -5320,7 +5320,6 @@ public function testEval() {
             if (version_compare($this->version, '2.5.0') < 0)
                 $this->markTestSkipped();
     
    -
             /* The eval_ro method uses the same underlying handlers as eval so we
                only need to verify we can call it. */
             if ($this->minVersionCheck('7.0.0'))
    @@ -5372,7 +5371,7 @@ public function testEval() {
                         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('zrange', '{eval-key}-zset', 0, -1),
                             redis.call('lrange', '{eval-key}-list', 0, -1)
                         }
                     }
    @@ -5427,7 +5426,7 @@ public function testEval() {
              */
     
             $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_args   = ['{k}1', '{k}2', '{k}3', 'v1', 'v2', 'v3'];
             $args_result = $this->redis->eval($args_script, $args_args, 3);
             $this->assertEquals($args_args, $args_result);
     
    @@ -5436,7 +5435,7 @@ public function testEval() {
             $args_result = $this->redis->eval($args_script, $args_args, 3);
     
             // Make sure our first three are prefixed
    -        for ($i = 0; $i< count($args_result); $i++) {
    +        for ($i = 0; $i < count($args_result); $i++) {
                 if ($i < 3) {
                     $this->assertEquals('prefix:' . $args_args[$i], $args_result[$i]);
                 } else {
    @@ -5527,7 +5526,7 @@ public function testUnserialize() {
                 }
     
                 // Run through our array comparing values
    -            for ($i = 0; $i< count($vals); $i++) {
    +            for ($i = 0; $i < count($vals); $i++) {
                     // reset serializer
                     $this->redis->setOption(Redis::OPT_SERIALIZER, $mode);
                     $this->assertEquals($vals[$i], $this->redis->_unserialize($vals_enc[$i]));
    @@ -5705,7 +5704,7 @@ public function testConfig() {
                 $this->redis->clearLastError();
             }
     
    -        if (!$this->minVersionCheck('7.0.0'))
    +        if ( ! $this->minVersionCheck('7.0.0'))
                 return;
     
             /* Test getting multiple values */
    @@ -5720,14 +5719,14 @@ public function testConfig() {
             list($timeout, $max_intset) = [$settings['timeout'], $settings['set-max-intset-entries']];
     
             $updates = [
    -            ['timeout' => (string)($timeout + 30), 'set-max-intset-entries' => (string)($max_intset + 128)],
    -            ['timeout' => (string)($timeout), 'set-max-intset-entries' => (string)$max_intset],
    +            ['timeout' => $timeout + 30, 'set-max-intset-entries' => $max_intset + 128],
    +            ['timeout' => $timeout,      'set-max-intset-entries' => $max_intset],
             ];
     
             foreach ($updates as $update) {
                 $this->assertTrue($this->redis->config('set', $update));
                 $vals = $this->redis->config('get', array_keys($update));
    -            $this->assertEqualsCanonicalizing($vals, $update, true);
    +            $this->assertEqualsWeak($vals, $update, true);
             }
     
             /* Make sure PhpRedis catches malformed multiple get/set calls */
    @@ -5976,7 +5975,7 @@ public function testHScan() {
             $this->redis->del('hash');
             $foo_mems = 0;
     
    -        for ($i = 0; $i< 100; $i++) {
    +        for ($i = 0; $i < 100; $i++) {
                 if ($i > 3) {
                     $this->redis->hset('hash', "member:$i", "value:$i");
                 } else {
    @@ -6115,7 +6114,7 @@ public function testScanErrors() {
     
         protected function createPFKey($key, $count) {
             $mems = [];
    -        for ($i = 0; $i< $count; $i++) {
    +        for ($i = 0; $i < $count; $i++) {
                 $mems[] = uniqid('pfmem:');
             }
     
    @@ -6127,12 +6126,11 @@ public function testPFCommands() {
             if (version_compare($this->version, '2.8.9') < 0)
                 $this->markTestSkipped();
     
    -        $uniq = uniqid();
             $mems = [];
     
    -        for ($i = 0; $i< 1000; $i++) {
    +        for ($i = 0; $i < 1000; $i++) {
                 if ($i % 2 == 0) {
    -                $mems[] = "$uniq-$i";
    +                $mems[] = uniqid();
                 } else {
                     $mems[] = $i;
                 }
    @@ -6204,7 +6202,7 @@ protected function addCities($key) {
     
         /* GEOADD */
         public function testGeoAdd() {
    -        if (!$this->minVersionCheck('3.2'))
    +        if ( ! $this->minVersionCheck('3.2'))
                 $this->markTestSkipped();
     
             $this->redis->del('geokey');
    @@ -6226,7 +6224,7 @@ public function testGeoAdd() {
     
         /* GEORADIUS */
         public function genericGeoRadiusTest($cmd) {
    -        if (!$this->minVersionCheck('3.2.0'))
    +        if ( ! $this->minVersionCheck('3.2.0'))
                 $this->markTestSkipped();
     
             /* Chico */
    @@ -6326,7 +6324,7 @@ public function genericGeoRadiusTest($cmd) {
         }
     
         public function testGeoRadius() {
    -        if (!$this->minVersionCheck('3.2.0'))
    +        if ( ! $this->minVersionCheck('3.2.0'))
                 $this->markTestSkipped();
     
             $this->genericGeoRadiusTest('georadius');
    @@ -6334,7 +6332,7 @@ public function testGeoRadius() {
         }
     
         public function testGeoRadiusByMember() {
    -        if (!$this->minVersionCheck('3.2.0'))
    +        if ( ! $this->minVersionCheck('3.2.0'))
                 $this->markTestSkipped();
     
             $this->genericGeoRadiusTest('georadiusbymember');
    @@ -6342,7 +6340,7 @@ public function testGeoRadiusByMember() {
         }
     
         public function testGeoPos() {
    -        if (!$this->minVersionCheck('3.2.0'))
    +        if ( ! $this->minVersionCheck('3.2.0'))
                 $this->markTestSkipped();
     
             $this->addCities('gk');
    @@ -6351,7 +6349,7 @@ public function testGeoPos() {
         }
     
         public function testGeoHash() {
    -        if (!$this->minVersionCheck('3.2.0'))
    +        if ( ! $this->minVersionCheck('3.2.0'))
                 $this->markTestSkipped();
     
             $this->addCities('gk');
    @@ -6360,7 +6358,7 @@ public function testGeoHash() {
         }
     
         public function testGeoDist() {
    -        if (!$this->minVersionCheck('3.2.0'))
    +        if ( ! $this->minVersionCheck('3.2.0'))
                 $this->markTestSkipped();
     
             $this->addCities('gk');
    @@ -6375,7 +6373,7 @@ public function testGeoDist() {
         }
     
         public function testGeoSearch() {
    -        if (!$this->minVersionCheck('6.2.0'))
    +        if ( ! $this->minVersionCheck('6.2.0'))
                 $this->markTestSkipped();
     
             $this->addCities('gk');
    @@ -6392,7 +6390,7 @@ public function testGeoSearch() {
         }
     
         public function testGeoSearchStore() {
    -        if (!$this->minVersionCheck('6.2.0'))
    +        if ( ! $this->minVersionCheck('6.2.0'))
                 $this->markTestSkipped();
     
             $this->addCities('{gk}src');
    @@ -6441,7 +6439,7 @@ protected function addStreamsAndGroups($streams, $count, $groups) {
         }
     
         public function testXAdd() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             $this->redis->del('stream');
    @@ -6506,7 +6504,7 @@ protected function doXRangeTest($reverse) {
         }
     
         public function testXRange() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             foreach ([false, true] as $reverse) {
    @@ -6521,7 +6519,7 @@ public function testXRange() {
         }
     
         protected function testXLen() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             $this->redis->del('{stream}');
    @@ -6532,7 +6530,7 @@ protected function testXLen() {
         }
     
         public function testXGroup() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             /* CREATE MKSTREAM */
    @@ -6560,7 +6558,7 @@ public function testXGroup() {
     
             $this->assertEquals(0, $this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer'));
     
    -        if (!$this->minVersionCheck('6.2.0'))
    +        if ( ! $this->minVersionCheck('6.2.0'))
                 return;
     
             /* CREATECONSUMER */
    @@ -6586,7 +6584,7 @@ public function testXGroup() {
             $this->assertFalse(@$this->redis->xGroup('create'));
             $this->assertNull($this->redis->getLastError());
     
    -        if (!$this->minVersionCheck('7.0.0'))
    +        if ( ! $this->minVersionCheck('7.0.0'))
                 return;
     
             /* ENTRIESREAD */
    @@ -6597,7 +6595,7 @@ public function testXGroup() {
         }
     
         public function testXAck() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             for ($n = 1; $n <= 3; $n++) {
    @@ -6618,7 +6616,7 @@ public function testXAck() {
         }
     
         protected function doXReadTest() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             $row = ['f1' => 'v1', 'f2' => 'v2'];
    @@ -6668,7 +6666,7 @@ protected function doXReadTest() {
         }
     
         public function testXRead() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             foreach ($this->getSerializers() as $serializer) {
    @@ -6699,7 +6697,7 @@ protected function compareStreamIds($redis, $control) {
         }
     
         public function testXReadGroup() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             /* Create some streams and groups */
    @@ -6768,7 +6766,7 @@ public function testXReadGroup() {
         }
     
         public function testXPending() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             $rows = 5;
    @@ -6801,7 +6799,7 @@ public function testXPending() {
         }
     
         public function testXDel() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             for ($n = 5; $n > 0; $n--) {
    @@ -6815,7 +6813,7 @@ public function testXDel() {
         }
     
         public function testXTrim() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             for ($maxlen = 0; $maxlen <= 50; $maxlen += 10) {
    @@ -6830,7 +6828,7 @@ public function testXTrim() {
             $this->assertEquals(0, $this->redis->xTrim('stream', 1, true));
     
             /* We need Redis >= 6.2.0 for MINID and LIMIT options */
    -        if (!$this->minVersionCheck('6.2.0'))
    +        if ( ! $this->minVersionCheck('6.2.0'))
                 return;
     
             $this->assertEquals(1, $this->redis->del('stream'));
    @@ -6854,7 +6852,7 @@ public function testXTrim() {
         /* 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'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             foreach ([0, 100] as $min_idle_time) {
    @@ -6893,7 +6891,7 @@ public function testXClaim() {
     
                             /* Now have pavlo XCLAIM them */
                             $cids = $this->redis->xClaim('s', 'group1', 'Pavlo', $min_idle_time, $oids, $opts);
    -                        if (!$justid) $cids = array_keys($cids);
    +                        if ( ! $justid) $cids = array_keys($cids);
     
                             if ($min_idle_time == 0) {
                                 $this->assertEquals($cids, $oids);
    @@ -6902,7 +6900,7 @@ public function testXClaim() {
                                  * assigned to a PEL group */
                                 $opts[] = 'FORCE';
                                 $freturn = $this->redis->xClaim('f', 'group1', 'Test', 0, $fids, $opts);
    -                            if (!$justid) $freturn = array_keys($freturn);
    +                            if ( ! $justid) $freturn = array_keys($freturn);
                                 $this->assertEquals($freturn, $fids);
     
                                 if ($retrycount || $tvalue !== NULL) {
    @@ -6970,7 +6968,7 @@ public function testXAutoClaim() {
         }
     
         public function testXInfo() {
    -        if (!$this->minVersionCheck('5.0'))
    +        if ( ! $this->minVersionCheck('5.0'))
                 $this->markTestSkipped();
     
             /* Create some streams and groups */
    @@ -7003,7 +7001,7 @@ public function testXInfo() {
             $this->assertIsArray($info);
     
             /* XINFO STREAM FULL [COUNT N] Requires >= 6.0.0 */
    -        if (!$this->minVersionCheck('6.0'))
    +        if ( ! $this->minVersionCheck('6.0'))
                 return;
     
             /* Add some items to the stream so we can test COUNT */
    @@ -7101,7 +7099,7 @@ function($o) { $o->auth(['1337haxx00r', 'lolwut']); }, '/^WRONGPASS.*$/');
     
             /* We attempted a bad login.  We should have an ACL log entry */
             $log = $this->redis->acl('log');
    -        if (! $log || !is_array($log)) {
    +        if ( !  $log || !is_array($log)) {
                 $this->assert('Expected an array from ACL LOG, got: ' . var_export($log, true));
                 return;
             }
    @@ -7181,7 +7179,7 @@ public function testUnixSocket() {
     
         protected function detectRedis($host, $port) {
             $sock = @fsockopen($host, $port, $errno, $errstr, .1);
    -        if (! $sock)
    +        if ( !  $sock)
                 return false;
     
             stream_set_timeout($sock, 0, 100000);
    @@ -7587,7 +7585,7 @@ protected function execWaitAOF() {
         }
     
         public function testWaitAOF() {
    -        if (!$this->minVersionCheck('7.2.0'))
    +        if ( ! $this->minVersionCheck('7.2.0'))
                 $this->markTestSkipped();
     
             $res = $this->execWaitAOF();
    diff --git a/tests/TestSuite.php b/tests/TestSuite.php
    index 605d042d7c..be38c616da 100644
    --- a/tests/TestSuite.php
    +++ b/tests/TestSuite.php
    @@ -542,8 +542,8 @@ public static function run($class_name, ?string $limit = NULL,
                     continue;
                 }
     
    -            $str_out_name = str_pad($name, $max_test_len + 1);
    -            echo self::make_bold($str_out_name);
    +            $padded_name = str_pad($name, $max_test_len + 1);
    +            echo self::make_bold($padded_name);
     
                 $count = count($class_name::$errors);
                 $rt = new $class_name($host, $port, $auth);
    diff --git a/tests/getSessionData.php b/tests/getSessionData.php
    index c97da57ead..cf2ad08bd8 100644
    --- a/tests/getSessionData.php
    +++ b/tests/getSessionData.php
    @@ -24,7 +24,7 @@
     ini_set('session.gc_maxlifetime', $lifetime);
     
     session_id($id);
    -if (!session_start()) {
    +if ( ! session_start()) {
         fprintf(STDERR, "session_start() was nut successful");
         exit(1);
     } else {
    diff --git a/tests/regenerateSessionId.php b/tests/regenerateSessionId.php
    index 03a45dad63..d9dcef753c 100644
    --- a/tests/regenerateSessionId.php
    +++ b/tests/regenerateSessionId.php
    @@ -78,9 +78,9 @@ public function write($session_id, $session_data)
     
     session_id($id);
     
    -if (!session_start()) {
    +if ( !  session_start()) {
         $result = "FAILED: session_start()";
    -} else if (!session_regenerate_id($destroy_previous)) {
    +} else if ( ! session_regenerate_id($destroy_previous)) {
         $result = "FAILED: session_regenerateId()";
     } else {
         $result = session_id();
    
    From dab6a62d3463a4f003ebfbaedddbf9eaff103f90 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 30 May 2024 11:46:36 -0700
    Subject: [PATCH 1886/1986] Code formatting
    
    ---
     tests/RedisClusterTest.php | 2 +-
     tests/RedisTest.php        | 6 +++---
     tests/TestSuite.php        | 4 ++--
     3 files changed, 6 insertions(+), 6 deletions(-)
    
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index 3e081e9ecf..968ed9220b 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -391,7 +391,7 @@ public function testSlowlog() {
             $this->assertIsArray($this->redis->slowlog($key, 'get', 10));
             $this->assertIsInt($this->redis->slowlog($key, 'len'));
             $this->assertTrue($this->redis->slowlog($key, 'reset'));
    -        $this->assertFalse($this->redis->slowlog($key, 'notvalid'));
    +        $this->assertFalse(@$this->redis->slowlog($key, 'notvalid'));
         }
     
         /* INFO COMMANDSTATS requires a key or ip:port for node direction */
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 0d371d20f3..ea9785923f 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -6467,7 +6467,7 @@ public function testXAdd() {
             $this->assertEquals(count(explode('-', $id)), 2);
     
             /* Empty message should fail */
    -        $this->redis->xAdd('stream', '*', []);
    +        @$this->redis->xAdd('stream', '*', []);
         }
     
         protected function doXRangeTest($reverse) {
    @@ -6662,7 +6662,7 @@ protected function doXReadTest() {
             );
     
             /* Empty query should fail */
    -        $this->assertFalse($this->redis->xRead([]));
    +        $this->assertFalse(@$this->redis->xRead([]));
         }
     
         public function testXRead() {
    @@ -6809,7 +6809,7 @@ public function testXDel() {
             }
     
             /* Empty array should fail */
    -        $this->assertFalse($this->redis->xDel('s', []));
    +        $this->assertFalse(@$this->redis->xDel('s', []));
         }
     
         public function testXTrim() {
    diff --git a/tests/TestSuite.php b/tests/TestSuite.php
    index be38c616da..b4f9d736a3 100644
    --- a/tests/TestSuite.php
    +++ b/tests/TestSuite.php
    @@ -523,8 +523,8 @@ public static function run($class_name, ?string $limit = NULL,
                                    ?string $host = NULL, ?int $port = NULL,
                                    $auth = NULL)
         {
    -        /* Lowercase our limit arg if we're passed one */
    -        $limit ??= strtolower($limit);
    +        if ($limit)
    +            $limit = strtolower($limit);
     
             $rc = new ReflectionClass($class_name);
             $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC);
    
    From c139de3abac1dd33b97ef0de5af41b6e3a78f7ab Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sat, 1 Jun 2024 13:09:30 -0700
    Subject: [PATCH 1887/1986] We don't need to use a ranom value for our ECHO
     liveness challenge.
    
    A microsecond resolution timestamp combined with a monotonically
    incremented counter should be sufficient.
    
    This also fixes PHP 8.4 compilation as PHP 8.4 doesn't seem to have
    `php_rand()`.
    ---
     library.c | 4 +++-
     1 file changed, 3 insertions(+), 1 deletion(-)
    
    diff --git a/library.c b/library.c
    index ce0cbda263..42a132c4cd 100644
    --- a/library.c
    +++ b/library.c
    @@ -2886,11 +2886,13 @@ redis_sock_create(char *host, int host_len, int port,
     }
     
     static int redis_uniqid(char *buf, size_t buflen) {
    +    static unsigned long counter = 0;
         struct timeval tv;
    +
         gettimeofday(&tv, NULL);
     
         return snprintf(buf, buflen, "phpredis:%08lx%05lx:%08lx",
    -                    (long)tv.tv_sec, (long)tv.tv_usec, (long)php_rand());
    +                    (long)tv.tv_sec, (long)tv.tv_usec, counter++);
     }
     
     static int redis_stream_liveness_check(php_stream *stream) {
    
    From f8c762e70bd754a2494589c127a69c066336ee8f Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sat, 1 Jun 2024 13:13:07 -0700
    Subject: [PATCH 1888/1986] Use ZEND_STRL where appropriate.
    
    Use the `ZEND_STRL` macro in several places rather than manually sending
    a static string and its length as a constant.
    ---
     cluster_library.c  | 14 +++++++-------
     library.c          | 35 +++++++++++++++++++++--------------
     redis.c            |  8 +++++---
     redis_array_impl.c |  3 ++-
     4 files changed, 35 insertions(+), 25 deletions(-)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index 3bd4fc784b..322faab740 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -1910,15 +1910,15 @@ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
         // Switch on the type
         if (strncmp (c->line_reply, "string", 6) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_STRING);
    -    } else if (strncmp(c->line_reply, "set", 3) == 0) {
    +    } else if (strncmp(c->line_reply, ZEND_STRL("set")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_SET);
    -    } else if (strncmp(c->line_reply, "list", 4) == 0) {
    +    } else if (strncmp(c->line_reply, ZEND_STRL("list")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_LIST);
    -    } else if (strncmp(c->line_reply, "hash", 4) == 0) {
    +    } else if (strncmp(c->line_reply, ZEND_STRL("hash")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_HASH);
    -    } else if (strncmp(c->line_reply, "zset", 4) == 0) {
    +    } else if (strncmp(c->line_reply, ZEND_STRL("zset")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_ZSET);
    -    } else if (strncmp(c->line_reply, "stream", 6) == 0) {
    +    } else if (strncmp(c->line_reply, ZEND_STRL("stream")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_STREAM);
         } else {
             CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND);
    @@ -1977,8 +1977,8 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
             }
     
             // Make sure we have a message or pmessage
    -        if (!strncmp(Z_STRVAL_P(z_type), "message", 7) ||
    -            !strncmp(Z_STRVAL_P(z_type), "pmessage", 8)
    +        if (!strncmp(Z_STRVAL_P(z_type), ZEND_STRL("message")) ||
    +            !strncmp(Z_STRVAL_P(z_type), ZEND_STRL("pmessage"))
             ) {
                 is_pmsg = *Z_STRVAL_P(z_type) == 'p';
             } else {
    diff --git a/library.c b/library.c
    index 42a132c4cd..05f8dd468e 100644
    --- a/library.c
    +++ b/library.c
    @@ -155,7 +155,7 @@ static int reselect_db(RedisSock *redis_sock) {
             return -1;
         }
     
    -    if (strncmp(response, "+OK", 3)) {
    +    if (strncmp(response, ZEND_STRL("+OK"))) {
             efree(response);
             return -1;
         }
    @@ -254,7 +254,9 @@ PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) {
         }
         efree(cmd);
     
    -    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "+OK", 3)) {
    +    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
    +        strncmp(inbuf, ZEND_STRL("+OK")))
    +    {
             return FAILURE;
         }
         return SUCCESS;
    @@ -1208,17 +1210,17 @@ PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
             return FAILURE;
         }
     
    -    if (strncmp(response, "+string", 7) == 0) {
    +    if (strncmp(response, ZEND_STRL("+string")) == 0) {
             l = REDIS_STRING;
    -    } else if (strncmp(response, "+set", 4) == 0){
    +    } else if (strncmp(response, ZEND_STRL("+set")) == 0){
             l = REDIS_SET;
    -    } else if (strncmp(response, "+list", 5) == 0){
    +    } else if (strncmp(response, ZEND_STRL("+list")) == 0){
             l = REDIS_LIST;
    -    } else if (strncmp(response, "+zset", 5) == 0){
    +    } else if (strncmp(response, ZEND_STRL("+zset")) == 0){
             l = REDIS_ZSET;
    -    } else if (strncmp(response, "+hash", 5) == 0){
    +    } else if (strncmp(response, ZEND_STRL("+hash")) == 0){
             l = REDIS_HASH;
    -    } else if (strncmp(response, "+stream", 7) == 0) {
    +    } else if (strncmp(response, ZEND_STRL("+stream")) == 0) {
             l = REDIS_STREAM;
         } else {
             l = REDIS_NOT_FOUND;
    @@ -3019,14 +3021,19 @@ redis_sock_check_liveness(RedisSock *redis_sock)
         }
     
         if (auth) {
    -        if (strncmp(inbuf, "+OK", 3) == 0 || strncmp(inbuf, "-ERR Client sent AUTH", 21) == 0) {
    +        if (strncmp(inbuf, ZEND_STRL("+OK")) == 0 ||
    +            strncmp(inbuf, ZEND_STRL("-ERR Client sent AUTH")) == 0)
    +        {
                 /* successfully authenticated or authentication isn't required */
                 if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
                     goto failure;
                 }
    -        } else if (strncmp(inbuf, "-NOAUTH", 7) == 0) {
    -            /* connection is fine but authentication failed, next command must fails too */
    -            if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "-NOAUTH", 7) != 0) {
    +        } else if (strncmp(inbuf, ZEND_STRL("-NOAUTH")) == 0) {
    +            /* connection is fine but authentication failed, next command must
    +             * fail too */
    +            if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0
    +                || strncmp(inbuf, ZEND_STRL("-NOAUTH")) != 0)
    +            {
                     goto failure;
                 }
                 return SUCCESS;
    @@ -3035,7 +3042,7 @@ redis_sock_check_liveness(RedisSock *redis_sock)
             }
             redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED;
         } else {
    -        if (strncmp(inbuf, "-NOAUTH", 7) == 0) {
    +        if (strncmp(inbuf, ZEND_STRL("-NOAUTH")) == 0) {
                 /* connection is fine but authentication required */
                 return SUCCESS;
             }
    @@ -3043,7 +3050,7 @@ redis_sock_check_liveness(RedisSock *redis_sock)
     
         /* check echo response */
         if ((redis_sock->sentinel && (
    -        strncmp(inbuf, "-ERR unknown command", 20) != 0 ||
    +        strncmp(inbuf, ZEND_STRL("-ERR unknown command")) != 0 ||
             strstr(inbuf, id) == NULL
         )) || *inbuf != TYPE_BULK || atoi(inbuf + 1) != idlen ||
             redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
    diff --git a/redis.c b/redis.c
    index 45231b67d2..063ce0f905 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -1906,7 +1906,7 @@ PHP_METHOD(Redis, multi)
                     }
                     if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
                         RETURN_FALSE;
    -                } else if (strncmp(resp, "+OK", 3) != 0) {
    +                } else if (strncmp(resp, ZEND_STRL("+OK")) != 0) {
                         efree(resp);
                         RETURN_FALSE;
                     }
    @@ -2045,7 +2045,7 @@ redis_response_enqueued(RedisSock *redis_sock)
         int resp_len, ret = FAILURE;
     
         if ((resp = redis_sock_read(redis_sock, &resp_len)) != NULL) {
    -        if (strncmp(resp, "+QUEUED", 7) == 0) {
    +        if (strncmp(resp, ZEND_STRL("+QUEUED")) == 0) {
                 ret = SUCCESS;
             }
             efree(resp);
    @@ -2068,7 +2068,9 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
             size_t len;
             char inbuf[255];
     
    -        if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "+OK", 3) != 0) {
    +        if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
    +            strncmp(inbuf, ZEND_STRL("+OK")) != 0)
    +        {
                 return FAILURE;
             }
     
    diff --git a/redis_array_impl.c b/redis_array_impl.c
    index 8c1bc6eef2..6a64996878 100644
    --- a/redis_array_impl.c
    +++ b/redis_array_impl.c
    @@ -277,7 +277,8 @@ RedisArray *ra_load_array(const char *name) {
             array_init(&z_tmp);
             sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
             if ((z_data = zend_hash_str_find(Z_ARRVAL(z_tmp), name, name_len)) != NULL) {
    -            consistent = Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0;
    +            consistent = Z_TYPE_P(z_data) == IS_STRING &&
    +                         strncmp(Z_STRVAL_P(z_data), ZEND_STRL("1")) == 0;
             }
             zval_dtor(&z_tmp);
         }
    
    From d3b2d87b103cada2fc5b988a6b141f149069eb07 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sat, 1 Jun 2024 17:21:06 -0700
    Subject: [PATCH 1889/1986] Don't use `$k1` as a variable name.
    
    There is a very strange edge case whn you try to run PHP under valgrind
    and use certain specific strings like "$k1".
    
    PHP interns these values in such a way that valgrind can't handle it and
    hard aborts on sigsegv.  I don't know what the actual cause is but
    simply renaming the variables is a workaround.
    ---
     tests/RedisTest.php | 28 ++++++++++++++--------------
     1 file changed, 14 insertions(+), 14 deletions(-)
    
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index ea9785923f..fa3b1e3f68 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -1449,23 +1449,23 @@ public function testlMove() {
             if (version_compare($this->version, '6.2.0') < 0)
                 $this->markTestSkipped();
     
    -        [$k1, $k2] = ['{l}0', '{l}1'];
    +        [$list1, $list2] = ['{l}0', '{l}1'];
             $left  = $this->getLeftConstant();
             $right = $this->getRightConstant();
     
    -        $this->redis->del($k1, $k2);
    -        $this->redis->lPush($k1, 'a');
    -        $this->redis->lPush($k1, 'b');
    -        $this->redis->lPush($k1, 'c');
    +        $this->redis->del($list1, $list2);
    +        $this->redis->lPush($list1, 'a');
    +        $this->redis->lPush($list1, 'b');
    +        $this->redis->lPush($list1, 'c');
     
    -        $return = $this->redis->lMove($k1, $k2, $left, $right);
    +        $return = $this->redis->lMove($list1, $list2, $left, $right);
             $this->assertEquals('c', $return);
     
    -        $return = $this->redis->lMove($k1, $k2, $right, $left);
    +        $return = $this->redis->lMove($list1, $list2, $right, $left);
             $this->assertEquals('a', $return);
     
    -        $this->assertEquals(['b'], $this->redis->lRange($k1, 0, -1));
    -        $this->assertEquals(['a', 'c'], $this->redis->lRange($k2, 0, -1));
    +        $this->assertEquals(['b'], $this->redis->lRange($list1, 0, -1));
    +        $this->assertEquals(['a', 'c'], $this->redis->lRange($list2, 0, -1));
     
         }
     
    @@ -1473,17 +1473,17 @@ public function testBlmove() {
             if (version_compare($this->version, '6.2.0') < 0)
                 $this->markTestSkipped();
     
    -        [$k1, $k2] = ['{l}0', '{l}1'];
    +        [$list1, $list2] = ['{l}0', '{l}1'];
             $left = $this->getLeftConstant();
     
    -        $this->redis->del($k1, $k2);
    -        $this->redis->rpush($k1, 'a');
    +        $this->redis->del($list1, $list2);
    +        $this->redis->rpush($list1, 'a');
     
     
    -        $this->assertEquals('a', $this->redis->blmove($k1, $k2, $left, $left, 1.));
    +        $this->assertEquals('a', $this->redis->blmove($list1, $list2, $left, $left, 1.));
     
             $st = microtime(true);
    -        $ret = $this->redis->blmove($k1, $k2, $left, $left, .1);
    +        $ret = $this->redis->blmove($list1, $list2, $left, $left, .1);
             $et = microtime(true);
     
             $this->assertFalse($ret);
    
    From 7050c9890977f6197290f5d7ccbb9b64ce55fa4d Mon Sep 17 00:00:00 2001
    From: Michael Grunder 
    Date: Sat, 15 Jun 2024 14:48:30 -0700
    Subject: [PATCH 1890/1986] Play around with more ZEND_STRL usage (#2505)
    
    * Play around with more ZEND_STRL usage
    
    * strncasecmp is a macro on Windows
    ---
     library.c          |  2 +-
     redis.c            | 20 +++++++-------
     redis_array_impl.c | 69 +++++++++++++++++++++++++---------------------
     3 files changed, 48 insertions(+), 43 deletions(-)
    
    diff --git a/library.c b/library.c
    index 05f8dd468e..dc86557a84 100644
    --- a/library.c
    +++ b/library.c
    @@ -3125,7 +3125,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
     
                 int limit = INI_INT("redis.pconnect.connection_limit");
                 if (limit > 0 && p->nb_active >= limit) {
    -                redis_sock_set_err(redis_sock, "Connection limit reached", sizeof("Connection limit reached") - 1);
    +                redis_sock_set_err(redis_sock, ZEND_STRL("Connection limit reached"));
                     return FAILURE;
                 }
     
    diff --git a/redis.c b/redis.c
    index 063ce0f905..b52e126b77 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -567,8 +567,8 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
     
         /* Does the host look like a unix socket */
         af_unix = (host_len > 0 && host[0] == '/') ||
    -              (host_len > 6 && !strncasecmp(host, "unix://", sizeof("unix://") - 1)) ||
    -              (host_len > 6 && !strncasecmp(host, "file://", sizeof("file://") - 1));
    +              (host_len > 6 && (!strncasecmp(host, "unix://", sizeof("unix://") - 1) ||
    +                                !strncasecmp(host, "file://", sizeof("file://") - 1)));
     
         /* If it's not a unix socket, set to default */
         if (port == -1 && !af_unix) {
    @@ -1258,18 +1258,18 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha)
         }
     
         /* Start constructing final command and append key */
    -    redis_cmd_init_sstr(&cmd, argc, "SORT", 4);
    +    redis_cmd_init_sstr(&cmd, argc, ZEND_STRL("SORT"));
         redis_cmd_append_sstr_key(&cmd, key, keylen, redis_sock, NULL);
     
         /* BY pattern */
         if (pattern && patternlen) {
    -        redis_cmd_append_sstr(&cmd, "BY", sizeof("BY") - 1);
    +        redis_cmd_append_sstr(&cmd, ZEND_STRL("BY"));
             redis_cmd_append_sstr(&cmd, pattern, patternlen);
         }
     
         /* LIMIT offset count */
         if (offset >= 0 && count >= 0) {
    -        redis_cmd_append_sstr(&cmd, "LIMIT", sizeof("LIMIT") - 1);
    +        redis_cmd_append_sstr(&cmd, ZEND_STRL("LIMIT"));
             redis_cmd_append_sstr_long(&cmd, offset);
             redis_cmd_append_sstr_long(&cmd, count);
         }
    @@ -1279,25 +1279,25 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha)
             if (Z_TYPE_P(zget) == IS_ARRAY) {
                 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zget), zele) {
                     zpattern = zval_get_string(zele);
    -                redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1);
    +                redis_cmd_append_sstr(&cmd, ZEND_STRL("GET"));
                     redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern));
                     zend_string_release(zpattern);
                 } ZEND_HASH_FOREACH_END();
             } else {
                 zpattern = zval_get_string(zget);
    -            redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1);
    +            redis_cmd_append_sstr(&cmd, ZEND_STRL("GET"));
                 redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern));
                 zend_string_release(zpattern);
             }
         }
     
         /* Append optional DESC and ALPHA modifiers */
    -    if (desc)  redis_cmd_append_sstr(&cmd, "DESC", sizeof("DESC") - 1);
    -    if (alpha) redis_cmd_append_sstr(&cmd, "ALPHA", sizeof("ALPHA") - 1);
    +    if (desc)  redis_cmd_append_sstr(&cmd, ZEND_STRL("DESC"));
    +    if (alpha) redis_cmd_append_sstr(&cmd, ZEND_STRL("ALPHA"));
     
         /* Finally append STORE if we've got it */
         if (store && storelen) {
    -        redis_cmd_append_sstr(&cmd, "STORE", sizeof("STORE") - 1);
    +        redis_cmd_append_sstr(&cmd, ZEND_STRL("STORE"));
             redis_cmd_append_sstr_key(&cmd, store, storelen, redis_sock, NULL);
         }
     
    diff --git a/redis_array_impl.c b/redis_array_impl.c
    index 6a64996878..c7e335e88c 100644
    --- a/redis_array_impl.c
    +++ b/redis_array_impl.c
    @@ -90,38 +90,43 @@ ra_init_function_table(RedisArray *ra)
         ALLOC_HASHTABLE(ra->pure_cmds);
         zend_hash_init(ra->pure_cmds, 0, NULL, NULL, 0);
     
    -    zend_hash_str_update_ptr(ra->pure_cmds, "EXISTS", sizeof("EXISTS") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "GET", sizeof("GET") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "GETBIT", sizeof("GETBIT") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "GETRANGE", sizeof("GETRANGE") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "HEXISTS", sizeof("HEXISTS") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "HGET", sizeof("HGET") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "HGETALL", sizeof("HGETALL") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "HKEYS", sizeof("HKEYS") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "HLEN", sizeof("HLEN") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "HMGET", sizeof("HMGET") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "HVALS", sizeof("HVALS") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "LINDEX", sizeof("LINDEX") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "LLEN", sizeof("LLEN") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "LRANGE", sizeof("LRANGE") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "OBJECT", sizeof("OBJECT") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "SCARD", sizeof("SCARD") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "SDIFF", sizeof("SDIFF") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "SINTER", sizeof("SINTER") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "SISMEMBER", sizeof("SISMEMBER") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "SMEMBERS", sizeof("SMEMBERS") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "SRANDMEMBER", sizeof("SRANDMEMBER") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "STRLEN", sizeof("STRLEN") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "SUNION", sizeof("SUNION") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "TYPE", sizeof("TYPE") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "ZCARD", sizeof("ZCARD") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "ZCOUNT", sizeof("ZCOUNT") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "ZRANGE", sizeof("ZRANGE") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "ZRANK", sizeof("ZRANK") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANGE", sizeof("ZREVRANGE") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANGEBYSCORE", sizeof("ZREVRANGEBYSCORE") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANK", sizeof("ZREVRANK") - 1, NULL);
    -    zend_hash_str_update_ptr(ra->pure_cmds, "ZSCORE", sizeof("ZSCORE") - 1, NULL);
    +    #define ra_add_pure_cmd(cmd) \
    +        zend_hash_str_update_ptr(ra->pure_cmds, cmd, sizeof(cmd) - 1, NULL);
    +
    +    ra_add_pure_cmd("EXISTS");
    +    ra_add_pure_cmd("GET");
    +    ra_add_pure_cmd("GETBIT");
    +    ra_add_pure_cmd("GETRANGE");
    +    ra_add_pure_cmd("HEXISTS");
    +    ra_add_pure_cmd("HGET");
    +    ra_add_pure_cmd("HGETALL");
    +    ra_add_pure_cmd("HKEYS");
    +    ra_add_pure_cmd("HLEN");
    +    ra_add_pure_cmd("HMGET");
    +    ra_add_pure_cmd("HVALS");
    +    ra_add_pure_cmd("LINDEX");
    +    ra_add_pure_cmd("LLEN");
    +    ra_add_pure_cmd("LRANGE");
    +    ra_add_pure_cmd("OBJECT");
    +    ra_add_pure_cmd("SCARD");
    +    ra_add_pure_cmd("SDIFF");
    +    ra_add_pure_cmd("SINTER");
    +    ra_add_pure_cmd("SISMEMBER");
    +    ra_add_pure_cmd("SMEMBERS");
    +    ra_add_pure_cmd("SRANDMEMBER");
    +    ra_add_pure_cmd("STRLEN");
    +    ra_add_pure_cmd("SUNION");
    +    ra_add_pure_cmd("TYPE");
    +    ra_add_pure_cmd("ZCARD");
    +    ra_add_pure_cmd("ZCOUNT");
    +    ra_add_pure_cmd("ZRANGE");
    +    ra_add_pure_cmd("ZRANK");
    +    ra_add_pure_cmd("ZREVRANGE");
    +    ra_add_pure_cmd("ZREVRANGEBYSCORE");
    +    ra_add_pure_cmd("ZREVRANK");
    +    ra_add_pure_cmd("ZSCORE");
    +
    +    #undef ra_add_pure_cmd
     }
     
     static int
    
    From b808cc60ed09bd5f0efc22508c43db90a3e1219e Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 17 Jun 2024 10:42:47 -0700
    Subject: [PATCH 1891/1986] Update tests so they can run in php-cgi.
    
    This probably isn't a very common scenerio since we've never had someone
    ask it in a decade, but it was very simple to get them working.
    
    Primarily we just needed to test for `STDTOUT`/`STDERR` and use
    `__DIR__` instead of `$_SERVER['PHP_SELF']`.
    
    Fixes #2507
    ---
     tests/RedisArrayTest.php    |  3 ++-
     tests/RedisClusterTest.php  | 13 +++++------
     tests/RedisSentinelTest.php |  2 +-
     tests/RedisTest.php         | 43 +++++++++++++++++++++++++++++++++++--
     tests/TestRedis.php         | 10 ++++-----
     tests/TestSuite.php         | 12 ++++++++++-
     6 files changed, 67 insertions(+), 16 deletions(-)
    
    diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php
    index 0586f09289..82e11ddab2 100644
    --- a/tests/RedisArrayTest.php
    +++ b/tests/RedisArrayTest.php
    @@ -1,5 +1,6 @@
     loadSeedsFromHostPort($host, $port)))
                 return $seeds;
     
    -        fprintf(STDERR, "Error:  Unable to load seeds for RedisCluster tests\n");
    +        TestSuite::errorMessage("Error:  Unable to load seeds for RedisCluster tests");
             foreach (self::$seed_messages as $msg) {
    -            fprintf(STDERR, "   Tried: %s\n", $msg);
    +            TestSuite::errorMessage("   Tried: %s", $msg);
             }
     
             exit(1);
    @@ -139,9 +140,9 @@ protected function newInstance() {
             try {
                 return new RedisCluster(NULL, self::$seeds, 30, 30, true, $this->getAuth());
             } catch (Exception $ex) {
    -            fprintf(STDERR, "Fatal error: %s\n", $ex->getMessage());
    -            fprintf(STDERR, "Seeds: %s\n", implode(' ', self::$seeds));
    -            fprintf(STDERR, "Seed source: %s\n", self::$seed_source);
    +            TestSuite::errorMessage("Fatal error: %s\n", $ex->getMessage());
    +            TestSuite::errorMessage("Seeds: %s\n", implode(' ', self::$seeds));
    +            TestSuite::errorMessage("Seed source: %s\n", self::$seed_source);
                 exit(1);
             }
         }
    diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php
    index 0fdc3a957e..cfb7d6b044 100644
    --- a/tests/RedisSentinelTest.php
    +++ b/tests/RedisSentinelTest.php
    @@ -1,6 +1,6 @@
     savePath($this->sessionSavePath());
         }
     
    +    protected function testRequiresMode(string $mode) {
    +        if (php_sapi_name() != $mode) {
    +            $this->markTestSkipped("Test requires PHP running in '$mode' mode");
    +        }
    +    }
    +
         public function testSession_compression() {
    +        $this->testRequiresMode('cli');
    +
             foreach ($this->getCompressors() as $name => $val) {
                 $data = "testing_compression_$name";
     
    @@ -7244,6 +7252,8 @@ public function testSession_compression() {
         }
     
         public function testSession_savedToRedis() {
    +        $this->testRequiresMode('cli');
    +
             $runner = $this->sessionRunner();
     
             $this->assertEquals('SUCCESS', $runner->execFg());
    @@ -7260,6 +7270,8 @@ protected function sessionWaitSec() {
         }
     
         public function testSession_lockKeyCorrect() {
    +        $this->testRequiresMode('cli');
    +
             $runner = $this->sessionRunner()->sleep(5);
     
             $this->assertTrue($runner->execBg());
    @@ -7272,6 +7284,8 @@ public function testSession_lockKeyCorrect() {
         }
     
         public function testSession_lockingDisabledByDefault() {
    +        $this->testRequiresMode('cli');
    +
             $runner = $this->sessionRunner()
                 ->lockingEnabled(false)
                 ->sleep(5);
    @@ -7281,6 +7295,8 @@ public function testSession_lockingDisabledByDefault() {
         }
     
         public function testSession_lockReleasedOnClose() {
    +        $this->testRequiresMode('cli');
    +
             $runner = $this->sessionRunner()
                 ->sleep(1)
                 ->lockingEnabled(true);
    @@ -7291,6 +7307,8 @@ public function testSession_lockReleasedOnClose() {
         }
     
         public function testSession_lock_ttlMaxExecutionTime() {
    +        $this->testRequiresMode('cli');
    +
             $runner1 = $this->sessionRunner()
                 ->sleep(10)
                 ->maxExecutionTime(2);
    @@ -7309,6 +7327,7 @@ public function testSession_lock_ttlMaxExecutionTime() {
         }
     
         public function testSession_lock_ttlLockExpire() {
    +        $this->testRequiresMode('cli');
     
             $runner1 = $this->sessionRunner()
                 ->sleep(10)
    @@ -7329,6 +7348,8 @@ public function testSession_lock_ttlLockExpire() {
         }
     
         public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() {
    +        $this->testRequiresMode('cli');
    +
             $id = 'test-id';
     
             $runner = $this->sessionRunner()
    @@ -7352,6 +7373,8 @@ public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() {
         }
     
         public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() {
    +        $this->testRequiresMode('cli');
    +
             $runner = $this->sessionRunner()
                 ->sleep(2)
                 ->lockingEnabled(true)
    @@ -7363,6 +7386,8 @@ public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() {
         }
     
         public function testSession_correctLockRetryCount() {
    +        $this->testRequiresMode('cli');
    +
             $runner = $this->sessionRunner()
                 ->sleep(10);
     
    @@ -7394,6 +7419,8 @@ public function testSession_correctLockRetryCount() {
         }
     
         public function testSession_defaultLockRetryCount() {
    +        $this->testRequiresMode('cli');
    +
             $runner = $this->sessionRunner()
                 ->sleep(10);
     
    @@ -7420,6 +7447,8 @@ public function testSession_defaultLockRetryCount() {
         }
     
         public function testSession_noUnlockOfOtherProcess() {
    +        $this->testRequiresMode('cli');
    +
             $st = microtime(true);
     
             $sleep = 3;
    @@ -7453,6 +7482,8 @@ public function testSession_noUnlockOfOtherProcess() {
         }
     
         public function testSession_lockWaitTime() {
    +        $this->testRequiresMode('cli');
    +
     
             $runner = $this->sessionRunner()
                 ->sleep(1)
    @@ -7603,6 +7634,8 @@ public function testBadOptionValue() {
         }
     
         protected function regenerateIdHelper(bool $lock, bool $destroy, bool $proxy) {
    +        $this->testRequiresMode('cli');
    +
             $data   = uniqid('regenerate-id:');
             $runner = $this->sessionRunner()
                 ->sleep(0)
    @@ -7652,12 +7685,16 @@ public  function testSession_regenerateSessionId_withLock_withDestroy_withProxy(
         }
     
         public function testSession_ttl_equalsToSessionLifetime() {
    +        $this->testRequiresMode('cli');
    +
             $runner = $this->sessionRunner()->lifetime(600);
             $this->assertEquals('SUCCESS', $runner->execFg());
             $this->assertEquals(600, $this->redis->ttl($runner->getSessionKey()));
         }
     
         public function testSession_ttl_resetOnWrite() {
    +        $this->testRequiresMode('cli');
    +
             $runner1 = $this->sessionRunner()->lifetime(600);
             $this->assertEquals('SUCCESS', $runner1->execFg());
     
    @@ -7668,6 +7705,8 @@ public function testSession_ttl_resetOnWrite() {
         }
     
         public function testSession_ttl_resetOnRead() {
    +        $this->testRequiresMode('cli');
    +
             $data = uniqid(__FUNCTION__);
     
             $runner = $this->sessionRunner()->lifetime(600)->data($data);
    diff --git a/tests/TestRedis.php b/tests/TestRedis.php
    index 3efca43ffd..7ddb231574 100644
    --- a/tests/TestRedis.php
    +++ b/tests/TestRedis.php
    @@ -1,10 +1,10 @@
     host; }
         public function getPort() { return $this->port; }
         public function getAuth() { return $this->auth; }
     
    +    public static function errorMessage(string $fmt, ...$args) {
    +        $msg = vsprintf($fmt . "\n", $args);
    +
    +        if (defined('STDERR')) {
    +            fwrite(STDERR, $msg);
    +        } else {
    +            echo $msg;
    +        }
    +    }
    +
         public static function make_bold(string $msg) {
             return self::$colorize ? self::$BOLD_ON . $msg . self::$BOLD_OFF : $msg;
         }
    @@ -516,7 +526,7 @@ public static function loadTestClass($class) {
         /* Flag colorization */
         public static function flagColorization(bool $override) {
             self::$colorize = $override && function_exists('posix_isatty') &&
    -            posix_isatty(STDOUT);
    +                          defined('STDOUT') && posix_isatty(STDOUT);
         }
     
         public static function run($class_name, ?string $limit = NULL,
    
    From b1771defdcb46392046317f6d62ba9988a0c6361 Mon Sep 17 00:00:00 2001
    From: Michael Grunder 
    Date: Tue, 18 Jun 2024 14:53:22 -0700
    Subject: [PATCH 1892/1986] More unit  test utility functions/usage. (#2509)
    
    * More unit  test utility functions/usage.
    
    * Add `assertKeyEquals` and `assertKeyEqualsWeak` as we test key values
      hundreds of places in `RedisTest.php`
    
    * We are almost always using `$this->redis` when we want to run an
      assertion that needs access to the client, so make this argument
      optional and default to `$this->redis`.
    
    * Update a few more assertions to use our new methods.
    
    * Various minor fixes/tweaks.
    
    * Update RedisTest.php
    
    typo
    ---
     tests/RedisTest.php | 293 +++++++++++++++++++++-----------------------
     tests/TestSuite.php |  30 ++++-
     2 files changed, 165 insertions(+), 158 deletions(-)
    
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index d40df5e09e..cc178935f5 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -148,7 +148,7 @@ public function reset() {
             $this->tearDown();
         }
     
    -  /* Helper function to determine if the clsas has pipeline support */
    +    /* Helper function to determine if the class has pipeline support */
         protected function havePipeline() {
             return defined(get_class($this->redis) . '::PIPELINE');
         }
    @@ -203,8 +203,8 @@ public function testPubSub() {
     
             // PUBSUB NUMSUB
     
    -        $c1 = uniqid() . '-' . rand(1, 100);
    -        $c2 = uniqid() . '-' . rand(1, 100);
    +        $c1 = uniqid();
    +        $c2 = uniqid();
     
             $result = $this->redis->pubsub('numsub', [$c1, $c2]);
     
    @@ -271,7 +271,6 @@ public function testBitop() {
         }
     
         public function testBitsets() {
    -
             $this->redis->del('key');
             $this->assertEquals(0, $this->redis->getBit('key', 0));
             $this->assertFalse($this->redis->getBit('key', -1));
    @@ -287,23 +286,23 @@ public function testBitsets() {
             $this->assertEquals(1, $this->redis->setBit('key', 0, 0));
             $this->assertEquals(0, $this->redis->setBit('key', 0, 0));
             $this->assertEquals(0, $this->redis->getBit('key', 0));
    -        $this->assertEquals("\x7f", $this->redis->get('key'));
    +        $this->assertKeyEquals("\x7f", 'key');
     
             // change bit 1
             $this->assertEquals(1, $this->redis->setBit('key', 1, 0));
             $this->assertEquals(0, $this->redis->setBit('key', 1, 0));
             $this->assertEquals(0, $this->redis->getBit('key', 1));
    -        $this->assertEquals("\x3f", $this->redis->get('key'));
    +        $this->assertKeyEquals("\x3f", 'key');
     
             // change bit > 1
             $this->assertEquals(1, $this->redis->setBit('key', 2, 0));
             $this->assertEquals(0, $this->redis->setBit('key', 2, 0));
             $this->assertEquals(0, $this->redis->getBit('key', 2));
    -        $this->assertEquals("\x1f", $this->redis->get('key'));
    +        $this->assertKeyEquals("\x1f", 'key');
     
             // values above 1 are changed to 1 but don't overflow on bits to the right.
             $this->assertEquals(0, $this->redis->setBit('key', 0, 0xff));
    -        $this->assertEquals("\x9f", $this->redis->get('key'));
    +        $this->assertKeyEquals("\x9f", 'key');
     
             // Verify valid offset ranges
             $this->assertFalse($this->redis->getBit('key', -1));
    @@ -465,7 +464,7 @@ public function testSetLargeKeys() {
             foreach ([1000, 100000, 1000000] as $size) {
                 $value = str_repeat('A', $size);
                 $this->assertTrue($this->redis->set('x', $value));
    -            $this->assertEquals($value, $this->redis->get('x'));
    +            $this->assertKeyEquals($value, 'x');
             }
         }
     
    @@ -477,38 +476,38 @@ public function testEcho() {
     
         public function testErr() {
             $this->redis->set('x', '-ERR');
    -        $this->assertEquals('-ERR', $this->redis->get('x'));
    +        $this->assertKeyEquals('-ERR', 'x');
         }
     
         public function testSet() {
             $this->assertTrue($this->redis->set('key', 'nil'));
    -        $this->assertEquals('nil', $this->redis->get('key'));
    +        $this->assertKeyEquals('nil', 'key');
     
             $this->assertTrue($this->redis->set('key', 'val'));
     
    -        $this->assertEquals('val', $this->redis->get('key'));
    -        $this->assertEquals('val', $this->redis->get('key'));
    +        $this->assertKeyEquals('val', 'key');
    +        $this->assertKeyEquals('val', 'key');
             $this->redis->del('keyNotExist');
    -        $this->assertFalse($this->redis->get('keyNotExist'));
    +        $this->assertKeyMissing('keyNotExist');
     
             $this->redis->set('key2', 'val');
    -        $this->assertEquals('val', $this->redis->get('key2'));
    +        $this->assertKeyEquals('val', 'key2');
     
             $value1 = bin2hex(random_bytes(rand(64, 128)));
             $value2 = random_bytes(rand(65536, 65536 * 2));;
     
             $this->redis->set('key2', $value1);
    -        $this->assertEquals($value1, $this->redis->get('key2'));
    -        $this->assertEquals($value1, $this->redis->get('key2'));
    +        $this->assertKeyEquals($value1, 'key2');
    +        $this->assertKeyEquals($value1, 'key2');
     
             $this->redis->del('key');
             $this->redis->del('key2');
     
     
             $this->redis->set('key', $value2);
    -        $this->assertEquals($value2, $this->redis->get('key'));
    +        $this->assertKeyEquals($value2, 'key');
             $this->redis->del('key');
    -        $this->assertFalse($this->redis->get('key'));
    +        $this->assertKeyMissing('key');
     
             $data = gzcompress('42');
             $this->assertTrue($this->redis->set('key', $data));
    @@ -521,20 +520,20 @@ public function testSet() {
     
             $this->redis->del('key');
             $this->assertTrue($this->redis->set('key', 0));
    -        $this->assertEquals('0', $this->redis->get('key'));
    +        $this->assertKeyEquals('0', 'key');
             $this->assertTrue($this->redis->set('key', 1));
    -        $this->assertEquals('1', $this->redis->get('key'));
    +        $this->assertKeyEquals('1', 'key');
             $this->assertTrue($this->redis->set('key', 0.1));
    -        $this->assertEquals('0.1', $this->redis->get('key'));
    +        $this->assertKeyEquals('0.1', 'key');
             $this->assertTrue($this->redis->set('key', '0.1'));
    -        $this->assertEquals('0.1', $this->redis->get('key'));
    +        $this->assertKeyEquals('0.1', 'key');
             $this->assertTrue($this->redis->set('key', true));
    -        $this->assertEquals('1', $this->redis->get('key'));
    +        $this->assertKeyEquals('1', 'key');
     
             $this->assertTrue($this->redis->set('key', ''));
    -        $this->assertEquals('', $this->redis->get('key'));
    +        $this->assertKeyEquals('', 'key');
             $this->assertTrue($this->redis->set('key', NULL));
    -        $this->assertEquals('', $this->redis->get('key'));
    +        $this->assertKeyEquals('', 'key');
     
             $this->assertTrue($this->redis->set('key', gzcompress('42')));
             $this->assertEquals('42', gzuncompress($this->redis->get('key')));
    @@ -549,13 +548,13 @@ public function testExtendedSet() {
             /* Legacy SETEX redirection */
             $this->redis->del('foo');
             $this->assertTrue($this->redis->set('foo', 'bar', 20));
    -        $this->assertEquals('bar', $this->redis->get('foo'));
    +        $this->assertKeyEquals('bar', 'foo');
             $this->assertEquals(20, $this->redis->ttl('foo'));
     
             /* Should coerce doubles into long */
             $this->assertTrue($this->redis->set('foo', 'bar-20.5', 20.5));
             $this->assertEquals(20, $this->redis->ttl('foo'));
    -        $this->assertEquals('bar-20.5', $this->redis->get('foo'));
    +        $this->assertKeyEquals('bar-20.5', 'foo');
     
             /* Invalid third arguments */
             $this->assertFalse(@$this->redis->set('foo', 'bar', 'baz'));
    @@ -564,12 +563,12 @@ public function testExtendedSet() {
             /* Set if not exist */
             $this->redis->del('foo');
             $this->assertTrue($this->redis->set('foo', 'bar', ['nx']));
    -        $this->assertEquals('bar', $this->redis->get('foo'));
    +        $this->assertKeyEquals('bar', 'foo');
             $this->assertFalse($this->redis->set('foo', 'bar', ['nx']));
     
             /* Set if exists */
             $this->assertTrue($this->redis->set('foo', 'bar', ['xx']));
    -        $this->assertEquals('bar', $this->redis->get('foo'));
    +        $this->assertKeyEquals('bar', 'foo');
             $this->redis->del('foo');
             $this->assertFalse($this->redis->set('foo', 'bar', ['xx']));
     
    @@ -584,25 +583,25 @@ public function testExtendedSet() {
             /* Set if exists, with a TTL */
             $this->assertTrue($this->redis->set('foo', 'bar', ['xx', 'ex' => 105]));
             $this->assertEquals(105, $this->redis->ttl('foo'));
    -        $this->assertEquals('bar', $this->redis->get('foo'));
    +        $this->assertKeyEquals('bar', 'foo');
     
             /* Set if not exists, with a TTL */
             $this->redis->del('foo');
             $this->assertTrue($this->redis->set('foo', 'bar', ['nx', 'ex' => 110]));
             $this->assertEquals(110, $this->redis->ttl('foo'));
    -        $this->assertEquals('bar', $this->redis->get('foo'));
    +        $this->assertKeyEquals('bar', 'foo');
             $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(200, $this->redis->ttl('foo'));
    -        $this->assertEquals('barbaz', $this->redis->get('foo'));
    +        $this->assertKeyEquals('barbaz', 'foo');
     
             /* Pass NULL as the optional arguments which should be ignored */
             $this->redis->del('foo');
             $this->redis->set('foo', 'bar', NULL);
    -        $this->assertEquals('bar', $this->redis->get('foo'));
    +        $this->assertKeyEquals('bar', 'foo');
             $this->assertLT(0, $this->redis->ttl('foo'));
     
             /* Make sure we ignore bad/non-string options (regression test for #1835) */
    @@ -640,7 +639,7 @@ public function testGetSet() {
         public function testRandomKey() {
             for ($i = 0; $i < 1000; $i++) {
                 $k = $this->redis->randomKey();
    -            $this->assertKeyExists($this->redis, $k);
    +            $this->assertKeyExists($k);
             }
         }
     
    @@ -649,8 +648,8 @@ public function testRename() {
             $this->redis->del('{key}0');
             $this->redis->set('{key}0', 'val0');
             $this->redis->rename('{key}0', '{key}1');
    -        $this->assertFalse($this->redis->get('{key}0'));
    -        $this->assertEquals('val0', $this->redis->get('{key}1'));
    +        $this->assertKeyMissing('{key}0');
    +        $this->assertKeyEquals('val0', '{key}1');
         }
     
         public function testRenameNx() {
    @@ -659,8 +658,8 @@ public function testRenameNx() {
             $this->redis->set('{key}0', 'val0');
             $this->redis->set('{key}1', 'val1');
             $this->assertFalse($this->redis->renameNx('{key}0', '{key}1'));
    -        $this->assertEquals('val0', $this->redis->get('{key}0'));
    -        $this->assertEquals('val1', $this->redis->get('{key}1'));
    +        $this->assertKeyEquals('val0', '{key}0');
    +        $this->assertKeyEquals('val1', '{key}1');
     
             // lists
             $this->redis->del('{key}0');
    @@ -720,11 +719,12 @@ public function testMultipleBin() {
         public function testSetTimeout() {
             $this->redis->del('key');
             $this->redis->set('key', 'value');
    -        $this->assertEquals('value', $this->redis->get('key'));
    +
    +        $this->assertKeyEquals('value', 'key');
             $this->redis->expire('key', 1);
    -        $this->assertEquals('value', $this->redis->get('key'));
    +        $this->assertKeyEquals('value', 'key');
             sleep(2);
    -        $this->assertFalse($this->redis->get('key'));
    +        $this->assertKeyMissing('key');
         }
     
         /* This test is prone to failure in the Travis container, so attempt to
    @@ -745,7 +745,7 @@ public function testExpireAt() {
     
         function testExpireOptions() {
             if ( ! $this->minVersionCheck('7.0.0'))
    -            return;
    +            $this->markTestSkipped();
     
             $this->redis->set('eopts', 'value');
     
    @@ -795,25 +795,25 @@ public function testSetEx() {
             $this->redis->del('key');
             $this->assertTrue($this->redis->setex('key', 7, 'val'));
             $this->assertEquals(7, $this->redis->ttl('key'));
    -        $this->assertEquals('val', $this->redis->get('key'));
    +        $this->assertKeyEquals('val', 'key');
         }
     
         public function testPSetEx() {
             $this->redis->del('key');
             $this->assertTrue($this->redis->psetex('key', 7 * 1000, 'val'));
             $this->assertEquals(7, $this->redis->ttl('key'));
    -        $this->assertEquals('val', $this->redis->get('key'));
    +        $this->assertKeyEquals('val', 'key');
         }
     
         public function testSetNX() {
     
             $this->redis->set('key', 42);
             $this->assertFalse($this->redis->setnx('key', 'err'));
    -        $this->assertEquals('42', $this->redis->get('key'));
    +        $this->assertKeyEquals('42', 'key');
     
             $this->redis->del('key');
             $this->assertTrue($this->redis->setnx('key', '42'));
    -        $this->assertEquals('42', $this->redis->get('key'));
    +        $this->assertKeyEquals('42', 'key');
         }
     
         public function testExpireAtWithLong() {
    @@ -830,32 +830,32 @@ public function testIncr() {
             $this->redis->set('key', 0);
     
             $this->redis->incr('key');
    -        $this->assertEquals(1, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(1, 'key');
     
             $this->redis->incr('key');
    -        $this->assertEquals(2, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(2, 'key');
     
             $this->redis->incrBy('key', 3);
    -        $this->assertEquals(5, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(5, 'key');
     
             $this->redis->incrBy('key', 1);
    -        $this->assertEquals(6, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(6, 'key');
     
             $this->redis->incrBy('key', -1);
    -        $this->assertEquals(5, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(5, 'key');
     
             $this->redis->incr('key', 5);
    -        $this->assertEquals(10, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(10, 'key');
     
             $this->redis->del('key');
     
             $this->redis->set('key', 'abc');
     
             $this->redis->incr('key');
    -        $this->assertEquals('abc', $this->redis->get('key'));
    +        $this->assertKeyEquals('abc', 'key');
     
             $this->redis->incr('key');
    -        $this->assertEquals('abc', $this->redis->get('key'));
    +        $this->assertKeyEquals('abc', 'key');
     
             $this->redis->set('key', 0);
             $this->assertEquals(PHP_INT_MAX, $this->redis->incrby('key', PHP_INT_MAX));
    @@ -871,29 +871,29 @@ public function testIncrByFloat() {
             $this->redis->set('key', 0);
     
             $this->redis->incrbyfloat('key', 1.5);
    -        $this->assertEquals('1.5', $this->redis->get('key'));
    +        $this->assertKeyEquals('1.5', 'key');
     
             $this->redis->incrbyfloat('key', 2.25);
    -        $this->assertEquals('3.75', $this->redis->get('key'));
    +        $this->assertKeyEquals('3.75', 'key');
     
             $this->redis->incrbyfloat('key', -2.25);
    -        $this->assertEquals('1.5', $this->redis->get('key'));
    +        $this->assertKeyEquals('1.5', 'key');
     
             $this->redis->set('key', 'abc');
     
             $this->redis->incrbyfloat('key', 1.5);
    -        $this->assertEquals('abc', $this->redis->get('key'));
    +        $this->assertKeyEquals('abc', 'key');
     
             $this->redis->incrbyfloat('key', -1.5);
    -        $this->assertEquals('abc', $this->redis->get('key'));
    +        $this->assertKeyEquals('abc', 'key');
     
             // Test with prefixing
             $this->redis->setOption(Redis::OPT_PREFIX, 'someprefix:');
             $this->redis->del('key');
             $this->redis->incrbyfloat('key',1.8);
    -        $this->assertEqualsWeak(1.8, $this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(1.8, 'key');
             $this->redis->setOption(Redis::OPT_PREFIX, '');
    -        $this->assertKeyExists($this->redis, 'someprefix:key');
    +        $this->assertKeyExists('someprefix:key');
             $this->redis->del('someprefix:key');
         }
     
    @@ -901,31 +901,31 @@ public function testDecr() {
             $this->redis->set('key', 5);
     
             $this->redis->decr('key');
    -        $this->assertEquals(4, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(4, 'key');
     
             $this->redis->decr('key');
    -        $this->assertEquals(3, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(3, 'key');
     
             $this->redis->decrBy('key', 2);
    -        $this->assertEquals(1, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(1, 'key');
     
             $this->redis->decrBy('key', 1);
    -        $this->assertEquals(0, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(0, 'key');
     
             $this->redis->decrBy('key', -10);
    -        $this->assertEquals(10, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(10, 'key');
     
             $this->redis->decr('key', 10);
    -        $this->assertEquals(0, (int)$this->redis->get('key'));
    +        $this->assertKeyEqualsWeak(0, 'key');
         }
     
     
         public function testExists() {
             /* Single key */
             $this->redis->del('key');
    -        $this->assertEquals(0, $this->redis->exists('key'));
    +        $this->assertKeyMissing('key');
             $this->redis->set('key', 'val');
    -        $this->assertEquals(1, $this->redis->exists('key'));
    +        $this->assertKeyExists('key');
     
             /* Add multiple keys */
             $mkeys = [];
    @@ -978,13 +978,13 @@ public function testKeys() {
             $this->assertEquals((count($keys) + 1), count($keys2));
     
             // empty array when no key matches
    -        $this->assertEquals([], $this->redis->keys(rand().rand().rand().'*'));
    +        $this->assertEquals([], $this->redis->keys(uniqid() . '*'));
         }
     
         protected function genericDelUnlink($cmd) {
    -        $key = 'key' . rand();
    +        $key = uniqid('key:');
             $this->redis->set($key, 'val');
    -        $this->assertEquals('val', $this->redis->get($key));
    +        $this->assertKeyEquals('val', $key);
             $this->assertEquals(1, $this->redis->$cmd($key));
             $this->assertFalse($this->redis->get($key));
     
    @@ -1073,17 +1073,17 @@ public function testType() {
         public function testStr() {
             $this->redis->set('key', 'val1');
             $this->assertEquals(8, $this->redis->append('key', 'val2'));
    -        $this->assertEquals('val1val2', $this->redis->get('key'));
    +        $this->assertKeyEquals('val1val2', 'key');
     
             $this->redis->del('keyNotExist');
             $this->assertEquals(5, $this->redis->append('keyNotExist', 'value'));
    -        $this->assertEquals('value', $this->redis->get('keyNotExist'));
    +        $this->assertKeyEquals('value', 'keyNotExist');
     
             $this->redis->set('key', 'This is a string') ;
             $this->assertEquals('This', $this->redis->getRange('key', 0, 3));
             $this->assertEquals('string', $this->redis->getRange('key', -6, -1));
             $this->assertEquals('string', $this->redis->getRange('key', -6, 100000));
    -        $this->assertEquals('This is a string', $this->redis->get('key'));
    +        $this->assertKeyEquals('This is a string', 'key');
     
             $this->redis->set('key', 'This is a string') ;
             $this->assertEquals(16, $this->redis->strlen('key'));
    @@ -3234,16 +3234,16 @@ public function testSetRange() {
             $this->redis->del('key');
             $this->redis->set('key', 'hello world');
             $this->redis->setRange('key', 6, 'redis');
    -        $this->assertEquals('hello redis', $this->redis->get('key'));
    +        $this->assertKeyEquals('hello redis', 'key');
             $this->redis->setRange('key', 6, 'you'); // don't cut off the end
    -        $this->assertEquals('hello youis', $this->redis->get('key'));
    +        $this->assertKeyEquals('hello youis', 'key');
     
             $this->redis->set('key', 'hello world');
     
             // fill with zeros if needed
             $this->redis->del('key');
             $this->redis->setRange('key', 6, 'foo');
    -        $this->assertEquals("\x00\x00\x00\x00\x00\x00foo", $this->redis->get('key'));
    +        $this->assertKeyEquals("\x00\x00\x00\x00\x00\x00foo", 'key');
         }
     
         public function testObject() {
    @@ -3941,7 +3941,6 @@ protected function sequence($mode) {
         }
     
         protected function differentType($mode) {
    -
             // string
             $key = '{hash}string';
             $dkey = '{hash}' . __FUNCTION__;
    @@ -4847,46 +4846,48 @@ public function testSerializerPHP() {
         }
     
         public function testSerializerIGBinary() {
    -        if (defined('Redis::SERIALIZER_IGBINARY')) {
    -            $this->checkSerializer(Redis::SERIALIZER_IGBINARY);
    +        if ( ! defined('Redis::SERIALIZER_IGBINARY'))
    +            $this->markTestSkipped('Redis::SERIALIZER_IGBINARY is not defined');
    +
    +        $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, '');
    +        // with prefix
    +        $this->redis->setOption(Redis::OPT_PREFIX, 'test:');
    +        $this->checkSerializer(Redis::SERIALIZER_IGBINARY);
    +        $this->redis->setOption(Redis::OPT_PREFIX, '');
     
    -            /* Test our igbinary header check logic.  The check allows us to do
    -               simple INCR type operations even with the serializer enabled, and
    -               should also protect against igbinary-like data from being erroneously
    -               deserialized */
    -            $this->redis->del('incrkey');
    +        /* Test our igbinary header check logic.  The check allows us to do
    +           simple INCR type operations even with the serializer enabled, and
    +           should also protect against igbinary-like data from being erroneously
    +           deserialized */
    +        $this->redis->del('incrkey');
     
    -            $this->redis->set('spoof-1', "\x00\x00\x00\x00");
    -            $this->redis->set('spoof-2', "\x00\x00\x00\x00bad-version1");
    -            $this->redis->set('spoof-3', "\x00\x00\x00\x05bad-version2");
    -            $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);
    +        $this->redis->set('spoof-1', "\x00\x00\x00\x00");
    +        $this->redis->set('spoof-2', "\x00\x00\x00\x00bad-version1");
    +        $this->redis->set('spoof-3', "\x00\x00\x00\x05bad-version2");
    +        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);
     
    -            $this->assertEquals(16, $this->redis->incrby('incrkey', 16));
    -            $this->assertEquals('16', $this->redis->get('incrkey'));
    +        $this->assertEquals(16, $this->redis->incrby('incrkey', 16));
    +        $this->assertKeyEquals('16', 'incrkey');
     
    -            $this->assertEquals("\x00\x00\x00\x00", $this->redis->get('spoof-1'));
    -            $this->assertEquals("\x00\x00\x00\x00bad-version1", $this->redis->get('spoof-2'));
    -            $this->assertEquals("\x00\x00\x00\x05bad-version2", $this->redis->get('spoof-3'));
    -            $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
    +        $this->assertKeyEquals("\x00\x00\x00\x00", 'spoof-1');
    +        $this->assertKeyEquals("\x00\x00\x00\x00bad-version1", 'spoof-2');
    +        $this->assertKeyEquals("\x00\x00\x00\x05bad-version2", 'spoof-3');
    +        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
     
    -            $this->redis->del('incrkey', 'spoof-1', 'spoof-2', 'spoof-3');
    -        }
    +        $this->redis->del('incrkey', 'spoof-1', 'spoof-2', 'spoof-3');
         }
     
         public function testSerializerMsgPack() {
    -        if (defined('Redis::SERIALIZER_MSGPACK')) {
    -            $this->checkSerializer(Redis::SERIALIZER_MSGPACK);
    +        if ( ! defined('Redis::SERIALIZER_MSGPACK'))
    +            $this->markTestSkipped('Redis::SERIALIZER_MSGPACK is not defined');
     
    -            // with prefix
    -            $this->redis->setOption(Redis::OPT_PREFIX, 'test:');
    -            $this->checkSerializer(Redis::SERIALIZER_MSGPACK);
    -            $this->redis->setOption(Redis::OPT_PREFIX, '');
    -        }
    +        $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() {
    @@ -4899,7 +4900,6 @@ public function testSerializerJSON() {
         }
     
         private function checkSerializer($mode) {
    -
             $this->redis->del('key');
             $this->assertEquals(Redis::SERIALIZER_NONE, $this->redis->getOption(Redis::OPT_SERIALIZER));   // default
     
    @@ -4962,7 +4962,7 @@ private function checkSerializer($mode) {
             $this->redis->sAdd('k', 'a', 'b', 'c', 'd');
             $this->assertEquals(2, $this->redis->sRem('k', 'a', 'd'));
             $this->assertEquals(2, $this->redis->sRem('k', 'b', 'c', 'e'));
    -        $this->assertKeyMissing($this->redis, 'k');
    +        $this->assertKeyMissing('k');
     
             // sismember
             $this->assertTrue($this->redis->sismember('{set}key', $s[0]));
    @@ -5034,7 +5034,7 @@ private function checkSerializer($mode) {
             $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']];
             $this->assertTrue($this->redis->mset($a));
             foreach ($a as $k => $v) {
    -            $this->assertEquals($v, $this->redis->get($k));
    +            $this->assertKeyEquals($v, $k);
             }
     
             $a = ['f0' => 1, 'f1' => 42, 'f2' => NULL, 'f3' => FALSE, 'f4' => ['a' => 'b']];
    @@ -5117,11 +5117,6 @@ private function checkSerializer($mode) {
             $this->assertEquals(Redis::SERIALIZER_NONE, $this->redis->getOption(Redis::OPT_SERIALIZER));       // get ok
         }
     
    -    // check that zRem doesn't crash with a missing parameter (GitHub issue #102):
    -//    public function testGHIssue_102() {
    -//        $this->assertFalse(@$this->redis->zRem('key'));
    -//    }
    -
         public function testCompressionLZF() {
             if ( ! defined('Redis::COMPRESSION_LZF'))
                 $this->markTestSkipped();
    @@ -5130,7 +5125,7 @@ public function testCompressionLZF() {
             $payload = 'not-actually-lzf-compressed';
             $this->redis->set('badlzf', $payload);
             $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_LZF);
    -        $this->assertEquals($payload, $this->redis->get('badlzf'));
    +        $this->assertKeyEquals($payload, 'badlzf');
             $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
     
             $this->checkCompression(Redis::COMPRESSION_LZF, 0);
    @@ -5144,7 +5139,7 @@ public function testCompressionZSTD() {
             $this->redis->del('badzstd');
             $this->redis->set('badzstd', '123');
             $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_ZSTD);
    -        $this->assertEquals('123', $this->redis->get('badzstd'));
    +        $this->assertKeyEquals('123', 'badzstd');
             $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
     
             $this->checkCompression(Redis::COMPRESSION_ZSTD, 0);
    @@ -5183,17 +5178,17 @@ private function checkCompression($mode, $level) {
     
             $val = 'xxxxxxxxxx';
             $this->redis->set('key', $val);
    -        $this->assertEquals($val, $this->redis->get('key'));
    +        $this->assertKeyEquals($val, 'key');
     
             /* Empty data */
             $this->redis->set('key', '');
    -        $this->assertEquals('', $this->redis->get('key'));
    +        $this->assertKeyEquals('', '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'));
    +                $this->assertKeyEquals($val, 'key');
                 }
             }
     
    @@ -5224,8 +5219,8 @@ public function testDumpRestore() {
             $this->assertTrue($this->redis->restore('bar', 0, $d_foo));
     
             // Now check that the keys have switched
    -        $this->assertEquals('this-is-bar', $this->redis->get('foo'));
    -        $this->assertEquals('this-is-foo', $this->redis->get('bar'));
    +        $this->assertKeyEquals('this-is-bar', 'foo');
    +        $this->assertKeyEquals('this-is-foo', 'bar');
     
             /* Test that we can REPLACE a key */
             $this->assertTrue($this->redis->set('foo', 'some-value'));
    @@ -5495,19 +5490,13 @@ public function testSerialize() {
         }
     
         public function testUnserialize() {
    -        $vals = [
    -            1, 1.5,'one',['this', 'is', 'an', 'array']
    -        ];
    -
    -        $serializers = [Redis::SERIALIZER_PHP];
    +        $vals = [1, 1.5,'one',['this', 'is', 'an', 'array']];
     
    -        if (defined('Redis::SERIALIZER_IGBINARY')) {
    -            $serializers[] = Redis::SERIALIZER_IGBINARY;
    -        }
    -
    -        if (defined('Redis::SERIALIZER_MSGPACK')) {
    -            $serializers[] = Redis::SERIALIZER_MSGPACK;
    -        }
    +        /* We want to skip SERIALIZER_NONE because strict type checking will
    +           fail on the assertions (which is expected). */
    +        $serializers = array_filter($this->getSerializers(), function($v) {
    +            return $v != Redis::SERIALIZER_NONE;
    +        });
     
             foreach ($serializers as $mode) {
                 $vals_enc = [];
    @@ -5758,14 +5747,13 @@ public function testReconnectSelect() {
             sleep($this->minVersionCheck('3.0.0') ? 2 : 11);
     
             // Make sure we're still using the same DB.
    -        $this->assertEquals($value, $this->redis->get($key));
    +        $this->assertKeyEquals($value, $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();
     
    @@ -5776,7 +5764,6 @@ public function testTime() {
         }
     
         public function testReadTimeoutOption() {
    -
             $this->assertTrue(defined('Redis::OPT_READ_TIMEOUT'));
     
             $this->redis->setOption(Redis::OPT_READ_TIMEOUT, '12.3');
    @@ -5798,7 +5785,7 @@ public function testTransferredBytes() {
             $get_tx_resp = "*3\r\n$3\r\nGET\r\n$3\r\nkey\r\n";
             $get_rx_resp = "$3\r\nval\r\n";
     
    -        $this->assertEquals('val', $this->redis->get('key'));
    +        $this->assertKeyEquals('val', 'key');
             list ($tx, $rx) = $this->redis->getTransferredBytes();
             $this->assertEquals(strlen($get_tx_resp), $tx);
             $this->assertEquals(strlen($get_rx_resp), $rx);
    @@ -5850,7 +5837,7 @@ public function testScan() {
             $this->assertEquals(0, $key_count);
     
             // Unique keys, for pattern matching
    -        $uniq = uniqid() . '-' . uniqid();
    +        $uniq = uniqid();
             for ($i = 0; $i < 10; $i++) {
                 $this->redis->set($uniq . "::$i", "bar::$i");
             }
    @@ -6140,7 +6127,7 @@ public function testPFCommands() {
             $key_count = 10;
     
             // Iterate prefixing/serialization options
    -        foreach ([Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP] as $ser) {
    +        foreach ($this->getSerializers() as $ser) {
                 foreach (['', 'hl-key-prefix:'] as $prefix) {
                     $keys = [];
     
    @@ -7147,9 +7134,8 @@ function($o) { $o->auth(['1337haxx00r', 'lolwut']); }, '/^WRONGPASS.*$/');
     
         /* 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')) {
    +        if ( ! file_exists('/tmp/redis.sock'))
                 $this->markTestSkipped();
    -        }
     
             $sock_tests = [
                 ['/tmp/redis.sock'],
    @@ -7257,7 +7243,7 @@ public function testSession_savedToRedis() {
             $runner = $this->sessionRunner();
     
             $this->assertEquals('SUCCESS', $runner->execFg());
    -        $this->assertKeyExists($this->redis, $runner->getSessionKey());
    +        $this->assertKeyExists($runner->getSessionKey());
         }
     
         protected function sessionWaitUsec() {
    @@ -7291,7 +7277,7 @@ public function testSession_lockingDisabledByDefault() {
                 ->sleep(5);
     
             $this->assertEquals('SUCCESS', $runner->execFg());
    -        $this->assertKeyMissing($this->redis, $runner->getSessionLockKey());
    +        $this->assertKeyMissing($runner->getSessionLockKey());
         }
     
         public function testSession_lockReleasedOnClose() {
    @@ -7303,7 +7289,7 @@ public function testSession_lockReleasedOnClose() {
     
             $this->assertTrue($runner->execBg());
             usleep($this->sessionWaitUsec() + 100000);
    -        $this->assertKeyMissing($this->redis, $runner->getSessionLockKey());
    +        $this->assertKeyMissing($runner->getSessionLockKey());
         }
     
         public function testSession_lock_ttlMaxExecutionTime() {
    @@ -7394,8 +7380,8 @@ public function testSession_correctLockRetryCount() {
             $this->assertTrue($runner->execBg());
             if ( ! $runner->waitForLockKey($this->redis, 2)) {
                 $this->externalCmdFailure($runner->getCmd(), $runner->output(),
    -                                 'Failed waiting for session lock key',
    -                                 $runner->getExitCode());
    +                                      'Failed waiting for session lock key',
    +                                      $runner->getExitCode());
             }
     
             $runner2 = $this->sessionRunner()
    @@ -7484,7 +7470,6 @@ public function testSession_noUnlockOfOtherProcess() {
         public function testSession_lockWaitTime() {
             $this->testRequiresMode('cli');
     
    -
             $runner = $this->sessionRunner()
                 ->sleep(1)
                 ->maxExecutionTime(300);
    @@ -7562,14 +7547,14 @@ public function testCopy() {
             $this->redis->del('{key}dst');
             $this->redis->set('{key}src', 'foo');
             $this->assertTrue($this->redis->copy('{key}src', '{key}dst'));
    -        $this->assertEquals('foo', $this->redis->get('{key}dst'));
    +        $this->assertKeyEquals('foo', '{key}dst');
     
             $this->redis->set('{key}src', 'bar');
             $this->assertFalse($this->redis->copy('{key}src', '{key}dst'));
    -        $this->assertEquals('foo', $this->redis->get('{key}dst'));
    +        $this->assertKeyEquals('foo', '{key}dst');
     
             $this->assertTrue($this->redis->copy('{key}src', '{key}dst', ['replace' => true]));
    -        $this->assertEquals('bar', $this->redis->get('{key}dst'));
    +        $this->assertKeyEquals('bar', '{key}dst');
         }
     
         public function testCommand() {
    diff --git a/tests/TestSuite.php b/tests/TestSuite.php
    index 44de96c085..2e156c72cb 100644
    --- a/tests/TestSuite.php
    +++ b/tests/TestSuite.php
    @@ -125,8 +125,30 @@ protected function assert($fmt, ...$args) {
             self::$errors []= $this->assertionTrace($fmt, ...$args);
         }
     
    -    protected function assertKeyExists($redis, $key): bool {
    -        if ($redis->exists($key))
    +    protected function assertKeyEquals($expected, $key, $redis = NULL): bool {
    +        $actual = ($redis ??= $this->redis)->get($key);
    +        if ($actual === $expected)
    +            return true;
    +
    +        self::$errors []= $this->assertionTrace("%s !== %s", $this->printArg($actual),
    +                                                $this->printArg($expected));
    +
    +        return false;
    +    }
    +
    +    protected function assertKeyEqualsWeak($expected, $key, $redis = NULL): bool {
    +        $actual = ($redis ??= $this->redis)->get($key);
    +        if ($actual == $expected)
    +            return true;
    +
    +        self::$errors []= $this->assertionTrace("%s != %s", $this->printArg($actual),
    +                                                $this->printArg($expected));
    +
    +        return false;
    +    }
    +
    +    protected function assertKeyExists($key, $redis = NULL): bool {
    +        if (($redis ??= $this->redis)->exists($key))
                 return true;
     
             self::$errors []= $this->assertionTrace("Key '%s' does not exist.", $key);
    @@ -134,8 +156,8 @@ protected function assertKeyExists($redis, $key): bool {
             return false;
         }
     
    -    protected function assertKeyMissing($redis, $key): bool {
    -        if ( ! $redis->exists($key))
    +    protected function assertKeyMissing($key, $redis = NULL): bool {
    +        if ( ! ($redis ??= $this->redis)->exists($key))
                 return true;
     
             self::$errors []= $this->assertionTrace("Key '%s' exists but shouldn't.", $key);
    
    From 57304970cd9dc26e5c925105a7430ad8059b59e0 Mon Sep 17 00:00:00 2001
    From: Michael Grunder 
    Date: Tue, 18 Jun 2024 16:05:21 -0700
    Subject: [PATCH 1893/1986] PHP might throw a fatal error if we send no args to
     exists (#2510)
    
    ---
     tests/RedisTest.php | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index cc178935f5..e9ceaf1a62 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -939,7 +939,8 @@ public function testExists() {
     
             /* Test passing an array as well as the keys variadic */
             $this->assertEquals(count($mkeys), $this->redis->exists($mkeys));
    -        $this->assertEquals(count($mkeys), $this->redis->exists(...$mkeys));
    +        if (count($mkeys))
    +            $this->assertEquals(count($mkeys), $this->redis->exists(...$mkeys));
         }
     
         public function testTouch() {
    
    From 7c551424b62f00f81e9bee9e9a9a55c88c53471f Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Thu, 20 Jun 2024 21:05:20 +0300
    Subject: [PATCH 1894/1986] Refactor redis_script_cmd
    
    - Update redis_script_cmd to use redis_build_script_cmd.
    - Fix condition for parsing sync/async arguments of flush sub-command.
    ---
     redis_commands.c | 33 ++++++++++++++++++++++++---------
     1 file changed, 24 insertions(+), 9 deletions(-)
    
    diff --git a/redis_commands.c b/redis_commands.c
    index 5a8f46384e..1b3719ec93 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -168,16 +168,17 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args)
             return NULL;
         }
         // Branch based on the directive
    -    if (!strcasecmp(Z_STRVAL(z_args[0]), "kill")) {
    +    if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "kill")) {
             // Simple SCRIPT_KILL command
             REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT");
             redis_cmd_append_sstr(cmd, ZEND_STRL("KILL"));
    -    } else if (!strcasecmp(Z_STRVAL(z_args[0]), "flush")) {
    +    } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "flush")) {
             // Simple SCRIPT FLUSH [ASYNC | SYNC]
             if (argc > 1 && (
    -            Z_TYPE(z_args[1]) != IS_STRING ||
    -            strcasecmp(Z_STRVAL(z_args[1]), "sync") ||
    -            strcasecmp(Z_STRVAL(z_args[1]), "async")
    +            Z_TYPE(z_args[1]) != IS_STRING || (
    +                !zend_string_equals_literal_ci(Z_STR(z_args[1]), "sync") &&
    +                !zend_string_equals_literal_ci(Z_STR(z_args[1]), "async")
    +            )
             )) {
                 return NULL;
             }
    @@ -186,7 +187,7 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args)
             if (argc > 1) {
                 redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
             }
    -    } else if (!strcasecmp(Z_STRVAL(z_args[0]), "load")) {
    +    } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "load")) {
             // Make sure we have a second argument, and it's not empty.  If it is
             // empty, we can just return an empty array (which is what Redis does)
             if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING || Z_STRLEN(z_args[1]) < 1) {
    @@ -196,7 +197,7 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args)
             REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT");
             redis_cmd_append_sstr(cmd, ZEND_STRL("LOAD"));
             redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1]));
    -    } else if (!strcasecmp(Z_STRVAL(z_args[0]), "exists")) {
    +    } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "exists")) {
             // Make sure we have a second argument
             if (argc < 2) {
                 return NULL;
    @@ -2106,8 +2107,22 @@ int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     int redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                        char **cmd, int *cmd_len, short *slot, void **ctx)
     {
    -    return gen_vararg_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1,
    -                          "SCRIPT", cmd, cmd_len, slot, ctx);
    +    int argc = 0;
    +    smart_string cmdstr = {0};
    +    zval *argv = NULL;
    +
    +    ZEND_PARSE_PARAMETERS_START(1, -1)
    +        Z_PARAM_VARIADIC('*', argv, argc)
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
    +
    +    if (redis_build_script_cmd(&cmdstr, argc, argv) == NULL) {
    +        return FAILURE;
    +    }
    +
    +    *cmd = cmdstr.c;
    +    *cmd_len = cmdstr.len;
    +
    +    return SUCCESS;
     }
     
     /* Generic handling of every blocking pop command (BLPOP, BZPOP[MIN/MAX], etc */
    
    From 981c69314dd2ce3d5c65573bec298480327c453e Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 20 Jun 2024 11:29:42 -0700
    Subject: [PATCH 1895/1986] Add `GETEX` to README docs + minor change to
     command.
    
    * Adds `GETEX` to the README.md documentation.
    * Allow the user to send `PERSIST` either as an array key or just in the
      array, to conform with similar methods.
    * Implement getEx for `RedisCluster`
    
    Fixes #2512
    ---
     README.md                      | 23 +++++++++++++++++++++++
     redis_cluster.c                |  4 ++++
     redis_cluster.stub.php         |  5 +++++
     redis_cluster_arginfo.h        |  9 ++++++++-
     redis_cluster_legacy_arginfo.h | 20 ++++++++++++--------
     redis_commands.c               |  5 +++++
     tests/RedisTest.php            | 29 ++++++++++++++++++++++++++++-
     7 files changed, 85 insertions(+), 10 deletions(-)
    
    diff --git a/README.md b/README.md
    index 80a42b9131..2a176e8008 100644
    --- a/README.md
    +++ b/README.md
    @@ -787,6 +787,7 @@ $redis->slowLog('len');
     * [bitOp](#bitop) - Perform bitwise operations between strings
     * [decr, decrBy](#decr-decrby) - Decrement the value of a key
     * [get](#get) - Get the value of a key
    +* [getEx](#getex) - Get the value of a key and set its expiration
     * [getBit](#getbit) - Returns the bit value at offset in the string value stored at key
     * [getRange](#getrange) - Get a substring of the string stored at a key
     * [getSet](#getset) - Set the string value of a key and return its old value
    @@ -841,6 +842,28 @@ _**Description**_: Get the value related to the specified key
     $redis->get('key');
     ~~~
     
    +### getEx
    +-----
    +_**Description**_: Get the value related to the specified key and set its expiration
    +
    +##### *Parameters*
    +*key* 
    +*options array* (optional) with the following keys:
    +  * `EX` - expire time in seconds
    +  * `PX` - expire time in milliseconds
    +  * `EXAT` - expire time in seconds since UNIX epoch
    +  * `PXAT` - expire time in milliseconds since UNIX epoch
    +  * `PERSIST` - remove the expiration from the key
    +
    +##### *Return value*
    +*String* or *Bool*: If key didn't exist, `FALSE` is returned. Otherwise, the value related to this key is returned.
    +
    +##### *Examples*
    +
    +~~~php
    +$redis->getEx('key', ['EX' => 10]); // get key and set its expiration to 10 seconds
    +~~~
    +
     ### set
     -----
     _**Description**_: Set the string value in argument as value of the key.  If you're using Redis >= 2.6.12, you can pass extended options as explained below
    diff --git a/redis_cluster.c b/redis_cluster.c
    index b60f8045f0..a19514f168 100644
    --- a/redis_cluster.c
    +++ b/redis_cluster.c
    @@ -727,6 +727,10 @@ PHP_METHOD(RedisCluster, msetnx) {
     }
     /* }}} */
     
    +PHP_METHOD(RedisCluster, getex) {
    +    CLUSTER_PROCESS_CMD(getex, cluster_bulk_resp, 0);
    +}
    +
     /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */
     PHP_METHOD(RedisCluster, setex) {
         CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, cluster_bool_resp, 0);
    diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
    index 0210b4d074..16f3154a7f 100644
    --- a/redis_cluster.stub.php
    +++ b/redis_cluster.stub.php
    @@ -390,6 +390,11 @@ public function geosearchstore(string $dst, string $src, array|string $position,
          */
         public function get(string $key): mixed;
     
    +    /**
    +     * @see Redis::getEx
    +     */
    +    public function getex(string $key, array $options = []): RedisCluster|string|false;
    +
         /**
          * @see Redis::getbit
          */
    diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
    index 5a66276a0d..ff9a281dde 100644
    --- a/redis_cluster_arginfo.h
    +++ b/redis_cluster_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: c19108e54b637b6c76a529c1285104a0c38da220 */
    + * Stub hash: 5713c5b2f88ddead50088f14026447801120fa33 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
    @@ -325,6 +325,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_get, 0, 1, IS
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getex, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
    +ZEND_END_ARG_INFO()
    +
     #define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_decrby
     
     #define arginfo_class_RedisCluster_getlasterror arginfo_class_RedisCluster__redir
    @@ -1109,6 +1114,7 @@ ZEND_METHOD(RedisCluster, georadiusbymember_ro);
     ZEND_METHOD(RedisCluster, geosearch);
     ZEND_METHOD(RedisCluster, geosearchstore);
     ZEND_METHOD(RedisCluster, get);
    +ZEND_METHOD(RedisCluster, getex);
     ZEND_METHOD(RedisCluster, getbit);
     ZEND_METHOD(RedisCluster, getlasterror);
     ZEND_METHOD(RedisCluster, getmode);
    @@ -1335,6 +1341,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, getex, arginfo_class_RedisCluster_getex, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
    diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
    index 137dc7c5b9..a3cb82d386 100644
    --- a/redis_cluster_legacy_arginfo.h
    +++ b/redis_cluster_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: c19108e54b637b6c76a529c1285104a0c38da220 */
    + * Stub hash: 5713c5b2f88ddead50088f14026447801120fa33 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_INFO(0, name)
    @@ -295,6 +295,11 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_RedisCluster_get arginfo_class_RedisCluster__prefix
     
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getex, 0, 0, 1)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, options)
    +ZEND_END_ARG_INFO()
    +
     #define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_append
     
     #define arginfo_class_RedisCluster_getlasterror arginfo_class_RedisCluster__masters
    @@ -363,10 +368,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hscan, 0, 0, 2)
     	ZEND_ARG_INFO(0, count)
     ZEND_END_ARG_INFO()
     
    -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hrandfield, 0, 0, 1)
    -	ZEND_ARG_INFO(0, key)
    -	ZEND_ARG_INFO(0, options)
    -ZEND_END_ARG_INFO()
    +#define arginfo_class_RedisCluster_hrandfield arginfo_class_RedisCluster_getex
     
     #define arginfo_class_RedisCluster_hset arginfo_class_RedisCluster_hincrby
     
    @@ -636,9 +638,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_smove, 0, 0, 3)
     	ZEND_ARG_INFO(0, member)
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_RedisCluster_sort arginfo_class_RedisCluster_hrandfield
    +#define arginfo_class_RedisCluster_sort arginfo_class_RedisCluster_getex
     
    -#define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_hrandfield
    +#define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_getex
     
     #define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster_lpop
     
    @@ -824,7 +826,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangestore, 0, 0, 4)
     	ZEND_ARG_INFO(0, options)
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_RedisCluster_zrandmember arginfo_class_RedisCluster_hrandfield
    +#define arginfo_class_RedisCluster_zrandmember arginfo_class_RedisCluster_getex
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3)
     	ZEND_ARG_INFO(0, key)
    @@ -954,6 +956,7 @@ ZEND_METHOD(RedisCluster, georadiusbymember_ro);
     ZEND_METHOD(RedisCluster, geosearch);
     ZEND_METHOD(RedisCluster, geosearchstore);
     ZEND_METHOD(RedisCluster, get);
    +ZEND_METHOD(RedisCluster, getex);
     ZEND_METHOD(RedisCluster, getbit);
     ZEND_METHOD(RedisCluster, getlasterror);
     ZEND_METHOD(RedisCluster, getmode);
    @@ -1180,6 +1183,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, getex, arginfo_class_RedisCluster_getex, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
    diff --git a/redis_commands.c b/redis_commands.c
    index 1b3719ec93..bf0b4c4b77 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -2476,6 +2476,11 @@ redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                         persist = zval_is_true(z_ele);
                         exp_type = NULL;
                     }
    +            } else if (Z_TYPE_P(z_ele) == IS_STRING &&
    +                       zend_string_equals_literal_ci(Z_STR_P(z_ele), "PERSIST"))
    +            {
    +                persist = zval_is_true(z_ele);
    +                exp_type = NULL;
                 }
             } ZEND_HASH_FOREACH_END();
         }
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index e9ceaf1a62..74e0ffc3ac 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -790,8 +790,35 @@ public function testExpiretime() {
             $this->redis->del('key1');
         }
     
    -    public function testSetEx() {
    +    public function testGetEx() {
    +        if (version_compare($this->version, '6.2.0') < 0)
    +            $this->markTestSkipped();
    +
    +        $this->assertTrue($this->redis->set('key', 'value'));
    +
    +        $this->assertEquals('value', $this->redis->getEx('key', ['EX' => 100]));
    +        $this->assertBetween($this->redis->ttl('key'), 95, 100);
    +
    +        $this->assertEquals('value', $this->redis->getEx('key', ['PX' => 100000]));
    +        $this->assertBetween($this->redis->pttl('key'), 95000, 100000);
    +
    +        $this->assertEquals('value', $this->redis->getEx('key', ['EXAT' => time() + 200]));
    +        $this->assertBetween($this->redis->ttl('key'), 195, 200);
     
    +        $this->assertEquals('value', $this->redis->getEx('key', ['PXAT' => (time()*1000) + 25000]));
    +        $this->assertBetween($this->redis->pttl('key'), 24000, 25000);
    +
    +        $this->assertEquals('value', $this->redis->getEx('key', ['PERSIST' => true]));
    +        $this->assertEquals(-1, $this->redis->ttl('key'));
    +
    +        $this->assertTrue($this->redis->expire('key', 100));
    +        $this->assertBetween($this->redis->ttl('key'), 95, 100);
    +
    +        $this->assertEquals('value', $this->redis->getEx('key', ['PERSIST']));
    +        $this->assertEquals(-1, $this->redis->ttl('key'));
    +    }
    +
    +    public function testSetEx() {
             $this->redis->del('key');
             $this->assertTrue($this->redis->setex('key', 7, 'val'));
             $this->assertEquals(7, $this->redis->ttl('key'));
    
    From 7de29d57d919f835f902f87a83312ed2549c1a13 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sun, 7 Jul 2024 19:20:36 -0700
    Subject: [PATCH 1896/1986] Fix a macOS (M1) compiler warning.
    
    ---
     library.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/library.c b/library.c
    index dc86557a84..852a583146 100644
    --- a/library.c
    +++ b/library.c
    @@ -3130,7 +3130,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
                 }
     
                 gettimeofday(&tv, NULL);
    -            persistent_id = strpprintf(0, "phpredis_%ld%ld", tv.tv_sec, tv.tv_usec);
    +            persistent_id = strpprintf(0, "phpredis_%ld%ld", tv.tv_sec, (long)tv.tv_usec);
             } else {
                 if (redis_sock->persistent_id) {
                     persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id));
    
    From 50529f56e45d4735beeabda61c9ba8cae9ba03c4 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Wed, 10 Jul 2024 11:35:25 -0700
    Subject: [PATCH 1897/1986] Context array should be nullable
    
    Fixes #2521
    ---
     redis.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/redis.c b/redis.c
    index b52e126b77..31d9611efb 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -537,7 +537,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
     #endif
     
         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
    -                                     "Os|lds!lda", &object, redis_ce, &host,
    +                                     "Os|lds!lda!", &object, redis_ce, &host,
                                          &host_len, &port, &timeout, &persistent_id,
                                          &persistent_id_len, &retry_interval,
                                          &read_timeout, &context) == FAILURE)
    
    From eeb5109967db6600278bddeca794506ac1e0a8e4 Mon Sep 17 00:00:00 2001
    From: Michael Dwyer 
    Date: Thu, 11 Jul 2024 23:49:29 -0500
    Subject: [PATCH 1898/1986] Update documentation (#2523)
    
    * Remove/update mentions of removed methods
    
    These methods were deprecated in a previous release
    
    * Correct documentation for zInter/zinterstore
    
    * Correct documentation for zUnion/zunionstore
    
    * Add documentation for zDiff/zdiffstore
    
    * Add documentation for zMscore
    ---
     README.md           | 283 +++++++++++++++++++++++++++++++-------------
     serialize.list      |  16 +--
     tests/RedisTest.php |  22 ++--
     3 files changed, 222 insertions(+), 99 deletions(-)
    
    diff --git a/README.md b/README.md
    index 2a176e8008..9f3389d893 100644
    --- a/README.md
    +++ b/README.md
    @@ -793,7 +793,7 @@ $redis->slowLog('len');
     * [getSet](#getset) - Set the string value of a key and return its old value
     * [incr, incrBy](#incr-incrby) - Increment the value of a key
     * [incrByFloat](#incrbyfloat) - Increment the float value of a key by the given amount
    -* [mGet, getMultiple](#mget-getmultiple) - Get the values of all the given keys
    +* [mGet](#mget) - Get the values of all the given keys
     * [mSet, mSetNX](#mset-msetnx) - Set multiple keys to multiple values
     * [set](#set) - Set the string value of a key
     * [setBit](#setbit) - Sets or clears the bit at offset in the string value stored at key
    @@ -808,16 +808,16 @@ $redis->slowLog('len');
     * [del, delete, unlink](#del-delete-unlink) - Delete a key
     * [dump](#dump) - Return a serialized version of the value stored at the specified key.
     * [exists](#exists) - Determine if a key exists
    -* [expire, setTimeout, pexpire](#expire-pexpire) - Set a key's time to live in seconds
    +* [expire, pexpire](#expire-pexpire) - Set a key's time to live in seconds
     * [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp
    -* [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern
    +* [keys](#keys) - Find all keys matching the given pattern
     * [scan](#scan) - Scan for keys in the keyspace (Redis >= 2.8.0)
     * [migrate](#migrate) - Atomically transfer a key from a Redis instance to another one
     * [move](#move) - Move a key to another database
     * [object](#object) - Inspect the internals of Redis objects
     * [persist](#persist) - Remove the expiration from a key
     * [randomKey](#randomkey) - Return a random key from the keyspace
    -* [rename, renameKey](#rename-renamekey) - Rename a key
    +* [rename](#rename) - Rename a key
     * [renameNx](#renamenx) - Rename a key, only if the new key does not exist
     * [type](#type) - Determine the type stored at key
     * [sort](#sort) - Sort the elements in a list, set or sorted set
    @@ -1052,7 +1052,7 @@ $redis->decr('key1', 10);   /* -13 */
     $redis->decrBy('key1', 10); /* -23 */
     ~~~
     
    -### mGet, getMultiple
    +### mGet
     -----
     _**Description**_: Get the values of all the specified keys. If one or more keys don't exist, the array will contain `FALSE` at the position of the key.
     
    @@ -1071,8 +1071,6 @@ $redis->mGet(['key1', 'key2', 'key3']); /* ['value1', 'value2', 'value3'];
     $redis->mGet(['key0', 'key1', 'key5']); /* [`FALSE`, 'value1', `FALSE`];
     ~~~
     
    -**Note:** `getMultiple` is an alias for `mGet` and will be removed in future versions of phpredis.
    -
     ### getSet
     -----
     _**Description**_: Sets a value and returns the previous entry at that key.
    @@ -1126,7 +1124,7 @@ $redis->select(1);	// switch to DB 1
     $redis->get('x');	// will return 42
     ~~~
     
    -### rename, renameKey
    +### rename
     -----
     _**Description**_: Renames a key.
     ##### *Parameters*
    @@ -1144,8 +1142,6 @@ $redis->get('y'); 	// → 42
     $redis->get('x'); 	// → `FALSE`
     ~~~
     
    -**Note:** `renameKey` is an alias for `rename` and will be removed in future versions of phpredis.
    -
     ### renameNx
     -----
     _**Description**_: Same as rename, but will not replace a key if the destination already exists. This is the same behaviour as setNx.
    @@ -1170,8 +1166,6 @@ sleep(5);				// wait 5 seconds
     $redis->get('x'); 		// will return `FALSE`, as 'x' has expired.
     ~~~
     
    -**Note:** `setTimeout` is an alias for `expire` and will be removed in future versions of phpredis.
    -
     ### expireAt, pexpireAt
     -----
     _**Description**_: Seta specific timestamp for a key to expire in seconds or milliseconds.
    @@ -1193,7 +1187,7 @@ sleep(5);				// wait 5 seconds
     $redis->get('x'); 	// will return `FALSE`, as 'x' has expired.
     ~~~
     
    -### keys, getKeys
    +### keys
     -----
     _**Description**_: Returns the keys that match a certain pattern.
     
    @@ -1209,8 +1203,6 @@ $allKeys = $redis->keys('*');	// all keys will match this.
     $keyWithUserPrefix = $redis->keys('user*');
     ~~~
     
    -**Note:** `getKeys` is an alias for `keys` and will be removed in future versions of phpredis.
    -
     ### scan
     -----
     _**Description**_:  Scan the keyspace for keys
    @@ -1337,8 +1329,6 @@ $redis->getRange('key', 0, 5); /* 'string' */
     $redis->getRange('key', -5, -1); /* 'value' */
     ~~~
     
    -**Note**: `substr` is an alias for `getRange` and will be removed in future versions of phpredis.
    -
     ### setRange
     -----
     _**Description**_: Changes a substring of a larger string.
    @@ -1895,16 +1885,16 @@ _**Description**_: Get the string length of the value associated with field in t
     
     * [blPop, brPop](#blpop-brpop) - Remove and get the first/last element in a list
     * [bRPopLPush](#brpoplpush) - Pop a value from a list, push it to another list and return it
    -* [lIndex, lGet](#lindex-lget) - Get an element from a list by its index
    +* [lIndex](#lindex) - Get an element from a list by its index
     * [lInsert](#linsert) - Insert an element before or after another element in a list
    -* [lLen, lSize](#llen-lsize) - Get the length/size of a list
    +* [lLen](#llen) - Get the length/size of a list
     * [lPop](#lpop) - Remove and get the first element in a list
     * [lPush](#lpush) - Prepend one or multiple values to a list
     * [lPushx](#lpushx) - Prepend a value to a list, only if the list exists
    -* [lRange, lGetRange](#lrange-lgetrange) - Get a range of elements from a list
    -* [lRem, lRemove](#lrem-lremove) - Remove elements from a list
    +* [lRange](#lrange) - Get a range of elements from a list
    +* [lRem](#lrem) - Remove elements from a list
     * [lSet](#lset) - Set the value of an element in a list by its index
    -* [lTrim, listTrim](#ltrim-listtrim) - Trim a list to the specified range
    +* [lTrim](#ltrim) - Trim a list to the specified range
     * [rPop](#rpop) - Remove and get the last element in a list
     * [rPopLPush](#rpoplpush) - Remove the last element in a list, append it to another list and return it (redis >= 1.1)
     * [rPush](#rpush) - Append one or multiple values to a list
    @@ -1969,7 +1959,7 @@ _**Description**_: A blocking version of `rPopLPush`, with an integral timeout i
     ##### *Return value*
     *STRING* The element that was moved in case of success, `FALSE` in case of timeout.
     
    -### lIndex, lGet
    +### lIndex
     -----
     _**Description**_: Return the specified element of the list stored at the specified key.
     
    @@ -1996,8 +1986,6 @@ $redis->lindex('key1', -1); /* 'C' */
     $redis->lindex('key1', 10); /* `FALSE` */
     ~~~
     
    -**Note:** `lGet` is an alias for `lIndex` and will be removed in future versions of phpredis.
    -
     ### lInsert
     -----
     _**Description**_: Insert value in the list before or after the pivot value.
    @@ -2096,7 +2084,7 @@ $redis->lPushx('key1', 'C'); // returns 3
     /* key1 now points to the following list: [ 'A', 'B', 'C' ] */
     ~~~
     
    -### lRange, lGetRange
    +### lRange
     -----
     _**Description**_: Returns the specified elements of the list stored at the specified key in the range [start, end]. start and stop are interpreted as indices:  
     0 the first element, 1 the second ...  
    @@ -2118,9 +2106,7 @@ $redis->rPush('key1', 'C');
     $redis->lRange('key1', 0, -1); /* ['A', 'B', 'C'] */
     ~~~
     
    -**Note:** `lGetRange` is an alias for `lRange` and will be removed in future versions of phpredis.
    -
    -### lRem, lRemove
    +### lRem
     -----
     _**Description**_: Removes the first `count` occurrences of the value element from the list. If count is zero, all the matching elements are removed. If count is negative, elements are removed from tail to head.
     
    @@ -2148,8 +2134,6 @@ $redis->lRem('key1', 'A', 2); /* 2 */
     $redis->lRange('key1', 0, -1); /* ['C', 'B', 'A'] */
     ~~~
     
    -**Note:** `lRemove` is an alias for `lRem` and will be removed in future versions of phpredis.
    -
     ### lSet
     -----
     _**Description**_: Set the list at index with the new value.
    @@ -2172,7 +2156,7 @@ $redis->lSet('key1', 0, 'X');
     $redis->lindex('key1', 0); /* 'X' */
     ~~~
     
    -### lTrim, listTrim
    +### lTrim
     -----
     _**Description**_: Trims an existing list so that it will contain only a specified range of elements.
     
    @@ -2195,8 +2179,6 @@ $redis->lTrim('key1', 0, 1);
     $redis->lRange('key1', 0, -1); /* ['A', 'B'] */
     ~~~
     
    -**Note:** `listTrim` is an alias for `lTrim` and will be removed in future versions of phpredis.
    -
     ### rPop
     -----
     _**Description**_: Returns and removes the last element of the list.
    @@ -2302,7 +2284,7 @@ $redis->rPushX('key1', 'C'); // returns 3
     /* key1 now points to the following list: [ 'A', 'B', 'C' ] */
     ~~~
     
    -### lLen, lSize
    +### lLen
     -----
     _**Description**_: Returns the size of a list identified by Key.
     
    @@ -2325,23 +2307,21 @@ $redis->rPop('key1');
     $redis->lLen('key1');/* 2 */
     ~~~
     
    -**Note:** `lSize` is an alias for `lLen` and will be removed in future versions of phpredis.
    -
     
     ## Sets
     
     * [sAdd](#sadd) - Add one or more members to a set
    -* [sCard, sSize](#scard-ssize) - Get the number of members in a set
    +* [sCard](#scard) - Get the number of members in a set
     * [sDiff](#sdiff) - Subtract multiple sets
     * [sDiffStore](#sdiffstore) - Subtract multiple sets and store the resulting set in a key
     * [sInter](#sinter) - Intersect multiple sets
     * [sInterStore](#sinterstore) - Intersect multiple sets and store the resulting set in a key
    -* [sIsMember, sContains](#sismember-scontains) - Determine if a given value is a member of a set
    -* [sMembers, sGetMembers](#smembers-sgetmembers) - Get all the members in a set
    +* [sIsMember](#sismember) - Determine if a given value is a member of a set
    +* [sMembers](#smembers) - Get all the members in a set
     * [sMove](#smove) - Move a member from one set to another
     * [sPop](#spop) - Remove and return one or more members of a set at random
     * [sRandMember](#srandmember) - Get one or multiple random members from a set
    -* [sRem, sRemove](#srem-sremove) - Remove one or more members from a set
    +* [sRem](#srem) - Remove one or more members from a set
     * [sUnion](#sunion) - Add multiple sets
     * [sUnionStore](#sunionstore) - Add multiple sets and store the resulting set in a key
     * [sScan](#sscan) - Scan a set for members
    @@ -2362,7 +2342,7 @@ $redis->sAdd('key1' , 'member2', 'member3'); /* 2, 'key1' => {'member1', 'member
     $redis->sAdd('key1' , 'member2'); /* 0, 'key1' => {'member1', 'member2', 'member3'}*/
     ~~~
     
    -### sCard, sSize
    +### sCard
     -----
     _**Description**_: Returns the cardinality of the set identified by key.
     ##### *Parameters*
    @@ -2378,8 +2358,6 @@ $redis->sCard('key1'); /* 3 */
     $redis->sCard('keyX'); /* 0 */
     ~~~
     
    -**Note:** `sSize` is an alias for `sCard` and will be removed in future versions of phpredis.
    -
     ### sDiff
     -----
     _**Description**_: Performs the difference between N sets and returns it.
    @@ -2533,7 +2511,7 @@ array(2) {
     }
     ~~~
     
    -### sIsMember, sContains
    +### sIsMember
     -----
     _**Description**_: Checks if `value` is a member of the set stored at the key `key`.
     ##### *Parameters*
    @@ -2552,9 +2530,7 @@ $redis->sIsMember('key1', 'member1'); /* TRUE */
     $redis->sIsMember('key1', 'memberX'); /* FALSE */
     ~~~
     
    -**Note:** `sContains` is an alias for `sIsMember` and will be removed in future versions of phpredis.
    -
    -### sMembers, sGetMembers
    +### sMembers
     -----
     _**Description**_: Returns the contents of a set.
     
    @@ -2587,8 +2563,6 @@ array(3) {
     ~~~
     The order is random and corresponds to redis' own internal representation of the set structure.
     
    -**Note:** `sGetMembers` is an alias for `sMembers` and will be removed in future versions of phpredis.
    -
     ### sMove
     -----
     _**Description**_: Moves the specified member from the set at srcKey to the set at dstKey.
    @@ -2664,7 +2638,7 @@ $redis->sRandMember('empty-set', 100); // Will return an empty array
     $redis->sRandMember('not-a-set', 100); // Will return FALSE
     ~~~
     
    -### sRem, sRemove
    +### sRem
     -----
     _**Description**_: Removes the specified member from the set value stored at key.
     ##### *Parameters*
    @@ -2680,8 +2654,6 @@ $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'}
     $redis->sRem('key1', 'member2', 'member3'); /*return 2. 'key1' => {'member1'} */
     ~~~
     
    -**Note:** `sRemove` is an alias for `sRem` and will be removed in future versions of phpredis.
    -
     ### sUnion
     -----
     _**Description**_: Performs the union between N sets and returns it.
    @@ -2807,21 +2779,26 @@ while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) {
     
     * [bzPop](#bzpop) - Block until Redis can pop the highest or lowest scoring member from one or more ZSETs.
     * [zAdd](#zadd) - Add one or more members to a sorted set or update its score if it already exists
    -* [zCard, zSize](#zcard-zsize) - Get the number of members in a sorted set
    +* [zCard](#zcard) - Get the number of members in a sorted set
     * [zCount](#zcount) - Count the members in a sorted set with scores within the given values
    +* [zDiff](#zdiff) - Computes the difference between the first and all successive input sorted sets and return the resulting sorted set
    +* [zdiffstore](#zdiffstore) - Computes the difference between the first and all successive input sorted sets and stores the result in a new key
     * [zIncrBy](#zincrby) - Increment the score of a member in a sorted set
    -* [zinterstore, zInter](#zinterstore-zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key
    +* [zInter](#zinter) - Intersect multiple sorted sets and return the resulting sorted set
    +* [zinterstore](#zinterstore) - Intersect multiple sorted sets and store the resulting sorted set in a new key
    +* [zMscore](#zmscore) - Get the scores associated with the given members in a sorted set
     * [zPop](#zpop) - Redis can pop the highest or lowest scoring member from one a ZSET.
     * [zRange](#zrange) - Return a range of members in a sorted set, by index
     * [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score
     * [zRangeByLex](#zrangebylex) - Return a lexicographical range from members that share the same score
     * [zRank, zRevRank](#zrank-zrevrank) - Determine the index of a member in a sorted set
    -* [zRem, zDelete, zRemove](#zrem-zdelete-zremove) - Remove one or more members from a sorted set
    -* [zRemRangeByRank, zDeleteRangeByRank](#zremrangebyrank-zdeleterangebyrank) - Remove all members in a sorted set within the given indexes
    -* [zRemRangeByScore, zDeleteRangeByScore, zRemoveRangeByScore](#zremrangebyscore-zdeleterangebyscore-zremoverangebyscore) - Remove all members in a sorted set within the given scores
    +* [zRem](#zrem) - Remove one or more members from a sorted set
    +* [zRemRangeByRank](#zremrangebyrank) - Remove all members in a sorted set within the given indexes
    +* [zRemRangeByScore](#zremrangebyscore) - Remove all members in a sorted set within the given scores
     * [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low
     * [zScore](#zscore) - Get the score associated with the given member in a sorted set
    -* [zunionstore, zUnion](#zunionstore-zunion) - Add multiple sorted sets and store the resulting sorted set in a new key
    +* [zUnion](#zunion) - Add multiple sorted sets and return the resulting sorted set
    +* [zunionstore](#zunionstore) - Add multiple sorted sets and store the resulting sorted set in a new key
     * [zScan](#zscan) - Scan a sorted set for members
     
     ### bzPop
    @@ -2885,7 +2862,7 @@ $redis->zRange('key', 0, -1); // [val0, val1, val5]
     $redis->zAdd('key', ['CH'], 5, 'val5', 10, 'val10', 15, 'val15');
     ~~~
     
    -### zCard, zSize
    +### zCard
     -----
     _**Description**_: Returns the cardinality of an ordered set.
     
    @@ -2903,8 +2880,6 @@ $redis->zAdd('key', 10, 'val10');
     $redis->zCard('key'); /* 3 */
     ~~~
     
    -**Note**: `zSize` is an alias for `zCard` and will be removed in future versions of phpredis.
    -
     ### zCount
     -----
     _**Description**_: Returns the *number* of elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits.
    @@ -2925,6 +2900,75 @@ $redis->zAdd('key', 10, 'val10');
     $redis->zCount('key', 0, 3); /* 2, corresponding to ['val0', 'val2'] */
     ~~~
     
    +### zDiff
    +-----
    +_**Description**_: Computes the difference between the first and all successive input sorted sets in the first argument.  The result of the difference will be returned.
    +
    +The second argument is a set of options.  It can define `WITHSCORES` so that the scores are returned as well.
    +
    +##### *Parameters*
    +*arrayZSetKeys*  
    +*arrayOptions* One option is available: `withscores => TRUE`.
    +
    +##### *Return value*
    +*ARRAY* The result of the difference of sets.
    +
    +##### *Example*
    +~~~php
    +$redis->del('k1');
    +$redis->del('k2');
    +$redis->del('k3');
    +
    +$redis->zAdd('k1', 0, 'val0');
    +$redis->zAdd('k1', 1, 'val1');
    +$redis->zAdd('k1', 3, 'val3');
    +
    +$redis->zAdd('k2', 5, 'val1');
    +
    +$redis->zAdd('k3', 5, 'val0');
    +$redis->zAdd('k3', 3, 'val4');
    +
    +$redis->zDiff(['k1', 'k2']); 				                 /* ['val0', 'val3'] */
    +$redis->zDiff(['k2', 'k1']); 				                 /* [] */
    +$redis->zDiff(['k1', 'k2'], ['withscores' => true]); /* ['val0' => 0.0, 'val3' => 3.0] */
    +
    +$redis->zDiff(['k1', 'k2', 'k3']);                   /* ['val3'] */
    +$redis->zDiff(['k3', 'k2', 'k1']);                   /* ['val4'] */
    +~~~
    +
    +### zdiffstore
    +-----
    +_**Description**_: Computes the difference between the first and all successive input sorted sets in the second argument. The result of the difference will be stored in the sorted set defined by the first argument.
    +
    +##### *Parameters*
    +*keyOutput*  
    +*arrayZSetKeys*  
    +
    +##### *Return value*
    +*LONG* The number of values in the new sorted set.
    +
    +##### *Example*
    +~~~php
    +$redis->del('k1');
    +$redis->del('k2');
    +$redis->del('k3');
    +
    +$redis->zAdd('k1', 0, 'val0');
    +$redis->zAdd('k1', 1, 'val1');
    +$redis->zAdd('k1', 3, 'val3');
    +
    +$redis->zAdd('k2', 5, 'val1');
    +
    +$redis->zAdd('k3', 5, 'val0');
    +$redis->zAdd('k3', 3, 'val4');
    +
    +$redis->zdiffstore('ko1', ['k1', 'k2']); 		   /* 2, 'ko1' => ['val0', 'val3'] */
    +$redis->zdiffstore('ko2', ['k2', 'k1']); 			 /* 0, 'ko2' => [] */
    +
    +$redis->zdiffstore('ko3', ['k1', 'k2', 'k3']); /* 1, 'ko3' => ['val3'] */
    +$redis->zdiffstore('ko4', ['k3', 'k2', 'k1']); /* 1, 'k04' => ['val4'] */
    +~~~
    +
     ### zIncrBy
     -----
     _**Description**_: Increments the score of a member from a sorted set by a given amount.
    @@ -2945,12 +2989,48 @@ $redis->zIncrBy('key', 2.5, 'member1'); /* key or member1 didn't exist, so membe
     $redis->zIncrBy('key', 1, 'member1'); /* 3.5 */
     ~~~
     
    -### zinterstore, zInter
    +### zInter
     -----
    -_**Description**_: Creates an intersection of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument.
    +_**Description**_: Creates an intersection of sorted sets given in first argument. The result of the intersection will be returned.
    +
    +The second optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation.
    +The third argument is a set of options.  It can define the `AGGREGATE` option which specify how the results of the intersection are aggregated.  It can also define `WITHSCORES` so that the scores are returned as well.
    +
    +##### *Parameters*
    +*arrayZSetKeys*  
    +*arrayWeights*  
    +*arrayOptions* Two options are available: `withscores => TRUE`, and `aggregate => $behaviour`.  Either "SUM", "MIN", or "MAX" defines the behaviour to use on duplicate entries during the zinter.
    +
    +##### *Return value*
    +*ARRAY* The result of the intersection of sets.
    +
    +##### *Example*
    +~~~php
    +$redis->del('k1');
    +$redis->del('k2');
    +$redis->del('k3');
    +
    +$redis->zAdd('k1', 0, 'val0');
    +$redis->zAdd('k1', 1, 'val1');
    +$redis->zAdd('k1', 3, 'val3');
    +
    +$redis->zAdd('k2', 5, 'val1');
    +$redis->zAdd('k2', 3, 'val3');
    +
    +$redis->zinter(['k1', 'k2']); 				/* ['val1', 'val3'] */
    +$redis->zinter(['k1', 'k2'], [1, 1]); /* ['val1', 'val3'] */
    +
    +/* Weighted zinter */
    +$redis->zinter(['k1', 'k2'], [1, 5], 'min'); /* ['val1', 'val3'] */
    +$redis->zinter(['k1', 'k2'], [1, 5], 'max'); /* ['val3', 'val1'] */
    +~~~
    +
    +### zinterstore
    +-----
    +_**Description**_: Creates an intersection of sorted sets given in second argument. The result of the intersection will be stored in the sorted set defined by the first argument.
     
     The third optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation.
    -The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated.
    +The forth argument defines the `AGGREGATE` option which specify how the results of the intersection are aggregated.
     
     ##### *Parameters*
     *keyOutput*  
    @@ -2976,18 +3056,35 @@ $redis->zAdd('k1', 0, 'val0');
     $redis->zAdd('k1', 1, 'val1');
     $redis->zAdd('k1', 3, 'val3');
     
    -$redis->zAdd('k2', 2, 'val1');
    +$redis->zAdd('k2', 5, 'val1');
     $redis->zAdd('k2', 3, 'val3');
     
     $redis->zinterstore('ko1', ['k1', 'k2']); 				/* 2, 'ko1' => ['val1', 'val3'] */
    -$redis->zinterstore('ko2', ['k1', 'k2'], [1, 1]); 	/* 2, 'ko2' => ['val1', 'val3'] */
    +$redis->zinterstore('ko2', ['k1', 'k2'], [1, 1]); /* 2, 'ko2' => ['val1', 'val3'] */
     
     /* Weighted zinterstore */
     $redis->zinterstore('ko3', ['k1', 'k2'], [1, 5], 'min'); /* 2, 'ko3' => ['val1', 'val3'] */
     $redis->zinterstore('ko4', ['k1', 'k2'], [1, 5], 'max'); /* 2, 'ko4' => ['val3', 'val1'] */
     ~~~
     
    -**Note:** `zInter` is an alias for `zinterstore` and will be removed in future versions of phpredis.
    +### zMscore
    +-----
    +_**Description**_: Returns the scores of the given members in the specified sorted set.
    +
    +##### *Parameters*
    +*key*  
    +*members*: member1, member2, ... , memberN: Any number of members in the specified sorted set.
    +
    +##### *Return value*
    +*ARRAY* or *FALSE* when the key is not found.  Array entries corresponding to members that do not exist will be `false`.
    +
    +##### *Example*
    +~~~php
    +$redis->zAdd('key', 2.5, 'val2');
    +$redis->zAdd('key', 4.5, 'val4');
    +
    +$redis->zMscore('key', 'val2', 'val3', 'val4'); /* [2.5, false, 4.5] */
    +~~~
     
     ### zPop
     -----
    @@ -3115,7 +3212,7 @@ $redis->zRevRank('key', 'one'); /* 1 */
     $redis->zRevRank('key', 'two'); /* 0 */
     ~~~
     
    -### zRem, zDelete, zRemove
    +### zRem
     -----
     _**Description**_: Delete one or more members from a sorted set.
     
    @@ -3133,9 +3230,7 @@ $redis->zAdd('key', 0, 'val0', 1, 'val1', 2, 'val2');
     $redis->zRem('key', 'val0', 'val1', 'val2'); // Returns: 3
     ~~~
     
    -**Note:** `zDelete` and `zRemove` are an alias for `zRem` and will be removed in future versions of phpredis.
    -
    -### zRemRangeByRank, zDeleteRangeByRank
    +### zRemRangeByRank
     -----
     _**Description**_: Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end].
     
    @@ -3156,9 +3251,7 @@ $redis->zRemRangeByRank('key', 0, 1); /* 2 */
     $redis->zRange('key', 0, -1, ['withscores' => TRUE]); /* ['three' => 3] */
     ~~~
     
    -**Note:** `zDeleteRangeByRank` is an alias for `zRemRangeByRank` and will be removed in future versions of phpredis.
    -
    -### zRemRangeByScore, zDeleteRangeByScore, zRemoveRangeByScore
    +### zRemRangeByScore
     -----
     _**Description**_: Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end].
     
    @@ -3178,8 +3271,6 @@ $redis->zAdd('key', 10, 'val10');
     $redis->zRemRangeByScore('key', 0, 3); /* 2 */
     ~~~
     
    -**Note:** `zDeleteRangeByScore` and `zRemoveRangeByScore` are an alias for `zRemRangeByScore` and will be removed in future versions of phpredis.
    -
     ### zRevRange
     -----
     _**Description**_: Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpreted as zero-based indices:  
    @@ -3223,7 +3314,41 @@ $redis->zAdd('key', 2.5, 'val2');
     $redis->zScore('key', 'val2'); /* 2.5 */
     ~~~
     
    -### zunionstore, zUnion
    +### zUnion
    +-----
    +_**Description**_: Creates an union of sorted sets given in first argument. The result of the union will be returned.
    +
    +The second optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation.
    +The third argument is a set of options.  It can define the `AGGREGATE` option which specify how the results of the intersection are aggregated.  It can also define `WITHSCORES` so that the scores are returned as well.
    +
    +##### *Parameters*
    +*arrayZSetKeys*  
    +*arrayWeights*  
    +*arrayOptions* Two options are available: `withscores => TRUE`, and `aggregate => $behaviour`.  Either "SUM", "MIN", or "MAX" defines the behaviour to use on duplicate entries during the zunion.
    +
    +##### *Return value*
    +*ARRAY* The result of the union of sets.
    +
    +##### *Example*
    +~~~php
    +$redis->del('k1');
    +$redis->del('k2');
    +$redis->del('k3');
    +
    +$redis->zAdd('k1', 0, 'val0');
    +$redis->zAdd('k1', 1, 'val1');
    +
    +$redis->zAdd('k2', 2, 'val2');
    +$redis->zAdd('k2', 3, 'val3');
    +
    +$redis->zunion(['k1', 'k2']); /* ['val0', 'val1', 'val2', 'val3'] */
    +
    +/* Weighted zunion */
    +$redis->zunion(['k1', 'k2'], [1, 1]); /* ['val0', 'val1', 'val2', 'val3'] */
    +$redis->zunion(['k1', 'k2'], [5, 1]); /* ['val0', 'val2', 'val3', 'val1'] */
    +~~~
    +
    +### zunionstore
     -----
     _**Description**_: Creates an union of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument.
     
    @@ -3261,8 +3386,6 @@ $redis->zunionstore('ko2', ['k1', 'k2'], [1, 1]); /* 4, 'ko2' => ['val0', 'val1'
     $redis->zunionstore('ko3', ['k1', 'k2'], [5, 1]); /* 4, 'ko3' => ['val0', 'val2', 'val3', 'val1'] */
     ~~~
     
    -**Note:** `zUnion` is an alias for `zunionstore` and will be removed in future versions of phpredis.
    -
     ### zScan
     -----
     _**Description**_: Scan a sorted set for members, with optional pattern and count
    diff --git a/serialize.list b/serialize.list
    index d0971e287a..ecb92ac1bb 100644
    --- a/serialize.list
    +++ b/serialize.list
    @@ -5,9 +5,9 @@ This file lists which methods support serialization. Only indented methods have
     	setex
     	setnx
     	getSet
    -	getMultiple
    +	mGet
     append
    -substr
    +getRange
     strlen
     	lPush
     	lPushx
    @@ -17,19 +17,19 @@ strlen
     	rPop
     	blPop
     	brPop
    -	lRemove
    -	lGet
    -	lGetRange
    +	lRange
    +	lRem
    +	lIndex
     	lSet
     	lInsert
     
     	sAdd
    -	sRemove
    +	sRem
     	sMove
    -	sContains
    +	sIsMember
     
     	zAdd
    -	zDelete
    +	zRem
     	zScore
     	zRank
     	zRevRank
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 74e0ffc3ac..5ed3a64e9b 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -716,7 +716,7 @@ public function testMultipleBin() {
                                 $this->redis->mget(array_keys($kvals)));
         }
     
    -    public function testSetTimeout() {
    +    public function testExpire() {
             $this->redis->del('key');
             $this->redis->set('key', 'value');
     
    @@ -1289,7 +1289,7 @@ public function testlPos() {
             }
         }
     
    -    // ltrim, lsize, lpop
    +    // ltrim, lLen, lpop
         public function testltrim() {
             $this->redis->del('list');
     
    @@ -4049,13 +4049,13 @@ protected function differentType($mode) {
             $this->assertFalse($ret[$i++]); // ltrim
             $this->assertFalse($ret[$i++]); // lindex
             $this->assertFalse($ret[$i++]); // lset
    -        $this->assertFalse($ret[$i++]); // lremove
    +        $this->assertFalse($ret[$i++]); // lrem
             $this->assertFalse($ret[$i++]); // lpop
             $this->assertFalse($ret[$i++]); // rpop
             $this->assertFalse($ret[$i++]); // rpoplush
     
             $this->assertFalse($ret[$i++]); // sadd
    -        $this->assertFalse($ret[$i++]); // sremove
    +        $this->assertFalse($ret[$i++]); // srem
             $this->assertFalse($ret[$i++]); // spop
             $this->assertFalse($ret[$i++]); // smove
             $this->assertFalse($ret[$i++]); // scard
    @@ -4171,7 +4171,7 @@ protected function differentType($mode) {
             $this->assertFalse($ret[$i++]); // decrBy
     
             $this->assertFalse($ret[$i++]); // sadd
    -        $this->assertFalse($ret[$i++]); // sremove
    +        $this->assertFalse($ret[$i++]); // srem
             $this->assertFalse($ret[$i++]); // spop
             $this->assertFalse($ret[$i++]); // smove
             $this->assertFalse($ret[$i++]); // scard
    @@ -4295,7 +4295,7 @@ protected function differentType($mode) {
             $this->assertFalse($ret[$i++]); // ltrim
             $this->assertFalse($ret[$i++]); // lindex
             $this->assertFalse($ret[$i++]); // lset
    -        $this->assertFalse($ret[$i++]); // lremove
    +        $this->assertFalse($ret[$i++]); // lrem
             $this->assertFalse($ret[$i++]); // lpop
             $this->assertFalse($ret[$i++]); // rpop
             $this->assertFalse($ret[$i++]); // rpoplush
    @@ -4411,13 +4411,13 @@ protected function differentType($mode) {
             $this->assertFalse($ret[$i++]); // ltrim
             $this->assertFalse($ret[$i++]); // lindex
             $this->assertFalse($ret[$i++]); // lset
    -        $this->assertFalse($ret[$i++]); // lremove
    +        $this->assertFalse($ret[$i++]); // lrem
             $this->assertFalse($ret[$i++]); // lpop
             $this->assertFalse($ret[$i++]); // rpop
             $this->assertFalse($ret[$i++]); // rpoplush
     
             $this->assertFalse($ret[$i++]); // sadd
    -        $this->assertFalse($ret[$i++]); // sremove
    +        $this->assertFalse($ret[$i++]); // srem
             $this->assertFalse($ret[$i++]); // spop
             $this->assertFalse($ret[$i++]); // smove
             $this->assertFalse($ret[$i++]); // scard
    @@ -4527,13 +4527,13 @@ protected function differentType($mode) {
             $this->assertFalse($ret[$i++]); // ltrim
             $this->assertFalse($ret[$i++]); // lindex
             $this->assertFalse($ret[$i++]); // lset
    -        $this->assertFalse($ret[$i++]); // lremove
    +        $this->assertFalse($ret[$i++]); // lrem
             $this->assertFalse($ret[$i++]); // lpop
             $this->assertFalse($ret[$i++]); // rpop
             $this->assertFalse($ret[$i++]); // rpoplush
     
             $this->assertFalse($ret[$i++]); // sadd
    -        $this->assertFalse($ret[$i++]); // sremove
    +        $this->assertFalse($ret[$i++]); // srem
             $this->assertFalse($ret[$i++]); // spop
             $this->assertFalse($ret[$i++]); // smove
             $this->assertFalse($ret[$i++]); // scard
    @@ -5099,7 +5099,7 @@ private function checkSerializer($mode) {
                 $this->assertEquals($a[$k], $v);
             }
     
    -        // getMultiple
    +        // mGet
             $this->redis->set('a', NULL);
             $this->redis->set('b', FALSE);
             $this->redis->set('c', 42);
    
    From 99f9fd8353810c9d65d24217d2e5d1b1d52682cd Mon Sep 17 00:00:00 2001
    From: Michael Grunder 
    Date: Sat, 13 Jul 2024 22:42:25 -0700
    Subject: [PATCH 1899/1986] Fix HRANDFIELD command when WITHVALUES is used.
     (#2524)
    
    Redis requires the user to send a count if `WITHVALUES` is specified,
    otherwise it sees the `WITHVALUES` argument as the count and will error
    out that it's not a number.
    
    We can also return false if the key doesn't exist.
    ---
     redis.stub.php         |  2 +-
     redis_arginfo.h        |  9 ++++++---
     redis_commands.c       |  8 ++++++++
     redis_legacy_arginfo.h |  2 +-
     tests/RedisTest.php    | 10 ++++++++++
     5 files changed, 26 insertions(+), 5 deletions(-)
    
    diff --git a/redis.stub.php b/redis.stub.php
    index 79f8132593..920d003cf0 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1798,7 +1798,7 @@ public function hMset(string $key, array $fieldvals): Redis|bool;
          * @example $redis->hrandfield('settings');
          * @example $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);
          */
    -    public function hRandField(string $key, ?array $options = null): Redis|string|array;
    +    public function hRandField(string $key, ?array $options = null): Redis|string|array|false;
     
         public function hSet(string $key, string $member, mixed $value): Redis|int|false;
     
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index c4ebf5c261..c10c909323 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 04fe88bbcc4d3dc3be06385e8931dfb080442f23 */
    + * Stub hash: 70b942571cb2e3ef0b2531492840d9207f693b00 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -434,7 +434,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMset, 0, 2, Red
     	ZEND_ARG_TYPE_INFO(0, fieldvals, IS_ARRAY, 0)
     ZEND_END_ARG_INFO()
     
    -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY)
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
     ZEND_END_ARG_INFO()
    @@ -1069,7 +1069,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zrangestore, 0,
     	ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null")
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRandMember, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    +ZEND_END_ARG_INFO()
     
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRank, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    diff --git a/redis_commands.c b/redis_commands.c
    index bf0b4c4b77..85f56b2597 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -3577,10 +3577,18 @@ redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                     } else if (zend_string_equals_literal_ci(zkey, "withvalues")) {
                         withvalues = zval_is_true(z_ele);
                     }
    +            } else if (Z_TYPE_P(z_ele) == IS_STRING) {
    +                if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "WITHVALUES")) {
    +                    withvalues = 1;
    +                }
                 }
             } ZEND_HASH_FOREACH_END();
         }
     
    +    /* If we're sending WITHVALUES we must also send a count */
    +    if (count == 0 && withvalues)
    +        count = 1;
    +
         REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (count != 0) + withvalues, "HRANDFIELD");
         redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot);
     
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index e29ce73322..6e36a070a5 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 04fe88bbcc4d3dc3be06385e8931dfb080442f23 */
    + * Stub hash: 70b942571cb2e3ef0b2531492840d9207f693b00 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 5ed3a64e9b..6d4c2ab821 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -3255,6 +3255,16 @@ public function testHRandField() {
             $result = $this->redis->hRandField('key', ['count' => 2, 'withvalues' => true]);
             $this->assertEquals(2, count($result));
             $this->assertEquals(array_intersect_key($result, ['a' => 0, 'b' => 1, 'c' => 'foo', 'd' => 'bar', 'e' => null]), $result);
    +
    +        /* Make sure PhpRedis sends COUNt (1) when `WITHVALUES` is set */
    +        $result = $this->redis->hRandField('key', ['withvalues' => true]);
    +        $this->assertNull($this->redis->getLastError());
    +        $this->assertIsArray($result);
    +        $this->assertEquals(1, count($result));
    +
    +        /* We can return false if the key doesn't exist */
    +        $this->assertIsInt($this->redis->del('notahash'));
    +        $this->assertFalse($this->redis->hRandField('notahash'));
         }
     
         public function testSetRange() {
    
    From 6673b5b2bed7f50600aad0bf02afd49110a49d81 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sat, 13 Jul 2024 22:43:51 -0700
    Subject: [PATCH 1900/1986] SRANDMEMBER can return any type because of
     serialization.
    
    ---
     redis.stub.php         |  2 +-
     redis_arginfo.h        |  7 +++++--
     redis_legacy_arginfo.h |  2 +-
     tests/RedisTest.php    |  7 +++++++
     tests/TestSuite.php    | 20 ++++++++++++++++++++
     5 files changed, 34 insertions(+), 4 deletions(-)
    
    diff --git a/redis.stub.php b/redis.stub.php
    index 920d003cf0..ec88a17191 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -2826,7 +2826,7 @@ public function sPop(string $key, int $count = 0): Redis|string|array|false;
          * @example $redis->sRandMember('myset', 10);
          * @example $redis->sRandMember('myset', -10);
          */
    -    public function sRandMember(string $key, int $count = 0): Redis|string|array|false;
    +    public function sRandMember(string $key, int $count = 0): mixed;
     
         /**
          * Returns the union of one or more Redis SET keys.
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index c10c909323..a2ac457b30 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 70b942571cb2e3ef0b2531492840d9207f693b00 */
    + * Stub hash: a888154a03dc0edbe479e0226f012a34c7cb4100 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -750,7 +750,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sPop, 0, 1, Redi
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_Redis_sRandMember arginfo_class_Redis_sPop
    +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sRandMember, 0, 1, IS_MIXED, 0)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
    +ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_sUnion arginfo_class_Redis_sDiff
     
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 6e36a070a5..152b9b297d 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 70b942571cb2e3ef0b2531492840d9207f693b00 */
    + * Stub hash: a888154a03dc0edbe479e0226f012a34c7cb4100 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 6d4c2ab821..7479204d2f 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -1697,6 +1697,13 @@ public function testsRandMember() {
                 $this->assertInArray($reply_mem, $mems);
             }
     
    +        /* Ensure we can handle basically any return type */
    +        foreach ([3.1415, new stdClass(), 42, 'hello', NULL] as $val) {
    +            $this->assertEquals(1, $this->redis->del('set0'));
    +            $this->assertEquals(1, $this->redis->sadd('set0', $val));
    +            $this->assertSameType($val, $this->redis->srandmember('set0'));
    +        }
    +
             $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
         }
     
    diff --git a/tests/TestSuite.php b/tests/TestSuite.php
    index 2e156c72cb..410fa0e298 100644
    --- a/tests/TestSuite.php
    +++ b/tests/TestSuite.php
    @@ -237,6 +237,15 @@ protected function assertIsInt($v): bool {
             return false;
         }
     
    +    protected function assertIsFloat($v): bool {
    +        if (is_float($v))
    +            return true;
    +
    +        self::$errors []= $this->assertionTrace("%s is not a float", $this->printArg($v));
    +
    +        return false;
    +    }
    +
         protected function assertIsObject($v, ?string $type = NULL): bool {
             if ( ! is_object($v)) {
                 self::$errors []= $this->assertionTrace("%s is not an object", $this->printArg($v));
    @@ -250,6 +259,17 @@ protected function assertIsObject($v, ?string $type = NULL): bool {
             return true;
         }
     
    +    protected function assertSameType($expected, $actual): bool {
    +        if (gettype($expected) === gettype($actual))
    +            return true;
    +
    +        self::$errors []= $this->assertionTrace("%s is not the same type as %s",
    +                                                $this->printArg($actual),
    +                                                $this->printArg($expected));
    +
    +        return false;
    +    }
    +
         protected function assertIsArray($v, ?int $size = null): bool {
             if ( ! is_array($v)) {
                 self::$errors []= $this->assertionTrace("%s is not an array", $this->printArg($v));
    
    From 6ea5b3e08bdbf8cbe93e0dc56b18e8316d65097c Mon Sep 17 00:00:00 2001
    From: =?UTF-8?q?Viktor=20Djupsj=C3=B6backa?=
     
    Date: Wed, 17 Jul 2024 15:10:41 +0300
    Subject: [PATCH 1901/1986] Fix argument count issue in HSET with associative
     array, update method signature for HSET and add documentation
    
    ---
     redis.stub.php         | 16 +++++++++++++++-
     redis_arginfo.h        |  7 +++----
     redis_commands.c       |  2 +-
     redis_legacy_arginfo.h |  7 +++----
     tests/RedisTest.php    | 18 ++++++++++++++++++
     5 files changed, 40 insertions(+), 10 deletions(-)
    
    diff --git a/redis.stub.php b/redis.stub.php
    index ec88a17191..68ac8fd7dd 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1800,7 +1800,21 @@ public function hMset(string $key, array $fieldvals): Redis|bool;
          */
         public function hRandField(string $key, ?array $options = null): Redis|string|array|false;
     
    -    public function hSet(string $key, string $member, mixed $value): Redis|int|false;
    +    /**
    +     * Add or update one or more hash fields and values.
    +     *
    +     * @param string $key             The hash to create/update.
    +     * @param mixed  $fields_and_vals Argument pairs of fields and values. Alternatively, an associative array with the
    +     *                                fields and their values.
    +     *
    +     * @return Redis|int|false The number of fields that were added, or false on failure.
    +     *
    +     * @see https://redis.io/commands/hset/
    +     *
    +     * @example $redis->hSet('player:1', 'name', 'Kim', 'score', 78);
    +     * @example $redis->hSet('player:1', ['name' => 'Kim', 'score' => 78]);
    +     */
    +    public function hSet(string $key, mixed ...$fields_and_vals): Redis|int|false;
     
         /**
          * Set a hash field and value, but only if that field does not exist
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index a2ac457b30..182a18518c 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: a888154a03dc0edbe479e0226f012a34c7cb4100 */
    + * Stub hash: 1cc5fe0df8dfa7d95f2bc45c2383132a68629f24 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -439,10 +439,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
     ZEND_END_ARG_INFO()
     
    -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSet, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSet, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    -	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
    -	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
    +	ZEND_ARG_VARIADIC_TYPE_INFO(0, fields_and_vals, IS_MIXED, 0)
     ZEND_END_ARG_INFO()
     
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSetNx, 0, 3, Redis, MAY_BE_BOOL)
    diff --git a/redis_commands.c b/redis_commands.c
    index 85f56b2597..68572efc08 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -3481,7 +3481,7 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             }
     
             /* Initialize our command */
    -        redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL(z_args[1])), ZEND_STRL("HSET"));
    +        redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL(z_args[1])) * 2, ZEND_STRL("HSET"));
     
             /* Append key */
             zkey = zval_get_string(&z_args[0]);
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 152b9b297d..524aa5ad93 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: a888154a03dc0edbe479e0226f012a34c7cb4100 */
    + * Stub hash: 1cc5fe0df8dfa7d95f2bc45c2383132a68629f24 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    @@ -395,10 +395,9 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_hRandField arginfo_class_Redis_getEx
     
    -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hSet, 0, 0, 3)
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hSet, 0, 0, 1)
     	ZEND_ARG_INFO(0, key)
    -	ZEND_ARG_INFO(0, member)
    -	ZEND_ARG_INFO(0, value)
    +	ZEND_ARG_VARIADIC_INFO(0, fields_and_vals)
     ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_hSetNx arginfo_class_Redis_hIncrBy
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 7479204d2f..6bf0655939 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -3237,6 +3237,24 @@ public function testHashes() {
             $this->assertEquals('Object', $h1['z']);
             $this->assertEquals('', $h1['t']);
     
    +        // hset with fields + values as an associative array
    +        if (version_compare($this->version, '4.0.0') >= 0) {
    +            $this->redis->del('h');
    +            $this->assertEquals(3, $this->redis->hSet('h', ['x' => 123, 'y' => 456, 'z' => 'abc']));
    +            $this->assertEquals(['x' => '123', 'y' => '456', 'z' => 'abc'], $this->redis->hGetAll('h'));
    +            $this->assertEquals(0, $this->redis->hSet('h', ['x' => 789]));
    +            $this->assertEquals(['x' => '789', 'y' => '456', 'z' => 'abc'], $this->redis->hGetAll('h'));
    +        }
    +
    +        // hset with variadic fields + values
    +        if (version_compare($this->version, '4.0.0') >= 0) {
    +            $this->redis->del('h');
    +            $this->assertEquals(3, $this->redis->hSet('h', 'x', 123, 'y', 456, 'z', 'abc'));
    +            $this->assertEquals(['x' => '123', 'y' => '456', 'z' => 'abc'], $this->redis->hGetAll('h'));
    +            $this->assertEquals(0, $this->redis->hSet('h', 'x', 789));
    +            $this->assertEquals(['x' => '789', 'y' => '456', 'z' => 'abc'], $this->redis->hGetAll('h'));
    +        }
    +
             // hstrlen
             if (version_compare($this->version, '3.2.0') >= 0) {
                 $this->redis->del('h');
    
    From ff3d5e3e0661ef20baeb145a64e896b4c5952884 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Tue, 30 Jul 2024 09:52:54 -0700
    Subject: [PATCH 1902/1986] Prepare to tag 6.1.0RC1
    
    ---
     CHANGELOG.md | 306 +++++++++++++++++++++++++++++++++++++++++++++++++--
     package.xml  | 255 +++++++++++++++++++++++++++++++++++++-----
     php_redis.h  |   2 +-
     3 files changed, 525 insertions(+), 38 deletions(-)
    
    diff --git a/CHANGELOG.md b/CHANGELOG.md
    index 8feb1cea0f..95baede0e7 100644
    --- a/CHANGELOG.md
    +++ b/CHANGELOG.md
    @@ -5,20 +5,308 @@ All changes to phpredis will be documented in this file.
     We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
     and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
     
    -## [Unreleased]
    +## [6.1.0RC1] - 2024-08-04 ([GitHub](https://github.com/phpredis/phpredis/releases/6.1.0RC1), [PECL](https://pecl.php.net/package/redis/6.1.0RC1))
     
     ### Sponsors :sparkling_heart:
     
    -- [Audiomack](https://audiomack.com)
    +- [A-VISION](https://github.com/A-VISION-BV)
     - [Open LMS](https://openlms.net/)
    -- [BlueHost](https://bluehost.com)
    -- [Object Cache Pro for WordPress](https://objectcache.pro/)
     - [Avtandil Kikabidze](https://github.com/akalongman)
    -- [Zaher Ghaibeh](https://github.com/zaherg)
    -- [BatchLabs](https://batch.com)
    -- [Stackhero](https://github.com/stackhero-io)
    -- [Florian Levis](https://github.com/Gounlaf)
    -- [Luis Zárate](https://github.com/jlzaratec)
    +- [Ty Karok](https://github.com/karock)
    +- [Object Cache Pro for WordPress](https://objectcache.pro/)
    +
    +### Contributors to this release :sparkling_heart:
    +
    +  @michael-grunder, @yatsukhnenko, @bitactive, @OrangeJuiced, @crocodele,
    +  @kalifg, @divinity76, @PlavorSeol, @kjoe, @tstarling, @acorncom, @tuxmartin,
    +  @BenMorel, @szepeviktor, @SplotyCode, @taka-oyama, @PROFeNoM, @woodongwong,
    +  @RobiNN1, @vtsykun, @solracsf, @tillkruss, @deiga, @tutuna
    +
    +### Fixed
    +
    +- Fix random connection timeouts with Redis Cluster.
    +  [eb7f31e7](https://github.com/phpredis/phpredis/commit/eb7f31e7)
    +  ([Jozsef Koszo](https://github.com/kjoe))
    +  [#1142](https://github.com/phpredis/phpredis/pull/1142)
    +  [#1385](https://github.com/phpredis/phpredis/pull/1385)
    +  [#1633](https://github.com/phpredis/phpredis/pull/1633)
    +  [#1707](https://github.com/phpredis/phpredis/pull/1707)
    +  [#1811](https://github.com/phpredis/phpredis/pull/1811)
    +  [#2407](https://github.com/phpredis/phpredis/pull/2407)
    +- Fix argument count issue in HSET with associative array
    +  [6ea5b3e0](https://github.com/phpredis/phpredis/commit/6ea5b3e0)
    +  ([Viktor Djupsjöbacka](https://github.com/crocodele))
    +- SRANDMEMBER can return any type because of serialization.
    +  [6673b5b2](https://github.com/phpredis/phpredis/commit/6673b5b2)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Fix HRANDFIELD command when WITHVALUES is used.
    +  [99f9fd83](https://github.com/phpredis/phpredis/commit/99f9fd83)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +  [#2524](https://github.com/phpredis/phpredis/pull/2524)
    +- Allow context array to be nullable
    +  [50529f56](https://github.com/phpredis/phpredis/commit/50529f56)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +  [#2521](https://github.com/phpredis/phpredis/pull/2521)
    +- Fix a macOS (M1) compiler warning.
    +  [7de29d57](https://github.com/phpredis/phpredis/commit/7de29d57)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- `GETEX` documentation/updates and implentation in `RedisCluster`
    +  [981c6931](https://github.com/phpredis/phpredis/commit/981c6931)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +  [#2512](https://github.com/phpredis/phpredis/pull/2512)
    +- Refactor redis_script_cmd and fix to `flush` subcommand.
    +  [7c551424](https://github.com/phpredis/phpredis/commit/7c551424)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Update liveness check and fix PHP 8.4 compilation error.
    +  [c139de3a](https://github.com/phpredis/phpredis/commit/c139de3a)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Rework how we declare ZSTD min/max constants.
    +  [34b5bd81](https://github.com/phpredis/phpredis/commit/34b5bd81)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +  [#2487](https://github.com/phpredis/phpredis/pull/2487)
    +- Fix memory leak if we fail in ps_open_redis.
    +  [0e926165](https://github.com/phpredis/phpredis/commit/0e926165)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Fix segfault and remove redundant macros
    +  [a9e53fd1](https://github.com/phpredis/phpredis/commit/a9e53fd1)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Fix PHP 8.4 includes
    +  [a51215ce](https://github.com/phpredis/phpredis/commit/a51215ce)
    +  [#2463](https://github.com/phpredis/phpredis/pull/2463)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Handle arbitrarily large `SCAN` cursors properly.
    +  [2612d444](https://github.com/phpredis/phpredis/commit/2612d444)
    +  [e52f0afa](https://github.com/phpredis/phpredis/commit/e52f0afa)
    +  [#2454](https://github.com/phpredis/phpredis/pull/2454)
    +  [#2458](https://github.com/phpredis/phpredis/pull/2458)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Improve warning when we encounter an invalid EXPIRY in SET
    +  [732e466a](https://github.com/phpredis/phpredis/commit/732e466a)
    +  [#2448](https://github.com/phpredis/phpredis/pull/2448)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Fix Arginfo / zpp mismatch for DUMP command
    +  [50e5405c](https://github.com/phpredis/phpredis/commit/50e5405c)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- RedisCluster::publish returns a cluster_long_resp
    +  [14f93339](https://github.com/phpredis/phpredis/commit/14f93339)
    +  ([Alexandre Choura](https://github.com/PROFeNoM))
    +- Fix segfault when passing just false to auth.
    +  [6dc0a0be](https://github.com/phpredis/phpredis/commit/6dc0a0be)
    +  [#2430](https://github.com/phpredis/phpredis/pull/2430)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- the VALUE argument type for hSetNx must be the same as for hSet
    +  [df074dbe](https://github.com/phpredis/phpredis/commit/df074dbe)
    +  ([Uładzimir Tsykun](https://github.com/vtsykun))
    +- Fix `PSUBSCRIBE` to find callback by pattern not string literal.
    +  [2f276dcd](https://github.com/phpredis/phpredis/commit/2f276dcd)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +  [#2395](https://github.com/phpredis/phpredis/pull/2395)
    +- Fix memory leak and segfault in Redis::exec
    +  [362e1141](https://github.com/phpredis/phpredis/commit/362e1141)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Fix unknown expiration modifier warning when null argument passed
    +  [264c0c7e](https://github.com/phpredis/phpredis/commit/264c0c7e)
    +  [3eb60f58](https://github.com/phpredis/phpredis/commit/3eb60f58)
    +  [#2388](https://github.com/phpredis/phpredis/pull/2388)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Other fixes
    +  [e18f6c6d](https://github.com/phpredis/phpredis/commit/e18f6c6d)
    +  [3d7be358](https://github.com/phpredis/phpredis/commit/3d7be358)
    +  [2b555c89](https://github.com/phpredis/phpredis/commit/2b555c89)
    +  [fa1a283a](https://github.com/phpredis/phpredis/commit/fa1a283a)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +  [37c5f8d4](https://github.com/phpredis/phpredis/commit/37c5f8d4)
    +  ([Viktor Szépe](https://github.com/szepeviktor))
    +
    +### Added
    +
    +- Compression support for PHP sessions.
    +  [da4ab0a7](https://github.com/phpredis/phpredis/commit/da4ab0a7)
    +  [#2473](https://github.com/phpredis/phpredis/pull/2473)
    +  ([bitactive](https://github.com/bitactive))
    +- Support for early_refresh in Redis sessions to match cluster behavior
    +  [b6989018](https://github.com/phpredis/phpredis/commit/b6989018)
    +  ([Bitactive](https://github.com/bitactive))
    +- Implement WAITAOF command.
    +  [ed7c9f6f](https://github.com/phpredis/phpredis/commit/ed7c9f6f)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +
    +### Removed
    +
    +- PHP 7.1, 7.2, and 7.3 CI jobs
    +  [d68c30f8](https://github.com/phpredis/phpredis/commit/d68c30f8)
    +  [dc39bd55](https://github.com/phpredis/phpredis/commit/dc39bd55)
    +  [#2478](https://github.com/phpredis/phpredis/pull/2478)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +
    +### Changed
    +
    +- Fix the time unit of retry_interval
    +  [3fdd52b4](https://github.com/phpredis/phpredis/commit/3fdd52b4)
    +  ([woodong](https://github.com/woodongwong))
    +
    +### Documentation
    +
    +- Many documentation fixes.
    +  [eeb51099](https://github.com/phpredis/phpredis/commit/eeb51099)
    +  ([Michael Dwyer](https://github.com/kalifg))
    +  [#2523](https://github.com/phpredis/phpredis/pull/2523)
    +- fix missing  tags
    +  [f865d5b9](https://github.com/phpredis/phpredis/commit/f865d5b9)
    +  ([divinity76](https://github.com/divinity76))
    +- Mention Valkey support
    +  [5f1eecfb](https://github.com/phpredis/phpredis/commit/5f1eecfb)
    +  ([PlavorSeol](https://github.com/PlavorSeol))
    +- Mention KeyDB support in README.md
    +  [37fa3592](https://github.com/phpredis/phpredis/commit/37fa3592)
    +  ([Tim Starling](https://github.com/tstarling))
    +- Remove mention of pickle
    +  [c7a73abb](https://github.com/phpredis/phpredis/commit/c7a73abb)
    +  ([David Baker](https://github.com/acorncom))
    +- Add session.save_path examples
    +  [8a39caeb](https://github.com/phpredis/phpredis/commit/8a39caeb)
    +  ([Martin Vancl](https://github.com/tuxmartin))
    +- Tighter return types for Redis::(keys|hKeys|hVals|hGetAll)()
    +  [77ab62bc](https://github.com/phpredis/phpredis/commit/77ab62bc)
    +  ([Benjamin Morel](https://github.com/BenMorel))
    +- Update stubs
    +  [4d233977](https://github.com/phpredis/phpredis/commit/4d233977)
    +  [ff305349](https://github.com/phpredis/phpredis/commit/ff305349)
    +  [12966a74](https://github.com/phpredis/phpredis/commit/12966a74)
    +  [a4a283ab](https://github.com/phpredis/phpredis/commit/a4a283ab)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +  [8f8ff72a](https://github.com/phpredis/phpredis/commit/8f8ff72a)
    +  ([Takayasu Oyama](https://github.com/taka-oyama))
    +  [5d293245](https://github.com/phpredis/phpredis/commit/5d293245)
    +  [95bd184b](https://github.com/phpredis/phpredis/commit/95bd184b)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Fix config.m4 when using custom dep paths
    +  [ece3f7be](https://github.com/phpredis/phpredis/commit/ece3f7be)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +  [#2453](https://github.com/phpredis/phpredis/pull/2453)
    +  [#2452](https://github.com/phpredis/phpredis/pull/2452)
    +- Fix retry_internal documentation
    +  [142c1f4a](https://github.com/phpredis/phpredis/commit/142c1f4a)
    +  ([SplotyCode](https://github.com/SplotyCode))
    +- Fix anchor link
    +  [9b5cad31](https://github.com/phpredis/phpredis/commit/9b5cad31)
    +  ([Git'Fellow](https://github.com/solracsf))
    +- Fix typo in link
    +  [bfd379f0](https://github.com/phpredis/phpredis/commit/bfd379f0)
    +  [#2349](https://github.com/phpredis/phpredis/pull/2349)
    +  ([deiga](https://github.com/deiga))
    +- Fix Fedora package url
    +  [60b1ba14](https://github.com/phpredis/phpredis/commit/60b1ba14)
    +  [717713e1](https://github.com/phpredis/phpredis/commit/717713e1)
    +  ([Dmitrii Kotov](https://github.com/tutunak))
    +- Update Redis Sentinel documentation to reflect changes to constructor in 6.0 release
    +  [dc05d65c](https://github.com/phpredis/phpredis/commit/dc05d65c)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +  [#2381](https://github.com/phpredis/phpredis/pull/2381)
    +- Add back old examples with note
    +  [1ad95b63](https://github.com/phpredis/phpredis/commit/1ad95b63)
    +  ([Joost](https://github.com/OrangeJuiced))
    +
    +### Tests/CI
    +
    +- Avoid fatal error in test execution.
    +  [57304970](https://github.com/phpredis/phpredis/commit/57304970)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +  [#2510](https://github.com/phpredis/phpredis/pull/2510)
    +- Refactor unit test framework.
    +  [b1771def](https://github.com/phpredis/phpredis/commit/b1771def)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +  [#2509](https://github.com/phpredis/phpredis/pull/2509)
    +- Get unit tests working in `php-cgi`.
    +  [b808cc60](https://github.com/phpredis/phpredis/commit/b808cc60)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +  [#2507](https://github.com/phpredis/phpredis/pull/2507)
    +- Switch to `ZEND_STRL` in more places.
    +  [7050c989](https://github.com/phpredis/phpredis/commit/7050c989)
    +  [f8c762e7](https://github.com/phpredis/phpredis/commit/f8c762e7)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +  [#2505](https://github.com/phpredis/phpredis/pull/2505)
    +- Workaround weird PHP compiler crash.
    +  [d3b2d87b](https://github.com/phpredis/phpredis/commit/d3b2d87b)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Refactor tests (formatting, modernization, etc).
    +  [dab6a62d](https://github.com/phpredis/phpredis/commit/dab6a62d)
    +  [c6cd665b](https://github.com/phpredis/phpredis/commit/c6cd665b)
    +  [78b70ca8](https://github.com/phpredis/phpredis/commit/78b70ca8)
    +  [3c125b09](https://github.com/phpredis/phpredis/commit/3c125b09)
    +  [18b0da72](https://github.com/phpredis/phpredis/commit/18b0da72)
    +  [b88e72b1](https://github.com/phpredis/phpredis/commit/b88e72b1)
    +  [#2492](https://github.com/phpredis/phpredis/pull/2492)
    +  [0f94d9c1](https://github.com/phpredis/phpredis/commit/0f94d9c1)
    +  [59965971](https://github.com/phpredis/phpredis/commit/59965971)
    +  [3dbc2bd8](https://github.com/phpredis/phpredis/commit/3dbc2bd8)
    +  [9b90c03b](https://github.com/phpredis/phpredis/commit/9b90c03b)
    +  [c0d6f042](https://github.com/phpredis/phpredis/commit/c0d6f042)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Spelling fixes
    +  [0d89e928](https://github.com/phpredis/phpredis/commit/0d89e928)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Added Valkey support.
    +  [f350dc34](https://github.com/phpredis/phpredis/commit/f350dc34)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Add a test for session compression.
    +  [9f3ca98c](https://github.com/phpredis/phpredis/commit/9f3ca98c)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +  [#2473](https://github.com/phpredis/phpredis/pull/2473)
    +  [#2480](https://github.com/phpredis/phpredis/pull/2480)
    +- Test against valkey
    +  [a819a44b](https://github.com/phpredis/phpredis/commit/a819a44b)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- sessionSaveHandler injection.
    +  [9f8f80ca](https://github.com/phpredis/phpredis/commit/9f8f80ca)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- KeyDB addiions
    +  [54d62c72](https://github.com/phpredis/phpredis/commit/54d62c72)
    +  [d9c48b78](https://github.com/phpredis/phpredis/commit/d9c48b78)
    +  [#2466](https://github.com/phpredis/phpredis/pull/2466)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Add PHP 8.3 to CI
    +  [78d15140](https://github.com/phpredis/phpredis/commit/78d15140)
    +  ([Róbert Kelčák](https://github.com/RobiNN1))
    +  [e051a5db](https://github.com/phpredis/phpredis/commit/e051a5db)
    +  [#2427](https://github.com/phpredis/phpredis/pull/2427)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Fix deprecation error when passing null to match_type parameter
    +  [b835aaa3](https://github.com/phpredis/phpredis/commit/b835aaa3)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Fix crash in `OBJECT` command in pipeline.
    +  [a7f51f70](https://github.com/phpredis/phpredis/commit/a7f51f70)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Use newInstance in RedisClusterTest
    +  [954fbab8](https://github.com/phpredis/phpredis/commit/954fbab8)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Use actions/checkout@v4
    +  [f4c2ac26](https://github.com/phpredis/phpredis/commit/f4c2ac26)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Cluster nodes from ENV
    +  [eda39958](https://github.com/phpredis/phpredis/commit/eda39958)
    +  [0672703b](https://github.com/phpredis/phpredis/commit/0672703b)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Ensure we're talking to redis-server in our high ports test.
    +  [7825efbc](https://github.com/phpredis/phpredis/commit/7825efbc)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Add missing option to installation example
    +  [2bddd84f](https://github.com/phpredis/phpredis/commit/2bddd84f)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +  [#2378](https://github.com/phpredis/phpredis/pull/2378)
    +- Update sentinel documentation to reflect changes to constructor in 6.0 release
    +  [849bedb6](https://github.com/phpredis/phpredis/commit/849bedb6)
    +  ([Joost](https://github.com/OrangeJuiced))
    +- Add missing option to example
    +  [3674d663](https://github.com/phpredis/phpredis/commit/3674d663)
    +  ([Till Krüss](https://github.com/tillkruss))
    +- Fix typo in link
    +  [8f6bc98f](https://github.com/phpredis/phpredis/commit/8f6bc98f)
    +  ([Timo Sand](https://github.com/deiga))
    +- Update tests to allow users to use a custom class.
    +  [5f6ce414](https://github.com/phpredis/phpredis/commit/5f6ce414)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +
     
     ## [6.0.2] - 2023-10-22 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.2), [PECL](https://pecl.php.net/package/redis/6.0.2))
     
    diff --git a/package.xml b/package.xml
    index e800ba8e5a..e352e51fbd 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -21,47 +21,128 @@ http://pear.php.net/dtd/package-2.0.xsd">
       p.yatsukhnenko@gmail.com
       yes
      
    - 
    -  Nicolas Favre-Felix
    -  nff
    -  n.favrefelix@gmail.com
    -  no
    - 
    - 2023-10-22
    + 2024-08-04
      
    -  6.0.2
    +  6.1.0RC1
       6.0.0
      
      
    -  stable
    -  stable
    +  beta
    +  beta
      
      PHP
      
    -    --- Sponsors ---
    +    Sponsors
     
         Audiomack - https://audiomack.com
         Open LMS - https://openlms.net
    -    BlueHost - https://bluehost.com
    -    Object Cache Pro for WordPress - https://objectcache.pro
         Avtandil Kikabidze - https://github.com/akalongman
    -    Zaher Ghaibeh - https://github.com/zaherg
    -    BatchLabs - https://batch.com
    -    Stackhero - https://github.com/stackhero-io
    -    Florian Levis - https://github.com/Gounlaf
    -    Luis Zarate - https://github.com/jlzaratec
    -
    -    ---
    -
    -    phpredis 6.0.2
    +    Ty Karok - https://github.com/karock
    +    Object Cache Pro for WordPress - https://objectcache.pro
     
    -    This release contains fixes for OBJECT, PSUBSCRIBE and SCAN commands.
    -    You can find a detailed list of changes in CHANGELOG.md and package.xml
    -    or by inspecting the git commit logs.
    +    Fixed:
     
    -    * Fix deprecation error when passing null to match_type parameter.[b835aaa3] (Pavlo Yatsukhnenko) 
    -    * Fix flaky test and OBJECT in a pipeline. [a7f51f70] (Michael Grunder)
    -    * Find our callback by pattern with PSUBSCRIBE [2f276dcd] (Michael Grunder)
    +    * Fix random connection timeouts with Redis Cluster. [eb7f31e7] (Jozsef Koszo)
    +    * Fix argument count issue in HSET with associative array [6ea5b3e0]
    +      (Viktor Djupsjobacka)
    +    * SRANDMEMBER can return any type because of serialization. [6673b5b2]
    +      (Michael Grunder)
    +    * Fix HRANDFIELD command when WITHVALUES is used. [99f9fd83] (Michael Grunder)
    +    * Allow context array to be nullable [50529f56] (Michael Grunder)
    +    * Fix a macOS (M1) compiler warning. [7de29d57] (Michael Grunder)
    +    * `GETEX` documentation/updates and implentation in `RedisCluster` [981c6931]
    +      (Michael Grunder)
    +    * Refactor redis_script_cmd and fix to `flush` subcommand. [7c551424]
    +      (Pavlo Yatsukhnenko)
    +    * Update liveness check and fix PHP 8.4 compilation error. [c139de3a]
    +      (Michael Grunder)
    +    * Rework how we declare ZSTD min/max constants. [34b5bd81] (Michael Grunder)
    +    * Fix memory leak if we fail in ps_open_redis. [0e926165] (Michael Grunder)
    +    * Fix segfault and remove redundant macros [a9e53fd1] (Pavlo Yatsukhnenko)
    +    * Fix PHP 8.4 includes [a51215ce] (Michael Grunder)
    +    * Handle arbitrarily large `SCAN` cursors properly. [2612d444, e52f0afa]
    +      (Michael Grunder)
    +    * Improve warning when we encounter an invalid EXPIRY in SET [732e466a]
    +      (Michael Grunder)
    +    * Fix Arginfo / zpp mismatch for DUMP command [50e5405c] (Pavlo Yatsukhnenko)
    +    * RedisCluster::publish returns a cluster_long_resp [14f93339] (Alexandre Choura)
    +    * Fix segfault when passing just false to auth. [6dc0a0be] (Michael Grunder)
    +    * the VALUE argument type for hSetNx must be the same as for hSet [df074dbe]
    +      (Uladzimir Tsykun)
    +    * Fix `PSUBSCRIBE` to find callback by pattern not string literal. [2f276dcd]
    +      (Michael Grunder)
    +    * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko)
    +    * Fix unknown expiration modifier warning when null argument passed [264c0c7e,
    +      3eb60f58] (Pavlo Yatsukhnenko)
    +    * Other fixes [e18f6c6d, 3d7be358, 2b555c89, fa1a283a, 37c5f8d4] (Michael Grunder, Viktor Szepe)
    +
    +    Added:
    +
    +    * Compression support for PHP sessions. [da4ab0a7] (bitactive)
    +    * Support for early_refresh in Redis sessions to match cluster behavior
    +      [b6989018] (Bitactive)
    +    * Implement WAITAOF command. [ed7c9f6f] (Michael Grunder)
    +
    +    Removed:
    +
    +    * PHP 7.1, 7.2, and 7.3 CI jobs [d68c30f8, dc39bd55] (Michael Grunder)
    +
    +    Changed:
    +
    +    * Fix the time unit of retry_interval [3fdd52b4] (woodong)
    +
    +    Documentation:
    +
    +    * Many documentation fixes. [eeb51099] (Michael Dwyer)
    +    * fix missing code tags [f865d5b9] (divinity76)
    +    * Mention Valkey support [5f1eecfb] (PlavorSeol)
    +    * Mention KeyDB support in README.md [37fa3592] (Tim Starling)
    +    * Remove mention of pickle [c7a73abb] (David Baker)
    +    * Add session.save_path examples [8a39caeb] (Martin Vancl)
    +    * Tighter return types for Redis::(keys|hKeys|hVals|hGetAll) [77ab62bc]
    +      (Benjamin Morel)
    +    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a, 5d293245,
    +      95bd184b] (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
    +    * Fix config.m4 when using custom dep paths [ece3f7be] (Michael Grunder)
    +    * Fix retry_internal documentation [142c1f4a] (SplotyCode)
    +    * Fix anchor link [9b5cad31] (Git'Fellow)
    +    * Fix typo in link [bfd379f0] (deiga)
    +    * Fix Fedora package url [60b1ba14, 717713e1] (Dmitrii Kotov)
    +    * Update Redis Sentinel documentation to reflect changes to constructor in 6.0
    +      release [dc05d65c] (Pavlo Yatsukhnenko)
    +    * Add back old examples with note [1ad95b63] (Joost)
    +
    +    Tests/CI:
    +
    +    * Avoid fatal error in test execution. [57304970] (Michael Grunder)
    +    * Refactor unit test framework. [b1771def] (Michael Grunder)
    +    * Get unit tests working in `php-cgi`. [b808cc60] (Michael Grunder)
    +    * Switch to `ZEND_STRL` in more places. [7050c989, f8c762e7] (Michael Grunder)
    +    * Workaround weird PHP compiler crash. [d3b2d87b] (Michael Grunder)
    +    * Refactor tests (formatting, modernization, etc). [dab6a62d, c6cd665b, 78b70ca8,
    +      3c125b09, 18b0da72, b88e72b1, 0f94d9c1, 59965971, 3dbc2bd8, 9b90c03b, c0d6f042]
    +      (Michael Grunder)
    +    * Spelling fixes [0d89e928] (Michael Grunder)
    +    * Added Valkey support. [f350dc34] (Michael Grunder)
    +    * Add a test for session compression. [9f3ca98c] (Michael Grunder)
    +    * Test against valkey [a819a44b] (Michael Grunder)
    +    * sessionSaveHandler injection. [9f8f80ca] (Pavlo Yatsukhnenko)
    +    * KeyDB addiions [54d62c72, d9c48b78] (Michael Grunder)
    +    * Add PHP 8.3 to CI [78d15140, e051a5db] (Robert Kelcak, Pavlo Yatsukhnenko)
    +    * Fix deprecation error when passing null to match_type parameter [b835aaa3]
    +      (Pavlo Yatsukhnenko)
    +    * Fix crash in `OBJECT` command in pipeline. [a7f51f70] (Michael Grunder)
    +    * Use newInstance in RedisClusterTest [954fbab8] (Pavlo Yatsukhnenko)
    +    * Use actions/checkout@v4 [f4c2ac26] (Pavlo Yatsukhnenko)
    +    * Cluster nodes from ENV [eda39958, 0672703b] (Pavlo Yatsukhnenko)
    +    * Ensure we're talking to redis-server in our high ports test. [7825efbc]
    +      (Michael Grunder)
    +    * Add missing option to installation example [2bddd84f] (Pavlo Yatsukhnenko)
    +    * Update sentinel documentation to reflect changes to constructor in 6.0 release
    +      [849bedb6] (Joost)
    +    * Add missing option to example [3674d663] (Till Kruss)
    +    * Fix typo in link [8f6bc98f] (Timo Sand)
    +    * Update tests to allow users to use a custom class. [5f6ce414] (Michael Grunder)
      
      
       
    @@ -153,6 +234,124 @@ http://pear.php.net/dtd/package-2.0.xsd">
       
      
      
    + 
    +   betabeta
    +   6.1.0RC16.0.0
    +   2024-08-04
    +   
    +    --- Sponsors ---
    +
    +    Audiomack - https://audiomack.com
    +    Open LMS - https://openlms.net
    +    Avtandil Kikabidze - https://github.com/akalongman
    +    Ty Karok - https://github.com/karock
    +    Object Cache Pro for WordPress - https://objectcache.pro
    +
    +    Fixed:
    +
    +    * Fix random connection timeouts with Redis Cluster. [eb7f31e7] (Jozsef Koszo)
    +    * Fix argument count issue in HSET with associative array [6ea5b3e0]
    +      (Viktor Djupsjobacka)
    +    * SRANDMEMBER can return any type because of serialization. [6673b5b2]
    +      (Michael Grunder)
    +    * Fix HRANDFIELD command when WITHVALUES is used. [99f9fd83] (Michael Grunder)
    +    * Allow context array to be nullable [50529f56] (Michael Grunder)
    +    * Fix a macOS (M1) compiler warning. [7de29d57] (Michael Grunder)
    +    * `GETEX` documentation/updates and implentation in `RedisCluster` [981c6931]
    +      (Michael Grunder)
    +    * Refactor redis_script_cmd and fix to `flush` subcommand. [7c551424]
    +      (Pavlo Yatsukhnenko)
    +    * Update liveness check and fix PHP 8.4 compilation error. [c139de3a]
    +      (Michael Grunder)
    +    * Rework how we declare ZSTD min/max constants. [34b5bd81] (Michael Grunder)
    +    * Fix memory leak if we fail in ps_open_redis. [0e926165] (Michael Grunder)
    +    * Fix segfault and remove redundant macros [a9e53fd1] (Pavlo Yatsukhnenko)
    +    * Fix PHP 8.4 includes [a51215ce] (Michael Grunder)
    +    * Handle arbitrarily large `SCAN` cursors properly. [2612d444, e52f0afa]
    +      (Michael Grunder)
    +    * Improve warning when we encounter an invalid EXPIRY in SET [732e466a]
    +      (Michael Grunder)
    +    * Fix Arginfo / zpp mismatch for DUMP command [50e5405c] (Pavlo Yatsukhnenko)
    +    * RedisCluster::publish returns a cluster_long_resp [14f93339] (Alexandre Choura)
    +    * Fix segfault when passing just false to auth. [6dc0a0be] (Michael Grunder)
    +    * the VALUE argument type for hSetNx must be the same as for hSet [df074dbe]
    +      (Uladzimir Tsykun)
    +    * Fix `PSUBSCRIBE` to find callback by pattern not string literal. [2f276dcd]
    +      (Michael Grunder)
    +    * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko)
    +    * Fix unknown expiration modifier warning when null argument passed [264c0c7e,
    +      3eb60f58] (Pavlo Yatsukhnenko)
    +    * Other fixes [e18f6c6d, 3d7be358, 2b555c89, fa1a283a, 37c5f8d4] (Michael Grunder, Viktor Szepe)
    +
    +    Added:
    +
    +    * Compression support for PHP sessions. [da4ab0a7] (bitactive)
    +    * Support for early_refresh in Redis sessions to match cluster behavior
    +      [b6989018] (Bitactive)
    +    * Implement WAITAOF command. [ed7c9f6f] (Michael Grunder)
    +
    +    Removed:
    +
    +    * PHP 7.1, 7.2, and 7.3 CI jobs [d68c30f8, dc39bd55] (Michael Grunder)
    +
    +    Changed:
    +
    +    * Fix the time unit of retry_interval [3fdd52b4] (woodong)
    +
    +    Documentation:
    +
    +    * Many documentation fixes. [eeb51099] (Michael Dwyer)
    +    * fix missing code tags [f865d5b9] (divinity76)
    +    * Mention Valkey support [5f1eecfb] (PlavorSeol)
    +    * Mention KeyDB support in README.md [37fa3592] (Tim Starling)
    +    * Remove mention of pickle [c7a73abb] (David Baker)
    +    * Add session.save_path examples [8a39caeb] (Martin Vancl)
    +    * Tighter return types for Redis::(keys|hKeys|hVals|hGetAll) [77ab62bc]
    +      (Benjamin Morel)
    +    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a, 5d293245,
    +      95bd184b] (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
    +    * Fix config.m4 when using custom dep paths [ece3f7be] (Michael Grunder)
    +    * Fix retry_internal documentation [142c1f4a] (SplotyCode)
    +    * Fix anchor link [9b5cad31] (Git'Fellow)
    +    * Fix typo in link [bfd379f0] (deiga)
    +    * Fix Fedora package url [60b1ba14, 717713e1] (Dmitrii Kotov)
    +    * Update Redis Sentinel documentation to reflect changes to constructor in 6.0
    +      release [dc05d65c] (Pavlo Yatsukhnenko)
    +    * Add back old examples with note [1ad95b63] (Joost)
    +
    +    Tests/CI:
    +
    +    * Avoid fatal error in test execution. [57304970] (Michael Grunder)
    +    * Refactor unit test framework. [b1771def] (Michael Grunder)
    +    * Get unit tests working in `php-cgi`. [b808cc60] (Michael Grunder)
    +    * Switch to `ZEND_STRL` in more places. [7050c989, f8c762e7] (Michael Grunder)
    +    * Workaround weird PHP compiler crash. [d3b2d87b] (Michael Grunder)
    +    * Refactor tests (formatting, modernization, etc). [dab6a62d, c6cd665b, 78b70ca8,
    +      3c125b09, 18b0da72, b88e72b1, 0f94d9c1, 59965971, 3dbc2bd8, 9b90c03b, c0d6f042]
    +      (Michael Grunder)
    +    * Spelling fixes [0d89e928] (Michael Grunder)
    +    * Added Valkey support. [f350dc34] (Michael Grunder)
    +    * Add a test for session compression. [9f3ca98c] (Michael Grunder)
    +    * Test against valkey [a819a44b] (Michael Grunder)
    +    * sessionSaveHandler injection. [9f8f80ca] (Pavlo Yatsukhnenko)
    +    * KeyDB addiions [54d62c72, d9c48b78] (Michael Grunder)
    +    * Add PHP 8.3 to CI [78d15140, e051a5db] (Robert Kelcak, Pavlo Yatsukhnenko)
    +    * Fix deprecation error when passing null to match_type parameter [b835aaa3]
    +      (Pavlo Yatsukhnenko)
    +    * Fix crash in `OBJECT` command in pipeline. [a7f51f70] (Michael Grunder)
    +    * Use newInstance in RedisClusterTest [954fbab8] (Pavlo Yatsukhnenko)
    +    * Use actions/checkout@v4 [f4c2ac26] (Pavlo Yatsukhnenko)
    +    * Cluster nodes from ENV [eda39958, 0672703b] (Pavlo Yatsukhnenko)
    +    * Ensure we're talking to redis-server in our high ports test. [7825efbc]
    +      (Michael Grunder)
    +    * Add missing option to installation example [2bddd84f] (Pavlo Yatsukhnenko)
    +    * Update sentinel documentation to reflect changes to constructor in 6.0 release
    +      [849bedb6] (Joost)
    +    * Add missing option to example [3674d663] (Till Kruss)
    +    * Fix typo in link [8f6bc98f] (Timo Sand)
    +    * Update tests to allow users to use a custom class. [5f6ce414] (Michael Grunder)
    +   
    + 
      
        stablestable
        6.0.26.0.0
    diff --git a/php_redis.h b/php_redis.h
    index 3375e418dc..232025ff5e 100644
    --- a/php_redis.h
    +++ b/php_redis.h
    @@ -23,7 +23,7 @@
     #define PHP_REDIS_H
     
     /* phpredis version */
    -#define PHP_REDIS_VERSION "6.0.3-dev"
    +#define PHP_REDIS_VERSION "6.1.0RC1"
     
     /* For convenience we store the salt as a printable hex string which requires 2
      * characters per byte + 1 for the NULL terminator */
    
    From e9474b80cb84f0505a3a05879ba844001366ea37 Mon Sep 17 00:00:00 2001
    From: Remi Collet 
    Date: Mon, 5 Aug 2024 08:43:01 +0200
    Subject: [PATCH 1903/1986] add missing SessionHelpers.php in pecl package
    
    ---
     package.xml | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/package.xml b/package.xml
    index e352e51fbd..43ec421c0f 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -209,6 +209,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
          
          
          
    +     
          
          
         
    
    From 8b519423570bd11da9ffdb2c08d040d15cf4f6c3 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 5 Aug 2024 00:47:05 -0700
    Subject: [PATCH 1904/1986] Raise minimum supported PHP version to 7.4
    
    See #2531
    ---
     package.xml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/package.xml b/package.xml
    index 43ec421c0f..9135ea8010 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -218,7 +218,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
      
       
        
    -    7.2.0
    +    7.4.0
        
        
         1.4.0b1
    
    From 37cebdd70b06df252baba1b94f1e7e2b12fccf23 Mon Sep 17 00:00:00 2001
    From: Remi Collet 
    Date: Mon, 5 Aug 2024 15:08:58 +0200
    Subject: [PATCH 1905/1986] cleanup code for unsupported versions
    
    ---
     backoff.c       | 6 ------
     library.c       | 8 --------
     redis_session.c | 4 ----
     3 files changed, 18 deletions(-)
    
    diff --git a/backoff.c b/backoff.c
    index 1be04a8fe8..e795cb9405 100644
    --- a/backoff.c
    +++ b/backoff.c
    @@ -6,12 +6,6 @@
     #include 
     #endif
     
    -#if PHP_VERSION_ID < 70100
    -static zend_long php_mt_rand_range(zend_long min, zend_long max) {
    -	return min + php_rand() % (max - min + 1)
    -}
    -#endif
    -
     #include "backoff.h"
     
     static zend_ulong random_range(zend_ulong min, zend_ulong max) {
    diff --git a/library.c b/library.c
    index 852a583146..5dd802a2a4 100644
    --- a/library.c
    +++ b/library.c
    @@ -100,15 +100,7 @@ static int redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zre
     
     /* Register a persistent resource in a a way that works for every PHP 7 version. */
     void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id) {
    -#if PHP_VERSION_ID < 70300
    -    zend_resource res;
    -    res.type = le_id;
    -    res.ptr = ptr;
    -
    -    zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(id), ZSTR_LEN(id), &res, sizeof(res));
    -#else
         zend_register_persistent_resource(ZSTR_VAL(id), ZSTR_LEN(id), ptr, le_id);
    -#endif
     }
     
     static ConnectionPool *
    diff --git a/redis_session.c b/redis_session.c
    index 96f39be7d5..8abdbe8191 100644
    --- a/redis_session.c
    +++ b/redis_session.c
    @@ -413,11 +413,7 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_
         }
     }
     
    -#if PHP_VERSION_ID < 70300
    -#define REDIS_URL_STR(umem) umem
    -#else
     #define REDIS_URL_STR(umem) ZSTR_VAL(umem)
    -#endif
     
     /* {{{ PS_OPEN_FUNC
      */
    
    From 40c897364fa5be53b7b9dbe4184ea6a7571eb972 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 5 Aug 2024 10:50:00 -0700
    Subject: [PATCH 1906/1986] Remove erroneously duplicated changelog entries.
    
    When constructing the 6.1.0RC1 CHANGELOG.md and package.xml a few
    commits from older releases were accidentally included.
    
    See #2474
    ---
     CHANGELOG.md | 29 -----------------------------
     package.xml  | 32 ++++----------------------------
     2 files changed, 4 insertions(+), 57 deletions(-)
    
    diff --git a/CHANGELOG.md b/CHANGELOG.md
    index 95baede0e7..9c0d8832dc 100644
    --- a/CHANGELOG.md
    +++ b/CHANGELOG.md
    @@ -97,18 +97,6 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
     - the VALUE argument type for hSetNx must be the same as for hSet
       [df074dbe](https://github.com/phpredis/phpredis/commit/df074dbe)
       ([Uładzimir Tsykun](https://github.com/vtsykun))
    -- Fix `PSUBSCRIBE` to find callback by pattern not string literal.
    -  [2f276dcd](https://github.com/phpredis/phpredis/commit/2f276dcd)
    -  ([michael-grunder](https://github.com/michael-grunder))
    -  [#2395](https://github.com/phpredis/phpredis/pull/2395)
    -- Fix memory leak and segfault in Redis::exec
    -  [362e1141](https://github.com/phpredis/phpredis/commit/362e1141)
    -  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    -- Fix unknown expiration modifier warning when null argument passed
    -  [264c0c7e](https://github.com/phpredis/phpredis/commit/264c0c7e)
    -  [3eb60f58](https://github.com/phpredis/phpredis/commit/3eb60f58)
    -  [#2388](https://github.com/phpredis/phpredis/pull/2388)
    -  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
     - Other fixes
       [e18f6c6d](https://github.com/phpredis/phpredis/commit/e18f6c6d)
       [3d7be358](https://github.com/phpredis/phpredis/commit/3d7be358)
    @@ -178,8 +166,6 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
       [8f8ff72a](https://github.com/phpredis/phpredis/commit/8f8ff72a)
       ([Takayasu Oyama](https://github.com/taka-oyama))
       [5d293245](https://github.com/phpredis/phpredis/commit/5d293245)
    -  [95bd184b](https://github.com/phpredis/phpredis/commit/95bd184b)
    -  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
     - Fix config.m4 when using custom dep paths
       [ece3f7be](https://github.com/phpredis/phpredis/commit/ece3f7be)
       ([Michael Grunder](https://github.com/michael-grunder))
    @@ -203,9 +189,6 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
       [dc05d65c](https://github.com/phpredis/phpredis/commit/dc05d65c)
       ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
       [#2381](https://github.com/phpredis/phpredis/pull/2381)
    -- Add back old examples with note
    -  [1ad95b63](https://github.com/phpredis/phpredis/commit/1ad95b63)
    -  ([Joost](https://github.com/OrangeJuiced))
     
     ### Tests/CI
     
    @@ -271,12 +254,6 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
       [e051a5db](https://github.com/phpredis/phpredis/commit/e051a5db)
       [#2427](https://github.com/phpredis/phpredis/pull/2427)
       ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    -- Fix deprecation error when passing null to match_type parameter
    -  [b835aaa3](https://github.com/phpredis/phpredis/commit/b835aaa3)
    -  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    -- Fix crash in `OBJECT` command in pipeline.
    -  [a7f51f70](https://github.com/phpredis/phpredis/commit/a7f51f70)
    -  ([michael-grunder](https://github.com/michael-grunder))
     - Use newInstance in RedisClusterTest
       [954fbab8](https://github.com/phpredis/phpredis/commit/954fbab8)
       ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    @@ -294,12 +271,6 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
       [2bddd84f](https://github.com/phpredis/phpredis/commit/2bddd84f)
       ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
       [#2378](https://github.com/phpredis/phpredis/pull/2378)
    -- Update sentinel documentation to reflect changes to constructor in 6.0 release
    -  [849bedb6](https://github.com/phpredis/phpredis/commit/849bedb6)
    -  ([Joost](https://github.com/OrangeJuiced))
    -- Add missing option to example
    -  [3674d663](https://github.com/phpredis/phpredis/commit/3674d663)
    -  ([Till Krüss](https://github.com/tillkruss))
     - Fix typo in link
       [8f6bc98f](https://github.com/phpredis/phpredis/commit/8f6bc98f)
       ([Timo Sand](https://github.com/deiga))
    diff --git a/package.xml b/package.xml
    index 9135ea8010..3bbd1adaf5 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -69,11 +69,6 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Fix segfault when passing just false to auth. [6dc0a0be] (Michael Grunder)
         * the VALUE argument type for hSetNx must be the same as for hSet [df074dbe]
           (Uladzimir Tsykun)
    -    * Fix `PSUBSCRIBE` to find callback by pattern not string literal. [2f276dcd]
    -      (Michael Grunder)
    -    * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko)
    -    * Fix unknown expiration modifier warning when null argument passed [264c0c7e,
    -      3eb60f58] (Pavlo Yatsukhnenko)
         * Other fixes [e18f6c6d, 3d7be358, 2b555c89, fa1a283a, 37c5f8d4] (Michael Grunder, Viktor Szepe)
     
         Added:
    @@ -101,8 +96,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Add session.save_path examples [8a39caeb] (Martin Vancl)
         * Tighter return types for Redis::(keys|hKeys|hVals|hGetAll) [77ab62bc]
           (Benjamin Morel)
    -    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a, 5d293245,
    -      95bd184b] (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
    +    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a] 
    +      (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
         * Fix config.m4 when using custom dep paths [ece3f7be] (Michael Grunder)
         * Fix retry_internal documentation [142c1f4a] (SplotyCode)
         * Fix anchor link [9b5cad31] (Git'Fellow)
    @@ -110,7 +105,6 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Fix Fedora package url [60b1ba14, 717713e1] (Dmitrii Kotov)
         * Update Redis Sentinel documentation to reflect changes to constructor in 6.0
           release [dc05d65c] (Pavlo Yatsukhnenko)
    -    * Add back old examples with note [1ad95b63] (Joost)
     
         Tests/CI:
     
    @@ -129,18 +123,12 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * sessionSaveHandler injection. [9f8f80ca] (Pavlo Yatsukhnenko)
         * KeyDB addiions [54d62c72, d9c48b78] (Michael Grunder)
         * Add PHP 8.3 to CI [78d15140, e051a5db] (Robert Kelcak, Pavlo Yatsukhnenko)
    -    * Fix deprecation error when passing null to match_type parameter [b835aaa3]
    -      (Pavlo Yatsukhnenko)
    -    * Fix crash in `OBJECT` command in pipeline. [a7f51f70] (Michael Grunder)
         * Use newInstance in RedisClusterTest [954fbab8] (Pavlo Yatsukhnenko)
         * Use actions/checkout@v4 [f4c2ac26] (Pavlo Yatsukhnenko)
         * Cluster nodes from ENV [eda39958, 0672703b] (Pavlo Yatsukhnenko)
         * Ensure we're talking to redis-server in our high ports test. [7825efbc]
           (Michael Grunder)
         * Add missing option to installation example [2bddd84f] (Pavlo Yatsukhnenko)
    -    * Update sentinel documentation to reflect changes to constructor in 6.0 release
    -      [849bedb6] (Joost)
    -    * Add missing option to example [3674d663] (Till Kruss)
         * Fix typo in link [8f6bc98f] (Timo Sand)
         * Update tests to allow users to use a custom class. [5f6ce414] (Michael Grunder)
      
    @@ -277,11 +265,6 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Fix segfault when passing just false to auth. [6dc0a0be] (Michael Grunder)
         * the VALUE argument type for hSetNx must be the same as for hSet [df074dbe]
           (Uladzimir Tsykun)
    -    * Fix `PSUBSCRIBE` to find callback by pattern not string literal. [2f276dcd]
    -      (Michael Grunder)
    -    * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko)
    -    * Fix unknown expiration modifier warning when null argument passed [264c0c7e,
    -      3eb60f58] (Pavlo Yatsukhnenko)
         * Other fixes [e18f6c6d, 3d7be358, 2b555c89, fa1a283a, 37c5f8d4] (Michael Grunder, Viktor Szepe)
     
         Added:
    @@ -309,8 +292,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Add session.save_path examples [8a39caeb] (Martin Vancl)
         * Tighter return types for Redis::(keys|hKeys|hVals|hGetAll) [77ab62bc]
           (Benjamin Morel)
    -    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a, 5d293245,
    -      95bd184b] (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
    +    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a, 5d293245] 
    +      (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
         * Fix config.m4 when using custom dep paths [ece3f7be] (Michael Grunder)
         * Fix retry_internal documentation [142c1f4a] (SplotyCode)
         * Fix anchor link [9b5cad31] (Git'Fellow)
    @@ -318,7 +301,6 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Fix Fedora package url [60b1ba14, 717713e1] (Dmitrii Kotov)
         * Update Redis Sentinel documentation to reflect changes to constructor in 6.0
           release [dc05d65c] (Pavlo Yatsukhnenko)
    -    * Add back old examples with note [1ad95b63] (Joost)
     
         Tests/CI:
     
    @@ -337,18 +319,12 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * sessionSaveHandler injection. [9f8f80ca] (Pavlo Yatsukhnenko)
         * KeyDB addiions [54d62c72, d9c48b78] (Michael Grunder)
         * Add PHP 8.3 to CI [78d15140, e051a5db] (Robert Kelcak, Pavlo Yatsukhnenko)
    -    * Fix deprecation error when passing null to match_type parameter [b835aaa3]
    -      (Pavlo Yatsukhnenko)
    -    * Fix crash in `OBJECT` command in pipeline. [a7f51f70] (Michael Grunder)
         * Use newInstance in RedisClusterTest [954fbab8] (Pavlo Yatsukhnenko)
         * Use actions/checkout@v4 [f4c2ac26] (Pavlo Yatsukhnenko)
         * Cluster nodes from ENV [eda39958, 0672703b] (Pavlo Yatsukhnenko)
         * Ensure we're talking to redis-server in our high ports test. [7825efbc]
           (Michael Grunder)
         * Add missing option to installation example [2bddd84f] (Pavlo Yatsukhnenko)
    -    * Update sentinel documentation to reflect changes to constructor in 6.0 release
    -      [849bedb6] (Joost)
    -    * Add missing option to example [3674d663] (Till Kruss)
         * Fix typo in link [8f6bc98f] (Timo Sand)
         * Update tests to allow users to use a custom class. [5f6ce414] (Michael Grunder)
        
    
    From 9d380500934341cb85086675a97d09724f6c60d1 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 19 Sep 2024 14:25:17 -0700
    Subject: [PATCH 1907/1986] Upload artifact v2 is deprecated
    
    ---
     .github/workflows/ci.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index f2ef9a226e..e8dde4d6b6 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -292,7 +292,7 @@ jobs:
               copy LICENSE binaries
               Get-ChildItem -Recurse -Filter "php_redis.dll" | ForEach-Object {Copy-Item -Path $_.FullName -Destination "binaries"}
           - name: Upload artifacts
    -        uses: actions/upload-artifact@v2
    +        uses: actions/upload-artifact@v4
             with:
               name: redis-${{matrix.php}}-x64-${{matrix.ts}}
               path: binaries
    
    From a75a7e5a361b5cc79d858b18dafe7e25c42f9065 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 19 Sep 2024 14:14:58 -0700
    Subject: [PATCH 1908/1986] Fix SIGABRT in PHP 8.4
    
    PHP switched from `ZEND_ASSUME` to `ZEND_ASSERT` when making sure
    `Z_PTR_P(zv)` was nonnull in `zend_hash_str_update_ptr`.
    
    This commit just switches to `zend_hash_str_add_empty_element` which
    is semantically more correct anyway.
    
    Fixes #2539
    ---
     cluster_library.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index 322faab740..3196eba14f 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -2995,7 +2995,7 @@ static zend_string **get_valid_seeds(HashTable *input, uint32_t *nseeds) {
             }
     
             /* Add as a key to avoid duplicates */
    -        zend_hash_str_update_ptr(valid, Z_STRVAL_P(z_seed), Z_STRLEN_P(z_seed), NULL);
    +        zend_hash_str_add_empty_element(valid, Z_STRVAL_P(z_seed), Z_STRLEN_P(z_seed));
         } ZEND_HASH_FOREACH_END();
     
         /* We need at least one valid seed */
    
    From b59e35a64f91e85b03b9f51bb41a393d1eac88d7 Mon Sep 17 00:00:00 2001
    From: James Titcumb 
    Date: Wed, 18 Sep 2024 20:47:47 +0100
    Subject: [PATCH 1909/1986] Added a composer.json to enable support for PIE
    
    ---
     composer.json | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
     1 file changed, 62 insertions(+)
     create mode 100644 composer.json
    
    diff --git a/composer.json b/composer.json
    new file mode 100644
    index 0000000000..e5c7077082
    --- /dev/null
    +++ b/composer.json
    @@ -0,0 +1,62 @@
    +
    +{
    +    "name": "phpredis/phpredis",
    +    "type": "php-ext",
    +    "license": "PHP-3.01",
    +    "description": "A PHP extension for Redis",
    +    "require": {
    +        "php": ">= 7.4.0"
    +    },
    +    "php-ext": {
    +        "extension-name": "redis",
    +        "configure-options": [
    +            {
    +                "name": "enable-redis",
    +                "description": "Enable redis support"
    +            },
    +            {
    +                "name": "disable-redis-session",
    +                "description": "Disable session support"
    +            },
    +            {
    +                "name": "disable-redis-json",
    +                "description": "Disable json serializer support"
    +            },
    +            {
    +                "name": "enable-redis-igbinary",
    +                "description": "Enable igbinary serializer support"
    +            },
    +            {
    +                "name": "enable-redis-msgpack",
    +                "description": "Enable msgpack serializer support"
    +            },
    +            {
    +                "name": "enable-redis-lzf",
    +                "description": "Enable lzf compression support"
    +            },
    +            {
    +                "name": "with-liblzf",
    +                "description": "Use system liblzf",
    +                "needs-value": true
    +            },
    +            {
    +                "name": "enable-redis-zstd",
    +                "description": "Enable Zstd compression support"
    +            },
    +            {
    +                "name": "with-libzstd",
    +                "description": "Use system libzstd",
    +                "needs-value": true
    +            },
    +            {
    +                "name": "enable-redis-lz4",
    +                "description": "Enable lz4 compression support"
    +            },
    +            {
    +                "name": "with-liblz4",
    +                "description": "Use system liblz4",
    +                "needs-value": true
    +            }
    +        ]
    +    }
    +}
    
    From 9bd2aaace407ab184148a0bd975f9d2237311267 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sun, 22 Sep 2024 18:59:58 -0700
    Subject: [PATCH 1910/1986] Prepare for 6.1.0RC2
    
    ---
     CHANGELOG.md | 42 ++++++++++++++++++++++++++++++--
     package.xml  | 68 ++++++++++++++++++++++++++++++++++++++++++++++------
     2 files changed, 101 insertions(+), 9 deletions(-)
    
    diff --git a/CHANGELOG.md b/CHANGELOG.md
    index 9c0d8832dc..40bca8a516 100644
    --- a/CHANGELOG.md
    +++ b/CHANGELOG.md
    @@ -5,7 +5,7 @@ All changes to phpredis will be documented in this file.
     We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
     and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
     
    -## [6.1.0RC1] - 2024-08-04 ([GitHub](https://github.com/phpredis/phpredis/releases/6.1.0RC1), [PECL](https://pecl.php.net/package/redis/6.1.0RC1))
    +## [6.1.0RC2] - 2024-09-23 ([Github](https://github.com/phpredis/phpredis/releases/6.1.0RC2), [PECL](https://pecl.php.net/package/redis/6.1.0RC2))
     
     ### Sponsors :sparkling_heart:
     
    @@ -24,6 +24,44 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
     
     ### Fixed
     
    +- Fixed a `SIGABRT` error in PHP 8.4
    +  [a75a7e5a](https://github.com/phpredis/phpredis/commit/a75a7e5a)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +- Clean up code for unsupported versions of PHP
    +  [37cebdd7](https://github.com/phpredis/phpredis/commit/37cebdd7)
    +  ([Remi Collet](https://github.com/remicollet))
    +- Add `SessionHelpers.php` to `package.xml`
    +  [e9474b80](https://github.com/phpredis/phpredis/commit/e9474b80)
    +  ([Remi Collet](https://github.com/remicollet))
    +
    +### Changed
    +
    +- Raised minimum supported PHP version to 7.4
    +  [8b519423](https://github.com/phpredis/phpredis/commit/8b519423)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +
    +### Removed
    +
    +- Removed erroneously duplicated changelog entries
    +  [40c89736](https://github.com/phpredis/phpredis/commit/40c89736)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +
    +### Tests/CI
    +
    +- Move to upload artifacts v4
    +  [9d3805009](https://github.com/phpredis/phpredis/commit/9d3805009)
    +  ([Michael Grunder](https://github.com/michael-grunder))
    +
    +### Added
    +
    +- Added `composer.json` to support [PIE](https://github.com/php/pie) (PHP Installer for Extensions)
    +  [b59e35a6](https://github.com/phpredis/phpredis/commit/b59e35a6)
    +  ([James Titcumb](https://github.com/asgrim))
    +
    +## [6.1.0RC1] - 2024-08-04 ([GitHub](https://github.com/phpredis/phpredis/releases/6.1.0RC1), [PECL](https://pecl.php.net/package/redis/6.1.0RC1))
    +
    +### Fixed
    +
     - Fix random connection timeouts with Redis Cluster.
       [eb7f31e7](https://github.com/phpredis/phpredis/commit/eb7f31e7)
       ([Jozsef Koszo](https://github.com/kjoe))
    @@ -1075,7 +1113,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
       [05129c3a3](https://github.com/phpredis/phpredis/commit/05129c3a3)
       [5bba6a7fc](https://github.com/phpredis/phpredis/commit/5bba6a7fc)
       ([Nathaniel Braun](https://github.com/nbraun-amazon))
    -- Added experimental support for detecting a dirty connection by 
    +- Added experimental support for detecting a dirty connection by
       trying to determine if the underlying stream is readable.
       [d68579562](https://github.com/phpredis/phpredis/commit/d68579562)
       [#2013](https://github.com/phpredis/phpredis/issues/2013)
    diff --git a/package.xml b/package.xml
    index 3bbd1adaf5..aaf53760c1 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -21,9 +21,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
       p.yatsukhnenko@gmail.com
       yes
      
    - 2024-08-04
    + 2024-09-23
      
    -  6.1.0RC1
    +  6.1.0RC2
       6.0.0
      
      
    @@ -40,6 +40,33 @@ http://pear.php.net/dtd/package-2.0.xsd">
         Ty Karok - https://github.com/karock
         Object Cache Pro for WordPress - https://objectcache.pro
     
    +    --- 6.1.0RC2 ---
    +
    +    Fixed:
    +
    +    * Fixed a `SIGABRT` error in PHP 8.4 [a75a7e5a] (Michael Grunder)
    +    * Clean up code for unsupported versions of PHP [37cebdd7] (Remi Collet)
    +    * Add `SessionHelpers.php` to `package.xml`[e9474b80] (Remi Collet)
    +
    +    Changed:
    +
    +    * Raised minimum supported PHP version to 7.4 [8b519423] (Michael Grunder)
    +
    +    Removed:
    +
    +    * Removed erroneously duplicated changelog entries [40c89736] (Michael Grunder)
    +
    +    Tests/CI:
    +
    +    * Move to upload artifacts v4 [9d380500] (Michael Grunder)
    +
    +    Added:
    +
    +    * Added `composer.json` to support PIE (PHP Installer for Extensions) [b59e35a6]
    +      (James Titcumb)
    +
    +    --- 6.1.0RC1 ---
    +
         Fixed:
     
         * Fix random connection timeouts with Redis Cluster. [eb7f31e7] (Jozsef Koszo)
    @@ -96,7 +123,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Add session.save_path examples [8a39caeb] (Martin Vancl)
         * Tighter return types for Redis::(keys|hKeys|hVals|hGetAll) [77ab62bc]
           (Benjamin Morel)
    -    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a] 
    +    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a]
           (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
         * Fix config.m4 when using custom dep paths [ece3f7be] (Michael Grunder)
         * Fix retry_internal documentation [142c1f4a] (SplotyCode)
    @@ -225,8 +252,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
      
      
        betabeta
    -   6.1.0RC16.0.0
    -   2024-08-04
    +   6.1.0RC26.0.0
    +   2024-09-23
        
         --- Sponsors ---
     
    @@ -236,6 +263,33 @@ http://pear.php.net/dtd/package-2.0.xsd">
         Ty Karok - https://github.com/karock
         Object Cache Pro for WordPress - https://objectcache.pro
     
    +    --- 6.1.0RC2 ---
    +
    +    Fixed:
    +
    +    * Fixed a `SIGABRT` error in PHP 8.4 [a75a7e5a] (Michael Grunder)
    +    * Clean up code for unsupported versions of PHP [37cebdd7] (Remi Collet)
    +    * Add `SessionHelpers.php` to `package.xml`[e9474b80] (Remi Collet)
    +
    +    Changed:
    +
    +    * Raised minimum supported PHP version to 7.4 [8b519423] (Michael Grunder)
    +
    +    Removed:
    +
    +    * Removed erroneously duplicated changelog entries [40c89736] (Michael Grunder)
    +
    +    Tests/CI:
    +
    +    * Move to upload artifacts v4 [9d380500] (Michael Grunder)
    +
    +    Added:
    +
    +    * Added `composer.json` to support PIE (PHP Installer for Extensions) [b59e35a6]
    +      (James Titcumb)
    +
    +    --- 6.1.0RC1 ---
    +
         Fixed:
     
         * Fix random connection timeouts with Redis Cluster. [eb7f31e7] (Jozsef Koszo)
    @@ -292,7 +346,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Add session.save_path examples [8a39caeb] (Martin Vancl)
         * Tighter return types for Redis::(keys|hKeys|hVals|hGetAll) [77ab62bc]
           (Benjamin Morel)
    -    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a, 5d293245] 
    +    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a, 5d293245]
           (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
         * Fix config.m4 when using custom dep paths [ece3f7be] (Michael Grunder)
         * Fix retry_internal documentation [142c1f4a] (SplotyCode)
    @@ -355,7 +409,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
         You can find a detailed list of changes in CHANGELOG.md and package.xml
         or by inspecting the git commit logs.
     
    -    * Fix deprecation error when passing null to match_type parameter.[b835aaa3] (Pavlo Yatsukhnenko) 
    +    * Fix deprecation error when passing null to match_type parameter.[b835aaa3] (Pavlo Yatsukhnenko)
         * Fix flaky test and OBJECT in a pipeline. [a7f51f70] (Michael Grunder)
         * Find our callback by pattern with PSUBSCRIBE [2f276dcd] (Michael Grunder)
        
    
    From 30c8f90cd9ccf5cd0381e861a7b71f245b17ba68 Mon Sep 17 00:00:00 2001
    From: Remi Collet 
    Date: Mon, 23 Sep 2024 07:50:53 +0200
    Subject: [PATCH 1911/1986] bump version
    
    ---
     php_redis.h | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/php_redis.h b/php_redis.h
    index 232025ff5e..ca6f758d3a 100644
    --- a/php_redis.h
    +++ b/php_redis.h
    @@ -23,7 +23,7 @@
     #define PHP_REDIS_H
     
     /* phpredis version */
    -#define PHP_REDIS_VERSION "6.1.0RC1"
    +#define PHP_REDIS_VERSION "6.1.0RC2"
     
     /* For convenience we store the salt as a printable hex string which requires 2
      * characters per byte + 1 for the NULL terminator */
    
    From bff3a22e9d9134cfe8a32b8423632ef2f1de3964 Mon Sep 17 00:00:00 2001
    From: Remi Collet 
    Date: Mon, 23 Sep 2024 07:51:08 +0200
    Subject: [PATCH 1912/1986] fix implicit nullable (8.4)
    
    ---
     tests/TestSuite.php | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/tests/TestSuite.php b/tests/TestSuite.php
    index 410fa0e298..f5135d3631 100644
    --- a/tests/TestSuite.php
    +++ b/tests/TestSuite.php
    @@ -284,7 +284,7 @@ protected function assertIsArray($v, ?int $size = null): bool {
             return true;
         }
     
    -    protected function assertArrayKey($arr, $key, callable $cb = NULL): bool {
    +    protected function assertArrayKey($arr, $key, ?callable $cb = NULL): bool {
             $cb ??= function ($v) { return true; };
     
             if (($exists = isset($arr[$key])) && $cb($arr[$key]))
    
    From 909c5cc13cb4b20521a02c7da7044f820bebebb3 Mon Sep 17 00:00:00 2001
    From: Michael Grunder 
    Date: Mon, 23 Sep 2024 13:56:48 -0700
    Subject: [PATCH 1913/1986] Finalize 6.1.0RC2 changelog for completeness.
     (#2554)
    
    * Finalize 6.1.0RC2 changelog for completeness.
    
    * Fix CHANGELOG.md formatting + link to contributor users
    ---
     CHANGELOG.md | 34 +++++++++++++++++++++++++++++-----
     package.xml  |  1 +
     2 files changed, 30 insertions(+), 5 deletions(-)
    
    diff --git a/CHANGELOG.md b/CHANGELOG.md
    index 40bca8a516..62bfd0876b 100644
    --- a/CHANGELOG.md
    +++ b/CHANGELOG.md
    @@ -17,10 +17,30 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
     
     ### Contributors to this release :sparkling_heart:
     
    -  @michael-grunder, @yatsukhnenko, @bitactive, @OrangeJuiced, @crocodele,
    -  @kalifg, @divinity76, @PlavorSeol, @kjoe, @tstarling, @acorncom, @tuxmartin,
    -  @BenMorel, @szepeviktor, @SplotyCode, @taka-oyama, @PROFeNoM, @woodongwong,
    -  @RobiNN1, @vtsykun, @solracsf, @tillkruss, @deiga, @tutuna
    +  [@michael-grunder](https://github.com/michael-grunder),
    +  [@yatsukhnenko](https://github.com/yatsukhnenko),
    +  [@bitactive](https://github.com/bitactive),
    +  [@OrangeJuiced](https://github.com/OrangeJuiced),
    +  [@crocodele](https://github.com/crocodele),
    +  [@kalifg](https://github.com/kalifg),
    +  [@divinity76](https://github.com/divinity76),
    +  [@PlavorSeol](https://github.com/PlavorSeol),
    +  [@kjoe](https://github.com/kjoe),
    +  [@tstarling](https://github.com/tstarling),
    +  [@acorncom](https://github.com/acorncom),
    +  [@tuxmartin](https://github.com/tuxmartin),
    +  [@BenMorel](https://github.com/BenMorel),
    +  [@szepeviktor](https://github.com/szepeviktor),
    +  [@SplotyCode](https://github.com/SplotyCode),
    +  [@taka-oyama](https://github.com/taka-oyama),
    +  [@PROFeNoM](https://github.com/PROFeNoM),
    +  [@woodongwong](https://github.com/woodongwong),
    +  [@RobiNN1](https://github.com/RobiNN1),
    +  [@vtsykun](https://github.com/vtsykun),
    +  [@solracsf](https://github.com/solracsf),
    +  [@tillkruss](https://github.com/tillkruss),
    +  [@deiga](https://github.com/deiga),
    +  [@tutuna](https://github.com/tutuna)
     
     ### Fixed
     
    @@ -33,6 +53,10 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
     - Add `SessionHelpers.php` to `package.xml`
       [e9474b80](https://github.com/phpredis/phpredis/commit/e9474b80)
       ([Remi Collet](https://github.com/remicollet))
    +- 8.4 implicit null fix, bump version
    +  [bff3a22e](https://github.com/phpredis/phpredis/commit/bff3a22e)
    +  [30c8f90c](https://github.com/phpredis/phpredis/commit/30c8f90c)
    +  ([Remi Collet](https://github.com/remicollet))
     
     ### Changed
     
    @@ -177,7 +201,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
       [eeb51099](https://github.com/phpredis/phpredis/commit/eeb51099)
       ([Michael Dwyer](https://github.com/kalifg))
       [#2523](https://github.com/phpredis/phpredis/pull/2523)
    -- fix missing  tags
    +- fix missing \ tags
       [f865d5b9](https://github.com/phpredis/phpredis/commit/f865d5b9)
       ([divinity76](https://github.com/divinity76))
     - Mention Valkey support
    diff --git a/package.xml b/package.xml
    index aaf53760c1..4cacd927d7 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -47,6 +47,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
         * Fixed a `SIGABRT` error in PHP 8.4 [a75a7e5a] (Michael Grunder)
         * Clean up code for unsupported versions of PHP [37cebdd7] (Remi Collet)
         * Add `SessionHelpers.php` to `package.xml`[e9474b80] (Remi Collet)
    +    * 8.4 implicit null fix, bump version [bff3a22e, 30c8f90c] [Remi Collet]
     
         Changed:
     
    
    From 0bae4bb0442e408c8f5c8875400800aaa3b6aa42 Mon Sep 17 00:00:00 2001
    From: Vincent Langlet 
    Date: Mon, 23 Sep 2024 14:58:24 +0200
    Subject: [PATCH 1914/1986] Fix urls
    
    ---
     redis_cluster.stub.php | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
    index 16f3154a7f..833d70949d 100644
    --- a/redis_cluster.stub.php
    +++ b/redis_cluster.stub.php
    @@ -1097,7 +1097,7 @@ public function zrangestore(string $dstkey, string $srckey, int $start, int $end
                                     array|bool|null $options = null): RedisCluster|int|false;
     
         /**
    -     * @see https://redis.io/commands/zRandMember
    +     * @see https://redis.io/commands/zrandmember
          */
         public function zrandmember(string $key, ?array $options = null): RedisCluster|string|array;
     
    @@ -1167,7 +1167,7 @@ public function zscan(string $key, null|int|string &$iterator, ?string $pattern
         public function zscore(string $key, mixed $member): RedisCluster|float|false;
     
         /**
    -     * @see https://redis.io/commands/zMscore
    +     * @see https://redis.io/commands/zmscore
          */
         public function zmscore(string $key, mixed $member, mixed ...$other_members): Redis|array|false;
     
    
    From cc1be32294a0b134e60be1476775e8d37dbf3f6a Mon Sep 17 00:00:00 2001
    From: Remi Collet 
    Date: Tue, 24 Sep 2024 14:47:04 +0200
    Subject: [PATCH 1915/1986] fix 2 tests with redis 6.2
    
    ---
     tests/RedisTest.php | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 6bf0655939..a33a062f0f 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -245,7 +245,7 @@ public function testBitcount() {
             $this->redis->set('bitcountkey', hex2bin('10eb8939e68bfdb640260f0629f3'));
             $this->assertEquals(1, $this->redis->bitcount('bitcountkey', 8, 8, false));
     
    -        if ( ! $this->is_keydb) {
    +        if ( ! $this->is_keydb && $this->minVersionCheck('7.0')) {
                 /* key, start, end, BIT */
                 $this->redis->set('bitcountkey', hex2bin('cd0e4c80f9e4590d888a10'));
                 $this->assertEquals(5, $this->redis->bitcount('bitcountkey', 0, 9, true));
    @@ -7625,7 +7625,7 @@ public function testCommand() {
             $this->assertIsArray($commands);
             $this->assertEquals(count($commands), $this->redis->command('count'));
     
    -        if ( ! $this->is_keydb) {
    +        if ( ! $this->is_keydb && $this->minVersionCheck('7.0')) {
                 $infos = $this->redis->command('info');
                 $this->assertIsArray($infos);
                 $this->assertEquals(count($infos), count($commands));
    
    From f89d4d8f6eecbe223e158651ffffd77ffa27449b Mon Sep 17 00:00:00 2001
    From: "Christoph M. Becker" 
    Date: Mon, 30 Sep 2024 15:38:44 +0200
    Subject: [PATCH 1916/1986] Windows CI: update setup-php-sdk to v0.10 and
     enable caching
    
    ---
     .github/workflows/ci.yml | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index e8dde4d6b6..8d08416058 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -270,12 +270,13 @@ jobs:
             with:
               submodules: true
           - name: Install PHP ${{ matrix.php }}
    -        uses: php/setup-php-sdk@v0.8
    +        uses: php/setup-php-sdk@v0.10
             id: setup-php-sdk
             with:
               version: ${{ matrix.php }}
               arch: x64
               ts: ${{matrix.ts}}
    +          cache: true
           - name: Install dependencies
             uses: ilammy/msvc-dev-cmd@v1
             with:
    
    From 52e69edefdb98ac19204ed7eb4b1708af6208d73 Mon Sep 17 00:00:00 2001
    From: Remi Collet 
    Date: Thu, 3 Oct 2024 20:06:56 +0200
    Subject: [PATCH 1917/1986] improve package summary and description (#2558)
    
    * improve package summary and description
    
    * improve package summary and description
    ---
     package.xml | 5 +++--
     1 file changed, 3 insertions(+), 2 deletions(-)
    
    diff --git a/package.xml b/package.xml
    index 4cacd927d7..bdb2c53afb 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -5,9 +5,10 @@ http://pear.php.net/dtd/package-2.0
     http://pear.php.net/dtd/package-2.0.xsd">
      redis
      pecl.php.net
    - PHP extension for interfacing with Redis
    + PHP extension for interfacing with key-value stores
      
    -   This extension provides an API for communicating with Redis servers.
    +   This extension provides an API for communicating with RESP-based key-value
    +   stores, such as Redis, Valkey, and KeyDB.
      
      
       Michael Grunder
    
    From 5419cc9c60d1ee04163b4d5323dd0fb02fb4f8bb Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 3 Oct 2024 14:57:39 -0700
    Subject: [PATCH 1918/1986] Prepare for 6.1.0 GA
    
    ---
     CHANGELOG.md | 23 ++++++++++++++++++++++-
     package.xml  | 43 ++++++++++++++++++++++++++++++++++++-------
     php_redis.h  |  2 +-
     3 files changed, 59 insertions(+), 9 deletions(-)
    
    diff --git a/CHANGELOG.md b/CHANGELOG.md
    index 62bfd0876b..12a5a4c155 100644
    --- a/CHANGELOG.md
    +++ b/CHANGELOG.md
    @@ -5,7 +5,24 @@ All changes to phpredis will be documented in this file.
     We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
     and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
     
    -## [6.1.0RC2] - 2024-09-23 ([Github](https://github.com/phpredis/phpredis/releases/6.1.0RC2), [PECL](https://pecl.php.net/package/redis/6.1.0RC2))
    +## [6.1.0] - 2024-10-04 ([Github](https://github.com/phpredis/phpredis/releases/6.1.0), [PECL](https://pecl.php.net/package/redis/6.1.0))
    +
    +**NOTE**: There were no changes to C code between 6.1.0RC2 and 6.1.0.
    +
    +### Documentation
    +
    +- Update package.xml to make it clearer that we support many key-value stores
    +  [52e69ede](https://github.com/phpredis/phpredis/commit/52e69ede)
    +  ([Remi Collet](https://github.com/remicollet))
    +- Fix redis.io urls
    +  [0bae4bb0](https://github.com/phpredis/phpredis/commit/0bae4bb0)
    +  ([Vincent Langlet](https://github.com/VincentLanglet))
    +
    +### Tests/CI
    +
    +- Fix 2 tests with redis 6.2
    +  [cc1be322](https://github.com/phpredis/phpredis/commit/cc1be322)
    +  ([Remi Collet](https://github.com/remicollet))
     
     ### Sponsors :sparkling_heart:
     
    @@ -41,6 +58,10 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
       [@tillkruss](https://github.com/tillkruss),
       [@deiga](https://github.com/deiga),
       [@tutuna](https://github.com/tutuna)
    +  [@VincentLanglet](https://github.com/VincentLanglet)
    +
    +
    +## [6.1.0RC2] - 2024-09-23 ([Github](https://github.com/phpredis/phpredis/releases/6.1.0RC2), [PECL](https://pecl.php.net/package/redis/6.1.0RC2))
     
     ### Fixed
     
    diff --git a/package.xml b/package.xml
    index bdb2c53afb..5dd79f8196 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -22,14 +22,14 @@ http://pear.php.net/dtd/package-2.0.xsd">
       p.yatsukhnenko@gmail.com
       yes
      
    - 2024-09-23
    + 2024-10-04
      
    -  6.1.0RC2
    +  6.1.0
       6.0.0
      
      
    -  beta
    -  beta
    +  stable
    +  stable
      
      PHP
      
    @@ -41,6 +41,20 @@ http://pear.php.net/dtd/package-2.0.xsd">
         Ty Karok - https://github.com/karock
         Object Cache Pro for WordPress - https://objectcache.pro
     
    +    --- 6.1.0 ---
    +
    +    NOTE: There were no changes to C code between 6.1.0RC2 and 6.1.0
    +
    +    Documentation:
    +
    +    * Update package.xml to make it clearer that we support many key-value stores
    +      [52e69ede] (Remi Collet)
    +    * Fix redis.io urls [0bae4bb0] (Vincent Langlet)
    +
    +    Tests/CI:
    +
    +    * Fix 2 tests with redis 6.2 [cc1be322] (Remi Collet)
    +
         --- 6.1.0RC2 ---
     
         Fixed:
    @@ -253,9 +267,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
      
      
      
    -   betabeta
    -   6.1.0RC26.0.0
    -   2024-09-23
    +   stablestable
    +   6.1.06.0.0
    +   2024-10-04
        
         --- Sponsors ---
     
    @@ -265,6 +279,21 @@ http://pear.php.net/dtd/package-2.0.xsd">
         Ty Karok - https://github.com/karock
         Object Cache Pro for WordPress - https://objectcache.pro
     
    +    --- 6.1.0 ---
    +
    +    NOTE: There were no changes to C code between 6.1.0RC2 and 6.1.0
    +
    +    Documentation:
    +
    +    * Update package.xml to make it clearer that we support many key-value stores
    +      [52e69ede] (Remi Collet)
    +    * Fix redis.io urls [0bae4bb0] (Vincent Langlet)
    +
    +    Tests/CI:
    +
    +    * Fix 2 tests with redis 6.2 [cc1be322] (Remi Collet)
    +
    +
         --- 6.1.0RC2 ---
     
         Fixed:
    diff --git a/php_redis.h b/php_redis.h
    index ca6f758d3a..77f31498c6 100644
    --- a/php_redis.h
    +++ b/php_redis.h
    @@ -23,7 +23,7 @@
     #define PHP_REDIS_H
     
     /* phpredis version */
    -#define PHP_REDIS_VERSION "6.1.0RC2"
    +#define PHP_REDIS_VERSION "6.1.0"
     
     /* For convenience we store the salt as a printable hex string which requires 2
      * characters per byte + 1 for the NULL terminator */
    
    From 985b0313fb664c9776c3d2c84e778ddd6733728e Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 14 Oct 2024 12:47:28 -0700
    Subject: [PATCH 1919/1986] KeyDB doesn't have a noble release yet.
    
    ---
     .github/workflows/ci.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index 8d08416058..4708d7e013 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -68,7 +68,7 @@ jobs:
               grep "REDIS_SHARED_LIBADD.*-L$GITHUB_WORKSPACE/libzstd" Makefile
     
       ubuntu:
    -    runs-on: ubuntu-latest
    +    runs-on: ubuntu:23.04
         continue-on-error: false
         strategy:
           fail-fast: false
    
    From eb66fc9e2fe60f13e5980ea2ecbe9457ca5ae8b4 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 14 Oct 2024 16:09:03 -0700
    Subject: [PATCH 1920/1986] Pin ubuntu version for KeyDB
    
    ---
     .github/workflows/ci.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index 4708d7e013..0d9cb2a84f 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -68,7 +68,7 @@ jobs:
               grep "REDIS_SHARED_LIBADD.*-L$GITHUB_WORKSPACE/libzstd" Makefile
     
       ubuntu:
    -    runs-on: ubuntu:23.04
    +    runs-on: ubuntu-22.04
         continue-on-error: false
         strategy:
           fail-fast: false
    
    From 085d61ecfb0d484832547b46343a2e4b275a372e Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 14 Oct 2024 10:56:47 -0700
    Subject: [PATCH 1921/1986] Create a strncmp wrapper
    
    On some glibc implementations strncmp is a macro. This commit simply creates a
    `redis_strncmp` static inline wrapper function so we can `ZEND_STRL` instead of
    manually counting the length or using `sizeof(s)-1` each time.
    
    Fixes #2565
    ---
     cluster_library.c  | 18 +++++++++---------
     common.h           |  6 ++++++
     library.c          | 30 +++++++++++++++---------------
     redis.c            |  8 ++++----
     redis_array_impl.c |  4 ++--
     redis_session.c    |  4 ++--
     6 files changed, 38 insertions(+), 32 deletions(-)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index 3196eba14f..f4284c6930 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -1908,17 +1908,17 @@ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
         }
     
         // Switch on the type
    -    if (strncmp (c->line_reply, "string", 6) == 0) {
    +    if (redis_strncmp(c->line_reply, ZEND_STRL("string")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_STRING);
    -    } else if (strncmp(c->line_reply, ZEND_STRL("set")) == 0) {
    +    } else if (redis_strncmp(c->line_reply, ZEND_STRL("set")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_SET);
    -    } else if (strncmp(c->line_reply, ZEND_STRL("list")) == 0) {
    +    } else if (redis_strncmp(c->line_reply, ZEND_STRL("list")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_LIST);
    -    } else if (strncmp(c->line_reply, ZEND_STRL("hash")) == 0) {
    +    } else if (redis_strncmp(c->line_reply, ZEND_STRL("hash")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_HASH);
    -    } else if (strncmp(c->line_reply, ZEND_STRL("zset")) == 0) {
    +    } else if (redis_strncmp(c->line_reply, ZEND_STRL("zset")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_ZSET);
    -    } else if (strncmp(c->line_reply, ZEND_STRL("stream")) == 0) {
    +    } else if (redis_strncmp(c->line_reply, ZEND_STRL("stream")) == 0) {
             CLUSTER_RETURN_LONG(c, REDIS_STREAM);
         } else {
             CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND);
    @@ -1977,9 +1977,9 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
             }
     
             // Make sure we have a message or pmessage
    -        if (!strncmp(Z_STRVAL_P(z_type), ZEND_STRL("message")) ||
    -            !strncmp(Z_STRVAL_P(z_type), ZEND_STRL("pmessage"))
    -        ) {
    +        if (zend_string_equals_literal(Z_STR_P(z_type), "message") ||
    +            zend_string_equals_literal(Z_STR_P(z_type), "pmessage"))
    +        {
                 is_pmsg = *Z_STRVAL_P(z_type) == 'p';
             } else {
                 zval_dtor(&z_tab);
    diff --git a/common.h b/common.h
    index c4e2ecb521..0d19d4c0e8 100644
    --- a/common.h
    +++ b/common.h
    @@ -262,6 +262,12 @@ typedef enum {
     #define REDIS_STRICMP_STATIC(s, len, sstr) \
         (len == sizeof(sstr) - 1 && !strncasecmp(s, sstr, len))
     
    +/* On some versions of glibc strncmp is a macro. This wrapper allows us to
    +   use it in combination with ZEND_STRL in those cases. */
    +static inline int redis_strncmp(const char *s1, const char *s2, size_t n) {
    +    return strncmp(s1, s2, n);
    +}
    +
     /* Test if a zval is a string and (case insensitive) matches a static string */
     #define ZVAL_STRICMP_STATIC(zv, sstr) \
         REDIS_STRICMP_STATIC(Z_STRVAL_P(zv), Z_STRLEN_P(zv), sstr)
    diff --git a/library.c b/library.c
    index 5dd802a2a4..40d888ce74 100644
    --- a/library.c
    +++ b/library.c
    @@ -147,7 +147,7 @@ static int reselect_db(RedisSock *redis_sock) {
             return -1;
         }
     
    -    if (strncmp(response, ZEND_STRL("+OK"))) {
    +    if (redis_strncmp(response, ZEND_STRL("+OK"))) {
             efree(response);
             return -1;
         }
    @@ -247,7 +247,7 @@ PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) {
         efree(cmd);
     
         if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
    -        strncmp(inbuf, ZEND_STRL("+OK")))
    +        redis_strncmp(inbuf, ZEND_STRL("+OK")))
         {
             return FAILURE;
         }
    @@ -1202,17 +1202,17 @@ PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
             return FAILURE;
         }
     
    -    if (strncmp(response, ZEND_STRL("+string")) == 0) {
    +    if (redis_strncmp(response, ZEND_STRL("+string")) == 0) {
             l = REDIS_STRING;
    -    } else if (strncmp(response, ZEND_STRL("+set")) == 0){
    +    } else if (redis_strncmp(response, ZEND_STRL("+set")) == 0){
             l = REDIS_SET;
    -    } else if (strncmp(response, ZEND_STRL("+list")) == 0){
    +    } else if (redis_strncmp(response, ZEND_STRL("+list")) == 0){
             l = REDIS_LIST;
    -    } else if (strncmp(response, ZEND_STRL("+zset")) == 0){
    +    } else if (redis_strncmp(response, ZEND_STRL("+zset")) == 0){
             l = REDIS_ZSET;
    -    } else if (strncmp(response, ZEND_STRL("+hash")) == 0){
    +    } else if (redis_strncmp(response, ZEND_STRL("+hash")) == 0){
             l = REDIS_HASH;
    -    } else if (strncmp(response, ZEND_STRL("+stream")) == 0) {
    +    } else if (redis_strncmp(response, ZEND_STRL("+stream")) == 0) {
             l = REDIS_STREAM;
         } else {
             l = REDIS_NOT_FOUND;
    @@ -3013,18 +3013,18 @@ redis_sock_check_liveness(RedisSock *redis_sock)
         }
     
         if (auth) {
    -        if (strncmp(inbuf, ZEND_STRL("+OK")) == 0 ||
    -            strncmp(inbuf, ZEND_STRL("-ERR Client sent AUTH")) == 0)
    +        if (redis_strncmp(inbuf, ZEND_STRL("+OK")) == 0 ||
    +            redis_strncmp(inbuf, ZEND_STRL("-ERR Client sent AUTH")) == 0)
             {
                 /* successfully authenticated or authentication isn't required */
                 if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
                     goto failure;
                 }
    -        } else if (strncmp(inbuf, ZEND_STRL("-NOAUTH")) == 0) {
    +        } else if (redis_strncmp(inbuf, ZEND_STRL("-NOAUTH")) == 0) {
                 /* connection is fine but authentication failed, next command must
                  * fail too */
                 if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0
    -                || strncmp(inbuf, ZEND_STRL("-NOAUTH")) != 0)
    +                || redis_strncmp(inbuf, ZEND_STRL("-NOAUTH")) != 0)
                 {
                     goto failure;
                 }
    @@ -3034,7 +3034,7 @@ redis_sock_check_liveness(RedisSock *redis_sock)
             }
             redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED;
         } else {
    -        if (strncmp(inbuf, ZEND_STRL("-NOAUTH")) == 0) {
    +        if (redis_strncmp(inbuf, ZEND_STRL("-NOAUTH")) == 0) {
                 /* connection is fine but authentication required */
                 return SUCCESS;
             }
    @@ -3042,11 +3042,11 @@ redis_sock_check_liveness(RedisSock *redis_sock)
     
         /* check echo response */
         if ((redis_sock->sentinel && (
    -        strncmp(inbuf, ZEND_STRL("-ERR unknown command")) != 0 ||
    +        redis_strncmp(inbuf, ZEND_STRL("-ERR unknown command")) != 0 ||
             strstr(inbuf, id) == NULL
         )) || *inbuf != TYPE_BULK || atoi(inbuf + 1) != idlen ||
             redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
    -        strncmp(inbuf, id, idlen) != 0
    +        redis_strncmp(inbuf, id, idlen) != 0
         ) {
             goto failure;
         }
    diff --git a/redis.c b/redis.c
    index 31d9611efb..e09b15f672 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -174,7 +174,7 @@ redis_send_discard(RedisSock *redis_sock)
            (resp = redis_sock_read(redis_sock,&resp_len)) != NULL)
         {
             /* success if we get OK */
    -        result = (resp_len == 3 && strncmp(resp,"+OK", 3) == 0) ? SUCCESS:FAILURE;
    +        result = (resp_len == 3 && redis_strncmp(resp, ZEND_STRL("+OK")) == 0) ? SUCCESS:FAILURE;
     
             /* free our response */
             efree(resp);
    @@ -1906,7 +1906,7 @@ PHP_METHOD(Redis, multi)
                     }
                     if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
                         RETURN_FALSE;
    -                } else if (strncmp(resp, ZEND_STRL("+OK")) != 0) {
    +                } else if (redis_strncmp(resp, ZEND_STRL("+OK")) != 0) {
                         efree(resp);
                         RETURN_FALSE;
                     }
    @@ -2045,7 +2045,7 @@ redis_response_enqueued(RedisSock *redis_sock)
         int resp_len, ret = FAILURE;
     
         if ((resp = redis_sock_read(redis_sock, &resp_len)) != NULL) {
    -        if (strncmp(resp, ZEND_STRL("+QUEUED")) == 0) {
    +        if (redis_strncmp(resp, ZEND_STRL("+QUEUED")) == 0) {
                 ret = SUCCESS;
             }
             efree(resp);
    @@ -2069,7 +2069,7 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
             char inbuf[255];
     
             if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
    -            strncmp(inbuf, ZEND_STRL("+OK")) != 0)
    +            redis_strncmp(inbuf, ZEND_STRL("+OK")) != 0)
             {
                 return FAILURE;
             }
    diff --git a/redis_array_impl.c b/redis_array_impl.c
    index c7e335e88c..78b6d16789 100644
    --- a/redis_array_impl.c
    +++ b/redis_array_impl.c
    @@ -139,7 +139,7 @@ ra_find_name(const char *name) {
         for(p = ini_names; p;) {
             next = strchr(p, ',');
             if(next) {
    -            if(strncmp(p, name, next - p) == 0) {
    +            if(redis_strncmp(p, name, next - p) == 0) {
                     return 1;
                 }
             } else {
    @@ -283,7 +283,7 @@ RedisArray *ra_load_array(const char *name) {
             sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
             if ((z_data = zend_hash_str_find(Z_ARRVAL(z_tmp), name, name_len)) != NULL) {
                 consistent = Z_TYPE_P(z_data) == IS_STRING &&
    -                         strncmp(Z_STRVAL_P(z_data), ZEND_STRL("1")) == 0;
    +                         redis_strncmp(Z_STRVAL_P(z_data), ZEND_STRL("1")) == 0;
             }
             zval_dtor(&z_tmp);
         }
    diff --git a/redis_session.c b/redis_session.c
    index 8abdbe8191..c355704f2f 100644
    --- a/redis_session.c
    +++ b/redis_session.c
    @@ -336,7 +336,7 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s
         return lock_status->is_locked ? SUCCESS : FAILURE;
     }
     
    -#define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !strncmp(reply, ZSTR_VAL(secret), len))
    +#define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !redis_strncmp(reply, ZSTR_VAL(secret), len))
     static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status)
     {
         if (!INI_INT("redis.session.locking_enabled")) {
    @@ -444,7 +444,7 @@ PS_OPEN_FUNC(redis)
                 zend_string *user = NULL, *pass = NULL;
     
                 /* translate unix: into file: */
    -            if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) {
    +            if (!redis_strncmp(save_path+i, ZEND_STRL("unix:"))) {
                     int len = j-i;
                     char *path = estrndup(save_path+i, len);
                     memcpy(path, "file:", sizeof("file:")-1);
    
    From 0fe45d24d4d8c115a5b52846be072ecb9bb43329 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Wed, 16 Oct 2024 15:40:17 -0700
    Subject: [PATCH 1922/1986] Fix XAUTOCLAIM argc when sending COUNT
    
    Add 2 to argc not 1 + count when sending a specific COUNT.
    ---
     redis_commands.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/redis_commands.c b/redis_commands.c
    index 68572efc08..9a1dd74bb2 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -5795,7 +5795,7 @@ redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             return FAILURE;
         }
     
    -    argc = 5 + (count > 0 ? 1 + count : 0) + justid;
    +    argc = 5 + (count > 0 ? 2 : 0) + justid;
     
         REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XAUTOCLAIM");
         redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot);
    
    From 8144db374338006a316beb11549f37926bd40c5d Mon Sep 17 00:00:00 2001
    From: Jacob Brown 
    Date: Mon, 4 Nov 2024 12:03:42 -0600
    Subject: [PATCH 1923/1986] better documentation for the $tlsOptions parameter
     of RedisCluster
    
    ---
     cluster.md | 6 ++++--
     1 file changed, 4 insertions(+), 2 deletions(-)
    
    diff --git a/cluster.md b/cluster.md
    index fa1237d062..3949f88fae 100644
    --- a/cluster.md
    +++ b/cluster.md
    @@ -22,8 +22,10 @@ $obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true
     // Connect with cluster using password.
     $obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, "password");
     
    -// Connect with cluster using SSL/TLS
    -// last argument is an array with [SSL context](https://www.php.net/manual/en/context.ssl.php) options
    +// Connect with cluster using TLS
    +// last argument is an optional array with [SSL context options](https://www.php.net/manual/en/context.ssl.php) (TLS options)
    +// If value is array (even empty), it will connect via TLS.  If not, it will connect without TLS.
    +// Note: If the seeds start with "ssl:// or tls://", it will connect to the seeds via TLS, but the subsequent connections will connect without TLS if this value is null.  So, if your nodes require TLS, this value must be an array, even if empty.
     $obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, NULL, ["verify_peer" => false]);
     ```
     
    
    From 4cd3f59356582a65aec1cceed44741bd5d161d9e Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 14 Nov 2024 21:44:07 -0800
    Subject: [PATCH 1924/1986] Implement KeyDB's EXPIREMEMBER[AT] commands
    
    ---
     redis.c                        |  8 ++++++
     redis.stub.php                 | 22 +++++++++++++++
     redis_arginfo.h                | 29 ++++++++++++++++---
     redis_cluster.c                |  8 ++++++
     redis_cluster.stub.php         | 10 +++++++
     redis_cluster_arginfo.h        | 25 +++++++++++++++--
     redis_cluster_legacy_arginfo.h | 19 ++++++++++++-
     redis_commands.c               | 51 ++++++++++++++++++++++++++++++++++
     redis_commands.h               |  6 ++++
     redis_legacy_arginfo.h         | 19 ++++++++++++-
     tests/RedisTest.php            | 16 +++++++++++
     11 files changed, 204 insertions(+), 9 deletions(-)
    
    diff --git a/redis.c b/redis.c
    index e09b15f672..2c45840728 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -1379,6 +1379,14 @@ PHP_METHOD(Redis, pexpiretime) {
         REDIS_PROCESS_KW_CMD("PEXPIRETIME", redis_key_cmd, redis_long_response);
     }
     
    +PHP_METHOD(Redis, expiremember) {
    +    REDIS_PROCESS_CMD(expiremember, redis_long_response);
    +}
    +
    +PHP_METHOD(Redis, expirememberat) {
    +    REDIS_PROCESS_CMD(expirememberat, redis_long_response);
    +}
    +
     /* }}} */
     /* {{{ proto array Redis::lSet(string key, int index, string value) */
     PHP_METHOD(Redis, lSet) {
    diff --git a/redis.stub.php b/redis.stub.php
    index 68ac8fd7dd..e5e1279691 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1903,6 +1903,28 @@ public function hVals(string $key): Redis|array|false;
          */
         public function hscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool;
     
    +    /**
    +     * Set an expiration on a key member (KeyDB only).
    +     *
    +     * @see https://docs.keydb.dev/docs/commands/#expiremember
    +     *
    +     * @param string $key The key to expire
    +     * @param string $field The field to expire
    +     * @param string|null $unit The unit of the ttl (s, or ms).
    +     */
    +    public function expiremember(string $key, string $field, int $ttl, ?string $unit = null): Redis|int|false;
    +
    +    /**
    +     * Set an expiration on a key membert to a specific unix timestamp (KeyDB only).
    +     *
    +     * @see https://docs.keydb.dev/docs/commands/#expirememberat
    +     *
    +     * @param string $key The key to expire
    +     * @param string $field The field to expire
    +     * @param int $timestamp The unix timestamp to expire at.
    +     */
    +    public function expirememberat(string $key, string $field, int $timestamp): Redis|int|false;
    +
         /**
          * Increment a key's value, optionally by a specific amount.
          *
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index 182a18518c..27290dde78 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 1cc5fe0df8dfa7d95f2bc45c2383132a68629f24 */
    + * Stub hash: bacbe6b1d55da4ba6d370fff1090e8de0363c4c2 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -464,6 +464,19 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Red
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expiremember, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null")
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expirememberat, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
    +ZEND_END_ARG_INFO()
    +
     #define arginfo_class_Redis_incr arginfo_class_Redis_decr
     
     #define arginfo_class_Redis_incrBy arginfo_class_Redis_decrBy
    @@ -1270,6 +1283,8 @@ ZEND_METHOD(Redis, hSetNx);
     ZEND_METHOD(Redis, hStrLen);
     ZEND_METHOD(Redis, hVals);
     ZEND_METHOD(Redis, hscan);
    +ZEND_METHOD(Redis, expiremember);
    +ZEND_METHOD(Redis, expirememberat);
     ZEND_METHOD(Redis, incr);
     ZEND_METHOD(Redis, incrBy);
     ZEND_METHOD(Redis, incrByFloat);
    @@ -1526,6 +1541,8 @@ static const zend_function_entry class_Redis_methods[] = {
     	ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, expiremember, arginfo_class_Redis_expiremember, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, expirememberat, arginfo_class_Redis_expirememberat, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
    @@ -2027,12 +2044,16 @@ static zend_class_entry *register_class_Redis(void)
     	zend_string *const_OPT_BACKOFF_CAP_name = zend_string_init_interned("OPT_BACKOFF_CAP", sizeof("OPT_BACKOFF_CAP") - 1, 1);
     	zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_CAP_name, &const_OPT_BACKOFF_CAP_value, ZEND_ACC_PUBLIC, NULL);
     	zend_string_release(const_OPT_BACKOFF_CAP_name);
    -#if (PHP_VERSION_ID >= 80200)
    +#if (PHP_VERSION_ID >= 80000)
     
     
    -	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "auth", sizeof("auth") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
    +	zend_string *attribute_name_SensitiveParameter_func_auth_arg0_0 = zend_string_init_interned("SensitiveParameter", sizeof("SensitiveParameter") - 1, 1);
    +	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "auth", sizeof("auth") - 1), 0, attribute_name_SensitiveParameter_func_auth_arg0_0, 0);
    +	zend_string_release(attribute_name_SensitiveParameter_func_auth_arg0_0);
     
    -	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "migrate", sizeof("migrate") - 1), 7, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
    +	zend_string *attribute_name_SensitiveParameter_func_migrate_arg7_0 = zend_string_init_interned("SensitiveParameter", sizeof("SensitiveParameter") - 1, 1);
    +	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "migrate", sizeof("migrate") - 1), 7, attribute_name_SensitiveParameter_func_migrate_arg7_0, 0);
    +	zend_string_release(attribute_name_SensitiveParameter_func_migrate_arg7_0);
     #endif
     
     	return class_entry;
    diff --git a/redis_cluster.c b/redis_cluster.c
    index a19514f168..1106a42982 100644
    --- a/redis_cluster.c
    +++ b/redis_cluster.c
    @@ -1303,6 +1303,14 @@ PHP_METHOD(RedisCluster, getbit) {
     }
     /* }}} */
     
    +PHP_METHOD(RedisCluster, expiremember) {
    +    CLUSTER_PROCESS_CMD(expiremember, cluster_long_resp, 0);
    +}
    +
    +PHP_METHOD(RedisCluster, expirememberat) {
    +    CLUSTER_PROCESS_CMD(expiremember, cluster_long_resp, 0);
    +}
    +
     /* {{{ proto long RedisCluster::setbit(string key, long offset, bool onoff) */
     PHP_METHOD(RedisCluster, setbit) {
         CLUSTER_PROCESS_CMD(setbit, cluster_long_resp, 0);
    diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
    index 833d70949d..d5cab71f20 100644
    --- a/redis_cluster.stub.php
    +++ b/redis_cluster.stub.php
    @@ -495,6 +495,16 @@ public function hmset(string $key, array $key_values): RedisCluster|bool;
          */
         public function hscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): array|bool;
     
    +    /**
    +     * @see Redis::expiremember
    +     */
    +    public function expiremember(string $key, string $field, int $ttl, ?string $unit = null): Redis|int|false;
    +
    +    /**
    +     * @see Redis::expirememberat
    +     */
    +    public function expirememberat(string $key, string $field, int $timestamp): Redis|int|false;
    +
         /**
          * @see https://redis.io/commands/hrandfield
          */
    diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
    index ff9a281dde..85079322bf 100644
    --- a/redis_cluster_arginfo.h
    +++ b/redis_cluster_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 5713c5b2f88ddead50088f14026447801120fa33 */
    + * Stub hash: b9310b607794caa862d509ba316a2a512d2736fe */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
    @@ -416,6 +416,19 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2,
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiremember, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null")
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expirememberat, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
    +ZEND_END_ARG_INFO()
    +
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hrandfield, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -1135,6 +1148,8 @@ ZEND_METHOD(RedisCluster, hlen);
     ZEND_METHOD(RedisCluster, hmget);
     ZEND_METHOD(RedisCluster, hmset);
     ZEND_METHOD(RedisCluster, hscan);
    +ZEND_METHOD(RedisCluster, expiremember);
    +ZEND_METHOD(RedisCluster, expirememberat);
     ZEND_METHOD(RedisCluster, hrandfield);
     ZEND_METHOD(RedisCluster, hset);
     ZEND_METHOD(RedisCluster, hsetnx);
    @@ -1362,6 +1377,8 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, expiremember, arginfo_class_RedisCluster_expiremember, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, expirememberat, arginfo_class_RedisCluster_expirememberat, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hrandfield, arginfo_class_RedisCluster_hrandfield, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC)
    @@ -1541,10 +1558,12 @@ static zend_class_entry *register_class_RedisCluster(void)
     	zend_string *const_FAILOVER_DISTRIBUTE_SLAVES_name = zend_string_init_interned("FAILOVER_DISTRIBUTE_SLAVES", sizeof("FAILOVER_DISTRIBUTE_SLAVES") - 1, 1);
     	zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_SLAVES_name, &const_FAILOVER_DISTRIBUTE_SLAVES_value, ZEND_ACC_PUBLIC, NULL);
     	zend_string_release(const_FAILOVER_DISTRIBUTE_SLAVES_name);
    -#if (PHP_VERSION_ID >= 80200)
    +#if (PHP_VERSION_ID >= 80000)
     
     
    -	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 5, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
    +	zend_string *attribute_name_SensitiveParameter_func___construct_arg5_0 = zend_string_init_interned("SensitiveParameter", sizeof("SensitiveParameter") - 1, 1);
    +	zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 5, attribute_name_SensitiveParameter_func___construct_arg5_0, 0);
    +	zend_string_release(attribute_name_SensitiveParameter_func___construct_arg5_0);
     #endif
     
     	return class_entry;
    diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
    index a3cb82d386..64d695108d 100644
    --- a/redis_cluster_legacy_arginfo.h
    +++ b/redis_cluster_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 5713c5b2f88ddead50088f14026447801120fa33 */
    + * Stub hash: b9310b607794caa862d509ba316a2a512d2736fe */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_INFO(0, name)
    @@ -368,6 +368,19 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hscan, 0, 0, 2)
     	ZEND_ARG_INFO(0, count)
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expiremember, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, field)
    +	ZEND_ARG_INFO(0, ttl)
    +	ZEND_ARG_INFO(0, unit)
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expirememberat, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, field)
    +	ZEND_ARG_INFO(0, timestamp)
    +ZEND_END_ARG_INFO()
    +
     #define arginfo_class_RedisCluster_hrandfield arginfo_class_RedisCluster_getex
     
     #define arginfo_class_RedisCluster_hset arginfo_class_RedisCluster_hincrby
    @@ -977,6 +990,8 @@ ZEND_METHOD(RedisCluster, hlen);
     ZEND_METHOD(RedisCluster, hmget);
     ZEND_METHOD(RedisCluster, hmset);
     ZEND_METHOD(RedisCluster, hscan);
    +ZEND_METHOD(RedisCluster, expiremember);
    +ZEND_METHOD(RedisCluster, expirememberat);
     ZEND_METHOD(RedisCluster, hrandfield);
     ZEND_METHOD(RedisCluster, hset);
     ZEND_METHOD(RedisCluster, hsetnx);
    @@ -1204,6 +1219,8 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, expiremember, arginfo_class_RedisCluster_expiremember, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, expirememberat, arginfo_class_RedisCluster_expirememberat, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hrandfield, arginfo_class_RedisCluster_hrandfield, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC)
    diff --git a/redis_commands.c b/redis_commands.c
    index 9a1dd74bb2..3084c569c8 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -6079,6 +6079,57 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return SUCCESS;
     }
     
    +static int
    +generic_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +                         char *kw, size_t kw_len, int has_unit, char **cmd,
    +                         int *cmd_len, short *slot)
    +{
    +    zend_string *key, *mem, *unit = NULL;
    +    smart_string cmdstr = {0};
    +    zend_long expiry;
    +
    +    ZEND_PARSE_PARAMETERS_START(3, has_unit ? 4 : 3)
    +        Z_PARAM_STR(key)
    +        Z_PARAM_STR(mem)
    +        Z_PARAM_LONG(expiry)
    +        if (has_unit) {
    +            Z_PARAM_OPTIONAL
    +            Z_PARAM_STR_OR_NULL(unit)
    +        }
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
    +
    +    redis_cmd_init_sstr(&cmdstr, 3 + (unit != NULL), kw, kw_len);
    +    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
    +    redis_cmd_append_sstr_zstr(&cmdstr, mem);
    +    redis_cmd_append_sstr_long(&cmdstr, expiry);
    +
    +    if (unit != NULL) {
    +        redis_cmd_append_sstr_zstr(&cmdstr, unit);
    +    }
    +
    +    *cmd = cmdstr.c;
    +    *cmd_len = cmdstr.len;
    +
    +    return SUCCESS;
    +}
    +
    +
    +int redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +                           char **cmd, int *cmd_len, short *slot, void **ctx)
    +{
    +    return generic_expiremember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
    +                                    redis_sock, ZEND_STRL("EXPIREMEMBER"), 1,
    +                                    cmd, cmd_len, slot);
    +}
    +
    +int redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +                             char **cmd, int *cmd_len, short *slot, void **ctx)
    +{
    +    return generic_expiremember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
    +                                    redis_sock, ZEND_STRL("EXPIREMEMBERAT"), 0,
    +                                    cmd, cmd_len, slot);
    +}
    +
     int
     redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                         char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
    diff --git a/redis_commands.h b/redis_commands.h
    index dfaa8fd0d2..ab3d89e2c1 100644
    --- a/redis_commands.h
    +++ b/redis_commands.h
    @@ -350,6 +350,12 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         char **cmd, int *cmd_len, short *slot, void **ctx);
     
    +int redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +    char **cmd, int *cmd_len, short *slot, void **ctx);
    +
    +int redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +    char **cmd, int *cmd_len, short *slot, void **ctx);
    +
     int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
     
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 524aa5ad93..83b9f30057 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 1cc5fe0df8dfa7d95f2bc45c2383132a68629f24 */
    + * Stub hash: bacbe6b1d55da4ba6d370fff1090e8de0363c4c2 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    @@ -413,6 +413,19 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2)
     	ZEND_ARG_INFO(0, count)
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expiremember, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, field)
    +	ZEND_ARG_INFO(0, ttl)
    +	ZEND_ARG_INFO(0, unit)
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expirememberat, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, field)
    +	ZEND_ARG_INFO(0, timestamp)
    +ZEND_END_ARG_INFO()
    +
     #define arginfo_class_Redis_incr arginfo_class_Redis_decr
     
     #define arginfo_class_Redis_incrBy arginfo_class_Redis_append
    @@ -1113,6 +1126,8 @@ ZEND_METHOD(Redis, hSetNx);
     ZEND_METHOD(Redis, hStrLen);
     ZEND_METHOD(Redis, hVals);
     ZEND_METHOD(Redis, hscan);
    +ZEND_METHOD(Redis, expiremember);
    +ZEND_METHOD(Redis, expirememberat);
     ZEND_METHOD(Redis, incr);
     ZEND_METHOD(Redis, incrBy);
     ZEND_METHOD(Redis, incrByFloat);
    @@ -1369,6 +1384,8 @@ static const zend_function_entry class_Redis_methods[] = {
     	ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, expiremember, arginfo_class_Redis_expiremember, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, expirememberat, arginfo_class_Redis_expirememberat, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC)
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index a33a062f0f..6ada4dcd8e 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -716,6 +716,22 @@ public function testMultipleBin() {
                                 $this->redis->mget(array_keys($kvals)));
         }
     
    +    public function testExpireMember() {
    +        if ( ! $this->is_keydb)
    +            $this->markTestSkipped();
    +
    +        $this->redis->del('h');
    +        $this->redis->hmset('h', ['f1' => 'v1', 'f2' => 'v2', 'f3' => 'v3', 'f4' => 'v4']);
    +
    +        $this->assertEquals(1, $this->redis->expiremember('h', 'f1', 1));
    +        $this->assertEquals(1, $this->redis->expiremember('h', 'f2', 1000, 'ms'));
    +        $this->assertEquals(1, $this->redis->expiremember('h', 'f3', 1000,  null));
    +        $this->assertEquals(0, $this->redis->expiremember('h', 'nk', 10));
    +
    +        $this->assertEquals(1, $this->redis->expirememberat('h', 'f4', time() + 1));
    +        $this->assertEquals(0, $this->redis->expirememberat('h', 'nk', time() + 1));
    +    }
    +
         public function testExpire() {
             $this->redis->del('key');
             $this->redis->set('key', 'value');
    
    From 6097e7ba50c0a300bc4f420f84c5d2665ef99d90 Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Mon, 25 Nov 2024 16:20:00 +0200
    Subject: [PATCH 1925/1986] Add PHP 8.4 to CI
    
    ---
     .github/workflows/ci.yml | 6 +++---
     1 file changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index 0d9cb2a84f..569919c563 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -73,7 +73,7 @@ jobs:
         strategy:
           fail-fast: false
           matrix:
    -        php: ['7.4', '8.0', '8.1', '8.2', '8.3']
    +        php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
             server: ['redis', 'keydb', 'valkey']
     
         steps:
    @@ -232,7 +232,7 @@ jobs:
         strategy:
           fail-fast: false
           matrix:
    -        php: ['7.4', '8.0', '8.1', '8.2', '8.3']
    +        php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
         steps:
           - name: Checkout
             uses: actions/checkout@v4
    @@ -262,7 +262,7 @@ jobs:
         strategy:
           fail-fast: false
           matrix:
    -        php: ['8.0', '8.1', '8.2', '8.3']
    +        php: ['8.0', '8.1', '8.2', '8.3', '8.4']
             ts: [nts, ts]
         steps:
           - name: Checkout
    
    From b665925eeddfdf6a6fc1de471c0789ffb60cd067 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sat, 23 Nov 2024 12:07:21 +0100
    Subject: [PATCH 1926/1986] Use smart str for constructing pipeline cmd
    
    ---
     common.h  | 10 ++--------
     library.c |  4 +---
     redis.c   | 14 +++++---------
     3 files changed, 8 insertions(+), 20 deletions(-)
    
    diff --git a/common.h b/common.h
    index 0d19d4c0e8..ad74a0f453 100644
    --- a/common.h
    +++ b/common.h
    @@ -177,13 +177,7 @@ typedef enum {
     #define IS_PIPELINE(redis_sock) (redis_sock->mode & PIPELINE)
     
     #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \
    -    if (redis_sock->pipeline_cmd == NULL) { \
    -        redis_sock->pipeline_cmd = zend_string_init(cmd, cmd_len, 0); \
    -    } else { \
    -        size_t pipeline_len = ZSTR_LEN(redis_sock->pipeline_cmd); \
    -        redis_sock->pipeline_cmd = zend_string_realloc(redis_sock->pipeline_cmd, pipeline_len + cmd_len, 0); \
    -        memcpy(&ZSTR_VAL(redis_sock->pipeline_cmd)[pipeline_len], cmd, cmd_len); \
    -    } \
    +    smart_str_appendl(&redis_sock->pipeline_cmd, cmd, cmd_len); \
     } while (0)
     
     #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \
    @@ -324,7 +318,7 @@ typedef struct {
         struct fold_item    *head;
         struct fold_item    *current;
     
    -    zend_string         *pipeline_cmd;
    +    smart_str           pipeline_cmd;
     
         zend_string         *err;
     
    diff --git a/library.c b/library.c
    index 40d888ce74..d5e1fad924 100644
    --- a/library.c
    +++ b/library.c
    @@ -3605,9 +3605,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
         if (redis_sock->prefix) {
             zend_string_release(redis_sock->prefix);
         }
    -    if (redis_sock->pipeline_cmd) {
    -        zend_string_release(redis_sock->pipeline_cmd);
    -    }
    +    smart_str_free(&redis_sock->pipeline_cmd);
         if (redis_sock->err) {
             zend_string_release(redis_sock->err);
         }
    diff --git a/redis.c b/redis.c
    index 2c45840728..eab8534e01 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -1948,10 +1948,7 @@ PHP_METHOD(Redis, discard)
     
         if (IS_PIPELINE(redis_sock)) {
             ret = SUCCESS;
    -        if (redis_sock->pipeline_cmd) {
    -            zend_string_release(redis_sock->pipeline_cmd);
    -            redis_sock->pipeline_cmd = NULL;
    -        }
    +        smart_str_free(&redis_sock->pipeline_cmd);
         } else if (IS_MULTI(redis_sock)) {
             ret = redis_send_discard(redis_sock);
         }
    @@ -2022,12 +2019,12 @@ PHP_METHOD(Redis, exec)
         }
     
         if (IS_PIPELINE(redis_sock)) {
    -        if (redis_sock->pipeline_cmd == NULL) {
    +        if (smart_str_get_len(&redis_sock->pipeline_cmd) == 0) {
                 /* Empty array when no command was run. */
                 array_init(&z_ret);
             } else {
    -            if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd),
    -                    ZSTR_LEN(redis_sock->pipeline_cmd)) < 0) {
    +            if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd.s),
    +                    ZSTR_LEN(redis_sock->pipeline_cmd.s)) < 0) {
                     ZVAL_FALSE(&z_ret);
                 } else {
                     array_init(&z_ret);
    @@ -2037,8 +2034,7 @@ PHP_METHOD(Redis, exec)
                         ZVAL_FALSE(&z_ret);
                     }
                 }
    -            zend_string_release(redis_sock->pipeline_cmd);
    -            redis_sock->pipeline_cmd = NULL;
    +            smart_str_free(&redis_sock->pipeline_cmd);
             }
             free_reply_callbacks(redis_sock);
             REDIS_DISABLE_MODE(redis_sock, PIPELINE);
    
    From 99beb9221c815018f1d076654b033cafac22a6ce Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Thu, 21 Nov 2024 12:27:16 +0100
    Subject: [PATCH 1927/1986] Initialize arrays with known size
    
    We know in advance the array size, so it makes sense to avoid reallocation when adding new elements. Also use immutable empty array in when we know in advance that redis will return zero elements.
    ---
     library.c | 24 +++++++++++++++---------
     1 file changed, 15 insertions(+), 9 deletions(-)
    
    diff --git a/library.c b/library.c
    index d5e1fad924..3bcb66d2a2 100644
    --- a/library.c
    +++ b/library.c
    @@ -3350,8 +3350,10 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
         }
         if (numElems == -1 && redis_sock->null_mbulk_as_null) {
             ZVAL_NULL(&z_multi_result);
    +    } else if (numElems < 1) {
    +        ZVAL_EMPTY_ARRAY(&z_multi_result);
         } else {
    -        array_init(&z_multi_result);
    +        array_init_size(&z_multi_result, numElems);
             redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL);
         }
     
    @@ -3380,7 +3382,7 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
             return FAILURE;
         }
         zval z_multi_result;
    -    array_init(&z_multi_result); /* pre-allocate array for multi's results. */
    +    array_init_size(&z_multi_result, numElems); /* pre-allocate array for multi's results. */
     
         redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE);
     
    @@ -3409,14 +3411,18 @@ redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
             return FAILURE;
         }
     
    -    array_init(&z_multi_result);
    -    for (i = 0; i < numElems; ++i) {
    -        if ((line = redis_sock_read(redis_sock, &len)) == NULL) {
    -            add_next_index_bool(&z_multi_result, 0);
    -            continue;
    +    if (numElems < 1) {
    +        ZVAL_EMPTY_ARRAY(&z_multi_result);
    +    } else {
    +        array_init_size(&z_multi_result, numElems);
    +        for (i = 0; i < numElems; ++i) {
    +            if ((line = redis_sock_read(redis_sock, &len)) == NULL) {
    +                add_next_index_bool(&z_multi_result, 0);
    +                continue;
    +            }
    +            add_next_index_double(&z_multi_result, atof(line));
    +            efree(line);
             }
    -        add_next_index_double(&z_multi_result, atof(line));
    -        efree(line);
         }
     
         if (IS_ATOMIC(redis_sock)) {
    
    From 64da891e6fe5810b1aa2a47bc0632a2cd346659d Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Thu, 21 Nov 2024 15:29:34 +0100
    Subject: [PATCH 1928/1986] Do not allocate empty string or string with one
     character
    
    From PHP 8, empty strings or string with one character don't need to be allocated. This change will reduce memory usage.
    ---
     library.c | 15 +++++++++++----
     library.h |  3 +++
     2 files changed, 14 insertions(+), 4 deletions(-)
    
    diff --git a/library.c b/library.c
    index 3bcb66d2a2..612335ab13 100644
    --- a/library.c
    +++ b/library.c
    @@ -103,6 +103,13 @@ void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id) {
         zend_register_persistent_resource(ZSTR_VAL(id), ZSTR_LEN(id), ptr, le_id);
     }
     
    +/* Do not allocate empty string or string with one character */
    +static zend_always_inline void redis_add_next_index_stringl(zval *arg, const char *str, size_t length) {
    +    zval tmp;
    +    ZVAL_STRINGL_FAST(&tmp, str, length);
    +    zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp);
    +}
    +
     static ConnectionPool *
     redis_sock_get_connection_pool(RedisSock *redis_sock)
     {
    @@ -2666,14 +2673,14 @@ PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
         }
         if (IS_ATOMIC(redis_sock)) {
             if (!redis_unpack(redis_sock, response, response_len, return_value)) {
    -            RETVAL_STRINGL(response, response_len);
    +            RETVAL_STRINGL_FAST(response, response_len);
             }
         } else {
             zval z_unpacked;
             if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) {
                 add_next_index_zval(z_tab, &z_unpacked);
             } else {
    -            add_next_index_stringl(z_tab, response, response_len);
    +            redis_add_next_index_stringl(z_tab, response, response_len);
             }
         }
     
    @@ -3460,7 +3467,7 @@ redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count,
             if (unwrap && redis_unpack(redis_sock, line, len, &z_unpacked)) {
                 add_next_index_zval(z_tab, &z_unpacked);
             } else {
    -            add_next_index_stringl(z_tab, line, len);
    +            redis_add_next_index_stringl(z_tab, line, len);
             }
             efree(line);
         }
    @@ -3882,7 +3889,7 @@ redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) {
         /* Uncompress, then unserialize */
         if (redis_uncompress(redis_sock, &buf, &len, src, srclen)) {
             if (!redis_unserialize(redis_sock, buf, len, zdst)) {
    -            ZVAL_STRINGL(zdst, buf, len);
    +            ZVAL_STRINGL_FAST(zdst, buf, len);
             }
             efree(buf);
             return 1;
    diff --git a/library.h b/library.h
    index f758c33bc2..00d7f05288 100644
    --- a/library.h
    +++ b/library.h
    @@ -29,6 +29,9 @@
         /* use RedisException when ValueError not available */
         #define REDIS_VALUE_EXCEPTION(m) REDIS_THROW_EXCEPTION(m, 0)
         #define RETURN_THROWS() RETURN_FALSE
    +    /* ZVAL_STRINGL_FAST and RETVAL_STRINGL_FAST macros are supported since PHP 8 */
    +    #define ZVAL_STRINGL_FAST(z, s, l) ZVAL_STRINGL(z, s, l)
    +    #define RETVAL_STRINGL_FAST(s, l) RETVAL_STRINGL(s, l)
     #else
         #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(zstr)
     
    
    From 60b5a8860ae3ff2d02d7f06cc6f86b59cb53b2cf Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Fri, 22 Nov 2024 16:51:06 +0100
    Subject: [PATCH 1929/1986] Use immutable empty array in Redis::exec
    
    ---
     redis.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/redis.c b/redis.c
    index eab8534e01..a5e1d1ad94 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -2021,7 +2021,7 @@ PHP_METHOD(Redis, exec)
         if (IS_PIPELINE(redis_sock)) {
             if (smart_str_get_len(&redis_sock->pipeline_cmd) == 0) {
                 /* Empty array when no command was run. */
    -            array_init(&z_ret);
    +            ZVAL_EMPTY_ARRAY(&z_ret);
             } else {
                 if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd.s),
                         ZSTR_LEN(redis_sock->pipeline_cmd.s)) < 0) {
    
    From 3a2f3f45fc7bb01d1be2b9d97cf9d8bff0b0e818 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Fri, 22 Nov 2024 16:52:22 +0100
    Subject: [PATCH 1930/1986] Use immutable empty array in Redis::hKeys
    
    ---
     library.c | 8 ++++++--
     1 file changed, 6 insertions(+), 2 deletions(-)
    
    diff --git a/library.c b/library.c
    index 612335ab13..3c699b0c64 100644
    --- a/library.c
    +++ b/library.c
    @@ -3389,9 +3389,13 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
             return FAILURE;
         }
         zval z_multi_result;
    -    array_init_size(&z_multi_result, numElems); /* pre-allocate array for multi's results. */
     
    -    redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE);
    +    if (numElems < 1) {
    +        ZVAL_EMPTY_ARRAY(&z_multi_result);
    +    } else {
    +        array_init_size(&z_multi_result, numElems); /* pre-allocate array for multi's results. */
    +        redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE);
    +    }
     
         if (IS_ATOMIC(redis_sock)) {
             RETVAL_ZVAL(&z_multi_result, 0, 1);
    
    From 83a19656f49aec8f354596099dbf97ba7375d7af Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Fri, 22 Nov 2024 17:47:17 +0100
    Subject: [PATCH 1931/1986] Faster parameter parsing in redis_key_cmd and
     redis_key_long_val_cmd
    
    ---
     redis_commands.c | 18 ++++++++----------
     1 file changed, 8 insertions(+), 10 deletions(-)
    
    diff --git a/redis_commands.c b/redis_commands.c
    index 3084c569c8..28597250a4 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -268,11 +268,11 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         zend_long expire;
         zval *z_val;
     
    -    if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &key, &key_len,
    -                             &expire, &z_val) == FAILURE)
    -    {
    -        return FAILURE;
    -    }
    +    ZEND_PARSE_PARAMETERS_START(3, 3)
    +        Z_PARAM_STRING(key, key_len)
    +        Z_PARAM_LONG(expire)
    +        Z_PARAM_ZVAL(z_val)
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
     
         *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "klv", key, key_len, expire, z_val);
     
    @@ -451,11 +451,9 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         char *key;
         size_t key_len;
     
    -    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len)
    -                             ==FAILURE)
    -    {
    -        return FAILURE;
    -    }
    +    ZEND_PARSE_PARAMETERS_START(1, 1)
    +        Z_PARAM_STRING(key, key_len);
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
     
         *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "k", key, key_len);
     
    
    From 400503b8718104b766ceb4a0b84e4a446dbee09b Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Fri, 22 Nov 2024 19:27:00 +0100
    Subject: [PATCH 1932/1986] Optimise method array_zip_values_and_scores
    
    Initialize array with know size and reuse already allocated keys
    ---
     library.c | 30 ++++++++++++++++--------------
     1 file changed, 16 insertions(+), 14 deletions(-)
    
    diff --git a/library.c b/library.c
    index 3c699b0c64..32d5e9dd5b 100644
    --- a/library.c
    +++ b/library.c
    @@ -1687,10 +1687,9 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab,
     {
     
         zval z_ret, z_sub;
    -    HashTable *keytable;
    +    HashTable *keytable = Z_ARRVAL_P(z_tab);
     
    -    array_init(&z_ret);
    -    keytable = Z_ARRVAL_P(z_tab);
    +    array_init_size(&z_ret, zend_hash_num_elements(keytable) / 2);
     
         for(zend_hash_internal_pointer_reset(keytable);
             zend_hash_has_more_elements(keytable) == SUCCESS;
    @@ -1703,14 +1702,13 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab,
             }
     
             /* get current value, a key */
    -        zend_string *hkey = zval_get_string(z_key_p);
    +        zend_string *hkey = Z_STR_P(z_key_p);
     
             /* move forward */
             zend_hash_move_forward(keytable);
     
             /* fetch again */
             if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) {
    -            zend_string_release(hkey);
                 continue;   /* this should never happen, according to the PHP people. */
             }
     
    @@ -1719,14 +1717,13 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab,
     
             /* Decode the score depending on flag */
             if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) {
    -            add_assoc_long_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atoi(hval+1));
    +            ZVAL_LONG(&z_sub, atoi(hval+1));
             } else if (decode == SCORE_DECODE_DOUBLE) {
    -            add_assoc_double_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atof(hval));
    +            ZVAL_DOUBLE(&z_sub, atof(hval));
             } else {
                 ZVAL_ZVAL(&z_sub, z_value_p, 1, 0);
    -            add_assoc_zval_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), &z_sub);
             }
    -        zend_string_release(hkey);
    +        zend_symtable_update(Z_ARRVAL_P(&z_ret), hkey, &z_sub);
         }
     
         /* replace */
    @@ -1794,13 +1791,18 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             return FAILURE;
         }
         zval z_multi_result;
    -    array_init(&z_multi_result); /* pre-allocate array for multi's results. */
     
    -    /* Grab our key, value, key, value array */
    -    redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize);
    +    if (numElems < 1) {
    +        ZVAL_EMPTY_ARRAY(&z_multi_result);
    +    } else {
    +        array_init_size(&z_multi_result, numElems); /* pre-allocate array for multi's results. */
    +
    +        /* Grab our key, value, key, value array */
    +        redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize);
     
    -    /* Zip keys and values */
    -    array_zip_values_and_scores(redis_sock, &z_multi_result, decode);
    +        /* Zip keys and values */
    +        array_zip_values_and_scores(redis_sock, &z_multi_result, decode);
    +    }
     
         if (IS_ATOMIC(redis_sock)) {
             RETVAL_ZVAL(&z_multi_result, 0, 1);
    
    From 426de2bb71372f665f5a5bb5a779a7b9c586892d Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sun, 24 Nov 2024 17:15:58 +0100
    Subject: [PATCH 1933/1986] Test for empty pipeline and multi
    
    ---
     tests/RedisTest.php | 16 ++++++++++++++++
     1 file changed, 16 insertions(+)
    
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 6ada4dcd8e..61eecd724d 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -3456,6 +3456,22 @@ public function testPipelineMultiExec() {
             $this->assertEquals(5, count($ret)); // should be 5 atomic operations
         }
     
    +    public function testMultiEmpty()
    +    {
    +        $ret = $this->redis->multi()->exec();
    +        $this->assertEquals([], $ret);
    +    }
    +
    +    public function testPipelineEmpty()
    +    {
    +        if (!$this->havePipeline()) {
    +            $this->markTestSkipped();
    +        }
    +
    +        $ret = $this->redis->pipeline()->exec();
    +        $this->assertEquals([], $ret);
    +    }
    +
         /* GitHub issue #1211 (ignore redundant calls to pipeline or multi) */
         public function testDoublePipeNoOp() {
             /* Only the first pipeline should be honored */
    
    From 5156e0320242ff05f327a3801667140069688c0e Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sun, 24 Nov 2024 17:17:48 +0100
    Subject: [PATCH 1934/1986] If no command is issued in multi mode, return
     immutable empty array
    
    ---
     redis.c | 6 ++++++
     1 file changed, 6 insertions(+)
    
    diff --git a/redis.c b/redis.c
    index a5e1d1ad94..66646bb61a 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -1974,6 +1974,12 @@ redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
             return FAILURE;
         }
     
    +    // No command issued, return empty immutable array
    +    if (redis_sock->head == NULL) {
    +        ZVAL_EMPTY_ARRAY(z_tab);
    +        return SUCCESS;
    +    }
    +
         array_init(z_tab);
     
         return redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
    
    From 2a2f908f2b6b695a0e6705200160e592802f0e41 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Mon, 25 Nov 2024 14:53:53 +0100
    Subject: [PATCH 1935/1986] Optimise constructing Redis command string
    
    Instead of snprintf method, use zend_print_long_to_buf that can be inlined
    ---
     library.c | 18 +++++++++++-------
     1 file changed, 11 insertions(+), 7 deletions(-)
    
    diff --git a/library.c b/library.c
    index 32d5e9dd5b..b1ccb7c8e1 100644
    --- a/library.c
    +++ b/library.c
    @@ -1044,9 +1044,7 @@ int redis_cmd_append_sstr(smart_string *str, char *append, int append_len) {
      * Append an integer to a smart string command
      */
     int redis_cmd_append_sstr_int(smart_string *str, int append) {
    -    char int_buf[32];
    -    int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append);
    -    return redis_cmd_append_sstr(str, int_buf, int_len);
    +    return redis_cmd_append_sstr_long(str, (long) append);
     }
     
     /*
    @@ -1054,8 +1052,9 @@ int redis_cmd_append_sstr_int(smart_string *str, int append) {
      */
     int redis_cmd_append_sstr_long(smart_string *str, long append) {
         char long_buf[32];
    -    int long_len = snprintf(long_buf, sizeof(long_buf), "%ld", append);
    -    return redis_cmd_append_sstr(str, long_buf, long_len);
    +    char *result = zend_print_long_to_buf(long_buf + sizeof(long_buf) - 1, append);
    +    int int_len = long_buf + sizeof(long_buf) - 1 - result;
    +    return redis_cmd_append_sstr(str, result, int_len);
     }
     
     /*
    @@ -3937,10 +3936,15 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
                         break;
     
                     default: { /* copy */
    -                    zend_string *zstr = zval_get_string(z);
    +                    zend_string *zstr = zval_get_string_func(z);
    +                    if (ZSTR_IS_INTERNED(zstr)) { // do not reallocate interned strings
    +                        *val = ZSTR_VAL(zstr);
    +                        *val_len = ZSTR_LEN(zstr);
    +                        return 0;
    +                    }
                         *val = estrndup(ZSTR_VAL(zstr), ZSTR_LEN(zstr));
                         *val_len = ZSTR_LEN(zstr);
    -                    zend_string_release(zstr);
    +                    zend_string_efree(zstr);
                         return 1;
                     }
                 }
    
    From f6906470a52e2d24b1e1b9f2574726643edd7a64 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sat, 23 Nov 2024 17:02:53 +0100
    Subject: [PATCH 1936/1986] Use zval_get_tmp_string method that is faster when
     provided zval is string
    
    ---
     library.c        | 16 ++++++++--------
     redis_commands.c | 12 +++---------
     2 files changed, 11 insertions(+), 17 deletions(-)
    
    diff --git a/library.c b/library.c
    index b1ccb7c8e1..1cf21b738a 100644
    --- a/library.c
    +++ b/library.c
    @@ -1089,7 +1089,7 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value)
      * the value may be serialized, if we're configured to do that. */
     int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock) {
         int valfree, retval;
    -    zend_string *zstr;
    +    zend_string *zstr, *tmp;
         size_t vallen;
         char *val;
     
    @@ -1098,9 +1098,9 @@ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock
             retval = redis_cmd_append_sstr(str, val, vallen);
             if (valfree) efree(val);
         } else {
    -        zstr = zval_get_string(z);
    -        retval = redis_cmd_append_sstr_zstr(str, zstr);
    -        zend_string_release(zstr);
    +        zstr = zval_get_tmp_string(z, &tmp);
    +        retval = redis_cmd_append_sstr(str, ZSTR_VAL(zstr), ZSTR_LEN(zstr));
    +        zend_tmp_string_release(tmp);
         }
     
         return retval;
    @@ -1128,12 +1128,12 @@ int redis_cmd_append_sstr_key_zstr(smart_string *dst, zend_string *key, RedisSoc
     }
     
     int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot) {
    -    zend_string *key;
    +    zend_string *key, *tmp;
         int res;
     
    -    key = zval_get_string(zv);
    -    res = redis_cmd_append_sstr_key_zstr(dst, key, redis_sock, slot);
    -    zend_string_release(key);
    +    key = zval_get_tmp_string(zv, &tmp);
    +    res = redis_cmd_append_sstr_key(dst, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot);
    +    zend_tmp_string_release(tmp);
     
         return res;
     }
    diff --git a/redis_commands.c b/redis_commands.c
    index 28597250a4..d5dddbd51a 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -3482,9 +3482,7 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL(z_args[1])) * 2, ZEND_STRL("HSET"));
     
             /* Append key */
    -        zkey = zval_get_string(&z_args[0]);
    -        redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, slot);
    -        zend_string_release(zkey);
    +        redis_cmd_append_sstr_key_zval(&cmdstr, &z_args[0], redis_sock, slot);
     
             ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) {
                 if (zkey != NULL) {
    @@ -3502,15 +3500,11 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HSET"));
     
             /* Append key */
    -        zkey = zval_get_string(&z_args[0]);
    -        redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, slot);
    -        zend_string_release(zkey);
    +        redis_cmd_append_sstr_key_zval(&cmdstr, &z_args[0], redis_sock, slot);
     
             for (i = 1; i < argc; ++i) {
                 if (i % 2) {
    -                zkey = zval_get_string(&z_args[i]);
    -                redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey));
    -                zend_string_release(zkey);
    +                redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], NULL);
                 } else {
                     redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock);
                 }
    
    From 99650e15453f03b5dd99284548514551fde4c812 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sun, 24 Nov 2024 11:19:46 +0100
    Subject: [PATCH 1937/1986] Avoid unnecessary allocation in
     redis_key_varval_cmd
    
    This will slightly reduce memory usage for commands like RPUSH, LPUSH, SADD, SREM, etc
    ---
     redis_commands.c | 34 +++++++++++-----------------------
     1 file changed, 11 insertions(+), 23 deletions(-)
    
    diff --git a/redis_commands.c b/redis_commands.c
    index d5dddbd51a..5c4ef62e7c 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -1778,44 +1778,32 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                              char *kw, char **cmd, int *cmd_len, short *slot,
                              void **ctx)
     {
    -    zval *z_args;
    +    zval *args = NULL;
    +    zend_string *key = NULL;
         smart_string cmdstr = {0};
         size_t i;
    -    int argc = ZEND_NUM_ARGS();
    -
    -    // We at least need a key and one value
    -    if (argc < 2) {
    -        zend_wrong_param_count();
    -        return FAILURE;
    -    }
    +    int argc = 0;
     
    -    // Make sure we at least have a key, and we can get other args
    -    z_args = emalloc(argc * sizeof(zval));
    -    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
    -        efree(z_args);
    -        return FAILURE;
    -    }
    +    ZEND_PARSE_PARAMETERS_START(2, -1)
    +        Z_PARAM_STR(key)
    +        Z_PARAM_VARIADIC('*', args, argc)
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
     
         /* Initialize our command */
    -    redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw));
    +    redis_cmd_init_sstr(&cmdstr, argc + 1, kw, strlen(kw));
     
         /* Append key */
    -    zend_string *zstr = zval_get_string(&z_args[0]);
    -    redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot);
    -    zend_string_release(zstr);
    +    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
     
         /* Add members */
    -    for (i = 1; i < argc; i++ ){
    -        redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock);
    +    for (i = 0; i < argc; i++) {
    +        redis_cmd_append_sstr_zval(&cmdstr, &args[i], redis_sock);
         }
     
         // Push out values
         *cmd     = cmdstr.c;
         *cmd_len = cmdstr.len;
     
    -    // Cleanup arg array
    -    efree(z_args);
    -
         // Success!
         return SUCCESS;
     }
    
    From 4082dd07f714fd2f6a0918b1845eb46c403a9edd Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sun, 24 Nov 2024 12:48:44 +0100
    Subject: [PATCH 1938/1986] Avoid unnecessary allocation in redis_hdel_cmd
    
    This will slightly reduce memory usage for HDEL command
    ---
     redis_commands.c | 51 ++++++++++++------------------------------------
     1 file changed, 13 insertions(+), 38 deletions(-)
    
    diff --git a/redis_commands.c b/redis_commands.c
    index 5c4ef62e7c..ce92a27285 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -3867,57 +3867,32 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                        char **cmd, int *cmd_len, short *slot, void **ctx)
     {
    -    zval *z_args;
         smart_string cmdstr = {0};
    -    char *arg;
    -    int arg_free, i;
    -    size_t arg_len;
    -    int argc = ZEND_NUM_ARGS();
    -    zend_string *zstr;
    -
    -    // We need at least KEY and one member
    -    if (argc < 2) {
    -        return FAILURE;
    -    }
    -
    -    // Grab arguments as an array
    -    z_args = emalloc(argc * sizeof(zval));
    -    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
    -        efree(z_args);
    -        return FAILURE;
    -    }
    -
    -    // Get first argument (the key) as a string
    -    zstr = zval_get_string(&z_args[0]);
    -    arg = ZSTR_VAL(zstr);
    -    arg_len = ZSTR_LEN(zstr);
    +    zend_string *key = NULL;
    +    int i;
    +    int argc = 0;
    +    zval *args;
     
    -    // Prefix
    -    arg_free = redis_key_prefix(redis_sock, &arg, &arg_len);
    +    ZEND_PARSE_PARAMETERS_START(2, -1)
    +        Z_PARAM_STR(key)
    +        Z_PARAM_VARIADIC('*', args, argc)
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
     
         // Start command construction
    -    redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HDEL"));
    -    redis_cmd_append_sstr(&cmdstr, arg, arg_len);
    +    redis_cmd_init_sstr(&cmdstr, argc + 1, ZEND_STRL("HDEL"));
     
    -    // Set our slot, free key if we prefixed it
    -    CMD_SET_SLOT(slot,arg,arg_len);
    -    zend_string_release(zstr);
    -    if (arg_free) efree(arg);
    +    // Append key
    +    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
     
         // Iterate through the members we're removing
    -    for (i = 1; i < argc; i++) {
    -        zstr = zval_get_string(&z_args[i]);
    -        redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr));
    -        zend_string_release(zstr);
    +    for (i = 0; i < argc; i++) {
    +        redis_cmd_append_sstr_zval(&cmdstr, &args[i], NULL);
         }
     
         // Push out values
         *cmd     = cmdstr.c;
         *cmd_len = cmdstr.len;
     
    -    // Cleanup
    -    efree(z_args);
    -
         // Success!
         return SUCCESS;
     }
    
    From aba09933db05a1a36e947c6fa9dca9889c6a77ff Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sun, 24 Nov 2024 13:38:54 +0100
    Subject: [PATCH 1939/1986] Avoid unnecessary allocation in redis_hset_cmd
    
    This will slightly reduce memory usage for HSET command
    ---
     redis_commands.c | 45 ++++++++++++++++++---------------------------
     1 file changed, 18 insertions(+), 27 deletions(-)
    
    diff --git a/redis_commands.c b/redis_commands.c
    index ce92a27285..a3ccffa81e 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -3447,32 +3447,26 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     {
         int i, argc;
         smart_string cmdstr = {0};
    -    zend_string *zkey;
    -    zval *z_args, *z_ele;
    -
    -    if ((argc = ZEND_NUM_ARGS()) < 2) {
    -        return FAILURE;
    -    }
    +    zend_string *key, *zkey;
    +    zval *args, *z_ele;
     
    -    z_args = ecalloc(argc, sizeof(*z_args));
    -    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
    -        efree(z_args);
    -        return FAILURE;
    -    }
    +    ZEND_PARSE_PARAMETERS_START(2, -1)
    +        Z_PARAM_STR(key)
    +        Z_PARAM_VARIADIC('*', args, argc)
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
     
    -    if (argc == 2) {
    -        if (Z_TYPE(z_args[1]) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL(z_args[1])) == 0) {
    -            efree(z_args);
    +    if (argc == 1) {
    +        if (Z_TYPE_P(args) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(args)) == 0) {
                 return FAILURE;
             }
     
             /* Initialize our command */
    -        redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL(z_args[1])) * 2, ZEND_STRL("HSET"));
    +        redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL_P(args)) * 2, ZEND_STRL("HSET"));
     
             /* Append key */
    -        redis_cmd_append_sstr_key_zval(&cmdstr, &z_args[0], redis_sock, slot);
    +        redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
     
    -        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) {
    +        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(args), zkey, z_ele) {
                 if (zkey != NULL) {
                     ZVAL_DEREF(z_ele);
                     redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey));
    @@ -3480,21 +3474,21 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                 }
             } ZEND_HASH_FOREACH_END();
         } else {
    -        if (argc % 2 == 0) {
    -            efree(z_args);
    +        if (argc % 2 != 0) {
                 return FAILURE;
             }
    +
             /* Initialize our command */
    -        redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HSET"));
    +        redis_cmd_init_sstr(&cmdstr, argc + 1, ZEND_STRL("HSET"));
     
             /* Append key */
    -        redis_cmd_append_sstr_key_zval(&cmdstr, &z_args[0], redis_sock, slot);
    +        redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
     
    -        for (i = 1; i < argc; ++i) {
    +        for (i = 0; i < argc; ++i) {
                 if (i % 2) {
    -                redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], NULL);
    +                redis_cmd_append_sstr_zval(&cmdstr, &args[i], redis_sock);
                 } else {
    -                redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock);
    +                redis_cmd_append_sstr_zval(&cmdstr, &args[i], NULL);
                 }
             }
         }
    @@ -3503,9 +3497,6 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         *cmd = cmdstr.c;
         *cmd_len = cmdstr.len;
     
    -    // Cleanup arg array
    -    efree(z_args);
    -
         return SUCCESS;
     }
     
    
    From 2434ba294cbb3b2f5b4ee581c37056906902d0d9 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sun, 24 Nov 2024 18:22:02 +0100
    Subject: [PATCH 1940/1986] Optimise HMGET method
    
    Allocate output array to expected size and reuse already allocated string for output array keys
    ---
     library.c | 46 ++++++++++++++++++++++++----------------------
     1 file changed, 24 insertions(+), 22 deletions(-)
    
    diff --git a/library.c b/library.c
    index 1cf21b738a..347f6d72e3 100644
    --- a/library.c
    +++ b/library.c
    @@ -3534,7 +3534,7 @@ redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int coun
     PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
     {
         char *response;
    -    int response_len;
    +    int response_len, retval;
         int i, numElems;
     
         zval *z_keys = ctx;
    @@ -3545,44 +3545,46 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc
             } else {
                 add_next_index_bool(z_tab, 0);
             }
    -        goto failure;
    +        retval = FAILURE;
    +        goto end;
         }
    +
         zval z_multi_result;
    -    array_init(&z_multi_result); /* pre-allocate array for multi's results. */
    +    array_init_size(&z_multi_result, numElems); /* pre-allocate array for multi's results. */
     
         for(i = 0; i < numElems; ++i) {
    -        zend_string *zstr = zval_get_string(&z_keys[i]);
    +        zend_string *tmp_str;
    +        zend_string *zstr = zval_get_tmp_string(&z_keys[i], &tmp_str);
             response = redis_sock_read(redis_sock, &response_len);
    -        if(response != NULL) {
    -            zval z_unpacked;
    -            if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) {
    -                add_assoc_zval_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked);
    -            } else {
    -                add_assoc_stringl_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), response, response_len);
    +        zval z_unpacked;
    +        if (response != NULL) {
    +            if (!redis_unpack(redis_sock, response, response_len, &z_unpacked)) {
    +                ZVAL_STRINGL(&z_unpacked, response, response_len);
                 }
                 efree(response);
             } else {
    -            add_assoc_bool_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0);
    +            ZVAL_FALSE(&z_unpacked);
             }
    -        zend_string_release(zstr);
    -        zval_dtor(&z_keys[i]);
    +        zend_symtable_update(Z_ARRVAL(z_multi_result), zstr, &z_unpacked);
    +        zend_tmp_string_release(tmp_str);
         }
    -    efree(z_keys);
     
         if (IS_ATOMIC(redis_sock)) {
             RETVAL_ZVAL(&z_multi_result, 0, 1);
         } else {
             add_next_index_zval(z_tab, &z_multi_result);
         }
    -    return SUCCESS;
    -failure:
    -    if (z_keys != NULL) {
    -        for (i = 0; Z_TYPE(z_keys[i]) != IS_NULL; ++i) {
    -            zval_dtor(&z_keys[i]);
    -        }
    -        efree(z_keys);
    +
    +    retval = SUCCESS;
    +
    +end:
    +    // Cleanup z_keys
    +    for (i = 0; Z_TYPE(z_keys[i]) != IS_NULL; ++i) {
    +        zval_dtor(&z_keys[i]);
         }
    -    return FAILURE;
    +    efree(z_keys);
    +
    +    return retval;
     }
     
     /**
    
    From 7895636a3a7cd3cad396a83ebe3aa5fe0208f42d Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Wed, 27 Nov 2024 10:00:20 +0100
    Subject: [PATCH 1941/1986] Remove unused redis_debug_response method from
     library.c
    
    ---
     library.c | 61 -------------------------------------------------------
     1 file changed, 61 deletions(-)
    
    diff --git a/library.c b/library.c
    index 347f6d72e3..c82a6ff080 100644
    --- a/library.c
    +++ b/library.c
    @@ -2718,67 +2718,6 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         return SUCCESS;
     }
     
    -/* Response for DEBUG object which is a formatted single line reply */
    -PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    -                                        zval *z_tab, void *ctx)
    -{
    -    char *resp, *p, *p2, *p3, *p4;
    -    int is_numeric,  resp_len;
    -
    -    /* Add or return false if we can't read from the socket */
    -    if((resp = redis_sock_read(redis_sock, &resp_len))==NULL) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETURN_FALSE;
    -        }
    -        add_next_index_bool(z_tab, 0);
    -        return;
    -    }
    -
    -    zval z_result;
    -    array_init(&z_result);
    -
    -    /* Skip the '+' */
    -    p = resp + 1;
    -
    -    /* :  ... */
    -    while((p2 = strchr(p, ':'))!=NULL) {
    -        /* Null terminate at the ':' */
    -        *p2++ = '\0';
    -
    -        /* Null terminate at the space if we have one */
    -        if((p3 = strchr(p2, ' '))!=NULL) {
    -            *p3++ = '\0';
    -        } else {
    -            p3 = resp + resp_len;
    -        }
    -
    -        is_numeric = 1;
    -        for(p4=p2; *p4; ++p4) {
    -            if(*p4 < '0' || *p4 > '9') {
    -                is_numeric = 0;
    -                break;
    -            }
    -        }
    -
    -        /* Add our value */
    -        if(is_numeric) {
    -            add_assoc_long(&z_result, p, atol(p2));
    -        } else {
    -            add_assoc_string(&z_result, p, p2);
    -        }
    -
    -        p = p3;
    -    }
    -
    -    efree(resp);
    -
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_result, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_result);
    -    }
    -}
    -
     PHP_REDIS_API int
     redis_sock_configure(RedisSock *redis_sock, HashTable *opts)
     {
    
    From 571ffbc8e0a5da807a6cc4a2cc5aa90af72e23b0 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sun, 1 Dec 2024 09:36:21 +0100
    Subject: [PATCH 1942/1986] Switch pipeline_cmd from smart_str to smart_string
    
    As we don't need to extract zend_string from pipeline_cmd, we can use simple smart_string structure
    ---
     common.h  |  4 ++--
     library.c |  2 +-
     redis.c   | 10 +++++-----
     3 files changed, 8 insertions(+), 8 deletions(-)
    
    diff --git a/common.h b/common.h
    index ad74a0f453..5030d38c84 100644
    --- a/common.h
    +++ b/common.h
    @@ -177,7 +177,7 @@ typedef enum {
     #define IS_PIPELINE(redis_sock) (redis_sock->mode & PIPELINE)
     
     #define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \
    -    smart_str_appendl(&redis_sock->pipeline_cmd, cmd, cmd_len); \
    +    smart_string_appendl(&redis_sock->pipeline_cmd, cmd, cmd_len); \
     } while (0)
     
     #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \
    @@ -318,7 +318,7 @@ typedef struct {
         struct fold_item    *head;
         struct fold_item    *current;
     
    -    smart_str           pipeline_cmd;
    +    smart_string        pipeline_cmd;
     
         zend_string         *err;
     
    diff --git a/library.c b/library.c
    index c82a6ff080..a2509c0c57 100644
    --- a/library.c
    +++ b/library.c
    @@ -3564,7 +3564,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
         if (redis_sock->prefix) {
             zend_string_release(redis_sock->prefix);
         }
    -    smart_str_free(&redis_sock->pipeline_cmd);
    +    smart_string_free(&redis_sock->pipeline_cmd);
         if (redis_sock->err) {
             zend_string_release(redis_sock->err);
         }
    diff --git a/redis.c b/redis.c
    index 66646bb61a..530e0003e0 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -1948,7 +1948,7 @@ PHP_METHOD(Redis, discard)
     
         if (IS_PIPELINE(redis_sock)) {
             ret = SUCCESS;
    -        smart_str_free(&redis_sock->pipeline_cmd);
    +        smart_string_free(&redis_sock->pipeline_cmd);
         } else if (IS_MULTI(redis_sock)) {
             ret = redis_send_discard(redis_sock);
         }
    @@ -2025,12 +2025,12 @@ PHP_METHOD(Redis, exec)
         }
     
         if (IS_PIPELINE(redis_sock)) {
    -        if (smart_str_get_len(&redis_sock->pipeline_cmd) == 0) {
    +        if (redis_sock->pipeline_cmd.len == 0) {
                 /* Empty array when no command was run. */
                 ZVAL_EMPTY_ARRAY(&z_ret);
             } else {
    -            if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd.s),
    -                    ZSTR_LEN(redis_sock->pipeline_cmd.s)) < 0) {
    +            if (redis_sock_write(redis_sock, redis_sock->pipeline_cmd.c,
    +                    redis_sock->pipeline_cmd.len) < 0) {
                     ZVAL_FALSE(&z_ret);
                 } else {
                     array_init(&z_ret);
    @@ -2040,7 +2040,7 @@ PHP_METHOD(Redis, exec)
                         ZVAL_FALSE(&z_ret);
                     }
                 }
    -            smart_str_free(&redis_sock->pipeline_cmd);
    +            smart_string_free(&redis_sock->pipeline_cmd);
             }
             free_reply_callbacks(redis_sock);
             REDIS_DISABLE_MODE(redis_sock, PIPELINE);
    
    From be388562058a75ed8fd31926bb0e6a60e2d8cb08 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Sun, 1 Dec 2024 10:01:59 +0100
    Subject: [PATCH 1943/1986] Reuse redis_sock_append_auth method
    
    In library.c, there are currently two methods for constructing AUTH command, so we can reuse code from redis_sock_append_auth also in redis_sock_auth_cmd method
    ---
     library.c | 13 ++++---------
     1 file changed, 4 insertions(+), 9 deletions(-)
    
    diff --git a/library.c b/library.c
    index a2509c0c57..2cd06868f4 100644
    --- a/library.c
    +++ b/library.c
    @@ -223,19 +223,14 @@ redis_sock_free_auth(RedisSock *redis_sock) {
     
     PHP_REDIS_API char *
     redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen) {
    -    char *cmd;
    +    smart_string cmd = {0};
     
    -    /* AUTH requires at least a password */
    -    if (redis_sock->pass == NULL)
    +    if (redis_sock_append_auth(redis_sock, &cmd) == 0) {
             return NULL;
    -
    -    if (redis_sock->user) {
    -        *cmdlen = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "SS", redis_sock->user, redis_sock->pass);
    -    } else {
    -        *cmdlen = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "S", redis_sock->pass);
         }
     
    -    return cmd;
    +    *cmdlen = cmd.len;
    +    return cmd.c;
     }
     
     /* Send Redis AUTH and process response */
    
    From a551fdc94c14d7974f2303cd558f7bd3e0fd91d6 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Thu, 5 Dec 2024 16:07:05 +0100
    Subject: [PATCH 1944/1986] Switch from linked list to growing array for reply
     callbacks
    
    Reduce allocation and deallocation count and also memory usage when using pipelining
    ---
     common.h  | 17 ++++-------------
     library.c | 34 ++++++++++++++++++++++++----------
     library.h |  3 ++-
     redis.c   | 28 +++++++++++++---------------
     4 files changed, 43 insertions(+), 39 deletions(-)
    
    diff --git a/common.h b/common.h
    index 5030d38c84..1cfa3ff931 100644
    --- a/common.h
    +++ b/common.h
    @@ -181,17 +181,9 @@ typedef enum {
     } while (0)
     
     #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \
    -    fold_item *fi = malloc(sizeof(fold_item)); \
    +    fold_item *fi = redis_add_reply_callback(redis_sock); \
         fi->fun = callback; \
         fi->ctx = closure_context; \
    -    fi->next = NULL; \
    -    if (redis_sock->current) { \
    -        redis_sock->current->next = fi; \
    -    } \
    -    redis_sock->current = fi; \
    -    if (NULL == redis_sock->head) { \
    -        redis_sock->head = redis_sock->current; \
    -    } \
     } while (0)
     
     #define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \
    @@ -315,9 +307,9 @@ typedef struct {
         zend_string         *prefix;
     
         short               mode;
    -    struct fold_item    *head;
    -    struct fold_item    *current;
    -
    +    struct fold_item    *reply_callback;
    +    size_t              reply_callback_count;
    +    size_t              reply_callback_capacity;
         smart_string        pipeline_cmd;
     
         zend_string         *err;
    @@ -341,7 +333,6 @@ typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*,
     typedef struct fold_item {
         FailableResultCallback fun;
         void *ctx;
    -    struct fold_item *next;
     } fold_item;
     
     typedef struct {
    diff --git a/library.c b/library.c
    index 2cd06868f4..12dfe21fbe 100644
    --- a/library.c
    +++ b/library.c
    @@ -3176,7 +3176,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force, int is_reset_mode)
                 }
                 if (force || !IS_ATOMIC(redis_sock)) {
                     php_stream_pclose(redis_sock->stream);
    -                free_reply_callbacks(redis_sock);
    +                redis_free_reply_callbacks(redis_sock);
                     if (p) p->nb_active--;
                 } else if (p) {
                     zend_llist_prepend_element(&p->list, &redis_sock->stream);
    @@ -3536,17 +3536,31 @@ redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz)
         return -1;
     }
     
    +fold_item*
    +redis_add_reply_callback(RedisSock *redis_sock) {
    +    /* Grow array to double size if we need more space */
    +    if (UNEXPECTED(redis_sock->reply_callback_count == redis_sock->reply_callback_capacity)) {
    +        if (redis_sock->reply_callback_capacity == 0) {
    +            redis_sock->reply_callback_capacity = 8; /* initial capacity */
    +        } else if (redis_sock->reply_callback_capacity < 1024) {
    +            redis_sock->reply_callback_capacity *= 2;
    +        } else {
    +            redis_sock->reply_callback_capacity += 4 * 4096 / sizeof(fold_item);
    +        }
    +        redis_sock->reply_callback = erealloc(redis_sock->reply_callback, redis_sock->reply_callback_capacity * sizeof(fold_item));
    +    }
    +    return &redis_sock->reply_callback[redis_sock->reply_callback_count++];
    +}
    +
     void
    -free_reply_callbacks(RedisSock *redis_sock)
    +redis_free_reply_callbacks(RedisSock *redis_sock)
     {
    -    fold_item *fi;
    -
    -    while (redis_sock->head != NULL) {
    -        fi = redis_sock->head->next;
    -        free(redis_sock->head);
    -        redis_sock->head = fi;
    +    if (redis_sock->reply_callback != NULL) {
    +        efree(redis_sock->reply_callback);
    +        redis_sock->reply_callback = NULL;
    +        redis_sock->reply_callback_count = 0;
    +        redis_sock->reply_callback_capacity = 0;
         }
    -    redis_sock->current = NULL;
     }
     
     /**
    @@ -3577,7 +3591,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
             }
         }
         redis_sock_free_auth(redis_sock);
    -    free_reply_callbacks(redis_sock);
    +    redis_free_reply_callbacks(redis_sock);
         efree(redis_sock);
     }
     
    diff --git a/library.h b/library.h
    index 00d7f05288..e5d26f685f 100644
    --- a/library.h
    +++ b/library.h
    @@ -40,7 +40,8 @@
     
     
     void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id);
    -void free_reply_callbacks(RedisSock *redis_sock);
    +fold_item* redis_add_reply_callback(RedisSock *redis_sock);
    +void redis_free_reply_callbacks(RedisSock *redis_sock);
     
     PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass);
     
    diff --git a/redis.c b/redis.c
    index 530e0003e0..1fa0674100 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -491,7 +491,7 @@ PHP_METHOD(Redis,__destruct) {
                 // queued
                 redis_send_discard(redis_sock);
             }
    -        free_reply_callbacks(redis_sock);
    +        redis_free_reply_callbacks(redis_sock);
         }
     }
     
    @@ -750,7 +750,7 @@ PHP_METHOD(Redis, reset)
             RETURN_ZVAL(getThis(), 1, 0);
         }
     
    -    free_reply_callbacks(redis_sock);
    +    redis_free_reply_callbacks(redis_sock);
         redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
         redis_sock->mode = ATOMIC;
         redis_sock->dbNumber = 0;
    @@ -1953,7 +1953,7 @@ PHP_METHOD(Redis, discard)
             ret = redis_send_discard(redis_sock);
         }
         if (ret == SUCCESS) {
    -        free_reply_callbacks(redis_sock);
    +        redis_free_reply_callbacks(redis_sock);
             redis_sock->mode = ATOMIC;
             RETURN_TRUE;
         }
    @@ -1975,7 +1975,7 @@ redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
         }
     
         // No command issued, return empty immutable array
    -    if (redis_sock->head == NULL) {
    +    if (redis_sock->reply_callback == NULL) {
             ZVAL_EMPTY_ARRAY(z_tab);
             return SUCCESS;
         }
    @@ -2015,7 +2015,7 @@ PHP_METHOD(Redis, exec)
             }
             ret = redis_sock_read_multibulk_multi_reply(
                 INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret);
    -        free_reply_callbacks(redis_sock);
    +        redis_free_reply_callbacks(redis_sock);
             REDIS_DISABLE_MODE(redis_sock, MULTI);
             redis_sock->watching = 0;
             if (ret < 0) {
    @@ -2042,7 +2042,7 @@ PHP_METHOD(Redis, exec)
                 }
                 smart_string_free(&redis_sock->pipeline_cmd);
             }
    -        free_reply_callbacks(redis_sock);
    +        redis_free_reply_callbacks(redis_sock);
             REDIS_DISABLE_MODE(redis_sock, PIPELINE);
         }
         RETURN_ZVAL(&z_ret, 0, 1);
    @@ -2067,12 +2067,13 @@ PHP_REDIS_API int
     redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
                                                RedisSock *redis_sock, zval *z_tab)
     {
    -    fold_item *fi;
    +    fold_item fi;
    +    size_t i;
     
    -    for (fi = redis_sock->head; fi; /* void */) {
    -        if (fi->fun) {
    -            fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi->ctx);
    -            fi = fi->next;
    +    for (i = 0; i < redis_sock->reply_callback_count; i++) {
    +        fi = redis_sock->reply_callback[i];
    +        if (fi.fun) {
    +            fi.fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi.ctx);
                 continue;
             }
             size_t len;
    @@ -2084,7 +2085,7 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
                 return FAILURE;
             }
     
    -        while ((fi = fi->next) && fi->fun) {
    +        while (redis_sock->reply_callback[++i].fun) {
                 if (redis_response_enqueued(redis_sock) != SUCCESS) {
                     return FAILURE;
                 }
    @@ -2103,10 +2104,7 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
             if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) < 0) {
                 return FAILURE;
             }
    -
    -        if (fi) fi = fi->next;
         }
    -    redis_sock->current = fi;
         return SUCCESS;
     }
     
    
    From 42a427695e89577a1f1a554dba268527f3995708 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 9 Dec 2024 12:38:27 -0800
    Subject: [PATCH 1945/1986] Use defines for callback growth + sanity check
    
    See #2595
    ---
     library.c | 12 ++++++++----
     1 file changed, 8 insertions(+), 4 deletions(-)
    
    diff --git a/library.c b/library.c
    index 12dfe21fbe..736133fb8b 100644
    --- a/library.c
    +++ b/library.c
    @@ -73,6 +73,10 @@
     #define SCORE_DECODE_INT  1
     #define SCORE_DECODE_DOUBLE 2
     
    +#define REDIS_CALLBACKS_INIT_SIZE      8
    +#define REDIS_CALLBACKS_MAX_DOUBLE 32768
    +#define REDIS_CALLBACKS_ADD_SIZE    4096
    +
     /* PhpRedis often returns either FALSE or NULL depending on whether we have
      * an option set, so this macro just wraps that often repeated logic */
     #define REDIS_ZVAL_NULL(sock_, zv_) \
    @@ -3536,16 +3540,16 @@ redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz)
         return -1;
     }
     
    +/* Grow array to double size if we need more space */
     fold_item*
     redis_add_reply_callback(RedisSock *redis_sock) {
    -    /* Grow array to double size if we need more space */
         if (UNEXPECTED(redis_sock->reply_callback_count == redis_sock->reply_callback_capacity)) {
             if (redis_sock->reply_callback_capacity == 0) {
    -            redis_sock->reply_callback_capacity = 8; /* initial capacity */
    -        } else if (redis_sock->reply_callback_capacity < 1024) {
    +            redis_sock->reply_callback_capacity = REDIS_CALLBACKS_INIT_SIZE;
    +        } else if (redis_sock->reply_callback_capacity < REDIS_CALLBACKS_MAX_DOUBLE) {
                 redis_sock->reply_callback_capacity *= 2;
             } else {
    -            redis_sock->reply_callback_capacity += 4 * 4096 / sizeof(fold_item);
    +            redis_sock->reply_callback_capacity += REDIS_CALLBACKS_ADD_SIZE;
             }
             redis_sock->reply_callback = erealloc(redis_sock->reply_callback, redis_sock->reply_callback_capacity * sizeof(fold_item));
         }
    
    From f68544f70385e1d431fb0245fafe30b39ee7479a Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Mon, 9 Dec 2024 17:25:51 +0100
    Subject: [PATCH 1946/1986] Refactor and avoid allocation in rawcommand method
    
    ---
     redis.c | 24 ++++++------------------
     1 file changed, 6 insertions(+), 18 deletions(-)
    
    diff --git a/redis.c b/redis.c
    index 1fa0674100..949856994c 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -2654,34 +2654,22 @@ PHP_METHOD(Redis, client) {
     
     /* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */
     PHP_METHOD(Redis, rawcommand) {
    -    int argc = ZEND_NUM_ARGS(), cmd_len;
    +    int argc, cmd_len;
         char *cmd = NULL;
         RedisSock *redis_sock;
         zval *z_args;
     
    -    /* Sanity check on arguments */
    -    if (argc < 1) {
    -        php_error_docref(NULL, E_WARNING,
    -            "Must pass at least one command keyword");
    -        RETURN_FALSE;
    -    }
    -    z_args = emalloc(argc * sizeof(zval));
    -    if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
    -        php_error_docref(NULL, E_WARNING,
    -            "Internal PHP error parsing arguments");
    -        efree(z_args);
    -        RETURN_FALSE;
    -    } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len) < 0 ||
    +    ZEND_PARSE_PARAMETERS_START(1, -1)
    +        Z_PARAM_VARIADIC('+', z_args, argc)
    +    ZEND_PARSE_PARAMETERS_END();
    +
    +    if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len) < 0 ||
                    (redis_sock = redis_sock_get(getThis(), 0)) == NULL
         ) {
             if (cmd) efree(cmd);
    -        efree(z_args);
             RETURN_FALSE;
         }
     
    -    /* Clean up command array */
    -    efree(z_args);
    -
         /* Execute our command */
         REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
         if (IS_ATOMIC(redis_sock)) {
    
    From 138d07b67c5537373834f1cae99804e092db1631 Mon Sep 17 00:00:00 2001
    From: Bentley O'Kane-Chase 
    Date: Mon, 16 Dec 2024 10:29:24 +1000
    Subject: [PATCH 1947/1986] Print cursor as unsigned 64 bit integer
    
    ---
     library.c        | 9 +++++++++
     library.h        | 1 +
     redis_commands.c | 4 ++--
     redis_commands.h | 2 +-
     4 files changed, 13 insertions(+), 3 deletions(-)
    
    diff --git a/library.c b/library.c
    index 736133fb8b..3fe5d0a27a 100644
    --- a/library.c
    +++ b/library.c
    @@ -1065,6 +1065,15 @@ int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) {
         return redis_cmd_append_sstr(str, nbuf, len);
     }
     
    +/*
    + * Append a 64-bit unsigned integer to our command
    + */
    +int redis_cmd_append_sstr_ui64(smart_string *str, uint64_t append) {
    +    char nbuf[64];
    +    int len = snprintf(nbuf, sizeof(nbuf), "%" PRIu64, append);
    +    return redis_cmd_append_sstr(str, nbuf, len);
    +}
    +
     /*
      * Append a double to a smart string command
      */
    diff --git a/library.h b/library.h
    index e5d26f685f..d0e2f4209e 100644
    --- a/library.h
    +++ b/library.h
    @@ -50,6 +50,7 @@ int redis_cmd_append_sstr(smart_string *str, char *append, int append_len);
     int redis_cmd_append_sstr_int(smart_string *str, int append);
     int redis_cmd_append_sstr_long(smart_string *str, long append);
     int redis_cmd_append_sstr_i64(smart_string *str, int64_t append);
    +int redis_cmd_append_sstr_ui64(smart_string *str, uint64_t append);
     int redis_cmd_append_sstr_dbl(smart_string *str, double value);
     int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr);
     int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock);
    diff --git a/redis_commands.c b/redis_commands.c
    index a3ccffa81e..eed0c581f7 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -574,7 +574,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     
     /* Generic to construct SCAN and variant commands */
     int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
    -                       long it, char *pat, int pat_len, long count)
    +                       uint64_t it, char *pat, int pat_len, long count)
     {
         static char *kw[] = {"SCAN","SSCAN","HSCAN","ZSCAN"};
         int argc;
    @@ -591,7 +591,7 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
         }
     
         // Append cursor
    -    redis_cmd_append_sstr_long(&cmdstr, it);
    +    redis_cmd_append_sstr_ui64(&cmdstr, it);
     
         // Append count if we've got one
         if (count) {
    diff --git a/redis_commands.h b/redis_commands.h
    index ab3d89e2c1..b0c5895c4f 100644
    --- a/redis_commands.h
    +++ b/redis_commands.h
    @@ -309,7 +309,7 @@ int redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         char **cmd, int *cmd_len, short *slot, void **ctx);
     
     int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
    -    long it, char *pat, int pat_len, long count);
    +    uint64_t it, char *pat, int pat_len, long count);
     
     int redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         char **cmd, int *cmd_len, short *slot, void **ctx);
    
    From 35c5988027eda663167a64decde4512957cae738 Mon Sep 17 00:00:00 2001
    From: Bentley O'Kane-Chase 
    Date: Tue, 17 Dec 2024 11:06:47 +1000
    Subject: [PATCH 1948/1986] Formatting improvements
    
    ---
     library.c        | 4 ++--
     library.h        | 2 +-
     redis_commands.c | 2 +-
     3 files changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/library.c b/library.c
    index 3fe5d0a27a..5227a1eccc 100644
    --- a/library.c
    +++ b/library.c
    @@ -1068,8 +1068,8 @@ int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) {
     /*
      * Append a 64-bit unsigned integer to our command
      */
    -int redis_cmd_append_sstr_ui64(smart_string *str, uint64_t append) {
    -    char nbuf[64];
    +int redis_cmd_append_sstr_u64(smart_string *str, uint64_t append) {
    +    char nbuf[21];
         int len = snprintf(nbuf, sizeof(nbuf), "%" PRIu64, append);
         return redis_cmd_append_sstr(str, nbuf, len);
     }
    diff --git a/library.h b/library.h
    index d0e2f4209e..47c339ada2 100644
    --- a/library.h
    +++ b/library.h
    @@ -50,7 +50,7 @@ int redis_cmd_append_sstr(smart_string *str, char *append, int append_len);
     int redis_cmd_append_sstr_int(smart_string *str, int append);
     int redis_cmd_append_sstr_long(smart_string *str, long append);
     int redis_cmd_append_sstr_i64(smart_string *str, int64_t append);
    -int redis_cmd_append_sstr_ui64(smart_string *str, uint64_t append);
    +int redis_cmd_append_sstr_u64(smart_string *str, uint64_t append);
     int redis_cmd_append_sstr_dbl(smart_string *str, double value);
     int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr);
     int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock);
    diff --git a/redis_commands.c b/redis_commands.c
    index eed0c581f7..c49f5cd6c6 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -591,7 +591,7 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
         }
     
         // Append cursor
    -    redis_cmd_append_sstr_ui64(&cmdstr, it);
    +    redis_cmd_append_sstr_u64(&cmdstr, it);
     
         // Append count if we've got one
         if (count) {
    
    From 044b30386f0418e9ed2a2bbc3b79582520d008d8 Mon Sep 17 00:00:00 2001
    From: Bentley O'Kane-Chase 
    Date: Tue, 17 Dec 2024 11:07:58 +1000
    Subject: [PATCH 1949/1986] Reduce buffer size for signed integer,
     strlen(-9223372036854775808) = 20 + 1 for '\0'
    
    ---
     library.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/library.c b/library.c
    index 5227a1eccc..a264868003 100644
    --- a/library.c
    +++ b/library.c
    @@ -1060,7 +1060,7 @@ int redis_cmd_append_sstr_long(smart_string *str, long append) {
      * Append a 64-bit integer to our command
      */
     int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) {
    -    char nbuf[64];
    +    char nbuf[21];
         int len = snprintf(nbuf, sizeof(nbuf), "%" PRId64, append);
         return redis_cmd_append_sstr(str, nbuf, len);
     }
    
    From 43e6cab8792dc01580894d85600add9b68c27a42 Mon Sep 17 00:00:00 2001
    From: peter15914 <48548636+peter15914@users.noreply.github.com>
    Date: Thu, 2 Jan 2025 02:07:31 +0500
    Subject: [PATCH 1950/1986] Fix potential NULL dereference
    
    The return value of INI_STR() is always checked for NULL.
    ---
     redis_session.c | 7 ++++---
     1 file changed, 4 insertions(+), 3 deletions(-)
    
    diff --git a/redis_session.c b/redis_session.c
    index c355704f2f..5af0355211 100644
    --- a/redis_session.c
    +++ b/redis_session.c
    @@ -147,6 +147,10 @@ static int session_gc_maxlifetime(void) {
     /* Retrieve redis.session.compression from php.ini */
     static int session_compression_type(void) {
         const char *compression = INI_STR("redis.session.compression");
    +    if(compression == NULL || *compression == '\0' || strncasecmp(compression, "none", sizeof("none") - 1) == 0) {
    +        return REDIS_COMPRESSION_NONE;
    +    }
    +
     #ifdef HAVE_REDIS_LZF
         if(strncasecmp(compression, "lzf", sizeof("lzf") - 1) == 0) {
             return REDIS_COMPRESSION_LZF;
    @@ -162,9 +166,6 @@ static int session_compression_type(void) {
             return REDIS_COMPRESSION_LZ4;
         }
     #endif
    -    if(*compression == '\0' || strncasecmp(compression, "none", sizeof("none") - 1) == 0) {
    -        return REDIS_COMPRESSION_NONE;
    -    }
     
         // E_NOTICE when outside of valid values
         php_error_docref(NULL, E_NOTICE, "redis.session.compression is outside of valid values, disabling");
    
    From 5cad20763710d44f8efb8e537f8f84a812935604 Mon Sep 17 00:00:00 2001
    From: OHZEKI Naoki <0h23k1.n40k1@gmail.com>
    Date: Mon, 23 Dec 2024 12:56:35 +0900
    Subject: [PATCH 1951/1986] Fix phpdoc type of '$pattern'
    
    ---
     redis.stub.php | 13 ++++++++-----
     1 file changed, 8 insertions(+), 5 deletions(-)
    
    diff --git a/redis.stub.php b/redis.stub.php
    index e5e1279691..930ae1f1be 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1874,7 +1874,7 @@ public function hVals(string $key): Redis|array|false;
          * @param int    $iterator  The scan iterator, which should be initialized to NULL before the first call.
          *                          This value will be updated after every call to hscan, until it reaches zero
          *                          meaning the scan is complete.
    -     * @param string $pattern   An optional glob-style pattern to filter fields with.
    +     * @param string|null $pattern An optional glob-style pattern to filter fields with.
          * @param int    $count     An optional hint to Redis about how many fields and values to return per HSCAN.
          *
          * @return Redis|array|bool An array with a subset of fields and values.
    @@ -1995,7 +1995,10 @@ public function info(string ...$sections): Redis|array|false;
          */
         public function isConnected(): bool;
     
    -    /** @return Redis|list|false */
    +    /**
    +     * @param string $pattern
    +     * @return Redis|list|false
    +     */
         public function keys(string $pattern);
     
         /**
    @@ -2920,7 +2923,7 @@ public function save(): Redis|bool;
          *                         updated to a new number, until finally Redis will set the value to
          *                         zero, indicating that the scan is complete.
          *
    -     * @param string $pattern  An optional glob-style pattern for matching key names.  If passed as
    +     * @param string|null $pattern An optional glob-style pattern for matching key names.  If passed as
          *                         NULL, it is the equivalent of sending '*' (match every key).
          *
          * @param int    $count    A hint to redis that tells it how many keys to return in a single
    @@ -3311,7 +3314,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i
          *                          PhpRedis will update with the value returned from Redis after each
          *                          subsequent call to SSCAN.  Once this cursor is zero you know all
          *                          members have been traversed.
    -     * @param string $pattern   An optional glob style pattern to match against, so Redis only
    +     * @param string|null $pattern An optional glob style pattern to match against, so Redis only
          *                          returns the subset of members matching this pattern.
          * @param int    $count     A hint to Redis as to how many members it should scan in one command
          *                          before returning members for that iteration.
    @@ -4598,7 +4601,7 @@ public function zinterstore(string $dst, array $keys, ?array $weights = null, ?s
          * @param int    $iterator   A reference to an iterator that should be initialized to NULL initially, that
          *                           will be updated after each subsequent call to ZSCAN.  Once the iterator
          *                           has returned to zero the scan is complete
    -     * @param string $pattern    An optional glob-style pattern that limits which members are returned during
    +     * @param string|null $pattern An optional glob-style pattern that limits which members are returned during
          *                           the scanning process.
          * @param int    $count      A hint for Redis that tells it how many elements it should test before returning
          *                           from the call.  The higher the more work Redis may do in any one given call to
    
    From 9e504ede34749326a39f997db6cc5c4201f6a9bc Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Thu, 2 Jan 2025 22:18:58 +0200
    Subject: [PATCH 1952/1986] Set priority to 60
    
    ---
     composer.json | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/composer.json b/composer.json
    index e5c7077082..dd4d00af0c 100644
    --- a/composer.json
    +++ b/composer.json
    @@ -57,6 +57,7 @@
                     "description": "Use system liblz4",
                     "needs-value": true
                 }
    -        ]
    +        ],
    +        "priority": 60
         }
     }
    
    From 3f8dba6a44cda6e4b6e8fd360466dbc4f6af4147 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 13 Jan 2025 10:54:32 -0800
    Subject: [PATCH 1953/1986] Regnerate stub hash
    
    ---
     redis_arginfo.h        | 2 +-
     redis_legacy_arginfo.h | 2 +-
     2 files changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index 27290dde78..6df6763afe 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: bacbe6b1d55da4ba6d370fff1090e8de0363c4c2 */
    + * Stub hash: 1f8f22ab9cd1635066463b20ab12d295c11b4ac7 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 83b9f30057..80f212b2b4 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: bacbe6b1d55da4ba6d370fff1090e8de0363c4c2 */
    + * Stub hash: 1f8f22ab9cd1635066463b20ab12d295c11b4ac7 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    
    From faa4bc20868c76be4ecc4265015104a8adafccc4 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 13 Jan 2025 10:57:31 -0800
    Subject: [PATCH 1954/1986] Don't cast a uint64_t to a long.
    
    We recently updated PhpRedis to handle `SCAN` cursors > 2^63 as strings
    (as internally PHP integers are longs).
    
    However, the `redis_build_scan_cmd` took the cursor as a long, which
    would overflow if the value was > `2^63`.
    
    This commit simply changes the function to take a `uint64_t` and call
    our specific `redis_append_sstr_u64` so we send the cursor to Redis
    correctly.
    
    Fixes #2454.
    ---
     redis.c | 18 +++++++++---------
     1 file changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/redis.c b/redis.c
    index 949856994c..4ec516c1b5 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -2694,9 +2694,9 @@ PHP_METHOD(Redis, copy) {
     /* }}} */
     
     /* Helper to format any combination of SCAN arguments */
    -PHP_REDIS_API int
    +static int
     redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
    -                     long iter, char *pattern, int pattern_len, int count,
    +                     uint64_t cursor, char *pattern, int pattern_len, int count,
                          zend_string *match_type)
     {
         smart_string cmdstr = {0};
    @@ -2727,7 +2727,7 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
         /* Start the command */
         redis_cmd_init_sstr(&cmdstr, argc, keyword, strlen(keyword));
         if (key_len) redis_cmd_append_sstr(&cmdstr, key, key_len);
    -    redis_cmd_append_sstr_long(&cmdstr, iter);
    +    redis_cmd_append_sstr_u64(&cmdstr, cursor);
     
         /* Append COUNT if we've got it */
         if(count) {
    @@ -2751,7 +2751,7 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
         return cmdstr.len;
     }
     
    -/* {{{ proto redis::scan(&$iterator, [pattern, [count, [type]]]) */
    +/* {{{ proto redis::scan(&$cursor, [pattern, [count, [type]]]) */
     PHP_REDIS_API void
     generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         zval *object, *z_cursor;
    @@ -2818,7 +2818,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
          * pattern.  phpredis can be set up to abstract this from the user, by
          * setting OPT_SCAN to REDIS_SCAN_RETRY.  Otherwise we will return empty
          * keys and the user will need to make subsequent calls with an updated
    -     * iterator.
    +     * cursor.
          */
         do {
             /* Free our previous reply if we're back in the loop.  We know we are
    @@ -2829,10 +2829,10 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
             }
     
             // Format our SCAN command
    -        cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (long)cursor,
    -                                   pattern, pattern_len, count, match_type);
    +        cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, cursor,
    +                                       pattern, pattern_len, count, match_type);
     
    -        /* Execute our command getting our new iterator value */
    +        /* Execute our command getting our new cursor value */
             REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
             if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
                                           redis_sock,type, &cursor) < 0)
    @@ -2853,7 +2853,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
         /* Free our key if it was prefixed */
         if(key_free) efree(key);
     
    -    /* Update our iterator reference */
    +    /* Update our cursor reference */
         redisSetScanCursor(z_cursor, cursor);
     }
     
    
    From a2eef77f4419cda815052e75def3af81b0ccd80f Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sun, 19 Jan 2025 09:15:41 -0800
    Subject: [PATCH 1955/1986] Implement Valkey >= 8.1 IFEQ set option
    
    Implement the new `IFEQ` `SET` option that will be included in `Valkey`
    8.1.
    
    See: valkey-io/valkey#1324
    ---
     redis_commands.c           | 29 +++++++++++++++++++++++++----
     tests/RedisClusterTest.php |  1 +
     tests/RedisTest.php        | 18 ++++++++++++++++++
     tests/TestSuite.php        |  1 +
     4 files changed, 45 insertions(+), 4 deletions(-)
    
    diff --git a/redis_commands.c b/redis_commands.c
    index c49f5cd6c6..0c2aaa1232 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -2293,7 +2293,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
     {
         char *key = NULL, *exp_type = NULL, *set_type = NULL;
    -    zval *z_value, *z_opts=NULL;
    +    zend_string *ifeq = NULL, *tmp = NULL;
    +    zval *z_value, *z_opts = NULL;
         smart_string cmdstr = {0};
         zend_long expire = -1;
         zend_bool get = 0;
    @@ -2312,7 +2313,6 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             return FAILURE;
         }
     
    -
         // Check for an options array
         if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) {
             HashTable *kt = Z_ARRVAL_P(z_opts);
    @@ -2329,11 +2329,14 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                              zend_string_equals_literal_ci(zkey, "PXAT"))
                 ) {
                     if (redis_try_get_expiry(v, &expire) == FAILURE || expire < 1) {
    +                    zend_tmp_string_release(tmp);
                         setExpiryWarning(v);
                         return FAILURE;
                     }
     
                     exp_type = ZSTR_VAL(zkey);
    +            } else if (zkey && !ifeq && zend_string_equals_literal_ci(zkey, "IFEQ")) {
    +                ifeq = zval_get_tmp_string(v, &tmp);
                 } else if (Z_TYPE_P(v) == IS_STRING) {
                     if (zend_string_equals_literal_ci(Z_STR_P(v), "KEEPTTL")) {
                         keep_ttl  = 1;
    @@ -2348,6 +2351,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             } ZEND_HASH_FOREACH_END();
         } else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) {
             if (redis_try_get_expiry(z_opts, &expire) == FAILURE || expire < 1) {
    +            zend_tmp_string_release(tmp);
                 setExpiryWarning(z_opts);
                 return FAILURE;
             }
    @@ -2356,6 +2360,14 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         /* Protect the user from syntax errors but give them some info about what's wrong */
         if (exp_type && keep_ttl) {
             php_error_docref(NULL, E_WARNING, "KEEPTTL can't be combined with EX or PX option");
    +        zend_tmp_string_release(tmp);
    +        return FAILURE;
    +    }
    +
    +    /* You can't use IFEQ with NX or XX */
    +    if (set_type && ifeq) {
    +        php_error_docref(NULL, E_WARNING, "IFEQ can't be combined with NX or XX option");
    +        zend_tmp_string_release(tmp);
             return FAILURE;
         }
     
    @@ -2363,11 +2375,13 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
          * actually execute a SETEX command */
         if (expire > 0 && !exp_type && !set_type && !keep_ttl) {
             *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, z_value);
    +        zend_tmp_string_release(tmp);
             return SUCCESS;
         }
     
         /* Calculate argc based on options set */
    -    int argc = 2 + (exp_type ? 2 : 0) + (set_type != NULL) + (keep_ttl != 0) + get;
    +    int argc = 2 + (ifeq ? 2 : 0) + (exp_type ? 2 : 0) + (set_type != NULL) + 
    +        (keep_ttl != 0) + get;
     
         /* Initial SET   */
         redis_cmd_init_sstr(&cmdstr, argc, "SET", 3);
    @@ -2379,8 +2393,13 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             redis_cmd_append_sstr_long(&cmdstr, (long)expire);
         }
     
    -    if (set_type)
    +    if (ifeq) {
    +        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IFEQ");
    +        redis_cmd_append_sstr_zstr(&cmdstr, ifeq);
    +    } else if (set_type) {
             redis_cmd_append_sstr(&cmdstr, set_type, strlen(set_type));
    +    }
    +
         if (keep_ttl)
             redis_cmd_append_sstr(&cmdstr, "KEEPTTL", 7);
         if (get) {
    @@ -2388,6 +2407,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             *ctx = PHPREDIS_CTX_PTR;
         }
     
    +    zend_tmp_string_release(tmp);
    +
         /* Push command and length to the caller */
         *cmd = cmdstr.c;
         *cmd_len = cmdstr.len;
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index aceebf8376..1be83c2aee 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -133,6 +133,7 @@ public function setUp() {
             $info           = $this->redis->info(uniqid());
             $this->version  = $info['redis_version'] ?? '0.0.0';
             $this->is_keydb = $this->detectKeyDB($info);
    +        $this->is_valkey = $this->detectValkey($info);
         }
     
         /* Override newInstance as we want a RedisCluster object */
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 61eecd724d..c9b16c7b17 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -67,11 +67,16 @@ protected function detectKeyDB(array $info) {
                    isset($info['mvcc_depth']);
         }
     
    +    protected function detectValkey(array $info) {
    +        return isset($info['server_name']) && $info['server_name'] === 'valkey';
    +    }
    +
         public function setUp() {
             $this->redis = $this->newInstance();
             $info = $this->redis->info();
             $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0');
             $this->is_keydb = $this->detectKeyDB($info);
    +        $this->is_valkey = $this->detectValKey($info); 
         }
     
         protected function minVersionCheck($version) {
    @@ -629,6 +634,19 @@ public function testExtendedSet() {
             $this->assertEquals('bar', $this->redis->set('foo', 'baz', ['GET']));
         }
     
    +    /* Test Valkey >= 8.1 IFEQ SET option */
    +    public function testValkeyIfEq() {
    +        if ( ! $this->is_valkey || ! $this->minVersionCheck('8.1.0'))
    +            $this->markTestSkipped();
    +
    +        $this->redis->del('foo');
    +        $this->assertTrue($this->redis->set('foo', 'bar'));
    +        $this->assertTrue($this->redis->set('foo', 'bar2', ['IFEQ' => 'bar']));
    +        $this->assertFalse($this->redis->set('foo', 'bar4', ['IFEQ' => 'bar3']));
    +
    +        $this->assertEquals('bar2', $this->redis->set('foo', 'bar3', ['IFEQ' => 'bar2', 'GET']));
    +    }
    +
         public function testGetSet() {
             $this->redis->del('key');
             $this->assertFalse($this->redis->getSet('key', '42'));
    diff --git a/tests/TestSuite.php b/tests/TestSuite.php
    index f5135d3631..c3fe7f7ff3 100644
    --- a/tests/TestSuite.php
    +++ b/tests/TestSuite.php
    @@ -16,6 +16,7 @@ class TestSuite
         /* Redis server version */
         protected $version;
         protected bool $is_keydb;
    +    protected bool $is_valkey;
     
         private static bool $colorize = false;
     
    
    From c7b878431014789f35d2fb1834b95257ca6cbba5 Mon Sep 17 00:00:00 2001
    From: James Kennedy 
    Date: Thu, 19 Dec 2024 13:44:04 -0800
    Subject: [PATCH 1956/1986] Invalidate slot cache on failed cluster connections
    
    ---
     cluster_library.c | 9 +++++++++
     cluster_library.h | 1 +
     2 files changed, 10 insertions(+)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index f4284c6930..e919e2b7fa 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -1599,11 +1599,13 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char
     
         // If we've detected the cluster is down, throw an exception
         if (c->clusterdown) {
    +        cluster_cache_clear(c);
             CLUSTER_THROW_EXCEPTION("The Redis Cluster is down (CLUSTERDOWN)", 0);
             return -1;
         } else if (timedout || resp == -1) {
             // Make sure the socket is reconnected, it such that it is in a clean state
             redis_sock_disconnect(c->cmd_sock, 1, 1);
    +        cluster_cache_clear(c);
     
             if (timedout) {
                 CLUSTER_THROW_EXCEPTION("Timed out attempting to find data in the correct node!", 0);
    @@ -3115,5 +3117,12 @@ PHP_REDIS_API void cluster_cache_store(zend_string *hash, HashTable *nodes) {
         redis_register_persistent_resource(cc->hash, cc, le_cluster_slot_cache);
     }
     
    +void cluster_cache_clear(redisCluster *c)
    +{
    +    if (c->cache_key) {
    +        zend_hash_del(&EG(persistent_list), c->cache_key);
    +    }
    +}
    +
     
     /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
    diff --git a/cluster_library.h b/cluster_library.h
    index c2fd850221..cef7e75412 100644
    --- a/cluster_library.h
    +++ b/cluster_library.h
    @@ -390,6 +390,7 @@ PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, in
     
     PHP_REDIS_API void cluster_cache_store(zend_string *hash, HashTable *nodes);
     PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash);
    +void cluster_cache_clear(redisCluster *c);
     
     /*
      * Redis Cluster response handlers.  Our response handlers generally take the
    
    From a10bca35bba32bb969cc1e473564695d3f8a8811 Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Fri, 31 Jan 2025 22:07:21 +0200
    Subject: [PATCH 1957/1986] Update codeql to v3
    
    ---
     .github/workflows/codeql.yml | 6 +++---
     1 file changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
    index 228ef44957..6116b7b761 100644
    --- a/.github/workflows/codeql.yml
    +++ b/.github/workflows/codeql.yml
    @@ -8,7 +8,7 @@ jobs:
           - name: Checkout
             uses: actions/checkout@v4
           - name: Initialize CodeQL
    -        uses: github/codeql-action/init@v2
    +        uses: github/codeql-action/init@v3
             with:
               languages: cpp
               queries: +security-and-quality
    @@ -16,6 +16,6 @@ jobs:
             run: |
               phpize
           - name: Autobuild
    -        uses: github/codeql-action/autobuild@v2
    +        uses: github/codeql-action/autobuild@v3
           - name: Perform CodeQL Analysis
    -        uses: github/codeql-action/analyze@v2
    +        uses: github/codeql-action/analyze@v3
    
    From f9ce9429ef9f14a3de2c3fe1d68d02fb7440093d Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sun, 26 Jan 2025 13:39:42 -0800
    Subject: [PATCH 1958/1986] Introduce `Redis::OPT_PACK_IGNORE_NUMBERS` option.
    
    Adds an option that instructs PhpRedis to not serialize or compress
    numeric values. Specifically where `Z_TYPE_P(z) == IS_LONG` or
    `Z_TYPE_P(z) == IS_DOUBLE`.
    
    This flag lets the user enable serialization and/or compression while
    still using the various increment/decrement command (`INCR`, `INCRBY`,
    `DECR`, `DECRBY`, `INCRBYFLOAT`, `HINCRBY`, and `HINCRBYFLOAT`).
    
    Because PhpRedis can't be certain that this option was enabled when
    writing keys, there is a small runtime cost on the read-side that tests
    whether or not the value its reading is a pure integer or floating point
    value.
    
    See #23
    ---
     common.h               |  30 +++++++------
     library.c              |  60 ++++++++++++++++++++-----
     redis.stub.php         |   7 +++
     redis_arginfo.h        |   8 +++-
     redis_commands.c       |   5 +++
     redis_legacy_arginfo.h |   8 +++-
     tests/RedisTest.php    | 100 ++++++++++++++++++++++++++++++++++++++++-
     7 files changed, 189 insertions(+), 29 deletions(-)
    
    diff --git a/common.h b/common.h
    index 1cfa3ff931..c1ed664791 100644
    --- a/common.h
    +++ b/common.h
    @@ -91,20 +91,21 @@ typedef enum _PUBSUB_TYPE {
     #define REDIS_SUBS_BUCKETS   3
     
     /* options */
    -#define REDIS_OPT_SERIALIZER         1
    -#define REDIS_OPT_PREFIX             2
    -#define REDIS_OPT_READ_TIMEOUT       3
    -#define REDIS_OPT_SCAN               4
    -#define REDIS_OPT_FAILOVER           5
    -#define REDIS_OPT_TCP_KEEPALIVE      6
    -#define REDIS_OPT_COMPRESSION        7
    -#define REDIS_OPT_REPLY_LITERAL      8
    -#define REDIS_OPT_COMPRESSION_LEVEL  9
    -#define REDIS_OPT_NULL_MBULK_AS_NULL 10
    -#define REDIS_OPT_MAX_RETRIES        11
    -#define REDIS_OPT_BACKOFF_ALGORITHM  12
    -#define REDIS_OPT_BACKOFF_BASE       13
    -#define REDIS_OPT_BACKOFF_CAP        14
    +#define REDIS_OPT_SERIALIZER          1
    +#define REDIS_OPT_PREFIX              2
    +#define REDIS_OPT_READ_TIMEOUT        3
    +#define REDIS_OPT_SCAN                4
    +#define REDIS_OPT_FAILOVER            5
    +#define REDIS_OPT_TCP_KEEPALIVE       6
    +#define REDIS_OPT_COMPRESSION         7
    +#define REDIS_OPT_REPLY_LITERAL       8
    +#define REDIS_OPT_COMPRESSION_LEVEL   9
    +#define REDIS_OPT_NULL_MBULK_AS_NULL  10
    +#define REDIS_OPT_MAX_RETRIES         11
    +#define REDIS_OPT_BACKOFF_ALGORITHM   12
    +#define REDIS_OPT_BACKOFF_BASE        13
    +#define REDIS_OPT_BACKOFF_CAP         14
    +#define REDIS_OPT_PACK_IGNORE_NUMBERS 15
     
     /* cluster options */
     #define REDIS_FAILOVER_NONE              0
    @@ -300,6 +301,7 @@ typedef struct {
         zend_string         *persistent_id;
         HashTable           *subs[REDIS_SUBS_BUCKETS];
         redis_serializer    serializer;
    +    zend_bool           pack_ignore_numbers;
         int                 compression;
         int                 compression_level;
         long                dbNumber;
    diff --git a/library.c b/library.c
    index a264868003..e80cab3bfc 100644
    --- a/library.c
    +++ b/library.c
    @@ -3831,12 +3831,38 @@ redis_uncompress(RedisSock *redis_sock, char **dst, size_t *dstlen, const char *
         return 0;
     }
     
    +static int serialize_generic_zval(char **dst, size_t *len, zval *zsrc) {
    +    zend_string *zstr;
    +
    +    zstr = zval_get_string_func(zsrc);
    +    if (ZSTR_IS_INTERNED(zstr)) {
    +        *dst = ZSTR_VAL(zstr);
    +        *len = ZSTR_LEN(zstr);
    +        return 0;
    +    }
    +
    +    *dst = estrndup(ZSTR_VAL(zstr), ZSTR_LEN(zstr));
    +    *len = ZSTR_LEN(zstr);
    +
    +    zend_string_release(zstr);
    +
    +    return 1;
    +}
    +
    +
     PHP_REDIS_API int
     redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) {
         size_t tmplen;
         int tmpfree;
         char *tmp;
     
    +    /* Don't pack actual numbers if the user asked us not to */
    +    if (UNEXPECTED(redis_sock->pack_ignore_numbers &&
    +                   (Z_TYPE_P(z) == IS_LONG || Z_TYPE_P(z) == IS_DOUBLE)))
    +    {
    +        return serialize_generic_zval(val, val_len, z);
    +    }
    +
         /* First serialize */
         tmpfree = redis_serialize(redis_sock, z, &tmp, &tmplen);
     
    @@ -3851,9 +3877,29 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) {
     
     PHP_REDIS_API int
     redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) {
    +    zend_long lval;
    +    double dval;
         size_t len;
         char *buf;
     
    +    if (UNEXPECTED((redis_sock->serializer != REDIS_SERIALIZER_NONE &&
    +                    redis_sock->compression != REDIS_COMPRESSION_NONE) &&
    +                    redis_sock->pack_ignore_numbers) &&
    +                    srclen > 0 && srclen < 24)
    +    {
    +        switch (is_numeric_string(src, srclen, &lval, &dval, 0)) {
    +            case IS_LONG:
    +                ZVAL_LONG(zdst, lval);
    +                return 1;
    +            case IS_DOUBLE:
    +                ZVAL_DOUBLE(zdst, dval);
    +                return 1;
    +            default:
    +                /* Fallthrough */
    +                break;
    +        }
    +    }
    +
         /* Uncompress, then unserialize */
         if (redis_uncompress(redis_sock, &buf, &len, src, srclen)) {
             if (!redis_unserialize(redis_sock, buf, len, zdst)) {
    @@ -3898,18 +3944,8 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len)
                         *val_len = 5;
                         break;
     
    -                default: { /* copy */
    -                    zend_string *zstr = zval_get_string_func(z);
    -                    if (ZSTR_IS_INTERNED(zstr)) { // do not reallocate interned strings
    -                        *val = ZSTR_VAL(zstr);
    -                        *val_len = ZSTR_LEN(zstr);
    -                        return 0;
    -                    }
    -                    *val = estrndup(ZSTR_VAL(zstr), ZSTR_LEN(zstr));
    -                    *val_len = ZSTR_LEN(zstr);
    -                    zend_string_efree(zstr);
    -                    return 1;
    -                }
    +                default:
    +                    return serialize_generic_zval(val, val_len, z);
                 }
                 break;
             case REDIS_SERIALIZER_PHP:
    diff --git a/redis.stub.php b/redis.stub.php
    index 930ae1f1be..9a41768661 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -151,6 +151,13 @@ class Redis {
          */
         public const OPT_NULL_MULTIBULK_AS_NULL = UNKNOWN;
     
    +    /**
    +     * @var int
    +     * @cvalue REDIS_OPT_PACK_IGNORE_NUMBERS
    +     *
    +     */
    +    public const OPT_PACK_IGNORE_NUMBERS = UNKNOWN;
    +
         /**
          *
          * @var int
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index 6df6763afe..fb9cf97d3f 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 1f8f22ab9cd1635066463b20ab12d295c11b4ac7 */
    + * Stub hash: 78283cf59cefb411c09adf7a0f0bd234c65327b3 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -1817,6 +1817,12 @@ static zend_class_entry *register_class_Redis(void)
     	zend_declare_class_constant_ex(class_entry, const_OPT_NULL_MULTIBULK_AS_NULL_name, &const_OPT_NULL_MULTIBULK_AS_NULL_value, ZEND_ACC_PUBLIC, NULL);
     	zend_string_release(const_OPT_NULL_MULTIBULK_AS_NULL_name);
     
    +	zval const_OPT_PACK_IGNORE_NUMBERS_value;
    +	ZVAL_LONG(&const_OPT_PACK_IGNORE_NUMBERS_value, REDIS_OPT_PACK_IGNORE_NUMBERS);
    +	zend_string *const_OPT_PACK_IGNORE_NUMBERS_name = zend_string_init_interned("OPT_PACK_IGNORE_NUMBERS", sizeof("OPT_PACK_IGNORE_NUMBERS") - 1, 1);
    +	zend_declare_class_constant_ex(class_entry, const_OPT_PACK_IGNORE_NUMBERS_name, &const_OPT_PACK_IGNORE_NUMBERS_value, ZEND_ACC_PUBLIC, NULL);
    +	zend_string_release(const_OPT_PACK_IGNORE_NUMBERS_name);
    +
     	zval const_SERIALIZER_NONE_value;
     	ZVAL_LONG(&const_SERIALIZER_NONE_value, REDIS_SERIALIZER_NONE);
     	zend_string *const_SERIALIZER_NONE_name = zend_string_init_interned("SERIALIZER_NONE", sizeof("SERIALIZER_NONE") - 1, 1);
    diff --git a/redis_commands.c b/redis_commands.c
    index 0c2aaa1232..2d57007ce8 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -6147,6 +6147,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS,
                 RETURN_LONG(redis_sock->compression);
             case REDIS_OPT_COMPRESSION_LEVEL:
                 RETURN_LONG(redis_sock->compression_level);
    +        case REDIS_OPT_PACK_IGNORE_NUMBERS:
    +            RETURN_BOOL(redis_sock->pack_ignore_numbers);
             case REDIS_OPT_PREFIX:
                 if (redis_sock->prefix) {
                     RETURN_STRINGL(ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix));
    @@ -6235,6 +6237,9 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
                     RETURN_TRUE;
                 }
                 break;
    +        case REDIS_OPT_PACK_IGNORE_NUMBERS:
    +            redis_sock->pack_ignore_numbers = zval_is_true(val);
    +            RETURN_TRUE;
             case REDIS_OPT_COMPRESSION_LEVEL:
                 val_long = zval_get_long(val);
                 redis_sock->compression_level = val_long;
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 80f212b2b4..c382766c61 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 1f8f22ab9cd1635066463b20ab12d295c11b4ac7 */
    + * Stub hash: 78283cf59cefb411c09adf7a0f0bd234c65327b3 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    @@ -1660,6 +1660,12 @@ static zend_class_entry *register_class_Redis(void)
     	zend_declare_class_constant_ex(class_entry, const_OPT_NULL_MULTIBULK_AS_NULL_name, &const_OPT_NULL_MULTIBULK_AS_NULL_value, ZEND_ACC_PUBLIC, NULL);
     	zend_string_release(const_OPT_NULL_MULTIBULK_AS_NULL_name);
     
    +	zval const_OPT_PACK_IGNORE_NUMBERS_value;
    +	ZVAL_LONG(&const_OPT_PACK_IGNORE_NUMBERS_value, REDIS_OPT_PACK_IGNORE_NUMBERS);
    +	zend_string *const_OPT_PACK_IGNORE_NUMBERS_name = zend_string_init_interned("OPT_PACK_IGNORE_NUMBERS", sizeof("OPT_PACK_IGNORE_NUMBERS") - 1, 1);
    +	zend_declare_class_constant_ex(class_entry, const_OPT_PACK_IGNORE_NUMBERS_name, &const_OPT_PACK_IGNORE_NUMBERS_value, ZEND_ACC_PUBLIC, NULL);
    +	zend_string_release(const_OPT_PACK_IGNORE_NUMBERS_name);
    +
     	zval const_SERIALIZER_NONE_value;
     	ZVAL_LONG(&const_SERIALIZER_NONE_value, REDIS_SERIALIZER_NONE);
     	zend_string *const_SERIALIZER_NONE_name = zend_string_init_interned("SERIALIZER_NONE", sizeof("SERIALIZER_NONE") - 1, 1);
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index c9b16c7b17..3b46622337 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -76,7 +76,7 @@ public function setUp() {
             $info = $this->redis->info();
             $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0');
             $this->is_keydb = $this->detectKeyDB($info);
    -        $this->is_valkey = $this->detectValKey($info); 
    +        $this->is_valkey = $this->detectValKey($info);
         }
     
         protected function minVersionCheck($version) {
    @@ -4958,6 +4958,104 @@ public function testSerializerPHP() {
             $this->redis->setOption(Redis::OPT_PREFIX, '');
         }
     
    +    private function cartesianProduct(array $arrays) {
    +        $result = [[]];
    +
    +        foreach ($arrays as $array) {
    +            $append = [];
    +            foreach ($result as $product) {
    +                foreach ($array as $item) {
    +                    $newProduct = $product;
    +                    $newProduct[] = $item;
    +                    $append[] = $newProduct;
    +                }
    +            }
    +
    +            $result = $append;
    +        }
    +
    +        return $result;
    +    }
    +
    +    public function testIgnoreNumbers() {
    +        $combinations = $this->cartesianProduct([
    +            [false, true, false],
    +            $this->getSerializers(),
    +            $this->getCompressors(),
    +        ]);
    +
    +        foreach ($combinations as [$ignore, $serializer, $compression]) {
    +            $this->redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, $ignore);
    +            $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
    +            $this->redis->setOption(Redis::OPT_COMPRESSION, $compression);
    +
    +            $this->assertIsInt($this->redis->del('answer'));
    +            $this->assertIsInt($this->redis->del('hash'));
    +
    +            $transparent = $compression === Redis::COMPRESSION_NONE &&
    +                           ($serializer === Redis::SERIALIZER_NONE ||
    +                            $serializer === Redis::SERIALIZER_JSON);
    +
    +            if ($transparent || $ignore) {
    +                $expected_answer = 42;
    +                $expected_pi = 3.14;
    +            } else {
    +                $expected_answer = false;
    +                $expected_pi = false;
    +            }
    +
    +            $this->assertTrue($this->redis->set('answer', 32));
    +            $this->assertEquals($expected_answer, $this->redis->incr('answer', 10));
    +
    +            $this->assertTrue($this->redis->set('pi', 3.04));
    +            $this->assertEquals($expected_pi, $this->redis->incrByFloat('pi', 0.1));
    +
    +            $this->assertEquals(1, $this->redis->hset('hash', 'answer', 32));
    +            $this->assertEquals($expected_answer, $this->redis->hIncrBy('hash', 'answer', 10));
    +
    +            $this->assertEquals(1, $this->redis->hset('hash', 'pi', 3.04));
    +            $this->assertEquals($expected_pi, $this->redis->hIncrByFloat('hash', 'pi', 0.1));
    +        }
    +
    +        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
    +        $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
    +        $this->redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, false);
    +    }
    +
    +    function testIgnoreNumbersReturnTypes() {
    +        $combinations = $this->cartesianProduct([
    +            [false, true],
    +            array_filter($this->getSerializers(), function($s) {
    +                return $s !== Redis::SERIALIZER_NONE;
    +            }),
    +            array_filter($this->getCompressors(), function($c) {
    +                return $c !== Redis::COMPRESSION_NONE;
    +            }),
    +        ]);
    +
    +        foreach ($combinations as [$ignore, $serializer, $compression]) {
    +            $this->redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, $ignore);
    +            $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
    +            $this->redis->setOption(Redis::OPT_COMPRESSION, $compression);
    +
    +            foreach ([42, 3.14] as $value) {
    +                $this->assertTrue($this->redis->set('key', $value));
    +
    +                /* There's a known issue in the PHP JSON parser, which
    +                   can stringify numbers. Unclear the root cause */
    +                if ($serializer == Redis::SERIALIZER_JSON) {
    +                    $this->assertEqualsWeak($value, $this->redis->get('key'));
    +                } else {
    +                    $this->assertEquals($value, $this->redis->get('key'));
    +                }
    +            }
    +        }
    +
    +        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
    +        $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
    +        $this->redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, false);
    +    }
    +
         public function testSerializerIGBinary() {
             if ( ! defined('Redis::SERIALIZER_IGBINARY'))
                 $this->markTestSkipped('Redis::SERIALIZER_IGBINARY is not defined');
    
    From 29e5cf0d8c03069aa34c2a63322951fdf2c268c2 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Tue, 28 Jan 2025 08:30:55 -0800
    Subject: [PATCH 1959/1986] Minor refactor of ignroe numbers option
    
    * We want to run the logic if either a serializer OR a compression
      option is set.
    * IEE754 doubles can theoretically have a huge number of characters.
    ---
     library.c | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/library.c b/library.c
    index e80cab3bfc..35883fff38 100644
    --- a/library.c
    +++ b/library.c
    @@ -3882,10 +3882,10 @@ redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) {
         size_t len;
         char *buf;
     
    -    if (UNEXPECTED((redis_sock->serializer != REDIS_SERIALIZER_NONE &&
    +    if (UNEXPECTED((redis_sock->serializer != REDIS_SERIALIZER_NONE ||
                         redis_sock->compression != REDIS_COMPRESSION_NONE) &&
                         redis_sock->pack_ignore_numbers) &&
    -                    srclen > 0 && srclen < 24)
    +                    srclen > 0 && srclen < 512)
         {
             switch (is_numeric_string(src, srclen, &lval, &dval, 0)) {
                 case IS_LONG:
    
    From abb0f6ccc827f240a1de53633225abbc2848fc3a Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Wed, 5 Feb 2025 13:40:19 -0800
    Subject: [PATCH 1960/1986] Add details to the option doc block
    
    ---
     redis.stub.php         | 19 +++++++++++++++++++
     redis_arginfo.h        |  2 +-
     redis_legacy_arginfo.h |  2 +-
     3 files changed, 21 insertions(+), 2 deletions(-)
    
    diff --git a/redis.stub.php b/redis.stub.php
    index 9a41768661..8d0b7658c7 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -155,6 +155,25 @@ class Redis {
          * @var int
          * @cvalue REDIS_OPT_PACK_IGNORE_NUMBERS
          *
    +     * When enabled, this option tells PhpRedis to ignore purely numeric values
    +     * when packing and unpacking data. This does not include numeric strings.
    +     * If you want numeric strings to be ignored, typecast them to an int or float.
    +     *
    +     * The primary purpose of this option is to make it more ergonomic when
    +     * setting keys that will later be incremented or decremented.
    +     *
    +     * Note: This option incurs a small performance penalty when reading data
    +     * because we have to see if the data is a string representation of an int
    +     * or float.
    +     *
    +     * @example
    +     * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);
    +     * $redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, true);
    +     *
    +     * $redis->set('answer', 32);
    +     *
    +     * var_dump($redis->incrBy('answer', 10));  // int(42)
    +     * var_dump($redis->get('answer'));         // int(42)
          */
         public const OPT_PACK_IGNORE_NUMBERS = UNKNOWN;
     
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index fb9cf97d3f..072e1fb715 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 78283cf59cefb411c09adf7a0f0bd234c65327b3 */
    + * Stub hash: 3c4051fdd9f860523bcd72aba260b1af823d1d9c */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index c382766c61..27f0c44970 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 78283cf59cefb411c09adf7a0f0bd234c65327b3 */
    + * Stub hash: 3c4051fdd9f860523bcd72aba260b1af823d1d9c */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    
    From 9036ffca6adf0b5c8b2f4b08d9552b6d38a4bc33 Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Thu, 30 Jan 2025 20:15:00 +0200
    Subject: [PATCH 1961/1986] Add getWithMeta method
    
    ---
     cluster_library.c              | 39 ++++++++++++++++----------
     cluster_library.h              |  2 ++
     common.h                       |  7 +++++
     library.c                      | 51 +++++++++++++++++++++-------------
     library.h                      |  1 +
     redis.c                        | 35 +++++++++++++++++++----
     redis.stub.php                 | 10 +++++++
     redis_arginfo.h                | 18 +++++++-----
     redis_cluster.c                | 18 +++++++++++-
     redis_cluster.h                |  1 +
     redis_cluster.stub.php         |  5 ++++
     redis_cluster_arginfo.h        | 18 +++++++-----
     redis_cluster_legacy_arginfo.h |  6 +++-
     redis_legacy_arginfo.h         |  6 +++-
     tests/RedisClusterTest.php     | 15 ++++++++++
     tests/RedisTest.php            | 16 +++++++++++
     16 files changed, 192 insertions(+), 56 deletions(-)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index e919e2b7fa..45a60e2384 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -1677,27 +1677,33 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
                                   void *ctx)
     {
         char *resp;
    +    zval z_unpacked, z_ret, *zv;
     
         // Make sure we can read the response
    -    if (c->reply_type != TYPE_BULK ||
    -       (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL)
    -    {
    -        CLUSTER_RETURN_FALSE(c);
    +    if (c->reply_type != TYPE_BULK) {
    +        ZVAL_FALSE(&z_unpacked);
    +        c->reply_len = 0;
    +    } else if ((resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) {
    +        ZVAL_FALSE(&z_unpacked);
    +    } else {
    +        if (!redis_unpack(c->flags, resp, c->reply_len, &z_unpacked)) {
    +            ZVAL_STRINGL_FAST(&z_unpacked, resp, c->reply_len);
    +        }
    +        efree(resp);
    +    }
    +
    +    if (c->flags->flags & PHPREDIS_WITH_METADATA) {
    +        redis_with_metadata(&z_ret, &z_unpacked, c->reply_len);
    +        zv = &z_ret;
    +    } else {
    +        zv = &z_unpacked;
         }
     
         if (CLUSTER_IS_ATOMIC(c)) {
    -        if (!redis_unpack(c->flags, resp, c->reply_len, return_value)) {
    -            CLUSTER_RETURN_STRING(c, resp, c->reply_len);
    -        }
    +        RETVAL_ZVAL(zv, 0, 1);
         } else {
    -        zval z_unpacked;
    -        if (redis_unpack(c->flags, resp, c->reply_len, &z_unpacked)) {
    -            add_next_index_zval(&c->multi_resp, &z_unpacked);
    -        } else {
    -            add_next_index_stringl(&c->multi_resp, resp, c->reply_len);
    -        }
    +        add_next_index_zval(&c->multi_resp, zv);
         }
    -    efree(resp);
     }
     
     /* Bulk response where we expect a double */
    @@ -2553,8 +2559,9 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
                                          redisCluster *c, void *ctx)
     {
         zval *multi_resp = &c->multi_resp;
    -    array_init(multi_resp);
    +    uint8_t flags = c->flags->flags;
     
    +    array_init(multi_resp);
         clusterFoldItem *fi = c->multi_head;
         while (fi) {
             /* Make sure our transaction didn't fail here */
    @@ -2570,7 +2577,9 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
                     RETURN_FALSE;
                 }
     
    +            c->flags->flags = fi->flags;
                 fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx);
    +            c->flags->flags = flags;
             } else {
                 /* Just add false */
                 add_next_index_bool(multi_resp, 0);
    diff --git a/cluster_library.h b/cluster_library.h
    index cef7e75412..3adfaf00a0 100644
    --- a/cluster_library.h
    +++ b/cluster_library.h
    @@ -264,6 +264,8 @@ struct clusterFoldItem {
     
         /* Next item in our list */
         struct clusterFoldItem *next;
    +
    +    uint8_t flags;
     };
     
     /* Key and value container, with info if they need freeing */
    diff --git a/common.h b/common.h
    index c1ed664791..5720f8d2f1 100644
    --- a/common.h
    +++ b/common.h
    @@ -152,6 +152,7 @@ typedef enum {
     #define PIPELINE 2
     
     #define PHPREDIS_DEBUG_LOGGING 0
    +#define PHPREDIS_WITH_METADATA 1
     
     #if PHP_VERSION_ID < 80000
     #define Z_PARAM_ARRAY_HT_OR_NULL(dest) \
    @@ -184,6 +185,7 @@ typedef enum {
     #define REDIS_SAVE_CALLBACK(callback, closure_context) do { \
         fold_item *fi = redis_add_reply_callback(redis_sock); \
         fi->fun = callback; \
    +    fi->flags = redis_sock->flags; \
         fi->ctx = closure_context; \
     } while (0)
     
    @@ -266,6 +268,9 @@ static inline int redis_strncmp(const char *s1, const char *s2, size_t n) {
     #define REDIS_ENABLE_MODE(redis_sock, m) (redis_sock->mode |= m)
     #define REDIS_DISABLE_MODE(redis_sock, m) (redis_sock->mode &= ~m)
     
    +#define REDIS_ENABLE_FLAG(redis_sock, f) (redis_sock->flags |= f)
    +#define REDIS_DISABLE_FLAG(redis_sock, f) (redis_sock->flags &= ~f)
    +
     /* HOST_NAME_MAX doesn't exist everywhere */
     #ifndef HOST_NAME_MAX
         #if defined(_POSIX_HOST_NAME_MAX)
    @@ -325,6 +330,7 @@ typedef struct {
         int                 sentinel;
         size_t              txBytes;
         size_t              rxBytes;
    +    uint8_t             flags;
     } RedisSock;
     /* }}} */
     
    @@ -334,6 +340,7 @@ typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*,
     
     typedef struct fold_item {
         FailableResultCallback fun;
    +    uint8_t flags;
         void *ctx;
     } fold_item;
     
    diff --git a/library.c b/library.c
    index 35883fff38..05202788d1 100644
    --- a/library.c
    +++ b/library.c
    @@ -2669,32 +2669,34 @@ PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
     
         char *response;
         int response_len;
    +    zval z_unpacked, z_ret, *zv;
    +    zend_bool ret;
     
    -    if ((response = redis_sock_read(redis_sock, &response_len))
    -                                    == NULL)
    -    {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    +    if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
    +        ZVAL_FALSE(&z_unpacked);
    +        ret = FAILURE;
    +    } else {
    +        if (!redis_unpack(redis_sock, response, response_len, &z_unpacked)) {
    +            ZVAL_STRINGL_FAST(&z_unpacked, response, response_len);
             }
    -        return FAILURE;
    +        efree(response);
    +        ret = SUCCESS;
    +    }
    +
    +    if (redis_sock->flags & PHPREDIS_WITH_METADATA) {
    +        redis_with_metadata(&z_ret, &z_unpacked, response_len);
    +        zv = &z_ret;
    +    } else {
    +        zv = &z_unpacked;
         }
    +
         if (IS_ATOMIC(redis_sock)) {
    -        if (!redis_unpack(redis_sock, response, response_len, return_value)) {
    -            RETVAL_STRINGL_FAST(response, response_len);
    -        }
    +        RETVAL_ZVAL(zv, 0, 1);
         } else {
    -        zval z_unpacked;
    -        if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) {
    -            add_next_index_zval(z_tab, &z_unpacked);
    -        } else {
    -            redis_add_next_index_stringl(z_tab, response, response_len);
    -        }
    +        add_next_index_zval(z_tab, zv);
         }
     
    -    efree(response);
    -    return SUCCESS;
    +    return ret;
     }
     
     /* like string response, but never unserialized. */
    @@ -4455,6 +4457,17 @@ int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass)
         return FAILURE;
     }
     
    +PHP_REDIS_API void redis_with_metadata(zval *zdst, zval *zsrc, zend_long length) {
    +    zval z_sub;
    +
    +    array_init(zdst);
    +    add_next_index_zval(zdst, zsrc);
    +
    +    array_init(&z_sub);
    +    add_assoc_long_ex(&z_sub, ZEND_STRL("length"), length);
    +    add_next_index_zval(zdst, &z_sub);
    +}
    +
     /* Helper methods to extract configuration settings from a hash table */
     
     zval *redis_hash_str_find_type(HashTable *ht, const char *key, int keylen, int type) {
    diff --git a/library.h b/library.h
    index 47c339ada2..270694112a 100644
    --- a/library.h
    +++ b/library.h
    @@ -44,6 +44,7 @@ fold_item* redis_add_reply_callback(RedisSock *redis_sock);
     void redis_free_reply_callbacks(RedisSock *redis_sock);
     
     PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass);
    +PHP_REDIS_API void redis_with_metadata(zval *zdst, zval *zsrc, zend_long length);
     
     int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len);
     int redis_cmd_append_sstr(smart_string *str, char *append, int append_len);
    diff --git a/redis.c b/redis.c
    index 4ec516c1b5..d049989771 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -760,11 +760,32 @@ PHP_METHOD(Redis, reset)
     }
     /* }}} */
     
    +static void
    +redis_get_passthru(INTERNAL_FUNCTION_PARAMETERS)
    +{
    +    REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response);
    +}
    +
     /* {{{ proto string Redis::get(string key)
      */
     PHP_METHOD(Redis, get)
     {
    -    REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response);
    +    redis_get_passthru(INTERNAL_FUNCTION_PARAM_PASSTHRU);
    +}
    +/* }}} */
    +
    +/* {{{ proto Redis|array|false Redis::getWithMeta(string key)
    + */
    +PHP_METHOD(Redis, getWithMeta)
    +{
    +    RedisSock *redis_sock;
    +    if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
    +        RETURN_FALSE;
    +    }
    +
    +    REDIS_ENABLE_FLAG(redis_sock, PHPREDIS_WITH_METADATA);
    +    redis_get_passthru(INTERNAL_FUNCTION_PARAM_PASSTHRU);
    +    REDIS_DISABLE_FLAG(redis_sock, PHPREDIS_WITH_METADATA);
     }
     /* }}} */
     
    @@ -2067,13 +2088,17 @@ PHP_REDIS_API int
     redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
                                                RedisSock *redis_sock, zval *z_tab)
     {
    -    fold_item fi;
    +    fold_item *fi;
    +    uint8_t flags;
         size_t i;
     
    +    flags = redis_sock->flags;
         for (i = 0; i < redis_sock->reply_callback_count; i++) {
    -        fi = redis_sock->reply_callback[i];
    -        if (fi.fun) {
    -            fi.fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi.ctx);
    +        fi = &redis_sock->reply_callback[i];
    +        if (fi->fun) {
    +            redis_sock->flags = fi->flags;
    +            fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi->ctx);
    +            redis_sock->flags = flags;
                 continue;
             }
             size_t len;
    diff --git a/redis.stub.php b/redis.stub.php
    index 8d0b7658c7..5f2e7693ee 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1467,6 +1467,16 @@ public function geosearchstore(string $dst, string $src, array|string $position,
          */
         public function get(string $key): mixed;
     
    +    /**
    +     * Retrieve a value and metadata of key.
    +     *
    +     * @param  string  $key The key to query
    +     * @return Redis|array|false
    +     *
    +     * @example $redis->getWithMeta('foo');
    +     */
    +    public function getWithMeta(string $key): Redis|array|false;
    +
         /**
          * Get the authentication information on the connection, if any.
          *
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index 072e1fb715..e880450eb2 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 3c4051fdd9f860523bcd72aba260b1af823d1d9c */
    + * Stub hash: 6dd5a9e9d1d5ed8a78e248c99352232e30046f28 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -323,6 +323,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_get, 0, 1, IS_MIXED,
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getWithMeta, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +ZEND_END_ARG_INFO()
    +
     ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getAuth, 0, 0, IS_MIXED, 0)
     ZEND_END_ARG_INFO()
     
    @@ -404,9 +408,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGet, 0, 2, IS_MIXED
     	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hGetAll, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
    -	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    -ZEND_END_ARG_INFO()
    +#define arginfo_class_Redis_hGetAll arginfo_class_Redis_getWithMeta
     
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrBy, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    @@ -420,7 +422,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrByFloat, 0,
     	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_Redis_hKeys arginfo_class_Redis_hGetAll
    +#define arginfo_class_Redis_hKeys arginfo_class_Redis_getWithMeta
     
     #define arginfo_class_Redis_hLen arginfo_class_Redis_expiretime
     
    @@ -455,7 +457,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hStrLen, 0, 2, R
     	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_Redis_hVals arginfo_class_Redis_hGetAll
    +#define arginfo_class_Redis_hVals arginfo_class_Redis_getWithMeta
     
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    @@ -747,7 +749,7 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_sInterStore arginfo_class_Redis_del
     
    -#define arginfo_class_Redis_sMembers arginfo_class_Redis_hGetAll
    +#define arginfo_class_Redis_sMembers arginfo_class_Redis_getWithMeta
     
     #define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash
     
    @@ -1249,6 +1251,7 @@ ZEND_METHOD(Redis, georadiusbymember_ro);
     ZEND_METHOD(Redis, geosearch);
     ZEND_METHOD(Redis, geosearchstore);
     ZEND_METHOD(Redis, get);
    +ZEND_METHOD(Redis, getWithMeta);
     ZEND_METHOD(Redis, getAuth);
     ZEND_METHOD(Redis, getBit);
     ZEND_METHOD(Redis, getEx);
    @@ -1507,6 +1510,7 @@ static const zend_function_entry class_Redis_methods[] = {
     	ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, getWithMeta, arginfo_class_Redis_getWithMeta, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getEx, arginfo_class_Redis_getEx, ZEND_ACC_PUBLIC)
    diff --git a/redis_cluster.c b/redis_cluster.c
    index 1106a42982..ee11f258af 100644
    --- a/redis_cluster.c
    +++ b/redis_cluster.c
    @@ -275,12 +275,28 @@ PHP_METHOD(RedisCluster, close) {
         RETURN_TRUE;
     }
     
    +static void
    +cluster_get_passthru(INTERNAL_FUNCTION_PARAMETERS)
    +{
    +    CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp, 1);
    +}
    +
     /* {{{ proto string RedisCluster::get(string key) */
     PHP_METHOD(RedisCluster, get) {
    -    CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp, 1);
    +    cluster_get_passthru(INTERNAL_FUNCTION_PARAM_PASSTHRU);
     }
     /* }}} */
     
    +/* {{{ proto array|false RedisCluster::getWithMeta(string key) */
    +PHP_METHOD(RedisCluster, getWithMeta) {
    +    redisCluster *c = GET_CONTEXT();
    +    REDIS_ENABLE_FLAG(c->flags, PHPREDIS_WITH_METADATA);
    +    cluster_get_passthru(INTERNAL_FUNCTION_PARAM_PASSTHRU);
    +    REDIS_DISABLE_FLAG(c->flags, PHPREDIS_WITH_METADATA);
    +}
    +/* }}} */
    +
    +
     /* {{{ proto bool RedisCluster::set(string key, string value) */
     PHP_METHOD(RedisCluster, set) {
         CLUSTER_PROCESS_CMD(set, cluster_set_resp, 0);
    diff --git a/redis_cluster.h b/redis_cluster.h
    index ebef92184e..49e1bcd8d0 100644
    --- a/redis_cluster.h
    +++ b/redis_cluster.h
    @@ -22,6 +22,7 @@
         _item->slot = slot; \
         _item->ctx = ctx; \
         _item->next = NULL; \
    +    _item->flags = c->flags->flags; \
         if(c->multi_head == NULL) { \
             c->multi_head = _item; \
             c->multi_curr = _item; \
    diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
    index d5cab71f20..56c91f4ede 100644
    --- a/redis_cluster.stub.php
    +++ b/redis_cluster.stub.php
    @@ -390,6 +390,11 @@ public function geosearchstore(string $dst, string $src, array|string $position,
          */
         public function get(string $key): mixed;
     
    +    /**
    +     * @see Redis::getWithMeta
    +     */
    +    public function getWithMeta(string $key): RedisCluster|array|false;
    +
         /**
          * @see Redis::getEx
          */
    diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
    index 85079322bf..b182584c2d 100644
    --- a/redis_cluster_arginfo.h
    +++ b/redis_cluster_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: b9310b607794caa862d509ba316a2a512d2736fe */
    + * Stub hash: 5966b99fd578eca94880e09539542edfbcbcdaed */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
    @@ -325,6 +325,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_get, 0, 1, IS
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getWithMeta, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +ZEND_END_ARG_INFO()
    +
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getex, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
    @@ -379,9 +383,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hget, 0, 2, I
     	ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hgetall, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
    -	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    -ZEND_END_ARG_INFO()
    +#define arginfo_class_RedisCluster_hgetall arginfo_class_RedisCluster_getWithMeta
     
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hincrby, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    @@ -395,7 +397,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hincrbyfl
     	ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0)
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster_hgetall
    +#define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster_getWithMeta
     
     #define arginfo_class_RedisCluster_hlen arginfo_class_RedisCluster_expiretime
     
    @@ -451,7 +453,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hstrlen,
     	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster_hgetall
    +#define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster_getWithMeta
     
     #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr
     
    @@ -754,7 +756,7 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script
     
    -#define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster_hgetall
    +#define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster_getWithMeta
     
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_smove, 0, 3, RedisCluster, MAY_BE_BOOL)
     	ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
    @@ -1127,6 +1129,7 @@ ZEND_METHOD(RedisCluster, georadiusbymember_ro);
     ZEND_METHOD(RedisCluster, geosearch);
     ZEND_METHOD(RedisCluster, geosearchstore);
     ZEND_METHOD(RedisCluster, get);
    +ZEND_METHOD(RedisCluster, getWithMeta);
     ZEND_METHOD(RedisCluster, getex);
     ZEND_METHOD(RedisCluster, getbit);
     ZEND_METHOD(RedisCluster, getlasterror);
    @@ -1356,6 +1359,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, getWithMeta, arginfo_class_RedisCluster_getWithMeta, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getex, arginfo_class_RedisCluster_getex, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC)
    diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
    index 64d695108d..99edcca37a 100644
    --- a/redis_cluster_legacy_arginfo.h
    +++ b/redis_cluster_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: b9310b607794caa862d509ba316a2a512d2736fe */
    + * Stub hash: 5966b99fd578eca94880e09539542edfbcbcdaed */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_INFO(0, name)
    @@ -295,6 +295,8 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_RedisCluster_get arginfo_class_RedisCluster__prefix
     
    +#define arginfo_class_RedisCluster_getWithMeta arginfo_class_RedisCluster__prefix
    +
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getex, 0, 0, 1)
     	ZEND_ARG_INFO(0, key)
     	ZEND_ARG_INFO(0, options)
    @@ -969,6 +971,7 @@ ZEND_METHOD(RedisCluster, georadiusbymember_ro);
     ZEND_METHOD(RedisCluster, geosearch);
     ZEND_METHOD(RedisCluster, geosearchstore);
     ZEND_METHOD(RedisCluster, get);
    +ZEND_METHOD(RedisCluster, getWithMeta);
     ZEND_METHOD(RedisCluster, getex);
     ZEND_METHOD(RedisCluster, getbit);
     ZEND_METHOD(RedisCluster, getlasterror);
    @@ -1198,6 +1201,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, getWithMeta, arginfo_class_RedisCluster_getWithMeta, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getex, arginfo_class_RedisCluster_getex, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC)
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 27f0c44970..4fd45c7e37 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 3c4051fdd9f860523bcd72aba260b1af823d1d9c */
    + * Stub hash: 6dd5a9e9d1d5ed8a78e248c99352232e30046f28 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    @@ -301,6 +301,8 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_get arginfo_class_Redis__prefix
     
    +#define arginfo_class_Redis_getWithMeta arginfo_class_Redis__prefix
    +
     #define arginfo_class_Redis_getAuth arginfo_class_Redis___destruct
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2)
    @@ -1092,6 +1094,7 @@ ZEND_METHOD(Redis, georadiusbymember_ro);
     ZEND_METHOD(Redis, geosearch);
     ZEND_METHOD(Redis, geosearchstore);
     ZEND_METHOD(Redis, get);
    +ZEND_METHOD(Redis, getWithMeta);
     ZEND_METHOD(Redis, getAuth);
     ZEND_METHOD(Redis, getBit);
     ZEND_METHOD(Redis, getEx);
    @@ -1350,6 +1353,7 @@ static const zend_function_entry class_Redis_methods[] = {
     	ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, getWithMeta, arginfo_class_Redis_getWithMeta, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getEx, arginfo_class_Redis_getEx, ZEND_ACC_PUBLIC)
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index 1be83c2aee..7cea87f6ca 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -248,6 +248,21 @@ public function testClient() {
             $this->assertTrue($this->redis->client($key, 'kill', $addr));
         }
     
    +    public function testGetWithMeta() {
    +        $this->redis->del('key');
    +        $this->assertFalse($this->redis->get('key'));
    +        $this->assertEquals([false, ['length' => -1]], $this->redis->getWithMeta('key'));
    +
    +        $this->assertEquals([true, ['value', ['length' => strlen('value')]]], $this->redis->multi()->set('key', 'value')->getWithMeta('key')->exec());
    +
    +        $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER);
    +        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
    +        $this->assertTrue($this->redis->set('key', false));
    +        $this->assertEquals([false, ['length' => strlen(serialize(false))]], $this->redis->getWithMeta('key'));
    +        $this->assertFalse($this->redis->get('key'));
    +        $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
    +    }
    +
         public function testTime() {
             [$sec, $usec] = $this->redis->time(uniqid());
             $this->assertEquals(strval(intval($sec)), strval($sec));
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 3b46622337..93a106c694 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -5800,6 +5800,22 @@ public function testPackHelpers() {
             $this->redis->setOption(Redis::OPT_COMPRESSION, $oldcmp);
         }
     
    +    public function testGetWithMeta() {
    +        $this->redis->del('key');
    +        $this->assertFalse($this->redis->get('key'));
    +        $this->assertEquals([false, ['length' => -1]], $this->redis->getWithMeta('key'));
    +
    +        $this->assertEquals([false, [false, ['length' => -1]]], $this->redis->pipeline()->get('key')->getWithMeta('key')->exec());
    +        $this->assertEquals([true, ['value', ['length' => strlen('value')]]], $this->redis->multi()->set('key', 'value')->getWithMeta('key')->exec());
    +
    +        $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER);
    +        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
    +        $this->assertTrue($this->redis->set('key', false));
    +        $this->assertEquals([false, ['length' => strlen(serialize(false))]], $this->redis->getWithMeta('key'));
    +        $this->assertFalse($this->redis->get('key'));
    +        $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
    +    }
    +
         public function testPrefix() {
             // no prefix
             $this->redis->setOption(Redis::OPT_PREFIX, '');
    
    From 1b72964e39f4232fe3c56a40f46c4dcea1ca05e1 Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Wed, 19 Feb 2025 12:59:07 +0200
    Subject: [PATCH 1962/1986] Update CHANGELOG.md
    
    ---
     CHANGELOG.md | 9 +++++++++
     1 file changed, 9 insertions(+)
    
    diff --git a/CHANGELOG.md b/CHANGELOG.md
    index 12a5a4c155..5d1cf0aa4f 100644
    --- a/CHANGELOG.md
    +++ b/CHANGELOG.md
    @@ -5,6 +5,15 @@ All changes to phpredis will be documented in this file.
     We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
     and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
     
    +## [Unreleased]
    +
    +### Added
    +
    +- Added `getWithMeta` method
    +  [9036ffca](https://github.com/phpredis/phpredis/commit/9036ffca)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +
    +
     ## [6.1.0] - 2024-10-04 ([Github](https://github.com/phpredis/phpredis/releases/6.1.0), [PECL](https://pecl.php.net/package/redis/6.1.0))
     
     **NOTE**: There were no changes to C code between 6.1.0RC2 and 6.1.0.
    
    From 807f806fe8a4df77691c869289db24358a684f7f Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Tue, 25 Feb 2025 17:18:13 +0200
    Subject: [PATCH 1963/1986] Reorganize tests
    
    ---
     tests/RedisClusterTest.php | 38 ++++++++++++++++++++++++--
     tests/RedisTest.php        | 56 +++++++++++++++++++++++++++++++++++---
     2 files changed, 87 insertions(+), 7 deletions(-)
    
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index 7cea87f6ca..9e4150b037 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -251,14 +251,46 @@ public function testClient() {
         public function testGetWithMeta() {
             $this->redis->del('key');
             $this->assertFalse($this->redis->get('key'));
    -        $this->assertEquals([false, ['length' => -1]], $this->redis->getWithMeta('key'));
     
    -        $this->assertEquals([true, ['value', ['length' => strlen('value')]]], $this->redis->multi()->set('key', 'value')->getWithMeta('key')->exec());
    +        $result = $this->redis->getWithMeta('key');
    +        $this->assertIsArray($result, 2);
    +        $this->assertArrayKeyEquals($result, 0, false);
    +        $this->assertArrayKey($result, 1, function ($metadata) {
    +            $this->assertIsArray($metadata);
    +            $this->assertArrayKeyEquals($metadata, 'length', -1);
    +            return true;
    +        });
    +
    +        $batch = $this->redis->multi()
    +            ->set('key', 'value')
    +            ->getWithMeta('key')
    +            ->exec();
    +        $this->assertIsArray($batch, 2);
    +        $this->assertArrayKeyEquals($batch, 0, true);
    +        $this->assertArrayKey($batch, 1, function ($result) {
    +            $this->assertIsArray($result, 2);
    +            $this->assertArrayKeyEquals($result, 0, 'value');
    +            $this->assertArrayKey($result, 1, function ($metadata) {
    +                $this->assertIsArray($metadata);
    +                $this->assertArrayKeyEquals($metadata, 'length', strlen('value'));
    +                return true;
    +            });
    +            return true;
    +        });
     
             $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER);
             $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
             $this->assertTrue($this->redis->set('key', false));
    -        $this->assertEquals([false, ['length' => strlen(serialize(false))]], $this->redis->getWithMeta('key'));
    +
    +        $result = $this->redis->getWithMeta('key');
    +        $this->assertIsArray($result, 2);
    +        $this->assertArrayKeyEquals($result, 0, false);
    +        $this->assertArrayKey($result, 1, function ($metadata) {
    +            $this->assertIsArray($metadata);
    +            $this->assertArrayKeyEquals($metadata, 'length', strlen(serialize(false)));
    +            return true;
    +        });
    +
             $this->assertFalse($this->redis->get('key'));
             $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
         }
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 93a106c694..c16d1e02ea 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -5803,15 +5803,63 @@ public function testPackHelpers() {
         public function testGetWithMeta() {
             $this->redis->del('key');
             $this->assertFalse($this->redis->get('key'));
    -        $this->assertEquals([false, ['length' => -1]], $this->redis->getWithMeta('key'));
     
    -        $this->assertEquals([false, [false, ['length' => -1]]], $this->redis->pipeline()->get('key')->getWithMeta('key')->exec());
    -        $this->assertEquals([true, ['value', ['length' => strlen('value')]]], $this->redis->multi()->set('key', 'value')->getWithMeta('key')->exec());
    +        $result = $this->redis->getWithMeta('key');
    +        $this->assertIsArray($result, 2);
    +        $this->assertArrayKeyEquals($result, 0, false);
    +        $this->assertArrayKey($result, 1, function ($metadata) {
    +            $this->assertIsArray($metadata);
    +            $this->assertArrayKeyEquals($metadata, 'length', -1);
    +            return true;
    +        });
    +
    +        $batch = $this->redis->pipeline()
    +            ->get('key')
    +            ->getWithMeta('key')
    +            ->exec();
    +        $this->assertIsArray($batch, 2);
    +        $this->assertArrayKeyEquals($batch, 0, false);
    +        $this->assertArrayKey($batch, 1, function ($result) {
    +            $this->assertIsArray($result, 2);
    +            $this->assertArrayKeyEquals($result, 0, false);
    +            $this->assertArrayKey($result, 1, function ($metadata) {
    +                $this->assertIsArray($metadata);
    +                $this->assertArrayKeyEquals($metadata, 'length', -1);
    +                return true;
    +            });
    +            return true;
    +        });
    +
    +        $batch = $this->redis->multi()
    +            ->set('key', 'value')
    +            ->getWithMeta('key')
    +            ->exec();
    +        $this->assertIsArray($batch, 2);
    +        $this->assertArrayKeyEquals($batch, 0, true);
    +        $this->assertArrayKey($batch, 1, function ($result) {
    +            $this->assertIsArray($result, 2);
    +            $this->assertArrayKeyEquals($result, 0, 'value');
    +            $this->assertArrayKey($result, 1, function ($metadata) {
    +                $this->assertIsArray($metadata);
    +                $this->assertArrayKeyEquals($metadata, 'length', strlen('value'));
    +                return true;
    +            });
    +            return true;
    +        });
     
             $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER);
             $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
             $this->assertTrue($this->redis->set('key', false));
    -        $this->assertEquals([false, ['length' => strlen(serialize(false))]], $this->redis->getWithMeta('key'));
    +
    +        $result = $this->redis->getWithMeta('key');
    +        $this->assertIsArray($result, 2);
    +        $this->assertArrayKeyEquals($result, 0, false);
    +        $this->assertArrayKey($result, 1, function ($metadata) {
    +            $this->assertIsArray($metadata);
    +            $this->assertArrayKeyEquals($metadata, 'length', strlen(serialize(false)));
    +            return true;
    +        });
    +
             $this->assertFalse($this->redis->get('key'));
             $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
         }
    
    From d342e4ac18723607b001deb593c8d45e40bbc4c8 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 6 Mar 2025 08:53:21 -0800
    Subject: [PATCH 1964/1986] Implement `GETDEL` for `RedisCluster`
    
    Fixes #2629
    ---
     redis_cluster.c                | 6 ++++++
     redis_cluster.stub.php         | 5 +++++
     redis_cluster_arginfo.h        | 6 +++++-
     redis_cluster_legacy_arginfo.h | 6 +++++-
     tests/RedisTest.php            | 7 +++++++
     5 files changed, 28 insertions(+), 2 deletions(-)
    
    diff --git a/redis_cluster.c b/redis_cluster.c
    index ee11f258af..a412130df9 100644
    --- a/redis_cluster.c
    +++ b/redis_cluster.c
    @@ -287,6 +287,12 @@ PHP_METHOD(RedisCluster, get) {
     }
     /* }}} */
     
    +/* {{{ proto string RedisCluster::getdel(string key) */
    +PHP_METHOD(RedisCluster, getdel) {
    +    CLUSTER_PROCESS_KW_CMD("GETDEL", redis_key_cmd, cluster_bulk_resp, 1);
    +}
    +/* }}} */
    +
     /* {{{ proto array|false RedisCluster::getWithMeta(string key) */
     PHP_METHOD(RedisCluster, getWithMeta) {
         redisCluster *c = GET_CONTEXT();
    diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
    index 56c91f4ede..58cced5777 100644
    --- a/redis_cluster.stub.php
    +++ b/redis_cluster.stub.php
    @@ -390,6 +390,11 @@ public function geosearchstore(string $dst, string $src, array|string $position,
          */
         public function get(string $key): mixed;
     
    +    /**
    +     * @see Redis::getdel
    +     */
    +    public function getdel(string $key): mixed;
    +
         /**
          * @see Redis::getWithMeta
          */
    diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
    index b182584c2d..b3fb58475a 100644
    --- a/redis_cluster_arginfo.h
    +++ b/redis_cluster_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 5966b99fd578eca94880e09539542edfbcbcdaed */
    + * Stub hash: 43a43fa735ced4b48a361078ac8a10fb62cb1244 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
    @@ -325,6 +325,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_get, 0, 1, IS
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    +#define arginfo_class_RedisCluster_getdel arginfo_class_RedisCluster_get
    +
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getWithMeta, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     ZEND_END_ARG_INFO()
    @@ -1129,6 +1131,7 @@ ZEND_METHOD(RedisCluster, georadiusbymember_ro);
     ZEND_METHOD(RedisCluster, geosearch);
     ZEND_METHOD(RedisCluster, geosearchstore);
     ZEND_METHOD(RedisCluster, get);
    +ZEND_METHOD(RedisCluster, getdel);
     ZEND_METHOD(RedisCluster, getWithMeta);
     ZEND_METHOD(RedisCluster, getex);
     ZEND_METHOD(RedisCluster, getbit);
    @@ -1359,6 +1362,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, getdel, arginfo_class_RedisCluster_getdel, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getWithMeta, arginfo_class_RedisCluster_getWithMeta, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getex, arginfo_class_RedisCluster_getex, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC)
    diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
    index 99edcca37a..d117db522a 100644
    --- a/redis_cluster_legacy_arginfo.h
    +++ b/redis_cluster_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 5966b99fd578eca94880e09539542edfbcbcdaed */
    + * Stub hash: 43a43fa735ced4b48a361078ac8a10fb62cb1244 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_INFO(0, name)
    @@ -295,6 +295,8 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_RedisCluster_get arginfo_class_RedisCluster__prefix
     
    +#define arginfo_class_RedisCluster_getdel arginfo_class_RedisCluster__prefix
    +
     #define arginfo_class_RedisCluster_getWithMeta arginfo_class_RedisCluster__prefix
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getex, 0, 0, 1)
    @@ -971,6 +973,7 @@ ZEND_METHOD(RedisCluster, georadiusbymember_ro);
     ZEND_METHOD(RedisCluster, geosearch);
     ZEND_METHOD(RedisCluster, geosearchstore);
     ZEND_METHOD(RedisCluster, get);
    +ZEND_METHOD(RedisCluster, getdel);
     ZEND_METHOD(RedisCluster, getWithMeta);
     ZEND_METHOD(RedisCluster, getex);
     ZEND_METHOD(RedisCluster, getbit);
    @@ -1201,6 +1204,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, getdel, arginfo_class_RedisCluster_getdel, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getWithMeta, arginfo_class_RedisCluster_getWithMeta, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getex, arginfo_class_RedisCluster_getex, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC)
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index c16d1e02ea..69aaa5b190 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -654,6 +654,13 @@ public function testGetSet() {
             $this->assertEquals('123', $this->redis->getSet('key', '123'));
         }
     
    +    public function testGetDel() {
    +        $this->redis->del('key');
    +        $this->assertTrue($this->redis->set('key', 'iexist'));
    +        $this->assertEquals('iexist', $this->redis->getDel('key'));
    +        $this->assertEquals(0, $this->redis->exists('key'));
    +    }
    +
         public function testRandomKey() {
             for ($i = 0; $i < 1000; $i++) {
                 $k = $this->redis->randomKey();
    
    From e73130fee0c22a20e11ce1596579df3f6f826974 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 10 Mar 2025 11:46:30 -0700
    Subject: [PATCH 1965/1986] Fix error length calculation + UB sanity check.
    
    For an error reply we're starting at `buf + 1` so we want `len - 1`. As
    a sanity check we now return early if `len < 1`.
    
    Also, make certain that len > 2 for our special detection of `*-1` since
    we're doing `memcmp(buf + 1, "-1", 2);`
    ---
     library.c | 6 +++---
     1 file changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/library.c b/library.c
    index 05202788d1..2a134cee44 100644
    --- a/library.c
    +++ b/library.c
    @@ -765,13 +765,13 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len)
         size_t len;
     
         *buf_len = 0;
    -    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
    +    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || len < 1) {
             return NULL;
         }
     
         switch(inbuf[0]) {
             case '-':
    -            redis_sock_set_err(redis_sock, inbuf+1, len);
    +            redis_sock_set_err(redis_sock, inbuf + 1, len - 1);
     
                 /* Filter our ERROR through the few that should actually throw */
                 redis_error_throw(redis_sock);
    @@ -783,7 +783,7 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len)
     
             case '*':
                 /* For null multi-bulk replies (like timeouts from brpoplpush): */
    -            if(memcmp(inbuf + 1, "-1", 2) == 0) {
    +            if(len > 2 && memcmp(inbuf + 1, "-1", 2) == 0) {
                     return NULL;
                 }
                 REDIS_FALLTHROUGH;
    
    From f73f5fcce55ab9268c4eb40bf93cccdae418c1d2 Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Sun, 16 Mar 2025 11:38:58 +0200
    Subject: [PATCH 1966/1986] Fix arguments order for `SET` command
    
    Redis and Valkey doesn't consider command as invalid if order of arguments
    is changed but other servers like DragonflyDB does.
    In this commit `SET` command is fixed to more strictly follow the specs.
    Also fixed usage of `zend_tmp_string` for `ifeq` argument.
    ---
     redis_commands.c | 31 +++++++++++++------------------
     1 file changed, 13 insertions(+), 18 deletions(-)
    
    diff --git a/redis_commands.c b/redis_commands.c
    index 2d57007ce8..1d2c23360f 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -2293,8 +2293,8 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                       char **cmd, int *cmd_len, short *slot, void **ctx)
     {
         char *key = NULL, *exp_type = NULL, *set_type = NULL;
    -    zend_string *ifeq = NULL, *tmp = NULL;
    -    zval *z_value, *z_opts = NULL;
    +    zval *z_value, *z_opts = NULL, *ifeq = NULL;
    +    zend_string *zstr = NULL, *tmp = NULL;
         smart_string cmdstr = {0};
         zend_long expire = -1;
         zend_bool get = 0;
    @@ -2329,14 +2329,13 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                              zend_string_equals_literal_ci(zkey, "PXAT"))
                 ) {
                     if (redis_try_get_expiry(v, &expire) == FAILURE || expire < 1) {
    -                    zend_tmp_string_release(tmp);
                         setExpiryWarning(v);
                         return FAILURE;
                     }
     
                     exp_type = ZSTR_VAL(zkey);
    -            } else if (zkey && !ifeq && zend_string_equals_literal_ci(zkey, "IFEQ")) {
    -                ifeq = zval_get_tmp_string(v, &tmp);
    +            } else if (zkey && zend_string_equals_literal_ci(zkey, "IFEQ")) {
    +                ifeq = v;
                 } else if (Z_TYPE_P(v) == IS_STRING) {
                     if (zend_string_equals_literal_ci(Z_STR_P(v), "KEEPTTL")) {
                         keep_ttl  = 1;
    @@ -2351,7 +2350,6 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             } ZEND_HASH_FOREACH_END();
         } else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) {
             if (redis_try_get_expiry(z_opts, &expire) == FAILURE || expire < 1) {
    -            zend_tmp_string_release(tmp);
                 setExpiryWarning(z_opts);
                 return FAILURE;
             }
    @@ -2360,14 +2358,12 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         /* Protect the user from syntax errors but give them some info about what's wrong */
         if (exp_type && keep_ttl) {
             php_error_docref(NULL, E_WARNING, "KEEPTTL can't be combined with EX or PX option");
    -        zend_tmp_string_release(tmp);
             return FAILURE;
         }
     
         /* You can't use IFEQ with NX or XX */
         if (set_type && ifeq) {
             php_error_docref(NULL, E_WARNING, "IFEQ can't be combined with NX or XX option");
    -        zend_tmp_string_release(tmp);
             return FAILURE;
         }
     
    @@ -2375,7 +2371,6 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
          * actually execute a SETEX command */
         if (expire > 0 && !exp_type && !set_type && !keep_ttl) {
             *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, z_value);
    -        zend_tmp_string_release(tmp);
             return SUCCESS;
         }
     
    @@ -2388,26 +2383,26 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot);
         redis_cmd_append_sstr_zval(&cmdstr, z_value, redis_sock);
     
    -    if (exp_type) {
    -        redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type));
    -        redis_cmd_append_sstr_long(&cmdstr, (long)expire);
    -    }
    -
         if (ifeq) {
    +        zstr = zval_get_tmp_string(ifeq, &tmp);
             REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IFEQ");
    -        redis_cmd_append_sstr_zstr(&cmdstr, ifeq);
    +        redis_cmd_append_sstr_zstr(&cmdstr, zstr);
    +        zend_tmp_string_release(tmp);
         } else if (set_type) {
             redis_cmd_append_sstr(&cmdstr, set_type, strlen(set_type));
         }
     
    -    if (keep_ttl)
    -        redis_cmd_append_sstr(&cmdstr, "KEEPTTL", 7);
         if (get) {
             REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GET");
             *ctx = PHPREDIS_CTX_PTR;
         }
     
    -    zend_tmp_string_release(tmp);
    +    if (exp_type) {
    +        redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type));
    +        redis_cmd_append_sstr_long(&cmdstr, (long)expire);
    +    } else if (keep_ttl) {
    +        REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "KEEPTTL");
    +    }
     
         /* Push command and length to the caller */
         *cmd = cmdstr.c;
    
    From 056c2dbee7f6379a9f546e46584ace59449847c7 Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Sun, 16 Mar 2025 11:05:35 +0200
    Subject: [PATCH 1967/1986] Introduce `Redis::serverName` and
     `Redis::serverVersion` methods
    
    Right now we can't implement `HELLO` command to switch protocol
    because we don't support new reply types that come with RESP3.
    But we can use `HELLO` reply to expose some server information.
    ---
     common.h               |  9 +++--
     library.c              | 83 ++++++++++++++++++++++++++++++++++++++++++
     library.h              |  3 ++
     redis.c                | 24 ++++++++++++
     redis.stub.php         | 14 +++++++
     redis_arginfo.h        | 14 +++++--
     redis_legacy_arginfo.h | 10 ++++-
     7 files changed, 150 insertions(+), 7 deletions(-)
    
    diff --git a/common.h b/common.h
    index 5720f8d2f1..d87da945be 100644
    --- a/common.h
    +++ b/common.h
    @@ -287,6 +287,11 @@ static inline int redis_strncmp(const char *s1, const char *s2, size_t n) {
     #define RESP_EXEC_CMD          "*1\r\n$4\r\nEXEC\r\n"
     #define RESP_DISCARD_CMD       "*1\r\n$7\r\nDISCARD\r\n"
     
    +typedef struct RedisHello {
    +    zend_string *server;
    +    zend_string *version;
    +} RedisHello;
    +
     /* {{{ struct RedisSock */
     typedef struct {
         php_stream          *stream;
    @@ -310,9 +315,8 @@ typedef struct {
         int                 compression;
         int                 compression_level;
         long                dbNumber;
    -
         zend_string         *prefix;
    -
    +    struct RedisHello   hello;
         short               mode;
         struct fold_item    *reply_callback;
         size_t              reply_callback_count;
    @@ -320,7 +324,6 @@ typedef struct {
         smart_string        pipeline_cmd;
     
         zend_string         *err;
    -
         int                 scan;
     
         int                 readonly;
    diff --git a/library.c b/library.c
    index 2a134cee44..b3f8a418c0 100644
    --- a/library.c
    +++ b/library.c
    @@ -2046,6 +2046,75 @@ redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
         }
     }
     
    +static int
    +redis_hello_response(INTERNAL_FUNCTION_PARAMETERS,
    +                     RedisSock *redis_sock, zval *z_tab, void *ctx)
    +{
    +    int numElems;
    +    zval z_ret, *zv;
    +
    +    if (read_mbulk_header(redis_sock, &numElems) < 0) {
    +        if (IS_ATOMIC(redis_sock)) {
    +            RETVAL_FALSE;
    +        } else {
    +            add_next_index_bool(z_tab, 0);
    +        }
    +        return FAILURE;
    +    }
    +
    +    array_init(&z_ret);
    +    redis_mbulk_reply_zipped_raw_variant(redis_sock, &z_ret, numElems);
    +
    +    if (redis_sock->hello.server) {
    +        zend_string_release(redis_sock->hello.server);
    +    }
    +    zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("server"));
    +    redis_sock->hello.server = zv ? zval_get_string(zv) : ZSTR_EMPTY_ALLOC();
    +
    +    if (redis_sock->hello.version) {
    +        zend_string_release(redis_sock->hello.version);
    +    }
    +    zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("version"));
    +    redis_sock->hello.version = zv ? zval_get_string(zv) : ZSTR_EMPTY_ALLOC();
    +
    +    if (ctx != NULL) {
    +        zval_dtor(&z_ret);
    +        if (ctx == PHPREDIS_CTX_PTR) {
    +            ZVAL_STR_COPY(&z_ret, redis_sock->hello.server);
    +        } else if (ctx == PHPREDIS_CTX_PTR + 1) {
    +            ZVAL_STR_COPY(&z_ret, redis_sock->hello.version);
    +        } else {
    +            ZEND_ASSERT(!"memory corruption?");
    +            return FAILURE;
    +        }
    +    }
    +
    +    if (IS_ATOMIC(redis_sock)) {
    +        RETVAL_ZVAL(&z_ret, 0, 1);
    +    } else {
    +        add_next_index_zval(z_tab, &z_ret);
    +    }
    +
    +    return SUCCESS;
    +}
    +
    +
    +PHP_REDIS_API int
    +redis_hello_server_response(INTERNAL_FUNCTION_PARAMETERS,
    +                            RedisSock *redis_sock, zval *z_tab, void *ctx)
    +{
    +    return redis_hello_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
    +                                z_tab, PHPREDIS_CTX_PTR);
    +}
    +
    +PHP_REDIS_API int
    +redis_hello_version_response(INTERNAL_FUNCTION_PARAMETERS,
    +                             RedisSock *redis_sock, zval *z_tab, void *ctx)
    +{
    +    return redis_hello_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
    +                                z_tab, PHPREDIS_CTX_PTR + 1);
    +}
    +
     static int
     redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
     {
    @@ -3578,6 +3647,19 @@ redis_free_reply_callbacks(RedisSock *redis_sock)
         }
     }
     
    +static void
    +redis_sock_release_hello(struct RedisHello *hello) {
    +    if (hello->server) {
    +        zend_string_release(hello->server);
    +        hello->server = NULL;
    +    }
    +
    +    if (hello->version) {
    +        zend_string_release(hello->version);
    +        hello->version = NULL;
    +    }
    +}
    +
     /**
      * redis_free_socket
      */
    @@ -3607,6 +3689,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
         }
         redis_sock_free_auth(redis_sock);
         redis_free_reply_callbacks(redis_sock);
    +    redis_sock_release_hello(&redis_sock->hello);
         efree(redis_sock);
     }
     
    diff --git a/library.h b/library.h
    index 270694112a..5f1806c594 100644
    --- a/library.h
    +++ b/library.h
    @@ -211,6 +211,9 @@ PHP_REDIS_API int redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSoc
     PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
     PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
     
    +PHP_REDIS_API int redis_hello_server_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
    +PHP_REDIS_API int redis_hello_version_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
    +
     /* Helper methods to get configuration values from a HashTable. */
     
     #define REDIS_HASH_STR_FIND_STATIC(ht, sstr) \
    diff --git a/redis.c b/redis.c
    index d049989771..46d126855c 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -2577,6 +2577,30 @@ PHP_METHOD(Redis, getPort) {
         }
     }
     
    +PHP_METHOD(Redis, serverName) {
    +    RedisSock *rs;
    +
    +    if ((rs = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) {
    +        RETURN_FALSE;
    +    } else if (rs->hello.server != NULL) {
    +        RETURN_STR_COPY(rs->hello.server);
    +    }
    +
    +    REDIS_PROCESS_KW_CMD("HELLO", redis_empty_cmd, redis_hello_server_response);
    +}
    +
    +PHP_METHOD(Redis, serverVersion) {
    +    RedisSock *rs;
    +
    +    if ((rs = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) {
    +        RETURN_FALSE;
    +    } else if (rs->hello.version != NULL) {
    +        RETURN_STR_COPY(rs->hello.version);
    +    }
    +
    +    REDIS_PROCESS_KW_CMD("HELLO", redis_empty_cmd, redis_hello_version_response);
    +}
    +
     /* {{{ proto Redis::getDBNum */
     PHP_METHOD(Redis, getDBNum) {
         RedisSock *redis_sock;
    diff --git a/redis.stub.php b/redis.stub.php
    index 5f2e7693ee..57dc47da1a 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1590,6 +1590,20 @@ public function getPersistentID(): ?string;
          */
         public function getPort(): int;
     
    +    /**
    +     * Get the server name as reported by the `HELLO` response.
    +     *
    +     * @return string|false
    +     */
    +    public function serverName(): Redis|string|false;
    +
    +    /**
    +     * Get the server version as reported by the `HELLO` response.
    +     *
    +     * @return string|false
    +     */
    +    public function serverVersion(): Redis|string|false;
    +
         /**
          * Retrieve a substring of a string by index.
          *
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index e880450eb2..33938e7056 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 6dd5a9e9d1d5ed8a78e248c99352232e30046f28 */
    + * Stub hash: 79376d7ada29d6f9bb873e7c59e64e22af3ca559 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -363,6 +363,11 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_getPort arginfo_class_Redis_getDBNum
     
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_serverName, 0, 0, Redis, MAY_BE_STRING|MAY_BE_FALSE)
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_Redis_serverVersion arginfo_class_Redis_serverName
    +
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getRange, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     	ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
    @@ -681,8 +686,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rPop, 0, 1, Redi
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
     ZEND_END_ARG_INFO()
     
    -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_randomKey, 0, 0, Redis, MAY_BE_STRING|MAY_BE_FALSE)
    -ZEND_END_ARG_INFO()
    +#define arginfo_class_Redis_randomKey arginfo_class_Redis_serverName
     
     ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rawcommand, 0, 1, IS_MIXED, 0)
     	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
    @@ -1263,6 +1267,8 @@ ZEND_METHOD(Redis, getMode);
     ZEND_METHOD(Redis, getOption);
     ZEND_METHOD(Redis, getPersistentID);
     ZEND_METHOD(Redis, getPort);
    +ZEND_METHOD(Redis, serverName);
    +ZEND_METHOD(Redis, serverVersion);
     ZEND_METHOD(Redis, getRange);
     ZEND_METHOD(Redis, lcs);
     ZEND_METHOD(Redis, getReadTimeout);
    @@ -1522,6 +1528,8 @@ static const zend_function_entry class_Redis_methods[] = {
     	ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, serverName, arginfo_class_Redis_serverName, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, serverVersion, arginfo_class_Redis_serverVersion, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 4fd45c7e37..9755c6d270 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 6dd5a9e9d1d5ed8a78e248c99352232e30046f28 */
    + * Stub hash: 79376d7ada29d6f9bb873e7c59e64e22af3ca559 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    @@ -333,6 +333,10 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_getPort arginfo_class_Redis___destruct
     
    +#define arginfo_class_Redis_serverName arginfo_class_Redis___destruct
    +
    +#define arginfo_class_Redis_serverVersion arginfo_class_Redis___destruct
    +
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
     	ZEND_ARG_INFO(0, key)
     	ZEND_ARG_INFO(0, start)
    @@ -1106,6 +1110,8 @@ ZEND_METHOD(Redis, getMode);
     ZEND_METHOD(Redis, getOption);
     ZEND_METHOD(Redis, getPersistentID);
     ZEND_METHOD(Redis, getPort);
    +ZEND_METHOD(Redis, serverName);
    +ZEND_METHOD(Redis, serverVersion);
     ZEND_METHOD(Redis, getRange);
     ZEND_METHOD(Redis, lcs);
     ZEND_METHOD(Redis, getReadTimeout);
    @@ -1365,6 +1371,8 @@ static const zend_function_entry class_Redis_methods[] = {
     	ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, serverName, arginfo_class_Redis_serverName, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, serverVersion, arginfo_class_Redis_serverVersion, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
    
    From cbaf095ff708caf2728541bd627399a4058d0f19 Mon Sep 17 00:00:00 2001
    From: Pavlo Yatsukhnenko 
    Date: Mon, 17 Mar 2025 21:10:58 +0200
    Subject: [PATCH 1968/1986] Allow calling methods only in atomic mode
    
    ---
     library.c              | 19 +++++++++----------
     redis.c                | 12 ++++++++++--
     redis.stub.php         |  4 ++--
     redis_arginfo.h        |  7 ++++---
     redis_legacy_arginfo.h |  2 +-
     5 files changed, 26 insertions(+), 18 deletions(-)
    
    diff --git a/library.c b/library.c
    index b3f8a418c0..fe47f3ff4f 100644
    --- a/library.c
    +++ b/library.c
    @@ -2077,16 +2077,15 @@ redis_hello_response(INTERNAL_FUNCTION_PARAMETERS,
         zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("version"));
         redis_sock->hello.version = zv ? zval_get_string(zv) : ZSTR_EMPTY_ALLOC();
     
    -    if (ctx != NULL) {
    -        zval_dtor(&z_ret);
    -        if (ctx == PHPREDIS_CTX_PTR) {
    -            ZVAL_STR_COPY(&z_ret, redis_sock->hello.server);
    -        } else if (ctx == PHPREDIS_CTX_PTR + 1) {
    -            ZVAL_STR_COPY(&z_ret, redis_sock->hello.version);
    -        } else {
    -            ZEND_ASSERT(!"memory corruption?");
    -            return FAILURE;
    -        }
    +    zval_dtor(&z_ret);
    +
    +    if (ctx == PHPREDIS_CTX_PTR) {
    +        ZVAL_STR_COPY(&z_ret, redis_sock->hello.server);
    +    } else if (ctx == PHPREDIS_CTX_PTR + 1) {
    +        ZVAL_STR_COPY(&z_ret, redis_sock->hello.version);
    +    } else {
    +        ZEND_ASSERT(!"memory corruption?");
    +        return FAILURE;
         }
     
         if (IS_ATOMIC(redis_sock)) {
    diff --git a/redis.c b/redis.c
    index 46d126855c..3075437d1a 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -2580,7 +2580,11 @@ PHP_METHOD(Redis, getPort) {
     PHP_METHOD(Redis, serverName) {
         RedisSock *rs;
     
    -    if ((rs = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) {
    +    if ((rs = redis_sock_get_instance(getThis(), 1)) == NULL) {
    +        RETURN_FALSE;
    +    } else if (!IS_ATOMIC(rs)) {
    +        php_error_docref(NULL, E_ERROR,
    +            "Can't call serverName in multi or pipeline mode!");
             RETURN_FALSE;
         } else if (rs->hello.server != NULL) {
             RETURN_STR_COPY(rs->hello.server);
    @@ -2592,7 +2596,11 @@ PHP_METHOD(Redis, serverName) {
     PHP_METHOD(Redis, serverVersion) {
         RedisSock *rs;
     
    -    if ((rs = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) {
    +    if ((rs = redis_sock_get_instance(getThis(), 1)) == NULL) {
    +        RETURN_FALSE;
    +    } else if (!IS_ATOMIC(rs)) {
    +        php_error_docref(NULL, E_ERROR,
    +            "Can't call serverVersion in multi or pipeline mode!");
             RETURN_FALSE;
         } else if (rs->hello.version != NULL) {
             RETURN_STR_COPY(rs->hello.version);
    diff --git a/redis.stub.php b/redis.stub.php
    index 57dc47da1a..8ace66a8c5 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1595,14 +1595,14 @@ public function getPort(): int;
          *
          * @return string|false
          */
    -    public function serverName(): Redis|string|false;
    +    public function serverName(): string|false;
     
         /**
          * Get the server version as reported by the `HELLO` response.
          *
          * @return string|false
          */
    -    public function serverVersion(): Redis|string|false;
    +    public function serverVersion(): string|false;
     
         /**
          * Retrieve a substring of a string by index.
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index 33938e7056..08a2308ffe 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 79376d7ada29d6f9bb873e7c59e64e22af3ca559 */
    + * Stub hash: 805a66c17b7c9972c73a979bdd67f98f7c1f6c74 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -363,7 +363,7 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_getPort arginfo_class_Redis_getDBNum
     
    -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_serverName, 0, 0, Redis, MAY_BE_STRING|MAY_BE_FALSE)
    +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_serverName, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)
     ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_serverVersion arginfo_class_Redis_serverName
    @@ -686,7 +686,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_rPop, 0, 1, Redi
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
     ZEND_END_ARG_INFO()
     
    -#define arginfo_class_Redis_randomKey arginfo_class_Redis_serverName
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_randomKey, 0, 0, Redis, MAY_BE_STRING|MAY_BE_FALSE)
    +ZEND_END_ARG_INFO()
     
     ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_rawcommand, 0, 1, IS_MIXED, 0)
     	ZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 9755c6d270..6bfc3a39ab 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 79376d7ada29d6f9bb873e7c59e64e22af3ca559 */
    + * Stub hash: 805a66c17b7c9972c73a979bdd67f98f7c1f6c74 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    
    From fa3eb00683a2c8d539b52c0738db6821c74fef54 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Thu, 20 Mar 2025 13:36:56 -0700
    Subject: [PATCH 1969/1986] Add tests for `serverName()` and `serverVersion()`
    
    ---
     tests/RedisClusterTest.php |  2 ++
     tests/RedisTest.php        | 49 ++++++++++++++++++++++++++++++++++++++
     2 files changed, 51 insertions(+)
    
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index 9e4150b037..0b1636f830 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -27,6 +27,8 @@ class Redis_Cluster_Test extends Redis_Test {
         private static array  $seed_messages = [];
         private static string $seed_source = '';
     
    +    public function testServerInfo() { $this->markTestSkipped(); }
    +    public function testServerInfoOldRedis() { $this->markTestSkipped(); }
     
         /* Tests we'll skip all together in the context of RedisCluster.  The
          * RedisCluster class doesn't implement specialized (non-redis) commands
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 69aaa5b190..cb69686671 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -2457,6 +2457,55 @@ public function testInfo() {
             $this->assertTrue(is_array($res) && isset($res['redis_version']) && isset($res['used_memory']));
         }
     
    +    private function execHello() {
    +        $zipped = [];
    +
    +        $result = $this->redis->rawCommand('HELLO');
    +        if ( ! is_array($result) || count($result) % 2 != 0)
    +            return false;
    +
    +        for ($i = 0; $i < count($result); $i += 2) {
    +            $zipped[$result[$i]] = $result[$i + 1];
    +        }
    +
    +        return $zipped;
    +    }
    +
    +    public function testServerInfo() {
    +        if ( ! $this->minVersionCheck('6.0.0'))
    +            $this->markTestSkipped();
    +
    +        $hello = $this->execHello();
    +        if ( ! $this->assertArrayKey($hello, 'server') ||
    +             ! $this->assertArrayKey($hello, 'version'))
    +        {
    +            return false;
    +        }
    +
    +        $this->assertEquals($hello['server'], $this->redis->serverName());
    +        $this->assertEquals($hello['version'], $this->redis->serverVersion());
    +
    +        $info = $this->redis->info();
    +        $cmd1 = $info['total_commands_processed'];
    +
    +        /* Shouldn't hit the server */
    +        $this->assertEquals($hello['server'], $this->redis->serverName());
    +        $this->assertEquals($hello['version'], $this->redis->serverVersion());
    +
    +        $info = $this->redis->info();
    +        $cmd2 = $info['total_commands_processed'];
    +
    +        $this->assertEquals(1 + $cmd1, $cmd2);
    +    }
    +
    +    public function testServerInfoOldRedis() {
    +        if ($this->minVersionCheck('6.0.0'))
    +            $this->markTestSkipped();
    +
    +        $this->assertFalse($this->redis->serverName());
    +        $this->assertFalse($this->redis->serverVersion());
    +    }
    +
         public function testInfoCommandStats() {
             // INFO COMMANDSTATS is new in 2.6.0
             if (version_compare($this->version, '2.5.0') < 0)
    
    From 300c5fb218ebb55fb6eca4de91756a91e57912ea Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Fri, 21 Mar 2025 11:05:20 -0700
    Subject: [PATCH 1970/1986] Make execHello protected
    
    This lets a subclass override it
    ---
     tests/RedisTest.php | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index cb69686671..d551f30375 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -2457,7 +2457,7 @@ public function testInfo() {
             $this->assertTrue(is_array($res) && isset($res['redis_version']) && isset($res['used_memory']));
         }
     
    -    private function execHello() {
    +    protected function execHello() {
             $zipped = [];
     
             $result = $this->redis->rawCommand('HELLO');
    
    From 52e2b8a788863c105843119f1ad3be5adf9bfff2 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Mon, 17 Mar 2025 12:13:42 -0700
    Subject: [PATCH 1971/1986] Prepare for 6.2.0 release
    
    ---
     CHANGELOG.md | 164 ++++++++++++++++++++++++++++++-
     package.xml  | 265 ++++++++++++++++++++++++++-------------------------
     php_redis.h  |   2 +-
     3 files changed, 299 insertions(+), 132 deletions(-)
    
    diff --git a/CHANGELOG.md b/CHANGELOG.md
    index 5d1cf0aa4f..fcfb5e7607 100644
    --- a/CHANGELOG.md
    +++ b/CHANGELOG.md
    @@ -5,14 +5,176 @@ All changes to phpredis will be documented in this file.
     We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
     and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
     
    -## [Unreleased]
    +# [6.2.0] - 2025-03-24 ([Github](https://github.com/phpredis/phpredis/releases/6.2.0), [PECL](https://pecl.php.net/package/redis/6.2.0))
    +
    +### Sponsors :sparkling_heart:
    +
    +- [A-VISION](https://github.com/A-VISION-BV)
    +- [Avtandil Kikabidze](https://github.com/akalongman)
    +- [Geoffrey Hoffman](https://github.com/phpguru)
    +- [Object Cache Pro for WordPress](https://objectcache.pro/)
    +- [Open LMS](https://openlms.net/)
    +- [Salvatore Sanfilippo](https://github.com/antirez)
    +- [Ty Karok](https://github.com/karock)
    +- [Vanessa Santana](https://github.com/vanessa-dev)
    +
    +  Special thanks to [Jakub Onderka](https://github.com/jakubonderka) for nearly two dozen performance improvements in this release!
    +
    +## Fixed
    +
    +- Fix arguments order for `SET` command
    +  [f73f5fc](https://github.com/phpredis/phpredis/commit/f73f5fcce55ab9268c4eb40bf93cccdae418c1d2)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Fix error length calculation and UB sanity check
    +  [e73130fe](https://github.com/phpredis/phpredis/commit/e73130fee0c22a20e11ce1596579df3f6f826974)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Invalidate slot cache on failed cluster connections
    +  [c7b87843](https://github.com/phpredis/phpredis/commit/c7b878431014789f35d2fb1834b95257ca6cbba5)
    +  ([James Kennedy](https://github.com/jkenn99))
    +- Don't cast a uint64_t to a long
    +  [faa4bc20](https://github.com/phpredis/phpredis/commit/faa4bc20868c76be4ecc4265015104a8adafccc4)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Fix potential NULL dereference
    +  [43e6cab8](https://github.com/phpredis/phpredis/commit/43e6cab8792dc01580894d85600add9b68c27a42)
    +  ([peter15914](https://github.com/peter15914))
    +- Print cursor as unsigned 64 bit integer
    +  [138d07b6](https://github.com/phpredis/phpredis/commit/138d07b67c5537373834f1cae99804e092db1631)
    +  ([Bentley O'Kane-Chase](https://github.com/bentleyo))
    +- Fix XAUTOCLAIM argc when sending COUNT
    +  [0fe45d24](https://github.com/phpredis/phpredis/commit/0fe45d24d4d8c115a5b52846be072ecb9bb43329)
    +  ([michael-grunder](https://github.com/michael-grunder))
     
     ### Added
     
    +- Added `serverName()` and `serverVersion()` introspection methods
    +  [056c2dbe](https://github.com/phpredis/phpredis/commit/056c2dbee7f6379a9f546e46584ace59449847c7)
    +  [cbaf095f](https://github.com/phpredis/phpredis/commit/cbaf095ff708caf2728541bd627399a4058d0f19)
    +  [fa3eb006](https://github.com/phpredis/phpredis/commit/fa3eb00683a2c8d539b52c0738db6821c74fef54)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +  ([michael-grunder](https://github.com/michael-grunder))
     - Added `getWithMeta` method
       [9036ffca](https://github.com/phpredis/phpredis/commit/9036ffca)
       ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Implement `GETDEL` command for RedisCluster
    +  [d342e4ac](https://github.com/phpredis/phpredis/commit/d342e4ac18723607b001deb593c8d45e40bbc4c8)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Introduce `Redis::OPT_PACK_IGNORE_NUMBERS` option
    +  [f9ce9429](https://github.com/phpredis/phpredis/commit/f9ce9429ef9f14a3de2c3fe1d68d02fb7440093d)
    +  [29e5cf0d](https://github.com/phpredis/phpredis/commit/29e5cf0d8c03069aa34c2a63322951fdf2c268c2)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Implement Valkey >= 8.1 `IFEQ` `SET` option
    +  [a2eef77f](https://github.com/phpredis/phpredis/commit/a2eef77f4419cda815052e75def3af81b0ccd80f)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Implement KeyDB's EXPIREMEMBER[AT] commands
    +  [4cd3f593](https://github.com/phpredis/phpredis/commit/4cd3f59356582a65aec1cceed44741bd5d161d9e)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Set priority to 60 (for PIE installations)
    +  [9e504ede](https://github.com/phpredis/phpredis/commit/9e504ede34749326a39f997db6cc5c4201f6a9bc)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
     
    +### Documentation
    +
    +- Fix phpdoc type of `$pattern`
    +  [5cad2076](https://github.com/phpredis/phpredis/commit/5cad20763710d44f8efb8e537f8f84a812935604)
    +  ([OHZEKI Naoki](https://github.com/zeek0x))
    +- Better documentation for the `$tlsOptions` parameter of RedisCluster
    +  [8144db37](https://github.com/phpredis/phpredis/commit/8144db374338006a316beb11549f37926bd40c5d)
    +  ([Jacob Brown](https://github.com/JacobBrownAustin))
    +
    +### Tests/CI
    +
    +- Reorganize tests
    +  [807f806f](https://github.com/phpredis/phpredis/commit/807f806f)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Add details to the option doc block
    +  [abb0f6cc](https://github.com/phpredis/phpredis/commit/abb0f6ccc827f240a1de53633225abbc2848fc3a)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Update CodeQL to v3
    +  [41e11417](https://github.com/phpredis/phpredis/commit/41e114177a20a03e3013db2a3b90980a1f4f1635)
    +  [a10bca35](https://github.com/phpredis/phpredis/commit/a10bca35bba32bb969cc1e473564695d3f8a8811)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Add PHP 8.4 to CI
    +  [6097e7ba](https://github.com/phpredis/phpredis/commit/6097e7ba50c0a300bc4f420f84c5d2665ef99d90)
    +  ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko))
    +- Pin ubuntu version for KeyDB
    +  [eb66fc9e](https://github.com/phpredis/phpredis/commit/eb66fc9e2fe60f13e5980ea2ecbe9457ca5ae8b4)
    +  [985b0313](https://github.com/phpredis/phpredis/commit/985b0313fb664c9776c3d2c84e778ddd6733728e)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Windows CI: update setup-php-sdk to v0.10 and enable caching
    +  [f89d4d8f](https://github.com/phpredis/phpredis/commit/f89d4d8f6eecbe223e158651ffffd77ffa27449b)
    +  ([Christoph M. Becker](https://github.com/cmb69))
    +
    +### Internal/Performance
    +
    +- Reduce buffer size for signed integer
    +  [044b3038](https://github.com/phpredis/phpredis/commit/044b30386f0418e9ed2a2bbc3b79582520d008d8)
    +  [35c59880](https://github.com/phpredis/phpredis/commit/35c5988027eda663167a64decde4512957cae738)
    +  ([Bentley O'Kane-Chase](https://github.com/bentleyo))
    +- Create a strncmp wrapper
    +  [085d61ec](https://github.com/phpredis/phpredis/commit/085d61ecfb0d484832547b46343a2e4b275a372e)
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Refactor and avoid allocation in rawcommand method
    +  [f68544f7](https://github.com/phpredis/phpredis/commit/f68544f70385e1d431fb0245fafe30b39ee7479a)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Switch from linked list to growing array for reply callbacks
    +  [a551fdc9](https://github.com/phpredis/phpredis/commit/a551fdc94c14d7974f2303cd558f7bd3e0fd91d6)
    +  [42a42769](https://github.com/phpredis/phpredis/commit/42a427695e89577a1f1a554dba268527f3995708)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +  ([michael-grunder](https://github.com/michael-grunder))
    +- Reuse redis_sock_append_auth method
    +  [be388562](https://github.com/phpredis/phpredis/commit/be388562058a75ed8fd31926bb0e6a60e2d8cb08)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Switch pipeline_cmd from smart_str to smart_string
    +  [571ffbc8](https://github.com/phpredis/phpredis/commit/571ffbc8e0a5da807a6cc4a2cc5aa90af72e23b0)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Remove unused redis_debug_response method from library.c
    +  [7895636a](https://github.com/phpredis/phpredis/commit/7895636a3a7cd3cad396a83ebe3aa5fe0208f42d)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Optimise HMGET method
    +  [2434ba29](https://github.com/phpredis/phpredis/commit/2434ba294cbb3b2f5b4ee581c37056906902d0d9)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Avoid unnecessary allocation in redis_hset_cmd
    +  [aba09933](https://github.com/phpredis/phpredis/commit/aba09933db05a1a36e947c6fa9dca9889c6a77ff)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Avoid unnecessary allocation in redis_hdel_cmd
    +  [4082dd07](https://github.com/phpredis/phpredis/commit/4082dd07f714fd2f6a0918b1845eb46c403a9edd)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Avoid unnecessary allocation in redis_key_varval_cmd
    +  [99650e15](https://github.com/phpredis/phpredis/commit/99650e15453f03b5dd99284548514551fde4c812)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Use zval_get_tmp_string method that is faster when provided zval is string
    +  [f6906470](https://github.com/phpredis/phpredis/commit/f6906470a52e2d24b1e1b9f2574726643edd7a64)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Optimise constructing Redis command string
    +  [2a2f908f](https://github.com/phpredis/phpredis/commit/2a2f908f2b6b695a0e6705200160e592802f0e41)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- If no command is issued in multi mode, return immutable empty array
    +  [5156e032](https://github.com/phpredis/phpredis/commit/5156e0320242ff05f327a3801667140069688c0e)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Test for empty pipeline and multi
    +  [426de2bb](https://github.com/phpredis/phpredis/commit/426de2bb71372f665f5a5bb5a779a7b9c586892d)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Optimise method array_zip_values_and_scores
    +  [400503b8](https://github.com/phpredis/phpredis/commit/400503b8718104b766ceb4a0b84e4a446dbee09b)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Faster parameter parsing in redis_key_cmd and redis_key_long_val_cmd
    +  [83a19656](https://github.com/phpredis/phpredis/commit/83a19656f49aec8f354596099dbf97ba7375d7af)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Use immutable empty array in Redis::hKeys
    +  [3a2f3f45](https://github.com/phpredis/phpredis/commit/3a2f3f45fc7bb01d1be2b9d97cf9d8bff0b0e818)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Use immutable empty array in Redis::exec
    +  [60b5a886](https://github.com/phpredis/phpredis/commit/60b5a8860ae3ff2d02d7f06cc6f86b59cb53b2cf)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Do not allocate empty string or string with one character
    +  [64da891e](https://github.com/phpredis/phpredis/commit/64da891e6fe5810b1aa2a47bc0632a2cd346659d)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Initialize arrays with known size
    +  [99beb922](https://github.com/phpredis/phpredis/commit/99beb9221c815018f1d076654b033cafac22a6ce)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
    +- Use smart str for constructing pipeline cmd
    +  [b665925e](https://github.com/phpredis/phpredis/commit/b665925eeddfdf6a6fc1de471c0789ffb60cd067)
    +  ([JakubOnderka](https://github.com/JakubOnderka))
     
     ## [6.1.0] - 2024-10-04 ([Github](https://github.com/phpredis/phpredis/releases/6.1.0), [PECL](https://pecl.php.net/package/redis/6.1.0))
     
    diff --git a/package.xml b/package.xml
    index 5dd79f8196..1aa75ec7b2 100644
    --- a/package.xml
    +++ b/package.xml
    @@ -22,10 +22,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
       p.yatsukhnenko@gmail.com
       yes
      
    - 2024-10-04
    + 2025-03-24
      
    -  6.1.0
    -  6.0.0
    +  6.2.0
    +  6.2.0
      
      
       stable
    @@ -33,147 +33,75 @@ http://pear.php.net/dtd/package-2.0.xsd">
      
      PHP
      
    -    Sponsors
    +    --- Sponsors ---
     
    +    A-VISION Advisering - https://a-vision.nu/
         Audiomack - https://audiomack.com
    -    Open LMS - https://openlms.net
         Avtandil Kikabidze - https://github.com/akalongman
    -    Ty Karok - https://github.com/karock
    +    Geoffrey Hoffman - https://github.com/phpguru
         Object Cache Pro for WordPress - https://objectcache.pro
    +    Open LMS - https://openlms.net
    +    Salvatore Sanfilippo - https://github.com/antirez
    +    Ty Karok - https://github.com/karock
    +    Vanessa Santana - https://github.com/vanessa-dev
     
    -    --- 6.1.0 ---
    -
    -    NOTE: There were no changes to C code between 6.1.0RC2 and 6.1.0
    -
    -    Documentation:
    -
    -    * Update package.xml to make it clearer that we support many key-value stores
    -      [52e69ede] (Remi Collet)
    -    * Fix redis.io urls [0bae4bb0] (Vincent Langlet)
    -
    -    Tests/CI:
    -
    -    * Fix 2 tests with redis 6.2 [cc1be322] (Remi Collet)
    -
    -    --- 6.1.0RC2 ---
    -
    -    Fixed:
    -
    -    * Fixed a `SIGABRT` error in PHP 8.4 [a75a7e5a] (Michael Grunder)
    -    * Clean up code for unsupported versions of PHP [37cebdd7] (Remi Collet)
    -    * Add `SessionHelpers.php` to `package.xml`[e9474b80] (Remi Collet)
    -    * 8.4 implicit null fix, bump version [bff3a22e, 30c8f90c] [Remi Collet]
    -
    -    Changed:
    -
    -    * Raised minimum supported PHP version to 7.4 [8b519423] (Michael Grunder)
    -
    -    Removed:
    -
    -    * Removed erroneously duplicated changelog entries [40c89736] (Michael Grunder)
    -
    -    Tests/CI:
    -
    -    * Move to upload artifacts v4 [9d380500] (Michael Grunder)
    -
    -    Added:
    -
    -    * Added `composer.json` to support PIE (PHP Installer for Extensions) [b59e35a6]
    -      (James Titcumb)
    +    * A special thanks to Jakub Onderka for nearly two dozen performance improvements in this release!
     
    -    --- 6.1.0RC1 ---
    +    --- 6.2.0 ---
     
         Fixed:
    -
    -    * Fix random connection timeouts with Redis Cluster. [eb7f31e7] (Jozsef Koszo)
    -    * Fix argument count issue in HSET with associative array [6ea5b3e0]
    -      (Viktor Djupsjobacka)
    -    * SRANDMEMBER can return any type because of serialization. [6673b5b2]
    -      (Michael Grunder)
    -    * Fix HRANDFIELD command when WITHVALUES is used. [99f9fd83] (Michael Grunder)
    -    * Allow context array to be nullable [50529f56] (Michael Grunder)
    -    * Fix a macOS (M1) compiler warning. [7de29d57] (Michael Grunder)
    -    * `GETEX` documentation/updates and implentation in `RedisCluster` [981c6931]
    -      (Michael Grunder)
    -    * Refactor redis_script_cmd and fix to `flush` subcommand. [7c551424]
    -      (Pavlo Yatsukhnenko)
    -    * Update liveness check and fix PHP 8.4 compilation error. [c139de3a]
    -      (Michael Grunder)
    -    * Rework how we declare ZSTD min/max constants. [34b5bd81] (Michael Grunder)
    -    * Fix memory leak if we fail in ps_open_redis. [0e926165] (Michael Grunder)
    -    * Fix segfault and remove redundant macros [a9e53fd1] (Pavlo Yatsukhnenko)
    -    * Fix PHP 8.4 includes [a51215ce] (Michael Grunder)
    -    * Handle arbitrarily large `SCAN` cursors properly. [2612d444, e52f0afa]
    -      (Michael Grunder)
    -    * Improve warning when we encounter an invalid EXPIRY in SET [732e466a]
    -      (Michael Grunder)
    -    * Fix Arginfo / zpp mismatch for DUMP command [50e5405c] (Pavlo Yatsukhnenko)
    -    * RedisCluster::publish returns a cluster_long_resp [14f93339] (Alexandre Choura)
    -    * Fix segfault when passing just false to auth. [6dc0a0be] (Michael Grunder)
    -    * the VALUE argument type for hSetNx must be the same as for hSet [df074dbe]
    -      (Uladzimir Tsykun)
    -    * Other fixes [e18f6c6d, 3d7be358, 2b555c89, fa1a283a, 37c5f8d4] (Michael Grunder, Viktor Szepe)
    +    * Fix arguments order for SET command [f73f5fc] (Pavlo Yatsukhnenko)
    +    * Fix error length calculation and UB sanity check [e73130fe] (michael-grunder)
    +    * Invalidate slot cache on failed cluster connections [c7b87843] (James Kennedy)
    +    * Don't cast a uint64_t to a long [faa4bc20] (michael-grunder)
    +    * Fix potential NULL dereference [43e6cab8] (peter15914)
    +    * Print cursor as unsigned 64 bit integer [138d07b6] (Bentley O'Kane-Chase)
    +    * Fix XAUTOCLAIM argc when sending COUNT [0fe45d24] (michael-grunder)
     
         Added:
    -
    -    * Compression support for PHP sessions. [da4ab0a7] (bitactive)
    -    * Support for early_refresh in Redis sessions to match cluster behavior
    -      [b6989018] (Bitactive)
    -    * Implement WAITAOF command. [ed7c9f6f] (Michael Grunder)
    -
    -    Removed:
    -
    -    * PHP 7.1, 7.2, and 7.3 CI jobs [d68c30f8, dc39bd55] (Michael Grunder)
    -
    -    Changed:
    -
    -    * Fix the time unit of retry_interval [3fdd52b4] (woodong)
    +    * Added `serverName()` and `serverVersion()` [fa3eb006, cbaf095f, 056c2dbe]
    +      (Pavlo Yatsukhnenko, Michael Grunder)
    +    * Added getWithMeta method [9036ffca, 36ab5850] (Pavlo Yatsukhnenko)
    +    * Implement GETDEL command for RedisCluster [d342e4ac] (michael-grunder)
    +    * Introduce Redis::OPT_PACK_IGNORE_NUMBERS option [f9ce9429, 29e5cf0d] (michael-grunder)
    +    * Implement Valkey >= 8.1 IFEQ SET option [a2eef77f] (michael-grunder)
    +    * Implement KeyDB's EXPIREMEMBER[AT] commands [4cd3f593] (michael-grunder)
     
         Documentation:
    -
    -    * Many documentation fixes. [eeb51099] (Michael Dwyer)
    -    * fix missing code tags [f865d5b9] (divinity76)
    -    * Mention Valkey support [5f1eecfb] (PlavorSeol)
    -    * Mention KeyDB support in README.md [37fa3592] (Tim Starling)
    -    * Remove mention of pickle [c7a73abb] (David Baker)
    -    * Add session.save_path examples [8a39caeb] (Martin Vancl)
    -    * Tighter return types for Redis::(keys|hKeys|hVals|hGetAll) [77ab62bc]
    -      (Benjamin Morel)
    -    * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a]
    -      (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko)
    -    * Fix config.m4 when using custom dep paths [ece3f7be] (Michael Grunder)
    -    * Fix retry_internal documentation [142c1f4a] (SplotyCode)
    -    * Fix anchor link [9b5cad31] (Git'Fellow)
    -    * Fix typo in link [bfd379f0] (deiga)
    -    * Fix Fedora package url [60b1ba14, 717713e1] (Dmitrii Kotov)
    -    * Update Redis Sentinel documentation to reflect changes to constructor in 6.0
    -      release [dc05d65c] (Pavlo Yatsukhnenko)
    +    * Fix phpdoc type of $pattern [5cad2076] (OHZEKI Naoki)
    +    * Better documentation for the $tlsOptions parameter of RedisCluster [8144db37] (Jacob Brown)
     
         Tests/CI:
    -
    -    * Avoid fatal error in test execution. [57304970] (Michael Grunder)
    -    * Refactor unit test framework. [b1771def] (Michael Grunder)
    -    * Get unit tests working in `php-cgi`. [b808cc60] (Michael Grunder)
    -    * Switch to `ZEND_STRL` in more places. [7050c989, f8c762e7] (Michael Grunder)
    -    * Workaround weird PHP compiler crash. [d3b2d87b] (Michael Grunder)
    -    * Refactor tests (formatting, modernization, etc). [dab6a62d, c6cd665b, 78b70ca8,
    -      3c125b09, 18b0da72, b88e72b1, 0f94d9c1, 59965971, 3dbc2bd8, 9b90c03b, c0d6f042]
    -      (Michael Grunder)
    -    * Spelling fixes [0d89e928] (Michael Grunder)
    -    * Added Valkey support. [f350dc34] (Michael Grunder)
    -    * Add a test for session compression. [9f3ca98c] (Michael Grunder)
    -    * Test against valkey [a819a44b] (Michael Grunder)
    -    * sessionSaveHandler injection. [9f8f80ca] (Pavlo Yatsukhnenko)
    -    * KeyDB addiions [54d62c72, d9c48b78] (Michael Grunder)
    -    * Add PHP 8.3 to CI [78d15140, e051a5db] (Robert Kelcak, Pavlo Yatsukhnenko)
    -    * Use newInstance in RedisClusterTest [954fbab8] (Pavlo Yatsukhnenko)
    -    * Use actions/checkout@v4 [f4c2ac26] (Pavlo Yatsukhnenko)
    -    * Cluster nodes from ENV [eda39958, 0672703b] (Pavlo Yatsukhnenko)
    -    * Ensure we're talking to redis-server in our high ports test. [7825efbc]
    -      (Michael Grunder)
    -    * Add missing option to installation example [2bddd84f] (Pavlo Yatsukhnenko)
    -    * Fix typo in link [8f6bc98f] (Timo Sand)
    -    * Update tests to allow users to use a custom class. [5f6ce414] (Michael Grunder)
    +    * Add details to the option doc block [abb0f6cc] (michael-grunder)
    +    * Update CodeQL to v3 [41e11417, a10bca35] (Pavlo Yatsukhnenko)
    +    * Add PHP 8.4 to CI [6097e7ba] (Pavlo Yatsukhnenko)
    +    * Pin ubuntu version for KeyDB [eb66fc9e, 985b0313] (michael-grunder)
    +    * Windows CI: update setup-php-sdk to v0.10 and enable caching [f89d4d8f] (Christoph M. Becker)
    +
    +    Internal:
    +    * Reduce buffer size for signed integer [044b3038, 35c59880] (Bentley O'Kane-Chase)
    +    * Create a strncmp wrapper [085d61ec] (michael-grunder)
    +    * Refactor and avoid allocation in rawcommand method [f68544f7] (Jakub Onderka)
    +    * Use defines for callback growth + sanity check [42a42769] (michael-grunder)
    +    * Switch from linked list to growing array for reply callbacks [a551fdc9] (Jakub Onderka)
    +    * Reuse redis_sock_append_auth method [be388562] (Jakub Onderka)
    +    * Switch pipeline_cmd from smart_str to smart_string [571ffbc8] (Jakub Onderka)
    +    * Remove unused redis_debug_response method from library.c [7895636a] (Jakub Onderka)
    +    * Optimise HMGET method [2434ba29] (Jakub Onderka)
    +    * Avoid unnecessary allocation in redis_hset_cmd [aba09933] (Jakub Onderka)
    +    * Avoid unnecessary allocation in redis_hdel_cmd [4082dd07] (Jakub Onderka)
    +    * Avoid unnecessary allocation in redis_key_varval_cmd [99650e15] (Jakub Onderka)
    +    * Use zval_get_tmp_string method that is faster when provided zval is string [f6906470] (Jakub Onderka)
    +    * Optimise constructing Redis command string [2a2f908f] (Jakub Onderka)
    +    * If no command is issued in multi mode, return immutable empty array [5156e032] (Jakub Onderka)
    +    * Test for empty pipeline and multi [426de2bb] (Jakub Onderka)
    +    * Optimise method array_zip_values_and_scores [400503b8] (Jakub Onderka)
    +    * Faster parameter parsing in redis_key_cmd and redis_key_long_val_cmd [83a19656] (Jakub Onderka)
    +    * Use immutable empty array in Redis::hKeys [3a2f3f45] (Jakub Onderka)
    +    * Use immutable empty array in Redis::exec [60b5a886] (Jakub Onderka)
    +    * Do not allocate empty string or string with one character [64da891e] (Jakub Onderka)
    +    * Initialize arrays with known size [99beb922] (Jakub Onderka)
    +    * Use smart str for constructing pipeline cmd [b665925e] (Jakub Onderka)
      
      
       
    @@ -266,6 +194,83 @@ http://pear.php.net/dtd/package-2.0.xsd">
       
      
      
    + 
    +   stablestable
    +   6.2.06.2.0
    +   2025-03-24
    +   
    +    --- Sponsors ---
    +
    +    A-VISION Advisering - https://a-vision.nu/
    +    Audiomack - https://audiomack.com
    +    Avtandil Kikabidze - https://github.com/akalongman
    +    Geoffrey Hoffman - https://github.com/phpguru
    +    Object Cache Pro for WordPress - https://objectcache.pro
    +    Open LMS - https://openlms.net
    +    Salvatore Sanfilippo - https://github.com/antirez
    +    Ty Karok - https://github.com/karock
    +    Vanessa Santana - https://github.com/vanessa-dev
    +
    +    * Special thanks to Jakub Onderka for nearly two dozen performance improvements in this release!
    +
    +    --- 6.2.0 ---
    +
    +    Fixed:
    +    * Fix arguments order for SET command [f73f5fc] (Pavlo Yatsukhnenko)
    +    * Fix error length calculation and UB sanity check [e73130fe] (michael-grunder)
    +    * Invalidate slot cache on failed cluster connections [c7b87843] (James Kennedy)
    +    * Don't cast a uint64_t to a long [faa4bc20] (michael-grunder)
    +    * Fix potential NULL dereference [43e6cab8] (peter15914)
    +    * Print cursor as unsigned 64 bit integer [138d07b6] (Bentley O'Kane-Chase)
    +    * Fix XAUTOCLAIM argc when sending COUNT [0fe45d24] (michael-grunder)
    +
    +    Added:
    +    * Added `serverName()` and `serverVersion()` [fa3eb006, cbaf095f, 056c2dbe]
    +      (Pavlo Yatsukhnenko, Michael Grunder)
    +    * Added getWithMeta method [9036ffca, 36ab5850] (Pavlo Yatsukhnenko)
    +    * Implement GETDEL command for RedisCluster [d342e4ac] (michael-grunder)
    +    * Introduce Redis::OPT_PACK_IGNORE_NUMBERS option [f9ce9429, 29e5cf0d] (michael-grunder)
    +    * Implement Valkey >= 8.1 IFEQ SET option [a2eef77f] (michael-grunder)
    +    * Implement KeyDB's EXPIREMEMBER[AT] commands [4cd3f593] (michael-grunder)
    +    * Set priority to 60 (for PIE installations) [9e504ede] (Pavlo Yatsukhnenko)
    +
    +    Documentation:
    +    * Fix phpdoc type of $pattern [5cad2076] (OHZEKI Naoki)
    +    * Better documentation for the $tlsOptions parameter of RedisCluster [8144db37] (Jacob Brown)
    +
    +    Tests/CI:
    +    * Add details to the option doc block [abb0f6cc] (michael-grunder)
    +    * Update CodeQL to v3 [41e11417, a10bca35] (Pavlo Yatsukhnenko)
    +    * Add PHP 8.4 to CI [6097e7ba] (Pavlo Yatsukhnenko)
    +    * Pin ubuntu version for KeyDB [eb66fc9e, 985b0313] (michael-grunder)
    +    * Windows CI: update setup-php-sdk to v0.10 and enable caching [f89d4d8f] (Christoph M. Becker)
    +
    +    Internal/Performance:
    +    * Reduce buffer size for signed integer [044b3038, 35c59880] (Bentley O'Kane-Chase)
    +    * Create a strncmp wrapper [085d61ec] (michael-grunder)
    +    * Refactor and avoid allocation in rawcommand method [f68544f7] (Jakub Onderka)
    +    * Use defines for callback growth + sanity check [42a42769] (michael-grunder)
    +    * Switch from linked list to growing array for reply callbacks [a551fdc9] (Jakub Onderka)
    +    * Reuse redis_sock_append_auth method [be388562] (Jakub Onderka)
    +    * Switch pipeline_cmd from smart_str to smart_string [571ffbc8] (Jakub Onderka)
    +    * Remove unused redis_debug_response method from library.c [7895636a] (Jakub Onderka)
    +    * Optimise HMGET method [2434ba29] (Jakub Onderka)
    +    * Avoid unnecessary allocation in redis_hset_cmd [aba09933] (Jakub Onderka)
    +    * Avoid unnecessary allocation in redis_hdel_cmd [4082dd07] (Jakub Onderka)
    +    * Avoid unnecessary allocation in redis_key_varval_cmd [99650e15] (Jakub Onderka)
    +    * Use zval_get_tmp_string method that is faster when provided zval is string [f6906470] (Jakub Onderka)
    +    * Optimise constructing Redis command string [2a2f908f] (Jakub Onderka)
    +    * If no command is issued in multi mode, return immutable empty array [5156e032] (Jakub Onderka)
    +    * Test for empty pipeline and multi [426de2bb] (Jakub Onderka)
    +    * Optimise method array_zip_values_and_scores [400503b8] (Jakub Onderka)
    +    * Faster parameter parsing in redis_key_cmd and redis_key_long_val_cmd [83a19656] (Jakub Onderka)
    +    * Use immutable empty array in Redis::hKeys [3a2f3f45] (Jakub Onderka)
    +    * Use immutable empty array in Redis::exec [60b5a886] (Jakub Onderka)
    +    * Do not allocate empty string or string with one character [64da891e] (Jakub Onderka)
    +    * Initialize arrays with known size [99beb922] (Jakub Onderka)
    +    * Use smart str for constructing pipeline cmd [b665925e] (Jakub Onderka)
    +   
    + 
      
        stablestable
        6.1.06.0.0
    diff --git a/php_redis.h b/php_redis.h
    index 77f31498c6..8f535cb6fe 100644
    --- a/php_redis.h
    +++ b/php_redis.h
    @@ -23,7 +23,7 @@
     #define PHP_REDIS_H
     
     /* phpredis version */
    -#define PHP_REDIS_VERSION "6.1.0"
    +#define PHP_REDIS_VERSION "6.2.0"
     
     /* For convenience we store the salt as a printable hex string which requires 2
      * characters per byte + 1 for the NULL terminator */
    
    From 3828c9293b8fcd473e3c0b6d72c8db740b32bed8 Mon Sep 17 00:00:00 2001
    From: Remi Collet 
    Date: Wed, 26 Mar 2025 23:25:22 +0100
    Subject: [PATCH 1972/1986] cleanup session temp file (#2641)
    
    * cleanup session temp file
    
    * Fix Deprecated: Automatic conversion of false to array
    ---
     tests/RedisTest.php      | 1 +
     tests/SessionHelpers.php | 9 +++++++--
     2 files changed, 8 insertions(+), 2 deletions(-)
    
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index d551f30375..d7f8b48059 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -6185,6 +6185,7 @@ public function testScan() {
                 // Use a unique ID so we can find our type keys
                 $id = uniqid();
     
    +            $keys = [];
                 // Create some simple keys and lists
                 for ($i = 0; $i < 3; $i++) {
                     $simple = "simple:{$id}:$i";
    diff --git a/tests/SessionHelpers.php b/tests/SessionHelpers.php
    index 90ae73beb6..c2fa12056f 100644
    --- a/tests/SessionHelpers.php
    +++ b/tests/SessionHelpers.php
    @@ -80,7 +80,7 @@ class Runner {
         ];
     
         private $prefix = NULL;
    -    private $output_file;
    +    private $output_file = NULL;
         private $exit_code = -1;
         private $cmd = NULL;
         private $pid;
    @@ -90,6 +90,12 @@ public function __construct() {
             $this->args['id'] = $this->createId();
         }
     
    +    public function __destruct() {
    +        if ($this->output_file) {
    +            unlink($this->output_file);
    +        }
    +    }
    +
         public function getExitCode(): int {
             return $this->exit_code;
         }
    @@ -183,7 +189,6 @@ private function createId(): string {
         }
     
         private function getTmpFileName() {
    -        return '/tmp/sessiontmp.txt';
             return tempnam(sys_get_temp_dir(), 'session');
         }
     
    
    From 4f6a3ed1e71c70f80b631a9f53749e6a9fdb457a Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Thu, 27 Mar 2025 02:05:33 +0100
    Subject: [PATCH 1973/1986] New option 'database' for Redis class constructor
     (#2597)
    
    * New option 'database' for Redis class constructor
    
    Selecting database is very common action after connecting to Redis. This simplifies lazy connecting to Redis, when requested database will be selected after first command.
    
    * More specific exception message when invalid auth or database number is provided
    
    Before it was just 'Redis server went away'
    
    * Rename reselect_db method to redis_select_db and slightly optimise it
    ---
     README.md           |  6 +++--
     library.c           | 55 ++++++++++++++++++++++++++++-----------------
     redis.c             | 36 +++++++++++++++++++++--------
     tests/RedisTest.php | 21 +++++++++++++++++
     4 files changed, 86 insertions(+), 32 deletions(-)
    
    diff --git a/README.md b/README.md
    index 9f3389d893..68e70a2dee 100644
    --- a/README.md
    +++ b/README.md
    @@ -187,6 +187,7 @@ $redis = new Redis([
         'port' => 6379,
         'connectTimeout' => 2.5,
         'auth' => ['phpredis', 'phpredis'],
    +    'database' => 2,
         'ssl' => ['verify_peer' => false],
         'backoff' => [
             'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER,
    @@ -203,8 +204,9 @@ $redis = new Redis([
     *connectTimeout*: float, value in seconds (default is 0 meaning unlimited)  
     *retryInterval*: int, value in milliseconds (optional)  
     *readTimeout*: float, value in seconds (default is 0 meaning unlimited)  
    -*persistent*: mixed, if value is string then it used as persistend id, else value casts to boolean  
    -*auth*: mixed, authentication information  
    +*persistent*: mixed, if value is string then it used as persistent id, else value casts to boolean  
    +*auth*: mixed, authentication information
    +*database*: int, database number
     *ssl*: array, SSL context options  
     
     ### Class RedisException
    diff --git a/library.c b/library.c
    index fe47f3ff4f..d51a0bd34d 100644
    --- a/library.c
    +++ b/library.c
    @@ -139,31 +139,40 @@ redis_sock_get_connection_pool(RedisSock *redis_sock)
         return pool;
     }
     
    -/* Helper to reselect the proper DB number when we reconnect */
    -static int reselect_db(RedisSock *redis_sock) {
    -    char *cmd, *response;
    -    int cmd_len, response_len;
    -
    -    cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "SELECT", "d",
    -                             redis_sock->dbNumber);
    -
    -    if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) {
    -        efree(cmd);
    -        return -1;
    +static int redis_sock_response_ok(RedisSock *redis_sock, char *buf, int buf_size) {
    +    size_t len;
    +    if (UNEXPECTED(redis_sock_gets(redis_sock, buf, buf_size - 1, &len) < 0)) {
    +        return 0;
         }
    +    if (UNEXPECTED(redis_strncmp(buf, ZEND_STRL("+OK")))) {
    +        if (buf[0] == '-') {
    +            // Set error message in case of error
    +            redis_sock_set_err(redis_sock, buf + 1, len);
    +        }
    +        return 0;
    +    }
    +    return 1;
    +}
     
    -    efree(cmd);
    +/* Helper to select the proper DB number */
    +static int redis_select_db(RedisSock *redis_sock) {
    +    char response[4096];
    +    smart_string cmd = {0};
     
    -    if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
    +    REDIS_CMD_INIT_SSTR_STATIC(&cmd, 1, "SELECT");
    +    redis_cmd_append_sstr_long(&cmd, redis_sock->dbNumber);
    +
    +    if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) {
    +        efree(cmd.c);
             return -1;
         }
     
    -    if (redis_strncmp(response, ZEND_STRL("+OK"))) {
    -        efree(response);
    +    efree(cmd.c);
    +
    +    if (!redis_sock_response_ok(redis_sock, response, sizeof(response))) {
             return -1;
         }
     
    -    efree(response);
         return 0;
     }
     
    @@ -252,9 +261,7 @@ PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) {
         }
         efree(cmd);
     
    -    if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 ||
    -        redis_strncmp(inbuf, ZEND_STRL("+OK")))
    -    {
    +    if (!redis_sock_response_ok(redis_sock, inbuf, sizeof(inbuf))) {
             return FAILURE;
         }
         return SUCCESS;
    @@ -384,7 +391,7 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw)
                         redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED;
     
                         /* If we're using a non-zero db, reselect it */
    -                    if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) {
    +                    if (redis_sock->dbNumber && redis_select_db(redis_sock) != 0) {
                             errmsg = "SELECT failed while reconnecting";
                             break;
                         }
    @@ -2857,6 +2864,12 @@ redis_sock_configure(RedisSock *redis_sock, HashTable *opts)
                     return FAILURE;
                 }
                 redis_sock_set_auth_zval(redis_sock, val);
    +        } else if (zend_string_equals_literal_ci(zkey, "database")) {
    +            if (Z_TYPE_P(val) != IS_LONG || Z_LVAL_P(val) < 0 || Z_LVAL_P(val) > INT_MAX) {
    +                REDIS_VALUE_EXCEPTION("Invalid database number");
    +                return FAILURE;
    +            }
    +            redis_sock->dbNumber = Z_LVAL_P(val);
             } else if (zend_string_equals_literal_ci(zkey, "backoff")) {
                 if (redis_sock_set_backoff(redis_sock, val) != SUCCESS) {
                     REDIS_VALUE_EXCEPTION("Invalid backoff options");
    @@ -3229,7 +3242,7 @@ redis_sock_server_open(RedisSock *redis_sock)
                 redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED;
                 // fall through
             case REDIS_SOCK_STATUS_AUTHENTICATED:
    -            if (redis_sock->dbNumber && reselect_db(redis_sock) != SUCCESS) {
    +            if (redis_sock->dbNumber && redis_select_db(redis_sock) != SUCCESS) {
                     break;
                 }
                 redis_sock->status = REDIS_SOCK_STATUS_READY;
    diff --git a/redis.c b/redis.c
    index 3075437d1a..a1866476cd 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -239,6 +239,31 @@ redis_sock_get_instance(zval *id, int no_throw)
         return NULL;
     }
     
    +static zend_never_inline ZEND_COLD void redis_sock_throw_exception(RedisSock *redis_sock) {
    +    char *errmsg = NULL;
    +    if (redis_sock->status == REDIS_SOCK_STATUS_AUTHENTICATED) {
    +        if (redis_sock->err != NULL) {
    +            spprintf(&errmsg, 0, "Could not select database %ld '%s'", redis_sock->dbNumber, ZSTR_VAL(redis_sock->err));
    +        } else {
    +            spprintf(&errmsg, 0, "Could not select database %ld", redis_sock->dbNumber);
    +        }
    +    } else if (redis_sock->status == REDIS_SOCK_STATUS_CONNECTED) {
    +        if (redis_sock->err != NULL) {
    +            spprintf(&errmsg, 0, "Could not authenticate '%s'", ZSTR_VAL(redis_sock->err));
    +        } else {
    +            spprintf(&errmsg, 0, "Could not authenticate");
    +        }
    +    } else {
    +        if (redis_sock->port < 0) {
    +            spprintf(&errmsg, 0, "Redis server %s went away", ZSTR_VAL(redis_sock->host));
    +        } else {
    +            spprintf(&errmsg, 0, "Redis server %s:%d went away", ZSTR_VAL(redis_sock->host), redis_sock->port);
    +        }
    +    }
    +    REDIS_THROW_EXCEPTION(errmsg, 0);
    +    efree(errmsg);
    +}
    +
     /**
      * redis_sock_get
      */
    @@ -251,16 +276,9 @@ redis_sock_get(zval *id, int no_throw)
             return NULL;
         }
     
    -    if (redis_sock_server_open(redis_sock) < 0) {
    +    if (UNEXPECTED(redis_sock_server_open(redis_sock) < 0)) {
             if (!no_throw) {
    -            char *errmsg = NULL;
    -            if (redis_sock->port < 0) {
    -                spprintf(&errmsg, 0, "Redis server %s went away", ZSTR_VAL(redis_sock->host));
    -            } else {
    -                spprintf(&errmsg, 0, "Redis server %s:%d went away", ZSTR_VAL(redis_sock->host), redis_sock->port);
    -            }
    -            REDIS_THROW_EXCEPTION(errmsg, 0);
    -            efree(errmsg);
    +            redis_sock_throw_exception(redis_sock);
             }
             return NULL;
         }
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index d7f8b48059..5438f9545a 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -7836,6 +7836,27 @@ public function testMultipleConnect() {
             }
         }
     
    +    public function testConnectDatabaseSelect() {
    +        $options = [
    +            'host' => $this->getHost(),
    +            'port' => $this->getPort(),
    +            'database' => 2,
    +        ];
    +
    +        if ($this->getAuth()) {
    +            $options['auth'] = $this->getAuth();
    +        }
    +
    +        $redis = new Redis($options);
    +        $this->assertEquals(2, $redis->getDBNum());
    +        $this->assertEquals(2, $redis->client('info')['db']);
    +
    +        $this->assertTrue($redis->select(1));
    +
    +        $this->assertEquals(1, $redis->getDBNum());
    +        $this->assertEquals(1, $redis->client('info')['db']);
    +    }
    +
         public function testConnectException() {
             $host = 'github.com';
             if (gethostbyname($host) === $host)
    
    From 0445e683e7552d60dbc82f21e0ee845911844651 Mon Sep 17 00:00:00 2001
    From: Michael Grunder 
    Date: Mon, 31 Mar 2025 12:42:29 -0700
    Subject: [PATCH 1974/1986] Refactor `getWithMeta` logic (#2643)
    
    * Refactor `getWithMeta`
    
    * Consolidate `getWithMeta()` test.
    
    * Review comments
    ---
     cluster_library.c          | 61 +++++++++++++++++++++++------------
     cluster_library.h          |  2 ++
     common.h                   |  1 -
     library.c                  | 65 ++++++++++++++++++++++++++------------
     library.h                  |  1 +
     redis.c                    | 17 ++--------
     redis_cluster.c            | 13 ++------
     tests/RedisClusterTest.php | 47 ---------------------------
     tests/RedisTest.php        | 30 ++++++++++--------
     9 files changed, 107 insertions(+), 130 deletions(-)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index 45a60e2384..97e7ddf559 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -1672,37 +1672,56 @@ cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ct
         }
     }
     
    +static int cluster_bulk_resp_to_zval(redisCluster *c, zval *zdst) {
    +    char *resp;
    +
    +    if (c->reply_type != TYPE_BULK ||
    +        (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL)
    +    {
    +        if (c->reply_type != TYPE_BULK)
    +            c->reply_len = 0;
    +        ZVAL_FALSE(zdst);
    +        return FAILURE;
    +    }
    +
    +    if (!redis_unpack(c->flags, resp, c->reply_len, zdst)) {
    +        ZVAL_STRINGL_FAST(zdst, resp, c->reply_len);
    +    }
    +
    +    efree(resp);
    +
    +    return SUCCESS;
    +}
    +
     /* BULK response handler */
     PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
    -                              void *ctx)
    +                                     void *ctx)
     {
    -    char *resp;
    -    zval z_unpacked, z_ret, *zv;
    +    zval zret;
     
    -    // Make sure we can read the response
    -    if (c->reply_type != TYPE_BULK) {
    -        ZVAL_FALSE(&z_unpacked);
    -        c->reply_len = 0;
    -    } else if ((resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL) {
    -        ZVAL_FALSE(&z_unpacked);
    -    } else {
    -        if (!redis_unpack(c->flags, resp, c->reply_len, &z_unpacked)) {
    -            ZVAL_STRINGL_FAST(&z_unpacked, resp, c->reply_len);
    -        }
    -        efree(resp);
    -    }
    +    cluster_bulk_resp_to_zval(c, &zret);
     
    -    if (c->flags->flags & PHPREDIS_WITH_METADATA) {
    -        redis_with_metadata(&z_ret, &z_unpacked, c->reply_len);
    -        zv = &z_ret;
    +    if (CLUSTER_IS_ATOMIC(c)) {
    +        RETVAL_ZVAL(&zret, 0, 1);
         } else {
    -        zv = &z_unpacked;
    +        add_next_index_zval(&c->multi_resp, &zret);
         }
    +}
    +
    +PHP_REDIS_API void
    +cluster_bulk_withmeta_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
    +                           void *ctx)
    +{
    +    zval zbulk, zmeta;
    +
    +    cluster_bulk_resp_to_zval(c, &zbulk);
    +
    +    redis_with_metadata(&zmeta, &zbulk, c->reply_len);
     
         if (CLUSTER_IS_ATOMIC(c)) {
    -        RETVAL_ZVAL(zv, 0, 1);
    +        RETVAL_ZVAL(&zmeta, 0, 1);
         } else {
    -        add_next_index_zval(&c->multi_resp, zv);
    +        add_next_index_zval(&c->multi_resp, &zmeta);
         }
     }
     
    diff --git a/cluster_library.h b/cluster_library.h
    index 3adfaf00a0..aa5152cb67 100644
    --- a/cluster_library.h
    +++ b/cluster_library.h
    @@ -432,6 +432,8 @@ PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisC
         void *ctx);
     PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
         void *ctx);
    +PHP_REDIS_API void cluster_bulk_withmeta_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
    +    void *ctx);
     PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
         void *ctx);
     PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
    diff --git a/common.h b/common.h
    index d87da945be..10194a4769 100644
    --- a/common.h
    +++ b/common.h
    @@ -152,7 +152,6 @@ typedef enum {
     #define PIPELINE 2
     
     #define PHPREDIS_DEBUG_LOGGING 0
    -#define PHPREDIS_WITH_METADATA 1
     
     #if PHP_VERSION_ID < 80000
     #define Z_PARAM_ARRAY_HT_OR_NULL(dest) \
    diff --git a/library.c b/library.c
    index d51a0bd34d..d6f357c113 100644
    --- a/library.c
    +++ b/library.c
    @@ -250,7 +250,6 @@ redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen) {
     PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) {
         char *cmd, inbuf[4096];
         int cmdlen;
    -    size_t len;
     
         if ((cmd = redis_sock_auth_cmd(redis_sock, &cmdlen)) == NULL)
             return SUCCESS;
    @@ -2740,35 +2739,59 @@ redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ta
         return ret ? SUCCESS : FAILURE;
     }
     
    -PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
    +static int redis_bulk_resp_to_zval(RedisSock *redis_sock, zval *zdst, int *dstlen) {
    +    char *resp;
    +    int len;
     
    -    char *response;
    -    int response_len;
    -    zval z_unpacked, z_ret, *zv;
    -    zend_bool ret;
    +    resp = redis_sock_read(redis_sock, &len);
    +    if (dstlen) *dstlen = len;
     
    -    if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
    -        ZVAL_FALSE(&z_unpacked);
    -        ret = FAILURE;
    -    } else {
    -        if (!redis_unpack(redis_sock, response, response_len, &z_unpacked)) {
    -            ZVAL_STRINGL_FAST(&z_unpacked, response, response_len);
    -        }
    -        efree(response);
    -        ret = SUCCESS;
    +    if (resp == NULL) {
    +        ZVAL_FALSE(zdst);
    +        return FAILURE;
    +    }
    +
    +    if (!redis_unpack(redis_sock, resp, len, zdst)) {
    +        ZVAL_STRINGL_FAST(zdst, resp, len);
         }
     
    -    if (redis_sock->flags & PHPREDIS_WITH_METADATA) {
    -        redis_with_metadata(&z_ret, &z_unpacked, response_len);
    -        zv = &z_ret;
    +    efree(resp);
    +    return SUCCESS;
    +}
    +
    +PHP_REDIS_API int
    +redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +                      zval *z_tab, void *ctx)
    +{
    +    zval zret;
    +    int ret;
    +
    +    ret = redis_bulk_resp_to_zval(redis_sock, &zret, NULL);
    +
    +    if (IS_ATOMIC(redis_sock)) {
    +        RETVAL_ZVAL(&zret, 0, 1);
         } else {
    -        zv = &z_unpacked;
    +        add_next_index_zval(z_tab, &zret);
         }
     
    +    return ret;
    +}
    +
    +PHP_REDIS_API int
    +redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +                             zval *z_tab, void *ctx)
    +{
    +    zval zret, zbulk;
    +    int len, ret;
    +
    +    ret = redis_bulk_resp_to_zval(redis_sock, &zbulk, &len);
    +
    +    redis_with_metadata(&zret, &zbulk, len);
    +
         if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(zv, 0, 1);
    +        RETVAL_ZVAL(&zret, 0, 1);
         } else {
    -        add_next_index_zval(z_tab, zv);
    +        add_next_index_zval(z_tab, &zret);
         }
     
         return ret;
    diff --git a/library.h b/library.h
    index 5f1806c594..feb310442c 100644
    --- a/library.h
    +++ b/library.h
    @@ -73,6 +73,7 @@ PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, Redi
     PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
     PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
     PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
    +PHP_REDIS_API int redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
     PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
     PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
     PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
    diff --git a/redis.c b/redis.c
    index a1866476cd..3f13a59888 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -778,17 +778,11 @@ PHP_METHOD(Redis, reset)
     }
     /* }}} */
     
    -static void
    -redis_get_passthru(INTERNAL_FUNCTION_PARAMETERS)
    -{
    -    REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response);
    -}
    -
     /* {{{ proto string Redis::get(string key)
      */
     PHP_METHOD(Redis, get)
     {
    -    redis_get_passthru(INTERNAL_FUNCTION_PARAM_PASSTHRU);
    +    REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response);
     }
     /* }}} */
     
    @@ -796,14 +790,7 @@ PHP_METHOD(Redis, get)
      */
     PHP_METHOD(Redis, getWithMeta)
     {
    -    RedisSock *redis_sock;
    -    if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
    -        RETURN_FALSE;
    -    }
    -
    -    REDIS_ENABLE_FLAG(redis_sock, PHPREDIS_WITH_METADATA);
    -    redis_get_passthru(INTERNAL_FUNCTION_PARAM_PASSTHRU);
    -    REDIS_DISABLE_FLAG(redis_sock, PHPREDIS_WITH_METADATA);
    +    REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_bulk_withmeta_response);
     }
     /* }}} */
     
    diff --git a/redis_cluster.c b/redis_cluster.c
    index a412130df9..1cbd825925 100644
    --- a/redis_cluster.c
    +++ b/redis_cluster.c
    @@ -275,15 +275,9 @@ PHP_METHOD(RedisCluster, close) {
         RETURN_TRUE;
     }
     
    -static void
    -cluster_get_passthru(INTERNAL_FUNCTION_PARAMETERS)
    -{
    -    CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp, 1);
    -}
    -
     /* {{{ proto string RedisCluster::get(string key) */
     PHP_METHOD(RedisCluster, get) {
    -    cluster_get_passthru(INTERNAL_FUNCTION_PARAM_PASSTHRU);
    +    CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp, 1);
     }
     /* }}} */
     
    @@ -295,10 +289,7 @@ PHP_METHOD(RedisCluster, getdel) {
     
     /* {{{ proto array|false RedisCluster::getWithMeta(string key) */
     PHP_METHOD(RedisCluster, getWithMeta) {
    -    redisCluster *c = GET_CONTEXT();
    -    REDIS_ENABLE_FLAG(c->flags, PHPREDIS_WITH_METADATA);
    -    cluster_get_passthru(INTERNAL_FUNCTION_PARAM_PASSTHRU);
    -    REDIS_DISABLE_FLAG(c->flags, PHPREDIS_WITH_METADATA);
    +    CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_withmeta_resp, 1);
     }
     /* }}} */
     
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index 0b1636f830..1fe68942bd 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -250,53 +250,6 @@ public function testClient() {
             $this->assertTrue($this->redis->client($key, 'kill', $addr));
         }
     
    -    public function testGetWithMeta() {
    -        $this->redis->del('key');
    -        $this->assertFalse($this->redis->get('key'));
    -
    -        $result = $this->redis->getWithMeta('key');
    -        $this->assertIsArray($result, 2);
    -        $this->assertArrayKeyEquals($result, 0, false);
    -        $this->assertArrayKey($result, 1, function ($metadata) {
    -            $this->assertIsArray($metadata);
    -            $this->assertArrayKeyEquals($metadata, 'length', -1);
    -            return true;
    -        });
    -
    -        $batch = $this->redis->multi()
    -            ->set('key', 'value')
    -            ->getWithMeta('key')
    -            ->exec();
    -        $this->assertIsArray($batch, 2);
    -        $this->assertArrayKeyEquals($batch, 0, true);
    -        $this->assertArrayKey($batch, 1, function ($result) {
    -            $this->assertIsArray($result, 2);
    -            $this->assertArrayKeyEquals($result, 0, 'value');
    -            $this->assertArrayKey($result, 1, function ($metadata) {
    -                $this->assertIsArray($metadata);
    -                $this->assertArrayKeyEquals($metadata, 'length', strlen('value'));
    -                return true;
    -            });
    -            return true;
    -        });
    -
    -        $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER);
    -        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
    -        $this->assertTrue($this->redis->set('key', false));
    -
    -        $result = $this->redis->getWithMeta('key');
    -        $this->assertIsArray($result, 2);
    -        $this->assertArrayKeyEquals($result, 0, false);
    -        $this->assertArrayKey($result, 1, function ($metadata) {
    -            $this->assertIsArray($metadata);
    -            $this->assertArrayKeyEquals($metadata, 'length', strlen(serialize(false)));
    -            return true;
    -        });
    -
    -        $this->assertFalse($this->redis->get('key'));
    -        $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
    -    }
    -
         public function testTime() {
             [$sec, $usec] = $this->redis->time(uniqid());
             $this->assertEquals(strval(intval($sec)), strval($sec));
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 5438f9545a..e7854da442 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -5869,22 +5869,24 @@ public function testGetWithMeta() {
                 return true;
             });
     
    -        $batch = $this->redis->pipeline()
    -            ->get('key')
    -            ->getWithMeta('key')
    -            ->exec();
    -        $this->assertIsArray($batch, 2);
    -        $this->assertArrayKeyEquals($batch, 0, false);
    -        $this->assertArrayKey($batch, 1, function ($result) {
    -            $this->assertIsArray($result, 2);
    -            $this->assertArrayKeyEquals($result, 0, false);
    -            $this->assertArrayKey($result, 1, function ($metadata) {
    -                $this->assertIsArray($metadata);
    -                $this->assertArrayKeyEquals($metadata, 'length', -1);
    +        if ($this->havePipeline()) {
    +            $batch = $this->redis->pipeline()
    +                ->get('key')
    +                ->getWithMeta('key')
    +                ->exec();
    +            $this->assertIsArray($batch, 2);
    +            $this->assertArrayKeyEquals($batch, 0, false);
    +            $this->assertArrayKey($batch, 1, function ($result) {
    +                $this->assertIsArray($result, 2);
    +                $this->assertArrayKeyEquals($result, 0, false);
    +                $this->assertArrayKey($result, 1, function ($metadata) {
    +                    $this->assertIsArray($metadata);
    +                    $this->assertArrayKeyEquals($metadata, 'length', -1);
    +                    return true;
    +                });
                     return true;
                 });
    -            return true;
    -        });
    +        }
     
             $batch = $this->redis->multi()
                 ->set('key', 'value')
    
    From 60ca48f3ce80acd697863408e3633b298fa224c5 Mon Sep 17 00:00:00 2001
    From: Michael Grunder 
    Date: Tue, 1 Apr 2025 11:33:44 -0700
    Subject: [PATCH 1975/1986] Redis Cluster does not have `SELECT`. (#2644)
    
    ---
     tests/RedisClusterTest.php | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index 1fe68942bd..04d4286298 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -48,6 +48,7 @@ public function testTlsConnect() { $this->markTestSkipped(); }
         public function testReset() { $this->markTestSkipped(); }
         public function testInvalidAuthArgs() { $this->markTestSkipped(); }
         public function testScanErrors() { $this->markTestSkipped(); }
    +    public function testConnectDatabaseSelect() { $this->markTestSkipped(); }
     
         /* These 'directed node' commands work differently in RedisCluster */
         public function testConfig() { $this->markTestSkipped(); }
    
    From 0a85bd824a1506d54abe3c48a3ad12c34429b00d Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Wed, 4 Dec 2024 09:36:04 +0100
    Subject: [PATCH 1976/1986] Simplify redis_unpack method calling
    
    This method always unpack given string to zval, so it is not necessary to check output value
    ---
     cluster_library.c | 38 ++++++++++++--------------------------
     library.c         | 37 +++++++++++++++++++------------------
     redis_commands.c  |  4 +---
     3 files changed, 32 insertions(+), 47 deletions(-)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index 97e7ddf559..bdf89526cf 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -1684,9 +1684,7 @@ static int cluster_bulk_resp_to_zval(redisCluster *c, zval *zdst) {
             return FAILURE;
         }
     
    -    if (!redis_unpack(c->flags, resp, c->reply_len, zdst)) {
    -        ZVAL_STRINGL_FAST(zdst, resp, c->reply_len);
    -    }
    +    redis_unpack(c->flags, resp, c->reply_len, zdst);
     
         efree(resp);
     
    @@ -2853,11 +2851,8 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
     
             if (line != NULL) {
                 zval z_unpacked;
    -            if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) {
    -                add_next_index_zval(z_result, &z_unpacked);
    -            } else {
    -                add_next_index_stringl(z_result, line, line_len);
    -            }
    +            redis_unpack(redis_sock, line, line_len, &z_unpacked);
    +            add_next_index_zval(z_result, &z_unpacked);
                 efree(line);
             } else {
                 add_next_index_bool(z_result, 0);
    @@ -2893,11 +2888,8 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
             } else {
                 /* Attempt unpacking */
                 zval z_unpacked;
    -            if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) {
    -                add_assoc_zval(z_result, key, &z_unpacked);
    -            } else {
    -                add_assoc_stringl_ex(z_result, key, key_len, line, line_len);
    -            }
    +            redis_unpack(redis_sock, line, line_len, &z_unpacked);
    +            add_assoc_zval(z_result, key, &z_unpacked);
     
                 efree(line);
                 efree(key);
    @@ -2929,14 +2921,11 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result,
                     key_len = line_len;
                 } else {
                     zval zv, *z = &zv;
    -                if (redis_unpack(redis_sock,key,key_len, z)) {
    -                    zend_string *zstr = zval_get_string(z);
    -                    add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line));
    -                    zend_string_release(zstr);
    -                    zval_dtor(z);
    -                } else {
    -                    add_assoc_double_ex(z_result, key, key_len, atof(line));
    -                }
    +                redis_unpack(redis_sock,key,key_len, z);
    +                zend_string *zstr = zval_get_string(z);
    +                add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line));
    +                zend_string_release(zstr);
    +                zval_dtor(z);
     
                     /* Free our key and line */
                     efree(key);
    @@ -2963,11 +2952,8 @@ int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result,
     
             if (line != NULL) {
                 zval z_unpacked;
    -            if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) {
    -                add_assoc_zval_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked);
    -            } else {
    -                add_assoc_stringl_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), line, line_len);
    -            }
    +            redis_unpack(redis_sock, line, line_len, &z_unpacked);
    +            add_assoc_zval_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked);
                 efree(line);
             } else {
                 add_assoc_bool_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0);
    diff --git a/library.c b/library.c
    index d6f357c113..050fe3b62e 100644
    --- a/library.c
    +++ b/library.c
    @@ -107,13 +107,6 @@ void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id) {
         zend_register_persistent_resource(ZSTR_VAL(id), ZSTR_LEN(id), ptr, le_id);
     }
     
    -/* Do not allocate empty string or string with one character */
    -static zend_always_inline void redis_add_next_index_stringl(zval *arg, const char *str, size_t length) {
    -    zval tmp;
    -    ZVAL_STRINGL_FAST(&tmp, str, length);
    -    zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp);
    -}
    -
     static ConnectionPool *
     redis_sock_get_connection_pool(RedisSock *redis_sock)
     {
    @@ -2751,9 +2744,7 @@ static int redis_bulk_resp_to_zval(RedisSock *redis_sock, zval *zdst, int *dstle
             return FAILURE;
         }
     
    -    if (!redis_unpack(redis_sock, resp, len, zdst)) {
    -        ZVAL_STRINGL_FAST(zdst, resp, len);
    -    }
    +    redis_unpack(redis_sock, resp, len, zdst);
     
         efree(resp);
         return SUCCESS;
    @@ -3503,7 +3494,7 @@ PHP_REDIS_API void
     redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count,
                            int unserialize)
     {
    -    zval z_unpacked;
    +    zval z_value;
         char *line;
         int i, len;
     
    @@ -3522,11 +3513,13 @@ redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count,
                 (unserialize == UNSERIALIZE_VALS && i % 2 != 0)
             );
     
    -        if (unwrap && redis_unpack(redis_sock, line, len, &z_unpacked)) {
    -            add_next_index_zval(z_tab, &z_unpacked);
    +        if (unwrap) {
    +            redis_unpack(redis_sock, line, len, &z_value);
             } else {
    -            redis_add_next_index_stringl(z_tab, line, len);
    +            ZVAL_STRINGL_FAST(&z_value, line, len);
             }
    +        zend_hash_next_index_insert_new(Z_ARRVAL_P(z_tab), &z_value);
    +
             efree(line);
         }
     }
    @@ -3611,9 +3604,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc
             response = redis_sock_read(redis_sock, &response_len);
             zval z_unpacked;
             if (response != NULL) {
    -            if (!redis_unpack(redis_sock, response, response_len, &z_unpacked)) {
    -                ZVAL_STRINGL(&z_unpacked, response, response_len);
    -            }
    +            redis_unpack(redis_sock, response, response_len, &z_unpacked);
                 efree(response);
             } else {
                 ZVAL_FALSE(&z_unpacked);
    @@ -4020,6 +4011,12 @@ redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) {
             }
         }
     
    +    /* Input string is empty */
    +    if (srclen == 0) {
    +        ZVAL_STR(zdst, ZSTR_EMPTY_ALLOC());
    +        return 1;
    +    }
    +
         /* Uncompress, then unserialize */
         if (redis_uncompress(redis_sock, &buf, &len, src, srclen)) {
             if (!redis_unserialize(redis_sock, buf, len, zdst)) {
    @@ -4029,7 +4026,11 @@ redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) {
             return 1;
         }
     
    -    return redis_unserialize(redis_sock, buf, len, zdst);
    +    if (!redis_unserialize(redis_sock, src, srclen, zdst)) {
    +        ZVAL_STRINGL_FAST(zdst, src, srclen);
    +    }
    +
    +    return 1;
     }
     
     PHP_REDIS_API int
    diff --git a/redis_commands.c b/redis_commands.c
    index 1d2c23360f..d57d4c9f8b 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -6465,8 +6465,6 @@ void redis_unpack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
             RETURN_FALSE;
         }
     
    -    if (redis_unpack(redis_sock, ZSTR_VAL(str), ZSTR_LEN(str), return_value) == 0) {
    -        RETURN_STR_COPY(str);
    -    }
    +    redis_unpack(redis_sock, ZSTR_VAL(str), ZSTR_LEN(str), return_value);
     }
     /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
    
    From 5208818e8c8422f33f5299aafa51e27679561a78 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Wed, 2 Apr 2025 12:22:52 -0700
    Subject: [PATCH 1977/1986] We can use `zval_get_tmp_string` here
    
    ---
     cluster_library.c | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index bdf89526cf..eca2593c9f 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -2922,9 +2922,9 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result,
                 } else {
                     zval zv, *z = &zv;
                     redis_unpack(redis_sock,key,key_len, z);
    -                zend_string *zstr = zval_get_string(z);
    +                zend_string *tmp, *zstr = zval_get_tmp_string(z, &tmp);
                     add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line));
    -                zend_string_release(zstr);
    +                zend_tmp_string_release(tmp);
                     zval_dtor(z);
     
                     /* Free our key and line */
    
    From 614b86e457532f0cc3c6f41322740e6125949721 Mon Sep 17 00:00:00 2001
    From: Jakub Onderka 
    Date: Tue, 26 Nov 2024 21:26:43 +0100
    Subject: [PATCH 1978/1986] New macros REDIS_RESPONSE_ERROR and
     REDIS_RETURN_ZVAL
    
    Deduplicate code that is used in many methods. Also optimise adding new element to array in pipeline mode and returning zval in atomic mode
    ---
     library.c | 245 +++++++++++++-----------------------------------------
     1 file changed, 59 insertions(+), 186 deletions(-)
    
    diff --git a/library.c b/library.c
    index 050fe3b62e..a9fb523e48 100644
    --- a/library.c
    +++ b/library.c
    @@ -88,6 +88,27 @@
             } \
         } while (0)
     
    +/** Set return value to false in case of we are in atomic mode or add FALSE to output array in pipeline mode */
    +#define REDIS_RESPONSE_ERROR(redis_sock, z_tab) \
    +    do { \
    +        if (IS_ATOMIC(redis_sock)) { \
    +            RETVAL_FALSE; \
    +        } else { \
    +            add_next_index_bool(z_tab, 0); \
    +        } \
    +    } while (0)
    +
    +/** Set return value to `zval` in case of we are in atomic mode or add `zval` to output array in pipeline mode */
    +#define REDIS_RETURN_ZVAL(redis_sock, z_tab, zval) \
    +    do { \
    +        if (IS_ATOMIC(redis_sock)) { \
    +            /* Move value of `zval` to `return_value` */ \
    +            ZVAL_COPY_VALUE(return_value, &zval); \
    +        } else { \
    +            zend_hash_next_index_insert_new(Z_ARRVAL_P(z_tab), &zval); \
    +        } \
    +    } while (0)
    +
     #ifndef PHP_WIN32
         #include  /* TCP_NODELAY */
         #include   /* SO_KEEPALIVE */
    @@ -1182,11 +1203,7 @@ redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         double ret;
     
         if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
     
    @@ -1207,11 +1224,7 @@ PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
         long l;
     
         if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
     
    @@ -1292,11 +1305,7 @@ PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
         /* Free source response */
         efree(response);
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_ret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_ret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
     
         return SUCCESS;
     }
    @@ -1386,11 +1395,7 @@ redis_client_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zva
         efree(resp);
     
         /* Return or append depending if we're atomic */
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_ret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_ret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
     
         return SUCCESS;
     }
    @@ -1420,11 +1425,7 @@ redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zva
         efree(resp);
     
         /* Return or append depending if we're atomic */
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_ret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_ret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
     
         return SUCCESS;
     }
    @@ -1592,11 +1593,7 @@ redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z
             res = FAILURE;
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&zdst, 0, 0);
    -    } else {
    -        add_next_index_zval(z_tab, &zdst);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, zdst);
     
         return res;
     }
    @@ -1656,11 +1653,7 @@ PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS,
         int response_len;
     
         if ((response = redis_sock_read(redis_sock, &response_len)) == NULL || *response != TYPE_INT) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             if (response) efree(response);
             return FAILURE;
         }
    @@ -1789,11 +1782,7 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         int numElems;
     
         if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
         zval z_multi_result;
    @@ -1810,11 +1799,7 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             array_zip_values_and_scores(redis_sock, &z_multi_result, decode);
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_multi_result, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_multi_result);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result);
     
         return 0;
     }
    @@ -1903,11 +1888,7 @@ redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             ZVAL_FALSE(&zret);
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&zret, 0, 0);
    -    } else {
    -        add_next_index_zval(z_tab, &zret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, zret);
     
         return res;
     }
    @@ -1987,11 +1968,7 @@ redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             ZVAL_FALSE(&zret);
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&zret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &zret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, zret);
     
         return SUCCESS;
     }
    @@ -2003,11 +1980,7 @@ redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
         zval z_ret;
     
         if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
     
    @@ -2015,11 +1988,7 @@ redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
         redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret);
         array_zip_values_and_scores(redis_sock, &z_ret, 0);
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_ret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_ret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
     
         return SUCCESS;
     }
    @@ -2120,11 +2089,7 @@ redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *
         zval z_ret;
     
         if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
     
    @@ -2132,11 +2097,7 @@ redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *
         redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret);
         array_zip_values_recursive(&z_ret);
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_ret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_ret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
     
         return SUCCESS;
     }
    @@ -2164,21 +2125,13 @@ redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
         zval z_ret;
     
         if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
     
         array_init(&z_ret);
         redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret);
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_ret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_ret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
     
         return SUCCESS;
     }
    @@ -2249,19 +2202,11 @@ redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             redis_read_stream_messages(redis_sock, messages, &z_messages) < 0)
         {
             zval_dtor(&z_messages);
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return -1;
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_messages, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_messages);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_messages);
     
         return 0;
     }
    @@ -2318,21 +2263,13 @@ redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                 goto cleanup;
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_rv, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_rv);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_rv);
         return 0;
     
     cleanup:
         zval_dtor(&z_rv);
     failure:
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_FALSE;
    -    } else {
    -        add_next_index_bool(z_tab, 0);
    -    }
    +    REDIS_RESPONSE_ERROR(redis_sock, z_tab);
         return -1;
     }
     
    @@ -2459,20 +2396,12 @@ redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         if (redis_read_xclaim_reply(redis_sock, count, ctx == PHPREDIS_CTX_PTR, &z_ret) < 0)
             goto failure;
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_ret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_ret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
     
         return 0;
     
     failure:
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_FALSE;
    -    } else {
    -        add_next_index_bool(z_tab, 0);
    -    }
    +    REDIS_RESPONSE_ERROR(redis_sock, z_tab);
         return -1;
     }
     
    @@ -2550,20 +2479,13 @@ redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_t
         if (read_mbulk_header(redis_sock, &elements) == SUCCESS) {
             array_init(&z_ret);
             if (redis_read_xinfo_response(redis_sock, &z_ret, elements) == SUCCESS) {
    -            if (IS_ATOMIC(redis_sock)) {
    -                RETVAL_ZVAL(&z_ret, 0, 1);
    -            } else {
    -                add_next_index_zval(z_tab, &z_ret);
    -            }
    +            REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
                 return SUCCESS;
             }
             zval_dtor(&z_ret);
         }
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_FALSE;
    -    } else {
    -        add_next_index_bool(z_tab, 0);
    -    }
    +
    +    REDIS_RESPONSE_ERROR(redis_sock, z_tab);
         return FAILURE;
     }
     
    @@ -2661,11 +2583,7 @@ int redis_acl_custom_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
             ZVAL_FALSE(&zret);
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&zret, 0, 0);
    -    } else {
    -        add_next_index_zval(z_tab, &zret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, zret);
     
         return res;
     }
    @@ -2759,11 +2677,7 @@ redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     
         ret = redis_bulk_resp_to_zval(redis_sock, &zret, NULL);
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&zret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &zret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, zret);
     
         return ret;
     }
    @@ -2800,11 +2714,7 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         if ((response = redis_sock_read(redis_sock, &response_len))
                                         == NULL)
         {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
         if (IS_ATOMIC(redis_sock)) {
    @@ -3393,11 +3303,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
         int numElems;
     
         if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
         if (numElems == -1 && redis_sock->null_mbulk_as_null) {
    @@ -3409,11 +3315,7 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS,
             redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL);
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_multi_result, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_multi_result);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result);
     
         return 0;
     }
    @@ -3426,11 +3328,7 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
         int numElems;
     
         if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
         zval z_multi_result;
    @@ -3442,11 +3340,7 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval
             redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE);
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_multi_result, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_multi_result);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result);
     
         return SUCCESS;
     }
    @@ -3459,11 +3353,7 @@ redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
         zval z_multi_result;
     
         if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             return FAILURE;
         }
     
    @@ -3481,11 +3371,7 @@ redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
             }
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_multi_result, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_multi_result);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result);
     
         return SUCCESS;
     }
    @@ -3586,11 +3472,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc
         zval *z_keys = ctx;
     
         if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    +        REDIS_RESPONSE_ERROR(redis_sock, z_tab);
             retval = FAILURE;
             goto end;
         }
    @@ -3613,11 +3495,7 @@ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSoc
             zend_tmp_string_release(tmp_str);
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        RETVAL_ZVAL(&z_multi_result, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_multi_result);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result);
     
         retval = SUCCESS;
     
    @@ -4472,12 +4350,7 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                 return FAILURE;
         }
     
    -    if (IS_ATOMIC(redis_sock)) {
    -        /* Set our return value */
    -        RETVAL_ZVAL(&z_ret, 0, 1);
    -    } else {
    -        add_next_index_zval(z_tab, &z_ret);
    -    }
    +    REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret);
     
         /* Success */
         return 0;
    
    From 3c64b33ffe06a8929d61dd2b71ae5ea08014a455 Mon Sep 17 00:00:00 2001
    From: Rory 
    Date: Tue, 8 Apr 2025 16:31:38 +1200
    Subject: [PATCH 1979/1986] Fix SIGABRT in PHP 8.4 with RedisArray
    
    Same fix as 6e5360d1, with PHP switching from `ZEND_ASSUME` to `ZEND_ASSERT` in zend_hash_str_update_ptr.
    
    Fixes #2648
    ---
     redis_array_impl.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/redis_array_impl.c b/redis_array_impl.c
    index 78b6d16789..a8d06875ed 100644
    --- a/redis_array_impl.c
    +++ b/redis_array_impl.c
    @@ -91,7 +91,7 @@ ra_init_function_table(RedisArray *ra)
         zend_hash_init(ra->pure_cmds, 0, NULL, NULL, 0);
     
         #define ra_add_pure_cmd(cmd) \
    -        zend_hash_str_update_ptr(ra->pure_cmds, cmd, sizeof(cmd) - 1, NULL);
    +        zend_hash_str_add_empty_element(ra->pure_cmds, cmd, sizeof(cmd) - 1);
     
         ra_add_pure_cmd("EXISTS");
         ra_add_pure_cmd("GET");
    
    From bfbab8925878409d0f6614c17a597e74c30574a8 Mon Sep 17 00:00:00 2001
    From: Michael Giuffrida 
    Date: Sat, 19 Apr 2025 21:30:38 -0500
    Subject: [PATCH 1980/1986] Broaden return type for Redis::hGetAll
    
    `Redis::hGetAll()` returns an array indexed by `string`s and/or `int`s depending on the values in the hash set.
    
    The function in the PHP stub was annotated as though the array were keyed only by strings, which is tighter than reality.
    ---
     redis.stub.php | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/redis.stub.php b/redis.stub.php
    index 8ace66a8c5..a2cca878ad 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1733,7 +1733,7 @@ public function hGet(string $key, string $member): mixed;
          * Read every field and value from a hash.
          *
          * @param string $key The hash to query.
    -     * @return Redis|array|false All fields and values or false if the key didn't exist.
    +     * @return Redis|array|false All fields and values or false if the key didn't exist.
          *
          * @see https://redis.io/commands/hgetall
          *
    
    From b7a97e5ec37ade2481f875295e45a2e1b6dd5366 Mon Sep 17 00:00:00 2001
    From: AkameOuO 
    Date: Fri, 18 Apr 2025 13:32:05 +0800
    Subject: [PATCH 1981/1986] Update README.md
    
    ---
     README.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/README.md b/README.md
    index 68e70a2dee..61259f0fb6 100644
    --- a/README.md
    +++ b/README.md
    @@ -2,7 +2,7 @@
     
     [![Build Status](https://github.com/phpredis/phpredis/actions/workflows/ci.yml/badge.svg)](https://github.com/phpredis/phpredis/actions/workflows/ci.yml)
     [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis)
    -[![PHP version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://github.com/phpredis/phpredis)
    +[![PHP version](https://img.shields.io/badge/php-%3E%3D%207.4-8892BF.svg)](https://github.com/phpredis/phpredis)
     
     The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It also supports [KeyDB](https://docs.keydb.dev/) and [Valkey](https://valkey.io/), which are open source alternatives to Redis.
     
    
    From b48aa0d471bf7280a1365fb5b4cb7595b5920498 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sun, 20 Apr 2025 09:59:59 -0700
    Subject: [PATCH 1982/1986] Fix an unused variable warning
    
    ---
     cluster_library.c      | 3 +--
     redis_arginfo.h        | 2 +-
     redis_legacy_arginfo.h | 2 +-
     3 files changed, 3 insertions(+), 4 deletions(-)
    
    diff --git a/cluster_library.c b/cluster_library.c
    index eca2593c9f..38e1a6505b 100644
    --- a/cluster_library.c
    +++ b/cluster_library.c
    @@ -2867,8 +2867,8 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
                                long long count, void *ctx)
     {
         char *line, *key = NULL;
    -    int line_len, key_len = 0;
         long long idx = 0;
    +    int line_len;
     
         // Our count will need to be divisible by 2
         if (count % 2 != 0) {
    @@ -2884,7 +2884,6 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
             if (idx++ % 2 == 0) {
                 // Save our key and length
                 key = line;
    -            key_len = line_len;
             } else {
                 /* Attempt unpacking */
                 zval z_unpacked;
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index 08a2308ffe..7f31a5e21a 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 805a66c17b7c9972c73a979bdd67f98f7c1f6c74 */
    + * Stub hash: 3a08bc16dd5a73e721e0df8f7843acdbbb585df5 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index 6bfc3a39ab..a6aae1c1c2 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 805a66c17b7c9972c73a979bdd67f98f7c1f6c74 */
    + * Stub hash: 3a08bc16dd5a73e721e0df8f7843acdbbb585df5 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    
    From 593ba012ac49065343f6bbf10adca5047414ce85 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Sun, 4 May 2025 10:20:01 -0700
    Subject: [PATCH 1983/1986] Check for `dragonfly_version` in `HELLO` response
    
    DragonflyDB will report to be Redis but also include `dragonfly_version`
    in the hello response, which we can use to identify the fork.
    
    Also fix parsing of the `HELLO` response for `serverName()` and
    `serverVersion()`. Starting in Redis 8.0 there seem to always be modules
    running, which the previous function was not expecting or parsing.
    ---
     library.c           | 39 ++++++++++++++++++++++++++-------------
     redis.c             |  2 +-
     tests/RedisTest.php |  2 ++
     3 files changed, 29 insertions(+), 14 deletions(-)
    
    diff --git a/library.c b/library.c
    index a9fb523e48..ce3e2672d0 100644
    --- a/library.c
    +++ b/library.c
    @@ -2018,26 +2018,31 @@ static int
     redis_hello_response(INTERNAL_FUNCTION_PARAMETERS,
                          RedisSock *redis_sock, zval *z_tab, void *ctx)
     {
    -    int numElems;
         zval z_ret, *zv;
    +    int numElems;
     
    -    if (read_mbulk_header(redis_sock, &numElems) < 0) {
    -        if (IS_ATOMIC(redis_sock)) {
    -            RETVAL_FALSE;
    -        } else {
    -            add_next_index_bool(z_tab, 0);
    -        }
    -        return FAILURE;
    -    }
    +    if (read_mbulk_header(redis_sock, &numElems) < 0)
    +        goto fail;
     
         array_init(&z_ret);
    -    redis_mbulk_reply_zipped_raw_variant(redis_sock, &z_ret, numElems);
    +
    +    if (redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret) != SUCCESS ||
    +        array_zip_values_recursive(&z_ret) != SUCCESS) 
    +    {
    +        zval_dtor(&z_ret);
    +        goto fail;
    +    }
     
         if (redis_sock->hello.server) {
             zend_string_release(redis_sock->hello.server);
         }
    -    zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("server"));
    -    redis_sock->hello.server = zv ? zval_get_string(zv) : ZSTR_EMPTY_ALLOC();
    +
    +    if ((zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("dragonfly_version")))) {
    +        redis_sock->hello.server = zend_string_init(ZEND_STRL("dragonfly"), 0);
    +    } else {
    +        zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("server"));
    +        redis_sock->hello.server = zv ? zval_get_string(zv) : ZSTR_EMPTY_ALLOC();
    +    }
     
         if (redis_sock->hello.version) {
             zend_string_release(redis_sock->hello.version);
    @@ -2063,6 +2068,14 @@ redis_hello_response(INTERNAL_FUNCTION_PARAMETERS,
         }
     
         return SUCCESS;
    +
    +fail:
    +    if (IS_ATOMIC(redis_sock)) {
    +        RETVAL_FALSE;
    +    } else {
    +        add_next_index_bool(z_tab, 0);
    +    }
    +    return FAILURE;
     }
     
     
    @@ -4302,7 +4315,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, long long elements, int st
             elements--;
         }
     
    -    return 0;
    +    return SUCCESS;
     }
     
     static int
    diff --git a/redis.c b/redis.c
    index 3f13a59888..629dd5c20b 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -2131,7 +2131,7 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
     
             int num = atol(inbuf + 1);
     
    -        if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) < 0) {
    +        if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) != SUCCESS) {
                 return FAILURE;
             }
         }
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index e7854da442..1ebcc61e51 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -2476,6 +2476,7 @@ public function testServerInfo() {
                 $this->markTestSkipped();
     
             $hello = $this->execHello();
    +
             if ( ! $this->assertArrayKey($hello, 'server') ||
                  ! $this->assertArrayKey($hello, 'version'))
             {
    @@ -2486,6 +2487,7 @@ public function testServerInfo() {
             $this->assertEquals($hello['version'], $this->redis->serverVersion());
     
             $info = $this->redis->info();
    +
             $cmd1 = $info['total_commands_processed'];
     
             /* Shouldn't hit the server */
    
    From 7350768cd9285b7d0c5c28742eabe52cfb1b326a Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Tue, 6 May 2025 10:37:21 -0700
    Subject: [PATCH 1984/1986] Implement several hash expiration commands
    
    Commands implemented:
    
    `H[P]EXPIRE`
    `H[P]TTL`
    `H[P]EXPIREAT`
    `H[P]EXPIRETIME`
    `HPERSIST`
    ---
     library.c                      |   2 +-
     redis.c                        |  45 +++++++++++++
     redis.stub.php                 | 115 +++++++++++++++++++++++++++++++++
     redis_arginfo.h                |  53 ++++++++++++++-
     redis_cluster.c                |  43 ++++++++++++
     redis_cluster.stub.php         |  49 ++++++++++++++
     redis_cluster_arginfo.h        |  56 +++++++++++++++-
     redis_cluster_legacy_arginfo.h |  56 +++++++++++++++-
     redis_commands.c               |  88 ++++++++++++++++++++++++-
     redis_commands.h               |   6 ++
     redis_legacy_arginfo.h         |  53 ++++++++++++++-
     tests/RedisTest.php            |  62 ++++++++++++++++++
     12 files changed, 622 insertions(+), 6 deletions(-)
    
    diff --git a/library.c b/library.c
    index ce3e2672d0..c73858ef51 100644
    --- a/library.c
    +++ b/library.c
    @@ -2027,7 +2027,7 @@ redis_hello_response(INTERNAL_FUNCTION_PARAMETERS,
         array_init(&z_ret);
     
         if (redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret) != SUCCESS ||
    -        array_zip_values_recursive(&z_ret) != SUCCESS) 
    +        array_zip_values_recursive(&z_ret) != SUCCESS)
         {
             zval_dtor(&z_ret);
             goto fail;
    diff --git a/redis.c b/redis.c
    index 629dd5c20b..2ef2fc8fa9 100644
    --- a/redis.c
    +++ b/redis.c
    @@ -1878,6 +1878,51 @@ PHP_METHOD(Redis, hMset)
     }
     /* }}} */
     
    +PHP_METHOD(Redis, hexpire) {
    +    REDIS_PROCESS_KW_CMD("HEXPIRE", redis_hexpire_cmd,
    +                         redis_read_variant_reply);
    +}
    +
    +PHP_METHOD(Redis, hpexpire) {
    +    REDIS_PROCESS_KW_CMD("HPEXPIRE", redis_hexpire_cmd,
    +                         redis_read_variant_reply);
    +}
    +
    +PHP_METHOD(Redis, hexpireat) {
    +    REDIS_PROCESS_KW_CMD("HEXPIREAT", redis_hexpire_cmd,
    +                         redis_read_variant_reply);
    +}
    +
    +PHP_METHOD(Redis, hpexpireat) {
    +    REDIS_PROCESS_KW_CMD("HPEXPIREAT", redis_hexpire_cmd,
    +                         redis_read_variant_reply);
    +}
    +
    +PHP_METHOD(Redis, httl) {
    +    REDIS_PROCESS_KW_CMD("HTTL", redis_httl_cmd,
    +                         redis_read_variant_reply);
    +}
    +
    +PHP_METHOD(Redis, hpttl) {
    +    REDIS_PROCESS_KW_CMD("HPTTL", redis_httl_cmd,
    +                         redis_read_variant_reply);
    +}
    +
    +PHP_METHOD(Redis, hexpiretime) {
    +    REDIS_PROCESS_KW_CMD("HEXPIRETIME", redis_httl_cmd,
    +                         redis_read_variant_reply);
    +}
    +
    +PHP_METHOD(Redis, hpexpiretime) {
    +    REDIS_PROCESS_KW_CMD("HPEXPIRETIME", redis_httl_cmd,
    +                         redis_read_variant_reply);
    +}
    +
    +PHP_METHOD(Redis, hpersist) {
    +    REDIS_PROCESS_KW_CMD("HPERSIST", redis_httl_cmd,
    +                         redis_read_variant_reply);
    +}
    +
     /* {{{ proto bool Redis::hRandField(string key, [array $options]) */
     PHP_METHOD(Redis, hRandField)
     {
    diff --git a/redis.stub.php b/redis.stub.php
    index a2cca878ad..3f95468d7a 100644
    --- a/redis.stub.php
    +++ b/redis.stub.php
    @@ -1913,6 +1913,121 @@ public function hStrLen(string $key, string $field): Redis|int|false;
          */
         public function hVals(string $key): Redis|array|false;
     
    +    /**
    +     * Set the expiration on one or more fields in a hash.
    +     *
    +     * @param string $key    The hash to update.
    +     * @param int    $ttl    The time to live in seconds.
    +     * @param array  $fields The fields to set the expiration on.
    +     * @param string|null $option An optional mode (NX, XX, ETC)
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/hexpire
    +     */
    +    public function hexpire(string $key, int $ttl, array $fields,
    +                            ?string $mode = NULL): Redis|array|false;
    +
    +    /**
    +     * Set the expiration on one or more fields in a hash in milliseconds.
    +     *
    +     * @param string $key    The hash to update.
    +     * @param int    $ttl    The time to live in milliseconds.
    +     * @param array  $fields The fields to set the expiration on.
    +     * @param string|null $option An optional mode (NX, XX, ETC)
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/hexpire
    +     */
    +    public function hpexpire(string $key, int $ttl, array $fields,
    +                            ?string $mode = NULL): Redis|array|false;
    +
    +    /**
    +     * Set the expiration time on one or more fields of a hash.
    +     *
    +     * @param string $key    The hash to update.
    +     * @param int    $time   The time to live in seconds.
    +     * @param array  $fields The fields to set the expiration on.
    +     * @param string|null $option An optional mode (NX, XX, ETC)
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/hexpire
    +     */
    +    public function hexpireat(string $key, int $time, array $fields,
    +                              ?string $mode = NULL): Redis|array|false;
    +
    +    /**
    +     * Set the expiration time on one or more fields of a hash in milliseconds.
    +     *
    +     * @param string $key    The hash to update.
    +     * @param int    $mstime The time to live in milliseconds.
    +     * @param array  $fields The fields to set the expiration on.
    +     * @param string|null $option An optional mode (NX, XX, ETC)
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/hexpire
    +     */
    +    public function hpexpireat(string $key, int $mstime, array $fields,
    +                               ?string $mode = NULL): Redis|array|false;
    +
    +    /**
    +     * Get the TTL of one or more fields in a hash
    +     *
    +     * @param string $key    The hash to query.
    +     * @param array  $fields The fields to query.
    +     *
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/httl
    +     */
    +    public function httl(string $key, array $fields): Redis|array|false;
    +
    +    /**
    +     * Get the millisecond TTL of one or more fields in a hash
    +     *
    +     * @param string $key    The hash to query.
    +     * @param array  $fields The fields to query.
    +     *
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/hpttl
    +     */
    +    public function hpttl(string $key, array $fields): Redis|array|false;
    +
    +    /**
    +     * Get the expiration time of one or more fields in a hash
    +     *
    +     * @param string $key    The hash to query.
    +     * @param array  $fields The fields to query.
    +     *
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/hexpiretime
    +     */
    +    public function hexpiretime(string $key, array $fields): Redis|array|false;
    +
    +    /**
    +     * Get the expiration time in milliseconds of one or more fields in a hash
    +     *
    +     * @param string $key    The hash to query.
    +     * @param array  $fields The fields to query.
    +     *
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/hpexpiretime
    +     */
    +    public function hpexpiretime(string $key, array $fields): Redis|array|false;
    +
    +    /**
    +     * Persist one or more hash fields
    +     *
    +     * @param string $key    The hash to query.
    +     * @param array  $fields The fields to query.
    +     *
    +     * @return Redis|array|false
    +     *
    +     * @see https://redis.io/commands/hpersist
    +     */
    +    public function hpersist(string $key, array $fields): Redis|array|false;
     
         /**
          * Iterate over the fields and values of a hash in an incremental fashion.
    diff --git a/redis_arginfo.h b/redis_arginfo.h
    index 7f31a5e21a..32c2754da4 100644
    --- a/redis_arginfo.h
    +++ b/redis_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 3a08bc16dd5a73e721e0df8f7843acdbbb585df5 */
    + * Stub hash: c6205649cd23ff2b9fcc63a034b601ee566ef236 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
    @@ -464,6 +464,39 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_hVals arginfo_class_Redis_getWithMeta
     
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hexpire, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0)
    +	ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_Redis_hpexpire arginfo_class_Redis_hexpire
    +
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hexpireat, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, time, IS_LONG, 0)
    +	ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hpexpireat, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, mstime, IS_LONG, 0)
    +	ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_Redis_httl arginfo_class_Redis_hMget
    +
    +#define arginfo_class_Redis_hpttl arginfo_class_Redis_hMget
    +
    +#define arginfo_class_Redis_hexpiretime arginfo_class_Redis_hMget
    +
    +#define arginfo_class_Redis_hpexpiretime arginfo_class_Redis_hMget
    +
    +#define arginfo_class_Redis_hpersist arginfo_class_Redis_hMget
    +
     ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
     	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
     	ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL)
    @@ -1292,6 +1325,15 @@ ZEND_METHOD(Redis, hSet);
     ZEND_METHOD(Redis, hSetNx);
     ZEND_METHOD(Redis, hStrLen);
     ZEND_METHOD(Redis, hVals);
    +ZEND_METHOD(Redis, hexpire);
    +ZEND_METHOD(Redis, hpexpire);
    +ZEND_METHOD(Redis, hexpireat);
    +ZEND_METHOD(Redis, hpexpireat);
    +ZEND_METHOD(Redis, httl);
    +ZEND_METHOD(Redis, hpttl);
    +ZEND_METHOD(Redis, hexpiretime);
    +ZEND_METHOD(Redis, hpexpiretime);
    +ZEND_METHOD(Redis, hpersist);
     ZEND_METHOD(Redis, hscan);
     ZEND_METHOD(Redis, expiremember);
     ZEND_METHOD(Redis, expirememberat);
    @@ -1553,6 +1595,15 @@ static const zend_function_entry class_Redis_methods[] = {
     	ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hexpire, arginfo_class_Redis_hexpire, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpexpire, arginfo_class_Redis_hpexpire, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hexpireat, arginfo_class_Redis_hexpireat, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpexpireat, arginfo_class_Redis_hpexpireat, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, httl, arginfo_class_Redis_httl, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpttl, arginfo_class_Redis_hpttl, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hexpiretime, arginfo_class_Redis_hexpiretime, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpexpiretime, arginfo_class_Redis_hpexpiretime, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpersist, arginfo_class_Redis_hpersist, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, expiremember, arginfo_class_Redis_expiremember, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, expirememberat, arginfo_class_Redis_expirememberat, ZEND_ACC_PUBLIC)
    diff --git a/redis_cluster.c b/redis_cluster.c
    index 1cbd825925..7b63696298 100644
    --- a/redis_cluster.c
    +++ b/redis_cluster.c
    @@ -1203,6 +1203,49 @@ PHP_METHOD(RedisCluster, hmset) {
     }
     /* }}} */
     
    +PHP_METHOD(RedisCluster, hexpire) {
    +    CLUSTER_PROCESS_KW_CMD("HEXPIRE",
    +                           redis_hexpire_cmd, cluster_variant_resp, 0);
    +}
    +
    +PHP_METHOD(RedisCluster, hpexpire) {
    +    CLUSTER_PROCESS_KW_CMD("HPEXPIRE",
    +                           redis_hexpire_cmd, cluster_variant_resp, 0);
    +}
    +
    +PHP_METHOD(RedisCluster, hexpireat) {
    +    CLUSTER_PROCESS_KW_CMD("HEXPIREAT",
    +                           redis_hexpire_cmd, cluster_variant_resp, 0);
    +}
    +
    +PHP_METHOD(RedisCluster, hpexpireat) {
    +    CLUSTER_PROCESS_KW_CMD("HPEXPIREAT",
    +                           redis_hexpire_cmd, cluster_variant_resp, 0);
    +}
    +
    +PHP_METHOD(RedisCluster, httl) {
    +    CLUSTER_PROCESS_KW_CMD("HTTL", redis_httl_cmd, cluster_variant_resp, 1);
    +}
    +
    +PHP_METHOD(RedisCluster, hpttl) {
    +    CLUSTER_PROCESS_KW_CMD("HPTTL", redis_httl_cmd, cluster_variant_resp, 1);
    +}
    +
    +
    +PHP_METHOD(RedisCluster, hexpiretime) {
    +    CLUSTER_PROCESS_KW_CMD("HEXPIRETIME", redis_httl_cmd,
    +                           cluster_variant_resp, 1);
    +}
    +
    +PHP_METHOD(RedisCluster, hpexpiretime) {
    +    CLUSTER_PROCESS_KW_CMD("HPEXPIRETIME", redis_httl_cmd,
    +                           cluster_variant_resp, 1);
    +}
    +
    +PHP_METHOD(RedisCluster, hpersist) {
    +    CLUSTER_PROCESS_KW_CMD("HPERSIST", redis_httl_cmd, cluster_variant_resp, 0);
    +}
    +
     /* {{{ proto bool RedisCluster::hrandfield(string key, [array $options]) */
     PHP_METHOD(RedisCluster, hrandfield) {
         CLUSTER_PROCESS_CMD(hrandfield, cluster_hrandfield_resp, 1);
    diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
    index 58cced5777..05a6df7115 100644
    --- a/redis_cluster.stub.php
    +++ b/redis_cluster.stub.php
    @@ -535,6 +535,55 @@ public function hsetnx(string $key, string $member, mixed $value): RedisCluster|
          */
         public function hstrlen(string $key, string $field): RedisCluster|int|false;
     
    +    /**
    +     * @see Redis::hexpire
    +     */
    +    public function hexpire(string $key, int $ttl, array $fields,
    +                            ?string $mode = NULL): RedisCluster|array|false;
    +
    +    /**
    +     * @see Redis::hpexpire
    +     */
    +    public function hpexpire(string $key, int $ttl, array $fields,
    +                            ?string $mode = NULL): RedisCluster|array|false;
    +
    +    /**
    +     * @see Redis::hexpireat
    +     */
    +    public function hexpireat(string $key, int $time, array $fields,
    +                              ?string $mode = NULL): RedisCluster|array|false;
    +
    +    /**
    +     * @see Redis::hpexpireat
    +     */
    +    public function hpexpireat(string $key, int $mstime, array $fields,
    +                               ?string $mode = NULL): RedisCluster|array|false;
    +
    +    /**
    +     * @see Redis::httl
    +     */
    +    public function httl(string $key, array $fields): RedisCluster|array|false;
    +
    +    /**
    +     * @see Redis::hpttl
    +     */
    +    public function hpttl(string $key, array $fields): RedisCluster|array|false;
    +
    +    /**
    +     * @see Redis::hexpiretime
    +     */
    +    public function hexpiretime(string $key, array $fields): RedisCluster|array|false;
    +
    +    /**
    +     * @see Redis::hpexpiretime
    +     */
    +    public function hpexpiretime(string $key, array $fields): RedisCluster|array|false;
    +
    +    /**
    +     * @see Redis::hpexpiretime
    +     */
    +    public function hpersist(string $key, array $fields): RedisCluster|array|false;
    +
         /**
          * @see Redis::hvals
          */
    diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
    index b3fb58475a..4fea76b2f4 100644
    --- a/redis_cluster_arginfo.h
    +++ b/redis_cluster_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 43a43fa735ced4b48a361078ac8a10fb62cb1244 */
    + * Stub hash: 5788cd1d12611ef1ff5747efe07b99f66f07fa05 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
    @@ -455,6 +455,42 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hstrlen,
     	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hexpire, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0)
    +	ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_RedisCluster_hpexpire arginfo_class_RedisCluster_hexpire
    +
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hexpireat, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, time, IS_LONG, 0)
    +	ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hpexpireat, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, mstime, IS_LONG, 0)
    +	ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0)
    +	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_httl, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE)
    +	ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
    +	ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0)
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_RedisCluster_hpttl arginfo_class_RedisCluster_httl
    +
    +#define arginfo_class_RedisCluster_hexpiretime arginfo_class_RedisCluster_httl
    +
    +#define arginfo_class_RedisCluster_hpexpiretime arginfo_class_RedisCluster_httl
    +
    +#define arginfo_class_RedisCluster_hpersist arginfo_class_RedisCluster_httl
    +
     #define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster_getWithMeta
     
     #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr
    @@ -1160,6 +1196,15 @@ ZEND_METHOD(RedisCluster, hrandfield);
     ZEND_METHOD(RedisCluster, hset);
     ZEND_METHOD(RedisCluster, hsetnx);
     ZEND_METHOD(RedisCluster, hstrlen);
    +ZEND_METHOD(RedisCluster, hexpire);
    +ZEND_METHOD(RedisCluster, hpexpire);
    +ZEND_METHOD(RedisCluster, hexpireat);
    +ZEND_METHOD(RedisCluster, hpexpireat);
    +ZEND_METHOD(RedisCluster, httl);
    +ZEND_METHOD(RedisCluster, hpttl);
    +ZEND_METHOD(RedisCluster, hexpiretime);
    +ZEND_METHOD(RedisCluster, hpexpiretime);
    +ZEND_METHOD(RedisCluster, hpersist);
     ZEND_METHOD(RedisCluster, hvals);
     ZEND_METHOD(RedisCluster, incr);
     ZEND_METHOD(RedisCluster, incrby);
    @@ -1391,6 +1436,15 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hexpire, arginfo_class_RedisCluster_hexpire, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpexpire, arginfo_class_RedisCluster_hpexpire, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hexpireat, arginfo_class_RedisCluster_hexpireat, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpexpireat, arginfo_class_RedisCluster_hpexpireat, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, httl, arginfo_class_RedisCluster_httl, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpttl, arginfo_class_RedisCluster_hpttl, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hexpiretime, arginfo_class_RedisCluster_hexpiretime, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpexpiretime, arginfo_class_RedisCluster_hpexpiretime, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpersist, arginfo_class_RedisCluster_hpersist, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC)
    diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
    index d117db522a..e1a18b16df 100644
    --- a/redis_cluster_legacy_arginfo.h
    +++ b/redis_cluster_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 43a43fa735ced4b48a361078ac8a10fb62cb1244 */
    + * Stub hash: 5788cd1d12611ef1ff5747efe07b99f66f07fa05 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
     	ZEND_ARG_INFO(0, name)
    @@ -396,6 +396,42 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hstrlen, 0, 0, 2)
     	ZEND_ARG_INFO(0, field)
     ZEND_END_ARG_INFO()
     
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexpire, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, ttl)
    +	ZEND_ARG_INFO(0, fields)
    +	ZEND_ARG_INFO(0, mode)
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_RedisCluster_hpexpire arginfo_class_RedisCluster_hexpire
    +
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexpireat, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, time)
    +	ZEND_ARG_INFO(0, fields)
    +	ZEND_ARG_INFO(0, mode)
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hpexpireat, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, mstime)
    +	ZEND_ARG_INFO(0, fields)
    +	ZEND_ARG_INFO(0, mode)
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_httl, 0, 0, 2)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, fields)
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_RedisCluster_hpttl arginfo_class_RedisCluster_httl
    +
    +#define arginfo_class_RedisCluster_hexpiretime arginfo_class_RedisCluster_httl
    +
    +#define arginfo_class_RedisCluster_hpexpiretime arginfo_class_RedisCluster_httl
    +
    +#define arginfo_class_RedisCluster_hpersist arginfo_class_RedisCluster_httl
    +
     #define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster__prefix
     
     #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr
    @@ -1002,6 +1038,15 @@ ZEND_METHOD(RedisCluster, hrandfield);
     ZEND_METHOD(RedisCluster, hset);
     ZEND_METHOD(RedisCluster, hsetnx);
     ZEND_METHOD(RedisCluster, hstrlen);
    +ZEND_METHOD(RedisCluster, hexpire);
    +ZEND_METHOD(RedisCluster, hpexpire);
    +ZEND_METHOD(RedisCluster, hexpireat);
    +ZEND_METHOD(RedisCluster, hpexpireat);
    +ZEND_METHOD(RedisCluster, httl);
    +ZEND_METHOD(RedisCluster, hpttl);
    +ZEND_METHOD(RedisCluster, hexpiretime);
    +ZEND_METHOD(RedisCluster, hpexpiretime);
    +ZEND_METHOD(RedisCluster, hpersist);
     ZEND_METHOD(RedisCluster, hvals);
     ZEND_METHOD(RedisCluster, incr);
     ZEND_METHOD(RedisCluster, incrby);
    @@ -1233,6 +1278,15 @@ static const zend_function_entry class_RedisCluster_methods[] = {
     	ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hexpire, arginfo_class_RedisCluster_hexpire, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpexpire, arginfo_class_RedisCluster_hpexpire, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hexpireat, arginfo_class_RedisCluster_hexpireat, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpexpireat, arginfo_class_RedisCluster_hpexpireat, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, httl, arginfo_class_RedisCluster_httl, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpttl, arginfo_class_RedisCluster_hpttl, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hexpiretime, arginfo_class_RedisCluster_hexpiretime, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpexpiretime, arginfo_class_RedisCluster_hpexpiretime, ZEND_ACC_PUBLIC)
    +	ZEND_ME(RedisCluster, hpersist, arginfo_class_RedisCluster_hpersist, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC)
     	ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC)
    diff --git a/redis_commands.c b/redis_commands.c
    index d57d4c9f8b..f473da4e33 100644
    --- a/redis_commands.c
    +++ b/redis_commands.c
    @@ -2375,7 +2375,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         }
     
         /* Calculate argc based on options set */
    -    int argc = 2 + (ifeq ? 2 : 0) + (exp_type ? 2 : 0) + (set_type != NULL) + 
    +    int argc = 2 + (ifeq ? 2 : 0) + (exp_type ? 2 : 0) + (set_type != NULL) +
             (keep_ttl != 0) + get;
     
         /* Initial SET   */
    @@ -4634,6 +4634,92 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         Starting with Redis version 6.0.0: Added the AUTH2 option.
     */
     
    +int redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw,
    +                   char **cmd, int *cmd_len, short *slot, void **ctx)
    +{
    +    smart_string cmdstr = {0};
    +    zend_string *key, *field, *tmp;
    +    HashTable *fields;
    +    int argc;
    +    zval *zv;
    +
    +    ZEND_PARSE_PARAMETERS_START(2, 2)
    +        Z_PARAM_STR(key)
    +        Z_PARAM_ARRAY_HT(fields)
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
    +
    +    if (zend_hash_num_elements(fields) < 1) {
    +        php_error_docref(NULL, E_WARNING, "Must pass at least one field");
    +        return FAILURE;
    +    }
    +
    +    // 3 because  FIELDS 
    +    argc = 3 + zend_hash_num_elements(fields);
    +    redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw));
    +
    +    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
    +    REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS");
    +    redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields));
    +
    +    ZEND_HASH_FOREACH_VAL(fields, zv)
    +        field = zval_get_tmp_string(zv, &tmp);
    +        redis_cmd_append_sstr_zstr(&cmdstr, field);
    +        zend_tmp_string_release(tmp);
    +    ZEND_HASH_FOREACH_END();
    +
    +    *cmd = cmdstr.c;
    +    *cmd_len = cmdstr.len;
    +
    +    return SUCCESS;
    +}
    +
    +int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +                      char *kw, char **cmd, int *cmd_len, short *slot,
    +                      void **ctx)
    +{
    +    zend_string *key, *option = NULL, *tmp, *field;
    +    smart_string cmdstr = {0};
    +    HashTable *fields;
    +    zend_long ttl;
    +    zval *zv;
    +    int argc;
    +
    +    ZEND_PARSE_PARAMETERS_START(3, 4)
    +        Z_PARAM_STR(key)
    +        Z_PARAM_LONG(ttl)
    +        Z_PARAM_ARRAY_HT(fields)
    +        Z_PARAM_OPTIONAL
    +        Z_PARAM_STR(option)
    +    ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
    +
    +    if (zend_hash_num_elements(fields) < 1) {
    +        php_error_docref(NULL, E_WARNING, "Must pass at least one field");
    +        return FAILURE;
    +    }
    +
    +    // 4 because   FIELDS 
    +    argc = 4 + zend_hash_num_elements(fields) + (option ? 1 : 0);
    +    redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw));
    +
    +    redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
    +    redis_cmd_append_sstr_long(&cmdstr, ttl);
    +    if (option) redis_cmd_append_sstr_zstr(&cmdstr, option);
    +
    +    REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS");
    +    redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields));
    +
    +    ZEND_HASH_FOREACH_VAL(fields, zv)
    +        field = zval_get_tmp_string(zv, &tmp);
    +        redis_cmd_append_sstr_zstr(&cmdstr, field);
    +        zend_tmp_string_release(tmp);
    +    ZEND_HASH_FOREACH_END();
    +
    +    *cmd = cmdstr.c;
    +    *cmd_len = cmdstr.len;
    +
    +    return SUCCESS;
    +}
    +
     /* MIGRATE */
     int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
                           char **cmd, int *cmd_len, short *slot, void **ctx)
    diff --git a/redis_commands.h b/redis_commands.h
    index b0c5895c4f..6b52fee489 100644
    --- a/redis_commands.h
    +++ b/redis_commands.h
    @@ -356,6 +356,12 @@ int redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
     int redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         char **cmd, int *cmd_len, short *slot, void **ctx);
     
    +int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
    +    char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
    +
    +int redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw,
    +                   char **cmd, int *cmd_len, short *slot, void **ctx);
    +
     int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
         char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
     
    diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
    index a6aae1c1c2..27acccc659 100644
    --- a/redis_legacy_arginfo.h
    +++ b/redis_legacy_arginfo.h
    @@ -1,5 +1,5 @@
     /* This is a generated file, edit the .stub.php file instead.
    - * Stub hash: 3a08bc16dd5a73e721e0df8f7843acdbbb585df5 */
    + * Stub hash: c6205649cd23ff2b9fcc63a034b601ee566ef236 */
     
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
     	ZEND_ARG_INFO(0, options)
    @@ -412,6 +412,39 @@ ZEND_END_ARG_INFO()
     
     #define arginfo_class_Redis_hVals arginfo_class_Redis__prefix
     
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hexpire, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, ttl)
    +	ZEND_ARG_INFO(0, fields)
    +	ZEND_ARG_INFO(0, mode)
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_Redis_hpexpire arginfo_class_Redis_hexpire
    +
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hexpireat, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, time)
    +	ZEND_ARG_INFO(0, fields)
    +	ZEND_ARG_INFO(0, mode)
    +ZEND_END_ARG_INFO()
    +
    +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hpexpireat, 0, 0, 3)
    +	ZEND_ARG_INFO(0, key)
    +	ZEND_ARG_INFO(0, mstime)
    +	ZEND_ARG_INFO(0, fields)
    +	ZEND_ARG_INFO(0, mode)
    +ZEND_END_ARG_INFO()
    +
    +#define arginfo_class_Redis_httl arginfo_class_Redis_hMget
    +
    +#define arginfo_class_Redis_hpttl arginfo_class_Redis_hMget
    +
    +#define arginfo_class_Redis_hexpiretime arginfo_class_Redis_hMget
    +
    +#define arginfo_class_Redis_hpexpiretime arginfo_class_Redis_hMget
    +
    +#define arginfo_class_Redis_hpersist arginfo_class_Redis_hMget
    +
     ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2)
     	ZEND_ARG_INFO(0, key)
     	ZEND_ARG_INFO(1, iterator)
    @@ -1134,6 +1167,15 @@ ZEND_METHOD(Redis, hSet);
     ZEND_METHOD(Redis, hSetNx);
     ZEND_METHOD(Redis, hStrLen);
     ZEND_METHOD(Redis, hVals);
    +ZEND_METHOD(Redis, hexpire);
    +ZEND_METHOD(Redis, hpexpire);
    +ZEND_METHOD(Redis, hexpireat);
    +ZEND_METHOD(Redis, hpexpireat);
    +ZEND_METHOD(Redis, httl);
    +ZEND_METHOD(Redis, hpttl);
    +ZEND_METHOD(Redis, hexpiretime);
    +ZEND_METHOD(Redis, hpexpiretime);
    +ZEND_METHOD(Redis, hpersist);
     ZEND_METHOD(Redis, hscan);
     ZEND_METHOD(Redis, expiremember);
     ZEND_METHOD(Redis, expirememberat);
    @@ -1395,6 +1437,15 @@ static const zend_function_entry class_Redis_methods[] = {
     	ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hexpire, arginfo_class_Redis_hexpire, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpexpire, arginfo_class_Redis_hpexpire, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hexpireat, arginfo_class_Redis_hexpireat, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpexpireat, arginfo_class_Redis_hpexpireat, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, httl, arginfo_class_Redis_httl, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpttl, arginfo_class_Redis_hpttl, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hexpiretime, arginfo_class_Redis_hexpiretime, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpexpiretime, arginfo_class_Redis_hpexpiretime, ZEND_ACC_PUBLIC)
    +	ZEND_ME(Redis, hpersist, arginfo_class_Redis_hpersist, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, expiremember, arginfo_class_Redis_expiremember, ZEND_ACC_PUBLIC)
     	ZEND_ME(Redis, expirememberat, arginfo_class_Redis_expirememberat, ZEND_ACC_PUBLIC)
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 1ebcc61e51..7ca9e6856b 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -6288,6 +6288,68 @@ public function testBackoffOptions() {
             }
         }
     
    +    public function testHashExpiration() {
    +        if ( ! $this->minVersionCheck('7.4.0'))
    +            $this->markTestSkipped();
    +
    +        $hexpire_cmds = [
    +            'hexpire' => 10,
    +            'hpexpire' => 10000,
    +            'hexpireat' => time() + 10,
    +            'hpexpireat' => time() * 1000 + 10000,
    +        ];
    +
    +        $httl_cmds = ['httl', 'hpttl', 'hexpiretime', 'hpexpiretime'];
    +
    +        $hash = ['Picard' => 'Enterprise', 'Sisko' => 'Defiant'];
    +        $keys = array_keys($hash);
    +
    +        foreach ($hexpire_cmds as $exp_cmd => $ttl) {
    +            $this->redis->del('hash');
    +            $this->redis->hmset('hash', $hash);
    +
    +            /* Set a TTL on one existing and one non-existing field */
    +            $res = $this->redis->{$exp_cmd}('hash', $ttl, ['Picard', 'nofield']);
    +
    +            $this->assertEquals($res, [1, -2]);
    +
    +            foreach ($httl_cmds as $ttl_cmd) {
    +                $res = $this->redis->{$ttl_cmd}('hash', $keys);
    +                $this->assertIsArray($res);
    +                $this->assertEquals(count($keys), count($res));
    +
    +                /* Picard: has an expiry (>0), Siskto does not (<0) */
    +                $this->assertTrue($res[0] > 0);
    +                $this->assertTrue($res[1] < 0);
    +            }
    +
    +            $this->redis->del('m');
    +            $this->redis->hmset('m', ['F' => 'V']);
    +
    +            // NX - Only set expiry if it doesn't have one
    +            foreach ([[1], [0]] as $expected) {
    +                $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'NX');
    +                $this->assertEquals($expected, $res);
    +            }
    +
    +            // XX - Set if it has one
    +            $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'XX');
    +            $this->assertEquals([1], $res);
    +            $this->redis->hpersist('m', ['F']);
    +            $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'XX');
    +            $this->assertEquals([0], $res);
    +
    +            // GT - should set if the new expiration is larger
    +            $res = $this->redis->{$exp_cmd}('m', $ttl, ['F']);
    +            $res = $this->redis->{$exp_cmd}('m', $ttl + 100, ['F'], 'GT');
    +            $this->assertEquals([1], $res);
    +
    +            // LT - should not set if the new expiration is smaller
    +            $res = $this->redis->{$exp_cmd}('m', $ttl / 2, ['F'], 'LT');
    +            $this->assertTrue(is_array($res) && $res[0] > 0);
    +        }
    +    }
    +
         public function testHScan() {
             if (version_compare($this->version, '2.8.0') < 0)
                 $this->markTestSkipped();
    
    From 801400036946676e48f975468f2e9c28d2c17027 Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Wed, 7 May 2025 09:02:38 -0700
    Subject: [PATCH 1985/1986] Attempt to fix flaky GitHub CI tests.
    
    We often have to rerun the test suite on GitHub actions because of a
    hard to reproduce "Read error on connection" exception when getting a
    new `RedisCluster` instance.
    
    No one has ever reported this failure outside of GitHub CI and it's not
    clear exactly what might be going on.
    
    This commit does two main things:
    
    1. Allows for one failure to construct a new `RedisCluster` instance but
       only if we detect we're running in GitHub CI.
    
    2. Adds much more diagnostic information if we still have a fatal error
       (e.g. we can't connect in two tries, or some other fatal error
       happens). The new info includes the whole callstack before aborting
       as well as an attempt to manually ping the seeds with `redis-cli`.
    ---
     .github/workflows/ci.yml   |  4 +-
     tests/RedisClusterTest.php | 80 ++++++++++++++++++++++++++++++++++++--
     2 files changed, 78 insertions(+), 6 deletions(-)
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index 569919c563..5da8559e24 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -190,12 +190,12 @@ jobs:
               for PORT in {6379..6382} {7000..7005} {32767..32768} {26379..26380}; do
                 until echo PING | ${{ matrix.server }}-cli -p "$PORT" 2>&1 | grep -qE 'PONG|NOAUTH'; do
                   echo "Still waiting for ${{ matrix.server }} on port $PORT"
    -              sleep .05
    +              sleep .5
                 done
               done
               until echo PING | ${{ matrix.server }}-cli -s /tmp/redis.sock 2>&1 | grep -qE 'PONG|NOAUTH'; do
                 echo "Still waiting for ${{ matrix.server }} at /tmp/redis.sock"
    -            sleep .05
    +            sleep .5
               done
     
           - name: Initialize ${{ matrix.server }} cluster
    diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
    index 04d4286298..a9a70e2e39 100644
    --- a/tests/RedisClusterTest.php
    +++ b/tests/RedisClusterTest.php
    @@ -139,14 +139,86 @@ public function setUp() {
             $this->is_valkey = $this->detectValkey($info);
         }
     
    +    private function findCliExe() {
    +        foreach (['redis-cli', 'valkey-cli'] as $candidate) {
    +            $path = trim(shell_exec("command -v $candidate 2>/dev/null"));
    +            if (is_executable($path)) {
    +                return $path;
    +            }
    +        }
    +
    +        return NULL;
    +    }
    +
    +    private function getServerReply($host, $port, $cmd) {
    +        $cli = $this->findCliExe();
    +        if ( ! $cli) {
    +            return '(no redis-cli or valkey-cli found)';
    +        }
    +
    +        $args = [$cli, '-h', $host, '-p', $port];
    +
    +        $this->getAuthParts($user, $pass);
    +
    +        if ($user) $args = array_merge($args, ['--user', $user]);
    +        if ($pass) $args = array_merge($args, ['-a', $pass]);
    +
    +        $resp = shell_exec(implode(' ', $args) . ' ' . $cmd . ' 2>/dev/null');
    +
    +        return is_string($resp) ? trim($resp) : $resp;
    +    }
    +
    +    /* Try to gat a new RedisCluster instance. The strange logic is an attempt
    +       to solve a problem where this sometimes fails but only ever on GitHub
    +       runners. If we're not on a runner we just get a new instance. Otherwise
    +       we allow for two tries to get the instance. */
    +    private function getNewInstance() {
    +        if (getenv('GITHUB_ACTIONS') === 'true') {
    +            try {
    +                return new RedisCluster(NULL, self::$seeds, 30, 30, true,
    +                                        $this->getAuth());
    +            } catch (Exception $ex) {
    +                TestSuite::errorMessage("Failed to connect: %s", $ex->getMessage());
    +            }
    +        }
    +
    +        return new RedisCluster(NULL, self::$seeds, 30, 30, true, $this->getAuth());
    +    }
    +
         /* Override newInstance as we want a RedisCluster object */
         protected function newInstance() {
             try {
    -            return new RedisCluster(NULL, self::$seeds, 30, 30, true, $this->getAuth());
    +            return $this->getNewInstance();
             } catch (Exception $ex) {
    -            TestSuite::errorMessage("Fatal error: %s\n", $ex->getMessage());
    -            TestSuite::errorMessage("Seeds: %s\n", implode(' ', self::$seeds));
    -            TestSuite::errorMessage("Seed source: %s\n", self::$seed_source);
    +            TestSuite::errorMessage("");
    +            TestSuite::errorMessage("Fatal error: %s", $ex->getMessage());
    +            TestSuite::errorMessage("Seeds: %s", implode(' ', self::$seeds));
    +            TestSuite::errorMessage("Seed source: %s", self::$seed_source);
    +            TestSuite::errorMessage("");
    +
    +            TestSuite::errorMessage("Backtrace:");
    +            foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $i => $frame) {
    +                $file = isset($frame['file']) ? basename($frame['file']) : '[internal]';
    +                $line = $frame['line'] ?? '?';
    +                $func = $frame['function'] ?? 'unknown';
    +                TestSuite::errorMessage("  %s:%d [%s]", $file, $line, $func);
    +            }
    +
    +            TestSuite::errorMessage("\nServer responses:");
    +
    +            /* See if we can shed some light on whether Redis is available */
    +            foreach (self::$seeds as $seed) {
    +                list($host, $port) = explode(':', $seed);
    +
    +                $st = microtime(true);
    +                $reply = $this->getServerReply($host, $port, 'PING');
    +                $et = microtime(true);
    +
    +                TestSuite::errorMessage("  [%s:%d] PING -> %s (%.4f)", $host,
    +                                        $port, var_export($reply, true),
    +                                        $et - $st);
    +            }
    +
                 exit(1);
             }
         }
    
    From 152fdda9b15fe5e60914f43fa34f64fd6e19d90d Mon Sep 17 00:00:00 2001
    From: michael-grunder 
    Date: Wed, 7 May 2025 15:02:02 -0700
    Subject: [PATCH 1986/1986] Fix double -> int truncation warning
    
    ---
     tests/RedisTest.php | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/tests/RedisTest.php b/tests/RedisTest.php
    index 7ca9e6856b..783d23a9da 100644
    --- a/tests/RedisTest.php
    +++ b/tests/RedisTest.php
    @@ -6345,7 +6345,7 @@ public function testHashExpiration() {
                 $this->assertEquals([1], $res);
     
                 // LT - should not set if the new expiration is smaller
    -            $res = $this->redis->{$exp_cmd}('m', $ttl / 2, ['F'], 'LT');
    +            $res = $this->redis->{$exp_cmd}('m', intval($ttl / 2), ['F'], 'LT');
                 $this->assertTrue(is_array($res) && $res[0] > 0);
             }
         }